Improve the entire icon logic
- Tree Shaking for Icons (Closes #53) - New API for the config (See https://github.com/uwap/mqtt-control-map/wiki/Icons) - Icons can now be colored everywhere, not just on the map
This commit is contained in:
parent
43a33c3ab3
commit
856aab41ad
18 changed files with 288 additions and 269 deletions
|
|
@ -39,6 +39,14 @@ export type AppState = {
|
|||
error: ?string
|
||||
};
|
||||
|
||||
/*
|
||||
const App = (props: AppProps) => {
|
||||
const topics = Array.isArray(props.config.topics) ?
|
||||
Object.assign({}, ...props.config.topics) : props.config.topics;
|
||||
const [mqttConnected, setMqttConnected] = useState(false);
|
||||
};
|
||||
*/
|
||||
|
||||
class App extends React.PureComponent<AppProps & Classes, AppState> {
|
||||
controlMap: React.Node
|
||||
|
||||
|
|
@ -171,7 +179,7 @@ class App extends React.PureComponent<AppProps & Classes, AppState> {
|
|||
control={this.state.selectedControl}
|
||||
onCloseRequest={this.closeDrawer}
|
||||
icon={this.state.selectedControl == null ? null :
|
||||
this.state.selectedControl.icon(this.state.mqttState)}
|
||||
this.state.selectedControl.icon.render(this.state.mqttState)}
|
||||
>
|
||||
{this.state.selectedControl == null
|
||||
|| <UiItemList controls={this.state.selectedControl.ui} />}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import filter from "lodash/filter";
|
|||
import reduce from "lodash/reduce";
|
||||
import MqttContext from "mqtt/context";
|
||||
import type { Controls, Control, UIControl, ControlUI } from "config/flowtypes";
|
||||
import { renderToString } from 'react-dom/server'
|
||||
|
||||
export type Point = [number, number];
|
||||
|
||||
|
|
@ -28,21 +29,11 @@ const center = (props: ControlMapProps): Point =>
|
|||
props.height / 2
|
||||
]);
|
||||
|
||||
const iconColor = (control: Control, state: State): string => {
|
||||
if (control.iconColor != null) {
|
||||
return control.iconColor(state);
|
||||
}
|
||||
return "#000";
|
||||
};
|
||||
|
||||
const createLeafletIcon = (control: Control, state: State) => {
|
||||
const icon = control.icon(state);
|
||||
const iconClass = `${icon} mdi-36px`;
|
||||
return divIcon({
|
||||
iconSize: point(36, 36),
|
||||
iconAnchor: point(18, 18),
|
||||
html: `<i class="${iconClass}"
|
||||
style="line-height: 1; color: ${iconColor(control, state)}"></i>`
|
||||
html: renderToString(control.icon.render(state))
|
||||
});
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -8,16 +8,14 @@ import IconButton from "@material-ui/core/IconButton";
|
|||
import AppBar from "@material-ui/core/AppBar";
|
||||
import Toolbar from "@material-ui/core/Toolbar";
|
||||
import List from "@material-ui/core/List";
|
||||
import { renderRawIcon } from "config/icon";
|
||||
|
||||
import type { RawIcon } from "config/icon";
|
||||
import type { Control } from "config/flowtypes";
|
||||
|
||||
export type SideBarProps = {
|
||||
control: ?Control,
|
||||
open: boolean,
|
||||
onCloseRequest: () => void,
|
||||
icon?: ?RawIcon,
|
||||
icon?: ?React.Node,
|
||||
children?: React.Node
|
||||
};
|
||||
|
||||
|
|
@ -43,7 +41,7 @@ const SideBar = (props: SideBarProps) => {
|
|||
<AppBar position="static">
|
||||
<Toolbar>
|
||||
<span>
|
||||
{props.icon == null || renderRawIcon(props.icon, "mdi-36px")}
|
||||
{props.icon == null || props.icon}
|
||||
</span>
|
||||
<Typography variant="subtitle1" className={classes.title}>
|
||||
{props.control == null ? "" : props.control.name}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,8 @@ import { fade } from "@material-ui/core/styles/colorManipulator";
|
|||
import { makeStyles } from "@material-ui/core/styles";
|
||||
import Tooltip from "@material-ui/core/Tooltip";
|
||||
import IconButton from "@material-ui/core/IconButton";
|
||||
import ReactIcon from "@mdi/react";
|
||||
import { mdiMap } from "@mdi/js";
|
||||
|
||||
export type TopBarProps = {
|
||||
connected: boolean,
|
||||
|
|
@ -21,7 +23,7 @@ export type SearchBarProps = {
|
|||
|
||||
const renderConnectionIndicator = (connected: boolean) => {
|
||||
if (connected) {
|
||||
return (<i style={{fontSize: 32}} className="mdi mdi-map"></i>);
|
||||
return (<ReactIcon path={mdiMap} size={2} />);
|
||||
}
|
||||
return (
|
||||
<CircularProgress size={32} style={{color: "rgba(0, 0, 0, 0.54)"}} />
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@
|
|||
import React from "react";
|
||||
import createComponent from "./base";
|
||||
import { isEnabled, isDisabled } from "./utils";
|
||||
import { renderRawIcon } from "config/icon";
|
||||
|
||||
import type { UILink } from "config/flowtypes";
|
||||
|
||||
|
|
@ -19,7 +18,7 @@ const Icon = ({item, state}) => {
|
|||
if (item.icon == null) {
|
||||
return false;
|
||||
}
|
||||
return renderRawIcon(item.icon(state), "mdi-24px");
|
||||
return item.icon.render(state);
|
||||
};
|
||||
|
||||
const BaseComponent = (_h, item: UILink, state, _changeState) => (
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ import ListItemText from "@material-ui/core/ListItemText";
|
|||
import ListItemIcon from "@material-ui/core/ListItemIcon";
|
||||
|
||||
import throttle from "lodash/throttle";
|
||||
import { renderRawIcon } from "config/icon";
|
||||
import type { Icon } from "config/icon";
|
||||
|
||||
export type Helpers = {
|
||||
|
|
@ -41,15 +40,15 @@ type SuperT = $ReadOnly<{ text: string }>;
|
|||
|
||||
const IconHelper = ({item, state}: { item: { +icon?: Icon }, state: State }) =>
|
||||
( <ListItemIcon>
|
||||
{item.icon == null || renderRawIcon(item.icon(state), "mdi-24px")}
|
||||
{item.icon == null || item.icon.size(1).render(state)}
|
||||
</ListItemIcon>
|
||||
);
|
||||
|
||||
const createHelpers = <T: SuperT> (item: T) =>
|
||||
({
|
||||
Icon: IconHelper,
|
||||
Label: (props) => (
|
||||
<ListItemText primary={item.text} {...props} />
|
||||
Label: () => (
|
||||
<ListItemText primary={item.text} />
|
||||
),
|
||||
Action: (props) => (
|
||||
<ListItemSecondaryAction {...props} />
|
||||
|
|
|
|||
|
|
@ -100,7 +100,6 @@ export type Control = {
|
|||
name: string,
|
||||
position: [number, number],
|
||||
icon: Icon,
|
||||
iconColor?: (state: State) => Color,
|
||||
ui: Array<ControlUI>
|
||||
};
|
||||
export type Controls = Map<string, Control>;
|
||||
|
|
|
|||
|
|
@ -1,54 +1,85 @@
|
|||
// @flow
|
||||
import * as React from "react";
|
||||
import React from "react";
|
||||
import ReactIcon from "@mdi/react";
|
||||
import ReactContext from "mqtt/context";
|
||||
import { hex, type Color } from "./colors"
|
||||
import * as mdiIcons from "@mdi/js"
|
||||
|
||||
export opaque type RawIcon: string = string;
|
||||
|
||||
export type Icon = (State) => RawIcon;
|
||||
|
||||
export const rawMdi = (name: string): RawIcon => {
|
||||
return `mdi ${name.split(" ").map((icon) => "mdi-".concat(icon)).join(" ")}`;
|
||||
export type Icon = {
|
||||
render: (s: State) => React.Node,
|
||||
size: (n: number) => Icon,
|
||||
rotate: (n: number) => Icon,
|
||||
flip: () => Icon,
|
||||
flipV: () => Icon,
|
||||
color: (c: Color | (State) => Color) => Icon
|
||||
};
|
||||
|
||||
export const mdi = (icon: string) => () => rawMdi(icon);
|
||||
type IconPropHelper = {
|
||||
size?: number,
|
||||
rotate?: number,
|
||||
horizontal?: boolean,
|
||||
vertical?: boolean,
|
||||
color?: Color
|
||||
};
|
||||
|
||||
export const mdiBattery = (topic: string) => (state: State) => {
|
||||
export const svg = (data: string, props?: IconPropHelper): Icon => {
|
||||
const propColor = ((c: Color | (State) => Color) => (state: State) => {
|
||||
if (typeof c === "function") {
|
||||
return c(state);
|
||||
}
|
||||
return c;
|
||||
})(props?.color ?? "black");
|
||||
return {
|
||||
render: (state) => (
|
||||
<ReactIcon path={data} size={props?.size ?? 1.5}
|
||||
rotate={props?.rotate ?? 0}
|
||||
horizontal={props?.horizontal ?? false}
|
||||
vertical={props?.vertical ?? false}
|
||||
color={propColor(state)}
|
||||
/>
|
||||
),
|
||||
size: (n: number) => svg(data, {...props, size: n}),
|
||||
rotate: (n: number) => svg(data, {...props, rotate: n}),
|
||||
flip: () => svg(data, {...props, horizontal: !props?.horizontal ?? true}),
|
||||
flipV: () => svg(data, {...props, vertical: !props?.vertical ?? true}),
|
||||
color: (c: Color | (State) => Color) => svg(data, {...props, color: c})
|
||||
};
|
||||
}
|
||||
|
||||
export const withState = (f: (s: State) => Icon): Icon => {
|
||||
return {
|
||||
render: (state) => f(state).render(state),
|
||||
size: () => withState(f),
|
||||
rotate: () => withState(f),
|
||||
flip: () => withState(f),
|
||||
flipV: () => withState(f),
|
||||
color: () => withState(f)
|
||||
};
|
||||
}
|
||||
|
||||
export const mdiBattery = (topic: string): Icon => withState((state) => {
|
||||
const rawval = state[topic];
|
||||
const val = parseInt(rawval, 10);
|
||||
if (isNaN(val)) {
|
||||
return rawMdi("battery-unknown");
|
||||
return svg(mdiIcons.mdiBatteryUnknown);
|
||||
} else if (val > 95) {
|
||||
return rawMdi("battery");
|
||||
return svg(mdiIcons.mdiBattery);
|
||||
} else if (val > 85) {
|
||||
return rawMdi("battery-90");
|
||||
return svg(mdiIcons.mdiBattery90);
|
||||
} else if (val > 75) {
|
||||
return rawMdi("battery-80");
|
||||
return svg(mdiIcons.mdiBattery80);
|
||||
} else if (val > 65) {
|
||||
return rawMdi("battery-70");
|
||||
return svg(mdiIcons.mdiBattery70);
|
||||
} else if (val > 55) {
|
||||
return rawMdi("battery-60");
|
||||
return svg(mdiIcons.mdiBattery60);
|
||||
} else if (val > 45) {
|
||||
return rawMdi("battery-50");
|
||||
return svg(mdiIcons.mdiBattery50);
|
||||
} else if (val > 35) {
|
||||
return rawMdi("battery-40");
|
||||
return svg(mdiIcons.mdiBattery40);
|
||||
} else if (val > 25) {
|
||||
return rawMdi("battery-30");
|
||||
return svg(mdiIcons.mdiBattery30);
|
||||
} else if (val > 15) {
|
||||
return rawMdi("battery-20");
|
||||
return svg(mdiIcons.mdiBattery20);
|
||||
}
|
||||
return rawMdi("battery-10");
|
||||
};
|
||||
|
||||
export const renderRawIcon =
|
||||
(icon: RawIcon, extraClass?: string): React.Node => {
|
||||
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>
|
||||
);
|
||||
};
|
||||
return svg(mdiIcons.mdiBattery10);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ import ReactDOM from "react-dom";
|
|||
|
||||
import App from "components/App";
|
||||
|
||||
import "../node_modules/@mdi/font/css/materialdesignicons.min.css";
|
||||
import "../css/styles.css";
|
||||
|
||||
import type { Config } from "config/flowtypes";
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue