배포 다시.. #371
|
|
@ -376,6 +376,12 @@ export const PivotGridComponent: React.FC<PivotGridProps> = ({
|
|||
|
||||
const parsed = JSON.parse(savedState);
|
||||
|
||||
// 버전 체크 - 버전이 다르면 이전 상태 무시
|
||||
if (parsed.version !== PIVOT_STATE_VERSION) {
|
||||
localStorage.removeItem(stateStorageKey);
|
||||
return;
|
||||
}
|
||||
|
||||
// 필드 복원 시 유효성 검사 (중요!)
|
||||
if (parsed.fields && Array.isArray(parsed.fields) && parsed.fields.length > 0) {
|
||||
// 저장된 필드가 현재 데이터와 호환되는지 확인
|
||||
|
|
@ -501,19 +507,31 @@ export const PivotGridComponent: React.FC<PivotGridProps> = ({
|
|||
|
||||
if (activeFilters.length === 0) return data;
|
||||
|
||||
return data.filter((row) => {
|
||||
const result = data.filter((row) => {
|
||||
return activeFilters.every((filter) => {
|
||||
const value = row[filter.field];
|
||||
const rawValue = row[filter.field];
|
||||
const filterValues = filter.filterValues || [];
|
||||
const filterType = filter.filterType || "include";
|
||||
|
||||
// 타입 안전한 비교: 값을 문자열로 변환하여 비교
|
||||
const value = rawValue === null || rawValue === undefined
|
||||
? "(빈 값)"
|
||||
: String(rawValue);
|
||||
|
||||
if (filterType === "include") {
|
||||
return filterValues.includes(value);
|
||||
return filterValues.some((fv) => String(fv) === value);
|
||||
} else {
|
||||
return !filterValues.includes(value);
|
||||
return filterValues.every((fv) => String(fv) !== value);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// 모든 데이터가 필터링되면 경고 (디버깅용)
|
||||
if (result.length === 0 && data.length > 0) {
|
||||
console.warn("⚠️ [PivotGrid] 필터로 인해 모든 데이터가 제거됨");
|
||||
}
|
||||
|
||||
return result;
|
||||
}, [data, fields]);
|
||||
|
||||
// ==================== 피벗 처리 ====================
|
||||
|
|
@ -1654,7 +1672,10 @@ export const PivotGridComponent: React.FC<PivotGridProps> = ({
|
|||
<div
|
||||
ref={tableContainerRef}
|
||||
className="flex-1 overflow-auto focus:outline-none"
|
||||
style={{ maxHeight: enableVirtualScroll ? containerHeight : undefined }}
|
||||
style={{
|
||||
maxHeight: enableVirtualScroll && containerHeight > 0 ? containerHeight : undefined,
|
||||
minHeight: 100 // 최소 높이 보장 - 블라인드 효과 방지
|
||||
}}
|
||||
tabIndex={0}
|
||||
onKeyDown={handleKeyDown}
|
||||
>
|
||||
|
|
@ -1929,12 +1950,15 @@ export const PivotGridComponent: React.FC<PivotGridProps> = ({
|
|||
});
|
||||
})()}
|
||||
|
||||
{/* 가상 스크롤 하단 여백 */}
|
||||
{enableVirtualScroll && (
|
||||
<tr style={{ height: virtualScroll.totalHeight - virtualScroll.offsetTop - (visibleFlatRows.length * ROW_HEIGHT) }}>
|
||||
<td colSpan={rowFields.length + flatColumns.length + (totals?.showRowGrandTotals ? dataFields.length : 0)} />
|
||||
</tr>
|
||||
)}
|
||||
{/* 가상 스크롤 하단 여백 - 음수 방지 */}
|
||||
{enableVirtualScroll && (() => {
|
||||
const bottomPadding = Math.max(0, virtualScroll.totalHeight - virtualScroll.offsetTop - (visibleFlatRows.length * ROW_HEIGHT));
|
||||
return bottomPadding > 0 ? (
|
||||
<tr style={{ height: bottomPadding }}>
|
||||
<td colSpan={rowFields.length + flatColumns.length + (totals?.showRowGrandTotals ? dataFields.length : 0)} />
|
||||
</tr>
|
||||
) : null;
|
||||
})()}
|
||||
|
||||
{/* 열 총계 행 (하단 위치 - 기본값) */}
|
||||
{totals?.showColumnGrandTotals && totals?.rowGrandTotalPosition !== "top" && (
|
||||
|
|
|
|||
|
|
@ -51,14 +51,18 @@ export function useVirtualScroll(options: VirtualScrollOptions): VirtualScrollRe
|
|||
// 보이는 아이템 수
|
||||
const visibleCount = Math.ceil(containerHeight / itemHeight);
|
||||
|
||||
// 시작/끝 인덱스 계산
|
||||
// 시작/끝 인덱스 계산 (음수 방지)
|
||||
const { startIndex, endIndex } = useMemo(() => {
|
||||
// itemCount가 0이면 빈 배열
|
||||
if (itemCount === 0) {
|
||||
return { startIndex: 0, endIndex: -1 };
|
||||
}
|
||||
const start = Math.max(0, Math.floor(scrollTop / itemHeight) - overscan);
|
||||
const end = Math.min(
|
||||
itemCount - 1,
|
||||
Math.ceil((scrollTop + containerHeight) / itemHeight) + overscan
|
||||
);
|
||||
return { startIndex: start, endIndex: end };
|
||||
return { startIndex: start, endIndex: Math.max(start, end) }; // end가 start보다 작지 않도록
|
||||
}, [scrollTop, itemHeight, containerHeight, itemCount, overscan]);
|
||||
|
||||
// 전체 높이
|
||||
|
|
|
|||
|
|
@ -710,23 +710,9 @@ export function processPivotData(
|
|||
.filter((f) => f.area === "data" && f.visible !== false)
|
||||
.sort((a, b) => (a.areaIndex || 0) - (b.areaIndex || 0));
|
||||
|
||||
const filterFields = fields.filter(
|
||||
(f) => f.area === "filter" && f.visible !== false
|
||||
);
|
||||
|
||||
// 필터 적용
|
||||
let filteredData = data;
|
||||
for (const filterField of filterFields) {
|
||||
if (filterField.filterValues && filterField.filterValues.length > 0) {
|
||||
filteredData = filteredData.filter((row) => {
|
||||
const value = getFieldValue(row, filterField);
|
||||
if (filterField.filterType === "exclude") {
|
||||
return !filterField.filterValues!.includes(value);
|
||||
}
|
||||
return filterField.filterValues!.includes(value);
|
||||
});
|
||||
}
|
||||
}
|
||||
// 참고: 필터링은 PivotGridComponent에서 이미 처리됨
|
||||
// 여기서는 추가 필터링 없이 전달받은 데이터 사용
|
||||
const filteredData = data;
|
||||
|
||||
// 확장 경로 Set 변환 (잘못된 형식 필터링)
|
||||
const validRowPaths = (expandedRowPaths || []).filter(
|
||||
|
|
|
|||
Loading…
Reference in New Issue