@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.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
- elementsToWatch.push(child);
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
- const timer = setTimeout(() => {
1041
- setIsInitialized(true);
1042
- // 초기화 후 스크롤바 업데이트 (썸 높이 정확하게 계산)
1043
- updateScrollbar();
1044
- // 자동 숨김이 비활성화되어 있으면 스크롤바를 항상 표시
1045
- if (!finalAutoHideConfig.enabled && isScrollable()) {
1046
- setScrollbarVisible(true);
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", // Firefox
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") {