@platforma-sdk/model 1.74.0 → 1.75.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 (78) hide show
  1. package/dist/columns/column_collection_builder.cjs +4 -3
  2. package/dist/columns/column_collection_builder.cjs.map +1 -1
  3. package/dist/columns/column_collection_builder.d.ts +5 -5
  4. package/dist/columns/column_collection_builder.d.ts.map +1 -1
  5. package/dist/columns/column_collection_builder.js +4 -3
  6. package/dist/columns/column_collection_builder.js.map +1 -1
  7. package/dist/components/PlDataTable/createPlDataTable/createPTableDefV2.cjs.map +1 -1
  8. package/dist/components/PlDataTable/createPlDataTable/createPTableDefV2.js.map +1 -1
  9. package/dist/components/PlDataTable/createPlDataTable/createPTableDefV3.cjs.map +1 -1
  10. package/dist/components/PlDataTable/createPlDataTable/createPTableDefV3.js.map +1 -1
  11. package/dist/components/PlDataTable/createPlDataTable/createPlDataTableV2.cjs.map +1 -1
  12. package/dist/components/PlDataTable/createPlDataTable/createPlDataTableV2.d.ts +1 -1
  13. package/dist/components/PlDataTable/createPlDataTable/createPlDataTableV2.js.map +1 -1
  14. package/dist/components/PlDataTable/createPlDataTable/createPlDataTableV3.cjs +14 -14
  15. package/dist/components/PlDataTable/createPlDataTable/createPlDataTableV3.cjs.map +1 -1
  16. package/dist/components/PlDataTable/createPlDataTable/createPlDataTableV3.d.ts +3 -2
  17. package/dist/components/PlDataTable/createPlDataTable/createPlDataTableV3.d.ts.map +1 -1
  18. package/dist/components/PlDataTable/createPlDataTable/createPlDataTableV3.js +16 -16
  19. package/dist/components/PlDataTable/createPlDataTable/createPlDataTableV3.js.map +1 -1
  20. package/dist/components/PlDataTable/createPlDataTable/discoverColumns.cjs +50 -4
  21. package/dist/components/PlDataTable/createPlDataTable/discoverColumns.cjs.map +1 -1
  22. package/dist/components/PlDataTable/createPlDataTable/discoverColumns.js +51 -6
  23. package/dist/components/PlDataTable/createPlDataTable/discoverColumns.js.map +1 -1
  24. package/dist/components/PlDataTable/createPlDataTable/index.cjs.map +1 -1
  25. package/dist/components/PlDataTable/createPlDataTable/index.js.map +1 -1
  26. package/dist/components/PlDataTable/createPlDataTable/utils.cjs +7 -4
  27. package/dist/components/PlDataTable/createPlDataTable/utils.cjs.map +1 -1
  28. package/dist/components/PlDataTable/createPlDataTable/utils.js +16 -13
  29. package/dist/components/PlDataTable/createPlDataTable/utils.js.map +1 -1
  30. package/dist/components/PlDataTable/createPlDataTableSheet.cjs.map +1 -1
  31. package/dist/components/PlDataTable/createPlDataTableSheet.d.ts +1 -1
  32. package/dist/components/PlDataTable/createPlDataTableSheet.js.map +1 -1
  33. package/dist/components/PlDataTable/index.d.ts +1 -1
  34. package/dist/components/PlDataTable/state-migration.cjs +31 -5
  35. package/dist/components/PlDataTable/state-migration.cjs.map +1 -1
  36. package/dist/components/PlDataTable/state-migration.d.ts +13 -1
  37. package/dist/components/PlDataTable/state-migration.d.ts.map +1 -1
  38. package/dist/components/PlDataTable/state-migration.js +32 -6
  39. package/dist/components/PlDataTable/state-migration.js.map +1 -1
  40. package/dist/components/PlDataTable/typesV5.d.ts +19 -90
  41. package/dist/components/PlDataTable/typesV5.d.ts.map +1 -1
  42. package/dist/components/PlDataTable/typesV6.d.ts +98 -0
  43. package/dist/components/PlDataTable/typesV6.d.ts.map +1 -0
  44. package/dist/components/PlDatasetSelector/enrichment_discovery.cjs +5 -5
  45. package/dist/components/PlDatasetSelector/enrichment_discovery.cjs.map +1 -1
  46. package/dist/components/PlDatasetSelector/enrichment_discovery.js +5 -5
  47. package/dist/components/PlDatasetSelector/enrichment_discovery.js.map +1 -1
  48. package/dist/components/index.d.ts +1 -1
  49. package/dist/index.d.ts +2 -2
  50. package/dist/labels/derive_distinct_labels.cjs +2 -2
  51. package/dist/labels/derive_distinct_labels.cjs.map +1 -1
  52. package/dist/labels/derive_distinct_labels.js +2 -2
  53. package/dist/labels/derive_distinct_labels.js.map +1 -1
  54. package/dist/labels/derive_distinct_tooltips.cjs +2 -1
  55. package/dist/labels/derive_distinct_tooltips.cjs.map +1 -1
  56. package/dist/labels/derive_distinct_tooltips.js +2 -1
  57. package/dist/labels/derive_distinct_tooltips.js.map +1 -1
  58. package/dist/package.cjs +1 -1
  59. package/dist/package.js +1 -1
  60. package/package.json +8 -8
  61. package/src/columns/column_collection_builder.test.ts +3 -7
  62. package/src/columns/column_collection_builder.ts +14 -35
  63. package/src/components/PlDataTable/createPlDataTable/createPTableDefV2.ts +1 -1
  64. package/src/components/PlDataTable/createPlDataTable/createPTableDefV3.ts +1 -1
  65. package/src/components/PlDataTable/createPlDataTable/createPlDataTableV2.ts +1 -1
  66. package/src/components/PlDataTable/createPlDataTable/createPlDataTableV3.ts +36 -21
  67. package/src/components/PlDataTable/createPlDataTable/discoverColumns.ts +57 -8
  68. package/src/components/PlDataTable/createPlDataTable/index.ts +1 -1
  69. package/src/components/PlDataTable/createPlDataTable/utils.test.ts +1 -1
  70. package/src/components/PlDataTable/createPlDataTable/utils.ts +9 -8
  71. package/src/components/PlDataTable/createPlDataTableSheet.ts +1 -1
  72. package/src/components/PlDataTable/index.ts +1 -2
  73. package/src/components/PlDataTable/state-migration.ts +71 -8
  74. package/src/components/PlDataTable/typesV5.ts +14 -153
  75. package/src/components/PlDataTable/typesV6.ts +152 -0
  76. package/src/components/PlDatasetSelector/enrichment_discovery.ts +5 -5
  77. package/src/labels/derive_distinct_labels.ts +2 -2
  78. package/src/labels/derive_distinct_tooltips.ts +2 -1
