Skip to content

Commit 0c501d7

Browse files
authored
chore: consolidate calendar pickers (supabase#40052)
* chore: update react-day-picker * fix(design system): date picker demos * refactor(date picker): change logs date picker to use react-day-picker * refactor(date pickers): change remaining date pickers to use react-day-picker * cleanup(date pickers): minor code cleanup * fix(date picker): fix behavior for single day selection
1 parent 74486d3 commit 0c501d7

File tree

20 files changed

+300
-841
lines changed

20 files changed

+300
-841
lines changed

apps/design-system/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
"next-contentlayer2": "0.4.6",
3030
"next-themes": "^0.3.0",
3131
"react": "catalog:",
32+
"react-day-picker": "^9.11.1",
3233
"react-docgen": "^7.0.3",
3334
"react-dom": "catalog:",
3435
"react-hook-form": "^7.45.0",
@@ -53,8 +54,8 @@
5354
"@types/lodash.template": "4.5.0",
5455
"@types/react": "catalog:",
5556
"@types/react-dom": "catalog:",
56-
"config": "workspace:^",
5757
"concurrently": "^8.2.2",
58+
"config": "workspace:^",
5859
"mdast-util-toc": "^6.1.1",
5960
"postcss": "^8.5.3",
6061
"rimraf": "^4.1.3",

apps/design-system/registry/default/example/date-picker-demo.tsx

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,17 @@
11
'use client'
22

3-
import * as React from 'react'
43
import { format } from 'date-fns'
54
import { Calendar as CalendarIcon } from 'lucide-react'
5+
import * as React from 'react'
66

77
import { cn } from '@/lib/utils'
8-
import { Button } from 'ui'
9-
import { Calendar } from 'ui'
10-
import { Popover_Shadcn_, PopoverContent_Shadcn_, PopoverTrigger_Shadcn_ } from 'ui'
8+
import {
9+
Button,
10+
Calendar,
11+
Popover_Shadcn_,
12+
PopoverContent_Shadcn_,
13+
PopoverTrigger_Shadcn_,
14+
} from 'ui'
1115

1216
export default function DatePickerDemo() {
1317
const [date, setDate] = React.useState<Date>()
@@ -21,8 +25,8 @@ export default function DatePickerDemo() {
2125
'w-[280px] justify-start text-left font-normal',
2226
!date && 'text-muted-foreground'
2327
)}
28+
icon={<CalendarIcon className="h-4 w-4" />}
2429
>
25-
<CalendarIcon className="mr-2 h-4 w-4" />
2630
{date ? format(date, 'PPP') : <span>Pick a date</span>}
2731
</Button>
2832
</PopoverTrigger_Shadcn_>

apps/design-system/registry/default/example/date-picker-form.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,9 +62,9 @@ export default function DatePickerForm() {
6262
'w-[240px] pl-3 text-left font-normal',
6363
!field.value && 'text-muted-foreground'
6464
)}
65+
icon={<CalendarIcon className="h-4 w-4 opacity-50" />}
6566
>
6667
{field.value ? format(field.value, 'PPP') : <span>Pick a date</span>}
67-
<CalendarIcon className="ml-auto h-4 w-4 opacity-50" />
6868
</Button>
6969
</FormControl_Shadcn_>
7070
</PopoverTrigger_Shadcn_>

apps/design-system/registry/default/example/date-picker-with-presets.tsx

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
11
'use client'
22

3-
import * as React from 'react'
43
import { addDays, format } from 'date-fns'
54
import { Calendar as CalendarIcon } from 'lucide-react'
5+
import * as React from 'react'
66

77
import { cn } from '@/lib/utils'
8-
import { Button } from 'ui'
9-
import { Calendar } from 'ui'
10-
import { Popover_Shadcn_, PopoverContent_Shadcn_, PopoverTrigger_Shadcn_ } from 'ui'
118
import {
9+
Button,
10+
Calendar,
11+
Popover_Shadcn_,
12+
PopoverContent_Shadcn_,
13+
PopoverTrigger_Shadcn_,
1214
Select_Shadcn_,
1315
SelectContent_Shadcn_,
1416
SelectItem_Shadcn_,
@@ -28,8 +30,8 @@ export default function DatePickerWithPresets() {
2830
'w-[280px] justify-start text-left font-normal',
2931
!date && 'text-muted-foreground'
3032
)}
33+
icon={<CalendarIcon className="h-4 w-4" />}
3134
>
32-
<CalendarIcon className="mr-2 h-4 w-4" />
3335
{date ? format(date, 'PPP') : <span>Pick a date</span>}
3436
</Button>
3537
</PopoverTrigger_Shadcn_>

apps/design-system/registry/default/example/date-picker-with-range.tsx

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,18 @@
11
'use client'
22

3-
import * as React from 'react'
43
import { addDays, format } from 'date-fns'
54
import { Calendar as CalendarIcon } from 'lucide-react'
6-
// @ts-expect-error
5+
import * as React from 'react'
76
import { DateRange } from 'react-day-picker'
7+
88
import { cn } from '@/lib/utils'
9-
import { Button } from 'ui'
10-
import { Calendar } from 'ui'
11-
import { Popover_Shadcn_, PopoverContent_Shadcn_, PopoverTrigger_Shadcn_ } from 'ui'
9+
import {
10+
Button,
11+
Calendar,
12+
Popover_Shadcn_,
13+
PopoverContent_Shadcn_,
14+
PopoverTrigger_Shadcn_,
15+
} from 'ui'
1216

