一些检查失败了
automerge / automerge (push) Has been cancelled
react-native-android-build-apk / AssembleRelease (push) Has been cancelled
Build macOS M1 / Main (push) Has been cancelled
Joplin Continuous Integration / Main (macos-15-intel) (push) Has been cancelled
Joplin Continuous Integration / Main (ubuntu-22.04) (push) Has been cancelled
Joplin Continuous Integration / Main (ubuntu-22.04-arm) (push) Has been cancelled
Joplin Continuous Integration / Main (windows-11-vs2026-arm) (push) Has been cancelled
Joplin Continuous Integration / Main (windows-2025) (push) Has been cancelled
Joplin Continuous Integration / ServerDockerImage (ubuntu-22.04) (push) Has been cancelled
Joplin Continuous Integration / ServerDockerImage (ubuntu-22.04-arm) (push) Has been cancelled
Joplin UI tests / Main (ubuntu-22.04) (push) Has been cancelled
Joplin UI tests / Main (windows-2025) (push) Has been cancelled
Joplin UI tests / Mobile (push) Has been cancelled
Close stale issues / ProcessStaleIssues (push) Has been cancelled
59 行
1.5 KiB
TypeScript
59 行
1.5 KiB
TypeScript
import * as React from 'react';
|
|
import PasswordInput from './PasswordInput';
|
|
import { useId } from 'react';
|
|
import { ChangeEventHandler } from './types';
|
|
import { _ } from '@joplin/lib/locale';
|
|
|
|
interface Props {
|
|
labelText: string;
|
|
value: string;
|
|
onChange: ChangeEventHandler;
|
|
valid?: boolean;
|
|
reserveIconGutter?: boolean;
|
|
}
|
|
|
|
const LabelledPasswordInput: React.FC<Props> = props => {
|
|
const inputId = useId();
|
|
const statusIconId = useId();
|
|
|
|
const canRenderStatusIcon = (props.valid ?? null) !== null && props.value;
|
|
const renderStatusIcon = () => {
|
|
if (!canRenderStatusIcon) return null;
|
|
let title, classNames;
|
|
if (props.valid) {
|
|
title = _('Valid');
|
|
classNames = 'fas fa-check -valid';
|
|
} else {
|
|
title = _('Invalid');
|
|
classNames = 'fas fa-times -invalid';
|
|
}
|
|
return <i
|
|
className={`password-status-icon ${classNames}`}
|
|
id={statusIconId}
|
|
|
|
role='img'
|
|
aria-label={title}
|
|
title={title}
|
|
|
|
aria-live='polite'
|
|
></i>;
|
|
};
|
|
|
|
const rootClassName = `labelled-password-input form-input-group${props.reserveIconGutter ? ' -reserve-icon-gutter' : ''}`;
|
|
return <div className={rootClassName}>
|
|
<label htmlFor={inputId}>{props.labelText}</label>
|
|
<div className='password'>
|
|
<PasswordInput
|
|
inputId={inputId}
|
|
aria-invalid={canRenderStatusIcon ? !props.valid : undefined}
|
|
aria-errormessage={canRenderStatusIcon ? statusIconId : undefined}
|
|
value={props.value}
|
|
onChange={props.onChange}
|
|
/>
|
|
{renderStatusIcon()}
|
|
</div>
|
|
</div>;
|
|
};
|
|
|
|
export default LabelledPasswordInput;
|