Skip to content

Commit e21841f

Browse files
committed
feat: Toggle Component 추가
1 parent 95142e5 commit e21841f

File tree

5 files changed

+123
-1
lines changed

5 files changed

+123
-1
lines changed

packages/notion-to-jsx/src/components/Renderer/components/Block/BlockRenderer.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { Heading1, Heading2, Heading3, Paragraph } from '../Typography';
1111
import { ColumnList } from '../Column';
1212
import { Quote } from '../Quote';
1313
import Table from '../Table';
14+
import { Toggle } from '../Toggle';
1415

1516
export interface Props {
1617
block: any;
@@ -97,6 +98,9 @@ const BlockRenderer: React.FC<Props> = ({ block, onFocus, index }) => {
9798

9899
case 'table':
99100
return <Table block={block} tabIndex={blockProps.tabIndex} />;
101+
102+
case 'toggle':
103+
return <Toggle block={block} tabIndex={blockProps.tabIndex} onFocus={onFocus} />;
100104

101105
default:
102106
console.log(`지원되지 않는 블록 타입: ${block.type}`, block);
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import React, { useState } from 'react';
2+
import { NotionBlock } from '../../../../types';
3+
import {
4+
toggleContainer,
5+
toggleHeader,
6+
toggleIcon,
7+
toggleIconOpen,
8+
toggleContent
9+
} from './styles.css';
10+
import { RichTexts } from '../../components/RichText';
11+
import BlockRenderer from '../../components/Block/BlockRenderer';
12+
13+
interface ToggleProps {
14+
block: NotionBlock;
15+
tabIndex?: number;
16+
onFocus?: () => void;
17+
}
18+
19+
const Toggle: React.FC<ToggleProps> = ({ block, tabIndex = 0, onFocus }) => {
20+
const [isOpen, setIsOpen] = useState(false);
21+
22+
// Toggle이 없거나 children이 없는 경우 렌더링하지 않음
23+
if (!block.toggle || !block.children) {
24+
return null;
25+
}
26+
27+
const handleToggle = () => {
28+
setIsOpen(!isOpen);
29+
};
30+
31+
const handleKeyDown = (e: React.KeyboardEvent) => {
32+
if (e.key === 'Enter' || e.key === ' ') {
33+
e.preventDefault();
34+
handleToggle();
35+
}
36+
};
37+
38+
return (
39+
<div className={toggleContainer}>
40+
<div
41+
className={toggleHeader}
42+
onClick={handleToggle}
43+
onKeyDown={handleKeyDown}
44+
tabIndex={tabIndex}
45+
onFocus={onFocus}
46+
role="button"
47+
aria-expanded={isOpen}
48+
>
49+
<span className={`${toggleIcon} ${isOpen ? toggleIconOpen : ''}`}>
50+
51+
</span>
52+
<RichTexts richTexts={block.toggle.rich_text} />
53+
</div>
54+
55+
{isOpen && block.children && (
56+
<div className={toggleContent}>
57+
{block.children.map((childBlock, index) => (
58+
<BlockRenderer
59+
key={childBlock.id}
60+
block={childBlock}
61+
index={index}
62+
/>
63+
))}
64+
</div>
65+
)}
66+
</div>
67+
);
68+
};
69+
70+
export default Toggle;
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import Toggle from './Toggle';
2+
3+
export { Toggle };
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import { style } from '@vanilla-extract/css';
2+
import { vars } from '../../../../styles/theme.css';
3+
4+
export const toggleContainer = style({
5+
position: 'relative',
6+
});
7+
8+
export const toggleHeader = style({
9+
display: 'flex',
10+
alignItems: 'center',
11+
cursor: 'pointer',
12+
fontSize: vars.typography.fontSize.base,
13+
fontWeight: vars.typography.fontWeight.normal,
14+
color: 'inherit',
15+
padding: `${vars.spacing.xs} 0`,
16+
borderRadius: vars.borderRadius.sm,
17+
':hover': {
18+
background: 'rgba(55, 53, 47, 0.08)',
19+
},
20+
});
21+
22+
export const toggleIcon = style({
23+
marginRight: vars.spacing.sm,
24+
display: 'inline-flex',
25+
alignItems: 'center',
26+
justifyContent: 'center',
27+
transition: 'transform 0.2s ease',
28+
width: '1.2rem',
29+
height: '1.2rem',
30+
});
31+
32+
export const toggleIconOpen = style({
33+
transform: 'rotate(90deg)',
34+
});
35+
36+
export const toggleContent = style({
37+
paddingLeft: vars.spacing.lg,
38+
marginTop: vars.spacing.xs,
39+
overflow: 'hidden',
40+
});

packages/notion-to-jsx/src/types/index.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@ export interface NotionBlock {
1515
| 'bookmark'
1616
| 'table'
1717
| 'table_row'
18-
| 'quote';
18+
| 'quote'
19+
| 'toggle';
1920
paragraph?: {
2021
rich_text: RichTextItem[];
2122
color: string;
@@ -67,6 +68,10 @@ export interface NotionBlock {
6768
rich_text: RichTextItem[];
6869
color: string;
6970
};
71+
toggle?: {
72+
rich_text: RichTextItem[];
73+
color: string;
74+
};
7075
children?: NotionBlock[];
7176
has_children?: boolean;
7277
parent?: {

0 commit comments

Comments
 (0)