@weni/unnnic-system 2.6.0 → 2.6.1-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": "2.6.0",
3
+ "version": "2.6.1-alpha.0",
4
4
  "type": "commonjs",
5
5
  "files": [
6
6
  "dist",
@@ -82,4 +82,4 @@
82
82
  "vitest": "^1.6.0",
83
83
  "vue-eslint-parser": "^9.4.2"
84
84
  }
85
- }
85
+ }
@@ -0,0 +1,126 @@
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
+ export default {
23
+ name: 'UnnnicTour',
24
+
25
+ components: {
26
+ TourMask,
27
+ TourPopover,
28
+ },
29
+
30
+ props: {
31
+ steps: {
32
+ required: true,
33
+ type: Array,
34
+ },
35
+ },
36
+
37
+ data() {
38
+ return {
39
+ isTourActive: false,
40
+ currentStep: 1,
41
+ maskRect: {
42
+ x: 0,
43
+ y: 0,
44
+ width: 0,
45
+ height: 0,
46
+ },
47
+ };
48
+ },
49
+
50
+ computed: {
51
+ currentStepOptions() {
52
+ return this.steps[this.currentStep - 1];
53
+ },
54
+ stepStyle() {
55
+ const attachedElement = this.currentStepOptions.attachedElement;
56
+
57
+ if (!this.isTourActive || !attachedElement) {
58
+ return {};
59
+ }
60
+
61
+ const { top, left, width, height } =
62
+ attachedElement.getBoundingClientRect();
63
+ return {
64
+ top: `${top}px`,
65
+ left: `${left}px`,
66
+ width: `${width}px`,
67
+ height: `${height}px`,
68
+ };
69
+ },
70
+ },
71
+
72
+ watch: {
73
+ currentStep() {
74
+ this.updateMaskRect();
75
+ },
76
+ isTourActive(isTourActive) {
77
+ if (isTourActive) {
78
+ this.updateMaskRect();
79
+ }
80
+ },
81
+ },
82
+
83
+ methods: {
84
+ start() {
85
+ this.isTourActive = true;
86
+ this.currentStep = 1;
87
+ },
88
+ end() {
89
+ this.isTourActive = false;
90
+ this.currentStep = 1;
91
+ },
92
+ handleStep(step) {
93
+ if (this.currentStep < this.steps.length) {
94
+ this.currentStep = step;
95
+ }
96
+ },
97
+ nextStep() {
98
+ const { handleStep, steps, currentStep, end } = this;
99
+
100
+ if (currentStep === steps.length) {
101
+ end();
102
+ return;
103
+ }
104
+
105
+ handleStep(currentStep + 1);
106
+ },
107
+ updateMaskRect() {
108
+ const attachedElement = this.currentStepOptions?.attachedElement;
109
+ const { padding } = this.currentStepOptions;
110
+
111
+ if (!attachedElement) {
112
+ return;
113
+ }
114
+
115
+ const { top, left, width, height } =
116
+ attachedElement.getBoundingClientRect();
117
+ this.maskRect = {
118
+ x: left - (padding?.horizontal || 0) / 2,
119
+ y: top - (padding?.vertical || 0) / 2,
120
+ width: width + (padding?.horizontal || 0),
121
+ height: height + (padding?.vertical || 0),
122
+ };
123
+ },
124
+ },
125
+ };
126
+ </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,198 @@
1
+ import UnnnicTour from '../components/Tour/Tour.vue';
2
+ import UnnnicCard from '../components/Card/Card.vue';
3
+
4
+ export default {
5
+ title: 'Example/Tour',
6
+ component: UnnnicTour,
7
+ argTypes: {},
8
+ render: (args) => ({
9
+ components: {
10
+ UnnnicTour,
11
+ UnnnicCard,
12
+ },
13
+ setup() {
14
+ return { args };
15
+ },
16
+ methods: {
17
+ startTour() {
18
+ this.$refs.tour.start();
19
+ },
20
+ handleTourStep(step) {
21
+ this.$refs.tour.start();
22
+ this.$refs.tour.handleStep(step);
23
+ },
24
+ },
25
+ mounted() {
26
+ this.$nextTick(() => {
27
+ args.steps[0].attachedElement = this.$refs.step1.$el;
28
+ args.steps[1].attachedElement = this.$refs.step2.$el;
29
+ args.steps[2].attachedElement = this.$refs.step3.$el;
30
+ args.steps[3].attachedElement = this.$refs.step4.$el;
31
+ });
32
+ },
33
+ template: `
34
+ <div style="height: 50%; width: 30%; border: 1px solid #ccc; padding: 16px; margin: auto">
35
+ <button @click="startTour">Start tour</button>
36
+ <button @click="handleTourStep(3)">Go to step 3</button>
37
+ <UnnnicCard
38
+ type="default"
39
+ title="This is the title"
40
+ description="This is the description"
41
+ ref="step1"
42
+ />
43
+ <UnnnicCard
44
+ type="default"
45
+ title="This is the title"
46
+ description="This is the description"
47
+ ref="step2"
48
+ />
49
+ <UnnnicCard
50
+ type="default"
51
+ title="This is the title"
52
+ description="This is the description"
53
+ ref="step3"
54
+ />
55
+ <UnnnicCard
56
+ type="default"
57
+ title="This is the title"
58
+ description="This is the description"
59
+ ref="step4"
60
+ />
61
+ <unnnic-tour v-bind="args" ref="tour" />
62
+ </div>
63
+ `,
64
+ }),
65
+ };
66
+
67
+ export const Default = {
68
+ args: {
69
+ steps: [
70
+ {
71
+ title: 'Step 1',
72
+ description:
73
+ 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec dapibus euismod nibh vel elementum. Integer nisi lectus, hendrerit aliquet tellus nec, volutpat porttitor erat. Vivamus tincidunt sit amet ex non. ',
74
+ attachedElement: null,
75
+ popoverPosition: 'right',
76
+ },
77
+ {
78
+ title: 'Step 2',
79
+ description:
80
+ 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec luctus dui orci. ',
81
+ attachedElement: null,
82
+ popoverPosition: 'bottom',
83
+ },
84
+ {
85
+ title: 'Step 3',
86
+ description:
87
+ 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed condimentum accumsan erat, at bibendum nisi mollis et. Aliquam venenatis tristique.',
88
+ attachedElement: null,
89
+ popoverPosition: 'left',
90
+ },
91
+ {
92
+ title: 'Step 4',
93
+ description:
94
+ 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Mauris ex purus, ullamcorper sed nunc eu. ',
95
+ attachedElement: null,
96
+ popoverPosition: 'top',
97
+ },
98
+ ],
99
+ },
100
+ };
101
+
102
+ export const WithPadding = {
103
+ args: {
104
+ steps: [
105
+ {
106
+ title: 'Step 1',
107
+ description:
108
+ 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec dapibus euismod nibh vel elementum. Integer nisi lectus, hendrerit aliquet tellus nec, volutpat porttitor erat. Vivamus tincidunt sit amet ex non. ',
109
+ attachedElement: null,
110
+ popoverPosition: 'right',
111
+ padding: {
112
+ vertical: 20,
113
+ horizontal: 20,
114
+ },
115
+ },
116
+ ],
117
+ },
118
+ render: (args) => ({
119
+ components: {
120
+ UnnnicTour,
121
+ UnnnicCard,
122
+ },
123
+ setup() {
124
+ return { args };
125
+ },
126
+ methods: {
127
+ startTour() {
128
+ this.$refs.tour.start();
129
+ },
130
+ },
131
+ mounted() {
132
+ this.$nextTick(() => {
133
+ args.steps[0].attachedElement = this.$refs.step1.$el;
134
+ });
135
+ },
136
+ template: `
137
+ <div style="height: 50%; width: 30%; border: 1px solid #ccc; padding: 16px; margin: auto">
138
+ <button @click="startTour">Start tour</button>
139
+ <UnnnicCard
140
+ type="default"
141
+ title="This is the title"
142
+ description="This is the description"
143
+ ref="step1"
144
+ />
145
+ <unnnic-tour v-bind="args" ref="tour" />
146
+ </div>
147
+ `,
148
+ }),
149
+ };
150
+
151
+ export const WithNegativePadding = {
152
+ args: {
153
+ steps: [
154
+ {
155
+ title: 'Step 1',
156
+ description:
157
+ 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec dapibus euismod nibh vel elementum. Integer nisi lectus, hendrerit aliquet tellus nec, volutpat porttitor erat. Vivamus tincidunt sit amet ex non. ',
158
+ attachedElement: null,
159
+ popoverPosition: 'right',
160
+ padding: {
161
+ vertical: -30,
162
+ horizontal: -10,
163
+ },
164
+ },
165
+ ],
166
+ },
167
+ render: (args) => ({
168
+ components: {
169
+ UnnnicTour,
170
+ UnnnicCard,
171
+ },
172
+ setup() {
173
+ return { args };
174
+ },
175
+ methods: {
176
+ startTour() {
177
+ this.$refs.tour.start();
178
+ },
179
+ },
180
+ mounted() {
181
+ this.$nextTick(() => {
182
+ args.steps[0].attachedElement = this.$refs.step1.$el;
183
+ });
184
+ },
185
+ template: `
186
+ <div style="height: 50%; width: 30%; border: 1px solid #ccc; padding: 16px; margin: auto">
187
+ <button @click="startTour">Start tour</button>
188
+ <UnnnicCard
189
+ type="default"
190
+ title="This is the title"
191
+ description="This is the description"
192
+ ref="step1"
193
+ />
194
+ <unnnic-tour v-bind="args" ref="tour" />
195
+ </div>
196
+ `,
197
+ }),
198
+ };