@platforma-sdk/ui-vue 1.63.8 → 1.64.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 (43) hide show
  1. package/.turbo/turbo-build.log +25 -19
  2. package/.turbo/turbo-formatter$colon$check.log +2 -2
  3. package/.turbo/turbo-linter$colon$check.log +2 -2
  4. package/.turbo/turbo-types$colon$check.log +1 -1
  5. package/CHANGELOG.md +22 -0
  6. package/dist/components/PlAgDataTable/PlAgDataTableV2.js.map +1 -1
  7. package/dist/components/PlAgDataTable/PlAgDataTableV2.style.js.map +1 -1
  8. package/dist/components/PlAgDataTable/PlAgDataTableV2.vue.d.ts.map +1 -1
  9. package/dist/components/PlAgDataTable/PlAgDataTableV2.vue2.js +1 -1
  10. package/dist/components/PlAgDataTable/PlAgDataTableV2.vue2.js.map +1 -1
  11. package/dist/components/PlAgDataTable/compositions/useFilterableColumns.js +3 -3
  12. package/dist/components/PlAgDataTable/compositions/useFilterableColumns.js.map +1 -1
  13. package/dist/components/PlAgDataTable/sources/table-source-v2.d.ts.map +1 -1
  14. package/dist/components/PlAgDataTable/sources/table-source-v2.js +124 -90
  15. package/dist/components/PlAgDataTable/sources/table-source-v2.js.map +1 -1
  16. package/dist/components/PlAgDataTable/sources/table-state-v2.d.ts.map +1 -1
  17. package/dist/components/PlAgDataTable/sources/table-state-v2.js +1 -1
  18. package/dist/components/PlAgDataTable/sources/table-state-v2.js.map +1 -1
  19. package/dist/components/PlAgGridColumnManager/PlAgGridColumnManager.js.map +1 -1
  20. package/dist/components/PlAgGridColumnManager/PlAgGridColumnManager.style.js.map +1 -1
  21. package/dist/components/PlAgGridColumnManager/PlAgGridColumnManager.vue.d.ts.map +1 -1
  22. package/dist/components/PlAgGridColumnManager/PlAgGridColumnManager.vue2.js +37 -42
  23. package/dist/components/PlAgGridColumnManager/PlAgGridColumnManager.vue2.js.map +1 -1
  24. package/dist/components/PlAgGridColumnManager/useFilteredItems.d.ts +5 -5
  25. package/dist/components/PlAgGridColumnManager/useFilteredItems.d.ts.map +1 -1
  26. package/dist/components/PlAgGridColumnManager/useFilteredItems.js +2 -2
  27. package/dist/components/PlAgGridColumnManager/useFilteredItems.js.map +1 -1
  28. package/dist/components/PlAgGridColumnManager/useGridColumns.js +14 -0
  29. package/dist/components/PlAgGridColumnManager/useGridColumns.js.map +1 -0
  30. package/dist/components/PlTableFilters/PlTableFiltersV2.js.map +1 -1
  31. package/dist/components/PlTableFilters/PlTableFiltersV2.style.js.map +1 -1
  32. package/dist/components/PlTableFilters/PlTableFiltersV2.vue.d.ts.map +1 -1
  33. package/dist/components/PlTableFilters/PlTableFiltersV2.vue2.js +2 -8
  34. package/dist/components/PlTableFilters/PlTableFiltersV2.vue2.js.map +1 -1
  35. package/package.json +8 -8
  36. package/src/components/PlAgDataTable/PlAgDataTableV2.vue +2 -1
  37. package/src/components/PlAgDataTable/compositions/useFilterableColumns.ts +4 -4
  38. package/src/components/PlAgDataTable/sources/table-source-v2.ts +224 -126
  39. package/src/components/PlAgDataTable/sources/table-state-v2.ts +4 -10
  40. package/src/components/PlAgGridColumnManager/PlAgGridColumnManager.vue +17 -35
  41. package/src/components/PlAgGridColumnManager/useFilteredItems.ts +9 -11
  42. package/src/components/PlAgGridColumnManager/useGridColumns.ts +26 -0
  43. package/src/components/PlTableFilters/PlTableFiltersV2.vue +2 -6
@@ -1,13 +1,13 @@
1
1
  import type {
2
2
  AxesSpec,
3
3
  PTableColumnId,
4
+ PTableColumnSpecAxis,
4
5
  PTableColumnSpecColumn,
5
6
  PTableValue,
6
7
  } from "@platforma-sdk/model";
