This repository has no description
0

Configure Feed

Select the types of activity you want to include in your feed.

core / web / src / lib / components / ui / Textarea.svelte
3.0 kB 102 lines
1<script module lang="ts"> 2 import { tv, type VariantProps } from "tailwind-variants"; 3 4 export const textareaField = tv({ 5 slots: { 6 root: "relative flex max-h-64 items-start gap-1 rounded border bg-background-default px-2.5 py-2 transition-colors", 7 textarea: 8 "min-h-24 flex-1 bg-transparent text-sm leading-6 outline-none placeholder:text-foreground-placeholder read-only:resize-none disabled:cursor-not-allowed disabled:resize-none" 9 }, 10 variants: { 11 error: { 12 false: { 13 root: "border-border-default focus-within:border-border-strong focus-within:ring-1 focus-within:ring-border-strong" 14 }, 15 true: { 16 root: "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", 17 textarea: "placeholder:text-foreground-danger" 18 } 19 }, 20 disabled: { 21 false: "", 22 true: { 23 root: "cursor-not-allowed border-border-disabled bg-background-subtle text-foreground-muted" 24 } 25 }, 26 readonly: { 27 false: "", 28 true: { 29 root: "cursor-default border-border-disabled bg-background-subtle text-foreground-muted" 30 } 31 }, 32 resizeable: { 33 false: { textarea: "resize-none" }, 34 true: { textarea: "resize-y" } 35 } 36 }, 37 defaultVariants: { 38 error: false, 39 disabled: false, 40 readonly: false 41 } 42 }); 43 44 export type TextareaFieldVariants = VariantProps<typeof textareaField>; 45</script> 46 47<script lang="ts"> 48 import type { Component } from "svelte"; 49 import type { HTMLTextareaAttributes, SvelteHTMLElements } from "svelte/elements"; 50 import Spinner from "./Spinner.svelte"; 51 52 interface Props extends Omit<HTMLTextareaAttributes, "class" | "value"> { 53 value?: string; 54 error?: boolean; 55 disabled?: boolean; 56 loading?: boolean; 57 readonly?: boolean; 58 resizeable?: boolean; 59 iconLeft?: Component<SvelteHTMLElements["svg"]>; 60 iconRight?: Component<SvelteHTMLElements["svg"]>; 61 class?: string; 62 element?: HTMLTextAreaElement; 63 } 64 65 let { 66 value = $bindable(""), 67 error = false, 68 disabled = false, 69 loading = false, 70 readonly = false, 71 resizeable = false, 72 iconLeft, 73 iconRight, 74 class: className, 75 element = $bindable(), 76 ...rest 77 }: Props = $props(); 78 79 const { root, textarea } = $derived(textareaField({ error, disabled, readonly, resizeable })); 80</script> 81 82<div class={root({ class: className })}> 83 {#if iconLeft} 84 {@const IconLeft = iconLeft} 85 <IconLeft class="mt-0.5 mr-1 size-4 shrink-0 text-foreground-subtle" aria-hidden="true" /> 86 {/if} 87 <textarea 88 bind:this={element} 89 bind:value 90 {disabled} 91 {readonly} 92 aria-invalid={error} 93 aria-busy={loading} 94 class={textarea({ class: loading ? "pr-5" : undefined })} 95 {...rest}></textarea> 96 {#if loading} 97 <Spinner class="absolute top-2 right-2.5 size-4" /> 98 {:else if iconRight} 99 {@const IconRight = iconRight} 100 <IconRight class="mt-0.5 ml-1 size-4 shrink-0 text-foreground-subtle" aria-hidden="true" /> 101 {/if} 102</div>