@dargmuesli/nuxt-vio 2.0.1 → 3.0.0-beta.10

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.
Files changed (79) hide show
  1. package/app.config.ts +83 -40
  2. package/components/vio/_/VioApp.vue +92 -0
  3. package/components/{VioError.vue → vio/_/VioError.vue} +1 -1
  4. package/components/{VioLink.vue → vio/_/VioLink.vue} +2 -2
  5. package/components/vio/button/VioButtonColored.vue +52 -0
  6. package/components/vio/card/VioCard.vue +19 -0
  7. package/components/vio/card/state/VioCardState.vue +20 -0
  8. package/components/vio/card/state/VioCardStateAlert.vue +14 -0
  9. package/components/vio/form/VioForm.vue +84 -0
  10. package/components/vio/form/VioFormCheckbox.vue +27 -0
  11. package/components/vio/form/input/VioFormInput.vue +192 -0
  12. package/components/vio/form/input/VioFormInputIconWrapper.vue +7 -0
  13. package/components/vio/form/input/VioFormInputUrl.vue +54 -0
  14. package/components/vio/form/input/state/VioFormInputState.vue +5 -0
  15. package/components/vio/form/input/state/VioFormInputStateError.vue +32 -0
  16. package/components/vio/form/input/state/VioFormInputStateInfo.vue +32 -0
  17. package/components/vio/icon/IconArrowRight.vue +31 -0
  18. package/components/vio/icon/IconCalendar.vue +31 -0
  19. package/components/vio/icon/IconChatOutline.vue +27 -0
  20. package/components/vio/icon/IconChatSolid.vue +26 -0
  21. package/components/vio/icon/IconCheckCircle.vue +29 -0
  22. package/components/vio/icon/IconContainer.vue +15 -0
  23. package/components/vio/icon/IconDownload.vue +31 -0
  24. package/components/vio/icon/IconExclamationCircle.vue +29 -0
  25. package/components/vio/icon/IconHome.vue +31 -0
  26. package/components/vio/icon/IconHourglass.vue +32 -0
  27. package/components/vio/icon/IconLightbulb.vue +27 -0
  28. package/components/vio/icon/IconLogo.vue +17 -0
  29. package/components/vio/icon/IconMixcloud.vue +23 -0
  30. package/components/vio/icon/IconMusic.vue +27 -0
  31. package/components/vio/icon/IconPlay.vue +25 -0
  32. package/components/vio/icon/IconShare.vue +27 -0
  33. package/components/{VioLayout.vue → vio/layout/VioLayout.vue} +1 -1
  34. package/components/vio/layout/VioLayoutBreadcrumbs.vue +83 -0
  35. package/components/vio/layout/VioLayoutFooter.vue +40 -0
  36. package/components/vio/layout/VioLayoutFooterCategory.vue +17 -0
  37. package/components/vio/layout/VioLayoutHeader.vue +98 -0
  38. package/components/vio/layout/VioLayoutSpanList.vue +20 -0
  39. package/components/vio/loader/indicator/VioLoaderIndicator.vue +14 -0
  40. package/components/vio/loader/indicator/VioLoaderIndicatorPing.vue +12 -0
  41. package/components/vio/loader/indicator/VioLoaderIndicatorSpinner.vue +24 -0
  42. package/components/{VioLegalNotice.vue → vio/page/VioPageLegalNotice.vue} +10 -8
  43. package/components/{VioPrivacyPolicy.vue → vio/page/VioPagePrivacyPolicy.vue} +19 -12
  44. package/composables/useAppLayout.ts +12 -18
  45. package/composables/useDateTime.ts +17 -0
  46. package/composables/useFavicons.ts +5 -33
  47. package/composables/useFireError.ts +17 -0
  48. package/composables/useGetServiceHref.ts +21 -0
  49. package/composables/useHeadDefault.ts +21 -0
  50. package/composables/usePolyfills.ts +23 -0
  51. package/composables/useStrapiFetch.ts +10 -0
  52. package/error.vue +5 -2
  53. package/locales/de.json +7 -1
  54. package/locales/en.json +7 -1
  55. package/nuxt.config.ts +56 -10
  56. package/package.json +37 -25
  57. package/pages/legal-notice.vue +1 -1
  58. package/pages/privacy-policy.vue +1 -1
  59. package/plugins/dayjs.ts +34 -0
  60. package/plugins/gtag.client.ts +3 -0
  61. package/plugins/i18n.ts +5 -0
  62. package/plugins/marked.ts +9 -0
  63. package/server/middleware/headers.ts +41 -10
  64. package/server/tsconfig.json +1 -1
  65. package/server/utils/util.ts +2 -0
  66. package/store/auth.ts +32 -0
  67. package/tailwind.config.ts +131 -10
  68. package/types/api.d.ts +9 -0
  69. package/types/fetch.d.ts +8 -0
  70. package/types/modules/gql.d.ts +6 -0
  71. package/types/modules/graphql.d.ts +6 -0
  72. package/utils/constants.ts +10 -1
  73. package/utils/form.ts +19 -0
  74. package/utils/networking.ts +117 -0
  75. package/utils/text.ts +20 -0
  76. package/LICENSE +0 -674
  77. package/components/VioApp.vue +0 -59
  78. /package/components/{VioButton.vue → vio/button/VioButton.vue} +0 -0
  79. /package/components/{VioHr.vue → vio/layout/VioLayoutHr.vue} +0 -0
