@platforma-sdk/ui-vue 1.5.56 → 1.6.0

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 (38) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/dist/lib.js +31293 -31207
  3. package/dist/lib.umd.cjs +72 -72
  4. package/dist/src/aggrid.d.ts.map +1 -1
  5. package/dist/src/components/PlAgDataTable/PlAgDataTable.vue.d.ts +1 -1
  6. package/dist/src/components/PlAgDataTable/PlAgDataTable.vue.d.ts.map +1 -1
  7. package/dist/src/components/PlAgDataTable/{OverlayLoading.vue.d.ts → PlAgOverlayLoading.vue.d.ts} +1 -1
  8. package/dist/src/components/PlAgDataTable/PlAgOverlayLoading.vue.d.ts.map +1 -0
  9. package/dist/src/components/PlAgDataTable/{OverlayNoRows.vue.d.ts → PlAgOverlayNoRows.vue.d.ts} +1 -1
  10. package/dist/src/components/PlAgDataTable/PlAgOverlayNoRows.vue.d.ts.map +1 -0
  11. package/dist/src/components/PlAgDataTable/index.d.ts +2 -0
  12. package/dist/src/components/PlAgDataTable/index.d.ts.map +1 -1
  13. package/dist/src/components/PlAgDataTable/sources/table-source-heterogeneous.d.ts +37 -0
  14. package/dist/src/components/PlAgDataTable/sources/table-source-heterogeneous.d.ts.map +1 -0
  15. package/dist/src/components/PlAgDataTable/sources/table-source.d.ts +6 -4
  16. package/dist/src/components/PlAgDataTable/sources/table-source.d.ts.map +1 -1
  17. package/dist/src/components/PlAgDataTable/types.d.ts +3 -3
  18. package/dist/src/components/PlAgDataTable/types.d.ts.map +1 -1
  19. package/dist/src/lib.d.ts +3 -1
  20. package/dist/src/lib.d.ts.map +1 -1
  21. package/dist/style.css +1 -1
  22. package/dist/tsconfig.lib.tsbuildinfo +1 -1
  23. package/package.json +3 -3
  24. package/src/aggrid.ts +4 -2
  25. package/src/components/PlAgDataTable/PlAgDataTable.vue +70 -63
  26. package/src/components/PlAgDataTable/{OverlayLoading.vue → PlAgOverlayLoading.vue} +1 -1
  27. package/src/components/PlAgDataTable/{OverlayNoRows.vue → PlAgOverlayNoRows.vue} +1 -1
  28. package/src/components/PlAgDataTable/assets/loading-cat.png +0 -0
  29. package/src/components/PlAgDataTable/assets/no-data-cat.png +0 -0
  30. package/src/components/PlAgDataTable/index.ts +2 -0
  31. package/src/components/PlAgDataTable/sources/table-source-heterogeneous.ts +192 -0
  32. package/src/components/PlAgDataTable/sources/table-source.ts +56 -23
  33. package/src/components/PlAgDataTable/types.ts +3 -3
  34. package/src/lib.ts +3 -1
  35. package/dist/src/components/PlAgDataTable/OverlayLoading.vue.d.ts.map +0 -1
  36. package/dist/src/components/PlAgDataTable/OverlayNoRows.vue.d.ts.map +0 -1
  37. package/src/components/PlAgDataTable/assets/cat-in-bag.png +0 -0
  38. package/src/components/PlAgDataTable/assets/sad-cat.png +0 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@platforma-sdk/ui-vue",
3
- "version": "1.5.56",
3
+ "version": "1.6.0",
4
4
  "type": "module",
5
5
  "main": "dist/lib.umd.cjs",
6
6
  "module": "dist/lib.js",
@@ -21,8 +21,8 @@
21
21
  "lru-cache": "^11.0.1",
22
22
  "vue": "^3.5.11",
23
23
  "canonicalize": "^2.0.0",
24
- "@platforma-sdk/model": "^1.5.40",
25
- "@milaboratories/uikit": "^1.2.19"
24
+ "@milaboratories/uikit": "^1.2.20",
25
+ "@platforma-sdk/model": "^1.6.0"
26
26
  },
