Skip to content

Commit f272f42

Browse files
Hugos68endigo9740
andauthored
feat: tabs (#3679)
Co-authored-by: Chris Simmons <[email protected]>
1 parent 5d517a8 commit f272f42

File tree

44 files changed

+914
-0
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+914
-0
lines changed

.changeset/dry-coats-return.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
---
2+
"@skeletonlabs/skeleton-common": minor
3+
"@skeletonlabs/skeleton-svelte": minor
4+
"@skeletonlabs/skeleton-react": minor
5+
---
6+
7+
feat: tabs
8+
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import { defineSkeletonClasses } from '../internal/define-skeleton-classes' with { type: 'macro' };
2+
3+
export const classesTabs = defineSkeletonClasses({
4+
root: 'w-full flex data-[orientation=horizontal]:flex-col data-[orientation=vertical]:flex-row',
5+
list: 'relative data-[orientation=horizontal]:mb-4 data-[orientation=horizontal]:pb-2 data-[orientation=vertical]:pe-2 data-[orientation=vertical]:me-4 flex data-[orientation=horizontal]:flex-row data-[orientation=vertical]:flex-col gap-2 data-[orientation=horizontal]:border-b data-[orientation=vertical]:border-e border-surface-200-800',
6+
trigger: 'btn hover:preset-tonal-primary data-disabled:opacity-50 data-disabled:pointer-events-none',
7+
indicator:
8+
'bg-surface-950-50 data-[orientation=horizontal]:w-(--width) data-[orientation=horizontal]:h-0.5 data-[orientation=horizontal]:bottom-0 data-[orientation=vertical]:w-0.5 data-[orientation=vertical]:h-(--height) data-[orientation=vertical]:end-0',
9+
content: ''
10+
});

packages/skeleton-common/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@ export * from './themes.js';
22
export * from './classes/accordion.js';
33
export * from './classes/avatar.js';
44
export * from './classes/rating-group.js';
5+
export * from './classes/tabs.js';
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import { classesTabs } from '@skeletonlabs/skeleton-common';
2+
import { splitContentProps, type ContentProps } from '@zag-js/tabs';
3+
import { mergeProps } from '@zag-js/react';
4+
import { useContext, type ComponentProps } from 'react';
5+
import { TabsRootContext } from '../modules/tabs-root-context.js';
6+
import type { PropsWithElement } from '@/internal/props-with-element.js';
7+
8+
export interface TabsContentProps extends PropsWithElement, ContentProps, ComponentProps<'div'> {}
9+
10+
export default function (props: TabsContentProps) {
11+
const rootContext = useContext(TabsRootContext);
12+
const [itemProps, componentProps] = splitContentProps(props);
13+
const { element, children, ...restAttributes } = componentProps;
14+
const attributes = mergeProps(
15+
rootContext.api.getContentProps(itemProps),
16+
{
17+
className: classesTabs.content
18+
},
19+
restAttributes
20+
);
21+
return element ? element({ attributes }) : <div {...attributes}>{children}</div>;
22+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { classesTabs } from '@skeletonlabs/skeleton-common';
2+
import { mergeProps } from '@zag-js/react';
3+
import { useContext, type ComponentProps } from 'react';
4+
import { TabsRootContext } from '../modules/tabs-root-context.js';
5+
import type { PropsWithElement } from '@/internal/props-with-element.js';
6+
7+
export interface TabsIndicatorProps extends PropsWithElement, Omit<ComponentProps<'div'>, 'children'> {}
8+
9+
export default function (props: TabsIndicatorProps) {
10+
const rootContext = useContext(TabsRootContext);
11+
const { element, ...restAttributes } = props;
12+
const attributes = mergeProps(
13+
rootContext.api.getIndicatorProps(),
14+
{
15+
className: classesTabs.indicator
16+
},
17+
restAttributes
18+
);
19+
return element ? element({ attributes }) : <div {...attributes}></div>;
20+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { classesTabs } from '@skeletonlabs/skeleton-common';
2+
import { mergeProps } from '@zag-js/react';
3+
import { useContext, type ComponentProps } from 'react';
4+
import { TabsRootContext } from '../modules/tabs-root-context.js';
5+
import type { PropsWithElement } from '@/internal/props-with-element.js';
6+
7+
export interface TabsListProps extends PropsWithElement, Omit<ComponentProps<'div'>, 'id' | 'defaultValue' | 'dir'> {}
8+
9+
export default function (props: TabsListProps) {
10+
const rootContext = useContext(TabsRootContext);
11+
const { element, children, ...restAttributes } = props;
12+
const attributes = mergeProps(
13+
rootContext.api.getListProps(),
14+
{
15+
className: classesTabs.list
16+
},
17+
restAttributes
18+
);
19+
return element ? element({ attributes }) : <div {...attributes}>{children}</div>;
20+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { useContext, type ReactNode } from 'react';
2+
import { type TabsRootContextType, TabsRootContext } from '../modules/tabs-root-context.js';
3+
4+
export interface TabsRootContextProps {
5+
children: (context: TabsRootContextType) => ReactNode;
6+
}
7+
8+
export default function (props: TabsRootContextProps) {
9+
const rootContext = useContext(TabsRootContext);
10+
return props.children(rootContext);
11+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import { splitProps, machine, connect, type Props } from '@zag-js/tabs';
2+
import { mergeProps, normalizeProps, useMachine } from '@zag-js/react';
3+
import { useId, type ComponentProps } from 'react';
4+
import { TabsRootContext } from '../modules/tabs-root-context.js';
5+
import type { PropsWithElement } from '@/internal/props-with-element';
6+
import { classesTabs } from '@skeletonlabs/skeleton-common';
7+
8+
export interface TabsRootProps extends PropsWithElement, Omit<Props, 'id'>, Omit<ComponentProps<'div'>, 'id' | 'defaultValue' | 'dir'> {}
9+
10+
export default function (props: TabsRootProps) {
11+
const [machineProps, componentProps] = splitProps(props);
12+
const { element, children, ...restAttributes } = componentProps;
13+
const service = useMachine(machine, {
14+
id: useId(),
15+
...machineProps
16+
});
17+
const api = connect(service, normalizeProps);
18+
const attributes = mergeProps(
19+
api.getRootProps(),
20+
{
21+
className: classesTabs.root
22+
},
23+
restAttributes
24+
);
25+
return (
26+
<TabsRootContext.Provider value={{ api }}>
27+
{element ? element({ attributes }) : <div {...attributes}>{children}</div>}
28+
</TabsRootContext.Provider>
29+
);
30+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import { classesTabs } from '@skeletonlabs/skeleton-common';
2+
import { splitTriggerProps, type TriggerProps } from '@zag-js/tabs';
3+
import { mergeProps } from '@zag-js/react';
4+
import { useContext, type ComponentProps } from 'react';
5+
import { TabsRootContext } from '../modules/tabs-root-context.js';
6+
import type { PropsWithElement } from '@/internal/props-with-element.js';
7+
8+
export interface TabsTriggerProps extends PropsWithElement, TriggerProps, Omit<ComponentProps<'button'>, 'value'> {}
9+
10+
export default function (props: TabsTriggerProps) {
11+
const rootContext = useContext(TabsRootContext);
12+
const [itemProps, componentProps] = splitTriggerProps(props);
13+
const { element, children, ...restAttributes } = componentProps;
14+
const attributes = mergeProps(
15+
rootContext.api.getTriggerProps(itemProps),
16+
{
17+
className: classesTabs.trigger
18+
},
19+
restAttributes
20+
);
21+
return element ? element({ attributes }) : <button {...attributes}>{children}</button>;
22+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
export { Tabs } from './modules/tabs-anatomy.js';
2+
export type { TabsRootProps } from './anatomy/tabs-root.js';
3+
export type { TabsRootContextProps } from './anatomy/tabs-root-context.js';
4+
export type { TabsListProps } from './anatomy/tabs-list.js';
5+
export type { TabsTriggerProps } from './anatomy/tabs-trigger.js';
6+
export type { TabsIndicatorProps } from './anatomy/tabs-indicator.js';
7+
export type { TabsContentProps } from './anatomy/tabs-content.js';
8+
export type { TabsRootContextType as TabsRootContext } from './modules/tabs-root-context.js';

0 commit comments

Comments
 (0)