package/nuxt.config.ts CHANGED
@@ -1,12 +1,18 @@
1
1
  import { dirname, join } from 'node:path'
2
2
  import { fileURLToPath } from 'node:url'
3
3
 
4
- import { I18N_MODULE_CONFIG, SITE_NAME } from './utils/constants'
4
+ import {
5
+ I18N_COOKIE_NAME,
6
+ I18N_MODULE_CONFIG,
7
+ TIMEZONE_COOKIE_NAME,
8
+ SITE_NAME,
9
+ } from './utils/constants'
5
10
 
6
11
  const currentDir = dirname(fileURLToPath(import.meta.url))
7
12
 
8
13
  const BASE_URL =
9
- 'https://' +
14
+ (process.env.NUXT_PUBLIC_STACK_DOMAIN ? 'https' : 'http') +
15
+ '://' +
10
16
  (process.env.NUXT_PUBLIC_STACK_DOMAIN ||
11
17
  `${process.env.HOST || 'localhost'}:${
12
18
  !process.env.NODE_ENV || process.env.NODE_ENV === 'development'
@@ -17,22 +23,35 @@ const BASE_URL =
17
23
  // https://v3.nuxtjs.org/api/configuration/nuxt.config
18
24
  export default defineNuxtConfig({
19
25
  alias: {
20
- sweetalert2: 'sweetalert2', // TODO: remove (https://github.com/nuxt/nuxt/issues/19426)
21
- },
26
+ dayjs: 'dayjs',
27
+ sweetalert2: 'sweetalert2',
28
+ }, // TODO: remove (https://github.com/nuxt/nuxt/issues/19426)
22
29
  app: {
23
30
  head: {
24
31
  htmlAttrs: {
25
32
  lang: 'en', // fallback data to prevent invalid html at generation
26
33
  },
27
- titleTemplate: `%s`,
28
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,
29
45
  },
30
46
  },
31
47
  modules: [
32
48
  '@dargmuesli/nuxt-cookie-control',
49
+ '@nuxt/image',
50
+ '@nuxtjs/color-mode',
33
51
  '@nuxtjs/html-validator',
34
52
  '@nuxtjs/i18n',
35
53
  '@nuxtjs/tailwindcss',
54
+ '@pinia/nuxt',
36
55
  'nuxt-seo-kit-module',
37
56
  ],
38
57
  nitro: {
@@ -46,14 +65,26 @@ export default defineNuxtConfig({
46
65
  },
47
66
  isInProduction: process.env.NODE_ENV === 'production',
48
67
  isTesting: false,
68
+ stagingHost:
69
+ process.env.NODE_ENV !== 'production' &&
70
+ !process.env.NUXT_PUBLIC_STACK_DOMAIN
71
+ ? 'localhost:3000'
72
+ : undefined,
49
73
  },
50
74
  },
51
75
  typescript: {
52
76
  shim: false,
53
- strict: true,
77
+ // tsConfig: {
78
+ // compilerOptions: {
79
+ // esModuleInterop: true,
80
+ // },
81
+ // },
54
82
  },
55
83
 
56
84
  // modules
85
+ colorMode: {
86
+ classSuffix: '',
87
+ },
57
88
  cookieControl: {
58
89
  cookies: {
59
90
  necessary: [
@@ -79,7 +110,19 @@ export default defineNuxtConfig({
79
110
  de: 'Sprache',
80
111
  en: 'Language',
81
112
  },
82
- targetCookieIds: ['i18n_redirected'],
113
+ targetCookieIds: [I18N_COOKIE_NAME],
114
+ },
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],
83
126
  },
84
127
  ],
85
128
  optional: [
@@ -101,18 +144,20 @@ export default defineNuxtConfig({
101
144
  locales: ['en', 'de'],
102
145
  },
103
146
  htmlValidator: {
104
- // failOnError: true,
147
+ failOnError: false, // TODO: fix invalid html in nuxt html template (https://github.com/nuxt/nuxt/issues/22526)
105
148
  logLevel: 'warning',
106
149
  },
107
150
  i18n: {
108
151
  ...I18N_MODULE_CONFIG,
109
152
  defaultLocale: 'en', // Must be set for the default prefix_except_default prefix strategy.
110
153
  detectBrowserLanguage: {
154
+ cookieKey: I18N_COOKIE_NAME,
111
155
  cookieSecure: true,
112
156
  },
113
157
  },
114
158
  linkChecker: {
115
- failOnError: false, // TODO: enable (https://github.com/harlan-zw/nuxt-seo-kit/issues/4#issuecomment-1434522124)
159
+ debug: process.env.NODE_ENV === 'development',
160
+ failOnError: true,
116
161
  },
117
162
  seoKit: {
118
163
  splash: false,
@@ -120,10 +165,11 @@ export default defineNuxtConfig({
120
165
  site: {
121
166
  debug: process.env.NODE_ENV === 'development',
122
167
  name: SITE_NAME,
168
+ titleSeparator: '·',
123
169
  url: BASE_URL,
124
170
  },
125
171
  sitemap: {
126
- exclude: ['/api/**'],
172
+ exclude: ['/api/pages/**'],
127
173
  },
128
174
  tailwindcss: {
129
175
  cssPath: join(currentDir, './assets/css/tailwind.css'),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dargmuesli/nuxt-vio",
3
- "version": "2.0.1",
3
+ "version": "3.0.0-beta.10",
4
4
  "type": "module",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -8,16 +8,18 @@
8
8
  "engines": {
9
9
  "node": "20"
10
10
  },
11
- "packageManager": "pnpm@8.6.11",
11
+ "packageManager": "pnpm@8.6.12",
12
12
  "files": [
13
13
  "assets",
14
14
  "components",
15
15
  "composables",
16
16
  "layouts",
17
17
  "locales",
18
- "server",
19
18
  "pages",
20
19
  "plugins",
20
+ "server",
21
+ "store",
22
+ "types",
21
23
  "utils",
22
24
  "app.config.ts",
23
25
  "error.vue",
@@ -25,56 +27,66 @@
25
27
  "nuxt.config.ts",
26
28
  "tailwind.config.ts"
27
29
  ],
28
- "main": "./nuxt.config.ts",
30
+ "main": "nuxt.config.ts",
29
31
  "scripts": {
30
- "dev": "nuxi dev .playground",
31
- "build": "nuxi build .playground",
32
- "generate": "nuxi generate .playground",
33
- "preview": "nuxi preview .playground",
34
- "prepare": "pnpm husky install && pnpm nuxt prepare .playground",
35
- "lint": "pnpm lint:js && pnpm lint:ts && pnpm lint:style",
32
+ "build": "nuxt build .playground",
33
+ "dev": "nuxt dev .playground",
34
+ "generate": "nuxt generate .playground",
36
35
  "lint:fix": "pnpm lint:js --fix && pnpm lint:ts --fix && pnpm lint:style --fix",
37
36
  "lint:js": "eslint --cache --ext .js,.ts,.vue --ignore-path .gitignore .",
38
- "lint:staged": "pnpm lint-staged",
37
+ "lint:staged": "lint-staged",
39
38
  "lint:style": "stylelint **/*.{vue,css} --ignore-path .gitignore",
40
- "lint:ts": "nuxt typecheck"
39
+ "lint:ts": "nuxt typecheck",
40
+ "lint": "pnpm lint:js && pnpm lint:ts && pnpm lint:style",
41
+ "prepare": "nuxt prepare .playground",
42
+ "preview": "nuxt preview .playground"
41
43
  },
42
44
  "dependencies": {
43
45
  "@dargmuesli/nuxt-cookie-control": "6.1.5",
44
- "@dargmuesli/nuxt-vio": "link:",
45
46
  "@http-util/status-i18n": "0.7.0",
47
+ "@nuxt/image": "1.0.0-rc.1",
48
+ "@nuxtjs/color-mode": "3.3.0",
46
49
  "@nuxtjs/html-validator": "1.5.2",
47
- "@nuxtjs/i18n": "8.0.0-rc.1",
50
+ "@nuxtjs/i18n": "8.0.0-rc.2",
48
51
  "@nuxtjs/tailwindcss": "6.8.0",
52
+ "@pinia/nuxt": "0.4.11",
53
+ "@tailwindcss/forms": "0.5.4",
49
54
  "@tailwindcss/typography": "0.5.9",
55
+ "@urql/core": "4.1.1",
56
+ "@urql/devtools": "2.0.3",
57
+ "@urql/exchange-graphcache": "6.3.1",
58
+ "@urql/vue": "1.1.2",
59
+ "@vuelidate/core": "2.0.3",
60
+ "@vuelidate/validators": "2.0.3",
61
+ "clipboard": "2.0.11",
62
+ "dayjs": "2.0.0-alpha.4",
63
+ "is-https": "4.0.0",
64
+ "jose": "4.14.4",
65
+ "marked": "7.0.1",
50
66
  "nuxt-seo-kit-module": "2.0.0-beta.9",
67
+ "pinia": "2.1.6",
51
68
  "sweetalert2": "11.7.20",
52
69
  "vue-gtag": "2.0.1"
53
70
  },
54
71
  "devDependencies": {
55
- "@commitlint/cli": "17.6.7",
56
- "@commitlint/config-conventional": "17.6.7",
57
72
  "@intlify/eslint-plugin-vue-i18n": "3.0.0-next.3",
58
73
  "@nuxtjs/eslint-config-typescript": "12.0.0",
74
+ "@types/marked": "5.0.1",
59
75
  "eslint": "8.46.0",
60
- "eslint-config-prettier": "8.9.0",
76
+ "eslint-config-prettier": "9.0.0",
77
+ "eslint-plugin-compat": "4.1.4",
61
78
  "eslint-plugin-nuxt": "4.0.0",
62
79
  "eslint-plugin-prettier": "5.0.0",
63
80
  "eslint-plugin-yml": "1.8.0",
64
- "husky": "8.0.3",
65
81
  "lint-staged": "13.2.3",
66
82
  "nuxt": "3.6.5",
67
- "prettier": "3.0.0",
83
+ "prettier": "3.0.1",
68
84
  "stylelint": "15.10.2",
69
85
  "stylelint-config-recommended-vue": "1.5.0",
70
86
  "stylelint-config-standard": "34.0.0",
71
87
  "stylelint-no-unsupported-browser-features": "7.0.0",
72
- "typescript": "5.1.6",
88
+ "tailwindcss": "3.3.3",
89
+ "vue": "3.3.4",
73
90
  "vue-tsc": "1.8.8"
74
- },
75
- "pnpm": {
76
- "overrides": {
77
- "eslint-plugin-vue": "9.15.1"
78
- }
79
91
  }
80
92
  }
@@ -1,3 +1,3 @@
1
1
  <template>
2
- <VioLegalNotice />
2
+ <VioPageLegalNotice />
3
3
  </template>
@@ -1,5 +1,5 @@
1
1
  <template>
2
- <VioPrivacyPolicy
2
+ <VioPagePrivacyPolicy
3
3
  :is-enabled="{
4
4
  summary: {
5
5
  generalNotes: true,
@@ -0,0 +1,34 @@
1
+ import dayjs from 'dayjs'
2
+
3
+ // workaround for [1]
4
+ import de from 'dayjs/locale/de'
5
+ // import 'dayjs/locale/de' does not make locale available
6
+
7
+ import isSameOrBefore from 'dayjs/plugin/isSameOrBefore'
8
+ import localizedFormat from 'dayjs/plugin/localizedFormat'
9
+ import timezone from 'dayjs/plugin/timezone'
10
+ import utc from 'dayjs/plugin/utc'
11
+
12
+ export default defineNuxtPlugin((_nuxtApp) => {
13
+ dayjs.extend(isSameOrBefore)
14
+ dayjs.extend(localizedFormat)
15
+ dayjs.extend(timezone)
16
+ dayjs.extend(utc)
17
+
18
+ // workaround for [1]
19
+ dayjs.locale(de)
20
+ // dayjs.locale(en) makes `format` error
21
+
22
+ return {
23
+ provide: {
24
+ dayjs,
25
+ },
26
+ }
27
+ })
28
+
29
+ /*
30
+ [1]
31
+ https://github.com/nuxt/framework/issues/7534#issuecomment-1248596609
32
+ https://github.com/nuxt/framework/issues/7206
33
+ https://github.com/maevsi/maevsi/issues/956
34
+ */
@@ -11,6 +11,9 @@ export default defineNuxtPlugin((nuxtApp) => {
11
11
  bootstrap: !!cookieControl.cookiesEnabledIds.value?.includes('ga'),
12
12
  config: {
13
13
  id: config.public.googleAnalyticsId,
14
+ params: {
15
+ cookie_flags: 'secure;samesite=strict',
16
+ },
14
17
  },
15
18
  },
16
19
  router,
@@ -0,0 +1,5 @@
1
+ export default defineNuxtPlugin((nuxtApp) => {
2
+ nuxtApp.hook('i18n:localeSwitched', ({ newLocale }) => {
3
+ nuxtApp.vueApp.$nuxt.$dayjs.locale(newLocale)
4
+ })
5
+ })
@@ -0,0 +1,9 @@
1
+ import { marked } from 'marked'
2
+
3
+ export default defineNuxtPlugin((_nuxtApp) => {
4
+ return {
5
+ provide: {
6
+ marked: marked.parse,
7
+ },
8
+ }
9
+ })
@@ -1,13 +1,44 @@
1
- import { defineEventHandler } from 'h3'
1
+ import { appendHeader, defineEventHandler } from 'h3'
2
+ import type { H3Event } from 'h3'
3
+ import { AppConfig } from 'nuxt/schema'
2
4
 
3
- export default defineEventHandler((event) => {
4
- const { res } = event.node
5
+ import { TIMEZONE_HEADER_KEY } from '../../utils/constants'
6
+ import { getTimezone } from '../../utils/networking'
5
7
 
6
- res.setHeader('Permissions-Policy', '')
7
-
8
- // // Disabled until there is better browser support (https://caniuse.com/?search=report-to)
9
- // res.setHeader(
10
- // 'Report-To',
11
- // '{"group":"default","max_age":31536000,"endpoints":[{"url":"https://dargmuesli.report-uri.com/a/d/g"}],"include_subdomains":true}'
12
- // )
8
+ export default defineEventHandler(async (event) => {
9
+ setRequestHeader(event, TIMEZONE_HEADER_KEY, await getTimezone(event))
10
+ // setContentSecurityPolicy(event);
11
+ setResponseHeaders(event)
13
12
  })
13
+
14
+ // const setContentSecurityPolicy = (event: H3Event) => {
15
+ // const config = useAppConfig();
16
+
17
+ // appendHeader(
18
+ // event,
19
+ // "Content-Security-Policy",
20
+ // getCspAsString(config.public.vio.server.middleware.headers.csp)
21
+ // );
22
+ // };
23
+
24
+ const setRequestHeader = (event: H3Event, name: string, value?: string) => {
25
+ event.node.req.headers[name] = value
26
+ }
27
+
28
+ const setResponseHeaders = (event: H3Event) => {
29
+ const config = useAppConfig() as AppConfig
30
+
31
+ for (const entry of Object.entries(
32
+ config.vio.server.middleware.headers.csp.default,
33
+ )) {
34
+ appendHeader(event, entry[0], entry[1])
35
+ }
36
+
37
+ if (process.env.NODE_ENV === 'production') {
38
+ for (const entry of Object.entries(
39
+ config.vio.server.middleware.headers.csp.production,
40
+ )) {
41
+ appendHeader(event, entry[0], entry[1])
42
+ }
43
+ }
44
+ }
@@ -1,3 +1,3 @@
1
1
  {
2
- "extends": "../.nuxt/tsconfig.server.json"
2
+ "extends": "../.playground/.nuxt/tsconfig.server.json"
3
3
  }
@@ -0,0 +1,2 @@
1
+ export const getCspAsString = (csp = {} as Record<string, Array<string>>) =>
2
+ Object.keys(csp).reduce((p, c) => (p += `${c} ${csp[c].join(' ')};`), '')
package/store/auth.ts ADDED
@@ -0,0 +1,32 @@
1
+ import { decodeJwt, JWTPayload } from 'jose'
2
+ import { defineStore } from 'pinia'
3
+ import { ref } from 'vue'
4
+
5
+ export const useVioAuthStore = defineStore('vio-auth', () => {
6
+ const jwt = ref<string>()
7
+ const jwtDecoded = ref<JWTPayload>()
8
+ const signedInUsername = ref<string>()
9
+
10
+ const jwtRemove = () => jwtSet(undefined)
11
+
12
+ const jwtSet = (jwtNew?: string) => {
13
+ const jwtDecodedNew = jwtNew !== undefined ? decodeJwt(jwtNew) : undefined
14
+
15
+ jwt.value = jwtNew
16
+ jwtDecoded.value = jwtDecodedNew
17
+ signedInUsername.value =
18
+ jwtDecodedNew?.role === 'vio_account' &&
19
+ jwtDecodedNew.exp !== undefined &&
20
+ jwtDecodedNew.exp > Math.floor(Date.now() / 1000)
21
+ ? (jwtDecodedNew.username as string | undefined)
22
+ : undefined
23
+ }
24
+
25
+ return {
26
+ jwt,
27
+ jwtDecoded,
28
+ signedInUsername,
29
+ jwtRemove,
30
+ jwtSet,
31
+ }
32
+ })
@@ -1,13 +1,15 @@
1
- import colors from 'tailwindcss/colors'
2
- import { PluginAPI } from 'tailwindcss/types/config'
1
+ import formsPlugin from '@tailwindcss/forms'
3
2
  import typographyPlugin from '@tailwindcss/typography'
3
+ import type { Config } from 'tailwindcss'
4
+ import colors from 'tailwindcss/colors'
5
+ import type { PluginAPI } from 'tailwindcss/types/config'
4
6
 
5
- const heading = (theme: PluginAPI['theme']) =>
6
- ({
7
- fontWeight: theme('fontWeight.bold'),
8
- overflow: 'hidden',
9
- textOverflow: 'ellipsis',
10
- }) as Record<string, string>
7
+ const heading = (theme: PluginAPI['theme']): Record<string, string> => ({
8
+ fontWeight: theme('fontWeight.bold'),
9
+ // marginBottom: theme('margin.1'),
10
+ // marginTop: theme('margin.4'),
11
+ // set overflow truncate/ellipsis in the surrounding container, or larger fonts will be cut off due to their line-heights
12
+ })
11
13
 
12
14
  const gray = colors.gray // or slate, zinc, neutral, stone
13
15
 
@@ -39,14 +41,42 @@ const prose = (theme: PluginAPI['theme']) => ({
39
41
  })
40
42
 
41
43
  export default {
44
+ content: [
45
+ './components/**/*.{js,vue,ts}',
46
+ './composables/**/*.{js,vue,ts}',
47
+ './layouts/**/*.vue',
48
+ './pages/**/*.vue',
49
+ './plugins/**/*.{js,ts}',
50
+ // './nuxt.config.{js,ts}', // Does not work with i18n as of 2022-12-01
51
+ './app.vue',
52
+ ],
42
53
  darkMode: 'class',
43
54
  plugins: [
55
+ formsPlugin,
44
56
  typographyPlugin,
45
- ({ addBase, addComponents, theme }: PluginAPI) => {
57
+ ({ addBase, addComponents, addUtilities, theme }: PluginAPI) => {
46
58
  addBase({
59
+ ':disabled': {
60
+ cursor: theme('cursor.not-allowed'),
61
+ opacity: theme('opacity.50'),
62
+ },
63
+ 'a[target="_blank"]:after': {
64
+ backgroundColor: 'currentColor',
65
+ content: '""',
66
+ display: 'inline-table', // inline-table centers the element vertically in the tiptap text area, instead of inline-block
67
+ mask: 'url(data:image/svg+xml;base64,PHN2ZyBhcmlhLWhpZGRlbj0idHJ1ZSIgZm9jdXNhYmxlPSJmYWxzZSIgZGF0YS1wcmVmaXg9ImZhcyIgZGF0YS1pY29uPSJhcnJvdy11cC1yaWdodC1mcm9tLXNxdWFyZSIgY2xhc3M9InN2Zy1pbmxpbmUtLWZhIGZhLWFycm93LXVwLXJpZ2h0LWZyb20tc3F1YXJlIiByb2xlPSJpbWciIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgdmlld0JveD0iMCAwIDUxMiA1MTIiPjxwYXRoIGZpbGw9ImN1cnJlbnRDb2xvciIgZD0iTTM4NCAzMjBjLTE3LjY3IDAtMzIgMTQuMzMtMzIgMzJ2OTZINjRWMTYwaDk2YzE3LjY3IDAgMzItMTQuMzIgMzItMzJzLTE0LjMzLTMyLTMyLTMyTDY0IDk2Yy0zNS4zNSAwLTY0IDI4LjY1LTY0IDY0VjQ0OGMwIDM1LjM0IDI4LjY1IDY0IDY0IDY0aDI4OGMzNS4zNSAwIDY0LTI4LjY2IDY0LTY0di05NkM0MTYgMzM0LjMgNDAxLjcgMzIwIDM4NCAzMjB6TTUwMi42IDkuMzY3QzQ5Ni44IDMuNTc4IDQ4OC44IDAgNDgwIDBoLTE2MGMtMTcuNjcgMC0zMS4xIDE0LjMyLTMxLjEgMzEuMWMwIDE3LjY3IDE0LjMyIDMxLjEgMzEuOTkgMzEuMWg4Mi43NUwxNzguNyAyOTAuN2MtMTIuNSAxMi41LTEyLjUgMzIuNzYgMCA0NS4yNkMxOTEuMiAzNDguNSAyMTEuNSAzNDguNSAyMjQgMzM2bDIyNC0yMjYuOFYxOTJjMCAxNy42NyAxNC4zMyAzMS4xIDMxLjEgMzEuMVM1MTIgMjA5LjcgNTEyIDE5MlYzMS4xQzUxMiAyMy4xNiA1MDguNCAxNS4xNiA1MDIuNiA5LjM2N3oiPjwvcGF0aD48L3N2Zz4K) no-repeat 50% 50%',
68
+ maskSize: 'cover',
69
+ height: theme('fontSize.xs'),
70
+ marginLeft: '5px',
71
+ width: theme('fontSize.xs'),
72
+ },
73
+ address: {
74
+ margin: theme('margin.4'),
75
+ },
47
76
  h1: {
48
77
  ...heading(theme),
49
78
  fontSize: theme('fontSize.4xl'),
79
+ // marginBottom: theme('margin.4'),
50
80
  textAlign: 'center',
51
81
  },
52
82
  h2: {
@@ -70,14 +100,77 @@ export default {
70
100
  },
71
101
  })
72
102
  addComponents({
103
+ '::placeholder': {
104
+ fontStyle: 'italic',
105
+ '.form-input&,.form-textarea&': {
106
+ opacity: '0.5',
107
+ },
108
+ },
109
+ '.form-input': {
110
+ appearance: 'none',
111
+ backgroundColor: theme('colors.gray.50'),
112
+ borderColor: theme('colors.gray.300'),
113
+ borderRadius: theme('borderRadius.DEFAULT'),
114
+ borderWidth: theme('borderWidth.DEFAULT'),
115
+ boxShadow: theme('boxShadow.sm'),
116
+ color: theme('colors.text.dark'),
117
+ lineHeight: theme('lineHeight.tight'),
118
+ padding: theme('padding.2') + ' ' + theme('padding.4'),
119
+ width: theme('width.full'),
120
+ '&:focus': {
121
+ backgroundColor: theme('colors.white'),
122
+ },
123
+ },
124
+ '.form-input-error': {
125
+ input: {
126
+ borderColor: theme('colors.red.500'),
127
+ },
128
+ },
129
+ '.form-input-success': {
130
+ input: {
131
+ borderColor: theme('colors.green.600'),
132
+ },
133
+ },
134
+ '.form-input-warning': {
135
+ input: {
136
+ borderColor: theme('colors.yellow.600'),
137
+ },
138
+ },
139
+ '.fullscreen': {
140
+ bottom: '0',
141
+ height: theme('height.full'),
142
+ left: '0',
143
+ position: 'absolute',
144
+ right: '0',
145
+ top: '0',
146
+ width: theme('width.full'),
147
+ },
73
148
  '.object-position-custom': {
74
149
  objectPosition: '50% 30%',
75
150
  },
76
151
  })
152
+ addUtilities({
153
+ '.disabled': {
154
+ cursor: theme('cursor.not-allowed'),
155
+ opacity: theme('opacity.50'),
156
+ },
157
+ '.max-w-xxs': {
158
+ maxWidth: '15rem',
159
+ },
160
+ '.min-w-xxs': {
161
+ minWidth: '15rem',
162
+ },
163
+ '.mb-20vh': {
164
+ marginBottom: '20vh',
165
+ },
166
+ })
77
167
  },
78
168
  ],
79
169
  theme: {
80
170
  extend: {
171
+ animation: {
172
+ shake: 'shake 0.6s ease-in-out 0s 1 normal forwards running',
173
+ },
81
174
  colors: {
82
175
  background: {
83
176
  bright: colors.white,
@@ -94,6 +187,34 @@ export default {
94
187
  dark: gray['900'],
95
188
  },
96
189
  },
190
+ keyframes: {
191
+ shake: {
192
+ '0%': {
193
+ transform: 'translateX(0)',
194
+ },
195
+ '15%': {
196
+ transform: 'translateX(0.375rem)',
197
+ },
198
+ '30%': {
199
+ transform: 'translateX(-0.375rem)',
200
+ },
201
+ '45%': {
202
+ transform: 'translateX(0.375rem)',
203
+ },
204
+ '60%': {
205
+ transform: 'translateX(-0.375rem)',
206
+ },
207
+ '75%': {
208
+ transform: 'translateX(0.375rem)',
209
+ },
210
+ '90%': {
211
+ transform: 'translateX(-0.375rem)',
212
+ },
213
+ '100%': {
214
+ transform: 'translateX(0)',
215
+ },
216
+ },
217
+ },
97
218
  screens: {
98
219
  12: { raw: '(min-aspect-ratio: 2/1)' },
99
220
  },
@@ -107,4 +228,4 @@ export default {
107
228
  }),
108
229
  },
109
230
  },
110
- }
231
+ } as Config
package/types/api.d.ts ADDED
@@ -0,0 +1,9 @@
1
+ import type { CombinedError } from '@urql/core'
2
+
3
+ export type ApiData = ComputedRef<{
4
+ data?: Object
5
+ errors: BackendError[]
6
+ isFetching: boolean
7
+ }>
8
+
9
+ export type BackendError = CombinedError | { errcode: string; message: string }
@@ -0,0 +1,8 @@
1
+ export interface StrapiResult<T> {
2
+ data: CollectionItem<T>[]
3
+ meta: {
4
+ pagination: {
5
+ total: number
6
+ }
7
+ }
8
+ }
@@ -0,0 +1,6 @@
1
+ declare module '*.gql' {
2
+ import { DocumentNode } from 'graphql'
3
+
4
+ const content: DocumentNode
5
+ export default content
6
+ }
@@ -0,0 +1,6 @@
1
+ declare module '*.graphql' {
2
+ import { DocumentNode } from 'graphql'
3
+
4
+ const content: DocumentNode
5
+ export default content
6
+ }
@@ -1,3 +1,10 @@
1
+ export const SITE_NAME = 'Vio'
2
+
3
+ export const CACHE_VERSION = 'bOXMwoKlJr'
4
+ export const COOKIE_PREFIX = SITE_NAME.toLocaleLowerCase()
5
+ export const COOKIE_SEPARATOR = '_'
6
+ export const FETCH_RETRY_AMOUNT = 3
7
+ export const I18N_COOKIE_NAME = 'i18n_r'
1
8
  export const I18N_MODULE_CONFIG = {
2
9
  langDir: 'locales',
3
10
  lazy: true,
@@ -20,4 +27,6 @@ export const I18N_VUE_CONFIG = {
20
27
  fallbackWarn: false, // covered by linting
21
28
  missingWarn: false, // covered by linting
22
29
  }
23
- export const SITE_NAME = 'Vio'
30
+ export const TIMEZONE_COOKIE_NAME = [COOKIE_PREFIX, 'tz'].join(COOKIE_SEPARATOR)
31
+ export const TIMEZONE_HEADER_KEY = `X-${SITE_NAME}-Timezone`
32
+ export const VALIDATION_SUGGESTION_TITLE_LENGTH_MAXIMUM = 300