@sia.soul/sia-react-ui 0.1.37 → 0.1.39

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
@@ -1831,1530 +1831,1536 @@ function Tag({
1831
1831
  }
1832
1832
 
1833
1833
  // src/components/Tabs.tsx
1834
+ var import_react12 = require("react");
1835
+
1836
+ // src/components/FloatingScrollbarProvider.tsx
1837
+ var import_react11 = require("react");
1838
+
1839
+ // src/components/useScrollbarProximity.ts
1834
1840
  var import_react10 = require("react");
1835
- var import_jsx_runtime11 = require("react/jsx-runtime");
1836
- function Tabs({
1837
- items,
1838
- activeKey,
1839
- defaultActiveKey,
1840
- onChange,
1841
- onAfterChange,
1842
- deferContent = false,
1843
- type = "line",
1844
- size = "medium",
1845
- tabPosition,
1846
- orientation = "horizontal",
1847
- centered = false,
1848
- tabBarTitle,
1849
- tabBarExtraContent,
1850
- contentGap,
1851
- destroyInactiveTabPane = true,
1852
- closable = false,
1853
- onEdit,
1854
- tabContextMenu,
1855
- onTabContextMenuClick,
1856
- className = "",
1857
- ...props
1858
- }) {
1859
- const firstEnabledKey = items.find((item) => !item.disabled)?.key ?? "";
1860
- const [internalActiveKey, setInternalActiveKey] = (0, import_react10.useState)(defaultActiveKey ?? firstEnabledKey);
1861
- const currentKey = activeKey ?? internalActiveKey;
1862
- const selectedKey = items.some((item) => item.key === currentKey && !item.disabled) ? currentKey : firstEnabledKey;
1863
- const baseId = (0, import_react10.useId)().replace(/:/g, "");
1864
- const enabledItems = (0, import_react10.useMemo)(() => items.filter((item) => !item.disabled), [items]);
1865
- const resolvedPosition = tabPosition ?? (orientation === "vertical" ? "left" : "top");
1866
- const resolvedOrientation = resolvedPosition === "left" || resolvedPosition === "right" ? "vertical" : "horizontal";
1867
- const sliding = ["capsule", "line", "underline", "tech", "tech-line"].includes(type);
1868
- const listRef = (0, import_react10.useRef)(null);
1869
- const indicatorRef = (0, import_react10.useRef)(null);
1870
- const [contentState, setContentState] = (0, import_react10.useState)(() => ({ key: selectedKey, visited: /* @__PURE__ */ new Set([selectedKey]) }));
1871
- const settledKeyRef = (0, import_react10.useRef)(selectedKey);
1872
- const afterChangeRef = (0, import_react10.useRef)(onAfterChange);
1873
- (0, import_react10.useLayoutEffect)(() => {
1874
- afterChangeRef.current = onAfterChange;
1875
- }, [onAfterChange]);
1876
- (0, import_react10.useLayoutEffect)(() => {
1877
- if (!sliding) return;
1878
- const list = listRef.current;
1879
- const indicator = indicatorRef.current;
1880
- if (!list || !indicator) return;
1881
- function updateIndicator() {
1882
- if (!list || !indicator) return;
1883
- const active = list.querySelector('[role="tab"][aria-selected="true"]');
1884
- if (!active) {
1885
- indicator.style.visibility = "hidden";
1886
- return;
1887
- }
1888
- const listRect = list.getBoundingClientRect();
1889
- const rect = active.getBoundingClientRect();
1890
- indicator.style.transform = `translate(${rect.left - listRect.left - list.clientLeft + list.scrollLeft}px, ${rect.top - listRect.top - list.clientTop + list.scrollTop}px)`;
1891
- indicator.style.width = `${rect.width}px`;
1892
- indicator.style.height = `${rect.height}px`;
1893
- indicator.style.visibility = "visible";
1894
- if (!indicator.dataset.ready) {
1895
- indicator.getBoundingClientRect();
1896
- indicator.dataset.ready = "true";
1897
- }
1898
- }
1899
- updateIndicator();
1900
- const observer = new ResizeObserver(updateIndicator);
1901
- observer.observe(list);
1902
- list.querySelectorAll('[role="tab"]').forEach((tab) => observer.observe(tab));
1903
- return () => observer.disconnect();
1904
- }, [sliding, type, selectedKey, items, size, resolvedPosition, centered, closable]);
1841
+ var users = 0;
1842
+ var cleanup;
1843
+ function useScrollbarProximity() {
1905
1844
  (0, import_react10.useEffect)(() => {
1906
- let cancelled = false;
1907
- let frame = 0;
1908
- frame = requestAnimationFrame(() => {
1909
- const indicator = indicatorRef.current;
1910
- if (indicator) getComputedStyle(indicator).transform;
1911
- const animations = indicator?.getAnimations() ?? [];
1912
- const complete = () => {
1913
- if (cancelled) return;
1914
- setContentState((previous) => ({ key: selectedKey, visited: new Set([...previous.visited].filter((key) => items.some((item) => item.key === key))).add(selectedKey) }));
1915
- if (settledKeyRef.current !== selectedKey) {
1916
- settledKeyRef.current = selectedKey;
1917
- afterChangeRef.current?.(selectedKey);
1918
- }
1845
+ if (users++ === 0) {
1846
+ let frame = 0;
1847
+ let x = -Infinity;
1848
+ let y = -Infinity;
1849
+ const nearby = /* @__PURE__ */ new Set();
1850
+ const clear = () => {
1851
+ nearby.forEach((track) => track.removeAttribute("data-scrollbar-near"));
1852
+ nearby.clear();
1919
1853
  };
1920
- if (!animations.length) complete();
1921
- else Promise.allSettled(animations.map((animation) => animation.finished)).then(() => {
1922
- if (!cancelled) frame = requestAnimationFrame(complete);
1923
- });
1924
- });
1854
+ const update = () => {
1855
+ frame = 0;
1856
+ const next = /* @__PURE__ */ new Set();
1857
+ document.querySelectorAll(".sia-scrollbar-track").forEach((track) => {
1858
+ const rect = track.getBoundingClientRect();
1859
+ const proximity = Number.parseFloat(getComputedStyle(track).getPropertyValue("--sia-scrollbar-proximity")) || 16;
1860
+ if (rect.width && rect.height && x >= rect.left - proximity && x <= rect.right + proximity && y >= rect.top - proximity && y <= rect.bottom + proximity) {
1861
+ next.add(track);
1862
+ if (!nearby.has(track)) track.setAttribute("data-scrollbar-near", "true");
1863
+ }
1864
+ });
1865
+ nearby.forEach((track) => {
1866
+ if (!next.has(track)) track.removeAttribute("data-scrollbar-near");
1867
+ });
1868
+ nearby.clear();
1869
+ next.forEach((track) => nearby.add(track));
1870
+ };
1871
+ const move = (event) => {
1872
+ if (event.pointerType === "touch") return;
1873
+ x = event.clientX;
1874
+ y = event.clientY;
1875
+ if (!frame) frame = requestAnimationFrame(update);
1876
+ };
1877
+ const leave = () => {
1878
+ x = y = -Infinity;
1879
+ clear();
1880
+ };
1881
+ document.addEventListener("pointermove", move, { passive: true });
1882
+ document.documentElement.addEventListener("pointerleave", leave);
1883
+ window.addEventListener("blur", leave);
1884
+ cleanup = () => {
1885
+ if (frame) cancelAnimationFrame(frame);
1886
+ document.removeEventListener("pointermove", move);
1887
+ document.documentElement.removeEventListener("pointerleave", leave);
1888
+ window.removeEventListener("blur", leave);
1889
+ clear();
1890
+ };
1891
+ }
1925
1892
  return () => {
1926
- cancelled = true;
1927
- cancelAnimationFrame(frame);
1893
+ if (--users === 0) {
1894
+ cleanup?.();
1895
+ cleanup = void 0;
1896
+ }
1928
1897
  };
1929
- }, [selectedKey, type, deferContent]);
1930
- const contentKey = deferContent ? contentState.key : selectedKey;
1931
- function selectTab(key) {
1932
- if (key === selectedKey) return;
1933
- if (activeKey === void 0) setInternalActiveKey(key);
1934
- onChange?.(key);
1898
+ }, []);
1899
+ }
1900
+
1901
+ // src/components/FloatingScrollbarProvider.tsx
1902
+ var scrollableOverflow = /* @__PURE__ */ new Set(["auto", "scroll", "overlay"]);
1903
+ var clippingOverflow = /* @__PURE__ */ new Set(["auto", "scroll", "overlay", "hidden", "clip"]);
1904
+ function isRootScroller(target) {
1905
+ return target === document.scrollingElement || target === document.documentElement;
1906
+ }
1907
+ function canScroll(target, axis) {
1908
+ if (target.matches(".sia-table__viewport, [data-sia-native-scrollbar]")) return false;
1909
+ if (isRootScroller(target)) {
1910
+ return axis === "horizontal" ? target.scrollWidth > target.clientWidth + 1 : target.scrollHeight > target.clientHeight + 1;
1935
1911
  }
1936
- function focusAndSelect(key) {
1937
- selectTab(key);
1938
- document.getElementById(`${baseId}-tab-${key}`)?.focus();
1912
+ const style = getComputedStyle(target);
1913
+ const overflow = axis === "horizontal" ? style.overflowX : style.overflowY;
1914
+ const hasOverflow = axis === "horizontal" ? target.scrollWidth > target.clientWidth + 1 : target.scrollHeight > target.clientHeight + 1;
1915
+ return hasOverflow && scrollableOverflow.has(overflow);
1916
+ }
1917
+ function visibleRect(target) {
1918
+ if (isRootScroller(target)) {
1919
+ return { top: 0, right: window.innerWidth, bottom: window.innerHeight, left: 0, width: window.innerWidth, height: window.innerHeight };
1939
1920
  }
1940
- function handleKeyDown(event, key) {
1941
- const previousKey = resolvedOrientation === "horizontal" ? "ArrowLeft" : "ArrowUp";
1942
- const nextKey = resolvedOrientation === "horizontal" ? "ArrowRight" : "ArrowDown";
1943
- if (![previousKey, nextKey, "Home", "End"].includes(event.key) || enabledItems.length === 0) return;
1944
- event.preventDefault();
1945
- const currentIndex = Math.max(0, enabledItems.findIndex((item) => item.key === key));
1946
- if (event.key === "Home") return focusAndSelect(enabledItems[0].key);
1947
- if (event.key === "End") return focusAndSelect(enabledItems[enabledItems.length - 1].key);
1948
- const offset = event.key === nextKey ? 1 : -1;
1949
- const nextIndex = (currentIndex + offset + enabledItems.length) % enabledItems.length;
1950
- focusAndSelect(enabledItems[nextIndex].key);
1921
+ const ownRect = target.getBoundingClientRect();
1922
+ let top = Math.max(0, ownRect.top);
1923
+ let right = Math.min(window.innerWidth, ownRect.right);
1924
+ let bottom = Math.min(window.innerHeight, ownRect.bottom);
1925
+ let left = Math.max(0, ownRect.left);
1926
+ let parent = target.parentElement;
1927
+ while (parent && parent !== document.body && parent !== document.documentElement) {
1928
+ const style = getComputedStyle(parent);
1929
+ const clipX = clippingOverflow.has(style.overflowX);
1930
+ const clipY = clippingOverflow.has(style.overflowY);
1931
+ if (clipX || clipY) {
1932
+ const rect = parent.getBoundingClientRect();
1933
+ if (clipX) {
1934
+ left = Math.max(left, rect.left);
1935
+ right = Math.min(right, rect.right);
1936
+ }
1937
+ if (clipY) {
1938
+ top = Math.max(top, rect.top);
1939
+ bottom = Math.min(bottom, rect.bottom);
1940
+ }
1941
+ }
1942
+ parent = parent.parentElement;
1951
1943
  }
1952
- const hasPanels = items.some((item) => item.children !== void 0);
1953
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
1954
- "div",
1955
- {
1956
- className: `sia-tabs sia-tabs--${type}${type === "underline" ? " sia-tabs--line" : ""} sia-tabs--${size} sia-tabs--${resolvedOrientation} sia-tabs--${resolvedPosition}${centered ? " sia-tabs--centered" : ""} ${className}`.trim(),
1957
- ...withTitleTooltip(props),
1958
- children: [
1959
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "sia-tabs__header", children: [
1960
- tabBarTitle ? /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "sia-tabs__title", children: [
1961
- tabBarTitle,
1962
- type === "tech-line" ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("svg", { className: "sia-tabs__title-connector", viewBox: "0 0 32 40", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("path", { d: "M0 39.25 H5 Q8 39.25 10 35.25 L28 3.75 Q29 1.75 32 1.75", fill: "none", stroke: "currentColor", strokeWidth: "1.5", strokeLinejoin: "round" }) }) : null
1963
- ] }) : null,
1964
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { ref: listRef, className: `sia-tabs__list${sliding ? " sia-tabs__list--sliding" : ""}`, role: "tablist", "aria-orientation": resolvedOrientation, children: [
1965
- sliding ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { ref: indicatorRef, className: `sia-tabs__indicator${type === "capsule" ? " sia-tabs__capsule-indicator" : ""}`, "aria-hidden": "true", children: type === "tech-line" ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "sia-tabs__indicator-shape", "aria-hidden": "true" }) : null }, type) : null,
1966
- items.map((item) => {
1967
- const selected = item.key === selectedKey;
1968
- const canClose = item.closable ?? closable;
1969
- const tabNode = /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("span", { className: `sia-tabs__tab-wrap${canClose ? " is-closable" : ""}`, children: [
1970
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
1971
- "button",
1972
- {
1973
- id: `${baseId}-tab-${item.key}`,
1974
- type: "button",
1975
- className: "sia-tabs__tab",
1976
- role: "tab",
1977
- "aria-selected": selected,
1978
- "aria-controls": hasPanels ? `${baseId}-panel-${item.key}` : void 0,
1979
- tabIndex: selected ? 0 : -1,
1980
- disabled: item.disabled,
1981
- onClick: () => selectTab(item.key),
1982
- onKeyDown: (event) => handleKeyDown(event, item.key),
1983
- children: [
1984
- type === "tech-line" ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "sia-tabs__tab-shape", "aria-hidden": "true" }) : null,
1985
- item.icon ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "sia-tabs__icon", children: item.icon }) : null,
1986
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { children: item.label })
1987
- ]
1988
- }
1989
- ),
1990
- canClose ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1991
- "button",
1992
- {
1993
- type: "button",
1994
- className: "sia-tabs__close",
1995
- "aria-label": `\u5173\u95ED${typeof item.label === "string" ? item.label : "\u6807\u7B7E\u9875"}`,
1996
- disabled: item.disabled,
1997
- onClick: () => onEdit?.(item.key, "remove"),
1998
- children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Icon, { name: "close", size: 12 })
1999
- }
2000
- ) : null
2001
- ] }, item.key);
2002
- const contextMenuItems = typeof tabContextMenu === "function" ? tabContextMenu(item) : tabContextMenu;
2003
- if (!contextMenuItems?.length) return tabNode;
2004
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
2005
- Dropdown,
2006
- {
2007
- className: "sia-tabs__tab-context-trigger",
2008
- popupClassName: "sia-tabs__tab-context-menu",
2009
- trigger: ["contextMenu"],
2010
- tabIndex: -1,
2011
- menu: {
2012
- items: contextMenuItems,
2013
- selectable: false,
2014
- onClick: (info) => onTabContextMenuClick?.({ ...info, tabKey: item.key, tab: item })
2015
- },
2016
- children: tabNode
2017
- },
2018
- item.key
2019
- );
2020
- })
2021
- ] }),
2022
- tabBarExtraContent ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "sia-tabs__extra", children: tabBarExtraContent }) : null
2023
- ] }),
2024
- hasPanels ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "sia-tabs__panels", style: contentGap === void 0 ? void 0 : {
2025
- // 仅设置面板靠近导航的一侧,保留各方向布局原有的其余边界。
2026
- [resolvedPosition === "top" ? "paddingTop" : resolvedPosition === "bottom" ? "paddingBottom" : resolvedPosition === "left" ? "paddingLeft" : "paddingRight"]: contentGap
2027
- }, "aria-busy": deferContent && contentKey !== selectedKey || void 0, children: items.map((item) => {
2028
- const selected = item.key === contentKey;
2029
- if (destroyInactiveTabPane && !selected) return null;
2030
- if (deferContent && !selected && !contentState.visited.has(item.key)) return null;
2031
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
2032
- "div",
2033
- {
2034
- id: `${baseId}-panel-${item.key}`,
2035
- className: "sia-tabs__panel",
2036
- role: "tabpanel",
2037
- "aria-labelledby": `${baseId}-tab-${item.key}`,
2038
- hidden: !selected,
2039
- tabIndex: 0,
2040
- children: item.children
2041
- },
2042
- item.key
2043
- );
2044
- }) }) : null
2045
- ]
2046
- }
2047
- );
2048
- }
2049
-
2050
- // src/components/Feedback.tsx
2051
- var import_react13 = require("react");
2052
- var import_react_dom3 = require("react-dom");
2053
- var import_client2 = require("react-dom/client");
2054
-
2055
- // src/components/FloatingDisplay.tsx
2056
- var import_react12 = require("react");
2057
- var import_react_dom2 = require("react-dom");
2058
-
2059
- // src/components/Checkbox.tsx
2060
- var import_react11 = require("react");
2061
- var import_jsx_runtime12 = require("react/jsx-runtime");
2062
- var CheckboxGroupContext = (0, import_react11.createContext)(null);
2063
- var CheckboxRoot = (0, import_react11.forwardRef)(function Checkbox({ value, size: sizeProp, indeterminate = false, children, disabled, checked, defaultChecked, className = "", onChange, ...props }, forwardedRef) {
2064
- const size = useControlSize(sizeProp);
2065
- const group = (0, import_react11.useContext)(CheckboxGroupContext);
2066
- const localRef = (0, import_react11.useRef)(null);
2067
- const fallbackId = (0, import_react11.useId)();
2068
- const grouped = group !== null && value !== void 0;
2069
- const currentChecked = grouped ? group.values.includes(value) : checked;
2070
- (0, import_react11.useEffect)(() => {
2071
- if (localRef.current) localRef.current.indeterminate = indeterminate;
2072
- }, [indeterminate]);
2073
- function setRef(node) {
2074
- localRef.current = node;
2075
- if (typeof forwardedRef === "function") forwardedRef(node);
2076
- else if (forwardedRef) forwardedRef.current = node;
2077
- }
2078
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("label", { className: `sia-checkbox sia-checkbox--${size}${disabled || group?.disabled ? " is-disabled" : ""} ${className}`.trim(), children: [
2079
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
2080
- "input",
2081
- {
2082
- ...withTitleTooltip(props),
2083
- ref: setRef,
2084
- id: props.id ?? fallbackId,
2085
- className: "sia-checkbox__input",
2086
- type: "checkbox",
2087
- name: group?.name ?? props.name,
2088
- disabled: disabled || group?.disabled,
2089
- checked: currentChecked,
2090
- defaultChecked: grouped ? void 0 : defaultChecked,
2091
- "aria-checked": indeterminate ? "mixed" : currentChecked,
2092
- onChange: (event) => {
2093
- if (grouped) group.toggle(value, event.currentTarget.checked);
2094
- onChange?.(event.currentTarget.checked, event);
2095
- }
2096
- }
2097
- ),
2098
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "sia-checkbox__control", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", {}) }),
2099
- children !== void 0 ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "sia-checkbox__label", children }) : null
2100
- ] });
2101
- });
2102
- function CheckboxGroup({
2103
- size: sizeProp,
2104
- columns,
2105
- direction = "horizontal",
2106
- gap,
2107
- value,
2108
- defaultValue = [],
2109
- options,
2110
- disabled,
2111
- name,
2112
- className = "",
2113
- children,
2114
- onChange
2115
- }) {
2116
- const size = useControlSize(sizeProp);
2117
- const gridColumns = columns !== void 0 && Number.isInteger(columns) && columns > 0 ? columns : void 0;
2118
- const layoutStyle = {
2119
- gridTemplateColumns: gridColumns ? `repeat(${gridColumns}, minmax(0, 1fr))` : void 0,
2120
- gap: gap === void 0 ? void 0 : typeof gap === "number" ? Math.max(0, gap) : `${Math.max(0, gap[0])}px ${Math.max(0, gap[1])}px`
1944
+ return {
1945
+ top,
1946
+ right,
1947
+ bottom,
1948
+ left,
1949
+ width: Math.max(0, right - left),
1950
+ height: Math.max(0, bottom - top)
2121
1951
  };
2122
- const [values, setValues] = useControllableState({ value, defaultValue, onChange });
2123
- const context = (0, import_react11.useMemo)(() => ({
2124
- values,
2125
- disabled,
2126
- name,
2127
- toggle(optionValue, checked) {
2128
- const next = checked ? values.includes(optionValue) ? values : [...values, optionValue] : values.filter((item) => item !== optionValue);
2129
- setValues(next);
2130
- }
2131
- }), [disabled, name, setValues, values]);
2132
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(ControlSizeContext.Provider, { value: size, children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(CheckboxGroupContext.Provider, { value: context, children: /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: `sia-checkbox-group${gridColumns ? " sia-checkbox-group--grid" : direction === "vertical" ? " sia-checkbox-group--vertical" : ""} ${className}`.trim(), style: layoutStyle, role: "group", children: [
2133
- options?.map((option) => {
2134
- const normalized = typeof option === "object" ? option : { label: String(option), value: option };
2135
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(CheckboxRoot, { value: normalized.value, disabled: normalized.disabled, children: normalized.label }, normalized.value);
2136
- }),
2137
- children
2138
- ] }) }) });
2139
- }
2140
- var Checkbox2 = Object.assign(CheckboxRoot, {
2141
- /** 组合多个子项并统一管理布局或选择状态。 */
2142
- Group: CheckboxGroup
2143
- });
2144
-
2145
- // src/components/FloatingDisplay.tsx
2146
- var import_jsx_runtime13 = require("react/jsx-runtime");
2147
- var FLOATING_GAP = 9;
2148
- var FLOATING_VIEWPORT_PADDING = 8;
2149
- var FLOATING_ARROW_EDGE_OFFSET = 16;
2150
- var activeTooltipClose;
2151
- function clampFloating(value, min, max) {
2152
- return Math.min(Math.max(value, min), Math.max(min, max));
2153
1952
  }
