@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/core.cjs CHANGED
@@ -1757,6 +1757,7 @@ function Toolbar({ items, onAction, disabled = false, size = "small", variant =
1757
1757
  var import_react9 = require("react");
1758
1758
  var import_jsx_runtime9 = require("react/jsx-runtime");
1759
1759
  function Card({
1760
+ bordered = true,
1760
1761
  title,
1761
1762
  description,
1762
1763
  extra,
@@ -1808,7 +1809,7 @@ function Card({
1808
1809
  return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
1809
1810
  "section",
1810
1811
  {
1811
- className: `sia-card${isCollapsed ? " sia-card--collapsed" : ""}${selectable ? " sia-card--selectable" : ""}${selectable && isSelected ? " sia-card--selected" : ""}${hoverActions ? " sia-card--hover-actions" : ""} ${className}`.trim(),
1812
+ 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(),
1812
1813
  style: mergedStyle,
1813
1814
  role: selectable ? "checkbox" : void 0,
1814
1815
  "aria-checked": selectable ? isSelected : void 0,
@@ -2167,12 +2168,12 @@ function Tabs({
2167
2168
  }
2168
2169
 
2169
2170
  // src/components/Feedback.tsx
2170
- var import_react14 = require("react");
2171
+ var import_react16 = require("react");
2171
2172
  var import_react_dom3 = require("react-dom");
2172
2173
  var import_client2 = require("react-dom/client");
2173
2174
 
2174
2175
  // src/components/FloatingDisplay.tsx
2175
- var import_react13 = require("react");
2176
+ var import_react15 = require("react");
2176
2177
  var import_react_dom2 = require("react-dom");
2177
2178
 
2178
2179
  // src/components/Checkbox.tsx
@@ -2261,1287 +2262,1285 @@ var Checkbox2 = Object.assign(CheckboxRoot, {
2261
2262
  Group: CheckboxGroup
2262
2263
  });
2263
2264
 
2264
- // src/components/FloatingDisplay.tsx
2265
- var import_jsx_runtime13 = require("react/jsx-runtime");
2266
- var FLOATING_GAP = 9;
2267
- var FLOATING_VIEWPORT_PADDING = 8;
2268
- var FLOATING_ARROW_EDGE_OFFSET = 16;
2269
- var activeTooltipClose;
2270
- function clampFloating(value, min, max) {
2271
- return Math.min(Math.max(value, min), Math.max(min, max));
2265
+ // src/components/FloatingScrollbarProvider.tsx
2266
+ var import_react14 = require("react");
2267
+
2268
+ // src/components/useScrollbarProximity.ts
2269
+ var import_react13 = require("react");
2270
+ var users = 0;
2271
+ var cleanup;
2272
+ function useScrollbarProximity() {
2273
+ (0, import_react13.useEffect)(() => {
2274
+ if (users++ === 0) {
2275
+ let frame = 0;
2276
+ let x = -Infinity;
2277
+ let y = -Infinity;
2278
+ const nearby = /* @__PURE__ */ new Set();
2279
+ const clear = () => {
2280
+ nearby.forEach((track) => track.removeAttribute("data-scrollbar-near"));
2281
+ nearby.clear();
2282
+ };
2283
+ const update = () => {
2284
+ frame = 0;
2285
+ const next = /* @__PURE__ */ new Set();
2286
+ document.querySelectorAll(".sia-scrollbar-track").forEach((track) => {
2287
+ const rect = track.getBoundingClientRect();
2288
+ const proximity = Number.parseFloat(getComputedStyle(track).getPropertyValue("--sia-scrollbar-proximity")) || 16;
2289
+ if (rect.width && rect.height && x >= rect.left - proximity && x <= rect.right + proximity && y >= rect.top - proximity && y <= rect.bottom + proximity) {
2290
+ next.add(track);
2291
+ if (!nearby.has(track)) track.setAttribute("data-scrollbar-near", "true");
2292
+ }
2293
+ });
2294
+ nearby.forEach((track) => {
2295
+ if (!next.has(track)) track.removeAttribute("data-scrollbar-near");
2296
+ });
2297
+ nearby.clear();
2298
+ next.forEach((track) => nearby.add(track));
2299
+ };
2300
+ const move = (event) => {
2301
+ if (event.pointerType === "touch") return;
2302
+ x = event.clientX;
2303
+ y = event.clientY;
2304
+ if (!frame) frame = requestAnimationFrame(update);
2305
+ };
2306
+ const leave = () => {
2307
+ x = y = -Infinity;
2308
+ clear();
2309
+ };
2310
+ document.addEventListener("pointermove", move, { passive: true });
2311
+ document.documentElement.addEventListener("pointerleave", leave);
2312
+ window.addEventListener("blur", leave);
2313
+ cleanup = () => {
2314
+ if (frame) cancelAnimationFrame(frame);
2315
+ document.removeEventListener("pointermove", move);
2316
+ document.documentElement.removeEventListener("pointerleave", leave);
2317
+ window.removeEventListener("blur", leave);
2318
+ clear();
2319
+ };
2320
+ }
2321
+ return () => {
2322
+ if (--users === 0) {
2323
+ cleanup?.();
2324
+ cleanup = void 0;
2325
+ }
2326
+ };
2327
+ }, []);
2272
2328
  }
2273
- function normalizeFloatingPlacement(placement) {
2274
- const aliases = {
2275
- topLeft: "top-start",
2276
- topRight: "top-end",
2277
- bottomLeft: "bottom-start",
2278
- bottomRight: "bottom-end",
2279
- leftTop: "left-start",
2280
- leftBottom: "left-end",
2281
- rightTop: "right-start",
2282
- rightBottom: "right-end"
2283
- };
2284
- return aliases[placement] ?? placement;
2329
+
2330
+ // src/components/FloatingScrollbarProvider.tsx
2331
+ var scrollableOverflow = /* @__PURE__ */ new Set(["auto", "scroll", "overlay"]);
2332
+ var clippingOverflow = /* @__PURE__ */ new Set(["auto", "scroll", "overlay", "hidden", "clip"]);
2333
+ function isRootScroller(target) {
2334
+ return target === document.scrollingElement || target === document.documentElement;
2285
2335
  }
2286
- function resolveFloatingPosition(trigger, overlay, placement, pointAtCenter, adjust) {
2287
- const viewportWidth = document.documentElement.clientWidth;
2288
- const viewportHeight = document.documentElement.clientHeight;
2289
- let resolvedPlacement = normalizeFloatingPlacement(placement);
2290
- const side = resolvedPlacement.split("-")[0];
2291
- const available = { top: trigger.top, bottom: viewportHeight - trigger.bottom, left: trigger.left, right: viewportWidth - trigger.right };
2292
- const opposite = { top: "bottom", bottom: "top", left: "right", right: "left" };
2293
- const preferred = side;
2294
- const other = opposite[preferred];
2295
- const needed = (side === "top" || side === "bottom" ? overlay.height : overlay.width) + FLOATING_GAP + FLOATING_VIEWPORT_PADDING;
2296
- if (adjust && available[preferred] < needed && available[other] > available[preferred]) {
2297
- resolvedPlacement = resolvedPlacement.replace(preferred, other);
2298
- }
2299
- const vertical = resolvedPlacement.startsWith("top") || resolvedPlacement.startsWith("bottom");
2300
- const start = resolvedPlacement.endsWith("-start");
2301
- const end = resolvedPlacement.endsWith("-end");
2302
- 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;
2303
- 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;
2304
- if (adjust) {
2305
- if (vertical) left = clampFloating(left, FLOATING_VIEWPORT_PADDING, viewportWidth - overlay.width - FLOATING_VIEWPORT_PADDING);
2306
- else top = clampFloating(top, FLOATING_VIEWPORT_PADDING, viewportHeight - overlay.height - FLOATING_VIEWPORT_PADDING);
2336
+ function canScroll(target, axis) {
2337
+ if (target.matches(".sia-table__viewport, [data-sia-native-scrollbar]")) return false;
2338
+ if (isRootScroller(target)) {
2339
+ return axis === "horizontal" ? target.scrollWidth > target.clientWidth + 1 : target.scrollHeight > target.clientHeight + 1;
2307
2340
  }
2308
- const result = { top, left, placement: resolvedPlacement };
2309
- const size = vertical ? trigger.width : trigger.height;
2310
- const anchorStart = vertical ? trigger.left : trigger.top;
2311
- const edge = Math.min(size / 2, FLOATING_ARROW_EDGE_OFFSET + 4);
2312
- const target = anchorStart + (pointAtCenter || !start && !end ? size / 2 : start ? edge : size - edge);
2313
- if (vertical) result.arrowX = clampFloating(target - left - 4, 8, overlay.width - 16);
2314
- else result.arrowY = clampFloating(target - top - 4, 8, overlay.height - 16);
2315
- return result;
2341
+ const style = getComputedStyle(target);
2342
+ const overflow = axis === "horizontal" ? style.overflowX : style.overflowY;
2343
+ const hasOverflow = axis === "horizontal" ? target.scrollWidth > target.clientWidth + 1 : target.scrollHeight > target.clientHeight + 1;
2344
+ return hasOverflow && scrollableOverflow.has(overflow);
2316
2345
  }
2317
- function isFloatingAnchorHidden(anchor, rect) {
2318
- let left = 0, top = 0, right = document.documentElement.clientWidth, bottom = document.documentElement.clientHeight;
2319
- for (let parent = anchor.parentElement; parent; parent = parent.parentElement) {
2320
- const css = getComputedStyle(parent);
2321
- const bounds = parent.getBoundingClientRect();
2322
- if (/(auto|scroll|hidden|clip)/.test(css.overflowX)) {
2323
- left = Math.max(left, bounds.left);
2324
- right = Math.min(right, bounds.right);
2325
- }
2326
- if (/(auto|scroll|hidden|clip)/.test(css.overflowY)) {
2327
- top = Math.max(top, bounds.top);
2328
- bottom = Math.min(bottom, bounds.bottom);
2329
- }
2346
+ function visibleRect(target) {
2347
+ if (isRootScroller(target)) {
2348
+ return { top: 0, right: window.innerWidth, bottom: window.innerHeight, left: 0, width: window.innerWidth, height: window.innerHeight };
2330
2349
  }
2331
- return rect.right <= left || rect.left >= right || rect.bottom <= top || rect.top >= bottom;
2332
- }
2333
- function Floating({
2334
- children,
2335
- content,
2336
- placement = "top",
2337
- trigger = "hover",
2338
- open,
2339
- defaultOpen = false,
2340
- mouseEnterDelay = 0.1,
2341
- mouseLeaveDelay = 0.1,
2342
- arrow = true,
2343
- autoAdjustOverflow = true,
2344
- color,
2345
- className = "",
2346
- overlayClassName = "",
2347
- role = "tooltip",
2348
- onOpenChange
2349
- }) {
2350
- const rootRef = (0, import_react13.useRef)(null);
2351
- const triggerElementRef = (0, import_react13.useRef)(null);
2352
- const overlayRef = (0, import_react13.useRef)(null);
2353
- const enterTimer = (0, import_react13.useRef)();
2354
- const leaveTimer = (0, import_react13.useRef)();
2355
- const contentId = (0, import_react13.useId)();
2356
- const triggers = Array.isArray(trigger) ? trigger : [trigger];
2357
- const [visible, setVisible] = useControllableState({ value: open, defaultValue: defaultOpen, onChange: onOpenChange });
2358
- const [position, setPosition] = (0, import_react13.useState)();
2359
- const [portalContainer, setPortalContainer] = (0, import_react13.useState)(null);
2360
- (0, import_react13.useLayoutEffect)(() => {
2361
- if (!visible) return;
2362
- setPortalContainer(rootRef.current?.closest(".sia-modal-root, .sia-drawer-root") ?? document.body);
2363
- });
2364
- const setVisibleRef = (0, import_react13.useRef)(setVisible);
2365
- setVisibleRef.current = setVisible;
2366
- const closeSelfRef = (0, import_react13.useRef)();
2367
- if (!closeSelfRef.current) closeSelfRef.current = () => setVisibleRef.current(false);
2368
- const exclusiveTooltip = role === "tooltip" && triggers.includes("hover");
2369
- function openNow() {
2370
- if (exclusiveTooltip && activeTooltipClose !== closeSelfRef.current) {
2371
- activeTooltipClose?.();
2372
- activeTooltipClose = closeSelfRef.current;
2350
+ const ownRect = target.getBoundingClientRect();
2351
+ let top = Math.max(0, ownRect.top);
2352
+ let right = Math.min(window.innerWidth, ownRect.right);
2353
+ let bottom = Math.min(window.innerHeight, ownRect.bottom);
2354
+ let left = Math.max(0, ownRect.left);
2355
+ let parent = target.parentElement;
2356
+ while (parent && parent !== document.body && parent !== document.documentElement) {
2357
+ const style = getComputedStyle(parent);
2358
+ const clipX = clippingOverflow.has(style.overflowX);
2359
+ const clipY = clippingOverflow.has(style.overflowY);
2360
+ if (clipX || clipY) {
2361
+ const rect = parent.getBoundingClientRect();
2362
+ if (clipX) {
2363
+ left = Math.max(left, rect.left);
2364
+ right = Math.min(right, rect.right);
2365
+ }
2366
+ if (clipY) {
2367
+ top = Math.max(top, rect.top);
2368
+ bottom = Math.min(bottom, rect.bottom);
2369
+ }
2373
2370
  }
2374
- setVisible(true);
2375
- }
2376
- function closeNow() {
2377
- if (activeTooltipClose === closeSelfRef.current) activeTooltipClose = void 0;
2378
- setVisible(false);
2379
- }
2380
- function clearTimers() {
2381
- window.clearTimeout(enterTimer.current);
2382
- window.clearTimeout(leaveTimer.current);
2383
- }
2384
- function show(delay = 0) {
2385
- clearTimers();
2386
- if (delay <= 0) openNow();
2387
- else enterTimer.current = window.setTimeout(openNow, delay * 1e3);
2388
- }
2389
- function hide(delay = 0) {
2390
- clearTimers();
2391
- if (delay <= 0) closeNow();
2392
- else leaveTimer.current = window.setTimeout(closeNow, delay * 1e3);
2371
+ parent = parent.parentElement;
2393
2372
  }
2394
- (0, import_react13.useEffect)(() => {
2395
- if (!visible || !triggers.includes("click")) return;
2396
- const close = (event) => {
2397
- const target = event.target;
2398
- if (!rootRef.current?.contains(target) && !overlayRef.current?.contains(target)) closeNow();
2373
+ return {
2374
+ top,
2375
+ right,
2376
+ bottom,
2377
+ left,
2378
+ width: Math.max(0, right - left),
2379
+ height: Math.max(0, bottom - top)
2380
+ };
2381
+ }
2382
+ function createTrack(axis) {
2383
+ const track = document.createElement("div");
2384
+ const thumb = document.createElement("div");
2385
+ track.className = `sia-scrollbar-track sia-floating-scrollbar sia-floating-scrollbar--${axis}`;
2386
+ thumb.className = "sia-floating-scrollbar__thumb";
2387
+ track.dataset.axis = axis;
2388
+ track.setAttribute("aria-hidden", "true");
2389
+ track.append(thumb);
2390
+ return { track, thumb };
2391
+ }
2392
+ function FloatingScrollbarProvider({ disabled = false, targetRef }) {
2393
+ useScrollbarProximity();
2394
+ (0, import_react14.useEffect)(() => {
2395
+ if (disabled) return;
2396
+ const layer = document.createElement("div");
2397
+ layer.className = "sia-floating-scrollbar-layer";
2398
+ layer.setAttribute("aria-hidden", "true");
2399
+ document.body.append(layer);
2400
+ const entries = /* @__PURE__ */ new Map();
2401
+ let scanFrame = 0;
2402
+ let updateFrame = 0;
2403
+ const setEntryVisible = (entry, visible) => {
2404
+ entry.horizontalTrack.classList.toggle("sia-floating-scrollbar--visible", visible);
2405
+ entry.verticalTrack.classList.toggle("sia-floating-scrollbar--visible", visible);
2399
2406
  };
2400
- document.addEventListener("pointerdown", close);
2401
- return () => document.removeEventListener("pointerdown", close);
2402
- }, [triggers.join("|"), visible]);
2403
- (0, import_react13.useLayoutEffect)(() => {
2404
- if (!visible) {
2405
- setPosition(void 0);
2406
- return;
2407
- }
2408
- const getAnchor = () => {
2409
- const root = rootRef.current;
2410
- const target = triggerElementRef.current;
2411
- return target && root?.contains(target) ? target : root?.firstElementChild ?? root;
2412
- };
2413
- const update = () => {
2414
- if (!rootRef.current || !overlayRef.current) return;
2415
- const anchor2 = getAnchor();
2416
- if (anchor2) {
2417
- const rect = anchor2.getBoundingClientRect();
2418
- const next = resolveFloatingPosition(rect, overlayRef.current.getBoundingClientRect(), placement, typeof arrow === "object" && !!arrow.pointAtCenter, autoAdjustOverflow);
2419
- next.hidden = isFloatingAnchorHidden(anchor2, rect);
2420
- setPosition(next);
2421
- }
2407
+ const showEntry = (entry) => {
2408
+ if (entry.hideTimer !== null) window.clearTimeout(entry.hideTimer);
2409
+ entry.hideTimer = null;
2410
+ setEntryVisible(entry, true);
2422
2411
  };
2423
- update();
2424
- window.addEventListener("resize", update);
2425
- window.addEventListener("scroll", update, true);
2426
- window.addEventListener("animationend", update, true);
2427
- window.addEventListener("transitionend", update, true);
2428
- const observer = new ResizeObserver(update);
2429
- if (rootRef.current) observer.observe(rootRef.current);
2430
- const anchor = getAnchor();
2431
- if (anchor) observer.observe(anchor);
2432
- if (overlayRef.current) observer.observe(overlayRef.current);
2433
- return () => {
2434
- window.removeEventListener("resize", update);
2435
- window.removeEventListener("scroll", update, true);
2436
- window.removeEventListener("animationend", update, true);
2437
- window.removeEventListener("transitionend", update, true);
2438
- observer.disconnect();
2412
+ const scheduleHide = (entry, delay = 500) => {
2413
+ if (entry.hideTimer !== null) window.clearTimeout(entry.hideTimer);
2414
+ entry.hideTimer = window.setTimeout(() => {
2415
+ entry.hideTimer = null;
2416
+ if (!entry.hoveringTarget && !entry.hoveringTrack && !entry.dragging) setEntryVisible(entry, false);
2417
+ }, delay);
2439
2418
  };
2440
- }, [children, content, placement, visible, portalContainer, autoAdjustOverflow, typeof arrow === "object" && arrow.pointAtCenter]);
2441
- (0, import_react13.useEffect)(() => () => {
2442
- clearTimers();
2443
- if (activeTooltipClose === closeSelfRef.current) activeTooltipClose = void 0;
2444
- }, []);
2445
- const child = (0, import_react13.cloneElement)(children, {
2446
- ...triggers.includes("hover") ? { onMouseEnter: (event) => {
2447
- triggerElementRef.current = event.currentTarget;
2448
- children.props.onMouseEnter?.(event);
2449
- show(mouseEnterDelay);
2450
- }, onMouseLeave: (event) => {
2451
- children.props.onMouseLeave?.(event);
2452
- hide(mouseLeaveDelay);
2453
- } } : {},
2454
- ...triggers.includes("focus") ? { onFocus: (event) => {
2455
- triggerElementRef.current = event.currentTarget;
2456
- children.props.onFocus?.(event);
2457
- show();
2458
- }, onBlur: (event) => {
2459
- children.props.onBlur?.(event);
2460
- hide();
2461
- } } : {},
2462
- ...triggers.includes("click") ? { onClick: (event) => {
2463
- triggerElementRef.current = event.currentTarget;
2464
- children.props.onClick?.(event);
2465
- if (visible) closeNow();
2466
- else openNow();
2467
- } } : {},
2468
- "aria-describedby": visible ? contentId : void 0
2469
- });
2470
- const overlay = visible && content !== null && content !== void 0 && typeof document !== "undefined" ? (0, import_react_dom2.createPortal)(
2471
- /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
2472
- "span",
2473
- {
2474
- ref: overlayRef,
2475
- id: contentId,
2476
- role,
2477
- "data-custom-color": color ? "" : void 0,
2478
- className: `sia-floating__overlay sia-floating__overlay--${position?.placement ?? normalizeFloatingPlacement(placement)} ${overlayClassName}`.trim(),
2479
- style: {
2480
- "--sia-floating-color": color,
2481
- "--sia-floating-arrow-x": position?.arrowX == null ? void 0 : `${position.arrowX}px`,
2482
- "--sia-floating-arrow-y": position?.arrowY == null ? void 0 : `${position.arrowY}px`,
2483
- top: position?.top ?? 0,
2484
- left: position?.left ?? 0,
2485
- visibility: position && !position.hidden ? "visible" : "hidden"
2486
- },
2487
- onMouseEnter: triggers.includes("hover") ? () => show() : void 0,
2488
- onMouseLeave: triggers.includes("hover") ? () => hide(mouseLeaveDelay) : void 0,
2489
- children: [
2490
- content,
2491
- arrow ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "sia-floating__arrow" }) : null
2492
- ]
2493
- }
2494
- ),
2495
- portalContainer ?? document.body
2496
- ) : null;
2497
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_jsx_runtime13.Fragment, { children: [
2498
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { ref: rootRef, className: `sia-floating ${className}`.trim(), children: child }),
2499
- overlay
2500
- ] });
2501
- }
2502
- function Tooltip(props) {
2503
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Floating, { ...props, content: props.title });
2504
- }
2505
-
2506
- // src/components/Feedback.tsx
2507
- var import_jsx_runtime14 = require("react/jsx-runtime");
2508
- function Progress({
2509
- percent = 0,
2510
- type = "line",
2511
- status,
2512
- showInfo = true,
2513
- format,
2514
- strokeColor,
2515
- trailColor,
2516
- strokeWidth = 8,
2517
- width = 120,
2518
- steps,
2519
- success,
2520
- size = "default",
2521
- className = "",
2522
- style,
2523
- ...props
2524
- }) {
2525
- const safe = Math.max(0, Math.min(100, percent));
2526
- const resolved = status ?? (safe >= 100 ? "success" : "normal");
2527
- const color = typeof strokeColor === "string" ? strokeColor : void 0;
2528
- const background = typeof strokeColor === "object" ? `linear-gradient(90deg, ${strokeColor.from}, ${strokeColor.to})` : color;
2529
- const label = format?.(safe, success?.percent) ?? (resolved === "exception" ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Icon, { name: "close" }) : resolved === "success" ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Icon, { name: "check" }) : `${safe}%`);
2530
- if (type !== "line") {
2531
- const radius = 46;
2532
- const circumference = 2 * Math.PI * radius;
2533
- const dash = circumference * (safe / 100);
2534
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: `sia-progress sia-progress--${type} sia-progress--${resolved} ${className}`.trim(), style: { width, height: width, ...style }, ...withTitleTooltip(props), children: [
2535
- /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("svg", { viewBox: "0 0 100 100", role: "progressbar", "aria-valuenow": safe, children: [
2536
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("circle", { className: "sia-progress__trail", cx: "50", cy: "50", r: radius, style: { stroke: trailColor } }),
2537
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("circle", { className: "sia-progress__circle", cx: "50", cy: "50", r: radius, style: { stroke: color, strokeDasharray: type === "dashboard" ? `${circumference * 0.75} ${circumference * 0.25}` : circumference, strokeDashoffset: (type === "dashboard" ? circumference * 0.75 : circumference) - dash * (type === "dashboard" ? 0.75 : 1), strokeWidth } })
2538
- ] }),
2539
- showInfo ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "sia-progress__text", children: label }) : null
2540
- ] });
2541
- }
2542
- const height = Array.isArray(size) ? size[1] : typeof size === "number" ? size : size === "small" ? 6 : strokeWidth;
2543
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: `sia-progress sia-progress--line sia-progress--${resolved} ${className}`.trim(), style, ...withTitleTooltip(props), children: [
2544
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "sia-progress__outer", style: { height, backgroundColor: trailColor }, children: steps ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "sia-progress__steps", children: Array.from({ length: steps }, (_, index) => /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: index < Math.round(steps * safe / 100) ? "is-active" : "", style: { background: index < Math.round(steps * safe / 100) ? background : trailColor } }, index)) }) : /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_jsx_runtime14.Fragment, { children: [
2545
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "sia-progress__bar", style: { width: `${safe}%`, background } }),
2546
- success?.percent ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "sia-progress__success", style: { width: `${success.percent}%`, background: success.strokeColor } }) : null
2547
- ] }) }),
2548
- showInfo ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "sia-progress__info", children: label }) : null
2549
- ] });
2550
- }
2551
-
2552
- // src/components/DataDisplay.tsx
2553
- var import_react16 = require("react");
2554
- var import_react_dom5 = require("react-dom");
2555
-
2556
- // src/components/Overlay.tsx
2557
- var import_react15 = require("react");
2558
- var import_react_dom4 = require("react-dom");
2559
- var bodyLockCount = 0;
2560
- var previousBodyOverflow = "";
2561
- var overlayStack = [];
2562
- function lockBodyScroll() {
2563
- if (bodyLockCount === 0) {
2564
- previousBodyOverflow = document.body.style.overflow;
2565
- document.body.style.overflow = "hidden";
2566
- }
2567
- bodyLockCount += 1;
2568
- return () => {
2569
- bodyLockCount = Math.max(0, bodyLockCount - 1);
2570
- if (bodyLockCount === 0) document.body.style.overflow = previousBodyOverflow;
2571
- };
2572
- }
2573
- function focusableElements(container) {
2574
- return [...container.querySelectorAll(
2575
- 'a[href], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])'
2576
- )].filter((element) => !element.hidden && element.getAttribute("aria-hidden") !== "true");
2577
- }
2578
- function useOverlayLifecycle(open, panelRef, onRequestClose, keyboard = true) {
2579
- const overlayIdRef = (0, import_react15.useRef)(/* @__PURE__ */ Symbol("sia-overlay"));
2580
- const closeRef = (0, import_react15.useRef)(onRequestClose);
2581
- closeRef.current = onRequestClose;
2582
- (0, import_react15.useEffect)(() => {
2583
- if (!open || typeof document === "undefined") return void 0;
2584
- const overlayId = overlayIdRef.current;
2585
- const previouslyFocused = document.activeElement instanceof HTMLElement ? document.activeElement : null;
2586
- overlayStack.push(overlayId);
2587
- const unlockBody = lockBodyScroll();
2588
- const focusTimer = window.setTimeout(() => {
2589
- const panel = panelRef.current;
2590
- if (!panel) return;
2591
- const [firstFocusable] = focusableElements(panel);
2592
- (firstFocusable ?? panel).focus({ preventScroll: true });
2593
- }, 0);
2594
- function handleKeyDown(event) {
2595
- if (overlayStack.at(-1) !== overlayId) return;
2596
- if (event.key === "Escape" && keyboard) {
2597
- event.preventDefault();
2598
- closeRef.current();
2599
- return;
2600
- }
2601
- if (event.key !== "Tab" || !panelRef.current) return;
2602
- const focusable = focusableElements(panelRef.current);
2603
- if (focusable.length === 0) {
2604
- event.preventDefault();
2605
- panelRef.current.focus();
2606
- return;
2419
+ const updateEntry = (entry) => {
2420
+ const { target, horizontalTrack, horizontalThumb, verticalTrack, verticalThumb } = entry;
2421
+ const hasHorizontal = canScroll(target, "horizontal");
2422
+ const hasVertical = canScroll(target, "vertical");
2423
+ const rect = visibleRect(target);
2424
+ const thickness = 10;
2425
+ horizontalTrack.hidden = !hasHorizontal || rect.width <= thickness || rect.height <= 0;
2426
+ verticalTrack.hidden = !hasVertical || rect.height <= thickness || rect.width <= 0;
2427
+ if (!horizontalTrack.hidden) {
2428
+ const trackWidth = Math.max(0, rect.width - (hasVertical ? thickness : 0));
2429
+ const thumbWidth = Math.max(24, Math.min(trackWidth, trackWidth * target.clientWidth / target.scrollWidth));
2430
+ const travel = Math.max(0, trackWidth - thumbWidth);
2431
+ const maxScroll = Math.max(1, target.scrollWidth - target.clientWidth);
2432
+ horizontalTrack.style.left = `${rect.left}px`;
2433
+ horizontalTrack.style.top = `${rect.bottom - thickness}px`;
2434
+ horizontalTrack.style.width = `${trackWidth}px`;
2435
+ horizontalTrack.style.height = `${thickness}px`;
2436
+ horizontalThumb.style.width = `${thumbWidth}px`;
2437
+ horizontalThumb.style.transform = `translate3d(${travel * target.scrollLeft / maxScroll}px, 0, 0)`;
2607
2438
  }
2608
- const first = focusable[0];
2609
- const last = focusable.at(-1);
2610
- if (event.shiftKey && document.activeElement === first) {
2611
- event.preventDefault();
2612
- last.focus();
2613
- } else if (!event.shiftKey && document.activeElement === last) {
2614
- event.preventDefault();
2615
- first.focus();
2439
+ if (!verticalTrack.hidden) {
2440
+ const trackHeight = Math.max(0, rect.height - (hasHorizontal ? thickness : 0));
2441
+ const thumbHeight = Math.max(24, Math.min(trackHeight, trackHeight * target.clientHeight / target.scrollHeight));
2442
+ const travel = Math.max(0, trackHeight - thumbHeight);
2443
+ const maxScroll = Math.max(1, target.scrollHeight - target.clientHeight);
2444
+ verticalTrack.style.left = `${rect.right - thickness}px`;
2445
+ verticalTrack.style.top = `${rect.top}px`;
2446
+ verticalTrack.style.width = `${thickness}px`;
2447
+ verticalTrack.style.height = `${trackHeight}px`;
2448
+ verticalThumb.style.height = `${thumbHeight}px`;
2449
+ verticalThumb.style.transform = `translate3d(0, ${travel * target.scrollTop / maxScroll}px, 0)`;
2616
2450
  }
2617
- }
2618
- document.addEventListener("keydown", handleKeyDown);
2619
- return () => {
2620
- window.clearTimeout(focusTimer);
2621
- document.removeEventListener("keydown", handleKeyDown);
2622
- const index = overlayStack.lastIndexOf(overlayId);
2623
- if (index >= 0) overlayStack.splice(index, 1);
2624
- unlockBody();
2625
- previouslyFocused?.focus({ preventScroll: true });
2626
2451
  };
2627
- }, [keyboard, open, panelRef]);
2628
- }
2629
- function OverlayPortal({ children, container }) {
2630
- if (typeof document === "undefined") return null;
2631
- const target = typeof container === "function" ? container() : container ?? document.body;
2632
- return (0, import_react_dom4.createPortal)(children, target);
2452
+ const scheduleUpdateAll = () => {
2453
+ if (updateFrame) return;
2454
+ updateFrame = window.requestAnimationFrame(() => {
2455
+ updateFrame = 0;
2456
+ entries.forEach(updateEntry);
2457
+ });
2458
+ };
2459
+ const bindTrackDrag = (entry, axis, track, thumb) => {
2460
+ const target = entry.target;
2461
+ const onTrackPointerEnter = () => {
2462
+ entry.hoveringTrack = true;
2463
+ showEntry(entry);
2464
+ };
2465
+ const onTrackPointerLeave = () => {
2466
+ entry.hoveringTrack = false;
2467
+ scheduleHide(entry);
2468
+ };
2469
+ const onTrackPointerDown = (event) => {
2470
+ if (event.target === thumb || event.button !== 0) return;
2471
+ event.preventDefault();
2472
+ event.stopPropagation();
2473
+ const trackRect = track.getBoundingClientRect();
2474
+ const thumbRect = thumb.getBoundingClientRect();
2475
+ const trackLength = axis === "vertical" ? trackRect.height : trackRect.width;
2476
+ const thumbLength = axis === "vertical" ? thumbRect.height : thumbRect.width;
2477
+ const pointer = axis === "vertical" ? event.clientY - trackRect.top : event.clientX - trackRect.left;
2478
+ const ratio = Math.max(0, Math.min(1, (pointer - thumbLength / 2) / Math.max(1, trackLength - thumbLength)));
2479
+ if (axis === "vertical") target.scrollTop = ratio * (target.scrollHeight - target.clientHeight);
2480
+ else target.scrollLeft = ratio * (target.scrollWidth - target.clientWidth);
2481
+ };
2482
+ const onThumbPointerDown = (event) => {
2483
+ if (event.button !== 0) return;
2484
+ event.preventDefault();
2485
+ event.stopPropagation();
2486
+ entry.dragging = true;
2487
+ showEntry(entry);
2488
+ thumb.setPointerCapture(event.pointerId);
2489
+ const startPointer = axis === "vertical" ? event.clientY : event.clientX;
2490
+ const startScroll = axis === "vertical" ? target.scrollTop : target.scrollLeft;
2491
+ const trackRect = track.getBoundingClientRect();
2492
+ const thumbRect = thumb.getBoundingClientRect();
2493
+ const trackLength = axis === "vertical" ? trackRect.height : trackRect.width;
2494
+ const thumbLength = axis === "vertical" ? thumbRect.height : thumbRect.width;
2495
+ const maxScroll = axis === "vertical" ? target.scrollHeight - target.clientHeight : target.scrollWidth - target.clientWidth;
2496
+ const travel = Math.max(1, trackLength - thumbLength);
2497
+ const onPointerMove = (moveEvent) => {
2498
+ if (moveEvent.pointerId !== event.pointerId) return;
2499
+ const pointer = axis === "vertical" ? moveEvent.clientY : moveEvent.clientX;
2500
+ const nextScroll = startScroll + (pointer - startPointer) * maxScroll / travel;
2501
+ if (axis === "vertical") target.scrollTop = nextScroll;
2502
+ else target.scrollLeft = nextScroll;
2503
+ };
2504
+ const onPointerUp = (upEvent) => {
2505
+ if (upEvent.pointerId !== event.pointerId) return;
2506
+ entry.dragging = false;
2507
+ thumb.releasePointerCapture(event.pointerId);
2508
+ window.removeEventListener("pointermove", onPointerMove);
2509
+ window.removeEventListener("pointerup", onPointerUp);
2510
+ window.removeEventListener("pointercancel", onPointerUp);
2511
+ scheduleHide(entry);
2512
+ };
2513
+ window.addEventListener("pointermove", onPointerMove);
2514
+ window.addEventListener("pointerup", onPointerUp);
2515
+ window.addEventListener("pointercancel", onPointerUp);
2516
+ };
2517
+ track.addEventListener("pointerenter", onTrackPointerEnter);
2518
+ track.addEventListener("pointerleave", onTrackPointerLeave);
2519
+ track.addEventListener("pointerdown", onTrackPointerDown);
2520
+ thumb.addEventListener("pointerdown", onThumbPointerDown);
2521
+ return () => {
2522
+ track.removeEventListener("pointerenter", onTrackPointerEnter);
2523
+ track.removeEventListener("pointerleave", onTrackPointerLeave);
2524
+ track.removeEventListener("pointerdown", onTrackPointerDown);
2525
+ thumb.removeEventListener("pointerdown", onThumbPointerDown);
2526
+ };
2527
+ };
2528
+ const resizeObserver = new ResizeObserver((observedEntries) => {
2529
+ observedEntries.forEach(({ target }) => {
2530
+ const entry = entries.get(target);
2531
+ if (entry) updateEntry(entry);
2532
+ });
2533
+ });
2534
+ const addTarget = (target) => {
2535
+ if (entries.has(target)) return;
2536
+ const horizontal = createTrack("horizontal");
2537
+ const vertical = createTrack("vertical");
2538
+ layer.append(horizontal.track, vertical.track);
2539
+ const onPointerEnter = () => {
2540
+ const entry2 = entries.get(target);
2541
+ if (!entry2) return;
2542
+ entry2.hoveringTarget = true;
2543
+ showEntry(entry2);
2544
+ };
2545
+ const onPointerLeave = () => {
2546
+ const entry2 = entries.get(target);
2547
+ if (!entry2) return;
2548
+ entry2.hoveringTarget = false;
2549
+ scheduleHide(entry2);
2550
+ };
2551
+ const onScroll = () => {
2552
+ const entry2 = entries.get(target);
2553
+ if (!entry2) return;
2554
+ updateEntry(entry2);
2555
+ showEntry(entry2);
2556
+ scheduleHide(entry2, 700);
2557
+ };
2558
+ target.addEventListener("pointerenter", onPointerEnter);
2559
+ target.addEventListener("pointerleave", onPointerLeave);
2560
+ target.addEventListener("scroll", onScroll, { passive: true });
2561
+ const entry = {
2562
+ target,
2563
+ horizontalTrack: horizontal.track,
2564
+ horizontalThumb: horizontal.thumb,
2565
+ verticalTrack: vertical.track,
2566
+ verticalThumb: vertical.thumb,
2567
+ hideTimer: null,
2568
+ hoveringTarget: target.matches(":hover"),
2569
+ hoveringTrack: false,
2570
+ dragging: false,
2571
+ cleanup: () => void 0
2572
+ };
2573
+ const cleanupHorizontal = bindTrackDrag(entry, "horizontal", horizontal.track, horizontal.thumb);
2574
+ const cleanupVertical = bindTrackDrag(entry, "vertical", vertical.track, vertical.thumb);
2575
+ entry.cleanup = () => {
2576
+ if (entry.hideTimer !== null) window.clearTimeout(entry.hideTimer);
2577
+ cleanupHorizontal();
2578
+ cleanupVertical();
2579
+ target.removeEventListener("pointerenter", onPointerEnter);
2580
+ target.removeEventListener("pointerleave", onPointerLeave);
2581
+ target.removeEventListener("scroll", onScroll);
2582
+ resizeObserver.unobserve(target);
2583
+ target.removeAttribute("data-sia-floating-scrollbar");
2584
+ horizontal.track.remove();
2585
+ vertical.track.remove();
2586
+ };
2587
+ entries.set(target, entry);
2588
+ target.setAttribute("data-sia-floating-scrollbar", "");
2589
+ resizeObserver.observe(target);
2590
+ updateEntry(entry);
2591
+ if (entry.hoveringTarget) showEntry(entry);
2592
+ };
2593
+ const removeTarget = (target) => {
2594
+ const entry = entries.get(target);
2595
+ if (!entry) return;
2596
+ entry.cleanup();
2597
+ entries.delete(target);
2598
+ };
2599
+ const scanTargets = () => {
2600
+ scanFrame = 0;
2601
+ const managedSelector = ".sia-table__viewport, [data-sia-native-scrollbar], .sia-scrollbar-track, .sia-floating-scrollbar-layer";
2602
+ const discovered = [];
2603
+ if (!targetRef) {
2604
+ const walker = document.createTreeWalker(document.body, NodeFilter.SHOW_ELEMENT, {
2605
+ acceptNode: (node) => node.matches(managedSelector) ? NodeFilter.FILTER_REJECT : NodeFilter.FILTER_ACCEPT
2606
+ });
2607
+ for (let node = walker.nextNode(); node; node = walker.nextNode()) {
2608
+ if (node instanceof HTMLElement) discovered.push(node);
2609
+ }
2610
+ }
2611
+ const candidates = (targetRef ? [targetRef.current] : [document.scrollingElement, ...discovered]).filter((target) => target instanceof HTMLElement);
2612
+ const nextTargets = /* @__PURE__ */ new Set();
2613
+ candidates.forEach((target) => {
2614
+ if (canScroll(target, "horizontal") || canScroll(target, "vertical")) {
2615
+ nextTargets.add(target);
2616
+ if (entries.has(target) || !target.hasAttribute("data-sia-floating-scrollbar")) addTarget(target);
2617
+ }
2618
+ });
2619
+ entries.forEach((_entry, target) => {
2620
+ if (!target.isConnected || !nextTargets.has(target)) removeTarget(target);
2621
+ else updateEntry(_entry);
2622
+ });
2623
+ };
2624
+ const scheduleScan = () => {
2625
+ if (scanFrame) return;
2626
+ scanFrame = window.requestAnimationFrame(scanTargets);
2627
+ };
2628
+ const mutationObserver = new MutationObserver((mutations) => {
2629
+ if (mutations.some((mutation) => {
2630
+ const target = mutation.target instanceof Element ? mutation.target : mutation.target.parentElement;
2631
+ return target && !layer.contains(target) && !target.closest(".sia-table__viewport, [data-sia-native-scrollbar], .sia-scrollbar-track");
2632
+ })) scheduleScan();
2633
+ });
2634
+ mutationObserver.observe(document.documentElement, { subtree: true, childList: true, attributes: true, attributeFilter: ["class", "style", "hidden"] });
2635
+ window.addEventListener("resize", scheduleScan);
2636
+ window.addEventListener("scroll", scheduleUpdateAll, true);
2637
+ scheduleScan();
2638
+ return () => {
2639
+ mutationObserver.disconnect();
2640
+ resizeObserver.disconnect();
2641
+ window.removeEventListener("resize", scheduleScan);
2642
+ window.removeEventListener("scroll", scheduleUpdateAll, true);
2643
+ if (scanFrame) window.cancelAnimationFrame(scanFrame);
2644
+ if (updateFrame) window.cancelAnimationFrame(updateFrame);
2645
+ entries.forEach((entry) => entry.cleanup());
2646
+ entries.clear();
2647
+ layer.remove();
2648
+ };
2649
+ }, [disabled, targetRef]);
2650
+ return null;
2633
2651
  }
2634
2652
 
2635
- // src/components/DataDisplay.tsx
2636
- var import_jsx_runtime15 = require("react/jsx-runtime");
2637
- function BadgeRibbon({ text, color, placement = "end", className = "", children, ...props }) {
2638
- return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: `sia-ribbon-wrap ${className}`.trim(), ...withTitleTooltip(props), children: [
2639
- children,
2640
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: `sia-ribbon sia-ribbon--${placement}`, style: { backgroundColor: color }, children: text })
2641
- ] });
2653
+ // src/components/FloatingDisplay.tsx
2654
+ var import_jsx_runtime13 = require("react/jsx-runtime");
2655
+ var FLOATING_GAP = 9;
2656
+ var FLOATING_VIEWPORT_PADDING = 8;
2657
+ var FLOATING_ARROW_EDGE_OFFSET = 16;
2658
+ var activeTooltipClose;
2659
+ function clampFloating(value, min, max) {
2660
+ return Math.min(Math.max(value, min), Math.max(min, max));
2642
2661
  }
