ERP-node/frontend/components/report/designer/properties/MultiSelectProperties.tsx

207 lines
7.6 KiB
TypeScript

"use client";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { ComponentConfig } from "@/types/report";
import { useReportDesigner } from "@/contexts/ReportDesignerContext";
const FONT_FAMILIES = [
"Malgun Gothic",
"NanumGothic",
"NanumMyeongjo",
"굴림",
"돋움",
"바탕",
"Times New Roman",
"Arial",
];
interface Props {
componentIds: string[];
components: ComponentConfig[];
}
type EditableField =
| "fontSize"
| "fontFamily"
| "fontColor"
| "fontWeight"
| "textAlign"
| "backgroundColor"
| "borderWidth"
| "borderColor";
function getCommonValue<T>(components: ComponentConfig[], field: EditableField): T | "mixed" {
const values = components.map((c) => c[field as keyof ComponentConfig]);
const first = values[0];
return values.every((v) => v === first) ? (first as T) : "mixed";
}
export function MultiSelectProperties({ componentIds, components }: Props) {
const { updateComponent } = useReportDesigner();
const selected = components.filter((c) => componentIds.includes(c.id));
const count = selected.length;
const applyToAll = (patch: Partial<ComponentConfig>) => {
componentIds.forEach((id) => updateComponent(id, patch));
};
const commonFontSize = getCommonValue<number>(selected, "fontSize");
const commonFontFamily = getCommonValue<string>(selected, "fontFamily");
const commonFontColor = getCommonValue<string>(selected, "fontColor");
const commonFontWeight = getCommonValue<string>(selected, "fontWeight");
const commonTextAlign = getCommonValue<string>(selected, "textAlign");
const commonBgColor = getCommonValue<string>(selected, "backgroundColor");
const commonBorderWidth = getCommonValue<number>(selected, "borderWidth");
const commonBorderColor = getCommonValue<string>(selected, "borderColor");
return (
<Card>
<CardHeader className="pb-3">
<CardTitle className="text-sm">{count} </CardTitle>
</CardHeader>
<CardContent className="space-y-3">
<p className="text-xs text-gray-500"> .</p>
{/* 글꼴 크기 */}
<div>
<Label className="text-xs"> </Label>
<Input
type="number"
value={commonFontSize === "mixed" ? "" : (commonFontSize as number) || 13}
placeholder={commonFontSize === "mixed" ? "혼합" : undefined}
onChange={(e) => {
const val = parseInt(e.target.value);
if (val > 0) applyToAll({ fontSize: val });
}}
className="h-8"
/>
</div>
{/* 폰트 패밀리 */}
<div>
<Label className="text-xs"> </Label>
<Select
value={commonFontFamily === "mixed" ? "" : (commonFontFamily as string) || "Malgun Gothic"}
onValueChange={(value) => applyToAll({ fontFamily: value })}
>
<SelectTrigger className="h-8">
<SelectValue placeholder={commonFontFamily === "mixed" ? "혼합" : "선택"} />
</SelectTrigger>
<SelectContent>
{FONT_FAMILIES.map((f) => (
<SelectItem key={f} value={f} style={{ fontFamily: f }}>
{f}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
{/* 글꼴 색상 */}
<div>
<Label className="text-xs"> </Label>
<div className="flex gap-2">
<Input
type="color"
value={commonFontColor === "mixed" ? "#000000" : (commonFontColor as string) || "#000000"}
onChange={(e) => applyToAll({ fontColor: e.target.value })}
className="h-8 w-16"
/>
<Input
type="text"
value={commonFontColor === "mixed" ? "" : (commonFontColor as string) || "#000000"}
placeholder={commonFontColor === "mixed" ? "혼합" : undefined}
onChange={(e) => applyToAll({ fontColor: e.target.value })}
className="h-8 flex-1 font-mono text-xs"
/>
</div>
</div>
{/* 글꼴 굵기 */}
<div>
<Label className="text-xs"> </Label>
<Select
value={commonFontWeight === "mixed" ? "" : (commonFontWeight as string) || "normal"}
onValueChange={(value) => applyToAll({ fontWeight: value as "normal" | "bold" })}
>
<SelectTrigger className="h-8">
<SelectValue placeholder={commonFontWeight === "mixed" ? "혼합" : "선택"} />
</SelectTrigger>
<SelectContent>
<SelectItem value="normal"></SelectItem>
<SelectItem value="bold"></SelectItem>
</SelectContent>
</Select>
</div>
{/* 텍스트 정렬 */}
<div>
<Label className="text-xs"> </Label>
<Select
value={commonTextAlign === "mixed" ? "" : (commonTextAlign as string) || "left"}
onValueChange={(value) => applyToAll({ textAlign: value as "left" | "center" | "right" })}
>
<SelectTrigger className="h-8">
<SelectValue placeholder={commonTextAlign === "mixed" ? "혼합" : "선택"} />
</SelectTrigger>
<SelectContent>
<SelectItem value="left"></SelectItem>
<SelectItem value="center"></SelectItem>
<SelectItem value="right"></SelectItem>
</SelectContent>
</Select>
</div>
{/* 배경 색상 */}
<div>
<Label className="text-xs"> </Label>
<div className="flex gap-2">
<Input
type="color"
value={commonBgColor === "mixed" ? "#ffffff" : (commonBgColor as string) || "#ffffff"}
onChange={(e) => applyToAll({ backgroundColor: e.target.value })}
className="h-8 w-16"
/>
<Input
type="text"
value={commonBgColor === "mixed" ? "" : (commonBgColor as string) || "#ffffff"}
placeholder={commonBgColor === "mixed" ? "혼합" : undefined}
onChange={(e) => applyToAll({ backgroundColor: e.target.value })}
className="h-8 flex-1 font-mono text-xs"
/>
</div>
</div>
{/* 테두리 */}
<div className="grid grid-cols-2 gap-2">
<div>
<Label className="text-xs"> </Label>
<Input
type="number"
min="0"
max="10"
value={commonBorderWidth === "mixed" ? "" : (commonBorderWidth as number) || 0}
placeholder={commonBorderWidth === "mixed" ? "혼합" : undefined}
onChange={(e) => applyToAll({ borderWidth: parseInt(e.target.value) || 0 })}
className="h-8"
/>
</div>
<div>
<Label className="text-xs"> </Label>
<Input
type="color"
value={commonBorderColor === "mixed" ? "#cccccc" : (commonBorderColor as string) || "#cccccc"}
onChange={(e) => applyToAll({ borderColor: e.target.value })}
className="h-8"
/>
</div>
</div>
</CardContent>
</Card>
);
}