Skip to content

Commit aecbb0f

Browse files
committed
feat: add component collapsible
1 parent 4271a84 commit aecbb0f

File tree

11 files changed

+201
-1
lines changed

11 files changed

+201
-1
lines changed

packages/ui/package.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@
1111
"main": "./dist/index.js",
1212
"module": "./dist/index.js",
1313
"types": "./dist/index.d.ts",
14-
"files": ["dist"],
14+
"files": [
15+
"dist"
16+
],
1517
"scripts": {
1618
"build": "pnpm run pgk:prod && pnpm run build:components && pnpm run registry",
1719
"build:components": "tsdown",
@@ -27,6 +29,7 @@
2729
"@radix-ui/react-aspect-ratio": "1.1.7",
2830
"@radix-ui/react-avatar": "1.1.10",
2931
"@radix-ui/react-checkbox": "1.3.2",
32+
"@radix-ui/react-collapsible": "^1.1.11",
3033
"@radix-ui/react-compose-refs": "1.1.2",
3134
"@radix-ui/react-label": "2.1.7",
3235
"@radix-ui/react-popover": "1.1.14",
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import type { ComponentRef } from 'react';
2+
import { forwardRef } from 'react';
3+
4+
import CollapsibleContent from './CollapsibleContent';
5+
import CollapsibleRoot from './CollapsibleRoot';
6+
import type { CollapsibleProps } from './types';
7+
8+
const Collapsible = forwardRef<ComponentRef<typeof CollapsibleRoot>, CollapsibleProps>((props, ref) => {
9+
const { children, className, classNames, content, forceMountContent, ...rest } = props;
10+
11+
return (
12+
<CollapsibleRoot
13+
className={className || classNames?.root}
14+
ref={ref}
15+
{...rest}
16+
>
17+
{children}
18+
19+
<CollapsibleContent
20+
className={classNames?.content}
21+
forceMount={forceMountContent}
22+
>
23+
{content}
24+
</CollapsibleContent>
25+
</CollapsibleRoot>
26+
);
27+
});
28+
29+
Collapsible.displayName = 'Collapsible';
30+
31+
export default Collapsible;
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { Content } from '@radix-ui/react-collapsible';
2+
import type { ComponentRef } from 'react';
3+
import { forwardRef } from 'react';
4+
5+
import { cn } from '@/lib/utils';
6+
7+
import { collapsibleVariants } from './collapsible-variants';
8+
import type { CollapsibleContentProps } from './types';
9+
10+
const CollapsibleContent = forwardRef<ComponentRef<typeof Content>, CollapsibleContentProps>((props, ref) => {
11+
const { className, ...rest } = props;
12+
13+
const { content } = collapsibleVariants();
14+
15+
const mergedCls = cn(content(), className);
16+
17+
return (
18+
<Content
19+
className={mergedCls}
20+
ref={ref}
21+
{...rest}
22+
/>
23+
);
24+
});
25+
26+
CollapsibleContent.displayName = 'CollapsibleContent';
27+
28+
export default CollapsibleContent;
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { Root } from '@radix-ui/react-collapsible';
2+
import type { ComponentRef } from 'react';
3+
import { forwardRef } from 'react';
4+
5+
import { cn } from '@/lib/utils';
6+
7+
import { collapsibleVariants } from './collapsible-variants';
8+
import type { CollapsibleRootProps } from './types';
9+
10+
const CollapsibleRoot = forwardRef<ComponentRef<typeof Root>, CollapsibleRootProps>((props, ref) => {
11+
const { className, size, ...rest } = props;
12+
13+
const { root } = collapsibleVariants({ size });
14+
15+
const mergedCls = cn(root(), className);
16+
17+
return (
18+
<Root
19+
className={mergedCls}
20+
ref={ref}
21+
{...rest}
22+
/>
23+
);
24+
});
25+
26+
CollapsibleRoot.displayName = 'CollapsibleRoot';
27+
28+
export default CollapsibleRoot;
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import { tv } from 'tailwind-variants';
2+
3+
export const collapsibleVariants = tv({
4+
slots: {
5+
content: [
6+
'overflow-hidden transition will-change-auto',
7+
'data-[state=closed]:animate-collapsible-up data-[state=open]:animate-collapsible-down'
8+
],
9+
root: '',
10+
trigger: ''
11+
},
12+
variants: {
13+
size: {
14+
'2xl': {
15+
root: 'text-xl'
16+
},
17+
lg: {
18+
root: 'text-base'
19+
},
20+
md: {
21+
root: 'text-sm'
22+
},
23+
sm: {
24+
root: 'text-xs'
25+
},
26+
xl: {
27+
root: 'text-lg'
28+
},
29+
xs: {
30+
root: 'text-2xs'
31+
}
32+
}
33+
}
34+
});
35+
36+
export type CollapsibleSlots = keyof typeof collapsibleVariants.slots;
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
export { CollapsibleTrigger } from '@radix-ui/react-collapsible';
2+
export { default as Collapsible } from './Collapsible';
3+
export { default as CollapsibleContent } from './CollapsibleContent';
4+
export { default as CollapsibleRoot } from './CollapsibleRoot';
5+
export * from './types';
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import type {
2+
CollapsibleContentProps as _CollapsibleContentProps,
3+
CollapsibleProps as _CollapsibleRootProps
4+
} from '@radix-ui/react-collapsible';
5+
6+
import type { BaseNodeProps, ClassValue } from '@/types/other';
7+
8+
import type { CollapsibleSlots } from './collapsible-variants';
9+
10+
export type CollapsibleRootProps = BaseNodeProps<Omit<_CollapsibleRootProps, 'content'>>;
11+
12+
export type CollapsibleContentProps = BaseNodeProps<_CollapsibleContentProps>;
13+
14+
export type CollapsibleClassNames = Partial<Record<CollapsibleSlots, ClassValue>>;
15+
16+
export interface CollapsibleProps extends CollapsibleRootProps {
17+
classNames?: CollapsibleClassNames;
18+
content?: React.ReactNode;
19+
forceMountContent?: true;
20+
}

packages/ui/src/index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,15 @@ export * from './components/breadcrumb';
1515
export * from './components/button';
1616

1717
export * from './components/card';
18+
1819
export * from './components/carousel';
1920

2021
export * from './components/checkbox';
2122

2223
export * from './components/chip';
2324

25+
export * from './components/collapsible';
26+
2427
export * from './components/config-provider';
2528

2629
export * from './components/divider';
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import { ChevronsUpDown } from 'lucide-react';
2+
import { ButtonIcon, Card, Collapsible, CollapsibleTrigger } from 'soybean-react-ui';
3+
4+
const DefaultCollapsibleDemo = () => {
5+
return (
6+
<Card
7+
split
8+
title="Collapsible"
9+
>
10+
<Collapsible
11+
className="w-[350px] max-sm:w-auto space-y-2"
12+
classNames={{ content: 'space-y-2' }}
13+
content={
14+
<>
15+
<div className="border rounded-md px-4 py-3 text-sm font-mono">@soybean-ui/colors</div>
16+
<div className="border rounded-md px-4 py-3 text-sm font-mono">soybean-ui</div>
17+
</>
18+
}
19+
>
20+
<div className="flex-y-center justify-between px-2 space-x-4">
21+
<h4 className="text-sm font-semibold">@soybeanjs starred 3 repositories</h4>
22+
23+
<CollapsibleTrigger asChild>
24+
<ButtonIcon>
25+
<ChevronsUpDown />
26+
</ButtonIcon>
27+
</CollapsibleTrigger>
28+
</div>
29+
30+
<div className="border rounded-md px-4 py-3 text-sm font-mono">@soybean-ui/primitives</div>
31+
</Collapsible>
32+
</Card>
33+
);
34+
};
35+
36+
export default DefaultCollapsibleDemo;
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import DefaultCollapsibleDemo from './modules/DefaultCollapsibleDemo';
2+
3+
const CollapsiblePage = () => {
4+
return <DefaultCollapsibleDemo />;
5+
};
6+
7+
export default CollapsiblePage;

0 commit comments

Comments
 (0)