2643
- function Badge({ count, showZero = false, overflowCount = 99, dot = false, status, text, color, offset = [0, 0], size = "default", className = "", children, style, ...props }) {
2644
- const numeric = typeof count === "number";
2645
- const hidden = !dot && !status && (count === void 0 || count === null || count === 0 && !showZero);
2646
- const display = numeric && count > overflowCount ? `${overflowCount}+` : count;
2647
- const badge = !hidden ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("sup", { className: `sia-badge__count${dot || status ? " sia-badge__dot" : ""}${status ? ` sia-badge__dot--${status}` : ""} sia-badge__count--${size}`, style: { backgroundColor: color, transform: `translate(calc(50% + ${offset[0]}px), calc(-50% + ${offset[1]}px))` }, children: dot || status ? null : display }) : null;
2648
- if (status && !children) return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("span", { className: `sia-badge sia-badge--status ${className}`.trim(), style, ...withTitleTooltip(props), children: [
2649
- badge,
2650
- text ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "sia-badge__text", children: text }) : null
2651
- ] });
2652
- return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("span", { className: `sia-badge ${className}`.trim(), style, ...withTitleTooltip(props), children: [
2653
- children,
2654
- badge,
2655
- text && !status ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "sia-badge__text", children: text }) : null
2656
- ] });
2662
+ function normalizeFloatingPlacement(placement) {
2663
+ const aliases = {
2664
+ topLeft: "top-start",
2665
+ topRight: "top-end",
2666
+ bottomLeft: "bottom-start",
2667
+ bottomRight: "bottom-end",
2668
+ leftTop: "left-start",
2669
+ leftBottom: "left-end",
2670
+ rightTop: "right-start",
2671
+ rightBottom: "right-end"
2672
+ };
2673
+ return aliases[placement] ?? placement;
2657
2674
  }
