Last active
November 26, 2025 15:04
-
-
Save sillvva/3079c396d0e22dfdfa30a4bfdbd4734c to your computer and use it in GitHub Desktop.
Bindable input
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <script lang="ts" generics="V extends RemoteFormFieldValue"> | |
| import type { RemoteFormField, RemoteFormFieldValue } from "@sveltejs/kit"; | |
| import type { HTMLInputAttributes } from "svelte/elements"; | |
| import FieldMessage from "./field-message.svelte"; | |
| import InputWrapper from "./input-wrapper.svelte"; | |
| type InputTypeMap = | |
| | { | |
| type?: "text"; | |
| field: RemoteFormField<string>; | |
| value?: undefined; | |
| } | |
| | { | |
| type: "email" | "password" | "url" | "tel" | "search" | "button" | "reset" | "color" | "button"; | |
| field: RemoteFormField<string>; | |
| value?: undefined; | |
| } | |
| | { type: "number" | "range"; field: RemoteFormField<number>; value?: undefined } | |
| | { | |
| type: "checkbox"; | |
| value?: undefined; | |
| field: RemoteFormField<boolean>; | |
| } | |
| | { | |
| type: "checkbox"; | |
| value: string; | |
| field: RemoteFormField<string[]>; | |
| } | |
| | { | |
| type: "radio"; | |
| value: string; | |
| field: RemoteFormField<string>; | |
| } | |
| | { | |
| type: "hidden"; | |
| value?: string; | |
| field: RemoteFormField<string>; | |
| } | |
| | { | |
| type: "submit"; | |
| value: string; | |
| field: RemoteFormField<string>; | |
| } | |
| | { | |
| type: "file"; | |
| field: RemoteFormField<File>; | |
| value?: undefined; | |
| } | |
| | { | |
| type: "file multiple"; | |
| field: RemoteFormField<File[]>; | |
| value?: undefined; | |
| }; | |
| type Props = { | |
| label?: string; | |
| field: RemoteFormField<V>; | |
| description?: string; | |
| warning?: string; | |
| hidden?: boolean; | |
| } & InputTypeMap & | |
| Omit<HTMLInputAttributes, "type" | "name" | "id" | "value" | "checked" | "defaultValue" | "defaultChecked">; | |
| let { label, description, warning, hidden, required, type, value = $bindable(), field, ...rest }: Props = $props(); | |
| const attributes = $derived.by(() => { | |
| if (type === "hidden" || type === "submit" || type === "radio") { | |
| const f = field as RemoteFormField<string>; | |
| return f.as(type, value || (field.value() as string) || ""); | |
| } else if (type === "checkbox" && typeof value === "string") { | |
| const f = field as RemoteFormField<string[]>; | |
| return f.as("checkbox", value); | |
| } else if (type === "number" || type === "range") { | |
| const f = field as RemoteFormField<number>; | |
| return f.as(type); | |
| } else if (type === "file") { | |
| const f = field as RemoteFormField<File>; | |
| return f.as(type); | |
| } else if (type === "file multiple") { | |
| const f = field as RemoteFormField<File[]>; | |
| return f.as(type); | |
| } else { | |
| const t = type as "text" | "email" | "password" | "url" | "tel" | "search" | "button" | "reset" | "color"; | |
| const f = field as RemoteFormField<string>; | |
| return f.as(t); | |
| } | |
| }); | |
| const withRest = $derived({ ...rest, ...attributes }); | |
| const name = $derived(attributes.name); | |
| const issues = $derived(field.issues()); | |
| const invalid = $derived(!!issues?.length || undefined); | |
| </script> | |
| {#if type === "checkbox"} | |
| <label | |
| class={[ | |
| "label flex cursor-pointer rounded-lg border p-4 text-sm", | |
| invalid ? "border-error" : "border-base-content/20", | |
| hidden && "hidden" | |
| ]} | |
| > | |
| <div class="flex flex-1 flex-col gap-0.5"> | |
| <span> | |
| <span class="text-base-content">{label}</span> | |
| {#if required} | |
| <span class="text-error">*</span> | |
| {/if} | |
| </span> | |
| <FieldMessage {name} type="checkbox" {description} {warning} {issues} /> | |
| </div> | |
| <input {...withRest} bind:value aria-invalid={invalid} id={name} type="checkbox" class="checkbox-primary checkbox" /> | |
| </label> | |
| {:else} | |
| <InputWrapper type={hidden ? "hidden" : type || "text"} {label} {required}> | |
| <input | |
| {...withRest} | |
| bind:value | |
| aria-invalid={invalid} | |
| id={name} | |
| class={[type !== "hidden" && !hidden && "input focus:border-primary focus:aria-[invalid]:border-error w-full"]} | |
| {hidden} | |
| /> | |
| </InputWrapper> | |
| {/if} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment