새로고침해도 상태 유지하는 선택박스 만들었음

This commit is contained in:
leeheejin
2026-01-21 17:10:15 +09:00
parent 62a82b3bcf
commit 63a4753701
3 changed files with 413 additions and 149 deletions

View File

@@ -10,6 +10,7 @@ import {
PivotFlatRow,
PivotFlatColumn,
PivotCellValue,
PivotColumnHeaderCell,
DateGroupInterval,
AggregationType,
SummaryDisplayMode,
@@ -76,6 +77,31 @@ export function pathToKey(path: string[]): string {
return path.join("||");
}
/**
* 모든 가능한 경로 생성 (열 전체 확장용)
*/
function generateAllPaths(
data: Record<string, any>[],
fields: PivotFieldConfig[]
): string[] {
const allPaths: string[] = [];
// 각 레벨까지의 고유 경로 수집
for (let depth = 1; depth <= fields.length; depth++) {
const fieldsAtDepth = fields.slice(0, depth);
const pathSet = new Set<string>();
data.forEach((row) => {
const path = fieldsAtDepth.map((f) => getFieldValue(row, f));
pathSet.add(pathToKey(path));
});
pathSet.forEach((pathKey) => allPaths.push(pathKey));
}
return allPaths;
}
/**
* 키를 경로로 변환
*/
@@ -326,6 +352,66 @@ function getMaxColumnLevel(
return Math.min(maxLevel, totalFields - 1);
}
/**
* 다중 행 열 헤더 생성
* 각 레벨별로 셀과 colSpan 정보를 반환
*/
function buildColumnHeaderLevels(
nodes: PivotHeaderNode[],
totalLevels: number
): PivotColumnHeaderCell[][] {
if (totalLevels === 0 || nodes.length === 0) {
return [];
}
const levels: PivotColumnHeaderCell[][] = Array.from(
{ length: totalLevels },
() => []
);
// 리프 노드 수 계산 (colSpan 계산용)
function countLeaves(node: PivotHeaderNode): number {
if (!node.children || node.children.length === 0 || !node.isExpanded) {
return 1;
}
return node.children.reduce((sum, child) => sum + countLeaves(child), 0);
}
// 트리 순회하며 각 레벨에 셀 추가
function traverse(node: PivotHeaderNode, level: number) {
const colSpan = countLeaves(node);
levels[level].push({
caption: node.caption,
colSpan,
path: node.path,
level,
});
if (node.children && node.isExpanded) {
for (const child of node.children) {
traverse(child, level + 1);
}
} else if (level < totalLevels - 1) {
// 확장되지 않은 노드는 다음 레벨들에 빈 셀로 채움
for (let i = level + 1; i < totalLevels; i++) {
levels[i].push({
caption: "",
colSpan,
path: node.path,
level: i,
});
}
}
}
for (const node of nodes) {
traverse(node, 0);
}
return levels;
}
// ==================== 데이터 매트릭스 생성 ====================
/**
@@ -735,12 +821,11 @@ export function processPivotData(
uniqueValues.forEach((val) => expandedRowSet.add(val));
}
if (expandedColumnPaths.length === 0 && columnFields.length > 0) {
const firstField = columnFields[0];
const uniqueValues = new Set(
filteredData.map((row) => getFieldValue(row, firstField))
);
uniqueValues.forEach((val) => expandedColSet.add(val));
// 열은 항상 전체 확장 (열 헤더는 확장/축소 UI가 없음)
// 모든 가능한 열 경로를 확장 상태로 설정
if (columnFields.length > 0) {
const allColumnPaths = generateAllPaths(filteredData, columnFields);
allColumnPaths.forEach((pathKey) => expandedColSet.add(pathKey));
}
// 헤더 트리 생성
@@ -788,6 +873,12 @@ export function processPivotData(
grandTotals.grand
);
// 다중 행 열 헤더 생성
const columnHeaderLevels = buildColumnHeaderLevels(
columnHeaders,
columnFields.length
);
return {
rowHeaders,
columnHeaders,
@@ -799,6 +890,7 @@ export function processPivotData(
caption: path[path.length - 1] || "",
span: 1,
})),
columnHeaderLevels,
grandTotals,
};
}