@finos/legend-application-repl 0.0.55 → 0.0.57

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 (147) hide show
  1. package/lib/components/dataCube/editor/DataCubeEditor.d.ts.map +1 -1
  2. package/lib/components/dataCube/editor/DataCubeEditor.js +2 -4
  3. package/lib/components/dataCube/editor/DataCubeEditor.js.map +1 -1
  4. package/lib/components/dataCube/editor/DataCubeEditorColumnsPanel.js +1 -1
  5. package/lib/components/dataCube/editor/DataCubeEditorColumnsPanel.js.map +1 -1
  6. package/lib/components/dataCube/editor/DataCubeEditorHorizontalPivotsPanel.d.ts.map +1 -1
  7. package/lib/components/dataCube/editor/DataCubeEditorHorizontalPivotsPanel.js +5 -1
  8. package/lib/components/dataCube/editor/DataCubeEditorHorizontalPivotsPanel.js.map +1 -1
  9. package/lib/components/dataCube/extend/DataCubeColumnEditor.d.ts.map +1 -1
  10. package/lib/components/dataCube/extend/DataCubeColumnEditor.js +2 -1
  11. package/lib/components/dataCube/extend/DataCubeColumnEditor.js.map +1 -1
  12. package/lib/components/dataCube/grid/DataCubeGrid.js +2 -2
  13. package/lib/components/dataCube/grid/DataCubeGrid.js.map +1 -1
  14. package/lib/components/repl/Form.js +1 -1
  15. package/lib/components/repl/Form.js.map +1 -1
  16. package/lib/index.css +2 -2
  17. package/lib/index.css.map +1 -1
  18. package/lib/package.json +1 -1
  19. package/lib/stores/REPLStore.d.ts.map +1 -1
  20. package/lib/stores/REPLStore.js +3 -3
  21. package/lib/stores/REPLStore.js.map +1 -1
  22. package/lib/stores/dataCube/DataCubeEngine.d.ts +1 -1
  23. package/lib/stores/dataCube/DataCubeEngine.d.ts.map +1 -1
  24. package/lib/stores/dataCube/DataCubeEngine.js.map +1 -1
  25. package/lib/stores/dataCube/core/DataCubeConfiguration.d.ts +3 -4
  26. package/lib/stores/dataCube/core/DataCubeConfiguration.d.ts.map +1 -1
  27. package/lib/stores/dataCube/core/DataCubeConfiguration.js +6 -8
  28. package/lib/stores/dataCube/core/DataCubeConfiguration.js.map +1 -1
  29. package/lib/stores/dataCube/core/DataCubeQueryBuilder.d.ts.map +1 -1
  30. package/lib/stores/dataCube/core/DataCubeQueryBuilder.js +26 -13
  31. package/lib/stores/dataCube/core/DataCubeQueryBuilder.js.map +1 -1
  32. package/lib/stores/dataCube/core/DataCubeQueryBuilderUtils.d.ts +4 -2
  33. package/lib/stores/dataCube/core/DataCubeQueryBuilderUtils.d.ts.map +1 -1
  34. package/lib/stores/dataCube/core/DataCubeQueryBuilderUtils.js +83 -13
  35. package/lib/stores/dataCube/core/DataCubeQueryBuilderUtils.js.map +1 -1
  36. package/lib/stores/dataCube/core/DataCubeQueryEngine.d.ts +6 -7
  37. package/lib/stores/dataCube/core/DataCubeQueryEngine.d.ts.map +1 -1
  38. package/lib/stores/dataCube/core/DataCubeQueryEngine.js +4 -7
  39. package/lib/stores/dataCube/core/DataCubeQueryEngine.js.map +1 -1
  40. package/lib/stores/dataCube/core/DataCubeQuerySnapshot.d.ts +1 -10
  41. package/lib/stores/dataCube/core/DataCubeQuerySnapshot.d.ts.map +1 -1
  42. package/lib/stores/dataCube/core/DataCubeQuerySnapshot.js +23 -17
  43. package/lib/stores/dataCube/core/DataCubeQuerySnapshot.js.map +1 -1
  44. package/lib/stores/dataCube/core/DataCubeQuerySnapshotBuilder.d.ts.map +1 -1
  45. package/lib/stores/dataCube/core/DataCubeQuerySnapshotBuilder.js +67 -73
  46. package/lib/stores/dataCube/core/DataCubeQuerySnapshotBuilder.js.map +1 -1
  47. package/lib/stores/dataCube/editor/DataCubeEditorColumnPropertiesPanelState.d.ts +1 -0
  48. package/lib/stores/dataCube/editor/DataCubeEditorColumnPropertiesPanelState.d.ts.map +1 -1
  49. package/lib/stores/dataCube/editor/DataCubeEditorColumnPropertiesPanelState.js +21 -5
  50. package/lib/stores/dataCube/editor/DataCubeEditorColumnPropertiesPanelState.js.map +1 -1
  51. package/lib/stores/dataCube/editor/DataCubeEditorColumnsPanelState.d.ts +9 -1
  52. package/lib/stores/dataCube/editor/DataCubeEditorColumnsPanelState.d.ts.map +1 -1
  53. package/lib/stores/dataCube/editor/DataCubeEditorColumnsPanelState.js +33 -10
  54. package/lib/stores/dataCube/editor/DataCubeEditorColumnsPanelState.js.map +1 -1
  55. package/lib/stores/dataCube/editor/DataCubeEditorHorizontalPivotsPanelState.d.ts +7 -1
  56. package/lib/stores/dataCube/editor/DataCubeEditorHorizontalPivotsPanelState.d.ts.map +1 -1
  57. package/lib/stores/dataCube/editor/DataCubeEditorHorizontalPivotsPanelState.js +41 -3
  58. package/lib/stores/dataCube/editor/DataCubeEditorHorizontalPivotsPanelState.js.map +1 -1
  59. package/lib/stores/dataCube/editor/DataCubeEditorMutableConfiguration.d.ts +1 -2
  60. package/lib/stores/dataCube/editor/DataCubeEditorMutableConfiguration.d.ts.map +1 -1
  61. package/lib/stores/dataCube/editor/DataCubeEditorMutableConfiguration.js +4 -9
  62. package/lib/stores/dataCube/editor/DataCubeEditorMutableConfiguration.js.map +1 -1
  63. package/lib/stores/dataCube/editor/DataCubeEditorSortsPanelState.d.ts +1 -0
  64. package/lib/stores/dataCube/editor/DataCubeEditorSortsPanelState.d.ts.map +1 -1
  65. package/lib/stores/dataCube/editor/DataCubeEditorSortsPanelState.js +7 -2
  66. package/lib/stores/dataCube/editor/DataCubeEditorSortsPanelState.js.map +1 -1
  67. package/lib/stores/dataCube/editor/DataCubeEditorState.d.ts +2 -5
  68. package/lib/stores/dataCube/editor/DataCubeEditorState.d.ts.map +1 -1
  69. package/lib/stores/dataCube/editor/DataCubeEditorState.js +39 -21
  70. package/lib/stores/dataCube/editor/DataCubeEditorState.js.map +1 -1
  71. package/lib/stores/dataCube/editor/DataCubeEditorVerticalPivotsPanelState.d.ts +1 -0
  72. package/lib/stores/dataCube/editor/DataCubeEditorVerticalPivotsPanelState.d.ts.map +1 -1
  73. package/lib/stores/dataCube/editor/DataCubeEditorVerticalPivotsPanelState.js +6 -1
  74. package/lib/stores/dataCube/editor/DataCubeEditorVerticalPivotsPanelState.js.map +1 -1
  75. package/lib/stores/dataCube/extend/DataCubeColumnEditorState.d.ts.map +1 -1
  76. package/lib/stores/dataCube/extend/DataCubeColumnEditorState.js +1 -1
  77. package/lib/stores/dataCube/extend/DataCubeColumnEditorState.js.map +1 -1
  78. package/lib/stores/dataCube/extend/DataCubeExtendManagerState.d.ts +1 -1
  79. package/lib/stores/dataCube/extend/DataCubeExtendManagerState.d.ts.map +1 -1
  80. package/lib/stores/dataCube/extend/DataCubeExtendManagerState.js +8 -2
  81. package/lib/stores/dataCube/extend/DataCubeExtendManagerState.js.map +1 -1
  82. package/lib/stores/dataCube/grid/DataCubeGridClientEngine.d.ts +12 -3
  83. package/lib/stores/dataCube/grid/DataCubeGridClientEngine.d.ts.map +1 -1
  84. package/lib/stores/dataCube/grid/DataCubeGridClientEngine.js +94 -9
  85. package/lib/stores/dataCube/grid/DataCubeGridClientEngine.js.map +1 -1
  86. package/lib/stores/dataCube/grid/DataCubeGridConfigurationBuilder.d.ts +193 -1
  87. package/lib/stores/dataCube/grid/DataCubeGridConfigurationBuilder.d.ts.map +1 -1
  88. package/lib/stores/dataCube/grid/DataCubeGridConfigurationBuilder.js +176 -82
  89. package/lib/stores/dataCube/grid/DataCubeGridConfigurationBuilder.js.map +1 -1
  90. package/lib/stores/dataCube/grid/DataCubeGridControllerState.d.ts +8 -0
  91. package/lib/stores/dataCube/grid/DataCubeGridControllerState.d.ts.map +1 -1
  92. package/lib/stores/dataCube/grid/DataCubeGridControllerState.js +69 -5
  93. package/lib/stores/dataCube/grid/DataCubeGridControllerState.js.map +1 -1
  94. package/lib/stores/dataCube/grid/DataCubeGridMenuBuilder.d.ts.map +1 -1
  95. package/lib/stores/dataCube/grid/DataCubeGridMenuBuilder.js +69 -20
  96. package/lib/stores/dataCube/grid/DataCubeGridMenuBuilder.js.map +1 -1
  97. package/lib/stores/dataCube/grid/DataCubeGridQueryBuilder.d.ts.map +1 -1
  98. package/lib/stores/dataCube/grid/DataCubeGridQueryBuilder.js +0 -2
  99. package/lib/stores/dataCube/grid/DataCubeGridQueryBuilder.js.map +1 -1
  100. package/lib/stores/dataCube/grid/DataCubeGridQuerySnapshotBuilder.js +2 -2
  101. package/lib/stores/dataCube/grid/DataCubeGridQuerySnapshotBuilder.js.map +1 -1
  102. package/lib/stores/dataCube/grid/DataCubeGridState.d.ts +2 -11
  103. package/lib/stores/dataCube/grid/DataCubeGridState.d.ts.map +1 -1
  104. package/lib/stores/dataCube/grid/DataCubeGridState.js +15 -36
  105. package/lib/stores/dataCube/grid/DataCubeGridState.js.map +1 -1
  106. package/package.json +5 -5
  107. package/src/components/dataCube/editor/DataCubeEditor.tsx +2 -7
  108. package/src/components/dataCube/editor/DataCubeEditorColumnsPanel.tsx +1 -1
  109. package/src/components/dataCube/editor/DataCubeEditorHorizontalPivotsPanel.tsx +19 -1
  110. package/src/components/dataCube/extend/DataCubeColumnEditor.tsx +2 -1
  111. package/src/components/dataCube/grid/DataCubeGrid.tsx +2 -2
  112. package/src/components/repl/Form.tsx +1 -1
  113. package/src/stores/REPLStore.tsx +5 -2
  114. package/src/stores/dataCube/DataCubeEngine.ts +1 -1
  115. package/src/stores/dataCube/core/DataCubeConfiguration.ts +7 -9
  116. package/src/stores/dataCube/core/DataCubeQueryBuilder.ts +52 -13
  117. package/src/stores/dataCube/core/DataCubeQueryBuilderUtils.ts +142 -18
  118. package/src/stores/dataCube/core/DataCubeQueryEngine.ts +9 -13
  119. package/src/stores/dataCube/core/DataCubeQuerySnapshot.ts +24 -21
  120. package/src/stores/dataCube/core/DataCubeQuerySnapshotBuilder.ts +122 -113
  121. package/src/stores/dataCube/editor/DataCubeEditorColumnPropertiesPanelState.ts +33 -12
  122. package/src/stores/dataCube/editor/DataCubeEditorColumnsPanelState.ts +34 -28
  123. package/src/stores/dataCube/editor/DataCubeEditorHorizontalPivotsPanelState.ts +64 -5
  124. package/src/stores/dataCube/editor/DataCubeEditorMutableConfiguration.ts +4 -11
  125. package/src/stores/dataCube/editor/DataCubeEditorSortsPanelState.ts +17 -5
  126. package/src/stores/dataCube/editor/DataCubeEditorState.tsx +45 -22
  127. package/src/stores/dataCube/editor/DataCubeEditorVerticalPivotsPanelState.ts +12 -0
  128. package/src/stores/dataCube/extend/DataCubeColumnEditorState.tsx +1 -0
  129. package/src/stores/dataCube/extend/DataCubeExtendManagerState.tsx +9 -1
  130. package/src/stores/dataCube/grid/DataCubeGridClientEngine.ts +150 -10
  131. package/src/stores/dataCube/grid/DataCubeGridConfigurationBuilder.tsx +217 -81
  132. package/src/stores/dataCube/grid/DataCubeGridControllerState.ts +97 -4
  133. package/src/stores/dataCube/grid/DataCubeGridMenuBuilder.tsx +91 -24
  134. package/src/stores/dataCube/grid/DataCubeGridQueryBuilder.ts +0 -3
  135. package/src/stores/dataCube/grid/DataCubeGridQuerySnapshotBuilder.ts +2 -2
  136. package/src/stores/dataCube/grid/DataCubeGridState.ts +23 -45
  137. package/tsconfig.json +0 -2
  138. package/lib/components/dataCube/editor/DataCubeEditorCodePanel.d.ts +0 -22
  139. package/lib/components/dataCube/editor/DataCubeEditorCodePanel.d.ts.map +0 -1
  140. package/lib/components/dataCube/editor/DataCubeEditorCodePanel.js +0 -26
  141. package/lib/components/dataCube/editor/DataCubeEditorCodePanel.js.map +0 -1
  142. package/lib/stores/dataCube/editor/DataCubeEditorCodePanelState.d.ts +0 -39
  143. package/lib/stores/dataCube/editor/DataCubeEditorCodePanelState.d.ts.map +0 -1
  144. package/lib/stores/dataCube/editor/DataCubeEditorCodePanelState.js +0 -89
  145. package/lib/stores/dataCube/editor/DataCubeEditorCodePanelState.js.map +0 -1
  146. package/src/components/dataCube/editor/DataCubeEditorCodePanel.tsx +0 -43
  147. package/src/stores/dataCube/editor/DataCubeEditorCodePanelState.ts +0 -167
