@ehfuse/mui-virtual-data-table 1.0.1 → 1.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -0
- package/dist/VirtualDataTable.d.ts +1 -1
- package/dist/index.esm.js +146 -31
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +145 -30
- package/dist/index.js.map +1 -1
- package/dist/types.d.ts +4 -0
- package/package.json +2 -2
- package/dist/OverlayScrollbar.d.ts +0 -81
- package/dist/VirtuosoScrollbar.d.ts +0 -31
- package/dist/components/Loading.d.ts +0 -29
- package/dist/components/Scrollbar.d.ts +0 -39
- package/dist/original/VirtualDataTable copy.d.ts +0 -78
- package/dist/original/VirtuosoScrollbar.d.ts +0 -31
- package/dist/utils/dragScrollUtils.d.ts +0 -39
package/README.md
CHANGED
|
@@ -108,6 +108,10 @@ function App() {
|
|
|
108
108
|
columnHeight={number} // Header height in px (default: 56, auto 2x for grouped headers)
|
|
109
109
|
showPaper={boolean} // Wrap in Paper component (default: true)
|
|
110
110
|
paddingX={string | number} // Horizontal padding (default: "1rem")
|
|
111
|
+
paddingTop={string | number} // Top padding (default: 0)
|
|
112
|
+
paddingBottom={string | number} // Bottom padding (default: 0)
|
|
113
|
+
rowHoverColor={string} // Row hover background color (default: "#000000", auto-inverted brightness in dark mode)
|
|
114
|
+
rowHoverOpacity={number} // Row hover opacity 0-1 (default: 0.06)
|
|
111
115
|
|
|
112
116
|
// Optional - Infinite Scroll
|
|
113
117
|
loading={boolean} // Loading state (default: false)
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* MIT License
|
|
5
5
|
*
|
|
6
|
-
|
|
6
|
+
* Copyright (c) 2025 KIM YOUNG JIN (ehfuse@gmail.com)
|
|
7
7
|
*
|
|
8
8
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
9
9
|
* of this software and associated documentation files (the "Software"), to deal
|
package/dist/index.esm.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
2
|
-
import { useRef, useState, useEffect, forwardRef, useMemo, useImperativeHandle,
|
|
2
|
+
import { useRef, useState, useEffect, forwardRef, useMemo, useCallback, useImperativeHandle, useLayoutEffect, memo } from 'react';
|
|
3
3
|
import { Box, CircularProgress, TableContainer, TableRow, TableCell, TableSortLabel, TableBody, TableHead, Table, Typography, Paper } from '@mui/material';
|
|
4
4
|
import { TableVirtuoso } from 'react-virtuoso';
|
|
5
5
|
|
|
@@ -531,6 +531,21 @@ showScrollbar = true, }, ref) => {
|
|
|
531
531
|
const thumbMinHeight = finalThumbConfig.minHeight;
|
|
532
532
|
const showArrows = finalArrowsConfig.visible;
|
|
533
533
|
const arrowStep = finalArrowsConfig.step;
|
|
534
|
+
// 포커스 유지 함수 (키보드 입력이 계속 작동하도록)
|
|
535
|
+
const maintainFocus = useCallback(() => {
|
|
536
|
+
if (!containerRef.current)
|
|
537
|
+
return;
|
|
538
|
+
// 현재 포커스된 요소 확인
|
|
539
|
+
const activeElement = document.activeElement;
|
|
540
|
+
// 오버레이 스크롤바 내부에 이미 포커스된 요소가 있으면 스킵
|
|
541
|
+
if (activeElement &&
|
|
542
|
+
containerRef.current.contains(activeElement) &&
|
|
543
|
+
activeElement !== containerRef.current) {
|
|
544
|
+
return;
|
|
545
|
+
}
|
|
546
|
+
// 포커스된 요소가 없거나 외부에 있으면 컨테이너에 포커스
|
|
547
|
+
containerRef.current.focus();
|
|
548
|
+
}, []);
|
|
534
549
|
// ref를 통해 외부에서 스크롤 컨테이너에 접근할 수 있도록 함
|
|
535
550
|
useImperativeHandle(ref, () => ({
|
|
536
551
|
getScrollContainer: () => containerRef.current,
|
|
@@ -654,7 +669,6 @@ showScrollbar = true, }, ref) => {
|
|
|
654
669
|
]);
|
|
655
670
|
// 썸 드래그 시작
|
|
656
671
|
const handleThumbMouseDown = useCallback((event) => {
|
|
657
|
-
var _a;
|
|
658
672
|
event.preventDefault();
|
|
659
673
|
event.stopPropagation();
|
|
660
674
|
const actualScrollContainer = findScrollableElement();
|
|
@@ -669,8 +683,8 @@ showScrollbar = true, }, ref) => {
|
|
|
669
683
|
clearHideTimer();
|
|
670
684
|
setScrollbarVisible(true);
|
|
671
685
|
// 포커스 유지 (키보드 입력이 계속 작동하도록)
|
|
672
|
-
(
|
|
673
|
-
}, [findScrollableElement, clearHideTimer]);
|
|
686
|
+
maintainFocus();
|
|
687
|
+
}, [findScrollableElement, clearHideTimer, maintainFocus]);
|
|
674
688
|
// 썸 드래그 중
|
|
675
689
|
const handleMouseMove = useCallback((event) => {
|
|
676
690
|
if (!isDragging)
|
|
@@ -704,7 +718,6 @@ showScrollbar = true, }, ref) => {
|
|
|
704
718
|
}, [isScrollable, setHideTimer, finalAutoHideConfig.delay]);
|
|
705
719
|
// 트랙 클릭으로 스크롤 점프
|
|
706
720
|
const handleTrackClick = useCallback((event) => {
|
|
707
|
-
var _a;
|
|
708
721
|
if (!scrollbarRef.current) {
|
|
709
722
|
return;
|
|
710
723
|
}
|
|
@@ -724,16 +737,16 @@ showScrollbar = true, }, ref) => {
|
|
|
724
737
|
setScrollbarVisible(true);
|
|
725
738
|
setHideTimer(finalAutoHideConfig.delay);
|
|
726
739
|
// 포커스 유지 (키보드 입력이 계속 작동하도록)
|
|
727
|
-
(
|
|
740
|
+
maintainFocus();
|
|
728
741
|
}, [
|
|
729
742
|
updateScrollbar,
|
|
730
743
|
setHideTimer,
|
|
731
744
|
finalAutoHideConfig.delay,
|
|
732
745
|
findScrollableElement,
|
|
746
|
+
maintainFocus,
|
|
733
747
|
]);
|
|
734
748
|
// 위쪽 화살표 클릭 핸들러
|
|
735
749
|
const handleUpArrowClick = useCallback((event) => {
|
|
736
|
-
var _a;
|
|
737
750
|
event.preventDefault();
|
|
738
751
|
event.stopPropagation();
|
|
739
752
|
if (!containerRef.current)
|
|
@@ -744,16 +757,16 @@ showScrollbar = true, }, ref) => {
|
|
|
744
757
|
setScrollbarVisible(true);
|
|
745
758
|
setHideTimer(finalAutoHideConfig.delay);
|
|
746
759
|
// 포커스 유지 (키보드 입력이 계속 작동하도록)
|
|
747
|
-
(
|
|
760
|
+
maintainFocus();
|
|
748
761
|
}, [
|
|
749
762
|
updateScrollbar,
|
|
750
763
|
setHideTimer,
|
|
751
764
|
arrowStep,
|
|
752
765
|
finalAutoHideConfig.delay,
|
|
766
|
+
maintainFocus,
|
|
753
767
|
]);
|
|
754
768
|
// 아래쪽 화살표 클릭 핸들러
|
|
755
769
|
const handleDownArrowClick = useCallback((event) => {
|
|
756
|
-
var _a;
|
|
757
770
|
event.preventDefault();
|
|
758
771
|
event.stopPropagation();
|
|
759
772
|
if (!containerRef.current || !contentRef.current)
|
|
@@ -767,12 +780,13 @@ showScrollbar = true, }, ref) => {
|
|
|
767
780
|
setScrollbarVisible(true);
|
|
768
781
|
setHideTimer(finalAutoHideConfig.delay);
|
|
769
782
|
// 포커스 유지 (키보드 입력이 계속 작동하도록)
|
|
770
|
-
(
|
|
783
|
+
maintainFocus();
|
|
771
784
|
}, [
|
|
772
785
|
updateScrollbar,
|
|
773
786
|
setHideTimer,
|
|
774
787
|
arrowStep,
|
|
775
788
|
finalAutoHideConfig.delay,
|
|
789
|
+
maintainFocus,
|
|
776
790
|
]);
|
|
777
791
|
// 드래그 스크롤 시작
|
|
778
792
|
const handleDragScrollStart = useCallback((event) => {
|
|
@@ -1020,7 +1034,7 @@ showScrollbar = true, }, ref) => {
|
|
|
1020
1034
|
return () => clearTimeout(timer);
|
|
1021
1035
|
}, [updateScrollbar]);
|
|
1022
1036
|
// 컴포넌트 초기화 완료 표시 (hover 이벤트 활성화용)
|
|
1023
|
-
|
|
1037
|
+
useLayoutEffect(() => {
|
|
1024
1038
|
const timer = setTimeout(() => {
|
|
1025
1039
|
setIsInitialized(true);
|
|
1026
1040
|
// 초기화 후 스크롤바 업데이트 (썸 높이 정확하게 계산)
|
|
@@ -1029,11 +1043,7 @@ showScrollbar = true, }, ref) => {
|
|
|
1029
1043
|
if (!finalAutoHideConfig.enabled && isScrollable()) {
|
|
1030
1044
|
setScrollbarVisible(true);
|
|
1031
1045
|
}
|
|
1032
|
-
|
|
1033
|
-
if (containerRef.current) {
|
|
1034
|
-
containerRef.current.focus();
|
|
1035
|
-
}
|
|
1036
|
-
}, 100);
|
|
1046
|
+
}, 0);
|
|
1037
1047
|
return () => clearTimeout(timer);
|
|
1038
1048
|
}, [isScrollable, updateScrollbar, finalAutoHideConfig.enabled]);
|
|
1039
1049
|
// Resize observer로 크기 변경 감지
|
|
@@ -1263,7 +1273,7 @@ const OVERLAY_SCROLLBAR_TRACK_CONFIG = {
|
|
|
1263
1273
|
/**
|
|
1264
1274
|
* 데이터 기반 무한 스크롤 및 가상화를 지원하는 테이블 컴포넌트
|
|
1265
1275
|
*/
|
|
1266
|
-
function VirtualDataTableComponent({ data, loading = false, columns, onRowClick, rowHeight = 50, columnHeight = 56, striped, rowDivider = true, onSort, onLoadMore, sortBy, sortDirection, showPaper = true, paddingX = "1rem", scrollbars, emptyMessage = "NO DATA", LoadingComponent, }) {
|
|
1276
|
+
function VirtualDataTableComponent({ data, loading = false, columns, onRowClick, rowHeight = 50, columnHeight = 56, striped, rowDivider = true, onSort, onLoadMore, sortBy, sortDirection, showPaper = true, paddingX = "1rem", paddingTop = 0, paddingBottom = 0, rowHoverColor, rowHoverOpacity, scrollbars, emptyMessage = "NO DATA", LoadingComponent, }) {
|
|
1267
1277
|
// console.log("=== VirtualDataTable 렌더링 ===", {
|
|
1268
1278
|
// dataLength: data.length,
|
|
1269
1279
|
// loading,
|
|
@@ -1303,11 +1313,13 @@ function VirtualDataTableComponent({ data, loading = false, columns, onRowClick,
|
|
|
1303
1313
|
},
|
|
1304
1314
|
"& .MuiTable-root": {
|
|
1305
1315
|
paddingRight: paddingX,
|
|
1316
|
+
paddingTop: paddingTop,
|
|
1317
|
+
paddingBottom: paddingBottom,
|
|
1306
1318
|
},
|
|
1307
1319
|
} }) }));
|
|
1308
1320
|
}),
|
|
1309
1321
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
1310
|
-
[] // 빈 배열: 최초 마운트 시에만 생성, scrollbars
|
|
1322
|
+
[] // 빈 배열: 최초 마운트 시에만 생성, scrollbars, paddingX, paddingTop, paddingBottom은 클로저로 고정
|
|
1311
1323
|
);
|
|
1312
1324
|
// Striped row 배경색 계산
|
|
1313
1325
|
const stripedRowColor = useMemo(() => {
|
|
@@ -1350,6 +1362,9 @@ function VirtualDataTableComponent({ data, loading = false, columns, onRowClick,
|
|
|
1350
1362
|
const isMouseDownRef = useRef(false);
|
|
1351
1363
|
const initialScrollTopRef = useRef(0);
|
|
1352
1364
|
const totalDragDistanceRef = useRef(0);
|
|
1365
|
+
const isScrollDraggingRef = useRef(false); // OverlayScrollbar 드래그 스크롤 감지용
|
|
1366
|
+
const mouseDownPositionRef = useRef({ x: 0, y: 0 }); // 마우스 다운 시작 위치
|
|
1367
|
+
useRef(null);
|
|
1353
1368
|
/**
|
|
1354
1369
|
* 마우스 버튼 누름 이벤트 핸들러
|
|
1355
1370
|
* OverlayScrollbar 사용시에는 기본 드래그 스크롤을 비활성화
|
|
@@ -1374,7 +1389,6 @@ function VirtualDataTableComponent({ data, loading = false, columns, onRowClick,
|
|
|
1374
1389
|
// DOM 스타일 직접 변경 (리렌더링 방지)
|
|
1375
1390
|
if (scrollContainerRef.current) {
|
|
1376
1391
|
scrollContainerRef.current.style.userSelect = "none";
|
|
1377
|
-
scrollContainerRef.current.style.webkitUserSelect = "none";
|
|
1378
1392
|
}
|
|
1379
1393
|
}
|
|
1380
1394
|
if (isDraggingRef.current) {
|
|
@@ -1418,7 +1432,6 @@ function VirtualDataTableComponent({ data, loading = false, columns, onRowClick,
|
|
|
1418
1432
|
// DOM 스타일 초기화
|
|
1419
1433
|
if (scrollContainerRef.current) {
|
|
1420
1434
|
scrollContainerRef.current.style.userSelect = "auto";
|
|
1421
|
-
scrollContainerRef.current.style.webkitUserSelect = "auto";
|
|
1422
1435
|
}
|
|
1423
1436
|
}, []);
|
|
1424
1437
|
// 정렬이 변경될 때 모든 TableSortLabel의 hover 상태 초기화
|
|
@@ -1461,8 +1474,8 @@ function VirtualDataTableComponent({ data, loading = false, columns, onRowClick,
|
|
|
1461
1474
|
return;
|
|
1462
1475
|
}
|
|
1463
1476
|
window.lastRangeChangeTime = now;
|
|
1464
|
-
// 더 보수적인 조건:
|
|
1465
|
-
const bufferSize = Math.max(10, Math.floor(data.length * 0.
|
|
1477
|
+
// 더 보수적인 조건: 90% 지점에서 로드 (기존 VirtualDataTable 방식)
|
|
1478
|
+
const bufferSize = Math.max(10, Math.floor(data.length * 0.1)); // 데이터의 10% 또는 최소 10개
|
|
1466
1479
|
const shouldLoadMore = range.endIndex >= data.length - bufferSize;
|
|
1467
1480
|
// 추가 조건: 최소 30개 이상의 데이터에서만 더 가져오기 실행
|
|
1468
1481
|
const hasMinimumData = data.length >= 30;
|
|
@@ -1538,7 +1551,6 @@ function VirtualDataTableComponent({ data, loading = false, columns, onRowClick,
|
|
|
1538
1551
|
width: col.width,
|
|
1539
1552
|
minWidth: col.width,
|
|
1540
1553
|
...col.style,
|
|
1541
|
-
backgroundColor: "#ffffff",
|
|
1542
1554
|
fontWeight: "bold",
|
|
1543
1555
|
position: "sticky",
|
|
1544
1556
|
top: 0,
|
|
@@ -1599,7 +1611,6 @@ function VirtualDataTableComponent({ data, loading = false, columns, onRowClick,
|
|
|
1599
1611
|
width: col.width,
|
|
1600
1612
|
minWidth: col.width,
|
|
1601
1613
|
...col.style,
|
|
1602
|
-
backgroundColor: "#ffffff",
|
|
1603
1614
|
fontWeight: "bold",
|
|
1604
1615
|
position: "sticky",
|
|
1605
1616
|
top: 0,
|
|
@@ -1661,7 +1672,6 @@ function VirtualDataTableComponent({ data, loading = false, columns, onRowClick,
|
|
|
1661
1672
|
},
|
|
1662
1673
|
} })] }) })) : (col.text) }, String(col.id)))),
|
|
1663
1674
|
...Object.entries(groupMap).map(([group, cols]) => (jsx(TableCell, { align: "center", colSpan: cols.length, style: {
|
|
1664
|
-
backgroundColor: "#ffffff",
|
|
1665
1675
|
fontWeight: "bold",
|
|
1666
1676
|
position: "sticky",
|
|
1667
1677
|
top: 0,
|
|
@@ -1676,7 +1686,6 @@ function VirtualDataTableComponent({ data, loading = false, columns, onRowClick,
|
|
|
1676
1686
|
width: col.width,
|
|
1677
1687
|
minWidth: col.width,
|
|
1678
1688
|
...col.style,
|
|
1679
|
-
backgroundColor: "#ffffff",
|
|
1680
1689
|
fontWeight: "bold",
|
|
1681
1690
|
position: "sticky",
|
|
1682
1691
|
top: 0,
|
|
@@ -1776,7 +1785,6 @@ function VirtualDataTableComponent({ data, loading = false, columns, onRowClick,
|
|
|
1776
1785
|
height: columnHeight,
|
|
1777
1786
|
"& th": {
|
|
1778
1787
|
padding: "16px",
|
|
1779
|
-
backgroundColor: "#ffffff",
|
|
1780
1788
|
position: "sticky",
|
|
1781
1789
|
top: 0,
|
|
1782
1790
|
zIndex: 2,
|
|
@@ -1791,10 +1799,32 @@ function VirtualDataTableComponent({ data, loading = false, columns, onRowClick,
|
|
|
1791
1799
|
// react-virtuoso는 'data-index' 속성으로 index를 전달합니다
|
|
1792
1800
|
const rowIndex = rest["data-index"] ?? 0;
|
|
1793
1801
|
const isOddRow = rowIndex % 2 === 1;
|
|
1794
|
-
return (jsx(TableRow, { ...rest,
|
|
1795
|
-
|
|
1802
|
+
return (jsx(TableRow, { ...rest, onMouseDown: (e) => {
|
|
1803
|
+
// 마우스 다운 시 드래그 플래그 초기화 및 시작 위치 저장
|
|
1804
|
+
isScrollDraggingRef.current = false;
|
|
1805
|
+
mouseDownPositionRef.current = {
|
|
1806
|
+
x: e.clientX,
|
|
1807
|
+
y: e.clientY,
|
|
1808
|
+
};
|
|
1809
|
+
}, onMouseMove: (e) => {
|
|
1810
|
+
// 마우스가 5px 이상 움직였을 때만 드래그로 간주
|
|
1811
|
+
const deltaX = Math.abs(e.clientX - mouseDownPositionRef.current.x);
|
|
1812
|
+
const deltaY = Math.abs(e.clientY - mouseDownPositionRef.current.y);
|
|
1813
|
+
const dragThreshold = 5; // 5px 임계값
|
|
1814
|
+
if (deltaX > dragThreshold ||
|
|
1815
|
+
deltaY > dragThreshold) {
|
|
1816
|
+
isScrollDraggingRef.current = true;
|
|
1817
|
+
}
|
|
1818
|
+
}, onClick: () => {
|
|
1819
|
+
// 드래그 스크롤이 아니고, 아이템이 있고, onRowClick이 있을 때만 실행
|
|
1820
|
+
if (!isScrollDraggingRef.current &&
|
|
1821
|
+
!isDraggingRef.current &&
|
|
1822
|
+
item &&
|
|
1823
|
+
onRowClick) {
|
|
1796
1824
|
onRowClick(item, rowIndex);
|
|
1797
1825
|
}
|
|
1826
|
+
// 클릭 후 플래그 리셋
|
|
1827
|
+
isScrollDraggingRef.current = false;
|
|
1798
1828
|
}, sx: {
|
|
1799
1829
|
userSelect: "none",
|
|
1800
1830
|
height: rowHeight,
|
|
@@ -1813,7 +1843,85 @@ function VirtualDataTableComponent({ data, loading = false, columns, onRowClick,
|
|
|
1813
1843
|
},
|
|
1814
1844
|
"&:hover": onRowClick
|
|
1815
1845
|
? {
|
|
1816
|
-
backgroundColor:
|
|
1846
|
+
backgroundColor: (theme) => {
|
|
1847
|
+
const isDark = theme.palette.mode === "dark";
|
|
1848
|
+
const defaultColor = "#000000";
|
|
1849
|
+
const color = rowHoverColor ?? defaultColor;
|
|
1850
|
+
const opacity = rowHoverOpacity ?? 0.06;
|
|
1851
|
+
// hex를 rgb로 변환
|
|
1852
|
+
const hex = color.replace("#", "");
|
|
1853
|
+
let r = parseInt(hex.substring(0, 2), 16) / 255;
|
|
1854
|
+
let g = parseInt(hex.substring(2, 4), 16) / 255;
|
|
1855
|
+
let b = parseInt(hex.substring(4, 6), 16) / 255;
|
|
1856
|
+
// 다크 모드일 때 밝기만 반전 (HSL 변환)
|
|
1857
|
+
if (isDark) {
|
|
1858
|
+
// RGB to HSL
|
|
1859
|
+
const max = Math.max(r, g, b);
|
|
1860
|
+
const min = Math.min(r, g, b);
|
|
1861
|
+
let h = 0, s = 0, l = (max + min) / 2;
|
|
1862
|
+
if (max !== min) {
|
|
1863
|
+
const d = max - min;
|
|
1864
|
+
s =
|
|
1865
|
+
l > 0.5
|
|
1866
|
+
? d / (2 - max - min)
|
|
1867
|
+
: d / (max + min);
|
|
1868
|
+
switch (max) {
|
|
1869
|
+
case r:
|
|
1870
|
+
h =
|
|
1871
|
+
((g - b) / d +
|
|
1872
|
+
(g < b
|
|
1873
|
+
? 6
|
|
1874
|
+
: 0)) /
|
|
1875
|
+
6;
|
|
1876
|
+
break;
|
|
1877
|
+
case g:
|
|
1878
|
+
h =
|
|
1879
|
+
((b - r) / d +
|
|
1880
|
+
2) /
|
|
1881
|
+
6;
|
|
1882
|
+
break;
|
|
1883
|
+
case b:
|
|
1884
|
+
h =
|
|
1885
|
+
((r - g) / d +
|
|
1886
|
+
4) /
|
|
1887
|
+
6;
|
|
1888
|
+
break;
|
|
1889
|
+
}
|
|
1890
|
+
}
|
|
1891
|
+
// 밝기만 반전 (0.0 <-> 1.0)
|
|
1892
|
+
l = 1 - l;
|
|
1893
|
+
// HSL to RGB
|
|
1894
|
+
const hue2rgb = (p, q, t) => {
|
|
1895
|
+
if (t < 0)
|
|
1896
|
+
t += 1;
|
|
1897
|
+
if (t > 1)
|
|
1898
|
+
t -= 1;
|
|
1899
|
+
if (t < 1 / 6)
|
|
1900
|
+
return (p + (q - p) * 6 * t);
|
|
1901
|
+
if (t < 1 / 2)
|
|
1902
|
+
return q;
|
|
1903
|
+
if (t < 2 / 3)
|
|
1904
|
+
return (p +
|
|
1905
|
+
(q - p) *
|
|
1906
|
+
(2 / 3 - t) *
|
|
1907
|
+
6);
|
|
1908
|
+
return p;
|
|
1909
|
+
};
|
|
1910
|
+
if (s === 0) {
|
|
1911
|
+
r = g = b = l;
|
|
1912
|
+
}
|
|
1913
|
+
else {
|
|
1914
|
+
const q = l < 0.5
|
|
1915
|
+
? l * (1 + s)
|
|
1916
|
+
: l + s - l * s;
|
|
1917
|
+
const p = 2 * l - q;
|
|
1918
|
+
r = hue2rgb(p, q, h + 1 / 3);
|
|
1919
|
+
g = hue2rgb(p, q, h);
|
|
1920
|
+
b = hue2rgb(p, q, h - 1 / 3);
|
|
1921
|
+
}
|
|
1922
|
+
}
|
|
1923
|
+
return `rgba(${Math.round(r * 255)}, ${Math.round(g * 255)}, ${Math.round(b * 255)}, ${opacity})`;
|
|
1924
|
+
},
|
|
1817
1925
|
transition: "background-color 0.2s ease",
|
|
1818
1926
|
}
|
|
1819
1927
|
: {},
|
|
@@ -1828,6 +1936,8 @@ function VirtualDataTableComponent({ data, loading = false, columns, onRowClick,
|
|
|
1828
1936
|
stripedRowColor,
|
|
1829
1937
|
rowDivider,
|
|
1830
1938
|
columnHeight,
|
|
1939
|
+
rowHoverColor,
|
|
1940
|
+
rowHoverOpacity,
|
|
1831
1941
|
VirtuosoScroller,
|
|
1832
1942
|
]);
|
|
1833
1943
|
// 공통 테이블 내용
|
|
@@ -1835,6 +1945,11 @@ function VirtualDataTableComponent({ data, loading = false, columns, onRowClick,
|
|
|
1835
1945
|
position: "relative",
|
|
1836
1946
|
height: "100%",
|
|
1837
1947
|
width: "100%",
|
|
1948
|
+
"& .MuiTableHead-root": {
|
|
1949
|
+
backgroundColor: (theme) => theme.palette.mode === "dark"
|
|
1950
|
+
? "#1e1e1e !important"
|
|
1951
|
+
: "#ffffff !important",
|
|
1952
|
+
},
|
|
1838
1953
|
}, children: [jsx(TableVirtuoso, { ref: virtuosoRef, data: data, totalCount: onLoadMore ? data.length + 1 : data.length, fixedHeaderContent: fixedHeaderContent, itemContent: rowContent, rangeChanged: handleRangeChange, components: VirtuosoTableComponents, style: { height: "100%" }, increaseViewportBy: { top: 100, bottom: 300 }, overscan: 5, followOutput: false }, tableKey), data.length === 0 && !loading && (jsx(Box, { sx: {
|
|
1839
1954
|
position: "absolute",
|
|
1840
1955
|
top: 0,
|
|
@@ -1863,7 +1978,7 @@ function VirtualDataTableComponent({ data, loading = false, columns, onRowClick,
|
|
|
1863
1978
|
show: data.length === 0, // 최초 로딩에만 배경 표시
|
|
1864
1979
|
opacity: 0.8,
|
|
1865
1980
|
} })) }))] }));
|
|
1866
|
-
return showPaper ? (jsx(Paper, { className: "grow",
|
|
1981
|
+
return showPaper ? (jsx(Paper, { className: "grow", elevation: 1, sx: {
|
|
1867
1982
|
padding: 0,
|
|
1868
1983
|
paddingLeft: paddingX,
|
|
1869
1984
|
height: "100%",
|