@citizenplane/pimp 10.9.2 → 10.9.3

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@citizenplane/pimp",
3
- "version": "10.9.2",
3
+ "version": "10.9.3",
4
4
  "scripts": {
5
5
  "dev": "storybook dev -p 8080",
6
6
  "build-storybook": "storybook build --output-dir ./docs",
@@ -1,17 +1,26 @@
1
1
  <template>
2
2
  <div class="cpMultiselect">
3
- <base-input-label v-if="label" class="cpMultiselect__label" :is-invalid :required>
3
+ <base-input-label
4
+ v-if="label"
5
+ :id="labelId"
6
+ class="cpMultiselect__label"
7
+ :for="multiselectId"
8
+ :is-invalid
9
+ :required
10
+ >
4
11
  {{ label }}
5
12
  </base-input-label>
6
13
  <auto-complete
7
14
  ref="multiselect"
8
15
  v-model="selectModel"
9
16
  :append-to
17
+ :aria-labelledby="ariaLabelledby"
10
18
  auto-option-focus
11
19
  :data-key="trackBy"
12
20
  :disabled
13
21
  :force-selection
14
22
  :input-class
23
+ :input-id="multiselectId"
15
24
  :invalid="isInvalid"
16
25
  :multiple
17
26
  :name
@@ -24,6 +33,8 @@
24
33
  @click="handleClick"
25
34
  @complete="handleSearch"
26
35
  @hide="handleOverlayHidden"
36
+ @keydown.arrow-down="handleArrowKeys"
37
+ @keydown.arrow-up="handleArrowKeys"
27
38
  @keydown.enter="toggleDropdown"
28
39
  @keydown.esc.stop
29
40
  @show="handleOverlayShown"
@@ -61,12 +72,18 @@
61
72
  v-else
62
73
  class="cpMultiselect__toggle"
63
74
  :disabled="disabled"
75
+ tabindex="-1"
64
76
  type="button"
65
77
  @click.stop="handleDropdownIconClick"
66
78
  >
67
79
  <cp-icon class="cpMultiselect__dropdownIcon" :class="chevronDynamicClass" type="chevron-down" />
68
80
  </button>
69
- <base-select-clear-button v-if="displayClearButton" class="cpMultiselect__clear" @click="handleClear" />
81
+ <base-select-clear-button
82
+ v-if="displayClearButton"
83
+ class="cpMultiselect__clear"
84
+ tabindex="-1"
85
+ @click="handleClear"
86
+ />
70
87
  </template>
71
88
  </auto-complete>
72
89
  <transition-expand mode="out-in">
@@ -80,7 +97,7 @@
80
97
  <script setup lang="ts">
81
98
  import { absolutePosition, getOuterWidth } from '@primeuix/utils/dom'
82
99
  import AutoComplete from 'primevue/autocomplete'
83
- import { ref, computed, onMounted, useSlots } from 'vue'
100
+ import { ref, computed, onMounted, useSlots, useId } from 'vue'
84
101
 
85
102
  import BaseInputLabel from '@/components/BaseInputLabel.vue'
86
103
  import BaseSelectClearButton from '@/components/BaseSelectClearButton.vue'
@@ -138,14 +155,20 @@ const emit = defineEmits<Emits>()
138
155
 
139
156
  const slots = useSlots()
140
157
 
158
+ const multiselectId = useId()
159
+ const labelId = useId()
160
+
161
+ const ariaLabelledby = computed(() => (props.label ? labelId : undefined))
162
+
141
163
  const selectModel = computed({
142
164
  get() {
143
165
  return props.modelValue
144
166
  },
145
- set(value) {
167
+ set(value: string | object | string[] | null) {
146
168
  if (typeof value === 'string') {
147
169
  return
148
170
  }
171
+
149
172
  emit('update:modelValue', value)
150
173
  },
151
174
  })
@@ -167,13 +190,21 @@ const passThroughConfig = computed(() => ({
167
190
  loader: { class: 'cpMultiselect__hidden' },
168
191
  }))
169
192
 
170
- const multiselect = ref<InstanceType<typeof AutoComplete> | null>(null)
193
+ type ExtendedAutoComplete = InstanceType<typeof AutoComplete> & {
194
+ $el: HTMLElement
195
+ alignOverlay: () => void
196
+ hide: () => void
197
+ overlay: HTMLElement
198
+ overlayVisible: boolean
199
+ show: () => void
200
+ }
201
+
202
+ const multiselect = ref<ExtendedAutoComplete | null>(null)
171
203
 
172
204
  const searchQuery = ref('')
173
205
 
174
206
  const typeahead = computed(() => !props.withoutTypeahead)
175
207
 
176
- // @ts-expect-error 'overlayVisible' does not exist on type instance of AutoComplete
177
208
  const isDropdownOpen = computed(() => multiselect.value?.overlayVisible)
178
209
 
179
210
  const chevronDynamicClass = computed(() => {
@@ -202,11 +233,10 @@ const handleValueChange = (newValue: string | object) => {
202
233
 
203
234
  const getInputElement = () => {
204
235
  if (!multiselect.value) return null
205
- // @ts-expect-error '$el' does not exist on type instance of AutoComplete
206
236
  return multiselect.value.$el?.querySelector('input') || null
207
237
  }
208
238
 
209
- const selectInputContent = () => {
239
+ const selectInputElement = () => {
210
240
  const inputElement = getInputElement()
211
241
  if (inputElement) inputElement.select()
212
242
  }
@@ -215,13 +245,13 @@ const focusAndSelectInput = () => {
215
245
  const inputElement = getInputElement()
216
246
  if (inputElement) {
217
247
  inputElement.focus()
218
- inputElement.select()
248
+ selectInputElement()
219
249
  }
220
250
  }
221
251
 
222
252
  const handleClick = () => {
223
253
  toggleDropdown()
224
- selectInputContent()
254
+ selectInputElement()
225
255
  }
226
256
 
227
257
  const handleDropdownIconClick = () => {
@@ -229,12 +259,15 @@ const handleDropdownIconClick = () => {
229
259
  focusAndSelectInput()
230
260
  }
231
261
 
262
+ const handleArrowKeys = () => {
263
+ if (isDropdownOpen.value) return
264
+ return toggleDropdown()
265
+ }
266
+
232
267
  const toggleDropdown = () => {
233
268
  if (isDropdownOpen.value) {
234
- // @ts-expect-error 'hide' does not exist on type instance of AutoComplete
235
269
  multiselect.value?.hide()
236
270
  } else {
237
- // @ts-expect-error 'show' does not exist on type instance of AutoComplete
238
271
  multiselect.value?.show()
239
272
  }
240
273
  }
@@ -245,23 +278,17 @@ const handleUpdateModelValue = (value: Record<string, unknown> | string[] | null
245
278
  }
246
279
 
247
280
  const overrideAlignOverlay = () => {
248
- if (multiselect.value) {
249
- // @ts-expect-error 'alignOverlay' does not exist on type instance of AutoComplete
250
- multiselect.value.alignOverlay = alignOverlay
251
- }
281
+ if (!multiselect.value) return
282
+ multiselect.value.alignOverlay = alignOverlay
252
283
  }
253
284
 
254
285
  const alignOverlay = () => {
255
- // @ts-expect-error 'el' does not exist on type instance of AutoComplete
256
286
  const target = multiselect.value?.$el
257
287
 
258
- // @ts-expect-error 'overlay' does not exist on type instance of AutoComplete
259
288
  if (!multiselect.value?.overlay || !target) return
260
289
 
261
- // @ts-expect-error 'overlay' does not exist on type instance of AutoComplete
262
290
  multiselect.value.overlay.style.width = `${getOuterWidth(target)}px`
263
291
 
264
- // @ts-expect-error 'overlay' does not exist on type instance of AutoComplete
265
292
  absolutePosition(multiselect.value.overlay, target)
266
293
  }
267
294
 
@@ -470,7 +497,7 @@ onMounted(() => overrideAlignOverlay())
470
497
  color: var(--cp-foreground-primary);
471
498
  }
472
499
 
473
- &[data-p-selected='true'] {
500
+ &[data-p-selected='true']:not([data-p-focused='true']) {
474
501
  background: var(--cp-background-accent-primary);
475
502
  }
476
503