@finos/legend-data-cube 0.0.24 → 0.0.26

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.
Files changed (89) hide show
  1. package/docs/column-configuration.display-value-as-link.md +6 -0
  2. package/docs/column-configuration.kind.md +14 -0
  3. package/docs/column-configuration.missing-value-format.md +5 -0
  4. package/docs/column-configuration.unit.md +5 -0
  5. package/docs/extended-column.level.md +14 -0
  6. package/docs/grid-configuration.row-limit.md +5 -0
  7. package/lib/__lib__/DataCubeDocumentation.d.ts +5 -3
  8. package/lib/__lib__/DataCubeDocumentation.d.ts.map +1 -1
  9. package/lib/__lib__/DataCubeDocumentation.js +5 -3
  10. package/lib/__lib__/DataCubeDocumentation.js.map +1 -1
  11. package/lib/components/core/DataCubeFormUtils.d.ts +1 -0
  12. package/lib/components/core/DataCubeFormUtils.d.ts.map +1 -1
  13. package/lib/components/core/DataCubeFormUtils.js +4 -3
  14. package/lib/components/core/DataCubeFormUtils.js.map +1 -1
  15. package/lib/components/view/editor/DataCubeEditorColumnPropertiesPanel.d.ts.map +1 -1
  16. package/lib/components/view/editor/DataCubeEditorColumnPropertiesPanel.js +12 -9
  17. package/lib/components/view/editor/DataCubeEditorColumnPropertiesPanel.js.map +1 -1
  18. package/lib/components/view/editor/DataCubeEditorColumnSelector.js +2 -2
  19. package/lib/components/view/editor/DataCubeEditorGeneralPropertiesPanel.d.ts.map +1 -1
  20. package/lib/components/view/editor/DataCubeEditorGeneralPropertiesPanel.js +8 -7
  21. package/lib/components/view/editor/DataCubeEditorGeneralPropertiesPanel.js.map +1 -1
  22. package/lib/index.css +2 -2
  23. package/lib/index.css.map +1 -1
  24. package/lib/package.json +3 -3
  25. package/lib/stores/core/DataCubeQueryBuilder.d.ts +3 -0
  26. package/lib/stores/core/DataCubeQueryBuilder.d.ts.map +1 -1
  27. package/lib/stores/core/DataCubeQueryBuilder.js +8 -1
  28. package/lib/stores/core/DataCubeQueryBuilder.js.map +1 -1
  29. package/lib/stores/core/DataCubeQueryBuilderUtils.d.ts +1 -0
  30. package/lib/stores/core/DataCubeQueryBuilderUtils.d.ts.map +1 -1
  31. package/lib/stores/core/DataCubeQueryBuilderUtils.js +14 -7
  32. package/lib/stores/core/DataCubeQueryBuilderUtils.js.map +1 -1
  33. package/lib/stores/core/DataCubeQueryEngine.d.ts +6 -0
  34. package/lib/stores/core/DataCubeQueryEngine.d.ts.map +1 -1
  35. package/lib/stores/core/DataCubeQueryEngine.js +7 -0
  36. package/lib/stores/core/DataCubeQueryEngine.js.map +1 -1
  37. package/lib/stores/core/models/DataCubeConfiguration.d.ts +1 -0
  38. package/lib/stores/core/models/DataCubeConfiguration.d.ts.map +1 -1
  39. package/lib/stores/core/models/DataCubeConfiguration.js +2 -0
  40. package/lib/stores/core/models/DataCubeConfiguration.js.map +1 -1
  41. package/lib/stores/view/editor/DataCubeEditorGeneralPropertiesPanelState.d.ts +2 -2
  42. package/lib/stores/view/editor/DataCubeEditorGeneralPropertiesPanelState.d.ts.map +1 -1
  43. package/lib/stores/view/editor/DataCubeEditorGeneralPropertiesPanelState.js +4 -3
  44. package/lib/stores/view/editor/DataCubeEditorGeneralPropertiesPanelState.js.map +1 -1
  45. package/lib/stores/view/editor/DataCubeEditorMutableConfiguration.d.ts +1 -0
  46. package/lib/stores/view/editor/DataCubeEditorMutableConfiguration.d.ts.map +1 -1
  47. package/lib/stores/view/editor/DataCubeEditorMutableConfiguration.js +5 -0
  48. package/lib/stores/view/editor/DataCubeEditorMutableConfiguration.js.map +1 -1
  49. package/lib/stores/view/grid/DataCubeGridClientEngine.d.ts +14 -2
  50. package/lib/stores/view/grid/DataCubeGridClientEngine.d.ts.map +1 -1
  51. package/lib/stores/view/grid/DataCubeGridClientEngine.js +48 -42
  52. package/lib/stores/view/grid/DataCubeGridClientEngine.js.map +1 -1
  53. package/lib/stores/view/grid/DataCubeGridConfigurationBuilder.d.ts +20 -3
  54. package/lib/stores/view/grid/DataCubeGridConfigurationBuilder.d.ts.map +1 -1
  55. package/lib/stores/view/grid/DataCubeGridConfigurationBuilder.js +89 -40
  56. package/lib/stores/view/grid/DataCubeGridConfigurationBuilder.js.map +1 -1
  57. package/lib/stores/view/grid/DataCubeGridMenuBuilder.js +1 -1
  58. package/lib/stores/view/grid/DataCubeGridMenuBuilder.js.map +1 -1
  59. package/lib/stores/view/grid/DataCubeGridQueryBuilder.d.ts +13 -4
  60. package/lib/stores/view/grid/DataCubeGridQueryBuilder.d.ts.map +1 -1
  61. package/lib/stores/view/grid/DataCubeGridQueryBuilder.js +40 -9
  62. package/lib/stores/view/grid/DataCubeGridQueryBuilder.js.map +1 -1
  63. package/lib/stores/view/grid/DataCubeGridQuerySnapshotBuilder.d.ts +2 -2
  64. package/lib/stores/view/grid/DataCubeGridQuerySnapshotBuilder.d.ts.map +1 -1
  65. package/lib/stores/view/grid/DataCubeGridQuerySnapshotBuilder.js +30 -14
  66. package/lib/stores/view/grid/DataCubeGridQuerySnapshotBuilder.js.map +1 -1
  67. package/lib/stores/view/grid/DataCubeGridState.d.ts +2 -0
  68. package/lib/stores/view/grid/DataCubeGridState.d.ts.map +1 -1
  69. package/lib/stores/view/grid/DataCubeGridState.js +5 -1
  70. package/lib/stores/view/grid/DataCubeGridState.js.map +1 -1
  71. package/package.json +8 -8
  72. package/src/__lib__/DataCubeDocumentation.ts +5 -3
  73. package/src/components/core/DataCubeFormUtils.tsx +5 -1
  74. package/src/components/view/editor/DataCubeEditorColumnPropertiesPanel.tsx +40 -14
  75. package/src/components/view/editor/DataCubeEditorColumnSelector.tsx +2 -2
  76. package/src/components/view/editor/DataCubeEditorGeneralPropertiesPanel.tsx +41 -30
  77. package/src/stores/core/DataCubeQueryBuilder.ts +13 -0
  78. package/src/stores/core/DataCubeQueryBuilderUtils.ts +32 -6
  79. package/src/stores/core/DataCubeQueryEngine.ts +8 -0
  80. package/src/stores/core/models/DataCubeConfiguration.ts +2 -0
  81. package/src/stores/view/editor/DataCubeEditorGeneralPropertiesPanelState.ts +5 -4
  82. package/src/stores/view/editor/DataCubeEditorMutableConfiguration.ts +7 -0
  83. package/src/stores/view/grid/DataCubeGridClientEngine.ts +67 -52
  84. package/src/stores/view/grid/DataCubeGridConfigurationBuilder.tsx +99 -42
  85. package/src/stores/view/grid/DataCubeGridMenuBuilder.tsx +1 -1
  86. package/src/stores/view/grid/DataCubeGridQueryBuilder.ts +71 -15
  87. package/src/stores/view/grid/DataCubeGridQuerySnapshotBuilder.ts +40 -20
  88. package/src/stores/view/grid/DataCubeGridState.ts +12 -1
  89. package/tsconfig.json +2 -0
