pin input: make name optional, custom class support, fix hidden input

This commit is contained in:
Elijah Duffy
2025-07-03 11:54:53 -07:00
parent ef7bc6bbab
commit 37c3594d1a
2 changed files with 57 additions and 45 deletions

View File

@@ -1,6 +1,19 @@
<script lang="ts"> <script lang="ts">
import { isValueValid, validate, type ValidatorOptions } from '@svelte-toolkit/validate'; import { isValueValid, validate, type ValidatorOptions } from '@svelte-toolkit/validate';
import Label from './Label.svelte'; import Label from './Label.svelte';
import type { ClassValue } from 'svelte/elements';
import { generateIdentifier } from './util.js';
interface Props {
label?: string;
length: number;
required?: boolean;
name?: string;
value?: string;
oncomplete?: (value: string) => void;
onchange?: (value: string) => void;
class?: ClassValue | null | undefined;
}
let { let {
label, label,
@@ -9,16 +22,11 @@
name, name,
value = $bindable(''), value = $bindable(''),
oncomplete, oncomplete,
onchange onchange,
}: { class: classValue
label?: string; }: Props = $props();
length: number;
required?: boolean; const id = $derived(generateIdentifier('pin-input', name));
name: string;
value?: string;
oncomplete?: (value: string) => void;
onchange?: (value: string) => void;
} = $props();
let hiddenInput: HTMLInputElement; let hiddenInput: HTMLInputElement;
let valid: boolean = $state(true); let valid: boolean = $state(true);
@@ -148,41 +156,45 @@
}); });
</script> </script>
<input <div class={classValue}>
class="hidden" <input
use:validate={validateOpts} {id}
onvalidate={(e) => { {name}
valid = e.detail.valid; class="hidden"
}} use:validate={validateOpts}
bind:this={hiddenInput} onvalidate={(e) => {
/> valid = e.detail.valid;
}}
bind:this={hiddenInput}
/>
{#if label} {#if label}
<Label bigError={!valid} for={name}>{label}</Label> <Label bigError={!valid} for={id}>{label}</Label>
{/if} {/if}
<div> <div>
<div class="flex gap-4"> <div class="flex gap-4">
{#each { length: length } as _, i} {#each { length: length } as _, i}
<input <input
type="text" type="text"
class={[ class={[
'px[1.125rem] w-[5ch] rounded-sm pt-4 pb-3.5 transition-colors', 'px[1.125rem] w-[5ch] rounded-sm pt-4 pb-3.5 transition-colors',
'text-center align-middle font-mono font-normal placeholder:font-normal', 'text-center align-middle font-mono font-normal placeholder:font-normal',
'border-accent dark:border-accent/50 border', 'border-accent dark:border-accent/50 border',
'text-text placeholder:text-text/30 dark:text-background dark:placeholder:text-background/30', 'text-text placeholder:text-text/30 dark:text-background dark:placeholder:text-background/30',
'dark:bg-text-800 bg-white dark:sm:bg-slate-800', 'dark:bg-text-800 bg-white dark:sm:bg-slate-800',
!valid && i >= value.length && 'border-red-500!' !valid && i >= value.length && 'border-red-500!'
]} ]}
value={value[i] || ''} value={value[i] || ''}
{required} {required}
maxlength="1" maxlength="1"
onfocus={onfocusinput(i)} onfocus={onfocusinput(i)}
onmousedown={onmousedown(i)} onmousedown={onmousedown(i)}
onkeydown={onkeydowninput(i)} onkeydown={onkeydowninput(i)}
bind:this={inputs[i]} bind:this={inputs[i]}
placeholder="0" placeholder="0"
/> />
{/each} {/each}
</div>
</div> </div>
</div> </div>

View File

@@ -98,7 +98,7 @@
<div class="component"> <div class="component">
<p class="title">Pin Input</p> <p class="title">Pin Input</p>
<PinInput name="pin" length={6} /> <PinInput label="Please enter your 6-digit PIN" length={6} />
</div> </div>
<div class="component"> <div class="component">