2154
- function resolveFloatingPosition(trigger, overlay, placement) {
2155
- const viewportWidth = document.documentElement.clientWidth;
2156
- const viewportHeight = document.documentElement.clientHeight;
2157
- let resolvedPlacement = placement;
2158
- if (placement.startsWith("top") && trigger.top - overlay.height - FLOATING_GAP < FLOATING_VIEWPORT_PADDING && trigger.bottom + overlay.height + FLOATING_GAP <= viewportHeight - FLOATING_VIEWPORT_PADDING) {
2159
- resolvedPlacement = placement.replace("top", "bottom");
2160
- } else if (placement.startsWith("bottom") && trigger.bottom + overlay.height + FLOATING_GAP > viewportHeight - FLOATING_VIEWPORT_PADDING && trigger.top - overlay.height - FLOATING_GAP >= FLOATING_VIEWPORT_PADDING) {
2161
- resolvedPlacement = placement.replace("bottom", "top");
2162
- } else if (placement === "left" && trigger.left - overlay.width - FLOATING_GAP < FLOATING_VIEWPORT_PADDING && trigger.right + overlay.width + FLOATING_GAP <= viewportWidth - FLOATING_VIEWPORT_PADDING) {
2163
- resolvedPlacement = "right";
2164
- } else if (placement === "right" && trigger.right + overlay.width + FLOATING_GAP > viewportWidth - FLOATING_VIEWPORT_PADDING && trigger.left - overlay.width - FLOATING_GAP >= FLOATING_VIEWPORT_PADDING) {
2165
- resolvedPlacement = "left";
2166
- }
2167
- let top = trigger.top;
2168
- let left = trigger.left;
2169
- if (resolvedPlacement.startsWith("top") || resolvedPlacement.startsWith("bottom")) {
2170
- top = resolvedPlacement.startsWith("top") ? trigger.top - overlay.height - FLOATING_GAP : trigger.bottom + FLOATING_GAP;
2171
- left = resolvedPlacement.endsWith("-start") ? trigger.left : resolvedPlacement.endsWith("-end") ? trigger.right - overlay.width : trigger.left + (trigger.width - overlay.width) / 2;
2172
- } else {
2173
- top = trigger.top + (trigger.height - overlay.height) / 2;
2174
- left = resolvedPlacement === "left" ? trigger.left - overlay.width - FLOATING_GAP : trigger.right + FLOATING_GAP;
2175
- }
2176
- top = clampFloating(top, FLOATING_VIEWPORT_PADDING, viewportHeight - overlay.height - FLOATING_VIEWPORT_PADDING);
2177
- left = clampFloating(left, FLOATING_VIEWPORT_PADDING, viewportWidth - overlay.width - FLOATING_VIEWPORT_PADDING);
2178
- const result = { top, left, placement: resolvedPlacement };
2179
- if (resolvedPlacement.startsWith("top") || resolvedPlacement.startsWith("bottom")) {
2180
- result.arrowX = resolvedPlacement.endsWith("-start") ? clampFloating(FLOATING_ARROW_EDGE_OFFSET, 8, overlay.width - 16) : resolvedPlacement.endsWith("-end") ? clampFloating(overlay.width - FLOATING_ARROW_EDGE_OFFSET - 8, 8, overlay.width - 16) : clampFloating(trigger.left + trigger.width / 2 - left - 4, 8, overlay.width - 16);
2181
- } else {
2182
- result.arrowY = clampFloating(trigger.top + trigger.height / 2 - top - 4, 8, overlay.height - 16);
2183
- }
2184
- return result;
1953
+ function createTrack(axis) {
1954
+ const track = document.createElement("div");
1955
+ const thumb = document.createElement("div");
1956
+ track.className = `sia-scrollbar-track sia-floating-scrollbar sia-floating-scrollbar--${axis}`;
1957
+ thumb.className = "sia-floating-scrollbar__thumb";
1958
+ track.dataset.axis = axis;
1959
+ track.setAttribute("aria-hidden", "true");
1960
+ track.append(thumb);
1961
+ return { track, thumb };
2185
1962
  }
2186
- function Floating({
2187
- children,
2188
- content,
2189
- placement = "top",
2190
- trigger = "hover",
2191
- open,
2192
- defaultOpen = false,
2193
- mouseEnterDelay = 0.1,
2194
- mouseLeaveDelay = 0.1,
2195
- arrow = true,
2196
- color,
2197
- className = "",
2198
- overlayClassName = "",
2199
- role = "tooltip",
2200
- onOpenChange
2201
- }) {
2202
- const rootRef = (0, import_react12.useRef)(null);
2203
- const overlayRef = (0, import_react12.useRef)(null);
2204
- const enterTimer = (0, import_react12.useRef)();
2205
- const leaveTimer = (0, import_react12.useRef)();
2206
- const contentId = (0, import_react12.useId)();
2207
- const triggers = Array.isArray(trigger) ? trigger : [trigger];
2208
- const [visible, setVisible] = useControllableState({ value: open, defaultValue: defaultOpen, onChange: onOpenChange });
2209
- const [position, setPosition] = (0, import_react12.useState)();
2210
- const setVisibleRef = (0, import_react12.useRef)(setVisible);
2211
- setVisibleRef.current = setVisible;
2212
- const closeSelfRef = (0, import_react12.useRef)();
2213
- if (!closeSelfRef.current) closeSelfRef.current = () => setVisibleRef.current(false);
2214
- const exclusiveTooltip = role === "tooltip" && triggers.includes("hover");
2215
- function openNow() {
2216
- if (exclusiveTooltip && activeTooltipClose !== closeSelfRef.current) {
2217
- activeTooltipClose?.();
2218
- activeTooltipClose = closeSelfRef.current;
2219
- }
2220
- setVisible(true);
2221
- }
2222
- function closeNow() {
2223
- if (activeTooltipClose === closeSelfRef.current) activeTooltipClose = void 0;
2224
- setVisible(false);
2225
- }
2226
- function clearTimers() {
2227
- window.clearTimeout(enterTimer.current);
2228
- window.clearTimeout(leaveTimer.current);
2229
- }
2230
- function show(delay = 0) {
2231
- clearTimers();
2232
- if (delay <= 0) openNow();
2233
- else enterTimer.current = window.setTimeout(openNow, delay * 1e3);
2234
- }
2235
- function hide(delay = 0) {
2236
- clearTimers();
2237
- if (delay <= 0) closeNow();
2238
- else leaveTimer.current = window.setTimeout(closeNow, delay * 1e3);
2239
- }
2240
- (0, import_react12.useEffect)(() => {
2241
- if (!visible || !triggers.includes("click")) return;
2242
- const close = (event) => {
2243
- const target = event.target;
2244
- if (!rootRef.current?.contains(target) && !overlayRef.current?.contains(target)) closeNow();
1963
+ function FloatingScrollbarProvider({ disabled = false, targetRef }) {
1964
+ useScrollbarProximity();
1965
+ (0, import_react11.useEffect)(() => {
1966
+ if (disabled) return;
1967
+ const layer = document.createElement("div");
1968
+ layer.className = "sia-floating-scrollbar-layer";
1969
+ layer.setAttribute("aria-hidden", "true");
1970
+ document.body.append(layer);
1971
+ const entries = /* @__PURE__ */ new Map();
1972
+ let scanFrame = 0;
1973
+ let updateFrame = 0;
1974
+ const setEntryVisible = (entry, visible) => {
1975
+ entry.horizontalTrack.classList.toggle("sia-floating-scrollbar--visible", visible);
1976
+ entry.verticalTrack.classList.toggle("sia-floating-scrollbar--visible", visible);
2245
1977
  };
2246
- document.addEventListener("pointerdown", close);
2247
- return () => document.removeEventListener("pointerdown", close);
2248
- }, [triggers.join("|"), visible]);
2249
- (0, import_react12.useLayoutEffect)(() => {
2250
- if (!visible) {
2251
- setPosition(void 0);
2252
- return;
2253
- }
2254
- const update = () => {
2255
- if (!rootRef.current || !overlayRef.current) return;
2256
- setPosition(resolveFloatingPosition(rootRef.current.getBoundingClientRect(), overlayRef.current.getBoundingClientRect(), placement));
1978
+ const showEntry = (entry) => {
1979
+ if (entry.hideTimer !== null) window.clearTimeout(entry.hideTimer);
1980
+ entry.hideTimer = null;
1981
+ setEntryVisible(entry, true);
2257
1982
  };
2258
- update();
2259
- window.addEventListener("resize", update);
2260
- window.addEventListener("scroll", update, true);
2261
- const observer = new ResizeObserver(update);
2262
- if (rootRef.current) observer.observe(rootRef.current);
2263
- if (overlayRef.current) observer.observe(overlayRef.current);
2264
- return () => {
2265
- window.removeEventListener("resize", update);
2266
- window.removeEventListener("scroll", update, true);
2267
- observer.disconnect();
1983
+ const scheduleHide = (entry, delay = 500) => {
1984
+ if (entry.hideTimer !== null) window.clearTimeout(entry.hideTimer);
1985
+ entry.hideTimer = window.setTimeout(() => {
1986
+ entry.hideTimer = null;
1987
+ if (!entry.hoveringTarget && !entry.hoveringTrack && !entry.dragging) setEntryVisible(entry, false);
1988
+ }, delay);
2268
1989
  };
2269
- }, [children, content, placement, visible]);
2270
- (0, import_react12.useEffect)(() => () => {
2271
- clearTimers();
2272
- if (activeTooltipClose === closeSelfRef.current) activeTooltipClose = void 0;
2273
- }, []);
2274
- const child = (0, import_react12.cloneElement)(children, {
2275
- ...triggers.includes("hover") ? { onMouseEnter: (event) => {
2276
- children.props.onMouseEnter?.(event);
2277
- show(mouseEnterDelay);
2278
- }, onMouseLeave: (event) => {
2279
- children.props.onMouseLeave?.(event);
2280
- hide(mouseLeaveDelay);
2281
- } } : {},
2282
- ...triggers.includes("focus") ? { onFocus: (event) => {
2283
- children.props.onFocus?.(event);
2284
- show();
2285
- }, onBlur: (event) => {
2286
- children.props.onBlur?.(event);
2287
- hide();
2288
- } } : {},
2289
- ...triggers.includes("click") ? { onClick: (event) => {
2290
- children.props.onClick?.(event);
2291
- if (visible) closeNow();
2292
- else openNow();
2293
- } } : {},
2294
- "aria-describedby": visible ? contentId : void 0
2295
- });
2296
- const overlay = visible && content !== null && content !== void 0 && typeof document !== "undefined" ? (0, import_react_dom2.createPortal)(
2297
- /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
2298
- "span",
2299
- {
2300
- ref: overlayRef,
2301
- id: contentId,
2302
- role,
2303
- "data-custom-color": color ? "" : void 0,
2304
- className: `sia-floating__overlay sia-floating__overlay--${position?.placement ?? placement} ${overlayClassName}`.trim(),
2305
- style: {
2306
- "--sia-floating-color": color,
2307
- "--sia-floating-arrow-x": position?.arrowX == null ? void 0 : `${position.arrowX}px`,
2308
- "--sia-floating-arrow-y": position?.arrowY == null ? void 0 : `${position.arrowY}px`,
2309
- top: position?.top ?? 0,
2310
- left: position?.left ?? 0,
2311
- visibility: position ? "visible" : "hidden"
2312
- },
2313
- onMouseEnter: triggers.includes("hover") ? () => show() : void 0,
2314
- onMouseLeave: triggers.includes("hover") ? () => hide(mouseLeaveDelay) : void 0,
2315
- children: [
2316
- content,
2317
- arrow ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "sia-floating__arrow" }) : null
2318
- ]
2319
- }
2320
- ),
2321
- document.body
2322
- ) : null;
2323
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_jsx_runtime13.Fragment, { children: [
2324
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { ref: rootRef, className: `sia-floating ${className}`.trim(), children: child }),
2325
- overlay
2326
- ] });
2327
- }
2328
- function Tooltip(props) {
2329
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Floating, { ...props, content: props.title });
2330
- }
2331
-
2332
- // src/components/Feedback.tsx
2333
- var import_jsx_runtime14 = require("react/jsx-runtime");
2334
- function Progress({
2335
- percent = 0,
2336
- type = "line",
2337
- status,
2338
- showInfo = true,
2339
- format,
2340
- strokeColor,
2341
- trailColor,
2342
- strokeWidth = 8,
2343
- width = 120,
2344
- steps,
2345
- success,
2346
- size = "default",
2347
- className = "",
2348
- style,
2349
- ...props
2350
- }) {
2351
- const safe = Math.max(0, Math.min(100, percent));
2352
- const resolved = status ?? (safe >= 100 ? "success" : "normal");
2353
- const color = typeof strokeColor === "string" ? strokeColor : void 0;
2354
- const background = typeof strokeColor === "object" ? `linear-gradient(90deg, ${strokeColor.from}, ${strokeColor.to})` : color;
2355
- 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}%`);
2356
- if (type !== "line") {
2357
- const radius = 46;
2358
- const circumference = 2 * Math.PI * radius;
2359
- const dash = circumference * (safe / 100);
2360
- 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: [
2361
- /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("svg", { viewBox: "0 0 100 100", role: "progressbar", "aria-valuenow": safe, children: [
2362
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("circle", { className: "sia-progress__trail", cx: "50", cy: "50", r: radius, style: { stroke: trailColor } }),
2363
- /* @__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 } })
2364
- ] }),
2365
- showInfo ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "sia-progress__text", children: label }) : null
2366
- ] });
2367
- }
2368
- const height = Array.isArray(size) ? size[1] : typeof size === "number" ? size : size === "small" ? 6 : strokeWidth;
2369
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: `sia-progress sia-progress--line sia-progress--${resolved} ${className}`.trim(), style, ...withTitleTooltip(props), children: [
2370
- /* @__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: [
2371
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "sia-progress__bar", style: { width: `${safe}%`, background } }),
2372
- success?.percent ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "sia-progress__success", style: { width: `${success.percent}%`, background: success.strokeColor } }) : null
2373
- ] }) }),
2374
- showInfo ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "sia-progress__info", children: label }) : null
2375
- ] });
2376
- }
2377
-
2378
- // src/components/DataDisplay.tsx
2379
- var import_react15 = require("react");
2380
- var import_react_dom5 = require("react-dom");
2381
-
2382
- // src/components/Overlay.tsx
2383
- var import_react14 = require("react");
2384
- var import_react_dom4 = require("react-dom");
2385
- var bodyLockCount = 0;
2386
- var previousBodyOverflow = "";
2387
- var overlayStack = [];
2388
- function lockBodyScroll() {
2389
- if (bodyLockCount === 0) {
2390
- previousBodyOverflow = document.body.style.overflow;
2391
- document.body.style.overflow = "hidden";
2392
- }
2393
- bodyLockCount += 1;
2394
- return () => {
2395
- bodyLockCount = Math.max(0, bodyLockCount - 1);
2396
- if (bodyLockCount === 0) document.body.style.overflow = previousBodyOverflow;
2397
- };
2398
- }
2399
- function focusableElements(container) {
2400
- return [...container.querySelectorAll(
2401
- 'a[href], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])'
2402
- )].filter((element) => !element.hidden && element.getAttribute("aria-hidden") !== "true");
2403
- }
2404
- function useOverlayLifecycle(open, panelRef, onRequestClose, keyboard = true) {
2405
- const overlayIdRef = (0, import_react14.useRef)(/* @__PURE__ */ Symbol("sia-overlay"));
2406
- const closeRef = (0, import_react14.useRef)(onRequestClose);
2407
- closeRef.current = onRequestClose;
2408
- (0, import_react14.useEffect)(() => {
2409
- if (!open || typeof document === "undefined") return void 0;
2410
- const overlayId = overlayIdRef.current;
2411
- const previouslyFocused = document.activeElement instanceof HTMLElement ? document.activeElement : null;
2412
- overlayStack.push(overlayId);
2413
- const unlockBody = lockBodyScroll();
2414
- const focusTimer = window.setTimeout(() => {
2415
- const panel = panelRef.current;
2416
- if (!panel) return;
2417
- const [firstFocusable] = focusableElements(panel);
2418
- (firstFocusable ?? panel).focus({ preventScroll: true });
2419
- }, 0);
2420
- function handleKeyDown(event) {
2421
- if (overlayStack.at(-1) !== overlayId) return;
2422
- if (event.key === "Escape" && keyboard) {
2423
- event.preventDefault();
2424
- closeRef.current();
2425
- return;
1990
+ const updateEntry = (entry) => {
1991
+ const { target, horizontalTrack, horizontalThumb, verticalTrack, verticalThumb } = entry;
1992
+ const hasHorizontal = canScroll(target, "horizontal");
1993
+ const hasVertical = canScroll(target, "vertical");
1994
+ const rect = visibleRect(target);
1995
+ const thickness = 10;
1996
+ horizontalTrack.hidden = !hasHorizontal || rect.width <= thickness || rect.height <= 0;
1997
+ verticalTrack.hidden = !hasVertical || rect.height <= thickness || rect.width <= 0;
1998
+ if (!horizontalTrack.hidden) {
1999
+ const trackWidth = Math.max(0, rect.width - (hasVertical ? thickness : 0));
2000
+ const thumbWidth = Math.max(24, Math.min(trackWidth, trackWidth * target.clientWidth / target.scrollWidth));
2001
+ const travel = Math.max(0, trackWidth - thumbWidth);
2002
+ const maxScroll = Math.max(1, target.scrollWidth - target.clientWidth);
2003
+ horizontalTrack.style.left = `${rect.left}px`;
2004
+ horizontalTrack.style.top = `${rect.bottom - thickness}px`;
2005
+ horizontalTrack.style.width = `${trackWidth}px`;
2006
+ horizontalTrack.style.height = `${thickness}px`;
2007
+ horizontalThumb.style.width = `${thumbWidth}px`;
2008
+ horizontalThumb.style.transform = `translate3d(${travel * target.scrollLeft / maxScroll}px, 0, 0)`;
2426
2009
  }
2427
- if (event.key !== "Tab" || !panelRef.current) return;
2428
- const focusable = focusableElements(panelRef.current);
2429
- if (focusable.length === 0) {
2430
- event.preventDefault();
2431
- panelRef.current.focus();
2432
- return;
2010
+ if (!verticalTrack.hidden) {
2011
+ const trackHeight = Math.max(0, rect.height - (hasHorizontal ? thickness : 0));
2012
+ const thumbHeight = Math.max(24, Math.min(trackHeight, trackHeight * target.clientHeight / target.scrollHeight));
2013
+ const travel = Math.max(0, trackHeight - thumbHeight);
2014
+ const maxScroll = Math.max(1, target.scrollHeight - target.clientHeight);
2015
+ verticalTrack.style.left = `${rect.right - thickness}px`;
2016
+ verticalTrack.style.top = `${rect.top}px`;
2017
+ verticalTrack.style.width = `${thickness}px`;
2018
+ verticalTrack.style.height = `${trackHeight}px`;
2019
+ verticalThumb.style.height = `${thumbHeight}px`;
2020
+ verticalThumb.style.transform = `translate3d(0, ${travel * target.scrollTop / maxScroll}px, 0)`;
2433
2021
  }
2434
- const first = focusable[0];
2435
- const last = focusable.at(-1);
2436
- if (event.shiftKey && document.activeElement === first) {
2022
+ };
2023
+ const scheduleUpdateAll = () => {
2024
+ if (updateFrame) return;
2025
+ updateFrame = window.requestAnimationFrame(() => {
2026
+ updateFrame = 0;
2027
+ entries.forEach(updateEntry);
2028
+ });
2029
+ };
2030
+ const bindTrackDrag = (entry, axis, track, thumb) => {
2031
+ const target = entry.target;
2032
+ const onTrackPointerEnter = () => {
2033
+ entry.hoveringTrack = true;
2034
+ showEntry(entry);
2035
+ };
2036
+ const onTrackPointerLeave = () => {
2037
+ entry.hoveringTrack = false;
2038
+ scheduleHide(entry);
2039
+ };
2040
+ const onTrackPointerDown = (event) => {
2041
+ if (event.target === thumb || event.button !== 0) return;
2437
2042
  event.preventDefault();
2438
- last.focus();
2439
- } else if (!event.shiftKey && document.activeElement === last) {
2043
+ event.stopPropagation();
2044
+ const trackRect = track.getBoundingClientRect();
2045
+ const thumbRect = thumb.getBoundingClientRect();
2046
+ const trackLength = axis === "vertical" ? trackRect.height : trackRect.width;
2047
+ const thumbLength = axis === "vertical" ? thumbRect.height : thumbRect.width;
2048
+ const pointer = axis === "vertical" ? event.clientY - trackRect.top : event.clientX - trackRect.left;
2049
+ const ratio = Math.max(0, Math.min(1, (pointer - thumbLength / 2) / Math.max(1, trackLength - thumbLength)));
2050
+ if (axis === "vertical") target.scrollTop = ratio * (target.scrollHeight - target.clientHeight);
2051
+ else target.scrollLeft = ratio * (target.scrollWidth - target.clientWidth);
2052
+ };
2053
+ const onThumbPointerDown = (event) => {
2054
+ if (event.button !== 0) return;
2440
2055
  event.preventDefault();
2441
- first.focus();
2442
- }
2443
- }
2444
- document.addEventListener("keydown", handleKeyDown);
2056
+ event.stopPropagation();
2057
+ entry.dragging = true;
2058
+ showEntry(entry);
2059
+ thumb.setPointerCapture(event.pointerId);
2060
+ const startPointer = axis === "vertical" ? event.clientY : event.clientX;
2061
+ const startScroll = axis === "vertical" ? target.scrollTop : target.scrollLeft;
2062
+ const trackRect = track.getBoundingClientRect();
2063
+ const thumbRect = thumb.getBoundingClientRect();
2064
+ const trackLength = axis === "vertical" ? trackRect.height : trackRect.width;
2065
+ const thumbLength = axis === "vertical" ? thumbRect.height : thumbRect.width;
2066
+ const maxScroll = axis === "vertical" ? target.scrollHeight - target.clientHeight : target.scrollWidth - target.clientWidth;
2067
+ const travel = Math.max(1, trackLength - thumbLength);
2068
+ const onPointerMove = (moveEvent) => {
2069
+ if (moveEvent.pointerId !== event.pointerId) return;
2070
+ const pointer = axis === "vertical" ? moveEvent.clientY : moveEvent.clientX;
2071
+ const nextScroll = startScroll + (pointer - startPointer) * maxScroll / travel;
2072
+ if (axis === "vertical") target.scrollTop = nextScroll;
2073
+ else target.scrollLeft = nextScroll;
2074
+ };
2075
+ const onPointerUp = (upEvent) => {
2076
+ if (upEvent.pointerId !== event.pointerId) return;
2077
+ entry.dragging = false;
2078
+ thumb.releasePointerCapture(event.pointerId);
2079
+ window.removeEventListener("pointermove", onPointerMove);
2080
+ window.removeEventListener("pointerup", onPointerUp);
2081
+ window.removeEventListener("pointercancel", onPointerUp);
2082
+ scheduleHide(entry);
2083
+ };
2084
+ window.addEventListener("pointermove", onPointerMove);
2085
+ window.addEventListener("pointerup", onPointerUp);
2086
+ window.addEventListener("pointercancel", onPointerUp);
2087
+ };
2088
+ track.addEventListener("pointerenter", onTrackPointerEnter);
2089
+ track.addEventListener("pointerleave", onTrackPointerLeave);
2090
+ track.addEventListener("pointerdown", onTrackPointerDown);
2091
+ thumb.addEventListener("pointerdown", onThumbPointerDown);
2092
+ return () => {
2093
+ track.removeEventListener("pointerenter", onTrackPointerEnter);
2094
+ track.removeEventListener("pointerleave", onTrackPointerLeave);
2095
+ track.removeEventListener("pointerdown", onTrackPointerDown);
2096
+ thumb.removeEventListener("pointerdown", onThumbPointerDown);
2097
+ };
2098
+ };
2099
+ const resizeObserver = new ResizeObserver((observedEntries) => {
2100
+ observedEntries.forEach(({ target }) => {
2101
+ const entry = entries.get(target);
2102
+ if (entry) updateEntry(entry);
2103
+ });
2104
+ });
2105
+ const addTarget = (target) => {
2106
+ if (entries.has(target)) return;
2107
+ const horizontal = createTrack("horizontal");
2108
+ const vertical = createTrack("vertical");
2109
+ layer.append(horizontal.track, vertical.track);
2110
+ const onPointerEnter = () => {
2111
+ const entry2 = entries.get(target);
2112
+ if (!entry2) return;
2113
+ entry2.hoveringTarget = true;
2114
+ showEntry(entry2);
2115
+ };
2116
+ const onPointerLeave = () => {
2117
+ const entry2 = entries.get(target);
2118
+ if (!entry2) return;
2119
+ entry2.hoveringTarget = false;
2120
+ scheduleHide(entry2);
2121
+ };
2122
+ const onScroll = () => {
2123
+ const entry2 = entries.get(target);
2124
+ if (!entry2) return;
2125
+ updateEntry(entry2);
2126
+ showEntry(entry2);
2127
+ scheduleHide(entry2, 700);
2128
+ };
2129
+ target.addEventListener("pointerenter", onPointerEnter);
2130
+ target.addEventListener("pointerleave", onPointerLeave);
2131
+ target.addEventListener("scroll", onScroll, { passive: true });
2132
+ const entry = {
2133
+ target,
2134
+ horizontalTrack: horizontal.track,
2135
+ horizontalThumb: horizontal.thumb,
2136
+ verticalTrack: vertical.track,
2137
+ verticalThumb: vertical.thumb,
2138
+ hideTimer: null,
2139
+ hoveringTarget: target.matches(":hover"),
2140
+ hoveringTrack: false,
2141
+ dragging: false,
2142
+ cleanup: () => void 0
2143
+ };
2144
+ const cleanupHorizontal = bindTrackDrag(entry, "horizontal", horizontal.track, horizontal.thumb);
2145
+ const cleanupVertical = bindTrackDrag(entry, "vertical", vertical.track, vertical.thumb);
2146
+ entry.cleanup = () => {
2147
+ if (entry.hideTimer !== null) window.clearTimeout(entry.hideTimer);
2148
+ cleanupHorizontal();
2149
+ cleanupVertical();
2150
+ target.removeEventListener("pointerenter", onPointerEnter);
2151
+ target.removeEventListener("pointerleave", onPointerLeave);
2152
+ target.removeEventListener("scroll", onScroll);
2153
+ resizeObserver.unobserve(target);
2154
+ target.removeAttribute("data-sia-floating-scrollbar");
2155
+ horizontal.track.remove();
2156
+ vertical.track.remove();
2157
+ };
2158
+ entries.set(target, entry);
2159
+ target.setAttribute("data-sia-floating-scrollbar", "");
2160
+ resizeObserver.observe(target);
2161
+ updateEntry(entry);
2162
+ if (entry.hoveringTarget) showEntry(entry);
2163
+ };
2164
+ const removeTarget = (target) => {
2165
+ const entry = entries.get(target);
2166
+ if (!entry) return;
2167
+ entry.cleanup();
2168
+ entries.delete(target);
2169
+ };
2170
+ const scanTargets = () => {
2171
+ scanFrame = 0;
2172
+ const candidates = (targetRef ? [targetRef.current] : [document.scrollingElement, ...document.querySelectorAll("*")]).filter((target) => target instanceof HTMLElement);
2173
+ const nextTargets = /* @__PURE__ */ new Set();
2174
+ candidates.forEach((target) => {
2175
+ if (canScroll(target, "horizontal") || canScroll(target, "vertical")) {
2176
+ nextTargets.add(target);
2177
+ if (entries.has(target) || !target.hasAttribute("data-sia-floating-scrollbar")) addTarget(target);
2178
+ }
2179
+ });
2180
+ entries.forEach((_entry, target) => {
2181
+ if (!target.isConnected || !nextTargets.has(target)) removeTarget(target);
2182
+ else updateEntry(_entry);
2183
+ });
2184
+ };
2185
+ const scheduleScan = () => {
2186
+ if (scanFrame) return;
2187
+ scanFrame = window.requestAnimationFrame(scanTargets);
2188
+ };
2189
+ const mutationObserver = new MutationObserver((mutations) => {
2190
+ if (mutations.some((mutation) => !layer.contains(mutation.target))) scheduleScan();
2191
+ });
2192
+ mutationObserver.observe(document.documentElement, { subtree: true, childList: true, attributes: true });
2193
+ window.addEventListener("resize", scheduleScan);
2194
+ window.addEventListener("scroll", scheduleUpdateAll, true);
2195
+ scheduleScan();
2445
2196
  return () => {
2446
- window.clearTimeout(focusTimer);
2447
- document.removeEventListener("keydown", handleKeyDown);
2448
- const index = overlayStack.lastIndexOf(overlayId);
2449
- if (index >= 0) overlayStack.splice(index, 1);
2450
- unlockBody();
2451
- previouslyFocused?.focus({ preventScroll: true });
2197
+ mutationObserver.disconnect();
2198
+ resizeObserver.disconnect();
2199
+ window.removeEventListener("resize", scheduleScan);
2200
+ window.removeEventListener("scroll", scheduleUpdateAll, true);
2201
+ if (scanFrame) window.cancelAnimationFrame(scanFrame);
2202
+ if (updateFrame) window.cancelAnimationFrame(updateFrame);
2203
+ entries.forEach((entry) => entry.cleanup());
2204
+ entries.clear();
2205
+ layer.remove();
2452
2206
  };
2453
- }, [keyboard, open, panelRef]);
2454
- }
2455
- function OverlayPortal({ children, container }) {
2456
- if (typeof document === "undefined") return null;
2457
- const target = typeof container === "function" ? container() : container ?? document.body;
2458
- return (0, import_react_dom4.createPortal)(children, target);
2207
+ }, [disabled, targetRef]);
2208
+ return null;
2459
2209
  }
2460
2210
 
2461
- // src/components/DataDisplay.tsx
2462
- var import_jsx_runtime15 = require("react/jsx-runtime");
2463
- function BadgeRibbon({ text, color, placement = "end", className = "", children, ...props }) {
2464
- return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: `sia-ribbon-wrap ${className}`.trim(), ...withTitleTooltip(props), children: [
2465
- children,
2466
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: `sia-ribbon sia-ribbon--${placement}`, style: { backgroundColor: color }, children: text })
2467
- ] });
2468
- }
2469
- function Badge({ count, showZero = false, overflowCount = 99, dot = false, status, text, color, offset = [0, 0], size = "default", className = "", children, style, ...props }) {
2470
- const numeric = typeof count === "number";
2471
- const hidden = !dot && !status && (count === void 0 || count === null || count === 0 && !showZero);
2472
- const display = numeric && count > overflowCount ? `${overflowCount}+` : count;
2473
- 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;
2474
- if (status && !children) return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("span", { className: `sia-badge sia-badge--status ${className}`.trim(), style, ...withTitleTooltip(props), children: [
2475
- badge,
2476
- text ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "sia-badge__text", children: text }) : null
2477
- ] });
2478
- return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("span", { className: `sia-badge ${className}`.trim(), style, ...withTitleTooltip(props), children: [
2479
- children,
2480
- badge,
2481
- text && !status ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "sia-badge__text", children: text }) : null
2482
- ] });
2483
- }
2484
- Badge.Ribbon = BadgeRibbon;
2485
- function CollapsePanel({
2486
- open,
2487
- forceRender = false,
2488
- destroyInactivePanel = false,
2489
- children
2211
+ // src/components/Tabs.tsx
2212
+ var import_jsx_runtime11 = require("react/jsx-runtime");
2213
+ function Tabs({
2214
+ items,
2215
+ activeKey,
2216
+ defaultActiveKey,
2217
+ onChange,
2218
+ onAfterChange,
2219
+ deferContent = false,
2220
+ type = "line",
2221
+ size = "medium",
2222
+ tabPosition,
2223
+ orientation = "horizontal",
2224
+ centered = false,
2225
+ tabBarTitle,
2226
+ tabBarExtraContent,
2227
+ contentGap,
2228
+ destroyInactiveTabPane = true,
2229
+ closable = false,
2230
+ onEdit,
2231
+ tabContextMenu,
2232
+ onTabContextMenuClick,
2233
+ className = "",
2234
+ ...props
2490
2235
  }) {
2491
- const [keepChildren, setKeepChildren] = (0, import_react15.useState)(open || forceRender || !destroyInactivePanel);
2492
- (0, import_react15.useEffect)(() => {
2493
- if (open || forceRender || !destroyInactivePanel) {
2494
- setKeepChildren(true);
2495
- return void 0;
2236
+ const firstEnabledKey = items.find((item) => !item.disabled)?.key ?? "";
2237
+ const [internalActiveKey, setInternalActiveKey] = (0, import_react12.useState)(defaultActiveKey ?? firstEnabledKey);
2238
+ const currentKey = activeKey ?? internalActiveKey;
2239
+ const selectedKey = items.some((item) => item.key === currentKey && !item.disabled) ? currentKey : firstEnabledKey;
2240
+ const baseId = (0, import_react12.useId)().replace(/:/g, "");
2241
+ const enabledItems = (0, import_react12.useMemo)(() => items.filter((item) => !item.disabled), [items]);
2242
+ const resolvedPosition = tabPosition ?? (orientation === "vertical" ? "left" : "top");
2243
+ const resolvedOrientation = resolvedPosition === "left" || resolvedPosition === "right" ? "vertical" : "horizontal";
2244
+ const sliding = ["capsule", "line", "underline", "tech", "tech-line"].includes(type);
2245
+ const listRef = (0, import_react12.useRef)(null);
2246
+ const indicatorRef = (0, import_react12.useRef)(null);
2247
+ const [contentState, setContentState] = (0, import_react12.useState)(() => ({ key: selectedKey, visited: /* @__PURE__ */ new Set([selectedKey]) }));
2248
+ const settledKeyRef = (0, import_react12.useRef)(selectedKey);
2249
+ const afterChangeRef = (0, import_react12.useRef)(onAfterChange);
2250
+ (0, import_react12.useLayoutEffect)(() => {
2251
+ afterChangeRef.current = onAfterChange;
2252
+ }, [onAfterChange]);
2253
+ (0, import_react12.useLayoutEffect)(() => {
2254
+ if (!sliding) return;
2255
+ const list = listRef.current;
2256
+ const indicator = indicatorRef.current;
2257
+ if (!list || !indicator) return;
2258
+ function updateIndicator() {
2259
+ if (!list || !indicator) return;
2260
+ const active = list.querySelector('[role="tab"][aria-selected="true"]');
2261
+ if (!active) {
2262
+ indicator.style.visibility = "hidden";
2263
+ return;
2264
+ }
2265
+ const listRect = list.getBoundingClientRect();
2266
+ const rect = active.getBoundingClientRect();
2267
+ if (type === "tech-line" && resolvedOrientation === "horizontal") {
2268
+ if (rect.left < listRect.left) list.scrollLeft += rect.left - listRect.left;
2269
+ else if (rect.right > listRect.right) list.scrollLeft += rect.right - listRect.right;
2270
+ }
2271
+ const currentRect = active.getBoundingClientRect();
2272
+ indicator.style.transform = `translate(${currentRect.left - listRect.left - list.clientLeft + list.scrollLeft}px, ${currentRect.top - listRect.top - list.clientTop + list.scrollTop}px)`;
2273
+ indicator.style.width = `${rect.width}px`;
2274
+ indicator.style.height = `${rect.height}px`;
2275
+ indicator.style.visibility = "visible";
2276
+ if (!indicator.dataset.ready) {
2277
+ indicator.getBoundingClientRect();
2278
+ indicator.dataset.ready = "true";
2279
+ }
2496
2280
  }
2497
- const timer = window.setTimeout(() => setKeepChildren(false), 240);
2498
- return () => window.clearTimeout(timer);
2499
- }, [destroyInactivePanel, forceRender, open]);
2500
- const renderChildren = open || forceRender || !destroyInactivePanel || keepChildren;
2501
- 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 }) }) });
2502
- }
2503
- function Collapse({ items, activeKey, defaultActiveKey = [], accordion = false, bordered = true, ghost = false, destroyInactivePanel = false, expandIconPosition = "start", onChange, className = "", ...props }) {
2504
- const normalize = (key) => Array.isArray(key) ? [...key] : key === void 0 ? [] : [key];
2505
- const controlled = activeKey !== void 0;
2506
- const [internal, setInternal] = (0, import_react15.useState)(normalize(defaultActiveKey));
2507
- const openKeys = controlled ? normalize(activeKey) : internal;
2508
- function toggle(item) {
2509
- if (item.disabled || item.collapsible === "disabled") return;
2510
- const open = openKeys.includes(item.key);
2511
- const next = accordion ? open ? [] : [item.key] : open ? openKeys.filter((key) => key !== item.key) : [...openKeys, item.key];
2512
- if (!controlled) setInternal(next);
2513
- onChange?.(accordion ? next[0] ?? "" : next);
2514
- }
2515
- const borderClassName = !bordered || ghost ? "sia-collapse--borderless" : "sia-collapse--bordered";
2516
- 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) => {
2517
- const open = openKeys.includes(item.key);
2518
- 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 }) });
2519
- return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("section", { className: `sia-collapse__item${open ? " is-open" : ""}${item.disabled ? " is-disabled" : ""} ${item.className ?? ""}`.trim(), children: [
2520
- /* @__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) => {
2521
- if (event.key === "Enter" || event.key === " ") {
2522
- event.preventDefault();
2523
- toggle(item);
2524
- }
2525
- }, children: [
2526
- expandIconPosition === "start" ? arrow : null,
2527
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "sia-collapse__label", children: item.label }),
2528
- item.extra ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "sia-collapse__extra", onClick: (event) => event.stopPropagation(), children: item.extra }) : null,
2529
- expandIconPosition === "end" ? arrow : null
2530
- ] }),
2531
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(CollapsePanel, { open, forceRender: item.forceRender, destroyInactivePanel, children: item.children })
2532
- ] }, item.key);
2533
- }) });
2534
- }
2535
- function ImagePreview({ open, src, alt = "", onOpenChange }) {
2536
- const previewRef = (0, import_react15.useRef)(null);
2537
- const stageRef = (0, import_react15.useRef)(null);
2538
- const [natural, setNatural] = (0, import_react15.useState)({ src: "", width: 0, height: 0 });
2539
- const [area, setArea] = (0, import_react15.useState)({ width: 0, height: 0 });
2540
- const [zoom, setZoom] = (0, import_react15.useState)(1);
2541
- const [rotation, setRotation] = (0, import_react15.useState)(0);
2542
- const [flip, setFlip] = (0, import_react15.useState)({ x: 1, y: 1 });
2543
- const [pan, setPan] = (0, import_react15.useState)({ x: 0, y: 0 });
2544
- const [failed, setFailed] = (0, import_react15.useState)(false);
2545
- const drag = (0, import_react15.useRef)();
2546
- useOverlayLifecycle(open, previewRef, () => onOpenChange(false));
2547
- const reset = () => {
2548
- setZoom(1);
2549
- setRotation(0);
2550
- setFlip({ x: 1, y: 1 });
2551
- setPan({ x: 0, y: 0 });
2552
- drag.current = void 0;
2553
- };
2554
- (0, import_react15.useEffect)(() => {
2555
- reset();
2556
- setFailed(false);
2557
- }, [open, src]);
2558
- (0, import_react15.useEffect)(() => {
2559
- const stage = stageRef.current;
2560
- if (!open || !stage) return;
2561
- const measure = () => {
2562
- setArea({ width: stage.clientWidth, height: stage.clientHeight });
2563
- setPan({ x: 0, y: 0 });
2564
- };
2565
- measure();
2566
- const observer = new ResizeObserver(measure);
2567
- observer.observe(stage);
2281
+ updateIndicator();
2282
+ const observer = new ResizeObserver(updateIndicator);
2283
+ observer.observe(list);
2284
+ list.querySelectorAll('[role="tab"]').forEach((tab) => observer.observe(tab));
2568
2285
  return () => observer.disconnect();
2569
- }, [open, src]);
2570
- const ready = natural.src === src && natural.width > 0 && area.width > 0 && area.height > 0 && !failed;
2571
- const sideways = Math.abs(rotation % 180) === 90;
2572
- const fit = ready ? Math.min(1, area.width / (sideways ? natural.height : natural.width), area.height / (sideways ? natural.width : natural.height)) : 0;
2573
- const changeZoom = (factor) => setZoom((value) => Math.max(0.25, Math.min(8, value * factor)));
2574
- if (!open || !src || typeof document === "undefined") return null;
2575
- 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: [
2576
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Button, { variant: "text", className: "sia-image-preview__close", "aria-label": "\u5173\u95ED\u9884\u89C8", onClick: (event) => {
2577
- event.stopPropagation();
2578
- onOpenChange(false);
2579
- }, children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Icon, { name: "close" }) }),
2580
- /* @__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)(
2581
- "img",
2582
- {
2583
- src,
2584
- alt,
2585
- draggable: false,
2586
- 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})` },
2587
- onLoad: (event) => setNatural({ src, width: event.currentTarget.naturalWidth, height: event.currentTarget.naturalHeight }),
2588
- onError: () => setFailed(true),
2589
- onClick: (event) => event.stopPropagation(),
2590
- onPointerDown: (event) => {
2591
- if (event.button !== 0) return;
2592
- event.preventDefault();
2593
- event.stopPropagation();
2594
- event.currentTarget.setPointerCapture(event.pointerId);
2595
- drag.current = { id: event.pointerId, x: event.clientX, y: event.clientY, left: pan.x, top: pan.y };
2596
- },
2597
- onPointerMove: (event) => {
2598
- const start = drag.current;
2599
- if (start?.id === event.pointerId) setPan({ x: start.left + event.clientX - start.x, y: start.top + event.clientY - start.y });
2600
- },
2601
- onPointerUp: () => {
2602
- drag.current = void 0;
2603
- },
2604
- onPointerCancel: () => {
2605
- drag.current = void 0;
2606
- },
2607
- onLostPointerCapture: () => {
2608
- drag.current = void 0;
2286
+ }, [sliding, type, selectedKey, items, size, resolvedPosition, centered, closable]);
2287
+ (0, import_react12.useEffect)(() => {
2288
+ let cancelled = false;
2289
+ let frame = 0;
2290
+ frame = requestAnimationFrame(() => {
2291
+ const indicator = indicatorRef.current;
2292
+ if (indicator) getComputedStyle(indicator).transform;
2293
+ const animations = indicator?.getAnimations() ?? [];
2294
+ const complete = () => {
2295
+ if (cancelled) return;
2296
+ setContentState((previous) => ({ key: selectedKey, visited: new Set([...previous.visited].filter((key) => items.some((item) => item.key === key))).add(selectedKey) }));
2297
+ if (settledKeyRef.current !== selectedKey) {
2298
+ settledKeyRef.current = selectedKey;
2299
+ afterChangeRef.current?.(selectedKey);
2609
2300
  }
2610
- },
2611
- src
2612
- ) }),
2613
- /* @__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: [
2614
- /* @__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 }) }),
2615
- /* @__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 }) }),
2616
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Button, { variant: "text", title: "\u5411\u5DE6\u65CB\u8F6C", "aria-label": "\u5411\u5DE6\u65CB\u8F6C", disabled: !ready, onClick: () => {
2617
- setRotation((value) => value - 90);
2618
- setPan({ x: 0, y: 0 });
2619
- }, children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Icon, { name: "rotate-left", size: 20 }) }),
2620
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Button, { variant: "text", title: "\u5411\u53F3\u65CB\u8F6C", "aria-label": "\u5411\u53F3\u65CB\u8F6C", disabled: !ready, onClick: () => {
2621
- setRotation((value) => value + 90);
2622
- setPan({ x: 0, y: 0 });
2623
- }, children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Icon, { name: "rotate-right", size: 20 }) }),
2624
- /* @__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 }) }),
2625
- /* @__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" }),
2626
- /* @__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 }) }),
2627
- /* @__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 }) })
2628
- ] })
2629
- ] }), document.body);
2630
- }
2631
-
2632
- // src/components/FloatingPlaceholder.tsx
2633
- var import_jsx_runtime16 = require("react/jsx-runtime");
2634
- function useFloatingPlaceholder({ mode, placeholder, filled, active = false }) {
2635
- const enabled = usePlaceholderMode(mode) === "floating" && Boolean(placeholder);
2636
- return {
2637
- enabled,
2638
- className: enabled ? ` sia-placeholder-floating${filled ? " sia-placeholder-floating--filled" : ""}${active ? " sia-placeholder-floating--active" : ""}` : "",
2639
- label: enabled ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "sia-floating-placeholder", "aria-hidden": "true", children: placeholder }) : null
2640
- };
2641
- }
2642
-
2643
- // src/components/Select.tsx
2644
- var import_react22 = require("react");
2645
-
2646
- // src/components/Modal.tsx
2647
- var import_react16 = require("react");
2648
- var import_client3 = require("react-dom/client");
2649
- var import_jsx_runtime17 = require("react/jsx-runtime");
2650
- var modalHandles = /* @__PURE__ */ new Set();
2651
- var MODAL_MOTION_DURATION = 220;
2652
- var typeIcons = {
2653
- info: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(Icon, { name: "info", size: 23 }),
2654
- success: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(Icon, { name: "circle-check", size: 23 }),
2655
- warning: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(Icon, { name: "warning", size: 23 }),
2656
- error: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(Icon, { name: "circle-close", size: 23 }),
2657
- confirm: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(Icon, { name: "question", size: 23 })
2658
- };
2659
- var ModalRoot = (0, import_react16.forwardRef)(function Modal({
2660
- open,
2661
- title,
2662
- children,
2663
- footer,
2664
- footerExtra,
2665
- type = "default",
2666
- okText = "\u786E\u5B9A",
2667
- cancelText = "\u53D6\u6D88",
2668
- showCancel = type === "default" || type === "confirm",
2669
- confirmLoading = false,
2670
- okButtonProps,
2671
- cancelButtonProps,
2672
- closable = true,
2673
- maskClosable = true,
2674
- keyboard = true,
2675
- centered = false,
2676
- width = 520,
2677
- height,
2678
- zIndex = 1e3,
2679
- getContainer,
2680
- onOk,
2681
- onCancel,
2682
- onOpenChange,
2683
- afterOpenChange,
2684
- className = "",
2685
- style,
2686
- ...props
2687
- }, forwardedRef) {
2688
- const localRef = (0, import_react16.useRef)(null);
2689
- const titleId = (0, import_react16.useId)();
2690
- const [submitting, setSubmitting] = (0, import_react16.useState)(false);
2691
- const [rendered, setRendered] = (0, import_react16.useState)(open);
2692
- const [closing, setClosing] = (0, import_react16.useState)(false);
2693
- const afterOpenChangeRef = (0, import_react16.useRef)(afterOpenChange);
2694
- afterOpenChangeRef.current = afterOpenChange;
2695
- const submitLock = (0, import_react16.useRef)(false);
2696
- (0, import_react16.useEffect)(() => {
2697
- if (!open || !rendered) return void 0;
2698
- const timer = window.setTimeout(() => afterOpenChangeRef.current?.(true), MODAL_MOTION_DURATION);
2699
- return () => window.clearTimeout(timer);
2700
- }, [open, rendered]);
2701
- (0, import_react16.useEffect)(() => {
2702
- if (open) {
2703
- setRendered(true);
2704
- setClosing(false);
2705
- return void 0;
2706
- }
2707
- if (!rendered) return void 0;
2708
- setClosing(true);
2709
- const timer = window.setTimeout(() => {
2710
- setRendered(false);
2711
- setClosing(false);
2712
- afterOpenChangeRef.current?.(false);
2713
- }, MODAL_MOTION_DURATION);
2714
- return () => window.clearTimeout(timer);
2715
- }, [open, rendered]);
2716
- function setRef(node) {
2717
- localRef.current = node;
2718
- if (typeof forwardedRef === "function") forwardedRef(node);
2719
- else if (forwardedRef) forwardedRef.current = node;
2301
+ };
2302
+ if (!animations.length) complete();
2303
+ else Promise.allSettled(animations.map((animation) => animation.finished)).then(() => {
2304
+ if (!cancelled) frame = requestAnimationFrame(complete);
2305
+ });
2306
+ });
2307
+ return () => {
2308
+ cancelled = true;
2309
+ cancelAnimationFrame(frame);
2310
+ };
2311
+ }, [selectedKey, type, deferContent]);
2312
+ const contentKey = deferContent ? contentState.key : selectedKey;
2313
+ function selectTab(key) {
2314
+ if (key === selectedKey) return;
2315
+ if (activeKey === void 0) setInternalActiveKey(key);
2316
+ onChange?.(key);
2720
2317
  }
