@sia.soul/sia-react-ui 0.1.63 → 0.1.65

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.cjs CHANGED
@@ -2543,147 +2543,537 @@ function toggleTreeCheck(nodes, keys, node, strictly = false) {
2543
2543
  }
2544
2544
 
2545
2545
  // src/components/FloatingDisplay.tsx
2546
- var import_react14 = require("react");
2546
+ var import_react16 = require("react");
2547
2547
  var import_react_dom3 = require("react-dom");
2548
- var import_jsx_runtime14 = require("react/jsx-runtime");
2549
- var FLOATING_GAP = 9;
2550
- var FLOATING_VIEWPORT_PADDING = 8;
2551
- var FLOATING_ARROW_EDGE_OFFSET = 16;
2552
- var activeTooltipClose;
2553
- function clampFloating(value, min, max) {
2554
- return Math.min(Math.max(value, min), Math.max(min, max));
2548
+
2549
+ // src/components/FloatingScrollbarProvider.tsx
2550
+ var import_react15 = require("react");
2551
+
2552
+ // src/components/useScrollbarProximity.ts
2553
+ var import_react14 = require("react");
2554
+ var users = 0;
2555
+ var cleanup;
2556
+ function useScrollbarProximity() {
2557
+ (0, import_react14.useEffect)(() => {
2558
+ if (users++ === 0) {
2559
+ let frame = 0;
2560
+ let x = -Infinity;
2561
+ let y = -Infinity;
2562
+ const nearby = /* @__PURE__ */ new Set();
2563
+ const clear = () => {
2564
+ nearby.forEach((track) => track.removeAttribute("data-scrollbar-near"));
2565
+ nearby.clear();
2566
+ };
2567
+ const update = () => {
2568
+ frame = 0;
2569
+ const next = /* @__PURE__ */ new Set();
2570
+ document.querySelectorAll(".sia-scrollbar-track").forEach((track) => {
2571
+ const rect = track.getBoundingClientRect();
2572
+ const proximity = Number.parseFloat(getComputedStyle(track).getPropertyValue("--sia-scrollbar-proximity")) || 16;
2573
+ if (rect.width && rect.height && x >= rect.left - proximity && x <= rect.right + proximity && y >= rect.top - proximity && y <= rect.bottom + proximity) {
2574
+ next.add(track);
2575
+ if (!nearby.has(track)) track.setAttribute("data-scrollbar-near", "true");
2576
+ }
2577
+ });
2578
+ nearby.forEach((track) => {
2579
+ if (!next.has(track)) track.removeAttribute("data-scrollbar-near");
2580
+ });
2581
+ nearby.clear();
2582
+ next.forEach((track) => nearby.add(track));
2583
+ };
2584
+ const move = (event) => {
2585
+ if (event.pointerType === "touch") return;
2586
+ x = event.clientX;
2587
+ y = event.clientY;
2588
+ if (!frame) frame = requestAnimationFrame(update);
2589
+ };
2590
+ const leave = () => {
2591
+ x = y = -Infinity;
2592
+ clear();
2593
+ };
2594
+ document.addEventListener("pointermove", move, { passive: true });
2595
+ document.documentElement.addEventListener("pointerleave", leave);
2596
+ window.addEventListener("blur", leave);
2597
+ cleanup = () => {
2598
+ if (frame) cancelAnimationFrame(frame);
2599
+ document.removeEventListener("pointermove", move);
2600
+ document.documentElement.removeEventListener("pointerleave", leave);
2601
+ window.removeEventListener("blur", leave);
2602
+ clear();
2603
+ };
2604
+ }
2605
+ return () => {
2606
+ if (--users === 0) {
2607
+ cleanup?.();
2608
+ cleanup = void 0;
2609
+ }
2610
+ };
2611
+ }, []);
2555
2612
  }
2556
- function normalizeFloatingPlacement(placement) {
2557
- const aliases = {
2558
- topLeft: "top-start",
2559
- topRight: "top-end",
2560
- bottomLeft: "bottom-start",
2561
- bottomRight: "bottom-end",
2562
- leftTop: "left-start",
2563
- leftBottom: "left-end",
2564
- rightTop: "right-start",
2565
- rightBottom: "right-end"
2566
- };
2567
- return aliases[placement] ?? placement;
2613
+
2614
+ // src/components/FloatingScrollbarProvider.tsx
2615
+ var scrollableOverflow = /* @__PURE__ */ new Set(["auto", "scroll", "overlay"]);
2616
+ var clippingOverflow = /* @__PURE__ */ new Set(["auto", "scroll", "overlay", "hidden", "clip"]);
2617
+ function isRootScroller(target) {
2618
+ return target === document.scrollingElement || target === document.documentElement;
2568
2619
  }
2569
- function resolveFloatingPosition(trigger, overlay, placement, pointAtCenter, adjust) {
2570
- const viewportWidth = document.documentElement.clientWidth;
2571
- const viewportHeight = document.documentElement.clientHeight;
2572
- let resolvedPlacement = normalizeFloatingPlacement(placement);
2573
- const side = resolvedPlacement.split("-")[0];
2574
- const available = { top: trigger.top, bottom: viewportHeight - trigger.bottom, left: trigger.left, right: viewportWidth - trigger.right };
2575
- const opposite = { top: "bottom", bottom: "top", left: "right", right: "left" };
2576
- const preferred = side;
2577
- const other = opposite[preferred];
2578
- const needed = (side === "top" || side === "bottom" ? overlay.height : overlay.width) + FLOATING_GAP + FLOATING_VIEWPORT_PADDING;
2579
- if (adjust && available[preferred] < needed && available[other] > available[preferred]) {
2580
- resolvedPlacement = resolvedPlacement.replace(preferred, other);
2581
- }
2582
- const vertical = resolvedPlacement.startsWith("top") || resolvedPlacement.startsWith("bottom");
2583
- const start = resolvedPlacement.endsWith("-start");
2584
- const end = resolvedPlacement.endsWith("-end");
2585
- let top = vertical ? resolvedPlacement.startsWith("top") ? trigger.top - overlay.height - FLOATING_GAP : trigger.bottom + FLOATING_GAP : start ? trigger.top : end ? trigger.bottom - overlay.height : trigger.top + (trigger.height - overlay.height) / 2;
2586
- let left = vertical ? start ? trigger.left : end ? trigger.right - overlay.width : trigger.left + (trigger.width - overlay.width) / 2 : resolvedPlacement.startsWith("left") ? trigger.left - overlay.width - FLOATING_GAP : trigger.right + FLOATING_GAP;
2587
- if (adjust) {
2588
- if (vertical) left = clampFloating(left, FLOATING_VIEWPORT_PADDING, viewportWidth - overlay.width - FLOATING_VIEWPORT_PADDING);
2589
- else top = clampFloating(top, FLOATING_VIEWPORT_PADDING, viewportHeight - overlay.height - FLOATING_VIEWPORT_PADDING);
2620
+ function canScroll(target, axis) {
2621
+ if (target.matches(".sia-table__viewport, [data-sia-native-scrollbar]")) return false;
2622
+ if (isRootScroller(target)) {
2623
+ return axis === "horizontal" ? target.scrollWidth > target.clientWidth + 1 : target.scrollHeight > target.clientHeight + 1;
2590
2624
  }
2591
- const result = { top, left, placement: resolvedPlacement };
2592
- const size = vertical ? trigger.width : trigger.height;
2593
- const anchorStart = vertical ? trigger.left : trigger.top;
2594
- const edge = Math.min(size / 2, FLOATING_ARROW_EDGE_OFFSET + 4);
2595
- const target = anchorStart + (pointAtCenter || !start && !end ? size / 2 : start ? edge : size - edge);
2596
- if (vertical) result.arrowX = clampFloating(target - left - 4, 8, overlay.width - 16);
2597
- else result.arrowY = clampFloating(target - top - 4, 8, overlay.height - 16);
2598
- return result;
2625
+ const style = getComputedStyle(target);
2626
+ const overflow = axis === "horizontal" ? style.overflowX : style.overflowY;
2627
+ const hasOverflow = axis === "horizontal" ? target.scrollWidth > target.clientWidth + 1 : target.scrollHeight > target.clientHeight + 1;
2628
+ return hasOverflow && scrollableOverflow.has(overflow);
2599
2629
  }
2600
- function isFloatingAnchorHidden(anchor, rect) {
2601
- let left = 0, top = 0, right = document.documentElement.clientWidth, bottom = document.documentElement.clientHeight;
2602
- for (let parent = anchor.parentElement; parent; parent = parent.parentElement) {
2603
- const css = getComputedStyle(parent);
2604
- const bounds = parent.getBoundingClientRect();
2605
- if (/(auto|scroll|hidden|clip)/.test(css.overflowX)) {
2606
- left = Math.max(left, bounds.left);
2607
- right = Math.min(right, bounds.right);
2608
- }
2609
- if (/(auto|scroll|hidden|clip)/.test(css.overflowY)) {
2610
- top = Math.max(top, bounds.top);
2611
- bottom = Math.min(bottom, bounds.bottom);
2612
- }
2630
+ function visibleRect(target) {
2631
+ if (isRootScroller(target)) {
2632
+ return { top: 0, right: window.innerWidth, bottom: window.innerHeight, left: 0, width: window.innerWidth, height: window.innerHeight };
2613
2633
  }
2614
- return rect.right <= left || rect.left >= right || rect.bottom <= top || rect.top >= bottom;
2615
- }
2616
- function Floating({
2617
- children,
2618
- content,
2619
- placement = "top",
2620
- trigger = "hover",
2621
- open,
2622
- defaultOpen = false,
2623
- mouseEnterDelay = 0.1,
2624
- mouseLeaveDelay = 0.1,
2625
- arrow = true,
2626
- autoAdjustOverflow = true,
2627
- color,
2628
- className = "",
2629
- overlayClassName = "",
2630
- role = "tooltip",
2631
- onOpenChange
2632
- }) {
2633
- const rootRef = (0, import_react14.useRef)(null);
2634
- const triggerElementRef = (0, import_react14.useRef)(null);
2635
- const overlayRef = (0, import_react14.useRef)(null);
2636
- const enterTimer = (0, import_react14.useRef)();
2637
- const leaveTimer = (0, import_react14.useRef)();
2638
- const contentId = (0, import_react14.useId)();
2639
- const triggers = Array.isArray(trigger) ? trigger : [trigger];
2640
- const [visible, setVisible] = useControllableState({ value: open, defaultValue: defaultOpen, onChange: onOpenChange });
2641
- const [position, setPosition] = (0, import_react14.useState)();
2642
- const [portalContainer, setPortalContainer] = (0, import_react14.useState)(null);
2643
- (0, import_react14.useLayoutEffect)(() => {
2644
- if (!visible) return;
2645
- setPortalContainer(rootRef.current?.closest(".sia-modal-root, .sia-drawer-root") ?? document.body);
2646
- });
2647
- const setVisibleRef = (0, import_react14.useRef)(setVisible);
2648
- setVisibleRef.current = setVisible;
2649
- const closeSelfRef = (0, import_react14.useRef)();
2650
- if (!closeSelfRef.current) closeSelfRef.current = () => setVisibleRef.current(false);
2651
- const exclusiveTooltip = role === "tooltip" && triggers.includes("hover");
2652
- function openNow() {
2653
- if (exclusiveTooltip && activeTooltipClose !== closeSelfRef.current) {
2654
- activeTooltipClose?.();
2655
- activeTooltipClose = closeSelfRef.current;
2634
+ const ownRect = target.getBoundingClientRect();
2635
+ let top = Math.max(0, ownRect.top);
2636
+ let right = Math.min(window.innerWidth, ownRect.right);
2637
+ let bottom = Math.min(window.innerHeight, ownRect.bottom);
2638
+ let left = Math.max(0, ownRect.left);
2639
+ let parent = target.parentElement;
2640
+ while (parent && parent !== document.body && parent !== document.documentElement) {
2641
+ const style = getComputedStyle(parent);
2642
+ const clipX = clippingOverflow.has(style.overflowX);
2643
+ const clipY = clippingOverflow.has(style.overflowY);
2644
+ if (clipX || clipY) {
2645
+ const rect = parent.getBoundingClientRect();
2646
+ if (clipX) {
2647
+ left = Math.max(left, rect.left);
2648
+ right = Math.min(right, rect.right);
2649
+ }
2650
+ if (clipY) {
2651
+ top = Math.max(top, rect.top);
2652
+ bottom = Math.min(bottom, rect.bottom);
2653
+ }
2656
2654
  }
2657
- setVisible(true);
2658
- }
2659
- function closeNow() {
2660
- if (activeTooltipClose === closeSelfRef.current) activeTooltipClose = void 0;
2661
- setVisible(false);
2662
- }
2663
- function clearTimers() {
2664
- window.clearTimeout(enterTimer.current);
2665
- window.clearTimeout(leaveTimer.current);
2666
- }
2667
- function show(delay = 0) {
2668
- clearTimers();
2669
- if (delay <= 0) openNow();
2670
- else enterTimer.current = window.setTimeout(openNow, delay * 1e3);
2671
- }
2672
- function hide(delay = 0) {
2673
- clearTimers();
2674
- if (delay <= 0) closeNow();
2675
- else leaveTimer.current = window.setTimeout(closeNow, delay * 1e3);
2655
+ parent = parent.parentElement;
2676
2656
  }
2677
- (0, import_react14.useEffect)(() => {
2678
- if (!visible || !triggers.includes("click")) return;
2679
- const close = (event) => {
2680
- const target = event.target;
2681
- if (!rootRef.current?.contains(target) && !overlayRef.current?.contains(target)) closeNow();
2657
+ return {
2658
+ top,
2659
+ right,
2660
+ bottom,
2661
+ left,
2662
+ width: Math.max(0, right - left),
2663
+ height: Math.max(0, bottom - top)
2664
+ };
2665
+ }
2666
+ function createTrack(axis) {
2667
+ const track = document.createElement("div");
2668
+ const thumb = document.createElement("div");
2669
+ track.className = `sia-scrollbar-track sia-floating-scrollbar sia-floating-scrollbar--${axis}`;
2670
+ thumb.className = "sia-floating-scrollbar__thumb";
2671
+ track.dataset.axis = axis;
2672
+ track.setAttribute("aria-hidden", "true");
2673
+ track.append(thumb);
2674
+ return { track, thumb };
2675
+ }
2676
+ function FloatingScrollbarProvider({ disabled = false, targetRef }) {
2677
+ useScrollbarProximity();
2678
+ (0, import_react15.useEffect)(() => {
2679
+ if (disabled) return;
2680
+ const layer = document.createElement("div");
2681
+ layer.className = "sia-floating-scrollbar-layer";
2682
+ layer.setAttribute("aria-hidden", "true");
2683
+ document.body.append(layer);
2684
+ const entries = /* @__PURE__ */ new Map();
2685
+ let scanFrame = 0;
2686
+ let updateFrame = 0;
2687
+ const setEntryVisible = (entry, visible) => {
2688
+ entry.horizontalTrack.classList.toggle("sia-floating-scrollbar--visible", visible);
2689
+ entry.verticalTrack.classList.toggle("sia-floating-scrollbar--visible", visible);
2682
2690
  };
2683
- document.addEventListener("pointerdown", close);
2684
- return () => document.removeEventListener("pointerdown", close);
2685
- }, [triggers.join("|"), visible]);
2686
- (0, import_react14.useLayoutEffect)(() => {
2691
+ const showEntry = (entry) => {
2692
+ if (entry.hideTimer !== null) window.clearTimeout(entry.hideTimer);
2693
+ entry.hideTimer = null;
2694
+ setEntryVisible(entry, true);
2695
+ };
2696
+ const scheduleHide = (entry, delay = 500) => {
2697
+ if (entry.hideTimer !== null) window.clearTimeout(entry.hideTimer);
2698
+ entry.hideTimer = window.setTimeout(() => {
2699
+ entry.hideTimer = null;
2700
+ if (!entry.hoveringTarget && !entry.hoveringTrack && !entry.dragging) setEntryVisible(entry, false);
2701
+ }, delay);
2702
+ };
2703
+ const updateEntry = (entry) => {
2704
+ const { target, horizontalTrack, horizontalThumb, verticalTrack, verticalThumb } = entry;
2705
+ const hasHorizontal = canScroll(target, "horizontal");
2706
+ const hasVertical = canScroll(target, "vertical");
2707
+ const rect = visibleRect(target);
2708
+ const thickness = 10;
2709
+ horizontalTrack.hidden = !hasHorizontal || rect.width <= thickness || rect.height <= 0;
2710
+ verticalTrack.hidden = !hasVertical || rect.height <= thickness || rect.width <= 0;
2711
+ if (!horizontalTrack.hidden) {
2712
+ const trackWidth = Math.max(0, rect.width - (hasVertical ? thickness : 0));
2713
+ const thumbWidth = Math.max(24, Math.min(trackWidth, trackWidth * target.clientWidth / target.scrollWidth));
2714
+ const travel = Math.max(0, trackWidth - thumbWidth);
2715
+ const maxScroll = Math.max(1, target.scrollWidth - target.clientWidth);
2716
+ horizontalTrack.style.left = `${rect.left}px`;
2717
+ horizontalTrack.style.top = `${rect.bottom - thickness}px`;
2718
+ horizontalTrack.style.width = `${trackWidth}px`;
2719
+ horizontalTrack.style.height = `${thickness}px`;
2720
+ horizontalThumb.style.width = `${thumbWidth}px`;
2721
+ horizontalThumb.style.transform = `translate3d(${travel * target.scrollLeft / maxScroll}px, 0, 0)`;
2722
+ }
2723
+ if (!verticalTrack.hidden) {
2724
+ const trackHeight = Math.max(0, rect.height - (hasHorizontal ? thickness : 0));
2725
+ const thumbHeight = Math.max(24, Math.min(trackHeight, trackHeight * target.clientHeight / target.scrollHeight));
2726
+ const travel = Math.max(0, trackHeight - thumbHeight);
2727
+ const maxScroll = Math.max(1, target.scrollHeight - target.clientHeight);
2728
+ verticalTrack.style.left = `${rect.right - thickness}px`;
2729
+ verticalTrack.style.top = `${rect.top}px`;
2730
+ verticalTrack.style.width = `${thickness}px`;
2731
+ verticalTrack.style.height = `${trackHeight}px`;
2732
+ verticalThumb.style.height = `${thumbHeight}px`;
2733
+ verticalThumb.style.transform = `translate3d(0, ${travel * target.scrollTop / maxScroll}px, 0)`;
2734
+ }
2735
+ };
2736
+ const scheduleUpdateAll = () => {
2737
+ if (updateFrame) return;
2738
+ updateFrame = window.requestAnimationFrame(() => {
2739
+ updateFrame = 0;
2740
+ entries.forEach(updateEntry);
2741
+ });
2742
+ };
2743
+ const bindTrackDrag = (entry, axis, track, thumb) => {
2744
+ const target = entry.target;
2745
+ const onTrackPointerEnter = () => {
2746
+ entry.hoveringTrack = true;
2747
+ showEntry(entry);
2748
+ };
2749
+ const onTrackPointerLeave = () => {
2750
+ entry.hoveringTrack = false;
2751
+ scheduleHide(entry);
2752
+ };
2753
+ const onTrackPointerDown = (event) => {
2754
+ if (event.target === thumb || event.button !== 0) return;
2755
+ event.preventDefault();
2756
+ event.stopPropagation();
2757
+ const trackRect = track.getBoundingClientRect();
2758
+ const thumbRect = thumb.getBoundingClientRect();
2759
+ const trackLength = axis === "vertical" ? trackRect.height : trackRect.width;
2760
+ const thumbLength = axis === "vertical" ? thumbRect.height : thumbRect.width;
2761
+ const pointer = axis === "vertical" ? event.clientY - trackRect.top : event.clientX - trackRect.left;
2762
+ const ratio = Math.max(0, Math.min(1, (pointer - thumbLength / 2) / Math.max(1, trackLength - thumbLength)));
2763
+ if (axis === "vertical") target.scrollTop = ratio * (target.scrollHeight - target.clientHeight);
2764
+ else target.scrollLeft = ratio * (target.scrollWidth - target.clientWidth);
2765
+ };
2766
+ const onThumbPointerDown = (event) => {
2767
+ if (event.button !== 0) return;
2768
+ event.preventDefault();
2769
+ event.stopPropagation();
2770
+ entry.dragging = true;
2771
+ showEntry(entry);
2772
+ thumb.setPointerCapture(event.pointerId);
2773
+ const startPointer = axis === "vertical" ? event.clientY : event.clientX;
2774
+ const startScroll = axis === "vertical" ? target.scrollTop : target.scrollLeft;
2775
+ const trackRect = track.getBoundingClientRect();
2776
+ const thumbRect = thumb.getBoundingClientRect();
2777
+ const trackLength = axis === "vertical" ? trackRect.height : trackRect.width;
2778
+ const thumbLength = axis === "vertical" ? thumbRect.height : thumbRect.width;
2779
+ const maxScroll = axis === "vertical" ? target.scrollHeight - target.clientHeight : target.scrollWidth - target.clientWidth;
2780
+ const travel = Math.max(1, trackLength - thumbLength);
2781
+ const onPointerMove = (moveEvent) => {
2782
+ if (moveEvent.pointerId !== event.pointerId) return;
2783
+ const pointer = axis === "vertical" ? moveEvent.clientY : moveEvent.clientX;
2784
+ const nextScroll = startScroll + (pointer - startPointer) * maxScroll / travel;
2785
+ if (axis === "vertical") target.scrollTop = nextScroll;
2786
+ else target.scrollLeft = nextScroll;
2787
+ };
2788
+ const onPointerUp = (upEvent) => {
2789
+ if (upEvent.pointerId !== event.pointerId) return;
2790
+ entry.dragging = false;
2791
+ thumb.releasePointerCapture(event.pointerId);
2792
+ window.removeEventListener("pointermove", onPointerMove);
2793
+ window.removeEventListener("pointerup", onPointerUp);
2794
+ window.removeEventListener("pointercancel", onPointerUp);
2795
+ scheduleHide(entry);
2796
+ };
2797
+ window.addEventListener("pointermove", onPointerMove);
2798
+ window.addEventListener("pointerup", onPointerUp);
2799
+ window.addEventListener("pointercancel", onPointerUp);
2800
+ };
2801
+ track.addEventListener("pointerenter", onTrackPointerEnter);
2802
+ track.addEventListener("pointerleave", onTrackPointerLeave);
2803
+ track.addEventListener("pointerdown", onTrackPointerDown);
2804
+ thumb.addEventListener("pointerdown", onThumbPointerDown);
2805
+ return () => {
2806
+ track.removeEventListener("pointerenter", onTrackPointerEnter);
2807
+ track.removeEventListener("pointerleave", onTrackPointerLeave);
2808
+ track.removeEventListener("pointerdown", onTrackPointerDown);
2809
+ thumb.removeEventListener("pointerdown", onThumbPointerDown);
2810
+ };
2811
+ };
2812
+ const resizeObserver = new ResizeObserver((observedEntries) => {
2813
+ observedEntries.forEach(({ target }) => {
2814
+ const entry = entries.get(target);
2815
+ if (entry) updateEntry(entry);
2816
+ });
2817
+ });
2818
+ const addTarget = (target) => {
2819
+ if (entries.has(target)) return;
2820
+ const horizontal = createTrack("horizontal");
2821
+ const vertical = createTrack("vertical");
2822
+ layer.append(horizontal.track, vertical.track);
2823
+ const onPointerEnter = () => {
2824
+ const entry2 = entries.get(target);
2825
+ if (!entry2) return;
2826
+ entry2.hoveringTarget = true;
2827
+ showEntry(entry2);
2828
+ };
2829
+ const onPointerLeave = () => {
2830
+ const entry2 = entries.get(target);
2831
+ if (!entry2) return;
2832
+ entry2.hoveringTarget = false;
2833
+ scheduleHide(entry2);
2834
+ };
2835
+ const onScroll = () => {
2836
+ const entry2 = entries.get(target);
2837
+ if (!entry2) return;
2838
+ updateEntry(entry2);
2839
+ showEntry(entry2);
2840
+ scheduleHide(entry2, 700);
2841
+ };
2842
+ target.addEventListener("pointerenter", onPointerEnter);
2843
+ target.addEventListener("pointerleave", onPointerLeave);
2844
+ target.addEventListener("scroll", onScroll, { passive: true });
2845
+ const entry = {
2846
+ target,
2847
+ horizontalTrack: horizontal.track,
2848
+ horizontalThumb: horizontal.thumb,
2849
+ verticalTrack: vertical.track,
2850
+ verticalThumb: vertical.thumb,
2851
+ hideTimer: null,
2852
+ hoveringTarget: target.matches(":hover"),
2853
+ hoveringTrack: false,
2854
+ dragging: false,
2855
+ cleanup: () => void 0
2856
+ };
2857
+ const cleanupHorizontal = bindTrackDrag(entry, "horizontal", horizontal.track, horizontal.thumb);
2858
+ const cleanupVertical = bindTrackDrag(entry, "vertical", vertical.track, vertical.thumb);
2859
+ entry.cleanup = () => {
2860
+ if (entry.hideTimer !== null) window.clearTimeout(entry.hideTimer);
2861
+ cleanupHorizontal();
2862
+ cleanupVertical();
2863
+ target.removeEventListener("pointerenter", onPointerEnter);
2864
+ target.removeEventListener("pointerleave", onPointerLeave);
2865
+ target.removeEventListener("scroll", onScroll);
2866
+ resizeObserver.unobserve(target);
2867
+ target.removeAttribute("data-sia-floating-scrollbar");
2868
+ horizontal.track.remove();
2869
+ vertical.track.remove();
2870
+ };
2871
+ entries.set(target, entry);
2872
+ target.setAttribute("data-sia-floating-scrollbar", "");
2873
+ resizeObserver.observe(target);
2874
+ updateEntry(entry);
2875
+ if (entry.hoveringTarget) showEntry(entry);
2876
+ };
2877
+ const removeTarget = (target) => {
2878
+ const entry = entries.get(target);
2879
+ if (!entry) return;
2880
+ entry.cleanup();
2881
+ entries.delete(target);
2882
+ };
2883
+ const scanTargets = () => {
2884
+ scanFrame = 0;
2885
+ const managedSelector = ".sia-table__viewport, [data-sia-native-scrollbar], .sia-scrollbar-track, .sia-floating-scrollbar-layer";
2886
+ const discovered = [];
2887
+ if (!targetRef) {
2888
+ const walker = document.createTreeWalker(document.body, NodeFilter.SHOW_ELEMENT, {
2889
+ acceptNode: (node) => node.matches(managedSelector) ? NodeFilter.FILTER_REJECT : NodeFilter.FILTER_ACCEPT
2890
+ });
2891
+ for (let node = walker.nextNode(); node; node = walker.nextNode()) {
2892
+ if (node instanceof HTMLElement) discovered.push(node);
2893
+ }
2894
+ }
2895
+ const candidates = (targetRef ? [targetRef.current] : [document.scrollingElement, ...discovered]).filter((target) => target instanceof HTMLElement);
2896
+ const nextTargets = /* @__PURE__ */ new Set();
2897
+ candidates.forEach((target) => {
2898
+ if (canScroll(target, "horizontal") || canScroll(target, "vertical")) {
2899
+ nextTargets.add(target);
2900
+ if (entries.has(target) || !target.hasAttribute("data-sia-floating-scrollbar")) addTarget(target);
2901
+ }
2902
+ });
2903
+ entries.forEach((_entry, target) => {
2904
+ if (!target.isConnected || !nextTargets.has(target)) removeTarget(target);
2905
+ else updateEntry(_entry);
2906
+ });
2907
+ };
2908
+ const scheduleScan = () => {
2909
+ if (scanFrame) return;
2910
+ scanFrame = window.requestAnimationFrame(scanTargets);
2911
+ };
2912
+ const mutationObserver = new MutationObserver((mutations) => {
2913
+ if (mutations.some((mutation) => {
2914
+ const target = mutation.target instanceof Element ? mutation.target : mutation.target.parentElement;
2915
+ return target && !layer.contains(target) && !target.closest(".sia-table__viewport, [data-sia-native-scrollbar], .sia-scrollbar-track");
2916
+ })) scheduleScan();
2917
+ });
2918
+ mutationObserver.observe(document.documentElement, { subtree: true, childList: true, attributes: true, attributeFilter: ["class", "style", "hidden"] });
2919
+ window.addEventListener("resize", scheduleScan);
2920
+ window.addEventListener("scroll", scheduleUpdateAll, true);
2921
+ scheduleScan();
2922
+ return () => {
2923
+ mutationObserver.disconnect();
2924
+ resizeObserver.disconnect();
2925
+ window.removeEventListener("resize", scheduleScan);
2926
+ window.removeEventListener("scroll", scheduleUpdateAll, true);
2927
+ if (scanFrame) window.cancelAnimationFrame(scanFrame);
2928
+ if (updateFrame) window.cancelAnimationFrame(updateFrame);
2929
+ entries.forEach((entry) => entry.cleanup());
2930
+ entries.clear();
2931
+ layer.remove();
2932
+ };
2933
+ }, [disabled, targetRef]);
2934
+ return null;
2935
+ }
2936
+
2937
+ // src/components/FloatingDisplay.tsx
2938
+ var import_jsx_runtime14 = require("react/jsx-runtime");
2939
+ var FLOATING_GAP = 9;
2940
+ var FLOATING_VIEWPORT_PADDING = 8;
2941
+ var FLOATING_ARROW_EDGE_OFFSET = 16;
2942
+ var activeTooltipClose;
2943
+ function clampFloating(value, min, max) {
2944
+ return Math.min(Math.max(value, min), Math.max(min, max));
2945
+ }
2946
+ function normalizeFloatingPlacement(placement) {
2947
+ const aliases = {
2948
+ topLeft: "top-start",
2949
+ topRight: "top-end",
2950
+ bottomLeft: "bottom-start",
2951
+ bottomRight: "bottom-end",
2952
+ leftTop: "left-start",
2953
+ leftBottom: "left-end",
2954
+ rightTop: "right-start",
2955
+ rightBottom: "right-end"
2956
+ };
2957
+ return aliases[placement] ?? placement;
2958
+ }
2959
+ function resolveFloatingPosition(trigger, overlay, placement, pointAtCenter, adjust) {
2960
+ const viewportWidth = document.documentElement.clientWidth;
2961
+ const viewportHeight = document.documentElement.clientHeight;
2962
+ let resolvedPlacement = normalizeFloatingPlacement(placement);
2963
+ const side = resolvedPlacement.split("-")[0];
2964
+ const available = { top: trigger.top, bottom: viewportHeight - trigger.bottom, left: trigger.left, right: viewportWidth - trigger.right };
2965
+ const opposite = { top: "bottom", bottom: "top", left: "right", right: "left" };
2966
+ const preferred = side;
2967
+ const other = opposite[preferred];
2968
+ const needed = (side === "top" || side === "bottom" ? overlay.height : overlay.width) + FLOATING_GAP + FLOATING_VIEWPORT_PADDING;
2969
+ if (adjust && available[preferred] < needed && available[other] > available[preferred]) {
2970
+ resolvedPlacement = resolvedPlacement.replace(preferred, other);
2971
+ }
2972
+ const vertical = resolvedPlacement.startsWith("top") || resolvedPlacement.startsWith("bottom");
2973
+ const start = resolvedPlacement.endsWith("-start");
2974
+ const end = resolvedPlacement.endsWith("-end");
2975
+ let top = vertical ? resolvedPlacement.startsWith("top") ? trigger.top - overlay.height - FLOATING_GAP : trigger.bottom + FLOATING_GAP : start ? trigger.top : end ? trigger.bottom - overlay.height : trigger.top + (trigger.height - overlay.height) / 2;
2976
+ let left = vertical ? start ? trigger.left : end ? trigger.right - overlay.width : trigger.left + (trigger.width - overlay.width) / 2 : resolvedPlacement.startsWith("left") ? trigger.left - overlay.width - FLOATING_GAP : trigger.right + FLOATING_GAP;
2977
+ if (adjust) {
2978
+ if (vertical) left = clampFloating(left, FLOATING_VIEWPORT_PADDING, viewportWidth - overlay.width - FLOATING_VIEWPORT_PADDING);
2979
+ else top = clampFloating(top, FLOATING_VIEWPORT_PADDING, viewportHeight - overlay.height - FLOATING_VIEWPORT_PADDING);
2980
+ }
2981
+ const result = { top, left, placement: resolvedPlacement };
2982
+ const size = vertical ? trigger.width : trigger.height;
2983
+ const anchorStart = vertical ? trigger.left : trigger.top;
2984
+ const edge = Math.min(size / 2, FLOATING_ARROW_EDGE_OFFSET + 4);
2985
+ const target = anchorStart + (pointAtCenter || !start && !end ? size / 2 : start ? edge : size - edge);
2986
+ if (vertical) result.arrowX = clampFloating(target - left - 4, 8, overlay.width - 16);
2987
+ else result.arrowY = clampFloating(target - top - 4, 8, overlay.height - 16);
2988
+ return result;
2989
+ }
2990
+ function isFloatingAnchorHidden(anchor, rect) {
2991
+ let left = 0, top = 0, right = document.documentElement.clientWidth, bottom = document.documentElement.clientHeight;
2992
+ for (let parent = anchor.parentElement; parent; parent = parent.parentElement) {
2993
+ const css = getComputedStyle(parent);
2994
+ const bounds = parent.getBoundingClientRect();
2995
+ if (/(auto|scroll|hidden|clip)/.test(css.overflowX)) {
2996
+ left = Math.max(left, bounds.left);
2997
+ right = Math.min(right, bounds.right);
2998
+ }
2999
+ if (/(auto|scroll|hidden|clip)/.test(css.overflowY)) {
3000
+ top = Math.max(top, bounds.top);
3001
+ bottom = Math.min(bottom, bounds.bottom);
3002
+ }
3003
+ }
3004
+ return rect.right <= left || rect.left >= right || rect.bottom <= top || rect.top >= bottom;
3005
+ }
3006
+ function Floating({
3007
+ children,
3008
+ content,
3009
+ placement = "top",
3010
+ trigger = "hover",
3011
+ open,
3012
+ defaultOpen = false,
3013
+ mouseEnterDelay = 0.1,
3014
+ mouseLeaveDelay = 0.1,
3015
+ arrow = true,
3016
+ autoAdjustOverflow = true,
3017
+ color,
3018
+ className = "",
3019
+ overlayClassName = "",
3020
+ role = "tooltip",
3021
+ onOpenChange
3022
+ }) {
3023
+ const rootRef = (0, import_react16.useRef)(null);
3024
+ const triggerElementRef = (0, import_react16.useRef)(null);
3025
+ const overlayRef = (0, import_react16.useRef)(null);
3026
+ const enterTimer = (0, import_react16.useRef)();
3027
+ const leaveTimer = (0, import_react16.useRef)();
3028
+ const contentId = (0, import_react16.useId)();
3029
+ const triggers = Array.isArray(trigger) ? trigger : [trigger];
3030
+ const [visible, setVisible] = useControllableState({ value: open, defaultValue: defaultOpen, onChange: onOpenChange });
3031
+ const [position, setPosition] = (0, import_react16.useState)();
3032
+ const [portalContainer, setPortalContainer] = (0, import_react16.useState)(null);
3033
+ (0, import_react16.useLayoutEffect)(() => {
3034
+ if (!visible) return;
3035
+ setPortalContainer(rootRef.current?.closest(".sia-modal-root, .sia-drawer-root") ?? document.body);
3036
+ });
3037
+ const setVisibleRef = (0, import_react16.useRef)(setVisible);
3038
+ setVisibleRef.current = setVisible;
3039
+ const closeSelfRef = (0, import_react16.useRef)();
3040
+ if (!closeSelfRef.current) closeSelfRef.current = () => setVisibleRef.current(false);
3041
+ const exclusiveTooltip = role === "tooltip" && triggers.includes("hover");
3042
+ function openNow() {
3043
+ if (exclusiveTooltip && activeTooltipClose !== closeSelfRef.current) {
3044
+ activeTooltipClose?.();
3045
+ activeTooltipClose = closeSelfRef.current;
3046
+ }
3047
+ setVisible(true);
3048
+ }
3049
+ function closeNow() {
3050
+ if (activeTooltipClose === closeSelfRef.current) activeTooltipClose = void 0;
3051
+ setVisible(false);
3052
+ }
3053
+ function clearTimers() {
3054
+ window.clearTimeout(enterTimer.current);
3055
+ window.clearTimeout(leaveTimer.current);
3056
+ }
3057
+ function show(delay = 0) {
3058
+ clearTimers();
3059
+ if (delay <= 0) openNow();
3060
+ else enterTimer.current = window.setTimeout(openNow, delay * 1e3);
3061
+ }
3062
+ function hide(delay = 0) {
3063
+ clearTimers();
3064
+ if (delay <= 0) closeNow();
3065
+ else leaveTimer.current = window.setTimeout(closeNow, delay * 1e3);
3066
+ }
3067
+ (0, import_react16.useEffect)(() => {
3068
+ if (!visible || !triggers.includes("click")) return;
3069
+ const close = (event) => {
3070
+ const target = event.target;
3071
+ if (!rootRef.current?.contains(target) && !overlayRef.current?.contains(target)) closeNow();
3072
+ };
3073
+ document.addEventListener("pointerdown", close);
3074
+ return () => document.removeEventListener("pointerdown", close);
3075
+ }, [triggers.join("|"), visible]);
3076
+ (0, import_react16.useLayoutEffect)(() => {
2687
3077
  if (!visible) {
2688
3078
  setPosition(void 0);
2689
3079
  return;
@@ -2721,11 +3111,11 @@ function Floating({
2721
3111
  observer.disconnect();
2722
3112
  };
2723
3113
  }, [children, content, placement, visible, portalContainer, autoAdjustOverflow, typeof arrow === "object" && arrow.pointAtCenter]);
2724
- (0, import_react14.useEffect)(() => () => {
3114
+ (0, import_react16.useEffect)(() => () => {
2725
3115
  clearTimers();
2726
3116
  if (activeTooltipClose === closeSelfRef.current) activeTooltipClose = void 0;
2727
3117
  }, []);
2728
- const child = (0, import_react14.cloneElement)(children, {
3118
+ const child = (0, import_react16.cloneElement)(children, {
2729
3119
  ...triggers.includes("hover") ? { onMouseEnter: (event) => {
2730
3120
  triggerElementRef.current = event.currentTarget;
2731
3121
  children.props.onMouseEnter?.(event);
@@ -2783,652 +3173,366 @@ function Floating({
2783
3173
  ] });
2784
3174
  }
2785
3175
  function Tooltip(props) {
2786
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Floating, { ...props, content: props.title });
2787
- }
2788
- function Popover({ title, content, trigger = "click", overlayClassName = "", ...props }) {
2789
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Floating, { ...props, trigger, role: "dialog", overlayClassName: `sia-popover ${overlayClassName}`.trim(), content: /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_jsx_runtime14.Fragment, { children: [
2790
- title ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("strong", { className: "sia-popover__title", children: title }) : null,
2791
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "sia-popover__content", children: content })
2792
- ] }) });
2793
- }
2794
- function Timeline({ items = [], mode = "left", pending = false, pendingDot, reverse = false, className = "", ...props }) {
2795
- const values = [...items, ...pending ? [{ children: pending === true ? "\u52A0\u8F7D\u4E2D..." : pending, dot: pendingDot ?? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Icon, { name: "loader", className: "sia-spin-icon" }) }] : []];
2796
- if (reverse) values.reverse();
2797
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("ul", { className: `sia-timeline sia-timeline--${mode} ${className}`.trim(), ...withTitleTooltip(props), children: values.map((item, index) => {
2798
- const position = item.position ?? (mode === "alternate" ? index % 2 ? "right" : "left" : mode);
2799
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("li", { className: `sia-timeline__item sia-timeline__item--${position}`, children: [
2800
- item.label ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "sia-timeline__label", children: item.label }) : null,
2801
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "sia-timeline__rail", children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "sia-timeline__dot", style: { color: item.color && !["blue", "red", "green", "gray"].includes(item.color) ? item.color : void 0 }, "data-color": item.color ?? "blue", children: item.dot }) }),
2802
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "sia-timeline__content", children: item.children })
2803
- ] }, index);
2804
- }) });
2805
- }
2806
- function collectKeys(nodes, branchOnly = false, result = []) {
2807
- nodes.forEach((node) => {
2808
- if (!branchOnly || node.children?.length) result.push(node.key);
2809
- if (node.children) collectKeys(node.children, branchOnly, result);
2810
- });
2811
- return result;
2812
- }
2813
- function Tree({
2814
- treeData,
2815
- selectedKeys,
2816
- defaultSelectedKeys = [],
2817
- expandedKeys,
2818
- defaultExpandedKeys = [],
2819
- defaultExpandAll = false,
2820
- checkedKeys,
2821
- defaultCheckedKeys = [],
2822
- loadingKeys = [],
2823
- checkable = false,
2824
- checkStrictly = false,
2825
- multiple = false,
2826
- selectable = true,
2827
- showLine = false,
2828
- showIcon = false,
2829
- blockNode = false,
2830
- indentSize = 20,
2831
- switcherWidth = 24,
2832
- stickyAncestors = false,
2833
- stickyRowHeight = 34,
2834
- draggable = false,
2835
- loadData,
2836
- onSelect,
2837
- onCheck,
2838
- onExpand,
2839
- onDrop,
2840
- className = "",
2841
- ...props
2842
- }) {
2843
- const [selected, setSelected] = useControllableState({ value: selectedKeys, defaultValue: defaultSelectedKeys });
2844
- const [expanded, setExpanded] = useControllableState({ value: expandedKeys, defaultValue: defaultExpandAll ? collectKeys(treeData, true) : defaultExpandedKeys });
2845
- const [checked, setChecked] = useControllableState({ value: checkedKeys, defaultValue: defaultCheckedKeys });
2846
- const [loading, setLoading] = (0, import_react14.useState)(/* @__PURE__ */ new Set());
2847
- const controlledLoading = (0, import_react14.useMemo)(() => new Set(loadingKeys), [loadingKeys]);
2848
- const selectedKeySet = (0, import_react14.useMemo)(() => new Set(selected), [selected]);
2849
- const selectedAncestorKeys = (0, import_react14.useMemo)(() => {
2850
- const ancestors = /* @__PURE__ */ new Set();
2851
- const visit = (nodes, parentKeys) => {
2852
- nodes.forEach((node) => {
2853
- if (selectedKeySet.has(node.key)) parentKeys.forEach((key) => ancestors.add(key));
2854
- if (node.children?.length) visit(node.children, [...parentKeys, node.key]);
2855
- });
2856
- };
2857
- visit(treeData, []);
2858
- return ancestors;
2859
- }, [selectedKeySet, treeData]);
2860
- const dragNodeRef = (0, import_react14.useRef)();
2861
- const pointerDragRef = (0, import_react14.useRef)();
2862
- const suppressClickRef = (0, import_react14.useRef)(false);
2863
- const [dragOver, setDragOver] = (0, import_react14.useState)();
2864
- const normalizedSwitcherWidth = Math.max(0, switcherWidth);
2865
- async function toggleExpand(node) {
2866
- const nextExpanded = !expanded.includes(node.key);
2867
- if (nextExpanded && node.loaded !== true && !node.children?.length && node.isLeaf === false && loadData) {
2868
- setLoading((keys) => new Set(keys).add(node.key));
2869
- try {
2870
- await loadData(node);
2871
- } finally {
2872
- setLoading((keys) => {
2873
- const next2 = new Set(keys);
2874
- next2.delete(node.key);
2875
- return next2;
2876
- });
2877
- }
2878
- }
2879
- const next = nextExpanded ? [...expanded, node.key] : expanded.filter((key) => key !== node.key);
2880
- setExpanded(next);
2881
- onExpand?.([...next], { expanded: nextExpanded, node });
2882
- }
2883
- const checkState = (0, import_react14.useMemo)(() => getTreeCheckState(treeData, checked, checkStrictly), [treeData, checked, checkStrictly]);
2884
- function check(node) {
2885
- const next = toggleTreeCheck(treeData, checked, node, checkStrictly);
2886
- setChecked([...next.checked]);
2887
- onCheck?.([...next.checked], { checked: next.checkedValue, halfCheckedKeys: [...next.halfChecked], node });
2888
- }
2889
- function select(node, event) {
2890
- if (!selectable || node.selectable === false || node.disabled) return;
2891
- const exists = selected.includes(node.key);
2892
- const next = multiple ? exists ? selected.filter((key) => key !== node.key) : [...selected, node.key] : exists ? [] : [node.key];
2893
- setSelected(next);
2894
- onSelect?.([...next], { selected: !exists, node, nativeEvent: event });
2895
- }
2896
- function selectForContextMenu(node, event) {
2897
- if (!selectable || node.selectable === false || node.disabled || selected.includes(node.key)) return;
2898
- const next = [node.key];
2899
- setSelected(next);
2900
- onSelect?.(next, { selected: true, node, nativeEvent: event });
2901
- }
2902
- function findNode2(key, nodes = treeData) {
2903
- for (const node of nodes) {
2904
- if (String(node.key) === key) return node;
2905
- const child = node.children?.length ? findNode2(key, node.children) : void 0;
2906
- if (child) return child;
2907
- }
2908
- return void 0;
2909
- }
2910
- function getPointerDropTarget(clientX, clientY, dragNode) {
2911
- const targetElement = document.elementFromPoint(clientX, clientY)?.closest("[data-sia-tree-drop-key]");
2912
- if (!targetElement) return void 0;
2913
- const targetNode = targetElement?.dataset.siaTreeDropKey ? findNode2(targetElement.dataset.siaTreeDropKey) : void 0;
2914
- if (!targetNode || targetNode.key === dragNode.key || targetNode.disabled || targetNode.droppable === false) return void 0;
2915
- const bounds = targetElement.getBoundingClientRect();
2916
- const relativeY = bounds.height > 0 ? (clientY - bounds.top) / bounds.height : 0.5;
2917
- const dropPosition = relativeY < 0.25 ? "before" : relativeY > 0.75 ? "after" : "inside";
2918
- return { node: targetNode, dropPosition };
2919
- }
2920
- function beginPointerDrag(node, event) {
2921
- if (event.button !== 0) return;
2922
- pointerDragRef.current = {
2923
- node,
2924
- pointerId: event.pointerId,
2925
- startX: event.clientX,
2926
- startY: event.clientY,
2927
- active: false
2928
- };
2929
- event.currentTarget.setPointerCapture(event.pointerId);
2930
- }
2931
- function movePointerDrag(event) {
2932
- const pointerDrag = pointerDragRef.current;
2933
- if (!pointerDrag || pointerDrag.pointerId !== event.pointerId) return;
2934
- if (!pointerDrag.active && Math.hypot(event.clientX - pointerDrag.startX, event.clientY - pointerDrag.startY) < 5) return;
2935
- pointerDrag.active = true;
2936
- dragNodeRef.current = pointerDrag.node;
2937
- event.preventDefault();
2938
- const target = getPointerDropTarget(event.clientX, event.clientY, pointerDrag.node);
2939
- setDragOver(target ? { key: target.node.key, position: target.dropPosition } : void 0);
2940
- }
2941
- function finishPointerDrag(event) {
2942
- const pointerDrag = pointerDragRef.current;
2943
- if (!pointerDrag || pointerDrag.pointerId !== event.pointerId) return;
2944
- if (event.currentTarget.hasPointerCapture(event.pointerId)) event.currentTarget.releasePointerCapture(event.pointerId);
2945
- if (pointerDrag.active) {
2946
- event.preventDefault();
2947
- suppressClickRef.current = true;
2948
- const target = getPointerDropTarget(event.clientX, event.clientY, pointerDrag.node);
2949
- if (target) onDrop?.({ dragNode: pointerDrag.node, node: target.node, dropPosition: target.dropPosition });
2950
- window.setTimeout(() => {
2951
- suppressClickRef.current = false;
2952
- }, 0);
2953
- }
2954
- pointerDragRef.current = void 0;
2955
- dragNodeRef.current = void 0;
2956
- setDragOver(void 0);
2957
- }
2958
- function cancelPointerDrag(event) {
2959
- const pointerDrag = pointerDragRef.current;
2960
- if (!pointerDrag || pointerDrag.pointerId !== event.pointerId) return;
2961
- pointerDragRef.current = void 0;
2962
- dragNodeRef.current = void 0;
2963
- setDragOver(void 0);
2964
- }
2965
- function render(nodes, depth = 0) {
2966
- return nodes.map((node) => {
2967
- const open = expanded.includes(node.key);
2968
- const isSelected = selected.includes(node.key);
2969
- const isSelectedAncestor = selectedAncestorKeys.has(node.key);
2970
- const isChecked = checkState.checked.has(node.key);
2971
- const isHalfChecked = checkState.halfChecked.has(node.key);
2972
- const hasChildren = Boolean(node.children?.length || node.isLeaf === false);
2973
- const nodeLoading = loading.has(node.key) || controlledLoading.has(node.key);
2974
- const sticky = stickyAncestors && hasChildren && open;
2975
- const canDrag = draggable && node.draggable !== false && !node.disabled;
2976
- const { className: rowClassName = "", style: rowStyle, onClick: onRowClick, ...rowProps } = node.rowProps ?? {};
2977
- const unloaded = hasChildren && node.loaded === false;
2978
- const dropPosition = dragOver?.key === node.key ? dragOver.position : void 0;
2979
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: `sia-tree__node${isSelected ? " is-selected" : ""}${isSelectedAncestor ? " is-selected-ancestor" : ""}${dropPosition ? ` is-drag-over-${dropPosition}` : ""}${node.disabled ? " is-disabled" : ""}${unloaded ? " is-unloaded" : ""}`, role: "treeitem", "aria-expanded": hasChildren ? open : void 0, "aria-selected": isSelected, children: [
2980
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
2981
- "div",
2982
- {
2983
- ...withTitleTooltip(rowProps),
2984
- className: `sia-tree__row${blockNode ? " sia-tree__row--block" : ""}${sticky ? " sia-tree__row--sticky" : ""} ${rowClassName}`.trim(),
2985
- style: {
2986
- ...rowStyle,
2987
- paddingInlineStart: depth * Math.max(0, indentSize),
2988
- top: sticky ? depth * Math.max(1, stickyRowHeight) : rowStyle?.top,
2989
- zIndex: sticky ? Math.max(1, 100 - depth) : rowStyle?.zIndex
2990
- },
2991
- "aria-busy": nodeLoading || void 0,
2992
- "data-sia-tree-drop-key": String(node.key),
2993
- "data-sia-tree-draggable": canDrag || void 0,
2994
- onClick: (event) => {
2995
- onRowClick?.(event);
2996
- if (event.defaultPrevented || suppressClickRef.current || node.disabled) return;
2997
- select(node, event);
2998
- if (hasChildren) void toggleExpand(node);
2999
- },
3000
- children: (() => {
3001
- const content = /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("span", { className: "sia-tree__context-content", children: [
3002
- hasChildren ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("button", { type: "button", className: "sia-tree__switcher", "aria-label": open ? "\u6536\u8D77\u8282\u70B9" : "\u5C55\u5F00\u8282\u70B9", "aria-expanded": open, style: { width: normalizedSwitcherWidth, flexBasis: normalizedSwitcherWidth }, disabled: node.disabled, onClick: (event) => {
3003
- event.stopPropagation();
3004
- void toggleExpand(node);
3005
- }, children: nodeLoading ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Icon, { name: "loader", className: "sia-spin-icon", size: 13 }) : /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Icon, { name: open ? "chevron-down" : "chevron-right", size: 13 }) }) : /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "sia-tree__switcher", "aria-label": open ? "\u6536\u8D77\u8282\u70B9" : "\u5C55\u5F00\u8282\u70B9", "aria-expanded": open, style: { width: normalizedSwitcherWidth, flexBasis: normalizedSwitcherWidth }, "aria-hidden": "true" }),
3006
- checkable || node.checkable ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { onClick: (event) => event.stopPropagation(), children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Checkbox2, { checked: isChecked, indeterminate: isHalfChecked, disabled: node.disabled || node.disableCheckbox, onChange: () => check(node) }) }) : null,
3007
- node.prefix ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "sia-tree__prefix", onClick: (event) => event.stopPropagation(), children: node.prefix }) : null,
3008
- showIcon ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "sia-tree__icon", children: node.icon ?? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Icon, { name: hasChildren ? "folder" : "file", size: 15 }) }) : null,
3009
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
3010
- "button",
3011
- {
3012
- type: "button",
3013
- className: "sia-tree__title",
3014
- disabled: node.disabled,
3015
- onPointerDown: canDrag ? (event) => beginPointerDrag(node, event) : void 0,
3016
- onPointerMove: canDrag ? movePointerDrag : void 0,
3017
- onPointerUp: canDrag ? finishPointerDrag : void 0,
3018
- onPointerCancel: canDrag ? cancelPointerDrag : void 0,
3019
- children: node.title
3020
- }
3021
- ),
3022
- nodeLoading ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "sia-tree__loading-text", role: "status", children: node.loadingText ?? "\u6B63\u5728\u52A0\u8F7D\u2026" }) : null,
3023
- node.extra ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "sia-tree__extra", onClick: (event) => event.stopPropagation(), children: node.extra }) : null
3024
- ] });
3025
- return node.contextMenu ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
3026
- Dropdown,
3027
- {
3028
- className: "sia-tree__context-trigger",
3029
- popupClassName: node.contextMenuPopupClassName,
3030
- trigger: ["contextMenu"],
3031
- menu: node.contextMenu,
3032
- onContextMenu: (event) => selectForContextMenu(node, event),
3033
- children: content
3034
- }
3035
- ) : content;
3036
- })()
3037
- }
3038
- ),
3039
- open && node.children?.length ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "sia-tree__children", role: "group", children: render(node.children, depth + 1) }) : null
3040
- ] }, node.key);
3041
- });
3042
- }
3043
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: `sia-tree${showLine ? " sia-tree--line" : ""}${stickyAncestors ? " sia-tree--sticky-ancestors" : ""} ${className}`.trim(), role: "tree", "aria-multiselectable": multiple || void 0, ...withTitleTooltip(props), children: render(treeData) });
3044
- }
3045
-
3046
- // src/components/FloatingScrollbarProvider.tsx
3047
- var import_react16 = require("react");
3048
-
3049
- // src/components/useScrollbarProximity.ts
3050
- var import_react15 = require("react");
3051
- var users = 0;
3052
- var cleanup;
3053
- function useScrollbarProximity() {
3054
- (0, import_react15.useEffect)(() => {
3055
- if (users++ === 0) {
3056
- let frame = 0;
3057
- let x = -Infinity;
3058
- let y = -Infinity;
3059
- const nearby = /* @__PURE__ */ new Set();
3060
- const clear = () => {
3061
- nearby.forEach((track) => track.removeAttribute("data-scrollbar-near"));
3062
- nearby.clear();
3063
- };
3064
- const update = () => {
3065
- frame = 0;
3066
- const next = /* @__PURE__ */ new Set();
3067
- document.querySelectorAll(".sia-scrollbar-track").forEach((track) => {
3068
- const rect = track.getBoundingClientRect();
3069
- const proximity = Number.parseFloat(getComputedStyle(track).getPropertyValue("--sia-scrollbar-proximity")) || 16;
3070
- if (rect.width && rect.height && x >= rect.left - proximity && x <= rect.right + proximity && y >= rect.top - proximity && y <= rect.bottom + proximity) {
3071
- next.add(track);
3072
- if (!nearby.has(track)) track.setAttribute("data-scrollbar-near", "true");
3073
- }
3074
- });
3075
- nearby.forEach((track) => {
3076
- if (!next.has(track)) track.removeAttribute("data-scrollbar-near");
3077
- });
3078
- nearby.clear();
3079
- next.forEach((track) => nearby.add(track));
3080
- };
3081
- const move = (event) => {
3082
- if (event.pointerType === "touch") return;
3083
- x = event.clientX;
3084
- y = event.clientY;
3085
- if (!frame) frame = requestAnimationFrame(update);
3086
- };
3087
- const leave = () => {
3088
- x = y = -Infinity;
3089
- clear();
3090
- };
3091
- document.addEventListener("pointermove", move, { passive: true });
3092
- document.documentElement.addEventListener("pointerleave", leave);
3093
- window.addEventListener("blur", leave);
3094
- cleanup = () => {
3095
- if (frame) cancelAnimationFrame(frame);
3096
- document.removeEventListener("pointermove", move);
3097
- document.documentElement.removeEventListener("pointerleave", leave);
3098
- window.removeEventListener("blur", leave);
3099
- clear();
3100
- };
3101
- }
3102
- return () => {
3103
- if (--users === 0) {
3104
- cleanup?.();
3105
- cleanup = void 0;
3106
- }
3107
- };
3108
- }, []);
3176
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Floating, { ...props, content: props.title });
3109
3177
  }
