highlight search results on graph
This commit is contained in:
parent
bdf1f12a1c
commit
7e55296a26
|
@ -11,11 +11,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
|
||||
- Instance administrators can now log in to opt in or out of crawling.
|
||||
- Added ElasticSearch full-text search over instance domains and descriptions.
|
||||
- Search results are now highlighted on the graph.
|
||||
- When you hover a search result, it is now highlighted on the graph.
|
||||
|
||||
### Changed
|
||||
|
||||
- Instances are now crawled hourly instead of every 30 minutes.
|
||||
- The colors for color coding have been made brighter (more visible against the dark background.
|
||||
|
||||
### Deprecated
|
||||
|
||||
|
|
|
@ -102,7 +102,7 @@ defmodule Backend.Api do
|
|||
end
|
||||
|
||||
def search_instances(query, from \\ 0) do
|
||||
page_size = 10
|
||||
page_size = 50
|
||||
|
||||
search_response =
|
||||
Elasticsearch.post(Backend.Elasticsearch.Cluster, "/instances/_search", %{
|
||||
|
|
|
@ -47,7 +47,7 @@ const GraphKey: React.FC<IGraphKeyProps> = ({ current, colorSchemes, onItemSelec
|
|||
<H6>Key</H6>
|
||||
<ul className={Classes.LIST_UNSTYLED}>
|
||||
{current.values.map(v => (
|
||||
<StyledLi>
|
||||
<StyledLi key={v}>
|
||||
<InstanceType type={v} />
|
||||
</StyledLi>
|
||||
))}
|
||||
|
|
|
@ -1,9 +1,16 @@
|
|||
import cytoscape from "cytoscape";
|
||||
import { isEqual } from "lodash";
|
||||
import * as React from "react";
|
||||
import ReactDOM from "react-dom";
|
||||
import styled from "styled-components";
|
||||
import tippy, { Instance } from "tippy.js";
|
||||
import { DEFAULT_NODE_COLOR, HOVERED_NODE_COLOR, QUALITATIVE_COLOR_SCHEME, SELECTED_NODE_COLOR } from "../../constants";
|
||||
import {
|
||||
DEFAULT_NODE_COLOR,
|
||||
HOVERED_NODE_COLOR,
|
||||
QUALITATIVE_COLOR_SCHEME,
|
||||
SEARCH_RESULT_COLOR,
|
||||
SELECTED_NODE_COLOR
|
||||
} from "../../constants";
|
||||
import { IColorSchemeType } from "../../types";
|
||||
|
||||
const CytoscapeContainer = styled.div`
|
||||
|
@ -17,6 +24,7 @@ interface ICytoscapeProps {
|
|||
currentNodeId: string | null;
|
||||
elements: cytoscape.ElementsDefinition;
|
||||
hoveringOver?: string;
|
||||
searchResultIds?: string[];
|
||||
navigateToInstancePath?: (domain: string) => void;
|
||||
navigateToRoot?: () => void;
|
||||
}
|
||||
|
@ -132,6 +140,9 @@ class Cytoscape extends React.PureComponent<ICytoscapeProps> {
|
|||
if (prevProps.hoveringOver !== this.props.hoveringOver) {
|
||||
this.updateHoveredNodeClass(prevProps.hoveringOver);
|
||||
}
|
||||
if (!isEqual(prevProps.searchResultIds, this.props.searchResultIds)) {
|
||||
this.updateSearchResultNodeClass();
|
||||
}
|
||||
}
|
||||
|
||||
public componentWillUnmount() {
|
||||
|
@ -191,39 +202,44 @@ class Cytoscape extends React.PureComponent<ICytoscapeProps> {
|
|||
if (!style) {
|
||||
style = this.cy!.style() as any;
|
||||
}
|
||||
style = style
|
||||
.selector("node")
|
||||
.style({
|
||||
"background-color": DEFAULT_NODE_COLOR,
|
||||
// The size from the backend is log_10(userCount), which from 10 <= userCount <= 1,000,000 gives us the range
|
||||
// 1-6. We map this to the range of sizes we want.
|
||||
// TODO: I should probably check that that the backend is actually using log_10 and not log_e, but it look
|
||||
// quite good as it is, so...
|
||||
height: "mapData(size, 1, 6, 20, 200)",
|
||||
label: "data(id)",
|
||||
width: "mapData(size, 1, 6, 20, 200)"
|
||||
})
|
||||
.selector("node:selected")
|
||||
.style({
|
||||
"background-color": SELECTED_NODE_COLOR
|
||||
});
|
||||
style = style.selector("node").style({
|
||||
"background-color": DEFAULT_NODE_COLOR,
|
||||
// The size from the backend is log_10(userCount), which from 10 <= userCount <= 1,000,000 gives us the range
|
||||
// 1-6. We map this to the range of sizes we want.
|
||||
// TODO: I should probably check that that the backend is actually using log_10 and not log_e, but it look
|
||||
// quite good as it is, so...
|
||||
height: "mapData(size, 1, 6, 20, 200)",
|
||||
label: "data(id)",
|
||||
width: "mapData(size, 1, 6, 20, 200)"
|
||||
});
|
||||
|
||||
this.setHoveredNodeColorScheme(style);
|
||||
this.setNodeSearchColorScheme(style);
|
||||
};
|
||||
|
||||
/**
|
||||
* We always want to set node hover styled at the end of a style change to make sure they don't get overwritten.
|
||||
* We always want to set node search/hover styles at the end of a style change to make sure they don't get overwritten.
|
||||
*/
|
||||
private setHoveredNodeColorScheme = (style?: any) => {
|
||||
private setNodeSearchColorScheme = (style?: any) => {
|
||||
if (!style) {
|
||||
style = this.cy!.style() as any;
|
||||
}
|
||||
style
|
||||
.selector("node.searchResult")
|
||||
.style({
|
||||
"background-color": SEARCH_RESULT_COLOR,
|
||||
"border-color": SEARCH_RESULT_COLOR,
|
||||
"border-opacity": 0.7,
|
||||
"border-width": 250
|
||||
})
|
||||
.selector("node.hovered")
|
||||
.style({
|
||||
"border-color": HOVERED_NODE_COLOR,
|
||||
"border-width": 1000
|
||||
})
|
||||
.selector("node:selected")
|
||||
.style({
|
||||
"background-color": SELECTED_NODE_COLOR
|
||||
})
|
||||
.update();
|
||||
};
|
||||
|
||||
|
@ -241,9 +257,8 @@ class Cytoscape extends React.PureComponent<ICytoscapeProps> {
|
|||
"background-color": QUALITATIVE_COLOR_SCHEME[idx]
|
||||
});
|
||||
});
|
||||
style = style.selector("node:selected").style({ "background-color": SELECTED_NODE_COLOR });
|
||||
|
||||
this.setHoveredNodeColorScheme(style);
|
||||
this.setNodeSearchColorScheme(style);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -267,6 +282,22 @@ class Cytoscape extends React.PureComponent<ICytoscapeProps> {
|
|||
this.cy.$id(hoveringOver).addClass("hovered");
|
||||
}
|
||||
};
|
||||
|
||||
private updateSearchResultNodeClass = () => {
|
||||
if (!this.cy) {
|
||||
throw new Error("Expected cytoscape, but there wasn't one!");
|
||||
}
|
||||
const { searchResultIds } = this.props;
|
||||
|
||||
this.cy.batch(() => {
|
||||
this.cy!.nodes().removeClass("searchResult");
|
||||
|
||||
if (!!searchResultIds && searchResultIds.length > 0) {
|
||||
const currentResultSelector = searchResultIds.map(id => `node[id = "${id}"]`).join(", ");
|
||||
this.cy!.$(currentResultSelector).addClass("searchResult");
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
export default Cytoscape;
|
||||
|
|
|
@ -22,6 +22,7 @@ interface IGraphProps {
|
|||
graphLoadError: boolean;
|
||||
hoveringOverResult?: string;
|
||||
isLoadingGraph: boolean;
|
||||
searchResultDomains: string[];
|
||||
navigate: (path: string) => void;
|
||||
}
|
||||
interface IGraphState {
|
||||
|
@ -56,6 +57,7 @@ class GraphImpl extends React.PureComponent<IGraphProps, IGraphState> {
|
|||
hoveringOver={this.props.hoveringOverResult}
|
||||
navigateToInstancePath={this.navigateToInstancePath}
|
||||
navigateToRoot={this.navigateToRoot}
|
||||
searchResultIds={this.props.searchResultDomains}
|
||||
ref={this.cytoscapeComponent}
|
||||
/>
|
||||
<GraphTools
|
||||
|
@ -102,7 +104,8 @@ const mapStateToProps = (state: IAppState) => {
|
|||
graph: state.data.graph,
|
||||
graphLoadError: state.data.error,
|
||||
hoveringOverResult: state.search.hoveringOverResult,
|
||||
isLoadingGraph: state.data.isLoadingGraph
|
||||
isLoadingGraph: state.data.isLoadingGraph,
|
||||
searchResultDomains: state.search.results.map(r => r.name)
|
||||
};
|
||||
};
|
||||
const mapDispatchToProps = (dispatch: Dispatch) => ({
|
||||
|
|
|
@ -107,6 +107,7 @@ class SearchScreen extends React.PureComponent<ISearchScreenProps, ISearchScreen
|
|||
};
|
||||
|
||||
private selectInstanceFactory = (domain: string) => () => {
|
||||
this.props.setIsHoveringOver(undefined);
|
||||
this.props.navigateToInstance(domain);
|
||||
};
|
||||
|
||||
|
|
|
@ -3,20 +3,21 @@ export const DESKTOP_WIDTH_THRESHOLD = 1000;
|
|||
|
||||
export const DEFAULT_NODE_COLOR = "#CED9E0";
|
||||
export const SELECTED_NODE_COLOR = "#48AFF0";
|
||||
export const HOVERED_NODE_COLOR = "#FFB366";
|
||||
export const SEARCH_RESULT_COLOR = "#AD99FF";
|
||||
export const HOVERED_NODE_COLOR = SEARCH_RESULT_COLOR;
|
||||
|
||||
// From https://blueprintjs.com/docs/#core/colors.qualitative-color-schemes
|
||||
// From https://blueprintjs.com/docs/#core/colors.qualitative-color-schemes, but brightened
|
||||
export const QUALITATIVE_COLOR_SCHEME = [
|
||||
"#2965CC",
|
||||
"#29A634",
|
||||
"#D99E0B",
|
||||
"#D13913",
|
||||
"#8F398F",
|
||||
"#00B3A4",
|
||||
"#DB2C6F",
|
||||
"#9BBF30",
|
||||
"#96622D",
|
||||
"#7157D9"
|
||||
"#669EFF",
|
||||
"#62D96B",
|
||||
"#FFC940",
|
||||
"#FF6E4A",
|
||||
"#C274C2",
|
||||
"#2EE6D6",
|
||||
"#FF66A1",
|
||||
"#D1F26D",
|
||||
"#C99765",
|
||||
"#AD99FF"
|
||||
];
|
||||
|
||||
export const INSTANCE_DOMAIN_PATH = "/instance/:domain";
|
||||
|
|
Loading…
Reference in New Issue