@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
|
@@ -0,0 +1,330 @@
|
|
|
1
|
+
/** @jsxImportSource octane */
|
|
2
|
+
type DOMAttributes = Record<string, any>;
|
|
3
|
+
// Ported from adobe/react-spectrum@5ecb3333001313e83898cd07644227897e3bae1f (packages/react-aria/src/spinbutton/useSpinButton.ts).
|
|
4
|
+
/*
|
|
5
|
+
* Copyright 2020 Adobe. All rights reserved.
|
|
6
|
+
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
|
|
7
|
+
* you may not use this file except in compliance with the License. You may obtain a copy
|
|
8
|
+
* of the License at http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software distributed under
|
|
11
|
+
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
|
|
12
|
+
* OF ANY KIND, either express or implied. See the License for the specific language
|
|
13
|
+
* governing permissions and limitations under the License.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import { announce, clearAnnouncer } from '../live-announcer/LiveAnnouncer';
|
|
17
|
+
|
|
18
|
+
import { AriaButtonProps } from '../button/useButton';
|
|
19
|
+
import { InputBase, RangeInputBase, Validation, ValueBase } from '@react-types/shared';
|
|
20
|
+
import intlMessages from '../intl/spinbutton/index';
|
|
21
|
+
import { useCallback, useEffect, useRef, useState } from '../compat/react';
|
|
22
|
+
import { useEffectEvent } from '../utils/useEffectEvent';
|
|
23
|
+
import { useGlobalListeners } from '../utils/useGlobalListeners';
|
|
24
|
+
import { useKeyboard } from '../interactions/useKeyboard';
|
|
25
|
+
import { useLocalizedStringFormatter } from '../i18n/useLocalizedStringFormatter';
|
|
26
|
+
|
|
27
|
+
const noop = () => {};
|
|
28
|
+
|
|
29
|
+
export interface SpinButtonProps
|
|
30
|
+
extends InputBase, Validation<number>, ValueBase<number>, RangeInputBase<number> {
|
|
31
|
+
textValue?: string;
|
|
32
|
+
onIncrement?: () => void;
|
|
33
|
+
onIncrementPage?: () => void;
|
|
34
|
+
onDecrement?: () => void;
|
|
35
|
+
onDecrementPage?: () => void;
|
|
36
|
+
onDecrementToMin?: () => void;
|
|
37
|
+
onIncrementToMax?: () => void;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export interface SpinbuttonAria {
|
|
41
|
+
spinButtonProps: DOMAttributes;
|
|
42
|
+
incrementButtonProps: AriaButtonProps;
|
|
43
|
+
decrementButtonProps: AriaButtonProps;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function useSpinButton(props: SpinButtonProps, _slot?: symbol): SpinbuttonAria {
|
|
47
|
+
const _async = useRef<number | undefined>(undefined);
|
|
48
|
+
let {
|
|
49
|
+
value,
|
|
50
|
+
textValue,
|
|
51
|
+
minValue,
|
|
52
|
+
maxValue,
|
|
53
|
+
isDisabled,
|
|
54
|
+
isReadOnly,
|
|
55
|
+
isRequired,
|
|
56
|
+
onIncrement,
|
|
57
|
+
onIncrementPage,
|
|
58
|
+
onDecrement,
|
|
59
|
+
onDecrementPage,
|
|
60
|
+
onDecrementToMin,
|
|
61
|
+
onIncrementToMax,
|
|
62
|
+
} = props;
|
|
63
|
+
const stringFormatter = useLocalizedStringFormatter(intlMessages, '@react-aria/spinbutton');
|
|
64
|
+
|
|
65
|
+
let isSpinning = useRef(false);
|
|
66
|
+
const clearAsync = useCallback(() => {
|
|
67
|
+
clearTimeout(_async.current);
|
|
68
|
+
isSpinning.current = false;
|
|
69
|
+
}, []);
|
|
70
|
+
const clearAsyncEvent = useEffectEvent(() => {
|
|
71
|
+
clearAsync();
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
useEffect(() => {
|
|
75
|
+
return () => clearAsyncEvent();
|
|
76
|
+
}, []);
|
|
77
|
+
|
|
78
|
+
let { keyboardProps } = useKeyboard({
|
|
79
|
+
isDisabled: isDisabled || isReadOnly,
|
|
80
|
+
shortcuts: {
|
|
81
|
+
PageUp: () => {
|
|
82
|
+
if (onIncrementPage) {
|
|
83
|
+
onIncrementPage();
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
if (onIncrement) {
|
|
87
|
+
onIncrement();
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
return false;
|
|
91
|
+
},
|
|
92
|
+
ArrowUp: () => {
|
|
93
|
+
if (onIncrement) {
|
|
94
|
+
onIncrement();
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
return false;
|
|
98
|
+
},
|
|
99
|
+
PageDown: () => {
|
|
100
|
+
if (onDecrementPage) {
|
|
101
|
+
onDecrementPage();
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
if (onDecrement) {
|
|
105
|
+
onDecrement();
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
return false;
|
|
109
|
+
},
|
|
110
|
+
ArrowDown: () => {
|
|
111
|
+
if (onDecrement) {
|
|
112
|
+
onDecrement();
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
return false;
|
|
116
|
+
},
|
|
117
|
+
Home: () => {
|
|
118
|
+
if (onDecrementToMin) {
|
|
119
|
+
onDecrementToMin();
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
return false;
|
|
123
|
+
},
|
|
124
|
+
End: () => {
|
|
125
|
+
if (onIncrementToMax) {
|
|
126
|
+
onIncrementToMax();
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
return false;
|
|
130
|
+
},
|
|
131
|
+
},
|
|
132
|
+
allowRepeats: true,
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
let isFocused = useRef(false);
|
|
136
|
+
let onFocus = () => {
|
|
137
|
+
isFocused.current = true;
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
let onBlur = () => {
|
|
141
|
+
isFocused.current = false;
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
// Replace Unicode hyphen-minus (U+002D) with minus sign (U+2212).
|
|
145
|
+
// This ensures that macOS VoiceOver announces it as "minus" even with other characters between the minus sign
|
|
146
|
+
// and the number (e.g. currency symbol). Otherwise it announces nothing because it assumes the character is a hyphen.
|
|
147
|
+
// In addition, replace the empty string with the word "Empty" so that iOS VoiceOver does not read "50%" for an empty field.
|
|
148
|
+
let ariaTextValue =
|
|
149
|
+
textValue === ''
|
|
150
|
+
? stringFormatter.format('Empty')
|
|
151
|
+
: (textValue || `${value}`).replace('-', '\u2212');
|
|
152
|
+
|
|
153
|
+
useEffect(() => {
|
|
154
|
+
if (isFocused.current) {
|
|
155
|
+
clearAnnouncer('assertive');
|
|
156
|
+
announce(ariaTextValue, 'assertive');
|
|
157
|
+
}
|
|
158
|
+
}, [ariaTextValue]);
|
|
159
|
+
|
|
160
|
+
// For touch users, if they move their finger like they're scrolling, we don't want to trigger a spin.
|
|
161
|
+
let onPointerCancel = useCallback(() => {
|
|
162
|
+
clearAsync();
|
|
163
|
+
}, [clearAsync]);
|
|
164
|
+
|
|
165
|
+
const onIncrementEvent = useEffectEvent(onIncrement ?? noop);
|
|
166
|
+
const onDecrementEvent = useEffectEvent(onDecrement ?? noop);
|
|
167
|
+
|
|
168
|
+
const stepUpEvent = useEffectEvent(() => {
|
|
169
|
+
if (
|
|
170
|
+
maxValue === undefined ||
|
|
171
|
+
isNaN(maxValue) ||
|
|
172
|
+
value === undefined ||
|
|
173
|
+
isNaN(value) ||
|
|
174
|
+
value < maxValue
|
|
175
|
+
) {
|
|
176
|
+
onIncrementEvent();
|
|
177
|
+
// oxlint-disable-next-line react/react-compiler
|
|
178
|
+
onIncrementPressStartEvent(60);
|
|
179
|
+
}
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
const onIncrementPressStartEvent = useEffectEvent((initialStepDelay: number) => {
|
|
183
|
+
clearAsyncEvent();
|
|
184
|
+
isSpinning.current = true;
|
|
185
|
+
// Start spinning after initial delay
|
|
186
|
+
_async.current = window.setTimeout(stepUpEvent, initialStepDelay);
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
const stepDownEvent = useEffectEvent(() => {
|
|
190
|
+
if (
|
|
191
|
+
minValue === undefined ||
|
|
192
|
+
isNaN(minValue) ||
|
|
193
|
+
value === undefined ||
|
|
194
|
+
isNaN(value) ||
|
|
195
|
+
value > minValue
|
|
196
|
+
) {
|
|
197
|
+
onDecrementEvent();
|
|
198
|
+
// oxlint-disable-next-line react/react-compiler
|
|
199
|
+
onDecrementPressStartEvent(60);
|
|
200
|
+
}
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
const onDecrementPressStartEvent = useEffectEvent((initialStepDelay: number) => {
|
|
204
|
+
clearAsyncEvent();
|
|
205
|
+
isSpinning.current = true;
|
|
206
|
+
// Start spinning after initial delay
|
|
207
|
+
_async.current = window.setTimeout(stepDownEvent, initialStepDelay);
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
let cancelContextMenu = (e: MouseEvent) => {
|
|
211
|
+
e.preventDefault();
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
let { addGlobalListener, removeAllGlobalListeners } = useGlobalListeners();
|
|
215
|
+
|
|
216
|
+
// Tracks in touch if the press end event was preceded by a press up.
|
|
217
|
+
// If it wasn't, then we know the finger left the button while still in contact with the screen.
|
|
218
|
+
// This means that the user is trying to scroll or interact in some way that shouldn't trigger
|
|
219
|
+
// an increment or decrement.
|
|
220
|
+
let isUp = useRef(false);
|
|
221
|
+
|
|
222
|
+
let [isIncrementPressed, setIsIncrementPressed] = useState<'touch' | 'mouse' | null>(null);
|
|
223
|
+
useEffect(() => {
|
|
224
|
+
if (isIncrementPressed === 'touch') {
|
|
225
|
+
onIncrementPressStartEvent(600);
|
|
226
|
+
} else if (isIncrementPressed) {
|
|
227
|
+
onIncrementPressStartEvent(400);
|
|
228
|
+
} else if (!isIncrementPressed) {
|
|
229
|
+
clearAsyncEvent();
|
|
230
|
+
}
|
|
231
|
+
}, [isIncrementPressed]);
|
|
232
|
+
|
|
233
|
+
let [isDecrementPressed, setIsDecrementPressed] = useState<'touch' | 'mouse' | null>(null);
|
|
234
|
+
useEffect(() => {
|
|
235
|
+
if (isDecrementPressed === 'touch') {
|
|
236
|
+
onDecrementPressStartEvent(600);
|
|
237
|
+
} else if (isDecrementPressed) {
|
|
238
|
+
onDecrementPressStartEvent(400);
|
|
239
|
+
} else if (!isDecrementPressed) {
|
|
240
|
+
clearAsyncEvent();
|
|
241
|
+
}
|
|
242
|
+
}, [isDecrementPressed]);
|
|
243
|
+
|
|
244
|
+
return {
|
|
245
|
+
spinButtonProps: {
|
|
246
|
+
role: 'spinbutton',
|
|
247
|
+
'aria-valuenow': value !== undefined && !isNaN(value) ? value : undefined,
|
|
248
|
+
'aria-valuetext': ariaTextValue,
|
|
249
|
+
'aria-valuemin': minValue,
|
|
250
|
+
'aria-valuemax': maxValue,
|
|
251
|
+
'aria-disabled': isDisabled || undefined,
|
|
252
|
+
'aria-readonly': isReadOnly || undefined,
|
|
253
|
+
'aria-required': isRequired || undefined,
|
|
254
|
+
...keyboardProps,
|
|
255
|
+
onFocus,
|
|
256
|
+
onBlur,
|
|
257
|
+
},
|
|
258
|
+
incrementButtonProps: {
|
|
259
|
+
onPressStart: (e) => {
|
|
260
|
+
clearAsync();
|
|
261
|
+
if (e.pointerType !== 'touch') {
|
|
262
|
+
onIncrement?.();
|
|
263
|
+
setIsIncrementPressed('mouse');
|
|
264
|
+
} else {
|
|
265
|
+
addGlobalListener(window, 'pointercancel', onPointerCancel, { capture: true });
|
|
266
|
+
isUp.current = false;
|
|
267
|
+
// For touch users, don't trigger a decrement on press start, we'll wait for the press end to trigger it if
|
|
268
|
+
// the control isn't spinning.
|
|
269
|
+
setIsIncrementPressed('touch');
|
|
270
|
+
}
|
|
271
|
+
addGlobalListener(window, 'contextmenu', cancelContextMenu);
|
|
272
|
+
},
|
|
273
|
+
onPressUp: (e) => {
|
|
274
|
+
clearAsync();
|
|
275
|
+
if (e.pointerType === 'touch') {
|
|
276
|
+
isUp.current = true;
|
|
277
|
+
}
|
|
278
|
+
removeAllGlobalListeners();
|
|
279
|
+
setIsIncrementPressed(null);
|
|
280
|
+
},
|
|
281
|
+
onPressEnd: (e) => {
|
|
282
|
+
clearAsync();
|
|
283
|
+
if (e.pointerType === 'touch') {
|
|
284
|
+
if (!isSpinning.current && isUp.current) {
|
|
285
|
+
onIncrement?.();
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
isUp.current = false;
|
|
289
|
+
setIsIncrementPressed(null);
|
|
290
|
+
},
|
|
291
|
+
onFocus,
|
|
292
|
+
onBlur,
|
|
293
|
+
},
|
|
294
|
+
decrementButtonProps: {
|
|
295
|
+
onPressStart: (e) => {
|
|
296
|
+
clearAsync();
|
|
297
|
+
if (e.pointerType !== 'touch') {
|
|
298
|
+
onDecrement?.();
|
|
299
|
+
setIsDecrementPressed('mouse');
|
|
300
|
+
} else {
|
|
301
|
+
addGlobalListener(window, 'pointercancel', onPointerCancel, { capture: true });
|
|
302
|
+
isUp.current = false;
|
|
303
|
+
// For touch users, don't trigger a decrement on press start, we'll wait for the press end to trigger it if
|
|
304
|
+
// the control isn't spinning.
|
|
305
|
+
setIsDecrementPressed('touch');
|
|
306
|
+
}
|
|
307
|
+
},
|
|
308
|
+
onPressUp: (e) => {
|
|
309
|
+
clearAsync();
|
|
310
|
+
if (e.pointerType === 'touch') {
|
|
311
|
+
isUp.current = true;
|
|
312
|
+
}
|
|
313
|
+
removeAllGlobalListeners();
|
|
314
|
+
setIsDecrementPressed(null);
|
|
315
|
+
},
|
|
316
|
+
onPressEnd: (e) => {
|
|
317
|
+
clearAsync();
|
|
318
|
+
if (e.pointerType === 'touch') {
|
|
319
|
+
if (!isSpinning.current && isUp.current) {
|
|
320
|
+
onDecrement?.();
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
isUp.current = false;
|
|
324
|
+
setIsDecrementPressed(null);
|
|
325
|
+
},
|
|
326
|
+
onFocus,
|
|
327
|
+
onBlur,
|
|
328
|
+
},
|
|
329
|
+
};
|
|
330
|
+
}
|
|
@@ -38,7 +38,7 @@ import { useControlledState } from '../utils/useControlledState';
|
|
|
38
38
|
|
|
39
39
|
export type MenuTriggerAction = 'focus' | 'input' | 'manual';
|
|
40
40
|
export type SelectionMode = 'single' | 'multiple';
|
|
41
|
-
export type ValueType<M extends SelectionMode> = M extends 'single' ? Key | null : Key[];
|
|
41
|
+
export type ValueType<M extends SelectionMode> = M extends 'single' ? Key | null : readonly Key[];
|
|
42
42
|
export type ChangeValueType<M extends SelectionMode> = M extends 'single' ? Key | null : Key[];
|
|
43
43
|
type ValidationType<M extends SelectionMode> = M extends 'single' ? Key | null : Key[];
|
|
44
44
|
|
|
@@ -118,20 +118,19 @@ export function useGridState(...args: any[]): GridState<any, IGridCollection<any
|
|
|
118
118
|
rows.length - 1,
|
|
119
119
|
);
|
|
120
120
|
let newRow: GridNode<any> | null = null;
|
|
121
|
-
|
|
122
|
-
if (!selectionManager.isDisabled(rows[
|
|
123
|
-
newRow = rows[
|
|
121
|
+
for (let i = Math.max(0, index); i < rows.length; i++) {
|
|
122
|
+
if (!selectionManager.isDisabled(rows[i].key) && rows[i].type !== 'headerrow') {
|
|
123
|
+
newRow = rows[i];
|
|
124
124
|
break;
|
|
125
125
|
}
|
|
126
126
|
// Find next, not disabled row.
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
127
|
+
}
|
|
128
|
+
if (newRow === null) {
|
|
129
|
+
for (let i = index - 1; i >= 0; i--) {
|
|
130
|
+
if (!selectionManager.isDisabled(rows[i].key) && rows[i].type !== 'headerrow') {
|
|
131
|
+
newRow = rows[i];
|
|
132
|
+
break;
|
|
133
133
|
}
|
|
134
|
-
index--;
|
|
135
134
|
}
|
|
136
135
|
}
|
|
137
136
|
if (newRow) {
|
package/src/stately/index.ts
CHANGED
|
@@ -159,3 +159,14 @@ export type { TableBodyProps } from './table/TableBody';
|
|
|
159
159
|
export type { RowProps, RowElement } from './table/Row';
|
|
160
160
|
export type { CellProps, CellElement, CellRenderer } from './table/Cell';
|
|
161
161
|
export type { ITableCollection } from './table/TableCollection';
|
|
162
|
+
|
|
163
|
+
export { useTokenFieldState } from './tokenfield/useTokenFieldState';
|
|
164
|
+
export type { TokenFieldProps, TokenFieldState } from './tokenfield/useTokenFieldState';
|
|
165
|
+
export { TokenFieldValue } from './tokenfield/TokenFieldValue';
|
|
166
|
+
export type {
|
|
167
|
+
TokenFieldSegment,
|
|
168
|
+
TextSegment,
|
|
169
|
+
TokenSegment,
|
|
170
|
+
Position,
|
|
171
|
+
TokenFieldValueOptions,
|
|
172
|
+
} from './tokenfield/TokenFieldValue';
|
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
useOverlayTriggerState,
|
|
12
12
|
} from '../overlays/useOverlayTriggerState';
|
|
13
13
|
|
|
14
|
-
export type MenuTriggerType = 'press' | 'longPress';
|
|
14
|
+
export type MenuTriggerType = 'press' | 'longPress' | 'contextMenu';
|
|
15
15
|
|
|
16
16
|
export interface MenuTriggerProps extends OverlayTriggerProps {
|
|
17
17
|
/**
|
|
@@ -109,6 +109,8 @@ export function useSubmenuTriggerState(...args: any[]): SubmenuTriggerState {
|
|
|
109
109
|
// https://github.com/adobe/react-spectrum/pull/4976#discussion_r1336472863
|
|
110
110
|
setOpen: () => {},
|
|
111
111
|
toggle,
|
|
112
|
+
point: null,
|
|
113
|
+
setPoint: () => {},
|
|
112
114
|
}),
|
|
113
115
|
[isOpen, open, close, closeAll, focusStrategy, toggle, submenuLevel],
|
|
114
116
|
subSlot(slot, 'state'),
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// octane adaptations: public-hook slot threading (splitSlot/subSlot) per the binding
|
|
3
3
|
// convention; the public value-level `onOpenChange` callback is unchanged (the onInput
|
|
4
4
|
// rule applies only to DOM wiring); explicit dependency arrays are kept verbatim.
|
|
5
|
-
import { useCallback } from 'octane';
|
|
5
|
+
import { useCallback, useState } from 'octane';
|
|
6
6
|
|
|
7
7
|
import { S, splitSlot, subSlot } from '../../internal';
|
|
8
8
|
import { useControlledState } from '../utils/useControlledState';
|
|
@@ -16,6 +16,11 @@ export interface OverlayTriggerProps {
|
|
|
16
16
|
onOpenChange?: (isOpen: boolean) => void;
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
+
interface Point {
|
|
20
|
+
x: number;
|
|
21
|
+
y: number;
|
|
22
|
+
}
|
|
23
|
+
|
|
19
24
|
export interface OverlayTriggerState {
|
|
20
25
|
/** Whether the overlay is currently open. */
|
|
21
26
|
readonly isOpen: boolean;
|
|
@@ -27,6 +32,9 @@ export interface OverlayTriggerState {
|
|
|
27
32
|
close(): void;
|
|
28
33
|
/** Toggles the overlay's visibility. */
|
|
29
34
|
toggle(): void;
|
|
35
|
+
readonly point: Point | null;
|
|
36
|
+
/** Sets the cursor position relative to the window viewport. */
|
|
37
|
+
setPoint(point: Point): void;
|
|
30
38
|
}
|
|
31
39
|
|
|
32
40
|
/**
|
|
@@ -51,6 +59,7 @@ export function useOverlayTriggerState(...args: any[]): OverlayTriggerState {
|
|
|
51
59
|
subSlot(slot, 'open'),
|
|
52
60
|
);
|
|
53
61
|
|
|
62
|
+
let [point, setPoint] = useState<Point | null>(null, subSlot(slot, 'point'));
|
|
54
63
|
const open = useCallback(
|
|
55
64
|
() => {
|
|
56
65
|
setOpen(true);
|
|
@@ -81,5 +90,7 @@ export function useOverlayTriggerState(...args: any[]): OverlayTriggerState {
|
|
|
81
90
|
open,
|
|
82
91
|
close,
|
|
83
92
|
toggle,
|
|
93
|
+
point,
|
|
94
|
+
setPoint,
|
|
84
95
|
};
|
|
85
96
|
}
|
|
@@ -1,9 +1,13 @@
|
|
|
1
|
+
interface FocusEvents {
|
|
2
|
+
onFocus?: (event: FocusEvent) => void;
|
|
3
|
+
onBlur?: (event: FocusEvent) => void;
|
|
4
|
+
onFocusChange?: (isFocused: boolean) => void;
|
|
5
|
+
}
|
|
1
6
|
// Ported from react-stately (source: .react-spectrum/packages/react-stately/src/radio/useRadioGroupState.ts).
|
|
2
7
|
// octane adaptations: public-hook slot threading (splitSlot/subSlot) per the binding
|
|
3
8
|
// convention; the module-level instance/name counters port verbatim. The public
|
|
4
9
|
// value-level `onChange` callback is unchanged (the onInput rule applies only to DOM wiring).
|
|
5
10
|
import type {
|
|
6
|
-
FocusEvents,
|
|
7
11
|
HelpTextProps,
|
|
8
12
|
InputBase,
|
|
9
13
|
InputDOMProps,
|
|
@@ -104,6 +104,10 @@ export function calculateColumnSizes(
|
|
|
104
104
|
getDefaultWidth?: (index: number) => ColumnSize | null | undefined,
|
|
105
105
|
getDefaultMinWidth?: (index: number) => ColumnSize | null | undefined,
|
|
106
106
|
): number[] {
|
|
107
|
+
let originalWidth = availableWidth;
|
|
108
|
+
let flooredWidth = Math.floor(availableWidth);
|
|
109
|
+
let hasFractionalWidth = availableWidth - flooredWidth > 0;
|
|
110
|
+
availableWidth = flooredWidth;
|
|
107
111
|
let hasNonFrozenItems = false;
|
|
108
112
|
let flexItems: FlexItem[] = columns.map((column, index) => {
|
|
109
113
|
let width: ColumnSize = (
|
|
@@ -245,7 +249,17 @@ export function calculateColumnSizes(
|
|
|
245
249
|
});
|
|
246
250
|
}
|
|
247
251
|
|
|
248
|
-
|
|
252
|
+
let columnSizes = cascadeRounding(flexItems);
|
|
253
|
+
|
|
254
|
+
// Give the leftover sub-pixel width to the last column so the columns sum
|
|
255
|
+
// exactly to the (possibly fractional) available width.
|
|
256
|
+
if (hasFractionalWidth && columnSizes.length > 0) {
|
|
257
|
+
let tableFractionalWidth = originalWidth.toString().split('.')[1];
|
|
258
|
+
let columnWidth = columnSizes[columnSizes.length - 1].toString();
|
|
259
|
+
columnSizes[columnSizes.length - 1] = Number(columnWidth + '.' + tableFractionalWidth);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
return columnSizes;
|
|
249
263
|
}
|
|
250
264
|
|
|
251
265
|
function cascadeRounding(flexItems: FlexItem[]): number[] {
|
|
@@ -42,6 +42,13 @@ export interface TableProps<T> extends MultipleSelection, Sortable, Expandable {
|
|
|
42
42
|
shouldSelectOnPressUp?: boolean;
|
|
43
43
|
/** The id of the column that displays hierarchical data. */
|
|
44
44
|
treeColumn?: Key;
|
|
45
|
+
/**
|
|
46
|
+
* Whether keyboard navigation to focusable elements within the cells is
|
|
47
|
+
* via the left/right arrow keys or the tab key.
|
|
48
|
+
*
|
|
49
|
+
* @default 'arrow'
|
|
50
|
+
*/
|
|
51
|
+
keyboardNavigationBehavior?: 'arrow' | 'tab';
|
|
45
52
|
}
|
|
46
53
|
|
|
47
54
|
export interface TableState<T> extends GridState<T, ITableCollection<T>> {
|