3110
-
3111
- // src/components/FloatingScrollbarProvider.tsx
3112
- var scrollableOverflow = /* @__PURE__ */ new Set(["auto", "scroll", "overlay"]);
3113
- var clippingOverflow = /* @__PURE__ */ new Set(["auto", "scroll", "overlay", "hidden", "clip"]);
3114
- function isRootScroller(target) {
3115
- return target === document.scrollingElement || target === document.documentElement;
3178
+ function Popover({ title, content, trigger = "click", overlayClassName = "", ...props }) {
3179
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Floating, { ...props, trigger, role: "dialog", overlayClassName: `sia-popover ${overlayClassName}`.trim(), content: /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_jsx_runtime14.Fragment, { children: [
3180
+ title ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("strong", { className: "sia-popover__title", children: title }) : null,
3181
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "sia-popover__content", children: content })
3182
+ ] }) });
3116
3183
  }
3117
- function canScroll(target, axis) {
3118
- if (target.matches(".sia-table__viewport, [data-sia-native-scrollbar]")) return false;
3119
- if (isRootScroller(target)) {
3120
- return axis === "horizontal" ? target.scrollWidth > target.clientWidth + 1 : target.scrollHeight > target.clientHeight + 1;
3121
- }
3122
- const style = getComputedStyle(target);
3123
- const overflow = axis === "horizontal" ? style.overflowX : style.overflowY;
3124
- const hasOverflow = axis === "horizontal" ? target.scrollWidth > target.clientWidth + 1 : target.scrollHeight > target.clientHeight + 1;
3125
- return hasOverflow && scrollableOverflow.has(overflow);
3184
+ function Timeline({ items = [], mode = "left", pending = false, pendingDot, reverse = false, className = "", ...props }) {
3185
+ const values = [...items, ...pending ? [{ children: pending === true ? "\u52A0\u8F7D\u4E2D..." : pending, dot: pendingDot ?? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Icon, { name: "loader", className: "sia-spin-icon" }) }] : []];
3186
+ if (reverse) values.reverse();
3187
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("ul", { className: `sia-timeline sia-timeline--${mode} ${className}`.trim(), ...withTitleTooltip(props), children: values.map((item, index) => {
3188
+ const position = item.position ?? (mode === "alternate" ? index % 2 ? "right" : "left" : mode);
3189
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("li", { className: `sia-timeline__item sia-timeline__item--${position}`, children: [
3190
+ item.label ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "sia-timeline__label", children: item.label }) : null,
3191
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "sia-timeline__rail", children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "sia-timeline__dot", style: { color: item.color && !["blue", "red", "green", "gray"].includes(item.color) ? item.color : void 0 }, "data-color": item.color ?? "blue", children: item.dot }) }),
3192
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "sia-timeline__content", children: item.children })
3193
+ ] }, index);
3194
+ }) });
3126
3195
  }
