@weni/unnnic-system 1.16.26-develop.0 → 1.16.28

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": "@weni/unnnic-system",
3
- "version": "1.16.26-develop.0",
3
+ "version": "1.16.28",
4
4
  "main": "./dist/unnnic.common.js",
5
5
  "files": [
6
6
  "dist/*",
@@ -0,0 +1,603 @@
1
+ <template>
2
+ <div @keydown="onKeyDownSelect" v-click-outside="onClickOutside" class="unnnic-select-smart">
3
+ <dropdown-skeleton type="manual" :value="active" position="bottom" ref="dropdown-skeleton">
4
+ <text-input
5
+ class="unnnic-select-smart__input"
6
+ ref="selectSmartInput"
7
+ :value="inputValue"
8
+ :placeholder="autocompletePlaceholder || selectedLabel"
9
+ :type="type"
10
+ :size="size"
11
+ :disabled="disabled"
12
+ :readonly="!isAutocompleteAllowed"
13
+ :icon-left="isAutocompleteAllowed && autocompleteIconLeft ? 'search-1' : ''"
14
+ :icon-right="active ? 'arrow-button-up-1' : 'arrow-button-down-1'"
15
+ :icon-right-clickable="!disabled"
16
+ @icon-right-click="handleClickInput"
17
+ @click="handleClickInput"
18
+ @input="searchValue = $event"
19
+ />
20
+
21
+ <template v-slot:inside="props">
22
+ <div
23
+ v-if="active"
24
+ :style="{ width: props.width }"
25
+ :class="{
26
+ 'unnnic-select-smart__options': true,
27
+ active: active,
28
+ inactive: !active,
29
+ }"
30
+ >
31
+ <div :style="{ overflow: 'auto' }">
32
+ <select-smart-multiple-header
33
+ v-if="multiple"
34
+ :selectedOptions="selectedOptions"
35
+ :withoutSelectsMessage="multipleWithoutSelectsMessage"
36
+ @clear-selected-options="clearSelectedOptions"
37
+ @unselect-option="unselectOption"
38
+ />
39
+ <div
40
+ ref="selectSmartOptionsScrollArea"
41
+ :class="[
42
+ 'unnnic-select-smart__options__scroll-area',
43
+ `size-${size}`,
44
+ {
45
+ 'with-descriptions': hasDescriptionOptions,
46
+ },
47
+ ]"
48
+ >
49
+ <select-smart-option
50
+ v-for="(option, index) in filterOptions(options)"
51
+ :key="option.value"
52
+ :label="option.label"
53
+ :description="option.description"
54
+ :tabindex="index"
55
+ :size="size"
56
+ :active="option.value === value || optionIsSelected(option)"
57
+ :focused="focusedOption && focusedOption.value === option.value"
58
+ :allowCheckbox="!!multiple"
59
+ @click="handleSelect(option)"
60
+ />
61
+ <p
62
+ v-if="filterOptions(options).length === 0"
63
+ class="unnnic-select-smart__options--no-results"
64
+ >
65
+ {{ $t('select_smart.without_results') }}
66
+ </p>
67
+ </div>
68
+ </div>
69
+ </div>
70
+ </template>
71
+ </dropdown-skeleton>
72
+ </div>
73
+ </template>
74
+
75
+ <script>
76
+ import vClickOutside from 'v-click-outside';
77
+ import SelectSmartOption from './SelectSmartOption.vue';
78
+ import SelectSmartMultipleHeader from './SelectSmartMultipleHeader.vue';
79
+ import TextInput from '../Input/TextInput.vue';
80
+ import DropdownSkeleton from '../Dropdown/DropdownSkeleton.vue';
81
+
82
+ export default {
83
+ name: 'UnnnicSelectSmart',
84
+ components: {
85
+ TextInput,
86
+ SelectSmartOption,
87
+ SelectSmartMultipleHeader,
88
+ DropdownSkeleton,
89
+ },
90
+ props: {
91
+ options: {
92
+ type: Array,
93
+ required: true,
94
+ default: () => [
95
+ {
96
+ value: '',
97
+ label: 'Initial value',
98
+ },
99
+ {
100
+ value: 'option1',
101
+ label: 'Option1',
102
+ },
103
+ ],
104
+ },
105
+ value: {
106
+ type: null,
107
+ },
108
+ size: {
109
+ type: String,
110
+ default: 'md',
111
+ },
112
+ type: {
113
+ type: String,
114
+ default: 'normal',
115
+ validator(value) {
116
+ return ['normal', 'error'].indexOf(value) !== -1;
117
+ },
118
+ },
119
+ disabled: {
120
+ type: Boolean,
121
+ default: false,
122
+ },
123
+ multiple: {
124
+ type: Boolean,
125
+ default: false,
126
+ },
127
+ multipleWithoutSelectsMessage: {
128
+ type: String,
129
+ default: '',
130
+ },
131
+ autocomplete: {
132
+ type: Boolean,
133
+ default: false,
134
+ },
135
+ autocompleteIconLeft: {
136
+ type: Boolean,
137
+ default: false,
138
+ },
139
+ autocompleteClearOnFocus: {
140
+ type: Boolean,
141
+ default: false,
142
+ },
143
+ },
144
+
145
+ data() {
146
+ return {
147
+ active: false,
148
+ status: 'not-mounted',
149
+ focusedOption: null,
150
+
151
+ searchValue: '',
152
+ isAutocompleteAllowed: false,
153
+
154
+ selectedOptions: [],
155
+ multipleSelectedsTags: 2,
156
+ };
157
+ },
158
+
159
+ mounted() {
160
+ this.status = 'mounted';
161
+
162
+ if (this.multiple || this.autocomplete) {
163
+ // The "multiple" variation only exists with autocomplete, so it can't be false
164
+ this.isAutocompleteAllowed = true;
165
+ }
166
+
167
+ if (this.options[0].value) {
168
+ this.selectOption(this.options[0]);
169
+ }
170
+ },
171
+
172
+ watch: {
173
+ active(newValue) {
174
+ this.$nextTick(() => {
175
+ this.$refs['dropdown-skeleton'].calculatePosition();
176
+
177
+ if (newValue && !this.multiple) {
178
+ const activeOptionIndex = this.getOptionIndex('active');
179
+
180
+ if (activeOptionIndex !== -1) {
181
+ this.scrollToOption(activeOptionIndex, 'center');
182
+ }
183
+ }
184
+ });
185
+ },
186
+
187
+ searchValue() {
188
+ this.$nextTick(() => {
189
+ this.$refs['dropdown-skeleton'].calculatePosition();
190
+ });
191
+
192
+ this.focusedOption = null;
193
+
194
+ if (!this.active) this.active = true;
195
+ },
196
+
197
+ selectedOptions(newSelectedOptions) {
198
+ this.$emit('onChange', newSelectedOptions);
199
+ this.$emit('input', newSelectedOptions);
200
+
201
+ this.onSelectOption(newSelectedOptions.at(-1));
202
+ },
203
+
204
+ autocomplete(newAutocomplete) {
205
+ if (!this.multiple) {
206
+ // The "multiple" variation only exists with autocomplete, so it can't be false
207
+ this.isAutocompleteAllowed = newAutocomplete;
208
+ }
209
+ },
210
+ },
211
+
212
+ computed: {
213
+ selectedLabel() {
214
+ if (this.status !== 'mounted') {
215
+ return '';
216
+ }
217
+
218
+ const selected = this.options.find((option) => {
219
+ const isValueMatch = option.value === this.value;
220
+
221
+ if (this.isAutocompleteAllowed) {
222
+ return isValueMatch && option.value !== '';
223
+ }
224
+
225
+ const isEmptyOption = option.value === '' && this.value == null;
226
+ return isEmptyOption || isValueMatch;
227
+ });
228
+
229
+ if (selected) {
230
+ return selected.label;
231
+ }
232
+
233
+ return this.multiple ? '' : this.selectedOptions.at(-1)?.label || '';
234
+ },
235
+
236
+ hasDescriptionOptions() {
237
+ return this.options.some((item) => typeof item.description !== 'undefined');
238
+ },
239
+
240
+ autocompletePlaceholder() {
241
+ if (this.isAutocompleteAllowed) {
242
+ const selected = this.options.find((option) => option.value === '');
243
+
244
+ if (selected) {
245
+ return selected.label;
246
+ }
247
+ }
248
+ return '';
249
+ },
250
+
251
+ inputValue() {
252
+ const {
253
+ isAutocompleteAllowed, searchValue, multiple, selectedOptions,
254
+ } = this;
255
+
256
+ if (isAutocompleteAllowed || multiple) {
257
+ return searchValue;
258
+ }
259
+ if (!multiple && selectedOptions.length !== 0) {
260
+ return selectedOptions[0].label;
261
+ }
262
+
263
+ return '';
264
+ },
265
+ },
266
+
267
+ directives: {
268
+ clickOutside: vClickOutside.directive,
269
+ },
270
+
271
+ methods: {
272
+ optionIsSelected(option) {
273
+ return this.selectedOptions.some((selectedOption) => selectedOption.value === option.value);
274
+ },
275
+
276
+ clearSelectedOptions() {
277
+ this.selectedOptions = [];
278
+ },
279
+
280
+ handleSelect(option) {
281
+ if (option) {
282
+ if (this.multiple && this.optionIsSelected(option)) {
283
+ this.unselectOption(option);
284
+ return;
285
+ }
286
+
287
+ this.selectOption(option);
288
+ }
289
+ },
290
+
291
+ handleClickInput() {
292
+ if (this.isAutocompleteAllowed) {
293
+ if (this.active) {
294
+ return;
295
+ }
296
+ if (this.autocompleteClearOnFocus) {
297
+ this.searchValue = '';
298
+ }
299
+ }
300
+
301
+ this.active = !this.active;
302
+ },
303
+
304
+ filterOptions(options) {
305
+ const searchValue = this.searchValue.toLowerCase();
306
+ const searchTerms = searchValue
307
+ .normalize('NFD')
308
+ .replace(/[\u0300-\u036f]/g, '')
309
+ .split(' ');
310
+
311
+ const normalizeLabel = (label) => label
312
+ .toString()
313
+ .toLowerCase()
314
+ .normalize('NFD')
315
+ .replace(/[\u0300-\u036f]/g, '');
316
+
317
+ const getNumber = (str) => parseInt(str.match(/\d+/)?.[0], 10) || 0;
318
+
319
+ const filteredOptions = options.filter(({ value, label }, index, self) => {
320
+ const labelWithoutAccents = normalizeLabel(label);
321
+ const isValueUnique = self.findIndex((option) => option.value === value) === index;
322
+ const matchesSearchTerms = searchTerms.every((term) => labelWithoutAccents.includes(term));
323
+
324
+ return isValueUnique && matchesSearchTerms && value;
325
+ });
326
+
327
+ const sortedOptions = filteredOptions.sort((a, b) => {
328
+ const labelA = normalizeLabel(a.label);
329
+ const labelB = normalizeLabel(b.label);
330
+
331
+ const numberA = getNumber(labelA);
332
+ const numberB = getNumber(labelB);
333
+
334
+ return numberA - numberB || labelA.localeCompare(labelB);
335
+ });
336
+
337
+ return sortedOptions;
338
+ },
339
+
340
+ onClickOutside() {
341
+ if (this.active) {
342
+ if (this.isAutocompleteAllowed) {
343
+ this.searchValue = this.selectedLabel;
344
+ }
345
+ this.$nextTick(() => {
346
+ this.active = false;
347
+ });
348
+ }
349
+ },
350
+
351
+ getOptionIndex(type) {
352
+ const options = this.filterOptions(this.options);
353
+ let valueByType = '';
354
+ if (type === 'active') {
355
+ valueByType = this.value?.[0]?.value;
356
+ }
357
+ if (type === 'focused') {
358
+ valueByType = this.focusedOption?.value || this.selectedOptions.at(-1)?.value;
359
+ }
360
+ return options.findIndex((option) => option.value === valueByType);
361
+ },
362
+
363
+ scrollToOption(optionIndex, scrollBlock = 'nearest') {
364
+ const elementScroll = this.$refs.selectSmartOptionsScrollArea;
365
+
366
+ if (elementScroll && optionIndex >= 0 && optionIndex < elementScroll.childNodes.length) {
367
+ const optionElement = elementScroll.childNodes[optionIndex];
368
+
369
+ if (optionElement instanceof HTMLElement) {
370
+ optionElement.scrollIntoView({ block: scrollBlock });
371
+ }
372
+ }
373
+ },
374
+
375
+ selectOption(option) {
376
+ const selectedOption = option.value === null || option.value.length === 0 ? null : option;
377
+
378
+ this.selectedOptions = this.multiple
379
+ ? [...this.selectedOptions, selectedOption]
380
+ : [selectedOption];
381
+ },
382
+
383
+ unselectOption(option) {
384
+ const indexToRemove = this.selectedOptions.findIndex(
385
+ (selectedOption) => selectedOption === option,
386
+ );
387
+
388
+ if (indexToRemove !== -1) {
389
+ this.selectedOptions.splice(indexToRemove, 1);
390
+ }
391
+
392
+ if (this.multiple) {
393
+ this.searchValue = '';
394
+ }
395
+ },
396
+
397
+ onSelectOption(newOption) {
398
+ this.$nextTick(() => {
399
+ if (!this.multiple) {
400
+ this.active = false;
401
+ }
402
+ });
403
+
404
+ if (this.isAutocompleteAllowed && !this.multiple) {
405
+ this.searchValue = newOption.label;
406
+ return;
407
+ }
408
+
409
+ if (this.multiple) {
410
+ this.searchValue = '';
411
+ }
412
+ },
413
+
414
+ async onKeyDownSelect(event) {
415
+ const { key } = event;
416
+
417
+ const validKeys = ['Escape', 'Enter', 'ArrowUp', 'ArrowDown'];
418
+
419
+ if (validKeys.includes(key)) {
420
+ event.preventDefault();
421
+
422
+ const options = this.filterOptions(this.options);
423
+ const focusedOptionIndex = this.getOptionIndex('focused');
424
+ let newIndex;
425
+
426
+ // eslint-disable-next-line default-case
427
+ switch (key) {
428
+ case 'Escape':
429
+ this.active = false;
430
+ this.searchValue = '';
431
+ break;
432
+ case 'Enter':
433
+ if (this.focusedOption && this.active) {
434
+ this.handleSelect(this.focusedOption);
435
+ }
436
+ if (!this.active) {
437
+ this.active = true;
438
+ }
439
+ break;
440
+
441
+ case 'ArrowUp':
442
+ case 'ArrowDown':
443
+ if (this.multiple && !this.active) {
444
+ this.active = true;
445
+ await this.$nextTick();
446
+ }
447
+ newIndex = key === 'ArrowUp'
448
+ ? Math.max(focusedOptionIndex - 1, 0)
449
+ : Math.min(focusedOptionIndex + 1, options.length - 1);
450
+ if (!this.active) {
451
+ this.handleSelect(options[newIndex]);
452
+ }
453
+ break;
454
+ }
455
+
456
+ if (newIndex !== undefined && this.active) {
457
+ this.scrollToOption(newIndex);
458
+ }
459
+
460
+ const newOption = options[newIndex === undefined ? focusedOptionIndex : newIndex];
461
+ this.focusedOption = newOption;
462
+ }
463
+ },
464
+ },
465
+ };
466
+ </script>
467
+
468
+ <style lang="scss">
469
+ @import '../../assets/scss/unnnic.scss';
470
+ .unnnic-select-smart {
471
+ position: relative;
472
+
473
+ font-family: $unnnic-font-family-secondary;
474
+
475
+ cursor: pointer;
476
+
477
+ &__options {
478
+ left: 0;
479
+ right: 0;
480
+
481
+ margin-top: 2px;
482
+
483
+ border-radius: $unnnic-border-radius-sm;
484
+
485
+ box-shadow: $unnnic-shadow-level-near;
486
+
487
+ background-color: $unnnic-color-background-snow;
488
+
489
+ cursor: default;
490
+
491
+ &__scroll-area {
492
+ @function calc-max-height($value) {
493
+ @return ($value * $unnnic-font-size) - ($unnnic-spacing-xs * 2);
494
+ }
495
+
496
+ margin: $unnnic-spacing-xs;
497
+ margin-right: $unnnic-inline-xs;
498
+ padding-right: $unnnic-inline-xs;
499
+
500
+ max-height: calc-max-height(8.5);
501
+
502
+ overflow-y: auto;
503
+
504
+ &::-webkit-scrollbar {
505
+ width: $unnnic-spacing-inline-nano;
506
+ }
507
+
508
+ &::-webkit-scrollbar-thumb {
509
+ background: $unnnic-color-neutral-cleanest;
510
+ border-radius: $unnnic-border-radius-pill;
511
+ }
512
+
513
+ &::-webkit-scrollbar-track {
514
+ background: $unnnic-color-neutral-soft;
515
+ border-radius: $unnnic-border-radius-pill;
516
+ }
517
+
518
+ &.with-descriptions {
519
+ max-height: calc-max-height(13.5);
520
+ }
521
+
522
+ &.size-sm {
523
+ max-height: calc-max-height(8);
524
+
525
+ &.with-descriptions {
526
+ max-height: calc-max-height(12);
527
+ }
528
+ }
529
+ }
530
+
531
+ &--no-results {
532
+ margin: 0;
533
+
534
+ color: $unnnic-color-neutral-cleanest;
535
+ line-height: $unnnic-font-size-body-md + $unnnic-line-height-medium;
536
+ font-size: $unnnic-font-size-body-md;
537
+
538
+ padding: $unnnic-spacing-nano $unnnic-spacing-ant;
539
+ }
540
+
541
+ &.inactive {
542
+ display: none;
543
+ }
544
+
545
+ &.active {
546
+ display: block;
547
+ z-index: 2;
548
+ }
549
+ }
550
+
551
+ .unnnic-select-smart__input input {
552
+ // entire class name to have higher priority in styles
553
+ &:read-only {
554
+ cursor: pointer;
555
+
556
+ &::placeholder {
557
+ color: $unnnic-color-neutral-dark;
558
+ }
559
+ }
560
+
561
+ &:not(:read-only :focus) {
562
+ &::placeholder {
563
+ color: $unnnic-color-neutral-dark;
564
+ }
565
+ }
566
+
567
+ &:not(:read-only) {
568
+ &:focus {
569
+ &::placeholder {
570
+ color: $unnnic-color-neutral-cleanest;
571
+ }
572
+ }
573
+ }
574
+
575
+ &:disabled {
576
+ border: 1px solid $unnnic-color-neutral-cleanest;
577
+
578
+ cursor: not-allowed;
579
+
580
+ &::placeholder {
581
+ color: $unnnic-color-neutral-cleanest;
582
+ }
583
+
584
+ + .icon-right {
585
+ cursor: not-allowed;
586
+ }
587
+ }
588
+ }
589
+ }
590
+
591
+ ::-webkit-scrollbar {
592
+ width: 4px;
593
+ }
594
+
595
+ ::-webkit-scrollbar-track {
596
+ background: $unnnic-color-neutral-soft;
597
+ }
598
+
599
+ ::-webkit-scrollbar-thumb {
600
+ background: $unnnic-color-neutral-cleanest;
601
+ border-radius: $unnnic-border-radius-pill;
602
+ }
603
+ </style>
@@ -0,0 +1,136 @@
1
+ <template>
2
+ <div class="unnnic-select-smart__options__multiple">
3
+ <div
4
+ v-if="selectedOptions[0]"
5
+ class="unnnic-select-smart__options__multiple__selecteds__container"
6
+ >
7
+ <div class="unnnic-select-smart__options__multiple__selecteds">
8
+ <tag
9
+ v-for="option in firstMultipleSelecteds"
10
+ class="unnnic-select-smart__options__multiple__selecteds__option"
11
+ :key="option.value"
12
+ :text="option.label"
13
+ hasCloseIcon
14
+ @close="unselectOption(option)"
15
+ />
16
+ <p
17
+ v-if="selectedOptions.length > multipleSelectedsTags"
18
+ class="unnnic-select-smart__options__multiple__selecteds__remaining"
19
+ >
20
+ +{{ selectedOptions.length - multipleSelectedsTags }}
21
+ </p>
22
+ </div>
23
+ <icon-svg
24
+ class="unnnic-select-smart__options__multiple__selecteds__clear"
25
+ icon="close-1"
26
+ size="xs"
27
+ clickable
28
+ @click="clearSelectedOptions"
29
+ />
30
+ </div>
31
+ <p v-if="!selectedOptions[0]" class="unnnic-select-smart__options__multiple--without-multiples">
32
+ {{ withoutSelectsMessage || $t('select_smart.without_multiple_selected') }}
33
+ </p>
34
+ </div>
35
+ </template>
36
+
37
+ <script>
38
+ import Tag from '../Tag/Tag.vue';
39
+ import IconSvg from '../Icon.vue';
40
+
41
+ export default {
42
+ name: 'UnnnicSelectSmartMultipleHeader',
43
+ components: {
44
+ Tag,
45
+ IconSvg,
46
+ },
47
+ props: {
48
+ selectedOptions: {
49
+ type: Array,
50
+ required: true,
51
+ },
52
+ withoutSelectsMessage: {
53
+ type: String,
54
+ default: '',
55
+ },
56
+ },
57
+ data() {
58
+ return {
59
+ multipleSelectedsTags: 2,
60
+ };
61
+ },
62
+ computed: {
63
+ firstMultipleSelecteds() {
64
+ const { selectedOptions, multipleSelectedsTags } = this;
65
+ const selectedArray = [];
66
+
67
+ for (let i = 0; i < multipleSelectedsTags; i += 1) {
68
+ selectedArray.push(selectedOptions?.[i]);
69
+ }
70
+
71
+ return selectedArray.filter((option) => option !== undefined);
72
+ },
73
+ },
74
+ methods: {
75
+ clearSelectedOptions() {
76
+ this.$emit('clear-selected-options');
77
+ },
78
+ unselectOption(option) {
79
+ this.$emit('unselect-option', option);
80
+ },
81
+ },
82
+ };
83
+ </script>
84
+
85
+ <style lang="scss" scoped>
86
+ @import '../../assets/scss/unnnic.scss';
87
+
88
+ .unnnic-select-smart__options__multiple {
89
+ border-bottom: 1px solid $unnnic-color-neutral-soft;
90
+
91
+ &__selecteds {
92
+ display: flex;
93
+
94
+ color: $unnnic-color-neutral-dark;
95
+
96
+ &__container {
97
+ display: flex;
98
+ justify-content: space-between;
99
+ align-items: center;
100
+ }
101
+
102
+ &__option {
103
+ margin: $unnnic-spacing-xs;
104
+ margin-right: 0;
105
+
106
+ &.unnnic-tag {
107
+ outline-color: $unnnic-color-neutral-light;
108
+ background-color: $unnnic-color-neutral-light;
109
+
110
+ color: $unnnic-color-neutral-dark;
111
+ }
112
+ }
113
+
114
+ &__remaining {
115
+ margin-left: $unnnic-spacing-xs;
116
+
117
+ line-height: $unnnic-font-size-body-md + $unnnic-line-height-small;
118
+ font-size: $unnnic-font-size-body-gt;
119
+ }
120
+
121
+ &__clear {
122
+ margin-right: $unnnic-spacing-sm;
123
+ }
124
+ }
125
+
126
+ &--without-multiples {
127
+ margin: 0;
128
+
129
+ color: $unnnic-color-neutral-cleanest;
130
+ line-height: $unnnic-font-size-body-md + $unnnic-line-height-medium;
131
+ font-size: $unnnic-font-size-body-md;
132
+
133
+ padding: $unnnic-spacing-ant;
134
+ }
135
+ }
136
+ </style>
@@ -0,0 +1,141 @@
1
+ <template>
2
+ <div
3
+ @click="$emit('click')"
4
+ :class="{
5
+ 'unnnic-select-smart-option': true,
6
+ 'unnnic-select-smart-option--focused': focused,
7
+ 'unnnic-select-smart-option--active': active,
8
+ 'unnnic--clickable': selectable,
9
+ 'unnnic-select-smart-option--selectable': selectable,
10
+ 'unnnic-select-smart-option--with-checkbox': allowCheckbox,
11
+ }"
12
+ :title="label"
13
+ >
14
+ <unnnic-checkbox v-if="allowCheckbox" ref="checkbox" :value="active" :size="size" />
15
+ <div>
16
+ <span
17
+ :class="['unnnic-select-smart-option__label', `unnnic-select-smart-option__label--${size}`]"
18
+ >{{ label }}</span
19
+ >
20
+ <p
21
+ v-if="description"
22
+ :class="[
23
+ 'unnnic-select-smart-option__description',
24
+ `unnnic-select-smart-option__description--${size}`,
25
+ ]"
26
+ >
27
+ {{ description }}
28
+ </p>
29
+ </div>
30
+ </div>
31
+ </template>
32
+
33
+ <script>
34
+ import UnnnicCheckbox from '../Checkbox/Checkbox.vue';
35
+
36
+ export default {
37
+ name: 'UnnnicSelectSmartOption',
38
+ components: { UnnnicCheckbox },
39
+ props: {
40
+ label: {
41
+ type: String,
42
+ default: 'Option',
43
+ required: true,
44
+ },
45
+ description: {
46
+ type: String,
47
+ default: '',
48
+ required: false,
49
+ },
50
+ size: {
51
+ type: String,
52
+ default: '',
53
+ },
54
+ selectable: {
55
+ type: Boolean,
56
+ default: true,
57
+ },
58
+ focused: {
59
+ type: Boolean,
60
+ default: false,
61
+ },
62
+ active: {
63
+ type: Boolean,
64
+ default: false,
65
+ },
66
+ allowCheckbox: {
67
+ type: Boolean,
68
+ default: false,
69
+ },
70
+ },
71
+ };
72
+ </script>
73
+
74
+ <style lang="scss">
75
+ @import '../../assets/scss/unnnic.scss';
76
+ .unnnic-select-smart-option {
77
+ background-color: $unnnic-color-background-snow;
78
+ padding: $unnnic-spacing-stack-nano $unnnic-inline-xs;
79
+
80
+ max-width: 100%;
81
+
82
+ font-family: $unnnic-font-family-secondary;
83
+ color: $unnnic-color-neutral-dark;
84
+ font-weight: $unnnic-font-weight-regular;
85
+
86
+ white-space: nowrap;
87
+ text-overflow: ellipsis;
88
+ overflow: hidden;
89
+ -webkit-line-clamp: 1;
90
+
91
+ &--selectable:hover,
92
+ &--focused,
93
+ &--active {
94
+ border-radius: $unnnic-border-radius-sm;
95
+
96
+ background-color: $unnnic-color-neutral-light;
97
+ }
98
+
99
+ &--active:not(&--with-checkbox) {
100
+ color: $unnnic-color-weni-600;
101
+ font-weight: $unnnic-font-weight-bold;
102
+ }
103
+
104
+ &--with-checkbox {
105
+ display: flex;
106
+ align-items: center;
107
+ gap: $unnnic-spacing-xs;
108
+
109
+ .unnnic-checkbox rect {
110
+ rx: $unnnic-border-radius-md;
111
+ }
112
+ }
113
+
114
+ &__label {
115
+ &--md {
116
+ font-size: $unnnic-font-size-body-gt;
117
+ line-height: $unnnic-font-size-body-gt + $unnnic-line-height-medium;
118
+ }
119
+ &--sm {
120
+ font-size: $unnnic-font-size-body-md;
121
+ line-height: $unnnic-font-size-body-md + $unnnic-line-height-small;
122
+ }
123
+ }
124
+
125
+ &__description {
126
+ margin: 0;
127
+
128
+ color: $unnnic-color-neutral-cloudy;
129
+ font-weight: $unnnic-font-weight-regular;
130
+
131
+ &--md {
132
+ font-size: $unnnic-font-size-body-md;
133
+ line-height: $unnnic-font-size-body-md + $unnnic-line-height-medium;
134
+ }
135
+ &--sm {
136
+ font-size: $unnnic-font-size-body-sm;
137
+ line-height: $unnnic-font-size-body-md + $unnnic-line-height-small;
138
+ }
139
+ }
140
+ }
141
+ </style>
@@ -11,5 +11,9 @@
11
11
  },
