i18n 적용
This commit is contained in:
@@ -3,6 +3,8 @@
|
||||
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";
|
||||
import { useAppLocale } from "@/features/i18n/LocaleProvider";
|
||||
import { getEditorFormRadioPanelMessages } from "@/features/i18n/messages/editorFormRadioPanel";
|
||||
|
||||
interface FormRadioPropertiesPanelProps {
|
||||
block: Block;
|
||||
@@ -11,6 +13,8 @@ interface FormRadioPropertiesPanelProps {
|
||||
}
|
||||
|
||||
export function FormRadioPropertiesPanel({ block, selectedBlockId, updateBlock }: FormRadioPropertiesPanelProps) {
|
||||
const locale = useAppLocale();
|
||||
const m = getEditorFormRadioPanelMessages(locale);
|
||||
if (!selectedBlockId || block.id !== selectedBlockId) return null;
|
||||
|
||||
const radioProps = block.props as FormRadioBlockProps;
|
||||
@@ -18,7 +22,7 @@ export function FormRadioPropertiesPanel({ block, selectedBlockId, updateBlock }
|
||||
|
||||
return (
|
||||
<div className="space-y-3 text-xs">
|
||||
<h3 className="text-[11px] font-semibold text-slate-800 dark:text-slate-200">라디오 필드</h3>
|
||||
<h3 className="text-[11px] font-semibold text-slate-800 dark:text-slate-200">{m.sectionTitle}</h3>
|
||||
|
||||
<label className="flex flex-col gap-1">
|
||||
<span className="text-slate-500 dark:text-slate-400">그룹 타이틀 타입</span>
|
||||
@@ -225,7 +229,7 @@ export function FormRadioPropertiesPanel({ block, selectedBlockId, updateBlock }
|
||||
<span className="text-slate-500 dark:text-slate-400">옵션</span>
|
||||
<button
|
||||
type="button"
|
||||
className="rounded border border-slate-300 bg-white px-2 py-0.5 text-[10px] text-slate-900 hover:bg-slate-100 dark:border-slate-700 dark:bg-slate-900 dark:text-slate-100 dark:hover:bg-slate-800"
|
||||
className="rounded border border-slate-300 bg-white px-2 py-0.5 text-[10px] text-slate-900 hover:bg-slate-100 dark:border-slate-700 dark:bg-slate-900 dark:text-slate-100 dark:hover:bg-red-900/60 dark:hover:border-red-700"
|
||||
onClick={() => {
|
||||
const next = Array.isArray((radioProps as any).options)
|
||||
? [...(radioProps as any).options]
|
||||
@@ -393,15 +397,15 @@ export function FormRadioPropertiesPanel({ block, selectedBlockId, updateBlock }
|
||||
</div>
|
||||
|
||||
<label className="inline-flex items-center gap-2">
|
||||
<span className="text-slate-500">필수 여부는 폼 컨트롤러에서 설정합니다.</span>
|
||||
<span className="text-slate-500">{m.requiredNoticeText}</span>
|
||||
</label>
|
||||
|
||||
<div className="mt-3 space-y-2 border-t border-slate-800 pt-3">
|
||||
<h4 className="text-[11px] font-semibold text-slate-800 dark:text-slate-200">필드 스타일</h4>
|
||||
{/* 라디오 타이포 px 값을 직접 입력받아 em 스케일 변환 로직과 연동한다 */}
|
||||
<h4 className="text-[11px] font-semibold text-slate-800 dark:text-slate-200">{m.styleSectionTitle}</h4>
|
||||
|
||||
<NumericPropertyControl
|
||||
label="라디오 텍스트 크기 (px)"
|
||||
unitLabel="(px)"
|
||||
label={m.textSizeLabel}
|
||||
unitLabel={m.textSizeUnitLabel}
|
||||
value={(() => {
|
||||
const raw = radioProps.fontSizeCustom ?? "";
|
||||
const match = raw.match(/([0-9]+(?:\.[0-9]+)?)/);
|
||||
@@ -424,10 +428,10 @@ export function FormRadioPropertiesPanel({ block, selectedBlockId, updateBlock }
|
||||
} as any);
|
||||
}}
|
||||
/>
|
||||
{/* 줄간격 px 입력을 노출하여 preview 에서 em 변환 근거로 사용 */}
|
||||
|
||||
<NumericPropertyControl
|
||||
label="라디오 줄간격 (px)"
|
||||
unitLabel="(px)"
|
||||
label={m.lineHeightLabel}
|
||||
unitLabel={m.lineHeightUnitLabel}
|
||||
value={(() => {
|
||||
const raw = radioProps.lineHeightCustom ?? "";
|
||||
const match = raw.match(/([0-9]+(?:\.[0-9]+)?)/);
|
||||
@@ -448,10 +452,10 @@ export function FormRadioPropertiesPanel({ block, selectedBlockId, updateBlock }
|
||||
} as any);
|
||||
}}
|
||||
/>
|
||||
{/* 자간 px 입력 컨트롤로 프리뷰 적용 근거 제공 */}
|
||||
|
||||
<NumericPropertyControl
|
||||
label="라디오 자간 (px)"
|
||||
unitLabel="(px)"
|
||||
label={m.letterSpacingLabel}
|
||||
unitLabel={m.letterSpacingUnitLabel}
|
||||
value={(() => {
|
||||
const raw = radioProps.letterSpacingCustom ?? "";
|
||||
const match = raw.match(/(-?[0-9]+(?:\.[0-9]+)?)/);
|
||||
@@ -472,8 +476,9 @@ export function FormRadioPropertiesPanel({ block, selectedBlockId, updateBlock }
|
||||
} as any);
|
||||
}}
|
||||
/>
|
||||
|
||||
<label className="flex flex-col gap-1 text-[11px] text-slate-400">
|
||||
<span>필드 너비</span>
|
||||
<span>Field width</span>
|
||||
<select
|
||||
className="w-full rounded border border-slate-300 bg-white px-2 py-1 text-[11px] text-slate-900 outline-none focus:border-sky-500 dark:border-slate-700 dark:bg-slate-900 dark:text-slate-100"
|
||||
value={radioProps.widthMode ?? "full"}
|
||||
@@ -483,23 +488,24 @@ export function FormRadioPropertiesPanel({ block, selectedBlockId, updateBlock }
|
||||
} as any)
|
||||
}
|
||||
>
|
||||
<option value="auto">자동</option>
|
||||
<option value="full">전체 폭</option>
|
||||
<option value="fixed">고정 값</option>
|
||||
<option value="auto">Auto</option>
|
||||
<option value="full">Full width</option>
|
||||
<option value="fixed">Fixed width</option>
|
||||
</select>
|
||||
</label>
|
||||
|
||||
{(radioProps.widthMode ?? "full") === "fixed" && (
|
||||
<NumericPropertyControl
|
||||
label="필드 고정 너비"
|
||||
label="Field fixed width"
|
||||
unitLabel="(px)"
|
||||
value={radioProps.widthPx ?? 240}
|
||||
min={80}
|
||||
max={800}
|
||||
step={10}
|
||||
presets={[
|
||||
{ id: "sm", label: "작게", value: 200 },
|
||||
{ id: "md", label: "보통", value: 280 },
|
||||
{ id: "lg", label: "넓게", value: 360 },
|
||||
{ id: "sm", label: "Small", value: 200 },
|
||||
{ id: "md", label: "Medium", value: 280 },
|
||||
{ id: "lg", label: "Large", value: 360 },
|
||||
]}
|
||||
onChangeValue={(v) => {
|
||||
updateBlock(selectedBlockId, {
|
||||
@@ -508,19 +514,20 @@ export function FormRadioPropertiesPanel({ block, selectedBlockId, updateBlock }
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
||||
<NumericPropertyControl
|
||||
label="라디오 가로 패딩 (px)"
|
||||
unitLabel="(px)"
|
||||
label={m.paddingXLabel}
|
||||
unitLabel={m.paddingXUnitLabel}
|
||||
value={typeof radioProps.paddingX === "number" ? radioProps.paddingX : 12}
|
||||
min={0}
|
||||
max={60}
|
||||
step={2}
|
||||
presets={[
|
||||
{ id: "xs", label: "아주 얇게", value: 6 },
|
||||
{ id: "sm", label: "얇게", value: 10 },
|
||||
{ id: "md", label: "보통", value: 12 },
|
||||
{ id: "lg", label: "넓게", value: 16 },
|
||||
{ id: "xl", label: "아주 넓게", value: 20 },
|
||||
{ id: "xs", label: "Extra small", value: 6 },
|
||||
{ id: "sm", label: "Small", value: 10 },
|
||||
{ id: "md", label: "Medium", value: 12 },
|
||||
{ id: "lg", label: "Large", value: 16 },
|
||||
{ id: "xl", label: "Extra large", value: 20 },
|
||||
]}
|
||||
onChangeValue={(v) =>
|
||||
updateBlock(selectedBlockId, {
|
||||
@@ -528,19 +535,20 @@ export function FormRadioPropertiesPanel({ block, selectedBlockId, updateBlock }
|
||||
} as any)
|
||||
}
|
||||
/>
|
||||
|
||||
<NumericPropertyControl
|
||||
label="라디오 세로 패딩 (px)"
|
||||
unitLabel="(px)"
|
||||
label={m.paddingYLabel}
|
||||
unitLabel={m.paddingYUnitLabel}
|
||||
value={typeof radioProps.paddingY === "number" ? radioProps.paddingY : 10}
|
||||
min={0}
|
||||
max={40}
|
||||
step={1}
|
||||
presets={[
|
||||
{ id: "xs", label: "아주 얇게", value: 6 },
|
||||
{ id: "sm", label: "얇게", value: 8 },
|
||||
{ id: "md", label: "보통", value: 10 },
|
||||
{ id: "lg", label: "넓게", value: 12 },
|
||||
{ id: "xl", label: "아주 넓게", value: 16 },
|
||||
{ id: "xs", label: "Extra small", value: 6 },
|
||||
{ id: "sm", label: "Small", value: 8 },
|
||||
{ id: "md", label: "Medium", value: 10 },
|
||||
{ id: "lg", label: "Large", value: 12 },
|
||||
{ id: "xl", label: "Extra large", value: 16 },
|
||||
]}
|
||||
onChangeValue={(v) =>
|
||||
updateBlock(selectedBlockId, {
|
||||
@@ -548,18 +556,19 @@ export function FormRadioPropertiesPanel({ block, selectedBlockId, updateBlock }
|
||||
} as any)
|
||||
}
|
||||
/>
|
||||
|
||||
<NumericPropertyControl
|
||||
label="라디오 옵션 간격 (px)"
|
||||
unitLabel="(px)"
|
||||
label={m.optionGapLabel}
|
||||
unitLabel={m.optionGapUnitLabel}
|
||||
value={typeof radioProps.optionGapPx === "number" ? radioProps.optionGapPx : 4}
|
||||
min={0}
|
||||
max={40}
|
||||
step={1}
|
||||
presets={[
|
||||
{ id: "tight", label: "타이트", value: 2 },
|
||||
{ id: "normal", label: "보통", value: 4 },
|
||||
{ id: "relaxed", label: "느슨", value: 8 },
|
||||
{ id: "extra", label: "넓게", value: 12 },
|
||||
{ id: "tight", label: "Tight", value: 2 },
|
||||
{ id: "normal", label: "Normal", value: 4 },
|
||||
{ id: "relaxed", label: "Relaxed", value: 8 },
|
||||
{ id: "extra", label: "Extra", value: 12 },
|
||||
]}
|
||||
onChangeValue={(v) =>
|
||||
updateBlock(selectedBlockId, {
|
||||
@@ -567,10 +576,11 @@ export function FormRadioPropertiesPanel({ block, selectedBlockId, updateBlock }
|
||||
} as any)
|
||||
}
|
||||
/>
|
||||
|
||||
<ColorPickerField
|
||||
label="텍스트 색상"
|
||||
ariaLabelColorInput="라디오 텍스트 색상 피커"
|
||||
ariaLabelHexInput="라디오 텍스트 색상 HEX"
|
||||
label={m.textColorLabel}
|
||||
ariaLabelColorInput={m.textColorPickerAria}
|
||||
ariaLabelHexInput={m.textColorHexAria}
|
||||
value={
|
||||
radioProps.textColorCustom && radioProps.textColorCustom.trim() !== ""
|
||||
? radioProps.textColorCustom
|
||||
@@ -588,10 +598,11 @@ export function FormRadioPropertiesPanel({ block, selectedBlockId, updateBlock }
|
||||
} as any);
|
||||
}}
|
||||
/>
|
||||
|
||||
<ColorPickerField
|
||||
label="배경 색상"
|
||||
ariaLabelColorInput="라디오 배경 색상 피커"
|
||||
ariaLabelHexInput="라디오 배경 색상 HEX"
|
||||
label={m.fillColorLabel}
|
||||
ariaLabelColorInput={m.fillColorPickerAria}
|
||||
ariaLabelHexInput={m.fillColorHexAria}
|
||||
value={
|
||||
radioProps.fillColorCustom && radioProps.fillColorCustom.trim() !== ""
|
||||
? radioProps.fillColorCustom
|
||||
@@ -609,10 +620,11 @@ export function FormRadioPropertiesPanel({ block, selectedBlockId, updateBlock }
|
||||
} as any);
|
||||
}}
|
||||
/>
|
||||
|
||||
<ColorPickerField
|
||||
label="테두리 색상"
|
||||
ariaLabelColorInput="라디오 테두리 색상 피커"
|
||||
ariaLabelHexInput="라디오 테두리 색상 HEX"
|
||||
label={m.strokeColorLabel}
|
||||
ariaLabelColorInput={m.strokeColorPickerAria}
|
||||
ariaLabelHexInput={m.strokeColorHexAria}
|
||||
value={
|
||||
radioProps.strokeColorCustom && radioProps.strokeColorCustom.trim() !== ""
|
||||
? radioProps.strokeColorCustom
|
||||
@@ -630,8 +642,9 @@ export function FormRadioPropertiesPanel({ block, selectedBlockId, updateBlock }
|
||||
} as any);
|
||||
}}
|
||||
/>
|
||||
|
||||
<NumericPropertyControl
|
||||
label="필드 모서리 둥글기"
|
||||
label={m.borderRadiusLabel}
|
||||
value={(() => {
|
||||
const r = radioProps.borderRadius ?? "md";
|
||||
return r === "none" ? 0 : r === "sm" ? 2 : r === "lg" ? 6 : r === "full" ? 8 : 4;
|
||||
@@ -640,11 +653,11 @@ export function FormRadioPropertiesPanel({ block, selectedBlockId, updateBlock }
|
||||
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 },
|
||||
{ id: "none", label: m.borderRadiusPresetNone, value: 0 },
|
||||
{ id: "sm", label: m.borderRadiusPresetSmall, value: 2 },
|
||||
{ id: "md", label: m.borderRadiusPresetMedium, value: 4 },
|
||||
{ id: "lg", label: m.borderRadiusPresetLarge, value: 6 },
|
||||
{ id: "full", label: m.borderRadiusPresetFull, value: 8 },
|
||||
]}
|
||||
onChangeValue={(v) => {
|
||||
const next =
|
||||
|
||||
Reference in New Issue
Block a user