@dxos/react-ui-tabs 0.8.4-main.ead640a → 0.8.4-main.effb148878

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.ead640a",
3
+ "version": "0.8.4-main.effb148878",
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
- "license": "MIT",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "https://github.com/dxos/dxos"
10
+ },
11
+ "license": "FSL-1.1-Apache-2.0",
8
12
  "author": "DXOS.org",
9
- "sideEffects": true,
13
+ "sideEffects": false,
10
14
  "type": "module",
11
15
  "exports": {
12
16
  ".": {
@@ -17,41 +21,37 @@
17
21
  }
18
22
  },
19
23
  "types": "dist/types/src/index.d.ts",
20
- "typesVersions": {
21
- "*": {}
22
- },
23
24
  "files": [
24
25
  "dist",
25
26
  "src"
26
27
  ],
27
28
  "dependencies": {
28
- "@fluentui/react-tabster": "^9.24.2",
29
- "@preact-signals/safe-react": "^0.9.0",
29
+ "@fluentui/react-tabster": "9.26.11",
30
30
  "@radix-ui/primitive": "1.1.1",
31
31
  "@radix-ui/react-context": "1.1.1",
32
32
  "@radix-ui/react-primitive": "2.0.2",
33
33
  "@radix-ui/react-slot": "1.1.2",
34
34
  "@radix-ui/react-tabs": "1.1.3",
35
35
  "@radix-ui/react-use-controllable-state": "1.1.0",
36
- "@dxos/react-ui-attention": "0.8.4-main.ead640a",
37
- "@dxos/util": "0.8.4-main.ead640a"
36
+ "@dxos/react-ui-attention": "0.8.4-main.effb148878",
37
+ "@dxos/util": "0.8.4-main.effb148878"
38
38
  },
39
39
  "devDependencies": {
40
- "@types/react": "~19.2.2",
41
- "@types/react-dom": "~19.2.1",
42
- "react": "~19.2.0",
43
- "react-dom": "~19.2.0",
44
- "vite": "7.1.9",
45
- "@dxos/random": "0.8.4-main.ead640a",
46
- "@dxos/react-ui": "0.8.4-main.ead640a",
47
- "@dxos/react-ui-theme": "0.8.4-main.ead640a",
48
- "@dxos/storybook-utils": "0.8.4-main.ead640a"
40
+ "@types/react": "~19.2.7",
41
+ "@types/react-dom": "~19.2.3",
42
+ "react": "~19.2.3",
43
+ "react-dom": "~19.2.3",
44
+ "vite": "^8.0.14",
45
+ "@dxos/react-ui": "0.8.4-main.effb148878",
46
+ "@dxos/storybook-utils": "0.8.4-main.effb148878",
47
+ "@dxos/ui-theme": "0.8.4-main.effb148878",
48
+ "@dxos/random": "0.8.4-main.effb148878"
49
49
  },
50
50
  "peerDependencies": {
51
- "react": "^19.0.0",
52
- "react-dom": "^19.0.0",
53
- "@dxos/react-ui": "0.8.4-main.ead640a",
54
- "@dxos/react-ui-theme": "0.8.4-main.ead640a"
51
+ "react": "~19.2.3",
52
+ "react-dom": "~19.2.3",
53
+ "@dxos/react-ui": "0.8.4-main.effb148878",
54
+ "@dxos/ui-theme": "0.8.4-main.effb148878"
55
55
  },
56
56
  "publishConfig": {
57
57
  "access": "public"
@@ -5,66 +5,73 @@
5
5
  import { type Meta, type StoryObj } from '@storybook/react-vite';
6
6
  import React from 'react';
7
7
 
8
- import { faker } from '@dxos/random';
9
- import { Dialog, Icon } from '@dxos/react-ui';
10
- import { withTheme } from '@dxos/react-ui/testing';
8
+ import { random } from '@dxos/random';
9
+ import { withLayout, withTheme } from '@dxos/react-ui/testing';
10
+ import { mx } from '@dxos/ui-theme';
11
11
 
12
- import { Tabs as NaturalTabs } from './Tabs';
12
+ import { Tabs, TabsRootProps } from './Tabs';
13
13
 
14
- faker.seed(1234);
14
+ random.seed(1234);
15
15
 
16
- export const DefaultStory = () => {
16
+ const DefaultStory = ({ orientation }: TabsRootProps) => {
17
17
  return (
18
- <Dialog.Root open>
19
- <Dialog.Overlay blockAlign='start'>
20
- <Dialog.Content classNames='is-[calc(100dvw-4rem)] !max-is-full'>
21
- <NaturalTabs.Root orientation='vertical' defaultValue={Object.keys(content)[3]} defaultActivePart='list'>
22
- <NaturalTabs.Viewport>
23
- <NaturalTabs.Tablist>
24
- {Object.entries(content).map(([id, { title }]) => {
25
- return (
26
- <NaturalTabs.Tab key={id} value={id}>
27
- {title}
28
- </NaturalTabs.Tab>
29
- );
30
- })}
31
- </NaturalTabs.Tablist>
32
- {Object.entries(content).map(([id, { panel }]) => {
33
- return (
34
- <NaturalTabs.Tabpanel key={id} value={id} classNames='m-1'>
35
- <NaturalTabs.BackButton density='fine'>
36
- <Icon icon='ph--arrow-left--bold' size={4} />
37
- <span>Back to tab list</span>
38
- </NaturalTabs.BackButton>
39
- <p className='pli-1'>{panel}</p>
40
- </NaturalTabs.Tabpanel>
41
- );
42
- })}
43
- </NaturalTabs.Viewport>
44
- </NaturalTabs.Root>
45
- </Dialog.Content>
46
- </Dialog.Overlay>
47
- </Dialog.Root>
18
+ <Tabs.Root orientation={orientation} defaultValue={Object.keys(content)[3]} defaultActivePart='list'>
19
+ <Tabs.Viewport
20
+ classNames={mx(
21
+ 'w-full overflow-hidden grid',
22
+ orientation === 'vertical' && 'grid-cols-[minmax(min-content,1fr)_3fr]',
23
+ )}
24
+ >
25
+ <Tabs.Tablist>
26
+ {Object.entries(content).map(([id, { title }]) => (
27
+ <Tabs.Tab key={id} value={id}>
28
+ {title}
29
+ </Tabs.Tab>
30
+ ))}
31
+ </Tabs.Tablist>
32
+ <div className='dx-container'>
33
+ {Object.entries(content).map(([id, { panel }]) => (
34
+ <Tabs.Panel key={id} value={id}>
35
+ <p className='px-1'>{panel}</p>
36
+ </Tabs.Panel>
37
+ ))}
38
+ </div>
39
+ </Tabs.Viewport>
40
+ </Tabs.Root>
48
41
  );
49
42
  };
50
43
 
51
44
  const content = [...Array(24)].reduce((acc: { [key: string]: { title: string; panel: string } }, _, index) => {
52
45
  acc[`t${index}`] = {
53
- title: faker.commerce.productName(),
54
- panel: faker.lorem.paragraphs(5),
46
+ title: random.commerce.productName(),
47
+ panel: random.lorem.paragraphs(5),
55
48
  };
56
49
  return acc;
57
50
  }, {});
58
51
 
59
52
  const meta = {
60
53
  title: 'ui/react-ui-tabs/Tabs',
61
- component: NaturalTabs.Root,
54
+ component: Tabs.Root,
62
55
  render: DefaultStory,
63
- decorators: [withTheme],
56
+ decorators: [withTheme(), withLayout({ layout: 'column' })],
57
+ parameters: {
58
+ layout: 'fullscreen',
59
+ },
64
60
  } satisfies Meta<typeof DefaultStory>;
65
61
 
66
62
  export default meta;
67
63
 
68
64
  type Story = StoryObj<typeof meta>;
69
65
 
70
- export const Default: Story = {};
66
+ // TODO(burdon): Scrolling.
67
+ export const Horizontal: Story = {
68
+ args: {
69
+ orientation: 'horizontal',
70
+ },
71
+ };
72
+
73
+ export const Vertical: Story = {
74
+ args: {
75
+ orientation: 'vertical',
76
+ },
77
+ };
package/src/Tabs.tsx CHANGED
@@ -6,11 +6,25 @@ import { useArrowNavigationGroup, useFocusFinders, useFocusableGroup } from '@fl
6
6
  import { createContext } from '@radix-ui/react-context';
7
7
  import * as TabsPrimitive from '@radix-ui/react-tabs';
8
8
  import { useControllableState } from '@radix-ui/react-use-controllable-state';
9
- import React, { type ComponentPropsWithoutRef, type MouseEvent, useCallback, useLayoutEffect, useRef } from 'react';
10
-
11
- import { Button, type ButtonProps, IconButton, type IconButtonProps, type ThemedClassName } from '@dxos/react-ui';
9
+ import React, {
10
+ Activity,
11
+ type ComponentPropsWithoutRef,
12
+ type MouseEvent,
13
+ forwardRef,
14
+ useCallback,
15
+ useLayoutEffect,
16
+ } from 'react';
17
+
18
+ import {
19
+ Button,
20
+ type ButtonProps,
21
+ IconButton,
22
+ type IconButtonProps,
23
+ type ThemedClassName,
24
+ useForwardedRef,
25
+ } from '@dxos/react-ui';
12
26
  import { useAttention } from '@dxos/react-ui-attention';
13
- import { ghostSelectedContainerMd, mx } from '@dxos/react-ui-theme';
27
+ import { mx } from '@dxos/ui-theme';
14
28
 
15
29
  type TabsActivePart = 'list' | 'panel';
16
30
 
@@ -20,170 +34,166 @@ type TabsContextValue = {
20
34
  activePart: TabsActivePart;
21
35
  setActivePart: (nextActivePart: TabsActivePart) => void;
22
36
  attendableId?: string;
23
- verticalVariant?: 'stateful' | 'stateless';
24
37
  } & Pick<TabsPrimitive.TabsProps, 'orientation' | 'value'>;
25
38
 
26
39
  const [TabsContextProvider, useTabsContext] = createContext<TabsContextValue>(TABS_NAME, {
40
+ orientation: 'vertical',
27
41
  activePart: 'list',
28
42
  setActivePart: () => {},
29
- orientation: 'vertical',
30
43
  });
31
44
 
32
45
  type TabsRootProps = ThemedClassName<TabsPrimitive.TabsProps> &
33
- Partial<Pick<TabsContextValue, 'activePart' | 'verticalVariant' | 'attendableId'>> &
34
- Partial<{
35
- onActivePartChange: (nextActivePart: TabsActivePart) => void;
36
- defaultActivePart: TabsActivePart;
37
- }>;
38
-
39
- const TabsRoot = ({
40
- children,
41
- classNames,
42
- activePart: propsActivePart,
43
- onActivePartChange,
44
- defaultActivePart,
45
- value: propsValue,
46
- onValueChange,
47
- defaultValue,
48
- orientation = 'vertical',
49
- activationMode = 'manual',
50
- verticalVariant = 'stateful',
51
- attendableId,
52
- ...props
53
- }: TabsRootProps) => {
54
- // TODO(thure): Without these, we get Groupper/Mover `API used before initialization`, but why?
55
- const _1 = useArrowNavigationGroup();
56
- const _2 = useFocusableGroup();
57
- const [activePart = 'list', setActivePart] = useControllableState({
58
- prop: propsActivePart,
59
- onChange: onActivePartChange,
60
- defaultProp: defaultActivePart,
61
- });
62
-
63
- const [value, setValue] = useControllableState({
64
- prop: propsValue,
65
- onChange: onValueChange,
66
- defaultProp: defaultValue,
67
- });
68
-
69
- const handleValueChange = useCallback(
70
- (nextValue: string) => {
71
- setActivePart('panel');
72
- setValue(nextValue);
73
- },
74
- [value],
75
- );
76
-
77
- const { findFirstFocusable } = useFocusFinders();
78
- const tabsRoot = useRef<HTMLDivElement | null>(null);
79
-
80
- useLayoutEffect(() => {
81
- if (tabsRoot.current) {
82
- findFirstFocusable(tabsRoot.current)?.focus();
46
+ Partial<
47
+ Pick<TabsContextValue, 'activePart' | 'attendableId'> & {
48
+ onActivePartChange: (nextActivePart: TabsActivePart) => void;
49
+ defaultActivePart: TabsActivePart;
83
50
  }
84
- }, [activePart]);
85
-
86
- return (
87
- <TabsContextProvider
88
- orientation={orientation}
89
- activePart={activePart}
90
- setActivePart={setActivePart}
91
- value={value}
92
- attendableId={attendableId}
93
- verticalVariant={verticalVariant}
94
- >
95
- <TabsPrimitive.Root
96
- activationMode={activationMode}
97
- data-active={activePart}
51
+ >;
52
+
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
+ attendableId,
67
+ ...props
68
+ },
69
+ forwardedRef,
70
+ ) => {
71
+ // const tabsRoot = useRef<HTMLDivElement | null>(null);
72
+ const tabsRoot = useForwardedRef(forwardedRef);
73
+
74
+ // TODO(thure): Without these, we get Groupper/Mover `API used before initialization`, but why?
75
+ const _1 = useArrowNavigationGroup();
76
+ const _2 = useFocusableGroup();
77
+ const [activePart = 'list', setActivePart] = useControllableState({
78
+ prop: propsActivePart,
79
+ onChange: onActivePartChange,
80
+ defaultProp: defaultActivePart,
81
+ });
82
+
83
+ const [value, setValue] = useControllableState({
84
+ prop: propsValue,
85
+ onChange: onValueChange,
86
+ defaultProp: defaultValue,
87
+ });
88
+
89
+ const handleValueChange = useCallback(
90
+ (nextValue: string) => {
91
+ setActivePart('panel');
92
+ setValue(nextValue);
93
+ },
94
+ [value],
95
+ );
96
+
97
+ const { findFirstFocusable } = useFocusFinders();
98
+
99
+ useLayoutEffect(() => {
100
+ if (tabsRoot.current) {
101
+ findFirstFocusable(tabsRoot.current)?.focus();
102
+ }
103
+ }, [activePart]);
104
+
105
+ return (
106
+ <TabsContextProvider
98
107
  orientation={orientation}
99
- {...props}
108
+ activePart={activePart}
109
+ setActivePart={setActivePart}
100
110
  value={value}
101
- onValueChange={handleValueChange}
102
- className={mx(
103
- 'overflow-hidden',
104
- orientation === 'vertical' &&
105
- verticalVariant === 'stateful' &&
106
- '[&[data-active=list]_[role=tabpanel]]:invisible @md:[&[data-active=list]_[role=tabpanel]]:visible',
107
- classNames,
108
- )}
109
- ref={tabsRoot}
111
+ attendableId={attendableId}
110
112
  >
111
- {children}
112
- </TabsPrimitive.Root>
113
- </TabsContextProvider>
114
- );
115
- };
113
+ <TabsPrimitive.Root
114
+ {...props}
115
+ className={mx('overflow-hidden', classNames)}
116
+ orientation={orientation}
117
+ activationMode={activationMode}
118
+ data-active={activePart}
119
+ value={value}
120
+ onValueChange={handleValueChange}
121
+ ref={tabsRoot}
122
+ >
123
+ {children}
124
+ </TabsPrimitive.Root>
125
+ </TabsContextProvider>
126
+ );
127
+ },
128
+ );
129
+
130
+ TabsRoot.displayName = 'Tabs.Root';
116
131
 
117
132
  type TabsViewportProps = ThemedClassName<ComponentPropsWithoutRef<'div'>>;
118
133
 
119
- const TabsViewport = ({ classNames, children, ...props }: TabsViewportProps) => {
120
- const { orientation, activePart, verticalVariant } = useTabsContext('TabsViewport');
134
+ function TabsViewport({ classNames, children, ...props }: TabsViewportProps) {
135
+ const { activePart } = useTabsContext('TabsViewport');
121
136
  return (
122
- <div
123
- role='none'
124
- {...props}
125
- data-active={activePart}
126
- className={mx(
127
- orientation === 'vertical' &&
128
- verticalVariant === 'stateful' && [
129
- 'grid is-[200%] grid-cols-2 data-[active=panel]:mis-[-100%]',
130
- '@md:is-auto @md:data-[active=panel]:mis-0 @md:grid-cols-[minmax(min-content,1fr)_3fr] @md:gap-1',
131
- ],
132
- classNames,
133
- )}
134
- >
137
+ <div {...props} data-active={activePart} className={mx(classNames)}>
135
138
  {children}
136
139
  </div>
137
140
  );
138
- };
141
+ }
142
+
143
+ TabsViewport.displayName = 'Tabs.Viewport';
139
144
 
140
145
  type TabsTablistProps = ThemedClassName<TabsPrimitive.TabsListProps>;
