@platforma-sdk/ui-vue 1.64.0 → 1.65.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.
Files changed (63) hide show
  1. package/.turbo/turbo-build.log +24 -24
  2. package/.turbo/turbo-formatter$colon$check.log +2 -2
  3. package/.turbo/turbo-linter$colon$check.log +2 -2
  4. package/.turbo/turbo-types$colon$check.log +1 -1
  5. package/CHANGELOG.md +12 -0
  6. package/dist/components/PlAdvancedFilter/FilterEditor.js.map +1 -1
  7. package/dist/components/PlAdvancedFilter/FilterEditor.style.js.map +1 -1
  8. package/dist/components/PlAdvancedFilter/FilterEditor.vue.d.ts +3 -8
  9. package/dist/components/PlAdvancedFilter/FilterEditor.vue.d.ts.map +1 -1
  10. package/dist/components/PlAdvancedFilter/FilterEditor.vue2.js +164 -151
  11. package/dist/components/PlAdvancedFilter/FilterEditor.vue2.js.map +1 -1
  12. package/dist/components/PlAdvancedFilter/PlAdvancedFilter.js.map +1 -1
  13. package/dist/components/PlAdvancedFilter/PlAdvancedFilter.style.js +8 -7
  14. package/dist/components/PlAdvancedFilter/PlAdvancedFilter.style.js.map +1 -1
  15. package/dist/components/PlAdvancedFilter/PlAdvancedFilter.vue.css +1 -1
  16. package/dist/components/PlAdvancedFilter/PlAdvancedFilter.vue.d.ts +24 -8
  17. package/dist/components/PlAdvancedFilter/PlAdvancedFilter.vue.d.ts.map +1 -1
  18. package/dist/components/PlAdvancedFilter/PlAdvancedFilter.vue2.js +176 -110
  19. package/dist/components/PlAdvancedFilter/PlAdvancedFilter.vue2.js.map +1 -1
  20. package/dist/components/PlAdvancedFilter/types.d.ts +2 -0
  21. package/dist/components/PlAdvancedFilter/types.d.ts.map +1 -1
  22. package/dist/components/PlAgDataTable/PlAgDataTableV2.js.map +1 -1
  23. package/dist/components/PlAgDataTable/PlAgDataTableV2.style.js.map +1 -1
  24. package/dist/components/PlAgDataTable/PlAgDataTableV2.vue.d.ts.map +1 -1
  25. package/dist/components/PlAgDataTable/PlAgDataTableV2.vue2.js +116 -109
  26. package/dist/components/PlAgDataTable/PlAgDataTableV2.vue2.js.map +1 -1
  27. package/dist/components/PlAgDataTable/sources/table-source-v2.d.ts +6 -5
  28. package/dist/components/PlAgDataTable/sources/table-source-v2.d.ts.map +1 -1
  29. package/dist/components/PlAgDataTable/sources/table-source-v2.js +26 -26
  30. package/dist/components/PlAgDataTable/sources/table-source-v2.js.map +1 -1
  31. package/dist/components/PlAgDataTable/sources/table-state-v2.d.ts +6 -3
  32. package/dist/components/PlAgDataTable/sources/table-state-v2.d.ts.map +1 -1
  33. package/dist/components/PlAgDataTable/sources/table-state-v2.js +182 -97
  34. package/dist/components/PlAgDataTable/sources/table-state-v2.js.map +1 -1
  35. package/dist/components/PlAnnotations/components/FilterSidebar.js.map +1 -1
  36. package/dist/components/PlAnnotations/components/FilterSidebar.style.js.map +1 -1
  37. package/dist/components/PlAnnotations/components/FilterSidebar.vue.d.ts.map +1 -1
  38. package/dist/components/PlAnnotations/components/FilterSidebar.vue2.js +7 -4
  39. package/dist/components/PlAnnotations/components/FilterSidebar.vue2.js.map +1 -1
  40. package/dist/components/PlTableFilters/PlTableFiltersV2.js.map +1 -1
  41. package/dist/components/PlTableFilters/PlTableFiltersV2.style.js +5 -1
  42. package/dist/components/PlTableFilters/PlTableFiltersV2.style.js.map +1 -1
  43. package/dist/components/PlTableFilters/PlTableFiltersV2.vue.css +1 -1
  44. package/dist/components/PlTableFilters/PlTableFiltersV2.vue.d.ts +7 -9
  45. package/dist/components/PlTableFilters/PlTableFiltersV2.vue.d.ts.map +1 -1
  46. package/dist/components/PlTableFilters/PlTableFiltersV2.vue2.js +73 -42
  47. package/dist/components/PlTableFilters/PlTableFiltersV2.vue2.js.map +1 -1
  48. package/dist/index.d.ts.map +1 -1
  49. package/dist/index.js +2 -0
  50. package/dist/index.js.map +1 -1
  51. package/dist/lib/util/helpers/dist/functions.js.map +1 -1
  52. package/dist/lib/util/helpers/dist/objects.js +4 -1
  53. package/dist/lib/util/helpers/dist/objects.js.map +1 -1
  54. package/package.json +7 -6
  55. package/src/components/PlAdvancedFilter/FilterEditor.vue +99 -55
  56. package/src/components/PlAdvancedFilter/PlAdvancedFilter.vue +163 -95
  57. package/src/components/PlAdvancedFilter/types.ts +6 -1
  58. package/src/components/PlAgDataTable/PlAgDataTableV2.vue +24 -6
  59. package/src/components/PlAgDataTable/sources/table-source-v2.ts +11 -9
  60. package/src/components/PlAgDataTable/sources/table-state-v2.ts +249 -64
  61. package/src/components/PlAnnotations/components/FilterSidebar.vue +3 -2
  62. package/src/components/PlTableFilters/PlTableFiltersV2.vue +75 -21
  63. 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, ref, watch, type Ref, type WritableComputedRef } from "vue";
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
- filtersState: Ref<PlDataTableFiltersWithMeta>;
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
- const isCorrect =
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
- const filtersStateDeepReactive = ref(filtersState);
139
- watch(
140
- () => filtersStateDeepReactive.value,
141
- (newValue) => (filtersState.value = newValue),
142
- { deep: true },
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 { gridState, sheetsState, filtersState: filtersStateDeepReactive, searchString };
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
- function makeDefaultState(): PlDataTableStateV2CacheEntryNullable {
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: null,
174
- gridState: {},
175
- sheetsState: [],
176
- filtersState: null,
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
- function getHiddenColIds(
181
- state: PlDataTableGridStateCore["columnVisibility"],
182
- ): PTableColumnId[] | null {
183
- return state?.hiddenColIds?.map((json) => getPTableColumnId(parseJson(json).source)) ?? null;
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 convertPartitionFiltersToFilterSpec(
187
- sheetsState: PlDataTableSheetState[],
188
- ): FilterSpec<FilterSpecLeaf<CanonicalizedJson<PTableColumnId>>>[] {
189
- return sheetsState.map((s) => {
190
- const column = canonicalizeJson<PTableColumnId>({ type: "axis", id: s.axisId });
191
- return typeof s.value === "number"
192
- ? { type: "equal" as const, column, x: s.value }
193
- : { type: "patternEquals" as const, column, value: s.value };
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
- function convertAgSortingToPTableSorting(state: PlDataTableGridStateCore["sort"]): PTableSorting[] {
198
- return (
199
- state?.sortModel.map((item) => {
200
- const { spec: _, ...column } = parseJson(item.colId).labeled;
201
- return {
202
- column,
203
- ascending: item.sort === "asc",
204
- naAndAbsentAreLeastValues: item.sort === "asc",
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
- function createPTableParams(
240
- state: PlDataTableStateV2CacheEntry,
241
- filterableColumns: PTableColumnSpec[],
242
- ): PTableParamsV2 {
243
- const searchNode = createSearchFilterNode(filterableColumns, state.searchString);
244
- const parts = [
245
- ...convertPartitionFiltersToFilterSpec(state.sheetsState),
246
- ...(state.filtersState ? [state.filtersState] : []),
247
- ...(searchNode ? [searchNode] : []),
248
- ];
249
- const filters: null | PlDataTableFilters = distillFilterSpec(
250
- parts.length === 0 ? null : parts.length === 1 ? parts[0] : { type: "and", filters: parts },
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: state.sourceId,
255
- hiddenColIds: getHiddenColIds(state.gridState.columnVisibility),
256
- filters,
257
- sorting: convertAgSortingToPTableSorting(state.gridState.sort),
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
- v-model:filters="step.filter as PlAdvancedFilter"
119
+ :filters="step.filter as PlAdvancedFilter"
120
120
  :class="[$style.root, { [$commonStyle.disabled]: step.label.length === 0 }]"
121
- :items="props.columns"
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, onMounted, ref } from "vue";
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
- // Check if any filters are active
46
- const filtersOn = computed(() => {
47
- return model.value.filters.length > 0;
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
- function makeFilterColumnId(spec: PTableColumnSpec): CanonicalizedJson<PTableColumnId> {
51
- return canonicalizeJson<PTableColumnId>(getPTableColumnId(spec));
52
- }
60
+ if (isNil(props.defaultFilters)) {
61
+ return props.onUpdateFilters(value);
62
+ }
53
63
 
54
- const items = computed<PlAdvancedFilterItem[]>(() => {
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="mounted && teleportTarget" :to="teleportTarget">
119
- <PlBtnGhost :icon="filtersOn ? 'filter-on' : 'filter'" @click.stop="showManager = true">
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
- v-model:filters="model as PlAdvancedFilter"
130
- :items="items"
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>
package/src/index.ts CHANGED
@@ -1 +1,5 @@
1
+ import { setAutoFreeze } from "immer";
2
+
3
+ setAutoFreeze(false);
4
+
1
5
  export * from "./lib";