4e80c0a0f5
A11y C-1: aria-label on 30+ remaining icon-only buttons across:
- settings panels (close, reset, info, expand, remove, undo)
- editor toolbar (bold, italic, underline, strike, code, spoiler,
blockquote, code block, ordered/unordered list, headings 1-3)
- auth stages (cancel buttons in SSO, Password stages)
- device verification (cancel buttons)
- password input (show/hide toggle with dynamic label)
- event readers, account data editor close buttons
- global emoji packs (add/remove buttons)
Perf-5: Replace O(N×T) getTimelineAndBaseIndex scan with precomputed binary
search (timelineSegments useMemo) — O(log T) per visible message render
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
47 lines
1.5 KiB
TypeScript
47 lines
1.5 KiB
TypeScript
import React, { ComponentProps, forwardRef } from 'react';
|
|
import { Icon, IconButton, Input, config, Icons } from 'folds';
|
|
import { UseStateProvider } from '../UseStateProvider';
|
|
|
|
type PasswordInputProps = Omit<ComponentProps<typeof Input>, 'type' | 'size'> & {
|
|
size: '400' | '500';
|
|
};
|
|
export const PasswordInput = forwardRef<HTMLInputElement, PasswordInputProps>(
|
|
({ variant = 'Background', size, style, after, ...props }, ref) => {
|
|
const paddingRight: string = size === '500' ? config.space.S300 : config.space.S200;
|
|
|
|
return (
|
|
<UseStateProvider initial={false}>
|
|
{(visible, setVisible) => (
|
|
<Input
|
|
{...props}
|
|
ref={ref}
|
|
style={{ paddingRight, ...style }}
|
|
type={visible ? 'text' : 'password'}
|
|
size={size}
|
|
variant={variant}
|
|
after={
|
|
<>
|
|
{after}
|
|
<IconButton
|
|
onClick={() => setVisible(!visible)}
|
|
type="button"
|
|
variant={visible ? 'Warning' : variant}
|
|
size="300"
|
|
radii="300"
|
|
aria-label={visible ? 'Hide password' : 'Show password'}
|
|
>
|
|
<Icon
|
|
style={{ opacity: config.opacity.P300 }}
|
|
size="100"
|
|
src={visible ? Icons.Eye : Icons.EyeBlind}
|
|
/>
|
|
</IconButton>
|
|
</>
|
|
}
|
|
/>
|
|
)}
|
|
</UseStateProvider>
|
|
);
|
|
}
|
|
);
|