141
146
 
142
- const TabsTablist = ({ children, classNames, ...props }: TabsTablistProps) => {
143
- const { orientation, verticalVariant } = useTabsContext('TabsTablist');
147
+ function TabsTablist({ children, classNames, ...props }: TabsTablistProps) {
148
+ const { orientation } = useTabsContext('TabsTablist');
144
149
  return (
145
150
  <TabsPrimitive.List
146
151
  {...props}
147
152
  data-arrow-keys={orientation === 'vertical' ? 'up down' : 'left right'}
148
153
  className={mx(
149
- 'max-bs-full is-full',
150
- // NOTE: Padding should be common to Toolbar.
151
- orientation === 'vertical' ? 'overflow-y-auto' : 'flex items-stretch justify-start overflow-x-auto p-1 gap-1',
152
- orientation === 'vertical' && verticalVariant === 'stateful' && 'place-self-start p-1',
154
+ 'max-h-full w-full',
155
+ // TODO(burdon): Should be embeddable inside Toolbar (if horizontal).
156
+ orientation === 'vertical' ? 'overflow-y-auto' : 'flex p-1 gap-1 items-stretch justify-start overflow-x-auto',
153
157
  classNames,
154
158
  )}
155
159
  >
156
160
  {children}
157
161
  </TabsPrimitive.List>
158
162
  );
159
- };
163
+ }
164
+
165
+ TabsTablist.displayName = 'Tabs.Tablist';
160
166
 