@@ -52,10 +52,12 @@ import {
52
52
  INTERNAL__GRID_CLIENT_SIDE_BAR_WIDTH,
53
53
  INTERNAL__GRID_CLIENT_ROW_GROUPING_COUNT_AGG_COLUMN_ID,
54
54
  INTERNAL__GRID_CLIENT_MISSING_VALUE,
55
- INTERNAL__GRID_CLIENT_FILTER_TRIGGER_COLUMN_ID,
55
+ INTERNAL__GRID_CLIENT_DATA_FETCH_MANUAL_TRIGGER_COLUMN_ID,
56
+ INTERNAL__GRID_CLIENT_PIVOT_COLUMN_GROUP_COLOR_ROTATION_SIZE,
56
57
  } from './DataCubeGridClientEngine.js';
57
58
  import { PRIMITIVE_TYPE } from '@finos/legend-graph';
58
59
  import {
60
+ getNonNullableEntry,
59
61
  getQueryParameters,
60
62
  getQueryParameterValue,
61
63
  isNonNullable,
@@ -77,6 +79,7 @@ import {
77
79
  DataCubeQuerySortOperator,
78
80
  DataCubeColumnKind,
79
81
  DEFAULT_MISSING_VALUE_DISPLAY_TEXT,
82
+ PIVOT_COLUMN_NAME_VALUE_SEPARATOR,
80
83
  } from '../core/DataCubeQueryEngine.js';
81
84
  import type { CustomLoadingCellRendererProps } from '@ag-grid-community/react';
82
85
  import { DataCubeIcon } from '@finos/legend-art';
@@ -376,18 +379,19 @@ function _sortSpec(columnData: ColumnData) {
376
379
  } as ColDef;
377
380
  }
