st: fix: 그리드 mergeCell sort 오류 & ESLint 에러 수정

This commit is contained in:
2026-02-25 16:13:48 +09:00
parent 93e652a557
commit 5600ce01bd
19 changed files with 897 additions and 889 deletions
@@ -62,7 +62,7 @@ function createNumberComparer(dp: RealGridDataProviderComparer): (field: string
};
}
const CompactDataGrid = ({ columnInfo, fixedColumns, showHeaderCheckbox = false, rowData, setRowData }: DataGridProps) => {
const CompactDataGrid = ({ columnInfo, fixedColumns, showHeaderCheckbox = false, rowData }: DataGridProps) => {
const { columns, columnLayout } = columnInfo;
const gridRef = useRef<RealGridReact>(null);
@@ -255,7 +255,7 @@ const CompactDataGrid = ({ columnInfo, fixedColumns, showHeaderCheckbox = false,
autoGenerateField={true}
layout={columnLayout}
rows={rowData}
onInitialized={(grid) => {
onInitialized={() => {
const dp = gridRef.current?.dataProvider as { setFields?: (fields: unknown[]) => void } | undefined;
if (dp?.setFields) {
dp.setFields(buildFieldsFromColumns(columns));
+11 -4
View File
@@ -112,12 +112,14 @@ function createOnSortingHandler(
),
];
// mergeCells: true인 컬럼 찾기 (시군구 등) - 그룹 필드로 사용
// 시군구를 우선으로 찾고, 없으면 첫 번째 mergeCells 컬럼 사용
// 시군구(sggNm) 우선, 없으면 행정구역명(admdstNm), 없으면 첫 번째 mergeCells 컬럼
const mergeCellCols = columns.filter(
(c) => c.mergeCells === true && c.fieldName,
);
const mergeCellCol =
mergeCellCols.find((c) => c.fieldName === "gugun") || mergeCellCols[0];
mergeCellCols.find((c) => c.fieldName === "sggNm") ||
mergeCellCols.find((c) => c.fieldName === "admdstNm") ||
mergeCellCols[0];
const groupField = mergeCellCol?.fieldName || null;
const orderByField = "displayYear"; // 고정값 또는 설정 가능하게 할 수 있음
const hasMergeCells = mergeCellCols.length > 0;
@@ -148,7 +150,8 @@ function createOnSortingHandler(
const col = columns.find(
(c) => c.fieldName === sortField || c.name === sortField,
);
if (!col || col.sortdisable) return true;
// sortdisable 컬럼은 정렬 불가(클릭 무시)
if (!col || col.sortdisable) return false;
// mergeCells: true인 컬럼이 없으면 RealGrid 기본 정렬 사용
if (!hasMergeCells || !groupField) return true;
@@ -173,6 +176,7 @@ function createOnSortingHandler(
if (orig && setRows) {
setRows(orig);
setRowData(orig);
(g as { refresh?: () => void }).refresh?.();
}
sortDirRef.current[sortField] = "none";
return false;
@@ -191,11 +195,13 @@ function createOnSortingHandler(
);
setRows?.(sorted);
setRowData(sorted);
// 데이터 변경 후 merge 재적용
(g as { refresh?: () => void }).refresh?.();
return false;
};
}
/** sortPreserveGroupOrder: 그룹 간에는 sortField로 정렬, 그룹 내에서는 원본 순서 유지. */
/** sortPreserveGroupOrder: 그룹 간에는 sortField로 정렬, 그룹 내에서는 원본 순서 유지(구분 포함). */
function sortRowsGroupPreserving(
rows: Record<string, string | number>[],
sortField: string,
@@ -852,6 +858,7 @@ const DataGrid = ({
...(col.mergeCells && {
mergeRule: { criteria: "value" as const },
}),
sortable: !col.sortdisable,
};
if (col.footer) {
+83 -83
View File
@@ -57,96 +57,96 @@ function toNum(v: unknown): number {
}
/** 헤더에 정렬 화살표 표시 (CSS 방식) */
function updateHeaderSortIndicator(
gridView: {
columnByIndex?: (i: number) => { fieldName?: string; name?: string } | undefined;
columnByName?: (name: string) => { fieldName?: string; name?: string } | undefined;
getColumn?: (i: number) => { fieldName?: string; name?: string } | undefined;
getElement?: () => HTMLElement | null;
container?: HTMLElement;
},
columns: { fieldName?: string; name: string }[],
sortDir: Record<string, "asc" | "desc" | "none">,
gridElement?: HTMLElement | null
) {
try {
// 그리드 DOM 요소 찾기
let gridEl: HTMLElement | null = null;
if (gridElement) {
gridEl = gridElement;
} else if (gridView.getElement) {
gridEl = gridView.getElement();
} else if (gridView.container) {
gridEl = gridView.container;
} else {
// 폴백: 모든 그리드 중에서 찾기
const allGrids = document.querySelectorAll('.rg-grid, [data-grid-id]');
if (allGrids.length > 0) {
// 활성 탭의 그리드를 찾기 위해 TabsContent 내부의 그리드 찾기
const activeTabContent = document.querySelector('[data-state="active"]')?.closest('[role="tabpanel"]');
if (activeTabContent) {
gridEl = activeTabContent.querySelector('.rg-grid, [data-grid-id]') as HTMLElement;
}
if (!gridEl && allGrids.length === 1) {
gridEl = allGrids[0] as HTMLElement;
}
}
}
// function updateHeaderSortIndicator(
// gridView: {
// columnByIndex?: (i: number) => { fieldName?: string; name?: string } | undefined;
// columnByName?: (name: string) => { fieldName?: string; name?: string } | undefined;
// getColumn?: (i: number) => { fieldName?: string; name?: string } | undefined;
// getElement?: () => HTMLElement | null;
// container?: HTMLElement;
// },
// columns: { fieldName?: string; name: string }[],
// sortDir: Record<string, "asc" | "desc" | "none">,
// gridElement?: HTMLElement | null
// ) {
// try {
// // 그리드 DOM 요소 찾기
// let gridEl: HTMLElement | null = null;
// if (gridElement) {
// gridEl = gridElement;
// } else if (gridView.getElement) {
// gridEl = gridView.getElement();
// } else if (gridView.container) {
// gridEl = gridView.container;
// } else {
// // 폴백: 모든 그리드 중에서 찾기
// const allGrids = document.querySelectorAll('.rg-grid, [data-grid-id]');
// if (allGrids.length > 0) {
// // 활성 탭의 그리드를 찾기 위해 TabsContent 내부의 그리드 찾기
// const activeTabContent = document.querySelector('[data-state="active"]')?.closest('[role="tabpanel"]');
// if (activeTabContent) {
// gridEl = activeTabContent.querySelector('.rg-grid, [data-grid-id]') as HTMLElement;
// }
// if (!gridEl && allGrids.length === 1) {
// gridEl = allGrids[0] as HTMLElement;
// }
// }
// }
if (!gridEl) return;
// if (!gridEl) return;
columns.forEach((col) => {
if (!col.fieldName && !col.name) return;
// columns.forEach((col) => {
// if (!col.fieldName && !col.name) return;
const fieldName = col.fieldName || col.name;
const direction = sortDir[fieldName] || sortDir[col.name] || "none";
if (direction === "none") return;
// const fieldName = col.fieldName || col.name;
// const direction = sortDir[fieldName] || sortDir[col.name] || "none";
// if (direction === "none") return;
// 헤더 셀 찾기 (fieldName 또는 name으로)
const headerCells = gridEl.querySelectorAll('.rg-header-cell, [class*="header"], .rg-header');
let targetCell: HTMLElement | null = null;
// // 헤더 셀 찾기 (fieldName 또는 name으로)
// const headerCells = gridEl.querySelectorAll('.rg-header-cell, [class*="header"], .rg-header');
// let targetCell: HTMLElement | null = null;
headerCells.forEach((cell) => {
const el = cell as HTMLElement;
const text = el.textContent?.trim();
const colName = col.name;
const colFieldName = col.fieldName;
// 헤더 텍스트와 컬럼 이름이 일치하는지 확인
if (text && (
text === colName ||
text === colFieldName ||
text.includes(colName) ||
(colFieldName && text.includes(colFieldName))
)) {
targetCell = el;
}
});
// headerCells.forEach((cell) => {
// const el = cell as HTMLElement;
// const text = el.textContent?.trim();
// const colName = col.name;
// const colFieldName = col.fieldName;
// // 헤더 텍스트와 컬럼 이름이 일치하는지 확인
// if (text && (
// text === colName ||
// text === colFieldName ||
// text.includes(colName) ||
// (colFieldName && text.includes(colFieldName))
// )) {
// targetCell = el;
// }
// });
if (!targetCell) return;
// if (!targetCell) return;
const headerCell = targetCell as HTMLElement;
// const headerCell = targetCell as HTMLElement;
// 기존 화살표 제거
const existingIndicator = headerCell.querySelector(".rg-sort-indicator") as HTMLElement | null;
if (existingIndicator) {
existingIndicator.remove();
}
// // 기존 화살표 제거
// const existingIndicator = headerCell.querySelector(".rg-sort-indicator") as HTMLElement | null;
// if (existingIndicator) {
// existingIndicator.remove();
// }
// 화살표 추가
const indicator = document.createElement("span");
indicator.className = "rg-sort-indicator";
indicator.textContent = direction === "asc" ? "▲" : "▼";
indicator.style.marginLeft = "4px";
indicator.style.fontSize = "10px";
indicator.style.color = "#666";
indicator.style.display = "inline-block";
indicator.style.verticalAlign = "middle";
headerCell.appendChild(indicator);
});
} catch (e) {
console.warn("updateHeaderSortIndicator error:", e);
}
}
// // 화살표 추가
// const indicator = document.createElement("span");
// indicator.className = "rg-sort-indicator";
// indicator.textContent = direction === "asc" ? "▲" : "▼";
// indicator.style.marginLeft = "4px";
// indicator.style.fontSize = "10px";
// indicator.style.color = "#666";
// indicator.style.display = "inline-block";
// indicator.style.verticalAlign = "middle";
// headerCell.appendChild(indicator);
// });
// } catch (e) {
// console.warn("updateHeaderSortIndicator error:", e);
// }
// }
/** onSorting: mergeCells: true인 컬럼이 있으면 그룹 내 순서 유지 정렬, 없으면 RealGrid 기본 정렬 */
function createOnSortingHandler(
@@ -154,13 +154,13 @@ function createOnSortingHandler(
setRowData: React.Dispatch<React.SetStateAction<Record<string, string | number>[]>>,
sortDirRef: { current: Record<string, "asc" | "desc" | "none"> },
originalRowsRef: { current: Record<string, Record<string, string | number>[]> },
findGridElement?: () => HTMLElement | null
_findGridElement?: () => HTMLElement | null
) {
const fieldNames = [...new Set(columns.map((c) => c.fieldName).filter((x): x is string => typeof x === "string"))];
// mergeCells: true인 컬럼 찾기 (시군구 등) - 그룹 필드로 사용
// 시군구를 우선으로 찾고, 없으면 첫 번째 mergeCells 컬럼 사용
const mergeCellCols = columns.filter((c) => c.mergeCells === true && c.fieldName);
const mergeCellCol = mergeCellCols.find((c) => c.fieldName === 'gugun') || mergeCellCols[0];
const mergeCellCol = mergeCellCols.find((c) => c.fieldName === 'sggNm') || mergeCellCols[0];
const groupField = mergeCellCol?.fieldName || null;
const orderByField = 'displayYear'; // 고정값 또는 설정 가능하게 할 수 있음
const hasMergeCells = mergeCellCols.length > 0;