flowcloudai-ui 0.0.0 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -1,7 +1,9 @@
1
1
  "use strict";
2
+ var __create = Object.create;
2
3
  var __defProp = Object.defineProperty;
3
4
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
5
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
5
7
  var __hasOwnProp = Object.prototype.hasOwnProperty;
6
8
  var __export = (target, all) => {
7
9
  for (var name in all)
@@ -15,37 +17,2286 @@ var __copyProps = (to, from, except, desc) => {
15
17
  }
16
18
  return to;
17
19
  };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
18
28
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
29
 
20
30
  // src/index.ts
21
31
  var index_exports = {};
22
32
  __export(index_exports, {
23
- Button: () => Button
33
+ AlertProvider: () => AlertProvider,
34
+ Avatar: () => Avatar,
35
+ Button: () => Button,
36
+ ButtonGroup: () => ButtonGroup,
37
+ ButtonToolbar: () => ButtonToolbar,
38
+ Card: () => Card,
39
+ Chat: () => Chat,
40
+ CheckButton: () => CheckButton,
41
+ DeleteDialog: () => DeleteDialog,
42
+ Input: () => Input,
43
+ ListGroup: () => ListGroup,
44
+ ListGroupItem: () => ListGroupItem,
45
+ OrphanDialog: () => OrphanDialog,
46
+ RollingBox: () => RollingBox,
47
+ Select: () => Select,
48
+ SideBar: () => SideBar,
49
+ Slider: () => Slider,
50
+ Tabs: () => Tabs,
51
+ ThemeProvider: () => ThemeProvider,
52
+ Tree: () => Tree,
53
+ VirtualList: () => VirtualList,
54
+ findNodeInfo: () => findNodeInfo,
55
+ flatToTree: () => flatToTree,
56
+ isDescendantOf: () => isDescendantOf,
57
+ lazyLoad: () => lazyLoad,
58
+ useAlert: () => useAlert,
59
+ useTheme: () => useTheme
24
60
  });
25
61
  module.exports = __toCommonJS(index_exports);
26
62
 
27
- // src/Button.tsx
63
+ // src/ThemeProvider.tsx
64
+ var import_react = require("react");
28
65
  var import_jsx_runtime = require("react/jsx-runtime");
