@react-aria/datepicker 3.15.3 → 3.16.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.
@@ -17,7 +17,7 @@ import {CalendarProps} from '@react-types/calendar';
17
17
  import {createFocusManager} from '@react-aria/focus';
18
18
  import {DatePickerState} from '@react-stately/datepicker';
19
19
  import {DOMAttributes, GroupDOMAttributes, KeyboardEvent, RefObject, ValidationResult} from '@react-types/shared';
20
- import {filterDOMProps, mergeProps, useDescription, useId} from '@react-aria/utils';
20
+ import {filterDOMProps, mergeProps, nodeContains, useDescription, useId} from '@react-aria/utils';
21
21
  // @ts-ignore
22
22
  import intlMessages from '../intl/*.json';
23
23
  import {privateValidationStateProp} from '@react-stately/form';
@@ -84,7 +84,7 @@ export function useDatePicker<T extends DateValue>(props: AriaDatePickerProps<T>
84
84
  onBlurWithin: e => {
85
85
  // Ignore when focus moves into the popover.
86
86
  let dialog = document.getElementById(dialogId);
87
- if (!dialog?.contains(e.relatedTarget)) {
87
+ if (!nodeContains(dialog, e.relatedTarget)) {
88
88
  isFocused.current = false;
89
89
  props.onBlur?.(e);
90
90
  props.onFocusChange?.(false);
@@ -1,7 +1,7 @@
1
1
  import {createFocusManager, getFocusableTreeWalker} from '@react-aria/focus';
2
2
  import {DateFieldState, DatePickerState, DateRangePickerState} from '@react-stately/datepicker';
3
3
  import {DOMAttributes, FocusableElement, KeyboardEvent, RefObject} from '@react-types/shared';
4
- import {mergeProps} from '@react-aria/utils';
4
+ import {getEventTarget, mergeProps, nodeContains} from '@react-aria/utils';
5
5
  import {useLocale} from '@react-aria/i18n';
6
6
  import {useMemo} from 'react';
7
7
  import {usePress} from '@react-aria/interactions';
@@ -12,7 +12,7 @@ export function useDatePickerGroup(state: DatePickerState | DateRangePickerState
12
12
 
13
13
  // Open the popover on alt + arrow down
14
14
  let onKeyDown = (e: KeyboardEvent) => {
15
- if (!e.currentTarget.contains(e.target)) {
15
+ if (!nodeContains(e.currentTarget, getEventTarget(e) as Element)) {
16
16
  return;
17
17
  }
18
18
 
@@ -32,7 +32,7 @@ export function useDatePickerGroup(state: DatePickerState | DateRangePickerState
32
32
  e.stopPropagation();
33
33
  if (direction === 'rtl') {
34
34
  if (ref.current) {
35
- let target = e.target as FocusableElement;
35
+ let target = getEventTarget(e) as FocusableElement;
36
36
  let prev = findNextSegment(ref.current, target.getBoundingClientRect().left, -1);
37
37
 
38
38
  if (prev) {
@@ -48,7 +48,7 @@ export function useDatePickerGroup(state: DatePickerState | DateRangePickerState
48
48
  e.stopPropagation();
49
49
  if (direction === 'rtl') {
50
50
  if (ref.current) {
51
- let target = e.target as FocusableElement;
51
+ let target = getEventTarget(e) as FocusableElement;
52
52
  let next = findNextSegment(ref.current, target.getBoundingClientRect().left, 1);
53
53
 
54
54
  if (next) {
@@ -68,7 +68,7 @@ export function useDatePickerGroup(state: DatePickerState | DateRangePickerState
68
68
  return;
69
69
  }
70
70
  // Try to find the segment prior to the element that was clicked on.
71
- let target = window.event?.target as FocusableElement;
71
+ let target = window.event ? getEventTarget(window.event) as FocusableElement : null;
72
72
  let walker = getFocusableTreeWalker(ref.current, {tabbable: true});
73
73
  if (target) {
74
74
  walker.currentNode = target;
@@ -18,7 +18,7 @@ import {DateRange, RangeCalendarProps} from '@react-types/calendar';
18
18
  import {DateRangePickerState} from '@react-stately/datepicker';
19
19
  import {DEFAULT_VALIDATION_RESULT, mergeValidation, privateValidationStateProp} from '@react-stately/form';
20
20
  import {DOMAttributes, GroupDOMAttributes, KeyboardEvent, RefObject, ValidationResult} from '@react-types/shared';
21
- import {filterDOMProps, mergeProps, useDescription, useId} from '@react-aria/utils';
21
+ import {filterDOMProps, mergeProps, nodeContains, useDescription, useId} from '@react-aria/utils';
22
22
  import {focusManagerSymbol, roleSymbol} from './useDateField';
23
23
  // @ts-ignore
24
24
  import intlMessages from '../intl/*.json';
@@ -116,7 +116,7 @@ export function useDateRangePicker<T extends DateValue>(props: AriaDateRangePick
116
116
  onBlurWithin: e => {
117
117
  // Ignore when focus moves into the popover.
118
118
  let dialog = document.getElementById(dialogId);
119
- if (!dialog?.contains(e.relatedTarget)) {
119
+ if (!nodeContains(dialog, e.relatedTarget)) {
120
120
  isFocused.current = false;
121
121
  props.onBlur?.(e);
122
122
  props.onFocusChange?.(false);
@@ -12,7 +12,7 @@
12
12
 
13
13
  import {CalendarDate, toCalendar} from '@internationalized/date';
14
14
  import {DateFieldState, DateSegment} from '@react-stately/datepicker';
15
- import {getScrollParent, isIOS, isMac, mergeProps, scrollIntoViewport, useEvent, useId, useLabels, useLayoutEffect} from '@react-aria/utils';
15
+ import {getActiveElement, getScrollParent, isIOS, isMac, mergeProps, nodeContains, scrollIntoViewport, useEvent, useId, useLabels, useLayoutEffect} from '@react-aria/utils';
16
16
  import {hookData} from './useDateField';
17
17
  import {NumberParser} from '@internationalized/number';
18
18
  import React, {CSSProperties, useMemo, useRef} from 'react';
@@ -57,7 +57,7 @@ export function useDateSegment(segment: DateSegment, state: DateFieldState, ref:
57
57
  // The ARIA spec says aria-valuenow is optional if there's no value, but aXe seems to require it.
58
58
  // This doesn't seem to have any negative effects with real AT since we also use aria-valuetext.
59
59
  // https://github.com/dequelabs/axe-core/issues/3505
60
- value: segment.value,
60
+ value: segment.value ?? undefined,
61
61
  textValue,
62
62
  minValue: segment.minValue,
63
63
  maxValue: segment.maxValue,
@@ -82,15 +82,11 @@ export function useDateSegment(segment: DateSegment, state: DateFieldState, ref:
82
82
  },
83
83
  onIncrementToMax: () => {
84
84
  enteredKeys.current = '';
85
- if (segment.maxValue !== undefined) {
86
- state.setSegment(segment.type, segment.maxValue);
87
- }
85
+ state.incrementToMax(segment.type);
88
86
  },
89
87
  onDecrementToMin: () => {
90
88
  enteredKeys.current = '';
91
- if (segment.minValue !== undefined) {
92
- state.setSegment(segment.type, segment.minValue);
93
- }
89
+ state.decrementToMin(segment.type);
94
90
  }
95
91
  });
96
92
 
@@ -110,7 +106,7 @@ export function useDateSegment(segment: DateSegment, state: DateFieldState, ref:
110
106
  state.setSegment(segment.type, parsed);
111
107
  }
112
108
  enteredKeys.current = newValue;
113
- } else if (segment.type === 'dayPeriod') {
109
+ } else if (segment.type === 'dayPeriod' || segment.type === 'era') {
114
110
  state.clearSegment(segment.type);
115
111
  }
116
112
  };
@@ -193,7 +189,7 @@ export function useDateSegment(segment: DateSegment, state: DateFieldState, ref:
193
189
  if (startsWith(am, key)) {
194
190
  state.setSegment('dayPeriod', 0);
195
191
  } else if (startsWith(pm, key)) {
196
- state.setSegment('dayPeriod', 12);
192
+ state.setSegment('dayPeriod', 1);
197
193
  } else {
198
194
  break;
199
195
  }
@@ -219,26 +215,7 @@ export function useDateSegment(segment: DateSegment, state: DateFieldState, ref:
219
215
 
220
216
  let numberValue = parser.parse(newValue);
221
217
  let segmentValue = numberValue;
222
- let allowsZero = segment.minValue === 0;
223
- if (segment.type === 'hour' && state.dateFormatter.resolvedOptions().hour12) {
224
- switch (state.dateFormatter.resolvedOptions().hourCycle) {
225
- case 'h11':
226
- if (numberValue > 11) {
227
- segmentValue = parser.parse(key);
228
- }
229
- break;
230
- case 'h12':
231
- allowsZero = false;
232
- if (numberValue > 12) {
233
- segmentValue = parser.parse(key);
234
- }
235
- break;
236
- }
237
-
238
- if (segment.value !== undefined && segment.value >= 12 && numberValue > 1) {
239
- numberValue += 12;
240
- }
241
- } else if (segment.maxValue !== undefined && numberValue > segment.maxValue) {
218
+ if (segment.maxValue !== undefined && numberValue > segment.maxValue) {
242
219
  segmentValue = parser.parse(key);
243
220
  }
244
221
 
@@ -246,16 +223,11 @@ export function useDateSegment(segment: DateSegment, state: DateFieldState, ref:
246
223
  return;
247
224
  }
248
225
 
249
- let shouldSetValue = segmentValue !== 0 || allowsZero;
250
- if (shouldSetValue) {
251
- state.setSegment(segment.type, segmentValue);
252
- }
226
+ state.setSegment(segment.type, segmentValue);
253
227
 
254
228
  if (segment.maxValue !== undefined && (Number(numberValue + '0') > segment.maxValue || newValue.length >= String(segment.maxValue).length)) {
255
229
  enteredKeys.current = '';
256
- if (shouldSetValue) {
257
- focusManager.focusNext();
258
- }
230
+ focusManager.focusNext();
259
231
  } else {
260
232
  enteredKeys.current = newValue;
261
233
  }
@@ -281,7 +253,7 @@ export function useDateSegment(segment: DateSegment, state: DateFieldState, ref:
281
253
  // Otherwise, when tapping on a segment in Android Chrome and then entering text,
282
254
  // composition events will be fired that break the DOM structure and crash the page.
283
255
  let selection = window.getSelection();
284
- if (selection?.anchorNode && ref.current?.contains(selection?.anchorNode)) {
256
+ if (selection?.anchorNode && nodeContains(ref.current, selection?.anchorNode)) {
285
257
  selection.collapse(ref.current);
286
258
  }
287
259
  });
@@ -339,7 +311,7 @@ export function useDateSegment(segment: DateSegment, state: DateFieldState, ref:
339
311
  let element = ref.current;
340
312
  return () => {
341
313
  // If the focused segment is removed, focus the previous one, or the next one if there was no previous one.
342
- if (document.activeElement === element) {
314
+ if (getActiveElement() === element) {
343
315
  let prev = focusManager.focusPrevious();
344
316
  if (!prev) {
345
317
  focusManager.focusNext();