2025-10-24 10:37:02 +09:00
|
|
|
"use client";
|
|
|
|
|
|
|
|
|
|
import React, { useState } from "react";
|
|
|
|
|
import {
|
|
|
|
|
Dialog,
|
|
|
|
|
DialogContent,
|
|
|
|
|
DialogDescription,
|
|
|
|
|
DialogFooter,
|
|
|
|
|
DialogHeader,
|
|
|
|
|
DialogTitle,
|
|
|
|
|
} from "@/components/ui/dialog";
|
|
|
|
|
import { Button } from "@/components/ui/button";
|
|
|
|
|
import { Label } from "@/components/ui/label";
|
|
|
|
|
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
|
|
|
|
|
import { Input } from "@/components/ui/input";
|
|
|
|
|
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
|
|
|
|
|
import { Badge } from "@/components/ui/badge";
|
|
|
|
|
import { ArrowRight, ArrowDown } from "lucide-react";
|
|
|
|
|
|
|
|
|
|
interface FlowButtonGroupDialogProps {
|
|
|
|
|
open: boolean;
|
|
|
|
|
onOpenChange: (open: boolean) => void;
|
|
|
|
|
buttonCount: number;
|
|
|
|
|
onConfirm: (settings: {
|
|
|
|
|
direction: "horizontal" | "vertical";
|
|
|
|
|
gap: number;
|
|
|
|
|
align: "start" | "center" | "end" | "space-between" | "space-around";
|
|
|
|
|
}) => void;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export const FlowButtonGroupDialog: React.FC<FlowButtonGroupDialogProps> = ({
|
|
|
|
|
open,
|
|
|
|
|
onOpenChange,
|
|
|
|
|
buttonCount,
|
|
|
|
|
onConfirm,
|
|
|
|
|
}) => {
|
|
|
|
|
const [direction, setDirection] = useState<"horizontal" | "vertical">("horizontal");
|
|
|
|
|
const [gap, setGap] = useState<number>(8);
|
|
|
|
|
const [align, setAlign] = useState<"start" | "center" | "end" | "space-between" | "space-around">("start");
|
|
|
|
|
|
|
|
|
|
const handleConfirm = () => {
|
|
|
|
|
onConfirm({ direction, gap, align });
|
|
|
|
|
onOpenChange(false);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<Dialog open={open} onOpenChange={onOpenChange}>
|
|
|
|
|
<DialogContent className="max-w-[95vw] sm:max-w-[500px]">
|
|
|
|
|
<DialogHeader>
|
|
|
|
|
<DialogTitle className="text-base sm:text-lg">플로우 버튼 그룹 생성</DialogTitle>
|
|
|
|
|
<DialogDescription className="text-xs sm:text-sm">
|
|
|
|
|
{buttonCount}개의 버튼을 하나의 그룹으로 묶습니다. 자동 정렬 설정을 지정하세요.
|
|
|
|
|
</DialogDescription>
|
|
|
|
|
</DialogHeader>
|
|
|
|
|
|
|
|
|
|
<div className="space-y-4 sm:space-y-6">
|
|
|
|
|
{/* 정렬 방향 */}
|
|
|
|
|
<div className="space-y-3">
|
|
|
|
|
<Label className="text-sm font-medium">정렬 방향</Label>
|
|
|
|
|
<RadioGroup value={direction} onValueChange={(value: any) => setDirection(value)}>
|
|
|
|
|
<div className="flex items-center space-x-3">
|
|
|
|
|
<RadioGroupItem value="horizontal" id="direction-horizontal" />
|
|
|
|
|
<Label htmlFor="direction-horizontal" className="flex items-center gap-2 text-sm font-normal">
|
|
|
|
|
<ArrowRight className="h-4 w-4 text-blue-600" />
|
|
|
|
|
<span>가로 정렬</span>
|
|
|
|
|
<Badge variant="secondary" className="ml-2 text-xs">
|
|
|
|
|
← →
|
|
|
|
|
</Badge>
|
|
|
|
|
</Label>
|
|
|
|
|
</div>
|
|
|
|
|
<div className="flex items-center space-x-3">
|
|
|
|
|
<RadioGroupItem value="vertical" id="direction-vertical" />
|
|
|
|
|
<Label htmlFor="direction-vertical" className="flex items-center gap-2 text-sm font-normal">
|
|
|
|
|
<ArrowDown className="h-4 w-4 text-blue-600" />
|
|
|
|
|
<span>세로 정렬</span>
|
|
|
|
|
<Badge variant="secondary" className="ml-2 text-xs">
|
|
|
|
|
↑ ↓
|
|
|
|
|
</Badge>
|
|
|
|
|
</Label>
|
|
|
|
|
</div>
|
|
|
|
|
</RadioGroup>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
{/* 버튼 간격 */}
|
|
|
|
|
<div className="space-y-3">
|
|
|
|
|
<Label htmlFor="gap" className="text-sm font-medium">
|
|
|
|
|
버튼 간격 (px)
|
|
|
|
|
</Label>
|
|
|
|
|
<div className="flex items-center gap-3">
|
|
|
|
|
<Input
|
|
|
|
|
id="gap"
|
|
|
|
|
type="number"
|
|
|
|
|
min={0}
|
|
|
|
|
max={100}
|
|
|
|
|
value={gap}
|
|
|
|
|
onChange={(e) => setGap(Number(e.target.value))}
|
2025-10-28 17:33:03 +09:00
|
|
|
className="h-6 w-full px-2 py-0 text-xs" style={{ fontSize: "12px" }}
|
2025-10-24 10:37:02 +09:00
|
|
|
/>
|
|
|
|
|
<Badge variant="outline" className="text-xs">
|
|
|
|
|
{gap}px
|
|
|
|
|
</Badge>
|
|
|
|
|
</div>
|
|
|
|
|
<p className="text-[10px] text-muted-foreground sm:text-xs">버튼 사이의 간격을 설정합니다</p>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
{/* 정렬 방식 */}
|
|
|
|
|
<div className="space-y-3">
|
|
|
|
|
<Label htmlFor="align" className="text-sm font-medium">
|
|
|
|
|
정렬 방식
|
|
|
|
|
</Label>
|
|
|
|
|
<Select value={align} onValueChange={(value: any) => setAlign(value)}>
|
2025-10-28 17:33:03 +09:00
|
|
|
<SelectTrigger id="align" className="h-6 w-full px-2 py-0 text-xs" style={{ fontSize: "12px" }}>
|
2025-10-24 10:37:02 +09:00
|
|
|
<SelectValue />
|
|
|
|
|
</SelectTrigger>
|
|
|
|
|
<SelectContent>
|
|
|
|
|
<SelectItem value="start">
|
|
|
|
|
<div className="flex items-center gap-2">
|
|
|
|
|
<span>시작점 정렬</span>
|
|
|
|
|
<Badge variant="secondary" className="text-xs">
|
|
|
|
|
{direction === "horizontal" ? "← 왼쪽" : "↑ 위"}
|
|
|
|
|
</Badge>
|
|
|
|
|
</div>
|
|
|
|
|
</SelectItem>
|
|
|
|
|
<SelectItem value="center">
|
|
|
|
|
<div className="flex items-center gap-2">
|
|
|
|
|
<span>중앙 정렬</span>
|
|
|
|
|
<Badge variant="secondary" className="text-xs">
|
|
|
|
|
↔ 가운데
|
|
|
|
|
</Badge>
|
|
|
|
|
</div>
|
|
|
|
|
</SelectItem>
|
|
|
|
|
<SelectItem value="end">
|
|
|
|
|
<div className="flex items-center gap-2">
|
|
|
|
|
<span>끝점 정렬</span>
|
|
|
|
|
<Badge variant="secondary" className="text-xs">
|
|
|
|
|
{direction === "horizontal" ? "→ 오른쪽" : "↓ 아래"}
|
|
|
|
|
</Badge>
|
|
|
|
|
</div>
|
|
|
|
|
</SelectItem>
|
|
|
|
|
<SelectItem value="space-between">
|
|
|
|
|
<div className="flex items-center gap-2">
|
|
|
|
|
<span>양 끝 정렬</span>
|
|
|
|
|
<Badge variant="secondary" className="text-xs">
|
|
|
|
|
↔ 양끝
|
|
|
|
|
</Badge>
|
|
|
|
|
</div>
|
|
|
|
|
</SelectItem>
|
|
|
|
|
<SelectItem value="space-around">
|
|
|
|
|
<div className="flex items-center gap-2">
|
|
|
|
|
<span>균등 배분</span>
|
|
|
|
|
<Badge variant="secondary" className="text-xs">
|
|
|
|
|
↔ 균등
|
|
|
|
|
</Badge>
|
|
|
|
|
</div>
|
|
|
|
|
</SelectItem>
|
|
|
|
|
</SelectContent>
|
|
|
|
|
</Select>
|
|
|
|
|
<p className="text-[10px] text-muted-foreground sm:text-xs">
|
|
|
|
|
버튼들이 그룹 내에서 어떻게 배치될지 결정합니다
|
|
|
|
|
</p>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
{/* 미리보기 */}
|
|
|
|
|
<div className="rounded-lg border border-blue-200 bg-blue-50 p-4">
|
|
|
|
|
<p className="mb-3 text-xs font-medium text-blue-900">설정 미리보기</p>
|
|
|
|
|
<div className="space-y-2 text-xs text-blue-700">
|
|
|
|
|
<div className="flex items-center gap-2">
|
|
|
|
|
{direction === "horizontal" ? <ArrowRight className="h-3 w-3" /> : <ArrowDown className="h-3 w-3" />}
|
|
|
|
|
<span>
|
|
|
|
|
{direction === "horizontal" ? "가로" : "세로"} 방향으로 {gap}px 간격
|
|
|
|
|
</span>
|
|
|
|
|
</div>
|
|
|
|
|
<div className="flex items-center gap-2">
|
|
|
|
|
<span>•</span>
|
|
|
|
|
<span>
|
|
|
|
|
{align === "start" && "시작점"}
|
|
|
|
|
{align === "center" && "중앙"}
|
|
|
|
|
{align === "end" && "끝점"}
|
|
|
|
|
{align === "space-between" && "양 끝"}
|
|
|
|
|
{align === "space-around" && "균등"} 정렬
|
|
|
|
|
</span>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<DialogFooter className="gap-2 sm:gap-0">
|
|
|
|
|
<Button
|
|
|
|
|
variant="outline"
|
|
|
|
|
onClick={() => onOpenChange(false)}
|
|
|
|
|
className="h-9 flex-1 text-sm sm:h-10 sm:flex-none"
|
|
|
|
|
>
|
|
|
|
|
취소
|
|
|
|
|
</Button>
|
|
|
|
|
<Button onClick={handleConfirm} className="h-9 flex-1 text-sm sm:h-10 sm:flex-none">
|
|
|
|
|
그룹 생성
|
|
|
|
|
</Button>
|
|
|
|
|
</DialogFooter>
|
|
|
|
|
</DialogContent>
|
|
|
|
|
</Dialog>
|
|
|
|
|
);
|
|
|
|
|
};
|
|
|
|
|
|