Files
invyone/frontend/lib/registry/components/button/ButtonConfigPanel.tsx
T

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;