@platforma-sdk/model 1.22.97 → 1.23.4

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.
@@ -1,4 +1,4 @@
1
- import { AxisId, Option, PColumn, PColumnSpec, PColumnValues, PFrameDef, PFrameHandle, PObject, PObjectSpec, PSpecPredicate, PTableDef, PTableHandle, PTableRecordFilter, PTableSorting, PlRef, ResultCollection, ValueOrError } from '@milaboratories/pl-model-common';
1
+ import { APColumnSelector, AxisId, Option, PColumn, PColumnSelector, PColumnSpec, PColumnValues, PFrameDef, PFrameHandle, PObject, PObjectSpec, PSpecPredicate, PTableDef, PTableHandle, PTableRecordFilter, PTableSorting, PlRef, ResultCollection, ValueOrError, AnchorIdDeriver } from '@milaboratories/pl-model-common';
2
2
  import { Optional } from 'utility-types';
3
3
  import { TreeNodeAccessor } from './accessor';
4
4
  import { FutureRef } from './future';
@@ -10,7 +10,44 @@ export declare class ResultPool {
10
10
  */
11
11
  calculateOptions(predicate: PSpecPredicate): Option[];
12
12
  private defaultLabelFn;
13
- getOptions(predicate: (spec: PObjectSpec) => boolean, label?: ((spec: PObjectSpec, ref: PlRef) => string) | LabelDerivationOps): Option[];
13
+ getOptions(predicateOrSelector: ((spec: PObjectSpec) => boolean) | PColumnSelector | PColumnSelector[], label?: ((spec: PObjectSpec, ref: PlRef) => string) | LabelDerivationOps): Option[];
14
+ /**
15
+ * Calculates anchored identifier options for columns matching a given predicate.
16
+ *
17
+ * This function filters column specifications from the result pool that match the provided predicate,
18
+ * creates a standardized AnchorCtx from the provided anchors, and generates a list of label-value
19
+ * pairs for UI components (like dropdowns).
20
+ *
21
+ * @param anchorsOrCtx - Either:
22
+ * - An existing AnchorCtx instance
23
+ * - A record mapping anchor IDs to PColumnSpec objects
24
+ * - A record mapping anchor IDs to PlRef objects (which will be resolved to PColumnSpec)
25
+ * @param predicateOrSelector - Either:
26
+ * - A predicate function that takes a PColumnSpec and returns a boolean.
27
+ * Only specs that return true will be included.
28
+ * - An APColumnSelector object for declarative filtering, which will be
29
+ * resolved against the provided anchors and matched using matchPColumn.
30
+ * - An array of APColumnSelector objects - columns matching ANY selector
31
+ * in the array will be included (OR operation).
32
+ * @param labelOps - Optional configuration for label generation:
33
+ * - includeNativeLabel: Whether to include native column labels
34
+ * - separator: String to use between label parts (defaults to " / ")
35
+ * - addLabelAsSuffix: Whether to add labels as suffix instead of prefix
36
+ * @returns An array of objects with `label` (display text) and `value` (anchored ID string) properties,
37
+ * or undefined if any PlRef resolution fails.
38
+ */
39
+ getAnchoredOptions(anchorsOrCtx: AnchorIdDeriver, predicateOrSelectors: ((spec: PColumnSpec) => boolean) | APColumnSelector | APColumnSelector[], labelOps?: LabelDerivationOps): {
40
+ label: string;
41
+ value: string;
42
+ }[];
43
+ getAnchoredOptions(anchorsOrCtx: Record<string, PColumnSpec>, predicateOrSelectors: ((spec: PColumnSpec) => boolean) | APColumnSelector | APColumnSelector[], labelOps?: LabelDerivationOps): {
44
+ label: string;
45
+ value: string;
46
+ }[];
47
+ getAnchoredOptions(anchorsOrCtx: Record<string, PColumnSpec | PlRef>, predicateOrSelectors: ((spec: PColumnSpec) => boolean) | APColumnSelector | APColumnSelector[], labelOps?: LabelDerivationOps): {
48
+ label: string;
49
+ value: string;
50
+ }[] | undefined;
14
51
  /**
15
52
  * @deprecated use getData()
16
53
  */
@@ -1 +1 @@
1
- {"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../../src/render/api.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,MAAM,EACN,MAAM,EACN,OAAO,EACP,WAAW,EACX,aAAa,EACb,SAAS,EACT,YAAY,EACZ,OAAO,EACP,WAAW,EACX,cAAc,EACd,SAAS,EACT,YAAY,EACZ,kBAAkB,EAClB,aAAa,EACb,KAAK,EACL,gBAAgB,EAChB,YAAY,EAQb,MAAM,iCAAiC,CAAC;AACzC,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAEzC,OAAO,EAAE,gBAAgB,EAAS,MAAM,YAAY,CAAC;AACrD,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAErC,OAAO,EAAE,kBAAkB,EAAgB,MAAM,cAAc,CAAC;AAEhE,qBAAa,UAAU;IACrB,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAyC;IAE7D;;OAEG;IACI,gBAAgB,CAAC,SAAS,EAAE,cAAc,GAAG,MAAM,EAAE;IAI5D,OAAO,CAAC,cAAc,CACgC;IAE/C,UAAU,CACf,SAAS,EAAE,CAAC,IAAI,EAAE,WAAW,KAAK,OAAO,EACzC,KAAK,CAAC,EAAE,CAAC,CAAC,IAAI,EAAE,WAAW,EAAE,GAAG,EAAE,KAAK,KAAK,MAAM,CAAC,GAAG,kBAAkB,GACvE,MAAM,EAAE;IAcX;;OAEG;IACI,qBAAqB,IAAI,gBAAgB,CAAC,OAAO,CAAC,gBAAgB,CAAC,CAAC;IAIpE,OAAO,IAAI,gBAAgB,CAAC,OAAO,CAAC,gBAAgB,CAAC,CAAC;IAc7D;;OAEG;IACI,+BAA+B,IAAI,gBAAgB,CACxD,QAAQ,CAAC,OAAO,CAAC,YAAY,CAAC,gBAAgB,EAAE,MAAM,CAAC,CAAC,EAAE,IAAI,CAAC,CAChE;IAIM,iBAAiB,IAAI,gBAAgB,CAC1C,QAAQ,CAAC,OAAO,CAAC,YAAY,CAAC,gBAAgB,EAAE,MAAM,CAAC,CAAC,EAAE,IAAI,CAAC,CAChE;IAiBD;;OAEG;IACI,sBAAsB,IAAI,gBAAgB,CAAC,WAAW,CAAC;IAIvD,QAAQ,IAAI,gBAAgB,CAAC,WAAW,CAAC;IAIhD;;;OAGG;IACI,YAAY,CAAC,GAAG,EAAE,KAAK,GAAG,OAAO,CAAC,gBAAgB,CAAC,GAAG,SAAS;IAYtE;;;;OAIG;IACI,eAAe,CAAC,GAAG,EAAE,KAAK,GAAG,OAAO,CAAC,gBAAgB,CAAC,GAAG,SAAS;IAMzE;;;;OAIG;IACI,mBAAmB,CAAC,GAAG,EAAE,KAAK,GAAG,WAAW,GAAG,SAAS;IAO/D;;;OAGG;IACI,YAAY,CAAC,GAAG,EAAE,KAAK,GAAG,WAAW,GAAG,SAAS;IASxD;;;;OAIG;IACI,0BAA0B,CAAC,IAAI,EAAE,WAAW,GAAG,OAAO,CAAC,gBAAgB,CAAC,EAAE;CA4ClF;AAWD,iGAAiG;AACjG,qBAAa,SAAS,CAAC,IAAI,EAAE,OAAO;IAClC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAqB;IAEzC,SAAgB,IAAI,EAAE,IAAI,CAAC;IAC3B,SAAgB,OAAO,EAAE,OAAO,CAAC;;IASjC,OAAO,CAAC,gBAAgB,CAAC,CAAe;IAExC;;;SAGK;IACL,IAAW,UAAU,IAAI,IAAI,GAAG,SAAS,CAMxC;IAOD,OAAO,CAAC,gBAAgB;IAOxB,IAAW,MAAM,IAAI,gBAAgB,GAAG,SAAS,CAEhD;IAED,IAAW,OAAO,IAAI,gBAAgB,GAAG,SAAS,CAEjD;IAED,SAAgB,UAAU,aAAoB;IAE9C;;;OAGG;IACI,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,GAAG,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS;IA8B5E,OAAO,CAAC,0BAA0B;IAM3B,YAAY,CAAC,GAAG,EAAE,SAAS,CAAC,gBAAgB,GAAG,aAAa,CAAC,GAAG,YAAY;IAO5E,YAAY,CAAC,GAAG,EAAE,SAAS,CAAC,OAAO,CAAC,gBAAgB,GAAG,aAAa,CAAC,CAAC,GAAG,YAAY;IACrF,YAAY,CAAC,GAAG,EAAE;QACvB,OAAO,EAAE,OAAO,CAAC,gBAAgB,GAAG,aAAa,CAAC,EAAE,CAAC;QACrD,OAAO,CAAC,EAAE,kBAAkB,EAAE,CAAC;QAC/B,oBAAoB;QACpB,OAAO,CAAC,EAAE,aAAa,EAAE,CAAC;KAC3B,GAAG,YAAY;IAgChB,iDAAiD;IAC1C,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM;IAItC,wBAAwB,IAAI,MAAM,GAAG,SAAS;CAKtD;AAED,MAAM,MAAM,cAAc,CAAC,IAAI,GAAG,OAAO,EAAE,OAAO,GAAG,OAAO,EAAE,GAAG,GAAG,OAAO,IAAI,CAC7E,IAAI,EAAE,SAAS,CAAC,IAAI,EAAE,OAAO,CAAC,KAC3B,GAAG,CAAC;AAET,MAAM,MAAM,eAAe,CAAC,CAAC,IAC3B,CAAC,SAAS,SAAS,CAAC,MAAM,CAAC,CAAC,GACxB,CAAC,GACD,CAAC,SAAS,MAAM,GAAG,OAAO,GAAG,IAAI,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,SAAS,GACtE,CAAC,GACD;KAAG,GAAG,IAAI,MAAM,CAAC,GAAG,eAAe,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;CAAE,CAAC;AAEtD,MAAM,MAAM,yBAAyB,CAAC,EAAE,SAAS,QAAQ,IAAI,EAAE,SAAS,CAAC,GAAG,IAAI,EAAE,GAAG,KAAK,MAAM,CAAC,GAC7F,eAAe,CAAC,CAAC,CAAC,GAClB,KAAK,CAAC"}
1
+ {"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../../src/render/api.ts"],"names":[],"mappings":"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"}
package/dist/version.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- export declare const PlatformaSDKVersion = "1.22.97";
1
+ export declare const PlatformaSDKVersion = "1.23.4";
2
2
  //# sourceMappingURL=version.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"version.d.ts","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,mBAAmB,YAAY,CAAC"}
1
+ {"version":3,"file":"version.d.ts","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,mBAAmB,WAAW,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@platforma-sdk/model",
3
- "version": "1.22.97",
3
+ "version": "1.23.4",
4
4
  "description": "Platforma.bio SDK / Block Model",
5
5
  "types": "./dist/index.d.ts",
6
6
  "main": "./dist/index.js",
@@ -20,7 +20,7 @@
20
20
  "dependencies": {
21
21
  "utility-types": "^3.11.0",
22
22
  "zod": "~3.23.8",
23
- "@milaboratories/pl-model-common": "^1.10.6"
23
+ "@milaboratories/pl-model-common": "^1.11.0"
24
24
  },
25
25
  "devDependencies": {
26
26
  "typescript": "~5.5.4",
@@ -29,7 +29,8 @@
29
29
  "jest": "^29.7.0",
30
30
  "@jest/globals": "^29.7.0",
31
31
  "ts-jest": "^29.2.6",
32
- "@milaboratories/platforma-build-configs": "1.0.2"
32
+ "@milaboratories/platforma-build-configs": "1.0.2",
33
+ "@platforma-sdk/eslint-config": "1.0.1"
33
34
  },
34
35
  "scripts": {
35
36
  "type-check": "node ./scripts/save-package-version.cjs && tsc --noEmit --composite false",
@@ -1,5 +1,10 @@
1
1
  import {test, expect, describe} from '@jest/globals';
2
- import {enrichColumnsWithCompatible, getAdditionalColumns, IS_VIRTUAL_COLUMN} from './PFrameForGraphs';
2
+ import {
3
+ enrichColumnsWithCompatible,
4
+ getAdditionalColumns,
5
+ IS_VIRTUAL_COLUMN,
6
+ LABEL_ANNOTATION
7
+ } from './PFrameForGraphs';
3
8
  import {PColumn, PColumnSpec, PColumnValues, PObjectId} from "@milaboratories/pl-model-common";
4
9
  import {TreeNodeAccessor} from "../render";
5
10
 
@@ -181,4 +186,31 @@ describe('PFrameForGraph', () => {
181
186
  const enrichedColumns = enrichColumnsWithCompatible(columns, upstream);
182
187
  expect(enrichedColumns.map((c) => c.id)).toEqual(['id1', 'id2', 'id3'])
183
188
  })
189
+
190
+ test('Labels of added columns include added domains, but not include common domains', () => {
191
+ const columnSpec1: PColumnSpec = {
192
+ kind: 'PColumn',
193
+ name: 'column1',
194
+ valueType: 'Int',
195
+ axesSpec: [
196
+ {type: 'String', name: 'axis1', domain: {key0: 'commonDomain', key1: 'a', key2: 'c'}},
197
+ {type: 'String', name: 'axis1', domain: {key0: 'commonDomain', key1: 'b', key2: 'c'}},
198
+ ]
199
+ }
200
+ const columnSpec2: PColumnSpec = {
201
+ kind: 'PColumn',
202
+ name: 'column2',
203
+ valueType: 'Int',
204
+ annotations: {[LABEL_ANNOTATION]: 'Label of column2'},
205
+ axesSpec: [{type: 'String', name: 'axis1', domain: {key0: 'commonDomain'}}]
206
+ }
207
+ const columns: PColumn<TreeNodeAccessor | PColumnValues>[] = [
208
+ {id: 'id1' as PObjectId, spec: columnSpec1, data: []},
209
+ {id: 'id2' as PObjectId, spec: columnSpec2, data: []}
210
+ ] as PColumn<PColumnValues>[]
211
+
212
+ const additionalColumns = getAdditionalColumns(columns);
213
+ expect(additionalColumns[0].spec.annotations?.[LABEL_ANNOTATION]).toEqual('Label of column2 / a');
214
+ expect(additionalColumns[1].spec.annotations?.[LABEL_ANNOTATION]).toEqual('Label of column2 / b');
215
+ })
184
216
  })