161
- const TabsBackButton = ({ onClick, classNames, ...props }: ButtonProps) => {
167
+ function TabsBackButton({ onClick, classNames, ...props }: ButtonProps) {
162
168
  const { setActivePart } = useTabsContext('TabsBackButton');
163
169
  const handleClick = useCallback(
164
170
  (event: MouseEvent<HTMLButtonElement>) => {
165
171
  setActivePart('list');
166
172
  return onClick?.(event);
167
173
  },
168
- [onClick, setActivePart],
174
+ [setActivePart, onClick],
169
175
  );
170
176
 
171
- return <Button {...props} classNames={['is-full text-start @md:hidden mbe-2', classNames]} onClick={handleClick} />;
172
- };
177
+ return <Button {...props} classNames={['@md:hidden text-start', classNames]} onClick={handleClick} />;
178
+ }
179
+
180
+ TabsBackButton.displayName = 'Tabs.BackButton';
173
181
 
174
182
  type TabsTabGroupHeadingProps = ThemedClassName<ComponentPropsWithoutRef<'h2'>>;
175
183
 
176
- const TabsTabGroupHeading = ({ children, classNames, ...props }: ThemedClassName<TabsTabGroupHeadingProps>) => {
184
+ function TabsTabGroupHeading({ children, classNames, ...props }: ThemedClassName<TabsTabGroupHeadingProps>) {
177
185
  return (
178
- <h2 {...props} className={mx('mlb-1 pli-2 text-sm text-unAccent', classNames)}>
186
+ <h2 {...props} className={mx('my-1 px-2 text-sm text-un-accent', classNames)}>
179
187
  {children}
180
188
  </h2>
181
189
  );
182
- };
190
+ }
191
+
192
+ TabsTabGroupHeading.displayName = 'Tabs.TabGroupHeading';
183
193
 
184
194
  type TabsTabProps = ButtonProps & Pick<TabsPrimitive.TabsTriggerProps, 'value'>;
185
195
 
