@platforma-sdk/ui-vue 1.40.0 → 1.40.2

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 (74) hide show
  1. package/.turbo/turbo-build.log +27 -30
  2. package/.turbo/turbo-type-check.log +1 -1
  3. package/CHANGELOG.md +15 -0
  4. package/dist/AgGridVue/selection.d.ts.map +1 -1
  5. package/dist/AgGridVue/selection.js +11 -10
  6. package/dist/AgGridVue/selection.js.map +1 -1
  7. package/dist/AgGridVue/useAgGridOptions.js +53 -54
  8. package/dist/AgGridVue/useAgGridOptions.js.map +1 -1
  9. package/dist/_virtual/_commonjsHelpers.js +3 -5
  10. package/dist/_virtual/_commonjsHelpers.js.map +1 -1
  11. package/dist/components/PlAgDataTable/PlAgDataTableV2.vue.d.ts +4 -1
  12. package/dist/components/PlAgDataTable/PlAgDataTableV2.vue.d.ts.map +1 -1
  13. package/dist/components/PlAgDataTable/PlAgDataTableV2.vue2.js +184 -155
  14. package/dist/components/PlAgDataTable/PlAgDataTableV2.vue2.js.map +1 -1
  15. package/dist/components/PlAgDataTable/PlAgRowCount.vue.js +6 -7
  16. package/dist/components/PlAgDataTable/PlAgRowCount.vue.js.map +1 -1
  17. package/dist/components/PlAgDataTable/sources/focus-row.d.ts +5 -6
  18. package/dist/components/PlAgDataTable/sources/focus-row.d.ts.map +1 -1
  19. package/dist/components/PlAgDataTable/sources/focus-row.js +23 -27
  20. package/dist/components/PlAgDataTable/sources/focus-row.js.map +1 -1
  21. package/dist/components/PlAgDataTable/sources/menu-items.js +1 -1
  22. package/dist/components/PlAgDataTable/sources/menu-items.js.map +1 -1
  23. package/dist/components/PlAgDataTable/sources/table-source-v2.d.ts +13 -6
  24. package/dist/components/PlAgDataTable/sources/table-source-v2.d.ts.map +1 -1
  25. package/dist/components/PlAgDataTable/sources/table-source-v2.js +121 -113
  26. package/dist/components/PlAgDataTable/sources/table-source-v2.js.map +1 -1
  27. package/dist/components/PlAgDataTable/sources/table-state-v2.d.ts +1 -1
  28. package/dist/components/PlAgDataTable/sources/table-state-v2.d.ts.map +1 -1
  29. package/dist/components/PlAgDataTable/sources/table-state-v2.js +48 -49
  30. package/dist/components/PlAgDataTable/sources/table-state-v2.js.map +1 -1
  31. package/dist/components/PlAgDataTable/types.d.ts +18 -5
  32. package/dist/components/PlAgDataTable/types.d.ts.map +1 -1
  33. package/dist/components/PlAgDataTable/types.js +26 -25
  34. package/dist/components/PlAgDataTable/types.js.map +1 -1
  35. package/dist/components/PlAgRowNumCheckbox/PlAgRowNumCheckbox.vue.js +9 -10
  36. package/dist/components/PlAgRowNumCheckbox/PlAgRowNumCheckbox.vue.js.map +1 -1
  37. package/dist/components/PlAgRowNumHeader.vue.js +2 -3
  38. package/dist/components/PlAgRowNumHeader.vue.js.map +1 -1
  39. package/dist/lib/ui/uikit/dist/components/PlAutocomplete/PlAutocomplete.vue.js +1 -1
  40. package/dist/lib/ui/uikit/dist/components/PlDropdown/PlDropdown.vue.js +1 -1
  41. package/dist/lib/ui/uikit/dist/components/PlDropdownLegacy/PlDropdownLegacy.vue.js +1 -1
  42. package/dist/lib/ui/uikit/dist/components/PlDropdownMulti/PlDropdownMulti.vue.js +1 -1
  43. package/dist/lib/ui/uikit/dist/components/PlFileInput/PlFileInput.vue.js +1 -1
  44. package/dist/lib/ui/uikit/dist/components/PlTextArea/PlTextArea.vue.js +1 -1
  45. package/dist/lib/ui/uikit/dist/components/PlTextField/PlTextField.vue.js +1 -1
  46. package/dist/lib/ui/uikit/dist/composition/computedCached.js +13 -14
  47. package/dist/lib/ui/uikit/dist/composition/computedCached.js.map +1 -1
  48. package/dist/lib/ui/uikit/dist/composition/watchCached.js +15 -15
  49. package/dist/lib/ui/uikit/dist/composition/watchCached.js.map +1 -1
  50. package/dist/lib/ui/uikit/dist/generated/components/svg/images/{SvgRequired.vue.js → SvgRequired.vue2.js} +1 -1
  51. package/dist/lib/ui/uikit/dist/generated/components/svg/images/SvgRequired.vue2.js.map +1 -0
  52. package/dist/lib/ui/uikit/dist/sdk/model/dist/index.js +1 -1
  53. package/dist/lib/ui/uikit/dist/sdk/model/dist/index.js.map +1 -1
  54. package/dist/lib.js +256 -257
  55. package/dist/sdk/model/dist/index.js +111 -111
  56. package/dist/sdk/model/dist/index.js.map +1 -1
  57. package/package.json +5 -7
  58. package/src/AgGridVue/selection.ts +12 -10
  59. package/src/components/PlAgDataTable/PlAgDataTableV2.vue +66 -27
  60. package/src/components/PlAgDataTable/sources/focus-row.ts +26 -33
  61. package/src/components/PlAgDataTable/sources/menu-items.ts +1 -1
  62. package/src/components/PlAgDataTable/sources/table-source-v2.ts +75 -47
  63. package/src/components/PlAgDataTable/sources/table-state-v2.ts +30 -33
  64. package/src/components/PlAgDataTable/types.ts +23 -7
  65. package/dist/_virtual/lodash.js +0 -6
  66. package/dist/_virtual/lodash.js.map +0 -1
  67. package/dist/_virtual/lodash2.js +0 -5
  68. package/dist/_virtual/lodash2.js.map +0 -1
  69. package/dist/components/PlAgDataTable/sources/file-source.d.ts +0 -7
  70. package/dist/components/PlAgDataTable/sources/file-source.d.ts.map +0 -1
  71. package/dist/lib/ui/uikit/dist/generated/components/svg/images/SvgRequired.vue.js.map +0 -1
  72. package/dist/node_modules/.pnpm/lodash@4.17.21/node_modules/lodash/lodash.js +0 -3678
  73. package/dist/node_modules/.pnpm/lodash@4.17.21/node_modules/lodash/lodash.js.map +0 -1
  74. package/src/components/PlAgDataTable/sources/file-source.ts +0 -23
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@platforma-sdk/ui-vue",
3
- "version": "1.40.0",
3
+ "version": "1.40.2",
4
4
  "type": "module",
