form components: adds general fieldset component and accordion component
This commit is contained in:
committed by
Johannes Kirschbauer
parent
5b0334adda
commit
9bc23690a3
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";
|
||||||
|
|
||||||
|
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>
|
||||||
|
);
|
||||||
|
}
|
||||||
51
pkgs/webview-ui/app/src/components/accordion/index.tsx
Normal file
51
pkgs/webview-ui/app/src/components/accordion/index.tsx
Normal 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>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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">
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
Reference in New Issue
Block a user