@ulu/frontend-vue 0.1.0-beta.11 → 0.1.0-beta.13
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/{breakpoints-DS8rH5b6.js → breakpoints-DE6CbfnP.js} +1 -1
- package/dist/frontend-vue.css +1 -1
- package/dist/frontend-vue.js +71 -67
- package/dist/{index-C5pNSAJH.js → index-B7y9zYaR.js} +1819 -1884
- package/lib/components/systems/facets/useFacets.js +105 -24
- package/lib/components/systems/index.js +1 -0
- package/package.json +1 -1
- package/types/components/systems/facets/_mock-data.d.ts +18 -0
- package/types/components/systems/facets/_mock-data.d.ts.map +1 -0
- package/types/components/systems/facets/useFacets.d.ts +39 -0
- package/types/components/systems/facets/useFacets.d.ts.map +1 -0
- package/types/components/systems/index.d.ts +1 -1
|
@@ -1,46 +1,93 @@
|
|
|
1
|
-
import { ref, computed } from 'vue';
|
|
1
|
+
import { ref, computed, watch } from 'vue';
|
|
2
2
|
import Fuse from 'fuse.js';
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
/**
|
|
5
|
+
* Generates facet groups and their possible values from a list of items.
|
|
6
|
+
* @param {Array<Object>} allItems - The full list of items.
|
|
7
|
+
* @param {Array<Object>} facetFields - Configuration for which fields to create facets from.
|
|
8
|
+
* @returns {Array<Object>} The generated facet structure.
|
|
9
|
+
*/
|
|
10
|
+
function generateInitialFacets(allItems, facetFields) {
|
|
11
|
+
if (!facetFields || !Array.isArray(facetFields)) return [];
|
|
12
|
+
return facetFields.map(group => {
|
|
13
|
+
const allValues = new Set();
|
|
14
|
+
const getValue = group.getValue || (item => item[group.uid]);
|
|
15
|
+
|
|
16
|
+
allItems.forEach(item => {
|
|
17
|
+
const value = getValue(item);
|
|
18
|
+
if (Array.isArray(value)) {
|
|
19
|
+
value.forEach(v => v && allValues.add(v));
|
|
20
|
+
} else if (value) {
|
|
21
|
+
allValues.add(value);
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
const getLabel = group.getLabel || (value => value);
|
|
26
|
+
|
|
27
|
+
return {
|
|
28
|
+
...group,
|
|
29
|
+
children: [...allValues].sort().map(value => ({
|
|
30
|
+
uid: value,
|
|
31
|
+
label: getLabel(value),
|
|
32
|
+
selected: false
|
|
33
|
+
}))
|
|
34
|
+
};
|
|
35
|
+
});
|
|
7
36
|
}
|
|
8
|
-
|
|
9
|
-
az: { text: "A-Z", sort: sortAlpha },
|
|
10
|
-
za: { text: "Z-A", sort: items => sortAlpha(items).reverse() },
|
|
11
|
-
};
|
|
37
|
+
|
|
12
38
|
|
|
13
39
|
/**
|
|
14
40
|
* A composable for handling client-side faceted search, filtering, and sorting.
|
|
15
41
|
* @param {import('vue').Ref<Array<Object>>} allItems - A Vue ref containing the full list of items to be processed.
|
|
16
42
|
* @param {Object} options - Configuration options for the composable.
|
|
17
|
-
* @param {Array} [options.initialFacets
|
|
43
|
+
* @param {Array} [options.initialFacets] - The initial configuration for the facets. Can be generated automatically if `facetFields` is provided.
|
|
44
|
+
* @param {Array} [options.facetFields] - A simpler configuration to automatically generate facets from items. Each item can have `uid`, `name`, `open`, `getValue` and `getLabel`.
|
|
18
45
|
* @param {String} [options.initialSearchValue=''] - The initial value for the search input.
|
|
19
46
|
* @param {String} [options.initialSortType='az'] - The initial sort type.
|
|
20
47
|
* @param {Boolean} [options.noDefaultSorts=false] - If true, the default 'A-Z' and 'Z-A' sorts will not be included.
|
|
21
48
|
* @param {Object} [options.extraSortTypes={}] - Additional sort types to be merged with the default ones.
|
|
22
49
|
* @param {Object} [options.searchOptions={}] - Configuration options for Fuse.js.
|
|
23
|
-
* @param {Function} [options.getItemFacet] - A function to retrieve facet information from an item.
|
|
50
|
+
* @param {Function} [options.getItemFacet] - A function to retrieve facet information from an item. Should always return an array of values.
|
|
51
|
+
* @param {Function} [options.getSortValue] - A function to get the value to sort by from an item.
|
|
24
52
|
*/
|
|
25
53
|
export function useFacets(allItems, options = {}) {
|
|
54
|
+
const defaultGetItemFacet = (item, uid) => {
|
|
55
|
+
const value = item[uid];
|
|
56
|
+
if (value === null || typeof value === 'undefined') return [];
|
|
57
|
+
return Array.isArray(value) ? value : [value];
|
|
58
|
+
};
|
|
59
|
+
|
|
26
60
|
const {
|
|
27
|
-
initialFacets
|
|
61
|
+
initialFacets,
|
|
62
|
+
facetFields,
|
|
28
63
|
initialSearchValue = '',
|
|
29
64
|
initialSortType = 'az',
|
|
30
65
|
noDefaultSorts = false,
|
|
31
66
|
extraSortTypes = {},
|
|
32
67
|
searchOptions: initialSearchOptions = {},
|
|
33
|
-
getItemFacet =
|
|
68
|
+
getItemFacet = defaultGetItemFacet,
|
|
69
|
+
getSortValue = item => (item.title || item.label || "")
|
|
34
70
|
} = options;
|
|
35
71
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
72
|
+
const sortAlpha = items => {
|
|
73
|
+
return items.sort((a, b) => {
|
|
74
|
+
const va = getSortValue(a);
|
|
75
|
+
const vb = getSortValue(b);
|
|
76
|
+
if (va && vb) {
|
|
77
|
+
return String(valueA).localeCompare(String(valueB));
|
|
78
|
+
} else {
|
|
79
|
+
return va ? -1 : vb ? 1 : 0;
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
const defaultSorts = {
|
|
84
|
+
az: { text: "A-Z", sort: sortAlpha },
|
|
85
|
+
za: { text: "Z-A", sort: items => sortAlpha(items).reverse() },
|
|
86
|
+
};
|
|
40
87
|
|
|
41
88
|
// --- Helpers ---
|
|
42
89
|
function createFacets(initial) {
|
|
43
|
-
return initial.map(group => ({
|
|
90
|
+
return (initial || []).map(group => ({
|
|
44
91
|
...group,
|
|
45
92
|
open: group.open || false,
|
|
46
93
|
children: group.children.map(facet => ({
|
|
@@ -51,6 +98,23 @@ export function useFacets(allItems, options = {}) {
|
|
|
51
98
|
}));
|
|
52
99
|
}
|
|
53
100
|
|
|
101
|
+
const generatedFacets = computed(() => {
|
|
102
|
+
if (!facetFields || !allItems.value?.length) return null;
|
|
103
|
+
return generateInitialFacets(allItems.value, facetFields);
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
// --- State ---
|
|
107
|
+
const facets = ref(createFacets(initialFacets || generatedFacets.value));
|
|
108
|
+
const searchValue = ref(initialSearchValue);
|
|
109
|
+
const selectedSort = ref(initialSortType);
|
|
110
|
+
|
|
111
|
+
// If using facetFields, watch for changes in items and regenerate facets
|
|
112
|
+
if (facetFields && !initialFacets) {
|
|
113
|
+
watch(generatedFacets, (newGeneratedFacets) => {
|
|
114
|
+
facets.value = createFacets(newGeneratedFacets);
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
|
|
54
118
|
// --- Computed ---
|
|
55
119
|
const sortTypes = computed(() => ({
|
|
56
120
|
...(noDefaultSorts ? {} : defaultSorts),
|
|
@@ -91,10 +155,12 @@ export function useFacets(allItems, options = {}) {
|
|
|
91
155
|
return allItems.value;
|
|
92
156
|
}
|
|
93
157
|
return allItems.value.filter(item => {
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
158
|
+
// An item must match every active facet group
|
|
159
|
+
return selectedFacets.value.every(group => {
|
|
160
|
+
const itemFacetValues = getItemFacet(item, group.uid);
|
|
161
|
+
if (itemFacetValues && itemFacetValues.length) {
|
|
162
|
+
// An item can match any of the selected facets within a group
|
|
163
|
+
return group.children.some(facet => itemFacetValues.includes(facet.uid));
|
|
98
164
|
}
|
|
99
165
|
return false;
|
|
100
166
|
});
|
|
@@ -120,7 +186,21 @@ export function useFacets(allItems, options = {}) {
|
|
|
120
186
|
|
|
121
187
|
// --- Methods ---
|
|
122
188
|
function clearFilters() {
|
|
123
|
-
facets.value
|
|
189
|
+
facets.value.forEach(group => {
|
|
190
|
+
if (group.children) {
|
|
191
|
+
group.children.forEach(child => child.selected = false);
|
|
192
|
+
}
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function handleFacetChange({ groupUid, facetUid, selected }) {
|
|
197
|
+
const group = facets.value.find(g => g.uid === groupUid);
|
|
198
|
+
if (group) {
|
|
199
|
+
const facet = group.children.find(f => f.uid === facetUid);
|
|
200
|
+
if (facet) {
|
|
201
|
+
facet.selected = selected;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
124
204
|
}
|
|
125
205
|
|
|
126
206
|
return {
|
|
@@ -135,6 +215,7 @@ export function useFacets(allItems, options = {}) {
|
|
|
135
215
|
selectedFacets,
|
|
136
216
|
|
|
137
217
|
// Methods
|
|
138
|
-
clearFilters
|
|
218
|
+
clearFilters,
|
|
219
|
+
handleFacetChange
|
|
139
220
|
};
|
|
140
|
-
}
|
|
221
|
+
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
export { useFacets } from './facets/useFacets.js';
|
|
1
2
|
export { default as UluFacetsFilters } from './facets/UluFacetsFilters.vue';
|
|
2
3
|
export { default as UluFacetsResults } from './facets/UluFacetsResults.vue';
|
|
3
4
|
export { default as UluFacetsSearch } from './facets/UluFacetsSearch.vue';
|
package/package.json
CHANGED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export const initialMockFacets: {
|
|
2
|
+
name: string;
|
|
3
|
+
uid: string;
|
|
4
|
+
open: boolean;
|
|
5
|
+
children: {
|
|
6
|
+
uid: string;
|
|
7
|
+
label: string;
|
|
8
|
+
}[];
|
|
9
|
+
}[];
|
|
10
|
+
export const mockItems: {
|
|
11
|
+
id: number;
|
|
12
|
+
title: string;
|
|
13
|
+
description: string;
|
|
14
|
+
category: string[];
|
|
15
|
+
author: string[];
|
|
16
|
+
date: Date;
|
|
17
|
+
}[];
|
|
18
|
+
//# sourceMappingURL=_mock-data.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"_mock-data.d.ts","sourceRoot":"","sources":["../../../../lib/components/systems/facets/_mock-data.js"],"names":[],"mappings":"AAAA;;;;;;;;IAwBE;AAEF;;;;;;;IAaE"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A composable for handling client-side faceted search, filtering, and sorting.
|
|
3
|
+
* @param {import('vue').Ref<Array<Object>>} allItems - A Vue ref containing the full list of items to be processed.
|
|
4
|
+
* @param {Object} options - Configuration options for the composable.
|
|
5
|
+
* @param {Array} [options.initialFacets] - The initial configuration for the facets. Can be generated automatically if `facetFields` is provided.
|
|
6
|
+
* @param {Array} [options.facetFields] - A simpler configuration to automatically generate facets from items. Each item can have `uid`, `name`, `open`, `getValue` and `getLabel`.
|
|
7
|
+
* @param {String} [options.initialSearchValue=''] - The initial value for the search input.
|
|
8
|
+
* @param {String} [options.initialSortType='az'] - The initial sort type.
|
|
9
|
+
* @param {Boolean} [options.noDefaultSorts=false] - If true, the default 'A-Z' and 'Z-A' sorts will not be included.
|
|
10
|
+
* @param {Object} [options.extraSortTypes={}] - Additional sort types to be merged with the default ones.
|
|
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
|
+
* @param {Function} [options.getSortValue] - A function to get the value to sort by from an item.
|
|
14
|
+
*/
|
|
15
|
+
export function useFacets(allItems: import("vue").Ref<Array<any>>, options?: {
|
|
16
|
+
initialFacets?: any[];
|
|
17
|
+
facetFields?: any[];
|
|
18
|
+
initialSearchValue?: string;
|
|
19
|
+
initialSortType?: string;
|
|
20
|
+
noDefaultSorts?: boolean;
|
|
21
|
+
extraSortTypes?: any;
|
|
22
|
+
searchOptions?: any;
|
|
23
|
+
getItemFacet?: Function;
|
|
24
|
+
getSortValue?: Function;
|
|
25
|
+
}): {
|
|
26
|
+
facets: import("vue").Ref<any, any>;
|
|
27
|
+
searchValue: import("vue").Ref<string, string>;
|
|
28
|
+
selectedSort: import("vue").Ref<string, string>;
|
|
29
|
+
sortTypes: import("vue").ComputedRef<any>;
|
|
30
|
+
displayItems: import("vue").ComputedRef<any>;
|
|
31
|
+
selectedFacets: import("vue").ComputedRef<any[]>;
|
|
32
|
+
clearFilters: () => void;
|
|
33
|
+
handleFacetChange: ({ groupUid, facetUid, selected }: {
|
|
34
|
+
groupUid: any;
|
|
35
|
+
facetUid: any;
|
|
36
|
+
selected: any;
|
|
37
|
+
}) => void;
|
|
38
|
+
};
|
|
39
|
+
//# sourceMappingURL=useFacets.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useFacets.d.ts","sourceRoot":"","sources":["../../../../lib/components/systems/facets/useFacets.js"],"names":[],"mappings":"AAsCA;;;;;;;;;;;;;GAaG;AACH,oCAZW,OAAO,KAAK,EAAE,GAAG,CAAC,KAAK,KAAQ,CAAC,YAExC;IAAwB,aAAa;IACb,WAAW;IACV,kBAAkB;IAClB,eAAe;IACd,cAAc;IACf,cAAc;IACd,aAAa;IACX,YAAY;IACZ,YAAY;CACzC;;;;;;;;;;;;;EAyKA"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export {};
|
|
1
|
+
export { useFacets } from "./facets/useFacets.js";
|
|
2
2
|
//# sourceMappingURL=index.d.ts.map
|