@@ -33,7 +33,7 @@ import type {
33
33
  ICellRendererParams,
34
34
  } from '@ag-grid-community/core';
35
35
  import {
36
- GridClientSortDirection,
36
+ DataCubeGridClientSortDirection,
37
37
  INTERNAL__GRID_CLIENT_COLUMN_MIN_WIDTH,
38
38
  INTERNAL__GridClientUtilityCssClassName,
39
39
  generateFontFamilyUtilityClassName,
@@ -43,7 +43,7 @@ import {
43
43
  generateTextColorUtilityClassName,
44
44
  generateBackgroundColorUtilityClassName,
45
45
  generateFontCaseUtilityClassName,
46
- GridClientPinnedAlignement,
46
+ DataCubeGridClientPinnedAlignement,
47
47
  INTERNAL__GRID_CLIENT_ROW_HEIGHT,
48
48
  INTERNAL__GRID_CLIENT_AUTO_RESIZE_PADDING,
49
49
  INTERNAL__GRID_CLIENT_HEADER_HEIGHT,
@@ -54,6 +54,7 @@ import {
54
54
  INTERNAL__GRID_CLIENT_DATA_FETCH_MANUAL_TRIGGER_COLUMN_ID,
55
55
  INTERNAL__GRID_CLIENT_PIVOT_COLUMN_GROUP_COLOR_ROTATION_SIZE,
56
56
  INTERNAL__GRID_CLIENT_TREE_COLUMN_ID,
57
+ INTERNAL__GRID_CLIENT_ROOT_AGGREGATION_COLUMN_ID,
57
58
  } from './DataCubeGridClientEngine.js';
58
59
  import {
59
60
  getNonNullableEntry,
@@ -82,6 +83,7 @@ import {
82
83
  PIVOT_COLUMN_NAME_VALUE_SEPARATOR,
83
84
  isPivotResultColumnName,
84
85
  TREE_COLUMN_VALUE_SEPARATOR,
86
+ DEFAULT_ROOT_AGGREGATION_COLUMN_VALUE,
85
87
  } from '../../core/DataCubeQueryEngine.js';
86
88
  import type { CustomLoadingCellRendererProps } from '@ag-grid-community/react';
87
89
  import { DataCubeIcon } from '@finos/legend-art';
@@ -213,21 +215,23 @@ function _displaySpec(columnData: ColumnData) {
213
215
  hide:
214
216
  column.hideFromView ||
215
217
  !column.isSelected ||
216
- Boolean(
218
+ (Boolean(
217
219
  snapshot.data.pivot &&
218
220
  !snapshot.data.pivot.castColumns.find((col) => col.name === name),
219
- ),
221
+ ) &&
222
+ !snapshot.data.groupExtendedColumns.find((col) => col.name === name)),
220
223
  lockVisible:
221
224
  !column.isSelected ||
222
- Boolean(
225
+ (Boolean(
223
226
  snapshot.data.pivot &&
224
227
  !snapshot.data.pivot.castColumns.find((col) => col.name === name),
225
- ),
228
+ ) &&
229
+ !snapshot.data.groupExtendedColumns.find((col) => col.name === name)),
226
230
  pinned:
227
231
  column.pinned !== undefined
228
232
  ? column.pinned === DataCubeColumnPinPlacement.RIGHT
229
- ? GridClientPinnedAlignement.RIGHT
230
- : GridClientPinnedAlignement.LEFT
233
+ ? DataCubeGridClientPinnedAlignement.RIGHT
234
+ : DataCubeGridClientPinnedAlignement.LEFT
231
235
  : null,
232
236
  headerClass: isPivotResultColumnName(name)
233
237
  ? 'pl-1 border border-neutral-300'
@@ -313,13 +317,18 @@ function _displaySpec(columnData: ColumnData) {
313
317
  }
314
318
  : {}),
315
319
  });
316
- // 3. add the parentheses (and then the unit)
317
- return (
320
+ // 3. add the parentheses, scale unit, unit, etc.
321
+ let displayValue =
318
322
  (showNegativeNumberInParens
319
323
  ? `(${formattedValue})`
320
324
  : formattedValue) +
321
- (scaledNumber.unit ? ` ${scaledNumber.unit}` : '')
322
- );
325
+ (scaledNumber.unit ? ` ${scaledNumber.unit}` : '');
326
+ if (column.unit) {
327
+ displayValue = !column.unit.startsWith('_')
328
+ ? `${displayValue}${column.unit}`
329
+ : `${column.unit.substring(1)}${displayValue}`;
330
+ }
331
+ return displayValue;
323
332
  }