29
- function Button({ children, style, ...props }) {
30
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
66
+ var ThemeContext = (0, import_react.createContext)(null);
67
+ function useTheme() {
68
+ const ctx = (0, import_react.useContext)(ThemeContext);
69
+ if (!ctx) throw new Error("useTheme must be used within <ThemeProvider>");
70
+ return ctx;
71
+ }
72
+ function getSystemTheme() {
73
+ return typeof window !== "undefined" && window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
74
+ }
75
+ function ThemeProvider({ children, defaultTheme = "system", target }) {
76
+ const [theme, setTheme] = (0, import_react.useState)(defaultTheme);
77
+ const [systemTheme, setSystemTheme] = (0, import_react.useState)(getSystemTheme);
78
+ const resolvedTheme = theme === "system" ? systemTheme : theme;
79
+ (0, import_react.useEffect)(() => {
80
+ const el = target ?? document.documentElement;
81
+ el.classList.remove("theme-light", "theme-dark");
82
+ el.classList.add(`theme-${resolvedTheme}`);
83
+ el.setAttribute("data-theme", resolvedTheme);
84
+ if (resolvedTheme === "dark") {
85
+ document.body.style.backgroundColor = "#0F0F0F";
86
+ document.body.style.color = "#E8E8E6";
87
+ } else {
88
+ document.body.style.backgroundColor = "";
89
+ document.body.style.color = "";
90
+ }
91
+ }, [resolvedTheme, target]);
92
+ (0, import_react.useEffect)(() => {
93
+ if (theme !== "system") return;
94
+ const mq = window.matchMedia("(prefers-color-scheme: dark)");
95
+ const handler = () => setSystemTheme(mq.matches ? "dark" : "light");
96
+ mq.addEventListener("change", handler);
97
+ return () => mq.removeEventListener("change", handler);
98
+ }, [theme]);
99
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ThemeContext.Provider, { value: { theme, resolvedTheme, setTheme }, children });
100
+ }
101
+
102
+ // src/components/Button/Button.tsx
103
+ var import_jsx_runtime2 = require("react/jsx-runtime");
104
+ function Button({
105
+ variant = "primary",
106
+ size = "md",
107
+ disabled = false,
108
+ loading = false,
109
+ block = false,
110
+ circle = false,
111
+ iconOnly = false,
112
+ iconLeft,
113
+ iconRight,
114
+ className,
115
+ children,
116
+ ...props
117
+ }) {
118
+ const classNames = [
119
+ "fc-btn",
120
+ `fc-btn--${variant}`,
121
+ `fc-btn--${size}`,
122
+ block && "fc-btn--block",
123
+ circle && "fc-btn--circle",
124
+ iconOnly && "fc-btn--icon-only",
125
+ loading && "is-loading",
126
+ disabled && "is-disabled",
127
+ className
128
+ ].filter(Boolean).join(" ");
129
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
31
130
  "button",
32
131
  {
132
+ className: classNames,
133
+ disabled: disabled || loading,
33
134
  ...props,
34
- style: {
35
- padding: "10px 16px",
36
- border: "none",
37
- borderRadius: 10,
38
- background: "#863bff",
39
- color: "#fff",
40
- cursor: "pointer",
41
- fontSize: 14,
42
- ...style
43
- },
44
- children
135
+ children: [
136
+ iconLeft && !loading && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "fc-btn__icon fc-btn__icon--left", children: iconLeft }),
137
+ children,
138
+ iconRight && !loading && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "fc-btn__icon fc-btn__icon--right", children: iconRight })
139
+ ]
140
+ }
141
+ );
142
+ }
143
+ function ButtonGroup({ children, className, ...props }) {
144
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: `fc-btn-group ${className ?? ""}`, ...props, children });
145
+ }
146
+ function ButtonToolbar({
147
+ children,
148
+ align = "left",
149
+ className,
150
+ ...props
151
+ }) {
152
+ const alignClass = {
153
+ left: "",
154
+ center: "fc-btn-toolbar--center",
155
+ right: "fc-btn-toolbar--right",
156
+ between: "fc-btn-toolbar--between"
157
+ }[align];
158
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: `fc-btn-toolbar ${alignClass} ${className ?? ""}`, ...props, children });
159
+ }
160
+
161
+ // src/components/Button/CheckButton.tsx
162
+ var React = __toESM(require("react"), 1);
163
+ var import_jsx_runtime3 = require("react/jsx-runtime");
164
+ function CheckButton({
165
+ checked: controlledChecked,
166
+ defaultChecked = false,
167
+ onChange,
168
+ disabled = false,
169
+ size = "md",
170
+ labelLeft,
171
+ labelRight,
172
+ className = "",
173
+ style
174
+ }) {
175
+ const isControlled = controlledChecked !== void 0;
176
+ const [internalChecked, setInternalChecked] = React.useState(defaultChecked);
177
+ const checked = isControlled ? controlledChecked : internalChecked;
178
+ const toggle = () => {
179
+ if (disabled) return;
180
+ const next = !checked;
181
+ if (!isControlled) setInternalChecked(next);
182
+ onChange?.(next);
183
+ };
184
+ const handleKeyDown = (e) => {
185
+ if (e.key === "Enter" || e.key === " ") {
186
+ e.preventDefault();
187
+ toggle();
188
+ }
189
+ };
190
+ const cls = [
191
+ "fc-check",
192
+ `fc-check--${size}`,
193
+ checked && "fc-check--checked",
194
+ disabled && "fc-check--disabled",
195
+ className
196
+ ].filter(Boolean).join(" ");
197
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
198
+ "div",
199
+ {
200
+ className: cls,
201
+ style,
202
+ role: "switch",
203
+ "aria-checked": checked,
204
+ tabIndex: disabled ? -1 : 0,
205
+ onClick: toggle,
206
+ onKeyDown: handleKeyDown,
207
+ children: [
208
+ labelLeft && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "fc-check__label", children: labelLeft }),
209
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "fc-check__track", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "fc-check__thumb", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "fc-check__thumb-inner" }) }) }),
210
+ labelRight && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "fc-check__label", children: labelRight })
211
+ ]
212
+ }
213
+ );
214
+ }
215
+
216
+ // src/components/Box/RollingBox.tsx
217
+ var React2 = __toESM(require("react"), 1);
218
+ var import_jsx_runtime4 = require("react/jsx-runtime");
219
+ function RollingBox({
220
+ showThumb = "auto",
221
+ horizontal = false,
222
+ vertical = true,
223
+ thumbSize = "normal",
224
+ showTrack = false,
225
+ children,
226
+ className,
227
+ ...props
228
+ }) {
229
+ const containerRef = React2.useRef(null);
230
+ const [isScrolling, setIsScrolling] = React2.useState(false);
231
+ const scrollTimeoutRef = React2.useRef(null);
232
+ const handleScroll = React2.useCallback(() => {
233
+ if (showThumb !== "auto") return;
234
+ setIsScrolling(true);
235
+ if (scrollTimeoutRef.current) {
236
+ clearTimeout(scrollTimeoutRef.current);
237
+ }
238
+ scrollTimeoutRef.current = setTimeout(() => {
239
+ setIsScrolling(false);
240
+ }, 1e3);
241
+ }, [showThumb]);
242
+ React2.useEffect(() => {
243
+ return () => {
244
+ if (scrollTimeoutRef.current) {
245
+ clearTimeout(scrollTimeoutRef.current);
246
+ }
247
+ };
248
+ }, []);
249
+ const resolvedDirection = horizontal ? "horizontal" : "vertical";
250
+ const classNames = [
251
+ "fc-roll",
252
+ `fc-roll--thumb-${showThumb}`,
253
+ `fc-roll--size-${thumbSize}`,
254
+ `fc-roll--${resolvedDirection}`,
255
+ showTrack && "fc-roll--track",
256
+ isScrolling && "fc-roll--scrolling",
257
+ className
258
+ ].filter(Boolean).join(" ");
259
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
260
+ "div",
261
+ {
262
+ ref: containerRef,
263
+ className: classNames,
264
+ onScroll: handleScroll,
265
+ ...props,
266
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "fc-roll__content", children })
267
+ }
268
+ );
269
+ }
270
+
271
+ // src/components/Bar/SideBar.tsx
272
+ var React3 = __toESM(require("react"), 1);
273
+ var import_jsx_runtime5 = require("react/jsx-runtime");
274
+ function SideBar({
275
+ items,
276
+ selectedKeys: controlledSelected,
277
+ defaultSelectedKeys = [],
278
+ openKeys: controlledOpen,
279
+ defaultOpenKeys = [],
280
+ onSelect,
281
+ onOpenChange,
282
+ collapsed: controlledCollapsed,
283
+ defaultCollapsed = false,
284
+ onCollapse,
285
+ className,
286
+ width = 240,
287
+ collapsedWidth = 64
288
+ }) {
289
+ const [internalSelected, setInternalSelected] = React3.useState(defaultSelectedKeys);
290
+ const [internalOpen, setInternalOpen] = React3.useState(defaultOpenKeys);
291
+ const [internalCollapsed, setInternalCollapsed] = React3.useState(defaultCollapsed);
292
+ const currentSelected = controlledSelected ?? internalSelected;
293
+ const currentOpen = controlledOpen ?? internalOpen;
294
+ const currentCollapsed = controlledCollapsed ?? internalCollapsed;
295
+ const updateOpen = React3.useCallback((next) => {
296
+ if (controlledOpen === void 0) setInternalOpen(next);
297
+ onOpenChange?.(next);
298
+ }, [controlledOpen, onOpenChange]);
299
+ const toggleOpen = React3.useCallback((key) => {
300
+ const next = currentOpen.includes(key) ? currentOpen.filter((k) => k !== key) : [...currentOpen, key];
301
+ updateOpen(next);
302
+ }, [currentOpen, updateOpen]);
303
+ const handleSelect = React3.useCallback((key, item) => {
304
+ if (item.disabled) return;
305
+ const next = [key];
306
+ if (controlledSelected === void 0) setInternalSelected(next);
307
+ onSelect?.(next);
308
+ }, [controlledSelected, onSelect]);
309
+ const toggleCollapse = React3.useCallback(() => {
310
+ const next = !currentCollapsed;
311
+ if (controlledCollapsed === void 0) setInternalCollapsed(next);
312
+ onCollapse?.(next);
313
+ }, [currentCollapsed, controlledCollapsed, onCollapse]);
314
+ const handleItemClick = React3.useCallback((item) => {
315
+ if (item.children?.length) {
316
+ toggleOpen(item.key);
317
+ } else {
318
+ handleSelect(item.key, item);
319
+ }
320
+ }, [toggleOpen, handleSelect]);
321
+ const handleItemKeyDown = React3.useCallback((e, item) => {
322
+ if (e.key === "Enter" || e.key === " ") {
323
+ e.preventDefault();
324
+ handleItemClick(item);
325
+ }
326
+ }, [handleItemClick]);
327
+ const renderMenuItem = (item, level = 0) => {
328
+ const hasChildren = (item.children?.length ?? 0) > 0;
329
+ const isOpen = currentOpen.includes(item.key);
330
+ const isSelected = currentSelected.includes(item.key);
331
+ const itemClassName = [
332
+ "fc-sidebar__item",
333
+ `fc-sidebar__item--level-${level}`,
334
+ hasChildren && "fc-sidebar__item--parent",
335
+ isOpen && "fc-sidebar__item--open",
336
+ isSelected && "fc-sidebar__item--selected",
337
+ item.disabled && "fc-sidebar__item--disabled"
338
+ ].filter(Boolean).join(" ");
339
+ const Tag = item.href ? "a" : "div";
340
+ const interactiveProps = {
341
+ role: hasChildren ? "treeitem" : "menuitem",
342
+ tabIndex: item.disabled ? -1 : 0,
343
+ "aria-selected": isSelected || void 0,
344
+ "aria-expanded": hasChildren ? isOpen : void 0,
345
+ "aria-disabled": item.disabled || void 0,
346
+ onClick: () => handleItemClick(item),
347
+ onKeyDown: (e) => handleItemKeyDown(e, item),
348
+ ...item.href ? { href: item.href } : {}
349
+ };
350
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "fc-sidebar__item-wrapper", children: [
351
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(Tag, { className: itemClassName, ...interactiveProps, children: [
352
+ item.icon && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "fc-sidebar__icon", "aria-hidden": "true", children: item.icon }),
353
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "fc-sidebar__label", children: item.label }),
354
+ hasChildren && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "fc-sidebar__arrow", "aria-hidden": "true", children: "\u25B6" })
355
+ ] }),
356
+ hasChildren && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
357
+ "div",
358
+ {
359
+ className: [
360
+ "fc-sidebar__submenu",
361
+ isOpen && "fc-sidebar__submenu--open"
362
+ ].filter(Boolean).join(" "),
363
+ role: "group",
364
+ children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "fc-sidebar__submenu-inner", children: item.children.map((child) => renderMenuItem(child, level + 1)) })
365
+ }
366
+ )
367
+ ] }, item.key);
368
+ };
369
+ const sidebarStyle = {
370
+ "--sidebar-width": `${currentCollapsed ? collapsedWidth : width}px`,
371
+ "--sidebar-collapsed-width": `${collapsedWidth}px`
372
+ };
373
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
374
+ "aside",
375
+ {
376
+ className: [
377
+ "fc-sidebar",
378
+ currentCollapsed && "fc-sidebar--collapsed",
379
+ className
380
+ ].filter(Boolean).join(" "),
381
+ style: sidebarStyle,
382
+ role: "navigation",
383
+ "aria-label": "Sidebar",
384
+ children: [
385
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "fc-sidebar__header", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
386
+ "button",
387
+ {
388
+ className: "fc-sidebar__collapse-btn",
389
+ onClick: toggleCollapse,
390
+ "aria-label": currentCollapsed ? "\u5C55\u5F00\u4FA7\u680F" : "\u6536\u8D77\u4FA7\u680F",
391
+ "aria-expanded": !currentCollapsed,
392
+ children: currentCollapsed ? "\u2192" : "\u2190"
393
+ }
394
+ ) }),
395
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("nav", { className: "fc-sidebar__menu", role: "tree", children: items.map((item) => renderMenuItem(item)) })
396
+ ]
397
+ }
398
+ );
399
+ }
400
+
401
+ // src/components/Input/Input.tsx
402
+ var React4 = __toESM(require("react"), 1);
403
+ var import_jsx_runtime6 = require("react/jsx-runtime");
404
+ var Input = React4.forwardRef(({
405
+ size = "md",
406
+ status = "default",
407
+ prefix,
408
+ suffix,
409
+ allowClear = false,
410
+ passwordToggle = false,
411
+ addonBefore,
412
+ addonAfter,
413
+ helperText,
414
+ className = "",
415
+ type: initialType = "text",
416
+ value,
417
+ defaultValue,
418
+ onChange,
419
+ onClear,
420
+ disabled,
421
+ ...props
422
+ }, ref) => {
423
+ const [type, setType] = React4.useState(initialType);
424
+ const [internalValue, setInternalValue] = React4.useState(defaultValue || "");
425
+ const isControlled = value !== void 0;
426
+ const currentValue = isControlled ? value : internalValue;
427
+ const handleChange = (e) => {
428
+ if (!isControlled) setInternalValue(e.target.value);
429
+ onChange?.(e);
430
+ };
431
+ const handleClear = () => {
432
+ if (!isControlled) setInternalValue("");
433
+ onClear?.();
434
+ const event = { target: { value: "" } };
435
+ onChange?.(event);
436
+ };
437
+ const togglePassword = () => {
438
+ setType((prev) => prev === "password" ? "text" : "password");
439
+ };
440
+ const showClear = allowClear && currentValue && !disabled;
441
+ const showPasswordToggle = passwordToggle && initialType === "password" && !disabled;
442
+ const classNames = [
443
+ "fc-input",
444
+ `fc-input--${size}`,
445
+ `fc-input--${status}`,
446
+ (prefix || addonBefore) && "fc-input--has-prefix",
447
+ (suffix || addonAfter || showClear || showPasswordToggle) && "fc-input--has-suffix",
448
+ addonBefore && "fc-input--addon-before",
449
+ addonAfter && "fc-input--addon-after",
450
+ disabled && "fc-input--disabled",
451
+ className
452
+ ].filter(Boolean).join(" ");
453
+ const input = /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
454
+ "input",
455
+ {
456
+ ref,
457
+ type,
458
+ value: currentValue,
459
+ onChange: handleChange,
460
+ disabled,
461
+ className: "fc-input__field",
462
+ ...props
463
+ }
464
+ );
465
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: classNames, children: [
466
+ addonBefore && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "fc-input__addon fc-input__addon--before", children: addonBefore }),
467
+ prefix && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "fc-input__prefix", children: prefix }),
468
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "fc-input__wrapper", children: [
469
+ input,
470
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: "fc-input__actions", children: [
471
+ showClear && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
472
+ "button",
473
+ {
474
+ type: "button",
475
+ className: "fc-input__action fc-input__clear",
476
+ onClick: handleClear,
477
+ tabIndex: -1,
478
+ children: "\u2715"
479
+ }
480
+ ),
481
+ showPasswordToggle && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
482
+ "button",
483
+ {
484
+ type: "button",
485
+ className: "fc-input__action fc-input__eye",
486
+ onClick: togglePassword,
487
+ tabIndex: -1,
488
+ children: type === "password" ? "\u{1F441}" : "\u{1F648}"
489
+ }
490
+ ),
491
+ suffix && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "fc-input__suffix", children: suffix })
492
+ ] })
493
+ ] }),
494
+ addonAfter && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "fc-input__addon fc-input__addon--after", children: addonAfter }),
495
+ helperText && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: `fc-input__helper fc-input__helper--${status}`, children: helperText })
496
+ ] });
497
+ });
498
+ Input.displayName = "Input";
499
+
500
+ // src/components/Slider/Slider.tsx
501
+ var React5 = __toESM(require("react"), 1);
502
+ var import_jsx_runtime7 = require("react/jsx-runtime");
503
+ function Slider({
504
+ value: controlledValue,
505
+ defaultValue,
506
+ onChange,
507
+ min = 0,
508
+ max = 100,
509
+ step = 1,
510
+ range = false,
511
+ orientation = "horizontal",
512
+ disabled = false,
513
+ marks,
514
+ tooltip = false,
515
+ className = ""
516
+ }) {
517
+ const trackRef = React5.useRef(null);
518
+ const draggingRef = React5.useRef(null);
519
+ const [dragging, setDragging] = React5.useState(null);
520
+ const initialValue = defaultValue ?? (range ? [min, max] : min);
521
+ const [internalValue, setInternalValue] = React5.useState(initialValue);
522
+ const isControlled = controlledValue !== void 0;
523
+ const currentValue = isControlled ? controlledValue : internalValue;
524
+ const getPercent = (val) => Math.max(0, Math.min(100, (val - min) / (max - min) * 100));
525
+ const getValueFromPercent = (percent) => {
526
+ const raw = min + percent / 100 * (max - min);
527
+ const stepped = Math.round(raw / step) * step;
528
+ return Math.max(min, Math.min(max, stepped));
529
+ };
530
+ const handleMove = React5.useCallback((clientX, clientY) => {
531
+ if (!trackRef.current || draggingRef.current === null || disabled) return;
532
+ const rect = trackRef.current.getBoundingClientRect();
533
+ const percent = orientation === "horizontal" ? (clientX - rect.left) / rect.width * 100 : (rect.bottom - clientY) / rect.height * 100;
534
+ const newValue = getValueFromPercent(Math.max(0, Math.min(100, percent)));
535
+ let nextValue;
536
+ if (range) {
537
+ const [start, end] = currentValue;
538
+ nextValue = draggingRef.current === 0 ? [Math.min(newValue, end), end] : [start, Math.max(newValue, start)];
539
+ } else {
540
+ nextValue = newValue;
541
+ }
542
+ if (!isControlled) setInternalValue(nextValue);
543
+ onChange?.(nextValue);
544
+ }, [disabled, orientation, range, currentValue, isControlled, onChange, min, max, step]);
545
+ const handleMouseDown = (index) => (e) => {
546
+ if (disabled) return;
547
+ e.preventDefault();
548
+ draggingRef.current = index;
549
+ setDragging(index);
550
+ const handleMouseMove = (e2) => handleMove(e2.clientX, e2.clientY);
551
+ const handleMouseUp = () => {
552
+ draggingRef.current = null;
553
+ setDragging(null);
554
+ document.removeEventListener("mousemove", handleMouseMove);
555
+ document.removeEventListener("mouseup", handleMouseUp);
556
+ };
557
+ document.addEventListener("mousemove", handleMouseMove);
558
+ document.addEventListener("mouseup", handleMouseUp);
559
+ };
560
+ const handleTrackClick = (e) => {
561
+ if (disabled || draggingRef.current !== null) return;
562
+ handleMove(e.clientX, e.clientY);
563
+ };
564
+ const [startVal, endVal] = range ? currentValue : [min, currentValue];
565
+ const startPercent = getPercent(startVal);
566
+ const endPercent = getPercent(endVal);
567
+ const isHorizontal = orientation === "horizontal";
568
+ const thumbStyle = (percent) => isHorizontal ? { left: `${percent}%` } : { bottom: `${percent}%` };
569
+ const cls = [
570
+ "fc-slider",
571
+ `fc-slider--${orientation}`,
572
+ range && "fc-slider--range",
573
+ disabled && "fc-slider--disabled",
574
+ dragging !== null && "fc-slider--dragging",
575
+ className
576
+ ].filter(Boolean).join(" ");
577
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: cls, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
578
+ "div",
579
+ {
580
+ ref: trackRef,
581
+ className: "fc-slider__track",
582
+ onClick: handleTrackClick,
583
+ children: [
584
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
585
+ "div",
586
+ {
587
+ className: "fc-slider__fill",
588
+ style: isHorizontal ? { left: `${startPercent}%`, width: `${endPercent - startPercent}%` } : { bottom: `${startPercent}%`, height: `${endPercent - startPercent}%` }
589
+ }
590
+ ),
591
+ range && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
592
+ "div",
593
+ {
594
+ className: `fc-slider__thumb ${dragging === 0 ? "fc-slider__thumb--active" : ""}`,
595
+ style: thumbStyle(startPercent),
596
+ onMouseDown: handleMouseDown(0),
597
+ children: tooltip && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "fc-slider__tooltip", children: startVal })
598
+ }
599
+ ),
600
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
601
+ "div",
602
+ {
603
+ className: `fc-slider__thumb ${dragging === (range ? 1 : 0) ? "fc-slider__thumb--active" : ""}`,
604
+ style: thumbStyle(endPercent),
605
+ onMouseDown: handleMouseDown(range ? 1 : 0),
606
+ children: tooltip && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "fc-slider__tooltip", children: endVal })
607
+ }
608
+ ),
609
+ marks && Object.entries(marks).map(([val, label]) => /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
610
+ "div",
611
+ {
612
+ className: "fc-slider__mark",
613
+ style: thumbStyle(getPercent(Number(val))),
614
+ children: [
615
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "fc-slider__mark-dot" }),
616
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "fc-slider__mark-label", children: label })
617
+ ]
618
+ },
619
+ val
620
+ ))
621
+ ]
45
622
  }
