compote-ui 0.50.0 → 0.51.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,7 +1,7 @@
1
1
  const TYPE_DEFAULTS = {
2
- number: { align: 'right' },
3
- currency: { align: 'right' },
4
- percent: { align: 'right' },
2
+ number: { align: 'right', size: 120 },
3
+ currency: { align: 'right', size: 120 },
4
+ percent: { align: 'right', size: 100 },
5
5
  date: { align: 'center', size: 110 },
6
6
  time: { align: 'center', size: 80 },
7
7
  'date-time': { align: 'center', size: 160 },
@@ -267,12 +267,12 @@
267
267
  </button>
268
268
  </div>
269
269
  {:else if getColumnType(column) === 'select'}
270
- {@const allOptions = getFacetedValues(column)}
271
- {@const search = localSelectSearch[column.id] ?? ''}
272
- {@const options = search
270
+ {const allOptions = getFacetedValues(column)}
271
+ {const search = localSelectSearch[column.id] ?? ''}
272
+ {const options = search
273
273
  ? allOptions.filter((o) => o.toLowerCase().includes(search.toLowerCase()))
274
274
  : allOptions}
275
- {@const selected = getSelectValues(column)}
275
+ {const selected = getSelectValues(column)}
276
276
  <div class="flex flex-col gap-1">
277
277
  <Field.Root>
278
278
  <Field.Input
@@ -285,30 +285,28 @@
285
285
  }}
286
286
  />
287
287
  </Field.Root>
