Use React Context API
This commit is contained in:
parent
3b78a4d8ad
commit
df237ef336
7 changed files with 90 additions and 72 deletions
|
|
@ -21,6 +21,7 @@ import ControlMap from "components/ControlMap";
|
||||||
import TopBar from "components/TopBar";
|
import TopBar from "components/TopBar";
|
||||||
import UiItemList from "components/UiItemList";
|
import UiItemList from "components/UiItemList";
|
||||||
|
|
||||||
|
import MqttContext from "mqtt/context";
|
||||||
import connectMqtt from "../connectMqtt";
|
import connectMqtt from "../connectMqtt";
|
||||||
|
|
||||||
export type AppProps = {
|
export type AppProps = {
|
||||||
|
|
@ -119,7 +120,6 @@ class App extends React.PureComponent<AppProps & Classes, AppState> {
|
||||||
const val =
|
const val =
|
||||||
transformValue == null ? value : transformValue(Buffer.from(value));
|
transformValue == null ? value : transformValue(Buffer.from(value));
|
||||||
this.state.mqttSend(rawTopic, Buffer.from(val));
|
this.state.mqttSend(rawTopic, Buffer.from(val));
|
||||||
throw new Error("test");
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
this.setState({ error: err.toString() });
|
this.setState({ error: err.toString() });
|
||||||
}
|
}
|
||||||
|
|
@ -127,9 +127,12 @@ class App extends React.PureComponent<AppProps & Classes, AppState> {
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<div>
|
<MqttContext.Provider value={{
|
||||||
|
state: this.state.mqttState,
|
||||||
|
changeState: this.changeState.bind(this)
|
||||||
|
}}>
|
||||||
<MuiThemeProvider theme={this.theme}>
|
<MuiThemeProvider theme={this.theme}>
|
||||||
<div>
|
<React.Fragment>
|
||||||
<TopBar title={`${this.props.config.space.name} Map`}
|
<TopBar title={`${this.props.config.space.name} Map`}
|
||||||
connected={this.state.mqttConnected} />
|
connected={this.state.mqttConnected} />
|
||||||
<SideBar open={this.state.drawerOpened}
|
<SideBar open={this.state.drawerOpened}
|
||||||
|
|
@ -139,18 +142,14 @@ class App extends React.PureComponent<AppProps & Classes, AppState> {
|
||||||
this.state.selectedControl.icon(this.state.mqttState)}
|
this.state.selectedControl.icon(this.state.mqttState)}
|
||||||
>
|
>
|
||||||
{this.state.selectedControl == null
|
{this.state.selectedControl == null
|
||||||
|| <UiItemList state={this.state.mqttState}
|
|| <UiItemList controls={this.state.selectedControl.ui} />}
|
||||||
controls={this.state.selectedControl.ui}
|
|
||||||
onChangeState={this.changeState.bind(this)}
|
|
||||||
/>}
|
|
||||||
</SideBar>
|
</SideBar>
|
||||||
</div>
|
</React.Fragment>
|
||||||
</MuiThemeProvider>
|
</MuiThemeProvider>
|
||||||
<ControlMap width={1000} height={700} zoom={0}
|
<ControlMap width={1000} height={700} zoom={0}
|
||||||
layers={this.props.config.layers}
|
layers={this.props.config.layers}
|
||||||
controls={this.props.config.controls}
|
controls={this.props.config.controls}
|
||||||
onChangeControl={this.changeControl.bind(this)}
|
onChangeControl={this.changeControl.bind(this)}
|
||||||
state={this.state.mqttState}
|
|
||||||
/>
|
/>
|
||||||
<Snackbar
|
<Snackbar
|
||||||
anchorOrigin={{
|
anchorOrigin={{
|
||||||
|
|
@ -176,7 +175,7 @@ class App extends React.PureComponent<AppProps & Classes, AppState> {
|
||||||
<i className="mdi mdi-close" />
|
<i className="mdi mdi-close" />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
} />
|
} />
|
||||||
</div>
|
</MqttContext.Provider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ import React from "react";
|
||||||
import { Map, ImageOverlay, Marker, LayersControl } from "react-leaflet";
|
import { Map, ImageOverlay, Marker, LayersControl } from "react-leaflet";
|
||||||
import { CRS, point, divIcon } from "leaflet";
|
import { CRS, point, divIcon } from "leaflet";
|
||||||
import map from "lodash/map";
|
import map from "lodash/map";
|
||||||
|
import MqttContext from "mqtt/context";
|
||||||
import type { Controls, Control } from "config/flowtypes";
|
import type { Controls, Control } from "config/flowtypes";
|
||||||
|
|
||||||
export type Point = [number, number];
|
export type Point = [number, number];
|
||||||
|
|
@ -16,8 +16,7 @@ export type ControlMapProps = {
|
||||||
zoom: number,
|
zoom: number,
|
||||||
layers: Array<Layer>,
|
layers: Array<Layer>,
|
||||||
controls: Controls,
|
controls: Controls,
|
||||||
onChangeControl: (control: Control) => void,
|
onChangeControl: (control: Control) => void
|
||||||
state: State
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default class ControlMap extends React.PureComponent<ControlMapProps> {
|
export default class ControlMap extends React.PureComponent<ControlMapProps> {
|
||||||
|
|
@ -48,32 +47,36 @@ export default class ControlMap extends React.PureComponent<ControlMapProps> {
|
||||||
return map(this.props.controls, this.renderMarker.bind(this));
|
return map(this.props.controls, this.renderMarker.bind(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
createLeafletIcon(control: Control) {
|
createLeafletIcon(control: Control, state: State) {
|
||||||
const icon = control.icon(this.props.state);
|
const icon = control.icon(state);
|
||||||
const iconClass = `${icon} mdi-36px`;
|
const iconClass = `${icon} mdi-36px`;
|
||||||
return divIcon({
|
return divIcon({
|
||||||
iconSize: point(36, 36),
|
iconSize: point(36, 36),
|
||||||
iconAnchor: point(18, 18),
|
iconAnchor: point(18, 18),
|
||||||
html: `<i class="${iconClass}"
|
html: `<i class="${iconClass}"
|
||||||
style="line-height: 1; color: ${this.iconColor(control)}"></i>`
|
style="line-height: 1; color: ${this.iconColor(control, state)}"></i>`
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
iconColor(control: Control): string {
|
iconColor(control: Control, state: State): string {
|
||||||
if (control.iconColor != null) {
|
if (control.iconColor != null) {
|
||||||
return control.iconColor(this.props.state);
|
return control.iconColor(state);
|
||||||
}
|
}
|
||||||
return "#000";
|
return "#000";
|
||||||
}
|
}
|
||||||
|
|
||||||
renderMarker(control: Control, key: string) {
|
renderMarker(control: Control, key: string) {
|
||||||
return (
|
return (
|
||||||
|
<MqttContext.Consumer>
|
||||||
|
{({ state }) => (
|
||||||
<Marker position={convertPoint(control.position)}
|
<Marker position={convertPoint(control.position)}
|
||||||
key={key}
|
key={key}
|
||||||
icon={this.createLeafletIcon(control)}
|
icon={this.createLeafletIcon(control, state)}
|
||||||
onClick={() => this.props.onChangeControl(control)}
|
onClick={() => this.props.onChangeControl(control)}
|
||||||
>
|
>
|
||||||
</Marker>
|
</Marker>
|
||||||
|
)}
|
||||||
|
</MqttContext.Consumer>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ import IconButton from "@material-ui/core/IconButton";
|
||||||
import AppBar from "@material-ui/core/AppBar";
|
import AppBar from "@material-ui/core/AppBar";
|
||||||
import Toolbar from "@material-ui/core/Toolbar";
|
import Toolbar from "@material-ui/core/Toolbar";
|
||||||
import List from "@material-ui/core/List";
|
import List from "@material-ui/core/List";
|
||||||
import { renderIcon } from "config/icon";
|
import { renderRawIcon } from "config/icon";
|
||||||
|
|
||||||
import type { RawIcon } from "config/icon";
|
import type { RawIcon } from "config/icon";
|
||||||
import type { Control } from "config/flowtypes";
|
import type { Control } from "config/flowtypes";
|
||||||
|
|
@ -57,7 +57,7 @@ class SideBar extends React.PureComponent<Props, SideBarState> {
|
||||||
<AppBar position="static">
|
<AppBar position="static">
|
||||||
<Toolbar>
|
<Toolbar>
|
||||||
<span>{this.props.icon == null
|
<span>{this.props.icon == null
|
||||||
|| renderIcon(this.props.icon, "mdi-36px")}</span>
|
|| renderRawIcon(this.props.icon, "mdi-36px")}</span>
|
||||||
<Typography variant="title" className={this.props.classes.flex}>
|
<Typography variant="title" className={this.props.classes.flex}>
|
||||||
{this.props.control == null ? "" : this.props.control.name}
|
{this.props.control == null ? "" : this.props.control.name}
|
||||||
</Typography>
|
</Typography>
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ import React from "react";
|
||||||
import keys from "lodash/keys";
|
import keys from "lodash/keys";
|
||||||
import map from "lodash/map";
|
import map from "lodash/map";
|
||||||
import debounce from "lodash/debounce";
|
import debounce from "lodash/debounce";
|
||||||
import { renderIcon } from "config/icon";
|
import { renderRawIcon } from "config/icon";
|
||||||
import ListItemSecondaryAction from "@material-ui/core/ListItemSecondaryAction";
|
import ListItemSecondaryAction from "@material-ui/core/ListItemSecondaryAction";
|
||||||
import ListItemText from "@material-ui/core/ListItemText";
|
import ListItemText from "@material-ui/core/ListItemText";
|
||||||
import ListSubheader from "@material-ui/core/ListSubheader";
|
import ListSubheader from "@material-ui/core/ListSubheader";
|
||||||
|
|
@ -218,7 +218,7 @@ export class Link extends UiItem<UILink> {
|
||||||
disabled={!this.isEnabled()}
|
disabled={!this.isEnabled()}
|
||||||
>
|
>
|
||||||
{this.props.item.icon == null ? ""
|
{this.props.item.icon == null ? ""
|
||||||
: renderIcon(this.props.item.icon(this.props.state), "mdi-24px")}
|
: renderRawIcon(this.props.item.icon(this.props.state), "mdi-24px")}
|
||||||
{this.props.item.text}
|
{this.props.item.text}
|
||||||
</Button>
|
</Button>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -2,17 +2,17 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import ListItem from "@material-ui/core/ListItem";
|
import ListItem from "@material-ui/core/ListItem";
|
||||||
import ListItemIcon from "@material-ui/core/ListItemIcon";
|
import ListItemIcon from "@material-ui/core/ListItemIcon";
|
||||||
import { renderIcon } from "config/icon";
|
import { renderRawIcon } from "config/icon";
|
||||||
|
|
||||||
import type { ControlUI } from "config/flowtypes";
|
import type { ControlUI } from "config/flowtypes";
|
||||||
|
|
||||||
import { Toggle, DropDown, Link,
|
import { Toggle, DropDown, Link,
|
||||||
Section, Text, Progress, Slider } from "./UiItem";
|
Section, Text, Progress, Slider } from "./UiItem";
|
||||||
|
import MqttContext from "mqtt/context";
|
||||||
|
import type { MqttContextValue } from "mqtt/context";
|
||||||
|
|
||||||
export type UiItemListProps = {
|
export type UiItemListProps = {
|
||||||
controls: Array<ControlUI>,
|
controls: Array<ControlUI>
|
||||||
state: State,
|
|
||||||
onChangeState: (topic: string, nextState: string) => void
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default class UiItemList extends React.PureComponent<UiItemListProps> {
|
export default class UiItemList extends React.PureComponent<UiItemListProps> {
|
||||||
|
|
@ -27,66 +27,60 @@ export default class UiItemList extends React.PureComponent<UiItemListProps> {
|
||||||
"A control is missing the \"type\" parameter"
|
"A control is missing the \"type\" parameter"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (control.type === "section") {
|
|
||||||
return this.renderControl(control, key.toString());
|
|
||||||
}
|
|
||||||
return (
|
return (
|
||||||
<ListItem key={key}>
|
<ListItem key={key}>
|
||||||
<React.Fragment>
|
<MqttContext.Consumer>
|
||||||
{control.icon == null || control.type === "link" ||
|
{this.renderListItem(control, key)}
|
||||||
<ListItemIcon key={`${key.toString()}-liicon`}>
|
</MqttContext.Consumer>
|
||||||
{renderIcon(control.icon(this.props.state), "mdi-24px")}
|
|
||||||
</ListItemIcon>}
|
|
||||||
{this.renderControl(control, key.toString())}
|
|
||||||
</React.Fragment>
|
|
||||||
</ListItem>
|
</ListItem>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
renderControl(control: ControlUI, key: string) {
|
renderListItem(control: ControlUI, key: number) {
|
||||||
|
return (mqtt: MqttContextValue) => {
|
||||||
|
const node = this.renderControl(control, key.toString(), mqtt);
|
||||||
|
if (control.icon == null || control.type === "link"
|
||||||
|
|| control.type === "section") {
|
||||||
|
return node;
|
||||||
|
} else {
|
||||||
|
const listIconNode = (
|
||||||
|
<ListItemIcon key={`${key.toString()}-liicon`}>
|
||||||
|
{renderRawIcon(control.icon(mqtt.state), "mdi-24px")}
|
||||||
|
</ListItemIcon>
|
||||||
|
);
|
||||||
|
return [listIconNode, node];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
renderControl(control: ControlUI, key: string, mqtt: MqttContextValue) {
|
||||||
|
const props = {
|
||||||
|
state: mqtt.state,
|
||||||
|
onChangeState: mqtt.changeState,
|
||||||
|
key: `${key}-licontrol`
|
||||||
|
};
|
||||||
switch (control.type) {
|
switch (control.type) {
|
||||||
case "toggle": {
|
case "toggle": {
|
||||||
return <Toggle item={control}
|
return <Toggle item={control} {...props} />;
|
||||||
state={this.props.state}
|
|
||||||
onChangeState={this.props.onChangeState}
|
|
||||||
key={`${key}-licontrol`} />;
|
|
||||||
}
|
}
|
||||||
case "dropDown": {
|
case "dropDown": {
|
||||||
return <DropDown item={control}
|
return <DropDown item={control} {...props} />;
|
||||||
state={this.props.state}
|
|
||||||
onChangeState={this.props.onChangeState}
|
|
||||||
key={`${key}-licontrol`} />;
|
|
||||||
}
|
}
|
||||||
case "section": {
|
case "section": {
|
||||||
return <Section item={control}
|
return <Section item={control} {...props} />;
|
||||||
state={this.props.state}
|
|
||||||
onChangeState={this.props.onChangeState}
|
|
||||||
key={`${key}-licontrol`} />;
|
|
||||||
}
|
}
|
||||||
case "link": {
|
case "link": {
|
||||||
return <Link item={control}
|
return <Link item={control} {...props} />;
|
||||||
state={this.props.state}
|
|
||||||
onChangeState={this.props.onChangeState}
|
|
||||||
key={`${key}-licontrol`} />;
|
|
||||||
}
|
}
|
||||||
case "slider": {
|
case "slider": {
|
||||||
return <Slider item={control}
|
return <Slider item={control} {...props} />;
|
||||||
state={this.props.state}
|
|
||||||
onChangeState={this.props.onChangeState}
|
|
||||||
key={`${key}-licontrol`} />;
|
|
||||||
}
|
}
|
||||||
case "text": {
|
case "text": {
|
||||||
return <Text item={control}
|
return <Text item={control} {...props} />;
|
||||||
state={this.props.state}
|
|
||||||
onChangeState={this.props.onChangeState}
|
|
||||||
key={`${key}-licontrol`} />;
|
|
||||||
}
|
}
|
||||||
case "progress": {
|
case "progress": {
|
||||||
return <Progress item={control}
|
return <Progress item={control} {...props} />;
|
||||||
state={this.props.state}
|
|
||||||
onChangeState={this.props.onChangeState}
|
|
||||||
key={`${key}-licontrol`} />;
|
|
||||||
}
|
}
|
||||||
default: {
|
default: {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
// @flow
|
// @flow
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
|
import ReactContext from "mqtt/context";
|
||||||
|
|
||||||
export opaque type RawIcon: string = string;
|
export opaque type RawIcon: string = string;
|
||||||
|
|
||||||
|
|
@ -39,7 +40,16 @@ export const mdiBattery = (topic: string) => (state: State) => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const renderIcon =
|
export const renderRawIcon =
|
||||||
(icon: RawIcon, extraClass?: string): React.Node => {
|
(icon: RawIcon, extraClass?: string): React.Node => {
|
||||||
return <i className={`${extraClass || ""} ${icon}`}></i>;
|
return <i className={`${extraClass || ""} ${icon}`}></i>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const renderIcon =
|
||||||
|
(icon: Icon, extraClass?: string): React.Node => {
|
||||||
|
return (
|
||||||
|
<ReactContext.Consumer>
|
||||||
|
{({state}) => renderRawIcon(icon(state), extraClass)}
|
||||||
|
</ReactContext.Consumer>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
|
||||||
12
src/mqtt/context.js
Normal file
12
src/mqtt/context.js
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
// @flow
|
||||||
|
import React from "react";
|
||||||
|
|
||||||
|
export type MqttContextValue = {
|
||||||
|
state: State,
|
||||||
|
changeState: (topic: string, value: string) => void
|
||||||
|
};
|
||||||
|
|
||||||
|
export default React.createContext({
|
||||||
|
state: {},
|
||||||
|
changeState: (_topic, _val) => {}
|
||||||
|
});
|
||||||
Loading…
Add table
Add a link
Reference in a new issue