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

Sign up to get free protection for your applications and to get access to all the features.
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