1
0
mirror of https://github.com/pocket-id/pocket-id.git synced 2026-02-04 13:21:45 +00:00

fix: add back month and year selection for date picker

This commit is contained in:
Elias Schneider
2025-05-23 11:56:08 +02:00
parent 869c4c5871
commit 6c35570e78
4 changed files with 110 additions and 39 deletions

View File

@@ -68,18 +68,23 @@
<div class="w-full" {...restProps}>
<Popover.Root bind:open>
<Popover.Trigger class="w-full">
<Button
{id}
variant="outline"
class={cn('w-full justify-start text-left font-normal', !value && 'text-muted-foreground')}
aria-label={m.select_a_date()}
>
<CalendarIcon class="mr-2 size-4" />
{calendarDisplayDate
? df.format(calendarDisplayDate.toDate(getLocalTimeZone()))
: m.select_a_date()}
</Button>
<Popover.Trigger {id} class="w-full" >
{#snippet child({ props })}
<Button
{...props}
variant="outline"
class={cn(
'w-full justify-start text-left font-normal',
!value && 'text-muted-foreground'
)}
aria-label={m.select_a_date()}
>
<CalendarIcon class="mr-2 size-4" />
{calendarDisplayDate
? df.format(calendarDisplayDate.toDate(getLocalTimeZone()))
: m.select_a_date()}
</Button>
{/snippet}
</Popover.Trigger>
<Popover.Content class="w-auto p-0" align="start">
<Calendar

View File

@@ -1,35 +1,99 @@
<script lang="ts">
import { Calendar as CalendarPrimitive } from 'bits-ui';
import * as Calendar from './index.js';
import { cn, type WithoutChildrenOrChild } from '$lib/utils/style.js';
import * as Calendar from '$lib/components/ui/calendar/index.js';
import * as Select from '$lib/components/ui/select/index.js';
import { cn } from '$lib/utils/style';
import { CalendarDate, DateFormatter, getLocalTimeZone, today } from '@internationalized/date';
import { Calendar as CalendarPrimitive, type WithoutChildrenOrChild } from 'bits-ui';
let {
ref = $bindable(null),
value = $bindable(),
placeholder = $bindable(),
class: className,
weekdayFormat = 'short',
...restProps
}: WithoutChildrenOrChild<CalendarPrimitive.RootProps> = $props();
placeholder = $bindable()
}: WithoutChildrenOrChild<CalendarPrimitive.RootProps> & {
value: CalendarDate | undefined;
} = $props();
const currentDate = today(getLocalTimeZone());
const monthFmt = new DateFormatter('en-US', {
month: 'long'
});
const monthOptions = Array.from({ length: 12 }, (_, i) => {
const month = currentDate.set({ month: i + 1 });
return {
value: month.month,
label: monthFmt.format(month.toDate(getLocalTimeZone()))
};
});
const yearOptions = Array.from({ length: 100 }, (_, i) => ({
label: String(new Date().getFullYear() + i),
value: new Date().getFullYear() + i
}));
const defaultYear = $derived(
placeholder ? { value: placeholder.year, label: String(placeholder.year) } : undefined
);
const defaultMonth = $derived(
placeholder
? {
value: placeholder.month,
label: monthFmt.format(placeholder.toDate(getLocalTimeZone()))
}
: undefined
);
const monthLabel = $derived(
monthOptions.find((m) => m.value === defaultMonth?.value)?.label ?? 'Select a month'
);
</script>
<!--
Discriminated Unions + Destructing (required for bindable) do not
get along, so we shut typescript up by casting `value` to `never`.
-->
<CalendarPrimitive.Root
bind:value={value as never}
bind:ref
type="single"
weekdayFormat="short"
class={cn('rounded-md border p-3')}
bind:value
bind:placeholder
{weekdayFormat}
class={cn('p-3', className)}
{...restProps}
>
{#snippet children({ months, weekdays })}
<Calendar.Header>
<Calendar.PrevButton />
<Calendar.Heading />
<Calendar.NextButton />
<Calendar.Header class="flex w-full items-center justify-between gap-2">
<Select.Root
type="single"
value={`${defaultMonth?.value}`}
onValueChange={(v) => {
if (!placeholder) return;
if (v === `${placeholder.month}`) return;
placeholder = placeholder.set({ month: Number.parseInt(v) });
}}
>
<Select.Trigger aria-label="Select month" class="w-[60%]">
{monthLabel}
</Select.Trigger>
<Select.Content class="max-h-[200px] overflow-y-auto">
{#each monthOptions as { value, label } (value)}
<Select.Item value={`${value}`} {label} />
{/each}
</Select.Content>
</Select.Root>
<Select.Root
type="single"
value={`${defaultYear?.value}`}
onValueChange={(v) => {
if (!v || !placeholder) return;
if (v === `${placeholder?.year}`) return;
placeholder = placeholder.set({ year: Number.parseInt(v) });
}}
>
<Select.Trigger aria-label="Select year" class="w-[40%]">
{defaultYear?.label ?? 'Select year'}
</Select.Trigger>
<Select.Content class="max-h-[200px] overflow-y-auto">
{#each yearOptions as { value, label } (value)}
<Select.Item value={`${value}`} {label} />
{/each}
</Select.Content>
</Select.Root>
</Calendar.Header>
<Calendar.Months>
{#each months as month (month)}
@@ -47,7 +111,7 @@ get along, so we shut typescript up by casting `value` to `never`.
{#each month.weeks as weekDates (weekDates)}
<Calendar.GridRow class="mt-2 w-full">
{#each weekDates as date (date)}
<Calendar.Cell {date} month={month.value}>
<Calendar.Cell class="select-none" {date} month={month.value}>
<Calendar.Day />
</Calendar.Cell>
{/each}

View File

@@ -19,7 +19,7 @@
</script>
<Dialog.Root open={!!apiKeyResponse} {onOpenChange}>
<Dialog.Content class="max-w-md">
<Dialog.Content class="max-w-md" onOpenAutoFocus={(e) => e.preventDefault()}>
<Dialog.Header>
<Dialog.Title>{m.api_key_created()}</Dialog.Title>
<Dialog.Description>
@@ -30,7 +30,6 @@
<div>
<div class="mb-2 font-medium">{m.name()}</div>
<p class="text-muted-foreground">{apiKeyResponse.apiKey.name}</p>
{#if apiKeyResponse.apiKey.description}
<div class="mt-4 mb-2 font-medium">{m.description()}</div>
<p class="text-muted-foreground">{apiKeyResponse.apiKey.description}</p>

View File

@@ -19,7 +19,7 @@ test.describe("API Key Management", () => {
// Choose the date
const currentDate = new Date();
await page.getByLabel("Expires At").click();
await page.getByRole("button", { name: "Select a date" }).click();
await page.getByLabel("Select year").click();
// Select the next year
await page.getByText((currentDate.getFullYear() + 1).toString()).click();
@@ -45,7 +45,10 @@ test.describe("API Key Management", () => {
expect(token?.length).toBe(32);
// Close the dialog
await page.getByRole("button", { name: "Close" }).click();
await page
.getByRole("button", { name: "Close", exact: true })
.nth(1)
.click();
await page.reload();