svelte-datetimerange-picker 0.3.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 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?: Date;
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?: Date;
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, shouldCloseWidgets = shouldCloseWidgetsOnSelect) {
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: Date | null, shouldCloseWidgets: boolean) {
363
- onChange([nextValueFrom, valueTo ? new Date(valueTo) : null], shouldCloseWidgets);
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 onChangeTo(nextValueTo: Date | null, shouldCloseWidgets: boolean) {
367
- onChange([valueFrom ? new Date(valueFrom) : null, nextValueTo], shouldCloseWidgets);
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: Date | null, source: string | Date | null | undefined): Date | null {
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 = new Date(source);
376
- const result = new Date(date);
377
-
378
- result.setHours(
379
- sourceDate.getHours(),
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, shouldCloseWidgets?: boolean) {
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={onChangeFrom}
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={onChangeTo}
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?: Date;
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?: Date;
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 const DateTimeRangePicker: import("svelte").Component<DateTimeRangePickerProps, {}, "">;
221
- type DateTimeRangePicker = ReturnType<typeof DateTimeRangePicker>;
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;
@@ -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 | Date | null;
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.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.3.0",
59
- "svelte-datetime-input": "^0.3.0",
60
- "svelte-fit-action": "^0.3.0",
61
- "svelte-range-calendar": "^0.3.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?: Date;
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?: Date;
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, shouldCloseWidgets = shouldCloseWidgetsOnSelect) {
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: Date | null, shouldCloseWidgets: boolean) {
363
- onChange([nextValueFrom, valueTo ? new Date(valueTo) : null], shouldCloseWidgets);
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 onChangeTo(nextValueTo: Date | null, shouldCloseWidgets: boolean) {
367
- onChange([valueFrom ? new Date(valueFrom) : null, nextValueTo], shouldCloseWidgets);
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: Date | null, source: string | Date | null | undefined): Date | null {
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 = new Date(source);
376
- const result = new Date(date);
377
-
378
- result.setHours(
379
- sourceDate.getHours(),
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, shouldCloseWidgets?: boolean) {
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={onChangeFrom}
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={onChangeTo}
606
+ onChange={(nextValue, shouldCloseWidgets) =>
607
+ onChangeTo(nextValue as T | null, shouldCloseWidgets)}
602
608
  value={valueTo}
603
609
  />
604
610
  {#if clearIcon !== null}
@@ -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 | Date | null;
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>>;