@finos/legend-query-builder 4.0.8 → 4.0.9

Sign up to get free protection for your applications and to get access to all the features.
Files changed (87) hide show
  1. package/lib/__lib__/QueryBuilderColorTheme.js +1 -1
  2. package/lib/__lib__/QueryBuilderColorTheme.js.map +1 -1
  3. package/lib/__lib__/QueryBuilderDocumentation.js +1 -1
  4. package/lib/__lib__/QueryBuilderDocumentation.js.map +1 -1
  5. package/lib/__lib__/QueryBuilderEvent.js +3 -3
  6. package/lib/__lib__/QueryBuilderEvent.js.map +1 -1
  7. package/lib/__lib__/QueryBuilderSetting.js +1 -1
  8. package/lib/__lib__/QueryBuilderSetting.js.map +1 -1
  9. package/lib/__lib__/QueryBuilderTesting.js +1 -1
  10. package/lib/__lib__/QueryBuilderTesting.js.map +1 -1
  11. package/lib/components/QueryBuilderComponentElement.js +1 -1
  12. package/lib/components/QueryBuilderComponentElement.js.map +1 -1
  13. package/lib/components/QueryBuilderResultPanel.d.ts.map +1 -1
  14. package/lib/components/QueryBuilderResultPanel.js +2 -2
  15. package/lib/components/QueryBuilderResultPanel.js.map +1 -1
  16. package/lib/components/QueryBuilder_LegendApplicationPlugin.js +1 -2
  17. package/lib/components/QueryBuilder_LegendApplicationPlugin.js.map +1 -1
  18. package/lib/components/explorer/QueryBuilderExplorerPanel.d.ts.map +1 -1
  19. package/lib/components/explorer/QueryBuilderExplorerPanel.js +3 -3
  20. package/lib/components/explorer/QueryBuilderExplorerPanel.js.map +1 -1
  21. package/lib/components/fetch-structure/QueryBuilderFetchStructurePanel.d.ts.map +1 -1
  22. package/lib/components/fetch-structure/QueryBuilderFetchStructurePanel.js +3 -3
  23. package/lib/components/fetch-structure/QueryBuilderFetchStructurePanel.js.map +1 -1
  24. package/lib/components/fetch-structure/QueryBuilderPostFilterPanel.d.ts.map +1 -1
  25. package/lib/components/fetch-structure/QueryBuilderPostFilterPanel.js +60 -21
  26. package/lib/components/fetch-structure/QueryBuilderPostFilterPanel.js.map +1 -1
  27. package/lib/components/fetch-structure/QueryBuilderTDSPanel.d.ts.map +1 -1
  28. package/lib/components/fetch-structure/QueryBuilderTDSPanel.js +10 -2
  29. package/lib/components/fetch-structure/QueryBuilderTDSPanel.js.map +1 -1
  30. package/lib/components/fetch-structure/QueryBuilderTDSWindowPanel.d.ts.map +1 -1
  31. package/lib/components/fetch-structure/QueryBuilderTDSWindowPanel.js +2 -2
  32. package/lib/components/fetch-structure/QueryBuilderTDSWindowPanel.js.map +1 -1
  33. package/lib/components/filter/QueryBuilderFilterPanel.d.ts +4 -0
  34. package/lib/components/filter/QueryBuilderFilterPanel.d.ts.map +1 -1
  35. package/lib/components/filter/QueryBuilderFilterPanel.js +69 -31
  36. package/lib/components/filter/QueryBuilderFilterPanel.js.map +1 -1
  37. package/lib/components/shared/CustomDatePicker.js +1 -1
  38. package/lib/components/shared/CustomDatePicker.js.map +1 -1
  39. package/lib/graph/QueryBuilderMetaModelConst.js +3 -3
  40. package/lib/graph/QueryBuilderMetaModelConst.js.map +1 -1
  41. package/lib/graph-manager/QueryBuilderConst.js +1 -1
  42. package/lib/graph-manager/QueryBuilderConst.js.map +1 -1
  43. package/lib/graph-manager/protocol/pure/v1/V1_QueryBuilder_PureGraphManagerExtension.d.ts.map +1 -1
  44. package/lib/graph-manager/protocol/pure/v1/V1_QueryBuilder_PureGraphManagerExtension.js +4 -4
  45. package/lib/graph-manager/protocol/pure/v1/V1_QueryBuilder_PureGraphManagerExtension.js.map +1 -1
  46. package/lib/index.css +17 -1
  47. package/lib/index.css.map +1 -1
  48. package/lib/package.json +5 -5
  49. package/lib/stores/QueryBuilderChangeDetectionState.js +1 -1
  50. package/lib/stores/QueryBuilderChangeDetectionState.js.map +1 -1
  51. package/lib/stores/QueryBuilderCommand.js +1 -1
  52. package/lib/stores/QueryBuilderCommand.js.map +1 -1
  53. package/lib/stores/QueryBuilderConfig.js +2 -2
  54. package/lib/stores/QueryBuilderConfig.js.map +1 -1
  55. package/lib/stores/QueryBuilderGroupOperationHelper.js +1 -1
  56. package/lib/stores/QueryBuilderGroupOperationHelper.js.map +1 -1
  57. package/lib/stores/QueryBuilderStateHashUtils.js +1 -1
  58. package/lib/stores/QueryBuilderStateHashUtils.js.map +1 -1
  59. package/lib/stores/QueryBuilderTextEditorState.js +1 -1
  60. package/lib/stores/QueryBuilderTextEditorState.js.map +1 -1
  61. package/lib/stores/execution-plan/ExecutionPlanState.js +1 -1
  62. package/lib/stores/execution-plan/ExecutionPlanState.js.map +1 -1
  63. package/lib/stores/explorer/QueryBuilderExplorerState.js +1 -1
  64. package/lib/stores/explorer/QueryBuilderExplorerState.js.map +1 -1
  65. package/lib/stores/fetch-structure/QueryBuilderFetchStructureImplementationState.js +1 -1
  66. package/lib/stores/fetch-structure/QueryBuilderFetchStructureImplementationState.js.map +1 -1
  67. package/lib/stores/fetch-structure/graph-fetch/QueryBuilderGraphFetchTreeState.js +2 -2
  68. package/lib/stores/fetch-structure/graph-fetch/QueryBuilderGraphFetchTreeState.js.map +1 -1
  69. package/lib/stores/fetch-structure/tds/QueryBuilderTDSState.js +1 -1
  70. package/lib/stores/fetch-structure/tds/QueryBuilderTDSState.js.map +1 -1
  71. package/lib/stores/fetch-structure/tds/QueryResultSetModifierState.js +1 -1
  72. package/lib/stores/fetch-structure/tds/QueryResultSetModifierState.js.map +1 -1
  73. package/lib/stores/fetch-structure/tds/post-filter/QueryBuilderPostFilterState.js +2 -2
  74. package/lib/stores/fetch-structure/tds/post-filter/QueryBuilderPostFilterState.js.map +1 -1
  75. package/lib/stores/filter/QueryBuilderFilterState.js +1 -1
  76. package/lib/stores/filter/QueryBuilderFilterState.js.map +1 -1
  77. package/lib/stores/shared/LambdaParameterState.js +1 -1
  78. package/lib/stores/shared/LambdaParameterState.js.map +1 -1
  79. package/package.json +13 -13
  80. package/src/components/QueryBuilderResultPanel.tsx +1 -2
  81. package/src/components/explorer/QueryBuilderExplorerPanel.tsx +10 -14
  82. package/src/components/fetch-structure/QueryBuilderFetchStructurePanel.tsx +6 -4
  83. package/src/components/fetch-structure/QueryBuilderPostFilterPanel.tsx +103 -14
  84. package/src/components/fetch-structure/QueryBuilderTDSPanel.tsx +18 -1
  85. package/src/components/fetch-structure/QueryBuilderTDSWindowPanel.tsx +12 -10
  86. package/src/components/filter/QueryBuilderFilterPanel.tsx +134 -42
  87. package/src/graph-manager/protocol/pure/v1/V1_QueryBuilder_PureGraphManagerExtension.ts +4 -5
