feat: Add auto-resize on text input

This commit is contained in:
April Hall 2025-02-07 18:48:10 -05:00
parent 52bd4e4ce4
commit 5288e09b27
Signed by: arithefirst
GPG Key ID: 4508A15C4DB91C5B
4 changed files with 89 additions and 3 deletions

View File

@ -0,0 +1,28 @@
import Root from "./textarea.svelte";
type FormTextareaEvent<T extends Event = Event> = T & {
currentTarget: EventTarget & HTMLTextAreaElement;
};
type TextareaEvents = {
blur: FormTextareaEvent<FocusEvent>;
change: FormTextareaEvent<Event>;
click: FormTextareaEvent<MouseEvent>;
focus: FormTextareaEvent<FocusEvent>;
keydown: FormTextareaEvent<KeyboardEvent>;
keypress: FormTextareaEvent<KeyboardEvent>;
keyup: FormTextareaEvent<KeyboardEvent>;
mouseover: FormTextareaEvent<MouseEvent>;
mouseenter: FormTextareaEvent<MouseEvent>;
mouseleave: FormTextareaEvent<MouseEvent>;
paste: FormTextareaEvent<ClipboardEvent>;
input: FormTextareaEvent<InputEvent>;
};
export {
Root,
//
Root as Textarea,
type TextareaEvents,
type FormTextareaEvent,
};

View File

@ -0,0 +1,43 @@
<script lang="ts">
import { cn } from '$lib/utils.js';
import type { HTMLTextareaAttributes } from 'svelte/elements';
import type { TextareaEvents } from './index.js';
// Note: The autoResize directive is NOT part of the shadcn-svelte package,
// it just has to be added directly to the root component because of a
// Svelte limitation.
import { autoResize } from '$lib/functions/autoresize.svelte';
type $$Props = HTMLTextareaAttributes;
type $$Events = TextareaEvents;
let className: $$Props['class'] = undefined;
export let value: $$Props['value'] = undefined;
export { className as class };
// Workaround for https://github.com/sveltejs/svelte/issues/9305
// Fixed in Svelte 5, but not backported to 4.x.
export let readonly: $$Props['readonly'] = undefined;
</script>
<textarea
class={cn(
'flex min-h-[60px] w-full rounded-md border border-input bg-transparent px-3 py-2 text-sm shadow-sm placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50',
className,
)}
bind:value
{readonly}
on:blur
on:change
on:click
on:focus
on:keydown
on:keypress
on:keyup
on:mouseover
on:mouseenter
on:mouseleave
on:paste
on:input
use:autoResize
{...$$restProps}></textarea>

View File

@ -0,0 +1,14 @@
export function autoResize(node: HTMLElement) {
function resize() {
node.style.height = 'auto';
node.style.height = node.scrollHeight + 'px';
}
$effect(() => {
resize();
node.addEventListener('input', resize);
return () => {
node.removeEventListener('input', resize);
};
});
}

View File

@ -2,7 +2,7 @@
import { page } from '$app/state';
import Message from '$lib/components/message.svelte';
import { Button } from '$lib/components/ui/button/index';
import { Input } from '$lib/components/ui/input/index';
import Textarea from '$lib/components/ui/textarea/textarea.svelte';
import Websocket from '$lib/functions/clientWebsocket.svelte';
import type { TypeMessage } from '$lib/types';
import Send from 'lucide-svelte/icons/send';
@ -10,6 +10,7 @@
import { onMount } from 'svelte';
import { v4 as uuidv4 } from 'uuid';
import type { PageData } from './$types';
import { autoResize } from '$lib/functions/autoresize.svelte';
const { data }: { data: PageData } = $props();
@ -50,7 +51,7 @@
socket?.sendMessage(user!, msg);
msg = '';
}}>
<Input type="text" placeholder="Type Here" bind:value={msg} />
<Button class="h-9 w-14" type="submit"><Send class="size-full" /></Button>
<Textarea placeholder="Type Here" bind:value={msg} />
<Button class="h-full w-14" type="submit"><Send class="size-full" /></Button>
</form>
</div>