@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.
@@ -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
- restProps.onDragStart?.(event);
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
- {...restProps}
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 || subField.filterable) {
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 || field.filterable) {
121
+ if (shouldShowFieldInMeta(field, includeNonFilterable)) {
118
122
  properties[field.name] = createFieldMetadata(field, includeNonFilterable);
119
123
  }
120
124
  });