27
27
  "devDependencies": {
28
28
  "@ag-grid-community/client-side-row-model": "^32.2.2",
package/src/aggrid.ts CHANGED
@@ -1,10 +1,12 @@
1
- import { getEnvironmentValue } from '@platforma-sdk/model';
2
1
  import { LicenseManager } from '@ag-grid-enterprise/core';
2
+ import { getEnvironmentValue } from '@platforma-sdk/model';
3
3
 
4
4
  export function activateAgGrid() {
5
5
  const agGridLicense = getEnvironmentValue('AGGRID_LICENSE');
6
6
  if (agGridLicense) {
7
7
  LicenseManager.setLicenseKey(agGridLicense);
8
- console.log('AGGrid Activated');
8
+ console.log('AG Grid Activated');
9
+ } else {
10
+ console.log('AG Grid License not found');
9
11
  }
10
12
  }
@@ -1,7 +1,5 @@
1
1
  <script lang="ts" setup>
2
- import './ag-theme.css';
3
2
  import { ClientSideRowModelModule } from '@ag-grid-community/client-side-row-model';
4
- import canonicalize from 'canonicalize';
5
3
  import type { GridApi, GridOptions, SortState } from '@ag-grid-community/core';
6
4
  import { ModuleRegistry } from '@ag-grid-community/core';
7
5
  import { InfiniteRowModelModule } from '@ag-grid-community/infinite-row-model';
@@ -10,14 +8,21 @@ import { ClipboardModule } from '@ag-grid-enterprise/clipboard';
10
8
  import { RangeSelectionModule } from '@ag-grid-enterprise/range-selection';
11
9
  import { PlDropdownLine } from '@milaboratories/uikit';
12
10
  import type { AxisId, PlDataTableState, PTableRecordFilter, PTableSorting } from '@platforma-sdk/model';
13
- import { computed, ref, watch, shallowRef, toRefs } from 'vue';
14
- import OverlayLoading from './OverlayLoading.vue';
15
- import OverlayNoRows from './OverlayNoRows.vue';
16
- import { updateXsvGridOptions } from './sources/file-source';
17
- import { parseColId, makeSheets, updatePFrameGridOptions, enrichJoinWithLabelColumns } from './sources/table-source';
18
- import type { PlDataTableSheet, PlDataTableSettings } from './types';
19
- import * as lodash from 'lodash';
20
11
  import { computedAsync } from '@vueuse/core';
12
+ import canonicalize from 'canonicalize';
13
+ import * as lodash from 'lodash';
14
+ import { computed, ref, shallowRef, toRefs, watch } from 'vue';
15
+ import './ag-theme.css';
16
+ import PlOverlayLoading from './PlAgOverlayLoading.vue';
17
+ import PlOverlayNoRows from './PlAgOverlayNoRows.vue';
18
+ import { updateXsvGridOptions } from './sources/file-source';
19
+ import {
20
+ enrichJoinWithLabelColumns,
21
+ makeSheets,
22
+ parseColId,
23
+ updatePFrameGridOptions
24
+ } from './sources/table-source';
25
+ import type { PlDataTableSettings, PlDataTableSheet } from './types';
21
26
 
22
27
  ModuleRegistry.registerModules([ClientSideRowModelModule, ClipboardModule, InfiniteRowModelModule, RangeSelectionModule]);
23
28
 
@@ -30,15 +35,18 @@ const { settings } = toRefs(props);
30
35
  watch(
31
36
  () => settings.value,
32
37
  async (settings, oldSettings) => {
33
- if (settings.sourceType !== 'pframe' || !settings.pFrame?.ok || !settings.pFrame.value || !settings.join) return;
38
+ if (
39
+ settings.sourceType !== 'pframe' ||
40
+ !settings.pFrame ||
41
+ !settings.join
42
+ ) return;
34
43
 
35
44
  if (
36
45
  oldSettings &&
37
46
  oldSettings.sourceType === 'pframe' &&
38
47
  lodash.isEqual(settings.pFrame, oldSettings.pFrame) &&
39
48
  lodash.isEqual(settings.join, oldSettings.join)
40
- )
41
- return;
49
+ ) return;
42
50
 
43
51
  const platforma = window.platforma;
44
52
  if (!platforma) throw Error('platforma not set');
@@ -46,7 +54,7 @@ watch(
46
54
  const pfDriver = platforma.pFrameDriver;
47
55
  if (!pfDriver) throw Error('platforma.pFrameDriver not set');
48
56
 
49
- const enrichedJoin = await enrichJoinWithLabelColumns(pfDriver, settings.pFrame.value, settings.join);
57
+ const enrichedJoin = await enrichJoinWithLabelColumns(pfDriver, settings.pFrame, settings.join);
50
58
 
51
59
  const state = tableState.value;
52
60
 
@@ -59,37 +67,39 @@ watch(
59
67
  state.pTableParams.join = enrichedJoin;
60
68
 
61
69
  tableState.value = state;
62
- },
70
+ }
63
71
  );
64
72
 
65
- const sheets = computedAsync<PlDataTableSheet[]>(async () => {
66
- const sourceType = settings.value.sourceType;
67
- switch (sourceType) {
68
- case 'pframe': {
69
- const platforma = window.platforma;
70
- if (!platforma) throw Error('platforma not set');
73
+ const sheets = computedAsync<PlDataTableSheet[]>(
74
+ async () => {
75
+ const sourceType = settings.value.sourceType;
76
+ switch (sourceType) {
77
+ case 'pframe': {
78
+ const platforma = window.platforma;
79
+ if (!platforma) throw Error('platforma not set');
71
80
 
72
- const pfDriver = platforma.pFrameDriver;
73
- if (!pfDriver) throw Error('platforma.pFrameDriver not set');
81
+ const pfDriver = platforma.pFrameDriver;
82
+ if (!pfDriver) throw Error('platforma.pFrameDriver not set');
74
83
 
75
- if (!settings.value.pFrame?.ok || !settings.value.pFrame.value) return [];
76
- const pFrame = settings.value.pFrame.value;
84
+ if (!settings.value.pFrame) return [];
85
+ const pFrame = settings.value.pFrame;
77
86
 
78
- const join = tableState.value.pTableParams?.join;
79
- if (!join) return [];
87
+ const join = tableState.value.pTableParams?.join;
88
+ if (!join) return [];
80
89
 
81
- return await makeSheets(pfDriver, pFrame, settings.value.sheetAxes, join);
82
- }
83
- case 'ptable': {
84
- return settings.value.sheets ?? [];
85
- }
86
- case 'xsv': {
87
- return [];
90
+ return await makeSheets(pfDriver, pFrame, settings.value.sheetAxes, join);
91
+ }
92
+ case 'ptable': {
93
+ return settings.value.sheets ?? [];
94
+ }
95
+ case 'xsv': {
96
+ return [];
97
+ }
98
+ default: throw Error(`unsupported source type: ${sourceType satisfies never}`);
88
99
  }
89
- default:
90
- throw Error(`unsupported source type: ${sourceType satisfies never}`);
91
- }
92
- }, []);
100
+ },
101
+ []
102
+ );
93
103
 
94
104
  function makeSorting(state?: SortState): PTableSorting[] | undefined {
95
105
  if (settings.value.sourceType !== 'ptable' && settings.value.sourceType !== 'pframe') return undefined;
@@ -114,10 +124,12 @@ const gridState = computed({
114
124
  };
115
125
  },
116
126
  set: (gridState) => {
117
- const sorting = makeSorting(gridState.sort);
118
127
 
119
128
  const state = tableState.value;
120
129
 
130
+ // do not apply driver sorting for client side rendering
131
+ const sorting = gridOptions.rowModelType === 'clientSide' ? undefined : makeSorting(gridState.sort);
132
+
121
133
  state.gridState.columnOrder = gridState.columnOrder;
122
134
  state.gridState.sort = gridState.sort;
123
135
 
@@ -144,13 +156,13 @@ function makeFilters(sheetsState: Record<string, string | number>): PTableRecord
144
156
  type: 'bySingleColumn',
145
157
  column: sheet.column
146
158
  ? {
147
- type: 'column',
148
- id: sheet.column,
149
- }
159
+ type: 'column',
160
+ id: sheet.column,
161
+ }
150
162
  : {
151
- type: 'axis',
152
- id: sheet.axis,
153
- },
163
+ type: 'axis',
164
+ id: sheet.axis,
165
+ },
154
166
  predicate: {
155
167
  operator: 'Equal',
156
168
  reference: sheetsState[makeSheetId(sheet.axis)],
@@ -188,12 +200,7 @@ watch(
188
200
  const [settings, sheets] = state;
189
201
  if (oldState) {
190
202
  const [oldSettings, oldSheets] = oldState;
191
- if (
192
- (settings.sourceType === 'ptable' || settings.sourceType === 'pframe') &&
193
- settings.sourceType === oldSettings?.sourceType &&
194
- lodash.isEqual(sheets, oldSheets)
195
- )
196
- return;
203
+ if ((settings.sourceType === 'ptable' || settings.sourceType === 'pframe') && settings.sourceType === oldSettings?.sourceType && lodash.isEqual(sheets, oldSheets)) return;
197
204
  }
198
205
 
199
206
  if (settings.sourceType !== 'ptable' && settings.sourceType !== 'pframe') {
@@ -217,7 +224,7 @@ const gridApi = shallowRef<GridApi>();
217
224
  const gridOptions: GridOptions = {
218
225
  animateRows: false,
219
226
  suppressColumnMoveAnimation: true,
220
- enableRangeSelection: true,
227
+ cellSelection: true,
221
228
  initialState: tableState.value.gridState,
222
229
  onGridReady: (event) => {
223
230
  gridApi.value = event.api;
@@ -232,14 +239,14 @@ const gridOptions: GridOptions = {
232
239
  onRowDataUpdated: (event) => {
233
240
  event.api.autoSizeAllColumns();
234
241
  },
235
- rowModelType: 'infinite',
242
+ // rowModelType: 'infinite', // will be set with the first data set
236
243
  maxBlocksInCache: 10000,
237
244
  cacheBlockSize: 100,
238
245
  getRowId: (params) => params.data.id,
239
246
  loading: true,
240
247
  loadingOverlayComponentParams: { notReady: true },
241
- loadingOverlayComponent: OverlayLoading,
242
- noRowsOverlayComponent: OverlayNoRows,
248
+ loadingOverlayComponent: PlOverlayLoading,
249
+ noRowsOverlayComponent: PlOverlayNoRows,
243
250
  };
244
251
 
245
252
  const reloadKey = ref(0);
@@ -285,7 +292,7 @@ watch(
285
292
  if (!pfDriver) throw Error('platforma.pFrameDriver not set');
286
293
 
287
294
  const pTable = settings.pTable;
288
- if (!pTable?.ok || !pTable.value) {
295
+ if (!pTable) {
289
296
  return gridApi.updateGridOptions({
290
297
  loading: true,
291
298
  loadingOverlayComponentParams: { notReady: true },
@@ -294,9 +301,9 @@ watch(
294
301
  });
295
302
  }
296
303
 
297
- const options = await updatePFrameGridOptions(gridApi, pfDriver, pTable.value, sheets);
304
+ const options = await updatePFrameGridOptions(gridApi, pfDriver, pTable, sheets);
298
305
  return gridApi.updateGridOptions({
299
- loading: true,
306
+ loading: options.rowModelType === 'infinite',
300
307
  loadingOverlayComponentParams: { notReady: false },
301
308
  ...options,
302
309
  });
@@ -335,13 +342,9 @@ watch(
335
342
  <div class="ap-ag-data-table-container">
336
343
  <Transition name="ap-ag-data-table-sheets-transition">
337
344
  <div v-if="sheets.length > 0" class="ap-ag-data-table-sheets">
338
- <PlDropdownLine
339
- v-for="(sheet, i) in sheets"
340
- :key="i"
341
- :model-value="sheetsState[makeSheetId(sheet.axis)]"
345
+ <PlDropdownLine v-for="(sheet, i) in sheets" :key="i" :model-value="sheetsState[makeSheetId(sheet.axis)]"
342
346
  :options="sheet.options"
343
- @update:model-value="(newValue) => onSheetChanged(makeSheetId(sheet.axis), newValue)"
344
- />
347
+ @update:model-value="(newValue) => onSheetChanged(makeSheetId(sheet.axis), newValue)" />
345
348
  </div>
346
349
  </Transition>
347
350
  <AgGridVue class="ap-ag-data-table-grid" :grid-options="gridOptions" />
@@ -355,14 +358,17 @@ watch(
355
358
  height: 100%;
356
359
  gap: 12px;
357
360
  }
361
+
358
362
  .ap-ag-data-table-sheets-transition-enter-active,
359
363
  .ap-ag-data-table-sheets-transition-leave-active {
360
364
  transition: all 0.2s ease-in-out;
361
365
  }
366
+
362
367
  .ap-ag-data-table-sheets-transition-enter-from,
363
368
  .ap-ag-data-table-sheets-transition-leave-to {
364
369
  margin-top: -52px;
365
370
  }
371
+
366
372
  .ap-ag-data-table-sheets {
367
373
  display: flex;
368
374
  flex-direction: row;
@@ -370,6 +376,7 @@ watch(
370
376
  flex-wrap: wrap;
371
377
  z-index: 3;
372
378
  }
379
+
373
380
  .ap-ag-data-table-grid {
374
381
  flex: 1;
375
382
  }
@@ -29,7 +29,7 @@ defineProps<{
29
29
  justify-content: center;
30
30
  }
31
31
  .grid-icon-cat-in-bag {
32
- background-image: url(./assets/cat-in-bag.png);
32
+ background-image: url(./assets/loading-cat.png);
33
33
  background-repeat: no-repeat;
34
34
  }
35
35
  .grid-mask-loading {
@@ -15,7 +15,7 @@
15
15
  justify-content: center;
16
16
  }
17
17
  .grid-icon-sad-cat {
18
- background-image: url(./assets/sad-cat.png);
18
+ background-image: url(./assets/no-data-cat.png);
19
19
  background-repeat: no-repeat;
20
20
  }
21
21
  .grid-overlay-container > div {
@@ -1,3 +1,5 @@
1
1
  export { default as PlDataTable } from './PlAgDataTable.vue';
2
+ export { default as PlAgOverlayLoading } from './PlAgOverlayLoading.vue';
3
+ export { default as PlAgOverlayNoRows } from './PlAgOverlayNoRows.vue';
2
4
 
3
5
  export * from './types';
@@ -0,0 +1,192 @@
1
+ import { ColDef, IDatasource, RowModelType } from '@ag-grid-community/core';
2
+ import { AxisSpec, getAxisId, PColumnSpec, PTableColumnSpec, PTableShape, PTableVector, PVectorDataString } from '@platforma-sdk/model';
3
+ import canonicalize from 'canonicalize';
4
+ import * as lodash from 'lodash';
5
+ import { defaultValueFormatter } from './table-source';
6
+
7
+ type HeterogeneousColumnInfo = {
8
+ /**
9
+ * Index of the column with heterogeneous axis in the p-table array
10
+ */
11
+ columnIdx: number;
12
+ /**
13
+ * Index of the corresponding heterogeneous axis in the p-table array
14
+ */
15
+ axisIdx: number;
16
+ };
17
+
18
+ function getHeterogeneousAxes(spec: PColumnSpec): AxisSpec[] {
19
+ return spec.axesSpec.filter((axSpec) => axSpec.annotations?.['pl7.app/axisNature'] === 'heterogeneous');
20
+ }
21
+
22
+ /**
23
+ * Returns a list of columns containing heterogeneous axis (if a column contains multiple, all will be returned)
24
+ * @param specs p-table column specs
25
+ * @param indices indices of columns of interest in the spec array
26
+ * @returns list of columns
27
+ */
28
+ export function getHeterogeneousColumns(specs: PTableColumnSpec[], indices: number[]): HeterogeneousColumnInfo[] {
29
+ const cols: HeterogeneousColumnInfo[] = [];
30
+
31
+ // check we have heterogeneous axis, which we need to transpose
32
+ for (const i of indices) {
33
+ if (specs[i].type === 'column') {
34
+ const hAxes = getHeterogeneousAxes(specs[i].spec);
35
+
36
+ for (const hAx of hAxes) {
37
+ if (hAx.type !== 'String') {
38
+ console.warn('heterogeneous axis with string type expected, got', hAx, 'skipping');
39
+ continue;
40
+ }
41
+
42
+ const axisId = getAxisId(hAx);
43
+ const axisIdx = specs.findIndex((s) => lodash.isEqual(s.id, axisId));
44
+
45
+ if (axisIdx === -1) {
46
+ console.error('axis not found', i, axisId, specs);
47
+ throw Error('axis not found');
48
+ }
49
+
50
+ cols.push({ columnIdx: i, axisIdx: axisIdx });
51
+ }
52
+ }
53
+ }
54
+
55
+ return cols;
56
+ }
57
+
58
+ // auxiliary function to get a field for i-th axis-value
59
+ const hColumnField = (originalLength: number, i: number) => (originalLength + i).toString();
60
+
61
+ /**
62
+ * Calculate GridOptions for p-table data source type
63
+ *
64
+ * @param hColumns heterogeneous columns list
65
+ * @param shape table shape
66
+ * @param columnDefs initial column definitions (with h-cols inside and no additional columns)
67
+ * @param data data array (including initial columns)
68
+ * @param fields fields corresponding to initial column defs
69
+ * @param indices indices in the original specs array
70
+ */
71
+ export function updatePFrameGridOptionsHeterogeneousAxes(
72
+ hColumns: HeterogeneousColumnInfo[],
73
+ shape: PTableShape,
74
+ columnDefs: ColDef[],
75
+ data: PTableVector[],
76
+ fields: number[],
77
+ indices: number[],
78
+ ): {
79
+ columnDefs: ColDef[];
80
+ datasource?: IDatasource;
81
+ rowModelType: RowModelType;
82
+ rowData?: unknown[];
83
+ } {
84
+ if (hColumns.length > 1) {
85
+ throw Error('hColumns.length > 1 is not supported');
86
+ }
87
+
88
+ const hColumn = hColumns[0];
89
+
90
+ // recalculate indices of h-cols to the positions in the resulting data
91
+ // index of axis & column in indices array
92
+ let axisIdx: number = -1;
93
+ let columnIdx: number = -1;
94
+ for (let i = 0; i < indices.length; ++i) {
95
+ if (indices[i] === hColumn.axisIdx) {
96
+ axisIdx = i;
97
+ }
98
+ if (indices[i] === hColumn.columnIdx) {
99
+ columnIdx = i;
100
+ }
101
+ }
102
+
103
+ // columns to add into the data table definition
104
+ const hAxisValues: string[] = (() => {
105
+ // only string data is supported
106
+ const allValues = data[axisIdx].data as PVectorDataString;
107
+ const notNull = allValues.filter((v) => v !== null);
108
+ return Array.from(new Set(notNull));
109
+ })();
110
+
111
+ // sort for canonicalization
112
+ hAxisValues.sort();
113
+
114
+ // remove heterogeneous column from the column defs
115
+ if (columnIdx > axisIdx) {
116
+ columnDefs.splice(columnIdx, 1);
117
+ columnDefs.splice(axisIdx, 1);
118
+ } else {
119
+ columnDefs.splice(axisIdx, 1);
120
+ columnDefs.splice(columnIdx, 1);
121
+ }
122
+
123
+ // calculate row data
124
+ const rowData = calculateRowData(fields, data, axisIdx, columnIdx, hAxisValues, shape.rows);
125
+
126
+ // add additional column definitions
127
+ for (let i = 0; i < hAxisValues.length; ++i) {
128
+ const header = hAxisValues[i];
129
+ const id = header + '@' + i; // at least add i to avoid name clash with other columns
130
+
131
+ columnDefs.push({
132
+ colId: id,
133
+ field: hColumnField(data.length, i),
134
+ headerName: header,
135
+ valueFormatter: defaultValueFormatter,
136
+ });
137
+ }
138
+
139
+ return {
140
+ rowModelType: 'clientSide',
141
+ columnDefs,
142
+ rowData,
143
+ };
144
+ }
145
+
146
+ /** calculate row data for the table with heterogeneous axis */
147
+ function calculateRowData(
148
+ fields: number[],
149
+ data: PTableVector[],
150
+ axisIdx: number,
151
+ columnIdx: number,
152
+ hAxisValues: string[],
153
+ nRows: number,
154
+ ): unknown[] {
155
+ const uniqueRowsMap: Map<string, Record<string, any>> = new Map();
156
+
157
+ // transposed iRow
158
+ let iRowT = 0;
159
+ for (let iRow = 0; iRow < nRows; iRow++) {
160
+ const rowPart: Record<string, any> = {};
161
+ let k = 0;
162
+ for (let iCol = 0; iCol < data.length; ++iCol) {
163
+ if (iCol === axisIdx || iCol === columnIdx) continue;
164
+ const field = fields[iCol].toString();
165
+ rowPart[field] = data[iCol].data[iRow];
166
+ ++k;
167
+ }
168
+
169
+ let row: Record<string, any>;
170
+ const id = canonicalize(Object.values(rowPart)) ?? '';
171
+ if (!uniqueRowsMap.has(id)) {
172
+ uniqueRowsMap.set(id, rowPart);
173
+ row = rowPart;
174
+ row['id'] = iRowT.toString();
175
+ iRowT++;
176
+ } else {
177
+ row = uniqueRowsMap.get(id)!;
178
+ }
179
+
180
+ const idx = hAxisValues.indexOf(data[axisIdx].data[iRow] as string);
181
+ if (idx === -1) {
182
+ throw Error('index not found');
183
+ }
184
+
185
+ const field = hColumnField(data.length, idx);
186
+ row[field] = data[columnIdx].data[iRow];
187
+ }
188
+
189
+ const result = Array.from(uniqueRowsMap.values());
190
+
191
+ return result;
192
+ }
@@ -1,22 +1,27 @@
1
- import type { ColDef, GridApi, IDatasource, IGetRowsParams } from '@ag-grid-community/core';
2
- import type { PFrameHandle, AxisId, PColumnIdAndSpec, JoinEntry, PObjectId } from '@platforma-sdk/model';
1
+ import type { ColDef, GridApi, IDatasource, IGetRowsParams, RowModelType } from '@ag-grid-community/core';
3
2
  import {
4
- type ValueType,
5
- type PValue,
3
+ type PColumnSpec,
6
4
  type PFrameDriver,
7
5
  type PTableColumnId,
8
6
  type PTableColumnSpec,
9
7
  type PTableHandle,
10
8
  type PTableVector,
11
- type PColumnSpec,
9
+ type PValue,
10
+ type ValueType,
11
+ AxisId,
12
12
  getAxesId,
13
- isValueNA,
14
13
  isValueAbsent,
14
+ isValueNA,
15
+ JoinEntry,
15
16
  mapJoinEntry,
17
+ PColumnIdAndSpec,
18
+ PFrameHandle,
19
+ PObjectId,
16
20
  } from '@platforma-sdk/model';
17
- import * as lodash from 'lodash';
18
21
  import canonicalize from 'canonicalize';
22
+ import * as lodash from 'lodash';
19
23
  import { type PlDataTableSheet } from '../types';
24
+ import { getHeterogeneousColumns, updatePFrameGridOptionsHeterogeneousAxes } from './table-source-heterogeneous';
20
25
 
21
26
  /**
22
27
  * Generate unique colId based on the column spec.
@@ -35,6 +40,17 @@ export function parseColId(str: string) {
35
40
  return JSON.parse(str) as PTableColumnId;
36
41
  }
37
42
 
43
+ export const defaultValueFormatter = (value: any) => {
44
+ if (!value) {
45
+ return 'ERROR';
46
+ } else if (value.value === undefined) {
47
+ return 'NULL';
48
+ } else if (value.value === null) {
49
+ return 'NA';
50
+ } else {
51
+ return value.value.toString();
52
+ }
53
+ };
38
54
  /**
39
55
  * Calculates column definition for a given p-table column
40
56
  */
@@ -44,17 +60,7 @@ function getColDef(iCol: number, spec: PTableColumnSpec): ColDef {
44
60
  field: iCol.toString(),
45
61
  headerName: spec.spec.annotations?.['pl7.app/label']?.trim() ?? 'Unlabeled ' + spec.type + ' ' + iCol.toString(),
46
62
  lockPosition: spec.type === 'axis',
47
- valueFormatter: (value) => {
48
- if (!value) {
49
- return 'ERROR';
50
- } else if (value.value === undefined) {
51
- return 'NULL';
52
- } else if (value.value === null) {
53
- return 'NA';
54
- } else {
55
- return value.value.toString();
56
- }
57
- },
63
+ valueFormatter: defaultValueFormatter,
58
64
  cellDataType: ((valueType: ValueType) => {
59
65
  switch (valueType) {
60
66
  case 'Int':
@@ -255,7 +261,7 @@ function columns2rows(fields: number[], columns: PTableVector[]): unknown[] {
255
261
  }
256
262
 
257
263
  // generate ID based on the axes information
258
- row['id'] = JSON.stringify(index);
264
+ row['id'] = iRow.toString();
259
265
 
260
266
  rowData.push(row);
261
267
  }
@@ -263,6 +269,8 @@ function columns2rows(fields: number[], columns: PTableVector[]): unknown[] {
263
269
  return rowData;
264
270
  }
265
271
 
272
+ const isLabelColumn = (col: PTableColumnSpec) => col.type === 'column' && col.spec.axesSpec.length === 1 && col.spec.name === 'pl7.app/label';
273
+
266
274
  /**
267
275
  * Calculate GridOptions for p-table data source type
268
276
  */
@@ -273,31 +281,55 @@ export async function updatePFrameGridOptions(
273
281
  sheets: PlDataTableSheet[],
274
282
  ): Promise<{
275
283
  columnDefs: ColDef[];
276
- datasource: IDatasource;
284
+ datasource?: IDatasource;
285
+ rowModelType: RowModelType;
286
+ rowData?: unknown[];
277
287
  }> {
278
288
  const specs = await pfDriver.getSpec(pt);
289
+
290
+ // column indices in the specs array that we are going to process
279
291
  const indices = [...specs.keys()].filter(
280
292
  (i) => !lodash.find(sheets, (sheet) => lodash.isEqual(sheet.axis, specs[i].id) || lodash.isEqual(sheet.column, specs[i].id)),
281
293
  );
282
294
  const fields = lodash.cloneDeep(indices);
283
295
 
296
+ // get columns with heterogeneous axes
297
+ const hColumns = getHeterogeneousColumns(specs, indices);
298
+
299
+ // process label columns
284
300
  for (let i = indices.length - 1; i >= 0; --i) {
285
301
  const idx = indices[i];
286
- if (!(specs[idx].type === 'column' && specs[idx].spec.axesSpec.length === 1 && specs[idx].spec.name === 'pl7.app/label')) continue;
302
+ if (!isLabelColumn(specs[idx])) continue;
287
303
 
288
304
  const axisId = getAxesId((specs[idx].spec as PColumnSpec).axesSpec)[0];
289
305
  const axisIdx = lodash.findIndex(indices, (idx) => lodash.isEqual(specs[idx].id, axisId));
290
306
  if (axisIdx === -1) continue;
291
307
 
308
+ // replace in h-columns
292
309
  indices[axisIdx] = idx;
310
+ for (const hCol of hColumns) {
311
+ if (hCol.axisIdx === idx) {
312
+ hCol.axisIdx = axisIdx;
313
+ }
314
+ }
315
+
316
+ // remove original axis
293
317
  indices.splice(i, 1);
294
318
  fields.splice(i, 1);
295
319
  }
296
320
 
297
- const columnDefs = fields.map((i) => getColDef(i, specs[i]));
298
-
299
321
  const ptShape = await pfDriver.getShape(pt);
300
322
  const rowCount = ptShape.rows;
323
+ const columnDefs = fields.map((i) => getColDef(i, specs[i]));
324
+
325
+ if (hColumns.length > 1) {
326
+ console.warn('Currently, only one heterogeneous axis is supported in the table, got', hColumns.length, ' transposition will not be applied.');
327
+ }
328
+
329
+ if (hColumns.length === 1) {
330
+ // return data
331
+ return updatePFrameGridOptionsHeterogeneousAxes(hColumns, ptShape, columnDefs, await pfDriver.getData(pt, indices), fields, indices);
332
+ }
301
333
 
302
334
  let lastParams: IGetRowsParams | undefined = undefined;
303
335
  const datasource = {
@@ -340,6 +372,7 @@ export async function updatePFrameGridOptions(
340
372
  } satisfies IDatasource;
341
373
 
342
374
  return {
375
+ rowModelType: 'infinite',
343
376
  columnDefs,
344
377
  datasource,
345
378
  };