@nocobase/flow-engine 2.0.0-alpha.3 → 2.0.0-alpha.5

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.
@@ -1,379 +0,0 @@
1
- /**
2
- * This file is part of the NocoBase (R) project.
3
- * Copyright (c) 2020-2024 NocoBase Co., Ltd.
4
- * Authors: NocoBase Team.
5
- *
6
- * This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
7
- * For more information, please refer to: https://www.nocobase.com/agreement.
8
- */
9
-
10
- import { uid } from '@formily/shared';
11
- import _ from 'lodash';
12
- import { ElementPosition } from './getMousePositionOnElement';
13
-
14
- type MoveDirection =
15
- | 'insert-row-above'
16
- | 'insert-row-below'
17
- | 'insert-same-column-above'
18
- | 'insert-same-column-below'
19
- | 'insert-column-left'
20
- | 'insert-column-right';
21
-
22
- interface GridLayoutData {
23
- rows: Record<string, string[][]>;
24
- sizes: Record<string, number[]>;
25
- }
26
-
27
- export const findModelUidPosition = (uid: string, rows: Record<string, string[][]>) => {
28
- // 找到 sourceUid 和 targetUid 的位置
29
- let result: { rowId: string; columnIndex: number; itemIndex: number } | null = null;
30
-
31
- for (const [rowId, columns] of Object.entries(rows)) {
32
- for (let columnIndex = 0; columnIndex < columns.length; columnIndex++) {
33
- const column = columns[columnIndex];
34
- for (let itemIndex = 0; itemIndex < column.length; itemIndex++) {
35
- if (column[itemIndex] === uid) {
36
- result = { rowId, columnIndex, itemIndex };
37
- }
38
- }
39
- }
40
- }
41
-
42
- return result;
43
- };
44
-
45
- export const moveBlock = ({
46
- sourceUid,
47
- targetUid,
48
- direction,
49
- layoutData,
50
- }: {
51
- sourceUid: string;
52
- targetUid: string;
53
- direction: MoveDirection;
54
- layoutData: GridLayoutData;
55
- }): GridLayoutData => {
56
- const { rows, sizes } = layoutData;
57
- const newSizes = _.cloneDeep(sizes);
58
- let newRows = _.cloneDeep(rows);
59
-
60
- // 找到 sourceUid 和 targetUid 的位置
61
- let sourcePosition: { rowId: string; columnIndex: number; itemIndex: number } | null = null;
62
- let targetPosition: { rowId: string; columnIndex: number; itemIndex: number } | null = null;
63
-
64
- for (const [rowId, columns] of Object.entries(newRows)) {
65
- for (let columnIndex = 0; columnIndex < columns.length; columnIndex++) {
66
- const column = columns[columnIndex];
67
- for (let itemIndex = 0; itemIndex < column.length; itemIndex++) {
68
- if (column[itemIndex] === sourceUid) {
69
- sourcePosition = { rowId, columnIndex, itemIndex };
70
- }
71
- if (column[itemIndex] === targetUid) {
72
- targetPosition = { rowId, columnIndex, itemIndex };
73
- }
74
- }
75
- }
76
- }
77
-
78
- if (!sourcePosition || !targetPosition) {
79
- return layoutData; // 如果没有找到 sourceUid 或 targetUid,直接返回原数据
80
- }
81
-
82
- const removeOldSourceUid = () => {
83
- // 从原位置移除 sourceUid
84
- newRows[sourcePosition.rowId][sourcePosition.columnIndex].splice(sourcePosition.itemIndex, 1);
85
-
86
- // 如果列变空了,移除该列
87
- if (newRows[sourcePosition.rowId][sourcePosition.columnIndex].length === 0) {
88
- newRows[sourcePosition.rowId]?.splice(sourcePosition.columnIndex, 1);
89
- newSizes[sourcePosition.rowId]?.splice(sourcePosition.columnIndex, 1);
90
- }
91
-
92
- // 如果行变空了,移除该行
93
- if (newRows[sourcePosition.rowId].length === 0) {
94
- delete newRows[sourcePosition.rowId];
95
- delete newSizes[sourcePosition.rowId];
96
- }
97
-
98
- // 移除完之后,重新设置 sourcePosition 和 targetPosition
99
- for (const [rowId, columns] of Object.entries(newRows)) {
100
- for (let columnIndex = 0; columnIndex < columns.length; columnIndex++) {
101
- const column = columns[columnIndex];
102
- for (let itemIndex = 0; itemIndex < column.length; itemIndex++) {
103
- if (column[itemIndex] === sourceUid) {
104
- sourcePosition = { rowId, columnIndex, itemIndex };
105
- }
106
- if (column[itemIndex] === targetUid) {
107
- targetPosition = { rowId, columnIndex, itemIndex };
108
- }
109
- }
110
- }
111
- }
112
- };
113
-
114
- switch (direction) {
115
- case 'insert-row-above': {
116
- // 检查 targetUid 上面是否有其他同列的 uid
117
- if (targetPosition.itemIndex > 0) {
118
- return layoutData; // 无效操作,直接返回原数据
119
- }
120
-
121
- removeOldSourceUid();
122
-
123
- // 创建新行 ID
124
- const newRowId = uid();
125
-
126
- // 插入到目标行前面
127
- newRows = insertKey({
128
- obj: newRows,
129
- newKey: newRowId,
130
- newValue: [[sourceUid]],
131
- referenceKey: targetPosition.rowId,
132
- position: 'before',
133
- });
134
- newSizes[newRowId] = [24];
135
- break;
136
- }
137
-
138
- case 'insert-row-below': {
139
- // 检查 targetUid 所在行的后面是否有同名的 sourceUid
140
- const nextRowKey = getNextKey(newRows, targetPosition.rowId);
141
-
142
- if (nextRowKey === sourcePosition.rowId && newRows[nextRowKey].length === 1) {
143
- return layoutData; // 无效操作,直接返回原数据
144
- }
145
-
146
- removeOldSourceUid();
147
-
148
- // 创建新行ID
149
- const newRowId = uid();
150
-
151
- // 插入到目标行后面
152
- newRows = insertKey({
153
- obj: newRows,
154
- newKey: newRowId,
155
- newValue: [[sourceUid]],
156
- referenceKey: targetPosition.rowId,
157
- position: 'after',
158
- });
159
- newSizes[newRowId] = [24];
160
- break;
161
- }
162
-
163
- case 'insert-same-column-above': {
164
- removeOldSourceUid();
165
-
166
- const column = newRows[targetPosition.rowId][targetPosition.columnIndex];
167
- if (column[targetPosition.itemIndex - 1] === sourceUid) {
168
- return layoutData; // 无效操作,直接返回原数据
169
- }
170
- column.splice(targetPosition.itemIndex, 0, sourceUid);
171
- break;
172
- }
173
-
174
- case 'insert-same-column-below': {
175
- removeOldSourceUid();
176
-
177
- const column = newRows[targetPosition.rowId][targetPosition.columnIndex];
178
- if (column[targetPosition.itemIndex + 1] === sourceUid) {
179
- return layoutData; // 无效操作,直接返回原数据
180
- }
181
- column.splice(targetPosition.itemIndex + 1, 0, sourceUid);
182
- break;
183
- }
184
-
185
- case 'insert-column-left': {
186
- removeOldSourceUid();
187
-
188
- // 在目标列左侧插入新列
189
- newRows[targetPosition.rowId].splice(targetPosition.columnIndex, 0, [sourceUid]);
190
-
191
- // 重新计算列宽
192
- const currentSizes = newSizes[targetPosition.rowId] || [];
193
- const columnCount = newRows[targetPosition.rowId].length || 0;
194
- const newColumnWidth = Math.floor(24 / columnCount);
195
- const remainingWidth = 24 - newColumnWidth;
196
-
197
- // 按原比例分配剩余宽度
198
- const totalOldWidth = currentSizes.reduce((sum, size) => sum + size, 0);
199
- const adjustedSizes = currentSizes.map((size) => Math.floor((size / totalOldWidth) * remainingWidth));
200
-
201
- // 插入新列宽度
202
- adjustedSizes.splice(targetPosition.columnIndex, 0, newColumnWidth);
203
-
204
- // 处理舍入误差
205
- const totalNewWidth = adjustedSizes.reduce((sum, size) => sum + size, 0);
206
- if (totalNewWidth < 24) {
207
- adjustedSizes[adjustedSizes.length - 1] += 24 - totalNewWidth;
208
- }
209
-
210
- newSizes[targetPosition.rowId] = adjustedSizes;
211
- break;
212
- }
213
-
214
- case 'insert-column-right': {
215
- removeOldSourceUid();
216
-
217
- // 在目标列右侧插入新列
218
- newRows[targetPosition.rowId].splice(targetPosition.columnIndex + 1, 0, [sourceUid]);
219
-
220
- // 重新计算列宽
221
- const currentSizes = newSizes[targetPosition.rowId] || [];
222
- const columnCount = newRows[targetPosition.rowId]?.length || 0;
223
- const newColumnWidth = Math.floor(24 / columnCount);
224
- const remainingWidth = 24 - newColumnWidth;
225
-
226
- // 按原比例分配剩余宽度
227
- const totalOldWidth = currentSizes.reduce((sum, size) => sum + size, 0);
228
- const adjustedSizes = currentSizes.map((size) => Math.floor((size / totalOldWidth) * remainingWidth));
229
-
230
- // 插入新列宽度
231
- adjustedSizes.splice(targetPosition.columnIndex + 1, 0, newColumnWidth);
232
-
233
- // 处理舍入误差
234
- const totalNewWidth = adjustedSizes.reduce((sum, size) => sum + size, 0);
235
- if (totalNewWidth < 24) {
236
- adjustedSizes[adjustedSizes.length - 1] += 24 - totalNewWidth;
237
- }
238
-
239
- newSizes[targetPosition.rowId] = adjustedSizes;
240
- break;
241
- }
242
- }
243
-
244
- return recalculateAllRowSizesWithProportion({ rows: newRows, sizes: newSizes });
245
- };
246
-
247
- /**
248
- * 将鼠标在元素上的位置转换为区块移动方向
249
- *
250
- * @param position 鼠标在元素上的位置
251
- * @returns 对应的移动方向
252
- */
253
- export const positionToDirection = (position: ElementPosition): MoveDirection => {
254
- switch (position) {
255
- case ElementPosition.TOP:
256
- return 'insert-same-column-above';
257
- case ElementPosition.BOTTOM:
258
- return 'insert-same-column-below';
259
- case ElementPosition.LEFT:
260
- return 'insert-column-left';
261
- case ElementPosition.RIGHT:
262
- return 'insert-column-right';
263
- case ElementPosition.TOP_EDGE:
264
- return 'insert-row-above';
265
- case ElementPosition.BOTTOM_EDGE:
266
- return 'insert-row-below';
267
- case ElementPosition.LEFT_EDGE:
268
- return 'insert-column-left';
269
- case ElementPosition.RIGHT_EDGE:
270
- return 'insert-column-right';
271
- default:
272
- throw new Error(`Unsupported position: ${position}`);
273
- }
274
- };
275
-
276
- function insertKey({
277
- obj,
278
- newKey,
279
- newValue,
280
- referenceKey,
281
- position,
282
- }: {
283
- obj: Record<string, any>;
284
- newKey: string;
285
- newValue: any;
286
- referenceKey: string;
287
- position: 'before' | 'after';
288
- }) {
289
- const result: Record<string, any> = {};
290
-
291
- for (const [key, value] of Object.entries(obj)) {
292
- if (position === 'before' && key === referenceKey) {
293
- result[newKey] = newValue;
294
- }
295
-
296
- result[key] = value;
297
-
298
- if (position === 'after' && key === referenceKey) {
299
- result[newKey] = newValue;
300
- }
301
- }
302
-
303
- return result;
304
- }
305
-
306
- function getNextKey(obj: Record<string, any>, currentKey: string): string | null {
307
- const keys = Object.keys(obj);
308
- const index = keys.indexOf(currentKey);
309
- return index >= 0 && index < keys.length - 1 ? keys[index + 1] : null;
310
- }
311
-
312
- /**
313
- * 重新计算所有行的列宽,保持原有比例的同时确保总宽度为24
314
- * @param layoutData 当前的布局数据
315
- * @returns 更新后的布局数据
316
- */
317
- function recalculateAllRowSizesWithProportion(layoutData: GridLayoutData): GridLayoutData {
318
- const { rows, sizes } = layoutData;
319
- const newSizes = _.cloneDeep(sizes);
320
-
321
- for (const [rowId, columns] of Object.entries(rows)) {
322
- const columnCount = columns.length;
323
- const currentSizes = sizes[rowId] || [];
324
-
325
- if (columnCount === 0) {
326
- continue;
327
- }
328
-
329
- // 如果当前没有尺寸信息或尺寸数量不匹配,使用均等分配
330
- if (currentSizes.length !== columnCount || currentSizes.every((size) => size === 0)) {
331
- const baseWidth = Math.floor(24 / columnCount);
332
- const remainder = 24 % columnCount;
333
- const newRowSizes = new Array(columnCount).fill(baseWidth);
334
-
335
- for (let i = 0; i < remainder; i++) {
336
- newRowSizes[i] += 1;
337
- }
338
-
339
- newSizes[rowId] = newRowSizes;
340
- continue;
341
- }
342
-
343
- // 计算当前总宽度
344
- const currentTotal = currentSizes.reduce((sum, size) => sum + size, 0);
345
-
346
- if (currentTotal === 0) {
347
- // 如果当前总宽度为0,使用均等分配
348
- const baseWidth = Math.floor(24 / columnCount);
349
- const remainder = 24 % columnCount;
350
- const newRowSizes = new Array(columnCount).fill(baseWidth);
351
-
352
- for (let i = 0; i < remainder; i++) {
353
- newRowSizes[i] += 1;
354
- }
355
-
356
- newSizes[rowId] = newRowSizes;
357
- } else {
358
- // 按比例重新分配
359
- const newRowSizes = currentSizes.map((size) => Math.floor((size / currentTotal) * 24));
360
-
361
- // 处理舍入误差
362
- const actualTotal = newRowSizes.reduce((sum, size) => sum + size, 0);
363
- const diff = 24 - actualTotal;
364
-
365
- // 将差值分配给最后几列
366
- for (let i = newRowSizes.length - 1; i >= 0 && diff > 0; i--) {
367
- const add = Math.min(diff, 1);
368
- newRowSizes[i] += add;
369
- }
370
-
371
- newSizes[rowId] = newRowSizes;
372
- }
373
- }
374
-
375
- return {
376
- rows,
377
- sizes: newSizes,
378
- };
379
- }