2658
- Badge.Ribbon = BadgeRibbon;
2659
- function CollapsePanel({
2660
- open,
2661
- forceRender = false,
2662
- destroyInactivePanel = false,
2663
- children
2664
- }) {
2665
- const [keepChildren, setKeepChildren] = (0, import_react16.useState)(open || forceRender || !destroyInactivePanel);
2666
- (0, import_react16.useEffect)(() => {
2667
- if (open || forceRender || !destroyInactivePanel) {
2668
- setKeepChildren(true);
2669
- return void 0;
2670
- }
2671
- const timer = window.setTimeout(() => setKeepChildren(false), 240);
2672
- return () => window.clearTimeout(timer);
2673
- }, [destroyInactivePanel, forceRender, open]);
2674
- const renderChildren = open || forceRender || !destroyInactivePanel || keepChildren;
2675
- return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "sia-collapse__panel", "aria-hidden": !open, children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "sia-collapse__panel-motion", children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "sia-collapse__panel-body", children: renderChildren ? children : null }) }) });
2676
- }
2677
- function Collapse({ items, activeKey, defaultActiveKey = [], accordion = false, bordered = true, ghost = false, destroyInactivePanel = false, expandIconPosition = "start", onChange, className = "", ...props }) {
2678
- const normalize = (key) => Array.isArray(key) ? [...key] : key === void 0 ? [] : [key];
2679
- const controlled = activeKey !== void 0;
2680
- const [internal, setInternal] = (0, import_react16.useState)(normalize(defaultActiveKey));
2681
- const openKeys = controlled ? normalize(activeKey) : internal;
2682
- function toggle(item) {
2683
- if (item.disabled || item.collapsible === "disabled") return;
2684
- const open = openKeys.includes(item.key);
2685
- const next = accordion ? open ? [] : [item.key] : open ? openKeys.filter((key) => key !== item.key) : [...openKeys, item.key];
2686
- if (!controlled) setInternal(next);
2687
- onChange?.(accordion ? next[0] ?? "" : next);
2675
+ function resolveFloatingPosition(trigger, overlay, placement, pointAtCenter, adjust) {
2676
+ const viewportWidth = document.documentElement.clientWidth;
2677
+ const viewportHeight = document.documentElement.clientHeight;
2678
+ let resolvedPlacement = normalizeFloatingPlacement(placement);
2679
+ const side = resolvedPlacement.split("-")[0];
2680
+ const available = { top: trigger.top, bottom: viewportHeight - trigger.bottom, left: trigger.left, right: viewportWidth - trigger.right };
2681
+ const opposite = { top: "bottom", bottom: "top", left: "right", right: "left" };
2682
+ const preferred = side;
2683
+ const other = opposite[preferred];
2684
+ const needed = (side === "top" || side === "bottom" ? overlay.height : overlay.width) + FLOATING_GAP + FLOATING_VIEWPORT_PADDING;
2685
+ if (adjust && available[preferred] < needed && available[other] > available[preferred]) {
2686
+ resolvedPlacement = resolvedPlacement.replace(preferred, other);
2688
2687
  }
2689
- const borderClassName = !bordered || ghost ? "sia-collapse--borderless" : "sia-collapse--bordered";
2690
- return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: `sia-collapse ${borderClassName}${ghost ? " sia-collapse--ghost" : ""} sia-collapse--icon-${expandIconPosition} ${className}`.trim(), ...withTitleTooltip(props), children: items.map((item) => {
2691
- const open = openKeys.includes(item.key);
2692
- const arrow = item.showArrow === false ? null : /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("button", { type: "button", className: "sia-collapse__arrow", "aria-label": open ? "\u6536\u8D77" : "\u5C55\u5F00", onClick: item.collapsible === "icon" ? () => toggle(item) : void 0, children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Icon, { name: "chevron-right", size: 14 }) });
2693
- return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("section", { className: `sia-collapse__item${open ? " is-open" : ""}${item.disabled ? " is-disabled" : ""} ${item.className ?? ""}`.trim(), children: [
2694
- /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("header", { className: "sia-collapse__header", role: "button", tabIndex: item.disabled ? -1 : 0, "aria-expanded": open, onClick: item.collapsible === "icon" ? void 0 : () => toggle(item), onKeyDown: (event) => {
2695
- if (event.key === "Enter" || event.key === " ") {
2696
- event.preventDefault();
2697
- toggle(item);
2698
- }
2699
- }, children: [
2700
- expandIconPosition === "start" ? arrow : null,
2701
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "sia-collapse__label", children: item.label }),
2702
- item.extra ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "sia-collapse__extra", onClick: (event) => event.stopPropagation(), children: item.extra }) : null,
2703
- expandIconPosition === "end" ? arrow : null
2704
- ] }),
2705
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(CollapsePanel, { open, forceRender: item.forceRender, destroyInactivePanel, children: item.children })
2706
- ] }, item.key);
2707
- }) });
2708
- }
2709
- function ImagePreview({ open, src, alt = "", onOpenChange }) {
2710
- const previewRef = (0, import_react16.useRef)(null);
2711
- const stageRef = (0, import_react16.useRef)(null);
2712
- const [natural, setNatural] = (0, import_react16.useState)({ src: "", width: 0, height: 0 });
2713
- const [area, setArea] = (0, import_react16.useState)({ width: 0, height: 0 });
2714
- const [zoom, setZoom] = (0, import_react16.useState)(1);
2715
- const [rotation, setRotation] = (0, import_react16.useState)(0);
2716
- const [flip, setFlip] = (0, import_react16.useState)({ x: 1, y: 1 });
2717
- const [pan, setPan] = (0, import_react16.useState)({ x: 0, y: 0 });
2718
- const [failed, setFailed] = (0, import_react16.useState)(false);
2719
- const drag = (0, import_react16.useRef)();
2720
- useOverlayLifecycle(open, previewRef, () => onOpenChange(false));
2721
- const reset = () => {
2722
- setZoom(1);
2723
- setRotation(0);
2724
- setFlip({ x: 1, y: 1 });
2725
- setPan({ x: 0, y: 0 });
2726
- drag.current = void 0;
2727
- };
2728
- (0, import_react16.useEffect)(() => {
2729
- reset();
2730
- setFailed(false);
2731
- }, [open, src]);
2732
- (0, import_react16.useEffect)(() => {
2733
- const stage = stageRef.current;
2734
- if (!open || !stage) return;
2735
- const measure = () => {
2736
- setArea({ width: stage.clientWidth, height: stage.clientHeight });
2737
- setPan({ x: 0, y: 0 });
2738
- };
2739
- measure();
2740
- const observer = new ResizeObserver(measure);
2741
- observer.observe(stage);
2742
- return () => observer.disconnect();
2743
- }, [open, src]);
2744
- const ready = natural.src === src && natural.width > 0 && area.width > 0 && area.height > 0 && !failed;
2745
- const sideways = Math.abs(rotation % 180) === 90;
2746
- const fit = ready ? Math.min(1, area.width / (sideways ? natural.height : natural.width), area.height / (sideways ? natural.width : natural.height)) : 0;
2747
- const changeZoom = (factor) => setZoom((value) => Math.max(0.25, Math.min(8, value * factor)));
2748
- if (!open || !src || typeof document === "undefined") return null;
2749
- return (0, import_react_dom5.createPortal)(/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { ref: previewRef, className: "sia-image-preview", role: "dialog", "aria-modal": "true", "aria-label": "\u56FE\u7247\u9884\u89C8", tabIndex: -1, onClick: () => onOpenChange(false), children: [
2750
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Button, { variant: "text", className: "sia-image-preview__close", "aria-label": "\u5173\u95ED\u9884\u89C8", onClick: (event) => {
2751
- event.stopPropagation();
2752
- onOpenChange(false);
2753
- }, children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Icon, { name: "close" }) }),
2754
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { ref: stageRef, className: "sia-image-preview__stage", children: failed ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "sia-image-preview__error", children: "\u56FE\u7247\u52A0\u8F7D\u5931\u8D25" }) : /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
2755
- "img",
2756
- {
2757
- src,
2758
- alt,
2759
- draggable: false,
2760
- style: { width: natural.width * fit, height: natural.height * fit, visibility: ready ? "visible" : "hidden", transform: `translate(${pan.x}px, ${pan.y}px) rotate(${rotation}deg) scale(${zoom * flip.x}, ${zoom * flip.y})` },
2761
- onLoad: (event) => setNatural({ src, width: event.currentTarget.naturalWidth, height: event.currentTarget.naturalHeight }),
2762
- onError: () => setFailed(true),
2763
- onClick: (event) => event.stopPropagation(),
2764
- onPointerDown: (event) => {
2765
- if (event.button !== 0) return;
2766
- event.preventDefault();
2767
- event.stopPropagation();
2768
- event.currentTarget.setPointerCapture(event.pointerId);
2769
- drag.current = { id: event.pointerId, x: event.clientX, y: event.clientY, left: pan.x, top: pan.y };
2770
- },
2771
- onPointerMove: (event) => {
2772
- const start = drag.current;
2773
- if (start?.id === event.pointerId) setPan({ x: start.left + event.clientX - start.x, y: start.top + event.clientY - start.y });
2774
- },
2775
- onPointerUp: () => {
2776
- drag.current = void 0;
2777
- },
2778
- onPointerCancel: () => {
2779
- drag.current = void 0;
2780
- },
2781
- onLostPointerCapture: () => {
2782
- drag.current = void 0;
2783
- }
2784
- },
2785
- src
2786
- ) }),
2787
- /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "sia-image-preview__toolbar", role: "toolbar", "aria-label": "\u56FE\u7247\u64CD\u4F5C", onClick: (event) => event.stopPropagation(), children: [
2788
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Button, { variant: "text", title: "\u6C34\u5E73\u7FFB\u8F6C", "aria-label": "\u6C34\u5E73\u7FFB\u8F6C", disabled: !ready, onClick: () => setFlip((value) => ({ ...value, x: -value.x })), children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Icon, { name: "flip-horizontal", size: 20 }) }),
2789
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Button, { variant: "text", title: "\u5782\u76F4\u7FFB\u8F6C", "aria-label": "\u5782\u76F4\u7FFB\u8F6C", disabled: !ready, onClick: () => setFlip((value) => ({ ...value, y: -value.y })), children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Icon, { name: "flip-vertical", size: 20 }) }),
2790
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Button, { variant: "text", title: "\u5411\u5DE6\u65CB\u8F6C", "aria-label": "\u5411\u5DE6\u65CB\u8F6C", disabled: !ready, onClick: () => {
2791
- setRotation((value) => value - 90);
2792
- setPan({ x: 0, y: 0 });
2793
- }, children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Icon, { name: "rotate-left", size: 20 }) }),
2794
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Button, { variant: "text", title: "\u5411\u53F3\u65CB\u8F6C", "aria-label": "\u5411\u53F3\u65CB\u8F6C", disabled: !ready, onClick: () => {
2795
- setRotation((value) => value + 90);
2796
- setPan({ x: 0, y: 0 });
2797
- }, children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Icon, { name: "rotate-right", size: 20 }) }),
2798
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Button, { variant: "text", title: "\u7F29\u5C0F", "aria-label": "\u7F29\u5C0F", disabled: !ready || zoom <= 0.25, onClick: () => changeZoom(1 / 1.25), children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Icon, { name: "zoom-out", size: 20 }) }),
2799
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("output", { className: "sia-image-preview__scale", "aria-label": "\u5F53\u524D\u7F29\u653E\u6BD4\u4F8B", "aria-live": "polite", children: ready ? `${Math.round(fit * zoom * 100)}%` : "\u2014" }),
2800
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Button, { variant: "text", title: "\u653E\u5927", "aria-label": "\u653E\u5927", disabled: !ready || zoom >= 8, onClick: () => changeZoom(1.25), children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Icon, { name: "zoom-in", size: 20 }) }),
2801
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Button, { variant: "text", title: "\u9002\u5E94\u7A97\u53E3", "aria-label": "\u9002\u5E94\u7A97\u53E3", disabled: !ready, onClick: reset, children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Icon, { name: "refresh", size: 20 }) })
2802
- ] })
2803
- ] }), document.body);
2688
+ const vertical = resolvedPlacement.startsWith("top") || resolvedPlacement.startsWith("bottom");
2689
+ const start = resolvedPlacement.endsWith("-start");
2690
+ const end = resolvedPlacement.endsWith("-end");
2691
+ 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;
2692
+ 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;
2693
+ if (adjust) {
2694
+ if (vertical) left = clampFloating(left, FLOATING_VIEWPORT_PADDING, viewportWidth - overlay.width - FLOATING_VIEWPORT_PADDING);
2695
+ else top = clampFloating(top, FLOATING_VIEWPORT_PADDING, viewportHeight - overlay.height - FLOATING_VIEWPORT_PADDING);
2696
+ }
2697
+ const result = { top, left, placement: resolvedPlacement };
2698
+ const size = vertical ? trigger.width : trigger.height;
2699
+ const anchorStart = vertical ? trigger.left : trigger.top;
2700
+ const edge = Math.min(size / 2, FLOATING_ARROW_EDGE_OFFSET + 4);
2701
+ const target = anchorStart + (pointAtCenter || !start && !end ? size / 2 : start ? edge : size - edge);
2702
+ if (vertical) result.arrowX = clampFloating(target - left - 4, 8, overlay.width - 16);
2703
+ else result.arrowY = clampFloating(target - top - 4, 8, overlay.height - 16);
2704
+ return result;
2804
2705
  }
