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

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.
@@ -15,715 +15,386 @@ import {
15
15
  withTitleTooltip
16
16
  } from "./chunk-H7IAG72O.js";
17
17
 
18
- // src/components/FloatingScrollbarProvider.tsx
19
- import { useEffect as useEffect2 } from "react";
20
-
21
- // src/components/useScrollbarProximity.ts
22
- import { useEffect } from "react";
23
- var users = 0;
24
- var cleanup;
25
- function useScrollbarProximity() {
18
+ // src/components/Checkbox.tsx
19
+ import { createContext, forwardRef, useContext, useEffect, useId, useMemo, useRef } from "react";
20
+ import { jsx, jsxs } from "react/jsx-runtime";
21
+ var CheckboxGroupContext = createContext(null);
22
+ var CheckboxRoot = forwardRef(function Checkbox({ value, size: sizeProp, indeterminate = false, children, disabled, checked, defaultChecked, className = "", onChange, ...props }, forwardedRef) {
23
+ const size = useControlSize(sizeProp);
24
+ const group = useContext(CheckboxGroupContext);
25
+ const localRef = useRef(null);
26
+ const fallbackId = useId();
27
+ const grouped = group !== null && value !== void 0;
28
+ const currentChecked = grouped ? group.values.includes(value) : checked;
26
29
  useEffect(() => {
27
- if (users++ === 0) {
28
- let frame = 0;
29
- let x = -Infinity;
30
- let y = -Infinity;
31
- const nearby = /* @__PURE__ */ new Set();
32
- const clear = () => {
33
- nearby.forEach((track) => track.removeAttribute("data-scrollbar-near"));
34
- nearby.clear();
35
- };
36
- const update = () => {
37
- frame = 0;
38
- const next = /* @__PURE__ */ new Set();
39
- document.querySelectorAll(".sia-scrollbar-track").forEach((track) => {
40
- const rect = track.getBoundingClientRect();
41
- const proximity = Number.parseFloat(getComputedStyle(track).getPropertyValue("--sia-scrollbar-proximity")) || 16;
42
- if (rect.width && rect.height && x >= rect.left - proximity && x <= rect.right + proximity && y >= rect.top - proximity && y <= rect.bottom + proximity) {
43
- next.add(track);
44
- if (!nearby.has(track)) track.setAttribute("data-scrollbar-near", "true");
45
- }
46
- });
47
- nearby.forEach((track) => {
48
- if (!next.has(track)) track.removeAttribute("data-scrollbar-near");
49
- });
50
- nearby.clear();
51
- next.forEach((track) => nearby.add(track));
52
- };
53
- const move = (event) => {
54
- if (event.pointerType === "touch") return;
55
- x = event.clientX;
56
- y = event.clientY;
57
- if (!frame) frame = requestAnimationFrame(update);
58
- };
59
- const leave = () => {
60
- x = y = -Infinity;
61
- clear();
62
- };
63
- document.addEventListener("pointermove", move, { passive: true });
64
- document.documentElement.addEventListener("pointerleave", leave);
65
- window.addEventListener("blur", leave);
66
- cleanup = () => {
67
- if (frame) cancelAnimationFrame(frame);
68
- document.removeEventListener("pointermove", move);
69
- document.documentElement.removeEventListener("pointerleave", leave);
70
- window.removeEventListener("blur", leave);
71
- clear();
72
- };
30
+ if (localRef.current) localRef.current.indeterminate = indeterminate;
31
+ }, [indeterminate]);
32
+ function setRef(node) {
33
+ localRef.current = node;
34
+ if (typeof forwardedRef === "function") forwardedRef(node);
35
+ else if (forwardedRef) forwardedRef.current = node;
36
+ }
37
+ return /* @__PURE__ */ jsxs("label", { className: `sia-checkbox sia-checkbox--${size}${disabled || group?.disabled ? " is-disabled" : ""} ${className}`.trim(), children: [
38
+ /* @__PURE__ */ jsx(
39
+ "input",
40
+ {
41
+ ...withTitleTooltip(props),
42
+ ref: setRef,
43
+ id: props.id ?? fallbackId,
44
+ className: "sia-checkbox__input",
45
+ type: "checkbox",
46
+ name: group?.name ?? props.name,
47
+ disabled: disabled || group?.disabled,
48
+ checked: currentChecked,
49
+ defaultChecked: grouped ? void 0 : defaultChecked,
50
+ "aria-checked": indeterminate ? "mixed" : currentChecked,
51
+ onChange: (event) => {
52
+ if (grouped) group.toggle(value, event.currentTarget.checked);
53
+ onChange?.(event.currentTarget.checked, event);
54
+ }
55
+ }
56
+ ),
57
+ /* @__PURE__ */ jsx("span", { className: "sia-checkbox__control", "aria-hidden": "true", children: /* @__PURE__ */ jsx("span", {}) }),
58
+ children !== void 0 ? /* @__PURE__ */ jsx("span", { className: "sia-checkbox__label", children }) : null
59
+ ] });
60
+ });
61
+ function CheckboxGroup({
62
+ size: sizeProp,
63
+ columns,
64
+ direction = "horizontal",
65
+ gap,
66
+ value,
67
+ defaultValue = [],
68
+ options,
69
+ disabled,
70
+ name,
71
+ className = "",
72
+ children,
73
+ onChange
74
+ }) {
75
+ const size = useControlSize(sizeProp);
76
+ const gridColumns = columns !== void 0 && Number.isInteger(columns) && columns > 0 ? columns : void 0;
77
+ const layoutStyle = {
78
+ gridTemplateColumns: gridColumns ? `repeat(${gridColumns}, minmax(0, 1fr))` : void 0,
79
+ 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`
80
+ };
81
+ const [values, setValues] = useControllableState({ value, defaultValue, onChange });
82
+ const context = useMemo(() => ({
83
+ values,
84
+ disabled,
85
+ name,
86
+ toggle(optionValue, checked) {
87
+ const next = checked ? values.includes(optionValue) ? values : [...values, optionValue] : values.filter((item) => item !== optionValue);
88
+ setValues(next);
73
89
  }
74
- return () => {
75
- if (--users === 0) {
76
- cleanup?.();
77
- cleanup = void 0;
90
+ }), [disabled, name, setValues, values]);
91
+ return /* @__PURE__ */ jsx(ControlSizeContext.Provider, { value: size, children: /* @__PURE__ */ jsx(CheckboxGroupContext.Provider, { value: context, children: /* @__PURE__ */ jsxs("div", { className: `sia-checkbox-group${gridColumns ? " sia-checkbox-group--grid" : direction === "vertical" ? " sia-checkbox-group--vertical" : ""} ${className}`.trim(), style: layoutStyle, role: "group", children: [
92
+ options?.map((option) => {
93
+ const normalized = typeof option === "object" ? option : { label: String(option), value: option };
94
+ return /* @__PURE__ */ jsx(CheckboxRoot, { value: normalized.value, disabled: normalized.disabled, children: normalized.label }, normalized.value);
95
+ }),
96
+ children
97
+ ] }) }) });
98
+ }
99
+ var Checkbox2 = Object.assign(CheckboxRoot, {
100
+ /** 组合多个子项并统一管理布局或选择状态。 */
101
+ Group: CheckboxGroup
102
+ });
103
+
104
+ // src/components/treeCheckState.ts
105
+ function affectedKeys(node) {
106
+ if (node.disabled || node.disableCheckbox) return [];
107
+ return [...node.checkable === false ? [] : [node.key], ...node.children?.flatMap(affectedKeys) ?? []];
108
+ }
109
+ function getTreeCheckState(nodes, keys, strictly = false) {
110
+ const checked = new Set(keys);
111
+ const halfChecked = /* @__PURE__ */ new Set();
112
+ if (strictly) return { checked, halfChecked };
113
+ const explicit = new Set(keys);
114
+ function expand(items) {
115
+ for (const node of items) {
116
+ if (explicit.has(node.key)) affectedKeys(node).forEach((key) => checked.add(key));
117
+ if (node.children) expand(node.children);
118
+ }
119
+ }
120
+ expand(nodes);
121
+ function fold(node) {
122
+ const children = node.children?.map(fold).filter((value) => value !== void 0) ?? [];
123
+ if (node.disabled || node.disableCheckbox) return void 0;
124
+ if (!children.length) return { checked: checked.has(node.key), partial: false };
125
+ const all = children.every((child) => child.checked);
126
+ const some = children.some((child) => child.checked || child.partial);
127
+ if (node.checkable !== false) {
128
+ if (all) checked.add(node.key);
129
+ else checked.delete(node.key);
130
+ if (!all && some) halfChecked.add(node.key);
131
+ }
132
+ return { checked: all, partial: !all && some };
133
+ }
134
+ nodes.forEach(fold);
135
+ return { checked, halfChecked };
136
+ }
137
+ function toggleTreeCheck(nodes, keys, node, strictly = false) {
138
+ const state = getTreeCheckState(nodes, keys, strictly);
139
+ const next = new Set(state.checked);
140
+ const checked = !next.has(node.key);
141
+ if (node.disabled || node.disableCheckbox || node.checkable === false) return { ...state, checkedValue: !checked };
142
+ (strictly ? [node.key] : affectedKeys(node)).forEach((key) => checked ? next.add(key) : next.delete(key));
143
+ if (!strictly && !checked) {
144
+ let removeParents2 = function(items) {
145
+ let found = false;
146
+ for (const item of items) {
147
+ const childFound = item.children ? removeParents2(item.children) : false;
148
+ if (childFound) next.delete(item.key);
149
+ if (item.key === node.key || childFound) found = true;
78
150
  }
151
+ return found;
79
152
  };
80
- }, []);
153
+ var removeParents = removeParents2;
154
+ removeParents2(nodes);
155
+ }
156
+ return { ...getTreeCheckState(nodes, [...next], strictly), checkedValue: checked };
81
157
  }
82
158
 
83
- // src/components/FloatingScrollbarProvider.tsx
84
- var scrollableOverflow = /* @__PURE__ */ new Set(["auto", "scroll", "overlay"]);
85
- var clippingOverflow = /* @__PURE__ */ new Set(["auto", "scroll", "overlay", "hidden", "clip"]);
86
- function isRootScroller(target) {
87
- return target === document.scrollingElement || target === document.documentElement;
159
+ // src/components/FloatingDisplay.tsx
160
+ import { cloneElement, useEffect as useEffect2, useId as useId2, useLayoutEffect, useMemo as useMemo2, useRef as useRef2, useState } from "react";
161
+ import { createPortal } from "react-dom";
162
+ import { Fragment, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
163
+ var FLOATING_GAP = 9;
164
+ var FLOATING_VIEWPORT_PADDING = 8;
165
+ var FLOATING_ARROW_EDGE_OFFSET = 16;
166
+ var activeTooltipClose;
167
+ function clampFloating(value, min, max) {
168
+ return Math.min(Math.max(value, min), Math.max(min, max));
88
169
  }
89
- function canScroll(target, axis) {
90
- if (target.matches(".sia-table__viewport, [data-sia-native-scrollbar]")) return false;
91
- if (isRootScroller(target)) {
92
- return axis === "horizontal" ? target.scrollWidth > target.clientWidth + 1 : target.scrollHeight > target.clientHeight + 1;
93
- }
94
- const style = getComputedStyle(target);
95
- const overflow = axis === "horizontal" ? style.overflowX : style.overflowY;
96
- const hasOverflow = axis === "horizontal" ? target.scrollWidth > target.clientWidth + 1 : target.scrollHeight > target.clientHeight + 1;
97
- return hasOverflow && scrollableOverflow.has(overflow);
170
+ function normalizeFloatingPlacement(placement) {
171
+ const aliases = {
172
+ topLeft: "top-start",
173
+ topRight: "top-end",
174
+ bottomLeft: "bottom-start",
175
+ bottomRight: "bottom-end",
176
+ leftTop: "left-start",
177
+ leftBottom: "left-end",
178
+ rightTop: "right-start",
179
+ rightBottom: "right-end"
180
+ };
181
+ return aliases[placement] ?? placement;
98
182
  }
99
- function visibleRect(target) {
100
- if (isRootScroller(target)) {
101
- return { top: 0, right: window.innerWidth, bottom: window.innerHeight, left: 0, width: window.innerWidth, height: window.innerHeight };
183
+ function resolveFloatingPosition(trigger, overlay, placement, pointAtCenter, adjust) {
184
+ const viewportWidth = document.documentElement.clientWidth;
185
+ const viewportHeight = document.documentElement.clientHeight;
186
+ let resolvedPlacement = normalizeFloatingPlacement(placement);
187
+ const side = resolvedPlacement.split("-")[0];
188
+ const available = { top: trigger.top, bottom: viewportHeight - trigger.bottom, left: trigger.left, right: viewportWidth - trigger.right };
189
+ const opposite = { top: "bottom", bottom: "top", left: "right", right: "left" };
190
+ const preferred = side;
191
+ const other = opposite[preferred];
192
+ const needed = (side === "top" || side === "bottom" ? overlay.height : overlay.width) + FLOATING_GAP + FLOATING_VIEWPORT_PADDING;
193
+ if (adjust && available[preferred] < needed && available[other] > available[preferred]) {
194
+ resolvedPlacement = resolvedPlacement.replace(preferred, other);
102
195
  }
103
- const ownRect = target.getBoundingClientRect();
104
- let top = Math.max(0, ownRect.top);
105
- let right = Math.min(window.innerWidth, ownRect.right);
106
- let bottom = Math.min(window.innerHeight, ownRect.bottom);
107
- let left = Math.max(0, ownRect.left);
108
- let parent = target.parentElement;
109
- while (parent && parent !== document.body && parent !== document.documentElement) {
110
- const style = getComputedStyle(parent);
111
- const clipX = clippingOverflow.has(style.overflowX);
112
- const clipY = clippingOverflow.has(style.overflowY);
113
- if (clipX || clipY) {
114
- const rect = parent.getBoundingClientRect();
115
- if (clipX) {
116
- left = Math.max(left, rect.left);
117
- right = Math.min(right, rect.right);
118
- }
119
- if (clipY) {
120
- top = Math.max(top, rect.top);
121
- bottom = Math.min(bottom, rect.bottom);
122
- }
196
+ const vertical = resolvedPlacement.startsWith("top") || resolvedPlacement.startsWith("bottom");
197
+ const start = resolvedPlacement.endsWith("-start");
198
+ const end = resolvedPlacement.endsWith("-end");
199
+ let top = vertical ? resolvedPlacement.startsWith("top") ? trigger.top - overlay.height - FLOATING_GAP : trigger.bottom + FLOATING_GAP : start ? trigger.top : end ? trigger.bottom - overlay.height : trigger.top + (trigger.height - overlay.height) / 2;
200
+ let left = vertical ? start ? trigger.left : end ? trigger.right - overlay.width : trigger.left + (trigger.width - overlay.width) / 2 : resolvedPlacement.startsWith("left") ? trigger.left - overlay.width - FLOATING_GAP : trigger.right + FLOATING_GAP;
201
+ if (adjust) {
202
+ if (vertical) left = clampFloating(left, FLOATING_VIEWPORT_PADDING, viewportWidth - overlay.width - FLOATING_VIEWPORT_PADDING);
203
+ else top = clampFloating(top, FLOATING_VIEWPORT_PADDING, viewportHeight - overlay.height - FLOATING_VIEWPORT_PADDING);
204
+ }
205
+ const result = { top, left, placement: resolvedPlacement };
206
+ const size = vertical ? trigger.width : trigger.height;
207
+ const anchorStart = vertical ? trigger.left : trigger.top;
208
+ const edge = Math.min(size / 2, FLOATING_ARROW_EDGE_OFFSET + 4);
209
+ const target = anchorStart + (pointAtCenter || !start && !end ? size / 2 : start ? edge : size - edge);
210
+ if (vertical) result.arrowX = clampFloating(target - left - 4, 8, overlay.width - 16);
211
+ else result.arrowY = clampFloating(target - top - 4, 8, overlay.height - 16);
212
+ return result;
213
+ }
214
+ function isFloatingAnchorHidden(anchor, rect) {
215
+ let left = 0, top = 0, right = document.documentElement.clientWidth, bottom = document.documentElement.clientHeight;
216
+ for (let parent = anchor.parentElement; parent; parent = parent.parentElement) {
217
+ const css = getComputedStyle(parent);
218
+ const bounds = parent.getBoundingClientRect();
219
+ if (/(auto|scroll|hidden|clip)/.test(css.overflowX)) {
220
+ left = Math.max(left, bounds.left);
221
+ right = Math.min(right, bounds.right);
222
+ }
223
+ if (/(auto|scroll|hidden|clip)/.test(css.overflowY)) {
224
+ top = Math.max(top, bounds.top);
225
+ bottom = Math.min(bottom, bounds.bottom);
123
226
  }
124
- parent = parent.parentElement;
125
227
  }
126
- return {
127
- top,
128
- right,
129
- bottom,
130
- left,
131
- width: Math.max(0, right - left),
132
- height: Math.max(0, bottom - top)
133
- };
228
+ return rect.right <= left || rect.left >= right || rect.bottom <= top || rect.top >= bottom;
134
229
  }
135
- function createTrack(axis) {
136
- const track = document.createElement("div");
137
- const thumb = document.createElement("div");
138
- track.className = `sia-scrollbar-track sia-floating-scrollbar sia-floating-scrollbar--${axis}`;
139
- thumb.className = "sia-floating-scrollbar__thumb";
140
- track.dataset.axis = axis;
141
- track.setAttribute("aria-hidden", "true");
142
- track.append(thumb);
143
- return { track, thumb };
144
- }
145
- function FloatingScrollbarProvider({ disabled = false, targetRef }) {
146
- useScrollbarProximity();
230
+ function Floating({
231
+ children,
232
+ content,
233
+ placement = "top",
234
+ trigger = "hover",
235
+ open,
236
+ defaultOpen = false,
237
+ mouseEnterDelay = 0.1,
238
+ mouseLeaveDelay = 0.1,
239
+ arrow = true,
240
+ autoAdjustOverflow = true,
241
+ color,
242
+ className = "",
243
+ overlayClassName = "",
244
+ role = "tooltip",
245
+ onOpenChange
246
+ }) {
247
+ const rootRef = useRef2(null);
248
+ const triggerElementRef = useRef2(null);
249
+ const overlayRef = useRef2(null);
250
+ const enterTimer = useRef2();
251
+ const leaveTimer = useRef2();
252
+ const contentId = useId2();
253
+ const triggers = Array.isArray(trigger) ? trigger : [trigger];
254
+ const [visible, setVisible] = useControllableState({ value: open, defaultValue: defaultOpen, onChange: onOpenChange });
255
+ const [position, setPosition] = useState();
256
+ const setVisibleRef = useRef2(setVisible);
257
+ setVisibleRef.current = setVisible;
258
+ const closeSelfRef = useRef2();
259
+ if (!closeSelfRef.current) closeSelfRef.current = () => setVisibleRef.current(false);
260
+ const exclusiveTooltip = role === "tooltip" && triggers.includes("hover");
261
+ function openNow() {
262
+ if (exclusiveTooltip && activeTooltipClose !== closeSelfRef.current) {
263
+ activeTooltipClose?.();
264
+ activeTooltipClose = closeSelfRef.current;
265
+ }
266
+ setVisible(true);
267
+ }
268
+ function closeNow() {
269
+ if (activeTooltipClose === closeSelfRef.current) activeTooltipClose = void 0;
270
+ setVisible(false);
271
+ }
272
+ function clearTimers() {
273
+ window.clearTimeout(enterTimer.current);
274
+ window.clearTimeout(leaveTimer.current);
275
+ }
276
+ function show(delay = 0) {
277
+ clearTimers();
278
+ if (delay <= 0) openNow();
279
+ else enterTimer.current = window.setTimeout(openNow, delay * 1e3);
280
+ }
281
+ function hide(delay = 0) {
282
+ clearTimers();
283
+ if (delay <= 0) closeNow();
284
+ else leaveTimer.current = window.setTimeout(closeNow, delay * 1e3);
285
+ }
147
286
  useEffect2(() => {
148
- if (disabled) return;
149
- const layer = document.createElement("div");
150
- layer.className = "sia-floating-scrollbar-layer";
151
- layer.setAttribute("aria-hidden", "true");
152
- document.body.append(layer);
153
- const entries = /* @__PURE__ */ new Map();
154
- let scanFrame = 0;
155
- let updateFrame = 0;
156
- const setEntryVisible = (entry, visible) => {
157
- entry.horizontalTrack.classList.toggle("sia-floating-scrollbar--visible", visible);
158
- entry.verticalTrack.classList.toggle("sia-floating-scrollbar--visible", visible);
159
- };
160
- const showEntry = (entry) => {
161
- if (entry.hideTimer !== null) window.clearTimeout(entry.hideTimer);
162
- entry.hideTimer = null;
163
- setEntryVisible(entry, true);
287
+ if (!visible || !triggers.includes("click")) return;
288
+ const close = (event) => {
289
+ const target = event.target;
290
+ if (!rootRef.current?.contains(target) && !overlayRef.current?.contains(target)) closeNow();
164
291
  };
165
- const scheduleHide = (entry, delay = 500) => {
166
- if (entry.hideTimer !== null) window.clearTimeout(entry.hideTimer);
167
- entry.hideTimer = window.setTimeout(() => {
168
- entry.hideTimer = null;
169
- if (!entry.hoveringTarget && !entry.hoveringTrack && !entry.dragging) setEntryVisible(entry, false);
170
- }, delay);
292
+ document.addEventListener("pointerdown", close);
293
+ return () => document.removeEventListener("pointerdown", close);
294
+ }, [triggers.join("|"), visible]);
295
+ useLayoutEffect(() => {
296
+ if (!visible) {
297
+ setPosition(void 0);
298
+ return;
299
+ }
300
+ const getAnchor = () => {
301
+ const root = rootRef.current;
302
+ const target = triggerElementRef.current;
303
+ return target && root?.contains(target) ? target : root?.firstElementChild ?? root;
171
304
  };
172
- const updateEntry = (entry) => {
173
- const { target, horizontalTrack, horizontalThumb, verticalTrack, verticalThumb } = entry;
174
- const hasHorizontal = canScroll(target, "horizontal");
175
- const hasVertical = canScroll(target, "vertical");
176
- const rect = visibleRect(target);
177
- const thickness = 10;
178
- horizontalTrack.hidden = !hasHorizontal || rect.width <= thickness || rect.height <= 0;
179
- verticalTrack.hidden = !hasVertical || rect.height <= thickness || rect.width <= 0;
180
- if (!horizontalTrack.hidden) {
181
- const trackWidth = Math.max(0, rect.width - (hasVertical ? thickness : 0));
182
- const thumbWidth = Math.max(24, Math.min(trackWidth, trackWidth * target.clientWidth / target.scrollWidth));
183
- const travel = Math.max(0, trackWidth - thumbWidth);
184
- const maxScroll = Math.max(1, target.scrollWidth - target.clientWidth);
185
- horizontalTrack.style.left = `${rect.left}px`;
186
- horizontalTrack.style.top = `${rect.bottom - thickness}px`;
187
- horizontalTrack.style.width = `${trackWidth}px`;
188
- horizontalTrack.style.height = `${thickness}px`;
189
- horizontalThumb.style.width = `${thumbWidth}px`;
190
- horizontalThumb.style.transform = `translate3d(${travel * target.scrollLeft / maxScroll}px, 0, 0)`;
191
- }
192
- if (!verticalTrack.hidden) {
193
- const trackHeight = Math.max(0, rect.height - (hasHorizontal ? thickness : 0));
194
- const thumbHeight = Math.max(24, Math.min(trackHeight, trackHeight * target.clientHeight / target.scrollHeight));
195
- const travel = Math.max(0, trackHeight - thumbHeight);
196
- const maxScroll = Math.max(1, target.scrollHeight - target.clientHeight);
197
- verticalTrack.style.left = `${rect.right - thickness}px`;
198
- verticalTrack.style.top = `${rect.top}px`;
199
- verticalTrack.style.width = `${thickness}px`;
200
- verticalTrack.style.height = `${trackHeight}px`;
201
- verticalThumb.style.height = `${thumbHeight}px`;
202
- verticalThumb.style.transform = `translate3d(0, ${travel * target.scrollTop / maxScroll}px, 0)`;
305
+ const update = () => {
306
+ if (!rootRef.current || !overlayRef.current) return;
307
+ const anchor2 = getAnchor();
308
+ if (anchor2) {
309
+ const rect = anchor2.getBoundingClientRect();
310
+ const next = resolveFloatingPosition(rect, overlayRef.current.getBoundingClientRect(), placement, typeof arrow === "object" && !!arrow.pointAtCenter, autoAdjustOverflow);
311
+ next.hidden = isFloatingAnchorHidden(anchor2, rect);
312
+ setPosition(next);
203
313
  }
204
314
  };
205
- const scheduleUpdateAll = () => {
206
- if (updateFrame) return;
207
- updateFrame = window.requestAnimationFrame(() => {
208
- updateFrame = 0;
209
- entries.forEach(updateEntry);
210
- });
211
- };
212
- const bindTrackDrag = (entry, axis, track, thumb) => {
213
- const target = entry.target;
214
- const onTrackPointerEnter = () => {
215
- entry.hoveringTrack = true;
216
- showEntry(entry);
217
- };
218
- const onTrackPointerLeave = () => {
219
- entry.hoveringTrack = false;
220
- scheduleHide(entry);
221
- };
222
- const onTrackPointerDown = (event) => {
223
- if (event.target === thumb || event.button !== 0) return;
224
- event.preventDefault();
225
- event.stopPropagation();
226
- const trackRect = track.getBoundingClientRect();
227
- const thumbRect = thumb.getBoundingClientRect();
228
- const trackLength = axis === "vertical" ? trackRect.height : trackRect.width;
229
- const thumbLength = axis === "vertical" ? thumbRect.height : thumbRect.width;
230
- const pointer = axis === "vertical" ? event.clientY - trackRect.top : event.clientX - trackRect.left;
231
- const ratio = Math.max(0, Math.min(1, (pointer - thumbLength / 2) / Math.max(1, trackLength - thumbLength)));
232
- if (axis === "vertical") target.scrollTop = ratio * (target.scrollHeight - target.clientHeight);
233
- else target.scrollLeft = ratio * (target.scrollWidth - target.clientWidth);
234
- };
235
- const onThumbPointerDown = (event) => {
236
- if (event.button !== 0) return;
237
- event.preventDefault();
238
- event.stopPropagation();
239
- entry.dragging = true;
240
- showEntry(entry);
241
- thumb.setPointerCapture(event.pointerId);
242
- const startPointer = axis === "vertical" ? event.clientY : event.clientX;
243
- const startScroll = axis === "vertical" ? target.scrollTop : target.scrollLeft;
244
- const trackRect = track.getBoundingClientRect();
245
- const thumbRect = thumb.getBoundingClientRect();
246
- const trackLength = axis === "vertical" ? trackRect.height : trackRect.width;
247
- const thumbLength = axis === "vertical" ? thumbRect.height : thumbRect.width;
248
- const maxScroll = axis === "vertical" ? target.scrollHeight - target.clientHeight : target.scrollWidth - target.clientWidth;
249
- const travel = Math.max(1, trackLength - thumbLength);
250
- const onPointerMove = (moveEvent) => {
251
- if (moveEvent.pointerId !== event.pointerId) return;
252
- const pointer = axis === "vertical" ? moveEvent.clientY : moveEvent.clientX;
253
- const nextScroll = startScroll + (pointer - startPointer) * maxScroll / travel;
254
- if (axis === "vertical") target.scrollTop = nextScroll;
255
- else target.scrollLeft = nextScroll;
256
- };
257
- const onPointerUp = (upEvent) => {
258
- if (upEvent.pointerId !== event.pointerId) return;
259
- entry.dragging = false;
260
- thumb.releasePointerCapture(event.pointerId);
261
- window.removeEventListener("pointermove", onPointerMove);
262
- window.removeEventListener("pointerup", onPointerUp);
263
- window.removeEventListener("pointercancel", onPointerUp);
264
- scheduleHide(entry);
265
- };
266
- window.addEventListener("pointermove", onPointerMove);
267
- window.addEventListener("pointerup", onPointerUp);
268
- window.addEventListener("pointercancel", onPointerUp);
269
- };
270
- track.addEventListener("pointerenter", onTrackPointerEnter);
271
- track.addEventListener("pointerleave", onTrackPointerLeave);
272
- track.addEventListener("pointerdown", onTrackPointerDown);
273
- thumb.addEventListener("pointerdown", onThumbPointerDown);
274
- return () => {
275
- track.removeEventListener("pointerenter", onTrackPointerEnter);
276
- track.removeEventListener("pointerleave", onTrackPointerLeave);
277
- track.removeEventListener("pointerdown", onTrackPointerDown);
278
- thumb.removeEventListener("pointerdown", onThumbPointerDown);
279
- };
315
+ update();
316
+ window.addEventListener("resize", update);
317
+ window.addEventListener("scroll", update, true);
318
+ const observer = new ResizeObserver(update);
319
+ if (rootRef.current) observer.observe(rootRef.current);
320
+ const anchor = getAnchor();
321
+ if (anchor) observer.observe(anchor);
322
+ if (overlayRef.current) observer.observe(overlayRef.current);
323
+ return () => {
324
+ window.removeEventListener("resize", update);
325
+ window.removeEventListener("scroll", update, true);
326
+ observer.disconnect();
280
327
  };
281
- const resizeObserver = new ResizeObserver((observedEntries) => {
282
- observedEntries.forEach(({ target }) => {
283
- const entry = entries.get(target);
284
- if (entry) updateEntry(entry);
285
- });
286
- });
287
- const addTarget = (target) => {
288
- if (entries.has(target)) return;
289
- const horizontal = createTrack("horizontal");
290
- const vertical = createTrack("vertical");
291
- layer.append(horizontal.track, vertical.track);
292
- const onPointerEnter = () => {
293
- const entry2 = entries.get(target);
294
- if (!entry2) return;
295
- entry2.hoveringTarget = true;
296
- showEntry(entry2);
297
- };
298
- const onPointerLeave = () => {
299
- const entry2 = entries.get(target);
300
- if (!entry2) return;
301
- entry2.hoveringTarget = false;
302
- scheduleHide(entry2);
303
- };
304
- const onScroll = () => {
305
- const entry2 = entries.get(target);
306
- if (!entry2) return;
307
- updateEntry(entry2);
308
- showEntry(entry2);
309
- scheduleHide(entry2, 700);
310
- };
311
- target.addEventListener("pointerenter", onPointerEnter);
312
- target.addEventListener("pointerleave", onPointerLeave);
313
- target.addEventListener("scroll", onScroll, { passive: true });
314
- const entry = {
315
- target,
316
- horizontalTrack: horizontal.track,
317
- horizontalThumb: horizontal.thumb,
318
- verticalTrack: vertical.track,
319
- verticalThumb: vertical.thumb,
320
- hideTimer: null,
321
- hoveringTarget: target.matches(":hover"),
322
- hoveringTrack: false,
323
- dragging: false,
324
- cleanup: () => void 0
325
- };
326
- const cleanupHorizontal = bindTrackDrag(entry, "horizontal", horizontal.track, horizontal.thumb);
327
- const cleanupVertical = bindTrackDrag(entry, "vertical", vertical.track, vertical.thumb);
328
- entry.cleanup = () => {
329
- if (entry.hideTimer !== null) window.clearTimeout(entry.hideTimer);
330
- cleanupHorizontal();
331
- cleanupVertical();
332
- target.removeEventListener("pointerenter", onPointerEnter);
333
- target.removeEventListener("pointerleave", onPointerLeave);
334
- target.removeEventListener("scroll", onScroll);
335
- resizeObserver.unobserve(target);
336
- target.removeAttribute("data-sia-floating-scrollbar");
337
- horizontal.track.remove();
338
- vertical.track.remove();
339
- };
340
- entries.set(target, entry);
341
- target.setAttribute("data-sia-floating-scrollbar", "");
342
- resizeObserver.observe(target);
343
- updateEntry(entry);
344
- if (entry.hoveringTarget) showEntry(entry);
345
- };
346
- const removeTarget = (target) => {
347
- const entry = entries.get(target);
348
- if (!entry) return;
349
- entry.cleanup();
350
- entries.delete(target);
351
- };
352
- const scanTargets = () => {
353
- scanFrame = 0;
354
- const candidates = (targetRef ? [targetRef.current] : [document.scrollingElement, ...document.querySelectorAll("*")]).filter((target) => target instanceof HTMLElement);
355
- const nextTargets = /* @__PURE__ */ new Set();
356
- candidates.forEach((target) => {
357
- if (canScroll(target, "horizontal") || canScroll(target, "vertical")) {
358
- nextTargets.add(target);
359
- if (entries.has(target) || !target.hasAttribute("data-sia-floating-scrollbar")) addTarget(target);
360
- }
361
- });
362
- entries.forEach((_entry, target) => {
363
- if (!target.isConnected || !nextTargets.has(target)) removeTarget(target);
364
- else updateEntry(_entry);
365
- });
366
- };
367
- const scheduleScan = () => {
368
- if (scanFrame) return;
369
- scanFrame = window.requestAnimationFrame(scanTargets);
370
- };
371
- const mutationObserver = new MutationObserver((mutations) => {
372
- if (mutations.some((mutation) => !layer.contains(mutation.target))) scheduleScan();
373
- });
374
- mutationObserver.observe(document.documentElement, { subtree: true, childList: true, attributes: true });
375
- window.addEventListener("resize", scheduleScan);
376
- window.addEventListener("scroll", scheduleUpdateAll, true);
377
- scheduleScan();
378
- return () => {
379
- mutationObserver.disconnect();
380
- resizeObserver.disconnect();
381
- window.removeEventListener("resize", scheduleScan);
382
- window.removeEventListener("scroll", scheduleUpdateAll, true);
383
- if (scanFrame) window.cancelAnimationFrame(scanFrame);
384
- if (updateFrame) window.cancelAnimationFrame(updateFrame);
385
- entries.forEach((entry) => entry.cleanup());
386
- entries.clear();
387
- layer.remove();
388
- };
389
- }, [disabled, targetRef]);
390
- return null;
391
- }
392
-
393
- // src/components/Checkbox.tsx
394
- import { createContext, forwardRef, useContext, useEffect as useEffect3, useId, useMemo, useRef } from "react";
395
- import { jsx, jsxs } from "react/jsx-runtime";
396
- var CheckboxGroupContext = createContext(null);
397
- var CheckboxRoot = forwardRef(function Checkbox({ value, size: sizeProp, indeterminate = false, children, disabled, checked, defaultChecked, className = "", onChange, ...props }, forwardedRef) {
398
- const size = useControlSize(sizeProp);
399
- const group = useContext(CheckboxGroupContext);
400
- const localRef = useRef(null);
401
- const fallbackId = useId();
402
- const grouped = group !== null && value !== void 0;
403
- const currentChecked = grouped ? group.values.includes(value) : checked;
404
- useEffect3(() => {
405
- if (localRef.current) localRef.current.indeterminate = indeterminate;
406
- }, [indeterminate]);
407
- function setRef(node) {
408
- localRef.current = node;
409
- if (typeof forwardedRef === "function") forwardedRef(node);
410
- else if (forwardedRef) forwardedRef.current = node;
411
- }
412
- return /* @__PURE__ */ jsxs("label", { className: `sia-checkbox sia-checkbox--${size}${disabled || group?.disabled ? " is-disabled" : ""} ${className}`.trim(), children: [
413
- /* @__PURE__ */ jsx(
414
- "input",
328
+ }, [children, content, placement, visible, autoAdjustOverflow, typeof arrow === "object" && arrow.pointAtCenter]);
329
+ useEffect2(() => () => {
330
+ clearTimers();
331
+ if (activeTooltipClose === closeSelfRef.current) activeTooltipClose = void 0;
332
+ }, []);
333
+ const child = cloneElement(children, {
334
+ ...triggers.includes("hover") ? { onMouseEnter: (event) => {
335
+ triggerElementRef.current = event.currentTarget;
336
+ children.props.onMouseEnter?.(event);
337
+ show(mouseEnterDelay);
338
+ }, onMouseLeave: (event) => {
339
+ children.props.onMouseLeave?.(event);
340
+ hide(mouseLeaveDelay);
341
+ } } : {},
342
+ ...triggers.includes("focus") ? { onFocus: (event) => {
343
+ triggerElementRef.current = event.currentTarget;
344
+ children.props.onFocus?.(event);
345
+ show();
346
+ }, onBlur: (event) => {
347
+ children.props.onBlur?.(event);
348
+ hide();
349
+ } } : {},
350
+ ...triggers.includes("click") ? { onClick: (event) => {
351
+ triggerElementRef.current = event.currentTarget;
352
+ children.props.onClick?.(event);
353
+ if (visible) closeNow();
354
+ else openNow();
355
+ } } : {},
356
+ "aria-describedby": visible ? contentId : void 0
357
+ });
358
+ const overlay = visible && content !== null && content !== void 0 && typeof document !== "undefined" ? createPortal(
359
+ /* @__PURE__ */ jsxs2(
360
+ "span",
415
361
  {
416
- ...withTitleTooltip(props),
417
- ref: setRef,
418
- id: props.id ?? fallbackId,
419
- className: "sia-checkbox__input",
420
- type: "checkbox",
421
- name: group?.name ?? props.name,
422
- disabled: disabled || group?.disabled,
423
- checked: currentChecked,
424
- defaultChecked: grouped ? void 0 : defaultChecked,
425
- "aria-checked": indeterminate ? "mixed" : currentChecked,
426
- onChange: (event) => {
427
- if (grouped) group.toggle(value, event.currentTarget.checked);
428
- onChange?.(event.currentTarget.checked, event);
429
- }
362
+ ref: overlayRef,
363
+ id: contentId,
364
+ role,
365
+ "data-custom-color": color ? "" : void 0,
366
+ className: `sia-floating__overlay sia-floating__overlay--${position?.placement ?? normalizeFloatingPlacement(placement)} ${overlayClassName}`.trim(),
367
+ style: {
368
+ "--sia-floating-color": color,
369
+ "--sia-floating-arrow-x": position?.arrowX == null ? void 0 : `${position.arrowX}px`,
370
+ "--sia-floating-arrow-y": position?.arrowY == null ? void 0 : `${position.arrowY}px`,
371
+ top: position?.top ?? 0,
372
+ left: position?.left ?? 0,
373
+ visibility: position && !position.hidden ? "visible" : "hidden"
374
+ },
375
+ onMouseEnter: triggers.includes("hover") ? () => show() : void 0,
376
+ onMouseLeave: triggers.includes("hover") ? () => hide(mouseLeaveDelay) : void 0,
377
+ children: [
378
+ content,
379
+ arrow ? /* @__PURE__ */ jsx2("span", { className: "sia-floating__arrow" }) : null
380
+ ]
430
381
  }
431
382
  ),
432
- /* @__PURE__ */ jsx("span", { className: "sia-checkbox__control", "aria-hidden": "true", children: /* @__PURE__ */ jsx("span", {}) }),
433
- children !== void 0 ? /* @__PURE__ */ jsx("span", { className: "sia-checkbox__label", children }) : null
383
+ document.body
384
+ ) : null;
385
+ return /* @__PURE__ */ jsxs2(Fragment, { children: [
386
+ /* @__PURE__ */ jsx2("span", { ref: rootRef, className: `sia-floating ${className}`.trim(), children: child }),
387
+ overlay
434
388
  ] });
435
- });
436
- function CheckboxGroup({
437
- size: sizeProp,
438
- columns,
439
- direction = "horizontal",
440
- gap,
441
- value,
442
- defaultValue = [],
443
- options,
444
- disabled,
445
- name,
446
- className = "",
447
- children,
448
- onChange
449
- }) {
450
- const size = useControlSize(sizeProp);
451
- const gridColumns = columns !== void 0 && Number.isInteger(columns) && columns > 0 ? columns : void 0;
452
- const layoutStyle = {
453
- gridTemplateColumns: gridColumns ? `repeat(${gridColumns}, minmax(0, 1fr))` : void 0,
454
- 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`
455
- };
456
- const [values, setValues] = useControllableState({ value, defaultValue, onChange });
457
- const context = useMemo(() => ({
458
- values,
459
- disabled,
460
- name,
461
- toggle(optionValue, checked) {
462
- const next = checked ? values.includes(optionValue) ? values : [...values, optionValue] : values.filter((item) => item !== optionValue);
463
- setValues(next);
464
- }
465
- }), [disabled, name, setValues, values]);
466
- return /* @__PURE__ */ jsx(ControlSizeContext.Provider, { value: size, children: /* @__PURE__ */ jsx(CheckboxGroupContext.Provider, { value: context, children: /* @__PURE__ */ jsxs("div", { className: `sia-checkbox-group${gridColumns ? " sia-checkbox-group--grid" : direction === "vertical" ? " sia-checkbox-group--vertical" : ""} ${className}`.trim(), style: layoutStyle, role: "group", children: [
467
- options?.map((option) => {
468
- const normalized = typeof option === "object" ? option : { label: String(option), value: option };
469
- return /* @__PURE__ */ jsx(CheckboxRoot, { value: normalized.value, disabled: normalized.disabled, children: normalized.label }, normalized.value);
470
- }),
471
- children
472
- ] }) }) });
473
389
  }
474
- var Checkbox2 = Object.assign(CheckboxRoot, {
475
- /** 组合多个子项并统一管理布局或选择状态。 */
476
- Group: CheckboxGroup
477
- });
478
-
479
- // src/components/treeCheckState.ts
480
- function affectedKeys(node) {
481
- if (node.disabled || node.disableCheckbox) return [];
482
- return [...node.checkable === false ? [] : [node.key], ...node.children?.flatMap(affectedKeys) ?? []];
390
+ function Tooltip(props) {
391
+ return /* @__PURE__ */ jsx2(Floating, { ...props, content: props.title });
483
392
  }
484
- function getTreeCheckState(nodes, keys, strictly = false) {
485
- const checked = new Set(keys);
486
- const halfChecked = /* @__PURE__ */ new Set();
487
- if (strictly) return { checked, halfChecked };
488
- const explicit = new Set(keys);
489
- function expand(items) {
490
- for (const node of items) {
491
- if (explicit.has(node.key)) affectedKeys(node).forEach((key) => checked.add(key));
492
- if (node.children) expand(node.children);
493
- }
494
- }
495
- expand(nodes);
496
- function fold(node) {
497
- const children = node.children?.map(fold).filter((value) => value !== void 0) ?? [];
498
- if (node.disabled || node.disableCheckbox) return void 0;
499
- if (!children.length) return { checked: checked.has(node.key), partial: false };
500
- const all = children.every((child) => child.checked);
501
- const some = children.some((child) => child.checked || child.partial);
502
- if (node.checkable !== false) {
503
- if (all) checked.add(node.key);
504
- else checked.delete(node.key);
505
- if (!all && some) halfChecked.add(node.key);
506
- }
507
- return { checked: all, partial: !all && some };
508
- }
509
- nodes.forEach(fold);
510
- return { checked, halfChecked };
511
- }
512
- function toggleTreeCheck(nodes, keys, node, strictly = false) {
513
- const state = getTreeCheckState(nodes, keys, strictly);
514
- const next = new Set(state.checked);
515
- const checked = !next.has(node.key);
516
- if (node.disabled || node.disableCheckbox || node.checkable === false) return { ...state, checkedValue: !checked };
517
- (strictly ? [node.key] : affectedKeys(node)).forEach((key) => checked ? next.add(key) : next.delete(key));
518
- if (!strictly && !checked) {
519
- let removeParents2 = function(items) {
520
- let found = false;
521
- for (const item of items) {
522
- const childFound = item.children ? removeParents2(item.children) : false;
523
- if (childFound) next.delete(item.key);
524
- if (item.key === node.key || childFound) found = true;
525
- }
526
- return found;
527
- };
528
- var removeParents = removeParents2;
529
- removeParents2(nodes);
530
- }
531
- return { ...getTreeCheckState(nodes, [...next], strictly), checkedValue: checked };
532
- }
533
-
534
- // src/components/FloatingDisplay.tsx
535
- import { cloneElement, useEffect as useEffect4, useId as useId2, useLayoutEffect, useMemo as useMemo2, useRef as useRef2, useState } from "react";
536
- import { createPortal } from "react-dom";
537
- import { Fragment, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
538
- var FLOATING_GAP = 9;
539
- var FLOATING_VIEWPORT_PADDING = 8;
540
- var FLOATING_ARROW_EDGE_OFFSET = 16;
541
- var activeTooltipClose;
542
- function clampFloating(value, min, max) {
543
- return Math.min(Math.max(value, min), Math.max(min, max));
544
- }
545
- function resolveFloatingPosition(trigger, overlay, placement) {
546
- const viewportWidth = document.documentElement.clientWidth;
547
- const viewportHeight = document.documentElement.clientHeight;
548
- let resolvedPlacement = placement;
549
- if (placement.startsWith("top") && trigger.top - overlay.height - FLOATING_GAP < FLOATING_VIEWPORT_PADDING && trigger.bottom + overlay.height + FLOATING_GAP <= viewportHeight - FLOATING_VIEWPORT_PADDING) {
550
- resolvedPlacement = placement.replace("top", "bottom");
551
- } else if (placement.startsWith("bottom") && trigger.bottom + overlay.height + FLOATING_GAP > viewportHeight - FLOATING_VIEWPORT_PADDING && trigger.top - overlay.height - FLOATING_GAP >= FLOATING_VIEWPORT_PADDING) {
552
- resolvedPlacement = placement.replace("bottom", "top");
553
- } else if (placement === "left" && trigger.left - overlay.width - FLOATING_GAP < FLOATING_VIEWPORT_PADDING && trigger.right + overlay.width + FLOATING_GAP <= viewportWidth - FLOATING_VIEWPORT_PADDING) {
554
- resolvedPlacement = "right";
555
- } else if (placement === "right" && trigger.right + overlay.width + FLOATING_GAP > viewportWidth - FLOATING_VIEWPORT_PADDING && trigger.left - overlay.width - FLOATING_GAP >= FLOATING_VIEWPORT_PADDING) {
556
- resolvedPlacement = "left";
557
- }
558
- let top = trigger.top;
559
- let left = trigger.left;
560
- if (resolvedPlacement.startsWith("top") || resolvedPlacement.startsWith("bottom")) {
561
- top = resolvedPlacement.startsWith("top") ? trigger.top - overlay.height - FLOATING_GAP : trigger.bottom + FLOATING_GAP;
562
- left = resolvedPlacement.endsWith("-start") ? trigger.left : resolvedPlacement.endsWith("-end") ? trigger.right - overlay.width : trigger.left + (trigger.width - overlay.width) / 2;
563
- } else {
564
- top = trigger.top + (trigger.height - overlay.height) / 2;
565
- left = resolvedPlacement === "left" ? trigger.left - overlay.width - FLOATING_GAP : trigger.right + FLOATING_GAP;
566
- }
567
- top = clampFloating(top, FLOATING_VIEWPORT_PADDING, viewportHeight - overlay.height - FLOATING_VIEWPORT_PADDING);
568
- left = clampFloating(left, FLOATING_VIEWPORT_PADDING, viewportWidth - overlay.width - FLOATING_VIEWPORT_PADDING);
569
- const result = { top, left, placement: resolvedPlacement };
570
- if (resolvedPlacement.startsWith("top") || resolvedPlacement.startsWith("bottom")) {
571
- 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);
572
- } else {
573
- result.arrowY = clampFloating(trigger.top + trigger.height / 2 - top - 4, 8, overlay.height - 16);
574
- }
575
- return result;
576
- }
577
- function Floating({
578
- children,
579
- content,
580
- placement = "top",
581
- trigger = "hover",
582
- open,
583
- defaultOpen = false,
584
- mouseEnterDelay = 0.1,
585
- mouseLeaveDelay = 0.1,
586
- arrow = true,
587
- color,
588
- className = "",
589
- overlayClassName = "",
590
- role = "tooltip",
591
- onOpenChange
592
- }) {
593
- const rootRef = useRef2(null);
594
- const overlayRef = useRef2(null);
595
- const enterTimer = useRef2();
596
- const leaveTimer = useRef2();
597
- const contentId = useId2();
598
- const triggers = Array.isArray(trigger) ? trigger : [trigger];
599
- const [visible, setVisible] = useControllableState({ value: open, defaultValue: defaultOpen, onChange: onOpenChange });
600
- const [position, setPosition] = useState();
601
- const setVisibleRef = useRef2(setVisible);
602
- setVisibleRef.current = setVisible;
603
- const closeSelfRef = useRef2();
604
- if (!closeSelfRef.current) closeSelfRef.current = () => setVisibleRef.current(false);
605
- const exclusiveTooltip = role === "tooltip" && triggers.includes("hover");
606
- function openNow() {
607
- if (exclusiveTooltip && activeTooltipClose !== closeSelfRef.current) {
608
- activeTooltipClose?.();
609
- activeTooltipClose = closeSelfRef.current;
610
- }
611
- setVisible(true);
612
- }
613
- function closeNow() {
614
- if (activeTooltipClose === closeSelfRef.current) activeTooltipClose = void 0;
615
- setVisible(false);
616
- }
617
- function clearTimers() {
618
- window.clearTimeout(enterTimer.current);
619
- window.clearTimeout(leaveTimer.current);
620
- }
621
- function show(delay = 0) {
622
- clearTimers();
623
- if (delay <= 0) openNow();
624
- else enterTimer.current = window.setTimeout(openNow, delay * 1e3);
625
- }
626
- function hide(delay = 0) {
627
- clearTimers();
628
- if (delay <= 0) closeNow();
629
- else leaveTimer.current = window.setTimeout(closeNow, delay * 1e3);
630
- }
631
- useEffect4(() => {
632
- if (!visible || !triggers.includes("click")) return;
633
- const close = (event) => {
634
- const target = event.target;
635
- if (!rootRef.current?.contains(target) && !overlayRef.current?.contains(target)) closeNow();
636
- };
637
- document.addEventListener("pointerdown", close);
638
- return () => document.removeEventListener("pointerdown", close);
639
- }, [triggers.join("|"), visible]);
640
- useLayoutEffect(() => {
641
- if (!visible) {
642
- setPosition(void 0);
643
- return;
644
- }
645
- const update = () => {
646
- if (!rootRef.current || !overlayRef.current) return;
647
- setPosition(resolveFloatingPosition(rootRef.current.getBoundingClientRect(), overlayRef.current.getBoundingClientRect(), placement));
648
- };
649
- update();
650
- window.addEventListener("resize", update);
651
- window.addEventListener("scroll", update, true);
652
- const observer = new ResizeObserver(update);
653
- if (rootRef.current) observer.observe(rootRef.current);
654
- if (overlayRef.current) observer.observe(overlayRef.current);
655
- return () => {
656
- window.removeEventListener("resize", update);
657
- window.removeEventListener("scroll", update, true);
658
- observer.disconnect();
659
- };
660
- }, [children, content, placement, visible]);
661
- useEffect4(() => () => {
662
- clearTimers();
663
- if (activeTooltipClose === closeSelfRef.current) activeTooltipClose = void 0;
664
- }, []);
665
- const child = cloneElement(children, {
666
- ...triggers.includes("hover") ? { onMouseEnter: (event) => {
667
- children.props.onMouseEnter?.(event);
668
- show(mouseEnterDelay);
669
- }, onMouseLeave: (event) => {
670
- children.props.onMouseLeave?.(event);
671
- hide(mouseLeaveDelay);
672
- } } : {},
673
- ...triggers.includes("focus") ? { onFocus: (event) => {
674
- children.props.onFocus?.(event);
675
- show();
676
- }, onBlur: (event) => {
677
- children.props.onBlur?.(event);
678
- hide();
679
- } } : {},
680
- ...triggers.includes("click") ? { onClick: (event) => {
681
- children.props.onClick?.(event);
682
- if (visible) closeNow();
683
- else openNow();
684
- } } : {},
685
- "aria-describedby": visible ? contentId : void 0
686
- });
687
- const overlay = visible && content !== null && content !== void 0 && typeof document !== "undefined" ? createPortal(
688
- /* @__PURE__ */ jsxs2(
689
- "span",
690
- {
691
- ref: overlayRef,
692
- id: contentId,
693
- role,
694
- "data-custom-color": color ? "" : void 0,
695
- className: `sia-floating__overlay sia-floating__overlay--${position?.placement ?? placement} ${overlayClassName}`.trim(),
696
- style: {
697
- "--sia-floating-color": color,
698
- "--sia-floating-arrow-x": position?.arrowX == null ? void 0 : `${position.arrowX}px`,
699
- "--sia-floating-arrow-y": position?.arrowY == null ? void 0 : `${position.arrowY}px`,
700
- top: position?.top ?? 0,
701
- left: position?.left ?? 0,
702
- visibility: position ? "visible" : "hidden"
703
- },
704
- onMouseEnter: triggers.includes("hover") ? () => show() : void 0,
705
- onMouseLeave: triggers.includes("hover") ? () => hide(mouseLeaveDelay) : void 0,
706
- children: [
707
- content,
708
- arrow ? /* @__PURE__ */ jsx2("span", { className: "sia-floating__arrow" }) : null
709
- ]
710
- }
711
- ),
712
- document.body
713
- ) : null;
714
- return /* @__PURE__ */ jsxs2(Fragment, { children: [
715
- /* @__PURE__ */ jsx2("span", { ref: rootRef, className: `sia-floating ${className}`.trim(), children: child }),
716
- overlay
717
- ] });
718
- }
719
- function Tooltip(props) {
720
- return /* @__PURE__ */ jsx2(Floating, { ...props, content: props.title });
721
- }
722
- function Popover({ title, content, trigger = "click", overlayClassName = "", ...props }) {
723
- return /* @__PURE__ */ jsx2(Floating, { ...props, trigger, role: "dialog", overlayClassName: `sia-popover ${overlayClassName}`.trim(), content: /* @__PURE__ */ jsxs2(Fragment, { children: [
724
- title ? /* @__PURE__ */ jsx2("strong", { className: "sia-popover__title", children: title }) : null,
725
- /* @__PURE__ */ jsx2("span", { className: "sia-popover__content", children: content })
726
- ] }) });
393
+ function Popover({ title, content, trigger = "click", overlayClassName = "", ...props }) {
394
+ return /* @__PURE__ */ jsx2(Floating, { ...props, trigger, role: "dialog", overlayClassName: `sia-popover ${overlayClassName}`.trim(), content: /* @__PURE__ */ jsxs2(Fragment, { children: [
395
+ title ? /* @__PURE__ */ jsx2("strong", { className: "sia-popover__title", children: title }) : null,
396
+ /* @__PURE__ */ jsx2("span", { className: "sia-popover__content", children: content })
397
+ ] }) });
727
398
  }
728
399
  function Timeline({ items = [], mode = "left", pending = false, pendingDot, reverse = false, className = "", ...props }) {
729
400
  const values = [...items, ...pending ? [{ children: pending === true ? "\u52A0\u8F7D\u4E2D..." : pending, dot: pendingDot ?? /* @__PURE__ */ jsx2(Icon, { name: "loader", className: "sia-spin-icon" }) }] : []];
@@ -978,11 +649,11 @@ function Tree({
978
649
  }
979
650
 
980
651
  // src/components/Modal.tsx
981
- import { forwardRef as forwardRef2, useEffect as useEffect6, useId as useId3, useRef as useRef4, useState as useState2 } from "react";
652
+ import { forwardRef as forwardRef2, useEffect as useEffect4, useId as useId3, useRef as useRef4, useState as useState2 } from "react";
982
653
  import { createRoot } from "react-dom/client";
983
654
 
984
655
  // src/components/Overlay.tsx
985
- import { useEffect as useEffect5, useRef as useRef3 } from "react";
656
+ import { useEffect as useEffect3, useRef as useRef3 } from "react";
986
657
  import { createPortal as createPortal2 } from "react-dom";
987
658
  var bodyLockCount = 0;
988
659
  var previousBodyOverflow = "";
@@ -1007,7 +678,7 @@ function useOverlayLifecycle(open, panelRef, onRequestClose, keyboard = true) {
1007
678
  const overlayIdRef = useRef3(/* @__PURE__ */ Symbol("sia-overlay"));
1008
679
  const closeRef = useRef3(onRequestClose);
1009
680
  closeRef.current = onRequestClose;
1010
- useEffect5(() => {
681
+ useEffect3(() => {
1011
682
  if (!open || typeof document === "undefined") return void 0;
1012
683
  const overlayId = overlayIdRef.current;
1013
684
  const previouslyFocused = document.activeElement instanceof HTMLElement ? document.activeElement : null;
@@ -1045,247 +716,622 @@ function useOverlayLifecycle(open, panelRef, onRequestClose, keyboard = true) {
1045
716
  }
1046
717
  document.addEventListener("keydown", handleKeyDown);
1047
718
  return () => {
1048
- window.clearTimeout(focusTimer);
1049
- document.removeEventListener("keydown", handleKeyDown);
1050
- const index = overlayStack.lastIndexOf(overlayId);
1051
- if (index >= 0) overlayStack.splice(index, 1);
1052
- unlockBody();
1053
- previouslyFocused?.focus({ preventScroll: true });
719
+ window.clearTimeout(focusTimer);
720
+ document.removeEventListener("keydown", handleKeyDown);
721
+ const index = overlayStack.lastIndexOf(overlayId);
722
+ if (index >= 0) overlayStack.splice(index, 1);
723
+ unlockBody();
724
+ previouslyFocused?.focus({ preventScroll: true });
725
+ };
726
+ }, [keyboard, open, panelRef]);
727
+ }
728
+ function OverlayPortal({ children, container }) {
729
+ if (typeof document === "undefined") return null;
730
+ const target = typeof container === "function" ? container() : container ?? document.body;
731
+ return createPortal2(children, target);
732
+ }
733
+
734
+ // src/components/Modal.tsx
735
+ import { Fragment as Fragment2, jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
736
+ var modalHandles = /* @__PURE__ */ new Set();
737
+ var MODAL_MOTION_DURATION = 220;
738
+ var typeIcons = {
739
+ info: /* @__PURE__ */ jsx3(Icon, { name: "info", size: 23 }),
740
+ success: /* @__PURE__ */ jsx3(Icon, { name: "circle-check", size: 23 }),
741
+ warning: /* @__PURE__ */ jsx3(Icon, { name: "warning", size: 23 }),
742
+ error: /* @__PURE__ */ jsx3(Icon, { name: "circle-close", size: 23 }),
743
+ confirm: /* @__PURE__ */ jsx3(Icon, { name: "question", size: 23 })
744
+ };
745
+ var ModalRoot = forwardRef2(function Modal({
746
+ open,
747
+ title,
748
+ children,
749
+ footer,
750
+ footerExtra,
751
+ type = "default",
752
+ okText = "\u786E\u5B9A",
753
+ cancelText = "\u53D6\u6D88",
754
+ showCancel = type === "default" || type === "confirm",
755
+ confirmLoading = false,
756
+ okButtonProps,
757
+ cancelButtonProps,
758
+ closable = true,
759
+ maskClosable = true,
760
+ keyboard = true,
761
+ centered = false,
762
+ width = 520,
763
+ height,
764
+ zIndex = 1e3,
765
+ getContainer,
766
+ onOk,
767
+ onCancel,
768
+ onOpenChange,
769
+ afterOpenChange,
770
+ className = "",
771
+ style,
772
+ ...props
773
+ }, forwardedRef) {
774
+ const localRef = useRef4(null);
775
+ const titleId = useId3();
776
+ const [submitting, setSubmitting] = useState2(false);
777
+ const [rendered, setRendered] = useState2(open);
778
+ const [closing, setClosing] = useState2(false);
779
+ const afterOpenChangeRef = useRef4(afterOpenChange);
780
+ afterOpenChangeRef.current = afterOpenChange;
781
+ const submitLock = useRef4(false);
782
+ useEffect4(() => {
783
+ if (!open || !rendered) return void 0;
784
+ const timer = window.setTimeout(() => afterOpenChangeRef.current?.(true), MODAL_MOTION_DURATION);
785
+ return () => window.clearTimeout(timer);
786
+ }, [open, rendered]);
787
+ useEffect4(() => {
788
+ if (open) {
789
+ setRendered(true);
790
+ setClosing(false);
791
+ return void 0;
792
+ }
793
+ if (!rendered) return void 0;
794
+ setClosing(true);
795
+ const timer = window.setTimeout(() => {
796
+ setRendered(false);
797
+ setClosing(false);
798
+ afterOpenChangeRef.current?.(false);
799
+ }, MODAL_MOTION_DURATION);
800
+ return () => window.clearTimeout(timer);
801
+ }, [open, rendered]);
802
+ function setRef(node) {
803
+ localRef.current = node;
804
+ if (typeof forwardedRef === "function") forwardedRef(node);
805
+ else if (forwardedRef) forwardedRef.current = node;
806
+ }
807
+ function requestClose() {
808
+ if (submitting || confirmLoading) return;
809
+ onCancel?.();
810
+ onOpenChange?.(false);
811
+ }
812
+ async function handleOk() {
813
+ if (submitLock.current || confirmLoading) return;
814
+ submitLock.current = true;
815
+ if (!onOk) {
816
+ onOpenChange?.(false);
817
+ submitLock.current = false;
818
+ return;
819
+ }
820
+ try {
821
+ const result = onOk();
822
+ if (result instanceof Promise) {
823
+ setSubmitting(true);
824
+ const resolved = await result;
825
+ if (resolved !== false) onOpenChange?.(false);
826
+ } else if (result !== false) {
827
+ onOpenChange?.(false);
828
+ }
829
+ } catch {
830
+ return;
831
+ } finally {
832
+ submitLock.current = false;
833
+ setSubmitting(false);
834
+ }
835
+ }
836
+ useOverlayLifecycle(rendered, localRef, requestClose, keyboard);
837
+ if (!rendered) return null;
838
+ const mergedStyle = {
839
+ ...height !== void 0 ? { display: "flex", flexDirection: "column", height, maxHeight: "calc(100dvh - 48px)" } : {},
840
+ ...style,
841
+ "--sia-modal-width": typeof width === "number" ? `${width}px` : width
842
+ };
843
+ const loading = submitting || confirmLoading;
844
+ const defaultFooter = /* @__PURE__ */ jsxs3(Fragment2, { children: [
845
+ showCancel ? /* @__PURE__ */ jsx3(Button, { ...cancelButtonProps, onClick: requestClose, disabled: loading || cancelButtonProps?.disabled, children: cancelText }) : null,
846
+ /* @__PURE__ */ jsx3(Button, { variant: "primary", ...okButtonProps, onClick: handleOk, loading, children: okText })
847
+ ] });
848
+ return /* @__PURE__ */ jsx3(OverlayPortal, { container: getContainer, children: /* @__PURE__ */ jsx3(
849
+ "div",
850
+ {
851
+ className: `sia-modal-root${centered ? " sia-modal-root--centered" : ""}`,
852
+ "data-state": closing ? "closing" : "open",
853
+ style: { zIndex },
854
+ onMouseDown: (event) => {
855
+ if (event.target === event.currentTarget && maskClosable) requestClose();
856
+ },
857
+ children: /* @__PURE__ */ jsxs3(
858
+ "div",
859
+ {
860
+ ref: setRef,
861
+ className: `sia-modal sia-modal--${type} ${className}`.trim(),
862
+ style: mergedStyle,
863
+ role: "dialog",
864
+ "aria-modal": "true",
865
+ "aria-labelledby": title ? titleId : void 0,
866
+ tabIndex: -1,
867
+ ...withTitleTooltip(props),
868
+ children: [
869
+ /* @__PURE__ */ jsxs3("div", { className: "sia-modal__header", style: height !== void 0 ? { flexShrink: 0 } : void 0, children: [
870
+ /* @__PURE__ */ jsxs3("div", { className: "sia-modal__heading", children: [
871
+ typeIcons[type] ? /* @__PURE__ */ jsx3("span", { className: "sia-modal__type-icon", "aria-hidden": "true", children: typeIcons[type] }) : null,
872
+ title ? /* @__PURE__ */ jsx3("strong", { id: titleId, children: title }) : null
873
+ ] }),
874
+ closable ? /* @__PURE__ */ jsx3("button", { type: "button", className: "sia-modal__close", "aria-label": "\u5173\u95ED\u5BF9\u8BDD\u6846", onClick: requestClose, children: /* @__PURE__ */ jsx3(Icon, { name: "close", size: 17 }) }) : null
875
+ ] }),
876
+ children !== void 0 ? /* @__PURE__ */ jsx3("div", { className: "sia-modal__body", style: height !== void 0 ? { flex: 1, minHeight: 0, overflow: "auto" } : void 0, children }) : null,
877
+ footer !== null ? /* @__PURE__ */ jsxs3("div", { className: "sia-modal__footer", style: height !== void 0 ? { flexShrink: 0 } : void 0, children: [
878
+ footer === void 0 && footerExtra != null ? /* @__PURE__ */ jsx3("div", { style: { marginRight: "auto" }, children: footerExtra }) : null,
879
+ footer ?? defaultFooter
880
+ ] }) : null
881
+ ]
882
+ }
883
+ )
884
+ }
885
+ ) });
886
+ });
887
+ function openModal(config) {
888
+ if (typeof document === "undefined") return { destroy() {
889
+ }, update() {
890
+ } };
891
+ const container = document.createElement("div");
892
+ container.className = "sia-modal-api-host";
893
+ document.body.appendChild(container);
894
+ const root = createRoot(container);
895
+ let currentConfig = config;
896
+ let currentOpen = true;
897
+ let destroyed = false;
898
+ let cleanupTimer;
899
+ function cleanup2() {
900
+ if (destroyed) return;
901
+ destroyed = true;
902
+ if (cleanupTimer !== void 0) window.clearTimeout(cleanupTimer);
903
+ root.unmount();
904
+ container.remove();
905
+ modalHandles.delete(handle);
906
+ }
907
+ function closeWithMotion() {
908
+ if (destroyed || !currentOpen) return;
909
+ currentOpen = false;
910
+ render();
911
+ cleanupTimer = window.setTimeout(cleanup2, MODAL_MOTION_DURATION);
912
+ }
913
+ function render() {
914
+ root.render(/* @__PURE__ */ jsx3(ModalRoot, { ...currentConfig, open: currentOpen, onOpenChange: (nextOpen) => {
915
+ currentConfig.onOpenChange?.(nextOpen);
916
+ if (!nextOpen) closeWithMotion();
917
+ } }));
918
+ }
919
+ const handle = {
920
+ destroy: closeWithMotion,
921
+ update(next) {
922
+ if (destroyed) return;
923
+ currentConfig = { ...currentConfig, ...next };
924
+ render();
925
+ }
926
+ };
927
+ modalHandles.add(handle);
928
+ render();
929
+ return handle;
930
+ }
931
+ function preset(type, defaults = {}) {
932
+ return (config) => openModal({
933
+ okText: type === "confirm" ? "\u786E\u5B9A" : "\u77E5\u9053\u4E86",
934
+ showCancel: type === "confirm",
935
+ closable: type !== "confirm",
936
+ maskClosable: type !== "confirm",
937
+ ...defaults,
938
+ ...config,
939
+ type
940
+ });
941
+ }
942
+ var Modal2 = Object.assign(ModalRoot, {
943
+ /** 直接打开实例,返回可用于更新或关闭的操作句柄。 */
944
+ open: openModal,
945
+ /** 显示普通信息提示。 */
946
+ info: preset("info", { title: "\u63D0\u793A" }),
947
+ /** 显示成功提示。 */
948
+ success: preset("success", { title: "\u64CD\u4F5C\u6210\u529F" }),
949
+ /** 显示警告提示。 */
950
+ warning: preset("warning", { title: "\u8BF7\u6CE8\u610F" }),
951
+ /** 显示错误提示。 */
952
+ error: preset("error", { title: "\u64CD\u4F5C\u5931\u8D25" }),
953
+ /** 打开确认弹框,支持异步确认;返回 false 可阻止关闭。 */
954
+ confirm: preset("confirm", { title: "\u786E\u8BA4\u64CD\u4F5C" }),
955
+ /** 关闭由此 API 打开的所有弹层。 */
956
+ destroyAll() {
957
+ [...modalHandles].forEach((handle) => handle.destroy());
958
+ }
959
+ });
960
+
961
+ // src/components/FloatingScrollbarProvider.tsx
962
+ import { useEffect as useEffect6 } from "react";
963
+
964
+ // src/components/useScrollbarProximity.ts
965
+ import { useEffect as useEffect5 } from "react";
966
+ var users = 0;
967
+ var cleanup;
968
+ function useScrollbarProximity() {
969
+ useEffect5(() => {
970
+ if (users++ === 0) {
971
+ let frame = 0;
972
+ let x = -Infinity;
973
+ let y = -Infinity;
974
+ const nearby = /* @__PURE__ */ new Set();
975
+ const clear = () => {
976
+ nearby.forEach((track) => track.removeAttribute("data-scrollbar-near"));
977
+ nearby.clear();
978
+ };
979
+ const update = () => {
980
+ frame = 0;
981
+ const next = /* @__PURE__ */ new Set();
982
+ document.querySelectorAll(".sia-scrollbar-track").forEach((track) => {
983
+ const rect = track.getBoundingClientRect();
984
+ const proximity = Number.parseFloat(getComputedStyle(track).getPropertyValue("--sia-scrollbar-proximity")) || 16;
985
+ if (rect.width && rect.height && x >= rect.left - proximity && x <= rect.right + proximity && y >= rect.top - proximity && y <= rect.bottom + proximity) {
986
+ next.add(track);
987
+ if (!nearby.has(track)) track.setAttribute("data-scrollbar-near", "true");
988
+ }
989
+ });
990
+ nearby.forEach((track) => {
991
+ if (!next.has(track)) track.removeAttribute("data-scrollbar-near");
992
+ });
993
+ nearby.clear();
994
+ next.forEach((track) => nearby.add(track));
995
+ };
996
+ const move = (event) => {
997
+ if (event.pointerType === "touch") return;
998
+ x = event.clientX;
999
+ y = event.clientY;
1000
+ if (!frame) frame = requestAnimationFrame(update);
1001
+ };
1002
+ const leave = () => {
1003
+ x = y = -Infinity;
1004
+ clear();
1005
+ };
1006
+ document.addEventListener("pointermove", move, { passive: true });
1007
+ document.documentElement.addEventListener("pointerleave", leave);
1008
+ window.addEventListener("blur", leave);
1009
+ cleanup = () => {
1010
+ if (frame) cancelAnimationFrame(frame);
1011
+ document.removeEventListener("pointermove", move);
1012
+ document.documentElement.removeEventListener("pointerleave", leave);
1013
+ window.removeEventListener("blur", leave);
1014
+ clear();
1015
+ };
1016
+ }
1017
+ return () => {
1018
+ if (--users === 0) {
1019
+ cleanup?.();
1020
+ cleanup = void 0;
1021
+ }
1022
+ };
1023
+ }, []);
1024
+ }
1025
+
1026
+ // src/components/FloatingScrollbarProvider.tsx
1027
+ var scrollableOverflow = /* @__PURE__ */ new Set(["auto", "scroll", "overlay"]);
1028
+ var clippingOverflow = /* @__PURE__ */ new Set(["auto", "scroll", "overlay", "hidden", "clip"]);
1029
+ function isRootScroller(target) {
1030
+ return target === document.scrollingElement || target === document.documentElement;
1031
+ }
1032
+ function canScroll(target, axis) {
1033
+ if (target.matches(".sia-table__viewport, [data-sia-native-scrollbar]")) return false;
1034
+ if (isRootScroller(target)) {
1035
+ return axis === "horizontal" ? target.scrollWidth > target.clientWidth + 1 : target.scrollHeight > target.clientHeight + 1;
1036
+ }
1037
+ const style = getComputedStyle(target);
1038
+ const overflow = axis === "horizontal" ? style.overflowX : style.overflowY;
1039
+ const hasOverflow = axis === "horizontal" ? target.scrollWidth > target.clientWidth + 1 : target.scrollHeight > target.clientHeight + 1;
1040
+ return hasOverflow && scrollableOverflow.has(overflow);
1041
+ }
1042
+ function visibleRect(target) {
1043
+ if (isRootScroller(target)) {
1044
+ return { top: 0, right: window.innerWidth, bottom: window.innerHeight, left: 0, width: window.innerWidth, height: window.innerHeight };
1045
+ }
1046
+ const ownRect = target.getBoundingClientRect();
1047
+ let top = Math.max(0, ownRect.top);
1048
+ let right = Math.min(window.innerWidth, ownRect.right);
1049
+ let bottom = Math.min(window.innerHeight, ownRect.bottom);
1050
+ let left = Math.max(0, ownRect.left);
1051
+ let parent = target.parentElement;
1052
+ while (parent && parent !== document.body && parent !== document.documentElement) {
1053
+ const style = getComputedStyle(parent);
1054
+ const clipX = clippingOverflow.has(style.overflowX);
1055
+ const clipY = clippingOverflow.has(style.overflowY);
1056
+ if (clipX || clipY) {
1057
+ const rect = parent.getBoundingClientRect();
1058
+ if (clipX) {
1059
+ left = Math.max(left, rect.left);
1060
+ right = Math.min(right, rect.right);
1061
+ }
1062
+ if (clipY) {
1063
+ top = Math.max(top, rect.top);
1064
+ bottom = Math.min(bottom, rect.bottom);
1065
+ }
1066
+ }
1067
+ parent = parent.parentElement;
1068
+ }
1069
+ return {
1070
+ top,
1071
+ right,
1072
+ bottom,
1073
+ left,
1074
+ width: Math.max(0, right - left),
1075
+ height: Math.max(0, bottom - top)
1076
+ };
1077
+ }
1078
+ function createTrack(axis) {
1079
+ const track = document.createElement("div");
1080
+ const thumb = document.createElement("div");
1081
+ track.className = `sia-scrollbar-track sia-floating-scrollbar sia-floating-scrollbar--${axis}`;
1082
+ thumb.className = "sia-floating-scrollbar__thumb";
1083
+ track.dataset.axis = axis;
1084
+ track.setAttribute("aria-hidden", "true");
1085
+ track.append(thumb);
1086
+ return { track, thumb };
1087
+ }
1088
+ function FloatingScrollbarProvider({ disabled = false, targetRef }) {
1089
+ useScrollbarProximity();
1090
+ useEffect6(() => {
1091
+ if (disabled) return;
1092
+ const layer = document.createElement("div");
1093
+ layer.className = "sia-floating-scrollbar-layer";
1094
+ layer.setAttribute("aria-hidden", "true");
1095
+ document.body.append(layer);
1096
+ const entries = /* @__PURE__ */ new Map();
1097
+ let scanFrame = 0;
1098
+ let updateFrame = 0;
1099
+ const setEntryVisible = (entry, visible) => {
1100
+ entry.horizontalTrack.classList.toggle("sia-floating-scrollbar--visible", visible);
1101
+ entry.verticalTrack.classList.toggle("sia-floating-scrollbar--visible", visible);
1102
+ };
1103
+ const showEntry = (entry) => {
1104
+ if (entry.hideTimer !== null) window.clearTimeout(entry.hideTimer);
1105
+ entry.hideTimer = null;
1106
+ setEntryVisible(entry, true);
1107
+ };
1108
+ const scheduleHide = (entry, delay = 500) => {
1109
+ if (entry.hideTimer !== null) window.clearTimeout(entry.hideTimer);
1110
+ entry.hideTimer = window.setTimeout(() => {
1111
+ entry.hideTimer = null;
1112
+ if (!entry.hoveringTarget && !entry.hoveringTrack && !entry.dragging) setEntryVisible(entry, false);
1113
+ }, delay);
1114
+ };
1115
+ const updateEntry = (entry) => {
1116
+ const { target, horizontalTrack, horizontalThumb, verticalTrack, verticalThumb } = entry;
1117
+ const hasHorizontal = canScroll(target, "horizontal");
1118
+ const hasVertical = canScroll(target, "vertical");
1119
+ const rect = visibleRect(target);
1120
+ const thickness = 10;
1121
+ horizontalTrack.hidden = !hasHorizontal || rect.width <= thickness || rect.height <= 0;
1122
+ verticalTrack.hidden = !hasVertical || rect.height <= thickness || rect.width <= 0;
1123
+ if (!horizontalTrack.hidden) {
1124
+ const trackWidth = Math.max(0, rect.width - (hasVertical ? thickness : 0));
1125
+ const thumbWidth = Math.max(24, Math.min(trackWidth, trackWidth * target.clientWidth / target.scrollWidth));
1126
+ const travel = Math.max(0, trackWidth - thumbWidth);
1127
+ const maxScroll = Math.max(1, target.scrollWidth - target.clientWidth);
1128
+ horizontalTrack.style.left = `${rect.left}px`;
1129
+ horizontalTrack.style.top = `${rect.bottom - thickness}px`;
1130
+ horizontalTrack.style.width = `${trackWidth}px`;
1131
+ horizontalTrack.style.height = `${thickness}px`;
1132
+ horizontalThumb.style.width = `${thumbWidth}px`;
1133
+ horizontalThumb.style.transform = `translate3d(${travel * target.scrollLeft / maxScroll}px, 0, 0)`;
1134
+ }
1135
+ if (!verticalTrack.hidden) {
1136
+ const trackHeight = Math.max(0, rect.height - (hasHorizontal ? thickness : 0));
1137
+ const thumbHeight = Math.max(24, Math.min(trackHeight, trackHeight * target.clientHeight / target.scrollHeight));
1138
+ const travel = Math.max(0, trackHeight - thumbHeight);
1139
+ const maxScroll = Math.max(1, target.scrollHeight - target.clientHeight);
1140
+ verticalTrack.style.left = `${rect.right - thickness}px`;
1141
+ verticalTrack.style.top = `${rect.top}px`;
1142
+ verticalTrack.style.width = `${thickness}px`;
1143
+ verticalTrack.style.height = `${trackHeight}px`;
1144
+ verticalThumb.style.height = `${thumbHeight}px`;
1145
+ verticalThumb.style.transform = `translate3d(0, ${travel * target.scrollTop / maxScroll}px, 0)`;
1146
+ }
1147
+ };
1148
+ const scheduleUpdateAll = () => {
1149
+ if (updateFrame) return;
1150
+ updateFrame = window.requestAnimationFrame(() => {
1151
+ updateFrame = 0;
1152
+ entries.forEach(updateEntry);
1153
+ });
1154
+ };
1155
+ const bindTrackDrag = (entry, axis, track, thumb) => {
1156
+ const target = entry.target;
1157
+ const onTrackPointerEnter = () => {
1158
+ entry.hoveringTrack = true;
1159
+ showEntry(entry);
1160
+ };
1161
+ const onTrackPointerLeave = () => {
1162
+ entry.hoveringTrack = false;
1163
+ scheduleHide(entry);
1164
+ };
1165
+ const onTrackPointerDown = (event) => {
1166
+ if (event.target === thumb || event.button !== 0) return;
1167
+ event.preventDefault();
1168
+ event.stopPropagation();
1169
+ const trackRect = track.getBoundingClientRect();
1170
+ const thumbRect = thumb.getBoundingClientRect();
1171
+ const trackLength = axis === "vertical" ? trackRect.height : trackRect.width;
1172
+ const thumbLength = axis === "vertical" ? thumbRect.height : thumbRect.width;
1173
+ const pointer = axis === "vertical" ? event.clientY - trackRect.top : event.clientX - trackRect.left;
1174
+ const ratio = Math.max(0, Math.min(1, (pointer - thumbLength / 2) / Math.max(1, trackLength - thumbLength)));
1175
+ if (axis === "vertical") target.scrollTop = ratio * (target.scrollHeight - target.clientHeight);
1176
+ else target.scrollLeft = ratio * (target.scrollWidth - target.clientWidth);
1177
+ };
1178
+ const onThumbPointerDown = (event) => {
1179
+ if (event.button !== 0) return;
1180
+ event.preventDefault();
1181
+ event.stopPropagation();
1182
+ entry.dragging = true;
1183
+ showEntry(entry);
1184
+ thumb.setPointerCapture(event.pointerId);
1185
+ const startPointer = axis === "vertical" ? event.clientY : event.clientX;
1186
+ const startScroll = axis === "vertical" ? target.scrollTop : target.scrollLeft;
1187
+ const trackRect = track.getBoundingClientRect();
1188
+ const thumbRect = thumb.getBoundingClientRect();
1189
+ const trackLength = axis === "vertical" ? trackRect.height : trackRect.width;
1190
+ const thumbLength = axis === "vertical" ? thumbRect.height : thumbRect.width;
1191
+ const maxScroll = axis === "vertical" ? target.scrollHeight - target.clientHeight : target.scrollWidth - target.clientWidth;
1192
+ const travel = Math.max(1, trackLength - thumbLength);
1193
+ const onPointerMove = (moveEvent) => {
1194
+ if (moveEvent.pointerId !== event.pointerId) return;
1195
+ const pointer = axis === "vertical" ? moveEvent.clientY : moveEvent.clientX;
1196
+ const nextScroll = startScroll + (pointer - startPointer) * maxScroll / travel;
1197
+ if (axis === "vertical") target.scrollTop = nextScroll;
1198
+ else target.scrollLeft = nextScroll;
1199
+ };
1200
+ const onPointerUp = (upEvent) => {
1201
+ if (upEvent.pointerId !== event.pointerId) return;
1202
+ entry.dragging = false;
1203
+ thumb.releasePointerCapture(event.pointerId);
1204
+ window.removeEventListener("pointermove", onPointerMove);
1205
+ window.removeEventListener("pointerup", onPointerUp);
1206
+ window.removeEventListener("pointercancel", onPointerUp);
1207
+ scheduleHide(entry);
1208
+ };
1209
+ window.addEventListener("pointermove", onPointerMove);
1210
+ window.addEventListener("pointerup", onPointerUp);
1211
+ window.addEventListener("pointercancel", onPointerUp);
1212
+ };
1213
+ track.addEventListener("pointerenter", onTrackPointerEnter);
1214
+ track.addEventListener("pointerleave", onTrackPointerLeave);
1215
+ track.addEventListener("pointerdown", onTrackPointerDown);
1216
+ thumb.addEventListener("pointerdown", onThumbPointerDown);
1217
+ return () => {
1218
+ track.removeEventListener("pointerenter", onTrackPointerEnter);
1219
+ track.removeEventListener("pointerleave", onTrackPointerLeave);
1220
+ track.removeEventListener("pointerdown", onTrackPointerDown);
1221
+ thumb.removeEventListener("pointerdown", onThumbPointerDown);
1222
+ };
1223
+ };
1224
+ const resizeObserver = new ResizeObserver((observedEntries) => {
1225
+ observedEntries.forEach(({ target }) => {
1226
+ const entry = entries.get(target);
1227
+ if (entry) updateEntry(entry);
1228
+ });
1229
+ });
1230
+ const addTarget = (target) => {
1231
+ if (entries.has(target)) return;
1232
+ const horizontal = createTrack("horizontal");
1233
+ const vertical = createTrack("vertical");
1234
+ layer.append(horizontal.track, vertical.track);
1235
+ const onPointerEnter = () => {
1236
+ const entry2 = entries.get(target);
1237
+ if (!entry2) return;
1238
+ entry2.hoveringTarget = true;
1239
+ showEntry(entry2);
1240
+ };
1241
+ const onPointerLeave = () => {
1242
+ const entry2 = entries.get(target);
1243
+ if (!entry2) return;
1244
+ entry2.hoveringTarget = false;
1245
+ scheduleHide(entry2);
1246
+ };
1247
+ const onScroll = () => {
1248
+ const entry2 = entries.get(target);
1249
+ if (!entry2) return;
1250
+ updateEntry(entry2);
1251
+ showEntry(entry2);
1252
+ scheduleHide(entry2, 700);
1253
+ };
1254
+ target.addEventListener("pointerenter", onPointerEnter);
1255
+ target.addEventListener("pointerleave", onPointerLeave);
1256
+ target.addEventListener("scroll", onScroll, { passive: true });
1257
+ const entry = {
1258
+ target,
1259
+ horizontalTrack: horizontal.track,
1260
+ horizontalThumb: horizontal.thumb,
1261
+ verticalTrack: vertical.track,
1262
+ verticalThumb: vertical.thumb,
1263
+ hideTimer: null,
1264
+ hoveringTarget: target.matches(":hover"),
1265
+ hoveringTrack: false,
1266
+ dragging: false,
1267
+ cleanup: () => void 0
1268
+ };
1269
+ const cleanupHorizontal = bindTrackDrag(entry, "horizontal", horizontal.track, horizontal.thumb);
1270
+ const cleanupVertical = bindTrackDrag(entry, "vertical", vertical.track, vertical.thumb);
1271
+ entry.cleanup = () => {
1272
+ if (entry.hideTimer !== null) window.clearTimeout(entry.hideTimer);
1273
+ cleanupHorizontal();
1274
+ cleanupVertical();
1275
+ target.removeEventListener("pointerenter", onPointerEnter);
1276
+ target.removeEventListener("pointerleave", onPointerLeave);
1277
+ target.removeEventListener("scroll", onScroll);
1278
+ resizeObserver.unobserve(target);
1279
+ target.removeAttribute("data-sia-floating-scrollbar");
1280
+ horizontal.track.remove();
1281
+ vertical.track.remove();
1282
+ };
1283
+ entries.set(target, entry);
1284
+ target.setAttribute("data-sia-floating-scrollbar", "");
1285
+ resizeObserver.observe(target);
1286
+ updateEntry(entry);
1287
+ if (entry.hoveringTarget) showEntry(entry);
1288
+ };
1289
+ const removeTarget = (target) => {
1290
+ const entry = entries.get(target);
1291
+ if (!entry) return;
1292
+ entry.cleanup();
1293
+ entries.delete(target);
1294
+ };
1295
+ const scanTargets = () => {
1296
+ scanFrame = 0;
1297
+ const candidates = (targetRef ? [targetRef.current] : [document.scrollingElement, ...document.querySelectorAll("*")]).filter((target) => target instanceof HTMLElement);
1298
+ const nextTargets = /* @__PURE__ */ new Set();
1299
+ candidates.forEach((target) => {
1300
+ if (canScroll(target, "horizontal") || canScroll(target, "vertical")) {
1301
+ nextTargets.add(target);
1302
+ if (entries.has(target) || !target.hasAttribute("data-sia-floating-scrollbar")) addTarget(target);
1303
+ }
1304
+ });
1305
+ entries.forEach((_entry, target) => {
1306
+ if (!target.isConnected || !nextTargets.has(target)) removeTarget(target);
1307
+ else updateEntry(_entry);
1308
+ });
1309
+ };
1310
+ const scheduleScan = () => {
1311
+ if (scanFrame) return;
1312
+ scanFrame = window.requestAnimationFrame(scanTargets);
1313
+ };
1314
+ const mutationObserver = new MutationObserver((mutations) => {
1315
+ if (mutations.some((mutation) => !layer.contains(mutation.target))) scheduleScan();
1316
+ });
1317
+ mutationObserver.observe(document.documentElement, { subtree: true, childList: true, attributes: true });
1318
+ window.addEventListener("resize", scheduleScan);
1319
+ window.addEventListener("scroll", scheduleUpdateAll, true);
1320
+ scheduleScan();
1321
+ return () => {
1322
+ mutationObserver.disconnect();
1323
+ resizeObserver.disconnect();
1324
+ window.removeEventListener("resize", scheduleScan);
1325
+ window.removeEventListener("scroll", scheduleUpdateAll, true);
1326
+ if (scanFrame) window.cancelAnimationFrame(scanFrame);
1327
+ if (updateFrame) window.cancelAnimationFrame(updateFrame);
1328
+ entries.forEach((entry) => entry.cleanup());
1329
+ entries.clear();
1330
+ layer.remove();
1054
1331
  };
1055
- }, [keyboard, open, panelRef]);
1056
- }
1057
- function OverlayPortal({ children, container }) {
1058
- if (typeof document === "undefined") return null;
1059
- const target = typeof container === "function" ? container() : container ?? document.body;
1060
- return createPortal2(children, target);
1061
- }
1062
-
1063
- // src/components/Modal.tsx
1064
- import { Fragment as Fragment2, jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
1065
- var modalHandles = /* @__PURE__ */ new Set();
1066
- var MODAL_MOTION_DURATION = 220;
1067
- var typeIcons = {
1068
- info: /* @__PURE__ */ jsx3(Icon, { name: "info", size: 23 }),
1069
- success: /* @__PURE__ */ jsx3(Icon, { name: "circle-check", size: 23 }),
1070
- warning: /* @__PURE__ */ jsx3(Icon, { name: "warning", size: 23 }),
1071
- error: /* @__PURE__ */ jsx3(Icon, { name: "circle-close", size: 23 }),
1072
- confirm: /* @__PURE__ */ jsx3(Icon, { name: "question", size: 23 })
1073
- };
1074
- var ModalRoot = forwardRef2(function Modal({
1075
- open,
1076
- title,
1077
- children,
1078
- footer,
1079
- footerExtra,
1080
- type = "default",
1081
- okText = "\u786E\u5B9A",
1082
- cancelText = "\u53D6\u6D88",
1083
- showCancel = type === "default" || type === "confirm",
1084
- confirmLoading = false,
1085
- okButtonProps,
1086
- cancelButtonProps,
1087
- closable = true,
1088
- maskClosable = true,
1089
- keyboard = true,
1090
- centered = false,
1091
- width = 520,
1092
- height,
1093
- zIndex = 1e3,
1094
- getContainer,
1095
- onOk,
1096
- onCancel,
1097
- onOpenChange,
1098
- afterOpenChange,
1099
- className = "",
1100
- style,
1101
- ...props
1102
- }, forwardedRef) {
1103
- const localRef = useRef4(null);
1104
- const titleId = useId3();
1105
- const [submitting, setSubmitting] = useState2(false);
1106
- const [rendered, setRendered] = useState2(open);
1107
- const [closing, setClosing] = useState2(false);
1108
- const afterOpenChangeRef = useRef4(afterOpenChange);
1109
- afterOpenChangeRef.current = afterOpenChange;
1110
- const submitLock = useRef4(false);
1111
- useEffect6(() => {
1112
- if (!open || !rendered) return void 0;
1113
- const timer = window.setTimeout(() => afterOpenChangeRef.current?.(true), MODAL_MOTION_DURATION);
1114
- return () => window.clearTimeout(timer);
1115
- }, [open, rendered]);
1116
- useEffect6(() => {
1117
- if (open) {
1118
- setRendered(true);
1119
- setClosing(false);
1120
- return void 0;
1121
- }
1122
- if (!rendered) return void 0;
1123
- setClosing(true);
1124
- const timer = window.setTimeout(() => {
1125
- setRendered(false);
1126
- setClosing(false);
1127
- afterOpenChangeRef.current?.(false);
1128
- }, MODAL_MOTION_DURATION);
1129
- return () => window.clearTimeout(timer);
1130
- }, [open, rendered]);
1131
- function setRef(node) {
1132
- localRef.current = node;
1133
- if (typeof forwardedRef === "function") forwardedRef(node);
1134
- else if (forwardedRef) forwardedRef.current = node;
1135
- }
1136
- function requestClose() {
1137
- if (submitting || confirmLoading) return;
1138
- onCancel?.();
1139
- onOpenChange?.(false);
1140
- }
1141
- async function handleOk() {
1142
- if (submitLock.current || confirmLoading) return;
1143
- submitLock.current = true;
1144
- if (!onOk) {
1145
- onOpenChange?.(false);
1146
- submitLock.current = false;
1147
- return;
1148
- }
1149
- try {
1150
- const result = onOk();
1151
- if (result instanceof Promise) {
1152
- setSubmitting(true);
1153
- const resolved = await result;
1154
- if (resolved !== false) onOpenChange?.(false);
1155
- } else if (result !== false) {
1156
- onOpenChange?.(false);
1157
- }
1158
- } catch {
1159
- return;
1160
- } finally {
1161
- submitLock.current = false;
1162
- setSubmitting(false);
1163
- }
1164
- }
1165
- useOverlayLifecycle(rendered, localRef, requestClose, keyboard);
1166
- if (!rendered) return null;
1167
- const mergedStyle = {
1168
- ...height !== void 0 ? { display: "flex", flexDirection: "column", height, maxHeight: "calc(100dvh - 48px)" } : {},
1169
- ...style,
1170
- "--sia-modal-width": typeof width === "number" ? `${width}px` : width
1171
- };
1172
- const loading = submitting || confirmLoading;
1173
- const defaultFooter = /* @__PURE__ */ jsxs3(Fragment2, { children: [
1174
- showCancel ? /* @__PURE__ */ jsx3(Button, { ...cancelButtonProps, onClick: requestClose, disabled: loading || cancelButtonProps?.disabled, children: cancelText }) : null,
1175
- /* @__PURE__ */ jsx3(Button, { variant: "primary", ...okButtonProps, onClick: handleOk, loading, children: okText })
1176
- ] });
1177
- return /* @__PURE__ */ jsx3(OverlayPortal, { container: getContainer, children: /* @__PURE__ */ jsx3(
1178
- "div",
1179
- {
1180
- className: `sia-modal-root${centered ? " sia-modal-root--centered" : ""}`,
1181
- "data-state": closing ? "closing" : "open",
1182
- style: { zIndex },
1183
- onMouseDown: (event) => {
1184
- if (event.target === event.currentTarget && maskClosable) requestClose();
1185
- },
1186
- children: /* @__PURE__ */ jsxs3(
1187
- "div",
1188
- {
1189
- ref: setRef,
1190
- className: `sia-modal sia-modal--${type} ${className}`.trim(),
1191
- style: mergedStyle,
1192
- role: "dialog",
1193
- "aria-modal": "true",
1194
- "aria-labelledby": title ? titleId : void 0,
1195
- tabIndex: -1,
1196
- ...withTitleTooltip(props),
1197
- children: [
1198
- /* @__PURE__ */ jsxs3("div", { className: "sia-modal__header", style: height !== void 0 ? { flexShrink: 0 } : void 0, children: [
1199
- /* @__PURE__ */ jsxs3("div", { className: "sia-modal__heading", children: [
1200
- typeIcons[type] ? /* @__PURE__ */ jsx3("span", { className: "sia-modal__type-icon", "aria-hidden": "true", children: typeIcons[type] }) : null,
1201
- title ? /* @__PURE__ */ jsx3("strong", { id: titleId, children: title }) : null
1202
- ] }),
1203
- closable ? /* @__PURE__ */ jsx3("button", { type: "button", className: "sia-modal__close", "aria-label": "\u5173\u95ED\u5BF9\u8BDD\u6846", onClick: requestClose, children: /* @__PURE__ */ jsx3(Icon, { name: "close", size: 17 }) }) : null
1204
- ] }),
1205
- children !== void 0 ? /* @__PURE__ */ jsx3("div", { className: "sia-modal__body", style: height !== void 0 ? { flex: 1, minHeight: 0, overflow: "auto" } : void 0, children }) : null,
1206
- footer !== null ? /* @__PURE__ */ jsxs3("div", { className: "sia-modal__footer", style: height !== void 0 ? { flexShrink: 0 } : void 0, children: [
1207
- footer === void 0 && footerExtra != null ? /* @__PURE__ */ jsx3("div", { style: { marginRight: "auto" }, children: footerExtra }) : null,
1208
- footer ?? defaultFooter
1209
- ] }) : null
1210
- ]
1211
- }
1212
- )
1213
- }
1214
- ) });
1215
- });
1216
- function openModal(config) {
1217
- if (typeof document === "undefined") return { destroy() {
1218
- }, update() {
1219
- } };
1220
- const container = document.createElement("div");
1221
- container.className = "sia-modal-api-host";
1222
- document.body.appendChild(container);
1223
- const root = createRoot(container);
1224
- let currentConfig = config;
1225
- let currentOpen = true;
1226
- let destroyed = false;
1227
- let cleanupTimer;
1228
- function cleanup2() {
1229
- if (destroyed) return;
1230
- destroyed = true;
1231
- if (cleanupTimer !== void 0) window.clearTimeout(cleanupTimer);
1232
- root.unmount();
1233
- container.remove();
1234
- modalHandles.delete(handle);
1235
- }
1236
- function closeWithMotion() {
1237
- if (destroyed || !currentOpen) return;
1238
- currentOpen = false;
1239
- render();
1240
- cleanupTimer = window.setTimeout(cleanup2, MODAL_MOTION_DURATION);
1241
- }
1242
- function render() {
1243
- root.render(/* @__PURE__ */ jsx3(ModalRoot, { ...currentConfig, open: currentOpen, onOpenChange: (nextOpen) => {
1244
- currentConfig.onOpenChange?.(nextOpen);
1245
- if (!nextOpen) closeWithMotion();
1246
- } }));
1247
- }
1248
- const handle = {
1249
- destroy: closeWithMotion,
1250
- update(next) {
1251
- if (destroyed) return;
1252
- currentConfig = { ...currentConfig, ...next };
1253
- render();
1254
- }
1255
- };
1256
- modalHandles.add(handle);
1257
- render();
1258
- return handle;
1259
- }
1260
- function preset(type, defaults = {}) {
1261
- return (config) => openModal({
1262
- okText: type === "confirm" ? "\u786E\u5B9A" : "\u77E5\u9053\u4E86",
1263
- showCancel: type === "confirm",
1264
- closable: type !== "confirm",
1265
- maskClosable: type !== "confirm",
1266
- ...defaults,
1267
- ...config,
1268
- type
1269
- });
1332
+ }, [disabled, targetRef]);
1333
+ return null;
1270
1334
  }
1271
- var Modal2 = Object.assign(ModalRoot, {
1272
- /** 直接打开实例,返回可用于更新或关闭的操作句柄。 */
1273
- open: openModal,
1274
- /** 显示普通信息提示。 */
1275
- info: preset("info", { title: "\u63D0\u793A" }),
1276
- /** 显示成功提示。 */
1277
- success: preset("success", { title: "\u64CD\u4F5C\u6210\u529F" }),
1278
- /** 显示警告提示。 */
1279
- warning: preset("warning", { title: "\u8BF7\u6CE8\u610F" }),
1280
- /** 显示错误提示。 */
1281
- error: preset("error", { title: "\u64CD\u4F5C\u5931\u8D25" }),
1282
- /** 打开确认弹框,支持异步确认;返回 false 可阻止关闭。 */
1283
- confirm: preset("confirm", { title: "\u786E\u8BA4\u64CD\u4F5C" }),
1284
- /** 关闭由此 API 打开的所有弹层。 */
1285
- destroyAll() {
1286
- [...modalHandles].forEach((handle) => handle.destroy());
1287
- }
1288
- });
1289
1335
 
1290
1336
  // src/components/SelectionPanel.tsx
1291
1337
  import { useEffect as useEffect7, useId as useId4, useMemo as useMemo3, useRef as useRef5, useState as useState3 } from "react";
@@ -2006,8 +2052,6 @@ var Select = forwardRef3(function Select2({
2006
2052
  });
2007
2053
 
2008
2054
  export {
2009
- useScrollbarProximity,
2010
- FloatingScrollbarProvider,
2011
2055
  Checkbox2 as Checkbox,
2012
2056
  Tooltip,
2013
2057
  Popover,
@@ -2016,6 +2060,8 @@ export {
2016
2060
  useOverlayLifecycle,
2017
2061
  OverlayPortal,
2018
2062
  Modal2 as Modal,
2063
+ useScrollbarProximity,
2064
+ FloatingScrollbarProvider,
2019
2065
  SelectionPanel,
2020
2066
  OverflowTags,
2021
2067
  Select