@weni/unnnic-system 3.14.0-alpha-teleports.0 → 3.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (82) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/README.md +1 -9
  3. package/dist/{es-0d53b5b2.mjs → es-b51fb49d.mjs} +1 -1
  4. package/dist/{index-d7070de8.mjs → index-88ec0352.mjs} +26529 -29321
  5. package/dist/index.d.ts +1485 -4826
  6. package/dist/{pt-br-bf4e1f97.mjs → pt-br-cd9f0dbc.mjs} +1 -1
  7. package/dist/style.css +1 -1
  8. package/dist/unnnic.mjs +204 -232
  9. package/dist/unnnic.umd.js +46 -50
  10. package/package.json +2 -3
  11. package/src/assets/scss/tailwind.scss +0 -8
  12. package/src/components/Alert/__tests__/__snapshots__/Alert.spec.js.snap +1 -1
  13. package/src/components/ChartFunnel/DefaultFunnel/ChartDefaultFunnelBase.vue +1 -2
  14. package/src/components/ChartFunnel/SvgFunnel/ChartFunnelTwoRows.vue +60 -61
  15. package/src/components/CheckboxGroup/CheckboxGroup.vue +7 -5
  16. package/src/components/Chip/Chip.vue +1 -1
  17. package/src/components/DatePicker/DatePicker.vue +1 -10
  18. package/src/components/Drawer/Drawer.vue +270 -180
  19. package/src/components/Drawer/__tests__/Drawer.spec.js +43 -32
  20. package/src/components/Drawer/__tests__/__snapshots__/Drawer.spec.js.snap +19 -18
  21. package/src/components/FormElement/FormElement.vue +96 -87
  22. package/src/components/InputDatePicker/InputDatePicker.vue +73 -68
  23. package/src/components/InputDatePicker/__test__/InputDatePicker.spec.js +24 -31
  24. package/src/components/ModalDialog/ModalDialog.vue +154 -63
  25. package/src/components/ModalDialog/__tests__/ModalDialog.spec.js +210 -30
  26. package/src/components/ModalDialog/__tests__/__snapshots__/ModalDialog.spec.js.snap +22 -1
  27. package/src/components/Radio/Radio.vue +12 -6
  28. package/src/components/Radio/__test__/Radio.spec.js +3 -1
  29. package/src/components/RadioGroup/RadioGroup.vue +18 -10
  30. package/src/components/Select/__tests__/Select.spec.js +74 -0
  31. package/src/components/Select/__tests__/SelectItem.spec.js +35 -15
  32. package/src/components/Select/__tests__/__snapshots__/Select.spec.js.snap +18 -0
  33. package/src/components/Select/index.vue +131 -22
  34. package/src/components/Switch/Switch.vue +10 -3
  35. package/src/components/Tab/__test__/__snapshots__/Tab.spec.js.snap +1 -3
  36. package/src/components/TemplatePreview/TemplatePreview.vue +28 -25
  37. package/src/components/TemplatePreview/TemplatePreviewModal.vue +10 -10
  38. package/src/components/TemplatePreview/types.d.ts +3 -3
  39. package/src/components/Toast/Toast.vue +1 -4
  40. package/src/components/Toast/ToastManager.ts +1 -4
  41. package/src/components/Toast/__tests__/ToastManager.spec.js +6 -10
  42. package/src/components/ToolTip/ToolTip.vue +177 -25
  43. package/src/components/ToolTip/__tests__/ToolTip.spec.js +61 -339
  44. package/src/components/index.ts +0 -56
  45. package/src/components/ui/popover/PopoverContent.vue +4 -7
  46. package/src/components/ui/popover/PopoverTrigger.vue +1 -5
  47. package/src/index.ts +2 -9
  48. package/src/stories/Drawer.stories.js +1 -1
  49. package/src/stories/ModalDialog.mdx +0 -3
  50. package/src/stories/ModalDialog.stories.js +1 -96
  51. package/src/stories/Select.stories.js +86 -0
  52. package/src/stories/TemplatePreview.stories.js +27 -27
  53. package/src/stories/TemplatePreviewModal.stories.js +31 -31
  54. package/src/components/ui/dialog/Dialog.vue +0 -19
  55. package/src/components/ui/dialog/DialogClose.vue +0 -29
  56. package/src/components/ui/dialog/DialogContent.vue +0 -140
  57. package/src/components/ui/dialog/DialogFooter.vue +0 -50
  58. package/src/components/ui/dialog/DialogHeader.vue +0 -83
  59. package/src/components/ui/dialog/DialogTitle.vue +0 -38
  60. package/src/components/ui/dialog/DialogTrigger.vue +0 -16
  61. package/src/components/ui/dialog/index.ts +0 -7
  62. package/src/components/ui/drawer/Drawer.vue +0 -27
  63. package/src/components/ui/drawer/DrawerClose.vue +0 -31
  64. package/src/components/ui/drawer/DrawerContent.vue +0 -113
  65. package/src/components/ui/drawer/DrawerDescription.vue +0 -40
  66. package/src/components/ui/drawer/DrawerFooter.vue +0 -38
  67. package/src/components/ui/drawer/DrawerHeader.vue +0 -57
  68. package/src/components/ui/drawer/DrawerOverlay.vue +0 -33
  69. package/src/components/ui/drawer/DrawerTitle.vue +0 -37
  70. package/src/components/ui/drawer/DrawerTrigger.vue +0 -31
  71. package/src/components/ui/drawer/index.ts +0 -10
  72. package/src/components/ui/tooltip/Tooltip.vue +0 -21
  73. package/src/components/ui/tooltip/TooltipContent.vue +0 -77
  74. package/src/components/ui/tooltip/TooltipTrigger.vue +0 -24
  75. package/src/components/ui/tooltip/index.ts +0 -3
  76. package/src/lib/__tests__/teleport-target.spec.ts +0 -73
  77. package/src/lib/layer-manager.ts +0 -64
  78. package/src/lib/teleport-target.ts +0 -46
  79. package/src/stories/Dialog.stories.js +0 -832
  80. package/src/stories/DrawerNext.stories.js +0 -611
  81. package/src/stories/LayerManager.docs.mdx +0 -40
  82. package/src/stories/LayerManager.stories.js +0 -407
