214 lines
6.3 KiB
TypeScript
214 lines
6.3 KiB
TypeScript
"use client";
|
|
|
|
/**
|
|
* PivotGrid 컨텍스트 메뉴 컴포넌트
|
|
* 우클릭 시 정렬, 필터, 확장/축소 등의 옵션 제공
|
|
*/
|
|
|
|
import React from "react";
|
|
import {
|
|
ContextMenu,
|
|
ContextMenuContent,
|
|
ContextMenuItem,
|
|
ContextMenuSeparator,
|
|
ContextMenuSub,
|
|
ContextMenuSubContent,
|
|
ContextMenuSubTrigger,
|
|
ContextMenuTrigger,
|
|
} from "@/components/ui/context-menu";
|
|
import {
|
|
ArrowUpAZ,
|
|
ArrowDownAZ,
|
|
Filter,
|
|
ChevronDown,
|
|
ChevronRight,
|
|
Copy,
|
|
Eye,
|
|
EyeOff,
|
|
BarChart3,
|
|
} from "lucide-react";
|
|
import { PivotFieldConfig, AggregationType } from "../types";
|
|
|
|
interface PivotContextMenuProps {
|
|
children: React.ReactNode;
|
|
// 현재 컨텍스트 정보
|
|
cellType: "header" | "data" | "rowHeader" | "columnHeader";
|
|
field?: PivotFieldConfig;
|
|
rowPath?: string[];
|
|
columnPath?: string[];
|
|
value?: any;
|
|
// 콜백
|
|
onSort?: (field: string, direction: "asc" | "desc") => void;
|
|
onFilter?: (field: string) => void;
|
|
onExpand?: (path: string[]) => void;
|
|
onCollapse?: (path: string[]) => void;
|
|
onExpandAll?: () => void;
|
|
onCollapseAll?: () => void;
|
|
onCopy?: (value: any) => void;
|
|
onHideField?: (field: string) => void;
|
|
onChangeSummary?: (field: string, summaryType: AggregationType) => void;
|
|
onDrillDown?: (rowPath: string[], columnPath: string[]) => void;
|
|
}
|
|
|
|
export const PivotContextMenu: React.FC<PivotContextMenuProps> = ({
|
|
children,
|
|
cellType,
|
|
field,
|
|
rowPath,
|
|
columnPath,
|
|
value,
|
|
onSort,
|
|
onFilter,
|
|
onExpand,
|
|
onCollapse,
|
|
onExpandAll,
|
|
onCollapseAll,
|
|
onCopy,
|
|
onHideField,
|
|
onChangeSummary,
|
|
onDrillDown,
|
|
}) => {
|
|
const handleCopy = () => {
|
|
if (value !== undefined && value !== null) {
|
|
navigator.clipboard.writeText(String(value));
|
|
onCopy?.(value);
|
|
}
|
|
};
|
|
|
|
return (
|
|
<ContextMenu>
|
|
<ContextMenuTrigger asChild>{children}</ContextMenuTrigger>
|
|
<ContextMenuContent className="w-48">
|
|
{/* 정렬 옵션 (헤더에서만) */}
|
|
{(cellType === "rowHeader" || cellType === "columnHeader") && field && (
|
|
<>
|
|
<ContextMenuSub>
|
|
<ContextMenuSubTrigger>
|
|
<ArrowUpAZ className="mr-2 h-4 w-4" />
|
|
정렬
|
|
</ContextMenuSubTrigger>
|
|
<ContextMenuSubContent>
|
|
<ContextMenuItem onClick={() => onSort?.(field.field, "asc")}>
|
|
<ArrowUpAZ className="mr-2 h-4 w-4" />
|
|
오름차순
|
|
</ContextMenuItem>
|
|
<ContextMenuItem onClick={() => onSort?.(field.field, "desc")}>
|
|
<ArrowDownAZ className="mr-2 h-4 w-4" />
|
|
내림차순
|
|
</ContextMenuItem>
|
|
</ContextMenuSubContent>
|
|
</ContextMenuSub>
|
|
<ContextMenuSeparator />
|
|
</>
|
|
)}
|
|
|
|
{/* 확장/축소 옵션 */}
|
|
{(cellType === "rowHeader" || cellType === "columnHeader") && (
|
|
<>
|
|
{rowPath && rowPath.length > 0 && (
|
|
<>
|
|
<ContextMenuItem onClick={() => onExpand?.(rowPath)}>
|
|
<ChevronDown className="mr-2 h-4 w-4" />
|
|
확장
|
|
</ContextMenuItem>
|
|
<ContextMenuItem onClick={() => onCollapse?.(rowPath)}>
|
|
<ChevronRight className="mr-2 h-4 w-4" />
|
|
축소
|
|
</ContextMenuItem>
|
|
</>
|
|
)}
|
|
<ContextMenuItem onClick={onExpandAll}>
|
|
<ChevronDown className="mr-2 h-4 w-4" />
|
|
전체 확장
|
|
</ContextMenuItem>
|
|
<ContextMenuItem onClick={onCollapseAll}>
|
|
<ChevronRight className="mr-2 h-4 w-4" />
|
|
전체 축소
|
|
</ContextMenuItem>
|
|
<ContextMenuSeparator />
|
|
</>
|
|
)}
|
|
|
|
{/* 필터 옵션 */}
|
|
{field && onFilter && (
|
|
<>
|
|
<ContextMenuItem onClick={() => onFilter(field.field)}>
|
|
<Filter className="mr-2 h-4 w-4" />
|
|
필터
|
|
</ContextMenuItem>
|
|
<ContextMenuSeparator />
|
|
</>
|
|
)}
|
|
|
|
{/* 집계 함수 변경 (데이터 필드에서만) */}
|
|
{cellType === "data" && field && onChangeSummary && (
|
|
<>
|
|
<ContextMenuSub>
|
|
<ContextMenuSubTrigger>
|
|
<BarChart3 className="mr-2 h-4 w-4" />
|
|
집계 함수
|
|
</ContextMenuSubTrigger>
|
|
<ContextMenuSubContent>
|
|
<ContextMenuItem
|
|
onClick={() => onChangeSummary(field.field, "sum")}
|
|
>
|
|
합계
|
|
</ContextMenuItem>
|
|
<ContextMenuItem
|
|
onClick={() => onChangeSummary(field.field, "count")}
|
|
>
|
|
개수
|
|
</ContextMenuItem>
|
|
<ContextMenuItem
|
|
onClick={() => onChangeSummary(field.field, "avg")}
|
|
>
|
|
평균
|
|
</ContextMenuItem>
|
|
<ContextMenuItem
|
|
onClick={() => onChangeSummary(field.field, "min")}
|
|
>
|
|
최소
|
|
</ContextMenuItem>
|
|
<ContextMenuItem
|
|
onClick={() => onChangeSummary(field.field, "max")}
|
|
>
|
|
최대
|
|
</ContextMenuItem>
|
|
</ContextMenuSubContent>
|
|
</ContextMenuSub>
|
|
<ContextMenuSeparator />
|
|
</>
|
|
)}
|
|
|
|
{/* 드릴다운 (데이터 셀에서만) */}
|
|
{cellType === "data" && rowPath && columnPath && onDrillDown && (
|
|
<>
|
|
<ContextMenuItem onClick={() => onDrillDown(rowPath, columnPath)}>
|
|
<Eye className="mr-2 h-4 w-4" />
|
|
상세 데이터 보기
|
|
</ContextMenuItem>
|
|
<ContextMenuSeparator />
|
|
</>
|
|
)}
|
|
|
|
{/* 필드 숨기기 */}
|
|
{field && onHideField && (
|
|
<ContextMenuItem onClick={() => onHideField(field.field)}>
|
|
<EyeOff className="mr-2 h-4 w-4" />
|
|
필드 숨기기
|
|
</ContextMenuItem>
|
|
)}
|
|
|
|
{/* 복사 */}
|
|
<ContextMenuItem onClick={handleCopy}>
|
|
<Copy className="mr-2 h-4 w-4" />
|
|
복사
|
|
</ContextMenuItem>
|
|
</ContextMenuContent>
|
|
</ContextMenu>
|
|
);
|
|
};
|
|
|
|
export default PivotContextMenu;
|
|
|