@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.
- package/CHANGELOG.md +11 -0
- package/README.md +1 -9
- package/dist/{es-0d53b5b2.mjs → es-b51fb49d.mjs} +1 -1
- package/dist/{index-d7070de8.mjs → index-88ec0352.mjs} +26529 -29321
- package/dist/index.d.ts +1485 -4826
- package/dist/{pt-br-bf4e1f97.mjs → pt-br-cd9f0dbc.mjs} +1 -1
- package/dist/style.css +1 -1
- package/dist/unnnic.mjs +204 -232
- package/dist/unnnic.umd.js +46 -50
- package/package.json +2 -3
- package/src/assets/scss/tailwind.scss +0 -8
- package/src/components/Alert/__tests__/__snapshots__/Alert.spec.js.snap +1 -1
- package/src/components/ChartFunnel/DefaultFunnel/ChartDefaultFunnelBase.vue +1 -2
- package/src/components/ChartFunnel/SvgFunnel/ChartFunnelTwoRows.vue +60 -61
- package/src/components/CheckboxGroup/CheckboxGroup.vue +7 -5
- package/src/components/Chip/Chip.vue +1 -1
- package/src/components/DatePicker/DatePicker.vue +1 -10
- package/src/components/Drawer/Drawer.vue +270 -180
- package/src/components/Drawer/__tests__/Drawer.spec.js +43 -32
- package/src/components/Drawer/__tests__/__snapshots__/Drawer.spec.js.snap +19 -18
- package/src/components/FormElement/FormElement.vue +96 -87
- package/src/components/InputDatePicker/InputDatePicker.vue +73 -68
- package/src/components/InputDatePicker/__test__/InputDatePicker.spec.js +24 -31
- package/src/components/ModalDialog/ModalDialog.vue +154 -63
- package/src/components/ModalDialog/__tests__/ModalDialog.spec.js +210 -30
- package/src/components/ModalDialog/__tests__/__snapshots__/ModalDialog.spec.js.snap +22 -1
- package/src/components/Radio/Radio.vue +12 -6
- package/src/components/Radio/__test__/Radio.spec.js +3 -1
- package/src/components/RadioGroup/RadioGroup.vue +18 -10
- package/src/components/Select/__tests__/Select.spec.js +74 -0
- package/src/components/Select/__tests__/SelectItem.spec.js +35 -15
- package/src/components/Select/__tests__/__snapshots__/Select.spec.js.snap +18 -0
- package/src/components/Select/index.vue +131 -22
- package/src/components/Switch/Switch.vue +10 -3
- package/src/components/Tab/__test__/__snapshots__/Tab.spec.js.snap +1 -3
- package/src/components/TemplatePreview/TemplatePreview.vue +28 -25
- package/src/components/TemplatePreview/TemplatePreviewModal.vue +10 -10
- package/src/components/TemplatePreview/types.d.ts +3 -3
- package/src/components/Toast/Toast.vue +1 -4
- package/src/components/Toast/ToastManager.ts +1 -4
- package/src/components/Toast/__tests__/ToastManager.spec.js +6 -10
- package/src/components/ToolTip/ToolTip.vue +177 -25
- package/src/components/ToolTip/__tests__/ToolTip.spec.js +61 -339
- package/src/components/index.ts +0 -56
- package/src/components/ui/popover/PopoverContent.vue +4 -7
- package/src/components/ui/popover/PopoverTrigger.vue +1 -5
- package/src/index.ts +2 -9
- package/src/stories/Drawer.stories.js +1 -1
- package/src/stories/ModalDialog.mdx +0 -3
- package/src/stories/ModalDialog.stories.js +1 -96
- package/src/stories/Select.stories.js +86 -0
- package/src/stories/TemplatePreview.stories.js +27 -27
- package/src/stories/TemplatePreviewModal.stories.js +31 -31
- package/src/components/ui/dialog/Dialog.vue +0 -19
- package/src/components/ui/dialog/DialogClose.vue +0 -29
- package/src/components/ui/dialog/DialogContent.vue +0 -140
- package/src/components/ui/dialog/DialogFooter.vue +0 -50
- package/src/components/ui/dialog/DialogHeader.vue +0 -83
- package/src/components/ui/dialog/DialogTitle.vue +0 -38
- package/src/components/ui/dialog/DialogTrigger.vue +0 -16
- package/src/components/ui/dialog/index.ts +0 -7
- package/src/components/ui/drawer/Drawer.vue +0 -27
- package/src/components/ui/drawer/DrawerClose.vue +0 -31
- package/src/components/ui/drawer/DrawerContent.vue +0 -113
- package/src/components/ui/drawer/DrawerDescription.vue +0 -40
- package/src/components/ui/drawer/DrawerFooter.vue +0 -38
- package/src/components/ui/drawer/DrawerHeader.vue +0 -57
- package/src/components/ui/drawer/DrawerOverlay.vue +0 -33
- package/src/components/ui/drawer/DrawerTitle.vue +0 -37
- package/src/components/ui/drawer/DrawerTrigger.vue +0 -31
- package/src/components/ui/drawer/index.ts +0 -10
- package/src/components/ui/tooltip/Tooltip.vue +0 -21
- package/src/components/ui/tooltip/TooltipContent.vue +0 -77
- package/src/components/ui/tooltip/TooltipTrigger.vue +0 -24
- package/src/components/ui/tooltip/index.ts +0 -3
- package/src/lib/__tests__/teleport-target.spec.ts +0 -73
- package/src/lib/layer-manager.ts +0 -64
- package/src/lib/teleport-target.ts +0 -46
- package/src/stories/Dialog.stories.js +0 -832
- package/src/stories/DrawerNext.stories.js +0 -611
- package/src/stories/LayerManager.docs.mdx +0 -40
- package/src/stories/LayerManager.stories.js +0 -407
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<section
|
|
3
|
-
|
|
4
|
-
|
|
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(
|
|
72
|
-
|
|
73
|
-
|
|
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
|
|
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(
|
|
80
|
-
|
|
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(
|
|
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 =
|
|
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({
|
|
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({
|
|
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({
|
|
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
|
|
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
|
-
<
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
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
|
-
|
|
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-
|
|
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
|
|
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:
|
|
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-
|
|
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
|
|
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
|
|
102
|
+
import { computed } from 'vue';
|
|
100
103
|
|
|
101
|
-
import type { Template } from
|
|
104
|
+
import type { Template } from './types';
|
|
102
105
|
|
|
103
|
-
import imagePreview from
|
|
104
|
-
import documentPreview from
|
|
105
|
-
import videoPreview from
|
|
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
|
|
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 ===
|
|
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(
|
|
138
|
-
.replaceAll(/(?:\*)([^*<\n]+)(?:\*)/g,
|
|
139
|
-
.replaceAll(/(?:_)([^_<\n]+)(?:_)/g,
|
|
140
|
-
.replaceAll(/(?:~)([^~<\n]+)(?:~)/g,
|
|
141
|
-
.replaceAll(/(?:```)([^```<\n]+)(?:```)/g,
|
|
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:
|
|
150
|
-
URL:
|
|
151
|
-
COPY_CODE:
|
|
152
|
-
FLOW:
|
|
153
|
-
QUICK_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
|
|
164
|
+
@use '@/assets/scss/unnnic' as *;
|
|
162
165
|
|
|
163
166
|
.unnnic-template-preview {
|
|
164
167
|
display: flex;
|