324
333
  : (params) =>
325
334
  params.value === INTERNAL__GRID_CLIENT_MISSING_VALUE
@@ -357,7 +366,7 @@ function _groupDisplaySpec(
357
366
  hide: !snapshot.data.groupBy,
358
367
  lockPosition: true,
359
368
  lockPinned: true,
360
- pinned: GridClientPinnedAlignement.LEFT,
369
+ pinned: DataCubeGridClientPinnedAlignement.LEFT,
361
370
  cellClassRules: {
362
371
  [generateFontFamilyUtilityClassName(fontFamily)]: () => true,
363
372
  [generateFontSizeUtilityClassName(fontSize)]: () => true,
@@ -413,11 +422,6 @@ function _sizeSpec(columnData: ColumnData) {
413
422
  INTERNAL__GRID_CLIENT_COLUMN_MIN_WIDTH,
414
423
  ),
415
424
  maxWidth: column.maxWidth,
416
- // Make sure the column spans the width if possible so when total width
417
- // is less than grid width, removing/adding one column will cause the other
418
- // columns to take/give up space
419
- flex:
420
- column.fixedWidth === undefined ? 1 : (undefined as unknown as number),
421
425
  } as ColDef;
422
426
  }
423
427
 
@@ -429,28 +433,34 @@ function _sortSpec(columnData: ColumnData) {
429
433
  sortable: true, // if this is pivot column, no sorting is supported yet
430
434
  sort: sortCol
431
435
  ? sortCol.direction === DataCubeQuerySortDirection.ASCENDING
432
- ? GridClientSortDirection.ASCENDING
433
- : GridClientSortDirection.DESCENDING
436
+ ? DataCubeGridClientSortDirection.ASCENDING
437
+ : DataCubeGridClientSortDirection.DESCENDING
434
438
  : null,
435
439
  sortIndex: sortCol ? sortColumns.indexOf(sortCol) : null,
436
440
  } satisfies ColDef;
437
441
  }
438
442
 
