@dargmuesli/nuxt-vio 3.0.0-beta.9 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (32) hide show
  1. package/assets/css/tailwind.css +24 -9
  2. package/components/vio/_/VioApp.vue +4 -1
  3. package/components/vio/_/VioError.vue +4 -1
  4. package/components/vio/_/VioLink.vue +5 -3
  5. package/components/vio/form/VioForm.vue +1 -1
  6. package/components/vio/form/input/VioFormInput.vue +3 -3
  7. package/components/vio/form/input/state/VioFormInputStateError.vue +1 -1
  8. package/components/vio/form/input/state/VioFormInputStateInfo.vue +1 -1
  9. package/components/vio/icon/IconArrowRight.vue +2 -2
  10. package/components/vio/icon/IconCalendar.vue +2 -2
  11. package/components/vio/icon/IconCheckCircle.vue +2 -2
  12. package/components/vio/icon/IconDownload.vue +2 -2
  13. package/components/vio/icon/IconExclamationCircle.vue +2 -2
  14. package/components/vio/icon/IconHome.vue +2 -2
  15. package/components/vio/icon/IconHourglass.vue +2 -2
  16. package/components/vio/layout/VioLayoutBreadcrumbs.vue +1 -1
  17. package/components/vio/layout/VioLayoutFooter.vue +3 -3
  18. package/components/vio/layout/VioLayoutFooterCategory.vue +1 -1
  19. package/components/vio/loader/indicator/VioLoaderIndicator.vue +1 -1
  20. package/components/vio/loader/indicator/VioLoaderIndicatorPing.vue +1 -1
  21. package/components/vio/page/VioPageLegalNotice.vue +2 -2
  22. package/components/vio/page/VioPagePrivacyPolicy.vue +2 -2
  23. package/composables/useGetServiceHref.ts +1 -1
  24. package/composables/useHeadDefault.ts +1 -1
  25. package/composables/usePolyfills.ts +23 -0
  26. package/nuxt.config.ts +152 -157
  27. package/package.json +18 -17
  28. package/plugins/gtag.client.ts +1 -1
  29. package/utils/constants.ts +50 -0
  30. package/utils/routing.ts +4 -2
  31. package/components/vio/icon/IconLogo.vue +0 -17
  32. package/components/vio/layout/VioLayoutHeader.vue +0 -98
@@ -1,43 +1,58 @@
1
+ /* stylelint-disable at-rule-no-unknown */
1
2
  @tailwind base;
2
3
  @tailwind components;
3
4
  @tailwind utilities;
5
+ /* stylelint-enable at-rule-no-unknown */
4
6
 
7
+ /* doiuse-disable css-cascade-layers */
5
8
  @layer base {
9
+ /* doiuse-enable css-cascade-layers */
6
10
  a:focus {
7
- @apply outline-none ring;
11
+ /* doiuse-disable css-nesting */
12
+ @apply outline-none ring; /* doiuse-enable css-nesting */
8
13
  }
9
14
 
10
15
  button:focus {
11
- @apply outline-none ring;
16
+ /* doiuse-disable css-nesting */
17
+ @apply outline-none ring; /* doiuse-enable css-nesting */
12
18
  }
13
19
 
14
20
  thead {
15
- @apply sticky top-0 z-10 vio-bg-darken;
21
+ /* doiuse-disable css-nesting */
22
+ @apply vio-bg-darken sticky top-0 z-10; /* doiuse-enable css-nesting */
16
23
  }
17
24
 
18
25
  tbody {
19
- @apply divide-y vio-divide-darken;
26
+ /* doiuse-disable css-nesting */
27
+ @apply vio-divide-darken divide-y; /* doiuse-enable css-nesting */
20
28
  }
21
29
  }
22
30
 
31
+ /* doiuse-disable css-cascade-layers */
23
32
  @layer utilities {
33
+ /* doiuse-enable css-cascade-layers */
24
34
  .vio-prose {
25
- @apply m-auto prose dark:prose-invert prose-sm sm:prose-base lg:prose-lg xl:prose-xl; /* 2xl:prose-2xl */
35
+ /* doiuse-disable css-nesting */
36
+ @apply prose prose-sm dark:prose-invert sm:prose-base lg:prose-lg xl:prose-xl; /* 2xl:prose-2xl */ /* doiuse-enable css-nesting */
26
37
  }
27
38
 
28
39
  .vio-prose-fullwidth {
29
- @apply vio-prose max-w-full sm:max-w-full md:max-w-full xl:max-w-full 2xl:max-w-full;
40
+ /* doiuse-disable css-nesting */
41
+ @apply vio-prose max-w-full sm:max-w-full md:max-w-full xl:max-w-full 2xl:max-w-full; /* doiuse-enable css-nesting */
30
42
  }
31
43
 
32
44
  .vio-divide-darken {
33
- @apply divide-background-brighten dark:divide-background-darken;
45
+ /* doiuse-disable css-nesting */
46
+ @apply divide-background-brighten dark:divide-background-darken; /* doiuse-enable css-nesting */
34
47
  }
35
48
 
36
49
  .vio-bg-darken {
37
- @apply bg-background-brighten dark:bg-background-darken;
50
+ /* doiuse-disable css-nesting */
51
+ @apply bg-background-brighten dark:bg-background-darken; /* doiuse-enable css-nesting */
38
52
  }
39
53
 
40
54
  .vio-border-darken {
41
- @apply border-background-brighten dark:border-background-darken;
55
+ /* doiuse-disable css-nesting */
56
+ @apply border-background-brighten dark:border-background-darken; /* doiuse-enable css-nesting */
42
57
  }
43
58
  }
@@ -38,6 +38,8 @@ const locale = i18n.locale as WritableComputedRef<Locale>
38
38
 
39
39
  // methods
