@nocobase/flow-engine 2.0.46 → 2.0.47
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/lib/components/dnd/gridDragPlanner.d.ts +59 -2
- package/lib/components/dnd/gridDragPlanner.js +595 -19
- package/lib/components/dnd/index.js +9 -4
- package/lib/utils/createCollectionContextMeta.js +6 -2
- package/package.json +4 -4
- package/src/__tests__/objectVariable.test.ts +24 -0
- package/src/components/__tests__/gridDragPlanner.test.ts +426 -5
- package/src/components/dnd/__tests__/DndProvider.test.tsx +98 -0
- package/src/components/dnd/gridDragPlanner.ts +735 -17
- package/src/components/dnd/index.tsx +9 -3
- package/src/utils/__tests__/createCollectionContextMeta.test.ts +48 -0
- package/src/utils/createCollectionContextMeta.ts +6 -2
|
@@ -74,16 +74,19 @@ export const Droppable: FC<{ model: FlowModel<any>; children: React.ReactNode }>
|
|
|
74
74
|
export const DndProvider: FC<DndContextProps & PersistOptions> = ({
|
|
75
75
|
persist = true,
|
|
76
76
|
children,
|
|
77
|
+
onDragStart,
|
|
77
78
|
onDragEnd,
|
|
79
|
+
onDragCancel,
|
|
78
80
|
...restProps
|
|
79
81
|
}) => {
|
|
80
82
|
const [activeId, setActiveId] = useState<string | null>(null);
|
|
81
83
|
const flowEngine = useFlowEngine();
|
|
82
84
|
return (
|
|
83
85
|
<DndContext
|
|
86
|
+
{...restProps}
|
|
84
87
|
onDragStart={(event) => {
|
|
85
88
|
setActiveId(event.active.id as string);
|
|
86
|
-
|
|
89
|
+
onDragStart?.(event);
|
|
87
90
|
}}
|
|
88
91
|
onDragEnd={(event) => {
|
|
89
92
|
setActiveId(null);
|
|
@@ -97,13 +100,17 @@ export const DndProvider: FC<DndContextProps & PersistOptions> = ({
|
|
|
97
100
|
onDragEnd(event);
|
|
98
101
|
}
|
|
99
102
|
}}
|
|
100
|
-
{
|
|
103
|
+
onDragCancel={(event) => {
|
|
104
|
+
setActiveId(null);
|
|
105
|
+
onDragCancel?.(event);
|
|
106
|
+
}}
|
|
101
107
|
>
|
|
102
108
|
{children}
|
|
103
109
|
{createPortal(
|
|
104
110
|
<DragOverlay dropAnimation={null} zIndex={2000}>
|
|
105
111
|
{activeId && (
|
|
106
112
|
<span
|
|
113
|
+
data-testid="flow-drag-preview"
|
|
107
114
|
style={{
|
|
108
115
|
display: 'inline-flex',
|
|
109
116
|
alignItems: 'center',
|
|
@@ -113,7 +120,6 @@ export const DndProvider: FC<DndContextProps & PersistOptions> = ({
|
|
|
113
120
|
borderRadius: 4,
|
|
114
121
|
padding: '4px 12px',
|
|
115
122
|
color: '#1890ff',
|
|
116
|
-
// fontSize: 18,
|
|
117
123
|
boxShadow: '0 2px 8px rgba(0,0,0,0.15)',
|
|
118
124
|
}}
|
|
119
125
|
>
|
|
@@ -26,6 +26,7 @@ describe('createCollectionContextMeta', () => {
|
|
|
26
26
|
{ name: 'id', type: 'integer', interface: 'number', filterable: true },
|
|
27
27
|
{ name: 'email', type: 'string', interface: 'text', filterable: true },
|
|
28
28
|
{ name: 'nickname', type: 'string', interface: 'text' }, // 未声明 filterable
|
|
29
|
+
{ name: 'rawUserPayload', type: 'json', filterable: true },
|
|
29
30
|
],
|
|
30
31
|
});
|
|
31
32
|
|
|
@@ -34,6 +35,7 @@ describe('createCollectionContextMeta', () => {
|
|
|
34
35
|
fields: [
|
|
35
36
|
{ name: 'title', type: 'string', interface: 'text', filterable: true },
|
|
36
37
|
{ name: 'author', type: 'belongsTo', target: 'users', interface: 'm2o', filterable: true },
|
|
38
|
+
{ name: 'rawPostPayload', type: 'json', filterable: true },
|
|
37
39
|
],
|
|
38
40
|
});
|
|
39
41
|
|
|
@@ -44,8 +46,54 @@ describe('createCollectionContextMeta', () => {
|
|
|
44
46
|
const authorMeta: any = props?.author;
|
|
45
47
|
const authorFields = await authorMeta?.properties?.();
|
|
46
48
|
|
|
49
|
+
expect(props).toHaveProperty('title');
|
|
50
|
+
expect(props).toHaveProperty('author');
|
|
51
|
+
expect(props).not.toHaveProperty('rawPostPayload');
|
|
47
52
|
expect(authorFields).toBeTruthy();
|
|
48
53
|
expect(authorFields).toHaveProperty('email');
|
|
49
54
|
expect(authorFields).not.toHaveProperty('nickname');
|
|
55
|
+
expect(authorFields).not.toHaveProperty('rawUserPayload');
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it('keeps interfaced non-filterable fields but hides fields without interface when includeNonFilterable is true', async () => {
|
|
59
|
+
const engine = new FlowEngine();
|
|
60
|
+
const dm = engine.dataSourceManager as any;
|
|
61
|
+
dm.collectionFieldInterfaceManager = new CollectionFieldInterfaceManager([], {}, dm);
|
|
62
|
+
engine.context.defineProperty('app', { value: { dataSourceManager: dm } });
|
|
63
|
+
const ds = dm.getDataSource('main')!;
|
|
64
|
+
|
|
65
|
+
ds.addCollection({
|
|
66
|
+
name: 'users',
|
|
67
|
+
fields: [
|
|
68
|
+
{ name: 'id', type: 'integer', interface: 'number', filterable: true },
|
|
69
|
+
{ name: 'email', type: 'string', interface: 'text', filterable: true },
|
|
70
|
+
{ name: 'nickname', type: 'string', interface: 'text' },
|
|
71
|
+
{ name: 'rawUserPayload', type: 'json', filterable: true },
|
|
72
|
+
],
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
ds.addCollection({
|
|
76
|
+
name: 'posts',
|
|
77
|
+
fields: [
|
|
78
|
+
{ name: 'title', type: 'string', interface: 'text', filterable: true },
|
|
79
|
+
{ name: 'internalName', type: 'string', interface: 'text' },
|
|
80
|
+
{ name: 'rawPostPayload', type: 'json', filterable: true },
|
|
81
|
+
{ name: 'author', type: 'belongsTo', target: 'users', interface: 'm2o', filterable: true },
|
|
82
|
+
],
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
const posts = ds.getCollection('posts')!;
|
|
86
|
+
const metaFactory = createCollectionContextMeta(posts, 'Posts', true);
|
|
87
|
+
const meta = await metaFactory();
|
|
88
|
+
const props = await (meta?.properties as any)?.();
|
|
89
|
+
const authorFields = await props?.author?.properties?.();
|
|
90
|
+
|
|
91
|
+
expect(props).toHaveProperty('title');
|
|
92
|
+
expect(props).toHaveProperty('internalName');
|
|
93
|
+
expect(props).toHaveProperty('author');
|
|
94
|
+
expect(props).not.toHaveProperty('rawPostPayload');
|
|
95
|
+
expect(authorFields).toHaveProperty('email');
|
|
96
|
+
expect(authorFields).toHaveProperty('nickname');
|
|
97
|
+
expect(authorFields).not.toHaveProperty('rawUserPayload');
|
|
50
98
|
});
|
|
51
99
|
});
|
|
@@ -14,6 +14,10 @@ import type { PropertyMetaFactory } from '../flowContext';
|
|
|
14
14
|
const RELATION_FIELD_TYPES = ['belongsTo', 'hasOne', 'hasMany', 'belongsToMany', 'belongsToArray'] as const;
|
|
15
15
|
const NUMERIC_FIELD_TYPES = ['integer', 'float', 'double', 'decimal'] as const;
|
|
16
16
|
|
|
17
|
+
function shouldShowFieldInMeta(field: CollectionField, includeNonFilterable?: boolean) {
|
|
18
|
+
return Boolean(field.interface && (includeNonFilterable || field.filterable));
|
|
19
|
+
}
|
|
20
|
+
|
|
17
21
|
/**
|
|
18
22
|
* 创建字段的完整元数据(统一处理关联和非关联字段)
|
|
19
23
|
*/
|
|
@@ -36,7 +40,7 @@ function createFieldMetadata(field: CollectionField, includeNonFilterable?: bool
|
|
|
36
40
|
properties: async () => {
|
|
37
41
|
const subProperties: Record<string, any> = {};
|
|
38
42
|
targetCollection.fields.forEach((subField) => {
|
|
39
|
-
if (includeNonFilterable
|
|
43
|
+
if (shouldShowFieldInMeta(subField, includeNonFilterable)) {
|
|
40
44
|
subProperties[subField.name] = createFieldMetadata(subField, includeNonFilterable);
|
|
41
45
|
}
|
|
42
46
|
});
|
|
@@ -114,7 +118,7 @@ export function createCollectionContextMeta(
|
|
|
114
118
|
|
|
115
119
|
// 添加所有字段
|
|
116
120
|
collection.fields.forEach((field) => {
|
|
117
|
-
if (includeNonFilterable
|
|
121
|
+
if (shouldShowFieldInMeta(field, includeNonFilterable)) {
|
|
118
122
|
properties[field.name] = createFieldMetadata(field, includeNonFilterable);
|
|
119
123
|
}
|
|
120
124
|
});
|