@citizenplane/pimp 8.25.5 → 8.26.0

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.
Files changed (72) hide show
  1. package/dist/{IconAccompaniedMinorEach-Cx5Bfcmr.js → IconAccompaniedMinorEach-DDeSlaA_.js} +1 -1
  2. package/dist/{IconAccompaniedMinorNone-BQEYAxoo.js → IconAccompaniedMinorNone-BKs1gk1K.js} +1 -1
  3. package/dist/{IconAccompaniedMinorOne-Bi2kXTM4.js → IconAccompaniedMinorOne-CpL8FNve.js} +1 -1
  4. package/dist/{IconAddReceipt-BO0fhfpX.js → IconAddReceipt-DLH3pqjA.js} +1 -1
  5. package/dist/{IconAirportTerminal-CnADY0bO.js → IconAirportTerminal-Cc4CU6MC.js} +1 -1
  6. package/dist/{IconArrival-DxunnDcP.js → IconArrival-CdtUsRVo.js} +1 -1
  7. package/dist/{IconBroadcast-eYqgZYdX.js → IconBroadcast-QjMZtFA9.js} +1 -1
  8. package/dist/{IconCabinBag-DBjCF2Xv.js → IconCabinBag-xVC6mOQF.js} +1 -1
  9. package/dist/{IconCheckedBaggage-Cc_Vcylv.js → IconCheckedBaggage-I_1h49TJ.js} +1 -1
  10. package/dist/{IconCheckedBaggage20-BK8BcWZs.js → IconCheckedBaggage20-CyPcsqrn.js} +1 -1
  11. package/dist/{IconCheckedBaggage30-BiZ7g8Aj.js → IconCheckedBaggage30-s5htbUIA.js} +1 -1
  12. package/dist/{IconChild-zmKeUrtL.js → IconChild-DL43kbm1.js} +1 -1
  13. package/dist/{IconContact-B_vCsT11.js → IconContact-FdhTr03q.js} +1 -1
  14. package/dist/{IconDeparture-CvB1tCf8.js → IconDeparture-DNR3OGPD.js} +1 -1
  15. package/dist/{IconDistribution-OwUgE77-.js → IconDistribution-Cl8L6177.js} +1 -1
  16. package/dist/{IconDistributionClosed-KSOCX-B1.js → IconDistributionClosed-DBT6wR78.js} +1 -1
  17. package/dist/{IconDistributionExclusivePair-D8NB9AEy.js → IconDistributionExclusivePair-CkRG6yNV.js} +1 -1
  18. package/dist/{IconDistributionSided-CL1YtqJY.js → IconDistributionSided-D_wdvDNf.js} +1 -1
  19. package/dist/{IconDistributionSupplySided-BTo3QGcS.js → IconDistributionSupplySided-C3VcE8Dw.js} +1 -1
  20. package/dist/{IconDynamicContent-DDYXTrt5.js → IconDynamicContent-Ct1HRd3s.js} +1 -1
  21. package/dist/{IconFares-w-fch-MB.js → IconFares-dIGVrtE4.js} +1 -1
  22. package/dist/{IconFaresOutlined-2munwzp-.js → IconFaresOutlined-bd8kT44f.js} +1 -1
  23. package/dist/{IconFemale-DIK-uMjs.js → IconFemale-CMeYHrDy.js} +1 -1
  24. package/dist/{IconFindConversation-BFBdGFw_.js → IconFindConversation-BdN2Yd3R.js} +1 -1
  25. package/dist/{IconFire-CJP4meA_.js → IconFire-obwuluyJ.js} +1 -1
  26. package/dist/{IconFlight-BSQIWsWY.js → IconFlight-B_6tJxB3.js} +1 -1
  27. package/dist/{IconFlightReturn-DfOxod0d.js → IconFlightReturn-BEO97PwR.js} +1 -1
  28. package/dist/{IconHandHeart-CPX-j2Eo.js → IconHandHeart-hs0yFSEL.js} +1 -1
  29. package/dist/{IconHistory-DAkQZP1Y.js → IconHistory-CT-jOMRt.js} +1 -1
  30. package/dist/{IconHourGlass-BVk7qcq5.js → IconHourGlass-CF940vmy.js} +1 -1
  31. package/dist/{IconIdCard-tjli750D.js → IconIdCard-DKkU58Ix.js} +1 -1
  32. package/dist/{IconInfant-CyTuBv9E.js → IconInfant-DIUT-6_4.js} +1 -1
  33. package/dist/{IconItinerary-BkgXDYJv.js → IconItinerary-D7mOGUmj.js} +1 -1
  34. package/dist/{IconLeave-BdqJt7vt.js → IconLeave-BExl4uyV.js} +1 -1
  35. package/dist/{IconMale-DpMX6cGS.js → IconMale-eHGH5XBz.js} +1 -1
  36. package/dist/{IconMultiSegments-DgoJo4Jp.js → IconMultiSegments-KkrKiZrK.js} +1 -1
  37. package/dist/{IconNoPassport-DkIgwNpl.js → IconNoPassport-PJ2QqqoI.js} +1 -1
  38. package/dist/{IconNoRefund-ChebOxWH.js → IconNoRefund-W47AQisu.js} +1 -1
  39. package/dist/{IconNotion-CGMc2WxX.js → IconNotion-CCnhatKK.js} +1 -1
  40. package/dist/{IconOffline-BwI0SYKI.js → IconOffline-3wunTEJG.js} +1 -1
  41. package/dist/{IconOneWay-Cp2QZ-lJ.js → IconOneWay-BQ5K41Bu.js} +1 -1
  42. package/dist/{IconPaid-DgXr6F7W.js → IconPaid-zwDwWzF8.js} +1 -1
  43. package/dist/{IconWithPassport-C459CztM.js → IconPassport-IxhNLS8P.js} +4 -4
  44. package/dist/{IconPayout-DGzBJcuJ.js → IconPayout-BNSON3dj.js} +1 -1
  45. package/dist/{IconReceipt-Dc7e8pcN.js → IconReceipt-DOosYtgi.js} +1 -1
  46. package/dist/{IconRecurrence-Czm3YpvW.js → IconRecurrence-B8i5kO2Q.js} +1 -1
  47. package/dist/{IconRefund-DllPRUY_.js → IconRefund-DkjF9lEI.js} +1 -1
  48. package/dist/{IconRoundTrip-2CZyPNqg.js → IconRoundTrip-BZvhS65z.js} +1 -1
  49. package/dist/{IconRouteNoStop-B-2IDDyI.js → IconRouteNoStop-CeSlfnpx.js} +1 -1
  50. package/dist/{IconRouteOneStop-fdwQBGKM.js → IconRouteOneStop-aP1Mj-Bv.js} +1 -1
  51. package/dist/{IconScheduleChange-BmJxqPtk.js → IconScheduleChange-QQ4AMAMI.js} +1 -1
  52. package/dist/{IconSeatEmpty-BFkq1dVx.js → IconSeatEmpty-DgVBD165.js} +1 -1
  53. package/dist/{IconSeatSold-CfEnilSn.js → IconSeatSold-BGBwNZUa.js} +1 -1
  54. package/dist/{IconSeatTotal-Dr1gIhZ4.js → IconSeatTotal-Dq9uXccZ.js} +1 -1
  55. package/dist/{IconTemplate--1IoIlaU.js → IconTemplate-BgruEhjI.js} +1 -1
  56. package/dist/{IconTicket-B09w5uee.js → IconTicket-BTaYnuPt.js} +1 -1
  57. package/dist/{IconTimer-BIpzrmmg.js → IconTimer-CyJzne5T.js} +1 -1
  58. package/dist/{IconTrafficControl-B_X7ClFd.js → IconTrafficControl-BeQtpwhK.js} +1 -1
  59. package/dist/{index-BHKeoZcE.js → index-B8RNa4cx.js} +10398 -4751
  60. package/dist/pimp.es.js +1 -1
  61. package/dist/pimp.umd.js +691 -3
  62. package/dist/style.css +1 -1
  63. package/package.json +2 -1
  64. package/src/components/icons/IconPassport.vue +14 -10
  65. package/src/components/index.ts +5 -0
  66. package/src/components/selects/CpMultiselect.vue +426 -0
  67. package/src/constants/src/CpCustomIcons.ts +0 -2
  68. package/src/stories/CpMultiselect.stories.ts +237 -0
  69. package/dist/IconPassport-tXkO0SCe.js +0 -22
  70. package/dist/IconSemiMoon-BkqZsW5b.js +0 -19
  71. package/src/components/icons/IconSemiMoon.vue +0 -29
  72. package/src/components/icons/IconWithPassport.vue +0 -25
