@dxos/react-ui-tabs 0.8.4-main.e8ec1fe → 0.8.4-main.ef1bc66f44

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.e8ec1fe",
3
+ "version": "0.8.4-main.ef1bc66f44",
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.e8ec1fe",
37
- "@dxos/util": "0.8.4-main.e8ec1fe"
39
+ "@dxos/react-ui-attention": "0.8.4-main.ef1bc66f44",
40
+ "@dxos/util": "0.8.4-main.ef1bc66f44"
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",
43
+ "@types/react": "~19.2.7",
44
+ "@types/react-dom": "~19.2.3",
45
+ "react": "~19.2.3",
46
+ "react-dom": "~19.2.3",
44
47
  "vite": "7.1.9",
45
- "@dxos/random": "0.8.4-main.e8ec1fe",
46
- "@dxos/react-ui": "0.8.4-main.e8ec1fe",
47
- "@dxos/storybook-utils": "0.8.4-main.e8ec1fe",
48
- "@dxos/react-ui-theme": "0.8.4-main.e8ec1fe"
48
+ "@dxos/react-ui": "0.8.4-main.ef1bc66f44",
49
+ "@dxos/storybook-utils": "0.8.4-main.ef1bc66f44",
50
+ "@dxos/ui-theme": "0.8.4-main.ef1bc66f44",
51
+ "@dxos/random": "0.8.4-main.ef1bc66f44"
49
52
  },
50
53
  "peerDependencies": {
51
- "react": "^19.0.0",
52
- "react-dom": "^19.0.0",
53
- "@dxos/react-ui": "0.8.4-main.e8ec1fe",
54
- "@dxos/react-ui-theme": "0.8.4-main.e8ec1fe"
54
+ "react": "~19.2.3",
55
+ "react-dom": "~19.2.3",
56
+ "@dxos/react-ui": "0.8.4-main.ef1bc66f44",
57
+ "@dxos/ui-theme": "0.8.4-main.ef1bc66f44"
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>
@@ -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 { ghostSelectedContainerMd, mx } from '@dxos/ui-theme';
21
28
 
22
29
  type TabsActivePart = 'list' | 'panel';
23
30
 
@@ -43,83 +50,90 @@ 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
+ const TabsRoot = forwardRef<HTMLDivElement, TabsRootProps>(
54
+ (
55
+ {
56
+ children,
57
+ classNames,
58
+ activePart: propsActivePart,
59
+ onActivePartChange,
60
+ defaultActivePart,
61
+ value: propsValue,
62
+ onValueChange,
63
+ defaultValue,
64
+ orientation = 'vertical',
65
+ activationMode = 'manual',
66
+ verticalVariant = 'stateful',
67
+ attendableId,
68
+ ...props
80
69
  },
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}
70
+ forwardedRef,
71
+ ) => {
72
+ // const tabsRoot = useRef<HTMLDivElement | null>(null);
73
+ const tabsRoot = useForwardedRef(forwardedRef);
74
+
75
+ // TODO(thure): Without these, we get Groupper/Mover `API used before initialization`, but why?
76
+ const _1 = useArrowNavigationGroup();
77
+ const _2 = useFocusableGroup();
78
+ const [activePart = 'list', setActivePart] = useControllableState({
79
+ prop: propsActivePart,
80
+ onChange: onActivePartChange,
81
+ defaultProp: defaultActivePart,
82
+ });
83
+
84
+ const [value, setValue] = useControllableState({
85
+ prop: propsValue,
86
+ onChange: onValueChange,
87
+ defaultProp: defaultValue,
88
+ });
89
+
90
+ const handleValueChange = useCallback(
91
+ (nextValue: string) => {
92
+ setActivePart('panel');
93
+ setValue(nextValue);
94
+ },
95
+ [value],
96
+ );
97
+
98
+ const { findFirstFocusable } = useFocusFinders();
99
+
100
+ useLayoutEffect(() => {
101
+ if (tabsRoot.current) {
102
+ findFirstFocusable(tabsRoot.current)?.focus();
103
+ }
104
+ }, [activePart]);
105
+
106
+ return (
107
+ <TabsContextProvider
105
108
  orientation={orientation}
106
- {...props}
109
+ activePart={activePart}
110
+ setActivePart={setActivePart}
107
111
  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}
112
+ attendableId={attendableId}
113
+ verticalVariant={verticalVariant}
117
114
  >
118
- {children}
119
- </TabsPrimitive.Root>
120
- </TabsContextProvider>
121
- );
122
- };
115
+ <TabsPrimitive.Root
116
+ activationMode={activationMode}
117
+ data-active={activePart}
118
+ orientation={orientation}
119
+ {...props}
120
+ value={value}
121
+ onValueChange={handleValueChange}
122
+ className={mx(
123
+ 'overflow-hidden',
124
+ orientation === 'vertical' &&
125
+ verticalVariant === 'stateful' &&
126
+ '[&[data-active=list]_[role=tabpanel]]:invisible @md:[&[data-active=list]_[role=tabpanel]]:visible',
127
+ classNames,
128
+ )}
129
+ ref={tabsRoot}
130
+ >
131
+ {children}
132
+ </TabsPrimitive.Root>
133
+ </TabsContextProvider>
134
+ );
135
+ },
136
+ );
123
137
 
124
138
  type TabsViewportProps = ThemedClassName<ComponentPropsWithoutRef<'div'>>;
125
139