@finos/legend-query-builder 4.14.31 → 4.14.33

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. package/lib/__lib__/QueryBuilderTesting.d.ts +3 -1
  2. package/lib/__lib__/QueryBuilderTesting.d.ts.map +1 -1
  3. package/lib/__lib__/QueryBuilderTesting.js +3 -1
  4. package/lib/__lib__/QueryBuilderTesting.js.map +1 -1
  5. package/lib/components/QueryBuilderPropertyExpressionEditor.d.ts +3 -3
  6. package/lib/components/QueryBuilderPropertyExpressionEditor.d.ts.map +1 -1
  7. package/lib/components/QueryBuilderPropertyExpressionEditor.js +23 -15
  8. package/lib/components/QueryBuilderPropertyExpressionEditor.js.map +1 -1
  9. package/lib/components/fetch-structure/QueryBuilderTDSPanel.d.ts.map +1 -1
  10. package/lib/components/fetch-structure/QueryBuilderTDSPanel.js +7 -7
  11. package/lib/components/fetch-structure/QueryBuilderTDSPanel.js.map +1 -1
  12. package/lib/components/filter/QueryBuilderFilterPanel.d.ts.map +1 -1
  13. package/lib/components/filter/QueryBuilderFilterPanel.js +32 -25
  14. package/lib/components/filter/QueryBuilderFilterPanel.js.map +1 -1
  15. package/lib/components/result/QueryBuilderResultPanel.d.ts +7 -0
  16. package/lib/components/result/QueryBuilderResultPanel.d.ts.map +1 -1
  17. package/lib/components/result/QueryBuilderResultPanel.js +1 -1
  18. package/lib/components/result/QueryBuilderResultPanel.js.map +1 -1
  19. package/lib/index.css +2 -2
  20. package/lib/index.css.map +1 -1
  21. package/lib/index.d.ts +1 -0
  22. package/lib/index.d.ts.map +1 -1
  23. package/lib/index.js +1 -0
  24. package/lib/index.js.map +1 -1
  25. package/lib/package.json +1 -1
  26. package/lib/stores/QueryBuilderStateBuilder.d.ts.map +1 -1
  27. package/lib/stores/QueryBuilderStateBuilder.js +7 -1
  28. package/lib/stores/QueryBuilderStateBuilder.js.map +1 -1
  29. package/lib/stores/fetch-structure/tds/projection/QueryBuilderProjectionStateBuilder.d.ts +0 -1
  30. package/lib/stores/fetch-structure/tds/projection/QueryBuilderProjectionStateBuilder.d.ts.map +1 -1
  31. package/lib/stores/fetch-structure/tds/projection/QueryBuilderProjectionStateBuilder.js +2 -27
  32. package/lib/stores/fetch-structure/tds/projection/QueryBuilderProjectionStateBuilder.js.map +1 -1
  33. package/lib/stores/fetch-structure/tds/projection/QueryBuilderTypedProjectionStateBuilder.d.ts +20 -0
  34. package/lib/stores/fetch-structure/tds/projection/QueryBuilderTypedProjectionStateBuilder.d.ts.map +1 -0
  35. package/lib/stores/fetch-structure/tds/projection/QueryBuilderTypedProjectionStateBuilder.js +54 -0
  36. package/lib/stores/fetch-structure/tds/projection/QueryBuilderTypedProjectionStateBuilder.js.map +1 -0
  37. package/package.json +2 -2
  38. package/src/__lib__/QueryBuilderTesting.ts +3 -1
  39. package/src/components/QueryBuilderPropertyExpressionEditor.tsx +34 -20
  40. package/src/components/fetch-structure/QueryBuilderTDSPanel.tsx +9 -16
  41. package/src/components/filter/QueryBuilderFilterPanel.tsx +133 -126
  42. package/src/components/result/QueryBuilderResultPanel.tsx +1 -1
  43. package/src/index.ts +1 -0
  44. package/src/stores/QueryBuilderStateBuilder.ts +18 -5
  45. package/src/stores/fetch-structure/tds/projection/QueryBuilderProjectionStateBuilder.ts +1 -68
  46. package/src/stores/fetch-structure/tds/projection/QueryBuilderTypedProjectionStateBuilder.ts +103 -0
  47. package/tsconfig.json +1 -0
