Merge pull request 'theme-ui' (#2443) from hsjobeki/clan-core:theme-ui into main
This commit is contained in:
@@ -219,7 +219,7 @@ export function SelectInput(props: SelectInputpProps) {
|
||||
<span class="label-text text-neutral">{props.helperText}</span>
|
||||
)}
|
||||
{props.error && (
|
||||
<span class="label-text-alt font-bold text-error">
|
||||
<span class="label-text-alt font-bold text-error-700">
|
||||
{props.error}
|
||||
</span>
|
||||
)}
|
||||
|
||||
@@ -60,7 +60,9 @@ export function TextInput(props: TextInputProps) {
|
||||
<span class="label-text text-neutral">{props.helperText}</span>
|
||||
)}
|
||||
{props.error && (
|
||||
<span class="label-text-alt font-bold text-error">{props.error}</span>
|
||||
<span class="label-text-alt font-bold text-error-700">
|
||||
{props.error}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</label>
|
||||
|
||||
@@ -132,7 +132,7 @@ interface UnsupportedProps {
|
||||
|
||||
const Unsupported = (props: UnsupportedProps) => (
|
||||
<div>
|
||||
{props.error && <div class="font-bold text-error">{props.error}</div>}
|
||||
{props.error && <div class="font-bold text-error-700">{props.error}</div>}
|
||||
<span>
|
||||
Invalid or unsupported schema entry of type:{" "}
|
||||
<b>{JSON.stringify(props.schema.type)}</b>
|
||||
@@ -193,7 +193,7 @@ export function StringField<T extends FieldValues, R extends ResponseData>(
|
||||
props.schema.type !== "integer"
|
||||
) {
|
||||
return (
|
||||
<span class="text-error">
|
||||
<span class="text-error-700">
|
||||
Error cannot render the following as String input.
|
||||
<Unsupported schema={props.schema} />
|
||||
</span>
|
||||
@@ -339,7 +339,9 @@ interface OptionSchemaProps {
|
||||
}
|
||||
export function OptionSchema(props: OptionSchemaProps) {
|
||||
return (
|
||||
<Switch fallback={<option class="text-error">Item spec unhandled</option>}>
|
||||
<Switch
|
||||
fallback={<option class="text-error-700">Item spec unhandled</option>}
|
||||
>
|
||||
<Match when={typeof props.itemSpec === "string" && props.itemSpec}>
|
||||
{(o) => <option>{o()}</option>}
|
||||
</Match>
|
||||
@@ -444,7 +446,7 @@ export function ArrayFields<T extends FieldValues, R extends ResponseData>(
|
||||
) {
|
||||
if (props.schema.type !== "array") {
|
||||
return (
|
||||
<span class="text-error">
|
||||
<span class="text-error-700">
|
||||
Error cannot render the following as array.
|
||||
<Unsupported schema={props.schema} />
|
||||
</span>
|
||||
@@ -621,7 +623,7 @@ export function ArrayFields<T extends FieldValues, R extends ResponseData>(
|
||||
</ListValueDisplay>
|
||||
)}
|
||||
</For>
|
||||
<span class="label-text-alt font-bold text-error">
|
||||
<span class="label-text-alt font-bold text-error-700">
|
||||
{fieldArray.error}
|
||||
</span>
|
||||
|
||||
@@ -703,7 +705,7 @@ export function ObjectFields<T extends FieldValues, R extends ResponseData>(
|
||||
) {
|
||||
if (props.schema.type !== "object") {
|
||||
return (
|
||||
<span class="text-error">
|
||||
<span class="text-error-700">
|
||||
Error cannot render the following as Object
|
||||
<Unsupported schema={props.schema} />
|
||||
</span>
|
||||
@@ -748,7 +750,7 @@ export function ObjectFields<T extends FieldValues, R extends ResponseData>(
|
||||
/>
|
||||
)}
|
||||
{typeof propSchema === "boolean" && (
|
||||
<span class="text-error">
|
||||
<span class="text-error-700">
|
||||
Schema: Object of Boolean not supported
|
||||
</span>
|
||||
)}
|
||||
|
||||
@@ -19,7 +19,7 @@ export const Sidebar = (props: RouteSectionProps) => {
|
||||
}));
|
||||
|
||||
return (
|
||||
<aside class="w-80 rounded-xl border border-slate-900 bg-slate-800 pb-10">
|
||||
<aside class="w-80 rounded-xl border border-slate-900 bg-slate-800 pb-10">
|
||||
<div class="m-4 flex flex-col text-center capitalize text-white">
|
||||
<span class="text-lg">{clanQuery.data?.name}</span>
|
||||
<span class="text-sm">{clanQuery.data?.description}</span>
|
||||
|
||||
@@ -92,7 +92,9 @@ export function FileInput(props: FileInputProps) {
|
||||
aria-errormessage={`${props.name}-error`}
|
||||
/>
|
||||
{props.error && (
|
||||
<span class="label-text-alt font-bold text-error">{props.error}</span>
|
||||
<span class="label-text-alt font-bold text-error-700">
|
||||
{props.error}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
20
pkgs/webview-ui/app/src/components/Helpers/List.tsx
Normal file
20
pkgs/webview-ui/app/src/components/Helpers/List.tsx
Normal file
@@ -0,0 +1,20 @@
|
||||
import { type JSX } from "solid-js";
|
||||
|
||||
type sizes = "small" | "medium" | "large";
|
||||
|
||||
const gapSizes: { [size in sizes]: string } = {
|
||||
small: "gap-2",
|
||||
medium: "gap-4",
|
||||
large: "gap-6",
|
||||
};
|
||||
|
||||
interface List {
|
||||
children: JSX.Element;
|
||||
gapSize: sizes;
|
||||
}
|
||||
|
||||
export const List = (props: List) => {
|
||||
const { children, gapSize } = props;
|
||||
|
||||
return <ul class={`flex flex-col ${gapSizes[gapSize]}`}> {children}</ul>;
|
||||
};
|
||||
1
pkgs/webview-ui/app/src/components/Helpers/index.tsx
Normal file
1
pkgs/webview-ui/app/src/components/Helpers/index.tsx
Normal file
@@ -0,0 +1 @@
|
||||
export { List } from "./List";
|
||||
@@ -103,7 +103,7 @@ export const MachineListItem = (props: MachineListItemProps) => {
|
||||
};
|
||||
return (
|
||||
<li>
|
||||
<div class="card card-side m-2 bg-base-100">
|
||||
<div class="card card-side m-2">
|
||||
<figure class="pl-2">
|
||||
<span
|
||||
class="material-icons content-center text-5xl"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { children, Component, createSignal, type JSX } from "solid-js";
|
||||
import { children, createSignal, type JSX } from "solid-js";
|
||||
import { useFloating } from "@/src/floating";
|
||||
import {
|
||||
autoUpdate,
|
||||
|
||||
@@ -47,7 +47,9 @@ export function SelectInput<T extends FieldValues, R extends ResponseData>(
|
||||
</select>
|
||||
|
||||
{props.error && (
|
||||
<span class="label-text-alt font-bold text-error">{props.error}</span>
|
||||
<span class="label-text-alt font-bold text-error-700">
|
||||
{props.error}
|
||||
</span>
|
||||
)}
|
||||
</label>
|
||||
);
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
import { List } from "@/src/components/Helpers";
|
||||
import { SidebarListItem } from "../SidebarListItem";
|
||||
|
||||
export const SidebarFlyout = () => {
|
||||
return (
|
||||
<div class="sidebar__flyout">
|
||||
<div class="sidebar__flyout__inner">
|
||||
<List gapSize="small">
|
||||
<SidebarListItem href="/settings" title="Settings" />
|
||||
</List>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
60
pkgs/webview-ui/app/src/components/Sidebar/SidebarHeader.tsx
Normal file
60
pkgs/webview-ui/app/src/components/Sidebar/SidebarHeader.tsx
Normal file
@@ -0,0 +1,60 @@
|
||||
import { createSignal, Show } from "solid-js";
|
||||
|
||||
import { Typography } from "@/src/components/Typography";
|
||||
import { SidebarFlyout } from "./SidebarFlyout";
|
||||
|
||||
interface SidebarHeader {
|
||||
clanName: string;
|
||||
}
|
||||
|
||||
export const SidebarHeader = (props: SidebarHeader) => {
|
||||
const { clanName } = props;
|
||||
|
||||
const [showFlyout, toggleFlyout] = createSignal(false);
|
||||
|
||||
function handleClick() {
|
||||
toggleFlyout(!showFlyout());
|
||||
}
|
||||
|
||||
const renderClanProfile = () => (
|
||||
<div
|
||||
class={`sidebar__profile ${showFlyout() ? "sidebar__profile--flyout" : ""}`}
|
||||
>
|
||||
<Typography
|
||||
classes="sidebar__profile__character"
|
||||
tag="span"
|
||||
hierarchy="title"
|
||||
size="m"
|
||||
weight="bold"
|
||||
color="primary"
|
||||
inverted={true}
|
||||
>
|
||||
{clanName.slice(0, 1)}
|
||||
</Typography>
|
||||
</div>
|
||||
);
|
||||
|
||||
const renderClanTitle = () => (
|
||||
<Typography
|
||||
classes="sidebar__title"
|
||||
tag="h3"
|
||||
hierarchy="body"
|
||||
size="default"
|
||||
weight="medium"
|
||||
color="primary"
|
||||
inverted={true}
|
||||
>
|
||||
{clanName}
|
||||
</Typography>
|
||||
);
|
||||
|
||||
return (
|
||||
<header class="sidebar__header">
|
||||
<div onClick={handleClick} class="sidebar__header__inner">
|
||||
{renderClanProfile()}
|
||||
{renderClanTitle()}
|
||||
</div>
|
||||
{showFlyout() && <SidebarFlyout />}
|
||||
</header>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,30 @@
|
||||
import { A } from "@solidjs/router";
|
||||
|
||||
import { Typography } from "@/src/components/Typography";
|
||||
|
||||
interface SidebarListItem {
|
||||
title: string;
|
||||
href: string;
|
||||
}
|
||||
|
||||
export const SidebarListItem = (props: SidebarListItem) => {
|
||||
const { title, href } = props;
|
||||
|
||||
return (
|
||||
<li class="sidebar__list__item">
|
||||
<A class="sidebar__list__link" href={href}>
|
||||
<Typography
|
||||
classes="sidebar__list__content"
|
||||
tag="span"
|
||||
hierarchy="body"
|
||||
size="s"
|
||||
weight="normal"
|
||||
color="primary"
|
||||
inverted={true}
|
||||
>
|
||||
{title}
|
||||
</Typography>
|
||||
</A>
|
||||
</li>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,20 @@
|
||||
.sidebar__flyout {
|
||||
top: 0;
|
||||
position: absolute;
|
||||
z-index: theme(zIndex.30);
|
||||
|
||||
padding: theme(padding[1]);
|
||||
width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.sidebar__flyout__inner {
|
||||
position: relative;
|
||||
width: inherit;
|
||||
height: inherit;
|
||||
|
||||
padding: theme(padding.12) theme(padding.3) theme(padding.3);
|
||||
background-color: rgba(var(--clr-bg-inv-4) / 0.95);
|
||||
border: 1px solid rgb(var(--clr-border-inv-4));
|
||||
border-radius: theme(borderRadius.lg);
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
.sidebar__header {
|
||||
position: relative;
|
||||
padding: 1px 1px 0;
|
||||
cursor: pointer;
|
||||
|
||||
&:after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: rgb(var(--clr-bg-inv-3));
|
||||
|
||||
border-bottom: 1px solid var(--clr-border-inv-3);
|
||||
border-top-left-radius: theme(borderRadius.xl);
|
||||
border-top-right-radius: theme(borderRadius.xl);
|
||||
}
|
||||
}
|
||||
|
||||
.sidebar__header__inner {
|
||||
position: relative;
|
||||
z-index: theme(zIndex.40);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0 theme(gap.3);
|
||||
|
||||
padding: theme(padding.3) theme(padding.3);
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
.sidebar__list__item {
|
||||
position: relative;
|
||||
cursor: theme(cursor.pointer);
|
||||
|
||||
&:after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
z-index: theme(zIndex.10);
|
||||
top: 0;
|
||||
left: 0;
|
||||
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: theme(borderRadius.md);
|
||||
transform: scale(0.98);
|
||||
transition: transform 0.24s ease-in-out;
|
||||
}
|
||||
|
||||
&:hover:after {
|
||||
background: rgb(var(--clr-bg-inv-acc-2));
|
||||
transform: scale(theme(scale.100));
|
||||
transition: transform 0.24s ease-in-out;
|
||||
}
|
||||
|
||||
&:active {
|
||||
transform: scale(0.99);
|
||||
transition: transform 0.08s ease-in-out;
|
||||
}
|
||||
|
||||
&:active:after {
|
||||
background: rgb(var(--clr-bg-inv-acc-3));
|
||||
transform: scale(theme(scale.100));
|
||||
}
|
||||
}
|
||||
|
||||
.sidebar__list__link {
|
||||
position: relative;
|
||||
z-index: 20;
|
||||
display: block;
|
||||
padding: theme(padding.3);
|
||||
}
|
||||
|
||||
.sidebar__list__content {
|
||||
position: relative;
|
||||
z-index: 20;
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
.sidebar__profile {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
width: theme(width.8);
|
||||
height: theme(height.8);
|
||||
|
||||
background: rgb(var(--clr-bg-inv-4));
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.sidebar__profile--flyout {
|
||||
background: rgb(var(--clr-bg-def-2));
|
||||
}
|
||||
|
||||
.sidebar__profile--flyout > .sidebar__profile__character {
|
||||
color: rgb(var(--clr-fg-def-1)) !important;
|
||||
}
|
||||
29
pkgs/webview-ui/app/src/components/Sidebar/css/sidebar.css
Normal file
29
pkgs/webview-ui/app/src/components/Sidebar/css/sidebar.css
Normal file
@@ -0,0 +1,29 @@
|
||||
/* Sidebar Elements */
|
||||
|
||||
@import "./sidebar-header";
|
||||
@import "./sidebar-flyout";
|
||||
@import "./sidebar-list-item";
|
||||
@import "./sidebar-profile";
|
||||
|
||||
/* Sidebar Structure */
|
||||
|
||||
.sidebar {
|
||||
min-width: theme(width.72);
|
||||
height: 100%;
|
||||
background-color: rgb(var(--clr-bg-inv-2));
|
||||
border: 1px solid rgb(var(--clr-border-inv-2));
|
||||
border-radius: theme(borderRadius.xl);
|
||||
}
|
||||
|
||||
.sidebar__body {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: theme(padding.2);
|
||||
padding: theme(padding.4) theme(padding.2);
|
||||
}
|
||||
|
||||
.sidebar__section {
|
||||
padding: theme(padding.2);
|
||||
background-color: rgba(var(--clr-bg-inv-3) / 0.9);
|
||||
border-radius: theme(borderRadius.md);
|
||||
}
|
||||
93
pkgs/webview-ui/app/src/components/Sidebar/index.tsx
Normal file
93
pkgs/webview-ui/app/src/components/Sidebar/index.tsx
Normal file
@@ -0,0 +1,93 @@
|
||||
import { For, createEffect, Show, type JSX, children } from "solid-js";
|
||||
import { A, RouteSectionProps } from "@solidjs/router";
|
||||
import { activeURI } from "@/src/App";
|
||||
import { createQuery } from "@tanstack/solid-query";
|
||||
import { callApi } from "@/src/api";
|
||||
import { AppRoute, routes } from "@/src/index";
|
||||
|
||||
import { List } from "../Helpers";
|
||||
import { SidebarHeader } from "./SidebarHeader";
|
||||
|
||||
import { SidebarListItem } from "./SidebarListItem";
|
||||
import "./css/sidebar.css";
|
||||
import { Typography } from "../Typography";
|
||||
|
||||
export const SidebarSection = (props: {
|
||||
title: string;
|
||||
children: JSX.Element;
|
||||
}) => {
|
||||
const { title, children } = props;
|
||||
|
||||
return (
|
||||
<details class="sidebar__section accordeon" open>
|
||||
<summary class="accordeon__header">
|
||||
<Typography
|
||||
classes="uppercase"
|
||||
tag="p"
|
||||
hierarchy="body"
|
||||
size="xs"
|
||||
weight="normal"
|
||||
color="tertiary"
|
||||
inverted={true}
|
||||
>
|
||||
{title}
|
||||
</Typography>
|
||||
</summary>
|
||||
<div class="accordeon__body">{children}</div>
|
||||
</details>
|
||||
);
|
||||
};
|
||||
|
||||
export const Sidebar = (props: RouteSectionProps) => {
|
||||
createEffect(() => {
|
||||
console.log("machines");
|
||||
console.log(routes);
|
||||
});
|
||||
|
||||
const query = createQuery(() => ({
|
||||
queryKey: [activeURI(), "meta"],
|
||||
queryFn: async () => {
|
||||
const curr = activeURI();
|
||||
if (curr) {
|
||||
const result = await callApi("show_clan_meta", { uri: curr });
|
||||
|
||||
if (result.status === "error") throw new Error("Failed to fetch data");
|
||||
|
||||
return result.data;
|
||||
}
|
||||
},
|
||||
}));
|
||||
|
||||
return (
|
||||
<div class="sidebar bg-transparent/95">
|
||||
<Show
|
||||
when={query.data}
|
||||
fallback={<SidebarHeader clanName={"Untitled"} />}
|
||||
>
|
||||
{(meta) => <SidebarHeader clanName={meta().name} />}
|
||||
</Show>
|
||||
<div class="sidebar__body">
|
||||
<For each={routes.filter((r) => !r.hidden && r.path != "/clans")}>
|
||||
{(route: AppRoute) => (
|
||||
<Show when={route.children}>
|
||||
{(children) => (
|
||||
<SidebarSection title={route.label}>
|
||||
<ul>
|
||||
<For each={children().filter((r) => !r.hidden)}>
|
||||
{(child) => (
|
||||
<SidebarListItem
|
||||
href={`${route.path}${child.path}`}
|
||||
title={child.label}
|
||||
/>
|
||||
)}
|
||||
</For>
|
||||
</ul>
|
||||
</SidebarSection>
|
||||
)}
|
||||
</Show>
|
||||
)}
|
||||
</For>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -63,7 +63,9 @@ export function TextInput<T extends FieldValues, R extends ResponseData>(
|
||||
</Show>
|
||||
</div>
|
||||
{props.error && (
|
||||
<span class="label-text-alt font-bold text-error">{props.error}</span>
|
||||
<span class="label-text-alt font-bold text-error-700">
|
||||
{props.error}
|
||||
</span>
|
||||
)}
|
||||
</label>
|
||||
);
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
.fnt-clr-primary {
|
||||
color: (rgb(--clr-fg-def-1));
|
||||
}
|
||||
|
||||
.fnt-clr-secondary {
|
||||
color: rgb(var(--clr-fg-def-2));
|
||||
}
|
||||
|
||||
.fnt-clr-tertiary {
|
||||
color: rgb(var(--clr-fg-def-3));
|
||||
}
|
||||
|
||||
.fnt-clr-primary.fnt-clr--inverted {
|
||||
color: rgb(var(--clr-fg-inv-1));
|
||||
}
|
||||
|
||||
.fnt-clr-secondary.fnt-clr--inverted {
|
||||
color: rgb(var(--clr-fg-inv-2));
|
||||
}
|
||||
|
||||
.fnt-clr-tertiary.fnt-clr--inverted {
|
||||
color: rgb(var(--clr-fg-inv-3));
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
@import "./typography-body.css";
|
||||
@import "./typography-title.css";
|
||||
@import "./typography-headline.css";
|
||||
@@ -0,0 +1,17 @@
|
||||
.fnt-body-default {
|
||||
font-size: 1rem;
|
||||
line-height: 132%;
|
||||
letter-spacing: 3%;
|
||||
}
|
||||
|
||||
.fnt-body-s {
|
||||
font-size: 0.925rem;
|
||||
line-height: 132%;
|
||||
letter-spacing: 3%;
|
||||
}
|
||||
|
||||
.fnt-body-xs {
|
||||
font-size: 0.875rem;
|
||||
line-height: 132%;
|
||||
letter-spacing: 3%;
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
.fnt-headline-default {
|
||||
font-size: 1.5rem;
|
||||
line-height: 116%;
|
||||
letter-spacing: 1%;
|
||||
}
|
||||
|
||||
.fnt-headline-m {
|
||||
font-size: 1.75rem;
|
||||
line-height: 116%;
|
||||
letter-spacing: 1%;
|
||||
}
|
||||
|
||||
.fnt-headline-l {
|
||||
font-size: 2rem;
|
||||
line-height: 116%;
|
||||
letter-spacing: 1%;
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
.fnt-title-default {
|
||||
font-size: 1.125rem;
|
||||
line-height: 124%;
|
||||
letter-spacing: 3%;
|
||||
}
|
||||
|
||||
.fnt-title-m {
|
||||
font-size: 1.25rem;
|
||||
line-height: 124%;
|
||||
letter-spacing: 3%;
|
||||
}
|
||||
|
||||
.fnt-title-l {
|
||||
font-size: 1.375rem;
|
||||
line-height: 124%;
|
||||
letter-spacing: 3%;
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
@import "./typography-hierarchy/";
|
||||
@import "./typography-color.css";
|
||||
|
||||
.fnt-weight-normal {
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.fnt-weight-medium {
|
||||
font-weight: medium;
|
||||
}
|
||||
|
||||
.fnt-weight-bold {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.fnt-weight-normal.fnt-clr--inverted {
|
||||
font-weight: light;
|
||||
}
|
||||
|
||||
.fnt-weight-medium.fnt-clr--inverted {
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.fnt-weight-bold.fnt-clr--inverted {
|
||||
font-weight: 600;
|
||||
}
|
||||
96
pkgs/webview-ui/app/src/components/Typography/index.tsx
Normal file
96
pkgs/webview-ui/app/src/components/Typography/index.tsx
Normal file
@@ -0,0 +1,96 @@
|
||||
import { type JSX } from "solid-js";
|
||||
import { Dynamic } from "solid-js/web";
|
||||
import cx from "classnames";
|
||||
import "./css/typography.css";
|
||||
|
||||
type Hierarchy = "body" | "title" | "headline";
|
||||
type Color = "primary" | "secondary" | "tertiary";
|
||||
type Weight = "normal" | "medium" | "bold";
|
||||
type Tag = "span" | "p" | "h1" | "h2" | "h3" | "h4";
|
||||
|
||||
const colorMap: Record<Color, string> = {
|
||||
primary: cx("fnt-clr-primary"),
|
||||
secondary: cx("fnt-clr-secondary"),
|
||||
tertiary: cx("fnt-clr-tertiary"),
|
||||
};
|
||||
|
||||
// type Size = "default" | "xs" | "s" | "m" | "l";
|
||||
interface SizeForHierarchy {
|
||||
body: {
|
||||
default: string;
|
||||
xs: string;
|
||||
s: string;
|
||||
};
|
||||
headline: {
|
||||
default: string;
|
||||
m: string;
|
||||
l: string;
|
||||
};
|
||||
title: {
|
||||
default: string;
|
||||
m: string;
|
||||
l: string;
|
||||
};
|
||||
}
|
||||
|
||||
type AllowedSizes<H extends Hierarchy> = keyof SizeForHierarchy[H];
|
||||
|
||||
const sizeHierarchyMap: SizeForHierarchy = {
|
||||
body: {
|
||||
default: cx("fnt-body-default"),
|
||||
xs: cx("fnt-body-xs"),
|
||||
s: cx("fnt-body-s"),
|
||||
// m: cx("fnt-body-m"),
|
||||
// l: cx("fnt-body-l"),
|
||||
},
|
||||
headline: {
|
||||
default: cx("fnt-headline-default"),
|
||||
// xs: cx("fnt-headline-xs"),
|
||||
// s: cx("fnt-headline-s"),
|
||||
m: cx("fnt-headline-m"),
|
||||
l: cx("fnt-headline-l"),
|
||||
},
|
||||
title: {
|
||||
default: cx("fnt-title-default"),
|
||||
// xs: cx("fnt-title-xs"),
|
||||
// s: cx("fnt-title-s"),
|
||||
m: cx("fnt-title-m"),
|
||||
l: cx("fnt-title-l"),
|
||||
},
|
||||
};
|
||||
|
||||
const weightMap: Record<Weight, string> = {
|
||||
normal: cx("fnt-weight-normal"),
|
||||
medium: cx("fnt-weight-medium"),
|
||||
bold: cx("fnt-weight-bold"),
|
||||
};
|
||||
|
||||
interface TypographyProps<H extends Hierarchy> {
|
||||
hierarchy: H;
|
||||
weight: Weight;
|
||||
color: Color;
|
||||
inverted: boolean;
|
||||
size: AllowedSizes<H>;
|
||||
tag: Tag;
|
||||
children: JSX.Element;
|
||||
classes?: string;
|
||||
}
|
||||
export const Typography = <H extends Hierarchy>(props: TypographyProps<H>) => {
|
||||
const { size, color, inverted, hierarchy, weight, tag, children, classes } =
|
||||
props;
|
||||
|
||||
return (
|
||||
<Dynamic
|
||||
component={tag}
|
||||
class={cx(
|
||||
classes,
|
||||
colorMap[color],
|
||||
inverted && "fnt-clr--inverted",
|
||||
sizeHierarchyMap[hierarchy][size] as string,
|
||||
weightMap[weight],
|
||||
)}
|
||||
>
|
||||
{children}
|
||||
</Dynamic>
|
||||
);
|
||||
};
|
||||
@@ -1,5 +1,6 @@
|
||||
@import "material-icons/iconfont/filled.css";
|
||||
/* List of icons: https://marella.me/material-icons/demo/ */
|
||||
/* @import url(./components/Typography/css/typography.css); */
|
||||
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@@ -21,8 +22,67 @@
|
||||
src: url(../.fonts/Archiv0-Bold.otf) format("opentype");
|
||||
}
|
||||
|
||||
:root {
|
||||
--clr-bg-def-1: theme(colors.white);
|
||||
--clr-bg-def-2: theme(colors.secondary.50);
|
||||
--clr-bg-def-3: theme(colors.secondary.100);
|
||||
--clr-bg-def-4: theme(colors.secondary.200);
|
||||
--clr-bg-def-5: theme(colors.secondary.300);
|
||||
|
||||
--clr-border-def-1: theme(colors.secondary.50);
|
||||
--clr-border-def-2: theme(colors.secondary.100);
|
||||
--clr-border-def-3: theme(colors.secondary.200);
|
||||
--clr-border-def-4: theme(colors.secondary.300);
|
||||
--clr-border-def-5: theme(colors.secondary.400);
|
||||
|
||||
--clr-bg-inv-1: theme(colors.primary.600);
|
||||
--clr-bg-inv-2: theme(colors.primary.700);
|
||||
--clr-bg-inv-3: theme(colors.primary.800);
|
||||
--clr-bg-inv-4: theme(colors.primary.900);
|
||||
--clr-bg-inv-5: theme(colors.primary.950);
|
||||
|
||||
--clr-border-inv-1: theme(colors.secondary.800);
|
||||
--clr-border-inv-2: theme(colors.secondary.900);
|
||||
--clr-border-inv-3: theme(colors.secondary.900);
|
||||
--clr-border-inv-4: theme(colors.secondary.950);
|
||||
--clr-border-inv-5: theme(colors.black);
|
||||
|
||||
--clr-bg-inv-acc-1: theme(colors.secondary.500);
|
||||
--clr-bg-inv-acc-2: theme(colors.secondary.600);
|
||||
--clr-bg-inv-acc-3: theme(colors.secondary.700);
|
||||
|
||||
--clr-fg-def-1: theme(colors.secondary.950);
|
||||
--clr-fg-def-2: theme(colors.secondary.900);
|
||||
--clr-fg-def-3: theme(colors.secondary.700);
|
||||
--clr-fg-def-4: theme(colors.secondary.500);
|
||||
|
||||
--clr-fg-inv-1: theme(colors.white);
|
||||
--clr-fg-inv-2: theme(colors.secondary.100);
|
||||
--clr-fg-inv-3: theme(colors.secondary.300);
|
||||
--clr-fg-inv-4: theme(colors.secondary.400);
|
||||
}
|
||||
|
||||
html {
|
||||
@apply font-sans;
|
||||
overflow-x: hidden;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
.accordeon {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: theme(gap.3);
|
||||
}
|
||||
|
||||
.accordeon__header {
|
||||
padding: theme(padding.2) theme(padding[1.5]);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.accordeon__header::-webkit-details-marker {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.accordeon__body {
|
||||
padding: theme(padding.2) 0 theme(padding.1);
|
||||
}
|
||||
|
||||
@@ -59,7 +59,7 @@ export const Header = (props: HeaderProps) => {
|
||||
<span class="flex flex-col">
|
||||
<Show when={query.data}>
|
||||
{(meta) => [
|
||||
<span class="text-primary">{meta().name}</span>,
|
||||
<span class="text-primary-800">{meta().name}</span>,
|
||||
<span class="text-neutral">{meta()?.description}</span>,
|
||||
]}
|
||||
</Show>
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { Component, createEffect, Show } from "solid-js";
|
||||
import { Header } from "./header";
|
||||
import { Sidebar } from "../Sidebar";
|
||||
import { Sidebar } from "@/src/components/Sidebar";
|
||||
import { activeURI, clanList } from "../App";
|
||||
import { redirect, RouteSectionProps, useNavigate } from "@solidjs/router";
|
||||
import { RouteSectionProps, useNavigate } from "@solidjs/router";
|
||||
|
||||
export const Layout: Component<RouteSectionProps> = (props) => {
|
||||
const navigate = useNavigate();
|
||||
@@ -16,9 +16,10 @@ export const Layout: Component<RouteSectionProps> = (props) => {
|
||||
navigate("/welcome");
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<div class="h-screen bg-base-100 p-4">
|
||||
<div class="drawer lg:drawer-open ">
|
||||
<div class="drawer h-full lg:drawer-open ">
|
||||
<input
|
||||
id="toplevel-drawer"
|
||||
type="checkbox"
|
||||
|
||||
@@ -103,7 +103,7 @@ export const CreateClan = () => {
|
||||
{(field, props) => (
|
||||
<label class="form-control w-full">
|
||||
<div class="label">
|
||||
<span class="label-text block after:ml-0.5 after:text-primary after:content-['*']">
|
||||
<span class="label-text block after:ml-0.5 after:text-primary-800 after:content-['*']">
|
||||
Name
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@@ -74,8 +74,8 @@ const EditClanForm = (props: EditClanFormProps) => {
|
||||
<>
|
||||
<figure class="p-1">
|
||||
<div class="flex flex-col items-center">
|
||||
<div class="text-3xl text-primary">{curr_name()}</div>
|
||||
<div class="text-secondary">Wide settings</div>
|
||||
<div class="text-3xl text-primary-800">{curr_name()}</div>
|
||||
<div class="text-secondary-800">Wide settings</div>
|
||||
</div>
|
||||
</figure>
|
||||
<figure>
|
||||
@@ -100,7 +100,7 @@ const EditClanForm = (props: EditClanFormProps) => {
|
||||
)}
|
||||
</Field>
|
||||
<div class="card-body">
|
||||
<span class="text-xl text-primary">General</span>
|
||||
<span class="text-xl text-primary-800">General</span>
|
||||
<Field
|
||||
name="name"
|
||||
validate={[required("Please enter a unique name for the clan.")]}
|
||||
@@ -108,7 +108,7 @@ const EditClanForm = (props: EditClanFormProps) => {
|
||||
{(field, props) => (
|
||||
<label class="form-control w-full">
|
||||
<div class="label">
|
||||
<span class="label-text block after:ml-0.5 after:text-primary after:content-['*']">
|
||||
<span class="label-text block after:ml-0.5 after:text-primary-800 after:content-['*']">
|
||||
Name
|
||||
</span>
|
||||
</div>
|
||||
@@ -226,9 +226,9 @@ const AdminModuleForm = (props: AdminModuleFormProps) => {
|
||||
return (
|
||||
<Form onSubmit={handleSubmit}>
|
||||
<div class="card-body">
|
||||
<span class="text-xl text-primary">Administration</span>
|
||||
<span class="text-xl text-primary-800">Administration</span>
|
||||
<div class="grid grid-cols-12 gap-2">
|
||||
<span class="col-span-12 text-lg text-neutral">
|
||||
<span class="col-span-12 text-lg text-neutral-800">
|
||||
Each of the following keys can be used to authenticate on machines
|
||||
</span>
|
||||
<For each={keys()}>
|
||||
|
||||
@@ -59,7 +59,7 @@ const ClanItem = (props: ClanItemProps) => {
|
||||
|
||||
return (
|
||||
<div class="stat">
|
||||
<div class="stat-figure text-primary">
|
||||
<div class="stat-figure text-primary-800">
|
||||
<div class="join">
|
||||
<button
|
||||
class="join-item btn-sm"
|
||||
|
||||
@@ -431,7 +431,7 @@ const MachineForm = (props: MachineDetailsProps) => {
|
||||
</div>
|
||||
</figure>
|
||||
<div class="card-body">
|
||||
<span class="text-xl text-primary">General</span>
|
||||
<span class="text-xl text-primary-800">General</span>
|
||||
{/*
|
||||
<Field name="machine.tags" type="string[]">
|
||||
{(field, props) => field.value}
|
||||
@@ -536,14 +536,14 @@ const MachineForm = (props: MachineDetailsProps) => {
|
||||
{(module) => (
|
||||
<>
|
||||
<div class="divider"></div>
|
||||
<span class="text-xl text-primary">{module.name}</span>
|
||||
<span class="text-xl text-primary-800">{module.name}</span>
|
||||
{module.component}
|
||||
</>
|
||||
)}
|
||||
</For>
|
||||
<div class="divider"></div>
|
||||
|
||||
<span class="text-xl text-primary">Actions</span>
|
||||
<span class="text-xl text-primary-800">Actions</span>
|
||||
<div class="my-4 flex flex-col gap-6">
|
||||
<span class="max-w-md text-neutral">
|
||||
Installs the system for the first time. Used to bootstrap the remote
|
||||
|
||||
@@ -14,7 +14,7 @@ const ModuleListItem = (props: { name: string; info: ModuleInfo }) => {
|
||||
|
||||
return (
|
||||
<div class="stat">
|
||||
<div class="stat-figure text-primary">
|
||||
<div class="stat-figure text-primary-800">
|
||||
<div class="join">more</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ export const Welcome = () => {
|
||||
Build your own
|
||||
</button>
|
||||
<button
|
||||
class="link w-full text-right text-primary"
|
||||
class="link w-full text-right text-primary-800"
|
||||
onClick={async () => {
|
||||
const uri = await registerClan();
|
||||
if (uri) {
|
||||
|
||||
@@ -5,9 +5,7 @@ import core from "./tailwind/core-plugin";
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
const config = {
|
||||
content: ["./src/**/*.{js,jsx,ts,tsx}"],
|
||||
theme: {
|
||||
extend: {},
|
||||
},
|
||||
theme: {},
|
||||
daisyui: {
|
||||
themes: [
|
||||
{
|
||||
@@ -25,7 +23,7 @@ const config = {
|
||||
},
|
||||
],
|
||||
},
|
||||
plugins: [typography, daisyui, core],
|
||||
plugins: [typography, core, daisyui],
|
||||
};
|
||||
|
||||
export default config;
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
import plugin from "tailwindcss/plugin";
|
||||
import { typography } from "./typography";
|
||||
import theme from "tailwindcss/defaultTheme";
|
||||
// @ts-expect-error: lib of tailwind has no types
|
||||
import { parseColor } from "tailwindcss/lib/util/color";
|
||||
|
||||
/* Converts HEX color to RGB */
|
||||
const toRGB = (value: string) => parseColor(value).color.join(" ");
|
||||
|
||||
export default plugin.withOptions(
|
||||
(_options = {}) =>
|
||||
@@ -10,6 +14,64 @@ export default plugin.withOptions(
|
||||
// add configuration which is merged with the final config
|
||||
() => ({
|
||||
theme: {
|
||||
extend: {
|
||||
colors: {
|
||||
white: toRGB("#ffffff"),
|
||||
black: toRGB("#000000"),
|
||||
primary: {
|
||||
50: toRGB("#f4f9f9"),
|
||||
100: toRGB("#dbeceb"),
|
||||
200: toRGB("#b6d9d6"),
|
||||
300: toRGB("#8abebc"),
|
||||
400: toRGB("#478585"),
|
||||
500: toRGB("#526f6f"),
|
||||
600: toRGB("#4b6667"),
|
||||
700: toRGB("#345253"),
|
||||
800: toRGB("#2a4647"),
|
||||
900: toRGB("#1f3536"),
|
||||
950: toRGB("#162324"),
|
||||
},
|
||||
secondary: {
|
||||
50: toRGB("#f7f9f9"),
|
||||
100: toRGB("#e7f2f4"),
|
||||
200: toRGB("#d7e8ea"),
|
||||
300: toRGB("#afc6ca"),
|
||||
400: toRGB("#8fb2b6"),
|
||||
500: toRGB("#7b9a9e"),
|
||||
600: toRGB("#4f747a"),
|
||||
700: toRGB("#415e63"),
|
||||
800: toRGB("#445f64"),
|
||||
900: toRGB("#2b4347"),
|
||||
950: toRGB("#0d1415"),
|
||||
},
|
||||
info: {
|
||||
50: toRGB("#eff9ff"),
|
||||
100: toRGB("#dff2ff"),
|
||||
200: toRGB("#b8e8ff"),
|
||||
300: toRGB("#78d6ff"),
|
||||
400: toRGB("#2cc0ff"),
|
||||
500: toRGB("#06aaf1"),
|
||||
600: toRGB("#006ca7"),
|
||||
700: toRGB("#006ca7"),
|
||||
800: toRGB("#025b8a"),
|
||||
900: toRGB("#084c72"),
|
||||
950: toRGB("#06304b"),
|
||||
},
|
||||
error: {
|
||||
50: toRGB("#fcf3f8"),
|
||||
100: toRGB("#f9eaf4"),
|
||||
200: toRGB("#f5d5e9"),
|
||||
300: toRGB("#ea9ecb"),
|
||||
400: toRGB("#e383ba"),
|
||||
500: toRGB("#d75d9f"),
|
||||
600: toRGB("#c43e81"),
|
||||
700: toRGB("#a82e67"),
|
||||
800: toRGB("#8c2855"),
|
||||
900: toRGB("#75264a"),
|
||||
950: toRGB("#461129"),
|
||||
},
|
||||
},
|
||||
},
|
||||
...typography,
|
||||
},
|
||||
}),
|
||||
|
||||
Reference in New Issue
Block a user