@nocobase/client-v2 2.1.0-alpha.26 → 2.1.0-alpha.28
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/es/BaseApplication.d.ts +1 -0
- package/es/flow/actions/dataScopeFilter.d.ts +9 -0
- package/es/flow/components/Grid/index.d.ts +5 -3
- package/es/flow/components/code-editor/types.d.ts +1 -0
- package/es/flow/internal/utils/rebuildFieldSubModel.d.ts +2 -1
- package/es/flow/models/base/GridModel.d.ts +19 -2
- package/es/flow/models/blocks/filter-form/FilterFormGridModel.d.ts +1 -0
- package/es/flow/models/fields/JSFieldModel.d.ts +5 -0
- package/es/index.mjs +83 -83
- package/lib/index.js +83 -83
- package/package.json +5 -5
- package/src/BaseApplication.tsx +4 -0
- package/src/flow/actions/__tests__/dataScopeFilter.test.ts +158 -0
- package/src/flow/actions/dataScope.tsx +6 -4
- package/src/flow/actions/dataScopeFilter.ts +70 -0
- package/src/flow/actions/setTargetDataScope.tsx +6 -5
- package/src/flow/components/Grid/index.tsx +66 -20
- package/src/flow/components/code-editor/__tests__/linter.test.ts +18 -0
- package/src/flow/components/code-editor/__tests__/runjsDiagnostics.test.ts +23 -0
- package/src/flow/components/code-editor/index.tsx +18 -17
- package/src/flow/components/code-editor/linter.ts +222 -158
- package/src/flow/components/code-editor/runjsDiagnostics.ts +161 -97
- package/src/flow/components/code-editor/types.ts +1 -0
- package/src/flow/internal/utils/__tests__/rebuildFieldSubModel.test.ts +77 -2
- package/src/flow/internal/utils/rebuildFieldSubModel.ts +21 -5
- package/src/flow/models/base/BlockGridModel.tsx +2 -2
- package/src/flow/models/base/GridModel.tsx +428 -195
- package/src/flow/models/base/__tests__/BlockGridModel.dragOverlayConfig.test.ts +44 -0
- package/src/flow/models/base/__tests__/GridModel.computeOverlayRect.test.ts +29 -0
- package/src/flow/models/base/__tests__/GridModel.dragSnapshotContainer.test.ts +181 -2
- package/src/flow/models/base/__tests__/GridModel.resizeLayout.test.ts +124 -0
- package/src/flow/models/base/__tests__/GridModel.visibleLayout.test.ts +55 -15
- package/src/flow/models/blocks/details/DetailsGridModel.tsx +6 -6
- package/src/flow/models/blocks/filter-form/FilterFormBlockModel.tsx +9 -5
- package/src/flow/models/blocks/filter-form/FilterFormGridModel.tsx +54 -14
- package/src/flow/models/blocks/filter-form/__tests__/FilterFormBlockModel.cleanup.test.ts +138 -0
- package/src/flow/models/blocks/filter-form/__tests__/FilterFormGridModel.toggleFormFieldsCollapse.test.ts +45 -0
- package/src/flow/models/blocks/form/FormGridModel.tsx +6 -6
- package/src/flow/models/blocks/form/__tests__/FormBlockModel.test.tsx +22 -0
- package/src/flow/models/blocks/table/JSColumnModel.tsx +30 -2
- package/src/flow/models/blocks/table/TableBlockModel.tsx +8 -1
- package/src/flow/models/blocks/table/TableColumnModel.tsx +1 -0
- package/src/flow/models/blocks/table/__tests__/JSColumnModel.test.tsx +51 -0
- package/src/flow/models/blocks/table/__tests__/TableBlockModel.quickEditRefresh.test.ts +49 -0
- package/src/flow/models/fields/JSFieldModel.tsx +54 -14
|
@@ -9,25 +9,32 @@
|
|
|
9
9
|
|
|
10
10
|
import { DragCancelEvent, DragEndEvent, DragMoveEvent, DragStartEvent } from '@dnd-kit/core';
|
|
11
11
|
import { uid } from '@formily/shared';
|
|
12
|
+
import type { FlowModelRendererProps } from '@nocobase/flow-engine';
|
|
12
13
|
import {
|
|
14
|
+
buildLayoutSnapshot,
|
|
13
15
|
DndProvider,
|
|
14
16
|
DragHandler,
|
|
15
|
-
Droppable,
|
|
16
17
|
DragOverlayConfig,
|
|
18
|
+
Droppable,
|
|
17
19
|
EMPTY_COLUMN_UID,
|
|
18
|
-
|
|
19
|
-
findModelUidPosition,
|
|
20
|
+
findModelUidLayoutPosition,
|
|
20
21
|
FlowModel,
|
|
21
|
-
MemoFlowModelRenderer,
|
|
22
|
-
buildLayoutSnapshot,
|
|
23
22
|
getSlotKey,
|
|
23
|
+
GridCellV2,
|
|
24
24
|
GridLayoutData,
|
|
25
|
+
GridLayoutPath,
|
|
26
|
+
GridLayoutV2,
|
|
27
|
+
GridRowV2,
|
|
28
|
+
isSameGridLayout,
|
|
25
29
|
LayoutSlot,
|
|
30
|
+
MemoFlowModelRenderer,
|
|
31
|
+
normalizeGridLayout,
|
|
32
|
+
projectLayoutToLegacyRows,
|
|
26
33
|
Rect,
|
|
27
34
|
resolveDropIntent,
|
|
28
35
|
simulateLayoutForSlot,
|
|
36
|
+
tExpr,
|
|
29
37
|
} from '@nocobase/flow-engine';
|
|
30
|
-
import type { FlowModelRendererProps } from '@nocobase/flow-engine';
|
|
31
38
|
import { Space } from 'antd';
|
|
32
39
|
import _ from 'lodash';
|
|
33
40
|
import React from 'react';
|
|
@@ -60,9 +67,12 @@ interface DragState {
|
|
|
60
67
|
containerEl: HTMLDivElement | null;
|
|
61
68
|
containerRect: Rect;
|
|
62
69
|
pointerOrigin?: { x: number; y: number };
|
|
70
|
+
pointerPosition?: { x: number; y: number };
|
|
63
71
|
activeSlotKey: string | null;
|
|
64
72
|
previewLayout?: GridLayoutData;
|
|
65
73
|
refreshTimer?: ReturnType<typeof setTimeout> | null;
|
|
74
|
+
cleanupListeners?: () => void;
|
|
75
|
+
generatedIds: Map<string, string>;
|
|
66
76
|
}
|
|
67
77
|
|
|
68
78
|
const getClientPoint = (event: any): { x: number; y: number } | null => {
|
|
@@ -107,6 +117,53 @@ export class GridModel<T extends { subModels: { items: FlowModel[] } } = Default
|
|
|
107
117
|
];
|
|
108
118
|
private dragState?: DragState;
|
|
109
119
|
private _memoItemFlowSettings?: Exclude<FlowModelRendererProps['showFlowSettings'], boolean>;
|
|
120
|
+
|
|
121
|
+
private updateDragPointerPosition = (event: Event) => {
|
|
122
|
+
if (!this.dragState) {
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const point = getClientPoint(event);
|
|
127
|
+
if (!point) {
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
this.dragState.pointerPosition = point;
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
private handleDragScroll = () => {
|
|
135
|
+
if (!this.dragState) {
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
this.scheduleSnapshotRefresh();
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
private bindDragDocumentListeners() {
|
|
143
|
+
const ownerDocument = this.getDragContainer()?.ownerDocument || (typeof document === 'undefined' ? null : document);
|
|
144
|
+
if (!ownerDocument) {
|
|
145
|
+
return undefined;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const options: AddEventListenerOptions = { capture: true, passive: true };
|
|
149
|
+
const removeOptions: EventListenerOptions = { capture: true };
|
|
150
|
+
const ownerWindow = ownerDocument.defaultView;
|
|
151
|
+
|
|
152
|
+
ownerDocument.addEventListener('pointermove', this.updateDragPointerPosition, options);
|
|
153
|
+
ownerDocument.addEventListener('mousemove', this.updateDragPointerPosition, options);
|
|
154
|
+
ownerDocument.addEventListener('touchmove', this.updateDragPointerPosition, options);
|
|
155
|
+
ownerDocument.addEventListener('scroll', this.handleDragScroll, options);
|
|
156
|
+
ownerWindow?.addEventListener('scroll', this.handleDragScroll, options);
|
|
157
|
+
|
|
158
|
+
return () => {
|
|
159
|
+
ownerDocument.removeEventListener('pointermove', this.updateDragPointerPosition, removeOptions);
|
|
160
|
+
ownerDocument.removeEventListener('mousemove', this.updateDragPointerPosition, removeOptions);
|
|
161
|
+
ownerDocument.removeEventListener('touchmove', this.updateDragPointerPosition, removeOptions);
|
|
162
|
+
ownerDocument.removeEventListener('scroll', this.handleDragScroll, removeOptions);
|
|
163
|
+
ownerWindow?.removeEventListener('scroll', this.handleDragScroll, removeOptions);
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
|
|
110
167
|
protected deriveRowOrder(rows: Record<string, string[][]>, provided?: string[]) {
|
|
111
168
|
const order: string[] = [];
|
|
112
169
|
const used = new Set<string>();
|
|
@@ -165,69 +222,199 @@ export class GridModel<T extends { subModels: { items: FlowModel[] } } = Default
|
|
|
165
222
|
return this._memoItemFlowSettings;
|
|
166
223
|
}
|
|
167
224
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
225
|
+
getItemUids() {
|
|
226
|
+
return (this.subModels.items || []).map((item) => item.uid);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
protected normalizeLayoutFromSource(source?: Partial<GridLayoutData>): GridLayoutV2 {
|
|
230
|
+
const params = this.getStepParams(GRID_FLOW_KEY, GRID_STEP) || {};
|
|
231
|
+
return normalizeGridLayout({
|
|
232
|
+
layout: source?.layout ?? this.props.layout ?? params.layout,
|
|
233
|
+
rows: source?.rows ?? this.props.rows ?? params.rows,
|
|
234
|
+
sizes: source?.sizes ?? this.props.sizes ?? params.sizes,
|
|
235
|
+
rowOrder: source?.rowOrder ?? this.props.rowOrder ?? params.rowOrder,
|
|
236
|
+
itemUids: this.getItemUids(),
|
|
237
|
+
gridUid: this.uid,
|
|
238
|
+
logger: console,
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
getGridLayout(): GridLayoutV2 {
|
|
243
|
+
return this.normalizeLayoutFromSource();
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
syncLayoutProps(layout: GridLayoutV2) {
|
|
247
|
+
const projection = projectLayoutToLegacyRows(layout);
|
|
248
|
+
this.setProps('layout', layout);
|
|
249
|
+
this.setProps('rows', projection.rows);
|
|
250
|
+
this.setProps('sizes', projection.sizes);
|
|
251
|
+
this.setProps('rowOrder', projection.rowOrder);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
setGridStepLayout(layout: GridLayoutV2) {
|
|
255
|
+
this.setStepParams(GRID_FLOW_KEY, {
|
|
256
|
+
[GRID_STEP]: {
|
|
257
|
+
layout,
|
|
258
|
+
},
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
private findLayoutRowByPath(layout: GridLayoutV2, path?: GridLayoutPath): GridRowV2 | null {
|
|
263
|
+
if (!path?.length) {
|
|
264
|
+
return null;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
let rows = layout.rows;
|
|
268
|
+
for (let index = 0; index < path.length; index += 1) {
|
|
269
|
+
const entry = path[index];
|
|
270
|
+
const row = rows.find((candidate) => candidate.id === entry.rowId);
|
|
271
|
+
if (!row) {
|
|
272
|
+
return null;
|
|
173
273
|
}
|
|
174
|
-
|
|
274
|
+
if (index === path.length - 1) {
|
|
275
|
+
return row;
|
|
276
|
+
}
|
|
277
|
+
const cell = row.cells.find((candidate) => candidate.id === entry.cellId);
|
|
278
|
+
rows = cell?.rows || [];
|
|
279
|
+
}
|
|
175
280
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
281
|
+
return null;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
private collectCellItemsForResize(cell: GridCellV2): string[] {
|
|
285
|
+
if (Array.isArray(cell.items)) {
|
|
286
|
+
return [...cell.items];
|
|
287
|
+
}
|
|
288
|
+
return (cell.rows || []).flatMap((row) =>
|
|
289
|
+
row.cells.flatMap((childCell) => this.collectCellItemsForResize(childCell)),
|
|
290
|
+
);
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
private buildResizedCells(row: GridRowV2, cells: string[][]): GridCellV2[] {
|
|
294
|
+
const usedCellIndexes = new Set<number>();
|
|
295
|
+
|
|
296
|
+
return cells.map((items, index) => {
|
|
297
|
+
const matchedIndex = row.cells.findIndex((cell, cellIndex) => {
|
|
298
|
+
return !usedCellIndexes.has(cellIndex) && _.isEqual(this.collectCellItemsForResize(cell), items);
|
|
299
|
+
});
|
|
190
300
|
|
|
191
|
-
|
|
192
|
-
|
|
301
|
+
if (matchedIndex !== -1) {
|
|
302
|
+
usedCellIndexes.add(matchedIndex);
|
|
303
|
+
const matchedCell = row.cells[matchedIndex];
|
|
304
|
+
if (matchedCell.rows && !matchedCell.items) {
|
|
305
|
+
return _.cloneDeep(matchedCell);
|
|
306
|
+
}
|
|
307
|
+
return {
|
|
308
|
+
id: matchedCell.id,
|
|
309
|
+
items: [...items],
|
|
310
|
+
};
|
|
193
311
|
}
|
|
312
|
+
|
|
313
|
+
return {
|
|
314
|
+
id: `${row.id}:cell:${index}:${uid()}`,
|
|
315
|
+
items: [...items],
|
|
316
|
+
};
|
|
194
317
|
});
|
|
195
|
-
|
|
196
|
-
const modelUid = model.uid;
|
|
318
|
+
}
|
|
197
319
|
|
|
198
|
-
|
|
199
|
-
|
|
320
|
+
private getResizeRowPath(path?: GridLayoutPath): GridLayoutPath {
|
|
321
|
+
if (!path?.length) {
|
|
322
|
+
return [];
|
|
323
|
+
}
|
|
200
324
|
|
|
201
|
-
|
|
202
|
-
|
|
325
|
+
const rowPath = path.map((entry) => ({ ...entry }));
|
|
326
|
+
delete rowPath[rowPath.length - 1].cellId;
|
|
327
|
+
return rowPath;
|
|
328
|
+
}
|
|
203
329
|
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
330
|
+
private getResizeContainerWidth(path?: GridLayoutPath) {
|
|
331
|
+
const rowPath = this.getResizeRowPath(path);
|
|
332
|
+
const fallbackWidth = this.gridContainerRef.current?.clientWidth || 0;
|
|
333
|
+
if (!rowPath.length) {
|
|
334
|
+
return fallbackWidth;
|
|
335
|
+
}
|
|
208
336
|
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
337
|
+
const rows = Array.from(this.gridContainerRef.current?.querySelectorAll<HTMLElement>('[data-grid-row-id]') || []);
|
|
338
|
+
const rowElement = rows.find((element) => {
|
|
339
|
+
try {
|
|
340
|
+
return _.isEqual(JSON.parse(element.dataset.gridPath || '[]'), rowPath);
|
|
341
|
+
} catch {
|
|
342
|
+
return false;
|
|
343
|
+
}
|
|
344
|
+
});
|
|
345
|
+
return rowElement?.parentElement?.clientWidth || fallbackWidth;
|
|
346
|
+
}
|
|
213
347
|
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
348
|
+
private resizeGridLayout({
|
|
349
|
+
direction,
|
|
350
|
+
resizeDistance,
|
|
351
|
+
model,
|
|
352
|
+
}: {
|
|
353
|
+
direction: 'left' | 'right';
|
|
354
|
+
resizeDistance: number;
|
|
355
|
+
model: FlowModel;
|
|
356
|
+
}): { layout: GridLayoutV2; moveDistance: number } | null {
|
|
357
|
+
const layout = this.getGridLayout();
|
|
358
|
+
const position = findModelUidLayoutPosition(layout, model.uid);
|
|
359
|
+
if (!position) {
|
|
360
|
+
return null;
|
|
361
|
+
}
|
|
218
362
|
|
|
219
|
-
|
|
220
|
-
|
|
363
|
+
const row = this.findLayoutRowByPath(layout, position.path);
|
|
364
|
+
if (!row) {
|
|
365
|
+
return null;
|
|
366
|
+
}
|
|
221
367
|
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
368
|
+
const gridContainerWidth = this.getResizeContainerWidth(position.path);
|
|
369
|
+
const rowCells = row.cells.map((cell) => this.collectCellItemsForResize(cell));
|
|
370
|
+
const { newRows, newSizes, moveDistance } = recalculateGridSizes({
|
|
371
|
+
position: {
|
|
372
|
+
rowId: row.id,
|
|
373
|
+
columnIndex: position.cellIndex,
|
|
374
|
+
itemIndex: position.itemIndex,
|
|
375
|
+
},
|
|
376
|
+
direction,
|
|
377
|
+
resizeDistance,
|
|
378
|
+
prevMoveDistance: this.prevMoveDistance,
|
|
379
|
+
oldRows: { [row.id]: rowCells },
|
|
380
|
+
oldSizes: { [row.id]: row.sizes || [] },
|
|
381
|
+
gutter: this.context.themeToken?.marginBlock ?? 16,
|
|
382
|
+
gridContainerWidth,
|
|
383
|
+
});
|
|
227
384
|
|
|
228
|
-
|
|
229
|
-
|
|
385
|
+
const nextLayout = _.cloneDeep(layout);
|
|
386
|
+
const resizedRow = this.findLayoutRowByPath(nextLayout, position.path);
|
|
387
|
+
if (!resizedRow) {
|
|
388
|
+
return null;
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
resizedRow.cells = this.buildResizedCells(row, newRows[row.id] || []);
|
|
392
|
+
resizedRow.sizes = newSizes[row.id] || [];
|
|
393
|
+
|
|
394
|
+
return {
|
|
395
|
+
layout: normalizeGridLayout({
|
|
396
|
+
layout: nextLayout,
|
|
397
|
+
itemUids: this.getItemUids(),
|
|
398
|
+
gridUid: this.uid,
|
|
399
|
+
logger: console,
|
|
400
|
+
}),
|
|
401
|
+
moveDistance,
|
|
402
|
+
};
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
onMount(): void {
|
|
406
|
+
super.onMount();
|
|
407
|
+
this.emitter.on('onSubModelAdded', (model: FlowModel) => {
|
|
408
|
+
if (!model.isNew) {
|
|
409
|
+
return;
|
|
230
410
|
}
|
|
411
|
+
this.resetRows(true);
|
|
412
|
+
this.setGridStepLayout(this.props.layout);
|
|
413
|
+
});
|
|
414
|
+
this.emitter.on('onSubModelDestroyed', (model: FlowModel) => {
|
|
415
|
+
const modelUid = model.uid;
|
|
416
|
+
this.resetRows(true);
|
|
417
|
+
this.setGridStepLayout(this.props.layout);
|
|
231
418
|
|
|
232
419
|
// 删除筛选配置
|
|
233
420
|
this.context.filterManager?.removeFilterConfig({ targetId: modelUid });
|
|
@@ -240,57 +427,33 @@ export class GridModel<T extends { subModels: { items: FlowModel[] } } = Default
|
|
|
240
427
|
});
|
|
241
428
|
|
|
242
429
|
this.emitter.on('onResizeLeft', ({ resizeDistance, model }) => {
|
|
243
|
-
const
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
resizeDistance,
|
|
249
|
-
prevMoveDistance: this.prevMoveDistance,
|
|
250
|
-
oldRows: this.props.rows || {},
|
|
251
|
-
oldSizes: this.props.sizes || {},
|
|
252
|
-
gutter: this.context.themeToken.marginBlock,
|
|
253
|
-
gridContainerWidth,
|
|
254
|
-
});
|
|
255
|
-
this.prevMoveDistance = moveDistance;
|
|
256
|
-
this.setProps('rows', newRows);
|
|
257
|
-
this.setProps('sizes', newSizes);
|
|
430
|
+
const resized = this.resizeGridLayout({ direction: 'left', resizeDistance, model });
|
|
431
|
+
if (resized) {
|
|
432
|
+
this.prevMoveDistance = resized.moveDistance;
|
|
433
|
+
this.syncLayoutProps(resized.layout);
|
|
434
|
+
}
|
|
258
435
|
});
|
|
259
436
|
this.emitter.on('onResizeRight', ({ resizeDistance, model }) => {
|
|
260
|
-
const
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
resizeDistance,
|
|
266
|
-
prevMoveDistance: this.prevMoveDistance,
|
|
267
|
-
oldRows: this.props.rows || {},
|
|
268
|
-
oldSizes: this.props.sizes || {},
|
|
269
|
-
gutter: this.context.themeToken.marginBlock,
|
|
270
|
-
gridContainerWidth,
|
|
271
|
-
});
|
|
272
|
-
this.prevMoveDistance = moveDistance;
|
|
273
|
-
this.setProps('rows', newRows);
|
|
274
|
-
this.setProps('sizes', newSizes);
|
|
437
|
+
const resized = this.resizeGridLayout({ direction: 'right', resizeDistance, model });
|
|
438
|
+
if (resized) {
|
|
439
|
+
this.prevMoveDistance = resized.moveDistance;
|
|
440
|
+
this.syncLayoutProps(resized.layout);
|
|
441
|
+
}
|
|
275
442
|
});
|
|
276
443
|
this.emitter.on('onResizeBottom', ({ resizeDistance, model }) => {});
|
|
277
444
|
this.emitter.on('onResizeEnd', () => {
|
|
278
445
|
this.prevMoveDistance = 0;
|
|
279
|
-
this.saveGridLayout();
|
|
446
|
+
this.saveGridLayout(this.props.layout);
|
|
280
447
|
});
|
|
281
448
|
}
|
|
282
449
|
|
|
283
|
-
saveGridLayout(layout?: GridLayoutData) {
|
|
284
|
-
const
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
this.
|
|
289
|
-
|
|
290
|
-
sizes,
|
|
291
|
-
rowOrder,
|
|
292
|
-
});
|
|
293
|
-
this.setProps('rowOrder', rowOrder);
|
|
450
|
+
saveGridLayout(layout?: GridLayoutData | GridLayoutV2) {
|
|
451
|
+
const normalizedLayout =
|
|
452
|
+
layout && 'version' in layout
|
|
453
|
+
? normalizeGridLayout({ layout, itemUids: this.getItemUids(), gridUid: this.uid, logger: console })
|
|
454
|
+
: this.normalizeLayoutFromSource(layout as any);
|
|
455
|
+
this.setGridStepLayout(normalizedLayout);
|
|
456
|
+
this.syncLayoutProps(normalizedLayout);
|
|
294
457
|
this.saveStepParams();
|
|
295
458
|
}
|
|
296
459
|
|
|
@@ -332,19 +495,11 @@ export class GridModel<T extends { subModels: { items: FlowModel[] } } = Default
|
|
|
332
495
|
|
|
333
496
|
resetRows(syncProps = false) {
|
|
334
497
|
const params = this.getStepParams(GRID_FLOW_KEY, GRID_STEP) || {};
|
|
335
|
-
const
|
|
336
|
-
|
|
337
|
-
const sizes = this.orderSizesByRowOrder(params.sizes || {}, rowOrder);
|
|
338
|
-
this.setStepParams(GRID_FLOW_KEY, GRID_STEP, {
|
|
339
|
-
rows,
|
|
340
|
-
sizes,
|
|
341
|
-
rowOrder,
|
|
342
|
-
});
|
|
498
|
+
const layout = this.normalizeLayoutFromSource(params);
|
|
499
|
+
this.setGridStepLayout(layout);
|
|
343
500
|
|
|
344
501
|
if (syncProps) {
|
|
345
|
-
this.
|
|
346
|
-
this.setProps('sizes', sizes);
|
|
347
|
-
this.setProps('rowOrder', rowOrder);
|
|
502
|
+
this.syncLayoutProps(layout);
|
|
348
503
|
}
|
|
349
504
|
}
|
|
350
505
|
|
|
@@ -352,6 +507,10 @@ export class GridModel<T extends { subModels: { items: FlowModel[] } } = Default
|
|
|
352
507
|
if (!this.dragState) {
|
|
353
508
|
return null;
|
|
354
509
|
}
|
|
510
|
+
if (this.dragState.pointerPosition) {
|
|
511
|
+
return this.dragState.pointerPosition;
|
|
512
|
+
}
|
|
513
|
+
|
|
355
514
|
const origin = this.dragState.pointerOrigin;
|
|
356
515
|
if (origin) {
|
|
357
516
|
return {
|
|
@@ -407,7 +566,7 @@ export class GridModel<T extends { subModels: { items: FlowModel[] } } = Default
|
|
|
407
566
|
return;
|
|
408
567
|
}
|
|
409
568
|
if (this.dragState.refreshTimer) {
|
|
410
|
-
|
|
569
|
+
return;
|
|
411
570
|
}
|
|
412
571
|
this.dragState.refreshTimer = setTimeout(() => {
|
|
413
572
|
if (!this.dragState) {
|
|
@@ -415,9 +574,24 @@ export class GridModel<T extends { subModels: { items: FlowModel[] } } = Default
|
|
|
415
574
|
}
|
|
416
575
|
this.dragState.refreshTimer = null;
|
|
417
576
|
this.updateLayoutSnapshot();
|
|
577
|
+
this.refreshPreviewFromPointer();
|
|
418
578
|
}, 16);
|
|
419
579
|
}
|
|
420
580
|
|
|
581
|
+
private refreshPreviewFromPointer() {
|
|
582
|
+
if (!this.dragState) {
|
|
583
|
+
return;
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
const point = this.dragState.pointerPosition || this.dragState.pointerOrigin;
|
|
587
|
+
if (!point) {
|
|
588
|
+
return;
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
const slot = this.resolveDragSlot(point);
|
|
592
|
+
this.applyPreview(slot);
|
|
593
|
+
}
|
|
594
|
+
|
|
421
595
|
/**
|
|
422
596
|
* 根据 slot 类型、位置和配置计算最终的 overlay 尺寸和位置
|
|
423
597
|
*/
|
|
@@ -448,6 +622,17 @@ export class GridModel<T extends { subModels: { items: FlowModel[] } } = Default
|
|
|
448
622
|
height: baseRect.height,
|
|
449
623
|
};
|
|
450
624
|
}
|
|
625
|
+
case 'item-edge': {
|
|
626
|
+
const edgeConfig = config?.columnEdge?.[slot.direction];
|
|
627
|
+
const width = edgeConfig?.width ?? baseRect.width;
|
|
628
|
+
const offsetLeft = edgeConfig?.offsetLeft ?? 0;
|
|
629
|
+
return {
|
|
630
|
+
top: baseRect.top,
|
|
631
|
+
left: baseRect.left + offsetLeft,
|
|
632
|
+
width,
|
|
633
|
+
height: baseRect.height,
|
|
634
|
+
};
|
|
635
|
+
}
|
|
451
636
|
case 'row-gap': {
|
|
452
637
|
const gapConfig = config?.rowGap?.[slot.position];
|
|
453
638
|
const height = gapConfig?.height ?? baseRect.height;
|
|
@@ -479,6 +664,25 @@ export class GridModel<T extends { subModels: { items: FlowModel[] } } = Default
|
|
|
479
664
|
}
|
|
480
665
|
}
|
|
481
666
|
|
|
667
|
+
private getHitTestSlot(slot: LayoutSlot): LayoutSlot {
|
|
668
|
+
return {
|
|
669
|
+
...slot,
|
|
670
|
+
// 命中区域与实际显示的 overlay 保持一致,避免鼠标仍在蓝框内时命中跳走
|
|
671
|
+
rect: this.computeOverlayRect(slot),
|
|
672
|
+
};
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
private resolveDragSlot(point: { x: number; y: number }): LayoutSlot | null {
|
|
676
|
+
if (!this.dragState?.slots.length) {
|
|
677
|
+
return null;
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
return resolveDropIntent(
|
|
681
|
+
point,
|
|
682
|
+
this.dragState.slots.map((slot) => this.getHitTestSlot(slot)),
|
|
683
|
+
);
|
|
684
|
+
}
|
|
685
|
+
|
|
482
686
|
private applyPreview(slot: LayoutSlot | null) {
|
|
483
687
|
if (!this.dragState) {
|
|
484
688
|
return;
|
|
@@ -503,24 +707,32 @@ export class GridModel<T extends { subModels: { items: FlowModel[] } } = Default
|
|
|
503
707
|
sourceUid: this.dragState.sourceUid,
|
|
504
708
|
layout: this.dragState.snapshot,
|
|
505
709
|
generateRowId: uid,
|
|
710
|
+
generatedIds: this.dragState.generatedIds,
|
|
711
|
+
generateId: () => uid(),
|
|
506
712
|
});
|
|
507
713
|
|
|
714
|
+
if (
|
|
715
|
+
preview.layout &&
|
|
716
|
+
this.dragState.snapshot.layout &&
|
|
717
|
+
isSameGridLayout(preview.layout, this.dragState.snapshot.layout)
|
|
718
|
+
) {
|
|
719
|
+
this.applyPreview(null);
|
|
720
|
+
return;
|
|
721
|
+
}
|
|
722
|
+
|
|
508
723
|
this.dragState.previewLayout = {
|
|
509
724
|
rows: _.cloneDeep(preview.rows),
|
|
510
725
|
sizes: _.cloneDeep(preview.sizes),
|
|
511
726
|
rowOrder: _.cloneDeep(preview.rowOrder),
|
|
727
|
+
layout: _.cloneDeep(preview.layout),
|
|
512
728
|
};
|
|
513
729
|
|
|
514
|
-
const container = this.getDragContainer();
|
|
515
|
-
const scrollTop = container?.scrollTop ?? 0;
|
|
516
|
-
const scrollLeft = container?.scrollLeft ?? 0;
|
|
517
|
-
|
|
518
730
|
// 计算最终的 overlay 矩形
|
|
519
731
|
const rect = this.computeOverlayRect(slot);
|
|
520
732
|
|
|
521
733
|
const overlay: DragOverlayState = {
|
|
522
|
-
top: rect.top - this.dragState.containerRect.top
|
|
523
|
-
left: rect.left - this.dragState.containerRect.left
|
|
734
|
+
top: rect.top - this.dragState.containerRect.top,
|
|
735
|
+
left: rect.left - this.dragState.containerRect.left,
|
|
524
736
|
width: rect.width,
|
|
525
737
|
height: rect.height,
|
|
526
738
|
type: slot.type,
|
|
@@ -531,14 +743,15 @@ export class GridModel<T extends { subModels: { items: FlowModel[] } } = Default
|
|
|
531
743
|
|
|
532
744
|
handleDragStart(event: DragStartEvent) {
|
|
533
745
|
const sourceUid = event.active.id as string;
|
|
534
|
-
const
|
|
535
|
-
const
|
|
746
|
+
const layout = this.normalizeLayoutFromSource();
|
|
747
|
+
const projection = projectLayoutToLegacyRows(layout);
|
|
536
748
|
this.dragState = {
|
|
537
749
|
sourceUid,
|
|
538
750
|
snapshot: {
|
|
539
|
-
rows: _.cloneDeep(rows),
|
|
540
|
-
sizes: _.cloneDeep(sizes),
|
|
541
|
-
rowOrder,
|
|
751
|
+
rows: _.cloneDeep(projection.rows),
|
|
752
|
+
sizes: _.cloneDeep(projection.sizes),
|
|
753
|
+
rowOrder: _.cloneDeep(projection.rowOrder),
|
|
754
|
+
layout: _.cloneDeep(layout),
|
|
542
755
|
},
|
|
543
756
|
slots: [],
|
|
544
757
|
containerEl: this.gridContainerRef.current,
|
|
@@ -547,7 +760,9 @@ export class GridModel<T extends { subModels: { items: FlowModel[] } } = Default
|
|
|
547
760
|
activeSlotKey: null,
|
|
548
761
|
previewLayout: undefined,
|
|
549
762
|
refreshTimer: null,
|
|
763
|
+
generatedIds: new Map(),
|
|
550
764
|
};
|
|
765
|
+
this.dragState.cleanupListeners = this.bindDragDocumentListeners();
|
|
551
766
|
this.setProps('dragOverlayRect', null);
|
|
552
767
|
this.updateLayoutSnapshot();
|
|
553
768
|
this.scheduleSnapshotRefresh();
|
|
@@ -568,7 +783,7 @@ export class GridModel<T extends { subModels: { items: FlowModel[] } } = Default
|
|
|
568
783
|
return;
|
|
569
784
|
}
|
|
570
785
|
|
|
571
|
-
const slot =
|
|
786
|
+
const slot = this.resolveDragSlot(point);
|
|
572
787
|
this.applyPreview(slot);
|
|
573
788
|
}
|
|
574
789
|
|
|
@@ -581,6 +796,7 @@ export class GridModel<T extends { subModels: { items: FlowModel[] } } = Default
|
|
|
581
796
|
clearTimeout(this.dragState.refreshTimer);
|
|
582
797
|
}
|
|
583
798
|
|
|
799
|
+
this.dragState.cleanupListeners?.();
|
|
584
800
|
this.dragState = undefined;
|
|
585
801
|
this.setProps('dragOverlayRect', null);
|
|
586
802
|
}
|
|
@@ -592,10 +808,14 @@ export class GridModel<T extends { subModels: { items: FlowModel[] } } = Default
|
|
|
592
808
|
|
|
593
809
|
const previewLayout = this.dragState.previewLayout;
|
|
594
810
|
if (previewLayout) {
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
this.setProps('
|
|
811
|
+
if (previewLayout.layout) {
|
|
812
|
+
this.syncLayoutProps(_.cloneDeep(previewLayout.layout));
|
|
813
|
+
} else {
|
|
814
|
+
this.setProps('rows', _.cloneDeep(previewLayout.rows));
|
|
815
|
+
this.setProps('sizes', _.cloneDeep(previewLayout.sizes));
|
|
816
|
+
if (previewLayout.rowOrder) {
|
|
817
|
+
this.setProps('rowOrder', _.cloneDeep(previewLayout.rowOrder));
|
|
818
|
+
}
|
|
599
819
|
}
|
|
600
820
|
this.saveGridLayout(previewLayout);
|
|
601
821
|
}
|
|
@@ -617,73 +837,90 @@ export class GridModel<T extends { subModels: { items: FlowModel[] } } = Default
|
|
|
617
837
|
* - 运行态仅在判断为“整列/整行都不可见”时做过滤,不写回 props/stepParams,布局元数据保持不变。
|
|
618
838
|
*/
|
|
619
839
|
private getVisibleLayout() {
|
|
620
|
-
const
|
|
621
|
-
const
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
const { rows: orderedBaseRows, rowOrder } = this.normalizeRowsWithOrder(
|
|
629
|
-
|
|
840
|
+
const rawLayout = this.normalizeLayoutFromSource();
|
|
841
|
+
const baseLayout = this.context.isMobileLayout
|
|
842
|
+
? normalizeGridLayout({
|
|
843
|
+
rows: transformRowsToSingleColumn(projectLayoutToLegacyRows(rawLayout).rows),
|
|
844
|
+
itemUids: this.getItemUids(),
|
|
845
|
+
})
|
|
846
|
+
: rawLayout;
|
|
847
|
+
const baseProjection = projectLayoutToLegacyRows(baseLayout);
|
|
848
|
+
const { rows: orderedBaseRows, rowOrder } = this.normalizeRowsWithOrder(
|
|
849
|
+
baseProjection.rows,
|
|
850
|
+
baseProjection.rowOrder,
|
|
851
|
+
);
|
|
852
|
+
const orderedBaseSizes = this.orderSizesByRowOrder(baseProjection.sizes, rowOrder);
|
|
630
853
|
|
|
631
854
|
// 配置态:不做任何过滤,保持完整布局
|
|
632
855
|
if (this.context.flowSettingsEnabled) {
|
|
633
|
-
return { rows: orderedBaseRows, sizes: orderedBaseSizes };
|
|
856
|
+
return { layout: baseLayout, rows: orderedBaseRows, sizes: orderedBaseSizes };
|
|
634
857
|
}
|
|
635
858
|
|
|
636
859
|
const items = this.subModels?.items || [];
|
|
637
860
|
if (!items.length) {
|
|
638
|
-
return { rows:
|
|
861
|
+
return { layout: baseLayout, rows: baseProjection.rows, sizes: baseProjection.sizes };
|
|
639
862
|
}
|
|
640
863
|
|
|
641
864
|
const modelByUid = new Map(items.map((m: FlowModel) => [m.uid, m]));
|
|
642
865
|
|
|
643
|
-
const rows:
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
866
|
+
const filterRows = (rows: GridLayoutV2['rows']): GridLayoutV2['rows'] => {
|
|
867
|
+
return rows
|
|
868
|
+
.map((row) => {
|
|
869
|
+
const cells: GridLayoutV2['rows'][number]['cells'] = [];
|
|
870
|
+
const keptSizes: number[] = [];
|
|
871
|
+
|
|
872
|
+
row.cells.forEach((cell, index) => {
|
|
873
|
+
const sourceSize = row.sizes?.[index];
|
|
874
|
+
const keepSize = Number.isFinite(sourceSize) && sourceSize > 0 ? sourceSize : 1;
|
|
875
|
+
if (cell.rows) {
|
|
876
|
+
const childRows = filterRows(cell.rows);
|
|
877
|
+
if (childRows.length) {
|
|
878
|
+
cells.push({ ...cell, rows: childRows });
|
|
879
|
+
keptSizes.push(keepSize);
|
|
880
|
+
}
|
|
881
|
+
return;
|
|
882
|
+
}
|
|
883
|
+
|
|
884
|
+
const cellItems = (cell.items || []).filter((uid) => {
|
|
885
|
+
if (uid === EMPTY_COLUMN_UID) {
|
|
886
|
+
return false;
|
|
887
|
+
}
|
|
888
|
+
return modelByUid.get(uid)?.hidden !== true;
|
|
889
|
+
});
|
|
890
|
+
const hasVisibleItem = cellItems.some((uid) => {
|
|
891
|
+
const model = modelByUid.get(uid);
|
|
892
|
+
return !model || !model.hidden;
|
|
893
|
+
});
|
|
894
|
+
if (hasVisibleItem) {
|
|
895
|
+
cells.push({ ...cell, items: cellItems });
|
|
896
|
+
keptSizes.push(keepSize);
|
|
897
|
+
}
|
|
898
|
+
});
|
|
899
|
+
|
|
900
|
+
return cells.length
|
|
901
|
+
? {
|
|
902
|
+
...row,
|
|
903
|
+
cells,
|
|
904
|
+
sizes: keptSizes,
|
|
905
|
+
}
|
|
906
|
+
: null;
|
|
907
|
+
})
|
|
908
|
+
.filter(Boolean) as GridLayoutV2['rows'];
|
|
909
|
+
};
|
|
672
910
|
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
}
|
|
679
|
-
}
|
|
911
|
+
const visibleLayout = normalizeGridLayout({
|
|
912
|
+
layout: {
|
|
913
|
+
version: 2,
|
|
914
|
+
rows: filterRows(baseLayout.rows),
|
|
915
|
+
},
|
|
680
916
|
});
|
|
917
|
+
const projection = projectLayoutToLegacyRows(visibleLayout);
|
|
681
918
|
|
|
682
|
-
return { rows, sizes };
|
|
919
|
+
return { layout: visibleLayout, rows: projection.rows, sizes: projection.sizes };
|
|
683
920
|
}
|
|
684
921
|
|
|
685
922
|
render() {
|
|
686
|
-
const { rows, sizes } = this.getVisibleLayout();
|
|
923
|
+
const { layout, rows, sizes } = this.getVisibleLayout();
|
|
687
924
|
const hasAnyVisibleRow = Object.keys(rows || {}).length > 0;
|
|
688
925
|
|
|
689
926
|
return (
|
|
@@ -704,6 +941,7 @@ export class GridModel<T extends { subModels: { items: FlowModel[] } } = Default
|
|
|
704
941
|
<Grid
|
|
705
942
|
rowGap={this.props.rowGap}
|
|
706
943
|
colGap={this.props.colGap}
|
|
944
|
+
layout={layout}
|
|
707
945
|
rows={rows}
|
|
708
946
|
sizes={sizes}
|
|
709
947
|
dragOverlayRect={this.props.dragOverlayRect}
|
|
@@ -789,34 +1027,29 @@ GridModel.registerFlow({
|
|
|
789
1027
|
},
|
|
790
1028
|
grid: {
|
|
791
1029
|
uiSchema: {
|
|
792
|
-
|
|
793
|
-
title: tExpr('
|
|
1030
|
+
layout: {
|
|
1031
|
+
title: tExpr('Layout'),
|
|
794
1032
|
'x-decorator': 'FormItem',
|
|
795
1033
|
'x-component': JsonEditor,
|
|
796
1034
|
'x-component-props': {
|
|
797
1035
|
autoSize: { minRows: 10, maxRows: 20 },
|
|
798
|
-
description: tExpr('Configure the
|
|
1036
|
+
description: tExpr('Configure the nested layout of the grid.'),
|
|
799
1037
|
},
|
|
800
1038
|
},
|
|
801
|
-
sizes: {
|
|
802
|
-
title: tExpr('Sizes'),
|
|
803
|
-
'x-decorator': 'FormItem',
|
|
804
|
-
'x-component': JsonEditor,
|
|
805
|
-
'x-component-props': {
|
|
806
|
-
rows: 5,
|
|
807
|
-
},
|
|
808
|
-
description: tExpr(
|
|
809
|
-
'Configure the sizes of each row. The value is an array of numbers representing the width of each column in the row.',
|
|
810
|
-
),
|
|
811
|
-
},
|
|
812
1039
|
},
|
|
813
1040
|
async handler(ctx, params) {
|
|
814
|
-
const
|
|
815
|
-
const
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
1041
|
+
const model = ctx.model as GridModel;
|
|
1042
|
+
const layout = normalizeGridLayout({
|
|
1043
|
+
layout: params.layout,
|
|
1044
|
+
rows: params.rows,
|
|
1045
|
+
sizes: params.sizes,
|
|
1046
|
+
rowOrder: params.rowOrder,
|
|
1047
|
+
itemUids: model.getItemUids(),
|
|
1048
|
+
gridUid: model.uid,
|
|
1049
|
+
logger: console,
|
|
1050
|
+
});
|
|
1051
|
+
model.setGridStepLayout(layout);
|
|
1052
|
+
model.syncLayoutProps(layout);
|
|
820
1053
|
},
|
|
821
1054
|
},
|
|
822
1055
|
},
|