40
40
  const init = () => {
41
+ $dayjs.locale(locale.value)
42
+
41
43
  if (process.client) {
42
44
  const cookieTimezone = useCookie(TIMEZONE_COOKIE_NAME, {
43
45
  // default: () => undefined, // setting `default` on the client side only does not write the cookie
@@ -78,7 +80,8 @@ defineOgImage({
78
80
  description: siteDescriptionProp.value,
79
81
  })
80
82
  useAppLayout()
81
- useFavicons() // TODO: move to head default
83
+ useFavicons()
84
+ usePolyfills()
82
85
  useSchemaOrg([
83
86
  defineWebSite({
84
87
  description: siteDescriptionProp,
@@ -3,7 +3,10 @@
3
3
  <div>
4
4
  {{ description }}
5
5
  </div>
6
- <pre v-if="stack && !runtimeConfig.public.isInProduction" v-html="stack" />
6
+ <pre
7
+ v-if="stack && !runtimeConfig.public.vio.isInProduction"
8
+ v-html="stack"
9
+ />
7
10
  </template>
8
11
 
9
12
  <script setup lang="ts">
@@ -1,9 +1,9 @@
1
1
  <template>
2
2
  <a
3
- v-if="to.match(/^((ftp|http(s)?):\/\/|(mailto):)/)"
3
+ v-if="to?.toString().match(/^((ftp|http(s)?):\/\/|(mailto):)/)"
4
4
  :aria-label="ariaLabel"
5
5
  :class="classes"
6
- :href="to"
6
+ :href="to.toString()"
7
7
  :rel="
8
8
  [...(nofollow ? ['nofollow'] : []), 'noopener', 'noreferrer'].join(' ')
9
9
  "
@@ -24,13 +24,15 @@
24
24
  </template>
25
25
 
26
26
  <script setup lang="ts">
27
+ import { NuxtLinkProps } from '#app'
28
+
27
29
  export interface Props {
28
30
  ariaLabel?: string
29
31
  isColored?: boolean
30
32
  isToRelative?: boolean
31
33
  isUnderlined?: boolean
32
34
  nofollow?: boolean
33
- to: string
35
+ to: NuxtLinkProps['to']
34
36
  }
35
37
  const props = withDefaults(defineProps<Props>(), {
36
38
  ariaLabel: undefined,
@@ -9,7 +9,7 @@
9
9
  @submit="(e) => emit('submit', e)"
10
10
  >
11
11
  <VioCard class="flex flex-col" is-high>
12
- <div class="flex flex-col min-h-0 overflow-y-auto gap-6">
12
+ <div class="flex min-h-0 flex-col gap-6 overflow-y-auto">
13
13
  <slot />
14
14
  <div class="flex flex-col items-center justify-between">
15
15
  <VioButtonColored
@@ -51,7 +51,7 @@
51
51
  />
52
52
  <div v-if="validationProperty && isValidatable">
53
53
  <VioFormInputIconWrapper v-if="validationProperty.$pending">
54
- <IconHourglass
54
+ <VioIconHourglass
55
55
  class="text-blue-600"
56
56
  :title="t('globalStatusLoading')"
57
57
  />
@@ -61,14 +61,14 @@
61
61
  validationProperty.$model && !validationProperty.$invalid
62
62
  "
63
63
  >
64
- <IconCheckCircle class="text-green-600" :title="t('valid')" />
64
+ <VioIconCheckCircle class="text-green-600" :title="t('valid')" />
65
65
  </VioFormInputIconWrapper>
66
66
  <VioFormInputIconWrapper
67
67
  v-else-if="
68
68
  validationProperty.$model && validationProperty.$invalid
69
69
  "
70
70
  >
71
- <IconExclamationCircle
71
+ <VioIconExclamationCircle
72
72
  class="text-red-600"
73
73
  :title="t('validNot')"
74
74
  />
@@ -11,7 +11,7 @@
11
11
  "
12
12
  class="text-red-600"
13
13
  >
14
- <IconExclamationCircle v-if="formInput && validationProperty" />
14
+ <VioIconExclamationCircle v-if="formInput && validationProperty" />
15
15
  <slot />
16
16
  </VioFormInputState>
17
17
  </template>
@@ -11,7 +11,7 @@
11
11
  "
12
12
  class="text-gray-500 dark:text-gray-400"
13
13
  >
14
- <IconInformationCircle />
14
+ <VioIconInformationCircle />
15
15
  <slot />
16
16
  </VioFormInputState>
17
17
  </template>
@@ -1,5 +1,5 @@
1
1
  <template>
2
- <IconContainer
2
+ <VioIconContainer
3
3
  fill="none"
4
4
  viewBox="0 0 24 24"
5
5
  stroke="currentColor"
@@ -11,7 +11,7 @@
11
11
  stroke-linejoin="round"
12
12
  d="M14 5l7 7m0 0l-7 7m7-7H3"
13
13
  />
14
- </IconContainer>
14
+ </VioIconContainer>
15
15
  </template>
16
16
 
17
17
  <script setup lang="ts">
@@ -1,5 +1,5 @@
1
1
  <template>
2
- <IconContainer
2
+ <VioIconContainer
3
3
  fill="none"
4
4
  viewBox="0 0 24 24"
5
5
  stroke="currentColor"
@@ -11,7 +11,7 @@
11
11
  stroke-width="2"
12
12
  d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"
13
13
  />
14
- </IconContainer>
14
+ </VioIconContainer>
15
15
  </template>
16
16
 
17
17
  <script setup lang="ts">
@@ -1,5 +1,5 @@
1
1
  <template>
2
- <IconContainer
2
+ <VioIconContainer
3
3
  viewBox="0 0 20 20"
4
4
  fill="currentColor"
5
5
  :title="title || t('title')"
@@ -9,7 +9,7 @@
9
9
  d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z"
10
10
  clip-rule="evenodd"
11
11
  />
12
- </IconContainer>
12
+ </VioIconContainer>
13
13
  </template>
14
14
 
15
15
  <script setup lang="ts">
@@ -1,5 +1,5 @@
1
1
  <template>
2
- <IconContainer
2
+ <VioIconContainer
3
3
  fill="none"
4
4
  viewBox="0 0 24 24"
5
5
  stroke="currentColor"
@@ -11,7 +11,7 @@
11
11
  stroke-width="2"
12
12
  d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4"
13
13
  />
14
- </IconContainer>
14
+ </VioIconContainer>
15
15
  </template>
16
16
 
17
17
  <script setup lang="ts">
@@ -1,5 +1,5 @@
1
1
  <template>
2
- <IconContainer
2
+ <VioIconContainer
3
3
  viewBox="0 0 20 20"
4
4
  fill="currentColor"
5
5
  :title="title || t('title')"
@@ -9,7 +9,7 @@
9
9
  d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7 4a1 1 0 11-2 0 1 1 0 012 0zm-1-9a1 1 0 00-1 1v4a1 1 0 102 0V6a1 1 0 00-1-1z"
10
10
  clip-rule="evenodd"
11
11
  />
12
- </IconContainer>
12
+ </VioIconContainer>
13
13
  </template>
14
14
 
15
15
  <script setup lang="ts">
@@ -1,5 +1,5 @@
1
1
  <template>
2
- <IconContainer
2
+ <VioIconContainer
3
3
  fill="none"
4
4
  viewBox="0 0 24 24"
5
5
  stroke="currentColor"
@@ -11,7 +11,7 @@
11
11
  stroke-width="2"
12
12
  d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6"
13
13
  />
14
- </IconContainer>
14
+ </VioIconContainer>
15
15
  </template>
16
16
 
17
17
  <script setup lang="ts">
@@ -1,5 +1,5 @@
1
1
  <template>
2
- <IconContainer
2
+ <VioIconContainer
3
3
  aria-hidden="true"
4
4
  focusable="false"
5
5
  data-prefix="fas"
@@ -12,7 +12,7 @@
12
12
  fill="currentColor"
13
13
  d="M352 0C369.7 0 384 14.33 384 32C384 49.67 369.7 64 352 64V74.98C352 117.4 335.1 158.1 305.1 188.1L237.3 256L305.1 323.9C335.1 353.9 352 394.6 352 437V448C369.7 448 384 462.3 384 480C384 497.7 369.7 512 352 512H32C14.33 512 0 497.7 0 480C0 462.3 14.33 448 32 448V437C32 394.6 48.86 353.9 78.86 323.9L146.7 256L78.86 188.1C48.86 158.1 32 117.4 32 74.98V64C14.33 64 0 49.67 0 32C0 14.33 14.33 0 32 0H352zM111.1 128H272C282.4 112.4 288 93.98 288 74.98V64H96V74.98C96 93.98 101.6 112.4 111.1 128zM111.1 384H272C268.5 378.7 264.5 373.7 259.9 369.1L192 301.3L124.1 369.1C119.5 373.7 115.5 378.7 111.1 384V384z"
14
14
  />
15
- </IconContainer>
15
+ </VioIconContainer>
16
16
  </template>
17
17
 
18
18
  <script setup lang="ts">
@@ -6,7 +6,7 @@
6
6
  :is-colored="false"
7
7
  :to="localePath('/')"
8
8
  >
9
- <IconHome classes="h-6 w-6" />
9
+ <VioIconHome classes="h-6 w-6" />
10
10
  </VioLink>
11
11
  <ul v-if="prefixes" class="flex items-center gap-2">
12
12
  <li
@@ -3,7 +3,7 @@
3
3
  <div class="flex flex-col gap-8">
4
4
  <div class="flex items-center">
5
5
  <VioLayoutHr />
6
- <IconLogo class="mx-12 h-12 w-12 brightness-0 opacity-60 invert" />
6
+ <slot name="logo" />
7
7
  <VioLayoutHr />
8
8
  </div>
9
9
  <!-- Justifying evenly, instead of "between", centers a single element. -->
@@ -24,11 +24,11 @@
24
24
 
25
25
  <script setup lang="ts">
26
26
  const { t } = useI18n()
27
- const config = useRuntimeConfig()
27
+ const runtimeConfig = useRuntimeConfig()
28
28
 
29
29
  // computations
30
30
  const year = computed(() =>
31
- config.public.isTesting ? 1337 : new Date().getFullYear(),
31
+ runtimeConfig.public.vio.isTesting ? 1337 : new Date().getFullYear(),
32
32
  )
33
33
  </script>
34
34
 
@@ -1,6 +1,6 @@
1
1
  <template>
2
2
  <div
3
- class="flex flex-1 flex-col items-start p-4 basis-1/2 md:flex-none md:basis-auto gap-2"
3
+ class="flex flex-1 basis-1/2 flex-col items-start gap-2 p-4 md:flex-none md:basis-auto"
4
4
  >
5
5
  <span class="whitespace-nowrap text-lg font-medium leading-7">
6
6
  {{ heading }}
@@ -3,7 +3,7 @@
3
3
  class="flex h-full items-center justify-center"
4
4
  :title="t('globalStatusLoading')"
5
5
  >
6
- <div class="flex items-center justify-center w-1/2">
6
+ <div class="flex w-1/2 items-center justify-center">
7
7
  <slot />
8
8
  </div>
9
9
  </div>
@@ -1,7 +1,7 @@
1
1
  <template>
2
2
  <VioLoaderIndicator>
3
3
  <div
4
- class="w-1/2 aspect-square animate-ping rounded-full bg-gray-500"
4
+ class="aspect-square w-1/2 animate-ping rounded-full bg-gray-500"
5
5
  :title="t('globalStatusLoading')"
6
6
  />
7
7
  </VioLoaderIndicator>
@@ -1,5 +1,5 @@
1
1
  <template>
2
- <div class="vio-prose">
2
+ <div class="vio-prose m-auto">
3
3
  <h1>{{ title }}</h1>
4
4
  <h2>{{ t('tmg') }}</h2>
5
5
  <address>
@@ -75,7 +75,7 @@ const { t } = useI18n()
75
75
  const title = t('title')
76
76
 
77
77
  // initialization
78
- useServerSeoMeta({ title })
78
+ useHeadDefault({ title })
79
79
  </script>
80
80
 
81
81
  <i18n lang="yaml">
@@ -1,5 +1,5 @@
1
1
  <template>
2
- <div class="vio-prose">
2
+ <div class="vio-prose m-auto">
3
3
  <h1>{{ title }}</h1>
4
4
  <ol v-if="isEnabled">
5
5
  <li v-if="isEnabled.summary">
@@ -509,7 +509,7 @@ const { t } = useI18n()
509
509
  const title = t('title')
510
510
 
511
511
  // initialization
512
- useServerSeoMeta({ title })
512
+ useHeadDefault({ title })
513
513
  </script>
514
514
 
515
515
  <i18n lang="yaml">
@@ -16,6 +16,6 @@ export const useGetServiceHref = () => {
16
16
  isSsr,
17
17
  name,
18
18
  port,
19
- stagingHost: runtimeConfig.public.stagingHost,
19
+ stagingHost: runtimeConfig.public.vio.stagingHost,
20
20
  })
21
21
  }
@@ -17,5 +17,5 @@ export const useHeadDefault = ({
17
17
  twitterTitle: title,
18
18
  }
19
19
 
20
- return useSeoMeta(defu(extension, defaults)) // TODO: use `useServerSeoMeta`
20
+ useSeoMeta(defu(extension, defaults)) // TODO: use `useServerSeoMeta` when Nuxt 3.7 is used
21
21
  }
@@ -0,0 +1,23 @@
1
+ export const usePolyfills = () => {
2
+ const polyfills =
3
+ 'https://polyfill.io/v3/polyfill.min.js?features=Promise&flags=gated'
4
+
5
+ useServerHead({
6
+ link: [
7
+ {
8
+ rel: 'preload',
9
+ href: polyfills,
10
+ crossorigin: 'anonymous',
11
+ as: 'script',
12
+ 'data-testid': 'polyfill-preload',
13
+ },
14
+ ],
15
+ script: [
16
+ {
17
+ src: polyfills,
18
+ crossorigin: 'anonymous',
19
+ 'data-testid': 'polyfill-script',
20
+ },
21
+ ],
22
+ })
23
+ }
package/nuxt.config.ts CHANGED
@@ -1,177 +1,172 @@
1
1
  import { dirname, join } from 'node:path'
2
2
  import { fileURLToPath } from 'node:url'
3
3
 
4
+ import { defu } from 'defu'
5
+
4
6
  import {
7
+ BASE_URL,
5
8
  I18N_COOKIE_NAME,
6
- I18N_MODULE_CONFIG,
7
- TIMEZONE_COOKIE_NAME,
8
9
  SITE_NAME,
10
+ TIMEZONE_COOKIE_NAME,
11
+ VIO_NUXT_BASE_CONFIG,
9
12
  } from './utils/constants'
10
13
 
11
14
  const currentDir = dirname(fileURLToPath(import.meta.url))
12
15
 
13
- const BASE_URL =
14
- (process.env.NUXT_PUBLIC_STACK_DOMAIN ? 'https' : 'http') +
15
- '://' +
16
- (process.env.NUXT_PUBLIC_STACK_DOMAIN ||
17
- `${process.env.HOST || 'localhost'}:${
18
- !process.env.NODE_ENV || process.env.NODE_ENV === 'development'
19
- ? '3000'
20
- : '3001'
21
- }`)
22
-
23
16
  // https://v3.nuxtjs.org/api/configuration/nuxt.config
24
- export default defineNuxtConfig({
25
- alias: {
26
- dayjs: 'dayjs',
27
- sweetalert2: 'sweetalert2',
28
- }, // TODO: remove (https://github.com/nuxt/nuxt/issues/19426)
29
- app: {
30
- head: {
31
- htmlAttrs: {
32
- lang: 'en', // fallback data to prevent invalid html at generation
33
- },
34
- title: SITE_NAME, // fallback data to prevent invalid html at generation
35
- titleTemplate: '%s', // fully set in `composables/useAppLayout.ts`
36
- },
37
- pageTransition: {
38
- name: 'layout',
39
- },
40
- },
41
- devtools: {
42
- enabled: process.env.NODE_ENV !== 'production',
43
- timeline: {
44
- enabled: true,
45
- },
46
- },
47
- modules: [
48
- '@dargmuesli/nuxt-cookie-control',
49
- '@nuxt/image',
50
- '@nuxtjs/color-mode',
51
- '@nuxtjs/html-validator',
52
- '@nuxtjs/i18n',
53
- '@nuxtjs/tailwindcss',
54
- '@pinia/nuxt',
55
- 'nuxt-seo-kit-module',
56
- ],
57
- nitro: {
58
- compressPublicAssets: true,
59
- },
60
- runtimeConfig: {
61
- public: {
62
- googleAnalyticsId: '', // set via environment variable `NUXT_PUBLIC_GOOGLE_ANALYTICS_ID` only
63
- i18n: {
64
- baseUrl: BASE_URL,
65
- },
66
- isInProduction: process.env.NODE_ENV === 'production',
67
- isTesting: false,
68
- stagingHost:
69
- process.env.NODE_ENV !== 'production' &&
70
- !process.env.NUXT_PUBLIC_STACK_DOMAIN
71
- ? 'localhost:3000'
72
- : undefined,
73
- },
74
- },
75
- typescript: {
76
- shim: false,
77
- // tsConfig: {
78
- // compilerOptions: {
79
- // esModuleInterop: true,
80
- // },
81
- // },
82
- },
83
-
84
- // modules
85
- colorMode: {
86
- classSuffix: '',
87
- },
88
- cookieControl: {
89
- cookies: {
90
- necessary: [
91
- {
92
- description: {
93
- de: 'Dieser Cookie von uns speichert die Einstellungen, die in diesem Dialog getroffen werden.',
94
- en: 'This cookie of ours stores the settings made in this dialog.',
95
- },
96
- id: 'c',
97
- name: {
98
- de: 'Cookie-Präferenzen',
99
- en: 'Cookie Preferences',
17
+ export default defineNuxtConfig(
18
+ defu(
19
+ {
20
+ alias: {
21
+ clipboard: 'clipboard',
22
+ dayjs: 'dayjs',
23
+ sweetalert2: 'sweetalert2',
24
+ }, // TODO: remove (https://github.com/nuxt/nuxt/issues/19426)
25
+ app: {
26
+ head: {
27
+ htmlAttrs: {
28
+ lang: 'en', // fallback data to prevent invalid html at generation
100
29
  },
101
- targetCookieIds: ['ncc_c', 'ncc_e'],
30
+ titleTemplate: '%s', // fully set in `composables/useAppLayout.ts`
102
31
  },
103
- {
104
- description: {
105
- de: 'Dieser Cookie von uns speichert die Sprache, in der diese Webseite angezeigt wird.',
106
- en: "This cookie of ours stores the language that's used to display this website.",
107
- },
108
- id: 'l',
109
- name: {
110
- de: 'Sprache',
111
- en: 'Language',
112
- },
113
- targetCookieIds: [I18N_COOKIE_NAME],
32
+ pageTransition: {
33
+ name: 'layout',
114
34
  },
115
- {
116
- description: {
117
- de: 'Dieser Cookie von uns speichert die Zeitzone, in der sich das Gerät zu befinden scheint.',
118
- en: 'This cookie of ours saves the timezone in which the device appears to be located.',
119
- },
120
- id: 't',
121
- name: {
122
- de: 'Zeitzone',
123
- en: 'Timezone',
124
- },
125
- targetCookieIds: [TIMEZONE_COOKIE_NAME],
35
+ },
36
+ devtools: {
37
+ enabled: process.env.NODE_ENV !== 'production',
38
+ timeline: {
39
+ enabled: true,
126
40
  },
41
+ },
42
+ modules: [
43
+ '@dargmuesli/nuxt-cookie-control',
44
+ '@nuxt/image',
45
+ '@nuxtjs/color-mode',
46
+ '@nuxtjs/html-validator',
47
+ '@nuxtjs/i18n',
48
+ '@nuxtjs/tailwindcss',
49
+ '@pinia/nuxt',
50
+ 'nuxt-seo-kit-module',
127
51
  ],
128
- optional: [
129
- {
130
- description: {
131
- de: 'Die Cookies vom Drittanbieter Google ermöglichen die Analyse von Nutzerverhalten. Diese Analyse hilft uns unsere Dienste zu verbessern, indem wir verstehen, wie diese Webseite genutzt wird.',
132
- en: 'The third-party cookies by Google enable the analysis of user behavior. This analysis helps us to improve our services by understanding how this website is used.',
52
+ nitro: {
53
+ compressPublicAssets: true,
54
+ },
55
+ runtimeConfig: {
56
+ public: {
57
+ vio: {
58
+ googleAnalyticsId: '', // set via environment variable `NUXT_PUBLIC_GOOGLE_ANALYTICS_ID` only
59
+ isInProduction: process.env.NODE_ENV === 'production',
60
+ isTesting: false,
133
61
  },
134
- id: 'ga',
135
- links: {
136
- 'https://policies.google.com/privacy': 'Google Privacy Policy',
137
- 'https://policies.google.com/terms': 'Google Terms of Service',
62
+ },
63
+ },
64
+ typescript: {
65
+ shim: false,
66
+ tsConfig: {
67
+ compilerOptions: {
68
+ esModuleInterop: true,
69
+ // moduleResolution: 'bundler',
70
+ // noErrorTruncation: true,
138
71
  },
139
- name: 'Analytics',
140
- targetCookieIds: ['_ga', '_ga_K4R41W62BR'],
141
72
  },
142
- ],
143
- },
144
- locales: ['en', 'de'],
145
- },
146
- htmlValidator: {
147
- failOnError: false, // TODO: fix invalid html in nuxt html template (https://github.com/nuxt/nuxt/issues/22526)
148
- logLevel: 'warning',
149
- },
150
- i18n: {
151
- ...I18N_MODULE_CONFIG,
152
- defaultLocale: 'en', // Must be set for the default prefix_except_default prefix strategy.
153
- detectBrowserLanguage: {
154
- cookieKey: I18N_COOKIE_NAME,
155
- cookieSecure: true,
73
+ },
74
+
75
+ // modules
76
+ colorMode: {
77
+ classSuffix: '',
78
+ },
79
+ cookieControl: {
80
+ cookies: {
81
+ necessary: [
82
+ {
83
+ description: {
84
+ de: 'Dieser Cookie von uns speichert die Einstellungen, die in diesem Dialog getroffen werden.',
85
+ en: 'This cookie of ours stores the settings made in this dialog.',
86
+ },
87
+ id: 'c',
88
+ name: {
89
+ de: 'Cookie-Präferenzen',
90
+ en: 'Cookie Preferences',
91
+ },
92
+ targetCookieIds: ['ncc_c', 'ncc_e'],
93
+ },
94
+ {
95
+ description: {
96
+ de: 'Dieser Cookie von uns speichert die Sprache, in der diese Webseite angezeigt wird.',
97
+ en: "This cookie of ours stores the language that's used to display this website.",
98
+ },
99
+ id: 'l',
100
+ name: {
101
+ de: 'Sprache',
102
+ en: 'Language',
103
+ },
104
+ targetCookieIds: [I18N_COOKIE_NAME],
105
+ },
106
+ {
107
+ description: {
108
+ de: 'Dieser Cookie von uns speichert die Zeitzone, in der sich das Gerät zu befinden scheint.',
109
+ en: 'This cookie of ours saves the timezone in which the device appears to be located.',
110
+ },
111
+ id: 't',
112
+ name: {
113
+ de: 'Zeitzone',
114
+ en: 'Timezone',
115
+ },
116
+ targetCookieIds: [TIMEZONE_COOKIE_NAME],
117
+ },
118
+ ],
119
+ optional: [
120
+ {
121
+ description: {
122
+ de: 'Die Cookies vom Drittanbieter Google ermöglichen die Analyse von Nutzerverhalten. Diese Analyse hilft uns unsere Dienste zu verbessern, indem wir verstehen, wie diese Webseite genutzt wird.',
123
+ en: 'The third-party cookies by Google enable the analysis of user behavior. This analysis helps us to improve our services by understanding how this website is used.',
124
+ },
125
+ id: 'ga',
126
+ links: {
127
+ 'https://policies.google.com/privacy': 'Google Privacy Policy',
128
+ 'https://policies.google.com/terms': 'Google Terms of Service',
129
+ },
130
+ name: 'Analytics',
131
+ targetCookieIds: ['_ga', '_ga_K4R41W62BR'],
132
+ },
133
+ ],
134
+ },
135
+ locales: ['en', 'de'],
136
+ },
137
+ htmlValidator: {
138
+ failOnError: false, // TODO: fix invalid html in nuxt html template (https://github.com/nuxt/nuxt/issues/22526)
139
+ logLevel: 'warning',
140
+ },
141
+ i18n: {
142
+ defaultLocale: 'en', // Must be set for the default prefix_except_default prefix strategy.
143
+ detectBrowserLanguage: {
144
+ cookieKey: I18N_COOKIE_NAME,
145
+ cookieSecure: true,
146
+ },
147
+ },
148
+ linkChecker: {
149
+ debug: process.env.NODE_ENV === 'development',
150
+ failOnError: true,
151
+ },
152
+ seoKit: {
153
+ splash: false,
154
+ },
155
+ site: {
156
+ debug: process.env.NODE_ENV === 'development',
157
+ titleSeparator: '·',
158
+ },
159
+ sitemap: {
160
+ exclude: ['/api/pages/**'],
161
+ },
162
+ tailwindcss: {
163
+ cssPath: join(currentDir, './assets/css/tailwind.css'),
164
+ },
156
165
  },
157
- },
158
- linkChecker: {
159
- debug: process.env.NODE_ENV === 'development',
160
- failOnError: true,
161
- },
162
- seoKit: {
163
- splash: false,
164
- },
165
- site: {
166
- debug: process.env.NODE_ENV === 'development',
167
- name: SITE_NAME,
168
- titleSeparator: '·',
169
- url: BASE_URL,
170
- },
171
- sitemap: {
172
- exclude: ['/api/pages/**'],
173
- },
174
- tailwindcss: {
175
- cssPath: join(currentDir, './assets/css/tailwind.css'),
176
- },
177
- })
166
+ VIO_NUXT_BASE_CONFIG({
167
+ baseUrl: BASE_URL,
168
+ siteName: SITE_NAME,
169
+ stagingHost: 'localhost:3000',
170
+ }),
171
+ ),
172
+ )
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dargmuesli/nuxt-vio",
3
- "version": "3.0.0-beta.9",
3
+ "version": "3.0.0",
4
4
  "type": "module",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -35,26 +35,26 @@
35
35
  "lint:fix": "pnpm lint:js --fix && pnpm lint:ts --fix && pnpm lint:style --fix",
36
36
  "lint:js": "eslint --cache --ext .js,.ts,.vue --ignore-path .gitignore .",
37
37
  "lint:staged": "lint-staged",
38
- "lint:style": "stylelint **/*.{vue,css} --ignore-path .gitignore",
38
+ "lint:style": "stylelint --cache \"**/*.{vue,css}\" --ignore-path .gitignore",
39
39
  "lint:ts": "nuxt typecheck",
40
40
  "lint": "pnpm lint:js && pnpm lint:ts && pnpm lint:style",
41
41
  "prepare": "nuxt prepare .playground",
42
42
  "preview": "nuxt preview .playground"
43
43
  },
44
44
  "dependencies": {
45
- "@dargmuesli/nuxt-cookie-control": "6.1.5",
46
- "@http-util/status-i18n": "0.7.0",
45
+ "@dargmuesli/nuxt-cookie-control": "6.4.1",
46
+ "@http-util/status-i18n": "0.8.1",
47
47
  "@nuxt/image": "1.0.0-rc.1",
48
48
  "@nuxtjs/color-mode": "3.3.0",
49
49
  "@nuxtjs/html-validator": "1.5.2",
50
- "@nuxtjs/i18n": "8.0.0-rc.2",
50
+ "@nuxtjs/i18n": "8.0.0-rc.3",
51
51
  "@nuxtjs/tailwindcss": "6.8.0",
52
52
  "@pinia/nuxt": "0.4.11",
53
- "@tailwindcss/forms": "0.5.4",
53
+ "@tailwindcss/forms": "0.5.5",
54
54
  "@tailwindcss/typography": "0.5.9",
55
55
  "@urql/core": "4.1.1",
56
56
  "@urql/devtools": "2.0.3",
57
- "@urql/exchange-graphcache": "6.3.1",
57
+ "@urql/exchange-graphcache": "6.3.2",
58
58
  "@urql/vue": "1.1.2",
59
59
  "@vuelidate/core": "2.0.3",
60
60
  "@vuelidate/validators": "2.0.3",
@@ -62,31 +62,32 @@
62
62
  "dayjs": "2.0.0-alpha.4",
63
63
  "is-https": "4.0.0",
64
64
  "jose": "4.14.4",
65
- "marked": "7.0.1",
66
- "nuxt-seo-kit-module": "2.0.0-beta.9",
65
+ "marked": "7.0.4",
66
+ "nuxt-seo-kit-module": "2.0.0-beta.13",
67
67
  "pinia": "2.1.6",
68
- "sweetalert2": "11.7.20",
69
- "vue-gtag": "2.0.1"
68
+ "sweetalert2": "11.7.27",
69
+ "vue-gtag": "2.0.1",
70
+ "vue-tsc": "1.8.8"
70
71
  },
71
72
  "devDependencies": {
72
73
  "@intlify/eslint-plugin-vue-i18n": "3.0.0-next.3",
73
74
  "@nuxtjs/eslint-config-typescript": "12.0.0",
74
75
  "@types/marked": "5.0.1",
75
- "eslint": "8.46.0",
76
+ "eslint": "8.47.0",
76
77
  "eslint-config-prettier": "9.0.0",
77
78
  "eslint-plugin-compat": "4.1.4",
78
79
  "eslint-plugin-nuxt": "4.0.0",
79
80
  "eslint-plugin-prettier": "5.0.0",
80
81
  "eslint-plugin-yml": "1.8.0",
81
- "lint-staged": "13.2.3",
82
+ "lint-staged": "14.0.1",
82
83
  "nuxt": "3.6.5",
83
- "prettier": "3.0.1",
84
- "stylelint": "15.10.2",
84
+ "prettier": "3.0.2",
85
+ "prettier-plugin-tailwindcss": "0.5.3",
86
+ "stylelint": "15.10.3",
85
87
  "stylelint-config-recommended-vue": "1.5.0",
86
88
  "stylelint-config-standard": "34.0.0",
87
89
  "stylelint-no-unsupported-browser-features": "7.0.0",
88
90
  "tailwindcss": "3.3.3",
89
- "vue": "3.3.4",
90
- "vue-tsc": "1.8.8"
91
+ "vue": "3.3.4"
91
92
  }
92
93
  }
@@ -10,7 +10,7 @@ export default defineNuxtPlugin((nuxtApp) => {
10
10
  {
11
11
  bootstrap: !!cookieControl.cookiesEnabledIds.value?.includes('ga'),
12
12
  config: {
13
- id: config.public.googleAnalyticsId,
13
+ id: config.public.vio.googleAnalyticsId,
14
14
  params: {
15
15
  cookie_flags: 'secure;samesite=strict',
16
16
  },
@@ -1,5 +1,14 @@
1
1
  export const SITE_NAME = 'Vio'
2
2
 
3
+ export const BASE_URL =
4
+ (process.env.NUXT_PUBLIC_STACK_DOMAIN ? 'https' : 'http') +
5
+ '://' +
6
+ (process.env.NUXT_PUBLIC_STACK_DOMAIN ||
7
+ `${process.env.HOST || 'localhost'}:${
8
+ !process.env.NODE_ENV || process.env.NODE_ENV === 'development'
9
+ ? '3000'
10
+ : '3001'
11
+ }`)
3
12
  export const CACHE_VERSION = 'bOXMwoKlJr'
4
13
  export const COOKIE_PREFIX = SITE_NAME.toLocaleLowerCase()
5
14
  export const COOKIE_SEPARATOR = '_'
@@ -30,3 +39,44 @@ export const I18N_VUE_CONFIG = {
30
39
  export const TIMEZONE_COOKIE_NAME = [COOKIE_PREFIX, 'tz'].join(COOKIE_SEPARATOR)
31
40
  export const TIMEZONE_HEADER_KEY = `X-${SITE_NAME}-Timezone`
32
41
  export const VALIDATION_SUGGESTION_TITLE_LENGTH_MAXIMUM = 300
42
+ export const VIO_NUXT_BASE_CONFIG = ({
43
+ baseUrl,
44
+ siteName,
45
+ stagingHost,
46
+ }: {
47
+ baseUrl?: string
48
+ siteName: string
49
+ stagingHost?: string
50
+ }) =>
51
+ ({
52
+ app: {
53
+ head: {
54
+ title: SITE_NAME, // fallback data to prevent invalid html at generation
55
+ },
56
+ },
57
+ runtimeConfig: {
58
+ public: {
59
+ i18n: {
60
+ ...(baseUrl ? { baseUrl } : {}),
61
+ },
62
+ vio: {
63
+ ...(stagingHost
64
+ ? {
65
+ stagingHost:
66
+ process.env.NODE_ENV !== 'production' &&
67
+ !process.env.NUXT_PUBLIC_STACK_DOMAIN
68
+ ? stagingHost
69
+ : undefined,
70
+ }
71
+ : {}),
72
+ },
73
+ },
74
+ },
75
+
76
+ // modules
77
+ i18n: I18N_MODULE_CONFIG, // `langDir`, `lazy` and `locales` must be configured to extend a layer having lazy-loaded translations (https://v8.i18n.nuxtjs.org/guide/layers#locales)
78
+ site: {
79
+ name: siteName,
80
+ ...(baseUrl ? { url: baseUrl } : {}),
81
+ },
82
+ }) as Parameters<typeof defineNuxtConfig>[0]
package/utils/routing.ts CHANGED
@@ -1,2 +1,4 @@
1
- export const append = (path: string, pathToAppend: string) =>
2
- path + (path.endsWith('/') ? '' : '/') + pathToAppend
1
+ import { RouteLocationRaw } from '#vue-router'
2
+
3
+ export const append = (path: string, pathToAppend?: RouteLocationRaw) =>
4
+ path + (path.endsWith('/') ? '' : '/') + (pathToAppend ?? '')
@@ -1,17 +0,0 @@
1
- <template>
2
- <svg
3
- xmlns="http://www.w3.org/2000/svg"
4
- width="1064pt"
5
- height="1064pt"
6
- version="1.1"
7
- viewBox="0 0 1064 1064"
8
- >
9
- <g id="#9a9a9aff">
10
- <path
11
- fill="#ecc94b"
12
- d="M 802.62 185.62 C 806.76 181.54 810.71 177.26 815.06 173.41 C 828.57 186.40 841.60 199.89 854.94 213.06 C 866.81 225.07 878.95 236.83 890.68 248.99 C 858.25 281.90 825.33 314.33 792.78 347.13 C 801.85 360.72 810.67 374.54 817.81 389.27 C 849.17 451.62 859.14 524.40 845.66 592.88 C 835.73 644.31 812.88 693.19 779.77 733.77 C 748.01 772.91 706.76 804.28 660.61 824.52 C 601.85 850.47 535.17 858.08 472.10 845.87 C 399.83 832.28 332.81 792.71 286.03 735.97 C 261.50 706.51 242.51 672.51 229.95 636.31 C 214.56 591.74 209.12 543.75 214.40 496.88 C 219.97 445.89 238.13 396.34 266.77 353.78 C 297.11 308.55 339.24 271.29 387.93 246.85 C 436.80 222.00 492.22 210.23 546.98 212.87 C 592.26 214.95 637.06 226.87 677.39 247.55 C 691.14 254.44 704.16 262.67 716.88 271.28 C 745.53 242.80 774.02 214.15 802.62 185.62 M 668.11 321.11 C 659.08 330.37 649.57 339.17 640.85 348.72 C 671.30 366.97 697.16 392.75 715.33 423.27 C 726.58 412.55 737.35 401.34 748.40 390.41 C 762.78 375.92 777.38 361.65 791.64 347.04 C 771.18 318.23 745.87 292.82 717.00 272.43 C 700.48 288.43 684.45 304.92 668.11 321.11 M 436.34 342.35 C 401.95 359.59 372.51 386.48 352.08 419.07 C 331.27 452.09 319.83 490.96 319.57 530.00 C 319.10 567.70 329.06 605.44 347.89 638.09 C 365.96 669.51 392.23 696.14 423.39 714.64 C 456.33 734.24 494.65 744.78 533.00 744.45 C 574.05 744.36 615.02 731.94 649.19 709.17 C 676.80 690.95 700.05 666.14 716.43 637.39 C 736.74 602.03 746.42 560.71 744.11 520.01 C 742.05 486.63 732.53 453.44 715.15 424.78 C 673.22 466.47 631.56 508.43 589.65 550.14 C 586.42 547.81 583.82 544.77 581.00 542.00 C 559.16 520.17 537.32 498.33 515.49 476.50 C 514.40 475.63 513.59 474.12 515.09 473.18 C 556.44 431.67 598.01 390.37 639.32 348.82 C 615.75 334.67 589.17 325.65 561.99 321.72 C 519.31 315.51 474.81 322.86 436.34 342.35 Z"
13
- opacity="1"
14
- />
15
- </g>
16
- </svg>
17
- </template>
@@ -1,98 +0,0 @@
1
- <template>
2
- <header class="flex items-center justify-between gap-4 mb-8">
3
- <VioButton :aria-label="t('creal')" :to="localePath('/')">
4
- <span class="text-lg font-bold">{{ t('creal') }}</span>
5
- <template #prefix>
6
- <IconLogo class="h-10 w-10" />
7
- </template>
8
- </VioButton>
9
- <VioLink
10
- v-if="eventsCurrentCount"
11
- class="flex items-center gap-2 rounded-full border px-4 py-2 font-bold focus:rounded-full sm:px-4"
12
- :is-colored="false"
13
- :to="localePath('/events')"
14
- >
15
- <VioLayoutLivePulse />
16
- <span class="hidden whitespace-nowrap sm:inline">
17
- {{ t('live') }}
18
- </span>
19
- </VioLink>
20
- <VioLink
21
- v-else-if="eventsFutureCount"
22
- class="flex items-center gap-2 rounded-full border px-2 py-2 font-bold focus:rounded-full sm:px-4"
23
- :is-colored="false"
24
- :to="localePath('/events')"
25
- >
26
- <VioLayoutLivePulse />
27
- <span class="hidden whitespace-nowrap sm:inline">
28
- {{ t('eventsFuture') }}
29
- </span>
30
- </VioLink>
31
- <VioButton
32
- :aria-label="t('bookCreal')"
33
- class="basis-0 text-lg font-bold"
34
- :is-colored="false"
35
- :to="`mailto:e-mail+creal@jonas-thelemann.de?subject=${encodeURIComponent(
36
- t('bookingSubject'),
37
- )}`"
38
- >
39
- <span class="basis-0 whitespace-nowrap">{{ t('bookCreal') }}</span>
40
- <template #suffix>
41
- <IconArrowRight />
42
- </template>
43
- </VioButton>
44
- </header>
45
- </template>
46
-
47
- <script setup lang="ts">
48
- const { $dayjs } = useNuxtApp()
49
- const { t } = useI18n()
50
- const localePath = useLocalePath()
51
- const strapiFetch = useStrapiFetch()
52
-
53
- // async data
54
- let eventsCurrentCount = 0
55
- let eventsFutureCount = 0
56
-
57
- // methods
58
- const init = async () => {
59
- eventsCurrentCount = (
60
- (await strapiFetch('/events', {
61
- query: {
62
- 'filters[$and][0][dateStart][$lte]': $dayjs().toISOString(),
63
- 'filters[$and][1][$or][0][dateEnd][$gt]': $dayjs().toISOString(),
64
- 'filters[$and][1][$or][1][dateStart][$gte]': $dayjs()
65
- .startOf('day')
66
- .toISOString(),
67
- },
68
- })) as any
69
- ).meta.pagination.total
70
- eventsFutureCount = (
71
- (await strapiFetch('/events', {
72
- query: {
73
- 'filters[dateStart][$gt]': $dayjs().toISOString(),
74
- },
75
- })) as any
76
- ).meta.pagination.total
77
- }
78
-
79
- // initialization
80
- try {
81
- await init()
82
- } catch (error: any) {}
83
- </script>
84
-
85
- <i18n lang="yaml">
86
- en:
87
- bookCreal: Book cReal
88
- bookingSubject: Booking Request
89
- creal: cReal
90
- eventsFuture: Upcoming events
91
- live: Live
92
- de:
93
- bookCreal: cReal buchen
94
- bookingSubject: Buchungsanfrage
95
- creal: cReal
96
- eventsFuture: Kommende Veranstaltungen
97
- live: Live
98
- </i18n>