378
381
 
379
- function _rowGroupSpec(columnData: ColumnData) {
382
+ function _aggSpec(columnData: ColumnData) {
380
383
  const { snapshot, column } = columnData;
381
384
  const data = snapshot.data;
382
385
  const groupByCol = _findCol(data.groupBy?.columns, column.name);
386
+ const pivotCol = _findCol(data.pivot?.columns, column.name);
383
387
  const isGroupExtendedColumn = Boolean(
384
388
  _findCol(data.groupExtendedColumns, column.name),
385
389
  );
386
390
  return {
387
391
  enableRowGroup:
388
392
  !isGroupExtendedColumn && column.kind === DataCubeColumnKind.DIMENSION,
389
- enableValue: false, // disable GUI interactions to modify this column's aggregate function
390
- allowedAggFuncs: [], // disable GUI for options of the agg functions
393
+ enablePivot:
394
+ !isGroupExtendedColumn && column.kind === DataCubeColumnKind.DIMENSION,
391
395
  rowGroup: !isGroupExtendedColumn && Boolean(groupByCol),
392
396
  rowGroupIndex:
393
397
  !isGroupExtendedColumn && groupByCol
@@ -396,10 +400,16 @@ function _rowGroupSpec(columnData: ColumnData) {
396
400
  // NOTE: we don't quite care about populating these accurately
397
401
  // since ag-grid aggregation does not support parameters, so
398
402
  // its set of supported aggregators will never match that specified
399
- // in the editor.
400
- // But we need to set this to make sure sorting works when row grouping
401
- // is used, so we use a dummy value here.
402
- aggFunc: !isGroupExtendedColumn ? () => 0 : null,
403
+ // in the editor. But we MUST set this to make sure sorting works
404
+ // when row grouping is used, so we need to set a non-null value here.
405
+ aggFunc: !isGroupExtendedColumn ? column.aggregateOperator : null,
406
+ enableValue: false, // disable GUI interactions to modify this column's aggregate function
407
+ allowedAggFuncs: [], // disable GUI for options of the agg functions
408
+ pivot: !isGroupExtendedColumn && Boolean(pivotCol),
409
+ pivotIndex:
410
+ !isGroupExtendedColumn && groupByCol
411
+ ? (data.groupBy?.columns.indexOf(groupByCol) ?? null)
412
+ : null,
403
413
  } satisfies ColDef;
404
414
  }
405
415
 
@@ -421,12 +431,15 @@ export function generateBaseGridOptions(dataCube: DataCubeState): GridOptions {
421
431
  groupDisplayType: 'custom', // keeps the column set stable even when row grouping is used
422
432
  suppressRowGroupHidesColumns: true, // keeps the column set stable even when row grouping is used
423
433
  suppressAggFuncInHeader: true, // keeps the columns stable when aggregation is used
424
- // purgeClosedRowNodes: true, // remove closed row nodes from the cache to allow reloading failed rows? - or should we have declarative action to retry?
425
434
  getChildCount: (data) =>
426
435
  data[INTERNAL__GRID_CLIENT_ROW_GROUPING_COUNT_AGG_COLUMN_ID],
427
436
  // -------------------------------------- PIVOT --------------------------------------
428
- // pivotPanelShow: "always"
429
- // pivotMode:true, // TODO: need to make sure we don't hide away any columns when this is enabled
437
+ // NOTE: we opt-out from ag-grid native support for pivot mode as it has a lot of constraints
438
+ // e.g. pivot mode impacts how row-grouping column drilldown is handled: when enabled, one
439
+ // cannot drill down to the leaf level.
440
+ // Another problem is we cannot use custom group column when in pivot mode.
441
+ // See https://github.com/ag-grid/ag-grid/issues/8088
442
+ pivotMode: false,
430
443
  // -------------------------------------- SORT --------------------------------------
431
444
  // Force multi-sorting since this is what the query supports anyway
432
445
  alwaysMultiSort: true,
@@ -526,9 +539,8 @@ export function generateBaseGridOptions(dataCube: DataCubeState): GridOptions {
526
539
  width: INTERNAL__GRID_CLIENT_SIDE_BAR_WIDTH,
527
540
  toolPanelParams: {
528
541
  suppressValues: true,
529
- // TODO: enable when we support pivot
530
- suppressPivotMode: true,
531
542
  suppressPivots: true,
543
+ suppressPivotMode: true,
532
544
  },
533
545
  },
534
546
  ],
@@ -541,6 +553,197 @@ export function generateBaseGridOptions(dataCube: DataCubeState): GridOptions {
541
553
  };
542
554
  }
543
555
 
556
+ function generateDefinitionForPivotResultColumns(
557
+ pivotResultColumns: DataCubeQuerySnapshotColumn[],
558
+ snapshot: DataCubeQuerySnapshot,
559
+ configuration: DataCubeConfiguration,
560
+ dataCube: DataCubeState,
561
+ ) {
562
+ const columns = pivotResultColumns
563
+ .map((col) => ({
564
+ ...col,
565
+ values: col.name.split(PIVOT_COLUMN_NAME_VALUE_SEPARATOR),
566
+ }))
567
+ .filter((col) => col.values.length > 1);
568
+
569
+ const columnDefs: ColGroupDef[] = [];
570
+
571
+ columns.forEach((col) => {
572
+ const groups: ColGroupDef[] = [];
573
+ let leaf!: ColDef;
574
+ let id = '';
575
+ for (let i = 0; i < col.values.length; i++) {
576
+ const value = getNonNullableEntry(col.values, i);
577
+ id =
578
+ id === ''
579
+ ? getNonNullableEntry(col.values, i)
580
+ : `${id}${PIVOT_COLUMN_NAME_VALUE_SEPARATOR}${value}`;
581
+
582
+ if (i !== col.values.length - 1) {
583
+ groups.push({
584
+ groupId: id,
585
+ children: [],
586
+ suppressColumnsToolPanel: true,
587
+ headerName: value,
588
+ } satisfies ColGroupDef);
589
+ } else {
590
+ const column = _findCol(configuration.columns, value);
591
+ if (column) {
592
+ const columnData = {
593
+ snapshot,
594
+ column,
595
+ configuration,
596
+ };
597
+ leaf = {
598
+ headerName: column.displayName ?? column.name,
599
+ colId: col.name,
600
+ field: col.name,
601
+ menuTabs: [],
602
+
603
+ ..._displaySpec(columnData),
604
+ ..._sizeSpec(columnData),
605
+
606
+ // disallow pinning and moving pivot result columns
607
+ pinned: false,
608
+ lockPinned: true,
609
+ lockPosition: true,
610
+ suppressColumnsToolPanel: true, // hide from column tool panel
611
+
612
+ // ..._sortSpec(columnData),
613
+ // ..._aggSpec(columnData),
614
+ } satisfies ColDef;
615
+ } else {
616
+ leaf = {
617
+ headerName: value,
618
+ colId: col.name,
619
+ field: col.name,
620
+
621
+ // NOTE: hide columns which do not have a corresponding base column configuration
622
+ // these could be internal columns (such as count)
623
+ hide: true,
624
+ suppressColumnsToolPanel: true, // hide from column tool panel
625
+ } satisfies ColDef;
626
+ }
627
+ }
628
+ }
629
+
630
+ let currentCollection: (ColDef | ColGroupDef)[] = columnDefs;
631
+ groups.forEach((group) => {
632
+ const existingGroup = currentCollection.find(
633
+ (collection) =>
634
+ 'groupId' in collection && collection.groupId === group.groupId,
635
+ );
636
+ if (existingGroup) {
637
+ currentCollection = (existingGroup as ColGroupDef).children;
638
+ } else {
639
+ const newGroup = {
640
+ ...group,
641
+ headerClass: `${INTERNAL__GridClientUtilityCssClassName.PIVOT_COLUMN_GROUP} ${INTERNAL__GridClientUtilityCssClassName.PIVOT_COLUMN_GROUP_PREFIX}${currentCollection.length % INTERNAL__GRID_CLIENT_PIVOT_COLUMN_GROUP_COLOR_ROTATION_SIZE}`,
642
+ } satisfies ColGroupDef;
643
+ currentCollection.push(newGroup);
644
+ currentCollection = newGroup.children;
645
+ }
646
+ });
647
+ currentCollection.push(leaf);
648
+ });
649
+
650
+ // TODO: decorate header colors, etc.
651
+
652
+ return columnDefs;
653
+ }
654
+
655
+ export function generateColumnDefs(
656
+ snapshot: DataCubeQuerySnapshot,
657
+ configuration: DataCubeConfiguration,
658
+ dataCube: DataCubeState,
659
+ ) {
660
+ let pivotResultColumns: DataCubeQuerySnapshotColumn[] = [];
661
+ let columnsToDisplay = configuration.columns;
662
+ if (snapshot.data.pivot) {
663
+ const castColumns = snapshot.data.pivot.castColumns;
664
+ pivotResultColumns = castColumns.filter((column) =>
665
+ column.name.includes(PIVOT_COLUMN_NAME_VALUE_SEPARATOR),
666
+ );
667
+ columnsToDisplay = configuration.columns.filter(
668
+ (column) =>
669
+ castColumns.find((col) => col.name === column.name) ??
670
+ snapshot.data.groupExtendedColumns.find(
671
+ (col) => col.name === column.name,
672
+ ),
673
+ );
674
+ }
675
+ return [
676
+ {
677
+ headerName: '',
678
+ colId: INTERNAL__GRID_CLIENT_TREE_COLUMN_ID,
679
+ cellRenderer: 'agGroupCellRenderer',
680
+ tooltipValueGetter: (params) => {
681
+ if (
682
+ isNonNullable(params.value) &&
683
+ params.value !== INTERNAL__GRID_CLIENT_MISSING_VALUE
684
+ ) {
685
+ return (
686
+ `Group Value = ${params.value === '' ? "''" : params.value === true ? 'TRUE' : params.value === false ? 'FALSE' : params.value}` +
687
+ `${params.data[INTERNAL__GRID_CLIENT_ROW_GROUPING_COUNT_AGG_COLUMN_ID] !== undefined ? ` (${params.data[INTERNAL__GRID_CLIENT_ROW_GROUPING_COUNT_AGG_COLUMN_ID]})` : ''}`
688
+ );
689
+ }
690
+ return null;
691
+ },
692
+ showRowGroup: true,
693
+ hide: !snapshot.data.groupBy,
694
+ lockPinned: true,
695
+ lockPosition: true,
696
+ pinned: GridClientPinnedAlignement.LEFT,
697
+ cellStyle: {
698
+ flex: 1,
699
+ justifyContent: 'space-between',
700
+ display: 'flex',
701
+ },
702
+ cellDataType: 'text',
703
+ flex: 1,
704
+ loadingCellRenderer: DataCubeGridLoadingCellRenderer,
705
+ // TODO: we can support this in the configuration (support sorting by tree-column?)
706
+ sortable: true,
707
+ } satisfies ColDef,
708
+ // NOTE: Internal column used for programatically trigger data fetch when filter is modified
709
+ {
710
+ colId: INTERNAL__GRID_CLIENT_DATA_FETCH_MANUAL_TRIGGER_COLUMN_ID,
711
+ hide: true,
712
+ enableValue: false, // disable GUI interactions to modify this column's aggregate function
713
+ allowedAggFuncs: [], // disable GUI for options of the agg functions
714
+ enablePivot: false,
715
+ enableRowGroup: false,
716
+ filter: 'agTextColumnFilter',
717
+ suppressColumnsToolPanel: true,
718
+ },
719
+ ...generateDefinitionForPivotResultColumns(
720
+ pivotResultColumns,
721
+ snapshot,
722
+ configuration,
723
+ dataCube,
724
+ ),
725
+ ...columnsToDisplay.map((column) => {
726
+ const columnData = {
727
+ snapshot,
728
+ column,
729
+ configuration,
730
+ };
731
+ return {
732
+ headerName: column.displayName ?? column.name,
733
+ suppressSpanHeaderHeight: true,
734
+ colId: column.name,
735
+ field: column.name,
736
+ menuTabs: [],
737
+
738
+ ..._displaySpec(columnData),
739
+ ..._sizeSpec(columnData),
740
+ ..._sortSpec(columnData),
741
+ ..._aggSpec(columnData),
742
+ } satisfies ColDef;
743
+ }),
744
+ ] satisfies (ColDef | ColGroupDef)[];
745
+ }
746
+
544
747
  export function generateGridOptionsFromSnapshot(
545
748
  snapshot: DataCubeQuerySnapshot,
546
749
  configuration: DataCubeConfiguration,
@@ -602,74 +805,7 @@ export function generateGridOptionsFromSnapshot(
602
805
 
603
806
  // -------------------------------------- COLUMNS --------------------------------------
604
807
 
605
- columnDefs: [
606
- {
607
- headerName: '',
608
- colId: INTERNAL__GRID_CLIENT_TREE_COLUMN_ID,
609
- cellRenderer: 'agGroupCellRenderer',
610
- // TODO: display: coloring, text, etc.
611
- // TODO: tooltip
612
- // cellRendererParams: {
613
- // innerRenderer: (params: ICellRendererParams) => (
614
- // <>
615
- // <span>{params.value}</span>
616
- // {Boolean(
617
- // params.data[
618
- // INTERNAL__GRID_CLIENT_ROW_GROUPING_COUNT_AGG_COLUMN_ID
619
- // ],
620
- // ) && (
621
- // <span>{`(${params.data[INTERNAL__GRID_CLIENT_ROW_GROUPING_COUNT_AGG_COLUMN_ID]})`}</span>
622
- // )}
623
- // </>
624
- // ),
625
- // suppressCount: true,
626
- // } satisfies IGroupCellRendererParams,
627
- showRowGroup: true,
628
- hide: !snapshot.data.groupBy,
629
- lockPinned: true,
630
- lockPosition: true,
631
- pinned: GridClientPinnedAlignement.LEFT,
632
- cellStyle: {
633
- flex: 1,
634
- justifyContent: 'space-between',
635
- display: 'flex',
636
- },
637
- cellDataType: 'text',
638
- flex: 1,
639
- loadingCellRenderer: DataCubeGridLoadingCellRenderer,
640
- // TODO: we can support this in the configuration (support sorting by tree-column?)
641
- // sortable: false,
642
- } satisfies ColDef,
643
- // NOTE: Internal column used for programatically trigger data fetch when filter is modified
644
- {
645
- colId: INTERNAL__GRID_CLIENT_FILTER_TRIGGER_COLUMN_ID,
646
- hide: true,
647
- enableValue: false, // disable GUI interactions to modify this column's aggregate function
648
- allowedAggFuncs: [], // disable GUI for options of the agg functions
649
- enablePivot: false,
650
- enableRowGroup: false,
651
- filter: 'agTextColumnFilter',
652
- suppressColumnsToolPanel: true,
653
- },
654
- // TODO: handle pivot and column grouping
655
- ...configuration.columns.map((column) => {
656
- const columnData = {
657
- snapshot,
658
- column,
659
- configuration,
660
- };
661
- return {
662
- headerName: column.displayName ?? column.name,
663
- field: column.name,
664
- menuTabs: [],
665
-
666
- ..._displaySpec(columnData),
667
- ..._sizeSpec(columnData),
668
- ..._sortSpec(columnData),
669
- ..._rowGroupSpec(columnData),
670
- } satisfies ColDef | ColGroupDef;
671
- }),
672
- ],
808
+ columnDefs: generateColumnDefs(snapshot, configuration, dataCube),
673
809
  } as GridOptions;
674
810
 
675
811
  return gridOptions;
@@ -27,6 +27,7 @@ import {
27
27
  type DataCubeColumnPinPlacement,
28
28
  DataCubeColumnKind,
29
29
  DataCubeQueryFilterGroupOperator,
30
+ PIVOT_COLUMN_NAME_VALUE_SEPARATOR,
30
31
  } from '../core/DataCubeQueryEngine.js';
31
32
  import type {
32
33
  GetContextMenuItemsParams,
@@ -152,9 +153,15 @@ export class DataCubeGridControllerState extends DataCubeQuerySnapshotController
152
153
  }
153
154
 
154
155
  rearrangeColumns(columnByNames: string[]) {
155
- this.configuration.columns = columnByNames
156
+ const columnConfigurations = columnByNames
156
157
  .map((colName) => this.getColumnConfiguration(colName))
157
158
  .filter(isNonNullable);
159
+ this.configuration.columns = [
160
+ ...this.configuration.columns.filter(
161
+ (col) => !columnConfigurations.includes(col),
162
+ ),
163
+ ...columnConfigurations,
164
+ ];
158
165
  this.selectColumns = this.configuration.columns
159
166
  .map((column) =>
160
167
  this.selectColumns.find((col) => col.name === column.name),
@@ -176,6 +183,75 @@ export class DataCubeGridControllerState extends DataCubeQuerySnapshotController
176
183
  }
177
184
  }
178
185
 
186
+ // --------------------------------- PIVOT ---------------------------------
187
+
188
+ horizontalPivotableColumns: DataCubeQuerySnapshotColumn[] = [];
189
+ horizontalPivotedColumns: DataCubeQuerySnapshotColumn[] = [];
190
+ horizontalPivotCastColumns: DataCubeQuerySnapshotColumn[] = [];
191
+
192
+ setHorizontalPivotOnColumn(colName: string | undefined) {
193
+ const column = this.horizontalPivotableColumns.find(
194
+ (col) => col.name === colName,
195
+ );
196
+ if (column) {
197
+ this.horizontalPivotedColumns = [column];
198
+ /** TODO?: @datacube pivot - naively propagate this change, this might cause a bug so to be revisited */
199
+ this.verticalPivotedColumns = this.verticalPivotedColumns.filter(
200
+ (col) =>
201
+ !this.horizontalPivotedColumns.find((c) => c.name === col.name),
202
+ );
203
+ this.applyChanges();
204
+ }
205
+ }
206
+
207
+ addHorizontalPivotOnColumn(colName: string | undefined) {
208
+ const column = this.horizontalPivotableColumns.find(
209
+ (col) => col.name === colName,
210
+ );
211
+ if (column) {
212
+ this.horizontalPivotedColumns = [
213
+ ...this.horizontalPivotedColumns,
214
+ column,
215
+ ];
216
+ /** TODO?: @datacube pivot - naively propagate this change, this might cause a bug so to be revisited */
217
+ this.verticalPivotedColumns = this.verticalPivotedColumns.filter(
218
+ (col) =>
219
+ !this.horizontalPivotedColumns.find((c) => c.name === col.name),
220
+ );
221
+ this.applyChanges();
222
+ }
223
+ }
224
+
225
+ clearAllHorizontalPivots() {
226
+ this.horizontalPivotedColumns = [];
227
+ this.applyChanges();
228
+ }
229
+
230
+ excludeColumnFromHorizontalPivot(colName: string | undefined) {
231
+ if (colName?.includes(PIVOT_COLUMN_NAME_VALUE_SEPARATOR)) {
232
+ const baseColumnName = colName.substring(
233
+ colName.lastIndexOf(PIVOT_COLUMN_NAME_VALUE_SEPARATOR) +
234
+ PIVOT_COLUMN_NAME_VALUE_SEPARATOR.length,
235
+ );
236
+ const columnConfiguration = this.getColumnConfiguration(baseColumnName);
237
+ if (
238
+ columnConfiguration &&
239
+ !columnConfiguration.excludedFromHorizontalPivot
240
+ ) {
241
+ columnConfiguration.excludedFromHorizontalPivot = true;
242
+ this.applyChanges();
243
+ }
244
+ }
245
+ }
246
+
247
+ includeColumnInHorizontalPivot(colName: string | undefined) {
248
+ const columnConfiguration = this.getColumnConfiguration(colName);
249
+ if (columnConfiguration?.excludedFromHorizontalPivot) {
250
+ columnConfiguration.excludedFromHorizontalPivot = false;
251
+ this.applyChanges();
252
+ }
253
+ }
254
+
179
255
  // --------------------------------- GROUP BY ---------------------------------
180
256
 
181
257
  verticalPivotableColumns: DataCubeQuerySnapshotColumn[] = [];
@@ -281,6 +357,13 @@ export class DataCubeGridControllerState extends DataCubeQuerySnapshotController
281
357
  this.configuration,
282
358
  );
283
359
 
360
+ snapshot.data.pivot = this.horizontalPivotedColumns.length
361
+ ? {
362
+ columns: this.horizontalPivotedColumns,
363
+ castColumns: this.horizontalPivotCastColumns,
364
+ }
365
+ : undefined;
366
+
284
367
  snapshot.data.groupBy = this.verticalPivotedColumns.length
285
368
  ? {
286
369
  columns: this.verticalPivotedColumns,
@@ -319,11 +402,18 @@ export class DataCubeGridControllerState extends DataCubeQuerySnapshotController
319
402
  ...newSnapshot.data.groupExtendedColumns,
320
403
  ];
321
404
 
322
- this.sortableColumns = newSnapshot.stageCols('sort');
323
- this.sortColumns = newSnapshot.data.sortColumns;
405
+ this.horizontalPivotableColumns = newSnapshot
406
+ .stageCols('pivot')
407
+ .filter(
408
+ (column) =>
409
+ this.getColumnConfiguration(column.name)?.kind ===
410
+ DataCubeColumnKind.DIMENSION,
411
+ );
412
+ this.horizontalPivotedColumns = newSnapshot.data.pivot?.columns ?? [];
413
+ this.horizontalPivotCastColumns = newSnapshot.data.pivot?.castColumns ?? [];
324
414
 
325
415
  this.verticalPivotableColumns = newSnapshot
326
- .stageCols('aggregation')
416
+ .stageCols('group-by')
327
417
  .filter(
328
418
  (column) =>
329
419
  this.getColumnConfiguration(column.name)?.kind ===
@@ -331,6 +421,9 @@ export class DataCubeGridControllerState extends DataCubeQuerySnapshotController
331
421
  );
332
422
  this.verticalPivotedColumns = newSnapshot.data.groupBy?.columns ?? [];
333
423
 
424
+ this.sortableColumns = newSnapshot.stageCols('sort');
425
+ this.sortColumns = newSnapshot.data.sortColumns;
426
+
334
427
  this.menuBuilder = generateMenuBuilder(this);
335
428
  }
