@sia.soul/sia-react-ui 0.1.6 → 0.1.8

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.
@@ -1,2154 +0,0 @@
1
- import {
2
- TextArea
3
- } from "./chunk-WHARTF2I.js";
4
- import {
5
- Dropdown
6
- } from "./chunk-IHTODT53.js";
7
- import {
8
- Button
9
- } from "./chunk-MBS2HXLZ.js";
10
- import {
11
- Icon
12
- } from "./chunk-EJ23MVR6.js";
13
- import {
14
- useControllableState
15
- } from "./chunk-EJDPRAU2.js";
16
-
17
- // src/components/Card.tsx
18
- import { useState } from "react";
19
- import { Fragment, jsx, jsxs } from "react/jsx-runtime";
20
- function Card({
21
- title,
22
- description,
23
- extra,
24
- accent,
25
- accentColor,
26
- collapsible = false,
27
- collapsed,
28
- defaultCollapsed = false,
29
- onCollapsedChange,
30
- bodyHeight,
31
- bodyClassName = "",
32
- bodyStyle,
33
- className = "",
34
- style,
35
- children,
36
- ...props
37
- }) {
38
- const [internalCollapsed, setInternalCollapsed] = useState(defaultCollapsed);
39
- const isCollapsed = collapsed ?? internalCollapsed;
40
- const hasHeader = title || extra || collapsible;
41
- const mergedStyle = accentColor ? { "--sia-card-accent": accentColor, ...style } : style;
42
- const mergedBodyStyle = bodyHeight === void 0 ? { ...bodyStyle } : { height: bodyHeight, overflowY: "auto", ...bodyStyle };
43
- function toggleCollapsed() {
44
- const nextCollapsed = !isCollapsed;
45
- if (collapsed === void 0) setInternalCollapsed(nextCollapsed);
46
- onCollapsedChange?.(nextCollapsed);
47
- }
48
- const heading = /* @__PURE__ */ jsxs("span", { className: "sia-card__heading", children: [
49
- accent || accentColor ? /* @__PURE__ */ jsx("span", { className: `sia-card__accent${accent ? ` sia-card__accent--${accent}` : ""}`, "aria-hidden": "true" }) : null,
50
- title ? /* @__PURE__ */ jsx("h3", { className: "sia-card__title", children: title }) : null
51
- ] });
52
- return /* @__PURE__ */ jsxs("section", { className: `sia-card${isCollapsed ? " sia-card--collapsed" : ""} ${className}`.trim(), style: mergedStyle, ...props, children: [
53
- hasHeader ? /* @__PURE__ */ jsxs("header", { className: "sia-card__header", children: [
54
- collapsible ? /* @__PURE__ */ jsxs("button", { type: "button", className: "sia-card__collapse-trigger", "aria-expanded": !isCollapsed, onClick: toggleCollapsed, children: [
55
- heading,
56
- /* @__PURE__ */ jsx(Icon, { name: isCollapsed ? "chevron-down" : "chevron-up", size: 16 })
57
- ] }) : heading,
58
- extra ? /* @__PURE__ */ jsx("div", { className: "sia-card__extra", children: extra }) : null
59
- ] }) : null,
60
- !isCollapsed ? /* @__PURE__ */ jsxs(Fragment, { children: [
61
- description ? /* @__PURE__ */ jsx("p", { className: "sia-card__description", children: description }) : null,
62
- /* @__PURE__ */ jsx("div", { className: `sia-card__body ${bodyClassName}`.trim(), style: mergedBodyStyle, children })
63
- ] }) : null
64
- ] });
65
- }
66
-
67
- // src/components/Tag.tsx
68
- import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
69
- function Tag({
70
- status = "default",
71
- compact = false,
72
- variant = "soft",
73
- icon,
74
- closable = false,
75
- disabled = false,
76
- onClose,
77
- className = "",
78
- children,
79
- ...props
80
- }) {
81
- return /* @__PURE__ */ jsxs2(
82
- "span",
83
- {
84
- className: `sia-tag sia-tag--${status} sia-tag--${variant}${compact ? " sia-tag--compact" : ""}${disabled ? " sia-tag--disabled" : ""} ${className}`.trim(),
85
- "aria-disabled": disabled || void 0,
86
- ...props,
87
- children: [
88
- icon ? /* @__PURE__ */ jsx2("span", { className: "sia-tag__icon", children: icon }) : null,
89
- /* @__PURE__ */ jsx2("span", { children }),
90
- closable ? /* @__PURE__ */ jsx2(
91
- "button",
92
- {
93
- type: "button",
94
- className: "sia-tag__close",
95
- "aria-label": "\u79FB\u9664\u6807\u7B7E",
96
- disabled,
97
- onClick: (event) => {
98
- event.stopPropagation();
99
- onClose?.(event);
100
- },
101
- children: /* @__PURE__ */ jsx2(Icon, { name: "close", size: 12 })
102
- }
103
- ) : null
104
- ]
105
- }
106
- );
107
- }
108
-
109
- // src/components/Tabs.tsx
110
- import { useId, useMemo, useState as useState2 } from "react";
111
- import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
112
- function Tabs({
113
- items,
114
- activeKey,
115
- defaultActiveKey,
116
- onChange,
117
- type = "line",
118
- size = "medium",
119
- tabPosition,
120
- orientation = "horizontal",
121
- centered = false,
122
- tabBarTitle,
123
- tabBarExtraContent,
124
- destroyInactiveTabPane = true,
125
- closable = false,
126
- onEdit,
127
- tabContextMenu,
128
- onTabContextMenuClick,
129
- className = "",
130
- ...props
131
- }) {
132
- const firstEnabledKey = items.find((item) => !item.disabled)?.key ?? "";
133
- const [internalActiveKey, setInternalActiveKey] = useState2(defaultActiveKey ?? firstEnabledKey);
134
- const currentKey = activeKey ?? internalActiveKey;
135
- const selectedKey = items.some((item) => item.key === currentKey && !item.disabled) ? currentKey : firstEnabledKey;
136
- const baseId = useId().replace(/:/g, "");
137
- const enabledItems = useMemo(() => items.filter((item) => !item.disabled), [items]);
138
- const resolvedPosition = tabPosition ?? (orientation === "vertical" ? "left" : "top");
139
- const resolvedOrientation = resolvedPosition === "left" || resolvedPosition === "right" ? "vertical" : "horizontal";
140
- function selectTab(key) {
141
- if (key === selectedKey) return;
142
- if (activeKey === void 0) setInternalActiveKey(key);
143
- onChange?.(key);
144
- }
145
- function focusAndSelect(key) {
146
- selectTab(key);
147
- document.getElementById(`${baseId}-tab-${key}`)?.focus();
148
- }
149
- function handleKeyDown(event, key) {
150
- const previousKey = resolvedOrientation === "horizontal" ? "ArrowLeft" : "ArrowUp";
151
- const nextKey = resolvedOrientation === "horizontal" ? "ArrowRight" : "ArrowDown";
152
- if (![previousKey, nextKey, "Home", "End"].includes(event.key) || enabledItems.length === 0) return;
153
- event.preventDefault();
154
- const currentIndex = Math.max(0, enabledItems.findIndex((item) => item.key === key));
155
- if (event.key === "Home") return focusAndSelect(enabledItems[0].key);
156
- if (event.key === "End") return focusAndSelect(enabledItems[enabledItems.length - 1].key);
157
- const offset = event.key === nextKey ? 1 : -1;
158
- const nextIndex = (currentIndex + offset + enabledItems.length) % enabledItems.length;
159
- focusAndSelect(enabledItems[nextIndex].key);
160
- }
161
- const hasPanels = items.some((item) => item.children !== void 0);
162
- return /* @__PURE__ */ jsxs3(
163
- "div",
164
- {
165
- className: `sia-tabs sia-tabs--${type} sia-tabs--${size} sia-tabs--${resolvedOrientation} sia-tabs--${resolvedPosition}${centered ? " sia-tabs--centered" : ""} ${className}`.trim(),
166
- ...props,
167
- children: [
168
- /* @__PURE__ */ jsxs3("div", { className: "sia-tabs__header", children: [
169
- tabBarTitle ? /* @__PURE__ */ jsxs3("div", { className: "sia-tabs__title", children: [
170
- tabBarTitle,
171
- type === "tech-line" ? /* @__PURE__ */ jsx3("svg", { className: "sia-tabs__title-connector", viewBox: "0 0 32 40", "aria-hidden": "true", children: /* @__PURE__ */ jsx3("path", { d: "M0 39 H5 Q8 39 10 35 L28 3 Q29 1 32 1", fill: "none", stroke: "currentColor", strokeWidth: "1.5" }) }) : null
172
- ] }) : null,
173
- /* @__PURE__ */ jsx3("div", { className: "sia-tabs__list", role: "tablist", "aria-orientation": resolvedOrientation, children: items.map((item) => {
174
- const selected = item.key === selectedKey;
175
- const canClose = item.closable ?? closable;
176
- const tabNode = /* @__PURE__ */ jsxs3("span", { className: `sia-tabs__tab-wrap${canClose ? " is-closable" : ""}`, children: [
177
- /* @__PURE__ */ jsxs3(
178
- "button",
179
- {
180
- id: `${baseId}-tab-${item.key}`,
181
- type: "button",
182
- className: "sia-tabs__tab",
183
- role: "tab",
184
- "aria-selected": selected,
185
- "aria-controls": hasPanels ? `${baseId}-panel-${item.key}` : void 0,
186
- tabIndex: selected ? 0 : -1,
187
- disabled: item.disabled,
188
- onClick: () => selectTab(item.key),
189
- onKeyDown: (event) => handleKeyDown(event, item.key),
190
- children: [
191
- type === "tech-line" ? /* @__PURE__ */ jsxs3("svg", { className: "sia-tabs__tab-shape", viewBox: "0 0 100 34", preserveAspectRatio: "none", "aria-hidden": "true", children: [
192
- /* @__PURE__ */ jsx3("defs", { children: /* @__PURE__ */ jsxs3("linearGradient", { id: `${baseId}-tab-fill-${item.key}`, x1: "0", y1: "0", x2: "1", y2: "0", children: [
193
- /* @__PURE__ */ jsx3("stop", { offset: "0", stopColor: "var(--sia-tabs-line-fill-edge, var(--sia-tabs-line-fill))" }),
194
- /* @__PURE__ */ jsx3("stop", { offset: ".53", stopColor: "var(--sia-tabs-line-fill-center, var(--sia-tabs-line-fill))" }),
195
- /* @__PURE__ */ jsx3("stop", { offset: "1", stopColor: "var(--sia-tabs-line-fill-edge, var(--sia-tabs-line-fill))" })
196
- ] }) }),
197
- /* @__PURE__ */ jsx3(
198
- "path",
199
- {
200
- d: "M19 1 H96 Q99 1 97.5 4 L83.5 30 Q82 33 79 33 H4 Q1 33 2.5 30 L16.5 4 Q18 1 19 1 Z",
201
- fill: `url(#${baseId}-tab-fill-${item.key})`,
202
- stroke: "currentColor",
203
- strokeWidth: "1.5",
204
- vectorEffect: "non-scaling-stroke"
205
- }
206
- )
207
- ] }) : null,
208
- item.icon ? /* @__PURE__ */ jsx3("span", { className: "sia-tabs__icon", children: item.icon }) : null,
209
- /* @__PURE__ */ jsx3("span", { children: item.label })
210
- ]
211
- }
212
- ),
213
- canClose ? /* @__PURE__ */ jsx3(
214
- "button",
215
- {
216
- type: "button",
217
- className: "sia-tabs__close",
218
- "aria-label": `\u5173\u95ED${typeof item.label === "string" ? item.label : "\u6807\u7B7E\u9875"}`,
219
- disabled: item.disabled,
220
- onClick: () => onEdit?.(item.key, "remove"),
221
- children: /* @__PURE__ */ jsx3(Icon, { name: "close", size: 12 })
222
- }
223
- ) : null
224
- ] }, item.key);
225
- const contextMenuItems = typeof tabContextMenu === "function" ? tabContextMenu(item) : tabContextMenu;
226
- if (!contextMenuItems?.length) return tabNode;
227
- return /* @__PURE__ */ jsx3(
228
- Dropdown,
229
- {
230
- className: "sia-tabs__tab-context-trigger",
231
- popupClassName: "sia-tabs__tab-context-menu",
232
- trigger: ["contextMenu"],
233
- tabIndex: -1,
234
- menu: {
235
- items: contextMenuItems,
236
- selectable: false,
237
- onClick: (info) => onTabContextMenuClick?.({ ...info, tabKey: item.key, tab: item })
238
- },
239
- children: tabNode
240
- },
241
- item.key
242
- );
243
- }) }),
244
- tabBarExtraContent ? /* @__PURE__ */ jsx3("div", { className: "sia-tabs__extra", children: tabBarExtraContent }) : null
245
- ] }),
246
- hasPanels ? /* @__PURE__ */ jsx3("div", { className: "sia-tabs__panels", children: items.map((item) => {
247
- const selected = item.key === selectedKey;
248
- if (destroyInactiveTabPane && !selected) return null;
249
- return /* @__PURE__ */ jsx3(
250
- "div",
251
- {
252
- id: `${baseId}-panel-${item.key}`,
253
- className: "sia-tabs__panel",
254
- role: "tabpanel",
255
- "aria-labelledby": `${baseId}-tab-${item.key}`,
256
- hidden: !selected,
257
- tabIndex: 0,
258
- children: item.children
259
- },
260
- item.key
261
- );
262
- }) }) : null
263
- ]
264
- }
265
- );
266
- }
267
-
268
- // src/components/Checkbox.tsx
269
- import { createContext, forwardRef, useContext, useEffect, useId as useId2, useMemo as useMemo2, useRef } from "react";
270
- import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
271
- var CheckboxGroupContext = createContext(null);
272
- var CheckboxRoot = forwardRef(function Checkbox({ value, indeterminate = false, children, disabled, checked, defaultChecked, className = "", onChange, ...props }, forwardedRef) {
273
- const group = useContext(CheckboxGroupContext);
274
- const localRef = useRef(null);
275
- const fallbackId = useId2();
276
- const grouped = group !== null && value !== void 0;
277
- const currentChecked = grouped ? group.values.includes(value) : checked;
278
- useEffect(() => {
279
- if (localRef.current) localRef.current.indeterminate = indeterminate;
280
- }, [indeterminate]);
281
- function setRef(node) {
282
- localRef.current = node;
283
- if (typeof forwardedRef === "function") forwardedRef(node);
284
- else if (forwardedRef) forwardedRef.current = node;
285
- }
286
- return /* @__PURE__ */ jsxs4("label", { className: `sia-checkbox${disabled || group?.disabled ? " is-disabled" : ""} ${className}`.trim(), children: [
287
- /* @__PURE__ */ jsx4(
288
- "input",
289
- {
290
- ...props,
291
- ref: setRef,
292
- id: props.id ?? fallbackId,
293
- className: "sia-checkbox__input",
294
- type: "checkbox",
295
- name: group?.name ?? props.name,
296
- disabled: disabled || group?.disabled,
297
- checked: currentChecked,
298
- defaultChecked: grouped ? void 0 : defaultChecked,
299
- "aria-checked": indeterminate ? "mixed" : currentChecked,
300
- onChange: (event) => {
301
- if (grouped) group.toggle(value, event.currentTarget.checked);
302
- onChange?.(event.currentTarget.checked, event);
303
- }
304
- }
305
- ),
306
- /* @__PURE__ */ jsx4("span", { className: "sia-checkbox__control", "aria-hidden": "true", children: /* @__PURE__ */ jsx4("span", {}) }),
307
- children !== void 0 ? /* @__PURE__ */ jsx4("span", { className: "sia-checkbox__label", children }) : null
308
- ] });
309
- });
310
- function CheckboxGroup({
311
- value,
312
- defaultValue = [],
313
- options,
314
- disabled,
315
- name,
316
- className = "",
317
- children,
318
- onChange
319
- }) {
320
- const [values, setValues] = useControllableState({ value, defaultValue, onChange });
321
- const context = useMemo2(() => ({
322
- values,
323
- disabled,
324
- name,
325
- toggle(optionValue, checked) {
326
- const next = checked ? values.includes(optionValue) ? values : [...values, optionValue] : values.filter((item) => item !== optionValue);
327
- setValues(next);
328
- }
329
- }), [disabled, name, setValues, values]);
330
- return /* @__PURE__ */ jsx4(CheckboxGroupContext.Provider, { value: context, children: /* @__PURE__ */ jsxs4("div", { className: `sia-checkbox-group ${className}`.trim(), role: "group", children: [
331
- options?.map((option) => {
332
- const normalized = typeof option === "object" ? option : { label: String(option), value: option };
333
- return /* @__PURE__ */ jsx4(CheckboxRoot, { value: normalized.value, disabled: normalized.disabled, children: normalized.label }, normalized.value);
334
- }),
335
- children
336
- ] }) });
337
- }
338
- var Checkbox2 = Object.assign(CheckboxRoot, { Group: CheckboxGroup });
339
-
340
- // src/components/FloatingDisplay.tsx
341
- import { cloneElement, useEffect as useEffect2, useId as useId3, useLayoutEffect, useMemo as useMemo3, useRef as useRef2, useState as useState3 } from "react";
342
- import { createPortal } from "react-dom";
343
- import { Fragment as Fragment2, jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
344
- var FLOATING_GAP = 9;
345
- var FLOATING_VIEWPORT_PADDING = 8;
346
- var FLOATING_ARROW_EDGE_OFFSET = 16;
347
- var activeTooltipClose;
348
- function clampFloating(value, min, max) {
349
- return Math.min(Math.max(value, min), Math.max(min, max));
350
- }
351
- function resolveFloatingPosition(trigger, overlay, placement) {
352
- const viewportWidth = document.documentElement.clientWidth;
353
- const viewportHeight = document.documentElement.clientHeight;
354
- let resolvedPlacement = placement;
355
- if (placement.startsWith("top") && trigger.top - overlay.height - FLOATING_GAP < FLOATING_VIEWPORT_PADDING && trigger.bottom + overlay.height + FLOATING_GAP <= viewportHeight - FLOATING_VIEWPORT_PADDING) {
356
- resolvedPlacement = placement.replace("top", "bottom");
357
- } else if (placement.startsWith("bottom") && trigger.bottom + overlay.height + FLOATING_GAP > viewportHeight - FLOATING_VIEWPORT_PADDING && trigger.top - overlay.height - FLOATING_GAP >= FLOATING_VIEWPORT_PADDING) {
358
- resolvedPlacement = placement.replace("bottom", "top");
359
- } else if (placement === "left" && trigger.left - overlay.width - FLOATING_GAP < FLOATING_VIEWPORT_PADDING && trigger.right + overlay.width + FLOATING_GAP <= viewportWidth - FLOATING_VIEWPORT_PADDING) {
360
- resolvedPlacement = "right";
361
- } else if (placement === "right" && trigger.right + overlay.width + FLOATING_GAP > viewportWidth - FLOATING_VIEWPORT_PADDING && trigger.left - overlay.width - FLOATING_GAP >= FLOATING_VIEWPORT_PADDING) {
362
- resolvedPlacement = "left";
363
- }
364
- let top = trigger.top;
365
- let left = trigger.left;
366
- if (resolvedPlacement.startsWith("top") || resolvedPlacement.startsWith("bottom")) {
367
- top = resolvedPlacement.startsWith("top") ? trigger.top - overlay.height - FLOATING_GAP : trigger.bottom + FLOATING_GAP;
368
- left = resolvedPlacement.endsWith("-start") ? trigger.left : resolvedPlacement.endsWith("-end") ? trigger.right - overlay.width : trigger.left + (trigger.width - overlay.width) / 2;
369
- } else {
370
- top = trigger.top + (trigger.height - overlay.height) / 2;
371
- left = resolvedPlacement === "left" ? trigger.left - overlay.width - FLOATING_GAP : trigger.right + FLOATING_GAP;
372
- }
373
- top = clampFloating(top, FLOATING_VIEWPORT_PADDING, viewportHeight - overlay.height - FLOATING_VIEWPORT_PADDING);
374
- left = clampFloating(left, FLOATING_VIEWPORT_PADDING, viewportWidth - overlay.width - FLOATING_VIEWPORT_PADDING);
375
- const result = { top, left, placement: resolvedPlacement };
376
- if (resolvedPlacement.startsWith("top") || resolvedPlacement.startsWith("bottom")) {
377
- 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);
378
- } else {
379
- result.arrowY = clampFloating(trigger.top + trigger.height / 2 - top - 4, 8, overlay.height - 16);
380
- }
381
- return result;
382
- }
383
- function Floating({
384
- children,
385
- content,
386
- placement = "top",
387
- trigger = "hover",
388
- open,
389
- defaultOpen = false,
390
- mouseEnterDelay = 0.1,
391
- mouseLeaveDelay = 0.1,
392
- arrow = true,
393
- color,
394
- className = "",
395
- overlayClassName = "",
396
- role = "tooltip",
397
- onOpenChange
398
- }) {
399
- const rootRef = useRef2(null);
400
- const overlayRef = useRef2(null);
401
- const enterTimer = useRef2();
402
- const leaveTimer = useRef2();
403
- const contentId = useId3();
404
- const triggers = Array.isArray(trigger) ? trigger : [trigger];
405
- const [visible, setVisible] = useControllableState({ value: open, defaultValue: defaultOpen, onChange: onOpenChange });
406
- const [position, setPosition] = useState3();
407
- const setVisibleRef = useRef2(setVisible);
408
- setVisibleRef.current = setVisible;
409
- const closeSelfRef = useRef2();
410
- if (!closeSelfRef.current) closeSelfRef.current = () => setVisibleRef.current(false);
411
- const exclusiveTooltip = role === "tooltip" && triggers.includes("hover");
412
- function openNow() {
413
- if (exclusiveTooltip && activeTooltipClose !== closeSelfRef.current) {
414
- activeTooltipClose?.();
415
- activeTooltipClose = closeSelfRef.current;
416
- }
417
- setVisible(true);
418
- }
419
- function closeNow() {
420
- if (activeTooltipClose === closeSelfRef.current) activeTooltipClose = void 0;
421
- setVisible(false);
422
- }
423
- function clearTimers() {
424
- window.clearTimeout(enterTimer.current);
425
- window.clearTimeout(leaveTimer.current);
426
- }
427
- function show(delay = 0) {
428
- clearTimers();
429
- if (delay <= 0) openNow();
430
- else enterTimer.current = window.setTimeout(openNow, delay * 1e3);
431
- }
432
- function hide(delay = 0) {
433
- clearTimers();
434
- if (delay <= 0) closeNow();
435
- else leaveTimer.current = window.setTimeout(closeNow, delay * 1e3);
436
- }
437
- useEffect2(() => {
438
- if (!visible || !triggers.includes("click")) return;
439
- const close = (event) => {
440
- const target = event.target;
441
- if (!rootRef.current?.contains(target) && !overlayRef.current?.contains(target)) closeNow();
442
- };
443
- document.addEventListener("pointerdown", close);
444
- return () => document.removeEventListener("pointerdown", close);
445
- }, [triggers.join("|"), visible]);
446
- useLayoutEffect(() => {
447
- if (!visible) {
448
- setPosition(void 0);
449
- return;
450
- }
451
- const update = () => {
452
- if (!rootRef.current || !overlayRef.current) return;
453
- setPosition(resolveFloatingPosition(rootRef.current.getBoundingClientRect(), overlayRef.current.getBoundingClientRect(), placement));
454
- };
455
- update();
456
- window.addEventListener("resize", update);
457
- window.addEventListener("scroll", update, true);
458
- const observer = new ResizeObserver(update);
459
- if (rootRef.current) observer.observe(rootRef.current);
460
- if (overlayRef.current) observer.observe(overlayRef.current);
461
- return () => {
462
- window.removeEventListener("resize", update);
463
- window.removeEventListener("scroll", update, true);
464
- observer.disconnect();
465
- };
466
- }, [children, content, placement, visible]);
467
- useEffect2(() => () => {
468
- clearTimers();
469
- if (activeTooltipClose === closeSelfRef.current) activeTooltipClose = void 0;
470
- }, []);
471
- const child = cloneElement(children, {
472
- ...triggers.includes("hover") ? { onMouseEnter: (event) => {
473
- children.props.onMouseEnter?.(event);
474
- show(mouseEnterDelay);
475
- }, onMouseLeave: (event) => {
476
- children.props.onMouseLeave?.(event);
477
- hide(mouseLeaveDelay);
478
- } } : {},
479
- ...triggers.includes("focus") ? { onFocus: (event) => {
480
- children.props.onFocus?.(event);
481
- show();
482
- }, onBlur: (event) => {
483
- children.props.onBlur?.(event);
484
- hide();
485
- } } : {},
486
- ...triggers.includes("click") ? { onClick: (event) => {
487
- children.props.onClick?.(event);
488
- if (visible) closeNow();
489
- else openNow();
490
- } } : {},
491
- "aria-describedby": visible ? contentId : void 0
492
- });
493
- const overlay = visible && content !== null && content !== void 0 && typeof document !== "undefined" ? createPortal(
494
- /* @__PURE__ */ jsxs5(
495
- "span",
496
- {
497
- ref: overlayRef,
498
- id: contentId,
499
- role,
500
- "data-custom-color": color ? "" : void 0,
501
- className: `sia-floating__overlay sia-floating__overlay--${position?.placement ?? placement} ${overlayClassName}`.trim(),
502
- style: {
503
- "--sia-floating-color": color,
504
- "--sia-floating-arrow-x": position?.arrowX == null ? void 0 : `${position.arrowX}px`,
505
- "--sia-floating-arrow-y": position?.arrowY == null ? void 0 : `${position.arrowY}px`,
506
- top: position?.top ?? 0,
507
- left: position?.left ?? 0,
508
- visibility: position ? "visible" : "hidden"
509
- },
510
- onMouseEnter: triggers.includes("hover") ? () => show() : void 0,
511
- onMouseLeave: triggers.includes("hover") ? () => hide(mouseLeaveDelay) : void 0,
512
- children: [
513
- content,
514
- arrow ? /* @__PURE__ */ jsx5("span", { className: "sia-floating__arrow" }) : null
515
- ]
516
- }
517
- ),
518
- document.body
519
- ) : null;
520
- return /* @__PURE__ */ jsxs5(Fragment2, { children: [
521
- /* @__PURE__ */ jsx5("span", { ref: rootRef, className: `sia-floating ${className}`.trim(), children: child }),
522
- overlay
523
- ] });
524
- }
525
- function Tooltip(props) {
526
- return /* @__PURE__ */ jsx5(Floating, { ...props, content: props.title });
527
- }
528
- function Popover({ title, content, trigger = "click", overlayClassName = "", ...props }) {
529
- return /* @__PURE__ */ jsx5(Floating, { ...props, trigger, role: "dialog", overlayClassName: `sia-popover ${overlayClassName}`.trim(), content: /* @__PURE__ */ jsxs5(Fragment2, { children: [
530
- title ? /* @__PURE__ */ jsx5("strong", { className: "sia-popover__title", children: title }) : null,
531
- /* @__PURE__ */ jsx5("span", { className: "sia-popover__content", children: content })
532
- ] }) });
533
- }
534
- function Timeline({ items = [], mode = "left", pending = false, pendingDot, reverse = false, className = "", ...props }) {
535
- const values = [...items, ...pending ? [{ children: pending === true ? "\u52A0\u8F7D\u4E2D..." : pending, dot: pendingDot ?? /* @__PURE__ */ jsx5(Icon, { name: "loader", className: "sia-spin-icon" }) }] : []];
536
- if (reverse) values.reverse();
537
- return /* @__PURE__ */ jsx5("ul", { className: `sia-timeline sia-timeline--${mode} ${className}`.trim(), ...props, children: values.map((item, index) => {
538
- const position = item.position ?? (mode === "alternate" ? index % 2 ? "right" : "left" : mode);
539
- return /* @__PURE__ */ jsxs5("li", { className: `sia-timeline__item sia-timeline__item--${position}`, children: [
540
- item.label ? /* @__PURE__ */ jsx5("span", { className: "sia-timeline__label", children: item.label }) : null,
541
- /* @__PURE__ */ jsx5("span", { className: "sia-timeline__rail", children: /* @__PURE__ */ jsx5("span", { className: "sia-timeline__dot", style: { color: item.color && !["blue", "red", "green", "gray"].includes(item.color) ? item.color : void 0 }, "data-color": item.color ?? "blue", children: item.dot }) }),
542
- /* @__PURE__ */ jsx5("span", { className: "sia-timeline__content", children: item.children })
543
- ] }, index);
544
- }) });
545
- }
546
- function collectKeys(nodes, branchOnly = false, result = []) {
547
- nodes.forEach((node) => {
548
- if (!branchOnly || node.children?.length) result.push(node.key);
549
- if (node.children) collectKeys(node.children, branchOnly, result);
550
- });
551
- return result;
552
- }
553
- function descendants(node) {
554
- return node.children ? collectKeys(node.children) : [];
555
- }
556
- function Tree({
557
- treeData,
558
- selectedKeys,
559
- defaultSelectedKeys = [],
560
- expandedKeys,
561
- defaultExpandedKeys = [],
562
- defaultExpandAll = false,
563
- checkedKeys,
564
- defaultCheckedKeys = [],
565
- loadingKeys = [],
566
- checkable = false,
567
- checkStrictly = false,
568
- multiple = false,
569
- selectable = true,
570
- showLine = false,
571
- showIcon = false,
572
- blockNode = false,
573
- indentSize = 20,
574
- switcherWidth = 24,
575
- stickyAncestors = false,
576
- stickyRowHeight = 34,
577
- draggable = false,
578
- loadData,
579
- onSelect,
580
- onCheck,
581
- onExpand,
582
- onDrop,
583
- className = "",
584
- ...props
585
- }) {
586
- const [selected, setSelected] = useControllableState({ value: selectedKeys, defaultValue: defaultSelectedKeys });
587
- const [expanded, setExpanded] = useControllableState({ value: expandedKeys, defaultValue: defaultExpandAll ? collectKeys(treeData, true) : defaultExpandedKeys });
588
- const [checked, setChecked] = useControllableState({ value: checkedKeys, defaultValue: defaultCheckedKeys });
589
- const [loading, setLoading] = useState3(/* @__PURE__ */ new Set());
590
- const controlledLoading = useMemo3(() => new Set(loadingKeys), [loadingKeys]);
591
- const selectedKeySet = useMemo3(() => new Set(selected), [selected]);
592
- const selectedAncestorKeys = useMemo3(() => {
593
- const ancestors = /* @__PURE__ */ new Set();
594
- const visit = (nodes, parentKeys) => {
595
- nodes.forEach((node) => {
596
- if (selectedKeySet.has(node.key)) parentKeys.forEach((key) => ancestors.add(key));
597
- if (node.children?.length) visit(node.children, [...parentKeys, node.key]);
598
- });
599
- };
600
- visit(treeData, []);
601
- return ancestors;
602
- }, [selectedKeySet, treeData]);
603
- const dragNodeRef = useRef2();
604
- const pointerDragRef = useRef2();
605
- const suppressClickRef = useRef2(false);
606
- const [dragOver, setDragOver] = useState3();
607
- const normalizedSwitcherWidth = Math.max(0, switcherWidth);
608
- async function toggleExpand(node) {
609
- const nextExpanded = !expanded.includes(node.key);
610
- if (nextExpanded && node.loaded !== true && !node.children?.length && node.isLeaf === false && loadData) {
611
- setLoading((keys) => new Set(keys).add(node.key));
612
- try {
613
- await loadData(node);
614
- } finally {
615
- setLoading((keys) => {
616
- const next2 = new Set(keys);
617
- next2.delete(node.key);
618
- return next2;
619
- });
620
- }
621
- }
622
- const next = nextExpanded ? [...expanded, node.key] : expanded.filter((key) => key !== node.key);
623
- setExpanded(next);
624
- onExpand?.([...next], { expanded: nextExpanded, node });
625
- }
626
- function check(node) {
627
- const nodeChecked = checked.includes(node.key);
628
- const affected = checkStrictly ? [node.key] : [node.key, ...descendants(node)];
629
- const next = nodeChecked ? checked.filter((key) => !affected.includes(key)) : [.../* @__PURE__ */ new Set([...checked, ...affected])];
630
- setChecked(next);
631
- onCheck?.([...next], { checked: !nodeChecked, node });
632
- }
633
- function select(node, event) {
634
- if (!selectable || node.selectable === false || node.disabled) return;
635
- const exists = selected.includes(node.key);
636
- const next = multiple ? exists ? selected.filter((key) => key !== node.key) : [...selected, node.key] : exists ? [] : [node.key];
637
- setSelected(next);
638
- onSelect?.([...next], { selected: !exists, node, nativeEvent: event });
639
- }
640
- function selectForContextMenu(node, event) {
641
- if (!selectable || node.selectable === false || node.disabled || selected.includes(node.key)) return;
642
- const next = [node.key];
643
- setSelected(next);
644
- onSelect?.(next, { selected: true, node, nativeEvent: event });
645
- }
646
- function findNode(key, nodes = treeData) {
647
- for (const node of nodes) {
648
- if (String(node.key) === key) return node;
649
- const child = node.children?.length ? findNode(key, node.children) : void 0;
650
- if (child) return child;
651
- }
652
- return void 0;
653
- }
654
- function getPointerDropTarget(clientX, clientY, dragNode) {
655
- const targetElement = document.elementFromPoint(clientX, clientY)?.closest("[data-sia-tree-drop-key]");
656
- if (!targetElement) return void 0;
657
- const targetNode = targetElement?.dataset.siaTreeDropKey ? findNode(targetElement.dataset.siaTreeDropKey) : void 0;
658
- if (!targetNode || targetNode.key === dragNode.key || targetNode.disabled || targetNode.droppable === false) return void 0;
659
- const bounds = targetElement.getBoundingClientRect();
660
- const relativeY = bounds.height > 0 ? (clientY - bounds.top) / bounds.height : 0.5;
661
- const dropPosition = relativeY < 0.25 ? "before" : relativeY > 0.75 ? "after" : "inside";
662
- return { node: targetNode, dropPosition };
663
- }
664
- function beginPointerDrag(node, event) {
665
- if (event.button !== 0) return;
666
- pointerDragRef.current = {
667
- node,
668
- pointerId: event.pointerId,
669
- startX: event.clientX,
670
- startY: event.clientY,
671
- active: false
672
- };
673
- event.currentTarget.setPointerCapture(event.pointerId);
674
- }
675
- function movePointerDrag(event) {
676
- const pointerDrag = pointerDragRef.current;
677
- if (!pointerDrag || pointerDrag.pointerId !== event.pointerId) return;
678
- if (!pointerDrag.active && Math.hypot(event.clientX - pointerDrag.startX, event.clientY - pointerDrag.startY) < 5) return;
679
- pointerDrag.active = true;
680
- dragNodeRef.current = pointerDrag.node;
681
- event.preventDefault();
682
- const target = getPointerDropTarget(event.clientX, event.clientY, pointerDrag.node);
683
- setDragOver(target ? { key: target.node.key, position: target.dropPosition } : void 0);
684
- }
685
- function finishPointerDrag(event) {
686
- const pointerDrag = pointerDragRef.current;
687
- if (!pointerDrag || pointerDrag.pointerId !== event.pointerId) return;
688
- if (event.currentTarget.hasPointerCapture(event.pointerId)) event.currentTarget.releasePointerCapture(event.pointerId);
689
- if (pointerDrag.active) {
690
- event.preventDefault();
691
- suppressClickRef.current = true;
692
- const target = getPointerDropTarget(event.clientX, event.clientY, pointerDrag.node);
693
- if (target) onDrop?.({ dragNode: pointerDrag.node, node: target.node, dropPosition: target.dropPosition });
694
- window.setTimeout(() => {
695
- suppressClickRef.current = false;
696
- }, 0);
697
- }
698
- pointerDragRef.current = void 0;
699
- dragNodeRef.current = void 0;
700
- setDragOver(void 0);
701
- }
702
- function cancelPointerDrag(event) {
703
- const pointerDrag = pointerDragRef.current;
704
- if (!pointerDrag || pointerDrag.pointerId !== event.pointerId) return;
705
- pointerDragRef.current = void 0;
706
- dragNodeRef.current = void 0;
707
- setDragOver(void 0);
708
- }
709
- function render(nodes, depth = 0) {
710
- return nodes.map((node) => {
711
- const open = expanded.includes(node.key);
712
- const isSelected = selected.includes(node.key);
713
- const isSelectedAncestor = selectedAncestorKeys.has(node.key);
714
- const isChecked = checked.includes(node.key);
715
- const hasChildren = Boolean(node.children?.length || node.isLeaf === false);
716
- const nodeLoading = loading.has(node.key) || controlledLoading.has(node.key);
717
- const sticky = stickyAncestors && hasChildren && open;
718
- const canDrag = draggable && node.draggable !== false && !node.disabled;
719
- const { className: rowClassName = "", style: rowStyle, onClick: onRowClick, ...rowProps } = node.rowProps ?? {};
720
- const unloaded = hasChildren && node.loaded === false;
721
- const dropPosition = dragOver?.key === node.key ? dragOver.position : void 0;
722
- return /* @__PURE__ */ jsxs5("div", { className: `sia-tree__node${isSelected ? " is-selected" : ""}${isSelectedAncestor ? " is-selected-ancestor" : ""}${dropPosition ? ` is-drag-over-${dropPosition}` : ""}${node.disabled ? " is-disabled" : ""}${unloaded ? " is-unloaded" : ""}`, role: "treeitem", "aria-expanded": hasChildren ? open : void 0, "aria-selected": isSelected, children: [
723
- /* @__PURE__ */ jsx5(
724
- "div",
725
- {
726
- ...rowProps,
727
- className: `sia-tree__row${blockNode ? " sia-tree__row--block" : ""}${sticky ? " sia-tree__row--sticky" : ""} ${rowClassName}`.trim(),
728
- style: {
729
- ...rowStyle,
730
- paddingInlineStart: depth * Math.max(0, indentSize),
731
- top: sticky ? depth * Math.max(1, stickyRowHeight) : rowStyle?.top,
732
- zIndex: sticky ? Math.max(1, 100 - depth) : rowStyle?.zIndex
733
- },
734
- "aria-busy": nodeLoading || void 0,
735
- "data-sia-tree-drop-key": String(node.key),
736
- "data-sia-tree-draggable": canDrag || void 0,
737
- onClick: (event) => {
738
- onRowClick?.(event);
739
- if (event.defaultPrevented || suppressClickRef.current || node.disabled) return;
740
- select(node, event);
741
- if (hasChildren) void toggleExpand(node);
742
- },
743
- children: (() => {
744
- const content = /* @__PURE__ */ jsxs5("span", { className: "sia-tree__context-content", children: [
745
- hasChildren ? /* @__PURE__ */ jsx5("button", { type: "button", className: "sia-tree__switcher", "aria-label": open ? "\u6536\u8D77\u8282\u70B9" : "\u5C55\u5F00\u8282\u70B9", "aria-expanded": open, style: { width: normalizedSwitcherWidth, flexBasis: normalizedSwitcherWidth }, disabled: node.disabled, onClick: (event) => {
746
- event.stopPropagation();
747
- void toggleExpand(node);
748
- }, children: nodeLoading ? /* @__PURE__ */ jsx5(Icon, { name: "loader", className: "sia-spin-icon", size: 13 }) : /* @__PURE__ */ jsx5(Icon, { name: open ? "chevron-down" : "chevron-right", size: 13 }) }) : /* @__PURE__ */ jsx5("span", { className: "sia-tree__switcher", "aria-label": open ? "\u6536\u8D77\u8282\u70B9" : "\u5C55\u5F00\u8282\u70B9", "aria-expanded": open, style: { width: normalizedSwitcherWidth, flexBasis: normalizedSwitcherWidth }, "aria-hidden": "true" }),
749
- checkable || node.checkable ? /* @__PURE__ */ jsx5("span", { onClick: (event) => event.stopPropagation(), children: /* @__PURE__ */ jsx5(Checkbox2, { checked: isChecked, disabled: node.disabled || node.disableCheckbox, onChange: () => check(node) }) }) : null,
750
- node.prefix ? /* @__PURE__ */ jsx5("span", { className: "sia-tree__prefix", onClick: (event) => event.stopPropagation(), children: node.prefix }) : null,
751
- showIcon ? /* @__PURE__ */ jsx5("span", { className: "sia-tree__icon", children: node.icon ?? /* @__PURE__ */ jsx5(Icon, { name: hasChildren ? "folder" : "file", size: 15 }) }) : null,
752
- /* @__PURE__ */ jsx5(
753
- "button",
754
- {
755
- type: "button",
756
- className: "sia-tree__title",
757
- disabled: node.disabled,
758
- onPointerDown: canDrag ? (event) => beginPointerDrag(node, event) : void 0,
759
- onPointerMove: canDrag ? movePointerDrag : void 0,
760
- onPointerUp: canDrag ? finishPointerDrag : void 0,
761
- onPointerCancel: canDrag ? cancelPointerDrag : void 0,
762
- children: node.title
763
- }
764
- ),
765
- nodeLoading ? /* @__PURE__ */ jsx5("span", { className: "sia-tree__loading-text", role: "status", children: node.loadingText ?? "\u6B63\u5728\u52A0\u8F7D\u2026" }) : null,
766
- node.extra ? /* @__PURE__ */ jsx5("span", { className: "sia-tree__extra", onClick: (event) => event.stopPropagation(), children: node.extra }) : null
767
- ] });
768
- return node.contextMenu ? /* @__PURE__ */ jsx5(
769
- Dropdown,
770
- {
771
- className: "sia-tree__context-trigger",
772
- popupClassName: node.contextMenuPopupClassName,
773
- trigger: ["contextMenu"],
774
- menu: node.contextMenu,
775
- onContextMenu: (event) => selectForContextMenu(node, event),
776
- children: content
777
- }
778
- ) : content;
779
- })()
780
- }
781
- ),
782
- open && node.children?.length ? /* @__PURE__ */ jsx5("div", { className: "sia-tree__children", role: "group", children: render(node.children, depth + 1) }) : null
783
- ] }, node.key);
784
- });
785
- }
786
- return /* @__PURE__ */ jsx5("div", { className: `sia-tree${showLine ? " sia-tree--line" : ""}${stickyAncestors ? " sia-tree--sticky-ancestors" : ""} ${className}`.trim(), role: "tree", "aria-multiselectable": multiple || void 0, ...props, children: render(treeData) });
787
- }
788
-
789
- // src/components/Feedback.tsx
790
- import { useEffect as useEffect3, useMemo as useMemo4, useState as useState4 } from "react";
791
- import { createPortal as createPortal2 } from "react-dom";
792
- import { createRoot } from "react-dom/client";
793
- import { Fragment as Fragment3, jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
794
- function Popconfirm({
795
- children,
796
- title,
797
- description,
798
- okText = "\u786E\u5B9A",
799
- cancelText = "\u53D6\u6D88",
800
- okButtonProps,
801
- cancelButtonProps,
802
- placement = "top",
803
- trigger = "click",
804
- open,
805
- defaultOpen = false,
806
- disabled = false,
807
- showCancel = true,
808
- icon = /* @__PURE__ */ jsx6(Icon, { name: "question", variant: "filled" }),
809
- onConfirm,
810
- onCancel,
811
- onOpenChange
812
- }) {
813
- const [visible, setVisible] = useControllableState({ value: open, defaultValue: defaultOpen, onChange: onOpenChange });
814
- const [loading, setLoading] = useState4(false);
815
- const content = /* @__PURE__ */ jsxs6("div", { className: "sia-popconfirm", children: [
816
- /* @__PURE__ */ jsxs6("div", { className: "sia-popconfirm__message", children: [
817
- /* @__PURE__ */ jsx6("span", { children: icon }),
818
- /* @__PURE__ */ jsxs6("div", { children: [
819
- /* @__PURE__ */ jsx6("strong", { children: title }),
820
- description ? /* @__PURE__ */ jsx6("p", { children: description }) : null
821
- ] })
822
- ] }),
823
- /* @__PURE__ */ jsxs6("div", { className: "sia-popconfirm__actions", children: [
824
- showCancel ? /* @__PURE__ */ jsx6(Button, { size: "small", ...cancelButtonProps, onClick: (event) => {
825
- cancelButtonProps?.onClick?.(event);
826
- onCancel?.(event);
827
- setVisible(false);
828
- }, children: cancelText }) : null,
829
- /* @__PURE__ */ jsx6(Button, { size: "small", variant: "primary", ...okButtonProps, loading: loading || okButtonProps?.loading, onClick: async (event) => {
830
- okButtonProps?.onClick?.(event);
831
- const result = onConfirm?.(event);
832
- if (result instanceof Promise) {
833
- setLoading(true);
834
- try {
835
- if (await result !== false) setVisible(false);
836
- } finally {
837
- setLoading(false);
838
- }
839
- } else if (result !== false) setVisible(false);
840
- }, children: okText })
841
- ] })
842
- ] });
843
- return /* @__PURE__ */ jsx6(Popover, { content, placement, trigger: disabled ? [] : trigger, open: visible, onOpenChange: setVisible, children });
844
- }
845
- function Progress({
846
- percent = 0,
847
- type = "line",
848
- status,
849
- showInfo = true,
850
- format,
851
- strokeColor,
852
- trailColor,
853
- strokeWidth = 8,
854
- width = 120,
855
- steps,
856
- success,
857
- size = "default",
858
- className = "",
859
- style,
860
- ...props
861
- }) {
862
- const safe = Math.max(0, Math.min(100, percent));
863
- const resolved = status ?? (safe >= 100 ? "success" : "normal");
864
- const color = typeof strokeColor === "string" ? strokeColor : void 0;
865
- const background = typeof strokeColor === "object" ? `linear-gradient(90deg, ${strokeColor.from}, ${strokeColor.to})` : color;
866
- const label = format?.(safe, success?.percent) ?? (resolved === "exception" ? /* @__PURE__ */ jsx6(Icon, { name: "close" }) : resolved === "success" ? /* @__PURE__ */ jsx6(Icon, { name: "check" }) : `${safe}%`);
867
- if (type !== "line") {
868
- const radius = 46;
869
- const circumference = 2 * Math.PI * radius;
870
- const dash = circumference * (safe / 100);
871
- return /* @__PURE__ */ jsxs6("div", { className: `sia-progress sia-progress--${type} sia-progress--${resolved} ${className}`.trim(), style: { width, height: width, ...style }, ...props, children: [
872
- /* @__PURE__ */ jsxs6("svg", { viewBox: "0 0 100 100", role: "progressbar", "aria-valuenow": safe, children: [
873
- /* @__PURE__ */ jsx6("circle", { className: "sia-progress__trail", cx: "50", cy: "50", r: radius, style: { stroke: trailColor } }),
874
- /* @__PURE__ */ jsx6("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 } })
875
- ] }),
876
- showInfo ? /* @__PURE__ */ jsx6("span", { className: "sia-progress__text", children: label }) : null
877
- ] });
878
- }
879
- const height = Array.isArray(size) ? size[1] : typeof size === "number" ? size : size === "small" ? 6 : strokeWidth;
880
- return /* @__PURE__ */ jsxs6("div", { className: `sia-progress sia-progress--line sia-progress--${resolved} ${className}`.trim(), style, ...props, children: [
881
- /* @__PURE__ */ jsx6("div", { className: "sia-progress__outer", style: { height, backgroundColor: trailColor }, children: steps ? /* @__PURE__ */ jsx6("div", { className: "sia-progress__steps", children: Array.from({ length: steps }, (_, index) => /* @__PURE__ */ jsx6("span", { className: index < Math.round(steps * safe / 100) ? "is-active" : "", style: { background: index < Math.round(steps * safe / 100) ? background : trailColor } }, index)) }) : /* @__PURE__ */ jsxs6(Fragment3, { children: [
882
- /* @__PURE__ */ jsx6("span", { className: "sia-progress__bar", style: { width: `${safe}%`, background } }),
883
- success?.percent ? /* @__PURE__ */ jsx6("span", { className: "sia-progress__success", style: { width: `${success.percent}%`, background: success.strokeColor } }) : null
884
- ] }) }),
885
- showInfo ? /* @__PURE__ */ jsx6("span", { className: "sia-progress__info", children: label }) : null
886
- ] });
887
- }
888
- function Spin({ spinning = true, size = "default", tip, indicator, delay = 0, fullscreen = false, className = "", children, ...props }) {
889
- const [visible, setVisible] = useState4(delay === 0 && spinning);
890
- useEffect3(() => {
891
- if (!spinning) {
892
- setVisible(false);
893
- return;
894
- }
895
- const timer = window.setTimeout(() => setVisible(true), delay);
896
- return () => window.clearTimeout(timer);
897
- }, [delay, spinning]);
898
- const spinner = visible ? /* @__PURE__ */ jsxs6("div", { className: `sia-spin sia-spin--${size}`, role: "status", "aria-live": "polite", children: [
899
- indicator ?? /* @__PURE__ */ jsxs6("span", { className: "sia-spin__indicator", children: [
900
- /* @__PURE__ */ jsx6("i", {}),
901
- /* @__PURE__ */ jsx6("i", {}),
902
- /* @__PURE__ */ jsx6("i", {}),
903
- /* @__PURE__ */ jsx6("i", {})
904
- ] }),
905
- tip ? /* @__PURE__ */ jsx6("span", { className: "sia-spin__tip", children: tip }) : null
906
- ] }) : null;
907
- if (fullscreen) return visible && typeof document !== "undefined" ? createPortal2(/* @__PURE__ */ jsx6("div", { className: "sia-spin-fullscreen", children: spinner }), document.body) : null;
908
- if (!children) return /* @__PURE__ */ jsx6("div", { className: `sia-spin-wrap ${className}`.trim(), ...props, children: spinner });
909
- return /* @__PURE__ */ jsxs6("div", { className: `sia-spin-container${visible ? " is-spinning" : ""} ${className}`.trim(), ...props, children: [
910
- children,
911
- visible ? /* @__PURE__ */ jsx6("div", { className: "sia-spin-mask", children: spinner }) : null
912
- ] });
913
- }
914
- function escapeXml(value) {
915
- return value.replace(/[&<>"']/g, (char) => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;", "'": "&apos;" })[char]);
916
- }
917
- function Watermark({ content = "Sia Web UI", image, width = 120, height = 64, rotate = -22, zIndex = 9, gap = [100, 100], offset = [0, 0], font, className = "", children, ...props }) {
918
- const background = useMemo4(() => {
919
- const tileWidth = width + gap[0], tileHeight = height + gap[1];
920
- const lines = Array.isArray(content) ? content : [content];
921
- const family = font?.fontFamily ?? "sans-serif", size = font?.fontSize ?? 16, weight = font?.fontWeight ?? 400, color = font?.color ?? "rgba(0,0,0,.15)";
922
- const body = image ? `<image href="${escapeXml(image)}" x="0" y="0" width="${width}" height="${height}"/>` : lines.map((line, index) => `<text x="${width / 2}" y="${height / 2 + (index - (lines.length - 1) / 2) * (size + 5)}" text-anchor="middle" dominant-baseline="middle" fill="${color}" font-size="${size}" font-weight="${weight}" font-family="${family}">${escapeXml(line)}</text>`).join("");
923
- const svg = `<svg xmlns="http://www.w3.org/2000/svg" width="${tileWidth}" height="${tileHeight}"><g transform="translate(${offset[0]} ${offset[1]}) rotate(${rotate} ${width / 2} ${height / 2})">${body}</g></svg>`;
924
- return `url("data:image/svg+xml,${encodeURIComponent(svg)}")`;
925
- }, [content, font?.color, font?.fontFamily, font?.fontSize, font?.fontWeight, gap[0], gap[1], height, image, offset[0], offset[1], rotate, width]);
926
- return /* @__PURE__ */ jsxs6("div", { className: `sia-watermark ${className}`.trim(), ...props, children: [
927
- children,
928
- /* @__PURE__ */ jsx6("div", { className: "sia-watermark__layer", "aria-hidden": "true", style: { zIndex, backgroundImage: background } })
929
- ] });
930
- }
931
- var notificationListeners = /* @__PURE__ */ new Set();
932
- var notificationEntries = [];
933
- var notificationSeed = 0;
934
- var notificationHost = null;
935
- var notificationTimers = /* @__PURE__ */ new Map();
936
- function emitNotifications() {
937
- notificationListeners.forEach((listener) => listener([...notificationEntries]));
938
- }
939
- function closeNotification(key) {
940
- const entry = notificationEntries.find((item) => item.key === key);
941
- notificationEntries = notificationEntries.filter((item) => item.key !== key);
942
- window.clearTimeout(notificationTimers.get(key));
943
- notificationTimers.delete(key);
944
- emitNotifications();
945
- entry?.onClose?.();
946
- }
947
- function NotificationHost() {
948
- const [entries, setEntries] = useState4(notificationEntries);
949
- useEffect3(() => {
950
- notificationListeners.add(setEntries);
951
- return () => {
952
- notificationListeners.delete(setEntries);
953
- };
954
- }, []);
955
- const placements = ["topLeft", "topRight", "bottomLeft", "bottomRight"];
956
- return /* @__PURE__ */ jsx6(Fragment3, { children: placements.map((placement) => /* @__PURE__ */ jsx6("div", { className: `sia-notification sia-notification--${placement}`, children: entries.filter((item) => (item.placement ?? "topRight") === placement).map((item) => /* @__PURE__ */ jsxs6("div", { className: `sia-notification__notice sia-notification__notice--${item.type ?? "info"}`, role: item.role ?? "alert", onClick: item.onClick, children: [
957
- /* @__PURE__ */ jsx6("span", { className: "sia-notification__icon", children: item.icon ?? /* @__PURE__ */ jsx6(Icon, { name: item.type === "success" ? "circle-check" : item.type === "warning" ? "warning" : item.type === "error" ? "circle-close" : "info", variant: "filled" }) }),
958
- /* @__PURE__ */ jsxs6("div", { children: [
959
- /* @__PURE__ */ jsx6("strong", { children: item.message }),
960
- item.description ? /* @__PURE__ */ jsx6("p", { children: item.description }) : null,
961
- item.btn
962
- ] }),
963
- /* @__PURE__ */ jsx6("button", { type: "button", "aria-label": "\u5173\u95ED\u901A\u77E5", onClick: (event) => {
964
- event.stopPropagation();
965
- closeNotification(item.key);
966
- }, children: item.closeIcon ?? /* @__PURE__ */ jsx6(Icon, { name: "close", size: 14 }) })
967
- ] }, item.key)) }, placement)) });
968
- }
969
- function ensureNotificationHost() {
970
- if (typeof document === "undefined" || notificationHost) return;
971
- notificationHost = document.createElement("div");
972
- notificationHost.dataset.siaNotificationHost = "";
973
- document.body.appendChild(notificationHost);
974
- createRoot(notificationHost).render(/* @__PURE__ */ jsx6(NotificationHost, {}));
975
- }
976
- function openNotification(config) {
977
- if (typeof window === "undefined" || typeof document === "undefined") return { key: config.key ?? "ssr", close: () => void 0 };
978
- ensureNotificationHost();
979
- const key = config.key ?? `sia-notification-${++notificationSeed}`;
980
- const entry = { ...config, key };
981
- const exists = notificationEntries.some((item) => item.key === key);
982
- notificationEntries = exists ? notificationEntries.map((item) => item.key === key ? entry : item) : [...notificationEntries, entry];
983
- emitNotifications();
984
- window.clearTimeout(notificationTimers.get(key));
985
- if ((config.duration ?? 4.5) > 0) notificationTimers.set(key, window.setTimeout(() => closeNotification(key), (config.duration ?? 4.5) * 1e3));
986
- return { key, close: () => closeNotification(key) };
987
- }
988
- var notification = {
989
- open: openNotification,
990
- info: (config) => openNotification({ ...config, type: "info" }),
991
- success: (config) => openNotification({ ...config, type: "success" }),
992
- warning: (config) => openNotification({ ...config, type: "warning" }),
993
- error: (config) => openNotification({ ...config, type: "error" }),
994
- destroy: (key) => {
995
- if (key !== void 0) closeNotification(key);
996
- else {
997
- [...notificationEntries].forEach((item) => closeNotification(item.key));
998
- }
999
- }
1000
- };
1001
- function Tour({ open = false, steps, current, defaultCurrent = 0, mask = true, closable = true, disabledInteraction = false, onChange, onClose, onFinish }) {
1002
- const [index, setIndex] = useControllableState({ value: current, defaultValue: defaultCurrent, onChange });
1003
- const [, force] = useState4(0);
1004
- useEffect3(() => {
1005
- if (!open) return;
1006
- const update = () => force((value) => value + 1);
1007
- window.addEventListener("resize", update);
1008
- window.addEventListener("scroll", update, true);
1009
- return () => {
1010
- window.removeEventListener("resize", update);
1011
- window.removeEventListener("scroll", update, true);
1012
- };
1013
- }, [open]);
1014
- if (!open || !steps[index] || typeof document === "undefined") return null;
1015
- const step = steps[index];
1016
- const target = typeof step.target === "function" ? step.target() : step.target;
1017
- const rect = target?.getBoundingClientRect();
1018
- const placement = step.placement ?? (rect ? "bottom" : "center");
1019
- const panelStyle = placement === "center" || !rect ? { top: "50%", left: "50%", transform: "translate(-50%, -50%)" } : placement.startsWith("bottom") ? { top: rect.bottom + 14, left: rect.left + rect.width / 2, transform: "translateX(-50%)" } : placement.startsWith("top") ? { top: rect.top - 14, left: rect.left + rect.width / 2, transform: "translate(-50%, -100%)" } : placement === "left" ? { top: rect.top + rect.height / 2, left: rect.left - 14, transform: "translate(-100%, -50%)" } : { top: rect.top + rect.height / 2, left: rect.right + 14, transform: "translateY(-50%)" };
1020
- return createPortal2(/* @__PURE__ */ jsxs6("div", { className: "sia-tour", role: "dialog", "aria-modal": "true", children: [
1021
- mask && !rect ? /* @__PURE__ */ jsx6("div", { className: "sia-tour__mask" }) : null,
1022
- rect ? /* @__PURE__ */ jsx6("div", { className: "sia-tour__target", style: { top: rect.top - 5, left: rect.left - 5, width: rect.width + 10, height: rect.height + 10, pointerEvents: disabledInteraction ? "auto" : "none", boxShadow: mask ? "0 0 0 5px var(--sia-color-surface), 0 0 0 9999px rgb(0 0 0 / 46%)" : "0 0 0 5px var(--sia-color-surface)" } }) : null,
1023
- /* @__PURE__ */ jsxs6("div", { className: "sia-tour__panel", style: panelStyle, children: [
1024
- closable ? /* @__PURE__ */ jsx6("button", { className: "sia-tour__close", "aria-label": "\u5173\u95ED\u5F15\u5BFC", onClick: () => onClose?.(index), children: /* @__PURE__ */ jsx6(Icon, { name: "close", size: 14 }) }) : null,
1025
- step.cover,
1026
- /* @__PURE__ */ jsx6("strong", { children: step.title }),
1027
- step.description ? /* @__PURE__ */ jsx6("div", { className: "sia-tour__description", children: step.description }) : null,
1028
- /* @__PURE__ */ jsxs6("footer", { children: [
1029
- /* @__PURE__ */ jsxs6("span", { children: [
1030
- index + 1,
1031
- " / ",
1032
- steps.length
1033
- ] }),
1034
- /* @__PURE__ */ jsxs6("div", { children: [
1035
- index > 0 ? /* @__PURE__ */ jsx6(Button, { size: "small", ...step.prevButtonProps, onClick: () => setIndex(index - 1), children: step.prevButtonProps?.children ?? "\u4E0A\u4E00\u6B65" }) : null,
1036
- /* @__PURE__ */ jsx6(Button, { size: "small", variant: "primary", ...step.nextButtonProps, onClick: () => {
1037
- if (index >= steps.length - 1) onFinish?.();
1038
- else setIndex(index + 1);
1039
- }, children: step.nextButtonProps?.children ?? (index >= steps.length - 1 ? "\u5B8C\u6210" : "\u4E0B\u4E00\u6B65") })
1040
- ] })
1041
- ] })
1042
- ] })
1043
- ] }), document.body);
1044
- }
1045
-
1046
- // src/components/DataDisplay.tsx
1047
- import { Children, useEffect as useEffect5, useMemo as useMemo5, useRef as useRef5, useState as useState5 } from "react";
1048
- import { createPortal as createPortal4 } from "react-dom";
1049
-
1050
- // src/components/Overlay.tsx
1051
- import { useEffect as useEffect4, useRef as useRef4 } from "react";
1052
- import { createPortal as createPortal3 } from "react-dom";
1053
- var bodyLockCount = 0;
1054
- var previousBodyOverflow = "";
1055
- var overlayStack = [];
1056
- function lockBodyScroll() {
1057
- if (bodyLockCount === 0) {
1058
- previousBodyOverflow = document.body.style.overflow;
1059
- document.body.style.overflow = "hidden";
1060
- }
1061
- bodyLockCount += 1;
1062
- return () => {
1063
- bodyLockCount = Math.max(0, bodyLockCount - 1);
1064
- if (bodyLockCount === 0) document.body.style.overflow = previousBodyOverflow;
1065
- };
1066
- }
1067
- function focusableElements(container) {
1068
- return [...container.querySelectorAll(
1069
- 'a[href], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])'
1070
- )].filter((element) => !element.hidden && element.getAttribute("aria-hidden") !== "true");
1071
- }
1072
- function useOverlayLifecycle(open, panelRef, onRequestClose, keyboard = true) {
1073
- const overlayIdRef = useRef4(/* @__PURE__ */ Symbol("sia-overlay"));
1074
- const closeRef = useRef4(onRequestClose);
1075
- closeRef.current = onRequestClose;
1076
- useEffect4(() => {
1077
- if (!open || typeof document === "undefined") return void 0;
1078
- const overlayId = overlayIdRef.current;
1079
- const previouslyFocused = document.activeElement instanceof HTMLElement ? document.activeElement : null;
1080
- overlayStack.push(overlayId);
1081
- const unlockBody = lockBodyScroll();
1082
- const focusTimer = window.setTimeout(() => {
1083
- const panel = panelRef.current;
1084
- if (!panel) return;
1085
- const [firstFocusable] = focusableElements(panel);
1086
- (firstFocusable ?? panel).focus({ preventScroll: true });
1087
- }, 0);
1088
- function handleKeyDown(event) {
1089
- if (overlayStack.at(-1) !== overlayId) return;
1090
- if (event.key === "Escape" && keyboard) {
1091
- event.preventDefault();
1092
- closeRef.current();
1093
- return;
1094
- }
1095
- if (event.key !== "Tab" || !panelRef.current) return;
1096
- const focusable = focusableElements(panelRef.current);
1097
- if (focusable.length === 0) {
1098
- event.preventDefault();
1099
- panelRef.current.focus();
1100
- return;
1101
- }
1102
- const first = focusable[0];
1103
- const last = focusable.at(-1);
1104
- if (event.shiftKey && document.activeElement === first) {
1105
- event.preventDefault();
1106
- last.focus();
1107
- } else if (!event.shiftKey && document.activeElement === last) {
1108
- event.preventDefault();
1109
- first.focus();
1110
- }
1111
- }
1112
- document.addEventListener("keydown", handleKeyDown);
1113
- return () => {
1114
- window.clearTimeout(focusTimer);
1115
- document.removeEventListener("keydown", handleKeyDown);
1116
- const index = overlayStack.lastIndexOf(overlayId);
1117
- if (index >= 0) overlayStack.splice(index, 1);
1118
- unlockBody();
1119
- previouslyFocused?.focus({ preventScroll: true });
1120
- };
1121
- }, [keyboard, open, panelRef]);
1122
- }
1123
- function OverlayPortal({ children, container }) {
1124
- if (typeof document === "undefined") return null;
1125
- const target = typeof container === "function" ? container() : container ?? document.body;
1126
- return createPortal3(children, target);
1127
- }
1128
-
1129
- // src/components/DataDisplay.tsx
1130
- import { Fragment as Fragment4, jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
1131
- function Rate({ value, defaultValue = 0, count = 5, allowHalf = false, allowClear = true, disabled = false, character, tooltips, onChange, onHoverChange, className = "", ...props }) {
1132
- const [current, setCurrent] = useControllableState({ value, defaultValue, onChange });
1133
- const [hover, setHover] = useState5(0);
1134
- const shown = hover || current;
1135
- function choose(next) {
1136
- setCurrent(allowClear && next === current ? 0 : next);
1137
- }
1138
- return /* @__PURE__ */ jsx7("div", { className: `sia-rate${disabled ? " is-disabled" : ""} ${className}`.trim(), role: "radiogroup", "aria-label": "\u8BC4\u5206", onMouseLeave: () => {
1139
- setHover(0);
1140
- onHoverChange?.(0);
1141
- }, ...props, children: Array.from({ length: count }, (_, index) => {
1142
- const whole = index + 1;
1143
- const fill = Math.max(0, Math.min(1, shown - index));
1144
- const content = typeof character === "function" ? character(index) : character ?? /* @__PURE__ */ jsx7(Icon, { name: "star", variant: "filled" });
1145
- return /* @__PURE__ */ jsx7("span", { className: "sia-rate__item", title: tooltips?.[index], children: /* @__PURE__ */ jsxs7("button", { type: "button", disabled, role: "radio", "aria-checked": current === whole, "aria-label": `${whole} \u661F`, onMouseMove: (event) => {
1146
- const rect = event.currentTarget.getBoundingClientRect();
1147
- const next = allowHalf && event.clientX - rect.left < rect.width / 2 ? whole - 0.5 : whole;
1148
- setHover(next);
1149
- onHoverChange?.(next);
1150
- }, onClick: (event) => {
1151
- const rect = event.currentTarget.getBoundingClientRect();
1152
- choose(allowHalf && event.clientX - rect.left < rect.width / 2 ? whole - 0.5 : whole);
1153
- }, onKeyDown: (event) => {
1154
- if (event.key === "ArrowRight" || event.key === "ArrowUp") {
1155
- event.preventDefault();
1156
- setCurrent(Math.min(count, current + (allowHalf ? 0.5 : 1)));
1157
- }
1158
- if (event.key === "ArrowLeft" || event.key === "ArrowDown") {
1159
- event.preventDefault();
1160
- setCurrent(Math.max(0, current - (allowHalf ? 0.5 : 1)));
1161
- }
1162
- }, children: [
1163
- /* @__PURE__ */ jsx7("span", { className: "sia-rate__base", children: content }),
1164
- /* @__PURE__ */ jsx7("span", { className: "sia-rate__fill", style: { width: `${fill * 100}%` }, children: content })
1165
- ] }) }, whole);
1166
- }) });
1167
- }
1168
- function BadgeRibbon({ text, color, placement = "end", className = "", children, ...props }) {
1169
- return /* @__PURE__ */ jsxs7("div", { className: `sia-ribbon-wrap ${className}`.trim(), ...props, children: [
1170
- children,
1171
- /* @__PURE__ */ jsx7("span", { className: `sia-ribbon sia-ribbon--${placement}`, style: { backgroundColor: color }, children: text })
1172
- ] });
1173
- }
1174
- function Badge({ count, showZero = false, overflowCount = 99, dot = false, status, text, color, offset = [0, 0], size = "default", className = "", children, style, ...props }) {
1175
- const numeric = typeof count === "number";
1176
- const hidden = !dot && !status && (count === void 0 || count === null || count === 0 && !showZero);
1177
- const display = numeric && count > overflowCount ? `${overflowCount}+` : count;
1178
- const badge = !hidden ? /* @__PURE__ */ jsx7("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;
1179
- if (status && !children) return /* @__PURE__ */ jsxs7("span", { className: `sia-badge sia-badge--status ${className}`.trim(), style, ...props, children: [
1180
- badge,
1181
- text ? /* @__PURE__ */ jsx7("span", { className: "sia-badge__text", children: text }) : null
1182
- ] });
1183
- return /* @__PURE__ */ jsxs7("span", { className: `sia-badge ${className}`.trim(), style, ...props, children: [
1184
- children,
1185
- badge,
1186
- text && !status ? /* @__PURE__ */ jsx7("span", { className: "sia-badge__text", children: text }) : null
1187
- ] });
1188
- }
1189
- Badge.Ribbon = BadgeRibbon;
1190
- function pad(value) {
1191
- return String(value).padStart(2, "0");
1192
- }
1193
- function formatDate(date) {
1194
- return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}`;
1195
- }
1196
- function parseDate(value) {
1197
- const date = value ? /* @__PURE__ */ new Date(`${value}T00:00:00`) : /* @__PURE__ */ new Date();
1198
- return Number.isNaN(date.getTime()) ? /* @__PURE__ */ new Date() : date;
1199
- }
1200
- var WEEK = ["\u4E00", "\u4E8C", "\u4E09", "\u56DB", "\u4E94", "\u516D", "\u65E5"];
1201
- var MONTHS = ["\u4E00\u6708", "\u4E8C\u6708", "\u4E09\u6708", "\u56DB\u6708", "\u4E94\u6708", "\u516D\u6708", "\u4E03\u6708", "\u516B\u6708", "\u4E5D\u6708", "\u5341\u6708", "\u5341\u4E00\u6708", "\u5341\u4E8C\u6708"];
1202
- function Calendar({ value, defaultValue, mode = "month", fullscreen = true, validRange, disabledDate, dateCellRender, fullCellRender, headerRender, onChange, onSelect, onPanelChange, className = "", ...props }) {
1203
- const [selected, setSelected] = useControllableState({ value, defaultValue: defaultValue ?? formatDate(/* @__PURE__ */ new Date()), onChange });
1204
- const [panelMode, setPanelMode] = useState5(mode);
1205
- const [panel, setPanel] = useState5(() => {
1206
- const date = parseDate(value ?? defaultValue);
1207
- return new Date(date.getFullYear(), date.getMonth(), 1);
1208
- });
1209
- useEffect5(() => {
1210
- if (value) {
1211
- const date = parseDate(value);
1212
- setPanel(new Date(date.getFullYear(), date.getMonth(), 1));
1213
- }
1214
- }, [value]);
1215
- const grid = useMemo5(() => {
1216
- const first = new Date(panel.getFullYear(), panel.getMonth(), 1);
1217
- const mondayIndex = (first.getDay() + 6) % 7;
1218
- return Array.from({ length: 42 }, (_, index) => new Date(panel.getFullYear(), panel.getMonth(), index - mondayIndex + 1));
1219
- }, [panel]);
1220
- function setPanelValue(next) {
1221
- setPanel(next);
1222
- onPanelChange?.(formatDate(next), panelMode);
1223
- }
1224
- function setMode(next) {
1225
- setPanelMode(next);
1226
- onPanelChange?.(formatDate(panel), next);
1227
- }
1228
- const headerConfig = { value: formatDate(panel), mode: panelMode, onChange: (next) => setPanelValue(parseDate(next)), onTypeChange: setMode };
1229
- return /* @__PURE__ */ jsxs7("div", { className: `sia-calendar${fullscreen ? " sia-calendar--fullscreen" : " sia-calendar--mini"} ${className}`.trim(), ...props, children: [
1230
- /* @__PURE__ */ jsx7("div", { className: "sia-calendar__header", children: headerRender ? headerRender(headerConfig) : /* @__PURE__ */ jsxs7(Fragment4, { children: [
1231
- /* @__PURE__ */ jsx7("button", { type: "button", "aria-label": "\u4E0A\u4E00\u9875", onClick: () => setPanelValue(new Date(panel.getFullYear() - (panelMode === "year" ? 1 : 0), panel.getMonth() - (panelMode === "month" ? 1 : 0), 1)), children: /* @__PURE__ */ jsx7(Icon, { name: "chevron-left" }) }),
1232
- /* @__PURE__ */ jsxs7("strong", { children: [
1233
- panel.getFullYear(),
1234
- " \u5E74",
1235
- panelMode === "month" ? ` ${panel.getMonth() + 1} \u6708` : ""
1236
- ] }),
1237
- /* @__PURE__ */ jsxs7("div", { className: "sia-calendar__modes", children: [
1238
- /* @__PURE__ */ jsx7("button", { className: panelMode === "month" ? "is-active" : "", onClick: () => setMode("month"), children: "\u6708" }),
1239
- /* @__PURE__ */ jsx7("button", { className: panelMode === "year" ? "is-active" : "", onClick: () => setMode("year"), children: "\u5E74" })
1240
- ] }),
1241
- /* @__PURE__ */ jsx7("button", { type: "button", "aria-label": "\u4E0B\u4E00\u9875", onClick: () => setPanelValue(new Date(panel.getFullYear() + (panelMode === "year" ? 1 : 0), panel.getMonth() + (panelMode === "month" ? 1 : 0), 1)), children: /* @__PURE__ */ jsx7(Icon, { name: "chevron-right" }) })
1242
- ] }) }),
1243
- panelMode === "year" ? /* @__PURE__ */ jsx7("div", { className: "sia-calendar__months", children: MONTHS.map((month, index) => /* @__PURE__ */ jsx7("button", { className: panel.getMonth() === index ? "is-selected" : "", onClick: () => {
1244
- setPanelValue(new Date(panel.getFullYear(), index, 1));
1245
- setMode("month");
1246
- }, children: month }, month)) }) : /* @__PURE__ */ jsxs7(Fragment4, { children: [
1247
- /* @__PURE__ */ jsx7("div", { className: "sia-calendar__week", children: WEEK.map((day) => /* @__PURE__ */ jsx7("span", { children: day }, day)) }),
1248
- /* @__PURE__ */ jsx7("div", { className: "sia-calendar__grid", children: grid.map((date) => {
1249
- const key = formatDate(date);
1250
- const outside = date.getMonth() !== panel.getMonth();
1251
- const disabled = Boolean(disabledDate?.(key) || validRange && (key < validRange[0] || key > validRange[1]));
1252
- const origin = /* @__PURE__ */ jsxs7(Fragment4, { children: [
1253
- /* @__PURE__ */ jsx7("span", { children: date.getDate() }),
1254
- dateCellRender?.(key)
1255
- ] });
1256
- return /* @__PURE__ */ jsx7("button", { disabled, className: `${outside ? "is-outside" : ""}${selected === key ? " is-selected" : ""}${formatDate(/* @__PURE__ */ new Date()) === key ? " is-today" : ""}`, onClick: () => {
1257
- setSelected(key);
1258
- onSelect?.(key);
1259
- if (outside) setPanelValue(new Date(date.getFullYear(), date.getMonth(), 1));
1260
- }, children: fullCellRender?.(key, { originNode: origin, type: "date" }) ?? origin }, key);
1261
- }) })
1262
- ] })
1263
- ] });
1264
- }
1265
- function Carousel({ autoplay = false, autoplaySpeed = 3e3, arrows = false, dots = true, effect = "scroll", infinite = true, initialSlide = 0, pauseOnHover = true, beforeChange, afterChange, className = "", children, ...props }) {
1266
- const slides = Children.toArray(children);
1267
- const [current, setCurrent] = useState5(Math.min(initialSlide, Math.max(0, slides.length - 1)));
1268
- const [paused, setPaused] = useState5(false);
1269
- function go(next) {
1270
- if (!slides.length) return;
1271
- const resolved = infinite ? (next + slides.length) % slides.length : Math.max(0, Math.min(slides.length - 1, next));
1272
- if (resolved === current) return;
1273
- beforeChange?.(current, resolved);
1274
- setCurrent(resolved);
1275
- afterChange?.(resolved);
1276
- }
1277
- useEffect5(() => {
1278
- if (!autoplay || paused || slides.length < 2) return;
1279
- const timer = window.setInterval(() => go(current + 1), autoplaySpeed);
1280
- return () => window.clearInterval(timer);
1281
- }, [autoplay, autoplaySpeed, current, paused, slides.length]);
1282
- return /* @__PURE__ */ jsxs7("div", { className: `sia-carousel sia-carousel--${effect} ${className}`.trim(), onMouseEnter: pauseOnHover ? () => setPaused(true) : void 0, onMouseLeave: pauseOnHover ? () => setPaused(false) : void 0, ...props, children: [
1283
- /* @__PURE__ */ jsx7("div", { className: "sia-carousel__viewport", children: /* @__PURE__ */ jsx7("div", { className: "sia-carousel__track", style: effect === "scroll" ? { transform: `translateX(-${current * 100}%)` } : void 0, children: slides.map((slide, index) => /* @__PURE__ */ jsx7("div", { className: `sia-carousel__slide${index === current ? " is-active" : ""}`, "aria-hidden": index !== current, children: slide }, index)) }) }),
1284
- arrows ? /* @__PURE__ */ jsxs7(Fragment4, { children: [
1285
- /* @__PURE__ */ jsx7("button", { className: "sia-carousel__arrow sia-carousel__arrow--prev", disabled: !infinite && current === 0, "aria-label": "\u4E0A\u4E00\u5F20", onClick: () => go(current - 1), children: /* @__PURE__ */ jsx7(Icon, { name: "chevron-left" }) }),
1286
- /* @__PURE__ */ jsx7("button", { className: "sia-carousel__arrow sia-carousel__arrow--next", disabled: !infinite && current === slides.length - 1, "aria-label": "\u4E0B\u4E00\u5F20", onClick: () => go(current + 1), children: /* @__PURE__ */ jsx7(Icon, { name: "chevron-right" }) })
1287
- ] }) : null,
1288
- dots ? /* @__PURE__ */ jsx7("div", { className: `sia-carousel__dots${typeof dots === "object" && dots.className ? ` ${dots.className}` : ""}`, children: slides.map((_, index) => /* @__PURE__ */ jsx7("button", { "aria-label": `\u5207\u6362\u5230\u7B2C ${index + 1} \u5F20`, "aria-current": index === current, onClick: () => go(index), children: /* @__PURE__ */ jsx7("span", {}) }, index)) }) : null
1289
- ] });
1290
- }
1291
- function CollapsePanel({
1292
- open,
1293
- forceRender = false,
1294
- destroyInactivePanel = false,
1295
- children
1296
- }) {
1297
- const [keepChildren, setKeepChildren] = useState5(open || forceRender || !destroyInactivePanel);
1298
- useEffect5(() => {
1299
- if (open || forceRender || !destroyInactivePanel) {
1300
- setKeepChildren(true);
1301
- return void 0;
1302
- }
1303
- const timer = window.setTimeout(() => setKeepChildren(false), 240);
1304
- return () => window.clearTimeout(timer);
1305
- }, [destroyInactivePanel, forceRender, open]);
1306
- const renderChildren = open || forceRender || !destroyInactivePanel || keepChildren;
1307
- return /* @__PURE__ */ jsx7("div", { className: "sia-collapse__panel", "aria-hidden": !open, children: /* @__PURE__ */ jsx7("div", { className: "sia-collapse__panel-motion", children: /* @__PURE__ */ jsx7("div", { className: "sia-collapse__panel-body", children: renderChildren ? children : null }) }) });
1308
- }
1309
- function Collapse({ items, activeKey, defaultActiveKey = [], accordion = false, bordered = true, ghost = false, destroyInactivePanel = false, expandIconPosition = "start", onChange, className = "", ...props }) {
1310
- const normalize = (key) => Array.isArray(key) ? [...key] : key === void 0 ? [] : [key];
1311
- const controlled = activeKey !== void 0;
1312
- const [internal, setInternal] = useState5(normalize(defaultActiveKey));
1313
- const openKeys = controlled ? normalize(activeKey) : internal;
1314
- function toggle(item) {
1315
- if (item.disabled || item.collapsible === "disabled") return;
1316
- const open = openKeys.includes(item.key);
1317
- const next = accordion ? open ? [] : [item.key] : open ? openKeys.filter((key) => key !== item.key) : [...openKeys, item.key];
1318
- if (!controlled) setInternal(next);
1319
- onChange?.(accordion ? next[0] ?? "" : next);
1320
- }
1321
- const borderClassName = !bordered || ghost ? "sia-collapse--borderless" : "sia-collapse--bordered";
1322
- return /* @__PURE__ */ jsx7("div", { className: `sia-collapse ${borderClassName}${ghost ? " sia-collapse--ghost" : ""} sia-collapse--icon-${expandIconPosition} ${className}`.trim(), ...props, children: items.map((item) => {
1323
- const open = openKeys.includes(item.key);
1324
- const arrow = item.showArrow === false ? null : /* @__PURE__ */ jsx7("button", { type: "button", className: "sia-collapse__arrow", "aria-label": open ? "\u6536\u8D77" : "\u5C55\u5F00", onClick: item.collapsible === "icon" ? () => toggle(item) : void 0, children: /* @__PURE__ */ jsx7(Icon, { name: "chevron-right", size: 14 }) });
1325
- return /* @__PURE__ */ jsxs7("section", { className: `sia-collapse__item${open ? " is-open" : ""}${item.disabled ? " is-disabled" : ""} ${item.className ?? ""}`.trim(), children: [
1326
- /* @__PURE__ */ jsxs7("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) => {
1327
- if (event.key === "Enter" || event.key === " ") {
1328
- event.preventDefault();
1329
- toggle(item);
1330
- }
1331
- }, children: [
1332
- expandIconPosition === "start" ? arrow : null,
1333
- /* @__PURE__ */ jsx7("span", { className: "sia-collapse__label", children: item.label }),
1334
- item.extra ? /* @__PURE__ */ jsx7("span", { className: "sia-collapse__extra", onClick: (event) => event.stopPropagation(), children: item.extra }) : null,
1335
- expandIconPosition === "end" ? arrow : null
1336
- ] }),
1337
- /* @__PURE__ */ jsx7(CollapsePanel, { open, forceRender: item.forceRender, destroyInactivePanel, children: item.children })
1338
- ] }, item.key);
1339
- }) });
1340
- }
1341
- function ImagePreview({ open, src, alt = "", onOpenChange }) {
1342
- const previewRef = useRef5(null);
1343
- useOverlayLifecycle(open, previewRef, () => onOpenChange(false));
1344
- if (!open || !src || typeof document === "undefined") return null;
1345
- return createPortal4(/* @__PURE__ */ jsxs7("div", { ref: previewRef, className: "sia-image-preview", role: "dialog", "aria-modal": "true", "aria-label": "\u56FE\u7247\u9884\u89C8", tabIndex: -1, onClick: () => onOpenChange(false), children: [
1346
- /* @__PURE__ */ jsx7("button", { type: "button", "aria-label": "\u5173\u95ED\u9884\u89C8", onClick: () => onOpenChange(false), children: /* @__PURE__ */ jsx7(Icon, { name: "close" }) }),
1347
- /* @__PURE__ */ jsx7("img", { src, alt, onClick: (event) => event.stopPropagation() })
1348
- ] }), document.body);
1349
- }
1350
- function Image({ fallback, placeholder, preview = true, rootClassName = "", className = "", src, alt = "", onError, style, ...props }) {
1351
- const config = typeof preview === "object" ? preview : {};
1352
- const controlled = typeof preview === "object" && preview.open !== void 0;
1353
- const [internalOpen, setInternalOpen] = useState5(false);
1354
- const open = controlled ? config.open : internalOpen;
1355
- const [loaded, setLoaded] = useState5(false);
1356
- const [failed, setFailed] = useState5(false);
1357
- const displaySrc = failed && fallback ? fallback : src;
1358
- const radius = typeof style?.borderRadius === "number" ? `${style.borderRadius}px` : style?.borderRadius;
1359
- const rootStyle = {
1360
- ...style,
1361
- "--sia-image-radius": radius ?? "var(--sia-radius)"
1362
- };
1363
- function setOpen(next) {
1364
- if (!controlled) setInternalOpen(next);
1365
- config.onOpenChange?.(next);
1366
- }
1367
- return /* @__PURE__ */ jsxs7("span", { className: `sia-image ${rootClassName}`.trim(), style: rootStyle, children: [
1368
- !loaded && placeholder ? /* @__PURE__ */ jsx7("span", { className: "sia-image__placeholder", children: placeholder }) : null,
1369
- /* @__PURE__ */ jsx7("img", { ...props, src: displaySrc, alt, className, onLoad: () => setLoaded(true), onError: (event) => {
1370
- if (!failed && fallback) setFailed(true);
1371
- onError?.(event);
1372
- } }),
1373
- preview ? /* @__PURE__ */ jsx7("button", { type: "button", className: "sia-image__mask", "aria-label": "\u9884\u89C8\u56FE\u7247", onClick: () => setOpen(true), children: config.mask ?? /* @__PURE__ */ jsxs7(Fragment4, { children: [
1374
- /* @__PURE__ */ jsx7(Icon, { name: "eye" }),
1375
- "\u9884\u89C8"
1376
- ] }) }) : null,
1377
- /* @__PURE__ */ jsx7(ImagePreview, { open, src: config.src ?? displaySrc, alt, onOpenChange: setOpen })
1378
- ] });
1379
- }
1380
-
1381
- // src/components/Upload.tsx
1382
- import { useId as useId4, useRef as useRef6 } from "react";
1383
- import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
1384
- var LIST_IGNORE = /* @__PURE__ */ Symbol("SIA_UPLOAD_LIST_IGNORE");
1385
- function UploadRoot({
1386
- accept,
1387
- multiple = false,
1388
- directory = false,
1389
- disabled = false,
1390
- maxCount: maxCount2,
1391
- fileList,
1392
- defaultFileList = [],
1393
- listType = "text",
1394
- showUploadList = true,
1395
- beforeUpload,
1396
- customRequest,
1397
- onChange,
1398
- onRemove,
1399
- children,
1400
- className = "",
1401
- onDragOver,
1402
- onDrop,
1403
- ...props
1404
- }) {
1405
- const inputRef = useRef6(null);
1406
- const filesRef = useRef6(fileList ?? defaultFileList);
1407
- const id = useId4();
1408
- const [files, setFiles] = useControllableState({ value: fileList, defaultValue: defaultFileList });
1409
- filesRef.current = files;
1410
- function emit(file, nextList) {
1411
- filesRef.current = nextList;
1412
- setFiles(nextList);
1413
- onChange?.({ file, fileList: nextList });
1414
- }
1415
- async function processFile(file, allFiles) {
1416
- const beforeResult = await beforeUpload?.(file, allFiles);
1417
- if (beforeResult === LIST_IGNORE) return;
1418
- if (beforeResult === false) {
1419
- const pending = { uid: `${Date.now()}-${file.name}`, name: file.name, size: file.size, type: file.type, status: "ready", originFileObj: file };
1420
- emit(pending, maxCount2 === 1 ? [pending] : [...filesRef.current, pending].slice(-(maxCount2 ?? Number.POSITIVE_INFINITY)));
1421
- return;
1422
- }
1423
- const uploadFile = beforeResult instanceof File ? beforeResult : file;
1424
- const item = { uid: `${Date.now()}-${Math.random().toString(36).slice(2)}`, name: uploadFile.name, size: uploadFile.size, type: uploadFile.type, status: "uploading", percent: 0, originFileObj: uploadFile };
1425
- const nextList = maxCount2 === 1 ? [item] : [...filesRef.current, item].slice(-(maxCount2 ?? Number.POSITIVE_INFINITY));
1426
- emit(item, nextList);
1427
- const request = {
1428
- file: uploadFile,
1429
- filename: uploadFile.name,
1430
- onProgress: (percent) => {
1431
- const progressing = { ...item, status: "uploading", percent };
1432
- const progressList = filesRef.current.map((entry) => entry.uid === item.uid ? progressing : entry);
1433
- emit(progressing, progressList);
1434
- },
1435
- onSuccess: (response) => {
1436
- const done = { ...item, status: "done", percent: 100, response };
1437
- const completed = filesRef.current.map((entry) => entry.uid === item.uid ? done : entry);
1438
- emit(done, completed);
1439
- },
1440
- onError: (error) => {
1441
- const failed = { ...item, status: "error", error };
1442
- const completed = filesRef.current.map((entry) => entry.uid === item.uid ? failed : entry);
1443
- emit(failed, completed);
1444
- }
1445
- };
1446
- if (customRequest) customRequest(request);
1447
- else window.setTimeout(() => request.onSuccess({ local: true }), 180);
1448
- }
1449
- function handleFiles(event) {
1450
- const selected = [...event.currentTarget.files ?? []];
1451
- selected.forEach((file) => void processFile(file, selected));
1452
- event.currentTarget.value = "";
1453
- }
1454
- function handleDrop(event) {
1455
- onDrop?.(event);
1456
- if (event.defaultPrevented || disabled) return;
1457
- event.preventDefault();
1458
- const selected = [...event.dataTransfer.files];
1459
- selected.forEach((file) => void processFile(file, selected));
1460
- }
1461
- async function remove(file) {
1462
- if (await onRemove?.(file) === false) return;
1463
- const next = files.filter((item) => item.uid !== file.uid);
1464
- emit({ ...file, status: "ready" }, next);
1465
- }
1466
- return /* @__PURE__ */ jsxs8(
1467
- "div",
1468
- {
1469
- className: `sia-upload sia-upload--${listType} ${className}`.trim(),
1470
- onDragOver: (event) => {
1471
- onDragOver?.(event);
1472
- if (!event.defaultPrevented && !disabled) event.preventDefault();
1473
- },
1474
- onDrop: handleDrop,
1475
- ...props,
1476
- children: [
1477
- /* @__PURE__ */ jsx8(
1478
- "input",
1479
- {
1480
- ref: inputRef,
1481
- id,
1482
- className: "sia-upload__input",
1483
- type: "file",
1484
- accept,
1485
- multiple,
1486
- disabled,
1487
- ...directory ? { webkitdirectory: "", directory: "" } : {},
1488
- onChange: handleFiles
1489
- }
1490
- ),
1491
- /* @__PURE__ */ jsx8("div", { className: "sia-upload__trigger", onClick: () => !disabled && inputRef.current?.click(), children: children ?? /* @__PURE__ */ jsx8(Button, { icon: /* @__PURE__ */ jsx8(Icon, { name: "upload", size: 16 }), disabled, children: "\u9009\u62E9\u6587\u4EF6" }) }),
1492
- showUploadList && files.length ? /* @__PURE__ */ jsx8("div", { className: "sia-upload__list", children: files.map((file) => /* @__PURE__ */ jsxs8("div", { className: `sia-upload__item sia-upload__item--${file.status ?? "ready"}`, children: [
1493
- /* @__PURE__ */ jsx8(Icon, { name: file.status === "done" ? "circle-check" : "file", size: 16 }),
1494
- /* @__PURE__ */ jsx8("span", { className: "sia-upload__name", title: file.name, children: file.name }),
1495
- file.status === "uploading" ? /* @__PURE__ */ jsx8("span", { className: "sia-upload__progress", children: /* @__PURE__ */ jsx8("span", { style: { width: `${file.percent ?? 0}%` } }) }) : null,
1496
- /* @__PURE__ */ jsx8("button", { type: "button", "aria-label": `\u79FB\u9664 ${file.name}`, onClick: () => void remove(file), children: /* @__PURE__ */ jsx8(Icon, { name: "close", size: 14 }) })
1497
- ] }, file.uid)) }) : null
1498
- ]
1499
- }
1500
- );
1501
- }
1502
- function UploadDragger({ hint = "\u652F\u6301\u5355\u4E2A\u6216\u6279\u91CF\u4E0A\u4F20", children, className = "", ...props }) {
1503
- return /* @__PURE__ */ jsx8(UploadRoot, { ...props, className: `sia-upload-dragger ${className}`.trim(), children: children ?? /* @__PURE__ */ jsxs8("div", { className: "sia-upload-dragger__content", children: [
1504
- /* @__PURE__ */ jsx8(Icon, { name: "upload", size: 28 }),
1505
- /* @__PURE__ */ jsx8("strong", { children: "\u70B9\u51FB\u6216\u62D6\u62FD\u6587\u4EF6\u5230\u6B64\u533A\u57DF\u4E0A\u4F20" }),
1506
- /* @__PURE__ */ jsx8("span", { children: hint })
1507
- ] }) });
1508
- }
1509
- var Upload = Object.assign(UploadRoot, { Dragger: UploadDragger, LIST_IGNORE });
1510
-
1511
- // src/components/Modal.tsx
1512
- import { forwardRef as forwardRef2, useEffect as useEffect6, useId as useId5, useRef as useRef7, useState as useState6 } from "react";
1513
- import { createRoot as createRoot2 } from "react-dom/client";
1514
- import { Fragment as Fragment5, jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
1515
- var modalHandles = /* @__PURE__ */ new Set();
1516
- var MODAL_MOTION_DURATION = 220;
1517
- var typeIcons = {
1518
- info: /* @__PURE__ */ jsx9(Icon, { name: "info", size: 23 }),
1519
- success: /* @__PURE__ */ jsx9(Icon, { name: "circle-check", size: 23 }),
1520
- warning: /* @__PURE__ */ jsx9(Icon, { name: "warning", size: 23 }),
1521
- error: /* @__PURE__ */ jsx9(Icon, { name: "circle-close", size: 23 }),
1522
- confirm: /* @__PURE__ */ jsx9(Icon, { name: "question", size: 23 })
1523
- };
1524
- var ModalRoot = forwardRef2(function Modal({
1525
- open,
1526
- title,
1527
- children,
1528
- footer,
1529
- type = "default",
1530
- okText = "\u786E\u5B9A",
1531
- cancelText = "\u53D6\u6D88",
1532
- showCancel = type === "default" || type === "confirm",
1533
- confirmLoading = false,
1534
- okButtonProps,
1535
- cancelButtonProps,
1536
- closable = true,
1537
- maskClosable = true,
1538
- keyboard = true,
1539
- centered = false,
1540
- width = 520,
1541
- zIndex = 1e3,
1542
- getContainer,
1543
- onOk,
1544
- onCancel,
1545
- onOpenChange,
1546
- afterOpenChange,
1547
- className = "",
1548
- style,
1549
- ...props
1550
- }, forwardedRef) {
1551
- const localRef = useRef7(null);
1552
- const titleId = useId5();
1553
- const [submitting, setSubmitting] = useState6(false);
1554
- const [rendered, setRendered] = useState6(open);
1555
- const [closing, setClosing] = useState6(false);
1556
- const afterOpenChangeRef = useRef7(afterOpenChange);
1557
- afterOpenChangeRef.current = afterOpenChange;
1558
- const submitLock = useRef7(false);
1559
- useEffect6(() => {
1560
- if (!open || !rendered) return void 0;
1561
- const timer = window.setTimeout(() => afterOpenChangeRef.current?.(true), MODAL_MOTION_DURATION);
1562
- return () => window.clearTimeout(timer);
1563
- }, [open, rendered]);
1564
- useEffect6(() => {
1565
- if (open) {
1566
- setRendered(true);
1567
- setClosing(false);
1568
- return void 0;
1569
- }
1570
- if (!rendered) return void 0;
1571
- setClosing(true);
1572
- const timer = window.setTimeout(() => {
1573
- setRendered(false);
1574
- setClosing(false);
1575
- afterOpenChangeRef.current?.(false);
1576
- }, MODAL_MOTION_DURATION);
1577
- return () => window.clearTimeout(timer);
1578
- }, [open, rendered]);
1579
- function setRef(node) {
1580
- localRef.current = node;
1581
- if (typeof forwardedRef === "function") forwardedRef(node);
1582
- else if (forwardedRef) forwardedRef.current = node;
1583
- }
1584
- function requestClose() {
1585
- if (submitting || confirmLoading) return;
1586
- onCancel?.();
1587
- onOpenChange?.(false);
1588
- }
1589
- async function handleOk() {
1590
- if (submitLock.current || confirmLoading) return;
1591
- submitLock.current = true;
1592
- if (!onOk) {
1593
- onOpenChange?.(false);
1594
- submitLock.current = false;
1595
- return;
1596
- }
1597
- try {
1598
- const result = onOk();
1599
- if (result instanceof Promise) {
1600
- setSubmitting(true);
1601
- const resolved = await result;
1602
- if (resolved !== false) onOpenChange?.(false);
1603
- } else if (result !== false) {
1604
- onOpenChange?.(false);
1605
- }
1606
- } catch {
1607
- return;
1608
- } finally {
1609
- submitLock.current = false;
1610
- setSubmitting(false);
1611
- }
1612
- }
1613
- useOverlayLifecycle(rendered, localRef, requestClose, keyboard);
1614
- if (!rendered) return null;
1615
- const mergedStyle = {
1616
- ...style,
1617
- "--sia-modal-width": typeof width === "number" ? `${width}px` : width
1618
- };
1619
- const loading = submitting || confirmLoading;
1620
- const defaultFooter = /* @__PURE__ */ jsxs9(Fragment5, { children: [
1621
- showCancel ? /* @__PURE__ */ jsx9(Button, { ...cancelButtonProps, onClick: requestClose, disabled: loading || cancelButtonProps?.disabled, children: cancelText }) : null,
1622
- /* @__PURE__ */ jsx9(Button, { variant: "primary", ...okButtonProps, onClick: handleOk, loading, children: okText })
1623
- ] });
1624
- return /* @__PURE__ */ jsx9(OverlayPortal, { container: getContainer, children: /* @__PURE__ */ jsx9(
1625
- "div",
1626
- {
1627
- className: `sia-modal-root${centered ? " sia-modal-root--centered" : ""}`,
1628
- "data-state": closing ? "closing" : "open",
1629
- style: { zIndex },
1630
- onMouseDown: (event) => {
1631
- if (event.target === event.currentTarget && maskClosable) requestClose();
1632
- },
1633
- children: /* @__PURE__ */ jsxs9(
1634
- "div",
1635
- {
1636
- ref: setRef,
1637
- className: `sia-modal sia-modal--${type} ${className}`.trim(),
1638
- style: mergedStyle,
1639
- role: "dialog",
1640
- "aria-modal": "true",
1641
- "aria-labelledby": title ? titleId : void 0,
1642
- tabIndex: -1,
1643
- ...props,
1644
- children: [
1645
- /* @__PURE__ */ jsxs9("div", { className: "sia-modal__header", children: [
1646
- /* @__PURE__ */ jsxs9("div", { className: "sia-modal__heading", children: [
1647
- typeIcons[type] ? /* @__PURE__ */ jsx9("span", { className: "sia-modal__type-icon", "aria-hidden": "true", children: typeIcons[type] }) : null,
1648
- title ? /* @__PURE__ */ jsx9("strong", { id: titleId, children: title }) : null
1649
- ] }),
1650
- closable ? /* @__PURE__ */ jsx9("button", { type: "button", className: "sia-modal__close", "aria-label": "\u5173\u95ED\u5BF9\u8BDD\u6846", onClick: requestClose, children: /* @__PURE__ */ jsx9(Icon, { name: "close", size: 17 }) }) : null
1651
- ] }),
1652
- children !== void 0 ? /* @__PURE__ */ jsx9("div", { className: "sia-modal__body", children }) : null,
1653
- footer !== null ? /* @__PURE__ */ jsx9("div", { className: "sia-modal__footer", children: footer ?? defaultFooter }) : null
1654
- ]
1655
- }
1656
- )
1657
- }
1658
- ) });
1659
- });
1660
- function openModal(config) {
1661
- if (typeof document === "undefined") return { destroy() {
1662
- }, update() {
1663
- } };
1664
- const container = document.createElement("div");
1665
- container.className = "sia-modal-api-host";
1666
- document.body.appendChild(container);
1667
- const root = createRoot2(container);
1668
- let currentConfig = config;
1669
- let currentOpen = true;
1670
- let destroyed = false;
1671
- let cleanupTimer;
1672
- function cleanup() {
1673
- if (destroyed) return;
1674
- destroyed = true;
1675
- if (cleanupTimer !== void 0) window.clearTimeout(cleanupTimer);
1676
- root.unmount();
1677
- container.remove();
1678
- modalHandles.delete(handle);
1679
- }
1680
- function closeWithMotion() {
1681
- if (destroyed || !currentOpen) return;
1682
- currentOpen = false;
1683
- render();
1684
- cleanupTimer = window.setTimeout(cleanup, MODAL_MOTION_DURATION);
1685
- }
1686
- function render() {
1687
- root.render(/* @__PURE__ */ jsx9(ModalRoot, { ...currentConfig, open: currentOpen, onOpenChange: (nextOpen) => {
1688
- currentConfig.onOpenChange?.(nextOpen);
1689
- if (!nextOpen) closeWithMotion();
1690
- } }));
1691
- }
1692
- const handle = {
1693
- destroy: closeWithMotion,
1694
- update(next) {
1695
- if (destroyed) return;
1696
- currentConfig = { ...currentConfig, ...next };
1697
- render();
1698
- }
1699
- };
1700
- modalHandles.add(handle);
1701
- render();
1702
- return handle;
1703
- }
1704
- function preset(type, defaults = {}) {
1705
- return (config) => openModal({
1706
- okText: type === "confirm" ? "\u786E\u5B9A" : "\u77E5\u9053\u4E86",
1707
- showCancel: type === "confirm",
1708
- closable: type !== "confirm",
1709
- maskClosable: type !== "confirm",
1710
- ...defaults,
1711
- ...config,
1712
- type
1713
- });
1714
- }
1715
- var Modal2 = Object.assign(ModalRoot, {
1716
- open: openModal,
1717
- info: preset("info", { title: "\u63D0\u793A" }),
1718
- success: preset("success", { title: "\u64CD\u4F5C\u6210\u529F" }),
1719
- warning: preset("warning", { title: "\u8BF7\u6CE8\u610F" }),
1720
- error: preset("error", { title: "\u64CD\u4F5C\u5931\u8D25" }),
1721
- confirm: preset("confirm", { title: "\u786E\u8BA4\u64CD\u4F5C" }),
1722
- destroyAll() {
1723
- [...modalHandles].forEach((handle) => handle.destroy());
1724
- }
1725
- });
1726
-
1727
- // src/components/Message.tsx
1728
- import { createRoot as createRoot3 } from "react-dom/client";
1729
- import { jsx as jsx10, jsxs as jsxs10 } from "react/jsx-runtime";
1730
- var messageSeed = 0;
1731
- var messageRoot = null;
1732
- var messageContainer = null;
1733
- var messageItems = [];
1734
- var defaultDuration = 3;
1735
- var maxCount = 5;
1736
- var messageTimers = /* @__PURE__ */ new Map();
1737
- var messageIcons = {
1738
- info: /* @__PURE__ */ jsx10(Icon, { name: "info", size: 18 }),
1739
- success: /* @__PURE__ */ jsx10(Icon, { name: "circle-check", size: 18 }),
1740
- warning: /* @__PURE__ */ jsx10(Icon, { name: "warning", size: 18 }),
1741
- error: /* @__PURE__ */ jsx10(Icon, { name: "circle-close", size: 18 }),
1742
- loading: /* @__PURE__ */ jsx10(Icon, { name: "loader", size: 18, spin: true })
1743
- };
1744
- function ensureMessageRoot() {
1745
- if (typeof document === "undefined") return false;
1746
- if (!messageContainer) {
1747
- messageContainer = document.createElement("div");
1748
- messageContainer.className = "sia-message-root";
1749
- document.body.appendChild(messageContainer);
1750
- messageRoot = createRoot3(messageContainer);
1751
- }
1752
- return true;
1753
- }
1754
- function renderMessages() {
1755
- if (!ensureMessageRoot()) return;
1756
- messageRoot.render(/* @__PURE__ */ jsx10("div", { className: "sia-message-list", "aria-live": "polite", children: messageItems.map((item) => /* @__PURE__ */ jsxs10("div", { className: `sia-message sia-message--${item.type}`, role: item.type === "loading" ? "status" : "alert", children: [
1757
- /* @__PURE__ */ jsx10("span", { className: "sia-message__icon", "aria-hidden": "true", children: messageIcons[item.type] }),
1758
- /* @__PURE__ */ jsx10("span", { className: "sia-message__content", children: item.content }),
1759
- item.closable ? /* @__PURE__ */ jsx10("button", { type: "button", className: "sia-message__close", "aria-label": "\u5173\u95ED\u63D0\u793A", onClick: () => closeMessage(item.key), children: /* @__PURE__ */ jsx10(Icon, { name: "close", size: 14 }) }) : null
1760
- ] }, item.key)) }));
1761
- }
1762
- function closeMessage(key) {
1763
- const item = messageItems.find((candidate) => candidate.key === key);
1764
- const timer = messageTimers.get(key);
1765
- if (timer !== void 0) window.clearTimeout(timer);
1766
- messageTimers.delete(key);
1767
- messageItems = messageItems.filter((candidate) => candidate.key !== key);
1768
- item?.onClose?.();
1769
- renderMessages();
1770
- }
1771
- function scheduleMessage(item) {
1772
- const existingTimer = messageTimers.get(item.key);
1773
- if (existingTimer !== void 0) window.clearTimeout(existingTimer);
1774
- const duration = item.duration ?? (item.type === "loading" ? 0 : defaultDuration);
1775
- if (duration > 0) {
1776
- messageTimers.set(item.key, window.setTimeout(() => closeMessage(item.key), duration * 1e3));
1777
- }
1778
- }
1779
- function openMessage(input) {
1780
- const config = typeof input === "object" && input !== null && "content" in input ? input : { content: input };
1781
- const key = config.key ?? `sia-message-${++messageSeed}`;
1782
- const item = { ...config, key, type: config.type ?? "info" };
1783
- const existingIndex = messageItems.findIndex((candidate) => candidate.key === key);
1784
- if (existingIndex >= 0) messageItems = messageItems.map((candidate, index) => index === existingIndex ? item : candidate);
1785
- else messageItems = [...messageItems, item];
1786
- while (messageItems.length > maxCount) closeMessage(messageItems[0].key);
1787
- renderMessages();
1788
- scheduleMessage(item);
1789
- return {
1790
- close: () => closeMessage(key),
1791
- update(next) {
1792
- const current = messageItems.find((candidate) => candidate.key === key);
1793
- if (!current) return;
1794
- const updated = { ...current, ...next, key, type: next.type ?? current.type };
1795
- messageItems = messageItems.map((candidate) => candidate.key === key ? updated : candidate);
1796
- renderMessages();
1797
- scheduleMessage(updated);
1798
- }
1799
- };
1800
- }
1801
- function shortcut(type) {
1802
- return (content, duration) => openMessage({ content, duration, type });
1803
- }
1804
- var message = {
1805
- open: openMessage,
1806
- info: shortcut("info"),
1807
- success: shortcut("success"),
1808
- warning: shortcut("warning"),
1809
- error: shortcut("error"),
1810
- loading: shortcut("loading"),
1811
- destroy(key) {
1812
- if (key !== void 0) {
1813
- closeMessage(key);
1814
- return;
1815
- }
1816
- [...messageItems].forEach((item) => closeMessage(item.key));
1817
- },
1818
- config(config) {
1819
- if (config.duration !== void 0) defaultDuration = config.duration;
1820
- if (config.maxCount !== void 0) maxCount = Math.max(1, config.maxCount);
1821
- }
1822
- };
1823
-
1824
- // src/components/Breadcrumb.tsx
1825
- import { Fragment as Fragment6, jsx as jsx11, jsxs as jsxs11 } from "react/jsx-runtime";
1826
- function Breadcrumb({ items, separator = "/", itemRender, className = "", ...props }) {
1827
- return /* @__PURE__ */ jsx11("nav", { "aria-label": "\u9762\u5305\u5C51\u5BFC\u822A", ...props, className: `sia-breadcrumb ${className}`.trim(), children: /* @__PURE__ */ jsx11("ol", { className: "sia-breadcrumb__list", children: items.map((item, index) => {
1828
- const current = index === items.length - 1;
1829
- const content = /* @__PURE__ */ jsxs11(Fragment6, { children: [
1830
- item.icon ? /* @__PURE__ */ jsx11("span", { className: "sia-breadcrumb__icon", "aria-hidden": "true", children: item.icon }) : null,
1831
- /* @__PURE__ */ jsx11("span", { className: "sia-breadcrumb__title", children: item.title })
1832
- ] });
1833
- const custom = itemRender?.(item, index, items);
1834
- const label = item.disabled ? /* @__PURE__ */ jsx11("span", { className: "sia-breadcrumb__label", "aria-disabled": "true", children: content }) : custom !== void 0 ? /* @__PURE__ */ jsx11("span", { className: "sia-breadcrumb__label", children: custom }) : item.href ? /* @__PURE__ */ jsx11("a", { className: "sia-breadcrumb__label", href: item.href, target: item.target, rel: item.target === "_blank" ? "noopener noreferrer" : void 0, onClick: item.onClick, children: content }) : item.onClick ? /* @__PURE__ */ jsx11(Button, { className: "sia-breadcrumb__label", variant: "text", onClick: item.onClick, children: content }) : /* @__PURE__ */ jsx11("span", { className: "sia-breadcrumb__label", children: content });
1835
- return /* @__PURE__ */ jsxs11("li", { className: `sia-breadcrumb__item${current ? " is-current" : ""}${item.disabled ? " is-disabled" : ""}`, children: [
1836
- /* @__PURE__ */ jsxs11("span", { className: "sia-breadcrumb__entry", "aria-current": current ? "page" : void 0, children: [
1837
- label,
1838
- item.menu?.items.length ? /* @__PURE__ */ jsx11(Dropdown, { menu: item.menu, trigger: ["click"], disabled: item.disabled, children: /* @__PURE__ */ jsx11(Button, { className: "sia-breadcrumb__menu-trigger", variant: "text", size: "small", disabled: item.disabled, "aria-label": `\u5C55\u5F00${typeof item.title === "string" ? item.title : "\u5C42\u7EA7"}\u83DC\u5355`, icon: /* @__PURE__ */ jsx11(Icon, { name: "chevron-down", size: 12 }) }) }) : null
1839
- ] }),
1840
- !current ? /* @__PURE__ */ jsx11("span", { className: "sia-breadcrumb__separator", "aria-hidden": "true", children: item.separator === void 0 ? separator : item.separator }) : null
1841
- ] }, item.key ?? index);
1842
- }) }) });
1843
- }
1844
-
1845
- // src/components/Typography.tsx
1846
- import { useEffect as useEffect7, useLayoutEffect as useLayoutEffect2, useRef as useRef8, useState as useState7 } from "react";
1847
- import { Fragment as Fragment7, jsx as jsx12, jsxs as jsxs12 } from "react/jsx-runtime";
1848
- function TypographyContent({
1849
- as: Tag2 = "span",
1850
- type,
1851
- disabled,
1852
- strong,
1853
- italic,
1854
- underline,
1855
- delete: deleted,
1856
- mark,
1857
- code,
1858
- keyboard,
1859
- copyable,
1860
- editable,
1861
- ellipsis,
1862
- children,
1863
- className = "",
1864
- ...props
1865
- }) {
1866
- const contentRef = useRef8(null);
1867
- const editRef = useRef8(null);
1868
- const timer = useRef8();
1869
- const [localText, setLocalText] = useState7();
1870
- const [draft, setDraft] = useState7("");
1871
- const [editing, setEditing] = useState7(false);
1872
- const [copied, setCopied] = useState7(false);
1873
- const [copyError, setCopyError] = useState7(false);
1874
- const [expanded, setExpanded] = useState7(false);
1875
- const [overflow, setOverflow] = useState7(false);
1876
- const editConfig = typeof editable === "object" ? editable : void 0;
1877
- const content = editConfig?.text ?? localText ?? children;
1878
- const requestedRows = typeof ellipsis === "object" ? ellipsis.rows ?? 1 : 1;
1879
- const rows = Number.isFinite(requestedRows) ? Math.max(1, Math.floor(requestedRows)) : 1;
1880
- const expandable = typeof ellipsis === "object" && ellipsis.expandable;
1881
- useEffect7(() => {
1882
- setLocalText(void 0);
1883
- }, [children]);
1884
- useEffect7(() => () => clearTimeout(timer.current), []);
1885
- useLayoutEffect2(() => {
1886
- const element = contentRef.current;
1887
- if (!element || !ellipsis || editing || expanded) return;
1888
- const measure = () => setOverflow(element.scrollHeight > element.clientHeight + 1 || element.scrollWidth > element.clientWidth + 1);
1889
- measure();
1890
- const observer = new ResizeObserver(measure);
1891
- observer.observe(element);
1892
- return () => observer.disconnect();
1893
- }, [content, ellipsis, rows, editing, expanded]);
1894
- async function copy() {
1895
- try {
1896
- const text = typeof copyable === "object" ? copyable.text ?? contentRef.current?.textContent ?? "" : contentRef.current?.textContent ?? "";
1897
- await navigator.clipboard.writeText(text);
1898
- setCopied(true);
1899
- setCopyError(false);
1900
- clearTimeout(timer.current);
1901
- timer.current = setTimeout(() => setCopied(false), 2e3);
1902
- if (typeof copyable === "object") copyable.onCopy?.();
1903
- } catch {
1904
- setCopyError(true);
1905
- }
1906
- }
1907
- function finish(save) {
1908
- if (save) {
1909
- setLocalText(draft);
1910
- editConfig?.onChange?.(draft);
1911
- }
1912
- setEditing(false);
1913
- requestAnimationFrame(() => editRef.current?.focus());
1914
- }
1915
- let formatted = content;
1916
- if (strong) formatted = /* @__PURE__ */ jsx12("strong", { children: formatted });
1917
- if (italic) formatted = /* @__PURE__ */ jsx12("em", { children: formatted });
1918
- if (underline) formatted = /* @__PURE__ */ jsx12("u", { children: formatted });
1919
- if (deleted) formatted = /* @__PURE__ */ jsx12("del", { children: formatted });
1920
- if (mark) formatted = /* @__PURE__ */ jsx12("mark", { children: formatted });
1921
- if (code) formatted = /* @__PURE__ */ jsx12("code", { children: formatted });
1922
- if (keyboard) formatted = /* @__PURE__ */ jsx12("kbd", { children: formatted });
1923
- return /* @__PURE__ */ jsx12(Tag2, { ...props, "aria-disabled": disabled || void 0, className: `sia-typography sia-typography--${Tag2}${type ? ` sia-typography--${type}` : ""}${disabled ? " is-disabled" : ""} ${className}`.trim(), children: editing ? /* @__PURE__ */ jsxs12("span", { className: "sia-typography__editor", children: [
1924
- /* @__PURE__ */ jsx12(
1925
- TextArea,
1926
- {
1927
- autoFocus: true,
1928
- "aria-label": "\u7F16\u8F91\u6587\u672C",
1929
- value: draft,
1930
- maxLength: editConfig?.maxLength,
1931
- rows: 3,
1932
- onChange: (event) => setDraft(event.target.value),
1933
- onKeyDown: (event) => {
1934
- if (event.nativeEvent.isComposing) return;
1935
- if (event.key === "Escape") {
1936
- event.preventDefault();
1937
- finish(false);
1938
- }
1939
- if (event.key === "Enter" && !event.shiftKey) {
1940
- event.preventDefault();
1941
- finish(true);
1942
- }
1943
- }
1944
- }
1945
- ),
1946
- /* @__PURE__ */ jsxs12("span", { className: "sia-typography__edit-actions", children: [
1947
- /* @__PURE__ */ jsx12(Button, { size: "small", onClick: () => finish(false), children: "\u53D6\u6D88" }),
1948
- /* @__PURE__ */ jsx12(Button, { size: "small", variant: "primary", onClick: () => finish(true), children: "\u4FDD\u5B58" })
1949
- ] })
1950
- ] }) : /* @__PURE__ */ jsxs12(Fragment7, { children: [
1951
- /* @__PURE__ */ jsx12("span", { ref: contentRef, className: ellipsis && !expanded ? "sia-typography__ellipsis" : void 0, style: ellipsis && !expanded ? { "--sia-typography-rows": rows } : void 0, children: formatted }),
1952
- expandable && (overflow || expanded) ? /* @__PURE__ */ jsx12(Button, { variant: "link", size: "small", className: "sia-typography__action", "aria-expanded": expanded, disabled, onClick: () => setExpanded(!expanded), children: expanded ? "\u6536\u8D77" : "\u5C55\u5F00" }) : null,
1953
- editable ? /* @__PURE__ */ jsx12(Button, { ref: editRef, variant: "text", size: "small", className: "sia-typography__action", "aria-label": "\u7F16\u8F91\u6587\u672C", title: "\u7F16\u8F91", disabled, icon: /* @__PURE__ */ jsx12(Icon, { name: "edit", size: 14 }), onClick: () => {
1954
- setDraft(contentRef.current?.textContent ?? "");
1955
- setEditing(true);
1956
- } }) : null,
1957
- copyable ? /* @__PURE__ */ jsx12(Button, { variant: "text", size: "small", className: "sia-typography__action", "aria-label": copied ? "\u5DF2\u590D\u5236" : "\u590D\u5236\u6587\u672C", title: copied ? "\u5DF2\u590D\u5236" : "\u590D\u5236", disabled, icon: /* @__PURE__ */ jsx12(Icon, { name: copied ? "check" : "copy", size: 14 }), onClick: () => void copy() }) : null,
1958
- copyError ? /* @__PURE__ */ jsx12("span", { role: "status", className: "sia-typography--danger", children: "\u590D\u5236\u5931\u8D25\uFF0C\u8BF7\u624B\u52A8\u9009\u62E9\u6587\u672C\u590D\u5236\u3002" }) : null
1959
- ] }) });
1960
- }
1961
- function Title({ level = 1, ...props }) {
1962
- return /* @__PURE__ */ jsx12(TypographyContent, { ...props, as: `h${level}` });
1963
- }
1964
- function Text(props) {
1965
- return /* @__PURE__ */ jsx12(TypographyContent, { ...props });
1966
- }
1967
- function Paragraph(props) {
1968
- return /* @__PURE__ */ jsx12(TypographyContent, { ...props, as: "p" });
1969
- }
1970
- function Link({ disabled, className = "", onClick, href, target, rel, ...props }) {
1971
- return /* @__PURE__ */ jsx12(
1972
- "a",
1973
- {
1974
- ...props,
1975
- href: disabled ? void 0 : href,
1976
- target,
1977
- rel: rel ?? (target === "_blank" ? "noopener noreferrer" : void 0),
1978
- "aria-disabled": disabled || void 0,
1979
- tabIndex: disabled ? -1 : props.tabIndex,
1980
- className: `sia-typography sia-typography--link${disabled ? " is-disabled" : ""} ${className}`.trim(),
1981
- onClick: (event) => {
1982
- if (disabled) event.preventDefault();
1983
- else onClick?.(event);
1984
- }
1985
- }
1986
- );
1987
- }
1988
- function TypographyRoot({ className = "", ...props }) {
1989
- return /* @__PURE__ */ jsx12("div", { ...props, className: `sia-typography ${className}`.trim() });
1990
- }
1991
- var Typography = Object.assign(TypographyRoot, { Title, Text, Paragraph, Link });
1992
-
1993
- // src/components/FloatButton.tsx
1994
- import { createContext as createContext2, forwardRef as forwardRef3, useContext as useContext2, useEffect as useEffect8, useId as useId6, useRef as useRef9, useState as useState8 } from "react";
1995
- import { jsx as jsx13, jsxs as jsxs13 } from "react/jsx-runtime";
1996
- var GroupShape = createContext2(void 0);
1997
- var FloatButtonRoot = forwardRef3(function FloatButton({
1998
- icon,
1999
- description,
2000
- tooltip,
2001
- type = "default",
2002
- shape = "circle",
2003
- badge,
2004
- htmlType = "button",
2005
- className = "",
2006
- style,
2007
- children,
2008
- ...props
2009
- }, ref) {
2010
- const groupShape = useContext2(GroupShape);
2011
- const label = props["aria-label"] ?? (typeof tooltip === "string" ? tooltip : typeof description === "string" ? description : "\u60AC\u6D6E\u64CD\u4F5C");
2012
- const button = /* @__PURE__ */ jsx13(
2013
- "button",
2014
- {
2015
- ...props,
2016
- ref,
2017
- type: htmlType,
2018
- "aria-label": label,
2019
- className: `sia-float-button sia-float-button--${groupShape ?? shape} sia-float-button--${type} ${className}`.trim(),
2020
- children: /* @__PURE__ */ jsx13(Badge, { ...badge, className: "sia-float-button__badge", children: /* @__PURE__ */ jsxs13("span", { className: "sia-float-button__body", children: [
2021
- icon !== null ? /* @__PURE__ */ jsx13("span", { className: "sia-float-button__icon", children: icon ?? /* @__PURE__ */ jsx13(Icon, { name: "question", size: 20 }) }) : null,
2022
- description != null || children != null ? /* @__PURE__ */ jsx13("span", { className: "sia-float-button__description", children: description ?? children }) : null
2023
- ] }) })
2024
- }
2025
- );
2026
- return /* @__PURE__ */ jsx13("span", { className: "sia-float-button-root", style, children: tooltip ? /* @__PURE__ */ jsx13(Tooltip, { title: tooltip, placement: "left", children: button }) : button });
2027
- });
2028
- function FloatButtonGroup({
2029
- shape = "circle",
2030
- type = "default",
2031
- icon,
2032
- closeIcon,
2033
- tooltip,
2034
- trigger,
2035
- placement = "top",
2036
- open,
2037
- defaultOpen = false,
2038
- onOpenChange,
2039
- children,
2040
- className = "",
2041
- onMouseEnter,
2042
- onMouseLeave,
2043
- onKeyDown,
2044
- onBlur,
2045
- ...props
2046
- }) {
2047
- const [visible, setVisible] = useControllableState({ value: open, defaultValue: defaultOpen, onChange: onOpenChange });
2048
- const rootRef = useRef9(null);
2049
- const triggerRef = useRef9(null);
2050
- const id = useId6();
2051
- useEffect8(() => {
2052
- if (!trigger || !visible) return;
2053
- const close = (event) => {
2054
- if (!rootRef.current?.contains(event.target)) setVisible(false);
2055
- };
2056
- document.addEventListener("pointerdown", close);
2057
- return () => document.removeEventListener("pointerdown", close);
2058
- }, [trigger, visible, setVisible]);
2059
- return /* @__PURE__ */ jsx13(GroupShape.Provider, { value: shape, children: /* @__PURE__ */ jsxs13(
2060
- "div",
2061
- {
2062
- ...props,
2063
- ref: rootRef,
2064
- role: "group",
2065
- className: `sia-float-button-group sia-float-button-group--${shape} sia-float-button-group--${placement}${trigger ? " sia-float-button-group--menu" : ""} ${className}`.trim(),
2066
- onMouseEnter: (event) => {
2067
- onMouseEnter?.(event);
2068
- if (trigger === "hover") setVisible(true);
2069
- },
2070
- onMouseLeave: (event) => {
2071
- onMouseLeave?.(event);
2072
- if (trigger === "hover" && !event.currentTarget.contains(document.activeElement)) setVisible(false);
2073
- },
2074
- onBlur: (event) => {
2075
- onBlur?.(event);
2076
- if (trigger === "hover" && !event.currentTarget.contains(event.relatedTarget) && !event.currentTarget.matches(":hover")) setVisible(false);
2077
- },
2078
- onKeyDown: (event) => {
2079
- onKeyDown?.(event);
2080
- if (!event.defaultPrevented && trigger && event.key === "Escape") {
2081
- setVisible(false);
2082
- triggerRef.current?.focus();
2083
- }
2084
- },
2085
- children: [
2086
- !trigger || visible ? /* @__PURE__ */ jsx13("div", { id, className: "sia-float-button-group__list", children }) : null,
2087
- trigger ? /* @__PURE__ */ jsx13(
2088
- FloatButtonRoot,
2089
- {
2090
- ref: triggerRef,
2091
- type,
2092
- icon: visible ? closeIcon ?? /* @__PURE__ */ jsx13(Icon, { name: "close", size: 20 }) : icon ?? /* @__PURE__ */ jsx13(Icon, { name: "plus", size: 20 }),
2093
- tooltip,
2094
- "aria-label": visible ? "\u6536\u8D77\u60AC\u6D6E\u83DC\u5355" : "\u5C55\u5F00\u60AC\u6D6E\u83DC\u5355",
2095
- "aria-expanded": visible,
2096
- "aria-controls": visible ? id : void 0,
2097
- onClick: () => setVisible(!visible)
2098
- }
2099
- ) : null
2100
- ]
2101
- }
2102
- ) });
2103
- }
2104
- function FloatButtonBackTop({ target, visibilityHeight = 400, behavior = "smooth", onClick, icon, tooltip = "\u8FD4\u56DE\u9876\u90E8", ...props }) {
2105
- const [visible, setVisible] = useState8(false);
2106
- useEffect8(() => {
2107
- const element = target ? target() : window;
2108
- if (!element) return;
2109
- const update = () => setVisible((element === window ? window.scrollY : element.scrollTop) >= visibilityHeight);
2110
- update();
2111
- element.addEventListener("scroll", update, { passive: true });
2112
- return () => element.removeEventListener("scroll", update);
2113
- }, [target, visibilityHeight]);
2114
- if (!visible) return null;
2115
- return /* @__PURE__ */ jsx13(FloatButtonRoot, { ...props, tooltip, icon: icon ?? /* @__PURE__ */ jsx13(Icon, { name: "arrow-up", size: 20 }), onClick: (event) => {
2116
- onClick?.(event);
2117
- if (event.defaultPrevented) return;
2118
- const element = target ? target() : window;
2119
- element?.scrollTo({ top: 0, behavior: window.matchMedia("(prefers-reduced-motion: reduce)").matches ? "auto" : behavior });
2120
- } });
2121
- }
2122
- var FloatButton2 = Object.assign(FloatButtonRoot, { Group: FloatButtonGroup, BackTop: FloatButtonBackTop });
2123
-
2124
- export {
2125
- Card,
2126
- Tag,
2127
- Tabs,
2128
- Checkbox2 as Checkbox,
2129
- Tooltip,
2130
- Popover,
2131
- Timeline,
2132
- Tree,
2133
- Popconfirm,
2134
- Progress,
2135
- Spin,
2136
- Watermark,
2137
- notification,
2138
- Tour,
2139
- useOverlayLifecycle,
2140
- OverlayPortal,
2141
- Rate,
2142
- Badge,
2143
- Calendar,
2144
- Carousel,
2145
- Collapse,
2146
- ImagePreview,
2147
- Image,
2148
- Upload,
2149
- Modal2 as Modal,
2150
- message,
2151
- Breadcrumb,
2152
- Typography,
2153
- FloatButton2 as FloatButton
2154
- };