Add search
This commit is contained in:
parent
68be5fd1c3
commit
c9ec79442b
6 changed files with 1286 additions and 1391 deletions
|
|
@ -21,7 +21,6 @@
|
|||
"react": "^16.0.0",
|
||||
"react-dom": "^16.0.0",
|
||||
"react-leaflet": "^2.0.0",
|
||||
"react-tap-event-plugin": "^3.0.0",
|
||||
"redux": "^3.7.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ export type AppState = {
|
|||
mqttState: State,
|
||||
mqttSend: (topic: string, value: Buffer) => void,
|
||||
mqttConnected: boolean,
|
||||
search: string,
|
||||
error: ?string
|
||||
};
|
||||
|
||||
|
|
@ -57,14 +58,16 @@ class App extends React.PureComponent<AppProps & Classes, AppState> {
|
|||
(x) => this.topics[x].state.name)
|
||||
}),
|
||||
mqttConnected: false,
|
||||
search: "",
|
||||
error: null
|
||||
};
|
||||
this.controlMap =
|
||||
<ControlMap width={1000} height={700} zoom={0}
|
||||
layers={this.props.config.layers}
|
||||
controls={this.props.config.controls}
|
||||
onChangeControl={this.changeControl}
|
||||
/>;
|
||||
this.controlMap = (search: string) =>
|
||||
<ControlMap width={1000} height={700} zoom={0}
|
||||
layers={this.props.config.layers}
|
||||
controls={this.props.config.controls}
|
||||
onChangeControl={this.changeControl}
|
||||
search={search}
|
||||
/>;
|
||||
}
|
||||
|
||||
get topics(): Topics {
|
||||
|
|
@ -144,7 +147,8 @@ class App extends React.PureComponent<AppProps & Classes, AppState> {
|
|||
changeState: this.changeState
|
||||
}}>
|
||||
<TopBar title={`${this.props.config.space.name} Map`}
|
||||
connected={this.state.mqttConnected} />
|
||||
connected={this.state.mqttConnected}
|
||||
onSearch={(s) => this.setState({ search: s })} />
|
||||
<SideBar open={this.state.drawerOpened}
|
||||
control={this.state.selectedControl}
|
||||
onCloseRequest={this.closeDrawer}
|
||||
|
|
@ -154,7 +158,7 @@ class App extends React.PureComponent<AppProps & Classes, AppState> {
|
|||
{this.state.selectedControl == null
|
||||
|| <UiItemList controls={this.state.selectedControl.ui} />}
|
||||
</SideBar>
|
||||
{this.controlMap}
|
||||
{this.controlMap(this.state.search)}
|
||||
<Snackbar
|
||||
anchorOrigin={{
|
||||
vertical: "bottom",
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import React from "react";
|
|||
import { Map, ImageOverlay, Marker, LayersControl } from "react-leaflet";
|
||||
import { CRS, point, divIcon } from "leaflet";
|
||||
import map from "lodash/map";
|
||||
import filter from "lodash/filter";
|
||||
import MqttContext from "mqtt/context";
|
||||
import type { Controls, Control } from "config/flowtypes";
|
||||
|
||||
|
|
@ -16,6 +17,7 @@ export type ControlMapProps = {
|
|||
zoom: number,
|
||||
layers: Array<Layer>,
|
||||
controls: Controls,
|
||||
search: string,
|
||||
onChangeControl: (control: Control) => void
|
||||
};
|
||||
|
||||
|
|
@ -56,8 +58,42 @@ const renderMarker = (props: ControlMapProps) =>
|
|||
</MqttContext.Consumer>
|
||||
);
|
||||
|
||||
const safeIncludes = (o: {type?: string, text?: string, topic?: string},
|
||||
s: string) => {
|
||||
if (o.type != null) {
|
||||
if (o.type.toLowerCase().includes(s)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (o.text != null) {
|
||||
if (o.text.toLowerCase().includes(s)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (o.topic != null) {
|
||||
if (o.topic.toLowerCase().includes(s)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
const isVisible = (props: ControlMapProps) => (c: UIControl) => {
|
||||
if (safeIncludes(c, props.search.toLowerCase())) {
|
||||
return true;
|
||||
}
|
||||
if (c.ui != null) {
|
||||
for (let k in c.ui) {
|
||||
if (safeIncludes(c.ui[k], props.search.toLowerCase())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
const renderMarkers = (props: ControlMapProps) =>
|
||||
map(props.controls, renderMarker(props));
|
||||
map(filter(props.controls, isVisible(props)), renderMarker(props));
|
||||
|
||||
const renderLayer = (layer: Layer) => {
|
||||
const LayersControlType =
|
||||
|
|
|
|||
|
|
@ -5,25 +5,92 @@ import AppBar from "@material-ui/core/AppBar";
|
|||
import Toolbar from "@material-ui/core/Toolbar";
|
||||
import Typography from "@material-ui/core/Typography";
|
||||
import CircularProgress from "@material-ui/core/CircularProgress";
|
||||
import InputBase from "@material-ui/core/InputBase";
|
||||
import { fade } from "@material-ui/core/styles/colorManipulator";
|
||||
import { withStyles } from "@material-ui/core/styles";
|
||||
|
||||
export type TopBarProps = {
|
||||
title: string,
|
||||
connected: boolean
|
||||
connected: boolean,
|
||||
onSearch: string => void
|
||||
};
|
||||
|
||||
export type SearchBarProps = {
|
||||
onSearch: string => void
|
||||
};
|
||||
|
||||
const renderConnectionIndicator = (connected: boolean) => {
|
||||
if (connected) {
|
||||
return (<i style={{fontSize: 48}} className="mdi mdi-map"></i>);
|
||||
return (<i style={{fontSize: 32}} className="mdi mdi-map"></i>);
|
||||
}
|
||||
return (
|
||||
<CircularProgress size={48} style={{color: "rgba(0, 0, 0, 0.54)"}} />
|
||||
<CircularProgress size={32} style={{color: "rgba(0, 0, 0, 0.54)"}} />
|
||||
);
|
||||
};
|
||||
|
||||
const searchStyles = (theme) => ({
|
||||
search: {
|
||||
position: "relative",
|
||||
borderRadius: theme.shape.borderRadius,
|
||||
backgroundColor: fade(theme.palette.common.white, 0.15),
|
||||
"&:hover": {
|
||||
backgroundColor: fade(theme.palette.common.white, 0.25)
|
||||
},
|
||||
marginRight: theme.spacing.unit * 2,
|
||||
marginLeft: 0,
|
||||
width: "100%",
|
||||
[theme.breakpoints.up("sm")]: {
|
||||
marginLeft: theme.spacing.unit * 3,
|
||||
width: "auto"
|
||||
}
|
||||
},
|
||||
searchIcon: {
|
||||
width: theme.spacing.unit * 6,
|
||||
height: "100%",
|
||||
position: "absolute",
|
||||
pointerEvents: "none",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
fontSize: "24px"
|
||||
},
|
||||
inputRoot: {
|
||||
color: "inherit",
|
||||
width: "100%"
|
||||
},
|
||||
inputInput: {
|
||||
paddingTop: theme.spacing.unit,
|
||||
paddingRight: theme.spacing.unit,
|
||||
paddingBottom: theme.spacing.unit,
|
||||
paddingLeft: theme.spacing.unit * 6,
|
||||
transition: theme.transitions.create("width"),
|
||||
width: "100%",
|
||||
[theme.breakpoints.up("md")]: {
|
||||
width: 200
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const RawSearch = (props: SearchBarProps & Classes) => (
|
||||
<div className={props.classes.search}>
|
||||
<i className={`mdi mdi-magnify ${props.classes.searchIcon}`}></i>
|
||||
<InputBase placeholder="Search…" type="search"
|
||||
onChange={(e) => props.onSearch(e.target.value)}
|
||||
classes={{
|
||||
root: props.classes.inputRoot,
|
||||
input: props.classes.inputInput
|
||||
}} />
|
||||
</div>
|
||||
);
|
||||
|
||||
const Search = withStyles(searchStyles)(RawSearch);
|
||||
|
||||
const TopBar = (props: TopBarProps) => (
|
||||
<AppBar position="static">
|
||||
<Toolbar>
|
||||
{renderConnectionIndicator(props.connected)}
|
||||
<Search onSearch={props.onSearch} />
|
||||
<span style={{flex: 1}}></span>
|
||||
<Typography variant="title">{props.title}</Typography>
|
||||
</Toolbar>
|
||||
</AppBar>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
// @flow
|
||||
import React from "react";
|
||||
import ReactDOM from "react-dom";
|
||||
import injectTapEventPlugin from "react-tap-event-plugin";
|
||||
|
||||
import App from "components/App";
|
||||
|
||||
|
|
@ -11,7 +10,6 @@ import "../css/styles.css";
|
|||
import type { Config } from "config/flowtypes";
|
||||
|
||||
const config: Config = window.config;
|
||||
injectTapEventPlugin();
|
||||
|
||||
document.title = `${config.space.name} Map`;
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue