@lilaquadrat/design-core 0.1.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/README.md +236 -0
- package/html/index.html +25 -0
- package/html/index.mail.html +15 -0
- package/html/index.server.html +29 -0
- package/package.json +148 -0
- package/src/client-entry.ts +28 -0
- package/src/components/partials/action.partial.vue +30 -0
- package/src/components/partials/client-only.partial.vue +11 -0
- package/src/components/partials/error.partial.vue +73 -0
- package/src/components/partials/main-components.partial.vue +167 -0
- package/src/components/partials/mediadetection.partial.vue +71 -0
- package/src/components/partials/qrcode.partial.vue +35 -0
- package/src/customs.ts +12 -0
- package/src/env.d.ts +1 -0
- package/src/functions/PaymenyProviderFactory.ts +27 -0
- package/src/functions/lila-dom.ts +100 -0
- package/src/functions/shopify.provider.ts +378 -0
- package/src/functions/stripe.provider.ts +181 -0
- package/src/globals.d.ts +19 -0
- package/src/index.ts +47 -0
- package/src/interfaces/AppState.interface.ts +4 -0
- package/src/interfaces/EditorConfiguration.interface.ts +15 -0
- package/src/interfaces/EventDeclaration.interface.ts +19 -0
- package/src/interfaces/FrontendConfig.interface.ts +42 -0
- package/src/interfaces/GenericEvents.interface.ts +6 -0
- package/src/interfaces/GenericState.interface.ts +5 -0
- package/src/interfaces/IconsPartial.ts +9 -0
- package/src/interfaces/IdTokenExtended.interface.ts +7 -0
- package/src/interfaces/ModuleBaseProps.interface.ts +8 -0
- package/src/interfaces/PaymentProvider.interface.ts +13 -0
- package/src/libs/ActionNotice.ts +235 -0
- package/src/libs/Models.class.ts +482 -0
- package/src/main.ts +84 -0
- package/src/mixins/createCookieString.ts +78 -0
- package/src/mixins/createModelDeclaration.ts +29 -0
- package/src/mixins/createRouter.ts +30 -0
- package/src/mixins/date.ts +23 -0
- package/src/mixins/formatSize.ts +12 -0
- package/src/mixins/getAnchor.ts +14 -0
- package/src/mixins/getRoutes.ts +50 -0
- package/src/mixins/hasSlotContent.ts +32 -0
- package/src/mixins/hooks.ts +104 -0
- package/src/mixins/loadComponents.ts +107 -0
- package/src/mixins/logger.ts +32 -0
- package/src/mixins/replaceVariables.ts +19 -0
- package/src/mixins/scroll.ts +83 -0
- package/src/models/Address.model.ts +24 -0
- package/src/models/Contact.model.ts +22 -0
- package/src/models.ts +2 -0
- package/src/plugins/auth.ts +121 -0
- package/src/plugins/currency.ts +26 -0
- package/src/plugins/events.ts +156 -0
- package/src/plugins/filters.ts +43 -0
- package/src/plugins/inview.ts +351 -0
- package/src/plugins/replacer.ts +18 -0
- package/src/plugins/resize.ts +128 -0
- package/src/plugins/signupFlow.ts +89 -0
- package/src/plugins/traceable.ts +53 -0
- package/src/plugins/translations.ts +29 -0
- package/src/plugins/youtube.ts +80 -0
- package/src/routes.ts +132 -0
- package/src/server-entry.ts +113 -0
- package/src/stores/calls.store.ts +33 -0
- package/src/stores/cart.store.ts +186 -0
- package/src/stores/content.store.ts +83 -0
- package/src/stores/editor.store.ts +27 -0
- package/src/stores/files.store.ts +166 -0
- package/src/stores/main.store.ts +184 -0
- package/src/stores/user.store.ts +124 -0
- package/src/translations/de.ts +138 -0
- package/src/views/content.view.vue +219 -0
- package/src/views/download.view.vue +143 -0
- package/src/views/editor.view.vue +243 -0
- package/src/views/login.view.vue +82 -0
- package/src/views/signup-account.view.vue +64 -0
- package/tooling/cypress/config.ts +80 -0
- package/tooling/cypress/generate-manifest.js +5 -0
- package/tooling/cypress/manifest-utils.mjs +40 -0
- package/tooling/cypress/run-parallel.mjs +77 -0
- package/tooling/cypress/support/commands.ts +436 -0
- package/tooling/cypress/support/e2e.ts +17 -0
- package/tooling/eslint.config.js +223 -0
- package/tooling/preview.plugin.ts +134 -0
- package/tooling/ssr-test/index.mjs +391 -0
- package/tooling/stylelint.config.mjs +24 -0
- package/tooling/vite.ts +246 -0
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import type GenericEvents from '../interfaces/GenericEvents.interface';
|
|
2
|
+
import type EventDeclaration from '../interfaces/EventDeclaration.interface';
|
|
3
|
+
import { auth } from './auth';
|
|
4
|
+
import useUserStore from '../stores/user.store';
|
|
5
|
+
import useCartStore from '../stores/cart.store';
|
|
6
|
+
import { useTraceable } from './traceable';
|
|
7
|
+
import StudioSDK from '@lilaquadrat/sdk';
|
|
8
|
+
import useMainStore from '../stores/main.store';
|
|
9
|
+
import logger from '../mixins/logger';
|
|
10
|
+
import type { Ref } from 'vue';
|
|
11
|
+
|
|
12
|
+
const eventDeclarations: EventDeclaration[] = [
|
|
13
|
+
{
|
|
14
|
+
id : 'login',
|
|
15
|
+
name : 'Anmelden',
|
|
16
|
+
description: 'löst den Login für Benutzer aus',
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
id : 'logout',
|
|
20
|
+
name : 'Abmelden',
|
|
21
|
+
description: 'löst den Logout für Benutzer aus',
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
id : 'register',
|
|
25
|
+
name : 'Registrieren',
|
|
26
|
+
description: 'erstellt ein neues Benutzerkonto',
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
id : 'resendConfirmationMail',
|
|
30
|
+
name : 'Bestätigungscode erneut senden',
|
|
31
|
+
description : 'Eingeloggte Nutzer',
|
|
32
|
+
confirmRequired: true,
|
|
33
|
+
operation : true,
|
|
34
|
+
feedback : {
|
|
35
|
+
resolved: 'Bestätigungscode wurde versendet',
|
|
36
|
+
rejected: 'Bestätigungscode konnte nicht gesendet werden',
|
|
37
|
+
pending : 'Bestätigungscode wird versendet'
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
id : 'refresh_token',
|
|
42
|
+
name : 'Anmeldung aktualisieren',
|
|
43
|
+
description: 'aktualisiert das Authentifizierungstoken für den Benutzer',
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
id : 'addToCart',
|
|
47
|
+
name : 'In den Warenkorb legen',
|
|
48
|
+
description : 'fügt das angegebene Produkt dem Warenkorb hinzu',
|
|
49
|
+
additionalData: {
|
|
50
|
+
name : 'shopifyid',
|
|
51
|
+
description: 'die Produkt-ID'
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
id : 'scrollToNextModule',
|
|
56
|
+
name : 'Scroll zum nächstes Module',
|
|
57
|
+
description: 'Scrollt zum nächsten Module',
|
|
58
|
+
}
|
|
59
|
+
];
|
|
60
|
+
const events: GenericEvents = {
|
|
61
|
+
login: () => {
|
|
62
|
+
|
|
63
|
+
const userStore = useUserStore();
|
|
64
|
+
|
|
65
|
+
auth.triggerLogin(userStore.customer?._id, userStore.emailConfirmationCode);
|
|
66
|
+
|
|
67
|
+
},
|
|
68
|
+
logout: () => {
|
|
69
|
+
|
|
70
|
+
auth.triggerLogout();
|
|
71
|
+
|
|
72
|
+
},
|
|
73
|
+
register: () => {
|
|
74
|
+
|
|
75
|
+
const userStore = useUserStore();
|
|
76
|
+
|
|
77
|
+
auth.triggerSignup(userStore.customer?._id, userStore.emailConfirmationCode);
|
|
78
|
+
|
|
79
|
+
},
|
|
80
|
+
addToCart: (product?: string) => {
|
|
81
|
+
|
|
82
|
+
console.log('add to cart', product);
|
|
83
|
+
|
|
84
|
+
if (!product) {
|
|
85
|
+
console.warn('ADD_TO_CART_FAILED_PRODUCT_ID_MISSING');
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const cartStore = useCartStore();
|
|
90
|
+
|
|
91
|
+
cartStore.addProduct(product as string);
|
|
92
|
+
|
|
93
|
+
},
|
|
94
|
+
refresh_token: () => {
|
|
95
|
+
|
|
96
|
+
auth.triggerLogin()
|
|
97
|
+
|
|
98
|
+
},
|
|
99
|
+
scrollToNextModule: (additionalData: string | undefined, mouseEvent: MouseEvent) => {
|
|
100
|
+
|
|
101
|
+
const target = mouseEvent.target as HTMLElement;
|
|
102
|
+
const parentModule = target?.closest('.lila-module');
|
|
103
|
+
const nextSibling = parentModule?.nextElementSibling;
|
|
104
|
+
|
|
105
|
+
console.log('scroll to next module');
|
|
106
|
+
|
|
107
|
+
if (nextSibling) {
|
|
108
|
+
|
|
109
|
+
nextSibling.scrollIntoView({
|
|
110
|
+
behavior: 'smooth',
|
|
111
|
+
block : 'center'
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
},
|
|
117
|
+
resendConfirmationMail: async (additionalData: string | undefined, mouseEvent: MouseEvent, traceId: Ref<string | undefined>) => {
|
|
118
|
+
|
|
119
|
+
const { apiConfig } = useMainStore();
|
|
120
|
+
const { traceable } = useTraceable();
|
|
121
|
+
const sdk = new StudioSDK(apiConfig)
|
|
122
|
+
|
|
123
|
+
return traceable(sdk.members.me.resendConfirmationMail(), traceId);
|
|
124
|
+
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* triggers a predefined event
|
|
130
|
+
*/
|
|
131
|
+
function triggerEvent (type: keyof typeof events | string, additionalData: string | undefined, mouseEvent: MouseEvent, traceId: Ref<string | undefined>) {
|
|
132
|
+
|
|
133
|
+
const event = events[type as keyof typeof events];
|
|
134
|
+
|
|
135
|
+
if (!event) {
|
|
136
|
+
|
|
137
|
+
console.error('EVENT_MISSING', type, event);
|
|
138
|
+
throw new Error('EVENT_MISSING');
|
|
139
|
+
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
return event(additionalData, mouseEvent, traceId);
|
|
143
|
+
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const useEvents = () => ({ triggerEvent, eventDeclarations, events })
|
|
147
|
+
const plugin = {
|
|
148
|
+
install: () => {
|
|
149
|
+
logger.plugins('events installed');
|
|
150
|
+
},
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
export default plugin;
|
|
154
|
+
export {
|
|
155
|
+
useEvents
|
|
156
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import useDate from '../mixins/date';
|
|
2
|
+
import logger from '../mixins/logger';
|
|
3
|
+
import type { App } from 'vue';
|
|
4
|
+
|
|
5
|
+
const HelpersPlugin = {
|
|
6
|
+
|
|
7
|
+
leadingZero (value: string | number, minLength: number) {
|
|
8
|
+
return value.toString().padStart(minLength, '0');
|
|
9
|
+
},
|
|
10
|
+
|
|
11
|
+
getFilename (contentType: 'public' | 'members', path?: string) {
|
|
12
|
+
|
|
13
|
+
const contentIdArray = [];
|
|
14
|
+
|
|
15
|
+
if(!path || path === '/') return 'home';
|
|
16
|
+
|
|
17
|
+
if(contentType === 'members' && path !== '/m' && !path.startsWith('/m/')) contentIdArray.push('m');
|
|
18
|
+
|
|
19
|
+
contentIdArray.push(...path.split('/'));
|
|
20
|
+
|
|
21
|
+
return contentIdArray.filter((single) => single.length).join('/');
|
|
22
|
+
|
|
23
|
+
},
|
|
24
|
+
|
|
25
|
+
date (date: string | Date, format?: string) {
|
|
26
|
+
|
|
27
|
+
return useDate(date).locale('de').format(format || 'DD.MM.YYYY HH:mm');
|
|
28
|
+
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
}
|
|
32
|
+
const plugin = {
|
|
33
|
+
install: (vue: App): void => {
|
|
34
|
+
|
|
35
|
+
vue.config.globalProperties.$helpers = HelpersPlugin;
|
|
36
|
+
logger.plugins('helpers installed')
|
|
37
|
+
},
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
export default plugin;
|
|
41
|
+
export {
|
|
42
|
+
HelpersPlugin
|
|
43
|
+
};
|
|
@@ -0,0 +1,351 @@
|
|
|
1
|
+
import { computed, watch } from 'vue';
|
|
2
|
+
import { ref } from 'vue';
|
|
3
|
+
import { type Ref } from 'vue';
|
|
4
|
+
|
|
5
|
+
class Inview {
|
|
6
|
+
|
|
7
|
+
timeout!: NodeJS.Timeout | undefined;
|
|
8
|
+
|
|
9
|
+
lastScrollingAdjustment = 0;
|
|
10
|
+
|
|
11
|
+
debounceTime: number = 50;
|
|
12
|
+
|
|
13
|
+
isTop = ref<boolean>(false);
|
|
14
|
+
|
|
15
|
+
scrolledEvent: Event;
|
|
16
|
+
|
|
17
|
+
scrolled = ref<number>(0);
|
|
18
|
+
|
|
19
|
+
moduleAdded = ref<number>(0);
|
|
20
|
+
|
|
21
|
+
scrollDirection = ref<'up' | 'down'>();
|
|
22
|
+
|
|
23
|
+
parentElement = ref<HTMLElement>();
|
|
24
|
+
|
|
25
|
+
lastScrollY = 0;
|
|
26
|
+
lastTimestamp = 0;
|
|
27
|
+
scrollVelocity = 0;
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* if true we fire an inital scroll event to indicate that scrolling has started without waiting for the debounce
|
|
31
|
+
*/
|
|
32
|
+
triggerStart = ref<boolean>(true);
|
|
33
|
+
|
|
34
|
+
safeWindow = computed(() => {
|
|
35
|
+
|
|
36
|
+
if (typeof window === 'undefined') return null;
|
|
37
|
+
if (this.parentElement.value) return this.parentElement.value;
|
|
38
|
+
return window;
|
|
39
|
+
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
scrollY () {
|
|
43
|
+
|
|
44
|
+
if (this.safeWindow.value instanceof Window) {
|
|
45
|
+
|
|
46
|
+
return this.safeWindow.value.scrollY;
|
|
47
|
+
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (this.parentElement.value) {
|
|
51
|
+
|
|
52
|
+
return this.parentElement.value.scrollTop;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return 0
|
|
56
|
+
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
height () {
|
|
60
|
+
|
|
61
|
+
if (this.safeWindow.value instanceof Window) {
|
|
62
|
+
|
|
63
|
+
return this.safeWindow.value.outerHeight;
|
|
64
|
+
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (this.parentElement.value) {
|
|
68
|
+
|
|
69
|
+
return this.parentElement.value.offsetHeight;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return 0
|
|
73
|
+
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
constructor () {
|
|
77
|
+
|
|
78
|
+
this.scrolledEvent = new Event('scrolled');
|
|
79
|
+
this.checkIsTop();
|
|
80
|
+
|
|
81
|
+
if (this.safeWindow.value) {
|
|
82
|
+
|
|
83
|
+
this.safeWindow.value?.addEventListener('scroll', () => {
|
|
84
|
+
|
|
85
|
+
this.debounce();
|
|
86
|
+
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
setParentElement (element: HTMLElement) {
|
|
94
|
+
|
|
95
|
+
this.parentElement.value = element;
|
|
96
|
+
this.addScrollListener(element);
|
|
97
|
+
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
addScrollListener (element: Element) {
|
|
101
|
+
|
|
102
|
+
element.addEventListener('scroll', () => {
|
|
103
|
+
|
|
104
|
+
const currentTimestamp = performance.now();
|
|
105
|
+
const deltaY = this.scrollY() - this.lastScrollY;
|
|
106
|
+
const deltaTime = currentTimestamp - this.lastTimestamp;
|
|
107
|
+
|
|
108
|
+
// Calculate velocity (pixels per millisecond)
|
|
109
|
+
this.scrollVelocity = Math.abs(deltaY / deltaTime);
|
|
110
|
+
|
|
111
|
+
this.lastScrollY = this.scrollY();
|
|
112
|
+
this.lastTimestamp = currentTimestamp;
|
|
113
|
+
|
|
114
|
+
// console.log(`Scroll Velocity: ${this.scrollVelocity.toFixed(2)} pixels/ms`);
|
|
115
|
+
|
|
116
|
+
this.debounce();
|
|
117
|
+
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
trigger (isStart?: boolean) {
|
|
123
|
+
|
|
124
|
+
if (!this.safeWindow.value) return;
|
|
125
|
+
|
|
126
|
+
this.checkIsTop();
|
|
127
|
+
this.scrollDirection.value = this.scrolled.value > this.scrollY()
|
|
128
|
+
? 'down'
|
|
129
|
+
: 'up'
|
|
130
|
+
this.scrolled.value = this.scrollY();
|
|
131
|
+
|
|
132
|
+
this.safeWindow.value?.dispatchEvent(this.scrolledEvent);
|
|
133
|
+
if (!isStart) this.triggerStart.value = true;
|
|
134
|
+
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
checkIsTop () {
|
|
138
|
+
|
|
139
|
+
if (!this.safeWindow.value) return;
|
|
140
|
+
|
|
141
|
+
this.isTop.value = this.scrollY() <= 0
|
|
142
|
+
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
debounce () {
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* indicates that the scrolling has begun and fire an inital scrolling event without waiting for the debounce
|
|
149
|
+
*/
|
|
150
|
+
if (this.triggerStart.value) {
|
|
151
|
+
|
|
152
|
+
this.triggerStart.value = false;
|
|
153
|
+
this.trigger(true);
|
|
154
|
+
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
clearTimeout(this.timeout);
|
|
158
|
+
this.timeout = setTimeout(() => {
|
|
159
|
+
|
|
160
|
+
this.trigger();
|
|
161
|
+
|
|
162
|
+
}, this.debounceTime);
|
|
163
|
+
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
check (component: HTMLElement, state: Ref<string[]>, options?: { align?: boolean }) {
|
|
167
|
+
const element = component;
|
|
168
|
+
const newState: string[] = [];
|
|
169
|
+
|
|
170
|
+
if (typeof element?.getBoundingClientRect !== 'function') return;
|
|
171
|
+
if (!this.safeWindow.value) return;
|
|
172
|
+
|
|
173
|
+
const viewportHeight = this.height();
|
|
174
|
+
const height = viewportHeight / 2;
|
|
175
|
+
const rect = element.getBoundingClientRect();
|
|
176
|
+
let top = rect.top - height + 10;
|
|
177
|
+
let bottom = rect.bottom - height + 10;
|
|
178
|
+
let visibleHeight = 0;
|
|
179
|
+
let marginToTop = 0;
|
|
180
|
+
|
|
181
|
+
if (this.parentElement.value) {
|
|
182
|
+
|
|
183
|
+
const parentRect = this.parentElement.value.getBoundingClientRect();
|
|
184
|
+
// Adjust top and bottom based on parent element's top and bottom
|
|
185
|
+
const parentTop = parentRect.top;
|
|
186
|
+
const parentBottom = parentRect.bottom;
|
|
187
|
+
|
|
188
|
+
top = rect.top - parentTop - height + 10;
|
|
189
|
+
bottom = rect.bottom - parentTop - height + 10;
|
|
190
|
+
|
|
191
|
+
marginToTop = Math.abs(rect.top - parentTop);
|
|
192
|
+
|
|
193
|
+
// Adjust visibleTop and visibleBottom for parent element
|
|
194
|
+
const visibleTop = Math.max(parentTop, rect.top);
|
|
195
|
+
const visibleBottom = Math.min(parentBottom, rect.bottom);
|
|
196
|
+
|
|
197
|
+
visibleHeight = Math.max(0, visibleBottom - visibleTop);
|
|
198
|
+
|
|
199
|
+
} else {
|
|
200
|
+
|
|
201
|
+
// Original calculations if no parent element is provided
|
|
202
|
+
const visibleTop = Math.max(0, rect.top);
|
|
203
|
+
const visibleBottom = Math.min(rect.bottom, viewportHeight);
|
|
204
|
+
|
|
205
|
+
marginToTop = Math.abs(rect.top);
|
|
206
|
+
|
|
207
|
+
visibleHeight = Math.max(0, visibleBottom - visibleTop);
|
|
208
|
+
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
if (top > 0) {
|
|
212
|
+
newState.push('inview-after');
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
if (top <= 0 && bottom >= 0) {
|
|
216
|
+
newState.push('inview-current');
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
if (bottom < 0) {
|
|
220
|
+
newState.push('inview-before');
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// Check if more than 30% of the element is visible
|
|
224
|
+
if (visibleHeight / rect.height >= 0.3) {
|
|
225
|
+
newState.push('inview-visible');
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
if (newState.includes('inview-current') && options?.align) {
|
|
229
|
+
|
|
230
|
+
if(!this.lastScrollingAdjustment || Date.now() - this.lastScrollingAdjustment > 1500) {
|
|
231
|
+
|
|
232
|
+
requestAnimationFrame(() => this.adjustScrolling(element, marginToTop));
|
|
233
|
+
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
state.value.length = 0;
|
|
239
|
+
state.value.push(...newState);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
checkPreload (component: HTMLElement, state: Ref<boolean>) {
|
|
243
|
+
|
|
244
|
+
const preloadRange = window?.outerHeight * 2;
|
|
245
|
+
const rect = component.getBoundingClientRect();
|
|
246
|
+
|
|
247
|
+
if (rect.bottom > -preloadRange && rect.top < preloadRange) {
|
|
248
|
+
|
|
249
|
+
state.value = true;
|
|
250
|
+
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
adjustScrolling (component: HTMLElement, top: number) {
|
|
256
|
+
|
|
257
|
+
const offset = this.height() / 10;
|
|
258
|
+
|
|
259
|
+
if (top < offset && top > (offset * -1)) {
|
|
260
|
+
|
|
261
|
+
this.lastScrollingAdjustment = Date.now();
|
|
262
|
+
|
|
263
|
+
component.scrollIntoView({
|
|
264
|
+
behavior: 'smooth',
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
moduleRendered () {
|
|
272
|
+
|
|
273
|
+
this.moduleAdded.value++;
|
|
274
|
+
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
const inview = new Inview();
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* the preload option is used to check if the element is in range of two times of the viewport and changes the preload value accordingly
|
|
283
|
+
*
|
|
284
|
+
* without the preload options the inviewState array holds the following values
|
|
285
|
+
** inview-before - the element is before the viewport
|
|
286
|
+
** inview-current - the element is centered in the viewport
|
|
287
|
+
** inview-after - the element is after the viewport
|
|
288
|
+
** inview-visible - more than 30% of the element are visible in the current viewport, regardless if it is centered or nor
|
|
289
|
+
*/
|
|
290
|
+
|
|
291
|
+
export function useInview (element?: Ref<HTMLElement | undefined>, options?: { align?: boolean, preload?: boolean }) {
|
|
292
|
+
|
|
293
|
+
const state = ref<string[]>([]);
|
|
294
|
+
const preload = ref<boolean>(false);
|
|
295
|
+
|
|
296
|
+
if (element) {
|
|
297
|
+
|
|
298
|
+
watch(element, () => {
|
|
299
|
+
|
|
300
|
+
if (element.value) {
|
|
301
|
+
|
|
302
|
+
inview.moduleRendered();
|
|
303
|
+
|
|
304
|
+
if (options?.preload) {
|
|
305
|
+
|
|
306
|
+
inview.checkPreload(element.value, preload);
|
|
307
|
+
|
|
308
|
+
const unwatchPreload = watch(inview.scrolled, () => {
|
|
309
|
+
|
|
310
|
+
if (!element.value) return;
|
|
311
|
+
|
|
312
|
+
inview.checkPreload(element.value as HTMLElement, preload);
|
|
313
|
+
|
|
314
|
+
//unwatch when the preload value is true
|
|
315
|
+
if (preload.value) unwatchPreload();
|
|
316
|
+
|
|
317
|
+
})
|
|
318
|
+
|
|
319
|
+
} else {
|
|
320
|
+
|
|
321
|
+
inview.check(element.value, state, options);
|
|
322
|
+
|
|
323
|
+
watch(inview.scrolled, () => {
|
|
324
|
+
|
|
325
|
+
inview.check(element.value as HTMLElement, state, options)
|
|
326
|
+
|
|
327
|
+
})
|
|
328
|
+
|
|
329
|
+
watch(inview.moduleAdded, () => {
|
|
330
|
+
|
|
331
|
+
requestAnimationFrame(() => {
|
|
332
|
+
|
|
333
|
+
inview.check(element.value as HTMLElement, state, options);
|
|
334
|
+
|
|
335
|
+
})
|
|
336
|
+
|
|
337
|
+
})
|
|
338
|
+
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
})
|
|
344
|
+
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
return { inviewState: state, scrolled: inview.scrolled, scrollDirection: inview.scrollDirection, preload };
|
|
348
|
+
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
export default inview;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import logger from '../mixins/logger';
|
|
2
|
+
import replaceVariables from '../mixins/replaceVariables';
|
|
3
|
+
import useContentStore from '../stores/content.store';
|
|
4
|
+
import useUserStore from '../stores/user.store';
|
|
5
|
+
import type { App } from 'vue';
|
|
6
|
+
|
|
7
|
+
const plugin = {
|
|
8
|
+
install: (vue: App): void => {
|
|
9
|
+
|
|
10
|
+
const contentStore = useContentStore();
|
|
11
|
+
const userStore = useUserStore();
|
|
12
|
+
|
|
13
|
+
vue.config.globalProperties.$replacer = (content: string) => replaceVariables(content, {...contentStore.context, recipient: contentStore.recipient, list: contentStore.list, user: userStore.userData });
|
|
14
|
+
logger.plugins('replacer installed')
|
|
15
|
+
},
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export default plugin;
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import logger from '../mixins/logger';
|
|
2
|
+
import { ref, computed } from 'vue';
|
|
3
|
+
|
|
4
|
+
class Resize {
|
|
5
|
+
|
|
6
|
+
timeout: any;
|
|
7
|
+
|
|
8
|
+
debounceTime: number = 50;
|
|
9
|
+
|
|
10
|
+
resizedEvent: Event;
|
|
11
|
+
|
|
12
|
+
mediaEvent: Event;
|
|
13
|
+
|
|
14
|
+
media = ref<string>('mobile');
|
|
15
|
+
|
|
16
|
+
resized = ref<number>(Date.now());
|
|
17
|
+
|
|
18
|
+
realHeight = ref<number>(0);
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* if true we fire an inital resized event to indicate that resizing has started without waiting for the debounce
|
|
22
|
+
*/
|
|
23
|
+
triggerStart = ref<boolean>(true);
|
|
24
|
+
|
|
25
|
+
safeWindow = computed(() => {
|
|
26
|
+
|
|
27
|
+
if(typeof window === 'undefined') return null;
|
|
28
|
+
return window;
|
|
29
|
+
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
constructor () {
|
|
33
|
+
|
|
34
|
+
this.resizedEvent = new Event('resized');
|
|
35
|
+
this.mediaEvent = new Event('media');
|
|
36
|
+
this.realHeight.value = this.safeWindow.value?.innerHeight || 0;
|
|
37
|
+
|
|
38
|
+
this.getMediaQuery();
|
|
39
|
+
|
|
40
|
+
this.safeWindow.value?.addEventListener('resize', () => this.debounce());
|
|
41
|
+
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
trigger (isStart?: boolean) {
|
|
45
|
+
|
|
46
|
+
this.getMediaQuery();
|
|
47
|
+
|
|
48
|
+
if(this.safeWindow.value) {
|
|
49
|
+
|
|
50
|
+
this.realHeight.value = this.safeWindow.value?.innerHeight;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
this.resized.value = Date.now();
|
|
54
|
+
this.safeWindow.value?.dispatchEvent(this.resizedEvent);
|
|
55
|
+
|
|
56
|
+
if(!isStart) this.triggerStart.value = true;
|
|
57
|
+
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
getMediaQuery (): void {
|
|
61
|
+
|
|
62
|
+
if(typeof document === 'undefined') return;
|
|
63
|
+
|
|
64
|
+
const element = document.getElementById('mediadetection');
|
|
65
|
+
|
|
66
|
+
if (!element) return;
|
|
67
|
+
|
|
68
|
+
const child = Array.from(element.children).find(child => this.safeWindow.value?.getComputedStyle(child).display === 'block');
|
|
69
|
+
|
|
70
|
+
if (child) {
|
|
71
|
+
const childClass = child.getAttribute('class') as string;
|
|
72
|
+
|
|
73
|
+
if (this.media.value !== childClass) {
|
|
74
|
+
|
|
75
|
+
this.media.value = childClass;
|
|
76
|
+
this.safeWindow.value?.dispatchEvent(this.mediaEvent);
|
|
77
|
+
|
|
78
|
+
} else {
|
|
79
|
+
|
|
80
|
+
this.media.value = childClass;
|
|
81
|
+
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
debounce () {
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* indicates that the scrolling has begun and fire an inital scrolling event without waiting for the debounce
|
|
90
|
+
*/
|
|
91
|
+
if(this.triggerStart.value) {
|
|
92
|
+
|
|
93
|
+
this.triggerStart.value = false;
|
|
94
|
+
this.trigger(true);
|
|
95
|
+
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
clearTimeout(this.timeout);
|
|
99
|
+
this.timeout = setTimeout(() => {
|
|
100
|
+
|
|
101
|
+
this.trigger();
|
|
102
|
+
|
|
103
|
+
}, this.debounceTime);
|
|
104
|
+
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const resize = new Resize();
|
|
110
|
+
const plugin = {
|
|
111
|
+
install: (): void => {
|
|
112
|
+
// this exists only for initialization purposes
|
|
113
|
+
logger.plugins('resize installed');
|
|
114
|
+
},
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
function useResize () {
|
|
118
|
+
|
|
119
|
+
return {realHeight: resize.realHeight, media: resize.media, resized: resize.resized, plugin: resize};
|
|
120
|
+
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export default plugin;
|
|
124
|
+
|
|
125
|
+
export {
|
|
126
|
+
useResize,
|
|
127
|
+
resize
|
|
128
|
+
};
|