From 9121d9b61a4a974df8155f1a5a7a140f24e48c3f Mon Sep 17 00:00:00 2001 From: wonellyho Date: Sat, 3 Jan 2026 01:14:22 +0900 Subject: [PATCH 1/3] =?UTF-8?q?feat:=20SlidePage=20=EA=B5=AC=ED=98=84?= =?UTF-8?q?=EA=B3=BC=20ScriptBox=20=EC=95=A0=EB=8B=88=EB=A9=94=EC=9D=B4?= =?UTF-8?q?=EC=85=98=20=EB=B3=80=ED=99=94=20(#18)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/layout/Layout.tsx | 10 +- src/components/script-box/ScriptBox.tsx | 65 ++++-- .../script-box/ScriptBoxContent.tsx | 3 +- src/pages/SlidePage.tsx | 196 +++++++++++++++++- 4 files changed, 248 insertions(+), 26 deletions(-) diff --git a/src/components/layout/Layout.tsx b/src/components/layout/Layout.tsx index 68a71961..b8e3a188 100644 --- a/src/components/layout/Layout.tsx +++ b/src/components/layout/Layout.tsx @@ -11,14 +11,18 @@ interface LayoutProps { export function Layout({ left, center, right }: LayoutProps) { return ( -
+
{left ?? }
{center}
{right}
-
- + +
+ {/* Outlet이 들어가는 영역(= 네 컨텐츠)이 이제 정확한 높이를 가짐 */} +
+ +
); diff --git a/src/components/script-box/ScriptBox.tsx b/src/components/script-box/ScriptBox.tsx index dae66eef..079192a0 100644 --- a/src/components/script-box/ScriptBox.tsx +++ b/src/components/script-box/ScriptBox.tsx @@ -1,4 +1,4 @@ -import { useState } from 'react'; +import { useEffect, useState } from 'react'; import clsx from 'clsx'; @@ -7,31 +7,62 @@ import ScriptBoxHeader from './ScriptBoxHeader'; interface ScriptBoxProps { slideTitle?: string; + /** + * [기존] ScriptBox는 내부에서만 접힘 상태를 알고 있었고, + * 슬라이드(상단 콘텐츠)는 ScriptBox 상태에 맞춰 움직일 수 없었음. + * [현재] 부모가 접힘 상태를 알 수 있어야 "슬라이드를 살짝 내려오는" UI 연동이 가능함. + */ + onCollapsedChange?: (collapsed: boolean) => void; } -export default function ScriptBox({ slideTitle = '슬라이드 1' }: ScriptBoxProps) { +export default function ScriptBox({ + slideTitle = '슬라이드 1', + onCollapsedChange, +}: ScriptBoxProps) { + // ScriptBox 접힌 상태인지 체크 const [isCollapsed, setIsCollapsed] = useState(false); const handleToggleCollapse = () => { setIsCollapsed((prev) => !prev); }; + /** + * 접힘 상태가 바뀔 때마다 부모에게 알려줌 + * - 부모는 이 상태를 받아 슬라이드 영역에 transform/padding 등으로 반응하게 됨 + */ + useEffect(() => { + onCollapsedChange?.(isCollapsed); + }, [isCollapsed, onCollapsedChange]); + return ( -
- - +
+ {/* 헤더는 그대로 */} +
+ +
+ + {/** + * 본문 영역만 따로 감싸서 접힘 애니메이션 처리 + * + * 왜 wrapper를 하나 더 뒀나? + * - 헤더는 popover 때문에 잘리면 안 됨 → overflow-hidden을 헤더가 아닌 "본문 wrapper"에만 적용 + * - 접힘 애니메이션은 "height 변화"로 처리하는 게 가장 예측 가능함 + * + * [기존] translate로 박스를 숨김 → 공간 계산, popover, 클릭 영역이 꼬일 수 있음 + * [현재] content wrapper의 높이를 h-0으로 만들어 진짜로 본문이 없어지게 함(접힌 상태라면 아예 랜더 자체 하지 않음) + */} +
+ {!isCollapsed && } +
); } diff --git a/src/components/script-box/ScriptBoxContent.tsx b/src/components/script-box/ScriptBoxContent.tsx index e18ea108..39029eb4 100644 --- a/src/components/script-box/ScriptBoxContent.tsx +++ b/src/components/script-box/ScriptBoxContent.tsx @@ -4,7 +4,8 @@ export default function ScriptBoxContent() { const [script, setScript] = useState(''); return ( -
+ // ScriptBox 전체 높이에서 헤더만큼 뺀 영역을 그대로 사용 +