mirror of
https://github.com/yangshun/tech-interview-handbook.git
synced 2025-07-14 18:05:55 +08:00
ui: add more props to TextInput
This commit is contained in:
@ -14,12 +14,12 @@ export default {
|
|||||||
autoComplete: {
|
autoComplete: {
|
||||||
control: 'text',
|
control: 'text',
|
||||||
},
|
},
|
||||||
|
disabled: {
|
||||||
|
control: 'boolean',
|
||||||
|
},
|
||||||
errorMessage: {
|
errorMessage: {
|
||||||
control: 'text',
|
control: 'text',
|
||||||
},
|
},
|
||||||
isDisabled: {
|
|
||||||
control: 'boolean',
|
|
||||||
},
|
|
||||||
isLabelHidden: {
|
isLabelHidden: {
|
||||||
control: 'boolean',
|
control: 'boolean',
|
||||||
},
|
},
|
||||||
@ -103,7 +103,7 @@ export function Icon() {
|
|||||||
export function Disabled() {
|
export function Disabled() {
|
||||||
return (
|
return (
|
||||||
<TextInput
|
<TextInput
|
||||||
isDisabled={true}
|
disabled={true}
|
||||||
label="Disabled input"
|
label="Disabled input"
|
||||||
placeholder="John Doe"
|
placeholder="John Doe"
|
||||||
type="text"
|
type="text"
|
||||||
|
@ -1,23 +1,46 @@
|
|||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import type { ChangeEvent } from 'react';
|
import type {
|
||||||
import React, { useId } from 'react';
|
ChangeEvent,
|
||||||
|
FocusEvent,
|
||||||
|
ForwardedRef,
|
||||||
|
InputHTMLAttributes,
|
||||||
|
} from 'react';
|
||||||
|
import React, { forwardRef, useId } from 'react';
|
||||||
|
|
||||||
|
type TextInputInputAttributes = Pick<
|
||||||
|
InputHTMLAttributes<HTMLInputElement>,
|
||||||
|
| 'autoComplete'
|
||||||
|
| 'disabled'
|
||||||
|
| 'max'
|
||||||
|
| 'maxLength'
|
||||||
|
| 'min'
|
||||||
|
| 'minLength'
|
||||||
|
| 'name'
|
||||||
|
| 'pattern'
|
||||||
|
| 'placeholder'
|
||||||
|
| 'required'
|
||||||
|
| 'type'
|
||||||
|
>;
|
||||||
|
|
||||||
|
type TextInputDOMAttributes = Pick<
|
||||||
|
InputHTMLAttributes<HTMLInputElement>,
|
||||||
|
'onBlur' | 'onFocus'
|
||||||
|
>;
|
||||||
|
|
||||||
type Props = Readonly<{
|
type Props = Readonly<{
|
||||||
autoComplete?: string;
|
|
||||||
defaultValue?: string;
|
defaultValue?: string;
|
||||||
endIcon?: React.ComponentType<React.ComponentProps<'svg'>>;
|
endIcon?: React.ComponentType<React.ComponentProps<'svg'>>;
|
||||||
errorMessage?: React.ReactNode;
|
errorMessage?: React.ReactNode;
|
||||||
id?: string;
|
id?: string;
|
||||||
isDisabled?: boolean;
|
|
||||||
isLabelHidden?: boolean;
|
isLabelHidden?: boolean;
|
||||||
label: string;
|
label: string;
|
||||||
name?: string;
|
onBlur?: (event: FocusEvent<HTMLInputElement>) => void;
|
||||||
onChange?: (value: string, event: ChangeEvent<HTMLInputElement>) => void;
|
onChange?: (value: string, event: ChangeEvent<HTMLInputElement>) => void;
|
||||||
placeholder?: string;
|
|
||||||
startIcon?: React.ComponentType<React.ComponentProps<'svg'>>;
|
startIcon?: React.ComponentType<React.ComponentProps<'svg'>>;
|
||||||
type?: 'email' | 'password' | 'text';
|
|
||||||
value?: string;
|
value?: string;
|
||||||
}>;
|
}> &
|
||||||
|
Readonly<TextInputDOMAttributes> &
|
||||||
|
Readonly<TextInputInputAttributes>;
|
||||||
|
|
||||||
type State = 'error' | 'normal';
|
type State = 'error' | 'normal';
|
||||||
|
|
||||||
@ -28,22 +51,23 @@ const stateClasses: Record<State, string> = {
|
|||||||
'placeholder:text-slate-400 focus:ring-primary-500 focus:border-primary-500 border-slate-300',
|
'placeholder:text-slate-400 focus:ring-primary-500 focus:border-primary-500 border-slate-300',
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function TextInput({
|
function TextInput(
|
||||||
autoComplete,
|
{
|
||||||
defaultValue,
|
defaultValue,
|
||||||
endIcon: EndIcon,
|
disabled,
|
||||||
errorMessage,
|
endIcon: EndIcon,
|
||||||
id: idParam,
|
errorMessage,
|
||||||
isDisabled,
|
id: idParam,
|
||||||
isLabelHidden = false,
|
isLabelHidden = false,
|
||||||
label,
|
label,
|
||||||
name,
|
startIcon: StartIcon,
|
||||||
placeholder,
|
type = 'text',
|
||||||
startIcon: StartIcon,
|
value,
|
||||||
type = 'text',
|
onChange,
|
||||||
value,
|
...props
|
||||||
onChange,
|
}: Props,
|
||||||
}: Props) {
|
ref: ForwardedRef<HTMLInputElement>,
|
||||||
|
) {
|
||||||
const hasError = errorMessage != null;
|
const hasError = errorMessage != null;
|
||||||
const generatedId = useId();
|
const generatedId = useId();
|
||||||
const id = idParam ?? generatedId;
|
const id = idParam ?? generatedId;
|
||||||
@ -68,21 +92,19 @@ export default function TextInput({
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<input
|
<input
|
||||||
|
ref={ref}
|
||||||
aria-describedby={hasError ? errorId : undefined}
|
aria-describedby={hasError ? errorId : undefined}
|
||||||
aria-invalid={hasError ? true : undefined}
|
aria-invalid={hasError ? true : undefined}
|
||||||
autoComplete={autoComplete}
|
|
||||||
className={clsx(
|
className={clsx(
|
||||||
'block w-full rounded-md sm:text-sm',
|
'block w-full rounded-md sm:text-sm',
|
||||||
StartIcon && 'pl-10',
|
StartIcon && 'pl-10',
|
||||||
EndIcon && 'pr-10',
|
EndIcon && 'pr-10',
|
||||||
stateClasses[state],
|
stateClasses[state],
|
||||||
isDisabled && 'bg-slate-100',
|
disabled && 'bg-slate-100',
|
||||||
)}
|
)}
|
||||||
defaultValue={defaultValue}
|
defaultValue={defaultValue}
|
||||||
disabled={isDisabled}
|
disabled={disabled}
|
||||||
id={id}
|
id={id}
|
||||||
name={name}
|
|
||||||
placeholder={placeholder}
|
|
||||||
type={type}
|
type={type}
|
||||||
value={value != null ? value : undefined}
|
value={value != null ? value : undefined}
|
||||||
onChange={(event) => {
|
onChange={(event) => {
|
||||||
@ -92,6 +114,7 @@ export default function TextInput({
|
|||||||
|
|
||||||
onChange(event.target.value, event);
|
onChange(event.target.value, event);
|
||||||
}}
|
}}
|
||||||
|
{...props}
|
||||||
/>
|
/>
|
||||||
{EndIcon && (
|
{EndIcon && (
|
||||||
<div className="pointer-events-none absolute inset-y-0 right-0 flex items-center pr-3">
|
<div className="pointer-events-none absolute inset-y-0 right-0 flex items-center pr-3">
|
||||||
@ -107,3 +130,5 @@ export default function TextInput({
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export default forwardRef(TextInput);
|
||||||
|
Reference in New Issue
Block a user