st: feat: 일반검증 컴포넌트 구현중

This commit is contained in:
2026-03-06 13:04:14 +09:00
parent 727d448dc5
commit 6b4dd6aba7
3 changed files with 235 additions and 51 deletions
@@ -1,9 +1,9 @@
import { useRef, useEffect } from "react"
import { useRef, useEffect, useLayoutEffect } from "react"
import { ValueType } from "realgrid"
import RealGrid, { GridFitStyle } from "realgrid"
import { RealGridReact, RGDataColumn } from "realgrid-react"
import type { DataGridProps, RealGridColumnInfo, GridViewWithSetColumns, FooterItemWithCallback } from "../type/DataGrid.type"
import type { DataGridProps, RealGridColumnInfo, GridViewWithSetColumns, FooterItemWithCallback, CellStyleCallback } from "../type/DataGrid.type"
import { getComputedValueCallback } from "@/components/util/grid/computedFieldCallbacks"
// RealGrid 타입 확장
@@ -62,15 +62,36 @@ function createNumberComparer(dp: RealGridDataProviderComparer): (field: string
};
}
const CompactDataGrid = ({ columnInfo, fixedColumns, showHeaderCheckbox = false, rowData }: DataGridProps) => {
const CompactDataGrid = ({
columnInfo,
fixedColumns,
showHeaderCheckbox = false,
rowData,
setRowData,
getGridDataRef,
cellStyleCallback,
onRowClick,
}: DataGridProps) => {
const { columns, columnLayout } = columnInfo;
const gridRef = useRef<RealGridReact>(null);
const rowDataRef = useRef<Record<string, string | number>[]>(rowData);
const cellStyleCallbackRef = useRef<CellStyleCallback | undefined>(cellStyleCallback);
rowDataRef.current = rowData;
cellStyleCallbackRef.current = cellStyleCallback;
useLayoutEffect(() => {
if (!cellStyleCallback || !gridRef.current?.gridView) return;
gridRef.current.gridView.refresh();
}, [cellStyleCallback, rowData]);
useEffect(() => {
if (!gridRef.current?.gridView || !gridRef.current?.dataProvider) return;
const gridView = gridRef.current.gridView;
const dataProvider = gridRef.current.dataProvider;
if ((dataProvider as { setRows?: (r: unknown[]) => void })?.setRows && rowData) {
(dataProvider as { setRows: (r: unknown[]) => void }).setRows(rowData);
}
// setDataComparer로 숫자 정렬 (setFields 미사용 → 데이터 유실·필드 중복 오류 없음)
const dp = dataProvider as RealGridDataProviderComparer;
@@ -231,11 +252,11 @@ const CompactDataGrid = ({ columnInfo, fixedColumns, showHeaderCheckbox = false,
// footer가 단일 객체이고 valueCallback이 있는 경우
columns.forEach(col => {
if (!col.footer || Array.isArray(col.footer)) return;
try {
const column = currentGridView.columnByName(col.name);
if (!column || !column.footer) return;
if (col.footer.valueCallback) {
(column.footer as { valueCallback?: typeof col.footer.valueCallback }).valueCallback = col.footer.valueCallback;
}
@@ -243,9 +264,75 @@ const CompactDataGrid = ({ columnInfo, fixedColumns, showHeaderCheckbox = false,
console.warn(`Error setting footer valueCallback for column ${col.name}:`, e);
}
});
// cellStyleCallback이 있으면 컬럼별 styleCallback 설정
if (cellStyleCallbackRef.current) {
columns.forEach((column) => {
const baseStyleName = column.styleName;
const gridCol = currentGridView.columnByName(column.name) as {
styleCallback?: (g: unknown, dataCell: { itemIndex?: number; index?: { itemIndex?: number }; dataField?: string; fieldName?: string }) => string;
} | null;
if (gridCol) {
gridCol.styleCallback = (_g, dataCell) => {
const itemIndex = dataCell?.index?.itemIndex ?? dataCell?.itemIndex ?? -1;
const fieldName = (dataCell?.dataField ?? dataCell?.fieldName ?? column.fieldName) ?? '';
const data = rowDataRef.current;
if (itemIndex < 0 || itemIndex >= data.length) return baseStyleName ?? '';
const row = data[itemIndex] as Record<string, string | number>;
const extra = cellStyleCallbackRef.current?.({ rowIndex: itemIndex, fieldName, row });
if (!extra) return baseStyleName ?? '';
return baseStyleName ? `${extra} ${baseStyleName}` : extra;
};
}
});
}
currentGridView.refresh?.();
}, 200);
}, [columns, columnLayout, fixedColumns, rowData, showHeaderCheckbox]);
if (getGridDataRef) {
const fieldNames = columns.map((c) => c.fieldName).filter(Boolean) as string[];
getGridDataRef.current = () => {
const gv = gridRef.current?.gridView as {
getDataSource?: () => { getRowCount(): number; getValue(r: number, f: string): unknown };
} | undefined;
if (!gv?.getDataSource) return [];
const ds = gv.getDataSource();
if (!ds) return [];
const cnt = ds.getRowCount();
const rows: Record<string, string | number>[] = [];
for (let i = 0; i < cnt; i++) {
const r: Record<string, string | number> = {};
for (const f of fieldNames) r[f] = ds.getValue(i, f) as string | number;
rows.push(r);
}
return rows;
};
}
return () => {
if (getGridDataRef) getGridDataRef.current = null;
};
}, [columns, columnLayout, fixedColumns, rowData, showHeaderCheckbox, getGridDataRef]);
// 행 클릭 이벤트
useEffect(() => {
const gridView = gridRef.current?.gridView;
if (!gridView || !onRowClick) return;
gridView.onCellClicked = (grid, clickData) => {
if (clickData.cellType !== "data" || clickData.itemIndex === undefined) return;
try {
const row = rowDataRef.current[clickData.itemIndex];
onRowClick((row ?? {}) as Record<string, string | number>);
} catch (error) {
console.error("행 데이터 가져오기 오류:", error);
}
};
return () => {
gridView.onCellClicked = undefined;
};
}, [onRowClick]);
return (
<div className="flex flex-col gap-[0.6rem] mx-[0.6rem] w-1/2">
@@ -256,10 +343,15 @@ const CompactDataGrid = ({ columnInfo, fixedColumns, showHeaderCheckbox = false,
layout={columnLayout}
rows={rowData}
onInitialized={() => {
const dp = gridRef.current?.dataProvider as { setFields?: (fields: unknown[]) => void } | undefined;
const dp = gridRef.current?.dataProvider as
| { setFields?(f: unknown[]): void; setRows?(r: Record<string, string | number>[]): void }
| undefined;
if (dp?.setFields) {
dp.setFields(buildFieldsFromColumns(columns));
}
if (dp?.setRows && rowData?.length) {
dp.setRows(rowData);
}
}}
>
{columns.map((column) => (