"use client"; import type { Block, FormInputBlockProps } from "@/features/editor/state/editorStore"; import { ColorPickerField, TEXT_COLOR_PALETTE } from "@/features/editor/components/ColorPickerField"; import { NumericPropertyControl } from "@/features/editor/components/NumericPropertyControl"; interface FormInputPropertiesPanelProps { block: Block; selectedBlockId: string | null; updateBlock: (id: string, partial: any) => void; } export function FormInputPropertiesPanel({ block, selectedBlockId, updateBlock }: FormInputPropertiesPanelProps) { if (!selectedBlockId || block.id !== selectedBlockId || block.type !== "formInput") return null; const inputProps = block.props as FormInputBlockProps; return (

입력 필드

{inputProps.labelMode === "image" && ( <> )}

필드 스타일

{ const raw = inputProps.fontSizeCustom ?? ""; const match = raw.match(/([0-9]+(?:\.[0-9]+)?)/); if (match) return Number(match[1]); return 14; })()} min={8} max={40} step={1} presets={[ { id: "xs", label: "XS", value: 12 }, { id: "sm", label: "S", value: 14 }, { id: "md", label: "M", value: 16 }, { id: "lg", label: "L", value: 18 }, { id: "xl", label: "XL", value: 20 }, ]} onChangeValue={(v) => { updateBlock(selectedBlockId, { fontSizeCustom: `${v}px`, } as any); }} /> {/* 줄간격 px 입력을 노출하여 preview em 변환의 근거를 제공 */} { const raw = inputProps.lineHeightCustom ?? ""; const match = raw.match(/([0-9]+(?:\.[0-9]+)?)/); if (match) return Number(match[1]); return 20; })()} min={8} max={60} step={1} presets={[ { id: "tight", label: "타이트", value: 18 }, { id: "normal", label: "보통", value: 24 }, { id: "loose", label: "루즈", value: 32 }, ]} onChangeValue={(v) => { updateBlock(selectedBlockId, { lineHeightCustom: `${v}px`, } as any); }} /> {/* 자간 px 입력으로 폼 입력 타이포를 세밀하게 조정 */} { const raw = inputProps.letterSpacingCustom ?? ""; const match = raw.match(/(-?[0-9]+(?:\.[0-9]+)?)/); if (match) return Number(match[1]); return 0; })()} min={-10} max={20} step={0.5} presets={[ { id: "tight", label: "좁게", value: -1 }, { id: "normal", label: "보통", value: 0 }, { id: "wide", label: "넓게", value: 2 }, ]} onChangeValue={(v) => { updateBlock(selectedBlockId, { letterSpacingCustom: `${v}px`, } as any); }} /> {(inputProps.labelLayout ?? "stacked") === "inline" && ( updateBlock(selectedBlockId, { labelGapPx: v, } as any) } /> )} {(inputProps.widthMode ?? "full") === "fixed" && ( { updateBlock(selectedBlockId, { widthPx: v, } as any); }} /> )} { updateBlock(selectedBlockId, { paddingX: v, } as any); }} /> { updateBlock(selectedBlockId, { paddingY: v, } as any); }} /> { updateBlock(selectedBlockId, { textColorCustom: hex, } as any); }} palette={TEXT_COLOR_PALETTE} onPaletteSelect={(item) => { updateBlock(selectedBlockId, { textColorCustom: item.color, } as any); }} /> { updateBlock(selectedBlockId, { fillColorCustom: hex, } as any); }} palette={TEXT_COLOR_PALETTE} onPaletteSelect={(item) => { updateBlock(selectedBlockId, { fillColorCustom: item.color, } as any); }} /> { updateBlock(selectedBlockId, { strokeColorCustom: hex, } as any); }} palette={TEXT_COLOR_PALETTE} onPaletteSelect={(item) => { updateBlock(selectedBlockId, { strokeColorCustom: item.color, } as any); }} /> { const r = inputProps.borderRadius ?? "md"; return r === "none" ? 0 : r === "sm" ? 2 : r === "lg" ? 6 : r === "full" ? 8 : 4; })()} min={0} max={8} step={1} presets={[ { id: "none", label: "없음", value: 0 }, { id: "sm", label: "작게", value: 2 }, { id: "md", label: "보통", value: 4 }, { id: "lg", label: "크게", value: 6 }, { id: "full", label: "완전 둥글게", value: 8 }, ]} onChangeValue={(v) => { const next = v <= 0 ? "none" : v <= 2 ? "sm" : v <= 5 ? "md" : v <= 7 ? "lg" : "full"; updateBlock(selectedBlockId, { borderRadius: next, } as any); }} />
); }