@@ -0,0 +1,426 @@
1
+ <template>
2
+ <div class="cpMultiselect">
3
+ <base-input-label v-if="label" :required="required" class="cpMultiselect__label">
4
+ {{ label }}
5
+ </base-input-label>
6
+
7
+ <AutoComplete
8
+ ref="multiselect"
9
+ v-model="selectModel"
10
+ :suggestions="options"
11
+ :option-label="optionLabel"
12
+ :name="name"
13
+ force-selection
14
+ :data-key="trackBy"
15
+ :multiple="multiple"
16
+ input-class="cpMultiselect__input"
17
+ :invalid="isInvalid"
18
+ auto-option-focus
19
+ :placeholder="placeholder"
20
+ :disabled="disabled"
21
+ option-disabled="disabled"
22
+ :pt="passThroughConfig"
23
+ @update:model-value="handleUpdateModelValue"
24
+ @complete="handleSearch"
25
+ @keydown.esc.stop
26
+ >
27
+ <template #empty>
28
+ <slot name="empty">
29
+ <div class="cpMultiselect__empty">{{ emptyMessage }}</div>
30
+ </slot>
31
+ </template>
32
+
33
+ <template #chip="{ value, removeCallback }">
34
+ <slot name="selected-option" :option="value" :remove="removeCallback">
35
+ <cp-badge is-clearable size="sm" @on-clear="removeCallback()">
36
+ <template #leading-icon>
37
+ <slot name="selected-option-leading-icon" :option="value" />
38
+ </template>
39
+ {{ value.name }}
40
+ </cp-badge>
41
+ </slot>
42
+ </template>
43
+
44
+ <template #option="{ option }">
45
+ <slot name="option" :option="option" />
46
+ </template>
47
+
48
+ <template #dropdown>
49
+ <div v-if="displayPrefix" class="cpMultiselect__prefix">
50
+ <slot name="prefix" />
51
+ </div>
52
+
53
+ <cp-loader v-if="isLoading" class="cpMultiselect__loader" color="#B2B2BD" />
54
+ <button v-else :disabled="disabled" type="button" class="cpMultiselect__toggle" @click.stop="toggleDropdown">
55
+ <cp-icon type="chevron-down" class="cpMultiselect__dropdownIcon" :class="chevronDynamicClass" />
56
+ </button>
57
+
58
+ <base-select-clear-button v-if="displayClearButton" class="cpMultiselect__clear" @click="handleClear" />
59
+ </template>
60
+ </AutoComplete>
61
+
62
+ <transition-expand>
63
+ <p v-if="isInvalid" class="cpMultiselect__error">
64
+ {{ errorMessage }}
65
+ </p>
66
+ </transition-expand>
67
+ </div>
68
+ </template>
69
+
70
+ <script setup>
71
+ import { ref, computed, onMounted } from 'vue'
72
+ import AutoComplete from 'primevue/autocomplete'
73
+
74
+ import { absolutePosition, getOuterWidth } from '@primeuix/utils/dom'
75
+
76
+ import BaseInputLabel from '@/components/core/BaseInputLabel.vue'
77
+ import TransitionExpand from '@/components/helpers-utilities/TransitionExpand.vue'
78
+ import BaseSelectClearButton from '@/components/core/BaseSelectClearButton.vue'
79
+
80
+ import { isEmpty } from '@/helpers/object'
81
+
82
+ const props = defineProps({
83
+ label: {
84
+ type: String,
85
+ required: false,
86
+ default: '',
87
+ },
88
+ required: {
89
+ type: Boolean,
90
+ required: false,
91
+ default: false,
92
+ },
93
+ name: {
94
+ type: String,
95
+ required: false,
96
+ default: '',
97
+ },
98
+ placeholder: {
99
+ type: String,
100
+ required: false,
101
+ default: '',
102
+ },
103
+ isInvalid: {
104
+ type: Boolean,
105
+ required: false,
106
+ default: false,
107
+ },
108
+ isClearable: {
109
+ type: Boolean,
110
+ required: false,
111
+ default: false,
112
+ },
113
+ isLoading: {
114
+ type: Boolean,
115
+ required: false,
116
+ default: false,
117
+ },
118
+ disabled: {
119
+ type: Boolean,
120
+ required: false,
121
+ default: false,
122
+ },
123
+ multiple: {
124
+ type: Boolean,
125
+ required: false,
126
+ default: false,
127
+ },
128
+ options: {
129
+ type: Array,
130
+ required: false,
131
+ default: () => [],
132
+ },
133
+ optionLabel: {
134
+ type: String,
135
+ required: false,
136
+ default: 'name',
137
+ },
138
+ trackBy: {
139
+ type: String,
140
+ required: false,
141
+ default: 'id',
142
+ },
143
+ emptyMessage: {
144
+ type: String,
145
+ required: false,
146
+ default: 'No results found',
147
+ },
148
+ errorMessage: {
149
+ type: String,
150
+ required: false,
151
+ default: '',
152
+ },
153
+ modelValue: {
154
+ type: [Array, Object],
155
+ required: false,
156
+ },
157
+ })
158
+
159
+ const emit = defineEmits(['search', 'select', 'clear'])
160
+
161
+ const selectModel = computed({
162
+ get() {
163
+ return props.modelValue
164
+ },
165
+ set(value) {
166
+ if (typeof value === 'string') {
167
+ return
168
+ }
169
+
170
+ emit('update:modelValue', value)
171
+ },
172
+ })
173
+
174
+ const passThroughConfig = {
175
+ root: { class: 'cpMultiselect__select' },
176
+ inputmultiple: { class: 'cpMultiselect__tags' },
177
+ dropdown: { class: 'cpMultiselect__toggle' },
178
+ inputchip: { class: 'cpMultiselect__inputWrapper' },
179
+ overlay: { class: 'cpMultiselect__overlay' },
180
+ listcontainer: { class: 'cpMultiselect__listWrapper' },
181
+ list: { class: 'cpMultiselect__list' },
182
+ option: { class: 'cpMultiselect__option' },
183
+ loader: { class: 'cpMultiselect__hidden' },
184
+ }
185
+
186
+ const multiselect = ref(null)
187
+
188
+ const isDropdownOpen = computed(() => multiselect.value?.overlayVisible)
189
+
190
+ const chevronDynamicClass = computed(() => {
191
+ return {
192
+ 'cpMultiselect__dropdownIcon--isRotated': isDropdownOpen.value,
193
+ }
194
+ })
195
+
196
+ const displayPrefix = computed(() => {
197
+ if (!props.multiple) return true
198
+ return !selectModel.value?.length
199
+ })
200
+
201
+ const displayClearButton = computed(() => {
202
+ if (props.multiple) return false
203
+ return props.isClearable && !isEmpty(selectModel.value)
204
+ })
205
+
206
+ const handleSearch = (event) => emit('search', event.query)
207
+ const handleClear = () => (selectModel.value = null)
208
+
209
+ const toggleDropdown = () => {
210
+ if (isDropdownOpen.value) {
211
+ multiselect.value.hide()
212
+ } else {
213
+ multiselect.value.show()
214
+ }
215
+ }
216
+
217
+ const handleUpdateModelValue = (value) => {
218
+ // Autocomplete will set the model value to the query string if not blocked
219
+ if (!value || typeof value === 'string') {
220
+ return
221
+ }
222
+ }
223
+
224
+ const overrideAlignOverlay = () => (multiselect.value.alignOverlay = alignOverlay)
225
+
226
+ const alignOverlay = () => {
227
+ const target = multiselect.value.$el
228
+ if (!multiselect.value.overlay || !target) return
229
+
230
+ multiselect.value.overlay.style.width = `${getOuterWidth(target)}px`
231
+
232
+ absolutePosition(multiselect.value.overlay, target)
233
+ }
234
+
235
+ onMounted(() => overrideAlignOverlay())
236
+ </script>
237
+
238
+ <style lang="scss">
239
+ .cpMultiselect {
240
+ display: flex;
241
+ flex-direction: column;
242
+ gap: sp.$space;
243
+
244
+ &__label {
245
+ margin-bottom: 0;
246
+ }
247
+
248
+ &__prefix {
249
+ order: -1;
250
+
251
+ &:empty {
252
+ display: none;
253
+ }
254
+ }
255
+
256
+ &__select {
257
+ display: flex;
258
+ min-height: fn.px-to-rem(46);
259
+ align-items: center;
260
+ justify-content: space-between;
261
+ padding: fn.px-to-rem(8);
262
+ border: 1px solid colors.$border-color;
263
+ border-radius: fn.px-to-rem(10);
264
+ gap: sp.$space;
265
+
266
+ &:has(input:focus-visible) {
267
+ box-shadow: 0 0 0 fn.px-to-em(3) color.scale(colors.$primary-color, $lightness: 70%);
268
+ }
269
+
270
+ &:has(input:disabled) {
271
+ background-color: colors.$neutral-light-1;
272
+ cursor: not-allowed;
273
+ }
274
+
275
+ &:has([data-p='invalid']) {
276
+ border-color: colors.$error-color;
277
+
278
+ &:has(input:focus-visible) {
279
+ box-shadow: 0 0 0 fn.px-to-em(3) color.scale(colors.$error-color, $lightness: 70%);
280
+ }
281
+ }
282
+ }
283
+
284
+ &__tags {
285
+ display: flex;
286
+ flex: 1;
287
+ flex-wrap: wrap;
288
+ gap: sp.$space;
289
+ }
290
+
291
+ &__toggle {
292
+ @extend %u-focus-outline;
293
+
294
+ display: flex;
295
+ align-items: center;
296
+ justify-content: center;
297
+
298
+ &:disabled {
299
+ cursor: not-allowed;
300
+ }
301
+ }
302
+
303
+ &__inputWrapper {
304
+ display: flex;
305
+ flex: 1;
306
+ align-items: center;
307
+ }
308
+
309
+ &__input {
310
+ padding: 0;
311
+ flex: 1;
312
+ font-size: fn.px-to-rem(14);
313
+ line-height: fn.px-to-rem(24);
314
+ background-color: transparent;
315
+
316
+ &:disabled {
317
+ cursor: not-allowed;
318
+ }
319
+ }
320
+
321
+ &__input {
322
+ padding: 0;
323
+ flex: 1;
324
+ font-size: fn.px-to-rem(14);
325
+ line-height: fn.px-to-rem(24);
326
+ }
327
+
328
+ &__dropdownIcon {
329
+ @include mx.square-sizing(16);
330
+
331
+ transition: rotate 200ms ease;
332
+
333
+ &--isRotated {
334
+ rotate: 180deg;
335
+ }
336
+ }
337
+
338
+ &__loader {
339
+ @include mx.square-sizing(16);
340
+ }
341
+
342
+ &__overlay {
343
+ left: 0;
344
+ overflow: hidden;
345
+ background: colors.$neutral-light;
346
+ border-radius: fn.px-to-rem(8);
347
+ margin-block: sp.$space;
348
+ box-shadow:
349
+ 0 2px 4px 0 rgba(18, 18, 23, 0.04),
350
+ 0 5px 8px 0 rgba(18, 18, 23, 0.04),
351
+ 0 10px 18px 0 rgba(18, 18, 23, 0.03),
352
+ 0 24px 48px 0 rgba(18, 18, 23, 0.03),
353
+ 0 0 0 1px rgba(18, 18, 23, 0.1);
354
+ }
355
+
356
+ &__listWrapper {
357
+ overflow-y: auto;
358
+ }
359
+
360
+ &__hidden {
361
+ display: none;
362
+ }
363
+
364
+ &__list {
365
+ padding: sp.$space-sm;
366
+ }
367
+
368
+ &__option {
369
+ display: flex;
370
+ align-items: center;
371
+ padding: fn.px-to-rem(10);
372
+ border-radius: fn.px-to-rem(6);
373
+ gap: sp.$space-sm;
374
+ font-size: fn.px-to-rem(14);
375
+ line-height: fn.px-to-rem(18);
376
+
377
+ &:hover,
378
+ &[data-p-focused='true'],
379
+ &[data-p-selected='true'] {
380
+ background: colors.$neutral-dark-5;
381
+ color: colors.$neutral-dark;
382
+ }
383
+
384
+ &[data-p-selected='true'] {
385
+ border: 1px dashed colors.$border-color;
386
+ }
387
+
388
+ &[data-p-disabled='true'] {
389
+ opacity: 0.5;
390
+ cursor: not-allowed;
391
+ pointer-events: none;
392
+ }
393
+ }
394
+
395
+ &__empty {
396
+ padding: sp.$space;
397
+ font-size: fn.px-to-rem(14);
398
+ color: colors.$neutral-dark-1;
399
+ }
400
+
401
+ &__error {
402
+ color: colors.$error-color;
403
+ font-size: fn.px-to-rem(14);
404
+ font-weight: 500;
405
+ line-height: fn.px-to-rem(24);
406
+ }
407
+
408
+ @include mx.media-query-pointer-device-only {
409
+ &__clear {
410
+ display: none;
411
+ }
412
+ }
413
+ }
414
+
415
+ @include mx.media-query-pointer-device-only {
416
+ .cpMultiselect:has(.cpMultiselect__clear):is(:hover, :focus-within) {
417
+ .cpMultiselect__clear {
418
+ display: flex;
419
+ }
420
+
421
+ .cpMultiselect__toggle {
422
+ display: none;
423
+ }
424
+ }
425
+ }
426
+ </style>
@@ -63,7 +63,6 @@ export default {
63
63
  'seat-empty': defineAsyncComponent(() => import('@/components/icons/IconSeatEmpty.vue')),
64
64
  'seat-sold': defineAsyncComponent(() => import('@/components/icons/IconSeatSold.vue')),
65
65
  'seat-total': defineAsyncComponent(() => import('@/components/icons/IconSeatTotal.vue')),
66
- 'semi-moon': defineAsyncComponent(() => import('@/components/icons/IconSemiMoon.vue')),
67
66
  supplier: defineAsyncComponent(() => import('@/components/icons/IconSupplier.vue')),
68
67
  template: defineAsyncComponent(() => import('@/components/icons/IconTemplate.vue')),
69
68
  'third-party': defineAsyncComponent(() => import('@/components/icons/IconThirdParty.vue')),
@@ -71,5 +70,4 @@ export default {
71
70
  timer: defineAsyncComponent(() => import('@/components/icons/IconTimer.vue')),
72
71
  tooltip: defineAsyncComponent(() => import('@/components/icons/IconTooltip.vue')),
73
72
  'traffic-control': defineAsyncComponent(() => import('@/components/icons/IconTrafficControl.vue')),
74
- 'with-passport': defineAsyncComponent(() => import('@/components/icons/IconWithPassport.vue')),
75
73
  }
@@ -0,0 +1,237 @@
1
+ import type { Meta, StoryObj } from '@storybook/vue3'
2
+ import { ref } from 'vue'
3
+
4
+ import CpMultiselect from '@/components/selects/CpMultiselect.vue'
5
+
6
+ const meta = {
7
+ title: 'CpMultiSelect',
8
+ component: CpMultiselect,
9
+ argTypes: {
10
+ label: {
11
+ control: 'text',
12
+ description: 'Label of the select',
13
+ },
14
+ required: {
15
+ control: 'boolean',
16
+ description: 'Whether the select is required',
17
+ },
18
+ name: {
19
+ control: 'text',
20
+ description: 'Name of the select',
21
+ },
22
+ placeholder: {
23
+ control: 'text',
24
+ description: 'Placeholder of the select',
25
+ },
26
+ isInvalid: {
27
+ control: 'boolean',
28
+ description: 'Whether the select is invalid',
29
+ },
30
+ multiple: {
31
+ control: 'boolean',
32
+ description: 'Whether the select should be multiple',
33
+ },
34
+ options: {
35
+ control: 'object',
36
+ description: 'Options of the select',
37
+ },
38
+ errorMessage: {
39
+ control: 'text',
40
+ description: 'Error message of the select',
41
+ },
42
+ disabled: {
43
+ control: 'boolean',
44
+ description: 'Whether the select is disabled',
45
+ },
46
+ isLoading: {
47
+ control: 'boolean',
48
+ description: 'Whether the select is loading',
49
+ },
50
+ emptyMessage: {
51
+ control: 'text',
52
+ description: 'Message displayed when no options are found',
53
+ },
54
+ trackBy: {
55
+ control: 'text',
56
+ description: 'Property to track the selected option',
57
+ },
58
+ },
59
+ } satisfies Meta<typeof CpMultiselect>
60
+
61
+ export default meta
62
+ type Story = StoryObj<typeof meta>
63
+
64
+ export const Single: Story = {
65
+ args: {
66
+ placeholder: 'Select a supplier',
67
+ multiple: false,
68
+ options: [
69
+ { id: 1, name: 'MGA AIRLINES' },
70
+ { id: 2, name: 'ZENITH - EUROATLANTIC AIRWAYS' },
71
+ { id: 3, name: 'ZENITH - SOUTHERN AIRWAYS EXPRESS MOKULELE AIRLINES AND SURF AIR MOBILITY' },
72
+ { id: 4, name: 'EDO TUNISAIR ITALY' },
73
+ { id: 5, name: 'LMX SUISSE SA' },
74
+ { id: 6, name: 'LMX VOYAGES SAS' },
75
+ { id: 7, name: 'FLY KHIVA TRAVEL' },
76
+ { id: 8, name: 'ZENITH - AIR CHATHAMS' },
77
+ { id: 9, name: 'CP EMPLOYEES' },
78
+ { id: 10, name: 'MONDIAL TOURISME' },
79
+ { id: 11, name: 'UNITRAVEL UTAZÁSI IRODA' },
80
+ ],
81
+ },
82
+ render: (args) => ({
83
+ components: { CpMultiselect },
84
+ setup() {
85
+ const searchQuery = ref('')
86
+ const isLoading = ref(false)
87
+
88
+ const originalOptions = ref(args.options)
89
+ const dynamicOptions = ref(originalOptions.value)
90
+
91
+ const handleSearch = async (query: string) => {
92
+ isLoading.value = true
93
+ searchQuery.value = query
94
+
95
+ dynamicOptions.value = originalOptions.value
96
+
97
+ await new Promise((resolve) => setTimeout(resolve, 500))
98
+
99
+ dynamicOptions.value = dynamicOptions.value.filter((option: { name: string }) => {
100
+ return option.name.toLowerCase().includes(searchQuery.value.toLowerCase())
101
+ })
102
+
103
+ isLoading.value = false
104
+ }
105
+
106
+ const selectedSupplier = ref(null)
107
+ return { args, selectedSupplier, dynamicOptions, handleSearch, isLoading }
108
+ },
109
+ template: `
110
+ <div style="padding: 20px;">
111
+ <CpMultiselect v-model="selectedSupplier" v-bind="args" :options="dynamicOptions" :is-loading="isLoading" @search="handleSearch">
112
+ <template #prefix>
113
+ <cp-partner-badge type="supplier" size="xs" />
114
+ </template>
115
+ <template #option="{ option }">
116
+ <div style="display: flex; align-items: center; gap: 8px;">
117
+ <cp-partner-badge type="supplier" size="xs" />
118
+ {{ option.name }}
119
+ </div>
120
+ </template>
121
+ </CpMultiselect>
122
+ </div>
123
+ `,
124
+ }),
125
+ }
126
+
127
+ export const Multiple: Story = {
128
+ args: {
129
+ required: false,
130
+ name: 'select',
131
+ placeholder: 'All airlines',
132
+ isInvalid: false,
133
+ disabled: false,
134
+ multiple: true,
135
+ emptyMessage: 'No airlines found',
136
+ options: [
137
+ { id: 1, name: 'United Airlines', iata_code: 'UA' },
138
+ { id: 2, name: 'Delta Airlines', iata_code: 'DL' },
139
+ { id: 3, name: 'American Airlines', iata_code: 'AA' },
140
+ { id: 4, name: 'Southwest Airlines', iata_code: 'WN' },
141
+ { id: 5, name: 'Alaska Airlines', iata_code: 'AS' },
142
+ { id: 6, name: 'JetBlue Airways', iata_code: 'B6' },
143
+ { id: 7, name: 'Spirit Airlines', iata_code: 'NK' },
144
+ { id: 8, name: 'Frontier Airlines', iata_code: 'F9' },
145
+ { id: 9, name: 'Hawaiian Airlines', iata_code: 'HA' },
146
+ { id: 10, name: 'SkyWest Airlines', iata_code: 'OO' },
147
+ { id: 11, name: 'Allegiant Air', iata_code: 'G4' },
148
+ { id: 12, name: 'Atlantic Southeast Airlines', iata_code: 'EV' },
149
+ { id: 13, name: 'American Eagle Airlines', iata_code: 'MQ' },
150
+ { id: 14, name: 'Alaska Airlines', iata_code: 'AS' },
151
+ { id: 15, name: 'Air France', iata_code: 'AF', disabled: true },
152
+ { id: 16, name: 'Air Canada', iata_code: 'AC' },
153
+ { id: 17, name: 'Air New Zealand', iata_code: 'NZ' },
154
+ { id: 18, name: 'Air China', iata_code: 'CA' },
155
+ { id: 19, name: 'Air India', iata_code: 'AI' },
156
+ { id: 20, name: 'Air Berlin', iata_code: 'AB' },
157
+ { id: 21, name: 'AirAsia', iata_code: 'AK' },
158
+ ],
159
+ },
160
+ render: (args) => ({
161
+ components: { CpMultiselect },
162
+ setup() {
163
+ const searchQuery = ref('')
164
+ const isLoading = ref(false)
165
+
166
+ const originalOptions = ref(args.options)
167
+ const dynamicOptions = ref(originalOptions.value)
168
+
169
+ const handleSearch = async (query: string) => {
170
+ isLoading.value = true
171
+ searchQuery.value = query
172
+
173
+ if (!searchQuery.value) return (dynamicOptions.value = originalOptions.value)
174
+
175
+ await new Promise((resolve) => setTimeout(resolve, 500))
176
+
177
+ dynamicOptions.value = dynamicOptions.value.filter((option: { name: string }) => {
178
+ return option.name.toLowerCase().includes(searchQuery.value.toLowerCase())
179
+ })
180
+
181
+ isLoading.value = false
182
+ }
183
+
184
+ const selectedAirlines = ref([])
185
+ return { args, selectedAirlines, dynamicOptions, handleSearch, isLoading }
186
+ },
187
+ template: `
188
+ <div style="padding: 20px;">
189
+ <CpMultiselect v-model="selectedAirlines" v-bind="args" :options="dynamicOptions" :is-loading="isLoading" @search="handleSearch">
190
+ <template #prefix>
191
+ <cp-partner-badge type="airline" size="xs" />
192
+ </template>
193
+ <template #selected-option-leading-icon="{ option }">
194
+ <cp-airline-logo :iata-code="option.iata_code" size="14" />
195
+ </template>
196
+ <template #option="{ option }">
197
+ <div style="display: flex; align-items: center; gap: 8px;">
198
+ <cp-airline-logo :iata-code="option.iata_code" size="14" />
199
+ {{ option.name }}
200
+ </div>
201
+ </template>
202
+ </CpMultiselect>
203
+ </div>
204
+ `,
205
+ }),
206
+ }
207
+
208
+ export const Invalid: Story = {
209
+ args: {
210
+ placeholder: 'Select a supplier',
211
+ disabled: false,
212
+ isInvalid: true,
213
+ options: [],
214
+ },
215
+ render: (args) => ({
216
+ components: { CpMultiselect },
217
+ setup() {
218
+ const selectedSupplier = ref(null)
219
+ return { args, selectedSupplier }
220
+ },
221
+ template: `
222
+ <div style="padding: 20px;">
223
+ <CpMultiselect v-model="selectedSupplier" v-bind="args">
224
+ <template #prefix>
225
+ <cp-partner-badge type="supplier" size="xs" />
226
+ </template>
227
+ <template #option="{ option }">
228
+ <div style="display: flex; align-items: center; gap: 8px;">
229
+ <cp-partner-badge type="supplier" size="xs" />
230
+ {{ option.name }}
231
+ </div>
232
+ </template>
233
+ </CpMultiselect>
234
+ </div>
235
+ `,
236
+ }),
237
+ }
@@ -1,22 +0,0 @@
1
- import { createElementBlock as t, openBlock as o, createElementVNode as C } from "vue";
2
- import { _ as r } from "./index-BHKeoZcE.js";
3
- const l = {}, n = {
4
- width: "24",
5
- height: "24",
6
- viewBox: "0 0 24 24",
7
- fill: "currentColor",
8
- xmlns: "http://www.w3.org/2000/svg",
9
- "fill-rule": "evenodd",
10
- "clip-rule": "evenodd"
11
- };
12
- function s(c, e) {
13
- return o(), t("svg", n, e[0] || (e[0] = [
14
- C("path", { d: "M3.45483 3.88318C3.45483 2.22632 4.79798 0.883179 6.45483 0.883179H17.545C19.2018 0.883179 20.545 2.22632 20.545 3.88318V20.1169C20.545 21.7737 19.2018 23.1169 17.545 23.1169H6.45483C4.79798 23.1169 3.45483 21.7737 3.45483 20.1169V3.88318ZM6.45483 2.88318C5.90255 2.88318 5.45483 3.33089 5.45483 3.88318V20.1169C5.45483 20.6691 5.90255 21.1169 6.45483 21.1169H17.545C18.0973 21.1169 18.545 20.6691 18.545 20.1169V3.88318C18.545 3.33089 18.0973 2.88318 17.545 2.88318H6.45483Z" }, null, -1),
15
- C("path", { d: "M7.57593 18.2964C7.57593 17.7441 8.02364 17.2964 8.57593 17.2964H15.4242C15.9765 17.2964 16.4242 17.7441 16.4242 18.2964C16.4242 18.8487 15.9765 19.2964 15.4242 19.2964H8.57593C8.02364 19.2964 7.57593 18.8487 7.57593 18.2964Z" }, null, -1),
16
- C("path", { d: "M11.9999 14.5239C14.8415 14.5239 17.1452 12.221 17.1466 9.37975L17.1466 9.37732C17.1466 6.53491 14.8423 4.23059 11.9999 4.23059C9.1575 4.23059 6.85327 6.53482 6.85327 9.37724C6.85327 12.2196 9.1575 14.5239 11.9999 14.5239ZM10.2876 6.15677C9.34877 6.657 8.65444 7.55578 8.43046 8.62732H9.54302C9.64653 7.76371 9.89961 6.92801 10.2876 6.15677ZM13.7124 6.15688C14.1004 6.92809 14.3535 7.76375 14.457 8.62732H15.5694C15.3454 7.55586 14.6512 6.65713 13.7124 6.15688ZM12.9435 8.62732C12.8122 7.75568 12.4898 6.9241 12 6.19247C11.5102 6.9241 11.1877 7.75568 11.0565 8.62732H12.9435ZM11.0565 10.1273H12.9435C12.8122 10.9989 12.4897 11.8304 12 12.5619C11.5103 11.8304 11.1878 10.9989 11.0565 10.1273ZM9.54305 10.1273H8.43049C8.65452 11.1988 9.34884 12.0975 10.2877 12.5977C9.89968 11.8265 9.64659 10.9909 9.54305 10.1273ZM13.7123 12.5976C14.1003 11.8265 14.3534 10.9908 14.4569 10.1273H15.5693C15.3453 11.1987 14.6511 12.0974 13.7123 12.5976Z" }, null, -1)
17
- ]));
18
- }
19
- const i = /* @__PURE__ */ r(l, [["render", s]]);
20
- export {
21
- i as default
22
- };