2805
-
2806
- // src/components/FloatingPlaceholder.tsx
2807
- var import_jsx_runtime16 = require("react/jsx-runtime");
2808
- function useFloatingPlaceholder({ mode, placeholder, filled, active = false }) {
2809
- const enabled = usePlaceholderMode(mode) === "floating" && Boolean(placeholder);
2810
- return {
2811
- enabled,
2812
- className: enabled ? ` sia-placeholder-floating${filled ? " sia-placeholder-floating--filled" : ""}${active ? " sia-placeholder-floating--active" : ""}` : "",
2813
- label: enabled ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "sia-floating-placeholder", "aria-hidden": "true", children: placeholder }) : null
2814
- };
2706
+ function isFloatingAnchorHidden(anchor, rect) {
2707
+ let left = 0, top = 0, right = document.documentElement.clientWidth, bottom = document.documentElement.clientHeight;
2708
+ for (let parent = anchor.parentElement; parent; parent = parent.parentElement) {
2709
+ const css = getComputedStyle(parent);
2710
+ const bounds = parent.getBoundingClientRect();
2711
+ if (/(auto|scroll|hidden|clip)/.test(css.overflowX)) {
2712
+ left = Math.max(left, bounds.left);
2713
+ right = Math.min(right, bounds.right);
2714
+ }
2715
+ if (/(auto|scroll|hidden|clip)/.test(css.overflowY)) {
2716
+ top = Math.max(top, bounds.top);
2717
+ bottom = Math.min(bottom, bounds.bottom);
2718
+ }
2719
+ }
2720
+ return rect.right <= left || rect.left >= right || rect.bottom <= top || rect.top >= bottom;
2815
2721
  }
2816
-
2817
- // src/components/Select.tsx
2818
- var import_react23 = require("react");
2819
-
2820
- // src/components/Modal.tsx
2821
- var import_react17 = require("react");
2822
- var import_client3 = require("react-dom/client");
2823
- var import_jsx_runtime17 = require("react/jsx-runtime");
2824
- var modalHandles = /* @__PURE__ */ new Set();
2825
- var MODAL_MOTION_DURATION = 220;
2826
- var typeIcons = {
2827
- info: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(Icon, { name: "info", size: 23 }),
2828
- success: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(Icon, { name: "circle-check", size: 23 }),
2829
- warning: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(Icon, { name: "warning", size: 23 }),
2830
- error: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(Icon, { name: "circle-close", size: 23 }),
2831
- confirm: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(Icon, { name: "question", size: 23 })
2832
- };
2833
- var ModalRoot = (0, import_react17.forwardRef)(function Modal({
2834
- open,
2835
- title,
2722
+ function Floating({
2836
2723
  children,
2837
- footer,
2838
- footerExtra,
2839
- type = "default",
2840
- okText = "\u786E\u5B9A",
2841
- cancelText = "\u53D6\u6D88",
2842
- showCancel = type === "default" || type === "confirm",
2843
- confirmLoading = false,
2844
- okButtonProps,
2845
- cancelButtonProps,
2846
- closable = true,
2847
- maskClosable = true,
2848
- keyboard = true,
2849
- centered = false,
2850
- width = 520,
2851
- height,
2852
- zIndex = 1e3,
2853
- getContainer,
2854
- onOk,
2855
- onCancel,
2856
- onOpenChange,
2857
- afterOpenChange,
2724
+ content,
2725
+ placement = "top",
2726
+ trigger = "hover",
2727
+ open,
2728
+ defaultOpen = false,
2729
+ mouseEnterDelay = 0.1,
2730
+ mouseLeaveDelay = 0.1,
2731
+ arrow = true,
2732
+ autoAdjustOverflow = true,
2733
+ color,
2858
2734
  className = "",
2859
- style,
2860
- ...props
2861
- }, forwardedRef) {
2862
- const localRef = (0, import_react17.useRef)(null);
2863
- const titleId = (0, import_react17.useId)();
2864
- const [submitting, setSubmitting] = (0, import_react17.useState)(false);
2865
- const [rendered, setRendered] = (0, import_react17.useState)(open);
2866
- const [closing, setClosing] = (0, import_react17.useState)(false);
2867
- const afterOpenChangeRef = (0, import_react17.useRef)(afterOpenChange);
2868
- afterOpenChangeRef.current = afterOpenChange;
2869
- const submitLock = (0, import_react17.useRef)(false);
2870
- (0, import_react17.useEffect)(() => {
2871
- if (!open || !rendered) return void 0;
2872
- const timer = window.setTimeout(() => afterOpenChangeRef.current?.(true), MODAL_MOTION_DURATION);
2873
- return () => window.clearTimeout(timer);
2874
- }, [open, rendered]);
2875
- (0, import_react17.useEffect)(() => {
2876
- if (open) {
2877
- setRendered(true);
2878
- setClosing(false);
2879
- return void 0;
2735
+ overlayClassName = "",
2736
+ role = "tooltip",
2737
+ onOpenChange
2738
+ }) {
2739
+ const rootRef = (0, import_react15.useRef)(null);
2740
+ const triggerElementRef = (0, import_react15.useRef)(null);
2741
+ const overlayRef = (0, import_react15.useRef)(null);
2742
+ const enterTimer = (0, import_react15.useRef)();
2743
+ const leaveTimer = (0, import_react15.useRef)();
2744
+ const contentId = (0, import_react15.useId)();
2745
+ const triggers = Array.isArray(trigger) ? trigger : [trigger];
2746
+ const [visible, setVisible] = useControllableState({ value: open, defaultValue: defaultOpen, onChange: onOpenChange });
2747
+ const [position, setPosition] = (0, import_react15.useState)();
2748
+ const [portalContainer, setPortalContainer] = (0, import_react15.useState)(null);
2749
+ (0, import_react15.useLayoutEffect)(() => {
2750
+ if (!visible) return;
2751
+ setPortalContainer(rootRef.current?.closest(".sia-modal-root, .sia-drawer-root") ?? document.body);
2752
+ });
2753
+ const setVisibleRef = (0, import_react15.useRef)(setVisible);
2754
+ setVisibleRef.current = setVisible;
2755
+ const closeSelfRef = (0, import_react15.useRef)();
2756
+ if (!closeSelfRef.current) closeSelfRef.current = () => setVisibleRef.current(false);
2757
+ const exclusiveTooltip = role === "tooltip" && triggers.includes("hover");
2758
+ function openNow() {
2759
+ if (exclusiveTooltip && activeTooltipClose !== closeSelfRef.current) {
2760
+ activeTooltipClose?.();
2761
+ activeTooltipClose = closeSelfRef.current;
2880
2762
  }
2881
- if (!rendered) return void 0;
2882
- setClosing(true);
2883
- const timer = window.setTimeout(() => {
2884
- setRendered(false);
2885
- setClosing(false);
2886
- afterOpenChangeRef.current?.(false);
2887
- }, MODAL_MOTION_DURATION);
2888
- return () => window.clearTimeout(timer);
2889
- }, [open, rendered]);
2890
- function setRef(node) {
2891
- localRef.current = node;
2892
- if (typeof forwardedRef === "function") forwardedRef(node);
2893
- else if (forwardedRef) forwardedRef.current = node;
2894
- }
2895
- function requestClose() {
2896
- if (submitting || confirmLoading) return;
2897
- onCancel?.();
2898
- onOpenChange?.(false);
2763
+ setVisible(true);
2899
2764
  }
2900
- async function handleOk() {
2901
- if (submitLock.current || confirmLoading) return;
2902
- submitLock.current = true;
2903
- if (!onOk) {
2904
- onOpenChange?.(false);
2905
- submitLock.current = false;
2906
- return;
2907
- }
2908
- try {
2909
- const result = onOk();
2910
- if (result instanceof Promise) {
2911
- setSubmitting(true);
2912
- const resolved = await result;
2913
- if (resolved !== false) onOpenChange?.(false);
2914
- } else if (result !== false) {
2915
- onOpenChange?.(false);
2916
- }
2917
- } catch {
2918
- return;
2919
- } finally {
2920
- submitLock.current = false;
2921
- setSubmitting(false);
2922
- }
2765
+ function closeNow() {
2766
+ if (activeTooltipClose === closeSelfRef.current) activeTooltipClose = void 0;
2767
+ setVisible(false);
2923
2768
  }
2924
- useOverlayLifecycle(rendered, localRef, requestClose, keyboard);
2925
- if (!rendered) return null;
2926
- const mergedStyle = {
2927
- ...height !== void 0 ? { display: "flex", flexDirection: "column", height, maxHeight: "calc(100dvh - 48px)" } : {},
2928
- ...style,
2929
- "--sia-modal-width": typeof width === "number" ? `${width}px` : width
2930
- };
2931
- const loading = submitting || confirmLoading;
2932
- const defaultFooter = /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_jsx_runtime17.Fragment, { children: [
2933
- showCancel ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(Button, { ...cancelButtonProps, onClick: requestClose, disabled: loading || cancelButtonProps?.disabled, children: cancelText }) : null,
2934
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(Button, { variant: "primary", ...okButtonProps, onClick: handleOk, loading, children: okText })
2935
- ] });
2936
- return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(OverlayPortal, { container: getContainer, children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
2937
- "div",
2938
- {
2939
- className: `sia-modal-root${centered ? " sia-modal-root--centered" : ""}`,
2940
- "data-state": closing ? "closing" : "open",
2941
- style: { zIndex },
2942
- onMouseDown: (event) => {
2943
- if (event.target === event.currentTarget && maskClosable) requestClose();
2944
- },
2945
- children: /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
2946
- "div",
2947
- {
2948
- ref: setRef,
2949
- className: `sia-modal sia-modal--${type} ${className}`.trim(),
2950
- style: mergedStyle,
2951
- role: "dialog",
2952
- "aria-modal": "true",
2953
- "aria-labelledby": title ? titleId : void 0,
2954
- tabIndex: -1,
2955
- ...withTitleTooltip(props),
2956
- children: [
2957
- /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "sia-modal__header", style: height !== void 0 ? { flexShrink: 0 } : void 0, children: [
2958
- /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "sia-modal__heading", children: [
2959
- typeIcons[type] ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "sia-modal__type-icon", "aria-hidden": "true", children: typeIcons[type] }) : null,
2960
- title ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("strong", { id: titleId, children: title }) : null
2961
- ] }),
2962
- closable ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("button", { type: "button", className: "sia-modal__close", "aria-label": "\u5173\u95ED\u5BF9\u8BDD\u6846", onClick: requestClose, children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(Icon, { name: "close", size: 17 }) }) : null
2963
- ] }),
2964
- children !== void 0 ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "sia-modal__body", style: height !== void 0 ? { flex: 1, minHeight: 0, overflow: "auto" } : void 0, children }) : null,
2965
- footer !== null ? /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "sia-modal__footer", style: height !== void 0 ? { flexShrink: 0 } : void 0, children: [
2966
- footer === void 0 && footerExtra != null ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { style: { marginRight: "auto" }, children: footerExtra }) : null,
2967
- footer ?? defaultFooter
2968
- ] }) : null
2969
- ]
2970
- }
2971
- )
2972
- }
2973
- ) });
2974
- });
2975
- function openModal(config) {
2976
- if (typeof document === "undefined") return { destroy() {
2977
- }, update() {
2978
- } };
2979
- const container = document.createElement("div");
2980
- container.className = "sia-modal-api-host";
2981
- document.body.appendChild(container);
2982
- const root = (0, import_client3.createRoot)(container);
2983
- let currentConfig = config;
2984
- let currentOpen = true;
2985
- let destroyed = false;
2986
- let cleanupTimer;
2987
- function cleanup2() {
2988
- if (destroyed) return;
2989
- destroyed = true;
2990
- if (cleanupTimer !== void 0) window.clearTimeout(cleanupTimer);
2991
- root.unmount();
2992
- container.remove();
2993
- modalHandles.delete(handle);
2769
+ function clearTimers() {
2770
+ window.clearTimeout(enterTimer.current);
2771
+ window.clearTimeout(leaveTimer.current);
2994
2772
  }
2995
- function closeWithMotion() {
2996
- if (destroyed || !currentOpen) return;
2997
- currentOpen = false;
2998
- render();
2999
- cleanupTimer = window.setTimeout(cleanup2, MODAL_MOTION_DURATION);
2773
+ function show(delay = 0) {
2774
+ clearTimers();
2775
+ if (delay <= 0) openNow();
2776
+ else enterTimer.current = window.setTimeout(openNow, delay * 1e3);
3000
2777
  }
3001
- function render() {
3002
- root.render(/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(ModalRoot, { ...currentConfig, open: currentOpen, onOpenChange: (nextOpen) => {
3003
- currentConfig.onOpenChange?.(nextOpen);
3004
- if (!nextOpen) closeWithMotion();
3005
- } }));
2778
+ function hide(delay = 0) {
2779
+ clearTimers();
2780
+ if (delay <= 0) closeNow();
2781
+ else leaveTimer.current = window.setTimeout(closeNow, delay * 1e3);
3006
2782
  }
3007
- const handle = {
3008
- destroy: closeWithMotion,
3009
- update(next) {
3010
- if (destroyed) return;
3011
- currentConfig = { ...currentConfig, ...next };
3012
- render();
2783
+ (0, import_react15.useEffect)(() => {
2784
+ if (!visible || !triggers.includes("click")) return;
2785
+ const close = (event) => {
2786
+ const target = event.target;
2787
+ if (!rootRef.current?.contains(target) && !overlayRef.current?.contains(target)) closeNow();
2788
+ };
2789
+ document.addEventListener("pointerdown", close);
2790
+ return () => document.removeEventListener("pointerdown", close);
2791
+ }, [triggers.join("|"), visible]);
2792
+ (0, import_react15.useLayoutEffect)(() => {
2793
+ if (!visible) {
2794
+ setPosition(void 0);
2795
+ return;
3013
2796
  }
3014
- };
3015
- modalHandles.add(handle);
3016
- render();
3017
- return handle;
3018
- }
3019
- function preset(type, defaults = {}) {
3020
- return (config) => openModal({
3021
- okText: type === "confirm" ? "\u786E\u5B9A" : "\u77E5\u9053\u4E86",
3022
- showCancel: type === "confirm",
3023
- closable: type !== "confirm",
3024
- maskClosable: type !== "confirm",
3025
- ...defaults,
3026
- ...config,
3027
- type
2797
+ const getAnchor = () => {
2798
+ const root = rootRef.current;
2799
+ const target = triggerElementRef.current;
2800
+ return target && root?.contains(target) ? target : root?.firstElementChild ?? root;
2801
+ };
2802
+ const update = () => {
2803
+ if (!rootRef.current || !overlayRef.current) return;
2804
+ const anchor2 = getAnchor();
2805
+ if (anchor2) {
2806
+ const rect = anchor2.getBoundingClientRect();
2807
+ const next = resolveFloatingPosition(rect, overlayRef.current.getBoundingClientRect(), placement, typeof arrow === "object" && !!arrow.pointAtCenter, autoAdjustOverflow);
2808
+ next.hidden = isFloatingAnchorHidden(anchor2, rect);
2809
+ setPosition(next);
2810
+ }
2811
+ };
2812
+ update();
2813
+ window.addEventListener("resize", update);
2814
+ window.addEventListener("scroll", update, true);
2815
+ window.addEventListener("animationend", update, true);
2816
+ window.addEventListener("transitionend", update, true);
2817
+ const observer = new ResizeObserver(update);
2818
+ if (rootRef.current) observer.observe(rootRef.current);
2819
+ const anchor = getAnchor();
2820
+ if (anchor) observer.observe(anchor);
2821
+ if (overlayRef.current) observer.observe(overlayRef.current);
2822
+ return () => {
2823
+ window.removeEventListener("resize", update);
2824
+ window.removeEventListener("scroll", update, true);
2825
+ window.removeEventListener("animationend", update, true);
2826
+ window.removeEventListener("transitionend", update, true);
2827
+ observer.disconnect();
2828
+ };
2829
+ }, [children, content, placement, visible, portalContainer, autoAdjustOverflow, typeof arrow === "object" && arrow.pointAtCenter]);
2830
+ (0, import_react15.useEffect)(() => () => {
2831
+ clearTimers();
2832
+ if (activeTooltipClose === closeSelfRef.current) activeTooltipClose = void 0;
2833
+ }, []);
2834
+ const child = (0, import_react15.cloneElement)(children, {
2835
+ ...triggers.includes("hover") ? { onMouseEnter: (event) => {
2836
+ triggerElementRef.current = event.currentTarget;
2837
+ children.props.onMouseEnter?.(event);
2838
+ show(mouseEnterDelay);
2839
+ }, onMouseLeave: (event) => {
2840
+ children.props.onMouseLeave?.(event);
2841
+ hide(mouseLeaveDelay);
2842
+ } } : {},
2843
+ ...triggers.includes("focus") ? { onFocus: (event) => {
2844
+ triggerElementRef.current = event.currentTarget;
2845
+ children.props.onFocus?.(event);
2846
+ show();
2847
+ }, onBlur: (event) => {
2848
+ children.props.onBlur?.(event);
2849
+ hide();
2850
+ } } : {},
2851
+ ...triggers.includes("click") ? { onClick: (event) => {
2852
+ triggerElementRef.current = event.currentTarget;
2853
+ children.props.onClick?.(event);
2854
+ if (visible) closeNow();
2855
+ else openNow();
2856
+ } } : {},
2857
+ "aria-describedby": visible ? contentId : void 0
3028
2858
  });
2859
+ const overlay = visible && content !== null && content !== void 0 && typeof document !== "undefined" ? (0, import_react_dom2.createPortal)(
2860
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
2861
+ "span",
2862
+ {
2863
+ ref: overlayRef,
2864
+ id: contentId,
2865
+ role,
2866
+ "data-custom-color": color ? "" : void 0,
2867
+ className: `sia-floating__overlay sia-floating__overlay--${position?.placement ?? normalizeFloatingPlacement(placement)} ${overlayClassName}`.trim(),
2868
+ style: {
2869
+ "--sia-floating-color": color,
2870
+ "--sia-floating-arrow-x": position?.arrowX == null ? void 0 : `${position.arrowX}px`,
2871
+ "--sia-floating-arrow-y": position?.arrowY == null ? void 0 : `${position.arrowY}px`,
2872
+ top: position?.top ?? 0,
2873
+ left: position?.left ?? 0,
2874
+ visibility: position && !position.hidden ? "visible" : "hidden"
2875
+ },
2876
+ onMouseEnter: triggers.includes("hover") ? () => show() : void 0,
2877
+ onMouseLeave: triggers.includes("hover") ? () => hide(mouseLeaveDelay) : void 0,
2878
+ children: [
2879
+ content,
2880
+ arrow ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "sia-floating__arrow" }) : null
2881
+ ]
2882
+ }
2883
+ ),
2884
+ portalContainer ?? document.body
2885
+ ) : null;
2886
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_jsx_runtime13.Fragment, { children: [
2887
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { ref: rootRef, className: `sia-floating ${className}`.trim(), children: child }),
2888
+ overlay
2889
+ ] });
2890
+ }
2891
+ function Tooltip(props) {
2892
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Floating, { ...props, content: props.title });
3029
2893
  }