186
- const TabsTab = ({ value, classNames, children, onClick, ...props }: TabsTabProps) => {
196
+ function TabsTab({ value, classNames, children, onClick, ...props }: TabsTabProps) {
187
197
  const { setActivePart, orientation, value: contextValue, attendableId } = useTabsContext('TabsTab');
188
198
  const { hasAttention } = useAttention(attendableId);
189
199
 
@@ -199,27 +209,28 @@ const TabsTab = ({ value, classNames, children, onClick, ...props }: TabsTabProp
199
209
  return (
200
210
  <TabsPrimitive.Trigger value={value} asChild>
201
211
  <Button
202
- density='fine'
212
+ {...props}
203
213
  variant={
204
214
  orientation === 'horizontal' && contextValue === value ? (hasAttention ? 'primary' : 'default') : 'ghost'
205
215
  }
206
- {...props}
207
- onClick={handleClick}
208
216
  classNames={[
209
- orientation === 'vertical' && 'block justify-start text-start is-full',
210
- orientation === 'vertical' && ghostSelectedContainerMd,
217
+ orientation === 'vertical' && 'block justify-start text-start w-full',
218
+ orientation === 'vertical' && 'dx-selected',
211
219
  classNames,
212
220
  ]}
221
+ onClick={handleClick}
213
222
  >
214
223
  {children}
215
224
  </Button>
216
225
  </TabsPrimitive.Trigger>
217
226
  );
218
- };
227
+ }
228
+
229
+ TabsTab.displayName = 'Tabs.Tab';
219
230
 
220
231
  type TabsIconTabProps = IconButtonProps & Pick<TabsPrimitive.TabsTriggerProps, 'value'>;
221
232
 
222
- const TabsIconTab = ({ value, classNames, onClick, ...props }: TabsIconTabProps) => {
233
+ function TabsIconTab({ value, classNames, onClick, ...props }: TabsIconTabProps) {
223
234
  const { setActivePart, orientation, value: contextValue, attendableId } = useTabsContext('TabsTab');
224
235
  const { hasAttention } = useAttention(attendableId);
225
236
 
@@ -235,31 +246,37 @@ const TabsIconTab = ({ value, classNames, onClick, ...props }: TabsIconTabProps)
235
246
  return (
236
247
  <TabsPrimitive.Trigger value={value} asChild>
237
248
  <IconButton
238
- density='fine'
249
+ {...props}
239
250
  variant={
240
251
  orientation === 'horizontal' && contextValue === value ? (hasAttention ? 'primary' : 'default') : 'ghost'
241
252
  }
242
- {...props}
243
- onClick={handleClick}
244
253
  classNames={[
245
- orientation === 'vertical' && 'justify-start text-start is-full',
246
- orientation === 'vertical' && ghostSelectedContainerMd,
254
+ orientation === 'vertical' && 'justify-start text-start w-full',
255
+ orientation === 'vertical' && 'dx-selected',
247
256
  classNames,
248
257
  ]}
258
+ onClick={handleClick}
249
259
  />
250
260
  </TabsPrimitive.Trigger>
251
261
  );
252
- };
262
+ }
253
263
 
254
- type TabsTabpanelProps = ThemedClassName<TabsPrimitive.TabsContentProps>;
264
+ TabsIconTab.displayName = 'Tabs.IconTab';
255
265
 
256
- const TabsTabpanel = ({ classNames, children, ...props }: TabsTabpanelProps) => {
266
+ type TabsPanelProps = ThemedClassName<TabsPrimitive.TabsContentProps>;
267
+
268
+ function TabsPanel({ classNames, children, ...props }: TabsPanelProps) {
269
+ const { value: contextValue } = useTabsContext('TabsTab');
257
270
  return (
258
- <TabsPrimitive.Content {...props} className={mx('dx-focus-ring-inset-over-all', classNames)}>
259
- {children}
260
- </TabsPrimitive.Content>
271
+ <Activity mode={contextValue === props.value ? 'visible' : 'hidden'}>
272
+ <TabsPrimitive.Content {...props} className={mx('p-0! dx-focus-ring-inset-over-all', classNames)}>
273
+ {children}
274
+ </TabsPrimitive.Content>
275
+ </Activity>
261
276
  );
262
- };
277
+ }
278
+
279
+ TabsPanel.displayName = 'Tabs.Panel';
263
280
 
264
281
  type TabsTabPrimitiveProps = TabsPrimitive.TabsTriggerProps;
265
282
 
@@ -270,7 +287,7 @@ export const Tabs = {
270
287
  IconTab: TabsIconTab,
271
288
  TabPrimitive: TabsPrimitive.Trigger,
272
289
  TabGroupHeading: TabsTabGroupHeading,
273
- Tabpanel: TabsTabpanel,
290
+ Panel: TabsPanel,
274
291
  BackButton: TabsBackButton,
275
292
  Viewport: TabsViewport,
276
293
  };
@@ -282,6 +299,6 @@ export type {
282
299
  TabsTabProps,
283
300
  TabsTabPrimitiveProps,
284
301
  TabsTabGroupHeadingProps,
285
- TabsTabpanelProps,
302
+ TabsPanelProps,
286
303
  TabsViewportProps,
287
304
  };