@weni/unnnic-system 3.27.1 → 3.28.2-alpha.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@weni/unnnic-system",
3
- "version": "3.27.1",
3
+ "version": "3.28.2-alpha.0",
4
4
  "type": "commonjs",
5
5
  "files": [
6
6
  "dist",
@@ -140,4 +140,4 @@
140
140
  "vue-eslint-parser": "^10.4.0",
141
141
  "vue-tsc": "^3.0.5"
142
142
  }
143
- }
143
+ }
@@ -1,5 +1,6 @@
1
1
  import { mount } from '@vue/test-utils';
2
2
  import { beforeEach, describe, expect, afterEach, test } from 'vitest';
3
+ import { h } from 'vue';
3
4
  import UnnnicSelect from '../index.vue';
4
5
  import i18n from '@/utils/plugins/i18n';
5
6
 
@@ -484,4 +485,142 @@ describe('UnnnicSelect.vue', () => {
484
485
  expect(wrapper.html()).toMatchSnapshot();
485
486
  });
486
487
  });
488
+
489
+ describe('option slot', () => {
490
+ test('renders custom content for each option through the option slot', async () => {
491
+ const slotWrapper = mountWrapper(
492
+ {},
493
+ {
494
+ option: (slotProps) =>
495
+ h('span', { class: 'custom-option' }, `custom-${slotProps.label}`),
496
+ },
497
+ );
498
+
499
+ slotWrapper.vm.setOpenPopover(true);
500
+ await slotWrapper.vm.$nextTick();
501
+
502
+ const options = slotWrapper.findAllComponents({
503
+ name: 'UnnnicPopoverOption',
504
+ });
505
+ expect(options.length).toBe(3);
506
+ expect(options[0].find('.custom-option').exists()).toBe(true);
507
+ expect(options[0].find('.custom-option').text()).toBe('custom-Option 1');
508
+
509
+ slotWrapper.unmount();
510
+ });
511
+
512
+ test('falls back to default label rendering without option slot', async () => {
513
+ wrapper.vm.setOpenPopover(true);
514
+ await wrapper.vm.$nextTick();
515
+
516
+ const options = wrapper.findAllComponents({
517
+ name: 'UnnnicPopoverOption',
518
+ });
519
+ expect(options[0].find('.custom-option').exists()).toBe(false);
520
+ expect(options[0].find('.unnnic-popover-option__label').exists()).toBe(
521
+ true,
522
+ );
523
+ });
524
+ });
525
+
526
+ describe('selected slot (custom trigger)', () => {
527
+ const selectedSlot = {
528
+ selected: (slotProps) =>
529
+ h('span', { class: 'custom-selected' }, `selected-${slotProps.label}`),
530
+ };
531
+
532
+ test('renders custom trigger when selected slot is used and an item is selected', () => {
533
+ const slotWrapper = mountWrapper({ modelValue: 'option1' }, selectedSlot);
534
+
535
+ expect(slotWrapper.find('.unnnic-select__trigger').exists()).toBe(true);
536
+ expect(slotWrapper.find('.custom-selected').text()).toBe(
537
+ 'selected-Option 1',
538
+ );
539
+ expect(slotWrapper.findComponent({ name: 'UnnnicInput' }).exists()).toBe(
540
+ false,
541
+ );
542
+
543
+ slotWrapper.unmount();
544
+ });
545
+
546
+ test('falls back to UnnnicInput when no item is selected', () => {
547
+ const slotWrapper = mountWrapper({ modelValue: null }, selectedSlot);
548
+
549
+ expect(slotWrapper.find('.unnnic-select__trigger').exists()).toBe(false);
550
+ expect(slotWrapper.findComponent({ name: 'UnnnicInput' }).exists()).toBe(
551
+ true,
552
+ );
553
+
554
+ slotWrapper.unmount();
555
+ });
556
+
557
+ test('falls back to UnnnicInput when selected slot is not provided', () => {
558
+ const fallbackWrapper = mountWrapper({ modelValue: 'option1' });
559
+
560
+ expect(fallbackWrapper.find('.unnnic-select__trigger').exists()).toBe(
561
+ false,
562
+ );
563
+ expect(
564
+ fallbackWrapper.findComponent({ name: 'UnnnicInput' }).exists(),
565
+ ).toBe(true);
566
+
567
+ fallbackWrapper.unmount();
568
+ });
569
+
570
+ test('shows the field label above the custom trigger', () => {
571
+ const slotWrapper = mountWrapper(
572
+ { modelValue: 'option1', label: 'Representative' },
573
+ selectedSlot,
574
+ );
575
+
576
+ const label = slotWrapper.find('.unnnic-select__trigger-label');
577
+ expect(label.exists()).toBe(true);
578
+ expect(label.text()).toBe('Representative');
579
+
580
+ slotWrapper.unmount();
581
+ });
582
+
583
+ test('reflects the popover state through the chevron icon', async () => {
584
+ const slotWrapper = mountWrapper({ modelValue: 'option1' }, selectedSlot);
585
+
586
+ const arrow = slotWrapper.find('.unnnic-select__trigger-arrow');
587
+ expect(arrow.exists()).toBe(true);
588
+
589
+ slotWrapper.vm.setOpenPopover(true);
590
+ await slotWrapper.vm.$nextTick();
591
+ expect(slotWrapper.vm.openPopover).toBe(true);
592
+
593
+ slotWrapper.unmount();
594
+ });
595
+
596
+ test('emits update:modelValue with empty value when clear is clicked', async () => {
597
+ const slotWrapper = mountWrapper(
598
+ { modelValue: 'option1', clearable: true },
599
+ selectedSlot,
600
+ );
601
+
602
+ const clear = slotWrapper.find('.unnnic-select__trigger-clear');
603
+ expect(clear.exists()).toBe(true);
604
+
605
+ await clear.trigger('click');
606
+
607
+ expect(slotWrapper.emitted('update:modelValue')).toBeTruthy();
608
+ expect(slotWrapper.emitted('update:modelValue')[0]).toEqual(['']);
609
+
610
+ slotWrapper.unmount();
611
+ });
612
+
613
+ test('does not render clear icon when clearable is false', () => {
614
+ const slotWrapper = mountWrapper(
615
+ { modelValue: 'option1', clearable: false },
616
+ selectedSlot,
617
+ );
618
+
619
+ expect(slotWrapper.find('.unnnic-select__trigger-clear').exists()).toBe(
620
+ false,
621
+ );
622
+
623
+ slotWrapper.unmount();
624
+ });
625
+ });
487
626
  });