623
+ ) });
624
+ }
625
+
626
+ // src/components/Select/Select.tsx
627
+ var React6 = __toESM(require("react"), 1);
628
+ var import_jsx_runtime8 = require("react/jsx-runtime");
629
+ function Select({
630
+ options,
631
+ value: controlledValue,
632
+ defaultValue,
633
+ onChange,
634
+ placeholder = "\u8BF7\u9009\u62E9",
635
+ searchable = false,
636
+ multiple = false,
637
+ disabled = false,
638
+ className = "",
639
+ virtualScroll = false,
640
+ virtualItemHeight = 32,
641
+ maxHeight = 256
642
+ }) {
643
+ const [isOpen, setIsOpen] = React6.useState(false);
644
+ const [searchValue, setSearchValue] = React6.useState("");
645
+ const [highlightedIndex, setHighlightedIndex] = React6.useState(0);
646
+ const containerRef = React6.useRef(null);
647
+ const listRef = React6.useRef(null);
648
+ const isControlled = controlledValue !== void 0;
649
+ const [internalValue, setInternalValue] = React6.useState(
650
+ defaultValue ?? (multiple ? [] : void 0)
46
651
  );
652
+ const currentValue = isControlled ? controlledValue : internalValue;
653
+ const groupedOptions = React6.useMemo(() => {
654
+ const filtered = searchable && searchValue ? options.filter((o) => o.label.toLowerCase().includes(searchValue.toLowerCase())) : options;
655
+ const groups = {};
656
+ filtered.forEach((opt) => {
657
+ const group = opt.group || "";
658
+ if (!groups[group]) groups[group] = [];
659
+ groups[group].push(opt);
660
+ });
661
+ return groups;
662
+ }, [options, searchValue, searchable]);
663
+ const flatOptions = React6.useMemo(() => {
664
+ return Object.values(groupedOptions).flat();
665
+ }, [groupedOptions]);
666
+ const [scrollTop, setScrollTop] = React6.useState(0);
667
+ const visibleCount = Math.ceil(maxHeight / virtualItemHeight);
668
+ const startIndex = Math.floor(scrollTop / virtualItemHeight);
669
+ const endIndex = Math.min(startIndex + visibleCount + 1, flatOptions.length);
670
+ const visibleOptions = virtualScroll ? flatOptions.slice(startIndex, endIndex) : flatOptions;
671
+ const totalHeight = flatOptions.length * virtualItemHeight;
672
+ const offsetY = startIndex * virtualItemHeight;
673
+ const handleSelect = (option) => {
674
+ if (option.disabled) return;
675
+ let nextValue;
676
+ if (multiple) {
677
+ const arr = currentValue || [];
678
+ const exists = arr.includes(option.value);
679
+ nextValue = exists ? arr.filter((v) => v !== option.value) : [...arr, option.value];
680
+ } else {
681
+ nextValue = option.value;
682
+ setIsOpen(false);
683
+ }
684
+ if (!isControlled) setInternalValue(nextValue);
685
+ onChange?.(nextValue);
686
+ if (!multiple) setSearchValue("");
687
+ };
688
+ const isSelected = (value) => {
689
+ if (multiple) return currentValue?.includes(value);
690
+ return currentValue === value;
691
+ };
692
+ const displayLabel = () => {
693
+ if (multiple) {
694
+ const count = currentValue?.length || 0;
695
+ return count > 0 ? `\u5DF2\u9009\u62E9 ${count} \u9879` : placeholder;
696
+ }
697
+ const selected = options.find((o) => o.value === currentValue);
698
+ return selected?.label || placeholder;
699
+ };
700
+ React6.useEffect(() => {
701
+ const handleClick = (e) => {
702
+ if (!containerRef.current?.contains(e.target)) {
703
+ setIsOpen(false);
704
+ }
705
+ };
706
+ document.addEventListener("mousedown", handleClick);
707
+ return () => document.removeEventListener("mousedown", handleClick);
708
+ }, []);
709
+ const classNames = [
710
+ "fc-select",
711
+ isOpen && "fc-select--open",
712
+ multiple && "fc-select--multiple",
713
+ disabled && "fc-select--disabled",
714
+ className
715
+ ].filter(Boolean).join(" ");
716
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { ref: containerRef, className: classNames, children: [
717
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
718
+ "div",
719
+ {
720
+ className: "fc-select__trigger",
721
+ onClick: () => !disabled && setIsOpen(!isOpen),
722
+ children: [
723
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: `fc-select__value ${!currentValue && "fc-select__value--placeholder"}`, children: displayLabel() }),
724
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "fc-select__arrow", children: "\u25BC" })
725
+ ]
726
+ }
727
+ ),
728
+ isOpen && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "fc-select__dropdown", children: [
729
+ searchable && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "fc-select__search", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
730
+ "input",
731
+ {
732
+ type: "text",
733
+ value: searchValue,
734
+ onChange: (e) => setSearchValue(e.target.value),
735
+ placeholder: "\u641C\u7D22...",
736
+ className: "fc-select__search-input",
737
+ autoFocus: true
738
+ }
739
+ ) }),
740
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
741
+ "div",
742
+ {
743
+ ref: listRef,
744
+ className: "fc-select__list",
745
+ style: { maxHeight },
746
+ onScroll: (e) => virtualScroll && setScrollTop(e.target.scrollTop),
747
+ children: [
748
+ virtualScroll && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { style: { height: totalHeight, position: "relative" }, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { style: { transform: `translateY(${offsetY}px)` }, children: renderOptions(visibleOptions, startIndex) }) }),
749
+ !virtualScroll && renderGroupedOptions()
750
+ ]
751
+ }
752
+ )
753
+ ] })
754
+ ] });
755
+ function renderOptions(opts, baseIndex = 0) {
756
+ return opts.map((option, idx) => {
757
+ const actualIndex = baseIndex + idx;
758
+ const selected = isSelected(option.value);
759
+ const highlighted = actualIndex === highlightedIndex;
760
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
761
+ "div",
762
+ {
763
+ className: [
764
+ "fc-select__option",
765
+ selected && "fc-select__option--selected",
766
+ option.disabled && "fc-select__option--disabled",
767
+ highlighted && "fc-select__option--highlighted"
768
+ ].filter(Boolean).join(" "),
769
+ onClick: () => handleSelect(option),
770
+ onMouseEnter: () => setHighlightedIndex(actualIndex),
771
+ style: { height: virtualItemHeight },
772
+ children: [
773
+ multiple && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: `fc-select__checkbox ${selected && "fc-select__checkbox--checked"}`, children: selected && "\u2713" }),
774
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "fc-select__option-label", children: highlightText(option.label, searchValue) }),
775
+ !multiple && selected && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "fc-select__check", children: "\u2713" })
776
+ ]
777
+ },
778
+ option.value
779
+ );
780
+ });
781
+ }
782
+ function renderGroupedOptions() {
783
+ return Object.entries(groupedOptions).map(([group, opts]) => /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "fc-select__group", children: [
784
+ group && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "fc-select__group-label", children: group }),
785
+ renderOptions(opts)
786
+ ] }, group));
787
+ }
788
+ function highlightText(text, highlight) {
789
+ if (!highlight) return text;
790
+ const parts = text.split(new RegExp(`(${highlight})`, "gi"));
791
+ return parts.map(
792
+ (part, i) => part.toLowerCase() === highlight.toLowerCase() ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("mark", { className: "fc-select__highlight", children: part }, i) : part
793
+ );
794
+ }
795
+ }
796
+
797
+ // src/components/Tree/Tree.tsx
798
+ var import_react3 = require("react");
799
+ var import_core = require("@dnd-kit/core");
800
+
801
+ // src/components/Tree/DeleteDialog.tsx
802
+ var import_react2 = require("react");
803
+ var import_jsx_runtime9 = require("react/jsx-runtime");
804
+ function DeleteDialog({ node, onClose, onDelete }) {
805
+ const [phase, setPhase] = (0, import_react2.useState)("choose");
806
+ const [loading, setLoading] = (0, import_react2.useState)(false);
807
+ if (!node) return null;
808
+ const reset = () => {
809
+ setPhase("choose");
810
+ setLoading(false);
811
+ };
812
+ const handleClose = () => {
813
+ reset();
814
+ onClose();
815
+ };
816
+ const handleLift = async () => {
817
+ setLoading(true);
818
+ try {
819
+ await onDelete(node.key, "lift");
820
+ reset();
821
+ onClose();
822
+ } catch {
823
+ setLoading(false);
824
+ }
825
+ };
826
+ const handleCascade = async () => {
827
+ setLoading(true);
828
+ try {
829
+ await onDelete(node.key, "cascade");
830
+ reset();
831
+ onClose();
832
+ } catch {
833
+ setLoading(false);
834
+ }
835
+ };
836
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "fc-dialog-overlay", onMouseDown: handleClose, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "fc-dialog", onMouseDown: (e) => e.stopPropagation(), children: [
837
+ phase === "choose" && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_jsx_runtime9.Fragment, { children: [
838
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "fc-dialog__header", children: [
839
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "fc-dialog__icon", children: "\u{1F5C2}" }),
840
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("h3", { className: "fc-dialog__title", children: [
841
+ "\u5220\u9664\u300C",
842
+ node.title,
843
+ "\u300D"
844
+ ] })
845
+ ] }),
846
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("p", { className: "fc-dialog__desc", children: "\u8BE5\u5206\u7C7B\u4E0B\u53EF\u80FD\u5305\u542B\u5B50\u5206\u7C7B\u548C\u8BCD\u6761\uFF0C\u8BF7\u9009\u62E9\u5220\u9664\u65B9\u5F0F\uFF1A" }),
847
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "fc-dialog__options", children: [
848
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
849
+ "button",
850
+ {
851
+ className: "fc-dialog__option",
852
+ onClick: handleLift,
853
+ disabled: loading,
854
+ children: [
855
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "fc-dialog__option-icon", children: "\u{1F4E4}" }),
856
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("span", { className: "fc-dialog__option-body", children: [
857
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "fc-dialog__option-title", children: "\u79FB\u4EA4\u7ED9\u4E0A\u7EA7\u5206\u7C7B" }),
858
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "fc-dialog__option-desc", children: "\u6240\u6709\u5B50\u5206\u7C7B\u548C\u8BCD\u6761\u5C06\u79FB\u81F3\u4E0A\u7EA7\u5206\u7C7B\uFF1B\u82E5\u5DF2\u662F\u6839\u5206\u7C7B\uFF0C\u5219\u53D8\u4E3A\u65E0\u5206\u7C7B" })
859
+ ] })
860
+ ]
861
+ }
862
+ ),
863
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
864
+ "button",
865
+ {
866
+ className: "fc-dialog__option fc-dialog__option--danger",
867
+ onClick: () => setPhase("confirm-cascade"),
868
+ disabled: loading,
869
+ children: [
870
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "fc-dialog__option-icon", children: "\u{1F5D1}" }),
871
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("span", { className: "fc-dialog__option-body", children: [
872
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "fc-dialog__option-title", children: "\u5F7B\u5E95\u5220\u9664" }),
873
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "fc-dialog__option-desc", children: "\u5220\u9664\u8BE5\u5206\u7C7B\u53CA\u6240\u6709\u5B50\u5206\u7C7B\uFF1B\u8BCD\u6761\u4E0D\u4F1A\u88AB\u5220\u9664\uFF0C\u5C06\u53D8\u4E3A\u65E0\u5206\u7C7B" })
874
+ ] })
875
+ ]
876
+ }
877
+ )
878
+ ] }),
879
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "fc-dialog__footer", children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("button", { className: "fc-dialog__btn", onClick: handleClose, disabled: loading, children: "\u53D6\u6D88" }) })
880
+ ] }),
881
+ phase === "confirm-cascade" && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_jsx_runtime9.Fragment, { children: [
882
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "fc-dialog__header", children: [
883
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "fc-dialog__icon", children: "\u26A0\uFE0F" }),
884
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("h3", { className: "fc-dialog__title fc-dialog__title--danger", children: "\u786E\u8BA4\u5F7B\u5E95\u5220\u9664\uFF1F" })
885
+ ] }),
886
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("p", { className: "fc-dialog__desc", children: [
887
+ "\u6B64\u64CD\u4F5C",
888
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("strong", { children: "\u4E0D\u53EF\u9006" }),
889
+ "\u3002\u300C",
890
+ node.title,
891
+ "\u300D\u53CA\u5176\u6240\u6709\u5B50\u5206\u7C7B\u5C06\u88AB\u6C38\u4E45\u5220\u9664\uFF0C \u5176\u4E0B\u8BCD\u6761\u5C06\u53D8\u4E3A\u65E0\u5206\u7C7B\u3002"
892
+ ] }),
893
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "fc-dialog__footer", children: [
894
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
895
+ "button",
896
+ {
897
+ className: "fc-dialog__btn",
898
+ onClick: () => setPhase("choose"),
899
+ disabled: loading,
900
+ children: "\u8FD4\u56DE"
901
+ }
902
+ ),
903
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
904
+ "button",
905
+ {
906
+ className: "fc-dialog__btn fc-dialog__btn--danger",
907
+ onClick: handleCascade,
908
+ disabled: loading,
909
+ children: loading ? "\u5220\u9664\u4E2D\u2026" : "\u786E\u8BA4\u5220\u9664"
910
+ }
911
+ )
912
+ ] })
913
+ ] })
914
+ ] }) });
47
915
  }
