started on icons
This commit is contained in:
@@ -3,6 +3,8 @@
|
|||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@iconify-icons/mdi": "^1.1.15",
|
||||||
|
"@iconify/react": "^1.1.4",
|
||||||
"@jaames/iro": "^5.5.1",
|
"@jaames/iro": "^5.5.1",
|
||||||
"@testing-library/jest-dom": "^5.11.4",
|
"@testing-library/jest-dom": "^5.11.4",
|
||||||
"@testing-library/react": "^11.1.0",
|
"@testing-library/react": "^11.1.0",
|
||||||
@@ -11,11 +13,13 @@
|
|||||||
"@types/node": "^12.0.0",
|
"@types/node": "^12.0.0",
|
||||||
"@types/react": "^17.0.0",
|
"@types/react": "^17.0.0",
|
||||||
"@types/react-dom": "^17.0.0",
|
"@types/react-dom": "^17.0.0",
|
||||||
|
"axios": "^0.21.1",
|
||||||
"hookrouter": "^1.2.5",
|
"hookrouter": "^1.2.5",
|
||||||
"kelvin-to-rgb": "^1.0.2",
|
"kelvin-to-rgb": "^1.0.2",
|
||||||
"node-sass": "4.14.1",
|
"node-sass": "4.14.1",
|
||||||
"react": "^17.0.2",
|
"react": "^17.0.2",
|
||||||
"react-dom": "^17.0.2",
|
"react-dom": "^17.0.2",
|
||||||
|
"react-modal": "^3.13.1",
|
||||||
"react-scripts": "4.0.3",
|
"react-scripts": "4.0.3",
|
||||||
"react-semantic-ui-range": "^0.7.1",
|
"react-semantic-ui-range": "^0.7.1",
|
||||||
"typescript": "^4.1.2",
|
"typescript": "^4.1.2",
|
||||||
|
|||||||
+18
-16
@@ -2,7 +2,9 @@ import React from 'react';
|
|||||||
import {HookRouter, navigate, usePath, useRoutes} from "hookrouter";
|
import {HookRouter, navigate, usePath, useRoutes} from "hookrouter";
|
||||||
import {Tabs} from "./primitives/Layout";
|
import {Tabs} from "./primitives/Layout";
|
||||||
import {HSColorPicker} from "./primitives/Forms";
|
import {HSColorPicker} from "./primitives/Forms";
|
||||||
import {ColorElement} from "./primitives/Elements";
|
import {IconElement} from "./primitives/Elements";
|
||||||
|
import SettingsPage from "./pages/SettingsPage";
|
||||||
|
import {LuciferIcon} from "./models/Icons";
|
||||||
|
|
||||||
const routeObj: HookRouter.RouteObject = {
|
const routeObj: HookRouter.RouteObject = {
|
||||||
"/": () => (
|
"/": () => (
|
||||||
@@ -12,22 +14,22 @@ const routeObj: HookRouter.RouteObject = {
|
|||||||
),
|
),
|
||||||
"/devices": () => (
|
"/devices": () => (
|
||||||
<div>
|
<div>
|
||||||
<ColorElement name="Grønn" value={{h: 125, s: 0.5}}/>
|
<IconElement icon={LuciferIcon.Hexagon} caption="Grønn" color={{h: 125, s: 0.5}}/>
|
||||||
<ColorElement name="Kelvin 1k" value={{kelvin: 1000}}/>
|
<IconElement icon={LuciferIcon.Square} caption="Kelvin 1k" color={{kelvin: 1000}}/>
|
||||||
<ColorElement name="Kelvin 2k" value={{kelvin: 2000}}/>
|
<IconElement icon={LuciferIcon.Bulb} caption="Kelvin 2k" color={{kelvin: 2000}}/>
|
||||||
<ColorElement name="Kelvin 3k" value={{kelvin: 3000}}/>
|
<IconElement icon={LuciferIcon.BulbGroup} caption="Kelvin 3k" color={{kelvin: 3000}}/>
|
||||||
<ColorElement name="Kelvin 4k" value={{kelvin: 4000}}/>
|
<IconElement icon={LuciferIcon.Bridge} caption="Kelvin 4k" color={{kelvin: 4000}}/>
|
||||||
<ColorElement name="Kelvin 5k" value={{kelvin: 5000}}/>
|
<IconElement caption="Kelvin 5k" color={{kelvin: 5000}}/>
|
||||||
<ColorElement name="Kelvin 6k" value={{kelvin: 6000}}/>
|
<IconElement caption="Kelvin 6k" color={{kelvin: 6000}}/>
|
||||||
<ColorElement name="Kelvin 7k" value={{kelvin: 7000}}/>
|
<IconElement caption="Kelvin 7k" color={{kelvin: 7000}}/>
|
||||||
<ColorElement name="Kelvin 8k" value={{kelvin: 8000}}/>
|
<IconElement caption="Kelvin 8k" color={{kelvin: 8000}}/>
|
||||||
<ColorElement name="Kelvin 9k" value={{kelvin: 9000}}/>
|
<IconElement caption="Kelvin 9k" color={{kelvin: 9000}}/>
|
||||||
<ColorElement name="Kelvin 10k" value={{kelvin: 10000}}/>
|
<IconElement caption="Kelvin 10k" color={{kelvin: 10000}}/>
|
||||||
<ColorElement name="Mer... (HS)" value="hs-gradient"/>
|
<IconElement caption="Mer" color="hs-gradient"/>
|
||||||
<ColorElement name="Mer... (K)" value="k-gradient"/>
|
<IconElement caption="Mer" color="k-gradient"/>
|
||||||
</div>
|
</div>
|
||||||
),
|
),
|
||||||
"/settings": () => <div>3</div>,
|
"/settings": () => <SettingsPage/>,
|
||||||
}
|
}
|
||||||
|
|
||||||
const routeList = ["/", "/devices", "/settings"];
|
const routeList = ["/", "/devices", "/settings"];
|
||||||
@@ -43,7 +45,7 @@ function App() {
|
|||||||
<Tabs tabNames={tabNames}
|
<Tabs tabNames={tabNames}
|
||||||
index={routeList.indexOf(path)}
|
index={routeList.indexOf(path)}
|
||||||
onChange={i => navigate(routeList[i])}
|
onChange={i => navigate(routeList[i])}
|
||||||
boldIndex={0}
|
large boldIndex={0}
|
||||||
/>
|
/>
|
||||||
{route || <div>B</div>}
|
{route || <div>B</div>}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
import {ColorPreset} from "../models/Colors";
|
||||||
|
import {getRequest, postRequest} from "../helpers/rest";
|
||||||
|
|
||||||
|
export function listColorPresets(): Promise<ColorPreset[]> {
|
||||||
|
return getRequest("/color-presets");
|
||||||
|
}
|
||||||
|
|
||||||
|
export function addColorPreset(name: string, colorString: string) {
|
||||||
|
return postRequest("/color-presets", {name, colorString});
|
||||||
|
}
|
||||||
@@ -1,5 +1,56 @@
|
|||||||
import React from "react";
|
import React, {createContext, useCallback, useEffect, useState} from "react";
|
||||||
|
import {ColorPreset} from "../models/Colors";
|
||||||
|
import {DataTarget} from "../models/Shared";
|
||||||
|
import {unimplemented} from "../helpers/misc";
|
||||||
|
import {listColorPresets} from "../actions/ColorPresets";
|
||||||
|
|
||||||
interface DataContextValue {
|
interface DataContextValue {
|
||||||
|
colorPresets: ColorPreset[]
|
||||||
|
|
||||||
|
ready(target?: DataTarget): boolean
|
||||||
|
|
||||||
|
refresh(target?: DataTarget): void
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const DataContext = createContext<DataContextValue>({
|
||||||
|
colorPresets: [],
|
||||||
|
ready: unimplemented,
|
||||||
|
refresh: unimplemented,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const DataContextProvider: React.FC = ({children}) => {
|
||||||
|
const [colorPresets, setColorPresets] = useState<ColorPreset[]>([]);
|
||||||
|
|
||||||
|
const [readyMap, setReadyMap] = useState<{ [key: string]: boolean }>({
|
||||||
|
[DataTarget.Default]: false,
|
||||||
|
[DataTarget.ColorPresets]: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
const ready = useCallback((target: DataTarget = DataTarget.Default) => {
|
||||||
|
return readyMap[target] || false;
|
||||||
|
}, [readyMap]);
|
||||||
|
|
||||||
|
const refresh = useCallback((target: DataTarget = DataTarget.Default) => {
|
||||||
|
const refreshing: Promise<any>[] = [];
|
||||||
|
if ([DataTarget.Default, DataTarget.ColorPresets].includes(target)) {
|
||||||
|
refreshing.push(listColorPresets().then(setColorPresets));
|
||||||
|
}
|
||||||
|
|
||||||
|
Promise.all(refreshing).then(() => setReadyMap(prev => ({...prev, [target]: true})));
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
refresh();
|
||||||
|
}, [refresh]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<DataContext.Provider value={{
|
||||||
|
colorPresets,
|
||||||
|
ready, refresh,
|
||||||
|
}}>
|
||||||
|
{children}
|
||||||
|
</DataContext.Provider>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default DataContext;
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
// @ts-ignore
|
|
||||||
import kelvinToRgb from "kelvin-to-rgb";
|
|
||||||
import {ColorValue} from "../models/Colors";
|
import {ColorValue} from "../models/Colors";
|
||||||
import iro from "@jaames/iro";
|
import iro from "@jaames/iro";
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
export function unimplemented<T>(): T {
|
||||||
|
throw new Error("unimplemented");
|
||||||
|
}
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
import Axios, {AxiosError, AxiosRequestConfig, AxiosResponse} from "axios";
|
||||||
|
|
||||||
|
interface Response<T> {
|
||||||
|
code: number
|
||||||
|
message: string
|
||||||
|
data: T
|
||||||
|
}
|
||||||
|
|
||||||
|
export const authError = new Error("unauthenticated");
|
||||||
|
|
||||||
|
export async function getRequest<T>(url: string): Promise<T> {
|
||||||
|
const cors = url.includes("//");
|
||||||
|
|
||||||
|
return sendRequest<T>({
|
||||||
|
method: "GET",
|
||||||
|
url: cors ? url : `/api${url}`,
|
||||||
|
responseType: "json",
|
||||||
|
}, cors);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function postRequest<T>(url: string, data: object = {}): Promise<T> {
|
||||||
|
return sendRequest<T>({
|
||||||
|
method: "POST",
|
||||||
|
url: `/api${url}`,
|
||||||
|
responseType: "json",
|
||||||
|
data,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function putRequest<T>(url: string, data: object): Promise<T> {
|
||||||
|
return sendRequest<T>({
|
||||||
|
method: "PUT",
|
||||||
|
url: `/api${url}`,
|
||||||
|
responseType: "json",
|
||||||
|
data,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function deleteRequest<T>(url: string): Promise<T> {
|
||||||
|
return sendRequest<T>({
|
||||||
|
method: "DELETE",
|
||||||
|
url: `/api${url}`,
|
||||||
|
responseType: "json",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function sendRequest<T>(config: AxiosRequestConfig, cors: boolean = false): Promise<T> {
|
||||||
|
try {
|
||||||
|
const res = await Axios(config);
|
||||||
|
|
||||||
|
return await handleResponse(res, cors);
|
||||||
|
} catch (e) {
|
||||||
|
if (e === authError) {
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
|
||||||
|
return await handleResponse((e as AxiosError).response as AxiosResponse);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleResponse<T>(response: AxiosResponse, object: boolean = false): Promise<T> {
|
||||||
|
if (object) {
|
||||||
|
return Promise.resolve(response.data);
|
||||||
|
} else {
|
||||||
|
const obj: Response<T> | undefined = response?.data;
|
||||||
|
|
||||||
|
if (![200, 201].includes(response.status)) {
|
||||||
|
if (response.status === 403) {
|
||||||
|
return Promise.reject(authError);
|
||||||
|
} else {
|
||||||
|
return Promise.reject(obj?.message || "Ukjent feil");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!obj) {
|
||||||
|
return Promise.reject("Ingen data mottatt");
|
||||||
|
}
|
||||||
|
|
||||||
|
return Promise.resolve(obj.data);
|
||||||
|
}
|
||||||
|
}
|
||||||
+4
-1
@@ -3,10 +3,13 @@ import ReactDOM from 'react-dom';
|
|||||||
import './index.css';
|
import './index.css';
|
||||||
import App from './App';
|
import App from './App';
|
||||||
import reportWebVitals from "./reportWebVitals";
|
import reportWebVitals from "./reportWebVitals";
|
||||||
|
import {DataContextProvider} from "./contexts/DataContext";
|
||||||
|
|
||||||
ReactDOM.render(
|
ReactDOM.render(
|
||||||
<React.StrictMode>
|
<React.StrictMode>
|
||||||
<App />
|
<DataContextProvider>
|
||||||
|
<App/>
|
||||||
|
</DataContextProvider>
|
||||||
</React.StrictMode>,
|
</React.StrictMode>,
|
||||||
document.getElementById('root')
|
document.getElementById('root')
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
import bridgeIcon from "@iconify-icons/mdi/bridge";
|
||||||
|
import hexagonIcon from "@iconify-icons/mdi/hexagon";
|
||||||
|
import lightBulbIcon from "@iconify-icons/mdi/lightbulb";
|
||||||
|
import lightBulbGroupIcon from "@iconify-icons/mdi/lightbulb-group";
|
||||||
|
import squareIcon from "@iconify-icons/mdi/square-rounded";
|
||||||
|
|
||||||
|
// To add a new icon, follow the instructions on this page:
|
||||||
|
// https://iconify.design/icon-sets/mdi/
|
||||||
|
|
||||||
|
export enum LuciferIcon {
|
||||||
|
Bridge = "Bridge",
|
||||||
|
Bulb = "Bulb",
|
||||||
|
BulbGroup = "BulbGroup",
|
||||||
|
Hexagon = "Hexagon",
|
||||||
|
Square = "Square",
|
||||||
|
}
|
||||||
|
|
||||||
|
const iconMap = {
|
||||||
|
[LuciferIcon.Bridge]: bridgeIcon,
|
||||||
|
[LuciferIcon.Bulb]: lightBulbIcon,
|
||||||
|
[LuciferIcon.BulbGroup]: lightBulbGroupIcon,
|
||||||
|
[LuciferIcon.Hexagon]: hexagonIcon,
|
||||||
|
[LuciferIcon.Square]: squareIcon,
|
||||||
|
}
|
||||||
|
|
||||||
|
export function toIconify(icon: LuciferIcon): object {
|
||||||
|
return iconMap[icon];
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
export enum DataTarget {
|
||||||
|
Default = "",
|
||||||
|
ColorPresets = "ColorPresets",
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
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);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Page title="Oppsett">
|
||||||
|
<h1>Definerte farger</h1>
|
||||||
|
{colorPresets.map(cp => (
|
||||||
|
<IconElement key={cp.id} {...cp} caption={cp.name}/>
|
||||||
|
))}
|
||||||
|
<button>Add</button>
|
||||||
|
</Page>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default SettingsPage;
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
import React, {CSSProperties, useMemo} from "react";
|
import React, {CSSProperties, useMemo} from "react";
|
||||||
import {ColorValue} from "../models/Colors";
|
import {ColorValue} from "../models/Colors";
|
||||||
// @ts-ignore
|
import {kelvinToRgbHex} from "../helpers/kelvin";
|
||||||
import kelvinToRgb from "kelvin-to-rgb";
|
import {Icon} from "@iconify/react";
|
||||||
|
|
||||||
import "./Elements.sass";
|
import "./Elements.sass";
|
||||||
import {kelvinToColorValue, kelvinToRgbHex} from "../helpers/kelvin";
|
import {LuciferIcon, toIconify} from "../models/Icons";
|
||||||
|
|
||||||
function Element({children}: React.PropsWithChildren<{}>) {
|
function Element({children}: React.PropsWithChildren<{}>) {
|
||||||
return (
|
return (
|
||||||
@@ -13,35 +13,41 @@ function Element({children}: React.PropsWithChildren<{}>) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface ColorElementProps {
|
interface ColorElementProps {
|
||||||
name: string
|
caption: string
|
||||||
value: ColorValue | "hs-gradient" | "k-gradient"
|
icon?: LuciferIcon
|
||||||
|
color?: ColorValue | "hs-gradient" | "k-gradient"
|
||||||
}
|
}
|
||||||
|
|
||||||
export function ColorElement({name, value}: ColorElementProps) {
|
export function IconElement({caption, color, icon}: ColorElementProps) {
|
||||||
const style: CSSProperties = useMemo(() => {
|
const style: CSSProperties = useMemo(() => {
|
||||||
const s: CSSProperties = {};
|
const s: CSSProperties = {
|
||||||
|
backgroundClip: "text",
|
||||||
|
// @ts-ignore
|
||||||
|
textFillColor: "transparent",
|
||||||
|
};
|
||||||
|
|
||||||
if (value === "hs-gradient") {
|
if (color !== undefined) {
|
||||||
s.backgroundImage = "linear-gradient(to bottom right, red, yellow, green, blue, violet)";
|
if (color === "hs-gradient") {
|
||||||
} else if (value === "k-gradient") {
|
s.backgroundImage = "linear-gradient(to bottom right, red, yellow, green, blue, violet)";
|
||||||
s.backgroundImage = "linear-gradient(to bottom right, #ff9329, #ffd6aa, #fffaf4, #fff, #c9e2ff, #40a3ff)";
|
} else if (color === "k-gradient") {
|
||||||
} else if (value.kelvin !== undefined && value.kelvin > 0) {
|
s.backgroundImage = "linear-gradient(to bottom right, #ff9329, #ffd6aa, #fffaf4, #fff, #c9e2ff, #40a3ff)";
|
||||||
s.backgroundColor = kelvinToRgbHex(value.kelvin);
|
} else if (color.kelvin !== undefined && color.kelvin > 0) {
|
||||||
} else {
|
s.color = kelvinToRgbHex(color.kelvin);
|
||||||
const hue = value.h || 0;
|
} else {
|
||||||
const sat = Math.floor((value.s || 0) * 100);
|
const hue = color.h || 0;
|
||||||
|
const sat = Math.floor((color.s || 0) * 100);
|
||||||
|
|
||||||
s.backgroundColor = `hsl(${hue}, ${sat}%, 50%)`;
|
s.color = `hsl(${hue}, ${sat}%, 50%)`;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return s;
|
return s;
|
||||||
}, [value]);
|
}, [color]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Element>
|
<Element>
|
||||||
<div className="ColorElement-block"
|
<Icon icon={toIconify(icon || LuciferIcon.Square)} className="ColorElement-block" style={style}/>
|
||||||
style={style}
|
{caption}
|
||||||
/>
|
|
||||||
{name}
|
|
||||||
</Element>
|
</Element>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,10 @@
|
|||||||
.Tabs-container
|
.Tabs-container
|
||||||
background-color: $color-foreground-dark
|
background-color: $color-foreground-dark
|
||||||
|
|
||||||
|
&.Tabs-large
|
||||||
|
text-align: center
|
||||||
|
font-size: 175%
|
||||||
|
|
||||||
.Tabs-element
|
.Tabs-element
|
||||||
display: inline-block
|
display: inline-block
|
||||||
padding: 0.25em 0.7ch
|
padding: 0.25em 0.7ch
|
||||||
@@ -14,4 +18,8 @@
|
|||||||
|
|
||||||
.Tabs-bold
|
.Tabs-bold
|
||||||
font-family: 'Bitstream Vera Serif', 'Lucida Fax', serif
|
font-family: 'Bitstream Vera Serif', 'Lucida Fax', serif
|
||||||
font-weight: 600
|
font-weight: 600
|
||||||
|
|
||||||
|
.Page
|
||||||
|
max-width: 800px
|
||||||
|
margin: 0.25em auto
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import React, {useCallback, useEffect} from "react";
|
import React, {PropsWithChildren, useCallback, useEffect} from "react";
|
||||||
import "./Layout.sass";
|
import "./Layout.sass";
|
||||||
|
|
||||||
interface TabsProps {
|
interface TabsProps {
|
||||||
@@ -6,9 +6,10 @@ interface TabsProps {
|
|||||||
index: number
|
index: number
|
||||||
onChange: (newIndex: number) => void
|
onChange: (newIndex: number) => void
|
||||||
boldIndex?: number
|
boldIndex?: number
|
||||||
|
large?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export function Tabs({tabNames, index, onChange, boldIndex}: TabsProps) {
|
export function Tabs({tabNames, index, onChange, large, boldIndex}: TabsProps) {
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (index < 0) {
|
if (index < 0) {
|
||||||
onChange(0);
|
onChange(0);
|
||||||
@@ -32,14 +33,47 @@ export function Tabs({tabNames, index, onChange, boldIndex}: TabsProps) {
|
|||||||
}, [index, boldIndex])
|
}, [index, boldIndex])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="Tabs-container">
|
<div className={`Tabs-container ${large ? "Tabs-large" : ""}`}>
|
||||||
{tabNames.map((name, i) => (
|
<div className="Tabs-inner">
|
||||||
<div className={tabClass(i)}
|
{tabNames.map((name, i) => (
|
||||||
onClick={() => onChange(i)}
|
<div className={tabClass(i)}
|
||||||
>
|
onClick={() => onChange(i)}
|
||||||
{name}
|
>
|
||||||
</div>
|
{name}
|
||||||
))}
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface PageProps {
|
||||||
|
title?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export function Page({title, children}: PropsWithChildren<PageProps>) {
|
||||||
|
useEffect(() => {
|
||||||
|
window.document.title = title ? `${title} - Lucifer` : "Lucifer";
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="Page">
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
interface DialogProps {
|
||||||
|
title: string
|
||||||
|
buttons?: {
|
||||||
|
text: string
|
||||||
|
onClick?: (() => void)
|
||||||
|
}[]
|
||||||
|
loading?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
export function Dialog({}: PropsWithChildren<DialogProps>) {
|
||||||
|
|
||||||
|
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|||||||
+51
-2
@@ -1209,6 +1209,16 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
"@hapi/hoek" "^8.3.0"
|
"@hapi/hoek" "^8.3.0"
|
||||||
|
|
||||||
|
"@iconify-icons/mdi@^1.1.15":
|
||||||
|
version "1.1.15"
|
||||||
|
resolved "https://registry.yarnpkg.com/@iconify-icons/mdi/-/mdi-1.1.15.tgz#eff8e16d5095828e60bacc476c9facc993e8c301"
|
||||||
|
integrity sha512-NozPlEOMiXwYheHet3u5ktu0o/GP+pCtta8kb2jGf0jSgOePirK73wj2u8QniZUot/xtT0KprvwXaBg/IFX2Og==
|
||||||
|
|
||||||
|
"@iconify/react@^1.1.4":
|
||||||
|
version "1.1.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/@iconify/react/-/react-1.1.4.tgz#c778eddbaf196e55705d0bedff00d039dc1de8d3"
|
||||||
|
integrity sha512-oxq8IMOq8q3nKGiDHbQPC8FcFuBsKve68JWBo140d5LRnj0bv5TB/FE/y01ZSvEZ7PlI2HIrnb2qivPN8kTDgw==
|
||||||
|
|
||||||
"@irojs/iro-core@^1.2.0":
|
"@irojs/iro-core@^1.2.0":
|
||||||
version "1.2.0"
|
version "1.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/@irojs/iro-core/-/iro-core-1.2.0.tgz#3587c2db7a6de09f76dbf75b94605ac251039ca8"
|
resolved "https://registry.yarnpkg.com/@irojs/iro-core/-/iro-core-1.2.0.tgz#3587c2db7a6de09f76dbf75b94605ac251039ca8"
|
||||||
@@ -2583,6 +2593,13 @@ axe-core@^4.0.2:
|
|||||||
resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.1.2.tgz#7cf783331320098bfbef620df3b3c770147bc224"
|
resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.1.2.tgz#7cf783331320098bfbef620df3b3c770147bc224"
|
||||||
integrity sha512-V+Nq70NxKhYt89ArVcaNL9FDryB3vQOd+BFXZIfO3RP6rwtj+2yqqqdHEkacutglPaZLkJeuXKCjCJDMGPtPqg==
|
integrity sha512-V+Nq70NxKhYt89ArVcaNL9FDryB3vQOd+BFXZIfO3RP6rwtj+2yqqqdHEkacutglPaZLkJeuXKCjCJDMGPtPqg==
|
||||||
|
|
||||||
|
axios@^0.21.1:
|
||||||
|
version "0.21.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/axios/-/axios-0.21.1.tgz#22563481962f4d6bde9a76d516ef0e5d3c09b2b8"
|
||||||
|
integrity sha512-dKQiRHxGD9PPRIUNIWvZhPTPpl1rf/OxTYKsqKUDjBwYylTvV7SjSHJb9ratfyzM6wCdLCOYLzs73qpg5c4iGA==
|
||||||
|
dependencies:
|
||||||
|
follow-redirects "^1.10.0"
|
||||||
|
|
||||||
axobject-query@^2.2.0:
|
axobject-query@^2.2.0:
|
||||||
version "2.2.0"
|
version "2.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/axobject-query/-/axobject-query-2.2.0.tgz#943d47e10c0b704aa42275e20edf3722648989be"
|
resolved "https://registry.yarnpkg.com/axobject-query/-/axobject-query-2.2.0.tgz#943d47e10c0b704aa42275e20edf3722648989be"
|
||||||
@@ -4858,6 +4875,11 @@ execa@^4.0.0:
|
|||||||
signal-exit "^3.0.2"
|
signal-exit "^3.0.2"
|
||||||
strip-final-newline "^2.0.0"
|
strip-final-newline "^2.0.0"
|
||||||
|
|
||||||
|
exenv@^1.2.0:
|
||||||
|
version "1.2.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/exenv/-/exenv-1.2.2.tgz#2ae78e85d9894158670b03d47bec1f03bd91bb9d"
|
||||||
|
integrity sha1-KueOhdmJQVhnCwPUe+wfA72Ru50=
|
||||||
|
|
||||||
exit@^0.1.2:
|
exit@^0.1.2:
|
||||||
version "0.1.2"
|
version "0.1.2"
|
||||||
resolved "https://registry.yarnpkg.com/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c"
|
resolved "https://registry.yarnpkg.com/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c"
|
||||||
@@ -5162,6 +5184,11 @@ follow-redirects@^1.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.13.2.tgz#dd73c8effc12728ba5cf4259d760ea5fb83e3147"
|
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.13.2.tgz#dd73c8effc12728ba5cf4259d760ea5fb83e3147"
|
||||||
integrity sha512-6mPTgLxYm3r6Bkkg0vNM0HTjfGrOEtsfbhagQvbxDEsEkpNhw582upBaoRZylzen6krEmxXJgt9Ju6HiI4O7BA==
|
integrity sha512-6mPTgLxYm3r6Bkkg0vNM0HTjfGrOEtsfbhagQvbxDEsEkpNhw582upBaoRZylzen6krEmxXJgt9Ju6HiI4O7BA==
|
||||||
|
|
||||||
|
follow-redirects@^1.10.0:
|
||||||
|
version "1.14.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.1.tgz#d9114ded0a1cfdd334e164e6662ad02bfd91ff43"
|
||||||
|
integrity sha512-HWqDgT7ZEkqRzBvc2s64vSZ/hfOceEol3ac/7tKwzuvEyWx3/4UegXh5oBOIotkGsObyk3xznnSRVADBgWSQVg==
|
||||||
|
|
||||||
for-in@^1.0.2:
|
for-in@^1.0.2:
|
||||||
version "1.0.2"
|
version "1.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80"
|
resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80"
|
||||||
@@ -7207,7 +7234,7 @@ loglevel@^1.6.8:
|
|||||||
resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.7.1.tgz#005fde2f5e6e47068f935ff28573e125ef72f197"
|
resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.7.1.tgz#005fde2f5e6e47068f935ff28573e125ef72f197"
|
||||||
integrity sha512-Hesni4s5UkWkwCGJMQGAh71PaLUmKFM60dHvq0zi/vDhhrzuk+4GgNbTXJ12YYQJn6ZKBDNIjYcuQGKudvqrIw==
|
integrity sha512-Hesni4s5UkWkwCGJMQGAh71PaLUmKFM60dHvq0zi/vDhhrzuk+4GgNbTXJ12YYQJn6ZKBDNIjYcuQGKudvqrIw==
|
||||||
|
|
||||||
loose-envify@^1.1.0, loose-envify@^1.4.0:
|
loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.4.0:
|
||||||
version "1.4.0"
|
version "1.4.0"
|
||||||
resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf"
|
resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf"
|
||||||
integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==
|
integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==
|
||||||
@@ -9166,7 +9193,7 @@ prompts@2.4.0, prompts@^2.0.1:
|
|||||||
kleur "^3.0.3"
|
kleur "^3.0.3"
|
||||||
sisteransi "^1.0.5"
|
sisteransi "^1.0.5"
|
||||||
|
|
||||||
prop-types@^15.7.2:
|
prop-types@^15.5.10, prop-types@^15.7.2:
|
||||||
version "15.7.2"
|
version "15.7.2"
|
||||||
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5"
|
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5"
|
||||||
integrity sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==
|
integrity sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==
|
||||||
@@ -9401,6 +9428,21 @@ react-is@^17.0.1:
|
|||||||
resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.1.tgz#5b3531bd76a645a4c9fb6e693ed36419e3301339"
|
resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.1.tgz#5b3531bd76a645a4c9fb6e693ed36419e3301339"
|
||||||
integrity sha512-NAnt2iGDXohE5LI7uBnLnqvLQMtzhkiAOLXTmv+qnF9Ky7xAPcX8Up/xWIhxvLVGJvuLiNc4xQLtuqDRzb4fSA==
|
integrity sha512-NAnt2iGDXohE5LI7uBnLnqvLQMtzhkiAOLXTmv+qnF9Ky7xAPcX8Up/xWIhxvLVGJvuLiNc4xQLtuqDRzb4fSA==
|
||||||
|
|
||||||
|
react-lifecycles-compat@^3.0.0:
|
||||||
|
version "3.0.4"
|
||||||
|
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==
|
||||||
|
dependencies:
|
||||||
|
exenv "^1.2.0"
|
||||||
|
prop-types "^15.5.10"
|
||||||
|
react-lifecycles-compat "^3.0.0"
|
||||||
|
warning "^4.0.3"
|
||||||
|
|
||||||
react-refresh@^0.8.3:
|
react-refresh@^0.8.3:
|
||||||
version "0.8.3"
|
version "0.8.3"
|
||||||
resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.8.3.tgz#721d4657672d400c5e3c75d063c4a85fb2d5d68f"
|
resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.8.3.tgz#721d4657672d400c5e3c75d063c4a85fb2d5d68f"
|
||||||
@@ -11470,6 +11512,13 @@ walker@^1.0.7, walker@~1.0.5:
|
|||||||
dependencies:
|
dependencies:
|
||||||
makeerror "1.0.x"
|
makeerror "1.0.x"
|
||||||
|
|
||||||
|
warning@^4.0.3:
|
||||||
|
version "4.0.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/warning/-/warning-4.0.3.tgz#16e9e077eb8a86d6af7d64aa1e05fd85b4678ca3"
|
||||||
|
integrity sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==
|
||||||
|
dependencies:
|
||||||
|
loose-envify "^1.0.0"
|
||||||
|
|
||||||
watchpack-chokidar2@^2.0.1:
|
watchpack-chokidar2@^2.0.1:
|
||||||
version "2.0.1"
|
version "2.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/watchpack-chokidar2/-/watchpack-chokidar2-2.0.1.tgz#38500072ee6ece66f3769936950ea1771be1c957"
|
resolved "https://registry.yarnpkg.com/watchpack-chokidar2/-/watchpack-chokidar2-2.0.1.tgz#38500072ee6ece66f3769936950ea1771be1c957"
|
||||||
|
|||||||
Reference in New Issue
Block a user