@octanejs/aria 0.0.46 → 0.0.47
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 +7 -6
- package/UPSTREAM.md +58 -69
- package/package.json +11 -11
- package/src/calendar/useCalendarGrid.tsx +64 -62
- package/src/collections/Document.ts +3 -0
- package/src/color/useColorArea.tsx +95 -69
- package/src/color/useColorWheel.tsx +38 -37
- package/src/combobox/{useComboBox.ts → useComboBox.tsx} +239 -259
- package/src/components/ComboBox.ts +35 -2
- package/src/components/Dialog.ts +12 -1
- package/src/components/DropZone.tsx +3 -2
- package/src/components/FileTrigger.tsx +1 -0
- package/src/components/GridList.ts +11 -2
- package/src/components/ListBox.ts +1 -1
- package/src/components/Menu.ts +1 -0
- package/src/components/Meter.ts +3 -1
- package/src/components/Popover.ts +20 -8
- package/src/components/PreviewTrigger.tsx +101 -0
- package/src/components/ProgressBar.ts +10 -2
- package/src/components/Select.ts +1 -1
- package/src/components/Table.ts +26 -15
- package/src/components/TokenField.tsx +395 -0
- package/src/components/Tooltip.ts +4 -6
- package/src/components/Tree.ts +14 -7
- package/src/components/Virtualizer.tsx +30 -14
- package/src/components/index.ts +22 -0
- package/src/components/utils.ts +1 -1
- package/src/datepicker/useDateField.tsx +2 -11
- package/src/datepicker/useDatePickerGroup.tsx +44 -33
- package/src/datepicker/useDateSegment.tsx +31 -30
- package/src/dialog/useDialog.ts +8 -1
- package/src/dnd/DragManager.tsx +5 -1
- package/src/dnd/useDrag.tsx +2 -1
- package/src/focus/FocusScope.ts +2 -2
- package/src/grid/useGrid.ts +3 -0
- package/src/grid/{useGridCell.ts → useGridCell.tsx} +121 -64
- package/src/grid/utils.ts +1 -0
- package/src/gridlist/{useGridListItem.ts → useGridListItem.tsx} +152 -111
- package/src/index.ts +22 -0
- package/src/interactions/createEventHandler.ts +11 -8
- package/src/interactions/createKeyboardShortcutHandler.tsx +218 -0
- package/src/interactions/textSelection.ts +3 -3
- package/src/interactions/useContextMenu.tsx +125 -0
- package/src/interactions/useFocusVisible.ts +4 -4
- package/src/interactions/useKeyboard.ts +50 -2
- package/src/interactions/useLongPress.ts +33 -22
- package/src/interactions/useMove.ts +6 -6
- package/src/interactions/usePress.ts +8 -0
- package/src/interactions/utils.ts +10 -10
- package/src/intl/combobox/fi-FI.json +2 -2
- package/src/intl/previewtrigger/ar-AE.json +3 -0
- package/src/intl/previewtrigger/bg-BG.json +3 -0
- package/src/intl/previewtrigger/cs-CZ.json +3 -0
- package/src/intl/previewtrigger/da-DK.json +3 -0
- package/src/intl/previewtrigger/de-DE.json +3 -0
- package/src/intl/previewtrigger/el-GR.json +3 -0
- package/src/intl/previewtrigger/en-US.json +3 -0
- package/src/intl/previewtrigger/es-ES.json +3 -0
- package/src/intl/previewtrigger/et-EE.json +3 -0
- package/src/intl/previewtrigger/fi-FI.json +3 -0
- package/src/intl/previewtrigger/fr-FR.json +3 -0
- package/src/intl/previewtrigger/he-IL.json +3 -0
- package/src/intl/previewtrigger/hr-HR.json +3 -0
- package/src/intl/previewtrigger/hu-HU.json +3 -0
- package/src/intl/previewtrigger/index.ts +73 -0
- package/src/intl/previewtrigger/it-IT.json +3 -0
- package/src/intl/previewtrigger/ja-JP.json +3 -0
- package/src/intl/previewtrigger/ko-KR.json +3 -0
- package/src/intl/previewtrigger/lt-LT.json +3 -0
- package/src/intl/previewtrigger/lv-LV.json +3 -0
- package/src/intl/previewtrigger/nb-NO.json +3 -0
- package/src/intl/previewtrigger/nl-NL.json +3 -0
- package/src/intl/previewtrigger/pl-PL.json +3 -0
- package/src/intl/previewtrigger/pt-BR.json +3 -0
- package/src/intl/previewtrigger/pt-PT.json +3 -0
- package/src/intl/previewtrigger/ro-RO.json +3 -0
- package/src/intl/previewtrigger/ru-RU.json +3 -0
- package/src/intl/previewtrigger/sk-SK.json +3 -0
- package/src/intl/previewtrigger/sl-SI.json +3 -0
- package/src/intl/previewtrigger/sr-SP.json +3 -0
- package/src/intl/previewtrigger/sv-SE.json +3 -0
- package/src/intl/previewtrigger/tr-TR.json +3 -0
- package/src/intl/previewtrigger/uk-UA.json +3 -0
- package/src/intl/previewtrigger/zh-CN.json +3 -0
- package/src/intl/previewtrigger/zh-TW.json +3 -0
- package/src/intl/table/ar-AE.json +3 -3
- package/src/intl/table/bg-BG.json +3 -3
- package/src/intl/table/cs-CZ.json +3 -3
- package/src/intl/table/da-DK.json +3 -3
- package/src/intl/table/de-DE.json +3 -3
- package/src/intl/table/el-GR.json +3 -3
- package/src/intl/table/es-ES.json +3 -3
- package/src/intl/table/et-EE.json +3 -3
- package/src/intl/table/fi-FI.json +3 -3
- package/src/intl/table/fr-FR.json +3 -3
- package/src/intl/table/he-IL.json +3 -3
- package/src/intl/table/hr-HR.json +3 -3
- package/src/intl/table/hu-HU.json +3 -3
- package/src/intl/table/it-IT.json +3 -3
- package/src/intl/table/ja-JP.json +3 -3
- package/src/intl/table/ko-KR.json +3 -3
- package/src/intl/table/lt-LT.json +3 -3
- package/src/intl/table/lv-LV.json +3 -3
- package/src/intl/table/nb-NO.json +3 -3
- package/src/intl/table/nl-NL.json +3 -3
- package/src/intl/table/pl-PL.json +3 -3
- package/src/intl/table/pt-BR.json +3 -3
- package/src/intl/table/pt-PT.json +3 -3
- package/src/intl/table/ro-RO.json +3 -3
- package/src/intl/table/ru-RU.json +3 -3
- package/src/intl/table/sk-SK.json +3 -3
- package/src/intl/table/sl-SI.json +3 -3
- package/src/intl/table/sr-SP.json +3 -3
- package/src/intl/table/sv-SE.json +3 -3
- package/src/intl/table/tr-TR.json +3 -3
- package/src/intl/table/uk-UA.json +3 -3
- package/src/intl/table/zh-CN.json +4 -4
- package/src/intl/table/zh-TW.json +3 -3
- package/src/intl/tree/es-ES.json +1 -1
- package/src/link/useLink.ts +2 -0
- package/src/menu/useMenuItem.ts +33 -36
- package/src/menu/useMenuTrigger.tsx +217 -0
- package/src/menu/{useSubmenuTrigger.ts → useSubmenuTrigger.tsx} +112 -152
- package/src/numberfield/{useNumberField.ts → useNumberField.tsx} +185 -239
- package/src/overlays/Overlay.ts +5 -1
- package/src/overlays/ariaHideOutside.ts +6 -8
- package/src/overlays/useCloseOnScroll.ts +7 -5
- package/src/overlays/{useOverlay.ts → useOverlay.tsx} +72 -84
- package/src/overlays/usePopover.ts +10 -5
- package/src/overlays/usePreventScroll.ts +4 -4
- package/src/progress/useProgressBar.ts +2 -1
- package/src/radio/{useRadioGroup.ts → useRadioGroup.tsx} +83 -80
- package/src/searchfield/useSearchField.tsx +147 -0
- package/src/select/useSelect.ts +20 -20
- package/src/selection/useSelectableCollection.tsx +751 -0
- package/src/selection/useSelectableItem.ts +48 -11
- package/src/slider/{useSliderThumb.ts → useSliderThumb.tsx} +116 -159
- package/src/spinbutton/useSpinButton.tsx +330 -0
- package/src/stately/combobox/useComboBoxState.ts +1 -1
- package/src/stately/grid/useGridState.ts +9 -10
- package/src/stately/index.ts +11 -0
- package/src/stately/menu/useMenuTriggerState.ts +1 -1
- package/src/stately/menu/useSubmenuTriggerState.ts +2 -0
- package/src/stately/overlays/useOverlayTriggerState.ts +12 -1
- package/src/stately/radio/useRadioGroupState.ts +5 -1
- package/src/stately/table/TableUtils.ts +15 -1
- package/src/stately/table/useTableState.ts +7 -0
- package/src/stately/tokenfield/TokenFieldValue.tsx +440 -0
- package/src/stately/tokenfield/useTokenFieldState.tsx +57 -0
- package/src/stately/tooltip/useTooltipTriggerState.ts +16 -7
- package/src/stately/virtualizer/Layout.tsx +2 -0
- package/src/stately/virtualizer/ScrollAnchor.tsx +338 -0
- package/src/stately/virtualizer/Virtualizer.tsx +52 -1
- package/src/stately/virtualizer/types.tsx +2 -0
- package/src/table/useTableCell.ts +12 -0
- package/src/table/useTableColumnHeader.ts +8 -1
- package/src/table/useTableColumnResize.tsx +354 -0
- package/src/tag/useTag.ts +22 -15
- package/src/textfield/useTextField.ts +2 -0
- package/src/tokenfield/useToken.tsx +64 -0
- package/src/tokenfield/useTokenField.tsx +849 -0
- package/src/tooltip/usePreviewTrigger.tsx +266 -0
- package/src/tooltip/useSafeArea.tsx +171 -0
- package/src/upstream-exports/react-aria/mergeRefs.tsx +3 -0
- package/src/upstream-exports/react-aria/usePreviewTrigger.tsx +18 -0
- package/src/upstream-exports/react-aria/useTokenField.tsx +22 -0
- package/src/upstream-exports/react-stately/private/collections/getChildNodes.tsx +8 -0
- package/src/upstream-exports/react-stately/private/collections/getItemCount.tsx +2 -0
- package/src/upstream-exports/react-stately/private/grid/GridCollection.tsx +7 -0
- package/src/upstream-exports/react-stately/private/grid/useGridState.tsx +6 -0
- package/src/upstream-exports/react-stately/private/utils/number.tsx +2 -0
- package/src/upstream-exports/react-stately/useComboBoxState.tsx +23 -0
- package/src/upstream-exports/react-stately/useListState.tsx +15 -0
- package/src/upstream-exports/react-stately/useMenuTriggerState.tsx +26 -0
- package/src/upstream-exports/react-stately/useMultipleSelectionState.tsx +21 -0
- package/src/upstream-exports/react-stately/useNumberFieldState.tsx +20 -0
- package/src/upstream-exports/react-stately/useOverlayTriggerState.tsx +20 -0
- package/src/upstream-exports/react-stately/useRadioGroupState.tsx +16 -0
- package/src/upstream-exports/react-stately/useSearchFieldState.tsx +19 -0
- package/src/upstream-exports/react-stately/useSliderState.tsx +20 -0
- package/src/upstream-exports/react-stately/useTableState.tsx +45 -0
- package/src/upstream-exports/react-stately/useTokenFieldState.tsx +25 -0
- package/src/upstream-exports/react-stately/useTooltipTriggerState.tsx +20 -0
- package/src/upstream-exports/react-stately/useTreeState.tsx +14 -0
- package/src/utils/domHelpers.ts +74 -17
- package/src/utils/isVirtualEvent.ts +2 -1
- package/src/utils/openLink.ts +8 -3
- package/src/utils/platform.ts +7 -3
- package/src/utils/scrollIntoView.ts +4 -4
- package/src/utils/shadowdom/DOMFunctions.ts +42 -2
- package/src/utils/useId.ts +11 -2
- package/src/utils/useLoadMoreSentinel.ts +8 -3
- package/src/utils/useViewportSize.ts +3 -3
- package/src/virtualizer/ScrollView.tsx +13 -4
- package/src/virtualizer/VirtualizerItem.tsx +3 -1
- package/src/virtualizer/useVirtualizerItem.tsx +43 -3
- package/src/menu/useMenuTrigger.ts +0 -189
- package/src/searchfield/useSearchField.ts +0 -156
- package/src/selection/useSelectableCollection.ts +0 -750
- package/src/spinbutton/useSpinButton.ts +0 -380
- package/src/table/useTableColumnResize.ts +0 -374
|
@@ -1,71 +1,69 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
//
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
1
|
+
/** @jsxImportSource octane */
|
|
2
|
+
interface TextInputDOMEvents {
|
|
3
|
+
onCopy?: (e: ClipboardEvent) => void;
|
|
4
|
+
onCut?: (e: ClipboardEvent) => void;
|
|
5
|
+
onPaste?: (e: ClipboardEvent) => void;
|
|
6
|
+
onCompositionStart?: (e: CompositionEvent) => void;
|
|
7
|
+
onCompositionEnd?: (e: CompositionEvent) => void;
|
|
8
|
+
onCompositionUpdate?: (e: CompositionEvent) => void;
|
|
9
|
+
onSelect?: (e: Event) => void;
|
|
10
|
+
onBeforeInput?: (e: InputEvent) => void;
|
|
11
|
+
onInput?: (e: Event) => void;
|
|
12
|
+
}
|
|
13
|
+
type DOMAttributes = Record<string, any>;
|
|
14
|
+
// Ported from adobe/react-spectrum@5ecb3333001313e83898cd07644227897e3bae1f (packages/react-aria/src/numberfield/useNumberField.ts).
|
|
15
|
+
/*
|
|
16
|
+
* Copyright 2020 Adobe. All rights reserved.
|
|
17
|
+
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
|
|
18
|
+
* you may not use this file except in compliance with the License. You may obtain a copy
|
|
19
|
+
* of the License at http://www.apache.org/licenses/LICENSE-2.0
|
|
20
|
+
*
|
|
21
|
+
* Unless required by applicable law or agreed to in writing, software distributed under
|
|
22
|
+
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
|
|
23
|
+
* OF ANY KIND, either express or implied. See the License for the specific language
|
|
24
|
+
* governing permissions and limitations under the License.
|
|
25
|
+
*/
|
|
26
|
+
|
|
20
27
|
import { announce } from '../live-announcer/LiveAnnouncer';
|
|
21
|
-
import
|
|
22
|
-
import
|
|
23
|
-
|
|
24
|
-
|
|
28
|
+
import { AriaButtonProps } from '../button/useButton';
|
|
29
|
+
import {
|
|
30
|
+
AriaLabelingProps,
|
|
31
|
+
DOMProps,
|
|
32
|
+
GroupDOMAttributes,
|
|
33
|
+
TextInputDOMProps,
|
|
34
|
+
ValidationResult,
|
|
35
|
+
} from '@react-types/shared';
|
|
36
|
+
import {
|
|
37
|
+
InputHTMLAttributes,
|
|
38
|
+
LabelHTMLAttributes,
|
|
39
|
+
RefObject,
|
|
40
|
+
useCallback,
|
|
41
|
+
useMemo,
|
|
42
|
+
useState,
|
|
43
|
+
} from '../compat/react';
|
|
25
44
|
import { filterDOMProps } from '../utils/filterDOMProps';
|
|
45
|
+
import { flushSync } from 'octane';
|
|
26
46
|
import { getActiveElement, getEventTarget } from '../utils/shadowdom/DOMFunctions';
|
|
27
|
-
import intlMessages from '../intl/numberfield';
|
|
47
|
+
import intlMessages from '../intl/numberfield/index';
|
|
28
48
|
import { isAndroid, isIOS, isIPhone } from '../utils/platform';
|
|
29
49
|
import { mergeProps } from '../utils/mergeProps';
|
|
30
|
-
import
|
|
50
|
+
import {
|
|
31
51
|
NumberFieldProps,
|
|
32
52
|
NumberFieldState,
|
|
33
|
-
} from '../stately/
|
|
34
|
-
import { privateValidationStateProp } from '../stately/form/useFormValidationState';
|
|
53
|
+
} from '../upstream-exports/react-stately/useNumberFieldState';
|
|
54
|
+
import { privateValidationStateProp } from '../upstream-exports/react-stately/private/form/useFormValidationState';
|
|
35
55
|
import { useFocus } from '../interactions/useFocus';
|
|
36
56
|
import { useFocusWithin } from '../interactions/useFocusWithin';
|
|
37
57
|
import { useFormattedTextField } from '../textfield/useFormattedTextField';
|
|
38
58
|
import { useFormReset } from '../utils/useFormReset';
|
|
39
59
|
import { useId } from '../utils/useId';
|
|
60
|
+
import { useKeyboard } from '../interactions/useKeyboard';
|
|
40
61
|
import { useLayoutEffect } from '../utils/useLayoutEffect';
|
|
41
62
|
import { useLocalizedStringFormatter } from '../i18n/useLocalizedStringFormatter';
|
|
42
63
|
import { useNumberFormatter } from '../i18n/useNumberFormatter';
|
|
43
64
|
import { useScrollWheel } from '../interactions/useScrollWheel';
|
|
44
65
|
import { useSpinButton } from '../spinbutton/useSpinButton';
|
|
45
66
|
|
|
46
|
-
import { S, splitSlot, subSlot } from '../internal';
|
|
47
|
-
|
|
48
|
-
// octane adaptation: minimal structural DOMAttributes (upstream's drags React's synthetic
|
|
49
|
-
// handler types along).
|
|
50
|
-
type DOMAttributes = Record<string, any>;
|
|
51
|
-
|
|
52
|
-
// octane adaptation: structural native-event versions of upstream's TextInputDOMEvents.
|
|
53
|
-
interface TextInputDOMEvents {
|
|
54
|
-
// Clipboard events
|
|
55
|
-
onCopy?: (e: ClipboardEvent) => void;
|
|
56
|
-
onCut?: (e: ClipboardEvent) => void;
|
|
57
|
-
onPaste?: (e: ClipboardEvent) => void;
|
|
58
|
-
// Composition events
|
|
59
|
-
onCompositionEnd?: (e: CompositionEvent) => void;
|
|
60
|
-
onCompositionStart?: (e: CompositionEvent) => void;
|
|
61
|
-
onCompositionUpdate?: (e: CompositionEvent) => void;
|
|
62
|
-
// Selection events
|
|
63
|
-
onSelect?: (e: Event) => void;
|
|
64
|
-
// Input events
|
|
65
|
-
onBeforeInput?: (e: InputEvent) => void;
|
|
66
|
-
onInput?: (e: Event) => void;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
67
|
export interface AriaNumberFieldProps
|
|
70
68
|
extends NumberFieldProps, DOMProps, AriaLabelingProps, TextInputDOMEvents {
|
|
71
69
|
/**
|
|
@@ -86,11 +84,11 @@ export interface AriaNumberFieldProps
|
|
|
86
84
|
|
|
87
85
|
export interface NumberFieldAria extends ValidationResult {
|
|
88
86
|
/** Props for the label element. */
|
|
89
|
-
labelProps:
|
|
87
|
+
labelProps: LabelHTMLAttributes<HTMLLabelElement>;
|
|
90
88
|
/** Props for the group wrapper around the input and stepper buttons. */
|
|
91
|
-
groupProps:
|
|
89
|
+
groupProps: GroupDOMAttributes;
|
|
92
90
|
/** Props for the input element. */
|
|
93
|
-
inputProps:
|
|
91
|
+
inputProps: InputHTMLAttributes<HTMLInputElement>;
|
|
94
92
|
/** Props for the increment button, to be passed to `useButton`. */
|
|
95
93
|
incrementButtonProps: AriaButtonProps;
|
|
96
94
|
/** Props for the decrement button, to be passed to `useButton`. */
|
|
@@ -110,21 +108,8 @@ export function useNumberField(
|
|
|
110
108
|
props: AriaNumberFieldProps,
|
|
111
109
|
state: NumberFieldState,
|
|
112
110
|
inputRef: RefObject<HTMLInputElement | null>,
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
export function useNumberField(
|
|
116
|
-
props: AriaNumberFieldProps,
|
|
117
|
-
state: NumberFieldState,
|
|
118
|
-
inputRef: RefObject<HTMLInputElement | null>,
|
|
119
|
-
slot: symbol | undefined,
|
|
120
|
-
): NumberFieldAria;
|
|
121
|
-
export function useNumberField(...args: any[]): NumberFieldAria {
|
|
122
|
-
const [user, slotArg] = splitSlot(args);
|
|
123
|
-
const slot = slotArg ?? S('useNumberField');
|
|
124
|
-
const props = user[0] as AriaNumberFieldProps;
|
|
125
|
-
const state = user[1] as NumberFieldState;
|
|
126
|
-
const inputRef = user[2] as RefObject<HTMLInputElement | null>;
|
|
127
|
-
|
|
111
|
+
_slot?: symbol,
|
|
112
|
+
): NumberFieldAria {
|
|
128
113
|
let {
|
|
129
114
|
id,
|
|
130
115
|
decrementAriaLabel,
|
|
@@ -159,85 +144,60 @@ export function useNumberField(...args: any[]): NumberFieldAria {
|
|
|
159
144
|
commitValidation,
|
|
160
145
|
} = state;
|
|
161
146
|
|
|
162
|
-
const stringFormatter = useLocalizedStringFormatter(
|
|
163
|
-
|
|
164
|
-
'
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
let oldValue = inputRef.current?.value ?? '';
|
|
170
|
-
// Set input value to normalized valid value
|
|
171
|
-
flushSync(() => {
|
|
172
|
-
commit();
|
|
173
|
-
});
|
|
174
|
-
|
|
175
|
-
if (inputRef.current?.value !== oldValue) {
|
|
176
|
-
announce(inputRef.current?.value ?? '', 'assertive');
|
|
177
|
-
}
|
|
178
|
-
},
|
|
179
|
-
[commit, inputRef],
|
|
180
|
-
subSlot(slot, 'commitAndAnnounce'),
|
|
181
|
-
);
|
|
147
|
+
const stringFormatter = useLocalizedStringFormatter(intlMessages, '@react-aria/numberfield');
|
|
148
|
+
let commitAndAnnounce = useCallback(() => {
|
|
149
|
+
let oldValue = inputRef.current?.value ?? '';
|
|
150
|
+
// Set input value to normalized valid value
|
|
151
|
+
flushSync(() => {
|
|
152
|
+
commit();
|
|
153
|
+
});
|
|
182
154
|
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
155
|
+
if (inputRef.current?.value !== oldValue) {
|
|
156
|
+
announce(inputRef.current?.value ?? '', 'assertive');
|
|
157
|
+
}
|
|
158
|
+
}, [commit, inputRef]);
|
|
159
|
+
|
|
160
|
+
let inputId = useId(id);
|
|
161
|
+
let { focusProps } = useFocus({
|
|
162
|
+
onBlur() {
|
|
163
|
+
commitAndAnnounce();
|
|
189
164
|
},
|
|
190
|
-
|
|
191
|
-
);
|
|
165
|
+
});
|
|
192
166
|
|
|
193
|
-
let numberFormatter = useNumberFormatter(formatOptions
|
|
194
|
-
let intlOptions = useMemo(
|
|
195
|
-
() => numberFormatter.resolvedOptions(),
|
|
196
|
-
[numberFormatter],
|
|
197
|
-
subSlot(slot, 'intlOptions'),
|
|
198
|
-
);
|
|
167
|
+
let numberFormatter = useNumberFormatter(formatOptions);
|
|
168
|
+
let intlOptions = useMemo(() => numberFormatter.resolvedOptions(), [numberFormatter]);
|
|
199
169
|
|
|
200
170
|
// Replace negative textValue formatted using currencySign: 'accounting'
|
|
201
171
|
// with a textValue that can be announced using a minus sign.
|
|
202
|
-
let textValueFormatter = useNumberFormatter(
|
|
203
|
-
{ ...formatOptions, currencySign: undefined },
|
|
204
|
-
subSlot(slot, 'textValueFormatter'),
|
|
205
|
-
);
|
|
172
|
+
let textValueFormatter = useNumberFormatter({ ...formatOptions, currencySign: undefined });
|
|
206
173
|
let textValue = useMemo(
|
|
207
174
|
() => (isNaN(numberValue) ? '' : textValueFormatter.format(numberValue)),
|
|
208
175
|
[textValueFormatter, numberValue],
|
|
209
|
-
subSlot(slot, 'textValue'),
|
|
210
176
|
);
|
|
211
177
|
|
|
212
178
|
let {
|
|
213
179
|
spinButtonProps,
|
|
214
180
|
incrementButtonProps: incButtonProps,
|
|
215
181
|
decrementButtonProps: decButtonProps,
|
|
216
|
-
} = useSpinButton(
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
},
|
|
230
|
-
subSlot(slot, 'spinButton'),
|
|
231
|
-
);
|
|
182
|
+
} = useSpinButton({
|
|
183
|
+
isDisabled,
|
|
184
|
+
isReadOnly,
|
|
185
|
+
isRequired,
|
|
186
|
+
maxValue,
|
|
187
|
+
minValue,
|
|
188
|
+
onIncrement: increment,
|
|
189
|
+
onIncrementToMax: incrementToMax,
|
|
190
|
+
onDecrement: decrement,
|
|
191
|
+
onDecrementToMin: decrementToMin,
|
|
192
|
+
value: numberValue,
|
|
193
|
+
textValue,
|
|
194
|
+
});
|
|
232
195
|
|
|
233
|
-
let [focusWithin, setFocusWithin] = useState(false
|
|
234
|
-
let { focusWithinProps } = useFocusWithin(
|
|
235
|
-
{ isDisabled, onFocusWithinChange: setFocusWithin },
|
|
236
|
-
subSlot(slot, 'focusWithinHook'),
|
|
237
|
-
);
|
|
196
|
+
let [focusWithin, setFocusWithin] = useState(false);
|
|
197
|
+
let { focusWithinProps } = useFocusWithin({ isDisabled, onFocusWithinChange: setFocusWithin });
|
|
238
198
|
|
|
239
199
|
let onWheel = useCallback(
|
|
240
|
-
(e
|
|
200
|
+
(e) => {
|
|
241
201
|
// if on a trackpad, users can scroll in both X and Y at once, check the magnitude of the change
|
|
242
202
|
// if it's mostly in the X direction, then just return, the user probably doesn't mean to inc/dec
|
|
243
203
|
// this isn't perfect, events come in fast with small deltas and a part of the scroll may give a false indication
|
|
@@ -252,15 +212,10 @@ export function useNumberField(...args: any[]): NumberFieldAria {
|
|
|
252
212
|
}
|
|
253
213
|
},
|
|
254
214
|
[decrement, increment],
|
|
255
|
-
subSlot(slot, 'wheel'),
|
|
256
215
|
);
|
|
257
216
|
// If the input isn't supposed to receive input, disable scrolling.
|
|
258
217
|
let scrollingDisabled = isWheelDisabled || isDisabled || isReadOnly || !focusWithin;
|
|
259
|
-
useScrollWheel(
|
|
260
|
-
{ onScroll: onWheel, isDisabled: scrollingDisabled },
|
|
261
|
-
inputRef,
|
|
262
|
-
subSlot(slot, 'scrollWheel'),
|
|
263
|
-
);
|
|
218
|
+
useScrollWheel({ onScroll: onWheel, isDisabled: scrollingDisabled }, inputRef);
|
|
264
219
|
|
|
265
220
|
// The inputMode attribute influences the software keyboard that is shown on touch devices.
|
|
266
221
|
// Browsers and operating systems are quite inconsistent about what keys are available, however.
|
|
@@ -268,7 +223,7 @@ export function useNumberField(...args: any[]): NumberFieldAria {
|
|
|
268
223
|
// and based on testing on various devices to determine what keys are available in each inputMode.
|
|
269
224
|
let hasDecimals = (intlOptions.maximumFractionDigits ?? 0) > 0;
|
|
270
225
|
let hasNegative = state.minValue === undefined || isNaN(state.minValue) || state.minValue < 0;
|
|
271
|
-
let inputMode:
|
|
226
|
+
let inputMode: TextInputDOMProps['inputMode'] = 'numeric';
|
|
272
227
|
if (isIPhone()) {
|
|
273
228
|
// iPhone doesn't have a minus sign in either numeric or decimal.
|
|
274
229
|
// Note this is only for iPhone, not iPad, which always has both
|
|
@@ -314,31 +269,25 @@ export function useNumberField(...args: any[]): NumberFieldAria {
|
|
|
314
269
|
};
|
|
315
270
|
|
|
316
271
|
let domProps = filterDOMProps(props);
|
|
317
|
-
let
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
if (e.isComposing) {
|
|
322
|
-
return;
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
if (e.key === 'Enter') {
|
|
272
|
+
let { keyboardProps } = useKeyboard({
|
|
273
|
+
isDisabled: isDisabled || isReadOnly,
|
|
274
|
+
shortcuts: {
|
|
275
|
+
Enter: () => {
|
|
326
276
|
flushSync(() => {
|
|
327
277
|
commit();
|
|
328
278
|
});
|
|
329
279
|
commitValidation();
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
}
|
|
280
|
+
return { shouldPreventDefault: false };
|
|
281
|
+
},
|
|
333
282
|
},
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
);
|
|
283
|
+
onKeyDown,
|
|
284
|
+
onKeyUp,
|
|
285
|
+
});
|
|
337
286
|
|
|
338
287
|
let { isInvalid, validationErrors, validationDetails } = state.displayValidation;
|
|
339
288
|
let {
|
|
340
289
|
labelProps,
|
|
341
|
-
inputProps:
|
|
290
|
+
inputProps: textFieldPropsFromHook,
|
|
342
291
|
descriptionProps,
|
|
343
292
|
errorMessageProps,
|
|
344
293
|
} = useFormattedTextField(
|
|
@@ -367,22 +316,20 @@ export function useNumberField(...args: any[]): NumberFieldAria {
|
|
|
367
316
|
onBlur,
|
|
368
317
|
onFocus,
|
|
369
318
|
onFocusChange,
|
|
370
|
-
onKeyDown: useMemo(
|
|
371
|
-
() => chain(onKeyDownEnter, onKeyDown),
|
|
372
|
-
[onKeyDownEnter, onKeyDown],
|
|
373
|
-
subSlot(slot, 'keyDownChain'),
|
|
374
|
-
),
|
|
375
|
-
onKeyUp,
|
|
376
319
|
onPaste,
|
|
377
320
|
description,
|
|
378
321
|
errorMessage,
|
|
379
|
-
}
|
|
322
|
+
},
|
|
380
323
|
state,
|
|
381
324
|
inputRef,
|
|
382
|
-
subSlot(slot, 'textField'),
|
|
383
325
|
);
|
|
384
326
|
|
|
385
|
-
|
|
327
|
+
// Merge outside useFormattedTextField so useKeyboard's createEventHandler is not nested inside
|
|
328
|
+
// useTextField/useFocusable's createEventHandler (avoids redundant stopPropagation on RS events).
|
|
329
|
+
// Shortcuts run first (mergeProps chains the second argument after the first).
|
|
330
|
+
let textFieldProps = mergeProps(keyboardProps, textFieldPropsFromHook);
|
|
331
|
+
|
|
332
|
+
useFormReset(inputRef, state.defaultNumberValue, state.setNumberValue);
|
|
386
333
|
useNativeValidation(
|
|
387
334
|
state,
|
|
388
335
|
props.validationBehavior,
|
|
@@ -392,27 +339,31 @@ export function useNumberField(...args: any[]): NumberFieldAria {
|
|
|
392
339
|
state.maxValue,
|
|
393
340
|
props.step,
|
|
394
341
|
state.numberValue,
|
|
395
|
-
subSlot(slot, 'nativeValidation'),
|
|
396
342
|
);
|
|
397
343
|
|
|
398
|
-
let inputProps:
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
344
|
+
let inputProps: InputHTMLAttributes<HTMLInputElement> = mergeProps(
|
|
345
|
+
spinButtonProps,
|
|
346
|
+
focusProps,
|
|
347
|
+
textFieldProps,
|
|
348
|
+
{
|
|
349
|
+
// override the spinbutton role, we can't focus a spin button with VO
|
|
350
|
+
role: null,
|
|
351
|
+
// ignore aria-roledescription on iOS so that required state will announce when it is present
|
|
352
|
+
'aria-roledescription': !isIOS() ? stringFormatter.format('numberField') : null,
|
|
353
|
+
'aria-valuemax': null,
|
|
354
|
+
'aria-valuemin': null,
|
|
355
|
+
'aria-valuenow': null,
|
|
356
|
+
'aria-valuetext': null,
|
|
357
|
+
autoCorrect: 'off',
|
|
358
|
+
spellCheck: 'false',
|
|
359
|
+
},
|
|
360
|
+
);
|
|
410
361
|
|
|
411
362
|
if (props.validationBehavior === 'native') {
|
|
412
363
|
inputProps['aria-required'] = undefined;
|
|
413
364
|
}
|
|
414
365
|
|
|
415
|
-
let onButtonPressStart = (e:
|
|
366
|
+
let onButtonPressStart = (e: import('@react-types/shared').PressEvent) => {
|
|
416
367
|
// If focus is already on the input, keep it there so we don't hide the
|
|
417
368
|
// software keyboard when tapping the increment/decrement buttons.
|
|
418
369
|
if (getActiveElement() === inputRef.current) {
|
|
@@ -425,7 +376,7 @@ export function useNumberField(...args: any[]): NumberFieldAria {
|
|
|
425
376
|
if (e.pointerType === 'mouse') {
|
|
426
377
|
inputRef.current?.focus();
|
|
427
378
|
} else {
|
|
428
|
-
e.target.focus();
|
|
379
|
+
(e.target as HTMLElement).focus();
|
|
429
380
|
}
|
|
430
381
|
};
|
|
431
382
|
|
|
@@ -445,9 +396,10 @@ export function useNumberField(...args: any[]): NumberFieldAria {
|
|
|
445
396
|
ariaLabelledby = props.label != null ? labelProps.id : props['aria-labelledby'];
|
|
446
397
|
}
|
|
447
398
|
|
|
448
|
-
let incrementId = useId(
|
|
449
|
-
let decrementId = useId(
|
|
399
|
+
let incrementId = useId();
|
|
400
|
+
let decrementId = useId();
|
|
450
401
|
|
|
402
|
+
// oxlint-disable-next-line react/react-compiler
|
|
451
403
|
let incrementButtonProps: AriaButtonProps = mergeProps(incButtonProps, {
|
|
452
404
|
'aria-label': incrementAriaLabel || stringFormatter.format('increase', { fieldLabel }).trim(),
|
|
453
405
|
id: ariaLabelledby && !incrementAriaLabel ? incrementId : null,
|
|
@@ -461,6 +413,7 @@ export function useNumberField(...args: any[]): NumberFieldAria {
|
|
|
461
413
|
onPressStart: onButtonPressStart,
|
|
462
414
|
});
|
|
463
415
|
|
|
416
|
+
// oxlint-disable-next-line react/react-compiler
|
|
464
417
|
let decrementButtonProps: AriaButtonProps = mergeProps(decButtonProps, {
|
|
465
418
|
'aria-label': decrementAriaLabel || stringFormatter.format('decrease', { fieldLabel }).trim(),
|
|
466
419
|
id: ariaLabelledby && !decrementAriaLabel ? decrementId : null,
|
|
@@ -504,69 +457,62 @@ function useNativeValidation(
|
|
|
504
457
|
max: number | undefined,
|
|
505
458
|
step: number | undefined,
|
|
506
459
|
value: number | undefined,
|
|
507
|
-
slot: symbol | undefined,
|
|
508
460
|
) {
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
) {
|
|
520
|
-
return;
|
|
521
|
-
}
|
|
461
|
+
useLayoutEffect(() => {
|
|
462
|
+
let input = inputRef.current;
|
|
463
|
+
if (
|
|
464
|
+
commitBehavior !== 'validate' ||
|
|
465
|
+
state.realtimeValidation.isInvalid ||
|
|
466
|
+
!input ||
|
|
467
|
+
input.disabled
|
|
468
|
+
) {
|
|
469
|
+
return;
|
|
470
|
+
}
|
|
522
471
|
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
472
|
+
// Create a native number input and use it to implement validation of min/max/step.
|
|
473
|
+
// This lets us get the native validation message provided by the browser instead of needing our own translations.
|
|
474
|
+
if (!numberInput && typeof document !== 'undefined') {
|
|
475
|
+
numberInput = document.createElement('input');
|
|
476
|
+
numberInput.type = 'number';
|
|
477
|
+
}
|
|
529
478
|
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
479
|
+
if (!numberInput) {
|
|
480
|
+
// For TypeScript.
|
|
481
|
+
return;
|
|
482
|
+
}
|
|
534
483
|
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
null,
|
|
570
|
-
subSlot(slot, 'effect'),
|
|
571
|
-
);
|
|
484
|
+
numberInput.min = min != null && !isNaN(min) ? String(min) : '';
|
|
485
|
+
numberInput.max = max != null && !isNaN(max) ? String(max) : '';
|
|
486
|
+
numberInput.step = step != null && !isNaN(step) ? String(step) : '';
|
|
487
|
+
numberInput.value = value != null && !isNaN(value) ? String(value) : '';
|
|
488
|
+
|
|
489
|
+
// Merge validity with the visible text input (for other validations like required).
|
|
490
|
+
let valid = input.validity.valid && numberInput.validity.valid;
|
|
491
|
+
let validationMessage = input.validationMessage || numberInput.validationMessage;
|
|
492
|
+
let validity = {
|
|
493
|
+
isInvalid: !valid,
|
|
494
|
+
validationErrors: validationMessage ? [validationMessage] : [],
|
|
495
|
+
validationDetails: {
|
|
496
|
+
badInput: input.validity.badInput,
|
|
497
|
+
customError: input.validity.customError,
|
|
498
|
+
patternMismatch: input.validity.patternMismatch,
|
|
499
|
+
rangeOverflow: numberInput.validity.rangeOverflow,
|
|
500
|
+
rangeUnderflow: numberInput.validity.rangeUnderflow,
|
|
501
|
+
stepMismatch: numberInput.validity.stepMismatch,
|
|
502
|
+
tooLong: input.validity.tooLong,
|
|
503
|
+
tooShort: input.validity.tooShort,
|
|
504
|
+
typeMismatch: input.validity.typeMismatch,
|
|
505
|
+
valueMissing: input.validity.valueMissing,
|
|
506
|
+
valid,
|
|
507
|
+
},
|
|
508
|
+
};
|
|
509
|
+
|
|
510
|
+
state.updateValidation(validity);
|
|
511
|
+
|
|
512
|
+
// Block form submission if validation behavior is native.
|
|
513
|
+
// This won't overwrite any user-defined validation message because we checked realtimeValidation above.
|
|
514
|
+
if (validationBehavior === 'native' && !numberInput.validity.valid) {
|
|
515
|
+
input.setCustomValidity(numberInput.validationMessage);
|
|
516
|
+
}
|
|
517
|
+
});
|
|
572
518
|
}
|
package/src/overlays/Overlay.ts
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
// Provider descriptor keeps a stable `{ value, children }` shape; React's ReactNode/JSX/Context
|
|
6
6
|
// types → `any`; `useOverlayFocusContain` gets public-hook slot threading.
|
|
7
7
|
import { ClearPressResponder } from '../interactions/PressResponder';
|
|
8
|
+
import { FocusableContext } from '../interactions/useFocusable';
|
|
8
9
|
import { FocusScope } from '../focus/FocusScope';
|
|
9
10
|
import { createContext, createElement, createPortal, useContext, useMemo, useState } from 'octane';
|
|
10
11
|
import { useIsSSR } from '../ssr/SSRProvider';
|
|
@@ -81,7 +82,10 @@ export function Overlay(props: OverlayProps): any {
|
|
|
81
82
|
|
|
82
83
|
contents = createElement(OverlayContext.Provider, {
|
|
83
84
|
value: contextValue as OverlayContextValue,
|
|
84
|
-
children: createElement(
|
|
85
|
+
children: createElement(FocusableContext.Provider, {
|
|
86
|
+
value: null,
|
|
87
|
+
children: createElement(ClearPressResponder, { children: contents }),
|
|
88
|
+
}),
|
|
85
89
|
});
|
|
86
90
|
|
|
87
91
|
return createPortal(contents, portalContainer);
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
// no slot threading. Module-level ref-count map and observer stack are ported verbatim.
|
|
5
5
|
import { createShadowTreeWalker } from '../utils/shadowdom/ShadowTreeWalker';
|
|
6
6
|
|
|
7
|
-
import { getOwnerDocument, getOwnerWindow } from '../utils/domHelpers';
|
|
7
|
+
import { getOwnerDocument, getOwnerWindow, isShadowRoot } from '../utils/domHelpers';
|
|
8
8
|
import { nodeContains } from '../utils/shadowdom/DOMFunctions';
|
|
9
9
|
import { shadowDOM } from '../stately/flags';
|
|
10
10
|
|
|
@@ -72,14 +72,12 @@ export function ariaHideOutside(targets: Element[], options?: AriaHideOutsideOpt
|
|
|
72
72
|
if (shadowDOM()) {
|
|
73
73
|
// find all shadow roots that are ancestors of the targets
|
|
74
74
|
// traverse upwards until the root is reached
|
|
75
|
+
let boundary = root.getRootNode();
|
|
75
76
|
for (let target of targets) {
|
|
76
|
-
let
|
|
77
|
-
while (
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
shadowRootsToWatch.add(root.shadowRoot as ShadowRoot);
|
|
81
|
-
}
|
|
82
|
-
node = root.parentNode as Element;
|
|
77
|
+
let current = target.getRootNode();
|
|
78
|
+
while (isShadowRoot(current) && current !== boundary) {
|
|
79
|
+
shadowRootsToWatch.add(current);
|
|
80
|
+
current = current.host.getRootNode();
|
|
83
81
|
}
|
|
84
82
|
}
|
|
85
83
|
}
|
|
@@ -1,10 +1,15 @@
|
|
|
1
|
+
import { addEvent } from '../utils/domHelpers';
|
|
1
2
|
// Ported from react-aria (source: .react-spectrum/packages/react-aria/src/overlays/useCloseOnScroll.ts).
|
|
2
3
|
// octane adaptations: public-hook slot threading (splitSlot/subSlot) per the binding
|
|
3
4
|
// convention; the explicit dependency array is kept verbatim.
|
|
4
5
|
//
|
|
5
6
|
// NOTE: ported ahead of the rest of the overlays area because useOverlayTrigger (needed by
|
|
6
7
|
// useMenuTrigger) shares its `onCloseMap` backward-compatibility channel.
|
|
7
|
-
import {
|
|
8
|
+
import {
|
|
9
|
+
getEventTarget,
|
|
10
|
+
getPropagationTargets,
|
|
11
|
+
nodeContains,
|
|
12
|
+
} from '../utils/shadowdom/DOMFunctions';
|
|
8
13
|
import type { RefObject } from '@react-types/shared';
|
|
9
14
|
import { useEffect } from 'octane';
|
|
10
15
|
|
|
@@ -64,10 +69,7 @@ export function useCloseOnScroll(...args: any[]): void {
|
|
|
64
69
|
}
|
|
65
70
|
};
|
|
66
71
|
|
|
67
|
-
|
|
68
|
-
return () => {
|
|
69
|
-
window.removeEventListener('scroll', onScroll, true);
|
|
70
|
-
};
|
|
72
|
+
return addEvent(getPropagationTargets(triggerRef.current), 'scroll', onScroll, true);
|
|
71
73
|
},
|
|
72
74
|
[isOpen, onClose, triggerRef],
|
|
73
75
|
subSlot(slot, 'scroll'),
|