@stackonward/cli 1.3.3 → 1.4.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/index.js +33 -34
- package/dist/index.js.map +1 -1
- package/dist/resources/components/nuxt/site-source/app/app.vue +4 -2
- package/dist/resources/components/nuxt/site-source/app/assets/css/site-shell.css +50 -50
- package/dist/resources/components/nuxt/site-source/app/components/{site/ApplicationHeader.vue → app/Header.vue} +3 -3
- package/dist/resources/components/nuxt/site-source/app/components/{site/ApplicationShell.vue → app/Shell.vue} +2 -2
- package/dist/resources/components/nuxt/site-source/app/components/{site/ApplicationSidebar.vue → app/Sidebar.vue} +2 -2
- package/dist/resources/components/nuxt/site-source/app/components/{ui/CheckoutStatus.vue → checkout/Status.vue} +8 -3
- package/dist/resources/components/nuxt/site-source/app/components/{ui/CheckoutSummary.vue → checkout/Summary.vue} +11 -11
- package/dist/resources/components/nuxt/site-source/app/components/{site/CookieConsent.vue → consent/Banner.vue} +3 -3
- package/dist/resources/components/nuxt/site-source/app/components/{site/ContentArticlePage.vue → content/Article.vue} +3 -3
- package/dist/resources/components/nuxt/site-source/app/components/{site/ContentListPage.vue → content/List.vue} +2 -2
- package/dist/resources/components/nuxt/site-source/app/components/{ui/GoogleSignInButton.vue → google/SignInButton.vue} +3 -3
- package/dist/resources/components/nuxt/site-source/app/components/{site/LocaleSwitcher.vue → locale/Menu.vue} +2 -2
- package/dist/resources/components/nuxt/site-source/app/components/site/{MarketingFooter.vue → Footer.vue} +8 -8
- package/dist/resources/components/nuxt/site-source/app/components/site/{MarketingHeader.vue → Header.vue} +27 -27
- package/dist/resources/components/nuxt/site-source/app/components/site/Shell.vue +29 -0
- package/dist/resources/components/nuxt/site-source/app/components/{site/ThemeSwitcher.vue → theme/Menu.vue} +2 -2
- package/dist/resources/components/nuxt/site-source/app/components/ui/{BaseButton.vue → Button.vue} +1 -8
- package/dist/resources/components/nuxt/site-source/app/components/ui/InfiniteScroll.vue +2 -2
- package/dist/resources/components/nuxt/site-source/app/components/{ui/YoutubeEmbed.vue → youtube/Embed.vue} +10 -2
- package/dist/resources/components/nuxt/site-source/app/composables/useConsent.ts +8 -10
- package/dist/resources/components/nuxt/site-source/app/composables/useNavigation.ts +9 -14
- package/dist/resources/components/nuxt/site-source/app/composables/useSeo.ts +3 -1
- package/dist/resources/components/nuxt/site-source/app/composables/useSiteShell.ts +19 -22
- package/dist/resources/components/nuxt/site-source/app/consent/types.ts +59 -0
- package/dist/resources/components/nuxt/site-source/app/content/types.ts +17 -0
- package/dist/resources/components/nuxt/site-source/app/layouts/app.vue +2 -2
- package/dist/resources/components/nuxt/site-source/app/layouts/default.vue +2 -2
- package/dist/resources/components/nuxt/site-source/app/navigation/types.ts +68 -0
- package/dist/resources/components/nuxt/site-source/app/plugins/consent.ts +2 -2
- package/dist/resources/components/nuxt/site-source/app/site/config.ts +35 -0
- package/dist/resources/components/registry.json +63 -51
- package/dist/resources/registry.json +6 -6
- package/dist/resources/templates/nuxt/README.md +3 -3
- package/dist/resources/templates/nuxt/app/app.config.ts +1 -1
- package/dist/resources/templates/nuxt/app/i18n/locales/en.json +1 -1
- package/dist/resources/templates/nuxt/app/pages/app/index.vue +1 -1
- package/dist/resources/templates/nuxt/app/pages/authors/[slug].vue +2 -2
- package/dist/resources/templates/nuxt/app/pages/blog/[slug].vue +4 -3
- package/dist/resources/templates/nuxt/app/pages/blog/category/[category].vue +2 -2
- package/dist/resources/templates/nuxt/app/pages/blog/index.vue +2 -2
- package/dist/resources/templates/nuxt/app/pages/legal/cookies.vue +3 -3
- package/dist/resources/templates/nuxt/app/pages/legal/privacy.vue +2 -2
- package/dist/resources/templates/nuxt/app/pages/legal/terms.vue +2 -2
- package/dist/resources/templates/nuxt/app/pages/ui/index.vue +45 -45
- package/dist/resources/templates/nuxt/nuxt.config.ts +4 -10
- package/dist/resources/templates/nuxt/package.json +2 -2
- package/dist/resources/templates/nuxt/test/e2e/rendering.test.ts +1 -0
- package/dist/resources/templates/nuxt/test/unit/consent.test.ts +4 -7
- package/package.json +3 -3
- package/dist/resources/components/nuxt/site-source/app/components/site/MarketingShell.vue +0 -33
- package/dist/resources/components/nuxt/site-source/app/types/site-shell.ts +0 -172
- /package/dist/resources/components/nuxt/site-source/app/components/{site/ConsentSettingsButton.vue → consent/Settings.vue} +0 -0
- /package/dist/resources/components/nuxt/site-source/app/components/{site/StaticPage.vue → content/Document.vue} +0 -0
- /package/dist/resources/components/nuxt/site-source/app/components/{site/ToastViewport.vue → feedback/Toast.vue} +0 -0
- /package/dist/resources/components/nuxt/site-source/app/components/site/{SiteBrand.vue → Brand.vue} +0 -0
- /package/dist/resources/components/nuxt/site-source/app/components/ui/{BaseGradientBorder.vue → GradientBorder.vue} +0 -0
- /package/dist/resources/components/nuxt/site-source/app/components/ui/{BaseModal.vue → Modal.vue} +0 -0
- /package/dist/resources/components/nuxt/site-source/app/components/ui/{BasePopover.vue → Popover.vue} +0 -0
- /package/dist/resources/components/nuxt/site-source/app/components/ui/{BaseText.vue → Text.vue} +0 -0
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
+
import { formatTitle } from "@stackonward/nuxt-site-shell/runtime";
|
|
3
|
+
|
|
2
4
|
const site = useSiteShell();
|
|
3
5
|
const { localeProperties, t } = useI18n();
|
|
4
6
|
const localeHead = useLocaleHead({ seo: true });
|
|
5
7
|
|
|
6
8
|
useHead(() => ({
|
|
7
9
|
htmlAttrs: localeHead.value.htmlAttrs,
|
|
8
|
-
titleTemplate: (title?: string) => (title
|
|
10
|
+
titleTemplate: (title?: string) => formatTitle(title, site.value.name),
|
|
9
11
|
meta: [
|
|
10
12
|
...(localeHead.value.meta || []),
|
|
11
13
|
{ name: "description", content: t(site.value.descriptionKey) },
|
|
@@ -40,6 +42,6 @@ useHead(() => ({
|
|
|
40
42
|
<NuxtPage />
|
|
41
43
|
</NuxtLayout>
|
|
42
44
|
</div>
|
|
43
|
-
<
|
|
45
|
+
<FeedbackToast />
|
|
44
46
|
</UApp>
|
|
45
47
|
</template>
|
|
@@ -268,7 +268,7 @@ summary {
|
|
|
268
268
|
white-space: nowrap;
|
|
269
269
|
}
|
|
270
270
|
|
|
271
|
-
.
|
|
271
|
+
.site-shell {
|
|
272
272
|
min-height: 100dvh;
|
|
273
273
|
display: flex;
|
|
274
274
|
flex-direction: column;
|
|
@@ -277,12 +277,12 @@ summary {
|
|
|
277
277
|
linear-gradient(180deg, rgb(168 101 80 / 2.5%), transparent 520px), var(--site-color-bg);
|
|
278
278
|
}
|
|
279
279
|
|
|
280
|
-
.
|
|
280
|
+
.site-shell__main {
|
|
281
281
|
width: 100%;
|
|
282
282
|
flex: 1;
|
|
283
283
|
}
|
|
284
284
|
|
|
285
|
-
.
|
|
285
|
+
.site-header {
|
|
286
286
|
position: sticky;
|
|
287
287
|
top: 0;
|
|
288
288
|
z-index: 40;
|
|
@@ -291,7 +291,7 @@ summary {
|
|
|
291
291
|
backdrop-filter: blur(18px);
|
|
292
292
|
}
|
|
293
293
|
|
|
294
|
-
.
|
|
294
|
+
.site-header__inner {
|
|
295
295
|
display: flex;
|
|
296
296
|
width: min(1180px, calc(100% - 32px));
|
|
297
297
|
height: var(--site-shell-header-height);
|
|
@@ -301,7 +301,7 @@ summary {
|
|
|
301
301
|
gap: 24px;
|
|
302
302
|
}
|
|
303
303
|
|
|
304
|
-
.
|
|
304
|
+
.site-header__nav {
|
|
305
305
|
display: flex;
|
|
306
306
|
min-width: 0;
|
|
307
307
|
flex: 1;
|
|
@@ -310,8 +310,8 @@ summary {
|
|
|
310
310
|
gap: 6px;
|
|
311
311
|
}
|
|
312
312
|
|
|
313
|
-
.
|
|
314
|
-
.
|
|
313
|
+
.site-nav-link,
|
|
314
|
+
.site-nav-menu > summary {
|
|
315
315
|
display: inline-flex;
|
|
316
316
|
min-height: 40px;
|
|
317
317
|
align-items: center;
|
|
@@ -324,26 +324,26 @@ summary {
|
|
|
324
324
|
cursor: pointer;
|
|
325
325
|
}
|
|
326
326
|
|
|
327
|
-
.
|
|
328
|
-
.
|
|
327
|
+
.site-nav-link:hover,
|
|
328
|
+
.site-nav-menu > summary:hover {
|
|
329
329
|
border-color: rgb(168 101 80 / 18%);
|
|
330
330
|
background: rgb(168 101 80 / 7%);
|
|
331
331
|
color: var(--site-color-text);
|
|
332
332
|
}
|
|
333
333
|
|
|
334
|
-
.
|
|
334
|
+
.site-nav-menu {
|
|
335
335
|
position: relative;
|
|
336
336
|
}
|
|
337
337
|
|
|
338
|
-
.
|
|
338
|
+
.site-nav-menu > summary {
|
|
339
339
|
list-style: none;
|
|
340
340
|
}
|
|
341
341
|
|
|
342
|
-
.
|
|
342
|
+
.site-nav-menu > summary::-webkit-details-marker {
|
|
343
343
|
display: none;
|
|
344
344
|
}
|
|
345
345
|
|
|
346
|
-
.
|
|
346
|
+
.site-nav-menu__panel {
|
|
347
347
|
position: absolute;
|
|
348
348
|
top: calc(100% + 10px);
|
|
349
349
|
left: 50%;
|
|
@@ -358,33 +358,33 @@ summary {
|
|
|
358
358
|
transform: translateX(-50%);
|
|
359
359
|
}
|
|
360
360
|
|
|
361
|
-
.
|
|
361
|
+
.site-nav-menu__item {
|
|
362
362
|
display: grid;
|
|
363
363
|
gap: 4px;
|
|
364
364
|
border-radius: var(--site-radius-md);
|
|
365
365
|
padding: 12px;
|
|
366
366
|
}
|
|
367
367
|
|
|
368
|
-
.
|
|
368
|
+
.site-nav-menu__item:hover {
|
|
369
369
|
background: rgb(168 101 80 / 7%);
|
|
370
370
|
}
|
|
371
371
|
|
|
372
|
-
.
|
|
372
|
+
.site-nav-menu__item small {
|
|
373
373
|
color: var(--site-color-muted);
|
|
374
374
|
line-height: 1.45;
|
|
375
375
|
}
|
|
376
376
|
|
|
377
|
-
.
|
|
377
|
+
.site-header__actions {
|
|
378
378
|
display: flex;
|
|
379
379
|
align-items: center;
|
|
380
380
|
gap: 10px;
|
|
381
381
|
}
|
|
382
382
|
|
|
383
|
-
.
|
|
383
|
+
.site-header__actions > .base-button--secondary {
|
|
384
384
|
background: color-mix(in srgb, var(--site-color-surface) 78%, transparent);
|
|
385
385
|
}
|
|
386
386
|
|
|
387
|
-
.
|
|
387
|
+
.site-header__preferences,
|
|
388
388
|
.mobile-nav__preferences {
|
|
389
389
|
display: flex;
|
|
390
390
|
align-items: center;
|
|
@@ -468,7 +468,7 @@ summary {
|
|
|
468
468
|
margin-inline-start: auto;
|
|
469
469
|
}
|
|
470
470
|
|
|
471
|
-
.
|
|
471
|
+
.site-header__menu-button {
|
|
472
472
|
display: none;
|
|
473
473
|
}
|
|
474
474
|
|
|
@@ -476,13 +476,13 @@ summary {
|
|
|
476
476
|
display: none;
|
|
477
477
|
}
|
|
478
478
|
|
|
479
|
-
.
|
|
479
|
+
.site-footer {
|
|
480
480
|
border-top: 1px solid var(--site-color-border);
|
|
481
481
|
background: linear-gradient(180deg, var(--site-color-surface), var(--site-color-bg-soft));
|
|
482
482
|
color: var(--site-color-text);
|
|
483
483
|
}
|
|
484
484
|
|
|
485
|
-
.
|
|
485
|
+
.site-footer__inner {
|
|
486
486
|
display: grid;
|
|
487
487
|
width: min(1180px, calc(100% - 32px));
|
|
488
488
|
margin: 0 auto;
|
|
@@ -491,39 +491,39 @@ summary {
|
|
|
491
491
|
padding: 56px 0;
|
|
492
492
|
}
|
|
493
493
|
|
|
494
|
-
.
|
|
495
|
-
.
|
|
494
|
+
.site-footer .site-brand__text,
|
|
495
|
+
.site-footer .site-brand {
|
|
496
496
|
color: var(--site-color-text);
|
|
497
497
|
}
|
|
498
498
|
|
|
499
|
-
.
|
|
499
|
+
.site-footer__brand {
|
|
500
500
|
display: grid;
|
|
501
501
|
align-content: start;
|
|
502
502
|
gap: 18px;
|
|
503
503
|
}
|
|
504
504
|
|
|
505
|
-
.
|
|
505
|
+
.site-footer__brand p {
|
|
506
506
|
margin: 0;
|
|
507
507
|
max-width: 360px;
|
|
508
508
|
color: var(--site-color-muted);
|
|
509
509
|
line-height: 1.65;
|
|
510
510
|
}
|
|
511
511
|
|
|
512
|
-
.
|
|
513
|
-
.
|
|
512
|
+
.site-footer__social,
|
|
513
|
+
.site-footer__legal nav {
|
|
514
514
|
display: flex;
|
|
515
515
|
flex-wrap: wrap;
|
|
516
516
|
gap: 12px;
|
|
517
517
|
}
|
|
518
518
|
|
|
519
|
-
.
|
|
520
|
-
.
|
|
519
|
+
.site-footer__social a,
|
|
520
|
+
.site-footer__legal a,
|
|
521
521
|
.consent-settings-button {
|
|
522
522
|
color: var(--site-color-muted);
|
|
523
523
|
}
|
|
524
524
|
|
|
525
|
-
.
|
|
526
|
-
.
|
|
525
|
+
.site-footer__social a:hover,
|
|
526
|
+
.site-footer__legal a:hover,
|
|
527
527
|
.consent-settings-button:hover {
|
|
528
528
|
color: var(--site-color-accent);
|
|
529
529
|
}
|
|
@@ -536,20 +536,20 @@ summary {
|
|
|
536
536
|
cursor: pointer;
|
|
537
537
|
}
|
|
538
538
|
|
|
539
|
-
.
|
|
539
|
+
.site-footer__nav {
|
|
540
540
|
display: grid;
|
|
541
541
|
grid-template-columns: repeat(3, minmax(0, 1fr));
|
|
542
542
|
gap: 24px;
|
|
543
543
|
}
|
|
544
544
|
|
|
545
|
-
.
|
|
545
|
+
.site-footer__group h2 {
|
|
546
546
|
margin: 0 0 14px;
|
|
547
547
|
color: var(--site-color-accent-warm);
|
|
548
548
|
font-size: 13px;
|
|
549
549
|
font-weight: 650;
|
|
550
550
|
}
|
|
551
551
|
|
|
552
|
-
.
|
|
552
|
+
.site-footer__group ul {
|
|
553
553
|
display: grid;
|
|
554
554
|
gap: 10px;
|
|
555
555
|
margin: 0;
|
|
@@ -557,16 +557,16 @@ summary {
|
|
|
557
557
|
list-style: none;
|
|
558
558
|
}
|
|
559
559
|
|
|
560
|
-
.
|
|
560
|
+
.site-footer__group a {
|
|
561
561
|
color: var(--site-color-muted);
|
|
562
562
|
font-size: 14px;
|
|
563
563
|
}
|
|
564
564
|
|
|
565
|
-
.
|
|
565
|
+
.site-footer__group a:hover {
|
|
566
566
|
color: var(--site-color-text);
|
|
567
567
|
}
|
|
568
568
|
|
|
569
|
-
.
|
|
569
|
+
.site-footer__legal {
|
|
570
570
|
display: flex;
|
|
571
571
|
width: min(1180px, calc(100% - 32px));
|
|
572
572
|
margin: 0 auto;
|
|
@@ -1065,30 +1065,30 @@ summary {
|
|
|
1065
1065
|
--site-shell-header-height: 64px;
|
|
1066
1066
|
}
|
|
1067
1067
|
|
|
1068
|
-
.
|
|
1068
|
+
.site-header__inner {
|
|
1069
1069
|
width: min(100% - 24px, 1180px);
|
|
1070
1070
|
gap: 12px;
|
|
1071
1071
|
}
|
|
1072
1072
|
|
|
1073
|
-
.
|
|
1073
|
+
.site-header .site-brand {
|
|
1074
1074
|
flex: 1 1 auto;
|
|
1075
1075
|
}
|
|
1076
1076
|
|
|
1077
|
-
.
|
|
1077
|
+
.site-header .site-brand__text {
|
|
1078
1078
|
min-width: 0;
|
|
1079
1079
|
}
|
|
1080
1080
|
|
|
1081
|
-
.
|
|
1082
|
-
.
|
|
1083
|
-
.
|
|
1081
|
+
.site-header__nav,
|
|
1082
|
+
.site-header__preferences,
|
|
1083
|
+
.site-header__actions > .base-button {
|
|
1084
1084
|
display: none;
|
|
1085
1085
|
}
|
|
1086
1086
|
|
|
1087
|
-
.
|
|
1087
|
+
.site-header__actions {
|
|
1088
1088
|
flex: 0 0 auto;
|
|
1089
1089
|
}
|
|
1090
1090
|
|
|
1091
|
-
.
|
|
1091
|
+
.site-header__menu-button {
|
|
1092
1092
|
display: inline-flex;
|
|
1093
1093
|
}
|
|
1094
1094
|
|
|
@@ -1144,18 +1144,18 @@ summary {
|
|
|
1144
1144
|
width: 100%;
|
|
1145
1145
|
}
|
|
1146
1146
|
|
|
1147
|
-
.
|
|
1148
|
-
.
|
|
1147
|
+
.site-footer__inner,
|
|
1148
|
+
.site-footer__nav,
|
|
1149
1149
|
.content-list__grid {
|
|
1150
1150
|
grid-template-columns: 1fr;
|
|
1151
1151
|
}
|
|
1152
1152
|
|
|
1153
|
-
.
|
|
1153
|
+
.site-footer__inner {
|
|
1154
1154
|
gap: 34px;
|
|
1155
1155
|
padding: 44px 0;
|
|
1156
1156
|
}
|
|
1157
1157
|
|
|
1158
|
-
.
|
|
1158
|
+
.site-footer__legal {
|
|
1159
1159
|
align-items: flex-start;
|
|
1160
1160
|
flex-direction: column;
|
|
1161
1161
|
}
|
|
@@ -27,10 +27,10 @@ defineEmits<{
|
|
|
27
27
|
|
|
28
28
|
<div class="application-header__right">
|
|
29
29
|
<slot name="actions">
|
|
30
|
-
<
|
|
31
|
-
<
|
|
30
|
+
<ThemeMenu />
|
|
31
|
+
<LocaleMenu />
|
|
32
32
|
<NuxtLinkLocale :to="site.homePath" class="base-button base-button--secondary">
|
|
33
|
-
{{ t("site.
|
|
33
|
+
{{ t("site.openSite") }}
|
|
34
34
|
</NuxtLinkLocale>
|
|
35
35
|
</slot>
|
|
36
36
|
</div>
|
|
@@ -29,7 +29,7 @@ watch(
|
|
|
29
29
|
<template>
|
|
30
30
|
<div class="application-shell">
|
|
31
31
|
<slot name="header">
|
|
32
|
-
<
|
|
32
|
+
<AppHeader @toggle-sidebar="sidebarOpen = !sidebarOpen" />
|
|
33
33
|
</slot>
|
|
34
34
|
|
|
35
35
|
<div class="application-shell__body">
|
|
@@ -44,7 +44,7 @@ watch(
|
|
|
44
44
|
:class="{ 'application-shell__sidebar--open': sidebarOpen }"
|
|
45
45
|
>
|
|
46
46
|
<slot name="sidebar">
|
|
47
|
-
<
|
|
47
|
+
<AppSidebar @navigate="closeSidebar" />
|
|
48
48
|
</slot>
|
|
49
49
|
</div>
|
|
50
50
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
const {
|
|
2
|
+
const { appGroups } = useNavigation();
|
|
3
3
|
const { t } = useI18n();
|
|
4
4
|
|
|
5
5
|
defineEmits<{
|
|
@@ -11,7 +11,7 @@ defineEmits<{
|
|
|
11
11
|
<aside class="application-sidebar" :aria-label="t('navigation.application')">
|
|
12
12
|
<nav class="application-sidebar__nav">
|
|
13
13
|
<section
|
|
14
|
-
v-for="group in
|
|
14
|
+
v-for="group in appGroups"
|
|
15
15
|
:key="group.label || group.items[0]?.to"
|
|
16
16
|
class="application-sidebar__group"
|
|
17
17
|
>
|
|
@@ -27,7 +27,12 @@ const visible = computed(() => props.showIdle || resolvedStatus.value !== "idle"
|
|
|
27
27
|
const statusMeta = computed(() => {
|
|
28
28
|
const map: Record<
|
|
29
29
|
CheckoutStatusValue,
|
|
30
|
-
{
|
|
30
|
+
{
|
|
31
|
+
icon: string;
|
|
32
|
+
tone: "neutral" | "info" | "success" | "warning" | "danger";
|
|
33
|
+
title: string;
|
|
34
|
+
description: string;
|
|
35
|
+
}
|
|
31
36
|
> = {
|
|
32
37
|
idle: {
|
|
33
38
|
icon: "i-lucide-circle",
|
|
@@ -69,8 +74,8 @@ const statusMeta = computed(() => {
|
|
|
69
74
|
<section v-if="visible" class="checkout-status" :data-tone="statusMeta.tone" role="status">
|
|
70
75
|
<UIcon :name="statusMeta.icon" class="checkout-status__icon" />
|
|
71
76
|
<div>
|
|
72
|
-
<
|
|
73
|
-
<
|
|
77
|
+
<UiText as="h3" size="lg" weight="bold">{{ statusMeta.title }}</UiText>
|
|
78
|
+
<UiText as="p" tone="muted">{{ statusMeta.description }}</UiText>
|
|
74
79
|
</div>
|
|
75
80
|
</section>
|
|
76
81
|
</template>
|
|
@@ -87,33 +87,33 @@ function openCheckout(): void {
|
|
|
87
87
|
<template>
|
|
88
88
|
<section class="checkout-summary" aria-label="Checkout summary">
|
|
89
89
|
<div class="checkout-summary__header">
|
|
90
|
-
<
|
|
91
|
-
<
|
|
92
|
-
>{{ lineItems.length }} item{{ lineItems.length === 1 ? "" : "s" }}</
|
|
90
|
+
<UiText as="h3" size="lg" weight="bold">Checkout summary</UiText>
|
|
91
|
+
<UiText tone="muted" size="sm"
|
|
92
|
+
>{{ lineItems.length }} item{{ lineItems.length === 1 ? "" : "s" }}</UiText
|
|
93
93
|
>
|
|
94
94
|
</div>
|
|
95
95
|
|
|
96
96
|
<ul class="checkout-summary__items">
|
|
97
97
|
<li v-for="item in lineItems" :key="item.id" class="checkout-summary__item">
|
|
98
98
|
<span>
|
|
99
|
-
<
|
|
100
|
-
<
|
|
99
|
+
<UiText weight="semibold">{{ item.label }}</UiText>
|
|
100
|
+
<UiText v-if="item.description" as="span" tone="muted" size="sm">
|
|
101
101
|
{{ item.description }}
|
|
102
|
-
</
|
|
102
|
+
</UiText>
|
|
103
103
|
</span>
|
|
104
104
|
<span class="checkout-summary__item-meta">
|
|
105
|
-
<
|
|
106
|
-
<
|
|
105
|
+
<UiText as="span" tone="muted" size="sm">x{{ item.quantity }}</UiText>
|
|
106
|
+
<UiText as="strong" weight="bold">{{ formatLineItem(item) }}</UiText>
|
|
107
107
|
</span>
|
|
108
108
|
</li>
|
|
109
109
|
</ul>
|
|
110
110
|
|
|
111
111
|
<div class="checkout-summary__total">
|
|
112
|
-
<
|
|
113
|
-
<
|
|
112
|
+
<UiText as="span" tone="muted">Total</UiText>
|
|
113
|
+
<UiText as="strong" size="xl" weight="bold">{{ formattedTotal }}</UiText>
|
|
114
114
|
</div>
|
|
115
115
|
|
|
116
|
-
<
|
|
116
|
+
<UiButton
|
|
117
117
|
:label="actionLabel"
|
|
118
118
|
trailing-icon="i-lucide-arrow-right"
|
|
119
119
|
:loading="loading"
|
|
@@ -5,7 +5,7 @@ const site = useSiteShell();
|
|
|
5
5
|
const consent = useConsent();
|
|
6
6
|
const { t } = useI18n();
|
|
7
7
|
|
|
8
|
-
const
|
|
8
|
+
const storageMessage = computed(() => {
|
|
9
9
|
if (consent.state.value.error === "read-failed") {
|
|
10
10
|
return t("consent.storageUnavailable");
|
|
11
11
|
}
|
|
@@ -47,8 +47,8 @@ const persistenceMessage = computed(() => {
|
|
|
47
47
|
<NuxtLink class="site-consent__policy" :to="site.consent.policyPath">
|
|
48
48
|
{{ t("consent.policy") }}
|
|
49
49
|
</NuxtLink>
|
|
50
|
-
<p v-if="
|
|
51
|
-
{{
|
|
50
|
+
<p v-if="storageMessage" class="site-consent__status" role="status">
|
|
51
|
+
{{ storageMessage }}
|
|
52
52
|
</p>
|
|
53
53
|
<div class="site-consent__actions">
|
|
54
54
|
<button type="button" class="base-button base-button--secondary" @click="consent.reject">
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import type {
|
|
2
|
+
import type { Article, ListItem } from "~/content/types";
|
|
3
3
|
|
|
4
4
|
defineProps<{
|
|
5
|
-
article:
|
|
6
|
-
related?:
|
|
5
|
+
article: Article;
|
|
6
|
+
related?: ListItem[];
|
|
7
7
|
}>();
|
|
8
8
|
const { t } = useI18n();
|
|
9
9
|
</script>
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import type {
|
|
2
|
+
import type { ListItem } from "~/content/types";
|
|
3
3
|
|
|
4
4
|
defineProps<{
|
|
5
5
|
title: string;
|
|
6
6
|
description?: string;
|
|
7
|
-
items:
|
|
7
|
+
items: ListItem[];
|
|
8
8
|
}>();
|
|
9
9
|
</script>
|
|
10
10
|
|
|
@@ -95,7 +95,7 @@ watch(
|
|
|
95
95
|
<template>
|
|
96
96
|
<div class="google-sign-in">
|
|
97
97
|
<div v-if="identity.enabled" ref="buttonRef" class="google-sign-in__button" />
|
|
98
|
-
<
|
|
98
|
+
<UiButton
|
|
99
99
|
v-else
|
|
100
100
|
color="neutral"
|
|
101
101
|
variant="outline"
|
|
@@ -103,8 +103,8 @@ watch(
|
|
|
103
103
|
:label="disabledLabel"
|
|
104
104
|
disabled
|
|
105
105
|
/>
|
|
106
|
-
<
|
|
107
|
-
<
|
|
106
|
+
<UiText v-if="loading" as="p" size="sm" tone="muted">Completing sign-in...</UiText>
|
|
107
|
+
<UiText v-if="errorMessage" as="p" size="sm" tone="danger">{{ errorMessage }}</UiText>
|
|
108
108
|
</div>
|
|
109
109
|
</template>
|
|
110
110
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
const emit = defineEmits<{
|
|
2
|
+
const emit = defineEmits<{ select: [code: typeof locale.value] }>();
|
|
3
3
|
const { locale, locales, setLocale, t } = useI18n();
|
|
4
4
|
const dropdown = ref<HTMLDetailsElement | null>(null);
|
|
5
5
|
|
|
@@ -16,7 +16,7 @@ const current = computed(
|
|
|
16
16
|
async function selectLocale(code: typeof locale.value): Promise<void> {
|
|
17
17
|
dropdown.value?.removeAttribute("open");
|
|
18
18
|
if (code !== locale.value) await setLocale(code);
|
|
19
|
-
emit("
|
|
19
|
+
emit("select", code);
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
function handleKeydown(event: KeyboardEvent): void {
|
|
@@ -6,12 +6,12 @@ const year = new Date().getFullYear();
|
|
|
6
6
|
</script>
|
|
7
7
|
|
|
8
8
|
<template>
|
|
9
|
-
<footer class="
|
|
10
|
-
<div class="
|
|
11
|
-
<section class="
|
|
9
|
+
<footer class="site-footer">
|
|
10
|
+
<div class="site-footer__inner">
|
|
11
|
+
<section class="site-footer__brand" :aria-label="t('site.region')">
|
|
12
12
|
<SiteBrand />
|
|
13
13
|
<p>{{ t(site.descriptionKey) }}</p>
|
|
14
|
-
<div v-if="socialLinks.length" class="
|
|
14
|
+
<div v-if="socialLinks.length" class="site-footer__social">
|
|
15
15
|
<a
|
|
16
16
|
v-for="link in socialLinks"
|
|
17
17
|
:key="link.label"
|
|
@@ -24,8 +24,8 @@ const year = new Date().getFullYear();
|
|
|
24
24
|
</div>
|
|
25
25
|
</section>
|
|
26
26
|
|
|
27
|
-
<nav class="
|
|
28
|
-
<section v-for="group in footerGroups" :key="group.title" class="
|
|
27
|
+
<nav class="site-footer__nav" :aria-label="t('footer.navigation')">
|
|
28
|
+
<section v-for="group in footerGroups" :key="group.title" class="site-footer__group">
|
|
29
29
|
<h2>{{ group.title }}</h2>
|
|
30
30
|
<ul>
|
|
31
31
|
<li v-for="link in group.links" :key="link.label">
|
|
@@ -41,13 +41,13 @@ const year = new Date().getFullYear();
|
|
|
41
41
|
</nav>
|
|
42
42
|
</div>
|
|
43
43
|
|
|
44
|
-
<div class="
|
|
44
|
+
<div class="site-footer__legal">
|
|
45
45
|
<span>{{ t("footer.copyright", { year, site: site.name }) }}</span>
|
|
46
46
|
<nav :aria-label="t('footer.legal')">
|
|
47
47
|
<NuxtLinkLocale v-for="link in legalLinks" :key="link.label" :to="link.to">
|
|
48
48
|
{{ link.label }}
|
|
49
49
|
</NuxtLinkLocale>
|
|
50
|
-
<
|
|
50
|
+
<ConsentSettings />
|
|
51
51
|
</nav>
|
|
52
52
|
</div>
|
|
53
53
|
</footer>
|