@platforma-sdk/model 1.65.9 → 1.66.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 (121) hide show
  1. package/dist/block_model.cjs +8 -11
  2. package/dist/block_model.cjs.map +1 -1
  3. package/dist/block_model.d.ts.map +1 -1
  4. package/dist/block_model.js +8 -10
  5. package/dist/block_model.js.map +1 -1
  6. package/dist/columns/column_collection_builder.cjs +61 -74
  7. package/dist/columns/column_collection_builder.cjs.map +1 -1
  8. package/dist/columns/column_collection_builder.d.ts +16 -22
  9. package/dist/columns/column_collection_builder.d.ts.map +1 -1
  10. package/dist/columns/column_collection_builder.js +62 -75
  11. package/dist/columns/column_collection_builder.js.map +1 -1
  12. package/dist/columns/column_selector.cjs.map +1 -1
  13. package/dist/columns/column_selector.d.ts +1 -1
  14. package/dist/columns/column_selector.js.map +1 -1
  15. package/dist/columns/column_snapshot.cjs.map +1 -1
  16. package/dist/columns/column_snapshot.d.ts +4 -4
  17. package/dist/columns/column_snapshot.d.ts.map +1 -1
  18. package/dist/columns/column_snapshot.js.map +1 -1
  19. package/dist/columns/ctx_column_sources.cjs.map +1 -1
  20. package/dist/columns/ctx_column_sources.d.ts +1 -1
  21. package/dist/columns/ctx_column_sources.d.ts.map +1 -1
  22. package/dist/columns/ctx_column_sources.js.map +1 -1
  23. package/dist/components/PlDataTable/createPlDataTable/createPTableDefV2.cjs +2 -2
  24. package/dist/components/PlDataTable/createPlDataTable/createPTableDefV2.cjs.map +1 -1
  25. package/dist/components/PlDataTable/createPlDataTable/createPTableDefV2.js +2 -2
  26. package/dist/components/PlDataTable/createPlDataTable/createPTableDefV2.js.map +1 -1
  27. package/dist/components/PlDataTable/createPlDataTable/createPTableDefV3.cjs +17 -18
  28. package/dist/components/PlDataTable/createPlDataTable/createPTableDefV3.cjs.map +1 -1
  29. package/dist/components/PlDataTable/createPlDataTable/createPTableDefV3.js +17 -18
  30. package/dist/components/PlDataTable/createPlDataTable/createPTableDefV3.js.map +1 -1
  31. package/dist/components/PlDataTable/createPlDataTable/createPlDataTableV3.cjs +99 -91
  32. package/dist/components/PlDataTable/createPlDataTable/createPlDataTableV3.cjs.map +1 -1
  33. package/dist/components/PlDataTable/createPlDataTable/createPlDataTableV3.d.ts +16 -16
  34. package/dist/components/PlDataTable/createPlDataTable/createPlDataTableV3.d.ts.map +1 -1
  35. package/dist/components/PlDataTable/createPlDataTable/createPlDataTableV3.js +102 -94
  36. package/dist/components/PlDataTable/createPlDataTable/createPlDataTableV3.js.map +1 -1
  37. package/dist/components/PlDataTable/createPlDataTable/discoverColumns.cjs +32 -23
  38. package/dist/components/PlDataTable/createPlDataTable/discoverColumns.cjs.map +1 -1
  39. package/dist/components/PlDataTable/createPlDataTable/discoverColumns.d.ts +5 -5
  40. package/dist/components/PlDataTable/createPlDataTable/discoverColumns.d.ts.map +1 -1
  41. package/dist/components/PlDataTable/createPlDataTable/discoverColumns.js +33 -24
  42. package/dist/components/PlDataTable/createPlDataTable/discoverColumns.js.map +1 -1
  43. package/dist/components/PlDataTable/createPlDataTable/index.cjs.map +1 -1
  44. package/dist/components/PlDataTable/createPlDataTable/index.d.ts +2 -3
  45. package/dist/components/PlDataTable/createPlDataTable/index.d.ts.map +1 -1
  46. package/dist/components/PlDataTable/createPlDataTable/index.js.map +1 -1
  47. package/dist/components/PlDataTable/createPlDataTable/utils.cjs +133 -16
  48. package/dist/components/PlDataTable/createPlDataTable/utils.cjs.map +1 -1
  49. package/dist/components/PlDataTable/createPlDataTable/utils.d.ts +8 -6
  50. package/dist/components/PlDataTable/createPlDataTable/utils.d.ts.map +1 -1
  51. package/dist/components/PlDataTable/createPlDataTable/utils.js +130 -17
  52. package/dist/components/PlDataTable/createPlDataTable/utils.js.map +1 -1
  53. package/dist/components/PlDataTable/labels.cjs +1 -2
  54. package/dist/components/PlDataTable/labels.cjs.map +1 -1
  55. package/dist/components/PlDataTable/labels.js +1 -2
  56. package/dist/components/PlDataTable/labels.js.map +1 -1
  57. package/dist/filters/distill.cjs +73 -30
  58. package/dist/filters/distill.cjs.map +1 -1
  59. package/dist/filters/distill.d.ts.map +1 -1
  60. package/dist/filters/distill.js +73 -30
  61. package/dist/filters/distill.js.map +1 -1
  62. package/dist/index.cjs +19 -15
  63. package/dist/index.d.ts +4 -2
  64. package/dist/index.js +6 -4
  65. package/dist/labels/derive_distinct_tooltips.cjs +85 -0
  66. package/dist/labels/derive_distinct_tooltips.cjs.map +1 -0
  67. package/dist/labels/derive_distinct_tooltips.d.ts +17 -0
  68. package/dist/labels/derive_distinct_tooltips.d.ts.map +1 -0
  69. package/dist/labels/derive_distinct_tooltips.js +84 -0
  70. package/dist/labels/derive_distinct_tooltips.js.map +1 -0
  71. package/dist/labels/index.cjs +1 -0
  72. package/dist/labels/index.d.ts +2 -1
  73. package/dist/labels/index.js +1 -0
  74. package/dist/package.cjs +1 -1
  75. package/dist/package.js +1 -1
  76. package/dist/render/api.cjs +8 -13
  77. package/dist/render/api.cjs.map +1 -1
  78. package/dist/render/api.d.ts +8 -11
  79. package/dist/render/api.d.ts.map +1 -1
  80. package/dist/render/api.js +8 -13
  81. package/dist/render/api.js.map +1 -1
  82. package/dist/services/get_services.cjs +19 -0
  83. package/dist/services/get_services.cjs.map +1 -0
  84. package/dist/services/get_services.d.ts +7 -0
  85. package/dist/services/get_services.d.ts.map +1 -0
  86. package/dist/services/get_services.js +19 -0
  87. package/dist/services/get_services.js.map +1 -0
  88. package/dist/services/index.cjs +1 -0
  89. package/dist/services/index.d.ts +2 -1
  90. package/dist/services/index.js +1 -0
  91. package/dist/services/service_bridge.cjs +4 -4
  92. package/dist/services/service_bridge.cjs.map +1 -1
  93. package/dist/services/service_bridge.d.ts +4 -4
  94. package/dist/services/service_bridge.d.ts.map +1 -1
  95. package/dist/services/service_bridge.js +4 -4
  96. package/dist/services/service_bridge.js.map +1 -1
  97. package/package.json +6 -6
  98. package/src/block_model.ts +8 -11
  99. package/src/columns/column_collection_builder.test.ts +75 -30
  100. package/src/columns/column_collection_builder.ts +96 -133
  101. package/src/columns/column_selector.ts +1 -1
  102. package/src/columns/column_snapshot.ts +7 -4
  103. package/src/columns/ctx_column_sources.ts +1 -3
  104. package/src/components/PFrameForGraphs.test.ts +4 -4
  105. package/src/components/PlDataTable/createPlDataTable/createPTableDefV2.ts +2 -2
  106. package/src/components/PlDataTable/createPlDataTable/createPTableDefV3.ts +44 -21
  107. package/src/components/PlDataTable/createPlDataTable/createPlDataTableV3.ts +202 -218
  108. package/src/components/PlDataTable/createPlDataTable/discoverColumns.ts +69 -56
  109. package/src/components/PlDataTable/createPlDataTable/index.ts +6 -7
  110. package/src/components/PlDataTable/createPlDataTable/utils.test.ts +97 -1
  111. package/src/components/PlDataTable/createPlDataTable/utils.ts +190 -35
  112. package/src/components/PlDataTable/labels.ts +3 -7
  113. package/src/filters/distill.test.ts +91 -0
  114. package/src/filters/distill.ts +102 -46
  115. package/src/labels/derive_distinct_tooltips.test.ts +233 -0
  116. package/src/labels/derive_distinct_tooltips.ts +130 -0
  117. package/src/labels/index.ts +1 -0
  118. package/src/render/api.ts +15 -50
  119. package/src/services/get_services.ts +28 -0
  120. package/src/services/index.ts +1 -0
  121. package/src/services/service_bridge.ts +5 -5