3030
- var Modal2 = Object.assign(ModalRoot, {
3031
- /** 直接打开实例,返回可用于更新或关闭的操作句柄。 */
3032
- open: openModal,
3033
- /** 显示普通信息提示。 */
3034
- info: preset("info", { title: "\u63D0\u793A" }),
3035
- /** 显示成功提示。 */
3036
- success: preset("success", { title: "\u64CD\u4F5C\u6210\u529F" }),
3037
- /** 显示警告提示。 */
3038
- warning: preset("warning", { title: "\u8BF7\u6CE8\u610F" }),
3039
- /** 显示错误提示。 */
3040
- error: preset("error", { title: "\u64CD\u4F5C\u5931\u8D25" }),
3041
- /** 打开确认弹框,支持异步确认;返回 false 可阻止关闭。 */
3042
- confirm: preset("confirm", { title: "\u786E\u8BA4\u64CD\u4F5C" }),
3043
- /** 关闭由此 API 打开的所有弹层。 */
3044
- destroyAll() {
3045
- [...modalHandles].forEach((handle) => handle.destroy());
3046
- }
3047
- });
3048
2894
 
3049
- // src/components/Input.tsx
3050
- var import_react18 = require("react");
3051
- var import_jsx_runtime18 = require("react/jsx-runtime");
3052
- var Input = (0, import_react18.forwardRef)(function Input2({
3053
- label,
3054
- hint,
3055
- status = "default",
3056
- size: sizeProp,
3057
- prefix,
3058
- suffix,
3059
- allowClear = true,
3060
- onClear,
3061
- showCount = false,
3062
- placeholderMode,
3063
- placeholder,
2895
+ // src/components/Feedback.tsx
2896
+ var import_jsx_runtime14 = require("react/jsx-runtime");
2897
+ function Progress({
2898
+ percent = 0,
2899
+ type = "line",
2900
+ status,
2901
+ showInfo = true,
2902
+ format,
2903
+ strokeColor,
2904
+ trailColor,
2905
+ strokeWidth = 8,
2906
+ width = 120,
2907
+ steps,
2908
+ success,
2909
+ size = "default",
3064
2910
  className = "",
3065
- id,
3066
- disabled,
3067
- value,
3068
- defaultValue,
3069
- maxLength,
3070
- onChange,
2911
+ style,
3071
2912
  ...props
3072
- }, forwardedRef) {
3073
- const size = useControlSize(sizeProp);
3074
- const fallbackId = (0, import_react18.useId)();
3075
- const inputId = id ?? fallbackId;
3076
- const localRef = (0, import_react18.useRef)(null);
3077
- const [uncontrolledValue, setUncontrolledValue] = (0, import_react18.useState)(() => defaultValue === void 0 ? "" : String(defaultValue));
3078
- const currentText = value !== void 0 ? String(value) : uncontrolledValue;
3079
- const hasValue = currentText.length > 0;
3080
- const floatingPlaceholder = usePlaceholderMode(placeholderMode) === "floating" && Boolean(placeholder);
3081
- const hintId = `${inputId}-hint`;
3082
- const describedBy = [props["aria-describedby"], hint ? hintId : null].filter(Boolean).join(" ") || void 0;
3083
- function setRef(node) {
3084
- localRef.current = node;
3085
- if (typeof forwardedRef === "function") forwardedRef(node);
3086
- else if (forwardedRef) forwardedRef.current = node;
3087
- }
3088
- function handleChange(event) {
3089
- if (maxLength !== void 0 && maxLength >= 0 && event.target.value.length > maxLength) {
3090
- const valueSetter = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, "value")?.set;
3091
- valueSetter?.call(event.target, event.target.value.slice(0, maxLength));
3092
- }
3093
- setUncontrolledValue(event.target.value);
3094
- onChange?.(event);
3095
- }
3096
- function clearValue() {
3097
- const input2 = localRef.current;
3098
- if (!input2 || disabled) return;
3099
- const valueSetter = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, "value")?.set;
3100
- valueSetter?.call(input2, "");
3101
- input2.dispatchEvent(new Event("input", { bubbles: true }));
3102
- setUncontrolledValue("");
3103
- input2.focus();
3104
- onClear?.();
3105
- }
3106
- function handleControlMouseDown(event) {
3107
- if (disabled || event.defaultPrevented || event.button !== 0) return;
3108
- const target = event.target;
3109
- if (!(target instanceof Element)) return;
3110
- const interactive = target.closest("input, textarea, select, button, a[href], [tabindex], [contenteditable]:not([contenteditable='false']), [role='button'], [role='link']");
3111
- if (interactive && event.currentTarget.contains(interactive) && !interactive.classList.contains("sia-input-control__clear")) return;
3112
- event.preventDefault();
3113
- localRef.current?.focus({ preventScroll: true });
2913
+ }) {
2914
+ const safe = Math.max(0, Math.min(100, percent));
2915
+ const resolved = status ?? (safe >= 100 ? "success" : "normal");
2916
+ const color = typeof strokeColor === "string" ? strokeColor : void 0;
2917
+ const background = typeof strokeColor === "object" ? `linear-gradient(90deg, ${strokeColor.from}, ${strokeColor.to})` : color;
2918
+ const label = format?.(safe, success?.percent) ?? (resolved === "exception" ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Icon, { name: "close" }) : resolved === "success" ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Icon, { name: "check" }) : `${safe}%`);
2919
+ if (type !== "line") {
2920
+ const radius = 46;
2921
+ const circumference = 2 * Math.PI * radius;
2922
+ const dash = circumference * (safe / 100);
2923
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: `sia-progress sia-progress--${type} sia-progress--${resolved} ${className}`.trim(), style: { width, height: width, ...style }, ...withTitleTooltip(props), children: [
2924
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("svg", { viewBox: "0 0 100 100", role: "progressbar", "aria-valuenow": safe, children: [
2925
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("circle", { className: "sia-progress__trail", cx: "50", cy: "50", r: radius, style: { stroke: trailColor } }),
2926
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("circle", { className: "sia-progress__circle", cx: "50", cy: "50", r: radius, style: { stroke: color, strokeDasharray: type === "dashboard" ? `${circumference * 0.75} ${circumference * 0.25}` : circumference, strokeDashoffset: (type === "dashboard" ? circumference * 0.75 : circumference) - dash * (type === "dashboard" ? 0.75 : 1), strokeWidth } })
2927
+ ] }),
2928
+ showInfo ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "sia-progress__text", children: label }) : null
2929
+ ] });
3114
2930
  }
3115
- const input = /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
3116
- "input",
3117
- {
3118
- ...withTitleTooltip(props),
3119
- ref: setRef,
3120
- id: inputId,
3121
- className: "sia-input",
3122
- disabled,
3123
- value,
3124
- defaultValue,
3125
- maxLength,
3126
- placeholder,
3127
- "aria-describedby": describedBy,
3128
- "aria-invalid": status === "error" || void 0,
3129
- onChange: handleChange
3130
- }
3131
- );
3132
- const control = /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("span", { onMouseDown: handleControlMouseDown, className: `sia-input-control sia-input-control--${size} sia-input-control--${status}${floatingPlaceholder ? " sia-input-control--floating" : ""}${floatingPlaceholder && hasValue ? " is-filled" : ""}${disabled ? " is-disabled" : ""}`, children: [
3133
- prefix ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "sia-input-control__affix", children: prefix }) : null,
3134
- floatingPlaceholder ? /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("span", { className: "sia-input-control__input-wrap", children: [
3135
- input,
3136
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "sia-input-control__floating-placeholder", "aria-hidden": "true", children: placeholder })
3137
- ] }) : input,
3138
- allowClear && hasValue && !disabled ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("button", { className: "sia-input-control__clear", type: "button", "aria-label": "\u6E05\u7A7A\u8F93\u5165", onClick: clearValue, children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(Icon, { name: "circle-close", variant: "filled", size: 15 }) }) : null,
3139
- suffix ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "sia-input-control__affix", children: suffix }) : null,
3140
- showCount ? /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("span", { className: `sia-input-control__count${maxLength !== void 0 && currentText.length >= maxLength ? " is-limit" : ""}`, "aria-live": "polite", children: [
3141
- currentText.length,
3142
- maxLength !== void 0 ? `/${maxLength}` : null
3143
- ] }) : null
3144
- ] });
3145
- if (!label && !hint) return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: `sia-field ${className}`.trim(), children: control });
3146
- return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("span", { className: `sia-field ${className}`.trim(), children: [
3147
- label ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("label", { className: "sia-field__label", htmlFor: inputId, children: label }) : null,
3148
- control,
3149
- hint ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { id: hintId, className: `sia-field__hint sia-field__hint--${status}`, children: hint }) : null
2931
+ const height = Array.isArray(size) ? size[1] : typeof size === "number" ? size : size === "small" ? 6 : strokeWidth;
2932
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: `sia-progress sia-progress--line sia-progress--${resolved} ${className}`.trim(), style, ...withTitleTooltip(props), children: [
2933
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "sia-progress__outer", style: { height, backgroundColor: trailColor }, children: steps ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "sia-progress__steps", children: Array.from({ length: steps }, (_, index) => /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: index < Math.round(steps * safe / 100) ? "is-active" : "", style: { background: index < Math.round(steps * safe / 100) ? background : trailColor } }, index)) }) : /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_jsx_runtime14.Fragment, { children: [
2934
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "sia-progress__bar", style: { width: `${safe}%`, background } }),
2935
+ success?.percent ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "sia-progress__success", style: { width: `${success.percent}%`, background: success.strokeColor } }) : null
2936
+ ] }) }),
2937
+ showInfo ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "sia-progress__info", children: label }) : null
3150
2938
  ] });
3151
- });
3152
-
3153
- // src/components/SelectionPanel.tsx
3154
- var import_react21 = require("react");
3155
-
3156
- // src/components/FloatingScrollbarProvider.tsx
3157
- var import_react20 = require("react");
3158
-
3159
- // src/components/useScrollbarProximity.ts
3160
- var import_react19 = require("react");
3161
- var users = 0;
3162
- var cleanup;
3163
- function useScrollbarProximity() {
3164
- (0, import_react19.useEffect)(() => {
3165
- if (users++ === 0) {
3166
- let frame = 0;
3167
- let x = -Infinity;
3168
- let y = -Infinity;
3169
- const nearby = /* @__PURE__ */ new Set();
3170
- const clear = () => {
3171
- nearby.forEach((track) => track.removeAttribute("data-scrollbar-near"));
3172
- nearby.clear();
3173
- };
3174
- const update = () => {
3175
- frame = 0;
3176
- const next = /* @__PURE__ */ new Set();
3177
- document.querySelectorAll(".sia-scrollbar-track").forEach((track) => {
3178
- const rect = track.getBoundingClientRect();
3179
- const proximity = Number.parseFloat(getComputedStyle(track).getPropertyValue("--sia-scrollbar-proximity")) || 16;
3180
- if (rect.width && rect.height && x >= rect.left - proximity && x <= rect.right + proximity && y >= rect.top - proximity && y <= rect.bottom + proximity) {
3181
- next.add(track);
3182
- if (!nearby.has(track)) track.setAttribute("data-scrollbar-near", "true");
3183
- }
3184
- });
3185
- nearby.forEach((track) => {
3186
- if (!next.has(track)) track.removeAttribute("data-scrollbar-near");
3187
- });
3188
- nearby.clear();
3189
- next.forEach((track) => nearby.add(track));
3190
- };
3191
- const move = (event) => {
3192
- if (event.pointerType === "touch") return;
3193
- x = event.clientX;
3194
- y = event.clientY;
3195
- if (!frame) frame = requestAnimationFrame(update);
3196
- };
3197
- const leave = () => {
3198
- x = y = -Infinity;
3199
- clear();
3200
- };
3201
- document.addEventListener("pointermove", move, { passive: true });
3202
- document.documentElement.addEventListener("pointerleave", leave);
3203
- window.addEventListener("blur", leave);
3204
- cleanup = () => {
3205
- if (frame) cancelAnimationFrame(frame);
3206
- document.removeEventListener("pointermove", move);
3207
- document.documentElement.removeEventListener("pointerleave", leave);
3208
- window.removeEventListener("blur", leave);
3209
- clear();
3210
- };
3211
- }
3212
- return () => {
3213
- if (--users === 0) {
3214
- cleanup?.();
3215
- cleanup = void 0;
3216
- }
3217
- };
3218
- }, []);
3219
2939
  }
3220
2940
 
