@mozaic-ds/vue 0.36.0 → 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 +94 -129
- package/dist/mozaic-vue.common.js +94 -129
- package/dist/mozaic-vue.common.js.map +1 -1
- package/dist/mozaic-vue.css +1 -1
- package/dist/mozaic-vue.umd.js +94 -129
- 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 +28 -17
- 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
|
},
|
|
@@ -197,9 +203,8 @@ export default {
|
|
|
197
203
|
return this.listboxValue.length.toString() + ' ' + this.tagLabel;
|
|
198
204
|
},
|
|
199
205
|
getListboxValue() {
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
: this.listboxValue !== undefined;
|
|
206
|
+
const val = this.multiple ? this.listboxValue.length : this.listboxValue;
|
|
207
|
+
return !!val;
|
|
203
208
|
},
|
|
204
209
|
isClearable() {
|
|
205
210
|
return this.clearable && this.getListboxValue && !this.disabled;
|
|
@@ -229,22 +234,28 @@ export default {
|
|
|
229
234
|
},
|
|
230
235
|
immediate: true,
|
|
231
236
|
},
|
|
232
|
-
listboxValue:
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
+
listboxValue: {
|
|
238
|
+
handler: function (val) {
|
|
239
|
+
const selectedItems = this.getSelectedItems(val);
|
|
240
|
+
const seletedLabels = selectedItems.map(
|
|
241
|
+
(item) => item[this.dataTextExpr]
|
|
242
|
+
);
|
|
237
243
|
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
244
|
+
this.buttonValue =
|
|
245
|
+
((!val || val.length === 0) && this.placeholder) ||
|
|
246
|
+
seletedLabels.join(', ');
|
|
241
247
|
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
248
|
+
if (this.multiple) {
|
|
249
|
+
this.tagValue = val;
|
|
250
|
+
}
|
|
251
|
+
},
|
|
252
|
+
immediate: true,
|
|
245
253
|
},
|
|
246
|
-
tagValue:
|
|
247
|
-
|
|
254
|
+
tagValue: {
|
|
255
|
+
handler() {
|
|
256
|
+
this.setTagWidth();
|
|
257
|
+
},
|
|
258
|
+
immediate: true,
|
|
248
259
|
},
|
|
249
260
|
openState: function (val) {
|
|
250
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
|
+
}
|