Browse Source

Hmm...

webui
Stian Aune 5 years ago
parent
commit
01daa28000
  1. 239
      webui/package-lock.json
  2. 9
      webui/package.json
  3. 9
      webui/src/App.css
  4. 15
      webui/src/App.js
  5. 4
      webui/src/Components/Forms/LoginForm.jsx
  6. 11
      webui/src/Components/Group.jsx
  7. 10
      webui/src/Components/Groups.jsx
  8. 40
      webui/src/Components/Light.jsx
  9. 12
      webui/src/Components/Loading.jsx
  10. 31
      webui/src/Components/Misc/ColorPicker.jsx
  11. 23
      webui/src/Components/Modals/ColorModal.jsx
  12. 10
      webui/src/Components/Pages/GroupPage.jsx
  13. 2
      webui/src/Components/Pages/IndexPage.jsx
  14. 4
      webui/src/Components/Structure/Header.jsx
  15. 26
      webui/src/Helpers/lights.js
  16. 3
      webui/src/Helpers/percentage.js
  17. 6
      webui/src/Hooks/light.js

239
webui/package-lock.json

@ -844,6 +844,16 @@
"resolved": "https://registry.npmjs.org/@csstools/convert-colors/-/convert-colors-1.4.0.tgz",
"integrity": "sha512-5a6wqoJV/xEdbRNKVo6I4hO3VjyDq//8q2f9I6PBAvMesJHFauXDorcNCsr9RzvsZnaWi5NYCcfyqP1QeFHFbw=="
},
"@icons/material": {
"version": "0.2.4",
"resolved": "https://registry.npmjs.org/@icons/material/-/material-0.2.4.tgz",
"integrity": "sha512-QPcGmICAPbGLGb6F/yNf/KzKqvFx8z5qx3D1yFqVAjoFmXK35EgyW+cJ57Te3CNsmzblwtzakLGFqHPqrfb4Tw=="
},
"@jaames/iro": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/@jaames/iro/-/iro-4.0.1.tgz",
"integrity": "sha512-Q4DcB7poRpH3Gok74isb5PD/FTBlxPbZ5RMovM7bUIJ7q8kDyJow1qUJ4qIywmWnUNZyXCa6dpvNXnQ24WP5TQ=="
},
"@mrmlnc/readdir-enhanced": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz",
@ -1149,6 +1159,14 @@
"resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-6.1.1.tgz",
"integrity": "sha512-OtUw6JUTgxA2QoqqmrmQ7F2NYqiBPi/L2jqHyFtllhOUvXYQXf0Z1CYUinIfyT4bTCGmrA7gX9FvHA81uzCoVw=="
},
"add-dom-event-listener": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/add-dom-event-listener/-/add-dom-event-listener-1.1.0.tgz",
"integrity": "sha512-WCxx1ixHT0GQU9hb0KI/mhgRQhnU+U3GvwY6ZvVjYq8rsihIGoaIOUbY0yMPBxLH5MDtr0kz3fisWGNcbWW7Jw==",
"requires": {
"object-assign": "4.x"
}
},
"address": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/address/-/address-1.0.3.tgz",
@ -2981,11 +2999,24 @@
"resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz",
"integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs="
},
"component-classes": {
"version": "1.2.6",
"resolved": "https://registry.npmjs.org/component-classes/-/component-classes-1.2.6.tgz",
"integrity": "sha1-xkI5TDYYpNiwuJGe/Mu9kw5c1pE=",
"requires": {
"component-indexof": "0.0.3"
}
},
"component-emitter": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz",
"integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY="
},
"component-indexof": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/component-indexof/-/component-indexof-0.0.3.tgz",
"integrity": "sha1-EdCRMSI5648yyPJa6csAL/6NPCQ="
},
"compressible": {
"version": "2.0.15",
"resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.15.tgz",
@ -3168,6 +3199,16 @@
"sha.js": "^2.4.8"
}
},
"create-react-class": {
"version": "15.6.3",
"resolved": "https://registry.npmjs.org/create-react-class/-/create-react-class-15.6.3.tgz",
"integrity": "sha512-M+/3Q6E6DLO6Yx3OwrWjwHBnvfXXYA7W+dFjt/ZDBemHO1DDZhsalX/NUtnTYclN6GfnBDRh4qRHjcDHmlJBJg==",
"requires": {
"fbjs": "^0.8.9",
"loose-envify": "^1.3.1",
"object-assign": "^4.1.1"
}
},
"cross-spawn": {
"version": "6.0.5",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz",
@ -3198,6 +3239,15 @@
"randomfill": "^1.0.3"
}
},
"css-animation": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/css-animation/-/css-animation-1.5.0.tgz",
"integrity": "sha512-hWYoWiOZ7Vr20etzLh3kpWgtC454tW5vn4I6rLANDgpzNSkO7UfOqyCEeaoBSG9CYWQpRkFWTWbWW8o3uZrNLw==",
"requires": {
"babel-runtime": "6.x",
"component-classes": "^1.2.5"
}
},
"css-color-names": {
"version": "0.0.4",
"resolved": "https://registry.npmjs.org/css-color-names/-/css-color-names-0.0.4.tgz",
@ -3816,6 +3866,11 @@
"esutils": "^2.0.2"
}
},
"dom-align": {
"version": "1.8.0",
"resolved": "https://registry.npmjs.org/dom-align/-/dom-align-1.8.0.tgz",
"integrity": "sha512-B85D4ef2Gj5lw0rK0KM2+D5/pH7yqNxg2mB+E8uzFaolpm7RQmsxEfjyEuNiF8UBBkffumYDeKRzTzc3LePP+w=="
},
"dom-converter": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/dom-converter/-/dom-converter-0.2.0.tgz",
@ -3965,6 +4020,14 @@
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
"integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k="
},
"encoding": {
"version": "0.1.12",
"resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.12.tgz",
"integrity": "sha1-U4tm8+5izRq1HsMjgp0flIDHS+s=",
"requires": {
"iconv-lite": "~0.4.13"
}
},
"end-of-stream": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz",
@ -4970,6 +5033,35 @@
"bser": "^2.0.0"
}
},
"fbjs": {
"version": "0.8.17",
"resolved": "https://registry.npmjs.org/fbjs/-/fbjs-0.8.17.tgz",
"integrity": "sha1-xNWY6taUkRJlPWWIsBpc3Nn5D90=",
"requires": {
"core-js": "^1.0.0",
"isomorphic-fetch": "^2.1.1",
"loose-envify": "^1.0.0",
"object-assign": "^4.1.0",
"promise": "^7.1.1",
"setimmediate": "^1.0.5",
"ua-parser-js": "^0.7.18"
},
"dependencies": {
"core-js": {
"version": "1.2.7",
"resolved": "https://registry.npmjs.org/core-js/-/core-js-1.2.7.tgz",
"integrity": "sha1-ZSKUwUZR2yj6k70tX/KYOk8IxjY="
},
"promise": {
"version": "7.3.1",
"resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz",
"integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==",
"requires": {
"asap": "~2.0.3"
}
}
}
},
"figgy-pudding": {
"version": "3.5.1",
"resolved": "https://registry.npmjs.org/figgy-pudding/-/figgy-pudding-3.5.1.tgz",
@ -7340,6 +7432,15 @@
"resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
"integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8="
},
"isomorphic-fetch": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz",
"integrity": "sha1-YRrhrPFPXoH3KVB0coGf6XM1WKk=",
"requires": {
"node-fetch": "^1.0.1",
"whatwg-fetch": ">=0.10.0"
}
},
"isstream": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
@ -8305,6 +8406,11 @@
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz",
"integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg=="
},
"lodash._getnative": {
"version": "3.9.1",
"resolved": "https://registry.npmjs.org/lodash._getnative/-/lodash._getnative-3.9.1.tgz",
"integrity": "sha1-VwvH3t5G1hzc3mh9ZdPuy6o6r/U="
},
"lodash._reinterpolate": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz",
@ -8320,6 +8426,16 @@
"resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
"integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168="
},
"lodash.isarguments": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz",
"integrity": "sha1-L1c9hcaiQon/AGY7SRwdM4/zRYo="
},
"lodash.isarray": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/lodash.isarray/-/lodash.isarray-3.0.4.tgz",
"integrity": "sha1-eeTriMNqgSKvhvhEqpvNhRtfu1U="
},
"lodash.isfunction": {
"version": "3.0.9",
"resolved": "https://registry.npmjs.org/lodash.isfunction/-/lodash.isfunction-3.0.9.tgz",
@ -8330,6 +8446,16 @@
"resolved": "https://registry.npmjs.org/lodash.isobject/-/lodash.isobject-3.0.2.tgz",
"integrity": "sha1-PI+41bW/S/kK4G4U8qUwpO2TXh0="
},
"lodash.keys": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-3.1.2.tgz",
"integrity": "sha1-TbwEcrFWvlCgsoaFXRvQsMZWCYo=",
"requires": {
"lodash._getnative": "^3.0.0",
"lodash.isarguments": "^3.0.0",
"lodash.isarray": "^3.0.0"
}
},
"lodash.memoize": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz",
@ -8448,6 +8574,11 @@
"object-visit": "^1.0.0"
}
},
"material-colors": {
"version": "1.2.6",
"resolved": "https://registry.npmjs.org/material-colors/-/material-colors-1.2.6.tgz",
"integrity": "sha512-6qE4B9deFBIa9YSpOc9O0Sgc43zTeVYbgDT5veRKSlB2+ZuHNoVVxA1L/ckMUayV9Ay9y7Z/SZCLcGteW9i7bg=="
},
"math-random": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/math-random/-/math-random-1.0.1.tgz",
@ -8786,6 +8917,15 @@
"lower-case": "^1.1.1"
}
},
"node-fetch": {
"version": "1.7.3",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-1.7.3.tgz",
"integrity": "sha512-NhZ4CsKx7cYm2vSrBAr2PvFOe6sWDf0UYLRqA6svUYg7+/TSfVAu49jYC4BvQ4Sms9SZgdqGBgroqfDhJdTyKQ==",
"requires": {
"encoding": "^0.1.11",
"is-stream": "^1.0.1"
}
},
"node-forge": {
"version": "0.7.5",
"resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.7.5.tgz",
@ -11406,6 +11546,66 @@
}
}
},
"rc-align": {
"version": "2.4.5",
"resolved": "https://registry.npmjs.org/rc-align/-/rc-align-2.4.5.tgz",
"integrity": "sha512-nv9wYUYdfyfK+qskThf4BQUSIadeI/dCsfaMZfNEoxm9HwOIioQ+LyqmMK6jWHAZQgOzMLaqawhuBXlF63vgjw==",
"requires": {
"babel-runtime": "^6.26.0",
"dom-align": "^1.7.0",
"prop-types": "^15.5.8",
"rc-util": "^4.0.4"
}
},
"rc-animate": {
"version": "2.6.0",
"resolved": "https://registry.npmjs.org/rc-animate/-/rc-animate-2.6.0.tgz",
"integrity": "sha512-JXDycchgbOI+7T/VKmFWnAIn042LLScK1fNkmNunb0jz5q5aPGCAybx2bTo7X5t31Jkj9OsxKNb/vZPDPWufCg==",
"requires": {
"babel-runtime": "6.x",
"classnames": "^2.2.6",
"css-animation": "^1.3.2",
"prop-types": "15.x",
"raf": "^3.4.0",
"react-lifecycles-compat": "^3.0.4"
}
},
"rc-color-picker": {
"version": "1.2.6",
"resolved": "https://registry.npmjs.org/rc-color-picker/-/rc-color-picker-1.2.6.tgz",
"integrity": "sha512-AaC9Pg7qCHSy5M4eVbqDIaNb2FC4SEw82GOHB2C4R/+vF2FVa/r5XA+Igg5+zLPmAvBLhz9tL4MAfkRA8yWNJw==",
"requires": {
"classnames": "^2.2.5",
"prop-types": "^15.5.8",
"rc-trigger": "1.x",
"rc-util": "^4.0.2",
"tinycolor2": "^1.4.1"
}
},
"rc-trigger": {
"version": "1.11.5",
"resolved": "https://registry.npmjs.org/rc-trigger/-/rc-trigger-1.11.5.tgz",
"integrity": "sha512-MBuUPw1nFzA4K7jQOwb7uvFaZFjXGd00EofUYiZ+l/fgKVq8wnLC0lkv36kwqM7vfKyftRo2sh7cWVpdPuNnnw==",
"requires": {
"babel-runtime": "6.x",
"create-react-class": "15.x",
"prop-types": "15.x",
"rc-align": "2.x",
"rc-animate": "2.x",
"rc-util": "4.x"
}
},
"rc-util": {
"version": "4.6.0",
"resolved": "https://registry.npmjs.org/rc-util/-/rc-util-4.6.0.tgz",
"integrity": "sha512-rbgrzm1/i8mgfwOI4t1CwWK7wGe+OwX+dNa7PVMgxZYPBADGh86eD4OcJO1UKGeajIMDUUKMluaZxvgraQIOmw==",
"requires": {
"add-dom-event-listener": "^1.1.0",
"babel-runtime": "6.x",
"prop-types": "^15.5.10",
"shallowequal": "^0.2.2"
}
},
"react": {
"version": "16.8.1",
"resolved": "https://registry.npmjs.org/react/-/react-16.8.1.tgz",
@ -11447,6 +11647,19 @@
}
}
},
"react-color": {
"version": "2.17.0",
"resolved": "https://registry.npmjs.org/react-color/-/react-color-2.17.0.tgz",
"integrity": "sha512-kJfE5tSaFe6GzalXOHksVjqwCPAsTl+nzS9/BWfP7j3EXbQ4IiLAF9sZGNzk3uq7HfofGYgjmcUgh0JP7xAQ0w==",
"requires": {
"@icons/material": "^0.2.4",
"lodash": ">4.17.4",
"material-colors": "^1.2.1",
"prop-types": "^15.5.10",
"reactcss": "^1.2.0",
"tinycolor2": "^1.4.1"
}
},
"react-dev-utils": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-7.0.1.tgz",
@ -11719,6 +11932,14 @@
"react-lifecycles-compat": "^3.0.4"
}
},
"reactcss": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/reactcss/-/reactcss-1.2.3.tgz",
"integrity": "sha512-KiwVUcFu1RErkI97ywr8nvx8dNOpT03rbnma0SSalTYjkrPYaEajR4a/MRt6DZ46K6arDRbWMNHF+xH7G7n/8A==",
"requires": {
"lodash": "^4.0.1"
}
},
"reactn": {
"version": "0.2.2",
"resolved": "https://registry.npmjs.org/reactn/-/reactn-0.2.2.tgz",
@ -13016,6 +13237,14 @@
}
}
},
"shallowequal": {
"version": "0.2.2",
"resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-0.2.2.tgz",
"integrity": "sha1-HjL9W8q2rWiKSBLLDMBO/HXHAU4=",
"requires": {
"lodash.keys": "^3.1.2"
}
},
"shebang-command": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz",
@ -13819,6 +14048,11 @@
"resolved": "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz",
"integrity": "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q="
},
"tinycolor2": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/tinycolor2/-/tinycolor2-1.4.1.tgz",
"integrity": "sha1-9PrTM0R7wLB9TcjpIJ2POaisd+g="
},
"tmp": {
"version": "0.0.33",
"resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz",
@ -13960,6 +14194,11 @@
"resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
"integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c="
},
"ua-parser-js": {
"version": "0.7.19",
"resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.19.tgz",
"integrity": "sha512-T3PVJ6uz8i0HzPxOF9SWzWAlfN/DavlpQqepn22xgve/5QecC+XMCAtmUNnY7C9StehaV6exjUCI801lOI7QlQ=="
},
"uglify-js": {
"version": "3.4.9",
"resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.4.9.tgz",

9
webui/package.json

@ -3,14 +3,17 @@
"version": "0.1.0",
"private": true,
"dependencies": {
"@jaames/iro": "^4.0.1",
"bootstrap": "^4.2.1",
"rc-color-picker": "^1.2.6",
"react": "^16.8.1",
"react-color": "^2.17.0",
"react-dom": "^16.8.1",
"react-router": "^4.3.1",
"react-router-dom": "^4.3.1",
"react-scripts": "2.1.3",
"reactstrap": "^7.1.0",
"reactn": "^0.2.2",
"react-router-dom": "^4.3.1",
"react-router": "^4.3.1"
"reactstrap": "^7.1.0"
},
"devDependencies": {
"@types/react": "^16.8.1",

9
webui/src/App.css

@ -5,3 +5,12 @@
a, button {
cursor: pointer;
}
.badge {
cursor: pointer;
}
.loading {
margin: 2em 0;
text-align: center;
}

15
webui/src/App.js

@ -1,13 +1,15 @@
import React, {useEffect, useState} from 'react';
import './App.css';
import Header from "./Components/Header";
import LoginForm from "./Components/LoginForm";
import {Container, Spinner} from "reactstrap";
import Header from "./Components/Structure/Header";
import LoginForm from "./Components/Forms/LoginForm";
import {Container} from "reactstrap";
import {BrowserRouter} from "react-router-dom";
import {Route} from "react-router";
import {setGlobal} from "reactn";
import useAuth from "./Hooks/auth";
import IndexPage from "./Components/IndexPage";
import IndexPage from "./Components/Pages/IndexPage";
import GroupPage from "./Components/Pages/GroupPage";
import Loading from "./Components/Loading";
setGlobal({
"auth/login": false,
@ -30,11 +32,12 @@ export default function App() {
<>
<Header/>
<Container>
{!isChecked && <Spinner color="primary" style={{width: "4rem", height: "4rem"}}/>}
{!isChecked && <Loading/>}
{isChecked && !isLoggedIn && <LoginForm/>}
{isChecked && isLoggedIn && (
<>
<Route exact path="/" component={IndexPage} />
<Route exact path="/" component={IndexPage}/>
<Route exact path="/groups" component={GroupPage}/>
</>
)}
</Container>

4
webui/src/Components/LoginForm.jsx → webui/src/Components/Forms/LoginForm.jsx

@ -16,8 +16,8 @@ import {
TabPane
} from "reactstrap";
import TabContent from "reactstrap/es/TabContent";
import useAuth from "../Hooks/auth";
import {onEnter} from "../Helpers/keys";
import useAuth from "../../Hooks/auth";
import {onEnter} from "../../Helpers/keys";
export default function LoginForm() {
const {login, register} = useAuth();

11
webui/src/Components/Group.jsx

@ -1,13 +1,20 @@
import React from "react";
import {Button, Card, CardFooter, CardHeader} from "reactstrap";
import {Button, Card, CardBody, CardFooter, CardHeader, ListGroup} from "reactstrap";
import useLights from "../Hooks/light";
import Light from "./Light";
import Loading from "./Loading";
function Group({id, name, permissions}) {
function Group({id, name}) {
const lights = useLights({groupId: id});
return (
<Card className="mt-3">
<CardHeader>{name}</CardHeader>
<CardBody>
{lights !== null
? <ListGroup>{lights.map(light => <Light key={light.id} {...light} />)}</ListGroup>
: <Loading/>}
</CardBody>
<CardFooter>
<Button color="primary">Detaljer</Button>
{" "}

10
webui/src/Components/Groups.jsx

@ -1,22 +1,18 @@
import React from "react";
import {Spinner} from "reactstrap";
import useGroups from "../Hooks/group";
import Group from "./Group";
import Loading from "./Loading";
function Groups() {
const groups = useGroups();
if (groups === null) {
return (
<div>
<Spinner color="primary" style={{width: "4rem", height: "4rem"}}/>
</div>
);
return <Loading/>;
}
return (
<div>
{groups.map(group => <Group {...group} />)}
{groups.map(group => <Group key={group.id} {...group} />)}
</div>
);
}

40
webui/src/Components/Light.jsx

@ -0,0 +1,40 @@
import React, {useState} from "react";
import {Badge, Col, ListGroupItem, Row} from "reactstrap";
import {percentage} from "../Helpers/percentage";
import ColorModal from "./Modals/ColorModal";
import {changeColor} from "../Helpers/lights";
function Light({id, name, color, brightness}) {
const pc = percentage(brightness, 255);
const [modal, setModal] = useState(false);
return (
<ListGroupItem>
<Row>
<Col xs={7}>
{name}
</Col>
<Col xs={3}>
<Badge style={{backgroundColor: `#${color}`}} onClick={() => setModal(true)}>
{color.toUpperCase()}
</Badge>
</Col>
<Col xs={2}>
<Badge style={{backgroundColor: `gray(${brightness})`}}>{pc}</Badge>
</Col>
</Row>
{modal && (
<ColorModal value={color}
onConfirm={newColor => {
changeColor(id, newColor);
setModal(false);
}}
onCancel={() => setModal(false)}
/>
)}
</ListGroupItem>
);
}
export default Light;

12
webui/src/Components/Loading.jsx

@ -0,0 +1,12 @@
import React from "react";
import {Spinner} from "reactstrap";
function Loading() {
return (
<div className="loading">
<Spinner color="warning" style={{width: "4rem", height: "4rem"}}/>
</div>
);
}
export default Loading;

31
webui/src/Components/Misc/ColorPicker.jsx

@ -0,0 +1,31 @@
import React, {useLayoutEffect} from "react";
import iro from '@jaames/iro';
import {randId} from "../../Helpers/random";
function ColorPicker({color, onChange}) {
const random = randId();
useLayoutEffect(() => {
const colorPicker = new iro.ColorPicker("#color-picker-" + random, {
color: `#${color}`,
layout: [
{
component: iro.ui.Wheel,
options: {}
}
],
});
colorPicker.on("input:end", color => {
onChange(color.hexString.substr(1));
})
}, []);
return (
<div id={`color-picker-${random}`}>
</div>
);
}
export default ColorPicker;

23
webui/src/Components/Modals/ColorModal.jsx

@ -0,0 +1,23 @@
import React, {useState} from "react";
import {Button, Modal, ModalBody, ModalFooter, ModalHeader} from "reactstrap";
import ColorPicker from "../Misc/ColorPicker";
function ColorModal({value, onConfirm, onCancel}) {
const [color, setColor] = useState(value);
return (
<Modal isOpen={true}>
<ModalHeader>Fargevalg</ModalHeader>
<ModalBody style={{margin: "0 auto"}}>
<ColorPicker color={color} onChange={setColor}/>
</ModalBody>
<ModalFooter>
<Button color="primary" onClick={() => onConfirm(color)}>Lagre</Button>
{" "}
<Button color="secondary" onClick={onCancel}>Avbryt</Button>
</ModalFooter>
</Modal>
);
}
export default ColorModal;

10
webui/src/Components/Pages/GroupPage.jsx

@ -0,0 +1,10 @@
import React from "react";
import Groups from "./IndexPage";
function GroupPage() {
return (
<Groups/>
);
}
export default GroupPage;

2
webui/src/Components/IndexPage.jsx → webui/src/Components/Pages/IndexPage.jsx

@ -1,5 +1,5 @@
import React from "react";
import Groups from "./Groups";
import Groups from "../Groups";
function IndexPage() {
return (

4
webui/src/Components/Header.jsx → webui/src/Components/Structure/Header.jsx

@ -1,8 +1,8 @@
import React, {useState} from "react"
import {Collapse, Nav, Navbar, NavbarBrand, NavbarToggler, NavItem, NavLink} from "reactstrap";
import useAuth from "../Hooks/auth";
import useAuth from "../../Hooks/auth";
import {Link} from "react-router-dom";
import {onEnter} from "../Helpers/keys";
import {onEnter} from "../../Helpers/keys";
export default function Header() {
const [showMenu, setShowMenu] = useState(false);

26
webui/src/Helpers/lights.js

@ -29,6 +29,30 @@ export function subscribeToLight(lightId, callback) {
}
}
export function unsubscribeFromLight(callbackId) {
const callback = callbacks.find(c => c !== null && c.callbackId === callbackId);
const index = callbacks.indexOf(callback);
callbacks[index] = null;
}
export function changeColor(lightId, newColor) {
console.dir(localData);
const light = localData[lightId];
if (nullish(light)) {
return;
}
localData[lightId].color = newColor;
dispatch(Object.values(localData));
// TODO: Send request
}
export function changeBrightness(lightId, newBrightness) {
}
function fetchAll() {
fetchGet(`/light/`).then(({data, error}) => {
if (error === null) {
@ -49,7 +73,7 @@ function handleLights(lights) {
lights.forEach(l => handleLight(l));
for (let key in localData) {
if (localData.hasOwnProperty(key) && nullish(lights.find(l => l.id === key))) {
if (localData.hasOwnProperty(key) && nullish(lights.find(l => l.id === parseInt(key, 10)))) {
delete localData[key];
}
}

3
webui/src/Helpers/percentage.js

@ -0,0 +1,3 @@
export function percentage(value, max) {
return (100 * value / max).toFixed(1) + " %";
}

6
webui/src/Hooks/light.js

@ -1,10 +1,10 @@
import {useEffect, useState} from "react";
import {subscribeToLight} from "../Helpers/lights";
import {subscribeToLight, unsubscribeFromLight} from "../Helpers/lights";
export default function useLights({groupId = -1, id = -1}) {
const [light, setLight] = useState(null);
function onChange() {
function onChange(light) {
setLight(light);
}
@ -12,7 +12,7 @@ export default function useLights({groupId = -1, id = -1}) {
const cbId = subscribeToLight(id, onChange);
return () => {
subscribeToLight(cbId);
unsubscribeFromLight(cbId);
};
}, []);

Loading…
Cancel
Save