@@ -17,7 +17,7 @@ import { canonicalizeJson, getAxisId, parseJson, uniqueBy } from "@milaboratorie
17
17
  import { collectFilterSpecColumns, traverseFilterSpec } from "../../../filters/traverse";
18
18
  import type { RenderCtxBase, PColumnDataUniversal } from "../../../render";
19
19
  import { isEmpty } from "es-toolkit/compat";
20
- import type { PlDataTableFilters, PlDataTableFilterSpecLeaf, PlDataTableModel } from "../typesV5";
20
+ import type { PlDataTableFilters, PlDataTableFilterSpecLeaf, PlDataTableModel } from "../typesV6";
21
21
  import { upgradePlDataTableStateV2 } from "../state-migration";
22
22
  import type { PlDataTableStateV2 } from "../state-migration";
23
23
  import type { ColumnSelector, ColumnSnapshot, ColumnVariant, MatchingMode } from "../../../columns";
@@ -36,8 +36,12 @@ import {
36
36
  } from "./utils";
37
37
  import type { PrimaryEntry, SecondaryGroup } from "./createPTableDefV3";
38
38
  import { createPTableDefV3 } from "./createPTableDefV3";
39
- import { discoverTableColumnSnaphots, type DiscoverTableColumnOptions } from "./discoverColumns";
40
- import { isNil, isPlainObject, throwError, type Nil } from "@milaboratories/helpers";
39
+ import {
40
+ discoverLabelColumnVariants,
41
+ discoverTableColumnSnaphots,
42
+ type DiscoverTableColumnOptions,
43
+ } from "./discoverColumns";
44
+ import { getField, isNil, isPlainObject, throwError, type Nil } from "@milaboratories/helpers";
41
45
  import { flow } from "es-toolkit";
42
46
 
