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