@eox/pages-theme-eox 0.6.0 → 0.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eox/pages-theme-eox",
3
- "version": "0.6.0",
3
+ "version": "0.7.0",
4
4
  "type": "module",
5
5
  "description": "Vitepress Theme with EOX branding",
6
6
  "main": "src/index.js",
package/src/Layout.vue CHANGED
@@ -194,6 +194,12 @@
194
194
  />
195
195
  </header>
196
196
  </template>
197
+ <template #not-found>
198
+ <ClientOnly v-if="router.route.path === '/cookie-settings'">
199
+ <CookieSettings></CookieSettings>
200
+ </ClientOnly>
201
+ <NotFound v-else></NotFound>
202
+ </template>
197
203
  <template #page-bottom>
198
204
  <FeaturesGallery
199
205
  v-if="page.relativePath.startsWith('features/')"
@@ -220,6 +226,17 @@
220
226
  >{{ theme.nav.find((i) => i.link.includes("contact")).text }}</a
221
227
  >
222
228
  <p v-html="theme.footer.copyright"></p>
229
+ <p class="middle-align">
230
+ Powered by
231
+ <a
232
+ href="https://hub.eox.at"
233
+ target="_blank"
234
+ class="left-margin small-margin"
235
+ ><img
236
+ src="https://hub.eox.at/hub/custom/logos/eoxhub.svg"
237
+ style="height: 25px"
238
+ /></a>
239
+ </p>
223
240
  </div>
224
241
  <div class="s12 l6">
225
242
  <div class="grid large-line">
@@ -285,10 +302,11 @@
285
302
 
286
303
  <script setup>
287
304
  import DefaultTheme from "vitepress/theme";
288
- import { useData, withBase } from "vitepress";
305
+ import { useData, useRouter, withBase } from "vitepress";
289
306
  import { trackEvent } from "./helpers";
290
307
  const { Layout } = DefaultTheme;
291
308
  const { frontmatter, page, site, theme } = useData();
309
+ const router = useRouter();
292
310
 