@@ -19,7 +19,7 @@ exports[`UnnnicSelect.vue > snapshot testing > matches snapshot with default pro
19
19
  `;
20
20
 
21
21
  exports[`UnnnicSelect.vue > snapshot testing > matches snapshot with disabled state 1`] = `
22
- "<div data-v-6077efb7="" class="unnnic-select"><button data-v-9d52eef8="" data-v-6077efb7="" class="unnnic-popover-trigger w-full" id="reka-popover-trigger-v-0" type="button" aria-haspopup="dialog" aria-expanded="false" aria-controls="" data-state="closed">
22
+ "<div data-v-6077efb7="" class="unnnic-select"><button data-v-9d52eef8="" data-v-6077efb7="" class="unnnic-popover-trigger w-full" id="reka-popover-trigger-v-0" type="button" aria-haspopup="dialog" aria-expanded="false" aria-controls="reka-popover-content-v-1" data-state="closed">
23
23
  <section data-v-9f8d6c86="" data-v-d890ad85="" data-v-6077efb7="" class="unnnic-form-element unnnic-form-element--disabled unnnic-form md unnnic-select__input" data-testid="form-element">
24
24
  <!--v-if-->
25
25
  <div data-v-a0d36167="" data-v-d890ad85="" class="text-input size--md unnnic-select__input unnnic-form-input" hascloudycolor="false" mask=""><input data-v-86533b41="" data-v-a0d36167="" class="unnnic-select__input unnnic-form-input input-itself input size-md normal input--has-icon-right use-focus-prop unnnic-select__input unnnic-form-input input-itself" hascloudycolor="false" placeholder="" iconleft="" iconright="keyboard_arrow_down" iconleftclickable="false" iconrightclickable="false" showclear="false" type="text" readonly="" value="" disabled="">
@@ -55,7 +55,7 @@ exports[`UnnnicSelect.vue > snapshot testing > matches snapshot with infinite sc
55
55
  `;
56
56
 
57
57
  exports[`UnnnicSelect.vue > snapshot testing > matches snapshot with search enabled 1`] = `
58
- "<div data-v-6077efb7="" class="unnnic-select"><button data-v-9d52eef8="" data-v-6077efb7="" class="unnnic-popover-trigger w-full" id="reka-popover-trigger-v-0" type="button" aria-haspopup="dialog" aria-expanded="false" aria-controls="" data-state="closed">
58
+ "<div data-v-6077efb7="" class="unnnic-select"><button data-v-9d52eef8="" data-v-6077efb7="" class="unnnic-popover-trigger w-full" id="reka-popover-trigger-v-0" type="button" aria-haspopup="dialog" aria-expanded="false" aria-controls="reka-popover-content-v-1" data-state="closed">
59
59
  <section data-v-9f8d6c86="" data-v-d890ad85="" data-v-6077efb7="" class="unnnic-form-element unnnic-form md unnnic-select__input" data-testid="form-element">
60
60
  <!--v-if-->
61
61
  <div data-v-a0d36167="" data-v-d890ad85="" class="text-input size--md unnnic-select__input unnnic-form-input" hascloudycolor="false" mask=""><input data-v-86533b41="" data-v-a0d36167="" class="unnnic-select__input unnnic-form-input input-itself input size-md normal input--has-icon-right use-focus-prop unnnic-select__input unnnic-form-input input-itself" hascloudycolor="false" placeholder="" iconleft="" iconright="keyboard_arrow_down" iconleftclickable="false" iconrightclickable="false" showclear="false" type="text" readonly="" value="">
@@ -73,7 +73,7 @@ exports[`UnnnicSelect.vue > snapshot testing > matches snapshot with search enab
73
73
  `;
74
74
 
75
75
  exports[`UnnnicSelect.vue > snapshot testing > matches snapshot with selected value 1`] = `
76
- "<div data-v-6077efb7="" class="unnnic-select"><button data-v-9d52eef8="" data-v-6077efb7="" class="unnnic-popover-trigger w-full" id="reka-popover-trigger-v-0" type="button" aria-haspopup="dialog" aria-expanded="false" aria-controls="" data-state="closed">
76
+ "<div data-v-6077efb7="" class="unnnic-select"><button data-v-9d52eef8="" data-v-6077efb7="" class="unnnic-popover-trigger w-full" id="reka-popover-trigger-v-0" type="button" aria-haspopup="dialog" aria-expanded="false" aria-controls="reka-popover-content-v-1" data-state="closed">
77
77
  <section data-v-9f8d6c86="" data-v-d890ad85="" data-v-6077efb7="" class="unnnic-form-element unnnic-form md unnnic-select__input" data-testid="form-element">
78
78
  <!--v-if-->
79
79
  <div data-v-a0d36167="" data-v-d890ad85="" class="text-input size--md unnnic-select__input unnnic-form-input" hascloudycolor="false" mask=""><input data-v-86533b41="" data-v-a0d36167="" class="unnnic-select__input unnnic-form-input input-itself input size-md normal input--has-icon-right use-focus-prop unnnic-select__input unnnic-form-input input-itself" hascloudycolor="false" placeholder="" iconleft="" iconright="keyboard_arrow_down" iconleftclickable="false" iconrightclickable="false" showclear="false" type="text" readonly="" value="Option 1">
@@ -5,7 +5,53 @@
5
5
  @update:open="setOpenPopover"
6
6
  >
7
7
  <PopoverTrigger class="w-full">
8
+ <section
9
+ v-if="hasSelectedSlot && selectedItem"
10
+ ref="selectInputRef"
11
+ class="unnnic-select__trigger-wrapper"
12
+ >
13
+ <span
14
+ v-if="props.label"
15
+ class="unnnic-select__trigger-label"
16
+ >
17
+ {{ props.label }}
18
+ </span>
19
+ <section
20
+ :class="[
21
+ 'unnnic-select__trigger',
22
+ `unnnic-select__trigger--${props.size}`,
23
+ {
24
+ 'unnnic-select__trigger--focused': openPopover,
25
+ 'unnnic-select__trigger--disabled': props.disabled,
26
+ },
27
+ ]"
28
+ >
29
+ <section class="unnnic-select__trigger-content">
30
+ <slot
31
+ name="selected"
32
+ :option="selectedItem"
33
+ :label="inputValue as string"
34
+ />
35
+ </section>
36
+ <UnnnicIcon
37
+ v-if="props.clearable && !props.disabled"
38
+ class="unnnic-select__trigger-clear"
39
+ icon="close"
40
+ size="ant"
41
+ scheme="fg-base"
42
+ clickable
43
+ @click.stop="emit('update:modelValue', '')"
44
+ />
45
+ <UnnnicIcon
46
+ class="unnnic-select__trigger-arrow"
47
+ :icon="openPopover ? 'keyboard_arrow_up' : 'keyboard_arrow_down'"
48
+ size="ant"
49
+ scheme="fg-base"
50
+ />
51
+ </section>
52
+ </section>
8
53
  <UnnnicInput
54
+ v-else
9
55
  ref="selectInputRef"
10
56
  :modelValue="inputValue as string"
11
57
  class="unnnic-select__input"
@@ -60,7 +106,23 @@
60
106
  :focused="focusedOptionIndex === index"
61
107
  :disabled="option.disabled"
62
108
  @click="handleSelectOption(option)"
63
- />
109
+ >
110
+ <template
111
+ v-if="$slots.option"
112
+ #default
113
+ >
114
+ <slot
115
+ name="option"
116
+ :option="option"
117
+ :label="option[props.itemLabel] as string"
118
+ :active="
119
+ option[props.itemValue] === selectedItem?.[props.itemValue]
120
+ "
121
+ :focused="focusedOptionIndex === index"
122
+ :index="index"
123
+ />
124
+ </template>
125
+ </PopoverOption>
64
126
  <div
65
127
  v-if="props.infiniteScroll && infiniteScrollLoading"
66
128
  class="unnnic-select__infinite-loading"
@@ -85,11 +147,13 @@ import {
85
147
  nextTick,
86
148
  onBeforeUnmount,
87
149
  useTemplateRef,
150
+ useSlots,
88
151
  } from 'vue';
89
152
 
90
153
  import { useInfiniteScroll } from '@vueuse/core';
91
154
 
92
155
  import UnnnicInput from '../Input/Input.vue';
156
+ import UnnnicIcon from '../Icon.vue';
93
157
  import UnnnicIconLoading from '../IconLoading/IconLoading.vue';
94
158
  import {
95
159
  Popover,
@@ -141,6 +205,20 @@ const emit = defineEmits<{
141
205
  'scroll-end': [];
142
206
  }>();
143
207
 
208
+ defineSlots<{
209
+ option?: (props: {
210
+ option: SelectOption;
211
+ label: string;
212
+ active: boolean;
213
+ focused: boolean;
214
+ index: number;
215
+ }) => unknown;
216
+ selected?: (props: { option: SelectOption; label: string }) => unknown;
217
+ }>();
218
+
219
+ const slots = useSlots();
220
+ const hasSelectedSlot = computed(() => !!slots.selected);
221
+
144
222
  const selectInputRef = useTemplateRef<HTMLElement>('selectInputRef');
145
223
  const contentRef = useTemplateRef<HTMLDivElement>('contentRef');
146
224
 
@@ -155,8 +233,11 @@ function setOpenPopover(value: boolean) {
155
233
  base.openPopover.value = value;
156
234
  }
157
235
 
158
- const selectedItem = computed(() => {
159
- if (props.returnObject) return props.modelValue;
236
+ const selectedItem = computed((): SelectOption | undefined => {
237
+ if (props.returnObject) {
238
+ if (props.modelValue == null || props.modelValue === '') return undefined;
239
+ return props.modelValue as SelectOption;
240
+ }
160
241
 
161
242
  return props.options.find(
162
243
  (option) => option[props.itemValue] === props.modelValue,
@@ -289,6 +370,70 @@ defineExpose({
289
370
  }
290
371
 
291
372
  .unnnic-select {
373
+ &__trigger-wrapper {
374
+ display: flex;
375
+ flex-direction: column;
376
+ gap: $unnnic-space-1;
377
+ width: 100%;
378
+ text-align: left;
379
+ font: $unnnic-font-body;
380
+ }
381
+
382
+ &__trigger-label {
383
+ font: $unnnic-font-body;
384
+ color: $unnnic-color-fg-base;
385
+ }
386
+
387
+ &__trigger {
388
+ cursor: pointer;
389
+ display: flex;
390
+ align-items: center;
391
+ gap: $unnnic-space-2;
392
+ width: 100%;
393
+ max-width: 100%;
394
+ overflow: hidden;
395
+ box-sizing: border-box;
396
+
397
+ background: $unnnic-color-bg-base;
398
+ border: 1px solid $unnnic-color-border-base;
399
+ border-radius: $unnnic-radius-2;
400
+ padding: $unnnic-space-3 $unnnic-space-4;
401
+ height: 45px;
402
+
403
+ transition: border-color 0.1s ease-in-out;
404
+
405
+ &--sm {
406
+ padding: $unnnic-space-2 $unnnic-space-4;
407
+ height: 37px;
408
+ }
409
+
410
+ &--focused {
411
+ border-color: $unnnic-color-border-accent-strong;
412
+ }
413
+
414
+ &--disabled {
415
+ cursor: not-allowed;
416
+ border-color: $unnnic-color-border-muted;
417
+ background-color: $unnnic-color-bg-muted;
418
+ }
419
+
420
+ &-content {
421
+ flex: 1 1 0;
422
+ min-width: 0;
423
+ display: flex;
424
+ align-items: center;
425
+ gap: $unnnic-space-2;
426
+ overflow: hidden;
427
+ font: $unnnic-font-body;
428
+ color: $unnnic-color-fg-emphasized;
429
+ }
430
+
431
+ &-clear,
432
+ &-arrow {
433
+ flex-shrink: 0;
434
+ }
435
+ }
436
+
292
437
  &__content {
293
438
  display: flex;
294
439
  flex-direction: column;
@@ -73,20 +73,34 @@ const getComponentName = (vnode: VNode): string | undefined => {
73
73
  return componentType?.name || componentType?.__name;
74
74
  };
75
75
 
76
- const contentChildren = computed(() => {
77
- const defaultSlot = slots.default?.() || [];
78
- return defaultSlot.filter(
79
- (vnode: VNode) => getComponentName(vnode) !== 'UnnnicPopoverFooter',
80
- );
81
- });
76
+ const isFooter = (vnode: VNode) =>
77
+ getComponentName(vnode) === 'UnnnicPopoverFooter';
78
+
79
+ // This function recursively checks if there is a UnnnicPopoverFooter in the slot,
80
+ // and splits the content into content and footer
81
+ const splitSlot = computed(() => {
82
+ const footers: VNode[] = [];
83
+
84
+ const content = (function extract(vnodes: VNode[]): VNode[] {
85
+ return vnodes.filter((vnode: VNode) => {
86
+ if (!vnode || typeof vnode !== 'object') return true;
87
+ if (isFooter(vnode)) return footers.push(vnode) && false;
82
88
 
83
- const footerChildren = computed(() => {
84
- const defaultSlot = slots.default?.() || [];
85
- return defaultSlot.filter(
86
- (vnode: VNode) => getComponentName(vnode) === 'UnnnicPopoverFooter',
87
- );
89
+ if (Array.isArray(vnode.children) && vnode.children.length) {
90
+ vnode.children = extract(vnode.children as VNode[]);
91
+ return (vnode.children as VNode[]).length > 0;
92
+ }
93
+
94
+ return true;
95
+ });
96
+ })(slots.default?.() || []);
97
+
98
+ return { content, footers };
88
99
  });
89
100
 
101
+ const contentChildren = computed(() => splitSlot.value.content);
102
+ const footerChildren = computed(() => splitSlot.value.footers);
103
+
90
104
  const contentWidth = computed(() => {
91
105
  if (props.width) return props.width;
92
106
 
@@ -21,12 +21,8 @@ $popover-space: $unnnic-space-4;
21
21
  padding: $popover-space;
22
22
 
23
23
  display: flex;
24
- justify-content: center;
24
+ justify-content: flex-end;
25
25
  align-items: center;
26
26
  gap: $unnnic-space-2;
27
-
28
- > * {
29
- width: 100%;
30
- }
31
27
  }
32
28
  </style>
@@ -6,6 +6,7 @@
6
6
  'unnnic-popover-option--disabled': props.disabled,
7
7
  'unnnic-popover-option--active': props.active,
8
8
  'unnnic-popover-option--focused': props.focused,
9
+ 'unnnic-popover-option--with-content': hasDefaultSlot,
9
10
  },
10
11
  ]"
11
12
  >
@@ -15,15 +16,17 @@
15
16
  :scheme="schemeColor"
16
17
  size="ant"
17
18
  />
18
- <p
19
- :class="[
20
- 'unnnic-popover-option__label',
21
- `unnnic-popover-option__label--${schemeColor}`,
22
- `unnnic-popover-option--disabled: ${props.disabled}`,
23
- ]"
24
- >
25
- {{ props.label }}
26
- </p>
19
+ <slot>
20
+ <p
21
+ :class="[
22
+ 'unnnic-popover-option__label',
23
+ `unnnic-popover-option__label--${schemeColor}`,
24
+ `unnnic-popover-option--disabled: ${props.disabled}`,
25
+ ]"
26
+ >
27
+ {{ props.label }}
28
+ </p>
29
+ </slot>
27
30
  </div>
28
31
  </template>
29
32
 
@@ -31,7 +34,7 @@
31
34
  import UnnnicIcon from '@/components/Icon.vue';
32
35
 
33
36
  import type { SchemeColor } from '@/types/scheme-colors';
34
- import { computed } from 'vue';
37
+ import { computed, useSlots } from 'vue';
35
38
 
36
39
  defineOptions({
37
40
  name: 'UnnnicPopoverOption',
@@ -54,6 +57,9 @@ const props = withDefaults(defineProps<PopoverOptionProps>(), {
54
57
  icon: '',
55
58
  });
56
59
 
60
+ const slots = useSlots();
61
+ const hasDefaultSlot = computed(() => !!slots.default);
62
+
57
63
  const schemeColor = computed(() => {
58
64
  if (props.active) {
59
65
  return 'fg-on-primary';
@@ -82,6 +88,10 @@ const schemeColor = computed(() => {
82
88
  gap: $unnnic-space-2;
83
89
  align-items: center;
84
90
 
91
+ &--with-content {
92
+ justify-content: space-between;
93
+ }
94
+
85
95
  &:hover:not(&--active):not(&--disabled),
86
96
  &--focused {
87
97
  background-color: $unnnic-color-bg-soft;
@@ -1,4 +1,5 @@
1
1
  import { PopoverOption } from '../components/ui/popover';
2
+ import UnnnicTag from '../components/Tag/Tag.vue';
2
3
  import colorsList from '../utils/colorsList';
3
4
 
4
5
  export default {
@@ -73,3 +74,55 @@ export const Default = {
73
74
  `,
74
75
  }),
