From 43a33c3ab3e742ee67f54dff9c64334c28d5869e Mon Sep 17 00:00:00 2001 From: uwap Date: Mon, 5 Oct 2020 22:02:30 +0200 Subject: [PATCH 1/7] Fix minor eslint warnings --- config/rzl/index.js | 2 +- src/components/UiItems/base.js | 6 +++--- src/components/UiItems/index.js | 3 ++- src/config/icon.js | 3 +-- src/config/types.js | 2 +- src/connectMqtt.js | 2 +- src/mqtt/context.js | 4 ++-- 7 files changed, 11 insertions(+), 11 deletions(-) diff --git a/config/rzl/index.js b/config/rzl/index.js index ef8f322..f084566 100644 --- a/config/rzl/index.js +++ b/config/rzl/index.js @@ -633,7 +633,7 @@ const config: Config = { position: [1413, 500], icon: mdi("pool"), iconColor: ({whirlpoolBubbles}) => - (parseInt(whirlpoolBubbles) > 0 ? hex("#00ff00") : hex("#000000")), + (parseInt(whirlpoolBubbles, 10) > 0 ? hex("#00ff00") : hex("#000000")), ui: [ { type: "text", diff --git a/src/components/UiItems/base.js b/src/components/UiItems/base.js index dd359a6..2b7a5b9 100644 --- a/src/components/UiItems/base.js +++ b/src/components/UiItems/base.js @@ -11,9 +11,9 @@ import { renderRawIcon } from "config/icon"; import type { Icon } from "config/icon"; export type Helpers = { - Icon: (props: Object) => React.Node, - Label: (props: Object) => React.Node, - Action: (props: Object) => React.Node + Icon: (props: { item: { +icon?: Icon }, state: State }) => React.Node, + Label: (props: {}) => React.Node, + Action: (props: {}) => React.Node }; export type BaseComponent = ( diff --git a/src/components/UiItems/index.js b/src/components/UiItems/index.js index 8c8e44f..875fd60 100644 --- a/src/components/UiItems/index.js +++ b/src/components/UiItems/index.js @@ -6,10 +6,11 @@ import Link from "./Link"; import Slider from "./Slider"; import Text from "./Text"; import Progress from "./Progress"; +import * as React from "react"; import type { ControlUI } from "config/flowtypes"; -const Control = ({item}: {item: ControlUI}) => { +const Control = ({item}: {item: ControlUI}): React.Node => { switch (item.type) { case "toggle": { return Toggle.component(item); diff --git a/src/config/icon.js b/src/config/icon.js index a8a44b1..6844980 100644 --- a/src/config/icon.js +++ b/src/config/icon.js @@ -35,9 +35,8 @@ export const mdiBattery = (topic: string) => (state: State) => { return rawMdi("battery-30"); } else if (val > 15) { return rawMdi("battery-20"); - } else { - return rawMdi("battery-10"); } + return rawMdi("battery-10"); }; export const renderRawIcon = diff --git a/src/config/types.js b/src/config/types.js index 6867807..478108d 100644 --- a/src/config/types.js +++ b/src/config/types.js @@ -17,7 +17,7 @@ export const option = (values: TypeOptionParam): TopicType => { return values.otherwise; } else { throw new Error( - `Value ${x.toString()} cannot by mapped by the option parameters given` + `Value ${x.toString()} cannot be mapped by the option parameters given` ); } }; diff --git a/src/connectMqtt.js b/src/connectMqtt.js index 0085f28..ee1af34 100644 --- a/src/connectMqtt.js +++ b/src/connectMqtt.js @@ -49,7 +49,7 @@ export default function connectMqtt( } }); return (topic: string, message: Buffer) => { - client.publish(topic, message, null, (error) => { + client.publish(topic, message, {}, (error) => { if (error == null && settings.onMessageSent != null) { settings.onMessageSent(topic, message); } diff --git a/src/mqtt/context.js b/src/mqtt/context.js index bb14c9c..1f47c9f 100644 --- a/src/mqtt/context.js +++ b/src/mqtt/context.js @@ -3,10 +3,10 @@ import React from "react"; export type MqttContextValue = { state: State, - changeState: (topic: string, value: string) => void + changeState: (topic: string, value: string) => State }; export default React.createContext({ state: {}, - changeState: (_topic, _val) => {} + changeState: (_topic, _val) => ({}) }); From 856aab41ad0ff1d7b44c4480649095b45b11346c Mon Sep 17 00:00:00 2001 From: uwap Date: Thu, 8 Oct 2020 08:36:56 +0200 Subject: [PATCH 2/7] 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 --- config/entropia/index.js | 10 +-- config/rzl/index.js | 150 ++++++++++++++++----------------- config/rzl/kitchen.js | 49 +++++------ config/rzl/olymp.js | 32 ++++--- config/rzl/onkyo.js | 17 ++-- config/rzl/utils.js | 13 +-- config/uwap-home/index.js | 126 +++++++++++++-------------- package.json | 3 +- src/components/App.js | 10 ++- src/components/ControlMap.js | 13 +-- src/components/SideBar.js | 6 +- src/components/TopBar.js | 4 +- src/components/UiItems/Link.js | 3 +- src/components/UiItems/base.js | 7 +- src/config/flowtypes.js | 1 - src/config/icon.js | 101 ++++++++++++++-------- src/index.jsx | 1 - yarn.lock | 11 ++- 18 files changed, 288 insertions(+), 269 deletions(-) diff --git a/config/entropia/index.js b/config/entropia/index.js index c3237a8..e28e63e 100644 --- a/config/entropia/index.js +++ b/config/entropia/index.js @@ -2,7 +2,8 @@ import type { Config } from "config/flowtypes"; import { hex } from "config/colors"; import * as types from "config/types"; -import { mdi } from "config/icon"; +import * as icons from "@mdi/js"; +import { svg } from "config/icon"; const config: Config = { space: { @@ -32,20 +33,19 @@ const config: Config = { hauptraumTableLight: { name: "Hauptraum Tisch", position: [450, 450], - icon: mdi("white-balance-iridescent"), - iconColor: () => hex("#000000"), + icon: svg(icons.mdiWhiteBalanceIridescent), ui: [ { type: "toggle", text: "Licht", topic: "hauptraumTableLight", - icon: mdi("power") + icon: svg(icons.mdiPower) }, { type: "toggle", text: "Licht", topic: "hauptraumTableLightOnHack", - icon: mdi("power") + icon: svg(icons.mdiPower) } ] } diff --git a/config/rzl/index.js b/config/rzl/index.js index f084566..dd5bebf 100644 --- a/config/rzl/index.js +++ b/config/rzl/index.js @@ -2,8 +2,9 @@ import type { Config } from "config/flowtypes"; import * as types from "config/types"; import { hex, rainbow } from "config/colors"; -import { mdi, rawMdi } from "config/icon"; +import { svg, withState } from "config/icon"; import { esper, tasmota } from "./utils"; +import * as icons from "@mdi/js" import * as onkyo from "./onkyo"; import * as olymp from "./olymp"; @@ -275,29 +276,28 @@ const config: Config = { ledStahltrager: { name: "LED Stahlträger", position: [340, 590], - icon: mdi("white-balance-iridescent"), - iconColor: ({ledStahltraeger}) => - (ledStahltraeger === "on" ? rainbow : hex("#000000")), + icon: svg(icons.mdiWhiteBalanceIridescent).color(({ledStahltraeger}) => + ledStahltraeger === "on" ? rainbow : hex("#000000")), ui: [ { type: "toggle", text: "Stahlträger LED", topic: "ledStahltraeger", - icon: mdi("power") + icon: svg(icons.mdiPower) } ] }, snackbar: { name: "Snackbar", position: [510, 500], - icon: mdi("fridge"), - iconColor: tasmota.iconColor("snackbar", hex("#E20074")), + icon: svg(icons.mdiFridge).color( + tasmota.iconColor("snackbar", hex("#E20074"))), ui: [ { type: "toggle", text: "Snackbar", topic: "snackbar", - icon: mdi("power") + icon: svg(icons.mdiPower) }, { type: "section", @@ -307,7 +307,7 @@ const config: Config = { type: "text", text: "LED-Streifen", topic: "snackbarLedOnline", - icon: mdi("white-balance-iridescent") + icon: svg(icons.mdiWhiteBalanceIridescent) }, { type: "dropDown", @@ -326,7 +326,7 @@ const config: Config = { "11": "Rainbow Pattern", "12": "Fire Pattern" }, - icon: mdi("settings"), + icon: svg(icons.mdiCog), enableCondition: ({ snackbarLedOnline }) => snackbarLedOnline === "on" }, { @@ -335,7 +335,7 @@ const config: Config = { topic: "snackbarDimmmer", min: 0, max: 100, - icon: mdi("brightness-7"), + icon: svg(icons.mdiBrightness7), enableCondition: ({ snackbarLedOnline }) => snackbarLedOnline === "on" }, { @@ -344,7 +344,7 @@ const config: Config = { topic: "snackbarSpeed", min: 0, max: 20, - icon: mdi("speedometer"), + icon: svg(icons.mdiSpeedometer), enableCondition: ({ snackbarLedOnline }) => snackbarLedOnline === "on" } ] @@ -352,155 +352,151 @@ const config: Config = { twinkle: { name: "Twinkle", position: [530, 560], - icon: ({twinkle}) => - (twinkle === "on" ? rawMdi("led-on flip-v") : rawMdi("led-off flip-v")), - iconColor: ({twinkle}) => (twinkle === "on" ? rainbow : hex("#000000")), + icon: withState(({twinkle}) => + twinkle === "on" ? svg(icons.mdiLedOn).flipV().color(rainbow) + : svg(icons.mdiLedOff).flipV() + ), ui: [ { type: "toggle", text: "Twinkle", topic: "twinkle", - icon: mdi("power") + icon: svg(icons.mdiPower) } ] }, fan: { name: "Ventilator", position: [530, 440], - icon: mdi("fan"), - iconColor: ({fan}) => (fan === "on" ? hex("#00FF00") : hex("#000000")), + icon: svg(icons.mdiFan).color(({fan}) => + fan === "on" ? hex("#00FF00") : hex("#000000")), ui: [ { type: "toggle", text: "Ventilator", topic: "fan", - icon: mdi("power") + icon: svg(icons.mdiPower) } ] }, cashdesk: { name: "Cashdesk", position: [510, 467], - icon: mdi("coin"), + icon: svg(icons.mdiCurrencyUsdCircle), ui: [ { type: "link", link: "http://cashdesk.rzl:8000/", text: "Open Cashdesk", - icon: mdi("open-in-new") + icon: svg(icons.mdiOpenInNew) } ] }, flyfry: { name: "Fliegenbratgerät", position: [450, 570], - icon: mdi("fire"), - iconColor: ({flyfry}) => - (flyfry === "on" ? hex("#6666FF") : hex("#000000")), + icon: svg(icons.mdiFire).color(({flyfry}) => + flyfry === "on" ? hex("#6666FF") : hex("#000000")), ui: esper.statistics("flyfry", [ { type: "toggle", text: "Fliegenbratgerät", topic: "flyfry", - icon: mdi("power") + icon: svg(icons.mdiPower) } ]) }, projector: { name: "Beamer", position: [380, 590], - icon: mdi("projector flip-v"), - iconColor: ({projector}) => + icon: svg(icons.mdiProjector).flipV().color(({projector}) => ({ transientOn: hex("#b3b300"), transientOff: hex("#b3b300"), on: hex("#00ff00"), off: hex("#000000"), unknown: hex("#888888") - })[projector], + })[projector]), ui: [ { type: "toggle", text: "Beamer", topic: "projector", toggled: (val) => val === "transientOn" || val === "on", - icon: mdi("power") + icon: svg(icons.mdiPower) } ] }, loetarbeitsplatz4: { name: "Lötarbeitsplatz", position: [205, 455], - icon: mdi("eyedropper-variant"), - iconColor: ({loetarbeitsplatz4}) => - (loetarbeitsplatz4 === "on" ? hex("#FF0000") : hex("#000000")), + icon: svg(icons.mdiEyedropperVariant).color(({loetarbeitsplatz4}) => + loetarbeitsplatz4 === "on" ? hex("#FF0000") : hex("#000000")), ui: [ { type: "text", text: "Status", topic: "loetarbeitsplatz4", - icon: mdi("eyedropper-variant") + icon: svg(icons.mdiEyedropperVariant) } ] }, loetarbeitsplatz5: { name: "Lötarbeitsplatz", position: [205, 405], - icon: mdi("eyedropper-variant"), - iconColor: ({loetarbeitsplatz5}) => - (loetarbeitsplatz5 === "on" ? hex("#FF0000") : hex("#000000")), + icon: svg(icons.mdiEyedropperVariant).color(({loetarbeitsplatz4}) => + loetarbeitsplatz4 === "on" ? hex("#FF0000") : hex("#000000")), ui: [ { type: "text", text: "Status", topic: "loetarbeitsplatz5", - icon: mdi("eyedropper-variant") + icon: svg(icons.mdiEyedropperVariant) } ] }, door: { name: "Tür", position: [455, 350], - icon: mdi("swap-vertical"), - iconColor: ({doorStatus}) => - (doorStatus === "on" ? hex("#00FF00") : hex("#FF0000")), + icon: svg(icons.mdiSwapVertical).color(({doorStatus}) => + doorStatus === "on" ? hex("#00FF00") : hex("#FF0000")), ui: [ { type: "link", link: "http://s.rzl.so", text: "Open Status Page", - icon: mdi("open-in-new") + icon: svg(icons.mdiOpenInNew) }, { type: "link", // eslint-disable-next-line max-len link: "http://kunterbunt.vm.rzl/dashboard/db/allgemeines-copy-ranlvor?orgId=1", text: "RZL-Dashboard", - icon: mdi("open-in-new") + icon: svg(icons.mdiOpenInNew) }, { type: "text", text: "Anwesend", topic: "presenceStatus", - icon: mdi("account") + icon: svg(icons.mdiAccount) }, { type: "text", text: "Devices", topic: "devicesStatus", - icon: mdi("wifi") + icon: svg(icons.mdiWifi) }, { type: "toggle", text: "Deko", topic: "deko", - icon: mdi("invert-colors") + icon: svg(icons.mdiInvertColors) }, { type: "text", text: "Power Hauptraum", topic: "powerConsumption", - icon: mdi("speedometer") + icon: svg(icons.mdiSpeedometer) } ] @@ -508,56 +504,56 @@ const config: Config = { infoscreen: { name: "Infoscreen", position: [255, 495], - icon: mdi("television-guide flip-v"), - iconColor: tasmota.iconColor("infoscreen", hex("#4444FF")), + icon: svg(icons.mdiTelevisionGuide).flipV().color( + tasmota.iconColor("infoscreen", hex("#4444FF")) + ), ui: [ { type: "toggle", text: "Infoscreen", topic: "infoscreen", - icon: mdi("power") + icon: svg(icons.mdiPower) }, { type: "link", link: "http://cashdesk.rzl:3030/rzl", text: "Open Infoscreen", - icon: mdi("open-in-new") + icon: svg(icons.mdiOpenInNew) } ] }, pilze: { name: "Pilze", position: [48, 499], - icon: ({pilze}) => - (pilze === "on" ? rawMdi("led-on") : rawMdi("led-off")), - iconColor: tasmota.iconColor("pilze", rainbow), + icon: withState(({pilze}) => + pilze === "on" ? svg(icons.mdiLedOn) : svg(icons.mdiLedOff)).color( + tasmota.iconColor("pilze", rainbow)), ui: [ { type: "toggle", text: "Pilze", topic: "pilze", - icon: mdi("power") + icon: svg(icons.mdiPower) } ] }, printer3D: { name: "Ultimaker 3", position: [754, 560], - icon: mdi("printer-3d"), - iconColor: ({printer3DStatus}) => + icon: svg(icons.mdiPrinter3d).color(({printer3DStatus}) => ({ awaitingInteraction: hex("#b3b300"), printing: hex("#00ff00"), idle: hex("#000000"), unavailable: hex("#888888"), error: hex("#ff0000") - })[printer3DStatus], + })[printer3DStatus]), ui: [ { type: "link", link: "http://ultimaker.rzl/", text: "Open Webinterface", - icon: mdi("open-in-new") + icon: svg(icons.mdiOpenInNew) }, { type: "section", @@ -565,7 +561,7 @@ const config: Config = { }, { type: "progress", - icon: mdi("rotate-right"), + icon: svg(icons.mdiRotateRight), min: 0, max: 1, text: "Printing Progress", @@ -574,7 +570,7 @@ const config: Config = { { type: "text", text: "Time Left", - icon: mdi("clock"), + icon: svg(icons.mdiClock), topic: "printer3Dremaining" } ] @@ -582,47 +578,45 @@ const config: Config = { partkeepr: { name: "Partkeepr", position: [48, 450], - icon: mdi("chip"), + icon: svg(icons.mdiChip), ui: [ { type: "link", link: "http://partkeepr.rzl/", text: "Open Partkeepr", - icon: mdi("open-in-new") + icon: svg(icons.mdiOpenInNew) } ] }, printerAnnette: { name: "Drucker", position: [800, 350], - icon: mdi("printer"), - iconColor: tasmota.iconColor("printerAnnette"), + icon: svg(icons.mdiPrinter).color(tasmota.iconColor("printerAnnette")), ui: [ { type: "toggle", text: "Drucker", topic: "printerAnnette", - icon: mdi("power") + icon: svg(icons.mdiPower) }, { type: "link", link: "http://annette.rzl/", text: "Open Annette", - icon: mdi("open-in-new") + icon: svg(icons.mdiOpenInNew) } ] }, nebenraumPowerStatus: { name: "Strom Fablab", position: [613, 537], - icon: ({nebenraumPowerStatus}) => - (nebenraumPowerStatus === "on" ? rawMdi("flash") : rawMdi("flash-off")), - iconColor: ({nebenraumPowerStatus}) => - (nebenraumPowerStatus === "on" ? hex("#00ff00") : hex("#000000")), + icon: withState(({nebenraumPowerStatus}) => + nebenraumPowerStatus === "on" ? svg(icons.mdiFlash).color(hex("#00FF00")) + : svg(icons.mdiFlashOff)), ui: [ { type: "text", - icon: mdi("power"), + icon: svg(icons.mdiPower), text: "Strom Fablab", topic: "nebenraumPowerStatus" } @@ -631,19 +625,19 @@ const config: Config = { whirlpool: { name: "Vorstandswhirlpool", position: [1413, 500], - icon: mdi("pool"), - iconColor: ({whirlpoolBubbles}) => - (parseInt(whirlpoolBubbles, 10) > 0 ? hex("#00ff00") : hex("#000000")), + icon: svg(icons.mdiPool).color( + ({whirlpoolBubbles}) => + parseInt(whirlpoolBubbles, 10) > 0 ? hex("#00ff00") : hex("#000000")), ui: [ { type: "text", - icon: mdi("oil-temperature"), + icon: svg(icons.mdiOilTemperature), text: "Temperatur", topic: "whirlpoolTemperature" }, { type: "text", - icon: mdi("oil-temperature"), + icon: svg(icons.mdiOilTemperature), text: "Temperatur Sollwert", topic: "whirlpoolTemperatureSetpoint" }, @@ -652,7 +646,7 @@ const config: Config = { min: 4, max: 100, text: "Temperatur Sollwert", - icon: mdi("oil-temperature"), + icon: svg(icons.mdiOilTemperature), topic: "whirlpoolTemperatureSetpoint" }, { @@ -660,7 +654,7 @@ const config: Config = { min: 0, max: 9, text: "Bubbles", - icon: mdi("chart-bubble"), + icon: svg(icons.mdiChartBubble), topic: "whirlpoolBubbles" } ] diff --git a/config/rzl/kitchen.js b/config/rzl/kitchen.js index a98dee5..86c9558 100644 --- a/config/rzl/kitchen.js +++ b/config/rzl/kitchen.js @@ -1,9 +1,10 @@ // @flow import type { Topics, Controls } from "config/flowtypes"; -import { mdi, mdiBattery } from "config/icon"; +import { svg, mdiBattery } from "config/icon"; import { hex } from "config/colors"; import * as types from "config/types"; import { floalt, tradfri, tasmota } from "./utils"; +import * as icons from "@mdi/js" export const topics: Topics = { //Kuechen-Floalts @@ -65,7 +66,7 @@ export const controls: Controls = { kitchenLight: { name: "Deckenlicht Küche", position: [325, 407], - icon: mdi("ceiling-light"), + icon: svg(icons.mdiCeilingLight), ui: [ { type: "toggle", @@ -74,14 +75,14 @@ export const controls: Controls = { toggled: (n) => parseInt(n, 10) > 0, topic: "kitchenLightBrightness", text: "Ein/Ausschalten", - icon: mdi("power") + icon: svg(icons.mdiPower) }, { type: "slider", min: 0, max: 100, text: "Helligkeit", - icon: mdi("brightness-7"), + icon: svg(icons.mdiBrightness7), topic: "kitchenLightBrightness" }, { @@ -89,7 +90,7 @@ export const controls: Controls = { min: 0, max: 100, text: "Farbtemperatur", - icon: mdi("weather-sunset-down"), + icon: svg(icons.mdiWeatherSunsetDown), topic: "kitchenLightColor" }, { @@ -101,7 +102,7 @@ export const controls: Controls = { min: 0, max: 100, text: "Helligkeit", - icon: mdi("brightness-7"), + icon: svg(icons.mdiBrightness7), topic: floalt.brightness("65537") }, { @@ -109,7 +110,7 @@ export const controls: Controls = { min: 0, max: 100, text: "Farbtemperatur", - icon: mdi("weather-sunset-down"), + icon: svg(icons.mdiWeatherSunsetDown), topic: floalt.color("65537") }, { @@ -121,7 +122,7 @@ export const controls: Controls = { min: 0, max: 100, text: "Helligkeit", - icon: mdi("brightness-7"), + icon: svg(icons.mdiBrightness7), topic: floalt.brightness("65538") }, { @@ -129,7 +130,7 @@ export const controls: Controls = { min: 0, max: 100, text: "Farbtemperatur", - icon: mdi("weather-sunset-down"), + icon: svg(icons.mdiWeatherSunsetDown), topic: floalt.color("65538") }, { @@ -141,7 +142,7 @@ export const controls: Controls = { min: 0, max: 100, text: "Helligkeit", - icon: mdi("brightness-7"), + icon: svg(icons.mdiBrightness7), topic: floalt.brightness("65539") }, { @@ -149,7 +150,7 @@ export const controls: Controls = { min: 0, max: 100, text: "Farbtemperatur", - icon: mdi("weather-sunset-down"), + icon: svg(icons.mdiWeatherSunsetDown), topic: floalt.color("65539") }, { @@ -161,7 +162,7 @@ export const controls: Controls = { min: 0, max: 100, text: "Helligkeit", - icon: mdi("brightness-7"), + icon: svg(icons.mdiBrightness7), topic: floalt.brightness("65540") }, { @@ -169,7 +170,7 @@ export const controls: Controls = { min: 0, max: 100, text: "Farbtemperatur", - icon: mdi("weather-sunset-down"), + icon: svg(icons.mdiWeatherSunsetDown), topic: floalt.color("65540") } ] @@ -177,7 +178,7 @@ export const controls: Controls = { kitchenSinkLight: { name: "Licht Spüle", position: [300, 345], - icon: mdi("wall-sconce-flat"), + icon: svg(icons.mdiWallSconceFlat), ui: [ { type: "toggle", @@ -186,14 +187,14 @@ export const controls: Controls = { toggled: (n) => parseInt(n, 10) > 0, topic: "kitchenSinkLightBrightness", text: "Ein/Ausschalten", - icon: mdi("power") + icon: svg(icons.mdiPower) }, { type: "slider", min: 0, max: 100, text: "Helligkeit", - icon: mdi("brightness-7"), + icon: svg(icons.mdiBrightness7), topic: "kitchenSinkLightBrightness" } ] @@ -201,7 +202,7 @@ export const controls: Controls = { kitchenCounterLight: { name: "Deckenlicht Theke", position: [400, 440], - icon: mdi("ceiling-light"), + icon: svg(icons.mdiCeilingLight), ui: [ { type: "section", @@ -212,7 +213,7 @@ export const controls: Controls = { min: 0, max: 100, text: "Helligkeit", - icon: mdi("brightness-7"), + icon: svg(icons.mdiBrightness7), topic: floalt.brightness("65544") }, { @@ -220,7 +221,7 @@ export const controls: Controls = { min: 0, max: 100, text: "Farbtemperatur", - icon: mdi("weather-sunset-down"), + icon: svg(icons.mdiWeatherSunsetDown), topic: floalt.color("65544") }, { @@ -232,7 +233,7 @@ export const controls: Controls = { min: 0, max: 100, text: "Helligkeit", - icon: mdi("brightness-7"), + icon: svg(icons.mdiBrightness7), topic: floalt.brightness("65543") }, { @@ -240,7 +241,7 @@ export const controls: Controls = { min: 0, max: 100, text: "Farbtemperatur", - icon: mdi("weather-sunset-down"), + icon: svg(icons.mdiWeatherSunsetDown), topic: floalt.color("65543") } ] @@ -248,21 +249,21 @@ export const controls: Controls = { lichtDunstabzug: { name: "Licht Dunstabzugshaube", position: [252, 405], - icon: mdi("ceiling-light"), + icon: svg(icons.mdiCeilingLight), iconColor: tasmota.iconColor("lichtDunstabzug"), ui: [ { type: "toggle", text: "Licht Dunstabzugshaube", topic: "lichtDunstabzug", - icon: mdi("power") + icon: svg(icons.mdiPower) } ] }, remotes: { name: "Fernbedinungen", position: [400, 344], - icon: mdi("light-switch"), + icon: svg(icons.mdiLightSwitch), iconColor: (state) => //if any remote is low make icon red (["65536", "65542", "65546", "65547"] .some((x) => state[tradfri.remote.low(x)] === "true") diff --git a/config/rzl/olymp.js b/config/rzl/olymp.js index 02a2d50..40a158f 100644 --- a/config/rzl/olymp.js +++ b/config/rzl/olymp.js @@ -1,9 +1,10 @@ // @flow import type { Topics, Controls } from "config/flowtypes"; -import { mdi } from "config/icon"; +import { svg } from "config/icon"; import { hex, rainbow } from "config/colors"; import * as types from "config/types"; import { tasmota, esper } from "./utils"; +import * as icons from "@mdi/js" export const topics: Topics = { ...tasmota.topics("8", "ledOlymp"), @@ -47,66 +48,63 @@ export const controls: Controls = { ledOlymp: { name: "LED Olymp", position: [196, 154], - icon: mdi("white-balance-iridescent rotate-45"), - iconColor: tasmota.iconColor("ledOlymp", rainbow), + icon: svg(icons.mdiWhiteBalanceIridescent).rotate(45).color( + tasmota.iconColor("ledOlymp", rainbow)), ui: [ { type: "toggle", text: "LED Olymp", topic: "ledOlymp", - icon: mdi("power") + icon: svg(icons.mdiPower) } ] }, videogames: { name: "Videospiele", position: [100, 100], - icon: mdi("gamepad-variant"), - iconColor: ({videogames}) => - (videogames === "on" ? hex("#00FF00") : hex("#000000")), + icon: svg(icons.mdiGamepadVariant).color(({videogames}) => + videogames === "on" ? hex("#00FF00") : hex("#000000")), ui: [ { type: "toggle", text: "Videospiele", topic: "videogames", - icon: mdi("power") + icon: svg(icons.mdiPower) } ] }, olympPC: { name: "Rechner", position: [297, 90], - icon: mdi("desktop-classic"), - iconColor: ({olympPC}) => - (olympPC === "on" ? hex("#00FF00") : hex("#000000")), + icon: svg(icons.mdiDesktopClassic).color(({olympPC}) => + olympPC === "on" ? hex("#00FF00") : hex("#000000")), ui: [ { type: "toggle", text: "Rechner", topic: "olympPC", - icon: mdi("power") + icon: svg(icons.mdiPower) } ] }, rundumleuchte: { name: "Rundumleuchte", position: [310, 275], - icon: mdi("alarm-light"), - iconColor: ({rundumleuchte}) => - (rundumleuchte === "on" ? hex("#F0DF10") : hex("#000000")), + icon: svg(icons.mdiAlarmLight).color(({rundumleuchte}) => + rundumleuchte === "on" ? hex("#F0DF10") : hex("#000000")), ui: [ { type: "toggle", text: "Rundumleuchte", topic: "rundumleuchte", - icon: mdi("power") + icon: svg(icons.mdiPower) } ] }, alarm: { name: "Alarm", position: [340, 250], - icon: mdi("alarm-bell"), + icon: svg(icons.mdiAlarmBell), iconColor: () => hex("#000000"), ui: esper.statistics("alarm") } diff --git a/config/rzl/onkyo.js b/config/rzl/onkyo.js index 7d1e866..d1bca0a 100644 --- a/config/rzl/onkyo.js +++ b/config/rzl/onkyo.js @@ -1,8 +1,9 @@ // @flow import type { Topics, Controls } from "config/flowtypes"; -import { mdi } from "config/icon"; +import { svg } from "config/icon"; import { hex } from "config/colors"; import * as types from "config/types"; +import * as icons from "@mdi/js"; export const topics: Topics = { onkyoConnection: { @@ -132,12 +133,12 @@ export const controls: Controls = { iconColor: ({onkyoConnection, onkyoPower}) => (onkyoConnection !== "connected" ? hex("#888888") : (onkyoPower === "on" ? hex("#00FF00") : hex("#000000"))), - icon: mdi("audio-video"), + icon: svg(icons.mdiAudioVideo), ui: [ { type: "toggle", text: "Power", - icon: mdi("power"), + icon: svg(icons.mdiPower), topic: "onkyoPower", enableCondition: (state) => state.onkyoConnection === "connected" }, @@ -151,14 +152,14 @@ export const controls: Controls = { topic: "onkyoVolume", min: 0, max: 50, - icon: mdi("volume-high"), + icon: svg(icons.mdiVolumeHigh), enableCondition: (state) => state.onkyoConnection === "connected" }, { type: "toggle", text: "Mute", topic: "onkyoMute", - icon: mdi("volume-off"), + icon: svg(icons.mdiVolumeOff), enableCondition: (state) => state.onkyoConnection === "connected" }, { @@ -176,7 +177,7 @@ export const controls: Controls = { pult: "Pult", front: "Front HDMI" }, - icon: mdi("usb"), + icon: svg(icons.mdiUsb), enableCondition: (state) => state.onkyoConnection === "connected" }, { @@ -200,7 +201,7 @@ export const controls: Controls = { somafmChrismasLounge: "Christmas Lounge (SomaFM)", unknown: "Unknown" }, - icon: mdi("radio"), + icon: svg(icons.mdiRadio), enableCondition: (state) => state.onkyoConnection === "connected" && state.onkyoInputs === "netzwerk" }, @@ -212,7 +213,7 @@ export const controls: Controls = { type: "link", link: "http://mpd.rzl/mpd/player/index.php", text: "Open MPD Interface", - icon: mdi("open-in-new") + icon: svg(icons.mdiOpenInNew) } ] } diff --git a/config/rzl/utils.js b/config/rzl/utils.js index 3ac8e49..5c5b195 100644 --- a/config/rzl/utils.js +++ b/config/rzl/utils.js @@ -1,8 +1,9 @@ // @flow import type { ControlUI, Topics } from "config/flowtypes"; -import { mdi } from "config/icon"; +import { svg } from "config/icon"; import { hex, type Color } from "config/colors"; import * as types from "config/types"; +import * as icons from "@mdi/js" export const tasmota = { topics: (id: string, name: string): Topics => ({ @@ -106,31 +107,31 @@ const esperStatistics = (name: string, { type: "text", text: "Device Variant", - icon: mdi("chart-donut"), + icon: svg(icons.mdiChartDonut), topic: `esper_${name}_device` }, { type: "text", text: "Version", - icon: mdi("source-branch"), + icon: svg(icons.mdiSourceBranch), topic: `esper_${name}_version` }, { type: "text", text: "IP", - icon: mdi("access-point-network"), + icon: svg(icons.mdiAccessPointNetwork), topic: `esper_${name}_ip` }, { type: "text", text: "RSSI", - icon: mdi("wifi"), + icon: svg(icons.mdiWifi), topic: `esper_${name}_rssi` }, { type: "text", text: "Running since…", - icon: mdi("av-timer"), + icon: svg(icons.mdiAvTimer), topic: `esper_${name}_uptime` } ]) diff --git a/config/uwap-home/index.js b/config/uwap-home/index.js index 05ec243..2cad6f3 100644 --- a/config/uwap-home/index.js +++ b/config/uwap-home/index.js @@ -1,8 +1,9 @@ // @flow import type { Config } from "config/flowtypes"; import * as types from "config/types"; -import { mdi, rawMdi } from "config/icon"; +import { svg, withState } from "config/icon"; import { hex } from "config/colors"; +import * as icons from "@mdi/js" const topicBulbHomeRust = (bulb: string, argument: string) => ({ [`${bulb}${argument}`]: { @@ -100,7 +101,7 @@ const sliderRGB = (bulb: string, argument: string) => ( min: 0, max: 255, text: argument, - icon: mdi("brightness-7"), + icon: svg(icons.mdiBrightness7), topic: `${bulb}${argument}` }] ); @@ -110,7 +111,7 @@ const sliderH = (bulb: string, argument: string) => ( min: 0, max: 360, text: argument, - icon: mdi("brightness-7"), + icon: svg(icons.mdiBrightness7), topic: `${bulb}${argument}` }] ); @@ -121,7 +122,7 @@ const sliderSVXY = (bulb: string, argument: string) => ( max: 1, step: 0.01, text: argument, - icon: mdi("brightness-7"), + icon: svg(icons.mdiBrightness7), topic: `${bulb}${argument}` }] ); @@ -205,36 +206,35 @@ const config: Config = { bedroomLight: { name: "Schlafzimmer", position: [180, 130], - icon: mdi("ceiling-light"), - iconColor: ({bedroomState}) => - (bedroomState === "on" ? hex("#00FF00") : hex("#000000")), + icon: svg(icons.mdiCeilingLight).color(({bedroomState}) => + bedroomState === "on" ? hex("#00FF00") : hex("#000000")), ui: [ { type: "toggle", topic: "bedroomState", text: "Ein/Ausschalten", - icon: mdi("power") + icon: svg(icons.mdiPower) }, { type: "slider", min: 0, max: 255, text: "Helligkeit", - icon: mdi("brightness-7"), + icon: svg(icons.mdiBrightness7), topic: "bedroombrightness" }, { type: "toggle", topic: "bedroomWakeup", text: "Lichtwecker", - icon: mdi("weather-sunset-up") + icon: svg(icons.mdiWeatherSunsetUp) }, { type: "slider", min: 250, max: 454, text: "Farbtemperatur", - icon: mdi("weather-sunset-down"), + icon: svg(icons.mdiWeatherSunsetDown), topic: "bedroomcolor_temp" } ] @@ -242,26 +242,25 @@ const config: Config = { bedroomFan: { name: "Lüftung Schlafzimmer", position: [140, 25], - icon: mdi("fan"), - iconColor: ({fanBedroomState}) => - (fanBedroomState === "on" ? hex("#00FF00") : hex("#000000")), + icon: svg(icons.mdiFan).color(({fanBedroomState}) => + fanBedroomState === "on" ? hex("#00FF00") : hex("#000000")), ui: [ { type: "toggle", topic: "fanBedroomState", text: "Ein/Ausschalten", - icon: mdi("power") + icon: svg(icons.mdiPower) }, { type: "toggle", topic: "fanBedroomAuto", text: "Automatik", - icon: mdi("air-conditioner") + icon: svg(icons.mdiAirConditioner) }, { type: "text", text: "Zieltemperatur", - icon: mdi("temperature-celsius"), + icon: svg(icons.mdiTemperatureCelsius), topic: "fanBedroomTarget" }, { @@ -270,7 +269,7 @@ const config: Config = { max: 25, step: 0.1, text: "Zieltemperatur", - icon: mdi("oil-temperature"), + icon: svg(icons.mdiOilTemperature), topic: "fanBedroomTarget" } ] @@ -278,24 +277,23 @@ const config: Config = { officeSpeaker: { name: "Lautsprecher", position: [245, 658], - icon: ({speakerOfficeState}) => - (speakerOfficeState === "on" ? rawMdi("volume-high") - : rawMdi("volume-off")), - iconColor: ({speakerOfficeState}) => - (speakerOfficeState === "on" ? hex("#00FF00") : hex("#000000")), + icon: withState(({speakerOfficeState}) => + speakerOfficeState !== "on" ? svg(icons.mdiVolumeOff) + : svg(icons.mdiVolumeHigh).color(hex("#00FF00")) + ), ui: [ { type: "toggle", topic: "speakerOfficeState", text: "Ein/Ausschalten", - icon: mdi("power") + icon: svg(icons.mdiPower) } ] }, officeFan: { name: "Lüftung Büro", position: [140, 658], - icon: mdi("fan"), + icon: svg(icons.mdiFan), iconColor: ({fanOfficeState}) => (fanOfficeState === "on" ? hex("#00FF00") : hex("#000000")), ui: [ @@ -303,18 +301,18 @@ const config: Config = { type: "toggle", topic: "fanOfficeState", text: "Ein/Ausschalten", - icon: mdi("power") + icon: svg(icons.mdiPower) }, { type: "toggle", topic: "fanOfficeAuto", text: "Automatik", - icon: mdi("air-conditioner") + icon: svg(icons.mdiAirConditioner) }, { type: "text", text: "Zieltemperatur", - icon: mdi("temperature-celsius"), + icon: svg(icons.mdiTemperatureCelsius), topic: "fanOfficeTarget" }, { @@ -323,7 +321,7 @@ const config: Config = { max: 25, step: 0.1, text: "Zieltemperatur", - icon: mdi("oil-temperature"), + icon: svg(icons.mdiOilTemperature), topic: "fanOfficeTarget" } ] @@ -331,25 +329,24 @@ const config: Config = { tucana: { name: "tucana", position: [110, 658], - icon: mdi("desktop-tower"), - iconColor: ({tucanaPower}) => + icon: svg(icons.mdiDesktopTower).color(({tucanaPower}) => ({ "Link Down": hex("#888888"), "1000M": hex("#00ff00"), "10M": hex("#000000") - })[tucanaPower] || hex("#ff0000"), + })[tucanaPower] || hex("#ff0000")), ui: [ { type: "toggle", topic: "tucanaPower", text: "Einschalten", - icon: mdi("power"), + icon: svg(icons.mdiPower), on: "1000M" }, { type: "text", text: "Link Speed", - icon: mdi("ethernet"), + icon: svg(icons.mdiEthernet), topic: "tucanaPower" } ] @@ -357,19 +354,19 @@ const config: Config = { officeSwitch: { name: "Switch Büro", position: [200, 540], - icon: mdi("lan"), + icon: svg(icons.mdiLan), ui: [ { type: "toggle", topic: "officeSwitchPollingActive", text: "Poll switch status", - icon: mdi("power") + icon: svg(icons.mdiPower) }, { type: "link", link: "http://192.168.0.189/", text: "Open Webinterface", - icon: mdi("open-in-new") + icon: svg(icons.mdiOpenInNew) } ] @@ -377,22 +374,21 @@ const config: Config = { officeLight: { name: "Büro", position: [210, 570], - icon: mdi("ceiling-light"), - iconColor: ({officeState}) => - (officeState === "on" ? hex("#00FF00") : hex("#000000")), + icon: svg(icons.mdiCeilingLight).color(({officeState}) => + officeState === "on" ? hex("#00FF00") : hex("#000000")), ui: [ { type: "toggle", topic: "officeState", text: "Ein/Ausschalten", - icon: mdi("power") + icon: svg(icons.mdiPower) }, { type: "slider", min: 0, max: 255, text: "Helligkeit", - icon: mdi("brightness-7"), + icon: svg(icons.mdiBrightness7), topic: "officebrightness" } ] @@ -400,22 +396,21 @@ const config: Config = { hallwayLight: { name: "Flur", position: [520, 370], - icon: mdi("ceiling-light"), - iconColor: ({hallwayState}) => - (hallwayState === "on" ? hex("#00FF00") : hex("#000000")), + icon: svg(icons.mdiCeilingLight).color(({hallwayState}) => + hallwayState === "on" ? hex("#00FF00") : hex("#000000")), ui: [ { type: "toggle", topic: "hallwayState", text: "Ein/Ausschalten", - icon: mdi("power") + icon: svg(icons.mdiPower) }, { type: "slider", min: 0, max: 255, text: "Helligkeit", - icon: mdi("brightness-7"), + icon: svg(icons.mdiBrightness7), topic: "hallwaybrightness" } ] @@ -423,22 +418,21 @@ const config: Config = { hallway2Light: { name: "Flur", position: [250, 370], - icon: mdi("ceiling-light"), - iconColor: ({hallway2State}) => - (hallway2State === "on" ? hex("#00FF00") : hex("#000000")), + icon: svg(icons.mdiCeilingLight).color(({hallway2State}) => + hallway2State === "on" ? hex("#00FF00") : hex("#000000")), ui: [ { type: "toggle", topic: "hallway2State", text: "Ein/Ausschalten", - icon: mdi("power") + icon: svg(icons.mdiPower) }, { type: "slider", min: 0, max: 255, text: "Helligkeit", - icon: mdi("brightness-7"), + icon: svg(icons.mdiBrightness7), topic: "hallway2brightness" } ] @@ -446,62 +440,60 @@ const config: Config = { pi: { name: "Pi", position: [550, 75], - icon: mdi("raspberrypi"), + icon: svg(icons.mdiRaspberryPi), ui: [ { type: "toggle", topic: "lueftenHint", text: "Lüften Erinnerung", - icon: mdi("fan") + icon: svg(icons.mdiFan) }, { type: "link", link: "http://192.168.0.12:3000/", text: "Grafana", - icon: mdi("open-in-new") + icon: svg(icons.mdiOpenInNew) } ] }, nas: { name: "NAS", position: [550, 100], - icon: mdi("nas"), - iconColor: ({nasPower}) => - (nasPower === "on" ? hex("#00FF00") : hex("#000000")), + icon: svg(icons.mdiNas).color(({nasPower}) => + nasPower === "on" ? hex("#00FF00") : hex("#000000")), ui: [ { type: "toggle", topic: "nasPower", text: "Einschalten", - icon: mdi("power") + icon: svg(icons.mdiPower) } ] }, livingroomLight: { name: "Wohnzimmer", position: [450, 200], - icon: mdi("ceiling-light"), - iconColor: ({livingroomState}) => - (livingroomState === "on" ? hex("#00FF00") : hex("#000000")), + icon: svg(icons.mdiCeilingLight).color(({livingroomState}) => + livingroomState === "on" ? hex("#00FF00") : hex("#000000")), ui: ([ { type: "toggle", topic: "livingroomState", text: "Ein/Ausschalten", - icon: mdi("power") + icon: svg(icons.mdiPower) }, { type: "toggle", topic: "livingroomKodiControlled", text: "Kodi Einbindung", - icon: mdi("brightness-auto") + icon: svg(icons.mdiBrightnessAuto) }, { type: "slider", min: 0, max: 255, text: "Helligkeit", - icon: mdi("brightness-7"), + icon: svg(icons.mdiBrightness7), topic: "livingroombrightness" }, { @@ -510,7 +502,7 @@ const config: Config = { min: 300, step: -1, text: "Speed", - icon: mdi("speedometer"), + icon: svg(icons.mdiSpeedometer), topic: "livingroomanimation-speed" }, { @@ -525,7 +517,7 @@ const config: Config = { "3": "RGB Fade", "4": "Work" }, - icon: mdi("settings") + icon: svg(icons.mdiCog) }, { type: "section", diff --git a/package.json b/package.json index 5d60251..06aa0fb 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,7 @@ "dependencies": { "@material-ui/core": "^4.11.0", "@material-ui/lab": "^4.0.0-alpha.56", - "@mdi/font": "^5.6.55", + "@mdi/react": "^1.4.0", "leaflet": "^1.5.1", "lodash-es": "^4.17.15", "mqtt": "^4.2.1", @@ -31,6 +31,7 @@ "@babel/preset-env": "^7.5.5", "@babel/preset-flow": "^7.0.0-rc.1", "@babel/preset-react": "^7.0.0-rc.1", + "@mdi/js": "^5.6.55", "babel-eslint": "^10.0.2", "babel-loader": "^8.0.6", "clean-webpack-plugin": "^3.0.0", diff --git a/src/components/App.js b/src/components/App.js index c6df8d1..f2e0d84 100644 --- a/src/components/App.js +++ b/src/components/App.js @@ -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 { controlMap: React.Node @@ -171,7 +179,7 @@ class App extends React.PureComponent { 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 || } diff --git a/src/components/ControlMap.js b/src/components/ControlMap.js index 6532002..1fb8ebb 100644 --- a/src/components/ControlMap.js +++ b/src/components/ControlMap.js @@ -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: `` + html: renderToString(control.icon.render(state)) }); }; diff --git a/src/components/SideBar.js b/src/components/SideBar.js index 2d4499c..7983133 100644 --- a/src/components/SideBar.js +++ b/src/components/SideBar.js @@ -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) => { - {props.icon == null || renderRawIcon(props.icon, "mdi-36px")} + {props.icon == null || props.icon} {props.control == null ? "" : props.control.name} diff --git a/src/components/TopBar.js b/src/components/TopBar.js index 8f9055e..50abfa1 100644 --- a/src/components/TopBar.js +++ b/src/components/TopBar.js @@ -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 (); + return (); } return ( diff --git a/src/components/UiItems/Link.js b/src/components/UiItems/Link.js index 506f86d..5592e85 100644 --- a/src/components/UiItems/Link.js +++ b/src/components/UiItems/Link.js @@ -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) => ( diff --git a/src/components/UiItems/base.js b/src/components/UiItems/base.js index 2b7a5b9..4e55ffc 100644 --- a/src/components/UiItems/base.js +++ b/src/components/UiItems/base.js @@ -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 }) => ( - {item.icon == null || renderRawIcon(item.icon(state), "mdi-24px")} + {item.icon == null || item.icon.size(1).render(state)} ); const createHelpers = (item: T) => ({ Icon: IconHelper, - Label: (props) => ( - + Label: () => ( + ), Action: (props) => ( diff --git a/src/config/flowtypes.js b/src/config/flowtypes.js index f3fe9d3..b91eb02 100644 --- a/src/config/flowtypes.js +++ b/src/config/flowtypes.js @@ -100,7 +100,6 @@ export type Control = { name: string, position: [number, number], icon: Icon, - iconColor?: (state: State) => Color, ui: Array }; export type Controls = Map; diff --git a/src/config/icon.js b/src/config/icon.js index 6844980..55bdd4c 100644 --- a/src/config/icon.js +++ b/src/config/icon.js @@ -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) => ( + + ), + 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 ; - }; - -export const renderIcon = - (icon: Icon, extraClass?: string): React.Node => { - return ( - - {({state}) => renderRawIcon(icon(state), extraClass)} - - ); - }; + return svg(mdiIcons.mdiBattery10); +}); diff --git a/src/index.jsx b/src/index.jsx index 392f8e4..94341da 100644 --- a/src/index.jsx +++ b/src/index.jsx @@ -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"; diff --git a/yarn.lock b/yarn.lock index 4fe972b..73a8400 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1050,10 +1050,15 @@ prop-types "^15.7.2" react-is "^16.8.0" -"@mdi/font@^5.6.55": +"@mdi/js@^5.6.55": version "5.6.55" - resolved "https://registry.yarnpkg.com/@mdi/font/-/font-5.6.55.tgz#3536d7a7671eda9d5d9ba191b9fb9aceccc0a07e" - integrity sha512-6wWrpTXiv2wBtoCL+EJ9Xxfy6Tv6Q1KxmrX54/M23tBNmdGmh417y1tn327oXQxO1nq7BiHGAKkWQZ/GQPLTCA== + resolved "https://registry.yarnpkg.com/@mdi/js/-/js-5.6.55.tgz#d1e99da22c8d462c17d4c5b530a7d1b77e668230" + integrity sha512-FphEd6PTivVSfsxjRB/Z5sJhxUmxA0jL/+nEQmqk8Ok1miSnw92ibj4p1SPAmgnR8OFTNCkHX23AvlU8YZQb5A== + +"@mdi/react@^1.4.0": + version "1.4.0" + resolved "https://registry.yarnpkg.com/@mdi/react/-/react-1.4.0.tgz#7f6bad1bd8801226d0e5bad91659b215ce68a93f" + integrity sha512-OUH9RhfDJPhybQL3owwrSDIXz2yVKXg5lYeOZjyRCiT9wqywNK0FeYyDByOwNIZnnIQoQYmuSrMv+pOX0Uqkmw== "@octokit/auth-token@^2.4.0": version "2.4.2" From 8376f404e49ef1aa8c8d87a3d5ff4b7df1cfa56b Mon Sep 17 00:00:00 2001 From: uwap Date: Thu, 8 Oct 2020 08:46:06 +0200 Subject: [PATCH 3/7] Fix eslint errors --- config/entropia/index.js | 1 - config/rzl/index.js | 29 +++++++++++++++-------------- config/rzl/kitchen.js | 2 +- config/rzl/olymp.js | 8 ++++---- config/rzl/utils.js | 2 +- config/uwap-home/index.js | 22 +++++++++++----------- src/components/App.js | 12 ++++++------ src/components/ControlMap.js | 2 +- src/config/flowtypes.js | 1 - src/config/icon.js | 11 +++++------ 10 files changed, 44 insertions(+), 46 deletions(-) diff --git a/config/entropia/index.js b/config/entropia/index.js index e28e63e..593bd97 100644 --- a/config/entropia/index.js +++ b/config/entropia/index.js @@ -1,6 +1,5 @@ // @flow import type { Config } from "config/flowtypes"; -import { hex } from "config/colors"; import * as types from "config/types"; import * as icons from "@mdi/js"; import { svg } from "config/icon"; diff --git a/config/rzl/index.js b/config/rzl/index.js index dd5bebf..9c2ef78 100644 --- a/config/rzl/index.js +++ b/config/rzl/index.js @@ -4,7 +4,7 @@ import * as types from "config/types"; import { hex, rainbow } from "config/colors"; import { svg, withState } from "config/icon"; import { esper, tasmota } from "./utils"; -import * as icons from "@mdi/js" +import * as icons from "@mdi/js"; import * as onkyo from "./onkyo"; import * as olymp from "./olymp"; @@ -277,7 +277,7 @@ const config: Config = { name: "LED Stahlträger", position: [340, 590], icon: svg(icons.mdiWhiteBalanceIridescent).color(({ledStahltraeger}) => - ledStahltraeger === "on" ? rainbow : hex("#000000")), + (ledStahltraeger === "on" ? rainbow : hex("#000000"))), ui: [ { type: "toggle", @@ -353,8 +353,8 @@ const config: Config = { name: "Twinkle", position: [530, 560], icon: withState(({twinkle}) => - twinkle === "on" ? svg(icons.mdiLedOn).flipV().color(rainbow) - : svg(icons.mdiLedOff).flipV() + (twinkle === "on" ? svg(icons.mdiLedOn).flipV().color(rainbow) + : svg(icons.mdiLedOff).flipV()) ), ui: [ { @@ -369,7 +369,7 @@ const config: Config = { name: "Ventilator", position: [530, 440], icon: svg(icons.mdiFan).color(({fan}) => - fan === "on" ? hex("#00FF00") : hex("#000000")), + (fan === "on" ? hex("#00FF00") : hex("#000000"))), ui: [ { type: "toggle", @@ -396,7 +396,7 @@ const config: Config = { name: "Fliegenbratgerät", position: [450, 570], icon: svg(icons.mdiFire).color(({flyfry}) => - flyfry === "on" ? hex("#6666FF") : hex("#000000")), + (flyfry === "on" ? hex("#6666FF") : hex("#000000"))), ui: esper.statistics("flyfry", [ { type: "toggle", @@ -431,7 +431,7 @@ const config: Config = { name: "Lötarbeitsplatz", position: [205, 455], icon: svg(icons.mdiEyedropperVariant).color(({loetarbeitsplatz4}) => - loetarbeitsplatz4 === "on" ? hex("#FF0000") : hex("#000000")), + (loetarbeitsplatz4 === "on" ? hex("#FF0000") : hex("#000000"))), ui: [ { type: "text", @@ -445,7 +445,7 @@ const config: Config = { name: "Lötarbeitsplatz", position: [205, 405], icon: svg(icons.mdiEyedropperVariant).color(({loetarbeitsplatz4}) => - loetarbeitsplatz4 === "on" ? hex("#FF0000") : hex("#000000")), + (loetarbeitsplatz4 === "on" ? hex("#FF0000") : hex("#000000"))), ui: [ { type: "text", @@ -459,7 +459,7 @@ const config: Config = { name: "Tür", position: [455, 350], icon: svg(icons.mdiSwapVertical).color(({doorStatus}) => - doorStatus === "on" ? hex("#00FF00") : hex("#FF0000")), + (doorStatus === "on" ? hex("#00FF00") : hex("#FF0000"))), ui: [ { type: "link", @@ -526,8 +526,8 @@ const config: Config = { name: "Pilze", position: [48, 499], icon: withState(({pilze}) => - pilze === "on" ? svg(icons.mdiLedOn) : svg(icons.mdiLedOff)).color( - tasmota.iconColor("pilze", rainbow)), + (pilze === "on" ? svg(icons.mdiLedOn) : svg(icons.mdiLedOff))).color( + tasmota.iconColor("pilze", rainbow)), ui: [ { type: "toggle", @@ -611,8 +611,8 @@ const config: Config = { name: "Strom Fablab", position: [613, 537], icon: withState(({nebenraumPowerStatus}) => - nebenraumPowerStatus === "on" ? svg(icons.mdiFlash).color(hex("#00FF00")) - : svg(icons.mdiFlashOff)), + (nebenraumPowerStatus === "on" ? + svg(icons.mdiFlash).color(hex("#00FF00")) : svg(icons.mdiFlashOff))), ui: [ { type: "text", @@ -627,7 +627,8 @@ const config: Config = { position: [1413, 500], icon: svg(icons.mdiPool).color( ({whirlpoolBubbles}) => - parseInt(whirlpoolBubbles, 10) > 0 ? hex("#00ff00") : hex("#000000")), + (parseInt(whirlpoolBubbles, 10) > 0 ? hex("#00ff00") + : hex("#000000"))), ui: [ { type: "text", diff --git a/config/rzl/kitchen.js b/config/rzl/kitchen.js index 86c9558..89fd23a 100644 --- a/config/rzl/kitchen.js +++ b/config/rzl/kitchen.js @@ -4,7 +4,7 @@ import { svg, mdiBattery } from "config/icon"; import { hex } from "config/colors"; import * as types from "config/types"; import { floalt, tradfri, tasmota } from "./utils"; -import * as icons from "@mdi/js" +import * as icons from "@mdi/js"; export const topics: Topics = { //Kuechen-Floalts diff --git a/config/rzl/olymp.js b/config/rzl/olymp.js index 40a158f..8b8e989 100644 --- a/config/rzl/olymp.js +++ b/config/rzl/olymp.js @@ -4,7 +4,7 @@ import { svg } from "config/icon"; import { hex, rainbow } from "config/colors"; import * as types from "config/types"; import { tasmota, esper } from "./utils"; -import * as icons from "@mdi/js" +import * as icons from "@mdi/js"; export const topics: Topics = { ...tasmota.topics("8", "ledOlymp"), @@ -63,7 +63,7 @@ export const controls: Controls = { name: "Videospiele", position: [100, 100], icon: svg(icons.mdiGamepadVariant).color(({videogames}) => - videogames === "on" ? hex("#00FF00") : hex("#000000")), + (videogames === "on" ? hex("#00FF00") : hex("#000000"))), ui: [ { type: "toggle", @@ -77,7 +77,7 @@ export const controls: Controls = { name: "Rechner", position: [297, 90], icon: svg(icons.mdiDesktopClassic).color(({olympPC}) => - olympPC === "on" ? hex("#00FF00") : hex("#000000")), + (olympPC === "on" ? hex("#00FF00") : hex("#000000"))), ui: [ { type: "toggle", @@ -91,7 +91,7 @@ export const controls: Controls = { name: "Rundumleuchte", position: [310, 275], icon: svg(icons.mdiAlarmLight).color(({rundumleuchte}) => - rundumleuchte === "on" ? hex("#F0DF10") : hex("#000000")), + (rundumleuchte === "on" ? hex("#F0DF10") : hex("#000000"))), ui: [ { type: "toggle", diff --git a/config/rzl/utils.js b/config/rzl/utils.js index 5c5b195..5daf02f 100644 --- a/config/rzl/utils.js +++ b/config/rzl/utils.js @@ -3,7 +3,7 @@ import type { ControlUI, Topics } from "config/flowtypes"; import { svg } from "config/icon"; import { hex, type Color } from "config/colors"; import * as types from "config/types"; -import * as icons from "@mdi/js" +import * as icons from "@mdi/js"; export const tasmota = { topics: (id: string, name: string): Topics => ({ diff --git a/config/uwap-home/index.js b/config/uwap-home/index.js index 2cad6f3..18b6dd3 100644 --- a/config/uwap-home/index.js +++ b/config/uwap-home/index.js @@ -3,7 +3,7 @@ import type { Config } from "config/flowtypes"; import * as types from "config/types"; import { svg, withState } from "config/icon"; import { hex } from "config/colors"; -import * as icons from "@mdi/js" +import * as icons from "@mdi/js"; const topicBulbHomeRust = (bulb: string, argument: string) => ({ [`${bulb}${argument}`]: { @@ -207,7 +207,7 @@ const config: Config = { name: "Schlafzimmer", position: [180, 130], icon: svg(icons.mdiCeilingLight).color(({bedroomState}) => - bedroomState === "on" ? hex("#00FF00") : hex("#000000")), + (bedroomState === "on" ? hex("#00FF00") : hex("#000000"))), ui: [ { type: "toggle", @@ -243,7 +243,7 @@ const config: Config = { name: "Lüftung Schlafzimmer", position: [140, 25], icon: svg(icons.mdiFan).color(({fanBedroomState}) => - fanBedroomState === "on" ? hex("#00FF00") : hex("#000000")), + (fanBedroomState === "on" ? hex("#00FF00") : hex("#000000"))), ui: [ { type: "toggle", @@ -278,9 +278,9 @@ const config: Config = { name: "Lautsprecher", position: [245, 658], icon: withState(({speakerOfficeState}) => - speakerOfficeState !== "on" ? svg(icons.mdiVolumeOff) - : svg(icons.mdiVolumeHigh).color(hex("#00FF00")) - ), + (speakerOfficeState !== "on" ? svg(icons.mdiVolumeOff) + : svg(icons.mdiVolumeHigh).color(hex("#00FF00"))) + ), ui: [ { type: "toggle", @@ -375,7 +375,7 @@ const config: Config = { name: "Büro", position: [210, 570], icon: svg(icons.mdiCeilingLight).color(({officeState}) => - officeState === "on" ? hex("#00FF00") : hex("#000000")), + (officeState === "on" ? hex("#00FF00") : hex("#000000"))), ui: [ { type: "toggle", @@ -397,7 +397,7 @@ const config: Config = { name: "Flur", position: [520, 370], icon: svg(icons.mdiCeilingLight).color(({hallwayState}) => - hallwayState === "on" ? hex("#00FF00") : hex("#000000")), + (hallwayState === "on" ? hex("#00FF00") : hex("#000000"))), ui: [ { type: "toggle", @@ -419,7 +419,7 @@ const config: Config = { name: "Flur", position: [250, 370], icon: svg(icons.mdiCeilingLight).color(({hallway2State}) => - hallway2State === "on" ? hex("#00FF00") : hex("#000000")), + (hallway2State === "on" ? hex("#00FF00") : hex("#000000"))), ui: [ { type: "toggle", @@ -460,7 +460,7 @@ const config: Config = { name: "NAS", position: [550, 100], icon: svg(icons.mdiNas).color(({nasPower}) => - nasPower === "on" ? hex("#00FF00") : hex("#000000")), + (nasPower === "on" ? hex("#00FF00") : hex("#000000"))), ui: [ { type: "toggle", @@ -474,7 +474,7 @@ const config: Config = { name: "Wohnzimmer", position: [450, 200], icon: svg(icons.mdiCeilingLight).color(({livingroomState}) => - livingroomState === "on" ? hex("#00FF00") : hex("#000000")), + (livingroomState === "on" ? hex("#00FF00") : hex("#000000"))), ui: ([ { type: "toggle", diff --git a/src/components/App.js b/src/components/App.js index f2e0d84..818c481 100644 --- a/src/components/App.js +++ b/src/components/App.js @@ -40,12 +40,12 @@ export type AppState = { }; /* -const App = (props: AppProps) => { - const topics = Array.isArray(props.config.topics) ? - Object.assign({}, ...props.config.topics) : props.config.topics; - const [mqttConnected, setMqttConnected] = useState(false); -}; -*/ + *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 { controlMap: React.Node diff --git a/src/components/ControlMap.js b/src/components/ControlMap.js index 1fb8ebb..231dd1b 100644 --- a/src/components/ControlMap.js +++ b/src/components/ControlMap.js @@ -7,7 +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' +import { renderToString } from "react-dom/server"; export type Point = [number, number]; diff --git a/src/config/flowtypes.js b/src/config/flowtypes.js index b91eb02..be2091e 100644 --- a/src/config/flowtypes.js +++ b/src/config/flowtypes.js @@ -1,5 +1,4 @@ // @flow -import type { Color } from "config/colors"; import type { Icon } from "config/icon"; export type TopicType = (msg: Buffer) => string; diff --git a/src/config/icon.js b/src/config/icon.js index 55bdd4c..31939fb 100644 --- a/src/config/icon.js +++ b/src/config/icon.js @@ -1,9 +1,8 @@ // @flow 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" +import { type Color } from "./colors"; +import * as mdiIcons from "@mdi/js"; export type Icon = { render: (s: State) => React.Node, @@ -36,7 +35,7 @@ export const svg = (data: string, props?: IconPropHelper): Icon => { 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}), @@ -44,7 +43,7 @@ export const svg = (data: string, props?: IconPropHelper): Icon => { 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 { @@ -55,7 +54,7 @@ export const withState = (f: (s: State) => Icon): Icon => { flipV: () => withState(f), color: () => withState(f) }; -} +}; export const mdiBattery = (topic: string): Icon => withState((state) => { const rawval = state[topic]; From 9a5557db03c21e97399118ad3efcfbdf3bfab2c3 Mon Sep 17 00:00:00 2001 From: uwap Date: Thu, 8 Oct 2020 17:24:51 +0200 Subject: [PATCH 4/7] Fix sidebar close icon not being visible (Closes #147) --- src/components/SideBar.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/components/SideBar.js b/src/components/SideBar.js index 7983133..c4a3cce 100644 --- a/src/components/SideBar.js +++ b/src/components/SideBar.js @@ -8,6 +8,8 @@ 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 ReactIcon from "@mdi/react"; +import { mdiClose } from "@mdi/js"; import type { Control } from "config/flowtypes"; @@ -47,7 +49,7 @@ const SideBar = (props: SideBarProps) => { {props.control == null ? "" : props.control.name} - + From 1c1de6356c1022d79bccbe9825647472c6843187 Mon Sep 17 00:00:00 2001 From: uwap Date: Thu, 8 Oct 2020 17:40:42 +0200 Subject: [PATCH 5/7] Define icon transformations on withState (Closes #148) --- src/config/icon.js | 52 +++++++++++++++++++++++++--------------------- 1 file changed, 28 insertions(+), 24 deletions(-) diff --git a/src/config/icon.js b/src/config/icon.js index 31939fb..54a598b 100644 --- a/src/config/icon.js +++ b/src/config/icon.js @@ -4,15 +4,6 @@ import ReactIcon from "@mdi/react"; import { type Color } from "./colors"; import * as mdiIcons from "@mdi/js"; -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 -}; - type IconPropHelper = { size?: number, rotate?: number, @@ -21,6 +12,27 @@ type IconPropHelper = { color?: Color }; +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, + applyProps: (props: IconPropHelper) => Icon +}; + +const iconChainUtils = (cb: (x: T, p?: IconPropHelper) => Icon, + p1: T, p?: IconPropHelper) => ({ + size: (n: number) => cb(p1, {...p, size: n}), + rotate: (n: number) => cb(p1, {...p, rotate: n}), + flip: () => cb(p1, {...p, horizontal: !p?.horizontal ?? true}), + flipV: () => cb(p1, {...p, vertical: !p?.vertical ?? true}), + color: (c: Color | (State) => Color) => cb(p1, {...p, color: c}), + applyProps: (props: IconPropHelper) => cb(p1, {...p, ...props}) + } + ); + export const svg = (data: string, props?: IconPropHelper): Icon => { const propColor = ((c: Color | (State) => Color) => (state: State) => { if (typeof c === "function") { @@ -37,24 +49,16 @@ export const svg = (data: string, props?: IconPropHelper): Icon => { 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}) + ...iconChainUtils(svg, data, props) }; }; -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 withState = (f: (s: State) => Icon, + props?: IconPropHelper): Icon => ({ + render: (state) => f(state).applyProps(props).render(state), + ...iconChainUtils(withState, f, props) +} +); export const mdiBattery = (topic: string): Icon => withState((state) => { const rawval = state[topic]; From e43842fbed9b339d08b88ea6fa6ad8bff271cc88 Mon Sep 17 00:00:00 2001 From: uwap Date: Thu, 8 Oct 2020 18:05:40 +0200 Subject: [PATCH 6/7] Link to the wiki --- README.md | 31 ++----------------------------- 1 file changed, 2 insertions(+), 29 deletions(-) diff --git a/README.md b/README.md index bd53203..9eddf0e 100644 --- a/README.md +++ b/README.md @@ -11,33 +11,6 @@ your the mqtt control map for the given CONFIG everytime something changes. for the given config. 4. run `yarn build CONFIG` to generate all files for production use. -## Config +## Documentation -See `config/`. - -The Config format consists out of two sections. Topics and Controls. - -### Topics - -The topics section defines the mqtt interfaces. - -### Controls - -The Controls define the UI Controls. - -| Name | Type | Optional? | Default | Description | -|-----------------|-------------------|------------|-----------------|-------------| -| type | "toggle" \| "dropDown" \| "slider" | No | | The type of the UI element. | -| text | string | No | | The text displayed right next to the UI element. | -| topic | string | No | | The topic the UI element is supposed to change and/or receive its status from. | -| enableCondition | (key: string, value: *) => boolean | Yes | () => true | This option allows you to enable or disable UI elements, depending on the current state. The first parameter is the internal representation of the value. For example "off". The second parameter is the actual value that was received via MQTT. Return true to enable the element and false to disable it. | -| **Toggle Options** | -| on | string | Yes | "on" | If the state is equal to the value of this option the toggle will be toggled on (if the toggled function is not overriden). This is also the value that will be sent when the button is toggled on. | -| off | string | Yes | "off" | If the state is equal to the value of this option the toggle will be toggled off (if the toggled function is not overriden). This is also the value that will be sent when the button is toggled off. | -| toggled | (key: string, value: *) => boolean | Yes | Use the on and off options | This is the function that decides whether the button should be in a toggled state or not. The parameters are equivalent to those of `enableCondition`. Return true to set the button to a toggled state. Return false to set it to the untoggled state. | -| **DropDown Options** | -| options | Map| Yes | {} | This is an attribute set that will map all values defined in the topics section to a description text. For example `{ on: "Lights On", off: "Lights Off" }` will give the drop down element two options, one that is named `Lights On` and one that is named `Lights Off`. | -| **Slider Options** | -| min | number | Yes | 0 | The minimum value of that slider. | -| max | number | Yes | 1 | The maximum value of that slider. | -| step | number | Yes | 1 | The smallest step of the slider. | +The documentation can be found in our [mqtt-control-map wiki](https://github.com/uwap/mqtt-control-map/wiki). From 2997ff8862725304903e57735f5d37b3353a70d1 Mon Sep 17 00:00:00 2001 From: uwap Date: Thu, 8 Oct 2020 20:31:42 +0200 Subject: [PATCH 7/7] Various improvements --- .babelrc | 4 +++- index.ejs | 1 - package.json | 16 +++++++-------- src/components/TopBar.js | 15 ++++---------- src/index.jsx | 4 ++++ webpack.config.js | 13 ++++++------ yarn.lock | 44 +++++++++------------------------------- 7 files changed, 35 insertions(+), 62 deletions(-) diff --git a/.babelrc b/.babelrc index e0278e3..8b51aa3 100644 --- a/.babelrc +++ b/.babelrc @@ -2,7 +2,9 @@ "presets": [ ["@babel/preset-env", { modules: false, - corejs: 3 + corejs: "3.6", + useBuiltIns: "entry", + targets: "last 3 years" }], "@babel/preset-react", "@babel/preset-flow" diff --git a/index.ejs b/index.ejs index bede683..af4cf0c 100644 --- a/index.ejs +++ b/index.ejs @@ -2,7 +2,6 @@ - <%= htmlWebpackPlugin.options.title %> diff --git a/package.json b/package.json index 06aa0fb..6403a40 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "author": "uwap ", "description": "Control Devices via mqtt, visualized on a Map", "scripts": { - "build": "webpack --bail --config webpack.config.js -p --env", + "build": "webpack --bail --config webpack.config.js --mode production --env", "dev": "webpack --bail --config webpack.config.js --mode development --env", "watch": "webpack-dev-server --open --config webpack.config.js --mode development --env", "travis": "./travis.sh", @@ -13,15 +13,12 @@ }, "dependencies": { "@material-ui/core": "^4.11.0", - "@material-ui/lab": "^4.0.0-alpha.56", "@mdi/react": "^1.4.0", "leaflet": "^1.5.1", - "lodash-es": "^4.17.15", "mqtt": "^4.2.1", "react": "^16.8.6", "react-dom": "^16.8.6", - "react-leaflet": "^2.4.0", - "redux": "^4.0.4" + "react-leaflet": "^2.4.0" }, "devDependencies": { "@babel/cli": "^7.5.5", @@ -35,7 +32,7 @@ "babel-eslint": "^10.0.2", "babel-loader": "^8.0.6", "clean-webpack-plugin": "^3.0.0", - "core-js": "3", + "core-js": "^3.6.0", "css-loader": "^4.3.0", "eslint": "^7.10.0", "eslint-plugin-flowtype": "^5.2.0", @@ -47,11 +44,12 @@ "flow-typed": "^3.2.1", "html-webpack-plugin": "^4.5.0", "husky": "^4.3.0", + "lodash-es": "^4.17.15", "style-loader": "^1.3.0", - "webpack": "^4.39.1", - "webpack-cli": "^3.3.6", + "webpack": "^4.44.0", + "webpack-cli": "^3.3.0", "webpack-dev-server": "^3.7.2", - "webpack-shell-plugin": "^0.5.0" + "webpack-shell-plugin-next": "^1.2.0" }, "license": "MIT" } diff --git a/src/components/TopBar.js b/src/components/TopBar.js index 50abfa1..cd935c6 100644 --- a/src/components/TopBar.js +++ b/src/components/TopBar.js @@ -10,7 +10,7 @@ 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"; +import { mdiMap, mdiGithub } from "@mdi/js"; export type TopBarProps = { connected: boolean, @@ -91,22 +91,15 @@ const Search = (props: SearchBarProps) => { const openOnGithub = () => window.open( "https://github.com/uwap/mqtt-control-map", "_blank"); -const sendFeedback = () => window.open("mailto:mail+feedback@uwap.name"); - const TopBar = (props: TopBarProps) => ( {renderConnectionIndicator(props.connected)} - - - - - - - - + + + diff --git a/src/index.jsx b/src/index.jsx index 94341da..60400b8 100644 --- a/src/index.jsx +++ b/src/index.jsx @@ -1,4 +1,8 @@ // @flow +import "core-js/stable"; +import "regenerator-runtime/runtime"; +import "../node_modules/leaflet/dist/leaflet.css" + import React from "react"; import ReactDOM from "react-dom"; diff --git a/webpack.config.js b/webpack.config.js index 31501b3..89d6f9a 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -1,6 +1,6 @@ const path = require('path'); const webpack = require('webpack'); -const WebpackShellPlugin = require('webpack-shell-plugin'); +const WebpackShellPlugin = require('webpack-shell-plugin-next'); const HtmlWebpackPlugin = require('html-webpack-plugin'); const { CleanWebpackPlugin } = require('clean-webpack-plugin'); const preBuildScripts = process.env.NO_FLOW == undefined ? @@ -16,14 +16,15 @@ const configPath = env => { module.exports = env => ({ entry: { - main: ["core-js/stable", "regenerator-runtime/runtime", configPath(env), + main: [configPath(env), path.resolve(__dirname, 'src/index.jsx')] }, resolve: { modules: [path.resolve(__dirname, "src"), "node_modules"], extensions: ['.js', '.jsx'], alias: { - 'lodash': 'lodash-es' + 'lodash': 'lodash-es', + "leaflet": "leaflet/dist/leaflet-src.esm.js" } }, output: { @@ -34,8 +35,8 @@ module.exports = env => ({ rules: [ // TODO: CSS follow imports and minify + sourcemap on production { test: /\.css$/, use: [ 'style-loader', 'css-loader' ] }, - { test: /\.(woff2?|eot|ttf|svg)$/, use: [ { loader: "file-loader", options: { esModule: false } } ] }, - { test: /\.js(x)?$/, loader: "babel-loader?cacheDirectory=true" } + { test: /\.(woff2?|eot|ttf|svg|png)$/, use: [ { loader: "file-loader", options: { esModule: false } } ] }, + { test: /\.js(x)?$/, use: ["babel-loader?cacheDirectory=true"] } ] }, plugins: [ @@ -44,6 +45,6 @@ module.exports = env => ({ new HtmlWebpackPlugin({ title: 'Space Map', template: 'index.ejs' - }), + }) ] }); diff --git a/yarn.lock b/yarn.lock index 73a8400..ba267e7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -993,17 +993,6 @@ react-is "^16.8.0" react-transition-group "^4.4.0" -"@material-ui/lab@^4.0.0-alpha.56": - version "4.0.0-alpha.56" - resolved "https://registry.yarnpkg.com/@material-ui/lab/-/lab-4.0.0-alpha.56.tgz#ff63080949b55b40625e056bbda05e130d216d34" - integrity sha512-xPlkK+z/6y/24ka4gVJgwPfoCF4RCh8dXb1BNE7MtF9bXEBLN/lBxNTK8VAa0qm3V2oinA6xtUIdcRh0aeRtVw== - dependencies: - "@babel/runtime" "^7.4.4" - "@material-ui/utils" "^4.10.2" - clsx "^1.0.4" - prop-types "^15.7.2" - react-is "^16.8.0" - "@material-ui/styles@^4.10.0": version "4.10.0" resolved "https://registry.yarnpkg.com/@material-ui/styles/-/styles-4.10.0.tgz#2406dc23aa358217aa8cc772e6237bd7f0544071" @@ -1475,9 +1464,9 @@ acorn-jsx@^5.2.0: integrity sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng== acorn@^6.4.1: - version "6.4.1" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.4.1.tgz#531e58ba3f51b9dacb9a6646ca4debf5b14ca474" - integrity sha512-ZVA9k326Nwrj3Cj9jlh3wGFutC2ZornPNARZwsNYqQYgN0EsV2d53w5RN/co65Ohn4sUAUtb1rSUAOD6XN9idA== + version "6.4.2" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.4.2.tgz#35866fd710528e92de10cf06016498e47e39e1e6" + integrity sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ== acorn@^7.4.0: version "7.4.0" @@ -2436,7 +2425,7 @@ core-js-compat@^3.6.2: browserslist "^4.8.5" semver "7.0.0" -core-js@3: +core-js@^3.6.0: version "3.6.5" resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.6.5.tgz#7395dc273af37fb2e50e9bd3d9fe841285231d1a" integrity sha512-vZVEEwZoIsI+vPEuoF9Iqf5H7/M3eeQqWlQnYa8FSKKePuYTf5MWnxb5SDAzCa60b3JBRS5g9b+Dq7b1y/RCrA== @@ -5978,14 +5967,6 @@ rechoir@^0.6.2: dependencies: resolve "^1.1.6" -redux@^4.0.4: - version "4.0.5" - resolved "https://registry.yarnpkg.com/redux/-/redux-4.0.5.tgz#4db5de5816e17891de8a80c424232d06f051d93f" - integrity sha512-VSz1uMAH24DM6MF72vcojpYPtrTUu3ByVWfPL1nPfVRb5mZVTve5GnNCUV53QM/BZ66xfWrm0CTWoM+Xlz8V1w== - dependencies: - loose-envify "^1.4.0" - symbol-observable "^1.2.0" - regenerate-unicode-properties@^8.2.0: version "8.2.0" resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-8.2.0.tgz#e5de7111d655e7ba60c057dbe9ff37c87e65cdec" @@ -6757,11 +6738,6 @@ supports-color@^7.1.0: dependencies: has-flag "^4.0.0" -symbol-observable@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.2.0.tgz#c22688aed4eab3cdc2dfeacbb561660560a00804" - integrity sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ== - table@^5.2.3, table@^5.4.6: version "5.4.6" resolved "https://registry.yarnpkg.com/table/-/table-5.4.6.tgz#1292d19500ce3f86053b05f0e8e7e4a3bb21079e" @@ -7207,7 +7183,7 @@ wbuf@^1.1.0, wbuf@^1.7.3: dependencies: minimalistic-assert "^1.0.0" -webpack-cli@^3.3.6: +webpack-cli@^3.3.0: version "3.3.12" resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-3.3.12.tgz#94e9ada081453cd0aa609c99e500012fd3ad2d4a" integrity sha512-NVWBaz9k839ZH/sinurM+HcDvJOTXwSjYp1ku+5XKeOC03z8v5QitnK/x+lAxGXFyhdayoIf/GOpv85z3/xPag== @@ -7282,10 +7258,10 @@ webpack-log@^2.0.0: ansi-colors "^3.0.0" uuid "^3.3.2" -webpack-shell-plugin@^0.5.0: - version "0.5.0" - resolved "https://registry.yarnpkg.com/webpack-shell-plugin/-/webpack-shell-plugin-0.5.0.tgz#29b8a1d80ddeae0ddb10e729667f728653c2c742" - integrity sha1-Kbih2A3erg3bEOcpZn9yhlPCx0I= +webpack-shell-plugin-next@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/webpack-shell-plugin-next/-/webpack-shell-plugin-next-1.2.0.tgz#6a62931110c08ab36d2f0c225648a63331b7bbc2" + integrity sha512-jBLDs436jbKWnLZCqeN4hr7abXx0Tnz6Iwx7ejxh1h4JSmsnc82kGOBC63U6RKl0F6bvaXFsUOiIKV+DFKNxNw== webpack-sources@^1.4.0, webpack-sources@^1.4.1: version "1.4.3" @@ -7295,7 +7271,7 @@ webpack-sources@^1.4.0, webpack-sources@^1.4.1: source-list-map "^2.0.0" source-map "~0.6.1" -webpack@^4.39.1: +webpack@^4.44.0: version "4.44.2" resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.44.2.tgz#6bfe2b0af055c8b2d1e90ed2cd9363f841266b72" integrity sha512-6KJVGlCxYdISyurpQ0IPTklv+DULv05rs2hseIXer6D7KrUicRDLFb4IUM1S6LUAKypPM/nSiVSuv8jHu1m3/Q==