98 lines
3.0 KiB
TypeScript
98 lines
3.0 KiB
TypeScript
import type { ChangeEventHandler, FocusEventHandler, ReactNode, Ref } from 'react';
|
|
import type { ComponentSize } from './types';
|
|
|
|
type DatePickerKind = 'date' | 'datetime-local' | 'time';
|
|
type Layout = 'stacked' | 'inline';
|
|
|
|
export type DatePickerProps = {
|
|
label?: string;
|
|
placeholder?: string;
|
|
type: DatePickerKind;
|
|
size?: ComponentSize;
|
|
width?: ComponentSize;
|
|
layout?: Layout;
|
|
value: string;
|
|
name?: string;
|
|
onChange?: ChangeEventHandler<HTMLInputElement>;
|
|
onBlur?: FocusEventHandler<HTMLInputElement>;
|
|
inputRef?: Ref<HTMLInputElement>;
|
|
disabled?: boolean;
|
|
required?: boolean;
|
|
error?: string;
|
|
rightIcon?: ReactNode;
|
|
className?: string;
|
|
inputClassName?: string;
|
|
};
|
|
|
|
export function DatePicker({
|
|
label,
|
|
placeholder = '',
|
|
type,
|
|
size = 'md',
|
|
width = 'md',
|
|
layout = 'stacked',
|
|
value,
|
|
name,
|
|
onChange,
|
|
onBlur,
|
|
inputRef,
|
|
disabled = false,
|
|
required = false,
|
|
error,
|
|
rightIcon,
|
|
className = '',
|
|
inputClassName = '',
|
|
}: Readonly<DatePickerProps>) {
|
|
const containerWidthClass = {
|
|
sm: 'max-w-xs',
|
|
md: 'max-w-sm',
|
|
lg: 'max-w-md',
|
|
full: 'max-w-none',
|
|
}[width];
|
|
|
|
const inputSizeClass = {
|
|
sm: 'h-8 !text-xs',
|
|
md: 'h-10 text-sm',
|
|
lg: 'h-12 text-sm',
|
|
full: 'h-10 text-sm',
|
|
}[size];
|
|
|
|
const wrapperClass =
|
|
layout === 'inline' ? 'inline-flex w-auto items-center gap-2' : 'block w-full gap-1';
|
|
const labelClass = layout === 'inline' ? 'text-xs ui-body-secondary' : '';
|
|
const hasTrailingIcon = Boolean(rightIcon);
|
|
const inputWrapperClass = layout === 'inline' ? 'relative' : 'relative mt-1';
|
|
|
|
return (
|
|
<label
|
|
className={`${wrapperClass} text-sm font-medium ${disabled ? 'ui-label-disabled' : 'ui-label'} ${containerWidthClass} ${className}`.trim()}
|
|
>
|
|
{label ? <span className={labelClass}>{label}</span> : null}
|
|
<div className={inputWrapperClass}>
|
|
<input
|
|
type={type}
|
|
value={value}
|
|
name={name}
|
|
onChange={onChange}
|
|
onBlur={onBlur}
|
|
ref={inputRef}
|
|
placeholder={placeholder}
|
|
disabled={disabled}
|
|
required={required}
|
|
className={`field w-full ${hasTrailingIcon ? 'pr-10' : ''} ${inputSizeClass} ${error ? 'border-red-400/70 focus:border-red-400 focus:ring-red-400/30' : ''} ${inputClassName}`.trim()}
|
|
/>
|
|
{rightIcon ? (
|
|
<span className="pointer-events-none absolute inset-y-0 right-2 inline-flex items-center justify-center px-1">
|
|
{rightIcon}
|
|
</span>
|
|
) : null}
|
|
</div>
|
|
{error ? (
|
|
<span className="mt-1 block text-xs" style={{ color: 'var(--error-text)' }}>
|
|
{error}
|
|
</span>
|
|
) : null}
|
|
</label>
|
|
);
|
|
}
|