2721
- function requestClose() {
2722
- if (submitting || confirmLoading) return;
2723
- onCancel?.();
2724
- onOpenChange?.(false);
2318
+ function focusAndSelect(key) {
2319
+ selectTab(key);
2320
+ document.getElementById(`${baseId}-tab-${key}`)?.focus();
2725
2321
  }
2726
- async function handleOk() {
2727
- if (submitLock.current || confirmLoading) return;
2728
- submitLock.current = true;
2729
- if (!onOk) {
2730
- onOpenChange?.(false);
2731
- submitLock.current = false;
2732
- return;
2733
- }
2734
- try {
2735
- const result = onOk();
2736
- if (result instanceof Promise) {
2737
- setSubmitting(true);
2738
- const resolved = await result;
2739
- if (resolved !== false) onOpenChange?.(false);
2740
- } else if (result !== false) {
2741
- onOpenChange?.(false);
2742
- }
2743
- } catch {
2744
- return;
2745
- } finally {
2746
- submitLock.current = false;
2747
- setSubmitting(false);
2748
- }
2322
+ function handleKeyDown(event, key) {
2323
+ const previousKey = resolvedOrientation === "horizontal" ? "ArrowLeft" : "ArrowUp";
2324
+ const nextKey = resolvedOrientation === "horizontal" ? "ArrowRight" : "ArrowDown";
2325
+ if (![previousKey, nextKey, "Home", "End"].includes(event.key) || enabledItems.length === 0) return;
2326
+ event.preventDefault();
2327
+ const currentIndex = Math.max(0, enabledItems.findIndex((item) => item.key === key));
2328
+ if (event.key === "Home") return focusAndSelect(enabledItems[0].key);
2329
+ if (event.key === "End") return focusAndSelect(enabledItems[enabledItems.length - 1].key);
2330
+ const offset = event.key === nextKey ? 1 : -1;
2331
+ const nextIndex = (currentIndex + offset + enabledItems.length) % enabledItems.length;
2332
+ focusAndSelect(enabledItems[nextIndex].key);
2749
2333
  }
2750
- useOverlayLifecycle(rendered, localRef, requestClose, keyboard);
2751
- if (!rendered) return null;
2752
- const mergedStyle = {
2753
- ...height !== void 0 ? { display: "flex", flexDirection: "column", height, maxHeight: "calc(100dvh - 48px)" } : {},
2754
- ...style,
2755
- "--sia-modal-width": typeof width === "number" ? `${width}px` : width
2756
- };
2757
- const loading = submitting || confirmLoading;
2758
- const defaultFooter = /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_jsx_runtime17.Fragment, { children: [
2759
- showCancel ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(Button, { ...cancelButtonProps, onClick: requestClose, disabled: loading || cancelButtonProps?.disabled, children: cancelText }) : null,
2760
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(Button, { variant: "primary", ...okButtonProps, onClick: handleOk, loading, children: okText })
2761
- ] });
2762
- return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(OverlayPortal, { container: getContainer, children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
2334
+ const hasPanels = items.some((item) => item.children !== void 0);
2335
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
2763
2336
  "div",
2764
2337
  {
2765
- className: `sia-modal-root${centered ? " sia-modal-root--centered" : ""}`,
2766
- "data-state": closing ? "closing" : "open",
2767
- style: { zIndex },
2768
- onMouseDown: (event) => {
2769
- if (event.target === event.currentTarget && maskClosable) requestClose();
2770
- },
2771
- children: /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
2772
- "div",
2773
- {
2774
- ref: setRef,
2775
- className: `sia-modal sia-modal--${type} ${className}`.trim(),
2776
- style: mergedStyle,
2777
- role: "dialog",
2778
- "aria-modal": "true",
2779
- "aria-labelledby": title ? titleId : void 0,
2780
- tabIndex: -1,
2781
- ...withTitleTooltip(props),
2782
- children: [
2783
- /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "sia-modal__header", style: height !== void 0 ? { flexShrink: 0 } : void 0, children: [
2784
- /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "sia-modal__heading", children: [
2785
- typeIcons[type] ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "sia-modal__type-icon", "aria-hidden": "true", children: typeIcons[type] }) : null,
2786
- title ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("strong", { id: titleId, children: title }) : null
2787
- ] }),
2788
- 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
2789
- ] }),
2790
- 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,
2791
- footer !== null ? /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "sia-modal__footer", style: height !== void 0 ? { flexShrink: 0 } : void 0, children: [
2792
- footer === void 0 && footerExtra != null ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { style: { marginRight: "auto" }, children: footerExtra }) : null,
2793
- footer ?? defaultFooter
2794
- ] }) : null
2795
- ]
2796
- }
2797
- )
2798
- }
2799
- ) });
2800
- });
2801
- function openModal(config) {
2802
- if (typeof document === "undefined") return { destroy() {
2803
- }, update() {
2804
- } };
2805
- const container = document.createElement("div");
2806
- container.className = "sia-modal-api-host";
2807
- document.body.appendChild(container);
2808
- const root = (0, import_client3.createRoot)(container);
2809
- let currentConfig = config;
2810
- let currentOpen = true;
2811
- let destroyed = false;
2812
- let cleanupTimer;
2813
- function cleanup2() {
2814
- if (destroyed) return;
2815
- destroyed = true;
2816
- if (cleanupTimer !== void 0) window.clearTimeout(cleanupTimer);
2817
- root.unmount();
2818
- container.remove();
2819
- modalHandles.delete(handle);
2820
- }
2821
- function closeWithMotion() {
2822
- if (destroyed || !currentOpen) return;
2823
- currentOpen = false;
2824
- render();
2825
- cleanupTimer = window.setTimeout(cleanup2, MODAL_MOTION_DURATION);
2826
- }
2827
- function render() {
2828
- root.render(/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(ModalRoot, { ...currentConfig, open: currentOpen, onOpenChange: (nextOpen) => {
2829
- currentConfig.onOpenChange?.(nextOpen);
2830
- if (!nextOpen) closeWithMotion();
2831
- } }));
2832
- }
2833
- const handle = {
2834
- destroy: closeWithMotion,
2835
- update(next) {
2836
- if (destroyed) return;
2837
- currentConfig = { ...currentConfig, ...next };
2838
- render();
2338
+ className: `sia-tabs sia-tabs--${type}${type === "underline" ? " sia-tabs--line" : ""} sia-tabs--${size} sia-tabs--${resolvedOrientation} sia-tabs--${resolvedPosition}${centered ? " sia-tabs--centered" : ""} ${className}`.trim(),
2339
+ ...withTitleTooltip(props),
2340
+ children: [
2341
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "sia-tabs__header", children: [
2342
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(FloatingScrollbarProvider, { targetRef: listRef, disabled: type !== "tech-line" || resolvedOrientation !== "horizontal" }),
2343
+ tabBarTitle ? /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "sia-tabs__title", children: [
2344
+ tabBarTitle,
2345
+ type === "tech-line" ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("svg", { className: "sia-tabs__title-connector", viewBox: "0 0 32 40", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("path", { d: "M0 39.25 H5 Q8 39.25 10 35.25 L28 3.75 Q29 1.75 32 1.75", fill: "none", stroke: "currentColor", strokeWidth: "1.5", strokeLinejoin: "round" }) }) : null
2346
+ ] }) : null,
2347
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { ref: listRef, className: `sia-tabs__list${sliding ? " sia-tabs__list--sliding" : ""}`, role: "tablist", "aria-orientation": resolvedOrientation, children: [
2348
+ sliding ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { ref: indicatorRef, className: `sia-tabs__indicator${type === "capsule" ? " sia-tabs__capsule-indicator" : ""}`, "aria-hidden": "true", children: type === "tech-line" ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "sia-tabs__indicator-shape", "aria-hidden": "true" }) : null }, type) : null,
2349
+ items.map((item) => {
2350
+ const selected = item.key === selectedKey;
2351
+ const canClose = item.closable ?? closable;
2352
+ const tabNode = /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("span", { className: `sia-tabs__tab-wrap${canClose ? " is-closable" : ""}`, children: [
2353
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
2354
+ "button",
2355
+ {
2356
+ id: `${baseId}-tab-${item.key}`,
2357
+ type: "button",
2358
+ className: "sia-tabs__tab",
2359
+ role: "tab",
2360
+ "aria-selected": selected,
2361
+ "aria-controls": hasPanels ? `${baseId}-panel-${item.key}` : void 0,
2362
+ tabIndex: selected ? 0 : -1,
2363
+ disabled: item.disabled,
2364
+ onClick: () => selectTab(item.key),
2365
+ onKeyDown: (event) => handleKeyDown(event, item.key),
2366
+ children: [
2367
+ type === "tech-line" ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "sia-tabs__tab-shape", "aria-hidden": "true" }) : null,
2368
+ item.icon ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "sia-tabs__icon", children: item.icon }) : null,
2369
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { children: item.label })
2370
+ ]
2371
+ }
2372
+ ),
2373
+ canClose ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
2374
+ "button",
2375
+ {
2376
+ type: "button",
2377
+ className: "sia-tabs__close",
2378
+ "aria-label": `\u5173\u95ED${typeof item.label === "string" ? item.label : "\u6807\u7B7E\u9875"}`,
2379
+ disabled: item.disabled,
2380
+ onClick: () => onEdit?.(item.key, "remove"),
2381
+ children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Icon, { name: "close", size: 12 })
2382
+ }
2383
+ ) : null
2384
+ ] }, item.key);
2385
+ const contextMenuItems = typeof tabContextMenu === "function" ? tabContextMenu(item) : tabContextMenu;
2386
+ if (!contextMenuItems?.length) return tabNode;
2387
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
2388
+ Dropdown,
2389
+ {
2390
+ className: "sia-tabs__tab-context-trigger",
2391
+ popupClassName: "sia-tabs__tab-context-menu",
2392
+ trigger: ["contextMenu"],
2393
+ tabIndex: -1,
2394
+ menu: {
2395
+ items: contextMenuItems,
2396
+ selectable: false,
2397
+ onClick: (info) => onTabContextMenuClick?.({ ...info, tabKey: item.key, tab: item })
2398
+ },
2399
+ children: tabNode
2400
+ },
2401
+ item.key
2402
+ );
2403
+ })
2404
+ ] }),
2405
+ tabBarExtraContent ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "sia-tabs__extra", children: tabBarExtraContent }) : null
2406
+ ] }),
2407
+ hasPanels ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "sia-tabs__panels", style: contentGap === void 0 ? void 0 : {
2408
+ // 仅设置面板靠近导航的一侧,保留各方向布局原有的其余边界。
2409
+ [resolvedPosition === "top" ? "paddingTop" : resolvedPosition === "bottom" ? "paddingBottom" : resolvedPosition === "left" ? "paddingLeft" : "paddingRight"]: contentGap
2410
+ }, "aria-busy": deferContent && contentKey !== selectedKey || void 0, children: items.map((item) => {
2411
+ const selected = item.key === contentKey;
2412
+ if (destroyInactiveTabPane && !selected) return null;
2413
+ if (deferContent && !selected && !contentState.visited.has(item.key)) return null;
2414
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
2415
+ "div",
2416
+ {
2417
+ id: `${baseId}-panel-${item.key}`,
2418
+ className: "sia-tabs__panel",
2419
+ role: "tabpanel",
2420
+ "aria-labelledby": `${baseId}-tab-${item.key}`,
2421
+ hidden: !selected,
2422
+ tabIndex: 0,
2423
+ children: item.children
2424
+ },
2425
+ item.key
2426
+ );
2427
+ }) }) : null
2428
+ ]
2839
2429
  }
