{
+ setValue(event.target.value);
+ args.onChange?.(event);
+ }}
+ />
+ );
+ },
+};
+
+export const Error: Story = {
+ args: {
+ type: 'datetime-local',
+ label: 'Schedule at',
+ value: '',
+ error: 'Pick a valid future date and time',
+ },
+};
+
+export const Disabled: Story = {
+ args: {
+ type: 'datetime-local',
+ label: 'Published at',
+ value: '2031-05-20T14:30',
+ disabled: true,
+ },
+};
+
+export const SizeMatrix: Story = {
+ args: {
+ type: 'datetime-local',
+ label: 'Schedule at',
+ },
+ render: (args) => {
+ const [value, setValue] = useState('2031-05-20T14:30');
+ return (
+
+ setValue(event.target.value)}
+ />
+ setValue(event.target.value)}
+ />
+ setValue(event.target.value)}
+ />
+ setValue(event.target.value)}
+ />
+
+ );
+ },
+};
diff --git a/src/components/DatePicker.tsx b/src/components/DatePicker.tsx
new file mode 100644
index 0000000..e8185ee
--- /dev/null
+++ b/src/components/DatePicker.tsx
@@ -0,0 +1,95 @@
+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;
+ layout?: Layout;
+ value: string;
+ name?: string;
+ onChange?: ChangeEventHandler;
+ onBlur?: FocusEventHandler;
+ inputRef?: Ref;
+ disabled?: boolean;
+ required?: boolean;
+ error?: string;
+ rightIcon?: ReactNode;
+ className?: string;
+ inputClassName?: string;
+};
+
+export function DatePicker({
+ label,
+ placeholder = '',
+ type,
+ size = 'md',
+ layout = 'stacked',
+ value,
+ name,
+ onChange,
+ onBlur,
+ inputRef,
+ disabled = false,
+ required = false,
+ error,
+ rightIcon,
+ className = '',
+ inputClassName = '',
+}: Readonly) {
+ const containerSizeClass = {
+ sm: 'max-w-xs',
+ md: 'max-w-sm',
+ lg: 'max-w-md',
+ full: 'max-w-none',
+ }[size];
+
+ 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 (
+
+ );
+}
diff --git a/src/index.ts b/src/index.ts
index adec647..e4fbd03 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -1,5 +1,6 @@
export { Button } from './components/Button';
export { Chip } from './components/Chip';
+export { DatePicker } from './components/DatePicker';
export { Dropdown } from './components/Dropdown';
export { Form } from './components/Form';
export { InputField } from './components/InputField';
@@ -8,5 +9,6 @@ export { SidebarNavItem } from './components/SidebarNavItem';
export { Table } from './components/Table';
export type { TableHeader } from './components/Table';
+export type { DatePickerProps } from './components/DatePicker';
export type { ComponentSize } from './components/types';
export type { SortDirection, SortState } from './types/sort';