288
- <div class="max-h-44 overflow-hidden">
289
- <ScrollArea.Root class="h-full">
290
- <ScrollArea.Viewport>
291
- <ScrollArea.Content>
292
- <div class="flex flex-col gap-0.5">
293
- {#each options as option (option)}
294
- <Checkbox
295
- size="sm"
296
- label={option}
297
- class="min-h-7 rounded-sm px-2 hover:bg-surface-2"
298
- checked={selected.includes(option)}
299
- onCheckedChange={({ checked }) =>
300
- handleSelectChange(column, option, checked === true)}
301
- />
302
- {/each}
303
- </div>
304
- </ScrollArea.Content>
305
- </ScrollArea.Viewport>
306
- <ScrollArea.Scrollbar orientation="vertical">
307
- <ScrollArea.Thumb />
308
- </ScrollArea.Scrollbar>
309
- <ScrollArea.Corner />
310
- </ScrollArea.Root>
311
- </div>
288
+ <ScrollArea.Root class="max-h-44">
289
+ <ScrollArea.Viewport>
290
+ <ScrollArea.Content>
291
+ <div class="flex flex-col gap-0.5">
292
+ {#each options as option (option)}
293
+ <Checkbox
294
+ size="sm"
295
+ label={option}
296
+ class="min-h-7 rounded-sm px-2 hover:bg-surface-2"
297
+ checked={selected.includes(option)}
298
+ onCheckedChange={({ checked }) =>
299
+ handleSelectChange(column, option, checked === true)}
300
+ />
301
+ {/each}
302
+ </div>
303
+ </ScrollArea.Content>
304
+ </ScrollArea.Viewport>
305
+ <ScrollArea.Scrollbar orientation="vertical">
306
+ <ScrollArea.Thumb />
307
+ </ScrollArea.Scrollbar>
308
+ <ScrollArea.Corner />
309
+ </ScrollArea.Root>
312
310
  </div>
313
311
  {:else}
314
312
  <Field.Root>
@@ -21,12 +21,21 @@
21
21
  invalid,
22
22
  timeZone,
23
23
  granularity,
24
+ hourCycle,
24
25
  onValueChange
25
26
  }: DateFieldProps = $props();
26
27
 
27
28
  const locale = useLocaleContext();
28
29
  const id = $props.id();
29
30
  const showTimeInput = $derived(!!granularity && granularity !== 'day');
31
+ const resolvedHourCycle = $derived(
32
+ hourCycle ??
33
+ (new Intl.DateTimeFormat(locale().locale, { hour: 'numeric' })
34
+ .formatToParts(new Date(2024, 0, 1, 14))
35
+ .some((p) => p.type === 'dayPeriod')
36
+ ? 12
37
+ : 24)
38
+ );
30
39
 
31
40
  const datePicker = useDatePicker(() => ({
32
41
  id,
@@ -60,6 +69,7 @@
60
69
  id,
61
70
  locale: locale().locale,
62
71
  granularity: granularity ?? 'day',
72
+ hourCycle,
63
73
  value: datePicker().value,
64
74
  min,
65
75
  max,
@@ -102,7 +112,7 @@
102
112
  <PhCalendarBlank class="size-4" />
103
113
  </DatePicker.Trigger>
104
114
  </DatePicker.Control>
105
- <DatePickerCalendar {showTimeInput} />
115
+ <DatePickerCalendar {showTimeInput} hourCycle={resolvedHourCycle} />
106
116
  </DatePicker.RootProvider>
107
117
  </DateInput.Control>
108
118
  <DateInput.HiddenInput {name} />
@@ -6,4 +6,5 @@ export interface DateFieldProps extends Omit<DatePickerRootBaseProps, 'value' |
6
6
  label?: string;
7
7
  name?: string;
8
8
  granularity?: 'day' | 'hour' | 'minute' | 'second';
9
+ hourCycle?: 12 | 24;
9
10
  }
@@ -4,13 +4,25 @@
4
4
  import { CalendarDateTime } from '@internationalized/date';
5
5
  import { PhArrowLeft, PhArrowRight } from '../../icons';
6
6
  import type { DateValue } from '@ark-ui/svelte/date-picker';
7
+ import Select from '../select/select.svelte';
7
8
 
8
- let { showTimeInput = false }: { showTimeInput?: boolean } = $props();
9
+ let { showTimeInput = false, hourCycle = 24 }: { showTimeInput?: boolean; hourCycle?: 12 | 24 } =
10
+ $props();
9
11
 
10
- function getTimeValue(v: DateValue | undefined): string {
11
- if (!v || !('hour' in v)) return '';
12
- const dt = v as CalendarDateTime;
13
- return `${String(dt.hour).padStart(2, '0')}:${String(dt.minute).padStart(2, '0')}`;
12
+ function getHour(v: DateValue | undefined): number {
13
+ if (!v || !('hour' in v)) return 0;
14
+ const h = (v as CalendarDateTime).hour;
15
+ return hourCycle === 12 ? h % 12 || 12 : h;
16
+ }
17
+
18
+ function getMinute(v: DateValue | undefined): number {
19
+ if (!v || !('hour' in v)) return 0;
20
+ return (v as CalendarDateTime).minute;
21
+ }
22
+
23
+ function isPM(v: DateValue | undefined): boolean {
24
+ if (!v || !('hour' in v)) return false;
25
+ return (v as CalendarDateTime).hour >= 12;
14
26
  }
15
27
 
16
28
  function setTime(v: DateValue | undefined, hours: number, minutes: number): CalendarDateTime {
@@ -18,6 +30,32 @@
18
30
  const d = v ?? new CalendarDateTime(new Date().getFullYear(), 1, 1, 0, 0);
19
31
  return new CalendarDateTime(d.year, d.month, d.day, hours, minutes);
20
32
  }
33
+
34
+ function updateHour(v: DateValue | undefined, displayHour: number): CalendarDateTime {
35
+ let h = displayHour;
36
+ if (hourCycle === 12) {
37
+ const pm = isPM(v);
38
+ h = pm ? (displayHour % 12) + 12 : displayHour % 12;
39
+ }
40
+ return setTime(v, h, getMinute(v));
41
+ }
42
+
43
+ function togglePeriod(v: DateValue | undefined): CalendarDateTime {
44
+ const cur = v && 'hour' in v ? (v as CalendarDateTime).hour : 0;
45
+ return setTime(v, cur >= 12 ? cur - 12 : cur + 12, getMinute(v));
46
+ }
47
+
48
+ const minuteItems = Array.from({ length: 60 }, (_, m) => ({
49
+ value: m,
50
+ label: String(m).padStart(2, '0')
51
+ }));
52
+
53
+ const hourItems = $derived(
54
+ Array.from({ length: hourCycle === 12 ? 12 : 24 }, (_, i) => {
55
+ const h = hourCycle === 12 ? i + 1 : i;
56
+ return { value: h, label: String(h).padStart(2, '0') };
57
+ })
58
+ );
21
59
  </script>
22
60
 
23
61
  <Portal>
@@ -74,15 +112,43 @@
74
112
  </DatePicker.TableBody>
75
113
  </DatePicker.Table>
76
114
  {#if showTimeInput}
77
- <input
78
- type="time"
79
- value={getTimeValue(datePicker().value[0])}
80
- oninput={(e) => {
81
- const [h, m] = e.currentTarget.value.split(':').map(Number);
82
- datePicker().setValue([setTime(datePicker().value[0], h, m)]);
83
- }}
84
- class="mt-3 h-9 w-full rounded-md border border-border bg-surface-1 px-3 text-sm shadow-sm focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring"
85
- />
115
+ {@const cur = datePicker().value[0]}
116
+ <div class="mt-3 flex items-center justify-center gap-1">
117
+ <Select
118
+ items={hourItems}
119
+ value={getHour(cur)}
120
+ size="sm"
121
+ placeholder="HH"
122
+ onValueChange={(d) =>
123
+ datePicker().setValue([updateHour(cur, Number(d.items[0]?.value))])}
124
+ />
125
+ <span class="text-ink-dim">:</span>
126
+ <Select
127
+ items={minuteItems}
128
+ value={getMinute(cur)}
129
+ size="sm"
130
+ placeholder="mm"
131
+ onValueChange={(d) => {
132
+ const m = Number(d.items[0]?.value);
133
+ const h24 =
134
+ hourCycle === 12
135
+ ? isPM(cur)
136
+ ? (getHour(cur) % 12) + 12
137
+ : getHour(cur) % 12
138
+ : getHour(cur);
139
+ datePicker().setValue([setTime(cur, h24, m)]);
140
+ }}
141
+ />
142
+ {#if hourCycle === 12}
143
+ <button
144
+ type="button"
145
+ onclick={() => datePicker().setValue([togglePeriod(cur)])}
146
+ class="h-8 rounded-md border border-border bg-surface-1 px-2 text-sm shadow-sm hover:bg-surface-2 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring"
147
+ >
148
+ {isPM(cur) ? 'PM' : 'AM'}
149
+ </button>
150
+ {/if}
151
+ </div>
86
152
  {/if}
87
153
  {/snippet}
88
154
  </DatePicker.Context>
@@ -1,5 +1,6 @@
1
1
  type $$ComponentProps = {
2
2
  showTimeInput?: boolean;
3
+ hourCycle?: 12 | 24;
3
4
  };
4
5
  declare const DatePickerCalendar: import("svelte").Component<$$ComponentProps, {}, "">;
5
6
  type DatePickerCalendar = ReturnType<typeof DatePickerCalendar>;
@@ -14,12 +14,21 @@
14
14
  placeholder,
15
15
  name,
16
16
  granularity,
17
+ hourCycle,
17
18
  onValueChange,
18
19
  ...restProps
19
20
  }: DatePickerProps = $props();
20
21
 
21
22
  const locale = useLocaleContext();
22
23
  const showTimeInput = $derived(!!granularity && granularity !== 'day');
24
+ const resolvedHourCycle = $derived(
25
+ hourCycle ??
26
+ (new Intl.DateTimeFormat(locale().locale, { hour: 'numeric' })
27
+ .formatToParts(new Date(2024, 0, 1, 14))
28
+ .some((p) => p.type === 'dayPeriod')
29
+ ? 12
30
+ : 24)
31
+ );
23
32
  </script>
24
33
 
25
34
  <DatePicker.Root
@@ -30,7 +39,7 @@
30
39
  defaultValue={defaultValue ? [defaultValue] : undefined}
31
40
  onValueChange={(details) => {
32
41
  const picked = details.value[0] ?? null;
33
- if (picked && showTimeInput) {
42
+ if (picked && showTimeInput && !('hour' in picked)) {
34
43
  const h = value && 'hour' in value ? (value as CalendarDateTime).hour : 0;
35
44
  const m = value && 'hour' in value ? (value as CalendarDateTime).minute : 0;
36
45
  value = new CalendarDateTime(picked.year, picked.month, picked.day, h, m);
@@ -62,7 +71,7 @@
62
71
  <PhX class="size-3.5" />
63
72
  </DatePicker.ClearTrigger>
64
73
  </DatePicker.Control>
65
- <DatePickerCalendar {showTimeInput} />
74
+ <DatePickerCalendar {showTimeInput} hourCycle={resolvedHourCycle} />
66
75
  {#if name}
67
76
  <input type="hidden" {name} value={value ?? ''} />
68
77
  {/if}
@@ -7,4 +7,5 @@ export interface DatePickerProps extends Omit<DatePickerRootBaseProps, 'value' |
7
7
  placeholder?: string;
8
8
  name?: string;
9
9
  granularity?: 'day' | 'hour' | 'minute' | 'second';
10
+ hourCycle?: 12 | 24;
10
11
  }
@@ -19,6 +19,7 @@
19
19
  required,
20
20
  invalid,
21
21
  timeZone,
22
+ hourCycle,
22
23
  onValueChange
23
24
  }: DateRangeFieldProps = $props();
24
25
 
@@ -48,6 +49,7 @@
48
49
  id,
49
50
  locale: locale().locale,
50
51
  selectionMode: 'range',
52
+ hourCycle,
51
53
  value: datePicker().value,
52
54
  min,
53
55
  max,
@@ -5,4 +5,6 @@ export interface DateRangeFieldProps extends Omit<DatePickerRootBaseProps, 'valu
5
5
  defaultValue?: DateValue[];
6
6
  label?: string;
7
7
  name?: string;
8
+ granularity?: 'day' | 'hour' | 'minute' | 'second';
9
+ hourCycle?: 12 | 24;
8
10
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "compote-ui",
3
- "version": "0.50.0",
3
+ "version": "0.51.1",
4
4
  "license": "MIT",
5
5
  "scripts": {
6
6
  "dev": "vite dev --open",
@@ -64,25 +64,25 @@
64
64
  "@sveltejs/package": "^2.5.7",
65
65
  "@sveltejs/vite-plugin-svelte": "7.1.2",
66
66
  "@tailwindcss/vite": "^4.2.4",
67
- "@tanstack/svelte-virtual": "^3.13.26",
67
+ "@tanstack/svelte-virtual": "^3.13.28",
68
68
  "@tanstack/table-core": "^8.21.3",
69
69
  "@types/node": "^22.19.18",
70
- "eslint": "^10.4.0",
70
+ "eslint": "^10.4.1",
71
71
  "eslint-config-prettier": "^10.1.8",
72
- "eslint-plugin-svelte": "^3.17.0",
72
+ "eslint-plugin-svelte": "^3.19.0",
73
73
  "globals": "^17.5.0",
74
74
  "prettier": "^3.8.3",
75
- "prettier-plugin-svelte": "^4.0.1",
75
+ "prettier-plugin-svelte": "^4.1.0",
76
76
  "prettier-plugin-tailwindcss": "^0.8.0",
77
77
  "publint": "^0.3.21",
78
- "svelte": "^5.55.9",
79
- "svelte-check": "^4.4.6",
78
+ "svelte": "^5.56.1",
79
+ "svelte-check": "^4.5.0",
80
80
  "tailwindcss": "^4.2.4",
81
81
  "tw-animate-css": "^1.4.0",
82
82
  "typescript": "^6.0.3",
83
- "typescript-eslint": "^8.60.0",
83
+ "typescript-eslint": "^8.60.1",
84
84
  "unplugin-icons": "^23.0.1",
85
- "vite": "^8.0.14"
85
+ "vite": "^8.0.16"
86
86
  },
87
87
  "keywords": [
88
88
  "svelte"