3221
- // src/components/FloatingScrollbarProvider.tsx
3222
- var scrollableOverflow = /* @__PURE__ */ new Set(["auto", "scroll", "overlay"]);
3223
- var clippingOverflow = /* @__PURE__ */ new Set(["auto", "scroll", "overlay", "hidden", "clip"]);
3224
- function isRootScroller(target) {
3225
- return target === document.scrollingElement || target === document.documentElement;
3226
- }
3227
- function canScroll(target, axis) {
3228
- if (target.matches(".sia-table__viewport, [data-sia-native-scrollbar]")) return false;
3229
- if (isRootScroller(target)) {
3230
- return axis === "horizontal" ? target.scrollWidth > target.clientWidth + 1 : target.scrollHeight > target.clientHeight + 1;
2941
+ // src/components/DataDisplay.tsx
2942
+ var import_react18 = require("react");
2943
+ var import_react_dom5 = require("react-dom");
2944
+
2945
+ // src/components/Overlay.tsx
2946
+ var import_react17 = require("react");
2947
+ var import_react_dom4 = require("react-dom");
2948
+ var bodyLockCount = 0;
2949
+ var previousBodyOverflow = "";
2950
+ var overlayStack = [];
2951
+ function lockBodyScroll() {
2952
+ if (bodyLockCount === 0) {
2953
+ previousBodyOverflow = document.body.style.overflow;
2954
+ document.body.style.overflow = "hidden";
3231
2955
  }
3232
- const style = getComputedStyle(target);
3233
- const overflow = axis === "horizontal" ? style.overflowX : style.overflowY;
3234
- const hasOverflow = axis === "horizontal" ? target.scrollWidth > target.clientWidth + 1 : target.scrollHeight > target.clientHeight + 1;
3235
- return hasOverflow && scrollableOverflow.has(overflow);
2956
+ bodyLockCount += 1;
2957
+ return () => {
2958
+ bodyLockCount = Math.max(0, bodyLockCount - 1);
2959
+ if (bodyLockCount === 0) document.body.style.overflow = previousBodyOverflow;
2960
+ };
3236
2961
  }
3237
- function visibleRect(target) {
3238
- if (isRootScroller(target)) {
3239
- return { top: 0, right: window.innerWidth, bottom: window.innerHeight, left: 0, width: window.innerWidth, height: window.innerHeight };
3240
- }
3241
- const ownRect = target.getBoundingClientRect();
3242
- let top = Math.max(0, ownRect.top);
3243
- let right = Math.min(window.innerWidth, ownRect.right);
3244
- let bottom = Math.min(window.innerHeight, ownRect.bottom);
3245
- let left = Math.max(0, ownRect.left);
3246
- let parent = target.parentElement;
3247
- while (parent && parent !== document.body && parent !== document.documentElement) {
3248
- const style = getComputedStyle(parent);
3249
- const clipX = clippingOverflow.has(style.overflowX);
3250
- const clipY = clippingOverflow.has(style.overflowY);
3251
- if (clipX || clipY) {
3252
- const rect = parent.getBoundingClientRect();
3253
- if (clipX) {
3254
- left = Math.max(left, rect.left);
3255
- right = Math.min(right, rect.right);
2962
+ function focusableElements(container) {
2963
+ return [...container.querySelectorAll(
2964
+ 'a[href], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])'
2965
+ )].filter((element) => !element.hidden && element.getAttribute("aria-hidden") !== "true");
2966
+ }
2967
+ function useOverlayLifecycle(open, panelRef, onRequestClose, keyboard = true) {
2968
+ const overlayIdRef = (0, import_react17.useRef)(/* @__PURE__ */ Symbol("sia-overlay"));
2969
+ const closeRef = (0, import_react17.useRef)(onRequestClose);
2970
+ closeRef.current = onRequestClose;
2971
+ (0, import_react17.useEffect)(() => {
2972
+ if (!open || typeof document === "undefined") return void 0;
2973
+ const overlayId = overlayIdRef.current;
2974
+ const previouslyFocused = document.activeElement instanceof HTMLElement ? document.activeElement : null;
2975
+ overlayStack.push(overlayId);
2976
+ const unlockBody = lockBodyScroll();
2977
+ const focusTimer = window.setTimeout(() => {
2978
+ const panel = panelRef.current;
2979
+ if (!panel) return;
2980
+ const [firstFocusable] = focusableElements(panel);
2981
+ (firstFocusable ?? panel).focus({ preventScroll: true });
2982
+ }, 0);
2983
+ function handleKeyDown(event) {
2984
+ if (overlayStack.at(-1) !== overlayId) return;
2985
+ if (event.key === "Escape" && keyboard) {
2986
+ event.preventDefault();
2987
+ closeRef.current();
2988
+ return;
3256
2989
  }
3257
- if (clipY) {
3258
- top = Math.max(top, rect.top);
3259
- bottom = Math.min(bottom, rect.bottom);
2990
+ if (event.key !== "Tab" || !panelRef.current) return;
2991
+ const focusable = focusableElements(panelRef.current);
2992
+ if (focusable.length === 0) {
2993
+ event.preventDefault();
2994
+ panelRef.current.focus();
2995
+ return;
2996
+ }
2997
+ const first = focusable[0];
2998
+ const last = focusable.at(-1);
2999
+ if (event.shiftKey && document.activeElement === first) {
3000
+ event.preventDefault();
3001
+ last.focus();
3002
+ } else if (!event.shiftKey && document.activeElement === last) {
3003
+ event.preventDefault();
3004
+ first.focus();
3260
3005
  }
3261
3006
  }
3262
- parent = parent.parentElement;
3007
+ document.addEventListener("keydown", handleKeyDown);
3008
+ return () => {
3009
+ window.clearTimeout(focusTimer);
3010
+ document.removeEventListener("keydown", handleKeyDown);
3011
+ const index = overlayStack.lastIndexOf(overlayId);
3012
+ if (index >= 0) overlayStack.splice(index, 1);
3013
+ unlockBody();
3014
+ previouslyFocused?.focus({ preventScroll: true });
3015
+ };
3016
+ }, [keyboard, open, panelRef]);
3017
+ }
3018
+ function OverlayPortal({ children, container }) {
3019
+ if (typeof document === "undefined") return null;
3020
+ const target = typeof container === "function" ? container() : container ?? document.body;
3021
+ return (0, import_react_dom4.createPortal)(children, target);
3022
+ }
3023
+
3024
+ // src/components/DataDisplay.tsx
3025
+ var import_jsx_runtime15 = require("react/jsx-runtime");
3026
+ function BadgeRibbon({ text, color, placement = "end", className = "", children, ...props }) {
3027
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: `sia-ribbon-wrap ${className}`.trim(), ...withTitleTooltip(props), children: [
3028
+ children,
3029
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: `sia-ribbon sia-ribbon--${placement}`, style: { backgroundColor: color }, children: text })
3030
+ ] });
3031
+ }
3032
+ function Badge({ count, showZero = false, overflowCount = 99, dot = false, status, text, color, offset = [0, 0], size = "default", className = "", children, style, ...props }) {
3033
+ const numeric = typeof count === "number";
3034
+ const hidden = !dot && !status && (count === void 0 || count === null || count === 0 && !showZero);
3035
+ const display = numeric && count > overflowCount ? `${overflowCount}+` : count;
3036
+ const badge = !hidden ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("sup", { className: `sia-badge__count${dot || status ? " sia-badge__dot" : ""}${status ? ` sia-badge__dot--${status}` : ""} sia-badge__count--${size}`, style: { backgroundColor: color, transform: `translate(calc(50% + ${offset[0]}px), calc(-50% + ${offset[1]}px))` }, children: dot || status ? null : display }) : null;
3037
+ if (status && !children) return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("span", { className: `sia-badge sia-badge--status ${className}`.trim(), style, ...withTitleTooltip(props), children: [
3038
+ badge,
3039
+ text ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "sia-badge__text", children: text }) : null
3040
+ ] });
3041
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("span", { className: `sia-badge ${className}`.trim(), style, ...withTitleTooltip(props), children: [
3042
+ children,
3043
+ badge,
3044
+ text && !status ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "sia-badge__text", children: text }) : null
3045
+ ] });
3046
+ }
3047
+ Badge.Ribbon = BadgeRibbon;
3048
+ function CollapsePanel({
3049
+ open,
3050
+ forceRender = false,
3051
+ destroyInactivePanel = false,
3052
+ children
3053
+ }) {
3054
+ const [keepChildren, setKeepChildren] = (0, import_react18.useState)(open || forceRender || !destroyInactivePanel);
3055
+ (0, import_react18.useEffect)(() => {
3056
+ if (open || forceRender || !destroyInactivePanel) {
3057
+ setKeepChildren(true);
3058
+ return void 0;
3059
+ }
3060
+ const timer = window.setTimeout(() => setKeepChildren(false), 240);
3061
+ return () => window.clearTimeout(timer);
3062
+ }, [destroyInactivePanel, forceRender, open]);
3063
+ const renderChildren = open || forceRender || !destroyInactivePanel || keepChildren;
3064
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "sia-collapse__panel", "aria-hidden": !open, children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "sia-collapse__panel-motion", children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "sia-collapse__panel-body", children: renderChildren ? children : null }) }) });
3065
+ }
3066
+ function Collapse({ items, activeKey, defaultActiveKey = [], accordion = false, bordered = true, ghost = false, destroyInactivePanel = false, expandIconPosition = "start", onChange, className = "", ...props }) {
3067
+ const normalize = (key) => Array.isArray(key) ? [...key] : key === void 0 ? [] : [key];
3068
+ const controlled = activeKey !== void 0;
3069
+ const [internal, setInternal] = (0, import_react18.useState)(normalize(defaultActiveKey));
3070
+ const openKeys = controlled ? normalize(activeKey) : internal;
3071
+ function toggle(item) {
3072
+ if (item.disabled || item.collapsible === "disabled") return;
3073
+ const open = openKeys.includes(item.key);
3074
+ const next = accordion ? open ? [] : [item.key] : open ? openKeys.filter((key) => key !== item.key) : [...openKeys, item.key];
3075
+ if (!controlled) setInternal(next);
3076
+ onChange?.(accordion ? next[0] ?? "" : next);
3263
3077
  }
3078
+ const borderClassName = !bordered || ghost ? "sia-collapse--borderless" : "sia-collapse--bordered";
3079
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: `sia-collapse ${borderClassName}${ghost ? " sia-collapse--ghost" : ""} sia-collapse--icon-${expandIconPosition} ${className}`.trim(), ...withTitleTooltip(props), children: items.map((item) => {
3080
+ const open = openKeys.includes(item.key);
3081
+ const arrow = item.showArrow === false ? null : /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("button", { type: "button", className: "sia-collapse__arrow", "aria-label": open ? "\u6536\u8D77" : "\u5C55\u5F00", onClick: item.collapsible === "icon" ? () => toggle(item) : void 0, children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Icon, { name: "chevron-right", size: 14 }) });
3082
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("section", { className: `sia-collapse__item${open ? " is-open" : ""}${item.disabled ? " is-disabled" : ""} ${item.className ?? ""}`.trim(), children: [
3083
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("header", { className: "sia-collapse__header", role: "button", tabIndex: item.disabled ? -1 : 0, "aria-expanded": open, onClick: item.collapsible === "icon" ? void 0 : () => toggle(item), onKeyDown: (event) => {
3084
+ if (event.key === "Enter" || event.key === " ") {
3085
+ event.preventDefault();
3086
+ toggle(item);
3087
+ }
3088
+ }, children: [
3089
+ expandIconPosition === "start" ? arrow : null,
3090
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "sia-collapse__label", children: item.label }),
3091
+ item.extra ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "sia-collapse__extra", onClick: (event) => event.stopPropagation(), children: item.extra }) : null,
3092
+ expandIconPosition === "end" ? arrow : null
3093
+ ] }),
3094
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(CollapsePanel, { open, forceRender: item.forceRender, destroyInactivePanel, children: item.children })
3095
+ ] }, item.key);
3096
+ }) });
3097
+ }
3098
+ function ImagePreview({ open, src, alt = "", onOpenChange }) {
3099
+ const previewRef = (0, import_react18.useRef)(null);
3100
+ const stageRef = (0, import_react18.useRef)(null);
3101
+ const [natural, setNatural] = (0, import_react18.useState)({ src: "", width: 0, height: 0 });
3102
+ const [area, setArea] = (0, import_react18.useState)({ width: 0, height: 0 });
3103
+ const [zoom, setZoom] = (0, import_react18.useState)(1);
3104
+ const [rotation, setRotation] = (0, import_react18.useState)(0);
3105
+ const [flip, setFlip] = (0, import_react18.useState)({ x: 1, y: 1 });
3106
+ const [pan, setPan] = (0, import_react18.useState)({ x: 0, y: 0 });
3107
+ const [failed, setFailed] = (0, import_react18.useState)(false);
3108
+ const drag = (0, import_react18.useRef)();
3109
+ useOverlayLifecycle(open, previewRef, () => onOpenChange(false));
3110
+ const reset = () => {
3111
+ setZoom(1);
3112
+ setRotation(0);
3113
+ setFlip({ x: 1, y: 1 });
3114
+ setPan({ x: 0, y: 0 });
3115
+ drag.current = void 0;
3116
+ };
3117
+ (0, import_react18.useEffect)(() => {
3118
+ reset();
3119
+ setFailed(false);
3120
+ }, [open, src]);
3121
+ (0, import_react18.useEffect)(() => {
3122
+ const stage = stageRef.current;
3123
+ if (!open || !stage) return;
3124
+ const measure = () => {
3125
+ setArea({ width: stage.clientWidth, height: stage.clientHeight });
3126
+ setPan({ x: 0, y: 0 });
3127
+ };
3128
+ measure();
3129
+ const observer = new ResizeObserver(measure);
3130
+ observer.observe(stage);
3131
+ return () => observer.disconnect();
3132
+ }, [open, src]);
3133
+ const ready = natural.src === src && natural.width > 0 && area.width > 0 && area.height > 0 && !failed;
3134
+ const sideways = Math.abs(rotation % 180) === 90;
3135
+ const fit = ready ? Math.min(1, area.width / (sideways ? natural.height : natural.width), area.height / (sideways ? natural.width : natural.height)) : 0;
3136
+ const changeZoom = (factor) => setZoom((value) => Math.max(0.25, Math.min(8, value * factor)));
3137
+ if (!open || !src || typeof document === "undefined") return null;
3138
+ return (0, import_react_dom5.createPortal)(/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { ref: previewRef, className: "sia-image-preview", role: "dialog", "aria-modal": "true", "aria-label": "\u56FE\u7247\u9884\u89C8", tabIndex: -1, onClick: () => onOpenChange(false), children: [
3139
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Button, { variant: "text", className: "sia-image-preview__close", "aria-label": "\u5173\u95ED\u9884\u89C8", onClick: (event) => {
3140
+ event.stopPropagation();
3141
+ onOpenChange(false);
3142
+ }, children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Icon, { name: "close" }) }),
3143
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { ref: stageRef, className: "sia-image-preview__stage", children: failed ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "sia-image-preview__error", children: "\u56FE\u7247\u52A0\u8F7D\u5931\u8D25" }) : /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
3144
+ "img",
3145
+ {
3146
+ src,
3147
+ alt,
3148
+ draggable: false,
3149
+ style: { width: natural.width * fit, height: natural.height * fit, visibility: ready ? "visible" : "hidden", transform: `translate(${pan.x}px, ${pan.y}px) rotate(${rotation}deg) scale(${zoom * flip.x}, ${zoom * flip.y})` },
3150
+ onLoad: (event) => setNatural({ src, width: event.currentTarget.naturalWidth, height: event.currentTarget.naturalHeight }),
3151
+ onError: () => setFailed(true),
3152
+ onClick: (event) => event.stopPropagation(),
3153
+ onPointerDown: (event) => {
3154
+ if (event.button !== 0) return;
3155
+ event.preventDefault();
3156
+ event.stopPropagation();
3157
+ event.currentTarget.setPointerCapture(event.pointerId);
3158
+ drag.current = { id: event.pointerId, x: event.clientX, y: event.clientY, left: pan.x, top: pan.y };
3159
+ },
3160
+ onPointerMove: (event) => {
3161
+ const start = drag.current;
3162
+ if (start?.id === event.pointerId) setPan({ x: start.left + event.clientX - start.x, y: start.top + event.clientY - start.y });
3163
+ },
3164
+ onPointerUp: () => {
3165
+ drag.current = void 0;
3166
+ },
3167
+ onPointerCancel: () => {
3168
+ drag.current = void 0;
3169
+ },
3170
+ onLostPointerCapture: () => {
3171
+ drag.current = void 0;
3172
+ }
3173
+ },
3174
+ src
3175
+ ) }),
3176
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "sia-image-preview__toolbar", role: "toolbar", "aria-label": "\u56FE\u7247\u64CD\u4F5C", onClick: (event) => event.stopPropagation(), children: [
3177
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Button, { variant: "text", title: "\u6C34\u5E73\u7FFB\u8F6C", "aria-label": "\u6C34\u5E73\u7FFB\u8F6C", disabled: !ready, onClick: () => setFlip((value) => ({ ...value, x: -value.x })), children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Icon, { name: "flip-horizontal", size: 20 }) }),
3178
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Button, { variant: "text", title: "\u5782\u76F4\u7FFB\u8F6C", "aria-label": "\u5782\u76F4\u7FFB\u8F6C", disabled: !ready, onClick: () => setFlip((value) => ({ ...value, y: -value.y })), children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Icon, { name: "flip-vertical", size: 20 }) }),
3179
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Button, { variant: "text", title: "\u5411\u5DE6\u65CB\u8F6C", "aria-label": "\u5411\u5DE6\u65CB\u8F6C", disabled: !ready, onClick: () => {
3180
+ setRotation((value) => value - 90);
3181
+ setPan({ x: 0, y: 0 });
3182
+ }, children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Icon, { name: "rotate-left", size: 20 }) }),
3183
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Button, { variant: "text", title: "\u5411\u53F3\u65CB\u8F6C", "aria-label": "\u5411\u53F3\u65CB\u8F6C", disabled: !ready, onClick: () => {
3184
+ setRotation((value) => value + 90);
3185
+ setPan({ x: 0, y: 0 });
3186
+ }, children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Icon, { name: "rotate-right", size: 20 }) }),
3187
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Button, { variant: "text", title: "\u7F29\u5C0F", "aria-label": "\u7F29\u5C0F", disabled: !ready || zoom <= 0.25, onClick: () => changeZoom(1 / 1.25), children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Icon, { name: "zoom-out", size: 20 }) }),
3188
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("output", { className: "sia-image-preview__scale", "aria-label": "\u5F53\u524D\u7F29\u653E\u6BD4\u4F8B", "aria-live": "polite", children: ready ? `${Math.round(fit * zoom * 100)}%` : "\u2014" }),
3189
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Button, { variant: "text", title: "\u653E\u5927", "aria-label": "\u653E\u5927", disabled: !ready || zoom >= 8, onClick: () => changeZoom(1.25), children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Icon, { name: "zoom-in", size: 20 }) }),
3190
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Button, { variant: "text", title: "\u9002\u5E94\u7A97\u53E3", "aria-label": "\u9002\u5E94\u7A97\u53E3", disabled: !ready, onClick: reset, children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Icon, { name: "refresh", size: 20 }) })
3191
+ ] })
3192
+ ] }), document.body);
3193
+ }
3194
+
3195
+ // src/components/FloatingPlaceholder.tsx
3196
+ var import_jsx_runtime16 = require("react/jsx-runtime");
3197
+ function useFloatingPlaceholder({ mode, placeholder, filled, active = false }) {
3198
+ const enabled = usePlaceholderMode(mode) === "floating" && Boolean(placeholder);
3264
3199
  return {
3265
- top,
3266
- right,
3267
- bottom,
3268
- left,
3269
- width: Math.max(0, right - left),
3270
- height: Math.max(0, bottom - top)
3200
+ enabled,
3201
+ className: enabled ? ` sia-placeholder-floating${filled ? " sia-placeholder-floating--filled" : ""}${active ? " sia-placeholder-floating--active" : ""}` : "",
3202
+ label: enabled ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "sia-floating-placeholder", "aria-hidden": "true", children: placeholder }) : null
3271
3203
  };
