@react-aria/datepicker 3.0.0-nightly.3180 → 3.0.0-rc.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/src/index.ts CHANGED
@@ -14,4 +14,10 @@ export {useDatePicker} from './useDatePicker';
14
14
  export {useDateSegment} from './useDateSegment';
15
15
  export {useDateField, useTimeField} from './useDateField';
16
16
  export {useDateRangePicker} from './useDateRangePicker';
17
- export * from './useDisplayNames';
17
+ export {useDisplayNames} from './useDisplayNames';
18
+
19
+ export type {AriaDatePickerProps, AriaDateRangePickerProps} from '@react-types/datepicker';
20
+ export type {AriaDateFieldProps, DateFieldAria} from './useDateField';
21
+ export type {DatePickerAria} from './useDatePicker';
22
+ export type {DateRangePickerAria} from './useDateRangePicker';
23
+ export type {DateSegmentAria} from './useDateSegment';
@@ -13,16 +13,19 @@
13
13
  import {AriaDatePickerProps, AriaTimeFieldProps, DateValue, TimeValue} from '@react-types/datepicker';
14
14
  import {createFocusManager, FocusManager} from '@react-aria/focus';
15
15
  import {DateFieldState} from '@react-stately/datepicker';
16
+ import {filterDOMProps, mergeProps, useDescription} from '@react-aria/utils';
16
17
  import {HTMLAttributes, RefObject, useEffect, useMemo, useRef} from 'react';
17
- import {mergeProps, useDescription} from '@react-aria/utils';
18
+ // @ts-ignore
19
+ import intlMessages from '../intl/*.json';
18
20
  import {useDatePickerGroup} from './useDatePickerGroup';
19
21
  import {useField} from '@react-aria/label';
20
22
  import {useFocusWithin} from '@react-aria/interactions';
23
+ import {useMessageFormatter} from '@react-aria/i18n';
21
24
 
22
25
  // Allows this hook to also be used with TimeField
23
- interface DateFieldProps<T extends DateValue> extends Omit<AriaDatePickerProps<T>, 'value' | 'defaultValue' | 'onChange' | 'minValue' | 'maxValue' | 'placeholderValue'> {}
26
+ export interface AriaDateFieldProps<T extends DateValue> extends Omit<AriaDatePickerProps<T>, 'value' | 'defaultValue' | 'onChange' | 'minValue' | 'maxValue' | 'placeholderValue'> {}
24
27
 
