@dxos/react-ui-tabs 0.8.4-main.7ace549 → 0.8.4-main.8360d9e660
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/index.mjs +146 -187
- package/dist/lib/browser/index.mjs.map +3 -3
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/node-esm/index.mjs +146 -187
- package/dist/lib/node-esm/index.mjs.map +3 -3
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/types/src/Tabs.d.ts +6 -1
- package/dist/types/src/Tabs.d.ts.map +1 -1
- package/dist/types/src/Tabs.stories.d.ts +11 -1
- package/dist/types/src/Tabs.stories.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +22 -19
- package/src/Tabs.stories.tsx +3 -3
- package/src/Tabs.tsx +101 -85
package/package.json
CHANGED
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dxos/react-ui-tabs",
|
|
3
|
-
"version": "0.8.4-main.
|
|
3
|
+
"version": "0.8.4-main.8360d9e660",
|
|
4
4
|
"description": "Components for facilitating a Tabs pattern.",
|
|
5
5
|
"homepage": "https://dxos.org",
|
|
6
6
|
"bugs": "https://github.com/dxos/dxos/issues",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "https://github.com/dxos/dxos"
|
|
10
|
+
},
|
|
7
11
|
"license": "MIT",
|
|
8
12
|
"author": "DXOS.org",
|
|
9
|
-
"sideEffects":
|
|
13
|
+
"sideEffects": false,
|
|
10
14
|
"type": "module",
|
|
11
15
|
"exports": {
|
|
12
16
|
".": {
|
|
@@ -25,33 +29,32 @@
|
|
|
25
29
|
"src"
|
|
26
30
|
],
|
|
27
31
|
"dependencies": {
|
|
28
|
-
"@fluentui/react-tabster": "
|
|
29
|
-
"@preact-signals/safe-react": "^0.9.0",
|
|
32
|
+
"@fluentui/react-tabster": "9.26.11",
|
|
30
33
|
"@radix-ui/primitive": "1.1.1",
|
|
31
34
|
"@radix-ui/react-context": "1.1.1",
|
|
32
35
|
"@radix-ui/react-primitive": "2.0.2",
|
|
33
36
|
"@radix-ui/react-slot": "1.1.2",
|
|
34
37
|
"@radix-ui/react-tabs": "1.1.3",
|
|
35
38
|
"@radix-ui/react-use-controllable-state": "1.1.0",
|
|
36
|
-
"@dxos/react-ui-attention": "0.8.4-main.
|
|
37
|
-
"@dxos/util": "0.8.4-main.
|
|
39
|
+
"@dxos/react-ui-attention": "0.8.4-main.8360d9e660",
|
|
40
|
+
"@dxos/util": "0.8.4-main.8360d9e660"
|
|
38
41
|
},
|
|
39
42
|
"devDependencies": {
|
|
40
|
-
"@types/react": "~19.2.
|
|
41
|
-
"@types/react-dom": "~19.2.
|
|
42
|
-
"react": "~19.2.
|
|
43
|
-
"react-dom": "~19.2.
|
|
44
|
-
"vite": "7.1.
|
|
45
|
-
"@dxos/random": "0.8.4-main.
|
|
46
|
-
"@dxos/
|
|
47
|
-
"@dxos/
|
|
48
|
-
"@dxos/
|
|
43
|
+
"@types/react": "~19.2.7",
|
|
44
|
+
"@types/react-dom": "~19.2.3",
|
|
45
|
+
"react": "~19.2.3",
|
|
46
|
+
"react-dom": "~19.2.3",
|
|
47
|
+
"vite": "^7.1.11",
|
|
48
|
+
"@dxos/random": "0.8.4-main.8360d9e660",
|
|
49
|
+
"@dxos/ui-theme": "0.8.4-main.8360d9e660",
|
|
50
|
+
"@dxos/storybook-utils": "0.8.4-main.8360d9e660",
|
|
51
|
+
"@dxos/react-ui": "0.8.4-main.8360d9e660"
|
|
49
52
|
},
|
|
50
53
|
"peerDependencies": {
|
|
51
|
-
"react": "
|
|
52
|
-
"react-dom": "
|
|
53
|
-
"@dxos/react-ui": "0.8.4-main.
|
|
54
|
-
"@dxos/
|
|
54
|
+
"react": "~19.2.3",
|
|
55
|
+
"react-dom": "~19.2.3",
|
|
56
|
+
"@dxos/react-ui": "0.8.4-main.8360d9e660",
|
|
57
|
+
"@dxos/ui-theme": "0.8.4-main.8360d9e660"
|
|
55
58
|
},
|
|
56
59
|
"publishConfig": {
|
|
57
60
|
"access": "public"
|
package/src/Tabs.stories.tsx
CHANGED
|
@@ -17,7 +17,7 @@ export const DefaultStory = () => {
|
|
|
17
17
|
return (
|
|
18
18
|
<Dialog.Root open>
|
|
19
19
|
<Dialog.Overlay blockAlign='start'>
|
|
20
|
-
<Dialog.Content
|
|
20
|
+
<Dialog.Content size='xl'>
|
|
21
21
|
<NaturalTabs.Root orientation='vertical' defaultValue={Object.keys(content)[3]} defaultActivePart='list'>
|
|
22
22
|
<NaturalTabs.Viewport>
|
|
23
23
|
<NaturalTabs.Tablist>
|
|
@@ -36,7 +36,7 @@ export const DefaultStory = () => {
|
|
|
36
36
|
<Icon icon='ph--arrow-left--bold' size={4} />
|
|
37
37
|
<span>Back to tab list</span>
|
|
38
38
|
</NaturalTabs.BackButton>
|
|
39
|
-
<p className='
|
|
39
|
+
<p className='px-1'>{panel}</p>
|
|
40
40
|
</NaturalTabs.Tabpanel>
|
|
41
41
|
);
|
|
42
42
|
})}
|
|
@@ -60,7 +60,7 @@ const meta = {
|
|
|
60
60
|
title: 'ui/react-ui-tabs/Tabs',
|
|
61
61
|
component: NaturalTabs.Root,
|
|
62
62
|
render: DefaultStory,
|
|
63
|
-
decorators: [withTheme],
|
|
63
|
+
decorators: [withTheme()],
|
|
64
64
|
} satisfies Meta<typeof DefaultStory>;
|
|
65
65
|
|
|
66
66
|
export default meta;
|
package/src/Tabs.tsx
CHANGED
|
@@ -10,14 +10,21 @@ import React, {
|
|
|
10
10
|
Activity,
|
|
11
11
|
type ComponentPropsWithoutRef,
|
|
12
12
|
type MouseEvent,
|
|
13
|
+
forwardRef,
|
|
13
14
|
useCallback,
|
|
14
15
|
useLayoutEffect,
|
|
15
|
-
useRef,
|
|
16
16
|
} from 'react';
|
|
17
17
|
|
|
18
|
-
import {
|
|
18
|
+
import {
|
|
19
|
+
Button,
|
|
20
|
+
type ButtonProps,
|
|
21
|
+
IconButton,
|
|
22
|
+
type IconButtonProps,
|
|
23
|
+
type ThemedClassName,
|
|
24
|
+
useForwardedRef,
|
|
25
|
+
} from '@dxos/react-ui';
|
|
19
26
|
import { useAttention } from '@dxos/react-ui-attention';
|
|
20
|
-
import {
|
|
27
|
+
import { mx } from '@dxos/ui-theme';
|
|
21
28
|
|
|
22
29
|
type TabsActivePart = 'list' | 'panel';
|
|
23
30
|
|
|
@@ -43,83 +50,91 @@ type TabsRootProps = ThemedClassName<TabsPrimitive.TabsProps> &
|
|
|
43
50
|
defaultActivePart: TabsActivePart;
|
|
44
51
|
}>;
|
|
45
52
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
const _2 = useFocusableGroup();
|
|
64
|
-
const [activePart = 'list', setActivePart] = useControllableState({
|
|
65
|
-
prop: propsActivePart,
|
|
66
|
-
onChange: onActivePartChange,
|
|
67
|
-
defaultProp: defaultActivePart,
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
const [value, setValue] = useControllableState({
|
|
71
|
-
prop: propsValue,
|
|
72
|
-
onChange: onValueChange,
|
|
73
|
-
defaultProp: defaultValue,
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
const handleValueChange = useCallback(
|
|
77
|
-
(nextValue: string) => {
|
|
78
|
-
setActivePart('panel');
|
|
79
|
-
setValue(nextValue);
|
|
53
|
+
// TODO(burdon): Reconcile padding with Toolbar.
|
|
54
|
+
const TabsRoot = forwardRef<HTMLDivElement, TabsRootProps>(
|
|
55
|
+
(
|
|
56
|
+
{
|
|
57
|
+
children,
|
|
58
|
+
classNames,
|
|
59
|
+
activePart: propsActivePart,
|
|
60
|
+
onActivePartChange,
|
|
61
|
+
defaultActivePart,
|
|
62
|
+
value: propsValue,
|
|
63
|
+
onValueChange,
|
|
64
|
+
defaultValue,
|
|
65
|
+
orientation = 'vertical',
|
|
66
|
+
activationMode = 'manual',
|
|
67
|
+
verticalVariant = 'stateful',
|
|
68
|
+
attendableId,
|
|
69
|
+
...props
|
|
80
70
|
},
|
|
81
|
-
|
|
82
|
-
)
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
71
|
+
forwardedRef,
|
|
72
|
+
) => {
|
|
73
|
+
// const tabsRoot = useRef<HTMLDivElement | null>(null);
|
|
74
|
+
const tabsRoot = useForwardedRef(forwardedRef);
|
|
75
|
+
|
|
76
|
+
// TODO(thure): Without these, we get Groupper/Mover `API used before initialization`, but why?
|
|
77
|
+
const _1 = useArrowNavigationGroup();
|
|
78
|
+
const _2 = useFocusableGroup();
|
|
79
|
+
const [activePart = 'list', setActivePart] = useControllableState({
|
|
80
|
+
prop: propsActivePart,
|
|
81
|
+
onChange: onActivePartChange,
|
|
82
|
+
defaultProp: defaultActivePart,
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
const [value, setValue] = useControllableState({
|
|
86
|
+
prop: propsValue,
|
|
87
|
+
onChange: onValueChange,
|
|
88
|
+
defaultProp: defaultValue,
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
const handleValueChange = useCallback(
|
|
92
|
+
(nextValue: string) => {
|
|
93
|
+
setActivePart('panel');
|
|
94
|
+
setValue(nextValue);
|
|
95
|
+
},
|
|
96
|
+
[value],
|
|
97
|
+
);
|
|
98
|
+
|
|
99
|
+
const { findFirstFocusable } = useFocusFinders();
|
|
100
|
+
|
|
101
|
+
useLayoutEffect(() => {
|
|
102
|
+
if (tabsRoot.current) {
|
|
103
|
+
findFirstFocusable(tabsRoot.current)?.focus();
|
|
104
|
+
}
|
|
105
|
+
}, [activePart]);
|
|
106
|
+
|
|
107
|
+
return (
|
|
108
|
+
<TabsContextProvider
|
|
105
109
|
orientation={orientation}
|
|
106
|
-
{
|
|
110
|
+
activePart={activePart}
|
|
111
|
+
setActivePart={setActivePart}
|
|
107
112
|
value={value}
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
'overflow-hidden',
|
|
111
|
-
orientation === 'vertical' &&
|
|
112
|
-
verticalVariant === 'stateful' &&
|
|
113
|
-
'[&[data-active=list]_[role=tabpanel]]:invisible @md:[&[data-active=list]_[role=tabpanel]]:visible',
|
|
114
|
-
classNames,
|
|
115
|
-
)}
|
|
116
|
-
ref={tabsRoot}
|
|
113
|
+
attendableId={attendableId}
|
|
114
|
+
verticalVariant={verticalVariant}
|
|
117
115
|
>
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
}
|
|
116
|
+
<TabsPrimitive.Root
|
|
117
|
+
activationMode={activationMode}
|
|
118
|
+
data-active={activePart}
|
|
119
|
+
orientation={orientation}
|
|
120
|
+
{...props}
|
|
121
|
+
value={value}
|
|
122
|
+
onValueChange={handleValueChange}
|
|
123
|
+
className={mx(
|
|
124
|
+
'overflow-hidden',
|
|
125
|
+
orientation === 'vertical' &&
|
|
126
|
+
verticalVariant === 'stateful' &&
|
|
127
|
+
'[&[data-active=list]_[role=tabpanel]]:invisible @md:[&[data-active=list]_[role=tabpanel]]:visible',
|
|
128
|
+
classNames,
|
|
129
|
+
)}
|
|
130
|
+
ref={tabsRoot}
|
|
131
|
+
>
|
|
132
|
+
{children}
|
|
133
|
+
</TabsPrimitive.Root>
|
|
134
|
+
</TabsContextProvider>
|
|
135
|
+
);
|
|
136
|
+
},
|
|
137
|
+
);
|
|
123
138
|
|
|
124
139
|
type TabsViewportProps = ThemedClassName<ComponentPropsWithoutRef<'div'>>;
|
|
125
140
|
|
|
@@ -133,8 +148,8 @@ const TabsViewport = ({ classNames, children, ...props }: TabsViewportProps) =>
|
|
|
133
148
|
className={mx(
|
|
134
149
|
orientation === 'vertical' &&
|
|
135
150
|
verticalVariant === 'stateful' && [
|
|
136
|
-
'grid
|
|
137
|
-
'@md:
|
|
151
|
+
'grid w-[200%] grid-cols-2 data-[active=panel]:ms-[-100%]',
|
|
152
|
+
'@md:w-auto @md:data-[active=panel]:ms-0 @md:grid-cols-[minmax(min-content,1fr)_3fr] @md:gap-1',
|
|
138
153
|
],
|
|
139
154
|
classNames,
|
|
140
155
|
)}
|
|
@@ -146,6 +161,7 @@ const TabsViewport = ({ classNames, children, ...props }: TabsViewportProps) =>
|
|
|
146
161
|
|
|
147
162
|
type TabsTablistProps = ThemedClassName<TabsPrimitive.TabsListProps>;
|
|
148
163
|
|
|
164
|
+
// TODO(burdon): Should have same geometry as Toolbar.
|
|
149
165
|
const TabsTablist = ({ children, classNames, ...props }: TabsTablistProps) => {
|
|
150
166
|
const { orientation, verticalVariant } = useTabsContext('TabsTablist');
|
|
151
167
|
return (
|
|
@@ -153,7 +169,7 @@ const TabsTablist = ({ children, classNames, ...props }: TabsTablistProps) => {
|
|
|
153
169
|
{...props}
|
|
154
170
|
data-arrow-keys={orientation === 'vertical' ? 'up down' : 'left right'}
|
|
155
171
|
className={mx(
|
|
156
|
-
'max-
|
|
172
|
+
'max-h-full w-full',
|
|
157
173
|
// NOTE: Padding should be common to Toolbar.
|
|
158
174
|
orientation === 'vertical' ? 'overflow-y-auto' : 'flex items-stretch justify-start overflow-x-auto p-1 gap-1',
|
|
159
175
|
orientation === 'vertical' && verticalVariant === 'stateful' && 'place-self-start p-1',
|
|
@@ -175,14 +191,14 @@ const TabsBackButton = ({ onClick, classNames, ...props }: ButtonProps) => {
|
|
|
175
191
|
[onClick, setActivePart],
|
|
176
192
|
);
|
|
177
193
|
|
|
178
|
-
return <Button {...props} classNames={['
|
|
194
|
+
return <Button {...props} classNames={['w-full text-start @md:hidden mb-2', classNames]} onClick={handleClick} />;
|
|
179
195
|
};
|
|
180
196
|
|
|
181
197
|
type TabsTabGroupHeadingProps = ThemedClassName<ComponentPropsWithoutRef<'h2'>>;
|
|
182
198
|
|
|
183
199
|
const TabsTabGroupHeading = ({ children, classNames, ...props }: ThemedClassName<TabsTabGroupHeadingProps>) => {
|
|
184
200
|
return (
|
|
185
|
-
<h2 {...props} className={mx('
|
|
201
|
+
<h2 {...props} className={mx('my-1 px-2 text-sm text-un-accent', classNames)}>
|
|
186
202
|
{children}
|
|
187
203
|
</h2>
|
|
188
204
|
);
|
|
@@ -213,8 +229,8 @@ const TabsTab = ({ value, classNames, children, onClick, ...props }: TabsTabProp
|
|
|
213
229
|
{...props}
|
|
214
230
|
onClick={handleClick}
|
|
215
231
|
classNames={[
|
|
216
|
-
orientation === 'vertical' && 'block justify-start text-start
|
|
217
|
-
orientation === 'vertical' &&
|
|
232
|
+
orientation === 'vertical' && 'block justify-start text-start w-full',
|
|
233
|
+
orientation === 'vertical' && 'dx-selected',
|
|
218
234
|
classNames,
|
|
219
235
|
]}
|
|
220
236
|
>
|
|
@@ -249,8 +265,8 @@ const TabsIconTab = ({ value, classNames, onClick, ...props }: TabsIconTabProps)
|
|
|
249
265
|
{...props}
|
|
250
266
|
onClick={handleClick}
|
|
251
267
|
classNames={[
|
|
252
|
-
orientation === 'vertical' && 'justify-start text-start
|
|
253
|
-
orientation === 'vertical' &&
|
|
268
|
+
orientation === 'vertical' && 'justify-start text-start w-full',
|
|
269
|
+
orientation === 'vertical' && 'dx-selected',
|
|
254
270
|
classNames,
|
|
255
271
|
]}
|
|
256
272
|
/>
|