2840
- };
2841
- modalHandles.add(handle);
2842
- render();
2843
- return handle;
2844
- }
2845
- function preset(type, defaults = {}) {
2846
- return (config) => openModal({
2847
- okText: type === "confirm" ? "\u786E\u5B9A" : "\u77E5\u9053\u4E86",
2848
- showCancel: type === "confirm",
2849
- closable: type !== "confirm",
2850
- maskClosable: type !== "confirm",
2851
- ...defaults,
2852
- ...config,
2853
- type
2854
- });
2430
+ );
2855
2431
  }
2856
- var Modal2 = Object.assign(ModalRoot, {
2857
- /** 直接打开实例,返回可用于更新或关闭的操作句柄。 */
2858
- open: openModal,
2859
- /** 显示普通信息提示。 */
2860
- info: preset("info", { title: "\u63D0\u793A" }),
2861
- /** 显示成功提示。 */
2862
- success: preset("success", { title: "\u64CD\u4F5C\u6210\u529F" }),
2863
- /** 显示警告提示。 */
2864
- warning: preset("warning", { title: "\u8BF7\u6CE8\u610F" }),
2865
- /** 显示错误提示。 */
2866
- error: preset("error", { title: "\u64CD\u4F5C\u5931\u8D25" }),
2867
- /** 打开确认弹框,支持异步确认;返回 false 可阻止关闭。 */
2868
- confirm: preset("confirm", { title: "\u786E\u8BA4\u64CD\u4F5C" }),
2869
- /** 关闭由此 API 打开的所有弹层。 */
2870
- destroyAll() {
2871
- [...modalHandles].forEach((handle) => handle.destroy());
2872
- }
2873
- });
2874
2432
 