3127
- function visibleRect(target) {
3128
- if (isRootScroller(target)) {
3129
- return { top: 0, right: window.innerWidth, bottom: window.innerHeight, left: 0, width: window.innerWidth, height: window.innerHeight };
3130
- }
3131
- const ownRect = target.getBoundingClientRect();
3132
- let top = Math.max(0, ownRect.top);
3133
- let right = Math.min(window.innerWidth, ownRect.right);
3134
- let bottom = Math.min(window.innerHeight, ownRect.bottom);
3135
- let left = Math.max(0, ownRect.left);
3136
- let parent = target.parentElement;
3137
- while (parent && parent !== document.body && parent !== document.documentElement) {
3138
- const style = getComputedStyle(parent);
3139
- const clipX = clippingOverflow.has(style.overflowX);
3140
- const clipY = clippingOverflow.has(style.overflowY);
3141
- if (clipX || clipY) {
3142
- const rect = parent.getBoundingClientRect();
3143
- if (clipX) {
3144
- left = Math.max(left, rect.left);
3145
- right = Math.min(right, rect.right);
3146
- }
3147
- if (clipY) {
3148
- top = Math.max(top, rect.top);
3149
- bottom = Math.min(bottom, rect.bottom);
3150
- }
3151
- }
3152
- parent = parent.parentElement;
3153
- }
3154
- return {
3155
- top,
3156
- right,
3157
- bottom,
3158
- left,
3159
- width: Math.max(0, right - left),
3160
- height: Math.max(0, bottom - top)
3161
- };
3196
+ function collectKeys(nodes, branchOnly = false, result = []) {
3197
+ nodes.forEach((node) => {
3198
+ if (!branchOnly || node.children?.length) result.push(node.key);
3199
+ if (node.children) collectKeys(node.children, branchOnly, result);
3200
+ });
3201
+ return result;
3162
3202
  }
3163
- function createTrack(axis) {
3164
- const track = document.createElement("div");
3165
- const thumb = document.createElement("div");
3166
- track.className = `sia-scrollbar-track sia-floating-scrollbar sia-floating-scrollbar--${axis}`;
3167
- thumb.className = "sia-floating-scrollbar__thumb";
3168
- track.dataset.axis = axis;
3169
- track.setAttribute("aria-hidden", "true");
3170
- track.append(thumb);
3171
- return { track, thumb };
3203
+ function flattenVisibleTree(nodes, expandedKeys, depth = 0, ancestorKeys = [], result = []) {
3204
+ nodes.forEach((node, siblingIndex) => {
3205
+ result.push({ node, depth, ancestorKeys, index: result.length, positionInSet: siblingIndex + 1, setSize: nodes.length });
3206
+ if (expandedKeys.has(node.key) && node.children?.length) {
3207
+ flattenVisibleTree(node.children, expandedKeys, depth + 1, [...ancestorKeys, node.key], result);
3208
+ }
3209
+ });
3210
+ return result;
3172
3211
  }
3173
- function FloatingScrollbarProvider({ disabled = false, targetRef }) {
3174
- useScrollbarProximity();
3175
- (0, import_react16.useEffect)(() => {
3176
- if (disabled) return;
3177
- const layer = document.createElement("div");
3178
- layer.className = "sia-floating-scrollbar-layer";
3179
- layer.setAttribute("aria-hidden", "true");
3180
- document.body.append(layer);
3181
- const entries = /* @__PURE__ */ new Map();
3182
- let scanFrame = 0;
3183
- let updateFrame = 0;
3184
- const setEntryVisible = (entry, visible) => {
3185
- entry.horizontalTrack.classList.toggle("sia-floating-scrollbar--visible", visible);
3186
- entry.verticalTrack.classList.toggle("sia-floating-scrollbar--visible", visible);
3187
- };
3188
- const showEntry = (entry) => {
3189
- if (entry.hideTimer !== null) window.clearTimeout(entry.hideTimer);
3190
- entry.hideTimer = null;
3191
- setEntryVisible(entry, true);
3192
- };
3193
- const scheduleHide = (entry, delay = 500) => {
3194
- if (entry.hideTimer !== null) window.clearTimeout(entry.hideTimer);
3195
- entry.hideTimer = window.setTimeout(() => {
3196
- entry.hideTimer = null;
3197
- if (!entry.hoveringTarget && !entry.hoveringTrack && !entry.dragging) setEntryVisible(entry, false);
3198
- }, delay);
3199
- };
3200
- const updateEntry = (entry) => {
3201
- const { target, horizontalTrack, horizontalThumb, verticalTrack, verticalThumb } = entry;
3202
- const hasHorizontal = canScroll(target, "horizontal");
3203
- const hasVertical = canScroll(target, "vertical");
3204
- const rect = visibleRect(target);
3205
- const thickness = 10;
3206
- horizontalTrack.hidden = !hasHorizontal || rect.width <= thickness || rect.height <= 0;
3207
- verticalTrack.hidden = !hasVertical || rect.height <= thickness || rect.width <= 0;
3208
- if (!horizontalTrack.hidden) {
3209
- const trackWidth = Math.max(0, rect.width - (hasVertical ? thickness : 0));
3210
- const thumbWidth = Math.max(24, Math.min(trackWidth, trackWidth * target.clientWidth / target.scrollWidth));
3211
- const travel = Math.max(0, trackWidth - thumbWidth);
3212
- const maxScroll = Math.max(1, target.scrollWidth - target.clientWidth);
3213
- horizontalTrack.style.left = `${rect.left}px`;
3214
- horizontalTrack.style.top = `${rect.bottom - thickness}px`;
3215
- horizontalTrack.style.width = `${trackWidth}px`;
3216
- horizontalTrack.style.height = `${thickness}px`;
3217
- horizontalThumb.style.width = `${thumbWidth}px`;
3218
- horizontalThumb.style.transform = `translate3d(${travel * target.scrollLeft / maxScroll}px, 0, 0)`;
3219
- }
3220
- if (!verticalTrack.hidden) {
3221
- const trackHeight = Math.max(0, rect.height - (hasHorizontal ? thickness : 0));
3222
- const thumbHeight = Math.max(24, Math.min(trackHeight, trackHeight * target.clientHeight / target.scrollHeight));
3223
- const travel = Math.max(0, trackHeight - thumbHeight);
3224
- const maxScroll = Math.max(1, target.scrollHeight - target.clientHeight);
3225
- verticalTrack.style.left = `${rect.right - thickness}px`;
3226
- verticalTrack.style.top = `${rect.top}px`;
3227
- verticalTrack.style.width = `${thickness}px`;
3228
- verticalTrack.style.height = `${trackHeight}px`;
3229
- verticalThumb.style.height = `${thumbHeight}px`;
3230
- verticalThumb.style.transform = `translate3d(0, ${travel * target.scrollTop / maxScroll}px, 0)`;
3231
- }
3232
- };
3233
- const scheduleUpdateAll = () => {
3234
- if (updateFrame) return;
3235
- updateFrame = window.requestAnimationFrame(() => {
3236
- updateFrame = 0;
3237
- entries.forEach(updateEntry);
3238
- });
3239
- };
3240
- const bindTrackDrag = (entry, axis, track, thumb) => {
3241
- const target = entry.target;
3242
- const onTrackPointerEnter = () => {
3243
- entry.hoveringTrack = true;
3244
- showEntry(entry);
3245
- };
3246
- const onTrackPointerLeave = () => {
3247
- entry.hoveringTrack = false;
3248
- scheduleHide(entry);
3249
- };
3250
- const onTrackPointerDown = (event) => {
3251
- if (event.target === thumb || event.button !== 0) return;
3252
- event.preventDefault();
3253
- event.stopPropagation();
3254
- const trackRect = track.getBoundingClientRect();
3255
- const thumbRect = thumb.getBoundingClientRect();
3256
- const trackLength = axis === "vertical" ? trackRect.height : trackRect.width;
3257
- const thumbLength = axis === "vertical" ? thumbRect.height : thumbRect.width;
3258
- const pointer = axis === "vertical" ? event.clientY - trackRect.top : event.clientX - trackRect.left;
3259
- const ratio = Math.max(0, Math.min(1, (pointer - thumbLength / 2) / Math.max(1, trackLength - thumbLength)));
3260
- if (axis === "vertical") target.scrollTop = ratio * (target.scrollHeight - target.clientHeight);
3261
- else target.scrollLeft = ratio * (target.scrollWidth - target.clientWidth);
3262
- };
3263
- const onThumbPointerDown = (event) => {
3264
- if (event.button !== 0) return;
3265
- event.preventDefault();
3266
- event.stopPropagation();
3267
- entry.dragging = true;
3268
- showEntry(entry);
3269
- thumb.setPointerCapture(event.pointerId);
3270
- const startPointer = axis === "vertical" ? event.clientY : event.clientX;
3271
- const startScroll = axis === "vertical" ? target.scrollTop : target.scrollLeft;
3272
- const trackRect = track.getBoundingClientRect();
3273
- const thumbRect = thumb.getBoundingClientRect();
3274
- const trackLength = axis === "vertical" ? trackRect.height : trackRect.width;
3275
- const thumbLength = axis === "vertical" ? thumbRect.height : thumbRect.width;
3276
- const maxScroll = axis === "vertical" ? target.scrollHeight - target.clientHeight : target.scrollWidth - target.clientWidth;
3277
- const travel = Math.max(1, trackLength - thumbLength);
3278
- const onPointerMove = (moveEvent) => {
3279
- if (moveEvent.pointerId !== event.pointerId) return;
3280
- const pointer = axis === "vertical" ? moveEvent.clientY : moveEvent.clientX;
3281
- const nextScroll = startScroll + (pointer - startPointer) * maxScroll / travel;
3282
- if (axis === "vertical") target.scrollTop = nextScroll;
3283
- else target.scrollLeft = nextScroll;
3284
- };
3285
- const onPointerUp = (upEvent) => {
3286
- if (upEvent.pointerId !== event.pointerId) return;
3287
- entry.dragging = false;
3288
- thumb.releasePointerCapture(event.pointerId);
3289
- window.removeEventListener("pointermove", onPointerMove);
3290
- window.removeEventListener("pointerup", onPointerUp);
3291
- window.removeEventListener("pointercancel", onPointerUp);
3292
- scheduleHide(entry);
3293
- };
3294
- window.addEventListener("pointermove", onPointerMove);
3295
- window.addEventListener("pointerup", onPointerUp);
3296
- window.addEventListener("pointercancel", onPointerUp);
3297
- };
3298
- track.addEventListener("pointerenter", onTrackPointerEnter);
3299
- track.addEventListener("pointerleave", onTrackPointerLeave);
3300
- track.addEventListener("pointerdown", onTrackPointerDown);
3301
- thumb.addEventListener("pointerdown", onThumbPointerDown);
3302
- return () => {
3303
- track.removeEventListener("pointerenter", onTrackPointerEnter);
3304
- track.removeEventListener("pointerleave", onTrackPointerLeave);
3305
- track.removeEventListener("pointerdown", onTrackPointerDown);
3306
- thumb.removeEventListener("pointerdown", onThumbPointerDown);
3307
- };
3212
+ function Tree({
3213
+ treeData,
3214
+ selectedKeys,
3215
+ defaultSelectedKeys = [],
3216
+ expandedKeys,
3217
+ defaultExpandedKeys = [],
3218
+ defaultExpandAll = false,
3219
+ checkedKeys,
3220
+ defaultCheckedKeys = [],
3221
+ loadingKeys = [],
3222
+ checkable = false,
3223
+ checkStrictly = false,
3224
+ multiple = false,
3225
+ selectable = true,
3226
+ showLine = false,
3227
+ showIcon = false,
3228
+ blockNode = false,
3229
+ indentSize = 20,
3230
+ switcherWidth = 24,
3231
+ stickyAncestors = false,
3232
+ stickyRowHeight = 34,
3233
+ virtual = true,
3234
+ virtualThreshold = 100,
3235
+ virtualItemHeight,
3236
+ draggable = false,
3237
+ loadData,
3238
+ onSelect,
3239
+ onCheck,
3240
+ onExpand,
3241
+ onDrop,
3242
+ className = "",
3243
+ ...props
3244
+ }) {
3245
+ const scrollRef = (0, import_react16.useRef)(null);
3246
+ const [selected, setSelected] = useControllableState({ value: selectedKeys, defaultValue: defaultSelectedKeys });
3247
+ const [expanded, setExpanded] = useControllableState({ value: expandedKeys, defaultValue: defaultExpandAll ? collectKeys(treeData, true) : defaultExpandedKeys });
3248
+ const [checked, setChecked] = useControllableState({ value: checkedKeys, defaultValue: defaultCheckedKeys });
3249
+ const [loading, setLoading] = (0, import_react16.useState)(/* @__PURE__ */ new Set());
3250
+ const controlledLoading = (0, import_react16.useMemo)(() => new Set(loadingKeys), [loadingKeys]);
3251
+ const selectedKeySet = (0, import_react16.useMemo)(() => new Set(selected), [selected]);
3252
+ const expandedKeySet = (0, import_react16.useMemo)(() => new Set(expanded), [expanded]);
3253
+ const flatNodes = (0, import_react16.useMemo)(() => flattenVisibleTree(treeData, expandedKeySet), [expandedKeySet, treeData]);
3254
+ const normalizedVirtualThreshold = Math.max(1, Math.floor(virtualThreshold));
3255
+ const normalizedVirtualItemHeight = Math.max(1, virtualItemHeight ?? stickyRowHeight);
3256
+ const wantsVirtualScroll = virtual && flatNodes.length >= normalizedVirtualThreshold;
3257
+ const [virtualViewport, setVirtualViewport] = (0, import_react16.useState)({ height: 0, scrollTop: 0, ownsScroll: true });
3258
+ (0, import_react16.useLayoutEffect)(() => {
3259
+ if (!wantsVirtualScroll) return void 0;
3260
+ const element = scrollRef.current;
3261
+ if (!element) return void 0;
3262
+ let animationFrame = 0;
3263
+ const updateViewport = () => {
3264
+ animationFrame = 0;
3265
+ const overflowY = window.getComputedStyle(element).overflowY;
3266
+ const ownsScroll = overflowY === "auto" || overflowY === "scroll" || overflowY === "overlay";
3267
+ const next = { height: element.clientHeight, scrollTop: element.scrollTop, ownsScroll };
3268
+ setVirtualViewport((current) => current.height === next.height && current.scrollTop === next.scrollTop && current.ownsScroll === next.ownsScroll ? current : next);
3308
3269
  };
3309
- const resizeObserver = new ResizeObserver((observedEntries) => {
3310
- observedEntries.forEach(({ target }) => {
3311
- const entry = entries.get(target);
3312
- if (entry) updateEntry(entry);
3313
- });
3314
- });
3315
- const addTarget = (target) => {
3316
- if (entries.has(target)) return;
3317
- const horizontal = createTrack("horizontal");
3318
- const vertical = createTrack("vertical");
3319
- layer.append(horizontal.track, vertical.track);
3320
- const onPointerEnter = () => {
3321
- const entry2 = entries.get(target);
3322
- if (!entry2) return;
3323
- entry2.hoveringTarget = true;
3324
- showEntry(entry2);
3325
- };
3326
- const onPointerLeave = () => {
3327
- const entry2 = entries.get(target);
3328
- if (!entry2) return;
3329
- entry2.hoveringTarget = false;
3330
- scheduleHide(entry2);
3331
- };
3332
- const onScroll = () => {
3333
- const entry2 = entries.get(target);
3334
- if (!entry2) return;
3335
- updateEntry(entry2);
3336
- showEntry(entry2);
3337
- scheduleHide(entry2, 700);
3338
- };
3339
- target.addEventListener("pointerenter", onPointerEnter);
3340
- target.addEventListener("pointerleave", onPointerLeave);
3341
- target.addEventListener("scroll", onScroll, { passive: true });
3342
- const entry = {
3343
- target,
3344
- horizontalTrack: horizontal.track,
3345
- horizontalThumb: horizontal.thumb,
3346
- verticalTrack: vertical.track,
3347
- verticalThumb: vertical.thumb,
3348
- hideTimer: null,
3349
- hoveringTarget: target.matches(":hover"),
3350
- hoveringTrack: false,
3351
- dragging: false,
3352
- cleanup: () => void 0
3353
- };
3354
- const cleanupHorizontal = bindTrackDrag(entry, "horizontal", horizontal.track, horizontal.thumb);
3355
- const cleanupVertical = bindTrackDrag(entry, "vertical", vertical.track, vertical.thumb);
3356
- entry.cleanup = () => {
3357
- if (entry.hideTimer !== null) window.clearTimeout(entry.hideTimer);
3358
- cleanupHorizontal();
3359
- cleanupVertical();
3360
- target.removeEventListener("pointerenter", onPointerEnter);
3361
- target.removeEventListener("pointerleave", onPointerLeave);
3362
- target.removeEventListener("scroll", onScroll);
3363
- resizeObserver.unobserve(target);
3364
- target.removeAttribute("data-sia-floating-scrollbar");
3365
- horizontal.track.remove();
3366
- vertical.track.remove();
3367
- };
3368
- entries.set(target, entry);
3369
- target.setAttribute("data-sia-floating-scrollbar", "");
3370
- resizeObserver.observe(target);
3371
- updateEntry(entry);
3372
- if (entry.hoveringTarget) showEntry(entry);
3270
+ const scheduleViewportUpdate = () => {
3271
+ if (!animationFrame) animationFrame = window.requestAnimationFrame(updateViewport);
3373
3272
  };
3374
- const removeTarget = (target) => {
3375
- const entry = entries.get(target);
3376
- if (!entry) return;
3377
- entry.cleanup();
3378
- entries.delete(target);
3273
+ updateViewport();
3274
+ element.addEventListener("scroll", scheduleViewportUpdate, { passive: true });
3275
+ const resizeObserver = typeof ResizeObserver === "undefined" ? void 0 : new ResizeObserver(scheduleViewportUpdate);
3276
+ resizeObserver?.observe(element);
3277
+ return () => {
3278
+ element.removeEventListener("scroll", scheduleViewportUpdate);
3279
+ resizeObserver?.disconnect();
3280
+ if (animationFrame) window.cancelAnimationFrame(animationFrame);
3379
3281
  };
3380
- const scanTargets = () => {
3381
- scanFrame = 0;
3382
- const managedSelector = ".sia-table__viewport, [data-sia-native-scrollbar], .sia-scrollbar-track, .sia-floating-scrollbar-layer";
3383
- const discovered = [];
3384
- if (!targetRef) {
3385
- const walker = document.createTreeWalker(document.body, NodeFilter.SHOW_ELEMENT, {
3386
- acceptNode: (node) => node.matches(managedSelector) ? NodeFilter.FILTER_REJECT : NodeFilter.FILTER_ACCEPT
3387
- });
3388
- for (let node = walker.nextNode(); node; node = walker.nextNode()) {
3389
- if (node instanceof HTMLElement) discovered.push(node);
3390
- }
3391
- }
3392
- const candidates = (targetRef ? [targetRef.current] : [document.scrollingElement, ...discovered]).filter((target) => target instanceof HTMLElement);
3393
- const nextTargets = /* @__PURE__ */ new Set();
3394
- candidates.forEach((target) => {
3395
- if (canScroll(target, "horizontal") || canScroll(target, "vertical")) {
3396
- nextTargets.add(target);
3397
- if (entries.has(target) || !target.hasAttribute("data-sia-floating-scrollbar")) addTarget(target);
3398
- }
3399
- });
3400
- entries.forEach((_entry, target) => {
3401
- if (!target.isConnected || !nextTargets.has(target)) removeTarget(target);
3402
- else updateEntry(_entry);
3282
+ }, [className, props.style, wantsVirtualScroll]);
3283
+ const virtualEnabled = wantsVirtualScroll && virtualViewport.ownsScroll;
3284
+ const virtualWindow = (0, import_react16.useMemo)(() => {
3285
+ if (!virtualEnabled) return { start: 0, end: flatNodes.length };
3286
+ const overscan = 8;
3287
+ const viewportHeight = virtualViewport.height || normalizedVirtualItemHeight * 12;
3288
+ const maxStart = Math.max(0, flatNodes.length - 1);
3289
+ const start = Math.min(maxStart, Math.max(0, Math.floor(virtualViewport.scrollTop / normalizedVirtualItemHeight) - overscan));
3290
+ const end = Math.min(flatNodes.length, Math.ceil((virtualViewport.scrollTop + viewportHeight) / normalizedVirtualItemHeight) + overscan);
3291
+ return { start, end };
3292
+ }, [flatNodes.length, normalizedVirtualItemHeight, virtualEnabled, virtualViewport.height, virtualViewport.scrollTop]);
3293
+ const flatNodeByKey = (0, import_react16.useMemo)(() => new Map(flatNodes.map((item) => [item.node.key, item])), [flatNodes]);
3294
+ const virtualStickyNodes = (0, import_react16.useMemo)(() => {
3295
+ if (!virtualEnabled || !stickyAncestors || !flatNodes.length) return [];
3296
+ const anchorIndex = Math.min(flatNodes.length - 1, Math.max(0, Math.floor(virtualViewport.scrollTop / normalizedVirtualItemHeight)));
3297
+ return flatNodes[anchorIndex].ancestorKeys.map((key) => flatNodeByKey.get(key)).filter((item) => Boolean(item));
3298
+ }, [flatNodeByKey, flatNodes, normalizedVirtualItemHeight, stickyAncestors, virtualEnabled, virtualViewport.scrollTop]);
3299
+ const selectedAncestorKeys = (0, import_react16.useMemo)(() => {
3300
+ const ancestors = /* @__PURE__ */ new Set();
3301
+ const visit = (nodes, parentKeys) => {
3302
+ nodes.forEach((node) => {
3303
+ if (selectedKeySet.has(node.key)) parentKeys.forEach((key) => ancestors.add(key));
3304
+ if (node.children?.length) visit(node.children, [...parentKeys, node.key]);
3403
3305
  });
3404
3306
  };
3405
- const scheduleScan = () => {
3406
- if (scanFrame) return;
3407
- scanFrame = window.requestAnimationFrame(scanTargets);
3408
- };
3409
- const mutationObserver = new MutationObserver((mutations) => {
3410
- if (mutations.some((mutation) => {
3411
- const target = mutation.target instanceof Element ? mutation.target : mutation.target.parentElement;
3412
- return target && !layer.contains(target) && !target.closest(".sia-table__viewport, [data-sia-native-scrollbar], .sia-scrollbar-track");
3413
- })) scheduleScan();
3414
- });
3415
- mutationObserver.observe(document.documentElement, { subtree: true, childList: true, attributes: true, attributeFilter: ["class", "style", "hidden"] });
3416
- window.addEventListener("resize", scheduleScan);
3417
- window.addEventListener("scroll", scheduleUpdateAll, true);
3418
- scheduleScan();
3419
- return () => {
3420
- mutationObserver.disconnect();
3421
- resizeObserver.disconnect();
3422
- window.removeEventListener("resize", scheduleScan);
3423
- window.removeEventListener("scroll", scheduleUpdateAll, true);
3424
- if (scanFrame) window.cancelAnimationFrame(scanFrame);
3425
- if (updateFrame) window.cancelAnimationFrame(updateFrame);
3426
- entries.forEach((entry) => entry.cleanup());
3427
- entries.clear();
3428
- layer.remove();
3307
+ visit(treeData, []);
3308
+ return ancestors;
3309
+ }, [selectedKeySet, treeData]);
3310
+ const dragNodeRef = (0, import_react16.useRef)();
3311
+ const pointerDragRef = (0, import_react16.useRef)();
3312
+ const suppressClickRef = (0, import_react16.useRef)(false);
3313
+ const [dragOver, setDragOver] = (0, import_react16.useState)();
3314
+ const normalizedSwitcherWidth = Math.max(0, switcherWidth);
3315
+ async function toggleExpand(node) {
3316
+ const nextExpanded = !expanded.includes(node.key);
3317
+ if (nextExpanded && node.loaded !== true && !node.children?.length && node.isLeaf === false && loadData) {
3318
+ setLoading((keys) => new Set(keys).add(node.key));
3319
+ try {
3320
+ await loadData(node);
3321
+ } finally {
3322
+ setLoading((keys) => {
3323
+ const next2 = new Set(keys);
3324
+ next2.delete(node.key);
3325
+ return next2;
3326
+ });
3327
+ }
3328
+ }
3329
+ const next = nextExpanded ? [...expanded, node.key] : expanded.filter((key) => key !== node.key);
3330
+ setExpanded(next);
3331
+ onExpand?.([...next], { expanded: nextExpanded, node });
3332
+ }
3333
+ const checkState = (0, import_react16.useMemo)(() => getTreeCheckState(treeData, checked, checkStrictly), [treeData, checked, checkStrictly]);
3334
+ function check(node) {
3335
+ const next = toggleTreeCheck(treeData, checked, node, checkStrictly);
3336
+ setChecked([...next.checked]);
3337
+ onCheck?.([...next.checked], { checked: next.checkedValue, halfCheckedKeys: [...next.halfChecked], node });
3338
+ }
3339
+ function select(node, event) {
3340
+ if (!selectable || node.selectable === false || node.disabled) return;
3341
+ const exists = selected.includes(node.key);
3342
+ const next = multiple ? exists ? selected.filter((key) => key !== node.key) : [...selected, node.key] : exists ? [] : [node.key];
3343
+ setSelected(next);
3344
+ onSelect?.([...next], { selected: !exists, node, nativeEvent: event });
3345
+ }
3346
+ function selectForContextMenu(node, event) {
3347
+ if (!selectable || node.selectable === false || node.disabled || selected.includes(node.key)) return;
3348
+ const next = [node.key];
3349
+ setSelected(next);
3350
+ onSelect?.(next, { selected: true, node, nativeEvent: event });
3351
+ }
3352
+ function findNode2(key, nodes = treeData) {
3353
+ for (const node of nodes) {
3354
+ if (String(node.key) === key) return node;
3355
+ const child = node.children?.length ? findNode2(key, node.children) : void 0;
3356
+ if (child) return child;
3357
+ }
3358
+ return void 0;
3359
+ }
3360
+ function getPointerDropTarget(clientX, clientY, dragNode) {
3361
+ const targetElement = document.elementFromPoint(clientX, clientY)?.closest("[data-sia-tree-drop-key]");
3362
+ if (!targetElement) return void 0;
3363
+ const targetNode = targetElement?.dataset.siaTreeDropKey ? findNode2(targetElement.dataset.siaTreeDropKey) : void 0;
3364
+ if (!targetNode || targetNode.key === dragNode.key || targetNode.disabled || targetNode.droppable === false) return void 0;
3365
+ const bounds = targetElement.getBoundingClientRect();
3366
+ const relativeY = bounds.height > 0 ? (clientY - bounds.top) / bounds.height : 0.5;
3367
+ const dropPosition = relativeY < 0.25 ? "before" : relativeY > 0.75 ? "after" : "inside";
3368
+ return { node: targetNode, dropPosition };
3369
+ }
3370
+ function beginPointerDrag(node, event) {
3371
+ if (event.button !== 0) return;
3372
+ pointerDragRef.current = {
3373
+ node,
3374
+ pointerId: event.pointerId,
3375
+ startX: event.clientX,
3376
+ startY: event.clientY,
3377
+ active: false
3429
3378
  };
3430
- }, [disabled, targetRef]);
3431
- return null;
3379
+ event.currentTarget.setPointerCapture(event.pointerId);
3380
+ }
3381
+ function movePointerDrag(event) {
3382
+ const pointerDrag = pointerDragRef.current;
3383
+ if (!pointerDrag || pointerDrag.pointerId !== event.pointerId) return;
3384
+ if (!pointerDrag.active && Math.hypot(event.clientX - pointerDrag.startX, event.clientY - pointerDrag.startY) < 5) return;
3385
+ pointerDrag.active = true;
3386
+ dragNodeRef.current = pointerDrag.node;
3387
+ event.preventDefault();
3388
+ const target = getPointerDropTarget(event.clientX, event.clientY, pointerDrag.node);
3389
+ setDragOver(target ? { key: target.node.key, position: target.dropPosition } : void 0);
3390
+ }
3391
+ function finishPointerDrag(event) {
3392
+ const pointerDrag = pointerDragRef.current;
3393
+ if (!pointerDrag || pointerDrag.pointerId !== event.pointerId) return;
3394
+ if (event.currentTarget.hasPointerCapture(event.pointerId)) event.currentTarget.releasePointerCapture(event.pointerId);
3395
+ if (pointerDrag.active) {
3396
+ event.preventDefault();
3397
+ suppressClickRef.current = true;
3398
+ const target = getPointerDropTarget(event.clientX, event.clientY, pointerDrag.node);
3399
+ if (target) onDrop?.({ dragNode: pointerDrag.node, node: target.node, dropPosition: target.dropPosition });
3400
+ window.setTimeout(() => {
3401
+ suppressClickRef.current = false;
3402
+ }, 0);
3403
+ }
3404
+ pointerDragRef.current = void 0;
3405
+ dragNodeRef.current = void 0;
3406
+ setDragOver(void 0);
3407
+ }
3408
+ function cancelPointerDrag(event) {
3409
+ const pointerDrag = pointerDragRef.current;
3410
+ if (!pointerDrag || pointerDrag.pointerId !== event.pointerId) return;
3411
+ pointerDragRef.current = void 0;
3412
+ dragNodeRef.current = void 0;
3413
+ setDragOver(void 0);
3414
+ }
3415
+ function renderNode(node, depth, options = {}) {
3416
+ const open = expandedKeySet.has(node.key);
3417
+ const isSelected = selected.includes(node.key);
3418
+ const isSelectedAncestor = selectedAncestorKeys.has(node.key);
3419
+ const isChecked = checkState.checked.has(node.key);
3420
+ const isHalfChecked = checkState.halfChecked.has(node.key);
3421
+ const hasChildren = Boolean(node.children?.length || node.isLeaf === false);
3422
+ const nodeLoading = loading.has(node.key) || controlledLoading.has(node.key);
3423
+ const virtualizedNode = options.virtualTop !== void 0 || options.stickySlot !== void 0;
3424
+ const sticky = !virtualizedNode && stickyAncestors && hasChildren && open;
3425
+ const canDrag = draggable && node.draggable !== false && !node.disabled;
3426
+ const { className: rowClassName = "", style: rowStyle, onClick: onRowClick, ...rowProps } = node.rowProps ?? {};
3427
+ const unloaded = hasChildren && node.loaded === false;
3428
+ const dropPosition = dragOver?.key === node.key ? dragOver.position : void 0;
3429
+ const virtualNodeClassName = options.stickySlot !== void 0 ? " sia-tree__virtual-sticky-node" : options.virtualTop !== void 0 ? " sia-tree__virtual-node" : "";
3430
+ const virtualNodeStyle = options.stickySlot !== void 0 ? { top: options.stickySlot * normalizedVirtualItemHeight, height: normalizedVirtualItemHeight } : options.virtualTop !== void 0 ? { transform: `translateY(${options.virtualTop}px)`, height: normalizedVirtualItemHeight } : void 0;
3431
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
3432
+ "div",
3433
+ {
3434
+ className: `sia-tree__node${virtualNodeClassName}${isSelected ? " is-selected" : ""}${isSelectedAncestor ? " is-selected-ancestor" : ""}${dropPosition ? ` is-drag-over-${dropPosition}` : ""}${node.disabled ? " is-disabled" : ""}${unloaded ? " is-unloaded" : ""}`,
3435
+ style: virtualNodeStyle,
3436
+ role: "treeitem",
3437
+ "aria-expanded": hasChildren ? open : void 0,
3438
+ "aria-selected": isSelected,
3439
+ "aria-level": depth + 1,
3440
+ "aria-posinset": options.positionInSet,
3441
+ "aria-setsize": options.setSize,
3442
+ children: [
3443
+ virtualizedNode && showLine && depth > 0 ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "sia-tree__virtual-guides", "aria-hidden": "true", children: Array.from({ length: depth }, (_, guideDepth) => /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "sia-tree__virtual-guide", style: { insetInlineStart: (guideDepth + 1) * Math.max(0, indentSize) } }, guideDepth)) }) : null,
3444
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
3445
+ "div",
3446
+ {
3447
+ ...withTitleTooltip(rowProps),
3448
+ className: `sia-tree__row${blockNode ? " sia-tree__row--block" : ""}${sticky ? " sia-tree__row--sticky" : ""} ${rowClassName}`.trim(),
3449
+ style: {
3450
+ ...rowStyle,
3451
+ height: virtualizedNode ? "100%" : rowStyle?.height,
3452
+ paddingInlineStart: depth * Math.max(0, indentSize),
3453
+ top: sticky ? depth * Math.max(1, stickyRowHeight) : rowStyle?.top,
3454
+ zIndex: sticky ? Math.max(1, 100 - depth) : rowStyle?.zIndex
3455
+ },
3456
+ "aria-busy": nodeLoading || void 0,
3457
+ "data-sia-tree-drop-key": String(node.key),
3458
+ "data-sia-tree-draggable": canDrag || void 0,
3459
+ onClick: (event) => {
3460
+ onRowClick?.(event);
3461
+ if (event.defaultPrevented || suppressClickRef.current || node.disabled) return;
3462
+ select(node, event);
3463
+ if (hasChildren) void toggleExpand(node);
3464
+ },
3465
+ children: (() => {
3466
+ const content = /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("span", { className: "sia-tree__context-content", children: [
3467
+ hasChildren ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("button", { type: "button", className: "sia-tree__switcher", "aria-label": open ? "\u6536\u8D77\u8282\u70B9" : "\u5C55\u5F00\u8282\u70B9", "aria-expanded": open, style: { width: normalizedSwitcherWidth, flexBasis: normalizedSwitcherWidth }, disabled: node.disabled, onClick: (event) => {
3468
+ event.stopPropagation();
3469
+ void toggleExpand(node);
3470
+ }, children: nodeLoading ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Icon, { name: "loader", className: "sia-spin-icon", size: 13 }) : /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Icon, { name: open ? "chevron-down" : "chevron-right", size: 13 }) }) : /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "sia-tree__switcher", "aria-label": open ? "\u6536\u8D77\u8282\u70B9" : "\u5C55\u5F00\u8282\u70B9", "aria-expanded": open, style: { width: normalizedSwitcherWidth, flexBasis: normalizedSwitcherWidth }, "aria-hidden": "true" }),
3471
+ checkable || node.checkable ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { onClick: (event) => event.stopPropagation(), children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Checkbox2, { checked: isChecked, indeterminate: isHalfChecked, disabled: node.disabled || node.disableCheckbox, onChange: () => check(node) }) }) : null,
3472
+ node.prefix ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "sia-tree__prefix", onClick: (event) => event.stopPropagation(), children: node.prefix }) : null,
3473
+ showIcon ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "sia-tree__icon", children: node.icon ?? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Icon, { name: hasChildren ? "folder" : "file", size: 15 }) }) : null,
3474
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
3475
+ "button",
3476
+ {
3477
+ type: "button",
3478
+ className: "sia-tree__title",
3479
+ disabled: node.disabled,
3480
+ onPointerDown: canDrag ? (event) => beginPointerDrag(node, event) : void 0,
3481
+ onPointerMove: canDrag ? movePointerDrag : void 0,
3482
+ onPointerUp: canDrag ? finishPointerDrag : void 0,
3483
+ onPointerCancel: canDrag ? cancelPointerDrag : void 0,
3484
+ children: node.title
3485
+ }
3486
+ ),
3487
+ nodeLoading ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "sia-tree__loading-text", role: "status", children: node.loadingText ?? "\u6B63\u5728\u52A0\u8F7D\u2026" }) : null,
3488
+ node.extra ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "sia-tree__extra", onClick: (event) => event.stopPropagation(), children: node.extra }) : null
3489
+ ] });
3490
+ return node.contextMenu ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
3491
+ Dropdown,
3492
+ {
3493
+ className: "sia-tree__context-trigger",
3494
+ popupClassName: node.contextMenuPopupClassName,
3495
+ trigger: ["contextMenu"],
3496
+ menu: node.contextMenu,
3497
+ onContextMenu: (event) => selectForContextMenu(node, event),
3498
+ children: content
3499
+ }
3500
+ ) : content;
3501
+ })()
3502
+ }
3503
+ ),
3504
+ options.children
3505
+ ]
3506
+ },
3507
+ options.reactKey ?? node.key
3508
+ );
3509
+ }
3510
+ function render(nodes, depth = 0) {
3511
+ return nodes.map((node, siblingIndex) => renderNode(node, depth, {
3512
+ positionInSet: siblingIndex + 1,
3513
+ setSize: nodes.length,
3514
+ children: expandedKeySet.has(node.key) && node.children?.length ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "sia-tree__children", role: "group", children: render(node.children, depth + 1) }) : null
3515
+ }));
3516
+ }
3517
+ const stickyVirtualKeySet = new Set(virtualStickyNodes.map((item) => item.node.key));
3518
+ const virtualContent = virtualEnabled ? /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "sia-tree__virtual-space", style: { height: flatNodes.length * normalizedVirtualItemHeight }, children: [
3519
+ virtualStickyNodes.length ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "sia-tree__virtual-sticky-layer", children: virtualStickyNodes.map((item, stickySlot) => renderNode(item.node, item.depth, {
3520
+ reactKey: `sticky-${String(item.node.key)}`,
3521
+ stickySlot,
3522
+ positionInSet: item.positionInSet,
3523
+ setSize: item.setSize
3524
+ })) }) : null,
3525
+ flatNodes.slice(virtualWindow.start, virtualWindow.end).filter((item) => !stickyVirtualKeySet.has(item.node.key)).map((item) => renderNode(item.node, item.depth, {
3526
+ reactKey: `virtual-${String(item.node.key)}`,
3527
+ virtualTop: item.index * normalizedVirtualItemHeight,
3528
+ positionInSet: item.positionInSet,
3529
+ setSize: item.setSize
3530
+ }))
3531
+ ] }) : render(treeData);
3532
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { ref: scrollRef, className: `sia-tree${showLine ? " sia-tree--line" : ""}${stickyAncestors ? " sia-tree--sticky-ancestors" : ""}${virtualEnabled ? " sia-tree--virtual" : ""} ${className}`.trim(), role: "tree", "aria-multiselectable": multiple || void 0, "data-sia-tree-virtualized": virtualEnabled || void 0, ...withTitleTooltip(props), children: [
3533
+ virtualContent,
3534
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(FloatingScrollbarProvider, { targetRef: scrollRef })
3535
+ ] });
3432
3536
  }
