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

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