@platforma-sdk/model 1.59.3 → 1.60.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.
- package/dist/block_storage.cjs.map +1 -1
- package/dist/block_storage.d.ts +1 -11
- package/dist/block_storage.js.map +1 -1
- package/dist/block_storage_callbacks.cjs.map +1 -1
- package/dist/block_storage_callbacks.js.map +1 -1
- package/dist/columns/column_collection_builder.cjs +215 -0
- package/dist/columns/column_collection_builder.cjs.map +1 -0
- package/dist/columns/column_collection_builder.d.ts +112 -0
- package/dist/columns/column_collection_builder.js +214 -0
- package/dist/columns/column_collection_builder.js.map +1 -0
- package/dist/columns/column_selector.cjs +122 -0
- package/dist/columns/column_selector.cjs.map +1 -0
- package/dist/columns/column_selector.d.ts +41 -0
- package/dist/columns/column_selector.js +118 -0
- package/dist/columns/column_selector.js.map +1 -0
- package/dist/columns/column_snapshot.cjs +20 -0
- package/dist/columns/column_snapshot.cjs.map +1 -0
- package/dist/columns/column_snapshot.d.ts +39 -0
- package/dist/columns/column_snapshot.js +18 -0
- package/dist/columns/column_snapshot.js.map +1 -0
- package/dist/columns/column_snapshot_provider.cjs +112 -0
- package/dist/columns/column_snapshot_provider.cjs.map +1 -0
- package/dist/columns/column_snapshot_provider.d.ts +73 -0
- package/dist/columns/column_snapshot_provider.js +107 -0
- package/dist/columns/column_snapshot_provider.js.map +1 -0
- package/dist/columns/ctx_column_sources.cjs +84 -0
- package/dist/columns/ctx_column_sources.cjs.map +1 -0
- package/dist/columns/ctx_column_sources.d.ts +33 -0
- package/dist/columns/ctx_column_sources.js +82 -0
- package/dist/columns/ctx_column_sources.js.map +1 -0
- package/dist/columns/index.cjs +5 -0
- package/dist/columns/index.d.ts +5 -0
- package/dist/columns/index.js +5 -0
- package/dist/components/PlDataTable/createPlDataTable/createPlDataTableV2.cjs +111 -0
- package/dist/components/PlDataTable/createPlDataTable/createPlDataTableV2.cjs.map +1 -0
- package/dist/components/PlDataTable/createPlDataTable/createPlDataTableV2.d.ts +25 -0
- package/dist/components/PlDataTable/createPlDataTable/createPlDataTableV2.js +110 -0
- package/dist/components/PlDataTable/createPlDataTable/createPlDataTableV2.js.map +1 -0
- package/dist/components/PlDataTable/{table.cjs → createPlDataTable/createPlDataTableV3.cjs} +54 -54
- package/dist/components/PlDataTable/createPlDataTable/createPlDataTableV3.cjs.map +1 -0
- package/dist/components/PlDataTable/createPlDataTable/createPlDataTableV3.d.ts +39 -0
- package/dist/components/PlDataTable/{table.js → createPlDataTable/createPlDataTableV3.js} +53 -53
- package/dist/components/PlDataTable/createPlDataTable/createPlDataTableV3.js.map +1 -0
- package/dist/components/PlDataTable/createPlDataTable/index.cjs +12 -0
- package/dist/components/PlDataTable/createPlDataTable/index.cjs.map +1 -0
- package/dist/components/PlDataTable/createPlDataTable/index.d.ts +15 -0
- package/dist/components/PlDataTable/createPlDataTable/index.js +12 -0
- package/dist/components/PlDataTable/createPlDataTable/index.js.map +1 -0
- package/dist/components/PlDataTable/createPlDataTableSheet.cjs +18 -0
- package/dist/components/PlDataTable/createPlDataTableSheet.cjs.map +1 -0
- package/dist/components/PlDataTable/createPlDataTableSheet.d.ts +11 -0
- package/dist/components/PlDataTable/createPlDataTableSheet.js +17 -0
- package/dist/components/PlDataTable/createPlDataTableSheet.js.map +1 -0
- package/dist/components/PlDataTable/index.cjs +4 -1
- package/dist/components/PlDataTable/index.d.ts +5 -2
- package/dist/components/PlDataTable/index.js +4 -1
- package/dist/components/PlDataTable/state-migration.cjs.map +1 -1
- package/dist/components/PlDataTable/state-migration.d.ts +2 -2
- package/dist/components/PlDataTable/state-migration.js.map +1 -1
- package/dist/components/PlDataTable/{v4.d.ts → typesV4.d.ts} +2 -2
- package/dist/components/PlDataTable/{v5.d.ts → typesV5.d.ts} +2 -2
- package/dist/components/index.cjs +4 -1
- package/dist/components/index.d.ts +5 -2
- package/dist/components/index.js +4 -1
- package/dist/index.cjs +44 -16
- package/dist/index.d.ts +17 -5
- package/dist/index.js +15 -3
- package/dist/labels/derive_distinct_labels.cjs +156 -0
- package/dist/labels/derive_distinct_labels.cjs.map +1 -0
- package/dist/labels/derive_distinct_labels.d.ts +29 -0
- package/dist/labels/derive_distinct_labels.js +155 -0
- package/dist/labels/derive_distinct_labels.js.map +1 -0
- package/dist/labels/index.cjs +2 -0
- package/dist/labels/index.d.ts +2 -0
- package/dist/labels/index.js +2 -0
- package/dist/labels/write_labels_to_specs.cjs +15 -0
- package/dist/labels/write_labels_to_specs.cjs.map +1 -0
- package/dist/labels/write_labels_to_specs.d.ts +9 -0
- package/dist/labels/write_labels_to_specs.js +14 -0
- package/dist/labels/write_labels_to_specs.js.map +1 -0
- package/dist/package.cjs +1 -1
- package/dist/package.js +1 -1
- package/dist/render/api.cjs +11 -2
- package/dist/render/api.cjs.map +1 -1
- package/dist/render/api.d.ts +9 -5
- package/dist/render/api.js +12 -3
- package/dist/render/api.js.map +1 -1
- package/dist/render/index.d.ts +2 -1
- package/dist/render/index.js +1 -1
- package/dist/render/internal.cjs.map +1 -1
- package/dist/render/internal.d.ts +5 -2
- package/dist/render/internal.js.map +1 -1
- package/dist/render/util/column_collection.cjs +3 -3
- package/dist/render/util/column_collection.cjs.map +1 -1
- package/dist/render/util/column_collection.d.ts +3 -2
- package/dist/render/util/column_collection.js +4 -4
- package/dist/render/util/column_collection.js.map +1 -1
- package/dist/render/util/index.d.ts +2 -1
- package/dist/render/util/index.js +1 -1
- package/dist/render/util/label.cjs +7 -134
- package/dist/render/util/label.cjs.map +1 -1
- package/dist/render/util/label.d.ts +5 -50
- package/dist/render/util/label.js +8 -132
- package/dist/render/util/label.js.map +1 -1
- package/dist/render/util/split_selectors.d.ts +2 -2
- package/package.json +9 -7
- package/src/block_storage.ts +0 -11
- package/src/block_storage_callbacks.ts +1 -1
- package/src/columns/column_collection_builder.test.ts +427 -0
- package/src/columns/column_collection_builder.ts +455 -0
- package/src/columns/column_selector.test.ts +472 -0
- package/src/columns/column_selector.ts +212 -0
- package/src/columns/column_snapshot.ts +55 -0
- package/src/columns/column_snapshot_provider.ts +177 -0
- package/src/columns/ctx_column_sources.ts +107 -0
- package/src/columns/expand_by_partition.test.ts +289 -0
- package/src/columns/expand_by_partition.ts +187 -0
- package/src/columns/index.ts +5 -0
- package/src/components/PlDataTable/createPlDataTable/createPlDataTableV2.ts +193 -0
- package/src/components/PlDataTable/{table.ts → createPlDataTable/createPlDataTableV3.ts} +134 -70
- package/src/components/PlDataTable/createPlDataTable/index.ts +27 -0
- package/src/components/PlDataTable/createPlDataTableSheet.ts +20 -0
- package/src/components/PlDataTable/index.ts +6 -4
- package/src/components/PlDataTable/state-migration.ts +2 -2
- package/src/index.ts +2 -1
- package/src/labels/derive_distinct_labels.test.ts +461 -0
- package/src/labels/derive_distinct_labels.ts +289 -0
- package/src/labels/index.ts +2 -0
- package/src/labels/write_labels_to_specs.ts +12 -0
- package/src/render/api.ts +25 -3
- package/src/render/internal.ts +20 -1
- package/src/render/util/column_collection.ts +9 -6
- package/src/render/util/label.test.ts +1 -1
- package/src/render/util/label.ts +19 -235
- package/src/render/util/split_selectors.ts +3 -3
- package/dist/components/PlDataTable/table.cjs.map +0 -1
- package/dist/components/PlDataTable/table.d.ts +0 -30
- package/dist/components/PlDataTable/table.js.map +0 -1
- /package/src/components/PlDataTable/{v4.ts → typesV4.ts} +0 -0
- /package/src/components/PlDataTable/{v5.ts → typesV5.ts} +0 -0
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
AxisFilterByIdx,
|
|
3
|
+
AxisFilterValue,
|
|
4
|
+
AxisId,
|
|
5
|
+
PartitionedDataInfoEntries,
|
|
6
|
+
} from "@milaboratories/pl-model-common";
|
|
7
|
+
import {
|
|
8
|
+
canonicalizeAxisId,
|
|
9
|
+
entriesToDataInfo,
|
|
10
|
+
getAxisId,
|
|
11
|
+
isPartitionedDataInfoEntries,
|
|
12
|
+
} from "@milaboratories/pl-model-common";
|
|
13
|
+
import type { TreeNodeAccessor } from "../render/accessor";
|
|
14
|
+
import { filterDataInfoEntries } from "../render/util/axis_filtering";
|
|
15
|
+
import { convertOrParsePColumnData, getUniquePartitionKeys } from "../render/util/pcolumn_data";
|
|
16
|
+
import type { ColumnSnapshot } from "./column_snapshot";
|
|
17
|
+
import { createReadyColumnData } from "./column_snapshot";
|
|
18
|
+
|
|
19
|
+
// --- Types ---
|
|
20
|
+
|
|
21
|
+
export interface SplitAxis {
|
|
22
|
+
/** Index of the axis in the column's axesSpec to split by. */
|
|
23
|
+
readonly idx: number;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface ExpandByPartitionOpts {
|
|
27
|
+
/** Resolve axis values to human-readable labels. */
|
|
28
|
+
axisLabels?: (axisId: AxisId) => undefined | Record<string | number, string>;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface ExpandByPartitionResult {
|
|
32
|
+
/** Expanded snapshots (one per key combination per original snapshot). */
|
|
33
|
+
readonly items: ColumnSnapshot[];
|
|
34
|
+
/** False if any column's data was not ready for splitting. */
|
|
35
|
+
readonly complete: boolean;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// --- Implementation ---
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Expand snapshots by splitting along partition axes.
|
|
42
|
+
*
|
|
43
|
+
* For each snapshot, reads partition data, enumerates unique keys on the
|
|
44
|
+
* split axes, and produces one output snapshot per key combination —
|
|
45
|
+
* with the split axes removed from `axesSpec` and a `pl7.app/trace`
|
|
46
|
+
* annotation recording the split origin.
|
|
47
|
+
*
|
|
48
|
+
* Returns `{ items: [], complete: false }` when any snapshot's data
|
|
49
|
+
* is not ready (status !== 'ready' or partition data unavailable).
|
|
50
|
+
*/
|
|
51
|
+
export function expandByPartition(
|
|
52
|
+
snapshots: ColumnSnapshot[],
|
|
53
|
+
splitAxes: SplitAxis[],
|
|
54
|
+
opts?: ExpandByPartitionOpts,
|
|
55
|
+
): ExpandByPartitionResult {
|
|
56
|
+
if (splitAxes.length === 0) {
|
|
57
|
+
return { items: snapshots, complete: true };
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const splitAxisIdxs = splitAxes.map((a) => a.idx).sort((a, b) => a - b);
|
|
61
|
+
const result: ColumnSnapshot[] = [];
|
|
62
|
+
|
|
63
|
+
for (const snapshot of snapshots) {
|
|
64
|
+
if (snapshot.dataStatus !== "ready" || snapshot.data === undefined) {
|
|
65
|
+
return { items: [], complete: false };
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const rawData = snapshot.data.get();
|
|
69
|
+
const dataEntries = convertOrParsePColumnData(rawData as TreeNodeAccessor | undefined);
|
|
70
|
+
|
|
71
|
+
if (dataEntries === undefined) {
|
|
72
|
+
return { items: [], complete: false };
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (!isPartitionedDataInfoEntries(dataEntries)) {
|
|
76
|
+
throw new Error(
|
|
77
|
+
`Splitting requires Partitioned DataInfoEntries, but got ${dataEntries.type} for column ${String(snapshot.id)}`,
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const uniqueKeys = getUniquePartitionKeys(dataEntries);
|
|
82
|
+
|
|
83
|
+
const maxSplitIdx = splitAxisIdxs[splitAxisIdxs.length - 1];
|
|
84
|
+
if (maxSplitIdx >= dataEntries.partitionKeyLength) {
|
|
85
|
+
throw new Error(
|
|
86
|
+
`Not enough partition keys (${dataEntries.partitionKeyLength}) for requested split axes (max index ${maxSplitIdx}) in column ${snapshot.spec.name}`,
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Resolve labels for each split axis
|
|
91
|
+
const axesLabels: (undefined | Record<string | number, string>)[] = splitAxisIdxs.map((idx) =>
|
|
92
|
+
opts?.axisLabels?.(getAxisId(snapshot.spec.axesSpec[idx])),
|
|
93
|
+
);
|
|
94
|
+
|
|
95
|
+
// Generate all key combinations across split axes
|
|
96
|
+
const keyCombinations = generateKeyCombinations(uniqueKeys, splitAxisIdxs);
|
|
97
|
+
if (keyCombinations.length === 0) continue;
|
|
98
|
+
|
|
99
|
+
// Build adjusted axesSpec (remove split axes in reverse order)
|
|
100
|
+
const newAxesSpec = [...snapshot.spec.axesSpec];
|
|
101
|
+
for (let i = splitAxisIdxs.length - 1; i >= 0; i--) {
|
|
102
|
+
newAxesSpec.splice(splitAxisIdxs[i], 1);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
for (const keyCombo of keyCombinations) {
|
|
106
|
+
const axisFilters: AxisFilterByIdx[] = keyCombo.map(
|
|
107
|
+
(value, sAxisIdx): AxisFilterByIdx => [splitAxisIdxs[sAxisIdx], value as AxisFilterValue],
|
|
108
|
+
);
|
|
109
|
+
|
|
110
|
+
const traceEntries = keyCombo.map((value, sAxisIdx) => {
|
|
111
|
+
const axisIdx = splitAxisIdxs[sAxisIdx];
|
|
112
|
+
const axisId = getAxisId(snapshot.spec.axesSpec[axisIdx]);
|
|
113
|
+
const labelMap = axesLabels[sAxisIdx];
|
|
114
|
+
const label = labelMap?.[value] ?? String(value);
|
|
115
|
+
return {
|
|
116
|
+
type: `split:${canonicalizeAxisId(axisId)}`,
|
|
117
|
+
label,
|
|
118
|
+
importance: 1_000_000,
|
|
119
|
+
};
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
const filteredData = filterDataInfoEntries(
|
|
123
|
+
dataEntries as PartitionedDataInfoEntries<TreeNodeAccessor>,
|
|
124
|
+
axisFilters,
|
|
125
|
+
);
|
|
126
|
+
|
|
127
|
+
const adjustedSpec = {
|
|
128
|
+
...snapshot.spec,
|
|
129
|
+
axesSpec: newAxesSpec,
|
|
130
|
+
annotations: {
|
|
131
|
+
...snapshot.spec.annotations,
|
|
132
|
+
"pl7.app/trace": JSON.stringify(traceEntries),
|
|
133
|
+
},
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
result.push({
|
|
137
|
+
id: snapshot.id,
|
|
138
|
+
spec: adjustedSpec,
|
|
139
|
+
dataStatus: "ready",
|
|
140
|
+
data: createReadyColumnData(() => entriesToDataInfo(filteredData)),
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
return { items: result, complete: true };
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const MAX_KEY_COMBINATIONS = 10_000;
|
|
149
|
+
|
|
150
|
+
function generateKeyCombinations(
|
|
151
|
+
uniqueKeys: (string | number)[][],
|
|
152
|
+
splitAxisIdxs: number[],
|
|
153
|
+
): (string | number)[][] {
|
|
154
|
+
const combinations: (string | number)[][] = [];
|
|
155
|
+
|
|
156
|
+
function generate(currentCombo: (string | number)[], sAxisIdx: number): void {
|
|
157
|
+
if (sAxisIdx >= splitAxisIdxs.length) {
|
|
158
|
+
combinations.push([...currentCombo]);
|
|
159
|
+
if (combinations.length > MAX_KEY_COMBINATIONS) {
|
|
160
|
+
throw new Error("Too many key combinations, aborting.");
|
|
161
|
+
}
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const axisIdx = splitAxisIdxs[sAxisIdx];
|
|
166
|
+
if (axisIdx >= uniqueKeys.length) {
|
|
167
|
+
throw new Error(
|
|
168
|
+
`Axis index ${axisIdx} out of bounds for unique keys array (length ${uniqueKeys.length})`,
|
|
169
|
+
);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
const axisValues = uniqueKeys[axisIdx];
|
|
173
|
+
if (!axisValues || axisValues.length === 0) {
|
|
174
|
+
combinations.length = 0;
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
for (const val of axisValues) {
|
|
179
|
+
currentCombo.push(val);
|
|
180
|
+
generate(currentCombo, sAxisIdx + 1);
|
|
181
|
+
currentCombo.pop();
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
generate([], 0);
|
|
186
|
+
return combinations;
|
|
187
|
+
}
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
AxisId,
|
|
3
|
+
PColumn,
|
|
4
|
+
PObjectId,
|
|
5
|
+
PTableColumnId,
|
|
6
|
+
PTableColumnIdAxis,
|
|
7
|
+
PTableColumnIdColumn,
|
|
8
|
+
CanonicalizedJson,
|
|
9
|
+
} from "@milaboratories/pl-model-common";
|
|
10
|
+
import {
|
|
11
|
+
Annotation,
|
|
12
|
+
canonicalizeJson,
|
|
13
|
+
getAxisId,
|
|
14
|
+
getColumnIdAndSpec,
|
|
15
|
+
isLinkerColumn,
|
|
16
|
+
uniqueBy,
|
|
17
|
+
parseJson,
|
|
18
|
+
} from "@milaboratories/pl-model-common";
|
|
19
|
+
import type { PColumnDataUniversal, RenderCtxBase } from "../../../render";
|
|
20
|
+
import { allPColumnsReady, deriveLabels } from "../../../render";
|
|
21
|
+
import { identity } from "es-toolkit";
|
|
22
|
+
import type { CreatePlDataTableOps, PlDataTableFilters, PlDataTableModel } from "../typesV5";
|
|
23
|
+
import { upgradePlDataTableStateV2 } from "../state-migration";
|
|
24
|
+
import type { PlDataTableStateV2 } from "../state-migration";
|
|
25
|
+
import { getAllLabelColumns, getMatchingLabelColumns } from "../labels";
|
|
26
|
+
import { collectFilterSpecColumns } from "../../../filters/traverse";
|
|
27
|
+
import { isEmpty } from "es-toolkit/compat";
|
|
28
|
+
import { createPTableDef, isColumnOptional } from "./createPlDataTableV3";
|
|
29
|
+
|
|
30
|
+
export type createPlDataTableOptionsV2 = {
|
|
31
|
+
columns: PColumn<PColumnDataUniversal>[];
|
|
32
|
+
tableState?: PlDataTableStateV2;
|
|
33
|
+
options?: CreatePlDataTableOps;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Create p-table spec and handle given ui table state
|
|
38
|
+
*
|
|
39
|
+
* @param ctx context
|
|
40
|
+
* @param columns column list
|
|
41
|
+
* @param tableState table ui state
|
|
42
|
+
* @returns PlAgDataTableV2 table source
|
|
43
|
+
*/
|
|
44
|
+
export function createPlDataTableV2<A, U>(
|
|
45
|
+
ctx: RenderCtxBase<A, U>,
|
|
46
|
+
columns: createPlDataTableOptionsV2["columns"],
|
|
47
|
+
tableState?: createPlDataTableOptionsV2["tableState"],
|
|
48
|
+
options?: createPlDataTableOptionsV2["options"],
|
|
49
|
+
): PlDataTableModel | undefined {
|
|
50
|
+
if (columns.length === 0) return undefined;
|
|
51
|
+
|
|
52
|
+
const tableStateNormalized = upgradePlDataTableStateV2(tableState);
|
|
53
|
+
|
|
54
|
+
const allLabelColumns = getAllLabelColumns(ctx.resultPool);
|
|
55
|
+
if (!allLabelColumns) return undefined;
|
|
56
|
+
|
|
57
|
+
let fullLabelColumns = getMatchingLabelColumns(columns.map(getColumnIdAndSpec), allLabelColumns);
|
|
58
|
+
fullLabelColumns = deriveLabels(fullLabelColumns, identity, { includeNativeLabel: true }).map(
|
|
59
|
+
(v) => {
|
|
60
|
+
return {
|
|
61
|
+
...v.value,
|
|
62
|
+
spec: {
|
|
63
|
+
...v.value.spec,
|
|
64
|
+
annotations: {
|
|
65
|
+
...v.value.spec.annotations,
|
|
66
|
+
[Annotation.Label]: v.label,
|
|
67
|
+
},
|
|
68
|
+
},
|
|
69
|
+
};
|
|
70
|
+
},
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
const fullColumns = [...columns, ...fullLabelColumns];
|
|
74
|
+
|
|
75
|
+
const fullColumnsAxes = uniqueBy(
|
|
76
|
+
fullColumns.flatMap((c) => c.spec.axesSpec.map((a) => getAxisId(a))),
|
|
77
|
+
(a) => canonicalizeJson<AxisId>(a),
|
|
78
|
+
);
|
|
79
|
+
const fullColumnsIds: PTableColumnId[] = [
|
|
80
|
+
...fullColumnsAxes.map((a) => ({ type: "axis", id: a }) satisfies PTableColumnIdAxis),
|
|
81
|
+
...fullColumns.map((c) => ({ type: "column", id: c.id }) satisfies PTableColumnIdColumn),
|
|
82
|
+
];
|
|
83
|
+
const fullColumnsIdsSet = new Set(fullColumnsIds.map((c) => canonicalizeJson<PTableColumnId>(c)));
|
|
84
|
+
const isValidColumnId = (id: string): boolean =>
|
|
85
|
+
fullColumnsIdsSet.has(id as CanonicalizedJson<PTableColumnId>);
|
|
86
|
+
|
|
87
|
+
// -- Filtering validation --
|
|
88
|
+
const stateFilters = tableStateNormalized.pTableParams.filters;
|
|
89
|
+
const opsFilters = options?.filters ?? null;
|
|
90
|
+
const filters: null | PlDataTableFilters =
|
|
91
|
+
stateFilters != null && opsFilters != null
|
|
92
|
+
? { type: "and", filters: [stateFilters, opsFilters] }
|
|
93
|
+
: (stateFilters ?? opsFilters);
|
|
94
|
+
const filterColumns = filters ? collectFilterSpecColumns(filters) : [];
|
|
95
|
+
const firstInvalidFilterColumn = filterColumns.find((col) => !isValidColumnId(col));
|
|
96
|
+
if (firstInvalidFilterColumn)
|
|
97
|
+
throw new Error(
|
|
98
|
+
`Invalid filter column ${firstInvalidFilterColumn}: column reference does not match the table columns`,
|
|
99
|
+
);
|
|
100
|
+
|
|
101
|
+
// -- Sorting validation --
|
|
102
|
+
const userSorting = tableStateNormalized.pTableParams.sorting;
|
|
103
|
+
const sorting = (isEmpty(userSorting) ? options?.sorting : userSorting) ?? [];
|
|
104
|
+
const firstInvalidSortingColumn = sorting.find(
|
|
105
|
+
(s) => !isValidColumnId(canonicalizeJson<PTableColumnId>(s.column)),
|
|
106
|
+
);
|
|
107
|
+
if (firstInvalidSortingColumn)
|
|
108
|
+
throw new Error(
|
|
109
|
+
`Invalid sorting column ${JSON.stringify(firstInvalidSortingColumn.column)}: column reference does not match the table columns`,
|
|
110
|
+
);
|
|
111
|
+
|
|
112
|
+
const coreJoinType = options?.coreJoinType ?? "full";
|
|
113
|
+
const fullDef = createPTableDef({
|
|
114
|
+
columns,
|
|
115
|
+
labelColumns: fullLabelColumns,
|
|
116
|
+
coreJoinType,
|
|
117
|
+
filters,
|
|
118
|
+
sorting,
|
|
119
|
+
coreColumnPredicate: options?.coreColumnPredicate,
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
const fullHandle = ctx.createPTableV2(fullDef);
|
|
123
|
+
const pframeHandle = ctx.createPFrame(fullColumns);
|
|
124
|
+
if (!fullHandle || !pframeHandle) return undefined;
|
|
125
|
+
|
|
126
|
+
const hiddenColumns = new Set<PObjectId>(
|
|
127
|
+
((): PObjectId[] => {
|
|
128
|
+
// Inner join works as a filter - all columns must be present
|
|
129
|
+
if (coreJoinType === "inner") return [];
|
|
130
|
+
|
|
131
|
+
const hiddenColIds = tableStateNormalized.pTableParams.hiddenColIds;
|
|
132
|
+
if (hiddenColIds) return hiddenColIds;
|
|
133
|
+
|
|
134
|
+
return columns.filter((c) => isColumnOptional(c.spec)).map((c) => c.id);
|
|
135
|
+
})(),
|
|
136
|
+
);
|
|
137
|
+
|
|
138
|
+
// Preserve linker columns
|
|
139
|
+
columns.filter((c) => isLinkerColumn(c.spec)).forEach((c) => hiddenColumns.delete(c.id));
|
|
140
|
+
|
|
141
|
+
// Preserve core columns as they change the shape of join.
|
|
142
|
+
const coreColumnPredicate = options?.coreColumnPredicate;
|
|
143
|
+
if (coreColumnPredicate) {
|
|
144
|
+
const coreColumns = columns.flatMap((c) =>
|
|
145
|
+
coreColumnPredicate(getColumnIdAndSpec(c)) ? [c.id] : [],
|
|
146
|
+
);
|
|
147
|
+
coreColumns.forEach((c) => hiddenColumns.delete(c));
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// Preserve sorted columns from being hidden
|
|
151
|
+
sorting
|
|
152
|
+
.map((s) => s.column)
|
|
153
|
+
.filter((c): c is PTableColumnIdColumn => c.type === "column")
|
|
154
|
+
.forEach((c) => hiddenColumns.delete(c.id));
|
|
155
|
+
|
|
156
|
+
// Preserve filter columns from being hidden
|
|
157
|
+
if (filters) {
|
|
158
|
+
collectFilterSpecColumns(filters)
|
|
159
|
+
.flatMap((c) => {
|
|
160
|
+
const obj = parseJson(c);
|
|
161
|
+
return obj.type === "column" ? [obj.id] : [];
|
|
162
|
+
})
|
|
163
|
+
.forEach((c) => hiddenColumns.delete(c));
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const visibleColumns = columns.filter((c) => !hiddenColumns.has(c.id));
|
|
167
|
+
const visibleLabelColumns = getMatchingLabelColumns(
|
|
168
|
+
visibleColumns.map(getColumnIdAndSpec),
|
|
169
|
+
allLabelColumns,
|
|
170
|
+
);
|
|
171
|
+
|
|
172
|
+
// if at least one column is not yet computed, we can't show the table
|
|
173
|
+
if (!allPColumnsReady([...visibleColumns, ...visibleLabelColumns])) return undefined;
|
|
174
|
+
|
|
175
|
+
const visibleDef = createPTableDef({
|
|
176
|
+
columns: visibleColumns,
|
|
177
|
+
labelColumns: visibleLabelColumns,
|
|
178
|
+
coreJoinType,
|
|
179
|
+
filters,
|
|
180
|
+
sorting,
|
|
181
|
+
coreColumnPredicate,
|
|
182
|
+
});
|
|
183
|
+
const visibleHandle = ctx.createPTableV2(visibleDef);
|
|
184
|
+
|
|
185
|
+
if (!visibleHandle) return undefined;
|
|
186
|
+
|
|
187
|
+
return {
|
|
188
|
+
sourceId: tableStateNormalized.pTableParams.sourceId,
|
|
189
|
+
fullTableHandle: fullHandle,
|
|
190
|
+
fullPframeHandle: pframeHandle,
|
|
191
|
+
visibleTableHandle: visibleHandle,
|
|
192
|
+
} satisfies PlDataTableModel;
|
|
193
|
+
}
|