3433
3537
 
3434
3538
  // src/components/SelectionPanel.tsx
@@ -6413,6 +6517,7 @@ function FormRoot({
6413
6517
  "data-required-mark": requiredMark,
6414
6518
  onSubmit: (event) => {
6415
6519
  event.preventDefault();
6520
+ event.stopPropagation();
6416
6521
  instance.submit();
6417
6522
  },
6418
6523
  children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("fieldset", { disabled, children })
@@ -12987,7 +13092,6 @@ function TableSelectedItemsPanel({
12987
13092
  }
12988
13093
  function createSelectionPlugin(options = {}) {
12989
13094
  const { mode = "multiple", childrenColumnName = "children" } = options;
12990
- const preservedRows = /* @__PURE__ */ new Map();
12991
13095
  function selected(context) {
12992
13096
  return options.selectedKeys ?? context.state.selectedKeys ?? [];
12993
13097
  }
@@ -12998,6 +13102,7 @@ function createSelectionPlugin(options = {}) {
12998
13102
  return flattenRecords(context.sourceData, childrenColumnName);
12999
13103
  }
13000
13104
  function entriesForKeys(keys, context) {
13105
+ const preservedRows = context.state.preservedRows;
13001
13106
  const records = allRecords(context);
13002
13107
  const currentRows = new Map(
13003
13108
  records.map(
@@ -13032,7 +13137,7 @@ function createSelectionPlugin(options = {}) {
13032
13137
  return entriesForKeys(keys, context).map((entry) => entry.record);
13033
13138
  }
13034
13139
  function setKeys(keys, context) {
13035
- if (keys.length === 0) preservedRows.clear();
13140
+ if (keys.length === 0) context.state.preservedRows.clear();
13036
13141
  context.setState((state) => ({
13037
13142
  ...state,
13038
13143
  selectedKeys: keys,
@@ -13048,7 +13153,7 @@ function createSelectionPlugin(options = {}) {
13048
13153
  (key) => pageKeys.has(key)
13049
13154
  );
13050
13155
  setKeys(currentPageKeys, context);
13051
- preservedRows.clear();
13156
+ context.state.preservedRows.clear();
13052
13157
  }
13053
13158
  context.setState((state) => ({
13054
13159
  ...state,
@@ -13070,11 +13175,13 @@ function createSelectionPlugin(options = {}) {
13070
13175
  return {
13071
13176
  id: "selection",
13072
13177
  order: 20,
13073
- initialState: {
13178
+ // 用工厂给每个表格创建独立缓存,避免同一插件对象被多个表格复用时串数据。
13179
+ initialState: () => ({
13180
+ preservedRows: /* @__PURE__ */ new Map(),
13074
13181
  selectedKeys: options.defaultSelectedKeys ?? [],
13075
13182
  preserveSelectedRowKeys: options.preserveSelectedRowKeys ?? false,
13076
13183
  panelOpen: false
13077
- },
13184
+ }),
13078
13185
  transformColumns(columns, context) {
13079
13186
  if (mode === "row") return columns;
13080
13187
  const selectionColumn = {
@@ -13222,7 +13329,7 @@ function createSelectionPlugin(options = {}) {
13222
13329
  const records = allRecords(context);
13223
13330
  const record = records.find(
13224
13331
  (item, index) => Object.is(context.getRowKey(item, index), key)
13225
- ) ?? preservedRows.get(key);
13332
+ ) ?? context.state.preservedRows.get(key);
13226
13333
  if (record) toggle(key, record, context, checked);
13227
13334
  },
13228
13335
  getSelectedRows: () => rowsForKeys(selected(context), context),