@weni/unnnic-system 2.6.1 → 2.7.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 +13 -0
- package/dist/style.css +1 -1
- package/dist/unnnic.mjs +4806 -4456
- package/dist/unnnic.umd.js +18 -18
- package/package.json +1 -1
- package/src/components/EmojiPicker/EmojiPicker.vue +9 -1
- package/src/components/Tour/Tour.vue +129 -0
- package/src/components/Tour/TourMask.vue +135 -0
- package/src/components/Tour/TourPopover.vue +229 -0
- package/src/components/Tour/propsValidation.js +82 -0
- package/src/components/index.js +3 -0
- package/src/stories/EmojiPicker.stories.js +53 -5
- package/src/stories/Tour.stories.js +216 -0
package/package.json
CHANGED
|
@@ -13,6 +13,14 @@ import data from '@emoji-mart/data/sets/14/apple.json';
|
|
|
13
13
|
import { Picker } from 'emoji-mart/';
|
|
14
14
|
|
|
15
15
|
export default {
|
|
16
|
+
name: 'UnnnicEmojiPicker',
|
|
17
|
+
props: {
|
|
18
|
+
returnName: {
|
|
19
|
+
type: Boolean,
|
|
20
|
+
default: false,
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
emits: ['close', 'emojiSelected'],
|
|
16
24
|
computed: {
|
|
17
25
|
emojiPickerPreferences() {
|
|
18
26
|
return {
|
|
@@ -42,7 +50,7 @@ export default {
|
|
|
42
50
|
});
|
|
43
51
|
},
|
|
44
52
|
onEmojiSelect(emoji) {
|
|
45
|
-
this.$emit('emojiSelected', emoji.native);
|
|
53
|
+
this.$emit('emojiSelected', this.returnName ? emoji.id : emoji.native);
|
|
46
54
|
},
|
|
47
55
|
},
|
|
48
56
|
};
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<TourMask
|
|
3
|
+
v-if="isTourActive"
|
|
4
|
+
:maskRect="maskRect"
|
|
5
|
+
/>
|
|
6
|
+
|
|
7
|
+
<TourPopover
|
|
8
|
+
v-if="isTourActive"
|
|
9
|
+
:step="currentStepOptions"
|
|
10
|
+
:stepsLength="steps.length"
|
|
11
|
+
:currentStep="currentStep"
|
|
12
|
+
:attachedElement="currentStepOptions.attachedElement"
|
|
13
|
+
@end="end"
|
|
14
|
+
@next-step="nextStep"
|
|
15
|
+
/>
|
|
16
|
+
</template>
|
|
17
|
+
|
|
18
|
+
<script>
|
|
19
|
+
import TourMask from './TourMask.vue';
|
|
20
|
+
import TourPopover from './TourPopover.vue';
|
|
21
|
+
|
|
22
|
+
import { validateSteps } from './propsValidation';
|
|
23
|
+
|
|
24
|
+
export default {
|
|
25
|
+
name: 'UnnnicTour',
|
|
26
|
+
|
|
27
|
+
components: {
|
|
28
|
+
TourMask,
|
|
29
|
+
TourPopover,
|
|
30
|
+
},
|
|
31
|
+
|
|
32
|
+
props: {
|
|
33
|
+
steps: {
|
|
34
|
+
required: true,
|
|
35
|
+
type: Array,
|
|
36
|
+
validator: validateSteps,
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
|
|
40
|
+
data() {
|
|
41
|
+
return {
|
|
42
|
+
isTourActive: false,
|
|
43
|
+
currentStep: 1,
|
|
44
|
+
maskRect: {
|
|
45
|
+
x: 0,
|
|
46
|
+
y: 0,
|
|
47
|
+
width: 0,
|
|
48
|
+
height: 0,
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
},
|
|
52
|
+
|
|
53
|
+
computed: {
|
|
54
|
+
currentStepOptions() {
|
|
55
|
+
return this.steps[this.currentStep - 1];
|
|
56
|
+
},
|
|
57
|
+
stepStyle() {
|
|
58
|
+
const attachedElement = this.currentStepOptions.attachedElement;
|
|
59
|
+
|
|
60
|
+
if (!this.isTourActive || !attachedElement) {
|
|
61
|
+
return {};
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const { top, left, width, height } =
|
|
65
|
+
attachedElement.getBoundingClientRect();
|
|
66
|
+
return {
|
|
67
|
+
top: `${top}px`,
|
|
68
|
+
left: `${left}px`,
|
|
69
|
+
width: `${width}px`,
|
|
70
|
+
height: `${height}px`,
|
|
71
|
+
};
|
|
72
|
+
},
|
|
73
|
+
},
|
|
74
|
+
|
|
75
|
+
watch: {
|
|
76
|
+
currentStep() {
|
|
77
|
+
this.updateMaskRect();
|
|
78
|
+
},
|
|
79
|
+
isTourActive(isTourActive) {
|
|
80
|
+
if (isTourActive) {
|
|
81
|
+
this.updateMaskRect();
|
|
82
|
+
}
|
|
83
|
+
},
|
|
84
|
+
},
|
|
85
|
+
|
|
86
|
+
methods: {
|
|
87
|
+
start() {
|
|
88
|
+
this.isTourActive = true;
|
|
89
|
+
this.currentStep = 1;
|
|
90
|
+
},
|
|
91
|
+
end() {
|
|
92
|
+
this.isTourActive = false;
|
|
93
|
+
this.currentStep = 1;
|
|
94
|
+
},
|
|
95
|
+
handleStep(step) {
|
|
96
|
+
if (this.currentStep < this.steps.length) {
|
|
97
|
+
this.currentStep = step;
|
|
98
|
+
}
|
|
99
|
+
},
|
|
100
|
+
nextStep() {
|
|
101
|
+
const { handleStep, steps, currentStep, end } = this;
|
|
102
|
+
|
|
103
|
+
if (currentStep === steps.length) {
|
|
104
|
+
end();
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
handleStep(currentStep + 1);
|
|
109
|
+
},
|
|
110
|
+
updateMaskRect() {
|
|
111
|
+
const attachedElement = this.currentStepOptions?.attachedElement;
|
|
112
|
+
const { padding } = this.currentStepOptions;
|
|
113
|
+
|
|
114
|
+
if (!attachedElement) {
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const { top, left, width, height } =
|
|
119
|
+
attachedElement.getBoundingClientRect();
|
|
120
|
+
this.maskRect = {
|
|
121
|
+
x: left - (padding?.horizontal || 0) / 2,
|
|
122
|
+
y: top - (padding?.vertical || 0) / 2,
|
|
123
|
+
width: width + (padding?.horizontal || 0),
|
|
124
|
+
height: height + (padding?.vertical || 0),
|
|
125
|
+
};
|
|
126
|
+
},
|
|
127
|
+
},
|
|
128
|
+
};
|
|
129
|
+
</script>
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<section class="unnnic-tour__mask">
|
|
3
|
+
<svg
|
|
4
|
+
class="mask__svg"
|
|
5
|
+
width="100%"
|
|
6
|
+
height="100%"
|
|
7
|
+
>
|
|
8
|
+
<defs>
|
|
9
|
+
<mask
|
|
10
|
+
id="svgTourMask"
|
|
11
|
+
x="0"
|
|
12
|
+
y="0"
|
|
13
|
+
width="100%"
|
|
14
|
+
height="100%"
|
|
15
|
+
>
|
|
16
|
+
<rect
|
|
17
|
+
x="0"
|
|
18
|
+
y="0"
|
|
19
|
+
width="100vw"
|
|
20
|
+
height="100vh"
|
|
21
|
+
fill="white"
|
|
22
|
+
/>
|
|
23
|
+
<rect
|
|
24
|
+
:x="maskRect.x"
|
|
25
|
+
:y="maskRect.y"
|
|
26
|
+
:width="maskRect.width"
|
|
27
|
+
:height="maskRect.height"
|
|
28
|
+
rx="4"
|
|
29
|
+
fill="black"
|
|
30
|
+
class="unnnic-tour__step"
|
|
31
|
+
/>
|
|
32
|
+
</mask>
|
|
33
|
+
</defs>
|
|
34
|
+
<rect
|
|
35
|
+
x="0"
|
|
36
|
+
y="0"
|
|
37
|
+
width="100%"
|
|
38
|
+
height="100%"
|
|
39
|
+
mask="url(#svgTourMask)"
|
|
40
|
+
class="unnnic-tour__overlay"
|
|
41
|
+
/>
|
|
42
|
+
<rect
|
|
43
|
+
v-for="(rect, index) of clickBlockerRects"
|
|
44
|
+
:key="index"
|
|
45
|
+
:x="rect.x"
|
|
46
|
+
:y="rect.y"
|
|
47
|
+
:width="rect.width"
|
|
48
|
+
:height="rect.height"
|
|
49
|
+
:style="rect.style"
|
|
50
|
+
fill="transparent"
|
|
51
|
+
pointer-events="auto"
|
|
52
|
+
/>
|
|
53
|
+
</svg>
|
|
54
|
+
</section>
|
|
55
|
+
</template>
|
|
56
|
+
|
|
57
|
+
<script>
|
|
58
|
+
export default {
|
|
59
|
+
name: 'TourMask',
|
|
60
|
+
|
|
61
|
+
props: {
|
|
62
|
+
maskRect: {
|
|
63
|
+
type: Object,
|
|
64
|
+
required: true,
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
|
|
68
|
+
computed: {
|
|
69
|
+
clickBlockerRects() {
|
|
70
|
+
const { maskRect } = this;
|
|
71
|
+
return [
|
|
72
|
+
{
|
|
73
|
+
x: '0',
|
|
74
|
+
y: '0',
|
|
75
|
+
width: '100%',
|
|
76
|
+
height: maskRect.y,
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
x: '0',
|
|
80
|
+
y: '0',
|
|
81
|
+
width: maskRect.x,
|
|
82
|
+
height: '100%',
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
x: '0',
|
|
86
|
+
y: maskRect.y + maskRect.height,
|
|
87
|
+
style: { height: `calc(100vh - ${maskRect.y}px)` },
|
|
88
|
+
width: '100%',
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
x: maskRect.x + maskRect.width,
|
|
92
|
+
y: '0',
|
|
93
|
+
style: { width: `calc(100vw - ${maskRect.x}px)` },
|
|
94
|
+
height: '100%',
|
|
95
|
+
},
|
|
96
|
+
];
|
|
97
|
+
},
|
|
98
|
+
},
|
|
99
|
+
};
|
|
100
|
+
</script>
|
|
101
|
+
|
|
102
|
+
<style lang="scss" scoped>
|
|
103
|
+
@import '../../assets/scss/unnnic.scss';
|
|
104
|
+
|
|
105
|
+
.unnnic-tour {
|
|
106
|
+
&__mask {
|
|
107
|
+
position: fixed;
|
|
108
|
+
z-index: 1000;
|
|
109
|
+
|
|
110
|
+
inset: 0;
|
|
111
|
+
|
|
112
|
+
pointer-events: none;
|
|
113
|
+
|
|
114
|
+
width: 100%;
|
|
115
|
+
height: 100%;
|
|
116
|
+
|
|
117
|
+
&__svg {
|
|
118
|
+
position: absolute;
|
|
119
|
+
top: 0;
|
|
120
|
+
left: 0;
|
|
121
|
+
|
|
122
|
+
width: 100%;
|
|
123
|
+
height: 100%;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
&__step {
|
|
128
|
+
transition: all 0.2s ease;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
&__overlay {
|
|
132
|
+
fill: rgba($unnnic-color-neutral-black, $unnnic-opacity-level-overlay);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
</style>
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<section
|
|
3
|
+
ref="popover"
|
|
4
|
+
:class="['unnnic-tour__popover', step.popoverPosition]"
|
|
5
|
+
:style="style"
|
|
6
|
+
>
|
|
7
|
+
<header class="popover__header">
|
|
8
|
+
<h1 class="header__title">{{ step.title }}</h1>
|
|
9
|
+
<p
|
|
10
|
+
class="header__close-tour"
|
|
11
|
+
@click="$emit('end')"
|
|
12
|
+
>
|
|
13
|
+
{{ i18n('close_tour') }}
|
|
14
|
+
</p>
|
|
15
|
+
</header>
|
|
16
|
+
<p class="popover__description">{{ step.description }}</p>
|
|
17
|
+
<UnnnicButton
|
|
18
|
+
type="primary"
|
|
19
|
+
@click="$emit('nextStep')"
|
|
20
|
+
>
|
|
21
|
+
{{ i18n('understood') }} {{ currentStep }}/{{ stepsLength }}
|
|
22
|
+
</UnnnicButton>
|
|
23
|
+
</section>
|
|
24
|
+
</template>
|
|
25
|
+
|
|
26
|
+
<script>
|
|
27
|
+
import UnnnicI18n from '@/mixins/i18n';
|
|
28
|
+
|
|
29
|
+
import UnnnicButton from '@/components/Button/Button.vue';
|
|
30
|
+
|
|
31
|
+
export default {
|
|
32
|
+
name: 'UnnnicTourPopover',
|
|
33
|
+
|
|
34
|
+
components: {
|
|
35
|
+
UnnnicButton,
|
|
36
|
+
},
|
|
37
|
+
|
|
38
|
+
mixins: [UnnnicI18n],
|
|
39
|
+
|
|
40
|
+
props: {
|
|
41
|
+
step: {
|
|
42
|
+
type: Object,
|
|
43
|
+
required: true,
|
|
44
|
+
},
|
|
45
|
+
currentStep: {
|
|
46
|
+
type: Number,
|
|
47
|
+
required: true,
|
|
48
|
+
},
|
|
49
|
+
stepsLength: {
|
|
50
|
+
type: Number,
|
|
51
|
+
required: true,
|
|
52
|
+
},
|
|
53
|
+
attachedElement: {
|
|
54
|
+
type: Element,
|
|
55
|
+
required: true,
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
|
|
59
|
+
emits: ['end', 'nextStep'],
|
|
60
|
+
|
|
61
|
+
data() {
|
|
62
|
+
return {
|
|
63
|
+
style: {},
|
|
64
|
+
defaultTranslations: {
|
|
65
|
+
close_tour: {
|
|
66
|
+
'pt-br': 'Fechar tour',
|
|
67
|
+
en: 'Close tour',
|
|
68
|
+
es: 'Cerrar recorrido',
|
|
69
|
+
},
|
|
70
|
+
understood: {
|
|
71
|
+
'pt-br': 'Entendi',
|
|
72
|
+
en: 'I understood',
|
|
73
|
+
es: 'Entendí',
|
|
74
|
+
},
|
|
75
|
+
},
|
|
76
|
+
};
|
|
77
|
+
},
|
|
78
|
+
|
|
79
|
+
watch: {
|
|
80
|
+
attachedElement: {
|
|
81
|
+
immediate: true,
|
|
82
|
+
deep: true,
|
|
83
|
+
handler() {
|
|
84
|
+
this.$nextTick(() => {
|
|
85
|
+
this.updatePopoverStyle();
|
|
86
|
+
});
|
|
87
|
+
},
|
|
88
|
+
},
|
|
89
|
+
},
|
|
90
|
+
|
|
91
|
+
methods: {
|
|
92
|
+
updatePopoverStyle() {
|
|
93
|
+
const { attachedElement, step } = this;
|
|
94
|
+
|
|
95
|
+
const popoverElement = this.$refs.popover;
|
|
96
|
+
|
|
97
|
+
if (!attachedElement || !popoverElement) {
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const {
|
|
102
|
+
top: attachedTop,
|
|
103
|
+
left: attachedLeft,
|
|
104
|
+
width: attachedWidth,
|
|
105
|
+
height: attachedHeight,
|
|
106
|
+
} = attachedElement.getBoundingClientRect();
|
|
107
|
+
const popoverArrowSize = 12;
|
|
108
|
+
const popoverArrowMargin = 2;
|
|
109
|
+
const popoverArrowSpacing = popoverArrowSize + popoverArrowMargin;
|
|
110
|
+
const popoverWidth = popoverElement?.offsetWidth;
|
|
111
|
+
const popoverHeight = popoverElement?.offsetHeight;
|
|
112
|
+
|
|
113
|
+
const translateXMap = {
|
|
114
|
+
top: attachedLeft - (popoverWidth - attachedWidth) / 2,
|
|
115
|
+
bottom: attachedLeft - (popoverWidth - attachedWidth) / 2,
|
|
116
|
+
right: attachedLeft + attachedWidth + popoverArrowSpacing,
|
|
117
|
+
left: attachedLeft - popoverWidth - popoverArrowSpacing,
|
|
118
|
+
};
|
|
119
|
+
const translateYMap = {
|
|
120
|
+
top: attachedTop - popoverHeight - popoverArrowSpacing,
|
|
121
|
+
bottom: attachedTop + attachedHeight + popoverArrowSpacing,
|
|
122
|
+
right: attachedTop - popoverHeight / 2 + attachedHeight / 2,
|
|
123
|
+
left: attachedTop - popoverHeight / 2 + attachedHeight / 2,
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
let style = {
|
|
127
|
+
transform: `translate(${translateXMap[step.popoverPosition]}px, ${translateYMap[step.popoverPosition]}px)`,
|
|
128
|
+
transition: 'transform .3s ease',
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
this.style = style;
|
|
132
|
+
},
|
|
133
|
+
},
|
|
134
|
+
};
|
|
135
|
+
</script>
|
|
136
|
+
<style lang="scss" scoped>
|
|
137
|
+
@import '../../assets/scss/unnnic.scss';
|
|
138
|
+
|
|
139
|
+
* {
|
|
140
|
+
margin: 0;
|
|
141
|
+
padding: 0;
|
|
142
|
+
box-sizing: border-box;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
.unnnic-tour__popover {
|
|
146
|
+
display: flex;
|
|
147
|
+
flex-direction: column;
|
|
148
|
+
gap: $unnnic-spacing-ant;
|
|
149
|
+
position: fixed;
|
|
150
|
+
top: 0;
|
|
151
|
+
left: 0;
|
|
152
|
+
|
|
153
|
+
z-index: 1001;
|
|
154
|
+
|
|
155
|
+
border-radius: $unnnic-border-radius-sm;
|
|
156
|
+
box-shadow: $unnnic-shadow-level-near;
|
|
157
|
+
background: $unnnic-color-background-white;
|
|
158
|
+
padding: $unnnic-spacing-ant;
|
|
159
|
+
|
|
160
|
+
width: max-content;
|
|
161
|
+
max-width: 30vw;
|
|
162
|
+
|
|
163
|
+
color: $unnnic-color-neutral-darkest;
|
|
164
|
+
font-family: $unnnic-font-family-secondary;
|
|
165
|
+
font-size: $unnnic-font-size-body-gt;
|
|
166
|
+
font-weight: $unnnic-font-weight-regular;
|
|
167
|
+
|
|
168
|
+
$arrowSize: $unnnic-icon-size-xs;
|
|
169
|
+
$arrowHalfSize: calc($arrowSize / 2);
|
|
170
|
+
|
|
171
|
+
&::before {
|
|
172
|
+
content: '';
|
|
173
|
+
position: absolute;
|
|
174
|
+
|
|
175
|
+
width: $unnnic-icon-size-xs;
|
|
176
|
+
height: $unnnic-icon-size-xs;
|
|
177
|
+
|
|
178
|
+
border-radius: calc($unnnic-border-radius-sm / 2);
|
|
179
|
+
|
|
180
|
+
background: linear-gradient(
|
|
181
|
+
-45deg,
|
|
182
|
+
$unnnic-color-background-white 50%,
|
|
183
|
+
transparent 50%
|
|
184
|
+
);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
&.top::before {
|
|
188
|
+
left: 50%;
|
|
189
|
+
bottom: -$arrowHalfSize;
|
|
190
|
+
transform: translateX(-50%) rotate(45deg);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
&.bottom::before {
|
|
194
|
+
top: -$arrowHalfSize;
|
|
195
|
+
left: 50%;
|
|
196
|
+
transform: translateX(-50%) rotate(225deg);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
&.left::before {
|
|
200
|
+
right: -$arrowHalfSize;
|
|
201
|
+
top: 50%;
|
|
202
|
+
transform: translateY(-50%) rotate(315deg);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
&.right::before {
|
|
206
|
+
left: -$arrowHalfSize;
|
|
207
|
+
top: 50%;
|
|
208
|
+
transform: translateY(-50%) rotate(135deg);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
.popover__header {
|
|
212
|
+
display: flex;
|
|
213
|
+
justify-content: space-between;
|
|
214
|
+
align-items: center;
|
|
215
|
+
gap: $unnnic-spacing-nano;
|
|
216
|
+
|
|
217
|
+
.header__title {
|
|
218
|
+
font-weight: $unnnic-font-weight-bold;
|
|
219
|
+
font-size: $unnnic-font-size-body-gt;
|
|
220
|
+
}
|
|
221
|
+
.header__close-tour {
|
|
222
|
+
cursor: pointer;
|
|
223
|
+
|
|
224
|
+
font-size: $unnnic-font-size-body-md;
|
|
225
|
+
text-decoration-line: underline;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
</style>
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
const validateStep = (step) => {
|
|
2
|
+
const { title, description, attachedElement, popoverPosition } = step;
|
|
3
|
+
const hasObrigatoryProps =
|
|
4
|
+
'title' in step &&
|
|
5
|
+
'description' in step &&
|
|
6
|
+
'attachedElement' in step &&
|
|
7
|
+
'popoverPosition' in step;
|
|
8
|
+
|
|
9
|
+
if (!hasObrigatoryProps) {
|
|
10
|
+
throw new Error(
|
|
11
|
+
'Each item in "steps" must have "title", "description", "attachedElement" and "popoverPosition".',
|
|
12
|
+
);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const isTitleInvalid = typeof title !== 'string' || !title.trim();
|
|
16
|
+
if (isTitleInvalid) {
|
|
17
|
+
throw new Error('"title" must be a padded string');
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const isDescriptionInvalid =
|
|
21
|
+
typeof description !== 'string' || !description.trim();
|
|
22
|
+
if (isDescriptionInvalid) {
|
|
23
|
+
throw new Error('"description" must be a padded string');
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const isAttachedElementInvalid = !(attachedElement instanceof HTMLElement);
|
|
27
|
+
if (isAttachedElementInvalid) {
|
|
28
|
+
throw new Error('"attachedElement" must be an HTML element.');
|
|
29
|
+
}
|
|
30
|
+
const validPopoverPositions = ['top', 'bottom', 'left', 'right'];
|
|
31
|
+
const isPopoverPositionInvalid =
|
|
32
|
+
!validPopoverPositions.includes(popoverPosition);
|
|
33
|
+
if (isPopoverPositionInvalid) {
|
|
34
|
+
throw new Error(
|
|
35
|
+
'"popoverPosition" must be one of "top", "bottom", "left", or "right".',
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if ('padding' in step) {
|
|
40
|
+
const isPaddingInvalidType =
|
|
41
|
+
typeof step.padding !== 'object' || step.padding === null;
|
|
42
|
+
if (isPaddingInvalidType) {
|
|
43
|
+
throw new Error(
|
|
44
|
+
'Each item in "steps" that contains "padding" must assign it as an object.',
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const isPaddingEmpty = !(step.padding.vertical || step.padding.horizontal);
|
|
49
|
+
if (isPaddingEmpty) {
|
|
50
|
+
throw new Error(
|
|
51
|
+
'Each item in "steps" that contains "padding" must assign it as an object with "vertical" and/or "horizontal" keys.',
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const isPaddingVerticalInvalid =
|
|
56
|
+
'vertical' in step.padding && typeof step.padding.vertical !== 'number';
|
|
57
|
+
if (isPaddingVerticalInvalid) {
|
|
58
|
+
throw new Error('"vertical" in "padding" must be a number.');
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const isPaddingHorizontalInvalid =
|
|
62
|
+
'horizontal' in step.padding &&
|
|
63
|
+
typeof step.padding.horizontal !== 'number';
|
|
64
|
+
if (isPaddingHorizontalInvalid) {
|
|
65
|
+
throw new Error('"horizontal" in "padding" must be a number.');
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
export const validateSteps = (steps) => {
|
|
71
|
+
if (!Array.isArray(steps)) {
|
|
72
|
+
throw new Error('Property steps needs to be a padded array.');
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (!steps?.[0]) {
|
|
76
|
+
throw new Error('Steps property cannot be an empty array.');
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
steps.forEach(validateStep);
|
|
80
|
+
|
|
81
|
+
return true;
|
|
82
|
+
};
|
package/src/components/index.js
CHANGED
|
@@ -82,6 +82,7 @@ import Drawer from './Drawer/Drawer.vue';
|
|
|
82
82
|
import TableNext from './TableNext/TableNext.vue';
|
|
83
83
|
import ModalNext from './ModalNext/ModalNext.vue';
|
|
84
84
|
import ModalDialog from './ModalDialog/ModalDialog.vue';
|
|
85
|
+
import Tour from './Tour/Tour.vue';
|
|
85
86
|
|
|
86
87
|
export const components = {
|
|
87
88
|
unnnicFormElement: formElement,
|
|
@@ -169,6 +170,7 @@ export const components = {
|
|
|
169
170
|
unnnicDisclaimer: Disclaimer,
|
|
170
171
|
unnnicDrawer: Drawer,
|
|
171
172
|
unnnicTableNext: TableNext,
|
|
173
|
+
unnnicTour: Tour,
|
|
172
174
|
};
|
|
173
175
|
|
|
174
176
|
export const unnnicFontSize = fontSize;
|
|
@@ -257,3 +259,4 @@ export const unnnicChartFunnel = ChartFunnel;
|
|
|
257
259
|
export const unnnicDisclaimer = Disclaimer;
|
|
258
260
|
export const unnnicDrawer = Drawer;
|
|
259
261
|
export const unnnicTableNext = TableNext;
|
|
262
|
+
export const unnnicTour = Tour;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
1
|
+
import UnnnicButton from '../components/Button/Button.vue';
|
|
2
|
+
import UnnnicEmojiPicker from '../components/EmojiPicker/EmojiPicker.vue';
|
|
3
3
|
|
|
4
4
|
export default {
|
|
5
5
|
title: 'Form/EmojiPicker',
|
|
@@ -12,11 +12,11 @@ export default {
|
|
|
12
12
|
};
|
|
13
13
|
|
|
14
14
|
export const Default = () => ({
|
|
15
|
-
components: {
|
|
15
|
+
components: { UnnnicEmojiPicker, UnnnicButton },
|
|
16
16
|
template: `
|
|
17
17
|
<section style="position: relative;">
|
|
18
|
-
<
|
|
19
|
-
<
|
|
18
|
+
<UnnnicButton @click.stop="handleEmojiPicker" type="secondary" iconCenter="emoji" size="small"/>
|
|
19
|
+
<UnnnicEmojiPicker
|
|
20
20
|
v-show="isEmojiPickerOpen"
|
|
21
21
|
@emojiSelected="handleInput"
|
|
22
22
|
@close="closeEmojiPicker"
|
|
@@ -57,3 +57,51 @@ export const Default = () => ({
|
|
|
57
57
|
},
|
|
58
58
|
},
|
|
59
59
|
});
|
|
60
|
+
|
|
61
|
+
export const EmojiName = () => ({
|
|
62
|
+
components: { UnnnicEmojiPicker, UnnnicButton },
|
|
63
|
+
template: `
|
|
64
|
+
<section style="position: relative;">
|
|
65
|
+
<UnnnicButton @click.stop="handleEmojiPicker" type="secondary" iconCenter="emoji" size="small"/>
|
|
66
|
+
<UnnnicEmojiPicker
|
|
67
|
+
v-show="isEmojiPickerOpen"
|
|
68
|
+
returnName
|
|
69
|
+
@emojiSelected="handleInput"
|
|
70
|
+
@close="closeEmojiPicker"
|
|
71
|
+
/>
|
|
72
|
+
<input
|
|
73
|
+
placeholder="..."
|
|
74
|
+
:value="inputValue"
|
|
75
|
+
@input="handleInput"
|
|
76
|
+
/>
|
|
77
|
+
</section>
|
|
78
|
+
`,
|
|
79
|
+
data() {
|
|
80
|
+
return {
|
|
81
|
+
isEmojiPickerOpen: false,
|
|
82
|
+
inputValue: '',
|
|
83
|
+
};
|
|
84
|
+
},
|
|
85
|
+
methods: {
|
|
86
|
+
openEmojiPicker() {
|
|
87
|
+
this.isEmojiPickerOpen = true;
|
|
88
|
+
},
|
|
89
|
+
closeEmojiPicker() {
|
|
90
|
+
this.isEmojiPickerOpen = false;
|
|
91
|
+
},
|
|
92
|
+
handleEmojiPicker() {
|
|
93
|
+
if (this.isEmojiPickerOpen) {
|
|
94
|
+
this.closeEmojiPicker();
|
|
95
|
+
} else {
|
|
96
|
+
this.openEmojiPicker();
|
|
97
|
+
}
|
|
98
|
+
},
|
|
99
|
+
handleInput(event) {
|
|
100
|
+
if (typeof event === 'string') {
|
|
101
|
+
this.inputValue = event;
|
|
102
|
+
} else {
|
|
103
|
+
this.inputValue = event.target.value;
|
|
104
|
+
}
|
|
105
|
+
},
|
|
106
|
+
},
|
|
107
|
+
});
|