3272
3204
  }
3273
- function createTrack(axis) {
3274
- const track = document.createElement("div");
3275
- const thumb = document.createElement("div");
3276
- track.className = `sia-scrollbar-track sia-floating-scrollbar sia-floating-scrollbar--${axis}`;
3277
- thumb.className = "sia-floating-scrollbar__thumb";
3278
- track.dataset.axis = axis;
3279
- track.setAttribute("aria-hidden", "true");
3280
- track.append(thumb);
3281
- return { track, thumb };
3282
- }
3283
- function FloatingScrollbarProvider({ disabled = false, targetRef }) {
3284
- useScrollbarProximity();
3285
- (0, import_react20.useEffect)(() => {
3286
- if (disabled) return;
3287
- const layer = document.createElement("div");
3288
- layer.className = "sia-floating-scrollbar-layer";
3289
- layer.setAttribute("aria-hidden", "true");
3290
- document.body.append(layer);
3291
- const entries = /* @__PURE__ */ new Map();
3292
- let scanFrame = 0;
3293
- let updateFrame = 0;
3294
- const setEntryVisible = (entry, visible) => {
3295
- entry.horizontalTrack.classList.toggle("sia-floating-scrollbar--visible", visible);
3296
- entry.verticalTrack.classList.toggle("sia-floating-scrollbar--visible", visible);
3297
- };
3298
- const showEntry = (entry) => {
3299
- if (entry.hideTimer !== null) window.clearTimeout(entry.hideTimer);
3300
- entry.hideTimer = null;
3301
- setEntryVisible(entry, true);
3302
- };
3303
- const scheduleHide = (entry, delay = 500) => {
3304
- if (entry.hideTimer !== null) window.clearTimeout(entry.hideTimer);
3305
- entry.hideTimer = window.setTimeout(() => {
3306
- entry.hideTimer = null;
3307
- if (!entry.hoveringTarget && !entry.hoveringTrack && !entry.dragging) setEntryVisible(entry, false);
3308
- }, delay);
3309
- };
3310
- const updateEntry = (entry) => {
3311
- const { target, horizontalTrack, horizontalThumb, verticalTrack, verticalThumb } = entry;
3312
- const hasHorizontal = canScroll(target, "horizontal");
3313
- const hasVertical = canScroll(target, "vertical");
3314
- const rect = visibleRect(target);
3315
- const thickness = 10;
3316
- horizontalTrack.hidden = !hasHorizontal || rect.width <= thickness || rect.height <= 0;
3317
- verticalTrack.hidden = !hasVertical || rect.height <= thickness || rect.width <= 0;
3318
- if (!horizontalTrack.hidden) {
3319
- const trackWidth = Math.max(0, rect.width - (hasVertical ? thickness : 0));
3320
- const thumbWidth = Math.max(24, Math.min(trackWidth, trackWidth * target.clientWidth / target.scrollWidth));
3321
- const travel = Math.max(0, trackWidth - thumbWidth);
3322
- const maxScroll = Math.max(1, target.scrollWidth - target.clientWidth);
3323
- horizontalTrack.style.left = `${rect.left}px`;
3324
- horizontalTrack.style.top = `${rect.bottom - thickness}px`;
3325
- horizontalTrack.style.width = `${trackWidth}px`;
3326
- horizontalTrack.style.height = `${thickness}px`;
3327
- horizontalThumb.style.width = `${thumbWidth}px`;
3328
- horizontalThumb.style.transform = `translate3d(${travel * target.scrollLeft / maxScroll}px, 0, 0)`;
3329
- }
3330
- if (!verticalTrack.hidden) {
3331
- const trackHeight = Math.max(0, rect.height - (hasHorizontal ? thickness : 0));
3332
- const thumbHeight = Math.max(24, Math.min(trackHeight, trackHeight * target.clientHeight / target.scrollHeight));
3333
- const travel = Math.max(0, trackHeight - thumbHeight);
3334
- const maxScroll = Math.max(1, target.scrollHeight - target.clientHeight);
3335
- verticalTrack.style.left = `${rect.right - thickness}px`;
3336
- verticalTrack.style.top = `${rect.top}px`;
3337
- verticalTrack.style.width = `${thickness}px`;
3338
- verticalTrack.style.height = `${trackHeight}px`;
3339
- verticalThumb.style.height = `${thumbHeight}px`;
3340
- verticalThumb.style.transform = `translate3d(0, ${travel * target.scrollTop / maxScroll}px, 0)`;
3341
- }
3342
- };
3343
- const scheduleUpdateAll = () => {
3344
- if (updateFrame) return;
3345
- updateFrame = window.requestAnimationFrame(() => {
3346
- updateFrame = 0;
3347
- entries.forEach(updateEntry);
3348
- });
3349
- };
3350
- const bindTrackDrag = (entry, axis, track, thumb) => {
3351
- const target = entry.target;
3352
- const onTrackPointerEnter = () => {
3353
- entry.hoveringTrack = true;
3354
- showEntry(entry);
3355
- };
3356
- const onTrackPointerLeave = () => {
3357
- entry.hoveringTrack = false;
3358
- scheduleHide(entry);
3359
- };
3360
- const onTrackPointerDown = (event) => {
3361
- if (event.target === thumb || event.button !== 0) return;
3362
- event.preventDefault();
3363
- event.stopPropagation();
3364
- const trackRect = track.getBoundingClientRect();
3365
- const thumbRect = thumb.getBoundingClientRect();
3366
- const trackLength = axis === "vertical" ? trackRect.height : trackRect.width;
3367
- const thumbLength = axis === "vertical" ? thumbRect.height : thumbRect.width;
3368
- const pointer = axis === "vertical" ? event.clientY - trackRect.top : event.clientX - trackRect.left;
3369
- const ratio = Math.max(0, Math.min(1, (pointer - thumbLength / 2) / Math.max(1, trackLength - thumbLength)));
3370
- if (axis === "vertical") target.scrollTop = ratio * (target.scrollHeight - target.clientHeight);
3371
- else target.scrollLeft = ratio * (target.scrollWidth - target.clientWidth);
3372
- };
3373
- const onThumbPointerDown = (event) => {
3374
- if (event.button !== 0) return;
3375
- event.preventDefault();
3376
- event.stopPropagation();
3377
- entry.dragging = true;
3378
- showEntry(entry);
3379
- thumb.setPointerCapture(event.pointerId);
3380
- const startPointer = axis === "vertical" ? event.clientY : event.clientX;
3381
- const startScroll = axis === "vertical" ? target.scrollTop : target.scrollLeft;
3382
- const trackRect = track.getBoundingClientRect();
3383
- const thumbRect = thumb.getBoundingClientRect();
3384
- const trackLength = axis === "vertical" ? trackRect.height : trackRect.width;
3385
- const thumbLength = axis === "vertical" ? thumbRect.height : thumbRect.width;
3386
- const maxScroll = axis === "vertical" ? target.scrollHeight - target.clientHeight : target.scrollWidth - target.clientWidth;
3387
- const travel = Math.max(1, trackLength - thumbLength);
3388
- const onPointerMove = (moveEvent) => {
3389
- if (moveEvent.pointerId !== event.pointerId) return;
3390
- const pointer = axis === "vertical" ? moveEvent.clientY : moveEvent.clientX;
3391
- const nextScroll = startScroll + (pointer - startPointer) * maxScroll / travel;
3392
- if (axis === "vertical") target.scrollTop = nextScroll;
3393
- else target.scrollLeft = nextScroll;
3394
- };
3395
- const onPointerUp = (upEvent) => {
3396
- if (upEvent.pointerId !== event.pointerId) return;
3397
- entry.dragging = false;
3398
- thumb.releasePointerCapture(event.pointerId);
3399
- window.removeEventListener("pointermove", onPointerMove);
3400
- window.removeEventListener("pointerup", onPointerUp);
3401
- window.removeEventListener("pointercancel", onPointerUp);
3402
- scheduleHide(entry);
3403
- };
3404
- window.addEventListener("pointermove", onPointerMove);
3405
- window.addEventListener("pointerup", onPointerUp);
3406
- window.addEventListener("pointercancel", onPointerUp);
3407
- };
3408
- track.addEventListener("pointerenter", onTrackPointerEnter);
3409
- track.addEventListener("pointerleave", onTrackPointerLeave);
3410
- track.addEventListener("pointerdown", onTrackPointerDown);
3411
- thumb.addEventListener("pointerdown", onThumbPointerDown);
3412
- return () => {
3413
- track.removeEventListener("pointerenter", onTrackPointerEnter);
3414
- track.removeEventListener("pointerleave", onTrackPointerLeave);
3415
- track.removeEventListener("pointerdown", onTrackPointerDown);
3416
- thumb.removeEventListener("pointerdown", onThumbPointerDown);
3417
- };
3418
- };
3419
- const resizeObserver = new ResizeObserver((observedEntries) => {
3420
- observedEntries.forEach(({ target }) => {
3421
- const entry = entries.get(target);
3422
- if (entry) updateEntry(entry);
3423
- });
3424
- });
3425
- const addTarget = (target) => {
3426
- if (entries.has(target)) return;
3427
- const horizontal = createTrack("horizontal");
3428
- const vertical = createTrack("vertical");
3429
- layer.append(horizontal.track, vertical.track);
3430
- const onPointerEnter = () => {
3431
- const entry2 = entries.get(target);
3432
- if (!entry2) return;
3433
- entry2.hoveringTarget = true;
3434
- showEntry(entry2);
3435
- };
3436
- const onPointerLeave = () => {
3437
- const entry2 = entries.get(target);
3438
- if (!entry2) return;
3439
- entry2.hoveringTarget = false;
3440
- scheduleHide(entry2);
3441
- };
3442
- const onScroll = () => {
3443
- const entry2 = entries.get(target);
3444
- if (!entry2) return;
3445
- updateEntry(entry2);
3446
- showEntry(entry2);
3447
- scheduleHide(entry2, 700);
3448
- };
3449
- target.addEventListener("pointerenter", onPointerEnter);
3450
- target.addEventListener("pointerleave", onPointerLeave);
3451
- target.addEventListener("scroll", onScroll, { passive: true });
3452
- const entry = {
3453
- target,
3454
- horizontalTrack: horizontal.track,
3455
- horizontalThumb: horizontal.thumb,
3456
- verticalTrack: vertical.track,
3457
- verticalThumb: vertical.thumb,
3458
- hideTimer: null,
3459
- hoveringTarget: target.matches(":hover"),
3460
- hoveringTrack: false,
3461
- dragging: false,
3462
- cleanup: () => void 0
3463
- };
3464
- const cleanupHorizontal = bindTrackDrag(entry, "horizontal", horizontal.track, horizontal.thumb);
3465
- const cleanupVertical = bindTrackDrag(entry, "vertical", vertical.track, vertical.thumb);
3466
- entry.cleanup = () => {
3467
- if (entry.hideTimer !== null) window.clearTimeout(entry.hideTimer);
3468
- cleanupHorizontal();
3469
- cleanupVertical();
3470
- target.removeEventListener("pointerenter", onPointerEnter);
3471
- target.removeEventListener("pointerleave", onPointerLeave);
3472
- target.removeEventListener("scroll", onScroll);
3473
- resizeObserver.unobserve(target);
3474
- target.removeAttribute("data-sia-floating-scrollbar");
3475
- horizontal.track.remove();
3476
- vertical.track.remove();
3477
- };
3478
- entries.set(target, entry);
3479
- target.setAttribute("data-sia-floating-scrollbar", "");
3480
- resizeObserver.observe(target);
3481
- updateEntry(entry);
3482
- if (entry.hoveringTarget) showEntry(entry);
3483
- };
3484
- const removeTarget = (target) => {
3485
- const entry = entries.get(target);
3486
- if (!entry) return;
3487
- entry.cleanup();
3488
- entries.delete(target);
3489
- };
3490
- const scanTargets = () => {
3491
- scanFrame = 0;
3492
- const managedSelector = ".sia-table__viewport, [data-sia-native-scrollbar], .sia-scrollbar-track, .sia-floating-scrollbar-layer";
3493
- const discovered = [];
3494
- if (!targetRef) {
3495
- const walker = document.createTreeWalker(document.body, NodeFilter.SHOW_ELEMENT, {
3496
- acceptNode: (node) => node.matches(managedSelector) ? NodeFilter.FILTER_REJECT : NodeFilter.FILTER_ACCEPT
3497
- });
3498
- for (let node = walker.nextNode(); node; node = walker.nextNode()) {
3499
- if (node instanceof HTMLElement) discovered.push(node);
3500
- }
3205
+
3206
+ // src/components/Select.tsx
3207
+ var import_react23 = require("react");
3208
+
3209
+ // src/components/Modal.tsx
3210
+ var import_react19 = require("react");
3211
+ var import_client3 = require("react-dom/client");
3212
+ var import_jsx_runtime17 = require("react/jsx-runtime");
3213
+ var modalHandles = /* @__PURE__ */ new Set();
3214
+ var MODAL_MOTION_DURATION = 220;
3215
+ var typeIcons = {
3216
+ info: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(Icon, { name: "info", size: 23 }),
3217
+ success: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(Icon, { name: "circle-check", size: 23 }),
3218
+ warning: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(Icon, { name: "warning", size: 23 }),
3219
+ error: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(Icon, { name: "circle-close", size: 23 }),
3220
+ confirm: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(Icon, { name: "question", size: 23 })
3221
+ };
3222
+ var ModalRoot = (0, import_react19.forwardRef)(function Modal({
3223
+ open,
3224
+ title,
3225
+ children,
3226
+ footer,
3227
+ footerExtra,
3228
+ type = "default",
3229
+ okText = "\u786E\u5B9A",
3230
+ cancelText = "\u53D6\u6D88",
3231
+ showCancel = type === "default" || type === "confirm",
3232
+ confirmLoading = false,
3233
+ okButtonProps,
3234
+ cancelButtonProps,
3235
+ closable = true,
3236
+ maskClosable = true,
3237
+ keyboard = true,
3238
+ centered = false,
3239
+ width = 520,
3240
+ height,
3241
+ zIndex = 1e3,
3242
+ getContainer,
3243
+ onOk,
3244
+ onCancel,
3245
+ onOpenChange,
3246
+ afterOpenChange,
3247
+ className = "",
3248
+ style,
3249
+ ...props
3250
+ }, forwardedRef) {
3251
+ const localRef = (0, import_react19.useRef)(null);
3252
+ const titleId = (0, import_react19.useId)();
3253
+ const [submitting, setSubmitting] = (0, import_react19.useState)(false);
3254
+ const [rendered, setRendered] = (0, import_react19.useState)(open);
3255
+ const [closing, setClosing] = (0, import_react19.useState)(false);
3256
+ const afterOpenChangeRef = (0, import_react19.useRef)(afterOpenChange);
3257
+ afterOpenChangeRef.current = afterOpenChange;
3258
+ const submitLock = (0, import_react19.useRef)(false);
3259
+ (0, import_react19.useEffect)(() => {
3260
+ if (!open || !rendered) return void 0;
3261
+ const timer = window.setTimeout(() => afterOpenChangeRef.current?.(true), MODAL_MOTION_DURATION);
3262
+ return () => window.clearTimeout(timer);
3263
+ }, [open, rendered]);
3264
+ (0, import_react19.useEffect)(() => {
3265
+ if (open) {
3266
+ setRendered(true);
3267
+ setClosing(false);
3268
+ return void 0;
3269
+ }
3270
+ if (!rendered) return void 0;
3271
+ setClosing(true);
3272
+ const timer = window.setTimeout(() => {
3273
+ setRendered(false);
3274
+ setClosing(false);
3275
+ afterOpenChangeRef.current?.(false);
3276
+ }, MODAL_MOTION_DURATION);
3277
+ return () => window.clearTimeout(timer);
3278
+ }, [open, rendered]);
3279
+ function setRef(node) {
3280
+ localRef.current = node;
3281
+ if (typeof forwardedRef === "function") forwardedRef(node);
3282
+ else if (forwardedRef) forwardedRef.current = node;
3283
+ }
3284
+ function requestClose() {
3285
+ if (submitting || confirmLoading) return;
3286
+ onCancel?.();
3287
+ onOpenChange?.(false);
3288
+ }
3289
+ async function handleOk() {
3290
+ if (submitLock.current || confirmLoading) return;
3291
+ submitLock.current = true;
3292
+ if (!onOk) {
3293
+ onOpenChange?.(false);
3294
+ submitLock.current = false;
3295
+ return;
3296
+ }
3297
+ try {
3298
+ const result = onOk();
3299
+ if (result instanceof Promise) {
3300
+ setSubmitting(true);
3301
+ const resolved = await result;
3302
+ if (resolved !== false) onOpenChange?.(false);
3303
+ } else if (result !== false) {
3304
+ onOpenChange?.(false);
3501
3305
  }
3502
- const candidates = (targetRef ? [targetRef.current] : [document.scrollingElement, ...discovered]).filter((target) => target instanceof HTMLElement);
3503
- const nextTargets = /* @__PURE__ */ new Set();
3504
- candidates.forEach((target) => {
3505
- if (canScroll(target, "horizontal") || canScroll(target, "vertical")) {
3506
- nextTargets.add(target);
3507
- if (entries.has(target) || !target.hasAttribute("data-sia-floating-scrollbar")) addTarget(target);
3306
+ } catch {
3307
+ return;
3308
+ } finally {
3309
+ submitLock.current = false;
3310
+ setSubmitting(false);
3311
+ }
3312
+ }
3313
+ useOverlayLifecycle(rendered, localRef, requestClose, keyboard);
3314
+ if (!rendered) return null;
3315
+ const mergedStyle = {
3316
+ ...height !== void 0 ? { display: "flex", flexDirection: "column", height, maxHeight: "calc(100dvh - 48px)" } : {},
3317
+ ...style,
3318
+ "--sia-modal-width": typeof width === "number" ? `${width}px` : width
3319
+ };
3320
+ const loading = submitting || confirmLoading;
3321
+ const defaultFooter = /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_jsx_runtime17.Fragment, { children: [
3322
+ showCancel ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(Button, { ...cancelButtonProps, onClick: requestClose, disabled: loading || cancelButtonProps?.disabled, children: cancelText }) : null,
3323
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(Button, { variant: "primary", ...okButtonProps, onClick: handleOk, loading, children: okText })
3324
+ ] });
3325
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(OverlayPortal, { container: getContainer, children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
3326
+ "div",
3327
+ {
3328
+ className: `sia-modal-root${centered ? " sia-modal-root--centered" : ""}`,
3329
+ "data-state": closing ? "closing" : "open",
3330
+ style: { zIndex },
3331
+ onMouseDown: (event) => {
3332
+ if (event.target === event.currentTarget && maskClosable) requestClose();
3333
+ },
3334
+ children: /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
3335
+ "div",
3336
+ {
3337
+ ref: setRef,
3338
+ className: `sia-modal sia-modal--${type} ${className}`.trim(),
3339
+ style: mergedStyle,
3340
+ role: "dialog",
3341
+ "aria-modal": "true",
3342
+ "aria-labelledby": title ? titleId : void 0,
3343
+ tabIndex: -1,
3344
+ ...withTitleTooltip(props),
3345
+ children: [
3346
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "sia-modal__header", style: height !== void 0 ? { flexShrink: 0 } : void 0, children: [
3347
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "sia-modal__heading", children: [
3348
+ typeIcons[type] ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "sia-modal__type-icon", "aria-hidden": "true", children: typeIcons[type] }) : null,
3349
+ title ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("strong", { id: titleId, children: title }) : null
3350
+ ] }),
3351
+ closable ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("button", { type: "button", className: "sia-modal__close", "aria-label": "\u5173\u95ED\u5BF9\u8BDD\u6846", onClick: requestClose, children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(Icon, { name: "close", size: 17 }) }) : null
3352
+ ] }),
3353
+ children !== void 0 ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "sia-modal__body", style: height !== void 0 ? { flex: 1, minHeight: 0, overflow: "auto" } : void 0, children }) : null,
3354
+ footer !== null ? /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "sia-modal__footer", style: height !== void 0 ? { flexShrink: 0 } : void 0, children: [
3355
+ footer === void 0 && footerExtra != null ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { style: { marginRight: "auto" }, children: footerExtra }) : null,
3356
+ footer ?? defaultFooter
3357
+ ] }) : null
3358
+ ]
3508
3359
  }
3509
- });
3510
- entries.forEach((_entry, target) => {
3511
- if (!target.isConnected || !nextTargets.has(target)) removeTarget(target);
3512
- else updateEntry(_entry);
3513
- });
3514
- };
3515
- const scheduleScan = () => {
3516
- if (scanFrame) return;
3517
- scanFrame = window.requestAnimationFrame(scanTargets);
3518
- };
3519
- const mutationObserver = new MutationObserver((mutations) => {
3520
- if (mutations.some((mutation) => {
3521
- const target = mutation.target instanceof Element ? mutation.target : mutation.target.parentElement;
3522
- return target && !layer.contains(target) && !target.closest(".sia-table__viewport, [data-sia-native-scrollbar], .sia-scrollbar-track");
3523
- })) scheduleScan();
3524
- });
3525
- mutationObserver.observe(document.documentElement, { subtree: true, childList: true, attributes: true, attributeFilter: ["class", "style", "hidden"] });
3526
- window.addEventListener("resize", scheduleScan);
3527
- window.addEventListener("scroll", scheduleUpdateAll, true);
3528
- scheduleScan();
3529
- return () => {
3530
- mutationObserver.disconnect();
3531
- resizeObserver.disconnect();
3532
- window.removeEventListener("resize", scheduleScan);
3533
- window.removeEventListener("scroll", scheduleUpdateAll, true);
3534
- if (scanFrame) window.cancelAnimationFrame(scanFrame);
3535
- if (updateFrame) window.cancelAnimationFrame(updateFrame);
3536
- entries.forEach((entry) => entry.cleanup());
3537
- entries.clear();
3538
- layer.remove();
3539
- };
3540
- }, [disabled, targetRef]);
3541
- return null;
3360
+ )
3361
+ }
3362
+ ) });
3363
+ });
3364
+ function openModal(config) {
3365
+ if (typeof document === "undefined") return { destroy() {
3366
+ }, update() {
3367
+ } };
3368
+ const container = document.createElement("div");
3369
+ container.className = "sia-modal-api-host";
3370
+ document.body.appendChild(container);
3371
+ const root = (0, import_client3.createRoot)(container);
3372
+ let currentConfig = config;
3373
+ let currentOpen = true;
3374
+ let destroyed = false;
3375
+ let cleanupTimer;
3376
+ function cleanup2() {
3377
+ if (destroyed) return;
3378
+ destroyed = true;
3379
+ if (cleanupTimer !== void 0) window.clearTimeout(cleanupTimer);
3380
+ root.unmount();
3381
+ container.remove();
3382
+ modalHandles.delete(handle);
3383
+ }
3384
+ function closeWithMotion() {
3385
+ if (destroyed || !currentOpen) return;
3386
+ currentOpen = false;
3387
+ render();
3388
+ cleanupTimer = window.setTimeout(cleanup2, MODAL_MOTION_DURATION);
3389
+ }
3390
+ function render() {
3391
+ root.render(/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(ModalRoot, { ...currentConfig, open: currentOpen, onOpenChange: (nextOpen) => {
3392
+ currentConfig.onOpenChange?.(nextOpen);
3393
+ if (!nextOpen) closeWithMotion();
3394
+ } }));
3395
+ }
3396
+ const handle = {
3397
+ destroy: closeWithMotion,
3398
+ update(next) {
3399
+ if (destroyed) return;
3400
+ currentConfig = { ...currentConfig, ...next };
3401
+ render();
3402
+ }
3403
+ };
3404
+ modalHandles.add(handle);
3405
+ render();
3406
+ return handle;
3407
+ }
3408
+ function preset(type, defaults = {}) {
3409
+ return (config) => openModal({
3410
+ okText: type === "confirm" ? "\u786E\u5B9A" : "\u77E5\u9053\u4E86",
3411
+ showCancel: type === "confirm",
3412
+ closable: type !== "confirm",
3413
+ maskClosable: type !== "confirm",
3414
+ ...defaults,
3415
+ ...config,
3416
+ type
3417
+ });
3542
3418
  }
