design-system-next 2.14.3 → 2.15.1

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.
@@ -0,0 +1,538 @@
1
+ import { ref, toRefs, computed, type ComputedRef, watch, onMounted, type SetupContext } from 'vue';
2
+ import { useVModel } from '@vueuse/core';
3
+
4
+ import { countries } from 'countries-list';
5
+ import classNames from 'classnames';
6
+
7
+ import { type InputCurrencyPropTypes, type InputCurrencyEmitTypes, type CurrencyOption } from './input-currency';
8
+
9
+ interface CountryLike {
10
+ name: string;
11
+ currency: string[] | undefined;
12
+ }
13
+
14
+ interface InputCurrencyClasses {
15
+ dropdownBaseClasses: string;
16
+ dropdownWrappertClasses: string;
17
+ }
18
+
19
+ export const useInputCurrency = (props: InputCurrencyPropTypes, emit: SetupContext<InputCurrencyEmitTypes>['emit']) => {
20
+ const {
21
+ id,
22
+ preSelectedCurrency,
23
+ disabledCountryCurrency,
24
+ disabled,
25
+ autoFormat,
26
+ maxDecimals,
27
+ minDecimals,
28
+ disableRounding,
29
+ } = toRefs(props);
30
+
31
+ const inputCurrencyClasses: ComputedRef<InputCurrencyClasses> = computed(() => {
32
+ const dropdownBaseClasses = classNames(
33
+ '[&_#dropdown-wrapper]:spr-my-1',
34
+ '[&_#dropdown-wrapper[data-popper-placement="bottom-start"]]:spr-ml-[-10px]',
35
+ '[&_#dropdown-wrapper[data-popper-placement="bottom-start"]]:spr-mt-[6px]',
36
+ '[&_#dropdown-wrapper[data-popper-placement="top-start"]]:spr-ml-[-10px]',
37
+ '[&_#dropdown-wrapper[data-popper-placement="top-start"]]:spr-mt-[-6px]',
38
+ );
39
+
40
+ const dropdownWrappertClasses = classNames(
41
+ 'spr-font-weight-regular spr-font-size-200 spr-line-height-500 spr-letter-spacing-none spr-font-main',
42
+ 'spr-flex spr-items-center spr-gap-size-spacing-5xs',
43
+ {
44
+ 'spr-cursor-not-allowed': disabled.value,
45
+ 'spr-cursor-default': disabledCountryCurrency.value && !disabled.value,
46
+ 'spr-cursor-pointer': !disabledCountryCurrency.value && !disabled.value,
47
+ },
48
+ );
49
+
50
+ return {
51
+ dropdownBaseClasses,
52
+ dropdownWrappertClasses,
53
+ };
54
+ });
55
+
56
+ // fallback random id if user does not provide one (stable per component instance)
57
+ const fallbackId = ref(`currency-${Math.random().toString(36).slice(2, 8)}-dropdown`);
58
+ const dropdownId = computed(() => (id.value ? `${id.value}-dropdown` : fallbackId.value));
59
+
60
+ const modelValue = useVModel(props, 'modelValue', emit);
61
+ const popperState = ref(false);
62
+
63
+ // Locale mapping per currency (extend as needed). Fallback 'en-US'.
64
+ const currencyLocaleMap: Record<string, string> = {
65
+ EUR: 'de-DE',
66
+ USD: 'en-US',
67
+ GBP: 'en-GB',
68
+ JPY: 'ja-JP',
69
+ PHP: 'en-PH',
70
+ AUD: 'en-AU',
71
+ CAD: 'en-CA',
72
+ CHF: 'de-CH',
73
+ };
74
+
75
+ const currentLocale = computed(() => currencyLocaleMap[selected.value.currency] || 'en-US');
76
+
77
+ const buildNumberFormat = (minFrac: number, maxFrac: number) =>
78
+ new Intl.NumberFormat(currentLocale.value, {
79
+ style: 'currency',
80
+ currency: selected.value.currency,
81
+ currencyDisplay: 'symbol',
82
+ minimumFractionDigits: minFrac,
83
+ maximumFractionDigits: maxFrac,
84
+ });
85
+
86
+ const localeSeparators = computed(() => {
87
+ try {
88
+ const fmt = buildNumberFormat(0, 2);
89
+ const parts = fmt.formatToParts(1234.5);
90
+ const group = parts.find((p) => p.type === 'group')?.value || ',';
91
+ const decimal = parts.find((p) => p.type === 'decimal')?.value || '.';
92
+ return { group, decimal };
93
+ } catch {
94
+ return { group: ',', decimal: '.' };
95
+ }
96
+ });
97
+
98
+ // Numeric representation – removes locale grouping & normalizes decimal to '.'
99
+ const numericValue = computed<number | null>(() => {
100
+ if (!modelValue.value) return null;
101
+ const { group, decimal } = localeSeparators.value;
102
+ const escapedGroup = group.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&');
103
+ const escapedDec = decimal.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&');
104
+ let cleaned = String(modelValue.value)
105
+ .replace(new RegExp(escapedGroup, 'g'), '')
106
+ .replace(new RegExp(escapedDec, 'g'), '.');
107
+ cleaned = cleaned.replace(/[^0-9.-]/g, '');
108
+ const parsed = Number(cleaned);
109
+ return isNaN(parsed) ? null : parsed;
110
+ });
111
+
112
+ const rawNumericString = computed<string | null>(() => {
113
+ if (!modelValue.value) return null;
114
+ const { group, decimal } = localeSeparators.value;
115
+ const escapedGroup = group.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&');
116
+ const escapedDec = decimal.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&');
117
+ let cleaned = String(modelValue.value)
118
+ .replace(new RegExp(escapedGroup, 'g'), '')
119
+ .replace(new RegExp(escapedDec, 'g'), '.');
120
+ cleaned = cleaned.replace(/[^0-9.-]/g, '');
121
+ return cleaned || null;
122
+ });
123
+
124
+ /**
125
+ * Derive the default fraction digits for a currency using Intl metadata.
126
+ * Falls back to 2 when unavailable.
127
+ */
128
+ const getCurrencyFractionDigits = (code: string): number => {
129
+ try {
130
+ const fmt = new Intl.NumberFormat('en', { style: 'currency', currency: code });
131
+ const resolved = fmt.resolvedOptions();
132
+ if (resolved && typeof resolved.minimumFractionDigits === 'number') {
133
+ return resolved.minimumFractionDigits;
134
+ }
135
+ } catch {
136
+ /* ignore */
137
+ }
138
+ return 2;
139
+ };
140
+
141
+ const currentCurrencyFractionDigits = computed(() => getCurrencyFractionDigits(selected.value.currency));
142
+
143
+ const effectiveMinDecimals = computed(() => {
144
+ // Minimum is the greater of user minDecimals and native fraction digits lower bound (native min == native max in most cases)
145
+ const userMin = Math.min(Math.max(0, minDecimals.value), 6);
146
+ return Math.min(6, Math.max(0, Math.min(userMin, maxDecimals.value)));
147
+ });
148
+
149
+ const effectiveMaxDecimals = computed(() => {
150
+ // Use native fraction digits as baseline but allow user to reduce (rare) or extend (not beyond 6)
151
+ const native = currentCurrencyFractionDigits.value; // e.g. JPY => 0, USD => 2
152
+ const userMax = Math.min(Math.max(0, maxDecimals.value), 6);
153
+ // If user sets max below native, keep native to respect currency standard.
154
+ const adjustedMax = Math.max(native, userMax);
155
+ // Ensure max >= min
156
+ return Math.max(adjustedMax, effectiveMinDecimals.value);
157
+ });
158
+
159
+ const clampFractionDigits = (frac: string): string => frac.slice(0, effectiveMaxDecimals.value);
160
+
161
+ const formatNumberForBlur = (value: number): string => {
162
+ const fmt = buildNumberFormat(effectiveMinDecimals.value, effectiveMaxDecimals.value);
163
+ // Remove currency symbol from formatted output (component is responsible only for numeric part in input)
164
+ const parts = fmt.formatToParts(value).filter((p) => p.type !== 'currency');
165
+ return parts
166
+ .map((p) => p.value)
167
+ .join('')
168
+ .replace(/\s+/g, '');
169
+ };
170
+
171
+ /**
172
+ * Formats a numeric string (no currency symbol) applying grouping and truncating decimals but not padding.
173
+ * Used while user is still interacting (on input) if autoFormat.
174
+ */
175
+ const handleFormatDisplay = (raw: string): string => {
176
+ if (!autoFormat.value) return raw;
177
+ if (!raw) return '';
178
+ // Detect user decimal char as either '.' or last ','
179
+ let work = raw;
180
+ // Strip spaces
181
+ work = work.replace(/\s+/g, '');
182
+ // Allow only digits, separators, minus
183
+ work = work.replace(/[^0-9,.-]/g, '');
184
+ let sign = '';
185
+ if (work.startsWith('-')) {
186
+ sign = '-';
187
+ work = work.slice(1);
188
+ }
189
+ const lastDot = work.lastIndexOf('.');
190
+ const lastComma = work.lastIndexOf(',');
191
+ const lastSep = Math.max(lastDot, lastComma);
192
+ let intPart: string;
193
+ let fracPart = '';
194
+ if (lastSep !== -1) {
195
+ intPart = work.slice(0, lastSep).replace(/[.,]/g, '');
196
+ fracPart = work.slice(lastSep + 1);
197
+ } else {
198
+ intPart = work.replace(/[.,]/g, '');
199
+ }
200
+ if (!/^[0-9]*$/.test(intPart) || !/^[0-9]*$/.test(fracPart)) return raw;
201
+ const truncatedFrac = clampFractionDigits(fracPart);
202
+ const intNumber = Number(intPart || '0');
203
+ // Use Intl just for grouping integer portion
204
+ const fmtInt = buildNumberFormat(0, 0);
205
+ const groupedInt = fmtInt
206
+ .formatToParts(intNumber)
207
+ .filter((p) => p.type === 'integer' || p.type === 'group')
208
+ .map((p) => p.value)
209
+ .join('');
210
+ if (truncatedFrac.length > 0 || lastSep !== -1) {
211
+ // Use current locale decimal symbol
212
+ return sign + groupedInt + localeSeparators.value.decimal + truncatedFrac;
213
+ }
214
+ return sign + groupedInt;
215
+ };
216
+
217
+ /**
218
+ * Full formatting for blur: grouping + enforce min decimals padding and clamp to max.
219
+ */
220
+ const formatOnBlur = (raw: string): string => {
221
+ if (!raw) return '';
222
+
223
+ const cleaned = raw.replace(/[^0-9,.-]/g, '');
224
+ let work = cleaned;
225
+ let sign = '';
226
+ if (work.startsWith('-')) {
227
+ sign = '-';
228
+ work = work.slice(1);
229
+ }
230
+ const lastDot = work.lastIndexOf('.');
231
+ const lastComma = work.lastIndexOf(',');
232
+ const lastSep = Math.max(lastDot, lastComma);
233
+ let intPart: string;
234
+ let fracPart = '';
235
+ if (lastSep !== -1) {
236
+ intPart = work.slice(0, lastSep).replace(/[.,]/g, '');
237
+ fracPart = work.slice(lastSep + 1);
238
+ } else {
239
+ intPart = work.replace(/[.,]/g, '');
240
+ }
241
+ if (!intPart) intPart = '0';
242
+
243
+ // Truncate fractional part to max without rounding if disableRounding
244
+ if (fracPart && disableRounding.value) {
245
+ fracPart = fracPart.slice(0, effectiveMaxDecimals.value);
246
+ }
247
+
248
+ const canonical = sign + intPart + (fracPart ? '.' + fracPart : '');
249
+ let numeric = Number(canonical);
250
+ if (isNaN(numeric)) return raw;
251
+
252
+ if (disableRounding.value && fracPart) {
253
+ // Ensure numeric value is truncated (not rounded) at effectiveMaxDecimals
254
+ const factor = Math.pow(10, effectiveMaxDecimals.value);
255
+ numeric = Math.trunc(numeric * factor) / factor;
256
+ }
257
+
258
+ return formatNumberForBlur(numeric);
259
+ };
260
+
261
+ const handleCurrencyInput = (event: InputEvent) => {
262
+ const inputEl = event.target as HTMLInputElement;
263
+
264
+ let raw = inputEl.value;
265
+
266
+ raw = raw.replace(/\s+/g, '');
267
+
268
+ let sign = '';
269
+
270
+ if (raw.startsWith('-')) {
271
+ sign = '-';
272
+ raw = raw.slice(1);
273
+ }
274
+
275
+ raw = raw.replace(/[^0-9.,-]/g, '');
276
+
277
+ const firstDot = raw.indexOf('.');
278
+
279
+ if (firstDot !== -1) {
280
+ raw = raw.slice(0, firstDot + 1) + raw.slice(firstDot + 1).replace(/\./g, '');
281
+ }
282
+
283
+ // Do not truncate fractional digits while typing; allow user to input freely.
284
+ const formatted = handleFormatDisplay(sign + raw);
285
+
286
+ modelValue.value = formatted;
287
+
288
+ emit('getCurrencyErrors', []);
289
+ };
290
+
291
+ const handleBlur = () => {
292
+ // Format only if there is a value
293
+ if (modelValue.value) {
294
+ let out = modelValue.value;
295
+
296
+ out = formatOnBlur(out);
297
+
298
+ modelValue.value = out;
299
+ }
300
+
301
+ // Always emit meta on blur (even if empty -> numericValue/rawValue may be null)
302
+ emit('getSelectedCurrencyMeta', {
303
+ currency: selected.value.currency,
304
+ symbol: selected.value.symbol,
305
+ numericValue: numericValue.value,
306
+ rawValue: rawNumericString.value,
307
+ });
308
+
309
+ if (numericValue.value !== null) emit('getNumericValue', numericValue.value);
310
+ };
311
+
312
+ const handleSelectedCurrency = (currencyRaw: unknown) => {
313
+ let currencyCode: string | undefined;
314
+
315
+ if (typeof currencyRaw === 'string') {
316
+ currencyCode = currencyRaw;
317
+ } else if (
318
+ currencyRaw &&
319
+ typeof currencyRaw === 'object' &&
320
+ 'value' in currencyRaw &&
321
+ typeof (currencyRaw as { value: unknown }).value === 'string'
322
+ ) {
323
+ currencyCode = (currencyRaw as { value: string }).value;
324
+ }
325
+
326
+ if (typeof currencyCode !== 'string') {
327
+ return;
328
+ }
329
+
330
+ const found = currencyOptions.find((c) => c.value === currencyCode);
331
+
332
+ if (found) {
333
+ const previousCurrency = selected.value.currency;
334
+
335
+ // Locale-agnostic parse of current input before switching locale to avoid misinterpretation
336
+ let preSwitchNumeric: number | null = null;
337
+ if (modelValue.value) {
338
+ const raw = modelValue.value;
339
+ const cleaned = raw.replace(/[^0-9,.-]/g, '');
340
+ let work = cleaned;
341
+ let sign = '';
342
+ if (work.startsWith('-')) {
343
+ sign = '-';
344
+ work = work.slice(1);
345
+ }
346
+ // Identify last separator (either '.' or ',') as decimal; earlier ones are grouping
347
+ const lastDot = work.lastIndexOf('.');
348
+ const lastComma = work.lastIndexOf(',');
349
+ const lastSep = Math.max(lastDot, lastComma);
350
+ let intPart: string;
351
+ let fracPart = '';
352
+ if (lastSep !== -1) {
353
+ intPart = work.slice(0, lastSep).replace(/[.,]/g, '');
354
+ fracPart = work.slice(lastSep + 1);
355
+ } else {
356
+ intPart = work.replace(/[.,]/g, '');
357
+ }
358
+ const canonical = sign + intPart + (fracPart ? '.' + fracPart : '');
359
+ const parsed = Number(canonical);
360
+ preSwitchNumeric = isNaN(parsed) ? null : parsed;
361
+ }
362
+
363
+ selected.value = { ...found };
364
+ emit('getSelectedCurrencyMeta', {
365
+ currency: found.currency,
366
+ symbol: found.symbol,
367
+ numericValue: preSwitchNumeric !== null ? preSwitchNumeric : numericValue.value,
368
+ rawValue: preSwitchNumeric !== null ? String(preSwitchNumeric) : rawNumericString.value,
369
+ });
370
+ // Re-format current input according to new currency fraction rules only if currency actually changed
371
+ if (preSwitchNumeric !== null && previousCurrency !== found.currency) {
372
+ modelValue.value = formatNumberForBlur(preSwitchNumeric);
373
+ emit('getNumericValue', preSwitchNumeric);
374
+ }
375
+ }
376
+ };
377
+
378
+ const handlePopperState = (state: boolean) => {
379
+ popperState.value = state;
380
+ };
381
+
382
+ const dropdownDisplayText = computed(() => (props.displayAsCode ? selected.value.currency : selected.value.symbol));
383
+
384
+ // #region - Set Currency Options
385
+ // Collect currency codes first so we can derive ambiguity and symbols deterministically.
386
+ const currencyCodesSet = new Set<string>();
387
+ const currencyCodeToName = new Map<string, string>();
388
+
389
+ Object.values(countries).forEach((country) => {
390
+ const _country = country as CountryLike;
391
+ const codes: string[] = Array.isArray(_country.currency) ? _country.currency : [];
392
+
393
+ codes.forEach((code) => {
394
+ currencyCodesSet.add(code);
395
+ if (!currencyCodeToName.has(code)) {
396
+ currencyCodeToName.set(code, _country.name);
397
+ }
398
+ });
399
+ });
400
+
401
+ // Dynamically detect ambiguous narrow symbols so we fall back to currency code when a symbol represents >1 code.
402
+ const detectAmbiguousSymbols = (codes: string[], locale = 'en'): Set<string> => {
403
+ const symbolToCodes = new Map<string, Set<string>>();
404
+ for (const code of codes) {
405
+ try {
406
+ const parts = new Intl.NumberFormat(locale, {
407
+ style: 'currency',
408
+ currency: code,
409
+ currencyDisplay: 'narrowSymbol',
410
+ minimumFractionDigits: 0,
411
+ maximumFractionDigits: 0,
412
+ }).formatToParts(1);
413
+ const sym = parts.find((p) => p.type === 'currency')?.value;
414
+
415
+ if (!sym) continue;
416
+ if (!symbolToCodes.has(sym)) symbolToCodes.set(sym, new Set());
417
+
418
+ symbolToCodes.get(sym)!.add(code);
419
+ } catch {
420
+ // Ignore unsupported currency
421
+ }
422
+ }
423
+
424
+ const ambiguous = new Set<string>();
425
+
426
+ for (const [sym, set] of symbolToCodes.entries()) {
427
+ if (set.size > 1) ambiguous.add(sym);
428
+ }
429
+
430
+ if (ambiguous.size === 0) ['$', '¥', '﷼', 'kr'].forEach((s) => ambiguous.add(s));
431
+
432
+ return ambiguous;
433
+ };
434
+
435
+ const AMBIGUOUS_SYMBOLS: Set<string> = detectAmbiguousSymbols(Array.from(currencyCodesSet));
436
+
437
+ const deriveCurrencySymbol = (code: string, locale = 'en'): string => {
438
+ const formatter = new Intl.NumberFormat(locale, {
439
+ style: 'currency',
440
+ currency: code,
441
+ currencyDisplay: 'narrowSymbol',
442
+ minimumFractionDigits: 0,
443
+ maximumFractionDigits: 0,
444
+ });
445
+ const parts = formatter.formatToParts(1);
446
+ const currencyPart = parts.find((p) => p.type === 'currency');
447
+
448
+ if (currencyPart && currencyPart.value) {
449
+ return AMBIGUOUS_SYMBOLS.has(currencyPart.value) ? code : currencyPart.value;
450
+ }
451
+
452
+ return code;
453
+ };
454
+
455
+ let displayNames: { of(code: string): string | undefined } | null = null;
456
+
457
+ try {
458
+ const dn = (
459
+ Intl as unknown as { DisplayNames?: new (l: string[], o: { type: string }) => { of(code: string): string } }
460
+ ).DisplayNames;
461
+
462
+ if (dn) displayNames = new dn(['en'], { type: 'currency' });
463
+ } catch {
464
+ displayNames = null;
465
+ }
466
+
467
+ // Build UNSORTED_CURRENCY_OPTIONS now that we have supporting data & helpers.
468
+ const UNSORTED_CURRENCY_OPTIONS: CurrencyOption[] = Array.from(currencyCodesSet).map((code) => {
469
+ let displayName: string | null | undefined;
470
+
471
+ try {
472
+ displayName = displayNames ? displayNames.of(code) : null;
473
+ } catch {
474
+ displayName = null;
475
+ }
476
+
477
+ const fallbackName = currencyCodeToName.get(code) || code;
478
+ const name = typeof displayName === 'string' ? displayName : fallbackName;
479
+ const symbol = deriveCurrencySymbol(code);
480
+
481
+ return {
482
+ text: `${name} (${code})`,
483
+ value: code,
484
+ currency: code,
485
+ symbol,
486
+ };
487
+ });
488
+
489
+ const currencyOptions: CurrencyOption[] = [...UNSORTED_CURRENCY_OPTIONS].sort((a, b) =>
490
+ a.text.localeCompare(b.text, 'en', { sensitivity: 'base' }),
491
+ );
492
+
493
+ // Initialization of selected currency must occur AFTER currencyOptions is defined.
494
+ const initCurrency = () => {
495
+ const fallback = currencyOptions.find((c) => c.value === 'USD') || currencyOptions[0];
496
+
497
+ return currencyOptions.find((c) => c.value === preSelectedCurrency.value) || fallback;
498
+ };
499
+
500
+ const selected = ref(initCurrency());
501
+ // #endregion - Set Currency Options
502
+
503
+ watch(preSelectedCurrency, (code) => {
504
+ if (code) handleSelectedCurrency(code);
505
+ });
506
+
507
+ onMounted(() => {
508
+ handleSelectedCurrency(preSelectedCurrency.value);
509
+ if (modelValue.value && numericValue.value !== null) {
510
+ modelValue.value = formatNumberForBlur(numericValue.value);
511
+ emit('getNumericValue', numericValue.value);
512
+ emit('getSelectedCurrencyMeta', {
513
+ currency: selected.value.currency,
514
+ symbol: selected.value.symbol,
515
+ numericValue: numericValue.value,
516
+ rawValue: rawNumericString.value,
517
+ });
518
+ }
519
+ });
520
+
521
+ return {
522
+ inputCurrencyClasses,
523
+ dropdownId,
524
+ modelValue,
525
+ currencyOptions,
526
+ selected,
527
+ dropdownDisplayText,
528
+ popperState,
529
+ handleCurrencyInput,
530
+ handleBlur,
531
+ handleSelectedCurrency,
532
+ handlePopperState,
533
+ effectiveMinDecimals,
534
+ effectiveMaxDecimals,
535
+ currentLocale,
536
+ rawNumericString,
537
+ };
538
+ };
@@ -97,6 +97,10 @@ export const selectLadderizedPropTypes = {
97
97
  validator: (value: (typeof PLACEMENTS_TYPES)[number]) => PLACEMENTS_TYPES.includes(value),
98
98
  default: 'bottom',
99
99
  },
