feat(table-list): 컬럼 너비 자동 조정 및 정렬 상태 저장 기능 추가
- 데이터 내용 기반 컬럼 너비 자동 계산 (상위 50개 샘플링)
- 사용자가 조정한 컬럼 너비를 localStorage에 저장/복원
- 정렬 상태(컬럼, 방향)를 localStorage에 저장/복원
- 사용자별, 테이블별 독립적인 설정 관리
변경:
- TableListComponent.tsx: calculateOptimalColumnWidth 추가, 정렬 상태 저장/복원 로직 추가
- README.md: 새로운 기능 문서화
저장 키:
- table_column_widths_{테이블}_{사용자}: 컬럼 너비
- table_sort_state_{테이블}_{사용자}: 정렬 상태
Fixes: 수주관리 화면에서 컬럼 너비 수동 조정 번거로움, 정렬 설정 미유지 문제
This commit is contained in:
parent
51872de821
commit
1139cea838
|
|
@ -322,6 +322,7 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
|||
const [searchTerm, setSearchTerm] = useState("");
|
||||
const [sortColumn, setSortColumn] = useState<string | null>(null);
|
||||
const [sortDirection, setSortDirection] = useState<"asc" | "desc">("asc");
|
||||
const hasInitializedSort = useRef(false);
|
||||
const [columnLabels, setColumnLabels] = useState<Record<string, string>>({});
|
||||
const [tableLabel, setTableLabel] = useState<string>("");
|
||||
const [localPageSize, setLocalPageSize] = useState<number>(tableConfig.pagination?.pageSize || 20);
|
||||
|
|
@ -508,6 +509,28 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
|||
unregisterTable,
|
||||
]);
|
||||
|
||||
// 🎯 초기 로드 시 localStorage에서 정렬 상태 불러오기
|
||||
useEffect(() => {
|
||||
if (!tableConfig.selectedTable || !userId || hasInitializedSort.current) return;
|
||||
|
||||
const storageKey = `table_sort_state_${tableConfig.selectedTable}_${userId}`;
|
||||
const savedSort = localStorage.getItem(storageKey);
|
||||
|
||||
if (savedSort) {
|
||||
try {
|
||||
const { column, direction } = JSON.parse(savedSort);
|
||||
if (column && direction) {
|
||||
setSortColumn(column);
|
||||
setSortDirection(direction);
|
||||
hasInitializedSort.current = true;
|
||||
console.log("📂 localStorage에서 정렬 상태 복원:", { column, direction });
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("❌ 정렬 상태 복원 실패:", error);
|
||||
}
|
||||
}
|
||||
}, [tableConfig.selectedTable, userId]);
|
||||
|
||||
// 🆕 초기 로드 시 localStorage에서 컬럼 순서 불러오기
|
||||
useEffect(() => {
|
||||
if (!tableConfig.selectedTable || !userId) return;
|
||||
|
|
@ -955,6 +978,20 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
|||
newSortDirection = "asc";
|
||||
}
|
||||
|
||||
// 🎯 정렬 상태를 localStorage에 저장 (사용자별)
|
||||
if (tableConfig.selectedTable && userId) {
|
||||
const storageKey = `table_sort_state_${tableConfig.selectedTable}_${userId}`;
|
||||
try {
|
||||
localStorage.setItem(storageKey, JSON.stringify({
|
||||
column: newSortColumn,
|
||||
direction: newSortDirection
|
||||
}));
|
||||
console.log("💾 정렬 상태 저장:", { column: newSortColumn, direction: newSortDirection });
|
||||
} catch (error) {
|
||||
console.error("❌ 정렬 상태 저장 실패:", error);
|
||||
}
|
||||
}
|
||||
|
||||
console.log("📊 새로운 정렬 정보:", { newSortColumn, newSortDirection });
|
||||
console.log("🔍 onSelectedRowsChange 존재 여부:", !!onSelectedRowsChange);
|
||||
|
||||
|
|
@ -1876,11 +1913,59 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
|||
};
|
||||
}, [tableConfig.selectedTable, isDesignMode]);
|
||||
|
||||
// 초기 컬럼 너비 측정 (한 번만)
|
||||
// 🎯 컬럼 너비 자동 계산 (내용 기반)
|
||||
const calculateOptimalColumnWidth = useCallback((columnName: string, displayName: string): number => {
|
||||
// 기본 너비 설정
|
||||
const MIN_WIDTH = 100;
|
||||
const MAX_WIDTH = 400;
|
||||
const PADDING = 48; // 좌우 패딩 + 여유 공간
|
||||
const HEADER_PADDING = 60; // 헤더 추가 여유 (정렬 아이콘 등)
|
||||
|
||||
// 헤더 텍스트 너비 계산 (대략 8px per character)
|
||||
const headerWidth = (displayName?.length || columnName.length) * 10 + HEADER_PADDING;
|
||||
|
||||
// 데이터 셀 너비 계산 (상위 50개 샘플링)
|
||||
const sampleSize = Math.min(50, data.length);
|
||||
let maxDataWidth = headerWidth;
|
||||
|
||||
for (let i = 0; i < sampleSize; i++) {
|
||||
const cellValue = data[i]?.[columnName];
|
||||
if (cellValue !== null && cellValue !== undefined) {
|
||||
const cellText = String(cellValue);
|
||||
// 숫자는 좁게, 텍스트는 넓게 계산
|
||||
const isNumber = !isNaN(Number(cellValue)) && cellValue !== "";
|
||||
const charWidth = isNumber ? 8 : 9;
|
||||
const cellWidth = cellText.length * charWidth + PADDING;
|
||||
maxDataWidth = Math.max(maxDataWidth, cellWidth);
|
||||
}
|
||||
}
|
||||
|
||||
// 최소/최대 범위 내로 제한
|
||||
return Math.max(MIN_WIDTH, Math.min(MAX_WIDTH, Math.ceil(maxDataWidth)));
|
||||
}, [data]);
|
||||
|
||||
// 🎯 localStorage에서 컬럼 너비 불러오기 및 초기 계산
|
||||
useEffect(() => {
|
||||
if (!hasInitializedWidths.current && visibleColumns.length > 0) {
|
||||
// 약간의 지연을 두고 DOM이 완전히 렌더링된 후 측정
|
||||
if (!hasInitializedWidths.current && visibleColumns.length > 0 && data.length > 0) {
|
||||
const timer = setTimeout(() => {
|
||||
const storageKey = tableConfig.selectedTable && userId
|
||||
? `table_column_widths_${tableConfig.selectedTable}_${userId}`
|
||||
: null;
|
||||
|
||||
// 1. localStorage에서 저장된 너비 불러오기
|
||||
let savedWidths: Record<string, number> = {};
|
||||
if (storageKey) {
|
||||
try {
|
||||
const saved = localStorage.getItem(storageKey);
|
||||
if (saved) {
|
||||
savedWidths = JSON.parse(saved);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("컬럼 너비 불러오기 실패:", error);
|
||||
}
|
||||
}
|
||||
|
||||
// 2. 자동 계산 또는 저장된 너비 적용
|
||||
const newWidths: Record<string, number> = {};
|
||||
let hasAnyWidth = false;
|
||||
|
||||
|
|
@ -1888,13 +1973,18 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
|||
// 체크박스 컬럼은 제외 (고정 48px)
|
||||
if (column.columnName === "__checkbox__") return;
|
||||
|
||||
const thElement = columnRefs.current[column.columnName];
|
||||
if (thElement) {
|
||||
const measuredWidth = thElement.offsetWidth;
|
||||
if (measuredWidth > 0) {
|
||||
newWidths[column.columnName] = measuredWidth;
|
||||
hasAnyWidth = true;
|
||||
}
|
||||
// 저장된 너비가 있으면 우선 사용
|
||||
if (savedWidths[column.columnName]) {
|
||||
newWidths[column.columnName] = savedWidths[column.columnName];
|
||||
hasAnyWidth = true;
|
||||
} else {
|
||||
// 저장된 너비가 없으면 자동 계산
|
||||
const optimalWidth = calculateOptimalColumnWidth(
|
||||
column.columnName,
|
||||
columnLabels[column.columnName] || column.displayName
|
||||
);
|
||||
newWidths[column.columnName] = optimalWidth;
|
||||
hasAnyWidth = true;
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -1902,11 +1992,11 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
|||
setColumnWidths(newWidths);
|
||||
hasInitializedWidths.current = true;
|
||||
}
|
||||
}, 100);
|
||||
}, 150); // DOM 렌더링 대기
|
||||
|
||||
return () => clearTimeout(timer);
|
||||
}
|
||||
}, [visibleColumns]);
|
||||
}, [visibleColumns, data, tableConfig.selectedTable, userId, calculateOptimalColumnWidth, columnLabels]);
|
||||
|
||||
// ========================================
|
||||
// 페이지네이션 JSX
|
||||
|
|
@ -2241,7 +2331,21 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
|||
// 최종 너비를 state에 저장
|
||||
if (thElement) {
|
||||
const finalWidth = Math.max(80, thElement.offsetWidth);
|
||||
setColumnWidths((prev) => ({ ...prev, [column.columnName]: finalWidth }));
|
||||
setColumnWidths((prev) => {
|
||||
const newWidths = { ...prev, [column.columnName]: finalWidth };
|
||||
|
||||
// 🎯 localStorage에 컬럼 너비 저장 (사용자별)
|
||||
if (tableConfig.selectedTable && userId) {
|
||||
const storageKey = `table_column_widths_${tableConfig.selectedTable}_${userId}`;
|
||||
try {
|
||||
localStorage.setItem(storageKey, JSON.stringify(newWidths));
|
||||
} catch (error) {
|
||||
console.error("컬럼 너비 저장 실패:", error);
|
||||
}
|
||||
}
|
||||
|
||||
return newWidths;
|
||||
});
|
||||
}
|
||||
|
||||
// 텍스트 선택 복원
|
||||
|
|
|
|||
Loading…
Reference in New Issue