@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/LICENSE +102 -5
- package/dist/lib/browser/index.mjs +161 -199
- package/dist/lib/browser/index.mjs.map +3 -3
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/node-esm/index.mjs +161 -199
- 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 +44 -12
- package/dist/types/src/Tabs.d.ts.map +1 -1
- package/dist/types/src/Tabs.stories.d.ts +17 -4
- package/dist/types/src/Tabs.stories.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +23 -23
- package/src/Tabs.stories.tsx +48 -41
- package/src/Tabs.tsx +154 -137
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.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
|
-
"
|
|
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":
|
|
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": "
|
|
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.
|
|
37
|
-
"@dxos/util": "0.8.4-main.
|
|
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.
|
|
41
|
-
"@types/react-dom": "~19.2.
|
|
42
|
-
"react": "~19.2.
|
|
43
|
-
"react-dom": "~19.2.
|
|
44
|
-
"vite": "
|
|
45
|
-
"@dxos/
|
|
46
|
-
"@dxos/
|
|
47
|
-
"@dxos/
|
|
48
|
-
"@dxos/
|
|
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": "
|
|
52
|
-
"react-dom": "
|
|
53
|
-
"@dxos/react-ui": "0.8.4-main.
|
|
54
|
-
"@dxos/
|
|
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"
|
package/src/Tabs.stories.tsx
CHANGED
|
@@ -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 {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
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
|
|
12
|
+
import { Tabs, TabsRootProps } from './Tabs';
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
random.seed(1234);
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
const DefaultStory = ({ orientation }: TabsRootProps) => {
|
|
17
17
|
return (
|
|
18
|
-
<
|
|
19
|
-
<
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
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:
|
|
54
|
-
panel:
|
|
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:
|
|
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
|
-
|
|
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, {
|
|
10
|
-
|
|
11
|
-
|
|
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 {
|
|
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<
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
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
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
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
|
-
{
|
|
108
|
+
activePart={activePart}
|
|
109
|
+
setActivePart={setActivePart}
|
|
100
110
|
value={value}
|
|
101
|
-
|
|
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
|
-
|
|
112
|
-
|
|
113
|
-
|
|
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
|
-
|
|
120
|
-
const {
|
|
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
|
-
|
|
143
|
-
const { orientation
|
|
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-
|
|
150
|
-
//
|
|
151
|
-
orientation === 'vertical' ? 'overflow-y-auto' : 'flex items-stretch justify-start overflow-x-auto
|
|
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
|
-
|
|
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
|
-
[
|
|
174
|
+
[setActivePart, onClick],
|
|
169
175
|
);
|
|
170
176
|
|
|
171
|
-
return <Button {...props} classNames={['
|
|
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
|
-
|
|
184
|
+
function TabsTabGroupHeading({ children, classNames, ...props }: ThemedClassName<TabsTabGroupHeadingProps>) {
|
|
177
185
|
return (
|
|
178
|
-
<h2 {...props} className={mx('
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
210
|
-
orientation === 'vertical' &&
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
246
|
-
orientation === 'vertical' &&
|
|
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
|
-
|
|
264
|
+
TabsIconTab.displayName = 'Tabs.IconTab';
|
|
255
265
|
|
|
256
|
-
|
|
266
|
+
type TabsPanelProps = ThemedClassName<TabsPrimitive.TabsContentProps>;
|
|
267
|
+
|
|
268
|
+
function TabsPanel({ classNames, children, ...props }: TabsPanelProps) {
|
|
269
|
+
const { value: contextValue } = useTabsContext('TabsTab');
|
|
257
270
|
return (
|
|
258
|
-
<
|
|
259
|
-
{
|
|
260
|
-
|
|
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
|
-
|
|
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
|
-
|
|
302
|
+
TabsPanelProps,
|
|
286
303
|
TabsViewportProps,
|
|
287
304
|
};
|