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:
hsjobeki
2025-04-30 10:18:42 +00:00
25 changed files with 487 additions and 216 deletions

3
.gitignore vendored
View File

@@ -16,6 +16,9 @@ nixos.qcow2
/docs/out
**/.local.env
# MacOS stuff
**/.DS_store
# dream2nix
.dream2nix

View File

@@ -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;

View 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>
);
}

View File

@@ -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">
<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: (
<div class="flex w-full justify-end pb-2">
<Button
variant="ghost"
type="submit"
endIcon={<Icon icon={"Plus"} />}
endIcon={<Icon size={14} icon={"Plus"} />}
class="capitalize"
>
Add {itemsSchema().title}
</Button>
</div>
),
}}
// Add the new item to the FieldArray

View File

@@ -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"

View File

@@ -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}

View File

@@ -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 {

View File

@@ -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 {

View File

@@ -19,20 +19,22 @@ export const SidebarSection = (props: {
return (
<details class="sidebar__section accordeon" open>
<summary class="accordeon__header">
<summary style="display: contents;">
<div class="accordeon__header">
<Typography
class="inline-flex w-full gap-2 uppercase"
class="inline-flex w-full gap-2 uppercase !tracking-wider"
tag="p"
hierarchy="body"
size="xs"
size="xxs"
weight="normal"
color="tertiary"
inverted={true}
>
<Icon icon={props.icon} />
<Icon class="opacity-90" icon={props.icon} size={13} />
{title}
<Icon icon="CaretDown" class="ml-auto" />
<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

View File

@@ -17,7 +17,7 @@
}
.fnt-body-xxs {
font-size: 0.6875rem;
font-size: 0.75rem;
line-height: 132%;
letter-spacing: 0.00688rem;
}

View 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 {
}

View 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>
);
}

View File

@@ -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;
}

View File

@@ -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;
}
}

View 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;
}

View File

@@ -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>
);
};

View File

@@ -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}

View File

@@ -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);
}

View File

@@ -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">

View File

@@ -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>
);

View File

@@ -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,18 +75,30 @@ 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>
</>
)}
</Field>
<Fieldset legend="General">
<Field
name="opts.machine.name"
validate={[required("This field is required")]}
>
{(field, props) => (
<>
<TextInput
inputProps={props}
value={`${field.value}`}
@@ -106,10 +121,34 @@ export function CreateMachine() {
/>
)}
</Field>
<div class=" " tabindex="0">
<input type="checkbox" />
<div class=" font-medium ">Deployment Settings</div>
<div class="">
</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>

View File

@@ -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">
<div class="button-group">
<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">
<Button
onClick={() => navigate("create")}
size="s"
variant="light"
startIcon={<Icon icon="Plus" />}
startIcon={<Icon size={14} icon="Plus" />}
>
New Machine
</Button>
</span>
</>
}
/>

View File

@@ -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> */}
<header class="flex flex-col gap-4">
<A href={`/modules/details/${name}`}>
<div class="underline">
{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="default">
<Typography hierarchy="body" size="xs">
{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>
/>
<div class="border border-def-3">
<span class="" data-tip="List View">
<div class="button-group">
<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">
<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}>

View File

@@ -0,0 +1,6 @@
module.exports = {
extends: ["stylelint-config-standard", "stylelint-config-tailwindcss"],
rules: {
// You can adjust rules here
},
};

View File

@@ -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":