bi-sdk-react 0.0.1

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.
Files changed (146) hide show
  1. package/dist/es/css/bi-sdk.css +1 -0
  2. package/dist/es/js/bi-sdk.es.js +795 -0
  3. package/dist/es/js/bi-sdk.es.js.LICENSE.txt +9 -0
  4. package/dist/types/components/PageDesigner.d.ts +10 -0
  5. package/dist/types/components/context/DesignerContext.d.ts +15 -0
  6. package/dist/types/components/context/EnvContext.d.ts +15 -0
  7. package/dist/types/components/dnd/DropContainer.d.ts +21 -0
  8. package/dist/types/components/example.d.ts +225 -0
  9. package/dist/types/components/hooks/datasource.d.ts +1 -0
  10. package/dist/types/components/hooks/useDeepCompareEffect.d.ts +1 -0
  11. package/dist/types/components/icon/IconFont.d.ts +6 -0
  12. package/dist/types/components/index.d.ts +10 -0
  13. package/dist/types/components/layout/PageCanvas.d.ts +6 -0
  14. package/dist/types/components/layout/PageItem.d.ts +27 -0
  15. package/dist/types/components/panel/AiPanel.d.ts +8 -0
  16. package/dist/types/components/panel/CascadePanel.d.ts +2 -0
  17. package/dist/types/components/panel/ChatInput.d.ts +28 -0
  18. package/dist/types/components/panel/CodePanel.d.ts +2 -0
  19. package/dist/types/components/panel/ComponentPanel.d.ts +2 -0
  20. package/dist/types/components/panel/DatasourcePanel.d.ts +2 -0
  21. package/dist/types/components/panel/LayerPanel.d.ts +2 -0
  22. package/dist/types/components/panel/PaneHeader.d.ts +6 -0
  23. package/dist/types/components/panel/PropertiesPanel.d.ts +2 -0
  24. package/dist/types/components/panel/ScriptPanel.d.ts +2 -0
  25. package/dist/types/components/panel/VariablesPanel.d.ts +2 -0
  26. package/dist/types/components/panel/index.d.ts +9 -0
  27. package/dist/types/components/plugins/@antd/index.d.ts +22 -0
  28. package/dist/types/components/plugins/@antd/item-props/ButtonProps.d.ts +8 -0
  29. package/dist/types/components/plugins/@antd/item-props/CapsuleProps.d.ts +13 -0
  30. package/dist/types/components/plugins/@antd/item-props/CardProps.d.ts +8 -0
  31. package/dist/types/components/plugins/@antd/item-props/CheckboxProps.d.ts +13 -0
  32. package/dist/types/components/plugins/@antd/item-props/ColProps.d.ts +12 -0
  33. package/dist/types/components/plugins/@antd/item-props/DatePickerProps.d.ts +8 -0
  34. package/dist/types/components/plugins/@antd/item-props/EchartsProps.d.ts +6 -0
  35. package/dist/types/components/plugins/@antd/item-props/HtmlProps.d.ts +6 -0
  36. package/dist/types/components/plugins/@antd/item-props/ImageProps.d.ts +10 -0
  37. package/dist/types/components/plugins/@antd/item-props/InputNumberProps.d.ts +9 -0
  38. package/dist/types/components/plugins/@antd/item-props/InputProps.d.ts +10 -0
  39. package/dist/types/components/plugins/@antd/item-props/ListProps.d.ts +10 -0
  40. package/dist/types/components/plugins/@antd/item-props/PageHeaderProps.d.ts +7 -0
  41. package/dist/types/components/plugins/@antd/item-props/RowProps.d.ts +6 -0
  42. package/dist/types/components/plugins/@antd/item-props/SelectProps.d.ts +16 -0
  43. package/dist/types/components/plugins/@antd/item-props/SpaceProps.d.ts +7 -0
  44. package/dist/types/components/plugins/@antd/item-props/SwitchProps.d.ts +6 -0
  45. package/dist/types/components/plugins/@antd/item-props/TableProps.d.ts +15 -0
  46. package/dist/types/components/plugins/@antd/item-props/TextProps.d.ts +6 -0
  47. package/dist/types/components/plugins/@antd/item-props/index.d.ts +19 -0
  48. package/dist/types/components/plugins/@antd/item-props/types.d.ts +5 -0
  49. package/dist/types/components/plugins/@antd/items/ButtonRender.d.ts +14 -0
  50. package/dist/types/components/plugins/@antd/items/CapsuleRender.d.ts +14 -0
  51. package/dist/types/components/plugins/@antd/items/CardRender.d.ts +12 -0
  52. package/dist/types/components/plugins/@antd/items/CheckboxRender.d.ts +14 -0
  53. package/dist/types/components/plugins/@antd/items/ColRender.d.ts +13 -0
  54. package/dist/types/components/plugins/@antd/items/DatePickerRender.d.ts +14 -0
  55. package/dist/types/components/plugins/@antd/items/EchartsRender.d.ts +10 -0
  56. package/dist/types/components/plugins/@antd/items/HtmlRender.d.ts +20 -0
  57. package/dist/types/components/plugins/@antd/items/ImageRender.d.ts +10 -0
  58. package/dist/types/components/plugins/@antd/items/InputNumberRender.d.ts +17 -0
  59. package/dist/types/components/plugins/@antd/items/InputRender.d.ts +17 -0
  60. package/dist/types/components/plugins/@antd/items/ListRender.d.ts +11 -0
  61. package/dist/types/components/plugins/@antd/items/PageHeaderRender.d.ts +8 -0
  62. package/dist/types/components/plugins/@antd/items/RowRender.d.ts +13 -0
  63. package/dist/types/components/plugins/@antd/items/SelectRender.d.ts +18 -0
  64. package/dist/types/components/plugins/@antd/items/SpaceRender.d.ts +10 -0
  65. package/dist/types/components/plugins/@antd/items/SwitchRender.d.ts +14 -0
  66. package/dist/types/components/plugins/@antd/items/TableRender.d.ts +12 -0
  67. package/dist/types/components/plugins/@antd/items/TextRender.d.ts +9 -0
  68. package/dist/types/components/plugins/@antd/items/index.d.ts +19 -0
  69. package/dist/types/components/typing.d.ts +80 -0
  70. package/dist/types/components/utils.d.ts +9 -0
  71. package/dist/types/example.d.ts +2 -0
  72. package/dist/types/index.d.ts +1 -0
  73. package/dist/umd/css/bi-sdk.css +1 -0
  74. package/dist/umd/js/bi-sdk.umd.min.js +795 -0
  75. package/dist/umd/js/bi-sdk.umd.min.js.LICENSE.txt +9 -0
  76. package/package.json +52 -0
  77. package/src/components/PageDesigner.tsx +509 -0
  78. package/src/components/context/DesignerContext.tsx +71 -0
  79. package/src/components/context/EnvContext.tsx +42 -0
  80. package/src/components/dnd/DropContainer.tsx +474 -0
  81. package/src/components/example.ts +577 -0
  82. package/src/components/hooks/datasource.ts +23 -0
  83. package/src/components/hooks/useDeepCompareEffect.ts +12 -0
  84. package/src/components/icon/IconFont.tsx +16 -0
  85. package/src/components/index.ts +29 -0
  86. package/src/components/layout/PageCanvas.tsx +145 -0
  87. package/src/components/layout/PageItem.tsx +182 -0
  88. package/src/components/panel/AiPanel.tsx +116 -0
  89. package/src/components/panel/CascadePanel.tsx +163 -0
  90. package/src/components/panel/ChatInput.tsx +550 -0
  91. package/src/components/panel/CodePanel.tsx +48 -0
  92. package/src/components/panel/ComponentPanel.tsx +119 -0
  93. package/src/components/panel/DatasourcePanel.tsx +419 -0
  94. package/src/components/panel/LayerPanel.tsx +238 -0
  95. package/src/components/panel/PaneHeader.tsx +34 -0
  96. package/src/components/panel/PropertiesPanel.tsx +394 -0
  97. package/src/components/panel/ScriptPanel.tsx +175 -0
  98. package/src/components/panel/VariablesPanel.tsx +153 -0
  99. package/src/components/panel/index.ts +9 -0
  100. package/src/components/plugins/@antd/index.ts +445 -0
  101. package/src/components/plugins/@antd/item-props/ButtonProps.tsx +91 -0
  102. package/src/components/plugins/@antd/item-props/CapsuleProps.tsx +102 -0
  103. package/src/components/plugins/@antd/item-props/CardProps.tsx +58 -0
  104. package/src/components/plugins/@antd/item-props/CheckboxProps.tsx +75 -0
  105. package/src/components/plugins/@antd/item-props/ColProps.tsx +43 -0
  106. package/src/components/plugins/@antd/item-props/DatePickerProps.tsx +72 -0
  107. package/src/components/plugins/@antd/item-props/EchartsProps.tsx +35 -0
  108. package/src/components/plugins/@antd/item-props/HtmlProps.tsx +61 -0
  109. package/src/components/plugins/@antd/item-props/ImageProps.tsx +43 -0
  110. package/src/components/plugins/@antd/item-props/InputNumberProps.tsx +18 -0
  111. package/src/components/plugins/@antd/item-props/InputProps.tsx +89 -0
  112. package/src/components/plugins/@antd/item-props/ListProps.tsx +69 -0
  113. package/src/components/plugins/@antd/item-props/PageHeaderProps.tsx +20 -0
  114. package/src/components/plugins/@antd/item-props/RowProps.tsx +21 -0
  115. package/src/components/plugins/@antd/item-props/SelectProps.tsx +136 -0
  116. package/src/components/plugins/@antd/item-props/SpaceProps.tsx +52 -0
  117. package/src/components/plugins/@antd/item-props/SwitchProps.tsx +17 -0
  118. package/src/components/plugins/@antd/item-props/TableProps.tsx +200 -0
  119. package/src/components/plugins/@antd/item-props/TextProps.tsx +41 -0
  120. package/src/components/plugins/@antd/item-props/index.ts +20 -0
  121. package/src/components/plugins/@antd/item-props/types.ts +6 -0
  122. package/src/components/plugins/@antd/items/ButtonRender.tsx +63 -0
  123. package/src/components/plugins/@antd/items/CapsuleRender.tsx +49 -0
  124. package/src/components/plugins/@antd/items/CardRender.tsx +119 -0
  125. package/src/components/plugins/@antd/items/CheckboxRender.tsx +56 -0
  126. package/src/components/plugins/@antd/items/ColRender.tsx +78 -0
  127. package/src/components/plugins/@antd/items/DatePickerRender.tsx +117 -0
  128. package/src/components/plugins/@antd/items/EchartsRender.tsx +98 -0
  129. package/src/components/plugins/@antd/items/HtmlRender.tsx +74 -0
  130. package/src/components/plugins/@antd/items/ImageRender.tsx +37 -0
  131. package/src/components/plugins/@antd/items/InputNumberRender.tsx +57 -0
  132. package/src/components/plugins/@antd/items/InputRender.tsx +75 -0
  133. package/src/components/plugins/@antd/items/ListRender.tsx +227 -0
  134. package/src/components/plugins/@antd/items/PageHeaderRender.tsx +74 -0
  135. package/src/components/plugins/@antd/items/RowRender.tsx +47 -0
  136. package/src/components/plugins/@antd/items/SelectRender.tsx +53 -0
  137. package/src/components/plugins/@antd/items/SpaceRender.tsx +54 -0
  138. package/src/components/plugins/@antd/items/SwitchRender.tsx +52 -0
  139. package/src/components/plugins/@antd/items/TableRender.tsx +99 -0
  140. package/src/components/plugins/@antd/items/TextRender.tsx +25 -0
  141. package/src/components/plugins/@antd/items/index.ts +20 -0
  142. package/src/components/styles.css +12 -0
  143. package/src/components/typing.ts +80 -0
  144. package/src/components/utils.ts +37 -0
  145. package/src/example.tsx +136 -0
  146. package/src/index.tsx +17 -0
