@signal24/vue-foundation 4.16.1 → 4.17.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.
Files changed (45) hide show
  1. package/demo/components/demo-root.vue +21 -0
  2. package/demo/components/demo-vf-smart-select.vue +28 -0
  3. package/demo/index.html +14 -0
  4. package/demo/index.ts +10 -0
  5. package/demo/vite.config.ts +23 -0
  6. package/dist/demo/components/demo-root.vue.d.ts +2 -0
  7. package/dist/demo/components/demo-vf-smart-select.vue.d.ts +2 -0
  8. package/dist/demo/index.d.ts +1 -0
  9. package/dist/demo/vite.config.d.ts +2 -0
  10. package/dist/src/components/index.d.ts +5 -5
  11. package/dist/src/components/overlay-anchor.vue.d.ts +6 -8
  12. package/dist/src/components/overlay-container.d.ts +1 -1
  13. package/dist/src/components/toast-helpers.d.ts +1 -1
  14. package/dist/src/components/vf-ajax-select.vue.d.ts +26 -0
  15. package/dist/src/components/{alert-modal.vue.d.ts → vf-alert-modal.vue.d.ts} +1 -1
  16. package/dist/src/components/{ez-smart-select.vue.d.ts → vf-ez-smart-select.vue.d.ts} +14 -6
  17. package/dist/src/components/{modal.vue.d.ts → vf-modal.vue.d.ts} +9 -11
  18. package/dist/src/components/vf-smart-select.vue.d.ts +47 -0
  19. package/dist/src/components/{toast.vue.d.ts → vf-toast.vue.d.ts} +1 -1
  20. package/dist/vue-foundation.es.js +828 -894
  21. package/eslint.config.mjs +67 -0
  22. package/package.json +14 -12
  23. package/src/components/alert-helpers.ts +1 -1
  24. package/src/components/index.ts +5 -5
  25. package/src/components/overlay-container.ts +5 -1
  26. package/src/components/toast-helpers.ts +1 -1
  27. package/src/components/vf-ajax-select.vue +61 -0
  28. package/src/components/{alert-modal.vue → vf-alert-modal.vue} +5 -5
  29. package/src/components/{ez-smart-select.vue → vf-ez-smart-select.vue} +12 -8
  30. package/src/components/{modal.vue → vf-modal.vue} +3 -3
  31. package/src/components/vf-smart-select.vue +587 -0
  32. package/src/directives/duration.ts +3 -3
  33. package/src/filters/index.ts +1 -0
  34. package/src/helpers/array.ts +1 -0
  35. package/src/helpers/error.ts +4 -0
  36. package/src/helpers/object.ts +1 -0
  37. package/tsconfig.app.json +1 -1
  38. package/tsconfig.node.json +1 -1
  39. package/tsconfig.vitest.json +2 -2
  40. package/.eslintrc.cjs +0 -35
  41. package/dist/src/components/ajax-select.vue.d.ts +0 -19
  42. package/dist/src/components/smart-select.vue.d.ts +0 -115
  43. package/src/components/ajax-select.vue +0 -75
  44. package/src/components/smart-select.vue +0 -609
  45. /package/src/components/{toast.vue → vf-toast.vue} +0 -0