12
12
  "import_card": {
13
13
  "importing": "Importing"
14
+ },
15
+ "select_smart": {
16
+ "without_results": "No results found",
17
+ "without_multiple_selected": "No option selected"
14
18
  }
15
19
  }
@@ -11,5 +11,9 @@
11
11
  },
12
12
  "import_card": {
13
13
  "importing": "Importando"
14
+ },
15
+ "select_smart": {
16
+ "without_results": "Ningún resultado encontrado",
17
+ "without_multiple_selected": "Ninguna opción seleccionada"
14
18
  }
15
19
  }
@@ -11,5 +11,9 @@
11
11
  },
12
12
  "import_card": {
13
13
  "importing": "Importando"
14
+ },
15
+ "select_smart": {
16
+ "without_results": "Nenhum resultado encontrado",
17
+ "without_multiple_selected": "Nenhuma opção selecionada"
14
18
  }
15
19
  }
@@ -0,0 +1,133 @@
1
+ import unnnicSelectSmart from '../components/SelectSmart/SelectSmart.vue';
2
+
3
+ export default {
4
+ title: 'select/SelectSmart',
5
+ component: unnnicSelectSmart,
6
+ argTypes: {
7
+ size: { control: { type: 'select', options: ['md', 'sm'] } },
8
+ type: { control: { type: 'select', options: ['normal', 'error'] } },
9
+ autocomplete: { control: { type: 'boolean' } },
10
+ autocompleteIconLeft: { control: { type: 'boolean' } },
11
+ autocompleteClearOnFocus: { control: { type: 'boolean' } },
12
+ },
13
+ };
14
+
15
+ const Template = (args, { argTypes }) => ({
16
+ props: Object.keys(argTypes),
17
+ components: { unnnicSelectSmart },
18
+ data() {
19
+ return {
20
+ exampleValue: '',
21
+ };
22
+ },
23
+
24
+ methods: {
25
+ addDynamicOption() {
26
+ const value = Math.floor(Math.random() * 1e3);
27
+
28
+ this.exampleOptions.push({
29
+ value: `option${value}`,
30
+ label: `Option ${value}`,
31
+ });
32
+ },
33
+ },
34
+
35
+ template: `
36
+ <div>
37
+ <unnnic-select-smart
38
+ v-model="exampleValue"
39
+ :options="exampleOptions"
40
+ v-bind="$props"
41
+ />
42
+
43
+ <button v-if="!(disabled || autocomplete)" @click="addDynamicOption">Add dynamic option</button>
44
+
45
+ <p v-if="!disabled">v-model: {{exampleValue}}</p>
46
+ </div>
47
+ `,
48
+ });
49
+
50
+ const exampleOptionsDefault = [
51
+ { value: '', label: 'Select some option' },
52
+ { value: '1', label: 'Option 1' },
53
+ { value: '2', label: 'Option 2' },
54
+ { value: '3', label: 'Option 3' },
55
+ { value: '4', label: 'Option 4' },
56
+ { value: '5', label: 'Option 5' },
57
+ { value: '5', label: 'Option 5' },
58
+ ];
59
+
60
+ const exampleOptionsWithDescriptions = [
61
+ { value: '', label: 'Select some option' },
62
+ { value: '1', label: 'Option 1', description: 'This is the first option' },
63
+ { value: '2', label: 'Option 2', description: 'Another alternative you can consider' },
64
+ { value: '3', label: 'Option 3', description: 'A third option for your consideration' },
65
+ { value: '4', label: 'Option 4', description: 'Yet another choice among the options' },
66
+ { value: '5', label: 'Option 5', description: 'The last option available for selection' },
67
+ ];
68
+
69
+ const exampleOptionsCountries = [
70
+ { value: '', label: 'Select some option' },
71
+ { value: 'united_states', label: 'Estados Unidos' },
72
+ { value: 'brazil', label: 'Brasil' },
73
+ { value: 'china', label: 'China' },
74
+ { value: 'india', label: 'Índia' },
75
+ { value: 'russia', label: 'Rússia' },
76
+ { value: 'japan', label: 'Japão' },
77
+ { value: 'germany', label: 'Alemanha' },
78
+ { value: 'france', label: 'França' },
79
+ { value: 'canada', label: 'Canadá' },
80
+ { value: 'australia', label: 'Austrália' },
81
+ { value: 'south_korea', label: 'Coreia do Sul' },
82
+ { value: 'mexico', label: 'México' },
83
+ { value: 'egypt', label: 'Egito' },
84
+ { value: 'south_africa', label: 'África do Sul' },
85
+ { value: 'turkey', label: 'Turquia' },
86
+ { value: 'nigeria', label: 'Nigéria' },
87
+ { value: 'argentina', label: 'Argentina' },
88
+ { value: 'italy', label: 'Itália' },
89
+ { value: 'spain', label: 'Espanha' },
90
+ { value: 'saudi_arabia', label: 'Arábia Saudita' },
91
+ ];
92
+
93
+ const exampleOptionsFirstSelected = [
94
+ { value: '1', label: 'Option 1' },
95
+ { value: '2', label: 'Option 2' },
96
+ { value: '3', label: 'Option 3' },
97
+ { value: '4', label: 'Option 4' },
98
+ { value: '5', label: 'Option 5' },
99
+ { value: '5', label: 'Option 5' },
100
+ ];
101
+
102
+ export const Default = Template.bind({});
103
+ Default.args = {
104
+ exampleOptions: exampleOptionsDefault,
105
+ };
106
+
107
+ export const FirstSelected = Template.bind({});
108
+ FirstSelected.args = {
109
+ exampleOptions: exampleOptionsFirstSelected,
110
+ };
111
+
112
+ export const Disabled = Template.bind({});
113
+ Disabled.args = {
114
+ disabled: true,
115
+ };
116
+
117
+ export const WithDescriptions = Template.bind({});
118
+ WithDescriptions.args = {
119
+ exampleOptions: exampleOptionsWithDescriptions,
120
+ };
121
+
122
+ export const Autocomplete = Template.bind({});
123
+ Autocomplete.args = {
124
+ exampleOptions: exampleOptionsCountries,
125
+ autocomplete: true,
126
+ };
127
+
128
+ export const Multiple = Template.bind({});
129
+ Multiple.args = {
130
+ exampleOptions: exampleOptionsCountries,
131
+ autocomplete: true,
132
+ multiple: true,
133
+ };