25
- interface DateFieldAria {
28
+ export interface DateFieldAria {
26
29
  /** Props for the field's visible label element, if any. */
27
30
  labelProps: HTMLAttributes<HTMLElement>,
28
31
  /** Props for the field grouping element. */
@@ -53,7 +56,7 @@ export const focusManagerSymbol = '__focusManager_' + Date.now();
53
56
  * A date field allows users to enter and edit date and time values using a keyboard.
54
57
  * Each part of a date value is displayed in an individually editable segment.
55
58
  */
56
- export function useDateField<T extends DateValue>(props: DateFieldProps<T>, state: DateFieldState, ref: RefObject<HTMLElement>): DateFieldAria {
59
+ export function useDateField<T extends DateValue>(props: AriaDateFieldProps<T>, state: DateFieldState, ref: RefObject<HTMLElement>): DateFieldAria {
57
60
  let {labelProps, fieldProps, descriptionProps, errorMessageProps} = useField({
58
61
  ...props,
59
62
  labelElementType: 'span'
@@ -67,7 +70,11 @@ export function useDateField<T extends DateValue>(props: DateFieldProps<T>, stat
67
70
  }
68
71
  });
69
72
 
70
- let descProps = useDescription(state.formatValue({month: 'long'}));
73
+ let formatMessage = useMessageFormatter(intlMessages);
74
+ let message = state.maxGranularity === 'hour' ? 'selectedTimeDescription' : 'selectedDateDescription';
75
+ let field = state.maxGranularity === 'hour' ? 'time' : 'date';
76
+ let description = state.value ? formatMessage(message, {[field]: state.formatValue({month: 'long'})}) : '';
77
+ let descProps = useDescription(description);
71
78
 
72
79
  // If within a date picker or date range picker, the date field will have role="presentation" and an aria-describedby
73
80
  // will be passed in that references the value (e.g. entire range). Otherwise, add the field's value description.
@@ -111,6 +118,7 @@ export function useDateField<T extends DateValue>(props: DateFieldProps<T>, stat
111
118
  autoFocusRef.current = false;
112
119
  }, [focusManager]);
113
120
 
121
+ let domProps = filterDOMProps(props);
114
122
  return {
115
123
  labelProps: {
116
124
  ...labelProps,
@@ -118,7 +126,7 @@ export function useDateField<T extends DateValue>(props: DateFieldProps<T>, stat
118
126
  focusManager.focusFirst();
119
127
  }
120
128
  },
121
- fieldProps: mergeProps(fieldDOMProps, groupProps, focusWithinProps),
129
+ fieldProps: mergeProps(domProps, fieldDOMProps, groupProps, focusWithinProps),
122
130
  descriptionProps,
123
131
  errorMessageProps
124
132
  };
@@ -16,16 +16,16 @@ import {AriaDialogProps} from '@react-types/dialog';
16
16
  import {CalendarProps} from '@react-types/calendar';
17
17
  import {createFocusManager} from '@react-aria/focus';
18
18
  import {DatePickerState} from '@react-stately/datepicker';
19
+ import {filterDOMProps, mergeProps, useDescription, useId} from '@react-aria/utils';
19
20
  import {HTMLAttributes, RefObject} from 'react';
20
21
  // @ts-ignore
21
22
  import intlMessages from '../intl/*.json';
22
- import {mergeProps, useDescription, useId} from '@react-aria/utils';
23
23
  import {roleSymbol} from './useDateField';
24
24
  import {useDatePickerGroup} from './useDatePickerGroup';
25
25
  import {useField} from '@react-aria/label';
26
26
  import {useLocale, useMessageFormatter} from '@react-aria/i18n';
27
27
 
28
- interface DatePickerAria {
28
+ export interface DatePickerAria {
29
29
  /** Props for the date picker's visible label element, if any. */
30
30
  labelProps: HTMLAttributes<HTMLElement>,
31
31
  /** Props for the grouping element containing the date field and button. */
@@ -63,11 +63,14 @@ export function useDatePicker<T extends DateValue>(props: AriaDatePickerProps<T>
63
63
  let labelledBy = fieldProps['aria-labelledby'] || fieldProps.id;
64
64
 
65
65
  let {locale} = useLocale();
66
- let descProps = useDescription(state.formatValue(locale, {month: 'long'}));
66
+ let date = state.formatValue(locale, {month: 'long'});
67
+ let description = date ? formatMessage('selectedDateDescription', {date}) : '';
68
+ let descProps = useDescription(description);
67
69
  let ariaDescribedBy = [descProps['aria-describedby'], fieldProps['aria-describedby']].filter(Boolean).join(' ') || undefined;
70
+ let domProps = filterDOMProps(props);
68
71
 
69
72
  return {
70
- groupProps: mergeProps(groupProps, fieldProps, descProps, {
73
+ groupProps: mergeProps(domProps, groupProps, fieldProps, descProps, {
71
74
  role: 'group',
72
75
  'aria-disabled': props.isDisabled || null,
73
76
  'aria-labelledby': labelledBy,
@@ -123,7 +126,9 @@ export function useDatePicker<T extends DateValue>(props: AriaDatePickerProps<T>
123
126
  isDisabled: props.isDisabled,
124
127
  isReadOnly: props.isReadOnly,
125
128
  isDateUnavailable: props.isDateUnavailable,
126
- defaultFocusedValue: state.dateValue ? undefined : props.placeholderValue
129
+ defaultFocusedValue: state.dateValue ? undefined : props.placeholderValue,
130
+ validationState: state.validationState,
131
+ errorMessage: props.errorMessage
127
132
  }
128
133
  };
129
134
  }
@@ -15,18 +15,18 @@ import {AriaDatePickerProps, AriaDateRangePickerProps, DateValue} from '@react-t
15
15
  import {AriaDialogProps} from '@react-types/dialog';
16
16
  import {createFocusManager} from '@react-aria/focus';
17
17
  import {DateRangePickerState} from '@react-stately/datepicker';
18
+ import {filterDOMProps, mergeProps, useDescription, useId} from '@react-aria/utils';
18
19
  import {focusManagerSymbol, roleSymbol} from './useDateField';
19
20
  import {HTMLAttributes, RefObject, useMemo} from 'react';
20
21
  // @ts-ignore
21
22
  import intlMessages from '../intl/*.json';
22
- import {mergeProps, useDescription, useId} from '@react-aria/utils';
23
23
  import {RangeCalendarProps} from '@react-types/calendar';
24
24
  import {useDatePickerGroup} from './useDatePickerGroup';
25
25
  import {useField} from '@react-aria/label';
26
26
  import {useFocusWithin} from '@react-aria/interactions';
27
27
  import {useLocale, useMessageFormatter} from '@react-aria/i18n';
28
28
 
29
- interface DateRangePickerAria {
29
+ export interface DateRangePickerAria {
30
30
  /** Props for the date range picker's visible label element, if any. */
31
31
  labelProps: HTMLAttributes<HTMLElement>,
32
32
  /** Props for the grouping element containing the date fields and button. */
@@ -62,7 +62,8 @@ export function useDateRangePicker<T extends DateValue>(props: AriaDateRangePick
62
62
  let labelledBy = fieldProps['aria-labelledby'] || fieldProps.id;
63
63
 
64
64
  let {locale} = useLocale();
65
- let description = state.formatValue(locale, {month: 'long'});
65
+ let range = state.formatValue(locale, {month: 'long'});
66
+ let description = range ? formatMessage('selectedRangeDescription', {startDate: range.start, endDate: range.end}) : '';
66
67
  let descProps = useDescription(description);
67
68
 
68
69
  let startFieldProps = {
@@ -103,8 +104,10 @@ export function useDateRangePicker<T extends DateValue>(props: AriaDateRangePick
103
104
  validationState: state.validationState
104
105
  };
105
106
 
107
+ let domProps = filterDOMProps(props);
108
+
106
109
  return {
107
- groupProps: mergeProps(groupProps, fieldProps, descProps, focusWithinProps, {
110
+ groupProps: mergeProps(domProps, groupProps, fieldProps, descProps, focusWithinProps, {
108
111
  role: 'group',
109
112
  'aria-disabled': props.isDisabled || null,
110
113
  'aria-describedby': ariaDescribedBy
@@ -154,7 +157,9 @@ export function useDateRangePicker<T extends DateValue>(props: AriaDateRangePick
154
157
  isReadOnly: props.isReadOnly,
155
158
  isDateUnavailable: props.isDateUnavailable,
156
159
  allowsNonContiguousRanges: props.allowsNonContiguousRanges,
157
- defaultFocusedValue: state.dateRange ? undefined : props.placeholderValue
160
+ defaultFocusedValue: state.dateRange ? undefined : props.placeholderValue,
161
+ validationState: state.validationState,
162
+ errorMessage: props.errorMessage
158
163
  }
159
164
  };
160
165
  }
@@ -17,10 +17,9 @@ import {NumberParser} from '@internationalized/number';
17
17
  import React, {HTMLAttributes, RefObject, useMemo, useRef} from 'react';
18
18
  import {useDateFormatter, useFilter, useLocale} from '@react-aria/i18n';
19
19
  import {useDisplayNames} from './useDisplayNames';
20
- import {usePress} from '@react-aria/interactions';
21
20
  import {useSpinButton} from '@react-aria/spinbutton';
22
21
 
23
- interface DateSegmentAria {
22
+ export interface DateSegmentAria {
24
23
  /** Props for the segment element. */
25
24
  segmentProps: HTMLAttributes<HTMLDivElement>
26
25
  }
@@ -301,22 +300,6 @@ export function useDateSegment(segment: DateSegment, state: DateFieldState, ref:
301
300
  }
302
301
  });
303
302
 
304
- // Focus on mouse down/touch up to match native textfield behavior.
305
- // usePress handles canceling text selection.
306
- let {pressProps} = usePress({
307
- preventFocusOnPress: true,
308
- onPressStart: (e) => {
309
- if (e.pointerType === 'mouse') {
310
- e.target.focus();
311
- }
312
- },
313
- onPress(e) {
314
- if (e.pointerType !== 'mouse') {
315
- e.target.focus();
316
- }
317
- }
318
- });
319
-
320
303
  // For Android: prevent selection on long press.
321
304
  useEvent(ref, 'selectstart', e => {
322
305
  e.preventDefault();
@@ -360,7 +343,7 @@ export function useDateSegment(segment: DateSegment, state: DateFieldState, ref:
360
343
  }
361
344
 
362
345
  return {
363
- segmentProps: mergeProps(spinButtonProps, pressProps, labelProps, {
346
+ segmentProps: mergeProps(spinButtonProps, labelProps, {
364
347
  id,
365
348
  ...touchPropOverrides,
366
349
  'aria-invalid': state.validationState === 'invalid' ? 'true' : undefined,
@@ -379,7 +362,16 @@ export function useDateSegment(segment: DateSegment, state: DateFieldState, ref:
379
362
  onKeyDown,
380
363
  onFocus,
381
364
  style: {
382
- caretColor: 'transparent'
365
+ caretColor: 'transparent',
366
+ userSelect: 'none',
367
+ WebkitUserSelect: 'none'
368
+ },
369
+ // Prevent pointer events from reaching useDatePickerGroup, and allow native browser behavior to focus the segment.
370
+ onPointerDown(e) {
371
+ e.stopPropagation();
372
+ },
373
+ onMouseDown(e) {
374
+ e.stopPropagation();
383
375
  }
384
376
  })
385
377
  };
@@ -21,6 +21,7 @@ interface DisplayNames {
21
21
  of(field: Field): string
22
22
  }
23
23
 
24
+ /** @private */
24
25
  export function useDisplayNames(): DisplayNames {
25
26
  let {locale} = useLocale();
26
27
  return useMemo(() => {