@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,218 @@
|
|
|
1
|
+
/** @jsxImportSource octane */
|
|
2
|
+
// Ported from adobe/react-spectrum@5ecb3333001313e83898cd07644227897e3bae1f (packages/react-aria/src/interactions/createKeyboardShortcutHandler.ts).
|
|
3
|
+
/*
|
|
4
|
+
* Copyright 2025 Adobe. All rights reserved.
|
|
5
|
+
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License. You may obtain a copy
|
|
7
|
+
* of the License at http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
*
|
|
9
|
+
* Unless required by applicable law or agreed to in writing, software distributed under
|
|
10
|
+
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
|
|
11
|
+
* OF ANY KIND, either express or implied. See the License for the specific language
|
|
12
|
+
* governing permissions and limitations under the License.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { isMac } from '../utils/platform';
|
|
16
|
+
import type { BaseEvent } from './createEventHandler';
|
|
17
|
+
type KeyboardEvent = BaseEvent<globalThis.KeyboardEvent>;
|
|
18
|
+
|
|
19
|
+
export type KeyboardShortcutAction = (
|
|
20
|
+
e: KeyboardEvent,
|
|
21
|
+
) =>
|
|
22
|
+
void | boolean | Partial<{ shouldContinuePropagation?: boolean; shouldPreventDefault?: boolean }>;
|
|
23
|
+
|
|
24
|
+
/** Maps shortcut strings (e.g. `"Mod+s"`, `"Control+Shift+a"`) to handlers. */
|
|
25
|
+
export type KeyboardShortcutBindings = Record<string, KeyboardShortcutAction>;
|
|
26
|
+
|
|
27
|
+
/** Modifier names in shortcut strings (case-insensitive). Order in the string does not matter. */
|
|
28
|
+
const MODIFIER_NAMES = new Set([
|
|
29
|
+
'shift',
|
|
30
|
+
'alt',
|
|
31
|
+
'control',
|
|
32
|
+
'meta',
|
|
33
|
+
'mod', // OS dependent - Cmd on Mac, Control on Windows/Linux
|
|
34
|
+
]);
|
|
35
|
+
|
|
36
|
+
/** Canonical modifier order for stable keys (sorted, fixed order). */
|
|
37
|
+
const CANONICAL_MODIFIER_ORDER = ['Alt', 'Control', 'Meta', 'Shift'] as const;
|
|
38
|
+
|
|
39
|
+
export interface ParsedKeyboardShortcut {
|
|
40
|
+
shift: boolean;
|
|
41
|
+
alt: boolean;
|
|
42
|
+
ctrl: boolean;
|
|
43
|
+
meta: boolean;
|
|
44
|
+
/**
|
|
45
|
+
* Platform primary: Cmd on Mac, Control on Windows/Linux — expands to Meta or Control in
|
|
46
|
+
* canonical form.
|
|
47
|
+
*/
|
|
48
|
+
mod: boolean;
|
|
49
|
+
key: string;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Builds the set of canonical modifier tokens for a binding.
|
|
54
|
+
* `Mod` contributes Meta (Mac) or Ctrl (non-Mac); explicit Ctrl/Meta add those keys too.
|
|
55
|
+
*/
|
|
56
|
+
export function modifierSetFromParsed(parsed: ParsedKeyboardShortcut): Set<string> {
|
|
57
|
+
let set = new Set<string>();
|
|
58
|
+
if (parsed.alt) {
|
|
59
|
+
set.add('Alt');
|
|
60
|
+
}
|
|
61
|
+
if (parsed.shift) {
|
|
62
|
+
set.add('Shift');
|
|
63
|
+
}
|
|
64
|
+
if (parsed.ctrl) {
|
|
65
|
+
set.add('Control');
|
|
66
|
+
}
|
|
67
|
+
if (parsed.meta) {
|
|
68
|
+
set.add('Meta');
|
|
69
|
+
}
|
|
70
|
+
if (parsed.mod) {
|
|
71
|
+
set.add(isMac() ? 'Meta' : 'Control');
|
|
72
|
+
}
|
|
73
|
+
return set;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/** Modifier set from a keydown event (native flags only). */
|
|
77
|
+
export function modifierSetFromEvent(e: KeyboardEvent): Set<string> {
|
|
78
|
+
let set = new Set<string>();
|
|
79
|
+
if (e.altKey) {
|
|
80
|
+
set.add('Alt');
|
|
81
|
+
}
|
|
82
|
+
if (e.ctrlKey) {
|
|
83
|
+
set.add('Control');
|
|
84
|
+
}
|
|
85
|
+
if (e.metaKey) {
|
|
86
|
+
set.add('Meta');
|
|
87
|
+
}
|
|
88
|
+
if (e.shiftKey) {
|
|
89
|
+
set.add('Shift');
|
|
90
|
+
}
|
|
91
|
+
return set;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function sortedModifierTokens(set: Set<string>): string[] {
|
|
95
|
+
return CANONICAL_MODIFIER_ORDER.filter((name) => set.has(name));
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Parses a shortcut like `"Mod+Shift+z"`, `"Ctrl+Alt+Enter"`, or `"Escape"`.
|
|
100
|
+
* Modifiers are case-insensitive; order does not matter. `control` is an alias for `ctrl`.
|
|
101
|
+
*/
|
|
102
|
+
export function parseKeyboardShortcut(spec: string): ParsedKeyboardShortcut {
|
|
103
|
+
let parts = spec.split('+').reduce<ParsedKeyboardShortcut>(
|
|
104
|
+
(prev, part) => {
|
|
105
|
+
let lower = part.toLowerCase();
|
|
106
|
+
if (MODIFIER_NAMES.has(lower)) {
|
|
107
|
+
if (lower === 'shift') {
|
|
108
|
+
prev.shift = true;
|
|
109
|
+
} else if (lower === 'alt') {
|
|
110
|
+
prev.alt = true;
|
|
111
|
+
} else if (lower === 'control') {
|
|
112
|
+
prev.ctrl = true;
|
|
113
|
+
} else if (lower === 'meta') {
|
|
114
|
+
prev.meta = true;
|
|
115
|
+
} else if (lower === 'mod') {
|
|
116
|
+
prev.mod = true;
|
|
117
|
+
}
|
|
118
|
+
} else {
|
|
119
|
+
prev.key = part;
|
|
120
|
+
}
|
|
121
|
+
return prev;
|
|
122
|
+
},
|
|
123
|
+
{ shift: false, alt: false, ctrl: false, meta: false, mod: false, key: '' },
|
|
124
|
+
);
|
|
125
|
+
if (parts.key === '') {
|
|
126
|
+
throw new Error(
|
|
127
|
+
`Invalid keyboard shortcut: "${spec}". Must include exactly one non-modifier key (e.g. "a", "Enter", "ArrowDown"). Combine any of Shift, Alt, Ctrl, Meta, and Mod.`,
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
return parts;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function normalizeEventKey(key: string): string {
|
|
134
|
+
return key.toLowerCase();
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/** Short aliases for common keys (shortcut side, before match). */
|
|
138
|
+
const KEY_ALIASES: Record<string, string> = {
|
|
139
|
+
space: ' ',
|
|
140
|
+
esc: 'escape',
|
|
141
|
+
del: 'delete',
|
|
142
|
+
ins: 'insert',
|
|
143
|
+
left: 'arrowleft',
|
|
144
|
+
right: 'arrowright',
|
|
145
|
+
up: 'arrowup',
|
|
146
|
+
down: 'arrowdown',
|
|
147
|
+
pageup: 'pageup',
|
|
148
|
+
pagedown: 'pagedown',
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
/** Canonical key segment (lowercase); aliases like `down` → `arrowdown`. */
|
|
152
|
+
function canonicalKeyFromSpecKey(specKey: string): string {
|
|
153
|
+
let k = normalizeEventKey(specKey);
|
|
154
|
+
let aliased = KEY_ALIASES[k];
|
|
155
|
+
return aliased != null ? aliased : k;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/** Canonical shortcut string for a binding (modifiers sorted: Alt, Ctrl, Meta, Shift, then key). */
|
|
159
|
+
export function canonicalKeyboardShortcut(parsed: ParsedKeyboardShortcut): string {
|
|
160
|
+
let mods = sortedModifierTokens(modifierSetFromParsed(parsed));
|
|
161
|
+
let key = canonicalKeyFromSpecKey(parsed.key);
|
|
162
|
+
return mods.length > 0 ? `${mods.join('+')}+${key}` : key;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/** Canonical shortcut string for a keydown event. */
|
|
166
|
+
export function keyboardEventToCanonicalShortcut(e: KeyboardEvent): string {
|
|
167
|
+
let mods = sortedModifierTokens(modifierSetFromEvent(e));
|
|
168
|
+
let key = normalizeEventKey(e.key);
|
|
169
|
+
let prefix = mods.length > 0 ? `${mods.join('+')}+` : '';
|
|
170
|
+
return prefix + key;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Returns a keydown handler that runs the action only for an exact modifier+key match.
|
|
175
|
+
* Modifier order in the string does not matter (`Shift+Mod+a` ≡ `Mod+Shift+a`).
|
|
176
|
+
* Any combination of **Shift**, **Alt**, **Ctrl**, **Meta**, and **Mod** is allowed; **Mod** means
|
|
177
|
+
* Cmd on Apple platforms and Ctrl on Windows/Linux (same as before). **control** aliases **ctrl**.
|
|
178
|
+
*
|
|
179
|
+
* Duplicate bindings that normalize to the same shortcut: later object entries win.
|
|
180
|
+
*
|
|
181
|
+
* @example
|
|
182
|
+
* ```tsx
|
|
183
|
+
* let onKeyDown = createKeyboardShortcutHandler({
|
|
184
|
+
* 'Mod+s': e => {
|
|
185
|
+
* e.preventDefault();
|
|
186
|
+
* save();
|
|
187
|
+
* },
|
|
188
|
+
* 'Ctrl+Shift+k': () => palette(),
|
|
189
|
+
* 'Meta+Alt+ArrowLeft': () => back()
|
|
190
|
+
* });
|
|
191
|
+
* ```;
|
|
192
|
+
*/
|
|
193
|
+
export function createKeyboardShortcutHandler(
|
|
194
|
+
bindings: KeyboardShortcutBindings,
|
|
195
|
+
): (e: KeyboardEvent) => void {
|
|
196
|
+
let map = new Map<string, KeyboardShortcutAction>();
|
|
197
|
+
for (let [spec, action] of Object.entries(bindings)) {
|
|
198
|
+
let parsed = parseKeyboardShortcut(spec);
|
|
199
|
+
map.set(canonicalKeyboardShortcut(parsed), action);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
return (e: KeyboardEvent) => {
|
|
203
|
+
let canonical = keyboardEventToCanonicalShortcut(e);
|
|
204
|
+
let action = map.get(canonical);
|
|
205
|
+
let result = action?.(e);
|
|
206
|
+
if (result === undefined && action !== undefined) {
|
|
207
|
+
result = { shouldContinuePropagation: false, shouldPreventDefault: true };
|
|
208
|
+
} else if (typeof result === 'boolean') {
|
|
209
|
+
result = { shouldContinuePropagation: !result, shouldPreventDefault: result };
|
|
210
|
+
}
|
|
211
|
+
if (result?.shouldPreventDefault) {
|
|
212
|
+
e.preventDefault();
|
|
213
|
+
}
|
|
214
|
+
if (!action || result?.shouldContinuePropagation) {
|
|
215
|
+
e.continuePropagation();
|
|
216
|
+
}
|
|
217
|
+
};
|
|
218
|
+
}
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
// `string`, which this repo's strict indexing rejects).
|
|
5
5
|
import { getOwnerDocument } from '../utils/domHelpers';
|
|
6
6
|
|
|
7
|
-
import { isIOS } from '../utils/platform';
|
|
7
|
+
import { isIOS, isWebKit } from '../utils/platform';
|
|
8
8
|
import { runAfterTransition } from '../utils/runAfterTransition';
|
|
9
9
|
|
|
10
10
|
// Safari on iOS starts selecting text on long press. The only way to avoid this, it seems,
|
|
@@ -29,7 +29,7 @@ let savedUserSelect = '';
|
|
|
29
29
|
let modifiedElementMap = new WeakMap<Element, string>();
|
|
30
30
|
|
|
31
31
|
export function disableTextSelection(target?: Element): void {
|
|
32
|
-
if (isIOS()) {
|
|
32
|
+
if (isIOS() && isWebKit()) {
|
|
33
33
|
if (state === 'default') {
|
|
34
34
|
const documentObject = getOwnerDocument(target);
|
|
35
35
|
savedUserSelect = documentObject.documentElement.style.webkitUserSelect;
|
|
@@ -48,7 +48,7 @@ export function disableTextSelection(target?: Element): void {
|
|
|
48
48
|
}
|
|
49
49
|
|
|
50
50
|
export function restoreTextSelection(target?: Element): void {
|
|
51
|
-
if (isIOS()) {
|
|
51
|
+
if (isIOS() && isWebKit()) {
|
|
52
52
|
// If the state is already default, there's nothing to do.
|
|
53
53
|
// If it is restoring, then there's no need to queue a second restore.
|
|
54
54
|
if (state !== 'disabled') {
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
/** @jsxImportSource octane */
|
|
2
|
+
// Ported from adobe/react-spectrum@5ecb3333001313e83898cd07644227897e3bae1f (packages/react-aria/src/interactions/useContextMenu.ts).
|
|
3
|
+
/*
|
|
4
|
+
* Copyright 2020 Adobe. All rights reserved.
|
|
5
|
+
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License. You may obtain a copy
|
|
7
|
+
* of the License at http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
*
|
|
9
|
+
* Unless required by applicable law or agreed to in writing, software distributed under
|
|
10
|
+
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
|
|
11
|
+
* OF ANY KIND, either express or implied. See the License for the specific language
|
|
12
|
+
* governing permissions and limitations under the License.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { HTMLAttributes, useRef } from '../compat/react';
|
|
16
|
+
import { isIOS, isMac } from '../utils/platform';
|
|
17
|
+
import { mergeProps } from '../utils/mergeProps';
|
|
18
|
+
import { useLongPress } from './useLongPress';
|
|
19
|
+
|
|
20
|
+
export interface ContextMenuEvent {
|
|
21
|
+
/** The target element on which the event was triggered. */
|
|
22
|
+
target: Element;
|
|
23
|
+
/** X position relative to the target. */
|
|
24
|
+
x: number;
|
|
25
|
+
/** Y position relative to the target. */
|
|
26
|
+
y: number;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface ContextMenuProps {
|
|
30
|
+
/** Event that is called when a context menu is triggered. */
|
|
31
|
+
onContextMenu?: (e: ContextMenuEvent) => void;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface ContextMenuAria {
|
|
35
|
+
/** Props to spread on the target element. */
|
|
36
|
+
contextMenuProps: HTMLAttributes<HTMLElement>;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Handles context menu events across mouse, touch, keyboard, and screen reader interactions.
|
|
41
|
+
*/
|
|
42
|
+
export function useContextMenu(props: ContextMenuProps): ContextMenuAria {
|
|
43
|
+
// How to trigger context menu events on various platforms:
|
|
44
|
+
// - macOS
|
|
45
|
+
// - Mouse right click
|
|
46
|
+
// - Control + click
|
|
47
|
+
// - Control + Enter (does not fire the contextmenu event in certain WebKit / Chrome versions - https://bugs.webkit.org/show_bug.cgi?id=302049, https://issues.chromium.org/issues/369897039)
|
|
48
|
+
// - Control + Option + Shift + M with VoiceOver
|
|
49
|
+
// - Windows / Linux
|
|
50
|
+
// - Mouse right click
|
|
51
|
+
// - Shift + F10
|
|
52
|
+
// - Long press on a touch screen
|
|
53
|
+
// - iOS
|
|
54
|
+
// - Long press (does not fire contextmenu event - https://bugs.webkit.org/show_bug.cgi?id=213953)
|
|
55
|
+
// - Android
|
|
56
|
+
// - Long press
|
|
57
|
+
|
|
58
|
+
let { onContextMenu } = props;
|
|
59
|
+
let firedContextMenuEvent = useRef(false);
|
|
60
|
+
|
|
61
|
+
// iOS does not fire the contextmenu event, so use long press.
|
|
62
|
+
let { longPressProps } = useLongPress({
|
|
63
|
+
onLongPressStart() {
|
|
64
|
+
firedContextMenuEvent.current = false;
|
|
65
|
+
},
|
|
66
|
+
onLongPress(e) {
|
|
67
|
+
if (!firedContextMenuEvent.current) {
|
|
68
|
+
onContextMenu?.({ target: e.target, x: e.x, y: e.y });
|
|
69
|
+
} else {
|
|
70
|
+
firedContextMenuEvent.current = false;
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
if (!onContextMenu) {
|
|
76
|
+
return {
|
|
77
|
+
contextMenuProps: {},
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return {
|
|
82
|
+
// oxlint-disable-next-line react/react-compiler - it says we are reading a ref during render but that's not true...
|
|
83
|
+
contextMenuProps: mergeProps(isIOS() ? longPressProps : {}, {
|
|
84
|
+
onContextMenu(e: import('octane').MouseEvent<HTMLElement>) {
|
|
85
|
+
e.stopPropagation();
|
|
86
|
+
e.preventDefault();
|
|
87
|
+
firedContextMenuEvent.current = true;
|
|
88
|
+
|
|
89
|
+
let rect = e.currentTarget.getBoundingClientRect();
|
|
90
|
+
onContextMenu({
|
|
91
|
+
target: e.currentTarget,
|
|
92
|
+
x: e.clientX - rect.x,
|
|
93
|
+
y: e.clientY - rect.y,
|
|
94
|
+
});
|
|
95
|
+
},
|
|
96
|
+
onKeyDown(e: import('octane').KeyboardEvent<HTMLElement>) {
|
|
97
|
+
// macOS has a default keyboard shortcut to show the contextmenu: Ctrl + Enter.
|
|
98
|
+
// However, some versions of Safari and Chrome do not trigger the contextmenu event.
|
|
99
|
+
// Fixed in https://github.com/WebKit/WebKit/pull/62278 (currently in WekKit nightly) and
|
|
100
|
+
// https://github.com/chromium/chromium/commit/268c876c191cd4712c2d1043aab9760fb71d9be5 (Chrome 147).
|
|
101
|
+
// Remove this workaround once those are broadly available.
|
|
102
|
+
// An additional bug still occurs when the target has a border-radius: https://bugs.webkit.org/show_bug.cgi?id=317496
|
|
103
|
+
if (isMac()) {
|
|
104
|
+
if (e.ctrlKey && e.key === 'Enter') {
|
|
105
|
+
firedContextMenuEvent.current = false;
|
|
106
|
+
let target = e.currentTarget;
|
|
107
|
+
e.stopPropagation();
|
|
108
|
+
setTimeout(() => {
|
|
109
|
+
if (!firedContextMenuEvent.current) {
|
|
110
|
+
let rect = target.getBoundingClientRect();
|
|
111
|
+
onContextMenu({
|
|
112
|
+
target,
|
|
113
|
+
x: rect.width / 2,
|
|
114
|
+
y: rect.height / 2,
|
|
115
|
+
});
|
|
116
|
+
} else {
|
|
117
|
+
firedContextMenuEvent.current = false;
|
|
118
|
+
}
|
|
119
|
+
}, 10);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
},
|
|
123
|
+
} satisfies HTMLAttributes<HTMLElement>),
|
|
124
|
+
};
|
|
125
|
+
}
|
|
@@ -110,8 +110,8 @@ function handleFocusEvent(e: FocusEvent) {
|
|
|
110
110
|
// Firefox fires two extra focus events when the user first clicks into an iframe:
|
|
111
111
|
// first on the window, then on the document. We ignore these events so they don't
|
|
112
112
|
// cause keyboard focus rings to appear.
|
|
113
|
-
let ownerWindow = getOwnerWindow(getEventTarget(e)
|
|
114
|
-
let ownerDocument = getOwnerDocument(getEventTarget(e)
|
|
113
|
+
let ownerWindow = getOwnerWindow(getEventTarget(e));
|
|
114
|
+
let ownerDocument = getOwnerDocument(getEventTarget(e));
|
|
115
115
|
if (
|
|
116
116
|
getEventTarget(e) === ownerWindow ||
|
|
117
117
|
getEventTarget(e) === ownerDocument ||
|
|
@@ -352,7 +352,7 @@ const nonTextInputTypes = new Set([
|
|
|
352
352
|
*/
|
|
353
353
|
function isKeyboardFocusEvent(isTextInput: boolean, modality: Modality, e: HandlerEvent) {
|
|
354
354
|
let eventTarget = e ? (getEventTarget(e) as Element) : undefined;
|
|
355
|
-
let
|
|
355
|
+
let ownerDocument = getOwnerDocument(eventTarget);
|
|
356
356
|
let ownerWindow = getOwnerWindow(eventTarget);
|
|
357
357
|
const IHTMLInputElement =
|
|
358
358
|
typeof ownerWindow !== 'undefined' ? ownerWindow.HTMLInputElement : HTMLInputElement;
|
|
@@ -364,7 +364,7 @@ function isKeyboardFocusEvent(isTextInput: boolean, modality: Modality, e: Handl
|
|
|
364
364
|
|
|
365
365
|
// For keyboard events that occur on a non-input element that will move focus into input element (aka ArrowLeft going from Datepicker button to the main input group)
|
|
366
366
|
// we need to rely on the user passing isTextInput into here. This way we can skip toggling focus visiblity for said input element
|
|
367
|
-
let activeElement = getActiveElement(
|
|
367
|
+
let activeElement = getActiveElement(ownerDocument);
|
|
368
368
|
isTextInput =
|
|
369
369
|
isTextInput ||
|
|
370
370
|
(activeElement instanceof IHTMLInputElement && !nonTextInputTypes.has(activeElement.type)) ||
|
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
import { chain } from '../utils/chain';
|
|
2
|
+
import {
|
|
3
|
+
createKeyboardShortcutHandler,
|
|
4
|
+
type KeyboardShortcutBindings,
|
|
5
|
+
} from './createKeyboardShortcutHandler';
|
|
6
|
+
import { getEventTarget, nodeContains } from '../utils/shadowdom/DOMFunctions';
|
|
1
7
|
// Ported from react-aria (source: .react-spectrum/packages/react-aria/src/interactions/useKeyboard.ts).
|
|
2
8
|
// octane adaptations:
|
|
3
9
|
// - `KeyboardEvents` / `DOMAttributes` from '@react-types/shared' are typed over React's
|
|
@@ -20,6 +26,9 @@ export interface KeyboardEvents {
|
|
|
20
26
|
export type DOMAttributes = Record<string, any>;
|
|
21
27
|
|
|
22
28
|
export interface KeyboardProps extends KeyboardEvents {
|
|
29
|
+
shortcuts?: KeyboardShortcutBindings;
|
|
30
|
+
allowRepeats?: boolean;
|
|
31
|
+
allowComposing?: boolean;
|
|
23
32
|
/** Whether the keyboard events should be disabled. */
|
|
24
33
|
isDisabled?: boolean;
|
|
25
34
|
}
|
|
@@ -39,12 +48,51 @@ export function useKeyboard(...args: any[]): KeyboardResult {
|
|
|
39
48
|
const [user] = splitSlot(args);
|
|
40
49
|
const props = user[0] as KeyboardProps;
|
|
41
50
|
|
|
51
|
+
let { shortcuts, allowRepeats = false, allowComposing = false } = props;
|
|
52
|
+
let onKeyDown;
|
|
53
|
+
let onKeyUp;
|
|
54
|
+
if (shortcuts) {
|
|
55
|
+
let shortcutHandler = createKeyboardShortcutHandler(shortcuts);
|
|
56
|
+
let shortcutOnKeyDown = createEventHandler<KeyboardEvent>((e) => {
|
|
57
|
+
// If keyboard event didn't originate from a child of the current target,
|
|
58
|
+
// then it's a React event coming through a portal. We should ignore it.
|
|
59
|
+
if (!nodeContains(e.currentTarget as Node, getEventTarget(e) as Element)) {
|
|
60
|
+
e.continuePropagation();
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
if ((e.repeat && !allowRepeats) || (e.isComposing && !allowComposing)) {
|
|
64
|
+
e.continuePropagation();
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
shortcutHandler(e);
|
|
69
|
+
});
|
|
70
|
+
let shortcutOnKeyUp = createEventHandler<KeyboardEvent>((e) => {
|
|
71
|
+
// If keyboard event didn't originate from a child of the current target,
|
|
72
|
+
// then it's a React event coming through a portal. We should ignore it.
|
|
73
|
+
if (!nodeContains(e.currentTarget as Node, getEventTarget(e) as Element)) {
|
|
74
|
+
e.continuePropagation();
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
if ((e.repeat && !allowRepeats) || (e.isComposing && !allowComposing)) {
|
|
78
|
+
e.continuePropagation();
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
// implement shortcut handler on keyup, what should the map be called? or should it be another syntax on shortcuts?
|
|
82
|
+
e.continuePropagation();
|
|
83
|
+
});
|
|
84
|
+
onKeyDown = props.onKeyDown ? chain(props.onKeyDown, shortcutOnKeyDown) : shortcutOnKeyDown;
|
|
85
|
+
onKeyUp = props.onKeyUp ? chain(props.onKeyUp, shortcutOnKeyUp) : shortcutOnKeyUp;
|
|
86
|
+
} else {
|
|
87
|
+
onKeyDown = createEventHandler(props.onKeyDown);
|
|
88
|
+
onKeyUp = createEventHandler(props.onKeyUp);
|
|
89
|
+
}
|
|
42
90
|
return {
|
|
43
91
|
keyboardProps: props.isDisabled
|
|
44
92
|
? {}
|
|
45
93
|
: {
|
|
46
|
-
onKeyDown
|
|
47
|
-
onKeyUp
|
|
94
|
+
onKeyDown,
|
|
95
|
+
onKeyUp,
|
|
48
96
|
},
|
|
49
97
|
};
|
|
50
98
|
}
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
// synthetic handlers); the `contextmenu` listener param carries an explicit native
|
|
5
5
|
// annotation (upstream got it contextually).
|
|
6
6
|
// - Public-hook slot threading (splitSlot/subSlot) per the binding convention.
|
|
7
|
-
import type { FocusableElement, LongPressEvent } from '@react-types/shared';
|
|
7
|
+
import type { FocusableElement, LongPressEvent, PressEvent } from '@react-types/shared';
|
|
8
8
|
import { focusWithoutScrolling } from '../utils/focusWithoutScrolling';
|
|
9
9
|
import { getOwnerDocument, getOwnerWindow } from '../utils/domHelpers';
|
|
10
10
|
import { mergeProps } from '../utils/mergeProps';
|
|
@@ -21,6 +21,8 @@ type DOMAttributes = Record<string, any>;
|
|
|
21
21
|
export interface LongPressProps {
|
|
22
22
|
/** Whether long press events should be disabled. */
|
|
23
23
|
isDisabled?: boolean;
|
|
24
|
+
/** Which pointer type to listen for. By default, both mouse and touch are listened for. */
|
|
25
|
+
pointerType?: 'mouse' | 'touch';
|
|
24
26
|
/** Handler that is called when a long press interaction starts. */
|
|
25
27
|
onLongPressStart?: (e: LongPressEvent) => void;
|
|
26
28
|
/**
|
|
@@ -65,6 +67,7 @@ export function useLongPress(...args: any[]): LongPressResult {
|
|
|
65
67
|
|
|
66
68
|
let {
|
|
67
69
|
isDisabled,
|
|
70
|
+
pointerType,
|
|
68
71
|
onLongPressStart,
|
|
69
72
|
onLongPressEnd,
|
|
70
73
|
onLongPress,
|
|
@@ -76,14 +79,20 @@ export function useLongPress(...args: any[]): LongPressResult {
|
|
|
76
79
|
undefined,
|
|
77
80
|
subSlot(slot, 'timer'),
|
|
78
81
|
);
|
|
79
|
-
let { addGlobalListener,
|
|
82
|
+
let { addGlobalListener, removeAllGlobalListeners } = useGlobalListeners(
|
|
83
|
+
subSlot(slot, 'listeners'),
|
|
84
|
+
);
|
|
85
|
+
let isAcceptedPointerType = (e: PressEvent) =>
|
|
86
|
+
pointerType
|
|
87
|
+
? e.pointerType === pointerType
|
|
88
|
+
: e.pointerType === 'mouse' || e.pointerType === 'touch';
|
|
80
89
|
|
|
81
90
|
let { pressProps } = usePress(
|
|
82
91
|
{
|
|
83
92
|
isDisabled,
|
|
84
93
|
onPressStart(e) {
|
|
85
94
|
e.continuePropagation();
|
|
86
|
-
if (e
|
|
95
|
+
if (isAcceptedPointerType(e)) {
|
|
87
96
|
if (onLongPressStart) {
|
|
88
97
|
onLongPressStart({
|
|
89
98
|
...e,
|
|
@@ -95,6 +104,9 @@ export function useLongPress(...args: any[]): LongPressResult {
|
|
|
95
104
|
// Prevent other usePress handlers from also handling this event.
|
|
96
105
|
e.target.dispatchEvent(new PointerEvent('pointercancel', { bubbles: true }));
|
|
97
106
|
|
|
107
|
+
// Prevent default click action (e.g. opening a link) after a long press.
|
|
108
|
+
addGlobalListener(e.target, 'click', (e) => e.preventDefault(), { once: true });
|
|
109
|
+
|
|
98
110
|
// Ensure target is focused. On touch devices, browsers typically focus on pointer up.
|
|
99
111
|
if (getOwnerDocument(e.target).activeElement !== e.target) {
|
|
100
112
|
focusWithoutScrolling(e.target as FocusableElement);
|
|
@@ -111,25 +123,24 @@ export function useLongPress(...args: any[]): LongPressResult {
|
|
|
111
123
|
|
|
112
124
|
// Prevent context menu, which may be opened on long press on touch devices
|
|
113
125
|
if (e.pointerType === 'touch') {
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
};
|
|
117
|
-
|
|
118
|
-
let ownerWindow = getOwnerWindow(e.target);
|
|
119
|
-
addGlobalListener(e.target, 'contextmenu', onContextMenu, { once: true });
|
|
120
|
-
addGlobalListener(
|
|
121
|
-
ownerWindow,
|
|
122
|
-
'pointerup',
|
|
123
|
-
() => {
|
|
124
|
-
// If no contextmenu event is fired quickly after pointerup, remove the handler
|
|
125
|
-
// so future context menu events outside a long press are not prevented.
|
|
126
|
-
setTimeout(() => {
|
|
127
|
-
removeGlobalListener(e.target, 'contextmenu', onContextMenu);
|
|
128
|
-
}, 30);
|
|
129
|
-
},
|
|
130
|
-
{ once: true },
|
|
131
|
-
);
|
|
126
|
+
addGlobalListener(e.target, 'contextmenu', (e: Event) => e.preventDefault(), {
|
|
127
|
+
once: true,
|
|
128
|
+
});
|
|
132
129
|
}
|
|
130
|
+
|
|
131
|
+
let ownerWindow = getOwnerWindow(e.target);
|
|
132
|
+
addGlobalListener(
|
|
133
|
+
ownerWindow,
|
|
134
|
+
'pointerup',
|
|
135
|
+
() => {
|
|
136
|
+
// If no contextmenu event is fired quickly after pointerup, remove the handler
|
|
137
|
+
// so future context menu events outside a long press are not prevented.
|
|
138
|
+
setTimeout(() => {
|
|
139
|
+
removeAllGlobalListeners();
|
|
140
|
+
}, 100);
|
|
141
|
+
},
|
|
142
|
+
{ once: true },
|
|
143
|
+
);
|
|
133
144
|
}
|
|
134
145
|
},
|
|
135
146
|
onPressEnd(e) {
|
|
@@ -137,7 +148,7 @@ export function useLongPress(...args: any[]): LongPressResult {
|
|
|
137
148
|
clearTimeout(timeRef.current);
|
|
138
149
|
}
|
|
139
150
|
|
|
140
|
-
if (onLongPressEnd && (e
|
|
151
|
+
if (onLongPressEnd && isAcceptedPointerType(e)) {
|
|
141
152
|
onLongPressEnd({
|
|
142
153
|
...e,
|
|
143
154
|
type: 'longpressend',
|
|
@@ -130,7 +130,7 @@ export function useMove(...args: any[]): MoveResult {
|
|
|
130
130
|
let onMouseUp = (e: MouseEvent) => {
|
|
131
131
|
if (e.button === 0) {
|
|
132
132
|
endEvent(e, 'mouse');
|
|
133
|
-
let ownerWindow = getOwnerWindow(getEventTarget(e)
|
|
133
|
+
let ownerWindow = getOwnerWindow(getEventTarget(e));
|
|
134
134
|
removeGlobalListener(ownerWindow, 'mousemove', onMouseMove, false);
|
|
135
135
|
removeGlobalListener(ownerWindow, 'mouseup', onMouseUp, false);
|
|
136
136
|
}
|
|
@@ -141,7 +141,7 @@ export function useMove(...args: any[]): MoveResult {
|
|
|
141
141
|
e.stopPropagation();
|
|
142
142
|
e.preventDefault();
|
|
143
143
|
state.current.lastPosition = { pageX: e.pageX, pageY: e.pageY };
|
|
144
|
-
let ownerWindow = getOwnerWindow(getEventTarget(e)
|
|
144
|
+
let ownerWindow = getOwnerWindow(getEventTarget(e));
|
|
145
145
|
addGlobalListener(ownerWindow, 'mousemove', onMouseMove, false);
|
|
146
146
|
addGlobalListener(ownerWindow, 'mouseup', onMouseUp, false);
|
|
147
147
|
}
|
|
@@ -169,7 +169,7 @@ export function useMove(...args: any[]): MoveResult {
|
|
|
169
169
|
if (touch >= 0) {
|
|
170
170
|
endEvent(e, 'touch');
|
|
171
171
|
state.current.id = null;
|
|
172
|
-
let ownerWindow = getOwnerWindow(getEventTarget(e)
|
|
172
|
+
let ownerWindow = getOwnerWindow(getEventTarget(e));
|
|
173
173
|
removeGlobalListener(ownerWindow, 'touchmove', onTouchMove);
|
|
174
174
|
removeGlobalListener(ownerWindow, 'touchend', onTouchEnd);
|
|
175
175
|
removeGlobalListener(ownerWindow, 'touchcancel', onTouchEnd);
|
|
@@ -186,7 +186,7 @@ export function useMove(...args: any[]): MoveResult {
|
|
|
186
186
|
e.preventDefault();
|
|
187
187
|
state.current.lastPosition = { pageX, pageY };
|
|
188
188
|
state.current.id = identifier;
|
|
189
|
-
let ownerWindow = getOwnerWindow(getEventTarget(e)
|
|
189
|
+
let ownerWindow = getOwnerWindow(getEventTarget(e));
|
|
190
190
|
addGlobalListener(ownerWindow, 'touchmove', onTouchMove, false);
|
|
191
191
|
addGlobalListener(ownerWindow, 'touchend', onTouchEnd, false);
|
|
192
192
|
addGlobalListener(ownerWindow, 'touchcancel', onTouchEnd, false);
|
|
@@ -214,7 +214,7 @@ export function useMove(...args: any[]): MoveResult {
|
|
|
214
214
|
let pointerType = (e.pointerType || 'mouse') as PointerType;
|
|
215
215
|
endEvent(e, pointerType);
|
|
216
216
|
state.current.id = null;
|
|
217
|
-
let ownerWindow = getOwnerWindow(getEventTarget(e)
|
|
217
|
+
let ownerWindow = getOwnerWindow(getEventTarget(e));
|
|
218
218
|
removeGlobalListener(ownerWindow, 'pointermove', onPointerMove, false);
|
|
219
219
|
removeGlobalListener(ownerWindow, 'pointerup', onPointerUp, false);
|
|
220
220
|
removeGlobalListener(ownerWindow, 'pointercancel', onPointerUp, false);
|
|
@@ -228,7 +228,7 @@ export function useMove(...args: any[]): MoveResult {
|
|
|
228
228
|
e.preventDefault();
|
|
229
229
|
state.current.lastPosition = { pageX: e.pageX, pageY: e.pageY };
|
|
230
230
|
state.current.id = e.pointerId;
|
|
231
|
-
let ownerWindow = getOwnerWindow(getEventTarget(e)
|
|
231
|
+
let ownerWindow = getOwnerWindow(getEventTarget(e));
|
|
232
232
|
addGlobalListener(ownerWindow, 'pointermove', onPointerMove, false);
|
|
233
233
|
addGlobalListener(ownerWindow, 'pointerup', onPointerUp, false);
|
|
234
234
|
addGlobalListener(ownerWindow, 'pointercancel', onPointerUp, false);
|
|
@@ -1222,7 +1222,15 @@ function shouldPreventDefaultUp(target: Element) {
|
|
|
1222
1222
|
}
|
|
1223
1223
|
|
|
1224
1224
|
function shouldPreventDefaultKeyboard(target: Element, key: string) {
|
|
1225
|
+
if (isMac() && key === 'Enter') {
|
|
1226
|
+
return false;
|
|
1227
|
+
}
|
|
1228
|
+
|
|
1225
1229
|
if (target instanceof HTMLInputElement) {
|
|
1230
|
+
if (key === 'Enter' && (target.type === 'checkbox' || target.type === 'radio')) {
|
|
1231
|
+
// Enter on a checkbox or radio should do an implicit form submission, but not toggle the input.
|
|
1232
|
+
return false;
|
|
1233
|
+
}
|
|
1226
1234
|
return !isValidInputKey(target, key);
|
|
1227
1235
|
}
|
|
1228
1236
|
|