Merge pull request 'UI/chore: init typography xxs body' (#2627) from hsjobeki/clan-core:hsjobeki-main into main
This commit is contained in:
@@ -1,6 +1,8 @@
|
|||||||
import { createEffect, Show, type JSX } from "solid-js";
|
import { createEffect, Show, type JSX } from "solid-js";
|
||||||
import cx from "classnames";
|
import cx from "classnames";
|
||||||
import { Label } from "../base/label";
|
import { Label } from "../base/label";
|
||||||
|
import { InputBase, InputLabel } from "@/src/components/inputBase";
|
||||||
|
import { Typography } from "@/src/components/Typography";
|
||||||
|
|
||||||
interface TextInputProps {
|
interface TextInputProps {
|
||||||
value: string;
|
value: string;
|
||||||
@@ -22,49 +24,39 @@ interface TextInputProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function TextInput(props: TextInputProps) {
|
export function TextInput(props: TextInputProps) {
|
||||||
const value = () => props.value;
|
// createEffect(() => {
|
||||||
|
// console.log("TextInput", props.error, props.value);
|
||||||
|
// });
|
||||||
return (
|
return (
|
||||||
<label
|
<div
|
||||||
class={cx("form-control w-full", props.class)}
|
class="grid grid-cols-12"
|
||||||
aria-disabled={props.disabled}
|
classList={{
|
||||||
|
"mb-[14.5px]": !props.error,
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<div class="label">
|
<InputLabel
|
||||||
<Label label={props.label} required={props.required} />
|
class="col-span-2"
|
||||||
<span class="label-text-alt block">{props.altLabel}</span>
|
required={props.required}
|
||||||
</div>
|
error={!!props.error}
|
||||||
|
>
|
||||||
<div class="input input-bordered flex items-center gap-2">
|
{props.label}
|
||||||
<Show when={props.adornment && props.adornment.position === "start"}>
|
</InputLabel>
|
||||||
{props.adornment?.content}
|
<InputBase
|
||||||
</Show>
|
error={!!props.error}
|
||||||
{props.inlineLabel}
|
class="col-span-10"
|
||||||
<input
|
{...props.inputProps}
|
||||||
{...props.inputProps}
|
value={props.value}
|
||||||
value={value()}
|
/>
|
||||||
type={props.type ? props.type : "text"}
|
{props.error && (
|
||||||
class="grow"
|
<Typography
|
||||||
classList={{
|
hierarchy="body"
|
||||||
"input-disabled": props.disabled,
|
size="xxs"
|
||||||
}}
|
weight="medium"
|
||||||
placeholder={`${props.placeholder || props.label}`}
|
class="col-span-full px-1 !fg-semantic-4"
|
||||||
required
|
>
|
||||||
disabled={props.disabled}
|
{props.error}
|
||||||
/>
|
</Typography>
|
||||||
<Show when={props.adornment && props.adornment.position === "end"}>
|
)}
|
||||||
{props.adornment?.content}
|
</div>
|
||||||
</Show>
|
|
||||||
</div>
|
|
||||||
<div class="label">
|
|
||||||
{props.helperText && (
|
|
||||||
<span class="label-text text-neutral">{props.helperText}</span>
|
|
||||||
)}
|
|
||||||
{props.error && (
|
|
||||||
<span class="label-text-alt font-bold text-error-700">
|
|
||||||
{props.error}
|
|
||||||
</span>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</label>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import {
|
|||||||
getValues,
|
getValues,
|
||||||
SubmitHandler,
|
SubmitHandler,
|
||||||
} from "@modular-forms/solid";
|
} from "@modular-forms/solid";
|
||||||
import { TextInput } from "./components/TextInput";
|
import { TextInput } from "@/src/Form/fields/TextInput";
|
||||||
import { Button } from "./components/button";
|
import { Button } from "./components/button";
|
||||||
import { callApi } from "./api";
|
import { callApi } from "./api";
|
||||||
import { API } from "@/api/API";
|
import { API } from "@/api/API";
|
||||||
@@ -68,7 +68,6 @@ export const ApiTester = () => {
|
|||||||
label={"endpoint"}
|
label={"endpoint"}
|
||||||
value={field.value || ""}
|
value={field.value || ""}
|
||||||
inputProps={fieldProps}
|
inputProps={fieldProps}
|
||||||
formStore={formStore}
|
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</Field>
|
</Field>
|
||||||
@@ -78,7 +77,6 @@ export const ApiTester = () => {
|
|||||||
label={"payload"}
|
label={"payload"}
|
||||||
value={field.value || ""}
|
value={field.value || ""}
|
||||||
inputProps={fieldProps}
|
inputProps={fieldProps}
|
||||||
formStore={formStore}
|
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</Field>
|
</Field>
|
||||||
|
|||||||
@@ -1,72 +0,0 @@
|
|||||||
import { FieldValues, FormStore, ResponseData } from "@modular-forms/solid";
|
|
||||||
import { Show, type JSX } from "solid-js";
|
|
||||||
import cx from "classnames";
|
|
||||||
|
|
||||||
interface TextInputProps<T extends FieldValues, R extends ResponseData> {
|
|
||||||
formStore: FormStore<T, R>;
|
|
||||||
value: string;
|
|
||||||
inputProps: JSX.InputHTMLAttributes<HTMLInputElement>;
|
|
||||||
label: JSX.Element;
|
|
||||||
error?: string;
|
|
||||||
required?: boolean;
|
|
||||||
type?: string;
|
|
||||||
inlineLabel?: JSX.Element;
|
|
||||||
class?: string;
|
|
||||||
adornment?: {
|
|
||||||
position: "start" | "end";
|
|
||||||
content: JSX.Element;
|
|
||||||
};
|
|
||||||
placeholder?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function TextInput<T extends FieldValues, R extends ResponseData>(
|
|
||||||
props: TextInputProps<T, R>,
|
|
||||||
) {
|
|
||||||
const value = () => props.value;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<label
|
|
||||||
class={cx("form-control w-full", props.class)}
|
|
||||||
aria-disabled={props.formStore.submitting}
|
|
||||||
>
|
|
||||||
<div class="label">
|
|
||||||
<span
|
|
||||||
class="label-text block"
|
|
||||||
classList={{
|
|
||||||
"after:ml-0.5 after:text-primary after:content-['*']":
|
|
||||||
props.required,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{props.label}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="input input-bordered flex items-center gap-2">
|
|
||||||
<Show when={props.adornment && props.adornment.position === "start"}>
|
|
||||||
{props.adornment?.content}
|
|
||||||
</Show>
|
|
||||||
{props.inlineLabel}
|
|
||||||
<input
|
|
||||||
{...props.inputProps}
|
|
||||||
value={value()}
|
|
||||||
type={props.type ? props.type : "text"}
|
|
||||||
class="grow"
|
|
||||||
classList={{
|
|
||||||
"input-disabled": props.formStore.submitting,
|
|
||||||
}}
|
|
||||||
placeholder={`${props.placeholder || props.label}`}
|
|
||||||
required
|
|
||||||
disabled={props.formStore.submitting}
|
|
||||||
/>
|
|
||||||
<Show when={props.adornment && props.adornment.position === "end"}>
|
|
||||||
{props.adornment?.content}
|
|
||||||
</Show>
|
|
||||||
</div>
|
|
||||||
{props.error && (
|
|
||||||
<span class="label-text-alt font-bold text-error-700">
|
|
||||||
{props.error}
|
|
||||||
</span>
|
|
||||||
)}
|
|
||||||
</label>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -15,3 +15,9 @@
|
|||||||
line-height: 132%;
|
line-height: 132%;
|
||||||
letter-spacing: 3%;
|
letter-spacing: 3%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.fnt-body-xxs {
|
||||||
|
font-size: 0.6875rem;
|
||||||
|
line-height: 132%;
|
||||||
|
letter-spacing: 0.00688rem;
|
||||||
|
}
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ interface SizeForHierarchy {
|
|||||||
body: {
|
body: {
|
||||||
default: string;
|
default: string;
|
||||||
xs: string;
|
xs: string;
|
||||||
|
xxs: string;
|
||||||
s: string;
|
s: string;
|
||||||
};
|
};
|
||||||
headline: {
|
headline: {
|
||||||
@@ -43,10 +44,9 @@ type AllowedSizes<H extends Hierarchy> = keyof SizeForHierarchy[H];
|
|||||||
const sizeHierarchyMap: SizeForHierarchy = {
|
const sizeHierarchyMap: SizeForHierarchy = {
|
||||||
body: {
|
body: {
|
||||||
default: cx("fnt-body-default"),
|
default: cx("fnt-body-default"),
|
||||||
xs: cx("fnt-body-xs"),
|
|
||||||
s: cx("fnt-body-s"),
|
s: cx("fnt-body-s"),
|
||||||
// m: cx("fnt-body-m"),
|
xs: cx("fnt-body-xs"),
|
||||||
// l: cx("fnt-body-l"),
|
xxs: cx("fnt-body-xxs"),
|
||||||
},
|
},
|
||||||
headline: {
|
headline: {
|
||||||
default: cx("fnt-headline-default"),
|
default: cx("fnt-headline-default"),
|
||||||
|
|||||||
146
pkgs/webview-ui/app/src/components/inputBase/index.tsx
Normal file
146
pkgs/webview-ui/app/src/components/inputBase/index.tsx
Normal file
@@ -0,0 +1,146 @@
|
|||||||
|
import cx from "classnames";
|
||||||
|
import { createEffect, JSX, splitProps } from "solid-js";
|
||||||
|
import Icon, { IconVariant } from "../icon";
|
||||||
|
import { Typography } from "../Typography";
|
||||||
|
|
||||||
|
type Variants = "outlined" | "ghost";
|
||||||
|
interface InputBaseProps {
|
||||||
|
variant?: Variants;
|
||||||
|
value?: string;
|
||||||
|
inputProps?: JSX.InputHTMLAttributes<HTMLInputElement>;
|
||||||
|
required?: boolean;
|
||||||
|
type?: string;
|
||||||
|
inlineLabel?: JSX.Element;
|
||||||
|
class?: string;
|
||||||
|
placeholder?: string;
|
||||||
|
disabled?: boolean;
|
||||||
|
readonly?: boolean;
|
||||||
|
error?: boolean;
|
||||||
|
icon?: IconVariant;
|
||||||
|
}
|
||||||
|
|
||||||
|
const variantBorder: Record<Variants, string> = {
|
||||||
|
outlined: "border border-inv-3",
|
||||||
|
ghost: "",
|
||||||
|
};
|
||||||
|
|
||||||
|
const fgStateClasses = cx("aria-disabled:fg-def-4 aria-readonly:fg-def-3");
|
||||||
|
|
||||||
|
export const InputBase = (props: InputBaseProps) => {
|
||||||
|
const [, inputProps] = splitProps(props, ["class"]);
|
||||||
|
|
||||||
|
createEffect(() => {
|
||||||
|
console.log("InputBase", props.value, props.variant);
|
||||||
|
});
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
class={cx(
|
||||||
|
// Layout
|
||||||
|
"flex px-2 py-[0.375rem] flex-shrink-0 items-center justify-center gap-2 text-sm leading-6",
|
||||||
|
|
||||||
|
// Background
|
||||||
|
"bg-def-1 hover:bg-acc-1",
|
||||||
|
|
||||||
|
// Text
|
||||||
|
"fg-def-1",
|
||||||
|
fgStateClasses,
|
||||||
|
// Border
|
||||||
|
variantBorder[props.variant || "outlined"],
|
||||||
|
"rounded-sm",
|
||||||
|
"hover:border-inv-4",
|
||||||
|
"aria-disabled:border-def-2 aria-disabled:border",
|
||||||
|
// Outline
|
||||||
|
"outline-offset-1 outline-1",
|
||||||
|
"active:outline active:outline-inv-3",
|
||||||
|
"focus-visible:outline-double focus-visible:outline-int-1",
|
||||||
|
|
||||||
|
// Cursor
|
||||||
|
"aria-readonly:cursor-no-drop",
|
||||||
|
props.class,
|
||||||
|
)}
|
||||||
|
classList={{
|
||||||
|
[cx("!border !border-semantic-1 !outline-semantic-1")]: !!props.error,
|
||||||
|
}}
|
||||||
|
aria-invalid={props.error}
|
||||||
|
aria-disabled={props.disabled}
|
||||||
|
aria-readonly={props.readonly}
|
||||||
|
tabIndex={0}
|
||||||
|
role="textbox"
|
||||||
|
>
|
||||||
|
{props.icon && (
|
||||||
|
<i
|
||||||
|
class={cx("inline-flex fg-def-2", fgStateClasses)}
|
||||||
|
aria-invalid={props.error}
|
||||||
|
aria-disabled={props.disabled}
|
||||||
|
aria-readonly={props.readonly}
|
||||||
|
>
|
||||||
|
<Icon icon={props.icon} font-size="inherit" color="inherit" />
|
||||||
|
</i>
|
||||||
|
)}
|
||||||
|
<input
|
||||||
|
tabIndex={-1}
|
||||||
|
class="w-full bg-transparent outline-none aria-readonly:cursor-no-drop"
|
||||||
|
value={props.value}
|
||||||
|
type={props.type ? props.type : "text"}
|
||||||
|
readOnly={props.readonly}
|
||||||
|
placeholder={`${props.placeholder || ""}`}
|
||||||
|
required={props.required}
|
||||||
|
disabled={props.disabled}
|
||||||
|
aria-invalid={props.error}
|
||||||
|
aria-disabled={props.disabled}
|
||||||
|
aria-readonly={props.readonly}
|
||||||
|
{...inputProps}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
interface InputLabelProps extends JSX.LabelHTMLAttributes<HTMLLabelElement> {
|
||||||
|
description?: string;
|
||||||
|
required?: boolean;
|
||||||
|
error?: boolean;
|
||||||
|
help?: string;
|
||||||
|
}
|
||||||
|
export const InputLabel = (props: InputLabelProps) => {
|
||||||
|
const [labelProps, forwardProps] = splitProps(props, ["class"]);
|
||||||
|
return (
|
||||||
|
<label
|
||||||
|
class={cx("flex items-center gap-1", labelProps.class)}
|
||||||
|
{...forwardProps}
|
||||||
|
>
|
||||||
|
<Typography
|
||||||
|
hierarchy="label"
|
||||||
|
size="default"
|
||||||
|
weight="bold"
|
||||||
|
class="!fg-def-1"
|
||||||
|
classList={{
|
||||||
|
[cx("!fg-semantic-1")]: !!props.error,
|
||||||
|
}}
|
||||||
|
aria-invalid={props.error}
|
||||||
|
>
|
||||||
|
{props.children}
|
||||||
|
{props.required && (
|
||||||
|
<span class="inline-flex px-1 align-bottom leading-[0.5] fg-def-3">
|
||||||
|
{"*"}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
{props.help && (
|
||||||
|
<span
|
||||||
|
class="tooltip tooltip-bottom inline px-2"
|
||||||
|
data-tip={props.help}
|
||||||
|
style={{
|
||||||
|
"--tooltip-color": "#EFFFFF",
|
||||||
|
"--tooltip-text-color": "#0D1416",
|
||||||
|
"--tooltip-tail": "0.8125rem",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Icon class="inline fg-def-3" icon={"Info"} width={"0.8125rem"} />
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</Typography>
|
||||||
|
<Typography hierarchy="body" size="xs" weight="normal" color="secondary">
|
||||||
|
{props.description}
|
||||||
|
</Typography>
|
||||||
|
</label>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -20,6 +20,7 @@ import { ModuleDetails } from "./routes/modules/details";
|
|||||||
import { ModuleDetails as AddModule } from "./routes/modules/add";
|
import { ModuleDetails as AddModule } from "./routes/modules/add";
|
||||||
import { ApiTester } from "./api_test";
|
import { ApiTester } from "./api_test";
|
||||||
import { IconVariant } from "./components/icon";
|
import { IconVariant } from "./components/icon";
|
||||||
|
import { Components } from "./routes/components";
|
||||||
|
|
||||||
export const client = new QueryClient();
|
export const client = new QueryClient();
|
||||||
|
|
||||||
@@ -157,6 +158,12 @@ export const routes: AppRoute[] = [
|
|||||||
hidden: false,
|
hidden: false,
|
||||||
component: () => <ApiTester />,
|
component: () => <ApiTester />,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: "/components",
|
||||||
|
label: "Components",
|
||||||
|
hidden: false,
|
||||||
|
component: () => <Components />,
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import {
|
|||||||
} from "@modular-forms/solid";
|
} from "@modular-forms/solid";
|
||||||
import toast from "solid-toast";
|
import toast from "solid-toast";
|
||||||
import { setActiveURI, setClanList } from "@/src/App";
|
import { setActiveURI, setClanList } from "@/src/App";
|
||||||
import { TextInput } from "@/src/components/TextInput";
|
import { TextInput } from "@/src/Form/fields/TextInput";
|
||||||
import { useNavigate } from "@solidjs/router";
|
import { useNavigate } from "@solidjs/router";
|
||||||
import { Button } from "@/src/components/button";
|
import { Button } from "@/src/components/button";
|
||||||
import Icon from "@/src/components/icon";
|
import Icon from "@/src/components/icon";
|
||||||
@@ -165,7 +165,6 @@ export const CreateClan = () => {
|
|||||||
),
|
),
|
||||||
position: "start",
|
position: "start",
|
||||||
}}
|
}}
|
||||||
formStore={formStore}
|
|
||||||
inputProps={props}
|
inputProps={props}
|
||||||
label="Template to use"
|
label="Template to use"
|
||||||
value={field.value ?? ""}
|
value={field.value ?? ""}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import {
|
|||||||
setValue,
|
setValue,
|
||||||
SubmitHandler,
|
SubmitHandler,
|
||||||
} from "@modular-forms/solid";
|
} from "@modular-forms/solid";
|
||||||
import { TextInput } from "@/src/components/TextInput";
|
import { TextInput } from "@/src/Form/fields/TextInput";
|
||||||
import toast from "solid-toast";
|
import toast from "solid-toast";
|
||||||
import { set_single_service } from "@/src/api/inventory";
|
import { set_single_service } from "@/src/api/inventory";
|
||||||
import { Button } from "@/src/components/button";
|
import { Button } from "@/src/components/button";
|
||||||
@@ -215,7 +215,6 @@ const AdminModuleForm = (props: AdminModuleFormProps) => {
|
|||||||
<Field name={`allowedKeys.${idx()}.name`}>
|
<Field name={`allowedKeys.${idx()}.name`}>
|
||||||
{(field, props) => (
|
{(field, props) => (
|
||||||
<TextInput
|
<TextInput
|
||||||
formStore={formStore}
|
|
||||||
inputProps={props}
|
inputProps={props}
|
||||||
label={"Name"}
|
label={"Name"}
|
||||||
adornment={{
|
adornment={{
|
||||||
@@ -235,7 +234,6 @@ const AdminModuleForm = (props: AdminModuleFormProps) => {
|
|||||||
{(field, props) => (
|
{(field, props) => (
|
||||||
<>
|
<>
|
||||||
<TextInput
|
<TextInput
|
||||||
formStore={formStore}
|
|
||||||
inputProps={props}
|
inputProps={props}
|
||||||
label={"Value"}
|
label={"Value"}
|
||||||
value={field.value ?? ""}
|
value={field.value ?? ""}
|
||||||
@@ -243,7 +241,10 @@ const AdminModuleForm = (props: AdminModuleFormProps) => {
|
|||||||
class="col-span-6"
|
class="col-span-6"
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
<span class="tooltip mt-auto" data-tip="Select file">
|
<span
|
||||||
|
class="tooltip col-span-12 mt-auto"
|
||||||
|
data-tip="Select file"
|
||||||
|
>
|
||||||
<label
|
<label
|
||||||
class={"form-control w-full"}
|
class={"form-control w-full"}
|
||||||
aria-disabled={formStore.submitting}
|
aria-disabled={formStore.submitting}
|
||||||
|
|||||||
118
pkgs/webview-ui/app/src/routes/components/index.tsx
Normal file
118
pkgs/webview-ui/app/src/routes/components/index.tsx
Normal file
@@ -0,0 +1,118 @@
|
|||||||
|
import { Button } from "@/src/components/button";
|
||||||
|
import { InputBase, InputLabel } from "@/src/components/inputBase";
|
||||||
|
import { TextInput } from "@/src/Form/fields";
|
||||||
|
import { Header } from "@/src/layout/header";
|
||||||
|
import { createForm, required } from "@modular-forms/solid";
|
||||||
|
|
||||||
|
const disabled = [false, true];
|
||||||
|
const readOnly = [false, true];
|
||||||
|
const error = [false, true];
|
||||||
|
|
||||||
|
export const Components = () => {
|
||||||
|
const [formStore, { Form, Field }] = createForm<{ ef: string }>({});
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Header title="Components" />
|
||||||
|
|
||||||
|
<div class="grid grid-cols-2 gap-4 p-4">
|
||||||
|
<span class="col-span-2">Input </span>
|
||||||
|
|
||||||
|
<span>Default</span>
|
||||||
|
<span>Size S</span>
|
||||||
|
|
||||||
|
{disabled.map((disabled) =>
|
||||||
|
readOnly.map((readOnly) =>
|
||||||
|
error.map((hasError) => (
|
||||||
|
<>
|
||||||
|
<span>
|
||||||
|
{[
|
||||||
|
disabled ? "Disabled" : "(default)",
|
||||||
|
readOnly ? "ReadOnly" : "",
|
||||||
|
hasError ? "Error" : "",
|
||||||
|
]
|
||||||
|
.filter(Boolean)
|
||||||
|
.join(" + ")}
|
||||||
|
</span>
|
||||||
|
<InputBase
|
||||||
|
variant="outlined"
|
||||||
|
value="The Fox jumps!"
|
||||||
|
disabled={disabled}
|
||||||
|
error={hasError}
|
||||||
|
readonly={readOnly}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
)}
|
||||||
|
<span class="col-span-2">Input Ghost</span>
|
||||||
|
{disabled.map((disabled) =>
|
||||||
|
readOnly.map((readOnly) =>
|
||||||
|
error.map((hasError) => (
|
||||||
|
<>
|
||||||
|
<span>
|
||||||
|
{[
|
||||||
|
disabled ? "Disabled" : "(default)",
|
||||||
|
readOnly ? "ReadOnly" : "",
|
||||||
|
hasError ? "Error" : "",
|
||||||
|
]
|
||||||
|
.filter(Boolean)
|
||||||
|
.join(" + ")}
|
||||||
|
</span>
|
||||||
|
<InputBase
|
||||||
|
variant="ghost"
|
||||||
|
value="The Fox jumps!"
|
||||||
|
disabled={disabled}
|
||||||
|
error={hasError}
|
||||||
|
readonly={readOnly}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
)}
|
||||||
|
<span class="col-span-2">Input Label</span>
|
||||||
|
<span>Default</span>
|
||||||
|
<InputLabel>Labeltext</InputLabel>
|
||||||
|
<span>Required</span>
|
||||||
|
<InputLabel required>Labeltext</InputLabel>
|
||||||
|
<span>Error</span>
|
||||||
|
<InputLabel error>Labeltext</InputLabel>
|
||||||
|
<span>Error + Reuired</span>
|
||||||
|
<InputLabel error required>
|
||||||
|
Labeltext
|
||||||
|
</InputLabel>
|
||||||
|
<span>Icon</span>
|
||||||
|
<InputLabel help="Some Info">Labeltext</InputLabel>
|
||||||
|
<span>Description</span>
|
||||||
|
<InputLabel description="Some more words">Labeltext</InputLabel>
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-col gap-2">
|
||||||
|
<span class="col-span-full gap-4">Form Layout</span>
|
||||||
|
<TextInput label="Label" value="Value" />
|
||||||
|
<Form
|
||||||
|
onSubmit={() => {
|
||||||
|
console.log("Nothing");
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Field
|
||||||
|
name="ef"
|
||||||
|
validate={required(
|
||||||
|
"This field is required very long descriptive error message",
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{(field, inputProps) => (
|
||||||
|
<TextInput
|
||||||
|
label="Write something"
|
||||||
|
error={field.error}
|
||||||
|
required
|
||||||
|
value={field.value || ""}
|
||||||
|
inputProps={inputProps}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</Field>
|
||||||
|
<Button>Submit</Button>
|
||||||
|
</Form>
|
||||||
|
<TextInput label="Label" required value="Value" />
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -3,7 +3,7 @@ import { Button } from "@/src/components/button";
|
|||||||
import { FileInput } from "@/src/components/FileInput";
|
import { FileInput } from "@/src/components/FileInput";
|
||||||
import Icon from "@/src/components/icon";
|
import Icon from "@/src/components/icon";
|
||||||
import { SelectInput } from "@/src/components/SelectInput";
|
import { SelectInput } from "@/src/components/SelectInput";
|
||||||
import { TextInput } from "@/src/components/TextInput";
|
import { TextInput } from "@/src/Form/fields/TextInput";
|
||||||
import { Typography } from "@/src/components/Typography";
|
import { Typography } from "@/src/components/Typography";
|
||||||
import { Header } from "@/src/layout/header";
|
import { Header } from "@/src/layout/header";
|
||||||
import {
|
import {
|
||||||
@@ -269,7 +269,6 @@ export const Flash = () => {
|
|||||||
>
|
>
|
||||||
{(field, props) => (
|
{(field, props) => (
|
||||||
<TextInput
|
<TextInput
|
||||||
formStore={formStore}
|
|
||||||
inputProps={props}
|
inputProps={props}
|
||||||
label="SSID"
|
label="SSID"
|
||||||
value={field.value ?? ""}
|
value={field.value ?? ""}
|
||||||
@@ -286,7 +285,6 @@ export const Flash = () => {
|
|||||||
{(field, props) => (
|
{(field, props) => (
|
||||||
<div class="relative col-span-3 w-full">
|
<div class="relative col-span-3 w-full">
|
||||||
<TextInput
|
<TextInput
|
||||||
formStore={formStore}
|
|
||||||
inputProps={props}
|
inputProps={props}
|
||||||
type={
|
type={
|
||||||
passwordVisibility()[index()] ? "text" : "password"
|
passwordVisibility()[index()] ? "text" : "password"
|
||||||
@@ -357,7 +355,6 @@ export const Flash = () => {
|
|||||||
{(field, props) => (
|
{(field, props) => (
|
||||||
<>
|
<>
|
||||||
<TextInput
|
<TextInput
|
||||||
formStore={formStore}
|
|
||||||
inputProps={props}
|
inputProps={props}
|
||||||
label="Source (flake URL)"
|
label="Source (flake URL)"
|
||||||
value={String(field.value)}
|
value={String(field.value)}
|
||||||
@@ -377,7 +374,6 @@ export const Flash = () => {
|
|||||||
{(field, props) => (
|
{(field, props) => (
|
||||||
<>
|
<>
|
||||||
<TextInput
|
<TextInput
|
||||||
formStore={formStore}
|
|
||||||
inputProps={props}
|
inputProps={props}
|
||||||
label="Image Name (attribute name)"
|
label="Image Name (attribute name)"
|
||||||
value={String(field.value)}
|
value={String(field.value)}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { callApi, OperationArgs } from "@/src/api";
|
|||||||
import { activeURI } from "@/src/App";
|
import { activeURI } from "@/src/App";
|
||||||
import { Button } from "@/src/components/button";
|
import { Button } from "@/src/components/button";
|
||||||
import Icon from "@/src/components/icon";
|
import Icon from "@/src/components/icon";
|
||||||
import { TextInput } from "@/src/components/TextInput";
|
import { TextInput } from "@/src/Form/fields/TextInput";
|
||||||
import { Header } from "@/src/layout/header";
|
import { Header } from "@/src/layout/header";
|
||||||
import { createForm, required, reset } from "@modular-forms/solid";
|
import { createForm, required, reset } from "@modular-forms/solid";
|
||||||
import { useNavigate } from "@solidjs/router";
|
import { useNavigate } from "@solidjs/router";
|
||||||
@@ -73,7 +73,7 @@ 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-2">
|
||||||
<Form onSubmit={handleSubmit}>
|
<Form onSubmit={handleSubmit} class="">
|
||||||
<Field
|
<Field
|
||||||
name="opts.machine.name"
|
name="opts.machine.name"
|
||||||
validate={[required("This field is required")]}
|
validate={[required("This field is required")]}
|
||||||
@@ -85,7 +85,6 @@ export function CreateMachine() {
|
|||||||
</div>
|
</div>
|
||||||
<TextInput
|
<TextInput
|
||||||
inputProps={props}
|
inputProps={props}
|
||||||
formStore={formStore}
|
|
||||||
value={`${field.value}`}
|
value={`${field.value}`}
|
||||||
label={"name"}
|
label={"name"}
|
||||||
error={field.error}
|
error={field.error}
|
||||||
@@ -99,7 +98,6 @@ export function CreateMachine() {
|
|||||||
{(field, props) => (
|
{(field, props) => (
|
||||||
<TextInput
|
<TextInput
|
||||||
inputProps={props}
|
inputProps={props}
|
||||||
formStore={formStore}
|
|
||||||
value={`${field.value}`}
|
value={`${field.value}`}
|
||||||
label={"description"}
|
label={"description"}
|
||||||
error={field.error}
|
error={field.error}
|
||||||
@@ -143,7 +141,6 @@ export function CreateMachine() {
|
|||||||
<>
|
<>
|
||||||
<TextInput
|
<TextInput
|
||||||
inputProps={props}
|
inputProps={props}
|
||||||
formStore={formStore}
|
|
||||||
value={`${field.value}`}
|
value={`${field.value}`}
|
||||||
label={"Target"}
|
label={"Target"}
|
||||||
error={field.error}
|
error={field.error}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { activeURI } from "@/src/App";
|
|||||||
import { Button } from "@/src/components/button";
|
import { Button } from "@/src/components/button";
|
||||||
import { FileInput } from "@/src/components/FileInput";
|
import { FileInput } from "@/src/components/FileInput";
|
||||||
import Icon from "@/src/components/icon";
|
import Icon from "@/src/components/icon";
|
||||||
import { TextInput } from "@/src/components/TextInput";
|
import { TextInput } from "@/src/Form/fields/TextInput";
|
||||||
import { selectSshKeys } from "@/src/hooks";
|
import { selectSshKeys } from "@/src/hooks";
|
||||||
import {
|
import {
|
||||||
createForm,
|
createForm,
|
||||||
@@ -17,6 +17,7 @@ import { createSignal, For, Show } from "solid-js";
|
|||||||
import toast from "solid-toast";
|
import toast from "solid-toast";
|
||||||
import { MachineAvatar } from "./avatar";
|
import { MachineAvatar } from "./avatar";
|
||||||
import { Header } from "@/src/layout/header";
|
import { Header } from "@/src/layout/header";
|
||||||
|
import { InputLabel } from "@/src/components/inputBase";
|
||||||
|
|
||||||
type MachineFormInterface = MachineData & {
|
type MachineFormInterface = MachineData & {
|
||||||
sshKey?: File;
|
sshKey?: File;
|
||||||
@@ -302,14 +303,13 @@ const MachineForm = (props: MachineDetailsProps) => {
|
|||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Form onSubmit={handleSubmit}>
|
<div class="card-body">
|
||||||
<div class="card-body">
|
<span class="text-xl text-primary-800">General</span>
|
||||||
<span class="text-xl text-primary-800">General</span>
|
<MachineAvatar name={machineName()} />
|
||||||
<MachineAvatar name={machineName()} />
|
<Form onSubmit={handleSubmit} class="grid grid-cols-12 gap-y-4">
|
||||||
<Field name="machine.name">
|
<Field name="machine.name">
|
||||||
{(field, props) => (
|
{(field, props) => (
|
||||||
<TextInput
|
<TextInput
|
||||||
formStore={formStore}
|
|
||||||
inputProps={props}
|
inputProps={props}
|
||||||
label="Name"
|
label="Name"
|
||||||
value={field.value ?? ""}
|
value={field.value ?? ""}
|
||||||
@@ -321,22 +321,23 @@ const MachineForm = (props: MachineDetailsProps) => {
|
|||||||
</Field>
|
</Field>
|
||||||
<Field name="machine.tags" type="string[]">
|
<Field name="machine.tags" type="string[]">
|
||||||
{(field, props) => (
|
{(field, props) => (
|
||||||
<For each={field.value}>
|
<>
|
||||||
{(tag) => (
|
<InputLabel class="col-span-2">Tags</InputLabel>
|
||||||
<label class="p-1">
|
<span class="col-span-10">
|
||||||
Tags
|
<For each={field.value}>
|
||||||
<span class="mx-2 w-fit rounded-full px-3 py-1 bg-inv-4 fg-inv-1">
|
{(tag) => (
|
||||||
{tag}
|
<span class="mx-2 w-fit rounded-full px-3 py-1 bg-inv-4 fg-inv-1">
|
||||||
</span>
|
{tag}
|
||||||
</label>
|
</span>
|
||||||
)}
|
)}
|
||||||
</For>
|
</For>
|
||||||
|
</span>
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
</Field>
|
</Field>
|
||||||
<Field name="machine.description">
|
<Field name="machine.description">
|
||||||
{(field, props) => (
|
{(field, props) => (
|
||||||
<TextInput
|
<TextInput
|
||||||
formStore={formStore}
|
|
||||||
inputProps={props}
|
inputProps={props}
|
||||||
label="Description"
|
label="Description"
|
||||||
value={field.value ?? ""}
|
value={field.value ?? ""}
|
||||||
@@ -348,16 +349,24 @@ const MachineForm = (props: MachineDetailsProps) => {
|
|||||||
</Field>
|
</Field>
|
||||||
<Field name="hw_config">
|
<Field name="hw_config">
|
||||||
{(field, props) => (
|
{(field, props) => (
|
||||||
<label>Hardware report: {field.value || "None"}</label>
|
<>
|
||||||
|
<InputLabel class="col-span-2">
|
||||||
|
Hardware Configuration
|
||||||
|
</InputLabel>
|
||||||
|
<span class="col-span-10">{field.value || "None"}</span>
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
</Field>
|
</Field>
|
||||||
<Field name="disk_schema">
|
<Field name="disk_schema">
|
||||||
{(field, props) => (
|
{(field, props) => (
|
||||||
<span>Disk schema: {field.value || "None"}</span>
|
<>
|
||||||
|
<InputLabel class="col-span-2">Disk schema</InputLabel>
|
||||||
|
<span class="col-span-10">{field.value || "None"}</span>
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
</Field>
|
</Field>
|
||||||
|
|
||||||
<div class="collapse collapse-arrow" tabindex="0">
|
<div class="collapse collapse-arrow col-span-full" tabindex="0">
|
||||||
<input type="checkbox" />
|
<input type="checkbox" />
|
||||||
<div class="collapse-title link px-0 text-xl ">
|
<div class="collapse-title link px-0 text-xl ">
|
||||||
Connection Settings
|
Connection Settings
|
||||||
@@ -366,7 +375,6 @@ const MachineForm = (props: MachineDetailsProps) => {
|
|||||||
<Field name="machine.deploy.targetHost">
|
<Field name="machine.deploy.targetHost">
|
||||||
{(field, props) => (
|
{(field, props) => (
|
||||||
<TextInput
|
<TextInput
|
||||||
formStore={formStore}
|
|
||||||
inputProps={props}
|
inputProps={props}
|
||||||
label="Target Host"
|
label="Target Host"
|
||||||
value={field.value ?? ""}
|
value={field.value ?? ""}
|
||||||
@@ -411,7 +419,7 @@ const MachineForm = (props: MachineDetailsProps) => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{
|
{
|
||||||
<div class="card-actions justify-end">
|
<div class="card-actions col-span-full justify-end">
|
||||||
<Button
|
<Button
|
||||||
type="submit"
|
type="submit"
|
||||||
disabled={formStore.submitting || !formStore.dirty}
|
disabled={formStore.submitting || !formStore.dirty}
|
||||||
@@ -420,8 +428,8 @@ const MachineForm = (props: MachineDetailsProps) => {
|
|||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
</div>
|
</Form>
|
||||||
</Form>
|
</div>
|
||||||
|
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<div class="divider"></div>
|
<div class="divider"></div>
|
||||||
@@ -579,11 +587,10 @@ function WifiModule(props: MachineWifiProps) {
|
|||||||
<span class="text-neutral">Preconfigure wireless networks</span>
|
<span class="text-neutral">Preconfigure wireless networks</span>
|
||||||
<For each={nets()}>
|
<For each={nets()}>
|
||||||
{(_, idx) => (
|
{(_, idx) => (
|
||||||
<div class="flex gap-4">
|
<div class="grid grid-cols-2">
|
||||||
<Field name={`networks.${idx()}.ssid`}>
|
<Field name={`networks.${idx()}.ssid`}>
|
||||||
{(field, props) => (
|
{(field, props) => (
|
||||||
<TextInput
|
<TextInput
|
||||||
formStore={formStore}
|
|
||||||
inputProps={props}
|
inputProps={props}
|
||||||
label="Name"
|
label="Name"
|
||||||
value={field.value ?? ""}
|
value={field.value ?? ""}
|
||||||
@@ -595,7 +602,6 @@ function WifiModule(props: MachineWifiProps) {
|
|||||||
<Field name={`networks.${idx()}.password`}>
|
<Field name={`networks.${idx()}.password`}>
|
||||||
{(field, props) => (
|
{(field, props) => (
|
||||||
<TextInput
|
<TextInput
|
||||||
formStore={formStore}
|
|
||||||
inputProps={props}
|
inputProps={props}
|
||||||
label="Password"
|
label="Password"
|
||||||
value={field.value ?? ""}
|
value={field.value ?? ""}
|
||||||
|
|||||||
@@ -1,29 +1,13 @@
|
|||||||
import { callApi } from "@/src/api";
|
|
||||||
import { activeURI } from "@/src/App";
|
import { activeURI } from "@/src/App";
|
||||||
import { BackButton } from "@/src/components/BackButton";
|
import { BackButton } from "@/src/components/BackButton";
|
||||||
import { createModulesQuery } from "@/src/queries";
|
import { createModulesQuery } from "@/src/queries";
|
||||||
import { useParams, useNavigate } from "@solidjs/router";
|
import { useParams, useNavigate } from "@solidjs/router";
|
||||||
import {
|
import { createEffect, For, Match, Switch } from "solid-js";
|
||||||
createEffect,
|
|
||||||
createSignal,
|
|
||||||
For,
|
|
||||||
JSX,
|
|
||||||
Match,
|
|
||||||
Show,
|
|
||||||
Switch,
|
|
||||||
} from "solid-js";
|
|
||||||
import { SolidMarkdown } from "solid-markdown";
|
import { SolidMarkdown } from "solid-markdown";
|
||||||
import toast from "solid-toast";
|
|
||||||
import { ModuleInfo } from "./list";
|
import { ModuleInfo } from "./list";
|
||||||
import { createQuery } from "@tanstack/solid-query";
|
import { createQuery } from "@tanstack/solid-query";
|
||||||
import { JSONSchema7 } from "json-schema";
|
import { JSONSchema7 } from "json-schema";
|
||||||
import { TextInput } from "@/src/components/TextInput";
|
import { SubmitHandler } from "@modular-forms/solid";
|
||||||
import {
|
|
||||||
createForm,
|
|
||||||
getValue,
|
|
||||||
setValue,
|
|
||||||
SubmitHandler,
|
|
||||||
} from "@modular-forms/solid";
|
|
||||||
import { DynForm } from "@/src/Form/form";
|
import { DynForm } from "@/src/Form/form";
|
||||||
import { Button } from "@/src/components/button";
|
import { Button } from "@/src/components/button";
|
||||||
import Icon from "@/src/components/icon";
|
import Icon from "@/src/components/icon";
|
||||||
|
|||||||
Reference in New Issue
Block a user