@@ -37,6 +37,7 @@ import {
37
37
  import { observer } from 'mobx-react-lite';
38
38
  import {
39
39
  generateValueSpecificationForParameter,
40
+ getPropertyChainName,
40
41
  type QueryBuilderDerivedPropertyExpressionState,
41
42
  type QueryBuilderPropertyExpressionState,
42
43
  } from '../stores/QueryBuilderPropertyEditorState.js';
@@ -382,15 +383,16 @@ export const QueryBuilderPropertyExpressionEditor = observer(
382
383
  export const QueryBuilderEditablePropertyName = observer(
383
384
  (props: {
384
385
  columnName: string;
385
- changeColumnName?:
386
- | ((event: ChangeEvent<HTMLInputElement>) => void)
387
- | undefined;
386
+ setColumnName?: ((columnName: string) => void) | undefined;
388
387
  error: string | undefined;
389
388
  title: string;
389
+ defaultColumnName: string;
390
390
  }) => {
391
- const { columnName, changeColumnName, error, title } = props;
391
+ const { columnName, setColumnName, error, title, defaultColumnName } =
392
+ props;
392
393
 
393
394
  const [isEditingColumnName, setIsEditingColumnName] = useState(false);
395
+ const [selectedColumnName, setSelectedColumnName] = useState(columnName);
394
396
  const columnNameInputRef = useRef<HTMLInputElement>(null);
395
397
 
396
398
  useEffect(() => {
@@ -399,26 +401,33 @@ export const QueryBuilderEditablePropertyName = observer(
399
401
  }
400
402
  }, [isEditingColumnName, columnNameInputRef]);
401
403
 
404
+ const handleFinishEditing = (): void => {
405
+ const trimmedSelectedColumnName = selectedColumnName.trim();
406
+ if (trimmedSelectedColumnName.length > 0) {
407
+ setColumnName?.(trimmedSelectedColumnName);
408
+ setSelectedColumnName(trimmedSelectedColumnName);
409
+ } else {
410
+ setColumnName?.(defaultColumnName);
411
+ setSelectedColumnName(defaultColumnName);
412
+ }
413
+ setIsEditingColumnName(false);
414
+ };
415
+
402
416
  return isEditingColumnName ? (
403
417
  <div className="query-builder__property__name__editor">
404
418
  <InputWithInlineValidation
405
419
  className="query-builder__property__name__editor__input input-group__input"
406
420
  spellCheck={false}
407
- value={columnName}
408
- onChange={changeColumnName}
421
+ value={selectedColumnName}
422
+ onChange={(event: ChangeEvent<HTMLInputElement>) =>
423
+ setSelectedColumnName(event.target.value)
424
+ }
409
425
  onKeyDown={(event: React.KeyboardEvent<HTMLInputElement>) => {
410
426
  if (event.key === 'Enter') {
411
- if (columnName.length > 0) {
412
- setIsEditingColumnName(false);
413
- }
414
- }
415
- }}
416
- onBlur={() => {
417
- if (columnName.length > 0) {
418
- setIsEditingColumnName(false);
427
+ handleFinishEditing();
419
428
  }
420
429
  }}
421
- error={error}
430
+ onBlur={handleFinishEditing}
422
431
  ref={columnNameInputRef}
423
432
  draggable={true}
424
433
  onDragStart={(e: React.DragEvent<HTMLInputElement>) => {
@@ -434,10 +443,10 @@ export const QueryBuilderEditablePropertyName = observer(
434
443
  <span
435
444
  className={clsx('query-builder__property__name__display__content', {
436
445
  'query-builder__property__name__display__content--error': error,
437
- 'editable-value': changeColumnName,
446
+ 'editable-value': setColumnName,
438
447
  })}
439
448
  onClick={() => {
440
- if (changeColumnName) {
449
+ if (setColumnName) {
441
450
  setIsEditingColumnName(true);
442
451
  }
443
452
  }}
@@ -456,14 +465,14 @@ export const QueryBuilderPropertyExpressionBadge = observer(
456
465
  onPropertyExpressionChange: (
457
466
  node: QueryBuilderExplorerTreePropertyNodeData,
458
467
  ) => void;
459
- changeColumnName?: (event: ChangeEvent<HTMLInputElement>) => void;
468
+ setColumnName?: (columnName: string) => void;
460
469
  error?: string | undefined;
461
470
  }) => {
462
471
  const {
463
472
  columnName,
464
473
  propertyExpressionState,
465
474
  onPropertyExpressionChange,
466
- changeColumnName,
475
+ setColumnName,
467
476
  error,
468
477
  } = props;
469
478
  const type =
@@ -529,9 +538,14 @@ export const QueryBuilderPropertyExpressionBadge = observer(
529
538
  >
530
539
  <QueryBuilderEditablePropertyName
531
540
  columnName={columnName ?? propertyExpressionState.title}
532
- changeColumnName={changeColumnName}
541
+ setColumnName={setColumnName}
533
542
  error={error}
534
543
  title={`${propertyExpressionState.title} - ${propertyExpressionState.path}`}
544
+ defaultColumnName={getPropertyChainName(
545
+ propertyExpressionState.propertyExpression,
546
+ propertyExpressionState.queryBuilderState.explorerState
547
+ .humanizePropertyName,
548
+ )}
535
549
  />
536
550
  {hasDerivedPropertyInExpression && (
537
551
  <button
@@ -14,14 +14,7 @@
14
14
  * limitations under the License.
15
15
  */
16
16
 
17
- import {
18
- useEffect,
19
- useRef,
20
- useCallback,
21
- useState,
22
- forwardRef,
23
- type ChangeEvent,
24
- } from 'react';
17
+ import { useEffect, useRef, useCallback, useState, forwardRef } from 'react';
25
18
  import { observer } from 'mobx-react-lite';
26
19
  import {
27
20
  clsx,
@@ -161,10 +154,10 @@ const QueryBuilderProjectionColumnContextMenu = observer(
161
154
  const QueryBuilderSimpleProjectionColumnEditor = observer(
162
155
  (props: {
163
156
  projectionColumnState: QueryBuilderSimpleProjectionColumnState;
164
- changeColumnName: (event: ChangeEvent<HTMLInputElement>) => void;
157
+ setColumnName: (columnName: string) => void;
165
158
  error?: string | undefined;
166
159
  }) => {
167
- const { projectionColumnState, changeColumnName, error } = props;
160
+ const { projectionColumnState, setColumnName, error } = props;
168
161
  const onPropertyExpressionChange = (
169
162
  node: QueryBuilderExplorerTreePropertyNodeData,
170
163
  ): void =>
@@ -179,7 +172,7 @@ const QueryBuilderSimpleProjectionColumnEditor = observer(
179
172
  columnName={projectionColumnState.columnName}
180
173
  propertyExpressionState={projectionColumnState.propertyExpressionState}
181
174
  onPropertyExpressionChange={onPropertyExpressionChange}
182
- changeColumnName={changeColumnName}
175
+ setColumnName={setColumnName}
183
176
  error={error}
184
177
  />
185
178
  );
@@ -391,9 +384,8 @@ const QueryBuilderProjectionColumnEditor = observer(
391
384
  tdsState.removeColumn(projectionColumnState);
392
385
 
393
386
  // name
394
- const changeColumnName: React.ChangeEventHandler<HTMLInputElement> = (
395
- event,
396
- ) => projectionColumnState.setColumnName(event.target.value);
387
+ const setColumnName = (columnName: string): void =>
388
+ projectionColumnState.setColumnName(columnName);
397
389
  const isDuplicatedColumnName =
398
390
  projectionColumnState.tdsState.isDuplicateColumn(projectionColumnState);
399
391
 
@@ -787,7 +779,7 @@ const QueryBuilderProjectionColumnEditor = observer(
787
779
  <div className="query-builder__projection__column__value">
788
780
  <QueryBuilderSimpleProjectionColumnEditor
789
781
  projectionColumnState={projectionColumnState}
790
- changeColumnName={changeColumnName}
782
+ setColumnName={setColumnName}
791
783
  error={
792
784
  isDuplicatedColumnName
793
785
  ? 'Duplicated column'
@@ -814,7 +806,7 @@ const QueryBuilderProjectionColumnEditor = observer(
814
806
  <div className="query-builder__projection__column__value">
815
807
  <QueryBuilderEditablePropertyName
816
808
  columnName={projectionColumnState.columnName}
817
- changeColumnName={changeColumnName}
809
+ setColumnName={setColumnName}
818
810
  error={
819
811
  isDuplicatedColumnName
820
812
  ? 'Duplicated column'
@@ -823,6 +815,7 @@ const QueryBuilderProjectionColumnEditor = observer(
823
815
  : undefined
824
816
  }
825
817
  title={projectionColumnState.columnName}
818
+ defaultColumnName="(derivation)"
826
819
  />
827
820
  <QueryBuilderDerivationProjectionColumnEditor
828
821
  projectionColumnState={projectionColumnState}
@@ -25,7 +25,6 @@ import {
25
25
  MenuContent,
26
26
  MenuContentItem,
27
27
  BlankPanelPlaceholder,
28
- FilledTriangleIcon,
29
28
  CompressIcon,
30
29
  ExpandIcon,
31
30
  TrashIcon,
@@ -33,8 +32,6 @@ import {
33
32
  CircleIcon,
34
33
  CaretDownIcon,
35
34
  PlusIcon,
36
- ChevronDownIcon,
37
- ChevronRightIcon,
38
35
  PlusCircleIcon,
39
36
  TimesIcon,
40
37
  PanelDropZone,
@@ -632,28 +629,21 @@ const QueryBuilderFilterGroupConditionEditor = observer(
632
629
  node.groupOperation === QUERY_BUILDER_GROUP_OPERATION.AND ? 'AND' : 'OR';
633
630
 
634
631
  return (
635
- <div className="query-builder-filter-tree__node__label__content dnd__entry__container">
632
+ <div className="dnd__entry__container">
636
633
  <PanelEntryDropZonePlaceholder
637
634
  isDragOver={isDragOver}
638
635
  isDroppable={isDroppable}
639
- label={`Add to Logical Group '${operationName}'`}
636
+ label={operationName}
637
+ className="query-builder-filter-tree__group-node__drop-zone"
640
638
  >
641
639
  <div
642
- className={clsx('query-builder-filter-tree__group-node', {
643
- 'query-builder-filter-tree__group-node--and':
644
- node.groupOperation === QUERY_BUILDER_GROUP_OPERATION.AND,
645
- 'query-builder-filter-tree__group-node--or':
646
- node.groupOperation === QUERY_BUILDER_GROUP_OPERATION.OR,
647
- })}
640
+ className="query-builder-filter-tree__group-node"
648
641
  title="Switch Operation"
649
642
  onClick={switchOperation}
650
643
  >
651
- <div className="query-builder-filter-tree__group-node__label">
644
+ <div className="query-builder-filter-tree__group-node__label editable-value">
652
645
  {node.groupOperation}
653
646
  </div>
654
- <button className="query-builder-filter-tree__group-node__action">
655
- <FilledTriangleIcon />
656
- </button>
657
647
  </div>
658
648
  </PanelEntryDropZonePlaceholder>
659
649
  </div>
@@ -680,11 +670,12 @@ const QueryBuilderFilterExistsConditionEditor = observer(
680
670
  };
681
671
 
682
672
  return (
683
- <div className="query-builder-filter-tree__node__label__content dnd__entry__container">
673
+ <div className="dnd__entry__container">
684
674
  <PanelEntryDropZonePlaceholder
685
675
  isDragOver={isDragOver}
686
676
  isDroppable={isDroppable}
687
- label={`Add to Exists Group`}
677
+ label="Add to Exists Group"
678
+ className="query-builder-filter-tree__exists-node__drop-zone"
688
679
  >
689
680
  <div
690
681
  className="query-builder-filter-tree__exists-node"
@@ -810,7 +801,7 @@ const QueryBuilderFilterConditionEditor = observer(
810
801
 
811
802
  return (
812
803
  <div
813
- className="query-builder-filter-tree__node__label__content dnd__entry__container"
804
+ className="dnd__entry__container"
814
805
  data-testid={
815
806
  QUERY_BUILDER_TEST_ID.QUERY_BUILDER_FILTER_TREE_CONDITION_NODE_CONTENT
816
807
  }
@@ -975,17 +966,14 @@ const QueryBuilderFilterTreeNodeContainer = observer(
975
966
  }
976
967
  >,
977
968
  ) => {
978
- const { node, level, stepPaddingInRem, onNodeSelect, innerProps } = props;
969
+ const { node, onNodeSelect, innerProps } = props;
979
970
  const { queryBuilderState } = innerProps;
980
- const ref = useRef<HTMLDivElement>(null);
971
+ const dragRef = useRef<HTMLDivElement>(null);
981
972
  const [isSelectedFromContextMenu, setIsSelectedFromContextMenu] =
982
973
  useState(false);
983
974
  const applicationStore = useApplicationStore();
984
975
  const filterState = queryBuilderState.filterState;
985
- const isExpandable =
986
- node instanceof QueryBuilderFilterTreeOperationNodeData;
987
976
  const selectNode = (): void => onNodeSelect?.(node);
988
- const toggleExpandNode = (): void => node.setIsOpen(!node.isOpen);
989
977
  const removeNode = (): void => filterState.removeNodeAndPruneBranch(node);
990
978
 
991
979
  // Drag and Drop
@@ -1108,10 +1096,13 @@ const QueryBuilderFilterTreeNodeContainer = observer(
1108
1096
  : QUERY_BUILDER_FILTER_DND_TYPE.BLANK_CONDITION,
1109
1097
  item: () => ({ node }),
1110
1098
  end: (): void => filterState.setRearrangingConditions(false),
1099
+ canDrag: () =>
1100
+ node instanceof QueryBuilderFilterTreeConditionNodeData ||
1101
+ node instanceof QueryBuilderFilterTreeBlankConditionNodeData,
1111
1102
  }),
1112
1103
  [node, filterState],
1113
1104
  );
1114
- dragConnector(dropConnector(ref));
1105
+ dragConnector(dropConnector(dragRef));
1115
1106
  useDragPreviewLayer(dragPreviewConnector);
1116
1107
 
1117
1108
  const { isDroppable } = useDragLayer((monitor) => ({
@@ -1127,105 +1118,108 @@ const QueryBuilderFilterTreeNodeContainer = observer(
1127
1118
  const onContextMenuOpen = (): void => setIsSelectedFromContextMenu(true);
1128
1119
  const onContextMenuClose = (): void => setIsSelectedFromContextMenu(false);
1129
1120
 
1121
+ const isEmptyExistsNode =
1122
+ node instanceof QueryBuilderFilterTreeExistsNodeData &&
1123
+ node.childrenIds.length === 0;
1124
+ const showRemoveButton =
1125
+ node instanceof QueryBuilderFilterTreeConditionNodeData ||
1126
+ node instanceof QueryBuilderFilterTreeBlankConditionNodeData ||
1127
+ isEmptyExistsNode;
1128
+
1130
1129
  return (
1131
- <ContextMenu
1132
- content={
1133
- <QueryBuilderFilterConditionContextMenu
1134
- queryBuilderState={queryBuilderState}
1135
- node={node}
1136
- />
1130
+ <div
1131
+ data-testid={
1132
+ QUERY_BUILDER_TEST_ID.QUERY_BUILDER_FILTER_TREE_NODE_CONTAINER
1133
+ }
1134
+ onClick={
1135
+ node instanceof QueryBuilderFilterTreeConditionNodeData ||
1136
+ node instanceof QueryBuilderFilterTreeBlankConditionNodeData
1137
+ ? selectNode
1138
+ : undefined
1137
1139
  }
1138
- menuProps={{ elevation: 7 }}
1139
- onOpen={onContextMenuOpen}
1140
- onClose={onContextMenuClose}
1140
+ className={clsx('query-builder-filter-tree__node__container', {
1141
+ 'query-builder-filter-tree__node__container--group':
1142
+ node instanceof QueryBuilderFilterTreeGroupNodeData,
1143
+ 'query-builder-filter-tree__node__container--condition':
1144
+ node instanceof QueryBuilderFilterTreeConditionNodeData ||
1145
+ node instanceof QueryBuilderFilterTreeBlankConditionNodeData,
1146
+ 'query-builder-filter-tree__node__container--exists':
1147
+ node instanceof QueryBuilderFilterTreeExistsNodeData,
1148
+ 'query-builder-filter-tree__node__container--exists--empty':
1149
+ isEmptyExistsNode,
1150
+ 'query-builder-filter-tree__node__container--no-hover':
1151
+ filterState.isRearrangingConditions,
1152
+ 'query-builder-filter-tree__node__container--selected':
1153
+ node === filterState.selectedNode,
1154
+ 'query-builder-filter-tree__node__container--selected-from-context-menu':
1155
+ isSelectedFromContextMenu,
1156
+ })}
1141
1157
  >
1142
- <div
1143
- ref={ref}
1144
- data-testid={
1145
- QUERY_BUILDER_TEST_ID.QUERY_BUILDER_FILTER_TREE_NODE_CONTENT
1158
+ <ContextMenu
1159
+ content={
1160
+ <QueryBuilderFilterConditionContextMenu
1161
+ queryBuilderState={queryBuilderState}
1162
+ node={node}
1163
+ />
1146
1164
  }
1147
- className={clsx(
1148
- 'tree-view__node__container query-builder-filter-tree__node__container',
1149
- {
1150
- 'query-builder-filter-tree__node__container--no-hover':
1151
- filterState.isRearrangingConditions,
1152
- 'query-builder-filter-tree__node__container--selected':
1153
- node === filterState.selectedNode,
1154
- 'query-builder-filter-tree__node__container--selected-from-context-menu':
1155
- isSelectedFromContextMenu,
1156
- },
1157
- )}
1165
+ menuProps={{ elevation: 7 }}
1166
+ onOpen={onContextMenuOpen}
1167
+ onClose={onContextMenuClose}
1168
+ className="query-builder-filter-tree__node__context-menu"
1158
1169
  >
1159
1170
  <div
1171
+ data-testid={
1172
+ QUERY_BUILDER_TEST_ID.QUERY_BUILDER_FILTER_TREE_NODE_CONTENT
1173
+ }
1160
1174
  className="query-builder-filter-tree__node__content"
1161
- style={{
1162
- paddingLeft: `${(level - 1) * (stepPaddingInRem ?? 2) + 1.5}rem`,
1163
- display: 'flex',
1164
- }}
1165
- onClick={selectNode}
1175
+ ref={dragRef}
1166
1176
  >
1167
- {isExpandable && (
1168
- <div
1169
- className="query-builder-filter-tree__expand-icon"
1170
- onClick={toggleExpandNode}
1171
- >
1172
- {node.isOpen ? <ChevronDownIcon /> : <ChevronRightIcon />}
1173
- </div>
1177
+ {node instanceof QueryBuilderFilterTreeGroupNodeData && (
1178
+ <QueryBuilderFilterGroupConditionEditor
1179
+ node={node}
1180
+ isDroppable={isDroppable}
1181
+ isDragOver={isDragOver}
1182
+ />
1183
+ )}
1184
+ {node instanceof QueryBuilderFilterTreeExistsNodeData && (
1185
+ <QueryBuilderFilterExistsConditionEditor
1186
+ node={node}
1187
+ humanizePropertyName={
1188
+ filterState.queryBuilderState.explorerState
1189
+ .humanizePropertyName
1190
+ }
1191
+ isDroppable={isDroppable}
1192
+ isDragOver={isDragOver}
1193
+ />
1194
+ )}
1195
+ {node instanceof QueryBuilderFilterTreeConditionNodeData && (
1196
+ <QueryBuilderFilterConditionEditor
1197
+ node={node}
1198
+ isDragOver={isDragOver}
1199
+ />
1200
+ )}
1201
+ {node instanceof QueryBuilderFilterTreeBlankConditionNodeData && (
1202
+ <QueryBuilderFilterBlankConditionEditor
1203
+ node={node}
1204
+ isDragOver={isDragOver}
1205
+ isDroppable={isDroppable}
1206
+ />
1174
1207
  )}
1175
- <div
1176
- className={clsx(
1177
- 'tree-view__node__label query-builder-filter-tree__node__label',
1178
- {
1179
- 'query-builder-filter-tree__node__label--expandable':
1180
- isExpandable,
1181
- },
1182
- )}
1183
- >
1184
- {node instanceof QueryBuilderFilterTreeGroupNodeData && (
1185
- <QueryBuilderFilterGroupConditionEditor
1186
- node={node}
1187
- isDroppable={isDroppable}
1188
- isDragOver={isDragOver}
1189
- />
1190
- )}
1191
- {node instanceof QueryBuilderFilterTreeExistsNodeData && (
1192
- <QueryBuilderFilterExistsConditionEditor
1193
- node={node}
1194
- humanizePropertyName={
1195
- filterState.queryBuilderState.explorerState
1196
- .humanizePropertyName
1197
- }
1198
- isDroppable={isDroppable}
1199
- isDragOver={isDragOver}
1200
- />
1201
- )}
1202
- {node instanceof QueryBuilderFilterTreeConditionNodeData && (
1203
- <QueryBuilderFilterConditionEditor
1204
- node={node}
1205
- isDragOver={isDragOver}
1206
- />
1207
- )}
1208
- {node instanceof QueryBuilderFilterTreeBlankConditionNodeData && (
1209
- <QueryBuilderFilterBlankConditionEditor
1210
- node={node}
1211
- isDragOver={isDragOver}
1212
- isDroppable={isDroppable}
1213
- />
1214
- )}
1215
- </div>
1216
- </div>
1217
- <div className="query-builder-filter-tree__node__actions">
1218
- <button
1219
- className="query-builder-filter-tree__node__action"
1220
- tabIndex={-1}
1221
- title="Remove"
1222
- onClick={removeNode}
1223
- >
1224
- <TimesIcon />
1225
- </button>
1226
1208
  </div>
1227
- </div>
1228
- </ContextMenu>
1209
+ {showRemoveButton && (
1210
+ <div className="query-builder-filter-tree__node__actions">
1211
+ <button
1212
+ className="query-builder-filter-tree__node__action"
1213
+ tabIndex={-1}
1214
+ title="Remove"
1215
+ onClick={removeNode}
1216
+ >
1217
+ <TimesIcon />
1218
+ </button>
1219
+ </div>
1220
+ )}
1221
+ </ContextMenu>
1222
+ </div>
1229
1223
  );
1230
1224
  },
1231
1225
  );
@@ -1249,8 +1243,13 @@ const QueryBuilderFilterTreeNodeView = observer(
1249
1243
  } = props;
1250
1244
  return (
1251
1245
  <div
1252
- data-testid={QUERY_BUILDER_TEST_ID.QUERY_BUILDER_FILTER_TREE_NODE}
1253
- className="tree-view__node__block"
1246
+ data-testid={QUERY_BUILDER_TEST_ID.QUERY_BUILDER_FILTER_TREE_NODE_BLOCK}
1247
+ className={clsx('query-builder-filter-tree__node__block', {
1248
+ 'query-builder-filter-tree__node__block--group':
1249
+ node instanceof QueryBuilderFilterTreeGroupNodeData,
1250
+ 'query-builder-filter-tree__node__block--exists':
1251
+ node instanceof QueryBuilderFilterTreeExistsNodeData,
1252
+ })}
1254
1253
  >
1255
1254
  <QueryBuilderFilterTreeNodeContainer
1256
1255
  node={node}
@@ -1259,17 +1258,25 @@ const QueryBuilderFilterTreeNodeView = observer(
1259
1258
  onNodeSelect={onNodeSelect}
1260
1259
  innerProps={innerProps}
1261
1260
  />
1262
- {node.isOpen &&
1263
- getChildNodes(node).map((childNode) => (
1264
- <QueryBuilderFilterTreeNodeView
1265
- key={childNode.id}
1266
- node={childNode}
1267
- level={level + 1}
1268
- onNodeSelect={onNodeSelect}
1269
- getChildNodes={getChildNodes}
1270
- innerProps={innerProps}
1271
- />
1272
- ))}
1261
+ {node.isOpen && getChildNodes(node).length > 0 && (
1262
+ <div
1263
+ data-testid={
1264
+ QUERY_BUILDER_TEST_ID.QUERY_BUILDER_FILTER_TREE_NODE_CHILDREN
1265
+ }
1266
+ className="query-builder-filter-tree__node__children"
1267
+ >
1268
+ {getChildNodes(node).map((childNode) => (
1269
+ <QueryBuilderFilterTreeNodeView
1270
+ key={childNode.id}
1271
+ node={childNode}
1272
+ level={level + 1}
1273
+ onNodeSelect={onNodeSelect}
1274
+ getChildNodes={getChildNodes}
1275
+ innerProps={innerProps}
1276
+ />
1277
+ ))}
1278
+ </div>
1279
+ )}
1273
1280
  </div>
1274
1281
  );
1275
1282
  },
@@ -71,7 +71,7 @@ import { getExecutedSqlFromExecutionResult } from './tds/QueryBuilderTDSResultSh
71
71
  import { QueryBuilderTDSGridResult } from './tds/QueryBuilderTDSGridResult.js';
72
72
  import type { QueryBuilder_LegendApplicationPlugin_Extension } from '../../stores/QueryBuilder_LegendApplicationPlugin_Extension.js';
73
73
 
74
- const QueryBuilderResultValues = observer(
74
+ export const QueryBuilderResultValues = observer(
75
75
  (props: {
76
76
  executionResult: ExecutionResult;
77
77
  queryBuilderState: QueryBuilderState;
package/src/index.ts CHANGED
@@ -69,6 +69,7 @@ export {
69
69
  QueryBuilderDiffViewState,
70
70
  QueryBuilderChangeDetectionState,
71
71
  } from './stores/QueryBuilderChangeDetectionState.js';
72
+ export { QueryBuilderResultValues } from './components/result/QueryBuilderResultPanel.js';
72
73
 
73
74
  // ------------------------------------------- Shared components -------------------------------------------
74
75
 
@@ -93,6 +93,10 @@ import {
93
93
  import { checkIfEquivalent } from './milestoning/QueryBuilderMilestoningHelper.js';
94
94
  import type { QueryBuilderParameterValue } from './QueryBuilderParametersState.js';
95
95
  import { QueryBuilderEmbeddedFromExecutionContextState } from './QueryBuilderExecutionContextState.js';
96
+ import {
97
+ isTypedProjectionExpression,
98
+ processTypedTDSProjectExpression,
99
+ } from './fetch-structure/tds/projection/QueryBuilderTypedProjectionStateBuilder.js';
96
100
 
97
101
  const processGetAllExpression = (
98
102
  expression: SimpleFunctionExpression,
@@ -644,11 +648,20 @@ export class QueryBuilderValueSpecificationProcessor
644
648
  QUERY_BUILDER_SUPPORTED_FUNCTIONS.RELATION_PROJECT,
645
649
  ])
646
650
  ) {
647
- processTDSProjectExpression(
648
- valueSpecification,
649
- this.queryBuilderState,
650
- this.parentLambda,
651
- );
651
+ if (isTypedProjectionExpression(valueSpecification)) {
652
+ processTypedTDSProjectExpression(
653
+ valueSpecification,
654
+ this.queryBuilderState,
655
+ this.parentLambda,
656
+ );
657
+ } else {
658
+ processTDSProjectExpression(
659
+ valueSpecification,
660
+ this.queryBuilderState,
661
+ this.parentLambda,
662
+ );
663
+ }
664
+
652
665
  return;
653
666
  } else if (
654
667
  matchFunctionName(