@platforma-sdk/ui-vue 1.64.0 → 1.65.3
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 +25 -25
- 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 +19 -0
- package/dist/components/PlAdvancedFilter/FilterEditor.js.map +1 -1
- package/dist/components/PlAdvancedFilter/FilterEditor.style.js.map +1 -1
- package/dist/components/PlAdvancedFilter/FilterEditor.vue.d.ts +3 -8
- package/dist/components/PlAdvancedFilter/FilterEditor.vue.d.ts.map +1 -1
- package/dist/components/PlAdvancedFilter/FilterEditor.vue2.js +164 -151
- package/dist/components/PlAdvancedFilter/FilterEditor.vue2.js.map +1 -1
- package/dist/components/PlAdvancedFilter/PlAdvancedFilter.js.map +1 -1
- package/dist/components/PlAdvancedFilter/PlAdvancedFilter.style.js +8 -7
- package/dist/components/PlAdvancedFilter/PlAdvancedFilter.style.js.map +1 -1
- package/dist/components/PlAdvancedFilter/PlAdvancedFilter.vue.css +1 -1
- package/dist/components/PlAdvancedFilter/PlAdvancedFilter.vue.d.ts +24 -8
- package/dist/components/PlAdvancedFilter/PlAdvancedFilter.vue.d.ts.map +1 -1
- package/dist/components/PlAdvancedFilter/PlAdvancedFilter.vue2.js +176 -110
- package/dist/components/PlAdvancedFilter/PlAdvancedFilter.vue2.js.map +1 -1
- package/dist/components/PlAdvancedFilter/types.d.ts +2 -0
- package/dist/components/PlAdvancedFilter/types.d.ts.map +1 -1
- 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 +116 -109
- package/dist/components/PlAgDataTable/PlAgDataTableV2.vue2.js.map +1 -1
- package/dist/components/PlAgDataTable/sources/table-source-v2.d.ts +6 -5
- package/dist/components/PlAgDataTable/sources/table-source-v2.d.ts.map +1 -1
- package/dist/components/PlAgDataTable/sources/table-source-v2.js +26 -26
- package/dist/components/PlAgDataTable/sources/table-source-v2.js.map +1 -1
- package/dist/components/PlAgDataTable/sources/table-state-v2.d.ts +6 -3
- package/dist/components/PlAgDataTable/sources/table-state-v2.d.ts.map +1 -1
- package/dist/components/PlAgDataTable/sources/table-state-v2.js +182 -97
- package/dist/components/PlAgDataTable/sources/table-state-v2.js.map +1 -1
- package/dist/components/PlAnnotations/components/FilterSidebar.js.map +1 -1
- package/dist/components/PlAnnotations/components/FilterSidebar.style.js.map +1 -1
- package/dist/components/PlAnnotations/components/FilterSidebar.vue.d.ts.map +1 -1
- package/dist/components/PlAnnotations/components/FilterSidebar.vue2.js +7 -4
- package/dist/components/PlAnnotations/components/FilterSidebar.vue2.js.map +1 -1
- package/dist/components/PlTableFilters/PlTableFiltersV2.js.map +1 -1
- package/dist/components/PlTableFilters/PlTableFiltersV2.style.js +5 -1
- package/dist/components/PlTableFilters/PlTableFiltersV2.style.js.map +1 -1
- package/dist/components/PlTableFilters/PlTableFiltersV2.vue.css +1 -1
- package/dist/components/PlTableFilters/PlTableFiltersV2.vue.d.ts +7 -9
- package/dist/components/PlTableFilters/PlTableFiltersV2.vue.d.ts.map +1 -1
- package/dist/components/PlTableFilters/PlTableFiltersV2.vue2.js +73 -42
- package/dist/components/PlTableFilters/PlTableFiltersV2.vue2.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/lib/util/helpers/dist/functions.js.map +1 -1
- package/dist/lib/util/helpers/dist/objects.js +4 -1
- package/dist/lib/util/helpers/dist/objects.js.map +1 -1
- package/package.json +7 -6
- package/src/components/PlAdvancedFilter/FilterEditor.vue +99 -55
- package/src/components/PlAdvancedFilter/PlAdvancedFilter.vue +163 -95
- package/src/components/PlAdvancedFilter/types.ts +6 -1
- package/src/components/PlAgDataTable/PlAgDataTableV2.vue +24 -6
- package/src/components/PlAgDataTable/sources/table-source-v2.ts +11 -9
- package/src/components/PlAgDataTable/sources/table-state-v2.ts +249 -64
- package/src/components/PlAnnotations/components/FilterSidebar.vue +3 -2
- package/src/components/PlTableFilters/PlTableFiltersV2.vue +75 -21
- package/src/index.ts +4 -0
|
@@ -14,15 +14,17 @@ import {
|
|
|
14
14
|
type PlDataTableStateV2Normalized,
|
|
15
15
|
type PTableParamsV2,
|
|
16
16
|
type PTableSorting,
|
|
17
|
+
type PlDataTableFilterSpecLeaf,
|
|
18
|
+
type PlDataTableFilterMeta,
|
|
17
19
|
type PlDataTableFilters,
|
|
18
20
|
distillFilterSpec,
|
|
19
21
|
PlDataTableFiltersWithMeta,
|
|
20
22
|
getPTableColumnId,
|
|
21
23
|
CanonicalizedJson,
|
|
22
24
|
} from "@platforma-sdk/model";
|
|
23
|
-
import { computed,
|
|
25
|
+
import { computed, type Ref, type WritableComputedRef } from "vue";
|
|
24
26
|
import type { PlDataTableSettingsV2 } from "../types";
|
|
25
|
-
import { isJsonEqual, randomInt } from "@milaboratories/helpers";
|
|
27
|
+
import { isJsonEqual, randomInt, getField, Nil } from "@milaboratories/helpers";
|
|
26
28
|
import { computedCached } from "@milaboratories/uikit";
|
|
27
29
|
import { isStringValueType, isNumericValueType } from "../../PlAdvancedFilter/utils";
|
|
28
30
|
import { debounce, isNil } from "es-toolkit";
|
|
@@ -31,11 +33,15 @@ export function useTableState(
|
|
|
31
33
|
tableStateDenormalized: Ref<PlDataTableStateV2>,
|
|
32
34
|
settings: Ref<PlDataTableSettingsV2>,
|
|
33
35
|
columns: Ref<PTableColumnSpec[]>,
|
|
36
|
+
defaultFilters: Ref<Nil | PlDataTableFilters>,
|
|
34
37
|
): {
|
|
35
38
|
gridState: WritableComputedRef<PlDataTableGridStateCore>;
|
|
36
39
|
sheetsState: WritableComputedRef<PlDataTableSheetState[]>;
|
|
37
|
-
|
|
40
|
+
|
|
38
41
|
searchString: WritableComputedRef<string>;
|
|
42
|
+
filtersState: Ref<PlDataTableFiltersWithMeta>;
|
|
43
|
+
defaultFiltersState: Ref<null | PlDataTableFiltersWithMeta>;
|
|
44
|
+
resetDefaultFilters: () => void;
|
|
39
45
|
} {
|
|
40
46
|
const tableStateNormalized = computedCached<PlDataTableStateV2Normalized>({
|
|
41
47
|
get: () => upgradePlDataTableStateV2(tableStateDenormalized.value),
|
|
@@ -112,18 +118,11 @@ export function useTableState(
|
|
|
112
118
|
},
|
|
113
119
|
});
|
|
114
120
|
|
|
121
|
+
// --- User filters (editable by user) ---
|
|
115
122
|
const filtersState = computed<PlDataTableFiltersWithMeta>({
|
|
116
123
|
get: () => {
|
|
117
124
|
const raw = tableState.value.filtersState;
|
|
118
|
-
|
|
119
|
-
raw &&
|
|
120
|
-
(raw.type === "and" || raw.type === "or") &&
|
|
121
|
-
"filters" in raw &&
|
|
122
|
-
Array.isArray(raw.filters);
|
|
123
|
-
|
|
124
|
-
return isCorrect
|
|
125
|
-
? (raw satisfies PlDataTableFiltersWithMeta)
|
|
126
|
-
: { id: randomInt(), type: "and" as const, isExpanded: true, filters: [] };
|
|
125
|
+
return isNil(raw) ? getEmptyGroupWithMeta() : normalizeFiltersState(raw);
|
|
127
126
|
},
|
|
128
127
|
set: (filtersState: PlDataTableFiltersWithMeta) => {
|
|
129
128
|
const oldState = tableState.value;
|
|
@@ -135,12 +134,35 @@ export function useTableState(
|
|
|
135
134
|
}
|
|
136
135
|
},
|
|
137
136
|
});
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
(
|
|
142
|
-
|
|
143
|
-
|
|
137
|
+
|
|
138
|
+
// --- Default filters (from model, separate list) ---
|
|
139
|
+
const defaultFiltersState = computed<null | PlDataTableFiltersWithMeta>({
|
|
140
|
+
get: () => {
|
|
141
|
+
const raw = tableState.value.defaultFiltersState;
|
|
142
|
+
if (!isNil(raw)) {
|
|
143
|
+
return normalizeFiltersState(raw);
|
|
144
|
+
}
|
|
145
|
+
if (!isNil(defaultFilters.value)) {
|
|
146
|
+
return annotateFiltersWithIds(normalizeFiltersState(defaultFilters.value));
|
|
147
|
+
}
|
|
148
|
+
return null;
|
|
149
|
+
},
|
|
150
|
+
set: (defaultFiltersState: null | PlDataTableFiltersWithMeta) => {
|
|
151
|
+
const oldState = tableState.value;
|
|
152
|
+
if (oldState.sourceId) {
|
|
153
|
+
tableState.value = {
|
|
154
|
+
...oldState,
|
|
155
|
+
defaultFiltersState,
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
},
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
function resetDefaultFilters(): void {
|
|
162
|
+
defaultFiltersState.value = isNil(defaultFilters.value)
|
|
163
|
+
? null
|
|
164
|
+
: annotateFiltersWithIds(normalizeFiltersState(defaultFilters.value));
|
|
165
|
+
}
|
|
144
166
|
|
|
145
167
|
const searchString = computed<string>({
|
|
146
168
|
get: () => tableState.value.searchString ?? "",
|
|
@@ -155,9 +177,18 @@ export function useTableState(
|
|
|
155
177
|
},
|
|
156
178
|
});
|
|
157
179
|
|
|
158
|
-
return {
|
|
180
|
+
return {
|
|
181
|
+
gridState,
|
|
182
|
+
sheetsState,
|
|
183
|
+
searchString,
|
|
184
|
+
filtersState,
|
|
185
|
+
defaultFiltersState,
|
|
186
|
+
resetDefaultFilters,
|
|
187
|
+
};
|
|
159
188
|
}
|
|
160
189
|
|
|
190
|
+
// --- Types ---
|
|
191
|
+
|
|
161
192
|
type PlDataTableStateV2CacheEntryNullable =
|
|
162
193
|
| PlDataTableStateV2CacheEntry
|
|
163
194
|
| {
|
|
@@ -165,46 +196,138 @@ type PlDataTableStateV2CacheEntryNullable =
|
|
|
165
196
|
gridState: Record<string, never>;
|
|
166
197
|
sheetsState: [];
|
|
167
198
|
filtersState: null;
|
|
199
|
+
defaultFiltersState: null;
|
|
168
200
|
searchString?: string;
|
|
169
201
|
};
|
|
170
202
|
|
|
171
|
-
|
|
203
|
+
type FilterNode = FilterSpec<PlDataTableFilterSpecLeaf>;
|
|
204
|
+
type AnnotatedFilterSpec = FilterSpec<PlDataTableFilterSpecLeaf, PlDataTableFilterMeta>;
|
|
205
|
+
|
|
206
|
+
// --- Core ---
|
|
207
|
+
|
|
208
|
+
function createPTableParams(
|
|
209
|
+
state: PlDataTableStateV2CacheEntry,
|
|
210
|
+
filterableColumns: PTableColumnSpec[],
|
|
211
|
+
): PTableParamsV2 {
|
|
212
|
+
// User filters: sheets + user filter state + search
|
|
213
|
+
const searchNode = createSearchFilterNode(filterableColumns, state.searchString);
|
|
214
|
+
const unsuppressedUserFilters = isNil(state.filtersState)
|
|
215
|
+
? null
|
|
216
|
+
: stripSuppressedFilters(state.filtersState);
|
|
217
|
+
const userParts = [
|
|
218
|
+
...convertPartitionFiltersToFilterSpec(state.sheetsState),
|
|
219
|
+
...(isNil(unsuppressedUserFilters) ? [] : [unsuppressedUserFilters]),
|
|
220
|
+
...(isNil(searchNode) ? [] : [searchNode]),
|
|
221
|
+
];
|
|
222
|
+
const filters: null | PlDataTableFilters = distillFilterSpec(
|
|
223
|
+
userParts.length === 0
|
|
224
|
+
? null
|
|
225
|
+
: userParts.length === 1
|
|
226
|
+
? userParts[0]
|
|
227
|
+
: { type: "and", filters: userParts },
|
|
228
|
+
);
|
|
229
|
+
const unsuppressedDefaultFilters = isNil(state.defaultFiltersState)
|
|
230
|
+
? null
|
|
231
|
+
: stripSuppressedFilters(state.defaultFiltersState);
|
|
232
|
+
const defaultFilters: null | PlDataTableFilters = isNil(unsuppressedDefaultFilters)
|
|
233
|
+
? null
|
|
234
|
+
: // If all filters are suppressed, we should pass an empty filter group instead of null to prevent fallback to defaults in the model
|
|
235
|
+
(distillFilterSpec(unsuppressedDefaultFilters) ?? getEmptyGroup());
|
|
236
|
+
|
|
172
237
|
return {
|
|
173
|
-
sourceId:
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
238
|
+
sourceId: state.sourceId,
|
|
239
|
+
hiddenColIds: getHiddenColIds(state.gridState.columnVisibility),
|
|
240
|
+
sorting: convertAgSortingToPTableSorting(state.gridState.sort),
|
|
241
|
+
filters,
|
|
242
|
+
defaultFilters,
|
|
177
243
|
};
|
|
178
244
|
}
|
|
179
245
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
)
|
|
183
|
-
|
|
246
|
+
/**
|
|
247
|
+
* Normalizes raw filter state into a valid root filter structure.
|
|
248
|
+
* Valid structure is Root(Group, Group, ...) — double nesting required:
|
|
249
|
+
* root is and/or group, each child is also and/or group containing leaf filters.
|
|
250
|
+
* - null/undefined/invalid → empty root
|
|
251
|
+
* - Leaf node → Root(Group(leaf))
|
|
252
|
+
* - Group with leaf children → Root(Group(leaves...))
|
|
253
|
+
* - Group with group children → as-is
|
|
254
|
+
*/
|
|
255
|
+
function normalizeFiltersState(raw: FilterNode) {
|
|
256
|
+
// Leaf node → wrap in double nesting: Root(Group(leaf))
|
|
257
|
+
if (raw.type !== "and" && raw.type !== "or" && raw.type !== "not") {
|
|
258
|
+
if ("type" in raw && !isNil(raw.type)) {
|
|
259
|
+
return {
|
|
260
|
+
id: randomInt(),
|
|
261
|
+
type: "and" as const,
|
|
262
|
+
isExpanded: true,
|
|
263
|
+
filters: [
|
|
264
|
+
{
|
|
265
|
+
id: randomInt(),
|
|
266
|
+
type: "and" as const,
|
|
267
|
+
isExpanded: true,
|
|
268
|
+
filters: [raw as AnnotatedFilterSpec],
|
|
269
|
+
} as AnnotatedFilterSpec,
|
|
270
|
+
],
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
return getEmptyGroupWithMeta();
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// Already a group — ensure children are also groups (double nesting)
|
|
277
|
+
if ((raw.type === "and" || raw.type === "or") && "filters" in raw && Array.isArray(raw.filters)) {
|
|
278
|
+
const allChildrenAreGroups = raw.filters.every(
|
|
279
|
+
(f: FilterNode) => f.type === "and" || f.type === "or" || f.type === "not",
|
|
280
|
+
);
|
|
281
|
+
if (allChildrenAreGroups) {
|
|
282
|
+
return raw as PlDataTableFiltersWithMeta;
|
|
283
|
+
}
|
|
284
|
+
// Children are leaves — wrap them in a single group
|
|
285
|
+
return {
|
|
286
|
+
id: randomInt(),
|
|
287
|
+
type: raw.type as "and" | "or",
|
|
288
|
+
isExpanded: true,
|
|
289
|
+
filters: [
|
|
290
|
+
{
|
|
291
|
+
id: randomInt(),
|
|
292
|
+
type: raw.type as "and" | "or",
|
|
293
|
+
isExpanded: true,
|
|
294
|
+
filters: raw.filters as AnnotatedFilterSpec[],
|
|
295
|
+
} as AnnotatedFilterSpec,
|
|
296
|
+
],
|
|
297
|
+
};
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
return getEmptyGroupWithMeta();
|
|
184
301
|
}
|
|
185
302
|
|
|
186
|
-
function
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
303
|
+
function getEmptyGroup(): PlDataTableFilters {
|
|
304
|
+
return {
|
|
305
|
+
type: "and" as const,
|
|
306
|
+
filters: [],
|
|
307
|
+
};
|
|
308
|
+
}
|
|
309
|
+
function getEmptyGroupWithMeta(): PlDataTableFiltersWithMeta {
|
|
310
|
+
return {
|
|
311
|
+
...getEmptyGroup(),
|
|
312
|
+
id: randomInt(),
|
|
313
|
+
isExpanded: true,
|
|
314
|
+
} as PlDataTableFiltersWithMeta;
|
|
195
315
|
}
|
|
196
316
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
317
|
+
/**
|
|
318
|
+
* Recursively removes nodes where isSuppressed === true from a PlDataTableFiltersWithMeta tree.
|
|
319
|
+
*/
|
|
320
|
+
function stripSuppressedFilters(node: PlDataTableFiltersWithMeta): PlDataTableFiltersWithMeta {
|
|
321
|
+
return {
|
|
322
|
+
...node,
|
|
323
|
+
filters: node.filters
|
|
324
|
+
.filter((child) => !("isSuppressed" in child && child.isSuppressed === true))
|
|
325
|
+
.map((child) =>
|
|
326
|
+
"filters" in child && (child.type === "and" || child.type === "or")
|
|
327
|
+
? stripSuppressedFilters(child as PlDataTableFiltersWithMeta)
|
|
328
|
+
: child,
|
|
329
|
+
),
|
|
330
|
+
};
|
|
208
331
|
}
|
|
209
332
|
|
|
210
333
|
function createSearchFilterNode(
|
|
@@ -236,24 +359,86 @@ function createSearchFilterNode(
|
|
|
236
359
|
return { type: "or", filters: parts };
|
|
237
360
|
}
|
|
238
361
|
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
362
|
+
// --- Helpers ---
|
|
363
|
+
|
|
364
|
+
/**
|
|
365
|
+
* Recursively ensures every node in a filter tree has an `id` field.
|
|
366
|
+
* Does not set `source` meta — defaults are now a separate list.
|
|
367
|
+
*/
|
|
368
|
+
function annotateFiltersWithIds(
|
|
369
|
+
filters: PlDataTableFilters | PlDataTableFiltersWithMeta,
|
|
370
|
+
): PlDataTableFiltersWithMeta {
|
|
371
|
+
return annotateNodeWithIds(filters) as PlDataTableFiltersWithMeta;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
function annotateNodeWithIds(
|
|
375
|
+
node: FilterNode | PlDataTableFilters | PlDataTableFiltersWithMeta,
|
|
376
|
+
): AnnotatedFilterSpec {
|
|
377
|
+
switch (node.type) {
|
|
378
|
+
case "and":
|
|
379
|
+
return {
|
|
380
|
+
id: getField(node, "id") ?? randomInt(),
|
|
381
|
+
isExpanded: getField(node, "isExpanded") ?? true,
|
|
382
|
+
type: "and" as const,
|
|
383
|
+
filters: node.filters.map((child) => annotateNodeWithIds(child)),
|
|
384
|
+
};
|
|
385
|
+
case "or":
|
|
386
|
+
return {
|
|
387
|
+
id: getField(node, "id") ?? randomInt(),
|
|
388
|
+
isExpanded: getField(node, "isExpanded") ?? true,
|
|
389
|
+
type: "or" as const,
|
|
390
|
+
filters: node.filters.map((child) => annotateNodeWithIds(child)),
|
|
391
|
+
};
|
|
392
|
+
case "not":
|
|
393
|
+
return {
|
|
394
|
+
id: randomInt(),
|
|
395
|
+
isExpanded: true,
|
|
396
|
+
type: "not" as const,
|
|
397
|
+
filter: annotateNodeWithIds(node.filter),
|
|
398
|
+
};
|
|
399
|
+
default:
|
|
400
|
+
return { ...node, id: getField(node, "id") ?? randomInt() } as AnnotatedFilterSpec;
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
// --- Utilities ---
|
|
405
|
+
|
|
406
|
+
function convertPartitionFiltersToFilterSpec(
|
|
407
|
+
sheetsState: PlDataTableSheetState[],
|
|
408
|
+
): FilterSpec<FilterSpecLeaf<CanonicalizedJson<PTableColumnId>>>[] {
|
|
409
|
+
return sheetsState.map((s) => {
|
|
410
|
+
const column = canonicalizeJson<PTableColumnId>({ type: "axis", id: s.axisId });
|
|
411
|
+
return typeof s.value === "number"
|
|
412
|
+
? { type: "equal" as const, column, x: s.value }
|
|
413
|
+
: { type: "patternEquals" as const, column, value: s.value };
|
|
414
|
+
});
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
function convertAgSortingToPTableSorting(state: PlDataTableGridStateCore["sort"]): PTableSorting[] {
|
|
418
|
+
return (
|
|
419
|
+
state?.sortModel.map((item) => {
|
|
420
|
+
const { spec: _, ...column } = parseJson(item.colId).labeled;
|
|
421
|
+
return {
|
|
422
|
+
column,
|
|
423
|
+
ascending: item.sort === "asc",
|
|
424
|
+
naAndAbsentAreLeastValues: item.sort === "asc",
|
|
425
|
+
};
|
|
426
|
+
}) ?? []
|
|
251
427
|
);
|
|
428
|
+
}
|
|
252
429
|
|
|
430
|
+
function getHiddenColIds(
|
|
431
|
+
state: PlDataTableGridStateCore["columnVisibility"],
|
|
432
|
+
): PTableColumnId[] | null {
|
|
433
|
+
return state?.hiddenColIds?.map((json) => getPTableColumnId(parseJson(json).source)) ?? null;
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
function makeDefaultState(): PlDataTableStateV2CacheEntryNullable {
|
|
253
437
|
return {
|
|
254
|
-
sourceId:
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
438
|
+
sourceId: null,
|
|
439
|
+
gridState: {},
|
|
440
|
+
sheetsState: [],
|
|
441
|
+
filtersState: null,
|
|
442
|
+
defaultFiltersState: null,
|
|
258
443
|
};
|
|
259
444
|
}
|
|
@@ -116,13 +116,14 @@ const supportedFilters = [
|
|
|
116
116
|
</template>
|
|
117
117
|
<template #body-content>
|
|
118
118
|
<PlAdvancedFilterComponent
|
|
119
|
-
|
|
119
|
+
:filters="step.filter as PlAdvancedFilter"
|
|
120
120
|
:class="[$style.root, { [$commonStyle.disabled]: step.label.length === 0 }]"
|
|
121
|
-
:
|
|
121
|
+
:options="props.columns"
|
|
122
122
|
:supported-filters="supportedFilters"
|
|
123
123
|
:get-suggest-options="props.getSuggestOptions"
|
|
124
124
|
:enable-dnd="false"
|
|
125
125
|
:enable-add-group-button="true"
|
|
126
|
+
@update-filters="(v) => (step = { ...step, filter: v as typeof step.filter })"
|
|
126
127
|
>
|
|
127
128
|
<template #add-group-buttons>
|
|
128
129
|
<div :class="$style.actions">
|
|
@@ -16,7 +16,7 @@ import {
|
|
|
16
16
|
parseJson,
|
|
17
17
|
getPTableColumnId,
|
|
18
18
|
} from "@platforma-sdk/model";
|
|
19
|
-
import { computed,
|
|
19
|
+
import { computed, ref } from "vue";
|
|
20
20
|
import { PlBtnGhost, PlSlideModal, usePlBlockPageTitleTeleportTarget } from "@milaboratories/uikit";
|
|
21
21
|
import {
|
|
22
22
|
PlAdvancedFilter,
|
|
@@ -26,32 +26,47 @@ import {
|
|
|
26
26
|
} from "../PlAdvancedFilter";
|
|
27
27
|
import type { PlAdvancedFilterColumnId } from "../PlAdvancedFilter/types";
|
|
28
28
|
import type { Nil } from "@milaboratories/helpers";
|
|
29
|
-
import { isNil } from "es-toolkit";
|
|
29
|
+
import { isFunction, isNil } from "es-toolkit";
|
|
30
30
|
|
|
31
|
-
const model = defineModel<PlDataTableFiltersWithMeta>({ required: true });
|
|
32
31
|
const props = defineProps<{
|
|
33
|
-
pframeHandle: Nil | PFrameHandle;
|
|
34
32
|
columns: PTableColumnSpec[];
|
|
33
|
+
pframeHandle: Nil | PFrameHandle;
|
|
34
|
+
filters: PlDataTableFiltersWithMeta;
|
|
35
|
+
defaultFilters: Nil | PlDataTableFiltersWithMeta;
|
|
36
|
+
onUpdateFilters: (value: PlDataTableFiltersWithMeta) => void;
|
|
37
|
+
onResetDefaultFilters?: () => void;
|
|
38
|
+
onUpdateDefaultFilters?: (value: PlDataTableFiltersWithMeta) => void;
|
|
35
39
|
}>();
|
|
36
40
|
|
|
37
|
-
// Teleport for "Filters" button
|
|
38
|
-
const mounted = ref(false);
|
|
39
|
-
onMounted(() => {
|
|
40
|
-
mounted.value = true;
|
|
41
|
-
});
|
|
42
41
|
const teleportTarget = usePlBlockPageTitleTeleportTarget("PlTableFiltersV2");
|
|
43
42
|
const showManager = ref(false);
|
|
43
|
+
const hasFilters = computed(() => props.filters.filters.length > 0);
|
|
44
|
+
const hasDefaultFilters = computed(
|
|
45
|
+
() => !isNil(props.defaultFilters) && props.defaultFilters.filters.length > 0,
|
|
46
|
+
);
|
|
47
|
+
const filters = computed<PlDataTableFiltersWithMeta>(() => {
|
|
48
|
+
if (isNil(props.defaultFilters) || props.defaultFilters?.filters.length === 0) {
|
|
49
|
+
return props.filters;
|
|
50
|
+
}
|
|
44
51
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
52
|
+
return {
|
|
53
|
+
...props.filters,
|
|
54
|
+
filters: [...(props.defaultFilters?.filters ?? []), ...props.filters.filters],
|
|
55
|
+
};
|
|
48
56
|
});
|
|
57
|
+
const onUpdateFilters = (_value: PlAdvancedFilter) => {
|
|
58
|
+
const value = _value as PlDataTableFiltersWithMeta;
|
|
49
59
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
}
|
|
60
|
+
if (isNil(props.defaultFilters)) {
|
|
61
|
+
return props.onUpdateFilters(value);
|
|
62
|
+
}
|
|
53
63
|
|
|
54
|
-
const
|
|
64
|
+
const [defaults, ...rest] = value.filters;
|
|
65
|
+
props.onUpdateFilters({ ...value, filters: rest });
|
|
66
|
+
props.onUpdateDefaultFilters?.({ ...props.defaultFilters, filters: [defaults] });
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
const options = computed<PlAdvancedFilterItem[]>(() => {
|
|
55
70
|
return props.columns.map((col, idx) => {
|
|
56
71
|
const id = makeFilterColumnId(col);
|
|
57
72
|
const label =
|
|
@@ -112,11 +127,18 @@ function handleSuggestOptions(params: {
|
|
|
112
127
|
searchQueryValue: params.searchType === "value" ? params.searchStr : undefined,
|
|
113
128
|
}).then((v) => v.values);
|
|
114
129
|
}
|
|
130
|
+
|
|
131
|
+
function makeFilterColumnId(spec: PTableColumnSpec): CanonicalizedJson<PTableColumnId> {
|
|
132
|
+
return canonicalizeJson<PTableColumnId>(getPTableColumnId(spec));
|
|
133
|
+
}
|
|
115
134
|
</script>
|
|
116
135
|
|
|
117
136
|
<template>
|
|
118
|
-
<Teleport v-if="
|
|
119
|
-
<PlBtnGhost
|
|
137
|
+
<Teleport v-if="teleportTarget" :to="teleportTarget">
|
|
138
|
+
<PlBtnGhost
|
|
139
|
+
:icon="hasFilters || hasDefaultFilters ? 'filter-on' : 'filter'"
|
|
140
|
+
@click.stop="showManager = true"
|
|
141
|
+
>
|
|
120
142
|
Filters
|
|
121
143
|
</PlBtnGhost>
|
|
122
144
|
</Teleport>
|
|
@@ -126,13 +148,31 @@ function handleSuggestOptions(params: {
|
|
|
126
148
|
|
|
127
149
|
<div :class="$style.root">
|
|
128
150
|
<PlAdvancedFilterComponent
|
|
129
|
-
|
|
130
|
-
:
|
|
151
|
+
:filters="filters as PlAdvancedFilter"
|
|
152
|
+
:options="options"
|
|
131
153
|
:supported-filters="supportedFilters"
|
|
132
154
|
:get-suggest-options="handleSuggestOptions"
|
|
155
|
+
:is-pinned="(_, index) => hasDefaultFilters && index === 0"
|
|
156
|
+
:is-removable="(_, index) => (hasDefaultFilters ? index > 0 : true)"
|
|
157
|
+
:is-draggable="(_, index) => (hasDefaultFilters ? index > 0 : true)"
|
|
133
158
|
:enable-dnd="false"
|
|
159
|
+
:enable-toggling="true"
|
|
134
160
|
:enable-add-group-button="true"
|
|
135
|
-
|
|
161
|
+
@update-filters="onUpdateFilters"
|
|
162
|
+
>
|
|
163
|
+
<template #group-title="{ index }">
|
|
164
|
+
<div v-if="hasDefaultFilters && index === 0" :class="$style.defaultGroupTitle">
|
|
165
|
+
Default Group
|
|
166
|
+
<PlBtnGhost
|
|
167
|
+
v-if="isFunction(props.onResetDefaultFilters)"
|
|
168
|
+
icon="restart"
|
|
169
|
+
:class="$style.restartBtn"
|
|
170
|
+
@click.stop="props.onResetDefaultFilters()"
|
|
171
|
+
/>
|
|
172
|
+
</div>
|
|
173
|
+
<div v-else>Custom Group</div>
|
|
174
|
+
</template>
|
|
175
|
+
</PlAdvancedFilterComponent>
|
|
136
176
|
</div>
|
|
137
177
|
</PlSlideModal>
|
|
138
178
|
</template>
|
|
@@ -143,4 +183,18 @@ function handleSuggestOptions(params: {
|
|
|
143
183
|
flex-direction: column;
|
|
144
184
|
gap: 12px;
|
|
145
185
|
}
|
|
186
|
+
.defaultGroupTitle {
|
|
187
|
+
display: flex;
|
|
188
|
+
align-items: center;
|
|
189
|
+
gap: 4px;
|
|
190
|
+
}
|
|
191
|
+
.restartBtn {
|
|
192
|
+
width: 24px;
|
|
193
|
+
height: 24px;
|
|
194
|
+
padding: 4px;
|
|
195
|
+
--button-width: 24px;
|
|
196
|
+
--btn-min-width: 24px;
|
|
197
|
+
--button-height: 24px;
|
|
198
|
+
--btn-min-height: 24x;
|
|
199
|
+
}
|
|
146
200
|
</style>
|