@lumeer/pivot 0.0.2 → 0.0.5

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.
@@ -1,10 +1,11 @@
1
1
  import * as i0 from '@angular/core';
2
2
  import { Directive, Pipe, Injectable, EventEmitter, TemplateRef, Component, ChangeDetectionStrategy, Input, Output, ContentChild, NgModule } from '@angular/core';
3
- import { cleanQueryAttribute, DataAggregator, UnknownConstraint, ConstraintType, attributesResourcesAttributesMap, aggregateDataResources, AttributesResourceType, dataAggregationConstraint, DataAggregationType, PercentageConstraint, aggregateDataValues, isValueAggregation, NumberConstraint } from '@lumeer/data-filters';
3
+ import { cleanQueryAttribute, DataAggregator, UnknownConstraint, ConstraintType, attributesResourcesAttributesMap, aggregateDataResources, AttributesResourceType, dataAggregationConstraint, DataAggregationType, PercentageConstraint, aggregateDataValues, isValueAggregation, NumberConstraint, generateId, LanguageTag } from '@lumeer/data-filters';
4
4
  import { deepObjectsEquals, hex2rgba, isArray, isNotNullOrUndefined, flattenMatrix, uniqueValues, flattenValues, shadeColor, deepObjectCopy, isNullOrUndefined, isNumeric, toNumber } from '@lumeer/utils';
5
5
  import { BehaviorSubject, filter, throttleTime, asyncScheduler, map, tap } from 'rxjs';
6
6
  import * as i1 from '@angular/common';
7
7
  import { CommonModule } from '@angular/common';
8
+ import { ScrollingModule } from '@angular/cdk/scrolling';
8
9
 
9
10
  var LmrPivotConfigVersion;
