From 22c80a90a1899797debd1dfbe494e43d77458cfb Mon Sep 17 00:00:00 2001 From: Stian Fredrik Aune Date: Thu, 10 Jun 2021 21:48:09 +0200 Subject: [PATCH] dialogs sorta work now --- webui/package.json | 5 +- webui/src/App.tsx | 49 ++++++++++++++------ webui/src/contexts/DialogContext.tsx | 34 ++++++++++++++ webui/src/models/Dialog.ts | 9 ++++ webui/src/models/Icons.ts | 23 ++++++++++ webui/src/pages/SettingsPage.tsx | 3 +- webui/src/primitives/Elements.tsx | 20 ++++++-- webui/src/primitives/Layout.sass | 69 +++++++++++++++++++++++++++- webui/src/primitives/Layout.tsx | 41 +++++++++++++---- webui/src/res/colors.sass | 3 ++ webui/yarn.lock | 19 +++++--- 11 files changed, 238 insertions(+), 37 deletions(-) create mode 100644 webui/src/contexts/DialogContext.tsx create mode 100644 webui/src/models/Dialog.ts 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)}/> -
+ + + Test + +

 

+

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"