@asd20/ui-next 2.0.22 → 2.0.24

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,19 @@
1
1
  # Changelog
2
2
 
3
+ ## [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
+
5
+
6
+ ### Bug Fixes
7
+
8
+ * prevent double activation of multi select on mouse click ([0e8dfbd](https://github.com/academydistrict20/asd20-ui-next/commit/0e8dfbdd6ac6c20181065c47324f7e624d9f49e6))
9
+
10
+ ## [2.0.23](https://github.com/academydistrict20/asd20-ui-next/compare/ui-next-v2.0.22...ui-next-v2.0.23) (2026-04-03)
11
+
12
+
13
+ ### Bug Fixes
14
+
15
+ * ensure search result urls are accurate ([0684303](https://github.com/academydistrict20/asd20-ui-next/commit/06843037c47bdb7b7ee4adee215c118695a1e2dc))
16
+
3
17
  ## [2.0.22](https://github.com/academydistrict20/asd20-ui-next/compare/ui-next-v2.0.21...ui-next-v2.0.22) (2026-04-02)
4
18
 
5
19
  ## [2.0.21](https://github.com/academydistrict20/asd20-ui-next/compare/ui-next-v2.0.20...ui-next-v2.0.21) (2026-04-02)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@asd20/ui-next",
3
- "version": "2.0.22",
3
+ "version": "2.0.24",
4
4
  "private": false,
5
5
  "description": "ASD20 UI component library for Vue 3.",
6
6
  "license": "MIT",
@@ -75,6 +75,22 @@ function normalizeItem(item, itemLabel, itemValue) {
75
75
  return normalized
76
76
  }
77
77
 
78
+ function getItemKey(item, itemLabel, itemValue) {
79
+ const normalized = normalizeItem(item, itemLabel, itemValue)
80
+ return normalized[itemValue] ?? normalized[itemLabel]
81
+ }
82
+
83
+ function sameItemKey(left, right) {
84
+ return (
85
+ left === right ||
86
+ (left !== undefined &&
87
+ left !== null &&
88
+ right !== undefined &&
89
+ right !== null &&
90
+ String(left) === String(right))
91
+ )
92
+ }
93
+
78
94
  const MULTISELECT_OMITTED_INPUT_ATTRS = [
79
95
  'autocomplete',
80
96
  'disabled',
@@ -101,9 +117,7 @@ export default {
101
117
  computed: {
102
118
  multiselectValue() {
103
119
  if (Array.isArray(this.resolvedValue)) {
104
- return this.resolvedValue.map(item =>
105
- normalizeItem(item, this.itemLabel, this.itemValue)
106
- )
120
+ return this.resolvedValue.map(item => this.resolveSelectedItem(item))
107
121
  }
108
122
 
109
123
  if (
@@ -114,7 +128,7 @@ export default {
114
128
  return []
115
129
  }
116
130
 
117
- return [normalizeItem(this.resolvedValue, this.itemLabel, this.itemValue)]
131
+ return [this.resolveSelectedItem(this.resolvedValue)]
118
132
  },
119
133
  multiselectItems() {
120
134
  const seen = new Set()
@@ -156,9 +170,21 @@ export default {
156
170
  emitValue(value) {
157
171
  this.$emit('update:modelValue', value)
158
172
  },
159
- getItemKey(item) {
173
+ resolveSelectedItem(item) {
160
174
  const normalized = normalizeItem(item, this.itemLabel, this.itemValue)
161
- return normalized[this.itemValue] ?? normalized[this.itemLabel]
175
+ const itemKey = getItemKey(normalized, this.itemLabel, this.itemValue)
176
+
177
+ return (
178
+ this.computedItems.find(candidate =>
179
+ sameItemKey(
180
+ getItemKey(candidate, this.itemLabel, this.itemValue),
181
+ itemKey
182
+ )
183
+ ) || normalized
184
+ )
185
+ },
186
+ getItemKey(item) {
187
+ return getItemKey(item, this.itemLabel, this.itemValue)
162
188
  },
163
189
  addTag(newTag) {
164
190
  const normalizedTag = normalizeItem(newTag, this.itemLabel, this.itemValue)
@@ -1790,46 +1790,7 @@ export default {
1790
1790
 
1791
1791
  appendSearchContextToUrl(rawUrl) {
1792
1792
  if (!rawUrl || typeof rawUrl !== 'string') return rawUrl
1793
- if (typeof window === 'undefined' || !window.location) return rawUrl
1794
-
1795
- const searchState = this.getCurrentSearchStateForRoute()
1796
- if (!searchState) return rawUrl
1797
- if (/^(mailto:|tel:|javascript:|#)/i.test(rawUrl)) return rawUrl
1798
-
1799
- try {
1800
- const parsed = new URL(rawUrl, window.location.origin)
1801
- const currentHost = window.location.hostname || ''
1802
- const targetHost = parsed.hostname || ''
1803
- const sameOrigin = parsed.origin === window.location.origin
1804
- const currentIsLocal = this.isLocalDevelopmentHost(currentHost)
1805
- const currentIsAsd20 =
1806
- currentHost === 'asd20.org' || currentHost.endsWith('.asd20.org')
1807
- const targetIsAsd20 =
1808
- targetHost === 'asd20.org' || targetHost.endsWith('.asd20.org')
1809
- const sameAsd20Domain = currentIsAsd20 && targetIsAsd20
1810
- const mapAsd20ToLocal = currentIsLocal && targetIsAsd20
1811
- if (!sameOrigin && !sameAsd20Domain && !mapAsd20ToLocal) return rawUrl
1812
-
1813
- const resolved = mapAsd20ToLocal
1814
- ? new URL(
1815
- `${parsed.pathname}${parsed.search}${parsed.hash}`,
1816
- window.location.origin
1817
- )
1818
- : parsed
1819
-
1820
- const queryValues = this.buildRouteSearchQueryValues(searchState)
1821
- Object.keys(queryValues).forEach(key => {
1822
- resolved.searchParams.set(key, queryValues[key])
1823
- })
1824
-
1825
- if (mapAsd20ToLocal) {
1826
- return `${resolved.pathname}${resolved.search}${resolved.hash}`
1827
- }
1828
- if (/^https?:\/\//i.test(rawUrl)) return resolved.toString()
1829
- return `${resolved.pathname}${resolved.search}${resolved.hash}`
1830
- } catch (error) {
1831
- return rawUrl
1832
- }
1793
+ return rawUrl
1833
1794
  },
1834
1795
 
1835
1796
  async restoreSearchStateFromRoute() {
@@ -27,7 +27,7 @@
27
27
  class="multiselect__tag-icon"
28
28
  :aria-label="`Remove ${getOptionLabel(option)}`"
29
29
  @mousedown.prevent.stop="removeOption(option)"
30
- @click.prevent.stop="removeOption(option)"
30
+ @click.prevent.stop="handleRemoveClick($event, option)"
31
31
  />
32
32
  </span>
33
33
  </div>
@@ -82,7 +82,6 @@
82
82
  role="option"
83
83
  :aria-selected="isSelected(option) ? 'true' : 'false'"
84
84
  @mousedown.prevent="toggleOption(option)"
85
- @click.prevent="toggleOption(option)"
86
85
  >
87
86
  <div class="option__desc">
88
87
  <span
@@ -102,7 +101,6 @@
102
101
  role="option"
103
102
  :aria-selected="'false'"
104
103
  @mousedown.prevent="createTag"
105
- @click.prevent="createTag"
106
104
  >
107
105
  <div class="option__desc">
108
106
  <span class="option__title">
@@ -384,6 +382,11 @@ export default {
384
382
  )
385
383
  this.open()
386
384
  },
385
+ handleRemoveClick(event, option) {
386
+ // Buttons need click handling for keyboard and assistive-tech activation.
387
+ if (event.detail !== 0) return
388
+ this.removeOption(option)
389
+ },
387
390
  moveActive(step) {
388
391
  if (!this.isOpen) {
389
392
  this.open()