@ulu/frontend-vue 0.1.2-beta.7 → 0.1.2-beta.9
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.
|
@@ -9,9 +9,11 @@
|
|
|
9
9
|
* @param {Boolean} [options.noDefaultSorts=false] - If true, the default 'A-Z' and 'Z-A' sorts will not be included.
|
|
10
10
|
* @param {Object} [options.extraSortTypes={}] - Additional sort types to be merged with the default ones.
|
|
11
11
|
* @param {Object} [options.searchOptions={}] - Configuration options for Fuse.js.
|
|
12
|
-
* @param {Function} [options.getItemFacet] - A function to retrieve facet information from an item. Should always return an array of values.
|
|
13
12
|
* @param {Function} [options.getSortValue] - A function to get the value to sort by from an item.
|
|
14
13
|
* @param {String} [options.countMode='none'] - The mode for calculating facet counts. Can be 'none', 'simple', or 'intuitive'.
|
|
14
|
+
* @param {Object} [options.urlSync] - Optional configuration to sync state with URL.
|
|
15
|
+
* @param {import('vue-router').Router} [options.urlSync.router] - The Vue Router instance.
|
|
16
|
+
* @param {import('vue-router').RouteLocationNormalizedLoaded} [options.urlSync.route] - The current route instance.
|
|
15
17
|
*/
|
|
16
18
|
export function useFacets(allItems: import("vue").Ref<Array<Object>>, options?: {
|
|
17
19
|
initialFacets?: any[] | undefined;
|
|
@@ -21,9 +23,12 @@ export function useFacets(allItems: import("vue").Ref<Array<Object>>, options?:
|
|
|
21
23
|
noDefaultSorts?: boolean | undefined;
|
|
22
24
|
extraSortTypes?: Object | undefined;
|
|
23
25
|
searchOptions?: Object | undefined;
|
|
24
|
-
getItemFacet?: Function | undefined;
|
|
25
26
|
getSortValue?: Function | undefined;
|
|
26
27
|
countMode?: string | undefined;
|
|
28
|
+
urlSync?: {
|
|
29
|
+
router?: import("vue-router").Router | undefined;
|
|
30
|
+
route?: import("vue-router").RouteLocationNormalizedLoadedGeneric | undefined;
|
|
31
|
+
} | undefined;
|
|
27
32
|
}): {
|
|
28
33
|
facets: import("vue").Ref<never[], never[]>;
|
|
29
34
|
searchValue: import("vue").Ref<string, string>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useFacets.d.ts","sourceRoot":"","sources":["../../../../../lib/components/systems/facets/useFacets.js"],"names":[],"mappings":"AA+GA
|
|
1
|
+
{"version":3,"file":"useFacets.d.ts","sourceRoot":"","sources":["../../../../../lib/components/systems/facets/useFacets.js"],"names":[],"mappings":"AA+GA;;;;;;;;;;;;;;;;GAgBG;AACH,oCAfW,OAAO,KAAK,EAAE,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,YAExC;IAAwB,aAAa;IACb,WAAW;IACV,kBAAkB;IAClB,eAAe;IACd,cAAc;IACf,cAAc;IACd,aAAa;IACX,YAAY;IACd,SAAS;IACT,OAAO;;;;CAGlC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAkWA"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ref, computed, watch } from 'vue';
|
|
1
|
+
import { ref, computed, watch, nextTick, watchPostEffect } from 'vue';
|
|
2
2
|
import Fuse from 'fuse.js';
|
|
3
3
|
|
|
4
4
|
/**
|
|
@@ -120,17 +120,13 @@ function generateInitialFacets(allItems, facetFields) {
|
|
|
120
120
|
* @param {Boolean} [options.noDefaultSorts=false] - If true, the default 'A-Z' and 'Z-A' sorts will not be included.
|
|
121
121
|
* @param {Object} [options.extraSortTypes={}] - Additional sort types to be merged with the default ones.
|
|
122
122
|
* @param {Object} [options.searchOptions={}] - Configuration options for Fuse.js.
|
|
123
|
-
* @param {Function} [options.getItemFacet] - A function to retrieve facet information from an item. Should always return an array of values.
|
|
124
123
|
* @param {Function} [options.getSortValue] - A function to get the value to sort by from an item.
|
|
125
124
|
* @param {String} [options.countMode='none'] - The mode for calculating facet counts. Can be 'none', 'simple', or 'intuitive'.
|
|
125
|
+
* @param {Object} [options.urlSync] - Optional configuration to sync state with URL.
|
|
126
|
+
* @param {import('vue-router').Router} [options.urlSync.router] - The Vue Router instance.
|
|
127
|
+
* @param {import('vue-router').RouteLocationNormalizedLoaded} [options.urlSync.route] - The current route instance.
|
|
126
128
|
*/
|
|
127
129
|
export function useFacets(allItems, options = {}) {
|
|
128
|
-
const defaultGetItemFacet = (item, uid) => {
|
|
129
|
-
const value = item[uid];
|
|
130
|
-
if (value === null || typeof value === 'undefined') return [];
|
|
131
|
-
return Array.isArray(value) ? value : [value];
|
|
132
|
-
};
|
|
133
|
-
|
|
134
130
|
const {
|
|
135
131
|
initialFacets,
|
|
136
132
|
facetFields,
|
|
@@ -139,9 +135,9 @@ export function useFacets(allItems, options = {}) {
|
|
|
139
135
|
noDefaultSorts = false,
|
|
140
136
|
extraSortTypes = {},
|
|
141
137
|
searchOptions: initialSearchOptions = {},
|
|
142
|
-
getItemFacet = defaultGetItemFacet,
|
|
143
138
|
getSortValue = item => (item.title || item.label || ""),
|
|
144
|
-
countMode = 'none' // 'none', 'simple', 'intuitive'
|
|
139
|
+
countMode = 'none', // 'none', 'simple', 'intuitive'
|
|
140
|
+
urlSync: urlSyncOptions
|
|
145
141
|
} = options;
|
|
146
142
|
|
|
147
143
|
const sortAlpha = items => {
|
|
@@ -319,14 +315,26 @@ export function useFacets(allItems, options = {}) {
|
|
|
319
315
|
if (currentSelected === prevSelected && currentSearchedItems === prevSearchedItems) return;
|
|
320
316
|
|
|
321
317
|
if (countMode === 'simple') {
|
|
318
|
+
const index = facetIndex.value;
|
|
319
|
+
if (index.size === 0 && searchedItems.value.length > 0 && facetFields?.length > 0) {
|
|
320
|
+
return; // Index not ready
|
|
321
|
+
}
|
|
322
|
+
const allItemsSet = new Set(searchedItems.value.map((_, i) => i));
|
|
323
|
+
|
|
322
324
|
facets.value.forEach(group => {
|
|
323
325
|
const otherSelected = currentSelected.filter(g => g.uid !== group.uid);
|
|
324
|
-
|
|
326
|
+
|
|
327
|
+
// Get the set of indices for items filtered by OTHER groups
|
|
328
|
+
const filteredByOthersSet = getFilteredSetFromIndex(otherSelected, index, allItemsSet);
|
|
329
|
+
|
|
325
330
|
group.children.forEach(child => {
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
331
|
+
const key = `${group.uid}:${child.uid}`;
|
|
332
|
+
const childSet = index.get(key) || new Set();
|
|
333
|
+
|
|
334
|
+
// The count is the size of the intersection between items that have this child facet
|
|
335
|
+
// and items that match all the *other* selected groups.
|
|
336
|
+
const intersection = intersectSets([filteredByOthersSet, childSet]);
|
|
337
|
+
child.count = intersection.size;
|
|
330
338
|
});
|
|
331
339
|
});
|
|
332
340
|
} else if (countMode === 'intuitive') {
|
|
@@ -344,7 +352,6 @@ export function useFacets(allItems, options = {}) {
|
|
|
344
352
|
const childSet = index.get(key) || new Set();
|
|
345
353
|
|
|
346
354
|
if (child.selected) {
|
|
347
|
-
// --- Logic for already-selected facets (now performant) ---
|
|
348
355
|
if (group.multiple) {
|
|
349
356
|
// This is the intersection of currently filtered items and items with this facet value.
|
|
350
357
|
const intersection = intersectSets([currentFilteredSet, childSet]);
|
|
@@ -381,6 +388,87 @@ export function useFacets(allItems, options = {}) {
|
|
|
381
388
|
}
|
|
382
389
|
}, { deep: true, immediate: true });
|
|
383
390
|
|
|
391
|
+
// --- URL Sync Logic ---
|
|
392
|
+
if (urlSyncOptions?.router && urlSyncOptions?.route) {
|
|
393
|
+
const { router, route } = urlSyncOptions;
|
|
394
|
+
|
|
395
|
+
const areFacetsReady = () => facets.value && facets.value.length > 0;
|
|
396
|
+
|
|
397
|
+
const updateUrlFromState = () => {
|
|
398
|
+
if (!areFacetsReady()) return;
|
|
399
|
+
|
|
400
|
+
const query = { ...route.query };
|
|
401
|
+
|
|
402
|
+
// Clean up params that we manage, preserving others
|
|
403
|
+
delete query.sort;
|
|
404
|
+
delete query.search;
|
|
405
|
+
facets.value.forEach(group => delete query[group.uid]);
|
|
406
|
+
|
|
407
|
+
// Add sort if it's not the default
|
|
408
|
+
if (selectedSort.value && selectedSort.value !== initialSortType) {
|
|
409
|
+
query.sort = selectedSort.value;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
// Add search if present
|
|
413
|
+
if (searchValue.value) {
|
|
414
|
+
query.search = searchValue.value;
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
// Add selected facets
|
|
418
|
+
selectedFacets.value.forEach(group => {
|
|
419
|
+
if (group.children.length > 0) {
|
|
420
|
+
query[group.uid] = group.children.map(c => c.uid).join(',');
|
|
421
|
+
}
|
|
422
|
+
});
|
|
423
|
+
|
|
424
|
+
if (JSON.stringify(query) !== JSON.stringify(route.query)) {
|
|
425
|
+
router.push({ query });
|
|
426
|
+
}
|
|
427
|
+
};
|
|
428
|
+
|
|
429
|
+
const updateStateFromUrl = () => {
|
|
430
|
+
const query = route.query;
|
|
431
|
+
|
|
432
|
+
if (query.sort) {
|
|
433
|
+
selectedSort.value = query.sort;
|
|
434
|
+
}
|
|
435
|
+
if (query.search) {
|
|
436
|
+
searchValue.value = query.search;
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
const selectionsFromUrl = new Map();
|
|
440
|
+
facets.value.forEach(group => {
|
|
441
|
+
const selectedUids = query[group.uid] ? query[group.uid].split(',') : [];
|
|
442
|
+
selectionsFromUrl.set(group.uid, new Set(selectedUids));
|
|
443
|
+
});
|
|
444
|
+
|
|
445
|
+
facets.value.forEach(group => {
|
|
446
|
+
const shouldBeSelected = selectionsFromUrl.get(group.uid) || new Set();
|
|
447
|
+
group.children.forEach(child => {
|
|
448
|
+
const isSelected = child.selected;
|
|
449
|
+
const shouldBe = shouldBeSelected.has(child.uid);
|
|
450
|
+
if (isSelected !== shouldBe) {
|
|
451
|
+
handleFacetChange({ groupUid: group.uid, facetUid: child.uid, selected: shouldBe });
|
|
452
|
+
}
|
|
453
|
+
});
|
|
454
|
+
});
|
|
455
|
+
};
|
|
456
|
+
|
|
457
|
+
// Initial sync from URL
|
|
458
|
+
const stopInitWatcher = watchPostEffect(() => {
|
|
459
|
+
if (facets.value && facets.value.length > 0) {
|
|
460
|
+
updateStateFromUrl();
|
|
461
|
+
stopInitWatcher();
|
|
462
|
+
}
|
|
463
|
+
});
|
|
464
|
+
|
|
465
|
+
// Sync state changes TO the URL
|
|
466
|
+
watch([selectedSort, searchValue, selectedFacets], updateUrlFromState, { deep: true });
|
|
467
|
+
|
|
468
|
+
// Sync URL changes TO the state
|
|
469
|
+
watch(() => route.query, updateStateFromUrl);
|
|
470
|
+
}
|
|
471
|
+
|
|
384
472
|
return {
|
|
385
473
|
facets,
|
|
386
474
|
searchValue,
|
|
@@ -391,4 +479,4 @@ export function useFacets(allItems, options = {}) {
|
|
|
391
479
|
clearFilters,
|
|
392
480
|
handleFacetChange
|
|
393
481
|
};
|
|
394
|
-
}
|
|
482
|
+
}
|