Start on a GM dashboard application #246

Merged
savanni merged 7 commits from gm-dash/init into main 2024-08-20 17:01:37 +00:00
12 changed files with 245 additions and 117 deletions
Showing only changes of commit 102c95e6c6 - Show all commits

View File

@ -15,8 +15,10 @@
"@types/node": "^16.18.105", "@types/node": "^16.18.105",
"@types/react": "^18.3.3", "@types/react": "^18.3.3",
"@types/react-dom": "^18.3.0", "@types/react-dom": "^18.3.0",
"classnames": "^2.5.1",
"react": "^18.3.1", "react": "^18.3.1",
"react-dom": "^18.3.1", "react-dom": "^18.3.1",
"react-router-dom": "^6.26.1",
"react-scripts": "5.0.1", "react-scripts": "5.0.1",
"typescript": "^4.9.5", "typescript": "^4.9.5",
"web-vitals": "^2.1.4" "web-vitals": "^2.1.4"
@ -3287,6 +3289,14 @@
} }
} }
}, },
"node_modules/@remix-run/router": {
"version": "1.19.1",
"resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.19.1.tgz",
"integrity": "sha512-S45oynt/WH19bHbIXjtli6QmwNYvaz+vtnubvNpNDvUOoA/OWh6j1OikIP3G+v5GHdxyC6EXoChG3HgYGEUfcg==",
"engines": {
"node": ">=14.0.0"
}
},
"node_modules/@rollup/plugin-babel": { "node_modules/@rollup/plugin-babel": {
"version": "5.3.1", "version": "5.3.1",
"resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz", "resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz",
@ -5816,6 +5826,11 @@
"resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.3.1.tgz", "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.3.1.tgz",
"integrity": "sha512-a3KdPAANPbNE4ZUv9h6LckSl9zLsYOP4MBmhIPkRaeyybt+r4UghLvq+xw/YwUcC1gqylCkL4rdVs3Lwupjm4Q==" "integrity": "sha512-a3KdPAANPbNE4ZUv9h6LckSl9zLsYOP4MBmhIPkRaeyybt+r4UghLvq+xw/YwUcC1gqylCkL4rdVs3Lwupjm4Q=="
}, },
"node_modules/classnames": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz",
"integrity": "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow=="
},
"node_modules/clean-css": { "node_modules/clean-css": {
"version": "5.3.3", "version": "5.3.3",
"resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.3.tgz", "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.3.tgz",
@ -14718,6 +14733,36 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/react-router": {
"version": "6.26.1",
"resolved": "https://registry.npmjs.org/react-router/-/react-router-6.26.1.tgz",
"integrity": "sha512-kIwJveZNwp7teQRI5QmwWo39A5bXRyqpH0COKKmPnyD2vBvDwgFXSqDUYtt1h+FEyfnE8eXr7oe0MxRzVwCcvQ==",
"dependencies": {
"@remix-run/router": "1.19.1"
},
"engines": {
"node": ">=14.0.0"
},
"peerDependencies": {
"react": ">=16.8"
}
},
"node_modules/react-router-dom": {
"version": "6.26.1",
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.26.1.tgz",
"integrity": "sha512-veut7m41S1fLql4pLhxeSW3jlqs+4MtjRLj0xvuCEXsxusJCbs6I8yn9BxzzDX2XDgafrccY6hwjmd/bL54tFw==",
"dependencies": {
"@remix-run/router": "1.19.1",
"react-router": "6.26.1"
},
"engines": {
"node": ">=14.0.0"
},
"peerDependencies": {
"react": ">=16.8",
"react-dom": ">=16.8"
}
},
"node_modules/react-scripts": { "node_modules/react-scripts": {
"version": "5.0.1", "version": "5.0.1",
"resolved": "https://registry.npmjs.org/react-scripts/-/react-scripts-5.0.1.tgz", "resolved": "https://registry.npmjs.org/react-scripts/-/react-scripts-5.0.1.tgz",

View File

@ -10,8 +10,10 @@
"@types/node": "^16.18.105", "@types/node": "^16.18.105",
"@types/react": "^18.3.3", "@types/react": "^18.3.3",
"@types/react-dom": "^18.3.0", "@types/react-dom": "^18.3.0",
"classnames": "^2.5.1",
"react": "^18.3.1", "react": "^18.3.1",
"react-dom": "^18.3.1", "react-dom": "^18.3.1",
"react-router-dom": "^6.26.1",
"react-scripts": "5.0.1", "react-scripts": "5.0.1",
"typescript": "^4.9.5", "typescript": "^4.9.5",
"web-vitals": "^2.1.4" "web-vitals": "^2.1.4"

View File

@ -3,78 +3,3 @@
justify-content: space-between; justify-content: space-between;
width: 100%; width: 100%;
} }
.palette {
display: flex;
}
.palette div {
width: 50px;
height: 50px;
border: 1px solid black;
border-radius: 5px;
margin: 1em;
padding: 0;
}
.item-1 {
background-color: var(--purple-1);
}
.item-2 {
background-color: var(--purple-2);
}
.item-3 {
background-color: var(--purple-3);
}
.item-4 {
background-color: var(--purple-4);
}
.item-5 {
background-color: var(--purple-5);
}
.item-6 {
background-color: var(--blue-1);
}
.item-7 {
background-color: var(--blue-2);
}
.item-8 {
background-color: var(--blue-3);
}
.item-9 {
background-color: var(--blue-4);
}
.item-10 {
background-color: var(--blue-5);
}
.item-11 {
background-color: var(--grey-1);
}
.item-12 {
background-color: var(--grey-2);
}
.item-13 {
background-color: var(--grey-3);
}
.item-14 {
background-color: var(--grey-4);
}
.item-15 {
background-color: var(--grey-5);
}

View File

@ -1,10 +1,10 @@
import './Dashboard.css'; import './Dashboard.css';
import Card from './components/Card/Card'; import Card from './components/Card/Card';
import Launcher from './components/Launcher/Launcher'; import Launcher from './components/Launcher/Launcher';
import LauncherGroup from './components/LauncherGroup/LauncherGroup'; import Launchpad from './components/Launchpad/Launchpad';
const LightThemes = () => <Card name="Light Themes"> const LightThemes = () => <Card name="Light Themes">
<LauncherGroup <Launchpad
exclusive={true} exclusive={true}
options={[ options={[
{ title: "Dark reds" }, { title: "Dark reds" },
@ -24,7 +24,7 @@ interface LightProps {
const Light = ({ name }: LightProps) => <div> <p> {name} </p> </div> const Light = ({ name }: LightProps) => <div> <p> {name} </p> </div>
const Tracks = () => <Card name="Tracks"> const Tracks = () => <Card name="Tracks">
<LauncherGroup <Launchpad
exclusive={false} exclusive={false}
options={[ options={[
{ title: "City BGM" }, { title: "City BGM" },
@ -41,7 +41,7 @@ interface TrackProps {
const Track = ({ name }: TrackProps) => <Launcher title={name} /> const Track = ({ name }: TrackProps) => <Launcher title={name} />
const Presets = () => <Card name="Presets"> const Presets = () => <Card name="Presets">
<LauncherGroup <Launchpad
exclusive={true} exclusive={true}
options={[ options={[
{ title: "Gilcrest Falls day" }, { title: "Gilcrest Falls day" },
@ -60,36 +60,9 @@ interface PresetProps {
const SceneEditor = () => <div> </div> const SceneEditor = () => <div> </div>
const Palette = () => <div className="palette">
<div className="item-1" />
<div className="item-2" />
<div className="item-3" />
<div className="item-4" />
<div className="item-5" />
</div>
const Palette2 = () => <div className="palette">
<div className="item-6" />
<div className="item-7" />
<div className="item-8" />
<div className="item-9" />
<div className="item-10" />
</div>
const Palette3 = () => <div className="palette">
<div className="item-11" />
<div className="item-12" />
<div className="item-13" />
<div className="item-14" />
<div className="item-15" />
</div>
const Dashboard = () => { const Dashboard = () => {
return ( return (
<div className="app"> <div className="app">
<Palette />
<Palette2 />
<Palette3 />
<div className="layout"> <div className="layout">
<Presets /> <Presets />
<div> <div>

78
gm-dash/ui/src/Design.css Normal file
View File

@ -0,0 +1,78 @@
.palette {
display: flex;
}
.palette div {
width: 50px;
height: 50px;
border: 1px solid black;
border-radius: 5px;
margin: 1em;
padding: 0;
}
.palette-purple div.item-1 {
background-color: var(--purple-1);
}
.palette-purple div.item-2 {
background-color: var(--purple-2);
}
.palette-purple div.item-3 {
background-color: var(--purple-3);
}
.palette-purple div.item-4 {
background-color: var(--purple-4);
}
.palette-purple div.item-5 {
background-color: var(--purple-5);
}
.palette-blue div.item-1 {
background-color: var(--blue-1);
}
.palette-blue div.item-2 {
background-color: var(--blue-2);
}
.palette-blue div.item-3 {
background-color: var(--blue-3);
}
.palette-blue div.item-4 {
background-color: var(--blue-4);
}
.palette-blue div.item-5 {
background-color: var(--blue-5);
}
.palette-grey div.item-1 {
background-color: var(--grey-1);
}
.palette-grey div.item-2 {
background-color: var(--grey-2);
}
.palette-grey div.item-3 {
background-color: var(--grey-3);
}
.palette-grey div.item-4 {
background-color: var(--grey-4);
}
.palette-grey div.item-5 {
background-color: var(--grey-5);
}
.design_horizontal {
display: flex;
flex-flow: row;
}

57
gm-dash/ui/src/Design.tsx Normal file
View File

@ -0,0 +1,57 @@
import './Design.css'
import Launchpad from './components/Launchpad/Launchpad'
const PaletteGrey = () => <div className="palette palette-grey">
<div className="item-1" />
<div className="item-2" />
<div className="item-3" />
<div className="item-4" />
<div className="item-5" />
</div>
const PalettePurple = () => <div className="palette palette-purple">
<div className="item-1" />
<div className="item-2" />
<div className="item-3" />
<div className="item-4" />
<div className="item-5" />
</div>
const PaletteBlue = () => <div className="palette palette-blue">
<div className="item-1" />
<div className="item-2" />
<div className="item-3" />
<div className="item-4" />
<div className="item-5" />
</div>
const Launchpads = () => <div className="design_horizontal">
<Launchpad
exclusive={true}
options={[
{ title: "Grey" },
{ title: "Purple" },
{ title: "Blue" },
]}
/>
<Launchpad
exclusive={false}
flow={"vertical"}
options={[
{ title: "Grey" },
{ title: "Purple" },
{ title: "Blue" },
]}
/>
</div>
const Design = () => <div>
<PaletteGrey />
<PalettePurple />
<PaletteBlue />
<Launchpads />
</div>
export default Design;

View File

@ -3,9 +3,9 @@
border-radius: 5px; border-radius: 5px;
margin: var(--spacer-m); margin: var(--spacer-m);
padding: var(--spacer-s); padding: var(--spacer-s);
box-shadow: 2px 2px 8px 0px var(--shadow-1); box-shadow: var(--shadow-deep);
} }
.activator_enabled { .activator_enabled {
box-shadow: 0px 0px 8px 4px var(--activator-ring); box-shadow: var(--activator-ring);
} }

View File

@ -0,0 +1,18 @@
.launchpad {
display: flex;
border: var(--border);
border-radius: var(--border-radius);
box-shadow: var(--shadow-depression);
margin: var(--spacer-l);
}
.launchpad_horizontal-flow {
display: flex;
flex-direction: row;
}
.launchpad_vertical-flow {
display: flex;
flex-direction: column;
}

View File

@ -1,13 +1,18 @@
import React from 'react'; import React from 'react';
import './Launchpad.css';
import Launcher, { LauncherProps } from '../Launcher/Launcher'; import Launcher, { LauncherProps } from '../Launcher/Launcher';
import classnames from 'classnames';
export interface Selectable { export interface Selectable {
onSelected?: (key: string) => void; onSelected?: (key: string) => void;
} }
interface LauncherGroupProps { export type Flow = "horizontal" | "vertical";
options: Array<LauncherProps>;
interface LaunchpadProps {
exclusive: boolean; exclusive: boolean;
flow?: Flow;
options: Array<LauncherProps>;
} }
const exclusiveSelect = (state: { [key: string]: boolean }, targetId: string) => { const exclusiveSelect = (state: { [key: string]: boolean }, targetId: string) => {
@ -23,14 +28,22 @@ const multiSelect = (state: { [key: string]: boolean }, targetId: string) => {
} }
} }
const LauncherGroup = ({ options, exclusive }: LauncherGroupProps) => { const Launchpad = ({ flow = "horizontal", options, exclusive }: LaunchpadProps) => {
const [selected, dispatch] = React.useReducer(exclusive ? exclusiveSelect : multiSelect, {}); const [selected, dispatch] = React.useReducer(exclusive ? exclusiveSelect : multiSelect, {});
let classOptions = [ "launchpad" ];
if (flow === "horizontal") {
classOptions.push("launchpad_horizontal-flow");
} else {
classOptions.push("launchpad_vertical-flow");
}
let tiedOptions = options.map(option => let tiedOptions = options.map(option =>
<Launcher key={option.title} title={option.title} onSelected={(key: string) => dispatch(key)} activated={selected[option.title]} /> <Launcher key={option.title} title={option.title} onSelected={(key: string) => dispatch(key)} activated={selected[option.title]} />
); );
return (<div> {tiedOptions} </div>) return (<div className={classnames(classOptions)}> {tiedOptions} </div>)
} }
export default LauncherGroup; export default Launchpad;

View File

@ -18,10 +18,15 @@
--grey-5: hsl(210, 0%, 85%); --grey-5: hsl(210, 0%, 85%);
--title-color: var(--grey-1); --title-color: var(--grey-1);
--shadow-1: var(--purple-1);
--shadow-2: var(--purple-2);
--activator-ring: var(--blue-4); --border: 1px solid var(--purple-1);
--activator-ring: 0px 0px 8px 4px var(--blue-4);
--shadow-depression: inset 1px 1px 2px 0px var(--purple-1);
--shadow-shallow: 1px 1px 2px 0px var(--purple-1);
--shadow-deep: 2px 2px 4px 0px var(--purple-1),
4px 4px 8px 0px var(--purple-2);
--border-radius: 8px;
--spacer-xs: 2px; --spacer-xs: 2px;
--spacer-s: 4px; --spacer-s: 4px;

View File

@ -2,14 +2,26 @@ import React from 'react';
import ReactDOM from 'react-dom/client'; import ReactDOM from 'react-dom/client';
import './index.css'; import './index.css';
import Dashboard from './Dashboard'; import Dashboard from './Dashboard';
import Design from './Design';
import reportWebVitals from './reportWebVitals'; import reportWebVitals from './reportWebVitals';
import { createBrowserRouter, RouterProvider } from 'react-router-dom';
const root = ReactDOM.createRoot( const root = ReactDOM.createRoot(
document.getElementById('root') as HTMLElement document.getElementById('root') as HTMLElement
); );
const router = createBrowserRouter([
{
path: "/",
element: <Dashboard />,
},
{
path: "/design",
element: <Design />,
},
]);
root.render( root.render(
<React.StrictMode> <React.StrictMode>
<Dashboard /> <RouterProvider router={router} />
</React.StrictMode> </React.StrictMode>
); );