@platforma-sdk/ui-vue 1.63.12 → 1.64.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/.turbo/turbo-build.log +24 -18
- package/.turbo/turbo-formatter$colon$check.log +2 -2
- package/.turbo/turbo-linter$colon$check.log +2 -2
- package/.turbo/turbo-types$colon$check.log +1 -1
- package/CHANGELOG.md +12 -0
- package/dist/components/PlAgDataTable/PlAgDataTableV2.js.map +1 -1
- package/dist/components/PlAgDataTable/PlAgDataTableV2.style.js.map +1 -1
- package/dist/components/PlAgDataTable/PlAgDataTableV2.vue.d.ts.map +1 -1
- package/dist/components/PlAgDataTable/PlAgDataTableV2.vue2.js +1 -1
- package/dist/components/PlAgDataTable/PlAgDataTableV2.vue2.js.map +1 -1
- package/dist/components/PlAgDataTable/compositions/useFilterableColumns.js +3 -3
- package/dist/components/PlAgDataTable/compositions/useFilterableColumns.js.map +1 -1
- package/dist/components/PlAgDataTable/sources/table-source-v2.d.ts.map +1 -1
- package/dist/components/PlAgDataTable/sources/table-source-v2.js +124 -90
- package/dist/components/PlAgDataTable/sources/table-source-v2.js.map +1 -1
- package/dist/components/PlAgDataTable/sources/table-state-v2.d.ts.map +1 -1
- package/dist/components/PlAgDataTable/sources/table-state-v2.js +1 -1
- package/dist/components/PlAgDataTable/sources/table-state-v2.js.map +1 -1
- package/dist/components/PlAgGridColumnManager/PlAgGridColumnManager.js.map +1 -1
- package/dist/components/PlAgGridColumnManager/PlAgGridColumnManager.style.js.map +1 -1
- package/dist/components/PlAgGridColumnManager/PlAgGridColumnManager.vue.d.ts.map +1 -1
- package/dist/components/PlAgGridColumnManager/PlAgGridColumnManager.vue2.js +37 -42
- package/dist/components/PlAgGridColumnManager/PlAgGridColumnManager.vue2.js.map +1 -1
- package/dist/components/PlAgGridColumnManager/useFilteredItems.d.ts +5 -5
- package/dist/components/PlAgGridColumnManager/useFilteredItems.d.ts.map +1 -1
- package/dist/components/PlAgGridColumnManager/useFilteredItems.js +2 -2
- package/dist/components/PlAgGridColumnManager/useFilteredItems.js.map +1 -1
- package/dist/components/PlAgGridColumnManager/useGridColumns.js +14 -0
- package/dist/components/PlAgGridColumnManager/useGridColumns.js.map +1 -0
- package/dist/components/PlTableFilters/PlTableFiltersV2.js.map +1 -1
- package/dist/components/PlTableFilters/PlTableFiltersV2.style.js.map +1 -1
- package/dist/components/PlTableFilters/PlTableFiltersV2.vue.d.ts.map +1 -1
- package/dist/components/PlTableFilters/PlTableFiltersV2.vue2.js +2 -8
- package/dist/components/PlTableFilters/PlTableFiltersV2.vue2.js.map +1 -1
- package/package.json +5 -5
- package/src/components/PlAgDataTable/PlAgDataTableV2.vue +2 -1
- package/src/components/PlAgDataTable/compositions/useFilterableColumns.ts +4 -4
- package/src/components/PlAgDataTable/sources/table-source-v2.ts +224 -126
- package/src/components/PlAgDataTable/sources/table-state-v2.ts +4 -10
- package/src/components/PlAgGridColumnManager/PlAgGridColumnManager.vue +17 -35
- package/src/components/PlAgGridColumnManager/useFilteredItems.ts +9 -11
- package/src/components/PlAgGridColumnManager/useGridColumns.ts +26 -0
- package/src/components/PlTableFilters/PlTableFiltersV2.vue +2 -6
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import type {
|
|
2
2
|
AxesSpec,
|
|
3
3
|
PTableColumnId,
|
|
4
|
+
PTableColumnSpecAxis,
|
|
4
5
|
PTableColumnSpecColumn,
|
|
5
6
|
PTableValue,
|
|
6
7
|
} from "@platforma-sdk/model";
|
|
7
8
|
import {
|
|
8
9
|
canonicalizeJson,
|
|
9
10
|
getAxisId,
|
|
10
|
-
isColumnOptional,
|
|
11
11
|
pTableValue,
|
|
12
12
|
type PFrameDriver,
|
|
13
13
|
type PlDataTableSheet,
|
|
@@ -19,12 +19,14 @@ import {
|
|
|
19
19
|
type PlTableColumnId,
|
|
20
20
|
type PlTableColumnIdJson,
|
|
21
21
|
isLabelColumn as isLabelColumnSpec,
|
|
22
|
+
isLinkerColumn as isLinkerColumnSpec,
|
|
22
23
|
isColumnHidden,
|
|
24
|
+
isColumnOptional,
|
|
23
25
|
matchAxisId,
|
|
24
26
|
readAnnotation,
|
|
27
|
+
readAnnotationJson,
|
|
25
28
|
Annotation,
|
|
26
29
|
ValueType,
|
|
27
|
-
readAnnotationJson,
|
|
28
30
|
getPTableColumnId,
|
|
29
31
|
} from "@platforma-sdk/model";
|
|
30
32
|
import type {
|
|
@@ -47,6 +49,7 @@ import { getColumnRenderingSpec } from "./value-rendering";
|
|
|
47
49
|
import type { Ref } from "vue";
|
|
48
50
|
import { isJsonEqual } from "@milaboratories/helpers";
|
|
49
51
|
import type { DeferredCircular } from "./focus-row";
|
|
52
|
+
import { isNil, uniq } from "es-toolkit";
|
|
50
53
|
|
|
51
54
|
export function isLabelColumn(column: PTableColumnSpec): column is PTableColumnSpecColumn {
|
|
52
55
|
return column.type === "column" && isLabelColumnSpec(column.spec);
|
|
@@ -56,20 +59,24 @@ export function isLabelColumn(column: PTableColumnSpec): column is PTableColumnS
|
|
|
56
59
|
function columns2rows(
|
|
57
60
|
fields: number[],
|
|
58
61
|
columns: PTableVector[],
|
|
59
|
-
|
|
60
|
-
|
|
62
|
+
fieldResultMapping: number[],
|
|
63
|
+
axesResultIndices: number[],
|
|
61
64
|
): PlAgDataTableV2Row[] {
|
|
62
65
|
const rowData: PlAgDataTableV2Row[] = [];
|
|
63
66
|
for (let iRow = 0; iRow < columns[0].data.length; ++iRow) {
|
|
64
|
-
const axesKey: PTableKey =
|
|
65
|
-
pTableValue(columns[resultMapping[iAxis]], iRow),
|
|
66
|
-
);
|
|
67
|
+
const axesKey: PTableKey = axesResultIndices.map((ri) => pTableValue(columns[ri], iRow));
|
|
67
68
|
const id = canonicalizeJson<PlTableRowId>(axesKey);
|
|
68
|
-
const row
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
69
|
+
const row = fields.reduce<PlAgDataTableV2Row>(
|
|
70
|
+
(acc, field, iCol) => {
|
|
71
|
+
acc[field.toString() as `${number}`] =
|
|
72
|
+
fieldResultMapping[iCol] === -1
|
|
73
|
+
? PTableHidden
|
|
74
|
+
: pTableValue(columns[fieldResultMapping[iCol]], iRow);
|
|
75
|
+
return acc;
|
|
76
|
+
},
|
|
77
|
+
{ id, axesKey },
|
|
78
|
+
);
|
|
79
|
+
|
|
73
80
|
rowData.push(row);
|
|
74
81
|
}
|
|
75
82
|
return rowData;
|
|
@@ -98,129 +105,60 @@ export async function calculateGridOptions({
|
|
|
98
105
|
}
|
|
99
106
|
> {
|
|
100
107
|
const stateGeneration = generation.value;
|
|
101
|
-
const stateChangedError = new Error("table state generation changed");
|
|
102
108
|
|
|
103
109
|
// get specs of the full table
|
|
104
|
-
const
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
const pt = model.visibleTableHandle;
|
|
109
|
-
const dataSpecs = await pfDriver.getSpec(pt);
|
|
110
|
-
if (stateGeneration !== generation.value) throw stateChangedError;
|
|
111
|
-
|
|
112
|
-
// create index mapping from full specs to visible subset (hidden columns would have -1)
|
|
113
|
-
const specId = (spec: PTableColumnSpec) =>
|
|
114
|
-
canonicalizeJson<PTableColumnId>(getPTableColumnId(spec));
|
|
115
|
-
const dataSpecsMap = new Map(dataSpecs.entries().map(([i, spec]) => [specId(spec), i]));
|
|
116
|
-
const specsToDataSpecsMapping = new Map(
|
|
117
|
-
specs.entries().map(([i, spec]) => {
|
|
118
|
-
const dataSpecIdx = dataSpecsMap.get(specId(spec)) ?? -1;
|
|
119
|
-
if (dataSpecIdx === -1 && spec.type === "axis")
|
|
120
|
-
throw new Error(`axis ${JSON.stringify(spec.spec)} not present in join result`);
|
|
121
|
-
return [i, dataSpecIdx];
|
|
122
|
-
}),
|
|
123
|
-
);
|
|
110
|
+
const [tableSpecs, visibleTableSpecs] = await Promise.all([
|
|
111
|
+
pfDriver.getSpec(model.fullTableHandle),
|
|
112
|
+
pfDriver.getSpec(model.visibleTableHandle),
|
|
113
|
+
]);
|
|
124
114
|
|
|
125
|
-
|
|
115
|
+
if (stateGeneration !== generation.value) throw new Error("table state generation changed");
|
|
126
116
|
|
|
127
|
-
|
|
128
|
-
const
|
|
117
|
+
// index mapping from full specs to visible subset (hidden columns → -1)
|
|
118
|
+
const specsToVisibleSpecsMapping = buildSpecsToVisibleSpecsMapping(tableSpecs, visibleTableSpecs);
|
|
129
119
|
|
|
130
|
-
const
|
|
131
|
-
const setLabelColumnIndex = (axisId: AxisId, labelColumnIdx: number) => {
|
|
132
|
-
if (!labelColumns.some((info) => matchAxisId(info.axisId, axisId))) {
|
|
133
|
-
labelColumns.push({ axisId, labelColumnIdx });
|
|
134
|
-
} else {
|
|
135
|
-
console.warn(`multiple label columns match axisId: ${JSON.stringify(axisId)}`);
|
|
136
|
-
}
|
|
137
|
-
};
|
|
138
|
-
const getLabelColumnIndex = (axisId: AxisId) => {
|
|
139
|
-
return labelColumns.find((info) => matchAxisId(info.axisId, axisId))?.labelColumnIdx ?? -1;
|
|
140
|
-
};
|
|
120
|
+
const isPartitionedAxis = createPartitionedAxisPredicate(sheets);
|
|
141
121
|
|
|
142
|
-
//
|
|
143
|
-
|
|
144
|
-
.entries()
|
|
145
|
-
.filter(([i, spec]) => {
|
|
146
|
-
switch (spec.type) {
|
|
147
|
-
case "axis":
|
|
148
|
-
return !isPartitionedAxis(spec.id);
|
|
149
|
-
case "column":
|
|
150
|
-
if (isLabelColumnSpec(spec.spec)) {
|
|
151
|
-
const labeledAxisId = getAxisId(spec.spec.axesSpec[0]);
|
|
152
|
-
if (!isPartitionedAxis(labeledAxisId)) {
|
|
153
|
-
setLabelColumnIndex(labeledAxisId, i);
|
|
154
|
-
}
|
|
155
|
-
return false;
|
|
156
|
-
}
|
|
157
|
-
return !isColumnHidden(spec.spec);
|
|
158
|
-
}
|
|
159
|
-
})
|
|
160
|
-
.map(([i]) => i)
|
|
161
|
-
.toArray();
|
|
122
|
+
// label columns indexed by labeled axis (for axis→label replacement later)
|
|
123
|
+
const getLabelColumnIndex = collectLabelColumnsByAxis(tableSpecs, isPartitionedAxis);
|
|
162
124
|
|
|
163
|
-
//
|
|
164
|
-
|
|
165
|
-
|
|
125
|
+
// displayable column indices ordered: axes first, then columns by OrderPriority
|
|
126
|
+
const fields = sortIndicesByTypeAndPriority(
|
|
127
|
+
selectDisplayableIndices(tableSpecs, isPartitionedAxis),
|
|
128
|
+
tableSpecs,
|
|
129
|
+
);
|
|
166
130
|
|
|
167
|
-
|
|
168
|
-
|
|
131
|
+
// same as fields, but each axis replaced by its label column index when available
|
|
132
|
+
const indices = replaceAxesWithLabelColumns(fields, tableSpecs, getLabelColumnIndex);
|
|
169
133
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
});
|
|
134
|
+
// default hidden columns derived from Optional annotation when no saved state
|
|
135
|
+
const resolvedHiddenColIds =
|
|
136
|
+
hiddenColIds ?? computeDefaultHiddenColIds(fields, indices, tableSpecs);
|
|
174
137
|
|
|
175
|
-
// fields are indices of columns that would go to columnDefs
|
|
176
|
-
const fields = [...indices];
|
|
177
|
-
// replace axes with label columns
|
|
178
|
-
indices = indices.map((i) => {
|
|
179
|
-
const spec = specs[i];
|
|
180
|
-
if (spec.type === "axis") {
|
|
181
|
-
const labelColumnIdx = getLabelColumnIndex(spec.id);
|
|
182
|
-
if (labelColumnIdx !== -1) {
|
|
183
|
-
return labelColumnIdx;
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
return i;
|
|
187
|
-
});
|
|
188
138
|
const columnDefs: ColDef<PlAgDataTableV2Row, PTableValue | PTableHidden>[] = [
|
|
189
139
|
makeRowNumberColDef(),
|
|
190
140
|
...fields.map((field, index) =>
|
|
191
|
-
makeColDef(
|
|
141
|
+
makeColDef(
|
|
142
|
+
field,
|
|
143
|
+
tableSpecs[field],
|
|
144
|
+
tableSpecs[indices[index]],
|
|
145
|
+
resolvedHiddenColIds,
|
|
146
|
+
cellButtonAxisParams,
|
|
147
|
+
),
|
|
192
148
|
),
|
|
193
149
|
];
|
|
194
150
|
|
|
195
|
-
//
|
|
196
|
-
const
|
|
197
|
-
.values()
|
|
198
|
-
.filter((spec) => spec.type === "axis")
|
|
199
|
-
.map((spec) => spec.spec)
|
|
200
|
-
.toArray();
|
|
201
|
-
const axes = axesSpec
|
|
202
|
-
.keys()
|
|
203
|
-
.map((i) => {
|
|
204
|
-
let r = indices.indexOf(i);
|
|
205
|
-
if (r === -1) {
|
|
206
|
-
r = indices.length;
|
|
207
|
-
indices.push(i);
|
|
208
|
-
}
|
|
209
|
-
return r;
|
|
210
|
-
})
|
|
211
|
-
.toArray();
|
|
151
|
+
// axes — taken directly from visible table (always present as part of join)
|
|
152
|
+
const visibleAxes = collectVisibleAxes(visibleTableSpecs);
|
|
212
153
|
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
resultMapping.push(-1);
|
|
222
|
-
}
|
|
223
|
-
});
|
|
154
|
+
// request indices: non-hidden display fields + visible axes for row selection keys.
|
|
155
|
+
// Axes replaced by label columns request label data (display); original axis values
|
|
156
|
+
// are fetched via visibleAxes (row keys).
|
|
157
|
+
const { requestIndices, fieldResultMapping, axesResultIndices } = buildRequestIndices(
|
|
158
|
+
indices,
|
|
159
|
+
specsToVisibleSpecsMapping,
|
|
160
|
+
visibleAxes.map(([i]) => i),
|
|
161
|
+
);
|
|
224
162
|
|
|
225
163
|
let rowCount = -1;
|
|
226
164
|
let lastParams: IServerSideGetRowsParams | undefined = undefined;
|
|
@@ -229,7 +167,7 @@ export async function calculateGridOptions({
|
|
|
229
167
|
if (stateGeneration !== generation.value) return params.fail();
|
|
230
168
|
try {
|
|
231
169
|
if (rowCount === -1) {
|
|
232
|
-
const ptShape = await pfDriver.getShape(
|
|
170
|
+
const ptShape = await pfDriver.getShape(model.visibleTableHandle);
|
|
233
171
|
if (stateGeneration !== generation.value || params.api.isDestroyed())
|
|
234
172
|
return params.fail();
|
|
235
173
|
rowCount = ptShape.rows;
|
|
@@ -259,13 +197,13 @@ export async function calculateGridOptions({
|
|
|
259
197
|
) {
|
|
260
198
|
length = Math.min(rowCount, params.request.endRow) - params.request.startRow;
|
|
261
199
|
if (length > 0) {
|
|
262
|
-
const data = await pfDriver.getData(
|
|
200
|
+
const data = await pfDriver.getData(model.visibleTableHandle, requestIndices, {
|
|
263
201
|
offset: params.request.startRow,
|
|
264
202
|
length,
|
|
265
203
|
});
|
|
266
204
|
if (stateGeneration !== generation.value || params.api.isDestroyed())
|
|
267
205
|
return params.fail();
|
|
268
|
-
rowData = columns2rows(fields, data,
|
|
206
|
+
rowData = columns2rows(fields, data, fieldResultMapping, axesResultIndices);
|
|
269
207
|
}
|
|
270
208
|
}
|
|
271
209
|
|
|
@@ -287,7 +225,7 @@ export async function calculateGridOptions({
|
|
|
287
225
|
};
|
|
288
226
|
|
|
289
227
|
return {
|
|
290
|
-
axesSpec,
|
|
228
|
+
axesSpec: visibleAxes.map(([, { spec }]) => spec),
|
|
291
229
|
columnDefs,
|
|
292
230
|
serverSideDatasource,
|
|
293
231
|
};
|
|
@@ -324,16 +262,19 @@ export function makeColDef(
|
|
|
324
262
|
cellStyle.fontFamily = columnRenderingSpec.fontFamily;
|
|
325
263
|
}
|
|
326
264
|
}
|
|
265
|
+
const headerName =
|
|
266
|
+
readAnnotation(labeledSpec.spec, Annotation.Label)?.trim() ??
|
|
267
|
+
readAnnotation(spec.spec, Annotation.Label)?.trim() ??
|
|
268
|
+
`Unlabeled ${spec.type} ${iCol}`;
|
|
269
|
+
|
|
327
270
|
return {
|
|
328
271
|
colId,
|
|
329
272
|
mainMenuItems: defaultMainMenuItems,
|
|
330
273
|
context: spec,
|
|
331
274
|
field: `${iCol}`,
|
|
332
|
-
headerName
|
|
333
|
-
readAnnotation(labeledSpec.spec, Annotation.Label)?.trim() ??
|
|
334
|
-
`Unlabeled ${spec.type} ${iCol}`,
|
|
275
|
+
headerName,
|
|
335
276
|
lockPosition: spec.type === "axis",
|
|
336
|
-
hide: hiddenColIds
|
|
277
|
+
hide: hiddenColIds !== undefined && hiddenColIds.includes(colId),
|
|
337
278
|
valueFormatter: columnRenderingSpec.valueFormatter,
|
|
338
279
|
headerComponent: PlAgColumnHeader,
|
|
339
280
|
cellRendererSelector: cellButtonAxisParams?.showCellButtonForAxisId
|
|
@@ -388,3 +329,160 @@ export function makeColDef(
|
|
|
388
329
|
})(),
|
|
389
330
|
};
|
|
390
331
|
}
|
|
332
|
+
|
|
333
|
+
type LabelColumnLookup = (axisId: AxisId) => number;
|
|
334
|
+
|
|
335
|
+
/** Build index mapping from full tableSpecs to their position in visibleTableSpecs (missing → -1). */
|
|
336
|
+
function buildSpecsToVisibleSpecsMapping(
|
|
337
|
+
tableSpecs: PTableColumnSpec[],
|
|
338
|
+
visibleTableSpecs: PTableColumnSpec[],
|
|
339
|
+
): Map<number, number> {
|
|
340
|
+
const specId = (spec: PTableColumnSpec) =>
|
|
341
|
+
canonicalizeJson<PTableColumnId>(getPTableColumnId(spec));
|
|
342
|
+
const visibleSpecsMap = new Map(
|
|
343
|
+
visibleTableSpecs.entries().map(([i, spec]) => [specId(spec), i] as const),
|
|
344
|
+
);
|
|
345
|
+
return new Map(
|
|
346
|
+
tableSpecs.entries().map(([i, spec]) => {
|
|
347
|
+
const visibleSpecIdx = visibleSpecsMap.get(specId(spec));
|
|
348
|
+
return [i, isNil(visibleSpecIdx) ? -1 : visibleSpecIdx];
|
|
349
|
+
}),
|
|
350
|
+
);
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
/** Predicate that returns true when an axis is one of the sheet partition axes. */
|
|
354
|
+
function createPartitionedAxisPredicate(sheets: PlDataTableSheet[]): (axisId: AxisId) => boolean {
|
|
355
|
+
const sheetAxesIds = sheets.map((sheet) => getAxisId(sheet.axis));
|
|
356
|
+
return (axisId) => sheetAxesIds.some((id) => matchAxisId(id, axisId));
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
/**
|
|
360
|
+
* Collect label columns (skipping partitioned axes and duplicates) and return a
|
|
361
|
+
* lookup function that resolves labeled axisId → label column index (or -1).
|
|
362
|
+
*/
|
|
363
|
+
function collectLabelColumnsByAxis(
|
|
364
|
+
tableSpecs: PTableColumnSpec[],
|
|
365
|
+
isPartitionedAxis: (axisId: AxisId) => boolean,
|
|
366
|
+
): LabelColumnLookup {
|
|
367
|
+
const labelColumns: { axisId: AxisId; labelColumnIdx: number }[] = [];
|
|
368
|
+
for (const [i, spec] of tableSpecs.entries()) {
|
|
369
|
+
if (spec.type !== "column" || !isLabelColumnSpec(spec.spec)) continue;
|
|
370
|
+
const labeledAxisId = getAxisId(spec.spec.axesSpec[0]);
|
|
371
|
+
if (isPartitionedAxis(labeledAxisId)) continue;
|
|
372
|
+
if (labelColumns.some((info) => matchAxisId(info.axisId, labeledAxisId))) {
|
|
373
|
+
console.warn(`multiple label columns match axisId: ${JSON.stringify(labeledAxisId)}`);
|
|
374
|
+
continue;
|
|
375
|
+
}
|
|
376
|
+
labelColumns.push({ axisId: labeledAxisId, labelColumnIdx: i });
|
|
377
|
+
}
|
|
378
|
+
return (axisId) =>
|
|
379
|
+
labelColumns.find((info) => matchAxisId(info.axisId, axisId))?.labelColumnIdx ?? -1;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
/** Indices of columns to display: drop partitioned axes, label/linker columns, hidden columns. */
|
|
383
|
+
function selectDisplayableIndices(
|
|
384
|
+
tableSpecs: PTableColumnSpec[],
|
|
385
|
+
isPartitionedAxis: (axisId: AxisId) => boolean,
|
|
386
|
+
): number[] {
|
|
387
|
+
return tableSpecs
|
|
388
|
+
.entries()
|
|
389
|
+
.filter(([, spec]) => {
|
|
390
|
+
switch (spec.type) {
|
|
391
|
+
case "axis":
|
|
392
|
+
return !isPartitionedAxis(spec.id);
|
|
393
|
+
case "column":
|
|
394
|
+
return (
|
|
395
|
+
!isLabelColumnSpec(spec.spec) &&
|
|
396
|
+
!isColumnHidden(spec.spec) &&
|
|
397
|
+
!isLinkerColumnSpec(spec.spec)
|
|
398
|
+
);
|
|
399
|
+
}
|
|
400
|
+
})
|
|
401
|
+
.map(([i]) => i)
|
|
402
|
+
.toArray();
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
/** Sort: axes first, then columns by OrderPriority annotation (higher priority = further left). */
|
|
406
|
+
function sortIndicesByTypeAndPriority(indices: number[], tableSpecs: PTableColumnSpec[]): number[] {
|
|
407
|
+
const priorityOf = (i: number): number => {
|
|
408
|
+
const spec = tableSpecs[i];
|
|
409
|
+
return spec.type === "column"
|
|
410
|
+
? (readAnnotationJson(spec.spec, Annotation.Table.OrderPriority) ?? 0)
|
|
411
|
+
: 0;
|
|
412
|
+
};
|
|
413
|
+
return [...indices].sort((a, b) => {
|
|
414
|
+
if (tableSpecs[a].type !== tableSpecs[b].type) {
|
|
415
|
+
return tableSpecs[a].type === "axis" ? -1 : 1;
|
|
416
|
+
}
|
|
417
|
+
return priorityOf(b) - priorityOf(a);
|
|
418
|
+
});
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
/** For each axis entry substitute the index of its matching label column when one exists. */
|
|
422
|
+
function replaceAxesWithLabelColumns(
|
|
423
|
+
fields: number[],
|
|
424
|
+
tableSpecs: PTableColumnSpec[],
|
|
425
|
+
getLabelColumnIndex: LabelColumnLookup,
|
|
426
|
+
): number[] {
|
|
427
|
+
return fields.map((i) => {
|
|
428
|
+
const spec = tableSpecs[i];
|
|
429
|
+
const labelIdx = spec.type === "axis" ? getLabelColumnIndex(spec.id) : -1;
|
|
430
|
+
return labelIdx === -1 ? i : labelIdx;
|
|
431
|
+
});
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
/** Default hidden col ids built from columns marked with the Optional annotation. */
|
|
435
|
+
function computeDefaultHiddenColIds(
|
|
436
|
+
fields: number[],
|
|
437
|
+
indices: number[],
|
|
438
|
+
tableSpecs: PTableColumnSpec[],
|
|
439
|
+
): PlTableColumnIdJson[] {
|
|
440
|
+
return fields.reduce<PlTableColumnIdJson[]>((acc, field, i) => {
|
|
441
|
+
const spec = tableSpecs[field];
|
|
442
|
+
if (spec.type !== "column" || !isColumnOptional(spec.spec)) return acc;
|
|
443
|
+
const labeledSpec = tableSpecs[indices[i]];
|
|
444
|
+
return [...acc, canonicalizeJson<PlTableColumnId>({ source: spec, labeled: labeledSpec })];
|
|
445
|
+
}, []);
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
/** Extract axis indices and specs from the visible table (always present as part of join). */
|
|
449
|
+
function collectVisibleAxes(
|
|
450
|
+
visibleTableSpecs: PTableColumnSpec[],
|
|
451
|
+
): [number, PTableColumnSpecAxis][] {
|
|
452
|
+
return visibleTableSpecs
|
|
453
|
+
.entries()
|
|
454
|
+
.filter((entry): entry is [number, PTableColumnSpecAxis] => entry[1].type === "axis")
|
|
455
|
+
.toArray();
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
/**
|
|
459
|
+
* Compose request indices for the visible table:
|
|
460
|
+
* non-hidden display fields first, then visible axes (deduplicated).
|
|
461
|
+
* Returns fieldResultMapping (display field → position in requestIndices, -1 if not requested)
|
|
462
|
+
* and axesResultIndices (visible axis → position in requestIndices).
|
|
463
|
+
*/
|
|
464
|
+
function buildRequestIndices(
|
|
465
|
+
indices: number[],
|
|
466
|
+
specsToVisibleSpecsMapping: Map<number, number>,
|
|
467
|
+
visibleAxesIndices: number[],
|
|
468
|
+
): {
|
|
469
|
+
requestIndices: number[];
|
|
470
|
+
fieldResultMapping: number[];
|
|
471
|
+
axesResultIndices: number[];
|
|
472
|
+
} {
|
|
473
|
+
const resolved = indices.map((displayField) => {
|
|
474
|
+
const idx = specsToVisibleSpecsMapping.get(displayField);
|
|
475
|
+
return idx === undefined || idx === -1 ? null : idx;
|
|
476
|
+
});
|
|
477
|
+
const requestedFields = resolved.filter((v): v is number => v !== null);
|
|
478
|
+
|
|
479
|
+
const fieldResultMapping: number[] = [];
|
|
480
|
+
let pos = 0;
|
|
481
|
+
for (const v of resolved) {
|
|
482
|
+
fieldResultMapping.push(v === null ? -1 : pos++);
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
const requestIndices = uniq([...requestedFields, ...visibleAxesIndices]);
|
|
486
|
+
const axesResultIndices = visibleAxesIndices.map((vi) => requestIndices.indexOf(vi));
|
|
487
|
+
return { requestIndices, fieldResultMapping, axesResultIndices };
|
|
488
|
+
}
|
|
@@ -12,7 +12,6 @@ import {
|
|
|
12
12
|
type PlDataTableStateV2,
|
|
13
13
|
type PlDataTableStateV2CacheEntry,
|
|
14
14
|
type PlDataTableStateV2Normalized,
|
|
15
|
-
type PObjectId,
|
|
16
15
|
type PTableParamsV2,
|
|
17
16
|
type PTableSorting,
|
|
18
17
|
type PlDataTableFilters,
|
|
@@ -178,15 +177,10 @@ function makeDefaultState(): PlDataTableStateV2CacheEntryNullable {
|
|
|
178
177
|
};
|
|
179
178
|
}
|
|
180
179
|
|
|
181
|
-
function getHiddenColIds(
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
acc.push(c.source.id);
|
|
186
|
-
}
|
|
187
|
-
return acc;
|
|
188
|
-
}, [] as PObjectId[]) ?? null
|
|
189
|
-
);
|
|
180
|
+
function getHiddenColIds(
|
|
181
|
+
state: PlDataTableGridStateCore["columnVisibility"],
|
|
182
|
+
): PTableColumnId[] | null {
|
|
183
|
+
return state?.hiddenColIds?.map((json) => getPTableColumnId(parseJson(json).source)) ?? null;
|
|
190
184
|
}
|
|
191
185
|
|
|
192
186
|
function convertPartitionFiltersToFilterSpec(
|
|
@@ -6,10 +6,11 @@ import {
|
|
|
6
6
|
PlSlideModal,
|
|
7
7
|
usePlBlockPageTitleTeleportTarget,
|
|
8
8
|
} from "@milaboratories/uikit";
|
|
9
|
-
import { type
|
|
10
|
-
import { computed, ref
|
|
9
|
+
import { type GridApi } from "ag-grid-enterprise";
|
|
10
|
+
import { computed, ref } from "vue";
|
|
11
11
|
import { PlAgDataTableRowNumberColId } from "../PlAgDataTable/sources/row-number";
|
|
12
12
|
import { useFilteredItems } from "./useFilteredItems";
|
|
13
|
+
import { useGridColumns } from "./useGridColumns";
|
|
13
14
|
|
|
14
15
|
const props = defineProps<{
|
|
15
16
|
/**
|
|
@@ -25,44 +26,25 @@ const props = defineProps<{
|
|
|
25
26
|
width?: string;
|
|
26
27
|
}>();
|
|
27
28
|
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
const
|
|
31
|
-
watch(
|
|
32
|
-
() => gridApi.value,
|
|
33
|
-
(gridApi) => {
|
|
34
|
-
if (gridApi.isDestroyed()) return;
|
|
35
|
-
|
|
36
|
-
gridApi.addEventListener("displayedColumnsChanged", (event: DisplayedColumnsChangedEvent) => {
|
|
37
|
-
columns.value = event.api.getAllGridColumns();
|
|
38
|
-
});
|
|
29
|
+
const query = ref("");
|
|
30
|
+
const slideModal = ref(false);
|
|
31
|
+
const teleportTarget = usePlBlockPageTitleTeleportTarget("PlAgGridColumnManager");
|
|
39
32
|
|
|
40
|
-
|
|
41
|
-
if (columns.value.length > 0) {
|
|
42
|
-
gridApi.moveColumns(columns.value, 0);
|
|
43
|
-
}
|
|
44
|
-
},
|
|
45
|
-
{ immediate: true },
|
|
46
|
-
);
|
|
33
|
+
const columns = useGridColumns(props);
|
|
47
34
|
|
|
48
35
|
const items = computed(() => {
|
|
49
|
-
return columns.value.map((col) => ({
|
|
36
|
+
return columns.value.map((col, i) => ({
|
|
50
37
|
column: col,
|
|
51
38
|
id: col.getId(),
|
|
52
|
-
label: col.getColDef().headerName
|
|
39
|
+
label: col.getColDef().headerName ?? `Unnamed Column (${i + 1})`,
|
|
53
40
|
}));
|
|
54
41
|
});
|
|
55
42
|
|
|
56
|
-
const
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
const teleportTarget = usePlBlockPageTitleTeleportTarget("PlAgGridColumnManager");
|
|
60
|
-
|
|
61
|
-
const { filteredItems, segments } = useFilteredItems(() => ({
|
|
62
|
-
items: items.value,
|
|
63
|
-
query: query.value,
|
|
43
|
+
const { filteredItems, segments } = useFilteredItems({
|
|
44
|
+
items,
|
|
45
|
+
query,
|
|
64
46
|
getStrings: (item) => [item.label],
|
|
65
|
-
})
|
|
47
|
+
});
|
|
66
48
|
</script>
|
|
67
49
|
|
|
68
50
|
<template>
|
|
@@ -79,17 +61,17 @@ const { filteredItems, segments } = useFilteredItems(() => ({
|
|
|
79
61
|
:is-draggable="(item) => !item.column.getColDef().lockPosition"
|
|
80
62
|
:on-sort="
|
|
81
63
|
(fromIndex, toIndex) => {
|
|
82
|
-
if (!
|
|
64
|
+
if (!props.api.isDestroyed()) {
|
|
83
65
|
const columnToMove = columns[fromIndex];
|
|
84
|
-
|
|
66
|
+
props.api.moveColumns([columnToMove], toIndex);
|
|
85
67
|
}
|
|
86
68
|
return true; // Let PlElementList handle the visual update
|
|
87
69
|
}
|
|
88
70
|
"
|
|
89
71
|
:on-toggle="
|
|
90
72
|
(item) => {
|
|
91
|
-
if (!
|
|
92
|
-
|
|
73
|
+
if (!props.api.isDestroyed()) {
|
|
74
|
+
props.api.setColumnsVisible([item.column], !item.column.isVisible());
|
|
93
75
|
}
|
|
94
76
|
}
|
|
95
77
|
"
|
|
@@ -1,25 +1,23 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Ref, computed, toValue } from "vue";
|
|
2
2
|
|
|
3
|
-
export function useFilteredItems<T>(
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
}>,
|
|
9
|
-
) {
|
|
3
|
+
export function useFilteredItems<T>(props: {
|
|
4
|
+
items: Ref<T[]>;
|
|
5
|
+
query: Ref<string>;
|
|
6
|
+
getStrings: (item: T) => Iterable<string>;
|
|
7
|
+
}) {
|
|
10
8
|
const result = computed(() => {
|
|
11
9
|
const { items, query, getStrings } = toValue(props);
|
|
12
10
|
const filteredItems: T[] = [];
|
|
13
11
|
const segments = new Map<string, StringSegment[]>();
|
|
14
|
-
for (const item of items) {
|
|
12
|
+
for (const item of items.value) {
|
|
15
13
|
let kept = false;
|
|
16
14
|
for (const string of getStrings(item)) {
|
|
17
15
|
let stringSegments = segments.get(string);
|
|
18
16
|
if (!stringSegments) {
|
|
19
|
-
stringSegments = matchSubstrings(string, query);
|
|
17
|
+
stringSegments = matchSubstrings(string, query.value);
|
|
20
18
|
segments.set(string, stringSegments);
|
|
21
19
|
}
|
|
22
|
-
if (!kept && (!query || stringSegments.some(({ match }) => match))) {
|
|
20
|
+
if (!kept && (!query.value || stringSegments.some(({ match }) => match))) {
|
|
23
21
|
filteredItems.push(item);
|
|
24
22
|
kept = true;
|
|
25
23
|
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { Column, GridApi } from "ag-grid-enterprise";
|
|
2
|
+
import { ref, watch } from "vue";
|
|
3
|
+
|
|
4
|
+
export function useGridColumns(props: { api: GridApi }) {
|
|
5
|
+
const columns = ref<Column[]>([]);
|
|
6
|
+
const syncColumns = () => {
|
|
7
|
+
columns.value = props.api.getAllGridColumns();
|
|
8
|
+
};
|
|
9
|
+
watch(
|
|
10
|
+
() => props.api,
|
|
11
|
+
(gridApi, _, onCleanup) => {
|
|
12
|
+
if (gridApi.isDestroyed()) return;
|
|
13
|
+
|
|
14
|
+
gridApi.addEventListener("displayedColumnsChanged", syncColumns);
|
|
15
|
+
onCleanup(() => gridApi.removeEventListener("displayedColumnsChanged", syncColumns));
|
|
16
|
+
|
|
17
|
+
columns.value = gridApi.getAllGridColumns();
|
|
18
|
+
if (columns.value.length > 0) {
|
|
19
|
+
gridApi.moveColumns(columns.value, 0);
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
{ immediate: true },
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
return columns;
|
|
26
|
+
}
|
|
@@ -12,9 +12,9 @@ import {
|
|
|
12
12
|
Domain,
|
|
13
13
|
readAnnotation,
|
|
14
14
|
readDomain,
|
|
15
|
-
getAxisId,
|
|
16
15
|
getUniqueSourceValuesWithLabels,
|
|
17
16
|
parseJson,
|
|
17
|
+
getPTableColumnId,
|
|
18
18
|
} from "@platforma-sdk/model";
|
|
19
19
|
import { computed, onMounted, ref } from "vue";
|
|
20
20
|
import { PlBtnGhost, PlSlideModal, usePlBlockPageTitleTeleportTarget } from "@milaboratories/uikit";
|
|
@@ -48,11 +48,7 @@ const filtersOn = computed(() => {
|
|
|
48
48
|
});
|
|
49
49
|
|
|
50
50
|
function makeFilterColumnId(spec: PTableColumnSpec): CanonicalizedJson<PTableColumnId> {
|
|
51
|
-
|
|
52
|
-
spec.type === "axis"
|
|
53
|
-
? { type: "axis", id: getAxisId(spec.spec) }
|
|
54
|
-
: { type: "column", id: spec.id };
|
|
55
|
-
return canonicalizeJson<PTableColumnId>(id);
|
|
51
|
+
return canonicalizeJson<PTableColumnId>(getPTableColumnId(spec));
|
|
56
52
|
}
|
|
57
53
|
|
|
58
54
|
const items = computed<PlAdvancedFilterItem[]>(() => {
|