@@ -62,6 +62,7 @@ function checkCompatibility(
62
62
  }
63
63
 
64
64
  export const IS_VIRTUAL_COLUMN = 'pl7.app/graph/isVirtual'; // annotation for column duplicates with extended domains
65
+ export const LABEL_ANNOTATION = 'pl7.app/label';
65
66
 
66
67
  /** Main column can have additional domains, if secondary column (meta-column) has all axes match main column axes
67
68
  we can add its copy with missed domain fields for compatibility */
@@ -87,8 +88,52 @@ function getAdditionalColumnsForPair(
87
88
  // all possible combinations of axes with added domains
88
89
  const secondaryIdsVariants = getKeysCombinations(secondaryIdsOptions);
89
90
 
90
- return secondaryIdsVariants.map(idsList => {
91
+ // sets of added to column domain fields
92
+ const allAddedDomainValues = new Set<string>();
93
+ const addedNotToAllVariantsDomainValues = new Set<string>();
94
+ const addedByVariantsDomainValues = secondaryIdsVariants.map(idsList => {
95
+ const addedSet = new Set<string>();
96
+ idsList.map((axisId, idx) => {
97
+ const d1 = secondaryColumn.spec.axesSpec[idx].domain;
98
+ const d2 = axisId.domain;
99
+ Object.entries(d2 ?? {}).forEach(([key, value]) => {
100
+ if (d1?.[key] === undefined) {
101
+ const item = JSON.stringify([key, value]);
102
+ addedSet.add(item);
103
+ allAddedDomainValues.add(item);
104
+ }
105
+ });
106
+ return ({
107
+ ...axisId,
108
+ annotations: secondaryColumn.spec.axesSpec[idx].annotations
109
+ })
110
+ });
111
+ return addedSet;
112
+ });
113
+ [...allAddedDomainValues].forEach(addedPart => {
114
+ if (addedByVariantsDomainValues.some(s => !s.has(addedPart))) {
115
+ addedNotToAllVariantsDomainValues.add(addedPart);
116
+ }
117
+ })
118
+
119
+ return secondaryIdsVariants.map((idsList, idx) => {
91
120
  const id = colId(secondaryColumn.id, idsList.map(id => id.domain));
121
+
122
+ const label = secondaryColumn.spec.annotations?.[LABEL_ANNOTATION] ?? '';
123
+ const labelDomainPart = ([...addedByVariantsDomainValues[idx]])
124
+ .filter(str => addedNotToAllVariantsDomainValues.has(str))
125
+ .sort()
126
+ .map((v) => JSON.parse(v)?.[1]) // use in labels only domain values, but sort them by key to save the same order in all column variants
127
+ .join(' / ');
128
+
129
+ const annotations:Record<string, string> = {
130
+ ...secondaryColumn.spec.annotations,
131
+ [IS_VIRTUAL_COLUMN]: 'true'
132
+ }
133
+ if (label || labelDomainPart) {
134
+ annotations[LABEL_ANNOTATION] = label && labelDomainPart ? label + ' / ' + labelDomainPart : label + labelDomainPart;
135
+ }
136
+
92
137
  return {
93
138
  id: id as PObjectId,
94
139
  spec: {
@@ -97,10 +142,7 @@ function getAdditionalColumnsForPair(
97
142
  ...axisId,
98
143
  annotations: secondaryColumn.spec.axesSpec[idx].annotations
99
144
  })),
100
- annotations: {
101
- ...secondaryColumn.spec.annotations,
102
- [IS_VIRTUAL_COLUMN]: 'true'
103
- }
145
+ annotations
104
146
  },
105
147
  data: secondaryColumn.data
106
148
  };
package/src/render/api.ts CHANGED
@@ -1,7 +1,9 @@
1
- import {
1
+ import type {
2
+ APColumnSelector,
2
3
  AxisId,
3
4
  Option,
4
5
  PColumn,
6
+ PColumnSelector,
5
7
  PColumnSpec,
6
8
  PColumnValues,
7
9
  PFrameDef,
@@ -15,21 +17,29 @@ import {
15
17
  PTableSorting,
16
18
  PlRef,
17
19
  ResultCollection,
18
- ValueOrError,
20
+ ValueOrError } from '@milaboratories/pl-model-common';
21
+ import {
22
+ AnchorIdDeriver,
23
+ resolveAnchors } from '@milaboratories/pl-model-common';
24
+ import {
19
25
  ensurePColumn,
20
26
  extractAllColumns,
21
27
  isPColumn,
22
28
  isPColumnSpec,
29
+ isPlRef,
23
30
  mapPObjectData,
24
31
  mapPTableDef,
25
- mapValueInVOE
32
+ mapValueInVOE,
33
+ selectorsToPredicate,
26
34
  } from '@milaboratories/pl-model-common';
27
- import { Optional } from 'utility-types';
35
+ import type { Optional } from 'utility-types';
28
36
  import { getCfgRenderCtx } from '../internal';
29
37
  import { TreeNodeAccessor, ifDef } from './accessor';
30
- import { FutureRef } from './future';
31
- import { GlobalCfgRenderCtx, MainAccessorName, StagingAccessorName } from './internal';
32
- import { LabelDerivationOps, deriveLabels } from './util/label';
38
+ import type { FutureRef } from './future';
39
+ import type { GlobalCfgRenderCtx } from './internal';
40
+ import { MainAccessorName, StagingAccessorName } from './internal';
41
+ import type { LabelDerivationOps } from './util/label';
42
+ import { deriveLabels } from './util/label';
33
43
 
34
44
  export class ResultPool {
35
45
  private readonly ctx: GlobalCfgRenderCtx = getCfgRenderCtx();
@@ -45,22 +55,116 @@ export class ResultPool {
45
55
  spec.annotations?.['pl7.app/label'] ?? `Unlabelled`;
46
56
 
47
57
  public getOptions(
48
- predicate: (spec: PObjectSpec) => boolean,
49
- label?: ((spec: PObjectSpec, ref: PlRef) => string) | LabelDerivationOps
58
+ predicateOrSelector: ((spec: PObjectSpec) => boolean) | PColumnSelector | PColumnSelector[],
59
+ label?: ((spec: PObjectSpec, ref: PlRef) => string) | LabelDerivationOps,
50
60
  ): Option[] {
61
+ const predicate = typeof predicateOrSelector === 'function'
62
+ ? predicateOrSelector
63
+ : selectorsToPredicate(predicateOrSelector);
51
64
  const filtered = this.getSpecs().entries.filter((s) => predicate(s.obj));
52
65
  if (typeof label === 'object' || typeof label === 'undefined') {
53
66
  return deriveLabels(filtered, (o) => o.obj, label ?? {}).map(({ value: { ref }, label }) => ({
54
67
  ref,
55
- label
68
+ label,
56
69
  }));
57
70
  } else
58
71
  return filtered.map((s) => ({
59
72
  ref: s.ref,
60
- label: label(s.obj, s.ref)
73
+ label: label(s.obj, s.ref),
61
74
  }));
62
75
  }
63
76
 
77
+ /**
78
+ * Calculates anchored identifier options for columns matching a given predicate.
79
+ *
80
+ * This function filters column specifications from the result pool that match the provided predicate,
81
+ * creates a standardized AnchorCtx from the provided anchors, and generates a list of label-value
82
+ * pairs for UI components (like dropdowns).
83
+ *
84
+ * @param anchorsOrCtx - Either:
85
+ * - An existing AnchorCtx instance
86
+ * - A record mapping anchor IDs to PColumnSpec objects
87
+ * - A record mapping anchor IDs to PlRef objects (which will be resolved to PColumnSpec)
88
+ * @param predicateOrSelector - Either:
89
+ * - A predicate function that takes a PColumnSpec and returns a boolean.
90
+ * Only specs that return true will be included.
91
+ * - An APColumnSelector object for declarative filtering, which will be
92
+ * resolved against the provided anchors and matched using matchPColumn.
93
+ * - An array of APColumnSelector objects - columns matching ANY selector
94
+ * in the array will be included (OR operation).
95
+ * @param labelOps - Optional configuration for label generation:
96
+ * - includeNativeLabel: Whether to include native column labels
97
+ * - separator: String to use between label parts (defaults to " / ")
98
+ * - addLabelAsSuffix: Whether to add labels as suffix instead of prefix
99
+ * @returns An array of objects with `label` (display text) and `value` (anchored ID string) properties,
100
+ * or undefined if any PlRef resolution fails.
101
+ */
102
+ // Overload for AnchorCtx - guaranteed to never return undefined
103
+ getAnchoredOptions(
104
+ anchorsOrCtx: AnchorIdDeriver,
105
+ predicateOrSelectors: ((spec: PColumnSpec) => boolean) | APColumnSelector | APColumnSelector[],
106
+ labelOps?: LabelDerivationOps,
107
+ ): { label: string; value: string }[];
108
+
109
+ // Overload for Record<string, PColumnSpec> - guaranteed to never return undefined
110
+ getAnchoredOptions(
111
+ anchorsOrCtx: Record<string, PColumnSpec>,
112
+ predicateOrSelectors: ((spec: PColumnSpec) => boolean) | APColumnSelector | APColumnSelector[],
113
+ labelOps?: LabelDerivationOps,
114
+ ): { label: string; value: string }[];
115
+
116
+ // Overload for Record<string, PColumnSpec | PlRef> - may return undefined if PlRef resolution fails
117
+ getAnchoredOptions(
118
+ anchorsOrCtx: Record<string, PColumnSpec | PlRef>,
119
+ predicateOrSelectors: ((spec: PColumnSpec) => boolean) | APColumnSelector | APColumnSelector[],
120
+ labelOps?: LabelDerivationOps,
121
+ ): { label: string; value: string }[] | undefined;
122
+
123
+ // Implementation
124
+ getAnchoredOptions(
125
+ anchorsOrCtx: AnchorIdDeriver | Record<string, PColumnSpec | PlRef>,
126
+ predicateOrSelectors: ((spec: PColumnSpec) => boolean) | APColumnSelector | APColumnSelector[],
127
+ labelOps?: LabelDerivationOps,
128
+ ): { label: string; value: string }[] | undefined {
129
+ // Handle PlRef objects by resolving them to PColumnSpec
130
+ const resolvedAnchors: Record<string, PColumnSpec> = {};
131
+
132
+ if (!(anchorsOrCtx instanceof AnchorIdDeriver)) {
133
+ for (const [key, value] of Object.entries(anchorsOrCtx)) {
134
+ if (isPlRef(value)) {
135
+ const resolvedSpec = this.getPColumnSpecByRef(value);
136
+ if (!resolvedSpec)
137
+ return undefined;
138
+ resolvedAnchors[key] = resolvedSpec;
139
+ } else {
140
+ // It's already a PColumnSpec
141
+ resolvedAnchors[key] = value as PColumnSpec;
142
+ }
143
+ }
144
+ }
145
+
146
+ const predicate = typeof predicateOrSelectors === 'function'
147
+ ? predicateOrSelectors
148
+ : selectorsToPredicate(Array.isArray(predicateOrSelectors)
149
+ ? predicateOrSelectors.map((selector) => resolveAnchors(resolvedAnchors, selector))
150
+ : resolveAnchors(resolvedAnchors, predicateOrSelectors),
151
+ );
152
+
153
+ const filtered = this.getSpecs().entries.filter(({ obj: spec }) => {
154
+ if (!isPColumnSpec(spec)) return false;
155
+ return predicate(spec);
156
+ });
157
+
158
+ const anchorIdDeriver = anchorsOrCtx instanceof AnchorIdDeriver
159
+ ? anchorsOrCtx
160
+ : new AnchorIdDeriver(resolvedAnchors);
161
+
162
+ return deriveLabels(filtered, (o) => o.obj, labelOps ?? {}).map(({ value: { obj: spec }, label }) => ({
163
+ value: anchorIdDeriver.deriveString(spec as PColumnSpec)!,
164
+ label,
165
+ }));
166
+ }
167
+
64
168
  /**
65
169
  * @deprecated use getData()
66
170
  */
@@ -76,9 +180,9 @@ export class ResultPool {
76
180
  ref: e.ref,
77
181
  obj: {
78
182
  ...e.obj,
79
- data: new TreeNodeAccessor(e.obj.data, [e.ref.blockId, e.ref.name])
80
- }
81
- }))
183
+ data: new TreeNodeAccessor(e.obj.data, [e.ref.blockId, e.ref.name]),
184
+ },
185
+ })),
82
186
  };
