@dxos/react-ui 0.8.4-main.21d9917 → 0.8.4-main.2244d791bb
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-CEKVHJ27.mjs → chunk-6DTBPJE4.mjs} +4 -4
- package/dist/lib/browser/chunk-6DTBPJE4.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +456 -309
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/testing/index.mjs +5 -4
- package/dist/lib/browser/testing/index.mjs.map +3 -3
- package/dist/lib/node-esm/{chunk-2NHEX4AD.mjs → chunk-JKHQSGNU.mjs} +4 -4
- package/dist/lib/node-esm/chunk-JKHQSGNU.mjs.map +7 -0
- package/dist/lib/node-esm/index.mjs +456 -309
- package/dist/lib/node-esm/index.mjs.map +4 -4
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/lib/node-esm/testing/index.mjs +5 -4
- package/dist/lib/node-esm/testing/index.mjs.map +3 -3
- package/dist/types/src/components/AnchoredOverflow/AnchoredOverflow.d.ts +7 -0
- package/dist/types/src/components/AnchoredOverflow/AnchoredOverflow.d.ts.map +1 -1
- package/dist/types/src/components/Avatars/Avatar.d.ts.map +1 -1
- package/dist/types/src/components/Breadcrumb/Breadcrumb.d.ts.map +1 -1
- package/dist/types/src/components/Button/Button.d.ts.map +1 -1
- package/dist/types/src/components/Dialog/AlertDialog.d.ts +4 -2
- package/dist/types/src/components/Dialog/AlertDialog.d.ts.map +1 -1
- package/dist/types/src/components/Dialog/AlertDialog.stories.d.ts.map +1 -1
- package/dist/types/src/components/Dialog/Dialog.d.ts +7 -1
- package/dist/types/src/components/Dialog/Dialog.d.ts.map +1 -1
- package/dist/types/src/components/Dialog/Dialog.stories.d.ts +1 -5
- package/dist/types/src/components/Dialog/Dialog.stories.d.ts.map +1 -1
- package/dist/types/src/components/Input/Input.d.ts.map +1 -1
- package/dist/types/src/components/Input/Input.stories.d.ts +1 -1
- package/dist/types/src/components/Input/Input.stories.d.ts.map +1 -1
- package/dist/types/src/components/List/List.d.ts.map +1 -1
- package/dist/types/src/components/List/Treegrid.d.ts.map +1 -1
- package/dist/types/src/components/Main/Main.d.ts +8 -9
- package/dist/types/src/components/Main/Main.d.ts.map +1 -1
- package/dist/types/src/components/Main/Main.stories.d.ts +0 -3
- package/dist/types/src/components/Main/Main.stories.d.ts.map +1 -1
- package/dist/types/src/components/Menu/ContextMenu.d.ts.map +1 -1
- package/dist/types/src/components/Message/Message.d.ts.map +1 -1
- package/dist/types/src/components/Message/Message.stories.d.ts +2 -3
- package/dist/types/src/components/Message/Message.stories.d.ts.map +1 -1
- package/dist/types/src/components/ScrollArea/ScrollArea.d.ts +23 -26
- package/dist/types/src/components/ScrollArea/ScrollArea.d.ts.map +1 -1
- package/dist/types/src/components/ScrollArea/ScrollArea.stories.d.ts +43 -9
- package/dist/types/src/components/ScrollArea/ScrollArea.stories.d.ts.map +1 -1
- package/dist/types/src/components/ScrollContainer/ScrollContainer.d.ts.map +1 -1
- package/dist/types/src/components/ScrollContainer/ScrollContainer.stories.d.ts +6 -1
- package/dist/types/src/components/ScrollContainer/ScrollContainer.stories.d.ts.map +1 -1
- package/dist/types/src/components/Select/Select.d.ts.map +1 -1
- package/dist/types/src/components/Skeleton/Skeleton.d.ts +12 -0
- package/dist/types/src/components/Skeleton/Skeleton.d.ts.map +1 -0
- package/dist/types/src/components/Skeleton/Skeleton.stories.d.ts +17 -0
- package/dist/types/src/components/Skeleton/Skeleton.stories.d.ts.map +1 -0
- package/dist/types/src/components/Skeleton/index.d.ts +2 -0
- package/dist/types/src/components/Skeleton/index.d.ts.map +1 -0
- package/dist/types/src/components/Splitter/Splitter.d.ts +26 -0
- package/dist/types/src/components/Splitter/Splitter.d.ts.map +1 -0
- package/dist/types/src/components/Splitter/Splitter.stories.d.ts +7 -0
- package/dist/types/src/components/Splitter/Splitter.stories.d.ts.map +1 -0
- package/dist/types/src/components/Splitter/index.d.ts +2 -0
- package/dist/types/src/components/Splitter/index.d.ts.map +1 -0
- package/dist/types/src/components/Tag/Tag.d.ts.map +1 -1
- package/dist/types/src/components/Tag/Tag.stories.d.ts +1 -5
- package/dist/types/src/components/Tag/Tag.stories.d.ts.map +1 -1
- package/dist/types/src/components/ThemeProvider/ThemeProvider.d.ts +1 -0
- package/dist/types/src/components/ThemeProvider/ThemeProvider.d.ts.map +1 -1
- package/dist/types/src/components/Toast/Toast.d.ts.map +1 -1
- package/dist/types/src/components/Toolbar/Toolbar.d.ts +7 -6
- package/dist/types/src/components/Toolbar/Toolbar.d.ts.map +1 -1
- package/dist/types/src/components/index.d.ts +2 -0
- package/dist/types/src/components/index.d.ts.map +1 -1
- package/dist/types/src/exemplars/generics.stories.d.ts +17 -0
- package/dist/types/src/exemplars/generics.stories.d.ts.map +1 -0
- package/dist/types/src/exemplars/slot.stories.d.ts +14 -0
- package/dist/types/src/exemplars/slot.stories.d.ts.map +1 -0
- package/dist/types/src/exemplars/tabster.stories.d.ts +8 -0
- package/dist/types/src/exemplars/tabster.stories.d.ts.map +1 -0
- package/dist/types/src/exemplars/virtualizer.stories.d.ts +11 -0
- package/dist/types/src/exemplars/virtualizer.stories.d.ts.map +1 -0
- package/dist/types/src/index.d.ts +1 -0
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/playground/Controls.stories.d.ts.map +1 -1
- package/dist/types/src/primitives/Container/Container.d.ts +23 -0
- package/dist/types/src/primitives/Container/Container.d.ts.map +1 -0
- package/dist/types/src/primitives/Container/Container.stories.d.ts +11 -0
- package/dist/types/src/primitives/Container/Container.stories.d.ts.map +1 -0
- package/dist/types/src/primitives/Container/Layout.d.ts +18 -0
- package/dist/types/src/primitives/Container/Layout.d.ts.map +1 -0
- package/dist/types/src/primitives/Container/Layout.stories.d.ts +10 -0
- package/dist/types/src/primitives/Container/Layout.stories.d.ts.map +1 -0
- package/dist/types/src/primitives/Container/index.d.ts +3 -0
- package/dist/types/src/primitives/Container/index.d.ts.map +1 -0
- package/dist/types/src/primitives/Flex/Flex.d.ts +8 -0
- package/dist/types/src/primitives/Flex/Flex.d.ts.map +1 -0
- package/dist/types/src/primitives/Flex/index.d.ts +2 -0
- package/dist/types/src/primitives/Flex/index.d.ts.map +1 -0
- package/dist/types/src/primitives/index.d.ts +3 -0
- package/dist/types/src/primitives/index.d.ts.map +1 -0
- package/dist/types/src/testing/decorators/withTheme.d.ts +3 -2
- package/dist/types/src/testing/decorators/withTheme.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +23 -22
- package/src/components/AnchoredOverflow/AnchoredOverflow.tsx +10 -12
- package/src/components/Avatars/Avatar.stories.tsx +2 -2
- package/src/components/Avatars/Avatar.tsx +2 -9
- package/src/components/Avatars/AvatarGroup.stories.tsx +2 -2
- package/src/components/Breadcrumb/Breadcrumb.stories.tsx +2 -2
- package/src/components/Breadcrumb/Breadcrumb.tsx +5 -31
- package/src/components/Button/Button.stories.tsx +3 -3
- package/src/components/Button/Button.tsx +1 -7
- package/src/components/Button/IconButton.stories.tsx +2 -2
- package/src/components/Button/IconButton.tsx +1 -1
- package/src/components/Button/Toggle.stories.tsx +2 -2
- package/src/components/Button/ToggleGroup.stories.tsx +2 -2
- package/src/components/Dialog/AlertDialog.stories.tsx +6 -7
- package/src/components/Dialog/AlertDialog.tsx +78 -9
- package/src/components/Dialog/Dialog.stories.tsx +11 -11
- package/src/components/Dialog/Dialog.tsx +44 -19
- package/src/components/Icon/Icon.stories.tsx +2 -2
- package/src/components/Icon/Icon.tsx +1 -1
- package/src/components/Input/Input.stories.tsx +11 -10
- package/src/components/Input/Input.tsx +10 -25
- package/src/components/Link/Link.stories.tsx +2 -2
- package/src/components/Link/Link.tsx +1 -1
- package/src/components/List/List.stories.tsx +2 -2
- package/src/components/List/List.tsx +7 -13
- package/src/components/List/Tree.stories.tsx +2 -2
- package/src/components/List/Treegrid.stories.tsx +2 -2
- package/src/components/List/Treegrid.tsx +4 -9
- package/src/components/Main/Main.stories.tsx +41 -23
- package/src/components/Main/Main.tsx +128 -71
- package/src/components/Menu/ContextMenu.stories.tsx +2 -2
- package/src/components/Menu/ContextMenu.tsx +7 -31
- package/src/components/Menu/DropdownMenu.stories.tsx +2 -2
- package/src/components/Menu/DropdownMenu.tsx +8 -8
- package/src/components/Message/Message.stories.tsx +23 -8
- package/src/components/Message/Message.tsx +8 -21
- package/src/components/Popover/Popover.stories.tsx +2 -2
- package/src/components/Popover/Popover.tsx +3 -3
- package/src/components/ScrollArea/ScrollArea.stories.tsx +152 -76
- package/src/components/ScrollArea/ScrollArea.tsx +68 -116
- package/src/components/ScrollArea/index.ts +1 -1
- package/src/components/ScrollContainer/ScrollContainer.stories.tsx +24 -9
- package/src/components/ScrollContainer/ScrollContainer.tsx +14 -9
- package/src/components/Select/Select.stories.tsx +2 -2
- package/src/components/Select/Select.tsx +9 -25
- package/src/components/Separator/Separator.tsx +1 -1
- package/src/components/Skeleton/Skeleton.stories.tsx +52 -0
- package/src/components/Skeleton/Skeleton.tsx +26 -0
- package/src/components/Skeleton/index.ts +5 -0
- package/src/components/Splitter/Splitter.stories.tsx +73 -0
- package/src/components/Splitter/Splitter.tsx +123 -0
- package/src/components/Splitter/index.ts +5 -0
- package/src/components/Status/Status.stories.tsx +2 -2
- package/src/components/Status/Status.tsx +2 -2
- package/src/components/Tag/Tag.stories.tsx +3 -7
- package/src/components/Tag/Tag.tsx +1 -6
- package/src/components/ThemeProvider/ThemeProvider.tsx +2 -1
- package/src/components/Toast/Toast.stories.tsx +2 -2
- package/src/components/Toast/Toast.tsx +6 -10
- package/src/components/Toolbar/Toolbar.stories.tsx +2 -2
- package/src/components/Toolbar/Toolbar.tsx +13 -9
- package/src/components/Tooltip/Tooltip.stories.tsx +2 -2
- package/src/components/Tooltip/Tooltip.tsx +2 -2
- package/src/components/index.ts +2 -0
- package/src/exemplars/generics.stories.tsx +44 -0
- package/src/exemplars/slot.stories.tsx +108 -0
- package/src/exemplars/tabster.stories.tsx +127 -0
- package/src/exemplars/virtualizer.stories.tsx +133 -0
- package/src/index.ts +1 -0
- package/src/playground/Controls.stories.tsx +3 -4
- package/src/playground/Custom.stories.tsx +2 -2
- package/src/playground/Typography.stories.tsx +2 -2
- package/src/primitives/Container/Container.stories.tsx +67 -0
- package/src/primitives/Container/Container.tsx +79 -0
- package/src/primitives/Container/Layout.stories.tsx +57 -0
- package/src/primitives/Container/Layout.tsx +61 -0
- package/src/primitives/Container/index.ts +6 -0
- package/src/primitives/Flex/Flex.tsx +26 -0
- package/src/primitives/Flex/index.ts +5 -0
- package/src/primitives/index.ts +6 -0
- package/src/testing/decorators/withLayoutVariants.tsx +1 -1
- package/src/testing/decorators/withTheme.tsx +19 -17
- package/dist/lib/browser/chunk-CEKVHJ27.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-2NHEX4AD.mjs.map +0 -7
|
@@ -485,7 +485,7 @@ const PopoverContentImpl = forwardRef<PopoverContentImplElement, PopoverContentI
|
|
|
485
485
|
{...contentProps}
|
|
486
486
|
collisionPadding={safeCollisionPadding}
|
|
487
487
|
collisionBoundary={computedCollisionBoundary}
|
|
488
|
-
className={tx('popover.content',
|
|
488
|
+
className={tx('popover.content', { elevation }, classNames)}
|
|
489
489
|
ref={forwardedRef}
|
|
490
490
|
style={{
|
|
491
491
|
...contentProps.style,
|
|
@@ -550,7 +550,7 @@ const PopoverArrow = forwardRef<PopoverArrowElement, PopoverArrowProps>(
|
|
|
550
550
|
<PopperPrimitive.Arrow
|
|
551
551
|
{...popperScope}
|
|
552
552
|
{...arrowProps}
|
|
553
|
-
className={tx('popover.arrow',
|
|
553
|
+
className={tx('popover.arrow', {}, classNames)}
|
|
554
554
|
ref={forwardedRef}
|
|
555
555
|
/>
|
|
556
556
|
);
|
|
@@ -576,7 +576,7 @@ const PopoverViewport = forwardRef<HTMLDivElement, PopoverViewportProps>(
|
|
|
576
576
|
return (
|
|
577
577
|
<Root
|
|
578
578
|
{...props}
|
|
579
|
-
className={tx('popover.viewport',
|
|
579
|
+
className={tx('popover.viewport', { constrainInline, constrainBlock }, classNames)}
|
|
580
580
|
ref={forwardedRef}
|
|
581
581
|
>
|
|
582
582
|
{children}
|
|
@@ -1,101 +1,177 @@
|
|
|
1
1
|
//
|
|
2
|
-
// Copyright
|
|
2
|
+
// Copyright 2026 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import
|
|
6
|
-
import React, { type PropsWithChildren } from 'react';
|
|
5
|
+
import React, { useMemo } from 'react';
|
|
7
6
|
|
|
8
7
|
import { faker } from '@dxos/random';
|
|
9
|
-
import {
|
|
8
|
+
import { mx } from '@dxos/ui-theme';
|
|
10
9
|
|
|
11
10
|
import { withLayout, withTheme } from '../../testing';
|
|
12
11
|
|
|
13
12
|
import { ScrollArea } from './ScrollArea';
|
|
14
13
|
|
|
15
|
-
faker.seed(
|
|
16
|
-
|
|
17
|
-
const DefaultStory = ({ children }: PropsWithChildren<{}>) => {
|
|
18
|
-
return (
|
|
19
|
-
<ScrollArea.Root
|
|
20
|
-
classNames={['is-[300px] bs-[400px] rounded', activeSurface, surfaceShadow({ elevation: 'positioned' })]}
|
|
21
|
-
>
|
|
22
|
-
<ScrollArea.Viewport classNames='rounded p-4'>
|
|
23
|
-
<p>{children}</p>
|
|
24
|
-
</ScrollArea.Viewport>
|
|
25
|
-
<ScrollArea.Scrollbar orientation='horizontal'>
|
|
26
|
-
<ScrollArea.Thumb />
|
|
27
|
-
</ScrollArea.Scrollbar>
|
|
28
|
-
<ScrollArea.Scrollbar orientation='vertical'>
|
|
29
|
-
<ScrollArea.Thumb />
|
|
30
|
-
</ScrollArea.Scrollbar>
|
|
31
|
-
<ScrollArea.Corner />
|
|
32
|
-
</ScrollArea.Root>
|
|
33
|
-
);
|
|
34
|
-
};
|
|
14
|
+
faker.seed(123);
|
|
35
15
|
|
|
36
|
-
|
|
37
|
-
title: 'ui/react-ui-core/ScrollArea',
|
|
38
|
-
component: ScrollArea
|
|
39
|
-
|
|
40
|
-
decorators: [withTheme, withLayout({ layout: 'fullscreen' })],
|
|
16
|
+
export default {
|
|
17
|
+
title: 'ui/react-ui-core/components/ScrollArea',
|
|
18
|
+
component: ScrollArea,
|
|
19
|
+
decorators: [withTheme()],
|
|
41
20
|
parameters: {
|
|
42
|
-
layout: '
|
|
21
|
+
layout: 'centered',
|
|
43
22
|
},
|
|
44
|
-
}
|
|
23
|
+
};
|
|
45
24
|
|
|
46
|
-
|
|
25
|
+
const Column = () => (
|
|
26
|
+
<div>
|
|
27
|
+
{Array.from({ length: 50 }).map((_, index) => (
|
|
28
|
+
<div key={index} className='pli-1 text-sm cursor-pointer hover:bg-hoverSurface'>
|
|
29
|
+
Item {index + 1}
|
|
30
|
+
</div>
|
|
31
|
+
))}
|
|
32
|
+
</div>
|
|
33
|
+
);
|
|
47
34
|
|
|
48
|
-
|
|
35
|
+
const Row = () => (
|
|
36
|
+
<div className='flex gap-2 is-max'>
|
|
37
|
+
{Array.from({ length: 50 }).map((_, index) => (
|
|
38
|
+
<div
|
|
39
|
+
key={index}
|
|
40
|
+
className='shrink-0 bs-20 is-20 cursor-pointer border border-separator rounded-md flex items-center justify-center text-sm hover:bg-hoverSurface'
|
|
41
|
+
>
|
|
42
|
+
{index + 1}
|
|
43
|
+
</div>
|
|
44
|
+
))}
|
|
45
|
+
</div>
|
|
46
|
+
);
|
|
49
47
|
|
|
50
|
-
export const
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
48
|
+
export const Vertical = {
|
|
49
|
+
render: () => (
|
|
50
|
+
<div className='bs-72 is-48 p-2 border border-separator rounded-md'>
|
|
51
|
+
<ScrollArea.Root orientation='vertical' padding>
|
|
52
|
+
<ScrollArea.Viewport>
|
|
53
|
+
<Column />
|
|
54
|
+
</ScrollArea.Viewport>
|
|
55
|
+
</ScrollArea.Root>
|
|
56
|
+
</div>
|
|
57
|
+
),
|
|
54
58
|
};
|
|
55
59
|
|
|
56
|
-
export const
|
|
57
|
-
render: () =>
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
60
|
+
export const VerticalThin = {
|
|
61
|
+
render: () => (
|
|
62
|
+
<div className='bs-72 is-48 p-2 border border-separator rounded-md'>
|
|
63
|
+
<ScrollArea.Root orientation='vertical' padding thin>
|
|
64
|
+
<ScrollArea.Viewport>
|
|
65
|
+
<Column />
|
|
66
|
+
</ScrollArea.Viewport>
|
|
67
|
+
</ScrollArea.Root>
|
|
68
|
+
</div>
|
|
69
|
+
),
|
|
70
|
+
};
|
|
62
71
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
72
|
+
export const Horizontal = {
|
|
73
|
+
render: () => (
|
|
74
|
+
<div className='is-96 p-2 border border-separator rounded-md'>
|
|
75
|
+
<ScrollArea.Root orientation='horizontal' padding>
|
|
76
|
+
<ScrollArea.Viewport>
|
|
77
|
+
<Row />
|
|
78
|
+
</ScrollArea.Viewport>
|
|
79
|
+
</ScrollArea.Root>
|
|
80
|
+
</div>
|
|
81
|
+
),
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
export const HorizontalThin = {
|
|
85
|
+
render: () => (
|
|
86
|
+
<div className='is-96 p-2 border border-separator rounded-md'>
|
|
87
|
+
<ScrollArea.Root orientation='horizontal' padding thin>
|
|
88
|
+
<ScrollArea.Viewport>
|
|
89
|
+
<Row />
|
|
90
|
+
</ScrollArea.Viewport>
|
|
91
|
+
</ScrollArea.Root>
|
|
92
|
+
</div>
|
|
93
|
+
),
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
export const Both = {
|
|
97
|
+
render: () => (
|
|
98
|
+
<div className='bs-96 is-96 p-2 border border-separator rounded-md'>
|
|
99
|
+
<ScrollArea.Root orientation='all' padding>
|
|
100
|
+
<ScrollArea.Viewport>
|
|
101
|
+
<div className='flex flex-col gap-2'>
|
|
102
|
+
{Array.from({ length: 50 }).map((_, rowIndex) => (
|
|
103
|
+
<div key={rowIndex} className='flex gap-2'>
|
|
104
|
+
{Array.from({ length: 50 }).map((_, colIndex) => (
|
|
105
|
+
<div
|
|
106
|
+
key={colIndex}
|
|
107
|
+
className='shrink-0 bs-20 is-20 flex items-center justify-center text-sm border border-separator font-mono'
|
|
108
|
+
>
|
|
109
|
+
[{colIndex}:{rowIndex}]
|
|
89
110
|
</div>
|
|
90
111
|
))}
|
|
91
112
|
</div>
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
113
|
+
))}
|
|
114
|
+
</div>
|
|
115
|
+
</ScrollArea.Viewport>
|
|
116
|
+
</ScrollArea.Root>
|
|
117
|
+
</div>
|
|
118
|
+
),
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
export const NestedScrollAreas = {
|
|
122
|
+
decorators: [withTheme(), withLayout({ layout: 'fullscreen' })],
|
|
123
|
+
render: () => {
|
|
124
|
+
const columns = useMemo(
|
|
125
|
+
() =>
|
|
126
|
+
Array.from({ length: 8 }).map((_, index) => ({
|
|
127
|
+
id: String(index),
|
|
128
|
+
count: faker.number.int({ min: 5, max: 20 }),
|
|
129
|
+
})),
|
|
130
|
+
[],
|
|
131
|
+
);
|
|
132
|
+
|
|
133
|
+
return (
|
|
134
|
+
<ScrollArea.Root thin orientation='horizontal'>
|
|
135
|
+
<ScrollArea.Viewport classNames='gap-4'>
|
|
136
|
+
{columns.map((column) => (
|
|
137
|
+
<section
|
|
138
|
+
key={column.id}
|
|
139
|
+
className='shrink-0 bs-full is-[16rem] grid grid-rows-[min-content_1fr_min-content] border border-separator'
|
|
140
|
+
>
|
|
141
|
+
<header className='flex shrink-0 p-2 border-be border-separator'>Column {column.id}</header>
|
|
142
|
+
<ScrollArea.Root thin orientation='vertical'>
|
|
143
|
+
<ScrollArea.Viewport classNames='plb-2 pli-2 gap-2'>
|
|
144
|
+
{Array.from({ length: column.count }, (_, i) => (
|
|
145
|
+
<div key={i} role='listitem' className={`shrink-0 p-2 text-sm border border-separator rounded-sm`}>
|
|
146
|
+
Item {i + 1}
|
|
147
|
+
</div>
|
|
148
|
+
))}
|
|
149
|
+
</ScrollArea.Viewport>
|
|
150
|
+
</ScrollArea.Root>
|
|
151
|
+
<footer className={`p-2 text-subdued border-bs border-separator`}>{column.count}</footer>
|
|
152
|
+
</section>
|
|
153
|
+
))}
|
|
154
|
+
</ScrollArea.Viewport>
|
|
155
|
+
</ScrollArea.Root>
|
|
99
156
|
);
|
|
100
157
|
},
|
|
101
158
|
};
|
|
159
|
+
|
|
160
|
+
export const NativeScroll = {
|
|
161
|
+
render: () => (
|
|
162
|
+
<div className='group bs-48 is-48 border border-separator'>
|
|
163
|
+
<div
|
|
164
|
+
className={mx(
|
|
165
|
+
'group bs-full is-full overflow-y-scroll',
|
|
166
|
+
'[&::-webkit-scrollbar]:is-3',
|
|
167
|
+
'[&::-webkit-scrollbar-thumb]:rounded-none',
|
|
168
|
+
'[&::-webkit-scrollbar-track]:bg-scrollbarTrack',
|
|
169
|
+
'[&::-webkit-scrollbar-thumb]:bg-scrollbarThumbSubdued',
|
|
170
|
+
'group-hover:[&::-webkit-scrollbar-thumb]:bg-scrollbarThumb',
|
|
171
|
+
)}
|
|
172
|
+
>
|
|
173
|
+
<Column />
|
|
174
|
+
</div>
|
|
175
|
+
</div>
|
|
176
|
+
),
|
|
177
|
+
};
|
|
@@ -1,138 +1,101 @@
|
|
|
1
1
|
//
|
|
2
|
-
// Copyright
|
|
2
|
+
// Copyright 2026 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
type ScrollAreaCornerProps as ScrollAreaPrimitiveCornerProps,
|
|
8
|
-
Root as ScrollAreaPrimitiveRoot,
|
|
9
|
-
type ScrollAreaProps as ScrollAreaPrimitiveRootProps,
|
|
10
|
-
Scrollbar as ScrollAreaPrimitiveScrollbar,
|
|
11
|
-
type ScrollAreaScrollbarProps as ScrollAreaPrimitiveScrollbarProps,
|
|
12
|
-
Thumb as ScrollAreaPrimitiveThumb,
|
|
13
|
-
type ScrollAreaThumbProps as ScrollAreaPrimitiveThumbProps,
|
|
14
|
-
Viewport as ScrollAreaPrimitiveViewport,
|
|
15
|
-
type ScrollAreaViewportProps as ScrollAreaPrimitiveViewportProps,
|
|
16
|
-
} from '@radix-ui/react-scroll-area';
|
|
17
|
-
import React, { type PropsWithChildren, forwardRef } from 'react';
|
|
18
|
-
|
|
19
|
-
import { mx } from '@dxos/ui-theme';
|
|
5
|
+
import { createContext } from '@radix-ui/react-context';
|
|
6
|
+
import React, { type HTMLAttributes, forwardRef } from 'react';
|
|
20
7
|
|
|
21
|
-
import {
|
|
22
|
-
import { type ThemedClassName } from '../../util';
|
|
8
|
+
import { type AllowedAxis, type SlottableProps, type ThemedClassName } from '@dxos/ui-types';
|
|
23
9
|
|
|
24
|
-
|
|
10
|
+
import { useThemeContext } from '../../hooks';
|
|
25
11
|
|
|
26
12
|
//
|
|
27
|
-
//
|
|
13
|
+
// Context
|
|
28
14
|
//
|
|
29
15
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
const ScrollAreaRoot = forwardRef<HTMLDivElement, ScrollAreaRootProps>(({ classNames, ...props }, forwardedRef) => {
|
|
33
|
-
const { tx } = useThemeContext();
|
|
34
|
-
return (
|
|
35
|
-
<ScrollAreaPrimitiveRoot
|
|
36
|
-
{...props}
|
|
37
|
-
className={tx('scrollArea.root', 'scroll-area', {}, classNames)}
|
|
38
|
-
ref={forwardedRef}
|
|
39
|
-
/>
|
|
40
|
-
);
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
//
|
|
44
|
-
// Viewport
|
|
45
|
-
//
|
|
16
|
+
const SCROLLAREA_NAME = 'ScrollArea';
|
|
46
17
|
|
|
47
|
-
type
|
|
18
|
+
type ScrollAreaContextType = {
|
|
19
|
+
/** Orientation of scrollbars. */
|
|
20
|
+
orientation: AllowedAxis;
|
|
21
|
+
/** Hide scrollbars when not scrolling. */
|
|
22
|
+
autoHide: boolean;
|
|
23
|
+
/** Apply padding to opposite side of scrollbar. */
|
|
24
|
+
margin?: boolean;
|
|
25
|
+
/** Apply padding. */
|
|
26
|
+
padding: boolean;
|
|
27
|
+
/** Use thin scrollbars. */
|
|
28
|
+
thin: boolean;
|
|
29
|
+
/** Enable snap scrolling. */
|
|
30
|
+
snap: boolean;
|
|
31
|
+
};
|
|
48
32
|
|
|
49
|
-
const
|
|
50
|
-
({ classNames, ...props }, forwardedRef) => {
|
|
51
|
-
const { tx } = useThemeContext();
|
|
52
|
-
return (
|
|
53
|
-
<ScrollAreaPrimitiveViewport
|
|
54
|
-
{...props}
|
|
55
|
-
className={tx('scrollArea.viewport', 'scroll-area', {}, classNames)}
|
|
56
|
-
ref={forwardedRef}
|
|
57
|
-
/>
|
|
58
|
-
);
|
|
59
|
-
},
|
|
60
|
-
);
|
|
33
|
+
const [ScrollAreaProvider, useScrollAreaContext] = createContext<ScrollAreaContextType>(SCROLLAREA_NAME);
|
|
61
34
|
|
|
62
35
|
//
|
|
63
|
-
//
|
|
36
|
+
// Root
|
|
64
37
|
//
|
|
65
38
|
|
|
66
|
-
|
|
39
|
+
const SCROLLAREA_ROOT_NAME = 'ScrollArea.Root';
|
|
40
|
+
|
|
41
|
+
type ScrollAreaRootProps = SlottableProps<HTMLDivElement> & Partial<ScrollAreaContextType>;
|
|
67
42
|
|
|
68
|
-
|
|
69
|
-
|
|
43
|
+
/**
|
|
44
|
+
* ScrollArea provides native scrollbars with custom styling.
|
|
45
|
+
*/
|
|
46
|
+
const ScrollAreaRoot = forwardRef<HTMLDivElement, ScrollAreaRootProps>(
|
|
47
|
+
(
|
|
48
|
+
{
|
|
49
|
+
classNames,
|
|
50
|
+
className,
|
|
51
|
+
children,
|
|
52
|
+
orientation = 'vertical',
|
|
53
|
+
autoHide = true,
|
|
54
|
+
margin = true, // TODO(burdon): Is this the right default?
|
|
55
|
+
padding = false,
|
|
56
|
+
thin = false,
|
|
57
|
+
snap = false,
|
|
58
|
+
...props
|
|
59
|
+
},
|
|
60
|
+
forwardedRef,
|
|
61
|
+
) => {
|
|
70
62
|
const { tx } = useThemeContext();
|
|
63
|
+
const options = { orientation, autoHide, margin, padding, thin, snap };
|
|
64
|
+
|
|
71
65
|
return (
|
|
72
|
-
<
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
/>
|
|
66
|
+
<ScrollAreaProvider {...options}>
|
|
67
|
+
<div {...props} className={tx('scrollArea.root', options, [className, classNames])} ref={forwardedRef}>
|
|
68
|
+
{children}
|
|
69
|
+
</div>
|
|
70
|
+
</ScrollAreaProvider>
|
|
78
71
|
);
|
|
79
72
|
},
|
|
80
73
|
);
|
|
81
74
|
|
|
82
|
-
|
|
83
|
-
// Thumb
|
|
84
|
-
//
|
|
85
|
-
|
|
86
|
-
type ScrollAreaThumbProps = ThemedClassName<ScrollAreaPrimitiveThumbProps>;
|
|
87
|
-
|
|
88
|
-
const ScrollAreaThumb = forwardRef<HTMLDivElement, ScrollAreaThumbProps>(({ classNames, ...props }, forwardedRef) => {
|
|
89
|
-
const { tx } = useThemeContext();
|
|
90
|
-
return (
|
|
91
|
-
<ScrollAreaPrimitiveThumb
|
|
92
|
-
{...props}
|
|
93
|
-
className={tx('scrollArea.thumb', 'scroll-area__thumb', {}, classNames)}
|
|
94
|
-
ref={forwardedRef}
|
|
95
|
-
/>
|
|
96
|
-
);
|
|
97
|
-
});
|
|
75
|
+
ScrollAreaRoot.displayName = SCROLLAREA_ROOT_NAME;
|
|
98
76
|
|
|
99
77
|
//
|
|
100
|
-
//
|
|
78
|
+
// Viewport
|
|
101
79
|
//
|
|
102
80
|
|
|
103
|
-
|
|
81
|
+
const SCROLLAREA_VIEWPORT_NAME = 'ScrollArea.Viewport';
|
|
104
82
|
|
|
105
|
-
|
|
106
|
-
const { tx } = useThemeContext();
|
|
107
|
-
return (
|
|
108
|
-
<ScrollAreaPrimitiveCorner
|
|
109
|
-
{...props}
|
|
110
|
-
className={tx('scrollArea.corner', 'scroll-area__corner', {}, classNames)}
|
|
111
|
-
ref={forwardedRef}
|
|
112
|
-
/>
|
|
113
|
-
);
|
|
114
|
-
});
|
|
83
|
+
type ScrollAreaViewportProps = ThemedClassName<HTMLAttributes<HTMLDivElement>>;
|
|
115
84
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
type ScrollAreaExpanderProps = ThemedClassName<PropsWithChildren>;
|
|
85
|
+
const ScrollAreaViewport = forwardRef<HTMLDivElement, ScrollAreaViewportProps>(
|
|
86
|
+
({ classNames, children, ...props }, forwardedRef) => {
|
|
87
|
+
const { tx } = useThemeContext();
|
|
88
|
+
const options = useScrollAreaContext(SCROLLAREA_VIEWPORT_NAME);
|
|
121
89
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
* NOTE: Radix ScrollArea.Viewport applies `display: table` to its immediate child,
|
|
125
|
-
* causing the content to participate in table layout and escape the intended fixed-size viewport.
|
|
126
|
-
*/
|
|
127
|
-
const ScrollAreaExpander = ({ classNames, children }: ScrollAreaExpanderProps) => {
|
|
128
|
-
return (
|
|
129
|
-
<div role='none' className={mx('relative bs-full is-full overflow-hidden', classNames)}>
|
|
130
|
-
<div role='none' className='absolute inset-0 overflow-hidden'>
|
|
90
|
+
return (
|
|
91
|
+
<div {...props} className={tx('scrollArea.viewport', options, classNames)} ref={forwardedRef}>
|
|
131
92
|
{children}
|
|
132
93
|
</div>
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
94
|
+
);
|
|
95
|
+
},
|
|
96
|
+
);
|
|
97
|
+
|
|
98
|
+
ScrollAreaViewport.displayName = SCROLLAREA_VIEWPORT_NAME;
|
|
136
99
|
|
|
137
100
|
//
|
|
138
101
|
// ScrollArea
|
|
@@ -141,17 +104,6 @@ const ScrollAreaExpander = ({ classNames, children }: ScrollAreaExpanderProps) =
|
|
|
141
104
|
export const ScrollArea = {
|
|
142
105
|
Root: ScrollAreaRoot,
|
|
143
106
|
Viewport: ScrollAreaViewport,
|
|
144
|
-
Scrollbar: ScrollAreaScrollbar,
|
|
145
|
-
Thumb: ScrollAreaThumb,
|
|
146
|
-
Corner: ScrollAreaCorner,
|
|
147
|
-
Expander: ScrollAreaExpander,
|
|
148
107
|
};
|
|
149
108
|
|
|
150
|
-
export type {
|
|
151
|
-
ScrollAreaRootProps,
|
|
152
|
-
ScrollAreaViewportProps,
|
|
153
|
-
ScrollAreaScrollbarProps,
|
|
154
|
-
ScrollAreaThumbProps,
|
|
155
|
-
ScrollAreaCornerProps,
|
|
156
|
-
ScrollAreaExpanderProps,
|
|
157
|
-
};
|
|
109
|
+
export type { ScrollAreaRootProps, ScrollAreaViewportProps };
|
|
@@ -7,16 +7,22 @@ import React, { useEffect, useRef, useState } from 'react';
|
|
|
7
7
|
|
|
8
8
|
import { faker } from '@dxos/random';
|
|
9
9
|
|
|
10
|
+
import { Layout } from '../../primitives';
|
|
10
11
|
import { withLayout, withTheme } from '../../testing';
|
|
11
12
|
import { Button } from '../Button';
|
|
12
13
|
import { Toolbar } from '../Toolbar';
|
|
13
14
|
|
|
14
15
|
import { ScrollContainer, type ScrollContainerRootProps, type ScrollController } from './ScrollContainer';
|
|
15
16
|
|
|
16
|
-
|
|
17
|
+
type StoryProps = ScrollContainerRootProps & { running?: boolean; initialLines?: number };
|
|
18
|
+
|
|
19
|
+
const DefaultStory = ({ initialLines = 0, running: runningProp, ...props }: StoryProps) => {
|
|
17
20
|
const [lines, setLines] = useState<string[]>([]);
|
|
18
|
-
const [running, setRunning] = useState(
|
|
21
|
+
const [running, setRunning] = useState(runningProp);
|
|
19
22
|
const scroller = useRef<ScrollController>(null);
|
|
23
|
+
useEffect(() => {
|
|
24
|
+
setLines(Array.from({ length: initialLines }, () => faker.lorem.paragraph()));
|
|
25
|
+
}, [initialLines]);
|
|
20
26
|
useEffect(() => {
|
|
21
27
|
if (!running) {
|
|
22
28
|
return;
|
|
@@ -30,32 +36,32 @@ const DefaultStory = (props: ScrollContainerRootProps) => {
|
|
|
30
36
|
}, [running]);
|
|
31
37
|
|
|
32
38
|
return (
|
|
33
|
-
<
|
|
39
|
+
<Layout.Main toolbar>
|
|
34
40
|
<Toolbar.Root>
|
|
35
41
|
<Button onClick={() => setRunning((running) => !running)}>{running ? 'Stop' : 'Start'}</Button>
|
|
36
42
|
<Button onClick={() => scroller.current?.scrollToBottom()}>Scroll to bottom</Button>
|
|
37
|
-
<
|
|
38
|
-
<div>{lines.length}</div>
|
|
43
|
+
<Toolbar.Separator variant='gap' />
|
|
44
|
+
<div className='pli-1'>{lines.length}</div>
|
|
39
45
|
</Toolbar.Root>
|
|
40
46
|
<ScrollContainer.Root {...props} ref={scroller}>
|
|
41
47
|
<ScrollContainer.Viewport>
|
|
42
48
|
{lines.map((line, index) => (
|
|
43
|
-
<div key={index} className='p-2'>
|
|
49
|
+
<div key={index} className='p-2 text-description'>
|
|
44
50
|
{line}
|
|
45
51
|
</div>
|
|
46
52
|
))}
|
|
47
53
|
</ScrollContainer.Viewport>
|
|
48
54
|
<ScrollContainer.ScrollDownButton />
|
|
49
55
|
</ScrollContainer.Root>
|
|
50
|
-
</
|
|
56
|
+
</Layout.Main>
|
|
51
57
|
);
|
|
52
58
|
};
|
|
53
59
|
|
|
54
60
|
const meta = {
|
|
55
|
-
title: 'ui/react-ui-core/ScrollContainer',
|
|
61
|
+
title: 'ui/react-ui-core/components/ScrollContainer',
|
|
56
62
|
component: ScrollContainer.Root,
|
|
57
63
|
render: DefaultStory,
|
|
58
|
-
decorators: [withTheme, withLayout({ layout: 'column', classNames: 'is-[30rem]' })],
|
|
64
|
+
decorators: [withTheme(), withLayout({ layout: 'column', classNames: 'is-[30rem]' })],
|
|
59
65
|
} satisfies Meta<typeof DefaultStory>;
|
|
60
66
|
|
|
61
67
|
export default meta;
|
|
@@ -66,5 +72,14 @@ export const Default: Story = {
|
|
|
66
72
|
args: {
|
|
67
73
|
pin: true,
|
|
68
74
|
fade: true,
|
|
75
|
+
running: true,
|
|
76
|
+
},
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
export const Large: Story = {
|
|
80
|
+
args: {
|
|
81
|
+
pin: true,
|
|
82
|
+
fade: true,
|
|
83
|
+
initialLines: 100,
|
|
69
84
|
},
|
|
70
85
|
};
|