336
429
  }
@@ -27,6 +27,7 @@ import {
27
27
  DataCubeColumnKind,
28
28
  type DataCubeOperationValue,
29
29
  DataCubeQueryFilterOperator,
30
+ PIVOT_COLUMN_NAME_VALUE_SEPARATOR,
30
31
  } from '../core/DataCubeQueryEngine.js';
31
32
  import {
32
33
  guaranteeIsNumber,
@@ -175,6 +176,16 @@ export function generateMenuBuilder(
175
176
  const column = params.column ?? undefined;
176
177
  const columnName = column?.getColId();
177
178
  const columnConfiguration = controller.getColumnConfiguration(columnName);
179
+ const baseColumnConfiguration = columnName?.includes(
180
+ PIVOT_COLUMN_NAME_VALUE_SEPARATOR,
181
+ )
182
+ ? controller.getColumnConfiguration(
183
+ columnName.substring(
184
+ columnName.lastIndexOf(PIVOT_COLUMN_NAME_VALUE_SEPARATOR) +
185
+ PIVOT_COLUMN_NAME_VALUE_SEPARATOR.length,
186
+ ),
187
+ )
188
+ : undefined;
178
189
  const isExtendedColumn =
179
190
  columnName &&
180
191
  controller.extendedColumns.find((col) => col.name === columnName);
@@ -507,6 +518,50 @@ export function generateMenuBuilder(
507
518
  controller.removeVerticalPivotOnColumn(columnName),
508
519
  },
509
520
  'separator',
521
+ {
522
+ name: `Horizontal Pivot on ${column.getColId()}`,
523
+ action: () =>
524
+ controller.setHorizontalPivotOnColumn(columnName),
525
+ },
526
+ {
527
+ name: `Add Horizontal Pivot on ${column.getColId()}`,
528
+ disabled: Boolean(
529
+ controller.horizontalPivotedColumns.find(
530
+ (col) => col.name === columnName,
531
+ ),
532
+ ),
533
+ action: () =>
534
+ controller.addHorizontalPivotOnColumn(columnName),
535
+ },
536
+ 'separator',
537
+ ]
538
+ : []),
539
+ ...(column &&
540
+ columnName &&
541
+ baseColumnConfiguration?.kind === DataCubeColumnKind.MEASURE &&
542
+ !baseColumnConfiguration.excludedFromHorizontalPivot &&
543
+ controller.horizontalPivotedColumns.length !== 0 // pivot must be active
544
+ ? [
545
+ {
546
+ name: `Exclude Column ${column.getColId()} from Horizontal Pivot`,
547
+ action: () =>
548
+ controller.excludeColumnFromHorizontalPivot(columnName),
549
+ },
550
+ 'separator',
551
+ ]
552
+ : []),
553
+ ...(column &&
554
+ columnName &&
555
+ columnConfiguration?.kind === DataCubeColumnKind.MEASURE &&
556
+ columnConfiguration.excludedFromHorizontalPivot &&
557
+ controller.horizontalPivotedColumns.length !== 0 // pivot must be active
558
+ ? [
559
+ {
560
+ name: `Include Column ${column.getColId()} in Horizontal Pivot`,
561
+ action: () =>
562
+ controller.includeColumnInHorizontalPivot(columnName),
563
+ },
564
+ 'separator',
510
565
  ]
511
566
  : []),