439
443
  function _aggregationSpec(columnData: ColumnData) {
440
- const { name, snapshot, column } = columnData;
444
+ const { name, snapshot, column, configuration } = columnData;
441
445
  const data = snapshot.data;
442
446
  const pivotCol = _findCol(data.pivot?.columns, name);
443
447
  const groupByCol = _findCol(data.groupBy?.columns, name);
444
448
  const isGroupExtendedColumn = Boolean(
445
449
  _findCol(data.groupExtendedColumns, name),
446
450
  );
451
+ const rowGroupIndex =
452
+ !isGroupExtendedColumn && groupByCol
453
+ ? (data.groupBy?.columns.indexOf(groupByCol) ?? null)
454
+ : null;
447
455
  return {
448
456
  enableRowGroup:
449
457
  !isGroupExtendedColumn && column.kind === DataCubeColumnKind.DIMENSION,
450
458
  rowGroup: !isGroupExtendedColumn && Boolean(groupByCol),
451
459
  rowGroupIndex:
452
- !isGroupExtendedColumn && groupByCol
453
- ? (data.groupBy?.columns.indexOf(groupByCol) ?? null)
460
+ rowGroupIndex !== null
461
+ ? configuration.showRootAggregation
462
+ ? rowGroupIndex + 1
463
+ : rowGroupIndex
454
464
  : null,
455
465
  enablePivot:
456
466
  !isGroupExtendedColumn && column.kind === DataCubeColumnKind.DIMENSION,
@@ -546,8 +556,6 @@ export function generateBaseGridOptions(view: DataCubeViewState): GridOptions {
546
556
  event.api.hidePopupMenu(); // hide context-menu while scrolling
547
557
  },
548
558
  onBodyScrollEnd: () => grid.setScrollHintText(undefined),
549
- scrollbarWidth: 10,
550
- alwaysShowVerticalScroll: true, // this is needed to ensure column resize calculation is accurate
551
559
  // -------------------------------------- CONTEXT MENU --------------------------------------
552
560
  preventDefaultOnContextMenu: true, // prevent showing the browser's context menu
553
561
  columnMenu: 'new', // ensure context menu works on header
@@ -572,16 +580,8 @@ export function generateBaseGridOptions(view: DataCubeViewState): GridOptions {
572
580
  },
573
581
  // -------------------------------------- COLUMN SIZING --------------------------------------
574
582
  autoSizePadding: INTERNAL__GRID_CLIENT_AUTO_RESIZE_PADDING,
575
- // ensure columns are resized to fit the grid width initially and on window resize
576
- // NOTE: this has to be used with `alwaysShowVerticalScroll` to ensure accurate sizing
577
- // otherwise on initial load, the computation will be done when no data is rendered,
578
- // so when data is rendered and the vertical scrollbar shows up, it will block a part of
579
- // the last column.
580
583
  autoSizeStrategy: {
581
- type: 'fitGridWidth',
582
- },
583
- onGridSizeChanged: (event) => {
584
- event.api.sizeColumnsToFit();
584
+ type: 'fitCellContents',
585
585
  },
586
586
  // -------------------------------------- TOOLTIP --------------------------------------
587
587
  tooltipShowDelay: INTERNAL__GRID_CLIENT_TOOLTIP_SHOW_DELAY,
@@ -594,6 +594,10 @@ export function generateBaseGridOptions(view: DataCubeViewState): GridOptions {
594
594
  suppressScrollOnNewData: true,
595
595
  // NOTE: use row loader instead of showing loader in each cell to improve performance
596
596
  // otherwise, when we have many columns (i.e. when pivoting), the app could freeze.
597
+ //
598
+ // This would render the loading cell as the full-width row, which, in combination with
599
+ // fit cell content auto-sizing strategy creates an unwanted row flashing effect while loading.
600
+ // To compensate for that, we modify the styling to make sure the full-width row has a blank background
597
601
  loadingCellRenderer: DataCubeGridLoadingCellRenderer,
598
602
  // By default, when row-grouping is active, ag-grid's caching mechanism causes sort
599
603
  // to not work properly for pivot result columns, so we must disable this mechanism.
@@ -807,7 +811,7 @@ export function generateColumnDefs(
807
811
  );
808
812
  }
809
813
 
810
- return [
814
+ const columnDefs = [
811
815
  // NOTE: Internal column used for programatically trigger data fetch when filter is modified
812
816
  {
813
817
  colId: INTERNAL__GRID_CLIENT_DATA_FETCH_MANUAL_TRIGGER_COLUMN_ID,
@@ -819,6 +823,24 @@ export function generateColumnDefs(
819
823
  filter: 'agTextColumnFilter',
820
824
  suppressColumnsToolPanel: true,
821
825
  },
826
+ ...(configuration.showRootAggregation
827
+ ? [
828
+ {
829
+ colId: INTERNAL__GRID_CLIENT_ROOT_AGGREGATION_COLUMN_ID,
830
+ headerName: 'Root',
831
+ field: INTERNAL__GRID_CLIENT_ROOT_AGGREGATION_COLUMN_ID,
832
+ hide: true,
833
+ enableValue: false, // disable GUI interactions to modify this column's aggregate function
834
+ allowedAggFuncs: [], // disable GUI for options of the agg functions
835
+ enablePivot: false,
836
+ enableRowGroup: false,
837
+
838
+ suppressColumnsToolPanel: true,
839
+ rowGroup: true,
840
+ rowGroupIndex: 0,
841
+ } satisfies ColDef,
842
+ ]
843
+ : []),
822
844
  ...generateDefinitionForPivotResultColumns(
823
845
  pivotResultColumns,
824
846
  snapshot,
@@ -851,6 +873,8 @@ export function generateColumnDefs(
851
873
  } satisfies ColDef;
852
874
  }),
853
875
  ] satisfies (ColDef | ColGroupDef)[];
876
+
877
+ return columnDefs;
854
878
  }
855
879
 
856
880
  export function generateGridOptionsFromSnapshot(
@@ -863,15 +887,36 @@ export function generateGridOptionsFromSnapshot(
863
887
  if (
864
888
  configuration.initialExpandLevel !== undefined &&
865
889
  configuration.initialExpandLevel > 0 &&
866
- params.rowNode.level <= configuration.initialExpandLevel - 1
890
+ params.rowNode.level <=
891
+ // root aggregation (if enabled) should not be counted when applying initial expand level
892
+ (configuration.showRootAggregation
893
+ ? configuration.initialExpandLevel + 1
894
+ : configuration.initialExpandLevel) -
895
+ 1
867
896
  ) {
868
897
  return true;
869
898
  }
870
899
 
871
- const routes = params.rowNode.getRoute();
872
- if (!routes) {
900
+ const routes = params.rowNode.getRoute() ?? [];
901
+ if (!routes.length) {
873
902
  return false;
874
903
  }
904
+
905
+ // when root aggregation is enabled, the root node should be
906
+ // expanded automatically by default
907
+ if (
908
+ configuration.showRootAggregation &&
909
+ routes.length === 1 &&
910
+ routes[0] === DEFAULT_ROOT_AGGREGATION_COLUMN_VALUE
911
+ ) {
912
+ return true;
913
+ }
914
+
915
+ // when root aggregation is enabled, the root node should not be removed
916
+ // from path when matching against all expanded paths
917
+ if (configuration.showRootAggregation) {
918
+ routes.shift();
919
+ }
875
920
  const path = routes.join(TREE_COLUMN_VALUE_SEPARATOR);
876
921
  if (configuration.pivotLayout.expandedPaths.includes(path)) {
877
922
  return true;
@@ -909,7 +954,7 @@ export function generateGridOptionsFromSnapshot(
909
954
  column.getColId(),
910
955
  pinned === null
911
956
  ? undefined
912
- : pinned === GridClientPinnedAlignement.LEFT
957
+ : pinned === DataCubeGridClientPinnedAlignement.LEFT
913
958
  ? DataCubeColumnPinPlacement.LEFT
914
959
  : DataCubeColumnPinPlacement.RIGHT,
915
960
  );
@@ -941,7 +986,13 @@ export function generateGridOptionsFromSnapshot(
941
986
  // expanded-to-level is specified, causing the groups to be automatically drilled down, resultant
942
987
  // expanded paths will not be kept for record.
943
988
  if (event.event) {
944
- const path = event.node.getRoute()?.join(TREE_COLUMN_VALUE_SEPARATOR);
989
+ const routes = event.node.getRoute() ?? [];
990
+ // when root aggregation is enabled, the root node should not be counted
991
+ // when formulating the expanded path
992
+ if (routes.length && configuration.showRootAggregation) {
993
+ routes.shift();
994
+ }
995
+ const path = routes.join(TREE_COLUMN_VALUE_SEPARATOR);
945
996
  if (!path) {
946
997
  return;
947
998
  }
@@ -953,6 +1004,14 @@ export function generateGridOptionsFromSnapshot(
953
1004
  }
954
1005
  },
955
1006
 
1007
+ // Virtual columns when being rendered (as user scrolls horizontally) would get resized
1008
+ // to fit their content dynamically. This can potentially cause some performance issues,
1009
+ // so we debounce the resize operation.
1010
+ onVirtualColumnsChanged: () => {
1011
+ view.grid.debouncedAutoResizeColumns?.cancel();
1012
+ view.grid.debouncedAutoResizeColumns?.();
1013
+ },
1014
+
956
1015
  // -------------------------------------- COLUMNS --------------------------------------
957
1016
 
958
1017
  columnDefs: generateColumnDefs(snapshot, configuration, view),
@@ -971,8 +1030,6 @@ export function generateGridOptionsFromSnapshot(
971
1030
  ..._groupDisplaySpec(snapshot, configuration),
972
1031
 
973
1032
  // size
974
- suppressAutoSize: true,
975
- suppressSizeToFit: true,
976
1033
  minWidth: 200,
977
1034
 
978
1035
  // sorting
@@ -452,7 +452,7 @@ export function generateMenuBuilder(
452
452
  disabled: true,
453
453
  },
454
454
  {
455
- name: 'Selected Row(s) as Plain Text',
455
+ name: 'Selected Rows as Plain Text',
456
456
  menuItem: WIP_GridMenuItem,
457
457
  cssClasses: ['!opacity-100'],
458
458
  disabled: true,
@@ -16,11 +16,11 @@
16
16
 
17
17
  import {
18
18
  PRIMITIVE_TYPE,
19
- V1_ClassInstance,
20
- V1_ColSpecArray,
21
19
  V1_GenericTypeInstance,
22
20
  type V1_ColSpec,
23
21
  type V1_AppliedFunction,
22
+ V1_GenericType,
23
+ V1_RelationType,
24
24
  } from '@finos/legend-graph';
25
25
  import { type DataCubeQuerySnapshot } from '../../core/DataCubeQuerySnapshot.js';
26
26
  import {
@@ -53,12 +53,16 @@ import {
53
53
  } from '../../core/DataCubeQueryBuilderUtils.js';
54
54
  import {
55
55
  INTERNAL__GRID_CLIENT_MISSING_VALUE,
56
+ INTERNAL__GRID_CLIENT_ROOT_AGGREGATION_COLUMN_ID,
56
57
  INTERNAL__GRID_CLIENT_ROW_GROUPING_COUNT_AGG_COLUMN_ID,
58
+ type DataCubeGridClientDataFetchRequest,
57
59
  } from './DataCubeGridClientEngine.js';
58
- import type { DataCubeConfiguration } from '../../core/models/DataCubeConfiguration.js';
60
+ import { DataCubeConfiguration } from '../../core/models/DataCubeConfiguration.js';
59
61
  import type { DataCubeQueryFilterOperation } from '../../core/filter/DataCubeQueryFilterOperation.js';
60
62
  import type { DataCubeQueryAggregateOperation } from '../../core/aggregation/DataCubeQueryAggregateOperation.js';
61
63
  import { _colSpecArrayParam } from '../../core/DataCubeQuerySnapshotBuilderUtils.js';
64
+ import { buildExecutableQuery } from '../../core/DataCubeQueryBuilder.js';
65
+ import type { DataCubeSource } from '../../core/models/DataCubeSource.js';
62
66
 
63
67
  /*****************************************************************************
64
68
  * [GRID]
@@ -71,7 +75,7 @@ import { _colSpecArrayParam } from '../../core/DataCubeQuerySnapshotBuilderUtils
71
75
 
72
76
  // --------------------------------- BUILDING BLOCKS ---------------------------------
73
77
 
74
- function _aggCountCol() {
78
+ export function _aggCountCol() {
75
79
  const variable = _var();
76
80
  return _colSpec(
77
81
  INTERNAL__GRID_CLIENT_ROW_GROUPING_COUNT_AGG_COLUMN_ID,
@@ -100,11 +104,13 @@ function _addCountAggColumnToPivot(
100
104
  const castColumns = guaranteeType(
101
105
  guaranteeType(
102
106
  guaranteeType(funcMap.pivotCast.parameters[0], V1_GenericTypeInstance)
103
- .typeArguments[0],
104
- V1_ClassInstance,
105
- ).value,
106
- V1_ColSpecArray,
107
- ).colSpecs;
107
+ .genericType.typeArguments[0],
108
+ V1_GenericType,
109
+ ).rawType,
110
+ V1_RelationType,
111
+ ).columns.map((column) =>
112
+ _colSpec(column.name, undefined, undefined, column.type),
113
+ );
108
114
  uniq(
109
115
  castColumns
110
116
  .filter((col) => isPivotResultColumnName(col.name))
@@ -131,8 +137,58 @@ function _addCountAggColumnToPivot(
131
137
 
132
138
  // --------------------------------- MAIN ---------------------------------
133
139
 
134
- export function generateRowGroupingDrilldownExecutableQueryPostProcessor(
135
- drilldownValues: (string | null | undefined)[],
140
+ export function buildGridDataFetchExecutableQuery(
141
+ request: DataCubeGridClientDataFetchRequest,
142
+ snapshot: DataCubeQuerySnapshot,
143
+ source: DataCubeSource,
144
+ executionContextBuilder: (
145
+ source: DataCubeSource,
146
+ ) => V1_AppliedFunction | undefined,
147
+ filterOperations: DataCubeQueryFilterOperation[],
148
+ aggregateOperations: DataCubeQueryAggregateOperation[],
149
+ enablePagination: boolean,
150
+ ) {
151
+ const processedSnapshot = snapshot.clone();
152
+ const configuration = DataCubeConfiguration.serialization.fromJson(
153
+ processedSnapshot.data.configuration,
154
+ );
155
+ if (configuration.showRootAggregation) {
156
+ processedSnapshot.data.groupBy = {
157
+ columns: [
158
+ {
159
+ name: INTERNAL__GRID_CLIENT_ROOT_AGGREGATION_COLUMN_ID,
160
+ type: PRIMITIVE_TYPE.STRING,
161
+ },
162
+ ...(processedSnapshot.data.groupBy?.columns ?? []),
163
+ ],
164
+ };
165
+ }
166
+ return buildExecutableQuery(
167
+ processedSnapshot,
168
+ source,
169
+ executionContextBuilder,
170
+ filterOperations,
171
+ aggregateOperations,
172
+ {
173
+ postProcessor: generateGridDataFetchExecutableQueryPostProcessor(request),
174
+ rootAggregation: {
175
+ columnName: INTERNAL__GRID_CLIENT_ROOT_AGGREGATION_COLUMN_ID,
176
+ },
177
+ pagination:
178
+ enablePagination &&
179
+ request.startRow !== undefined &&
180
+ request.endRow !== undefined
181
+ ? {
182
+ start: request.startRow,
183
+ end: request.endRow,
184
+ }
185
+ : undefined,
186
+ },
187
+ );
188
+ }
189
+
190
+ function generateGridDataFetchExecutableQueryPostProcessor(
191
+ request: DataCubeGridClientDataFetchRequest,
136
192
  ) {
137
193
  return (
138
194
  snapshot: DataCubeQuerySnapshot,
@@ -151,7 +207,7 @@ export function generateRowGroupingDrilldownExecutableQueryPostProcessor(
151
207
 
152
208
  // if any level of drilldown has been done
153
209
  // add a pre-filter to groupBy()
154
- if (drilldownValues.length) {
210
+ if (request.groupKeys.length) {
155
211
  sequence.splice(
156
212
  sequence.indexOf(funcMap.groupBy),
157
213
  0,
@@ -161,7 +217,7 @@ export function generateRowGroupingDrilldownExecutableQueryPostProcessor(
161
217
  [
162
218
  _filter(
163
219
  {
164
- conditions: drilldownValues.map((value, i) => {
220
+ conditions: request.groupKeys.map((value, i) => {
165
221
  const groupByColumn = guaranteeNonNullable(
166
222
  groupBy.columns[i],
167
223
  );
@@ -230,14 +286,14 @@ export function generateRowGroupingDrilldownExecutableQueryPostProcessor(
230
286
  }
231
287
 
232
288
  // modify groupBy() based off the current drilldown level
233
- if (drilldownValues.length < groupBy.columns.length) {
289
+ if (request.groupKeys.length < groupBy.columns.length) {
234
290
  const countAggColumns: V1_ColSpec[] = [];
235
291
  _addCountAggColumnToPivot(funcMap, countAggColumns);
236
292
 
237
293
  const groupByIdx = sequence.indexOf(funcMap.groupBy);
238
294
  const groupByColumns = groupBy.columns.slice(
239
295
  0,
240
- drilldownValues.length + 1,
296
+ request.groupKeys.length + 1,
241
297
  );
242
298
  const groupByFunc = _function(DataCubeFunction.GROUP_BY, [
243
299
  _cols(groupByColumns.map((col) => _colSpec(col.name))),
@@ -21,12 +21,13 @@
21
21
  * of the grid client, AG Grid.
22
22
  ***************************************************************************************/
23
23
 
24
- import type { IServerSideGetRowsRequest } from '@ag-grid-community/core';
25
24
  import { type DataCubeQuerySnapshot } from '../../core/DataCubeQuerySnapshot.js';
26
25
  import { _toCol } from '../../core/models/DataCubeColumn.js';
27
26
  import {
28
- GridClientSortDirection,
27
+ DataCubeGridClientSortDirection,
28
+ INTERNAL__GRID_CLIENT_ROOT_AGGREGATION_COLUMN_ID,
29
29
  INTERNAL__GRID_CLIENT_TREE_COLUMN_ID,
30
+ type DataCubeGridClientDataFetchRequest,
30
31
  } from './DataCubeGridClientEngine.js';
31
32
  import {
32
33
  DataCubeQuerySortDirection,
@@ -50,24 +51,27 @@ export function getColumnConfiguration(
50
51
  // --------------------------------- MAIN ---------------------------------
51
52
 
52
53
  export function buildQuerySnapshot(
53
- request: IServerSideGetRowsRequest,
54
+ request: DataCubeGridClientDataFetchRequest,
54
55
  baseSnapshot: DataCubeQuerySnapshot,
55
56
  ) {
56
57
  const snapshot = baseSnapshot.clone();
57
58
  const configuration = DataCubeConfiguration.serialization.fromJson(
58
59
  snapshot.data.configuration,
59
60
  );
61
+ const rowGroupColumns = request.rowGroupColumns.filter(
62
+ (col) => col !== INTERNAL__GRID_CLIENT_ROOT_AGGREGATION_COLUMN_ID,
63
+ );
60
64
 
61
65
  // --------------------------------- SELECT ---------------------------------
62
66
 
63
67
  snapshot.data.selectColumns = uniqBy(
64
68
  [
65
69
  ...snapshot.data.selectColumns,
66
- ...request.pivotCols.map((col) =>
67
- getColumnConfiguration(col.id, configuration),
70
+ ...request.pivotColumns.map((col) =>
71
+ getColumnConfiguration(col, configuration),
68
72
  ),
69
- ...request.rowGroupCols.map((col) =>
70
- getColumnConfiguration(col.id, configuration),
73
+ ...rowGroupColumns.map((col) =>
74
+ getColumnConfiguration(col, configuration),
71
75
  ),
72
76
  ],
73
77
  (col) => col.name,
@@ -75,10 +79,10 @@ export function buildQuerySnapshot(
75
79
 
76
80
  // --------------------------------- PIVOT ---------------------------------
77
81
 
78
- snapshot.data.pivot = request.pivotCols.length
82
+ snapshot.data.pivot = request.pivotColumns.length
79
83
  ? {
80
- columns: request.pivotCols.map((col) =>
81
- _toCol(getColumnConfiguration(col.id, configuration)),
84
+ columns: request.pivotColumns.map((col) =>
85
+ _toCol(getColumnConfiguration(col, configuration)),
82
86
  ),
83
87
  // NOTE: since we re-fetch the cast columns anyway in this flow, we just
84
88
  // reuse the current cast columns
@@ -88,33 +92,39 @@ export function buildQuerySnapshot(
88
92
 
89
93
  // --------------------------------- GROUP BY ---------------------------------
90
94
 
91
- snapshot.data.groupBy = request.rowGroupCols.length
95
+ snapshot.data.groupBy = rowGroupColumns.length
92
96
  ? {
93
- columns: request.rowGroupCols.map((col) =>
94
- _toCol(getColumnConfiguration(col.id, configuration)),
97
+ columns: rowGroupColumns.map((col) =>
98
+ _toCol(getColumnConfiguration(col, configuration)),
95
99
  ),
96
100
  }
97
101
  : undefined;
98
102
 
99
103
  // --------------------------------- SORT ---------------------------------
100
104
 
101
- snapshot.data.sortColumns = request.sortModel
105
+ snapshot.data.sortColumns = request.sortColumns
102
106
  // Make sure the tree column is not being sorted since it's a synthetic column
103
107
  // the sorting state of this special column is `synthesized` by ag-grid
104
108
  // so when all group by columns are sorted in the same direction, the tree group
105
109
  // column will be sorted in that direction, and vice versa, when user sorts
106
110
  // the tree column, all groupBy columns will be sorted in that direction
107
- .filter((item) => item.colId !== INTERNAL__GRID_CLIENT_TREE_COLUMN_ID)
111
+ .filter(
112
+ (item) =>
113
+ ![
114
+ INTERNAL__GRID_CLIENT_TREE_COLUMN_ID,
115
+ INTERNAL__GRID_CLIENT_ROOT_AGGREGATION_COLUMN_ID,
116
+ ].includes(item.name),
117
+ )
108
118
  .map((item) => ({
109
- name: item.colId,
119
+ name: item.name,
110
120
  type: getColumnConfiguration(
111
- isPivotResultColumnName(item.colId)
112
- ? getPivotResultColumnBaseColumnName(item.colId)
113
- : item.colId,
121
+ isPivotResultColumnName(item.name)
122
+ ? getPivotResultColumnBaseColumnName(item.name)
123
+ : item.name,
114
124
  configuration,
115
125
  ).type,
116
126
  direction:
117
- item.sort === GridClientSortDirection.ASCENDING
127
+ item.direction === DataCubeGridClientSortDirection.ASCENDING
118
128
  ? DataCubeQuerySortDirection.ASCENDING
119
129
  : DataCubeQuerySortDirection.DESCENDING,
120
130
  }));
@@ -126,6 +136,16 @@ export function buildQuerySnapshot(
126
136
  snapshot.data.groupBy?.columns ?? [],
127
137
  configuration.pivotLayout.expandedPaths,
128
138
  );
139
+ // if root aggregation synthetic column has been removed
140
+ // pdate the configuration to disable root aggregation
141
+ if (
142
+ !request.rowGroupColumns.includes(
143
+ INTERNAL__GRID_CLIENT_ROOT_AGGREGATION_COLUMN_ID,
144
+ ) &&
145
+ configuration.showRootAggregation
146
+ ) {
147
+ configuration.showRootAggregation = false;
148
+ }
129
149
  snapshot.data.configuration = configuration.serialize();
130
150
 
131
151
  // --------------------------------- FINALIZE ---------------------------------
@@ -14,7 +14,11 @@
14
14
  * limitations under the License.
15
15
  */
16
16
 
17
- import { guaranteeNonNullable } from '@finos/legend-shared';
17
+ import {
18
+ debounce,
19
+ guaranteeNonNullable,
20
+ type DebouncedFunc,
21
+ } from '@finos/legend-shared';
18
22
  import { action, makeObservable, observable } from 'mobx';
19
23
  import type { GridApi } from '@ag-grid-community/core';
20
24
  import type { DataCubeViewState } from '../DataCubeViewState.js';
@@ -59,6 +63,9 @@ export class DataCubeGridState extends DataCubeQuerySnapshotController {
59
63
  rowLimit?: number | undefined;
60
64
  isPaginationEnabled = INTERNAL__GRID_CLIENT_DEFAULT_ENABLE_PAGINATION;
61
65
  scrollHintText?: string | undefined;
66
+ // As we resize columns dynamically to fit their content, virtual columns are being rendered
67
+ // and resized as user scrolls horizontally, this can cause performance issues, so we debounce.
68
+ debouncedAutoResizeColumns?: DebouncedFunc<() => void>;
62
69
 
63
70
  constructor(view: DataCubeViewState) {
64
71
  super(view);
@@ -112,6 +119,10 @@ export class DataCubeGridState extends DataCubeQuerySnapshotController {
112
119
 
113
120
  configureClient(val: GridApi | undefined) {
114
121
  this._client = val;
122
+ this.debouncedAutoResizeColumns = debounce(
123
+ () => val?.autoSizeAllColumns(),
124
+ 100,
125
+ );
115
126
  }
116
127
 
117
128
  override getSnapshotSubscriberName() {
package/tsconfig.json CHANGED
@@ -30,6 +30,8 @@
30
30
  "isolatedModules": true,
31
31
  "esModuleInterop": true,
32
32
  "allowSyntheticDefaultImports": true,
33
+ "resolvePackageJsonExports": true,
34
+ "resolvePackageJsonImports": true,
33
35
  "preserveConstEnums": true,
34
36
  "incremental": true,
35
37
  "useDefineForClassFields": true,