forked from clan/clan-core
Webview: bootstrap layout
This commit is contained in:
parent
d0d973b797
commit
9bf76037aa
@ -19,6 +19,7 @@ export default tseslint.config(
|
||||
"error",
|
||||
{
|
||||
callees: ["cx"],
|
||||
whitelist: ["material-icons"],
|
||||
},
|
||||
],
|
||||
},
|
||||
|
@ -25,9 +25,12 @@ fs.readFile(manifestPath, { encoding: "utf8" }, (err, data) => {
|
||||
|
||||
// Add linked stylesheets
|
||||
assets.forEach((asset) => {
|
||||
console.log(asset);
|
||||
if (asset.src === "index.html") {
|
||||
asset.css.forEach((cssEntry) => {
|
||||
htmlContent += `\n <link rel="stylesheet" href="${cssEntry}">`;
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
htmlContent += `
|
||||
|
16
pkgs/webview-ui/app/mock.ts
Normal file
16
pkgs/webview-ui/app/mock.ts
Normal file
@ -0,0 +1,16 @@
|
||||
import { JSONSchemaFaker } from "json-schema-faker";
|
||||
import { schema } from "./api/index";
|
||||
import { OperationNames } from "./src/message";
|
||||
|
||||
const faker = JSONSchemaFaker;
|
||||
|
||||
faker.option({
|
||||
alwaysFakeOptionals: true,
|
||||
});
|
||||
|
||||
const getFakeResponse = (method: OperationNames, data: any) => {
|
||||
const fakeData = faker.generate(schema.properties[method].properties.return);
|
||||
return fakeData;
|
||||
};
|
||||
|
||||
export { getFakeResponse };
|
144
pkgs/webview-ui/app/package-lock.json
generated
144
pkgs/webview-ui/app/package-lock.json
generated
@ -9,10 +9,12 @@
|
||||
"version": "0.0.1",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"material-icons": "^1.13.12",
|
||||
"solid-js": "^1.8.11"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9.3.0",
|
||||
"@tailwindcss/typography": "^0.5.13",
|
||||
"@types/node": "^20.12.12",
|
||||
"@typescript-eslint/parser": "^7.10.0",
|
||||
"autoprefixer": "^10.4.19",
|
||||
@ -20,6 +22,7 @@
|
||||
"daisyui": "^4.11.1",
|
||||
"eslint": "^8.57.0",
|
||||
"eslint-plugin-tailwindcss": "^3.17.0",
|
||||
"json-schema-faker": "^0.5.6",
|
||||
"json-schema-to-ts": "^3.1.0",
|
||||
"postcss": "^8.4.38",
|
||||
"prettier": "^3.2.5",
|
||||
@ -1482,6 +1485,34 @@
|
||||
"solid-js": "^1.6.12"
|
||||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/typography": {
|
||||
"version": "0.5.13",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/typography/-/typography-0.5.13.tgz",
|
||||
"integrity": "sha512-ADGcJ8dX21dVVHIwTRgzrcunY6YY9uSlAHHGVKvkA+vLc5qLwEszvKts40lx7z0qc4clpjclwLeK5rVCV2P/uw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"lodash.castarray": "^4.4.0",
|
||||
"lodash.isplainobject": "^4.0.6",
|
||||
"lodash.merge": "^4.6.2",
|
||||
"postcss-selector-parser": "6.0.10"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"tailwindcss": ">=3.0.0 || insiders"
|
||||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/typography/node_modules/postcss-selector-parser": {
|
||||
"version": "6.0.10",
|
||||
"resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz",
|
||||
"integrity": "sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"cssesc": "^3.0.0",
|
||||
"util-deprecate": "^1.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/babel__core": {
|
||||
"version": "7.20.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz",
|
||||
@ -1990,6 +2021,12 @@
|
||||
"node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
|
||||
}
|
||||
},
|
||||
"node_modules/call-me-maybe": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/call-me-maybe/-/call-me-maybe-1.0.2.tgz",
|
||||
"integrity": "sha512-HpX65o1Hnr9HH25ojC1YGs7HCQLq0GCOibSaWER0eNpgJ/Z1MZv2mTc7+xh6WOPxbRVcmgbv4hGU+uSQ/2xFZQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/callsites": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
|
||||
@ -2623,6 +2660,19 @@
|
||||
"url": "https://opencollective.com/eslint"
|
||||
}
|
||||
},
|
||||
"node_modules/esprima": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
|
||||
"integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"esparse": "bin/esparse.js",
|
||||
"esvalidate": "bin/esvalidate.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/esquery": {
|
||||
"version": "1.5.0",
|
||||
"resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz",
|
||||
@ -2802,6 +2852,12 @@
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/format-util": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/format-util/-/format-util-1.0.5.tgz",
|
||||
"integrity": "sha512-varLbTj0e0yVyRpqQhuWV+8hlePAgaoFRhNFj50BNjEIrw1/DphHSObtqwskVCPWNgzwPoQrZAbfa/SBiicNeg==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/fraction.js": {
|
||||
"version": "4.3.7",
|
||||
"resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz",
|
||||
@ -3197,6 +3253,53 @@
|
||||
"integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/json-schema-faker": {
|
||||
"version": "0.5.6",
|
||||
"resolved": "https://registry.npmjs.org/json-schema-faker/-/json-schema-faker-0.5.6.tgz",
|
||||
"integrity": "sha512-u/cFC26/GDxh2vPiAC8B8xVvpXAW+QYtG2mijEbKrimCk8IHtiwQBjCE8TwvowdhALWq9IcdIWZ+/8ocXvdL3Q==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"json-schema-ref-parser": "^6.1.0",
|
||||
"jsonpath-plus": "^7.2.0"
|
||||
},
|
||||
"bin": {
|
||||
"jsf": "bin/gen.cjs"
|
||||
}
|
||||
},
|
||||
"node_modules/json-schema-ref-parser": {
|
||||
"version": "6.1.0",
|
||||
"resolved": "https://registry.npmjs.org/json-schema-ref-parser/-/json-schema-ref-parser-6.1.0.tgz",
|
||||
"integrity": "sha512-pXe9H1m6IgIpXmE5JSb8epilNTGsmTb2iPohAXpOdhqGFbQjNeHHsZxU+C8w6T81GZxSPFLeUoqDJmzxx5IGuw==",
|
||||
"deprecated": "Please switch to @apidevtools/json-schema-ref-parser",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"call-me-maybe": "^1.0.1",
|
||||
"js-yaml": "^3.12.1",
|
||||
"ono": "^4.0.11"
|
||||
}
|
||||
},
|
||||
"node_modules/json-schema-ref-parser/node_modules/argparse": {
|
||||
"version": "1.0.10",
|
||||
"resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
|
||||
"integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"sprintf-js": "~1.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/json-schema-ref-parser/node_modules/js-yaml": {
|
||||
"version": "3.14.1",
|
||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz",
|
||||
"integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"argparse": "^1.0.7",
|
||||
"esprima": "^4.0.0"
|
||||
},
|
||||
"bin": {
|
||||
"js-yaml": "bin/js-yaml.js"
|
||||
}
|
||||
},
|
||||
"node_modules/json-schema-to-ts": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/json-schema-to-ts/-/json-schema-to-ts-3.1.0.tgz",
|
||||
@ -3234,6 +3337,15 @@
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/jsonpath-plus": {
|
||||
"version": "7.2.0",
|
||||
"resolved": "https://registry.npmjs.org/jsonpath-plus/-/jsonpath-plus-7.2.0.tgz",
|
||||
"integrity": "sha512-zBfiUPM5nD0YZSBT/o/fbCUlCcepMIdP0CJZxM1+KgA4f2T206f6VAg9e7mX35+KlMaIc5qXW34f3BnwJ3w+RA==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=12.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/keyv": {
|
||||
"version": "4.5.4",
|
||||
"resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
|
||||
@ -3314,6 +3426,18 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/lodash.castarray": {
|
||||
"version": "4.4.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.castarray/-/lodash.castarray-4.4.0.tgz",
|
||||
"integrity": "sha512-aVx8ztPv7/2ULbArGJ2Y42bG1mEQ5mGjpdvrbJcJFU3TbYybe+QlLS4pst9zV52ymy2in1KpFPiZnAOATxD4+Q==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/lodash.isplainobject": {
|
||||
"version": "4.0.6",
|
||||
"resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
|
||||
"integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/lodash.merge": {
|
||||
"version": "4.6.2",
|
||||
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
|
||||
@ -3366,6 +3490,11 @@
|
||||
"semver": "bin/semver"
|
||||
}
|
||||
},
|
||||
"node_modules/material-icons": {
|
||||
"version": "1.13.12",
|
||||
"resolved": "https://registry.npmjs.org/material-icons/-/material-icons-1.13.12.tgz",
|
||||
"integrity": "sha512-/2YoaB79IjUK2B2JB+vIXXYGtBfHb/XG66LvoKVM5ykHW7yfrV5SP6d7KLX6iijY6/G9GqwgtPQ/sbhFnOURVA=="
|
||||
},
|
||||
"node_modules/merge-anything": {
|
||||
"version": "5.1.7",
|
||||
"resolved": "https://registry.npmjs.org/merge-anything/-/merge-anything-5.1.7.tgz",
|
||||
@ -3551,6 +3680,15 @@
|
||||
"wrappy": "1"
|
||||
}
|
||||
},
|
||||
"node_modules/ono": {
|
||||
"version": "4.0.11",
|
||||
"resolved": "https://registry.npmjs.org/ono/-/ono-4.0.11.tgz",
|
||||
"integrity": "sha512-jQ31cORBFE6td25deYeD80wxKBMj+zBmHTrVxnc6CKhx8gho6ipmWM5zj/oeoqioZ99yqBls9Z/9Nss7J26G2g==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"format-util": "^1.0.3"
|
||||
}
|
||||
},
|
||||
"node_modules/optionator": {
|
||||
"version": "0.9.4",
|
||||
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
|
||||
@ -4289,6 +4427,12 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/sprintf-js": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
|
||||
"integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/string-width": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz",
|
||||
|
@ -13,12 +13,15 @@
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9.3.0",
|
||||
"@tailwindcss/typography": "^0.5.13",
|
||||
"@types/node": "^20.12.12",
|
||||
"@typescript-eslint/parser": "^7.10.0",
|
||||
"autoprefixer": "^10.4.19",
|
||||
"classnames": "^2.5.1",
|
||||
"daisyui": "^4.11.1",
|
||||
"eslint": "^8.57.0",
|
||||
"eslint-plugin-tailwindcss": "^3.17.0",
|
||||
"json-schema-faker": "^0.5.6",
|
||||
"json-schema-to-ts": "^3.1.0",
|
||||
"postcss": "^8.4.38",
|
||||
"prettier": "^3.2.5",
|
||||
@ -27,10 +30,10 @@
|
||||
"typescript": "^5.4.5",
|
||||
"typescript-eslint": "^7.10.0",
|
||||
"vite": "^5.0.11",
|
||||
"vite-plugin-solid": "^2.8.2",
|
||||
"eslint-plugin-tailwindcss": "^3.17.0"
|
||||
"vite-plugin-solid": "^2.8.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"material-icons": "^1.13.12",
|
||||
"solid-js": "^1.8.11"
|
||||
}
|
||||
}
|
||||
|
@ -1,59 +1,18 @@
|
||||
import { Match, Switch, createSignal, type Component } from "solid-js";
|
||||
import { createSignal, type Component } from "solid-js";
|
||||
import { CountProvider } from "./Config";
|
||||
// import { Nested } from "./nested";
|
||||
import { Layout } from "./layout/layout";
|
||||
import cx from "classnames";
|
||||
import { Nested } from "./nested";
|
||||
import { Route, Router } from "./Routes";
|
||||
|
||||
type Route = "home" | "machines";
|
||||
// Global state
|
||||
const [route, setRoute] = createSignal<Route>("machines");
|
||||
|
||||
export { route, setRoute };
|
||||
|
||||
const App: Component = () => {
|
||||
const [route, setRoute] = createSignal<Route>("home");
|
||||
return (
|
||||
<CountProvider>
|
||||
<Layout>
|
||||
<div class="col-span-1">
|
||||
<div class={cx("text-zinc-500")}>Navigation</div>
|
||||
<ul>
|
||||
<li>
|
||||
<button
|
||||
onClick={() => setRoute("home")}
|
||||
classList={{ "bg-blue-500": route() === "home" }}
|
||||
>
|
||||
Home
|
||||
</button>
|
||||
</li>
|
||||
<li>
|
||||
{" "}
|
||||
<button
|
||||
onClick={() => setRoute("machines")}
|
||||
classList={{ "bg-blue-500": route() === "machines" }}
|
||||
>
|
||||
Machines
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="col-span-7">
|
||||
<div>{route()}</div>
|
||||
<Switch fallback={<p>{route()} not found</p>}>
|
||||
<Match when={route() == "home"}>
|
||||
<Nested />
|
||||
</Match>
|
||||
<Match when={route() == "machines"}>
|
||||
<div class="grid grid-cols-3 gap-2">
|
||||
<div class="h-10 w-20 bg-red-500">red</div>
|
||||
<div class="h-10 w-20 bg-green-500">green</div>
|
||||
<div class="h-10 w-20 bg-blue-500">blue</div>
|
||||
<div class="h-10 w-20 bg-yellow-500">yellow</div>
|
||||
<div class="h-10 w-20 bg-purple-500">purple</div>
|
||||
<div class="h-10 w-20 bg-cyan-500">cyan</div>
|
||||
<div class="h-10 w-20 bg-pink-500">pink</div>
|
||||
</div>
|
||||
</Match>
|
||||
</Switch>
|
||||
</div>
|
||||
<Router route={route} />
|
||||
</Layout>
|
||||
</CountProvider>
|
||||
);
|
||||
|
32
pkgs/webview-ui/app/src/Routes.tsx
Normal file
32
pkgs/webview-ui/app/src/Routes.tsx
Normal file
@ -0,0 +1,32 @@
|
||||
import { Accessor, For, Match, Switch } from "solid-js";
|
||||
import { MachineListView } from "./routes/machines/view";
|
||||
import { colors } from "./routes/colors/view";
|
||||
|
||||
export type Route = keyof typeof routes;
|
||||
|
||||
export const routes = {
|
||||
machines: {
|
||||
child: MachineListView,
|
||||
label: "Machines",
|
||||
icon: "devices_other",
|
||||
},
|
||||
colors: {
|
||||
child: colors,
|
||||
label: "Colors",
|
||||
icon: "color_lens",
|
||||
},
|
||||
};
|
||||
|
||||
interface RouterProps {
|
||||
route: Accessor<Route>;
|
||||
}
|
||||
export const Router = (props: RouterProps) => {
|
||||
const { route } = props;
|
||||
return (
|
||||
<Switch fallback={<p>route {route()} not found</p>}>
|
||||
<For each={Object.entries(routes)}>
|
||||
{([key, { child }]) => <Match when={route() === key}>{child}</Match>}
|
||||
</For>
|
||||
</Switch>
|
||||
);
|
||||
};
|
33
pkgs/webview-ui/app/src/Sidebar.tsx
Normal file
33
pkgs/webview-ui/app/src/Sidebar.tsx
Normal file
@ -0,0 +1,33 @@
|
||||
import { Accessor, For, Setter } from "solid-js";
|
||||
import { Route, routes } from "./Routes";
|
||||
|
||||
interface SidebarProps {
|
||||
route: Accessor<Route>;
|
||||
setRoute: Setter<Route>;
|
||||
}
|
||||
export const Sidebar = (props: SidebarProps) => {
|
||||
const { route, setRoute } = props;
|
||||
return (
|
||||
<aside class="min-h-screen w-80 bg-base-100">
|
||||
<div class="sticky top-0 z-20 hidden items-center gap-2 bg-base-100/90 px-4 py-2 shadow-sm backdrop-blur lg:flex">
|
||||
Icon
|
||||
</div>
|
||||
<ul class="menu px-4 py-0">
|
||||
<For each={Object.entries(routes)}>
|
||||
{([key, { label, icon }]) => (
|
||||
<li>
|
||||
<button
|
||||
onClick={() => setRoute(key as Route)}
|
||||
class="group"
|
||||
classList={{ "bg-blue-500": route() === key }}
|
||||
>
|
||||
<span class="material-icons">{icon}</span>
|
||||
{label}
|
||||
</button>
|
||||
</li>
|
||||
)}
|
||||
</For>
|
||||
</ul>
|
||||
</aside>
|
||||
);
|
||||
};
|
@ -1,3 +1,5 @@
|
||||
@import 'material-icons/iconfont/filled.css';
|
||||
/* List of icons: https://marella.me/material-icons/demo/ */
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
@ -3,7 +3,7 @@ import { render } from "solid-js/web";
|
||||
|
||||
import "./index.css";
|
||||
import App from "./App";
|
||||
|
||||
import { getFakeResponse } from "../mock";
|
||||
const root = document.getElementById("app");
|
||||
|
||||
window.clan = window.clan || {};
|
||||
@ -14,5 +14,26 @@ if (import.meta.env.DEV && !(root instanceof HTMLElement)) {
|
||||
);
|
||||
}
|
||||
|
||||
console.log(import.meta.env);
|
||||
if (import.meta.env.DEV) {
|
||||
console.log("Development mode");
|
||||
window.webkit = window.webkit || {
|
||||
messageHandlers: {
|
||||
gtk: {
|
||||
postMessage: (postMessage) => {
|
||||
const { method, data } = postMessage;
|
||||
console.debug("Python API call", { method, data });
|
||||
setTimeout(() => {
|
||||
const mock = getFakeResponse(method, data);
|
||||
console.log("mock", { mock });
|
||||
|
||||
window.clan[method](JSON.stringify(mock));
|
||||
}, 1000);
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
render(() => <App />, root!);
|
||||
|
19
pkgs/webview-ui/app/src/layout/header.tsx
Normal file
19
pkgs/webview-ui/app/src/layout/header.tsx
Normal file
@ -0,0 +1,19 @@
|
||||
export const Header = () => {
|
||||
return (
|
||||
<div class="navbar bg-base-100">
|
||||
<div class="flex-none">
|
||||
<button class="btn btn-square btn-ghost">
|
||||
<span class="material-icons">home</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="flex-1">
|
||||
<a class="btn btn-ghost text-xl">Clan</a>
|
||||
</div>
|
||||
<div class="flex-none">
|
||||
<button class="btn btn-square btn-ghost">
|
||||
<span class="material-icons">menu</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
@ -1,9 +1,35 @@
|
||||
import { Component, JSXElement } from "solid-js";
|
||||
import { Header } from "./header";
|
||||
import { Sidebar } from "../Sidebar";
|
||||
import { route, setRoute } from "../App";
|
||||
|
||||
interface LayoutProps {
|
||||
children: JSXElement;
|
||||
}
|
||||
|
||||
export const Layout: Component<LayoutProps> = (props) => {
|
||||
return <div class="grid grid-cols-8">{props.children}</div>;
|
||||
return (
|
||||
<>
|
||||
<div class="drawer bg-base-100 lg:drawer-open">
|
||||
<input
|
||||
id="toplevel-drawer"
|
||||
type="checkbox"
|
||||
class="drawer-toggle hidden"
|
||||
/>
|
||||
<div class="drawer-content">
|
||||
<Header />
|
||||
|
||||
{props.children}
|
||||
</div>
|
||||
<div class="drawer-side z-40">
|
||||
<label
|
||||
for="toplevel-drawer"
|
||||
aria-label="close sidebar"
|
||||
class="drawer-overlay"
|
||||
></label>
|
||||
<Sidebar route={route} setRoute={setRoute} />
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
@ -1,39 +0,0 @@
|
||||
import { For, Match, Switch, createEffect, type Component } from "solid-js";
|
||||
import { useCountContext } from "./Config";
|
||||
|
||||
export const Nested: Component = () => {
|
||||
const [{ machines, loading }, { getMachines }] = useCountContext();
|
||||
|
||||
const list = () => Object.values(machines());
|
||||
|
||||
createEffect(() => {
|
||||
console.log("1", list());
|
||||
});
|
||||
createEffect(() => {
|
||||
console.log("2", machines());
|
||||
});
|
||||
return (
|
||||
<div>
|
||||
<button onClick={() => getMachines()} class="btn btn-primary">
|
||||
Get machines
|
||||
</button>
|
||||
<div></div>
|
||||
<Switch>
|
||||
<Match when={loading()}>Loading...</Match>
|
||||
<Match when={!loading() && Object.entries(machines()).length === 0}>
|
||||
No machines found
|
||||
</Match>
|
||||
<Match when={!loading()}>
|
||||
<For each={list()}>
|
||||
{(entry, i) => (
|
||||
<li>
|
||||
{i() + 1}: {entry.machine_name}{" "}
|
||||
{entry.machine_description || "No description"}
|
||||
</li>
|
||||
)}
|
||||
</For>
|
||||
</Match>
|
||||
</Switch>
|
||||
</div>
|
||||
);
|
||||
};
|
13
pkgs/webview-ui/app/src/routes/colors/view.tsx
Normal file
13
pkgs/webview-ui/app/src/routes/colors/view.tsx
Normal file
@ -0,0 +1,13 @@
|
||||
export const colors = () => {
|
||||
return (
|
||||
<div class="grid grid-cols-3 gap-2">
|
||||
<div class="h-10 w-20 bg-red-500">red</div>
|
||||
<div class="h-10 w-20 bg-green-500">green</div>
|
||||
<div class="h-10 w-20 bg-blue-500">blue</div>
|
||||
<div class="h-10 w-20 bg-yellow-500">yellow</div>
|
||||
<div class="h-10 w-20 bg-purple-500">purple</div>
|
||||
<div class="h-10 w-20 bg-cyan-500">cyan</div>
|
||||
<div class="h-10 w-20 bg-pink-500">pink</div>
|
||||
</div>
|
||||
);
|
||||
};
|
76
pkgs/webview-ui/app/src/routes/machines/view.tsx
Normal file
76
pkgs/webview-ui/app/src/routes/machines/view.tsx
Normal file
@ -0,0 +1,76 @@
|
||||
import { For, Match, Switch, createEffect, type Component } from "solid-js";
|
||||
import { useCountContext } from "../../Config";
|
||||
import { route } from "@/src/App";
|
||||
|
||||
export const MachineListView: Component = () => {
|
||||
const [{ machines, loading }, { getMachines }] = useCountContext();
|
||||
|
||||
const list = () => Object.values(machines());
|
||||
|
||||
createEffect(() => {
|
||||
if (route() === "machines") getMachines();
|
||||
});
|
||||
return (
|
||||
<div class="max-w-screen-lg">
|
||||
<div class="tooltip" data-tip="Refresh ">
|
||||
<button class="btn btn-ghost" onClick={() => getMachines()}>
|
||||
<span class="material-icons ">refresh</span>
|
||||
</button>
|
||||
</div>
|
||||
<Switch>
|
||||
<Match when={loading()}>
|
||||
{/* Loading skeleton */}
|
||||
<li>
|
||||
<div class="card card-side m-2 bg-base-100 shadow-lg">
|
||||
<figure class="pl-2">
|
||||
<div class="skeleton size-12"></div>
|
||||
</figure>
|
||||
<div class="card-body">
|
||||
<h2 class="card-title">
|
||||
<div class="skeleton h-12 w-80"></div>
|
||||
</h2>
|
||||
<div class="skeleton h-8 w-72"></div>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</Match>
|
||||
<Match when={!loading() && Object.entries(machines()).length === 0}>
|
||||
No machines found
|
||||
</Match>
|
||||
<Match when={!loading()}>
|
||||
<For each={list()}>
|
||||
{(entry) => (
|
||||
<li>
|
||||
<div class="card card-side m-2 bg-base-100 shadow-lg">
|
||||
<figure class="pl-2">
|
||||
<span class="material-icons content-center text-5xl">
|
||||
devices_other
|
||||
</span>
|
||||
</figure>
|
||||
<div class="card-body flex-row justify-between">
|
||||
<div class="flex flex-col">
|
||||
<h2 class="card-title">{entry.machine_name}</h2>
|
||||
<p
|
||||
classList={{
|
||||
"text-gray-400": !entry.machine_description,
|
||||
"text-gray-600": !!entry.machine_description,
|
||||
}}
|
||||
>
|
||||
{entry.machine_description || "No description"}
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<button class="btn btn-ghost">
|
||||
<span class="material-icons">more_vert</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
)}
|
||||
</For>
|
||||
</Match>
|
||||
</Switch>
|
||||
</div>
|
||||
);
|
||||
};
|
@ -1,9 +1,11 @@
|
||||
const typography = require("@tailwindcss/typography");
|
||||
const daisyui = require("daisyui");
|
||||
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
module.exports = {
|
||||
content: ["./src/**/*.{js,jsx,ts,tsx}"],
|
||||
theme: {
|
||||
extend: {},
|
||||
},
|
||||
plugins: [daisyui],
|
||||
plugins: [typography, daisyui],
|
||||
};
|
||||
|
@ -11,7 +11,7 @@
|
||||
|
||||
npmDeps = pkgs.fetchNpmDeps {
|
||||
src = ./app;
|
||||
hash = "sha256-E0++hupVKnDqmLk7ljoMcqcI4w+DIMlfRYRPbKUsT2c=";
|
||||
hash = "sha256-4ZurUbY5uMq7KeKnYRJ1+/Go9WoURFOpeZgLE0S6WZI=";
|
||||
};
|
||||
# The prepack script runs the build script, which we'd rather do in the build phase.
|
||||
npmPackFlags = [ "--ignore-scripts" ];
|
||||
|
Loading…
Reference in New Issue
Block a user