@ehfuse/mui-virtual-data-table 1.0.3 → 1.0.5
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/dist/index.esm.js +61 -34
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +61 -34
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.esm.js
CHANGED
|
@@ -421,7 +421,7 @@ const DEFAULT_TRACK_CONFIG = {};
|
|
|
421
421
|
const DEFAULT_ARROWS_CONFIG = {};
|
|
422
422
|
const DEFAULT_DRAG_SCROLL_CONFIG = {};
|
|
423
423
|
const DEFAULT_AUTO_HIDE_CONFIG = {};
|
|
424
|
-
const OverlayScrollbar = forwardRef(({ className = "", style = {}, children, onScroll,
|
|
424
|
+
const OverlayScrollbar = forwardRef(({ className = "", style = {}, containerStyle = {}, contentStyle = {}, children, onScroll,
|
|
425
425
|
// 그룹화된 설정 객체들
|
|
426
426
|
thumb = DEFAULT_THUMB_CONFIG, track = DEFAULT_TRACK_CONFIG, arrows = DEFAULT_ARROWS_CONFIG, dragScroll = DEFAULT_DRAG_SCROLL_CONFIG, autoHide = DEFAULT_AUTO_HIDE_CONFIG,
|
|
427
427
|
// 기타 설정들
|
|
@@ -455,6 +455,7 @@ showScrollbar = true, }, ref) => {
|
|
|
455
455
|
const [dragStart, setDragStart] = useState({ y: 0, scrollTop: 0 });
|
|
456
456
|
const [thumbHeight, setThumbHeight] = useState(0);
|
|
457
457
|
const [thumbTop, setThumbTop] = useState(0);
|
|
458
|
+
const [hasScrollableContent, setHasScrollableContent] = useState(false);
|
|
458
459
|
// 드래그 스크롤 상태
|
|
459
460
|
const [isDragScrolling, setIsDragScrolling] = useState(false);
|
|
460
461
|
const [dragScrollStart, setDragScrollStart] = useState({
|
|
@@ -591,9 +592,33 @@ showScrollbar = true, }, ref) => {
|
|
|
591
592
|
return containerRef.current;
|
|
592
593
|
}
|
|
593
594
|
// children 요소에서 스크롤 가능한 요소 찾기
|
|
595
|
+
// 중첩된 OverlayScrollbar의 영역은 제외 (다른 OverlayScrollbar의 container는 스킵)
|
|
594
596
|
const childScrollableElements = containerRef.current.querySelectorAll('[data-virtuoso-scroller], [style*="overflow"], .virtuoso-scroller, [style*="overflow: auto"], [style*="overflow:auto"]');
|
|
595
597
|
for (const child of childScrollableElements) {
|
|
596
598
|
const element = child;
|
|
599
|
+
// 이 요소가 다른 OverlayScrollbar의 container인지 확인
|
|
600
|
+
// (자신의 containerRef는 아니어야 하고, overlay-scrollbar-container 클래스를 가진 경우)
|
|
601
|
+
if (element !== containerRef.current &&
|
|
602
|
+
element.classList.contains("overlay-scrollbar-container")) {
|
|
603
|
+
// 중첩된 OverlayScrollbar의 container이므로 스킵
|
|
604
|
+
continue;
|
|
605
|
+
}
|
|
606
|
+
// 이 요소의 부모 중에 다른 OverlayScrollbar container가 있는지 확인
|
|
607
|
+
let parent = element.parentElement;
|
|
608
|
+
let isNestedInAnotherScrollbar = false;
|
|
609
|
+
while (parent && parent !== containerRef.current) {
|
|
610
|
+
if (parent.classList.contains("overlay-scrollbar-container") &&
|
|
611
|
+
parent !== containerRef.current) {
|
|
612
|
+
// 다른 OverlayScrollbar 내부의 요소이므로 스킵
|
|
613
|
+
isNestedInAnotherScrollbar = true;
|
|
614
|
+
break;
|
|
615
|
+
}
|
|
616
|
+
parent = parent.parentElement;
|
|
617
|
+
}
|
|
618
|
+
if (isNestedInAnotherScrollbar) {
|
|
619
|
+
continue;
|
|
620
|
+
}
|
|
621
|
+
// 스크롤 가능한 요소인지 확인
|
|
597
622
|
if (element.scrollHeight > element.clientHeight + 2) {
|
|
598
623
|
cachedScrollContainerRef.current = element;
|
|
599
624
|
return element;
|
|
@@ -626,15 +651,18 @@ showScrollbar = true, }, ref) => {
|
|
|
626
651
|
}, [clearHideTimer, finalAutoHideConfig.enabled]);
|
|
627
652
|
// 스크롤바 위치 및 크기 업데이트
|
|
628
653
|
const updateScrollbar = useCallback(() => {
|
|
629
|
-
if (!scrollbarRef.current)
|
|
630
|
-
return;
|
|
631
654
|
const scrollableElement = findScrollableElement();
|
|
632
655
|
if (!scrollableElement) {
|
|
633
656
|
// 스크롤 불가능하면 숨김
|
|
634
657
|
setScrollbarVisible(false);
|
|
658
|
+
setHasScrollableContent(false);
|
|
635
659
|
clearHideTimer();
|
|
636
660
|
return;
|
|
637
661
|
}
|
|
662
|
+
// 스크롤 가능한 콘텐츠가 있음을 표시
|
|
663
|
+
setHasScrollableContent(true);
|
|
664
|
+
if (!scrollbarRef.current)
|
|
665
|
+
return;
|
|
638
666
|
// 자동 숨김이 비활성화되어 있으면 스크롤바를 항상 표시
|
|
639
667
|
if (!finalAutoHideConfig.enabled) {
|
|
640
668
|
setScrollbarVisible(true);
|
|
@@ -892,10 +920,25 @@ showScrollbar = true, }, ref) => {
|
|
|
892
920
|
const container = containerRef.current;
|
|
893
921
|
if (container && !scrollableElement) {
|
|
894
922
|
elementsToWatch.push(container);
|
|
895
|
-
// children 요소들의 스크롤도 감지
|
|
923
|
+
// children 요소들의 스크롤도 감지 (중첩된 OverlayScrollbar 제외)
|
|
896
924
|
const childScrollableElements = container.querySelectorAll('[data-virtuoso-scroller], [style*="overflow"], .virtuoso-scroller, [style*="overflow: auto"], [style*="overflow:auto"]');
|
|
897
925
|
childScrollableElements.forEach((child) => {
|
|
898
|
-
|
|
926
|
+
const element = child;
|
|
927
|
+
// 다른 OverlayScrollbar의 container는 제외
|
|
928
|
+
if (element !== container &&
|
|
929
|
+
element.classList.contains("overlay-scrollbar-container")) {
|
|
930
|
+
return;
|
|
931
|
+
}
|
|
932
|
+
// 부모 중에 다른 OverlayScrollbar container가 있으면 제외
|
|
933
|
+
let parent = element.parentElement;
|
|
934
|
+
while (parent && parent !== container) {
|
|
935
|
+
if (parent.classList.contains("overlay-scrollbar-container") &&
|
|
936
|
+
parent !== container) {
|
|
937
|
+
return; // 중첩된 OverlayScrollbar 내부이므로 제외
|
|
938
|
+
}
|
|
939
|
+
parent = parent.parentElement;
|
|
940
|
+
}
|
|
941
|
+
elementsToWatch.push(element);
|
|
899
942
|
});
|
|
900
943
|
}
|
|
901
944
|
// 모든 요소에 이벤트 리스너 등록
|
|
@@ -1035,16 +1078,13 @@ showScrollbar = true, }, ref) => {
|
|
|
1035
1078
|
}, [updateScrollbar]);
|
|
1036
1079
|
// 컴포넌트 초기화 완료 표시 (hover 이벤트 활성화용)
|
|
1037
1080
|
useLayoutEffect(() => {
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
}
|
|
1046
|
-
}, 0);
|
|
1047
|
-
return () => clearTimeout(timer);
|
|
1081
|
+
setIsInitialized(true);
|
|
1082
|
+
// 초기화 직후 스크롤바 업데이트 (썸 높이 정확하게 계산)
|
|
1083
|
+
updateScrollbar();
|
|
1084
|
+
// 자동 숨김이 비활성화되어 있으면 스크롤바를 항상 표시
|
|
1085
|
+
if (!finalAutoHideConfig.enabled && isScrollable()) {
|
|
1086
|
+
setScrollbarVisible(true);
|
|
1087
|
+
}
|
|
1048
1088
|
}, [isScrollable, updateScrollbar, finalAutoHideConfig.enabled]);
|
|
1049
1089
|
// Resize observer로 크기 변경 감지
|
|
1050
1090
|
useEffect(() => {
|
|
@@ -1129,24 +1169,11 @@ showScrollbar = true, }, ref) => {
|
|
|
1129
1169
|
}
|
|
1130
1170
|
};
|
|
1131
1171
|
}, []);
|
|
1132
|
-
return (jsxs("div", { className: `overlay-scrollbar-wrapper ${className}`, style: Object.assign({ display: "flex", flexDirection: "column", position: "relative", minHeight: 0, height: "100%", flex: "1 1 0%" }, style), children: [jsx("div", { ref: containerRef, className: "overlay-scrollbar-container", tabIndex: -1, onMouseDown: handleDragScrollStart, style: {
|
|
1133
|
-
width: "100%", // 명시적 너비 설정
|
|
1134
|
-
height: "100%", // 부모의 전체 높이 사용
|
|
1135
|
-
flex: "1 1 auto", // flex item으로 설정하여 높이를 자동으로 계산
|
|
1136
|
-
minHeight: 0, // 최소 높이 보장
|
|
1137
|
-
overflow: "auto", // 네이티브 스크롤 기능 유지
|
|
1172
|
+
return (jsxs("div", { className: `overlay-scrollbar-wrapper ${className}`, style: Object.assign({ display: "flex", flexDirection: "column", position: "relative", minHeight: 0, height: "100%", flex: "1 1 0%" }, style), children: [jsx("div", { ref: containerRef, className: "overlay-scrollbar-container", tabIndex: -1, onMouseDown: handleDragScrollStart, style: Object.assign({ width: "100%", height: "100%", flex: "1 1 auto", minHeight: 0, overflow: "auto",
|
|
1138
1173
|
// 브라우저 기본 스크롤바만 숨기기
|
|
1139
|
-
scrollbarWidth: "none",
|
|
1140
|
-
msOverflowStyle: "none", // IE/Edge
|
|
1174
|
+
scrollbarWidth: "none", msOverflowStyle: "none",
|
|
1141
1175
|
// 키보드 포커스 스타일 (접근성)
|
|
1142
|
-
outline: "none",
|
|
1143
|
-
userSelect: isDragScrolling ? "none" : "auto", // 드래그 중 텍스트 선택 방지
|
|
1144
|
-
}, children: jsx("div", { ref: contentRef, className: "overlay-scrollbar-content", style: {
|
|
1145
|
-
height: "100%", // min-height 대신 height 사용
|
|
1146
|
-
minHeight: 0, // flex shrink 허용
|
|
1147
|
-
display: "flex", // flex 컨테이너로 설정
|
|
1148
|
-
flexDirection: "column", // 세로 방향 정렬
|
|
1149
|
-
}, children: children }) }), showScrollbar && (jsxs("div", { ref: scrollbarRef, className: "overlay-scrollbar-track", onMouseEnter: () => {
|
|
1176
|
+
outline: "none", userSelect: isDragScrolling ? "none" : "auto" }, containerStyle), children: jsx("div", { ref: contentRef, className: "overlay-scrollbar-content", style: Object.assign({ minHeight: 0, display: "flex", flexDirection: "column" }, contentStyle), children: children }) }), showScrollbar && hasScrollableContent && (jsxs("div", { ref: scrollbarRef, className: "overlay-scrollbar-track", onMouseEnter: () => {
|
|
1150
1177
|
clearHideTimer();
|
|
1151
1178
|
setScrollbarVisible(true);
|
|
1152
1179
|
}, onMouseLeave: () => {
|
|
@@ -1209,7 +1236,7 @@ showScrollbar = true, }, ref) => {
|
|
|
1209
1236
|
borderRadius: `${finalThumbConfig.radius}px`,
|
|
1210
1237
|
cursor: "pointer",
|
|
1211
1238
|
transition: "background-color 0.2s ease-in-out, opacity 0.2s ease-in-out",
|
|
1212
|
-
} })] })), showScrollbar && showArrows && (jsx("div", { className: "overlay-scrollbar-up-arrow", onClick: handleUpArrowClick, onMouseEnter: () => setHoveredArrow("up"), onMouseLeave: () => setHoveredArrow(null), style: {
|
|
1239
|
+
} })] })), showScrollbar && hasScrollableContent && showArrows && (jsx("div", { className: "overlay-scrollbar-up-arrow", onClick: handleUpArrowClick, onMouseEnter: () => setHoveredArrow("up"), onMouseLeave: () => setHoveredArrow(null), style: {
|
|
1213
1240
|
position: "absolute",
|
|
1214
1241
|
top: `${finalTrackConfig.margin}px`,
|
|
1215
1242
|
right: finalTrackConfig.alignment === "right"
|
|
@@ -1235,7 +1262,7 @@ showScrollbar = true, }, ref) => {
|
|
|
1235
1262
|
: finalArrowsConfig.opacity
|
|
1236
1263
|
: 0,
|
|
1237
1264
|
transition: "opacity 0.2s ease-in-out, color 0.15s ease-in-out",
|
|
1238
|
-
}, children: "\u25B2" })), showScrollbar && showArrows && (jsx("div", { className: "overlay-scrollbar-down-arrow", onClick: handleDownArrowClick, onMouseEnter: () => setHoveredArrow("down"), onMouseLeave: () => setHoveredArrow(null), style: {
|
|
1265
|
+
}, children: "\u25B2" })), showScrollbar && hasScrollableContent && showArrows && (jsx("div", { className: "overlay-scrollbar-down-arrow", onClick: handleDownArrowClick, onMouseEnter: () => setHoveredArrow("down"), onMouseLeave: () => setHoveredArrow(null), style: {
|
|
1239
1266
|
position: "absolute",
|
|
1240
1267
|
bottom: `${finalTrackConfig.margin}px`,
|
|
1241
1268
|
right: finalTrackConfig.alignment === "right"
|
|
@@ -1284,7 +1311,7 @@ function VirtualDataTableComponent({ data, loading = false, columns, onRowClick,
|
|
|
1284
1311
|
// 각 테이블 인스턴스별로 Scroller 컴포넌트 생성 (scrollbars, paddingX를 초기값으로 고정)
|
|
1285
1312
|
const VirtuosoScroller = useMemo(() => forwardRef((props, ref) => {
|
|
1286
1313
|
const scrollContainerRef = useRef(null);
|
|
1287
|
-
return (jsx(OverlayScrollbar, { track: OVERLAY_SCROLLBAR_TRACK_CONFIG, ...scrollbars, children: jsx(TableContainer, { component: Box, ...props, ref: (node) => {
|
|
1314
|
+
return (jsx(OverlayScrollbar, { track: OVERLAY_SCROLLBAR_TRACK_CONFIG, contentStyle: { height: "100%" }, ...scrollbars, children: jsx(TableContainer, { component: Box, ...props, ref: (node) => {
|
|
1288
1315
|
scrollContainerRef.current =
|
|
1289
1316
|
node;
|
|
1290
1317
|
if (typeof ref === "function") {
|