7
8
  import {
8
9
  canonicalizeJson,
9
10
  getAxisId,
10
- isColumnOptional,
11
11
  pTableValue,
12
12
  type PFrameDriver,
13
13
  type PlDataTableSheet,
@@ -19,12 +19,14 @@ import {
19
19
  type PlTableColumnId,
20
20
  type PlTableColumnIdJson,
21
21
  isLabelColumn as isLabelColumnSpec,
22
+ isLinkerColumn as isLinkerColumnSpec,
22
23
  isColumnHidden,
24
+ isColumnOptional,
23
25
  matchAxisId,
24
26
  readAnnotation,
27
+ readAnnotationJson,
25
28
  Annotation,
26
29
  ValueType,
27
- readAnnotationJson,
28
30
  getPTableColumnId,
29
31
  } from "@platforma-sdk/model";
30
32
  import type {
@@ -47,6 +49,7 @@ import { getColumnRenderingSpec } from "./value-rendering";
47
49
  import type { Ref } from "vue";
48
50
  import { isJsonEqual } from "@milaboratories/helpers";
49
51
  import type { DeferredCircular } from "./focus-row";
52
+ import { isNil, uniq } from "es-toolkit";
50
53
 
51
54
  export function isLabelColumn(column: PTableColumnSpec): column is PTableColumnSpecColumn {
52
55
  return column.type === "column" && isLabelColumnSpec(column.spec);
@@ -56,20 +59,24 @@ export function isLabelColumn(column: PTableColumnSpec): column is PTableColumnS
56
59
  function columns2rows(
57
60
  fields: number[],
58
61
  columns: PTableVector[],
59
- axes: number[],
60
- resultMapping: number[],
62
+ fieldResultMapping: number[],
63
+ axesResultIndices: number[],
61
64
  ): PlAgDataTableV2Row[] {
62
65
  const rowData: PlAgDataTableV2Row[] = [];
63
66
  for (let iRow = 0; iRow < columns[0].data.length; ++iRow) {
64
- const axesKey: PTableKey = axes.map((iAxis) =>
65
- pTableValue(columns[resultMapping[iAxis]], iRow),
66
- );
67
+ const axesKey: PTableKey = axesResultIndices.map((ri) => pTableValue(columns[ri], iRow));
67
68
  const id = canonicalizeJson<PlTableRowId>(axesKey);
68
- const row: PlAgDataTableV2Row = { id, axesKey };
69
- fields.forEach((field, iCol) => {
70
- row[field.toString() as `${number}`] =
71
- resultMapping[iCol] === -1 ? PTableHidden : pTableValue(columns[resultMapping[iCol]], iRow);
72
- });
69
+ const row = fields.reduce<PlAgDataTableV2Row>(
70
+ (acc, field, iCol) => {
71
+ acc[field.toString() as `${number}`] =
72
+ fieldResultMapping[iCol] === -1
73
+ ? PTableHidden
74
+ : pTableValue(columns[fieldResultMapping[iCol]], iRow);
75
+ return acc;
76
+ },
77
+ { id, axesKey },
78
+ );
79
+
73
80
  rowData.push(row);
74
81
  }
75
82
  return rowData;
@@ -98,129 +105,60 @@ export async function calculateGridOptions({
98
105
  }
99
106
  > {
100
107
  const stateGeneration = generation.value;
101
- const stateChangedError = new Error("table state generation changed");
102
108
 
103
109
  // get specs of the full table
104
- const specs = await pfDriver.getSpec(model.fullTableHandle);
105
- if (stateGeneration !== generation.value) throw stateChangedError;
106
-
107
- // get specs of the visible table (with hidden columns omitted)
108
- const pt = model.visibleTableHandle;
109
- const dataSpecs = await pfDriver.getSpec(pt);
110
- if (stateGeneration !== generation.value) throw stateChangedError;
111
-
112
- // create index mapping from full specs to visible subset (hidden columns would have -1)
113
- const specId = (spec: PTableColumnSpec) =>
114
- canonicalizeJson<PTableColumnId>(getPTableColumnId(spec));
115
- const dataSpecsMap = new Map(dataSpecs.entries().map(([i, spec]) => [specId(spec), i]));
116
- const specsToDataSpecsMapping = new Map(
117
- specs.entries().map(([i, spec]) => {
118
- const dataSpecIdx = dataSpecsMap.get(specId(spec)) ?? -1;
119
- if (dataSpecIdx === -1 && spec.type === "axis")
120
- throw new Error(`axis ${JSON.stringify(spec.spec)} not present in join result`);
121
- return [i, dataSpecIdx];
122
- }),
123
- );
110
+ const [tableSpecs, visibleTableSpecs] = await Promise.all([
111
+ pfDriver.getSpec(model.fullTableHandle),
112
+ pfDriver.getSpec(model.visibleTableHandle),
113
+ ]);
124
114
 
125
- // gether indices of columns that would be displayed in the table
115
+ if (stateGeneration !== generation.value) throw new Error("table state generation changed");
126
116
 
127
- const sheetAxesIds = sheets.map((sheet) => getAxisId(sheet.axis));
128
- const isPartitionedAxis = (axisId: AxisId) => sheetAxesIds.some((id) => matchAxisId(id, axisId));
117
+ // index mapping from full specs to visible subset (hidden columns → -1)
118
+ const specsToVisibleSpecsMapping = buildSpecsToVisibleSpecsMapping(tableSpecs, visibleTableSpecs);
129
119
 
130
- const labelColumns: { axisId: AxisId; labelColumnIdx: number }[] = [];
131
- const setLabelColumnIndex = (axisId: AxisId, labelColumnIdx: number) => {
132
- if (!labelColumns.some((info) => matchAxisId(info.axisId, axisId))) {
133
- labelColumns.push({ axisId, labelColumnIdx });
134
- } else {
135
- console.warn(`multiple label columns match axisId: ${JSON.stringify(axisId)}`);
136
- }
137
- };
138
- const getLabelColumnIndex = (axisId: AxisId) => {
139
- return labelColumns.find((info) => matchAxisId(info.axisId, axisId))?.labelColumnIdx ?? -1;
140
- };
120
+ const isPartitionedAxis = createPartitionedAxisPredicate(sheets);
141
121
 
142
- // filter out partitioned axes, label columns and hidden columns
143
- let indices = specs
144
- .entries()
145
- .filter(([i, spec]) => {
146
- switch (spec.type) {
147
- case "axis":
148
- return !isPartitionedAxis(spec.id);
149
- case "column":
150
- if (isLabelColumnSpec(spec.spec)) {
151
- const labeledAxisId = getAxisId(spec.spec.axesSpec[0]);
152
- if (!isPartitionedAxis(labeledAxisId)) {
153
- setLabelColumnIndex(labeledAxisId, i);
154
- }
155
- return false;
156
- }
157
- return !isColumnHidden(spec.spec);
158
- }
159
- })
160
- .map(([i]) => i)
161
- .toArray();
122
+ // label columns indexed by labeled axis (for axis→label replacement later)
123
+ const getLabelColumnIndex = collectLabelColumnsByAxis(tableSpecs, isPartitionedAxis);
162
124
 
163
- // order columns by priority
164
- indices.sort((a, b) => {
165
- if (specs[a].type !== specs[b].type) return specs[a].type === "axis" ? -1 : 1;
125
+ // displayable column indices ordered: axes first, then columns by OrderPriority
126
+ const fields = sortIndicesByTypeAndPriority(
127
+ selectDisplayableIndices(tableSpecs, isPartitionedAxis),
128
+ tableSpecs,
129
+ );
166
130
 
167
- const aPriority = readAnnotationJson(specs[a].spec, Annotation.Table.OrderPriority);
168
- const bPriority = readAnnotationJson(specs[b].spec, Annotation.Table.OrderPriority);
131
+ // same as fields, but each axis replaced by its label column index when available
132
+ const indices = replaceAxesWithLabelColumns(fields, tableSpecs, getLabelColumnIndex);
169
133
 
170
- if (aPriority === undefined) return bPriority === undefined ? 0 : 1;
171
- if (bPriority === undefined) return -1;
172
- return bPriority - aPriority;
173
- });
134
+ // default hidden columns derived from Optional annotation when no saved state
135
+ const resolvedHiddenColIds =
136
+ hiddenColIds ?? computeDefaultHiddenColIds(fields, indices, tableSpecs);
174
137
 
175
- // fields are indices of columns that would go to columnDefs
176
- const fields = [...indices];
177
- // replace axes with label columns
178
- indices = indices.map((i) => {
179
- const spec = specs[i];
180
- if (spec.type === "axis") {
181
- const labelColumnIdx = getLabelColumnIndex(spec.id);
182
- if (labelColumnIdx !== -1) {
183
- return labelColumnIdx;
184
- }
185
- }
186
- return i;
187
- });
188
138
  const columnDefs: ColDef<PlAgDataTableV2Row, PTableValue | PTableHidden>[] = [
189
139
  makeRowNumberColDef(),
190
140
  ...fields.map((field, index) =>
191
- makeColDef(field, specs[field], specs[indices[index]], hiddenColIds, cellButtonAxisParams),
141
+ makeColDef(
142
+ field,
143
+ tableSpecs[field],
144
+ tableSpecs[indices[index]],
145
+ resolvedHiddenColIds,
146
+ cellButtonAxisParams,
147
+ ),
192
148
  ),
193
149
  ];
194
150
 
195
- // mix in indices of skipped axes (axes that were partitioned or replaced with label columns)
196
- const axesSpec = specs
197
- .values()
198
- .filter((spec) => spec.type === "axis")
199
- .map((spec) => spec.spec)
200
- .toArray();
201
- const axes = axesSpec
202
- .keys()
203
- .map((i) => {
204
- let r = indices.indexOf(i);
205
- if (r === -1) {
206
- r = indices.length;
207
- indices.push(i);
208
- }
209
- return r;
210
- })
211
- .toArray();
151
+ // axes taken directly from visible table (always present as part of join)
152
+ const visibleAxes = collectVisibleAxes(visibleTableSpecs);
212
153
 
213
- const requestIndices: number[] = [];
214
- const resultMapping: number[] = [];
215
- indices.forEach((idx) => {
216
- const dataSpecIdx = specsToDataSpecsMapping.get(idx)!;
217
- if (dataSpecIdx !== -1) {
218
- resultMapping.push(requestIndices.length);
219
- requestIndices.push(dataSpecIdx);
220
- } else {
221
- resultMapping.push(-1);
222
- }
223
- });
154
+ // request indices: non-hidden display fields + visible axes for row selection keys.
155
+ // Axes replaced by label columns request label data (display); original axis values
156
+ // are fetched via visibleAxes (row keys).
157
+ const { requestIndices, fieldResultMapping, axesResultIndices } = buildRequestIndices(
158
+ indices,
159
+ specsToVisibleSpecsMapping,
160
+ visibleAxes.map(([i]) => i),
161
+ );
224
162
 
225
163
  let rowCount = -1;
226
164
  let lastParams: IServerSideGetRowsParams | undefined = undefined;
@@ -229,7 +167,7 @@ export async function calculateGridOptions({
229
167
  if (stateGeneration !== generation.value) return params.fail();
230
168
  try {
231
169
  if (rowCount === -1) {
232
- const ptShape = await pfDriver.getShape(pt);
170
+ const ptShape = await pfDriver.getShape(model.visibleTableHandle);
233
171
  if (stateGeneration !== generation.value || params.api.isDestroyed())
234
172
  return params.fail();
235
173
  rowCount = ptShape.rows;
@@ -259,13 +197,13 @@ export async function calculateGridOptions({
259
197
  ) {
260
198
  length = Math.min(rowCount, params.request.endRow) - params.request.startRow;
261
199
  if (length > 0) {
262
- const data = await pfDriver.getData(pt, requestIndices, {
200
+ const data = await pfDriver.getData(model.visibleTableHandle, requestIndices, {
263
201
  offset: params.request.startRow,
264
202
  length,
265
203
  });
266
204
  if (stateGeneration !== generation.value || params.api.isDestroyed())
267
205
  return params.fail();
268
- rowData = columns2rows(fields, data, axes, resultMapping);
206
+ rowData = columns2rows(fields, data, fieldResultMapping, axesResultIndices);
269
207
  }
270
208
  }
271
209
 
@@ -287,7 +225,7 @@ export async function calculateGridOptions({
287
225
  };
288
226
 
289
227
  return {
290
- axesSpec,
228
+ axesSpec: visibleAxes.map(([, { spec }]) => spec),
291
229
  columnDefs,
292
230
  serverSideDatasource,
293
231
  };
@@ -324,16 +262,19 @@ export function makeColDef(
324
262
  cellStyle.fontFamily = columnRenderingSpec.fontFamily;
325
263
  }
326
264
  }
265
+ const headerName =
266
+ readAnnotation(labeledSpec.spec, Annotation.Label)?.trim() ??
267
+ readAnnotation(spec.spec, Annotation.Label)?.trim() ??
268
+ `Unlabeled ${spec.type} ${iCol}`;
269
+
327
270
  return {
328
271
  colId,
329
272
  mainMenuItems: defaultMainMenuItems,
330
273
  context: spec,
331
274
  field: `${iCol}`,
332
- headerName:
333
- readAnnotation(labeledSpec.spec, Annotation.Label)?.trim() ??
334
- `Unlabeled ${spec.type} ${iCol}`,
275
+ headerName,
335
276
  lockPosition: spec.type === "axis",
336
- hide: hiddenColIds?.includes(colId) ?? isColumnOptional(spec.spec),
277
+ hide: hiddenColIds !== undefined && hiddenColIds.includes(colId),
337
278
  valueFormatter: columnRenderingSpec.valueFormatter,
338
279
  headerComponent: PlAgColumnHeader,
339
280
  cellRendererSelector: cellButtonAxisParams?.showCellButtonForAxisId
@@ -388,3 +329,160 @@ export function makeColDef(
388
329
  })(),
389
330
  };
390
331
  }
332
+
333
+ type LabelColumnLookup = (axisId: AxisId) => number;
334
+
335
+ /** Build index mapping from full tableSpecs to their position in visibleTableSpecs (missing → -1). */
336
+ function buildSpecsToVisibleSpecsMapping(
337
+ tableSpecs: PTableColumnSpec[],
338
+ visibleTableSpecs: PTableColumnSpec[],
339
+ ): Map<number, number> {
340
+ const specId = (spec: PTableColumnSpec) =>
341
+ canonicalizeJson<PTableColumnId>(getPTableColumnId(spec));
342
+ const visibleSpecsMap = new Map(
343
+ visibleTableSpecs.entries().map(([i, spec]) => [specId(spec), i] as const),
344
+ );
345
+ return new Map(
346
+ tableSpecs.entries().map(([i, spec]) => {
347
+ const visibleSpecIdx = visibleSpecsMap.get(specId(spec));
348
+ return [i, isNil(visibleSpecIdx) ? -1 : visibleSpecIdx];
349
+ }),
350
+ );
351
+ }
352
+
353
+ /** Predicate that returns true when an axis is one of the sheet partition axes. */
354
+ function createPartitionedAxisPredicate(sheets: PlDataTableSheet[]): (axisId: AxisId) => boolean {
355
+ const sheetAxesIds = sheets.map((sheet) => getAxisId(sheet.axis));
356
+ return (axisId) => sheetAxesIds.some((id) => matchAxisId(id, axisId));
357
+ }
358
+
359
+ /**
360
+ * Collect label columns (skipping partitioned axes and duplicates) and return a
361
+ * lookup function that resolves labeled axisId → label column index (or -1).
362
+ */
363
+ function collectLabelColumnsByAxis(
364
+ tableSpecs: PTableColumnSpec[],
365
+ isPartitionedAxis: (axisId: AxisId) => boolean,
366
+ ): LabelColumnLookup {
367
+ const labelColumns: { axisId: AxisId; labelColumnIdx: number }[] = [];
368
+ for (const [i, spec] of tableSpecs.entries()) {
369
+ if (spec.type !== "column" || !isLabelColumnSpec(spec.spec)) continue;
370
+ const labeledAxisId = getAxisId(spec.spec.axesSpec[0]);
371
+ if (isPartitionedAxis(labeledAxisId)) continue;
372
+ if (labelColumns.some((info) => matchAxisId(info.axisId, labeledAxisId))) {
373
+ console.warn(`multiple label columns match axisId: ${JSON.stringify(labeledAxisId)}`);
374
+ continue;
375
+ }
376
+ labelColumns.push({ axisId: labeledAxisId, labelColumnIdx: i });
377
+ }
378
+ return (axisId) =>
379
+ labelColumns.find((info) => matchAxisId(info.axisId, axisId))?.labelColumnIdx ?? -1;
380
+ }
381
+
382
+ /** Indices of columns to display: drop partitioned axes, label/linker columns, hidden columns. */
383
+ function selectDisplayableIndices(
384
+ tableSpecs: PTableColumnSpec[],
385
+ isPartitionedAxis: (axisId: AxisId) => boolean,
386
+ ): number[] {
387
+ return tableSpecs
388
+ .entries()
389
+ .filter(([, spec]) => {
390
+ switch (spec.type) {
391
+ case "axis":
392
+ return !isPartitionedAxis(spec.id);
393
+ case "column":
394
+ return (
395
+ !isLabelColumnSpec(spec.spec) &&
396
+ !isColumnHidden(spec.spec) &&
397
+ !isLinkerColumnSpec(spec.spec)
398
+ );
399
+ }
400
+ })
401
+ .map(([i]) => i)
402
+ .toArray();
403
+ }
404
+
405
+ /** Sort: axes first, then columns by OrderPriority annotation (higher priority = further left). */
406
+ function sortIndicesByTypeAndPriority(indices: number[], tableSpecs: PTableColumnSpec[]): number[] {
407
+ const priorityOf = (i: number): number => {
408
+ const spec = tableSpecs[i];
409
+ return spec.type === "column"
410
+ ? (readAnnotationJson(spec.spec, Annotation.Table.OrderPriority) ?? 0)
411
+ : 0;
412
+ };
413
+ return [...indices].sort((a, b) => {
414
+ if (tableSpecs[a].type !== tableSpecs[b].type) {
415
+ return tableSpecs[a].type === "axis" ? -1 : 1;
416
+ }
417
+ return priorityOf(b) - priorityOf(a);
418
+ });
419
+ }
420
+
421
+ /** For each axis entry substitute the index of its matching label column when one exists. */
422
+ function replaceAxesWithLabelColumns(
423
+ fields: number[],
424
+ tableSpecs: PTableColumnSpec[],
425
+ getLabelColumnIndex: LabelColumnLookup,
426
+ ): number[] {
427
+ return fields.map((i) => {
428
+ const spec = tableSpecs[i];
429
+ const labelIdx = spec.type === "axis" ? getLabelColumnIndex(spec.id) : -1;
430
+ return labelIdx === -1 ? i : labelIdx;
431
+ });
432
+ }
433
+
434
+ /** Default hidden col ids built from columns marked with the Optional annotation. */
435
+ function computeDefaultHiddenColIds(
436
+ fields: number[],
437
+ indices: number[],
438
+ tableSpecs: PTableColumnSpec[],
439
+ ): PlTableColumnIdJson[] {
440
+ return fields.reduce<PlTableColumnIdJson[]>((acc, field, i) => {
441
+ const spec = tableSpecs[field];
442
+ if (spec.type !== "column" || !isColumnOptional(spec.spec)) return acc;
443
+ const labeledSpec = tableSpecs[indices[i]];
444
+ return [...acc, canonicalizeJson<PlTableColumnId>({ source: spec, labeled: labeledSpec })];
445
+ }, []);
446
+ }
447
+
448
+ /** Extract axis indices and specs from the visible table (always present as part of join). */
449
+ function collectVisibleAxes(
450
+ visibleTableSpecs: PTableColumnSpec[],
451
+ ): [number, PTableColumnSpecAxis][] {
452
+ return visibleTableSpecs
453
+ .entries()
454
+ .filter((entry): entry is [number, PTableColumnSpecAxis] => entry[1].type === "axis")
455
+ .toArray();
456
+ }
457
+
458
+ /**
459
+ * Compose request indices for the visible table:
460
+ * non-hidden display fields first, then visible axes (deduplicated).
461
+ * Returns fieldResultMapping (display field → position in requestIndices, -1 if not requested)
462
+ * and axesResultIndices (visible axis → position in requestIndices).
463
+ */
464
+ function buildRequestIndices(
465
+ indices: number[],
466
+ specsToVisibleSpecsMapping: Map<number, number>,
467
+ visibleAxesIndices: number[],
468
+ ): {
469
+ requestIndices: number[];
470
+ fieldResultMapping: number[];
471
+ axesResultIndices: number[];
472
+ } {
473
+ const resolved = indices.map((displayField) => {
474
+ const idx = specsToVisibleSpecsMapping.get(displayField);
475
+ return idx === undefined || idx === -1 ? null : idx;
476
+ });
477
+ const requestedFields = resolved.filter((v): v is number => v !== null);
478
+
479
+ const fieldResultMapping: number[] = [];
480
+ let pos = 0;
481
+ for (const v of resolved) {
482
+ fieldResultMapping.push(v === null ? -1 : pos++);
483
+ }
484
+
485
+ const requestIndices = uniq([...requestedFields, ...visibleAxesIndices]);
486
+ const axesResultIndices = visibleAxesIndices.map((vi) => requestIndices.indexOf(vi));
487
+ return { requestIndices, fieldResultMapping, axesResultIndices };
488
+ }
@@ -12,7 +12,6 @@ import {
12
12
  type PlDataTableStateV2,
13
13
  type PlDataTableStateV2CacheEntry,
14
14
  type PlDataTableStateV2Normalized,
15
- type PObjectId,
16
15
  type PTableParamsV2,
17
16
  type PTableSorting,
18
17
  type PlDataTableFilters,
@@ -178,15 +177,10 @@ function makeDefaultState(): PlDataTableStateV2CacheEntryNullable {
178
177
  };
179
178
  }
180
179
 
181
- function getHiddenColIds(state: PlDataTableGridStateCore["columnVisibility"]): PObjectId[] | null {
182
- return (
183
- state?.hiddenColIds?.map(parseJson).reduce((acc, c) => {
184
- if (c.source.type === "column") {
185
- acc.push(c.source.id);
186
- }
187
- return acc;
188
- }, [] as PObjectId[]) ?? null
189
- );
180
+ function getHiddenColIds(
181
+ state: PlDataTableGridStateCore["columnVisibility"],
182
+ ): PTableColumnId[] | null {
183
+ return state?.hiddenColIds?.map((json) => getPTableColumnId(parseJson(json).source)) ?? null;
190
184
  }
191
185
 
192
186
  function convertPartitionFiltersToFilterSpec(
@@ -6,10 +6,11 @@ import {
6
6
  PlSlideModal,
7
7
  usePlBlockPageTitleTeleportTarget,
8
8
  } from "@milaboratories/uikit";
9
- import { type Column, type DisplayedColumnsChangedEvent, type GridApi } from "ag-grid-enterprise";
10
- import { computed, ref, toRefs, watch } from "vue";
9
+ import { type GridApi } from "ag-grid-enterprise";
10
+ import { computed, ref } from "vue";
11
11
  import { PlAgDataTableRowNumberColId } from "../PlAgDataTable/sources/row-number";
12
12
  import { useFilteredItems } from "./useFilteredItems";
13
+ import { useGridColumns } from "./useGridColumns";
13
14
 
14
15
  const props = defineProps<{
15
16
  /**
@@ -25,44 +26,25 @@ const props = defineProps<{
25
26
  width?: string;
26
27
  }>();
27
28
 
28
- const { api: gridApi } = toRefs(props);
29
-
30
- const columns = ref<Column[]>([]);
31
- watch(
32
- () => gridApi.value,
33
- (gridApi) => {
34
- if (gridApi.isDestroyed()) return;
35
-
36
- gridApi.addEventListener("displayedColumnsChanged", (event: DisplayedColumnsChangedEvent) => {
37
- columns.value = event.api.getAllGridColumns();
38
- });
29
+ const query = ref("");
30
+ const slideModal = ref(false);
31
+ const teleportTarget = usePlBlockPageTitleTeleportTarget("PlAgGridColumnManager");
39
32
 
40
- columns.value = gridApi.getAllGridColumns();
41
- if (columns.value.length > 0) {
42
- gridApi.moveColumns(columns.value, 0);
43
- }
44
- },
45
- { immediate: true },
46
- );
33
+ const columns = useGridColumns(props);
47
34
 
48
35
  const items = computed(() => {
49
- return columns.value.map((col) => ({
36
+ return columns.value.map((col, i) => ({
50
37
  column: col,
51
38
  id: col.getId(),
52
- label: col.getColDef().headerName!,
39
+ label: col.getColDef().headerName ?? `Unnamed Column (${i + 1})`,
53
40
  }));
54
41
  });
55
42
 
56
- const query = ref("");
57
-
58
- const slideModal = ref(false);
59
- const teleportTarget = usePlBlockPageTitleTeleportTarget("PlAgGridColumnManager");
60
-
61
- const { filteredItems, segments } = useFilteredItems(() => ({
62
- items: items.value,
63
- query: query.value,
43
+ const { filteredItems, segments } = useFilteredItems({
44
+ items,
45
+ query,
64
46
  getStrings: (item) => [item.label],
65
- }));
47
+ });
66
48
  </script>
67
49
 
68
50
  <template>
@@ -79,17 +61,17 @@ const { filteredItems, segments } = useFilteredItems(() => ({
79
61
  :is-draggable="(item) => !item.column.getColDef().lockPosition"
80
62
  :on-sort="
81
63
  (fromIndex, toIndex) => {
82
- if (!gridApi.isDestroyed()) {
64
+ if (!props.api.isDestroyed()) {
83
65
  const columnToMove = columns[fromIndex];
84
- gridApi.moveColumns([columnToMove], toIndex);
66
+ props.api.moveColumns([columnToMove], toIndex);
85
67
  }
86
68
  return true; // Let PlElementList handle the visual update
87
69
  }
88
70
  "
89
71
  :on-toggle="
90
72
  (item) => {
91
- if (!gridApi.isDestroyed()) {
92
- gridApi.setColumnsVisible([item.column], !item.column.isVisible());
73
+ if (!props.api.isDestroyed()) {
74
+ props.api.setColumnsVisible([item.column], !item.column.isVisible());
93
75
  }
94
76
  }
95
77
  "
@@ -1,25 +1,23 @@
1
- import { computed, type MaybeRefOrGetter, toValue } from "vue";
1
+ import { Ref, computed, toValue } from "vue";
2
2
 
3
- export function useFilteredItems<T>(
4
- props: MaybeRefOrGetter<{
5
- items: T[];
6
- query: string;
7
- getStrings: (item: T) => Iterable<string>;
8
- }>,
9
- ) {
3
+ export function useFilteredItems<T>(props: {
4
+ items: Ref<T[]>;
5
+ query: Ref<string>;
6
+ getStrings: (item: T) => Iterable<string>;
7
+ }) {
10
8
  const result = computed(() => {
11
9
  const { items, query, getStrings } = toValue(props);
12
10
  const filteredItems: T[] = [];
13
11
  const segments = new Map<string, StringSegment[]>();
14
- for (const item of items) {
12
+ for (const item of items.value) {
15
13
  let kept = false;
16
14
  for (const string of getStrings(item)) {
17
15
  let stringSegments = segments.get(string);
18
16
  if (!stringSegments) {
19
- stringSegments = matchSubstrings(string, query);
17
+ stringSegments = matchSubstrings(string, query.value);
20
18
  segments.set(string, stringSegments);
21
19
  }
22
- if (!kept && (!query || stringSegments.some(({ match }) => match))) {
20
+ if (!kept && (!query.value || stringSegments.some(({ match }) => match))) {
23
21
  filteredItems.push(item);
24
22
  kept = true;
25
23
  }
@@ -0,0 +1,26 @@
1
+ import { Column, GridApi } from "ag-grid-enterprise";
2
+ import { ref, watch } from "vue";
3
+
4
+ export function useGridColumns(props: { api: GridApi }) {
5
+ const columns = ref<Column[]>([]);
6
+ const syncColumns = () => {
7
+ columns.value = props.api.getAllGridColumns();
8
+ };
9
+ watch(
10
+ () => props.api,
11
+ (gridApi, _, onCleanup) => {
12
+ if (gridApi.isDestroyed()) return;
13
+
14
+ gridApi.addEventListener("displayedColumnsChanged", syncColumns);
15
+ onCleanup(() => gridApi.removeEventListener("displayedColumnsChanged", syncColumns));
16
+
17
+ columns.value = gridApi.getAllGridColumns();
18
+ if (columns.value.length > 0) {
19
+ gridApi.moveColumns(columns.value, 0);
20
+ }
21
+ },
22
+ { immediate: true },
23
+ );
24
+
25
+ return columns;
26
+ }
@@ -12,9 +12,9 @@ import {
12
12
  Domain,
13
13
  readAnnotation,
14
14
  readDomain,
15
- getAxisId,
16
15
  getUniqueSourceValuesWithLabels,
17
16
  parseJson,
17
+ getPTableColumnId,
18
18
  } from "@platforma-sdk/model";
19
19
  import { computed, onMounted, ref } from "vue";
20
20
  import { PlBtnGhost, PlSlideModal, usePlBlockPageTitleTeleportTarget } from "@milaboratories/uikit";
@@ -48,11 +48,7 @@ const filtersOn = computed(() => {
48
48
  });
49
49
 
50
50
  function makeFilterColumnId(spec: PTableColumnSpec): CanonicalizedJson<PTableColumnId> {
51
- const id: PTableColumnId =
52
- spec.type === "axis"
53
- ? { type: "axis", id: getAxisId(spec.spec) }
54
- : { type: "column", id: spec.id };
55
- return canonicalizeJson<PTableColumnId>(id);
51
+ return canonicalizeJson<PTableColumnId>(getPTableColumnId(spec));
56
52
  }
57
53
 
58
54
  const items = computed<PlAdvancedFilterItem[]>(() => {