@@ -1,609 +0,0 @@
1
- <template>
2
- <div class="vf-smart-select" :class="{ disabled: effectiveDisabled, open: shouldDisplayOptions }">
3
- <input
4
- v-model="searchText"
5
- ref="searchField"
6
- type="text"
7
- :class="{ nullable: !!nullTitle }"
8
- @keydown="handleKeyDown"
9
- :placeholder="effectivePlaceholder"
10
- v-disabled="effectiveDisabled"
11
- @focus="handleInputFocused"
12
- @blur="handleInputBlurred"
13
- :required="required"
14
- data-1p-ignore
15
- />
16
- <div v-if="shouldDisplayOptions" ref="optionsContainer" class="vf-smart-select-options">
17
- <div v-if="!isLoaded" class="no-results">Loading...</div>
18
- <template v-else>
19
- <div
20
- v-for="option in effectiveOptions"
21
- :key="String(option.key)"
22
- class="option"
23
- :class="{
24
- highlighted: highlightedOptionKey === option.key
25
- }"
26
- @mousemove="handleOptionHover(option)"
27
- @mousedown="selectOption(option)"
28
- >
29
- <div class="title" v-html="option.title" />
30
- <div v-if="option.subtitle" class="subtitle" v-html="option.subtitle" />
31
- </div>
32
- <div v-if="!effectiveOptions.length && searchText" class="no-results">
33
- {{ effectiveNoResultsText }}
34
- </div>
35
- </template>
36
- </div>
37
- </div>
38
- </template>
39
-
40
- <script lang="ts">
41
- import { debounce, isEqual } from 'lodash';
42
- import type { PropType } from 'vue';
43
-
44
- import { escapeHtml } from '../helpers/string';
45
-
46
- const NullSymbol = Symbol('null');
47
- const CreateSymbol = Symbol('create');
48
-
49
- const VALID_KEYS = `\`1234567890-=[]\\;',./~!@#$%^&*()_+{}|:"<>?qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM`;
50
-
51
- // todo: make type safe when Vue alpha is released
52
-
53
- export type GenericObject = { [key: string]: any };
54
- export interface OptionDescriptor {
55
- key: string | Symbol;
56
- title: string;
57
- subtitle?: string | null;
58
- searchContent?: string;
59
- ref?: GenericObject;
60
- }
61
-
62
- export default {
63
- props: {
64
- modelValue: {
65
- type: null as unknown as PropType<any>,
66
- default: null
67
- },
68
- loadOptions: Function as PropType<(searchText: string | null) => Promise<GenericObject[]>>,
69
- options: Object as PropType<GenericObject[]>,
70
- prependOptions: Object as PropType<GenericObject[]>,
71
- appendOptions: Object as PropType<GenericObject[]>,
72
- onCreateItem: Function as PropType<(searchText: string) => void>,
73
- preload: Boolean as PropType<boolean>,
74
- remoteSearch: Boolean as PropType<boolean>,
75
- searchFields: Object as PropType<string[]>,
76
- placeholder: String as PropType<string>,
77
- keyExtractor: Function as PropType<(option: any) => string | symbol>,
78
- valueExtractor: Function as PropType<(option: any) => any>,
79
- formatter: {
80
- type: Function as PropType<(option: any) => string>,
81
- required: true
82
- },
83
- subtitleFormatter: Function as PropType<(option: any) => string>,
84
- nullTitle: String as PropType<string>,
85
- noResultsText: String as PropType<string>,
86
- disabled: Boolean as PropType<boolean>,
87
- optionsListId: String as PropType<string>,
88
- debug: Boolean as PropType<boolean>,
89
- required: Boolean as PropType<boolean>
90
- },
91
-
92
- emits: {
93
- optionsLoaded: Object as (options: any[]) => void,
94
- 'update:modelValue': Object as (value: any) => void
95
- },
96
-
97
- data() {
98
- return {
99
- isLoading: false,
100
- isLoaded: false,
101
- loadedOptions: [],
102
- isSearching: false,
103
- searchText: '',
104
- selectedOption: null,
105
- selectedOptionTitle: null,
106
- shouldDisplayOptions: false,
107
- highlightedOptionKey: null,
108
- shouldShowCreateOption: false
109
- } as {
110
- isLoading: boolean;
111
- isLoaded: boolean;
112
- loadedOptions: GenericObject[];
113
- isSearching: boolean;
114
- searchText: string;
115
- selectedOption: GenericObject | null;
116
- selectedOptionTitle: string | null;
117
- shouldDisplayOptions: boolean;
118
- highlightedOptionKey: string | Symbol | null;
119
- shouldShowCreateOption: boolean;
120
- };
121
- },
122
-
123
- computed: {
124
- /**
125
- * EFFECTIVE PROPS
126
- */
127
- effectivePrependOptions() {
128
- return this.prependOptions ?? [];
129
- },
130
-
131
- effectiveAppendOptions() {
132
- return this.appendOptions ?? [];
133
- },
134
-
135
- effectiveDisabled() {
136
- return !!this.disabled; // there was another condition here but it didn't make sense
137
- },
138
-
139
- effectivePlaceholder() {
140
- if (!this.isLoaded && this.preload) return 'Loading...';
141
- if (this.nullTitle) return this.nullTitle;
142
- return this.placeholder || '';
143
- },
144
-
145
- effectiveNoResultsText() {
146
- return this.noResultsText || 'No options match your search.';
147
- },
148
-
149
- effectiveKeyExtractor() {
150
- return this.keyExtractor ?? this.valueExtractor;
151
- },
152
-
153
- /**
154
- * OPTIONS GENERATION
155
- */
156
-
157
- allOptions() {
158
- return [...this.effectivePrependOptions, ...this.loadedOptions, ...this.effectiveAppendOptions];
159
- },
160
-
161
- optionsDescriptors() {
162
- return this.allOptions.map((option, index) => {
163
- const title = this.formatter!(option);
164
- const subtitle = this.subtitleFormatter?.(option);
165
- const strippedTitle = title ? title.trim().toLowerCase() : '';
166
- const strippedSubtitle = subtitle ? subtitle.trim().toLowerCase() : '';
167
-
168
- const searchContent = [];
169
- if (this.searchFields) {
170
- this.searchFields.forEach(field => {
171
- option[field] && searchContent.push(String(option[field]).toLowerCase());
172
- });
173
- } else {
174
- searchContent.push(strippedTitle);
175
- strippedSubtitle && searchContent.push(strippedSubtitle);
176
- }
177
-
178
- return {
179
- // eslint-disable-next-line vue/no-use-computed-property-like-method
180
- key: this.effectiveKeyExtractor?.(option) ?? String(index),
181
- title,
182
- subtitle,
183
- searchContent: searchContent.join(''),
184
- ref: option
185
- } as OptionDescriptor;
186
- });
187
- },
188
-
189
- effectiveOptions() {
190
- let options = [...this.optionsDescriptors];
191
-
192
- if (this.isSearching) {
193
- const strippedSearchText = this.searchText.trim().toLowerCase();
194
-
195
- if (strippedSearchText.length) {
196
- options = options.filter(option => option.searchContent!.includes(strippedSearchText));
197
-
198
- const escapedSearchText = escapeHtml(this.searchText).replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&');
199
- const searchRe = new RegExp(`(${escapedSearchText})`, 'ig');
200
-
201
- options = options.map(option => ({
202
- ...option,
203
- title: option.title.replace(searchRe, '<mark>$1</mark>'),
204
- subtitle: option.subtitle?.replace(searchRe, '<mark>$1</mark>')
205
- }));
206
-
207
- if (this.shouldShowCreateOption) {
208
- const hasExactMatch = options.find(option => option.searchContent === strippedSearchText) !== undefined;
209
- if (!hasExactMatch) {
210
- options.push({
211
- key: CreateSymbol,
212
- title: 'Create <strong>' + this.searchText.trim() + '</strong>...'
213
- });
214
- }
215
- }
216
- }
217
- } else if (this.nullTitle) {
218
- options.unshift({
219
- key: NullSymbol,
220
- title: this.nullTitle
221
- });
222
- }
223
-
224
- return options;
225
- }
226
- },
227
-
228
- watch: {
229
- // props
230
-
231
- modelValue() {
232
- this.handleValueChanged();
233
- },
234
-
235
- options() {
236
- this.loadedOptions = this.options ?? [];
237
- this.isLoaded = true;
238
- },
239
-
240
- // data
241
-
242
- optionsDescriptors() {
243
- if (this.shouldDisplayOptions) {
244
- setTimeout(this.highlightInitialOption, 0);
245
- }
246
- },
247
-
248
- searchText() {
249
- // don't disable searching here if it's remote search, as that will need to be done after the fetch
250
- if (this.isSearching && !this.remoteSearch && !this.searchText.trim().length) {
251
- this.isSearching = false;
252
- }
253
- },
254
-
255
- shouldDisplayOptions() {
256
- if (this.shouldDisplayOptions) {
257
- setTimeout(this.handleOptionsDisplayed, 0);
258
- } else {
259
- this.isSearching = false;
260
- this.searchText = this.selectedOptionTitle || '';
261
-
262
- if (this.$refs.optionsContainer) {
263
- (this.$refs.optionsContainer as HTMLElement).style.visibility = 'hidden';
264
- }
265
- }
266
- },
267
-
268
- effectiveOptions() {
269
- if (this.modelValue && !this.selectedOption) {
270
- this.handleValueChanged();
271
- }
272
-
273
- if (this.highlightedOptionKey && !this.effectiveOptions.find(option => option.key == this.highlightedOptionKey)) {
274
- this.highlightedOptionKey = this.effectiveOptions[0]?.key ?? NullSymbol;
275
- }
276
- }
277
- },
278
-
279
- async mounted() {
280
- this.shouldShowCreateOption = this.onCreateItem !== undefined;
281
-
282
- if (this.options) {
283
- this.loadedOptions = this.options;
284
- this.isLoaded = true;
285
- } else if (this.preload) {
286
- await this.loadRemoteOptions();
287
- }
288
-
289
- this.handleValueChanged();
290
-
291
- this.$watch('selectedOption', () => {
292
- if (this.selectedOption !== this.modelValue) {
293
- this.$emit(
294
- 'update:modelValue',
295
- this.selectedOption && this.valueExtractor ? this.valueExtractor(this.selectedOption) : this.selectedOption
296
- );
297
- }
298
- });
299
-
300
- if (this.remoteSearch) {
301
- this.$watch('searchText', debounce(this.reloadOptionsIfSearching, 250));
302
- }
303
- },
304
-
305
- methods: {
306
- async loadRemoteOptions() {
307
- await this.reloadOptions();
308
- this.loadedOptions && this.$emit('optionsLoaded', this.loadedOptions);
309
- },
310
-
311
- async reloadOptions() {
312
- const searchText = this.remoteSearch && this.isSearching && this.searchText ? this.searchText : null;
313
- this.isLoading = true;
314
- this.loadedOptions = (await this.loadOptions?.(searchText)) ?? [];
315
- this.isLoading = false;
316
- this.isLoaded = true;
317
- },
318
-
319
- reloadOptionsIfSearching() {
320
- if (this.isSearching) {
321
- this.reloadOptions();
322
- this.isSearching = this.searchText.trim().length > 0;
323
- }
324
- },
325
-
326
- handleKeyDown(e: KeyboardEvent) {
327
- if (e.key == 'Escape') {
328
- e.stopPropagation();
329
- (e.target as any).blur();
330
- return;
331
- }
332
-
333
- if (e.key == 'ArrowLeft' || e.key == 'ArrowRight') return;
334
- if (e.key == 'Tab') return;
335
-
336
- if (!this.isLoaded) {
337
- this.isSearching || e.preventDefault();
338
- return;
339
- }
340
-
341
- if (e.key == 'ArrowUp' || e.key == 'ArrowDown') {
342
- e.preventDefault();
343
- return this.incrementHighlightedOption(e.key == 'ArrowUp' ? -1 : 1);
344
- }
345
-
346
- if (e.key == 'PageUp' || e.key == 'PageDown') {
347
- e.preventDefault();
348
- return this.incrementHighlightedOption(e.key == 'PageUp' ? -10 : 10);
349
- }
350
-
351
- if (e.key == 'Home' || e.key == 'End') {
352
- e.preventDefault();
353
- return this.incrementHighlightedOption(e.key == 'Home' ? -Number.MAX_SAFE_INTEGER : Number.MAX_SAFE_INTEGER);
354
- }
355
-
356
- if (e.key == 'Enter') {
357
- e.preventDefault();
358
- const highlightedOption = this.effectiveOptions.find(option => option.key == this.highlightedOptionKey);
359
- if (highlightedOption) return this.selectOption(highlightedOption);
360
- }
361
-
362
- if (e.key === 'Delete' || e.key === 'Backspace') {
363
- if (this.searchText.length > 1) {
364
- this.isSearching = true;
365
- }
366
- return;
367
- }
368
-
369
- if (!e.metaKey && VALID_KEYS.includes(e.key)) {
370
- this.isSearching = true;
371
- }
372
- },
373
-
374
- handleInputFocused() {
375
- this.setHighlightedOptionKey();
376
- this.shouldDisplayOptions = true;
377
- },
378
-
379
- setHighlightedOptionKey(useFirstItemAsFallback?: boolean) {
380
- if (this.selectedOption) {
381
- this.highlightedOptionKey = this.getOptionKey(this.selectedOption);
382
- } else if (useFirstItemAsFallback) {
383
- this.highlightedOptionKey = this.effectiveOptions?.[0].key ?? NullSymbol;
384
- } else if (this.nullTitle) {
385
- this.highlightedOptionKey = NullSymbol;
386
- }
387
- },
388
-
389
- getOptionKey(option: GenericObject): string | Symbol {
390
- if (this.effectiveKeyExtractor) {
391
- // eslint-disable-next-line vue/no-use-computed-property-like-method
392
- return this.effectiveKeyExtractor(this.selectedOption)!;
393
- }
394
-
395
- return this.getOptionDescriptor(option)?.key ?? '';
396
- },
397
-
398
- getOptionDescriptor(option: GenericObject) {
399
- const matchedRef = this.effectiveOptions.find(o => o.ref === option);
400
- if (matchedRef) {
401
- return matchedRef;
402
- }
403
-
404
- // didn't find an object match, so we'll try a content match. a couple reasons:
405
- // 1) the initial selection may have come from an owning object and the object as a whole may differ from the full content list
406
- // 2) for reasons I've yet to determine, the prepend options, although they are wrapped by proxies and have identical content,
407
- // are not the same proxy object as selectedOption once assigned -- even though the loaded data *is* the same. I've tried
408
- // setting them as reactive using the same method (via data props rather than computed) and it didn't change anything.
409
- // therefore, falling back to an isEqual check here when there's no equal object
410
- const matcher = this.keyExtractor ? (a: GenericObject, b: GenericObject) => this.keyExtractor!(a) === this.keyExtractor!(b) : isEqual;
411
- const matchedObj = this.effectiveOptions.find(o => matcher(o.ref!, option));
412
- if (matchedObj) {
413
- return matchedObj;
414
- }
415
-
416
- return null;
417
- },
418
-
419
- handleInputBlurred() {
420
- if (this.debug) return;
421
-
422
- if (!this.searchText.length && this.nullTitle) {
423
- this.selectedOption = null;
424
- this.selectedOptionTitle = null;
425
- }
426
-
427
- this.shouldDisplayOptions = false;
428
- },
429
-
430
- handleOptionsDisplayed() {
431
- this.isLoaded || this.loadRemoteOptions();
432
- this.optionsListId && (this.$refs.optionsContainer as HTMLElement).setAttribute('id', this.optionsListId);
433
- this.teleportOptionsContainer();
434
- },
435
-
436
- teleportOptionsContainer() {
437
- const elRect = this.$el.getBoundingClientRect();
438
- const targetTop = elRect.y + elRect.height + 2;
439
- const targetLeft = elRect.x;
440
-
441
- const optionsEl = this.$refs.optionsContainer as HTMLElement;
442
- const styles = window.getComputedStyle(this.$el);
443
-
444
- for (let key in styles) {
445
- if (!/^(font|text)/.test(key)) continue;
446
- optionsEl.style[key] = styles[key];
447
- }
448
-
449
- optionsEl.style.top = targetTop + 'px';
450
- optionsEl.style.left = targetLeft + 'px';
451
- optionsEl.style.minWidth = elRect.width + 'px';
452
-
453
- if (!styles.maxHeight || styles.maxHeight == 'none') {
454
- const maxHeight = window.innerHeight - targetTop - 12;
455
- optionsEl.style.maxHeight = maxHeight + 'px';
456
- }
457
-
458
- optionsEl.style.visibility = 'visible';
459
-
460
- document.body.appendChild(optionsEl);
461
-
462
- setTimeout(this.highlightInitialOption, 0);
463
- },
464
-
465
- highlightInitialOption() {
466
- if (!this.isLoaded) return;
467
- if (!this.highlightedOptionKey) return;
468
- const highlightedOptionIdx = this.effectiveOptions.findIndex(option => option.key == this.highlightedOptionKey);
469
- const containerEl = this.$refs.optionsContainer as HTMLElement;
470
- const highlightedOptionEl = containerEl.querySelectorAll('.option')[highlightedOptionIdx] as HTMLElement;
471
- containerEl.scrollTop = highlightedOptionEl.offsetTop;
472
- },
473
-
474
- handleOptionHover(option: OptionDescriptor) {
475
- this.highlightedOptionKey = option ? option.key : null;
476
- },
477
-
478
- incrementHighlightedOption(increment: number) {
479
- const highlightedOptionIdx = this.effectiveOptions.findIndex(option => option.key == this.highlightedOptionKey);
480
- let targetOptionIdx = highlightedOptionIdx + increment;
481
-
482
- if (targetOptionIdx < 0) targetOptionIdx = 0;
483
- else if (targetOptionIdx >= this.effectiveOptions.length) targetOptionIdx = this.effectiveOptions.length - 1;
484
-
485
- if (highlightedOptionIdx == targetOptionIdx) return;
486
-
487
- this.highlightedOptionKey = this.effectiveOptions[targetOptionIdx].key;
488
-
489
- const containerEl = this.$refs.optionsContainer as HTMLElement;
490
- const targetOptionEl = containerEl.querySelectorAll('.option')[targetOptionIdx] as HTMLElement;
491
-
492
- if (targetOptionEl.offsetTop < containerEl.scrollTop) {
493
- containerEl.scrollTop = targetOptionEl.offsetTop;
494
- } else if (targetOptionEl.offsetTop + targetOptionEl.offsetHeight > containerEl.scrollTop + containerEl.clientHeight) {
495
- containerEl.scrollTop = targetOptionEl.offsetTop + targetOptionEl.offsetHeight - containerEl.clientHeight;
496
- }
497
- },
498
-
499
- selectOption(option: OptionDescriptor) {
500
- this.isSearching = false;
501
-
502
- if (option.key == NullSymbol) {
503
- this.searchText = '';
504
- this.selectedOption = null;
505
- this.selectedOptionTitle = null;
506
- } else if (option.key === CreateSymbol) {
507
- const createText = this.searchText.trim();
508
- this.searchText = '';
509
- this.selectedOption = null;
510
- this.selectedOptionTitle = null;
511
- this.onCreateItem?.(createText);
512
- } else {
513
- const selectedDecoratedOption = this.optionsDescriptors.find(decoratedOption => decoratedOption.key == option.key);
514
- const realOption = selectedDecoratedOption!.ref;
515
- this.selectedOption = realOption!;
516
- this.selectedOptionTitle = this.formatter!(realOption!);
517
- this.searchText = this.selectedOptionTitle || '';
518
- }
519
-
520
- (this.$refs.searchField as HTMLElement).blur();
521
- },
522
-
523
- handleValueChanged() {
524
- if (this.modelValue) {
525
- this.selectedOption = this.valueExtractor ? this.allOptions.find(o => this.modelValue === this.valueExtractor!(o)) : this.modelValue;
526
- this.selectedOptionTitle = this.selectedOption ? this.formatter!(this.selectedOption) : null;
527
- this.searchText = this.selectedOptionTitle || '';
528
- } else {
529
- this.selectedOption = null;
530
- this.selectedOptionTitle = null;
531
- this.searchText = '';
532
- }
533
- },
534
-
535
- addRemoteOption(option: GenericObject) {
536
- this.loadedOptions.unshift(option);
537
- }
538
- }
539
- };
540
- </script>
541
-
542
- <style lang="scss">
543
- .vf-smart-select {
544
- position: relative;
545
-
546
- input {
547
- width: 100%;
548
- padding-right: 24px !important;
549
-
550
- &.nullable::placeholder {
551
- color: #000;
552
- }
553
- }
554
-
555
- &:after {
556
- content: ' ';
557
- display: block;
558
- position: absolute;
559
- top: 50%;
560
- right: 8px;
561
- margin-top: -3px;
562
- width: 0;
563
- height: 0;
564
- border-style: solid;
565
- border-width: 5px 5px 0 5px;
566
- border-color: #333333 transparent transparent transparent;
567
- pointer-events: none;
568
- }
569
-
570
- &.open:after {
571
- margin-top: -4px;
572
- border-width: 0 5px 5px 5px;
573
- border-color: transparent transparent #333333 transparent;
574
- }
575
-
576
- &:not(.disabled) {
577
- input {
578
- cursor: pointer;
579
- }
580
- }
581
-
582
- &.disabled:after {
583
- opacity: 0.4;
584
- }
585
- }
586
-
587
- .vf-smart-select-options {
588
- visibility: hidden;
589
- position: absolute;
590
- min-height: 20px;
591
- border: 1px solid #e8e8e8;
592
- background: white;
593
- overflow: auto;
594
- z-index: 101;
595
-
596
- .option,
597
- .no-results {
598
- padding: 5px 8px;
599
- }
600
-
601
- .option {
602
- cursor: pointer;
603
-
604
- &.highlighted {
605
- background-color: #f5f5f5;
606
- }
607
- }
608
- }
609
- </style>
File without changes