197 lines
6.9 KiB
TypeScript
197 lines
6.9 KiB
TypeScript
"use client";
|
|
|
|
import React from "react";
|
|
import type { ButtonConfig } from "./types";
|
|
import { IconPicker } from "../common/IconPicker";
|
|
|
|
/**
|
|
* Button ConfigPanel — V2PropertiesPanel 에서 호출되는 설정 패널.
|
|
*
|
|
* text / variant / size / actionType / confirm / 색상 / 아이콘 편집.
|
|
* Phase A-3 의 최소 구현; Phase F 에서 아이콘 선택기 / action flow 연결 등 확장.
|
|
*/
|
|
|
|
export interface ButtonConfigPanelProps {
|
|
config?: ButtonConfig;
|
|
onChange?: (config: ButtonConfig) => void;
|
|
onUpdateProperty?: (componentId: string, path: string, value: unknown) => void;
|
|
selectedComponent?: { id: string; config?: ButtonConfig; [k: string]: any };
|
|
}
|
|
|
|
export const ButtonConfigPanel: React.FC<ButtonConfigPanelProps> = ({
|
|
config,
|
|
onChange,
|
|
selectedComponent,
|
|
}) => {
|
|
const current: ButtonConfig =
|
|
(config as ButtonConfig) ||
|
|
(selectedComponent?.config as ButtonConfig) ||
|
|
{};
|
|
|
|
const patch = (p: Partial<ButtonConfig>) => {
|
|
onChange?.({ ...current, ...p });
|
|
};
|
|
|
|
return (
|
|
<div className="flex flex-col gap-3 p-3 text-xs">
|
|
<div>
|
|
<label className="text-muted-foreground mb-1 block text-[0.62rem] font-semibold tracking-wider uppercase">
|
|
텍스트
|
|
</label>
|
|
<input
|
|
type="text"
|
|
value={current.text || ""}
|
|
onChange={(e) => patch({ text: e.target.value })}
|
|
placeholder="버튼"
|
|
className="border-border bg-background w-full rounded border px-2 py-1 text-xs"
|
|
/>
|
|
</div>
|
|
|
|
<div>
|
|
<label className="text-muted-foreground mb-1 block text-[0.62rem] font-semibold tracking-wider uppercase">
|
|
스타일 (variant)
|
|
</label>
|
|
<select
|
|
value={current.variant || "primary"}
|
|
onChange={(e) =>
|
|
patch({ variant: e.target.value as ButtonConfig["variant"] })
|
|
}
|
|
className="border-border bg-background w-full rounded border px-2 py-1 text-xs"
|
|
>
|
|
<option value="primary">주 (Primary)</option>
|
|
<option value="secondary">보조 (Secondary)</option>
|
|
<option value="default">기본 (Default)</option>
|
|
<option value="destructive">위험 (Destructive)</option>
|
|
<option value="outline">외곽선 (Outline)</option>
|
|
<option value="ghost">투명 (Ghost)</option>
|
|
</select>
|
|
</div>
|
|
|
|
<div>
|
|
<label className="text-muted-foreground mb-1 block text-[0.62rem] font-semibold tracking-wider uppercase">
|
|
크기
|
|
</label>
|
|
<select
|
|
value={current.size || "md"}
|
|
onChange={(e) =>
|
|
patch({ size: e.target.value as ButtonConfig["size"] })
|
|
}
|
|
className="border-border bg-background w-full rounded border px-2 py-1 text-xs"
|
|
>
|
|
<option value="sm">작게</option>
|
|
<option value="md">보통</option>
|
|
<option value="lg">크게</option>
|
|
</select>
|
|
</div>
|
|
|
|
<div>
|
|
<label className="text-muted-foreground mb-1 block text-[0.62rem] font-semibold tracking-wider uppercase">
|
|
액션 종류
|
|
</label>
|
|
<select
|
|
value={current.actionType || "save"}
|
|
onChange={(e) =>
|
|
patch({ actionType: e.target.value as ButtonConfig["actionType"] })
|
|
}
|
|
className="border-border bg-background w-full rounded border px-2 py-1 text-xs"
|
|
>
|
|
<option value="save">저장 (save)</option>
|
|
<option value="edit">수정 (edit)</option>
|
|
<option value="delete">삭제 (delete)</option>
|
|
<option value="add">추가 (add)</option>
|
|
<option value="cancel">취소 (cancel)</option>
|
|
<option value="close">닫기 (close)</option>
|
|
<option value="navigate">이동 (navigate)</option>
|
|
<option value="popup">팝업 (popup)</option>
|
|
<option value="search">검색 (search)</option>
|
|
<option value="reset">초기화 (reset)</option>
|
|
<option value="submit">제출 (submit)</option>
|
|
<option value="approval">승인 (approval)</option>
|
|
<option value="custom">커스텀 (custom)</option>
|
|
</select>
|
|
</div>
|
|
|
|
<div>
|
|
<label className="text-muted-foreground mb-1 block text-[0.62rem] font-semibold tracking-wider uppercase">
|
|
확인 메시지 (선택)
|
|
</label>
|
|
<input
|
|
type="text"
|
|
value={current.confirm || ""}
|
|
onChange={(e) => patch({ confirm: e.target.value || undefined })}
|
|
placeholder="비우면 즉시 실행"
|
|
className="border-border bg-background w-full rounded border px-2 py-1 text-xs"
|
|
/>
|
|
</div>
|
|
|
|
<div>
|
|
<label className="text-muted-foreground mb-1 block text-[0.62rem] font-semibold tracking-wider uppercase">
|
|
배경 색 (override)
|
|
</label>
|
|
<input
|
|
type="color"
|
|
value={current.backgroundColor || "#3b82f6"}
|
|
onChange={(e) => patch({ backgroundColor: e.target.value })}
|
|
className="border-border bg-background h-7 w-full rounded border px-1"
|
|
/>
|
|
</div>
|
|
|
|
<div>
|
|
<label className="text-muted-foreground mb-1 block text-[0.62rem] font-semibold tracking-wider uppercase">
|
|
텍스트 색 (override)
|
|
</label>
|
|
<input
|
|
type="color"
|
|
value={current.textColor || "#ffffff"}
|
|
onChange={(e) => patch({ textColor: e.target.value })}
|
|
className="border-border bg-background h-7 w-full rounded border px-1"
|
|
/>
|
|
</div>
|
|
|
|
<div>
|
|
<label className="text-muted-foreground mb-1 block text-[0.62rem] font-semibold tracking-wider uppercase">
|
|
모서리 반경
|
|
</label>
|
|
<input
|
|
type="text"
|
|
value={current.borderRadius || "6px"}
|
|
onChange={(e) => patch({ borderRadius: e.target.value })}
|
|
placeholder="6px"
|
|
className="border-border bg-background w-full rounded border px-2 py-1 text-xs"
|
|
/>
|
|
</div>
|
|
|
|
<div>
|
|
<label className="text-muted-foreground mb-1 block text-[0.62rem] font-semibold tracking-wider uppercase">
|
|
아이콘
|
|
</label>
|
|
<IconPicker
|
|
value={current.icon}
|
|
onChange={(v) => patch({ icon: v || undefined })}
|
|
/>
|
|
{current.icon && (
|
|
<select
|
|
value={current.iconPosition || "left"}
|
|
onChange={(e) => patch({ iconPosition: e.target.value as "left" | "right" })}
|
|
className="border-border bg-background mt-1 w-full rounded border px-2 py-1 text-xs"
|
|
>
|
|
<option value="left">아이콘 왼쪽</option>
|
|
<option value="right">아이콘 오른쪽</option>
|
|
</select>
|
|
)}
|
|
</div>
|
|
|
|
<label className="flex items-center gap-2 text-xs">
|
|
<input
|
|
type="checkbox"
|
|
checked={!!current.disabled}
|
|
onChange={(e) => patch({ disabled: e.target.checked })}
|
|
/>
|
|
<span>비활성화</span>
|
|
</label>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default ButtonConfigPanel;
|