512
567
  {
@@ -514,6 +569,11 @@ export function generateMenuBuilder(
514
569
  disabled: controller.verticalPivotedColumns.length === 0,
515
570
  action: () => controller.clearAllVerticalPivots(),
516
571
  },
572
+ {
573
+ name: `Clear All Horizontal Pivots`,
574
+ disabled: controller.verticalPivotedColumns.length === 0,
575
+ action: () => controller.clearAllHorizontalPivots(),
576
+ },
517
577
  ],
518
578
  },
519
579
  {
@@ -643,29 +703,36 @@ export function generateMenuBuilder(
643
703
  {
644
704
  name: 'Pin',
645
705
  subMenu: [
646
- {
647
- name: `Pin Left`,
648
- disabled: !column || column.isPinnedLeft(),
649
- checked: Boolean(column?.isPinnedLeft()),
650
- action: () =>
651
- controller.pinColumn(columnName, DataCubeColumnPinPlacement.LEFT),
652
- },
653
- {
654
- name: `Pin Right`,
655
- disabled: !column || column.isPinnedRight(),
656
- checked: Boolean(column?.isPinnedRight()),
657
- action: () =>
658
- controller.pinColumn(
659
- columnName,
660
- DataCubeColumnPinPlacement.RIGHT,
661
- ),
662
- },
663
- {
664
- name: `Unpin`,
665
- disabled: !column?.isPinned(),
666
- action: () => controller.pinColumn(columnName, undefined),
667
- },
668
- 'separator',
706
+ ...(columnConfiguration
707
+ ? [
708
+ {
709
+ name: `Pin Left`,
710
+ disabled: !column || column.isPinnedLeft(),
711
+ checked: Boolean(column?.isPinnedLeft()),
712
+ action: () =>
713
+ controller.pinColumn(
714
+ columnName,
715
+ DataCubeColumnPinPlacement.LEFT,
716
+ ),
717
+ },
718
+ {
719
+ name: `Pin Right`,
720
+ disabled: !column || column.isPinnedRight(),
721
+ checked: Boolean(column?.isPinnedRight()),
722
+ action: () =>
723
+ controller.pinColumn(
724
+ columnName,
725
+ DataCubeColumnPinPlacement.RIGHT,
726
+ ),
727
+ },
728
+ {
729
+ name: `Unpin`,
730
+ disabled: !column?.isPinned(),
731
+ action: () => controller.pinColumn(columnName, undefined),
732
+ },
733
+ 'separator',
734
+ ]
735
+ : []),
669
736
  {
670
737
  name: `Remove All Pinnings`,
671
738
  disabled: controller.configuration.columns.every(
@@ -677,7 +744,7 @@ export function generateMenuBuilder(
677
744
  },
678
745
  {
679
746
  name: 'Hide',
680
- disabled: !column,
747
+ disabled: !columnConfiguration,
681
748
  action: () => controller.showColumn(columnName, false),
682
749
  },
683
750
  'separator',
@@ -195,9 +195,6 @@ export function generateRowGroupingDrilldownExecutableQueryPostProcessor(
195
195
  // filter the data to match drilldown values, no groupBy() is needed.
196
196
  _unprocess('groupBy');
197
197
  }
198
-
199
- // --------------------------------- PIVOT ---------------------------------
200
- /** TODO: @datacube pivot - implement this and CAST */
201
198
  }
202
199
  };
203
200
  }