@octanejs/aria 0.0.46 → 0.0.48
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} +243 -258
- 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} +128 -66
- package/src/grid/utils.ts +1 -0
- package/src/gridlist/{useGridListItem.ts → useGridListItem.tsx} +154 -112
- 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 +222 -0
- package/src/menu/{useSubmenuTrigger.ts → useSubmenuTrigger.tsx} +116 -151
- package/src/numberfield/{useNumberField.ts → useNumberField.tsx} +188 -238
- 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} +76 -83
- 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} +87 -79
- package/src/searchfield/useSearchField.tsx +152 -0
- package/src/select/useSelect.ts +20 -20
- package/src/selection/useSelectableCollection.tsx +755 -0
- package/src/selection/useSelectableItem.ts +48 -11
- package/src/slider/{useSliderThumb.ts → useSliderThumb.tsx} +120 -158
- package/src/spinbutton/useSpinButton.tsx +335 -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 +359 -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 +4 -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,3 +1,4 @@
|
|
|
1
|
+
import type { FocusableElement } from '@react-types/shared';
|
|
1
2
|
// Ported from react-aria-components (source: .react-spectrum/packages/react-aria-components/src/ComboBox.tsx).
|
|
2
3
|
// octane adaptations: `.tsx` → `.ts`, JSX → `createElement`; NO forwardRef —
|
|
3
4
|
// `createHideableComponent` forwards `props.ref` positionally exactly like TextField; the
|
|
@@ -34,6 +35,7 @@ import { useResizeObserver } from '../utils/useResizeObserver';
|
|
|
34
35
|
import { ButtonContext } from './Button';
|
|
35
36
|
import { OverlayTriggerStateContext } from './Dialog';
|
|
36
37
|
import { FieldErrorContext } from './FieldError';
|
|
38
|
+
import { FieldInputContext } from './Autocomplete';
|
|
37
39
|
import { FormContext } from './Form';
|
|
38
40
|
import { GroupContext } from './Group';
|
|
39
41
|
import { InputContext } from './Input';
|
|
@@ -186,7 +188,14 @@ export const ComboBox: <T, M extends SelectionMode = 'single'>(
|
|
|
186
188
|
}) as any;
|
|
187
189
|
|
|
188
190
|
// Contexts to clear inside the popover.
|
|
189
|
-
const CLEAR_CONTEXTS = [
|
|
191
|
+
const CLEAR_CONTEXTS = [
|
|
192
|
+
LabelContext,
|
|
193
|
+
ButtonContext,
|
|
194
|
+
InputContext,
|
|
195
|
+
FieldInputContext,
|
|
196
|
+
GroupContext,
|
|
197
|
+
TextContext,
|
|
198
|
+
];
|
|
190
199
|
|
|
191
200
|
interface ComboBoxInnerProps<T> {
|
|
192
201
|
props: ComboBoxProps<T, SelectionMode>;
|
|
@@ -226,6 +235,11 @@ function ComboBoxInner<T>({ props, collection, comboBoxRef: ref }: ComboBoxInner
|
|
|
226
235
|
!props['aria-label'] && !props['aria-labelledby'],
|
|
227
236
|
subSlot(slot, 'labelSlot'),
|
|
228
237
|
);
|
|
238
|
+
let [labelElementType, setLabelElementType] = useState<'span' | 'label'>(
|
|
239
|
+
'label',
|
|
240
|
+
subSlot(slot, 'labelElementType'),
|
|
241
|
+
);
|
|
242
|
+
|
|
229
243
|
let {
|
|
230
244
|
buttonProps,
|
|
231
245
|
inputProps,
|
|
@@ -242,6 +256,7 @@ function ComboBoxInner<T>({ props, collection, comboBoxRef: ref }: ComboBoxInner
|
|
|
242
256
|
inputRef,
|
|
243
257
|
buttonRef,
|
|
244
258
|
listBoxRef,
|
|
259
|
+
labelElementType,
|
|
245
260
|
popoverRef,
|
|
246
261
|
name: formValue === 'text' ? name : undefined,
|
|
247
262
|
validationBehavior,
|
|
@@ -334,9 +349,27 @@ function ComboBoxInner<T>({ props, collection, comboBoxRef: ref }: ComboBoxInner
|
|
|
334
349
|
return createElement(Provider, {
|
|
335
350
|
values: [
|
|
336
351
|
[ComboBoxStateContext, state],
|
|
337
|
-
[LabelContext, { ...labelProps, ref: labelRef }],
|
|
352
|
+
[LabelContext, { ...labelProps, elementType: labelElementType, ref: labelRef }],
|
|
338
353
|
[ButtonContext, { ...buttonProps, ref: buttonRef, isPressed: state.isOpen }],
|
|
339
354
|
[InputContext, { ...inputProps, ref: inputRef }],
|
|
355
|
+
[
|
|
356
|
+
FieldInputContext,
|
|
357
|
+
{
|
|
358
|
+
...inputProps,
|
|
359
|
+
ref: useCallback(
|
|
360
|
+
(el: FocusableElement) => {
|
|
361
|
+
inputRef.current = el as HTMLInputElement; // TODO: figure out how to fix non-input element types in useComboBox/useTextField
|
|
362
|
+
if (el) {
|
|
363
|
+
setLabelElementType(el.tagName.toLowerCase() === 'input' ? 'label' : 'span');
|
|
364
|
+
}
|
|
365
|
+
},
|
|
366
|
+
[],
|
|
367
|
+
subSlot(slot, 'fieldInputRef'),
|
|
368
|
+
),
|
|
369
|
+
value: state.inputValue,
|
|
370
|
+
onChange: (v: string) => state.setInputValue(v as string),
|
|
371
|
+
} as any,
|
|
372
|
+
],
|
|
340
373
|
[OverlayTriggerStateContext, state],
|
|
341
374
|
[
|
|
342
375
|
PopoverContext,
|
package/src/components/Dialog.ts
CHANGED
|
@@ -19,6 +19,7 @@ import type {
|
|
|
19
19
|
import { PopoverContext } from './Popover';
|
|
20
20
|
import { PressResponder } from '../interactions/PressResponder';
|
|
21
21
|
import { RootMenuTriggerStateContext } from './Menu';
|
|
22
|
+
import { TextContext } from './Text';
|
|
22
23
|
import { useId } from '../utils/useId';
|
|
23
24
|
import { useMenuTriggerState } from '../stately/menu/useMenuTriggerState';
|
|
24
25
|
import { useOverlayTrigger } from '../overlays/useOverlayTrigger';
|
|
@@ -101,6 +102,7 @@ export function DialogTrigger(props: DialogTriggerProps): any {
|
|
|
101
102
|
{
|
|
102
103
|
trigger: 'DialogTrigger',
|
|
103
104
|
triggerRef: buttonRef,
|
|
105
|
+
id: overlayProps.id,
|
|
104
106
|
'aria-labelledby': (overlayProps as any)['aria-labelledby'],
|
|
105
107
|
},
|
|
106
108
|
],
|
|
@@ -122,7 +124,7 @@ export function Dialog(props: DialogProps): any {
|
|
|
122
124
|
let originalAriaLabelledby = props['aria-labelledby'];
|
|
123
125
|
let ref: any;
|
|
124
126
|
[props, ref] = useContextProps(props, (props as any).ref, DialogContext, subSlot(slot, 'ctx'));
|
|
125
|
-
let { dialogProps, titleProps } = useDialog(
|
|
127
|
+
let { dialogProps, titleProps, contentProps } = useDialog(
|
|
126
128
|
{
|
|
127
129
|
...props,
|
|
128
130
|
// Only pass aria-labelledby from props, not context.
|
|
@@ -177,6 +179,15 @@ export function Dialog(props: DialogProps): any {
|
|
|
177
179
|
},
|
|
178
180
|
},
|
|
179
181
|
],
|
|
182
|
+
[
|
|
183
|
+
TextContext,
|
|
184
|
+
{
|
|
185
|
+
slots: {
|
|
186
|
+
[DEFAULT_SLOT]: {},
|
|
187
|
+
description: contentProps,
|
|
188
|
+
},
|
|
189
|
+
},
|
|
190
|
+
],
|
|
180
191
|
[
|
|
181
192
|
ButtonContext,
|
|
182
193
|
{
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
/** @jsxImportSource octane */
|
|
2
|
+
import { focusWithoutScrolling } from '../utils/focusWithoutScrolling';
|
|
2
3
|
// Ported from adobe/react-spectrum@1c84a49a1faf50b571c84e00bcf9c60b22ddd03e (packages/react-aria-components/src/DropZone.tsx).
|
|
3
4
|
/*
|
|
4
5
|
* Copyright 2023 Adobe. All rights reserved.
|
|
@@ -154,8 +155,8 @@ export const DropZone = forwardRef(function DropZone(
|
|
|
154
155
|
while (target && nodeContains(dropzoneRef.current, target)) {
|
|
155
156
|
if (isFocusable(target)) {
|
|
156
157
|
break;
|
|
157
|
-
} else if (target === dropzoneRef.current) {
|
|
158
|
-
buttonRef.current
|
|
158
|
+
} else if (target === dropzoneRef.current && buttonRef.current) {
|
|
159
|
+
focusWithoutScrolling(buttonRef.current);
|
|
159
160
|
break;
|
|
160
161
|
}
|
|
161
162
|
|
|
@@ -84,6 +84,7 @@ export const FileTrigger = forwardRef(function FileTrigger(
|
|
|
84
84
|
className=""
|
|
85
85
|
type="file"
|
|
86
86
|
ref={inputRef}
|
|
87
|
+
onClick={(e: MouseEvent) => e.stopPropagation()}
|
|
87
88
|
style={{ display: 'none' }}
|
|
88
89
|
accept={acceptedFileTypes?.toString()}
|
|
89
90
|
onChange={(e: React.ChangeEvent<HTMLInputElement>) => onSelect?.(e.currentTarget.files)}
|
|
@@ -554,6 +554,13 @@ export interface GridListItemProps<T = object>
|
|
|
554
554
|
* on the collection's `selectionBehavior` prop and the interaction modality.
|
|
555
555
|
*/
|
|
556
556
|
onAction?: () => void;
|
|
557
|
+
focusMode?: 'child' | 'row';
|
|
558
|
+
/**
|
|
559
|
+
* Whether the row should support arrow key navigation even when the containing collection uses
|
|
560
|
+
* tab keyboard navigation. Allows users to navigate between rows with arrow keys while
|
|
561
|
+
* focus is on an interactive child element within the row.
|
|
562
|
+
*/
|
|
563
|
+
allowsArrowNavigation?: boolean;
|
|
557
564
|
}
|
|
558
565
|
|
|
559
566
|
/**
|
|
@@ -577,6 +584,8 @@ export const GridListItem: <T extends object = object>(
|
|
|
577
584
|
let { rowProps, gridCellProps, descriptionProps, ...states } = useGridListItem(
|
|
578
585
|
{
|
|
579
586
|
node: item!,
|
|
587
|
+
focusMode: props.focusMode,
|
|
588
|
+
allowsArrowNavigation: props.allowsArrowNavigation,
|
|
580
589
|
shouldSelectOnPressUp: !!dragState,
|
|
581
590
|
isVirtualized,
|
|
582
591
|
},
|
|
@@ -902,7 +911,7 @@ function RootDropIndicator(): any {
|
|
|
902
911
|
|
|
903
912
|
export interface GridListLoadMoreItemProps
|
|
904
913
|
extends
|
|
905
|
-
Omit<LoadMoreSentinelProps, 'collection'>,
|
|
914
|
+
Omit<LoadMoreSentinelProps, 'collection' | 'direction'>,
|
|
906
915
|
StyleProps,
|
|
907
916
|
DOMRenderProps<'div', undefined>,
|
|
908
917
|
GlobalDOMAttributes {
|
|
@@ -945,7 +954,7 @@ export const GridListLoadMoreItem: (props: GridListLoadMoreItemProps & { ref?: a
|
|
|
945
954
|
sentinelRef,
|
|
946
955
|
scrollOffset,
|
|
947
956
|
}),
|
|
948
|
-
[onLoadMore, scrollOffset, state?.collection],
|
|
957
|
+
[onLoadMore, scrollOffset, sentinelRef, state?.collection],
|
|
949
958
|
subSlot(slot, 'loadMoreProps'),
|
|
950
959
|
);
|
|
951
960
|
useLoadMoreSentinel(
|
|
@@ -786,7 +786,7 @@ function ListBoxDropIndicator(props: ListBoxDropIndicatorProps): any {
|
|
|
786
786
|
|
|
787
787
|
export interface ListBoxLoadMoreItemProps
|
|
788
788
|
extends
|
|
789
|
-
Omit<LoadMoreSentinelProps, 'collection'>,
|
|
789
|
+
Omit<LoadMoreSentinelProps, 'collection' | 'direction'>,
|
|
790
790
|
StyleProps,
|
|
791
791
|
DOMRenderProps<'div', undefined>,
|
|
792
792
|
GlobalDOMAttributes {
|
package/src/components/Menu.ts
CHANGED
|
@@ -145,6 +145,7 @@ export function MenuTrigger(props: MenuTriggerProps): any {
|
|
|
145
145
|
triggerRef: ref,
|
|
146
146
|
scrollRef,
|
|
147
147
|
placement: 'bottom start',
|
|
148
|
+
offset: props.trigger === 'contextMenu' ? 0 : undefined,
|
|
148
149
|
'aria-labelledby': (menuProps as any)['aria-labelledby'],
|
|
149
150
|
},
|
|
150
151
|
],
|
package/src/components/Meter.ts
CHANGED
|
@@ -65,6 +65,8 @@ export function Meter(props: MeterProps): any {
|
|
|
65
65
|
let { value = 0, minValue = 0, maxValue = 100 } = props;
|
|
66
66
|
value = clamp(value, minValue, maxValue);
|
|
67
67
|
|
|
68
|
+
let range = maxValue - minValue;
|
|
69
|
+
|
|
68
70
|
let [labelRef, label] = useSlot(
|
|
69
71
|
!props['aria-label'] && !props['aria-labelledby'],
|
|
70
72
|
subSlot(slot, 'labelSlot'),
|
|
@@ -72,7 +74,7 @@ export function Meter(props: MeterProps): any {
|
|
|
72
74
|
let { meterProps, labelProps } = useMeter({ ...props, label }, subSlot(slot, 'meter'));
|
|
73
75
|
|
|
74
76
|
// Calculate the width of the progress bar as a percentage
|
|
75
|
-
let percentage = ((value - minValue) /
|
|
77
|
+
let percentage = range === 0 ? 0 : ((value - minValue) / range) * 100;
|
|
76
78
|
|
|
77
79
|
let renderProps = useRenderProps(
|
|
78
80
|
{
|
|
@@ -100,6 +100,8 @@ export interface PopoverProps
|
|
|
100
100
|
* @default document.body
|
|
101
101
|
*/
|
|
102
102
|
UNSTABLE_portalContainer?: Element;
|
|
103
|
+
/** Whether enter and exit animations should be skipped. */
|
|
104
|
+
shouldSkipAnimation?: boolean;
|
|
103
105
|
/**
|
|
104
106
|
* The additional offset applied along the main axis between the element and its
|
|
105
107
|
* anchor element.
|
|
@@ -138,6 +140,7 @@ export interface PopoverRenderProps {
|
|
|
138
140
|
|
|
139
141
|
interface PopoverContextValue extends PopoverProps {
|
|
140
142
|
/** Contexts to clear. */
|
|
143
|
+
id?: string;
|
|
141
144
|
clearContexts?: Context<any>[];
|
|
142
145
|
}
|
|
143
146
|
|
|
@@ -157,8 +160,8 @@ export function Popover(props: PopoverProps): any {
|
|
|
157
160
|
let localState = useOverlayTriggerState(props, subSlot(slot, 'state'));
|
|
158
161
|
let state =
|
|
159
162
|
props.isOpen != null || props.defaultOpen != null || !contextState ? localState : contextState;
|
|
160
|
-
let
|
|
161
|
-
|
|
163
|
+
let exitAnimation = useExitAnimation(ref, state.isOpen, subSlot(slot, 'exit'));
|
|
164
|
+
let isExiting = props.isExiting || (!props.shouldSkipAnimation && exitAnimation) || false;
|
|
162
165
|
let isHidden = useIsHidden();
|
|
163
166
|
let { direction } = useLocale(subSlot(slot, 'locale'));
|
|
164
167
|
|
|
@@ -198,10 +201,12 @@ interface PopoverInnerProps extends AriaPopoverProps, RenderProps<PopoverRenderP
|
|
|
198
201
|
state: OverlayTriggerState;
|
|
199
202
|
isEntering?: boolean;
|
|
200
203
|
isExiting: boolean;
|
|
204
|
+
shouldSkipAnimation?: boolean;
|
|
201
205
|
UNSTABLE_portalContainer?: Element;
|
|
202
206
|
trigger?: string;
|
|
203
207
|
dir?: 'ltr' | 'rtl';
|
|
204
208
|
clearContexts?: Context<any>[];
|
|
209
|
+
id?: string;
|
|
205
210
|
}
|
|
206
211
|
|
|
207
212
|
function PopoverInner(allProps: PopoverInnerProps): any {
|
|
@@ -229,8 +234,9 @@ function PopoverInner(allProps: PopoverInnerProps): any {
|
|
|
229
234
|
);
|
|
230
235
|
|
|
231
236
|
let ref = props.popoverRef as RefObject<HTMLDivElement | null>;
|
|
232
|
-
let
|
|
233
|
-
|
|
237
|
+
let enterAnimation = useEnterAnimation(ref, !!placement, subSlot(slot, 'enter'));
|
|
238
|
+
// oxlint-disable-next-line react/react-compiler
|
|
239
|
+
let isEntering = props.isEntering || (!props.shouldSkipAnimation && enterAnimation) || false;
|
|
234
240
|
let renderProps = useRenderProps(
|
|
235
241
|
{
|
|
236
242
|
...props,
|
|
@@ -247,8 +253,12 @@ function PopoverInner(allProps: PopoverInnerProps): any {
|
|
|
247
253
|
|
|
248
254
|
// Automatically render Popover with role=dialog except when isNonModal is true,
|
|
249
255
|
// or a dialog is already nested inside the popover.
|
|
250
|
-
let shouldBeDialog =
|
|
251
|
-
|
|
256
|
+
let shouldBeDialog =
|
|
257
|
+
!props.isNonModal || props.trigger === 'SubmenuTrigger' || props.trigger === 'PreviewTrigger';
|
|
258
|
+
let [isDialog, setDialog] = useState(
|
|
259
|
+
props.trigger === 'PreviewTrigger',
|
|
260
|
+
subSlot(slot, 'isDialog'),
|
|
261
|
+
);
|
|
252
262
|
useLayoutEffect(
|
|
253
263
|
() => {
|
|
254
264
|
if (ref.current) {
|
|
@@ -265,6 +275,7 @@ function PopoverInner(allProps: PopoverInnerProps): any {
|
|
|
265
275
|
() => {
|
|
266
276
|
if (
|
|
267
277
|
isDialog &&
|
|
278
|
+
props.trigger !== 'PreviewTrigger' &&
|
|
268
279
|
(props.trigger !== 'SubmenuTrigger' || getInteractionModality() !== 'pointer') &&
|
|
269
280
|
ref.current &&
|
|
270
281
|
!isFocusWithin(ref.current)
|
|
@@ -327,6 +338,7 @@ function PopoverInner(allProps: PopoverInnerProps): any {
|
|
|
327
338
|
{
|
|
328
339
|
...mergeProps(filterDOMProps(props, { global: true }), popoverProps),
|
|
329
340
|
...renderProps,
|
|
341
|
+
id: isDialog ? props.id : undefined,
|
|
330
342
|
role: isDialog ? 'dialog' : undefined,
|
|
331
343
|
tabIndex: isDialog ? -1 : undefined,
|
|
332
344
|
'aria-label': props['aria-label'],
|
|
@@ -354,7 +366,7 @@ function PopoverInner(allProps: PopoverInnerProps): any {
|
|
|
354
366
|
Overlay,
|
|
355
367
|
{
|
|
356
368
|
...props,
|
|
357
|
-
shouldContainFocus: isDialog,
|
|
369
|
+
shouldContainFocus: isDialog && props.trigger !== 'PreviewTrigger',
|
|
358
370
|
isExiting,
|
|
359
371
|
portalContainer: UNSTABLE_portalContainer,
|
|
360
372
|
// octane adaptation: children arrive positionally below.
|
|
@@ -380,7 +392,7 @@ function PopoverInner(allProps: PopoverInnerProps): any {
|
|
|
380
392
|
// Submenus/subdialogs are mounted into the root popover's container.
|
|
381
393
|
return createElement(Overlay, {
|
|
382
394
|
...props,
|
|
383
|
-
shouldContainFocus: isDialog,
|
|
395
|
+
shouldContainFocus: isDialog && props.trigger !== 'PreviewTrigger',
|
|
384
396
|
isExiting,
|
|
385
397
|
portalContainer: UNSTABLE_portalContainer ?? groupCtx?.current ?? undefined,
|
|
386
398
|
children: overlay,
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/** @jsxImportSource octane */
|
|
2
|
+
import type { OctaneNode } from 'octane';
|
|
3
|
+
// Ported from adobe/react-spectrum@5ecb3333001313e83898cd07644227897e3bae1f (packages/react-aria-components/src/PreviewTrigger.tsx).
|
|
4
|
+
/*
|
|
5
|
+
* Copyright 2026 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 {
|
|
17
|
+
AriaPreviewTriggerProps,
|
|
18
|
+
usePreviewTrigger,
|
|
19
|
+
} from '../upstream-exports/react-aria/usePreviewTrigger';
|
|
20
|
+
import { FocusableElement } from '@react-types/shared';
|
|
21
|
+
import { FocusableProvider } from '../interactions/useFocusable';
|
|
22
|
+
import { OverlayTriggerState } from '../upstream-exports/react-stately/useOverlayTriggerState';
|
|
23
|
+
import { OverlayTriggerStateContext } from './Dialog';
|
|
24
|
+
import { PopoverContext } from './Popover';
|
|
25
|
+
import { Provider } from './utils';
|
|
26
|
+
import React, { JSX, useMemo, useRef } from '../compat/react';
|
|
27
|
+
import { useTooltipTriggerState } from '../upstream-exports/react-stately/useTooltipTriggerState';
|
|
28
|
+
|
|
29
|
+
export interface PreviewTriggerProps extends AriaPreviewTriggerProps {
|
|
30
|
+
/** The trigger and Popover that make up the preview trigger. */
|
|
31
|
+
children: OctaneNode;
|
|
32
|
+
/**
|
|
33
|
+
* The delay time in milliseconds before the preview opens.
|
|
34
|
+
*
|
|
35
|
+
* @default 600
|
|
36
|
+
*/
|
|
37
|
+
delay?: number;
|
|
38
|
+
/**
|
|
39
|
+
* The delay time in milliseconds before the preview closes.
|
|
40
|
+
*
|
|
41
|
+
* @default 200
|
|
42
|
+
*/
|
|
43
|
+
closeDelay?: number;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* A PreviewTrigger displays a non-modal popover on hover, focus, or long press. Unlike a tooltip,
|
|
48
|
+
* the popover may contain interactive content.
|
|
49
|
+
*/
|
|
50
|
+
export function PreviewTrigger(props: PreviewTriggerProps): JSX.Element {
|
|
51
|
+
let state = useTooltipTriggerState({
|
|
52
|
+
...props,
|
|
53
|
+
delay: props.delay ?? 600,
|
|
54
|
+
closeDelay: props.closeDelay ?? 200,
|
|
55
|
+
});
|
|
56
|
+
let triggerRef = useRef<FocusableElement>(null);
|
|
57
|
+
let popoverRef = useRef<HTMLElement>(null);
|
|
58
|
+
let { triggerProps, popoverProps } = usePreviewTrigger(
|
|
59
|
+
{ ...props, triggerRef, popoverRef },
|
|
60
|
+
state,
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
// The Popover and usePopover expect an OverlayTriggerState. Adapt the TooltipTriggerState (which
|
|
64
|
+
// provides the warmup/cooldown delay behavior) to that interface.
|
|
65
|
+
let overlayState = useMemo<OverlayTriggerState>(
|
|
66
|
+
() => ({
|
|
67
|
+
isOpen: state.isOpen,
|
|
68
|
+
open: () => state.open(),
|
|
69
|
+
close: () => state.close(),
|
|
70
|
+
setOpen: (isOpen) => (isOpen ? state.open() : state.close()),
|
|
71
|
+
toggle: () => (state.isOpen ? state.close() : state.open()),
|
|
72
|
+
point: null,
|
|
73
|
+
setPoint: () => {},
|
|
74
|
+
}),
|
|
75
|
+
[state],
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
return (
|
|
79
|
+
<Provider
|
|
80
|
+
values={[
|
|
81
|
+
[OverlayTriggerStateContext, overlayState],
|
|
82
|
+
[
|
|
83
|
+
PopoverContext,
|
|
84
|
+
{
|
|
85
|
+
trigger: 'PreviewTrigger',
|
|
86
|
+
triggerRef,
|
|
87
|
+
ref: popoverRef,
|
|
88
|
+
isNonModal: true,
|
|
89
|
+
// Skip enter/exit animations when swapping between previews during the warmup period.
|
|
90
|
+
shouldSkipAnimation: state.shouldSkipAnimation,
|
|
91
|
+
...popoverProps,
|
|
92
|
+
},
|
|
93
|
+
],
|
|
94
|
+
]}
|
|
95
|
+
>
|
|
96
|
+
<FocusableProvider {...triggerProps} ref={triggerRef}>
|
|
97
|
+
{props.children}
|
|
98
|
+
</FocusableProvider>
|
|
99
|
+
</Provider>
|
|
100
|
+
);
|
|
101
|
+
}
|
|
@@ -83,8 +83,16 @@ export function ProgressBar(props: ProgressBarProps): any {
|
|
|
83
83
|
);
|
|
84
84
|
|
|
85
85
|
// Calculate the width of the progress bar as a percentage
|
|
86
|
-
let
|
|
87
|
-
|
|
86
|
+
let range = maxValue - minValue;
|
|
87
|
+
// Calculate the width of the progress bar as a percentage
|
|
88
|
+
let percentage: number | undefined = undefined;
|
|
89
|
+
if (!isIndeterminate) {
|
|
90
|
+
if (range === 0) {
|
|
91
|
+
percentage = 0;
|
|
92
|
+
} else {
|
|
93
|
+
percentage = ((value - minValue) / range) * 100;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
88
96
|
let renderProps = useRenderProps(
|
|
89
97
|
{
|
|
90
98
|
...props,
|
package/src/components/Select.ts
CHANGED
|
@@ -146,7 +146,7 @@ export interface SelectRenderProps {
|
|
|
146
146
|
isRequired: boolean;
|
|
147
147
|
}
|
|
148
148
|
|
|
149
|
-
export interface SelectProps<T, M extends SelectionMode = 'single'>
|
|
149
|
+
export interface SelectProps<T = {}, M extends SelectionMode = 'single'>
|
|
150
150
|
extends
|
|
151
151
|
Omit<
|
|
152
152
|
AriaSelectProps<T, M>,
|
package/src/components/Table.ts
CHANGED
|
@@ -1308,6 +1308,13 @@ export interface ColumnProps
|
|
|
1308
1308
|
* `<ResizableTableContainer>`.
|
|
1309
1309
|
*/
|
|
1310
1310
|
maxWidth?: ColumnStaticSize | null;
|
|
1311
|
+
focusMode?: 'child' | 'cell';
|
|
1312
|
+
/**
|
|
1313
|
+
* Whether the column should support arrow key navigation even when the containing table uses tab
|
|
1314
|
+
* keyboard navigation. Allows users to navigate between columns and rows with arrow keys while
|
|
1315
|
+
* focus is on an interactive child element within the column header.
|
|
1316
|
+
*/
|
|
1317
|
+
allowsArrowNavigation?: boolean;
|
|
1311
1318
|
}
|
|
1312
1319
|
|
|
1313
1320
|
class TableColumnNode extends CollectionNode<unknown> {
|
|
@@ -1337,7 +1344,12 @@ export const Column: (props: ColumnProps & { ref?: any }) => any =
|
|
|
1337
1344
|
let state = useContext(TableStateContext)!;
|
|
1338
1345
|
let { isVirtualized } = useContext(CollectionRendererContext);
|
|
1339
1346
|
let { columnHeaderProps, isPressed } = useTableColumnHeader(
|
|
1340
|
-
{
|
|
1347
|
+
{
|
|
1348
|
+
node: column as GridNode<unknown>,
|
|
1349
|
+
isVirtualized,
|
|
1350
|
+
focusMode: props.focusMode,
|
|
1351
|
+
allowsArrowNavigation: props.allowsArrowNavigation,
|
|
1352
|
+
},
|
|
1341
1353
|
state,
|
|
1342
1354
|
ref,
|
|
1343
1355
|
subSlot(slot, 'columnHeader'),
|
|
@@ -1500,7 +1512,7 @@ export function ColumnResizer(props: ColumnResizerProps & { ref?: any }): any {
|
|
|
1500
1512
|
let { onResizeStart, onResize, onResizeEnd } = useContext(ResizableTableContainerContext)!;
|
|
1501
1513
|
let { column, triggerRef } = useContext(ColumnResizerContext)!;
|
|
1502
1514
|
let inputRef = useRef<HTMLInputElement | null>(null, subSlot(slot, 'input'));
|
|
1503
|
-
let { resizerProps, inputProps, isResizing } = useTableColumnResize(
|
|
1515
|
+
let { resizerProps, inputProps, isResizing, isMouseResizing } = useTableColumnResize(
|
|
1504
1516
|
{
|
|
1505
1517
|
column,
|
|
1506
1518
|
'aria-label': props['aria-label'] || stringFormatter.format('tableResizer'),
|
|
@@ -1562,17 +1574,6 @@ export function ColumnResizer(props: ColumnResizerProps & { ref?: any }): any {
|
|
|
1562
1574
|
subSlot(slot, 'render'),
|
|
1563
1575
|
);
|
|
1564
1576
|
|
|
1565
|
-
let [isMouseDown, setMouseDown] = useState(false, subSlot(slot, 'mouseDown'));
|
|
1566
|
-
let onPointerDown = (e: PointerEvent) => {
|
|
1567
|
-
if (e.pointerType === 'mouse') {
|
|
1568
|
-
setMouseDown(true);
|
|
1569
|
-
}
|
|
1570
|
-
};
|
|
1571
|
-
|
|
1572
|
-
if (!isResizing && isMouseDown) {
|
|
1573
|
-
setMouseDown(false);
|
|
1574
|
-
}
|
|
1575
|
-
|
|
1576
1577
|
let DOMProps = filterDOMProps(props, { global: true });
|
|
1577
1578
|
|
|
1578
1579
|
// Positional (non-array) children: the resizer content, the visually hidden input, and
|
|
@@ -1582,7 +1583,7 @@ export function ColumnResizer(props: ColumnResizerProps & { ref?: any }): any {
|
|
|
1582
1583
|
{
|
|
1583
1584
|
ref: objectRef,
|
|
1584
1585
|
role: 'presentation',
|
|
1585
|
-
...mergeProps(DOMProps, renderProps, resizerProps,
|
|
1586
|
+
...mergeProps(DOMProps, renderProps, resizerProps, hoverProps),
|
|
1586
1587
|
'data-hovered': isHovered || undefined,
|
|
1587
1588
|
'data-focused': isFocused || undefined,
|
|
1588
1589
|
'data-focus-visible': isFocusVisible || undefined,
|
|
@@ -1591,9 +1592,10 @@ export function ColumnResizer(props: ColumnResizerProps & { ref?: any }): any {
|
|
|
1591
1592
|
},
|
|
1592
1593
|
renderProps.children,
|
|
1593
1594
|
createElement('input', { ref: inputRef, ...mergeProps(inputProps, focusProps) }),
|
|
1594
|
-
isResizing &&
|
|
1595
|
+
isResizing && isMouseResizing
|
|
1595
1596
|
? createPortal(
|
|
1596
1597
|
createElement('div', {
|
|
1598
|
+
'data-testid': 'cursor-overlay',
|
|
1597
1599
|
style: { position: 'fixed', top: 0, left: 0, bottom: 0, right: 0, cursor },
|
|
1598
1600
|
}),
|
|
1599
1601
|
document.body,
|
|
@@ -2250,6 +2252,13 @@ export interface CellProps extends RenderProps<CellRenderProps, 'td' | 'div'>, G
|
|
|
2250
2252
|
textValue?: string;
|
|
2251
2253
|
/** Indicates how many columns the data cell spans. */
|
|
2252
2254
|
colSpan?: number;
|
|
2255
|
+
focusMode?: 'child' | 'cell';
|
|
2256
|
+
/**
|
|
2257
|
+
* Whether the cell should support arrow key navigation even when the containing table uses
|
|
2258
|
+
* tab keyboard navigation. Allows users to navigate between cells and rows with arrow keys while
|
|
2259
|
+
* focus is on an interactive child element within the cell.
|
|
2260
|
+
*/
|
|
2261
|
+
allowsArrowNavigation?: boolean;
|
|
2253
2262
|
}
|
|
2254
2263
|
|
|
2255
2264
|
class TableCellNode extends CollectionNode<unknown> {
|
|
@@ -2285,6 +2294,8 @@ export const Cell: (props: CellProps & { ref?: any }) => any = /*#__PURE__*/ cre
|
|
|
2285
2294
|
let { gridCellProps, isPressed } = useTableCell(
|
|
2286
2295
|
{
|
|
2287
2296
|
node: cell,
|
|
2297
|
+
focusMode: props.focusMode,
|
|
2298
|
+
allowsArrowNavigation: props.allowsArrowNavigation,
|
|
2288
2299
|
shouldSelectOnPressUp: !!dragState,
|
|
2289
2300
|
isVirtualized,
|
|
2290
2301
|
},
|