i18n 적용
CI / test (push) Failing after 11m12s
CI / e2e (push) Has been cancelled
CI / pr_and_merge (push) Has been cancelled

This commit is contained in:
2025-12-10 15:56:51 +09:00
parent 73e9bc6a1c
commit f71207aeb5
127 changed files with 7346 additions and 2079 deletions
@@ -3,6 +3,8 @@
import type { DividerBlockProps } from "@/features/editor/state/editorStore";
import { NumericPropertyControl } from "@/features/editor/components/NumericPropertyControl";
import { ColorPickerField, TEXT_COLOR_PALETTE } from "@/features/editor/components/ColorPickerField";
import { useAppLocale } from "@/features/i18n/LocaleProvider";
import { getEditorDividerPropertiesPanelMessages } from "@/features/i18n/messages/editorDividerPropertiesPanel";
export type DividerPropertiesPanelProps = {
dividerProps: DividerBlockProps;
@@ -11,77 +13,80 @@ export type DividerPropertiesPanelProps = {
};
export function DividerPropertiesPanel({ dividerProps, selectedBlockId, updateBlock }: DividerPropertiesPanelProps) {
const locale = useAppLocale();
const m = getEditorDividerPropertiesPanelMessages(locale);
return (
<>
<div className="space-y-1">
<label className="flex flex-col gap-1 text-xs text-slate-400">
<span></span>
<span>{m.alignLabel}</span>
<select
className="rounded border border-slate-300 bg-white px-2 py-1 text-xs text-slate-900 outline-none focus:border-sky-500 dark:border-slate-700 dark:bg-slate-900 dark:text-slate-100"
aria-label="구분선 정렬"
aria-label={m.alignAria}
value={dividerProps.align}
onChange={(e) => {
const value = e.target.value as DividerBlockProps["align"];
updateBlock(selectedBlockId, { align: value } as any);
}}
>
<option value="left"></option>
<option value="center"></option>
<option value="right"></option>
<option value="left">{m.alignOptionLeft}</option>
<option value="center">{m.alignOptionCenter}</option>
<option value="right">{m.alignOptionRight}</option>
</select>
</label>
</div>
<div className="space-y-1">
<label className="flex flex-col gap-1 text-xs text-slate-400">
<span></span>
<span>{m.thicknessLabel}</span>
<select
className="rounded border border-slate-300 bg-white px-2 py-1 text-xs text-slate-900 outline-none focus:border-sky-500 dark:border-slate-700 dark:bg-slate-900 dark:text-slate-100"
aria-label="구분선 두께"
aria-label={m.thicknessAria}
value={dividerProps.thickness}
onChange={(e) => {
const value = e.target.value as DividerBlockProps["thickness"];
updateBlock(selectedBlockId, { thickness: value } as any);
}}
>
<option value="thin"></option>
<option value="medium"></option>
<option value="thin">{m.thicknessOptionThin}</option>
<option value="medium">{m.thicknessOptionMedium}</option>
</select>
</label>
</div>
<div className="mt-3 space-y-2 border-t border-slate-800 pt-3 text-xs text-slate-400">
<h4 className="text-[11px] font-semibold text-slate-800 dark:text-slate-200"> </h4>
<h4 className="text-[11px] font-semibold text-slate-800 dark:text-slate-200">{m.styleSectionTitle}</h4>
{/* 길이/너비 모드 */}
<label className="flex flex-col gap-1">
<span> </span>
<span>{m.lengthModeLabel}</span>
<select
className="rounded border border-slate-300 bg-white px-2 py-1 text-xs text-slate-900 outline-none focus:border-sky-500 dark:border-slate-700 dark:bg-slate-900 dark:text-slate-100"
aria-label="구분선 길이 모드"
aria-label={m.lengthModeAria}
value={dividerProps.widthMode ?? "full"}
onChange={(e) => {
const value = e.target.value as NonNullable<DividerBlockProps["widthMode"]>;
updateBlock(selectedBlockId, { widthMode: value } as any);
}}
>
<option value="auto"> </option>
<option value="full"> </option>
<option value="fixed"> (px)</option>
<option value="auto">{m.lengthModeOptionAuto}</option>
<option value="full">{m.lengthModeOptionFull}</option>
<option value="fixed">{m.lengthModeOptionFixed}</option>
</select>
</label>
{(dividerProps.widthMode ?? "full") === "fixed" && (
<NumericPropertyControl
label="고정 길이 (px)"
unitLabel="(px)"
label={m.fixedLengthLabel}
unitLabel={m.fixedLengthUnitLabel}
value={dividerProps.widthPx ?? 320}
min={40}
max={1200}
step={10}
presets={[
{ id: "sm", label: "짧게", value: 240 },
{ id: "md", label: "보통", value: 320 },
{ id: "lg", label: "길게", value: 480 },
{ id: "sm", label: m.fixedLengthPresetShort, value: 240 },
{ id: "md", label: m.fixedLengthPresetNormal, value: 320 },
{ id: "lg", label: m.fixedLengthPresetLong, value: 480 },
]}
onChangeValue={(v) => {
updateBlock(selectedBlockId, { widthPx: v } as any);
@@ -91,9 +96,9 @@ export function DividerPropertiesPanel({ dividerProps, selectedBlockId, updateBl
{/* 색상 */}
<ColorPickerField
label="선 색상"
ariaLabelColorInput="구분선 색상 피커"
ariaLabelHexInput="구분선 색상 HEX"
label={m.colorLabel}
ariaLabelColorInput={m.colorPickerAria}
ariaLabelHexInput={m.colorHexAria}
value={
dividerProps.colorHex && dividerProps.colorHex.trim() !== ""
? dividerProps.colorHex
@@ -110,8 +115,8 @@ export function DividerPropertiesPanel({ dividerProps, selectedBlockId, updateBl
{/* 상하 여백 (슬라이더) */}
<NumericPropertyControl
label="위/아래 여백"
unitLabel="(px)"
label={m.marginLabel}
unitLabel={m.marginUnitLabel}
value={(() => {
if (typeof dividerProps.marginYPx === "number") {
return dividerProps.marginYPx;
@@ -123,9 +128,9 @@ export function DividerPropertiesPanel({ dividerProps, selectedBlockId, updateBl
max={50}
step={2}
presets={[
{ id: "sm", label: "작게", value: 8 },
{ id: "md", label: "보통", value: 16 },
{ id: "lg", label: "크게", value: 24 },
{ id: "sm", label: m.marginPresetSmall, value: 8 },
{ id: "md", label: m.marginPresetNormal, value: 16 },
{ id: "lg", label: m.marginPresetLarge, value: 24 },
]}
onChangeValue={(v) => {
updateBlock(selectedBlockId, { marginYPx: v } as any);