@@ -1,8 +1,10 @@
1
1
  <template>
2
- <section :class="[
3
- 'unnnic-radio-group__container',
4
- `unnnic-radio-group--state-${state}`
5
- ]">
2
+ <section
3
+ :class="[
4
+ 'unnnic-radio-group__container',
5
+ `unnnic-radio-group--state-${state}`,
6
+ ]"
7
+ >
6
8
  <UnnnicLabel
7
9
  v-if="label"
8
10
  :label="label"
@@ -68,11 +70,14 @@ const emit = defineEmits(['update:modelValue']);
68
70
 
69
71
  const contextModelValue = ref(props.modelValue);
70
72
 
71
- watch(() => props.modelValue, (newVal) => {
72
- if (newVal !== contextModelValue.value) {
73
- contextModelValue.value = newVal;
74
- }
75
- });
73
+ watch(
74
+ () => props.modelValue,
75
+ (newVal) => {
76
+ if (newVal !== contextModelValue.value) {
77
+ contextModelValue.value = newVal;
78
+ }
79
+ },
80
+ );
76
81
 
77
82
  watch(contextModelValue, (newVal) => {
78
83
  if (newVal !== props.modelValue) {
@@ -81,7 +86,10 @@ watch(contextModelValue, (newVal) => {
81
86
  });
82
87
 
83
88
  const computedName = computed(() => {
84
- return props.name || `unnnic-radio-group-${Math.random().toString(36).substring(2, 15)}`;
89
+ return (
90
+ props.name ||
91
+ `unnnic-radio-group-${Math.random().toString(36).substring(2, 15)}`
92
+ );
85
93
  });
86
94
 
87
95
  provide('contextModelValue', contextModelValue);
@@ -399,6 +399,72 @@ describe('UnnnicSelect.vue', () => {
399
399
  });
400
400
  });
401
401
 
402
+ describe('infinite scroll functionality', () => {
403
+ test('infinite scroll is disabled by default', () => {
404
+ expect(wrapper.vm.infiniteScroll).toBe(false);
405
+ });
406
+
407
+ test('applies infinite scroll props correctly', async () => {
408
+ await wrapper.setProps({
409
+ infiniteScroll: true,
410
+ infiniteScrollDistance: 20,
411
+ infiniteScrollCanLoadMore: () => false,
412
+ });
413
+
414
+ expect(wrapper.vm.infiniteScroll).toBe(true);
415
+ expect(wrapper.vm.infiniteScrollDistance).toBe(20);
416
+ expect(wrapper.vm.infiniteScrollCanLoadMore()).toBe(false);
417
+ });
418
+
419
+ test('does not render loading when infiniteScrollLoading is false', async () => {
420
+ await wrapper.setProps({ infiniteScroll: true });
421
+ wrapper.vm.openPopover = true;
422
+ await wrapper.vm.$nextTick();
423
+
424
+ const loading = wrapper.find('.unnnic-select__infinite-loading');
425
+ expect(loading.exists()).toBe(false);
426
+ });
427
+
428
+ test('sets infiniteScrollLoading to true and verifies state', async () => {
429
+ await wrapper.setProps({
430
+ infiniteScroll: true,
431
+ options: [
432
+ { label: 'Option 1', value: 'option1' },
433
+ { label: 'Option 2', value: 'option2' },
434
+ ],
435
+ });
436
+
437
+ wrapper.vm.openPopover = true;
438
+ await wrapper.vm.$nextTick();
439
+
440
+ expect(wrapper.vm.infiniteScrollLoading).toBe(false);
441
+
442
+ wrapper.vm.infiniteScrollLoading = true;
443
+ await wrapper.vm.$nextTick();
444
+
445
+ expect(wrapper.vm.infiniteScrollLoading).toBe(true);
446
+ expect(wrapper.vm.infiniteScroll).toBe(true);
447
+ });
448
+
449
+ test('finishInfiniteScroll sets loading to false', async () => {
450
+ await wrapper.setProps({ infiniteScroll: true });
451
+ wrapper.vm.infiniteScrollLoading = true;
452
+ expect(wrapper.vm.infiniteScrollLoading).toBe(true);
453
+
454
+ wrapper.vm.finishInfiniteScroll();
455
+ expect(wrapper.vm.infiniteScrollLoading).toBe(false);
456
+ });
457
+
458
+ test('resetInfiniteScroll sets loading to false', async () => {
459
+ await wrapper.setProps({ infiniteScroll: true });
460
+ wrapper.vm.infiniteScrollLoading = true;
461
+ expect(wrapper.vm.infiniteScrollLoading).toBe(true);
462
+
463
+ wrapper.vm.resetInfiniteScroll();
464
+ expect(wrapper.vm.infiniteScrollLoading).toBe(false);
465
+ });
466
+ });
467
+
402
468
  describe('snapshot testing', () => {
403
469
  test('matches snapshot with default props', () => {
404
470
  expect(wrapper.html()).toMatchSnapshot();
@@ -418,5 +484,13 @@ describe('UnnnicSelect.vue', () => {
418
484
  await wrapper.setProps({ disabled: true });
419
485
  expect(wrapper.html()).toMatchSnapshot();
420
486
  });
487
+
488
+ test('matches snapshot with infinite scroll enabled', async () => {
489
+ await wrapper.setProps({ infiniteScroll: true });
490
+ wrapper.vm.openPopover = true;
491
+ wrapper.vm.infiniteScrollLoading = true;
492
+ await wrapper.vm.$nextTick();
493
+ expect(wrapper.html()).toMatchSnapshot();
494
+ });
421
495
  });
422
496
  });
@@ -3,9 +3,9 @@ import { beforeEach, describe, expect, test } from 'vitest';
3
3
  import SelectItem from '../SelectItem.vue';
4
4
 
5
5
  const createWrapper = (props = {}, slots = {}) => {
6
- return mount(SelectItem, {
6
+ return mount(SelectItem, {
7
7
  props,
8
- slots: slots.default ? { default: slots.default } : {}
8
+ slots: slots.default ? { default: slots.default } : {},
9
9
  });
10
10
  };
11
11
 
@@ -76,8 +76,12 @@ describe('SelectItem.vue', () => {
76
76
  test('does not apply size class when size is empty', async () => {
77
77
  await wrapper.setProps({ size: '' });
78
78
  const labelElement = wrapper.find('.unnnic-select-item__label');
79
- expect(labelElement.classes()).not.toContain('unnnic-select-item__label--md');
80
- expect(labelElement.classes()).not.toContain('unnnic-select-item__label--sm');
79
+ expect(labelElement.classes()).not.toContain(
80
+ 'unnnic-select-item__label--md',
81
+ );
82
+ expect(labelElement.classes()).not.toContain(
83
+ 'unnnic-select-item__label--sm',
84
+ );
81
85
  });
82
86
  });
83
87
 
@@ -128,7 +132,10 @@ describe('SelectItem.vue', () => {
128
132
  });
129
133
 
130
134
  test('emits click event even when selectable is false', async () => {
131
- const clickWrapper = createWrapper({ selectable: false }, { default: 'Test Item' });
135
+ const clickWrapper = createWrapper(
136
+ { selectable: false },
137
+ { default: 'Test Item' },
138
+ );
132
139
  await clickWrapper.trigger('click');
133
140
  expect(clickWrapper.emitted('click')).toBeTruthy();
134
141
  expect(clickWrapper.emitted('click').length).toBeGreaterThanOrEqual(1);
@@ -194,7 +201,7 @@ describe('SelectItem.vue', () => {
194
201
  test('has proper semantic structure', () => {
195
202
  const div = wrapper.find('.unnnic-select-item');
196
203
  const span = wrapper.find('.unnnic-select-item__label');
197
-
204
+
198
205
  expect(div.exists()).toBe(true);
199
206
  expect(span.exists()).toBe(true);
200
207
  expect(span.element.tagName).toBe('SPAN');
@@ -214,7 +221,8 @@ describe('SelectItem.vue', () => {
214
221
  });
215
222
 
216
223
  test('handles long slot content', async () => {
217
- const longText = 'This is a very long text content that should be handled properly by the component';
224
+ const longText =
225
+ 'This is a very long text content that should be handled properly by the component';
218
226
  const longWrapper = createWrapper({}, { default: longText });
219
227
  const labelElement = longWrapper.find('.unnnic-select-item__label');
220
228
  expect(labelElement.text()).toBe(longText);
@@ -237,8 +245,12 @@ describe('SelectItem.vue', () => {
237
245
 
238
246
  describe('CSS class combinations', () => {
239
247
  test('applies correct classes for non-selectable inactive item', async () => {
240
- await wrapper.setProps({ selectable: false, active: false, textFocused: false });
241
-
248
+ await wrapper.setProps({
249
+ selectable: false,
250
+ active: false,
251
+ textFocused: false,
252
+ });
253
+
242
254
  expect(wrapper.classes()).toContain('unnnic-select-item');
243
255
  expect(wrapper.classes()).not.toContain('unnnic-select-item--selectable');
244
256
  expect(wrapper.classes()).not.toContain('unnnic--clickable');
@@ -247,8 +259,12 @@ describe('SelectItem.vue', () => {
247
259
  });
248
260
 
249
261
  test('applies correct classes for selectable active item', async () => {
250
- await wrapper.setProps({ selectable: true, active: true, textFocused: false });
251
-
262
+ await wrapper.setProps({
263
+ selectable: true,
264
+ active: true,
265
+ textFocused: false,
266
+ });
267
+
252
268
  expect(wrapper.classes()).toContain('unnnic-select-item');
253
269
  expect(wrapper.classes()).toContain('unnnic-select-item--selectable');
254
270
  expect(wrapper.classes()).toContain('unnnic--clickable');
@@ -257,8 +273,12 @@ describe('SelectItem.vue', () => {
257
273
  });
258
274
 
259
275
  test('applies correct classes for text-focused item', async () => {
260
- await wrapper.setProps({ selectable: true, active: false, textFocused: true });
261
-
276
+ await wrapper.setProps({
277
+ selectable: true,
278
+ active: false,
279
+ textFocused: true,
280
+ });
281
+
262
282
  expect(wrapper.classes()).toContain('unnnic-select-item');
263
283
  expect(wrapper.classes()).toContain('unnnic-select-item--selectable');
264
284
  expect(wrapper.classes()).toContain('unnnic--clickable');
@@ -298,11 +318,11 @@ describe('SelectItem.vue', () => {
298
318
  });
299
319
 
300
320
  test('matches snapshot with all states combined', async () => {
301
- await wrapper.setProps({
321
+ await wrapper.setProps({
302
322
  size: 'md',
303
323
  selectable: true,
304
324
  active: true,
305
- textFocused: true
325
+ textFocused: true,
306
326
  });
307
327
  expect(wrapper.html()).toMatchSnapshot();
308
328
  });
@@ -36,6 +36,24 @@ exports[`UnnnicSelect.vue > snapshot testing > matches snapshot with disabled st
36
36
  </div>"
37
37
  `;
38
38
 
39
+ exports[`UnnnicSelect.vue > snapshot testing > matches snapshot with infinite scroll enabled 1`] = `
40
+ "<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="true" aria-controls="reka-popover-content-v-1" data-state="open">
41
+ <section data-v-9f8d6c86="" data-v-d890ad85="" data-v-6077efb7="" class="unnnic-form-element unnnic-form md unnnic-select__input" data-testid="form-element">
42
+ <!--v-if-->
43
+ <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 focus use-focus-prop unnnic-select__input unnnic-form-input input-itself" hascloudycolor="false" placeholder="" iconleft="" iconright="keyboard_arrow_up" iconleftclickable="false" iconrightclickable="false" showclear="false" type="text" readonly="" value="">
44
+ <!--v-if-->
45
+ <section data-v-a0d36167="" class="icon-right-container">
46
+ <!--v-if--><span data-v-26446d8e="" data-v-a0d36167="" class="unnnic-icon material-symbols-rounded unnnic-icon-scheme--fg-base unnnic-icon-size--ant unnnic-icon__size--ant icon-right" data-testid="material-icon" translate="no">keyboard_arrow_up</span>
47
+ </section>
48
+ </div>
49
+ <!--v-if-->
50
+ </section>
51
+ </button>
52
+ <!--teleport start-->
53
+ <!--teleport end-->
54
+ </div>"
55
+ `;
56
+
39
57
  exports[`UnnnicSelect.vue > snapshot testing > matches snapshot with search enabled 1`] = `
40
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">
41
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">
@@ -32,7 +32,10 @@
32
32
  :style="popoverContentCustomStyles"
33
33
  :width="inputWidthString"
34
34
  >
35
- <div class="unnnic-select__content">
35
+ <div
36
+ ref="contentRef"
37
+ class="unnnic-select__content"
38
+ >
36
39
  <UnnnicInput
37
40
  v-if="props.enableSearch"
38
41
  class="unnnic-select__input-search"
@@ -47,20 +50,30 @@
47
50
  >
48
51
  {{ $t('without_results') }}
49
52
  </p>
50
- <PopoverOption
51
- v-for="(option, index) in filteredOptions"
52
- v-else
53
- :key="option[props.itemValue]"
54
- :data-option-index="index"
55
- data-testid="select-option"
56
- :label="option[props.itemLabel]"
57
- :active="
58
- option[props.itemValue] === selectedItem?.[props.itemValue]
59
- "
60
- :focused="focusedOptionIndex === index"
61
- :disabled="option.disabled"
62
- @click="handleSelectOption(option)"
63
- />
53
+ <template v-else>
54
+ <PopoverOption
55
+ v-for="(option, index) in filteredOptions"
56
+ :key="option[props.itemValue]"
57
+ :data-option-index="index"
58
+ data-testid="select-option"
59
+ :label="option[props.itemLabel]"
60
+ :active="
61
+ option[props.itemValue] === selectedItem?.[props.itemValue]
62
+ "
63
+ :focused="focusedOptionIndex === index"
64
+ :disabled="option.disabled"
65
+ @click="handleSelectOption(option)"
66
+ />
67
+ <div
68
+ v-if="props.infiniteScroll && infiniteScrollLoading"
69
+ class="unnnic-select__infinite-loading"
70
+ >
71
+ <UnnnicIconLoading
72
+ scheme="neutral-dark"
73
+ size="sm"
74
+ />
75
+ </div>
76
+ </template>
64
77
  </div>
65
78
  </PopoverContent>
66
79
  </Popover>
@@ -68,10 +81,11 @@
68
81
  </template>
69
82
 
70
83
  <script setup lang="ts">
71
- import { computed, ref, watch, nextTick } from 'vue';
72
- import { useElementSize } from '@vueuse/core';
84
+ import { computed, ref, watch, nextTick, onBeforeUnmount } from 'vue';
85
+ import { useElementSize, useInfiniteScroll } from '@vueuse/core';
73
86
 
74
87
  import UnnnicInput from '../Input/Input.vue';
88
+ import UnnnicIconLoading from '../IconLoading/IconLoading.vue';
75
89
 
76
90
  import {
77
91
  Popover,
@@ -106,6 +120,9 @@ interface SelectProps {
106
120
  search?: string;
107
121
  locale?: string;
108
122
  disabled?: boolean;
123
+ infiniteScroll?: boolean;
124
+ infiniteScrollDistance?: number;
125
+ infiniteScrollCanLoadMore?: () => boolean;
109
126
  }
110
127
 
111
128
  const props = withDefaults(defineProps<SelectProps>(), {
@@ -123,17 +140,24 @@ const props = withDefaults(defineProps<SelectProps>(), {
123
140
  errors: '',
124
141
  message: '',
125
142
  search: '',
143
+ infiniteScroll: false,
144
+ infiniteScrollDistance: 10,
145
+ infiniteScrollCanLoadMore: () => true,
126
146
  });
127
147
 
128
148
  const emit = defineEmits<{
129
149
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
130
150
  'update:modelValue': [value: any];
131
151
  'update:search': [value: string];
152
+ 'scroll-end': [];
132
153
  }>();
133
154
 
134
155
  const openPopover = ref(false);
135
156
  const selectInputRef = ref<HTMLInputElement | null>(null);
136
157
  const { width: inputWidth } = useElementSize(selectInputRef);
158
+ const contentRef = ref<HTMLDivElement | null>(null);
159
+ const infiniteScrollReset = ref<(() => void) | null>(null);
160
+ const infiniteScrollLoading = ref(false);
137
161
 
138
162
  const inputWidthString = computed(() => {
139
163
  return `${inputWidth.value}px`;
@@ -153,6 +177,12 @@ watch(openPopover, () => {
153
177
  );
154
178
  scrollToOption(selectedOptionIndex, 'instant', 'center');
155
179
  }
180
+
181
+ if (openPopover.value && props.infiniteScroll) {
182
+ nextTick(() => {
183
+ setupInfiniteScroll();
184
+ });
185
+ }
156
186
  });
157
187
 
158
188
  const handleKeyDown = (event) => {
@@ -263,6 +293,79 @@ const filteredOptions = computed(() => {
263
293
  .includes(props.search?.toLowerCase()),
264
294
  );
265
295
  });
296
+
297
+ const setupInfiniteScroll = () => {
298
+ if (!props.infiniteScroll) {
299
+ return;
300
+ }
301
+
302
+ if (infiniteScrollReset.value) {
303
+ infiniteScrollReset.value();
304
+ infiniteScrollReset.value = null;
305
+ }
306
+
307
+ nextTick(() => {
308
+ const scrollElement = contentRef.value;
309
+
310
+ if (!scrollElement) {
311
+ return;
312
+ }
313
+
314
+ const { reset } = useInfiniteScroll(
315
+ scrollElement,
316
+ () => {
317
+ if (!infiniteScrollLoading.value) {
318
+ infiniteScrollLoading.value = true;
319
+ emit('scroll-end');
320
+ }
321
+ },
322
+ {
323
+ distance: props.infiniteScrollDistance,
324
+ canLoadMore: () => {
325
+ return (
326
+ props.infiniteScrollCanLoadMore() && !infiniteScrollLoading.value
327
+ );
328
+ },
329
+ },
330
+ );
331
+
332
+ infiniteScrollReset.value = reset;
333
+ });
334
+ };
335
+
336
+ const finishInfiniteScroll = () => {
337
+ infiniteScrollLoading.value = false;
338
+
339
+ if (openPopover.value && props.infiniteScroll) {
340
+ nextTick(() => {
341
+ setupInfiniteScroll();
342
+ });
343
+ }
344
+ };
345
+
346
+ const resetInfiniteScroll = () => {
347
+ infiniteScrollLoading.value = false;
348
+ if (infiniteScrollReset.value) {
349
+ infiniteScrollReset.value();
350
+ }
351
+
352
+ if (openPopover.value && props.infiniteScroll) {
353
+ nextTick(() => {
354
+ setupInfiniteScroll();
355
+ });
356
+ }
357
+ };
358
+
359
+ onBeforeUnmount(() => {
360
+ if (infiniteScrollReset.value) {
361
+ infiniteScrollReset.value();
362
+ }
363
+ });
364
+
365
+ defineExpose({
366
+ finishInfiniteScroll,
367
+ resetInfiniteScroll,
368
+ });
266
369
  </script>
267
370
 
268
371
  <style lang="scss" scoped>
@@ -270,16 +373,14 @@ const filteredOptions = computed(() => {
270
373
 
271
374
  :deep(.unnnic-select__input) {
272
375
  cursor: pointer;
273
- }
274
376
 
275
- :deep(.unnnic-select__input-search) {
276
- > .icon-left {
377
+ > .icon-right {
277
378
  color: $unnnic-color-fg-base;
278
379
  }
279
380
  }
280
381
 
281
- :deep(.unnnic-select__input) {
282
- > .icon-right {
382
+ :deep(.unnnic-select__input-search) {
383
+ > .icon-left {
283
384
  color: $unnnic-color-fg-base;
284
385
  }
285
386
  }
@@ -304,5 +405,13 @@ const filteredOptions = computed(() => {
304
405
  color: $unnnic-color-fg-muted;
305
406
  }
306
407
  }
408
+
409
+ &__infinite-loading {
410
+ display: flex;
411
+ justify-content: center;
412
+ align-items: center;
413
+ padding: $unnnic-space-2 0;
414
+ min-height: 40px;
415
+ }
307
416
  }
308
417
  </style>
@@ -8,7 +8,12 @@
8
8
  class="unnnic-switch__label"
9
9
  />
10
10
 
11
- <label :class="['unnnic-switch__input-wrapper', {'unnnic-switch__input-wrapper--disabled': disabled}]">
11
+ <label
12
+ :class="[
13
+ 'unnnic-switch__input-wrapper',
14
+ { 'unnnic-switch__input-wrapper--disabled': disabled },
15
+ ]"
16
+ >
12
17
  <input
13
18
  class="unnnic-switch__input"
14
19
  type="checkbox"
@@ -78,7 +83,7 @@ export default {
78
83
  type: String,
79
84
  default: '',
80
85
  },
81
-
86
+
82
87
  helper: {
83
88
  type: String,
84
89
  default: '',
@@ -176,7 +181,9 @@ $switch-height: 20px;
176
181
  background-repeat: no-repeat;
177
182
  background-position: 4px center;
178
183
 
179
- transition: 120ms linear background-position, 120ms linear background-color;
184
+ transition:
185
+ 120ms linear background-position,
186
+ 120ms linear background-color;
180
187
 
181
188
  cursor: pointer;
182
189
 
@@ -4,9 +4,7 @@ exports[`Tab.vue > matches the snapshot 1`] = `
4
4
  "<div data-v-b4e39fac="" class="tab size-md">
5
5
  <header data-v-b4e39fac="" class="tab-header">
6
6
  <ul data-v-b4e39fac="" class="tab-content">
7
- <li data-v-b4e39fac="" class="tab-head">tab1<div data-v-b3d24f2b="" class="unnnic-tooltip-trigger" data-testid="tooltip-trigger" data-state="closed" data-grace-area-trigger=""><span data-v-26446d8e="" data-v-b4e39fac="" class="unnnic-icon material-symbols-rounded unnnic-icon-scheme--fg-base unnnic-icon-size--sm unnnic-icon__size--sm" data-testid="material-icon" translate="no">help</span></div>
8
- <!--teleport start-->
9
- <!--teleport end-->
7
+ <li data-v-b4e39fac="" class="tab-head">tab1<div data-v-bf0cf546="" data-v-b4e39fac="" class="unnnic-tooltip"><span data-v-26446d8e="" data-v-b4e39fac="" class="unnnic-icon material-symbols-rounded unnnic-icon-scheme--fg-base unnnic-icon-size--sm unnnic-icon__size--sm" data-testid="material-icon" translate="no">help</span><span data-v-bf0cf546="" class="unnnic-tooltip-label unnnic-tooltip-label-bottom" data-testid="tooltip-label" style="left: 0px; top: 8px;">Tooltip text <br data-v-bf0cf546=""><!--v-if--></span></div>
10
8
  </li>
11
9
  <li data-v-b4e39fac="" class="tab-head tab-head--active">tab2
12
10
  <!--v-if-->
@@ -75,7 +75,10 @@
75
75
  {{ template?.footer }}
76
76
  </footer>
77
77
  </section>
78
- <footer v-if="hasButtons" class="unnnic-template-preview__buttons">
78
+ <footer
79
+ v-if="hasButtons"
80
+ class="unnnic-template-preview__buttons"
81
+ >
79
82
  <section
80
83
  v-for="(button, index) in template?.buttons"
81
84
  :key="`button-${index}`"
@@ -96,15 +99,15 @@
96
99
  </template>
97
100
 
98
101
  <script lang="ts" setup>
99
- import { computed } from "vue";
102
+ import { computed } from 'vue';
100
103
 
101
- import type { Template } from "./types";
104
+ import type { Template } from './types';
102
105
 
103
- import imagePreview from "../../assets/img/previews/image-preview.png";
104
- import documentPreview from "../../assets/img/previews/doc-preview.png";
105
- import videoPreview from "../../assets/img/previews/video-preview.png";
106
+ import imagePreview from '../../assets/img/previews/image-preview.png';
107
+ import documentPreview from '../../assets/img/previews/doc-preview.png';
108
+ import videoPreview from '../../assets/img/previews/video-preview.png';
106
109
 
107
- import UnnnicIcon from "../Icon.vue";
110
+ import UnnnicIcon from '../Icon.vue';
108
111
 
109
112
  interface Props {
110
113
  template?: Template | null;
@@ -115,30 +118,30 @@ const props = withDefaults(defineProps<Props>(), {
115
118
  });
116
119
 
117
120
  const hasHeader = computed(
118
- () => props.template?.header && props.template.header.type
121
+ () => props.template?.header && props.template.header.type,
119
122
  );
120
123
  const hasHeaderMedia = computed(
121
- () => !!props.template?.header && props.template.header.type === "MEDIA"
124
+ () => !!props.template?.header && props.template.header.type === 'MEDIA',
122
125
  );
123
126
  const hasBody = computed(
124
- () => !!props.template?.body && props.template.body.length > 0
127
+ () => !!props.template?.body && props.template.body.length > 0,
125
128
  );
126
129
  const hasFooter = computed(
127
- () => !!props.template?.footer && props.template.footer.length > 0
130
+ () => !!props.template?.footer && props.template.footer.length > 0,
128
131
  );
129
132
  const hasButtons = computed(
130
- () => !!props.template?.buttons && props.template.buttons.length > 0
133
+ () => !!props.template?.buttons && props.template.buttons.length > 0,
131
134
  );
132
135
  const parsedBody = computed(() => {
133
- if (!hasBody.value) return "";
136
+ if (!hasBody.value) return '';
134
137
 
135
138
  // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
136
139
  const result = props
137
- .template!.body!.replaceAll("\n", "<br/>")
138
- .replaceAll(/(?:\*)([^*<\n]+)(?:\*)/g, "<strong>$1</strong>")
139
- .replaceAll(/(?:_)([^_<\n]+)(?:_)/g, "<i>$1</i>")
140
- .replaceAll(/(?:~)([^~<\n]+)(?:~)/g, "<s>$1</s>")
141
- .replaceAll(/(?:```)([^```<\n]+)(?:```)/g, "<tt>$1</tt>")
140
+ .template!.body!.replaceAll('\n', '<br/>')
141
+ .replaceAll(/(?:\*)([^*<\n]+)(?:\*)/g, '<strong>$1</strong>')
142
+ .replaceAll(/(?:_)([^_<\n]+)(?:_)/g, '<i>$1</i>')
143
+ .replaceAll(/(?:~)([^~<\n]+)(?:~)/g, '<s>$1</s>')
144
+ .replaceAll(/(?:```)([^```<\n]+)(?:```)/g, '<tt>$1</tt>')
142
145
  .replaceAll(/{{.*?}}/g, (match) => `<strong>${match}</strong>`);
143
146
 
144
147
  return result;
@@ -146,19 +149,19 @@ const parsedBody = computed(() => {
146
149
 
147
150
  const getButtonIcon = (buttonType) => {
148
151
  const buttonMapper = {
149
- PHONE_NUMBER: "phone",
150
- URL: "open_in_new",
151
- COPY_CODE: "content_copy",
152
- FLOW: "",
153
- QUICK_REPLY: "reply",
152
+ PHONE_NUMBER: 'phone',
153
+ URL: 'open_in_new',
154
+ COPY_CODE: 'content_copy',
155
+ FLOW: '',
156
+ QUICK_REPLY: 'reply',
154
157
  };
155
158
 
156
- return buttonMapper[buttonType] || "";
159
+ return buttonMapper[buttonType] || '';
157
160
  };
158
161
  </script>
159
162
 
160
163
  <style lang="scss" scoped>
161
- @use "@/assets/scss/unnnic" as *;
164
+ @use '@/assets/scss/unnnic' as *;
162
165
 
163
166
  .unnnic-template-preview {
164
167
  display: flex;