5
5
  "main": "dist/lib.js",
6
6
  "styles": "dist/lib.js",
@@ -12,7 +12,6 @@
12
12
  "./styles": "./dist/lib.js"
13
13
  },
14
14
  "dependencies": {
15
- "@types/lodash": "^4.17.16",
16
15
  "@types/node": "~20.16.15",
17
16
  "@types/semver": "^7.7.0",
18
17
  "@types/sortablejs": "^1.15.6",
@@ -27,11 +26,10 @@
27
26
  "@vueuse/integrations": "^13.3.0",
28
27
  "sortablejs": "^1.15.6",
29
28
  "d3-format": "^3.1.0",
30
- "lodash": "^4.17.17",
31
29
  "zod": "~3.23.8",
30
+ "@platforma-sdk/model": "~1.40.1",
32
31
  "@milaboratories/biowasm-tools": "^1.1.0",
33
- "@milaboratories/uikit": "2.3.10",
34
- "@platforma-sdk/model": "~1.40.0"
32
+ "@milaboratories/uikit": "2.3.11"
35
33
  },
36
34
  "devDependencies": {
37
35
  "happy-dom": "^15.11.7",
@@ -44,9 +42,9 @@
44
42
  "vitest": "^2.1.9",
45
43
  "vue-tsc": "^2.2.10",
46
44
  "yarpm": "^1.2.0",
47
- "@milaboratories/ts-configs": "1.0.4",
48
- "@milaboratories/build-configs": "1.0.4",
49
45
  "@milaboratories/eslint-config": "^1.0.4",
46
+ "@milaboratories/build-configs": "1.0.4",
47
+ "@milaboratories/ts-configs": "1.0.4",
50
48
  "@milaboratories/helpers": "^1.6.17"
51
49
  },
