@react-aria/datepicker 3.0.0-nightly.3175 → 3.0.0-rc.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/main.js +321 -294
- package/dist/main.js.map +1 -1
- package/dist/module.js +322 -282
- package/dist/module.js.map +1 -1
- package/dist/types.d.ts +46 -44
- package/dist/types.d.ts.map +1 -1
- package/package.json +18 -18
- package/src/index.ts +7 -1
- package/src/useDateField.ts +49 -21
- package/src/useDatePicker.ts +15 -8
- package/src/useDatePickerGroup.ts +34 -5
- package/src/useDateRangePicker.ts +22 -17
- package/src/useDateSegment.ts +32 -50
- package/src/useDisplayNames.ts +1 -0
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"mappings":";;;;;;
|
|
1
|
+
{"mappings":"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","sources":["packages/@react-aria/datepicker/src/packages/@react-aria/datepicker/src/useDatePickerGroup.ts","packages/@react-aria/datepicker/src/packages/@react-aria/datepicker/src/useDateField.ts","packages/@react-aria/datepicker/src/packages/@react-aria/datepicker/src/useDatePicker.ts","packages/@react-aria/datepicker/src/packages/@react-aria/datepicker/src/useDisplayNames.ts","packages/@react-aria/datepicker/src/packages/@react-aria/datepicker/src/useDateSegment.ts","packages/@react-aria/datepicker/src/packages/@react-aria/datepicker/src/useDateRangePicker.ts","packages/@react-aria/datepicker/src/packages/@react-aria/datepicker/src/index.ts","packages/@react-aria/datepicker/src/index.ts"],"sourcesContent":[null,null,null,null,null,null,null,"/*\n * Copyright 2020 Adobe. All rights reserved.\n * This file is licensed to you under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License. You may obtain a copy\n * of the License at http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under\n * the License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS\n * OF ANY KIND, either express or implied. See the License for the specific language\n * governing permissions and limitations under the License.\n */\n\nexport {useDatePicker} from './useDatePicker';\nexport {useDateSegment} from './useDateSegment';\nexport {useDateField, useTimeField} from './useDateField';\nexport {useDateRangePicker} from './useDateRangePicker';\nexport {useDisplayNames} from './useDisplayNames';\n\nexport type {AriaDatePickerProps, AriaDateRangePickerProps} from '@react-types/datepicker';\nexport type {AriaDateFieldProps, DateFieldAria} from './useDateField';\nexport type {DatePickerAria} from './useDatePicker';\nexport type {DateRangePickerAria} from './useDateRangePicker';\nexport type {DateSegmentAria} from './useDateSegment';\n"],"names":[],"version":3,"file":"types.d.ts.map"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@react-aria/datepicker",
|
|
3
|
-
"version": "3.0.0-
|
|
3
|
+
"version": "3.0.0-rc.1",
|
|
4
4
|
"description": "Spectrum UI components in React",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"main": "dist/main.js",
|
|
@@ -18,27 +18,27 @@
|
|
|
18
18
|
},
|
|
19
19
|
"dependencies": {
|
|
20
20
|
"@babel/runtime": "^7.6.2",
|
|
21
|
-
"@internationalized/message": "3.0.
|
|
22
|
-
"@internationalized/number": "3.
|
|
23
|
-
"@react-aria/focus": "3.
|
|
24
|
-
"@react-aria/i18n": "3.
|
|
25
|
-
"@react-aria/interactions": "3.
|
|
26
|
-
"@react-aria/label": "3.
|
|
27
|
-
"@react-aria/spinbutton": "3.
|
|
28
|
-
"@react-aria/utils": "3.
|
|
29
|
-
"@react-stately/datepicker": "3.0.0-
|
|
30
|
-
"@react-types/button": "3.
|
|
31
|
-
"@react-types/calendar": "3.0.0-
|
|
32
|
-
"@react-types/datepicker": "3.0.0-
|
|
33
|
-
"@react-types/dialog": "3.
|
|
34
|
-
"@react-types/shared": "3.
|
|
21
|
+
"@internationalized/message": "^3.0.7",
|
|
22
|
+
"@internationalized/number": "^3.1.1",
|
|
23
|
+
"@react-aria/focus": "^3.6.0",
|
|
24
|
+
"@react-aria/i18n": "^3.4.0",
|
|
25
|
+
"@react-aria/interactions": "^3.9.0",
|
|
26
|
+
"@react-aria/label": "^3.3.0",
|
|
27
|
+
"@react-aria/spinbutton": "^3.1.0",
|
|
28
|
+
"@react-aria/utils": "^3.13.0",
|
|
29
|
+
"@react-stately/datepicker": "3.0.0-rc.1",
|
|
30
|
+
"@react-types/button": "^3.5.0",
|
|
31
|
+
"@react-types/calendar": "3.0.0-rc.1",
|
|
32
|
+
"@react-types/datepicker": "3.0.0-rc.1",
|
|
33
|
+
"@react-types/dialog": "^3.4.0",
|
|
34
|
+
"@react-types/shared": "^3.13.0"
|
|
35
35
|
},
|
|
36
36
|
"peerDependencies": {
|
|
37
|
-
"react": "^16.8.0 || ^17.0.0-rc.1",
|
|
38
|
-
"react-dom": "^16.8.0 || ^17.0.0-rc.1"
|
|
37
|
+
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0",
|
|
38
|
+
"react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0"
|
|
39
39
|
},
|
|
40
40
|
"publishConfig": {
|
|
41
41
|
"access": "public"
|
|
42
42
|
},
|
|
43
|
-
"gitHead": "
|
|
43
|
+
"gitHead": "8f921ec5094e7c2b3c301bcb6133372e35a2052b"
|
|
44
44
|
}
|
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
|
|
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';
|
package/src/useDateField.ts
CHANGED
|
@@ -12,19 +12,20 @@
|
|
|
12
12
|
|
|
13
13
|
import {AriaDatePickerProps, AriaTimeFieldProps, DateValue, TimeValue} from '@react-types/datepicker';
|
|
14
14
|
import {createFocusManager, FocusManager} from '@react-aria/focus';
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
15
|
+
import {DateFieldState} from '@react-stately/datepicker';
|
|
16
|
+
import {filterDOMProps, mergeProps, useDescription} from '@react-aria/utils';
|
|
17
17
|
import {HTMLAttributes, RefObject, useEffect, useMemo, useRef} from 'react';
|
|
18
|
-
|
|
19
|
-
import
|
|
18
|
+
// @ts-ignore
|
|
19
|
+
import intlMessages from '../intl/*.json';
|
|
20
20
|
import {useDatePickerGroup} from './useDatePickerGroup';
|
|
21
21
|
import {useField} from '@react-aria/label';
|
|
22
22
|
import {useFocusWithin} from '@react-aria/interactions';
|
|
23
|
+
import {useMessageFormatter} from '@react-aria/i18n';
|
|
23
24
|
|
|
24
25
|
// Allows this hook to also be used with TimeField
|
|
25
|
-
interface
|
|
26
|
+
export interface AriaDateFieldProps<T extends DateValue> extends Omit<AriaDatePickerProps<T>, 'value' | 'defaultValue' | 'onChange' | 'minValue' | 'maxValue' | 'placeholderValue'> {}
|
|
26
27
|
|
|
27
|
-
interface DateFieldAria {
|
|
28
|
+
export interface DateFieldAria {
|
|
28
29
|
/** Props for the field's visible label element, if any. */
|
|
29
30
|
labelProps: HTMLAttributes<HTMLElement>,
|
|
30
31
|
/** Props for the field grouping element. */
|
|
@@ -37,48 +38,78 @@ interface DateFieldAria {
|
|
|
37
38
|
|
|
38
39
|
// Data that is passed between useDateField and useDateSegment.
|
|
39
40
|
interface HookData {
|
|
41
|
+
ariaLabel: string,
|
|
40
42
|
ariaLabelledBy: string,
|
|
41
43
|
ariaDescribedBy: string,
|
|
42
44
|
focusManager: FocusManager
|
|
43
45
|
}
|
|
44
46
|
|
|
45
|
-
export const hookData = new WeakMap<
|
|
47
|
+
export const hookData = new WeakMap<DateFieldState, HookData>();
|
|
48
|
+
|
|
49
|
+
// Private props that we pass from useDatePicker/useDateRangePicker.
|
|
50
|
+
// Ideally we'd use a Symbol for this, but React doesn't support them: https://github.com/facebook/react/issues/7552
|
|
51
|
+
export const roleSymbol = '__role_' + Date.now();
|
|
52
|
+
export const focusManagerSymbol = '__focusManager_' + Date.now();
|
|
46
53
|
|
|
47
54
|
/**
|
|
48
55
|
* Provides the behavior and accessibility implementation for a date field component.
|
|
49
56
|
* A date field allows users to enter and edit date and time values using a keyboard.
|
|
50
57
|
* Each part of a date value is displayed in an individually editable segment.
|
|
51
58
|
*/
|
|
52
|
-
export function useDateField<T extends DateValue>(props:
|
|
59
|
+
export function useDateField<T extends DateValue>(props: AriaDateFieldProps<T>, state: DateFieldState, ref: RefObject<HTMLElement>): DateFieldAria {
|
|
53
60
|
let {labelProps, fieldProps, descriptionProps, errorMessageProps} = useField({
|
|
54
61
|
...props,
|
|
55
62
|
labelElementType: 'span'
|
|
56
63
|
});
|
|
57
64
|
|
|
58
|
-
let groupProps = useDatePickerGroup(state, ref);
|
|
59
|
-
|
|
60
65
|
let {focusWithinProps} = useFocusWithin({
|
|
61
66
|
onBlurWithin() {
|
|
62
67
|
state.confirmPlaceholder();
|
|
63
68
|
}
|
|
64
69
|
});
|
|
65
70
|
|
|
66
|
-
let
|
|
67
|
-
let
|
|
71
|
+
let formatMessage = useMessageFormatter(intlMessages);
|
|
72
|
+
let message = state.maxGranularity === 'hour' ? 'selectedTimeDescription' : 'selectedDateDescription';
|
|
73
|
+
let field = state.maxGranularity === 'hour' ? 'time' : 'date';
|
|
74
|
+
let description = state.value ? formatMessage(message, {[field]: state.formatValue({month: 'long'})}) : '';
|
|
75
|
+
let descProps = useDescription(description);
|
|
68
76
|
|
|
69
|
-
|
|
70
|
-
|
|
77
|
+
// If within a date picker or date range picker, the date field will have role="presentation" and an aria-describedby
|
|
78
|
+
// will be passed in that references the value (e.g. entire range). Otherwise, add the field's value description.
|
|
79
|
+
let describedBy = props[roleSymbol] === 'presentation'
|
|
80
|
+
? fieldProps['aria-describedby']
|
|
81
|
+
: [descProps['aria-describedby'], fieldProps['aria-describedby']].filter(Boolean).join(' ') || undefined;
|
|
71
82
|
let propsFocusManager = props[focusManagerSymbol];
|
|
72
83
|
let focusManager = useMemo(() => propsFocusManager || createFocusManager(ref), [propsFocusManager, ref]);
|
|
84
|
+
let groupProps = useDatePickerGroup(state, ref, props[roleSymbol] === 'presentation');
|
|
73
85
|
|
|
86
|
+
// Pass labels and other information to segments.
|
|
74
87
|
hookData.set(state, {
|
|
75
|
-
|
|
88
|
+
ariaLabel: props['aria-label'],
|
|
89
|
+
ariaLabelledBy: [props['aria-labelledby'], labelProps.id].filter(Boolean).join(' ') || undefined,
|
|
76
90
|
ariaDescribedBy: describedBy,
|
|
77
91
|
focusManager
|
|
78
92
|
});
|
|
79
93
|
|
|
80
94
|
let autoFocusRef = useRef(props.autoFocus);
|
|
81
95
|
|
|
96
|
+
// When used within a date picker or date range picker, the field gets role="presentation"
|
|
97
|
+
// rather than role="group". Since the date picker/date range picker already has a role="group"
|
|
98
|
+
// with a label and description, and the segments are already labeled by this as well, this
|
|
99
|
+
// avoids very verbose duplicate announcements.
|
|
100
|
+
let fieldDOMProps: HTMLAttributes<HTMLElement>;
|
|
101
|
+
if (props[roleSymbol] === 'presentation') {
|
|
102
|
+
fieldDOMProps = {
|
|
103
|
+
role: 'presentation'
|
|
104
|
+
};
|
|
105
|
+
} else {
|
|
106
|
+
fieldDOMProps = mergeProps(fieldProps, {
|
|
107
|
+
role: 'group',
|
|
108
|
+
'aria-disabled': props.isDisabled || undefined,
|
|
109
|
+
'aria-describedby': describedBy
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
|
|
82
113
|
useEffect(() => {
|
|
83
114
|
if (autoFocusRef.current) {
|
|
84
115
|
focusManager.focusFirst();
|
|
@@ -86,6 +117,7 @@ export function useDateField<T extends DateValue>(props: DateFieldProps<T>, stat
|
|
|
86
117
|
autoFocusRef.current = false;
|
|
87
118
|
}, [focusManager]);
|
|
88
119
|
|
|
120
|
+
let domProps = filterDOMProps(props);
|
|
89
121
|
return {
|
|
90
122
|
labelProps: {
|
|
91
123
|
...labelProps,
|
|
@@ -93,11 +125,7 @@ export function useDateField<T extends DateValue>(props: DateFieldProps<T>, stat
|
|
|
93
125
|
focusManager.focusFirst();
|
|
94
126
|
}
|
|
95
127
|
},
|
|
96
|
-
fieldProps: mergeProps(
|
|
97
|
-
role: 'group',
|
|
98
|
-
'aria-disabled': props.isDisabled || undefined,
|
|
99
|
-
'aria-describedby': describedBy
|
|
100
|
-
}),
|
|
128
|
+
fieldProps: mergeProps(domProps, fieldDOMProps, groupProps, focusWithinProps),
|
|
101
129
|
descriptionProps,
|
|
102
130
|
errorMessageProps
|
|
103
131
|
};
|
|
@@ -108,6 +136,6 @@ export function useDateField<T extends DateValue>(props: DateFieldProps<T>, stat
|
|
|
108
136
|
* A time field allows users to enter and edit time values using a keyboard.
|
|
109
137
|
* Each part of a time value is displayed in an individually editable segment.
|
|
110
138
|
*/
|
|
111
|
-
export function useTimeField<T extends TimeValue>(props: AriaTimeFieldProps<T>, state:
|
|
139
|
+
export function useTimeField<T extends TimeValue>(props: AriaTimeFieldProps<T>, state: DateFieldState, ref: RefObject<HTMLElement>): DateFieldAria {
|
|
112
140
|
return useDateField(props, state, ref);
|
|
113
141
|
}
|
package/src/useDatePicker.ts
CHANGED
|
@@ -16,15 +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 {
|
|
19
|
+
import {filterDOMProps, mergeProps, useDescription, useId} from '@react-aria/utils';
|
|
20
|
+
import {HTMLAttributes, RefObject, useMemo} from 'react';
|
|
20
21
|
// @ts-ignore
|
|
21
22
|
import intlMessages from '../intl/*.json';
|
|
22
|
-
import {
|
|
23
|
+
import {roleSymbol} from './useDateField';
|
|
23
24
|
import {useDatePickerGroup} from './useDatePickerGroup';
|
|
24
25
|
import {useField} from '@react-aria/label';
|
|
25
26
|
import {useLocale, useMessageFormatter} from '@react-aria/i18n';
|
|
26
27
|
|
|
27
|
-
interface DatePickerAria {
|
|
28
|
+
export interface DatePickerAria {
|
|
28
29
|
/** Props for the date picker's visible label element, if any. */
|
|
29
30
|
labelProps: HTMLAttributes<HTMLElement>,
|
|
30
31
|
/** Props for the grouping element containing the date field and button. */
|
|
@@ -62,11 +63,15 @@ export function useDatePicker<T extends DateValue>(props: AriaDatePickerProps<T>
|
|
|
62
63
|
let labelledBy = fieldProps['aria-labelledby'] || fieldProps.id;
|
|
63
64
|
|
|
64
65
|
let {locale} = useLocale();
|
|
65
|
-
let
|
|
66
|
+
let date = state.formatValue(locale, {month: 'long'});
|
|
67
|
+
let description = date ? formatMessage('selectedDateDescription', {date}) : '';
|
|
68
|
+
let descProps = useDescription(description);
|
|
66
69
|
let ariaDescribedBy = [descProps['aria-describedby'], fieldProps['aria-describedby']].filter(Boolean).join(' ') || undefined;
|
|
70
|
+
let domProps = filterDOMProps(props);
|
|
71
|
+
let focusManager = useMemo(() => createFocusManager(ref), [ref]);
|
|
67
72
|
|
|
68
73
|
return {
|
|
69
|
-
groupProps: mergeProps(groupProps, descProps, {
|
|
74
|
+
groupProps: mergeProps(domProps, groupProps, fieldProps, descProps, {
|
|
70
75
|
role: 'group',
|
|
71
76
|
'aria-disabled': props.isDisabled || null,
|
|
72
77
|
'aria-labelledby': labelledBy,
|
|
@@ -75,12 +80,13 @@ export function useDatePicker<T extends DateValue>(props: AriaDatePickerProps<T>
|
|
|
75
80
|
labelProps: {
|
|
76
81
|
...labelProps,
|
|
77
82
|
onClick: () => {
|
|
78
|
-
let focusManager = createFocusManager(ref);
|
|
79
83
|
focusManager.focusFirst();
|
|
80
84
|
}
|
|
81
85
|
},
|
|
82
86
|
fieldProps: {
|
|
83
87
|
...fieldProps,
|
|
88
|
+
[roleSymbol]: 'presentation',
|
|
89
|
+
'aria-describedby': ariaDescribedBy,
|
|
84
90
|
value: state.value,
|
|
85
91
|
onChange: state.setValue,
|
|
86
92
|
minValue: props.minValue,
|
|
@@ -100,7 +106,6 @@ export function useDatePicker<T extends DateValue>(props: AriaDatePickerProps<T>
|
|
|
100
106
|
buttonProps: {
|
|
101
107
|
...descProps,
|
|
102
108
|
id: buttonId,
|
|
103
|
-
excludeFromTabOrder: true,
|
|
104
109
|
'aria-haspopup': 'dialog',
|
|
105
110
|
'aria-label': formatMessage('calendar'),
|
|
106
111
|
'aria-labelledby': `${labelledBy} ${buttonId}`,
|
|
@@ -120,7 +125,9 @@ export function useDatePicker<T extends DateValue>(props: AriaDatePickerProps<T>
|
|
|
120
125
|
isDisabled: props.isDisabled,
|
|
121
126
|
isReadOnly: props.isReadOnly,
|
|
122
127
|
isDateUnavailable: props.isDateUnavailable,
|
|
123
|
-
defaultFocusedValue: state.dateValue ? undefined : props.placeholderValue
|
|
128
|
+
defaultFocusedValue: state.dateValue ? undefined : props.placeholderValue,
|
|
129
|
+
validationState: state.validationState,
|
|
130
|
+
errorMessage: props.errorMessage
|
|
124
131
|
}
|
|
125
132
|
};
|
|
126
133
|
}
|
|
@@ -1,18 +1,47 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import {createFocusManager, getFocusableTreeWalker} from '@react-aria/focus';
|
|
2
|
+
import {DateFieldState, DatePickerState, DateRangePickerState} from '@react-stately/datepicker';
|
|
3
3
|
import {KeyboardEvent} from '@react-types/shared';
|
|
4
4
|
import {mergeProps} from '@react-aria/utils';
|
|
5
|
-
import {RefObject} from 'react';
|
|
5
|
+
import {RefObject, useMemo} from 'react';
|
|
6
|
+
import {useLocale} from '@react-aria/i18n';
|
|
6
7
|
import {usePress} from '@react-aria/interactions';
|
|
7
8
|
|
|
8
|
-
export function useDatePickerGroup(state: DatePickerState | DateRangePickerState |
|
|
9
|
+
export function useDatePickerGroup(state: DatePickerState | DateRangePickerState | DateFieldState, ref: RefObject<HTMLElement>, disableArrowNavigation?: boolean) {
|
|
10
|
+
let {direction} = useLocale();
|
|
11
|
+
let focusManager = useMemo(() => createFocusManager(ref), [ref]);
|
|
12
|
+
|
|
9
13
|
// Open the popover on alt + arrow down
|
|
10
14
|
let onKeyDown = (e: KeyboardEvent) => {
|
|
11
|
-
if (e.altKey && e.key === 'ArrowDown' && 'setOpen' in state) {
|
|
15
|
+
if (e.altKey && (e.key === 'ArrowDown' || e.key === 'ArrowUp') && 'setOpen' in state) {
|
|
12
16
|
e.preventDefault();
|
|
13
17
|
e.stopPropagation();
|
|
14
18
|
state.setOpen(true);
|
|
15
19
|
}
|
|
20
|
+
|
|
21
|
+
if (disableArrowNavigation) {
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
switch (e.key) {
|
|
26
|
+
case 'ArrowLeft':
|
|
27
|
+
e.preventDefault();
|
|
28
|
+
e.stopPropagation();
|
|
29
|
+
if (direction === 'rtl') {
|
|
30
|
+
focusManager.focusNext();
|
|
31
|
+
} else {
|
|
32
|
+
focusManager.focusPrevious();
|
|
33
|
+
}
|
|
34
|
+
break;
|
|
35
|
+
case 'ArrowRight':
|
|
36
|
+
e.preventDefault();
|
|
37
|
+
e.stopPropagation();
|
|
38
|
+
if (direction === 'rtl') {
|
|
39
|
+
focusManager.focusPrevious();
|
|
40
|
+
} else {
|
|
41
|
+
focusManager.focusNext();
|
|
42
|
+
}
|
|
43
|
+
break;
|
|
44
|
+
}
|
|
16
45
|
};
|
|
17
46
|
|
|
18
47
|
// Focus the first placeholder segment from the end on mouse down/touch up in the field.
|
|
@@ -15,17 +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';
|
|
19
|
+
import {focusManagerSymbol, roleSymbol} from './useDateField';
|
|
18
20
|
import {HTMLAttributes, RefObject, useMemo} from 'react';
|
|
19
21
|
// @ts-ignore
|
|
20
22
|
import intlMessages from '../intl/*.json';
|
|
21
|
-
import {mergeProps, useDescription, useId, useLabels} from '@react-aria/utils';
|
|
22
23
|
import {RangeCalendarProps} from '@react-types/calendar';
|
|
23
24
|
import {useDatePickerGroup} from './useDatePickerGroup';
|
|
24
25
|
import {useField} from '@react-aria/label';
|
|
25
26
|
import {useFocusWithin} from '@react-aria/interactions';
|
|
26
27
|
import {useLocale, useMessageFormatter} from '@react-aria/i18n';
|
|
27
28
|
|
|
28
|
-
interface DateRangePickerAria {
|
|
29
|
+
export interface DateRangePickerAria {
|
|
29
30
|
/** Props for the date range picker's visible label element, if any. */
|
|
30
31
|
labelProps: HTMLAttributes<HTMLElement>,
|
|
31
32
|
/** Props for the grouping element containing the date fields and button. */
|
|
@@ -46,10 +47,6 @@ interface DateRangePickerAria {
|
|
|
46
47
|
calendarProps: RangeCalendarProps<DateValue>
|
|
47
48
|
}
|
|
48
49
|
|
|
49
|
-
// Used to pass the focus manager to the date fields.
|
|
50
|
-
// Ideally we'd use a Symbol for this, but React doesn't support them: https://github.com/facebook/react/issues/7552
|
|
51
|
-
export const focusManagerSymbol = '__focusManager_' + Date.now();
|
|
52
|
-
|
|
53
50
|
/**
|
|
54
51
|
* Provides the behavior and accessibility implementation for a date picker component.
|
|
55
52
|
* A date range picker combines two DateFields and a RangeCalendar popover to allow
|
|
@@ -65,18 +62,19 @@ export function useDateRangePicker<T extends DateValue>(props: AriaDateRangePick
|
|
|
65
62
|
let labelledBy = fieldProps['aria-labelledby'] || fieldProps.id;
|
|
66
63
|
|
|
67
64
|
let {locale} = useLocale();
|
|
68
|
-
let
|
|
65
|
+
let range = state.formatValue(locale, {month: 'long'});
|
|
66
|
+
let description = range ? formatMessage('selectedRangeDescription', {startDate: range.start, endDate: range.end}) : '';
|
|
69
67
|
let descProps = useDescription(description);
|
|
70
68
|
|
|
71
|
-
let startFieldProps =
|
|
69
|
+
let startFieldProps = {
|
|
72
70
|
'aria-label': formatMessage('startDate'),
|
|
73
71
|
'aria-labelledby': labelledBy
|
|
74
|
-
}
|
|
72
|
+
};
|
|
75
73
|
|
|
76
|
-
let endFieldProps =
|
|
74
|
+
let endFieldProps = {
|
|
77
75
|
'aria-label': formatMessage('endDate'),
|
|
78
76
|
'aria-labelledby': labelledBy
|
|
79
|
-
}
|
|
77
|
+
};
|
|
80
78
|
|
|
81
79
|
let buttonId = useId();
|
|
82
80
|
let dialogId = useId();
|
|
@@ -89,9 +87,15 @@ export function useDateRangePicker<T extends DateValue>(props: AriaDateRangePick
|
|
|
89
87
|
});
|
|
90
88
|
|
|
91
89
|
let ariaDescribedBy = [descProps['aria-describedby'], fieldProps['aria-describedby']].filter(Boolean).join(' ') || undefined;
|
|
92
|
-
let focusManager = useMemo(() => createFocusManager(ref
|
|
90
|
+
let focusManager = useMemo(() => createFocusManager(ref, {
|
|
91
|
+
// Exclude the button from the focus manager.
|
|
92
|
+
accept: element => element.id !== buttonId
|
|
93
|
+
}), [ref, buttonId]);
|
|
94
|
+
|
|
93
95
|
let commonFieldProps = {
|
|
94
96
|
[focusManagerSymbol]: focusManager,
|
|
97
|
+
[roleSymbol]: 'presentation',
|
|
98
|
+
'aria-describedby': ariaDescribedBy,
|
|
95
99
|
minValue: props.minValue,
|
|
96
100
|
maxValue: props.maxValue,
|
|
97
101
|
placeholderValue: props.placeholderValue,
|
|
@@ -104,8 +108,10 @@ export function useDateRangePicker<T extends DateValue>(props: AriaDateRangePick
|
|
|
104
108
|
validationState: state.validationState
|
|
105
109
|
};
|
|
106
110
|
|
|
111
|
+
let domProps = filterDOMProps(props);
|
|
112
|
+
|
|
107
113
|
return {
|
|
108
|
-
groupProps: mergeProps(groupProps, fieldProps, descProps, focusWithinProps, {
|
|
114
|
+
groupProps: mergeProps(domProps, groupProps, fieldProps, descProps, focusWithinProps, {
|
|
109
115
|
role: 'group',
|
|
110
116
|
'aria-disabled': props.isDisabled || null,
|
|
111
117
|
'aria-describedby': ariaDescribedBy
|
|
@@ -119,7 +125,6 @@ export function useDateRangePicker<T extends DateValue>(props: AriaDateRangePick
|
|
|
119
125
|
buttonProps: {
|
|
120
126
|
...descProps,
|
|
121
127
|
id: buttonId,
|
|
122
|
-
excludeFromTabOrder: true,
|
|
123
128
|
'aria-haspopup': 'dialog',
|
|
124
129
|
'aria-label': formatMessage('calendar'),
|
|
125
130
|
'aria-labelledby': `${labelledBy} ${buttonId}`,
|
|
@@ -133,7 +138,6 @@ export function useDateRangePicker<T extends DateValue>(props: AriaDateRangePick
|
|
|
133
138
|
startFieldProps: {
|
|
134
139
|
...startFieldProps,
|
|
135
140
|
...commonFieldProps,
|
|
136
|
-
'aria-describedby': fieldProps['aria-describedby'],
|
|
137
141
|
value: state.value?.start,
|
|
138
142
|
onChange: start => state.setDateTime('start', start),
|
|
139
143
|
autoFocus: props.autoFocus
|
|
@@ -141,7 +145,6 @@ export function useDateRangePicker<T extends DateValue>(props: AriaDateRangePick
|
|
|
141
145
|
endFieldProps: {
|
|
142
146
|
...endFieldProps,
|
|
143
147
|
...commonFieldProps,
|
|
144
|
-
'aria-describedby': fieldProps['aria-describedby'],
|
|
145
148
|
value: state.value?.end,
|
|
146
149
|
onChange: end => state.setDateTime('end', end)
|
|
147
150
|
},
|
|
@@ -157,7 +160,9 @@ export function useDateRangePicker<T extends DateValue>(props: AriaDateRangePick
|
|
|
157
160
|
isReadOnly: props.isReadOnly,
|
|
158
161
|
isDateUnavailable: props.isDateUnavailable,
|
|
159
162
|
allowsNonContiguousRanges: props.allowsNonContiguousRanges,
|
|
160
|
-
defaultFocusedValue: state.dateRange ? undefined : props.placeholderValue
|
|
163
|
+
defaultFocusedValue: state.dateRange ? undefined : props.placeholderValue,
|
|
164
|
+
validationState: state.validationState,
|
|
165
|
+
errorMessage: props.errorMessage
|
|
161
166
|
}
|
|
162
167
|
};
|
|
163
168
|
}
|