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

View File

@ -26,7 +26,7 @@ function Calendar({ className, classNames, showOutsideDays = true, ...props }: C
nav_button_next: 'absolute right-1', nav_button_next: 'absolute right-1',
table: 'w-full border-collapse space-y-1', table: 'w-full border-collapse space-y-1',
head_row: 'flex', 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', row: 'flex w-full mt-2',
cell: cn( 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', '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' : '[&:has([aria-selected])]:rounded-md'
), ),
day: cn( 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_start: 'day-range-start',
day_range_end: 'day-range-end', day_range_end: 'day-range-end',
day_selected: 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_today: 'bg-accent text-accent-foreground',
day_outside: day_outside:
'day-outside text-muted-foreground aria-selected:bg-accent/50 aria-selected:text-muted-foreground', 'day-outside text-gray-light aria-selected:bg-accent/50 aria-selected:text-gray-light cursor-not-allowed!',
day_disabled: 'text-muted-foreground opacity-50 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_range_middle: 'aria-selected:bg-accent aria-selected:text-accent-foreground',
day_hidden: 'invisible', day_hidden: 'invisible',
...classNames ...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 PopoverTrigger
} from '@/components/ui/tooltip/PopoverBase'; } from '@/components/ui/tooltip/PopoverBase';
import { formatDate } from '@/lib'; import { formatDate } from '@/lib';
import { DayPickerProps } from 'react-day-picker';
export type DatePickerProps = CalendarProps & { export type DatePickerProps = Omit<CalendarProps, 'selected'> & {
dateFormat?: string; dateFormat?: string;
placeholder?: string; placeholder?: string;
selected?: Date;
onSelect: (date: Date | undefined) => void;
}; };
export function DatePicker({ export function DatePicker({
dateFormat = 'lll', dateFormat = 'lll',
placeholder = 'Pick a date', placeholder = 'Pick a date',
selected, selected,
onSelect,
...props ...props
}: DatePickerProps) { }: DatePickerProps) {
return ( return (
@ -47,7 +48,7 @@ export function DatePicker({
</Button> </Button>
</PopoverTrigger> </PopoverTrigger>
<PopoverContent className="w-auto p-0"> <PopoverContent className="w-auto p-0">
<Calendar {...props} /> <Calendar mode="single" selected={selected} onSelect={onSelect} initialFocus />
</PopoverContent> </PopoverContent>
</Popover> </Popover>
); );