ERP-node/frontend/components/screen/GroupingToolbar.tsx

350 lines
13 KiB
TypeScript

"use client";
import React, { useState } from "react";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Badge } from "@/components/ui/badge";
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
} from "@/components/ui/dialog";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
import {
Group,
Ungroup,
Palette,
Settings,
X,
Check,
AlignLeft,
AlignCenter,
AlignRight,
StretchHorizontal,
StretchVertical,
} from "lucide-react";
import { GroupState, ComponentData, ComponentStyle } from "@/types/screen";
import { createGroupStyle } from "@/lib/utils/groupingUtils";
interface GroupingToolbarProps {
groupState: GroupState;
onGroupStateChange: (state: GroupState) => void;
onGroupCreate: (componentIds: string[], title: string, style?: ComponentStyle) => void;
onGroupUngroup: (groupId: string) => void;
selectedComponents: ComponentData[];
allComponents: ComponentData[];
onGroupAlign?: (mode: "left" | "centerX" | "right" | "top" | "centerY" | "bottom") => void;
onGroupDistribute?: (orientation: "horizontal" | "vertical") => void;
}
export const GroupingToolbar: React.FC<GroupingToolbarProps> = ({
groupState,
onGroupStateChange,
onGroupCreate,
onGroupUngroup,
selectedComponents,
allComponents,
onGroupAlign,
onGroupDistribute,
}) => {
const [showCreateDialog, setShowCreateDialog] = useState(false);
const [groupTitle, setGroupTitle] = useState("새 그룹");
const [groupStyle, setGroupStyle] = useState<ComponentStyle>(createGroupStyle());
// 선택된 컴포넌트가 2개 이상인지 확인
const canCreateGroup = selectedComponents.length >= 2;
// 선택된 컴포넌트가 그룹인지 확인
const selectedGroup = selectedComponents.length === 1 && selectedComponents[0].type === "group";
const handleCreateGroup = () => {
if (canCreateGroup) {
setGroupTitle("새 그룹");
setGroupStyle(createGroupStyle());
setShowCreateDialog(true);
}
};
const handleUngroup = () => {
if (selectedGroup) {
onGroupUngroup(selectedComponents[0].id);
onGroupStateChange({
...groupState,
selectedComponents: [],
isGrouping: false,
});
}
};
const handleConfirmCreate = () => {
if (groupTitle.trim()) {
const componentIds = selectedComponents.map((c) => c.id);
onGroupCreate(componentIds, groupTitle.trim(), groupStyle);
setShowCreateDialog(false);
onGroupStateChange({
...groupState,
selectedComponents: [],
isGrouping: false,
});
}
};
const handleCancelCreate = () => {
setShowCreateDialog(false);
setGroupTitle("새 그룹");
setGroupStyle(createGroupStyle());
};
const handleStyleChange = (property: string, value: string) => {
setGroupStyle((prev) => ({
...prev,
[property]: value,
}));
};
return (
<>
<div className="flex items-center space-x-2 border-b bg-gray-50 p-2">
<div className="flex items-center space-x-2">
<Group className="h-4 w-4 text-blue-600" />
<span className="text-sm font-medium"></span>
</div>
{/* 선택된 컴포넌트 표시 */}
{selectedComponents.length > 0 && (
<Badge variant="secondary" className="ml-2">
{selectedComponents.length}
{selectedComponents.length > 1 && (
<span className="ml-1 text-xs opacity-75">(Shift+ , )</span>
)}
</Badge>
)}
<div className="ml-auto flex items-center space-x-1">
{/* 그룹 생성 버튼 */}
<Button
variant="outline"
size="sm"
onClick={handleCreateGroup}
disabled={!canCreateGroup}
title={canCreateGroup ? "선택된 컴포넌트를 그룹으로 묶기" : "2개 이상의 컴포넌트를 선택하세요"}
>
<Group className="mr-1 h-3 w-3" />
</Button>
{/* 그룹 해제 버튼 */}
<Button
variant="outline"
size="sm"
onClick={handleUngroup}
disabled={!selectedGroup}
title={selectedGroup ? "선택된 그룹 해제" : "그룹을 선택하세요"}
>
<Ungroup className="mr-1 h-3 w-3" />
</Button>
{/* 선택 해제 버튼 */}
{selectedComponents.length > 0 && (
<Button
variant="ghost"
size="sm"
onClick={() =>
onGroupStateChange({
...groupState,
selectedComponents: [],
isGrouping: false,
})
}
title="선택 해제"
>
<X className="h-3 w-3" />
</Button>
)}
{/* 정렬/분배 도구 */}
{selectedComponents.length > 1 && (
<div className="ml-2 flex items-center space-x-1">
<span className="mr-1 text-xs text-gray-500"></span>
<Button variant="outline" size="sm" onClick={() => onGroupAlign?.("left")} title="좌측 정렬">
<AlignLeft className="h-3 w-3" />
</Button>
<Button variant="outline" size="sm" onClick={() => onGroupAlign?.("centerX")} title="가로 중앙 정렬">
<AlignCenter className="h-3 w-3" />
</Button>
<Button variant="outline" size="sm" onClick={() => onGroupAlign?.("right")} title="우측 정렬">
<AlignRight className="h-3 w-3" />
</Button>
<Button variant="outline" size="sm" onClick={() => onGroupAlign?.("top")} title="상단 정렬">
<AlignLeft className="h-3 w-3 rotate-90" />
</Button>
<Button variant="outline" size="sm" onClick={() => onGroupAlign?.("centerY")} title="세로 중앙 정렬">
<AlignCenter className="h-3 w-3 rotate-90" />
</Button>
<Button variant="outline" size="sm" onClick={() => onGroupAlign?.("bottom")} title="하단 정렬">
<AlignRight className="h-3 w-3 rotate-90" />
</Button>
<div className="mx-1 h-4 w-px bg-gray-200" />
<span className="mr-1 text-xs text-gray-500"></span>
<Button
variant="outline"
size="sm"
onClick={() => onGroupDistribute?.("horizontal")}
title="가로 균등 분배"
>
<StretchHorizontal className="h-3 w-3" />
</Button>
<Button
variant="outline"
size="sm"
onClick={() => onGroupDistribute?.("vertical")}
title="세로 균등 분배"
>
<StretchVertical className="h-3 w-3" />
</Button>
</div>
)}
</div>
</div>
{/* 그룹 생성 다이얼로그 */}
<Dialog open={showCreateDialog} onOpenChange={setShowCreateDialog}>
<DialogContent className="sm:max-w-md">
<DialogHeader>
<DialogTitle> </DialogTitle>
<DialogDescription> {selectedComponents.length} .</DialogDescription>
</DialogHeader>
<div className="space-y-4">
{/* 그룹 제목 */}
<div className="space-y-2">
<Label htmlFor="groupTitle"> </Label>
<Input
id="groupTitle"
value={groupTitle}
onChange={(e) => setGroupTitle(e.target.value)}
placeholder="그룹 제목을 입력하세요"
maxLength={50}
/>
</div>
{/* 그룹 스타일 */}
<div className="space-y-4">
<div className="flex items-center space-x-2">
<Palette className="h-4 w-4 text-gray-600" />
<Label className="text-sm font-medium"> </Label>
</div>
<div className="grid grid-cols-2 gap-4">
{/* 배경색 */}
<div className="space-y-2">
<Label htmlFor="backgroundColor" className="text-xs">
</Label>
<Select
value={groupStyle.backgroundColor || "#f8f9fa"}
onValueChange={(value) => handleStyleChange("backgroundColor", value)}
>
<SelectTrigger>
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="#f8f9fa"> </SelectItem>
<SelectItem value="#ffffff"></SelectItem>
<SelectItem value="#e3f2fd"> </SelectItem>
<SelectItem value="#f3e5f5"> </SelectItem>
<SelectItem value="#e8f5e8"> </SelectItem>
<SelectItem value="#fff3e0"> </SelectItem>
</SelectContent>
</Select>
</div>
{/* 테두리 스타일 */}
<div className="space-y-2">
<Label htmlFor="borderStyle" className="text-xs">
</Label>
<Select
value={groupStyle.borderStyle || "solid"}
onValueChange={(value) => handleStyleChange("borderStyle", value)}
>
<SelectTrigger>
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="solid"></SelectItem>
<SelectItem value="dashed"></SelectItem>
<SelectItem value="dotted"></SelectItem>
<SelectItem value="none"></SelectItem>
</SelectContent>
</Select>
</div>
{/* 테두리 색상 */}
<div className="space-y-2">
<Label htmlFor="borderColor" className="text-xs">
</Label>
<Select
value={groupStyle.borderColor || "#dee2e6"}
onValueChange={(value) => handleStyleChange("borderColor", value)}
>
<SelectTrigger>
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="#dee2e6"></SelectItem>
<SelectItem value="#007bff"></SelectItem>
<SelectItem value="#28a745"></SelectItem>
<SelectItem value="#ffc107"></SelectItem>
<SelectItem value="#dc3545"></SelectItem>
<SelectItem value="#6f42c1"></SelectItem>
</SelectContent>
</Select>
</div>
{/* 모서리 둥글기 */}
<div className="space-y-2">
<Label htmlFor="borderRadius" className="text-xs">
</Label>
<Select
value={String(groupStyle.borderRadius || 8)}
onValueChange={(value) => handleStyleChange("borderRadius", value)}
>
<SelectTrigger>
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="0"> </SelectItem>
<SelectItem value="4"> </SelectItem>
<SelectItem value="8"> </SelectItem>
<SelectItem value="12"> </SelectItem>
<SelectItem value="16"> </SelectItem>
</SelectContent>
</Select>
</div>
</div>
</div>
</div>
<DialogFooter>
<Button variant="outline" onClick={handleCancelCreate}>
</Button>
<Button onClick={handleConfirmCreate} disabled={!groupTitle.trim()}>
<Check className="mr-1 h-3 w-3" />
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
</>
);
};