mirror of
https://github.com/weaveworks/scope.git
synced 2026-03-03 18:20:27 +00:00
140 lines
4.2 KiB
JavaScript
140 lines
4.2 KiB
JavaScript
import React from 'react';
|
|
import { connect } from 'react-redux';
|
|
import { isEmpty } from 'lodash';
|
|
import { Search } from 'weaveworks-ui-components';
|
|
import styled from 'styled-components';
|
|
|
|
import { blurSearch, focusSearch, updateSearch, toggleHelp } from '../actions/app-actions';
|
|
import { searchMatchCountByTopologySelector } from '../selectors/search';
|
|
import { isResourceViewModeSelector } from '../selectors/topology';
|
|
import { slugify } from '../utils/string-utils';
|
|
import { isTopologyNodeCountZero } from '../utils/topology-utils';
|
|
import { trackAnalyticsEvent } from '../utils/tracking-utils';
|
|
|
|
|
|
const SearchWrapper = styled.div`
|
|
margin: 0 8px;
|
|
min-width: 160px;
|
|
text-align: right;
|
|
`;
|
|
|
|
const SearchContainer = styled.div`
|
|
display: inline-block;
|
|
position: relative;
|
|
pointer-events: all;
|
|
line-height: 100%;
|
|
max-width: 400px;
|
|
width: 100%;
|
|
`;
|
|
|
|
const SearchHint = styled.div`
|
|
font-size: ${props => props.theme.fontSizes.tiny};
|
|
color: ${props => props.theme.colors.purple400};
|
|
transition: transform 0.3s 0s ease-in-out, opacity 0.3s 0s ease-in-out;
|
|
text-align: left;
|
|
margin-top: 3px;
|
|
padding: 0 1em;
|
|
opacity: 0;
|
|
|
|
${props => props.active && `
|
|
opacity: 1;
|
|
`};
|
|
`;
|
|
|
|
const SearchHintIcon = styled.span`
|
|
font-size: ${props => props.theme.fontSizes.normal};
|
|
cursor: pointer;
|
|
|
|
&:hover {
|
|
color: ${props => props.theme.colors.purple600};
|
|
}
|
|
`;
|
|
|
|
function shortenHintLabel(text) {
|
|
return text
|
|
.split(' ')[0]
|
|
.toLowerCase()
|
|
.substr(0, 12);
|
|
}
|
|
|
|
// dynamic hint based on node names
|
|
function getHint(nodes) {
|
|
let label = 'mycontainer';
|
|
let metadataLabel = 'ip';
|
|
let metadataValue = '10.1.0.1';
|
|
|
|
const node = nodes.filter(n => !n.get('pseudo') && n.has('metadata')).last();
|
|
if (node) {
|
|
[label] = shortenHintLabel(node.get('label')).split('.');
|
|
if (node.get('metadata')) {
|
|
const metadataField = node.get('metadata').first();
|
|
metadataLabel = shortenHintLabel(slugify(metadataField.get('label')))
|
|
.split('.').pop();
|
|
metadataValue = shortenHintLabel(metadataField.get('value'));
|
|
}
|
|
}
|
|
|
|
return `Try "${label}", "${metadataLabel}:${metadataValue}", or "cpu > 2%".`;
|
|
}
|
|
|
|
|
|
class SearchComponent extends React.Component {
|
|
handleChange = (searchQuery, pinnedSearches) => {
|
|
trackAnalyticsEvent('scope.search.query.change', {
|
|
layout: this.props.topologyViewMode,
|
|
topologyId: this.props.currentTopology.get('id'),
|
|
parentTopologyId: this.props.currentTopology.get('parentId'),
|
|
});
|
|
this.props.updateSearch(searchQuery, pinnedSearches);
|
|
}
|
|
|
|
render() {
|
|
const {
|
|
searchHint, searchMatchesCount, searchQuery, pinnedSearches, topologiesLoaded,
|
|
isResourceViewMode, isTopologyEmpty,
|
|
} = this.props;
|
|
|
|
return (
|
|
<SearchWrapper>
|
|
<SearchContainer title={searchMatchesCount ? `${searchMatchesCount} matches` : undefined}>
|
|
<Search
|
|
placeholder="search"
|
|
query={searchQuery}
|
|
pinnedTerms={pinnedSearches}
|
|
disabled={topologiesLoaded && !isResourceViewMode && isTopologyEmpty}
|
|
onChange={this.handleChange}
|
|
onFocus={this.props.focusSearch}
|
|
onBlur={this.props.blurSearch}
|
|
/>
|
|
<SearchHint active={this.props.searchFocused && isEmpty(pinnedSearches)}>
|
|
{searchHint} <SearchHintIcon
|
|
className="fa fa-question-circle"
|
|
onMouseDown={this.props.toggleHelp}
|
|
/>
|
|
</SearchHint>
|
|
</SearchContainer>
|
|
</SearchWrapper>
|
|
);
|
|
}
|
|
}
|
|
|
|
|
|
export default connect(
|
|
state => ({
|
|
searchHint: getHint(state.get('nodes')),
|
|
searchFocused: state.get('searchFocused'),
|
|
topologyViewMode: state.get('topologyViewMode'),
|
|
isResourceViewMode: isResourceViewModeSelector(state),
|
|
isTopologyEmpty: isTopologyNodeCountZero(state),
|
|
currentTopology: state.get('currentTopology'),
|
|
topologiesLoaded: state.get('topologiesLoaded'),
|
|
pinnedSearches: state.get('pinnedSearches').toJS(),
|
|
searchQuery: state.get('searchQuery'),
|
|
searchMatchesCount: searchMatchCountByTopologySelector(state)
|
|
.reduce((count, topologyMatchCount) => count + topologyMatchCount, 0),
|
|
}),
|
|
{
|
|
blurSearch, focusSearch, updateSearch, toggleHelp
|
|
}
|
|
)(SearchComponent);
|