From 02c4bb1c9b9630071af462b30475b0cd00592fb9 Mon Sep 17 00:00:00 2001 From: byGalax Date: Fri, 15 May 2026 22:54:58 +0200 Subject: [PATCH] feat(desktop): shared PinInput component --- apps/desktop/src/components/PinInput.tsx | 48 ++++++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 apps/desktop/src/components/PinInput.tsx diff --git a/apps/desktop/src/components/PinInput.tsx b/apps/desktop/src/components/PinInput.tsx new file mode 100644 index 0000000..71675b9 --- /dev/null +++ b/apps/desktop/src/components/PinInput.tsx @@ -0,0 +1,48 @@ +import { useEffect, useRef } from 'react'; + +interface Props { + value: string; + onChange: (next: string) => void; + length?: number; + autoFocus?: boolean; + disabled?: boolean; + ariaLabel: string; + onSubmit?: () => void; +} + +export function PinInput({ value, onChange, length = 6, autoFocus, disabled, ariaLabel, onSubmit }: Props) { + const ref = useRef(null); + useEffect(() => { if (autoFocus) ref.current?.focus(); }, [autoFocus]); + return ( +
ref.current?.focus()}> + onChange(e.target.value.replace(/\D/g, '').slice(0, length))} + onKeyDown={(e) => { if (e.key === 'Enter' && value.length === length) onSubmit?.(); }} + className="absolute h-px w-px overflow-hidden p-0 opacity-0" + /> +
+ {Array.from({ length }).map((_, i) => { + const filled = i < value.length; + return ( + {filled ? '•' : ''} + ); + })} +
+
+ ); +}