svelte-datetimerange-picker 0.2.0 → 0.5.0
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/README.md +8 -2
- package/dist/DateTimeRangePicker.svelte +36 -30
- package/dist/DateTimeRangePicker.svelte.d.ts +32 -11
- package/dist/shared/types.d.ts +4 -4
- package/package.json +5 -5
- package/src/DateTimeRangePicker.svelte +36 -30
- package/src/shared/types.ts +4 -4
package/README.md
CHANGED
|
@@ -8,7 +8,7 @@ A datetime range picker for your Svelte 5 app.
|
|
|
8
8
|
|
|
9
9
|
## tl;dr
|
|
10
10
|
|
|
11
|
-
- Install by executing `npm install svelte-datetimerange-picker`.
|
|
11
|
+
- Install by executing `npm install svelte-datetimerange-picker svelte-date-utils`.
|
|
12
12
|
- Import by adding `import DateTimeRangePicker from 'svelte-datetimerange-picker'`.
|
|
13
13
|
- Use by adding `<DateTimeRangePicker />`. Use the `onChange` prop for getting new values.
|
|
14
14
|
|
|
@@ -29,24 +29,30 @@ Your project needs to use Svelte 5 or later. The picker relies on modern web tec
|
|
|
29
29
|
|
|
30
30
|
### Installation
|
|
31
31
|
|
|
32
|
-
Add svelte-datetimerange-picker to your project by executing `npm install svelte-datetimerange-picker`.
|
|
32
|
+
Add svelte-datetimerange-picker and its date adapter utilities to your project by executing `npm install svelte-datetimerange-picker svelte-date-utils`.
|
|
33
33
|
|
|
34
34
|
### Usage
|
|
35
35
|
|
|
36
36
|
```svelte
|
|
37
37
|
<script lang="ts">
|
|
38
38
|
import DateTimeRangePicker from 'svelte-datetimerange-picker';
|
|
39
|
+
import { AdapterDate } from 'svelte-date-utils';
|
|
40
|
+
import { provideDateAdapter } from 'svelte-date-utils';
|
|
39
41
|
|
|
40
42
|
type ValuePiece = Date | null;
|
|
41
43
|
|
|
42
44
|
type Value = ValuePiece | [ValuePiece, ValuePiece];
|
|
43
45
|
|
|
46
|
+
provideDateAdapter(new AdapterDate());
|
|
47
|
+
|
|
44
48
|
let value = $state<Value>([new Date(), new Date()]);
|
|
45
49
|
</script>
|
|
46
50
|
|
|
47
51
|
<DateTimeRangePicker onChange={(nextValue) => (value = nextValue)} {value} />
|
|
48
52
|
```
|
|
49
53
|
|
|
54
|
+
The adapter is required and is read from Svelte context. Call `provideDateAdapter(adapter)` in this component or an ancestor before rendering the picker. Without a provider, the picker throws an error. The selected adapter determines the date object type used by `value` and `onChange`; `AdapterDate` uses native `Date`. For date-fns, Luxon, or `@internationalized/date`, use the matching adapter exported by `svelte-date-utils/date-fns`, `svelte-date-utils/luxon`, or `svelte-date-utils/internationalized`.
|
|
55
|
+
|
|
50
56
|
### Styling
|
|
51
57
|
|
|
52
58
|
Styles for the picker, the calendar and the clock are imported automatically. All elements use BEM class names starting with `svelte-datetimerange-picker`, `svelte-calendar` and `svelte-clock`, so you can build on them.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
<script lang="ts">
|
|
1
|
+
<script lang="ts" generics="T">
|
|
2
2
|
import './DateTimeRangePicker.css';
|
|
3
3
|
|
|
4
4
|
import { untrack } from 'svelte';
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
import { DateTimeInput } from 'svelte-datetime-input';
|
|
7
7
|
import { fit, portal } from 'svelte-fit-action';
|
|
8
8
|
import { Calendar } from 'svelte-range-calendar';
|
|
9
|
+
import { getDateAdapter } from 'svelte-date-utils';
|
|
9
10
|
|
|
10
11
|
import type { ComponentProps, Snippet } from 'svelte';
|
|
11
12
|
import type { ClassValue, HTMLAttributes } from 'svelte/elements';
|
|
@@ -15,11 +16,11 @@
|
|
|
15
16
|
const outsideActionEvents = ['mousedown', 'focusin', 'touchstart'] as const;
|
|
16
17
|
const allViews = ['hour', 'minute', 'second'] as const;
|
|
17
18
|
|
|
18
|
-
type CalendarProps = Omit<ComponentProps<typeof Calendar>, 'onChange' | 'selectRange' | 'value'>;
|
|
19
|
+
type CalendarProps<T> = Omit<ComponentProps<typeof Calendar>, 'onChange' | 'selectRange' | 'value'>;
|
|
19
20
|
|
|
20
|
-
type ClockProps = Omit<ComponentProps<typeof Clock>, 'value'>;
|
|
21
|
+
type ClockProps<T> = Omit<ComponentProps<typeof Clock>, 'value'>;
|
|
21
22
|
|
|
22
|
-
type DateTimeRangePickerProps = {
|
|
23
|
+
type DateTimeRangePickerProps<T> = {
|
|
23
24
|
/** `aria-label` for the AM/PM select input. */
|
|
24
25
|
amPmAriaLabel?: string;
|
|
25
26
|
/** Automatically focuses the input on mount. */
|
|
@@ -31,7 +32,7 @@
|
|
|
31
32
|
*/
|
|
32
33
|
calendarIcon?: Snippet | string | null;
|
|
33
34
|
/** Props to pass to the Calendar component. */
|
|
34
|
-
calendarProps?: CalendarProps
|
|
35
|
+
calendarProps?: CalendarProps<T>;
|
|
35
36
|
/**
|
|
36
37
|
* Class name(s) that will be added along with `"svelte-datetimerange-picker"` to the main `<div>` element.
|
|
37
38
|
*
|
|
@@ -46,7 +47,7 @@
|
|
|
46
47
|
*/
|
|
47
48
|
clearIcon?: Snippet | string | null;
|
|
48
49
|
/** Props to pass to the Clock component. */
|
|
49
|
-
clockProps?: ClockProps
|
|
50
|
+
clockProps?: ClockProps<T>;
|
|
50
51
|
/**
|
|
51
52
|
* Whether to close the widgets on value selection. **Note**: It's recommended to use `shouldCloseWidgets` function instead.
|
|
52
53
|
*
|
|
@@ -114,7 +115,7 @@
|
|
|
114
115
|
*/
|
|
115
116
|
locale?: string;
|
|
116
117
|
/** Maximum date that the user can select. */
|
|
117
|
-
maxDate?:
|
|
118
|
+
maxDate?: T;
|
|
118
119
|
/**
|
|
119
120
|
* The most detailed calendar view that the user shall see. Can be `"hour"`, `"minute"` or `"second"`.
|
|
120
121
|
*
|
|
@@ -122,7 +123,7 @@
|
|
|
122
123
|
*/
|
|
123
124
|
maxDetail?: Detail;
|
|
124
125
|
/** Minimum date that the user can select. */
|
|
125
|
-
minDate?:
|
|
126
|
+
minDate?: T;
|
|
126
127
|
/** `aria-label` for the minute input. */
|
|
127
128
|
minuteAriaLabel?: string;
|
|
128
129
|
/**
|
|
@@ -152,7 +153,7 @@
|
|
|
152
153
|
/** Function called when the calendar opens. */
|
|
153
154
|
onCalendarOpen?: () => void;
|
|
154
155
|
/** Function called when the user picks a valid datetime. */
|
|
155
|
-
onChange?: (value: Value) => void;
|
|
156
|
+
onChange?: (value: Value<T>) => void;
|
|
156
157
|
/** Function called when the clock closes. */
|
|
157
158
|
onClockClose?: () => void;
|
|
158
159
|
/** Function called when the clock opens. */
|
|
@@ -214,7 +215,7 @@
|
|
|
214
215
|
* @example [new Date(2017, 0, 1, 22, 15), new Date(2017, 0, 1, 23, 45)]
|
|
215
216
|
* @example ["2017-01-01T22:15:00", "2017-01-01T23:45:00"]
|
|
216
217
|
*/
|
|
217
|
-
value?: LooseValue
|
|
218
|
+
value?: LooseValue<T>;
|
|
218
219
|
/** `aria-label` for the year input. */
|
|
219
220
|
yearAriaLabel?: string;
|
|
220
221
|
/**
|
|
@@ -277,7 +278,9 @@
|
|
|
277
278
|
yearAriaLabel,
|
|
278
279
|
yearPlaceholder,
|
|
279
280
|
...otherProps
|
|
280
|
-
}: DateTimeRangePickerProps = $props();
|
|
281
|
+
}: DateTimeRangePickerProps<T> = $props();
|
|
282
|
+
|
|
283
|
+
const adapter = getDateAdapter<T>('DateTimeRangePicker');
|
|
281
284
|
|
|
282
285
|
let isCalendarOpen = $state<boolean | null>(untrack(() => isCalendarOpenProps));
|
|
283
286
|
let isClockOpen = $state<boolean | null>(untrack(() => isClockOpenProps));
|
|
@@ -351,7 +354,7 @@
|
|
|
351
354
|
closeClock({ reason });
|
|
352
355
|
}
|
|
353
356
|
|
|
354
|
-
function onChange(nextValue: Value
|
|
357
|
+
function onChange(nextValue: Value<T>, shouldCloseWidgets = shouldCloseWidgetsOnSelect) {
|
|
355
358
|
if (shouldCloseWidgets) {
|
|
356
359
|
closeAllWidgets({ reason: 'select' });
|
|
357
360
|
}
|
|
@@ -359,33 +362,34 @@
|
|
|
359
362
|
onChangeProps?.(nextValue);
|
|
360
363
|
}
|
|
361
364
|
|
|
362
|
-
function onChangeFrom(nextValueFrom:
|
|
363
|
-
onChange([nextValueFrom,
|
|
365
|
+
function onChangeFrom(nextValueFrom: T | null, shouldCloseWidgets: boolean) {
|
|
366
|
+
onChange([nextValueFrom, normalizeDate(valueTo)], shouldCloseWidgets);
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
function onChangeTo(nextValueTo: T | null, shouldCloseWidgets: boolean) {
|
|
370
|
+
onChange([normalizeDate(valueFrom), nextValueTo], shouldCloseWidgets);
|
|
364
371
|
}
|
|
365
372
|
|
|
366
|
-
function
|
|
367
|
-
|
|
373
|
+
function normalizeDate(value: string | T | null | undefined): T | null {
|
|
374
|
+
if (value === null || value === undefined || value === '') return null;
|
|
375
|
+
return typeof value === 'string' ? adapter.date(value) : value;
|
|
368
376
|
}
|
|
369
377
|
|
|
370
|
-
function withTimeOf(date:
|
|
378
|
+
function withTimeOf(date: T | null, source: string | T | null | undefined): T | null {
|
|
371
379
|
if (!source || !date) {
|
|
372
380
|
return date;
|
|
373
381
|
}
|
|
374
382
|
|
|
375
|
-
const sourceDate =
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
result.
|
|
379
|
-
|
|
380
|
-
sourceDate.getMinutes(),
|
|
381
|
-
sourceDate.getSeconds(),
|
|
382
|
-
sourceDate.getMilliseconds(),
|
|
383
|
-
);
|
|
383
|
+
const sourceDate = typeof source === 'string' ? adapter.date(source) : source;
|
|
384
|
+
let result = adapter.setHours(date, adapter.getHours(sourceDate));
|
|
385
|
+
result = adapter.setMinutes(result, adapter.getMinutes(sourceDate));
|
|
386
|
+
result = adapter.setSeconds(result, adapter.getSeconds(sourceDate));
|
|
387
|
+
result = adapter.setMilliseconds(result, adapter.getMilliseconds(sourceDate));
|
|
384
388
|
|
|
385
389
|
return result;
|
|
386
390
|
}
|
|
387
391
|
|
|
388
|
-
function onDateChange(nextValue: Value
|
|
392
|
+
function onDateChange(nextValue: Value<T>, shouldCloseWidgets?: boolean) {
|
|
389
393
|
// The calendar passes an array of values when selectRange is enabled
|
|
390
394
|
const [rawNextValueFrom, rawNextValueTo] = Array.isArray(nextValue) ? nextValue : [nextValue];
|
|
391
395
|
|
|
@@ -551,7 +555,7 @@
|
|
|
551
555
|
{locale}
|
|
552
556
|
{maxDate}
|
|
553
557
|
{minDate}
|
|
554
|
-
onChange={(nextValue) => onDateChange(nextValue)}
|
|
558
|
+
onChange={(nextValue) => onDateChange(nextValue as Value<T>)}
|
|
555
559
|
selectRange
|
|
556
560
|
{value}
|
|
557
561
|
{...calendarProps}
|
|
@@ -585,7 +589,8 @@
|
|
|
585
589
|
{...commonInputProps}
|
|
586
590
|
{autoFocus}
|
|
587
591
|
name="{name}_from"
|
|
588
|
-
onChange={
|
|
592
|
+
onChange={(nextValue, shouldCloseWidgets) =>
|
|
593
|
+
onChangeFrom(nextValue as T | null, shouldCloseWidgets)}
|
|
589
594
|
value={valueFrom}
|
|
590
595
|
/>
|
|
591
596
|
<span class="{baseClassName}__range-divider" data-testid="range-divider">
|
|
@@ -598,7 +603,8 @@
|
|
|
598
603
|
<DateTimeInput
|
|
599
604
|
{...commonInputProps}
|
|
600
605
|
name="{name}_to"
|
|
601
|
-
onChange={
|
|
606
|
+
onChange={(nextValue, shouldCloseWidgets) =>
|
|
607
|
+
onChangeTo(nextValue as T | null, shouldCloseWidgets)}
|
|
602
608
|
value={valueTo}
|
|
603
609
|
/>
|
|
604
610
|
{#if clearIcon !== null}
|
|
@@ -4,9 +4,9 @@ import { Calendar } from 'svelte-range-calendar';
|
|
|
4
4
|
import type { ComponentProps, Snippet } from 'svelte';
|
|
5
5
|
import type { ClassValue, HTMLAttributes } from 'svelte/elements';
|
|
6
6
|
import type { CloseReason, Detail, LooseValue, OpenReason, Value } from './shared/types.js';
|
|
7
|
-
type CalendarProps = Omit<ComponentProps<typeof Calendar>, 'onChange' | 'selectRange' | 'value'>;
|
|
8
|
-
type ClockProps = Omit<ComponentProps<typeof Clock>, 'value'>;
|
|
9
|
-
type DateTimeRangePickerProps = {
|
|
7
|
+
type CalendarProps<T> = Omit<ComponentProps<typeof Calendar>, 'onChange' | 'selectRange' | 'value'>;
|
|
8
|
+
type ClockProps<T> = Omit<ComponentProps<typeof Clock>, 'value'>;
|
|
9
|
+
type DateTimeRangePickerProps<T> = {
|
|
10
10
|
/** `aria-label` for the AM/PM select input. */
|
|
11
11
|
amPmAriaLabel?: string;
|
|
12
12
|
/** Automatically focuses the input on mount. */
|
|
@@ -18,7 +18,7 @@ type DateTimeRangePickerProps = {
|
|
|
18
18
|
*/
|
|
19
19
|
calendarIcon?: Snippet | string | null;
|
|
20
20
|
/** Props to pass to the Calendar component. */
|
|
21
|
-
calendarProps?: CalendarProps
|
|
21
|
+
calendarProps?: CalendarProps<T>;
|
|
22
22
|
/**
|
|
23
23
|
* Class name(s) that will be added along with `"svelte-datetimerange-picker"` to the main `<div>` element.
|
|
24
24
|
*
|
|
@@ -33,7 +33,7 @@ type DateTimeRangePickerProps = {
|
|
|
33
33
|
*/
|
|
34
34
|
clearIcon?: Snippet | string | null;
|
|
35
35
|
/** Props to pass to the Clock component. */
|
|
36
|
-
clockProps?: ClockProps
|
|
36
|
+
clockProps?: ClockProps<T>;
|
|
37
37
|
/**
|
|
38
38
|
* Whether to close the widgets on value selection. **Note**: It's recommended to use `shouldCloseWidgets` function instead.
|
|
39
39
|
*
|
|
@@ -101,7 +101,7 @@ type DateTimeRangePickerProps = {
|
|
|
101
101
|
*/
|
|
102
102
|
locale?: string;
|
|
103
103
|
/** Maximum date that the user can select. */
|
|
104
|
-
maxDate?:
|
|
104
|
+
maxDate?: T;
|
|
105
105
|
/**
|
|
106
106
|
* The most detailed calendar view that the user shall see. Can be `"hour"`, `"minute"` or `"second"`.
|
|
107
107
|
*
|
|
@@ -109,7 +109,7 @@ type DateTimeRangePickerProps = {
|
|
|
109
109
|
*/
|
|
110
110
|
maxDetail?: Detail;
|
|
111
111
|
/** Minimum date that the user can select. */
|
|
112
|
-
minDate?:
|
|
112
|
+
minDate?: T;
|
|
113
113
|
/** `aria-label` for the minute input. */
|
|
114
114
|
minuteAriaLabel?: string;
|
|
115
115
|
/**
|
|
@@ -139,7 +139,7 @@ type DateTimeRangePickerProps = {
|
|
|
139
139
|
/** Function called when the calendar opens. */
|
|
140
140
|
onCalendarOpen?: () => void;
|
|
141
141
|
/** Function called when the user picks a valid datetime. */
|
|
142
|
-
onChange?: (value: Value) => void;
|
|
142
|
+
onChange?: (value: Value<T>) => void;
|
|
143
143
|
/** Function called when the clock closes. */
|
|
144
144
|
onClockClose?: () => void;
|
|
145
145
|
/** Function called when the clock opens. */
|
|
@@ -207,7 +207,7 @@ type DateTimeRangePickerProps = {
|
|
|
207
207
|
* @example [new Date(2017, 0, 1, 22, 15), new Date(2017, 0, 1, 23, 45)]
|
|
208
208
|
* @example ["2017-01-01T22:15:00", "2017-01-01T23:45:00"]
|
|
209
209
|
*/
|
|
210
|
-
value?: LooseValue
|
|
210
|
+
value?: LooseValue<T>;
|
|
211
211
|
/** `aria-label` for the year input. */
|
|
212
212
|
yearAriaLabel?: string;
|
|
213
213
|
/**
|
|
@@ -217,6 +217,27 @@ type DateTimeRangePickerProps = {
|
|
|
217
217
|
*/
|
|
218
218
|
yearPlaceholder?: string;
|
|
219
219
|
} & Omit<HTMLAttributes<HTMLDivElement>, 'class' | 'onchange' | 'onfocus'>;
|
|
220
|
-
declare
|
|
221
|
-
|
|
220
|
+
declare function $$render<T>(): {
|
|
221
|
+
props: DateTimeRangePickerProps<T>;
|
|
222
|
+
exports: {};
|
|
223
|
+
bindings: "";
|
|
224
|
+
slots: {};
|
|
225
|
+
events: {};
|
|
226
|
+
};
|
|
227
|
+
declare class __sveltets_Render<T> {
|
|
228
|
+
props(): ReturnType<typeof $$render<T>>['props'];
|
|
229
|
+
events(): ReturnType<typeof $$render<T>>['events'];
|
|
230
|
+
slots(): ReturnType<typeof $$render<T>>['slots'];
|
|
231
|
+
bindings(): "";
|
|
232
|
+
exports(): {};
|
|
233
|
+
}
|
|
234
|
+
interface $$IsomorphicComponent {
|
|
235
|
+
new <T>(options: import('svelte').ComponentConstructorOptions<ReturnType<__sveltets_Render<T>['props']>>): import('svelte').SvelteComponent<ReturnType<__sveltets_Render<T>['props']>, ReturnType<__sveltets_Render<T>['events']>, ReturnType<__sveltets_Render<T>['slots']>> & {
|
|
236
|
+
$$bindings?: ReturnType<__sveltets_Render<T>['bindings']>;
|
|
237
|
+
} & ReturnType<__sveltets_Render<T>['exports']>;
|
|
238
|
+
<T>(internal: unknown, props: ReturnType<__sveltets_Render<T>['props']> & {}): ReturnType<__sveltets_Render<T>['exports']>;
|
|
239
|
+
z_$$bindings?: ReturnType<__sveltets_Render<any>['bindings']>;
|
|
240
|
+
}
|
|
241
|
+
declare const DateTimeRangePicker: $$IsomorphicComponent;
|
|
242
|
+
type DateTimeRangePicker<T> = InstanceType<typeof DateTimeRangePicker<T>>;
|
|
222
243
|
export default DateTimeRangePicker;
|
package/dist/shared/types.d.ts
CHANGED
|
@@ -2,9 +2,9 @@ export type Range<T> = [T, T];
|
|
|
2
2
|
export type AmPmType = 'am' | 'pm';
|
|
3
3
|
export type CloseReason = 'buttonClick' | 'escape' | 'outsideAction' | 'select';
|
|
4
4
|
export type Detail = 'hour' | 'minute' | 'second';
|
|
5
|
-
type LooseValuePiece = string |
|
|
6
|
-
export type LooseValue = LooseValuePiece | Range<LooseValuePiece
|
|
5
|
+
type LooseValuePiece<T = Date> = string | T | null;
|
|
6
|
+
export type LooseValue<T = Date> = LooseValuePiece<T> | Range<LooseValuePiece<T>>;
|
|
7
7
|
export type OpenReason = 'buttonClick' | 'focus';
|
|
8
|
-
type ValuePiece = Date | null;
|
|
9
|
-
export type Value = ValuePiece | Range<ValuePiece
|
|
8
|
+
type ValuePiece<T = Date> = T | null;
|
|
9
|
+
export type Value<T = Date> = ValuePiece<T> | Range<ValuePiece<T>>;
|
|
10
10
|
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "svelte-datetimerange-picker",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "A datetime range picker for your Svelte app.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"calendar",
|
|
@@ -55,10 +55,10 @@
|
|
|
55
55
|
"watch": "svelte-package --input src --output dist --watch"
|
|
56
56
|
},
|
|
57
57
|
"dependencies": {
|
|
58
|
-
"svelte-analog-clock": "^0.
|
|
59
|
-
"svelte-datetime-input": "^0.
|
|
60
|
-
"svelte-fit-action": "^0.
|
|
61
|
-
"svelte-range-calendar": "^0.
|
|
58
|
+
"svelte-analog-clock": "^0.5.0",
|
|
59
|
+
"svelte-datetime-input": "^0.5.0",
|
|
60
|
+
"svelte-fit-action": "^0.5.0",
|
|
61
|
+
"svelte-range-calendar": "^0.5.0"
|
|
62
62
|
},
|
|
63
63
|
"devDependencies": {
|
|
64
64
|
"@sveltejs/package": "^3.0.1",
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
<script lang="ts">
|
|
1
|
+
<script lang="ts" generics="T">
|
|
2
2
|
import './DateTimeRangePicker.css';
|
|
3
3
|
|
|
4
4
|
import { untrack } from 'svelte';
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
import { DateTimeInput } from 'svelte-datetime-input';
|
|
7
7
|
import { fit, portal } from 'svelte-fit-action';
|
|
8
8
|
import { Calendar } from 'svelte-range-calendar';
|
|
9
|
+
import { getDateAdapter } from 'svelte-date-utils';
|
|
9
10
|
|
|
10
11
|
import type { ComponentProps, Snippet } from 'svelte';
|
|
11
12
|
import type { ClassValue, HTMLAttributes } from 'svelte/elements';
|
|
@@ -15,11 +16,11 @@
|
|
|
15
16
|
const outsideActionEvents = ['mousedown', 'focusin', 'touchstart'] as const;
|
|
16
17
|
const allViews = ['hour', 'minute', 'second'] as const;
|
|
17
18
|
|
|
18
|
-
type CalendarProps = Omit<ComponentProps<typeof Calendar>, 'onChange' | 'selectRange' | 'value'>;
|
|
19
|
+
type CalendarProps<T> = Omit<ComponentProps<typeof Calendar>, 'onChange' | 'selectRange' | 'value'>;
|
|
19
20
|
|
|
20
|
-
type ClockProps = Omit<ComponentProps<typeof Clock>, 'value'>;
|
|
21
|
+
type ClockProps<T> = Omit<ComponentProps<typeof Clock>, 'value'>;
|
|
21
22
|
|
|
22
|
-
type DateTimeRangePickerProps = {
|
|
23
|
+
type DateTimeRangePickerProps<T> = {
|
|
23
24
|
/** `aria-label` for the AM/PM select input. */
|
|
24
25
|
amPmAriaLabel?: string;
|
|
25
26
|
/** Automatically focuses the input on mount. */
|
|
@@ -31,7 +32,7 @@
|
|
|
31
32
|
*/
|
|
32
33
|
calendarIcon?: Snippet | string | null;
|
|
33
34
|
/** Props to pass to the Calendar component. */
|
|
34
|
-
calendarProps?: CalendarProps
|
|
35
|
+
calendarProps?: CalendarProps<T>;
|
|
35
36
|
/**
|
|
36
37
|
* Class name(s) that will be added along with `"svelte-datetimerange-picker"` to the main `<div>` element.
|
|
37
38
|
*
|
|
@@ -46,7 +47,7 @@
|
|
|
46
47
|
*/
|
|
47
48
|
clearIcon?: Snippet | string | null;
|
|
48
49
|
/** Props to pass to the Clock component. */
|
|
49
|
-
clockProps?: ClockProps
|
|
50
|
+
clockProps?: ClockProps<T>;
|
|
50
51
|
/**
|
|
51
52
|
* Whether to close the widgets on value selection. **Note**: It's recommended to use `shouldCloseWidgets` function instead.
|
|
52
53
|
*
|
|
@@ -114,7 +115,7 @@
|
|
|
114
115
|
*/
|
|
115
116
|
locale?: string;
|
|
116
117
|
/** Maximum date that the user can select. */
|
|
117
|
-
maxDate?:
|
|
118
|
+
maxDate?: T;
|
|
118
119
|
/**
|
|
119
120
|
* The most detailed calendar view that the user shall see. Can be `"hour"`, `"minute"` or `"second"`.
|
|
120
121
|
*
|
|
@@ -122,7 +123,7 @@
|
|
|
122
123
|
*/
|
|
123
124
|
maxDetail?: Detail;
|
|
124
125
|
/** Minimum date that the user can select. */
|
|
125
|
-
minDate?:
|
|
126
|
+
minDate?: T;
|
|
126
127
|
/** `aria-label` for the minute input. */
|
|
127
128
|
minuteAriaLabel?: string;
|
|
128
129
|
/**
|
|
@@ -152,7 +153,7 @@
|
|
|
152
153
|
/** Function called when the calendar opens. */
|
|
153
154
|
onCalendarOpen?: () => void;
|
|
154
155
|
/** Function called when the user picks a valid datetime. */
|
|
155
|
-
onChange?: (value: Value) => void;
|
|
156
|
+
onChange?: (value: Value<T>) => void;
|
|
156
157
|
/** Function called when the clock closes. */
|
|
157
158
|
onClockClose?: () => void;
|
|
158
159
|
/** Function called when the clock opens. */
|
|
@@ -214,7 +215,7 @@
|
|
|
214
215
|
* @example [new Date(2017, 0, 1, 22, 15), new Date(2017, 0, 1, 23, 45)]
|
|
215
216
|
* @example ["2017-01-01T22:15:00", "2017-01-01T23:45:00"]
|
|
216
217
|
*/
|
|
217
|
-
value?: LooseValue
|
|
218
|
+
value?: LooseValue<T>;
|
|
218
219
|
/** `aria-label` for the year input. */
|
|
219
220
|
yearAriaLabel?: string;
|
|
220
221
|
/**
|
|
@@ -277,7 +278,9 @@
|
|
|
277
278
|
yearAriaLabel,
|
|
278
279
|
yearPlaceholder,
|
|
279
280
|
...otherProps
|
|
280
|
-
}: DateTimeRangePickerProps = $props();
|
|
281
|
+
}: DateTimeRangePickerProps<T> = $props();
|
|
282
|
+
|
|
283
|
+
const adapter = getDateAdapter<T>('DateTimeRangePicker');
|
|
281
284
|
|
|
282
285
|
let isCalendarOpen = $state<boolean | null>(untrack(() => isCalendarOpenProps));
|
|
283
286
|
let isClockOpen = $state<boolean | null>(untrack(() => isClockOpenProps));
|
|
@@ -351,7 +354,7 @@
|
|
|
351
354
|
closeClock({ reason });
|
|
352
355
|
}
|
|
353
356
|
|
|
354
|
-
function onChange(nextValue: Value
|
|
357
|
+
function onChange(nextValue: Value<T>, shouldCloseWidgets = shouldCloseWidgetsOnSelect) {
|
|
355
358
|
if (shouldCloseWidgets) {
|
|
356
359
|
closeAllWidgets({ reason: 'select' });
|
|
357
360
|
}
|
|
@@ -359,33 +362,34 @@
|
|
|
359
362
|
onChangeProps?.(nextValue);
|
|
360
363
|
}
|
|
361
364
|
|
|
362
|
-
function onChangeFrom(nextValueFrom:
|
|
363
|
-
onChange([nextValueFrom,
|
|
365
|
+
function onChangeFrom(nextValueFrom: T | null, shouldCloseWidgets: boolean) {
|
|
366
|
+
onChange([nextValueFrom, normalizeDate(valueTo)], shouldCloseWidgets);
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
function onChangeTo(nextValueTo: T | null, shouldCloseWidgets: boolean) {
|
|
370
|
+
onChange([normalizeDate(valueFrom), nextValueTo], shouldCloseWidgets);
|
|
364
371
|
}
|
|
365
372
|
|
|
366
|
-
function
|
|
367
|
-
|
|
373
|
+
function normalizeDate(value: string | T | null | undefined): T | null {
|
|
374
|
+
if (value === null || value === undefined || value === '') return null;
|
|
375
|
+
return typeof value === 'string' ? adapter.date(value) : value;
|
|
368
376
|
}
|
|
369
377
|
|
|
370
|
-
function withTimeOf(date:
|
|
378
|
+
function withTimeOf(date: T | null, source: string | T | null | undefined): T | null {
|
|
371
379
|
if (!source || !date) {
|
|
372
380
|
return date;
|
|
373
381
|
}
|
|
374
382
|
|
|
375
|
-
const sourceDate =
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
result.
|
|
379
|
-
|
|
380
|
-
sourceDate.getMinutes(),
|
|
381
|
-
sourceDate.getSeconds(),
|
|
382
|
-
sourceDate.getMilliseconds(),
|
|
383
|
-
);
|
|
383
|
+
const sourceDate = typeof source === 'string' ? adapter.date(source) : source;
|
|
384
|
+
let result = adapter.setHours(date, adapter.getHours(sourceDate));
|
|
385
|
+
result = adapter.setMinutes(result, adapter.getMinutes(sourceDate));
|
|
386
|
+
result = adapter.setSeconds(result, adapter.getSeconds(sourceDate));
|
|
387
|
+
result = adapter.setMilliseconds(result, adapter.getMilliseconds(sourceDate));
|
|
384
388
|
|
|
385
389
|
return result;
|
|
386
390
|
}
|
|
387
391
|
|
|
388
|
-
function onDateChange(nextValue: Value
|
|
392
|
+
function onDateChange(nextValue: Value<T>, shouldCloseWidgets?: boolean) {
|
|
389
393
|
// The calendar passes an array of values when selectRange is enabled
|
|
390
394
|
const [rawNextValueFrom, rawNextValueTo] = Array.isArray(nextValue) ? nextValue : [nextValue];
|
|
391
395
|
|
|
@@ -551,7 +555,7 @@
|
|
|
551
555
|
{locale}
|
|
552
556
|
{maxDate}
|
|
553
557
|
{minDate}
|
|
554
|
-
onChange={(nextValue) => onDateChange(nextValue)}
|
|
558
|
+
onChange={(nextValue) => onDateChange(nextValue as Value<T>)}
|
|
555
559
|
selectRange
|
|
556
560
|
{value}
|
|
557
561
|
{...calendarProps}
|
|
@@ -585,7 +589,8 @@
|
|
|
585
589
|
{...commonInputProps}
|
|
586
590
|
{autoFocus}
|
|
587
591
|
name="{name}_from"
|
|
588
|
-
onChange={
|
|
592
|
+
onChange={(nextValue, shouldCloseWidgets) =>
|
|
593
|
+
onChangeFrom(nextValue as T | null, shouldCloseWidgets)}
|
|
589
594
|
value={valueFrom}
|
|
590
595
|
/>
|
|
591
596
|
<span class="{baseClassName}__range-divider" data-testid="range-divider">
|
|
@@ -598,7 +603,8 @@
|
|
|
598
603
|
<DateTimeInput
|
|
599
604
|
{...commonInputProps}
|
|
600
605
|
name="{name}_to"
|
|
601
|
-
onChange={
|
|
606
|
+
onChange={(nextValue, shouldCloseWidgets) =>
|
|
607
|
+
onChangeTo(nextValue as T | null, shouldCloseWidgets)}
|
|
602
608
|
value={valueTo}
|
|
603
609
|
/>
|
|
604
610
|
{#if clearIcon !== null}
|
package/src/shared/types.ts
CHANGED
|
@@ -6,12 +6,12 @@ export type CloseReason = 'buttonClick' | 'escape' | 'outsideAction' | 'select';
|
|
|
6
6
|
|
|
7
7
|
export type Detail = 'hour' | 'minute' | 'second';
|
|
8
8
|
|
|
9
|
-
type LooseValuePiece = string |
|
|
9
|
+
type LooseValuePiece<T = Date> = string | T | null;
|
|
10
10
|
|
|
11
|
-
export type LooseValue = LooseValuePiece | Range<LooseValuePiece
|
|
11
|
+
export type LooseValue<T = Date> = LooseValuePiece<T> | Range<LooseValuePiece<T>>;
|
|
12
12
|
|
|
13
13
|
export type OpenReason = 'buttonClick' | 'focus';
|
|
14
14
|
|
|
15
|
-
type ValuePiece = Date | null;
|
|
15
|
+
type ValuePiece<T = Date> = T | null;
|
|
16
16
|
|
|
17
|
-
export type Value = ValuePiece | Range<ValuePiece
|
|
17
|
+
export type Value<T = Date> = ValuePiece<T> | Range<ValuePiece<T>>;
|