@platforma-sdk/model 1.33.8 → 1.33.14
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.
- package/dist/components/PFrameForGraphs.d.ts +8 -8
- package/dist/components/PFrameForGraphs.d.ts.map +1 -1
- package/dist/components/PlDataTable.d.ts +3 -3
- package/dist/components/PlDataTable.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +616 -620
- package/dist/index.mjs.map +1 -1
- package/dist/render/api.d.ts +5 -4
- package/dist/render/api.d.ts.map +1 -1
- package/dist/render/util/column_collection.d.ts +8 -8
- package/dist/render/util/column_collection.d.ts.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.d.ts.map +1 -1
- package/package.json +5 -5
- package/src/components/PFrameForGraphs.test.ts +58 -159
- package/src/components/PFrameForGraphs.ts +105 -165
- package/src/components/PlDataTable.ts +6 -5
- package/src/render/api.ts +11 -9
- package/src/render/util/column_collection.ts +26 -16
|
@@ -1,11 +1,8 @@
|
|
|
1
1
|
import type {
|
|
2
|
-
AxisId,
|
|
3
|
-
CanonicalizedJson,
|
|
2
|
+
AxisId, CanonicalizedJson,
|
|
4
3
|
DataInfo,
|
|
5
4
|
PColumn,
|
|
6
|
-
PColumnIdAndSpec,
|
|
7
5
|
PColumnSpec,
|
|
8
|
-
PColumnSpecId,
|
|
9
6
|
PColumnValues,
|
|
10
7
|
PFrameHandle,
|
|
11
8
|
PObjectId,
|
|
@@ -13,13 +10,11 @@ import type {
|
|
|
13
10
|
import {
|
|
14
11
|
canonicalizeJson,
|
|
15
12
|
getAxisId,
|
|
16
|
-
|
|
17
|
-
isPColumn,
|
|
18
|
-
matchAxisId,
|
|
19
|
-
parseJson,
|
|
13
|
+
matchAxisId, parseJson,
|
|
20
14
|
} from '@milaboratories/pl-model-common';
|
|
21
|
-
import type { RenderCtx } from '../render';
|
|
22
|
-
import { TreeNodeAccessor } from '../render';
|
|
15
|
+
import type { PColumnDataUniversal, RenderCtx } from '../render';
|
|
16
|
+
import { PColumnCollection, TreeNodeAccessor } from '../render';
|
|
17
|
+
import { isLabelColumn } from './PlDataTable';
|
|
23
18
|
|
|
24
19
|
/** Create id for column copy with added keys in axes domains */
|
|
25
20
|
const colId = (id: PObjectId, domains: (Record<string, string> | undefined)[]) => {
|
|
@@ -56,104 +51,92 @@ export function isLinkerColumn(column: PColumnSpec) {
|
|
|
56
51
|
return column.axesSpec.length === 2 && column.annotations?.[LINKER_COLUMN_ANNOTATION] === 'true';
|
|
57
52
|
}
|
|
58
53
|
|
|
59
|
-
export
|
|
60
|
-
export
|
|
54
|
+
export const IS_VIRTUAL_COLUMN = 'pl7.app/graph/isVirtual'; // annotation for column duplicates with extended domains
|
|
55
|
+
export const LABEL_ANNOTATION = 'pl7.app/label';
|
|
56
|
+
export const LINKER_COLUMN_ANNOTATION = 'pl7.app/isLinkerColumn';
|
|
57
|
+
|
|
58
|
+
export type LinkerColumnsMap = Map<CanonicalizedJson<AxisId>, Set<CanonicalizedJson<AxisId>>>;
|
|
59
|
+
export function getLinkerColumnsMap(linkerColumns: PColumn<PColumnDataUniversal>[]) {
|
|
61
60
|
const resultMap: LinkerColumnsMap = new Map();
|
|
62
|
-
for (const {
|
|
63
|
-
const
|
|
64
|
-
|
|
65
|
-
resultMap.
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
61
|
+
for (const { spec } of linkerColumns) {
|
|
62
|
+
const axisIds = spec.axesSpec.map(getAxisId).map(canonicalizeJson);
|
|
63
|
+
axisIds.forEach((id) => {
|
|
64
|
+
if (!resultMap.has(id)) {
|
|
65
|
+
resultMap.set(id, new Set());
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
for (let i = 0; i < axisIds.length - 1; i++) {
|
|
69
|
+
for (let j = i + 1; j < axisIds.length; j++) {
|
|
70
|
+
const id1 = axisIds[i];
|
|
71
|
+
const id2 = axisIds[j];
|
|
72
|
+
resultMap.get(id1)?.add(id2);
|
|
73
|
+
resultMap.get(id2)?.add(id1);
|
|
74
|
+
}
|
|
69
75
|
}
|
|
70
|
-
resultMap.get(idA)?.set(idB, { columnId: id, spec })
|
|
71
|
-
resultMap.get(idB)?.set(idA, { columnId: id, spec })
|
|
72
76
|
}
|
|
73
77
|
return resultMap;
|
|
74
78
|
}
|
|
75
79
|
|
|
76
|
-
export function
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
80
|
+
export function getAvailableWithLinkersAxes(
|
|
81
|
+
linkerColumns: PColumn<PColumnDataUniversal>[],
|
|
82
|
+
blockAxes: Map<CanonicalizedJson<AxisId>, AxisId>,
|
|
83
|
+
): Map<CanonicalizedJson<AxisId>, AxisId> {
|
|
84
|
+
const linkerColumnsMap = getLinkerColumnsMap(linkerColumns);
|
|
81
85
|
const linkerColumnsMapIds = [...linkerColumnsMap.keys()].map(parseJson);
|
|
82
|
-
const
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
86
|
+
const startKeys: CanonicalizedJson<AxisId>[] = [];
|
|
87
|
+
for (const startId of blockAxes.values()) {
|
|
88
|
+
const matched = linkerColumnsMapIds.find((id) => matchAxisId(startId, id));
|
|
89
|
+
if (matched) {
|
|
90
|
+
startKeys.push(canonicalizeJson(matched)); // linker column can contain fewer domains than in block's columns, it's fixed on next step in enrichCompatible
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
const visited: Set<CanonicalizedJson<AxisId>> = new Set(startKeys);
|
|
94
|
+
const addedAvailableAxes: Map<CanonicalizedJson<AxisId>, AxisId> = new Map();
|
|
95
|
+
let nextKeys = [...startKeys];
|
|
88
96
|
|
|
89
97
|
while (nextKeys.length) {
|
|
90
98
|
const next: CanonicalizedJson<AxisId>[] = [];
|
|
91
99
|
for (const nextKey of nextKeys) {
|
|
92
|
-
for (const availableKey of linkerColumnsMap.get(nextKey)
|
|
93
|
-
const availableId = parseJson(availableKey);
|
|
94
|
-
if (matchAxisId(endId, availableId)) return true;
|
|
100
|
+
for (const availableKey of linkerColumnsMap.get(nextKey) ?? []) {
|
|
95
101
|
if (!visited.has(availableKey)) {
|
|
96
102
|
next.push(availableKey);
|
|
97
103
|
visited.add(availableKey);
|
|
104
|
+
addedAvailableAxes.set(availableKey, parseJson(availableKey));
|
|
98
105
|
}
|
|
99
106
|
}
|
|
100
107
|
}
|
|
101
108
|
nextKeys = next;
|
|
102
109
|
}
|
|
103
|
-
return
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
/** Check if axes of secondary column are exactly in axes of main column */
|
|
107
|
-
function checkFullCompatibility(
|
|
108
|
-
mainColumn: PColumnSpec,
|
|
109
|
-
secondaryColumn: PColumnSpec,
|
|
110
|
-
linkerColumnsMap?: LinkerColumnsMap,
|
|
111
|
-
): boolean {
|
|
112
|
-
const mainAxesIds = mainColumn.axesSpec.map(getAxisId);
|
|
113
|
-
const secondaryAxesIds = secondaryColumn.axesSpec.map(getAxisId);
|
|
114
|
-
// with fixed axes (sliced columns) in data-mapping there is enough to have only one axis in intersection
|
|
115
|
-
return secondaryAxesIds.some((id) => mainAxesIds.some((mainId) => matchAxisId(mainId, id) && matchAxisId(id, mainId)))
|
|
116
|
-
|| (!!linkerColumnsMap && secondaryAxesIds.some((id) => mainAxesIds.some((mainId) => hasPathWithLinkerColumns(linkerColumnsMap, mainId, id))));
|
|
110
|
+
return addedAvailableAxes;
|
|
117
111
|
}
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
const mainAxesIds = mainColumn.axesSpec.map(getAxisId);
|
|
126
|
-
const secondaryAxesIds = secondaryColumn.axesSpec.map(getAxisId);
|
|
127
|
-
// with fixed axes (sliced columns) in data-mapping there is enough to have only one axis in intersection
|
|
128
|
-
return secondaryAxesIds.some((id) => mainAxesIds.some((mainId) => matchAxisId(mainId, id)))
|
|
129
|
-
|| (!!linkerColumnsMap && secondaryAxesIds.some((id) => mainAxesIds.some((mainId) => hasPathWithLinkerColumns(linkerColumnsMap, mainId, id))));
|
|
112
|
+
/** Add columns with fully compatible axes created from partial compatible ones */
|
|
113
|
+
export function enrichCompatible(blockAxes: Map<string, AxisId>, columns: PColumn<PColumnDataUniversal>[]) {
|
|
114
|
+
const result: PColumn<PColumnDataUniversal>[] = [];
|
|
115
|
+
columns.forEach((column) => {
|
|
116
|
+
result.push(...getAdditionalColumnsForColumn(blockAxes, column));
|
|
117
|
+
});
|
|
118
|
+
return result;
|
|
130
119
|
}
|
|
131
120
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
121
|
+
function getAdditionalColumnsForColumn(
|
|
122
|
+
blockAxes: Map<string, AxisId>,
|
|
123
|
+
column: PColumn<PColumnDataUniversal>,
|
|
124
|
+
): PColumn<PColumnDataUniversal>[] {
|
|
125
|
+
const columnAxesIds = column.spec.axesSpec.map(getAxisId);
|
|
135
126
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
function getAdditionalColumnsForPair(
|
|
139
|
-
mainColumn: PColumn<TreeNodeAccessor | DataInfo<TreeNodeAccessor> | PColumnValues>,
|
|
140
|
-
secondaryColumn: PColumn<TreeNodeAccessor | DataInfo<TreeNodeAccessor> | PColumnValues>,
|
|
141
|
-
linkerColumnsMap?: LinkerColumnsMap,
|
|
142
|
-
): PColumn<TreeNodeAccessor | DataInfo<TreeNodeAccessor> | PColumnValues>[] {
|
|
143
|
-
const mainAxesIds = mainColumn.spec.axesSpec.map(getAxisId);
|
|
144
|
-
const secondaryAxesIds = secondaryColumn.spec.axesSpec.map(getAxisId);
|
|
145
|
-
|
|
146
|
-
const isFullCompatible = checkFullCompatibility(mainColumn.spec, secondaryColumn.spec, linkerColumnsMap);
|
|
147
|
-
if (isFullCompatible) { // in this case it isn't necessary to add more columns
|
|
148
|
-
return [];
|
|
149
|
-
}
|
|
150
|
-
const isCompatible = checkCompatibility(mainColumn.spec, secondaryColumn.spec, linkerColumnsMap);
|
|
151
|
-
if (!isCompatible) { // in this case it is impossible to add some compatible column
|
|
152
|
-
return [];
|
|
127
|
+
if (columnAxesIds.every((id) => blockAxes.has(canonicalizeJson(id)))) {
|
|
128
|
+
return [column]; // the column is compatible with its own domains without modifications
|
|
153
129
|
}
|
|
130
|
+
|
|
154
131
|
// options with different possible domains for every axis of secondary column
|
|
155
|
-
const secondaryIdsOptions =
|
|
156
|
-
|
|
132
|
+
const secondaryIdsOptions = columnAxesIds.map((id) => {
|
|
133
|
+
const result = [];
|
|
134
|
+
for (const [_, mainId] of blockAxes) {
|
|
135
|
+
if (matchAxisId(mainId, id) && !matchAxisId(id, mainId)) {
|
|
136
|
+
result.push(mainId);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
return result;
|
|
157
140
|
});
|
|
158
141
|
// all possible combinations of axes with added domains
|
|
159
142
|
const secondaryIdsVariants = getKeysCombinations(secondaryIdsOptions);
|
|
@@ -164,7 +147,7 @@ function getAdditionalColumnsForPair(
|
|
|
164
147
|
const addedByVariantsDomainValues = secondaryIdsVariants.map((idsList) => {
|
|
165
148
|
const addedSet = new Set<string>();
|
|
166
149
|
idsList.map((axisId, idx) => {
|
|
167
|
-
const d1 =
|
|
150
|
+
const d1 = column.spec.axesSpec[idx].domain;
|
|
168
151
|
const d2 = axisId.domain;
|
|
169
152
|
Object.entries(d2 ?? {}).forEach(([key, value]) => {
|
|
170
153
|
if (d1?.[key] === undefined) {
|
|
@@ -175,7 +158,7 @@ function getAdditionalColumnsForPair(
|
|
|
175
158
|
});
|
|
176
159
|
return ({
|
|
177
160
|
...axisId,
|
|
178
|
-
annotations:
|
|
161
|
+
annotations: column.spec.axesSpec[idx].annotations,
|
|
179
162
|
});
|
|
180
163
|
});
|
|
181
164
|
return addedSet;
|
|
@@ -186,10 +169,10 @@ function getAdditionalColumnsForPair(
|
|
|
186
169
|
}
|
|
187
170
|
});
|
|
188
171
|
|
|
189
|
-
|
|
190
|
-
const id = colId(
|
|
172
|
+
const additionalColumns = secondaryIdsVariants.map((idsList, idx) => {
|
|
173
|
+
const id = colId(column.id, idsList.map((id) => id.domain));
|
|
191
174
|
|
|
192
|
-
const label =
|
|
175
|
+
const label = column.spec.annotations?.[LABEL_ANNOTATION] ?? '';
|
|
193
176
|
const labelDomainPart = ([...addedByVariantsDomainValues[idx]])
|
|
194
177
|
.filter((str) => addedNotToAllVariantsDomainValues.has(str))
|
|
195
178
|
.sort()
|
|
@@ -197,7 +180,7 @@ function getAdditionalColumnsForPair(
|
|
|
197
180
|
.join(' / ');
|
|
198
181
|
|
|
199
182
|
const annotations: Record<string, string> = {
|
|
200
|
-
...
|
|
183
|
+
...column.spec.annotations,
|
|
201
184
|
[IS_VIRTUAL_COLUMN]: 'true',
|
|
202
185
|
};
|
|
203
186
|
if (label || labelDomainPart) {
|
|
@@ -207,101 +190,58 @@ function getAdditionalColumnsForPair(
|
|
|
207
190
|
return {
|
|
208
191
|
id: id as PObjectId,
|
|
209
192
|
spec: {
|
|
210
|
-
...
|
|
193
|
+
...column.spec,
|
|
211
194
|
axesSpec: idsList.map((axisId, idx) => ({
|
|
212
195
|
...axisId,
|
|
213
|
-
annotations:
|
|
196
|
+
annotations: column.spec.axesSpec[idx].annotations,
|
|
214
197
|
})),
|
|
215
198
|
annotations,
|
|
216
199
|
},
|
|
217
|
-
data:
|
|
200
|
+
data: column.data,
|
|
218
201
|
};
|
|
219
202
|
});
|
|
220
|
-
}
|
|
221
203
|
|
|
222
|
-
|
|
223
|
-
columns: PColumn<TreeNodeAccessor | DataInfo<TreeNodeAccessor> | PColumnValues>[],
|
|
224
|
-
linkerColumnsMap: LinkerColumnsMap = new Map(),
|
|
225
|
-
): PColumn<TreeNodeAccessor | DataInfo<TreeNodeAccessor> | PColumnValues>[] {
|
|
226
|
-
const additionalColumns: PColumn<TreeNodeAccessor | DataInfo<TreeNodeAccessor> | PColumnValues>[] = [];
|
|
227
|
-
for (let i = 0; i < columns.length; i++) {
|
|
228
|
-
for (let j = i + 1; j < columns.length; j++) {
|
|
229
|
-
const column1 = columns[i];
|
|
230
|
-
const column2 = columns[j];
|
|
231
|
-
|
|
232
|
-
// check if column 1 is meta for column 2 or backward
|
|
233
|
-
additionalColumns.push(
|
|
234
|
-
...getAdditionalColumnsForPair(column1, column2, linkerColumnsMap),
|
|
235
|
-
...getAdditionalColumnsForPair(column2, column1, linkerColumnsMap),
|
|
236
|
-
);
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
return additionalColumns;
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
export function enrichColumnsWithCompatible(
|
|
243
|
-
mainColumns: PColumn<TreeNodeAccessor | DataInfo<TreeNodeAccessor> | PColumnValues>[],
|
|
244
|
-
secondaryColumns: PColumn<TreeNodeAccessor | DataInfo<TreeNodeAccessor> | PColumnValues>[],
|
|
245
|
-
linkerColumns: PColumn<TreeNodeAccessor | DataInfo<TreeNodeAccessor> | PColumnValues>[] = [],
|
|
246
|
-
linkerColumnsMap: LinkerColumnsMap = new Map(),
|
|
247
|
-
): PColumn<TreeNodeAccessor | DataInfo<TreeNodeAccessor> | PColumnValues>[] {
|
|
248
|
-
const mainColumnsIds = new Set<PObjectId>();
|
|
249
|
-
const mainColumnsBySpec = new Map<CanonicalizedJson<PColumnSpecId>, typeof mainColumns[number]>();
|
|
250
|
-
mainColumns.forEach((column) => {
|
|
251
|
-
mainColumnsIds.add(column.id);
|
|
252
|
-
mainColumnsBySpec.set(canonicalizeJson(getPColumnSpecId(column.spec)), column);
|
|
253
|
-
});
|
|
254
|
-
|
|
255
|
-
const secondaryColumnsBySpec = new Map<CanonicalizedJson<PColumnSpecId>, typeof secondaryColumns[number]>();
|
|
256
|
-
for (const secondaryColumn of secondaryColumns) {
|
|
257
|
-
if (mainColumnsIds.has(secondaryColumn.id)) continue;
|
|
258
|
-
|
|
259
|
-
const spec = canonicalizeJson(getPColumnSpecId(secondaryColumn.spec));
|
|
260
|
-
if (mainColumnsBySpec.has(spec)) continue;
|
|
261
|
-
|
|
262
|
-
for (const mainColumn of mainColumnsBySpec.values()) {
|
|
263
|
-
if (checkCompatibility(mainColumn.spec, secondaryColumn.spec, linkerColumnsMap)) {
|
|
264
|
-
secondaryColumnsBySpec.set(spec, secondaryColumn);
|
|
265
|
-
break;
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
for (const linkerColumn of linkerColumns) {
|
|
271
|
-
if (mainColumnsIds.has(linkerColumn.id)) continue;
|
|
272
|
-
|
|
273
|
-
const spec = canonicalizeJson(getPColumnSpecId(linkerColumn.spec));
|
|
274
|
-
if (mainColumnsBySpec.has(spec)) continue;
|
|
275
|
-
if (secondaryColumnsBySpec.has(spec)) continue;
|
|
276
|
-
|
|
277
|
-
mainColumnsIds.add(linkerColumn.id);
|
|
278
|
-
mainColumnsBySpec.set(spec, linkerColumn);
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
return [...mainColumnsBySpec.values(), ...secondaryColumnsBySpec.values()];
|
|
204
|
+
return [column, ...additionalColumns];
|
|
282
205
|
}
|
|
283
206
|
|
|
284
207
|
export function createPFrameForGraphs<A, U>(
|
|
285
208
|
ctx: RenderCtx<A, U>,
|
|
286
|
-
blockColumns: PColumn<
|
|
209
|
+
blockColumns: PColumn<PColumnDataUniversal>[] | undefined,
|
|
287
210
|
): PFrameHandle | undefined {
|
|
288
211
|
if (!blockColumns) return undefined;
|
|
289
212
|
|
|
290
|
-
const
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
213
|
+
const columns = new PColumnCollection();
|
|
214
|
+
columns.addColumnProvider(ctx.resultPool);
|
|
215
|
+
columns.addColumns(blockColumns);
|
|
216
|
+
|
|
217
|
+
// all possible axes from block columns
|
|
218
|
+
const allAxes = new Map<CanonicalizedJson<AxisId>, AxisId>();
|
|
219
|
+
for (const c of blockColumns) {
|
|
220
|
+
for (const id of c.spec.axesSpec) {
|
|
221
|
+
const aid = getAxisId(id);
|
|
222
|
+
allAxes.set(canonicalizeJson(aid), aid);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
294
225
|
|
|
295
|
-
const allAvailableColumns = [...blockColumns, ...upstreamColumns];
|
|
296
226
|
// all linker columns always go to pFrame - even it's impossible to use some of them they all are hidden
|
|
297
|
-
const linkerColumns =
|
|
227
|
+
const linkerColumns = columns.getColumns((spec) => isLinkerColumn(spec)) ?? [];
|
|
228
|
+
const availableWithLinkersAxes = getAvailableWithLinkersAxes(linkerColumns, allAxes);
|
|
229
|
+
const labelColumns = columns.getColumns(isLabelColumn) ?? [];
|
|
298
230
|
|
|
299
|
-
|
|
300
|
-
|
|
231
|
+
// all possible axes from connected linkers
|
|
232
|
+
for (const item of availableWithLinkersAxes) {
|
|
233
|
+
allAxes.set(...item);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
let compatible = (columns.getColumns([...allAxes.values()]
|
|
237
|
+
.map((ax) => ({
|
|
238
|
+
axes: [ax],
|
|
239
|
+
partialAxesMatch: true,
|
|
240
|
+
}))) ?? []).filter((column) => !isLabelColumn(column.spec));
|
|
241
|
+
compatible = compatible.concat(labelColumns); // add all available labels columns to avoid problems with fixed axes, but don't add twice
|
|
301
242
|
|
|
302
|
-
// additional columns are duplicates with extra fields in domains for compatibility
|
|
303
|
-
|
|
304
|
-
const extendedColumns = [...columnsWithCompatibleFromUpstream];
|
|
243
|
+
// additional columns are duplicates with extra fields in domains for compatibility if there are ones with partial match
|
|
244
|
+
const extendedColumns = enrichCompatible(allAxes, compatible);
|
|
305
245
|
|
|
306
246
|
// if at least one column is not yet ready, we can't show the table
|
|
307
247
|
if (
|
|
@@ -28,6 +28,7 @@ import {
|
|
|
28
28
|
import type {
|
|
29
29
|
AxisLabelProvider,
|
|
30
30
|
ColumnProvider,
|
|
31
|
+
PColumnDataUniversal,
|
|
31
32
|
RenderCtx,
|
|
32
33
|
} from '../render';
|
|
33
34
|
import {
|
|
@@ -363,7 +364,7 @@ export function isLabelColumn(column: PColumnSpec) {
|
|
|
363
364
|
/** Get all label columns from the result pool */
|
|
364
365
|
export function getAllLabelColumns(
|
|
365
366
|
resultPool: AxisLabelProvider & ColumnProvider,
|
|
366
|
-
): PColumn<
|
|
367
|
+
): PColumn<PColumnDataUniversal>[] | undefined {
|
|
367
368
|
return new PColumnCollection()
|
|
368
369
|
.addAxisLabelProvider(resultPool)
|
|
369
370
|
.addColumnProvider(resultPool)
|
|
@@ -376,8 +377,8 @@ export function getAllLabelColumns(
|
|
|
376
377
|
/** Get label columns matching the provided columns from the result pool */
|
|
377
378
|
export function getMatchingLabelColumns(
|
|
378
379
|
columns: PColumnIdAndSpec[],
|
|
379
|
-
allLabelColumns: PColumn<
|
|
380
|
-
): PColumn<
|
|
380
|
+
allLabelColumns: PColumn<PColumnDataUniversal>[],
|
|
381
|
+
): PColumn<PColumnDataUniversal>[] {
|
|
381
382
|
// split input columns into label and value columns
|
|
382
383
|
const inputLabelColumns: typeof columns = [];
|
|
383
384
|
const inputValueColumns: typeof columns = [];
|
|
@@ -485,8 +486,8 @@ export function allColumnsComputed(
|
|
|
485
486
|
}
|
|
486
487
|
|
|
487
488
|
function createPTableDef(
|
|
488
|
-
columns: PColumn<
|
|
489
|
-
labelColumns: PColumn<
|
|
489
|
+
columns: PColumn<PColumnDataUniversal>[],
|
|
490
|
+
labelColumns: PColumn<PColumnDataUniversal>[],
|
|
490
491
|
coreJoinType: 'inner' | 'full',
|
|
491
492
|
filters: PTableRecordSingleValueFilterV2[],
|
|
492
493
|
sorting: PTableSorting[],
|
package/src/render/api.ts
CHANGED
|
@@ -51,6 +51,8 @@ import type { LabelDerivationOps } from './util/label';
|
|
|
51
51
|
import { deriveLabels } from './util/label';
|
|
52
52
|
import type { APColumnSelectorWithSplit } from './util/split_selectors';
|
|
53
53
|
|
|
54
|
+
export type PColumnDataUniversal = TreeNodeAccessor | DataInfo<TreeNodeAccessor> | PColumnValues;
|
|
55
|
+
|
|
54
56
|
/**
|
|
55
57
|
* Helper function to match domain objects
|
|
56
58
|
* @param query Optional domain to match against
|
|
@@ -73,7 +75,7 @@ export type UniversalColumnOption = { label: string; value: SUniversalPColumnId
|
|
|
73
75
|
* @param data Data from a PColumn to transform
|
|
74
76
|
* @returns Transformed data compatible with platform API
|
|
75
77
|
*/
|
|
76
|
-
function transformPColumnData(data: PColumn<
|
|
78
|
+
function transformPColumnData(data: PColumn<PColumnDataUniversal>):
|
|
77
79
|
PColumn<PColumnValues | AccessorHandle | DataInfo<AccessorHandle>> {
|
|
78
80
|
return mapPObjectData(data, (d) => {
|
|
79
81
|
if (d instanceof TreeNodeAccessor) {
|
|
@@ -191,7 +193,7 @@ export class ResultPool implements ColumnProvider, AxisLabelProvider {
|
|
|
191
193
|
anchorsOrCtx: AnchoredIdDeriver | Record<string, PColumnSpec | PlRef>,
|
|
192
194
|
predicateOrSelectors: ((spec: PColumnSpec) => boolean) | APColumnSelectorWithSplit | APColumnSelectorWithSplit[],
|
|
193
195
|
opts?: UniversalPColumnOpts,
|
|
194
|
-
): PColumn<
|
|
196
|
+
): PColumn<PColumnDataUniversal>[] | undefined {
|
|
195
197
|
const anchorCtx = this.resolveAnchorCtx(anchorsOrCtx);
|
|
196
198
|
if (!anchorCtx) return undefined;
|
|
197
199
|
return new PColumnCollection()
|
|
@@ -565,7 +567,7 @@ export class RenderCtx<Args, UiState> {
|
|
|
565
567
|
return this.resultPool.findLabels(axis);
|
|
566
568
|
}
|
|
567
569
|
|
|
568
|
-
private verifyInlineAndExplicitColumnsSupport(columns: PColumn<
|
|
570
|
+
private verifyInlineAndExplicitColumnsSupport(columns: PColumn<PColumnDataUniversal>[]) {
|
|
569
571
|
const hasInlineColumns = columns.some((c) => !(c.data instanceof TreeNodeAccessor) || isDataInfo(c.data)); // Updated check for DataInfo
|
|
570
572
|
const inlineColumnsSupport = this.ctx.featureFlags?.inlineColumnsSupport === true;
|
|
571
573
|
if (hasInlineColumns && !inlineColumnsSupport) throw Error(`Inline or explicit columns not supported`); // Combined check
|
|
@@ -574,7 +576,7 @@ export class RenderCtx<Args, UiState> {
|
|
|
574
576
|
}
|
|
575
577
|
|
|
576
578
|
// TODO remove all non-PColumn fields
|
|
577
|
-
public createPFrame(def: PFrameDef<
|
|
579
|
+
public createPFrame(def: PFrameDef<PColumnDataUniversal>): PFrameHandle {
|
|
578
580
|
this.verifyInlineAndExplicitColumnsSupport(def);
|
|
579
581
|
return this.ctx.createPFrame(
|
|
580
582
|
def.map((c) => transformPColumnData(c)),
|
|
@@ -582,24 +584,24 @@ export class RenderCtx<Args, UiState> {
|
|
|
582
584
|
}
|
|
583
585
|
|
|
584
586
|
// TODO remove all non-PColumn fields
|
|
585
|
-
public createPTable(def: PTableDef<PColumn<
|
|
587
|
+
public createPTable(def: PTableDef<PColumn<PColumnDataUniversal>>): PTableHandle;
|
|
586
588
|
public createPTable(def: {
|
|
587
|
-
columns: PColumn<
|
|
589
|
+
columns: PColumn<PColumnDataUniversal>[];
|
|
588
590
|
filters?: PTableRecordFilter[];
|
|
589
591
|
/** Table sorting */
|
|
590
592
|
sorting?: PTableSorting[];
|
|
591
593
|
}): PTableHandle;
|
|
592
594
|
public createPTable(
|
|
593
595
|
def:
|
|
594
|
-
| PTableDef<PColumn<
|
|
596
|
+
| PTableDef<PColumn<PColumnDataUniversal>>
|
|
595
597
|
| {
|
|
596
|
-
columns: PColumn<
|
|
598
|
+
columns: PColumn<PColumnDataUniversal>[];
|
|
597
599
|
filters?: PTableRecordFilter[];
|
|
598
600
|
/** Table sorting */
|
|
599
601
|
sorting?: PTableSorting[];
|
|
600
602
|
},
|
|
601
603
|
): PTableHandle {
|
|
602
|
-
let rawDef: PTableDef<PColumn<
|
|
604
|
+
let rawDef: PTableDef<PColumn<PColumnDataUniversal>>;
|
|
603
605
|
if ('columns' in def) {
|
|
604
606
|
rawDef = {
|
|
605
607
|
src: {
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import type {
|
|
2
2
|
AnchoredIdDeriver,
|
|
3
3
|
AxisId,
|
|
4
|
-
DataInfo,
|
|
5
4
|
PColumn,
|
|
6
5
|
PColumnSelector,
|
|
7
6
|
PColumnSpec,
|
|
@@ -13,6 +12,7 @@ import type {
|
|
|
13
12
|
PartitionedDataInfoEntries,
|
|
14
13
|
ResolveAnchorsOptions,
|
|
15
14
|
NativePObjectId,
|
|
15
|
+
PColumnValues,
|
|
16
16
|
} from '@milaboratories/pl-model-common';
|
|
17
17
|
import {
|
|
18
18
|
selectorsToPredicate,
|
|
@@ -32,9 +32,17 @@ import type { APColumnSelectorWithSplit, PColumnSelectorWithSplit } from './spli
|
|
|
32
32
|
import canonicalize from 'canonicalize';
|
|
33
33
|
import { getUniquePartitionKeys, convertOrParsePColumnData } from './pcolumn_data';
|
|
34
34
|
import { filterDataInfoEntries } from './axis_filtering';
|
|
35
|
+
import type { PColumnDataUniversal } from '../api';
|
|
36
|
+
|
|
37
|
+
function isPColumnValues(value: unknown): value is PColumnValues {
|
|
38
|
+
if (!Array.isArray(value)) return false;
|
|
39
|
+
if (value.length === 0) return true;
|
|
40
|
+
const first = value[0];
|
|
41
|
+
return typeof first === 'object' && first !== null && 'key' in first && 'val' in first;
|
|
42
|
+
}
|
|
35
43
|
|
|
36
44
|
export interface ColumnProvider {
|
|
37
|
-
selectColumns(selectors: ((spec: PColumnSpec) => boolean) | PColumnSelector | PColumnSelector[]): PColumn<
|
|
45
|
+
selectColumns(selectors: ((spec: PColumnSpec) => boolean) | PColumnSelector | PColumnSelector[]): PColumn<PColumnDataUniversal | undefined>[];
|
|
38
46
|
}
|
|
39
47
|
|
|
40
48
|
export interface AxisLabelProvider {
|
|
@@ -45,13 +53,13 @@ export interface AxisLabelProvider {
|
|
|
45
53
|
* A simple implementation of {@link ColumnProvider} backed by a pre-defined array of columns.
|
|
46
54
|
*/
|
|
47
55
|
class ArrayColumnProvider implements ColumnProvider {
|
|
48
|
-
constructor(private readonly columns: PColumn<
|
|
56
|
+
constructor(private readonly columns: PColumn<PColumnDataUniversal | undefined>[]) {}
|
|
49
57
|
|
|
50
58
|
selectColumns(selectors: ((spec: PColumnSpec) => boolean) | PColumnSelector | PColumnSelector[]):
|
|
51
|
-
PColumn<
|
|
59
|
+
PColumn<PColumnDataUniversal | undefined>[] {
|
|
52
60
|
const predicate = typeof selectors === 'function' ? selectors : selectorsToPredicate(selectors);
|
|
53
61
|
// Filter based on spec, ignoring data type for now
|
|
54
|
-
return this.columns.filter((column): column is PColumn<
|
|
62
|
+
return this.columns.filter((column): column is PColumn<PColumnDataUniversal | undefined> => predicate(column.spec));
|
|
55
63
|
}
|
|
56
64
|
}
|
|
57
65
|
|
|
@@ -59,7 +67,7 @@ export type PColumnEntryWithLabel = {
|
|
|
59
67
|
id: PObjectId;
|
|
60
68
|
spec: PColumnSpec;
|
|
61
69
|
/** Lazy calculates the data, returns undefined if data is not ready. */
|
|
62
|
-
data():
|
|
70
|
+
data(): PColumnDataUniversal | undefined;
|
|
63
71
|
label: string;
|
|
64
72
|
};
|
|
65
73
|
|
|
@@ -79,7 +87,7 @@ type AxisFilterInfo = {
|
|
|
79
87
|
// Intermediate representation for columns requiring splitting
|
|
80
88
|
type IntermediateSplitEntry = {
|
|
81
89
|
type: 'split';
|
|
82
|
-
originalColumn: PColumn<
|
|
90
|
+
originalColumn: PColumn<PColumnDataUniversal | undefined>;
|
|
83
91
|
spec: PColumnSpec;
|
|
84
92
|
/** With splitting axes removed */
|
|
85
93
|
adjustedSpec: PColumnSpec;
|
|
@@ -90,7 +98,7 @@ type IntermediateSplitEntry = {
|
|
|
90
98
|
// Intermediate representation for columns NOT requiring splitting
|
|
91
99
|
type IntermediateDirectEntry = {
|
|
92
100
|
type: 'direct';
|
|
93
|
-
originalColumn: PColumn<
|
|
101
|
+
originalColumn: PColumn<PColumnDataUniversal | undefined>;
|
|
94
102
|
spec: PColumnSpec;
|
|
95
103
|
/** The same as `spec` */
|
|
96
104
|
adjustedSpec: PColumnSpec;
|
|
@@ -169,7 +177,7 @@ type UniversalPColumnOpts = UniversalPColumnOptsNoDeriver & {
|
|
|
169
177
|
} & ResolveAnchorsOptions;
|
|
170
178
|
|
|
171
179
|
export class PColumnCollection {
|
|
172
|
-
private readonly defaultProviderStore: PColumn<
|
|
180
|
+
private readonly defaultProviderStore: PColumn<PColumnDataUniversal | undefined>[] = [];
|
|
173
181
|
private readonly providers: ColumnProvider[] = [new ArrayColumnProvider(this.defaultProviderStore)];
|
|
174
182
|
private readonly axisLabelProviders: AxisLabelProvider[] = [];
|
|
175
183
|
|
|
@@ -185,12 +193,12 @@ export class PColumnCollection {
|
|
|
185
193
|
return this;
|
|
186
194
|
}
|
|
187
195
|
|
|
188
|
-
public addColumns(columns: PColumn<
|
|
196
|
+
public addColumns(columns: PColumn<PColumnDataUniversal | undefined>[]): this {
|
|
189
197
|
this.defaultProviderStore.push(...columns);
|
|
190
198
|
return this;
|
|
191
199
|
}
|
|
192
200
|
|
|
193
|
-
public addColumn(column: PColumn<
|
|
201
|
+
public addColumn(column: PColumn<PColumnDataUniversal | undefined>): this {
|
|
194
202
|
this.defaultProviderStore.push(column);
|
|
195
203
|
return this;
|
|
196
204
|
}
|
|
@@ -256,7 +264,7 @@ export class PColumnCollection {
|
|
|
256
264
|
currentSelector = rawSelector as PColumnSelectorWithSplit | ((spec: PColumnSpec) => boolean);
|
|
257
265
|
|
|
258
266
|
const selectedIds = new Set<PObjectId>();
|
|
259
|
-
const selectedColumns: PColumn<
|
|
267
|
+
const selectedColumns: PColumn<PColumnDataUniversal | undefined>[] = [];
|
|
260
268
|
for (const provider of this.providers) {
|
|
261
269
|
const providerColumns = provider.selectColumns(currentSelector);
|
|
262
270
|
for (const col of providerColumns) {
|
|
@@ -283,6 +291,8 @@ export class PColumnCollection {
|
|
|
283
291
|
const originalSpec = column.spec;
|
|
284
292
|
|
|
285
293
|
if (needsSplitting) {
|
|
294
|
+
if (isPColumnValues(column.data))
|
|
295
|
+
throw new Error(`Splitting is not supported for PColumns with PColumnValues data format. Column id: ${column.id}`);
|
|
286
296
|
const dataEntries = convertOrParsePColumnData(column.data);
|
|
287
297
|
|
|
288
298
|
if (!dataEntries) {
|
|
@@ -417,20 +427,20 @@ export class PColumnCollection {
|
|
|
417
427
|
|
|
418
428
|
public getColumns(
|
|
419
429
|
predicateOrSelectors: ((spec: PColumnSpec) => boolean) | APColumnSelectorWithSplit | APColumnSelectorWithSplit[],
|
|
420
|
-
opts: UniversalPColumnOpts): PColumn<
|
|
430
|
+
opts: UniversalPColumnOpts): PColumn<PColumnDataUniversal>[] | undefined;
|
|
421
431
|
public getColumns(
|
|
422
432
|
predicateOrSelectors: ((spec: PColumnSpec) => boolean) | PColumnSelectorWithSplit | PColumnSelectorWithSplit[],
|
|
423
|
-
opts?: UniversalPColumnOptsNoDeriver): PColumn<
|
|
433
|
+
opts?: UniversalPColumnOptsNoDeriver): PColumn<PColumnDataUniversal>[] | undefined;
|
|
424
434
|
public getColumns(
|
|
425
435
|
predicateOrSelectors: ((spec: PColumnSpec) => boolean) | APColumnSelectorWithSplit | APColumnSelectorWithSplit[],
|
|
426
|
-
opts?: Optional<UniversalPColumnOpts, 'anchorCtx'>): PColumn<
|
|
436
|
+
opts?: Optional<UniversalPColumnOpts, 'anchorCtx'>): PColumn<PColumnDataUniversal>[] | undefined {
|
|
427
437
|
const entries = this.getUniversalEntries(predicateOrSelectors, {
|
|
428
438
|
overrideLabelAnnotation: true, // default for getColumns
|
|
429
439
|
...(opts ?? {}),
|
|
430
440
|
} as UniversalPColumnOpts);
|
|
431
441
|
if (!entries) return undefined;
|
|
432
442
|
|
|
433
|
-
const columns: PColumn<
|
|
443
|
+
const columns: PColumn<PColumnDataUniversal>[] = [];
|
|
434
444
|
for (const entry of entries) {
|
|
435
445
|
const data = entry.data();
|
|
436
446
|
if (!data) {
|