Skip to content

Commit a89bb66

Browse files
committed
feat(docs): implement Accordion component using Radix UI
1 parent ef5275c commit a89bb66

File tree

3 files changed

+130
-0
lines changed

3 files changed

+130
-0
lines changed

apps/docs/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
"@mdx-js/react": "^3.1.1",
2727
"@vercel/analytics": "^1.5.0",
2828
"@vercel/speed-insights": "^1.2.0",
29+
"@radix-ui/react-accordion": "^1.2.12",
2930
"clsx": "^2.1.1",
3031
"docusaurus-plugin-image-zoom": "^3.0.1",
3132
"docusaurus-plugin-sass": "^0.2.6",
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
@keyframes accordion-down {
2+
from {
3+
height: 0;
4+
}
5+
to {
6+
height: var(--radix-accordion-content-height);
7+
}
8+
}
9+
10+
@keyframes accordion-up {
11+
from {
12+
height: var(--radix-accordion-content-height);
13+
}
14+
to {
15+
height: 0;
16+
}
17+
}
18+
19+
.accordionContent {
20+
overflow: hidden;
21+
transition: all 0.2s ease-out;
22+
23+
&[data-state='closed'] {
24+
animation: accordion-up 0.2s ease-out;
25+
}
26+
27+
&[data-state='open'] {
28+
animation: accordion-down 0.2s ease-out;
29+
}
30+
}
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
import * as AccordionPrimitive from '@radix-ui/react-accordion';
2+
import clsx from 'clsx';
3+
import React, { type ReactNode } from 'react';
4+
5+
import ChevronDownIcon from '@site/src/assets/icons/ChevronDown.svg';
6+
import { BodyBold } from '@site/src/components/Typography';
7+
8+
import styles from './Accordion.module.scss';
9+
10+
export interface AccordionItem {
11+
title: string;
12+
content: ReactNode;
13+
value: string;
14+
}
15+
16+
export interface AccordionProps {
17+
items: AccordionItem[];
18+
type?: 'single' | 'multiple';
19+
defaultValue?: string;
20+
className?: string;
21+
}
22+
23+
const AccordionRoot: React.FC<AccordionProps> = ({ items, type = 'single', defaultValue, className }) => {
24+
if (type === 'single') {
25+
return (
26+
<AccordionPrimitive.Root
27+
type="single"
28+
defaultValue={defaultValue}
29+
collapsible
30+
className={clsx('flex flex-col items-start w-full', className)}
31+
>
32+
{items.map((item) => (
33+
<AccordionItem key={item.value} value={item.value}>
34+
<AccordionTrigger>{item.title}</AccordionTrigger>
35+
<AccordionContent>{item.content}</AccordionContent>
36+
</AccordionItem>
37+
))}
38+
</AccordionPrimitive.Root>
39+
);
40+
}
41+
42+
return (
43+
<AccordionPrimitive.Root
44+
type="multiple"
45+
defaultValue={defaultValue ? [defaultValue] : undefined}
46+
className={clsx('flex flex-col items-start w-full', className)}
47+
>
48+
{items.map((item) => (
49+
<AccordionItem key={item.value} value={item.value}>
50+
<AccordionTrigger>{item.title}</AccordionTrigger>
51+
<AccordionContent>{item.content}</AccordionContent>
52+
</AccordionItem>
53+
))}
54+
</AccordionPrimitive.Root>
55+
);
56+
};
57+
58+
const AccordionItem: React.FC<{
59+
children: ReactNode;
60+
value: string;
61+
className?: string;
62+
}> = ({ children, value, className }) => {
63+
return (
64+
<AccordionPrimitive.Item value={value} className={clsx('border-b border-white w-full', className)}>
65+
{children}
66+
</AccordionPrimitive.Item>
67+
);
68+
};
69+
70+
const AccordionTrigger: React.FC<{
71+
children: ReactNode;
72+
className?: string;
73+
}> = ({ children, className }) => {
74+
return (
75+
<AccordionPrimitive.Trigger
76+
className={clsx(
77+
'flex flex-1 items-center justify-between py-4 px-0 cursor-pointer text-white bg-transparent border-none transition-all w-full',
78+
'data-[state=open]:[&_svg]:rotate-180',
79+
className,
80+
)}
81+
>
82+
<BodyBold className="mb-0!">{children}</BodyBold>
83+
<ChevronDownIcon className="h-4 w-4 shrink-0 transition-transform duration-200" />
84+
</AccordionPrimitive.Trigger>
85+
);
86+
};
87+
88+
const AccordionContent: React.FC<{
89+
children: ReactNode;
90+
className?: string;
91+
}> = ({ children, className }) => {
92+
return (
93+
<AccordionPrimitive.Content className={clsx(styles.accordionContent, className)}>
94+
<div className="pb-4 pt-0">{children}</div>
95+
</AccordionPrimitive.Content>
96+
);
97+
};
98+
99+
export default AccordionRoot;

0 commit comments

Comments
 (0)