@mozaic-ds/vue 0.36.1 → 0.37.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/dist/mozaic-vue.adeo.css +10 -10
- package/dist/mozaic-vue.adeo.umd.js +92 -128
- package/dist/mozaic-vue.common.js +92 -128
- package/dist/mozaic-vue.common.js.map +1 -1
- package/dist/mozaic-vue.css +1 -1
- package/dist/mozaic-vue.umd.js +92 -128
- package/dist/mozaic-vue.umd.js.map +1 -1
- package/dist/mozaic-vue.umd.min.js +2 -2
- package/dist/mozaic-vue.umd.min.js.map +1 -1
- package/package.json +5 -5
- package/src/components/autocomplete/MAutocomplete.vue +7 -1
- package/src/components/dropdown/MDropdown.vue +26 -14
- package/src/components/header/MHeader.vue +374 -0
- package/src/components/header/composable/useScrollDirection.js +61 -0
- package/src/components/header/index.js +7 -0
- package/src/components/kpi/MKpi.vue +218 -422
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mozaic-ds/vue",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.37.0",
|
|
4
4
|
"description": "Vue.js implementation of Mozaic Design System",
|
|
5
5
|
"author": "Adeo - Mozaic Design System",
|
|
6
6
|
"scripts": {
|
|
@@ -26,10 +26,10 @@
|
|
|
26
26
|
"@linusborg/vue-simple-portal": "^0.1.5",
|
|
27
27
|
"@mozaic-ds/css-dev-tools": "1.50.0",
|
|
28
28
|
"@mozaic-ds/icons": "1.52.0",
|
|
29
|
-
"@mozaic-ds/styles": "1.
|
|
29
|
+
"@mozaic-ds/styles": "1.53.0",
|
|
30
30
|
"@mozaic-ds/web-fonts": "1.22.0",
|
|
31
31
|
"core-js": "^3.27.2",
|
|
32
|
-
"libphonenumber-js": "^1.10.
|
|
32
|
+
"libphonenumber-js": "^1.10.19",
|
|
33
33
|
"vue": "^2.6.14",
|
|
34
34
|
"vue-country-flag": "2.3.2"
|
|
35
35
|
},
|
|
@@ -42,14 +42,14 @@
|
|
|
42
42
|
"@vue/compiler-sfc": "^3.2.45",
|
|
43
43
|
"@vue/eslint-config-prettier": "^7.0.0",
|
|
44
44
|
"babel-eslint": "^10.1.0",
|
|
45
|
-
"eslint": "^8.
|
|
45
|
+
"eslint": "^8.33.0",
|
|
46
46
|
"eslint-config-prettier": "^8.6.0",
|
|
47
47
|
"eslint-plugin-vue": "^9.9.0",
|
|
48
48
|
"postcss": "^8.4.21",
|
|
49
49
|
"postcss-loader": "^7.0.2",
|
|
50
50
|
"postcss-scss": "^4.0.6",
|
|
51
51
|
"prettier": "^2.8.3",
|
|
52
|
-
"sass": "^1.
|
|
52
|
+
"sass": "^1.58.0",
|
|
53
53
|
"sass-loader": "^13.2.0",
|
|
54
54
|
"vue-template-compiler": "^2.7.14"
|
|
55
55
|
},
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
<div
|
|
3
3
|
v-click-outside="closeListBox"
|
|
4
4
|
class="mc-autocomplete"
|
|
5
|
-
:class="classObject"
|
|
5
|
+
:class="[classObject, cssFieldElementClass]"
|
|
6
6
|
:style="setStyles"
|
|
7
7
|
>
|
|
8
8
|
<div class="mc-autocomplete__main">
|
|
@@ -100,6 +100,12 @@ export default {
|
|
|
100
100
|
},
|
|
101
101
|
},
|
|
102
102
|
|
|
103
|
+
inject: {
|
|
104
|
+
cssFieldElementClass: {
|
|
105
|
+
default: '',
|
|
106
|
+
},
|
|
107
|
+
},
|
|
108
|
+
|
|
103
109
|
model: {
|
|
104
110
|
event: 'change',
|
|
105
111
|
},
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
ref="dropdown"
|
|
4
4
|
v-click-outside="closeListBox"
|
|
5
5
|
class="mc-dropdown"
|
|
6
|
-
:class="classObject"
|
|
6
|
+
:class="[classObject, cssFieldElementClass]"
|
|
7
7
|
:style="setStyles"
|
|
8
8
|
>
|
|
9
9
|
<div class="mc-dropdown__main">
|
|
@@ -86,6 +86,12 @@ export default {
|
|
|
86
86
|
},
|
|
87
87
|
},
|
|
88
88
|
|
|
89
|
+
inject: {
|
|
90
|
+
cssFieldElementClass: {
|
|
91
|
+
default: '',
|
|
92
|
+
},
|
|
93
|
+
},
|
|
94
|
+
|
|
89
95
|
model: {
|
|
90
96
|
event: 'change',
|
|
91
97
|
},
|
|
@@ -228,22 +234,28 @@ export default {
|
|
|
228
234
|
},
|
|
229
235
|
immediate: true,
|
|
230
236
|
},
|
|
231
|
-
listboxValue:
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
237
|
+
listboxValue: {
|
|
238
|
+
handler: function (val) {
|
|
239
|
+
const selectedItems = this.getSelectedItems(val);
|
|
240
|
+
const seletedLabels = selectedItems.map(
|
|
241
|
+
(item) => item[this.dataTextExpr]
|
|
242
|
+
);
|
|
236
243
|
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
244
|
+
this.buttonValue =
|
|
245
|
+
((!val || val.length === 0) && this.placeholder) ||
|
|
246
|
+
seletedLabels.join(', ');
|
|
240
247
|
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
248
|
+
if (this.multiple) {
|
|
249
|
+
this.tagValue = val;
|
|
250
|
+
}
|
|
251
|
+
},
|
|
252
|
+
immediate: true,
|
|
244
253
|
},
|
|
245
|
-
tagValue:
|
|
246
|
-
|
|
254
|
+
tagValue: {
|
|
255
|
+
handler() {
|
|
256
|
+
this.setTagWidth();
|
|
257
|
+
},
|
|
258
|
+
immediate: true,
|
|
247
259
|
},
|
|
248
260
|
openState: function (val) {
|
|
249
261
|
const eventName = val ? 'open' : 'close';
|
|
@@ -0,0 +1,374 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div
|
|
3
|
+
ref="header"
|
|
4
|
+
class="mc-header"
|
|
5
|
+
:class="{
|
|
6
|
+
'mc-header--shadow': showShadow,
|
|
7
|
+
'mc-header--animate': animate,
|
|
8
|
+
'mc-header--sticky': sticky,
|
|
9
|
+
'mc-header--up': direction === 'up',
|
|
10
|
+
'mc-header--down': direction === 'down',
|
|
11
|
+
}"
|
|
12
|
+
>
|
|
13
|
+
<div class="mc-header__container">
|
|
14
|
+
<MBreadcrumb
|
|
15
|
+
v-if="hasBreadcrumb"
|
|
16
|
+
class="mc-header__breadcrumb mc-breadcrumb--no-padding"
|
|
17
|
+
:responsive="true"
|
|
18
|
+
:items="items"
|
|
19
|
+
/>
|
|
20
|
+
<div class="mc-header__content">
|
|
21
|
+
<div class="mc-header__content-main">
|
|
22
|
+
<component
|
|
23
|
+
:is="backHref ? 'a' : 'button'"
|
|
24
|
+
v-if="hasBack"
|
|
25
|
+
class="mc-header__back"
|
|
26
|
+
:href="backHref ?? null"
|
|
27
|
+
:type="!backHref ? 'button' : null"
|
|
28
|
+
@click="!backHref && $emit('click-back')"
|
|
29
|
+
>
|
|
30
|
+
<MIcon name="ArrowBack24" class="mc-header__back-icon" />
|
|
31
|
+
<span class="mc-header__back-label">
|
|
32
|
+
{{ backButtonLabel }}
|
|
33
|
+
</span>
|
|
34
|
+
</component>
|
|
35
|
+
|
|
36
|
+
<h1 class="mc-header__title" :class="`mc-header__title--${size}`">
|
|
37
|
+
{{ title }}
|
|
38
|
+
</h1>
|
|
39
|
+
</div>
|
|
40
|
+
<div
|
|
41
|
+
v-if="subTitle || status"
|
|
42
|
+
class="mc-header__content-aside"
|
|
43
|
+
:class="{ 'can-navigate': hasBack }"
|
|
44
|
+
>
|
|
45
|
+
<span v-if="subTitle" class="mc-header__subtitle">
|
|
46
|
+
{{ subTitle }}
|
|
47
|
+
</span>
|
|
48
|
+
<MBadge v-if="status" :type="status.type">{{ status.text }}</MBadge>
|
|
49
|
+
</div>
|
|
50
|
+
</div>
|
|
51
|
+
|
|
52
|
+
<div v-if="$slots.icons || $slots.selector" class="mc-header__actions">
|
|
53
|
+
<div v-if="$slots.icons" class="mc-header__icons">
|
|
54
|
+
<slot name="icons" />
|
|
55
|
+
</div>
|
|
56
|
+
<div v-if="$slots.selector" class="mc-header__selector">
|
|
57
|
+
<slot name="selector" />
|
|
58
|
+
</div>
|
|
59
|
+
</div>
|
|
60
|
+
</div>
|
|
61
|
+
|
|
62
|
+
<div
|
|
63
|
+
v-if="showTabs && hasTabs"
|
|
64
|
+
class="mc-header__tabs mc-divider-top mc-divider-top--light"
|
|
65
|
+
>
|
|
66
|
+
<MTab
|
|
67
|
+
:tabs="tabs"
|
|
68
|
+
:shadow="false"
|
|
69
|
+
:active-index="selectedTabIndex"
|
|
70
|
+
@tab-clicked="onTabClicked"
|
|
71
|
+
/>
|
|
72
|
+
</div>
|
|
73
|
+
</div>
|
|
74
|
+
</template>
|
|
75
|
+
|
|
76
|
+
<script>
|
|
77
|
+
import MBreadcrumb from '../breadcrumb/MBreadcrumb.vue';
|
|
78
|
+
import MIcon from '../icon/MIcon.vue';
|
|
79
|
+
import MBadge from '../badge/MBadge.vue';
|
|
80
|
+
import MTab from '../tabs/MTab.vue';
|
|
81
|
+
|
|
82
|
+
export default {
|
|
83
|
+
name: 'MHeader',
|
|
84
|
+
components: {
|
|
85
|
+
MBreadcrumb,
|
|
86
|
+
MIcon,
|
|
87
|
+
MBadge,
|
|
88
|
+
MTab,
|
|
89
|
+
},
|
|
90
|
+
props: {
|
|
91
|
+
/** Title of header. */
|
|
92
|
+
title: {
|
|
93
|
+
type: String,
|
|
94
|
+
required: true,
|
|
95
|
+
},
|
|
96
|
+
/** Sub title of header. */
|
|
97
|
+
subTitle: {
|
|
98
|
+
type: String,
|
|
99
|
+
default: undefined,
|
|
100
|
+
},
|
|
101
|
+
/** The badge information. */
|
|
102
|
+
status: {
|
|
103
|
+
type: Object,
|
|
104
|
+
default: undefined,
|
|
105
|
+
},
|
|
106
|
+
/** Items used by breadcrumb. */
|
|
107
|
+
items: {
|
|
108
|
+
type: Array,
|
|
109
|
+
default: () => [],
|
|
110
|
+
},
|
|
111
|
+
/** Items used by tabs. */
|
|
112
|
+
tabs: {
|
|
113
|
+
type: Array,
|
|
114
|
+
default: () => [],
|
|
115
|
+
},
|
|
116
|
+
/** Set selected tab by index. */
|
|
117
|
+
selectedTabIndex: {
|
|
118
|
+
type: Number,
|
|
119
|
+
default: -1,
|
|
120
|
+
},
|
|
121
|
+
/** Display shadow to bottom of the header. */
|
|
122
|
+
showShadow: {
|
|
123
|
+
type: Boolean,
|
|
124
|
+
default: false,
|
|
125
|
+
},
|
|
126
|
+
/** Show breadcrumb or not. */
|
|
127
|
+
showBreadcrumb: {
|
|
128
|
+
type: Boolean,
|
|
129
|
+
default: false,
|
|
130
|
+
},
|
|
131
|
+
/** Show tabs or not. */
|
|
132
|
+
showTabs: {
|
|
133
|
+
type: Boolean,
|
|
134
|
+
default: false,
|
|
135
|
+
},
|
|
136
|
+
/** Show back button or not. */
|
|
137
|
+
showBack: {
|
|
138
|
+
type: Boolean,
|
|
139
|
+
default: false,
|
|
140
|
+
},
|
|
141
|
+
/** Animate when user scroll. */
|
|
142
|
+
animate: {
|
|
143
|
+
type: Boolean,
|
|
144
|
+
default: false,
|
|
145
|
+
},
|
|
146
|
+
/** Set sticky position. */
|
|
147
|
+
sticky: {
|
|
148
|
+
type: Boolean,
|
|
149
|
+
default: false,
|
|
150
|
+
},
|
|
151
|
+
/** The size of title (l, s, m, or xl).*/
|
|
152
|
+
size: {
|
|
153
|
+
type: String,
|
|
154
|
+
default: 'l',
|
|
155
|
+
validator: (value) => ['s', 'm', 'l', 'xl'].includes(value),
|
|
156
|
+
},
|
|
157
|
+
/** Label of the back button. Useful for accessibilty reason */
|
|
158
|
+
backButtonLabel: {
|
|
159
|
+
type: String,
|
|
160
|
+
default: 'Back',
|
|
161
|
+
},
|
|
162
|
+
/** Href url of the back button */
|
|
163
|
+
backHref: {
|
|
164
|
+
type: String,
|
|
165
|
+
default: undefined,
|
|
166
|
+
},
|
|
167
|
+
},
|
|
168
|
+
data: () => ({
|
|
169
|
+
originalScrollPos: 0,
|
|
170
|
+
localDirection: undefined,
|
|
171
|
+
}),
|
|
172
|
+
computed: {
|
|
173
|
+
hasTabs() {
|
|
174
|
+
return this.tabs.length > 0;
|
|
175
|
+
},
|
|
176
|
+
hasBreadcrumb() {
|
|
177
|
+
return this.showBreadcrumb && this.items.length > 1;
|
|
178
|
+
},
|
|
179
|
+
hasBack() {
|
|
180
|
+
return this.showBack && !this.hasBreadcrumb;
|
|
181
|
+
},
|
|
182
|
+
direction() {
|
|
183
|
+
return this.localDirection;
|
|
184
|
+
},
|
|
185
|
+
},
|
|
186
|
+
watch: {
|
|
187
|
+
animate: {
|
|
188
|
+
handler(val) {
|
|
189
|
+
if (val) {
|
|
190
|
+
window.addEventListener('scroll', this.onScroll, { capture: true });
|
|
191
|
+
} else {
|
|
192
|
+
window.removeEventListener('scroll', this.onScroll, {
|
|
193
|
+
capture: true,
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
},
|
|
197
|
+
immediate: true,
|
|
198
|
+
},
|
|
199
|
+
},
|
|
200
|
+
beforeDestroy() {
|
|
201
|
+
if (this.animate) {
|
|
202
|
+
window.removeEventListener('scroll', this.onScroll, { capture: true });
|
|
203
|
+
}
|
|
204
|
+
},
|
|
205
|
+
methods: {
|
|
206
|
+
onTabClicked(_, e) {
|
|
207
|
+
const tabItemIndex = this.tabs.findIndex((tab) => tab.id === e.id);
|
|
208
|
+
|
|
209
|
+
if (tabItemIndex > -1 && this.selectedTabIndex !== tabItemIndex) {
|
|
210
|
+
this.$emit('update:selectedTabIndex', tabItemIndex);
|
|
211
|
+
}
|
|
212
|
+
},
|
|
213
|
+
onScroll(e) {
|
|
214
|
+
const element = this.$refs.header;
|
|
215
|
+
|
|
216
|
+
if (element) {
|
|
217
|
+
const container = element.parentElement;
|
|
218
|
+
|
|
219
|
+
if (container && e.target === container) {
|
|
220
|
+
const currentScrollPos = container.scrollTop;
|
|
221
|
+
|
|
222
|
+
if (currentScrollPos <= element.offsetHeight) {
|
|
223
|
+
this.localDirection = undefined;
|
|
224
|
+
} else if (currentScrollPos < this.originalScrollPos) {
|
|
225
|
+
this.localDirection = 'up';
|
|
226
|
+
} else {
|
|
227
|
+
this.localDirection = 'down';
|
|
228
|
+
}
|
|
229
|
+
this.originalScrollPos = currentScrollPos;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
},
|
|
233
|
+
},
|
|
234
|
+
};
|
|
235
|
+
</script>
|
|
236
|
+
|
|
237
|
+
<style lang="scss">
|
|
238
|
+
@import 'settings-tools/all-settings';
|
|
239
|
+
@import 'components/c.divider';
|
|
240
|
+
.mc-header {
|
|
241
|
+
@include set-font-face();
|
|
242
|
+
background-color: $color-grey-000;
|
|
243
|
+
z-index: 10;
|
|
244
|
+
&__container {
|
|
245
|
+
display: grid;
|
|
246
|
+
padding: $mu050 $mu150 $mu100;
|
|
247
|
+
@include set-from-screen('l') {
|
|
248
|
+
padding-left: $mu200;
|
|
249
|
+
padding-right: $mu200;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
&__breadcrumb {
|
|
253
|
+
grid-column: 1;
|
|
254
|
+
grid-row: auto;
|
|
255
|
+
}
|
|
256
|
+
&__content {
|
|
257
|
+
color: $color-font-darkest;
|
|
258
|
+
grid-column: 1;
|
|
259
|
+
grid-row: auto;
|
|
260
|
+
padding-top: $mu050;
|
|
261
|
+
&-main {
|
|
262
|
+
display: flex;
|
|
263
|
+
align-items: center;
|
|
264
|
+
gap: $mu100;
|
|
265
|
+
&:not(:only-child) {
|
|
266
|
+
margin-bottom: $mu025;
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
&-aside {
|
|
270
|
+
display: flex;
|
|
271
|
+
align-items: flex-start;
|
|
272
|
+
flex-direction: column;
|
|
273
|
+
gap: $mu050;
|
|
274
|
+
padding-bottom: $mu050;
|
|
275
|
+
&.can-navigate {
|
|
276
|
+
padding-left: $mu300;
|
|
277
|
+
}
|
|
278
|
+
@include set-from-screen('l') {
|
|
279
|
+
align-items: center;
|
|
280
|
+
flex-direction: row;
|
|
281
|
+
gap: $mu100;
|
|
282
|
+
padding-bottom: 0;
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
&__back {
|
|
287
|
+
@include unstyle-button();
|
|
288
|
+
align-items: center;
|
|
289
|
+
background-color: transparent;
|
|
290
|
+
justify-content: center;
|
|
291
|
+
color: currentColor;
|
|
292
|
+
display: flex;
|
|
293
|
+
height: $mu200;
|
|
294
|
+
width: $mu200;
|
|
295
|
+
&-icon {
|
|
296
|
+
color: $color-grey-800;
|
|
297
|
+
height: $mu150;
|
|
298
|
+
width: $mu150;
|
|
299
|
+
}
|
|
300
|
+
&-label {
|
|
301
|
+
@include visually-hidden();
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
&__title {
|
|
305
|
+
margin-top: 0;
|
|
306
|
+
margin-bottom: 0;
|
|
307
|
+
&--s {
|
|
308
|
+
@include set-font-scale('07', 'm'); // 23px / 32px
|
|
309
|
+
}
|
|
310
|
+
&--m {
|
|
311
|
+
@include set-font-scale('08', 'm'); // 28px / 36px
|
|
312
|
+
}
|
|
313
|
+
&--l {
|
|
314
|
+
@include set-font-scale('09', 'm'); // 34px / 44px
|
|
315
|
+
}
|
|
316
|
+
&--xl {
|
|
317
|
+
@include set-font-scale('10', 'm'); // 41px / 56px
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
&__subtitle {
|
|
321
|
+
@include set-font-scale('05', 'm'); // 16px / 22px
|
|
322
|
+
}
|
|
323
|
+
&__actions,
|
|
324
|
+
&__icons {
|
|
325
|
+
display: flex;
|
|
326
|
+
gap: $mu150;
|
|
327
|
+
}
|
|
328
|
+
&__actions {
|
|
329
|
+
align-items: flex-end;
|
|
330
|
+
align-self: flex-start;
|
|
331
|
+
grid-column: 1;
|
|
332
|
+
grid-row: 1;
|
|
333
|
+
justify-self: end;
|
|
334
|
+
margin-bottom: $mu050;
|
|
335
|
+
min-height: $mu250;
|
|
336
|
+
@include set-from-screen('l') {
|
|
337
|
+
grid-column: 2;
|
|
338
|
+
grid-row: 1;
|
|
339
|
+
margin-bottom: 0;
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
&__selector {
|
|
343
|
+
&:not(:only-child) {
|
|
344
|
+
@include set-divider('left', 'light', '100%', 's');
|
|
345
|
+
padding-left: px-to-rem(25);
|
|
346
|
+
position: relative;
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
&__tabs {
|
|
350
|
+
padding-left: $mu200;
|
|
351
|
+
padding-right: $mu200;
|
|
352
|
+
}
|
|
353
|
+
// Modifiers
|
|
354
|
+
&--shadow {
|
|
355
|
+
@include set-box-shadow('s');
|
|
356
|
+
}
|
|
357
|
+
&--animate {
|
|
358
|
+
left: 0;
|
|
359
|
+
position: absolute;
|
|
360
|
+
right: 0;
|
|
361
|
+
transition: transform ease-out 0.4s;
|
|
362
|
+
}
|
|
363
|
+
&--sticky {
|
|
364
|
+
position: sticky;
|
|
365
|
+
top: 0;
|
|
366
|
+
}
|
|
367
|
+
&--up {
|
|
368
|
+
transform: translateY(0);
|
|
369
|
+
}
|
|
370
|
+
&--down {
|
|
371
|
+
transform: translateY(calc(-100% - 5px));
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
</style>
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { unrefElement } from '@vueuse/core';
|
|
2
|
+
import { computed, onBeforeUnmount, reactive, watch } from 'vue';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Get information about scrolling animation.
|
|
6
|
+
* @param header @type {Ref<HTMLElement | undefined>} The header element ref.
|
|
7
|
+
* @param isAnimate @type {Ref<boolean>} The computed property indicate if animation is active.
|
|
8
|
+
* @returns Information about scrolling.
|
|
9
|
+
*/
|
|
10
|
+
export function useScrollDirection(header, isAnimate) {
|
|
11
|
+
let originalScrollPos = 0;
|
|
12
|
+
|
|
13
|
+
const state = reactive({
|
|
14
|
+
direction: undefined
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
const direction = computed(() => state.direction);
|
|
18
|
+
|
|
19
|
+
function onScroll(e) {
|
|
20
|
+
const element = unrefElement(header);
|
|
21
|
+
|
|
22
|
+
if (element) {
|
|
23
|
+
const container = element.parentElement;
|
|
24
|
+
|
|
25
|
+
if (container && e.target === container) {
|
|
26
|
+
const currentScrollPos = container.scrollTop;
|
|
27
|
+
|
|
28
|
+
if (currentScrollPos <= element.offsetHeight) {
|
|
29
|
+
state.direction = undefined;
|
|
30
|
+
} else if (currentScrollPos < originalScrollPos) {
|
|
31
|
+
state.direction = 'up';
|
|
32
|
+
} else {
|
|
33
|
+
state.direction = 'down';
|
|
34
|
+
}
|
|
35
|
+
originalScrollPos = currentScrollPos;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
watch(
|
|
41
|
+
() => isAnimate.value,
|
|
42
|
+
() => {
|
|
43
|
+
if (isAnimate.value) {
|
|
44
|
+
window.addEventListener('scroll', onScroll, { capture: true });
|
|
45
|
+
} else {
|
|
46
|
+
window.removeEventListener('scroll', onScroll, { capture: true });
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
{ immediate: true }
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
onBeforeUnmount(() => {
|
|
53
|
+
if (isAnimate.value) {
|
|
54
|
+
window.removeEventListener('scroll', onScroll, { capture: true });
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
return {
|
|
59
|
+
direction
|
|
60
|
+
};
|
|
61
|
+
}
|