Merge pull request 'fix(clan-app): Misc ui styling fixes' (#3451) from amunsen/clan-core:ui-fixes into main
Reviewed-on: https://git.clan.lol/clan/clan-core/pulls/3451
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -16,6 +16,9 @@ nixos.qcow2
|
||||
/docs/out
|
||||
**/.local.env
|
||||
|
||||
# MacOS stuff
|
||||
**/.DS_store
|
||||
|
||||
# dream2nix
|
||||
.dream2nix
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ import tseslint from "typescript-eslint";
|
||||
import tailwind from "eslint-plugin-tailwindcss";
|
||||
import pluginQuery from "@tanstack/eslint-plugin-query";
|
||||
|
||||
export default tseslint.config(
|
||||
const config = tseslint.config(
|
||||
eslint.configs.recommended,
|
||||
...pluginQuery.configs["flat/recommended"],
|
||||
...tseslint.configs.strict,
|
||||
@@ -30,3 +30,5 @@ export default tseslint.config(
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
export default config;
|
||||
|
||||
32
pkgs/webview-ui/app/src/Form/fieldset/index.tsx
Normal file
32
pkgs/webview-ui/app/src/Form/fieldset/index.tsx
Normal file
@@ -0,0 +1,32 @@
|
||||
import { JSX } from "solid-js";
|
||||
|
||||
import { Typography } from "@/src/components/Typography";
|
||||
|
||||
interface FieldsetProps {
|
||||
legend?: string;
|
||||
children: JSX.Element;
|
||||
class?: string;
|
||||
}
|
||||
|
||||
export default function Fieldset(props: FieldsetProps) {
|
||||
return (
|
||||
<fieldset class="flex flex-col gap-y-2.5">
|
||||
{props.legend && (
|
||||
<div class="px-2">
|
||||
<Typography
|
||||
hierarchy="body"
|
||||
tag="p"
|
||||
size="s"
|
||||
color="primary"
|
||||
weight="medium"
|
||||
>
|
||||
{props.legend}
|
||||
</Typography>
|
||||
</div>
|
||||
)}
|
||||
<div class="flex flex-col gap-y-3 rounded-md border border-secondary-200 bg-secondary-50 p-5">
|
||||
{props.children}
|
||||
</div>
|
||||
</fieldset>
|
||||
);
|
||||
}
|
||||
@@ -368,10 +368,10 @@ export function ListValueDisplay<T extends FieldValues, R extends ResponseData>(
|
||||
const bottomMost = () => props.idx === 0;
|
||||
|
||||
return (
|
||||
<div class="w-full border-l-4 border-gray-300">
|
||||
<div class="flex w-full items-end gap-2 px-4">
|
||||
<div class="w-full border-b border-secondary-200 px-2 pb-4">
|
||||
<div class="flex w-full items-center gap-2">
|
||||
{props.children}
|
||||
<div class="ml-4 min-w-fit pb-4">
|
||||
<div class="ml-4 min-w-fit">
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="s"
|
||||
@@ -541,7 +541,6 @@ export function ArrayFields<T extends FieldValues, R extends ResponseData>(
|
||||
)}
|
||||
</Field>
|
||||
</Match>
|
||||
|
||||
<Match
|
||||
when={
|
||||
itemsSchema().type === "string" ||
|
||||
@@ -629,9 +628,11 @@ export function ArrayFields<T extends FieldValues, R extends ResponseData>(
|
||||
</ListValueDisplay>
|
||||
)}
|
||||
</For>
|
||||
<span class=" font-bold text-error-700">
|
||||
{fieldArray.error}
|
||||
</span>
|
||||
<Show when={fieldArray.error}>
|
||||
<span class="font-bold text-error-700">
|
||||
{fieldArray.error}
|
||||
</span>
|
||||
</Show>
|
||||
|
||||
{/* Add new item */}
|
||||
<DynForm
|
||||
@@ -651,14 +652,16 @@ export function ArrayFields<T extends FieldValues, R extends ResponseData>(
|
||||
// Button for adding new items
|
||||
components={{
|
||||
before: (
|
||||
<Button
|
||||
variant="ghost"
|
||||
type="submit"
|
||||
endIcon={<Icon icon={"Plus"} />}
|
||||
class="capitalize"
|
||||
>
|
||||
Add {itemsSchema().title}
|
||||
</Button>
|
||||
<div class="flex w-full justify-end pb-2">
|
||||
<Button
|
||||
variant="ghost"
|
||||
type="submit"
|
||||
endIcon={<Icon size={14} icon={"Plus"} />}
|
||||
class="capitalize"
|
||||
>
|
||||
Add {itemsSchema().title}
|
||||
</Button>
|
||||
</div>
|
||||
),
|
||||
}}
|
||||
// Add the new item to the FieldArray
|
||||
|
||||
@@ -110,18 +110,18 @@ export const MachineListItem = (props: MachineListItemProps) => {
|
||||
setUpdating(false);
|
||||
};
|
||||
return (
|
||||
<div class="border rounded-lg border-def-2 p-3 m-2 w-64">
|
||||
<div class="m-2 w-64 rounded-lg border p-3 border-def-2">
|
||||
<figure class="h-fit rounded-xl border bg-def-2 border-def-5">
|
||||
<RndThumbnail name={name} width={220} height={120} />
|
||||
</figure>
|
||||
<div class="flex-row justify-between gap-4 pt-2 px-2">
|
||||
<div class="flex-row justify-between gap-4 px-2 pt-2">
|
||||
<div class="flex flex-col">
|
||||
<A href={`/machines/${name}`}>
|
||||
<Typography hierarchy="title" size="m" weight="bold">
|
||||
{name}
|
||||
</Typography>
|
||||
</A>
|
||||
<div class="text-slate-600 flex justify-between">
|
||||
<div class="flex justify-between text-slate-600">
|
||||
<div class="flex flex-nowrap">
|
||||
<span class="h-4">
|
||||
<Icon icon="Flash" class="h-4" font-size="inherit" />
|
||||
@@ -138,7 +138,7 @@ export const MachineListItem = (props: MachineListItemProps) => {
|
||||
popoverid={`menu-${props.name}`}
|
||||
label={<Icon icon={"More"} />}
|
||||
>
|
||||
<ul class="z-[1] w-64 p-2 shadow bg-white ">
|
||||
<ul class="z-[1] w-64 bg-white p-2 shadow ">
|
||||
<li>
|
||||
<Button
|
||||
variant="ghost"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { A } from "@solidjs/router";
|
||||
|
||||
import { Typography } from "@/src/components/Typography";
|
||||
import "./css/sidebar.css";
|
||||
|
||||
interface SidebarListItem {
|
||||
title: string;
|
||||
@@ -11,13 +11,13 @@ export const SidebarListItem = (props: SidebarListItem) => {
|
||||
const { title, href } = props;
|
||||
|
||||
return (
|
||||
<li class="sidebar__list__item">
|
||||
<li class="">
|
||||
<A class="sidebar__list__link" href={href}>
|
||||
<Typography
|
||||
class="sidebar__list__content"
|
||||
tag="span"
|
||||
hierarchy="body"
|
||||
size="s"
|
||||
size="xs"
|
||||
weight="normal"
|
||||
color="primary"
|
||||
inverted={true}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
.sidebar__list__item {
|
||||
.sidebar__list__link {
|
||||
position: relative;
|
||||
cursor: theme(cursor.pointer);
|
||||
|
||||
@@ -19,12 +19,12 @@
|
||||
&:hover:after {
|
||||
background: var(--clr-bg-inv-acc-2);
|
||||
transform: scale(theme(scale.100));
|
||||
transition: transform 0.24s ease-in-out;
|
||||
transition: transform 0.32s ease-in-out;
|
||||
}
|
||||
|
||||
&:active {
|
||||
transform: scale(0.99);
|
||||
transition: transform 0.08s ease-in-out;
|
||||
transition: transform 0.12s ease-in-out;
|
||||
}
|
||||
|
||||
&:active:after {
|
||||
@@ -37,7 +37,13 @@
|
||||
position: relative;
|
||||
z-index: 20;
|
||||
display: block;
|
||||
padding: theme(padding.3);
|
||||
padding: theme(padding.2) theme(padding.3);
|
||||
}
|
||||
|
||||
.sidebar__list__link.active {
|
||||
&:after {
|
||||
background: var(--clr-bg-inv-acc-3);
|
||||
}
|
||||
}
|
||||
|
||||
.sidebar__list__content {
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
|
||||
.sidebar {
|
||||
@apply bg-inv-2 h-full border border-solid border-inv-2 min-w-72 rounded-xl;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.sidebar__body {
|
||||
@@ -19,9 +21,9 @@
|
||||
}
|
||||
|
||||
.sidebar__section {
|
||||
padding: theme(padding.2);
|
||||
/* background-color: rgba(var(--clr-bg-inv-3) / 0.9); */
|
||||
@apply bg-primary-800/90;
|
||||
|
||||
padding: theme(padding.2);
|
||||
border-radius: theme(borderRadius.md);
|
||||
|
||||
::marker {
|
||||
|
||||
@@ -19,20 +19,22 @@ export const SidebarSection = (props: {
|
||||
|
||||
return (
|
||||
<details class="sidebar__section accordeon" open>
|
||||
<summary class="accordeon__header">
|
||||
<Typography
|
||||
class="inline-flex w-full gap-2 uppercase"
|
||||
tag="p"
|
||||
hierarchy="body"
|
||||
size="xs"
|
||||
weight="normal"
|
||||
color="tertiary"
|
||||
inverted={true}
|
||||
>
|
||||
<Icon icon={props.icon} />
|
||||
{title}
|
||||
<Icon icon="CaretDown" class="ml-auto" />
|
||||
</Typography>
|
||||
<summary style="display: contents;">
|
||||
<div class="accordeon__header">
|
||||
<Typography
|
||||
class="inline-flex w-full gap-2 uppercase !tracking-wider"
|
||||
tag="p"
|
||||
hierarchy="body"
|
||||
size="xxs"
|
||||
weight="normal"
|
||||
color="tertiary"
|
||||
inverted={true}
|
||||
>
|
||||
<Icon class="opacity-90" icon={props.icon} size={13} />
|
||||
{title}
|
||||
<Icon icon="CaretDown" class="ml-auto" size={10} />
|
||||
</Typography>
|
||||
</div>
|
||||
</summary>
|
||||
<div class="accordeon__body">{children}</div>
|
||||
</details>
|
||||
@@ -60,7 +62,7 @@ export const Sidebar = (props: RouteSectionProps) => {
|
||||
}));
|
||||
|
||||
return (
|
||||
<div class="sidebar opacity-95">
|
||||
<div class="sidebar">
|
||||
<Show
|
||||
when={query.data}
|
||||
fallback={<SidebarHeader clanName={"Untitled"} />}
|
||||
@@ -81,7 +83,7 @@ export const Sidebar = (props: RouteSectionProps) => {
|
||||
title={route.label}
|
||||
icon={route.icon || "Paperclip"}
|
||||
>
|
||||
<ul>
|
||||
<ul class="flex flex-col gap-y-0.5">
|
||||
<For each={children().filter((r) => !r.hidden)}>
|
||||
{(child) => (
|
||||
<SidebarListItem
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
}
|
||||
|
||||
.fnt-body-xxs {
|
||||
font-size: 0.6875rem;
|
||||
font-size: 0.75rem;
|
||||
line-height: 132%;
|
||||
letter-spacing: 0.00688rem;
|
||||
}
|
||||
|
||||
10
pkgs/webview-ui/app/src/components/accordion/accordion.css
Normal file
10
pkgs/webview-ui/app/src/components/accordion/accordion.css
Normal file
@@ -0,0 +1,10 @@
|
||||
.accordion {
|
||||
@apply flex flex-col gap-y-5;
|
||||
}
|
||||
|
||||
.accordion__title {
|
||||
@apply flex h-5 cursor-pointer items-center justify-end gap-x-0.5 px-1 font-medium;
|
||||
}
|
||||
|
||||
.accordion__body {
|
||||
}
|
||||
45
pkgs/webview-ui/app/src/components/accordion/index.tsx
Normal file
45
pkgs/webview-ui/app/src/components/accordion/index.tsx
Normal file
@@ -0,0 +1,45 @@
|
||||
import { createSignal, JSX, Show } from "solid-js";
|
||||
import Icon from "../icon";
|
||||
import { Button } from "../button";
|
||||
import cx from "classnames";
|
||||
import "./accordion.css";
|
||||
|
||||
interface AccordionProps {
|
||||
title: string;
|
||||
children: JSX.Element;
|
||||
class?: string;
|
||||
initiallyOpen?: boolean;
|
||||
}
|
||||
|
||||
export default function Accordion(props: AccordionProps) {
|
||||
const [isOpen, setIsOpen] = createSignal(props.initiallyOpen ?? false);
|
||||
return (
|
||||
<div class={cx(`accordion`, props.class)} tabindex="0">
|
||||
<div onClick={() => setIsOpen(!isOpen())} class="accordion__title">
|
||||
<Show
|
||||
when={isOpen()}
|
||||
fallback={
|
||||
<Button
|
||||
endIcon={<Icon size={12} icon={"CaretDown"} />}
|
||||
variant="light"
|
||||
size="s"
|
||||
>
|
||||
{props.title}
|
||||
</Button>
|
||||
}
|
||||
>
|
||||
<Button
|
||||
endIcon={<Icon size={12} icon={"CaretUp"} />}
|
||||
variant="dark"
|
||||
size="s"
|
||||
>
|
||||
{props.title}
|
||||
</Button>
|
||||
</Show>
|
||||
</div>
|
||||
<Show when={isOpen()}>
|
||||
<div class="accordion__body">{props.children}</div>
|
||||
</Show>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/* button DARK and states */
|
||||
|
||||
.button--dark {
|
||||
@apply border border-solid border-secondary-950 bg-primary-800 text-white;
|
||||
|
||||
box-shadow: inset 1px 1px theme(backgroundColor.secondary.700);
|
||||
|
||||
&:disabled {
|
||||
@apply disabled:bg-secondary-200 disabled:text-secondary-700 disabled:border-secondary-300;
|
||||
}
|
||||
|
||||
& .button__icon {
|
||||
color: theme(textColor.secondary.200);
|
||||
}
|
||||
}
|
||||
|
||||
.button--dark-hover:hover {
|
||||
@apply hover:bg-secondary-900;
|
||||
}
|
||||
|
||||
.button--dark-focus:focus {
|
||||
@apply focus:border-secondary-900;
|
||||
}
|
||||
|
||||
.button--dark-active:active {
|
||||
@apply focus:border-secondary-900;
|
||||
}
|
||||
|
||||
.button--dark-active:active {
|
||||
@apply active:border-secondary-900 active:shadow-inner-primary-active;
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/* button LIGHT and states */
|
||||
|
||||
.button--light {
|
||||
@apply border border-solid border-secondary-400 bg-secondary-100 text-secondary-950;
|
||||
|
||||
box-shadow: inset 1px 1px theme(backgroundColor.white);
|
||||
|
||||
&:disabled {
|
||||
@apply disabled:bg-secondary-50 disabled:text-secondary-200 disabled:border-secondary-700;
|
||||
}
|
||||
|
||||
& .button__icon {
|
||||
color: theme(textColor.secondary.900);
|
||||
}
|
||||
}
|
||||
|
||||
.button--light-hover:hover {
|
||||
@apply hover:bg-secondary-200;
|
||||
}
|
||||
|
||||
.button--light-focus:focus {
|
||||
@apply focus:bg-secondary-200;
|
||||
|
||||
& .button__label {
|
||||
color: theme(textColor.secondary.900) !important;
|
||||
}
|
||||
}
|
||||
|
||||
.button--light-active:active {
|
||||
@apply active:bg-secondary-200 border-secondary-600 active:text-secondary-900 active:shadow-inner-primary-active;
|
||||
|
||||
box-shadow: inset 2px 2px theme(backgroundColor.secondary.300);
|
||||
|
||||
& .button__label {
|
||||
color: theme(textColor.secondary.900) !important;
|
||||
}
|
||||
}
|
||||
54
pkgs/webview-ui/app/src/components/button/css/index.css
Normal file
54
pkgs/webview-ui/app/src/components/button/css/index.css
Normal file
@@ -0,0 +1,54 @@
|
||||
@import "./button-light.css";
|
||||
@import "./button-dark.css";
|
||||
|
||||
.button {
|
||||
@apply inline-flex items-center flex-shrink gap-1 justify-center p-4 font-semibold;
|
||||
letter-spacing: 0.0275rem;
|
||||
}
|
||||
|
||||
/* button SIZES */
|
||||
|
||||
.button--default {
|
||||
padding: theme(padding.2) theme(padding.4);
|
||||
height: theme(height.9);
|
||||
border-radius: theme(borderRadius.DEFAULT);
|
||||
|
||||
&:has(> .button__icon--start):has(> .button__label) {
|
||||
padding-left: theme(padding[2.5]);
|
||||
}
|
||||
|
||||
&:has(> .button__icon--end):has(> .button__label) {
|
||||
padding-right: theme(padding[2.5]);
|
||||
}
|
||||
}
|
||||
|
||||
.button--small {
|
||||
padding: theme(padding[1.5]) theme(padding[3]);
|
||||
height: theme(height.8);
|
||||
border-radius: 3px;
|
||||
|
||||
&:has(> .button__icon--start):has(> .button__label) {
|
||||
padding-left: theme(padding.2);
|
||||
}
|
||||
|
||||
&:has(> .button__label):has(> .button__icon--end) {
|
||||
padding-right: theme(padding.2);
|
||||
}
|
||||
}
|
||||
|
||||
/* button group */
|
||||
|
||||
.button-group .button:first-child {
|
||||
border-top-right-radius: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
}
|
||||
|
||||
.button-group .button:first-child {
|
||||
border-top-right-radius: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
}
|
||||
|
||||
.button-group .button:last-child {
|
||||
border-top-left-radius: 0;
|
||||
border-bottom-left-radius: 0;
|
||||
}
|
||||
@@ -1,6 +1,8 @@
|
||||
import { splitProps, type JSX } from "solid-js";
|
||||
import cx from "classnames";
|
||||
import { Typography } from "../Typography";
|
||||
//import './css/index.css'
|
||||
import "./css/index.css";
|
||||
|
||||
type Variants = "dark" | "light" | "ghost";
|
||||
type Size = "default" | "s";
|
||||
@@ -9,50 +11,31 @@ const variantColors: (
|
||||
disabled: boolean | undefined,
|
||||
) => Record<Variants, string> = (disabled) => ({
|
||||
dark: cx(
|
||||
"border border-solid",
|
||||
"border-secondary-950 bg-primary-900 text-white",
|
||||
"shadow-inner-primary",
|
||||
// Hover state
|
||||
// Focus state
|
||||
// Active state
|
||||
!disabled && "hover:border-secondary-900 hover:bg-secondary-700",
|
||||
!disabled && "focus:border-secondary-900",
|
||||
!disabled &&
|
||||
"active:border-secondary-900 active:shadow-inner-primary-active",
|
||||
"button--dark",
|
||||
!disabled && "button--dark-hover", // Hover state
|
||||
!disabled && "button--dark-focus", // Focus state
|
||||
!disabled && "button--dark-active", // Active state
|
||||
// Disabled
|
||||
"disabled:bg-secondary-200 disabled:text-secondary-700 disabled:border-secondary-300",
|
||||
),
|
||||
light: cx(
|
||||
"border border-solid",
|
||||
"border-secondary-800 bg-secondary-100 text-secondary-800",
|
||||
"shadow-inner-secondary",
|
||||
// Hover state
|
||||
// Focus state
|
||||
// Active state
|
||||
!disabled && "hover:bg-secondary-200 hover:text-secondary-900",
|
||||
!disabled && "focus:bg-secondary-200 focus:text-secondary-900",
|
||||
!disabled &&
|
||||
"active:bg-secondary-200 active:text-secondary-950 active:shadow-inner-secondary-active",
|
||||
// Disabled
|
||||
"disabled:bg-secondary-50 disabled:text-secondary-200 disabled:border-secondary-700",
|
||||
"button--light",
|
||||
|
||||
!disabled && "button--light-hover", // Hover state
|
||||
!disabled && "button--light-focus", // Focus state
|
||||
!disabled && "button--light-active", // Active state
|
||||
),
|
||||
ghost: cx(
|
||||
// "shadow-inner-secondary",
|
||||
// Hover state
|
||||
// Focus state
|
||||
// Active state
|
||||
!disabled && "hover:bg-secondary-200 hover:text-secondary-900",
|
||||
!disabled && "focus:bg-secondary-200 focus:text-secondary-900",
|
||||
!disabled &&
|
||||
"active:bg-secondary-200 active:text-secondary-950 active:shadow-inner-secondary-active",
|
||||
// Disabled
|
||||
"disabled:bg-secondary-50 disabled:text-secondary-200 disabled:border-secondary-700",
|
||||
!disabled && "hover:bg-secondary-200 hover:text-secondary-900", // Hover state
|
||||
!disabled && "focus:bg-secondary-200 focus:text-secondary-900", // Focus state
|
||||
!disabled && "button--light-active", // Active state
|
||||
),
|
||||
});
|
||||
|
||||
const sizePaddings: Record<Size, string> = {
|
||||
default: cx("rounded-[0.1875rem] px-4 py-2"),
|
||||
s: cx("rounded-sm py-[0.375rem] px-3"),
|
||||
default: cx("button--default"),
|
||||
s: cx("button button--small"), //cx("rounded-sm py-[0.375rem] px-3"),
|
||||
};
|
||||
|
||||
const sizeFont: Record<Size, string> = {
|
||||
@@ -77,26 +60,38 @@ export const Button = (props: ButtonProps) => {
|
||||
"endIcon",
|
||||
"class",
|
||||
]);
|
||||
|
||||
const buttonInvertion = (variant: Variants) => {
|
||||
return !(!variant || variant === "ghost" || variant === "light");
|
||||
};
|
||||
|
||||
return (
|
||||
<button
|
||||
class={cx(
|
||||
local.class,
|
||||
// Layout
|
||||
"inline-flex items-center flex-shrink gap-2 justify-center",
|
||||
// Styles
|
||||
"p-4",
|
||||
sizePaddings[local.size || "default"],
|
||||
// Colors
|
||||
variantColors(props.disabled)[local.variant || "dark"],
|
||||
//Font
|
||||
"leading-none font-semibold",
|
||||
sizeFont[local.size || "default"],
|
||||
"button", // default button class
|
||||
variantColors(props.disabled)[local.variant || "dark"], // button appereance
|
||||
sizePaddings[local.size || "default"], // button size
|
||||
)}
|
||||
{...other}
|
||||
>
|
||||
{local.startIcon && <span class="h-4">{local.startIcon}</span>}
|
||||
{local.children && <span>{local.children}</span>}
|
||||
{local.endIcon && <span class="h-4">{local.endIcon}</span>}
|
||||
{local.startIcon && (
|
||||
<span class="button__icon--start">{local.startIcon}</span>
|
||||
)}
|
||||
{local.children && (
|
||||
<Typography
|
||||
class="button__label"
|
||||
hierarchy="label"
|
||||
size={local.size || "default"}
|
||||
color="inherit"
|
||||
inverted={buttonInvertion(local.variant || "dark")}
|
||||
weight="medium"
|
||||
tag="span"
|
||||
>
|
||||
{local.children}
|
||||
</Typography>
|
||||
)}
|
||||
{local.endIcon && <span class="button__icon--end">{local.endIcon}</span>}
|
||||
</button>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -77,6 +77,7 @@ export type IconVariant = keyof typeof icons;
|
||||
|
||||
interface IconProps extends JSX.SvgSVGAttributes<SVGElement> {
|
||||
icon: IconVariant;
|
||||
size?: number;
|
||||
}
|
||||
|
||||
const Icon: Component<IconProps> = (props) => {
|
||||
@@ -85,8 +86,8 @@ const Icon: Component<IconProps> = (props) => {
|
||||
const IconComponent = icons[local.icon];
|
||||
return IconComponent ? (
|
||||
<IconComponent
|
||||
width={16}
|
||||
height={16}
|
||||
width={iconProps.size || 16}
|
||||
height={iconProps.size || 16}
|
||||
viewBox="0 0 48 48"
|
||||
// @ts-expect-error: dont know, fix this type nit later
|
||||
ref={iconProps.ref}
|
||||
|
||||
@@ -11,11 +11,13 @@
|
||||
font-weight: 400;
|
||||
src: url(../.fonts/ArchivoSemiCondensed-Regular.woff2) format("woff2");
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Archivo";
|
||||
font-weight: 500;
|
||||
src: url(../.fonts/ArchivoSemiCondensed-Medium.woff2) format("woff2");
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Archivo";
|
||||
font-weight: 600;
|
||||
@@ -30,7 +32,7 @@
|
||||
|
||||
:root {
|
||||
--clr-bg-def-1: theme(colors.white);
|
||||
--clr-bg-def-2: theme(colors.secondary.50);
|
||||
--clr-bg-def-2: theme(colors.primary.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);
|
||||
@@ -72,6 +74,15 @@ html {
|
||||
@apply font-sans;
|
||||
overflow-x: hidden;
|
||||
overflow-y: hidden;
|
||||
|
||||
-webkit-user-select: none;
|
||||
/* Safari */
|
||||
-moz-user-select: none;
|
||||
/* Firefox */
|
||||
-ms-user-select: none;
|
||||
/* Internet Explorer/Edge */
|
||||
user-select: none;
|
||||
/* Standard */
|
||||
}
|
||||
|
||||
.accordeon {
|
||||
@@ -81,7 +92,7 @@ html {
|
||||
}
|
||||
|
||||
.accordeon__header {
|
||||
padding: theme(padding.2) theme(padding[1.5]);
|
||||
padding: theme(padding.2) theme(padding[1.5]) theme(padding.1);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
@@ -90,5 +101,4 @@ html {
|
||||
}
|
||||
|
||||
.accordeon__body {
|
||||
padding: theme(padding.2) 0 theme(padding.1);
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ interface HeaderProps {
|
||||
}
|
||||
export const Header = (props: HeaderProps) => {
|
||||
return (
|
||||
<div class="flex border-b px-6 py-4 border-def-3">
|
||||
<div class="sticky top-0 z-20 flex items-center border-b bg-white/80 px-6 py-4 backdrop-blur-md border-def-3">
|
||||
<div class="flex-none">
|
||||
{props.showBack && <BackButton />}
|
||||
<span class=" lg:hidden" data-tip="Menu">
|
||||
|
||||
@@ -17,19 +17,11 @@ export const Layout: Component<RouteSectionProps> = (props) => {
|
||||
|
||||
return (
|
||||
<div class="h-screen w-full p-4 bg-def-2">
|
||||
<div class="h-full flex">
|
||||
<div
|
||||
class="z-40 h-full overflow-hidden"
|
||||
classList={{
|
||||
hidden:
|
||||
props.location.pathname === "welcome" || clanList().length === 0,
|
||||
}}
|
||||
>
|
||||
<Sidebar {...props} />
|
||||
</div>
|
||||
<div class="w-full my-2 ml-8 overflow-x-hidden overflow-y-scroll rounded-lg border bg-def-1 border-def-3">
|
||||
<div class="flex size-full flex-row-reverse">
|
||||
<div class="my-2 ml-8 flex-1 overflow-x-hidden overflow-y-scroll rounded-lg border bg-def-1 border-def-3">
|
||||
{props.children}
|
||||
</div>
|
||||
<Sidebar {...props} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -11,6 +11,9 @@ import { Match, Switch } from "solid-js";
|
||||
import toast from "solid-toast";
|
||||
import { MachineAvatar } from "./avatar";
|
||||
import { DynForm } from "@/src/Form/form";
|
||||
import { Typography } from "@/src/components/Typography";
|
||||
import Fieldset from "@/src/Form/fieldset";
|
||||
import Accordion from "@/src/components/accordion";
|
||||
|
||||
type CreateMachineForm = OperationArgs<"create_machine">;
|
||||
|
||||
@@ -72,44 +75,80 @@ export function CreateMachine() {
|
||||
<>
|
||||
<Header title="Create Machine" />
|
||||
<div class="flex w-full p-4">
|
||||
<div class="mt-4 w-full self-stretch px-2">
|
||||
<Form onSubmit={handleSubmit} class="gap-2 flex flex-col">
|
||||
<div class="mt-4 w-full self-stretch px-8">
|
||||
<Form
|
||||
onSubmit={handleSubmit}
|
||||
class="mx-auto flex w-full max-w-2xl flex-col gap-y-6"
|
||||
>
|
||||
<Field
|
||||
name="opts.machine.name"
|
||||
validate={[required("This field is required")]}
|
||||
>
|
||||
{(field, props) => (
|
||||
<>
|
||||
<div class="flex justify-center mb-4 pb-4 border-b">
|
||||
<div class="mb-4 flex justify-center border-b pb-4">
|
||||
<MachineAvatar name={field.value} />
|
||||
</div>
|
||||
|
||||
<TextInput
|
||||
inputProps={props}
|
||||
value={`${field.value}`}
|
||||
label={"name"}
|
||||
error={field.error}
|
||||
required
|
||||
placeholder="New_machine"
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</Field>
|
||||
<Field name="opts.machine.description">
|
||||
{(field, props) => (
|
||||
<TextInput
|
||||
inputProps={props}
|
||||
value={`${field.value}`}
|
||||
label={"description"}
|
||||
error={field.error}
|
||||
placeholder="My awesome machine"
|
||||
/>
|
||||
)}
|
||||
</Field>
|
||||
<div class=" " tabindex="0">
|
||||
<input type="checkbox" />
|
||||
<div class=" font-medium ">Deployment Settings</div>
|
||||
<div class="">
|
||||
<Fieldset legend="General">
|
||||
<Field
|
||||
name="opts.machine.name"
|
||||
validate={[required("This field is required")]}
|
||||
>
|
||||
{(field, props) => (
|
||||
<>
|
||||
<TextInput
|
||||
inputProps={props}
|
||||
value={`${field.value}`}
|
||||
label={"name"}
|
||||
error={field.error}
|
||||
required
|
||||
placeholder="New_machine"
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</Field>
|
||||
<Field name="opts.machine.description">
|
||||
{(field, props) => (
|
||||
<TextInput
|
||||
inputProps={props}
|
||||
value={`${field.value}`}
|
||||
label={"description"}
|
||||
error={field.error}
|
||||
placeholder="My awesome machine"
|
||||
/>
|
||||
)}
|
||||
</Field>
|
||||
</Fieldset>
|
||||
|
||||
<Fieldset legend="Tags">
|
||||
<Field name="opts.machine.tags" type="string[]">
|
||||
{(field, props) => (
|
||||
<div class="p-2">
|
||||
<DynForm
|
||||
initialValues={{ tags: ["all"] }}
|
||||
schema={{
|
||||
type: "object",
|
||||
properties: {
|
||||
tags: {
|
||||
type: "array",
|
||||
items: {
|
||||
title: "Tag",
|
||||
type: "string",
|
||||
},
|
||||
uniqueItems: true,
|
||||
},
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</Field>
|
||||
</Fieldset>
|
||||
<Accordion title="Advanced">
|
||||
<Fieldset>
|
||||
<Field name="opts.machine.deploy.targetHost">
|
||||
{(field, props) => (
|
||||
<>
|
||||
@@ -123,9 +162,10 @@ export function CreateMachine() {
|
||||
</>
|
||||
)}
|
||||
</Field>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-12 flex justify-end">
|
||||
</Fieldset>
|
||||
</Accordion>
|
||||
|
||||
<footer class="flex justify-end gap-y-3 border-t border-secondary-200 pt-5">
|
||||
<Button
|
||||
type="submit"
|
||||
disabled={formStore.submitting}
|
||||
@@ -141,7 +181,7 @@ export function CreateMachine() {
|
||||
<Match when={!formStore.submitting}>Create</Match>
|
||||
</Switch>
|
||||
</Button>
|
||||
</div>
|
||||
</footer>
|
||||
</Form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -82,37 +82,31 @@ export const MachineListView: Component = () => {
|
||||
size="s"
|
||||
onClick={() => refresh()}
|
||||
startIcon={<Icon icon="Update" />}
|
||||
></Button>
|
||||
/>
|
||||
</span>
|
||||
|
||||
<div class="border border-def-3">
|
||||
<span class="" data-tip="List View">
|
||||
<Button
|
||||
onclick={() => setView("list")}
|
||||
variant={view() == "list" ? "dark" : "light"}
|
||||
size="s"
|
||||
startIcon={<Icon icon="List" />}
|
||||
></Button>
|
||||
</span>
|
||||
<span class="" data-tip="Grid View">
|
||||
<Button
|
||||
onclick={() => setView("grid")}
|
||||
variant={view() == "grid" ? "dark" : "light"}
|
||||
size="s"
|
||||
startIcon={<Icon icon="Grid" />}
|
||||
></Button>
|
||||
</span>
|
||||
</div>
|
||||
<span class="" data-tip="New Machine">
|
||||
<div class="button-group">
|
||||
<Button
|
||||
onClick={() => navigate("create")}
|
||||
onclick={() => setView("list")}
|
||||
variant={view() == "list" ? "dark" : "light"}
|
||||
size="s"
|
||||
variant="light"
|
||||
startIcon={<Icon icon="Plus" />}
|
||||
>
|
||||
New Machine
|
||||
</Button>
|
||||
</span>
|
||||
startIcon={<Icon icon="List" />}
|
||||
/>
|
||||
<Button
|
||||
onclick={() => setView("grid")}
|
||||
variant={view() == "grid" ? "dark" : "light"}
|
||||
size="s"
|
||||
startIcon={<Icon icon="Grid" />}
|
||||
/>
|
||||
</div>
|
||||
<Button
|
||||
onClick={() => navigate("create")}
|
||||
size="s"
|
||||
variant="light"
|
||||
startIcon={<Icon size={14} icon="Plus" />}
|
||||
>
|
||||
New Machine
|
||||
</Button>
|
||||
</>
|
||||
}
|
||||
/>
|
||||
|
||||
@@ -19,9 +19,9 @@ interface CategoryProps {
|
||||
}
|
||||
const Categories = (props: CategoryProps) => {
|
||||
return (
|
||||
<span class="ml-6 inline-flex h-full align-middle">
|
||||
<span class="inline-flex h-full align-middle">
|
||||
{props.categories.map((category) => (
|
||||
<span class="">{category}</span>
|
||||
<span class="text-sm font-normal">{category}</span>
|
||||
))}
|
||||
</span>
|
||||
);
|
||||
@@ -32,10 +32,10 @@ interface RolesProps {
|
||||
}
|
||||
const Roles = (props: RolesProps) => {
|
||||
return (
|
||||
<div>
|
||||
<div class="flex flex-wrap items-center gap-2">
|
||||
<span>
|
||||
<Typography hierarchy="body" size="xs">
|
||||
Service Typography{" "}
|
||||
Service
|
||||
</Typography>
|
||||
</span>
|
||||
{props.roles.map((role) => (
|
||||
@@ -54,9 +54,14 @@ const ModuleItem = (props: {
|
||||
const navigate = useNavigate();
|
||||
|
||||
return (
|
||||
<div class={cx("rounded-lg shadow-md", props.class)}>
|
||||
<div class="text-primary-800">
|
||||
<div class="">
|
||||
<div
|
||||
class={cx(
|
||||
"col-span-1 flex flex-col gap-3 border-b border-secondary-200 pb-4",
|
||||
props.class,
|
||||
)}
|
||||
>
|
||||
{/* <div class="stat-figure text-primary-800">
|
||||
<div class="join">
|
||||
<Menu popoverid={`menu-${props.name}`} label={<Icon icon={"More"} />}>
|
||||
<ul class="z-[1] w-52 p-2 shadow">
|
||||
<li>
|
||||
@@ -71,20 +76,26 @@ const ModuleItem = (props: {
|
||||
</ul>
|
||||
</Menu>
|
||||
</div>
|
||||
</div>
|
||||
</div> */}
|
||||
|
||||
<A href={`/modules/details/${name}`}>
|
||||
<div class="underline">
|
||||
{name}
|
||||
<Categories categories={info.categories} />
|
||||
<header class="flex flex-col gap-4">
|
||||
<A href={`/modules/details/${name}`}>
|
||||
<div class="">
|
||||
<div class="flex flex-col">
|
||||
<Categories categories={info.categories} />
|
||||
<Typography hierarchy="title" size="m" weight="medium">
|
||||
{name}
|
||||
</Typography>
|
||||
</div>
|
||||
</div>
|
||||
</A>
|
||||
|
||||
<div class="w-full">
|
||||
<Typography hierarchy="body" size="xs">
|
||||
{info.description}
|
||||
</Typography>
|
||||
</div>
|
||||
</A>
|
||||
|
||||
<div class="w-full">
|
||||
<Typography hierarchy="body" size="default">
|
||||
{info.description}
|
||||
</Typography>
|
||||
</div>
|
||||
</header>
|
||||
<Roles roles={info.roles || []} />
|
||||
</div>
|
||||
);
|
||||
@@ -113,38 +124,33 @@ export const ModuleList = () => {
|
||||
title="Modules"
|
||||
toolbar={
|
||||
<>
|
||||
<span class="" data-tip="Reload">
|
||||
<Button
|
||||
variant="light"
|
||||
size="s"
|
||||
onClick={() => refresh()}
|
||||
startIcon={<Icon icon="Update" />}
|
||||
></Button>
|
||||
</span>
|
||||
<Button
|
||||
variant="light"
|
||||
size="s"
|
||||
onClick={() => refresh()}
|
||||
startIcon={<Icon icon="Update" />}
|
||||
/>
|
||||
|
||||
<div class="border border-def-3">
|
||||
<span class="" data-tip="List View">
|
||||
<Button
|
||||
onclick={() => setView("list")}
|
||||
variant={view() == "list" ? "dark" : "light"}
|
||||
size="s"
|
||||
startIcon={<Icon icon="List" />}
|
||||
></Button>
|
||||
</span>
|
||||
<span class="" data-tip="Grid View">
|
||||
<Button
|
||||
onclick={() => setView("grid")}
|
||||
variant={view() == "grid" ? "dark" : "light"}
|
||||
size="s"
|
||||
startIcon={<Icon icon="Grid" />}
|
||||
></Button>
|
||||
</span>
|
||||
<div class="button-group">
|
||||
<Button
|
||||
onclick={() => setView("list")}
|
||||
variant={view() == "list" ? "dark" : "light"}
|
||||
size="s"
|
||||
startIcon={<Icon icon="List" />}
|
||||
/>
|
||||
|
||||
<Button
|
||||
onclick={() => setView("grid")}
|
||||
variant={view() == "grid" ? "dark" : "light"}
|
||||
size="s"
|
||||
startIcon={<Icon icon="Grid" />}
|
||||
/>
|
||||
</div>
|
||||
<span class="" data-tip="New Machine">
|
||||
<Button
|
||||
size="s"
|
||||
variant="light"
|
||||
startIcon={<Icon icon="CaretUp" />}
|
||||
startIcon={<Icon size={14} icon="CaretUp" />}
|
||||
>
|
||||
Import Module
|
||||
</Button>
|
||||
@@ -156,10 +162,10 @@ export const ModuleList = () => {
|
||||
<Match when={modulesQuery.isFetching}>Loading....</Match>
|
||||
<Match when={modulesQuery.data}>
|
||||
<div
|
||||
class="my-4 flex flex-wrap gap-6 px-3 py-2"
|
||||
class="grid gap-6 p-6"
|
||||
classList={{
|
||||
"flex-col": view() === "list",
|
||||
"": view() === "grid",
|
||||
"grid-cols-1": view() === "list",
|
||||
"grid-cols-2": view() === "grid",
|
||||
}}
|
||||
>
|
||||
<For each={modulesQuery.data}>
|
||||
|
||||
6
pkgs/webview-ui/app/stylelint.config.js
Normal file
6
pkgs/webview-ui/app/stylelint.config.js
Normal file
@@ -0,0 +1,6 @@
|
||||
module.exports = {
|
||||
extends: ["stylelint-config-standard", "stylelint-config-tailwindcss"],
|
||||
rules: {
|
||||
// You can adjust rules here
|
||||
},
|
||||
};
|
||||
@@ -284,7 +284,7 @@ export default plugin.withOptions(
|
||||
"inner-primary":
|
||||
"2px 2px 0px 0px var(--clr-bg-inv-acc-3, #415E63) inset",
|
||||
"inner-primary-active":
|
||||
"0px 0px 0px 1px #FFF, 0px 0px 0px 2px var(--clr-bg-inv-acc-4, #203637), -2px -2px 0px 0px var(--clr-bg-inv-acc-1, #7B9B9F) inset",
|
||||
"0px 0px 0px 1px #FFF, 0px 0px 0px 2px var(--clr-bg-inv-acc-4, #203637)",
|
||||
"inner-secondary":
|
||||
"-2px -2px 0px 0px #CEDFE2 inset, 2px 2px 0px 0px white inset",
|
||||
"inner-secondary-active":
|
||||
|
||||
Reference in New Issue
Block a user