Files
page-builder/src/app/editor/panels/PropertiesSidebar.tsx
T
jaybe 48e13d2a75
CI / pr_and_merge (push) Has been skipped
CI / test (push) Failing after 46m46s
CI / test (pull_request) Failing after 43m8s
CI / pr_and_merge (pull_request) Has been skipped
video 블록 및 리펙터링
2025-11-27 10:07:59 +09:00

230 lines
8.2 KiB
TypeScript

"use client";
import type { Block, ButtonBlockProps, FormBlockProps, ListBlockProps, SectionBlockProps, TextBlockProps, VideoBlockProps } from "@/features/editor/state/editorStore";
import { ButtonPropertiesPanel } from "./ButtonPropertiesPanel";
import { TextPropertiesPanel } from "./TextPropertiesPanel";
import { ListPropertiesPanel } from "./ListPropertiesPanel";
import { DividerPropertiesPanel } from "./DividerPropertiesPanel";
import { ImagePropertiesPanel } from "./ImagePropertiesPanel";
import { SectionPropertiesPanel } from "./SectionPropertiesPanel";
import { VideoPropertiesPanel } from "./VideoPropertiesPanel";
import { FormInputPropertiesPanel } from "../forms/FormInputPropertiesPanel";
import { FormSelectPropertiesPanel } from "../forms/FormSelectPropertiesPanel";
import { FormCheckboxPropertiesPanel } from "../forms/FormCheckboxPropertiesPanel";
import { FormRadioPropertiesPanel } from "../forms/FormRadioPropertiesPanel";
import { FormControllerPanel } from "../forms/FormControllerPanel";
import { ProjectPropertiesPanel } from "./ProjectPropertiesPanel";
interface PropertiesSidebarProps {
blocks: Block[];
selectedBlockId: string | null;
selectedListItemId: string | null;
updateBlock: (id: string, partial: any) => void;
removeBlock: (id: string) => void;
duplicateBlock: (id: string) => void;
// 텍스트 속성 패널에서 사용하는 인라인 편집 상태를 그대로 전달한다.
editingBlockId: string | null;
setEditingText: (value: string) => void;
onMoveSelectedItemUp: (blockId: string) => void;
onMoveSelectedItemDown: (blockId: string) => void;
onIndentSelectedItem: (blockId: string) => void;
onOutdentSelectedItem: (blockId: string) => void;
}
// 우측 속성 패널을 분리한 컴포넌트
export function PropertiesSidebar(props: PropertiesSidebarProps) {
const {
blocks,
selectedBlockId,
selectedListItemId,
updateBlock,
removeBlock,
duplicateBlock,
editingBlockId,
setEditingText,
onMoveSelectedItemUp,
onMoveSelectedItemDown,
onIndentSelectedItem,
onOutdentSelectedItem,
} = props;
return (
<aside
data-testid="properties-sidebar"
className="w-80 p-4 text-sm border-l border-slate-800 flex flex-col gap-4 overflow-auto pb-scroll"
onKeyDownCapture={(e) => {
// 속성 패널 안에서 발생한 키 입력은 에디터 단축키/텍스트 블록 편집으로 전달되지 않도록 막는다.
e.stopPropagation();
}}
>
<h2 className="font-medium mb-2">속성 패널</h2>
{selectedBlockId ? (
<div className="space-y-3">
<div className="flex gap-2 text-[11px]">
<button
type="button"
className="flex-1 rounded border border-slate-700 bg-slate-900 px-2 py-1 text-slate-100 hover:bg-red-900/60 hover:border-red-700"
onClick={() => removeBlock(selectedBlockId)}
>
블록 삭제
</button>
<button
type="button"
className="flex-1 rounded border border-slate-700 bg-slate-900 px-2 py-1 text-slate-100 hover:bg-slate-800"
onClick={() => duplicateBlock(selectedBlockId)}
>
블록 복제
</button>
</div>
{(() => {
const selectedBlock = blocks.find((b) => b.id === selectedBlockId);
if (!selectedBlock) return null;
if (selectedBlock.type === "text") {
const textProps = selectedBlock.props as TextBlockProps;
return (
<TextPropertiesPanel
textProps={textProps}
selectedBlockId={selectedBlockId}
updateBlock={(id, partial) => updateBlock(id, partial as any)}
editingBlockId={editingBlockId}
setEditingText={setEditingText}
/>
);
}
if (selectedBlock.type === "button") {
const buttonProps = selectedBlock.props as ButtonBlockProps;
return (
<ButtonPropertiesPanel
buttonProps={buttonProps}
selectedBlockId={selectedBlockId}
updateBlock={(id, partial) => updateBlock(id, partial as any)}
/>
);
}
if (selectedBlock.type === "list") {
const listProps = selectedBlock.props as ListBlockProps;
return (
<ListPropertiesPanel
listProps={listProps}
selectedBlockId={selectedBlockId}
selectedListItemId={selectedListItemId}
updateBlock={(id, partial) => updateBlock(id, partial as any)}
/>
);
}
if (selectedBlock.type === "divider") {
const dividerProps = selectedBlock.props as any;
return (
<DividerPropertiesPanel
dividerProps={dividerProps}
selectedBlockId={selectedBlockId}
updateBlock={(id, partial) => updateBlock(id, partial as any)}
/>
);
}
if (selectedBlock.type === "image") {
const imageProps = selectedBlock.props as any;
return (
<ImagePropertiesPanel
imageProps={imageProps}
selectedBlockId={selectedBlockId}
updateBlock={(id, partial) => updateBlock(id, partial as any)}
/>
);
}
if (selectedBlock.type === "video") {
const videoProps = selectedBlock.props as VideoBlockProps;
return (
<VideoPropertiesPanel
videoProps={videoProps}
selectedBlockId={selectedBlockId}
updateBlock={(id, partial) => updateBlock(id, partial as any)}
/>
);
}
if (selectedBlock.type === "section") {
const sectionProps = selectedBlock.props as SectionBlockProps;
return (
<SectionPropertiesPanel
sectionProps={sectionProps}
selectedBlockId={selectedBlockId}
updateBlock={(id, partial) => updateBlock(id, partial as any)}
/>
);
}
if (selectedBlock.type === "formInput") {
return (
<FormInputPropertiesPanel
block={selectedBlock}
selectedBlockId={selectedBlockId}
updateBlock={(id, partial) => updateBlock(id, partial as any)}
/>
);
}
if (selectedBlock.type === "formSelect") {
return (
<FormSelectPropertiesPanel
block={selectedBlock}
selectedBlockId={selectedBlockId}
updateBlock={(id, partial) => updateBlock(id, partial as any)}
/>
);
}
if (selectedBlock.type === "formCheckbox") {
return (
<FormCheckboxPropertiesPanel
block={selectedBlock}
selectedBlockId={selectedBlockId}
updateBlock={(id, partial) => updateBlock(id, partial as any)}
/>
);
}
if (selectedBlock.type === "formRadio") {
return (
<FormRadioPropertiesPanel
block={selectedBlock}
selectedBlockId={selectedBlockId}
updateBlock={(id, partial) => updateBlock(id, partial as any)}
/>
);
}
if (selectedBlock.type === "form") {
return (
<FormControllerPanel
block={selectedBlock}
blocks={blocks}
selectedBlockId={selectedBlockId}
updateBlock={(id, partial) => updateBlock(id, partial as any)}
/>
);
}
return null;
})()}
</div>
) : (
<ProjectPropertiesPanel />
)}
</aside>
);
}