Monorepo for Tangled
tangled.org
1<script module lang="ts">
2 import { tv, type VariantProps } from "tailwind-variants";
3
4 export const inputField = tv({
5 base: "flex min-h-9 items-center gap-2 rounded border bg-background-default px-2 py-1 transition-colors",
6 variants: {
7 error: {
8 false:
9 "border-border-default focus-within:border-border-strong focus-within:ring-1 focus-within:ring-border-strong",
10 true: "border-border-danger bg-background-danger-subtle text-foreground-danger focus-within:border-border-focus-danger focus-within:ring-1 focus-within:ring-border-focus-danger"
11 },
12 disabled: {
13 false: "",
14 true: "cursor-not-allowed bg-background-inset text-foreground-disabled"
15 }
16 },
17 defaultVariants: {
18 error: false,
19 disabled: false
20 }
21 });
22
23 export type InputFieldVariants = VariantProps<typeof inputField>;
24</script>
25
26<script lang="ts">
27 import type { Component } from "svelte";
28 import type { HTMLInputAttributes, SvelteHTMLElements } from "svelte/elements";
29 import Spinner from "./Spinner.svelte";
30
31 interface Props extends Omit<HTMLInputAttributes, "class" | "value"> {
32 value?: string;
33 error?: boolean;
34 disabled?: boolean;
35 loading?: boolean;
36 iconLeft?: Component<SvelteHTMLElements["svg"]>;
37 iconRight?: Component<SvelteHTMLElements["svg"]>;
38 class?: string;
39 }
40
41 let {
42 value = $bindable(""),
43 error = false,
44 disabled = false,
45 loading = false,
46 iconLeft,
47 iconRight,
48 class: className,
49 ...rest
50 }: Props = $props();
51
52 const classes = $derived(inputField({ error, disabled, class: className }));
53</script>
54
55<div class={classes}>
56 {#if iconLeft}
57 {@const IconLeft = iconLeft}
58 <IconLeft class="size-4 shrink-0 text-foreground-subtle" aria-hidden="true" />
59 {/if}
60 <input
61 bind:value
62 {disabled}
63 aria-invalid={error}
64 aria-busy={loading}
65 class="flex-1 bg-transparent text-sm outline-none placeholder:text-foreground-placeholder disabled:cursor-not-allowed"
66 {...rest}
67 />
68 {#if loading}
69 <Spinner class="size-4 shrink-0" />
70 {:else if iconRight}
71 {@const IconRight = iconRight}
72 <IconRight class="size-4 shrink-0 text-foreground-subtle" aria-hidden="true" />
73 {/if}
74</div>