diff --git a/webui/package.json b/webui/package.json
index 6405cb5..179e735 100644
--- a/webui/package.json
+++ b/webui/package.json
@@ -19,7 +19,7 @@
"node-sass": "4.14.1",
"react": "^17.0.2",
"react-dom": "^17.0.2",
- "react-modal": "^3.13.1",
+ "react-modal": "^3.14.2",
"react-scripts": "4.0.3",
"react-semantic-ui-range": "^0.7.1",
"typescript": "^4.1.2",
@@ -50,6 +50,7 @@
]
},
"devDependencies": {
- "@types/hookrouter": "^2.2.5"
+ "@types/hookrouter": "^2.2.5",
+ "@types/react-modal": "^3.12.0"
}
}
diff --git a/webui/src/App.tsx b/webui/src/App.tsx
index 9e33ac0..b56139c 100644
--- a/webui/src/App.tsx
+++ b/webui/src/App.tsx
@@ -1,16 +1,24 @@
-import React from 'react';
+import React, {useContext, useLayoutEffect} from 'react';
import {HookRouter, navigate, usePath, useRoutes} from "hookrouter";
-import {Tabs} from "./primitives/Layout";
-import {HSColorPicker} from "./primitives/Forms";
+import {Dialog, Page, Tabs} from "./primitives/Layout";
import {IconElement} from "./primitives/Elements";
import SettingsPage from "./pages/SettingsPage";
import {LuciferIcon} from "./models/Icons";
+import ReactModal from "react-modal";
+import DialogContext, {DialogContextProvider, makeDialog} from "./contexts/DialogContext";
const routeObj: HookRouter.RouteObject = {
"/": () => (
-
- void (0)}/>
-
+
+
+
+ Enheter
+
),
"/devices": () => (
@@ -19,12 +27,13 @@ const routeObj: HookRouter.RouteObject = {
-
-
-
-
-
-
+
+
+
+
+
+
+
@@ -37,8 +46,21 @@ const routeList = ["/", "/devices", "/settings"];
const tabNames = ["Lucifer", "Enheter", "Oppsett"];
function App() {
+ return (
+
+
+
+ );
+}
+
+function AppInContext() {
const route = useRoutes(routeObj);
const path = usePath();
+ const {dialog} = useContext(DialogContext);
+
+ useLayoutEffect(() => {
+ ReactModal.setAppElement("#root");
+ }, []);
return (
@@ -47,7 +69,8 @@ function App() {
onChange={i => navigate(routeList[i])}
large boldIndex={0}
/>
- {route ||
B
}
+ {route ||
404}
+ {makeDialog(dialog)}
);
}
diff --git a/webui/src/contexts/DialogContext.tsx b/webui/src/contexts/DialogContext.tsx
new file mode 100644
index 0000000..c7e3d2c
--- /dev/null
+++ b/webui/src/contexts/DialogContext.tsx
@@ -0,0 +1,34 @@
+import React, {createContext, useState} from "react";
+import {DialogConfig, DialogType} from "../models/Dialog";
+import {unimplemented} from "../helpers/misc";
+
+interface DialogContextValue {
+ dialog: DialogConfig,
+ setDialog: (dialog: DialogConfig) => void,
+}
+
+const DialogContext = createContext({
+ dialog: {type: DialogType.None},
+ setDialog: unimplemented,
+});
+
+export const DialogContextProvider: React.FC = ({children}) => {
+ const [dialog, setDialog] = useState({type: DialogType.None});
+
+ return (
+
+ {children}
+
+ );
+};
+
+export function makeDialog(dialog: DialogConfig) {
+ switch (dialog.type) {
+ case DialogType.None:
+ return undefined;
+ case DialogType.AddColor:
+ return undefined;
+ }
+}
+
+export default DialogContext;
diff --git a/webui/src/models/Dialog.ts b/webui/src/models/Dialog.ts
new file mode 100644
index 0000000..d9aa456
--- /dev/null
+++ b/webui/src/models/Dialog.ts
@@ -0,0 +1,9 @@
+export enum DialogType {
+ None,
+ AddColor,
+}
+
+export type DialogConfig =
+ | { type: DialogType.None }
+ | { type: DialogType.AddColor }
+;
diff --git a/webui/src/models/Icons.ts b/webui/src/models/Icons.ts
index 0200710..7fe027f 100644
--- a/webui/src/models/Icons.ts
+++ b/webui/src/models/Icons.ts
@@ -1,26 +1,49 @@
import bridgeIcon from "@iconify-icons/mdi/bridge";
+import checkIcon from "@iconify-icons/mdi/check";
+import crossIcon from "@iconify-icons/mdi/window-close";
+import cogIcon from "@iconify-icons/mdi/cog";
import hexagonIcon from "@iconify-icons/mdi/hexagon";
+import hexagonGroupIcon from "@iconify-icons/mdi/hexagon-multiple";
import lightBulbIcon from "@iconify-icons/mdi/lightbulb";
import lightBulbGroupIcon from "@iconify-icons/mdi/lightbulb-group";
+import lightSwitchIcon from '@iconify-icons/mdi/light-switch';
+import routerIcon from "@iconify-icons/mdi/router-wireless";
import squareIcon from "@iconify-icons/mdi/square-rounded";
+import triangleIcon from "@iconify-icons/mdi/triangle";
+
+
// To add a new icon, follow the instructions on this page:
// https://iconify.design/icon-sets/mdi/
export enum LuciferIcon {
Bridge = "Bridge",
+ Check = "Check",
+ Cog = "Cog",
+ Cross = "Cross",
Bulb = "Bulb",
BulbGroup = "BulbGroup",
Hexagon = "Hexagon",
+ HexagonGroup = "HexagonGroup",
+ Router = "Router",
Square = "Square",
+ Switch = "Switch",
+ Triangle = "Triangle",
}
const iconMap = {
[LuciferIcon.Bridge]: bridgeIcon,
+ [LuciferIcon.Check]: checkIcon,
+ [LuciferIcon.Cog]: cogIcon,
+ [LuciferIcon.Cross]: crossIcon,
[LuciferIcon.Bulb]: lightBulbIcon,
[LuciferIcon.BulbGroup]: lightBulbGroupIcon,
[LuciferIcon.Hexagon]: hexagonIcon,
+ [LuciferIcon.HexagonGroup]: hexagonGroupIcon,
+ [LuciferIcon.Router]: routerIcon,
[LuciferIcon.Square]: squareIcon,
+ [LuciferIcon.Switch]: lightSwitchIcon,
+ [LuciferIcon.Triangle]: triangleIcon,
}
export function toIconify(icon: LuciferIcon): object {
diff --git a/webui/src/pages/SettingsPage.tsx b/webui/src/pages/SettingsPage.tsx
index 0c2b893..d11f63d 100644
--- a/webui/src/pages/SettingsPage.tsx
+++ b/webui/src/pages/SettingsPage.tsx
@@ -2,10 +2,9 @@ import React, {useContext, useEffect} from "react";
import {Page} from "../primitives/Layout";
import DataContext from "../contexts/DataContext";
import {IconElement} from "../primitives/Elements";
-import {addColorPreset} from "../actions/ColorPresets";
const SettingsPage: React.FC = () => {
- const {colorPresets, refresh} = useContext(DataContext);
+ const {colorPresets} = useContext(DataContext);
useEffect(() => {
}, []);
diff --git a/webui/src/primitives/Elements.tsx b/webui/src/primitives/Elements.tsx
index 9c32497..f16e137 100644
--- a/webui/src/primitives/Elements.tsx
+++ b/webui/src/primitives/Elements.tsx
@@ -12,13 +12,12 @@ function Element({children}: React.PropsWithChildren<{}>) {
)
}
-interface ColorElementProps {
- caption: string
+interface IconProps {
icon?: LuciferIcon
color?: ColorValue | "hs-gradient" | "k-gradient"
}
-export function IconElement({caption, color, icon}: ColorElementProps) {
+export function IconBlock({icon, color}: IconProps) {
const style: CSSProperties = useMemo(() => {
const s: CSSProperties = {
backgroundClip: "text",
@@ -44,9 +43,22 @@ export function IconElement({caption, color, icon}: ColorElementProps) {
return s;
}, [color]);
+ return (
+
+ );
+}
+
+interface IconElementProps extends IconProps {
+ caption: string
+}
+
+export function IconElement({caption, color, icon}: IconElementProps) {
return (
-
+
{caption}
);
diff --git a/webui/src/primitives/Layout.sass b/webui/src/primitives/Layout.sass
index 77c5fe5..2ae33dd 100644
--- a/webui/src/primitives/Layout.sass
+++ b/webui/src/primitives/Layout.sass
@@ -1,7 +1,7 @@
@import "../res/colors"
.Tabs-container
- background-color: $color-foreground-dark
+ background-color: $color-background-elevated
&.Tabs-large
text-align: center
@@ -22,4 +22,69 @@
.Page
max-width: 800px
- margin: 0.25em auto
\ No newline at end of file
+ margin: 0.25em auto
+
+
+.Dialog-overlay
+ background-color: rgba(0, 0, 0, 0.5)
+ position: fixed
+ left: 0
+ top: 0
+ right: 0
+ bottom: 0
+ overflow-y: scroll
+
+.Dialog-main
+ max-width: 640px
+ margin: 4em auto
+ // background-color: #202020
+ color: #ddd
+ outline: none
+ box-shadow: 5px 10px 20px rgba(0, 0, 0, 0.5)
+
+ .Card
+ margin: 0
+
+.Dialog-title
+ font-weight: 800
+ font-size: 200%
+ text-align: center
+ padding: 0.25em 0.5ch
+
+.Dialog-body
+ margin-bottom: 0
+
+.Dialog-footer
+ border-top: solid 1px #000
+
+.Dialog-container
+ background-color: $color-background
+
+.Dialog-header
+ font-size: 140%
+ font-weight: bold
+ text-align: center
+ padding: 0.1em 0
+ background-color: $color-background-elevated
+
+.Dialog-footer
+ display: flex
+ flex-direction: row
+
+ button
+ flex-grow: 4
+ text-align: center
+ font: inherit
+ padding: 0.5em 1.5ch
+ background-color: $color-background-elevated
+ color: $color-foreground
+ border: none
+ cursor: pointer
+
+ &:hover, &:active
+ background-color: $color-background-elevated2
+ color: $color-foreground-elevated
+
+
+ &:not(:last-of-type)
+ border-right: solid 1px #000
diff --git a/webui/src/primitives/Layout.tsx b/webui/src/primitives/Layout.tsx
index c657322..de54564 100644
--- a/webui/src/primitives/Layout.tsx
+++ b/webui/src/primitives/Layout.tsx
@@ -1,5 +1,8 @@
import React, {PropsWithChildren, useCallback, useEffect} from "react";
+import ReactModal from "react-modal";
import "./Layout.sass";
+import {LuciferIcon} from "../models/Icons";
+import {IconBlock} from "./Elements";
interface TabsProps {
tabNames: string[]
@@ -36,11 +39,11 @@ export function Tabs({tabNames, index, onChange, large, boldIndex}: TabsProps) {
{tabNames.map((name, i) => (
-
onChange(i)}
- >
- {name}
-
+
onChange(i)}
+ >
+ {name}
+
))}
@@ -54,7 +57,7 @@ interface PageProps {
export function Page({title, children}: PropsWithChildren) {
useEffect(() => {
window.document.title = title ? `${title} - Lucifer` : "Lucifer";
- }, []);
+ }, [title]);
return (
@@ -67,13 +70,35 @@ interface DialogProps {
title: string
buttons?: {
text: string
+ icon: LuciferIcon
onClick?: (() => void)
}[]
loading?: boolean
}
-export function Dialog({}: PropsWithChildren
) {
+export function Dialog({title, buttons, children}: PropsWithChildren) {
- return undefined;
+ return (
+
+
+
+ {title}
+
+ {children}
+
+ {buttons?.map(b => (
+
+ ))}
+
+
+
+ );
}
diff --git a/webui/src/res/colors.sass b/webui/src/res/colors.sass
index 4205a78..8ad08b3 100644
--- a/webui/src/res/colors.sass
+++ b/webui/src/res/colors.sass
@@ -1,8 +1,11 @@
$color-background: #111
$color-background-elevated: #222
+$color-background-elevated2: #333
$color-foreground: rgb(204, 204, 204)
+$color-foreground-elevated: #FFF
$color-foreground-dark: rgba(238, 238, 238, 0.05)
+
body, html
background-color: $color-background
color: $color-foreground
diff --git a/webui/yarn.lock b/webui/yarn.lock
index 8e6cda2..003709c 100644
--- a/webui/yarn.lock
+++ b/webui/yarn.lock
@@ -1829,6 +1829,13 @@
dependencies:
"@types/react" "*"
+"@types/react-modal@^3.12.0":
+ version "3.12.0"
+ resolved "https://registry.yarnpkg.com/@types/react-modal/-/react-modal-3.12.0.tgz#91fa86a76fd7fc57e36d2cf9b76efe5735a855a1"
+ integrity sha512-UnHu/YO73NZLwqFpju/c0tqiswR0UHIfeO16rkfLVJUIMfQsl7X0CBm99H5XXgBMe/PgtQ/Rkud72iuRBr1TeA==
+ dependencies:
+ "@types/react" "*"
+
"@types/react@*", "@types/react@^17.0.0":
version "17.0.6"
resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.6.tgz#0ec564566302c562bf497d73219797a5e0297013"
@@ -9193,7 +9200,7 @@ prompts@2.4.0, prompts@^2.0.1:
kleur "^3.0.3"
sisteransi "^1.0.5"
-prop-types@^15.5.10, prop-types@^15.7.2:
+prop-types@^15.7.2:
version "15.7.2"
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5"
integrity sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==
@@ -9433,13 +9440,13 @@ react-lifecycles-compat@^3.0.0:
resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362"
integrity sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==
-react-modal@^3.13.1:
- version "3.13.1"
- resolved "https://registry.yarnpkg.com/react-modal/-/react-modal-3.13.1.tgz#a02dce63bbfee7582936f1e9835d518ef8f56453"
- integrity sha512-m6yXK7I4YKssQnsjHK7xITSXy2O81BSOHOsg0/uWAsdKtuT9HF2tdoYhRuxNNQg2V+LgepsoHUPJKS8m6no+eg==
+react-modal@^3.14.2:
+ version "3.14.2"
+ resolved "https://registry.yarnpkg.com/react-modal/-/react-modal-3.14.2.tgz#e04e1119d64c9fbb3db80736eb80ea9372f28778"
+ integrity sha512-CYasEJanwneDsmvtx/fisXhgDxtt3I8jWTVX/tP9dM/J1NgDKU9lgjR9zuCCl33ub2jrTWhXyijCxCzYGN8sJg==
dependencies:
exenv "^1.2.0"
- prop-types "^15.5.10"
+ prop-types "^15.7.2"
react-lifecycles-compat "^3.0.0"
warning "^4.0.3"