100
+ distance: {
101
+ type: Number,
102
+ default: 6,
103
+ },
100
104
  triggers: {
101
105
  type: Array as PropType<(typeof TRIGGER_EVENTS)[number][]>,
102
106
  validator: (value: (typeof TRIGGER_EVENTS)[number][]) => {
@@ -113,7 +117,7 @@ export const selectLadderizedPropTypes = {
113
117
  },
114
118
  autoHide: {
115
119
  type: Boolean,
116
- default: true,
120
+ default: false,
117
121
  },
118
122
  wrapperPosition: {
119
123
  type: String,
@@ -10,7 +10,7 @@
10
10
  <Menu
11
11
  v-model:shown="ladderizedSelectPopperState"
12
12
  aria-id="ladderized-select-wrapper"
13
- distance="4"
13
+ :distance="props.distance"
14
14
  :placement="props.placement"
15
15
  :triggers="props.triggers"
16
16
  :popper-triggers="props.popperTriggers"
@@ -76,6 +76,10 @@ export const multiSelectPropTypes = {
76
76
  validator: (value: (typeof PLACEMENTS_TYPES)[number]) => PLACEMENTS_TYPES.includes(value),
77
77
  default: 'bottom',
78
78
  },
79
+ distance: {
80
+ type: Number,
81
+ default: 6,
82
+ },
79
83
  triggers: {
80
84
  type: Array as PropType<(typeof TRIGGER_EVENTS)[number][]>,
81
85
  validator: (value: (typeof TRIGGER_EVENTS)[number][]) => {
@@ -10,7 +10,7 @@
10
10
  <Menu
11
11
  v-model:shown="multiSelectPopperState"
12
12
  aria-id="multi-select-wrapper"
13
- distance="4"
13
+ :distance="props.distance"
14
14
  :placement="props.placement"
15
15
  :triggers="props.triggers"
16
16
  :popper-triggers="props.popperTriggers"
@@ -74,12 +74,16 @@ export const selectPropTypes = {
74
74
  validator: (value: (typeof PLACEMENTS_TYPES)[number]) => PLACEMENTS_TYPES.includes(value),
75
75
  default: 'bottom',
76
76
  },
77
+ distance: {
78
+ type: Number,
79
+ default: 6,
80
+ },
77
81
  triggers: {
78
82
  type: Array as PropType<(typeof TRIGGER_EVENTS)[number][]>,
79
83
  validator: (value: (typeof TRIGGER_EVENTS)[number][]) => {
80
84
  return value.every((val) => TRIGGER_EVENTS.includes(val));
81
85
  },
82
- default: () => ['click'],
86
+ default: () => [],
83
87
  },
84
88
  popperTriggers: {
85
89
  type: Array as PropType<(typeof TRIGGER_EVENTS)[number][]>,
@@ -90,7 +94,7 @@ export const selectPropTypes = {
90
94
  },
91
95
  autoHide: {
92
96
  type: Boolean,
93
- default: true,
97
+ default: false,
94
98
  },
95
99
  popperStrategy: {
96
100
  type: String,