@asd20/ui-next 2.0.24 → 2.0.25
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/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [2.0.25](https://github.com/academydistrict20/asd20-ui-next/compare/ui-next-v2.0.24...ui-next-v2.0.25) (2026-04-03)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Bug Fixes
|
|
7
|
+
|
|
8
|
+
* allow article list and digest pages to process more than one category selection ([5ebf88a](https://github.com/academydistrict20/asd20-ui-next/commit/5ebf88a9d51d44de3976df3951ccf925cec1f269))
|
|
9
|
+
|
|
3
10
|
## [2.0.24](https://github.com/academydistrict20/asd20-ui-next/compare/ui-next-v2.0.23...ui-next-v2.0.24) (2026-04-03)
|
|
4
11
|
|
|
5
12
|
|
package/package.json
CHANGED
|
@@ -90,7 +90,7 @@
|
|
|
90
90
|
:items="categoryOptions"
|
|
91
91
|
:hide-label="true"
|
|
92
92
|
placeholder="Filter by Category"
|
|
93
|
-
@update:modelValue="
|
|
93
|
+
@update:modelValue="onCategorySelect"
|
|
94
94
|
/>
|
|
95
95
|
</div>
|
|
96
96
|
|
|
@@ -282,6 +282,7 @@ import Intersect from '../../../components/utils/Intersect'
|
|
|
282
282
|
|
|
283
283
|
// Mixins
|
|
284
284
|
import pageTemplateMixin from '../../../mixins/pageTemplateMixin'
|
|
285
|
+
import categoryFilterQuerySyncMixin from '../../../mixins/categoryFilterQuerySyncMixin'
|
|
285
286
|
|
|
286
287
|
// Helpers
|
|
287
288
|
import mapMessageToCard from '../../../helpers/mapMessageToCard'
|
|
@@ -304,7 +305,7 @@ export default {
|
|
|
304
305
|
Intersect,
|
|
305
306
|
Asd20MediaSection,
|
|
306
307
|
},
|
|
307
|
-
mixins: [pageTemplateMixin],
|
|
308
|
+
mixins: [pageTemplateMixin, categoryFilterQuerySyncMixin],
|
|
308
309
|
|
|
309
310
|
props: {
|
|
310
311
|
keywords: { type: String, default: '' },
|
|
@@ -299,6 +299,7 @@ import Intersect from '../../../components/utils/Intersect'
|
|
|
299
299
|
|
|
300
300
|
// Mixins
|
|
301
301
|
import pageTemplateMixin from '../../../mixins/pageTemplateMixin'
|
|
302
|
+
import categoryFilterQuerySyncMixin from '../../../mixins/categoryFilterQuerySyncMixin'
|
|
302
303
|
|
|
303
304
|
// Helpers
|
|
304
305
|
import mapMessageToCard from '../../../helpers/mapMessageToCard'
|
|
@@ -322,7 +323,7 @@ export default {
|
|
|
322
323
|
Intersect,
|
|
323
324
|
Asd20MediaSection,
|
|
324
325
|
},
|
|
325
|
-
mixins: [pageTemplateMixin],
|
|
326
|
+
mixins: [pageTemplateMixin, categoryFilterQuerySyncMixin],
|
|
326
327
|
props: {
|
|
327
328
|
keywords: { type: String, default: '' },
|
|
328
329
|
selectedCategories: { type: Array, default: () => [] },
|
|
@@ -332,7 +333,6 @@ export default {
|
|
|
332
333
|
numberToShow: 25,
|
|
333
334
|
counter: 1,
|
|
334
335
|
counter2: 25,
|
|
335
|
-
currentCategory: null,
|
|
336
336
|
}
|
|
337
337
|
},
|
|
338
338
|
computed: {
|
|
@@ -367,57 +367,8 @@ export default {
|
|
|
367
367
|
this.reset() // Reset page data when categories change
|
|
368
368
|
}
|
|
369
369
|
},
|
|
370
|
-
categoryOptions(newOptions) {
|
|
371
|
-
// Ensure $route and $route.query are defined before accessing query.category
|
|
372
|
-
if (this.$route && this.$route.query) {
|
|
373
|
-
const categoryFromQuery = this.$route.query.category
|
|
374
|
-
if (categoryFromQuery && newOptions.length) {
|
|
375
|
-
this.setCategoryFromUrl(categoryFromQuery) // Set category from URL when options are ready
|
|
376
|
-
}
|
|
377
|
-
}
|
|
378
|
-
},
|
|
379
370
|
},
|
|
380
371
|
methods: {
|
|
381
|
-
setCategoryFromUrl(category) {
|
|
382
|
-
// Avoid re-processing if the category is already set
|
|
383
|
-
if (this.currentCategory !== category) {
|
|
384
|
-
const matchingCategory = this.categoryOptions.find(
|
|
385
|
-
opt => opt === category
|
|
386
|
-
)
|
|
387
|
-
if (matchingCategory) {
|
|
388
|
-
this.currentCategory = category // Update the current category
|
|
389
|
-
this.$emit('update:selected-categories', [
|
|
390
|
-
{ name: matchingCategory, id: matchingCategory },
|
|
391
|
-
])
|
|
392
|
-
}
|
|
393
|
-
}
|
|
394
|
-
},
|
|
395
|
-
onCategorySelect(selected) {
|
|
396
|
-
// Ensure $router and $route are defined before using them
|
|
397
|
-
if (this.$router && this.$route) {
|
|
398
|
-
if (selected.length > 0) {
|
|
399
|
-
const selectedCategory = selected[0].id
|
|
400
|
-
|
|
401
|
-
// Only update if the selection has changed
|
|
402
|
-
if (this.currentCategory !== selectedCategory) {
|
|
403
|
-
this.currentCategory = selectedCategory // Track the selected category
|
|
404
|
-
this.$emit('update:selected-categories', selected) // Update selected categories
|
|
405
|
-
this.$router.replace({
|
|
406
|
-
query: { ...this.$route.query, category: selectedCategory },
|
|
407
|
-
})
|
|
408
|
-
}
|
|
409
|
-
} else {
|
|
410
|
-
// Handle clearing the selection
|
|
411
|
-
this.currentCategory = null // Reset current category
|
|
412
|
-
this.$emit('update:selected-categories', []) // Clear selected categories
|
|
413
|
-
|
|
414
|
-
// Remove the 'category' parameter entirely from the URL
|
|
415
|
-
const { ...remainingQuery } = this.$route.query || {} // Ensure $route.query is defined
|
|
416
|
-
delete remainingQuery.category
|
|
417
|
-
this.$router.replace({ query: remainingQuery }) // Update the URL without the category
|
|
418
|
-
}
|
|
419
|
-
}
|
|
420
|
-
},
|
|
421
372
|
nextSet() {
|
|
422
373
|
if (
|
|
423
374
|
Math.ceil(this.counter2 / this.numberToShow) <
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
function asArray(value) {
|
|
2
|
+
if (Array.isArray(value)) return value
|
|
3
|
+
if (value === null || value === undefined || value === '') return []
|
|
4
|
+
return [value]
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
function uniqueValues(values) {
|
|
8
|
+
return [...new Set(values)]
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export default {
|
|
12
|
+
mounted() {
|
|
13
|
+
this.syncCategoriesFromRoute()
|
|
14
|
+
},
|
|
15
|
+
watch: {
|
|
16
|
+
categoryOptions(newOptions) {
|
|
17
|
+
if (Array.isArray(newOptions) && newOptions.length) {
|
|
18
|
+
this.syncCategoriesFromRoute()
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
methods: {
|
|
23
|
+
normalizeCategoryQueryValues(value) {
|
|
24
|
+
return uniqueValues(
|
|
25
|
+
asArray(value)
|
|
26
|
+
.flatMap(entry => String(entry).split(','))
|
|
27
|
+
.map(entry => entry.trim())
|
|
28
|
+
.filter(Boolean)
|
|
29
|
+
)
|
|
30
|
+
},
|
|
31
|
+
normalizeCategorySelection(selection) {
|
|
32
|
+
return asArray(selection)
|
|
33
|
+
.map(item => {
|
|
34
|
+
const id =
|
|
35
|
+
item && typeof item === 'object'
|
|
36
|
+
? item.id ?? item.name ?? item.title
|
|
37
|
+
: item
|
|
38
|
+
|
|
39
|
+
if (id === null || id === undefined || id === '') {
|
|
40
|
+
return null
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const label =
|
|
44
|
+
item && typeof item === 'object'
|
|
45
|
+
? item.name ?? item.title ?? id
|
|
46
|
+
: id
|
|
47
|
+
|
|
48
|
+
return {
|
|
49
|
+
id,
|
|
50
|
+
name: label,
|
|
51
|
+
}
|
|
52
|
+
})
|
|
53
|
+
.filter(Boolean)
|
|
54
|
+
},
|
|
55
|
+
getCategoryQueryValues(query = this.$route && this.$route.query) {
|
|
56
|
+
if (!query) return []
|
|
57
|
+
|
|
58
|
+
if (Object.prototype.hasOwnProperty.call(query, 'categories')) {
|
|
59
|
+
return this.normalizeCategoryQueryValues(query.categories)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (Object.prototype.hasOwnProperty.call(query, 'category')) {
|
|
63
|
+
return this.normalizeCategoryQueryValues(query.category)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return []
|
|
67
|
+
},
|
|
68
|
+
buildSelectionFromCategoryQuery() {
|
|
69
|
+
const queryValues = this.getCategoryQueryValues()
|
|
70
|
+
|
|
71
|
+
if (!queryValues.length || !Array.isArray(this.categoryOptions)) {
|
|
72
|
+
return []
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const optionLookup = new Map(
|
|
76
|
+
this.categoryOptions.map(option => [String(option), String(option)])
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
return queryValues
|
|
80
|
+
.filter(value => optionLookup.has(String(value)))
|
|
81
|
+
.map(value => {
|
|
82
|
+
const option = optionLookup.get(String(value))
|
|
83
|
+
return {
|
|
84
|
+
id: option,
|
|
85
|
+
name: option,
|
|
86
|
+
}
|
|
87
|
+
})
|
|
88
|
+
},
|
|
89
|
+
hasSameCategorySelection(left, right) {
|
|
90
|
+
const leftIds = this.normalizeCategorySelection(left).map(item => String(item.id))
|
|
91
|
+
const rightIds = this.normalizeCategorySelection(right).map(item => String(item.id))
|
|
92
|
+
|
|
93
|
+
return (
|
|
94
|
+
leftIds.length === rightIds.length &&
|
|
95
|
+
leftIds.every((id, index) => id === rightIds[index])
|
|
96
|
+
)
|
|
97
|
+
},
|
|
98
|
+
syncCategoriesFromRoute() {
|
|
99
|
+
const routeSelection = this.buildSelectionFromCategoryQuery()
|
|
100
|
+
|
|
101
|
+
if (!routeSelection.length) return
|
|
102
|
+
if (this.hasSameCategorySelection(routeSelection, this.selectedCategories)) {
|
|
103
|
+
return
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
this.$emit('update:selected-categories', routeSelection)
|
|
107
|
+
},
|
|
108
|
+
categoryQueryMatchesSelection(selection) {
|
|
109
|
+
const normalizedSelection = this.normalizeCategorySelection(selection)
|
|
110
|
+
const selectionIds = normalizedSelection.map(item => String(item.id))
|
|
111
|
+
const query = (this.$route && this.$route.query) || {}
|
|
112
|
+
const queryIds = this.getCategoryQueryValues(query)
|
|
113
|
+
|
|
114
|
+
if (
|
|
115
|
+
selectionIds.length !== queryIds.length ||
|
|
116
|
+
selectionIds.some((id, index) => id !== queryIds[index])
|
|
117
|
+
) {
|
|
118
|
+
return false
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const hasCategory = Object.prototype.hasOwnProperty.call(query, 'category')
|
|
122
|
+
const hasCategories = Object.prototype.hasOwnProperty.call(query, 'categories')
|
|
123
|
+
|
|
124
|
+
if (!selectionIds.length) {
|
|
125
|
+
return !hasCategory && !hasCategories
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if (String(query.category) !== selectionIds[0]) {
|
|
129
|
+
return false
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
if (selectionIds.length === 1) {
|
|
133
|
+
return !hasCategories
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const categoryQueryValues = this.normalizeCategoryQueryValues(query.categories)
|
|
137
|
+
|
|
138
|
+
return (
|
|
139
|
+
categoryQueryValues.length === selectionIds.length &&
|
|
140
|
+
categoryQueryValues.every((id, index) => id === selectionIds[index])
|
|
141
|
+
)
|
|
142
|
+
},
|
|
143
|
+
updateCategoryQuery(selection) {
|
|
144
|
+
if (!this.$router || !this.$route) return
|
|
145
|
+
if (this.categoryQueryMatchesSelection(selection)) return
|
|
146
|
+
|
|
147
|
+
const normalizedSelection = this.normalizeCategorySelection(selection)
|
|
148
|
+
const nextQuery = { ...(this.$route.query || {}) }
|
|
149
|
+
const selectionIds = normalizedSelection.map(item => item.id)
|
|
150
|
+
|
|
151
|
+
delete nextQuery.category
|
|
152
|
+
delete nextQuery.categories
|
|
153
|
+
|
|
154
|
+
if (selectionIds.length === 1) {
|
|
155
|
+
nextQuery.category = selectionIds[0]
|
|
156
|
+
} else if (selectionIds.length > 1) {
|
|
157
|
+
nextQuery.category = selectionIds[0]
|
|
158
|
+
nextQuery.categories = selectionIds
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
this.$router.replace({ query: nextQuery })
|
|
162
|
+
},
|
|
163
|
+
onCategorySelect(selection) {
|
|
164
|
+
const normalizedSelection = this.normalizeCategorySelection(selection)
|
|
165
|
+
|
|
166
|
+
this.$emit('update:selected-categories', normalizedSelection)
|
|
167
|
+
this.updateCategoryQuery(normalizedSelection)
|
|
168
|
+
},
|
|
169
|
+
},
|
|
170
|
+
}
|