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.css +1325 -587
- package/dist/index.d.ts +365 -79
- package/dist/index.js +1647 -629
- package/package.json +41 -41
- package/dist/index.cjs +0 -2302
- package/dist/index.d.cts +0 -390
package/dist/index.js
CHANGED
|
@@ -16,16 +16,7 @@ function ThemeProvider({ children, defaultTheme = "system", target }) {
|
|
|
16
16
|
const resolvedTheme = theme === "system" ? systemTheme : theme;
|
|
17
17
|
useEffect(() => {
|
|
18
18
|
const el = target ?? document.documentElement;
|
|
19
|
-
el.classList.remove("theme-light", "theme-dark");
|
|
20
|
-
el.classList.add(`theme-${resolvedTheme}`);
|
|
21
19
|
el.setAttribute("data-theme", resolvedTheme);
|
|
22
|
-
if (resolvedTheme === "dark") {
|
|
23
|
-
document.body.style.backgroundColor = "#0F0F0F";
|
|
24
|
-
document.body.style.color = "#E8E8E6";
|
|
25
|
-
} else {
|
|
26
|
-
document.body.style.backgroundColor = "";
|
|
27
|
-
document.body.style.color = "";
|
|
28
|
-
}
|
|
29
20
|
}, [resolvedTheme, target]);
|
|
30
21
|
useEffect(() => {
|
|
31
22
|
if (theme !== "system") return;
|
|
@@ -42,6 +33,7 @@ import { jsx as jsx2, jsxs } from "react/jsx-runtime";
|
|
|
42
33
|
function Button({
|
|
43
34
|
variant = "primary",
|
|
44
35
|
size = "md",
|
|
36
|
+
radius,
|
|
45
37
|
disabled = false,
|
|
46
38
|
loading = false,
|
|
47
39
|
block = false,
|
|
@@ -49,14 +41,44 @@ function Button({
|
|
|
49
41
|
iconOnly = false,
|
|
50
42
|
iconLeft,
|
|
51
43
|
iconRight,
|
|
44
|
+
background,
|
|
45
|
+
hoverBackground,
|
|
46
|
+
activeBackground,
|
|
47
|
+
color,
|
|
48
|
+
hoverColor,
|
|
49
|
+
activeColor,
|
|
50
|
+
borderColor,
|
|
51
|
+
hoverBorderColor,
|
|
52
52
|
className,
|
|
53
|
+
style,
|
|
53
54
|
children,
|
|
54
55
|
...props
|
|
55
56
|
}) {
|
|
57
|
+
const colorVars = {
|
|
58
|
+
"--btn-bg": background,
|
|
59
|
+
"--btn-bg-hover": hoverBackground,
|
|
60
|
+
"--btn-bg-active": activeBackground,
|
|
61
|
+
"--btn-color": color,
|
|
62
|
+
"--btn-color-hover": hoverColor,
|
|
63
|
+
"--btn-color-active": activeColor,
|
|
64
|
+
"--btn-border": borderColor,
|
|
65
|
+
"--btn-border-hover": hoverBorderColor
|
|
66
|
+
};
|
|
67
|
+
const overrideStyle = {};
|
|
68
|
+
for (const [key, value] of Object.entries(colorVars)) {
|
|
69
|
+
if (value !== void 0) {
|
|
70
|
+
overrideStyle[key] = value;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
const mergedStyle = {
|
|
74
|
+
...overrideStyle,
|
|
75
|
+
...style
|
|
76
|
+
};
|
|
56
77
|
const classNames = [
|
|
57
78
|
"fc-btn",
|
|
58
79
|
`fc-btn--${variant}`,
|
|
59
80
|
`fc-btn--${size}`,
|
|
81
|
+
radius && `fc-btn--radius-${radius}`,
|
|
60
82
|
block && "fc-btn--block",
|
|
61
83
|
circle && "fc-btn--circle",
|
|
62
84
|
iconOnly && "fc-btn--icon-only",
|
|
@@ -69,6 +91,7 @@ function Button({
|
|
|
69
91
|
{
|
|
70
92
|
className: classNames,
|
|
71
93
|
disabled: disabled || loading,
|
|
94
|
+
style: mergedStyle,
|
|
72
95
|
...props,
|
|
73
96
|
children: [
|
|
74
97
|
iconLeft && !loading && /* @__PURE__ */ jsx2("span", { className: "fc-btn__icon fc-btn__icon--left", children: iconLeft }),
|
|
@@ -105,8 +128,14 @@ function CheckButton({
|
|
|
105
128
|
onChange,
|
|
106
129
|
disabled = false,
|
|
107
130
|
size = "md",
|
|
131
|
+
radius,
|
|
108
132
|
labelLeft,
|
|
109
133
|
labelRight,
|
|
134
|
+
trackBackground,
|
|
135
|
+
checkedTrackBackground,
|
|
136
|
+
thumbBackground,
|
|
137
|
+
thumbDotColor,
|
|
138
|
+
labelColor,
|
|
110
139
|
className = "",
|
|
111
140
|
style
|
|
112
141
|
}) {
|
|
@@ -125,9 +154,27 @@ function CheckButton({
|
|
|
125
154
|
toggle();
|
|
126
155
|
}
|
|
127
156
|
};
|
|
157
|
+
const colorVars = {
|
|
158
|
+
"--check-track-bg": trackBackground,
|
|
159
|
+
"--check-track-bg-checked": checkedTrackBackground,
|
|
160
|
+
"--check-thumb-bg": thumbBackground,
|
|
161
|
+
"--check-thumb-dot-color": thumbDotColor,
|
|
162
|
+
"--check-label-color": labelColor
|
|
163
|
+
};
|
|
164
|
+
const overrideStyle = {};
|
|
165
|
+
for (const [key, value] of Object.entries(colorVars)) {
|
|
166
|
+
if (value !== void 0) {
|
|
167
|
+
overrideStyle[key] = value;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
const mergedStyle = {
|
|
171
|
+
...overrideStyle,
|
|
172
|
+
...style
|
|
173
|
+
};
|
|
128
174
|
const cls = [
|
|
129
175
|
"fc-check",
|
|
130
176
|
`fc-check--${size}`,
|
|
177
|
+
radius && `fc-check--radius-${radius}`,
|
|
131
178
|
checked && "fc-check--checked",
|
|
132
179
|
disabled && "fc-check--disabled",
|
|
133
180
|
className
|
|
@@ -136,7 +183,7 @@ function CheckButton({
|
|
|
136
183
|
"div",
|
|
137
184
|
{
|
|
138
185
|
className: cls,
|
|
139
|
-
style,
|
|
186
|
+
style: mergedStyle,
|
|
140
187
|
role: "switch",
|
|
141
188
|
"aria-checked": checked,
|
|
142
189
|
tabIndex: disabled ? -1 : 0,
|
|
@@ -162,8 +209,26 @@ function RollingBox({
|
|
|
162
209
|
showTrack = false,
|
|
163
210
|
children,
|
|
164
211
|
className,
|
|
212
|
+
thumbColor,
|
|
213
|
+
thumbHoverColor,
|
|
214
|
+
thumbActiveColor,
|
|
215
|
+
trackColor,
|
|
216
|
+
style,
|
|
165
217
|
...props
|
|
166
218
|
}) {
|
|
219
|
+
const colorVars = {
|
|
220
|
+
"--roll-thumb": thumbColor,
|
|
221
|
+
"--roll-thumb-hover": thumbHoverColor,
|
|
222
|
+
"--roll-thumb-active": thumbActiveColor,
|
|
223
|
+
"--roll-track": trackColor
|
|
224
|
+
};
|
|
225
|
+
const overrideStyle = {};
|
|
226
|
+
for (const [key, value] of Object.entries(colorVars)) {
|
|
227
|
+
if (value !== void 0) {
|
|
228
|
+
overrideStyle[key] = value;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
const mergedStyle = { ...overrideStyle, ...style };
|
|
167
232
|
const containerRef = React2.useRef(null);
|
|
168
233
|
const [isScrolling, setIsScrolling] = React2.useState(false);
|
|
169
234
|
const scrollTimeoutRef = React2.useRef(null);
|
|
@@ -184,6 +249,17 @@ function RollingBox({
|
|
|
184
249
|
}
|
|
185
250
|
};
|
|
186
251
|
}, []);
|
|
252
|
+
React2.useEffect(() => {
|
|
253
|
+
const el = containerRef.current;
|
|
254
|
+
if (!el || !horizontal) return;
|
|
255
|
+
const handler = (e) => {
|
|
256
|
+
if (e.deltaY === 0) return;
|
|
257
|
+
e.preventDefault();
|
|
258
|
+
el.scrollLeft += e.deltaY;
|
|
259
|
+
};
|
|
260
|
+
el.addEventListener("wheel", handler, { passive: false });
|
|
261
|
+
return () => el.removeEventListener("wheel", handler);
|
|
262
|
+
}, [horizontal]);
|
|
187
263
|
const resolvedDirection = horizontal ? "horizontal" : "vertical";
|
|
188
264
|
const classNames = [
|
|
189
265
|
"fc-roll",
|
|
@@ -199,6 +275,7 @@ function RollingBox({
|
|
|
199
275
|
{
|
|
200
276
|
ref: containerRef,
|
|
201
277
|
className: classNames,
|
|
278
|
+
style: mergedStyle,
|
|
202
279
|
onScroll: handleScroll,
|
|
203
280
|
...props,
|
|
204
281
|
children: /* @__PURE__ */ jsx4("div", { className: "fc-roll__content", children })
|
|
@@ -207,134 +284,110 @@ function RollingBox({
|
|
|
207
284
|
}
|
|
208
285
|
|
|
209
286
|
// src/components/Bar/SideBar.tsx
|
|
210
|
-
import
|
|
287
|
+
import { memo, useCallback as useCallback2 } from "react";
|
|
211
288
|
import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
collapsed: controlledCollapsed,
|
|
221
|
-
defaultCollapsed = false,
|
|
222
|
-
onCollapse,
|
|
223
|
-
className,
|
|
224
|
-
width = 240,
|
|
225
|
-
collapsedWidth = 64
|
|
226
|
-
}) {
|
|
227
|
-
const [internalSelected, setInternalSelected] = React3.useState(defaultSelectedKeys);
|
|
228
|
-
const [internalOpen, setInternalOpen] = React3.useState(defaultOpenKeys);
|
|
229
|
-
const [internalCollapsed, setInternalCollapsed] = React3.useState(defaultCollapsed);
|
|
230
|
-
const currentSelected = controlledSelected ?? internalSelected;
|
|
231
|
-
const currentOpen = controlledOpen ?? internalOpen;
|
|
232
|
-
const currentCollapsed = controlledCollapsed ?? internalCollapsed;
|
|
233
|
-
const updateOpen = React3.useCallback((next) => {
|
|
234
|
-
if (controlledOpen === void 0) setInternalOpen(next);
|
|
235
|
-
onOpenChange?.(next);
|
|
236
|
-
}, [controlledOpen, onOpenChange]);
|
|
237
|
-
const toggleOpen = React3.useCallback((key) => {
|
|
238
|
-
const next = currentOpen.includes(key) ? currentOpen.filter((k) => k !== key) : [...currentOpen, key];
|
|
239
|
-
updateOpen(next);
|
|
240
|
-
}, [currentOpen, updateOpen]);
|
|
241
|
-
const handleSelect = React3.useCallback((key, item) => {
|
|
242
|
-
if (item.disabled) return;
|
|
243
|
-
const next = [key];
|
|
244
|
-
if (controlledSelected === void 0) setInternalSelected(next);
|
|
245
|
-
onSelect?.(next);
|
|
246
|
-
}, [controlledSelected, onSelect]);
|
|
247
|
-
const toggleCollapse = React3.useCallback(() => {
|
|
248
|
-
const next = !currentCollapsed;
|
|
249
|
-
if (controlledCollapsed === void 0) setInternalCollapsed(next);
|
|
250
|
-
onCollapse?.(next);
|
|
251
|
-
}, [currentCollapsed, controlledCollapsed, onCollapse]);
|
|
252
|
-
const handleItemClick = React3.useCallback((item) => {
|
|
253
|
-
if (item.children?.length) {
|
|
254
|
-
toggleOpen(item.key);
|
|
255
|
-
} else {
|
|
256
|
-
handleSelect(item.key, item);
|
|
257
|
-
}
|
|
258
|
-
}, [toggleOpen, handleSelect]);
|
|
259
|
-
const handleItemKeyDown = React3.useCallback((e, item) => {
|
|
260
|
-
if (e.key === "Enter" || e.key === " ") {
|
|
261
|
-
e.preventDefault();
|
|
262
|
-
handleItemClick(item);
|
|
263
|
-
}
|
|
264
|
-
}, [handleItemClick]);
|
|
265
|
-
const renderMenuItem = (item, level = 0) => {
|
|
266
|
-
const hasChildren = (item.children?.length ?? 0) > 0;
|
|
267
|
-
const isOpen = currentOpen.includes(item.key);
|
|
268
|
-
const isSelected = currentSelected.includes(item.key);
|
|
269
|
-
const itemClassName = [
|
|
270
|
-
"fc-sidebar__item",
|
|
271
|
-
`fc-sidebar__item--level-${level}`,
|
|
272
|
-
hasChildren && "fc-sidebar__item--parent",
|
|
273
|
-
isOpen && "fc-sidebar__item--open",
|
|
274
|
-
isSelected && "fc-sidebar__item--selected",
|
|
275
|
-
item.disabled && "fc-sidebar__item--disabled"
|
|
276
|
-
].filter(Boolean).join(" ");
|
|
277
|
-
const Tag = item.href ? "a" : "div";
|
|
278
|
-
const interactiveProps = {
|
|
279
|
-
role: hasChildren ? "treeitem" : "menuitem",
|
|
280
|
-
tabIndex: item.disabled ? -1 : 0,
|
|
281
|
-
"aria-selected": isSelected || void 0,
|
|
282
|
-
"aria-expanded": hasChildren ? isOpen : void 0,
|
|
283
|
-
"aria-disabled": item.disabled || void 0,
|
|
284
|
-
onClick: () => handleItemClick(item),
|
|
285
|
-
onKeyDown: (e) => handleItemKeyDown(e, item),
|
|
286
|
-
...item.href ? { href: item.href } : {}
|
|
287
|
-
};
|
|
288
|
-
return /* @__PURE__ */ jsxs3("div", { className: "fc-sidebar__item-wrapper", children: [
|
|
289
|
-
/* @__PURE__ */ jsxs3(Tag, { className: itemClassName, ...interactiveProps, children: [
|
|
290
|
-
item.icon && /* @__PURE__ */ jsx5("span", { className: "fc-sidebar__icon", "aria-hidden": "true", children: item.icon }),
|
|
291
|
-
/* @__PURE__ */ jsx5("span", { className: "fc-sidebar__label", children: item.label }),
|
|
292
|
-
hasChildren && /* @__PURE__ */ jsx5("span", { className: "fc-sidebar__arrow", "aria-hidden": "true", children: "\u25B6" })
|
|
293
|
-
] }),
|
|
294
|
-
hasChildren && /* @__PURE__ */ jsx5(
|
|
295
|
-
"div",
|
|
296
|
-
{
|
|
297
|
-
className: [
|
|
298
|
-
"fc-sidebar__submenu",
|
|
299
|
-
isOpen && "fc-sidebar__submenu--open"
|
|
300
|
-
].filter(Boolean).join(" "),
|
|
301
|
-
role: "group",
|
|
302
|
-
children: /* @__PURE__ */ jsx5("div", { className: "fc-sidebar__submenu-inner", children: item.children.map((child) => renderMenuItem(child, level + 1)) })
|
|
303
|
-
}
|
|
304
|
-
)
|
|
305
|
-
] }, item.key);
|
|
306
|
-
};
|
|
307
|
-
const sidebarStyle = {
|
|
308
|
-
"--sidebar-width": `${currentCollapsed ? collapsedWidth : width}px`,
|
|
309
|
-
"--sidebar-collapsed-width": `${collapsedWidth}px`
|
|
310
|
-
};
|
|
289
|
+
var SideBarItemView = memo(({ item, isSelected, onClick }) => {
|
|
290
|
+
const classes = [
|
|
291
|
+
"fc-sidebar__item",
|
|
292
|
+
isSelected && "fc-sidebar__item--selected",
|
|
293
|
+
item.disabled && "fc-sidebar__item--disabled"
|
|
294
|
+
].filter(Boolean).join(" ");
|
|
295
|
+
const Tag = item.href ? "a" : "div";
|
|
296
|
+
const linkProps = item.href ? { href: item.href } : {};
|
|
311
297
|
return /* @__PURE__ */ jsxs3(
|
|
312
|
-
|
|
298
|
+
Tag,
|
|
313
299
|
{
|
|
314
|
-
className:
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
className
|
|
318
|
-
].filter(Boolean).join(" "),
|
|
319
|
-
style: sidebarStyle,
|
|
320
|
-
role: "navigation",
|
|
321
|
-
"aria-label": "Sidebar",
|
|
300
|
+
className: classes,
|
|
301
|
+
onClick: () => !item.disabled && onClick(item.key),
|
|
302
|
+
...linkProps,
|
|
322
303
|
children: [
|
|
323
|
-
/* @__PURE__ */ jsx5("
|
|
324
|
-
|
|
325
|
-
{
|
|
326
|
-
className: "fc-sidebar__collapse-btn",
|
|
327
|
-
onClick: toggleCollapse,
|
|
328
|
-
"aria-label": currentCollapsed ? "\u5C55\u5F00\u4FA7\u680F" : "\u6536\u8D77\u4FA7\u680F",
|
|
329
|
-
"aria-expanded": !currentCollapsed,
|
|
330
|
-
children: currentCollapsed ? "\u2192" : "\u2190"
|
|
331
|
-
}
|
|
332
|
-
) }),
|
|
333
|
-
/* @__PURE__ */ jsx5("nav", { className: "fc-sidebar__menu", role: "tree", children: items.map((item) => renderMenuItem(item)) })
|
|
304
|
+
item.icon && /* @__PURE__ */ jsx5("span", { className: "fc-sidebar__icon", children: item.icon }),
|
|
305
|
+
/* @__PURE__ */ jsx5("span", { className: "fc-sidebar__label", children: item.label })
|
|
334
306
|
]
|
|
335
307
|
}
|
|
336
308
|
);
|
|
337
|
-
}
|
|
309
|
+
});
|
|
310
|
+
SideBarItemView.displayName = "SideBarItemView";
|
|
311
|
+
var SideBar = memo(({
|
|
312
|
+
items,
|
|
313
|
+
bottomItems,
|
|
314
|
+
selectedKey,
|
|
315
|
+
collapsed,
|
|
316
|
+
width = 240,
|
|
317
|
+
collapsedWidth = 64,
|
|
318
|
+
onSelect,
|
|
319
|
+
onCollapse,
|
|
320
|
+
className = "",
|
|
321
|
+
style
|
|
322
|
+
}) => {
|
|
323
|
+
const handleClick = useCallback2(
|
|
324
|
+
(key) => onSelect(key),
|
|
325
|
+
[onSelect]
|
|
326
|
+
);
|
|
327
|
+
const toggleCollapse = useCallback2(
|
|
328
|
+
() => onCollapse(!collapsed),
|
|
329
|
+
[collapsed, onCollapse]
|
|
330
|
+
);
|
|
331
|
+
const rootClasses = [
|
|
332
|
+
"fc-sidebar",
|
|
333
|
+
collapsed && "fc-sidebar--collapsed",
|
|
334
|
+
className
|
|
335
|
+
].filter(Boolean).join(" ");
|
|
336
|
+
const rootStyle = {
|
|
337
|
+
"--sidebar-width": `${collapsed ? collapsedWidth : width}px`,
|
|
338
|
+
"--sidebar-collapsed-width": `${collapsedWidth}px`,
|
|
339
|
+
...style
|
|
340
|
+
};
|
|
341
|
+
return /* @__PURE__ */ jsxs3("aside", { className: rootClasses, style: rootStyle, children: [
|
|
342
|
+
/* @__PURE__ */ jsx5("div", { className: "fc-sidebar__header", children: /* @__PURE__ */ jsx5(
|
|
343
|
+
"button",
|
|
344
|
+
{
|
|
345
|
+
className: "fc-sidebar__collapse-btn",
|
|
346
|
+
onClick: toggleCollapse,
|
|
347
|
+
children: /* @__PURE__ */ jsx5(
|
|
348
|
+
"svg",
|
|
349
|
+
{
|
|
350
|
+
className: "fc-sidebar__collapse-icon",
|
|
351
|
+
width: "16",
|
|
352
|
+
height: "16",
|
|
353
|
+
viewBox: "0 0 16 16",
|
|
354
|
+
fill: "none",
|
|
355
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
356
|
+
children: /* @__PURE__ */ jsx5(
|
|
357
|
+
"path",
|
|
358
|
+
{
|
|
359
|
+
d: collapsed ? "M6 3L11 8L6 13" : "M10 3L5 8L10 13",
|
|
360
|
+
stroke: "currentColor",
|
|
361
|
+
strokeWidth: "1.5",
|
|
362
|
+
strokeLinecap: "round",
|
|
363
|
+
strokeLinejoin: "round"
|
|
364
|
+
}
|
|
365
|
+
)
|
|
366
|
+
}
|
|
367
|
+
)
|
|
368
|
+
}
|
|
369
|
+
) }),
|
|
370
|
+
/* @__PURE__ */ jsx5("nav", { className: "fc-sidebar__menu", children: items.map((item) => /* @__PURE__ */ jsx5(
|
|
371
|
+
SideBarItemView,
|
|
372
|
+
{
|
|
373
|
+
item,
|
|
374
|
+
isSelected: selectedKey === item.key,
|
|
375
|
+
onClick: handleClick
|
|
376
|
+
},
|
|
377
|
+
item.key
|
|
378
|
+
)) }),
|
|
379
|
+
bottomItems && bottomItems.length > 0 && /* @__PURE__ */ jsx5("div", { className: "fc-sidebar__footer", children: bottomItems.map((item) => /* @__PURE__ */ jsx5(
|
|
380
|
+
SideBarItemView,
|
|
381
|
+
{
|
|
382
|
+
item,
|
|
383
|
+
isSelected: selectedKey === item.key,
|
|
384
|
+
onClick: handleClick
|
|
385
|
+
},
|
|
386
|
+
item.key
|
|
387
|
+
)) })
|
|
388
|
+
] });
|
|
389
|
+
});
|
|
390
|
+
SideBar.displayName = "SideBar";
|
|
338
391
|
|
|
339
392
|
// src/components/Input/Input.tsx
|
|
340
393
|
import * as React4 from "react";
|
|
@@ -342,6 +395,7 @@ import { jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
|
342
395
|
var Input = React4.forwardRef(({
|
|
343
396
|
size = "md",
|
|
344
397
|
status = "default",
|
|
398
|
+
radius,
|
|
345
399
|
prefix,
|
|
346
400
|
suffix,
|
|
347
401
|
allowClear = false,
|
|
@@ -364,13 +418,12 @@ var Input = React4.forwardRef(({
|
|
|
364
418
|
const currentValue = isControlled ? value : internalValue;
|
|
365
419
|
const handleChange = (e) => {
|
|
366
420
|
if (!isControlled) setInternalValue(e.target.value);
|
|
367
|
-
onChange?.(e);
|
|
421
|
+
onChange?.(e.target.value);
|
|
368
422
|
};
|
|
369
423
|
const handleClear = () => {
|
|
370
424
|
if (!isControlled) setInternalValue("");
|
|
371
425
|
onClear?.();
|
|
372
|
-
|
|
373
|
-
onChange?.(event);
|
|
426
|
+
onChange?.("");
|
|
374
427
|
};
|
|
375
428
|
const togglePassword = () => {
|
|
376
429
|
setType((prev) => prev === "password" ? "text" : "password");
|
|
@@ -381,6 +434,7 @@ var Input = React4.forwardRef(({
|
|
|
381
434
|
"fc-input",
|
|
382
435
|
`fc-input--${size}`,
|
|
383
436
|
`fc-input--${status}`,
|
|
437
|
+
radius && `fc-input--radius-${radius}`,
|
|
384
438
|
(prefix || addonBefore) && "fc-input--has-prefix",
|
|
385
439
|
(suffix || addonAfter || showClear || showPasswordToggle) && "fc-input--has-suffix",
|
|
386
440
|
addonBefore && "fc-input--addon-before",
|
|
@@ -402,8 +456,8 @@ var Input = React4.forwardRef(({
|
|
|
402
456
|
);
|
|
403
457
|
return /* @__PURE__ */ jsxs4("div", { className: classNames, children: [
|
|
404
458
|
addonBefore && /* @__PURE__ */ jsx6("span", { className: "fc-input__addon fc-input__addon--before", children: addonBefore }),
|
|
405
|
-
prefix && /* @__PURE__ */ jsx6("span", { className: "fc-input__prefix", children: prefix }),
|
|
406
459
|
/* @__PURE__ */ jsxs4("div", { className: "fc-input__wrapper", children: [
|
|
460
|
+
prefix && /* @__PURE__ */ jsx6("span", { className: "fc-input__prefix", children: prefix }),
|
|
407
461
|
input,
|
|
408
462
|
/* @__PURE__ */ jsxs4("span", { className: "fc-input__actions", children: [
|
|
409
463
|
showClear && /* @__PURE__ */ jsx6(
|
|
@@ -450,54 +504,112 @@ function Slider({
|
|
|
450
504
|
disabled = false,
|
|
451
505
|
marks,
|
|
452
506
|
tooltip = false,
|
|
453
|
-
className = ""
|
|
507
|
+
className = "",
|
|
508
|
+
style,
|
|
509
|
+
trackBackground,
|
|
510
|
+
fillBackground,
|
|
511
|
+
thumbBackground,
|
|
512
|
+
thumbBorderColor,
|
|
513
|
+
markDotColor,
|
|
514
|
+
markLabelColor,
|
|
515
|
+
tooltipBackground,
|
|
516
|
+
tooltipColor
|
|
454
517
|
}) {
|
|
455
518
|
const trackRef = React5.useRef(null);
|
|
456
|
-
const draggingRef = React5.useRef(null);
|
|
457
519
|
const [dragging, setDragging] = React5.useState(null);
|
|
520
|
+
const dragCleanupRef = React5.useRef(null);
|
|
521
|
+
React5.useEffect(() => {
|
|
522
|
+
return () => {
|
|
523
|
+
dragCleanupRef.current?.();
|
|
524
|
+
};
|
|
525
|
+
}, []);
|
|
458
526
|
const initialValue = defaultValue ?? (range ? [min, max] : min);
|
|
459
527
|
const [internalValue, setInternalValue] = React5.useState(initialValue);
|
|
460
528
|
const isControlled = controlledValue !== void 0;
|
|
461
529
|
const currentValue = isControlled ? controlledValue : internalValue;
|
|
530
|
+
const colorVars = {
|
|
531
|
+
"--slider-track-bg": trackBackground,
|
|
532
|
+
"--slider-fill-bg": fillBackground,
|
|
533
|
+
"--slider-thumb-bg": thumbBackground,
|
|
534
|
+
"--slider-thumb-border": thumbBorderColor,
|
|
535
|
+
"--slider-mark-dot-bg": markDotColor,
|
|
536
|
+
"--slider-mark-label-color": markLabelColor,
|
|
537
|
+
"--slider-tooltip-bg": tooltipBackground,
|
|
538
|
+
"--slider-tooltip-color": tooltipColor
|
|
539
|
+
};
|
|
540
|
+
const overrideStyle = {};
|
|
541
|
+
for (const [key, value] of Object.entries(colorVars)) {
|
|
542
|
+
if (value !== void 0) {
|
|
543
|
+
overrideStyle[key] = value;
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
const mergedStyle = { ...overrideStyle, ...style };
|
|
462
547
|
const getPercent = (val) => Math.max(0, Math.min(100, (val - min) / (max - min) * 100));
|
|
463
548
|
const getValueFromPercent = (percent) => {
|
|
464
549
|
const raw = min + percent / 100 * (max - min);
|
|
465
550
|
const stepped = Math.round(raw / step) * step;
|
|
466
551
|
return Math.max(min, Math.min(max, stepped));
|
|
467
552
|
};
|
|
468
|
-
const handleMove = React5.useCallback((clientX, clientY) => {
|
|
469
|
-
if (!trackRef.current ||
|
|
553
|
+
const handleMove = React5.useCallback((clientX, clientY, activeIndex) => {
|
|
554
|
+
if (!trackRef.current || disabled) return;
|
|
470
555
|
const rect = trackRef.current.getBoundingClientRect();
|
|
471
556
|
const percent = orientation === "horizontal" ? (clientX - rect.left) / rect.width * 100 : (rect.bottom - clientY) / rect.height * 100;
|
|
472
557
|
const newValue = getValueFromPercent(Math.max(0, Math.min(100, percent)));
|
|
473
558
|
let nextValue;
|
|
474
559
|
if (range) {
|
|
475
560
|
const [start, end] = currentValue;
|
|
476
|
-
nextValue =
|
|
561
|
+
nextValue = activeIndex === 0 ? [Math.min(newValue, end), end] : [start, Math.max(newValue, start)];
|
|
477
562
|
} else {
|
|
478
563
|
nextValue = newValue;
|
|
479
564
|
}
|
|
480
565
|
if (!isControlled) setInternalValue(nextValue);
|
|
481
566
|
onChange?.(nextValue);
|
|
482
567
|
}, [disabled, orientation, range, currentValue, isControlled, onChange, min, max, step]);
|
|
483
|
-
const
|
|
568
|
+
const startDrag = (index, clientX, clientY) => {
|
|
484
569
|
if (disabled) return;
|
|
485
|
-
e.preventDefault();
|
|
486
|
-
draggingRef.current = index;
|
|
487
570
|
setDragging(index);
|
|
488
|
-
const handleMouseMove = (
|
|
489
|
-
const
|
|
490
|
-
|
|
491
|
-
|
|
571
|
+
const handleMouseMove = (ev) => handleMove(ev.clientX, ev.clientY, index);
|
|
572
|
+
const handleTouchMove = (ev) => {
|
|
573
|
+
ev.preventDefault();
|
|
574
|
+
const t = ev.touches[0];
|
|
575
|
+
handleMove(t.clientX, t.clientY, index);
|
|
576
|
+
};
|
|
577
|
+
const cleanup = () => {
|
|
492
578
|
document.removeEventListener("mousemove", handleMouseMove);
|
|
493
579
|
document.removeEventListener("mouseup", handleMouseUp);
|
|
580
|
+
document.removeEventListener("touchmove", handleTouchMove);
|
|
581
|
+
document.removeEventListener("touchend", handleTouchEnd);
|
|
582
|
+
dragCleanupRef.current = null;
|
|
494
583
|
};
|
|
584
|
+
const handleMouseUp = () => {
|
|
585
|
+
setDragging(null);
|
|
586
|
+
cleanup();
|
|
587
|
+
};
|
|
588
|
+
const handleTouchEnd = () => {
|
|
589
|
+
setDragging(null);
|
|
590
|
+
cleanup();
|
|
591
|
+
};
|
|
592
|
+
dragCleanupRef.current = cleanup;
|
|
495
593
|
document.addEventListener("mousemove", handleMouseMove);
|
|
496
594
|
document.addEventListener("mouseup", handleMouseUp);
|
|
595
|
+
document.addEventListener("touchmove", handleTouchMove, { passive: false });
|
|
596
|
+
document.addEventListener("touchend", handleTouchEnd);
|
|
597
|
+
handleMove(clientX, clientY, index);
|
|
598
|
+
};
|
|
599
|
+
const handleMouseDown = (index) => (e) => {
|
|
600
|
+
if (disabled) return;
|
|
601
|
+
e.preventDefault();
|
|
602
|
+
startDrag(index, e.clientX, e.clientY);
|
|
603
|
+
};
|
|
604
|
+
const handleTouchStart = (index) => (e) => {
|
|
605
|
+
if (disabled) return;
|
|
606
|
+
e.preventDefault();
|
|
607
|
+
const t = e.touches[0];
|
|
608
|
+
startDrag(index, t.clientX, t.clientY);
|
|
497
609
|
};
|
|
498
610
|
const handleTrackClick = (e) => {
|
|
499
|
-
if (disabled ||
|
|
500
|
-
handleMove(e.clientX, e.clientY);
|
|
611
|
+
if (disabled || dragging !== null) return;
|
|
612
|
+
handleMove(e.clientX, e.clientY, 0);
|
|
501
613
|
};
|
|
502
614
|
const [startVal, endVal] = range ? currentValue : [min, currentValue];
|
|
503
615
|
const startPercent = getPercent(startVal);
|
|
@@ -512,7 +624,7 @@ function Slider({
|
|
|
512
624
|
dragging !== null && "fc-slider--dragging",
|
|
513
625
|
className
|
|
514
626
|
].filter(Boolean).join(" ");
|
|
515
|
-
return /* @__PURE__ */ jsx7("div", { className: cls, children: /* @__PURE__ */ jsxs5(
|
|
627
|
+
return /* @__PURE__ */ jsx7("div", { className: cls, style: mergedStyle, children: /* @__PURE__ */ jsxs5(
|
|
516
628
|
"div",
|
|
517
629
|
{
|
|
518
630
|
ref: trackRef,
|
|
@@ -532,6 +644,7 @@ function Slider({
|
|
|
532
644
|
className: `fc-slider__thumb ${dragging === 0 ? "fc-slider__thumb--active" : ""}`,
|
|
533
645
|
style: thumbStyle(startPercent),
|
|
534
646
|
onMouseDown: handleMouseDown(0),
|
|
647
|
+
onTouchStart: handleTouchStart(0),
|
|
535
648
|
children: tooltip && /* @__PURE__ */ jsx7("span", { className: "fc-slider__tooltip", children: startVal })
|
|
536
649
|
}
|
|
537
650
|
),
|
|
@@ -541,6 +654,7 @@ function Slider({
|
|
|
541
654
|
className: `fc-slider__thumb ${dragging === (range ? 1 : 0) ? "fc-slider__thumb--active" : ""}`,
|
|
542
655
|
style: thumbStyle(endPercent),
|
|
543
656
|
onMouseDown: handleMouseDown(range ? 1 : 0),
|
|
657
|
+
onTouchStart: handleTouchStart(range ? 1 : 0),
|
|
544
658
|
children: tooltip && /* @__PURE__ */ jsx7("span", { className: "fc-slider__tooltip", children: endVal })
|
|
545
659
|
}
|
|
546
660
|
),
|
|
@@ -563,6 +677,23 @@ function Slider({
|
|
|
563
677
|
|
|
564
678
|
// src/components/Select/Select.tsx
|
|
565
679
|
import * as React6 from "react";
|
|
680
|
+
|
|
681
|
+
// src/hooks/useClickOutside.ts
|
|
682
|
+
import { useEffect as useEffect4 } from "react";
|
|
683
|
+
function useClickOutside(ref, handler, enabled = true) {
|
|
684
|
+
useEffect4(() => {
|
|
685
|
+
if (!enabled) return;
|
|
686
|
+
const listener = (e) => {
|
|
687
|
+
if (ref.current && !ref.current.contains(e.target)) {
|
|
688
|
+
handler();
|
|
689
|
+
}
|
|
690
|
+
};
|
|
691
|
+
document.addEventListener("pointerdown", listener);
|
|
692
|
+
return () => document.removeEventListener("pointerdown", listener);
|
|
693
|
+
}, [ref, handler, enabled]);
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
// src/components/Select/Select.tsx
|
|
566
697
|
import { jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
567
698
|
function Select({
|
|
568
699
|
options,
|
|
@@ -573,11 +704,32 @@ function Select({
|
|
|
573
704
|
searchable = false,
|
|
574
705
|
multiple = false,
|
|
575
706
|
disabled = false,
|
|
707
|
+
radius,
|
|
576
708
|
className = "",
|
|
709
|
+
style,
|
|
577
710
|
virtualScroll = false,
|
|
578
711
|
virtualItemHeight = 32,
|
|
579
|
-
maxHeight = 256
|
|
712
|
+
maxHeight = 256,
|
|
713
|
+
triggerBackground,
|
|
714
|
+
triggerBorderColor,
|
|
715
|
+
selectedColor,
|
|
716
|
+
selectedBackground,
|
|
717
|
+
hoverBackground
|
|
580
718
|
}) {
|
|
719
|
+
const colorVars = {
|
|
720
|
+
"--select-trigger-bg": triggerBackground,
|
|
721
|
+
"--select-trigger-border": triggerBorderColor,
|
|
722
|
+
"--select-option-selected-color": selectedColor,
|
|
723
|
+
"--select-option-selected-bg": selectedBackground,
|
|
724
|
+
"--select-option-hover-bg": hoverBackground
|
|
725
|
+
};
|
|
726
|
+
const overrideStyle = {};
|
|
727
|
+
for (const [key, value] of Object.entries(colorVars)) {
|
|
728
|
+
if (value !== void 0) {
|
|
729
|
+
overrideStyle[key] = value;
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
const mergedStyle = { ...overrideStyle, ...style };
|
|
581
733
|
const [isOpen, setIsOpen] = React6.useState(false);
|
|
582
734
|
const [searchValue, setSearchValue] = React6.useState("");
|
|
583
735
|
const [highlightedIndex, setHighlightedIndex] = React6.useState(0);
|
|
@@ -635,30 +787,65 @@ function Select({
|
|
|
635
787
|
const selected = options.find((o) => o.value === currentValue);
|
|
636
788
|
return selected?.label || placeholder;
|
|
637
789
|
};
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
790
|
+
useClickOutside(containerRef, () => setIsOpen(false));
|
|
791
|
+
const handleKeyDown = (e) => {
|
|
792
|
+
if (disabled) return;
|
|
793
|
+
switch (e.key) {
|
|
794
|
+
case "Enter":
|
|
795
|
+
case " ":
|
|
796
|
+
e.preventDefault();
|
|
797
|
+
if (!isOpen) {
|
|
798
|
+
setIsOpen(true);
|
|
799
|
+
break;
|
|
800
|
+
}
|
|
801
|
+
if (flatOptions[highlightedIndex]) handleSelect(flatOptions[highlightedIndex]);
|
|
802
|
+
break;
|
|
803
|
+
case "ArrowDown":
|
|
804
|
+
e.preventDefault();
|
|
805
|
+
if (!isOpen) {
|
|
806
|
+
setIsOpen(true);
|
|
807
|
+
break;
|
|
808
|
+
}
|
|
809
|
+
setHighlightedIndex((i) => Math.min(i + 1, flatOptions.length - 1));
|
|
810
|
+
break;
|
|
811
|
+
case "ArrowUp":
|
|
812
|
+
e.preventDefault();
|
|
813
|
+
setHighlightedIndex((i) => Math.max(i - 1, 0));
|
|
814
|
+
break;
|
|
815
|
+
case "Escape":
|
|
816
|
+
e.preventDefault();
|
|
641
817
|
setIsOpen(false);
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
818
|
+
break;
|
|
819
|
+
case "Home":
|
|
820
|
+
e.preventDefault();
|
|
821
|
+
setHighlightedIndex(0);
|
|
822
|
+
break;
|
|
823
|
+
case "End":
|
|
824
|
+
e.preventDefault();
|
|
825
|
+
setHighlightedIndex(flatOptions.length - 1);
|
|
826
|
+
break;
|
|
827
|
+
}
|
|
828
|
+
};
|
|
647
829
|
const classNames = [
|
|
648
830
|
"fc-select",
|
|
649
831
|
isOpen && "fc-select--open",
|
|
650
832
|
multiple && "fc-select--multiple",
|
|
651
833
|
disabled && "fc-select--disabled",
|
|
834
|
+
radius && `fc-select--radius-${radius}`,
|
|
652
835
|
className
|
|
653
836
|
].filter(Boolean).join(" ");
|
|
654
|
-
return /* @__PURE__ */ jsxs6("div", { ref: containerRef, className: classNames, children: [
|
|
837
|
+
return /* @__PURE__ */ jsxs6("div", { ref: containerRef, className: classNames, style: mergedStyle, onKeyDown: handleKeyDown, children: [
|
|
655
838
|
/* @__PURE__ */ jsxs6(
|
|
656
839
|
"div",
|
|
657
840
|
{
|
|
658
841
|
className: "fc-select__trigger",
|
|
659
842
|
onClick: () => !disabled && setIsOpen(!isOpen),
|
|
843
|
+
tabIndex: disabled ? -1 : 0,
|
|
844
|
+
role: "combobox",
|
|
845
|
+
"aria-expanded": isOpen,
|
|
846
|
+
"aria-haspopup": "listbox",
|
|
660
847
|
children: [
|
|
661
|
-
/* @__PURE__ */ jsx8("span", { className: `fc-select__value ${!currentValue && "fc-select__value--placeholder"}`, children: displayLabel() }),
|
|
848
|
+
/* @__PURE__ */ jsx8("span", { className: `fc-select__value ${(currentValue === void 0 || currentValue === null || multiple && !currentValue.length) && "fc-select__value--placeholder"}`, children: displayLabel() }),
|
|
662
849
|
/* @__PURE__ */ jsx8("span", { className: "fc-select__arrow", children: "\u25BC" })
|
|
663
850
|
]
|
|
664
851
|
}
|
|
@@ -725,7 +912,8 @@ function Select({
|
|
|
725
912
|
}
|
|
726
913
|
function highlightText(text, highlight) {
|
|
727
914
|
if (!highlight) return text;
|
|
728
|
-
const
|
|
915
|
+
const escaped = highlight.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
916
|
+
const parts = text.split(new RegExp(`(${escaped})`, "gi"));
|
|
729
917
|
return parts.map(
|
|
730
918
|
(part, i) => part.toLowerCase() === highlight.toLowerCase() ? /* @__PURE__ */ jsx8("mark", { className: "fc-select__highlight", children: part }, i) : part
|
|
731
919
|
);
|
|
@@ -735,13 +923,13 @@ function Select({
|
|
|
735
923
|
// src/components/Tree/Tree.tsx
|
|
736
924
|
import {
|
|
737
925
|
createContext as createContext2,
|
|
738
|
-
memo,
|
|
926
|
+
memo as memo2,
|
|
739
927
|
useCallback as useCallback4,
|
|
740
928
|
useContext as useContext2,
|
|
741
|
-
useEffect as
|
|
929
|
+
useEffect as useEffect5,
|
|
742
930
|
useMemo as useMemo2,
|
|
743
931
|
useRef as useRef4,
|
|
744
|
-
useState as
|
|
932
|
+
useState as useState8
|
|
745
933
|
} from "react";
|
|
746
934
|
import {
|
|
747
935
|
DndContext,
|
|
@@ -753,11 +941,11 @@ import {
|
|
|
753
941
|
} from "@dnd-kit/core";
|
|
754
942
|
|
|
755
943
|
// src/components/Tree/DeleteDialog.tsx
|
|
756
|
-
import { useState as
|
|
944
|
+
import { useState as useState7 } from "react";
|
|
757
945
|
import { Fragment, jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
758
946
|
function DeleteDialog({ node, onClose, onDelete }) {
|
|
759
|
-
const [phase, setPhase] =
|
|
760
|
-
const [loading, setLoading] =
|
|
947
|
+
const [phase, setPhase] = useState7("choose");
|
|
948
|
+
const [loading, setLoading] = useState7(false);
|
|
761
949
|
if (!node) return null;
|
|
762
950
|
const reset = () => {
|
|
763
951
|
setPhase("choose");
|
|
@@ -919,11 +1107,11 @@ import { Fragment as Fragment2, jsx as jsx10, jsxs as jsxs8 } from "react/jsx-ru
|
|
|
919
1107
|
var TreeActionsCtx = createContext2(null);
|
|
920
1108
|
var TreeStateCtx = createContext2(null);
|
|
921
1109
|
var DndStateCtx = createContext2(null);
|
|
922
|
-
var CollapsePanel =
|
|
1110
|
+
var CollapsePanel = memo2(function CollapsePanel2({ open, children }) {
|
|
923
1111
|
const innerRef = useRef4(null);
|
|
924
|
-
const [height, setHeight] =
|
|
925
|
-
const [ready, setReady] =
|
|
926
|
-
|
|
1112
|
+
const [height, setHeight] = useState8(0);
|
|
1113
|
+
const [ready, setReady] = useState8(false);
|
|
1114
|
+
useEffect5(() => {
|
|
927
1115
|
const el = innerRef.current;
|
|
928
1116
|
if (!el) return;
|
|
929
1117
|
const ro = new ResizeObserver(() => setHeight(el.offsetHeight));
|
|
@@ -938,7 +1126,7 @@ var CollapsePanel = memo(function CollapsePanel2({ open, children }) {
|
|
|
938
1126
|
transition: ready ? "height 0.12s ease-out" : "none"
|
|
939
1127
|
}, children: /* @__PURE__ */ jsx10("div", { ref: innerRef, children }) });
|
|
940
1128
|
});
|
|
941
|
-
var DndSlot =
|
|
1129
|
+
var DndSlot = memo2(function DndSlot2({
|
|
942
1130
|
nodeKey,
|
|
943
1131
|
disabled,
|
|
944
1132
|
children
|
|
@@ -959,15 +1147,15 @@ var DndSlot = memo(function DndSlot2({
|
|
|
959
1147
|
);
|
|
960
1148
|
return /* @__PURE__ */ jsx10(Fragment2, { children: children({ setRef, handleProps, isDragging, isDragSource, dropPosition }) });
|
|
961
1149
|
});
|
|
962
|
-
var TreeNodeItem =
|
|
1150
|
+
var TreeNodeItem = memo2(function TreeNodeItem2({ node, level, hidden = false }) {
|
|
963
1151
|
const actions = useContext2(TreeActionsCtx);
|
|
964
1152
|
const state = useContext2(TreeStateCtx);
|
|
965
1153
|
const isExpanded = state.expandedKeys.has(node.key);
|
|
966
1154
|
const isEditing = state.editingKey === node.key;
|
|
967
1155
|
const hasChildren = node.children.length > 0;
|
|
968
1156
|
const indent = level * 20 + 12;
|
|
969
|
-
const [localEdit, setLocalEdit] =
|
|
970
|
-
|
|
1157
|
+
const [localEdit, setLocalEdit] = useState8("");
|
|
1158
|
+
useEffect5(() => {
|
|
971
1159
|
if (isEditing) setLocalEdit(node.title);
|
|
972
1160
|
}, [isEditing, node.title]);
|
|
973
1161
|
const handleEditKeyDown = useCallback4((e) => {
|
|
@@ -1093,18 +1281,18 @@ function Tree({
|
|
|
1093
1281
|
scrollHeight = "400px",
|
|
1094
1282
|
className = ""
|
|
1095
1283
|
}) {
|
|
1096
|
-
const [expandedKeys, setExpandedKeys] =
|
|
1097
|
-
const [editingKey, setEditingKey] =
|
|
1098
|
-
const [deleteTarget, setDeleteTarget] =
|
|
1099
|
-
const [searchValue, setSearchValue] =
|
|
1100
|
-
const [dndState, setDndState] =
|
|
1284
|
+
const [expandedKeys, setExpandedKeys] = useState8(/* @__PURE__ */ new Set());
|
|
1285
|
+
const [editingKey, setEditingKey] = useState8(null);
|
|
1286
|
+
const [deleteTarget, setDeleteTarget] = useState8(null);
|
|
1287
|
+
const [searchValue, setSearchValue] = useState8("");
|
|
1288
|
+
const [dndState, setDndState] = useState8({
|
|
1101
1289
|
dropTargetKey: null,
|
|
1102
1290
|
dropPosition: null,
|
|
1103
1291
|
dragKey: null
|
|
1104
1292
|
});
|
|
1105
1293
|
const dropRef = useRef4({ key: null, pos: null });
|
|
1106
1294
|
const pointerYRef = useRef4(0);
|
|
1107
|
-
|
|
1295
|
+
useEffect5(() => {
|
|
1108
1296
|
const handler = (e) => {
|
|
1109
1297
|
pointerYRef.current = e.clientY;
|
|
1110
1298
|
};
|
|
@@ -1263,10 +1451,10 @@ function Tree({
|
|
|
1263
1451
|
}
|
|
1264
1452
|
|
|
1265
1453
|
// src/components/Tree/OrphanDialog.tsx
|
|
1266
|
-
import { useState as
|
|
1454
|
+
import { useState as useState9 } from "react";
|
|
1267
1455
|
import { jsx as jsx11, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
1268
1456
|
function OrphanDialog({ orphans, onResolve, onClose }) {
|
|
1269
|
-
const [resolutions, setResolutions] =
|
|
1457
|
+
const [resolutions, setResolutions] = useState9(
|
|
1270
1458
|
() => Object.fromEntries(orphans.map((o) => [o.key, "lift"]))
|
|
1271
1459
|
);
|
|
1272
1460
|
if (orphans.length === 0) return null;
|
|
@@ -1330,50 +1518,16 @@ function OrphanDialog({ orphans, onResolve, onClose }) {
|
|
|
1330
1518
|
}
|
|
1331
1519
|
|
|
1332
1520
|
// src/components/Avatar/Avatar.tsx
|
|
1333
|
-
import { useState as
|
|
1521
|
+
import { useState as useState10, useCallback as useCallback5, forwardRef as forwardRef2, useEffect as useEffect6 } from "react";
|
|
1334
1522
|
import { jsx as jsx12 } from "react/jsx-runtime";
|
|
1335
|
-
var SIZE_MAP = {
|
|
1336
|
-
xs: 20,
|
|
1337
|
-
sm: 28,
|
|
1338
|
-
md: 40,
|
|
1339
|
-
lg: 56,
|
|
1340
|
-
xl: 72
|
|
1341
|
-
};
|
|
1342
1523
|
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";
|
|
1343
|
-
var
|
|
1344
|
-
const
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
`ui-avatar-${size}`,
|
|
1348
|
-
`ui-avatar-${shape}`,
|
|
1349
|
-
bordered && "ui-avatar-bordered",
|
|
1350
|
-
onClick && "ui-avatar-clickable",
|
|
1351
|
-
loadState === "loading" && "ui-avatar-loading",
|
|
1352
|
-
!src && !children && "ui-avatar-empty",
|
|
1353
|
-
colorVariant && `ui-avatar--${colorVariant}`,
|
|
1354
|
-
className
|
|
1355
|
-
];
|
|
1356
|
-
return classNames.filter(Boolean).join(" ");
|
|
1357
|
-
}, [size, shape, bordered, onClick, loadState, src, children, colorVariant, className]);
|
|
1358
|
-
const style = useMemo3(() => ({
|
|
1359
|
-
width: SIZE_MAP[size],
|
|
1360
|
-
height: SIZE_MAP[size],
|
|
1361
|
-
fontSize: `${SIZE_MAP[size] * 0.4}px`,
|
|
1362
|
-
...color && { backgroundColor: color },
|
|
1363
|
-
...customStyle
|
|
1364
|
-
}), [size, color, customStyle]);
|
|
1365
|
-
return { classes, style };
|
|
1366
|
-
};
|
|
1367
|
-
var useImageLoader = (src, fallbackSrc, onImageLoad, onImageError, onStateChange) => {
|
|
1368
|
-
const [loadState, setLoadState] = useState11("idle");
|
|
1369
|
-
const [currentSrc, setCurrentSrc] = useState11(src);
|
|
1370
|
-
useEffect5(() => {
|
|
1524
|
+
var useImageLoader = (src, fallbackSrc, onImageLoad, onImageError) => {
|
|
1525
|
+
const [loadState, setLoadState] = useState10("idle");
|
|
1526
|
+
const [currentSrc, setCurrentSrc] = useState10(src);
|
|
1527
|
+
useEffect6(() => {
|
|
1371
1528
|
setCurrentSrc(src);
|
|
1372
1529
|
setLoadState(src ? "loading" : "idle");
|
|
1373
1530
|
}, [src]);
|
|
1374
|
-
useEffect5(() => {
|
|
1375
|
-
onStateChange?.(loadState);
|
|
1376
|
-
}, [loadState, onStateChange]);
|
|
1377
1531
|
const handleLoad = useCallback5(() => {
|
|
1378
1532
|
setLoadState("loaded");
|
|
1379
1533
|
onImageLoad?.();
|
|
@@ -1394,18 +1548,8 @@ var useImageLoader = (src, fallbackSrc, onImageLoad, onImageError, onStateChange
|
|
|
1394
1548
|
handleError
|
|
1395
1549
|
};
|
|
1396
1550
|
};
|
|
1397
|
-
var
|
|
1398
|
-
const handleKeyDown = useCallback5((e) => {
|
|
1399
|
-
if (onClick && (e.key === "Enter" || e.key === " ")) {
|
|
1400
|
-
e.preventDefault();
|
|
1401
|
-
onClick(e);
|
|
1402
|
-
}
|
|
1403
|
-
}, [onClick]);
|
|
1404
|
-
return { handleKeyDown };
|
|
1405
|
-
};
|
|
1406
|
-
var renderContent = (currentSrc, fallbackSrc, loadState, alt, lazyLoad2, children, handleLoad, handleError) => {
|
|
1551
|
+
var renderContent = (currentSrc, fallbackSrc, loadState, alt, lazyLoad2, handleLoad, handleError) => {
|
|
1407
1552
|
const showImage = currentSrc && loadState !== "error";
|
|
1408
|
-
const showFallback = !showImage && children;
|
|
1409
1553
|
const imageSrc = loadState === "error" && currentSrc !== fallbackSrc ? fallbackSrc : currentSrc;
|
|
1410
1554
|
if (showImage && imageSrc) {
|
|
1411
1555
|
return /* @__PURE__ */ jsx12(
|
|
@@ -1421,37 +1565,20 @@ var renderContent = (currentSrc, fallbackSrc, loadState, alt, lazyLoad2, childre
|
|
|
1421
1565
|
}
|
|
1422
1566
|
);
|
|
1423
1567
|
}
|
|
1424
|
-
|
|
1425
|
-
return /* @__PURE__ */ jsx12("span", { className: "ui-avatar-text", children });
|
|
1426
|
-
}
|
|
1427
|
-
return /* @__PURE__ */ jsx12("span", { className: "ui-avatar-placeholder", children: /* @__PURE__ */ jsx12("svg", { viewBox: "0 0 24 24", width: "40%", height: "40%", fill: "currentColor", opacity: 0.4, children: /* @__PURE__ */ jsx12("path", { d: DEFAULT_ICON }) }) });
|
|
1428
|
-
};
|
|
1429
|
-
var useAriaAttributes = (onClick, loadState, alt) => {
|
|
1430
|
-
return {
|
|
1431
|
-
role: onClick ? "button" : "img",
|
|
1432
|
-
tabIndex: onClick ? 0 : void 0,
|
|
1433
|
-
"aria-label": alt,
|
|
1434
|
-
"aria-busy": loadState === "loading",
|
|
1435
|
-
"data-load-state": loadState
|
|
1436
|
-
};
|
|
1568
|
+
return /* @__PURE__ */ jsx12("span", { className: "ui-avatar-placeholder", children: /* @__PURE__ */ jsx12("svg", { viewBox: "0 0 24 24", width: "40%", height: "40%", fill: "currentColor", children: /* @__PURE__ */ jsx12("path", { d: DEFAULT_ICON }) }) });
|
|
1437
1569
|
};
|
|
1438
1570
|
var Avatar = forwardRef2(
|
|
1439
1571
|
({
|
|
1440
|
-
children,
|
|
1441
1572
|
src,
|
|
1442
1573
|
fallbackSrc,
|
|
1443
|
-
|
|
1444
|
-
colorVariant,
|
|
1445
|
-
size = "md",
|
|
1574
|
+
size = 40,
|
|
1446
1575
|
shape = "circle",
|
|
1447
1576
|
alt = "\u7528\u6237\u5934\u50CF",
|
|
1448
1577
|
lazyLoad: lazyLoad2 = false,
|
|
1449
1578
|
onImageLoad,
|
|
1450
1579
|
onImageError,
|
|
1451
|
-
onStateChange,
|
|
1452
1580
|
bordered = false,
|
|
1453
1581
|
className = "",
|
|
1454
|
-
onClick,
|
|
1455
1582
|
style: customStyle,
|
|
1456
1583
|
...restProps
|
|
1457
1584
|
}, ref) => {
|
|
@@ -1459,31 +1586,28 @@ var Avatar = forwardRef2(
|
|
|
1459
1586
|
src,
|
|
1460
1587
|
fallbackSrc,
|
|
1461
1588
|
onImageLoad,
|
|
1462
|
-
onImageError
|
|
1463
|
-
onStateChange
|
|
1464
|
-
);
|
|
1465
|
-
const { classes, style } = useAvatarStyles(
|
|
1466
|
-
size,
|
|
1467
|
-
shape,
|
|
1468
|
-
bordered,
|
|
1469
|
-
onClick,
|
|
1470
|
-
loadState,
|
|
1471
|
-
src,
|
|
1472
|
-
children,
|
|
1473
|
-
colorVariant,
|
|
1474
|
-
className,
|
|
1475
|
-
color,
|
|
1476
|
-
customStyle
|
|
1589
|
+
onImageError
|
|
1477
1590
|
);
|
|
1478
|
-
const
|
|
1479
|
-
const
|
|
1591
|
+
const sizeValue = `${size}px`;
|
|
1592
|
+
const classes = [
|
|
1593
|
+
"ui-avatar",
|
|
1594
|
+
`ui-avatar-${shape}`,
|
|
1595
|
+
bordered && "ui-avatar-bordered",
|
|
1596
|
+
loadState === "loading" && "ui-avatar-loading",
|
|
1597
|
+
className
|
|
1598
|
+
].filter(Boolean).join(" ");
|
|
1599
|
+
const style = {
|
|
1600
|
+
width: sizeValue,
|
|
1601
|
+
height: sizeValue,
|
|
1602
|
+
fontSize: `calc(${sizeValue} * 0.4)`,
|
|
1603
|
+
...customStyle
|
|
1604
|
+
};
|
|
1480
1605
|
const content = renderContent(
|
|
1481
1606
|
currentSrc,
|
|
1482
1607
|
fallbackSrc,
|
|
1483
1608
|
loadState,
|
|
1484
1609
|
alt,
|
|
1485
1610
|
lazyLoad2,
|
|
1486
|
-
children,
|
|
1487
1611
|
handleLoad,
|
|
1488
1612
|
handleError
|
|
1489
1613
|
);
|
|
@@ -1493,9 +1617,9 @@ var Avatar = forwardRef2(
|
|
|
1493
1617
|
ref,
|
|
1494
1618
|
className: classes,
|
|
1495
1619
|
style,
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1620
|
+
role: "img",
|
|
1621
|
+
"aria-label": alt,
|
|
1622
|
+
"data-load-state": loadState,
|
|
1499
1623
|
...restProps,
|
|
1500
1624
|
children: content
|
|
1501
1625
|
}
|
|
@@ -1505,7 +1629,7 @@ var Avatar = forwardRef2(
|
|
|
1505
1629
|
Avatar.displayName = "Avatar";
|
|
1506
1630
|
|
|
1507
1631
|
// src/components/ListGroup/ListGroup.tsx
|
|
1508
|
-
import { forwardRef as forwardRef3, useMemo as
|
|
1632
|
+
import { forwardRef as forwardRef3, useMemo as useMemo3, useCallback as useCallback6 } from "react";
|
|
1509
1633
|
import { jsx as jsx13 } from "react/jsx-runtime";
|
|
1510
1634
|
var combineClassNames = (...classNames) => {
|
|
1511
1635
|
return classNames.filter(Boolean).join(" ");
|
|
@@ -1523,7 +1647,7 @@ var ListGroupItem = forwardRef3(
|
|
|
1523
1647
|
if (disabled) return;
|
|
1524
1648
|
onClick?.(e);
|
|
1525
1649
|
}, [disabled, onClick]);
|
|
1526
|
-
const classNames =
|
|
1650
|
+
const classNames = useMemo3(() => {
|
|
1527
1651
|
return combineClassNames(
|
|
1528
1652
|
"fc-list-group-item",
|
|
1529
1653
|
active && "fc-list-group-item--active",
|
|
@@ -1556,7 +1680,7 @@ var ListGroup = forwardRef3(
|
|
|
1556
1680
|
children,
|
|
1557
1681
|
...props
|
|
1558
1682
|
}, ref) => {
|
|
1559
|
-
const classNames =
|
|
1683
|
+
const classNames = useMemo3(() => {
|
|
1560
1684
|
return combineClassNames(
|
|
1561
1685
|
"fc-list-group",
|
|
1562
1686
|
bordered && "fc-list-group--bordered",
|
|
@@ -1579,8 +1703,10 @@ var ListGroup = forwardRef3(
|
|
|
1579
1703
|
ListGroup.displayName = "ListGroup";
|
|
1580
1704
|
|
|
1581
1705
|
// src/components/VirtualList/VirtualList.tsx
|
|
1582
|
-
import { useState as
|
|
1706
|
+
import { useState as useState11, useRef as useRef5, useCallback as useCallback7, useMemo as useMemo4, memo as memo3 } from "react";
|
|
1583
1707
|
import { jsx as jsx14 } from "react/jsx-runtime";
|
|
1708
|
+
var VirtualItem = memo3(({ children, height }) => /* @__PURE__ */ jsx14("div", { className: "fc-virtual-list__item", style: { height: `${height}px` }, children }));
|
|
1709
|
+
VirtualItem.displayName = "VirtualItem";
|
|
1584
1710
|
function VirtualList({
|
|
1585
1711
|
data,
|
|
1586
1712
|
height,
|
|
@@ -1593,7 +1719,7 @@ function VirtualList({
|
|
|
1593
1719
|
style
|
|
1594
1720
|
}) {
|
|
1595
1721
|
const containerRef = useRef5(null);
|
|
1596
|
-
const [scrollTop, setScrollTop] =
|
|
1722
|
+
const [scrollTop, setScrollTop] = useState11(0);
|
|
1597
1723
|
const totalHeight = data.length * itemHeight;
|
|
1598
1724
|
const handleScroll = useCallback7((e) => {
|
|
1599
1725
|
const newScrollTop = e.currentTarget.scrollTop;
|
|
@@ -1606,7 +1732,7 @@ function VirtualList({
|
|
|
1606
1732
|
}
|
|
1607
1733
|
}
|
|
1608
1734
|
}, [onScrollEnd]);
|
|
1609
|
-
const visibleRange =
|
|
1735
|
+
const visibleRange = useMemo4(() => {
|
|
1610
1736
|
const startIndex = Math.max(0, Math.floor(scrollTop / itemHeight) - overscan);
|
|
1611
1737
|
const endIndex = Math.min(
|
|
1612
1738
|
data.length,
|
|
@@ -1614,15 +1740,10 @@ function VirtualList({
|
|
|
1614
1740
|
);
|
|
1615
1741
|
return { startIndex, endIndex };
|
|
1616
1742
|
}, [scrollTop, height, itemHeight, data.length, overscan]);
|
|
1617
|
-
const visibleData =
|
|
1743
|
+
const visibleData = useMemo4(() => {
|
|
1618
1744
|
return data.slice(visibleRange.startIndex, visibleRange.endIndex);
|
|
1619
1745
|
}, [data, visibleRange]);
|
|
1620
1746
|
const offsetY = visibleRange.startIndex * itemHeight;
|
|
1621
|
-
useEffect6(() => {
|
|
1622
|
-
if (!showScrollbar && containerRef.current) {
|
|
1623
|
-
containerRef.current.style.scrollbarWidth = "none";
|
|
1624
|
-
}
|
|
1625
|
-
}, [showScrollbar]);
|
|
1626
1747
|
const classNames = [
|
|
1627
1748
|
"fc-virtual-list",
|
|
1628
1749
|
!showScrollbar && "fc-virtual-list--hide-scrollbar",
|
|
@@ -1647,15 +1768,7 @@ function VirtualList({
|
|
|
1647
1768
|
style: { transform: `translateY(${offsetY}px)` },
|
|
1648
1769
|
children: visibleData.map((item, idx) => {
|
|
1649
1770
|
const actualIndex = visibleRange.startIndex + idx;
|
|
1650
|
-
return /* @__PURE__ */ jsx14(
|
|
1651
|
-
"div",
|
|
1652
|
-
{
|
|
1653
|
-
className: "fc-virtual-list__item",
|
|
1654
|
-
style: { height: `${itemHeight}px` },
|
|
1655
|
-
children: renderItem(item, actualIndex)
|
|
1656
|
-
},
|
|
1657
|
-
actualIndex
|
|
1658
|
-
);
|
|
1771
|
+
return /* @__PURE__ */ jsx14(VirtualItem, { height: itemHeight, children: renderItem(item, actualIndex) }, actualIndex);
|
|
1659
1772
|
})
|
|
1660
1773
|
}
|
|
1661
1774
|
)
|
|
@@ -1666,7 +1779,7 @@ function VirtualList({
|
|
|
1666
1779
|
}
|
|
1667
1780
|
|
|
1668
1781
|
// src/components/Alert/AlertContext.tsx
|
|
1669
|
-
import { createContext as createContext3, useContext as useContext3, useState as
|
|
1782
|
+
import { createContext as createContext3, useContext as useContext3, useEffect as useEffect7, useRef as useRef6, useState as useState12 } from "react";
|
|
1670
1783
|
import { jsx as jsx15, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
1671
1784
|
var AlertContext = createContext3(null);
|
|
1672
1785
|
var ICONS = {
|
|
@@ -1742,55 +1855,84 @@ var ICONS = {
|
|
|
1742
1855
|
}
|
|
1743
1856
|
)
|
|
1744
1857
|
};
|
|
1745
|
-
function AlertProvider({ children }) {
|
|
1746
|
-
const [
|
|
1858
|
+
function AlertProvider({ children, background, borderColor }) {
|
|
1859
|
+
const [alert, setAlert] = useState12({
|
|
1747
1860
|
msg: "",
|
|
1748
1861
|
type: "info",
|
|
1862
|
+
mode: "alert",
|
|
1749
1863
|
visible: false,
|
|
1750
1864
|
choice: () => {
|
|
1751
1865
|
}
|
|
1752
1866
|
});
|
|
1753
|
-
const
|
|
1754
|
-
|
|
1867
|
+
const mountedRef = useRef6(true);
|
|
1868
|
+
useEffect7(() => {
|
|
1869
|
+
mountedRef.current = true;
|
|
1870
|
+
return () => {
|
|
1871
|
+
mountedRef.current = false;
|
|
1872
|
+
};
|
|
1873
|
+
}, []);
|
|
1874
|
+
const showAlert = (msg, type, mode = "alert", duration) => new Promise((resolve, reject) => {
|
|
1875
|
+
if (!mountedRef.current) {
|
|
1876
|
+
reject(new Error("AlertProvider unmounted"));
|
|
1877
|
+
return;
|
|
1878
|
+
}
|
|
1879
|
+
setAlert({
|
|
1755
1880
|
msg,
|
|
1756
1881
|
type,
|
|
1882
|
+
mode,
|
|
1757
1883
|
visible: true,
|
|
1758
|
-
|
|
1884
|
+
duration,
|
|
1759
1885
|
choice: (res) => {
|
|
1760
|
-
|
|
1886
|
+
if (!mountedRef.current) return;
|
|
1887
|
+
setAlert((p) => ({ ...p, visible: false }));
|
|
1761
1888
|
resolve(res);
|
|
1762
1889
|
}
|
|
1763
1890
|
});
|
|
1764
1891
|
});
|
|
1892
|
+
useEffect7(() => {
|
|
1893
|
+
if (!alert.visible || !alert.duration) return;
|
|
1894
|
+
const timer = setTimeout(() => alert.choice("auto"), alert.duration);
|
|
1895
|
+
return () => clearTimeout(timer);
|
|
1896
|
+
}, [alert.visible, alert.duration]);
|
|
1897
|
+
const overrideStyle = {};
|
|
1898
|
+
if (background !== void 0) overrideStyle["--alert-bg"] = background;
|
|
1899
|
+
if (borderColor !== void 0) overrideStyle["--alert-border"] = borderColor;
|
|
1765
1900
|
return /* @__PURE__ */ jsxs10(AlertContext.Provider, { value: { showAlert }, children: [
|
|
1766
1901
|
children,
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1902
|
+
alert.visible && /* @__PURE__ */ jsx15("div", { className: "fc-alert-overlay", children: /* @__PURE__ */ jsxs10(
|
|
1903
|
+
"div",
|
|
1904
|
+
{
|
|
1905
|
+
className: `fc-alert fc-alert--${alert.type} fc-alert--${alert.mode}`,
|
|
1906
|
+
style: overrideStyle,
|
|
1907
|
+
children: [
|
|
1908
|
+
/* @__PURE__ */ jsxs10("div", { className: "fc-alert__header", children: [
|
|
1909
|
+
ICONS[alert.type],
|
|
1910
|
+
/* @__PURE__ */ jsx15("span", { className: "fc-alert__title", children: "\u63D0\u793A" })
|
|
1911
|
+
] }),
|
|
1912
|
+
/* @__PURE__ */ jsx15(RollingBox, { className: "fc-alert__msg", children: alert.msg }),
|
|
1913
|
+
alert.mode !== "toast" && /* @__PURE__ */ jsxs10("div", { className: "fc-alert__footer", children: [
|
|
1914
|
+
alert.mode === "confirm" && /* @__PURE__ */ jsx15(
|
|
1915
|
+
Button,
|
|
1916
|
+
{
|
|
1917
|
+
variant: "secondary",
|
|
1918
|
+
size: "sm",
|
|
1919
|
+
onClick: () => alert.choice("no"),
|
|
1920
|
+
children: "\u53D6\u6D88"
|
|
1921
|
+
}
|
|
1922
|
+
),
|
|
1923
|
+
/* @__PURE__ */ jsx15(
|
|
1924
|
+
Button,
|
|
1925
|
+
{
|
|
1926
|
+
variant: "primary",
|
|
1927
|
+
size: "sm",
|
|
1928
|
+
onClick: () => alert.choice("yes"),
|
|
1929
|
+
children: "\u786E\u5B9A"
|
|
1930
|
+
}
|
|
1931
|
+
)
|
|
1932
|
+
] })
|
|
1933
|
+
]
|
|
1934
|
+
}
|
|
1935
|
+
) })
|
|
1794
1936
|
] });
|
|
1795
1937
|
}
|
|
1796
1938
|
var useAlert = () => useContext3(AlertContext);
|
|
@@ -1882,347 +2024,1217 @@ var Card = ({
|
|
|
1882
2024
|
] });
|
|
1883
2025
|
};
|
|
1884
2026
|
|
|
1885
|
-
// src/components/
|
|
1886
|
-
import {
|
|
2027
|
+
// src/components/Bar/TabBar.tsx
|
|
2028
|
+
import { useRef as useRef8, useCallback as useCallback9, memo as memo4, useEffect as useEffect9 } from "react";
|
|
2029
|
+
|
|
2030
|
+
// src/components/Bar/useAdaptiveTabLayout.ts
|
|
2031
|
+
import { useState as useState13, useRef as useRef7, useCallback as useCallback8, useEffect as useEffect8 } from "react";
|
|
2032
|
+
function useAdaptiveTabLayout(navRef, options) {
|
|
2033
|
+
const { itemsLength, addable, minWidthRatio, maxTabWidthRatio } = options;
|
|
2034
|
+
const itemsLengthRef = useRef7(itemsLength);
|
|
2035
|
+
itemsLengthRef.current = itemsLength;
|
|
2036
|
+
const addableRef = useRef7(addable);
|
|
2037
|
+
addableRef.current = addable;
|
|
2038
|
+
const minWidthRatioRef = useRef7(minWidthRatio);
|
|
2039
|
+
minWidthRatioRef.current = minWidthRatio;
|
|
2040
|
+
const maxTabWidthRatioRef = useRef7(maxTabWidthRatio);
|
|
2041
|
+
maxTabWidthRatioRef.current = maxTabWidthRatio;
|
|
2042
|
+
const [layout, setLayout] = useState13({ scrollMode: false, tabWidth: void 0 });
|
|
2043
|
+
const calculateTimeoutRef = useRef7(null);
|
|
2044
|
+
const lastLayoutRef = useRef7({ scrollMode: false, tabWidth: void 0 });
|
|
2045
|
+
const addBtnWidthRef = useRef7(0);
|
|
2046
|
+
const lastAddableRef = useRef7(void 0);
|
|
2047
|
+
const calculate = useCallback8(() => {
|
|
2048
|
+
const navOuter = navRef.current?.parentElement;
|
|
2049
|
+
const nav = navRef.current;
|
|
2050
|
+
const currentItemsLength = itemsLengthRef.current;
|
|
2051
|
+
const currentAddable = addableRef.current;
|
|
2052
|
+
const currentMinWidthRatio = minWidthRatioRef.current;
|
|
2053
|
+
const currentMaxTabWidthRatio = maxTabWidthRatioRef.current;
|
|
2054
|
+
if (!nav || !navOuter || currentItemsLength === 0) return;
|
|
2055
|
+
if (currentAddable !== lastAddableRef.current) {
|
|
2056
|
+
lastAddableRef.current = currentAddable;
|
|
2057
|
+
if (currentAddable) {
|
|
2058
|
+
const addBtnElement = navOuter.querySelector(".fc-tab-bar__add-btn");
|
|
2059
|
+
if (addBtnElement) {
|
|
2060
|
+
const btnStyle = getComputedStyle(addBtnElement);
|
|
2061
|
+
const marginL = parseFloat(btnStyle.marginLeft) || 0;
|
|
2062
|
+
const marginR = parseFloat(btnStyle.marginRight) || 0;
|
|
2063
|
+
addBtnWidthRef.current = addBtnElement.getBoundingClientRect().width + marginL + marginR;
|
|
2064
|
+
}
|
|
2065
|
+
} else {
|
|
2066
|
+
addBtnWidthRef.current = 0;
|
|
2067
|
+
}
|
|
2068
|
+
}
|
|
2069
|
+
const navOuterCS = getComputedStyle(navOuter);
|
|
2070
|
+
const navWrap = nav.querySelector(".fc-tab-bar__nav-wrap");
|
|
2071
|
+
const navOuterW = navOuter.clientWidth - parseFloat(navOuterCS.paddingLeft) - parseFloat(navOuterCS.paddingRight);
|
|
2072
|
+
const navW = navOuterW - addBtnWidthRef.current;
|
|
2073
|
+
const tabGap = navWrap ? parseFloat(getComputedStyle(navWrap).gap) : 0;
|
|
2074
|
+
const totalGapWidth = (currentItemsLength - 1) * tabGap;
|
|
2075
|
+
const idealW = (navW - totalGapWidth) / currentItemsLength;
|
|
2076
|
+
const threshold = window.innerWidth * currentMinWidthRatio;
|
|
2077
|
+
const maxAllowedWidth = window.innerWidth * currentMaxTabWidthRatio;
|
|
2078
|
+
const minAllowedWidth = 42;
|
|
2079
|
+
const prev = lastLayoutRef.current;
|
|
2080
|
+
const hysteresisFactor = 1.1;
|
|
2081
|
+
let newScrollMode;
|
|
2082
|
+
let newTabWidth;
|
|
2083
|
+
if (idealW < threshold) {
|
|
2084
|
+
newScrollMode = true;
|
|
2085
|
+
newTabWidth = Math.max(threshold, minAllowedWidth);
|
|
2086
|
+
} else if (prev.scrollMode && idealW < threshold * hysteresisFactor) {
|
|
2087
|
+
newScrollMode = true;
|
|
2088
|
+
newTabWidth = Math.max(threshold, minAllowedWidth);
|
|
2089
|
+
} else {
|
|
2090
|
+
newScrollMode = false;
|
|
2091
|
+
newTabWidth = Math.min(Math.max(idealW, minAllowedWidth), maxAllowedWidth);
|
|
2092
|
+
}
|
|
2093
|
+
if (prev.tabWidth !== newTabWidth || prev.scrollMode !== newScrollMode) {
|
|
2094
|
+
const newLayout = { scrollMode: newScrollMode, tabWidth: newTabWidth };
|
|
2095
|
+
lastLayoutRef.current = newLayout;
|
|
2096
|
+
setLayout(newLayout);
|
|
2097
|
+
}
|
|
2098
|
+
}, [navRef]);
|
|
2099
|
+
const calculateDebounced = useCallback8(() => {
|
|
2100
|
+
if (calculateTimeoutRef.current) clearTimeout(calculateTimeoutRef.current);
|
|
2101
|
+
calculateTimeoutRef.current = setTimeout(calculate, 50);
|
|
2102
|
+
}, [calculate]);
|
|
2103
|
+
const calculateDebouncedRef = useRef7(calculateDebounced);
|
|
2104
|
+
calculateDebouncedRef.current = calculateDebounced;
|
|
2105
|
+
useEffect8(() => {
|
|
2106
|
+
const rafId = requestAnimationFrame(calculate);
|
|
2107
|
+
const handleResize = () => calculateDebouncedRef.current();
|
|
2108
|
+
window.addEventListener("resize", handleResize);
|
|
2109
|
+
return () => {
|
|
2110
|
+
cancelAnimationFrame(rafId);
|
|
2111
|
+
window.removeEventListener("resize", handleResize);
|
|
2112
|
+
if (calculateTimeoutRef.current) clearTimeout(calculateTimeoutRef.current);
|
|
2113
|
+
};
|
|
2114
|
+
}, []);
|
|
2115
|
+
useEffect8(() => {
|
|
2116
|
+
calculate();
|
|
2117
|
+
}, [itemsLength, calculate]);
|
|
2118
|
+
return layout;
|
|
2119
|
+
}
|
|
2120
|
+
|
|
2121
|
+
// src/components/Bar/TabBar.tsx
|
|
1887
2122
|
import { jsx as jsx18, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
1888
|
-
var
|
|
2123
|
+
var TabItemView = memo4(({
|
|
2124
|
+
item,
|
|
2125
|
+
isActive,
|
|
2126
|
+
closable,
|
|
2127
|
+
draggable,
|
|
2128
|
+
tabWidth,
|
|
2129
|
+
tabClassName,
|
|
2130
|
+
activeTabClassName,
|
|
2131
|
+
tabStyle,
|
|
2132
|
+
activeTabStyle,
|
|
2133
|
+
renderCloseIcon,
|
|
2134
|
+
onClick,
|
|
2135
|
+
onClose,
|
|
2136
|
+
onDragStart,
|
|
2137
|
+
onDragOver,
|
|
2138
|
+
onDrop,
|
|
2139
|
+
onDragEnd
|
|
2140
|
+
}) => {
|
|
2141
|
+
const showClose = item.closable !== void 0 ? item.closable : closable;
|
|
2142
|
+
const isCompact = tabWidth !== void 0 && tabWidth < 42;
|
|
2143
|
+
const classes = [
|
|
2144
|
+
"fc-tab-bar__tab",
|
|
2145
|
+
isActive && "fc-tab-bar__tab--active",
|
|
2146
|
+
item.disabled && "fc-tab-bar__tab--disabled",
|
|
2147
|
+
draggable && !item.disabled && "fc-tab-bar__tab--draggable",
|
|
2148
|
+
tabClassName,
|
|
2149
|
+
isActive && activeTabClassName
|
|
2150
|
+
].filter(Boolean).join(" ");
|
|
2151
|
+
const mergedStyle = {
|
|
2152
|
+
...tabWidth !== void 0 ? { width: tabWidth, boxSizing: "border-box", flexShrink: 0 } : void 0,
|
|
2153
|
+
...tabStyle,
|
|
2154
|
+
...isActive ? activeTabStyle : void 0
|
|
2155
|
+
};
|
|
2156
|
+
return /* @__PURE__ */ jsxs13(
|
|
2157
|
+
"div",
|
|
2158
|
+
{
|
|
2159
|
+
className: classes,
|
|
2160
|
+
style: mergedStyle,
|
|
2161
|
+
"data-compact": isCompact || void 0,
|
|
2162
|
+
onClick: () => !item.disabled && onClick(item.key),
|
|
2163
|
+
draggable: draggable && !item.disabled,
|
|
2164
|
+
onDragStart: (e) => onDragStart(e, item.key),
|
|
2165
|
+
onDragOver,
|
|
2166
|
+
onDrop: (e) => onDrop(e, item.key),
|
|
2167
|
+
onDragEnd,
|
|
2168
|
+
role: "tab",
|
|
2169
|
+
"aria-selected": isActive,
|
|
2170
|
+
"aria-disabled": item.disabled,
|
|
2171
|
+
tabIndex: item.disabled ? -1 : 0,
|
|
2172
|
+
children: [
|
|
2173
|
+
/* @__PURE__ */ jsx18("span", { className: "fc-tab-bar__tab-label", children: item.label }),
|
|
2174
|
+
showClose && !item.disabled && /* @__PURE__ */ jsx18(
|
|
2175
|
+
"span",
|
|
2176
|
+
{
|
|
2177
|
+
className: "fc-tab-bar__tab-close",
|
|
2178
|
+
onClick: (e) => onClose(e, item.key),
|
|
2179
|
+
role: "button",
|
|
2180
|
+
"aria-label": `\u5173\u95ED`,
|
|
2181
|
+
children: renderCloseIcon ? renderCloseIcon(item.key) : "\xD7"
|
|
2182
|
+
}
|
|
2183
|
+
)
|
|
2184
|
+
]
|
|
2185
|
+
}
|
|
2186
|
+
);
|
|
2187
|
+
});
|
|
2188
|
+
TabItemView.displayName = "TabItemView";
|
|
2189
|
+
var TabBar = memo4(({
|
|
1889
2190
|
items,
|
|
1890
|
-
activeKey
|
|
1891
|
-
|
|
2191
|
+
activeKey,
|
|
2192
|
+
variant = "attached",
|
|
1892
2193
|
radius = "md",
|
|
2194
|
+
tabRadius,
|
|
1893
2195
|
closable = false,
|
|
1894
2196
|
addable = false,
|
|
2197
|
+
draggable = false,
|
|
2198
|
+
minWidthRatio = 0.07,
|
|
2199
|
+
maxTabWidthRatio = 0.15,
|
|
1895
2200
|
onChange,
|
|
1896
2201
|
onClose,
|
|
1897
2202
|
onAdd,
|
|
2203
|
+
onReorder,
|
|
2204
|
+
tabClassName,
|
|
2205
|
+
activeTabClassName,
|
|
2206
|
+
tabStyle,
|
|
2207
|
+
activeTabStyle,
|
|
2208
|
+
renderCloseIcon,
|
|
2209
|
+
renderAddButton,
|
|
1898
2210
|
className = "",
|
|
1899
|
-
style
|
|
2211
|
+
style,
|
|
2212
|
+
background,
|
|
2213
|
+
tabColor,
|
|
2214
|
+
tabHoverColor,
|
|
2215
|
+
tabHoverBackground,
|
|
2216
|
+
tabActiveColor,
|
|
2217
|
+
tabActiveBackground,
|
|
2218
|
+
activeIndicatorColor
|
|
1900
2219
|
}) => {
|
|
1901
|
-
const
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
}
|
|
1910
|
-
onChange?.(key);
|
|
2220
|
+
const colorVars = {
|
|
2221
|
+
"--tab-bar-bg": background,
|
|
2222
|
+
"--tab-color": tabColor,
|
|
2223
|
+
"--tab-hover-color": tabHoverColor,
|
|
2224
|
+
"--tab-hover-bg": tabHoverBackground,
|
|
2225
|
+
"--tab-active-color": tabActiveColor,
|
|
2226
|
+
"--tab-active-bg": tabActiveBackground,
|
|
2227
|
+
"--tab-active-indicator": activeIndicatorColor
|
|
1911
2228
|
};
|
|
1912
|
-
const
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
const currentIndex = items.findIndex((item) => item.key === key);
|
|
1917
|
-
const nextItem = items[currentIndex + 1] || items[currentIndex - 1];
|
|
1918
|
-
if (nextItem && !nextItem.disabled) {
|
|
1919
|
-
handleTabClick(nextItem.key);
|
|
1920
|
-
}
|
|
2229
|
+
const overrideStyle = {};
|
|
2230
|
+
for (const [key, value] of Object.entries(colorVars)) {
|
|
2231
|
+
if (value !== void 0) {
|
|
2232
|
+
overrideStyle[key] = value;
|
|
1921
2233
|
}
|
|
1922
|
-
}
|
|
1923
|
-
const
|
|
1924
|
-
|
|
1925
|
-
|
|
2234
|
+
}
|
|
2235
|
+
const mergedStyle = { ...overrideStyle, ...style };
|
|
2236
|
+
const dragKeyRef = useRef8(null);
|
|
2237
|
+
const navRef = useRef8(null);
|
|
2238
|
+
const layout = useAdaptiveTabLayout(navRef, {
|
|
2239
|
+
itemsLength: items.length,
|
|
2240
|
+
addable,
|
|
2241
|
+
minWidthRatio,
|
|
2242
|
+
maxTabWidthRatio
|
|
2243
|
+
});
|
|
2244
|
+
const prevItemsLengthRef = useRef8(items.length);
|
|
2245
|
+
useEffect9(() => {
|
|
2246
|
+
const prev = prevItemsLengthRef.current;
|
|
2247
|
+
prevItemsLengthRef.current = items.length;
|
|
2248
|
+
if (items.length <= prev || !layout.scrollMode) return;
|
|
2249
|
+
const roll = navRef.current?.querySelector(".fc-roll");
|
|
2250
|
+
if (!roll) return;
|
|
2251
|
+
requestAnimationFrame(() => {
|
|
2252
|
+
roll.scrollLeft = roll.scrollWidth;
|
|
2253
|
+
});
|
|
2254
|
+
}, [items.length, layout.scrollMode]);
|
|
2255
|
+
const handleClick = useCallback9(
|
|
2256
|
+
(key) => onChange(key),
|
|
2257
|
+
[onChange]
|
|
2258
|
+
);
|
|
2259
|
+
const handleClose = useCallback9(
|
|
2260
|
+
(e, key) => {
|
|
2261
|
+
e.stopPropagation();
|
|
2262
|
+
onClose?.(key);
|
|
2263
|
+
},
|
|
2264
|
+
[onClose]
|
|
2265
|
+
);
|
|
2266
|
+
const handleDragStart = useCallback9(
|
|
2267
|
+
(e, key) => {
|
|
2268
|
+
dragKeyRef.current = key;
|
|
2269
|
+
e.dataTransfer.effectAllowed = "move";
|
|
2270
|
+
const target = e.currentTarget;
|
|
2271
|
+
requestAnimationFrame(() => target.classList.add("fc-tab-bar__tab--dragging"));
|
|
2272
|
+
},
|
|
2273
|
+
[]
|
|
2274
|
+
);
|
|
2275
|
+
const handleDragOver = useCallback9((e) => {
|
|
2276
|
+
e.preventDefault();
|
|
2277
|
+
e.dataTransfer.dropEffect = "move";
|
|
2278
|
+
}, []);
|
|
2279
|
+
const handleDrop = useCallback9(
|
|
2280
|
+
(e, targetKey) => {
|
|
2281
|
+
e.preventDefault();
|
|
2282
|
+
const dragKey = dragKeyRef.current;
|
|
2283
|
+
if (!dragKey || dragKey === targetKey || !onReorder) return;
|
|
2284
|
+
const fromIndex = items.findIndex((i) => i.key === dragKey);
|
|
2285
|
+
const toIndex = items.findIndex((i) => i.key === targetKey);
|
|
2286
|
+
if (fromIndex === -1 || toIndex === -1) return;
|
|
2287
|
+
const reordered = [...items];
|
|
2288
|
+
const [moved] = reordered.splice(fromIndex, 1);
|
|
2289
|
+
reordered.splice(toIndex, 0, moved);
|
|
2290
|
+
onReorder(reordered);
|
|
2291
|
+
},
|
|
2292
|
+
[items, onReorder]
|
|
2293
|
+
);
|
|
2294
|
+
const handleDragEnd = useCallback9((e) => {
|
|
2295
|
+
dragKeyRef.current = null;
|
|
2296
|
+
e.currentTarget.classList.remove("fc-tab-bar__tab--dragging");
|
|
2297
|
+
}, []);
|
|
2298
|
+
const { scrollMode, tabWidth } = layout;
|
|
2299
|
+
const rootClasses = [
|
|
2300
|
+
"fc-tab-bar",
|
|
2301
|
+
`fc-tab-bar--${variant}`,
|
|
2302
|
+
`fc-tab-bar--radius-${radius}`,
|
|
2303
|
+
tabRadius && `fc-tab-bar--tab-radius-${tabRadius}`,
|
|
2304
|
+
!scrollMode && tabWidth !== void 0 && "fc-tab-bar--shrink",
|
|
1926
2305
|
className
|
|
1927
2306
|
].filter(Boolean).join(" ");
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
2307
|
+
const tabList = /* @__PURE__ */ jsx18("div", { className: "fc-tab-bar__nav-wrap", children: items.map((item) => /* @__PURE__ */ jsx18(
|
|
2308
|
+
TabItemView,
|
|
2309
|
+
{
|
|
2310
|
+
item,
|
|
2311
|
+
isActive: activeKey === item.key,
|
|
2312
|
+
closable,
|
|
2313
|
+
draggable,
|
|
2314
|
+
tabWidth,
|
|
2315
|
+
tabClassName,
|
|
2316
|
+
activeTabClassName,
|
|
2317
|
+
tabStyle,
|
|
2318
|
+
activeTabStyle,
|
|
2319
|
+
renderCloseIcon,
|
|
2320
|
+
onClick: handleClick,
|
|
2321
|
+
onClose: handleClose,
|
|
2322
|
+
onDragStart: handleDragStart,
|
|
2323
|
+
onDragOver: handleDragOver,
|
|
2324
|
+
onDrop: handleDrop,
|
|
2325
|
+
onDragEnd: handleDragEnd
|
|
2326
|
+
},
|
|
2327
|
+
item.key
|
|
2328
|
+
)) });
|
|
2329
|
+
const addBtn = addable && /* @__PURE__ */ jsx18(
|
|
2330
|
+
"div",
|
|
2331
|
+
{
|
|
2332
|
+
className: "fc-tab-bar__add-btn",
|
|
2333
|
+
onClick: onAdd,
|
|
2334
|
+
role: "button",
|
|
2335
|
+
"aria-label": "\u6DFB\u52A0\u6807\u7B7E",
|
|
2336
|
+
children: renderAddButton ? renderAddButton() : "+"
|
|
2337
|
+
}
|
|
2338
|
+
);
|
|
2339
|
+
return /* @__PURE__ */ jsx18("div", { className: rootClasses, style: mergedStyle, role: "tablist", children: /* @__PURE__ */ jsxs13("div", { className: `fc-tab-bar__nav-outer${scrollMode ? " fc-tab-bar__nav-outer--scroll" : ""}`, children: [
|
|
2340
|
+
/* @__PURE__ */ jsx18("div", { className: `fc-tab-bar__nav${scrollMode ? " fc-tab-bar__nav--scroll" : ""}`, ref: navRef, children: scrollMode ? /* @__PURE__ */ jsx18(RollingBox, { horizontal: true, showThumb: "hide", style: { flex: 1, minWidth: 0, height: "auto" }, children: tabList }) : tabList }),
|
|
2341
|
+
addBtn
|
|
2342
|
+
] }) });
|
|
2343
|
+
});
|
|
2344
|
+
TabBar.displayName = "TabBar";
|
|
1957
2345
|
|
|
1958
|
-
// src/components/
|
|
1959
|
-
import {
|
|
2346
|
+
// src/components/Tag/TagItem.tsx
|
|
2347
|
+
import { useEffect as useEffect10, useRef as useRef9, useState as useState14 } from "react";
|
|
1960
2348
|
import { Fragment as Fragment3, jsx as jsx19, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
const
|
|
1982
|
-
const [
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
2349
|
+
function TagItem({
|
|
2350
|
+
schema,
|
|
2351
|
+
value,
|
|
2352
|
+
onChange,
|
|
2353
|
+
mode = "show",
|
|
2354
|
+
background,
|
|
2355
|
+
color,
|
|
2356
|
+
borderColor
|
|
2357
|
+
}) {
|
|
2358
|
+
const [editing, setEditing] = useState14(false);
|
|
2359
|
+
const [draft, setDraft] = useState14(() => value !== void 0 ? String(value) : "");
|
|
2360
|
+
const inputRef = useRef9(null);
|
|
2361
|
+
useEffect10(() => {
|
|
2362
|
+
if (!editing) setDraft(value !== void 0 ? String(value) : "");
|
|
2363
|
+
}, [value, editing]);
|
|
2364
|
+
const colorVars = {
|
|
2365
|
+
"--tag-bg": background,
|
|
2366
|
+
"--tag-color": color,
|
|
2367
|
+
"--tag-border": borderColor
|
|
2368
|
+
};
|
|
2369
|
+
const overrideStyle = {};
|
|
2370
|
+
for (const [k, v] of Object.entries(colorVars)) {
|
|
2371
|
+
if (v !== void 0) overrideStyle[k] = v;
|
|
2372
|
+
}
|
|
2373
|
+
if (schema.type === "boolean") {
|
|
2374
|
+
const boolVal = value === true || value === "true";
|
|
2375
|
+
return /* @__PURE__ */ jsxs14(
|
|
2376
|
+
"span",
|
|
2377
|
+
{
|
|
2378
|
+
className: "fc-tag-item fc-tag-item--boolean",
|
|
2379
|
+
style: overrideStyle,
|
|
2380
|
+
onClick: () => onChange?.(!boolVal),
|
|
2381
|
+
title: "\u70B9\u51FB\u5207\u6362",
|
|
2382
|
+
children: [
|
|
2383
|
+
/* @__PURE__ */ jsx19("span", { className: "fc-tag-item__name", children: schema.name }),
|
|
2384
|
+
/* @__PURE__ */ jsx19("span", { className: "fc-tag-item__sep", children: "\xB7" }),
|
|
2385
|
+
/* @__PURE__ */ jsx19("span", { className: boolVal ? "fc-tag-item__bool-true" : "fc-tag-item__bool-false", children: boolVal ? "\u2713" : "\u2717" })
|
|
2386
|
+
]
|
|
2387
|
+
}
|
|
2388
|
+
);
|
|
2389
|
+
}
|
|
2390
|
+
const commit = () => {
|
|
2391
|
+
if (schema.type === "number") {
|
|
2392
|
+
const n = parseFloat(draft);
|
|
2393
|
+
if (!isNaN(n)) {
|
|
2394
|
+
const clamped = schema.range_min != null && n < schema.range_min ? schema.range_min : schema.range_max != null && n > schema.range_max ? schema.range_max : n;
|
|
2395
|
+
onChange?.(clamped);
|
|
2396
|
+
}
|
|
1990
2397
|
} else {
|
|
1991
|
-
|
|
1992
|
-
const dataTheme = document.documentElement.getAttribute("data-theme");
|
|
1993
|
-
if (dataTheme === "dark") {
|
|
1994
|
-
setCurrentTheme("dark");
|
|
1995
|
-
} else if (dataTheme === "light") {
|
|
1996
|
-
setCurrentTheme("light");
|
|
1997
|
-
} else {
|
|
1998
|
-
const hasDarkClass = document.documentElement.classList.contains("dark") || document.body.classList.contains("dark") || document.documentElement.classList.contains("theme-dark") || document.body.classList.contains("theme-dark");
|
|
1999
|
-
setCurrentTheme(hasDarkClass ? "dark" : "light");
|
|
2000
|
-
}
|
|
2001
|
-
};
|
|
2002
|
-
checkTheme();
|
|
2003
|
-
const observer = new MutationObserver(checkTheme);
|
|
2004
|
-
observer.observe(document.documentElement, {
|
|
2005
|
-
attributes: true,
|
|
2006
|
-
attributeFilter: ["data-theme", "class"]
|
|
2007
|
-
});
|
|
2008
|
-
observer.observe(document.body, {
|
|
2009
|
-
attributes: true,
|
|
2010
|
-
attributeFilter: ["class"]
|
|
2011
|
-
});
|
|
2012
|
-
return () => observer.disconnect();
|
|
2013
|
-
}
|
|
2014
|
-
}, [propTheme]);
|
|
2015
|
-
useEffect7(() => {
|
|
2016
|
-
messagesEndRef.current?.scrollIntoView({ behavior: "smooth" });
|
|
2017
|
-
}, [messages, loading]);
|
|
2018
|
-
useEffect7(() => {
|
|
2019
|
-
if (autoFocus && !disabled && inputRef.current) {
|
|
2020
|
-
inputRef.current.focus();
|
|
2398
|
+
onChange?.(draft);
|
|
2021
2399
|
}
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2400
|
+
if (mode === "show") setEditing(false);
|
|
2401
|
+
};
|
|
2402
|
+
const cancel = () => {
|
|
2403
|
+
setDraft(value !== void 0 ? String(value) : "");
|
|
2404
|
+
if (mode === "show") setEditing(false);
|
|
2405
|
+
};
|
|
2406
|
+
const isEditing = mode === "edit" || editing;
|
|
2407
|
+
if (isEditing) {
|
|
2408
|
+
return /* @__PURE__ */ jsxs14("span", { className: "fc-tag-item fc-tag-item--editing", style: overrideStyle, children: [
|
|
2409
|
+
/* @__PURE__ */ jsx19("span", { className: "fc-tag-item__name", children: schema.name }),
|
|
2410
|
+
/* @__PURE__ */ jsx19("span", { className: "fc-tag-item__sep", children: "\xB7" }),
|
|
2411
|
+
/* @__PURE__ */ jsx19(
|
|
2412
|
+
"input",
|
|
2413
|
+
{
|
|
2414
|
+
ref: inputRef,
|
|
2415
|
+
className: "fc-tag-item__input",
|
|
2416
|
+
type: schema.type === "number" ? "number" : "text",
|
|
2417
|
+
value: draft,
|
|
2418
|
+
min: schema.range_min ?? void 0,
|
|
2419
|
+
max: schema.range_max ?? void 0,
|
|
2420
|
+
autoFocus: true,
|
|
2421
|
+
onChange: (e) => setDraft(e.target.value),
|
|
2422
|
+
onBlur: commit,
|
|
2423
|
+
onKeyDown: (e) => {
|
|
2424
|
+
if (e.key === "Enter") {
|
|
2425
|
+
e.preventDefault();
|
|
2426
|
+
commit();
|
|
2427
|
+
}
|
|
2428
|
+
if (e.key === "Escape") {
|
|
2429
|
+
e.preventDefault();
|
|
2430
|
+
cancel();
|
|
2431
|
+
}
|
|
2432
|
+
}
|
|
2433
|
+
}
|
|
2434
|
+
)
|
|
2435
|
+
] });
|
|
2436
|
+
}
|
|
2437
|
+
return /* @__PURE__ */ jsxs14(
|
|
2438
|
+
"span",
|
|
2439
|
+
{
|
|
2440
|
+
className: "fc-tag-item fc-tag-item--show",
|
|
2441
|
+
style: overrideStyle,
|
|
2442
|
+
onDoubleClick: () => {
|
|
2443
|
+
setDraft(value !== void 0 ? String(value) : "");
|
|
2444
|
+
setEditing(true);
|
|
2445
|
+
setTimeout(() => inputRef.current?.select(), 0);
|
|
2446
|
+
},
|
|
2447
|
+
title: "\u53CC\u51FB\u7F16\u8F91",
|
|
2448
|
+
children: [
|
|
2449
|
+
/* @__PURE__ */ jsx19("span", { className: "fc-tag-item__name", children: schema.name }),
|
|
2450
|
+
value !== void 0 && /* @__PURE__ */ jsxs14(Fragment3, { children: [
|
|
2451
|
+
/* @__PURE__ */ jsx19("span", { className: "fc-tag-item__sep", children: "\xB7" }),
|
|
2452
|
+
/* @__PURE__ */ jsx19("span", { className: "fc-tag-item__value", children: String(value) })
|
|
2453
|
+
] })
|
|
2454
|
+
]
|
|
2029
2455
|
}
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2456
|
+
);
|
|
2457
|
+
}
|
|
2458
|
+
|
|
2459
|
+
// src/components/MarkdownEditor/MarkdownEditor.tsx
|
|
2460
|
+
import { useState as useState15 } from "react";
|
|
2461
|
+
import MDEditor, { commands } from "@uiw/react-md-editor";
|
|
2462
|
+
import { jsx as jsx20 } from "react/jsx-runtime";
|
|
2463
|
+
function withTitle(cmd, title) {
|
|
2464
|
+
return { ...cmd, buttonProps: { ...cmd.buttonProps ?? {}, title } };
|
|
2465
|
+
}
|
|
2466
|
+
var TOOLBAR_COMMANDS = [
|
|
2467
|
+
withTitle(commands.bold, "\u52A0\u7C97"),
|
|
2468
|
+
withTitle(commands.italic, "\u659C\u4F53"),
|
|
2469
|
+
withTitle(commands.strikethrough, "\u5220\u9664\u7EBF"),
|
|
2470
|
+
commands.divider,
|
|
2471
|
+
withTitle(commands.title1, "\u4E00\u7EA7\u6807\u9898"),
|
|
2472
|
+
withTitle(commands.title2, "\u4E8C\u7EA7\u6807\u9898"),
|
|
2473
|
+
withTitle(commands.title3, "\u4E09\u7EA7\u6807\u9898"),
|
|
2474
|
+
commands.divider,
|
|
2475
|
+
withTitle(commands.quote, "\u5F15\u7528"),
|
|
2476
|
+
withTitle(commands.code, "\u884C\u5185\u4EE3\u7801"),
|
|
2477
|
+
withTitle(commands.codeBlock, "\u4EE3\u7801\u5757"),
|
|
2478
|
+
commands.divider,
|
|
2479
|
+
withTitle(commands.link, "\u94FE\u63A5"),
|
|
2480
|
+
withTitle(commands.unorderedListCommand, "\u65E0\u5E8F\u5217\u8868"),
|
|
2481
|
+
withTitle(commands.orderedListCommand, "\u6709\u5E8F\u5217\u8868"),
|
|
2482
|
+
withTitle(commands.hr, "\u5206\u5272\u7EBF")
|
|
2483
|
+
];
|
|
2484
|
+
function MarkdownEditor({
|
|
2485
|
+
value,
|
|
2486
|
+
onChange,
|
|
2487
|
+
onAiComplete,
|
|
2488
|
+
minHeight = 200,
|
|
2489
|
+
placeholder = "\u5728\u6B64\u8F93\u5165\u5185\u5BB9...",
|
|
2490
|
+
mode = "edit",
|
|
2491
|
+
background,
|
|
2492
|
+
toolbarBackground,
|
|
2493
|
+
borderColor
|
|
2494
|
+
}) {
|
|
2495
|
+
const { resolvedTheme } = useTheme();
|
|
2496
|
+
const [showSplit, setShowSplit] = useState15(false);
|
|
2497
|
+
const colorVars = {
|
|
2498
|
+
"--md-bg": background,
|
|
2499
|
+
"--md-toolbar-bg": toolbarBackground,
|
|
2500
|
+
"--md-border": borderColor
|
|
2501
|
+
};
|
|
2502
|
+
const overrideStyle = {};
|
|
2503
|
+
for (const [k, v] of Object.entries(colorVars)) {
|
|
2504
|
+
if (v !== void 0) overrideStyle[k] = v;
|
|
2505
|
+
}
|
|
2506
|
+
const aiCommand = {
|
|
2507
|
+
name: "ai-complete",
|
|
2508
|
+
keyCommand: "ai-complete",
|
|
2509
|
+
buttonProps: { "aria-label": "AI \u8865\u5168", title: "AI \u8865\u5168", className: "fc-md-ai-btn" },
|
|
2510
|
+
icon: /* @__PURE__ */ jsx20("span", { children: "AI" }),
|
|
2511
|
+
execute: () => onAiComplete?.()
|
|
2512
|
+
};
|
|
2513
|
+
const splitCommand = {
|
|
2514
|
+
name: "split-view",
|
|
2515
|
+
keyCommand: "split-view",
|
|
2516
|
+
buttonProps: {
|
|
2517
|
+
"aria-label": showSplit ? "\u7EAF\u7F16\u8F91" : "\u53CC\u680F\u9884\u89C8",
|
|
2518
|
+
title: showSplit ? "\u7EAF\u7F16\u8F91" : "\u53CC\u680F\u9884\u89C8",
|
|
2519
|
+
className: `fc-md-split-btn${showSplit ? " fc-md-split-btn--active" : ""}`
|
|
2520
|
+
},
|
|
2521
|
+
icon: /* @__PURE__ */ jsx20("span", { className: "fc-md-split-icon", children: "\u229F" }),
|
|
2522
|
+
execute: () => setShowSplit((p) => !p)
|
|
2523
|
+
};
|
|
2524
|
+
const extraCommands = [
|
|
2525
|
+
splitCommand,
|
|
2526
|
+
...onAiComplete ? [commands.divider, aiCommand] : [],
|
|
2527
|
+
withTitle(commands.fullscreen, "\u5168\u5C4F")
|
|
2528
|
+
];
|
|
2529
|
+
const editorPreview = mode === "preview" ? "preview" : showSplit ? "live" : "edit";
|
|
2530
|
+
return /* @__PURE__ */ jsx20(
|
|
2531
|
+
"div",
|
|
2532
|
+
{
|
|
2533
|
+
className: "fc-md-wrap",
|
|
2534
|
+
style: overrideStyle,
|
|
2535
|
+
"data-color-mode": resolvedTheme,
|
|
2536
|
+
children: /* @__PURE__ */ jsx20(
|
|
2537
|
+
MDEditor,
|
|
2538
|
+
{
|
|
2539
|
+
value,
|
|
2540
|
+
onChange: (v) => onChange(v ?? ""),
|
|
2541
|
+
commands: TOOLBAR_COMMANDS,
|
|
2542
|
+
extraCommands,
|
|
2543
|
+
height: minHeight,
|
|
2544
|
+
preview: editorPreview,
|
|
2545
|
+
hideToolbar: mode === "preview",
|
|
2546
|
+
visibleDragbar: false,
|
|
2547
|
+
textareaProps: { placeholder }
|
|
2548
|
+
}
|
|
2549
|
+
)
|
|
2035
2550
|
}
|
|
2551
|
+
);
|
|
2552
|
+
}
|
|
2553
|
+
|
|
2554
|
+
// src/components/ContextMenu/ContextMenuContext.tsx
|
|
2555
|
+
import { createContext as createContext4, useContext as useContext4, useEffect as useEffect11, useRef as useRef10, useState as useState16 } from "react";
|
|
2556
|
+
import { jsx as jsx21, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
2557
|
+
var ContextMenuContext = createContext4(null);
|
|
2558
|
+
function ContextMenuProvider({
|
|
2559
|
+
children,
|
|
2560
|
+
background,
|
|
2561
|
+
borderColor,
|
|
2562
|
+
hoverBackground
|
|
2563
|
+
}) {
|
|
2564
|
+
const [menu, setMenu] = useState16({
|
|
2565
|
+
visible: false,
|
|
2566
|
+
x: 0,
|
|
2567
|
+
y: 0,
|
|
2568
|
+
items: []
|
|
2569
|
+
});
|
|
2570
|
+
const menuRef = useRef10(null);
|
|
2571
|
+
const showContextMenu = (e, items) => {
|
|
2572
|
+
e.preventDefault();
|
|
2573
|
+
e.stopPropagation();
|
|
2574
|
+
setMenu({ visible: true, x: e.clientX, y: e.clientY, items });
|
|
2575
|
+
};
|
|
2576
|
+
const hide = () => setMenu((s) => ({ ...s, visible: false }));
|
|
2577
|
+
useClickOutside(menuRef, hide, menu.visible);
|
|
2578
|
+
useEffect11(() => {
|
|
2579
|
+
if (!menu.visible) return;
|
|
2580
|
+
const onKeyDown = (e) => {
|
|
2581
|
+
if (e.key === "Escape") hide();
|
|
2582
|
+
};
|
|
2583
|
+
window.addEventListener("keydown", onKeyDown);
|
|
2584
|
+
return () => window.removeEventListener("keydown", onKeyDown);
|
|
2585
|
+
}, [menu.visible]);
|
|
2586
|
+
const getPosition = () => {
|
|
2587
|
+
const W = window.innerWidth;
|
|
2588
|
+
const H = window.innerHeight;
|
|
2589
|
+
const menuW = 180;
|
|
2590
|
+
const menuH = menu.items.length * 32 + 12;
|
|
2591
|
+
return {
|
|
2592
|
+
left: menu.x + menuW > W ? menu.x - menuW : menu.x,
|
|
2593
|
+
top: menu.y + menuH > H ? menu.y - menuH : menu.y
|
|
2594
|
+
};
|
|
2595
|
+
};
|
|
2596
|
+
const colorVars = {
|
|
2597
|
+
"--ctx-bg": background,
|
|
2598
|
+
"--ctx-border": borderColor,
|
|
2599
|
+
"--ctx-item-hover-bg": hoverBackground
|
|
2600
|
+
};
|
|
2601
|
+
const overrideStyle = { ...getPosition() };
|
|
2602
|
+
for (const [k, v] of Object.entries(colorVars)) {
|
|
2603
|
+
if (v !== void 0) overrideStyle[k] = v;
|
|
2604
|
+
}
|
|
2605
|
+
return /* @__PURE__ */ jsxs15(ContextMenuContext.Provider, { value: { showContextMenu }, children: [
|
|
2606
|
+
children,
|
|
2607
|
+
menu.visible && /* @__PURE__ */ jsx21(
|
|
2608
|
+
"ul",
|
|
2609
|
+
{
|
|
2610
|
+
ref: menuRef,
|
|
2611
|
+
className: "fc-context-menu",
|
|
2612
|
+
style: overrideStyle,
|
|
2613
|
+
children: menu.items.map((item, i) => {
|
|
2614
|
+
if ("type" in item && item.type === "divider") {
|
|
2615
|
+
return /* @__PURE__ */ jsx21("li", { className: "fc-context-menu__divider" }, i);
|
|
2616
|
+
}
|
|
2617
|
+
const action = item;
|
|
2618
|
+
return /* @__PURE__ */ jsxs15(
|
|
2619
|
+
"li",
|
|
2620
|
+
{
|
|
2621
|
+
className: [
|
|
2622
|
+
"fc-context-menu__item",
|
|
2623
|
+
action.danger ? "fc-context-menu__item--danger" : "",
|
|
2624
|
+
action.disabled ? "fc-context-menu__item--disabled" : ""
|
|
2625
|
+
].filter(Boolean).join(" "),
|
|
2626
|
+
onPointerDown: (e) => e.stopPropagation(),
|
|
2627
|
+
onClick: () => {
|
|
2628
|
+
if (action.disabled) return;
|
|
2629
|
+
action.onClick();
|
|
2630
|
+
hide();
|
|
2631
|
+
},
|
|
2632
|
+
children: [
|
|
2633
|
+
action.icon && /* @__PURE__ */ jsx21("span", { className: "fc-context-menu__icon", children: action.icon }),
|
|
2634
|
+
/* @__PURE__ */ jsx21("span", { className: "fc-context-menu__label", children: action.label })
|
|
2635
|
+
]
|
|
2636
|
+
},
|
|
2637
|
+
i
|
|
2638
|
+
);
|
|
2639
|
+
})
|
|
2640
|
+
}
|
|
2641
|
+
)
|
|
2642
|
+
] });
|
|
2643
|
+
}
|
|
2644
|
+
var useContextMenu = () => useContext4(ContextMenuContext);
|
|
2645
|
+
|
|
2646
|
+
// src/components/Chat/Chat.tsx
|
|
2647
|
+
import { useState as useState18, useRef as useRef11, useEffect as useEffect12, useCallback as useCallback11, useMemo as useMemo5 } from "react";
|
|
2648
|
+
|
|
2649
|
+
// src/components/SmartMessage/SmartMessage.tsx
|
|
2650
|
+
import { useState as useState17, useCallback as useCallback10 } from "react";
|
|
2651
|
+
import { Fragment as Fragment4, jsx as jsx22, jsxs as jsxs16 } from "react/jsx-runtime";
|
|
2652
|
+
var SmartMessage = ({
|
|
2653
|
+
id,
|
|
2654
|
+
content,
|
|
2655
|
+
role,
|
|
2656
|
+
status,
|
|
2657
|
+
toolName,
|
|
2658
|
+
toolResult,
|
|
2659
|
+
onCopy,
|
|
2660
|
+
className = "",
|
|
2661
|
+
style = {}
|
|
2662
|
+
}) => {
|
|
2663
|
+
const [copied, setCopied] = useState17(false);
|
|
2664
|
+
const handleCopy = useCallback10(async () => {
|
|
2036
2665
|
try {
|
|
2037
2666
|
await navigator.clipboard.writeText(content);
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
}, 2e3);
|
|
2667
|
+
setCopied(true);
|
|
2668
|
+
onCopy?.(content, role);
|
|
2669
|
+
setTimeout(() => setCopied(false), 2e3);
|
|
2042
2670
|
} catch (err) {
|
|
2043
2671
|
console.error("\u590D\u5236\u5931\u8D25:", err);
|
|
2044
|
-
if (err instanceof Error) {
|
|
2045
|
-
if (err.name === "NotAllowedError") {
|
|
2046
|
-
setCopyError("\u9700\u8981\u526A\u8D34\u677F\u6743\u9650\uFF0C\u8BF7\u5141\u8BB8\u540E\u91CD\u8BD5");
|
|
2047
|
-
} else if (err.name === "SecurityError") {
|
|
2048
|
-
setCopyError("\u51FA\u4E8E\u5B89\u5168\u539F\u56E0\uFF0C\u65E0\u6CD5\u590D\u5236\u5185\u5BB9");
|
|
2049
|
-
} else {
|
|
2050
|
-
setCopyError("\u590D\u5236\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5");
|
|
2051
|
-
}
|
|
2052
|
-
} else {
|
|
2053
|
-
setCopyError("\u590D\u5236\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5");
|
|
2054
|
-
}
|
|
2055
|
-
setTimeout(() => {
|
|
2056
|
-
setCopyError(null);
|
|
2057
|
-
}, 3e3);
|
|
2058
2672
|
}
|
|
2059
|
-
}, []);
|
|
2060
|
-
const
|
|
2061
|
-
const
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2673
|
+
}, [content, role, onCopy]);
|
|
2674
|
+
const getMessageClass = () => {
|
|
2675
|
+
const baseClass = "fc-smart-message";
|
|
2676
|
+
const roleClass = `fc-smart-message--${role}`;
|
|
2677
|
+
const statusClass = status ? `fc-smart-message--${status}` : "";
|
|
2678
|
+
return `${baseClass} ${roleClass} ${statusClass} ${className}`.trim();
|
|
2679
|
+
};
|
|
2680
|
+
const shouldShowCopyButton = () => {
|
|
2681
|
+
return role === "user" || role === "assistant";
|
|
2682
|
+
};
|
|
2683
|
+
const renderContent2 = () => {
|
|
2684
|
+
switch (role) {
|
|
2685
|
+
case "tool":
|
|
2686
|
+
return /* @__PURE__ */ jsxs16(Fragment4, { children: [
|
|
2687
|
+
toolName && /* @__PURE__ */ jsxs16("div", { className: "fc-smart-message-tool-info", children: [
|
|
2688
|
+
/* @__PURE__ */ jsx22("span", { className: "fc-smart-message-tool-icon", children: "\u{1F527}" }),
|
|
2689
|
+
/* @__PURE__ */ jsx22("span", { className: "fc-smart-message-tool-name", children: toolName })
|
|
2690
|
+
] }),
|
|
2691
|
+
/* @__PURE__ */ jsx22("div", { className: "fc-smart-message-content", children: /* @__PURE__ */ jsx22("pre", { className: "fc-smart-message-tool-result", children: (() => {
|
|
2692
|
+
try {
|
|
2693
|
+
return JSON.stringify(toolResult || content, null, 2);
|
|
2694
|
+
} catch {
|
|
2695
|
+
return "[\u5E8F\u5217\u5316\u5931\u8D25]";
|
|
2696
|
+
}
|
|
2697
|
+
})() }) })
|
|
2698
|
+
] });
|
|
2699
|
+
case "system":
|
|
2700
|
+
return /* @__PURE__ */ jsx22("div", { className: "fc-smart-message-content", children: /* @__PURE__ */ jsx22("div", { className: "fc-smart-message-text fc-smart-message-system-text", children: content }) });
|
|
2701
|
+
default:
|
|
2702
|
+
return /* @__PURE__ */ jsx22("div", { className: "fc-smart-message-content", children: /* @__PURE__ */ jsx22("div", { className: "fc-smart-message-text", children: content }) });
|
|
2066
2703
|
}
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
onTyping?.(false);
|
|
2078
|
-
}
|
|
2079
|
-
}).catch((error) => {
|
|
2080
|
-
console.error("\u53D1\u9001\u6D88\u606F\u5931\u8D25:", error);
|
|
2081
|
-
}).finally(() => {
|
|
2082
|
-
setIsSending(false);
|
|
2083
|
-
});
|
|
2084
|
-
} else {
|
|
2085
|
-
setInputValue("");
|
|
2086
|
-
if (inputRef.current) {
|
|
2087
|
-
inputRef.current.style.height = "auto";
|
|
2088
|
-
}
|
|
2089
|
-
if (isTyping) {
|
|
2090
|
-
setIsTyping(false);
|
|
2091
|
-
onTyping?.(false);
|
|
2704
|
+
};
|
|
2705
|
+
return /* @__PURE__ */ jsxs16("div", { className: getMessageClass(), style, "data-message-id": id, children: [
|
|
2706
|
+
/* @__PURE__ */ jsx22("div", { className: "fc-smart-message-content-wrapper", children: renderContent2() }),
|
|
2707
|
+
shouldShowCopyButton() && /* @__PURE__ */ jsx22(
|
|
2708
|
+
"button",
|
|
2709
|
+
{
|
|
2710
|
+
className: "fc-smart-message-copy-btn",
|
|
2711
|
+
onClick: handleCopy,
|
|
2712
|
+
title: "\u590D\u5236\u5185\u5BB9",
|
|
2713
|
+
children: copied ? /* @__PURE__ */ jsx22("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", children: /* @__PURE__ */ jsx22("path", { d: "M20 6L9 17L4 12", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }) }) : /* @__PURE__ */ jsx22("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", children: /* @__PURE__ */ jsx22("path", { d: "M16 1H4C2.9 1 2 1.9 2 3V17H4V3H16V1ZM19 5H8C6.9 5 6 5.9 6 7V21C6 22.1 6.9 23 8 23H19C20.1 23 21 22.1 21 21V7C21 5.9 20.1 5 19 5ZM19 21H8V7H19V21Z", fill: "currentColor" }) })
|
|
2092
2714
|
}
|
|
2093
|
-
|
|
2715
|
+
)
|
|
2716
|
+
] });
|
|
2717
|
+
};
|
|
2718
|
+
var SmartMessage_default = SmartMessage;
|
|
2719
|
+
|
|
2720
|
+
// src/components/Chat/Chat.tsx
|
|
2721
|
+
import { jsx as jsx23, jsxs as jsxs17 } from "react/jsx-runtime";
|
|
2722
|
+
var Chat = ({
|
|
2723
|
+
messages = [],
|
|
2724
|
+
title = "\u6D41\u4E91AI",
|
|
2725
|
+
loading = false,
|
|
2726
|
+
conversations = [],
|
|
2727
|
+
currentConversationId,
|
|
2728
|
+
emptyText = "\u6682\u65E0\u5386\u53F2\u5BF9\u8BDD",
|
|
2729
|
+
newConversationText = "\u65B0\u5EFA\u5BF9\u8BDD",
|
|
2730
|
+
historyTitle = "\u5386\u53F2\u5BF9\u8BDD",
|
|
2731
|
+
showHistoryButton = true,
|
|
2732
|
+
showMinimizeButton = true,
|
|
2733
|
+
showHeader = true,
|
|
2734
|
+
showFooter = false,
|
|
2735
|
+
autoScroll = true,
|
|
2736
|
+
onSwitchConversation,
|
|
2737
|
+
onNewConversation,
|
|
2738
|
+
onDeleteConversation,
|
|
2739
|
+
onMinimize,
|
|
2740
|
+
onRestore,
|
|
2741
|
+
onMessageCopy,
|
|
2742
|
+
className = "",
|
|
2743
|
+
style = {},
|
|
2744
|
+
headerClassName = "",
|
|
2745
|
+
headerStyle = {},
|
|
2746
|
+
messagesClassName = "",
|
|
2747
|
+
messagesStyle = {},
|
|
2748
|
+
bubbleClassName = "",
|
|
2749
|
+
height = "600px",
|
|
2750
|
+
width
|
|
2751
|
+
}) => {
|
|
2752
|
+
const [showHistory, setShowHistory] = useState18(false);
|
|
2753
|
+
const [isMinimized, setIsMinimized] = useState18(false);
|
|
2754
|
+
const messagesContainerRef = useRef11(null);
|
|
2755
|
+
const messagesEndRef = useRef11(null);
|
|
2756
|
+
const historyPanelRef = useRef11(null);
|
|
2757
|
+
const currentConversation = useMemo5(
|
|
2758
|
+
() => conversations.find((c) => c.id === currentConversationId),
|
|
2759
|
+
[conversations, currentConversationId]
|
|
2760
|
+
);
|
|
2761
|
+
const currentTitle = currentConversation?.title || title;
|
|
2762
|
+
useEffect12(() => {
|
|
2763
|
+
if (autoScroll && messagesContainerRef.current && !showHistory && !isMinimized) {
|
|
2764
|
+
messagesContainerRef.current.scrollTop = messagesContainerRef.current.scrollHeight;
|
|
2094
2765
|
}
|
|
2095
|
-
}, [
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
|
|
2766
|
+
}, [messages, loading, showHistory, isMinimized, autoScroll]);
|
|
2767
|
+
useClickOutside(historyPanelRef, () => setShowHistory(false), showHistory);
|
|
2768
|
+
const handleDeleteConversation = useCallback11((conversationId) => {
|
|
2769
|
+
onDeleteConversation?.(conversationId);
|
|
2770
|
+
}, [onDeleteConversation]);
|
|
2771
|
+
const handleMinimize = useCallback11(() => {
|
|
2772
|
+
setIsMinimized(true);
|
|
2773
|
+
onMinimize?.();
|
|
2774
|
+
}, [onMinimize]);
|
|
2775
|
+
const handleRestore = useCallback11(() => {
|
|
2776
|
+
setIsMinimized(false);
|
|
2777
|
+
onRestore?.();
|
|
2778
|
+
}, [onRestore]);
|
|
2779
|
+
const handleCopy = useCallback11((content) => {
|
|
2780
|
+
const message = messages.find((m) => m.content === content);
|
|
2781
|
+
if (message) {
|
|
2782
|
+
onMessageCopy?.(message);
|
|
2100
2783
|
}
|
|
2101
|
-
}, [
|
|
2102
|
-
const
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2784
|
+
}, [messages, onMessageCopy]);
|
|
2785
|
+
const containerStyle = useMemo5(() => ({
|
|
2786
|
+
height,
|
|
2787
|
+
width,
|
|
2788
|
+
...style
|
|
2789
|
+
}), [height, width, style]);
|
|
2790
|
+
const renderSystemMessage = (message) => {
|
|
2791
|
+
return /* @__PURE__ */ jsx23("div", { className: "system-message-container", children: /* @__PURE__ */ jsx23(
|
|
2792
|
+
SmartMessage_default,
|
|
2793
|
+
{
|
|
2794
|
+
id: message.id,
|
|
2795
|
+
content: message.content,
|
|
2796
|
+
role: message.type,
|
|
2797
|
+
timestamp: message.timestamp,
|
|
2798
|
+
status: message.status,
|
|
2799
|
+
toolName: message.toolName,
|
|
2800
|
+
toolResult: message.toolResult,
|
|
2801
|
+
onCopy: handleCopy,
|
|
2802
|
+
className: bubbleClassName
|
|
2112
2803
|
}
|
|
2113
|
-
}
|
|
2114
|
-
}
|
|
2115
|
-
const
|
|
2116
|
-
return new Date(date).toLocaleTimeString("zh-CN", {
|
|
2117
|
-
hour: "2-digit",
|
|
2118
|
-
minute: "2-digit"
|
|
2119
|
-
});
|
|
2120
|
-
}, []);
|
|
2121
|
-
const renderMessage = (message) => {
|
|
2804
|
+
) }, message.id);
|
|
2805
|
+
};
|
|
2806
|
+
const renderUserAssistantMessage = (message) => {
|
|
2122
2807
|
const isUser = message.type === "user";
|
|
2123
|
-
const
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
2808
|
+
const showCopyButton = message.type === "user" || message.type === "assistant";
|
|
2809
|
+
return /* @__PURE__ */ jsxs17(
|
|
2810
|
+
"div",
|
|
2811
|
+
{
|
|
2812
|
+
className: `message-wrapper message-wrapper--${isUser ? "user" : "assistant"}`,
|
|
2813
|
+
children: [
|
|
2814
|
+
/* @__PURE__ */ jsx23(
|
|
2815
|
+
SmartMessage_default,
|
|
2816
|
+
{
|
|
2817
|
+
id: message.id,
|
|
2818
|
+
content: message.content,
|
|
2819
|
+
role: message.type,
|
|
2820
|
+
timestamp: message.timestamp,
|
|
2821
|
+
status: message.status,
|
|
2822
|
+
toolName: message.toolName,
|
|
2823
|
+
toolResult: message.toolResult,
|
|
2824
|
+
onCopy: handleCopy,
|
|
2825
|
+
className: bubbleClassName
|
|
2826
|
+
}
|
|
2827
|
+
),
|
|
2828
|
+
showCopyButton && /* @__PURE__ */ jsx23("div", { className: `message-copy-wrapper message-copy-wrapper--${isUser ? "user" : "assistant"}`, children: /* @__PURE__ */ jsx23(
|
|
2829
|
+
"button",
|
|
2830
|
+
{
|
|
2831
|
+
className: "message-copy-btn",
|
|
2832
|
+
onClick: () => handleCopy(message.content),
|
|
2833
|
+
title: "\u590D\u5236\u5185\u5BB9",
|
|
2834
|
+
children: /* @__PURE__ */ jsx23("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", children: /* @__PURE__ */ jsx23(
|
|
2835
|
+
"path",
|
|
2836
|
+
{
|
|
2837
|
+
d: "M16 1H4C2.9 1 2 1.9 2 3V17H4V3H16V1ZM19 5H8C6.9 5 6 5.9 6 7V21C6 22.1 6.9 23 8 23H19C20.1 23 21 22.1 21 21V7C21 5.9 20.1 5 19 5ZM19 21H8V7H19V21Z",
|
|
2838
|
+
fill: "currentColor"
|
|
2839
|
+
}
|
|
2840
|
+
) })
|
|
2841
|
+
}
|
|
2842
|
+
) })
|
|
2843
|
+
]
|
|
2844
|
+
},
|
|
2845
|
+
message.id
|
|
2846
|
+
);
|
|
2847
|
+
};
|
|
2848
|
+
const renderToolMessage = (message) => {
|
|
2849
|
+
return /* @__PURE__ */ jsx23("div", { className: "tool-message-container", children: /* @__PURE__ */ jsx23(
|
|
2850
|
+
SmartMessage_default,
|
|
2851
|
+
{
|
|
2852
|
+
id: message.id,
|
|
2853
|
+
content: message.content,
|
|
2854
|
+
role: message.type,
|
|
2855
|
+
timestamp: message.timestamp,
|
|
2856
|
+
status: message.status,
|
|
2857
|
+
toolName: message.toolName,
|
|
2858
|
+
toolResult: message.toolResult,
|
|
2859
|
+
onCopy: handleCopy,
|
|
2860
|
+
className: bubbleClassName
|
|
2861
|
+
}
|
|
2862
|
+
) }, message.id);
|
|
2863
|
+
};
|
|
2864
|
+
const renderMessage = (message) => {
|
|
2865
|
+
switch (message.type) {
|
|
2866
|
+
case "system":
|
|
2867
|
+
return renderSystemMessage(message);
|
|
2868
|
+
case "tool":
|
|
2869
|
+
return renderToolMessage(message);
|
|
2870
|
+
default:
|
|
2871
|
+
return renderUserAssistantMessage(message);
|
|
2127
2872
|
}
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2873
|
+
};
|
|
2874
|
+
if (isMinimized) {
|
|
2875
|
+
return /* @__PURE__ */ jsx23("div", { className: `chat-container-minimized ${className}`, style: containerStyle, children: /* @__PURE__ */ jsx23("button", { className: "restore-btn-only", onClick: handleRestore, title: "\u5C55\u5F00", children: /* @__PURE__ */ jsx23("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", children: /* @__PURE__ */ jsx23("path", { d: "M12 5V19M5 12H19", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" }) }) }) });
|
|
2876
|
+
}
|
|
2877
|
+
return /* @__PURE__ */ jsxs17("div", { className: `chat-container ${className}`, style: containerStyle, children: [
|
|
2878
|
+
showHeader && /* @__PURE__ */ jsxs17("div", { className: `chat-header ${headerClassName}`, style: headerStyle, children: [
|
|
2879
|
+
/* @__PURE__ */ jsx23("div", { className: "chat-title", children: currentTitle }),
|
|
2880
|
+
/* @__PURE__ */ jsxs17("div", { className: "chat-header-actions", children: [
|
|
2881
|
+
showHistoryButton && /* @__PURE__ */ jsx23("button", { className: "history-btn", onClick: () => setShowHistory(!showHistory), title: "\u5386\u53F2\u5BF9\u8BDD", children: /* @__PURE__ */ jsxs17("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", children: [
|
|
2882
|
+
/* @__PURE__ */ jsx23("path", { d: "M12 8V12L15 15", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" }),
|
|
2883
|
+
/* @__PURE__ */ jsx23("circle", { cx: "12", cy: "12", r: "9", stroke: "currentColor", strokeWidth: "2" })
|
|
2884
|
+
] }) }),
|
|
2885
|
+
showMinimizeButton && /* @__PURE__ */ jsx23("button", { className: "minimize-btn", onClick: handleMinimize, title: "\u6700\u5C0F\u5316", children: /* @__PURE__ */ jsx23("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", children: /* @__PURE__ */ jsx23("path", { d: "M5 12H19", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" }) }) })
|
|
2886
|
+
] })
|
|
2887
|
+
] }),
|
|
2888
|
+
/* @__PURE__ */ jsxs17("div", { className: `chat-messages ${messagesClassName}`, ref: messagesContainerRef, style: messagesStyle, children: [
|
|
2889
|
+
messages.map((message) => renderMessage(message)),
|
|
2890
|
+
loading && /* @__PURE__ */ jsx23("div", { className: "typing-wrapper", children: /* @__PURE__ */ jsxs17("div", { className: "typing-indicator", children: [
|
|
2891
|
+
/* @__PURE__ */ jsx23("span", {}),
|
|
2892
|
+
/* @__PURE__ */ jsx23("span", {}),
|
|
2893
|
+
/* @__PURE__ */ jsx23("span", {})
|
|
2894
|
+
] }) }),
|
|
2895
|
+
/* @__PURE__ */ jsx23("div", { ref: messagesEndRef })
|
|
2896
|
+
] }),
|
|
2897
|
+
showFooter && /* @__PURE__ */ jsx23("div", { className: "chat-footer" }),
|
|
2898
|
+
showHistory && /* @__PURE__ */ jsxs17("div", { className: "history-panel", ref: historyPanelRef, children: [
|
|
2899
|
+
/* @__PURE__ */ jsxs17("div", { className: "history-header", children: [
|
|
2900
|
+
/* @__PURE__ */ jsx23("h3", { children: historyTitle }),
|
|
2901
|
+
/* @__PURE__ */ jsx23("button", { className: "close-history-btn", onClick: () => setShowHistory(false), children: "\u2715" })
|
|
2902
|
+
] }),
|
|
2903
|
+
/* @__PURE__ */ jsxs17("div", { className: "history-list", children: [
|
|
2904
|
+
/* @__PURE__ */ jsxs17("button", { className: "new-conversation-btn-large", onClick: () => {
|
|
2905
|
+
onNewConversation?.();
|
|
2906
|
+
setShowHistory(false);
|
|
2907
|
+
}, children: [
|
|
2908
|
+
/* @__PURE__ */ jsx23("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", children: /* @__PURE__ */ jsx23("path", { d: "M12 5V19M5 12H19", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" }) }),
|
|
2909
|
+
/* @__PURE__ */ jsx23("span", { children: newConversationText })
|
|
2139
2910
|
] }),
|
|
2140
|
-
|
|
2141
|
-
"
|
|
2911
|
+
conversations.length === 0 ? /* @__PURE__ */ jsx23("div", { className: "empty-history", children: /* @__PURE__ */ jsx23("p", { children: emptyText }) }) : conversations.map((conv) => /* @__PURE__ */ jsxs17(
|
|
2912
|
+
"div",
|
|
2142
2913
|
{
|
|
2143
|
-
className: `
|
|
2144
|
-
onClick: () =>
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
/* @__PURE__ */
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
/* @__PURE__ */
|
|
2153
|
-
|
|
2914
|
+
className: `history-item ${currentConversationId === conv.id ? "active" : ""}`,
|
|
2915
|
+
onClick: () => {
|
|
2916
|
+
onSwitchConversation?.(conv.id);
|
|
2917
|
+
setShowHistory(false);
|
|
2918
|
+
},
|
|
2919
|
+
children: [
|
|
2920
|
+
/* @__PURE__ */ jsxs17("div", { className: "history-item-content", children: [
|
|
2921
|
+
/* @__PURE__ */ jsx23("div", { className: "history-item-title", children: /* @__PURE__ */ jsx23("span", { children: conv.title }) }),
|
|
2922
|
+
/* @__PURE__ */ jsx23("div", { className: "history-item-preview", children: conv.lastMessage }),
|
|
2923
|
+
/* @__PURE__ */ jsxs17("div", { className: "history-item-meta", children: [
|
|
2924
|
+
/* @__PURE__ */ jsx23("span", { children: new Date(conv.timestamp).toLocaleDateString() }),
|
|
2925
|
+
/* @__PURE__ */ jsxs17("span", { children: [
|
|
2926
|
+
conv.messages.length,
|
|
2927
|
+
"\u6761\u6D88\u606F"
|
|
2928
|
+
] })
|
|
2929
|
+
] })
|
|
2154
2930
|
] }),
|
|
2155
|
-
/* @__PURE__ */
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
|
|
2931
|
+
/* @__PURE__ */ jsx23("div", { className: "history-item-actions", children: /* @__PURE__ */ jsx23(
|
|
2932
|
+
"button",
|
|
2933
|
+
{
|
|
2934
|
+
className: "delete-conversation-btn",
|
|
2935
|
+
onClick: (e) => {
|
|
2936
|
+
e.stopPropagation();
|
|
2937
|
+
handleDeleteConversation(conv.id);
|
|
2938
|
+
},
|
|
2939
|
+
title: "\u5220\u9664\u5BF9\u8BDD",
|
|
2940
|
+
children: /* @__PURE__ */ jsxs17("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", children: [
|
|
2941
|
+
/* @__PURE__ */ jsx23("path", { d: "M3 6H21", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" }),
|
|
2942
|
+
/* @__PURE__ */ jsx23("path", { d: "M19 6V20C19 21.1 18.1 22 17 22H7C5.9 22 5 21.1 5 20V6", stroke: "currentColor", strokeWidth: "2" }),
|
|
2943
|
+
/* @__PURE__ */ jsx23("path", { d: "M8 6V4C8 2.9 8.9 2 10 2H14C15.1 2 16 2.9 16 4V6", stroke: "currentColor", strokeWidth: "2" })
|
|
2944
|
+
] })
|
|
2945
|
+
}
|
|
2946
|
+
) })
|
|
2947
|
+
]
|
|
2948
|
+
},
|
|
2949
|
+
conv.id
|
|
2950
|
+
))
|
|
2159
2951
|
] })
|
|
2160
|
-
] }
|
|
2952
|
+
] })
|
|
2953
|
+
] });
|
|
2954
|
+
};
|
|
2955
|
+
|
|
2956
|
+
// src/components/Relation/Relation.tsx
|
|
2957
|
+
import { useCallback as useCallback12, useEffect as useEffect13 } from "react";
|
|
2958
|
+
import {
|
|
2959
|
+
ReactFlow,
|
|
2960
|
+
useNodesState,
|
|
2961
|
+
useEdgesState,
|
|
2962
|
+
addEdge,
|
|
2963
|
+
Handle,
|
|
2964
|
+
Position,
|
|
2965
|
+
MarkerType,
|
|
2966
|
+
ConnectionLineType,
|
|
2967
|
+
useReactFlow,
|
|
2968
|
+
ReactFlowProvider
|
|
2969
|
+
} from "@xyflow/react";
|
|
2970
|
+
import "@xyflow/react/dist/style.css";
|
|
2971
|
+
import { jsx as jsx24, jsxs as jsxs18 } from "react/jsx-runtime";
|
|
2972
|
+
var getIconEmoji = (iconType) => {
|
|
2973
|
+
const map = {
|
|
2974
|
+
war: "\u2694\uFE0F",
|
|
2975
|
+
target: "\u{1F3AF}",
|
|
2976
|
+
star: "\u2B50",
|
|
2977
|
+
award: "\u{1F3C6}",
|
|
2978
|
+
flag: "\u{1F6A9}",
|
|
2979
|
+
default: "\u{1F465}",
|
|
2980
|
+
user: "\u{1F464}",
|
|
2981
|
+
shield: "\u{1F6E1}\uFE0F"
|
|
2161
2982
|
};
|
|
2162
|
-
return
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
|
|
2167
|
-
|
|
2168
|
-
|
|
2169
|
-
|
|
2170
|
-
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
|
|
2983
|
+
return map[iconType || "default"] || "\u{1F465}";
|
|
2984
|
+
};
|
|
2985
|
+
var getStatusColor = (status) => {
|
|
2986
|
+
const map = {
|
|
2987
|
+
active: "#10b981",
|
|
2988
|
+
inactive: "#6b7280",
|
|
2989
|
+
warning: "#f59e0b"
|
|
2990
|
+
};
|
|
2991
|
+
return map[status || "active"];
|
|
2992
|
+
};
|
|
2993
|
+
var CustomNode = ({ data, theme = "light" }) => {
|
|
2994
|
+
const isDark = theme === "dark";
|
|
2995
|
+
const nodeStyle = {
|
|
2996
|
+
background: isDark ? "linear-gradient(135deg, #1e293b 0%, #0f172a 100%)" : "linear-gradient(135deg, #ffffff 0%, #f8fafc 100%)",
|
|
2997
|
+
border: `2px solid ${isDark ? "rgba(255, 255, 255, 0.12)" : "rgba(0, 0, 0, 0.08)"}`,
|
|
2998
|
+
borderRadius: "14px",
|
|
2999
|
+
boxShadow: isDark ? "0 8px 20px rgba(0, 0, 0, 0.25)" : "0 4px 12px rgba(0, 0, 0, 0.08)",
|
|
3000
|
+
minWidth: "240px",
|
|
3001
|
+
cursor: "pointer",
|
|
3002
|
+
transition: "all 0.3s cubic-bezier(0.4, 0, 0.2, 1)"
|
|
3003
|
+
};
|
|
3004
|
+
const iconStyle = {
|
|
3005
|
+
width: "52px",
|
|
3006
|
+
height: "52px",
|
|
3007
|
+
borderRadius: "14px",
|
|
3008
|
+
display: "flex",
|
|
3009
|
+
alignItems: "center",
|
|
3010
|
+
justifyContent: "center",
|
|
3011
|
+
background: isDark ? "rgba(255, 255, 255, 0.08)" : "rgba(0, 0, 0, 0.04)",
|
|
3012
|
+
transition: "all 0.3s ease"
|
|
3013
|
+
};
|
|
3014
|
+
const titleStyle = {
|
|
3015
|
+
fontWeight: 700,
|
|
3016
|
+
fontSize: "15px",
|
|
3017
|
+
marginBottom: "4px",
|
|
3018
|
+
whiteSpace: "nowrap",
|
|
3019
|
+
overflow: "hidden",
|
|
3020
|
+
textOverflow: "ellipsis",
|
|
3021
|
+
color: isDark ? "#ffffff" : "#1e293b"
|
|
3022
|
+
};
|
|
3023
|
+
const subtitleStyle = {
|
|
3024
|
+
fontSize: "11px",
|
|
3025
|
+
fontWeight: 500,
|
|
3026
|
+
marginBottom: "2px",
|
|
3027
|
+
color: isDark ? "rgba(255, 255, 255, 0.7)" : "#64748b"
|
|
3028
|
+
};
|
|
3029
|
+
const descriptionStyle = {
|
|
3030
|
+
fontSize: "10px",
|
|
3031
|
+
marginTop: "6px",
|
|
3032
|
+
whiteSpace: "nowrap",
|
|
3033
|
+
overflow: "hidden",
|
|
3034
|
+
textOverflow: "ellipsis",
|
|
3035
|
+
color: isDark ? "rgba(255, 255, 255, 0.45)" : "#94a3b8"
|
|
3036
|
+
};
|
|
3037
|
+
return /* @__PURE__ */ jsxs18("div", { style: nodeStyle, children: [
|
|
3038
|
+
/* @__PURE__ */ jsx24(Handle, { type: "target", position: Position.Top, isConnectable: true }),
|
|
3039
|
+
/* @__PURE__ */ jsxs18("div", { style: { display: "flex", alignItems: "center", gap: "14px", padding: "14px 18px" }, children: [
|
|
3040
|
+
/* @__PURE__ */ jsxs18("div", { style: { position: "relative", flexShrink: 0 }, children: [
|
|
3041
|
+
/* @__PURE__ */ jsx24("div", { style: iconStyle, children: data.imageUrl ? /* @__PURE__ */ jsx24("img", { src: data.imageUrl, alt: data.title, style: { width: "100%", height: "100%", objectFit: "cover", borderRadius: "12px" } }) : /* @__PURE__ */ jsx24("span", { style: { fontSize: "28px" }, children: getIconEmoji(data.iconType) }) }),
|
|
3042
|
+
data.status && /* @__PURE__ */ jsx24("div", { style: {
|
|
3043
|
+
position: "absolute",
|
|
3044
|
+
bottom: "-2px",
|
|
3045
|
+
right: "-2px",
|
|
3046
|
+
width: "12px",
|
|
3047
|
+
height: "12px",
|
|
3048
|
+
borderRadius: "50%",
|
|
3049
|
+
border: `2px solid ${isDark ? "#1e293b" : "#ffffff"}`,
|
|
3050
|
+
backgroundColor: getStatusColor(data.status)
|
|
3051
|
+
} })
|
|
2182
3052
|
] }),
|
|
2183
|
-
/* @__PURE__ */
|
|
3053
|
+
/* @__PURE__ */ jsxs18("div", { style: { flex: 1, minWidth: 0 }, children: [
|
|
3054
|
+
/* @__PURE__ */ jsx24("div", { style: titleStyle, children: data.title }),
|
|
3055
|
+
/* @__PURE__ */ jsx24("div", { style: subtitleStyle, children: data.subtitle }),
|
|
3056
|
+
data.description && /* @__PURE__ */ jsx24("div", { style: descriptionStyle, children: data.description })
|
|
3057
|
+
] })
|
|
2184
3058
|
] }),
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
3059
|
+
/* @__PURE__ */ jsx24(Handle, { type: "source", position: Position.Bottom, isConnectable: true })
|
|
3060
|
+
] });
|
|
3061
|
+
};
|
|
3062
|
+
var nodeTypes = {
|
|
3063
|
+
custom: CustomNode
|
|
3064
|
+
};
|
|
3065
|
+
var RelationContent = ({
|
|
3066
|
+
nodes: propNodes,
|
|
3067
|
+
edges: propEdges,
|
|
3068
|
+
onNodeClick,
|
|
3069
|
+
onNodeDoubleClick,
|
|
3070
|
+
onEdgeClick,
|
|
3071
|
+
onConnect: onConnectProp,
|
|
3072
|
+
onNodesChange: onNodesChangeProp,
|
|
3073
|
+
onEdgesChange: onEdgesChangeProp,
|
|
3074
|
+
fitView = true,
|
|
3075
|
+
fitViewOptions,
|
|
3076
|
+
className = "",
|
|
3077
|
+
style = {},
|
|
3078
|
+
height = "70vh",
|
|
3079
|
+
width = "100%",
|
|
3080
|
+
defaultViewport = { x: 0, y: 0, zoom: 1 },
|
|
3081
|
+
minZoom = 0.5,
|
|
3082
|
+
maxZoom = 2,
|
|
3083
|
+
snapToGrid = false,
|
|
3084
|
+
snapGrid = [15, 15],
|
|
3085
|
+
enableEdgeCreation = true,
|
|
3086
|
+
enableNodeDrag = true,
|
|
3087
|
+
onNodeContextMenu,
|
|
3088
|
+
theme: propTheme = "light"
|
|
3089
|
+
}) => {
|
|
3090
|
+
const [nodes, , onNodesChange] = useNodesState(propNodes || []);
|
|
3091
|
+
const [edges, setEdges, onEdgesChange] = useEdgesState(propEdges || []);
|
|
3092
|
+
const { fitView: fitViewFn } = useReactFlow();
|
|
3093
|
+
const theme = propTheme;
|
|
3094
|
+
const isDark = theme === "dark";
|
|
3095
|
+
const bgColor = isDark ? "#0f172a" : "#f5f7fa";
|
|
3096
|
+
useEffect13(() => {
|
|
3097
|
+
if (fitView && fitViewFn && (propNodes?.length || 0) > 0) {
|
|
3098
|
+
setTimeout(() => {
|
|
3099
|
+
fitViewFn({ duration: 300, ...fitViewOptions }).catch((error) => {
|
|
3100
|
+
console.warn("Fit view failed:", error);
|
|
3101
|
+
});
|
|
3102
|
+
}, 100);
|
|
3103
|
+
}
|
|
3104
|
+
}, [fitView, fitViewFn, fitViewOptions, propNodes]);
|
|
3105
|
+
const handleNodesChange = useCallback12(
|
|
3106
|
+
(changes) => {
|
|
3107
|
+
onNodesChange(changes);
|
|
3108
|
+
if (onNodesChangeProp) {
|
|
3109
|
+
onNodesChangeProp(nodes);
|
|
3110
|
+
}
|
|
3111
|
+
},
|
|
3112
|
+
[onNodesChange, onNodesChangeProp, nodes]
|
|
3113
|
+
);
|
|
3114
|
+
const handleEdgesChange = useCallback12(
|
|
3115
|
+
(changes) => {
|
|
3116
|
+
onEdgesChange(changes);
|
|
3117
|
+
if (onEdgesChangeProp) {
|
|
3118
|
+
onEdgesChangeProp(edges);
|
|
3119
|
+
}
|
|
3120
|
+
},
|
|
3121
|
+
[onEdgesChange, onEdgesChangeProp, edges]
|
|
3122
|
+
);
|
|
3123
|
+
const onConnect = useCallback12(
|
|
3124
|
+
(params) => {
|
|
3125
|
+
const newEdge = {
|
|
3126
|
+
...params,
|
|
3127
|
+
id: `edge-${Date.now()}-${Math.random()}`,
|
|
3128
|
+
type: "smoothstep",
|
|
3129
|
+
style: { stroke: "#ff6b6b", strokeWidth: 2 },
|
|
3130
|
+
markerEnd: { type: MarkerType.ArrowClosed, color: "#ff6b6b" },
|
|
3131
|
+
label: "\u65B0\u8FDE\u63A5"
|
|
3132
|
+
};
|
|
3133
|
+
setEdges((eds) => addEdge(newEdge, eds));
|
|
3134
|
+
if (onConnectProp) {
|
|
3135
|
+
onConnectProp(params);
|
|
3136
|
+
}
|
|
3137
|
+
},
|
|
3138
|
+
[setEdges, onConnectProp]
|
|
3139
|
+
);
|
|
3140
|
+
const handleNodeClick = useCallback12(
|
|
3141
|
+
(_event, node) => {
|
|
3142
|
+
if (onNodeClick && node.data) {
|
|
3143
|
+
onNodeClick(node.id, node.data, _event);
|
|
3144
|
+
}
|
|
3145
|
+
},
|
|
3146
|
+
[onNodeClick]
|
|
3147
|
+
);
|
|
3148
|
+
const handleNodeDoubleClick = useCallback12(
|
|
3149
|
+
(_event, node) => {
|
|
3150
|
+
if (onNodeDoubleClick && node.data) {
|
|
3151
|
+
onNodeDoubleClick(node.id, node.data);
|
|
3152
|
+
}
|
|
3153
|
+
},
|
|
3154
|
+
[onNodeDoubleClick]
|
|
3155
|
+
);
|
|
3156
|
+
const handleNodeContextMenu = useCallback12(
|
|
3157
|
+
(event, node) => {
|
|
3158
|
+
event.preventDefault();
|
|
3159
|
+
if (onNodeContextMenu && node.data) {
|
|
3160
|
+
onNodeContextMenu(node.id, node.data);
|
|
3161
|
+
}
|
|
3162
|
+
},
|
|
3163
|
+
[onNodeContextMenu]
|
|
3164
|
+
);
|
|
3165
|
+
const handleEdgeClick = useCallback12(
|
|
3166
|
+
(_event, edge) => {
|
|
3167
|
+
if (onEdgeClick) {
|
|
3168
|
+
onEdgeClick(edge.id, edge.data);
|
|
3169
|
+
}
|
|
3170
|
+
},
|
|
3171
|
+
[onEdgeClick]
|
|
3172
|
+
);
|
|
3173
|
+
const nodesWithTheme = nodes.map((node) => ({
|
|
3174
|
+
...node,
|
|
3175
|
+
data: { ...node.data, theme }
|
|
3176
|
+
}));
|
|
3177
|
+
const globalStyle = `
|
|
3178
|
+
.react-flow__background,
|
|
3179
|
+
.react-flow__pane,
|
|
3180
|
+
.react-flow__renderer,
|
|
3181
|
+
.react-flow__viewport {
|
|
3182
|
+
background-color: ${bgColor} !important;
|
|
3183
|
+
}
|
|
3184
|
+
`;
|
|
3185
|
+
return /* @__PURE__ */ jsxs18(
|
|
3186
|
+
"div",
|
|
3187
|
+
{
|
|
3188
|
+
className: `relation-container ${className}`,
|
|
3189
|
+
style: {
|
|
3190
|
+
width,
|
|
3191
|
+
height,
|
|
3192
|
+
...style,
|
|
3193
|
+
position: "relative",
|
|
3194
|
+
overflow: "hidden",
|
|
3195
|
+
backgroundColor: bgColor
|
|
3196
|
+
},
|
|
3197
|
+
children: [
|
|
3198
|
+
/* @__PURE__ */ jsx24("style", { children: globalStyle }),
|
|
3199
|
+
/* @__PURE__ */ jsx24(
|
|
3200
|
+
ReactFlow,
|
|
2208
3201
|
{
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
|
|
3202
|
+
nodes: nodesWithTheme,
|
|
3203
|
+
edges,
|
|
3204
|
+
onNodesChange: handleNodesChange,
|
|
3205
|
+
onEdgesChange: handleEdgesChange,
|
|
3206
|
+
onConnect: enableEdgeCreation ? onConnect : void 0,
|
|
3207
|
+
onNodeClick: handleNodeClick,
|
|
3208
|
+
onNodeDoubleClick: handleNodeDoubleClick,
|
|
3209
|
+
onNodeContextMenu: handleNodeContextMenu,
|
|
3210
|
+
onEdgeClick: handleEdgeClick,
|
|
3211
|
+
nodeTypes,
|
|
3212
|
+
fitView: false,
|
|
3213
|
+
fitViewOptions,
|
|
3214
|
+
defaultViewport,
|
|
3215
|
+
minZoom,
|
|
3216
|
+
maxZoom,
|
|
3217
|
+
snapToGrid,
|
|
3218
|
+
snapGrid,
|
|
3219
|
+
nodesDraggable: enableNodeDrag,
|
|
3220
|
+
nodesConnectable: enableEdgeCreation,
|
|
3221
|
+
connectionLineType: ConnectionLineType.SmoothStep,
|
|
3222
|
+
connectionLineStyle: { stroke: "#ff6b6b", strokeWidth: 2 },
|
|
3223
|
+
attributionPosition: "bottom-right",
|
|
3224
|
+
zoomOnScroll: true,
|
|
3225
|
+
zoomOnPinch: true,
|
|
3226
|
+
zoomOnDoubleClick: false,
|
|
3227
|
+
panOnScroll: false,
|
|
3228
|
+
panOnDrag: true,
|
|
3229
|
+
proOptions: { hideAttribution: true }
|
|
2213
3230
|
}
|
|
2214
3231
|
)
|
|
2215
|
-
]
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
maxInputLength
|
|
2222
|
-
] })
|
|
2223
|
-
] })
|
|
2224
|
-
] })
|
|
2225
|
-
] });
|
|
3232
|
+
]
|
|
3233
|
+
}
|
|
3234
|
+
);
|
|
3235
|
+
};
|
|
3236
|
+
var Relation = (props) => {
|
|
3237
|
+
return /* @__PURE__ */ jsx24(ReactFlowProvider, { children: /* @__PURE__ */ jsx24(RelationContent, { ...props }) });
|
|
2226
3238
|
};
|
|
2227
3239
|
export {
|
|
2228
3240
|
AlertProvider,
|
|
@@ -2233,16 +3245,21 @@ export {
|
|
|
2233
3245
|
Card,
|
|
2234
3246
|
Chat,
|
|
2235
3247
|
CheckButton,
|
|
3248
|
+
ContextMenuProvider,
|
|
2236
3249
|
DeleteDialog,
|
|
2237
3250
|
Input,
|
|
2238
3251
|
ListGroup,
|
|
2239
3252
|
ListGroupItem,
|
|
3253
|
+
MarkdownEditor,
|
|
2240
3254
|
OrphanDialog,
|
|
3255
|
+
Relation,
|
|
2241
3256
|
RollingBox,
|
|
2242
3257
|
Select,
|
|
2243
3258
|
SideBar,
|
|
2244
3259
|
Slider,
|
|
2245
|
-
|
|
3260
|
+
SmartMessage,
|
|
3261
|
+
TabBar,
|
|
3262
|
+
TagItem,
|
|
2246
3263
|
ThemeProvider,
|
|
2247
3264
|
Tree,
|
|
2248
3265
|
VirtualList,
|
|
@@ -2251,5 +3268,6 @@ export {
|
|
|
2251
3268
|
isDescendantOf,
|
|
2252
3269
|
lazyLoad,
|
|
2253
3270
|
useAlert,
|
|
3271
|
+
useContextMenu,
|
|
2254
3272
|
useTheme
|
|
2255
3273
|
};
|