From 03a70d27a3432ef6e50c6aa97942f6bc576684c0 Mon Sep 17 00:00:00 2001 From: Senko-san Date: Sat, 11 Jul 2026 16:18:26 +0300 Subject: [PATCH] chore: textfield visuals fixed --- src/components/text-field/index.tsx | 55 ++++++++++++++++------------- src/stories/TextField.stories.tsx | 6 +++- src/styles/components.css | 9 ++++- 3 files changed, 44 insertions(+), 26 deletions(-) diff --git a/src/components/text-field/index.tsx b/src/components/text-field/index.tsx index d40da22..de1e49a 100644 --- a/src/components/text-field/index.tsx +++ b/src/components/text-field/index.tsx @@ -63,11 +63,16 @@ function diffChars(prev: ReadonlyArray<{ id: number; char: string }>, next: stri return { reusedId, keptPrev }; } +/** Browsers render password glyphs as this bullet; match it in the overlay + * so animated fields don't leak the real characters visually. */ +const PASSWORD_MASK_CHAR = '•'; + function useFieldAnimation( multiline: boolean, externalRef: Ref, controlledValue: ComponentPropsWithoutRef<'input'>['value'], initial: ComponentPropsWithoutRef<'input'>['defaultValue'], + masked = false, ) { const innerRef = useRef(null); const overlayRef = useRef(null); @@ -144,33 +149,34 @@ function useFieldAnimation( const overlay = ( ); @@ -187,6 +193,7 @@ export const TextField = forwardRef( ref, props.value, props.defaultValue, + props.type === 'password', ); if (!animated) { return ( diff --git a/src/stories/TextField.stories.tsx b/src/stories/TextField.stories.tsx index f81905e..1fd4fc9 100644 --- a/src/stories/TextField.stories.tsx +++ b/src/stories/TextField.stories.tsx @@ -34,7 +34,9 @@ export const Playground: Story = {}; export const Disabled: Story = { args: { disabled: true, value: 'Read only' } }; export const Multiline: Story = { - render: () =>