@@ -0,0 +1,474 @@
1
+ import { dropTargetForElements } from "@atlaskit/pragmatic-drag-and-drop/element/adapter";
2
+ import React, { useContext, useEffect, useMemo, useRef, useState } from "react";
3
+ import { PageItem, PageItemProps } from "../layout/PageItem";
4
+ import { HtmlBaseProps, SchemaItemType } from "../typing";
5
+ import styled from "styled-components";
6
+ import { DesignerContext } from "../context/DesignerContext";
7
+
8
+ type DropContainerProps = {
9
+ rootComponent?: React.ComponentType<any>;
10
+ rootData?: Record<string, any>;
11
+ list?: SchemaItemType[];
12
+ item?: any;
13
+ onListChange?: (list: any[]) => void;
14
+ } & HtmlBaseProps;
15
+
16
+ const DropIndicator = styled.div<{
17
+ $offset: number;
18
+ $visible: boolean;
19
+ $vertical?: boolean;
20
+ }>`
21
+ position: absolute;
22
+ background: #1890ff;
23
+ pointer-events: none;
24
+ opacity: ${(props) => (props.$visible ? 1 : 0)};
25
+ transition: all 0.1s ease-out;
26
+
27
+ /* Horizontal bar (Vertical list) */
28
+ ${(props) =>
29
+ !props.$vertical &&
30
+ `
31
+ left: 8px;
32
+ right: 8px;
33
+ height: 2px;
34
+ top: ${props.$offset}px;
35
+ `}
36
+
37
+ /* Vertical bar (Horizontal list) */
38
+ ${(props) =>
39
+ props.$vertical &&
40
+ `
41
+ top: 8px;
42
+ bottom: 8px;
43
+ width: 2px;
44
+ left: ${props.$offset}px;
45
+ `}
46
+
47
+ &::before,
48
+ &::after {
49
+ content: "";
50
+ position: absolute;
51
+ width: 8px;
52
+ height: 8px;
53
+ background: #1890ff;
54
+ border-radius: 50%;
55
+ }
56
+
57
+ ${(props) =>
58
+ !props.$vertical &&
59
+ `
60
+ &::before { top: -3px; left: -4px; }
61
+ &::after { top: -3px; right: -4px; }
62
+ `}
63
+
64
+ ${(props) =>
65
+ props.$vertical &&
66
+ `
67
+ &::before { left: -3px; top: -4px; }
68
+ &::after { left: -3px; bottom: -4px; }
69
+ `}
70
+ `;
71
+
72
+ // 判断容器是否为水平布局
73
+ const isHorizontalLayout = (el: HTMLElement) => {
74
+ const children = Array.from(el.children).filter(
75
+ (child) =>
76
+ !child.classList.contains("drop-indicator") &&
77
+ child.getBoundingClientRect().width > 0
78
+ ) as HTMLElement[];
79
+
80
+ if (children.length === 0) {
81
+ const style = window.getComputedStyle(el);
82
+ const { display, flexDirection } = style;
83
+
84
+ if (display === 'flex' || display === 'inline-flex') {
85
+ return !flexDirection.includes('column');
86
+ }
87
+ if (display === 'grid') {
88
+ return !style.gridAutoFlow.includes('column');
89
+ }
90
+ return false;
91
+ }
92
+
93
+ if (children.length === 1) {
94
+ const childStyle = window.getComputedStyle(children[0]);
95
+ const elStyle = window.getComputedStyle(el);
96
+
97
+ if (elStyle.display === 'flex' || elStyle.display === 'inline-flex') {
98
+ return !elStyle.flexDirection.includes('column');
99
+ }
100
+ if (['inline', 'inline-block', 'inline-flex'].includes(childStyle.display)) return true;
101
+ if (childStyle.float !== 'none') return true;
102
+ return false;
103
+ }
104
+
105
+ // 使用方差(标准差)判断主轴
106
+ let sumX = 0, sumY = 0;
107
+ const centers = children.map(c => {
108
+ const r = c.getBoundingClientRect();
109
+ const cx = r.left + r.width/2;
110
+ const cy = r.top + r.height/2;
111
+ sumX += cx;
112
+ sumY += cy;
113
+ return { cx, cy };
114
+ });
115
+
116
+ const avgX = sumX / children.length;
117
+ const avgY = sumY / children.length;
118
+
119
+ let varX = 0, varY = 0;
120
+ centers.forEach(c => {
121
+ varX += (c.cx - avgX) ** 2;
122
+ varY += (c.cy - avgY) ** 2;
123
+ });
124
+
125
+ return varX >= varY;
126
+ };
127
+
128
+ // 计算拖放位置的索引和指示器位置
129
+ const getDropInfo = (
130
+ dropElement: HTMLElement,
131
+ clientX: number,
132
+ clientY: number,
133
+ list: any[]
134
+ ): { index: number; indicatorOffset: number; isHorizontal: boolean } => {
135
+ const containerRect = dropElement.getBoundingClientRect();
136
+ const children = Array.from(dropElement.children).filter(
137
+ (child) =>
138
+ !child.classList.contains("drop-indicator") &&
139
+ child.getBoundingClientRect().width > 0
140
+ ) as HTMLElement[];
141
+
142
+ const isHorizontal = isHorizontalLayout(dropElement);
143
+ const scrollLeft = dropElement.scrollLeft;
144
+ const scrollTop = dropElement.scrollTop;
145
+
146
+ if (children.length === 0) {
147
+ return { index: 0, indicatorOffset: 15, isHorizontal }; // padding offset
148
+ }
149
+
150
+ // 辅助函数:计算指示器位置
151
+ const getIndicatorOffset = (childRect: DOMRect, before: boolean) => {
152
+ if (isHorizontal) {
153
+ const visualOffset = before ? childRect.left : childRect.right;
154
+ return visualOffset - containerRect.left + scrollLeft + (before ? -4 : 4);
155
+ } else {
156
+ const visualOffset = before ? childRect.top : childRect.bottom;
157
+ return visualOffset - containerRect.top + scrollTop + (before ? -4 : 4);
158
+ }
159
+ };
160
+
161
+ // 辅助函数:获取元素在列表中的索引
162
+ const getElementIndex = (el: HTMLElement) => {
163
+ const id = el.getAttribute('data-item-id');
164
+ if (id) {
165
+ const idx = list.findIndex(i => i.id === id);
166
+ if (idx !== -1) return idx;
167
+ }
168
+ // 回退到 DOM 索引(可能不准确,但在找不到 ID 时作为备选)
169
+ return children.indexOf(el);
170
+ };
171
+
172
+ // 1. 尝试直接命中检测 (Hit Test)
173
+ for (let i = 0; i < children.length; i++) {
174
+ const rect = children[i].getBoundingClientRect();
175
+ // 增加一点 hit buffer,使得容易命中元素
176
+ const buffer = 5;
177
+ if (
178
+ clientX >= rect.left - buffer &&
179
+ clientX <= rect.right + buffer &&
180
+ clientY >= rect.top - buffer &&
181
+ clientY <= rect.bottom + buffer
182
+ ) {
183
+ // 命中了某个元素,根据中线判断前后
184
+ const isBefore = isHorizontal
185
+ ? clientX < rect.left + rect.width / 2
186
+ : clientY < rect.top + rect.height / 2;
187
+
188
+ const currentIdx = getElementIndex(children[i]);
189
+ return {
190
+ index: isBefore ? currentIdx : currentIdx + 1,
191
+ indicatorOffset: getIndicatorOffset(rect, isBefore),
192
+ isHorizontal,
193
+ };
194
+ }
195
+ }
196
+
197
+ // 2. 未命中任何元素,寻找最近的元素 (Nearest Neighbor)
198
+ let minDistance = Infinity;
199
+ let bestIndex = -1;
200
+ let isBeforeBest = false;
201
+ let bestRect: DOMRect | null = null;
202
+ let bestEl: HTMLElement | null = null;
203
+
204
+ for (let i = 0; i < children.length; i++) {
205
+ const rect = children[i].getBoundingClientRect();
206
+ // 计算中心点距离
207
+ const cx = rect.left + rect.width / 2;
208
+ const cy = rect.top + rect.height / 2;
209
+ const dist = (clientX - cx) ** 2 + (clientY - cy) ** 2;
210
+
211
+ if (dist < minDistance) {
212
+ minDistance = dist;
213
+ bestIndex = i; // 暂时存储 DOM index
214
+ bestRect = rect;
215
+ bestEl = children[i];
216
+ // 记录相对于该元素的方位
217
+ isBeforeBest = isHorizontal
218
+ ? clientX < cx
219
+ : clientY < cy;
220
+ }
221
+ }
222
+
223
+ if (bestEl && bestRect) {
224
+ const currentIdx = getElementIndex(bestEl);
225
+ return {
226
+ index: isBeforeBest ? currentIdx : currentIdx + 1,
227
+ indicatorOffset: getIndicatorOffset(bestRect, isBeforeBest),
228
+ isHorizontal,
229
+ };
230
+ }
231
+
232
+ // 3. 兜底:放在最后
233
+ const lastRect = children[children.length - 1].getBoundingClientRect();
234
+ return {
235
+ index: list.length,
236
+ indicatorOffset: getIndicatorOffset(lastRect, false),
237
+ isHorizontal,
238
+ };
239
+ };
240
+
241
+ /**
242
+ * DropContainer 组件
243
+ * 用途:容器级拖拽放置,合并父级 forwardedRef 与 react-dnd 的 drop 连接器
244
+ * 参数:
245
+ * - enabled:是否启用拖拽放置
246
+ * - rootComponent:容器根组件类型
247
+ * - rootData:根组件属性
248
+ * - list:容器内子项列表
249
+ * - 其余同 HtmlBaseProps
250
+ */
251
+ export const DropContainer: React.FC<DropContainerProps> = ({
252
+ rootComponent: RootComponent = "div",
253
+ rootData = {},
254
+ list = [],
255
+ item,
256
+ onListChange,
257
+ className,
258
+ }) => {
259
+ const {
260
+ designable,
261
+ selectedItem,
262
+ setSelectedItem: onSelect,
263
+ forceUpdate,
264
+ } = useContext(DesignerContext);
265
+ const localRef = useRef<any | null>(null);
266
+ const [isOver, setIsOver] = useState(false);
267
+ const [indicatorOffset, setIndicatorOffset] = useState(0);
268
+ const [isVertical, setIsVertical] = useState(false);
269
+ const [showIndicator, setShowIndicator] = useState(false);
270
+
271
+ const listRef = useRef(list);
272
+ const onListChangeRef = useRef(onListChange);
273
+
274
+ useEffect(() => {
275
+ listRef.current = list;
276
+ onListChangeRef.current = onListChange;
277
+ }, [list, onListChange]);
278
+
279
+ const memoList = useMemo(() => list || [], [list]);
280
+
281
+ useEffect(() => {
282
+ const el = localRef.current;
283
+ if (!el) return;
284
+ const cleanup = dropTargetForElements({
285
+ element: el,
286
+ canDrop: ({ source, element }) => {
287
+ if (!designable) {
288
+ return false;
289
+ }
290
+ const { data }: Record<string, any> = source.data || {};
291
+ if (element.classList.contains("ant-row") && data?.type !== "b-col") {
292
+ return false;
293
+ }
294
+ if (!element.classList.contains("ant-row") && data?.type === "b-col") {
295
+ return false;
296
+ }
297
+ return true;
298
+ },
299
+ getData: () => ({ localList: listRef.current }),
300
+ onDragEnter: (args) => {
301
+ if (!designable) {
302
+ return false;
303
+ }
304
+ const [closestTarget] = args.location.current.dropTargets;
305
+ if (closestTarget?.element !== el) return;
306
+
307
+ setIsOver(true);
308
+ setShowIndicator(true);
309
+ const clientY = args.location.current.input.clientY;
310
+ const clientX = args.location.current.input.clientX;
311
+ const { indicatorOffset, isHorizontal } = getDropInfo(el, clientX, clientY, listRef.current || []);
312
+ setIndicatorOffset(indicatorOffset);
313
+ setIsVertical(isHorizontal);
314
+ },
315
+ onDrag: (args) => {
316
+ if (!designable) {
317
+ return false;
318
+ }
319
+ const [closestTarget] = args.location.current.dropTargets;
320
+ if (closestTarget?.element !== el) {
321
+ setIsOver(false);
322
+ setShowIndicator(false);
323
+ return;
324
+ }
325
+
326
+ setIsOver(true);
327
+ setShowIndicator(true);
328
+ const clientY = args.location.current.input.clientY;
329
+ const clientX = args.location.current.input.clientX;
330
+ const { indicatorOffset, isHorizontal } = getDropInfo(el, clientX, clientY, listRef.current || []);
331
+ setIndicatorOffset(indicatorOffset);
332
+ setIsVertical(isHorizontal);
333
+ },
334
+ onDragLeave: () => {
335
+ if (!designable) {
336
+ return false;
337
+ }
338
+ setIsOver(false);
339
+ setShowIndicator(false);
340
+ },
341
+ onDrop(args) {
342
+ if (!designable) {
343
+ return false;
344
+ }
345
+ const [closestTarget] = args.location.current.dropTargets;
346
+ if (closestTarget?.element !== el) return;
347
+
348
+ setIsOver(false);
349
+ setShowIndicator(false);
350
+ const {
351
+ data: sourceItem,
352
+ list: sourceList,
353
+ onListChange: onSourceListChange,
354
+ index: sourceIndex,
355
+ } = args.source.data as {
356
+ data: any;
357
+ list?: any[];
358
+ index?: number;
359
+ onListChange?: React.Dispatch<React.SetStateAction<any[]>>;
360
+ };
361
+ onSelect?.(sourceItem);
362
+
363
+ const currentList = listRef.current || [];
364
+ const currentOnListChange = onListChangeRef.current;
365
+
366
+ // 判断是否为同一个列表(引用相同或包含该元素)
367
+ const isSameList =
368
+ sourceList === currentList ||
369
+ currentList.some((i: any) => i.id === sourceItem.id);
370
+
371
+ const clientY = args.location.current.input.clientY;
372
+ const clientX = args.location.current.input.clientX;
373
+ const { index: dropIndex } = getDropInfo(el, clientX, clientY, currentList);
374
+
375
+ if (!sourceList) {
376
+ // 情况1: !parentList - 从外部拖入,在指定位置插入
377
+ const newList = [...currentList];
378
+ newList.splice(dropIndex, 0, sourceItem);
379
+ currentOnListChange?.(newList);
380
+ } else if (isSameList) {
381
+ // 情况3: list === parentList - 同列表内排序
382
+ const newList = [...currentList];
383
+ // 重新查找索引,防止闭包过时
384
+ const currentSourceIndex = newList.findIndex(
385
+ (i: any) => i.id === sourceItem.id
386
+ );
387
+
388
+ if (currentSourceIndex !== -1) {
389
+ const [removed] = newList.splice(currentSourceIndex, 1);
390
+ // 如果目标位置在源位置之后,需要调整索引
391
+ const adjustedIndex =
392
+ dropIndex > currentSourceIndex ? dropIndex - 1 : dropIndex;
393
+ newList.splice(adjustedIndex, 0, removed);
394
+ currentOnListChange?.(newList);
395
+ } else {
396
+ // 异常情况:未找到元素,当作新插入
397
+ newList.splice(dropIndex, 0, sourceItem);
398
+ currentOnListChange?.(newList);
399
+ }
400
+ } else {
401
+ // 情况2: list !== parentList - 跨容器移动
402
+
403
+ // 1. 从源列表移除
404
+ if (onSourceListChange && sourceList) {
405
+ const newSourceList = sourceList.filter(
406
+ (i: any) => i.id !== sourceItem.id
407
+ );
408
+ onSourceListChange(newSourceList);
409
+ }
410
+
411
+ // 2. 添加到目标列表
412
+ const newList = [...currentList];
413
+ // 防重复检查
414
+ if (!newList.some((i: any) => i.id === sourceItem.id)) {
415
+ newList.splice(dropIndex, 0, sourceItem);
416
+ currentOnListChange?.(newList);
417
+ }
418
+
419
+ // forceUpdate?.();
420
+ }
421
+ },
422
+ });
423
+ return () => cleanup();
424
+ }, []);
425
+
426
+ // Determine classes
427
+ const classes = [
428
+ className,
429
+ "drag-container",
430
+ "page-item-component",
431
+ !list.length ? "empty-container" : "",
432
+ selectedItem === item ? "selected" : "",
433
+ designable ? "designable" : "",
434
+ ]
435
+ .filter(Boolean)
436
+ .join(" ");
437
+
438
+ return (
439
+ <RootComponent
440
+ {...(rootData || {})}
441
+ ref={localRef}
442
+ className={classes}
443
+ style={{
444
+ ...(rootData as any)?.style,
445
+ position: "relative",
446
+ minHeight: designable ? 24 : undefined,
447
+ outline: isOver ? "1px dashed #1890ff" : undefined,
448
+ }}
449
+ >
450
+ <DropIndicator
451
+ className="drop-indicator"
452
+ $offset={indicatorOffset}
453
+ $vertical={isVertical}
454
+ $visible={showIndicator}
455
+ />
456
+ {((Array.isArray(memoList) ? memoList : []) as any[])
457
+ .filter(Boolean)
458
+ .map((item: any, index: number) => (
459
+ <PageItem
460
+ key={item.id || index}
461
+ item={item}
462
+ parentList={memoList}
463
+ onListChange={onListChange}
464
+ index={index}
465
+ onDelete={() =>
466
+ onListChange?.(memoList.filter((i: any) => i.id !== item.id))
467
+ }
468
+ />
469
+ ))}
470
+ </RootComponent>
471
+ );
472
+ };
473
+
474
+ export default DropContainer;