@@ -10,51 +10,51 @@ import type {
10
10
  PTableSorting,
11
11
  PColumnSpec,
12
12
  MultiColumnSelector,
13
- SUniversalPColumnId,
14
- } from "@milaboratories/pl-model-common";
15
- import {
16
- canonicalizeJson,
17
- getAxisId,
18
- getColumnIdAndSpec,
19
- parseJson,
20
- uniqueBy,
13
+ PFrameSpecDriver,
14
+ DiscoveredPColumnId,
21
15
  } from "@milaboratories/pl-model-common";
16
+ import { canonicalizeJson, getAxisId, parseJson, uniqueBy } from "@milaboratories/pl-model-common";
22
17
  import { collectFilterSpecColumns, traverseFilterSpec } from "../../../filters/traverse";
23
18
  import type { RenderCtxBase, PColumnDataUniversal } from "../../../render";
24
19
  import { isEmpty } from "es-toolkit/compat";
25
20
  import type { PlDataTableFilters, PlDataTableFilterSpecLeaf, PlDataTableModel } from "../typesV5";
26
21
  import { upgradePlDataTableStateV2 } from "../state-migration";
27
22
  import type { PlDataTableStateV2 } from "../state-migration";
28
- import type { ColumnMatch, ColumnSnapshot, MatchingMode } from "../../../columns";
29
- import { Services, type RequireServices } from "@milaboratories/pl-model-common";
23
+ import type {
24
+ ColumnSelector,
25
+ ColumnSnapshot,
26
+ MatchingMode,
27
+ MatchQualifications,
28
+ MatchVariant,
29
+ } from "../../../columns";
30
30
  import { getAllLabelColumns, getMatchingLabelColumns } from "../labels";