43
47
  export type createPlDataTableOptionsV3 = {
@@ -90,7 +94,12 @@ export function createPlDataTableV3<A, U>(
90
94
 
91
95
  const discovered = isPlainObject(options.columns)
92
96
  ? discoverTableColumnSnaphots(ctx, options.columns)
93
- : options.columns;
97
+ : isNil(options.columns)
98
+ ? options.columns
99
+ : uniqueBy(
100
+ [...options.columns, ...discoverLabelColumnVariants(ctx, options.columns)],
101
+ (c) => c.column.id,
102
+ );
94
103
  if (isNil(discovered) || discovered.length === 0) return undefined;
95
104
 
96
105
  const splited = splitDiscoveredColumns(discovered);
@@ -111,7 +120,7 @@ export function createPlDataTableV3<A, U>(
111
120
  const derivedTooltips = deriveAllTooltips({
112
121
  columns: discovered.map((dc) => ({
113
122
  id: dc.column.id,
114
- originalId: dc.originalId,
123
+ originalId: getField(dc, "originalId"),
115
124
  spec: dc.column.spec,
116
125
  linkerPath: dc.path,
117
126
  qualifications: dc.qualifications,
@@ -133,7 +142,7 @@ export function createPlDataTableV3<A, U>(
133
142
 
134
143
  const columnIsAvailable = createColumnValidationById([
135
144
  ...annotated.direct.map((v) => v.column),
136
- ...annotated.linked.flatMap((lc) => [...lc.path.map((s) => s.linker), lc.column]),
145
+ ...annotated.linked.flatMap((lc) => [...(lc.path ?? []).map((s) => s.linker), lc.column]),
137
146
  ]);
138
147
 
139
148
  const remapedDefaultFilters = remapFilterColumnIds(options.filters, discovered);
@@ -204,8 +213,10 @@ export function createPlDataTableV3<A, U>(
204
213
  } satisfies PlDataTableModel;
205
214
  }
206
215
 
207
- export type TableColumnVariant = ColumnVariant<DiscoveredPColumnId> & {
208
- readonly originalId: PObjectId;
216
+ export type TableColumnVariant = (
217
+ | ColumnVariant<PObjectId>
218
+ | (ColumnVariant<DiscoveredPColumnId> & { readonly originalId: PObjectId })
219
+ ) & {
209
220
  readonly isPrimary?: boolean;
210
221
  };
211
222
 
@@ -226,15 +237,15 @@ type VisibleColumns = {
226
237
 
227
238
  /** Split discovered columns into direct (no linker path) and linked (with linker path). */
228
239
  function splitDiscoveredColumns(columns: TableColumnVariant[]): SplitDiscoveredColumns {
229
- const direct = columns.filter((dc) => dc.path.length === 0);
230
- const linked = columns.filter((dc) => dc.path.length > 0);
240
+ const direct = columns.filter((dc) => (dc.path?.length ?? 0) === 0);
241
+ const linked = columns.filter((dc) => (dc.path?.length ?? 0) > 0);
231
242
  return { direct, linked };
232
243
  }
233
244
 
234
245
  /** All linker snapshots across the given linked columns, deduped by id. */
235
246
  function collectLinkerSnapshots(linked: TableColumnVariant[]): ColumnSnapshot<PObjectId>[] {
236
247
  return uniqueBy(
237
- linked.flatMap((lc) => lc.path.map((s) => s.linker)),
248
+ linked.flatMap((lc) => (lc.path ?? []).map((s) => s.linker)),
238
249
  (c) => c.id,
239
250
  );
240
251
  }
@@ -299,9 +310,13 @@ function annotateColumnGroups(params: {
299
310
  }
300
311
 
301
312
  /** Lift a snapshot-array transform so it runs on the inner `column` of each variant. */
302
- function liftToVariantColumns<V extends { readonly column: ColumnSnapshot<DiscoveredPColumnId> }>(
313
+ function liftToVariantColumns<
314
+ V extends { readonly column: ColumnSnapshot<PObjectId | DiscoveredPColumnId> },
315
+ >(
303
316
  variants: V[],
304
- fn: (cols: ColumnSnapshot<DiscoveredPColumnId>[]) => ColumnSnapshot<DiscoveredPColumnId>[],
317
+ fn: (
318
+ cols: ColumnSnapshot<PObjectId | DiscoveredPColumnId>[],
319
+ ) => ColumnSnapshot<PObjectId | DiscoveredPColumnId>[],
305
320
  ): V[] {
306
321
  const cols = fn(variants.map((v) => v.column));
307
322
  if (cols.length !== variants.length)
@@ -315,7 +330,7 @@ function annotateLinkerPath(
315
330
  derivedLabels: Record<string, string>,
316
331
  path: TableColumnVariant["path"],
317
332
  ): TableColumnVariant["path"] {
318
- if (path.length === 0) return path;
333
+ if (isNil(path) || path.length === 0) return path;
319
334
  const annotatedLinkers = withHidenAxesAnnotations(
320
335
  withLabelAnnotations(
321
336
  derivedLabels,
@@ -410,8 +425,8 @@ function buildSecondaryGroups(
410
425
  return [
411
426
  ...direct.map(
412
427
  (c): SecondaryGroup<undefined | PColumnDataUniversal> => ({
413
- entries: [{ column: resolveSnapshot(c.column), qualifications: c.qualifications.forHit }],
414
- primaryQualifications: c.qualifications.forQueries,
428
+ entries: [{ column: resolveSnapshot(c.column), qualifications: c.qualifications?.forHit }],
429
+ primaryQualifications: c.qualifications?.forQueries,
415
430
  }),
416
431
  ),
417
432
  ...linked.map(
@@ -419,11 +434,11 @@ function buildSecondaryGroups(
419
434
  entries: [
420
435
  {
421
436
  column: resolveSnapshot(lc.column),
422
- qualifications: lc.qualifications.forHit,
423
- linkers: lc.path.map((s) => resolveSnapshot(s.linker)),
437
+ linkers: lc.path?.map((s) => resolveSnapshot(s.linker)),
438
+ qualifications: lc.qualifications?.forHit,
424
439
  },
425
440
  ],
426
- primaryQualifications: lc.qualifications.forQueries,
441
+ primaryQualifications: lc.qualifications?.forQueries,
427
442
  }),
428
443
  ),
429
444
  ];
@@ -492,7 +507,7 @@ function remapSortingColumnIds(
492
507
  if (s.column.type === "axis") return [s]; // Axis references are unaffected by column ID remapping
493
508
 
494
509
  const id = s.column.id;
495
- const column = columns.find((c) => (c.originalId ?? c.column.id) === id);
510
+ const column = columns.find((c) => (getField(c, "originalId") ?? c.column.id) === id);
496
511
  if (column === undefined) return [];
497
512
 
498
513
  return [
@@ -524,7 +539,7 @@ function remapFilterColumnIds(
524
539
 
525
540
  const originalId = parsed.id;
526
541
  const column =
527
- columns.find((c) => (c.originalId ?? c.column.id) === originalId) ??
542
+ columns.find((c) => (getField(c, "originalId") ?? c.column.id) === originalId) ??
528
543
  throwError(`Column ID "${parsed.id}" in filters does not match any discovered column`);
529
544
 
530
545
  return canonicalizeJson<PTableColumnId>({
@@ -1,5 +1,5 @@
1
1
  import type { PColumnSpec, PlRef, PObjectId } from "@milaboratories/pl-model-common";
2
- import { createDiscoveredPColumnId, isPlRef } from "@milaboratories/pl-model-common";
2
+ import { createDiscoveredPColumnId, isPlRef, PColumnName } from "@milaboratories/pl-model-common";
3
3
  import type { RenderCtxBase } from "../../../render";
4
4
  import type {
5
5
  ColumnSource,
@@ -42,9 +42,7 @@ export function discoverTableColumnSnaphots(
42
42
  if (collection === undefined) return undefined;
43
43
 
44
44
  try {
45
- const variants = collection.findColumnVariants({
46
- ...(resolvedOptions.selector ?? {}),
47
- });
45
+ const variants = collection.findColumnVariants(resolvedOptions.selector);
48
46
  const anchors = collection.getAnchors();
49
47
  return mapToTableColumnVariants(variants, anchors);
50
48
  } finally {
@@ -52,7 +50,58 @@ export function discoverTableColumnSnaphots(
52
50
  }
53
51
  }
54
52
 
55
- // --- Pure helper functions ---
53
+ /**
54
+ * Discover label columns matching the axes of the given value columns from
55
+ * ctx providers. Returns label snapshots wrapped as direct TableColumnVariants
56
+ * (path: [], empty qualifications, isPrimary: false).
57
+ */
58
+ export function discoverLabelColumnVariants<A, U>(
59
+ ctx: RenderCtxBase<A, U>,
60
+ columns: TableColumnVariant[],
61
+ ): TableColumnVariant[] {
62
+ if (columns.length === 0) return [];
63
+ const collection = new ColumnCollectionBuilder(ctx.getService("pframeSpec"))
64
+ .addSources(collectCtxColumnSnapshotProviders(ctx))
65
+ .addSource(columns.map((c) => c.column))
66
+ .build({
67
+ allowPartialColumnList: true,
68
+ anchors: Object.fromEntries(
69
+ columns
70
+ .filter((col) => (col.path?.length ?? 0) === 0 && col.isPrimary)
71
+ .map((col, i) => [`anchor_${i}`, col.column.spec]),
72
+ ),
73
+ });
74
+
75
+ try {
76
+ const axes = columns.flatMap((col) => col.column.spec.axesSpec);
77
+ return collection
78
+ .findColumnVariants({
79
+ include: axes.map((a) => ({
80
+ name: { type: "exact", value: PColumnName.Label },
81
+ axes: [{ name: { type: "exact", value: a.name } }],
82
+ })),
83
+ maxHops: columns.reduce((acc, c) => Math.max(acc, c.path?.length ?? 0), 0),
84
+ })
85
+ .map((variant) => ({
86
+ ...variant,
87
+ column: {
88
+ ...variant.column,
89
+ id: createDiscoveredPColumnId({
90
+ column: variant.column.id,
91
+ path: variant.path?.map((p) => ({
92
+ type: "linker",
93
+ column: p.linker.id,
94
+ })),
95
+ columnQualifications: variant.qualifications?.forHit,
96
+ queriesQualifications: variant.qualifications?.forQueries,
97
+ }),
98
+ originalId: variant.column.id,
99
+ },
100
+ }));
101
+ } finally {
102
+ collection.dispose();
103
+ }
104
+ }
56
105
 
57
106
  /** Resolve PlRef values in anchors to PColumnSpec via the result pool. */
58
107
  function resolveAnchors(
@@ -99,12 +148,12 @@ function mapToTableColumnVariants(
99
148
 
100
149
  const discoveredId = createDiscoveredPColumnId({
101
150
  column: snap.id,
102
- path: variant.path.map((p) => ({
151
+ path: variant.path?.map((p) => ({
103
152
  type: "linker",
104
153
  column: p.linker.id,
105
154
  })),
106
- columnQualifications: variant.qualifications.forHit,
107
- queriesQualifications: variant.qualifications.forQueries,
155
+ columnQualifications: variant.qualifications?.forHit,
156
+ queriesQualifications: variant.qualifications?.forQueries,
108
157
  });
109
158
  return {
110
159
  column: {
@@ -1,5 +1,5 @@
1
1
  import type { RenderCtxBase } from "../../../render";
2
- import type { PlDataTableModel } from "../typesV5";
2
+ import type { PlDataTableModel } from "../typesV6";
3
3
  import { createPlDataTableOptionsV2, createPlDataTableV2 } from "./createPlDataTableV2";
4
4
  import { createPlDataTableV3 } from "./createPlDataTableV3";
5
5
  import type { createPlDataTableOptionsV3 } from "./createPlDataTableV3";
@@ -114,7 +114,7 @@ describe("deriveAllLabels", () => {
114
114
  makeLabelableColumn(
115
115
  "c1",
116
116
  { name: "shared", annotations: { [Annotation.Label]: "Cluster size" } },
117
- [{ linker: { id: "lk" as PObjectId, spec: linkerSpec } as never, qualifications: [] }],
117
+ [{ linker: { id: "lk" as PObjectId, spec: linkerSpec } as never }],
118
118
  ),
119
119
  makeLabelableColumn("c2", {
120
120
  name: "shared",
@@ -7,7 +7,6 @@ import {
7
7
  canonicalizeAxisId,
8
8
  DiscoveredPColumnId,
9
9
  readAnnotation,
10
- readAnnotationJson,
11
10
  } from "@milaboratories/pl-model-common";
12
11
  import {
13
12
  deriveDistinctLabels,
@@ -22,6 +21,7 @@ import type { ColumnMatcher, ColumnOrderRule, ColumnVisibilityRule } from "./cre
22
21
  import type { ColumnSelector } from "../../../columns";
23
22
  import { ArrayColumnProvider, ColumnCollectionBuilder } from "../../../columns";
24
23
  import { isNil } from "es-toolkit";
24
+ import { getField } from "@milaboratories/helpers";
25
25
 
26
26
  /** Check if column should be omitted from the table */
27
27
  export function isColumnHidden(spec: { annotations?: Annotation }): boolean {
@@ -55,7 +55,8 @@ export function getOrderPriority(
55
55
  ): undefined | number {
56
56
  const rule = orderByColId?.get(col.id);
57
57
  if (rule !== undefined) return rule.priority;
58
- return readAnnotationJson(col.spec, Annotation.Table.OrderPriority);
58
+ const annotation = Number(readAnnotation(col.spec, Annotation.Table.OrderPriority));
59
+ return isNaN(annotation) ? undefined : annotation;
59
60
  }
60
61
 
61
62
  /**
@@ -276,9 +277,9 @@ export function deriveAllLabels(options: {
276
277
 
277
278
  /** Column shape required by tooltip derivation. */
278
279
  export type TooltipableColumn = {
279
- readonly id: DiscoveredPColumnId;
280
+ readonly id: PObjectId | DiscoveredPColumnId;
280
281
  readonly spec: PColumnSpec;
281
- readonly originalId: PObjectId;
282
+ readonly originalId?: PObjectId;
282
283
  readonly linkerPath?: MatchVariant["path"];
283
284
  readonly qualifications?: MatchQualifications;
284
285
  };
@@ -290,15 +291,15 @@ export function deriveAllTooltips(options: {
290
291
  const { columns } = options;
291
292
 
292
293
  const variantCountByOriginal = columns.reduce<Map<PObjectId, number>>((acc, c) => {
293
- return acc.set(c.originalId, (acc.get(c.originalId) ?? 0) + 1);
294
+ return acc.set(getField(c, "originalId") ?? c.id, (acc.get(c.originalId ?? c.id) ?? 0) + 1);
294
295
  }, new Map());
295
296
 
296
297
  const { entries } = columns.reduce(
297
298
  ({ entries, variantSeen }, c) => {
298
- const variantCount = variantCountByOriginal.get(c.originalId);
299
+ const id = getField(c, "originalId") ?? c.id;
300
+ const variantCount = variantCountByOriginal.get(id);
299
301
  const variantIndex =
300
- (variantSeen.set(c.originalId, (variantSeen.get(c.originalId) ?? 0) + 1),
301
- variantSeen.get(c.originalId));
302
+ (variantSeen.set(id, (variantSeen.get(id) ?? 0) + 1), variantSeen.get(id));
302
303
 
303
304
  entries.push({
304
305
  spec: c.spec,
@@ -1,6 +1,6 @@
1
1
  import type { AxisSpec } from "@milaboratories/pl-model-common";
2
2
  import type { RenderCtxBase } from "../../render";
3
- import type { PlDataTableSheet } from "./typesV5";
3
+ import type { PlDataTableSheet } from "./typesV6";
4
4
 
5
5
  /** Create sheet entries for PlDataTable */
6
6
  export function createPlDataTableSheet<A, U>(
@@ -1,7 +1,6 @@
1
1
  // If you need to export something from previous versions,
2
2
  // that mean you don't forget update deps to the latest version of the package.
3
3
  export type {
4
- PlTableColumnId,
5
4
  PlTableColumnIdJson,
6
5
  PlDataTableGridStateCore,
7
6
  PlDataTableSheet,
@@ -14,7 +13,7 @@ export type {
14
13
  PlDataTableFilterMeta,
15
14
  PlDataTableFilters,
16
15
  PlDataTableFiltersWithMeta,
17
- } from "./typesV5";
16
+ } from "./typesV6";
18
17
 
19
18
  export type { PlDataTableStateV2 } from "./state-migration";
20
19
  export {
@@ -7,7 +7,7 @@ import type {
7
7
  PTableRecordFilter,
8
8
  PTableSorting,
9
9
  } from "@milaboratories/pl-model-common";
10
- import { canonicalizeJson } from "@milaboratories/pl-model-common";
10
+ import { canonicalizeJson, parseJson } from "@milaboratories/pl-model-common";
11
11
  import { distillFilterSpec } from "../../filters";
12
12
  import type { PlDataTableFilterState, PlTableFilter } from "./typesV4";
13
13
  import type {
@@ -17,7 +17,8 @@ import type {
17
17
  PlDataTableStateV2CacheEntry,
18
18
  PlDataTableStateV2Normalized,
19
19
  PTableParamsV2,
20
- } from "./typesV5";
20
+ } from "./typesV6";
21
+ import type { PlDataTableGridStateV5, PlDataTableV5ColIdJson } from "./typesV5";
21
22
 
22
23
  /**
23
24
  * PlDataTableV2 persisted state
@@ -111,6 +112,19 @@ export type PlDataTableStateV2 =
111
112
  sorting: PTableSorting[];
112
113
  };
113
114
  }
115
+ // v5 stored colIds as `{source, labeled}` wrappers; only the gridState shape differs.
116
+ | {
117
+ version: 5;
118
+ stateCache: {
119
+ sourceId: string;
120
+ gridState: PlDataTableGridStateV5;
121
+ sheetsState: PlDataTableSheetState[];
122
+ filtersState: null | PlDataTableFiltersWithMeta;
123
+ defaultFiltersState: null | PlDataTableFiltersWithMeta;
124
+ searchString?: string;
125
+ }[];
126
+ pTableParams: PTableParamsV2;
127
+ }
114
128
  // Normalized state
115
129
  | PlDataTableStateV2Normalized;
116
130
 
@@ -143,15 +157,64 @@ export function upgradePlDataTableStateV2(
143
157
  // Non upgradeable as column ids calculation algorithm has changed, resetting state to default
144
158
  state = createPlDataTableStateV2();
145
159
  }
146
- // v4 -> v5: migrate per-column filters to tree-based format
160
+ // v4 -> v6: migrate per-column filters to tree-based format (skips v5).
161
+ // v4 gridState already used bare PTableColumnSpec colIds, so we jump
162
+ // straight to v6 without going through the v5 wrapper format.
147
163
  if (state.version === 4) {
148
- state = migrateV4toV5(state);
164
+ state = migrateV4toV6(state);
165
+ }
166
+ // v5 -> v6: unwrap `{source, labeled}` colIds in gridState back to bare PTableColumnSpec.
167
+ if (state.version === 5) {
168
+ state = migrateV5toV6(state);
149
169
  }
150
170
  return state;
151
171
  }
152
172
 
153
- /** Migrate v4 state to v5: convert per-column filters to tree-based format */
154
- function migrateV4toV5(
173
+ /** Migrate v5 to v6: unwrap `{source, labeled}` colIds in gridState. */
174
+ function migrateV5toV6(
175
+ state: Extract<PlDataTableStateV2, { version: 5 }>,
176
+ ): PlDataTableStateV2Normalized {
177
+ // pTableParams reset: v5 stored DiscoveredPColumnId-based hiddenColIds with
178
+ // empty-array fields (e.g. `{column, path: [], columnQualifications: [], ...}`).
179
+ // v6 distills empty fields, so the same logical column serialises differently
180
+ // and lookups would silently miss every previously-hidden discovered column.
181
+ // gridState colIds are derived from PTableColumnSpec and unaffected.
182
+ return {
183
+ version: 6,
184
+ stateCache: state.stateCache.map((entry) => ({
185
+ ...entry,
186
+ gridState: unwrapV5GridState(entry.gridState),
187
+ })),
188
+ pTableParams: createDefaultPTableParams(),
189
+ };
190
+ }
191
+
192
+ /** Convert v5 wrapped colId JSON to bare PTableColumnSpec JSON, taking `.source`. */
193
+ function unwrapV5ColId(json: PlDataTableV5ColIdJson): CanonicalizedJson<PTableColumnSpec> {
194
+ return canonicalizeJson(parseJson(json).source);
195
+ }
196
+
197
+ function unwrapV5GridState(gridState: PlDataTableGridStateV5): PlDataTableGridStateCore {
198
+ return {
199
+ columnOrder: gridState.columnOrder
200
+ ? { orderedColIds: gridState.columnOrder.orderedColIds.map(unwrapV5ColId) }
201
+ : undefined,
202
+ sort: gridState.sort
203
+ ? {
204
+ sortModel: gridState.sort.sortModel.map((s) => ({
205
+ colId: unwrapV5ColId(s.colId),
206
+ sort: s.sort,
207
+ })),
208
+ }
209
+ : undefined,
210
+ columnVisibility: gridState.columnVisibility
211
+ ? { hiddenColIds: gridState.columnVisibility.hiddenColIds.map(unwrapV5ColId) }
212
+ : undefined,
213
+ };
214
+ }
215
+
216
+ /** Migrate v4 state to v6: convert per-column filters to tree-based format (skips v5). */
217
+ function migrateV4toV6(
155
218
  state: Extract<PlDataTableStateV2, { version: 4 }>,
156
219
  ): PlDataTableStateV2Normalized {
157
220
  let idCounter = 0;
@@ -183,7 +246,7 @@ function migrateV4toV5(
183
246
  : undefined;
184
247
 
185
248
  return {
186
- version: 5,
249
+ version: 6,
187
250
  stateCache: migratedCache,
188
251
  pTableParams:
189
252
  currentCache && oldSourceId
@@ -283,7 +346,7 @@ export function createDefaultPTableParams(): PTableParamsV2 {
283
346
 
284
347
  export function createPlDataTableStateV2(): PlDataTableStateV2Normalized {
285
348
  return {
286
- version: 5,
349
+ version: 6,
287
350
  stateCache: [],
288
351
  pTableParams: createDefaultPTableParams(),
289
352
  };
@@ -1,159 +1,20 @@
1
- import type {
2
- AxisId,
3
- AxisSpec,
4
- CanonicalizedJson,
5
- ListOptionBase,
6
- PTableColumnSpec,
7
- PTableSorting,
8
- PColumnIdAndSpec,
9
- PTableHandle,
10
- RootFilterSpec,
11
- PTableColumnId,
12
- PFrameHandle,
13
- } from "@milaboratories/pl-model-common";
14
- import type { FilterSpecLeaf } from "../../filters";
15
- import { Nil } from "@milaboratories/helpers";
16
-
17
- export type PlTableColumnId = {
18
- /** Original column spec */
1
+ import type { CanonicalizedJson, PTableColumnSpec } from "@milaboratories/pl-model-common";
2
+
3
+ /**
4
+ * v5 colId wrapper. v5 stored every grid colId as `{ source, labeled }` —
5
+ * `source` was the original column spec, `labeled` was the spec with
6
+ * labeled axes substituted by their label columns. v6 dropped the wrapper
7
+ * and stores bare `PTableColumnSpec`. Kept here for the v5→v6 migration.
8
+ */
9
+ export type PlDataTableV5ColIdWrapper = {
19
10
  source: PTableColumnSpec;
20
- /** Column spec with labeled axes replaced by label columns */
21
11
  labeled: PTableColumnSpec;
22
12
  };
23
13
 
24
- export type PlTableColumnIdJson = CanonicalizedJson<PlTableColumnId>;
25
-
26
- export type PlDataTableGridStateCore = {
27
- /** Includes column ordering */
28
- columnOrder?: {
29
- /** All colIds in order */
30
- orderedColIds: PlTableColumnIdJson[];
31
- };
32
- /** Includes current sort columns and direction */
33
- sort?: {
34
- /** Sorted columns and directions in order */
35
- sortModel: {
36
- /** Column Id to apply the sort to. */
37
- colId: PlTableColumnIdJson;
38
- /** Sort direction */
39
- sort: "asc" | "desc";
40
- }[];
41
- };
42
- /** Includes column visibility */
43
- columnVisibility?: {
44
- /** All colIds which were hidden */
45
- hiddenColIds: PlTableColumnIdJson[];
46
- };
47
- };
48
-
49
- export type PlDataTableSheet = {
50
- /** spec of the axis to use */
51
- axis: AxisSpec;
52
- /** options to show in the filter dropdown */
53
- options: ListOptionBase<string | number>[];
54
- /** default (selected) value */
55
- defaultValue?: string | number;
56
- };
57
-
58
- export type PlDataTableSheetState = {
59
- /** id of the axis */
60
- axisId: AxisId;
61
- /** selected value */
62
- value: string | number;
63
- };
64
-
65
- /** Tree-based filter state compatible with PlAdvancedFilter's RootFilter */
66
- export type PlDataTableFilterMeta = {
67
- id: number;
68
- source?: "table-filter" | "table-search";
69
- isExpanded?: boolean;
70
- isSuppressed?: boolean;
71
- };
72
- export type PlDataTableFilterSpecLeaf = FilterSpecLeaf<CanonicalizedJson<PTableColumnId>>;
73
- export type PlDataTableFilters = RootFilterSpec<PlDataTableFilterSpecLeaf>;
74
- export type PlDataTableFiltersWithMeta = RootFilterSpec<
75
- PlDataTableFilterSpecLeaf,
76
- PlDataTableFilterMeta
77
- >;
78
-
79
- export type PlDataTableStateV2CacheEntry = {
80
- /** DataSource identifier for state management */
81
- sourceId: string;
82
- /** Internal ag-grid state */
83
- gridState: PlDataTableGridStateCore;
84
- /** Sheets state */
85
- sheetsState: PlDataTableSheetState[];
86
- /** User filters state (tree-based, compatible with PlAdvancedFilter) */
87
- filtersState: null | PlDataTableFiltersWithMeta;
88
- /** Default filters state from model (snapshot of defaults) */
89
- defaultFiltersState: null | PlDataTableFiltersWithMeta;
90
- /** Fast search string */
91
- searchString?: string;
92
- };
93
-
94
- export type PTableParamsV2 =
95
- | {
96
- sourceId: null;
97
- hiddenColIds: null;
98
- sorting: [];
99
- filters: null;
100
- defaultFilters: null;
101
- }
102
- | {
103
- sourceId: string;
104
- hiddenColIds: null | PTableColumnId[];
105
- sorting: PTableSorting[];
106
- filters: null | PlDataTableFilters;
107
- defaultFilters: null | PlDataTableFilters;
108
- };
109
-
110
- export type PlDataTableStateV2Normalized = {
111
- /** Version for upgrades */
112
- version: 5;
113
- /** Internal states, LRU cache for 5 sourceId-s */
114
- stateCache: PlDataTableStateV2CacheEntry[];
115
- /** PTable params derived from the cache state for the current sourceId */
116
- pTableParams: PTableParamsV2;
117
- };
118
-
119
- /** PlAgDataTable model */
120
- export type PlDataTableModel = {
121
- /** DataSource identifier for state management */
122
- sourceId: null | string;
123
- /** p-table including all columns, used to show the full specification of the table */
124
- fullTableHandle?: PTableHandle;
125
- /** p-frame handle */
126
- fullPframeHandle?: PFrameHandle;
127
- /** p-table including only visible columns, used to get the data */
128
- visibleTableHandle?: PTableHandle;
129
- /** Default filters from model options, surfaced for UI display */
130
- defaultFilters?: Nil | PlDataTableFilters;
131
- };
132
-
133
- export type CreatePlDataTableOps = {
134
- /** Filters for columns and non-partitioned axes */
135
- filters?: PlDataTableFilters;
136
-
137
- /** Sorting to columns hidden from user */
138
- sorting?: PTableSorting[];
139
-
140
- /**
141
- * Selects columns for which will be inner-joined to the table.
142
- *
143
- * Default behaviour: all columns are considered to be core
144
- */
145
- coreColumnPredicate?: (spec: PColumnIdAndSpec) => boolean;
14
+ export type PlDataTableV5ColIdJson = CanonicalizedJson<PlDataTableV5ColIdWrapper>;
146
15
 
147
- /**
148
- * Determines how core columns should be joined together:
149
- * inner - so user will only see records present in all core columns
150
- * full - so user will only see records present in any of the core columns
151
- *
152
- * All non-core columns will be left joined to the table produced by the core
153
- * columns, in other words records form the pool of non-core columns will only
154
- * make their way into the final table if core table contains corresponding key.
155
- *
156
- * Default: 'full'
157
- */
158
- coreJoinType?: "inner" | "full";
16
+ export type PlDataTableGridStateV5 = {
17
+ columnOrder?: { orderedColIds: PlDataTableV5ColIdJson[] };
18
+ sort?: { sortModel: { colId: PlDataTableV5ColIdJson; sort: "asc" | "desc" }[] };
19
+ columnVisibility?: { hiddenColIds: PlDataTableV5ColIdJson[] };
159
20
  };