2875
- // src/components/Input.tsx
2876
- var import_react17 = require("react");
2877
- var import_jsx_runtime18 = require("react/jsx-runtime");
2878
- var Input = (0, import_react17.forwardRef)(function Input2({
2879
- label,
2880
- hint,
2881
- status = "default",
2882
- size: sizeProp,
2883
- prefix,
2884
- suffix,
2885
- allowClear = true,
2886
- onClear,
2887
- showCount = false,
2888
- placeholderMode,
2889
- placeholder,
2890
- className = "",
2891
- id,
2892
- disabled,
2893
- value,
2894
- defaultValue,
2895
- maxLength,
2896
- onChange,
2897
- ...props
2898
- }, forwardedRef) {
2899
- const size = useControlSize(sizeProp);
2900
- const fallbackId = (0, import_react17.useId)();
2901
- const inputId = id ?? fallbackId;
2902
- const localRef = (0, import_react17.useRef)(null);
2903
- const [uncontrolledValue, setUncontrolledValue] = (0, import_react17.useState)(() => defaultValue === void 0 ? "" : String(defaultValue));
2904
- const currentText = value !== void 0 ? String(value) : uncontrolledValue;
2905
- const hasValue = currentText.length > 0;
2906
- const floatingPlaceholder = usePlaceholderMode(placeholderMode) === "floating" && Boolean(placeholder);
2907
- const hintId = `${inputId}-hint`;
2908
- const describedBy = [props["aria-describedby"], hint ? hintId : null].filter(Boolean).join(" ") || void 0;
2433
+ // src/components/Feedback.tsx
2434
+ var import_react15 = require("react");
2435
+ var import_react_dom3 = require("react-dom");
2436
+ var import_client2 = require("react-dom/client");
2437
+
2438
+ // src/components/FloatingDisplay.tsx
2439
+ var import_react14 = require("react");
2440
+ var import_react_dom2 = require("react-dom");
2441
+
2442
+ // src/components/Checkbox.tsx
2443
+ var import_react13 = require("react");
2444
+ var import_jsx_runtime12 = require("react/jsx-runtime");
2445
+ var CheckboxGroupContext = (0, import_react13.createContext)(null);
2446
+ var CheckboxRoot = (0, import_react13.forwardRef)(function Checkbox({ value, size: sizeProp, indeterminate = false, children, disabled, checked, defaultChecked, className = "", onChange, ...props }, forwardedRef) {
2447
+ const size = useControlSize(sizeProp);
2448
+ const group = (0, import_react13.useContext)(CheckboxGroupContext);
2449
+ const localRef = (0, import_react13.useRef)(null);
2450
+ const fallbackId = (0, import_react13.useId)();
2451
+ const grouped = group !== null && value !== void 0;
2452
+ const currentChecked = grouped ? group.values.includes(value) : checked;
2453
+ (0, import_react13.useEffect)(() => {
2454
+ if (localRef.current) localRef.current.indeterminate = indeterminate;
2455
+ }, [indeterminate]);
2909
2456
  function setRef(node) {
2910
2457
  localRef.current = node;
2911
2458
  if (typeof forwardedRef === "function") forwardedRef(node);
2912
2459
  else if (forwardedRef) forwardedRef.current = node;
2913
2460
  }
2914
- function handleChange(event) {
2915
- if (maxLength !== void 0 && maxLength >= 0 && event.target.value.length > maxLength) {
2916
- const valueSetter = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, "value")?.set;
2917
- valueSetter?.call(event.target, event.target.value.slice(0, maxLength));
2918
- }
2919
- setUncontrolledValue(event.target.value);
2920
- onChange?.(event);
2921
- }
2922
- function clearValue() {
2923
- const input2 = localRef.current;
2924
- if (!input2 || disabled) return;
2925
- const valueSetter = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, "value")?.set;
2926
- valueSetter?.call(input2, "");
2927
- input2.dispatchEvent(new Event("input", { bubbles: true }));
2928
- setUncontrolledValue("");
2929
- input2.focus();
2930
- onClear?.();
2931
- }
2932
- function handleControlMouseDown(event) {
2933
- if (disabled || event.defaultPrevented || event.button !== 0) return;
2934
- const target = event.target;
2935
- if (!(target instanceof Element)) return;
2936
- const interactive = target.closest("input, textarea, select, button, a[href], [tabindex], [contenteditable]:not([contenteditable='false']), [role='button'], [role='link']");
2937
- if (interactive && event.currentTarget.contains(interactive) && !interactive.classList.contains("sia-input-control__clear")) return;
2938
- event.preventDefault();
2939
- localRef.current?.focus({ preventScroll: true });
2940
- }
2941
- const input = /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
2942
- "input",
2943
- {
2944
- ...withTitleTooltip(props),
2945
- ref: setRef,
2946
- id: inputId,
2947
- className: "sia-input",
2948
- disabled,
2949
- value,
2950
- defaultValue,
2951
- maxLength,
2952
- placeholder,
2953
- "aria-describedby": describedBy,
2954
- "aria-invalid": status === "error" || void 0,
2955
- onChange: handleChange
2956
- }
2957
- );
2958
- 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: [
2959
- prefix ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "sia-input-control__affix", children: prefix }) : null,
2960
- floatingPlaceholder ? /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("span", { className: "sia-input-control__input-wrap", children: [
2961
- input,
2962
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "sia-input-control__floating-placeholder", "aria-hidden": "true", children: placeholder })
2963
- ] }) : input,
2964
- 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,
2965
- suffix ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "sia-input-control__affix", children: suffix }) : null,
2966
- 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: [
2967
- currentText.length,
2968
- maxLength !== void 0 ? `/${maxLength}` : null
2969
- ] }) : null
2970
- ] });
2971
- if (!label && !hint) return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: `sia-field ${className}`.trim(), children: control });
2972
- return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("span", { className: `sia-field ${className}`.trim(), children: [
2973
- label ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("label", { className: "sia-field__label", htmlFor: inputId, children: label }) : null,
2974
- control,
2975
- hint ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { id: hintId, className: `sia-field__hint sia-field__hint--${status}`, children: hint }) : null
2461
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("label", { className: `sia-checkbox sia-checkbox--${size}${disabled || group?.disabled ? " is-disabled" : ""} ${className}`.trim(), children: [
2462
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
2463
+ "input",
2464
+ {
2465
+ ...withTitleTooltip(props),
2466
+ ref: setRef,
2467
+ id: props.id ?? fallbackId,
2468
+ className: "sia-checkbox__input",
2469
+ type: "checkbox",
2470
+ name: group?.name ?? props.name,
2471
+ disabled: disabled || group?.disabled,
2472
+ checked: currentChecked,
2473
+ defaultChecked: grouped ? void 0 : defaultChecked,
2474
+ "aria-checked": indeterminate ? "mixed" : currentChecked,
2475
+ onChange: (event) => {
2476
+ if (grouped) group.toggle(value, event.currentTarget.checked);
2477
+ onChange?.(event.currentTarget.checked, event);
2478
+ }
2479
+ }
2480
+ ),
2481
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "sia-checkbox__control", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", {}) }),
2482
+ children !== void 0 ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "sia-checkbox__label", children }) : null
2976
2483
  ] });
2977
2484
  });
2978
-
2979
- // src/components/SelectionPanel.tsx
2980
- var import_react20 = require("react");
2981
-
2982
- // src/components/FloatingScrollbarProvider.tsx
2983
- var import_react19 = require("react");
2984
-
2985
- // src/components/useScrollbarProximity.ts
2986
- var import_react18 = require("react");
2987
- var users = 0;
2988
- var cleanup;
2989
- function useScrollbarProximity() {
2990
- (0, import_react18.useEffect)(() => {
2991
- if (users++ === 0) {
2992
- let frame = 0;
2993
- let x = -Infinity;
2994
- let y = -Infinity;
2995
- const nearby = /* @__PURE__ */ new Set();
2996
- const clear = () => {
2997
- nearby.forEach((track) => track.removeAttribute("data-scrollbar-near"));
2998
- nearby.clear();
2999
- };
3000
- const update = () => {
3001
- frame = 0;
3002
- const next = /* @__PURE__ */ new Set();
3003
- document.querySelectorAll(".sia-scrollbar-track").forEach((track) => {
3004
- const rect = track.getBoundingClientRect();
3005
- const proximity = Number.parseFloat(getComputedStyle(track).getPropertyValue("--sia-scrollbar-proximity")) || 16;
3006
- if (rect.width && rect.height && x >= rect.left - proximity && x <= rect.right + proximity && y >= rect.top - proximity && y <= rect.bottom + proximity) {
3007
- next.add(track);
3008
- if (!nearby.has(track)) track.setAttribute("data-scrollbar-near", "true");
3009
- }
3010
- });
3011
- nearby.forEach((track) => {
3012
- if (!next.has(track)) track.removeAttribute("data-scrollbar-near");
3013
- });
3014
- nearby.clear();
3015
- next.forEach((track) => nearby.add(track));
3016
- };
3017
- const move = (event) => {
3018
- if (event.pointerType === "touch") return;
3019
- x = event.clientX;
3020
- y = event.clientY;
3021
- if (!frame) frame = requestAnimationFrame(update);
3022
- };
3023
- const leave = () => {
3024
- x = y = -Infinity;
3025
- clear();
3026
- };
3027
- document.addEventListener("pointermove", move, { passive: true });
3028
- document.documentElement.addEventListener("pointerleave", leave);
3029
- window.addEventListener("blur", leave);
3030
- cleanup = () => {
3031
- if (frame) cancelAnimationFrame(frame);
3032
- document.removeEventListener("pointermove", move);
3033
- document.documentElement.removeEventListener("pointerleave", leave);
3034
- window.removeEventListener("blur", leave);
3035
- clear();
3036
- };
2485
+ function CheckboxGroup({
2486
+ size: sizeProp,
2487
+ columns,
2488
+ direction = "horizontal",
2489
+ gap,
2490
+ value,
2491
+ defaultValue = [],
2492
+ options,
2493
+ disabled,
2494
+ name,
2495
+ className = "",
2496
+ children,
2497
+ onChange
2498
+ }) {
2499
+ const size = useControlSize(sizeProp);
2500
+ const gridColumns = columns !== void 0 && Number.isInteger(columns) && columns > 0 ? columns : void 0;
2501
+ const layoutStyle = {
2502
+ gridTemplateColumns: gridColumns ? `repeat(${gridColumns}, minmax(0, 1fr))` : void 0,
2503
+ gap: gap === void 0 ? void 0 : typeof gap === "number" ? Math.max(0, gap) : `${Math.max(0, gap[0])}px ${Math.max(0, gap[1])}px`
2504
+ };
2505
+ const [values, setValues] = useControllableState({ value, defaultValue, onChange });
2506
+ const context = (0, import_react13.useMemo)(() => ({
2507
+ values,
2508
+ disabled,
2509
+ name,
2510
+ toggle(optionValue, checked) {
2511
+ const next = checked ? values.includes(optionValue) ? values : [...values, optionValue] : values.filter((item) => item !== optionValue);
2512
+ setValues(next);
3037
2513
  }
3038
- return () => {
3039
- if (--users === 0) {
3040
- cleanup?.();
3041
- cleanup = void 0;
3042
- }
3043
- };
3044
- }, []);
2514
+ }), [disabled, name, setValues, values]);
2515
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(ControlSizeContext.Provider, { value: size, children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(CheckboxGroupContext.Provider, { value: context, children: /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: `sia-checkbox-group${gridColumns ? " sia-checkbox-group--grid" : direction === "vertical" ? " sia-checkbox-group--vertical" : ""} ${className}`.trim(), style: layoutStyle, role: "group", children: [
2516
+ options?.map((option) => {
2517
+ const normalized = typeof option === "object" ? option : { label: String(option), value: option };
2518
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(CheckboxRoot, { value: normalized.value, disabled: normalized.disabled, children: normalized.label }, normalized.value);
2519
+ }),
2520
+ children
2521
+ ] }) }) });
3045
2522
  }
2523
+ var Checkbox2 = Object.assign(CheckboxRoot, {
2524
+ /** 组合多个子项并统一管理布局或选择状态。 */
2525
+ Group: CheckboxGroup
2526
+ });
3046
2527
 
