date picker updates

This commit is contained in:
Nate Kelley 2025-04-02 17:01:11 -06:00
parent 0d7159f569
commit fa6e5b88d4
No known key found for this signature in database
GPG Key ID: FD90372AB8D98B4F
4 changed files with 111 additions and 11 deletions

View File

@ -210,7 +210,7 @@ const LinkExpiration: React.FC<{
return createDayjsDate(new Date()).add(2, 'year');
}, []);
const onSelect: SelectSingleEventHandler = useMemoizedFn((date) => {
const onSelect = useMemoizedFn((date: Date | undefined) => {
onChangeLinkExpiry(date || null);
});
@ -219,7 +219,7 @@ const LinkExpiration: React.FC<{
<Text truncate>Link expiration</Text>
<DatePicker
selected={linkExpiry || new Date()}
selected={linkExpiry || undefined}
onSelect={onSelect}
mode="single"
dateFormat={dateFormat}

View File

@ -26,7 +26,7 @@ function Calendar({ className, classNames, showOutsideDays = true, ...props }: C
nav_button_next: 'absolute right-1',
table: 'w-full border-collapse space-y-1',
head_row: 'flex',
head_cell: 'text-muted-foreground rounded-md w-8 font-normal text-[0.8rem]',
head_cell: 'text-gray-light rounded-md w-8 font-normal text-[0.8rem]',
row: 'flex w-full mt-2',
cell: cn(
'relative p-0 text-center text-sm focus-within:relative focus-within:z-20 [&:has([aria-selected])]:bg-accent [&:has([aria-selected].day-outside)]:bg-accent/50 [&:has([aria-selected].day-range-end)]:rounded-r-md',
@ -35,16 +35,16 @@ function Calendar({ className, classNames, showOutsideDays = true, ...props }: C
: '[&:has([aria-selected])]:rounded-md'
),
day: cn(
'h-8 w-8 p-0 cursor-pointer font-normal aria-selected:opacity-100 hover:bg-item-hover rounded'
'h-8 w-8 p-0 cursor-pointer font-normal aria-selected:opacity-100 hover:bg-item-hover rounded flex items-center justify-center'
),
day_range_start: 'day-range-start',
day_range_end: 'day-range-end',
day_selected:
'bg-primary text-background hover:bg-primary hover:text-primary-foreground focus:bg-primary focus:text-primary-foreground',
'bg-primary swag text-background hover:bg-primary hover:text-background focus:bg-primary focus:text-background',
day_today: 'bg-accent text-accent-foreground',
day_outside:
'day-outside text-muted-foreground aria-selected:bg-accent/50 aria-selected:text-muted-foreground',
day_disabled: 'text-muted-foreground opacity-50 cursor-not-allowed!',
'day-outside text-gray-light aria-selected:bg-accent/50 aria-selected:text-gray-light cursor-not-allowed!',
day_disabled: 'text-gray-light opacity-50 cursor-not-allowed!',
day_range_middle: 'aria-selected:bg-accent aria-selected:text-accent-foreground',
day_hidden: 'invisible',
...classNames

View File

@ -0,0 +1,99 @@
import type { Meta, StoryObj } from '@storybook/react';
import { DatePicker } from './DatePicker';
import { action } from '@storybook/addon-actions';
import { useState } from 'react';
const meta: Meta<typeof DatePicker> = {
title: 'UI/Date/DatePicker',
component: DatePicker,
tags: ['autodocs'],
argTypes: {
dateFormat: {
control: 'text',
description: 'Format for displaying the selected date'
},
placeholder: {
control: 'text',
description: 'Text to display when no date is selected'
},
selected: {
control: 'date',
description: 'The currently selected date'
},
onSelect: {
action: 'date selected',
description: 'Callback when a date is selected'
},
mode: {
control: 'select',
options: ['single', 'range', 'multiple'],
description: 'Selection mode for the calendar'
},
disabled: {
control: 'boolean',
description: 'Whether the date picker is disabled'
},
fromDate: {
control: 'date',
description: 'Minimum selectable date'
},
toDate: {
control: 'date',
description: 'Maximum selectable date'
}
}
};
export default meta;
type Story = StoryObj<typeof DatePicker>;
// Create interactive wrappers for each story
const InteractiveDatePicker = ({ initialDate, ...args }: any) => {
const [date, setDate] = useState<Date | undefined>(initialDate);
const handleDateSelect = (newDate: Date | undefined) => {
setDate(newDate);
action('onSelect')(newDate);
};
return <DatePicker {...args} selected={date} onSelect={handleDateSelect} />;
};
export const Default: Story = {
render: (args) => <InteractiveDatePicker {...args} initialDate={undefined} />,
args: {
placeholder: 'Select a date'
}
};
export const WithSelectedDate: Story = {
render: (args) => <InteractiveDatePicker {...args} initialDate={new Date()} />,
args: {
placeholder: 'Select a date'
}
};
export const CustomDateFormat: Story = {
render: (args) => <InteractiveDatePicker {...args} initialDate={new Date()} />,
args: {
dateFormat: 'MMMM dd, yyyy',
placeholder: 'Select a date'
}
};
export const Disabled: Story = {
render: (args) => <InteractiveDatePicker {...args} initialDate={new Date()} />,
args: {
disabled: true,
placeholder: 'Date selection disabled'
}
};
export const WithDateRange: Story = {
render: (args) => <InteractiveDatePicker {...args} initialDate={undefined} />,
args: {
fromDate: new Date(new Date().setDate(new Date().getDate() - 30)),
toDate: new Date(new Date().setDate(new Date().getDate() + 30)),
placeholder: 'Select within 30 day range'
}
};

View File

@ -12,18 +12,19 @@ import {
PopoverTrigger
} from '@/components/ui/tooltip/PopoverBase';
import { formatDate } from '@/lib';
import { DayPickerProps } from 'react-day-picker';
export type DatePickerProps = CalendarProps & {
export type DatePickerProps = Omit<CalendarProps, 'selected'> & {
dateFormat?: string;
placeholder?: string;
selected?: Date;
onSelect: (date: Date | undefined) => void;
};
export function DatePicker({
dateFormat = 'lll',
placeholder = 'Pick a date',
selected,
onSelect,
...props
}: DatePickerProps) {
return (
@ -47,7 +48,7 @@ export function DatePicker({
</Button>
</PopoverTrigger>
<PopoverContent className="w-auto p-0">
<Calendar {...props} />
<Calendar mode="single" selected={selected} onSelect={onSelect} initialFocus />
</PopoverContent>
</Popover>
);