@enableai-base/grid-layout 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,1772 @@
1
+ /**
2
+ * @enableai-base/grid-layout v1.0.0
3
+ * (c) 2024-present Enableai
4
+ * @license Proprietary
5
+ **/
6
+ import React, { useState, useRef, useCallback, useEffect, useMemo, forwardRef, useLayoutEffect } from 'react';
7
+ import { Spin, Button, Flex, Dropdown, Typography, Popover, Avatar, DatePicker, Input, Skeleton, Drawer, Tabs, Tooltip, Select as Select$1, Space, Divider, Tag, Form, Menu } from 'antd';
8
+ import { useMount, createContextWithProvider, useDebounce } from '@enableai-sdk-common/react-hooks';
9
+ import { Base64 } from 'js-base64';
10
+ import { DeleteOutlined, MoreOutlined, HolderOutlined, CaretDownOutlined, UpOutlined, DownOutlined, CloseOutlined, LeftOutlined, RightOutlined, LoadingOutlined, SearchOutlined, QuestionCircleOutlined, PlusOutlined } from '@ant-design/icons';
11
+ import { UploadList, IconFont, AvatarPopoverContent, LinkEdit, Select } from '@enableai-base/ui';
12
+ import { ProFormText, ProForm, ProFormItem, ProFormDependency, ProFormList } from '@ant-design/pro-components';
13
+ import { TOOL_ICON, getColorByStr, parseDateFormat, inferShowTimeFromFormat, TZ_LABEL, formatValue, formatDuration, formatTime } from '@enableai-base/core';
14
+ import dayjs from 'dayjs';
15
+ import ReactMonacoEditor, { loader } from '@monaco-editor/react';
16
+ import { BaseModal, DragDiv } from '@enableai-sdk-ui/base-modal';
17
+ import { isMatch, debounce, isEqual, omitBy, isUndefined } from 'lodash';
18
+ import ReactGridLayout, { WidthProvider } from 'react-grid-layout';
19
+
20
+ class ObjectURLManager {
21
+ constructor() {
22
+ this.store = [];
23
+ }
24
+ /**
25
+ * 为指定的 Blob 或 MediaSource 创建对象 URL 并存储。
26
+ *
27
+ * @param {Blob | MediaSource} obj - 需要创建对象 URL 的数据对象。
28
+ * @returns {string} 创建的对象 URL。
29
+ */
30
+ create(obj) {
31
+ const src = window.URL.createObjectURL(obj);
32
+ this.store.push(src);
33
+ return src;
34
+ }
35
+ /**
36
+ * 撤销所有已创建的对象 URL,释放内存。
37
+ */
38
+ destroy() {
39
+ this.store.forEach((url) => window.URL.revokeObjectURL(url));
40
+ this.store = [];
41
+ }
42
+ }
43
+
44
+ const AttachmentPreview = ({ path, s3PathParser }) => {
45
+ const mode = "light";
46
+ const [iframeSrc, setIframeSrc] = useState("");
47
+ useMount(() => {
48
+ s3PathParser(path).then((realPath) => {
49
+ setIframeSrc(
50
+ window.location.origin + "/api/proxy/kkfileview/onlinePreview?url=" + encodeURIComponent(Base64.encode(realPath))
51
+ );
52
+ });
53
+ });
54
+ if (!iframeSrc) return /* @__PURE__ */ React.createElement(Spin, null);
55
+ return /* @__PURE__ */ React.createElement(AttachmentPreviewInner, { iframeSrc, mode });
56
+ };
57
+ const styleId = "iframe-style-id";
58
+ const AttachmentPreviewInner = ({ iframeSrc, mode }) => {
59
+ const iframeRef = useRef(null);
60
+ const getStyleElement = useCallback(() => {
61
+ const iframe = iframeRef.current;
62
+ if (!iframe) return;
63
+ const iframeDocument = iframe.contentDocument || iframe.contentWindow?.document;
64
+ if (!iframeDocument) return;
65
+ let style = iframeDocument.getElementById(styleId);
66
+ if (!style) {
67
+ style = iframeDocument.createElement("style");
68
+ style.id = styleId;
69
+ iframeDocument.head.appendChild(style);
70
+ }
71
+ return style;
72
+ }, []);
73
+ const applyStyles = useCallback(() => {
74
+ const isDark = mode === "dark";
75
+ const style = getStyleElement();
76
+ if (!style) return;
77
+ const iframe = iframeRef.current;
78
+ if (!iframe) return;
79
+ const iframeDocument = iframe.contentDocument || iframe.contentWindow?.document;
80
+ if (!iframeDocument) return;
81
+ const textData = iframeDocument.getElementById("textData");
82
+ if (textData) {
83
+ style.innerHTML = `
84
+ .panel-heading {
85
+ display: none;
86
+ }
87
+ body, .container, .panel-heading, .panel-body, .divContent {
88
+ background-color: ${isDark ? "#1F1F1F" : "#F5F5F5"} !important;
89
+ color: ${isDark ? "rgba(255, 255, 255, 0.85)" : "rgba(0, 0, 0, 0.88)"} !important;
90
+ }
91
+ `;
92
+ }
93
+ }, [mode, getStyleElement]);
94
+ useEffect(() => {
95
+ const iframe = iframeRef.current;
96
+ if (!iframe) return;
97
+ iframe.addEventListener("load", applyStyles);
98
+ return () => iframe.removeEventListener("load", applyStyles);
99
+ }, [iframeSrc]);
100
+ useEffect(() => {
101
+ const iframe = iframeRef.current;
102
+ if (!iframe) return;
103
+ applyStyles();
104
+ }, [applyStyles]);
105
+ if (!iframeSrc) return null;
106
+ return /* @__PURE__ */ React.createElement("iframe", { ref: iframeRef, src: iframeSrc, style: { width: "100%", height: "100%", border: "none" } });
107
+ };
108
+
109
+ const AttachmentEditor = ({
110
+ value,
111
+ s3PathParser,
112
+ uploadFunction,
113
+ onChange,
114
+ onPreview,
115
+ disabled
116
+ }) => {
117
+ const file = useMemo(() => value?.at(0), [value]);
118
+ if (!file)
119
+ return /* @__PURE__ */ React.createElement(
120
+ UploadList,
121
+ {
122
+ disabled,
123
+ uploadFunction,
124
+ maxCount: 1,
125
+ onChange,
126
+ onPreview
127
+ }
128
+ );
129
+ else {
130
+ if (!s3PathParser) throw new Error("[AttachmentEditor] s3PathParser is required");
131
+ return /* @__PURE__ */ React.createElement("div", { style: { width: "100%", height: "100%", position: "relative" } }, /* @__PURE__ */ React.createElement(
132
+ Button,
133
+ {
134
+ color: "default",
135
+ variant: "filled",
136
+ shape: "circle",
137
+ disabled,
138
+ style: { position: "absolute", right: 10, top: 10, color: "var(--ant-color-text-secondary)" },
139
+ size: "small",
140
+ icon: /* @__PURE__ */ React.createElement(DeleteOutlined, null),
141
+ onClick: () => onChange?.([])
142
+ }
143
+ ), /* @__PURE__ */ React.createElement(AttachmentPreview, { path: file.path, s3PathParser }));
144
+ }
145
+ };
146
+
147
+ function findAvailablePosition(layout, newW, newH, cols = 12) {
148
+ if (layout.length === 0) return { x: 0, y: 0 };
149
+ const maxY = Math.max(...layout.map((item) => item.y + item.h), 0);
150
+ for (let y = 0; y <= maxY + 10; y++) {
151
+ for (let x = 0; x <= cols - newW; x++) {
152
+ const newItem = { x, y, w: newW, h: newH };
153
+ const overlap = layout.some((item) => isOverlap(item, newItem));
154
+ if (!overlap) {
155
+ return { x, y };
156
+ }
157
+ }
158
+ }
159
+ return { x: 0, y: maxY + 1 };
160
+ }
161
+ function isOverlap(a, b) {
162
+ return !(a.x + a.w <= b.x || // a 在 b 左边
163
+ b.x + b.w <= a.x || // b 在 a 左边
164
+ a.y + a.h <= b.y || // a 在 b 上方
165
+ b.y + b.h <= a.y);
166
+ }
167
+ function generateCrossDashedBase64(width, height, x, y, padding = 1) {
168
+ const lineColor = "rgba(160, 160, 160, 0.25)";
169
+ const lineWidth = 2;
170
+ const dashPattern = [10, 5];
171
+ const canvas = document.createElement("canvas");
172
+ canvas.width = width;
173
+ canvas.height = height;
174
+ const ctx = canvas.getContext("2d");
175
+ if (!ctx) return;
176
+ ctx.strokeStyle = lineColor;
177
+ ctx.lineWidth = lineWidth;
178
+ ctx.setLineDash(dashPattern);
179
+ const start = padding;
180
+ const endX = x - padding;
181
+ const endY = y - padding;
182
+ ctx.beginPath();
183
+ ctx.moveTo(start, start);
184
+ ctx.lineTo(endX, start);
185
+ ctx.moveTo(endX, start);
186
+ ctx.lineTo(endX, endY);
187
+ ctx.moveTo(endX, endY);
188
+ ctx.lineTo(start, endY);
189
+ ctx.moveTo(start, endY);
190
+ ctx.lineTo(start, start);
191
+ ctx.stroke();
192
+ const base64 = canvas.toDataURL("image/png");
193
+ return base64;
194
+ }
195
+
196
+ const Dashboard = ({
197
+ dashboardData,
198
+ cols = 12,
199
+ gapX = 10,
200
+ gapY = 10,
201
+ rowHeight = 50,
202
+ children,
203
+ onLayoutChange,
204
+ onDrop,
205
+ droppingItem,
206
+ mode = "editable"
207
+ }) => {
208
+ const CustomReactGridLayout = useMemo(() => WidthProvider(ReactGridLayout), []);
209
+ const innerOnLayoutChange = useCallback(
210
+ (layout) => {
211
+ const layoutsBefore = dashboardData ?? [];
212
+ if (layout.length !== layoutsBefore.length) return;
213
+ const isStatic = layout.every((item, index) => isMatch(item, layoutsBefore[index]));
214
+ if (isStatic) return;
215
+ onLayoutChange?.(layout);
216
+ },
217
+ [dashboardData, onLayoutChange]
218
+ );
219
+ const gridImgRef = useRef({
220
+ width: 0,
221
+ height: 0,
222
+ gapX,
223
+ gapY,
224
+ url: ""
225
+ });
226
+ const containerRef = useRef(null);
227
+ const innerRef = useRef(null);
228
+ const [showScroll, setShowScroll] = useState(false);
229
+ const [resizing, setResizing] = useState(false);
230
+ const onDragStart = () => {
231
+ const gridImg = gridImgRef.current;
232
+ const widthProvider = innerRef.current;
233
+ const elementRef = widthProvider?.elementRef?.current;
234
+ if (!elementRef) return;
235
+ setResizing(true);
236
+ setShowScroll(true);
237
+ const width = (elementRef.clientWidth - gapX) / cols;
238
+ const height = rowHeight + gapY;
239
+ const gX = width - gapX;
240
+ const gY = rowHeight;
241
+ let imageUrl = gridImg.url;
242
+ if (gridImg.width !== width || gridImg.height !== height || gridImg.gapX !== gX || gridImg.gapY !== gY) {
243
+ const imageBase64 = generateCrossDashedBase64(width, height, gX, gY);
244
+ if (!imageBase64) return;
245
+ imageUrl = imageBase64;
246
+ gridImgRef.current = { width, height, gapX: gX, gapY: gY, url: imageBase64 };
247
+ }
248
+ elementRef.style.backgroundImage = `url(${imageUrl})`;
249
+ elementRef.style.backgroundPosition = `${gapX}px ${gapY}px`;
250
+ elementRef.style.backgroundSize = `${width}px ${height}px`;
251
+ };
252
+ const onDragStop = () => {
253
+ const widthProvider = innerRef.current;
254
+ const elementRef = widthProvider?.elementRef?.current;
255
+ const container = containerRef.current;
256
+ if (!elementRef || !container) return;
257
+ setResizing(false);
258
+ elementRef.style.backgroundImage = "";
259
+ if (container.clientHeight < elementRef.clientHeight - 40) {
260
+ setShowScroll(true);
261
+ } else {
262
+ setShowScroll(false);
263
+ }
264
+ };
265
+ return /* @__PURE__ */ React.createElement("div", { className: `dashboard-container ${showScroll ? "show-scroll" : ""}`, ref: containerRef }, /* @__PURE__ */ React.createElement(
266
+ CustomReactGridLayout,
267
+ {
268
+ isDroppable: true,
269
+ onDrop: (...props) => {
270
+ const widthProvider = innerRef.current;
271
+ const elementRef = widthProvider?.elementRef?.current;
272
+ elementRef.style.backgroundImage = "";
273
+ onDrop?.(...props);
274
+ },
275
+ droppingItem,
276
+ isDraggable: mode === "editable",
277
+ ref: innerRef,
278
+ className: `dashboard-layout ${resizing ? "dashboard-item-resizing" : ""}`,
279
+ cols,
280
+ rowHeight,
281
+ margin: [gapX, gapY],
282
+ containerPadding: [gapX, gapY],
283
+ layout: dashboardData,
284
+ useCSSTransforms: true,
285
+ allowOverlap: false,
286
+ onDragStart,
287
+ onDragStop,
288
+ onResizeStart: onDragStart,
289
+ onResizeStop: onDragStop,
290
+ resizeHandle: mode === "editable" && /* @__PURE__ */ React.createElement("div", { className: "resize-handle" }),
291
+ style: { minHeight: "100%" },
292
+ onLayoutChange: innerOnLayoutChange
293
+ },
294
+ children
295
+ ));
296
+ };
297
+
298
+ const DashboardItem = ({
299
+ id,
300
+ className = "",
301
+ isActive = false,
302
+ title,
303
+ menuItems = [],
304
+ children,
305
+ style,
306
+ onSelect,
307
+ onMouseDown,
308
+ onMouseUp,
309
+ onMenuClick,
310
+ mode = "editable"
311
+ }, ref) => {
312
+ const hasMenu = !!menuItems?.length;
313
+ const titleInfo = useMemo(() => {
314
+ if (typeof title === "string") {
315
+ return /* @__PURE__ */ React.createElement("span", { className: "title-text" }, title);
316
+ } else {
317
+ return title;
318
+ }
319
+ }, [title]);
320
+ return /* @__PURE__ */ React.createElement(
321
+ "div",
322
+ {
323
+ ref,
324
+ className: `dashboard-item ${isActive ? "isActive" : ""} ${className} ${mode}`,
325
+ style,
326
+ onMouseDown: (e) => onSelect?.(e, id)
327
+ },
328
+ /* @__PURE__ */ React.createElement(Flex, { className: `item-head`, onMouseDown, onMouseUp }, /* @__PURE__ */ React.createElement(Flex, { className: "head-inner", align: "center", justify: "space-between" }, /* @__PURE__ */ React.createElement(Flex, { className: "head-title", align: "center" }, titleInfo), hasMenu && mode === "editable" && /* @__PURE__ */ React.createElement(Flex, { className: "head-menu" }, /* @__PURE__ */ React.createElement(
329
+ Dropdown,
330
+ {
331
+ trigger: ["click"],
332
+ placement: "bottomRight",
333
+ popupRender: (menu) => /* @__PURE__ */ React.createElement("div", { onMouseDown: (e) => e.stopPropagation(), onPointerDown: (e) => e.stopPropagation() }, menu),
334
+ menu: {
335
+ items: menuItems,
336
+ onClick: (info) => {
337
+ if (onMenuClick) onMenuClick(info, id);
338
+ }
339
+ }
340
+ },
341
+ /* @__PURE__ */ React.createElement(
342
+ Button,
343
+ {
344
+ type: "default",
345
+ size: "small",
346
+ icon: /* @__PURE__ */ React.createElement(MoreOutlined, null),
347
+ onMouseDown: (e) => {
348
+ e.stopPropagation();
349
+ if (onSelect) onSelect(e, id);
350
+ }
351
+ }
352
+ )
353
+ ))), mode === "editable" && /* @__PURE__ */ React.createElement("div", { className: `drag-icon` }, /* @__PURE__ */ React.createElement(HolderOutlined, { rotate: 90 }))),
354
+ /* @__PURE__ */ React.createElement(Flex, { className: "item-body", align: "center", justify: "center" }, children)
355
+ );
356
+ };
357
+ var DashboardItem$1 = forwardRef(DashboardItem);
358
+
359
+ const { createUseContext, Provider: DetailsDrawerProvider } = createContextWithProvider(
360
+ ({ uploadFunction, columnsData, onClick, onPreview }) => {
361
+ return {
362
+ uploadFunction,
363
+ columnsData,
364
+ onClick,
365
+ onPreview
366
+ };
367
+ }
368
+ );
369
+ const useDetailsDrawerContext = createUseContext();
370
+
371
+ const RowLabel = ({ columnData }) => {
372
+ const { onClick: onClickTab } = useDetailsDrawerContext();
373
+ return /* @__PURE__ */ React.createElement(React.Fragment, null, onClickTab ? /* @__PURE__ */ React.createElement("button", { className: "text-btn-container title-container", onClick: (e) => onClickTab(e, columnData.field) }, /* @__PURE__ */ React.createElement("div", { className: "icon-wrapper" }, /* @__PURE__ */ React.createElement(IconFont, { name: TOOL_ICON[columnData.tool], size: 16, color: "#000" })), /* @__PURE__ */ React.createElement(Typography.Text, { className: "text-content" }, columnData.title || ""), /* @__PURE__ */ React.createElement("div", { className: "arrow-icon" }, /* @__PURE__ */ React.createElement(CaretDownOutlined, null))) : /* @__PURE__ */ React.createElement(Flex, { align: "center", gap: 4, className: "title-container" }, /* @__PURE__ */ React.createElement("div", { className: "icon-wrapper" }, /* @__PURE__ */ React.createElement(IconFont, { name: TOOL_ICON[columnData.tool], size: 16, color: "#000" })), /* @__PURE__ */ React.createElement(Typography.Text, { className: "text-content" }, columnData.title || "")));
374
+ };
375
+
376
+ const NormalAvatar = (props) => {
377
+ const { id, name, avatar, request } = props;
378
+ const backgroundColor = useMemo(() => name === "\u65E0" ? "#D9D9D9" : getColorByStr(name?.slice(-2) || ""), [name]);
379
+ return /* @__PURE__ */ React.createElement(Flex, { align: "center", gap: 8 }, /* @__PURE__ */ React.createElement(
380
+ Popover,
381
+ {
382
+ content: id !== "-2" ? /* @__PURE__ */ React.createElement(AvatarPopoverContent, { id, column: { request } }) : null,
383
+ placement: "top",
384
+ autoAdjustOverflow: false,
385
+ mouseEnterDelay: 0.3,
386
+ mouseLeaveDelay: 0.3,
387
+ arrow: { pointAtCenter: true },
388
+ getPopupContainer: () => document.body
389
+ },
390
+ /* @__PURE__ */ React.createElement(Flex, { align: "center", gap: 4 }, avatar ? /* @__PURE__ */ React.createElement(Avatar, { src: avatar }, name?.slice(-2)) : /* @__PURE__ */ React.createElement(Avatar, { style: { backgroundColor } }, name?.slice(-2)), /* @__PURE__ */ React.createElement("span", null, name))
391
+ ));
392
+ };
393
+ const FormAvatar = (props) => {
394
+ return /* @__PURE__ */ React.createElement(Flex, { align: "center", gap: 8, justify: "flex-start", wrap: "wrap" }, props.value?.map((item, index) => /* @__PURE__ */ React.createElement(NormalAvatar, { key: index, ...item, request: props.request })) || []);
395
+ };
396
+
397
+ const FormDateTimePicker = ({
398
+ value,
399
+ onChange,
400
+ format = "YYYY-MM-DD",
401
+ placeholder,
402
+ disabled
403
+ }) => {
404
+ const { baseFormat, showGmt8 } = parseDateFormat(format);
405
+ const showTimeConfig = inferShowTimeFromFormat(baseFormat);
406
+ return /* @__PURE__ */ React.createElement(
407
+ DatePicker,
408
+ {
409
+ value: value ? dayjs(value) : void 0,
410
+ format: (date) => {
411
+ if (!date) return "";
412
+ const text = date.format(baseFormat);
413
+ return showGmt8 ? `${text} (${TZ_LABEL})` : text;
414
+ },
415
+ placeholder: placeholder ?? (showGmt8 ? `${baseFormat} (${TZ_LABEL})` : baseFormat),
416
+ showTime: showTimeConfig,
417
+ disabled,
418
+ style: { width: "100%" },
419
+ onChange: (date) => {
420
+ onChange?.(date?.format(baseFormat) ?? void 0);
421
+ }
422
+ }
423
+ );
424
+ };
425
+
426
+ const FormInputNumber = ({
427
+ value,
428
+ onChange,
429
+ format = "one_decimal_decimal",
430
+ placeholder,
431
+ disabled
432
+ }) => {
433
+ const [focused, setFocused] = useState(false);
434
+ const [innerValue, setInnerValue] = useState("");
435
+ useEffect(() => {
436
+ if (!focused) {
437
+ setInnerValue(formatValue(value ?? "", format));
438
+ } else {
439
+ setInnerValue(value?.toString() ?? "");
440
+ }
441
+ }, [value, focused, format]);
442
+ const handleBlur = () => {
443
+ setFocused(false);
444
+ const num = Number(innerValue.replace(/,/g, "").replace("%", ""));
445
+ onChange?.(Number.isNaN(num) ? void 0 : num);
446
+ };
447
+ return /* @__PURE__ */ React.createElement(
448
+ Input,
449
+ {
450
+ value: innerValue,
451
+ placeholder,
452
+ disabled,
453
+ onFocus: () => {
454
+ setFocused(true);
455
+ setInnerValue(value?.toString() ?? "");
456
+ },
457
+ onBlur: handleBlur,
458
+ onChange: (e) => {
459
+ const val = e.target.value;
460
+ setInnerValue(val);
461
+ }
462
+ }
463
+ );
464
+ };
465
+
466
+ loader.config({
467
+ paths: {
468
+ // vs: '/monaco-assets/vs',
469
+ }
470
+ // 'vs/nls': { availableLanguages: { '*': 'zh-cn' } },
471
+ });
472
+ const JsonEditor = ({ value, onChange, dark, disabled }) => {
473
+ const monacoRef = useRef(null);
474
+ const monacoEditorRef = useRef(null);
475
+ const format = useCallback(() => {
476
+ monacoEditorRef.current?.getAction("editor.action.formatDocument")?.run();
477
+ }, []);
478
+ const options = useMemo(() => {
479
+ return {
480
+ lineNumbers: "off",
481
+ minimap: { enabled: false },
482
+ readOnly: !!disabled
483
+ };
484
+ }, [disabled]);
485
+ return /* @__PURE__ */ React.createElement(
486
+ ReactMonacoEditor,
487
+ {
488
+ height: "100%",
489
+ width: "100%",
490
+ defaultLanguage: "json",
491
+ value,
492
+ loading: /* @__PURE__ */ React.createElement(Spin, null),
493
+ theme: dark ? "vs-dark" : "light",
494
+ onChange,
495
+ options,
496
+ beforeMount: (monaco) => {
497
+ monacoRef.current = monaco;
498
+ monaco.languages.json.jsonDefaults.setDiagnosticsOptions({
499
+ validate: true,
500
+ schemas: [
501
+ {
502
+ uri: "test-schema",
503
+ fileMatch: ["*"],
504
+ schema: {
505
+ $schema: "http://json-schema.org/draft-07/schema#",
506
+ type: "object"
507
+ }
508
+ }
509
+ ]
510
+ });
511
+ format();
512
+ },
513
+ onMount: (monacoEditor) => {
514
+ monacoEditorRef.current = monacoEditor;
515
+ format();
516
+ }
517
+ }
518
+ );
519
+ };
520
+
521
+ const FormJsonEditor = (props) => {
522
+ return /* @__PURE__ */ React.createElement(
523
+ "div",
524
+ {
525
+ style: {
526
+ minHeight: 100,
527
+ height: 100,
528
+ maxHeight: 200
529
+ }
530
+ },
531
+ /* @__PURE__ */ React.createElement(JsonEditor, { ...props })
532
+ );
533
+ };
534
+
535
+ const FormLinkEdit = (props) => {
536
+ return /* @__PURE__ */ React.createElement(LinkEdit, { popoverStyles: { width: 440 }, placeholder: "\u8BF7\u8F93\u5165\u5185\u5BB9", ...props });
537
+ };
538
+
539
+ const FormMultiSelect = (props) => {
540
+ return /* @__PURE__ */ React.createElement(
541
+ Select,
542
+ {
543
+ width: 440,
544
+ ...props,
545
+ placeholder: props.placeholder || "\u8BF7\u9009\u62E9",
546
+ multiple: true,
547
+ tagAreaStyle: { cursor: "pointer" }
548
+ }
549
+ );
550
+ };
551
+
552
+ const FormSingleSelect = (props) => {
553
+ return /* @__PURE__ */ React.createElement(
554
+ Select,
555
+ {
556
+ width: 440,
557
+ ...props,
558
+ multiple: false,
559
+ placeholder: props.placeholder || "\u8BF7\u9009\u62E9",
560
+ tagAreaStyle: { cursor: "pointer" }
561
+ }
562
+ );
563
+ };
564
+
565
+ const FormTimeLen = (props) => {
566
+ const { value, onChange, ...fieldProps } = props;
567
+ const [inner, setInner] = useState("");
568
+ useEffect(() => {
569
+ if (value === void 0 || value === null) {
570
+ setInner("");
571
+ } else {
572
+ setInner(formatDuration(Number(value)));
573
+ }
574
+ }, [value]);
575
+ return /* @__PURE__ */ React.createElement(
576
+ Input,
577
+ {
578
+ ...fieldProps,
579
+ value: inner,
580
+ onFocus: () => {
581
+ setInner(value != null ? String(value) : "");
582
+ },
583
+ onChange: (e) => setInner(e.target.value),
584
+ onBlur: () => {
585
+ const seconds = Number(inner);
586
+ if (!isNaN(seconds)) {
587
+ onChange?.(seconds);
588
+ setInner(formatTime(seconds));
589
+ } else {
590
+ setInner(value != null ? formatTime(Number(value)) : "");
591
+ }
592
+ }
593
+ }
594
+ );
595
+ };
596
+
597
+ const FormUpload = (props) => {
598
+ const { uploadFunction, onPreview } = useDetailsDrawerContext();
599
+ const { format } = props;
600
+ let accept = "";
601
+ if (format === "image") accept = "image/*";
602
+ if (format === "document")
603
+ accept = "application/pdf,application/msword,application/vnd.ms-excel,application/vnd.ms-powerpoint,application/vnd.openxmlformats-officedocument.wordprocessingml.document,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,application/vnd.openxmlformats-officedocument.presentationml.presentation";
604
+ if (format === "video") accept = "video/*";
605
+ return /* @__PURE__ */ React.createElement(
606
+ UploadList,
607
+ {
608
+ uploadFunction,
609
+ maxCount: 1,
610
+ accept,
611
+ onPreview,
612
+ ...props
613
+ }
614
+ );
615
+ };
616
+
617
+ const FeatureRowFields = ({ currentColumn }) => {
618
+ const { Component, isPure } = useMemo(() => {
619
+ let DefaultComponent = ProFormText;
620
+ let isPure2 = false;
621
+ if (currentColumn.tool === 29) {
622
+ DefaultComponent = FormSingleSelect;
623
+ isPure2 = true;
624
+ } else if (currentColumn.tool === 30) {
625
+ DefaultComponent = FormMultiSelect;
626
+ isPure2 = true;
627
+ } else if (currentColumn.tool === 36) {
628
+ DefaultComponent = FormDateTimePicker;
629
+ isPure2 = true;
630
+ } else if (currentColumn.tool === 31) {
631
+ DefaultComponent = FormInputNumber;
632
+ isPure2 = true;
633
+ } else if (currentColumn.tool === 34) {
634
+ DefaultComponent = FormJsonEditor;
635
+ isPure2 = true;
636
+ } else if (currentColumn.tool === 28) {
637
+ DefaultComponent = ProFormText;
638
+ } else if (currentColumn.tool === 32) {
639
+ DefaultComponent = FormUpload;
640
+ isPure2 = true;
641
+ } else if (currentColumn.tool === 35) {
642
+ DefaultComponent = ProFormText;
643
+ } else if (currentColumn.tool === 33) {
644
+ DefaultComponent = FormLinkEdit;
645
+ isPure2 = true;
646
+ } else if (currentColumn.tool === 39) {
647
+ DefaultComponent = ProFormText;
648
+ } else if (currentColumn.tool === 37) {
649
+ DefaultComponent = FormTimeLen;
650
+ isPure2 = true;
651
+ } else if (currentColumn.tool === 38) {
652
+ DefaultComponent = FormAvatar;
653
+ isPure2 = true;
654
+ }
655
+ return {
656
+ Component: DefaultComponent,
657
+ isPure: isPure2
658
+ };
659
+ }, [currentColumn.tool]);
660
+ const props = {
661
+ name: currentColumn.field,
662
+ disabled: currentColumn.freeze,
663
+ label: /* @__PURE__ */ React.createElement(RowLabel, { columnData: currentColumn })
664
+ };
665
+ const fieldProps = {
666
+ options: currentColumn.options,
667
+ format: currentColumn.fmt,
668
+ disabled: currentColumn.freeze,
669
+ request: currentColumn.request
670
+ };
671
+ if (currentColumn.tool === 39) {
672
+ return null;
673
+ }
674
+ return isPure ? /* @__PURE__ */ React.createElement(ProForm.Item, { key: currentColumn.field, ...props }, /* @__PURE__ */ React.createElement(Component, { ...fieldProps })) : /* @__PURE__ */ React.createElement(Component, { key: currentColumn.field, ...props, fieldProps });
675
+ };
676
+
677
+ const FeatureRowField = ({ columnId, columnsData }) => {
678
+ const currentColumn = useMemo(() => {
679
+ const value = columnsData.find((item) => item.field === columnId);
680
+ return value;
681
+ }, [columnsData, columnId]);
682
+ if (!currentColumn) return null;
683
+ return /* @__PURE__ */ React.createElement(FeatureRowFields, { currentColumn });
684
+ };
685
+
686
+ const FeatureRowSkeleton = () => {
687
+ return /* @__PURE__ */ React.createElement(Flex, { align: "center", gap: 12, style: { padding: "8px 0" } }, /* @__PURE__ */ React.createElement(Skeleton.Avatar, { active: true, size: "small", shape: "square" }), /* @__PURE__ */ React.createElement(Skeleton.Input, { active: true, size: "small", style: { width: 80 } }), /* @__PURE__ */ React.createElement(Skeleton.Input, { active: true, size: "small", style: { width: 240 } }));
688
+ };
689
+ const DrawerForm = ({ form, initialValues, columnsData, onChange, loading }) => {
690
+ const debounceChange = useDebounce(onChange, 500);
691
+ if (loading) {
692
+ return /* @__PURE__ */ React.createElement(Flex, { className: "info-card", gap: 12, vertical: true, style: { padding: 16, width: "100%" } }, Array.from({ length: 10 }).map((_, i) => /* @__PURE__ */ React.createElement(FeatureRowSkeleton, { key: i })));
693
+ }
694
+ return /* @__PURE__ */ React.createElement(
695
+ ProForm,
696
+ {
697
+ form,
698
+ className: "row-setting-form",
699
+ formKey: "row-setting-form",
700
+ layout: "horizontal",
701
+ style: { width: "100%" },
702
+ submitter: {
703
+ render: false
704
+ },
705
+ initialValues,
706
+ onValuesChange: debounceChange
707
+ },
708
+ /* @__PURE__ */ React.createElement(Flex, { className: "info-card", gap: 12, vertical: true }, Object.entries(initialValues).map(([key]) => /* @__PURE__ */ React.createElement(FeatureRowField, { key, columnId: key, columnsData })))
709
+ );
710
+ };
711
+
712
+ const DetailsDrawer = ({
713
+ isOpen = false,
714
+ width = "650px",
715
+ placement = "right",
716
+ className,
717
+ form,
718
+ initialValues,
719
+ columnsData,
720
+ defaultActiveKey,
721
+ onClick,
722
+ onChange,
723
+ onClose,
724
+ uploadFunction,
725
+ onPreview,
726
+ historyNode,
727
+ closeIcon,
728
+ loading,
729
+ ...rest
730
+ }) => {
731
+ const tabItems = [
732
+ {
733
+ key: "detail",
734
+ label: "\u8BE6\u60C5",
735
+ children: /* @__PURE__ */ React.createElement(
736
+ DrawerForm,
737
+ {
738
+ form,
739
+ initialValues,
740
+ columnsData,
741
+ onChange,
742
+ loading
743
+ }
744
+ )
745
+ },
746
+ { key: "history", label: "\u5386\u53F2", children: historyNode }
747
+ ];
748
+ return /* @__PURE__ */ React.createElement(
749
+ DetailsDrawerProvider,
750
+ {
751
+ uploadFunction,
752
+ onPreview,
753
+ columnsData,
754
+ onClick
755
+ },
756
+ /* @__PURE__ */ React.createElement(
757
+ Drawer,
758
+ {
759
+ ...rest,
760
+ open: isOpen,
761
+ onClose,
762
+ closeIcon,
763
+ width,
764
+ placement,
765
+ className: `custom-drawer ${className}`,
766
+ style: {
767
+ mask: "rgba(0,0,0,0)"
768
+ },
769
+ styles: {
770
+ body: {
771
+ padding: 0,
772
+ overflow: "hidden"
773
+ }
774
+ }
775
+ },
776
+ /* @__PURE__ */ React.createElement(Flex, { vertical: true, onClick: (e) => e.stopPropagation(), style: { height: "100%" } }, /* @__PURE__ */ React.createElement("div", { className: "drawer-header-container" }, /* @__PURE__ */ React.createElement(
777
+ Tabs,
778
+ {
779
+ defaultActiveKey,
780
+ items: tabItems,
781
+ className: "custom-tabs",
782
+ style: { height: "100%" }
783
+ }
784
+ )))
785
+ )
786
+ );
787
+ };
788
+
789
+ const DrawerTitle = ({ onClose, isLast, isNext, onLastClick, onNextClick }) => {
790
+ return /* @__PURE__ */ React.createElement(Flex, { className: "drawer-title-container", justify: "space-between", align: "center", onClick: (e) => e.stopPropagation() }, /* @__PURE__ */ React.createElement(Flex, { className: "nav-arrows", gap: 4 }, /* @__PURE__ */ React.createElement(Tooltip, { title: "\u4E0A\u4E00\u9898" }, /* @__PURE__ */ React.createElement(
791
+ Button,
792
+ {
793
+ size: "small",
794
+ type: "text",
795
+ icon: /* @__PURE__ */ React.createElement(UpOutlined, { size: 20 }),
796
+ onClick: onLastClick,
797
+ disabled: !isLast
798
+ }
799
+ )), /* @__PURE__ */ React.createElement(Tooltip, { title: "\u4E0B\u4E00\u9898" }, /* @__PURE__ */ React.createElement(
800
+ Button,
801
+ {
802
+ size: "small",
803
+ type: "text",
804
+ icon: /* @__PURE__ */ React.createElement(DownOutlined, { size: 20 }),
805
+ onClick: onNextClick,
806
+ disabled: !isNext
807
+ }
808
+ ))), /* @__PURE__ */ React.createElement(
809
+ Button,
810
+ {
811
+ className: "close-btn",
812
+ size: "small",
813
+ type: "text",
814
+ icon: /* @__PURE__ */ React.createElement(CloseOutlined, { size: 20 }),
815
+ onClick: onClose
816
+ }
817
+ ));
818
+ };
819
+
820
+ const SearchPagination = ({ selectIndex, total, onClickArrow }) => {
821
+ return /* @__PURE__ */ React.createElement(Flex, { className: "search-suffix", align: "center" }, /* @__PURE__ */ React.createElement(
822
+ Button,
823
+ {
824
+ type: "link",
825
+ className: "arrow-icon",
826
+ icon: /* @__PURE__ */ React.createElement(LeftOutlined, { size: 12 }),
827
+ onClick: () => onClickArrow("left"),
828
+ onMouseDown: (e) => e.preventDefault()
829
+ }
830
+ ), /* @__PURE__ */ React.createElement(Flex, { className: "suffix-number", align: "center", justify: "center" }, `${selectIndex} / ${total}`), /* @__PURE__ */ React.createElement(
831
+ Button,
832
+ {
833
+ type: "link",
834
+ className: "arrow-icon",
835
+ icon: /* @__PURE__ */ React.createElement(RightOutlined, { size: 12 }),
836
+ onClick: () => onClickArrow("right"),
837
+ onMouseDown: (e) => e.preventDefault()
838
+ }
839
+ ));
840
+ };
841
+
842
+ const SearchModal = ({
843
+ offsetParent,
844
+ width = 400,
845
+ isLoading = false,
846
+ isOpen = false,
847
+ position,
848
+ placeholder = "\u5728\u6570\u636E\u8868\u4E2D\u67E5\u627E",
849
+ searchValue,
850
+ searchTotal = 0,
851
+ suffix,
852
+ onClose,
853
+ onPositionChange,
854
+ onSearchChange,
855
+ onPageChange
856
+ }) => {
857
+ const inputRef = useRef(null);
858
+ const [selectIndex, setSelectIndex] = useState(searchTotal > 0 ? 1 : 0);
859
+ useEffect(() => {
860
+ if (searchTotal <= 0) {
861
+ setSelectIndex(0);
862
+ } else {
863
+ setSelectIndex((prev) => {
864
+ if (prev === 0 || prev > searchTotal) {
865
+ return 1;
866
+ }
867
+ return prev;
868
+ });
869
+ }
870
+ }, [searchTotal]);
871
+ const onClickArrow = (type) => {
872
+ if (searchTotal <= 0) return;
873
+ const delta = type === "right" ? 1 : -1;
874
+ const newIndex = (selectIndex - 1 + delta + searchTotal) % searchTotal + 1;
875
+ if (newIndex !== selectIndex) {
876
+ setSelectIndex(newIndex);
877
+ if (onPageChange) onPageChange(newIndex);
878
+ }
879
+ };
880
+ useEffect(() => {
881
+ if (!isOpen) return;
882
+ requestAnimationFrame(() => {
883
+ inputRef.current?.focus({
884
+ cursor: "end"
885
+ // 光标在末尾
886
+ });
887
+ });
888
+ }, [isOpen]);
889
+ return /* @__PURE__ */ React.createElement(
890
+ BaseModal,
891
+ {
892
+ offsetParent,
893
+ width,
894
+ open: isOpen,
895
+ onClose,
896
+ value: position,
897
+ onChange: onPositionChange,
898
+ className: "search-modal"
899
+ },
900
+ /* @__PURE__ */ React.createElement(
901
+ DragDiv,
902
+ {
903
+ className: "search-modal-container",
904
+ onDrag: (dx, dy) => onPositionChange(({ x, y }) => ({ x: x + dx, y: y + dy }))
905
+ },
906
+ /* @__PURE__ */ React.createElement(
907
+ Flex,
908
+ {
909
+ className: "no-move",
910
+ justify: "center",
911
+ align: "center",
912
+ gap: 8,
913
+ flex: 1,
914
+ onMouseDown: (e) => e.stopPropagation()
915
+ },
916
+ /* @__PURE__ */ React.createElement(
917
+ Input,
918
+ {
919
+ ref: inputRef,
920
+ className: "search-input",
921
+ value: searchValue,
922
+ onChange: onSearchChange,
923
+ placeholder,
924
+ prefix: isLoading ? /* @__PURE__ */ React.createElement(LoadingOutlined, { spin: true }) : /* @__PURE__ */ React.createElement(SearchOutlined, null),
925
+ suffix: suffix ? suffix : /* @__PURE__ */ React.createElement(SearchPagination, { selectIndex, total: searchTotal, onClickArrow })
926
+ }
927
+ ),
928
+ /* @__PURE__ */ React.createElement(Button, { className: "search-close", type: "text", icon: /* @__PURE__ */ React.createElement(CloseOutlined, { size: 16 }), onClick: onClose })
929
+ )
930
+ )
931
+ );
932
+ };
933
+
934
+ const Operator = {
935
+ "EQUAL": "equal",
936
+ "NOT_EQUAL": "not_equal",
937
+ "GREATER_THAN": "greater_than",
938
+ "GREATER_THAN_OR_EQUAL": "greater_than_or_equal",
939
+ "LESS_THAN": "less_than",
940
+ "LESS_THAN_OR_EQUAL": "less_than_or_equal",
941
+ "BEFORE": "before",
942
+ "AFTER": "after",
943
+ "LIKE": "like",
944
+ "NOT_LIKE": "not_like",
945
+ "EMPTY": "empty",
946
+ "NOT_EMPTY": "not_empty"
947
+ };
948
+
949
+ const getDateRangeByOption = (type, operator) => {
950
+ const now = dayjs();
951
+ const dayStart = (d) => d.startOf("day");
952
+ const dayEnd = (d) => d.endOf("day");
953
+ switch (type) {
954
+ case "today": {
955
+ const start = dayStart(now);
956
+ const end = dayEnd(now);
957
+ if (operator === "equal") return [start, end];
958
+ if (operator === "before") return [void 0, start];
959
+ return [end, void 0];
960
+ }
961
+ case "yesterday": {
962
+ const d = now.subtract(1, "day");
963
+ const start = dayStart(d);
964
+ const end = dayEnd(d);
965
+ if (operator === "equal") return [start, end];
966
+ if (operator === "before") return [void 0, start];
967
+ return [end, void 0];
968
+ }
969
+ case "tomorrow": {
970
+ const d = now.add(1, "day");
971
+ const start = dayStart(d);
972
+ const end = dayEnd(d);
973
+ if (operator === "equal") return [start, end];
974
+ if (operator === "before") return [void 0, start];
975
+ return [end, void 0];
976
+ }
977
+ case "this_week":
978
+ return [now.startOf("week"), now.endOf("week")];
979
+ case "last_week": {
980
+ const d = now.subtract(1, "week");
981
+ return [d.startOf("week"), d.endOf("week")];
982
+ }
983
+ case "this_month":
984
+ return [now.startOf("month"), now.endOf("month")];
985
+ case "last_month": {
986
+ const d = now.subtract(1, "month");
987
+ return [d.startOf("month"), d.endOf("month")];
988
+ }
989
+ case "last_7_days":
990
+ return [dayStart(now.subtract(6, "day")), dayEnd(now)];
991
+ case "next_7_days":
992
+ return [dayStart(now), dayEnd(now.add(6, "day"))];
993
+ case "last_30_days":
994
+ return [dayStart(now.subtract(29, "day")), dayEnd(now)];
995
+ case "next_30_days":
996
+ return [dayStart(now), dayEnd(now.add(29, "day"))];
997
+ default:
998
+ return void 0;
999
+ }
1000
+ };
1001
+ const getDateBySpecific = (value, operator) => {
1002
+ const startOfDay = value.startOf("day");
1003
+ const endOfDay = value.endOf("day");
1004
+ switch (operator) {
1005
+ case "equal":
1006
+ return [startOfDay, endOfDay];
1007
+ case "before":
1008
+ return [void 0, startOfDay];
1009
+ case "after":
1010
+ return [endOfDay, void 0];
1011
+ default:
1012
+ return [void 0, void 0];
1013
+ }
1014
+ };
1015
+
1016
+ const getOperatorOptions = (config) => {
1017
+ if (config.numberSelect || config.time) {
1018
+ return [
1019
+ { label: "\u7B49\u4E8E", value: "equal" },
1020
+ { label: "\u4E0D\u7B49\u4E8E", value: "not_equal" },
1021
+ { label: "\u5927\u4E8E", value: "greater_than" },
1022
+ { label: "\u5927\u4E8E\u6216\u7B49\u4E8E", value: "greater_than_or_equal" },
1023
+ { label: "\u5C0F\u4E8E", value: "less_than" },
1024
+ { label: "\u5C0F\u4E8E\u6216\u7B49\u4E8E", value: "less_than_or_equal" },
1025
+ { label: "\u4E3A\u7A7A", value: "empty" },
1026
+ { label: "\u4E0D\u4E3A\u7A7A", value: "not_empty" }
1027
+ ];
1028
+ } else if (config.date) {
1029
+ return [
1030
+ { label: "\u7B49\u4E8E", value: "equal" },
1031
+ { label: "\u65E9\u4E8E", value: "before" },
1032
+ { label: "\u665A\u4E8E", value: "after" },
1033
+ { label: "\u4E3A\u7A7A", value: "empty" },
1034
+ { label: "\u4E0D\u4E3A\u7A7A", value: "not_empty" }
1035
+ ];
1036
+ } else {
1037
+ return [
1038
+ { label: "\u7B49\u4E8E", value: "equal" },
1039
+ { label: "\u4E0D\u7B49\u4E8E", value: "not_equal" },
1040
+ { label: "\u5305\u542B", value: "like" },
1041
+ { label: "\u4E0D\u5305\u542B", value: "not_like" },
1042
+ { label: "\u4E3A\u7A7A", value: "empty" },
1043
+ { label: "\u4E0D\u4E3A\u7A7A", value: "not_empty" }
1044
+ ];
1045
+ }
1046
+ };
1047
+ const FilterTypeSelect = ({ listIndex, value, form, tableConfig, onChange }) => {
1048
+ const operatorOptions = useMemo(() => {
1049
+ if (!tableConfig) return [];
1050
+ return getOperatorOptions(tableConfig);
1051
+ }, [tableConfig]);
1052
+ const handleChange = (type) => {
1053
+ const oldValue = form.getFieldValue(["filters", listIndex, "value"]);
1054
+ const isTimeBeforeOrAfter = type === "before" || type === "after";
1055
+ if (isTimeBeforeOrAfter && !["specific", "today", "tomorrow", "yesterday"].includes(oldValue)) {
1056
+ const oldData = form.getFieldValue(["filters", listIndex]);
1057
+ form.setFieldValue(["filters", listIndex], {
1058
+ ...oldData,
1059
+ value: "specific",
1060
+ date: void 0
1061
+ });
1062
+ onChange(type);
1063
+ } else if (type === "empty" || type === "not_empty") {
1064
+ const oldData = form.getFieldValue(["filters", listIndex]);
1065
+ form.setFieldValue(["filters", listIndex], {
1066
+ ...oldData,
1067
+ value: void 0,
1068
+ date: void 0
1069
+ });
1070
+ onChange(type);
1071
+ } else {
1072
+ onChange(type);
1073
+ }
1074
+ };
1075
+ return /* @__PURE__ */ React.createElement(
1076
+ Select$1,
1077
+ {
1078
+ value,
1079
+ className: "filter-form-operator",
1080
+ popupMatchSelectWidth: 200,
1081
+ options: operatorOptions,
1082
+ onChange: handleChange
1083
+ }
1084
+ );
1085
+ };
1086
+
1087
+ const FilterDatePicker = ({ value, columnConfig, onChange }) => {
1088
+ const [date, setDate] = useState(value ? dayjs(value) : void 0);
1089
+ const formatType = useMemo(() => {
1090
+ const type = columnConfig?.dateFormat;
1091
+ if (!type) {
1092
+ return "YYYY/MM/DD";
1093
+ } else {
1094
+ return type.split(" ")[0] || "YYYY/MM/DD";
1095
+ }
1096
+ }, [columnConfig?.dateFormat]);
1097
+ const handleSelectDate = (_date) => {
1098
+ setDate(_date);
1099
+ onChange(_date);
1100
+ };
1101
+ return /* @__PURE__ */ React.createElement(
1102
+ DatePicker,
1103
+ {
1104
+ format: formatType,
1105
+ allowClear: false,
1106
+ value: date,
1107
+ onChange: handleSelectDate,
1108
+ placeholder: formatType,
1109
+ suffixIcon: void 0
1110
+ }
1111
+ );
1112
+ };
1113
+
1114
+ const options = [
1115
+ { label: "\u5177\u4F53\u65E5\u671F", value: "specific" },
1116
+ { label: "\u4ECA\u5929", value: "today" },
1117
+ { label: "\u660E\u5929", value: "tomorrow" },
1118
+ { label: "\u6628\u5929", value: "yesterday" },
1119
+ { label: "\u672C\u5468", value: "this_week" },
1120
+ { label: "\u4E0A\u5468", value: "last_week" },
1121
+ { label: "\u672C\u6708", value: "this_month" },
1122
+ { label: "\u4E0A\u6708", value: "last_month" },
1123
+ { label: "\u8FC7\u53BB 7 \u5929\u5185", value: "last_7_days" },
1124
+ { label: "\u672A\u6765 7 \u5929\u5185", value: "next_7_days" },
1125
+ { label: "\u8FC7\u53BB 30 \u5929\u5185", value: "last_30_days" },
1126
+ { label: "\u672A\u6765 30 \u5929\u5185", value: "next_30_days" }
1127
+ ];
1128
+ const FilterDateSelect = ({ form, listIndex, value, operator, onChange }) => {
1129
+ const filterOptions = useMemo(() => {
1130
+ return operator === "before" || operator === "after" ? options.filter((item) => ["specific", "today", "tomorrow", "yesterday"].includes(item.value)) : options;
1131
+ }, [operator]);
1132
+ const handleChange = (value2) => {
1133
+ if (value2 !== "specific") {
1134
+ const oldData = form.getFieldValue(["filters", listIndex]);
1135
+ form.setFieldValue(["filters", listIndex], {
1136
+ ...oldData,
1137
+ value: value2,
1138
+ date: void 0
1139
+ });
1140
+ }
1141
+ onChange(value2);
1142
+ };
1143
+ useMount(() => {
1144
+ if (value === void 0) {
1145
+ onChange("specific");
1146
+ }
1147
+ });
1148
+ return /* @__PURE__ */ React.createElement(
1149
+ Select$1,
1150
+ {
1151
+ className: "form-date-type",
1152
+ placeholder: "\u8BF7\u9009\u62E9\u65E5\u671F",
1153
+ options: filterOptions,
1154
+ popupMatchSelectWidth: 200,
1155
+ value,
1156
+ onChange: handleChange
1157
+ }
1158
+ );
1159
+ };
1160
+
1161
+ const FilterFileSelect = ({ value, onChange }) => {
1162
+ const handleSelectChange = (value2) => {
1163
+ onChange(value2);
1164
+ };
1165
+ return /* @__PURE__ */ React.createElement(
1166
+ Select$1,
1167
+ {
1168
+ placeholder: "\u8BF7\u9009\u62E9\u6587\u4EF6\u7C7B\u578B",
1169
+ onChange: handleSelectChange,
1170
+ value,
1171
+ options: [
1172
+ {
1173
+ label: "\u56FE\u7247",
1174
+ value: "image"
1175
+ },
1176
+ {
1177
+ label: "\u6587\u6863",
1178
+ value: "document"
1179
+ },
1180
+ {
1181
+ label: "\u89C6\u9891",
1182
+ value: "video"
1183
+ }
1184
+ ]
1185
+ }
1186
+ );
1187
+ };
1188
+
1189
+ const FilterInput = ({ onChange, value }) => {
1190
+ return /* @__PURE__ */ React.createElement(Input, { placeholder: "\u8BF7\u8F93\u5165", onChange: (e) => onChange(e.target?.value), value });
1191
+ };
1192
+
1193
+ const sanitizeInput = (value) => {
1194
+ let sanitized = value.replace(/[^\d.-]/g, "");
1195
+ const parts = sanitized.split(".");
1196
+ if (parts.length > 2) sanitized = parts[0] + "." + parts.slice(1).join("");
1197
+ if (sanitized.includes("-") && sanitized.indexOf("-") > 0) sanitized = sanitized.replace(/-/g, "");
1198
+ return sanitized;
1199
+ };
1200
+ const getSliceNumber = (rawValue) => {
1201
+ const [intPart, decimalPart] = rawValue.split(".");
1202
+ if (decimalPart && decimalPart.length > 9) {
1203
+ const fixedValue = `${intPart}.${decimalPart.slice(0, 9)}`;
1204
+ return fixedValue;
1205
+ }
1206
+ return rawValue;
1207
+ };
1208
+ const FilterNumber = ({ onChange, value }) => {
1209
+ const [rawValue, setRawValue] = useState(value || "");
1210
+ const debounceOnChange = useDebounce((value2) => {
1211
+ onChange(value2);
1212
+ }, 500);
1213
+ const onChangeInput = (e) => {
1214
+ const input = sanitizeInput(e.target.value);
1215
+ setRawValue(input);
1216
+ if (rawValue !== input) {
1217
+ debounceOnChange(getSliceNumber(input));
1218
+ }
1219
+ };
1220
+ useEffect(() => {
1221
+ if (value === void 0) {
1222
+ setRawValue("");
1223
+ }
1224
+ }, [value]);
1225
+ const onBlur = () => {
1226
+ if (!rawValue) return;
1227
+ const newValue = getSliceNumber(rawValue);
1228
+ setRawValue(newValue);
1229
+ if (rawValue !== newValue) {
1230
+ debounceOnChange(getSliceNumber(newValue));
1231
+ }
1232
+ };
1233
+ return /* @__PURE__ */ React.createElement(Input, { placeholder: "\u8BF7\u8F93\u5165", value: rawValue, onBlur, onChange: onChangeInput });
1234
+ };
1235
+
1236
+ const FilterPeopleAvatar = ({ avatar, label }) => {
1237
+ const backgroundColor = useMemo(() => label === "\u65E0" ? "#D9D9D9" : getColorByStr(label?.slice(-2) || ""), [label]);
1238
+ return /* @__PURE__ */ React.createElement(React.Fragment, null, avatar ? /* @__PURE__ */ React.createElement(Avatar, { src: avatar, size: "small", style: { marginRight: 8 } }, label?.slice(-2)) : /* @__PURE__ */ React.createElement(
1239
+ Avatar,
1240
+ {
1241
+ style: {
1242
+ backgroundColor,
1243
+ marginRight: 8
1244
+ },
1245
+ size: "small"
1246
+ },
1247
+ label?.slice(-2)
1248
+ ));
1249
+ };
1250
+ const TagRender = ({ label, closable, onClose }) => {
1251
+ const onPreventMouseDown = (event) => {
1252
+ event.preventDefault();
1253
+ event.stopPropagation();
1254
+ };
1255
+ return /* @__PURE__ */ React.createElement(Tag, { onMouseDown: onPreventMouseDown, closable, onClose, bordered: false }, /* @__PURE__ */ React.createElement(
1256
+ "span",
1257
+ {
1258
+ style: {
1259
+ display: "inline-block",
1260
+ maxWidth: 80,
1261
+ overflow: "hidden",
1262
+ textOverflow: "ellipsis",
1263
+ whiteSpace: "nowrap",
1264
+ verticalAlign: "middle"
1265
+ }
1266
+ },
1267
+ label
1268
+ ));
1269
+ };
1270
+ const FilterPeopleSelect = ({ columnId, columnConfig, value, onChange }) => {
1271
+ const fetchRef = useRef(0);
1272
+ const [fetching, setFetching] = useState(false);
1273
+ const [options, setOptions] = useState([]);
1274
+ const inputRef = useRef(null);
1275
+ const [name, setName] = useState("");
1276
+ const handleSelectChange = (val) => {
1277
+ onChange(val);
1278
+ };
1279
+ const debounceFetcher = useMemo(() => {
1280
+ const loadOptions = (value2) => {
1281
+ if (!columnConfig.requestPeople) {
1282
+ setOptions([]);
1283
+ return;
1284
+ }
1285
+ fetchRef.current += 1;
1286
+ const fetchId = fetchRef.current;
1287
+ setOptions([]);
1288
+ setFetching(true);
1289
+ columnConfig.requestPeople(value2).then((data) => {
1290
+ if (fetchId !== fetchRef.current) {
1291
+ return;
1292
+ }
1293
+ const newOptions = data.map((item) => ({
1294
+ value: item.value,
1295
+ label: /* @__PURE__ */ React.createElement(Flex, { align: "center", justify: "center" }, /* @__PURE__ */ React.createElement(FilterPeopleAvatar, { label: item.label, avatar: item.avatar }), /* @__PURE__ */ React.createElement(
1296
+ "span",
1297
+ {
1298
+ style: {
1299
+ overflow: "hidden",
1300
+ textOverflow: "ellipsis",
1301
+ whiteSpace: "nowrap",
1302
+ flex: 1
1303
+ }
1304
+ },
1305
+ item.label
1306
+ )),
1307
+ data: item
1308
+ }));
1309
+ setOptions(newOptions);
1310
+ setFetching(false);
1311
+ });
1312
+ };
1313
+ return debounce(loadOptions, 300);
1314
+ }, [columnConfig]);
1315
+ const onNameChange = (event) => {
1316
+ setName(event.target.value);
1317
+ debounceFetcher(event.target.value);
1318
+ };
1319
+ useEffect(() => {
1320
+ debounceFetcher();
1321
+ }, [columnId, debounceFetcher]);
1322
+ const onOpenChange = (open) => {
1323
+ if (!open) {
1324
+ setName("");
1325
+ debounceFetcher();
1326
+ }
1327
+ };
1328
+ return /* @__PURE__ */ React.createElement(
1329
+ Select$1,
1330
+ {
1331
+ mode: "multiple",
1332
+ maxTagCount: 1,
1333
+ showSearch: false,
1334
+ placeholder: "\u8BF7\u9009\u62E9\u9009\u9879",
1335
+ value,
1336
+ options,
1337
+ onChange: handleSelectChange,
1338
+ filterOption: false,
1339
+ notFoundContent: fetching ? /* @__PURE__ */ React.createElement(Spin, { size: "small" }) : "\u6682\u65E0\u6570\u636E",
1340
+ tagRender: (props) => {
1341
+ return /* @__PURE__ */ React.createElement(TagRender, { ...props });
1342
+ },
1343
+ onOpenChange,
1344
+ popupRender: (menu) => /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(Space, { style: { padding: "4px 0px 0px" } }, /* @__PURE__ */ React.createElement(
1345
+ Input,
1346
+ {
1347
+ placeholder: "\u641C\u7D22\u6210\u5458",
1348
+ variant: "borderless",
1349
+ ref: inputRef,
1350
+ value: name,
1351
+ onChange: onNameChange,
1352
+ onKeyDown: (e) => e.stopPropagation()
1353
+ }
1354
+ )), /* @__PURE__ */ React.createElement(Divider, { style: { margin: "8px 0" } }), menu)
1355
+ }
1356
+ );
1357
+ };
1358
+
1359
+ const FilterSelect = ({ tableConfig, columnConfig, value, onChange }) => {
1360
+ const mode = useMemo(() => {
1361
+ const custom = tableConfig?.customOptions;
1362
+ return typeof custom === "object" && custom.multiSelect ? "multiple" : "single";
1363
+ }, [tableConfig]);
1364
+ const options = useMemo(() => Array.isArray(columnConfig["options"]) ? columnConfig["options"] : [], [columnConfig]);
1365
+ const handleChange = (value2) => {
1366
+ if (mode === "single") {
1367
+ onChange(value2.slice(value2.length - 1));
1368
+ } else {
1369
+ onChange(value2);
1370
+ }
1371
+ };
1372
+ return /* @__PURE__ */ React.createElement(
1373
+ Select$1,
1374
+ {
1375
+ mode: "multiple",
1376
+ value,
1377
+ maxTagCount: 1,
1378
+ placeholder: "\u8BF7\u9009\u62E9\u9009\u9879",
1379
+ onChange: handleChange,
1380
+ options
1381
+ }
1382
+ );
1383
+ };
1384
+
1385
+ const FilterValue = ({ form, columnId, tableConfig, columnConfig, listIndex }) => {
1386
+ const Component = useMemo(() => {
1387
+ let DefaultComponent;
1388
+ if (tableConfig.customOptions) {
1389
+ DefaultComponent = FilterSelect;
1390
+ } else if (tableConfig.date) {
1391
+ DefaultComponent = FilterDateSelect;
1392
+ } else if (tableConfig.numberSelect || tableConfig.time) {
1393
+ DefaultComponent = FilterNumber;
1394
+ } else if (tableConfig.people) {
1395
+ DefaultComponent = FilterPeopleSelect;
1396
+ } else if (tableConfig.file) {
1397
+ DefaultComponent = FilterFileSelect;
1398
+ } else {
1399
+ DefaultComponent = FilterInput;
1400
+ }
1401
+ return DefaultComponent;
1402
+ }, [tableConfig]);
1403
+ const props = { form, columnId, columnConfig, tableConfig, listIndex };
1404
+ return /* @__PURE__ */ React.createElement(Flex, { className: "filter-value-box", gap: 8 }, /* @__PURE__ */ React.createElement(ProFormItem, { name: "value", className: tableConfig.date ? "" : "item-value" }, /* @__PURE__ */ React.createElement(Component, { ...props })), tableConfig.date && /* @__PURE__ */ React.createElement(ProFormDependency, { name: ["value"] }, ({ value }) => {
1405
+ if (value !== "specific") return null;
1406
+ return /* @__PURE__ */ React.createElement(ProFormItem, { name: "date", className: "item-value" }, /* @__PURE__ */ React.createElement(FilterDatePicker, { ...props }));
1407
+ }));
1408
+ };
1409
+
1410
+ const FilterGroup = ({ form, columnId, columnData, listIndex, tools }) => {
1411
+ const columnConfig = useMemo(() => {
1412
+ return columnData.find((item) => item.id === columnId);
1413
+ }, [columnData, columnId]);
1414
+ const tableConfig = useMemo(() => {
1415
+ if (!columnConfig || !columnConfig["type"]) return;
1416
+ return tools.find((item) => item.value === Number(columnConfig["type"]))?.table;
1417
+ }, [columnConfig, tools]);
1418
+ if (!tableConfig) return;
1419
+ const props = { form, columnId, columnConfig, tableConfig, listIndex };
1420
+ return /* @__PURE__ */ React.createElement(Flex, { className: "filter-item-config", gap: 8 }, /* @__PURE__ */ React.createElement(ProFormItem, { name: "operator" }, /* @__PURE__ */ React.createElement(FilterTypeSelect, { ...props })), /* @__PURE__ */ React.createElement(ProFormDependency, { name: ["operator"] }, ({ operator }) => {
1421
+ if (operator === "empty" || operator === "not_empty") return null;
1422
+ return /* @__PURE__ */ React.createElement(FilterValue, { ...props, operator });
1423
+ }));
1424
+ };
1425
+
1426
+ const FilterTitle = ({ filterCount }) => {
1427
+ return /* @__PURE__ */ React.createElement(Flex, { align: "center", gap: 8, className: "filter-title-wrap" }, /* @__PURE__ */ React.createElement(Typography.Text, null, "\u8BBE\u7F6E\u7B5B\u9009\u6761\u4EF6"), /* @__PURE__ */ React.createElement(Flex, { className: "filter-type-container", align: "center", gap: 8, style: { display: filterCount > 1 ? "" : "none" } }, /* @__PURE__ */ React.createElement(Typography.Text, null, "\u7B26\u5408\u4EE5\u4E0B"), /* @__PURE__ */ React.createElement(ProFormItem, { name: "filterType", className: "filter-type-item" }, /* @__PURE__ */ React.createElement(
1428
+ Select$1,
1429
+ {
1430
+ size: "small",
1431
+ options: [
1432
+ { label: "\u6240\u6709", value: "all" },
1433
+ { label: "\u4EFB\u4E00", value: "any" }
1434
+ ]
1435
+ }
1436
+ )), /* @__PURE__ */ React.createElement(Typography.Text, null, "\u6761\u4EF6")), /* @__PURE__ */ React.createElement(Tooltip, { placement: "top", title: "\u5F53\u524D\u7B5B\u9009\u64CD\u4F5C\u4E0D\u5F71\u54CD\u5176\u4ED6\u534F\u4F5C\u8005" }, /* @__PURE__ */ React.createElement(QuestionCircleOutlined, null)));
1437
+ };
1438
+
1439
+ const FilterOperator = {
1440
+ "EQUAl": "equal",
1441
+ "NOT_EQUAL": "not_equal",
1442
+ "LIKE": "like",
1443
+ "NOT_LIKE": "not_like",
1444
+ "EMPTY": "empty",
1445
+ "NOT_EMPTY": "not_empty",
1446
+ "BETWEEN": "between",
1447
+ "GREATER_THAN": "greater_than",
1448
+ "GREATER_THAN_OR_EQUAL": "greater_than_or_equal",
1449
+ "LESS_THAN": "less_than",
1450
+ "LESS_THAN_OR_EQUAL": "less_than_or_equal"
1451
+ };
1452
+
1453
+ const TableFilterForm = ({ initialValues, columnData, tools, onValuesChange }) => {
1454
+ const [form] = Form.useForm();
1455
+ const outputValues = useRef(initialValues);
1456
+ const actionRef = useRef(void 0);
1457
+ const [filterCount, setFilterCount] = useState(initialValues?.filters.length ?? 0);
1458
+ const updateFilterCount = () => {
1459
+ const values = form.getFieldsValue();
1460
+ setFilterCount(values.filters?.length ?? 0);
1461
+ };
1462
+ const filedOptions = useMemo(
1463
+ () => columnData.map((column) => ({
1464
+ value: column.id,
1465
+ label: column.label
1466
+ })),
1467
+ [columnData]
1468
+ );
1469
+ const onDebounceChange = useMemo(
1470
+ () => debounce(() => {
1471
+ const allValues = form.getFieldsValue();
1472
+ const lastDate = {
1473
+ filterType: allValues.filterType,
1474
+ filters: []
1475
+ };
1476
+ allValues.filters.forEach((filter) => {
1477
+ const columnConfig = columnData.find((item) => item.id === filter.columnId);
1478
+ const tableConfig = columnConfig ? tools.find((item) => item.value === Number(columnConfig["type"]))?.table : void 0;
1479
+ const newFilter = {
1480
+ columnId: filter.columnId,
1481
+ operator: filter.operator
1482
+ };
1483
+ const operator = filter.operator;
1484
+ if (filter.value) {
1485
+ newFilter.value = filter.value;
1486
+ if (tableConfig?.date) {
1487
+ if (operator === "equal" || operator === "before" || operator === "after") {
1488
+ newFilter.operator = "between";
1489
+ }
1490
+ if (filter.date) {
1491
+ const date = filter.date;
1492
+ newFilter.value = getDateBySpecific(date, operator);
1493
+ lastDate.filters.push(newFilter);
1494
+ } else {
1495
+ const type = filter.value;
1496
+ newFilter.value = getDateRangeByOption(type, operator);
1497
+ if (newFilter.value) {
1498
+ lastDate.filters.push(newFilter);
1499
+ }
1500
+ }
1501
+ } else {
1502
+ lastDate.filters.push(newFilter);
1503
+ }
1504
+ } else if (operator === "empty" || operator === "not_empty") {
1505
+ lastDate.filters.push(newFilter);
1506
+ }
1507
+ });
1508
+ if (!isEqual(lastDate, outputValues.current)) {
1509
+ outputValues.current = lastDate;
1510
+ onValuesChange?.(lastDate);
1511
+ }
1512
+ }, 500),
1513
+ [columnData, form, onValuesChange, tools]
1514
+ );
1515
+ const onAddList = () => {
1516
+ const defaultColumnId = columnData[0]?.id || "";
1517
+ actionRef.current?.add({
1518
+ columnId: defaultColumnId,
1519
+ operator: "equal"
1520
+ });
1521
+ updateFilterCount();
1522
+ };
1523
+ const onDeleteList = (index) => {
1524
+ actionRef.current?.remove(index);
1525
+ updateFilterCount();
1526
+ onDebounceChange();
1527
+ };
1528
+ const onListChange = (index, values) => {
1529
+ const filters = form.getFieldValue("filters") || [];
1530
+ if (values.columnId) {
1531
+ values["operator"] = "equal";
1532
+ values["value"] = void 0;
1533
+ }
1534
+ const newFilters = filters.map((item, i) => i === index ? omitBy({ ...item, ...values }, isUndefined) : item);
1535
+ form.setFieldValue("filters", newFilters);
1536
+ };
1537
+ return /* @__PURE__ */ React.createElement(
1538
+ ProForm,
1539
+ {
1540
+ className: "filter-form",
1541
+ form,
1542
+ submitter: false,
1543
+ initialValues: {
1544
+ filterType: "all",
1545
+ ...initialValues
1546
+ },
1547
+ onValuesChange: onDebounceChange
1548
+ },
1549
+ /* @__PURE__ */ React.createElement(FilterTitle, { filterCount }),
1550
+ /* @__PURE__ */ React.createElement(
1551
+ ProFormList,
1552
+ {
1553
+ actionRef,
1554
+ className: "filter-form-list",
1555
+ name: "filters",
1556
+ copyIconProps: false,
1557
+ creatorButtonProps: false,
1558
+ deleteIconProps: false,
1559
+ itemContainerRender: (doms) => {
1560
+ return /* @__PURE__ */ React.createElement(ProForm.Group, null, doms);
1561
+ }
1562
+ },
1563
+ (_, listIndex, operator) => {
1564
+ return /* @__PURE__ */ React.createElement(Flex, { gap: 4, align: "center" }, /* @__PURE__ */ React.createElement(Flex, { className: "filter-form-item", gap: 16 }, /* @__PURE__ */ React.createElement(ProFormItem, { name: ["columnId"], className: "filter-item-id" }, /* @__PURE__ */ React.createElement(
1565
+ Select$1,
1566
+ {
1567
+ className: "filter-item-id-select",
1568
+ placeholder: "\u8BF7\u9009\u62E9\u5B57\u6BB5",
1569
+ options: filedOptions,
1570
+ popupMatchSelectWidth: 200,
1571
+ onChange: (columnId) => onListChange(listIndex, { columnId })
1572
+ }
1573
+ )), /* @__PURE__ */ React.createElement(ProFormDependency, { name: ["columnId"] }, ({ columnId }) => {
1574
+ return /* @__PURE__ */ React.createElement(
1575
+ FilterGroup,
1576
+ {
1577
+ form,
1578
+ columnId: columnId ?? columnData[0]?.id,
1579
+ columnData,
1580
+ listIndex,
1581
+ tools,
1582
+ operator
1583
+ }
1584
+ );
1585
+ })), /* @__PURE__ */ React.createElement(Button, { type: "text", size: "small", icon: /* @__PURE__ */ React.createElement(CloseOutlined, null), onClick: () => onDeleteList(listIndex) }));
1586
+ }
1587
+ ),
1588
+ /* @__PURE__ */ React.createElement(Flex, { align: "center", gap: 8, className: "filter-action-wrap" }, /* @__PURE__ */ React.createElement(Button, { type: "text", icon: /* @__PURE__ */ React.createElement(PlusOutlined, null), size: "small", onClick: onAddList }, "\u6DFB\u52A0\u6761\u4EF6"))
1589
+ );
1590
+ };
1591
+
1592
+ const FilterModal = ({
1593
+ offsetParent,
1594
+ width = 520,
1595
+ isOpen = false,
1596
+ position,
1597
+ initialValues,
1598
+ columnData,
1599
+ tools,
1600
+ onClose,
1601
+ onPositionChange,
1602
+ onValuesChange
1603
+ }) => {
1604
+ return /* @__PURE__ */ React.createElement(
1605
+ BaseModal,
1606
+ {
1607
+ offsetParent,
1608
+ width,
1609
+ open: isOpen,
1610
+ onClose,
1611
+ value: position,
1612
+ onChange: onPositionChange,
1613
+ className: "filter-modal"
1614
+ },
1615
+ /* @__PURE__ */ React.createElement(Flex, { className: "filter-container", onClick: (e) => e.stopPropagation() }, /* @__PURE__ */ React.createElement(
1616
+ TableFilterForm,
1617
+ {
1618
+ initialValues,
1619
+ columnData,
1620
+ tools,
1621
+ onValuesChange
1622
+ }
1623
+ ))
1624
+ );
1625
+ };
1626
+
1627
+ const dateValues = [
1628
+ "today",
1629
+ "tomorrow",
1630
+ "yesterday",
1631
+ "this_week",
1632
+ "last_week",
1633
+ "this_month",
1634
+ "last_month",
1635
+ "last_7_days",
1636
+ "next_7_days",
1637
+ "last_30_days",
1638
+ "next_30_days"
1639
+ ];
1640
+ function resolveDateRange(value, now = dayjs()) {
1641
+ switch (value) {
1642
+ case "today": {
1643
+ return [now.startOf("day"), now.endOf("day")];
1644
+ }
1645
+ case "tomorrow": {
1646
+ const d = now.add(1, "day");
1647
+ return [d.startOf("day"), d.endOf("day")];
1648
+ }
1649
+ case "yesterday": {
1650
+ const d = now.subtract(1, "day");
1651
+ return [d.startOf("day"), d.endOf("day")];
1652
+ }
1653
+ case "this_week": {
1654
+ return [now.startOf("week"), now.endOf("week")];
1655
+ }
1656
+ case "last_week": {
1657
+ const d = now.subtract(1, "week");
1658
+ return [d.startOf("week"), d.endOf("week")];
1659
+ }
1660
+ case "this_month": {
1661
+ return [now.startOf("month"), now.endOf("month")];
1662
+ }
1663
+ case "last_month": {
1664
+ const d = now.subtract(1, "month");
1665
+ return [d.startOf("month"), d.endOf("month")];
1666
+ }
1667
+ case "last_7_days": {
1668
+ return [now.subtract(7, "day").startOf("day"), now.endOf("day")];
1669
+ }
1670
+ case "next_7_days": {
1671
+ return [now.startOf("day"), now.add(7, "day").endOf("day")];
1672
+ }
1673
+ case "last_30_days": {
1674
+ return [now.subtract(30, "day").startOf("day"), now.endOf("day")];
1675
+ }
1676
+ case "next_30_days": {
1677
+ return [now.startOf("day"), now.add(30, "day").endOf("day")];
1678
+ }
1679
+ case "specific":
1680
+ default:
1681
+ return void 0;
1682
+ }
1683
+ }
1684
+
1685
+ const MARGIN = 8;
1686
+ const defaultAdjustOptions = {
1687
+ left: true,
1688
+ right: true,
1689
+ top: true,
1690
+ bottom: true
1691
+ };
1692
+ function adjustPositionToViewport(position, modalRect, options = {}) {
1693
+ const { left, right, top, bottom } = {
1694
+ ...defaultAdjustOptions,
1695
+ ...options
1696
+ };
1697
+ const viewportWidth = window.innerWidth;
1698
+ const viewportHeight = window.innerHeight;
1699
+ let x = position.x;
1700
+ let y = position.y;
1701
+ if (right && x + modalRect.width > viewportWidth - MARGIN) {
1702
+ x = viewportWidth - modalRect.width - MARGIN;
1703
+ }
1704
+ if (left && x < MARGIN) {
1705
+ x = MARGIN;
1706
+ }
1707
+ if (bottom && y + modalRect.height > viewportHeight - MARGIN) {
1708
+ y = viewportHeight - modalRect.height - MARGIN;
1709
+ }
1710
+ if (top && y < MARGIN) {
1711
+ y = MARGIN;
1712
+ }
1713
+ return { x, y };
1714
+ }
1715
+ const useModalPosition = (contentRef, position, setPosition, adjustOptions) => {
1716
+ useLayoutEffect(() => {
1717
+ if (!contentRef.current) return;
1718
+ const adjustPosition = () => {
1719
+ const rect = contentRef.current.getBoundingClientRect();
1720
+ const adjusted = adjustPositionToViewport(position, rect, adjustOptions);
1721
+ if (adjusted.x !== position.x || adjusted.y !== position.y) {
1722
+ setPosition(adjusted);
1723
+ }
1724
+ };
1725
+ adjustPosition();
1726
+ const resizeObserver = new ResizeObserver(() => {
1727
+ adjustPosition();
1728
+ });
1729
+ resizeObserver.observe(contentRef.current);
1730
+ return () => {
1731
+ resizeObserver.disconnect();
1732
+ };
1733
+ }, [contentRef, position, setPosition, adjustOptions]);
1734
+ };
1735
+
1736
+ const TableMenuModal = ({
1737
+ offsetParent,
1738
+ width = 180,
1739
+ isOpen = false,
1740
+ position,
1741
+ menuItems,
1742
+ onClose,
1743
+ onClick,
1744
+ onPositionChange
1745
+ }) => {
1746
+ const contentRef = useRef(null);
1747
+ useModalPosition(contentRef, position, onPositionChange);
1748
+ return /* @__PURE__ */ React.createElement(
1749
+ BaseModal,
1750
+ {
1751
+ offsetParent,
1752
+ width,
1753
+ open: isOpen,
1754
+ onClose,
1755
+ value: position,
1756
+ onChange: onPositionChange,
1757
+ className: "table-menu-modal"
1758
+ },
1759
+ /* @__PURE__ */ React.createElement(Flex, { className: "menu-container", gap: 6, ref: contentRef }, /* @__PURE__ */ React.createElement(
1760
+ Menu,
1761
+ {
1762
+ selectable: false,
1763
+ onClick,
1764
+ mode: "vertical",
1765
+ items: menuItems,
1766
+ getPopupContainer: (node) => node.parentNode
1767
+ }
1768
+ ))
1769
+ );
1770
+ };
1771
+
1772
+ export { AttachmentEditor, AttachmentPreview, Dashboard, DashboardItem$1 as DashboardItem, DetailsDrawer, DrawerForm, DrawerTitle, FeatureRowField, FeatureRowFields, FilterDatePicker, FilterDateSelect, FilterFileSelect, FilterGroup, FilterInput, FilterModal, FilterNumber, FilterOperator, FilterPeopleSelect, FilterSelect, FilterTitle, FilterTypeSelect, FilterValue, FormAvatar, FormDateTimePicker, FormInputNumber, FormJsonEditor, FormLinkEdit, FormMultiSelect, FormSingleSelect, FormTimeLen, FormUpload, JsonEditor, ObjectURLManager, Operator, RowLabel, SearchModal, TableFilterForm, TableMenuModal, adjustPositionToViewport, dateValues, findAvailablePosition, generateCrossDashedBase64, getDateBySpecific, getDateRangeByOption, resolveDateRange, useDetailsDrawerContext, useModalPosition };