10
11
  (function (LmrPivotConfigVersion) {
@@ -140,7 +141,8 @@ class PivotDataConverter {
140
141
  const pivotConstraint = aggregatorAttribute.data && aggregatorAttribute.data;
141
142
  const overrideConstraint = pivotConstraint && this.transform?.checkValidConstraintOverride?.(constraint, pivotConstraint);
142
143
  const finalConstraint = overrideConstraint || constraint || new UnknownConstraint();
143
- return this.formatDataValue(finalConstraint.createDataValue(value, constraintData), finalConstraint);
144
+ const serializedValue = (constraint || new UnknownConstraint()).createDataValue(value, constraintData).serialize();
145
+ return this.formatDataValue(finalConstraint.createDataValue(serializedValue, constraintData), finalConstraint);
144
146
  }
145
147
  formatDataValue(dataValue, constraint) {
146
148
  switch (constraint.type) {
@@ -226,14 +228,14 @@ class PivotDataConverter {
226
228
  const filteredValueAttributes = (config.valueAttributes || []).filter(valueAttr => !mergedValueAttributes.some(merAttr => deepObjectsEquals(valueAttr, merAttr)));
227
229
  mergedValueAttributes.push(...filteredValueAttributes);
228
230
  if (!additionalData) {
231
+ const rowSticky = this.mapStickyValues((config.rowAttributes || []).map(attr => attr.sticky));
232
+ const columnSticky = this.mapStickyValues((config.columnAttributes || []).map(attr => attr.sticky));
229
233
  additionalData = {
230
- rowShowSums: (config.rowAttributes || []).map(attr => attr.showSums),
231
- rowSticky: this.mapStickyValues((config.rowAttributes || []).map(attr => !!attr.sticky)),
232
- rowSorts: (config.rowAttributes || []).map(attr => attr.sort),
234
+ rowsConfig: (config.rowAttributes || []).map((attr, index) => ({ showSums: attr.showSums, sort: attr.sort, expressions: attr.expressions, sticky: rowSticky[index] })),
235
+ columnsConfig: (config.columnAttributes || []).map((attr, index) => ({ showSums: attr.showSums, sort: attr.sort, expressions: attr.expressions, sticky: columnSticky[index] })),
236
+ rowShowHeaderAttributes: (config.rowAttributes || []).map(attr => attr.showHeader),
233
237
  rowAttributes: (config.rowAttributes || []).map(attr => this.pivotAttributeAttribute(attr)),
234
- columnShowSums: (config.columnAttributes || []).map(attr => attr.showSums),
235
- columnSticky: this.mapStickyValues((config.columnAttributes || []).map(attr => !!attr.sticky)),
236
- columnSorts: (config.columnAttributes || []).map(attr => attr.sort),
238
+ columnShowHeaderAttributes: (config.columnAttributes || []).map(attr => false),
237
239
  columnAttributes: (config.columnAttributes || []).map(attr => this.pivotAttributeAttribute(attr)),
238
240
  };
239
241
  }
@@ -333,19 +335,17 @@ class PivotDataConverter {
333
335
  }, { titles: [], constraints: [], headers: [], values: [], dataResources: [], valueTypes: [], aggregations: [] });
334
336
  return {
335
337
  columnHeaders: data.headers,
338
+ columnHeaderAttributes: [],
336
339
  rowHeaders: [],
340
+ rowHeaderAttributes: [],
337
341
  valueTitles: data.titles,
338
342
  values: [data.values],
339
343
  dataResources: [data.dataResources],
340
344
  valuesConstraints: data.constraints,
341
345
  valueTypes: data.valueTypes,
342
346
  valueAggregations: data.aggregations,
343
- rowShowSums: [],
344
- rowSticky: [],
345
- rowSorts: [],
346
- columnShowSums: [],
347
- columnSticky: [],
348
- columnSorts: [],
347
+ rowsConfig: [],
348
+ columnsConfig: [],
349
349
  hasAdditionalColumnLevel: true,
350
350
  };
351
351
  }
@@ -360,8 +360,12 @@ class PivotDataConverter {
360
360
  }
361
361
  convertAggregatedData(aggregatedData, valueAttributes, pivotColors, additionalData) {
362
362
  const rowData = this.convertMapToPivotDataHeader(aggregatedData.map, aggregatedData.rowLevels, pivotColors.rows, pivotColors.values, additionalData.rowAttributes);
363
+ const rowHeaderAttributes = (additionalData.rowAttributes || [])
364
+ .map((attribute, index) => additionalData.rowShowHeaderAttributes?.[index] ? ({ title: attribute.name, color: pivotColors.rows?.[index] }) : undefined);
363
365
  const { titles: valueTitles, constraints: valuesConstraints } = this.createValueTitles(valueAttributes);
364
366
  const columnData = this.convertMapToPivotDataHeader(aggregatedData.rowLevels > 0 ? aggregatedData.columnsMap : aggregatedData.map, aggregatedData.columnLevels, pivotColors.columns, pivotColors.values, additionalData.columnAttributes, valueTitles);
367
+ const columnHeaderAttributes = (additionalData.columnAttributes || [])
368
+ .map((attribute, index) => additionalData.columnShowHeaderAttributes?.[index] ? ({ title: attribute.name, color: pivotColors.columns?.[index] }) : undefined);
365
369
  const values = this.initMatrix(rowData.maxIndex + 1, columnData.maxIndex + 1);
366
370
  const dataResources = this.initMatrix(rowData.maxIndex + 1, columnData.maxIndex + 1);
367
371
  if ((valueAttributes || []).length > 0) {
@@ -372,7 +376,9 @@ class PivotDataConverter {
372
376
  (aggregatedData.columnLevels > 0 && valueTitles.length > 1);
373
377
  return {
374
378
  rowHeaders: rowData.headers,
379
+ rowHeaderAttributes,
375
380
  columnHeaders: columnData.headers,
381
+ columnHeaderAttributes,
376
382
  valueTitles,
377
383
  values,
378
384
  dataResources,
@@ -499,7 +505,7 @@ class PivotDataConverter {
499
505
  }, { titles: [], constraints: [] });
500
506
  }
501
507
  createValueTitle(aggregation, attributeName) {
502
- const valueAggregationTitle = this.transform?.translateAggregation?.(aggregation) || aggregation.toString();
508
+ const valueAggregationTitle = this.transform?.formatAggregation?.(aggregation) || aggregation.toString();
503
509
  return `${valueAggregationTitle} ${attributeName || ''}`.trim();
504
510
  }
505
511
  initMatrix(rows, columns) {
@@ -621,12 +627,13 @@ class PivotTableConverter {
621
627
  static groupDataClass = 'pivot-data-group-cell';
622
628
  static rowHeaderClass = 'pivot-row-header-cell';
623
629
  static rowGroupHeaderClass = 'pivot-row-group-header-cell';
630
+ static rowAttributeHeaderClass = 'pivot-row-attribute-header-cell';
624
631
  static columnHeaderClass = 'pivot-column-header-cell';
625
632
  static columnGroupHeaderClass = 'pivot-column-group-header-cell';
626
633
  groupColors = [COLOR_GRAY100, COLOR_GRAY200, COLOR_GRAY300, COLOR_GRAY400, COLOR_GRAY500];
627
634
  percentageConstraint = new PercentageConstraint({ decimals: 2 });
628
635
  data;
629
- strings;
636
+ transform;
630
637
  values;
631
638
  dataResources;
632
639
  constraintData;
@@ -636,11 +643,12 @@ class PivotTableConverter {
636
643
  columnsTransformationArray;
637
644
  valueTypeInfo;
638
645
  nonStickyRowIndex;
639
- createTables(pivotData, strings) {
646
+ nonStickyColumnIndex;
647
+ createTables(pivotData, transform) {
640
648
  if (!pivotData) {
641
649
  return [{ cells: [] }];
642
650
  }
643
- this.strings = strings;
651
+ this.transform = transform;
644
652
  this.constraintData = pivotData.constraintData;
645
653
  return (pivotData.data || []).map(d => {
646
654
  if (this.dataAreEmpty(d)) {
@@ -657,11 +665,14 @@ class PivotTableConverter {
657
665
  const numberOfSums = Math.max(1, (data.valueTitles || []).length);
658
666
  this.valueTypeInfo = getValuesTypeInfo(data.values, data.valueTypes, numberOfSums);
659
667
  this.data = preparePivotData(data, this.constraintData, this.valueTypeInfo);
660
- this.nonStickyRowIndex = this.data.rowSticky?.findIndex(sticky => !sticky) || 0;
661
668
  this.values = data.values || [];
662
669
  this.dataResources = data.dataResources || [];
663
- this.rowLevels = (data.rowShowSums || []).length;
664
- this.columnLevels = (data.columnShowSums || []).length + (data.hasAdditionalColumnLevel ? 1 : 0);
670
+ this.rowLevels = (data.rowsConfig || []).length;
671
+ this.columnLevels = (data.columnsConfig || []).length + (data.hasAdditionalColumnLevel ? 1 : 0);
672
+ const rowAllSticky = this.data.rowsConfig?.length && this.data.rowsConfig.every(config => !!config?.sticky);
673
+ this.nonStickyRowIndex = Math.max(rowAllSticky ? this.rowLevels : (this.data.rowsConfig?.findIndex(config => !config?.sticky) || 0), 0);
674
+ const columnAllSticky = this.data.columnsConfig?.length && this.data.columnsConfig.every(config => !!config?.sticky);
675
+ this.nonStickyColumnIndex = Math.max(columnAllSticky ? this.columnLevels : (this.data.columnsConfig?.findIndex(config => !config?.sticky) || 0), 0);
665
676
  const hasValue = (data.valueTitles || []).length > 0;
666
677
  if ((this.data.rowHeaders || []).length > 0) {
667
678
  this.rowsTransformationArray = createTransformationMap(this.data.rowHeaders, this.rowShowSums, this.columnLevels, 1);
@@ -677,10 +688,10 @@ class PivotTableConverter {
677
688
  }
678
689
  }
679
690
  get rowShowSums() {
680
- return this.data.rowShowSums;
691
+ return (this.data.rowsConfig || []).map(config => config.showSums);
681
692
  }
682
693
  get columnShowSums() {
683
- return this.data.columnShowSums;
694
+ return (this.data.columnsConfig || []).map(config => config.showSums);
684
695
  }
685
696
  transformData() {
686
697
  const cells = this.initCells();
@@ -699,7 +710,7 @@ class PivotTableConverter {
699
710
  for (const header of headers) {
700
711
  const rowSpan = getDirectHeaderChildCount(header, level, showSums);
701
712
  cells[currentIndex][level] = {
702
- value: header.title,
713
+ value: this.formatRowHeader(header.title, level),
703
714
  cssClass: PivotTableConverter.rowHeaderClass,
704
715
  isHeader: true,
705
716
  stickyStart: this.isRowLevelSticky(level),
@@ -716,41 +727,19 @@ class PivotTableConverter {
716
727
  this.fillCellsForRow(cells, header.targetIndex);
717
728
  }
718
729
  currentIndex += getHeaderChildCount(header, level, showSums);
730
+ const expressions = header.expressions || [];
731
+ for (let i = 0; i < expressions.length; i++) {
732
+ const expressionIndex = currentIndex - (expressions.length - i);
733
+ const background = this.getSummaryBackground(level);
734
+ this.splitRowGroupHeader(cells, level, expressionIndex, background, expressions[i].title);
735
+ this.fillCellsForExpressionRow(cells, expressions[i], expressionIndex, background);
736
+ }
719
737
  }
720
738
  if (showSums[level]) {
739
+ const { title, summary } = this.formatSummaryHeader(parentHeader, level);
721
740
  const background = this.getSummaryBackground(level);
722
- const summary = level === 0 ? this.strings.summaryString : this.strings.headerSummaryString;
723
741
  const columnIndex = Math.max(level - 1, 0);
724
- let colSpan = this.rowLevels - columnIndex;
725
- const stickyStart = this.isRowLevelSticky(columnIndex);
726
- // split row group header cell because of correct sticky scroll
727
- if (stickyStart && this.nonStickyRowIndex > 0 && colSpan > 1) {
728
- const newColspan = this.nonStickyRowIndex - columnIndex;
729
- cells[currentIndex][this.nonStickyRowIndex] = {
730
- value: undefined,
731
- constraint: undefined,
732
- label: undefined,
733
- cssClass: PivotTableConverter.rowGroupHeaderClass,
734
- isHeader: true,
735
- rowSpan: 1,
736
- colSpan: colSpan - newColspan,
737
- background,
738
- summary: undefined,
739
- };
740
- colSpan = newColspan;
741
- }
742
- cells[currentIndex][columnIndex] = {
743
- value: parentHeader?.title,
744
- constraint: parentHeader?.constraint,
745
- label: parentHeader?.attributeName,
746
- cssClass: PivotTableConverter.rowGroupHeaderClass,
747
- isHeader: true,
748
- stickyStart,
749
- rowSpan: 1,
750
- colSpan,
751
- background,
752
- summary,
753
- };
742
+ this.splitRowGroupHeader(cells, columnIndex, currentIndex, background, summary, title, parentHeader?.constraint, parentHeader?.attributeName);
754
743
  const rowIndexes = getTargetIndexesForHeaders(headers);
755
744
  const transformedRowIndexes = rowIndexes
756
745
  .map(v => this.rowsTransformationArray[v])
@@ -759,6 +748,50 @@ class PivotTableConverter {
759
748
  this.fillCellsForGroupedRow(cells, rowIndexes, currentIndex, background);
760
749
  }
761
750
  }
751
+ splitRowGroupHeader(cells, columnIndex, currentIndex, background, summary, title, constraint, label) {
752
+ let colSpan = this.rowLevels - columnIndex;
753
+ const stickyStart = this.isRowLevelSticky(columnIndex);
754
+ // split row group header cell because of correct sticky scroll
755
+ if (stickyStart && this.nonStickyRowIndex > 0 && this.nonStickyRowIndex < this.rowLevels && colSpan > 1) {
756
+ const newColspan = this.nonStickyRowIndex - columnIndex;
757
+ cells[currentIndex][this.nonStickyRowIndex] = {
758
+ value: undefined,
759
+ constraint: undefined,
760
+ label: undefined,
761
+ cssClass: PivotTableConverter.rowGroupHeaderClass,
762
+ isHeader: true,
763
+ rowSpan: 1,
764
+ colSpan: colSpan - newColspan,
765
+ background,
766
+ summary: undefined,
767
+ };
768
+ colSpan = newColspan;
769
+ }
770
+ cells[currentIndex][columnIndex] = {
771
+ value: title,
772
+ constraint,
773
+ label,
774
+ cssClass: PivotTableConverter.rowGroupHeaderClass,
775
+ isHeader: true,
776
+ stickyStart,
777
+ rowSpan: 1,
778
+ colSpan,
779
+ background,
780
+ summary,
781
+ };
782
+ }
783
+ formatSummaryHeader(header, level) {
784
+ return this.transform.formatSummaryHeader?.(header, level) || {
785
+ title: header?.title,
786
+ summary: '',
787
+ };
788
+ }
789
+ formatRowHeader(title, level) {
790
+ return this.transform?.formatRowHeader?.(title, level) || title;
791
+ }
792
+ formatColumnHeader(title, level) {
793
+ return this.transform?.formatColumnHeader?.(title, level) || title;
794
+ }
762
795
  getHeaderBackground(header, level) {
763
796
  if (header?.color) {
764
797
  return shadeColor(header.color, this.getLevelOpacity(level));
@@ -769,11 +802,11 @@ class PivotTableConverter {
769
802
  return Math.min(80, 50 + level * 5) / 100;
770
803
  }
771
804
  isRowLevelSticky(level) {
772
- return this.data?.rowSticky?.[level];
805
+ return this.data?.rowsConfig?.[level]?.sticky;
773
806
  }
774
807
  isColumnLevelSticky(level) {
775
- const maxLevel = Math.min(level, (this.data?.columnSticky?.length ?? Number.MAX_SAFE_INTEGER) - 1);
776
- return this.data?.columnSticky?.[maxLevel];
808
+ const maxLevel = Math.min(level, (this.data?.columnsConfig?.length ?? Number.MAX_SAFE_INTEGER) - 1);
809
+ return this.data?.columnsConfig?.[maxLevel]?.sticky;
777
810
  }
778
811
  getSummaryBackground(level) {
779
812
  const index = Math.min(level, this.groupColors.length - 1);
@@ -870,6 +903,58 @@ class PivotTableConverter {
870
903
  }
871
904
  }
872
905
  }
906
+ fillCellsForExpressionRow(cells, expression, rowIndexInCells, background) {
907
+ for (let column = 0; column < this.columnsTransformationArray.length; column++) {
908
+ const columnIndexInCells = this.columnsTransformationArray[column];
909
+ if (isNotNullOrUndefined(columnIndexInCells)) {
910
+ const { value, dataResources } = this.evaluateExpression(expression, column);
911
+ const valueIndex = column % this.data.valueTitles.length;
912
+ const formattedValue = this.formatValueByConstraint(value, valueIndex);
913
+ cells[rowIndexInCells][columnIndexInCells] = {
914
+ value: String(formattedValue),
915
+ dataResources,
916
+ colSpan: 1,
917
+ rowSpan: 1,
918
+ cssClass: PivotTableConverter.groupDataClass,
919
+ isHeader: false,
920
+ background,
921
+ };
922
+ }
923
+ }
924
+ }
925
+ evaluateExpression(expression, column) {
926
+ return (expression.operands || []).reduce((result, operand, index) => {
927
+ const { value, dataResources } = this.evaluateOperand(operand, column);
928
+ result.dataResources.push(...dataResources);
929
+ switch (expression.operation) {
930
+ case "add":
931
+ result.value += value;
932
+ break;
933
+ case "subtract":
934
+ result.value = (index === 0 ? value : result.value - value);
935
+ break;
936
+ case 'multiply':
937
+ result.value = index === 0 ? value : result.value * value;
938
+ break;
939
+ case 'divide':
940
+ result.value = index === 0 ? value : value ? result.value / value : result.value;
941
+ break;
942
+ }
943
+ return result;
944
+ }, { value: 0, dataResources: [] });
945
+ }
946
+ evaluateOperand(operand, column) {
947
+ switch (operand.type) {
948
+ case "expression": return this.evaluateExpression(operand, column);
949
+ case "value": return { value: operand.value, dataResources: [] };
950
+ case "header": {
951
+ const rows = getTargetIndexesForHeaders(operand.headers);
952
+ const { values, dataResources } = this.getGroupedValuesForRowsAndCols(rows, [column]);
953
+ const { value } = this.aggregateDataValues(values, [column]);
954
+ return { value, dataResources };
955
+ }
956
+ }
957
+ }
873
958
  getGroupedValuesForRowsAndCols(rows, columns) {
874
959
  const values = [];
875
960
  const dataResources = [];
@@ -898,7 +983,7 @@ class PivotTableConverter {
898
983
  for (const header of headers) {
899
984
  const colSpan = getDirectHeaderChildCount(header, level, showSums, numberOfSums);
900
985
  cells[level][currentIndex] = {
901
- value: header.title,
986
+ value: this.formatColumnHeader(header.title, level),
902
987
  cssClass: PivotTableConverter.columnHeaderClass,
903
988
  isHeader: true,
904
989
  rowSpan: 1,
@@ -918,12 +1003,12 @@ class PivotTableConverter {
918
1003
  }
919
1004
  if (showSums[level]) {
920
1005
  const background = this.getSummaryBackground(level);
921
- const summary = level === 0 ? this.strings.summaryString : this.strings.headerSummaryString;
1006
+ const { title, summary } = this.formatSummaryHeader(parentHeader, level);
922
1007
  const numberOfValues = this.data.valueTitles.length;
923
1008
  const rowIndex = Math.max(level - 1, 0);
924
1009
  const shouldAddValueHeaders = numberOfValues > 1;
925
1010
  cells[rowIndex][currentIndex] = {
926
- value: parentHeader?.title,
1011
+ value: title,
927
1012
  constraint: parentHeader?.constraint,
928
1013
  label: parentHeader?.attributeName,
929
1014
  cssClass: PivotTableConverter.columnGroupHeaderClass,
@@ -982,14 +1067,17 @@ class PivotTableConverter {
982
1067
  }
983
1068
  }
984
1069
  aggregateAndFormatDataValues(values, rows, columns) {
1070
+ const { value, aggregation } = this.aggregateDataValues(values, columns);
1071
+ return aggregation === DataAggregationType.Join ? value : this.formatGroupedValueByValueType(value, rows, columns);
1072
+ }
1073
+ aggregateDataValues(values, columns) {
985
1074
  const aggregation = this.aggregationByColumns(columns);
986
1075
  if (aggregation === DataAggregationType.Join) {
987
1076
  const valueIndex = this.getValueIndexForColumns(columns);
988
1077
  const constraint = this.data.valuesConstraints?.[valueIndex] || this.valueTypeInfo[valueIndex]?.defaultConstraint;
989
- return aggregateDataValues(aggregation, values, constraint, false, this.constraintData);
1078
+ return { value: aggregateDataValues(aggregation, values, constraint, false, this.constraintData), aggregation };
990
1079
  }
991
- const aggregatedValue = aggregateDataValues(aggregation, values);
992
- return this.formatGroupedValueByValueType(aggregatedValue, rows, columns);
1080
+ return { value: aggregateDataValues(aggregation, values), aggregation };
993
1081
  }
994
1082
  aggregationByColumns(columns) {
995
1083
  const valueIndex = columns[0] % this.data.valueTitles.length;
@@ -1104,17 +1192,44 @@ class PivotTableConverter {
1104
1192
  }
1105
1193
  }
1106
1194
  if (this.rowLevels > 0 && this.columnLevels > 0) {
1107
- for (let i = 0; i < this.columnLevels; i++) {
1108
- for (let j = 0; j < this.rowLevels; j++) {
1109
- matrix[i][j] = {
1110
- value: '',
1111
- cssClass: PivotTableConverter.emptyClass,
1195
+ for (let j = 0; j < this.rowLevels; j++) {
1196
+ const rowHeaderAttribute = this.data.rowHeaderAttributes[j];
1197
+ if (rowHeaderAttribute) {
1198
+ const titleColSpan = this.nonStickyColumnIndex || this.columnLevels;
1199
+ matrix[0][j] = {
1200
+ value: rowHeaderAttribute.title,
1201
+ cssClass: PivotTableConverter.rowAttributeHeaderClass,
1202
+ isHeader: true,
1112
1203
  rowSpan: 1,
1113
- colSpan: 1,
1204
+ colSpan: titleColSpan,
1205
+ stickyTop: this.isColumnLevelSticky(0),
1114
1206
  stickyStart: this.isRowLevelSticky(j),
1115
- stickyTop: this.isColumnLevelSticky(i),
1116
- isHeader: false,
1207
+ background: rowHeaderAttribute.color,
1117
1208
  };
1209
+ if (this.columnLevels - titleColSpan > 0) {
1210
+ matrix[this.nonStickyColumnIndex][j] = {
1211
+ value: '',
1212
+ cssClass: PivotTableConverter.rowAttributeHeaderClass,
1213
+ isHeader: true,
1214
+ rowSpan: 1,
1215
+ colSpan: this.columnLevels - titleColSpan,
1216
+ background: rowHeaderAttribute.color,
1217
+ stickyStart: this.isRowLevelSticky(j),
1218
+ };
1219
+ }
1220
+ }
1221
+ else {
1222
+ for (let i = 0; i < this.columnLevels; i++) {
1223
+ matrix[i][j] = {
1224
+ value: '',
1225
+ cssClass: PivotTableConverter.emptyClass,
1226
+ rowSpan: 1,
1227
+ colSpan: 1,
1228
+ stickyStart: this.isRowLevelSticky(j),
1229
+ stickyTop: this.isColumnLevelSticky(i),
1230
+ isHeader: false,
1231
+ };
1232
+ }
1118
1233
  }
1119
1234
  }
1120
1235
  }
@@ -1137,7 +1252,66 @@ class PivotTableConverter {
1137
1252
  function preparePivotData(data, constraintData, valueTypeInfo) {
1138
1253
  const numberOfSums = Math.max(1, (data.valueTitles || []).length);
1139
1254
  const values = computeValuesByValueType(data.values, data.valueTypes, numberOfSums, valueTypeInfo);
1140
- return sortPivotData({ ...data, values }, constraintData);
1255
+ const sorted = sortPivotData({ ...data, values }, constraintData);
1256
+ return fillExpressionsToData(sorted);
1257
+ }
1258
+ function fillExpressionsToData(data) {
1259
+ return {
1260
+ ...data,
1261
+ rowHeaders: fillExpressionsToHeaders(data.rowHeaders, data.rowsConfig, 0),
1262
+ columnHeaders: fillExpressionsToHeaders(data.columnHeaders, data.columnsConfig, 0),
1263
+ };
1264
+ }
1265
+ function fillExpressionsToHeaders(headers, configs, index) {
1266
+ const expressions = configs?.[index]?.expressions || [];
1267
+ const headersCopy = [...(headers || [])];
1268
+ for (const expression of expressions) {
1269
+ const dataHeaderExpression = extendPivotExpression(expression, headers);
1270
+ if (dataHeaderExpression.lastHeaderIndex >= 0) {
1271
+ const lastHeaderIndex = dataHeaderExpression.lastHeaderIndex;
1272
+ const newHeader = { ...headersCopy[lastHeaderIndex], expressions: [...(headersCopy[lastHeaderIndex].expressions || []), dataHeaderExpression] };
1273
+ headersCopy.splice(lastHeaderIndex, 1, newHeader);
1274
+ }
1275
+ }
1276
+ if (configs?.[index + 1]) {
1277
+ for (let i = 0; i < headersCopy.length; i++) {
1278
+ headersCopy.splice(i, 1, { ...headersCopy[i], children: fillExpressionsToHeaders(headersCopy[i].children, configs, index + 1) });
1279
+ }
1280
+ }
1281
+ return headersCopy;
1282
+ }
1283
+ function extendPivotExpression(expression, headers) {
1284
+ const dataHeaderOperands = [];
1285
+ let lastHeaderIndex = -1;
1286
+ function traverse(operands) {
1287
+ for (const operand of operands) {
1288
+ if (operand.type === 'header') {
1289
+ const indexes = getOperandIndexesInHeaders(operand, headers);
1290
+ lastHeaderIndex = Math.max(lastHeaderIndex, ...indexes);
1291
+ const operandHeaders = indexes.map(index => headers[index]);
1292
+ dataHeaderOperands.push({ ...operand, headers: operandHeaders });
1293
+ }
1294
+ else if (operand.type === 'expression') {
1295
+ traverse(operand.operands);
1296
+ }
1297
+ else {
1298
+ dataHeaderOperands.push(operand);
1299
+ }
1300
+ }
1301
+ }
1302
+ traverse(expression.operands);
1303
+ return { ...expression, lastHeaderIndex, operands: dataHeaderOperands };
1304
+ }
1305
+ function getOperandIndexesInHeaders(operand, headers) {
1306
+ return (headers || []).reduce((indexes, header, index) => {
1307
+ if (operandContainsHeader(operand, header)) {
1308
+ indexes.push(index);
1309
+ }
1310
+ return indexes;
1311
+ }, []);
1312
+ }
1313
+ function operandContainsHeader(operand, header) {
1314
+ return header.title.match(new RegExp(operand.value))?.length > 0;
1141
1315
  }
1142
1316
  function computeValuesByValueType(values, valueTypes, numValues, valueTypeInfo) {
1143
1317
  const rowsIndexes = [...Array(values.length).keys()];
@@ -1239,6 +1413,7 @@ function iterateThroughTransformationMap(headers, additionalNum, array, level, s
1239
1413
  }
1240
1414
  else if (isNotNullOrUndefined(header.targetIndex)) {
1241
1415
  array[header.targetIndex] = i + additional;
1416
+ additional += (header.expressions || []).length;
1242
1417
  }
1243
1418
  }
1244
1419
  }
@@ -1261,11 +1436,12 @@ function getTargetIndexesForHeader(pivotDataHeader) {
1261
1436
  function getHeadersChildCount(headers, showSums, numberOfSums = 1) {
1262
1437
  return (headers || []).reduce((sum, header) => sum + getHeaderChildCount(header, 0, showSums, numberOfSums), showSums[0] ? numberOfSums : 0);
1263
1438
  }
1264
- function getHeaderChildCount(pivotDataHeader, level, showSums, numberOfSums = 1, includeChild = true) {
1439
+ function getHeaderChildCount(pivotDataHeader, level, showSums, numberOfSums = 1) {
1440
+ const numExpressions = (pivotDataHeader.expressions || []).length;
1265
1441
  if (pivotDataHeader.children) {
1266
- return pivotDataHeader.children.reduce((sum, header) => sum + getHeaderChildCount(header, level + 1, showSums, numberOfSums, includeChild), showSums[level + 1] ? numberOfSums : 0);
1442
+ return pivotDataHeader.children.reduce((sum, header) => sum + getHeaderChildCount(header, level + 1, showSums, numberOfSums), (showSums[level + 1] ? numberOfSums : 0) + numExpressions);
1267
1443
  }
1268
- return includeChild ? 1 : 0;
1444
+ return 1 + numExpressions;
1269
1445
  }
1270
1446
  function getDirectHeaderChildCount(pivotDataHeader, level, showSums, numberOfSums = 1) {
1271
1447
  if (pivotDataHeader.children) {
@@ -1274,10 +1450,12 @@ function getDirectHeaderChildCount(pivotDataHeader, level, showSums, numberOfSum
1274
1450
  return 1;
1275
1451
  }
1276
1452
  function sortPivotData(data, constraintData) {
1453
+ const rowSorts = (data.rowsConfig || []).map(config => config.sort);
1454
+ const columnSorts = (data.columnsConfig || []).map(config => config.sort);
1277
1455
  return {
1278
1456
  ...data,
1279
- rowHeaders: sortPivotRowDataHeaders(data.rowHeaders, data.rowSorts, data, constraintData),
1280
- columnHeaders: sortPivotColumnDataHeaders(data.columnHeaders, data.columnSorts, data, constraintData),
1457
+ rowHeaders: sortPivotRowDataHeaders(data.rowHeaders, rowSorts, data, constraintData),
1458
+ columnHeaders: sortPivotColumnDataHeaders(data.columnHeaders, columnSorts, data, constraintData),
1281
1459
  };
1282
1460
  }
1283
1461
  function sortPivotRowDataHeaders(rowHeaders, rowSorts, pivotData, constraintData) {
@@ -1535,9 +1713,11 @@ class LmrPivotTableComponent {
1535
1713
  constraintData;
1536
1714
  config;
1537
1715
  transform;
1538
- strings;
1716
+ emptyTablesTemplateInput;
1717
+ tableCellTemplateInput;
1539
1718
  cellClick = new EventEmitter();
1540
1719
  pivotDataChange = new EventEmitter();
1720
+ pivotTablesChange = new EventEmitter();
1541
1721
  emptyTablesTemplate;
1542
1722
  tableCellTemplate;
1543
1723
  pivotTransformer = new PivotDataConverter();
@@ -1548,7 +1728,7 @@ class LmrPivotTableComponent {
1548
1728
  ngOnInit() {
1549
1729
  const observable = this.dataSubject.pipe(filter(data => !!data));
1550
1730
  this.pivotData$ = observable.pipe(throttleTime(200, asyncScheduler, { trailing: true, leading: true }), map(data => this.handleData(data)), tap(data => this.pivotDataChange.emit(data)));
1551
- this.pivotTables$ = this.pivotData$.pipe(map(data => this.pivotTableConverter.createTables(data, this.strings)));
1731
+ this.pivotTables$ = this.pivotData$.pipe(map(data => this.pivotTableConverter.createTables(data, this.transform)), tap(tables => this.pivotTablesChange.emit(tables)));
1552
1732
  }
1553
1733
  handleData(data) {
1554
1734
  return this.pivotTransformer.createData(data.config, data.transform, data.collections, data.linkTypes, data.data, data.query, data.constraintData);
@@ -1565,11 +1745,11 @@ class LmrPivotTableComponent {
1565
1745
  });
1566
1746
  }
1567
1747
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: LmrPivotTableComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1568
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.2.12", type: LmrPivotTableComponent, selector: "lmr-pivot-table", inputs: { collections: "collections", data: "data", linkTypes: "linkTypes", query: "query", constraintData: "constraintData", config: "config", transform: "transform", strings: "strings" }, outputs: { cellClick: "cellClick", pivotDataChange: "pivotDataChange" }, queries: [{ propertyName: "emptyTablesTemplate", first: true, predicate: LmrEmptyTablesTemplateDirective, descendants: true, read: TemplateRef }, { propertyName: "tableCellTemplate", first: true, predicate: LmrTableCellTemplateDirective, descendants: true, read: TemplateRef }], usesOnChanges: true, ngImport: i0, template: "<ng-container *ngIf=\"{pivotTables: pivotTables$ | async, pivotData: pivotData$ | async} as data\">\n <ng-container *ngIf=\"data.pivotTables?.length && !(data.pivotData | pivotDataEmpty)\">\n <table *ngFor=\"let pivotTable of data.pivotTables; let first\"\n class=\"table table-without-padding table-borderless table-md\"\n [class.mt-4]=\"!first\">\n <tr *ngFor=\"let rowCells of pivotTable.cells; let rowIndex = index\">\n\n <ng-container *ngFor=\"let cell of rowCells; let cellIndex = index\">\n <td *ngIf=\"cell && {hasValue: cell | pivotTableCellHasValue} as cellData\"\n class=\"cell {{cell.constraint ? (cell.constraint.type | lowercase) : ''}} text-truncate\"\n [style.max-width.px]=\"300\"\n [class.sticky-start]=\"cell.stickyStart\"\n [class.sticky-top]=\"cell.stickyTop\"\n [rowSpan]=\"cell.rowSpan\"\n [colSpan]=\"cell.colSpan\"\n [style.top.px]=\"cell.stickyTop ? (rowIndex * 40) : undefined\"\n [style.left.px]=\"cell.stickyStart ? (cellIndex * 150) : undefined\"\n [ngClass]=\"cell.cssClass\"\n [style.background]=\"cell.background\"\n [style.color]=\"cell.background && (cell.background | contrastColor)\">\n <ng-container *ngIf=\"cell.summary\">\n <div class=\"d-flex align-items-center h-100\">\n <span class=\"summary\" [class.me-2]=\"cellData.hasValue\">{{cell.summary}}</span>\n <ng-container *ngIf=\"cellData.hasValue\">\n <ng-template #defaultTableCellTemplate>\n <span class=\"flex-grow-1 h-100 text-truncate d-inline-block\">\n {{ cell.value }}\n </span>\n </ng-template>\n\n <ng-template\n [ngTemplateOutlet]=\"tableCellTemplate || defaultTableCellTemplate\"\n [ngTemplateOutletContext]=\"{ value: cell.value, cell: cell }\">\n </ng-template>\n </ng-container>\n </div>\n </ng-container>\n <ng-container *ngIf=\"!cell.summary\">\n <ng-container *ngIf=\"cellData.hasValue\">\n <ng-template #defaultTableCellTemplate>\n <span class=\"d-inline-block\">\n {{ cell.value }}\n </span>\n </ng-template>\n\n <ng-template\n [ngTemplateOutlet]=\"tableCellTemplate || defaultTableCellTemplate\"\n [ngTemplateOutletContext]=\"{ value: cell.value, cell: cell }\">\n </ng-template>\n </ng-container>\n <ng-container *ngIf=\"!cellData.hasValue\">&nbsp;</ng-container>\n </ng-container>\n </td>\n </ng-container>\n </tr>\n </table>\n </ng-container>\n\n <ng-container *ngIf=\"!data.pivotTables?.length || (data.pivotData | pivotDataEmpty)\">\n <ng-template #defaultEmptyTablesTemplate>\n <div>&nbsp;</div>\n </ng-template>\n\n <ng-template\n [ngTemplateOutlet]=\"emptyTablesTemplate || defaultEmptyTablesTemplate\"\n [ngTemplateOutletContext]=\"{ }\">\n </ng-template>\n </ng-container>\n</ng-container>\n", styles: [".table{border-spacing:0;border-collapse:separate}.pivot-data-cell,.pivot-data-group-cell{text-align:right}.pivot-column-header-cell,.pivot-row-header-cell,.pivot-data-group-cell,.pivot-row-group-header-cell,.pivot-column-group-header-cell{font-weight:700;border:1px #f8f9fa solid}.pivot-row-header-cell.sticky-start,.pivot-row-group-header-cell.sticky-start,.pivot-empty-cell.sticky-start{position:sticky;width:150px;min-width:150px;max-width:150px!important;z-index:9}.pivot-column-header-cell.sticky-top,.pivot-column-group-header-cell.sticky-top,.pivot-empty-cell.sticky-top{position:sticky;z-index:10}.pivot-empty-cell{background:white;border:1px white solid}.pivot-empty-cell.sticky-top{z-index:11}.cell.color{padding:0!important;height:30px}.cell.color .summary{padding-left:.5rem}data-input-preview.clickable-cell{cursor:pointer}data-input-preview.clickable-cell:hover{text-decoration:underline}.data-input{min-height:30px}\n"], dependencies: [{ kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "pipe", type: i1.AsyncPipe, name: "async" }, { kind: "pipe", type: i1.LowerCasePipe, name: "lowercase" }, { kind: "pipe", type: PivotDataEmptyPipe, name: "pivotDataEmpty" }, { kind: "pipe", type: PivotTableCellHasValuePipe, name: "pivotTableCellHasValue" }, { kind: "pipe", type: ContrastColorPipe, name: "contrastColor" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1748
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.2.12", type: LmrPivotTableComponent, selector: "lmr-pivot-table", inputs: { collections: "collections", data: "data", linkTypes: "linkTypes", query: "query", constraintData: "constraintData", config: "config", transform: "transform", emptyTablesTemplateInput: "emptyTablesTemplateInput", tableCellTemplateInput: "tableCellTemplateInput" }, outputs: { cellClick: "cellClick", pivotDataChange: "pivotDataChange", pivotTablesChange: "pivotTablesChange" }, queries: [{ propertyName: "emptyTablesTemplate", first: true, predicate: LmrEmptyTablesTemplateDirective, descendants: true, read: TemplateRef }, { propertyName: "tableCellTemplate", first: true, predicate: LmrTableCellTemplateDirective, descendants: true, read: TemplateRef }], usesOnChanges: true, ngImport: i0, template: "<ng-container *ngIf=\"{pivotTables: pivotTables$ | async, pivotData: pivotData$ | async} as data\">\n <ng-container *ngIf=\"data.pivotTables?.length && !(data.pivotData | pivotDataEmpty)\">\n <table *ngFor=\"let pivotTable of data.pivotTables; let first\"\n class=\"table table-without-padding table-borderless table-md\"\n [class.mt-4]=\"!first\">\n <tr *ngFor=\"let rowCells of pivotTable.cells; let rowIndex = index\">\n\n <ng-container *ngFor=\"let cell of rowCells; let cellIndex = index\">\n <td *ngIf=\"cell && {hasValue: cell | pivotTableCellHasValue} as cellData\"\n class=\"cell {{cell.constraint ? (cell.constraint.type | lowercase) : ''}} text-truncate\"\n [style.max-width.px]=\"300\"\n [class.sticky-start]=\"cell.stickyStart\"\n [class.sticky-top]=\"cell.stickyTop\"\n [rowSpan]=\"cell.rowSpan\"\n [colSpan]=\"cell.colSpan\"\n [style.top.px]=\"cell.stickyTop ? (rowIndex * 40) : undefined\"\n [style.left.px]=\"cell.stickyStart ? (cellIndex * 150) : undefined\"\n [ngClass]=\"cell.cssClass\"\n [style.background]=\"cell.background\"\n [style.color]=\"cell.background && (cell.background | contrastColor)\">\n <ng-container *ngIf=\"cell.summary\">\n <ng-container *ngIf=\"cellData.hasValue\">\n <div class=\"d-flex align-items-center h-100\">\n <span class=\"summary me-2\">{{cell.summary}}</span>\n <ng-template #defaultTableCellTemplate>\n <span class=\"flex-grow-1 h-100 text-truncate d-inline-block\">\n {{ cell.value }}\n </span>\n </ng-template>\n\n <ng-template\n [ngTemplateOutlet]=\"tableCellTemplate || tableCellTemplateInput || defaultTableCellTemplate\"\n [ngTemplateOutletContext]=\"{ value: cell.value, cell: cell }\">\n </ng-template>\n </div>\n </ng-container>\n <ng-container *ngIf=\"!cellData.hasValue\">\n <span class=\"d-inline-block summary\">{{cell.summary}}</span>\n </ng-container>\n </ng-container>\n <ng-container *ngIf=\"!cell.summary\">\n <ng-container *ngIf=\"cellData.hasValue\">\n <ng-template #defaultTableCellTemplate>\n <span class=\"d-inline-block\">\n {{ cell.value }}\n </span>\n </ng-template>\n\n <ng-template\n [ngTemplateOutlet]=\"tableCellTemplate || tableCellTemplateInput || defaultTableCellTemplate\"\n [ngTemplateOutletContext]=\"{ value: cell.value, cell: cell }\">\n </ng-template>\n </ng-container>\n <ng-container *ngIf=\"!cellData.hasValue\">&nbsp;</ng-container>\n </ng-container>\n </td>\n </ng-container>\n </tr>\n </table>\n </ng-container>\n\n <ng-container *ngIf=\"!data.pivotTables?.length || (data.pivotData | pivotDataEmpty)\">\n <ng-template #defaultEmptyTablesTemplate>\n <div>&nbsp;</div>\n </ng-template>\n\n <ng-template\n [ngTemplateOutlet]=\"emptyTablesTemplate || emptyTablesTemplateInput || defaultEmptyTablesTemplate\"\n [ngTemplateOutletContext]=\"{ }\">\n </ng-template>\n </ng-container>\n</ng-container>\n", styles: [".table{border-spacing:0;border-collapse:separate}.pivot-data-cell,.pivot-data-group-cell,.pivot-column-header-cell,.pivot-column-group-header-cell{text-align:right}.pivot-column-header-cell,.pivot-row-attribute-header-cell,.pivot-row-header-cell,.pivot-data-group-cell,.pivot-row-group-header-cell,.pivot-column-group-header-cell{font-weight:700;border:1px #f8f9fa solid}.pivot-row-header-cell.sticky-start,.pivot-row-group-header-cell.sticky-start,.pivot-empty-cell.sticky-start{position:sticky;width:150px;min-width:150px;max-width:150px!important;z-index:9}.pivot-column-header-cell.sticky-top,.pivot-row-attribute-header-cell.sticky-top,.pivot-column-group-header-cell.sticky-top,.pivot-empty-cell.sticky-top{position:sticky;z-index:10}.pivot-row-attribute-header-cell.sticky-top,.pivot-row-attribute-header-cell.sticky-start{position:sticky;z-index:11}.pivot-empty-cell{background:white;border:1px white solid}.pivot-empty-cell.sticky-top{z-index:11}.cell.color{padding:0!important;height:30px}.cell.color .summary{padding-left:.5rem}\n"], dependencies: [{ kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "pipe", type: i1.AsyncPipe, name: "async" }, { kind: "pipe", type: i1.LowerCasePipe, name: "lowercase" }, { kind: "pipe", type: PivotDataEmptyPipe, name: "pivotDataEmpty" }, { kind: "pipe", type: PivotTableCellHasValuePipe, name: "pivotTableCellHasValue" }, { kind: "pipe", type: ContrastColorPipe, name: "contrastColor" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1569
1749
  }
1570
1750
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: LmrPivotTableComponent, decorators: [{
1571
1751
  type: Component,
1572
- args: [{ selector: 'lmr-pivot-table', changeDetection: ChangeDetectionStrategy.OnPush, template: "<ng-container *ngIf=\"{pivotTables: pivotTables$ | async, pivotData: pivotData$ | async} as data\">\n <ng-container *ngIf=\"data.pivotTables?.length && !(data.pivotData | pivotDataEmpty)\">\n <table *ngFor=\"let pivotTable of data.pivotTables; let first\"\n class=\"table table-without-padding table-borderless table-md\"\n [class.mt-4]=\"!first\">\n <tr *ngFor=\"let rowCells of pivotTable.cells; let rowIndex = index\">\n\n <ng-container *ngFor=\"let cell of rowCells; let cellIndex = index\">\n <td *ngIf=\"cell && {hasValue: cell | pivotTableCellHasValue} as cellData\"\n class=\"cell {{cell.constraint ? (cell.constraint.type | lowercase) : ''}} text-truncate\"\n [style.max-width.px]=\"300\"\n [class.sticky-start]=\"cell.stickyStart\"\n [class.sticky-top]=\"cell.stickyTop\"\n [rowSpan]=\"cell.rowSpan\"\n [colSpan]=\"cell.colSpan\"\n [style.top.px]=\"cell.stickyTop ? (rowIndex * 40) : undefined\"\n [style.left.px]=\"cell.stickyStart ? (cellIndex * 150) : undefined\"\n [ngClass]=\"cell.cssClass\"\n [style.background]=\"cell.background\"\n [style.color]=\"cell.background && (cell.background | contrastColor)\">\n <ng-container *ngIf=\"cell.summary\">\n <div class=\"d-flex align-items-center h-100\">\n <span class=\"summary\" [class.me-2]=\"cellData.hasValue\">{{cell.summary}}</span>\n <ng-container *ngIf=\"cellData.hasValue\">\n <ng-template #defaultTableCellTemplate>\n <span class=\"flex-grow-1 h-100 text-truncate d-inline-block\">\n {{ cell.value }}\n </span>\n </ng-template>\n\n <ng-template\n [ngTemplateOutlet]=\"tableCellTemplate || defaultTableCellTemplate\"\n [ngTemplateOutletContext]=\"{ value: cell.value, cell: cell }\">\n </ng-template>\n </ng-container>\n </div>\n </ng-container>\n <ng-container *ngIf=\"!cell.summary\">\n <ng-container *ngIf=\"cellData.hasValue\">\n <ng-template #defaultTableCellTemplate>\n <span class=\"d-inline-block\">\n {{ cell.value }}\n </span>\n </ng-template>\n\n <ng-template\n [ngTemplateOutlet]=\"tableCellTemplate || defaultTableCellTemplate\"\n [ngTemplateOutletContext]=\"{ value: cell.value, cell: cell }\">\n </ng-template>\n </ng-container>\n <ng-container *ngIf=\"!cellData.hasValue\">&nbsp;</ng-container>\n </ng-container>\n </td>\n </ng-container>\n </tr>\n </table>\n </ng-container>\n\n <ng-container *ngIf=\"!data.pivotTables?.length || (data.pivotData | pivotDataEmpty)\">\n <ng-template #defaultEmptyTablesTemplate>\n <div>&nbsp;</div>\n </ng-template>\n\n <ng-template\n [ngTemplateOutlet]=\"emptyTablesTemplate || defaultEmptyTablesTemplate\"\n [ngTemplateOutletContext]=\"{ }\">\n </ng-template>\n </ng-container>\n</ng-container>\n", styles: [".table{border-spacing:0;border-collapse:separate}.pivot-data-cell,.pivot-data-group-cell{text-align:right}.pivot-column-header-cell,.pivot-row-header-cell,.pivot-data-group-cell,.pivot-row-group-header-cell,.pivot-column-group-header-cell{font-weight:700;border:1px #f8f9fa solid}.pivot-row-header-cell.sticky-start,.pivot-row-group-header-cell.sticky-start,.pivot-empty-cell.sticky-start{position:sticky;width:150px;min-width:150px;max-width:150px!important;z-index:9}.pivot-column-header-cell.sticky-top,.pivot-column-group-header-cell.sticky-top,.pivot-empty-cell.sticky-top{position:sticky;z-index:10}.pivot-empty-cell{background:white;border:1px white solid}.pivot-empty-cell.sticky-top{z-index:11}.cell.color{padding:0!important;height:30px}.cell.color .summary{padding-left:.5rem}data-input-preview.clickable-cell{cursor:pointer}data-input-preview.clickable-cell:hover{text-decoration:underline}.data-input{min-height:30px}\n"] }]
1752
+ args: [{ selector: 'lmr-pivot-table', changeDetection: ChangeDetectionStrategy.OnPush, template: "<ng-container *ngIf=\"{pivotTables: pivotTables$ | async, pivotData: pivotData$ | async} as data\">\n <ng-container *ngIf=\"data.pivotTables?.length && !(data.pivotData | pivotDataEmpty)\">\n <table *ngFor=\"let pivotTable of data.pivotTables; let first\"\n class=\"table table-without-padding table-borderless table-md\"\n [class.mt-4]=\"!first\">\n <tr *ngFor=\"let rowCells of pivotTable.cells; let rowIndex = index\">\n\n <ng-container *ngFor=\"let cell of rowCells; let cellIndex = index\">\n <td *ngIf=\"cell && {hasValue: cell | pivotTableCellHasValue} as cellData\"\n class=\"cell {{cell.constraint ? (cell.constraint.type | lowercase) : ''}} text-truncate\"\n [style.max-width.px]=\"300\"\n [class.sticky-start]=\"cell.stickyStart\"\n [class.sticky-top]=\"cell.stickyTop\"\n [rowSpan]=\"cell.rowSpan\"\n [colSpan]=\"cell.colSpan\"\n [style.top.px]=\"cell.stickyTop ? (rowIndex * 40) : undefined\"\n [style.left.px]=\"cell.stickyStart ? (cellIndex * 150) : undefined\"\n [ngClass]=\"cell.cssClass\"\n [style.background]=\"cell.background\"\n [style.color]=\"cell.background && (cell.background | contrastColor)\">\n <ng-container *ngIf=\"cell.summary\">\n <ng-container *ngIf=\"cellData.hasValue\">\n <div class=\"d-flex align-items-center h-100\">\n <span class=\"summary me-2\">{{cell.summary}}</span>\n <ng-template #defaultTableCellTemplate>\n <span class=\"flex-grow-1 h-100 text-truncate d-inline-block\">\n {{ cell.value }}\n </span>\n </ng-template>\n\n <ng-template\n [ngTemplateOutlet]=\"tableCellTemplate || tableCellTemplateInput || defaultTableCellTemplate\"\n [ngTemplateOutletContext]=\"{ value: cell.value, cell: cell }\">\n </ng-template>\n </div>\n </ng-container>\n <ng-container *ngIf=\"!cellData.hasValue\">\n <span class=\"d-inline-block summary\">{{cell.summary}}</span>\n </ng-container>\n </ng-container>\n <ng-container *ngIf=\"!cell.summary\">\n <ng-container *ngIf=\"cellData.hasValue\">\n <ng-template #defaultTableCellTemplate>\n <span class=\"d-inline-block\">\n {{ cell.value }}\n </span>\n </ng-template>\n\n <ng-template\n [ngTemplateOutlet]=\"tableCellTemplate || tableCellTemplateInput || defaultTableCellTemplate\"\n [ngTemplateOutletContext]=\"{ value: cell.value, cell: cell }\">\n </ng-template>\n </ng-container>\n <ng-container *ngIf=\"!cellData.hasValue\">&nbsp;</ng-container>\n </ng-container>\n </td>\n </ng-container>\n </tr>\n </table>\n </ng-container>\n\n <ng-container *ngIf=\"!data.pivotTables?.length || (data.pivotData | pivotDataEmpty)\">\n <ng-template #defaultEmptyTablesTemplate>\n <div>&nbsp;</div>\n </ng-template>\n\n <ng-template\n [ngTemplateOutlet]=\"emptyTablesTemplate || emptyTablesTemplateInput || defaultEmptyTablesTemplate\"\n [ngTemplateOutletContext]=\"{ }\">\n </ng-template>\n </ng-container>\n</ng-container>\n", styles: [".table{border-spacing:0;border-collapse:separate}.pivot-data-cell,.pivot-data-group-cell,.pivot-column-header-cell,.pivot-column-group-header-cell{text-align:right}.pivot-column-header-cell,.pivot-row-attribute-header-cell,.pivot-row-header-cell,.pivot-data-group-cell,.pivot-row-group-header-cell,.pivot-column-group-header-cell{font-weight:700;border:1px #f8f9fa solid}.pivot-row-header-cell.sticky-start,.pivot-row-group-header-cell.sticky-start,.pivot-empty-cell.sticky-start{position:sticky;width:150px;min-width:150px;max-width:150px!important;z-index:9}.pivot-column-header-cell.sticky-top,.pivot-row-attribute-header-cell.sticky-top,.pivot-column-group-header-cell.sticky-top,.pivot-empty-cell.sticky-top{position:sticky;z-index:10}.pivot-row-attribute-header-cell.sticky-top,.pivot-row-attribute-header-cell.sticky-start{position:sticky;z-index:11}.pivot-empty-cell{background:white;border:1px white solid}.pivot-empty-cell.sticky-top{z-index:11}.cell.color{padding:0!important;height:30px}.cell.color .summary{padding-left:.5rem}\n"] }]
1573
1753
  }], propDecorators: { collections: [{
1574
1754
  type: Input
1575
1755
  }], data: [{
@@ -1584,12 +1764,109 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImpo
1584
1764
  type: Input
1585
1765
  }], transform: [{
1586
1766
  type: Input
1587
- }], strings: [{
1767
+ }], emptyTablesTemplateInput: [{
1768
+ type: Input
1769
+ }], tableCellTemplateInput: [{
1588
1770
  type: Input
1589
1771
  }], cellClick: [{
1590
1772
  type: Output
1591
1773
  }], pivotDataChange: [{
1592
1774
  type: Output
1775
+ }], pivotTablesChange: [{
1776
+ type: Output
1777
+ }], emptyTablesTemplate: [{
1778
+ type: ContentChild,
1779
+ args: [LmrEmptyTablesTemplateDirective, { read: TemplateRef }]
1780
+ }], tableCellTemplate: [{
1781
+ type: ContentChild,
1782
+ args: [LmrTableCellTemplateDirective, { read: TemplateRef }]
1783
+ }] } });
1784
+
1785
+ class LmrSimplePivotTableComponent {
1786
+ rows;
1787
+ attributes;
1788
+ color;
1789
+ config;
1790
+ transform;
1791
+ locale;
1792
+ cellClick = new EventEmitter();
1793
+ pivotDataChange = new EventEmitter();
1794
+ pivotTablesChange = new EventEmitter();
1795
+ emptyTablesTemplate;
1796
+ tableCellTemplate;
1797
+ collectionId = generateId();
1798
+ query = { stems: [{ collectionId: this.collectionId }] };
1799
+ collection = this.createCollection();
1800
+ pivotConfig = this.createConfig();
1801
+ data = this.createRows();
1802
+ constraintData;
1803
+ ngOnChanges(changes) {
1804
+ if (changes['rows']) {
1805
+ this.data = this.createRows();
1806
+ }
1807
+ if (changes['attributes'] || changes['color']) {
1808
+ this.collection = this.createCollection();
1809
+ }
1810
+ if (changes['config']) {
1811
+ this.pivotConfig = this.createConfig();
1812
+ }
1813
+ this.constraintData = {
1814
+ locale: this.locale || LanguageTag.USA,
1815
+ };
1816
+ }
1817
+ createCollection() {
1818
+ return {
1819
+ id: this.collectionId,
1820
+ attributes: (this.attributes || []),
1821
+ color: this.color,
1822
+ };
1823
+ }
1824
+ createConfig() {
1825
+ return {
1826
+ stemsConfigs: [{
1827
+ stem: this.query.stems[0],
1828
+ rowAttributes: (this.config?.rowAttributes || []).map(attribute => ({ ...attribute, resourceId: this.collectionId, resourceIndex: 0, resourceType: AttributesResourceType.Collection })),
1829
+ columnAttributes: (this.config?.columnAttributes || []).map(attribute => ({ ...attribute, resourceId: this.collectionId, resourceIndex: 0, resourceType: AttributesResourceType.Collection })),
1830
+ valueAttributes: (this.config?.valueAttributes || []).map(attribute => ({ ...attribute, resourceId: this.collectionId, resourceIndex: 0, resourceType: AttributesResourceType.Collection }))
1831
+ }]
1832
+ };
1833
+ }
1834
+ createRows() {
1835
+ const documents = (this.rows || []).map((row, index) => ({ id: index.toString(), collectionId: this.collection.id, data: row }));
1836
+ return ({
1837
+ uniqueDocuments: documents,
1838
+ uniqueLinkInstances: [],
1839
+ dataByStems: [{
1840
+ stem: this.query.stems[0],
1841
+ linkInstances: [],
1842
+ documents
1843
+ }]
1844
+ });
1845
+ }
1846
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: LmrSimplePivotTableComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1847
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.2.12", type: LmrSimplePivotTableComponent, selector: "lmr-simple-pivot-table", inputs: { rows: "rows", attributes: "attributes", color: "color", config: "config", transform: "transform", locale: "locale" }, outputs: { cellClick: "cellClick", pivotDataChange: "pivotDataChange", pivotTablesChange: "pivotTablesChange" }, queries: [{ propertyName: "emptyTablesTemplate", first: true, predicate: LmrEmptyTablesTemplateDirective, descendants: true, read: TemplateRef }, { propertyName: "tableCellTemplate", first: true, predicate: LmrTableCellTemplateDirective, descendants: true, read: TemplateRef }], usesOnChanges: true, ngImport: i0, template: "<lmr-pivot-table [transform]=\"transform\"\n [data]=\"data\"\n [config]=\"pivotConfig\"\n [query]=\"query\"\n [constraintData]=\"constraintData\"\n [collections]=\"[collection]\"\n [emptyTablesTemplateInput]=\"emptyTablesTemplate\"\n [tableCellTemplateInput]=\"tableCellTemplate\"\n (pivotDataChange)=\"pivotDataChange.emit($event)\"\n (pivotTablesChange)=\"pivotTablesChange.emit($event)\">\n</lmr-pivot-table>\n", dependencies: [{ kind: "component", type: LmrPivotTableComponent, selector: "lmr-pivot-table", inputs: ["collections", "data", "linkTypes", "query", "constraintData", "config", "transform", "emptyTablesTemplateInput", "tableCellTemplateInput"], outputs: ["cellClick", "pivotDataChange", "pivotTablesChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1848
+ }
1849
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: LmrSimplePivotTableComponent, decorators: [{
1850
+ type: Component,
1851
+ args: [{ selector: 'lmr-simple-pivot-table', changeDetection: ChangeDetectionStrategy.OnPush, template: "<lmr-pivot-table [transform]=\"transform\"\n [data]=\"data\"\n [config]=\"pivotConfig\"\n [query]=\"query\"\n [constraintData]=\"constraintData\"\n [collections]=\"[collection]\"\n [emptyTablesTemplateInput]=\"emptyTablesTemplate\"\n [tableCellTemplateInput]=\"tableCellTemplate\"\n (pivotDataChange)=\"pivotDataChange.emit($event)\"\n (pivotTablesChange)=\"pivotTablesChange.emit($event)\">\n</lmr-pivot-table>\n" }]
1852
+ }], propDecorators: { rows: [{
1853
+ type: Input
1854
+ }], attributes: [{
1855
+ type: Input
1856
+ }], color: [{
1857
+ type: Input
1858
+ }], config: [{
1859
+ type: Input
1860
+ }], transform: [{
1861
+ type: Input
1862
+ }], locale: [{
1863
+ type: Input
1864
+ }], cellClick: [{
1865
+ type: Output
1866
+ }], pivotDataChange: [{
1867
+ type: Output
1868
+ }], pivotTablesChange: [{
1869
+ type: Output
1593
1870
  }], emptyTablesTemplate: [{
1594
1871
  type: ContentChild,
1595
1872
  args: [LmrEmptyTablesTemplateDirective, { read: TemplateRef }]
@@ -1601,20 +1878,25 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImpo
1601
1878
  class LmrPivotTableModule {
1602
1879
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: LmrPivotTableModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
1603
1880
  static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "16.2.12", ngImport: i0, type: LmrPivotTableModule, declarations: [LmrPivotTableComponent,
1881
+ LmrSimplePivotTableComponent,
1604
1882
  PivotDataEmptyPipe,
1605
1883
  PivotTableCellHasValuePipe,
1606
1884
  ContrastColorPipe,
1607
1885
  LmrEmptyTablesTemplateDirective,
1608
- LmrTableCellTemplateDirective], imports: [CommonModule], exports: [LmrPivotTableComponent,
1886
+ LmrTableCellTemplateDirective], imports: [CommonModule,
1887
+ ScrollingModule], exports: [LmrPivotTableComponent,
1888
+ LmrSimplePivotTableComponent,
1609
1889
  LmrEmptyTablesTemplateDirective,
1610
1890
  LmrTableCellTemplateDirective] });
1611
- static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: LmrPivotTableModule, imports: [CommonModule] });
1891
+ static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: LmrPivotTableModule, imports: [CommonModule,
1892
+ ScrollingModule] });
1612
1893
  }
1613
1894
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: LmrPivotTableModule, decorators: [{
1614
1895
  type: NgModule,
1615
1896
  args: [{
1616
1897
  declarations: [
1617
1898
  LmrPivotTableComponent,
1899
+ LmrSimplePivotTableComponent,
1618
1900
  PivotDataEmptyPipe,
1619
1901
  PivotTableCellHasValuePipe,
1620
1902
  ContrastColorPipe,
@@ -1623,9 +1905,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImpo
1623
1905
  ],
1624
1906
  imports: [
1625
1907
  CommonModule,
1908
+ ScrollingModule,
1626
1909
  ],
1627
1910
  exports: [
1628
1911
  LmrPivotTableComponent,
1912
+ LmrSimplePivotTableComponent,
1629
1913
  LmrEmptyTablesTemplateDirective,
1630
1914
  LmrTableCellTemplateDirective,
1631
1915
  ]
@@ -1640,5 +1924,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImpo
1640
1924
  * Generated bundle index. Do not edit.
1641
1925
  */
1642
1926
 
1643
- export { LmrEmptyTablesTemplateDirective, LmrPivotConfigVersion, LmrPivotTableComponent, LmrPivotTableModule, LmrPivotValueType, LmrTableCellTemplateDirective, contrastColor, createDefaultPivotConfig, createDefaultPivotStemConfig, isPivotConfigChanged, pivotAttributesAreSame, pivotConfigIsEmpty, pivotStemConfigIsEmpty };
1927
+ export { LmrEmptyTablesTemplateDirective, LmrPivotConfigVersion, LmrPivotTableComponent, LmrPivotTableModule, LmrPivotValueType, LmrSimplePivotTableComponent, LmrTableCellTemplateDirective, contrastColor, createDefaultPivotConfig, createDefaultPivotStemConfig, isPivotConfigChanged, pivotAttributesAreSame, pivotConfigIsEmpty, pivotStemConfigIsEmpty };
1644
1928
  //# sourceMappingURL=lumeer-pivot.mjs.map