293
311
  if (!import.meta.env.SSR) {
294
312
  const scrollListener = () => {
@@ -377,7 +395,7 @@ header {
377
395
  margin-top: calc(var(--vp-nav-height) * -1);
378
396
  padding-top: 10rem !important;
379
397
  padding-bottom: 10rem !important;
380
- height: 100svh;
398
+ height: calc(100svh + 2px);
381
399
  max-height: 1000px;
382
400
  }
383
401
  @media (max-width: 1024px) {
@@ -3,121 +3,40 @@
3
3
  class="cookie-banner card surface medium-margin medium-padding medium-elevate no-round"
4
4
  >
5
5
  <p class="small-text">
6
- We use cookies to improve your experience and for marketing. Read our
6
+ We use optional cookies to improve your experience and for marketing. Read
7
+ our
7
8
  <a
8
9
  :href="theme.theme.brandConfig?.legal?.privacyPolicy"
9
10
  target="_blank"
10
11
  class="link"
11
12
  >
12
- Privacy Policy
13
+ privacy policy
13
14
  </a>
14
- for more information.
15
+ or <a class="link" href="/cookie-settings">manage cookies</a>.
15
16
  </p>
16
17
  <nav>
17
- <button class="small" @click="acceptCookies">Accept all</button>
18
- <button class="small" @click="declineCookies">Reject all</button>
18
+ <div class="max"></div>
19
+ <button class="small" @click="accept">Accept all</button>
20
+ <button class="small" @click="decline">Reject all</button>
19
21
  </nav>
20
22
  </div>
21
23
  </template>
22
24
 
23
25
  <script setup>
24
- import { onMounted, watch } from "vue";
26
+ import { onMounted } from "vue";
25
27
  import { useData, useRouter } from "vitepress";
26
- import { trackEvent } from "../helpers";
28
+ import {
29
+ acceptCookies,
30
+ declineCookies,
31
+ enableTracking,
32
+ showBanner,
33
+ } from "../helpers";
27
34
 
28
35
  const { theme } = useData();
29
36
  const router = useRouter();
30
37
 
31
- /**
32
- * Show/hide banner
33
- *
34
- * @param show Boolean
35
- */
36
- const showBanner = (show) => {
37
- document.querySelector(".cookie-banner").style.display = show
38
- ? "block"
39
- : "none";
40
- };
41
-
42
- /**
43
- * Accept cookies
44
- */
45
- const acceptCookies = () => {
46
- _paq.push(["rememberCookieConsentGiven"]);
47
- showBanner(false);
48
- trackPageScrolling(true);
49
- trackRouterNavigation(true);
50
- };
51
-
52
- /**
53
- * Decline cookies
54
- */
55
- const declineCookies = () => {
56
- _paq.push(["forgetCookieConsentGiven"]);
57
- _paq.push(["optUserOut"]);
58
- showBanner(false);
59
- trackPageScrolling(false);
60
- trackRouterNavigation(false);
61
- };
62
-
63
- /**
64
- * Track page scrolling depth
65
- *
66
- * @param enabled Boolean
67
- */
68
- const trackPageScrolling = (enabled) => {
69
- function getScrollPercent() {
70
- const h = document.documentElement,
71
- b = document.body,
72
- st = "scrollTop",
73
- sh = "scrollHeight";
74
- return ((h[st] || b[st]) / ((h[sh] || b[sh]) - h.clientHeight)) * 100;
75
- }
76
- const scrollTargets = {
77
- 25: false,
78
- 50: false,
79
- 75: false,
80
- 100: false,
81
- };
82
- const scrollListener = () => {
83
- Object.keys(scrollTargets).forEach((target) => {
84
- if (getScrollPercent() >= parseInt(target) && !scrollTargets[target]) {
85
- scrollTargets[target] = true;
86
- trackEvent(["Interaction", "Scroll Depth", "Page"`${target}%`]);
87
- }
88
- });
89
- };
90
- if (enabled) {
91
- document.addEventListener("scroll", scrollListener);
92
- } else {
93
- document.removeEventListener("scroll", scrollListener);
94
- }
95
- };
96
-
97
- /**
98
- * Track user navigation from one page to another
99
- *
100
- * @param enabled Boolean
101
- */
102
- const trackRouterNavigation = (enabled) => {
103
- const trackNavigation = () => {
104
- _paq.push(["setCustomUrl", router.route.path]);
105
- _paq.push(["setDocumentTitle", router.route.data.title]);
106
- _paq.push(["trackPageView"]);
107
- _paq.push(["enableLinkTracking"]);
108
- };
109
- if (enabled) {
110
- watch(() => router.route.data.relativePath, trackNavigation, {
111
- immediate: true,
112
- });
113
- } else {
114
- watch(
115
- () => router.route.data.relativePath,
116
- () => {},
117
- { immediate: true },
118
- );
119
- }
120
- };
38
+ const accept = () => acceptCookies(router);
39
+ const decline = () => declineCookies(router);
121
40
 
122
41
  onMounted(() => {
123
42
  setTimeout(() => {
@@ -128,6 +47,9 @@ onMounted(() => {
128
47
  ) {
129
48
  showBanner(true);
130
49
  }
50
+ if (document.cookie.includes("mtm_cookie_consent")) {
51
+ enableTracking(true, router);
52
+ }
131
53
  });
132
54
  });
133
55
  </script>
@@ -0,0 +1,180 @@
1
+ <template>
2
+ <div class="VPPage">
3
+ <h1>Cookie Settings</h1>
4
+ <p>
5
+ We use cookies and similar technologies to improve your experience and for
6
+ marketing purposes. Review and manage your cookie settings below to
7
+ control your privacy. For more information on how we use cookies, please
8
+ see our
9
+ <a
10
+ class="link"
11
+ :href="
12
+ theme.theme.brandConfig?.legal?.privacyPolicy ||
13
+ 'https://eox.at/privacy-notice/'
14
+ "
15
+ target="_blank"
16
+ >Privacy Policy</a
17
+ >.
18
+ </p>
19
+ <div
20
+ v-for="category in Object.keys(cookies)"
21
+ class="vertical-margin large-margin vertical-padding large-padding"
22
+ :id="category"
23
+ >
24
+ <nav>
25
+ <div class="max">
26
+ <h6 class="bold">{{ category }}</h6>
27
+ <p>
28
+ {{ cookies[category].description }}
29
+ </p>
30
+ </div>
31
+ <label class="switch">
32
+ <input
33
+ type="checkbox"
34
+ :checked="cookies[category].required"
35
+ :disabled="cookies[category].required"
36
+ />
37
+ <span></span>
38
+ </label>
39
+ </nav>
40
+ <details>
41
+ <summary class="middle-align">
42
+ <p class="primary-text bold">
43
+ <span class="view">View</span><span class="hide">Hide</span> cookies
44
+ </p>
45
+ <i class="small mdi mdi-chevron-down"></i>
46
+ </summary>
47
+ <table>
48
+ <thead>
49
+ <tr>
50
+ <th class="min">Name</th>
51
+ <th>Domain</th>
52
+ <th>Type</th>
53
+ <th>Duration</th>
54
+ </tr>
55
+ </thead>
56
+ <tbody>
57
+ <tr v-for="cookie in cookies[category].cookies">
58
+ <td class="min">
59
+ <code>{{ cookie.Name }}</code>
60
+ </td>
61
+ <td>{{ cookie.Domain }}</td>
62
+ <td>{{ cookie.Type }}</td>
63
+ <td>{{ cookie.Duration }}</td>
64
+ </tr>
65
+ </tbody>
66
+ </table>
67
+ </details>
68
+ </div>
69
+ </div>
70
+ <div class="large-space"></div>
71
+ </template>
72
+
73
+ <style>
74
+ .page {
75
+ margin: auto;
76
+ width: 100%;
77
+ max-width: 1200px;
78
+ padding: 48px 24px 0;
79
+ }
80
+ details summary i {
81
+ transition: transform 0.3s ease-in-out;
82
+ }
83
+ details[open] summary i {
84
+ transform: rotate(180deg);
85
+ }
86
+ details summary p {
87
+ display: flex;
88
+ gap: 0.25rem;
89
+ }
90
+ details summary p span {
91
+ display: none;
92
+ }
93
+ details:not([open]) summary .view {
94
+ display: block;
95
+ }
96
+ details[open] summary .hide {
97
+ display: block;
98
+ }
99
+ </style>
100
+
101
+ <script setup>
102
+ import { onMounted } from "vue";
103
+ import { useData, useRouter } from "vitepress";
104
+ import { enableTracking, showBanner } from "../helpers.js";
105
+ const { theme } = useData();
106
+ const router = useRouter();
107
+
108
+ router.route.data.title = "Cookie Settings";
109
+ router.onBeforeRouteChange = (to) => {
110
+ if (to === "/cookie-settings") {
111
+ showBanner(false);
112
+ } else {
113
+ showBanner(
114
+ !document.cookie.includes("mtm_cookie_consent") &&
115
+ !document.cookie.includes("mtm_consent_removed"),
116
+ );
117
+ }
118
+ };
119
+
120
+ const cookies = {
121
+ Essential: {
122
+ description:
123
+ "Cookies that are strictly necessary for basic website or app functionality.",
124
+ required: true,
125
+ cookies: [
126
+ {
127
+ Name: "mtm_consent_removed",
128
+ Domain: `.${window.location.host}`,
129
+ Type: "Opt-out management",
130
+ Duration: "13 months",
131
+ },
132
+ {
133
+ Name: "mtm_consent",
134
+ Domain: `.${window.location.host}`,
135
+ Type: "Consent management",
136
+ Duration: "13 months",
137
+ },
138
+ ],
139
+ },
140
+ Analytics: {
141
+ description:
142
+ "Cookies that are required for analyzing website or app usage.",
143
+ cookies: [
144
+ {
145
+ Name: "_pk_id",
146
+ Domain: `.${window.location.host}`,
147
+ Type: "First-party website analytics",
148
+ Duration: "13 months",
149
+ },
150
+ {
151
+ Name: "_pk_ses",
152
+ Domain: `.${window.location.host}`,
153
+ Type: "First-party website analytics",
154
+ Duration: "30 minutes",
155
+ },
156
+ ],
157
+ },
158
+ };
159
+
160
+ onMounted(() => {
161
+ setTimeout(() => {
162
+ showBanner(false);
163
+
164
+ const analyticsOpt = document.querySelector("#Analytics input");
165
+ analyticsOpt.checked = document.cookie.includes("mtm_cookie_consent");
166
+
167
+ analyticsOpt.addEventListener("input", () => {
168
+ if (analyticsOpt.checked) {
169
+ _paq.push(["forgetUserOptOut"]);
170
+ _paq.push(["setCookieConsentGiven"]);
171
+ _paq.push(["rememberCookieConsentGiven"]);
172
+ enableTracking(true, router);
173
+ } else {
174
+ _paq.push(["optUserOut"]);
175
+ enableTracking(false, router);
176
+ }
177
+ });
178
+ });
179
+ });
180
+ </script>
@@ -0,0 +1,14 @@
1
+ <template>
2
+ <div class="VPPage">
3
+ <h1>404</h1>
4
+ <p>The page you requested was not found.</p>
5
+ <div class="small-space"></div>
6
+ <nav>
7
+ <a class="button" href="/">
8
+ <i class="mdi mdi-arrow-left"></i>
9
+ <span>Back to home</span>
10
+ </a>
11
+ </nav>
12
+ <div class="large-space"></div>
13
+ </div>
14
+ </template>
package/src/helpers.js CHANGED
@@ -1,3 +1,5 @@
1
+ import { watch } from "vue";
2
+
1
3
  /**
2
4
  * Track Matomo event with array of four primary components
3
5
  *
@@ -15,3 +17,106 @@ export const trackEvent = (eventDetails) => {
15
17
  _paq.push(["trackEvent", ...eventDetails]);
16
18
  }
17
19
  };
20
+
21
+ /**
22
+ * Accept cookies
23
+ */
24
+ export const acceptCookies = (router) => {
25
+ _paq.push(["rememberCookieConsentGiven"]);
26
+ showBanner(false);
27
+ enableTracking(true, router);
28
+ };
29
+
30
+ /**
31
+ * Decline cookies
32
+ */
33
+ export const declineCookies = (router) => {
34
+ _paq.push(["forgetCookieConsentGiven"]);
35
+ _paq.push(["optUserOut"]);
36
+ showBanner(false);
37
+ enableTracking(false, router);
38
+ };
39
+
40
+ /**
41
+ * Show/hide banner
42
+ *
43
+ * @param show Boolean
44
+ */
45
+ export const showBanner = (show) => {
46
+ document.querySelector(".cookie-banner").style.display = show
47
+ ? "block"
48
+ : "none";
49
+ };
50
+
51
+ /**
52
+ * Enables tracking listeners
53
+ *
54
+ * @param {Boolean} enabled
55
+ * @param {router} router
56
+ */
57
+ export const enableTracking = (enabled, router) => {
58
+ trackPageScrolling(enabled, router);
59
+ trackRouterNavigation(router);
60
+ };
61
+
62
+ /**
63
+ * Track page scrolling depth
64
+ *
65
+ * @param enabled Boolean
66
+ */
67
+ export const trackPageScrolling = (enabled, router) => {
68
+ function getScrollPercent() {
69
+ const h = document.documentElement,
70
+ b = document.body,
71
+ st = "scrollTop",
72
+ sh = "scrollHeight";
73
+ return ((h[st] || b[st]) / ((h[sh] || b[sh]) - h.clientHeight)) * 100;
74
+ }
75
+ const scrollTargets = {
76
+ 25: [],
77
+ 50: [],
78
+ 75: [],
79
+ 100: [],
80
+ };
81
+ const scrollListener = () => {
82
+ Object.keys(scrollTargets).forEach((target) => {
83
+ if (
84
+ getScrollPercent() >= parseInt(target) &&
85
+ !scrollTargets[target].includes(router.route.path)
86
+ ) {
87
+ scrollTargets[target].push(router.route.path);
88
+ trackEvent([
89
+ "Interaction",
90
+ "Scroll Depth",
91
+ router.route.path,
92
+ `${target}%`,
93
+ ]);
94
+ }
95
+ });
96
+ };
97
+ if (enabled) {
98
+ document.addEventListener("scroll", scrollListener);
99
+ } else {
100
+ document.removeEventListener("scroll", scrollListener);
101
+ }
102
+ };
103
+
104
+ /**
105
+ * Track user navigation from one page to another
106
+ *
107
+ * @param enabled Boolean
108
+ */
109
+ export const trackRouterNavigation = (router) => {
110
+ const trackNavigation = () => {
111
+ if (!document.cookie.includes("mtm_cookie_consent")) {
112
+ return;
113
+ }
114
+ _paq.push(["setCustomUrl", router.route.path]);
115
+ _paq.push(["setDocumentTitle", router.route.data.title]);
116
+ _paq.push(["trackPageView"]);
117
+ _paq.push(["enableLinkTracking"]);
118
+ };
119
+ watch(() => router.route.data.relativePath, trackNavigation, {
120
+ immediate: true,
121
+ });
122
+ };
package/src/index.js CHANGED
@@ -4,6 +4,8 @@ import FeaturesGallery from "./components/FeaturesGallery.vue";
4
4
  import PricingTable from "./components/PricingTable.vue";
5
5
  import CTASection from "./components/CTASection.vue";
6
6
  import CookieBanner from "./components/CookieBanner.vue";
7
+ import CookieSettings from "./components/CookieSettings.vue";
8
+ import NotFound from "./components/NotFound.vue";
7
9
  import Layout from "./Layout.vue";
8
10
  import "./style.css";
9
11
 
@@ -16,6 +18,8 @@ export default {
16
18
  app.component("PricingTable", PricingTable);
17
19
  app.component("CTASection", CTASection);
18
20
  app.component("CookieBanner", CookieBanner);
21
+ app.component("CookieSettings", CookieSettings);
22
+ app.component("NotFound", NotFound);
19
23
 
20
24
  router.onAfterRouteChanged = () => {
21
25
  if (!import.meta.env.SSR) {