3047
- // src/components/FloatingScrollbarProvider.tsx
3048
- var scrollableOverflow = /* @__PURE__ */ new Set(["auto", "scroll", "overlay"]);
3049
- var clippingOverflow = /* @__PURE__ */ new Set(["auto", "scroll", "overlay", "hidden", "clip"]);
3050
- function isRootScroller(target) {
3051
- return target === document.scrollingElement || target === document.documentElement;
2528
+ // src/components/FloatingDisplay.tsx
2529
+ var import_jsx_runtime13 = require("react/jsx-runtime");
2530
+ var FLOATING_GAP = 9;
2531
+ var FLOATING_VIEWPORT_PADDING = 8;
2532
+ var FLOATING_ARROW_EDGE_OFFSET = 16;
2533
+ var activeTooltipClose;
2534
+ function clampFloating(value, min, max) {
2535
+ return Math.min(Math.max(value, min), Math.max(min, max));
3052
2536
  }
3053
- function canScroll(target, axis) {
3054
- if (target.matches(".sia-table__viewport, [data-sia-native-scrollbar]")) return false;
3055
- if (isRootScroller(target)) {
3056
- return axis === "horizontal" ? target.scrollWidth > target.clientWidth + 1 : target.scrollHeight > target.clientHeight + 1;
2537
+ function resolveFloatingPosition(trigger, overlay, placement) {
2538
+ const viewportWidth = document.documentElement.clientWidth;
2539
+ const viewportHeight = document.documentElement.clientHeight;
2540
+ let resolvedPlacement = placement;
2541
+ if (placement.startsWith("top") && trigger.top - overlay.height - FLOATING_GAP < FLOATING_VIEWPORT_PADDING && trigger.bottom + overlay.height + FLOATING_GAP <= viewportHeight - FLOATING_VIEWPORT_PADDING) {
2542
+ resolvedPlacement = placement.replace("top", "bottom");
2543
+ } else if (placement.startsWith("bottom") && trigger.bottom + overlay.height + FLOATING_GAP > viewportHeight - FLOATING_VIEWPORT_PADDING && trigger.top - overlay.height - FLOATING_GAP >= FLOATING_VIEWPORT_PADDING) {
2544
+ resolvedPlacement = placement.replace("bottom", "top");
2545
+ } else if (placement === "left" && trigger.left - overlay.width - FLOATING_GAP < FLOATING_VIEWPORT_PADDING && trigger.right + overlay.width + FLOATING_GAP <= viewportWidth - FLOATING_VIEWPORT_PADDING) {
2546
+ resolvedPlacement = "right";
2547
+ } else if (placement === "right" && trigger.right + overlay.width + FLOATING_GAP > viewportWidth - FLOATING_VIEWPORT_PADDING && trigger.left - overlay.width - FLOATING_GAP >= FLOATING_VIEWPORT_PADDING) {
2548
+ resolvedPlacement = "left";
3057
2549
  }
3058
- const style = getComputedStyle(target);
3059
- const overflow = axis === "horizontal" ? style.overflowX : style.overflowY;
3060
- const hasOverflow = axis === "horizontal" ? target.scrollWidth > target.clientWidth + 1 : target.scrollHeight > target.clientHeight + 1;
3061
- return hasOverflow && scrollableOverflow.has(overflow);
3062
- }
3063
- function visibleRect(target) {
3064
- if (isRootScroller(target)) {
3065
- return { top: 0, right: window.innerWidth, bottom: window.innerHeight, left: 0, width: window.innerWidth, height: window.innerHeight };
2550
+ let top = trigger.top;
2551
+ let left = trigger.left;
2552
+ if (resolvedPlacement.startsWith("top") || resolvedPlacement.startsWith("bottom")) {
2553
+ top = resolvedPlacement.startsWith("top") ? trigger.top - overlay.height - FLOATING_GAP : trigger.bottom + FLOATING_GAP;
2554
+ left = resolvedPlacement.endsWith("-start") ? trigger.left : resolvedPlacement.endsWith("-end") ? trigger.right - overlay.width : trigger.left + (trigger.width - overlay.width) / 2;
2555
+ } else {
2556
+ top = trigger.top + (trigger.height - overlay.height) / 2;
2557
+ left = resolvedPlacement === "left" ? trigger.left - overlay.width - FLOATING_GAP : trigger.right + FLOATING_GAP;
3066
2558
  }
3067
- const ownRect = target.getBoundingClientRect();
3068
- let top = Math.max(0, ownRect.top);
3069
- let right = Math.min(window.innerWidth, ownRect.right);
3070
- let bottom = Math.min(window.innerHeight, ownRect.bottom);
3071
- let left = Math.max(0, ownRect.left);
3072
- let parent = target.parentElement;
3073
- while (parent && parent !== document.body && parent !== document.documentElement) {
3074
- const style = getComputedStyle(parent);
3075
- const clipX = clippingOverflow.has(style.overflowX);
3076
- const clipY = clippingOverflow.has(style.overflowY);
3077
- if (clipX || clipY) {
3078
- const rect = parent.getBoundingClientRect();
3079
- if (clipX) {
3080
- left = Math.max(left, rect.left);
3081
- right = Math.min(right, rect.right);
3082
- }
3083
- if (clipY) {
3084
- top = Math.max(top, rect.top);
3085
- bottom = Math.min(bottom, rect.bottom);
3086
- }
3087
- }
3088
- parent = parent.parentElement;
2559
+ top = clampFloating(top, FLOATING_VIEWPORT_PADDING, viewportHeight - overlay.height - FLOATING_VIEWPORT_PADDING);
2560
+ left = clampFloating(left, FLOATING_VIEWPORT_PADDING, viewportWidth - overlay.width - FLOATING_VIEWPORT_PADDING);
2561
+ const result = { top, left, placement: resolvedPlacement };
2562
+ if (resolvedPlacement.startsWith("top") || resolvedPlacement.startsWith("bottom")) {
2563
+ result.arrowX = resolvedPlacement.endsWith("-start") ? clampFloating(FLOATING_ARROW_EDGE_OFFSET, 8, overlay.width - 16) : resolvedPlacement.endsWith("-end") ? clampFloating(overlay.width - FLOATING_ARROW_EDGE_OFFSET - 8, 8, overlay.width - 16) : clampFloating(trigger.left + trigger.width / 2 - left - 4, 8, overlay.width - 16);
2564
+ } else {
2565
+ result.arrowY = clampFloating(trigger.top + trigger.height / 2 - top - 4, 8, overlay.height - 16);
3089
2566
  }
3090
- return {
3091
- top,
3092
- right,
3093
- bottom,
3094
- left,
3095
- width: Math.max(0, right - left),
3096
- height: Math.max(0, bottom - top)
3097
- };
3098
- }
3099
- function createTrack(axis) {
3100
- const track = document.createElement("div");
3101
- const thumb = document.createElement("div");
3102
- track.className = `sia-scrollbar-track sia-floating-scrollbar sia-floating-scrollbar--${axis}`;
3103
- thumb.className = "sia-floating-scrollbar__thumb";
3104
- track.dataset.axis = axis;
3105
- track.setAttribute("aria-hidden", "true");
3106
- track.append(thumb);
3107
- return { track, thumb };
2567
+ return result;
3108
2568
  }
3109
- function FloatingScrollbarProvider({ disabled = false, targetRef }) {
3110
- useScrollbarProximity();
3111
- (0, import_react19.useEffect)(() => {
3112
- if (disabled) return;
3113
- const layer = document.createElement("div");
3114
- layer.className = "sia-floating-scrollbar-layer";
3115
- layer.setAttribute("aria-hidden", "true");
3116
- document.body.append(layer);
3117
- const entries = /* @__PURE__ */ new Map();
3118
- let scanFrame = 0;
3119
- let updateFrame = 0;
3120
- const setEntryVisible = (entry, visible) => {
3121
- entry.horizontalTrack.classList.toggle("sia-floating-scrollbar--visible", visible);
3122
- entry.verticalTrack.classList.toggle("sia-floating-scrollbar--visible", visible);
2569
+ function Floating({
2570
+ children,
2571
+ content,
2572
+ placement = "top",
2573
+ trigger = "hover",
2574
+ open,
2575
+ defaultOpen = false,
2576
+ mouseEnterDelay = 0.1,
2577
+ mouseLeaveDelay = 0.1,
2578
+ arrow = true,
2579
+ color,
2580
+ className = "",
2581
+ overlayClassName = "",
2582
+ role = "tooltip",
2583
+ onOpenChange
2584
+ }) {
2585
+ const rootRef = (0, import_react14.useRef)(null);
2586
+ const overlayRef = (0, import_react14.useRef)(null);
2587
+ const enterTimer = (0, import_react14.useRef)();
2588
+ const leaveTimer = (0, import_react14.useRef)();
2589
+ const contentId = (0, import_react14.useId)();
2590
+ const triggers = Array.isArray(trigger) ? trigger : [trigger];
2591
+ const [visible, setVisible] = useControllableState({ value: open, defaultValue: defaultOpen, onChange: onOpenChange });
2592
+ const [position, setPosition] = (0, import_react14.useState)();
2593
+ const setVisibleRef = (0, import_react14.useRef)(setVisible);
2594
+ setVisibleRef.current = setVisible;
2595
+ const closeSelfRef = (0, import_react14.useRef)();
2596
+ if (!closeSelfRef.current) closeSelfRef.current = () => setVisibleRef.current(false);
2597
+ const exclusiveTooltip = role === "tooltip" && triggers.includes("hover");
2598
+ function openNow() {
2599
+ if (exclusiveTooltip && activeTooltipClose !== closeSelfRef.current) {
2600
+ activeTooltipClose?.();
2601
+ activeTooltipClose = closeSelfRef.current;
2602
+ }
2603
+ setVisible(true);
2604
+ }
2605
+ function closeNow() {
2606
+ if (activeTooltipClose === closeSelfRef.current) activeTooltipClose = void 0;
2607
+ setVisible(false);
2608
+ }
2609
+ function clearTimers() {
2610
+ window.clearTimeout(enterTimer.current);
2611
+ window.clearTimeout(leaveTimer.current);
2612
+ }
2613
+ function show(delay = 0) {
2614
+ clearTimers();
2615
+ if (delay <= 0) openNow();
2616
+ else enterTimer.current = window.setTimeout(openNow, delay * 1e3);
2617
+ }
2618
+ function hide(delay = 0) {
2619
+ clearTimers();
2620
+ if (delay <= 0) closeNow();
2621
+ else leaveTimer.current = window.setTimeout(closeNow, delay * 1e3);
2622
+ }
2623
+ (0, import_react14.useEffect)(() => {
2624
+ if (!visible || !triggers.includes("click")) return;
2625
+ const close = (event) => {
2626
+ const target = event.target;
2627
+ if (!rootRef.current?.contains(target) && !overlayRef.current?.contains(target)) closeNow();
3123
2628
  };
3124
- const showEntry = (entry) => {
3125
- if (entry.hideTimer !== null) window.clearTimeout(entry.hideTimer);
3126
- entry.hideTimer = null;
3127
- setEntryVisible(entry, true);
2629
+ document.addEventListener("pointerdown", close);
2630
+ return () => document.removeEventListener("pointerdown", close);
2631
+ }, [triggers.join("|"), visible]);
2632
+ (0, import_react14.useLayoutEffect)(() => {
2633
+ if (!visible) {
2634
+ setPosition(void 0);
2635
+ return;
2636
+ }
2637
+ const update = () => {
2638
+ if (!rootRef.current || !overlayRef.current) return;
2639
+ setPosition(resolveFloatingPosition(rootRef.current.getBoundingClientRect(), overlayRef.current.getBoundingClientRect(), placement));
3128
2640
  };
3129
- const scheduleHide = (entry, delay = 500) => {
3130
- if (entry.hideTimer !== null) window.clearTimeout(entry.hideTimer);
3131
- entry.hideTimer = window.setTimeout(() => {
3132
- entry.hideTimer = null;
3133
- if (!entry.hoveringTarget && !entry.hoveringTrack && !entry.dragging) setEntryVisible(entry, false);
3134
- }, delay);
2641
+ update();
2642
+ window.addEventListener("resize", update);
2643
+ window.addEventListener("scroll", update, true);
2644
+ const observer = new ResizeObserver(update);
2645
+ if (rootRef.current) observer.observe(rootRef.current);
2646
+ if (overlayRef.current) observer.observe(overlayRef.current);
2647
+ return () => {
2648
+ window.removeEventListener("resize", update);
2649
+ window.removeEventListener("scroll", update, true);
2650
+ observer.disconnect();
3135
2651
  };
3136
- const updateEntry = (entry) => {
3137
- const { target, horizontalTrack, horizontalThumb, verticalTrack, verticalThumb } = entry;
3138
- const hasHorizontal = canScroll(target, "horizontal");
3139
- const hasVertical = canScroll(target, "vertical");
3140
- const rect = visibleRect(target);
3141
- const thickness = 10;
3142
- horizontalTrack.hidden = !hasHorizontal || rect.width <= thickness || rect.height <= 0;
3143
- verticalTrack.hidden = !hasVertical || rect.height <= thickness || rect.width <= 0;
3144
- if (!horizontalTrack.hidden) {
3145
- const trackWidth = Math.max(0, rect.width - (hasVertical ? thickness : 0));
3146
- const thumbWidth = Math.max(24, Math.min(trackWidth, trackWidth * target.clientWidth / target.scrollWidth));
3147
- const travel = Math.max(0, trackWidth - thumbWidth);
3148
- const maxScroll = Math.max(1, target.scrollWidth - target.clientWidth);
3149
- horizontalTrack.style.left = `${rect.left}px`;
3150
- horizontalTrack.style.top = `${rect.bottom - thickness}px`;
3151
- horizontalTrack.style.width = `${trackWidth}px`;
3152
- horizontalTrack.style.height = `${thickness}px`;
3153
- horizontalThumb.style.width = `${thumbWidth}px`;
3154
- horizontalThumb.style.transform = `translate3d(${travel * target.scrollLeft / maxScroll}px, 0, 0)`;
3155
- }
3156
- if (!verticalTrack.hidden) {
3157
- const trackHeight = Math.max(0, rect.height - (hasHorizontal ? thickness : 0));
3158
- const thumbHeight = Math.max(24, Math.min(trackHeight, trackHeight * target.clientHeight / target.scrollHeight));
3159
- const travel = Math.max(0, trackHeight - thumbHeight);
3160
- const maxScroll = Math.max(1, target.scrollHeight - target.clientHeight);
3161
- verticalTrack.style.left = `${rect.right - thickness}px`;
3162
- verticalTrack.style.top = `${rect.top}px`;
3163
- verticalTrack.style.width = `${thickness}px`;
3164
- verticalTrack.style.height = `${trackHeight}px`;
3165
- verticalThumb.style.height = `${thumbHeight}px`;
3166
- verticalThumb.style.transform = `translate3d(0, ${travel * target.scrollTop / maxScroll}px, 0)`;
2652
+ }, [children, content, placement, visible]);
2653
+ (0, import_react14.useEffect)(() => () => {
2654
+ clearTimers();
2655
+ if (activeTooltipClose === closeSelfRef.current) activeTooltipClose = void 0;
2656
+ }, []);
2657
+ const child = (0, import_react14.cloneElement)(children, {
2658
+ ...triggers.includes("hover") ? { onMouseEnter: (event) => {
2659
+ children.props.onMouseEnter?.(event);
2660
+ show(mouseEnterDelay);
2661
+ }, onMouseLeave: (event) => {
2662
+ children.props.onMouseLeave?.(event);
2663
+ hide(mouseLeaveDelay);
2664
+ } } : {},
2665
+ ...triggers.includes("focus") ? { onFocus: (event) => {
2666
+ children.props.onFocus?.(event);
2667
+ show();
2668
+ }, onBlur: (event) => {
2669
+ children.props.onBlur?.(event);
2670
+ hide();
2671
+ } } : {},
2672
+ ...triggers.includes("click") ? { onClick: (event) => {
2673
+ children.props.onClick?.(event);
2674
+ if (visible) closeNow();
2675
+ else openNow();
2676
+ } } : {},
2677
+ "aria-describedby": visible ? contentId : void 0
2678
+ });
2679
+ const overlay = visible && content !== null && content !== void 0 && typeof document !== "undefined" ? (0, import_react_dom2.createPortal)(
2680
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
2681
+ "span",
2682
+ {
2683
+ ref: overlayRef,
2684
+ id: contentId,
2685
+ role,
2686
+ "data-custom-color": color ? "" : void 0,
2687
+ className: `sia-floating__overlay sia-floating__overlay--${position?.placement ?? placement} ${overlayClassName}`.trim(),
2688
+ style: {
2689
+ "--sia-floating-color": color,
2690
+ "--sia-floating-arrow-x": position?.arrowX == null ? void 0 : `${position.arrowX}px`,
2691
+ "--sia-floating-arrow-y": position?.arrowY == null ? void 0 : `${position.arrowY}px`,
2692
+ top: position?.top ?? 0,
2693
+ left: position?.left ?? 0,
2694
+ visibility: position ? "visible" : "hidden"
2695
+ },
2696
+ onMouseEnter: triggers.includes("hover") ? () => show() : void 0,
2697
+ onMouseLeave: triggers.includes("hover") ? () => hide(mouseLeaveDelay) : void 0,
2698
+ children: [
2699
+ content,
2700
+ arrow ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "sia-floating__arrow" }) : null
2701
+ ]
3167
2702
  }
2703
+ ),
2704
+ document.body
2705
+ ) : null;
2706
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_jsx_runtime13.Fragment, { children: [
2707
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { ref: rootRef, className: `sia-floating ${className}`.trim(), children: child }),
2708
+ overlay
2709
+ ] });
2710
+ }
2711
+ function Tooltip(props) {
2712
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Floating, { ...props, content: props.title });
2713
+ }
2714
+
2715
+ // src/components/Feedback.tsx
2716
+ var import_jsx_runtime14 = require("react/jsx-runtime");
2717
+ function Progress({
2718
+ percent = 0,
2719
+ type = "line",
2720
+ status,
2721
+ showInfo = true,
2722
+ format,
2723
+ strokeColor,
2724
+ trailColor,
2725
+ strokeWidth = 8,
2726
+ width = 120,
2727
+ steps,
2728
+ success,
2729
+ size = "default",
2730
+ className = "",
2731
+ style,
2732
+ ...props
2733
+ }) {
2734
+ const safe = Math.max(0, Math.min(100, percent));
2735
+ const resolved = status ?? (safe >= 100 ? "success" : "normal");
2736
+ const color = typeof strokeColor === "string" ? strokeColor : void 0;
2737
+ const background = typeof strokeColor === "object" ? `linear-gradient(90deg, ${strokeColor.from}, ${strokeColor.to})` : color;
2738
+ 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}%`);
2739
+ if (type !== "line") {
2740
+ const radius = 46;
2741
+ const circumference = 2 * Math.PI * radius;
2742
+ const dash = circumference * (safe / 100);
2743
+ 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: [
2744
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("svg", { viewBox: "0 0 100 100", role: "progressbar", "aria-valuenow": safe, children: [
2745
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("circle", { className: "sia-progress__trail", cx: "50", cy: "50", r: radius, style: { stroke: trailColor } }),
2746
+ /* @__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 } })
2747
+ ] }),
2748
+ showInfo ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "sia-progress__text", children: label }) : null
2749
+ ] });
2750
+ }
2751
+ const height = Array.isArray(size) ? size[1] : typeof size === "number" ? size : size === "small" ? 6 : strokeWidth;
2752
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: `sia-progress sia-progress--line sia-progress--${resolved} ${className}`.trim(), style, ...withTitleTooltip(props), children: [
2753
+ /* @__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: [
2754
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "sia-progress__bar", style: { width: `${safe}%`, background } }),
2755
+ success?.percent ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "sia-progress__success", style: { width: `${success.percent}%`, background: success.strokeColor } }) : null
2756
+ ] }) }),
2757
+ showInfo ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "sia-progress__info", children: label }) : null
2758
+ ] });
2759
+ }
2760
+
2761
+ // src/components/DataDisplay.tsx
2762
+ var import_react17 = require("react");
2763
+ var import_react_dom5 = require("react-dom");
2764
+
2765
+ // src/components/Overlay.tsx
2766
+ var import_react16 = require("react");
2767
+ var import_react_dom4 = require("react-dom");
2768
+ var bodyLockCount = 0;
2769
+ var previousBodyOverflow = "";
2770
+ var overlayStack = [];
2771
+ function lockBodyScroll() {
2772
+ if (bodyLockCount === 0) {
2773
+ previousBodyOverflow = document.body.style.overflow;
2774
+ document.body.style.overflow = "hidden";
2775
+ }
2776
+ bodyLockCount += 1;
2777
+ return () => {
2778
+ bodyLockCount = Math.max(0, bodyLockCount - 1);
2779
+ if (bodyLockCount === 0) document.body.style.overflow = previousBodyOverflow;
2780
+ };
2781
+ }
2782
+ function focusableElements(container) {
2783
+ return [...container.querySelectorAll(
2784
+ 'a[href], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])'
2785
+ )].filter((element) => !element.hidden && element.getAttribute("aria-hidden") !== "true");
2786
+ }
2787
+ function useOverlayLifecycle(open, panelRef, onRequestClose, keyboard = true) {
2788
+ const overlayIdRef = (0, import_react16.useRef)(/* @__PURE__ */ Symbol("sia-overlay"));
2789
+ const closeRef = (0, import_react16.useRef)(onRequestClose);
2790
+ closeRef.current = onRequestClose;
2791
+ (0, import_react16.useEffect)(() => {
2792
+ if (!open || typeof document === "undefined") return void 0;
2793
+ const overlayId = overlayIdRef.current;
2794
+ const previouslyFocused = document.activeElement instanceof HTMLElement ? document.activeElement : null;
2795
+ overlayStack.push(overlayId);
2796
+ const unlockBody = lockBodyScroll();
2797
+ const focusTimer = window.setTimeout(() => {
2798
+ const panel = panelRef.current;
2799
+ if (!panel) return;
2800
+ const [firstFocusable] = focusableElements(panel);
2801
+ (firstFocusable ?? panel).focus({ preventScroll: true });
2802
+ }, 0);
2803
+ function handleKeyDown(event) {
2804
+ if (overlayStack.at(-1) !== overlayId) return;
2805
+ if (event.key === "Escape" && keyboard) {
2806
+ event.preventDefault();
2807
+ closeRef.current();
2808
+ return;
2809
+ }
2810
+ if (event.key !== "Tab" || !panelRef.current) return;
2811
+ const focusable = focusableElements(panelRef.current);
2812
+ if (focusable.length === 0) {
2813
+ event.preventDefault();
2814
+ panelRef.current.focus();
2815
+ return;
2816
+ }
2817
+ const first = focusable[0];
2818
+ const last = focusable.at(-1);
2819
+ if (event.shiftKey && document.activeElement === first) {
2820
+ event.preventDefault();
2821
+ last.focus();
2822
+ } else if (!event.shiftKey && document.activeElement === last) {
2823
+ event.preventDefault();
2824
+ first.focus();
2825
+ }
2826
+ }
2827
+ document.addEventListener("keydown", handleKeyDown);
2828
+ return () => {
2829
+ window.clearTimeout(focusTimer);
2830
+ document.removeEventListener("keydown", handleKeyDown);
2831
+ const index = overlayStack.lastIndexOf(overlayId);
2832
+ if (index >= 0) overlayStack.splice(index, 1);
2833
+ unlockBody();
2834
+ previouslyFocused?.focus({ preventScroll: true });
3168
2835
  };
3169
- const scheduleUpdateAll = () => {
3170
- if (updateFrame) return;
3171
- updateFrame = window.requestAnimationFrame(() => {
3172
- updateFrame = 0;
3173
- entries.forEach(updateEntry);
3174
- });
3175
- };
3176
- const bindTrackDrag = (entry, axis, track, thumb) => {
3177
- const target = entry.target;
3178
- const onTrackPointerEnter = () => {
3179
- entry.hoveringTrack = true;
3180
- showEntry(entry);
3181
- };
3182
- const onTrackPointerLeave = () => {
3183
- entry.hoveringTrack = false;
3184
- scheduleHide(entry);
3185
- };
3186
- const onTrackPointerDown = (event) => {
3187
- if (event.target === thumb || event.button !== 0) return;
3188
- event.preventDefault();
3189
- event.stopPropagation();
3190
- const trackRect = track.getBoundingClientRect();
3191
- const thumbRect = thumb.getBoundingClientRect();
3192
- const trackLength = axis === "vertical" ? trackRect.height : trackRect.width;
3193
- const thumbLength = axis === "vertical" ? thumbRect.height : thumbRect.width;
3194
- const pointer = axis === "vertical" ? event.clientY - trackRect.top : event.clientX - trackRect.left;
3195
- const ratio = Math.max(0, Math.min(1, (pointer - thumbLength / 2) / Math.max(1, trackLength - thumbLength)));
3196
- if (axis === "vertical") target.scrollTop = ratio * (target.scrollHeight - target.clientHeight);
3197
- else target.scrollLeft = ratio * (target.scrollWidth - target.clientWidth);
3198
- };
3199
- const onThumbPointerDown = (event) => {
3200
- if (event.button !== 0) return;
3201
- event.preventDefault();
3202
- event.stopPropagation();
3203
- entry.dragging = true;
3204
- showEntry(entry);
3205
- thumb.setPointerCapture(event.pointerId);
3206
- const startPointer = axis === "vertical" ? event.clientY : event.clientX;
3207
- const startScroll = axis === "vertical" ? target.scrollTop : target.scrollLeft;
3208
- const trackRect = track.getBoundingClientRect();
3209
- const thumbRect = thumb.getBoundingClientRect();
3210
- const trackLength = axis === "vertical" ? trackRect.height : trackRect.width;
3211
- const thumbLength = axis === "vertical" ? thumbRect.height : thumbRect.width;
3212
- const maxScroll = axis === "vertical" ? target.scrollHeight - target.clientHeight : target.scrollWidth - target.clientWidth;
3213
- const travel = Math.max(1, trackLength - thumbLength);
3214
- const onPointerMove = (moveEvent) => {
3215
- if (moveEvent.pointerId !== event.pointerId) return;
3216
- const pointer = axis === "vertical" ? moveEvent.clientY : moveEvent.clientX;
3217
- const nextScroll = startScroll + (pointer - startPointer) * maxScroll / travel;
3218
- if (axis === "vertical") target.scrollTop = nextScroll;
3219
- else target.scrollLeft = nextScroll;
3220
- };
3221
- const onPointerUp = (upEvent) => {
3222
- if (upEvent.pointerId !== event.pointerId) return;
3223
- entry.dragging = false;
3224
- thumb.releasePointerCapture(event.pointerId);
3225
- window.removeEventListener("pointermove", onPointerMove);
3226
- window.removeEventListener("pointerup", onPointerUp);
3227
- window.removeEventListener("pointercancel", onPointerUp);
3228
- scheduleHide(entry);
3229
- };
3230
- window.addEventListener("pointermove", onPointerMove);
3231
- window.addEventListener("pointerup", onPointerUp);
3232
- window.addEventListener("pointercancel", onPointerUp);
3233
- };
3234
- track.addEventListener("pointerenter", onTrackPointerEnter);
3235
- track.addEventListener("pointerleave", onTrackPointerLeave);
3236
- track.addEventListener("pointerdown", onTrackPointerDown);
3237
- thumb.addEventListener("pointerdown", onThumbPointerDown);
3238
- return () => {
3239
- track.removeEventListener("pointerenter", onTrackPointerEnter);
3240
- track.removeEventListener("pointerleave", onTrackPointerLeave);
3241
- track.removeEventListener("pointerdown", onTrackPointerDown);
3242
- thumb.removeEventListener("pointerdown", onThumbPointerDown);
3243
- };
3244
- };
3245
- const resizeObserver = new ResizeObserver((observedEntries) => {
3246
- observedEntries.forEach(({ target }) => {
3247
- const entry = entries.get(target);
3248
- if (entry) updateEntry(entry);
3249
- });
3250
- });
3251
- const addTarget = (target) => {
3252
- if (entries.has(target)) return;
3253
- const horizontal = createTrack("horizontal");
3254
- const vertical = createTrack("vertical");
3255
- layer.append(horizontal.track, vertical.track);
3256
- const onPointerEnter = () => {
3257
- const entry2 = entries.get(target);
3258
- if (!entry2) return;
3259
- entry2.hoveringTarget = true;
3260
- showEntry(entry2);
3261
- };
3262
- const onPointerLeave = () => {
3263
- const entry2 = entries.get(target);
3264
- if (!entry2) return;
3265
- entry2.hoveringTarget = false;
3266
- scheduleHide(entry2);
3267
- };
3268
- const onScroll = () => {
3269
- const entry2 = entries.get(target);
3270
- if (!entry2) return;
3271
- updateEntry(entry2);
3272
- showEntry(entry2);
3273
- scheduleHide(entry2, 700);
3274
- };
3275
- target.addEventListener("pointerenter", onPointerEnter);
3276
- target.addEventListener("pointerleave", onPointerLeave);
3277
- target.addEventListener("scroll", onScroll, { passive: true });
3278
- const entry = {
3279
- target,
3280
- horizontalTrack: horizontal.track,
3281
- horizontalThumb: horizontal.thumb,
3282
- verticalTrack: vertical.track,
3283
- verticalThumb: vertical.thumb,
3284
- hideTimer: null,
3285
- hoveringTarget: target.matches(":hover"),
3286
- hoveringTrack: false,
3287
- dragging: false,
3288
- cleanup: () => void 0
3289
- };
3290
- const cleanupHorizontal = bindTrackDrag(entry, "horizontal", horizontal.track, horizontal.thumb);
3291
- const cleanupVertical = bindTrackDrag(entry, "vertical", vertical.track, vertical.thumb);
3292
- entry.cleanup = () => {
3293
- if (entry.hideTimer !== null) window.clearTimeout(entry.hideTimer);
3294
- cleanupHorizontal();
3295
- cleanupVertical();
3296
- target.removeEventListener("pointerenter", onPointerEnter);
3297
- target.removeEventListener("pointerleave", onPointerLeave);
3298
- target.removeEventListener("scroll", onScroll);
3299
- resizeObserver.unobserve(target);
3300
- target.removeAttribute("data-sia-floating-scrollbar");
3301
- horizontal.track.remove();
3302
- vertical.track.remove();
3303
- };
3304
- entries.set(target, entry);
3305
- target.setAttribute("data-sia-floating-scrollbar", "");
3306
- resizeObserver.observe(target);
3307
- updateEntry(entry);
3308
- if (entry.hoveringTarget) showEntry(entry);
3309
- };
3310
- const removeTarget = (target) => {
3311
- const entry = entries.get(target);
3312
- if (!entry) return;
3313
- entry.cleanup();
3314
- entries.delete(target);
2836
+ }, [keyboard, open, panelRef]);
2837
+ }
2838
+ function OverlayPortal({ children, container }) {
2839
+ if (typeof document === "undefined") return null;
2840
+ const target = typeof container === "function" ? container() : container ?? document.body;
2841
+ return (0, import_react_dom4.createPortal)(children, target);
2842
+ }
2843
+
2844
+ // src/components/DataDisplay.tsx
2845
+ var import_jsx_runtime15 = require("react/jsx-runtime");
2846
+ function BadgeRibbon({ text, color, placement = "end", className = "", children, ...props }) {
2847
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: `sia-ribbon-wrap ${className}`.trim(), ...withTitleTooltip(props), children: [
2848
+ children,
2849
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: `sia-ribbon sia-ribbon--${placement}`, style: { backgroundColor: color }, children: text })
2850
+ ] });
2851
+ }
2852
+ function Badge({ count, showZero = false, overflowCount = 99, dot = false, status, text, color, offset = [0, 0], size = "default", className = "", children, style, ...props }) {
2853
+ const numeric = typeof count === "number";
2854
+ const hidden = !dot && !status && (count === void 0 || count === null || count === 0 && !showZero);
2855
+ const display = numeric && count > overflowCount ? `${overflowCount}+` : count;
2856
+ 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;
2857
+ if (status && !children) return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("span", { className: `sia-badge sia-badge--status ${className}`.trim(), style, ...withTitleTooltip(props), children: [
2858
+ badge,
2859
+ text ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "sia-badge__text", children: text }) : null
2860
+ ] });
2861
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("span", { className: `sia-badge ${className}`.trim(), style, ...withTitleTooltip(props), children: [
2862
+ children,
2863
+ badge,
2864
+ text && !status ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "sia-badge__text", children: text }) : null
2865
+ ] });
2866
+ }
2867
+ Badge.Ribbon = BadgeRibbon;
2868
+ function CollapsePanel({
2869
+ open,
2870
+ forceRender = false,
2871
+ destroyInactivePanel = false,
2872
+ children
2873
+ }) {
2874
+ const [keepChildren, setKeepChildren] = (0, import_react17.useState)(open || forceRender || !destroyInactivePanel);
2875
+ (0, import_react17.useEffect)(() => {
2876
+ if (open || forceRender || !destroyInactivePanel) {
2877
+ setKeepChildren(true);
2878
+ return void 0;
2879
+ }
2880
+ const timer = window.setTimeout(() => setKeepChildren(false), 240);
2881
+ return () => window.clearTimeout(timer);
2882
+ }, [destroyInactivePanel, forceRender, open]);
2883
+ const renderChildren = open || forceRender || !destroyInactivePanel || keepChildren;
2884
+ 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 }) }) });
2885
+ }
2886
+ function Collapse({ items, activeKey, defaultActiveKey = [], accordion = false, bordered = true, ghost = false, destroyInactivePanel = false, expandIconPosition = "start", onChange, className = "", ...props }) {
2887
+ const normalize = (key) => Array.isArray(key) ? [...key] : key === void 0 ? [] : [key];
2888
+ const controlled = activeKey !== void 0;
2889
+ const [internal, setInternal] = (0, import_react17.useState)(normalize(defaultActiveKey));
2890
+ const openKeys = controlled ? normalize(activeKey) : internal;
2891
+ function toggle(item) {
2892
+ if (item.disabled || item.collapsible === "disabled") return;
2893
+ const open = openKeys.includes(item.key);
2894
+ const next = accordion ? open ? [] : [item.key] : open ? openKeys.filter((key) => key !== item.key) : [...openKeys, item.key];
2895
+ if (!controlled) setInternal(next);
2896
+ onChange?.(accordion ? next[0] ?? "" : next);
2897
+ }
2898
+ const borderClassName = !bordered || ghost ? "sia-collapse--borderless" : "sia-collapse--bordered";
2899
+ 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) => {
2900
+ const open = openKeys.includes(item.key);
2901
+ 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 }) });
2902
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("section", { className: `sia-collapse__item${open ? " is-open" : ""}${item.disabled ? " is-disabled" : ""} ${item.className ?? ""}`.trim(), children: [
2903
+ /* @__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) => {
2904
+ if (event.key === "Enter" || event.key === " ") {
2905
+ event.preventDefault();
2906
+ toggle(item);
2907
+ }
2908
+ }, children: [
2909
+ expandIconPosition === "start" ? arrow : null,
2910
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "sia-collapse__label", children: item.label }),
2911
+ item.extra ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "sia-collapse__extra", onClick: (event) => event.stopPropagation(), children: item.extra }) : null,
2912
+ expandIconPosition === "end" ? arrow : null
2913
+ ] }),
2914
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(CollapsePanel, { open, forceRender: item.forceRender, destroyInactivePanel, children: item.children })
2915
+ ] }, item.key);
2916
+ }) });
2917
+ }
2918
+ function ImagePreview({ open, src, alt = "", onOpenChange }) {
2919
+ const previewRef = (0, import_react17.useRef)(null);
2920
+ const stageRef = (0, import_react17.useRef)(null);
2921
+ const [natural, setNatural] = (0, import_react17.useState)({ src: "", width: 0, height: 0 });
2922
+ const [area, setArea] = (0, import_react17.useState)({ width: 0, height: 0 });
2923
+ const [zoom, setZoom] = (0, import_react17.useState)(1);
2924
+ const [rotation, setRotation] = (0, import_react17.useState)(0);
2925
+ const [flip, setFlip] = (0, import_react17.useState)({ x: 1, y: 1 });
2926
+ const [pan, setPan] = (0, import_react17.useState)({ x: 0, y: 0 });
2927
+ const [failed, setFailed] = (0, import_react17.useState)(false);
2928
+ const drag = (0, import_react17.useRef)();
2929
+ useOverlayLifecycle(open, previewRef, () => onOpenChange(false));
2930
+ const reset = () => {
2931
+ setZoom(1);
2932
+ setRotation(0);
2933
+ setFlip({ x: 1, y: 1 });
2934
+ setPan({ x: 0, y: 0 });
2935
+ drag.current = void 0;
2936
+ };
2937
+ (0, import_react17.useEffect)(() => {
2938
+ reset();
2939
+ setFailed(false);
2940
+ }, [open, src]);
2941
+ (0, import_react17.useEffect)(() => {
2942
+ const stage = stageRef.current;
2943
+ if (!open || !stage) return;
2944
+ const measure = () => {
2945
+ setArea({ width: stage.clientWidth, height: stage.clientHeight });
2946
+ setPan({ x: 0, y: 0 });
3315
2947
  };
3316
- const scanTargets = () => {
3317
- scanFrame = 0;
3318
- const candidates = (targetRef ? [targetRef.current] : [document.scrollingElement, ...document.querySelectorAll("*")]).filter((target) => target instanceof HTMLElement);
3319
- const nextTargets = /* @__PURE__ */ new Set();
3320
- candidates.forEach((target) => {
3321
- if (canScroll(target, "horizontal") || canScroll(target, "vertical")) {
3322
- nextTargets.add(target);
3323
- if (entries.has(target) || !target.hasAttribute("data-sia-floating-scrollbar")) addTarget(target);
2948
+ measure();
2949
+ const observer = new ResizeObserver(measure);
2950
+ observer.observe(stage);
2951
+ return () => observer.disconnect();
2952
+ }, [open, src]);
2953
+ const ready = natural.src === src && natural.width > 0 && area.width > 0 && area.height > 0 && !failed;
2954
+ const sideways = Math.abs(rotation % 180) === 90;
2955
+ const fit = ready ? Math.min(1, area.width / (sideways ? natural.height : natural.width), area.height / (sideways ? natural.width : natural.height)) : 0;
2956
+ const changeZoom = (factor) => setZoom((value) => Math.max(0.25, Math.min(8, value * factor)));
2957
+ if (!open || !src || typeof document === "undefined") return null;
2958
+ 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: [
2959
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Button, { variant: "text", className: "sia-image-preview__close", "aria-label": "\u5173\u95ED\u9884\u89C8", onClick: (event) => {
2960
+ event.stopPropagation();
2961
+ onOpenChange(false);
2962
+ }, children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Icon, { name: "close" }) }),
2963
+ /* @__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)(
2964
+ "img",
2965
+ {
2966
+ src,
2967
+ alt,
2968
+ draggable: false,
2969
+ 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})` },
2970
+ onLoad: (event) => setNatural({ src, width: event.currentTarget.naturalWidth, height: event.currentTarget.naturalHeight }),
2971
+ onError: () => setFailed(true),
2972
+ onClick: (event) => event.stopPropagation(),
2973
+ onPointerDown: (event) => {
2974
+ if (event.button !== 0) return;
2975
+ event.preventDefault();
2976
+ event.stopPropagation();
2977
+ event.currentTarget.setPointerCapture(event.pointerId);
2978
+ drag.current = { id: event.pointerId, x: event.clientX, y: event.clientY, left: pan.x, top: pan.y };
2979
+ },
2980
+ onPointerMove: (event) => {
2981
+ const start = drag.current;
2982
+ if (start?.id === event.pointerId) setPan({ x: start.left + event.clientX - start.x, y: start.top + event.clientY - start.y });
2983
+ },
2984
+ onPointerUp: () => {
2985
+ drag.current = void 0;
2986
+ },
2987
+ onPointerCancel: () => {
2988
+ drag.current = void 0;
2989
+ },
2990
+ onLostPointerCapture: () => {
2991
+ drag.current = void 0;
2992
+ }
2993
+ },
2994
+ src
2995
+ ) }),
2996
+ /* @__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: [
2997
+ /* @__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 }) }),
2998
+ /* @__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 }) }),
2999
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Button, { variant: "text", title: "\u5411\u5DE6\u65CB\u8F6C", "aria-label": "\u5411\u5DE6\u65CB\u8F6C", disabled: !ready, onClick: () => {
3000
+ setRotation((value) => value - 90);
3001
+ setPan({ x: 0, y: 0 });
3002
+ }, children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Icon, { name: "rotate-left", size: 20 }) }),
3003
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Button, { variant: "text", title: "\u5411\u53F3\u65CB\u8F6C", "aria-label": "\u5411\u53F3\u65CB\u8F6C", disabled: !ready, onClick: () => {
3004
+ setRotation((value) => value + 90);
3005
+ setPan({ x: 0, y: 0 });
3006
+ }, children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Icon, { name: "rotate-right", size: 20 }) }),
3007
+ /* @__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 }) }),
3008
+ /* @__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" }),
3009
+ /* @__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 }) }),
3010
+ /* @__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 }) })
3011
+ ] })
3012
+ ] }), document.body);
3013
+ }
3014
+
3015
+ // src/components/FloatingPlaceholder.tsx
3016
+ var import_jsx_runtime16 = require("react/jsx-runtime");
3017
+ function useFloatingPlaceholder({ mode, placeholder, filled, active = false }) {
3018
+ const enabled = usePlaceholderMode(mode) === "floating" && Boolean(placeholder);
3019
+ return {
3020
+ enabled,
3021
+ className: enabled ? ` sia-placeholder-floating${filled ? " sia-placeholder-floating--filled" : ""}${active ? " sia-placeholder-floating--active" : ""}` : "",
3022
+ label: enabled ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "sia-floating-placeholder", "aria-hidden": "true", children: placeholder }) : null
3023
+ };
3024
+ }
3025
+
3026
+ // src/components/Select.tsx
3027
+ var import_react22 = require("react");
3028
+
3029
+ // src/components/Modal.tsx
3030
+ var import_react18 = require("react");
3031
+ var import_client3 = require("react-dom/client");
3032
+ var import_jsx_runtime17 = require("react/jsx-runtime");
3033
+ var modalHandles = /* @__PURE__ */ new Set();
3034
+ var MODAL_MOTION_DURATION = 220;
3035
+ var typeIcons = {
3036
+ info: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(Icon, { name: "info", size: 23 }),
3037
+ success: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(Icon, { name: "circle-check", size: 23 }),
3038
+ warning: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(Icon, { name: "warning", size: 23 }),
3039
+ error: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(Icon, { name: "circle-close", size: 23 }),
3040
+ confirm: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(Icon, { name: "question", size: 23 })
3041
+ };
3042
+ var ModalRoot = (0, import_react18.forwardRef)(function Modal({
3043
+ open,
3044
+ title,
3045
+ children,
3046
+ footer,
3047
+ footerExtra,
3048
+ type = "default",
3049
+ okText = "\u786E\u5B9A",
3050
+ cancelText = "\u53D6\u6D88",
3051
+ showCancel = type === "default" || type === "confirm",
3052
+ confirmLoading = false,
3053
+ okButtonProps,
3054
+ cancelButtonProps,
3055
+ closable = true,
3056
+ maskClosable = true,
3057
+ keyboard = true,
3058
+ centered = false,
3059
+ width = 520,
3060
+ height,
3061
+ zIndex = 1e3,
3062
+ getContainer,
3063
+ onOk,
3064
+ onCancel,
3065
+ onOpenChange,
3066
+ afterOpenChange,
3067
+ className = "",
3068
+ style,
3069
+ ...props
3070
+ }, forwardedRef) {
3071
+ const localRef = (0, import_react18.useRef)(null);
3072
+ const titleId = (0, import_react18.useId)();
3073
+ const [submitting, setSubmitting] = (0, import_react18.useState)(false);
3074
+ const [rendered, setRendered] = (0, import_react18.useState)(open);
3075
+ const [closing, setClosing] = (0, import_react18.useState)(false);
3076
+ const afterOpenChangeRef = (0, import_react18.useRef)(afterOpenChange);
3077
+ afterOpenChangeRef.current = afterOpenChange;
3078
+ const submitLock = (0, import_react18.useRef)(false);
3079
+ (0, import_react18.useEffect)(() => {
3080
+ if (!open || !rendered) return void 0;
3081
+ const timer = window.setTimeout(() => afterOpenChangeRef.current?.(true), MODAL_MOTION_DURATION);
3082
+ return () => window.clearTimeout(timer);
3083
+ }, [open, rendered]);
3084
+ (0, import_react18.useEffect)(() => {
3085
+ if (open) {
3086
+ setRendered(true);
3087
+ setClosing(false);
3088
+ return void 0;
3089
+ }
3090
+ if (!rendered) return void 0;
3091
+ setClosing(true);
3092
+ const timer = window.setTimeout(() => {
3093
+ setRendered(false);
3094
+ setClosing(false);
3095
+ afterOpenChangeRef.current?.(false);
3096
+ }, MODAL_MOTION_DURATION);
3097
+ return () => window.clearTimeout(timer);
3098
+ }, [open, rendered]);
3099
+ function setRef(node) {
3100
+ localRef.current = node;
3101
+ if (typeof forwardedRef === "function") forwardedRef(node);
3102
+ else if (forwardedRef) forwardedRef.current = node;
3103
+ }
3104
+ function requestClose() {
3105
+ if (submitting || confirmLoading) return;
3106
+ onCancel?.();
3107
+ onOpenChange?.(false);
3108
+ }
3109
+ async function handleOk() {
3110
+ if (submitLock.current || confirmLoading) return;
3111
+ submitLock.current = true;
3112
+ if (!onOk) {
3113
+ onOpenChange?.(false);
3114
+ submitLock.current = false;
3115
+ return;
3116
+ }
3117
+ try {
3118
+ const result = onOk();
3119
+ if (result instanceof Promise) {
3120
+ setSubmitting(true);
3121
+ const resolved = await result;
3122
+ if (resolved !== false) onOpenChange?.(false);
3123
+ } else if (result !== false) {
3124
+ onOpenChange?.(false);
3125
+ }
3126
+ } catch {
3127
+ return;
3128
+ } finally {
3129
+ submitLock.current = false;
3130
+ setSubmitting(false);
3131
+ }
3132
+ }
3133
+ useOverlayLifecycle(rendered, localRef, requestClose, keyboard);
3134
+ if (!rendered) return null;
3135
+ const mergedStyle = {
3136
+ ...height !== void 0 ? { display: "flex", flexDirection: "column", height, maxHeight: "calc(100dvh - 48px)" } : {},
3137
+ ...style,
3138
+ "--sia-modal-width": typeof width === "number" ? `${width}px` : width
3139
+ };
3140
+ const loading = submitting || confirmLoading;
3141
+ const defaultFooter = /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_jsx_runtime17.Fragment, { children: [
3142
+ showCancel ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(Button, { ...cancelButtonProps, onClick: requestClose, disabled: loading || cancelButtonProps?.disabled, children: cancelText }) : null,
3143
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(Button, { variant: "primary", ...okButtonProps, onClick: handleOk, loading, children: okText })
3144
+ ] });
3145
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(OverlayPortal, { container: getContainer, children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
3146
+ "div",
3147
+ {
3148
+ className: `sia-modal-root${centered ? " sia-modal-root--centered" : ""}`,
3149
+ "data-state": closing ? "closing" : "open",
3150
+ style: { zIndex },
3151
+ onMouseDown: (event) => {
3152
+ if (event.target === event.currentTarget && maskClosable) requestClose();
3153
+ },
3154
+ children: /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
3155
+ "div",
3156
+ {
3157
+ ref: setRef,
3158
+ className: `sia-modal sia-modal--${type} ${className}`.trim(),
3159
+ style: mergedStyle,
3160
+ role: "dialog",
3161
+ "aria-modal": "true",
3162
+ "aria-labelledby": title ? titleId : void 0,
3163
+ tabIndex: -1,
3164
+ ...withTitleTooltip(props),
3165
+ children: [
3166
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "sia-modal__header", style: height !== void 0 ? { flexShrink: 0 } : void 0, children: [
3167
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "sia-modal__heading", children: [
3168
+ typeIcons[type] ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "sia-modal__type-icon", "aria-hidden": "true", children: typeIcons[type] }) : null,
3169
+ title ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("strong", { id: titleId, children: title }) : null
3170
+ ] }),
3171
+ 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
3172
+ ] }),
3173
+ 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,
3174
+ footer !== null ? /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "sia-modal__footer", style: height !== void 0 ? { flexShrink: 0 } : void 0, children: [
3175
+ footer === void 0 && footerExtra != null ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { style: { marginRight: "auto" }, children: footerExtra }) : null,
3176
+ footer ?? defaultFooter
3177
+ ] }) : null
3178
+ ]
3324
3179
  }
