Files
vexplor/frontend/hooks/useTableSettings.ts
DDD1542 1cadb71d70 Merge branch 'jskim-node' of https://g.wace.me/jskim/vexplor_dev into jskim-node
; Conflicts:
;	frontend/app/(main)/COMPANY_16/outsourcing/subcontractor/page.tsx
2026-04-03 16:01:54 +09:00

178 lines
5.8 KiB
TypeScript

"use client";
/**
* useTableSettings — 날코딩 페이지용 테이블 설정 훅
*
* TableSettingsModal과 함께 사용하여 컬럼 표시/숨김, 순서, 너비를 관리합니다.
* 설정은 localStorage에 자동 저장/복원됩니다.
*
* @example
* const ts = useTableSettings("item-info", TABLE_NAME, GRID_COLUMNS);
*
* // 툴바 버튼
* <Button variant="ghost" size="sm" onClick={() => ts.setOpen(true)}>
* <Settings2 className="h-4 w-4" />
* </Button>
*
* // 테이블 헤더 — GRID_COLUMNS 대신 ts.visibleColumns 사용
* {ts.visibleColumns.map(col => <TableHead key={col.key}>{col.label}</TableHead>)}
*
* // 모달 (JSX 하단)
* <TableSettingsModal
* open={ts.open}
* onOpenChange={ts.setOpen}
* tableName={ts.tableName}
* settingsId={ts.settingsId}
* onSave={ts.applySettings}
* />
*/
import React, { useState, useEffect, useCallback, useMemo } from "react";
import { loadTableSettings, type TableSettings, type BaseFilter } from "@/components/common/TableSettingsModal";
export function useTableSettings<T extends { key: string }>(
settingsId: string,
tableName: string,
defaultColumns: T[],
/** 초기 표시 컬럼 키 (미지정 시 defaultColumns 전체) */
initialVisibleKeys?: string[],
) {
const [open, setOpen] = useState(false);
const [visibleKeys, setVisibleKeys] = useState<Set<string>>(
() => new Set(initialVisibleKeys || defaultColumns.map((c) => c.key)),
);
const [columnWidths, setColumnWidths] = useState<Record<string, number>>({});
const [orderedKeys, setOrderedKeys] = useState<string[]>(
() => initialVisibleKeys || defaultColumns.map((c) => c.key),
);
const [baseFilter, setBaseFilter] = useState<BaseFilter | undefined>();
// 초기 filterConfig: GRID_COLUMNS에 있는 컬럼만 필터 가능 (전부 비활성)
const [filterConfig, setFilterConfig] = useState<TableSettings["filters"]>(
() =>
defaultColumns.map((c) => ({
columnName: c.key,
displayName: (c as any).label || c.key,
enabled: false,
filterType: "text" as const,
width: 25,
})),
);
/** TableSettingsModal onSave에 전달할 콜백 */
const applySettings = useCallback(
(settings: TableSettings) => {
const visible = new Set<string>();
const widths: Record<string, number> = {};
const order: string[] = [];
for (const cs of settings.columns) {
if (cs.visible) {
visible.add(cs.columnName);
widths[cs.columnName] = cs.width;
order.push(cs.columnName);
}
}
// settings에 없는 새 컬럼은 초기 표시 목록에 있을 때만 보이도록 추가
const initKeys = initialVisibleKeys
? new Set(initialVisibleKeys)
: new Set(defaultColumns.map((c) => c.key));
for (const col of defaultColumns) {
if (!settings.columns.find((c) => c.columnName === col.key) && initKeys.has(col.key)) {
visible.add(col.key);
order.push(col.key);
}
}
setVisibleKeys(visible);
setColumnWidths(widths);
setOrderedKeys(order);
// 화면에 표시된 컬럼만 필터 가능하도록 제한
setFilterConfig(
settings.filters?.filter((f) => visible.has(f.columnName)),
);
// 기본 데이터 필터
setBaseFilter(settings.baseFilter);
},
[defaultColumns, initialVisibleKeys],
);
// 마운트 시 저장된 설정 복원
useEffect(() => {
const saved = loadTableSettings(settingsId);
if (saved) applySettings(saved);
}, []); // eslint-disable-line react-hooks/exhaustive-deps
/** 설정이 적용된 컬럼 목록 (순서 + 표시 필터 적용) */
const visibleColumns = useMemo((): T[] => {
const colMap = new Map(defaultColumns.map((c) => [c.key, c]));
const result: T[] = [];
// 저장된 순서대로
for (const key of orderedKeys) {
if (visibleKeys.has(key)) {
const col = colMap.get(key);
if (col) result.push(col);
}
}
// orderedKeys에 없는 컬럼 (새로 추가된 것)
for (const col of defaultColumns) {
if (!orderedKeys.includes(col.key) && visibleKeys.has(col.key)) {
result.push(col);
}
}
return result.length > 0 ? result : defaultColumns;
}, [defaultColumns, orderedKeys, visibleKeys]);
/** 컬럼 표시 여부 확인 */
const isVisible = useCallback((key: string) => visibleKeys.has(key), [visibleKeys]);
/** 컬럼 너비 가져오기 (설정값 or undefined) */
const getWidth = useCallback(
(key: string): number | undefined => columnWidths[key],
[columnWidths],
);
/** TableHead/TableCell에 적용할 style 객체 (0 = 자동, 값 있으면 고정) */
const thStyle = useCallback(
(key: string): React.CSSProperties | undefined => {
const w = columnWidths[key];
if (!w || w <= 0) return undefined; // 0이면 브라우저 자동
return { width: `${w}px`, minWidth: `${w}px`, maxWidth: `${w}px` };
},
[columnWidths],
);
return {
/** 모달 open 상태 */
open,
/** 모달 open 상태 setter */
setOpen,
/** web-types API 호출용 테이블명 */
tableName,
/** localStorage 키 */
settingsId,
/** TableSettingsModal onSave 콜백 */
applySettings,
/** 설정 적용된 컬럼 배열 (순서 + 표시 필터) */
visibleColumns,
/** 특정 컬럼 표시 여부 */
isVisible,
/** 특정 컬럼 너비 (px) */
getWidth,
/** TableHead/TableCell style 객체 반환 */
thStyle,
/** 필터 설정 */
filterConfig,
/** 기본 데이터 필터 (예: division = '판매') */
baseFilter,
/** GRID_COLUMNS 기본 컬럼 키 목록 (TableSettingsModal defaultVisibleKeys용) */
defaultVisibleKeys: initialVisibleKeys || defaultColumns.map((c) => c.key),
};
}