mirror of
https://github.com/pocket-id/pocket-id.git
synced 2025-12-06 13:12:58 +03:00
fix: add back month and year selection for date picker
This commit is contained in:
@@ -68,18 +68,23 @@
|
|||||||
|
|
||||||
<div class="w-full" {...restProps}>
|
<div class="w-full" {...restProps}>
|
||||||
<Popover.Root bind:open>
|
<Popover.Root bind:open>
|
||||||
<Popover.Trigger class="w-full">
|
<Popover.Trigger {id} class="w-full" >
|
||||||
<Button
|
{#snippet child({ props })}
|
||||||
{id}
|
<Button
|
||||||
variant="outline"
|
{...props}
|
||||||
class={cn('w-full justify-start text-left font-normal', !value && 'text-muted-foreground')}
|
variant="outline"
|
||||||
aria-label={m.select_a_date()}
|
class={cn(
|
||||||
>
|
'w-full justify-start text-left font-normal',
|
||||||
<CalendarIcon class="mr-2 size-4" />
|
!value && 'text-muted-foreground'
|
||||||
{calendarDisplayDate
|
)}
|
||||||
? df.format(calendarDisplayDate.toDate(getLocalTimeZone()))
|
aria-label={m.select_a_date()}
|
||||||
: m.select_a_date()}
|
>
|
||||||
</Button>
|
<CalendarIcon class="mr-2 size-4" />
|
||||||
|
{calendarDisplayDate
|
||||||
|
? df.format(calendarDisplayDate.toDate(getLocalTimeZone()))
|
||||||
|
: m.select_a_date()}
|
||||||
|
</Button>
|
||||||
|
{/snippet}
|
||||||
</Popover.Trigger>
|
</Popover.Trigger>
|
||||||
<Popover.Content class="w-auto p-0" align="start">
|
<Popover.Content class="w-auto p-0" align="start">
|
||||||
<Calendar
|
<Calendar
|
||||||
|
|||||||
@@ -1,35 +1,99 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { Calendar as CalendarPrimitive } from 'bits-ui';
|
import * as Calendar from '$lib/components/ui/calendar/index.js';
|
||||||
import * as Calendar from './index.js';
|
import * as Select from '$lib/components/ui/select/index.js';
|
||||||
import { cn, type WithoutChildrenOrChild } from '$lib/utils/style.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 {
|
let {
|
||||||
ref = $bindable(null),
|
|
||||||
value = $bindable(),
|
value = $bindable(),
|
||||||
placeholder = $bindable(),
|
placeholder = $bindable()
|
||||||
class: className,
|
}: WithoutChildrenOrChild<CalendarPrimitive.RootProps> & {
|
||||||
weekdayFormat = 'short',
|
value: CalendarDate | undefined;
|
||||||
...restProps
|
} = $props();
|
||||||
}: WithoutChildrenOrChild<CalendarPrimitive.RootProps> = $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>
|
</script>
|
||||||
|
|
||||||
<!--
|
|
||||||
Discriminated Unions + Destructing (required for bindable) do not
|
|
||||||
get along, so we shut typescript up by casting `value` to `never`.
|
|
||||||
-->
|
|
||||||
<CalendarPrimitive.Root
|
<CalendarPrimitive.Root
|
||||||
bind:value={value as never}
|
type="single"
|
||||||
bind:ref
|
weekdayFormat="short"
|
||||||
|
class={cn('rounded-md border p-3')}
|
||||||
|
bind:value
|
||||||
bind:placeholder
|
bind:placeholder
|
||||||
{weekdayFormat}
|
|
||||||
class={cn('p-3', className)}
|
|
||||||
{...restProps}
|
|
||||||
>
|
>
|
||||||
{#snippet children({ months, weekdays })}
|
{#snippet children({ months, weekdays })}
|
||||||
<Calendar.Header>
|
<Calendar.Header class="flex w-full items-center justify-between gap-2">
|
||||||
<Calendar.PrevButton />
|
<Select.Root
|
||||||
<Calendar.Heading />
|
type="single"
|
||||||
<Calendar.NextButton />
|
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.Header>
|
||||||
<Calendar.Months>
|
<Calendar.Months>
|
||||||
{#each months as month (month)}
|
{#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)}
|
{#each month.weeks as weekDates (weekDates)}
|
||||||
<Calendar.GridRow class="mt-2 w-full">
|
<Calendar.GridRow class="mt-2 w-full">
|
||||||
{#each weekDates as date (date)}
|
{#each weekDates as date (date)}
|
||||||
<Calendar.Cell {date} month={month.value}>
|
<Calendar.Cell class="select-none" {date} month={month.value}>
|
||||||
<Calendar.Day />
|
<Calendar.Day />
|
||||||
</Calendar.Cell>
|
</Calendar.Cell>
|
||||||
{/each}
|
{/each}
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Dialog.Root open={!!apiKeyResponse} {onOpenChange}>
|
<Dialog.Root open={!!apiKeyResponse} {onOpenChange}>
|
||||||
<Dialog.Content class="max-w-md">
|
<Dialog.Content class="max-w-md" onOpenAutoFocus={(e) => e.preventDefault()}>
|
||||||
<Dialog.Header>
|
<Dialog.Header>
|
||||||
<Dialog.Title>{m.api_key_created()}</Dialog.Title>
|
<Dialog.Title>{m.api_key_created()}</Dialog.Title>
|
||||||
<Dialog.Description>
|
<Dialog.Description>
|
||||||
@@ -30,7 +30,6 @@
|
|||||||
<div>
|
<div>
|
||||||
<div class="mb-2 font-medium">{m.name()}</div>
|
<div class="mb-2 font-medium">{m.name()}</div>
|
||||||
<p class="text-muted-foreground">{apiKeyResponse.apiKey.name}</p>
|
<p class="text-muted-foreground">{apiKeyResponse.apiKey.name}</p>
|
||||||
|
|
||||||
{#if apiKeyResponse.apiKey.description}
|
{#if apiKeyResponse.apiKey.description}
|
||||||
<div class="mt-4 mb-2 font-medium">{m.description()}</div>
|
<div class="mt-4 mb-2 font-medium">{m.description()}</div>
|
||||||
<p class="text-muted-foreground">{apiKeyResponse.apiKey.description}</p>
|
<p class="text-muted-foreground">{apiKeyResponse.apiKey.description}</p>
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ test.describe("API Key Management", () => {
|
|||||||
|
|
||||||
// Choose the date
|
// Choose the date
|
||||||
const currentDate = new 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();
|
await page.getByLabel("Select year").click();
|
||||||
// Select the next year
|
// Select the next year
|
||||||
await page.getByText((currentDate.getFullYear() + 1).toString()).click();
|
await page.getByText((currentDate.getFullYear() + 1).toString()).click();
|
||||||
@@ -45,7 +45,10 @@ test.describe("API Key Management", () => {
|
|||||||
expect(token?.length).toBe(32);
|
expect(token?.length).toBe(32);
|
||||||
|
|
||||||
// Close the dialog
|
// Close the dialog
|
||||||
await page.getByRole("button", { name: "Close" }).click();
|
await page
|
||||||
|
.getByRole("button", { name: "Close", exact: true })
|
||||||
|
.nth(1)
|
||||||
|
.click();
|
||||||
|
|
||||||
await page.reload();
|
await page.reload();
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user