83
187
  }
84
188
 
@@ -103,10 +207,10 @@ export class ResultPool {
103
207
  ...e.obj,
104
208
  data: mapValueInVOE(
105
209
  e.obj.data,
106
- (handle) => new TreeNodeAccessor(handle, [e.ref.blockId, e.ref.name])
107
- )
108
- }
109
- }))
210
+ (handle) => new TreeNodeAccessor(handle, [e.ref.blockId, e.ref.name]),
211
+ ),
212
+ },
213
+ })),
110
214
  };
111
215
  }
112
216
 
@@ -129,11 +233,11 @@ export class ResultPool {
129
233
  // @TODO remove after 1 Jan 2025; forward compatibility
130
234
  if (typeof this.ctx.getDataFromResultPoolByRef === 'undefined')
131
235
  return this.getData().entries.find(
132
- (f) => f.ref.blockId === ref.blockId && f.ref.name === ref.name
236
+ (f) => f.ref.blockId === ref.blockId && f.ref.name === ref.name,
133
237
  )?.obj;
134
238
  return mapPObjectData(
135
239
  this.ctx.getDataFromResultPoolByRef(ref.blockId, ref.name),
136
- (handle) => new TreeNodeAccessor(handle, [ref.blockId, ref.name])
240
+ (handle) => new TreeNodeAccessor(handle, [ref.blockId, ref.name]),
137
241
  );
138
242
  }
