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

라디오 필드

{radioProps.groupLabelMode === "image" && ( )}
옵션
{(((radioProps as any).options ?? []) as any[]).map((opt, index) => (
{ const next = [...(((radioProps as any).options ?? []) as any[])]; next[index] = { ...next[index], label: e.target.value, }; updateBlock(selectedBlockId, { options: next, } as any); }} /> { const next = [...(((radioProps as any).options ?? []) as any[])]; next[index] = { ...next[index], value: e.target.value, }; updateBlock(selectedBlockId, { options: next, } as any); }} />
{ const next = [...(((radioProps as any).options ?? []) as any[])]; next[index] = { ...next[index], labelImageUrl: e.target.value, }; updateBlock(selectedBlockId, { options: next, } as any); }} />
))}

필드 스타일

{/* 라디오 타이포 px 값을 직접 입력받아 em 스케일 변환 로직과 연동한다 */} { const raw = radioProps.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 = radioProps.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 = radioProps.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); }} /> {(radioProps.widthMode ?? "full") === "fixed" && ( { updateBlock(selectedBlockId, { widthPx: v, } as any); }} /> )} updateBlock(selectedBlockId, { paddingX: v, } as any) } /> updateBlock(selectedBlockId, { paddingY: v, } as any) } /> updateBlock(selectedBlockId, { optionGapPx: 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 = radioProps.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); }} />
); }