31
31
  import type { DeriveLabelsOptions } from "../../../labels/derive_distinct_labels";
32
32
  import {
33
33
  deriveAllLabels,
34
+ deriveAllTooltips,
35
+ evaluateRules,
34
36
  isColumnHidden,
35
37
  isColumnOptional,
38
+ withHidenAxesAnnotations,
36
39
  withLabelAnnotations,
37
40
  withTableVisualAnnotations,
41
+ withInfoAnnotations,
38
42
  } from "./utils";
43
+ import type { PrimaryEntry, SecondaryGroup } from "./createPTableDefV3";
39
44
  import { createPTableDefV3 } from "./createPTableDefV3";
40
- import { discoverTableColumnSnaphots, type DiscoveredTableColumnOptions } from "./discoverColumns";
41
- import { isNil, RequiredBy, throwError, type Nil } from "@milaboratories/helpers";
42
-
43
- export type createPlDataTableOptionsV3 = (
44
- | {
45
- discoverColumnOptions: DiscoveredTableColumnOptions;
46
- }
47
- | {
48
- columns: Nil | TableColumnSnapshot<SUniversalPColumnId>[];
49
- }
50
- ) & {
45
+ import { discoverTableColumnSnaphots, type DiscoverTableColumnOptions } from "./discoverColumns";
46
+ import { isNil, isPlainObject, throwError, type Nil } from "@milaboratories/helpers";
47
+
48
+ export type createPlDataTableOptionsV3 = {
49
+ tableState?: PlDataTableStateV2;
50
+
51
+ columns: Nil | DiscoverTableColumnOptions | TableColumnSnapshot[];
51
52
  filters?: PlDataTableFilters;
52
53
  sorting?: PTableSorting[];
53
54
  primaryJoinType?: "inner" | "full";
54
55
 
55
- tableState?: PlDataTableStateV2;
56
56
  labelsOptions?: DeriveLabelsOptions;
57
- columnsDisplayOptions?: ColumnsDisplayOptions;
57
+ displayOptions?: ColumnsDisplayOptions;
58
58
  };
59
59
 
60
60
  /** Structured source config — selectors/anchors instead of raw ColumnSource. */
@@ -73,13 +73,13 @@ export type ColumnsDisplayOptions = {
73
73
  };
74
74
 
75
75
  export type ColumnOrderRule = {
76
- match: ColumnMatcher;
76
+ match: ColumnMatcher | ColumnSelector;
77
77
  /** Higher number = further left in table */
78
78
  priority: number;
79
79
  };
80
80
 
81
81
  export type ColumnVisibilityRule = {
82
- match: ColumnMatcher;
82
+ match: ColumnMatcher | ColumnSelector;
83
83
  visibility: "default" | "optional" | "hidden";
84
84
  };
85
85
 
@@ -87,23 +87,25 @@ export type ColumnMatcher = (spec: PColumnSpec) => boolean;
87
87
 
88
88
  // Main Function
89
89
 
90
- export function createPlDataTableV3<A, U, S extends RequireServices<typeof Services.PFrameSpec>>(
91
- ctx: RenderCtxBase<A, U, S>,
90
+ export function createPlDataTableV3<A, U>(
91
+ ctx: RenderCtxBase<A, U>,
92
92
  options: createPlDataTableOptionsV3,
93
93
  ): PlDataTableModel | undefined {
94
+ const pframeSpec = ctx.getService("pframeSpec");
94
95
  const state = upgradePlDataTableStateV2(options.tableState);
95
96
  const primaryJoinType = options.primaryJoinType ?? "full";
96
97
 
97
- const discovered =
98
- "discoverColumnOptions" in options
99
- ? discoverTableColumnSnaphots(ctx, options.discoverColumnOptions)
100
- : options.columns;
98
+ const discovered = isPlainObject(options.columns)
99
+ ? discoverTableColumnSnaphots(ctx, options.columns)
100
+ : options.columns;
101
101
  if (isNil(discovered) || discovered.length === 0) return undefined;
102
102
 
103
103
  const splited = splitDiscoveredColumns(discovered);
104
- const resolved = resolveDiscoveredColumns(splited, discovered);
105
104
 
106
- const labelColumns = getMatchingLabelColumns(resolved.all, getAllLabelColumns(ctx));
105
+ const labelColumns = getMatchingLabelColumns(
106
+ [...splited.direct, ...splited.linked],
107
+ getAllLabelColumns(ctx),
108
+ );
107
109
 
108
110
  const derivedLabels = deriveAllLabels({
109
111
  columns: discovered.map((dc) => ({
@@ -118,24 +120,34 @@ export function createPlDataTableV3<A, U, S extends RequireServices<typeof Servi
118
120
  },
119
121
  });
120
122
 
121
- const annotated = annotateColumnGroups(
122
- resolved,
123
+ const derivedTooltips = deriveAllTooltips({
124
+ columns: discovered.map((dc) => ({
125
+ id: dc.id,
126
+ originalId: dc.originalId,
127
+ spec: dc.spec,
128
+ linkerPath: dc.linkerPath,
129
+ qualifications: dc.qualifications,
130
+ distinctiveQualifications: dc.distinctiveQualifications,
131
+ })),
132
+ });
133
+
134
+ const annotated = annotateColumnGroups({
135
+ pframeSpec,
136
+ ...splited,
123
137
  labelColumns,
124
138
  derivedLabels,
125
- options.columnsDisplayOptions,
126
- );
139
+ derivedTooltips,
140
+ displayOptions: options.displayOptions,
141
+ });
127
142
 
128
- const primaryColumnIds = new Set<PObjectId>(
129
- discovered.filter((dc) => dc.isPrimary).map((dc) => dc.id),
130
- );
131
- const primaryColumns = annotated.direct.filter((c) => primaryColumnIds.has(c.id));
132
- const secondaryColumns = annotated.direct.filter((c) => !primaryColumnIds.has(c.id));
143
+ const primarySnapshots = annotated.direct.filter((c) => c.isPrimary);
144
+ const secondarySnapshots = annotated.direct.filter((c) => !c.isPrimary);
133
145
 
134
- if (primaryColumns.length === 0) return undefined;
146
+ if (primarySnapshots.length === 0) return undefined;
135
147
 
136
148
  const columnIsAvailable = createColumnValidationById([
137
149
  ...annotated.direct,
138
- ...annotated.linked.flatMap((lc) => [...(annotated.linkers.get(lc.id) ?? []), lc]),
150
+ ...annotated.linked.flatMap((lc) => [...(lc.linkerPath ?? []).map((s) => s.linker), lc]),
139
151
  ...annotated.labels,
140
152
  ]);
141
153
 
@@ -152,24 +164,25 @@ export function createPlDataTableV3<A, U, S extends RequireServices<typeof Servi
152
164
  );
153
165
  validateSorting(sorting, columnIsAvailable);
154
166
 
167
+ const primaryEntries: PrimaryEntry<undefined | PColumnDataUniversal>[] = primarySnapshots.map(
168
+ (snap) => ({ column: resolveSnapshot(snap) }),
169
+ );
155
170
  const fullDef = createPTableDefV3({
156
171
  primaryJoinType,
157
- primaryColumns,
158
- secondaryGroups: [
159
- ...secondaryColumns.map((c) => [c]),
160
- ...annotated.linked.map((lc) => [...(annotated.linkers.get(lc.id) ?? []), lc]),
161
- ...annotated.labels.map((c) => [c]),
162
- ],
172
+ primary: primaryEntries,
173
+ secondary: buildSecondaryGroups(secondarySnapshots, annotated.linked, annotated.labels),
163
174
  filters,
164
175
  sorting,
165
176
  });
166
177
 
167
178
  const fullHandle = ctx.createPTableV2(fullDef);
179
+ // TODO: is workaround for dropdown suggestions.
180
+ // Pframe have not equivalent data for columns relativly to Ptable
168
181
  const pframeHandle = ctx.createPFrame([
169
- ...annotated.direct,
170
- ...annotated.linked,
182
+ ...annotated.direct.map(resolveSnapshot),
183
+ ...annotated.linked.map(resolveSnapshot),
171
184
  ...annotated.labels,
172
- ...uniqueBy([...annotated.linkers.values()].flat(), (c) => c.id),
185
+ ...collectLinkerSnapshots(annotated.linked).map(resolveSnapshot),
173
186
  ]);
174
187
 
175
188
  const hiddenSpecs = state.pTableParams.hiddenColIds;
@@ -181,22 +194,14 @@ export function createPlDataTableV3<A, U, S extends RequireServices<typeof Servi
181
194
  );
182
195
 
183
196
  const visible = buildVisibleColumns(annotated, hiddenColumnIds, labelColumns);
184
- const visibleNonCoreDirect = secondaryColumns.filter((c) => !hiddenColumnIds.has(c.id));
185
- const visibleLinkedGroups = buildVisibleLinkedGroups(
186
- visible.direct,
187
- visible.linked,
188
- annotated.linkers,
189
- hiddenSpecs,
190
- );
191
-
192
197
  const visibleDef = createPTableDefV3({
193
198
  primaryJoinType,
194
- primaryColumns,
195
- secondaryGroups: [
196
- ...visibleNonCoreDirect.map((c) => [c]),
197
- ...visibleLinkedGroups,
198
- ...visible.labels.map((c) => [c]),
199
- ],
199
+ primary: primaryEntries,
200
+ secondary: buildSecondaryGroups(
201
+ visible.direct.filter((c) => !c.isPrimary),
202
+ visible.linked,
203
+ visible.labels,
204
+ ),
200
205
  filters,
201
206
  sorting,
202
207
  });
@@ -212,97 +217,128 @@ export function createPlDataTableV3<A, U, S extends RequireServices<typeof Servi
212
217
  }
213
218
 
214
219
  /** A single column discovered from sources — normalized from raw ColumnSnapshot/ColumnMatch. */
215
- export type TableColumnSnapshot<Id extends PObjectId | SUniversalPColumnId> = ColumnSnapshot<Id> & {
220
+ export type TableColumnSnapshot = ColumnSnapshot<DiscoveredPColumnId> & {
221
+ readonly originalId: PObjectId;
216
222
  readonly isPrimary?: boolean;
217
- readonly originalId?: PObjectId;
218
- readonly linkerPath?: ColumnMatch["path"];
223
+ readonly linkerPath?: MatchVariant["path"];
224
+ readonly qualifications?: MatchQualifications;
225
+ readonly distinctiveQualifications?: MatchQualifications;
219
226
  };
220
227
 
221
- type TableColumn = PColumn<undefined | PColumnDataUniversal>;
222
-
223
228
  type SplitDiscoveredColumns = {
224
- readonly direct: TableColumnSnapshot<SUniversalPColumnId>[];
225
- readonly linked: TableColumnSnapshot<SUniversalPColumnId>[];
226
- };
227
-
228
- type ResolvedColumns = {
229
- readonly direct: TableColumn[];
230
- readonly linked: TableColumn[];
231
- readonly linkers: Map<PObjectId, TableColumn[]>;
232
- readonly all: TableColumn[];
229
+ readonly direct: TableColumnSnapshot[];
230
+ readonly linked: TableColumnSnapshot[];
233
231
  };
234
232
 
235
233
  type AnnotatedColumnGroups = {
236
- readonly direct: TableColumn[];
237
- readonly linked: TableColumn[];
238
- readonly linkers: Map<PObjectId, TableColumn[]>;
239
- readonly labels: TableColumn[];
234
+ readonly direct: TableColumnSnapshot[];
235
+ readonly linked: TableColumnSnapshot[];
236
+ readonly labels: PColumn<PColumnDataUniversal>[];
240
237
  };
241
238
 
242
239
  type VisibleColumns = {
243
- readonly direct: TableColumn[];
244
- readonly linked: TableColumn[];
240
+ readonly direct: TableColumnSnapshot[];
241
+ readonly linked: TableColumnSnapshot[];
245
242
  readonly labels: PColumn<PColumnDataUniversal>[];
246
243
  };
247
244
 
248
245
  /** Split discovered columns into direct (no linker path) and linked (with linker path). */
249
- function splitDiscoveredColumns(
250
- columns: TableColumnSnapshot<SUniversalPColumnId>[],
251
- ): SplitDiscoveredColumns {
252
- return {
253
- direct: columns.filter((dc) => isNil(dc.linkerPath) || dc.linkerPath.length === 0),
254
- linked: columns.filter((dc) => !isNil(dc.linkerPath) && dc.linkerPath.length > 0),
255
- };
246
+ function splitDiscoveredColumns(columns: TableColumnSnapshot[]): SplitDiscoveredColumns {
247
+ const direct = columns.filter((dc) => isNil(dc.linkerPath) || dc.linkerPath.length === 0);
248
+ const linked = columns.filter((dc) => !isNil(dc.linkerPath) && dc.linkerPath.length > 0);
249
+ return { direct, linked };
256
250
  }
257
251
 
258
- /** Resolve DiscoveredColumn snapshots into PColumn objects with lazily-evaluated data. */
259
- function resolveDiscoveredColumns(
260
- split: SplitDiscoveredColumns,
261
- allDiscovered: TableColumnSnapshot<SUniversalPColumnId>[],
262
- ): ResolvedColumns {
263
- const linked = split.linked.map(resolveSnapshot);
264
- const linkers = new Map<PObjectId, TableColumn[]>(
265
- split.linked
266
- .filter(
267
- (dc): dc is RequiredBy<TableColumnSnapshot<SUniversalPColumnId>, "linkerPath"> =>
268
- !isNil(dc.linkerPath),
269
- )
270
- .map((dc, i) => [linked[i].id, dc.linkerPath.map((s) => resolveSnapshot(s.linker))]),
252
+ /** All linker snapshots across the given linked columns, deduped by id. */
253
+ function collectLinkerSnapshots(linked: TableColumnSnapshot[]): ColumnSnapshot<PObjectId>[] {
254
+ return uniqueBy(
255
+ linked.flatMap((lc) => (lc.linkerPath ?? []).map((s) => s.linker)),
256
+ (c) => c.id,
271
257
  );
272
-
273
- return {
274
- all: allDiscovered.map(resolveSnapshot),
275
- direct: split.direct.map(resolveSnapshot),
276
- linked,
277
- linkers,
278
- };
279
258
  }
280
259
 
281
- /** Annotate all column groups with derived labels and display options. */
282
- function annotateColumnGroups(
283
- resolved: ResolvedColumns,
284
- labelColumns: PColumn<PColumnDataUniversal>[],
285
- derivedLabels: Record<string, string>,
286
- displayOptions: ColumnsDisplayOptions | undefined,
287
- ): AnnotatedColumnGroups {
288
- const direct = withTableVisualAnnotations(
289
- displayOptions,
290
- withLabelAnnotations(derivedLabels, resolved.direct),
291
- );
292
- const linked = withTableVisualAnnotations(
260
+ /**
261
+ * Annotate all column groups with derived labels and display-rule annotations.
262
+ * Evaluates `displayOptions` rules against all discovered columns (direct,
263
+ * linked, labels, linkers) and writes the winning visibility/priority into
264
+ * column annotations via `withTableVisualAnnotations`.
265
+ */
266
+ function annotateColumnGroups(params: {
267
+ direct: TableColumnSnapshot[];
268
+ linked: TableColumnSnapshot[];
269
+ labelColumns: PColumn<PColumnDataUniversal>[];
270
+ derivedLabels: Record<string, string>;
271
+ derivedTooltips: Record<string, string>;
272
+ displayOptions?: ColumnsDisplayOptions;
273
+ pframeSpec: PFrameSpecDriver;
274
+ }): AnnotatedColumnGroups {
275
+ const {
276
+ direct,
277
+ linked,
278
+ labelColumns,
279
+ derivedLabels,
280
+ derivedTooltips,
293
281
  displayOptions,
294
- withLabelAnnotations(derivedLabels, resolved.linked),
282
+ pframeSpec,
283
+ } = params;
284
+
285
+ const allColumnsForRules = [
286
+ ...direct,
287
+ ...linked,
288
+ ...labelColumns,
289
+ ...collectLinkerSnapshots(linked),
290
+ ];
291
+ const visibilityByColId = evaluateRules(
292
+ displayOptions?.visibility ?? [],
293
+ allColumnsForRules,
294
+ pframeSpec,
295
295
  );
296
- const linkers = new Map<PObjectId, TableColumn[]>(
297
- [...resolved.linkers].map(([id, cols]) => [id, withLabelAnnotations(derivedLabels, cols)]),
296
+ const orderByColId = evaluateRules(
297
+ displayOptions?.ordering ?? [],
298
+ allColumnsForRules,
299
+ pframeSpec,
298
300
  );
299
- const labels = withLabelAnnotations(derivedLabels, labelColumns);
300
301
 
301
- return { direct, linked, linkers, labels };
302
+ const directAnnotated = [
303
+ withLabelAnnotations.bind(null, derivedLabels),
304
+ withInfoAnnotations.bind(null, derivedTooltips),
305
+ withTableVisualAnnotations.bind(null, visibilityByColId, orderByColId),
306
+ ].reduce((cols, fn) => fn(cols) as TableColumnSnapshot[], direct);
307
+
308
+ const linkedAnnotated = [
309
+ withLabelAnnotations.bind(null, derivedLabels),
310
+ withInfoAnnotations.bind(null, derivedTooltips),
311
+ withTableVisualAnnotations.bind(null, visibilityByColId, orderByColId),
312
+ (cols: TableColumnSnapshot[]) =>
313
+ cols.map((lc) => ({ ...lc, linkerPath: annotateLinkerPath(derivedLabels, lc.linkerPath) })),
314
+ ].reduce((cols, fn) => fn(cols) as TableColumnSnapshot[], linked);
315
+
316
+ const labelColumnsAnnotated = withLabelAnnotations(derivedLabels, labelColumns);
317
+
318
+ return {
319
+ direct: directAnnotated,
320
+ linked: linkedAnnotated,
321
+ labels: labelColumnsAnnotated,
322
+ };
323
+ }
324
+ function annotateLinkerPath(
325
+ derivedLabels: Record<string, string>,
326
+ path: TableColumnSnapshot["linkerPath"],
327
+ ): TableColumnSnapshot["linkerPath"] {
328
+ if (isNil(path) || path.length === 0) return path;
329
+ const annotatedLinkers = withHidenAxesAnnotations(
330
+ withLabelAnnotations(
331
+ derivedLabels,
332
+ path.map((s) => s.linker),
333
+ ),
334
+ );
335
+ return path.map((s, i) => ({ ...s, linker: annotatedLinkers[i] }));
302
336
  }
303
337
 
304
338
  /** Build an index of all valid column IDs (axes + columns) for filter/sorting validation. */
305
- function createColumnValidationById(fullColumns: TableColumn[]) {
339
+ function createColumnValidationById(
340
+ fullColumns: { readonly id: PObjectId; readonly spec: PColumnSpec }[],
341
+ ) {
306
342
  const axisIds = uniqueBy(
307
343
  fullColumns.flatMap((c) => c.spec.axesSpec.map(getAxisId)),
308
344
  (a) => canonicalizeJson<AxisId>(a),
@@ -365,9 +401,39 @@ function validateSorting(sorting: PTableSorting[], isValidColumnId: (id: string)
365
401
  }
366
402
  }
367
403
 
404
+ function buildSecondaryGroups(
405
+ direct: TableColumnSnapshot[],
406
+ linked: TableColumnSnapshot[],
407
+ labels: PColumn<PColumnDataUniversal>[],
408
+ ): SecondaryGroup<undefined | PColumnDataUniversal>[] {
409
+ return [
410
+ ...direct.map(
411
+ (c): SecondaryGroup<undefined | PColumnDataUniversal> => ({
412
+ entries: [{ column: resolveSnapshot(c), qualifications: c.qualifications?.forHit }],
413
+ primaryQualifications: c.qualifications?.forQueries,
414
+ }),
415
+ ),
416
+ ...linked.map(
417
+ (lc): SecondaryGroup<undefined | PColumnDataUniversal> => ({
418
+ entries: [
419
+ ...(lc.linkerPath ?? []).map((s) => ({
420
+ column: resolveSnapshot(s.linker),
421
+ qualifications: s.qualifications,
422
+ })),
423
+ { column: resolveSnapshot(lc), qualifications: lc.qualifications?.forHit },
424
+ ],
425
+ primaryQualifications: lc.qualifications?.forQueries,
426
+ }),
427
+ ),
428
+ ...labels.map(
429
+ (c): SecondaryGroup<undefined | PColumnDataUniversal> => ({ entries: [{ column: c }] }),
430
+ ),
431
+ ];
432
+ }
433
+
368
434
  /** Determine which columns should be hidden based on state or optional-column defaults. */
369
435
  function computeHiddenColumns(
370
- columns: TableColumn[],
436
+ columns: { readonly id: PObjectId; readonly spec: PColumnSpec }[],
371
437
  sorting: Nil | PTableSorting[],
372
438
  filters: Nil | PlDataTableFilters,
373
439
  hiddenSpecs: Nil | PTableColumnId[],
@@ -382,85 +448,6 @@ function computeHiddenColumns(
382
448
  return new Set(initial.filter((id) => !preserved.has(id)));
383
449
  }
384
450
 
385
- /**
386
- * Build visible linked column groups. Non-hidden groups are included fully;
387
- * hidden groups are trimmed to only the prefix that brings axes not yet
388
- * covered by earlier groups (visible or previously trimmed).
389
- */
390
- function buildVisibleLinkedGroups(
391
- direct: TableColumn[],
392
- linked: TableColumn[],
393
- linkers: Map<PObjectId, TableColumn[]>,
394
- hiddenSpecs: PTableColumnId[] | null,
395
- ): TableColumn[][] {
396
- const result: TableColumn[][] = [];
397
- const coveredAxisIds = new Set<CanonicalizedJson<AxisId>>();
398
-
399
- const collectAxes = (group: TableColumn[]) => {
400
- for (const col of group) {
401
- for (const as of col.spec.axesSpec) {
402
- coveredAxisIds.add(canonicalizeJson(getAxisId(as)));
403
- }
404
- }
405
- };
406
-
407
- collectAxes(direct);
408
-
409
- for (const lc of linked) {
410
- const group = [...(linkers.get(lc.id) ?? []), lc];
411
- result.push(group);
412
- collectAxes(group);
413
- }
414
-
415
- for (const group of linkers.values()) {
416
- const trimmed = trimGroupByVisibleAxes(group, hiddenSpecs, coveredAxisIds);
417
- if (trimmed.length > 0) {
418
- result.push(trimmed);
419
- collectAxes(trimmed);
420
- }
421
- }
422
-
423
- return result;
424
- }
425
-
426
- /**
427
- * For a linked column group [linker1, ..., linkerN, column], find the rightmost
428
- * element that has at least one non-hidden and not-yet-covered axis.
429
- * Return the prefix up to and including that element.
430
- * If no element has such an axis, return empty array.
431
- */
432
- function trimGroupByVisibleAxes(
433
- group: TableColumn[],
434
- hiddenSpecs: PTableColumnId[] | null,
435
- coveredAxisIds: Set<CanonicalizedJson<AxisId>>,
436
- ): TableColumn[] {
437
- if (hiddenSpecs === null) return group;
438
-
439
- const hiddenAxisIds = new Set(
440
- hiddenSpecs
441
- .filter((s): s is PTableColumnIdAxis => s.type === "axis")
442
- .map((s) => canonicalizeJson(s.id)),
443
- );
444
-
445
- const uncoveredAxisIds = new Set(
446
- group
447
- .flatMap((c) => c.spec.axesSpec.map((as) => canonicalizeJson(getAxisId(as))))
448
- .filter((id) => !hiddenAxisIds.has(id) && !coveredAxisIds.has(id)),
449
- );
450
-
451
- let lastNeeded = -1;
452
- for (let i = 0; i < group.length; i++) {
453
- const newAxes = group[i].spec.axesSpec
454
- .map((as) => canonicalizeJson(getAxisId(as)))
455
- .filter((id) => uncoveredAxisIds.has(id));
456
- if (newAxes.length > 0) {
457
- for (const id of newAxes) uncoveredAxisIds.delete(id);
458
- lastNeeded = i;
459
- }
460
- }
461
- return lastNeeded === -1 ? [] : group.slice(0, lastNeeded + 1);
462
- }
463
-
464
451
  /** Collect IDs of columns that must remain visible (sorted, filtered). */
465
452
  function collectPreservedColumnIds(
466
453
  sorting: Nil | PTableSorting[],
@@ -489,10 +476,7 @@ function buildVisibleColumns(
489
476
  ): VisibleColumns {
490
477
  const direct = annotated.direct.filter((c) => !hiddenColumns.has(c.id));
491
478
  const linked = annotated.linked.filter((c) => !hiddenColumns.has(c.id));
492
- const labels = getMatchingLabelColumns(
493
- [...direct, ...linked].map(getColumnIdAndSpec),
494
- originalLabelColumns,
495
- );
479
+ const labels = getMatchingLabelColumns([...direct, ...linked], originalLabelColumns);
496
480
  return { direct, linked, labels };
497
481
  }
498
482
 
@@ -506,7 +490,7 @@ function resolveSnapshot(
506
490
  /** Remap column references in sorting entries. */
507
491
  function remapSortingColumnIds(
508
492
  sorting: Nil | PTableSorting[],
509
- columns: TableColumnSnapshot<PObjectId | SUniversalPColumnId>[],
493
+ columns: TableColumnSnapshot[],
510
494
  ): Nil | PTableSorting[] {
511
495
  return sorting?.map((s) => {
512
496
  if (s.column.type === "axis") return s; // Axis references are unaffected by column ID remapping
@@ -531,7 +515,7 @@ type PlDataTableFilterNode = FilterSpecNode<PlDataTableFilterSpecLeaf>;
531
515
  /** Remap column references in a filter tree. */
532
516
  function remapFilterColumnIds(
533
517
  filters: Nil | PlDataTableFilters,
534
- columns: TableColumnSnapshot<PObjectId | SUniversalPColumnId>[],
518
+ columns: TableColumnSnapshot[],
535
519
  ): Nil | PlDataTableFilters {
536
520
  if (isNil(filters)) return filters;
537
521