3325
- });
3326
- entries.forEach((_entry, target) => {
3327
- if (!target.isConnected || !nextTargets.has(target)) removeTarget(target);
3328
- else updateEntry(_entry);
3329
- });
3330
- };
3331
- const scheduleScan = () => {
3332
- if (scanFrame) return;
3333
- scanFrame = window.requestAnimationFrame(scanTargets);
3334
- };
3335
- const mutationObserver = new MutationObserver((mutations) => {
3336
- if (mutations.some((mutation) => !layer.contains(mutation.target))) scheduleScan();
3337
- });
3338
- mutationObserver.observe(document.documentElement, { subtree: true, childList: true, attributes: true });
3339
- window.addEventListener("resize", scheduleScan);
3340
- window.addEventListener("scroll", scheduleUpdateAll, true);
3341
- scheduleScan();
3342
- return () => {
3343
- mutationObserver.disconnect();
3344
- resizeObserver.disconnect();
3345
- window.removeEventListener("resize", scheduleScan);
3346
- window.removeEventListener("scroll", scheduleUpdateAll, true);
3347
- if (scanFrame) window.cancelAnimationFrame(scanFrame);
3348
- if (updateFrame) window.cancelAnimationFrame(updateFrame);
3349
- entries.forEach((entry) => entry.cleanup());
3350
- entries.clear();
3351
- layer.remove();
3352
- };
3353
- }, [disabled, targetRef]);
3354
- return null;
3180
+ )
3181
+ }
3182
+ ) });
3183
+ });
3184
+ function openModal(config) {
3185
+ if (typeof document === "undefined") return { destroy() {
3186
+ }, update() {
3187
+ } };
3188
+ const container = document.createElement("div");
3189
+ container.className = "sia-modal-api-host";
3190
+ document.body.appendChild(container);
3191
+ const root = (0, import_client3.createRoot)(container);
3192
+ let currentConfig = config;
3193
+ let currentOpen = true;
3194
+ let destroyed = false;
3195
+ let cleanupTimer;
3196
+ function cleanup2() {
3197
+ if (destroyed) return;
3198
+ destroyed = true;
3199
+ if (cleanupTimer !== void 0) window.clearTimeout(cleanupTimer);
3200
+ root.unmount();
3201
+ container.remove();
3202
+ modalHandles.delete(handle);
3203
+ }
3204
+ function closeWithMotion() {
3205
+ if (destroyed || !currentOpen) return;
3206
+ currentOpen = false;
3207
+ render();
3208
+ cleanupTimer = window.setTimeout(cleanup2, MODAL_MOTION_DURATION);
3209
+ }
3210
+ function render() {
3211
+ root.render(/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(ModalRoot, { ...currentConfig, open: currentOpen, onOpenChange: (nextOpen) => {
3212
+ currentConfig.onOpenChange?.(nextOpen);
3213
+ if (!nextOpen) closeWithMotion();
3214
+ } }));
3215
+ }
3216
+ const handle = {
3217
+ destroy: closeWithMotion,
3218
+ update(next) {
3219
+ if (destroyed) return;
3220
+ currentConfig = { ...currentConfig, ...next };
3221
+ render();
3222
+ }
3223
+ };
3224
+ modalHandles.add(handle);
3225
+ render();
3226
+ return handle;
3227
+ }
3228
+ function preset(type, defaults = {}) {
3229
+ return (config) => openModal({
3230
+ okText: type === "confirm" ? "\u786E\u5B9A" : "\u77E5\u9053\u4E86",
3231
+ showCancel: type === "confirm",
3232
+ closable: type !== "confirm",
3233
+ maskClosable: type !== "confirm",
3234
+ ...defaults,
3235
+ ...config,
3236
+ type
3237
+ });
3355
3238
  }