@@ -48,7 +48,12 @@ import {
48
48
  buildPropertyExpressionFromExplorerTreeNodeData,
49
49
  QUERY_BUILDER_EXPLORER_TREE_DND_TYPE,
50
50
  } from '../../stores/explorer/QueryBuilderExplorerState.js';
51
- import { type DropTargetMonitor, useDrag, useDrop } from 'react-dnd';
51
+ import {
52
+ type DropTargetMonitor,
53
+ useDrag,
54
+ useDrop,
55
+ useDragLayer,
56
+ } from 'react-dnd';
52
57
  import {
53
58
  type QueryBuilderProjectionColumnDragSource,
54
59
  type QueryBuilderProjectionColumnState,
@@ -932,6 +937,16 @@ export const QueryBuilderTDSPanel = observer(
932
937
  [handleDrop],
933
938
  );
934
939
 
940
+ const { showDroppableSuggestion } = useDragLayer((monitor) => ({
941
+ showDroppableSuggestion:
942
+ monitor.isDragging() &&
943
+ [
944
+ QUERY_BUILDER_EXPLORER_TREE_DND_TYPE.ENUM_PROPERTY,
945
+ QUERY_BUILDER_EXPLORER_TREE_DND_TYPE.PRIMITIVE_PROPERTY,
946
+ QUERY_BUILDER_FUNCTION_DND_TYPE,
947
+ ].includes(monitor.getItemType()?.toString() ?? ''),
948
+ }));
949
+
935
950
  useEffect(() => {
936
951
  flowResult(tdsState.convertDerivationProjectionObjects()).catch(
937
952
  applicationStore.alertUnhandledError,
@@ -974,6 +989,8 @@ export const QueryBuilderTDSPanel = observer(
974
989
  <div className="query-builder__projection__content">
975
990
  <PanelDropZone
976
991
  isDragOver={isDragOver}
992
+ showDroppableSuggestion={showDroppableSuggestion}
993
+ className="query-builder__panel--droppable"
977
994
  dropTargetConnector={dropTargetConnector}
978
995
  >
979
996
  {!projectionColumns.length && (
@@ -44,6 +44,10 @@ import {
44
44
  ModalFooterButton,
45
45
  PanelDnDEntryDragHandle,
46
46
  PanelDnDEntry,
47
+ PanelHeaderActionItem,
48
+ PanelHeader,
49
+ PanelHeaderActions,
50
+ Panel,
47
51
  } from '@finos/legend-art';
48
52
  import { assertErrorThrown, guaranteeNonNullable } from '@finos/legend-shared';
49
53
  import { observer } from 'mobx-react-lite';
@@ -1175,12 +1179,12 @@ export const QueryBuilderTDSWindowPanel = observer(
1175
1179
  [applicationStore, handleDrop],
1176
1180
  );
1177
1181
  return (
1178
- <div className="panel">
1182
+ <Panel>
1179
1183
  <div
1180
1184
  data-testid={QUERY_BUILDER_TEST_ID.QUERY_BUILDER_WINDOW_GROUPBY}
1181
1185
  className="panel"
1182
1186
  >
1183
- <div className="panel__header">
1187
+ <PanelHeader>
1184
1188
  <div className="panel__header__title">
1185
1189
  <div className="panel__header__title__label">Window Function</div>
1186
1190
  {tdsWindowState.windowValidationIssues.length && (
@@ -1189,18 +1193,16 @@ export const QueryBuilderTDSWindowPanel = observer(
1189
1193
  />
1190
1194
  )}
1191
1195
  </div>
1192
- <div className="panel__header__actions">
1193
- <button
1194
- className="panel__header__action"
1196
+ <PanelHeaderActions>
1197
+ <PanelHeaderActionItem
1195
1198
  onClick={createTDSWindow}
1196
1199
  disabled={!tdsWindowState.tdsState.tdsColumns.length}
1197
- tabIndex={-1}
1198
1200
  title="Create Window Function Column"
1199
1201
  >
1200
1202
  <PlusIcon />
1201
- </button>
1202
- </div>
1203
- </div>
1203
+ </PanelHeaderActionItem>
1204
+ </PanelHeaderActions>
1205
+ </PanelHeader>
1204
1206
  <PanelContent>
1205
1207
  <PanelDropZone
1206
1208
  isDragOver={isDragOver}
@@ -1234,7 +1236,7 @@ export const QueryBuilderTDSWindowPanel = observer(
1234
1236
  />
1235
1237
  )}
1236
1238
  </div>
1237
- </div>
1239
+ </Panel>
1238
1240
  );
1239
1241
  },
1240
1242
  );
@@ -56,7 +56,7 @@ import {
56
56
  QueryBuilderFilterTreeBlankConditionNodeData,
57
57
  QueryBuilderFilterTreeGroupNodeData,
58
58
  } from '../../stores/filter/QueryBuilderFilterState.js';
59
- import { useDrag, useDrop } from 'react-dnd';
59
+ import { useDrag, useDragLayer, useDrop } from 'react-dnd';
60
60
  import {
61
61
  type QueryBuilderExplorerTreeDragSource,
62
62
  type QueryBuilderExplorerTreePropertyNodeData,
@@ -88,12 +88,26 @@ import {
88
88
  } from '../shared/BasicValueSpecificationEditor.js';
89
89
  import { QueryBuilderTelemetryHelper } from '../../__lib__/QueryBuilderTelemetryHelper.js';
90
90
 
91
+ export const IS_DRAGGABLE_FILTER_DND_TYPES_FETCH_SUPPORTED = [
92
+ QUERY_BUILDER_FILTER_DND_TYPE.CONDITION,
93
+ QUERY_BUILDER_EXPLORER_TREE_DND_TYPE.ENUM_PROPERTY,
94
+ QUERY_BUILDER_EXPLORER_TREE_DND_TYPE.PRIMITIVE_PROPERTY,
95
+ QUERY_BUILDER_PROJECTION_COLUMN_DND_TYPE,
96
+ ];
97
+
98
+ export const IS_DRAGGABLE_FILTER_DND_TYPES = [
99
+ QUERY_BUILDER_FILTER_DND_TYPE.CONDITION,
100
+ QUERY_BUILDER_EXPLORER_TREE_DND_TYPE.ENUM_PROPERTY,
101
+ QUERY_BUILDER_EXPLORER_TREE_DND_TYPE.PRIMITIVE_PROPERTY,
102
+ ];
103
+
91
104
  const QueryBuilderFilterGroupConditionEditor = observer(
92
105
  (props: {
93
106
  node: QueryBuilderFilterTreeGroupNodeData;
94
107
  isDragOver: boolean;
108
+ showDroppableSuggestion: boolean;
95
109
  }) => {
96
- const { node, isDragOver } = props;
110
+ const { node, isDragOver, showDroppableSuggestion } = props;
97
111
  const switchOperation: React.MouseEventHandler<HTMLDivElement> = (
98
112
  event,
99
113
  ): void => {
@@ -105,30 +119,31 @@ const QueryBuilderFilterGroupConditionEditor = observer(
105
119
  );
106
120
  };
107
121
  return (
108
- <div className="query-builder-filter-tree__node__label__content">
109
- <PanelEntryDropZonePlaceholder
110
- showPlaceholder={isDragOver}
111
- label="Add to Logical Group"
112
- className="query-builder__dnd__placeholder"
113
- >
122
+ <div className="query-builder-filter-tree__node__label__content dnd__entry__container">
123
+ {showDroppableSuggestion && (
114
124
  <div
115
- className={clsx('query-builder-filter-tree__group-node', {
116
- 'query-builder-filter-tree__group-node--and':
117
- node.groupOperation === QUERY_BUILDER_GROUP_OPERATION.AND,
118
- 'query-builder-filter-tree__group-node--or':
119
- node.groupOperation === QUERY_BUILDER_GROUP_OPERATION.OR,
125
+ className={clsx('dnd__entry--droppable__indicator ', {
126
+ 'dnd__entry--droppable__indicator--dragover': isDragOver,
120
127
  })}
121
- title="Switch Operation"
122
- onClick={switchOperation}
123
- >
124
- <div className="query-builder-filter-tree__group-node__label">
125
- {node.groupOperation}
126
- </div>
127
- <button className="query-builder-filter-tree__group-node__action">
128
- <FilledTriangleIcon />
129
- </button>
128
+ ></div>
129
+ )}
130
+ <div
131
+ className={clsx('query-builder-filter-tree__group-node', {
132
+ 'query-builder-filter-tree__group-node--and':
133
+ node.groupOperation === QUERY_BUILDER_GROUP_OPERATION.AND,
134
+ 'query-builder-filter-tree__group-node--or':
135
+ node.groupOperation === QUERY_BUILDER_GROUP_OPERATION.OR,
136
+ })}
137
+ title="Switch Operation"
138
+ onClick={switchOperation}
139
+ >
140
+ <div className="query-builder-filter-tree__group-node__label">
141
+ {node.groupOperation}
130
142
  </div>
131
- </PanelEntryDropZonePlaceholder>
143
+ <button className="query-builder-filter-tree__group-node__action">
144
+ <FilledTriangleIcon />
145
+ </button>
146
+ </div>
132
147
  </div>
133
148
  );
134
149
  },
@@ -190,6 +205,7 @@ const QueryBuilderFilterConditionEditor = observer(
190
205
  }),
191
206
  [handleDrop],
192
207
  );
208
+
193
209
  const resetNode = (): void => {
194
210
  node.condition.setValue(
195
211
  node.condition.operator.getDefaultFilterConditionValue(node.condition),
@@ -215,8 +231,29 @@ const QueryBuilderFilterConditionEditor = observer(
215
231
  reloadValues: debouncedTypeaheadSearch,
216
232
  cleanUpReloadValues,
217
233
  };
234
+
235
+ const { showDroppableSuggestion } = useDragLayer((monitor) => ({
236
+ showDroppableSuggestion:
237
+ monitor.isDragging() &&
238
+ (queryBuilderState.TEMPORARY__isDnDFetchStructureToFilterSupported
239
+ ? IS_DRAGGABLE_FILTER_DND_TYPES_FETCH_SUPPORTED
240
+ : IS_DRAGGABLE_FILTER_DND_TYPES
241
+ ).includes(monitor.getItemType()?.toString() ?? ''),
242
+ }));
243
+
218
244
  return (
219
- <div className="query-builder-filter-tree__node__label__content">
245
+ <div className="query-builder-filter-tree__node__label__content dnd__entry__container">
246
+ {showDroppableSuggestion && (
247
+ <div
248
+ className={clsx(
249
+ 'dnd__entry--droppable__indicator dnd__entry-potential__oiodropzone__indicator--full',
250
+ {
251
+ 'dnd__entry--droppable__indicator--dragover': isDragOver,
252
+ },
253
+ )}
254
+ ></div>
255
+ )}
256
+
220
257
  <PanelEntryDropZonePlaceholder
221
258
  showPlaceholder={isDragOver}
222
259
  label="Add New Logical Group"
@@ -299,8 +336,9 @@ const QueryBuilderFilterBlankConditionEditor = observer(
299
336
  (props: {
300
337
  node: QueryBuilderFilterTreeBlankConditionNodeData;
301
338
  isDragOver: boolean;
339
+ showDroppableSuggestion: boolean;
302
340
  }) => {
303
- const { isDragOver } = props;
341
+ const { isDragOver, showDroppableSuggestion } = props;
304
342
  return (
305
343
  <div className="query-builder-filter-tree__node__label__content">
306
344
  <PanelEntryDropZonePlaceholder
@@ -308,6 +346,9 @@ const QueryBuilderFilterBlankConditionEditor = observer(
308
346
  label="Create Condition"
309
347
  className="query-builder__dnd__placeholder"
310
348
  >
349
+ {showDroppableSuggestion && (
350
+ <div className="query-builder-filter-tree__blank-node--droppable"></div>
351
+ )}
311
352
  <div className="query-builder-filter-tree__blank-node">blank</div>
312
353
  </PanelEntryDropZonePlaceholder>
313
354
  </div>
@@ -390,11 +431,31 @@ const QueryBuilderFilterTreeNodeContainer = observer(
390
431
  // Drag and Drop
391
432
  const handleDrop = useCallback(
392
433
  (item: QueryBuilderFilterDropTarget, type: string): void => {
393
- if (
394
- Object.values<string>(QUERY_BUILDER_FILTER_DND_TYPE).includes(type)
395
- ) {
396
- // const dropNode = (item as QueryBuilderFilterConditionDragSource).node;
397
- // TODO: re-arrange
434
+ if (QUERY_BUILDER_FILTER_DND_TYPE.CONDITION === type) {
435
+ const nodeBeingDragged = (
436
+ item as QueryBuilderFilterConditionDragSource
437
+ ).node;
438
+
439
+ const newCreatedNode = new QueryBuilderFilterTreeConditionNodeData(
440
+ undefined,
441
+ (
442
+ filterState.nodes.get(
443
+ nodeBeingDragged.id,
444
+ ) as QueryBuilderFilterTreeConditionNodeData
445
+ ).condition,
446
+ );
447
+
448
+ if (node instanceof QueryBuilderFilterTreeBlankConditionNodeData) {
449
+ filterState.replaceBlankNodeWithNode(newCreatedNode, node);
450
+ filterState.removeNodeAndPruneBranch(nodeBeingDragged);
451
+ } else if (node instanceof QueryBuilderFilterTreeConditionNodeData) {
452
+ filterState.newGroupWithConditionFromNode(newCreatedNode, node);
453
+
454
+ filterState.removeNodeAndPruneBranch(nodeBeingDragged);
455
+ } else if (node instanceof QueryBuilderFilterTreeGroupNodeData) {
456
+ filterState.addNodeFromNode(newCreatedNode, node);
457
+ filterState.removeNodeAndPruneBranch(nodeBeingDragged);
458
+ }
398
459
  } else {
399
460
  let filterConditionState: FilterConditionState;
400
461
  try {
@@ -469,17 +530,8 @@ const QueryBuilderFilterTreeNodeContainer = observer(
469
530
  () => ({
470
531
  accept:
471
532
  queryBuilderState.TEMPORARY__isDnDFetchStructureToFilterSupported
472
- ? [
473
- ...Object.values(QUERY_BUILDER_FILTER_DND_TYPE),
474
- QUERY_BUILDER_EXPLORER_TREE_DND_TYPE.ENUM_PROPERTY,
475
- QUERY_BUILDER_EXPLORER_TREE_DND_TYPE.PRIMITIVE_PROPERTY,
476
- QUERY_BUILDER_PROJECTION_COLUMN_DND_TYPE,
477
- ]
478
- : [
479
- ...Object.values(QUERY_BUILDER_FILTER_DND_TYPE),
480
- QUERY_BUILDER_EXPLORER_TREE_DND_TYPE.ENUM_PROPERTY,
481
- QUERY_BUILDER_EXPLORER_TREE_DND_TYPE.PRIMITIVE_PROPERTY,
482
- ],
533
+ ? IS_DRAGGABLE_FILTER_DND_TYPES_FETCH_SUPPORTED
534
+ : IS_DRAGGABLE_FILTER_DND_TYPES,
483
535
  drop: (item, monitor): void => {
484
536
  if (!monitor.didDrop()) {
485
537
  handleDrop(item, monitor.getItemType() as string);
@@ -508,6 +560,15 @@ const QueryBuilderFilterTreeNodeContainer = observer(
508
560
  dragConnector(dropConnector(ref));
509
561
  useDragPreviewLayer(dragPreviewConnector);
510
562
 
563
+ const { showDroppableSuggestion } = useDragLayer((monitor) => ({
564
+ showDroppableSuggestion:
565
+ monitor.isDragging() &&
566
+ (queryBuilderState.TEMPORARY__isDnDFetchStructureToFilterSupported
567
+ ? IS_DRAGGABLE_FILTER_DND_TYPES_FETCH_SUPPORTED
568
+ : IS_DRAGGABLE_FILTER_DND_TYPES
569
+ ).includes(monitor.getItemType()?.toString() ?? ''),
570
+ }));
571
+
511
572
  // context menu
512
573
  const onContextMenuOpen = (): void => setIsSelectedFromContextMenu(true);
513
574
  const onContextMenuClose = (): void => setIsSelectedFromContextMenu(false);
@@ -566,6 +627,7 @@ const QueryBuilderFilterTreeNodeContainer = observer(
566
627
  {node instanceof QueryBuilderFilterTreeGroupNodeData && (
567
628
  <QueryBuilderFilterGroupConditionEditor
568
629
  node={node}
630
+ showDroppableSuggestion={showDroppableSuggestion}
569
631
  isDragOver={isDragOver}
570
632
  />
571
633
  )}
@@ -579,6 +641,7 @@ const QueryBuilderFilterTreeNodeContainer = observer(
579
641
  <QueryBuilderFilterBlankConditionEditor
580
642
  node={node}
581
643
  isDragOver={isDragOver}
644
+ showDroppableSuggestion={showDroppableSuggestion}
582
645
  />
583
646
  )}
584
647
  </div>
@@ -747,6 +810,16 @@ export const QueryBuilderFilterPanel = observer(
747
810
  );
748
811
  }
749
812
  };
813
+
814
+ const { showDroppableSuggestion } = useDragLayer((monitor) => ({
815
+ showDroppableSuggestion:
816
+ monitor.isDragging() &&
817
+ (queryBuilderState.TEMPORARY__isDnDFetchStructureToFilterSupported
818
+ ? IS_DRAGGABLE_FILTER_DND_TYPES_FETCH_SUPPORTED
819
+ : IS_DRAGGABLE_FILTER_DND_TYPES
820
+ ).includes(monitor.getItemType()?.toString() ?? ''),
821
+ }));
822
+
750
823
  // Drag and Drop
751
824
  const handleDrop = useCallback(
752
825
  (item: QueryBuilderFilterDropTarget, type: string): void => {
@@ -795,6 +868,7 @@ export const QueryBuilderFilterPanel = observer(
795
868
  },
796
869
  [applicationStore, filterState],
797
870
  );
871
+
798
872
  const [{ isDragOver }, dropTargetConnector] = useDrop<
799
873
  QueryBuilderExplorerTreeDragSource,
800
874
  void,
@@ -913,7 +987,11 @@ export const QueryBuilderFilterPanel = observer(
913
987
  </div>
914
988
  <PanelContent>
915
989
  <PanelDropZone
916
- isDragOver={isDragOver}
990
+ isDragOver={isDragOver && filterState.isEmpty}
991
+ showDroppableSuggestion={
992
+ showDroppableSuggestion && filterState.isEmpty
993
+ }
994
+ className="query-builder__panel--droppable"
917
995
  dropTargetConnector={dropTargetConnector}
918
996
  >
919
997
  {filterState.isEmpty && (
@@ -933,6 +1011,20 @@ export const QueryBuilderFilterPanel = observer(
933
1011
  <QueryBuilderFilterTree queryBuilderState={queryBuilderState} />
934
1012
  </>
935
1013
  )}
1014
+
1015
+ {showDroppableSuggestion && !filterState.isEmpty && (
1016
+ <div
1017
+ ref={dropTargetConnector}
1018
+ className={clsx(
1019
+ 'query-builder-post-filter-tree__blank-node--droppable--tall',
1020
+ {
1021
+ 'dnd__entry--droppable__indicator--dragover': isDragOver,
1022
+ },
1023
+ )}
1024
+ >
1025
+ Add filter to main group
1026
+ </div>
1027
+ )}
936
1028
  </PanelDropZone>
937
1029
  </PanelContent>
938
1030
  </div>
@@ -25,7 +25,6 @@ import {
25
25
  V1_PureMultiExecution,
26
26
  V1_Service,
27
27
  V1_PackageableRuntime,
28
- type V1_PackageableElement,
29
28
  type PureModel,
30
29
  V1_PureGraphManager,
31
30
  PureClientVersion,
@@ -72,10 +71,10 @@ export class V1_QueryBuilder_PureGraphManagerExtension extends QueryBuilder_Pure
72
71
  entities,
73
72
  dependencyEntitiesIndex,
74
73
  (entity: Entity): boolean =>
75
- ((entity.content as PlainObject<V1_PackageableElement>)
76
- ._type as string) === V1_MAPPING_ELEMENT_PROTOCOL_TYPE ||
77
- ((entity.content as PlainObject<V1_PackageableElement>)
78
- ._type as string) === V1_PACKAGEABLE_RUNTIME_ELEMENT_PROTOCOL_TYPE,
74
+ (entity.content._type as string) ===
75
+ V1_MAPPING_ELEMENT_PROTOCOL_TYPE ||
76
+ (entity.content._type as string) ===
77
+ V1_PACKAGEABLE_RUNTIME_ELEMENT_PROTOCOL_TYPE,
79
78
  );
80
79
  // handle mapping includes
81
80
  const mappings = [