flowcloudai-ui 0.1.0 → 0.1.2

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 DELETED
@@ -1,2302 +0,0 @@
1
- "use strict";
2
- var __create = Object.create;
3
- var __defProp = Object.defineProperty;
4
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
- var __getOwnPropNames = Object.getOwnPropertyNames;
6
- var __getProtoOf = Object.getPrototypeOf;
7
- var __hasOwnProp = Object.prototype.hasOwnProperty;
8
- var __export = (target, all) => {
9
- for (var name in all)
10
- __defProp(target, name, { get: all[name], enumerable: true });
11
- };
12
- var __copyProps = (to, from, except, desc) => {
13
- if (from && typeof from === "object" || typeof from === "function") {
14
- for (let key of __getOwnPropNames(from))
15
- if (!__hasOwnProp.call(to, key) && key !== except)
16
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
- }
18
- return to;
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
- ));
28
- var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
-
30
- // src/index.ts
31
- var index_exports = {};
32
- __export(index_exports, {
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
60
- });
61
- module.exports = __toCommonJS(index_exports);
62
-
63
- // src/ThemeProvider.tsx
64
- var import_react = require("react");
65
- var import_jsx_runtime = require("react/jsx-runtime");
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)(
130
- "button",
131
- {
132
- className: classNames,
133
- disabled: disabled || loading,
134
- ...props,
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
- ]
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)
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
- ] }) });
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
- };
2273
- // Annotate the CommonJS export names for ESM import in node:
2274
- 0 && (module.exports = {
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
2302
- });