3239
+ var Modal2 = Object.assign(ModalRoot, {
3240
+ /** 直接打开实例,返回可用于更新或关闭的操作句柄。 */
3241
+ open: openModal,
3242
+ /** 显示普通信息提示。 */
3243
+ info: preset("info", { title: "\u63D0\u793A" }),
3244
+ /** 显示成功提示。 */
3245
+ success: preset("success", { title: "\u64CD\u4F5C\u6210\u529F" }),
3246
+ /** 显示警告提示。 */
3247
+ warning: preset("warning", { title: "\u8BF7\u6CE8\u610F" }),
3248
+ /** 显示错误提示。 */
3249
+ error: preset("error", { title: "\u64CD\u4F5C\u5931\u8D25" }),
3250
+ /** 打开确认弹框,支持异步确认;返回 false 可阻止关闭。 */
3251
+ confirm: preset("confirm", { title: "\u786E\u8BA4\u64CD\u4F5C" }),
3252
+ /** 关闭由此 API 打开的所有弹层。 */
3253
+ destroyAll() {
3254
+ [...modalHandles].forEach((handle) => handle.destroy());
3255
+ }
3256
+ });
3257
+
3258
+ // src/components/Input.tsx
3259
+ var import_react19 = require("react");
3260
+ var import_jsx_runtime18 = require("react/jsx-runtime");
3261
+ var Input = (0, import_react19.forwardRef)(function Input2({
3262
+ label,
3263
+ hint,
3264
+ status = "default",
3265
+ size: sizeProp,
3266
+ prefix,
3267
+ suffix,
3268
+ allowClear = true,
3269
+ onClear,
3270
+ showCount = false,
3271
+ placeholderMode,
3272
+ placeholder,
3273
+ className = "",
3274
+ id,
3275
+ disabled,
3276
+ value,
3277
+ defaultValue,
3278
+ maxLength,
3279
+ onChange,
3280
+ ...props
3281
+ }, forwardedRef) {
3282
+ const size = useControlSize(sizeProp);
3283
+ const fallbackId = (0, import_react19.useId)();
3284
+ const inputId = id ?? fallbackId;
3285
+ const localRef = (0, import_react19.useRef)(null);
3286
+ const [uncontrolledValue, setUncontrolledValue] = (0, import_react19.useState)(() => defaultValue === void 0 ? "" : String(defaultValue));
3287
+ const currentText = value !== void 0 ? String(value) : uncontrolledValue;
3288
+ const hasValue = currentText.length > 0;
3289
+ const floatingPlaceholder = usePlaceholderMode(placeholderMode) === "floating" && Boolean(placeholder);
3290
+ const hintId = `${inputId}-hint`;
3291
+ const describedBy = [props["aria-describedby"], hint ? hintId : null].filter(Boolean).join(" ") || void 0;
3292
+ function setRef(node) {
3293
+ localRef.current = node;
3294
+ if (typeof forwardedRef === "function") forwardedRef(node);
3295
+ else if (forwardedRef) forwardedRef.current = node;
3296
+ }
3297
+ function handleChange(event) {
3298
+ if (maxLength !== void 0 && maxLength >= 0 && event.target.value.length > maxLength) {
3299
+ const valueSetter = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, "value")?.set;
3300
+ valueSetter?.call(event.target, event.target.value.slice(0, maxLength));
3301
+ }
3302
+ setUncontrolledValue(event.target.value);
3303
+ onChange?.(event);
3304
+ }
3305
+ function clearValue() {
3306
+ const input2 = localRef.current;
3307
+ if (!input2 || disabled) return;
3308
+ const valueSetter = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, "value")?.set;
3309
+ valueSetter?.call(input2, "");
3310
+ input2.dispatchEvent(new Event("input", { bubbles: true }));
3311
+ setUncontrolledValue("");
3312
+ input2.focus();
3313
+ onClear?.();
3314
+ }
3315
+ function handleControlMouseDown(event) {
3316
+ if (disabled || event.defaultPrevented || event.button !== 0) return;
3317
+ const target = event.target;
3318
+ if (!(target instanceof Element)) return;
3319
+ const interactive = target.closest("input, textarea, select, button, a[href], [tabindex], [contenteditable]:not([contenteditable='false']), [role='button'], [role='link']");
3320
+ if (interactive && event.currentTarget.contains(interactive) && !interactive.classList.contains("sia-input-control__clear")) return;
3321
+ event.preventDefault();
3322
+ localRef.current?.focus({ preventScroll: true });
3323
+ }
3324
+ const input = /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
3325
+ "input",
3326
+ {
3327
+ ...withTitleTooltip(props),
3328
+ ref: setRef,
3329
+ id: inputId,
3330
+ className: "sia-input",
3331
+ disabled,
3332
+ value,
3333
+ defaultValue,
3334
+ maxLength,
3335
+ placeholder,
3336
+ "aria-describedby": describedBy,
3337
+ "aria-invalid": status === "error" || void 0,
3338
+ onChange: handleChange
3339
+ }
3340
+ );
3341
+ 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: [
3342
+ prefix ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "sia-input-control__affix", children: prefix }) : null,
3343
+ floatingPlaceholder ? /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("span", { className: "sia-input-control__input-wrap", children: [
3344
+ input,
3345
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "sia-input-control__floating-placeholder", "aria-hidden": "true", children: placeholder })
3346
+ ] }) : input,
3347
+ 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,
3348
+ suffix ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "sia-input-control__affix", children: suffix }) : null,
3349
+ 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: [
3350
+ currentText.length,
3351
+ maxLength !== void 0 ? `/${maxLength}` : null
3352
+ ] }) : null
3353
+ ] });
3354
+ if (!label && !hint) return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: `sia-field ${className}`.trim(), children: control });
3355
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("span", { className: `sia-field ${className}`.trim(), children: [
3356
+ label ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("label", { className: "sia-field__label", htmlFor: inputId, children: label }) : null,
3357
+ control,
3358
+ hint ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { id: hintId, className: `sia-field__hint sia-field__hint--${status}`, children: hint }) : null
3359
+ ] });
3360
+ });
3356
3361
 
3357
3362
  // src/components/SelectionPanel.tsx
3363
+ var import_react20 = require("react");
3358
3364
  var import_jsx_runtime19 = require("react/jsx-runtime");
3359
3365
  function SelectionPanel({
3360
3366
  options,