139
243
 
@@ -165,11 +269,6 @@ export class ResultPool {
165
269
  * @returns object spec associated with the ref
166
270
  */
167
271
  public getSpecByRef(ref: PlRef): PObjectSpec | undefined {
168
- // @TODO remove after 1 Jan 2025; forward compatibility
169
- if (typeof this.ctx.getSpecFromResultPoolByRef === 'undefined')
170
- return this.getSpecs().entries.find(
171
- (f) => f.ref.blockId === ref.blockId && f.ref.name === ref.name
172
- )?.obj;
173
272
  return this.ctx.getSpecFromResultPoolByRef(ref.blockId, ref.name);
174
273
  }
175
274
 
@@ -256,7 +355,7 @@ export class RenderCtx<Args, UiState> {
256
355
  public get activeArgs(): Args | undefined {
257
356
  if (this._activeArgsCache === undefined)
258
357
  this._activeArgsCache = {
259
- v: this.ctx.activeArgs ? JSON.parse(this.ctx.activeArgs) : undefined
358
+ v: this.ctx.activeArgs ? JSON.parse(this.ctx.activeArgs) : undefined,
260
359
  };
261
360
  return this._activeArgsCache.v;
262
361
  }
@@ -269,7 +368,7 @@ export class RenderCtx<Args, UiState> {
269
368
  private getNamedAccessor(name: string): TreeNodeAccessor | undefined {
270
369
  return ifDef(
271
370
  this.ctx.getAccessorHandleByName(name),
272
- (accessor) => new TreeNodeAccessor(accessor, [name])
371
+ (accessor) => new TreeNodeAccessor(accessor, [name]),
273
372
  );
274
373
  }
275
374
 
@@ -294,11 +393,11 @@ export class RenderCtx<Args, UiState> {
294
393
 
295
394
  const spec = column.obj.spec;
296
395
  if (
297
- spec.name === 'pl7.app/label' &&
298
- spec.axesSpec.length === 1 &&
299
- spec.axesSpec[0].name === axis.name &&
300
- spec.axesSpec[0].type === axis.type &&
301
- matchDomain(axis.domain, spec.axesSpec[0].domain)
396
+ spec.name === 'pl7.app/label'
397
+ && spec.axesSpec.length === 1
398
+ && spec.axesSpec[0].name === axis.name
399
+ && spec.axesSpec[0].type === axis.type
400
+ && matchDomain(axis.domain, spec.axesSpec[0].domain)
302
401
  ) {
303
402
  if (column.obj.data.resourceType.name !== 'PColumnData/Json') {
304
403
  throw Error(`Expected JSON column for labels, got: ${column.obj.data.resourceType.name}`);
@@ -307,8 +406,8 @@ export class RenderCtx<Args, UiState> {
307
406
  Object.entries(
308
407
  column.obj.data.getDataAsJson<{
309
408
  data: Record<string | number, string>;
310
- }>().data
311
- ).map((e) => [JSON.parse(e[0])[0], e[1]])
409
+ }>().data,
410
+ ).map((e) => [JSON.parse(e[0])[0], e[1]]),
312
411
  );
313
412
 
314
413
  return labels;
@@ -326,7 +425,7 @@ export class RenderCtx<Args, UiState> {
326
425
  public createPFrame(def: PFrameDef<TreeNodeAccessor | PColumnValues>): PFrameHandle {
327
426
  this.verifyInlineColumnsSupport(def);
328
427
  return this.ctx.createPFrame(
329
- def.map((c) => mapPObjectData(c, (d) => (d instanceof TreeNodeAccessor ? d.handle : d)))
428
+ def.map((c) => mapPObjectData(c, (d) => (d instanceof TreeNodeAccessor ? d.handle : d))),
330
429
  );
331
430
  }
332
431
 
@@ -341,21 +440,21 @@ export class RenderCtx<Args, UiState> {
341
440
  def:
342
441
  | PTableDef<PColumn<TreeNodeAccessor | PColumnValues>>
343
442
  | {
344
- columns: PColumn<TreeNodeAccessor | PColumnValues>[];
345
- filters?: PTableRecordFilter[];
346
- /** Table sorting */
347
- sorting?: PTableSorting[];
348
- }
443
+ columns: PColumn<TreeNodeAccessor | PColumnValues>[];
444
+ filters?: PTableRecordFilter[];
445
+ /** Table sorting */
446
+ sorting?: PTableSorting[];
447
+ },
349
448
  ): PTableHandle {
350
- var rawDef: PTableDef<PColumn<TreeNodeAccessor | PColumnValues>>;
449
+ let rawDef: PTableDef<PColumn<TreeNodeAccessor | PColumnValues>>;
351
450
  if ('columns' in def) {
352
451
  rawDef = {
353
452
  src: {
354
453
  type: 'full',
355
- entries: def.columns.map((c) => ({ type: 'column', column: c }))
454
+ entries: def.columns.map((c) => ({ type: 'column', column: c })),
356
455
  },
357
456
  filters: def.filters ?? [],
358
- sorting: def.sorting ?? []
457
+ sorting: def.sorting ?? [],
359
458
  };
360
459
  } else {
361
460
  rawDef = def;
@@ -363,8 +462,8 @@ export class RenderCtx<Args, UiState> {
363
462
  this.verifyInlineColumnsSupport(extractAllColumns(rawDef.src));
364
463
  return this.ctx.createPTable(
365
464
  mapPTableDef(rawDef, (po) =>
366
- mapPObjectData(po, (d) => (d instanceof TreeNodeAccessor ? d.handle : d))
367
- )
465
+ mapPObjectData(po, (d) => (d instanceof TreeNodeAccessor ? d.handle : d)),
466
+ ),
368
467
  );
369
468
  }
370
469