compote-ui 0.63.1 → 0.63.3

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.
@@ -3,7 +3,9 @@
3
3
  import { DateInput, useDateInput } from '@ark-ui/svelte/date-input';
4
4
  import { Field } from '@ark-ui/svelte/field';
5
5
  import { useLocaleContext } from '@ark-ui/svelte/locale';
6
- import { CalendarDateTime } from '@internationalized/date';
6
+ import { CalendarDateTime, getLocalTimeZone } from '@internationalized/date';
7
+ import { toDateValue, fromDateValue, dateValueShape } from '../../utils/date';
8
+ import type { DateValueShape } from '../../utils/date';
7
9
  import { PhCalendarBlank } from '../../icons';
8
10
  import type { DateFieldProps } from './types';
9
11
  import DatePickerCalendar from '../date-picker/date-picker-calendar.svelte';
@@ -20,54 +22,71 @@
20
22
  required,
21
23
  invalid,
22
24
  timeZone,
25
+ locale: localeProp,
23
26
  granularity,
24
27
  hourCycle,
25
28
  onValueChange
26
29
  }: DateFieldProps = $props();
27
30
 
28
- const locale = useLocaleContext();
31
+ const localeContext = useLocaleContext();
32
+ const resolvedLocale = $derived(localeProp ?? localeContext().locale);
29
33
  const id = $props.id();
30
34
  const showTimeInput = $derived(!!granularity && granularity !== 'day');
