@dxos/react-ui 0.8.4-main.5ea62a8 → 0.8.4-main.72ec0f3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/lib/browser/{chunk-P333G32W.mjs → chunk-53MI2QCM.mjs} +632 -299
- package/dist/lib/browser/chunk-53MI2QCM.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +12 -1
- package/dist/lib/browser/index.mjs.map +2 -2
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/testing/index.mjs +57 -29
- package/dist/lib/browser/testing/index.mjs.map +4 -4
- package/dist/lib/node-esm/{chunk-JRCE5UVS.mjs → chunk-ID67AFFF.mjs} +632 -299
- package/dist/lib/node-esm/chunk-ID67AFFF.mjs.map +7 -0
- package/dist/lib/node-esm/index.mjs +12 -1
- package/dist/lib/node-esm/index.mjs.map +2 -2
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/lib/node-esm/testing/index.mjs +57 -29
- package/dist/lib/node-esm/testing/index.mjs.map +4 -4
- package/dist/types/src/components/Avatars/Avatar.stories.d.ts +0 -6
- package/dist/types/src/components/Avatars/Avatar.stories.d.ts.map +1 -1
- package/dist/types/src/components/Avatars/AvatarGroup.stories.d.ts +0 -6
- package/dist/types/src/components/Avatars/AvatarGroup.stories.d.ts.map +1 -1
- package/dist/types/src/components/Breadcrumb/Breadcrumb.stories.d.ts +0 -6
- package/dist/types/src/components/Breadcrumb/Breadcrumb.stories.d.ts.map +1 -1
- package/dist/types/src/components/{Buttons → Button}/Button.d.ts +1 -1
- package/dist/types/src/components/Button/Button.d.ts.map +1 -0
- package/dist/types/src/components/{Buttons → Button}/Button.stories.d.ts +1 -7
- package/dist/types/src/components/Button/Button.stories.d.ts.map +1 -0
- package/dist/types/src/components/{Buttons → Button}/IconButton.d.ts +1 -2
- package/dist/types/src/components/Button/IconButton.d.ts.map +1 -0
- package/dist/types/src/components/{Buttons → Button}/IconButton.stories.d.ts +0 -6
- package/dist/types/src/components/Button/IconButton.stories.d.ts.map +1 -0
- package/dist/types/src/components/Button/Toggle.d.ts.map +1 -0
- package/dist/types/src/components/Button/Toggle.stories.d.ts +16 -0
- package/dist/types/src/components/Button/Toggle.stories.d.ts.map +1 -0
- package/dist/types/src/components/{Buttons → Button}/ToggleGroup.d.ts +6 -7
- package/dist/types/src/components/Button/ToggleGroup.d.ts.map +1 -0
- package/dist/types/src/components/{Buttons → Button}/ToggleGroup.stories.d.ts +2 -12
- package/dist/types/src/components/Button/ToggleGroup.stories.d.ts.map +1 -0
- package/dist/types/src/components/Button/index.d.ts.map +1 -0
- package/dist/types/src/components/Clipboard/CopyButton.d.ts +1 -1
- package/dist/types/src/components/Clipboard/CopyButton.d.ts.map +1 -1
- package/dist/types/src/components/Dialog/AlertDialog.d.ts.map +1 -0
- package/dist/types/src/components/{Dialogs → Dialog}/AlertDialog.stories.d.ts +0 -6
- package/dist/types/src/components/Dialog/AlertDialog.stories.d.ts.map +1 -0
- package/dist/types/src/components/Dialog/Dialog.d.ts.map +1 -0
- package/dist/types/src/components/{Dialogs → Dialog}/Dialog.stories.d.ts +0 -1
- package/dist/types/src/components/Dialog/Dialog.stories.d.ts.map +1 -0
- package/dist/types/src/components/Dialog/index.d.ts.map +1 -0
- package/dist/types/src/components/Icon/Icon.d.ts +1 -1
- package/dist/types/src/components/Icon/Icon.d.ts.map +1 -1
- package/dist/types/src/components/Icon/Icon.stories.d.ts +17 -0
- package/dist/types/src/components/Icon/Icon.stories.d.ts.map +1 -0
- package/dist/types/src/components/Input/Input.d.ts +0 -2
- package/dist/types/src/components/Input/Input.d.ts.map +1 -1
- package/dist/types/src/components/Input/Input.stories.d.ts +0 -6
- package/dist/types/src/components/Input/Input.stories.d.ts.map +1 -1
- package/dist/types/src/components/Link/Link.stories.d.ts +0 -6
- package/dist/types/src/components/Link/Link.stories.d.ts.map +1 -1
- package/dist/types/src/components/List/List.d.ts.map +1 -0
- package/dist/types/src/components/{Lists → List}/List.stories.d.ts +0 -6
- package/dist/types/src/components/List/List.stories.d.ts.map +1 -0
- package/dist/types/src/components/List/ListDropIndicator.d.ts.map +1 -0
- package/dist/types/src/components/List/Tree.d.ts.map +1 -0
- package/dist/types/src/components/{Lists → List}/Tree.stories.d.ts +0 -6
- package/dist/types/src/components/List/Tree.stories.d.ts.map +1 -0
- package/dist/types/src/components/List/TreeDropIndicator.d.ts.map +1 -0
- package/dist/types/src/components/{Lists → List}/Treegrid.d.ts.map +1 -1
- package/dist/types/src/components/List/Treegrid.stories.d.ts.map +1 -0
- package/dist/types/src/components/List/index.d.ts.map +1 -0
- package/dist/types/src/components/Main/Main.d.ts +9 -18
- package/dist/types/src/components/Main/Main.d.ts.map +1 -1
- package/dist/types/src/components/Main/Main.stories.d.ts +1 -2
- package/dist/types/src/components/Main/Main.stories.d.ts.map +1 -1
- package/dist/types/src/components/Menus/ContextMenu.d.ts.map +1 -1
- package/dist/types/src/components/Menus/ContextMenu.stories.d.ts +0 -6
- package/dist/types/src/components/Menus/ContextMenu.stories.d.ts.map +1 -1
- package/dist/types/src/components/Menus/DropdownMenu.d.ts +5 -4
- package/dist/types/src/components/Menus/DropdownMenu.d.ts.map +1 -1
- package/dist/types/src/components/Menus/DropdownMenu.stories.d.ts +0 -6
- package/dist/types/src/components/Menus/DropdownMenu.stories.d.ts.map +1 -1
- package/dist/types/src/components/Message/Message.stories.d.ts +0 -1
- package/dist/types/src/components/Message/Message.stories.d.ts.map +1 -1
- package/dist/types/src/components/Popover/Popover.d.ts +1 -1
- package/dist/types/src/components/Popover/Popover.d.ts.map +1 -1
- package/dist/types/src/components/Popover/Popover.stories.d.ts +0 -6
- package/dist/types/src/components/Popover/Popover.stories.d.ts.map +1 -1
- package/dist/types/src/components/ScrollArea/ScrollArea.stories.d.ts +0 -6
- package/dist/types/src/components/ScrollArea/ScrollArea.stories.d.ts.map +1 -1
- package/dist/types/src/components/ScrollContainer/ScrollContainer.d.ts +37 -0
- package/dist/types/src/components/ScrollContainer/ScrollContainer.d.ts.map +1 -0
- package/dist/types/src/components/ScrollContainer/ScrollContainer.stories.d.ts +18 -0
- package/dist/types/src/components/ScrollContainer/ScrollContainer.stories.d.ts.map +1 -0
- package/dist/types/src/components/ScrollContainer/index.d.ts +2 -0
- package/dist/types/src/components/ScrollContainer/index.d.ts.map +1 -0
- package/dist/types/src/components/Select/Select.d.ts +1 -1
- package/dist/types/src/components/Select/Select.d.ts.map +1 -1
- package/dist/types/src/components/Select/Select.stories.d.ts +0 -6
- package/dist/types/src/components/Select/Select.stories.d.ts.map +1 -1
- package/dist/types/src/components/Status/Status.stories.d.ts +0 -6
- package/dist/types/src/components/Status/Status.stories.d.ts.map +1 -1
- package/dist/types/src/components/Tag/Tag.stories.d.ts +8 -8
- package/dist/types/src/components/Tag/Tag.stories.d.ts.map +1 -1
- package/dist/types/src/components/Toast/Toast.stories.d.ts +0 -6
- package/dist/types/src/components/Toast/Toast.stories.d.ts.map +1 -1
- package/dist/types/src/components/Toolbar/Toolbar.d.ts +12 -12
- package/dist/types/src/components/Toolbar/Toolbar.d.ts.map +1 -1
- package/dist/types/src/components/Toolbar/Toolbar.stories.d.ts +0 -6
- package/dist/types/src/components/Toolbar/Toolbar.stories.d.ts.map +1 -1
- package/dist/types/src/components/Tooltip/Tooltip.d.ts +1 -3
- package/dist/types/src/components/Tooltip/Tooltip.d.ts.map +1 -1
- package/dist/types/src/components/Tooltip/Tooltip.stories.d.ts +0 -6
- package/dist/types/src/components/Tooltip/Tooltip.stories.d.ts.map +1 -1
- package/dist/types/src/components/index.d.ts +4 -3
- package/dist/types/src/components/index.d.ts.map +1 -1
- package/dist/types/src/hooks/useSafeArea.d.ts.map +1 -1
- package/dist/types/src/hooks/useVisualViewport.d.ts +2 -2
- package/dist/types/src/hooks/useVisualViewport.d.ts.map +1 -1
- package/dist/types/src/index.d.ts +1 -1
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/playground/Controls.stories.d.ts +0 -6
- package/dist/types/src/playground/Controls.stories.d.ts.map +1 -1
- package/dist/types/src/playground/Custom.stories.d.ts +1 -2
- package/dist/types/src/playground/Custom.stories.d.ts.map +1 -1
- package/dist/types/src/playground/Typography.stories.d.ts +0 -6
- package/dist/types/src/playground/Typography.stories.d.ts.map +1 -1
- package/dist/types/src/testing/decorators/index.d.ts +2 -1
- package/dist/types/src/testing/decorators/index.d.ts.map +1 -1
- package/dist/types/src/testing/decorators/withLayout.d.ts +15 -0
- package/dist/types/src/testing/decorators/withLayout.d.ts.map +1 -0
- package/dist/types/src/testing/decorators/{withSurfaceVariantsLayout.d.ts → withLayoutVariants.d.ts} +2 -2
- package/dist/types/src/testing/decorators/withLayoutVariants.d.ts.map +1 -0
- package/dist/types/src/testing/decorators/withTheme.d.ts +3 -0
- package/dist/types/src/testing/decorators/withTheme.d.ts.map +1 -1
- package/dist/types/src/util/domino.d.ts +18 -0
- package/dist/types/src/util/domino.d.ts.map +1 -0
- package/dist/types/src/util/index.d.ts +2 -0
- package/dist/types/src/util/index.d.ts.map +1 -1
- package/dist/types/src/util/usePx.d.ts +8 -0
- package/dist/types/src/util/usePx.d.ts.map +1 -0
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +24 -23
- package/src/components/Avatars/Avatar.stories.tsx +0 -2
- package/src/components/Avatars/AvatarGroup.stories.tsx +0 -3
- package/src/components/Breadcrumb/Breadcrumb.stories.tsx +1 -4
- package/src/components/{Buttons → Button}/Button.stories.tsx +3 -5
- package/src/components/{Buttons → Button}/IconButton.stories.tsx +0 -3
- package/src/components/{Buttons → Button}/IconButton.tsx +14 -13
- package/src/components/{Buttons → Button}/Toggle.stories.tsx +5 -7
- package/src/components/{Buttons → Button}/ToggleGroup.stories.tsx +0 -3
- package/src/components/{Buttons → Button}/ToggleGroup.tsx +14 -1
- package/src/components/Clipboard/CopyButton.tsx +1 -1
- package/src/components/{Dialogs → Dialog}/AlertDialog.stories.tsx +1 -4
- package/src/components/{Dialogs → Dialog}/Dialog.stories.tsx +6 -4
- package/src/components/Icon/Icon.stories.tsx +113 -0
- package/src/components/Icon/Icon.tsx +1 -1
- package/src/components/Input/Input.stories.tsx +0 -3
- package/src/components/Input/Input.tsx +3 -3
- package/src/components/Link/Link.stories.tsx +0 -3
- package/src/components/{Lists → List}/List.stories.tsx +2 -6
- package/src/components/{Lists → List}/ListDropIndicator.tsx +1 -1
- package/src/components/{Lists → List}/Tree.stories.tsx +0 -3
- package/src/components/{Lists → List}/Treegrid.tsx +57 -16
- package/src/components/Main/Main.stories.tsx +4 -4
- package/src/components/Main/Main.tsx +28 -19
- package/src/components/Menus/ContextMenu.stories.tsx +0 -3
- package/src/components/Menus/ContextMenu.tsx +1 -0
- package/src/components/Menus/DropdownMenu.stories.tsx +1 -4
- package/src/components/Menus/DropdownMenu.tsx +33 -6
- package/src/components/Message/Message.stories.tsx +0 -2
- package/src/components/Popover/Popover.stories.tsx +1 -4
- package/src/components/Popover/Popover.tsx +22 -5
- package/src/components/ScrollArea/ScrollArea.stories.tsx +1 -4
- package/src/components/ScrollContainer/ScrollContainer.stories.tsx +69 -0
- package/src/components/ScrollContainer/ScrollContainer.tsx +231 -0
- package/src/components/ScrollContainer/index.ts +5 -0
- package/src/components/Select/Select.stories.tsx +3 -5
- package/src/components/Select/Select.tsx +5 -4
- package/src/components/Status/Status.stories.tsx +0 -3
- package/src/components/Tag/Tag.stories.tsx +8 -7
- package/src/components/Toast/Toast.stories.tsx +1 -4
- package/src/components/Toolbar/Toolbar.stories.tsx +2 -7
- package/src/components/Toolbar/Toolbar.tsx +18 -6
- package/src/components/Tooltip/Tooltip.stories.tsx +1 -4
- package/src/components/Tooltip/Tooltip.tsx +2 -1
- package/src/components/index.ts +4 -3
- package/src/hooks/useSafeArea.ts +3 -2
- package/src/hooks/useVisualViewport.ts +4 -4
- package/src/index.ts +1 -1
- package/src/playground/Controls.stories.tsx +3 -5
- package/src/playground/Custom.stories.tsx +9 -20
- package/src/playground/Typography.stories.tsx +0 -3
- package/src/testing/decorators/index.ts +2 -1
- package/src/testing/decorators/withLayout.tsx +56 -0
- package/src/testing/decorators/{withSurfaceVariantsLayout.tsx → withLayoutVariants.tsx} +2 -2
- package/src/testing/decorators/withTheme.tsx +31 -0
- package/src/util/domino.ts +53 -0
- package/src/util/index.ts +2 -0
- package/src/util/usePx.ts +61 -0
- package/dist/lib/browser/chunk-P333G32W.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-JRCE5UVS.mjs.map +0 -7
- package/dist/types/src/components/Buttons/Button.d.ts.map +0 -1
- package/dist/types/src/components/Buttons/Button.stories.d.ts.map +0 -1
- package/dist/types/src/components/Buttons/IconButton.d.ts.map +0 -1
- package/dist/types/src/components/Buttons/IconButton.stories.d.ts.map +0 -1
- package/dist/types/src/components/Buttons/Toggle.d.ts.map +0 -1
- package/dist/types/src/components/Buttons/Toggle.stories.d.ts +0 -19
- package/dist/types/src/components/Buttons/Toggle.stories.d.ts.map +0 -1
- package/dist/types/src/components/Buttons/ToggleGroup.d.ts.map +0 -1
- package/dist/types/src/components/Buttons/ToggleGroup.stories.d.ts.map +0 -1
- package/dist/types/src/components/Buttons/index.d.ts.map +0 -1
- package/dist/types/src/components/Dialogs/AlertDialog.d.ts.map +0 -1
- package/dist/types/src/components/Dialogs/AlertDialog.stories.d.ts.map +0 -1
- package/dist/types/src/components/Dialogs/Dialog.d.ts.map +0 -1
- package/dist/types/src/components/Dialogs/Dialog.stories.d.ts.map +0 -1
- package/dist/types/src/components/Dialogs/index.d.ts.map +0 -1
- package/dist/types/src/components/Lists/List.d.ts.map +0 -1
- package/dist/types/src/components/Lists/List.stories.d.ts.map +0 -1
- package/dist/types/src/components/Lists/ListDropIndicator.d.ts.map +0 -1
- package/dist/types/src/components/Lists/Tree.d.ts.map +0 -1
- package/dist/types/src/components/Lists/Tree.stories.d.ts.map +0 -1
- package/dist/types/src/components/Lists/TreeDropIndicator.d.ts.map +0 -1
- package/dist/types/src/components/Lists/Treegrid.stories.d.ts.map +0 -1
- package/dist/types/src/components/Lists/index.d.ts.map +0 -1
- package/dist/types/src/testing/decorators/withSurfaceVariantsLayout.d.ts.map +0 -1
- package/src/testing/decorators/withTheme.ts +0 -25
- /package/dist/types/src/components/{Buttons → Button}/Toggle.d.ts +0 -0
- /package/dist/types/src/components/{Buttons → Button}/index.d.ts +0 -0
- /package/dist/types/src/components/{Dialogs → Dialog}/AlertDialog.d.ts +0 -0
- /package/dist/types/src/components/{Dialogs → Dialog}/Dialog.d.ts +0 -0
- /package/dist/types/src/components/{Dialogs → Dialog}/index.d.ts +0 -0
- /package/dist/types/src/components/{Lists → List}/List.d.ts +0 -0
- /package/dist/types/src/components/{Lists → List}/ListDropIndicator.d.ts +0 -0
- /package/dist/types/src/components/{Lists → List}/Tree.d.ts +0 -0
- /package/dist/types/src/components/{Lists → List}/TreeDropIndicator.d.ts +0 -0
- /package/dist/types/src/components/{Lists → List}/Treegrid.d.ts +0 -0
- /package/dist/types/src/components/{Lists → List}/Treegrid.stories.d.ts +0 -0
- /package/dist/types/src/components/{Lists → List}/index.d.ts +0 -0
- /package/src/components/{Buttons → Button}/Button.tsx +0 -0
- /package/src/components/{Buttons → Button}/Toggle.tsx +0 -0
- /package/src/components/{Buttons → Button}/index.ts +0 -0
- /package/src/components/{Dialogs → Dialog}/AlertDialog.tsx +0 -0
- /package/src/components/{Dialogs → Dialog}/Dialog.tsx +0 -0
- /package/src/components/{Dialogs → Dialog}/index.ts +0 -0
- /package/src/components/{Lists → List}/List.tsx +0 -0
- /package/src/components/{Lists → List}/Tree.tsx +0 -0
- /package/src/components/{Lists → List}/TreeDropIndicator.tsx +0 -0
- /package/src/components/{Lists → List}/Treegrid.stories.tsx +0 -0
- /package/src/components/{Lists → List}/index.ts +0 -0
|
@@ -2,12 +2,18 @@
|
|
|
2
2
|
// Copyright 2024 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import {
|
|
5
|
+
import { useFocusFinders } from '@fluentui/react-tabster';
|
|
6
6
|
import { type Scope, createContextScope } from '@radix-ui/react-context';
|
|
7
7
|
import { Primitive } from '@radix-ui/react-primitive';
|
|
8
8
|
import { Slot } from '@radix-ui/react-slot';
|
|
9
9
|
import { useControllableState } from '@radix-ui/react-use-controllable-state';
|
|
10
|
-
import React, {
|
|
10
|
+
import React, {
|
|
11
|
+
type CSSProperties,
|
|
12
|
+
type ComponentPropsWithRef,
|
|
13
|
+
type KeyboardEvent,
|
|
14
|
+
forwardRef,
|
|
15
|
+
useCallback,
|
|
16
|
+
} from 'react';
|
|
11
17
|
|
|
12
18
|
import { useThemeContext } from '../../hooks';
|
|
13
19
|
import { type ThemedClassName } from '../../util';
|
|
@@ -40,12 +46,58 @@ const TreegridRoot = forwardRef<HTMLDivElement, TreegridRootProps>(
|
|
|
40
46
|
({ asChild, classNames, children, style, gridTemplateColumns, ...props }, forwardedRef) => {
|
|
41
47
|
const { tx } = useThemeContext();
|
|
42
48
|
const Root = asChild ? Slot : Primitive.div;
|
|
43
|
-
const
|
|
49
|
+
const { findFirstFocusable } = useFocusFinders();
|
|
50
|
+
|
|
51
|
+
const handleKeyDown = useCallback(
|
|
52
|
+
(event: KeyboardEvent<HTMLDivElement>) => {
|
|
53
|
+
switch (event.key) {
|
|
54
|
+
case 'ArrowDown':
|
|
55
|
+
case 'ArrowUp': {
|
|
56
|
+
const direction = event.key === 'ArrowDown' ? 'down' : 'up';
|
|
57
|
+
const target = event.target as HTMLElement;
|
|
58
|
+
|
|
59
|
+
// Find ancestor with data-arrow-keys containing the relevant direction.
|
|
60
|
+
const ancestorWithArrowKeys = target.closest(`[data-arrow-keys*="${direction}"], [data-arrow-keys="all"]`);
|
|
61
|
+
|
|
62
|
+
// If no ancestor with data-arrow-keys found, proceed with row navigation.
|
|
63
|
+
if (!ancestorWithArrowKeys) {
|
|
64
|
+
// Find the closest row
|
|
65
|
+
const currentRow = target.closest('[role="row"]');
|
|
66
|
+
if (currentRow) {
|
|
67
|
+
// Find the treegrid container.
|
|
68
|
+
const treegrid = currentRow.closest('[role="treegrid"]');
|
|
69
|
+
if (treegrid) {
|
|
70
|
+
// Get all rows in the treegrid.
|
|
71
|
+
const rows = Array.from(treegrid.querySelectorAll('[role="row"]'));
|
|
72
|
+
const currentIndex = rows.indexOf(currentRow as Element);
|
|
73
|
+
|
|
74
|
+
// Find next or previous row.
|
|
75
|
+
const nextIndex = direction === 'down' ? currentIndex + 1 : currentIndex - 1;
|
|
76
|
+
const targetRow = rows[nextIndex];
|
|
77
|
+
|
|
78
|
+
if (targetRow) {
|
|
79
|
+
// Focus the first focusable element in the target row.
|
|
80
|
+
const firstFocusable = findFirstFocusable(targetRow as HTMLElement);
|
|
81
|
+
if (firstFocusable) {
|
|
82
|
+
event.preventDefault();
|
|
83
|
+
firstFocusable.focus();
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
break;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
props.onKeyDown?.(event);
|
|
93
|
+
},
|
|
94
|
+
[findFirstFocusable],
|
|
95
|
+
);
|
|
44
96
|
|
|
45
97
|
return (
|
|
46
98
|
<Root
|
|
47
99
|
role='treegrid'
|
|
48
|
-
{
|
|
100
|
+
onKeyDown={handleKeyDown}
|
|
49
101
|
{...props}
|
|
50
102
|
className={tx('treegrid.root', 'treegrid', {}, classNames)}
|
|
51
103
|
style={{ ...style, gridTemplateColumns }}
|
|
@@ -91,13 +143,6 @@ const TreegridRow = forwardRef<HTMLDivElement, TreegridRowScopedProps<TreegridRo
|
|
|
91
143
|
onChange: propsOnOpenChange,
|
|
92
144
|
defaultProp: defaultOpen,
|
|
93
145
|
});
|
|
94
|
-
const focusableGroupAttrs = useFocusableGroup({ tabBehavior: 'limited' });
|
|
95
|
-
const arrowGroupAttrs = useArrowNavigationGroup({
|
|
96
|
-
axis: 'horizontal',
|
|
97
|
-
tabbable: false,
|
|
98
|
-
circular: false,
|
|
99
|
-
memorizeCurrent: false,
|
|
100
|
-
});
|
|
101
146
|
|
|
102
147
|
return (
|
|
103
148
|
<TreegridRowProvider open={open} onOpenChange={onOpenChange} scope={__treegridRowScope}>
|
|
@@ -106,15 +151,11 @@ const TreegridRow = forwardRef<HTMLDivElement, TreegridRowScopedProps<TreegridRo
|
|
|
106
151
|
aria-level={level}
|
|
107
152
|
className={tx('treegrid.row', 'treegrid__row', { level }, classNames)}
|
|
108
153
|
{...(parentOf && { 'aria-expanded': open, 'aria-owns': parentOf })}
|
|
109
|
-
tabIndex={0}
|
|
110
|
-
{...focusableGroupAttrs}
|
|
111
154
|
{...props}
|
|
112
155
|
id={id}
|
|
113
156
|
ref={forwardedRef}
|
|
114
157
|
>
|
|
115
|
-
|
|
116
|
-
{children}
|
|
117
|
-
</div>
|
|
158
|
+
{children}
|
|
118
159
|
</Root>
|
|
119
160
|
</TreegridRowProvider>
|
|
120
161
|
);
|
|
@@ -2,13 +2,11 @@
|
|
|
2
2
|
// Copyright 2023 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import '@dxos-theme';
|
|
6
|
-
|
|
7
5
|
import { type Meta, type StoryObj } from '@storybook/react-vite';
|
|
8
6
|
import React from 'react';
|
|
9
7
|
|
|
10
8
|
import { withTheme } from '../../testing';
|
|
11
|
-
import { Button } from '../
|
|
9
|
+
import { Button } from '../Button';
|
|
12
10
|
|
|
13
11
|
import { Main, useSidebars } from './Main';
|
|
14
12
|
|
|
@@ -53,8 +51,10 @@ const meta = {
|
|
|
53
51
|
render: DefaultStory,
|
|
54
52
|
decorators: [withTheme],
|
|
55
53
|
parameters: {
|
|
56
|
-
chromatic: { disableSnapshot: false },
|
|
57
54
|
layout: 'fullscreen',
|
|
55
|
+
chromatic: {
|
|
56
|
+
disableSnapshot: false,
|
|
57
|
+
},
|
|
58
58
|
},
|
|
59
59
|
} satisfies Meta<typeof DefaultStory>;
|
|
60
60
|
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
// Copyright 2023 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
+
import { useFocusableGroup } from '@fluentui/react-tabster';
|
|
5
6
|
import { createContext } from '@radix-ui/react-context';
|
|
6
7
|
import { DialogContent, Root as DialogRoot, DialogTitle } from '@radix-ui/react-dialog';
|
|
7
8
|
import { Primitive } from '@radix-ui/react-primitive';
|
|
@@ -30,22 +31,12 @@ import { type Label, toLocalizedString, useTranslation } from '../ThemeProvider'
|
|
|
30
31
|
|
|
31
32
|
import { useSwipeToDismiss } from './useSwipeToDismiss';
|
|
32
33
|
|
|
34
|
+
const MAIN_NAME = 'Main';
|
|
33
35
|
const MAIN_ROOT_NAME = 'MainRoot';
|
|
34
36
|
const NAVIGATION_SIDEBAR_NAME = 'NavigationSidebar';
|
|
35
37
|
const COMPLEMENTARY_SIDEBAR_NAME = 'ComplementarySidebar';
|
|
36
|
-
const MAIN_NAME = 'Main';
|
|
37
38
|
const GENERIC_CONSUMER_NAME = 'GenericConsumer';
|
|
38
39
|
|
|
39
|
-
type SidebarState = 'expanded' | 'collapsed' | 'closed';
|
|
40
|
-
|
|
41
|
-
type MainContextValue = {
|
|
42
|
-
resizing: boolean;
|
|
43
|
-
navigationSidebarState: SidebarState;
|
|
44
|
-
setNavigationSidebarState: Dispatch<SetStateAction<SidebarState | undefined>>;
|
|
45
|
-
complementarySidebarState: SidebarState;
|
|
46
|
-
setComplementarySidebarState: Dispatch<SetStateAction<SidebarState | undefined>>;
|
|
47
|
-
};
|
|
48
|
-
|
|
49
40
|
const landmarkAttr = 'data-main-landmark';
|
|
50
41
|
|
|
51
42
|
/**
|
|
@@ -70,7 +61,10 @@ const useLandmarkMover = (propsOnKeyDown: ComponentPropsWithoutRef<'div'>['onKey
|
|
|
70
61
|
},
|
|
71
62
|
[propsOnKeyDown],
|
|
72
63
|
);
|
|
73
|
-
|
|
64
|
+
|
|
65
|
+
// TODO(thure): This was disconnected once before in #8818;
|
|
66
|
+
// if this should change again to support the browser extension, please ensure the change doesn’t break web, desktop and mobile.
|
|
67
|
+
const focusableGroupAttrs = useFocusableGroup({ tabBehavior: 'limited', ignoreDefaultKeydown: { Tab: true } });
|
|
74
68
|
|
|
75
69
|
return {
|
|
76
70
|
onKeyDown: handleKeyDown,
|
|
@@ -80,6 +74,16 @@ const useLandmarkMover = (propsOnKeyDown: ComponentPropsWithoutRef<'div'>['onKey
|
|
|
80
74
|
};
|
|
81
75
|
};
|
|
82
76
|
|
|
77
|
+
type SidebarState = 'expanded' | 'collapsed' | 'closed';
|
|
78
|
+
|
|
79
|
+
type MainContextValue = {
|
|
80
|
+
resizing: boolean;
|
|
81
|
+
navigationSidebarState: SidebarState;
|
|
82
|
+
setNavigationSidebarState: Dispatch<SetStateAction<SidebarState | undefined>>;
|
|
83
|
+
complementarySidebarState: SidebarState;
|
|
84
|
+
setComplementarySidebarState: Dispatch<SetStateAction<SidebarState | undefined>>;
|
|
85
|
+
};
|
|
86
|
+
|
|
83
87
|
const [MainProvider, useMainContext] = createContext<MainContextValue>(MAIN_NAME, {
|
|
84
88
|
resizing: false,
|
|
85
89
|
navigationSidebarState: 'closed',
|
|
@@ -141,7 +145,7 @@ const MainRoot = ({
|
|
|
141
145
|
children,
|
|
142
146
|
...props
|
|
143
147
|
}: MainRootProps) => {
|
|
144
|
-
const [isLg] = useMediaQuery('lg'
|
|
148
|
+
const [isLg] = useMediaQuery('lg');
|
|
145
149
|
const [navigationSidebarState = isLg ? 'expanded' : 'collapsed', setNavigationSidebarState] =
|
|
146
150
|
useControllableState<SidebarState>({
|
|
147
151
|
prop: propsNavigationSidebarState,
|
|
@@ -210,7 +214,7 @@ const MainSidebar = forwardRef<HTMLDivElement, MainSidebarProps>(
|
|
|
210
214
|
{ classNames, children, swipeToDismiss, onOpenAutoFocus, state, resizing, onStateChange, side, label, ...props },
|
|
211
215
|
forwardedRef,
|
|
212
216
|
) => {
|
|
213
|
-
const [isLg] = useMediaQuery('lg'
|
|
217
|
+
const [isLg] = useMediaQuery('lg');
|
|
214
218
|
const { tx } = useThemeContext();
|
|
215
219
|
const { t } = useTranslation();
|
|
216
220
|
const ref = useForwardedRef(forwardedRef);
|
|
@@ -218,10 +222,15 @@ const MainSidebar = forwardRef<HTMLDivElement, MainSidebarProps>(
|
|
|
218
222
|
useSwipeToDismiss(swipeToDismiss ? ref : noopRef, {
|
|
219
223
|
onDismiss: () => onStateChange?.('closed'),
|
|
220
224
|
});
|
|
225
|
+
// NOTE(thure): This is a workaround for something further down the tree grabbing focus on Escape. Adding this
|
|
226
|
+
// intervention to `Tabs.Root` or `Tabs.Tabpenel` instances is somehow ineffectual.
|
|
221
227
|
const handleKeyDown = useCallback(
|
|
222
228
|
(event: KeyboardEvent<HTMLDivElement>) => {
|
|
223
|
-
|
|
224
|
-
|
|
229
|
+
const focusGroupParent = (event.target as HTMLElement).closest('[data-tabster]');
|
|
230
|
+
if (event.key === 'Escape' && focusGroupParent) {
|
|
231
|
+
event.preventDefault();
|
|
232
|
+
event.stopPropagation();
|
|
233
|
+
(focusGroupParent as HTMLElement).focus();
|
|
225
234
|
}
|
|
226
235
|
props.onKeyDown?.(event);
|
|
227
236
|
},
|
|
@@ -239,8 +248,8 @@ const MainSidebar = forwardRef<HTMLDivElement, MainSidebarProps>(
|
|
|
239
248
|
data-state={state}
|
|
240
249
|
data-resizing={resizing ? 'true' : 'false'}
|
|
241
250
|
className={tx('main.sidebar', 'main__sidebar', {}, classNames)}
|
|
242
|
-
|
|
243
|
-
{...(state === 'closed' && { inert:
|
|
251
|
+
onKeyDownCapture={handleKeyDown}
|
|
252
|
+
{...(state === 'closed' && { inert: true })}
|
|
244
253
|
ref={ref}
|
|
245
254
|
>
|
|
246
255
|
{children}
|
|
@@ -329,7 +338,7 @@ MainContent.displayName = MAIN_NAME;
|
|
|
329
338
|
type MainOverlayProps = ThemedClassName<Omit<ComponentPropsWithRef<typeof Primitive.div>, 'children'>>;
|
|
330
339
|
|
|
331
340
|
const MainOverlay = forwardRef<HTMLDivElement, MainOverlayProps>(({ classNames, ...props }, forwardedRef) => {
|
|
332
|
-
const [isLg] = useMediaQuery('lg'
|
|
341
|
+
const [isLg] = useMediaQuery('lg');
|
|
333
342
|
const { navigationSidebarState, setNavigationSidebarState, complementarySidebarState, setComplementarySidebarState } =
|
|
334
343
|
useMainContext(MAIN_NAME);
|
|
335
344
|
const { tx } = useThemeContext();
|
|
@@ -2,8 +2,6 @@
|
|
|
2
2
|
// Copyright 2022 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import '@dxos-theme';
|
|
6
|
-
|
|
7
5
|
import { type Meta, type StoryObj } from '@storybook/react-vite';
|
|
8
6
|
import React from 'react';
|
|
9
7
|
|
|
@@ -98,7 +96,6 @@ const meta = {
|
|
|
98
96
|
component: ContextMenu.Root as any,
|
|
99
97
|
render: DefaultStory,
|
|
100
98
|
decorators: [withTheme],
|
|
101
|
-
parameters: { chromatic: { disableSnapshot: false } },
|
|
102
99
|
} satisfies Meta<typeof DefaultStory>;
|
|
103
100
|
|
|
104
101
|
export default meta;
|
|
@@ -34,6 +34,7 @@ const ContextMenuContent = forwardRef<HTMLDivElement, ContextMenuContentProps>(
|
|
|
34
34
|
return (
|
|
35
35
|
<ContextMenuPrimitive.Content
|
|
36
36
|
{...props}
|
|
37
|
+
data-arrow-keys='up down'
|
|
37
38
|
collisionPadding={safeCollisionPadding}
|
|
38
39
|
className={tx('menu.content', 'menu', { elevation }, classNames)}
|
|
39
40
|
ref={forwardedRef}
|
|
@@ -2,13 +2,11 @@
|
|
|
2
2
|
// Copyright 2022 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import '@dxos-theme';
|
|
6
|
-
|
|
7
5
|
import { type Meta, type StoryObj } from '@storybook/react-vite';
|
|
8
6
|
import React, { useRef, useState } from 'react';
|
|
9
7
|
|
|
10
8
|
import { withTheme } from '../../testing';
|
|
11
|
-
import { Button } from '../
|
|
9
|
+
import { Button } from '../Button';
|
|
12
10
|
|
|
13
11
|
import { DropdownMenu } from './DropdownMenu';
|
|
14
12
|
|
|
@@ -98,7 +96,6 @@ const meta = {
|
|
|
98
96
|
component: DropdownMenu.Root,
|
|
99
97
|
render: DefaultStory,
|
|
100
98
|
decorators: [withTheme],
|
|
101
|
-
parameters: { chromatic: { disableSnapshot: false } },
|
|
102
99
|
} satisfies Meta<typeof DefaultStory>;
|
|
103
100
|
|
|
104
101
|
export default meta;
|
|
@@ -25,6 +25,7 @@ import React, {
|
|
|
25
25
|
forwardRef,
|
|
26
26
|
useCallback,
|
|
27
27
|
useEffect,
|
|
28
|
+
useMemo,
|
|
28
29
|
useRef,
|
|
29
30
|
} from 'react';
|
|
30
31
|
|
|
@@ -124,6 +125,7 @@ const DropdownMenuTrigger = forwardRef<DropdownMenuTriggerElement, DropdownMenuT
|
|
|
124
125
|
disabled={disabled}
|
|
125
126
|
{...triggerProps}
|
|
126
127
|
ref={composeRefs(forwardedRef, context.triggerRef)}
|
|
128
|
+
data-arrow-keys='down'
|
|
127
129
|
onPointerDown={composeEventHandlers(props.onPointerDown, (event) => {
|
|
128
130
|
// only call handler if it's the left button (mousedown gets triggered by all mouse buttons)
|
|
129
131
|
// but not when the control key is pressed (avoiding MacOS right click)
|
|
@@ -167,7 +169,7 @@ DropdownMenuTrigger.displayName = TRIGGER_NAME;
|
|
|
167
169
|
const VIRTUAL_TRIGGER_NAME = 'DropdownMenuVirtualTrigger';
|
|
168
170
|
|
|
169
171
|
interface DropdownMenuVirtualTriggerProps {
|
|
170
|
-
virtualRef: RefObject<DropdownMenuTriggerElement>;
|
|
172
|
+
virtualRef: RefObject<DropdownMenuTriggerElement | null>;
|
|
171
173
|
}
|
|
172
174
|
|
|
173
175
|
const DropdownMenuVirtualTrigger = (props: ScopedProps<DropdownMenuVirtualTriggerProps>) => {
|
|
@@ -179,7 +181,7 @@ const DropdownMenuVirtualTrigger = (props: ScopedProps<DropdownMenuVirtualTrigge
|
|
|
179
181
|
context.triggerRef.current = virtualRef.current;
|
|
180
182
|
}
|
|
181
183
|
});
|
|
182
|
-
return <MenuPrimitive.Anchor {...menuScope} virtualRef={virtualRef} />;
|
|
184
|
+
return <MenuPrimitive.Anchor {...menuScope} virtualRef={virtualRef as RefObject<DropdownMenuTriggerElement>} />;
|
|
183
185
|
};
|
|
184
186
|
|
|
185
187
|
DropdownMenuVirtualTrigger.displayName = VIRTUAL_TRIGGER_NAME;
|
|
@@ -233,19 +235,35 @@ interface DropdownMenuContentProps extends Omit<MenuContentProps, 'onEntryFocus'
|
|
|
233
235
|
|
|
234
236
|
const DropdownMenuContent = forwardRef<DropdownMenuContentElement, DropdownMenuContentProps>(
|
|
235
237
|
(props: ScopedProps<DropdownMenuContentProps>, forwardedRef) => {
|
|
236
|
-
const { __scopeDropdownMenu, classNames, collisionPadding = 8, ...contentProps } = props;
|
|
238
|
+
const { __scopeDropdownMenu, classNames, collisionPadding = 8, collisionBoundary, ...contentProps } = props;
|
|
237
239
|
const { tx } = useThemeContext();
|
|
238
240
|
const context = useDropdownMenuContext(CONTENT_NAME, __scopeDropdownMenu);
|
|
239
241
|
const elevation = useElevationContext();
|
|
240
242
|
const menuScope = useMenuScope(__scopeDropdownMenu);
|
|
241
243
|
const hasInteractedOutsideRef = useRef(false);
|
|
242
244
|
const safeCollisionPadding = useSafeCollisionPadding(collisionPadding);
|
|
245
|
+
|
|
246
|
+
// Check for the closest annotated collision boundary in the DOM tree.
|
|
247
|
+
const computedCollisionBoundary = useMemo(() => {
|
|
248
|
+
const closestBoundary = context.triggerRef.current?.closest(
|
|
249
|
+
'[data-popover-collision-boundary]',
|
|
250
|
+
) as HTMLElement | null;
|
|
251
|
+
return closestBoundary
|
|
252
|
+
? Array.isArray(collisionBoundary)
|
|
253
|
+
? [closestBoundary, ...collisionBoundary]
|
|
254
|
+
: collisionBoundary
|
|
255
|
+
? [closestBoundary, collisionBoundary]
|
|
256
|
+
: [closestBoundary]
|
|
257
|
+
: collisionBoundary;
|
|
258
|
+
}, [context.open, collisionBoundary, context.triggerRef.current]);
|
|
259
|
+
|
|
243
260
|
return (
|
|
244
261
|
<MenuPrimitive.Content
|
|
245
262
|
id={context.contentId}
|
|
246
263
|
aria-labelledby={context.triggerId}
|
|
247
264
|
{...menuScope}
|
|
248
265
|
{...contentProps}
|
|
266
|
+
collisionBoundary={computedCollisionBoundary}
|
|
249
267
|
collisionPadding={safeCollisionPadding}
|
|
250
268
|
ref={forwardedRef}
|
|
251
269
|
onCloseAutoFocus={composeEventHandlers(props.onCloseAutoFocus, (event) => {
|
|
@@ -264,6 +282,7 @@ const DropdownMenuContent = forwardRef<DropdownMenuContentElement, DropdownMenuC
|
|
|
264
282
|
hasInteractedOutsideRef.current = true;
|
|
265
283
|
}
|
|
266
284
|
})}
|
|
285
|
+
data-arrow-keys='up down'
|
|
267
286
|
className={tx('menu.content', 'menu', { elevation }, classNames)}
|
|
268
287
|
style={{
|
|
269
288
|
...props.style,
|
|
@@ -415,13 +434,21 @@ const RADIO_ITEM_NAME = 'DropdownMenuRadioItem';
|
|
|
415
434
|
|
|
416
435
|
type DropdownMenuRadioItemElement = ElementRef<typeof MenuPrimitive.RadioItem>;
|
|
417
436
|
type MenuRadioItemProps = ComponentPropsWithoutRef<typeof MenuPrimitive.RadioItem>;
|
|
418
|
-
|
|
437
|
+
type DropdownMenuRadioItemProps = ThemedClassName<MenuRadioItemProps>;
|
|
419
438
|
|
|
420
439
|
const DropdownMenuRadioItem = forwardRef<DropdownMenuRadioItemElement, DropdownMenuRadioItemProps>(
|
|
421
440
|
(props: ScopedProps<DropdownMenuRadioItemProps>, forwardedRef) => {
|
|
422
|
-
const { __scopeDropdownMenu, ...
|
|
441
|
+
const { __scopeDropdownMenu, classNames, ...itemProps } = props;
|
|
423
442
|
const menuScope = useMenuScope(__scopeDropdownMenu);
|
|
424
|
-
|
|
443
|
+
const { tx } = useThemeContext();
|
|
444
|
+
return (
|
|
445
|
+
<MenuPrimitive.Item
|
|
446
|
+
{...menuScope}
|
|
447
|
+
{...itemProps}
|
|
448
|
+
className={tx('menu.item', 'menu__item', {}, classNames)}
|
|
449
|
+
ref={forwardedRef}
|
|
450
|
+
/>
|
|
451
|
+
);
|
|
425
452
|
},
|
|
426
453
|
);
|
|
427
454
|
|
|
@@ -2,15 +2,13 @@
|
|
|
2
2
|
// Copyright 2022 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import '@dxos-theme';
|
|
6
|
-
|
|
7
5
|
import { type Meta, type StoryObj } from '@storybook/react-vite';
|
|
8
6
|
import React, { type PropsWithChildren, type ReactNode, useRef, useState } from 'react';
|
|
9
7
|
|
|
10
8
|
import { faker } from '@dxos/random';
|
|
11
9
|
|
|
12
10
|
import { withTheme } from '../../testing';
|
|
13
|
-
import { Button } from '../
|
|
11
|
+
import { Button } from '../Button';
|
|
14
12
|
|
|
15
13
|
import { Popover } from './Popover';
|
|
16
14
|
|
|
@@ -35,7 +33,6 @@ const meta = {
|
|
|
35
33
|
component: Popover.Root,
|
|
36
34
|
render: DefaultStory,
|
|
37
35
|
decorators: [withTheme],
|
|
38
|
-
parameters: { chromatic: { disableSnapshot: false } },
|
|
39
36
|
} satisfies Meta<typeof DefaultStory>;
|
|
40
37
|
|
|
41
38
|
export default meta;
|
|
@@ -31,6 +31,7 @@ import React, {
|
|
|
31
31
|
forwardRef,
|
|
32
32
|
useCallback,
|
|
33
33
|
useEffect,
|
|
34
|
+
useMemo,
|
|
34
35
|
useRef,
|
|
35
36
|
useState,
|
|
36
37
|
} from 'react';
|
|
@@ -182,7 +183,7 @@ PopoverTrigger.displayName = TRIGGER_NAME;
|
|
|
182
183
|
const VIRTUAL_TRIGGER_NAME = 'PopoverVirtualTrigger';
|
|
183
184
|
|
|
184
185
|
interface PopoverVirtualTriggerProps {
|
|
185
|
-
virtualRef: RefObject<PopoverTriggerElement>;
|
|
186
|
+
virtualRef: RefObject<PopoverTriggerElement | null>;
|
|
186
187
|
}
|
|
187
188
|
|
|
188
189
|
const PopoverVirtualTrigger = (props: ScopedProps<PopoverVirtualTriggerProps>) => {
|
|
@@ -194,7 +195,7 @@ const PopoverVirtualTrigger = (props: ScopedProps<PopoverVirtualTriggerProps>) =
|
|
|
194
195
|
context.triggerRef.current = virtualRef.current;
|
|
195
196
|
}
|
|
196
197
|
});
|
|
197
|
-
return <PopperPrimitive.Anchor {...popperScope} virtualRef={virtualRef} />;
|
|
198
|
+
return <PopperPrimitive.Anchor {...popperScope} virtualRef={virtualRef as RefObject<PopoverTriggerElement>} />;
|
|
198
199
|
};
|
|
199
200
|
|
|
200
201
|
PopoverVirtualTrigger.displayName = VIRTUAL_TRIGGER_NAME;
|
|
@@ -396,6 +397,7 @@ type PopoverContentImplElement = ElementRef<typeof PopperPrimitive.Content>;
|
|
|
396
397
|
type FocusScopeProps = ComponentPropsWithoutRef<typeof FocusScope>;
|
|
397
398
|
type DismissableLayerProps = ComponentPropsWithoutRef<typeof DismissableLayer>;
|
|
398
399
|
type PopperContentProps = ThemedClassName<ComponentPropsWithoutRef<typeof PopperPrimitive.Content>>;
|
|
400
|
+
|
|
399
401
|
interface PopoverContentImplProps
|
|
400
402
|
extends Omit<PopperContentProps, 'onPlaced'>,
|
|
401
403
|
Omit<DismissableLayerProps, 'onDismiss'> {
|
|
@@ -431,6 +433,7 @@ const PopoverContentImpl = forwardRef<PopoverContentImplElement, PopoverContentI
|
|
|
431
433
|
onFocusOutside,
|
|
432
434
|
onInteractOutside,
|
|
433
435
|
collisionPadding = 8,
|
|
436
|
+
collisionBoundary,
|
|
434
437
|
classNames,
|
|
435
438
|
...contentProps
|
|
436
439
|
} = props;
|
|
@@ -440,10 +443,23 @@ const PopoverContentImpl = forwardRef<PopoverContentImplElement, PopoverContentI
|
|
|
440
443
|
const elevation = useElevationContext();
|
|
441
444
|
const safeCollisionPadding = useSafeCollisionPadding(collisionPadding);
|
|
442
445
|
|
|
443
|
-
// Make sure the whole tree has focus guards as our `Popover` may be
|
|
444
|
-
// the last element in the DOM (because of the `Portal`)
|
|
446
|
+
// Make sure the whole tree has focus guards as our `Popover` may be the last element in the DOM (because of the `Portal`)
|
|
445
447
|
useFocusGuards();
|
|
446
448
|
|
|
449
|
+
// Check for the closest annotated collision boundary in the DOM tree.
|
|
450
|
+
const computedCollisionBoundary = useMemo(() => {
|
|
451
|
+
const closestBoundary = context.triggerRef.current?.closest(
|
|
452
|
+
'[data-popover-collision-boundary]',
|
|
453
|
+
) as HTMLElement | null;
|
|
454
|
+
return closestBoundary
|
|
455
|
+
? Array.isArray(collisionBoundary)
|
|
456
|
+
? [closestBoundary, ...collisionBoundary]
|
|
457
|
+
: collisionBoundary
|
|
458
|
+
? [closestBoundary, collisionBoundary]
|
|
459
|
+
: [closestBoundary]
|
|
460
|
+
: collisionBoundary;
|
|
461
|
+
}, [context.open, collisionBoundary, context.triggerRef.current]);
|
|
462
|
+
|
|
447
463
|
return (
|
|
448
464
|
<FocusScope
|
|
449
465
|
asChild
|
|
@@ -468,11 +484,12 @@ const PopoverContentImpl = forwardRef<PopoverContentImplElement, PopoverContentI
|
|
|
468
484
|
{...popperScope}
|
|
469
485
|
{...contentProps}
|
|
470
486
|
collisionPadding={safeCollisionPadding}
|
|
487
|
+
collisionBoundary={computedCollisionBoundary}
|
|
471
488
|
className={tx('popover.content', 'popover', { elevation }, classNames)}
|
|
472
489
|
ref={forwardedRef}
|
|
473
490
|
style={{
|
|
474
491
|
...contentProps.style,
|
|
475
|
-
//
|
|
492
|
+
// Re-namespace exposed content custom properties.
|
|
476
493
|
...{
|
|
477
494
|
'--radix-popover-content-transform-origin': 'var(--radix-popper-transform-origin)',
|
|
478
495
|
'--radix-popover-content-available-width': 'var(--radix-popper-available-width)',
|
|
@@ -2,8 +2,6 @@
|
|
|
2
2
|
// Copyright 2023 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import '@dxos-theme';
|
|
6
|
-
|
|
7
5
|
import { type Meta, type StoryObj } from '@storybook/react-vite';
|
|
8
6
|
import React, { type PropsWithChildren } from 'react';
|
|
9
7
|
|
|
@@ -36,11 +34,10 @@ const DefaultStory = ({ children }: PropsWithChildren<{}>) => {
|
|
|
36
34
|
};
|
|
37
35
|
|
|
38
36
|
const meta = {
|
|
39
|
-
title: 'ui/react-ui-core/
|
|
37
|
+
title: 'ui/react-ui-core/ScrollArea',
|
|
40
38
|
component: ScrollArea as any,
|
|
41
39
|
render: DefaultStory,
|
|
42
40
|
decorators: [withTheme],
|
|
43
|
-
parameters: { chromatic: { disableSnapshot: false } },
|
|
44
41
|
} satisfies Meta<typeof DefaultStory>;
|
|
45
42
|
|
|
46
43
|
export default meta;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { type Meta, type StoryObj } from '@storybook/react-vite';
|
|
6
|
+
import React, { useEffect, useRef, useState } from 'react';
|
|
7
|
+
|
|
8
|
+
import { faker } from '@dxos/random';
|
|
9
|
+
|
|
10
|
+
import { withLayout, withTheme } from '../../testing';
|
|
11
|
+
import { Button } from '../Button';
|
|
12
|
+
import { Toolbar } from '../Toolbar';
|
|
13
|
+
|
|
14
|
+
import { ScrollContainer, type ScrollContainerRootProps, type ScrollController } from './ScrollContainer';
|
|
15
|
+
|
|
16
|
+
const DefaultStory = (props: ScrollContainerRootProps) => {
|
|
17
|
+
const [lines, setLines] = useState<string[]>([]);
|
|
18
|
+
const [running, setRunning] = useState(true);
|
|
19
|
+
const scroller = useRef<ScrollController>(null);
|
|
20
|
+
useEffect(() => {
|
|
21
|
+
if (!running) {
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const i = setInterval(() => {
|
|
26
|
+
setLines((lines) => [...lines, faker.lorem.paragraph()]);
|
|
27
|
+
}, 500);
|
|
28
|
+
|
|
29
|
+
return () => clearInterval(i);
|
|
30
|
+
}, [running]);
|
|
31
|
+
|
|
32
|
+
return (
|
|
33
|
+
<div className='flex flex-col bs-full overflow-hidden'>
|
|
34
|
+
<Toolbar.Root>
|
|
35
|
+
<Button onClick={() => setRunning((running) => !running)}>{running ? 'Stop' : 'Start'}</Button>
|
|
36
|
+
<Button onClick={() => scroller.current?.scrollToBottom()}>Scroll to bottom</Button>
|
|
37
|
+
<div className='flex-1' />
|
|
38
|
+
<div>{lines.length}</div>
|
|
39
|
+
</Toolbar.Root>
|
|
40
|
+
<ScrollContainer.Root {...props} ref={scroller}>
|
|
41
|
+
<ScrollContainer.Viewport>
|
|
42
|
+
{lines.map((line, index) => (
|
|
43
|
+
<div key={index} className='p-2'>
|
|
44
|
+
{line}
|
|
45
|
+
</div>
|
|
46
|
+
))}
|
|
47
|
+
</ScrollContainer.Viewport>
|
|
48
|
+
</ScrollContainer.Root>
|
|
49
|
+
</div>
|
|
50
|
+
);
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
const meta = {
|
|
54
|
+
title: 'ui/react-ui-core/ScrollContainer',
|
|
55
|
+
component: ScrollContainer.Root,
|
|
56
|
+
render: DefaultStory,
|
|
57
|
+
decorators: [withTheme, withLayout({ container: 'column', classNames: 'is-[30rem]' })],
|
|
58
|
+
} satisfies Meta<typeof DefaultStory>;
|
|
59
|
+
|
|
60
|
+
export default meta;
|
|
61
|
+
|
|
62
|
+
type Story = StoryObj<typeof meta>;
|
|
63
|
+
|
|
64
|
+
export const Default: Story = {
|
|
65
|
+
args: {
|
|
66
|
+
pin: true,
|
|
67
|
+
fade: true,
|
|
68
|
+
},
|
|
69
|
+
};
|