form components: adds general fieldset component and accordion component

This commit is contained in:
Timo
2025-04-30 08:48:29 +02:00
committed by Johannes Kirschbauer
parent 5b0334adda
commit 9bc23690a3
6 changed files with 157 additions and 36 deletions

View File

@@ -0,0 +1,32 @@
import { JSX } from "solid-js";
import { Typography } from "@/src/components/Typography";
type 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 p-5 border border-secondary-200 bg-secondary-50 rounded-md">
{props.children}
</div>
</fieldset>
);
}

View File

@@ -0,0 +1,51 @@
import { createSignal, JSX, Show } from "solid-js";
import Icon from "../icon";
import { Typography } from "../Typography";
import { Button } from "../button";
type 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={`accordion flex flex-col gap-y-5 ${props.class ?? ""}`}
tabindex="0"
>
<div
onClick={() => setIsOpen(!isOpen())}
class="accordion__title h-5 flex gap-x-0.5 items-center justify-end font-medium cursor-pointer px-1"
>
<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

@@ -31,8 +31,8 @@
padding-left: theme(padding.2); padding-left: theme(padding.2);
} }
&:has(> .button__icon--end):has(> .button__label) { &:has(> .button__label):has(> .button__icon--end) {
padding-left: theme(padding.2); padding-right: theme(padding.2);
} }
} }

View File

@@ -94,9 +94,7 @@ export const Button = (props: ButtonProps) => {
{local.children} {local.children}
</Typography> </Typography>
)} )}
{local.endIcon && ( {local.endIcon && <span class="button__icon--end">{local.endIcon}</span>}
<span class="button__icon--start">{local.endIcon}</span>
)}
</button> </button>
); );
}; };

View File

@@ -9,7 +9,7 @@ interface HeaderProps {
} }
export const Header = (props: HeaderProps) => { export const Header = (props: HeaderProps) => {
return ( return (
<div class="flex border-b px-6 py-4 border-def-3"> <div class="sticky top-0 z-20 navbar border-b px-6 py-4 border-def-3 bg-white bg-opacity-80 backdrop-blur-md">
<div class="flex-none"> <div class="flex-none">
{props.showBack && <BackButton />} {props.showBack && <BackButton />}
<span class=" lg:hidden" data-tip="Menu"> <span class=" lg:hidden" data-tip="Menu">

View File

@@ -11,6 +11,9 @@ import { Match, Switch } from "solid-js";
import toast from "solid-toast"; import toast from "solid-toast";
import { MachineAvatar } from "./avatar"; import { MachineAvatar } from "./avatar";
import { DynForm } from "@/src/Form/form"; 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">; type CreateMachineForm = OperationArgs<"create_machine">;
@@ -72,8 +75,11 @@ export function CreateMachine() {
<> <>
<Header title="Create Machine" /> <Header title="Create Machine" />
<div class="flex w-full p-4"> <div class="flex w-full p-4">
<div class="mt-4 w-full self-stretch px-2"> <div class="mt-4 w-full self-stretch px-8">
<Form onSubmit={handleSubmit} class="gap-2 flex flex-col"> <Form
onSubmit={handleSubmit}
class="mx-auto w-full max-w-2xl flex flex-col gap-y-6"
>
<Field <Field
name="opts.machine.name" name="opts.machine.name"
validate={[required("This field is required")]} validate={[required("This field is required")]}
@@ -83,33 +89,66 @@ export function CreateMachine() {
<div class="flex justify-center mb-4 pb-4 border-b"> <div class="flex justify-center mb-4 pb-4 border-b">
<MachineAvatar name={field.value} /> <MachineAvatar name={field.value} />
</div> </div>
<TextInput
inputProps={props}
value={`${field.value}`}
label={"name"}
error={field.error}
required
placeholder="New_machine"
/>
</> </>
)} )}
</Field> </Field>
<Field name="opts.machine.description"> <Fieldset legend="General">
{(field, props) => ( <Field
<TextInput name="opts.machine.name"
inputProps={props} validate={[required("This field is required")]}
value={`${field.value}`} >
label={"description"} {(field, props) => (
error={field.error} <>
placeholder="My awesome machine" <TextInput
/> inputProps={props}
)} value={`${field.value}`}
</Field> label={"name"}
<div class=" " tabindex="0"> error={field.error}
<input type="checkbox" /> required
<div class=" font-medium ">Deployment Settings</div> placeholder="New_machine"
<div class=""> />
</>
)}
</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 name="opts.machine.deploy.targetHost">
{(field, props) => ( {(field, props) => (
<> <>
@@ -123,9 +162,10 @@ export function CreateMachine() {
</> </>
)} )}
</Field> </Field>
</div> </Fieldset>
</div> </Accordion>
<div class="mt-12 flex justify-end">
<footer class="gap-y-3 pt-5 border-t border-secondary-200 flex justify-end">
<Button <Button
type="submit" type="submit"
disabled={formStore.submitting} disabled={formStore.submitting}
@@ -141,7 +181,7 @@ export function CreateMachine() {
<Match when={!formStore.submitting}>Create</Match> <Match when={!formStore.submitting}>Create</Match>
</Switch> </Switch>
</Button> </Button>
</div> </footer>
</Form> </Form>
</div> </div>
</div> </div>