31
35
  const resolvedHourCycle = $derived(
32
36
  hourCycle ??
33
- (new Intl.DateTimeFormat(locale().locale, { hour: 'numeric' })
37
+ (new Intl.DateTimeFormat(resolvedLocale, { hour: 'numeric' })
34
38
  .formatToParts(new Date(2024, 0, 1, 14))
35
39
  .some((p) => p.type === 'dayPeriod')
36
40
  ? 12
37
41
  : 24)
38
42
  );
39
43
 
44
+ // Default the display zone to the user's local zone. Without this Ark/zag
45
+ // formats segments in UTC, so a UTC value would show shifted hours.
46
+ const tz = $derived(timeZone ?? getLocalTimeZone());
47
+
48
+ // Remember the shape the consumer bound the value as, so changes are emitted
49
+ // back in the same shape (string in → string out, Date in → Date out).
50
+ // When the bound value starts null, default to string output.
51
+ let shape: DateValueShape = 'string';
52
+
53
+ const arkValue = $derived.by(() => {
54
+ const s = dateValueShape(value) ?? dateValueShape(defaultValue);
55
+ if (s) shape = s;
56
+ return toDateValue(value, tz);
57
+ });
58
+ const arkDefault = $derived(toDateValue(defaultValue, tz));
59
+
40
60
  const datePicker = useDatePicker(() => ({
41
61
  id,
42
- locale: locale().locale,
62
+ locale: resolvedLocale,
43
63
  closeOnSelect: !showTimeInput,
44
- value: value ? [value] : [],
45
- defaultValue: defaultValue ? [defaultValue] : undefined,
64
+ value: arkValue ? [arkValue] : [],
65
+ defaultValue: arkDefault ? [arkDefault] : undefined,
46
66
  min,
47
67
  max,
48
68
  disabled,
49
69
  readOnly,
50
70
  required,
51
71
  invalid,
52
- timeZone,
72
+ timeZone: tz,
53
73
  onValueChange(details) {
54
74
  const picked = details.value[0] ?? null;
75
+ let next = picked;
55
76
  if (picked && showTimeInput && !('hour' in picked)) {
56
- const h = value && 'hour' in value ? (value as CalendarDateTime).hour : 0;
57
- const m = value && 'hour' in value ? (value as CalendarDateTime).minute : 0;
58
- const merged = new CalendarDateTime(picked.year, picked.month, picked.day, h, m);
59
- value = merged;
60
- datePicker().setValue([merged]);
61
- } else {
62
- value = picked;
77
+ const h = arkValue && 'hour' in arkValue ? (arkValue as CalendarDateTime).hour : 0;
78
+ const m = arkValue && 'hour' in arkValue ? (arkValue as CalendarDateTime).minute : 0;
79
+ next = new CalendarDateTime(picked.year, picked.month, picked.day, h, m);
80
+ datePicker().setValue(next ? [next] : []);
63
81
  }
82
+ value = fromDateValue(next, shape, tz);
64
83
  onValueChange?.(details);
65
84
  }
66
85
  }));
67
86
 
68
87
  const dateInput = useDateInput(() => ({
69
88
  id,
70
- locale: locale().locale,
89
+ locale: resolvedLocale,
71
90
  granularity: granularity ?? 'day',
72
91
  hourCycle,
73
92
  value: datePicker().value,
@@ -77,7 +96,7 @@
77
96
  readOnly,
78
97
  required,
79
98
  invalid,
80
- timeZone,
99
+ timeZone: tz,
81
100
  onValueChange(details) {
82
101
  datePicker().setValue(details.value);
83
102
  }
@@ -1,8 +1,19 @@
1
1
  import type { DatePickerRootBaseProps } from '@ark-ui/svelte/date-picker';
2
- import type { DateValue } from '@ark-ui/svelte/date-picker';
2
+ import type { DateValue, DateInputValue } from '../../utils/date';
3
+ export type { DateValue };
3
4
  export interface DateFieldProps extends Omit<DatePickerRootBaseProps, 'value' | 'defaultValue'> {
4
- value?: DateValue | null;
5
- defaultValue?: DateValue;
5
+ /**
6
+ * Bound value. Accepts a `DateValue`, an ISO/DB string, or a native `Date` —
7
+ * no manual conversion needed. The value is emitted back in the same shape it
8
+ * was provided (string in → string out, `Date` in → `Date` out). When the
9
+ * value starts `null`, changes are emitted as a string by default.
10
+ *
11
+ * For UTC values from a database, bind the ISO string with its `Z`/offset and
12
+ * leave `timeZone` unset: the field displays the user's local zone and the
13
+ * emitted string is UTC.
14
+ */
15
+ value?: DateInputValue;
16
+ defaultValue?: DateInputValue;
6
17
  label?: string;
7
18
  name?: string;
8
19
  granularity?: 'day' | 'hour' | 'minute' | 'second';
@@ -2,7 +2,9 @@
2
2
  import { DatePicker } from '@ark-ui/svelte/date-picker';
3
3
  import { Field } from '@ark-ui/svelte/field';
4
4
  import { useLocaleContext } from '@ark-ui/svelte/locale';
5
- import { CalendarDateTime } from '@internationalized/date';
5
+ import { CalendarDateTime, getLocalTimeZone } from '@internationalized/date';
6
+ import { toDateValue, fromDateValue, dateValueShape } from '../../utils/date';
7
+ import type { DateValueShape } from '../../utils/date';
6
8
  import { PhCalendarBlank, PhX } from '../../icons';
7
9
  import type { DatePickerProps } from './types';
8
10
  import DatePickerCalendar from './date-picker-calendar.svelte';
@@ -15,37 +17,57 @@
15
17
  name,
16
18
  granularity,
17
19
  hourCycle,
20
+ locale: localeProp,
21
+ timeZone,
18
22
  onValueChange,
19
23
  ...restProps
20
24
  }: DatePickerProps = $props();
21
25
 
22
- const locale = useLocaleContext();
26
+ const localeContext = useLocaleContext();
27
+ const resolvedLocale = $derived(localeProp ?? localeContext().locale);
23
28
  const showTimeInput = $derived(!!granularity && granularity !== 'day');
24
29
  const resolvedHourCycle = $derived(
25
30
  hourCycle ??
26
- (new Intl.DateTimeFormat(locale().locale, { hour: 'numeric' })
31
+ (new Intl.DateTimeFormat(resolvedLocale, { hour: 'numeric' })
27
32
  .formatToParts(new Date(2024, 0, 1, 14))
28
33
  .some((p) => p.type === 'dayPeriod')
29
34
  ? 12
30
35
  : 24)
31
36
  );
37
+
38
+ // Default the display zone to the user's local zone. Without this Ark/zag
39
+ // formats segments in UTC, so a UTC value would show shifted hours.
40
+ const tz = $derived(timeZone ?? getLocalTimeZone());
41
+
42
+ // Remember the shape the consumer bound the value as, so changes are emitted
43
+ // back in the same shape (string in → string out, Date in → Date out).
44
+ // When the bound value starts null, default to string output.
45
+ let shape: DateValueShape = 'string';
46
+
47
+ const arkValue = $derived.by(() => {
48
+ const s = dateValueShape(value) ?? dateValueShape(defaultValue);
49
+ if (s) shape = s;
50
+ return toDateValue(value, tz);
51
+ });
52
+ const arkDefault = $derived(toDateValue(defaultValue, tz));
32
53
  </script>
33
54
 
34
55
  <DatePicker.Root
35
56
  {...restProps}
36
- locale={locale().locale}
57
+ locale={resolvedLocale}
58
+ timeZone={tz}
37
59
  closeOnSelect={!showTimeInput}
38
- value={value ? [value] : []}
39
- defaultValue={defaultValue ? [defaultValue] : undefined}
60
+ value={arkValue ? [arkValue] : []}
61
+ defaultValue={arkDefault ? [arkDefault] : undefined}
40
62
  onValueChange={(details) => {
41
63
  const picked = details.value[0] ?? null;
64
+ let next = picked;
42
65
  if (picked && showTimeInput && !('hour' in picked)) {
43
- const h = value && 'hour' in value ? (value as CalendarDateTime).hour : 0;
44
- const m = value && 'hour' in value ? (value as CalendarDateTime).minute : 0;
45
- value = new CalendarDateTime(picked.year, picked.month, picked.day, h, m);
46
- } else {
47
- value = picked;
66
+ const h = arkValue && 'hour' in arkValue ? (arkValue as CalendarDateTime).hour : 0;
67
+ const m = arkValue && 'hour' in arkValue ? (arkValue as CalendarDateTime).minute : 0;
68
+ next = new CalendarDateTime(picked.year, picked.month, picked.day, h, m);
48
69
  }
70
+ value = fromDateValue(next, shape, tz);
49
71
  onValueChange?.(details);
50
72
  }}
51
73
  >
@@ -1,8 +1,19 @@
1
1
  import type { DatePickerRootBaseProps } from '@ark-ui/svelte/date-picker';
2
- import type { DateValue } from '@ark-ui/svelte/date-picker';
2
+ import type { DateValue, DateInputValue } from '../../utils/date';
3
+ export type { DateValue };
3
4
  export interface DatePickerProps extends Omit<DatePickerRootBaseProps, 'value' | 'defaultValue'> {
4
- value?: DateValue | null;
5
- defaultValue?: DateValue;
5
+ /**
6
+ * Bound value. Accepts a `DateValue`, an ISO/DB string, or a native `Date` —
7
+ * no manual conversion needed. The value is emitted back in the same shape it
8
+ * was provided (string in → string out, `Date` in → `Date` out). When the
9
+ * value starts `null`, changes are emitted as a string by default.
10
+ *
11
+ * For UTC values from a database, bind the ISO string with its `Z`/offset and
12
+ * leave `timeZone` unset: the picker displays the user's local zone and the
13
+ * emitted string is UTC.
14
+ */
15
+ value?: DateInputValue;
16
+ defaultValue?: DateInputValue;
6
17
  label?: string;
7
18
  placeholder?: string;
8
19
  name?: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "compote-ui",
3
- "version": "0.63.1",
3
+ "version": "0.63.3",
4
4
  "license": "MIT",
5
5
  "scripts": {
6
6
  "dev": "vite dev --open",
@@ -65,8 +65,8 @@
65
65
  "@sveltejs/vite-plugin-svelte": "7.1.2",
66
66
  "@tailwindcss/vite": "^4.3.2",
67
67
  "@tanstack/intent": "^0.3.2",
68
- "@tanstack/svelte-table": "^9.0.0-beta.17",
69
- "@tanstack/svelte-virtual": "^3.13.30",
68
+ "@tanstack/svelte-table": "^9.0.0-beta.26",
69
+ "@tanstack/svelte-virtual": "^3.13.31",
70
70
  "@types/node": "^22.20.0",
71
71
  "eslint": "^10.5.0",
72
72
  "eslint-config-prettier": "^10.1.8",
@@ -83,7 +83,7 @@
83
83
  "typescript": "^6.0.3",
84
84
  "typescript-eslint": "^8.61.1",
85
85
  "unplugin-icons": "^23.0.1",
86
- "vite": "^8.0.16"
86
+ "vite": "^8.1.2"
87
87
  },
88
88
  "keywords": [
89
89
  "svelte",