75
76
  };
77
+
78
+ export const WithTag = {
79
+ parameters: {
80
+ docs: {
81
+ description: {
82
+ story:
83
+ 'Uses the default slot to render custom content, such as a label alongside a status tag. The option keeps the `space-between` layout, so the tag stays aligned to the end.',
84
+ },
85
+ source: {
86
+ code: `<UnnnicPopoverOption v-for="option in options" :key="option.value">
87
+ <span class="popover-option__label">{{ option.label }}</span>
88
+ <UnnnicTag
89
+ type="default"
90
+ size="small"
91
+ :scheme="statusConfig[option.status].scheme"
92
+ :text="statusConfig[option.status].text"
93
+ />
94
+ </UnnnicPopoverOption>`,
95
+ },
96
+ },
97
+ },
98
+ render: () => ({
99
+ components: { PopoverOption, UnnnicTag },
100
+ data() {
101
+ return {
102
+ options: [
103
+ { label: 'John Doe', value: 'john-doe', status: 'online' },
104
+ { label: 'Jane Doe', value: 'jane-doe', status: 'lunch' },
105
+ { label: 'James Smith', value: 'james-smith', status: 'offline' },
106
+ ],
107
+ statusConfig: {
108
+ online: { scheme: 'green', text: 'Online' },
109
+ lunch: { scheme: 'orange', text: 'Lunch' },
110
+ offline: { scheme: 'gray', text: 'Offline' },
111
+ },
112
+ };
113
+ },
114
+ template: `
115
+ <section style="display: flex; flex-direction: column; gap: 8px; width: 280px;">
116
+ <PopoverOption v-for="option in options" :key="option.value" :label="option.label">
117
+ <span style="flex: 1 1 auto; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;">{{ option.label }}</span>
118
+ <unnnic-tag
119
+ type="default"
120
+ size="small"
121
+ :scheme="statusConfig[option.status].scheme"
122
+ :text="statusConfig[option.status].text"
123
+ />
124
+ </PopoverOption>
125
+ </section>
126
+ `,
127
+ }),
128
+ };