@eox/pages-theme-eox 0.4.17 → 0.5.1
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 +1 -1
- package/src/Layout.vue +203 -99
- package/src/components/CTASection.vue +58 -0
- package/src/components/FeatureSection.vue +35 -9
- package/src/components/FeaturesGallery.vue +48 -27
- package/src/components/PricingTable.vue +439 -0
- package/src/index.js +5 -0
- package/src/style.css +102 -24
- package/src/vitepressConfig.mjs +2 -0
package/package.json
CHANGED
package/src/Layout.vue
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<Layout :class="`layout-${frontmatter.layout}`">
|
|
3
|
-
<!-- <main class="responsive"> -->
|
|
4
3
|
<template #layout-top>
|
|
5
4
|
<slot name="layout-top"></slot>
|
|
6
5
|
<div class="top-nav at-top row center-align">
|
|
@@ -27,7 +26,7 @@
|
|
|
27
26
|
</a>
|
|
28
27
|
<div class="max"></div>
|
|
29
28
|
<button data-ui="#mobile-menu" class="circle transparent">
|
|
30
|
-
<i class="mdi mdi-close
|
|
29
|
+
<i class="mdi mdi-close"></i>
|
|
31
30
|
</button>
|
|
32
31
|
</nav>
|
|
33
32
|
<ul class="list border large-space">
|
|
@@ -37,14 +36,9 @@
|
|
|
37
36
|
:href="withBase(item.link)"
|
|
38
37
|
:target="item.target"
|
|
39
38
|
:rel="item.rel"
|
|
40
|
-
:class="
|
|
41
|
-
item.action
|
|
42
|
-
? 'button large medium-elevate cta'
|
|
43
|
-
: 'primary-text'
|
|
44
|
-
"
|
|
39
|
+
:class="item.action ? 'button large medium-elevate cta' : ''"
|
|
45
40
|
>
|
|
46
41
|
<span>{{ item.text }}</span>
|
|
47
|
-
<i v-if="item.action" class="mdi mdi-arrow-right"></i>
|
|
48
42
|
</a>
|
|
49
43
|
</li>
|
|
50
44
|
</ul>
|
|
@@ -58,12 +52,17 @@
|
|
|
58
52
|
:rel="item.rel"
|
|
59
53
|
:class="
|
|
60
54
|
item.action
|
|
61
|
-
? `button responsive large
|
|
55
|
+
? `button responsive large ${item.action === 'primary' || item.action === 'secondary' ? `${item.action} medium-elevate` : 'border no-elevate'}`
|
|
62
56
|
: 'primary-text'
|
|
63
57
|
"
|
|
64
58
|
>
|
|
65
59
|
<span>{{ item.text }}</span>
|
|
66
|
-
<i
|
|
60
|
+
<i
|
|
61
|
+
v-if="
|
|
62
|
+
item.action === 'primary' || item.action === 'secondary'
|
|
63
|
+
"
|
|
64
|
+
class="mdi mdi-arrow-right"
|
|
65
|
+
></i>
|
|
67
66
|
</a>
|
|
68
67
|
</nav>
|
|
69
68
|
</div>
|
|
@@ -85,6 +84,7 @@
|
|
|
85
84
|
<ul class="left-align no-margin">
|
|
86
85
|
<li v-for="item in theme.nav.filter((i) => !i.action)">
|
|
87
86
|
<a
|
|
87
|
+
class="button text"
|
|
88
88
|
:href="withBase(item.link)"
|
|
89
89
|
:target="item.target"
|
|
90
90
|
:rel="item.rel"
|
|
@@ -94,50 +94,86 @@
|
|
|
94
94
|
</ul>
|
|
95
95
|
</nav>
|
|
96
96
|
<div class="max"></div>
|
|
97
|
-
<nav>
|
|
97
|
+
<nav class="actions">
|
|
98
98
|
<ul class="left-align no-margin">
|
|
99
99
|
<li v-for="item in theme.nav.filter((item) => item.action)">
|
|
100
100
|
<a
|
|
101
101
|
class="button right-align"
|
|
102
|
-
:class="
|
|
102
|
+
:class="
|
|
103
|
+
item.action === 'primary' || item.action === 'secondary'
|
|
104
|
+
? item.action
|
|
105
|
+
: 'border'
|
|
106
|
+
"
|
|
103
107
|
:href="withBase(item.link)"
|
|
104
108
|
:target="item.target"
|
|
105
109
|
:rel="item.rel"
|
|
106
110
|
>
|
|
107
111
|
<span>{{ item.text }}</span>
|
|
108
|
-
<i
|
|
112
|
+
<i
|
|
113
|
+
v-if="
|
|
114
|
+
item.action === 'primary' || item.action === 'secondary'
|
|
115
|
+
"
|
|
116
|
+
class="mdi mdi-arrow-right"
|
|
117
|
+
></i>
|
|
109
118
|
</a>
|
|
110
119
|
</li>
|
|
111
120
|
</ul>
|
|
112
121
|
</nav>
|
|
113
122
|
</nav>
|
|
114
123
|
</div>
|
|
115
|
-
<header
|
|
124
|
+
<header
|
|
125
|
+
v-if="frontmatter.hero"
|
|
126
|
+
class="primary primary-gradient-background"
|
|
127
|
+
>
|
|
128
|
+
<div
|
|
129
|
+
:class="`large-padding hero-container ${frontmatter.hero.image ? 'image' : ''}`"
|
|
130
|
+
>
|
|
131
|
+
<iframe
|
|
132
|
+
v-if="frontmatter.hero.image?.src?.includes('youtube')"
|
|
133
|
+
class="hero-image large-elevate small-round"
|
|
134
|
+
style="grid-area: image; aspect-ratio: 16/9"
|
|
135
|
+
:src="frontmatter.hero.image.src"
|
|
136
|
+
frameborder="0"
|
|
137
|
+
allowfullscreen
|
|
138
|
+
/>
|
|
139
|
+
<img
|
|
140
|
+
v-else-if="frontmatter.hero.image"
|
|
141
|
+
class="hero-image large-elevate small-round"
|
|
142
|
+
style="grid-area: image"
|
|
143
|
+
:src="withBase(frontmatter.hero.image.src)"
|
|
144
|
+
/>
|
|
145
|
+
<div class="title" style="grid-area: title; align-content: end">
|
|
146
|
+
<h1 class="bold" style="font-size: clamp(2rem, 5vw, 45px)">
|
|
147
|
+
{{ frontmatter.hero.text }}
|
|
148
|
+
</h1>
|
|
149
|
+
<p>{{ frontmatter.hero.tagline }}</p>
|
|
150
|
+
</div>
|
|
151
|
+
<div style="grid-area: actions">
|
|
152
|
+
<a
|
|
153
|
+
v-for="(action, i) in frontmatter.hero.actions"
|
|
154
|
+
:href="withBase(action.link)"
|
|
155
|
+
:target="action.target"
|
|
156
|
+
:rel="action.rel"
|
|
157
|
+
class="button extra small-margin"
|
|
158
|
+
:class="
|
|
159
|
+
action.theme === 'brand'
|
|
160
|
+
? 'primary-text surface medium-elevate'
|
|
161
|
+
: action.theme === 'secondary'
|
|
162
|
+
? 'secondary'
|
|
163
|
+
: 'border no-elevate white-text'
|
|
164
|
+
"
|
|
165
|
+
:style="i === 0 ? 'margin-left: 0 !important' : ''"
|
|
166
|
+
>
|
|
167
|
+
<span>{{ action.text }}</span>
|
|
168
|
+
<i v-if="action.theme !== 'alt'" class="mdi mdi-arrow-right"></i>
|
|
169
|
+
</a>
|
|
170
|
+
</div>
|
|
171
|
+
</div>
|
|
116
172
|
<img
|
|
117
|
-
v-if="frontmatter.hero.
|
|
173
|
+
v-if="frontmatter.hero.background"
|
|
118
174
|
class="background-image"
|
|
119
|
-
:src="withBase(frontmatter.hero.
|
|
175
|
+
:src="withBase(frontmatter.hero.background.src)"
|
|
120
176
|
/>
|
|
121
|
-
<h1
|
|
122
|
-
class="bold medium-margin"
|
|
123
|
-
style="font-size: clamp(2rem, 5vw, 45px)"
|
|
124
|
-
>
|
|
125
|
-
{{ frontmatter.hero.text }}
|
|
126
|
-
</h1>
|
|
127
|
-
<p>{{ frontmatter.hero.tagline }}</p>
|
|
128
|
-
<div>
|
|
129
|
-
<a
|
|
130
|
-
v-for="action in frontmatter.hero.actions"
|
|
131
|
-
:href="withBase(action.link)"
|
|
132
|
-
:target="action.target"
|
|
133
|
-
:rel="action.rel"
|
|
134
|
-
class="button large medium-elevate cta"
|
|
135
|
-
:class="action.theme"
|
|
136
|
-
>
|
|
137
|
-
<span>{{ action.text }}</span>
|
|
138
|
-
<i class="mdi mdi-arrow-right"></i>
|
|
139
|
-
</a>
|
|
140
|
-
</div>
|
|
141
177
|
</header>
|
|
142
178
|
</template>
|
|
143
179
|
<template #page-bottom>
|
|
@@ -146,41 +182,68 @@
|
|
|
146
182
|
></FeaturesGallery>
|
|
147
183
|
</template>
|
|
148
184
|
<template #layout-bottom>
|
|
149
|
-
<footer class="surface row center-align">
|
|
150
|
-
<div class="holder">
|
|
151
|
-
<div class="">
|
|
152
|
-
|
|
185
|
+
<footer class="surface-container-low row center-align">
|
|
186
|
+
<div class="holder large-padding">
|
|
187
|
+
<div class="large-space"></div>
|
|
188
|
+
<div class="grid">
|
|
189
|
+
<div class="s12 l6">
|
|
153
190
|
<img
|
|
154
191
|
:src="withBase(theme.logo)"
|
|
155
192
|
:alt="`${site.title} logo`"
|
|
156
193
|
class="logo"
|
|
157
194
|
/>
|
|
158
|
-
<
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
195
|
+
<div class="small-space"></div>
|
|
196
|
+
<a
|
|
197
|
+
v-if="theme.nav.find((i) => i.link.includes('contact'))"
|
|
198
|
+
:href="theme.nav.find((i) => i.link.includes('contact')).link"
|
|
199
|
+
class="button small border no-margin"
|
|
200
|
+
style="color: var(--on-surface)"
|
|
201
|
+
>{{ theme.nav.find((i) => i.link.includes("contact")).text }}</a
|
|
202
|
+
>
|
|
203
|
+
<p v-html="theme.footer.copyright"></p>
|
|
204
|
+
</div>
|
|
205
|
+
<div class="s12 l6">
|
|
206
|
+
<div class="grid large-line">
|
|
207
|
+
<div class="s6">
|
|
208
|
+
<p class="bold">About</p>
|
|
209
|
+
<p v-for="item in theme.nav.filter((i) => !i.action)">
|
|
210
|
+
<a
|
|
211
|
+
:href="withBase(item.link)"
|
|
212
|
+
:target="item.target"
|
|
213
|
+
:rel="item.rel"
|
|
214
|
+
class="link"
|
|
215
|
+
>{{ item.text }}</a
|
|
216
|
+
>
|
|
217
|
+
</p>
|
|
218
|
+
</div>
|
|
219
|
+
<div class="s6">
|
|
220
|
+
<p class="bold">Legal</p>
|
|
221
|
+
<p>
|
|
222
|
+
<a
|
|
223
|
+
href="https://eox.at/impressum"
|
|
224
|
+
target="_blank"
|
|
225
|
+
class="link"
|
|
226
|
+
>About & Terms</a
|
|
227
|
+
>
|
|
228
|
+
</p>
|
|
229
|
+
<p>
|
|
230
|
+
<a
|
|
231
|
+
href="https://eox.at/privacy-notice"
|
|
232
|
+
target="_blank"
|
|
233
|
+
class="link"
|
|
234
|
+
>Privacy</a
|
|
235
|
+
>
|
|
236
|
+
</p>
|
|
237
|
+
</div>
|
|
238
|
+
</div>
|
|
239
|
+
</div>
|
|
169
240
|
</div>
|
|
170
|
-
<
|
|
171
|
-
<span v-html="theme.footer.copyright"></span>
|
|
172
|
-
<a href="https://eox.at/impressum" target="_blank">About & Terms</a>
|
|
173
|
-
<a href="https://eox.at/privacy-notice" target="_blank">Privacy</a>
|
|
174
|
-
</nav>
|
|
241
|
+
<div class="large-space"></div>
|
|
175
242
|
</div>
|
|
176
243
|
</footer>
|
|
177
244
|
<slot name="layout-bottom"></slot>
|
|
178
245
|
</template>
|
|
179
|
-
<!-- </main> -->
|
|
180
246
|
</Layout>
|
|
181
|
-
<!-- <main class="responsive">
|
|
182
|
-
|
|
183
|
-
</main> -->
|
|
184
247
|
</template>
|
|
185
248
|
|
|
186
249
|
<script setup>
|
|
@@ -193,7 +256,7 @@ if (!import.meta.env.SSR) {
|
|
|
193
256
|
const scrollListener = () => {
|
|
194
257
|
const nav = document.querySelector(".top-nav");
|
|
195
258
|
|
|
196
|
-
if (window.scrollY >
|
|
259
|
+
if (window.scrollY > nav.clientHeight) {
|
|
197
260
|
nav.classList.remove("at-top");
|
|
198
261
|
} else {
|
|
199
262
|
nav.classList.add("at-top");
|
|
@@ -206,10 +269,13 @@ if (!import.meta.env.SSR) {
|
|
|
206
269
|
<style>
|
|
207
270
|
.top-nav {
|
|
208
271
|
position: sticky;
|
|
209
|
-
z-index:
|
|
272
|
+
z-index: 2;
|
|
210
273
|
top: 0;
|
|
211
274
|
background: var(--surface);
|
|
212
|
-
box-shadow:
|
|
275
|
+
box-shadow:
|
|
276
|
+
0 1px 5px #15142e0d,
|
|
277
|
+
0 4px 16px #15142e1a,
|
|
278
|
+
inset 0 0 0 1px #ffffff80;
|
|
213
279
|
display: flex;
|
|
214
280
|
width: 100%;
|
|
215
281
|
transition: all 0.3s ease-in-out;
|
|
@@ -217,19 +283,38 @@ if (!import.meta.env.SSR) {
|
|
|
217
283
|
.top-nav.at-top {
|
|
218
284
|
box-shadow: none;
|
|
219
285
|
}
|
|
286
|
+
.Layout.layout-home .top-nav.at-top .nav-desktop .button.primary,
|
|
287
|
+
.Layout.layout-home .top-nav.at-top .nav-desktop .button.secondary {
|
|
288
|
+
display: none;
|
|
289
|
+
}
|
|
290
|
+
.Layout.layout-home .top-nav.at-top nav.actions {
|
|
291
|
+
gap: 0;
|
|
292
|
+
}
|
|
293
|
+
.top-nav:not(.at-top) > nav {
|
|
294
|
+
padding-top: 16px !important;
|
|
295
|
+
padding-bottom: 16px !important;
|
|
296
|
+
}
|
|
220
297
|
.Layout.layout-home .top-nav.at-top {
|
|
221
298
|
background: transparent;
|
|
222
299
|
color: #f7f8f8;
|
|
223
300
|
}
|
|
224
|
-
.Layout
|
|
225
|
-
color:
|
|
301
|
+
.Layout .top-nav nav .button {
|
|
302
|
+
color: var(--on-surface);
|
|
303
|
+
}
|
|
304
|
+
.Layout.layout-home .top-nav.at-top .nav-desktop .button {
|
|
305
|
+
color: var(--on-surface);
|
|
226
306
|
background: var(--surface);
|
|
227
307
|
}
|
|
228
|
-
.Layout.layout-home .top-nav.at-top nav .button.text {
|
|
308
|
+
.Layout.layout-home .top-nav.at-top .nav-desktop .button.text {
|
|
229
309
|
color: #f7f8f8;
|
|
230
310
|
background: none;
|
|
231
311
|
}
|
|
232
|
-
.Layout.layout-home .top-nav.at-top nav
|
|
312
|
+
.Layout.layout-home .top-nav.at-top .nav-desktop .button.border {
|
|
313
|
+
color: #f7f8f8;
|
|
314
|
+
border-color: #f7f8f855;
|
|
315
|
+
background: none;
|
|
316
|
+
}
|
|
317
|
+
.Layout.layout-home .top-nav.at-top > nav > a > .logo {
|
|
233
318
|
filter: brightness(0) invert(1);
|
|
234
319
|
}
|
|
235
320
|
img.logo {
|
|
@@ -251,14 +336,9 @@ nav.nav-desktop {
|
|
|
251
336
|
}
|
|
252
337
|
header {
|
|
253
338
|
margin-top: calc(var(--vp-nav-height) * -1);
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
#0f9bff 0%,
|
|
257
|
-
#004170 60%,
|
|
258
|
-
#000c14 100%
|
|
259
|
-
);
|
|
339
|
+
padding-top: 10rem !important;
|
|
340
|
+
padding-bottom: 10rem !important;
|
|
260
341
|
height: 100svh;
|
|
261
|
-
padding: 0 6rem !important;
|
|
262
342
|
}
|
|
263
343
|
@media (max-width: 1024px) {
|
|
264
344
|
header {
|
|
@@ -276,41 +356,65 @@ header > img.background-image {
|
|
|
276
356
|
left: 0;
|
|
277
357
|
object-fit: cover;
|
|
278
358
|
opacity: 0.4;
|
|
359
|
+
z-index: 0;
|
|
279
360
|
}
|
|
280
|
-
header >
|
|
361
|
+
header > .hero-container {
|
|
362
|
+
max-width: 1200px;
|
|
363
|
+
max-height: 80%;
|
|
364
|
+
margin-left: auto;
|
|
365
|
+
margin-right: auto;
|
|
366
|
+
display: grid;
|
|
367
|
+
grid-gap: 2rem;
|
|
368
|
+
grid-template-areas:
|
|
369
|
+
"title"
|
|
370
|
+
"actions"
|
|
371
|
+
"image";
|
|
372
|
+
grid-auto-rows: min-content;
|
|
373
|
+
text-align: center;
|
|
374
|
+
z-index: 1;
|
|
375
|
+
}
|
|
376
|
+
@media (min-width: 768px) {
|
|
377
|
+
header > .hero-container {
|
|
378
|
+
grid-template-areas:
|
|
379
|
+
"title"
|
|
380
|
+
"actions";
|
|
381
|
+
text-align: center;
|
|
382
|
+
}
|
|
383
|
+
header > .hero-container.image {
|
|
384
|
+
grid-template-areas:
|
|
385
|
+
"title image"
|
|
386
|
+
"actions image";
|
|
387
|
+
text-align: left;
|
|
388
|
+
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
header > .hero-container > .hero-image {
|
|
392
|
+
width: 100%;
|
|
393
|
+
object-fit: cover;
|
|
394
|
+
}
|
|
395
|
+
header > .hero-container > .title > h1 {
|
|
281
396
|
margin-bottom: 24px;
|
|
282
397
|
}
|
|
283
|
-
header > p {
|
|
398
|
+
header > .hero-container > .title > p {
|
|
284
399
|
font-size: 1.2rem;
|
|
285
|
-
margin-bottom: 40px;
|
|
286
|
-
}
|
|
287
|
-
header .cta.brand {
|
|
288
|
-
background-color: #f7f8f8;
|
|
289
|
-
color: #00060a;
|
|
290
400
|
}
|
|
291
|
-
header .cta
|
|
292
|
-
|
|
293
|
-
color: #f7f8f8;
|
|
401
|
+
header .cta:first-child {
|
|
402
|
+
margin-left: 0;
|
|
294
403
|
}
|
|
295
|
-
|
|
296
|
-
|
|
404
|
+
header .cta:last-child {
|
|
405
|
+
margin-right: 0;
|
|
297
406
|
}
|
|
298
407
|
.top-nav > nav,
|
|
299
408
|
.top-nav > .holder,
|
|
300
|
-
footer > .holder
|
|
409
|
+
footer > .holder,
|
|
410
|
+
.full-width > .holder {
|
|
301
411
|
width: 100%;
|
|
302
|
-
max-width:
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
display: flex;
|
|
306
|
-
flex-direction: column;
|
|
307
|
-
}
|
|
308
|
-
footer nav > ul {
|
|
309
|
-
margin: 0;
|
|
412
|
+
max-width: 1200px;
|
|
413
|
+
margin-left: auto;
|
|
414
|
+
margin-right: auto;
|
|
310
415
|
}
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
}
|
|
416
|
+
.top-nav > nav,
|
|
417
|
+
.top-nav > .holder {
|
|
418
|
+
max-width: 1400px;
|
|
315
419
|
}
|
|
316
420
|
</style>
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<section class="cta-section">
|
|
3
|
+
<div
|
|
4
|
+
class="center-align full-width"
|
|
5
|
+
:class="
|
|
6
|
+
dark !== undefined
|
|
7
|
+
? 'primary primary-gradient-background'
|
|
8
|
+
: 'surface-container-low'
|
|
9
|
+
"
|
|
10
|
+
>
|
|
11
|
+
<div class="large-space"></div>
|
|
12
|
+
<div class="large-space"></div>
|
|
13
|
+
<div class="holder large-padding">
|
|
14
|
+
<h3 class="large bold">
|
|
15
|
+
{{ title }}
|
|
16
|
+
</h3>
|
|
17
|
+
<p class="large-text">{{ tagline }}</p>
|
|
18
|
+
<a
|
|
19
|
+
v-if="primaryButton"
|
|
20
|
+
class="button extra small-margin"
|
|
21
|
+
:class="dark !== undefined ? 'surface' : 'primary'"
|
|
22
|
+
:href="primaryLink"
|
|
23
|
+
>{{ primaryButton }} <i class="mdi mdi-arrow-right"></i
|
|
24
|
+
></a>
|
|
25
|
+
<a
|
|
26
|
+
v-if="secondaryButton"
|
|
27
|
+
class="button extra small-margin secondary"
|
|
28
|
+
:href="secondaryLink"
|
|
29
|
+
>{{ secondaryButton }}<i class="mdi mdi-arrow-right"></i
|
|
30
|
+
></a>
|
|
31
|
+
<a
|
|
32
|
+
v-if="altButton"
|
|
33
|
+
class="button border extra small-margin"
|
|
34
|
+
:class="dark !== undefined ? 'white-text' : ''"
|
|
35
|
+
:href="altLink"
|
|
36
|
+
>{{ altButton }}</a
|
|
37
|
+
>
|
|
38
|
+
</div>
|
|
39
|
+
<div class="large-space"></div>
|
|
40
|
+
<div class="large-space"></div>
|
|
41
|
+
<div class="large-space"></div>
|
|
42
|
+
</div>
|
|
43
|
+
</section>
|
|
44
|
+
</template>
|
|
45
|
+
|
|
46
|
+
<script setup>
|
|
47
|
+
const props = defineProps([
|
|
48
|
+
"dark",
|
|
49
|
+
"primaryButton",
|
|
50
|
+
"primaryLink",
|
|
51
|
+
"secondaryButton",
|
|
52
|
+
"secondaryLink",
|
|
53
|
+
"altButton",
|
|
54
|
+
"altLink",
|
|
55
|
+
"tagline",
|
|
56
|
+
"title",
|
|
57
|
+
]);
|
|
58
|
+
</script>
|
|
@@ -1,21 +1,29 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<section
|
|
3
|
-
:class="`feature-section full-width ${landing === undefined ? '' : 'landing'}`"
|
|
3
|
+
:class="`feature-section full-width ${landing === undefined ? '' : 'landing'} ${dark !== undefined ? 'dark' : ''} ${reverse !== undefined ? 'reverse' : ''}`"
|
|
4
4
|
>
|
|
5
|
-
<div
|
|
5
|
+
<div
|
|
6
|
+
class="holder"
|
|
7
|
+
:class="dark !== undefined ? 'primary-gradient-background' : ''"
|
|
8
|
+
>
|
|
6
9
|
<div class="text">
|
|
7
|
-
<nav class="
|
|
10
|
+
<nav class="">
|
|
8
11
|
<i :class="`mdi ${icon}`"></i>
|
|
9
12
|
{{ title }}
|
|
10
13
|
</nav>
|
|
11
|
-
<h5
|
|
14
|
+
<h5 v-if="landing !== undefined" class="title">{{ tagline }}</h5>
|
|
15
|
+
<h2 v-else class="title large">
|
|
16
|
+
<strong>{{ tagline }}</strong>
|
|
17
|
+
</h2>
|
|
12
18
|
<p><slot></slot></p>
|
|
13
|
-
<
|
|
19
|
+
<div class="small-space"></div>
|
|
20
|
+
<div>
|
|
14
21
|
<a
|
|
15
22
|
v-if="primaryLink !== undefined"
|
|
16
23
|
:href="withBase(primaryLink)"
|
|
17
24
|
:target="primaryLink.includes('https://') ? '_blank' : '_self'"
|
|
18
|
-
:class="`button primary medium-elevate`"
|
|
25
|
+
:class="`button primary medium-elevate no-margin`"
|
|
26
|
+
style="margin-right: 12px !important"
|
|
19
27
|
>
|
|
20
28
|
<span>{{ primaryButton || `Read more about ${title}` }}</span>
|
|
21
29
|
<i class="mdi mdi-arrow-right"></i>
|
|
@@ -24,14 +32,28 @@
|
|
|
24
32
|
v-if="secondaryLink !== undefined"
|
|
25
33
|
:href="withBase(secondaryLink)"
|
|
26
34
|
:target="secondaryLink.includes('https://') ? '_blank' : '_self'"
|
|
27
|
-
class="button
|
|
35
|
+
class="button secondary medium-elevate no-margin"
|
|
36
|
+
style="color: var(--on-surface); margin-top: 12px !important"
|
|
28
37
|
>
|
|
29
38
|
<span>{{ secondaryButton || "Contact sales" }}</span>
|
|
30
39
|
<i class="mdi mdi-arrow-right"></i>
|
|
31
40
|
</a>
|
|
32
|
-
|
|
41
|
+
<a
|
|
42
|
+
v-if="altLink !== undefined"
|
|
43
|
+
:href="withBase(altLink)"
|
|
44
|
+
:target="altLink.includes('https://') ? '_blank' : '_self'"
|
|
45
|
+
class="button border no-margin"
|
|
46
|
+
style="color: var(--on-surface); margin-top: 12px !important"
|
|
47
|
+
>
|
|
48
|
+
<span>{{ altButton || "Contact sales" }}</span>
|
|
49
|
+
</a>
|
|
50
|
+
</div>
|
|
33
51
|
</div>
|
|
34
|
-
<img
|
|
52
|
+
<img
|
|
53
|
+
:src="withBase(image)"
|
|
54
|
+
:alt="title"
|
|
55
|
+
class="small-round medium-elevate"
|
|
56
|
+
/>
|
|
35
57
|
</div>
|
|
36
58
|
</section>
|
|
37
59
|
</template>
|
|
@@ -39,13 +61,17 @@
|
|
|
39
61
|
<script setup>
|
|
40
62
|
import { withBase } from "vitepress";
|
|
41
63
|
const props = defineProps([
|
|
64
|
+
"dark",
|
|
42
65
|
"icon",
|
|
43
66
|
"image",
|
|
44
67
|
"landing",
|
|
45
68
|
"primaryButton",
|
|
46
69
|
"primaryLink",
|
|
70
|
+
"reverse",
|
|
47
71
|
"secondaryButton",
|
|
48
72
|
"secondaryLink",
|
|
73
|
+
"altButton",
|
|
74
|
+
"altLink",
|
|
49
75
|
"tagline",
|
|
50
76
|
"title",
|
|
51
77
|
]);
|
|
@@ -5,6 +5,9 @@ import { data as features } from "../features.data.js";
|
|
|
5
5
|
const { page, site } = useData();
|
|
6
6
|
|
|
7
7
|
const featuresExcerpts = features.map((f) => {
|
|
8
|
+
if (import.meta.env.SSR) {
|
|
9
|
+
return false;
|
|
10
|
+
}
|
|
8
11
|
const el = document.createElement("html");
|
|
9
12
|
el.innerHTML = f.html;
|
|
10
13
|
const featureSection = el.querySelector("featuresection");
|
|
@@ -21,36 +24,54 @@ const featuresExcerpts = features.map((f) => {
|
|
|
21
24
|
</script>
|
|
22
25
|
|
|
23
26
|
<template>
|
|
24
|
-
<
|
|
25
|
-
<div class="
|
|
26
|
-
<
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
27
|
+
<div class="large-space"></div>
|
|
28
|
+
<div class="primary primary-gradient-background full-width">
|
|
29
|
+
<div class="large-space"></div>
|
|
30
|
+
<div class="holder large-padding">
|
|
31
|
+
<h5>More {{ site.title }} features:</h5>
|
|
32
|
+
<div class="medium-space"></div>
|
|
33
|
+
<div class="grid">
|
|
34
|
+
<ClientOnly>
|
|
35
|
+
<a
|
|
36
|
+
v-for="(feature, index) in featuresExcerpts.filter((f) => f)"
|
|
37
|
+
class="feature s12 m4 wave"
|
|
38
|
+
:href="withBase(feature.link)"
|
|
39
|
+
>
|
|
40
|
+
<article class="large-padding">
|
|
41
|
+
<div class="row top-align">
|
|
42
|
+
<img
|
|
43
|
+
v-if="feature.image"
|
|
44
|
+
class="round extra"
|
|
45
|
+
:src="withBase(feature.image)"
|
|
46
|
+
/>
|
|
47
|
+
<div class="max">
|
|
48
|
+
<h5 class="small">
|
|
49
|
+
<strong>{{ feature.title }}</strong>
|
|
50
|
+
</h5>
|
|
51
|
+
<p>{{ feature.description }}</p>
|
|
52
|
+
<div class="max"></div>
|
|
53
|
+
<nav>
|
|
54
|
+
<a class="link" :href="withBase(feature.link)">
|
|
55
|
+
<span>Read more</span>
|
|
56
|
+
</a>
|
|
57
|
+
</nav>
|
|
58
|
+
</div>
|
|
59
|
+
</div>
|
|
60
|
+
</article>
|
|
61
|
+
</a>
|
|
62
|
+
</ClientOnly>
|
|
63
|
+
</div>
|
|
64
|
+
</div>
|
|
65
|
+
<div class="large-space"></div>
|
|
49
66
|
</div>
|
|
50
67
|
</template>
|
|
51
68
|
|
|
52
69
|
<style scoped>
|
|
53
|
-
|
|
54
|
-
|
|
70
|
+
.grid > a,
|
|
71
|
+
.grid > a > article {
|
|
72
|
+
height: 100%;
|
|
73
|
+
display: flex;
|
|
74
|
+
flex-direction: column;
|
|
75
|
+
justify-content: space-between;
|
|
55
76
|
}
|
|
56
77
|
</style>
|
|
@@ -0,0 +1,439 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<!-- keep overflow visible so sticky header works -->
|
|
3
|
+
<div class="container">
|
|
4
|
+
<div class="grid">
|
|
5
|
+
<div class="m6 s12 left-align">
|
|
6
|
+
<h6 v-if="!localDetails.length" class="bold">{{ title }}</h6>
|
|
7
|
+
</div>
|
|
8
|
+
|
|
9
|
+
<div :class="`m6 s12 ${isMobile ? 'left-align' : 'right-align'}`">
|
|
10
|
+
<label
|
|
11
|
+
v-for="(option, index) in localOptions"
|
|
12
|
+
:key="option.id"
|
|
13
|
+
class="checkbox"
|
|
14
|
+
>
|
|
15
|
+
<input
|
|
16
|
+
type="checkbox"
|
|
17
|
+
:id="option.id"
|
|
18
|
+
v-model="option.checked"
|
|
19
|
+
@change="updatePrices"
|
|
20
|
+
/>
|
|
21
|
+
<span class="margin" :for="option.id"
|
|
22
|
+
><h6 :class="`bold ${isMobile ? 'medium-text' : ''}`">
|
|
23
|
+
+ {{ option.label }}
|
|
24
|
+
</h6></span
|
|
25
|
+
>
|
|
26
|
+
</label>
|
|
27
|
+
</div>
|
|
28
|
+
</div>
|
|
29
|
+
<div
|
|
30
|
+
class="grid m l surface-bright"
|
|
31
|
+
style="position: sticky; top: 72px; z-index: 1000; padding-top: 20px"
|
|
32
|
+
v-if="localDetails.length"
|
|
33
|
+
>
|
|
34
|
+
<div class="s12 m3"></div>
|
|
35
|
+
<div
|
|
36
|
+
v-for="(plan, index) in localPlans"
|
|
37
|
+
:key="`header-${index}`"
|
|
38
|
+
class="s12 m3 center-align"
|
|
39
|
+
>
|
|
40
|
+
<h6 class="primary-text bold">{{ plan.name }}</h6>
|
|
41
|
+
<ClientOnly>
|
|
42
|
+
<a
|
|
43
|
+
:href="addPlanConfig(contactLink, plan)"
|
|
44
|
+
class="button responsive bold margin-top-1 margin-bottom-2"
|
|
45
|
+
>
|
|
46
|
+
Contact us
|
|
47
|
+
</a>
|
|
48
|
+
</ClientOnly>
|
|
49
|
+
</div>
|
|
50
|
+
</div>
|
|
51
|
+
|
|
52
|
+
<h6 v-if="localDetails.length" class="bold padding-4">{{ title }}</h6>
|
|
53
|
+
<!-- Main Plans Table -->
|
|
54
|
+
<div class="wrapper" :style="gridStyle">
|
|
55
|
+
<div class="cell orig-col-1 m l top-margin">
|
|
56
|
+
<h6 v-if="!localDetails.length" class="bold small">Plans:</h6>
|
|
57
|
+
</div>
|
|
58
|
+
<div
|
|
59
|
+
v-for="(plan, index) in localPlans"
|
|
60
|
+
:key="'head-' + index"
|
|
61
|
+
class="cell surface-container-low small-top-round bold top-margin"
|
|
62
|
+
:class="`orig-col-${index + 2}`"
|
|
63
|
+
>
|
|
64
|
+
<h6 class="primary-text bold">{{ plan.name }}</h6>
|
|
65
|
+
</div>
|
|
66
|
+
|
|
67
|
+
<div class="cell orig-col-1 m l">Price (per month):</div>
|
|
68
|
+
<div
|
|
69
|
+
v-for="(plan, index) in localPlans"
|
|
70
|
+
:key="'price-' + index"
|
|
71
|
+
class="surface-container-low primary-text bold"
|
|
72
|
+
:class="`cell orig-col-${index + 2}`"
|
|
73
|
+
>
|
|
74
|
+
<div class="small-round surface-container medium-padding">
|
|
75
|
+
<h4 class="primary-text bold">€ {{ calculatePrice(plan) }},-</h4>
|
|
76
|
+
</div>
|
|
77
|
+
</div>
|
|
78
|
+
|
|
79
|
+
<template
|
|
80
|
+
v-for="(row, rowIndex) in getRowKeys(localPlans)"
|
|
81
|
+
:key="'row-' + rowIndex"
|
|
82
|
+
>
|
|
83
|
+
<div
|
|
84
|
+
v-if="typeof row === 'string'"
|
|
85
|
+
class="cell orig-col-1 m l"
|
|
86
|
+
v-html="row + ':'"
|
|
87
|
+
></div>
|
|
88
|
+
|
|
89
|
+
<div
|
|
90
|
+
v-for="(plan, planIndex) in localPlans"
|
|
91
|
+
:key="`${planIndex}-${row}`"
|
|
92
|
+
class="bold surface-container-low"
|
|
93
|
+
:class="`cell orig-col-${planIndex + 2} ${rowIndex === getRowKeys(localPlans).length - 1 && !config.showSales ? 'small-bottom-round' : ''}`"
|
|
94
|
+
>
|
|
95
|
+
<div
|
|
96
|
+
v-if="typeof plan.details[row] === 'string'"
|
|
97
|
+
class="s grey-text"
|
|
98
|
+
v-html="row"
|
|
99
|
+
></div>
|
|
100
|
+
<div
|
|
101
|
+
v-if="typeof plan.details[row] === 'object'"
|
|
102
|
+
:class="`${plan.details[row].alternative ? 'border primary-border small-round small-padding' : ''}`"
|
|
103
|
+
>
|
|
104
|
+
<span v-html="plan.details[row].text"></span>
|
|
105
|
+
<template v-if="plan.details[row].alternative">
|
|
106
|
+
<hr class="medium" />
|
|
107
|
+
<label class="alternative checkbox bold">
|
|
108
|
+
<input
|
|
109
|
+
type="checkbox"
|
|
110
|
+
:id="`${plan.name}-${row}`"
|
|
111
|
+
@input="$forceUpdate()"
|
|
112
|
+
/>
|
|
113
|
+
<span :for="`${plan.name}-${row}`"
|
|
114
|
+
><h6 class="small bold">
|
|
115
|
+
{{ plan.details[row].alternative.name }}
|
|
116
|
+
</h6></span
|
|
117
|
+
>
|
|
118
|
+
</label>
|
|
119
|
+
<span
|
|
120
|
+
:for="`${plan.name}-${row}`"
|
|
121
|
+
v-html="plan.details[row].alternative.text"
|
|
122
|
+
style="white-space: wrap; display: block"
|
|
123
|
+
></span>
|
|
124
|
+
</template>
|
|
125
|
+
</div>
|
|
126
|
+
<template v-else>
|
|
127
|
+
<span v-html="plan.details[row]"></span>
|
|
128
|
+
</template>
|
|
129
|
+
</div>
|
|
130
|
+
</template>
|
|
131
|
+
<div class="cell orig-col-1 m l"></div>
|
|
132
|
+
<div
|
|
133
|
+
v-for="(plan, index) in localPlans"
|
|
134
|
+
:key="'cta-' + index"
|
|
135
|
+
:class="`surface-container-low small-bottom-round cell orig-col-${index + 2} center-align`"
|
|
136
|
+
>
|
|
137
|
+
<a v-if="plan.link" :href="plan.link"
|
|
138
|
+
>See all features
|
|
139
|
+
<svg
|
|
140
|
+
style="width: 16px; height: 16px"
|
|
141
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
142
|
+
viewBox="0 0 24 24"
|
|
143
|
+
>
|
|
144
|
+
<title>chevron-right</title>
|
|
145
|
+
<path
|
|
146
|
+
d="M8.59,16.58L13.17,12L8.59,7.41L10,6L16,12L10,18L8.59,16.58Z"
|
|
147
|
+
/>
|
|
148
|
+
</svg>
|
|
149
|
+
</a>
|
|
150
|
+
</div>
|
|
151
|
+
<!-- Contact us section -->
|
|
152
|
+
<div class="cell orig-col-1 m l"></div>
|
|
153
|
+
<div
|
|
154
|
+
v-if="showSales"
|
|
155
|
+
v-for="(plan, index) in localPlans"
|
|
156
|
+
:key="'cta-' + index"
|
|
157
|
+
:class="`surface-container-low small-bottom-round cell orig-col-${index + 2} center-align`"
|
|
158
|
+
>
|
|
159
|
+
<ClientOnly>
|
|
160
|
+
<a
|
|
161
|
+
v-if="showSales"
|
|
162
|
+
:href="addPlanConfig(contactLink, plan)"
|
|
163
|
+
class="button responsive bold"
|
|
164
|
+
>Contact us</a
|
|
165
|
+
>
|
|
166
|
+
</ClientOnly>
|
|
167
|
+
</div>
|
|
168
|
+
</div>
|
|
169
|
+
|
|
170
|
+
<!-- Add-on Details Tables -->
|
|
171
|
+
<template v-for="(detail, detailIndex) in localDetails" :key="detailIndex">
|
|
172
|
+
<hr class="margin-top-4" />
|
|
173
|
+
<div class="margin-top-2">
|
|
174
|
+
<div class="s12">
|
|
175
|
+
<h5 class="bold">{{ detail.title }}</h5>
|
|
176
|
+
</div>
|
|
177
|
+
</div>
|
|
178
|
+
|
|
179
|
+
<div class="wrapper" :style="secondaryGridStyle">
|
|
180
|
+
<div class="cell cell orig-col-1 m l">
|
|
181
|
+
<div class="">Additional price:</div>
|
|
182
|
+
</div>
|
|
183
|
+
<div
|
|
184
|
+
v-for="(plan, detailPlanIndex) in detail.plans"
|
|
185
|
+
:key="detailPlanIndex"
|
|
186
|
+
class="surface-container-low primary-text bold small-top-round top-margin"
|
|
187
|
+
:class="`cell orig-col-${detailPlanIndex + 2}`"
|
|
188
|
+
>
|
|
189
|
+
<h6 class="primary-text bold s">{{ plan.name }}</h6>
|
|
190
|
+
<h6 v-if="plan.additionalPrice" class="primary-text bold">
|
|
191
|
+
+ € {{ plan.additionalPrice }},-
|
|
192
|
+
</h6>
|
|
193
|
+
</div>
|
|
194
|
+
<template
|
|
195
|
+
v-for="(row, rowIndex) in getRowKeys(detail.plans)"
|
|
196
|
+
:key="'row-' + rowIndex"
|
|
197
|
+
>
|
|
198
|
+
<div
|
|
199
|
+
v-if="typeof row === 'string'"
|
|
200
|
+
class="cell orig-col-1 m l"
|
|
201
|
+
v-html="row + ':'"
|
|
202
|
+
></div>
|
|
203
|
+
<div
|
|
204
|
+
v-for="(plan, planIndex) in detail.plans"
|
|
205
|
+
:key="`${planIndex}-${row}`"
|
|
206
|
+
class="bold surface-container-low"
|
|
207
|
+
:class="`cell orig-col-${planIndex + 2} ${rowIndex === getRowKeys(detail.plans).length - 1 && !config.showSales ? 'small-bottom-round' : ''}`"
|
|
208
|
+
>
|
|
209
|
+
<div class="s grey-text" v-html="row + ':'"></div>
|
|
210
|
+
<template
|
|
211
|
+
v-if="
|
|
212
|
+
typeof plan.details[row] === 'object' &&
|
|
213
|
+
plan.details[row]?.checkmark
|
|
214
|
+
"
|
|
215
|
+
>
|
|
216
|
+
<i class="mdi mdi-check-circle-outline"></i>
|
|
217
|
+
</template>
|
|
218
|
+
<template v-else>
|
|
219
|
+
<span v-html="plan.details[row]"></span>
|
|
220
|
+
</template>
|
|
221
|
+
</div>
|
|
222
|
+
</template>
|
|
223
|
+
</div>
|
|
224
|
+
</template>
|
|
225
|
+
|
|
226
|
+
<p class="small grey-text m-4" v-if="showVAT">
|
|
227
|
+
* All prices are given excluding VAT. Prices are valid until 2025-06-30
|
|
228
|
+
</p>
|
|
229
|
+
</div>
|
|
230
|
+
</template>
|
|
231
|
+
|
|
232
|
+
<script>
|
|
233
|
+
export default {
|
|
234
|
+
name: "PricingTable",
|
|
235
|
+
props: {
|
|
236
|
+
config: {
|
|
237
|
+
type: Object,
|
|
238
|
+
default: () => ({
|
|
239
|
+
title: "Pricing Options",
|
|
240
|
+
showSales: true,
|
|
241
|
+
showVAT: true,
|
|
242
|
+
options: [],
|
|
243
|
+
plans: [],
|
|
244
|
+
details: [],
|
|
245
|
+
}),
|
|
246
|
+
},
|
|
247
|
+
},
|
|
248
|
+
data() {
|
|
249
|
+
return {
|
|
250
|
+
title: "",
|
|
251
|
+
showSales: true,
|
|
252
|
+
contactLink: "",
|
|
253
|
+
showVAT: true,
|
|
254
|
+
localOptions: [],
|
|
255
|
+
localPlans: [],
|
|
256
|
+
localDetails: [],
|
|
257
|
+
isMobile: !import.meta.env.SSR && window.innerWidth <= 768,
|
|
258
|
+
};
|
|
259
|
+
},
|
|
260
|
+
computed: {
|
|
261
|
+
gridStyle() {
|
|
262
|
+
// dynamically asign number of cols in grid when not on mobile
|
|
263
|
+
if (!this.isMobile) {
|
|
264
|
+
return {
|
|
265
|
+
"grid-template-columns": `repeat(${this.localPlans.length + 1}, 1fr)`,
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
},
|
|
269
|
+
secondaryGridStyle() {
|
|
270
|
+
if (!this.isMobile && this.localDetails) {
|
|
271
|
+
return {
|
|
272
|
+
"grid-template-columns": `repeat(${this.localDetails[0].plans.length + 1}, 1fr)`,
|
|
273
|
+
};
|
|
274
|
+
}
|
|
275
|
+
},
|
|
276
|
+
},
|
|
277
|
+
methods: {
|
|
278
|
+
calculatePrice(plan) {
|
|
279
|
+
const basePrice = plan.basePrice;
|
|
280
|
+
const additionalPrice = this.localOptions
|
|
281
|
+
.filter((option) => option.checked)
|
|
282
|
+
.reduce((sum, option) => sum + (option.modifier || 0), 0);
|
|
283
|
+
|
|
284
|
+
return basePrice + additionalPrice;
|
|
285
|
+
},
|
|
286
|
+
getRowKeys(plansArray) {
|
|
287
|
+
const keys = new Set();
|
|
288
|
+
plansArray.forEach((plan) => {
|
|
289
|
+
Object.keys(plan.details).forEach((key) => keys.add(key));
|
|
290
|
+
});
|
|
291
|
+
return Array.from(keys);
|
|
292
|
+
},
|
|
293
|
+
updatePrices() {
|
|
294
|
+
this.$forceUpdate();
|
|
295
|
+
},
|
|
296
|
+
initializeData() {
|
|
297
|
+
this.title = this.config.title ?? this.title;
|
|
298
|
+
this.showSales = this.config.showSales ?? this.showSales;
|
|
299
|
+
this.contactLink = this.config.contactLink ?? this.contactLink;
|
|
300
|
+
this.showVAT = this.config.showVAT ?? this.showVAT;
|
|
301
|
+
this.localOptions = this.config.options ?? this.localOptions;
|
|
302
|
+
this.localPlans = this.config.plans ?? this.localPlans;
|
|
303
|
+
this.localDetails = this.config.details ?? this.localDetails;
|
|
304
|
+
},
|
|
305
|
+
handleResize() {
|
|
306
|
+
this.isMobile = window.innerWidth <= 768;
|
|
307
|
+
},
|
|
308
|
+
updateOrderStyles() {
|
|
309
|
+
if (import.meta.env.SSR) {
|
|
310
|
+
return;
|
|
311
|
+
}
|
|
312
|
+
const count = this.localPlans.length + 1;
|
|
313
|
+
for (let i = 1; i <= count; i++) {
|
|
314
|
+
const elements = document.querySelectorAll(`.orig-col-${i}`);
|
|
315
|
+
elements.forEach((el) => {
|
|
316
|
+
el.style.order = this.isMobile ? i : "";
|
|
317
|
+
});
|
|
318
|
+
}
|
|
319
|
+
},
|
|
320
|
+
addPlanConfig(contactLink, plan) {
|
|
321
|
+
if (import.meta.env.SSR) {
|
|
322
|
+
return "";
|
|
323
|
+
}
|
|
324
|
+
const parts = contactLink.split("?");
|
|
325
|
+
let search = parts[1] || "";
|
|
326
|
+
const params = new URLSearchParams(search);
|
|
327
|
+
params.append("plan", plan.name);
|
|
328
|
+
const alternative = Object.entries(plan.details).find(
|
|
329
|
+
([key, value]) => value.alternative,
|
|
330
|
+
);
|
|
331
|
+
if (alternative) {
|
|
332
|
+
const checked = document.querySelector(
|
|
333
|
+
`[id='${plan.name}-${alternative[0]}']`,
|
|
334
|
+
)?.checked;
|
|
335
|
+
if (checked) {
|
|
336
|
+
params.append("alternative", alternative[1].alternative.name);
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
if (this.localOptions.length > 0)
|
|
340
|
+
[
|
|
341
|
+
this.localOptions.forEach((o) => {
|
|
342
|
+
const checked = document.querySelector(`input#${o.id}`)?.checked;
|
|
343
|
+
if (checked) {
|
|
344
|
+
params.append("option", o.label);
|
|
345
|
+
}
|
|
346
|
+
}),
|
|
347
|
+
];
|
|
348
|
+
search = params.toString();
|
|
349
|
+
return `${parts[0]}${search ? `?${search}` : ""}`;
|
|
350
|
+
},
|
|
351
|
+
},
|
|
352
|
+
created() {
|
|
353
|
+
this.initializeData();
|
|
354
|
+
},
|
|
355
|
+
mounted() {
|
|
356
|
+
window.addEventListener("resize", this.handleResize);
|
|
357
|
+
this.updateOrderStyles();
|
|
358
|
+
},
|
|
359
|
+
beforeDestroy() {
|
|
360
|
+
window.removeEventListener("resize", this.handleResize);
|
|
361
|
+
},
|
|
362
|
+
watch: {
|
|
363
|
+
options: {
|
|
364
|
+
handler() {
|
|
365
|
+
this.initializeData();
|
|
366
|
+
},
|
|
367
|
+
deep: true,
|
|
368
|
+
},
|
|
369
|
+
plans: {
|
|
370
|
+
handler() {
|
|
371
|
+
this.initializeData();
|
|
372
|
+
},
|
|
373
|
+
deep: true,
|
|
374
|
+
},
|
|
375
|
+
details: {
|
|
376
|
+
handler() {
|
|
377
|
+
this.initializeData();
|
|
378
|
+
},
|
|
379
|
+
deep: true,
|
|
380
|
+
},
|
|
381
|
+
localOptions: {
|
|
382
|
+
handler() {
|
|
383
|
+
this.$forceUpdate();
|
|
384
|
+
},
|
|
385
|
+
deep: true,
|
|
386
|
+
},
|
|
387
|
+
isMobile() {
|
|
388
|
+
if (!import.meta.env.SSR) {
|
|
389
|
+
this.updateOrderStyles();
|
|
390
|
+
}
|
|
391
|
+
},
|
|
392
|
+
},
|
|
393
|
+
};
|
|
394
|
+
</script>
|
|
395
|
+
|
|
396
|
+
<style scoped>
|
|
397
|
+
.container {
|
|
398
|
+
padding: 3rem 0;
|
|
399
|
+
z-index: 0;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
.wrapper {
|
|
403
|
+
display: grid;
|
|
404
|
+
grid-template-columns: 1fr;
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
.cell {
|
|
408
|
+
padding: 8px 16px 24px 16px;
|
|
409
|
+
overflow: hidden;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
.small-top-round {
|
|
413
|
+
border-top-left-radius: 16px;
|
|
414
|
+
border-top-right-radius: 16px;
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
.small-bottom-round {
|
|
418
|
+
border-bottom-left-radius: 16px;
|
|
419
|
+
border-bottom-right-radius: 16px;
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
@media screen and (min-width: 769px) {
|
|
423
|
+
.wrapper {
|
|
424
|
+
column-gap: 20px;
|
|
425
|
+
row-gap: 0;
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
.cell {
|
|
429
|
+
order: initial;
|
|
430
|
+
margin-top: 0;
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
.alternative:has(input:not(:checked)) + span,
|
|
435
|
+
span:has(+ * + * > input:checked) {
|
|
436
|
+
text-decoration: line-through;
|
|
437
|
+
opacity: 0.5;
|
|
438
|
+
}
|
|
439
|
+
</style>
|
package/src/index.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import DefaultTheme from "vitepress/theme";
|
|
2
2
|
import FeatureSection from "./components/FeatureSection.vue";
|
|
3
3
|
import FeaturesGallery from "./components/FeaturesGallery.vue";
|
|
4
|
+
import PricingTable from "./components/PricingTable.vue";
|
|
5
|
+
import CTASection from "./components/CTASection.vue";
|
|
4
6
|
import Layout from "./Layout.vue";
|
|
5
7
|
import "./style.css";
|
|
6
8
|
|
|
@@ -10,6 +12,8 @@ export default {
|
|
|
10
12
|
enhanceApp({ app, router, siteData }) {
|
|
11
13
|
app.component("FeatureSection", FeatureSection);
|
|
12
14
|
app.component("FeaturesGallery", FeaturesGallery);
|
|
15
|
+
app.component("PricingTable", PricingTable);
|
|
16
|
+
app.component("CTASection", CTASection);
|
|
13
17
|
|
|
14
18
|
router.onAfterRouteChanged = () => {
|
|
15
19
|
if (!import.meta.env.SSR) {
|
|
@@ -42,6 +46,7 @@ export default {
|
|
|
42
46
|
:root, body.light {
|
|
43
47
|
/* EOxUI */
|
|
44
48
|
--primary: ${siteData.value.themeConfig.theme.primaryColor};
|
|
49
|
+
--secondary: ${siteData.value.themeConfig.theme.secondaryColor};
|
|
45
50
|
}
|
|
46
51
|
`),
|
|
47
52
|
);
|
package/src/style.css
CHANGED
|
@@ -6,7 +6,6 @@
|
|
|
6
6
|
|
|
7
7
|
body {
|
|
8
8
|
margin: 0;
|
|
9
|
-
font-size: 1rem;
|
|
10
9
|
}
|
|
11
10
|
|
|
12
11
|
.VPNav,
|
|
@@ -24,11 +23,18 @@ body {
|
|
|
24
23
|
.VPContent h2.large,
|
|
25
24
|
.VPContent h3.large {
|
|
26
25
|
font-size: normal;
|
|
27
|
-
font-weight: normal;
|
|
28
26
|
line-height: normal;
|
|
29
27
|
border-top: none;
|
|
30
28
|
}
|
|
31
29
|
|
|
30
|
+
.VPContent h1,
|
|
31
|
+
.VPContent h2,
|
|
32
|
+
.VPContent h3,
|
|
33
|
+
.VPContent h4,
|
|
34
|
+
.VPContent h5 {
|
|
35
|
+
font-weight: bold;
|
|
36
|
+
}
|
|
37
|
+
|
|
32
38
|
*:has(> nav.top:not(.s, .m, .l)) {
|
|
33
39
|
padding-block-start: 0;
|
|
34
40
|
}
|
|
@@ -52,8 +58,67 @@ body {
|
|
|
52
58
|
.VPPage {
|
|
53
59
|
margin: auto;
|
|
54
60
|
width: 100%;
|
|
55
|
-
max-width:
|
|
56
|
-
padding: 48px 24px;
|
|
61
|
+
max-width: 1200px;
|
|
62
|
+
padding: 48px 24px 0;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
.VPHome {
|
|
66
|
+
margin-bottom: 0 !important;
|
|
67
|
+
padding-left: 0;
|
|
68
|
+
padding-right: 0;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
.VPHome .vp-doc a {
|
|
72
|
+
font-weight: 400;
|
|
73
|
+
color: inherit;
|
|
74
|
+
}
|
|
75
|
+
.VPHome .vp-doc a.button {
|
|
76
|
+
font-weight: 500;
|
|
77
|
+
color: var(--on-primary);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
.VPHome .vp-doc h1 {
|
|
81
|
+
font-size: 3.5625rem;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
.VPHome .vp-doc h2 {
|
|
85
|
+
font-size: 2.8125rem;
|
|
86
|
+
border-top: none;
|
|
87
|
+
margin-block-start: 1rem;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
.VPHome .vp-doc h3 {
|
|
91
|
+
font-size: 2.25rem;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
.VPHome .vp-doc h4 {
|
|
95
|
+
font-size: 2rem;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
.VPHome .vp-doc h5 {
|
|
99
|
+
font-size: 1.75rem;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
.VPHome .vp-doc h1 a.header-anchor,
|
|
103
|
+
.VPHome .vp-doc h2 a.header-anchor,
|
|
104
|
+
.VPHome .vp-doc h3 a.header-anchor,
|
|
105
|
+
.VPHome .vp-doc h4 a.header-anchor,
|
|
106
|
+
.VPHome .vp-doc h5 a.header-anchor {
|
|
107
|
+
display: none;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
.VPLocalNav {
|
|
111
|
+
display: none;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
.vp-doc.container {
|
|
115
|
+
padding-left: 24px;
|
|
116
|
+
padding-right: 24px;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**/
|
|
120
|
+
footer {
|
|
121
|
+
margin-top: 0 !important;
|
|
57
122
|
}
|
|
58
123
|
|
|
59
124
|
/**/
|
|
@@ -66,6 +131,10 @@ button.text:hover,
|
|
|
66
131
|
box-shadow: none;
|
|
67
132
|
}
|
|
68
133
|
|
|
134
|
+
:is(button, .button).border {
|
|
135
|
+
border-color: var(--outline-variant);
|
|
136
|
+
}
|
|
137
|
+
|
|
69
138
|
/* Feature sections */
|
|
70
139
|
.full-width {
|
|
71
140
|
width: 100svw;
|
|
@@ -86,17 +155,20 @@ button.text:hover,
|
|
|
86
155
|
flex-direction: column;
|
|
87
156
|
align-items: center;
|
|
88
157
|
justify-content: space-between;
|
|
89
|
-
|
|
90
|
-
|
|
158
|
+
margin-left: auto;
|
|
159
|
+
margin-right: auto;
|
|
160
|
+
max-width: 1200px;
|
|
161
|
+
padding-bottom: 32px;
|
|
91
162
|
}
|
|
92
163
|
.feature-section.landing .holder {
|
|
164
|
+
padding: 32px 0;
|
|
93
165
|
background: var(--surface-container-low);
|
|
94
166
|
}
|
|
95
167
|
.feature-section.dark .holder {
|
|
96
168
|
background: radial-gradient(
|
|
97
169
|
farthest-corner at 100% 100%,
|
|
98
170
|
#0f9bff 0%,
|
|
99
|
-
|
|
171
|
+
var(--primary) 60%,
|
|
100
172
|
#000c14 100%
|
|
101
173
|
);
|
|
102
174
|
color: #fff;
|
|
@@ -111,6 +183,11 @@ button.text:hover,
|
|
|
111
183
|
.feature-section .text {
|
|
112
184
|
padding: 48px;
|
|
113
185
|
}
|
|
186
|
+
.feature-section:not(.landing) .text {
|
|
187
|
+
padding-top: 0;
|
|
188
|
+
padding-left: 24px;
|
|
189
|
+
padding-right: 24px;
|
|
190
|
+
}
|
|
114
191
|
.feature-section .icon {
|
|
115
192
|
background: #61616133;
|
|
116
193
|
padding: 16px;
|
|
@@ -125,35 +202,27 @@ button.text:hover,
|
|
|
125
202
|
}
|
|
126
203
|
.feature-section img {
|
|
127
204
|
max-width: 80%;
|
|
128
|
-
border-radius: 8px;
|
|
129
205
|
background: var(--surface-bright);
|
|
130
|
-
box-shadow: 0px 11px 15px 0px #00000033;
|
|
131
206
|
}
|
|
132
207
|
@media (min-width: 1024px) {
|
|
133
|
-
.feature-section
|
|
208
|
+
.feature-section.landing,
|
|
209
|
+
.cta-section {
|
|
134
210
|
padding: 64px;
|
|
135
211
|
padding: clamp(64px, 10%, 128px);
|
|
136
212
|
}
|
|
137
|
-
.feature-section:not(.feature-section.landing) {
|
|
138
|
-
padding-top: 0;
|
|
139
|
-
}
|
|
140
213
|
.feature-section .holder {
|
|
141
214
|
flex-direction: row;
|
|
142
215
|
padding: 68px 0;
|
|
143
216
|
border-radius: 12px;
|
|
144
217
|
}
|
|
218
|
+
.feature-section:not(.feature-section.landing) .holder {
|
|
219
|
+
padding-top: 0;
|
|
220
|
+
padding-bottom: 120px;
|
|
221
|
+
}
|
|
145
222
|
.feature-section img {
|
|
146
223
|
max-width: 60%;
|
|
147
224
|
transform: translateX(10%);
|
|
148
225
|
}
|
|
149
|
-
.feature-section.reverse.dark .holder {
|
|
150
|
-
background: radial-gradient(
|
|
151
|
-
farthest-corner at 0% 100%,
|
|
152
|
-
#0f9bff 0%,
|
|
153
|
-
#004170 60%,
|
|
154
|
-
#000c14 100%
|
|
155
|
-
);
|
|
156
|
-
}
|
|
157
226
|
.feature-section.reverse .text {
|
|
158
227
|
order: 1;
|
|
159
228
|
}
|
|
@@ -166,10 +235,10 @@ button.text:hover,
|
|
|
166
235
|
}
|
|
167
236
|
}
|
|
168
237
|
@media (min-width: 1280px) {
|
|
169
|
-
.feature-section {
|
|
170
|
-
padding: 128px;
|
|
238
|
+
.feature-section.landing {
|
|
239
|
+
padding: 128px 24px;
|
|
171
240
|
}
|
|
172
|
-
.feature-section .holder {
|
|
241
|
+
.feature-section.landing .holder {
|
|
173
242
|
flex-direction: row;
|
|
174
243
|
padding: 68px 0;
|
|
175
244
|
}
|
|
@@ -223,3 +292,12 @@ button.text:hover,
|
|
|
223
292
|
}
|
|
224
293
|
}
|
|
225
294
|
}
|
|
295
|
+
|
|
296
|
+
.primary-gradient-background {
|
|
297
|
+
background: radial-gradient(
|
|
298
|
+
74.48% 130.95% at 50% -12.27%,
|
|
299
|
+
hsl(from var(--primary) h s calc(l + 7)) 0%,
|
|
300
|
+
var(--primary) 45%,
|
|
301
|
+
hsl(from var(--primary) h s calc(l - 20)) 100%
|
|
302
|
+
) !important;
|
|
303
|
+
}
|
package/src/vitepressConfig.mjs
CHANGED
|
@@ -14,6 +14,8 @@ export const generate = (brandConfig) => ({
|
|
|
14
14
|
siteTitle: false,
|
|
15
15
|
theme: {
|
|
16
16
|
primaryColor: brandConfig.theme?.primary_color,
|
|
17
|
+
secondaryColor:
|
|
18
|
+
brandConfig.theme?.secondary_color || brandConfig.theme?.primary_color,
|
|
17
19
|
},
|
|
18
20
|
logo: brandConfig.logo,
|
|
19
21
|
footer: {
|