916
+
917
+ // src/components/Tree/flatToTree.ts
918
+ function flatToTree(list) {
919
+ const nodeMap = /* @__PURE__ */ new Map();
920
+ for (const item of list) {
921
+ nodeMap.set(item.id, {
922
+ key: item.id,
923
+ title: item.name,
924
+ children: [],
925
+ raw: item
926
+ });
927
+ }
928
+ const roots = [];
929
+ const orphans = [];
930
+ for (const item of list) {
931
+ const node = nodeMap.get(item.id);
932
+ if (item.parent_id === null) {
933
+ roots.push(node);
934
+ } else if (nodeMap.has(item.parent_id)) {
935
+ nodeMap.get(item.parent_id).children.push(node);
936
+ } else {
937
+ orphans.push(node);
938
+ }
939
+ }
940
+ const sortLevel = (nodes) => {
941
+ nodes.sort((a, b) => a.raw.sort_order - b.raw.sort_order);
942
+ for (const node of nodes) sortLevel(node.children);
943
+ };
944
+ sortLevel(roots);
945
+ return { roots, orphans };
946
+ }
947
+ function findNodeInfo(nodes, key, parent = null) {
948
+ for (let i = 0; i < nodes.length; i++) {
949
+ if (nodes[i].key === key)
950
+ return { node: nodes[i], parent, siblings: nodes, index: i };
951
+ const found = findNodeInfo(nodes[i].children, key, nodes[i]);
952
+ if (found) return found;
953
+ }
954
+ return null;
955
+ }
956
+ function isDescendantOf(roots, ancestorKey, targetKey) {
957
+ const info = findNodeInfo(roots, ancestorKey);
958
+ if (!info) return false;
959
+ const check = (children) => children.some((n) => n.key === targetKey || check(n.children));
960
+ return check(info.node.children);
961
+ }
962
+
963
+ // src/components/Tree/Tree.tsx
964
+ var import_jsx_runtime10 = require("react/jsx-runtime");
965
+ var TreeActionsCtx = (0, import_react3.createContext)(null);
966
+ var TreeStateCtx = (0, import_react3.createContext)(null);
967
+ var DndStateCtx = (0, import_react3.createContext)(null);
968
+ var CollapsePanel = (0, import_react3.memo)(function CollapsePanel2({ open, children }) {
969
+ const innerRef = (0, import_react3.useRef)(null);
970
+ const [height, setHeight] = (0, import_react3.useState)(0);
971
+ const [ready, setReady] = (0, import_react3.useState)(false);
972
+ (0, import_react3.useEffect)(() => {
973
+ const el = innerRef.current;
974
+ if (!el) return;
975
+ const ro = new ResizeObserver(() => setHeight(el.offsetHeight));
976
+ ro.observe(el);
977
+ setHeight(el.offsetHeight);
978
+ requestAnimationFrame(() => requestAnimationFrame(() => setReady(true)));
979
+ return () => ro.disconnect();
980
+ }, []);
981
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { style: {
982
+ height: open ? height : 0,
983
+ overflow: "hidden",
984
+ transition: ready ? "height 0.12s ease-out" : "none"
985
+ }, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { ref: innerRef, children }) });
986
+ });
987
+ var DndSlot = (0, import_react3.memo)(function DndSlot2({
988
+ nodeKey,
989
+ disabled,
990
+ children
991
+ }) {
992
+ const dndState = (0, import_react3.useContext)(DndStateCtx);
993
+ const isDropTarget = dndState.dropTargetKey === nodeKey;
994
+ const dropPosition = isDropTarget ? dndState.dropPosition : null;
995
+ const isDragSource = dndState.dragKey === nodeKey;
996
+ const { attributes, listeners, setNodeRef: setDragRef, isDragging } = (0, import_core.useDraggable)({ id: nodeKey, disabled });
997
+ const { setNodeRef: setDropRef } = (0, import_core.useDroppable)({ id: nodeKey, disabled });
998
+ const setRef = (0, import_react3.useCallback)((el) => {
999
+ setDragRef(el);
1000
+ setDropRef(el);
1001
+ }, [setDragRef, setDropRef]);
1002
+ const handleProps = (0, import_react3.useMemo)(
1003
+ () => ({ ...attributes, ...listeners }),
1004
+ [attributes, listeners]
1005
+ );
1006
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_jsx_runtime10.Fragment, { children: children({ setRef, handleProps, isDragging, isDragSource, dropPosition }) });
1007
+ });
1008
+ var TreeNodeItem = (0, import_react3.memo)(function TreeNodeItem2({ node, level, hidden = false }) {
1009
+ const actions = (0, import_react3.useContext)(TreeActionsCtx);
1010
+ const state = (0, import_react3.useContext)(TreeStateCtx);
1011
+ const isExpanded = state.expandedKeys.has(node.key);
1012
+ const isEditing = state.editingKey === node.key;
1013
+ const hasChildren = node.children.length > 0;
1014
+ const indent = level * 20 + 12;
1015
+ const [localEdit, setLocalEdit] = (0, import_react3.useState)("");
1016
+ (0, import_react3.useEffect)(() => {
1017
+ if (isEditing) setLocalEdit(node.title);
1018
+ }, [isEditing, node.title]);
1019
+ const handleEditKeyDown = (0, import_react3.useCallback)((e) => {
1020
+ e.stopPropagation();
1021
+ if (e.key === "Enter") actions.commitEdit(node.key, localEdit).then();
1022
+ if (e.key === "Escape") actions.cancelEdit();
1023
+ }, [actions, node.key, localEdit]);
1024
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(DndSlot, { nodeKey: node.key, disabled: isEditing || hidden, children: ({ setRef, handleProps, isDragging, isDragSource, dropPosition }) => /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: `fc-tree__node ${isDragging ? "is-dragging" : ""}`, children: [
1025
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
1026
+ "div",
1027
+ {
1028
+ ref: setRef,
1029
+ className: [
1030
+ "fc-tree__item",
1031
+ isDragSource && "fc-tree__item--drag-source",
1032
+ dropPosition === "into" && "fc-tree__item--drop-into",
1033
+ dropPosition === "before" && "fc-tree__item--drop-before",
1034
+ dropPosition === "after" && "fc-tree__item--drop-after"
1035
+ ].filter(Boolean).join(" "),
1036
+ style: {
1037
+ paddingLeft: dropPosition === "into" ? indent + 8 : indent,
1038
+ "--fc-indent": `${indent}px`
1039
+ },
1040
+ children: [
1041
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1042
+ "span",
1043
+ {
1044
+ className: "fc-tree__drag-handle",
1045
+ title: "\u62D6\u62FD\u79FB\u52A8",
1046
+ ...handleProps,
1047
+ onMouseDown: (e) => e.stopPropagation(),
1048
+ children: "\u283F"
1049
+ }
1050
+ ),
1051
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1052
+ "span",
1053
+ {
1054
+ className: [
1055
+ "fc-tree__switcher",
1056
+ !hasChildren && "fc-tree__switcher--hidden",
1057
+ isExpanded && "fc-tree__switcher--open"
1058
+ ].filter(Boolean).join(" "),
1059
+ onClick: () => hasChildren && actions.toggleExpand(node.key),
1060
+ children: hasChildren ? "\u25B6" : ""
1061
+ }
1062
+ ),
1063
+ isEditing ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1064
+ "input",
1065
+ {
1066
+ autoFocus: true,
1067
+ className: "fc-tree__edit-input",
1068
+ value: localEdit,
1069
+ onChange: (e) => setLocalEdit(e.target.value),
1070
+ onBlur: () => actions.commitEdit(node.key, localEdit),
1071
+ onKeyDown: handleEditKeyDown,
1072
+ onClick: (e) => e.stopPropagation()
1073
+ }
1074
+ ) : /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1075
+ "span",
1076
+ {
1077
+ className: "fc-tree__title",
1078
+ onClick: () => {
1079
+ actions.select(node.key);
1080
+ if (hasChildren) actions.toggleExpand(node.key);
1081
+ },
1082
+ onDoubleClick: () => actions.startEdit(node.key),
1083
+ children: node.title
1084
+ }
1085
+ ),
1086
+ !isEditing && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("span", { className: "fc-tree__actions", children: [
1087
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1088
+ "button",
1089
+ {
1090
+ className: "fc-tree__action",
1091
+ title: "\u65B0\u5EFA\u5B50\u5206\u7C7B",
1092
+ onClick: (e) => {
1093
+ e.stopPropagation();
1094
+ actions.requestCreate(node.key).then();
1095
+ },
1096
+ children: "+"
1097
+ }
1098
+ ),
1099
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1100
+ "button",
1101
+ {
1102
+ className: "fc-tree__action",
1103
+ title: "\u91CD\u547D\u540D\uFF08\u53CC\u51FB\u4E5F\u53EF\uFF09",
1104
+ onClick: (e) => {
1105
+ e.stopPropagation();
1106
+ actions.startEdit(node.key);
1107
+ },
1108
+ children: "\u270F"
1109
+ }
1110
+ ),
1111
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1112
+ "button",
1113
+ {
1114
+ className: "fc-tree__action fc-tree__action--danger",
1115
+ title: "\u5220\u9664",
1116
+ onClick: (e) => {
1117
+ e.stopPropagation();
1118
+ actions.requestDelete(node);
1119
+ },
1120
+ children: "\u{1F5D1}"
1121
+ }
1122
+ )
1123
+ ] })
1124
+ ]
1125
+ }
1126
+ ),
1127
+ hasChildren && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(CollapsePanel, { open: isExpanded, children: node.children.map((child) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(TreeNodeItem2, { node: child, level: level + 1, hidden: hidden || !isExpanded }, child.key)) })
1128
+ ] }) });
1129
+ });
1130
+ function Tree({
1131
+ treeData,
1132
+ onRename,
1133
+ onCreate,
1134
+ onDelete,
1135
+ onMove,
1136
+ onSelect,
1137
+ selectedKey,
1138
+ searchable = false,
1139
+ scrollHeight = "400px",
1140
+ className = ""
1141
+ }) {
1142
+ const [expandedKeys, setExpandedKeys] = (0, import_react3.useState)(/* @__PURE__ */ new Set());
1143
+ const [editingKey, setEditingKey] = (0, import_react3.useState)(null);
1144
+ const [deleteTarget, setDeleteTarget] = (0, import_react3.useState)(null);
1145
+ const [searchValue, setSearchValue] = (0, import_react3.useState)("");
1146
+ const [dndState, setDndState] = (0, import_react3.useState)({
1147
+ dropTargetKey: null,
1148
+ dropPosition: null,
1149
+ dragKey: null
1150
+ });
1151
+ const dropRef = (0, import_react3.useRef)({ key: null, pos: null });
1152
+ const pointerYRef = (0, import_react3.useRef)(0);
1153
+ (0, import_react3.useEffect)(() => {
1154
+ const handler = (e) => {
1155
+ pointerYRef.current = e.clientY;
1156
+ };
1157
+ window.addEventListener("pointermove", handler);
1158
+ return () => window.removeEventListener("pointermove", handler);
1159
+ }, []);
1160
+ const sensors = (0, import_core.useSensors)(
1161
+ (0, import_core.useSensor)(import_core.PointerSensor, { activationConstraint: { distance: 8 } })
1162
+ );
1163
+ const toggleExpand = (0, import_react3.useCallback)((key) => {
1164
+ setExpandedKeys((prev) => {
1165
+ const next = new Set(prev);
1166
+ next.has(key) ? next.delete(key) : next.add(key);
1167
+ return next;
1168
+ });
1169
+ }, []);
1170
+ const select = (0, import_react3.useCallback)((key) => onSelect?.(key), [onSelect]);
1171
+ const startEdit = (0, import_react3.useCallback)((key) => setEditingKey(key), []);
1172
+ const cancelEdit = (0, import_react3.useCallback)(() => setEditingKey(null), []);
1173
+ const commitEdit = (0, import_react3.useCallback)(async (key, newTitle) => {
1174
+ setEditingKey(null);
1175
+ const trimmed = newTitle.trim();
1176
+ if (trimmed && onRename) await onRename(key, trimmed);
1177
+ }, [onRename]);
1178
+ const requestCreate = (0, import_react3.useCallback)(async (parentKey) => {
1179
+ if (!onCreate) return;
1180
+ const newKey = await onCreate(parentKey);
1181
+ if (parentKey) setExpandedKeys((prev) => /* @__PURE__ */ new Set([...prev, parentKey]));
1182
+ setEditingKey(newKey);
1183
+ }, [onCreate]);
1184
+ const requestDelete = (0, import_react3.useCallback)((node) => {
1185
+ setDeleteTarget(node);
1186
+ }, []);
1187
+ const treeDataRef = (0, import_react3.useRef)(treeData);
1188
+ const expandedRef = (0, import_react3.useRef)(expandedKeys);
1189
+ treeDataRef.current = treeData;
1190
+ expandedRef.current = expandedKeys;
1191
+ const handleDragStart = (0, import_react3.useCallback)(({ active }) => {
1192
+ dropRef.current = { key: null, pos: null };
1193
+ setDndState({ dropTargetKey: null, dropPosition: null, dragKey: active.id });
1194
+ }, []);
1195
+ const handleDragMove = (0, import_react3.useCallback)(({ over, active }) => {
1196
+ if (!over || over.id === active.id) {
1197
+ if (dropRef.current.key !== null) {
1198
+ dropRef.current = { key: null, pos: null };
1199
+ setDndState((prev) => ({ ...prev, dropTargetKey: null, dropPosition: null }));
1200
+ }
1201
+ return;
1202
+ }
1203
+ const targetKey = over.id;
1204
+ if (isDescendantOf(treeDataRef.current, active.id, targetKey)) {
1205
+ if (dropRef.current.key !== null) {
1206
+ dropRef.current = { key: null, pos: null };
1207
+ setDndState((prev) => ({ ...prev, dropTargetKey: null, dropPosition: null }));
1208
+ }
1209
+ return;
1210
+ }
1211
+ const rect = over.rect;
1212
+ const y = pointerYRef.current;
1213
+ const ratio = Math.max(0, Math.min(1, (y - rect.top) / rect.height));
1214
+ let position;
1215
+ if (ratio < 0.2) position = "before";
1216
+ else if (ratio > 0.8) position = "after";
1217
+ else position = "into";
1218
+ if (dropRef.current.key === targetKey && dropRef.current.pos === position) return;
1219
+ dropRef.current = { key: targetKey, pos: position };
1220
+ setDndState((prev) => ({ ...prev, dropTargetKey: targetKey, dropPosition: position }));
1221
+ }, []);
1222
+ const handleDragEnd = (0, import_react3.useCallback)(({ active }) => {
1223
+ const { key: target, pos: position } = dropRef.current;
1224
+ dropRef.current = { key: null, pos: null };
1225
+ setDndState({ dropTargetKey: null, dropPosition: null, dragKey: null });
1226
+ if (!target || !position || active.id === target) return;
1227
+ onMove?.(active.id, target, position);
1228
+ }, [onMove]);
1229
+ const handleDragCancel = (0, import_react3.useCallback)(() => {
1230
+ dropRef.current = { key: null, pos: null };
1231
+ setDndState({ dropTargetKey: null, dropPosition: null, dragKey: null });
1232
+ }, []);
1233
+ const displayData = (0, import_react3.useMemo)(() => {
1234
+ if (!searchValue) return treeData;
1235
+ const kw = searchValue.toLowerCase();
1236
+ const filter = (nodes) => nodes.reduce((acc, node) => {
1237
+ const match = node.title.toLowerCase().includes(kw);
1238
+ const filteredChildren = filter(node.children);
1239
+ if (match || filteredChildren.length > 0)
1240
+ acc.push({ ...node, children: filteredChildren });
1241
+ return acc;
1242
+ }, []);
1243
+ return filter(treeData);
1244
+ }, [treeData, searchValue]);
1245
+ const actionsValue = (0, import_react3.useMemo)(() => ({
1246
+ toggleExpand,
1247
+ select,
1248
+ startEdit,
1249
+ commitEdit,
1250
+ cancelEdit,
1251
+ requestCreate,
1252
+ requestDelete
1253
+ }), [toggleExpand, select, startEdit, commitEdit, cancelEdit, requestCreate, requestDelete]);
1254
+ const stateValue = (0, import_react3.useMemo)(() => ({
1255
+ expandedKeys,
1256
+ selectedKey: selectedKey ?? null,
1257
+ editingKey
1258
+ }), [expandedKeys, selectedKey, editingKey]);
1259
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(TreeActionsCtx.Provider, { value: actionsValue, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(TreeStateCtx.Provider, { value: stateValue, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(DndStateCtx.Provider, { value: dndState, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
1260
+ import_core.DndContext,
1261
+ {
1262
+ sensors,
1263
+ onDragStart: handleDragStart,
1264
+ onDragMove: handleDragMove,
1265
+ onDragEnd: handleDragEnd,
1266
+ onDragCancel: handleDragCancel,
1267
+ children: [
1268
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: `fc-tree ${className}`, children: [
1269
+ searchable && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "fc-tree__search", children: [
1270
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1271
+ "input",
1272
+ {
1273
+ type: "text",
1274
+ value: searchValue,
1275
+ onChange: (e) => setSearchValue(e.target.value),
1276
+ placeholder: "\u641C\u7D22\u5206\u7C7B\u2026",
1277
+ className: "fc-tree__search-input"
1278
+ }
1279
+ ),
1280
+ searchValue && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("button", { className: "fc-tree__search-clear", onClick: () => setSearchValue(""), children: "\u2715" })
1281
+ ] }),
1282
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(RollingBox, { showThumb: "show", style: { height: scrollHeight }, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "fc-tree__list", children: [
1283
+ displayData.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "fc-tree__empty", children: searchValue ? "\u65E0\u5339\u914D\u5206\u7C7B" : "\u6682\u65E0\u5206\u7C7B" }),
1284
+ displayData.map((node) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(TreeNodeItem, { node, level: 0 }, node.key))
1285
+ ] }) }),
1286
+ onCreate && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "fc-tree__add-root", children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1287
+ "button",
1288
+ {
1289
+ className: "fc-tree__add-root-btn",
1290
+ onClick: () => requestCreate(null),
1291
+ children: "+ \u65B0\u5EFA\u9876\u7EA7\u5206\u7C7B"
1292
+ }
1293
+ ) })
1294
+ ] }),
1295
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1296
+ DeleteDialog,
1297
+ {
1298
+ node: deleteTarget,
1299
+ onClose: () => setDeleteTarget(null),
1300
+ onDelete: async (key, mode) => {
1301
+ await onDelete?.(key, mode);
1302
+ setDeleteTarget(null);
1303
+ }
1304
+ }
1305
+ )
1306
+ ]
1307
+ }
1308
+ ) }) }) });
1309
+ }
1310
+
1311
+ // src/components/Tree/OrphanDialog.tsx
1312
+ var import_react4 = require("react");
1313
+ var import_jsx_runtime11 = require("react/jsx-runtime");
1314
+ function OrphanDialog({ orphans, onResolve, onClose }) {
1315
+ const [resolutions, setResolutions] = (0, import_react4.useState)(
1316
+ () => Object.fromEntries(orphans.map((o) => [o.key, "lift"]))
1317
+ );
1318
+ if (orphans.length === 0) return null;
1319
+ const set = (key, val) => setResolutions((prev) => ({ ...prev, [key]: val }));
1320
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "fc-dialog-overlay", children: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "fc-dialog fc-dialog--wide", children: [
1321
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "fc-dialog__header", children: [
1322
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "fc-dialog__icon", children: "\u{1F50D}" }),
1323
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("h3", { className: "fc-dialog__title fc-dialog__title--warning", children: [
1324
+ "\u68C0\u6D4B\u5230 ",
1325
+ orphans.length,
1326
+ " \u4E2A\u5B64\u7ACB\u5206\u7C7B"
1327
+ ] })
1328
+ ] }),
1329
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { className: "fc-dialog__desc", children: "\u4EE5\u4E0B\u5206\u7C7B\u7684\u7236\u5206\u7C7B\u5DF2\u4E0D\u5B58\u5728\uFF0C\u53EF\u80FD\u662F\u6570\u636E\u8FC1\u79FB\u6216\u5F02\u5E38\u5220\u9664\u5BFC\u81F4\u3002 \u8FD9\u4E9B\u5206\u7C7B\u76EE\u524D\u4E0D\u4F1A\u663E\u793A\u5728\u6811\u4E2D\uFF0C\u8BF7\u9009\u62E9\u5904\u7406\u65B9\u5F0F\uFF1A" }),
1330
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "fc-orphan-list", children: orphans.map((node) => /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "fc-orphan-item", children: [
1331
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "fc-orphan-name", title: node.key, children: node.title }),
1332
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("span", { className: "fc-orphan-id", children: [
1333
+ "id: ",
1334
+ node.key.slice(0, 8),
1335
+ "\u2026"
1336
+ ] }),
1337
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "fc-orphan-radios", children: [
1338
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("label", { className: `fc-orphan-radio ${resolutions[node.key] === "lift" ? "is-active" : ""}`, children: [
1339
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1340
+ "input",
1341
+ {
1342
+ type: "radio",
1343
+ name: node.key,
1344
+ checked: resolutions[node.key] === "lift",
1345
+ onChange: () => set(node.key, "lift")
1346
+ }
1347
+ ),
1348
+ "\u63D0\u5347\u4E3A\u6839\u5206\u7C7B"
1349
+ ] }),
1350
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("label", { className: `fc-orphan-radio fc-orphan-radio--danger ${resolutions[node.key] === "remove" ? "is-active" : ""}`, children: [
1351
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1352
+ "input",
1353
+ {
1354
+ type: "radio",
1355
+ name: node.key,
1356
+ checked: resolutions[node.key] === "remove",
1357
+ onChange: () => set(node.key, "remove")
1358
+ }
1359
+ ),
1360
+ "\u5220\u9664"
1361
+ ] })
1362
+ ] })
1363
+ ] }, node.key)) }),
1364
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "fc-dialog__footer", children: [
1365
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("button", { className: "fc-dialog__btn", onClick: onClose, children: "\u6682\u65F6\u5FFD\u7565" }),
1366
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1367
+ "button",
1368
+ {
1369
+ className: "fc-dialog__btn fc-dialog__btn--primary",
1370
+ onClick: () => onResolve(resolutions),
1371
+ children: "\u786E\u8BA4\u5904\u7406"
1372
+ }
1373
+ )
1374
+ ] })
1375
+ ] }) });
1376
+ }
1377
+
1378
+ // src/components/Avatar/Avatar.tsx
1379
+ var import_react5 = require("react");
1380
+ var import_jsx_runtime12 = require("react/jsx-runtime");
1381
+ var SIZE_MAP = {
1382
+ xs: 20,
1383
+ sm: 28,
1384
+ md: 40,
1385
+ lg: 56,
1386
+ xl: 72
1387
+ };
1388
+ var DEFAULT_ICON = "M12 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm0 2c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z";
1389
+ var useAvatarStyles = (size, shape, bordered, onClick, loadState, src, children, colorVariant, className, color, customStyle) => {
1390
+ const classes = (0, import_react5.useMemo)(() => {
1391
+ const classNames = [
1392
+ "ui-avatar",
1393
+ `ui-avatar-${size}`,
1394
+ `ui-avatar-${shape}`,
1395
+ bordered && "ui-avatar-bordered",
1396
+ onClick && "ui-avatar-clickable",
1397
+ loadState === "loading" && "ui-avatar-loading",
1398
+ !src && !children && "ui-avatar-empty",
1399
+ colorVariant && `ui-avatar--${colorVariant}`,
1400
+ className
1401
+ ];
1402
+ return classNames.filter(Boolean).join(" ");
1403
+ }, [size, shape, bordered, onClick, loadState, src, children, colorVariant, className]);
1404
+ const style = (0, import_react5.useMemo)(() => ({
1405
+ width: SIZE_MAP[size],
1406
+ height: SIZE_MAP[size],
1407
+ fontSize: `${SIZE_MAP[size] * 0.4}px`,
1408
+ ...color && { backgroundColor: color },
1409
+ ...customStyle
1410
+ }), [size, color, customStyle]);
1411
+ return { classes, style };
1412
+ };
1413
+ var useImageLoader = (src, fallbackSrc, onImageLoad, onImageError, onStateChange) => {
1414
+ const [loadState, setLoadState] = (0, import_react5.useState)("idle");
1415
+ const [currentSrc, setCurrentSrc] = (0, import_react5.useState)(src);
1416
+ (0, import_react5.useEffect)(() => {
1417
+ setCurrentSrc(src);
1418
+ setLoadState(src ? "loading" : "idle");
1419
+ }, [src]);
1420
+ (0, import_react5.useEffect)(() => {
1421
+ onStateChange?.(loadState);
1422
+ }, [loadState, onStateChange]);
1423
+ const handleLoad = (0, import_react5.useCallback)(() => {
1424
+ setLoadState("loaded");
1425
+ onImageLoad?.();
1426
+ }, [onImageLoad]);
1427
+ const handleError = (0, import_react5.useCallback)((e) => {
1428
+ if (fallbackSrc && currentSrc !== fallbackSrc) {
1429
+ setCurrentSrc(fallbackSrc);
1430
+ setLoadState("loading");
1431
+ } else {
1432
+ setLoadState("error");
1433
+ onImageError?.(e.nativeEvent);
1434
+ }
1435
+ }, [fallbackSrc, currentSrc, onImageError]);
1436
+ return {
1437
+ loadState,
1438
+ currentSrc,
1439
+ handleLoad,
1440
+ handleError
1441
+ };
1442
+ };
1443
+ var useKeyboardInteraction = (onClick) => {
1444
+ const handleKeyDown = (0, import_react5.useCallback)((e) => {
1445
+ if (onClick && (e.key === "Enter" || e.key === " ")) {
1446
+ e.preventDefault();
1447
+ onClick(e);
1448
+ }
1449
+ }, [onClick]);
1450
+ return { handleKeyDown };
1451
+ };
1452
+ var renderContent = (currentSrc, fallbackSrc, loadState, alt, lazyLoad2, children, handleLoad, handleError) => {
1453
+ const showImage = currentSrc && loadState !== "error";
1454
+ const showFallback = !showImage && children;
1455
+ const imageSrc = loadState === "error" && currentSrc !== fallbackSrc ? fallbackSrc : currentSrc;
1456
+ if (showImage && imageSrc) {
1457
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1458
+ "img",
1459
+ {
1460
+ src: imageSrc,
1461
+ alt,
1462
+ className: "ui-avatar-img",
1463
+ loading: lazyLoad2 ? "lazy" : "eager",
1464
+ onLoad: handleLoad,
1465
+ onError: handleError,
1466
+ decoding: "async"
1467
+ }
1468
+ );
1469
+ }
1470
+ if (showFallback) {
1471
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "ui-avatar-text", children });
1472
+ }
1473
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "ui-avatar-placeholder", children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("svg", { viewBox: "0 0 24 24", width: "40%", height: "40%", fill: "currentColor", opacity: 0.4, children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("path", { d: DEFAULT_ICON }) }) });
1474
+ };
1475
+ var useAriaAttributes = (onClick, loadState, alt) => {
1476
+ return {
1477
+ role: onClick ? "button" : "img",
1478
+ tabIndex: onClick ? 0 : void 0,
1479
+ "aria-label": alt,
1480
+ "aria-busy": loadState === "loading",
1481
+ "data-load-state": loadState
1482
+ };
1483
+ };
1484
+ var Avatar = (0, import_react5.forwardRef)(
1485
+ ({
1486
+ children,
1487
+ src,
1488
+ fallbackSrc,
1489
+ color,
1490
+ colorVariant,
1491
+ size = "md",
1492
+ shape = "circle",
1493
+ alt = "\u7528\u6237\u5934\u50CF",
1494
+ lazyLoad: lazyLoad2 = false,
1495
+ onImageLoad,
1496
+ onImageError,
1497
+ onStateChange,
1498
+ bordered = false,
1499
+ className = "",
1500
+ onClick,
1501
+ style: customStyle,
1502
+ ...restProps
1503
+ }, ref) => {
1504
+ const { loadState, currentSrc, handleLoad, handleError } = useImageLoader(
1505
+ src,
1506
+ fallbackSrc,
1507
+ onImageLoad,
1508
+ onImageError,
1509
+ onStateChange
1510
+ );
1511
+ const { classes, style } = useAvatarStyles(
1512
+ size,
1513
+ shape,
1514
+ bordered,
1515
+ onClick,
1516
+ loadState,
1517
+ src,
1518
+ children,
1519
+ colorVariant,
1520
+ className,
1521
+ color,
1522
+ customStyle
1523
+ );
1524
+ const { handleKeyDown } = useKeyboardInteraction(onClick);
1525
+ const ariaAttributes = useAriaAttributes(onClick, loadState, alt);
1526
+ const content = renderContent(
1527
+ currentSrc,
1528
+ fallbackSrc,
1529
+ loadState,
1530
+ alt,
1531
+ lazyLoad2,
1532
+ children,
1533
+ handleLoad,
1534
+ handleError
1535
+ );
1536
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1537
+ "div",
1538
+ {
1539
+ ref,
1540
+ className: classes,
1541
+ style,
1542
+ onClick,
1543
+ onKeyDown: handleKeyDown,
1544
+ ...ariaAttributes,
1545
+ ...restProps,
1546
+ children: content
1547
+ }
1548
+ );
1549
+ }
1550
+ );
1551
+ Avatar.displayName = "Avatar";
1552
+
1553
+ // src/components/ListGroup/ListGroup.tsx
1554
+ var import_react6 = require("react");
1555
+ var import_jsx_runtime13 = require("react/jsx-runtime");
1556
+ var combineClassNames = (...classNames) => {
1557
+ return classNames.filter(Boolean).join(" ");
1558
+ };
1559
+ var ListGroupItem = (0, import_react6.forwardRef)(
1560
+ ({
1561
+ active = false,
1562
+ disabled = false,
1563
+ onClick,
1564
+ className,
1565
+ children,
1566
+ ...props
1567
+ }, ref) => {
1568
+ const handleClick = (0, import_react6.useCallback)((e) => {
1569
+ if (disabled) return;
1570
+ onClick?.(e);
1571
+ }, [disabled, onClick]);
1572
+ const classNames = (0, import_react6.useMemo)(() => {
1573
+ return combineClassNames(
1574
+ "fc-list-group-item",
1575
+ active && "fc-list-group-item--active",
1576
+ disabled && "fc-list-group-item--disabled",
1577
+ className
1578
+ );
1579
+ }, [active, disabled, className]);
1580
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
1581
+ "li",
1582
+ {
1583
+ ref,
1584
+ className: classNames,
1585
+ onClick: handleClick,
1586
+ "aria-disabled": disabled,
1587
+ "aria-selected": active,
1588
+ role: onClick ? "button" : void 0,
1589
+ tabIndex: onClick && !disabled ? 0 : void 0,
1590
+ ...props,
1591
+ children
1592
+ }
1593
+ );
1594
+ }
1595
+ );
1596
+ ListGroupItem.displayName = "ListGroupItem";
1597
+ var ListGroup = (0, import_react6.forwardRef)(
1598
+ ({
1599
+ bordered = true,
1600
+ flush = false,
1601
+ className,
1602
+ children,
1603
+ ...props
1604
+ }, ref) => {
1605
+ const classNames = (0, import_react6.useMemo)(() => {
1606
+ return combineClassNames(
1607
+ "fc-list-group",
1608
+ bordered && "fc-list-group--bordered",
1609
+ flush && "fc-list-group--flush",
1610
+ className
1611
+ );
1612
+ }, [bordered, flush, className]);
1613
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
1614
+ "ul",
1615
+ {
1616
+ ref,
1617
+ className: classNames,
1618
+ role: "list",
1619
+ ...props,
1620
+ children
1621
+ }
1622
+ );
1623
+ }
1624
+ );
1625
+ ListGroup.displayName = "ListGroup";
1626
+
1627
+ // src/components/VirtualList/VirtualList.tsx
1628
+ var import_react7 = require("react");
1629
+ var import_jsx_runtime14 = require("react/jsx-runtime");
1630
+ function VirtualList({
1631
+ data,
1632
+ height,
1633
+ itemHeight,
1634
+ renderItem,
1635
+ className = "",
1636
+ overscan = 3,
1637
+ showScrollbar = true,
1638
+ onScrollEnd,
1639
+ style
1640
+ }) {
1641
+ const containerRef = (0, import_react7.useRef)(null);
1642
+ const [scrollTop, setScrollTop] = (0, import_react7.useState)(0);
1643
+ const totalHeight = data.length * itemHeight;
1644
+ const handleScroll = (0, import_react7.useCallback)((e) => {
1645
+ const newScrollTop = e.currentTarget.scrollTop;
1646
+ setScrollTop(newScrollTop);
1647
+ if (onScrollEnd) {
1648
+ const scrollHeight = e.currentTarget.scrollHeight;
1649
+ const clientHeight = e.currentTarget.clientHeight;
1650
+ if (newScrollTop + clientHeight >= scrollHeight - 5) {
1651
+ onScrollEnd();
1652
+ }
1653
+ }
1654
+ }, [onScrollEnd]);
1655
+ const visibleRange = (0, import_react7.useMemo)(() => {
1656
+ const startIndex = Math.max(0, Math.floor(scrollTop / itemHeight) - overscan);
1657
+ const endIndex = Math.min(
1658
+ data.length,
1659
+ Math.ceil((scrollTop + height) / itemHeight) + overscan
1660
+ );
1661
+ return { startIndex, endIndex };
1662
+ }, [scrollTop, height, itemHeight, data.length, overscan]);
1663
+ const visibleData = (0, import_react7.useMemo)(() => {
1664
+ return data.slice(visibleRange.startIndex, visibleRange.endIndex);
1665
+ }, [data, visibleRange]);
1666
+ const offsetY = visibleRange.startIndex * itemHeight;
1667
+ (0, import_react7.useEffect)(() => {
1668
+ if (!showScrollbar && containerRef.current) {
1669
+ containerRef.current.style.scrollbarWidth = "none";
1670
+ }
1671
+ }, [showScrollbar]);
1672
+ const classNames = [
1673
+ "fc-virtual-list",
1674
+ !showScrollbar && "fc-virtual-list--hide-scrollbar",
1675
+ className
1676
+ ].filter(Boolean).join(" ");
1677
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1678
+ "div",
1679
+ {
1680
+ ref: containerRef,
1681
+ className: classNames,
1682
+ style: { height: `${height}px`, ...style },
1683
+ onScroll: handleScroll,
1684
+ children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1685
+ "div",
1686
+ {
1687
+ className: "fc-virtual-list__container",
1688
+ style: { height: `${totalHeight}px` },
1689
+ children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1690
+ "div",
1691
+ {
1692
+ className: "fc-virtual-list__content",
1693
+ style: { transform: `translateY(${offsetY}px)` },
1694
+ children: visibleData.map((item, idx) => {
1695
+ const actualIndex = visibleRange.startIndex + idx;
1696
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1697
+ "div",
1698
+ {
1699
+ className: "fc-virtual-list__item",
1700
+ style: { height: `${itemHeight}px` },
1701
+ children: renderItem(item, actualIndex)
1702
+ },
1703
+ actualIndex
1704
+ );
1705
+ })
1706
+ }
1707
+ )
1708
+ }
1709
+ )
1710
+ }
1711
+ );
1712
+ }
1713
+
1714
+ // src/components/Alert/AlertContext.tsx
1715
+ var import_react8 = require("react");
1716
+ var import_jsx_runtime15 = require("react/jsx-runtime");
1717
+ var AlertContext = (0, import_react8.createContext)(null);
1718
+ var ICONS = {
1719
+ success: /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
1720
+ "svg",
1721
+ {
1722
+ className: "fc-alert__icon",
1723
+ xmlns: "http://www.w3.org/2000/svg",
1724
+ viewBox: "0 0 24 24",
1725
+ fill: "none",
1726
+ stroke: "currentColor",
1727
+ strokeWidth: "2",
1728
+ strokeLinecap: "round",
1729
+ strokeLinejoin: "round",
1730
+ children: [
1731
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("circle", { cx: "12", cy: "12", r: "10" }),
1732
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("polyline", { points: "17 9 11 17 6 12" })
1733
+ ]
1734
+ }
1735
+ ),
1736
+ error: /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
1737
+ "svg",
1738
+ {
1739
+ className: "fc-alert__icon",
1740
+ xmlns: "http://www.w3.org/2000/svg",
1741
+ viewBox: "0 0 24 24",
1742
+ fill: "none",
1743
+ stroke: "currentColor",
1744
+ strokeWidth: "2",
1745
+ strokeLinecap: "round",
1746
+ strokeLinejoin: "round",
1747
+ children: [
1748
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("circle", { cx: "12", cy: "12", r: "10" }),
1749
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("line", { x1: "15", y1: "9", x2: "9", y2: "15" }),
1750
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("line", { x1: "9", y1: "9", x2: "15", y2: "15" })
1751
+ ]
1752
+ }
1753
+ ),
1754
+ warning: /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
1755
+ "svg",
1756
+ {
1757
+ className: "fc-alert__icon",
1758
+ xmlns: "http://www.w3.org/2000/svg",
1759
+ viewBox: "0 0 24 24",
1760
+ fill: "none",
1761
+ stroke: "currentColor",
1762
+ strokeWidth: "2",
1763
+ strokeLinecap: "round",
1764
+ strokeLinejoin: "round",
1765
+ children: [
1766
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("path", { d: "M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z" }),
1767
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("line", { x1: "12", y1: "9", x2: "12", y2: "13" }),
1768
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("line", { x1: "12", y1: "17", x2: "12.01", y2: "17" })
1769
+ ]
1770
+ }
1771
+ ),
1772
+ info: /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
1773
+ "svg",
1774
+ {
1775
+ className: "fc-alert__icon",
1776
+ xmlns: "http://www.w3.org/2000/svg",
1777
+ viewBox: "0 0 24 24",
1778
+ fill: "none",
1779
+ stroke: "currentColor",
1780
+ strokeWidth: "2",
1781
+ strokeLinecap: "round",
1782
+ strokeLinejoin: "round",
1783
+ children: [
1784
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("circle", { cx: "12", cy: "12", r: "10" }),
1785
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("line", { x1: "12", y1: "8", x2: "12", y2: "12" }),
1786
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("line", { x1: "12", y1: "16", x2: "12.01", y2: "16" })
1787
+ ]
1788
+ }
1789
+ )
1790
+ };
1791
+ function AlertProvider({ children }) {
1792
+ const [props, setProps] = (0, import_react8.useState)({
1793
+ msg: "",
1794
+ type: "info",
1795
+ visible: false,
1796
+ choice: () => {
1797
+ }
1798
+ });
1799
+ const showAlert = (msg, type, confirm = false) => new Promise((resolve) => {
1800
+ setProps({
1801
+ msg,
1802
+ type,
1803
+ visible: true,
1804
+ confirm,
1805
+ choice: (res) => {
1806
+ setProps((p) => ({ ...p, visible: false }));
1807
+ resolve(res);
1808
+ }
1809
+ });
1810
+ });
1811
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(AlertContext.Provider, { value: { showAlert }, children: [
1812
+ children,
1813
+ props.visible && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "fc-alert-overlay", children: /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: `fc-alert fc-alert--${props.type}`, children: [
1814
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "fc-alert__header", children: [
1815
+ ICONS[props.type],
1816
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "fc-alert__title", children: "\u63D0\u793A" })
1817
+ ] }),
1818
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(RollingBox, { className: "fc-alert__msg", children: props.msg }),
1819
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "fc-alert__footer", children: [
1820
+ props.confirm && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
1821
+ Button,
1822
+ {
1823
+ variant: "secondary",
1824
+ size: "sm",
1825
+ onClick: () => props.choice("no"),
1826
+ children: "\u53D6\u6D88"
1827
+ }
1828
+ ),
1829
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
1830
+ Button,
1831
+ {
1832
+ variant: "primary",
1833
+ size: "sm",
1834
+ onClick: () => props.choice("yes"),
1835
+ children: "\u786E\u5B9A"
1836
+ }
1837
+ )
1838
+ ] })
1839
+ ] }) })
1840
+ ] });
1841
+ }
1842
+ var useAlert = () => (0, import_react8.useContext)(AlertContext);
1843
+
1844
+ // src/components/LazyLoad/LazyLoad.tsx
1845
+ var import_react9 = require("react");
1846
+ var import_jsx_runtime16 = require("react/jsx-runtime");
1847
+ function lazyLoad(importFn, options = {}) {
1848
+ const { fallback = /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(LazySpinner, {}), timeout = 1e4 } = options;
1849
+ const LazyComponent = (0, import_react9.lazy)(() => {
1850
+ let timeoutId;
1851
+ const loadPromise = Promise.race([
1852
+ importFn(),
1853
+ new Promise((_, reject) => {
1854
+ timeoutId = setTimeout(() => reject(new Error(`\u52A0\u8F7D\u8D85\u65F6`)), timeout);
1855
+ })
1856
+ ]).finally(() => clearTimeout(timeoutId));
1857
+ return loadPromise;
1858
+ });
1859
+ return (props) => /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_react9.Suspense, { fallback, children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(LazyComponent, { ...props }) });
1860
+ }
1861
+ function LazySpinner() {
1862
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "fc-lazy-spinner", children: [
1863
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { className: "fc-lazy-spinner-dot" }),
1864
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { className: "fc-lazy-spinner-dot" }),
1865
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { className: "fc-lazy-spinner-dot" })
1866
+ ] });
1867
+ }
1868
+
1869
+ // src/components/Card/Card.tsx
1870
+ var import_jsx_runtime17 = require("react/jsx-runtime");
1871
+ var Card = ({
1872
+ image,
1873
+ imageSlot,
1874
+ imageHeight = 200,
1875
+ title,
1876
+ description,
1877
+ actions,
1878
+ extraInfo,
1879
+ variant = "default",
1880
+ hoverable = false,
1881
+ disabled = false,
1882
+ className = "",
1883
+ style,
1884
+ onClick
1885
+ }) => {
1886
+ const handleClick = () => {
1887
+ if (!disabled && onClick) {
1888
+ onClick();
1889
+ }
1890
+ };
1891
+ const classes = [
1892
+ "fc-card",
1893
+ `fc-card--${variant}`,
1894
+ hoverable && "fc-card--hoverable",
1895
+ disabled && "fc-card--disabled",
1896
+ onClick && "fc-card--clickable",
1897
+ className
1898
+ ].filter(Boolean).join(" ");
1899
+ const renderImage = () => {
1900
+ if (imageSlot) return imageSlot;
1901
+ if (image) {
1902
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
1903
+ "img",
1904
+ {
1905
+ className: "fc-card__image",
1906
+ src: image,
1907
+ alt: typeof title === "string" ? title : "card image"
1908
+ }
1909
+ );
1910
+ }
1911
+ return null;
1912
+ };
1913
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: classes, style, onClick: handleClick, children: [
1914
+ renderImage() && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
1915
+ "div",
1916
+ {
1917
+ className: "fc-card__image-wrapper",
1918
+ style: { height: imageHeight },
1919
+ children: renderImage()
1920
+ }
1921
+ ),
1922
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "fc-card__content", children: [
1923
+ title && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "fc-card__title", children: title }),
1924
+ description && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "fc-card__description", children: description }),
1925
+ extraInfo && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "fc-card__extra-info", children: extraInfo }),
1926
+ actions && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "fc-card__actions", children: actions })
1927
+ ] })
1928
+ ] });
1929
+ };
1930
+
1931
+ // src/components/Tabs/Tabs.tsx
1932
+ var import_react10 = require("react");
1933
+ var import_jsx_runtime18 = require("react/jsx-runtime");
1934
+ var Tabs = ({
1935
+ items,
1936
+ activeKey: controlledActiveKey,
1937
+ defaultActiveKey,
1938
+ radius = "md",
1939
+ closable = false,
1940
+ addable = false,
1941
+ onChange,
1942
+ onClose,
1943
+ onAdd,
1944
+ className = "",
1945
+ style
1946
+ }) => {
1947
+ const [internalActiveKey, setInternalActiveKey] = (0, import_react10.useState)(
1948
+ defaultActiveKey || items[0]?.key || ""
1949
+ );
1950
+ const activeKey = controlledActiveKey !== void 0 ? controlledActiveKey : internalActiveKey;
1951
+ const handleTabClick = (key, disabled) => {
1952
+ if (disabled) return;
1953
+ if (controlledActiveKey === void 0) {
1954
+ setInternalActiveKey(key);
1955
+ }
1956
+ onChange?.(key);
1957
+ };
1958
+ const handleClose = (e, key) => {
1959
+ e.stopPropagation();
1960
+ onClose?.(key);
1961
+ if (key === activeKey) {
1962
+ const currentIndex = items.findIndex((item) => item.key === key);
1963
+ const nextItem = items[currentIndex + 1] || items[currentIndex - 1];
1964
+ if (nextItem && !nextItem.disabled) {
1965
+ handleTabClick(nextItem.key);
1966
+ }
1967
+ }
1968
+ };
1969
+ const classes = [
1970
+ "fc-tabs",
1971
+ `fc-tabs--radius-${radius}`,
1972
+ className
1973
+ ].filter(Boolean).join(" ");
1974
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: classes, style, children: [
1975
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { className: "fc-tabs__nav", children: /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "fc-tabs__nav-wrap", children: [
1976
+ items.map((item) => {
1977
+ const isActive = activeKey === item.key;
1978
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(
1979
+ "div",
1980
+ {
1981
+ className: `fc-tabs__tab ${isActive ? "fc-tabs__tab--active" : ""} ${item.disabled ? "fc-tabs__tab--disabled" : ""}`,
1982
+ onClick: () => handleTabClick(item.key, item.disabled),
1983
+ children: [
1984
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "fc-tabs__tab-label", children: item.label }),
1985
+ closable && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
1986
+ "span",
1987
+ {
1988
+ className: "fc-tabs__tab-close",
1989
+ onClick: (e) => handleClose(e, item.key),
1990
+ children: "\xD7"
1991
+ }
1992
+ )
1993
+ ]
1994
+ },
1995
+ item.key
1996
+ );
1997
+ }),
1998
+ addable && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { className: "fc-tabs__add-btn", onClick: onAdd, children: "+" })
1999
+ ] }) }),
2000
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { className: "fc-tabs__content", children: items.find((item) => item.key === activeKey)?.content })
2001
+ ] });
2002
+ };
2003
+
2004
+ // src/components/Chat/Chat.tsx
2005
+ var import_react11 = require("react");
2006
+ var import_jsx_runtime19 = require("react/jsx-runtime");
2007
+ var Chat = ({
2008
+ messages = [],
2009
+ onSendMessage,
2010
+ placeholder = "\u8F93\u5165\u6D88\u606F...",
2011
+ title = "AI \u52A9\u624B",
2012
+ height = "600px",
2013
+ showHeader = true,
2014
+ showFooter = true,
2015
+ loading = false,
2016
+ disabled = false,
2017
+ theme: propTheme,
2018
+ userName = "\u6211",
2019
+ assistantName = "AI\u52A9\u624B",
2020
+ onTyping,
2021
+ maxInputLength = 2e3,
2022
+ autoFocus = true,
2023
+ enableCopy = true
2024
+ }) => {
2025
+ const [inputValue, setInputValue] = (0, import_react11.useState)("");
2026
+ const [isTyping, setIsTyping] = (0, import_react11.useState)(false);
2027
+ const [isSending, setIsSending] = (0, import_react11.useState)(false);
2028
+ const [currentTheme, setCurrentTheme] = (0, import_react11.useState)("light");
2029
+ const [copiedMessageId, setCopiedMessageId] = (0, import_react11.useState)(null);
2030
+ const [copyError, setCopyError] = (0, import_react11.useState)(null);
2031
+ const messagesEndRef = (0, import_react11.useRef)(null);
2032
+ const inputRef = (0, import_react11.useRef)(null);
2033
+ (0, import_react11.useEffect)(() => {
2034
+ if (propTheme) {
2035
+ setCurrentTheme(propTheme);
2036
+ } else {
2037
+ const checkTheme = () => {
2038
+ const dataTheme = document.documentElement.getAttribute("data-theme");
2039
+ if (dataTheme === "dark") {
2040
+ setCurrentTheme("dark");
2041
+ } else if (dataTheme === "light") {
2042
+ setCurrentTheme("light");
2043
+ } else {
2044
+ const hasDarkClass = document.documentElement.classList.contains("dark") || document.body.classList.contains("dark") || document.documentElement.classList.contains("theme-dark") || document.body.classList.contains("theme-dark");
2045
+ setCurrentTheme(hasDarkClass ? "dark" : "light");
2046
+ }
2047
+ };
2048
+ checkTheme();
2049
+ const observer = new MutationObserver(checkTheme);
2050
+ observer.observe(document.documentElement, {
2051
+ attributes: true,
2052
+ attributeFilter: ["data-theme", "class"]
2053
+ });
2054
+ observer.observe(document.body, {
2055
+ attributes: true,
2056
+ attributeFilter: ["class"]
2057
+ });
2058
+ return () => observer.disconnect();
2059
+ }
2060
+ }, [propTheme]);
2061
+ (0, import_react11.useEffect)(() => {
2062
+ messagesEndRef.current?.scrollIntoView({ behavior: "smooth" });
2063
+ }, [messages, loading]);
2064
+ (0, import_react11.useEffect)(() => {
2065
+ if (autoFocus && !disabled && inputRef.current) {
2066
+ inputRef.current.focus();
2067
+ }
2068
+ }, [autoFocus, disabled]);
2069
+ (0, import_react11.useEffect)(() => {
2070
+ if (copyError) {
2071
+ const timer = setTimeout(() => {
2072
+ setCopyError(null);
2073
+ }, 2e3);
2074
+ return () => clearTimeout(timer);
2075
+ }
2076
+ }, [copyError]);
2077
+ const handleCopyMessage = (0, import_react11.useCallback)(async (messageId, content) => {
2078
+ if (!navigator.clipboard) {
2079
+ setCopyError("\u5F53\u524D\u6D4F\u89C8\u5668\u4E0D\u652F\u6301\u590D\u5236\u529F\u80FD");
2080
+ return;
2081
+ }
2082
+ try {
2083
+ await navigator.clipboard.writeText(content);
2084
+ setCopiedMessageId(messageId);
2085
+ setTimeout(() => {
2086
+ setCopiedMessageId(null);
2087
+ }, 2e3);
2088
+ } catch (err) {
2089
+ console.error("\u590D\u5236\u5931\u8D25:", err);
2090
+ if (err instanceof Error) {
2091
+ if (err.name === "NotAllowedError") {
2092
+ setCopyError("\u9700\u8981\u526A\u8D34\u677F\u6743\u9650\uFF0C\u8BF7\u5141\u8BB8\u540E\u91CD\u8BD5");
2093
+ } else if (err.name === "SecurityError") {
2094
+ setCopyError("\u51FA\u4E8E\u5B89\u5168\u539F\u56E0\uFF0C\u65E0\u6CD5\u590D\u5236\u5185\u5BB9");
2095
+ } else {
2096
+ setCopyError("\u590D\u5236\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5");
2097
+ }
2098
+ } else {
2099
+ setCopyError("\u590D\u5236\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5");
2100
+ }
2101
+ setTimeout(() => {
2102
+ setCopyError(null);
2103
+ }, 3e3);
2104
+ }
2105
+ }, []);
2106
+ const handleSend = (0, import_react11.useCallback)(() => {
2107
+ const trimmedMessage = inputValue.trim();
2108
+ if (!trimmedMessage || disabled || loading || isSending) return;
2109
+ if (trimmedMessage.length > maxInputLength) {
2110
+ console.warn(`\u6D88\u606F\u8D85\u8FC7\u6700\u5927\u957F\u5EA6\u9650\u5236: ${maxInputLength}`);
2111
+ return;
2112
+ }
2113
+ setIsSending(true);
2114
+ const sendPromise = onSendMessage?.(trimmedMessage);
2115
+ if (sendPromise) {
2116
+ void sendPromise.then(() => {
2117
+ setInputValue("");
2118
+ if (inputRef.current) {
2119
+ inputRef.current.style.height = "auto";
2120
+ }
2121
+ if (isTyping) {
2122
+ setIsTyping(false);
2123
+ onTyping?.(false);
2124
+ }
2125
+ }).catch((error) => {
2126
+ console.error("\u53D1\u9001\u6D88\u606F\u5931\u8D25:", error);
2127
+ }).finally(() => {
2128
+ setIsSending(false);
2129
+ });
2130
+ } else {
2131
+ setInputValue("");
2132
+ if (inputRef.current) {
2133
+ inputRef.current.style.height = "auto";
2134
+ }
2135
+ if (isTyping) {
2136
+ setIsTyping(false);
2137
+ onTyping?.(false);
2138
+ }
2139
+ setIsSending(false);
2140
+ }
2141
+ }, [inputValue, disabled, loading, isSending, onSendMessage, maxInputLength, isTyping, onTyping]);
2142
+ const handleKeyDown = (0, import_react11.useCallback)((e) => {
2143
+ if (e.key === "Enter" && !e.shiftKey && !e.nativeEvent.isComposing) {
2144
+ e.preventDefault();
2145
+ handleSend();
2146
+ }
2147
+ }, [handleSend]);
2148
+ const handleInputChange = (0, import_react11.useCallback)((e) => {
2149
+ const value = e.target.value;
2150
+ if (value.length <= maxInputLength) {
2151
+ setInputValue(value);
2152
+ e.target.style.height = "auto";
2153
+ e.target.style.height = `${Math.min(e.target.scrollHeight, 100)}px`;
2154
+ const hasContent = value.length > 0;
2155
+ if (hasContent !== isTyping) {
2156
+ setIsTyping(hasContent);
2157
+ onTyping?.(hasContent);
2158
+ }
2159
+ }
2160
+ }, [maxInputLength, isTyping, onTyping]);
2161
+ const formatTime = (0, import_react11.useCallback)((date) => {
2162
+ return new Date(date).toLocaleTimeString("zh-CN", {
2163
+ hour: "2-digit",
2164
+ minute: "2-digit"
2165
+ });
2166
+ }, []);
2167
+ const renderMessage = (message) => {
2168
+ const isUser = message.type === "user";
2169
+ const isSystem = message.type === "system";
2170
+ const isCopied = copiedMessageId === message.id;
2171
+ if (isSystem) {
2172
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("div", { className: "chat-message-system", children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "system-content", children: message.content }) }, message.id);
2173
+ }
2174
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: `chat-message ${isUser ? "user" : "assistant"}`, children: [
2175
+ !isUser && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("div", { className: "message-avatar", children: assistantName[0] }),
2176
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "message-content-wrapper", children: [
2177
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "message-header", children: [
2178
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "message-sender", children: isUser ? userName : assistantName }),
2179
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "message-time", children: formatTime(message.timestamp) })
2180
+ ] }),
2181
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "message-bubble", children: [
2182
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("div", { className: "message-text", children: message.content }),
2183
+ message.status === "sending" && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "message-status sending", children: "\u53D1\u9001\u4E2D..." }),
2184
+ message.status === "error" && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "message-status error", children: "\u53D1\u9001\u5931\u8D25" })
2185
+ ] }),
2186
+ enableCopy && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
2187
+ "button",
2188
+ {
2189
+ className: `copy-btn ${isCopied ? "copied" : ""}`,
2190
+ onClick: () => handleCopyMessage(message.id, message.content),
2191
+ title: isCopied ? "\u5DF2\u590D\u5236" : "\u590D\u5236\u5185\u5BB9",
2192
+ "aria-label": isCopied ? "\u5DF2\u590D\u5236" : "\u590D\u5236\u5185\u5BB9",
2193
+ children: isCopied ? /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(import_jsx_runtime19.Fragment, { children: [
2194
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("path", { d: "M20 6L9 17L4 12", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }) }),
2195
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { children: "\u5DF2\u590D\u5236" })
2196
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(import_jsx_runtime19.Fragment, { children: [
2197
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: [
2198
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("rect", { x: "9", y: "9", width: "13", height: "13", rx: "2", stroke: "currentColor", strokeWidth: "2" }),
2199
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("path", { d: "M5 15H4C2.9 15 2 14.1 2 13V4C2 2.9 2.9 2 4 2H13C14.1 2 15 2.9 15 4V5", stroke: "currentColor", strokeWidth: "2" })
2200
+ ] }),
2201
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { children: "\u590D\u5236" })
2202
+ ] })
2203
+ }
2204
+ )
2205
+ ] })
2206
+ ] }, message.id);
2207
+ };
2208
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: `chat-container ${currentTheme}`, style: { height }, children: [
2209
+ showHeader && /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "chat-header", children: [
2210
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("div", { className: "chat-title", children: title }),
2211
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "chat-status", children: [
2212
+ (loading || isSending) && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "status-dot" }),
2213
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { children: loading || isSending ? "AI \u6B63\u5728\u601D\u8003..." : "\u5728\u7EBF" })
2214
+ ] })
2215
+ ] }),
2216
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "chat-messages", children: [
2217
+ messages.map(renderMessage),
2218
+ (loading || isSending) && /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "chat-message assistant", children: [
2219
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("div", { className: "message-avatar", children: assistantName[0] }),
2220
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "message-content-wrapper", children: [
2221
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("div", { className: "message-header", children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "message-sender", children: assistantName }) }),
2222
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("div", { className: "message-bubble", children: /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "typing-indicator", children: [
2223
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", {}),
2224
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", {}),
2225
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", {})
2226
+ ] }) })
2227
+ ] })
2228
+ ] }),
2229
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("div", { ref: messagesEndRef })
2230
+ ] }),
2231
+ copyError && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("div", { className: "copy-error-toast", children: copyError }),
2232
+ showFooter && /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "chat-footer", children: [
2233
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "chat-input-wrapper", children: [
2234
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
2235
+ "textarea",
2236
+ {
2237
+ ref: inputRef,
2238
+ className: "chat-input",
2239
+ value: inputValue,
2240
+ onChange: handleInputChange,
2241
+ onKeyDown: handleKeyDown,
2242
+ placeholder,
2243
+ disabled: disabled || loading || isSending,
2244
+ rows: 1,
2245
+ maxLength: maxInputLength,
2246
+ spellCheck: false,
2247
+ autoCorrect: "off",
2248
+ autoCapitalize: "off",
2249
+ autoComplete: "off"
2250
+ }
2251
+ ),
2252
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
2253
+ "button",
2254
+ {
2255
+ className: `chat-send-btn ${!inputValue.trim() || disabled || loading || isSending ? "disabled" : ""}`,
2256
+ onClick: handleSend,
2257
+ disabled: !inputValue.trim() || disabled || loading || isSending,
2258
+ children: "\u53D1\u9001"
2259
+ }
2260
+ )
2261
+ ] }),
2262
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "chat-tips", children: [
2263
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { children: "\u6309 Enter \u53D1\u9001\uFF0CShift + Enter \u6362\u884C" }),
2264
+ maxInputLength && /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("span", { className: "char-count", children: [
2265
+ inputValue.length,
2266
+ "/",
2267
+ maxInputLength
2268
+ ] })
2269
+ ] })
2270
+ ] })
2271
+ ] });
2272
+ };
48
2273
  // Annotate the CommonJS export names for ESM import in node:
49
2274
  0 && (module.exports = {
50
- Button
2275
+ AlertProvider,
2276
+ Avatar,
2277
+ Button,
2278
+ ButtonGroup,
2279
+ ButtonToolbar,
2280
+ Card,
2281
+ Chat,
2282
+ CheckButton,
2283
+ DeleteDialog,
2284
+ Input,
2285
+ ListGroup,
2286
+ ListGroupItem,
2287
+ OrphanDialog,
2288
+ RollingBox,
2289
+ Select,
2290
+ SideBar,
2291
+ Slider,
2292
+ Tabs,
2293
+ ThemeProvider,
2294
+ Tree,
2295
+ VirtualList,
2296
+ findNodeInfo,
2297
+ flatToTree,
2298
+ isDescendantOf,
2299
+ lazyLoad,
2300
+ useAlert,
2301
+ useTheme
51
2302
  });