3419
+ var Modal2 = Object.assign(ModalRoot, {
3420
+ /** 直接打开实例,返回可用于更新或关闭的操作句柄。 */
3421
+ open: openModal,
3422
+ /** 显示普通信息提示。 */
3423
+ info: preset("info", { title: "\u63D0\u793A" }),
3424
+ /** 显示成功提示。 */
3425
+ success: preset("success", { title: "\u64CD\u4F5C\u6210\u529F" }),
3426
+ /** 显示警告提示。 */
3427
+ warning: preset("warning", { title: "\u8BF7\u6CE8\u610F" }),
3428
+ /** 显示错误提示。 */
3429
+ error: preset("error", { title: "\u64CD\u4F5C\u5931\u8D25" }),
3430
+ /** 打开确认弹框,支持异步确认;返回 false 可阻止关闭。 */
3431
+ confirm: preset("confirm", { title: "\u786E\u8BA4\u64CD\u4F5C" }),
3432
+ /** 关闭由此 API 打开的所有弹层。 */
3433
+ destroyAll() {
3434
+ [...modalHandles].forEach((handle) => handle.destroy());
3435
+ }
3436
+ });
3437
+
3438
+ // src/components/Input.tsx
3439
+ var import_react20 = require("react");
3440
+ var import_jsx_runtime18 = require("react/jsx-runtime");
3441
+ var Input = (0, import_react20.forwardRef)(function Input2({
3442
+ label,
3443
+ hint,
3444
+ status = "default",
3445
+ size: sizeProp,
3446
+ prefix,
3447
+ suffix,
3448
+ allowClear = true,
3449
+ onClear,
3450
+ showCount = false,
3451
+ placeholderMode,
3452
+ placeholder,
3453
+ className = "",
3454
+ id,
3455
+ disabled,
3456
+ value,
3457
+ defaultValue,
3458
+ maxLength,
3459
+ onChange,
3460
+ ...props
3461
+ }, forwardedRef) {
3462
+ const size = useControlSize(sizeProp);
3463
+ const fallbackId = (0, import_react20.useId)();
3464
+ const inputId = id ?? fallbackId;
3465
+ const localRef = (0, import_react20.useRef)(null);
3466
+ const [uncontrolledValue, setUncontrolledValue] = (0, import_react20.useState)(() => defaultValue === void 0 ? "" : String(defaultValue));
3467
+ const currentText = value !== void 0 ? String(value) : uncontrolledValue;
3468
+ const hasValue = currentText.length > 0;
3469
+ const floatingPlaceholder = usePlaceholderMode(placeholderMode) === "floating" && Boolean(placeholder);
3470
+ const hintId = `${inputId}-hint`;
3471
+ const describedBy = [props["aria-describedby"], hint ? hintId : null].filter(Boolean).join(" ") || void 0;
3472
+ function setRef(node) {
3473
+ localRef.current = node;
3474
+ if (typeof forwardedRef === "function") forwardedRef(node);
3475
+ else if (forwardedRef) forwardedRef.current = node;
3476
+ }
3477
+ function handleChange(event) {
3478
+ if (maxLength !== void 0 && maxLength >= 0 && event.target.value.length > maxLength) {
3479
+ const valueSetter = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, "value")?.set;
3480
+ valueSetter?.call(event.target, event.target.value.slice(0, maxLength));
3481
+ }
3482
+ setUncontrolledValue(event.target.value);
3483
+ onChange?.(event);
3484
+ }
3485
+ function clearValue() {
3486
+ const input2 = localRef.current;
3487
+ if (!input2 || disabled) return;
3488
+ const valueSetter = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, "value")?.set;
3489
+ valueSetter?.call(input2, "");
3490
+ input2.dispatchEvent(new Event("input", { bubbles: true }));
3491
+ setUncontrolledValue("");
3492
+ input2.focus();
3493
+ onClear?.();
3494
+ }
3495
+ function handleControlMouseDown(event) {
3496
+ if (disabled || event.defaultPrevented || event.button !== 0) return;
3497
+ const target = event.target;
3498
+ if (!(target instanceof Element)) return;
3499
+ const interactive = target.closest("input, textarea, select, button, a[href], [tabindex], [contenteditable]:not([contenteditable='false']), [role='button'], [role='link']");
3500
+ if (interactive && event.currentTarget.contains(interactive) && !interactive.classList.contains("sia-input-control__clear")) return;
3501
+ event.preventDefault();
3502
+ localRef.current?.focus({ preventScroll: true });
3503
+ }
3504
+ const input = /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
3505
+ "input",
3506
+ {
3507
+ ...withTitleTooltip(props),
3508
+ ref: setRef,
3509
+ id: inputId,
3510
+ className: "sia-input",
3511
+ disabled,
3512
+ value,
3513
+ defaultValue,
3514
+ maxLength,
3515
+ placeholder,
3516
+ "aria-describedby": describedBy,
3517
+ "aria-invalid": status === "error" || void 0,
3518
+ onChange: handleChange
3519
+ }
3520
+ );
3521
+ const control = /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("span", { onMouseDown: handleControlMouseDown, className: `sia-input-control sia-input-control--${size} sia-input-control--${status}${floatingPlaceholder ? " sia-input-control--floating" : ""}${floatingPlaceholder && hasValue ? " is-filled" : ""}${disabled ? " is-disabled" : ""}`, children: [
3522
+ prefix ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "sia-input-control__affix", children: prefix }) : null,
3523
+ floatingPlaceholder ? /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("span", { className: "sia-input-control__input-wrap", children: [
3524
+ input,
3525
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "sia-input-control__floating-placeholder", "aria-hidden": "true", children: placeholder })
3526
+ ] }) : input,
3527
+ allowClear && hasValue && !disabled ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("button", { className: "sia-input-control__clear", type: "button", "aria-label": "\u6E05\u7A7A\u8F93\u5165", onClick: clearValue, children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(Icon, { name: "circle-close", variant: "filled", size: 15 }) }) : null,
3528
+ suffix ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "sia-input-control__affix", children: suffix }) : null,
3529
+ showCount ? /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("span", { className: `sia-input-control__count${maxLength !== void 0 && currentText.length >= maxLength ? " is-limit" : ""}`, "aria-live": "polite", children: [
3530
+ currentText.length,
3531
+ maxLength !== void 0 ? `/${maxLength}` : null
3532
+ ] }) : null
3533
+ ] });
3534
+ if (!label && !hint) return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: `sia-field ${className}`.trim(), children: control });
3535
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("span", { className: `sia-field ${className}`.trim(), children: [
3536
+ label ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("label", { className: "sia-field__label", htmlFor: inputId, children: label }) : null,
3537
+ control,
3538
+ hint ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { id: hintId, className: `sia-field__hint sia-field__hint--${status}`, children: hint }) : null
3539
+ ] });
3540
+ });
3543
3541
 
3544
3542
  // src/components/SelectionPanel.tsx
3543
+ var import_react21 = require("react");
3545
3544
  var import_jsx_runtime19 = require("react/jsx-runtime");
3546
3545
  function SelectionPanel({
3547
3546
  options,
@@ -4528,7 +4527,7 @@ function UploadItem({ file, files, props, actions }) {
4528
4527
  }
4529
4528
  (0, import_react24.useEffect)(() => setFailed(false), [source]);
4530
4529
  const node = /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: `sia-upload__item sia-upload__item--${file.status ?? "ready"}`, children: [
4531
- props.listType !== "text" ? /* @__PURE__ */ (0, import_jsx_runtime23.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_runtime23.jsx)("img", { src: source, alt: file.name, onError: () => setFailed(true) }) : /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(Icon, { name: file.type?.startsWith("image/") ? "image" : "file", size: 28 }) }) : /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(Icon, { name: file.status === "done" ? "circle-check" : "file", size: 16 }),
4530
+ props.listType !== "text" ? /* @__PURE__ */ (0, import_jsx_runtime23.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_runtime23.jsx)("img", { src: source, alt: file.name, onError: () => setFailed(true) }) : /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(Icon, { name: file.type?.startsWith("image/") ? "image" : "file", size: 28 }) }) : /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { className: "sia-upload__file-icon", children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(Icon, { name: file.status === "done" ? "circle-check" : "file", size: 24 }) }),
4532
4531
  /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { className: "sia-upload__name", ...withTitleTooltip({ title: file.relativePath || file.name }), children: file.url ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("a", { href: file.url, target: "_blank", rel: "noreferrer", children: file.name }) : file.name }),
4533
4532
  /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("span", { className: "sia-upload__actions", children: [
4534
4533
  /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(Button, { variant: "text", title: "\u9884\u89C8", "aria-label": `\u9884\u89C8 ${file.name}`, disabled: !canPreview, onClick: actions.preview, children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(Icon, { name: "eye", size: 14 }) }),
@@ -4543,7 +4542,8 @@ function UploadItem({ file, files, props, actions }) {
4543
4542
  ] }),
4544
4543
  /* @__PURE__ */ (0, import_jsx_runtime23.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_runtime23.jsx)("span", { style: { width: `${file.percent ?? 0}%` } }) })
4545
4544
  ] }),
4546
- file.status === "error" && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { className: "sia-upload__error", children: "\u4E0A\u4F20\u5931\u8D25" })
4545
+ file.status === "error" && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { className: "sia-upload__error", children: "\u4E0A\u4F20\u5931\u8D25" }),
4546
+ props.listType === "text" && (file.status === void 0 || file.status === "ready" || file.status === "done") && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { className: "sia-upload__status", children: file.status === "done" ? "\u4E0A\u4F20\u5B8C\u6210" : "\u5DF2\u9009\u62E9" })
4547
4547
  ] });
4548
4548
  return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_jsx_runtime23.Fragment, { children: props.itemRender ? props.itemRender(node, file, files, actions) : node });
4549
4549
  }
@@ -4822,6 +4822,10 @@ var UploadRoot = (0, import_react24.forwardRef)(function UploadRoot2({
4822
4822
  }
4823
4823
  ),
4824
4824
  listType !== "picture-card" && trigger,
4825
+ showUploadList && listType === "text" && files.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: "sia-upload__list-heading", role: "status", children: [
4826
+ "\u5DF2\u9009\u6587\u4EF6 ",
4827
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { className: "sia-upload__count", children: files.length })
4828
+ ] }),
4825
4829
  /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: "sia-upload__list", children: [
4826
4830
  showUploadList && files.map((file) => /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(UploadItem, { file, files, props: { listType, disabled, itemRender, onPreview, downloadRequest, accept }, actions: {
4827
4831
  upload: () => start(file),
@@ -4860,7 +4864,7 @@ var UploadDragger = (0, import_react24.forwardRef)(function UploadDragger2({ hin
4860
4864
  ] }) });
4861
4865
  });
4862
4866
  var Upload = Object.assign(UploadRoot, {
4863
- /** 可点击、键盘操作和拖拽的上传区域,ref 支持手动上传、取消及移除。 */
4867
+ /** 可点击、键盘操作和拖拽的上传区域,ref 支持手动上传、取消及移除;默认内容在文字或图片列表非空且可见时收紧高度,清空后恢复。 */
4864
4868
  Dragger: UploadDragger,
4865
4869
  /** beforeUpload 返回时移除该文件,不进行上传。 */
4866
4870
  LIST_IGNORE