52
50
  "scripts": {
@@ -6,15 +6,17 @@ import type { GridApi } from 'ag-grid-enterprise';
6
6
  * @returns The number of selected rows.
7
7
  */
8
8
  export function getSelectedRowsCount(gridApi: GridApi) {
9
- const rowModel = gridApi.getGridOption('rowModelType');
10
- switch (rowModel) {
11
- case 'clientSide': {
12
- return gridApi.getSelectedRows().length;
13
- }
14
- case 'serverSide': {
15
- const state = gridApi.getServerSideSelectionState();
16
- // `state.selectAll` flag is ignored as we assume `selectAll` was used to select all rows
17
- return state?.toggledNodes?.length ?? 0;
9
+ if (!gridApi.getGridOption('loading')) {
10
+ const rowModel = gridApi.getGridOption('rowModelType');
11
+ switch (rowModel) {
12
+ case 'clientSide': {
13
+ return gridApi.getSelectedRows().length;
14
+ }
15
+ case 'serverSide': {
16
+ const state = gridApi.getServerSideSelectionState();
17
+ // `state.selectAll` flag is ignored as we assume `selectAll` was used to select all rows
18
+ return state?.toggledNodes?.length ?? 0;
19
+ }
18
20
  }
19
21
  }
20
22
  return 0;
@@ -69,7 +71,7 @@ export function deselectAll(gridApi: GridApi) {
69
71
  * @returns The total number of rows.
70
72
  */
71
73
  export function getTotalRowsCount(gridApi: GridApi) {
72
- return gridApi.getDisplayedRowCount();
74
+ return gridApi.getGridOption('loading') ? 0 : gridApi.getDisplayedRowCount();
73
75
  }
74
76
 
75
77
  /**
@@ -4,11 +4,12 @@ import {
4
4
  } from '@milaboratories/helpers';
5
5
  import type {
6
6
  AxisId,
7
+ AxisSpec,
7
8
  PlDataTableGridStateCore,
8
9
  PlDataTableStateV2,
9
10
  PlSelectionModel,
11
+ PlTableColumnIdJson,
10
12
  PTableColumnSpec,
11
- PTableColumnSpecJson,
12
13
  PTableKey,
13
14
  } from '@platforma-sdk/model';
14
15
  import {
@@ -49,7 +50,6 @@ import PlAgDataTableSheets from './PlAgDataTableSheets.vue';
49
50
  import {
50
51
  focusRow,
51
52
  makeOnceTracker,
52
- trackFirstDataRendered,
53
53
  } from './sources/focus-row';
54
54
  import {
55
55
  autoSizeRowNumberColumn,
@@ -59,7 +59,6 @@ import type {
59
59
  PlAgCellButtonAxisParams,
60
60
  } from './sources/table-source-v2';
61
61
  import {
62
- makeRowId,
63
62
  calculateGridOptions,
64
63
  } from './sources/table-source-v2';
65
64
  import type {
@@ -69,7 +68,8 @@ import type {
69
68
  PlAgOverlayNoRowsParams,
70
69
  PlDataTableSettingsV2,
71
70
  PlDataTableSheetsSettings,
72
- PTableKeyJson,
71
+ PlTableLabeledSelectionModel,
72
+ PlTableRowIdJson,
73
73
  } from './types';
74
74
  import {
75
75
  useTableState,
@@ -82,6 +82,7 @@ const tableState = defineModel<PlDataTableStateV2>({
82
82
  required: true,
83
83
  });
84
84
  const selection = defineModel<PlSelectionModel>('selection');
85
+ const selectionLabeled = defineModel<PlTableLabeledSelectionModel>('selectionLabeled');
85
86
  const props = defineProps<{
86
87
  /** Required component settings */
87
88
  settings: Readonly<PlDataTableSettingsV2>;
@@ -183,10 +184,10 @@ const firstDataRenderedTracker = makeOnceTracker<GridApi<PlAgDataTableV2Row>>();
183
184
  const gridOptions = shallowRef<GridOptions<PlAgDataTableV2Row>>({
184
185
  animateRows: false,
185
186
  suppressColumnMoveAnimation: true,
186
- cellSelection: selection.value === undefined,
187
+ cellSelection: !selection.value && !selectionLabeled.value,
187
188
  initialState: gridState.value,
188
189
  autoSizeStrategy: { type: 'fitCellContents' },
189
- rowSelection: selection.value !== undefined
190
+ rowSelection: selection.value || selectionLabeled.value
190
191
  ? {
191
192
  mode: 'multiRow',
192
193
  selectAll: 'all',
@@ -197,14 +198,18 @@ const gridOptions = shallowRef<GridOptions<PlAgDataTableV2Row>>({
197
198
  }
198
199
  : undefined,
199
200
  onSelectionChanged: (event) => {
201
+ const state = event.api.getServerSideSelectionState();
200
202
  if (selection.value) {
201
- const state = event.api.getServerSideSelectionState();
202
- const selectedKeys = state?.toggledNodes?.map((nodeId) => parseJson(nodeId as PTableKeyJson)) ?? [];
203
+ const selectedKeys = state?.toggledNodes?.map((nodeId) => parseJson(nodeId as PlTableRowIdJson).axesKey) ?? [];
203
204
  selection.value = { ...selection.value, selectedKeys };
204
205
  }
206
+ if (selectionLabeled.value) {
207
+ const selectedLabeledKeys = state?.toggledNodes?.map((nodeId) => parseJson(nodeId as PlTableRowIdJson).labeled) ?? [];
208
+ selectionLabeled.value = { ...selectionLabeled.value, selectedLabeledKeys };
209
+ }
205
210
  },
206
211
  onRowDoubleClicked: (event) => {
207
- if (event.data && event.data.key) emit('rowDoubleClicked', event.data.key);
212
+ if (event.data && event.data.axesKey) emit('rowDoubleClicked', event.data.axesKey);
208
213
  },
209
214
  defaultColDef: {
210
215
  suppressHeaderMenuButton: true,
@@ -247,7 +252,6 @@ const gridOptions = shallowRef<GridOptions<PlAgDataTableV2Row>>({
247
252
  },
248
253
  onGridReady: (event) => {
249
254
  const api = event.api;
250
- trackFirstDataRendered(api, firstDataRenderedTracker);
251
255
  autoSizeRowNumberColumn(api);
252
256
  const setGridOption = (
253
257
  key: ManagedGridOptionKey,
@@ -300,16 +304,16 @@ const gridOptions = shallowRef<GridOptions<PlAgDataTableV2Row>>({
300
304
  function makePartialState(state: GridState): PlDataTableGridStateCore {
301
305
  return {
302
306
  columnOrder: state.columnOrder as {
303
- orderedColIds: PTableColumnSpecJson[];
307
+ orderedColIds: PlTableColumnIdJson[];
304
308
  } | undefined,
305
309
  sort: state.sort as {
306
310
  sortModel: {
307
- colId: PTableColumnSpecJson;
311
+ colId: PlTableColumnIdJson;
308
312
  sort: 'asc' | 'desc';
309
313
  }[];
310
314
  } | undefined,
311
315
  columnVisibility: state.columnVisibility as {
312
- hiddenColIds: PTableColumnSpecJson[];
316
+ hiddenColIds: PlTableColumnIdJson[];
313
317
  } | undefined,
314
318
  };
315
319
  };
@@ -362,7 +366,10 @@ watch(
362
366
  );
363
367
 
364
368
  defineExpose<PlAgDataTableV2Controller>({
365
- focusRow: (rowKey) => focusRow(makeRowId(rowKey), firstDataRenderedTracker),
369
+ focusRow: (rowKey) => focusRow(
370
+ (row) => isJsonEqual(row.data?.axesKey, rowKey),
371
+ firstDataRenderedTracker,
372
+ ),
366
373
  });
367
374
 
368
375
  // Propagate columns for filter component
@@ -378,6 +385,12 @@ watchCached(
378
385
  selectedKeys: [],
379
386
  };
380
387
  }
388
+ if (selectionLabeled.value) {
389
+ selectionLabeled.value = {
390
+ spec: [],
391
+ selectedLabeledKeys: [],
392
+ };
393
+ }
381
394
  } else {
382
395
  const isColDef = (def: ColDef | ColGroupDef): def is ColDef =>
383
396
  !('children' in def);
@@ -386,18 +399,35 @@ watchCached(
386
399
  .map((def) => def.colId)
387
400
  .filter((colId) => colId !== undefined)
388
401
  .filter((colId) => colId !== PlAgDataTableRowNumberColId)
389
- .map((colId) => parseJson(colId as PTableColumnSpecJson))
402
+ .map((colId) => parseJson(colId as PlTableColumnIdJson))
390
403
  ?? [];
391
- filterableColumns.value = columns;
404
+ filterableColumns.value = columns.map((column) => column.labeled);
392
405
  if (selection.value) {
393
406
  const axesSpec = columns
394
- .filter((column) => column.type === 'axis')
395
- .map((axis) => axis.spec);
407
+ .reduce((acc, column) => {
408
+ if (column.source.type === 'axis') {
409
+ acc.push(column.source.spec);
410
+ }
411
+ return acc;
412
+ }, [] as AxisSpec[]);
396
413
  selection.value = {
397
414
  ...selection.value,
398
415
  axesSpec,
399
416
  };
400
417
  }
418
+ if (selectionLabeled.value) {
419
+ const spec = columns
420
+ .reduce((acc, column) => {
421
+ if (column.source.type === 'axis') {
422
+ acc.push(column.labeled);
423
+ }
424
+ return acc;
425
+ }, [] as PTableColumnSpec[]);
426
+ selectionLabeled.value = {
427
+ ...selectionLabeled.value,
428
+ spec,
429
+ };
430
+ }
401
431
  }
402
432
  },
403
433
  { immediate: true },
@@ -417,10 +447,11 @@ watch(
417
447
  try {
418
448
  // Hide no rows overlay if it is shown, or else loading overlay will not be shown
419
449
  gridApi.hideOverlay();
450
+ firstDataRenderedTracker.reset();
420
451
 
421
452
  // No data source selected -> reset state to default
422
453
  if (!settings.sourceId) {
423
- return gridApi.updateGridOptions({
454
+ gridApi.updateGridOptions({
424
455
  loading: true,
425
456
  loadingOverlayComponentParams: {
426
457
  ...gridOptions.value.loadingOverlayComponentParams,
@@ -429,6 +460,13 @@ watch(
429
460
  columnDefs: undefined,
430
461
  serverSideDatasource: undefined,
431
462
  });
463
+ if (selection.value || selectionLabeled.value) {
464
+ gridApi.setServerSideSelectionState({
465
+ selectAll: false,
466
+ toggledNodes: [],
467
+ });
468
+ }
469
+ return;
432
470
  }
433
471
 
434
472
  // Data source changed -> show full page loader, clear selection
@@ -440,7 +478,7 @@ watch(
440
478
  notReady: false,
441
479
  } satisfies PlAgOverlayLoadingParams,
442
480
  });
443
- if (selection.value) {
481
+ if (selection.value || selectionLabeled.value) {
444
482
  gridApi.setServerSideSelectionState({
445
483
  selectAll: false,
446
484
  toggledNodes: [],
@@ -466,19 +504,20 @@ watch(
466
504
 
467
505
  // Model ready -> calculate new state
468
506
  const stateGeneration = generation.value;
469
- calculateGridOptions(
507
+ calculateGridOptions({
470
508
  generation,
471
- getRawPlatformaInstance().pFrameDriver,
472
- settings.model,
473
- settings.sheets ?? [],
474
- gridState.value.columnVisibility?.hiddenColIds,
475
- {
509
+ pfDriver: getRawPlatformaInstance().pFrameDriver,
510
+ model: settings.model,
511
+ sheets: settings.sheets ?? [],
512
+ track: firstDataRenderedTracker.track,
513
+ hiddenColIds: gridState.value.columnVisibility?.hiddenColIds,
514
+ cellButtonAxisParams: {
476
515
  showCellButtonForAxisId: props.showCellButtonForAxisId,
477
516
  cellButtonInvokeRowsOnDoubleClick:
478
517
  props.cellButtonInvokeRowsOnDoubleClick,
479
518
  trigger: (key?: PTableKey) => emit('cellButtonClicked', key),
480
519
  } satisfies PlAgCellButtonAxisParams,
481
- ).then((options) => {
520
+ }).then((options) => {
482
521
  if (gridApi.isDestroyed() || stateGeneration !== generation.value) return;
483
522
  return gridApi.updateGridOptions({
484
523
  ...options,
@@ -1,45 +1,35 @@
1
1
  import type { GridApi, IRowNode } from 'ag-grid-enterprise';
2
- import { nextTick, shallowRef, watchEffect } from 'vue';
2
+ import { nextTick, shallowRef, watch } from 'vue';
3
3
 
4
4
  export function makeOnceTracker<TContext = undefined>() {
5
5
  const state = shallowRef<[false, undefined] | [true, TContext]>([false, undefined]);
6
- const track = (ctx: TContext) => state.value = [true, ctx];
7
- const reset = () => state.value = [false, undefined];
6
+ const track = (ctx: TContext): void => {
7
+ state.value = [true, ctx];
8
+ };
9
+ const reset = (): void => {
10
+ state.value = [false, undefined];
11
+ };
8
12
  const onceTracked = (callback: (ctx: TContext) => void) => {
9
- const { stop } = watchEffect(() => {
10
- const [tracked, context] = state.value;
11
- if (tracked) {
12
- callback(context);
13
- stop();
14
- }
15
- });
16
- return stop;
13
+ const handle = watch(
14
+ state,
15
+ ([tracked, context]) => {
16
+ if (tracked) {
17
+ callback(context);
18
+ nextTick(() => handle.stop());
19
+ }
20
+ },
21
+ { immediate: true },
22
+ );
23
+ return handle;
17
24
  };
18
25
  return { track, reset, onceTracked };
19
26
  }
20
27
  export type OnceTracker<TContext = undefined> = ReturnType<typeof makeOnceTracker<TContext>>;
21
28
 
22
- export function trackFirstDataRendered(gridApi: GridApi, tracker: OnceTracker<GridApi>): void {
23
- gridApi.addEventListener('firstDataRendered', (event) => {
24
- if (event.api.getGridOption('rowModelType') === 'clientSide') {
25
- tracker.track(event.api);
26
- }
27
- });
28
- gridApi.addEventListener('modelUpdated', (event) => {
29
- if (event.api.getGridOption('rowModelType') === 'serverSide') {
30
- const groupState = event.api.getServerSideGroupLevelState();
31
- if (groupState && groupState.length > 0 && groupState[0].lastRowIndexKnown) {
32
- tracker.track(event.api);
33
- }
34
- }
35
- });
36
- gridApi.addEventListener('gridPreDestroyed', () => tracker.reset());
37
- }
38
-
39
- function ensureNodeVisible(api: GridApi, rowId: string): void {
29
+ function ensureNodeVisible<TData>(api: GridApi<TData>, selector: (row: IRowNode<TData>) => boolean): void {
40
30
  let rowIndex: number | null = null;
41
- const nodeSelector = (row: IRowNode): boolean => {
42
- if (row.id === rowId) {
31
+ const nodeSelector = (row: IRowNode<TData>): boolean => {
32
+ if (selector(row)) {
43
33
  rowIndex = row.rowIndex;
44
34
  return true;
45
35
  }
@@ -55,10 +45,13 @@ function ensureNodeVisible(api: GridApi, rowId: string): void {
55
45
  }
56
46
  };
57
47
 
58
- export async function focusRow(rowId: string, tracker: OnceTracker<GridApi>): Promise<void> {
48
+ export async function focusRow<TData>(
49
+ selector: (row: IRowNode<TData>) => boolean,
50
+ tracker: OnceTracker<GridApi<TData>>,
51
+ ): Promise<void> {
59
52
  return new Promise((resolve) => {
60
53
  nextTick(() => tracker.onceTracked((gridApi) => {
61
- ensureNodeVisible(gridApi, rowId);
54
+ ensureNodeVisible(gridApi, selector);
62
55
  resolve();
63
56
  }));
64
57
  });
@@ -2,8 +2,8 @@ import type { DefaultMenuItem, MenuItemDef } from 'ag-grid-enterprise';
2
2
 
3
3
  export function defaultMainMenuItems(): (MenuItemDef | DefaultMenuItem)[] {
4
4
  return [
5
- 'sortAscending',
6
5
  'sortDescending',
6
+ 'sortAscending',
7
7
  'separator',
8
8
  'pinSubMenu',
9
9
  ];
@@ -12,22 +12,24 @@ import {
12
12
  type PlDataTableModel,
13
13
  type PTableColumnSpec,
14
14
  type PTableKey,
15
+ type PlTableColumnId,
16
+ type PlTableColumnIdJson,
15
17
  isLabelColumn as isLabelColumnSpec,
16
18
  } from '@platforma-sdk/model';
17
19
  import type {
18
20
  CellStyle,
19
21
  ColDef,
22
+ GridApi,
20
23
  ICellRendererParams,
21
24
  IServerSideDatasource,
22
25
  IServerSideGetRowsParams,
23
26
  ManagedGridOptions,
24
27
  } from 'ag-grid-enterprise';
25
28
  import canonicalize from 'canonicalize';
26
- import * as lodash from 'lodash';
27
29
  import type { PlAgHeaderComponentParams, PlAgHeaderComponentType } from '../../PlAgColumnHeader';
28
30
  import { PlAgColumnHeader } from '../../PlAgColumnHeader';
29
31
  import { PlAgTextAndButtonCell } from '../../PlAgTextAndButtonCell';
30
- import type { PlAgDataTableV2Row, PTableKeyJson } from '../types';
32
+ import type { PlAgDataTableV2Row, PlTableRowId } from '../types';
31
33
  import {
32
34
  PTableHidden,
33
35
  } from './common';
@@ -35,6 +37,7 @@ import { defaultMainMenuItems } from './menu-items';
35
37
  import { makeRowNumberColDef, PlAgDataTableRowNumberColId } from './row-number';
36
38
  import { getColumnRenderingSpec } from './value-rendering';
37
39
  import type { Ref } from 'vue';
40
+ import { isJsonEqual } from '@milaboratories/helpers';
38
41
 
39
42
  export function isLabelColumn(column: PTableColumnSpec) {
40
43
  return column.type === 'column' && isLabelColumnSpec(column.spec);
@@ -45,16 +48,26 @@ function columns2rows(
45
48
  fields: number[],
46
49
  columns: PTableVector[],
47
50
  axes: number[],
51
+ labeledAxes: number[],
48
52
  resultMapping: number[],
49
53
  ): PlAgDataTableV2Row[] {
50
54
  const rowData: PlAgDataTableV2Row[] = [];
51
55
  for (let iRow = 0; iRow < columns[0].data.length; ++iRow) {
52
- const key = axes.map((iAxis) => {
56
+ const axesKey = axes.map((iAxis) => {
53
57
  return mapPTableValueToAxisKey(
54
58
  pTableValue(columns[resultMapping[iAxis]], iRow),
55
59
  );
56
60
  });
57
- const row: PlAgDataTableV2Row = { id: makeRowId(key), key };
61
+ const labeled = labeledAxes.map((iAxis) => {
62
+ return mapPTableValueToAxisKey(
63
+ pTableValue(columns[resultMapping[iAxis]], iRow),
64
+ );
65
+ });
66
+ const id = canonicalizeJson<PlTableRowId>({
67
+ axesKey,
68
+ labeled,
69
+ });
70
+ const row: PlAgDataTableV2Row = { id, axesKey };
58
71
  fields.forEach((field, iCol) => {
59
72
  row[field.toString() as `${number}`] = resultMapping[iCol] === -1
60
73
  ? PTableHidden
@@ -66,14 +79,23 @@ function columns2rows(
66
79
  }
67
80
 
68
81
  /** Calculate GridOptions for selected p-table data source */
69
- export async function calculateGridOptions(
70
- generation: Ref<number>,
71
- pfDriver: PFrameDriver,
72
- model: PlDataTableModel,
73
- sheets: PlDataTableSheet[],
74
- hiddenColIds?: string[],
75
- cellButtonAxisParams?: PlAgCellButtonAxisParams,
76
- ): Promise<Pick<ManagedGridOptions<PlAgDataTableV2Row>, 'columnDefs' | 'serverSideDatasource'>> {
82
+ export async function calculateGridOptions({
83
+ generation,
84
+ pfDriver,
85
+ model,
86
+ sheets,
87
+ track,
88
+ hiddenColIds,
89
+ cellButtonAxisParams,
90
+ }: {
91
+ generation: Ref<number>;
92
+ pfDriver: PFrameDriver;
93
+ model: PlDataTableModel;
94
+ sheets: PlDataTableSheet[];
95
+ track: (ctx: GridApi<PlAgDataTableV2Row>) => void;
96
+ hiddenColIds?: PlTableColumnIdJson[];
97
+ cellButtonAxisParams?: PlAgCellButtonAxisParams;
98
+ }): Promise<Pick<ManagedGridOptions<PlAgDataTableV2Row>, 'columnDefs' | 'serverSideDatasource'>> {
77
99
  const pt = model.visibleTableHandle;
78
100
  const specs = await pfDriver.getSpec(model.fullTableHandle);
79
101
  type SpecId = string;
@@ -99,14 +121,13 @@ export async function calculateGridOptions(
99
121
  const indices = [...specs.keys()]
100
122
  .filter(
101
123
  (i) =>
102
- !lodash.some(
103
- sheets,
124
+ !sheets.some(
104
125
  (sheet) =>
105
- lodash.isEqual(getAxisId(sheet.axis), specs[i].id)
126
+ isJsonEqual(getAxisId(sheet.axis), specs[i].id)
106
127
  || (specs[i].type === 'column'
107
128
  && specs[i].spec.name === 'pl7.app/label'
108
129
  && specs[i].spec.axesSpec.length === 1
109
- && lodash.isEqual(getAxisId(sheet.axis), getAxisId(specs[i].spec.axesSpec[0]))),
130
+ && isJsonEqual(getAxisId(sheet.axis), getAxisId(specs[i].spec.axesSpec[0]))),
110
131
  ),
111
132
  )
112
133
  .sort((a, b) => {
@@ -130,7 +151,7 @@ export async function calculateGridOptions(
130
151
 
131
152
  // axis of labels
132
153
  const axisId = getAxisId((specs[idx].spec as PColumnSpec).axesSpec[0]);
133
- const axisIdx = indices.findIndex((idx) => lodash.isEqual(specs[idx].id, axisId));
154
+ const axisIdx = indices.findIndex((idx) => isJsonEqual(specs[idx].id, axisId));
134
155
  if (axisIdx !== -1) {
135
156
  indices[axisIdx] = idx;
136
157
  } else {
@@ -144,7 +165,7 @@ export async function calculateGridOptions(
144
165
 
145
166
  const columnDefs: ColDef<PlAgDataTableV2Row>[] = [
146
167
  makeRowNumberColDef(),
147
- ...fields.map((i) => makeColDef(i, specs[i], hiddenColIds, cellButtonAxisParams)),
168
+ ...fields.map((field, index) => makeColDef(field, specs[field], specs[indices[index]], hiddenColIds, cellButtonAxisParams)),
148
169
  ];
149
170
 
150
171
  // mixing in axis indices
@@ -170,21 +191,24 @@ export async function calculateGridOptions(
170
191
 
171
192
  // Construct the `axes` array for key generation in `columns2rows`.
172
193
  // The key components should be ordered according to the display order of axis columns from the `fields` array.
173
- const axes: number[] = fields.filter((idx) => specs[idx].type === 'axis').map((idx) => {
174
- const r = allIndices.indexOf(idx);
175
- if (r === -1) {
176
- console.error(
177
- 'Key construction error: Original axis spec index from `fields` not found in `allIndices`.',
178
- {
179
- originalAxisSpecIdx: idx,
180
- },
181
- );
182
- throw new Error(
183
- `Assertion failed: Original axis spec index ${idx} (from fields) for key construction not found in allIndices.`,
184
- );
185
- }
186
- return r;
187
- });
194
+ const axes: number[] = fields
195
+ .filter((field) => specs[field].type === 'axis')
196
+ .map((field) => {
197
+ const r = allIndices.indexOf(field);
198
+ if (r === -1) {
199
+ throw new Error(
200
+ `Assertion failed: Original axis spec index ${field} (from fields) for key construction not found in allIndices.`,
201
+ );
202
+ }
203
+ return r;
204
+ });
205
+ const labeledAxes: number[] = fields
206
+ .reduce((acc, field, index) => {
207
+ if (specs[field].type === 'axis') {
208
+ acc.push(allIndices.indexOf(indices[index]));
209
+ }
210
+ return acc;
211
+ }, [] as number[]);
188
212
 
189
213
  const requestIndices: number[] = [];
190
214
  const resultMapping: number[] = [];
@@ -207,7 +231,7 @@ export async function calculateGridOptions(
207
231
  try {
208
232
  if (rowCount === -1) {
209
233
  const ptShape = await pfDriver.getShape(pt);
210
- if (stateGeneration !== generation.value) return params.fail();
234
+ if (stateGeneration !== generation.value || params.api.isDestroyed()) return params.fail();
211
235
  rowCount = ptShape.rows;
212
236
  }
213
237
 
@@ -221,7 +245,7 @@ export async function calculateGridOptions(
221
245
  }
222
246
 
223
247
  // If sort has changed - show skeletons instead of data
224
- if (lastParams && !lodash.isEqual(lastParams.request.sortModel, params.request.sortModel)) {
248
+ if (lastParams && !isJsonEqual(lastParams.request.sortModel, params.request.sortModel)) {
225
249
  return params.success({ rowData: [], rowCount });
226
250
  }
227
251
  lastParams = params;
@@ -235,8 +259,8 @@ export async function calculateGridOptions(
235
259
  offset: params.request.startRow,
236
260
  length,
237
261
  });
238
- if (stateGeneration !== generation.value) return params.fail();
239
- rowData = columns2rows(fields, data, axes, resultMapping);
262
+ if (stateGeneration !== generation.value || params.api.isDestroyed()) return params.fail();
263
+ rowData = columns2rows(fields, data, axes, labeledAxes, resultMapping);
240
264
  }
241
265
  }
242
266
 
@@ -247,10 +271,14 @@ export async function calculateGridOptions(
247
271
  );
248
272
  params.api.setGridOption('loading', false);
249
273
  } catch (error: unknown) {
250
- if (stateGeneration !== generation.value) return params.fail();
274
+ if (stateGeneration !== generation.value || params.api.isDestroyed()) return params.fail();
251
275
  params.api.setGridOption('loading', true);
252
276
  params.fail();
253
277
  console.trace(error);
278
+ } finally {
279
+ if (!params.api.isDestroyed()) {
280
+ track(params.api);
281
+ }
254
282
  }
255
283
  },
256
284
  };
@@ -273,10 +301,14 @@ export type PlAgCellButtonAxisParams = {
273
301
  export function makeColDef(
274
302
  iCol: number,
275
303
  spec: PTableColumnSpec,
276
- hiddenColIds: string[] | undefined,
304
+ labeledSpec: PTableColumnSpec,
305
+ hiddenColIds: PlTableColumnIdJson[] | undefined,
277
306
  cellButtonAxisParams?: PlAgCellButtonAxisParams,
278
307
  ): ColDef {
279
- const colId = canonicalizeJson<PTableColumnSpec>(spec);
308
+ const colId = canonicalizeJson<PlTableColumnId>({
309
+ source: spec,
310
+ labeled: labeledSpec,
311
+ });
280
312
  const valueType = spec.type === 'axis' ? spec.spec.type : spec.spec.valueType;
281
313
  const columnRenderingSpec = getColumnRenderingSpec(spec);
282
314
  const cellStyle: CellStyle = {};
@@ -293,7 +325,7 @@ export function makeColDef(
293
325
  mainMenuItems: defaultMainMenuItems,
294
326
  context: spec,
295
327
  field: iCol.toString(),
296
- headerName: spec.spec.annotations?.['pl7.app/label']?.trim() ?? 'Unlabeled ' + spec.type + ' ' + iCol.toString(),
328
+ headerName: labeledSpec.spec.annotations?.['pl7.app/label']?.trim() ?? 'Unlabeled ' + spec.type + ' ' + iCol.toString(),
297
329
  lockPosition: spec.type === 'axis',
298
330
  hide: hiddenColIds?.includes(colId) ?? isColumnOptional(spec.spec),
299
331
  valueFormatter: columnRenderingSpec.valueFormatter,
@@ -303,13 +335,13 @@ export function makeColDef(
303
335
  if (spec.type !== 'axis') return;
304
336
 
305
337
  const axisId = (params.colDef?.context as PTableColumnSpec)?.id as AxisId;
306
- if (lodash.isEqual(axisId, cellButtonAxisParams.showCellButtonForAxisId)) {
338
+ if (isJsonEqual(axisId, cellButtonAxisParams.showCellButtonForAxisId)) {
307
339
  return {
308
340
  component: PlAgTextAndButtonCell,
309
341
  params: {
310
342
  invokeRowsOnDoubleClick: cellButtonAxisParams.cellButtonInvokeRowsOnDoubleClick,
311
343
  onClick: (params: ICellRendererParams<PlAgDataTableV2Row>) => {
312
- cellButtonAxisParams.trigger(params.data?.key);
344
+ cellButtonAxisParams.trigger(params.data?.axesKey);
313
345
  },
314
346
  },
315
347
  };
@@ -349,7 +381,3 @@ export function makeColDef(
349
381
  })(),
350
382
  };
351
383
  }
352
-
353
- export function makeRowId(rowKey: PTableKey): PTableKeyJson {
354
- return canonicalizeJson(rowKey);
355
- }