1317
export default function DatePickerWithRange({ className }: React.HTMLAttributes<HTMLDivElement>) {
1418
const [date, setDate] = React.useState<DateRange | undefined>({
@@ -27,8 +31,8 @@ export default function DatePickerWithRange({ className }: React.HTMLAttributes<
2731
'w-[300px] justify-start text-left font-normal',
2832
!date && 'text-muted-foreground'
2933
)}
34+
icon={<CalendarIcon className="h-4 w-4" />}
3035
>
31-
<CalendarIcon className="mr-2 h-4 w-4" />
3236
{date?.from ? (
3337
date.to ? (
3438
<>

apps/studio/components/interfaces/Account/AccessTokens/NewAccessTokenDialog.tsx

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { zodResolver } from '@hookform/resolvers/zod'
22
import dayjs from 'dayjs'
33
import { ExternalLink } from 'lucide-react'
44
import { useState } from 'react'
5-
import { type SubmitHandler, useForm } from 'react-hook-form'
5+
import { useForm, type SubmitHandler } from 'react-hook-form'
66
import { toast } from 'sonner'
77
import { z } from 'zod'
88

@@ -214,16 +214,13 @@ export const NewAccessTokenDialog = ({
214214
selectsRange={false}
215215
triggerButtonSize="small"
216216
contentSide="top"
217+
to={customExpiryDate?.date}
217218
minDate={new Date()}
218219
maxDate={dayjs().add(1, 'year').toDate()}
219220
onChange={(date) => {
220221
if (date.to) handleCustomDateChange({ date: date.to })
221222
}}
222-
>
223-
{customExpiryDate
224-
? `${dayjs(customExpiryDate.date).format('DD MMM, HH:mm')}`
225-
: 'Select date'}
226-
</DatePicker>
223+
/>
227224
)}
228225
</div>
229226
{field.value === NON_EXPIRING_TOKEN_VALUE && (

apps/studio/components/interfaces/Database/Backups/PITR/PITRForm.tsx

Lines changed: 22 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
1-
import { format } from 'date-fns'
21
import dayjs from 'dayjs'
3-
import { ChevronLeft, ChevronRight, HelpCircle } from 'lucide-react'
2+
import { HelpCircle } from 'lucide-react'
43
import { useMemo, useState } from 'react'
5-
import DatePicker from 'react-datepicker'
64

75
import { ButtonTooltip } from 'components/ui/ButtonTooltip'
86
import { FormPanel } from 'components/ui/Forms/FormPanel'
97
import InformationBox from 'components/ui/InformationBox'
8+
import { Calendar, cn } from 'ui'
109
import { Timezone } from './PITR.types'
1110
import {
1211
constrainDateToRange,
@@ -41,6 +40,8 @@ export function PITRForm({
4140
const latestAvailableBackup = dayjs
4241
.unix(latestAvailableBackupUnix ?? 0)
4342
.tz(selectedTimezone.utc[0])
43+
const earliestAvailableBackupAsDate = earliestAvailableBackup.toDate()
44+
const latestAvailableBackupAsDate = latestAvailableBackup.toDate()
4445

4546
const [selectedDateRaw, setSelectedDateRaw] = useState<Date>(latestAvailableBackup.toDate())
4647

@@ -125,51 +126,25 @@ export function PITRForm({
125126
>
126127
<div className="flex justify-between px-4 md:px-10 py-6 space-x-10">
127128
<div className="w-1/3 space-y-2">
128-
<DatePicker
129-
inline
129+
<Calendar
130+
mode="single"
131+
required={true}
130132
selected={selectedDateRaw}
131-
onChange={onUpdateDate}
132-
dayClassName={() => 'cursor-pointer'}
133-
minDate={earliestAvailableBackup.toDate()}
134-
maxDate={latestAvailableBackup.toDate()}
135-
highlightDates={availableDates.map((date) => date.toDate())}
136-
renderCustomHeader={({
137-
date,
138-
decreaseMonth,
139-
increaseMonth,
140-
prevMonthButtonDisabled,
141-
nextMonthButtonDisabled,
142-
}) => (
143-
<div className="flex items-center justify-between px-2 py-2">
144-
<div className="flex w-full items-center justify-between">
145-
<button
146-
onClick={decreaseMonth}
147-
disabled={prevMonthButtonDisabled}
148-
type="button"
149-
className={`
150-
${prevMonthButtonDisabled && 'cursor-not-allowed opacity-50'}
151-
text-foreground-light hover:text-foreground focus:outline-none
152-
`}
153-
>
154-
<ChevronLeft size={16} strokeWidth={2} />
155-
</button>
156-
<span className="text-foreground-light text-sm">
157-
{format(date, 'MMMM yyyy')}
158-
</span>
159-
<button
160-
onClick={increaseMonth}
161-
disabled={nextMonthButtonDisabled}
162-
type="button"
163-
className={`
164-
${nextMonthButtonDisabled && 'cursor-not-allowed opacity-50'}
165-
text-foreground-light hover:text-foreground focus:outline-none
166-
`}
167-
>
168-
<ChevronRight size={16} strokeWidth={2} />
169-
</button>
170-
</div>
171-
</div>
172-
)}
133+
onSelect={onUpdateDate}
134+
defaultMonth={latestAvailableBackupAsDate}
135+
startMonth={earliestAvailableBackupAsDate}
136+
endMonth={latestAvailableBackupAsDate}
137+
disabled={[
138+
{ before: earliestAvailableBackupAsDate },
139+
{ after: latestAvailableBackupAsDate },
140+
]}
141+
classNames={{
142+
day: cn(
143+
'[&:not(:has(:disabled))]:border [&:not(:has(:disabled))]:border-stronger [&:not(:last-child)]:border-r-0 [&:not(:has(:disabled))]:bg-overlay-hover',
144+
'rounded-none'
145+
),
146+
selected: '!bg-brand-500',
147+
}}
173148
/>
174149
{availableDates.length > 1 && (
175150
<div className="flex items-center space-x-2">

0 commit comments

Comments
 (0)