@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/package.json CHANGED
@@ -1,12 +1,16 @@
1
1
  {
2
2
  "name": "@dxos/react-ui-tabs",
3
- "version": "0.8.4-main.7ace549",
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": true,
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": "^9.24.2",
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.7ace549",
37
- "@dxos/util": "0.8.4-main.7ace549"
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.2",
41
- "@types/react-dom": "~19.2.2",
42
- "react": "~19.2.0",
43
- "react-dom": "~19.2.0",
44
- "vite": "7.1.9",
45
- "@dxos/random": "0.8.4-main.7ace549",
46
- "@dxos/react-ui": "0.8.4-main.7ace549",
47
- "@dxos/react-ui-theme": "0.8.4-main.7ace549",
48
- "@dxos/storybook-utils": "0.8.4-main.7ace549"
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": "^19.0.0",
52
- "react-dom": "^19.0.0",
53
- "@dxos/react-ui": "0.8.4-main.7ace549",
54
- "@dxos/react-ui-theme": "0.8.4-main.7ace549"
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"
@@ -17,7 +17,7 @@ export const DefaultStory = () => {
17
17
  return (
18
18
  <Dialog.Root open>
19
19
  <Dialog.Overlay blockAlign='start'>
20
- <Dialog.Content classNames='is-[calc(100dvw-4rem)] !max-is-full'>
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='pli-1'>{panel}</p>
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 { Button, type ButtonProps, IconButton, type IconButtonProps, type ThemedClassName } from '@dxos/react-ui';
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 { ghostSelectedContainerMd, mx } from '@dxos/react-ui-theme';
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
- const TabsRoot = ({
47
- children,
48
- classNames,
49
- activePart: propsActivePart,
50
- onActivePartChange,
51
- defaultActivePart,
52
- value: propsValue,
53
- onValueChange,
54
- defaultValue,
55
- orientation = 'vertical',
56
- activationMode = 'manual',
57
- verticalVariant = 'stateful',
58
- attendableId,
59
- ...props
60
- }: TabsRootProps) => {
61
- // TODO(thure): Without these, we get Groupper/Mover `API used before initialization`, but why?
62
- const _1 = useArrowNavigationGroup();
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
- [value],
82
- );
83
-
84
- const { findFirstFocusable } = useFocusFinders();
85
- const tabsRoot = useRef<HTMLDivElement | null>(null);
86
-
87
- useLayoutEffect(() => {
88
- if (tabsRoot.current) {
89
- findFirstFocusable(tabsRoot.current)?.focus();
90
- }
91
- }, [activePart]);
92
-
93
- return (
94
- <TabsContextProvider
95
- orientation={orientation}
96
- activePart={activePart}
97
- setActivePart={setActivePart}
98
- value={value}
99
- attendableId={attendableId}
100
- verticalVariant={verticalVariant}
101
- >
102
- <TabsPrimitive.Root
103
- activationMode={activationMode}
104
- data-active={activePart}
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
- {...props}
110
+ activePart={activePart}
111
+ setActivePart={setActivePart}
107
112
  value={value}
108
- onValueChange={handleValueChange}
109
- className={mx(
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
- {children}
119
- </TabsPrimitive.Root>
120
- </TabsContextProvider>
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 is-[200%] grid-cols-2 data-[active=panel]:mis-[-100%]',
137
- '@md:is-auto @md:data-[active=panel]:mis-0 @md:grid-cols-[minmax(min-content,1fr)_3fr] @md:gap-1',
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-bs-full is-full',
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={['is-full text-start @md:hidden mbe-2', classNames]} onClick={handleClick} />;
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('mlb-1 pli-2 text-sm text-unAccent', classNames)}>
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 is-full',
217
- orientation === 'vertical' && ghostSelectedContainerMd,
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 is-full',
253
- orientation === 'vertical' && ghostSelectedContainerMd,
268
+ orientation === 'vertical' && 'justify-start text-start w-full',
269
+ orientation === 'vertical' && 'dx-selected',
254
270
  classNames,
255
271
  ]}
256
272
  />