compote-ui 0.71.6 → 0.72.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.
- package/dist/components/date-field/date-field.svelte +6 -1
- package/dist/components/date-field/types.d.ts +6 -0
- package/dist/components/date-input/date-input.svelte +6 -2
- package/dist/components/date-range-field/date-range-field.svelte +70 -8
- package/dist/components/date-range-field/date-range-field.svelte.d.ts +1 -1
- package/dist/components/date-range-field/types.d.ts +28 -3
- package/package.json +5 -2
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
import { Field } from '@ark-ui/svelte/field';
|
|
5
5
|
import { useLocaleContext } from '@ark-ui/svelte/locale';
|
|
6
6
|
import { CalendarDateTime, getLocalTimeZone } from '@internationalized/date';
|
|
7
|
-
import { toDateValue, fromDateValue, dateValueShape } from '../../utils/date';
|
|
7
|
+
import { toDateValue, fromDateValue, dateValueShape, dateValueToString } from '../../utils/date';
|
|
8
8
|
import type { DateValueShape } from '../../utils/date';
|
|
9
9
|
import { PhCalendarBlank } from '../../icons';
|
|
10
10
|
import type { DateFieldProps } from './types';
|
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
locale: localeProp,
|
|
26
26
|
granularity,
|
|
27
27
|
hourCycle,
|
|
28
|
+
hideTimeZone = true,
|
|
28
29
|
onValueChange
|
|
29
30
|
}: DateFieldProps = $props();
|
|
30
31
|
|
|
@@ -89,6 +90,7 @@
|
|
|
89
90
|
locale: resolvedLocale,
|
|
90
91
|
granularity: granularity ?? 'day',
|
|
91
92
|
hourCycle,
|
|
93
|
+
hideTimeZone,
|
|
92
94
|
value: datePicker().value,
|
|
93
95
|
min,
|
|
94
96
|
max,
|
|
@@ -97,6 +99,9 @@
|
|
|
97
99
|
required,
|
|
98
100
|
invalid,
|
|
99
101
|
timeZone: tz,
|
|
102
|
+
// Submit a canonical ISO string rather than zag's locale-formatted display
|
|
103
|
+
// string. Only feeds the hidden input's value; segments are formatted separately.
|
|
104
|
+
format: (date) => dateValueToString(date),
|
|
100
105
|
onValueChange(details) {
|
|
101
106
|
datePicker().setValue(details.value);
|
|
102
107
|
}
|
|
@@ -18,4 +18,10 @@ export interface DateFieldProps extends Omit<DatePickerRootBaseProps, 'value' |
|
|
|
18
18
|
name?: string;
|
|
19
19
|
granularity?: 'day' | 'hour' | 'minute' | 'second';
|
|
20
20
|
hourCycle?: 12 | 24;
|
|
21
|
+
/**
|
|
22
|
+
* Hide the trailing time-zone segment (e.g. `GMT+2`) that a zoned value shows
|
|
23
|
+
* at time granularity. Defaults to `true`, matching `DateInput`. Not inherited
|
|
24
|
+
* from Ark: `hideTimeZone` is a date-input prop, not a date-picker one.
|
|
25
|
+
*/
|
|
26
|
+
hideTimeZone?: boolean;
|
|
21
27
|
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import { DateInput } from '@ark-ui/svelte/date-input';
|
|
3
3
|
import { Field, useFieldContext } from '@ark-ui/svelte/field';
|
|
4
|
-
import { toDateValue, fromDateValue, dateValueShape } from '../../utils/date';
|
|
4
|
+
import { toDateValue, fromDateValue, dateValueShape, dateValueToString } from '../../utils/date';
|
|
5
5
|
import { getLocalTimeZone } from '@internationalized/date';
|
|
6
|
-
import type { DateValueShape } from '../../utils/date';
|
|
6
|
+
import type { DateValue, DateValueShape } from '../../utils/date';
|
|
7
7
|
import type { DateInputProps } from './types';
|
|
8
8
|
|
|
9
9
|
let {
|
|
@@ -19,6 +19,9 @@
|
|
|
19
19
|
readOnly,
|
|
20
20
|
disabled,
|
|
21
21
|
hideTimeZone = true,
|
|
22
|
+
// Submit a canonical ISO string rather than zag's locale-formatted display
|
|
23
|
+
// string. Only feeds the hidden input's value; segments are formatted separately.
|
|
24
|
+
format = (date: DateValue) => dateValueToString(date),
|
|
22
25
|
...restProps
|
|
23
26
|
}: DateInputProps = $props();
|
|
24
27
|
|
|
@@ -50,6 +53,7 @@
|
|
|
50
53
|
{...restProps}
|
|
51
54
|
{granularity}
|
|
52
55
|
{hourCycle}
|
|
56
|
+
{format}
|
|
53
57
|
timeZone={tz}
|
|
54
58
|
{hideTimeZone}
|
|
55
59
|
invalid={isInvalid}
|
|
@@ -3,15 +3,22 @@
|
|
|
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 { getLocalTimeZone } from '@internationalized/date';
|
|
7
|
+
import { toDateValue, fromDateValue, dateValueShape, dateValueToString } from '../../utils/date';
|
|
8
|
+
import type { DateValueShape } from '../../utils/date';
|
|
6
9
|
import { PhCalendarBlank } from '../../icons';
|
|
7
10
|
import type { DateRangeFieldProps } from './types';
|
|
8
11
|
import DatePickerCalendar from '../date-picker/date-picker-calendar.svelte';
|
|
9
12
|
|
|
10
13
|
let {
|
|
11
|
-
|
|
12
|
-
|
|
14
|
+
start = $bindable(),
|
|
15
|
+
end = $bindable(),
|
|
16
|
+
defaultStart,
|
|
17
|
+
defaultEnd,
|
|
13
18
|
label,
|
|
14
19
|
name,
|
|
20
|
+
startName,
|
|
21
|
+
endName,
|
|
15
22
|
min,
|
|
16
23
|
max,
|
|
17
24
|
disabled,
|
|
@@ -19,28 +26,73 @@
|
|
|
19
26
|
required,
|
|
20
27
|
invalid,
|
|
21
28
|
timeZone,
|
|
29
|
+
granularity,
|
|
22
30
|
hourCycle,
|
|
31
|
+
hideTimeZone = true,
|
|
23
32
|
onValueChange
|
|
24
33
|
}: DateRangeFieldProps = $props();
|
|
25
34
|
|
|
26
35
|
const locale = useLocaleContext();
|
|
27
36
|
const id = $props.id();
|
|
28
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 each end was bound as, so changes are emitted back in the
|
|
43
|
+
// same shape. Tracked per end so a half-filled range still round-trips; when a
|
|
44
|
+
// side starts null, default to string output since that is the common case
|
|
45
|
+
// binding to database columns.
|
|
46
|
+
let startShape: DateValueShape = 'string';
|
|
47
|
+
let endShape: DateValueShape = 'string';
|
|
48
|
+
|
|
49
|
+
const arkStart = $derived.by(() => {
|
|
50
|
+
const s = dateValueShape(start) ?? dateValueShape(defaultStart);
|
|
51
|
+
if (s) startShape = s;
|
|
52
|
+
return toDateValue(start ?? defaultStart, tz);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
const arkEnd = $derived.by(() => {
|
|
56
|
+
const s = dateValueShape(end) ?? dateValueShape(defaultEnd);
|
|
57
|
+
if (s) endShape = s;
|
|
58
|
+
return toDateValue(end ?? defaultEnd, tz);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
// zag reads the range positionally, so an end without a start cannot be
|
|
62
|
+
// represented — it would silently become the start. Drop it instead.
|
|
63
|
+
const arkValue = $derived(arkStart ? (arkEnd ? [arkStart, arkEnd] : [arkStart]) : []);
|
|
64
|
+
|
|
65
|
+
// Submit from `arkValue`, not from arkStart/arkEnd, so the hidden inputs can
|
|
66
|
+
// never disagree with what the picker actually holds.
|
|
67
|
+
const submittedStart = $derived(arkValue[0] ? dateValueToString(arkValue[0]) : '');
|
|
68
|
+
const submittedEnd = $derived(arkValue[1] ? dateValueToString(arkValue[1]) : '');
|
|
69
|
+
|
|
70
|
+
const resolvedStartName = $derived(startName ?? (name ? `${name}Start` : undefined));
|
|
71
|
+
const resolvedEndName = $derived(endName ?? (name ? `${name}End` : undefined));
|
|
72
|
+
|
|
29
73
|
const datePicker = useDatePicker(() => ({
|
|
30
74
|
id,
|
|
31
75
|
locale: locale().locale,
|
|
32
76
|
selectionMode: 'range',
|
|
33
|
-
value,
|
|
34
|
-
defaultValue,
|
|
77
|
+
value: arkValue,
|
|
35
78
|
min,
|
|
36
79
|
max,
|
|
37
80
|
disabled,
|
|
38
81
|
readOnly,
|
|
39
82
|
required,
|
|
40
83
|
invalid,
|
|
41
|
-
timeZone,
|
|
84
|
+
timeZone: tz,
|
|
42
85
|
onValueChange(details) {
|
|
43
|
-
|
|
86
|
+
const [nextStart = null, nextEnd = null] = details.value;
|
|
87
|
+
// Guard: skip if Ark UI echoes back the same range (prevents reactive loop)
|
|
88
|
+
if (
|
|
89
|
+
nextStart?.toString() === arkStart?.toString() &&
|
|
90
|
+
nextEnd?.toString() === arkEnd?.toString()
|
|
91
|
+
) {
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
start = fromDateValue(nextStart, startShape, tz);
|
|
95
|
+
end = fromDateValue(nextEnd, endShape, tz);
|
|
44
96
|
onValueChange?.(details);
|
|
45
97
|
}
|
|
46
98
|
}));
|
|
@@ -49,7 +101,9 @@
|
|
|
49
101
|
id,
|
|
50
102
|
locale: locale().locale,
|
|
51
103
|
selectionMode: 'range',
|
|
104
|
+
granularity: granularity ?? 'day',
|
|
52
105
|
hourCycle,
|
|
106
|
+
hideTimeZone,
|
|
53
107
|
value: datePicker().value,
|
|
54
108
|
min,
|
|
55
109
|
max,
|
|
@@ -57,7 +111,7 @@
|
|
|
57
111
|
readOnly,
|
|
58
112
|
required,
|
|
59
113
|
invalid,
|
|
60
|
-
timeZone,
|
|
114
|
+
timeZone: tz,
|
|
61
115
|
onValueChange(details) {
|
|
62
116
|
datePicker().setValue(details.value);
|
|
63
117
|
}
|
|
@@ -104,5 +158,13 @@
|
|
|
104
158
|
<DatePickerCalendar />
|
|
105
159
|
</DatePicker.RootProvider>
|
|
106
160
|
</DateInput.Control>
|
|
107
|
-
|
|
161
|
+
<!-- Ark's HiddenInput would submit locale-formatted strings under names that
|
|
162
|
+
flip between `name` and `name[0]`/`name[1]` depending on how many dates are
|
|
163
|
+
selected. Plain inputs give stable names and canonical ISO values. -->
|
|
164
|
+
{#if resolvedStartName}
|
|
165
|
+
<input type="hidden" name={resolvedStartName} {disabled} value={submittedStart} />
|
|
166
|
+
{/if}
|
|
167
|
+
{#if resolvedEndName}
|
|
168
|
+
<input type="hidden" name={resolvedEndName} {disabled} value={submittedEnd} />
|
|
169
|
+
{/if}
|
|
108
170
|
</DateInput.RootProvider>
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import type { DateRangeFieldProps } from './types';
|
|
2
|
-
declare const DateRangeField: import("svelte").Component<DateRangeFieldProps, {}, "
|
|
2
|
+
declare const DateRangeField: import("svelte").Component<DateRangeFieldProps, {}, "start" | "end">;
|
|
3
3
|
type DateRangeField = ReturnType<typeof DateRangeField>;
|
|
4
4
|
export default DateRangeField;
|
|
@@ -1,10 +1,35 @@
|
|
|
1
1
|
import type { DatePickerRootBaseProps } from '@ark-ui/svelte/date-picker';
|
|
2
|
-
import type { DateValue } from '
|
|
2
|
+
import type { DateValue, DateInputValue } from '../../utils/date';
|
|
3
|
+
export type { DateValue, DateInputValue };
|
|
3
4
|
export interface DateRangeFieldProps extends Omit<DatePickerRootBaseProps, 'value' | 'defaultValue' | 'selectionMode'> {
|
|
4
|
-
|
|
5
|
-
|
|
5
|
+
/**
|
|
6
|
+
* Bound start of the range. Accepts a `DateValue`, an ISO/DB string, or a
|
|
7
|
+
* native `Date` — no manual conversion needed. Emitted back in the same shape
|
|
8
|
+
* it was provided (string in → string out, `Date` in → `Date` out); when it
|
|
9
|
+
* starts `null`, changes are emitted as a string.
|
|
10
|
+
*
|
|
11
|
+
* Bind strings when feeding SvelteKit remote functions: `query`/`command`
|
|
12
|
+
* arguments are serialized with devalue, which handles strings and `Date` but
|
|
13
|
+
* not `@internationalized/date` class instances.
|
|
14
|
+
*/
|
|
15
|
+
start?: DateInputValue;
|
|
16
|
+
/** Bound end of the range. Ignored while {@link start} is empty. */
|
|
17
|
+
end?: DateInputValue;
|
|
18
|
+
defaultStart?: DateInputValue;
|
|
19
|
+
defaultEnd?: DateInputValue;
|
|
6
20
|
label?: string;
|
|
21
|
+
/** Shorthand: names the hidden inputs `${name}Start` and `${name}End`. */
|
|
7
22
|
name?: string;
|
|
23
|
+
/** Names the start hidden input outright. Overrides {@link name}. */
|
|
24
|
+
startName?: string;
|
|
25
|
+
/** Names the end hidden input outright. Overrides {@link name}. */
|
|
26
|
+
endName?: string;
|
|
8
27
|
granularity?: 'day' | 'hour' | 'minute' | 'second';
|
|
9
28
|
hourCycle?: 12 | 24;
|
|
29
|
+
/**
|
|
30
|
+
* Hide the trailing time-zone segment (e.g. `GMT+2`) that a zoned value shows
|
|
31
|
+
* at time granularity. Defaults to `true`, matching `DateInput`. Not inherited
|
|
32
|
+
* from Ark: `hideTimeZone` is a date-input prop, not a date-picker one.
|
|
33
|
+
*/
|
|
34
|
+
hideTimeZone?: boolean;
|
|
10
35
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "compote-ui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.72.1",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"dev": "vite dev --open",
|
|
@@ -11,6 +11,8 @@
|
|
|
11
11
|
"prepack": "svelte-kit sync && svelte-package && publint",
|
|
12
12
|
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
|
|
13
13
|
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
|
|
14
|
+
"test": "vitest run",
|
|
15
|
+
"test:watch": "vitest",
|
|
14
16
|
"lint": "prettier --check . && eslint .",
|
|
15
17
|
"format": "prettier --write .",
|
|
16
18
|
"patch": "npm version patch && npm publish",
|
|
@@ -81,7 +83,8 @@
|
|
|
81
83
|
"typescript": "^6.0.3",
|
|
82
84
|
"typescript-eslint": "^8.68.0",
|
|
83
85
|
"unplugin-icons": "^23.0.1",
|
|
84
|
-
"vite": "^8.2.2"
|
|
86
|
+
"vite": "^8.2.2",
|
|
87
|
+
"vitest": "^5"
|
|
85
88
|
},
|
|
86
89
|
"keywords": [
|
|
87
90
|
"svelte",
|