@sia.soul/sia-react-ui 0.1.62 → 0.1.64

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,892 +2543,996 @@ 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)(() => {
2687
- if (!visible) {
2688
- setPosition(void 0);
2689
- return;
2690
- }
2691
- const getAnchor = () => {
2692
- const root = rootRef.current;
2693
- const target = triggerElementRef.current;
2694
- return target && root?.contains(target) ? target : root?.firstElementChild ?? root;
2691
+ const showEntry = (entry) => {
2692
+ if (entry.hideTimer !== null) window.clearTimeout(entry.hideTimer);
2693
+ entry.hideTimer = null;
2694
+ setEntryVisible(entry, true);
2695
2695
  };
2696
- const update = () => {
2697
- if (!rootRef.current || !overlayRef.current) return;
2698
- const anchor2 = getAnchor();
2699
- if (anchor2) {
2700
- const rect = anchor2.getBoundingClientRect();
2701
- const next = resolveFloatingPosition(rect, overlayRef.current.getBoundingClientRect(), placement, typeof arrow === "object" && !!arrow.pointAtCenter, autoAdjustOverflow);
2702
- next.hidden = isFloatingAnchorHidden(anchor2, rect);
2703
- setPosition(next);
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)`;
2704
2734
  }
2705
2735
  };
2706
- update();
2707
- window.addEventListener("resize", update);
2708
- window.addEventListener("scroll", update, true);
2709
- window.addEventListener("animationend", update, true);
2710
- window.addEventListener("transitionend", update, true);
2711
- const observer = new ResizeObserver(update);
2712
- if (rootRef.current) observer.observe(rootRef.current);
2713
- const anchor = getAnchor();
2714
- if (anchor) observer.observe(anchor);
2715
- if (overlayRef.current) observer.observe(overlayRef.current);
2716
- return () => {
2717
- window.removeEventListener("resize", update);
2718
- window.removeEventListener("scroll", update, true);
2719
- window.removeEventListener("animationend", update, true);
2720
- window.removeEventListener("transitionend", update, true);
2721
- observer.disconnect();
2736
+ const scheduleUpdateAll = () => {
2737
+ if (updateFrame) return;
2738
+ updateFrame = window.requestAnimationFrame(() => {
2739
+ updateFrame = 0;
2740
+ entries.forEach(updateEntry);
2741
+ });
2722
2742
  };
2723
- }, [children, content, placement, visible, portalContainer, autoAdjustOverflow, typeof arrow === "object" && arrow.pointAtCenter]);
2724
- (0, import_react14.useEffect)(() => () => {
2725
- clearTimers();
2726
- if (activeTooltipClose === closeSelfRef.current) activeTooltipClose = void 0;
2727
- }, []);
2728
- const child = (0, import_react14.cloneElement)(children, {
2729
- ...triggers.includes("hover") ? { onMouseEnter: (event) => {
2730
- triggerElementRef.current = event.currentTarget;
2731
- children.props.onMouseEnter?.(event);
2732
- show(mouseEnterDelay);
2733
- }, onMouseLeave: (event) => {
2734
- children.props.onMouseLeave?.(event);
2735
- hide(mouseLeaveDelay);
2736
- } } : {},
2737
- ...triggers.includes("focus") ? { onFocus: (event) => {
2738
- triggerElementRef.current = event.currentTarget;
2739
- children.props.onFocus?.(event);
2740
- show();
2741
- }, onBlur: (event) => {
2742
- children.props.onBlur?.(event);
2743
- hide();
2744
- } } : {},
2745
- ...triggers.includes("click") ? { onClick: (event) => {
2746
- triggerElementRef.current = event.currentTarget;
2747
- children.props.onClick?.(event);
2748
- if (visible) closeNow();
2749
- else openNow();
2750
- } } : {},
2751
- "aria-describedby": visible ? contentId : void 0
2752
- });
2753
- const overlay = visible && content !== null && content !== void 0 && typeof document !== "undefined" ? (0, import_react_dom3.createPortal)(
2754
- /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
2755
- "span",
2756
- {
2757
- ref: overlayRef,
2758
- id: contentId,
2759
- role,
2760
- "data-custom-color": color ? "" : void 0,
2761
- className: `sia-floating__overlay sia-floating__overlay--${position?.placement ?? normalizeFloatingPlacement(placement)} ${overlayClassName}`.trim(),
2762
- style: {
2763
- "--sia-floating-color": color,
2764
- "--sia-floating-arrow-x": position?.arrowX == null ? void 0 : `${position.arrowX}px`,
2765
- "--sia-floating-arrow-y": position?.arrowY == null ? void 0 : `${position.arrowY}px`,
2766
- top: position?.top ?? 0,
2767
- left: position?.left ?? 0,
2768
- visibility: position && !position.hidden ? "visible" : "hidden"
2769
- },
2770
- onMouseEnter: triggers.includes("hover") ? () => show() : void 0,
2771
- onMouseLeave: triggers.includes("hover") ? () => hide(mouseLeaveDelay) : void 0,
2772
- children: [
2773
- content,
2774
- arrow ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "sia-floating__arrow" }) : null
2775
- ]
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
+ }
2776
2894
  }
2777
- ),
2778
- portalContainer ?? document.body
2779
- ) : null;
2780
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_jsx_runtime14.Fragment, { children: [
2781
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { ref: rootRef, className: `sia-floating ${className}`.trim(), children: child }),
2782
- overlay
2783
- ] });
2784
- }
2785
- function Tooltip(props) {
2786
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Floating, { ...props, content: props.title });
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;
2787
2935
  }
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
- ] }) });
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));
2793
2945
  }
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
- }) });
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;
2805
2958
  }
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
- });
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);
2811
2988
  return result;
2812
2989
  }
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
- }
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);
2878
3002
  }
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
3003
  }
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;
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;
2907
3046
  }
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);
3047
+ setVisible(true);
2930
3048
  }
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);
3049
+ function closeNow() {
3050
+ if (activeTooltipClose === closeSelfRef.current) activeTooltipClose = void 0;
3051
+ setVisible(false);
2940
3052
  }
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);
3053
+ function clearTimers() {
3054
+ window.clearTimeout(enterTimer.current);
3055
+ window.clearTimeout(leaveTimer.current);
2957
3056
  }
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);
3057
+ function show(delay = 0) {
3058
+ clearTimers();
3059
+ if (delay <= 0) openNow();
3060
+ else enterTimer.current = window.setTimeout(openNow, delay * 1e3);
2964
3061
  }
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
- });
3062
+ function hide(delay = 0) {
3063
+ clearTimers();
3064
+ if (delay <= 0) closeNow();
3065
+ else leaveTimer.current = window.setTimeout(closeNow, delay * 1e3);
3042
3066
  }
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
- };
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)(() => {
3077
+ if (!visible) {
3078
+ setPosition(void 0);
3079
+ return;
3101
3080
  }
3102
- return () => {
3103
- if (--users === 0) {
3104
- cleanup?.();
3105
- cleanup = void 0;
3081
+ const getAnchor = () => {
3082
+ const root = rootRef.current;
3083
+ const target = triggerElementRef.current;
3084
+ return target && root?.contains(target) ? target : root?.firstElementChild ?? root;
3085
+ };
3086
+ const update = () => {
3087
+ if (!rootRef.current || !overlayRef.current) return;
3088
+ const anchor2 = getAnchor();
3089
+ if (anchor2) {
3090
+ const rect = anchor2.getBoundingClientRect();
3091
+ const next = resolveFloatingPosition(rect, overlayRef.current.getBoundingClientRect(), placement, typeof arrow === "object" && !!arrow.pointAtCenter, autoAdjustOverflow);
3092
+ next.hidden = isFloatingAnchorHidden(anchor2, rect);
3093
+ setPosition(next);
3106
3094
  }
3107
3095
  };
3096
+ update();
3097
+ window.addEventListener("resize", update);
3098
+ window.addEventListener("scroll", update, true);
3099
+ window.addEventListener("animationend", update, true);
3100
+ window.addEventListener("transitionend", update, true);
3101
+ const observer = new ResizeObserver(update);
3102
+ if (rootRef.current) observer.observe(rootRef.current);
3103
+ const anchor = getAnchor();
3104
+ if (anchor) observer.observe(anchor);
3105
+ if (overlayRef.current) observer.observe(overlayRef.current);
3106
+ return () => {
3107
+ window.removeEventListener("resize", update);
3108
+ window.removeEventListener("scroll", update, true);
3109
+ window.removeEventListener("animationend", update, true);
3110
+ window.removeEventListener("transitionend", update, true);
3111
+ observer.disconnect();
3112
+ };
3113
+ }, [children, content, placement, visible, portalContainer, autoAdjustOverflow, typeof arrow === "object" && arrow.pointAtCenter]);
3114
+ (0, import_react16.useEffect)(() => () => {
3115
+ clearTimers();
3116
+ if (activeTooltipClose === closeSelfRef.current) activeTooltipClose = void 0;
3108
3117
  }, []);
3118
+ const child = (0, import_react16.cloneElement)(children, {
3119
+ ...triggers.includes("hover") ? { onMouseEnter: (event) => {
3120
+ triggerElementRef.current = event.currentTarget;
3121
+ children.props.onMouseEnter?.(event);
3122
+ show(mouseEnterDelay);
3123
+ }, onMouseLeave: (event) => {
3124
+ children.props.onMouseLeave?.(event);
3125
+ hide(mouseLeaveDelay);
3126
+ } } : {},
3127
+ ...triggers.includes("focus") ? { onFocus: (event) => {
3128
+ triggerElementRef.current = event.currentTarget;
3129
+ children.props.onFocus?.(event);
3130
+ show();
3131
+ }, onBlur: (event) => {
3132
+ children.props.onBlur?.(event);
3133
+ hide();
3134
+ } } : {},
3135
+ ...triggers.includes("click") ? { onClick: (event) => {
3136
+ triggerElementRef.current = event.currentTarget;
3137
+ children.props.onClick?.(event);
3138
+ if (visible) closeNow();
3139
+ else openNow();
3140
+ } } : {},
3141
+ "aria-describedby": visible ? contentId : void 0
3142
+ });
3143
+ const overlay = visible && content !== null && content !== void 0 && typeof document !== "undefined" ? (0, import_react_dom3.createPortal)(
3144
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
3145
+ "span",
3146
+ {
3147
+ ref: overlayRef,
3148
+ id: contentId,
3149
+ role,
3150
+ "data-custom-color": color ? "" : void 0,
3151
+ className: `sia-floating__overlay sia-floating__overlay--${position?.placement ?? normalizeFloatingPlacement(placement)} ${overlayClassName}`.trim(),
3152
+ style: {
3153
+ "--sia-floating-color": color,
3154
+ "--sia-floating-arrow-x": position?.arrowX == null ? void 0 : `${position.arrowX}px`,
3155
+ "--sia-floating-arrow-y": position?.arrowY == null ? void 0 : `${position.arrowY}px`,
3156
+ top: position?.top ?? 0,
3157
+ left: position?.left ?? 0,
3158
+ visibility: position && !position.hidden ? "visible" : "hidden"
3159
+ },
3160
+ onMouseEnter: triggers.includes("hover") ? () => show() : void 0,
3161
+ onMouseLeave: triggers.includes("hover") ? () => hide(mouseLeaveDelay) : void 0,
3162
+ children: [
3163
+ content,
3164
+ arrow ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "sia-floating__arrow" }) : null
3165
+ ]
3166
+ }
3167
+ ),
3168
+ portalContainer ?? document.body
3169
+ ) : null;
3170
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_jsx_runtime14.Fragment, { children: [
3171
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { ref: rootRef, className: `sia-floating ${className}`.trim(), children: child }),
3172
+ overlay
3173
+ ] });
3109
3174
  }
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;
3175
+ function Tooltip(props) {
3176
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Floating, { ...props, content: props.title });
3116
3177
  }
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);
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
+ ] }) });
3126
3183
  }
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
- };
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
+ }) });
3162
3195
  }
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 };
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;
3172
3202
  }
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
- };
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;
3211
+ }
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
@@ -5112,6 +5216,7 @@ function Tour({ open = false, steps, current, defaultCurrent = 0, mask = true, c
5112
5216
  var import_react24 = require("react");
5113
5217
  var import_jsx_runtime22 = require("react/jsx-runtime");
5114
5218
  function Card({
5219
+ bordered = true,
5115
5220
  title,
5116
5221
  description,
5117
5222
  extra,
@@ -5163,7 +5268,7 @@ function Card({
5163
5268
  return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
5164
5269
  "section",
5165
5270
  {
5166
- className: `sia-card${isCollapsed ? " sia-card--collapsed" : ""}${selectable ? " sia-card--selectable" : ""}${selectable && isSelected ? " sia-card--selected" : ""}${hoverActions ? " sia-card--hover-actions" : ""} ${className}`.trim(),
5271
+ className: `sia-card${!bordered ? " sia-card--borderless" : ""}${isCollapsed ? " sia-card--collapsed" : ""}${selectable ? " sia-card--selectable" : ""}${selectable && isSelected ? " sia-card--selected" : ""}${hoverActions ? " sia-card--hover-actions" : ""} ${className}`.trim(),
5167
5272
  style: mergedStyle,
5168
5273
  role: selectable ? "checkbox" : void 0,
5169
5274
  "aria-checked": selectable ? isSelected : void 0,
@@ -6412,6 +6517,7 @@ function FormRoot({
6412
6517
  "data-required-mark": requiredMark,
6413
6518
  onSubmit: (event) => {
6414
6519
  event.preventDefault();
6520
+ event.stopPropagation();
6415
6521
  instance.submit();
6416
6522
  },
6417
6523
  children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("fieldset", { disabled, children })
@@ -8735,7 +8841,7 @@ function UploadItem({ file, files, props, actions }) {
8735
8841
  }
8736
8842
  (0, import_react37.useEffect)(() => setFailed(false), [source]);
8737
8843
  const node = /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)("div", { className: `sia-upload__item sia-upload__item--${file.status ?? "ready"}`, children: [
8738
- props.listType !== "text" ? /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(Button, { variant: "text", className: "sia-upload__thumbnail", "aria-label": `\u9884\u89C8\u7F29\u7565\u56FE ${file.name}`, onClick: actions.preview, disabled: !canPreview, children: source && !failed ? /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("img", { src: source, alt: file.name, onError: () => setFailed(true) }) : /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(Icon, { name: file.type?.startsWith("image/") ? "image" : "file", size: 28 }) }) : /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(Icon, { name: file.status === "done" ? "circle-check" : "file", size: 16 }),
8844
+ props.listType !== "text" ? /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(Button, { variant: "text", className: "sia-upload__thumbnail", "aria-label": `\u9884\u89C8\u7F29\u7565\u56FE ${file.name}`, onClick: actions.preview, disabled: !canPreview, children: source && !failed ? /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("img", { src: source, alt: file.name, onError: () => setFailed(true) }) : /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(Icon, { name: file.type?.startsWith("image/") ? "image" : "file", size: 28 }) }) : /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("span", { className: "sia-upload__file-icon", children: /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(Icon, { name: file.status === "done" ? "circle-check" : "file", size: 24 }) }),
8739
8845
  /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("span", { className: "sia-upload__name", ...withTitleTooltip({ title: file.relativePath || file.name }), children: file.url ? /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("a", { href: file.url, target: "_blank", rel: "noreferrer", children: file.name }) : file.name }),
8740
8846
  /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)("span", { className: "sia-upload__actions", children: [
8741
8847
  /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(Button, { variant: "text", title: "\u9884\u89C8", "aria-label": `\u9884\u89C8 ${file.name}`, disabled: !canPreview, onClick: actions.preview, children: /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(Icon, { name: "eye", size: 14 }) }),
@@ -8750,7 +8856,8 @@ function UploadItem({ file, files, props, actions }) {
8750
8856
  ] }),
8751
8857
  /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("span", { className: "sia-upload__progress", role: "progressbar", "aria-label": `${file.name} \u4E0A\u4F20\u8FDB\u5EA6`, "aria-valuemin": 0, "aria-valuemax": 100, "aria-valuenow": file.percent ?? 0, children: /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("span", { style: { width: `${file.percent ?? 0}%` } }) })
8752
8858
  ] }),
8753
- file.status === "error" && /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("span", { className: "sia-upload__error", children: "\u4E0A\u4F20\u5931\u8D25" })
8859
+ file.status === "error" && /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("span", { className: "sia-upload__error", children: "\u4E0A\u4F20\u5931\u8D25" }),
8860
+ props.listType === "text" && (file.status === void 0 || file.status === "ready" || file.status === "done") && /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("span", { className: "sia-upload__status", children: file.status === "done" ? "\u4E0A\u4F20\u5B8C\u6210" : "\u5DF2\u9009\u62E9" })
8754
8861
  ] });
8755
8862
  return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(import_jsx_runtime38.Fragment, { children: props.itemRender ? props.itemRender(node, file, files, actions) : node });
8756
8863
  }
@@ -9029,6 +9136,10 @@ var UploadRoot = (0, import_react37.forwardRef)(function UploadRoot2({
9029
9136
  }
9030
9137
  ),
9031
9138
  listType !== "picture-card" && trigger,
9139
+ showUploadList && listType === "text" && files.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)("div", { className: "sia-upload__list-heading", role: "status", children: [
9140
+ "\u5DF2\u9009\u6587\u4EF6 ",
9141
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("span", { className: "sia-upload__count", children: files.length })
9142
+ ] }),
9032
9143
  /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)("div", { className: "sia-upload__list", children: [
9033
9144
  showUploadList && files.map((file) => /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(UploadItem, { file, files, props: { listType, disabled, itemRender, onPreview, downloadRequest, accept }, actions: {
9034
9145
  upload: () => start(file),
@@ -9067,7 +9178,7 @@ var UploadDragger = (0, import_react37.forwardRef)(function UploadDragger2({ hin
9067
9178
  ] }) });
9068
9179
  });
9069
9180
  var Upload = Object.assign(UploadRoot, {
9070
- /** 可点击、键盘操作和拖拽的上传区域,ref 支持手动上传、取消及移除。 */
9181
+ /** 可点击、键盘操作和拖拽的上传区域,ref 支持手动上传、取消及移除;默认内容在文字或图片列表非空且可见时收紧高度,清空后恢复。 */
9071
9182
  Dragger: UploadDragger,
9072
9183
  /** beforeUpload 返回时移除该文件,不进行上传。 */
9073
9184
  LIST_IGNORE