mirror of
https://github.com/supabase/supabase.git
synced 2026-10-07 18:35:07 +03:00
## Problem Our `<Button>` component breaks the default `button` contract by redefining the `type` prop to set its variant (`primary`, `default`, etc) instead of the button type (`submit`, `button`, etc). This is confusing and forces to write more code when using it with shadcn components that expect/inject the standard button props. ## Solution - rename the `type` prop to `variant` - rename the `htmlType` prop to `type` - propagate the changes where necessary - format code ## How to test As this is just prop renaming, if it builds it's ok --------- Co-authored-by: Ivan Vasilov <vasilov.ivan@gmail.com>
164 lines
6.1 KiB
TypeScript
164 lines
6.1 KiB
TypeScript
import { useFlag } from 'common'
|
|
import { CheckIcon, ChevronsUpDown, Globe } from 'lucide-react'
|
|
import { useId, useMemo, useState } from 'react'
|
|
import {
|
|
Button,
|
|
Card,
|
|
CardContent,
|
|
cn,
|
|
Command,
|
|
CommandEmpty,
|
|
CommandGroup,
|
|
CommandInput,
|
|
CommandItem,
|
|
CommandList,
|
|
Popover,
|
|
PopoverContent,
|
|
PopoverTrigger,
|
|
ScrollArea,
|
|
} from 'ui'
|
|
import { FormItemLayout } from 'ui-patterns/form/FormItemLayout/FormItemLayout'
|
|
import {
|
|
PageSection,
|
|
PageSectionContent,
|
|
PageSectionDescription,
|
|
PageSectionMeta,
|
|
PageSectionSummary,
|
|
PageSectionTitle,
|
|
} from 'ui-patterns/PageSection'
|
|
|
|
import { findTimezoneByIana, TIMEZONES_BY_IANA } from '@/lib/constants/timezones'
|
|
import { useTimezone } from '@/lib/datetime'
|
|
import { guessLocalTimezone } from '@/lib/dayjs'
|
|
import { useTrack } from '@/lib/telemetry/track'
|
|
|
|
const AUTO_OPTION_VALUE = '__auto__'
|
|
|
|
export const TimezoneSettings = () => {
|
|
const timezonePickerEnabled = useFlag('timezonePicker')
|
|
const { timezone, storedTimezone, setTimezone, isAutoDetected } = useTimezone()
|
|
const track = useTrack()
|
|
const [open, setOpen] = useState(false)
|
|
const listboxId = useId()
|
|
|
|
// Browser timezone is captured once and stays stable even when the user has
|
|
// overridden the dashboard timezone — that's the value the "Auto detect"
|
|
// option will revert to.
|
|
const browserTimezone = useMemo(() => guessLocalTimezone(), [])
|
|
|
|
const triggerLabel = useMemo(() => findTimezoneByIana(timezone)?.text ?? timezone, [timezone])
|
|
|
|
if (!timezonePickerEnabled) return null
|
|
|
|
const handleSelect = (nextStored: string) => {
|
|
setTimezone(nextStored)
|
|
const resolvedNext = nextStored || guessLocalTimezone()
|
|
track('timezone_picker_clicked', {
|
|
previousTimezone: timezone,
|
|
nextTimezone: resolvedNext,
|
|
isAutoDetected: nextStored === '',
|
|
source: 'account_preferences',
|
|
})
|
|
setOpen(false)
|
|
}
|
|
|
|
return (
|
|
<PageSection>
|
|
<PageSectionMeta>
|
|
<PageSectionSummary>
|
|
<PageSectionTitle>Timezone</PageSectionTitle>
|
|
<PageSectionDescription>
|
|
Choose how dates and times in logs and other dashboard surfaces are displayed.
|
|
</PageSectionDescription>
|
|
</PageSectionSummary>
|
|
</PageSectionMeta>
|
|
<PageSectionContent>
|
|
<Card>
|
|
<CardContent>
|
|
<FormItemLayout
|
|
isReactForm={false}
|
|
label="Display timezone"
|
|
layout="flex-row-reverse"
|
|
description={
|
|
isAutoDetected
|
|
? `Auto detected from your browser (${browserTimezone}).`
|
|
: 'Pick "Auto detect" to follow your browser timezone again.'
|
|
}
|
|
>
|
|
<Popover open={open} onOpenChange={setOpen}>
|
|
<PopoverTrigger asChild>
|
|
<Button
|
|
role="combobox"
|
|
aria-expanded={open}
|
|
aria-controls={listboxId}
|
|
className="w-full justify-between"
|
|
variant="default"
|
|
size="small"
|
|
icon={<Globe />}
|
|
iconRight={<ChevronsUpDown size={14} strokeWidth={1.5} />}
|
|
>
|
|
<span className="truncate text-left">
|
|
{isAutoDetected ? `Auto detect (${timezone})` : triggerLabel}
|
|
</span>
|
|
</Button>
|
|
</PopoverTrigger>
|
|
<PopoverContent id={listboxId} className="w-[--radix-popover-trigger-width] p-0">
|
|
<Command>
|
|
<CommandInput placeholder="Search timezone..." className="h-9" />
|
|
<CommandList>
|
|
<CommandEmpty>No timezones found</CommandEmpty>
|
|
<CommandGroup>
|
|
<ScrollArea className="h-72">
|
|
<CommandItem
|
|
key={AUTO_OPTION_VALUE}
|
|
value={`Auto detect ${browserTimezone}`}
|
|
onSelect={() => handleSelect('')}
|
|
>
|
|
<div className="flex flex-col">
|
|
<span>Auto detect</span>
|
|
<span className="text-xs text-foreground-lighter">
|
|
{browserTimezone}
|
|
</span>
|
|
</div>
|
|
<CheckIcon
|
|
className={cn(
|
|
'ml-auto h-4 w-4',
|
|
isAutoDetected ? 'opacity-100' : 'opacity-0'
|
|
)}
|
|
/>
|
|
</CommandItem>
|
|
{TIMEZONES_BY_IANA.map((entry) => {
|
|
const ianaName = entry.utc[0]
|
|
const isSelected = !isAutoDetected && storedTimezone === ianaName
|
|
return (
|
|
<CommandItem
|
|
key={ianaName}
|
|
// CommandItem matches against the `value` prop for the input filter — include
|
|
// both the human label and the IANA name so search works for either.
|
|
value={`${entry.text} ${ianaName}`}
|
|
onSelect={() => handleSelect(ianaName)}
|
|
>
|
|
{entry.text}
|
|
<CheckIcon
|
|
className={cn(
|
|
'ml-auto h-4 w-4',
|
|
isSelected ? 'opacity-100' : 'opacity-0'
|
|
)}
|
|
/>
|
|
</CommandItem>
|
|
)
|
|
})}
|
|
</ScrollArea>
|
|
</CommandGroup>
|
|
</CommandList>
|
|
</Command>
|
|
</PopoverContent>
|
|
</Popover>
|
|
</FormItemLayout>
|
|
</CardContent>
|
|
</Card>
|
|
</PageSectionContent>
|
|
</PageSection>
|
|
)
|
|
}
|