create-nuxt-base 0.1.23 → 0.2.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.
Files changed (72) hide show
  1. package/.eslintignore +14 -0
  2. package/.eslintrc +3 -0
  3. package/.github/workflows/release.yml +1 -1
  4. package/.prettierignore +5 -0
  5. package/.prettierrc +6 -0
  6. package/.vscode/settings.json +10 -10
  7. package/CHANGELOG.md +20 -33
  8. package/README.md +1 -0
  9. package/index.js +30 -29
  10. package/nuxt-base-template/.env.example +8 -0
  11. package/nuxt-base-template/.eslintrc +1 -1
  12. package/nuxt-base-template/.vscode/settings.json +6 -16
  13. package/nuxt-base-template/nuxt.config.ts +27 -12
  14. package/nuxt-base-template/package-lock.json +5772 -4389
  15. package/nuxt-base-template/package.json +45 -29
  16. package/nuxt-base-template/src/app.vue +4 -4
  17. package/nuxt-base-template/src/assets/css/tailwind.css +42 -33
  18. package/nuxt-base-template/src/components/SocialMediaBubble.vue +1 -1
  19. package/nuxt-base-template/src/components/base/BaseAccordion.vue +16 -13
  20. package/nuxt-base-template/src/components/base/BaseButton.vue +10 -13
  21. package/nuxt-base-template/src/components/base/BaseContainer.vue +1 -1
  22. package/nuxt-base-template/src/components/base/BaseContextMenuContainer.vue +61 -0
  23. package/nuxt-base-template/src/components/base/BaseInfinityList.vue +1 -1
  24. package/nuxt-base-template/src/components/base/BaseProgressbar.vue +28 -30
  25. package/nuxt-base-template/src/components/base/BaseToggle.vue +17 -10
  26. package/nuxt-base-template/src/components/form/FormInput.vue +34 -0
  27. package/nuxt-base-template/src/components/form/FormPassword.vue +47 -0
  28. package/nuxt-base-template/src/components/form/FormSelect.vue +40 -0
  29. package/nuxt-base-template/src/components/form/FormSubmit.vue +18 -0
  30. package/nuxt-base-template/src/components/form/FormTextarea.vue +36 -0
  31. package/nuxt-base-template/src/components/form/FormToggle.vue +27 -0
  32. package/nuxt-base-template/src/components/modal/Modal.vue +48 -0
  33. package/nuxt-base-template/src/components/modal/ModalConfirm.vue +38 -0
  34. package/nuxt-base-template/src/components/{base/BaseModalContainer.vue → modal/ModalContainer.vue} +1 -1
  35. package/nuxt-base-template/src/components/modal/ModalInfo.vue +22 -0
  36. package/nuxt-base-template/src/components/{ModalShare.vue → modal/ModalShare.vue} +7 -11
  37. package/nuxt-base-template/src/components/{base/BaseNotification.vue → notification/Notification.vue} +8 -7
  38. package/nuxt-base-template/src/components/{base/BaseNotificationContainer.vue → notification/NotificationContainer.vue} +11 -5
  39. package/nuxt-base-template/src/components/pwa/pwa-install-banner.vue +224 -0
  40. package/nuxt-base-template/src/components/transition/TransitionFade.vue +10 -7
  41. package/nuxt-base-template/src/components/transition/TransitionFadeScale.vue +10 -7
  42. package/nuxt-base-template/src/composables/use-auth-fetch.ts +22 -8
  43. package/nuxt-base-template/src/composables/use-context-menu.ts +19 -0
  44. package/nuxt-base-template/src/composables/use-file.ts +1 -2
  45. package/nuxt-base-template/src/composables/use-modal.ts +1 -1
  46. package/nuxt-base-template/src/composables/use-notification.ts +2 -2
  47. package/nuxt-base-template/src/composables/use-share.ts +3 -3
  48. package/nuxt-base-template/src/error.vue +10 -14
  49. package/nuxt-base-template/src/layouts/default.vue +13 -0
  50. package/nuxt-base-template/src/middleware/auth.global.ts +2 -2
  51. package/nuxt-base-template/src/pages/index.vue +31 -6
  52. package/nuxt-base-template/src/plugins/auth.server.ts +72 -0
  53. package/nuxt-base-template/src/plugins/form.plugin.ts +21 -0
  54. package/nuxt-base-template/src/plugins/pwa.plugin.ts +110 -0
  55. package/nuxt-base-template/src/tests/init.test.ts +1 -1
  56. package/nuxt-base-template/tailwind.config.js +33 -23
  57. package/nuxt-base-template/vitest.config.js +3 -3
  58. package/package.json +3 -1
  59. package/nuxt-base-template/formkit-theme.js +0 -137
  60. package/nuxt-base-template/formkit.config.js +0 -33
  61. package/nuxt-base-template/src/components/hello-world.vue +0 -10
  62. package/nuxt-base-template/src/composables/use-form-helper.ts +0 -100
  63. package/nuxt-base-template/src/composables/use-helper.ts +0 -52
  64. package/nuxt-base-template/src/forms/inputs/InputCheckbox.vue +0 -29
  65. package/nuxt-base-template/src/forms/inputs/InputFreeTags.vue +0 -98
  66. package/nuxt-base-template/src/forms/inputs/InputImage.vue +0 -65
  67. package/nuxt-base-template/src/forms/inputs/InputTags.vue +0 -112
  68. package/nuxt-base-template/src/forms/inputs/InputToggle.vue +0 -18
  69. package/nuxt-base-template/src/forms/plugins/asterisk-plugin.ts +0 -29
  70. package/nuxt-base-template/src/forms/plugins/scroll-error-plugin.ts +0 -36
  71. package/nuxt-base-template/src/forms/plugins/value-changes-plugin.ts +0 -13
  72. package/nuxt-base-template/src/plugins/4.auth.server.ts +0 -70
@@ -18,7 +18,7 @@ export function useNotification() {
18
18
  };
19
19
 
20
20
  const remove = (uuid: string) => {
21
- notifications.value = notifications.value.filter(n => n.uuid !== uuid);
21
+ notifications.value = notifications.value.filter((n) => n.uuid !== uuid);
22
22
  };
23
23
 
24
24
  return {
@@ -26,4 +26,4 @@ export function useNotification() {
26
26
  remove,
27
27
  notifications,
28
28
  };
29
- }
29
+ }
@@ -1,5 +1,5 @@
1
- import { ModalShare } from '#components';
2
1
  import { useModal } from '~/composables/use-modal';
2
+ import ModalShare from '~/components/ModalShare.vue';
3
3
 
4
4
  export function useShare() {
5
5
  const route = useRoute();
@@ -12,11 +12,11 @@ export function useShare() {
12
12
  text: text ?? window.location.origin,
13
13
  });
14
14
  } else {
15
- useModal().open({ component: ModalShare, size: 'md', data: { link: url ?? window.location.origin } });
15
+ useModal().open({ component: ModalShare, size: 'md', data: { link: url ?? window.location.origin, name: window.name } });
16
16
  }
17
17
  }
18
18
 
19
19
  return {
20
20
  share,
21
21
  };
22
- }
22
+ }
@@ -7,7 +7,8 @@ console.error(props.error);
7
7
  const debugWord = ref<string>('');
8
8
  const { current } = useMagicKeys();
9
9
 
10
- watch(() => current.values(),
10
+ watch(
11
+ () => current.values(),
11
12
  () => {
12
13
  if (current.values().next().value === 'escape') {
13
14
  debugWord.value = '';
@@ -21,7 +22,7 @@ watch(() => current.values(),
21
22
  if (current.values().next().value && !debugWord.value.includes(current.values().next().value)) {
22
23
  debugWord.value += current.values().next().value;
23
24
  }
24
- },
25
+ }
25
26
  );
26
27
 
27
28
  const handleError = () => clearError({ redirect: '/' });
@@ -31,23 +32,18 @@ const handleError = () => clearError({ redirect: '/' });
31
32
  <NuxtLayout>
32
33
  <div class="w-full min-h-screen flex flex-col justify-center items-center">
33
34
  <div class="w-full flex flex-col items-center mb-20">
34
- <h1 class="text-[12rem] font-extrabold text-transparent bg-clip-text bg-gradient-to-r from-primary-600 to-primary-400">
35
- Oops!
36
- </h1>
35
+ <h1 class="text-[12rem] font-extrabold text-transparent bg-clip-text bg-gradient-to-r from-primary-600 to-primary-400">Oops!</h1>
37
36
  <h2 v-if="error?.statusCode" class="text-3xl text-gray-600">
38
37
  {{ error?.statusCode }}
39
38
  </h2>
40
39
  </div>
41
40
 
42
41
  <pre v-if="debugWord === 'debug'" class="w-full max-w-3xl mb-5 mx-auto bg-black/80 text-white font-mono p-2 rounded-lg overflow-x-scroll">
43
- {{
44
- { error }
45
- }}
46
- </pre>
47
-
48
- <BaseButton color="primary" appearance="outline" @click="handleError">
49
- Zurück zur Startseite
50
- </BaseButton>
42
+ {{ { error } }}
43
+ </pre
44
+ >
45
+
46
+ <BaseButton color="primary" appearance="outline" @click="handleError"> Zurück zur Startseite </BaseButton>
51
47
  </div>
52
48
  </NuxtLayout>
53
- </template>
49
+ </template>
@@ -0,0 +1,13 @@
1
+ <script setup lang="ts">
2
+ import { useGlobalMutationLoading, useGlobalQueryLoading } from '@vue/apollo-composable';
3
+
4
+ const queryLoading = useGlobalQueryLoading();
5
+ const mutationLoading = useGlobalMutationLoading();
6
+ </script>
7
+
8
+ <template>
9
+ <div>
10
+ <NuxtLoadingIndicator :loading="queryLoading || mutationLoading" />
11
+ <slot></slot>
12
+ </div>
13
+ </template>
@@ -1,8 +1,8 @@
1
1
  export default defineNuxtRouteMiddleware((to, from) => {
2
- const { accessToken } = useAuthState();
2
+ const { accessTokenState } = useAuthState();
3
3
 
4
4
  if (to.fullPath.startsWith('/app')) {
5
- if (!accessToken?.value) {
5
+ if (!accessTokenState?.value) {
6
6
  return navigateTo('/auth/login');
7
7
  }
8
8
  }
@@ -1,10 +1,35 @@
1
+ <script setup lang="ts">
2
+ import FormTextarea from '~/components/form/FormTextarea.vue';
3
+
4
+ const isSubmitting = ref(false);
5
+ </script>
6
+
1
7
  <template>
2
8
  <div class="flex h-screen items-center justify-center flex-col">
3
- <h1
4
- class="font-extrabold text-transparent text-8xl bg-clip-text bg-gradient-to-r from-purple-400 to-pink-600"
5
- >
6
- Lenne Nuxt Starter
7
- </h1>
8
- <HelloWorld />
9
+ <h1 class="font-extrabold text-transparent text-8xl bg-clip-text bg-gradient-to-r from-purple-400 to-pink-600">Lenne Nuxt Starter</h1>
10
+
11
+ <h2 class="mt-5">DEMO</h2>
12
+ <FormInput name="email" type="email" label="E-Mail" placeholder="max.mustermann@test.de" />
13
+ <FormTextarea name="description" label="Description" placeholder="..." />
14
+ <FormPassword name="password" label="Password" placeholder="Password" />
15
+ <FormSelect
16
+ name="select"
17
+ label="Select"
18
+ placeholder="placeholder"
19
+ :options="[
20
+ { label: 'Option 1', value: 1 },
21
+ { label: 'Option 2', value: 2 },
22
+ { label: 'Option 3', value: 3 },
23
+ ]"
24
+ />
25
+ <FormToggle name="toggle" label="Toggle" />
26
+ <div class="flex items-center gap-3">
27
+ <FormSubmit label="Save" :is-submitting="isSubmitting" @click="isSubmitting = true" />
28
+ <BaseButton size="sm" :loading="isSubmitting" @click="isSubmitting = true">Submit</BaseButton>
29
+ <BaseButton size="lg" :loading="isSubmitting" @click="isSubmitting = true">Submit</BaseButton>
30
+ <BaseButton size="md" :loading="isSubmitting" appearance="outline" @click="isSubmitting = true">Submit</BaseButton>
31
+ <BaseButton size="md" :loading="isSubmitting" appearance="none" @click="isSubmitting = true">Submit</BaseButton>
32
+ <BaseButton size="md" color="danger" @click="isSubmitting = false">Reset</BaseButton>
33
+ </div>
9
34
  </div>
10
35
  </template>
@@ -0,0 +1,72 @@
1
+ import { callWithNuxt, defineNuxtPlugin, useNuxtApp, useRuntimeConfig } from 'nuxt/app';
2
+ import { ofetch } from 'ofetch';
3
+
4
+ export default defineNuxtPlugin({
5
+ enforce: 'post',
6
+ name: 'auth-server',
7
+ async setup() {
8
+ const _nuxt = useNuxtApp();
9
+ const config = await callWithNuxt(_nuxt, useRuntimeConfig);
10
+ const { accessTokenState, currentUserState, refreshTokenState } = await callWithNuxt(_nuxt, useAuthState);
11
+ const { clearSession, getDecodedAccessToken, isTokenExpired, setCurrentUser, setTokens } = await callWithNuxt(_nuxt, useAuth);
12
+ const payload = accessTokenState.value ? getDecodedAccessToken(accessTokenState.value) : null;
13
+
14
+ if (!accessTokenState.value || !refreshTokenState.value || currentUserState.value) {
15
+ return;
16
+ }
17
+
18
+ let token = accessTokenState.value;
19
+ if (isTokenExpired(accessTokenState.value)) {
20
+ const refreshTokenResult = await ofetch(config.public.host, {
21
+ body: JSON.stringify({
22
+ query: 'mutation refreshToken {refreshToken {token, refreshToken}}',
23
+ variables: {},
24
+ }),
25
+ headers: {
26
+ Authorization: `Bearer ${refreshTokenState.value}`,
27
+ },
28
+ method: 'POST',
29
+ }).catch((err) => {
30
+ console.error('2.auth.server.ts::refreshToken::catch', err.data);
31
+ clearSession();
32
+ navigateTo('/auth/login');
33
+ });
34
+
35
+ const data = refreshTokenResult?.data?.refreshToken;
36
+ if (data) {
37
+ setTokens(data.token, data.refreshToken);
38
+ token = data?.token;
39
+ } else {
40
+ clearSession();
41
+ await navigateTo('/auth/login');
42
+ }
43
+ }
44
+
45
+ if (token && payload?.id) {
46
+ const userResult = await ofetch(config.public.host, {
47
+ body: JSON.stringify({
48
+ query: 'query getUser($id: String!){' + 'getUser(id: $id){' + 'id ' + 'avatar ' + 'firstName ' + 'lastName ' + 'email ' + 'gender ' + 'roles ' + '}}',
49
+ variables: {
50
+ id: payload.id,
51
+ },
52
+ }),
53
+ headers: {
54
+ Authorization: `Bearer ${token}`,
55
+ },
56
+ method: 'POST',
57
+ }).catch((err) => {
58
+ console.error('2.auth.server.ts::getUser::catch', err);
59
+ });
60
+
61
+ if (userResult?.errors) {
62
+ clearSession();
63
+ navigateTo('/auth/login');
64
+ return;
65
+ }
66
+
67
+ if (userResult?.data) {
68
+ setCurrentUser(userResult?.data?.getUser);
69
+ }
70
+ }
71
+ },
72
+ });
@@ -0,0 +1,21 @@
1
+ import { setLocale } from 'yup';
2
+
3
+ export default defineNuxtPlugin(async (_nuxtApp) => {
4
+ setLocale({
5
+ // use constant translation keys for messages without values
6
+ mixed: {
7
+ required: 'Dieses Feld ist erforderlich.',
8
+ default: 'Dieses Feld ist ungültig.',
9
+ notType: 'Dieses Feld ist ungültig.',
10
+ },
11
+ string: {
12
+ email: 'Bitte geben Sie eine gültige E-Mail-Adresse ein.',
13
+ },
14
+ });
15
+
16
+ // Custom validation rules
17
+ // https://github.com/jquense/yup?tab=readme-ov-file#addmethodschematype-schema-name-string-method--schema-void
18
+ // addMethod(string, 'plz', () => {
19
+ // return
20
+ // });
21
+ });
@@ -0,0 +1,110 @@
1
+ export default defineNuxtPlugin(async (_nuxtApp) => {
2
+ const applicationServerKey = useRuntimeConfig().public.webPushKey as string;
3
+ const permissionState = ref<PermissionState>('prompt');
4
+ const subscription = ref<PushSubscription | null>(null);
5
+
6
+ if (process.client) {
7
+ const iosPWASplash = (await import('ios-pwa-splash')).default;
8
+ iosPWASplash('/notification.png', '#FFFFFF');
9
+ }
10
+
11
+ async function subscribe() {
12
+ const sw = await navigator.serviceWorker.ready;
13
+ const push = await sw.pushManager.subscribe({
14
+ applicationServerKey,
15
+ userVisibleOnly: true,
16
+ });
17
+ await refreshSubscription();
18
+ await refreshPermissionState();
19
+ const body = {
20
+ payload: push,
21
+ };
22
+ try {
23
+ await useAuthFetch('/web-push/subscribe', {
24
+ baseURL: process.env.API_URL,
25
+ body,
26
+ method: 'POST',
27
+ });
28
+ } catch (error) {}
29
+ }
30
+
31
+ async function refreshPermissionState() {
32
+ try {
33
+ const sw = await navigator.serviceWorker.ready;
34
+ permissionState.value = await sw.pushManager.permissionState({
35
+ applicationServerKey,
36
+ userVisibleOnly: true,
37
+ });
38
+ } catch (error) {
39
+ console.error(error);
40
+ }
41
+ }
42
+
43
+ async function refreshSubscription() {
44
+ try {
45
+ const sw = await navigator.serviceWorker.ready;
46
+ subscription.value = await sw.pushManager.getSubscription();
47
+ } catch (e) {
48
+ console.error(e);
49
+ }
50
+ }
51
+
52
+ async function unsubscribe() {
53
+ if (subscription.value) {
54
+ await subscription.value.unsubscribe();
55
+ const body = {
56
+ payload: subscription.value,
57
+ };
58
+ await refreshPermissionState();
59
+ try {
60
+ await useAuthFetch('/web-push/', {
61
+ baseURL: process.env.API_URL,
62
+ body,
63
+ method: 'DELETE',
64
+ });
65
+ } catch (error) {}
66
+ subscription.value = null;
67
+ }
68
+ }
69
+
70
+ const route = useRoute();
71
+ const pwa = ref(false);
72
+ const isPwa = computed(() => route.query.standalone === 'true' || pwa.value);
73
+
74
+ if (process.client) {
75
+ if (window && window.matchMedia && document && window.matchMedia('(display-mode: standalone)').matches) {
76
+ if (navigator && navigator.serviceWorker) {
77
+ await refreshSubscription();
78
+ await refreshPermissionState();
79
+ }
80
+ pwa.value = true;
81
+ document.body.classList.add('select-none', 'overscroll-y-none', 'no-scrollbar', 'bg-rm-gray-1');
82
+ } else {
83
+ pwa.value = false;
84
+ document.body.classList.remove('select-none', 'overscroll-y-none', 'no-scrollbar', 'bg-rm-gray-1');
85
+ }
86
+ }
87
+
88
+ useHead({
89
+ htmlAttrs: { class: isPwa.value ? 'pwa' : '' },
90
+ meta: [
91
+ {
92
+ content: 'width=device-width, initial-scale=1, viewport-fit=cover, user-scalable=no',
93
+ hid: 'viewport',
94
+ name: 'viewport',
95
+ },
96
+ ],
97
+ });
98
+
99
+ return {
100
+ provide: {
101
+ pwa: {
102
+ isPwa: () => isPwa.value,
103
+ permissionState,
104
+ subscribe,
105
+ subscription,
106
+ unsubscribe,
107
+ },
108
+ },
109
+ };
110
+ });
@@ -9,4 +9,4 @@ describe('Initialization', async () => {
9
9
  test('Init test', () => {
10
10
  // ... write tests here
11
11
  });
12
- });
12
+ });
@@ -3,11 +3,10 @@ const defaultTheme = require('tailwindcss/defaultTheme');
3
3
  const { iconsPlugin, getIconCollections } = require('@egoist/tailwindcss-icons');
4
4
  const typography = require('@tailwindcss/typography');
5
5
  const forms = require('@tailwindcss/forms');
6
- const formkit = require('@formkit/themes/tailwindcss');
6
+ const plugin = require('tailwindcss/plugin');
7
7
 
8
8
  module.exports = {
9
- darkMode: "class",
10
- content: ['./formkit-theme.js'],
9
+ darkMode: 'class',
11
10
  theme: {
12
11
  // fontFamily: {
13
12
  // montserrat: ['Montserrat'],
@@ -15,34 +14,45 @@ module.exports = {
15
14
  // serif: ['Work Sans', 'Montserrat', ...defaultTheme.fontFamily.serif],
16
15
  // },
17
16
  extend: {
18
- // colors: {
19
- // primary: {
20
- // DEFAULT: '#57B39A',
21
- // 50: '#f3faf7',
22
- // 100: '#d6f1e7',
23
- // 200: '#ade2d0',
24
- // 300: '#7cccb3',
25
- // 400: '#57b39a',
26
- // 500: '#37957d',
27
- // 600: '#2a7765',
28
- // 700: '#256052',
29
- // 800: '#224d45',
30
- // 900: '#20413a',
31
- // 950: '#0d2621',
32
- // },
33
- // }
17
+ colors: {
18
+ primary: {
19
+ DEFAULT: '#57B39A',
20
+ 50: '#f3faf7',
21
+ 100: '#d6f1e7',
22
+ 200: '#ade2d0',
23
+ 300: '#7cccb3',
24
+ 400: '#57b39a',
25
+ 500: '#37957d',
26
+ 600: '#2a7765',
27
+ 700: '#256052',
28
+ 800: '#224d45',
29
+ 900: '#20413a',
30
+ 950: '#0d2621',
31
+ },
32
+ background: '#FFFFFF',
33
+ foreground: '#000000',
34
+ border: 'hsl(0 0% 0% / 0.5)',
35
+ hover: 'hsl(0 0% 100% / 0.2)',
36
+ active: 'hsl(0 0% 100% / 0.2)',
37
+ },
34
38
  screens: {
35
- '3xl': '2400px'
36
- }
37
- }
39
+ '3xl': '2400px',
40
+ },
41
+ },
38
42
  },
39
43
  plugins: [
40
44
  typography,
41
- formkit,
42
45
  forms,
43
46
  iconsPlugin({
44
47
  // Select the icon collections you want to use
45
48
  collections: getIconCollections(['bi']),
46
49
  }),
50
+ plugin(({ addVariant, e }) => {
51
+ addVariant('pwa', ({ modifySelectors, separator }) => {
52
+ modifySelectors(({ className }) => {
53
+ return `.pwa .${e(`pwa${separator}${className}`)}`;
54
+ });
55
+ });
56
+ }),
47
57
  ],
48
58
  };
@@ -1,5 +1,5 @@
1
- import vue from '@vitejs/plugin-vue'
2
- import { defineConfig } from 'vite'
1
+ import vue from '@vitejs/plugin-vue';
2
+ import { defineConfig } from 'vite';
3
3
 
4
4
  export default defineConfig({
5
5
  plugins: [vue()],
@@ -7,4 +7,4 @@ export default defineConfig({
7
7
  globals: true,
8
8
  environment: 'jsdom',
9
9
  },
10
- })
10
+ });
package/package.json CHANGED
@@ -1,12 +1,14 @@
1
1
  {
2
2
  "name": "create-nuxt-base",
3
- "version": "0.1.23",
3
+ "version": "0.2.0",
4
4
  "description": "Starter to generate a configured environment with VueJS, Nuxt, Tailwind, Eslint, @lenne.tech/nuxt-base, Unit Tests, Cypress etc.",
5
5
  "main": "index.js",
6
6
  "bin": {
7
7
  "create-nuxt-base": "./index.js"
8
8
  },
9
9
  "scripts": {
10
+ "prettier": "prettier \"*.{js,json,yml,md,html,ts}\" .",
11
+ "format": "npm run prettier -- --write",
10
12
  "release": "standard-version && git push --follow-tags origin main",
11
13
  "release:minor": "standard-version --release-as minor && git push --follow-tags origin main",
12
14
  "release:major": "standard-version --release-as major && git push --follow-tags origin main"
@@ -1,137 +0,0 @@
1
- export default {
2
- global: {
3
- fieldset: 'max-w-md border border-gray-400 rounded px-2 pb-1',
4
- help: 'text-xs text-gray-500 mt-1',
5
- inner: 'formkit-disabled:bg-gray-200 formkit-disabled:dark:bg-gray-200 formkit-disabled:cursor-not-allowed formkit-disabled:pointer-events-none rounded-[25px] bg-white formkit-invalid:ring-red',
6
- input: 'appearance-none bg-transparent focus:outline-none focus:ring-0 focus:shadow-none dark:text-white',
7
- label: 'block mb-1 text-sm text-gray-600 dark:text-white font-semibold text-left',
8
- legend: 'font-bold text-sm',
9
- loaderIcon: 'inline-flex items-center w-4 text-gray-600 animate-spin',
10
- message: 'text-red-500 mb-1 text-xs',
11
- messages: 'list-none p-0 mt-1 mb-0 text-left',
12
- outer: 'mb-4 formkit-disabled:opacity-50',
13
- prefixIcon: 'w-10 flex self-stretch grow-0 shrink-0 rounded-tl rounded-bl border-r border-gray-400 bg-white bg-gradient-to-b from-transparent to-gray-200 [&>svg]:w-full [&>svg]:max-w-[1em] [&>svg]:max-h-[1em] [&>svg]:m-auto',
14
- suffixIcon: 'w-7 pr-3 flex self-stretch grow-0 shrink-0 [&>svg]:w-full [&>svg]:max-w-[1em] [&>svg]:max-h-[1em] [&>svg]:m-auto'
15
- },
16
- // Family styles apply to all inputs that share a common family
17
- 'family:box': {
18
- decorator: 'block relative h-3 w-3 mr-2 rounded-full bg-white ring-1 ring-gray-600 formkit-invalid:ring-red peer-checked:ring-primary-500 text-transparent peer-checked:bg-primary-500',
19
- decoratorIcon: 'flex p-[3px] w-full h-full absolute top-1/2 left-1/2 -translate-y-1/2 -translate-x-1/2',
20
- help: 'mb-2 mt-1.5',
21
- input: 'absolute w-0 h-0 overflow-hidden opacity-0 pointer-events-none peer',
22
- label: '$reset text-sm text-gray-700 mt-1 select-none ps-1',
23
- wrapper: 'flex items-start mb-1',
24
- inner: '!bg-transparent mt-2 mx-auto',
25
- },
26
- 'family:button': {
27
- inner: 'bg-none',
28
- input: '$reset inline-flex items-center justify-center bg-primary-400 hover:bg-primary-500 transition-all duration-200 text-white dark:text-gray-100 text-base font-semibold py-2 px-6 rounded-[25px] focus-visible:outline-2 focus-visible:outline-primary-600 focus-visible:outline-offset-2 formkit-disabled:bg-gray-400 formkit-loading:before:w-4 formkit-loading:before:h-4 formkit-loading:before:mr-2 formkit-loading:before:border formkit-loading:before:border-2 formkit-loading:before:border-r-transparent formkit-loading:before:rounded-[25px] formkit-loading:before:border-white formkit-loading:before:animate-spin min-w-[200px] py-2 px-3 text-base',
29
- wrapper: 'mb-1',
30
- prefixIcon: '$reset block w-4 -ml-2 mr-2 stretch',
31
- suffixIcon: '$reset block w-4 ml-2 stretch',
32
- },
33
- 'family:dropdown': {
34
- dropdownWrapper: 'my-2 w-full drop-shadow-lg rounded [&::-webkit-scrollbar]:hidden',
35
- emptyMessageInner: 'flex items-center justify-center text-sm p-2 text-center w-full text-gray-500 [&>span]:mr-3 [&>span]:ml-0',
36
- inner: 'max-w-md relative flex ring-1 formkit-invalid:ring-red ring-gray-400 focus-within:ring-primary-500 focus-within:ring-2 rounded mb-1 formkit-disabled:focus-within:ring-gray-400 formkit-disabled:focus-within:ring-1 [&>span:first-child]:focus-within:text-primary-500',
37
- input: 'w-full px-3 py-2',
38
- listbox: 'bg-white drop-shadow-lg rounded overflow-hidden',
39
- listboxButton: 'flex w-12 self-stretch justify-center mx-auto',
40
- listitem: 'pl-7 relative hover:bg-gray-300 data-[is-active="true"]:bg-gray-300 data-[is-active="true"]:aria-selected:bg-primary-600 aria-selected:bg-primary-600 aria-selected:text-white',
41
- loaderIcon: 'ml-auto',
42
- loadMoreInner: 'flex items-center justify-center text-sm p-2 text-center w-full text-primary-500 formkit-loading:text-gray-500 cursor-pointer [&>span]:mr-3 [&>span]:ml-0',
43
- option: 'p-2.5',
44
- optionLoading: 'text-gray-500',
45
- placeholder: 'p-2.5 text-gray-300',
46
- selector: 'flex w-full justify-between items-center [&u]',
47
- selectedIcon: 'block absolute top-1/2 left-2 w-3 -translate-y-1/2',
48
- selectIcon: 'flex box-content w-4 px-2 self-stretch grow-0 shrink-0 dark:text-white',
49
- },
50
- 'family:text': {
51
- inner: 'flex items-center ring-1 ring-gray-400 formkit-invalid:ring-red focus-within:ring-primary-400 focus-within:ring-2 [&>label:first-child]:focus-within:text-primary-500 rounded mb-1',
52
- input: 'w-full px-3 py-2 border-none text-base text-gray-700 placeholder-gray-400',
53
- },
54
- tags: {
55
- tag: 'bg-primary text-white rounded-full px-3 flex items-center',
56
- tagIcon: 'ms-2 text-lg cursor-pointer',
57
- dropdown: 'bg-white ring-2 ring-primary-400 rounded-b-[25px] overflow-hidden',
58
- dropdownItem: 'text-base font-normal py-2 px-3 hover:bg-gray-100 hover:text-primary w-full text-start dark:bg-gray-100 dark:text-white dark:hover:text-primary',
59
- noItemsFound: 'text-base font-normal py-2 px-3 w-full text-start',
60
- inputWrapper: 'formkit-disabled:bg-gray-200 formkit-disabled:cursor-not-allowed formkit-disabled:pointer-events-none rounded-[25px] bg-white flex items-center ring-1 ring-gray-400 formkit-invalid:ring-red focus-within:ring-primary-400 focus-within:ring-2 [&>label:first-child]:focus-within:text-primary-500 focus-within:rounded-b-none mt-2 transition-all duration-200',
61
- input: 'w-full px-3 py-2 border-none text-base text-gray-700 placeholder-gray-400',
62
- selectIcon: 'dark:text-white'
63
- },
64
- freeTags: {
65
- tag: 'border border-primary formkit-invalid:border-red rounded-full px-3 flex items-center text-gray-600',
66
- tagIcon: 'ms-2 text-lg cursor-pointer text-gray-600',
67
- inputWrapper: 'formkit-disabled:bg-gray-200 formkit-disabled:cursor-not-allowed formkit-disabled:pointer-events-none rounded-[25px] bg-white flex items-center ring-1 ring-gray-400 formkit-invalid:ring-red focus-within:ring-primary-400 focus-within:ring-2 [&>label:first-child]:focus-within:text-primary-500 mt-2 transition-all duration-200',
68
- input: 'w-full px-3 py-2 border-none text-base text-gray-700 placeholder-gray-400',
69
- inputIcon: 'i-bi-plus me-2 text-lg text-gray-600',
70
- button: 'bg-primary-500 hover:bg-primary-400 text-primary-50 min-w-[200px] py-2 px-3 text-base rounded-full mt-3 disabled:bg-gray-200 disabled:cursor-not-allowed disabled:text-gray-400',
71
- suggestionsHeadline: 'text-sm text-gray-600 font-semibold mt-5 mb-3',
72
- suggestionsTag: 'border border-primary rounded-full px-3 flex items-center text-gray-600',
73
- suggestionsIcon: 'i-bi-plus text-lg text-gray-600 ms-2 cursor-pointer',
74
- selectIcon: 'dark:text-white'
75
- },
76
- image: {
77
- placeholder: 'dark:text-white',
78
- inner: 'block shadow-none w-full',
79
- uploader: 'relative p-4 text-center w-full h-full flex items-center justify-center',
80
- input: 'absolute top-0 left-0 right-0 bottom-0 opacity-0 cursor-pointer',
81
- fileList: 'p-0',
82
- fileItem: 'rounded-[20px] flex items-center h-full',
83
- fileItemImage: 'w-28 h-full rounded-[20px] object-cover object-center me-4',
84
- fileItemImageName: 'me-4 dark:text-white',
85
- fileItemRemove: 'appearance-none bg-none border-none font-bold shadow-none ms-auto cursor-pointer me-4 hover:text-red-500',
86
- container: 'h-36 border border-dashed formkit-invalid:border-red border-gray-200 rounded-[20px]',
87
- },
88
- // Specific styles apply only to a given input type
89
- color: {
90
- inner: 'flex max-w-[5.5em] w-full formkit-prefix-icon:max-w-[7.5em] formkit-suffix-icon:formkit-prefix-icon:max-w-[10em]',
91
- input: '$reset appearance-none w-full cursor-pointer border-none rounded p-0 m-0 bg-transparent [&::-webkit-color-swatch-wrapper]:p-0 [&::-webkit-color-swatch]:border-none',
92
- suffixIcon: 'min-w-[2.5em] pr-0 pl-0 m-auto'
93
- },
94
- file: {
95
- fileItem: 'flex items-center text-gray-800 mb-1 last:mb-0',
96
- fileItemIcon: 'w-4 mr-2 shrink-0',
97
- fileList: 'shrink grow peer px-3 py-2 formkit-multiple:data-[has-multiple="true"]:mb-6',
98
- fileName: 'break-all grow text-ellipsis',
99
- fileRemove: 'relative z-[2] ml-auto text-[0px] hover:text-red-500 pl-2 peer-data-[has-multiple=true]:text-sm peer-data-[has-multiple=true]:text-primary-500 peer-data-[has-multiple=true]:ml-3 peer-data-[has-multiple=true]:mb-2 formkit-multiple:bottom-[0.15em] formkit-multiple:pl-0 formkit-multiple:ml-0 formkit-multiple:left-[1em] formkit-multiple:formkit-prefix-icon:left-[3.75em]',
100
- fileRemoveIcon: 'block text-base w-3 relative z-[2]',
101
- inner: 'relative cursor-pointer formkit-multiple:[&>button]:absolute',
102
- input: 'cursor-pointer text-transparent absolute top-0 right-0 left-0 bottom-0 opacity-0 z-[2]',
103
- noFiles: 'flex w-full items-center px-3 py-2 text-gray-400',
104
- noFilesIcon: 'w-4 mr-2'
105
- },
106
- radio: {
107
- decorator: '!w-3 !h-3 rounded-full ring-gray-300',
108
- decoratorIcon: 'w-3 !p-[2px]',
109
- fieldset: 'border-none !p-0 !m-0 max-w-none !mt-2',
110
- options: 'flex flex-row gap-5 w-full',
111
- option: '',
112
- icon: 'text-md i-bi-eye dark:text-white',
113
- legend: 'flex items-center',
114
- label: `text-gray-700 font-normal text-[15px] leading-[140%] !mt-1`,
115
- wrapper: '!mb-0',
116
- inner: '',
117
- },
118
- search: {
119
- suffixIcon: 'dark:text-gray-700'
120
- },
121
- range: {
122
- inner: '$reset flex items-center max-w-md',
123
- input: '$reset w-full mb-1 h-2 p-0 rounded-full',
124
- prefixIcon: '$reset w-4 mr-1 flex self-stretch grow-0 shrink-0 [&>svg]:max-w-[1em] [&>svg]:max-h-[1em] [&>svg]:m-auto',
125
- suffixIcon: '$reset w-4 ml-1 flex self-stretch grow-0 shrink-0 [&>svg]:max-w-[1em] [&>svg]:max-h-[1em] [&>svg]:m-auto'
126
- },
127
- select: {
128
- inner: 'flex relative items-center rounded mb-1 ring-1 ring-gray-400 formkit-invalid:ring-red focus-within:ring-primary-500 focus-within:ring-2 [&>span:first-child]:focus-within:text-primary-500',
129
- input: 'w-full pl-3 pr-8 py-2 border-none text-base text-gray-700 placeholder-gray-300 formkit-multiple:p-0 data-[placeholder="true"]:text-gray-400 formkit-multiple:data-[placeholder="true"]:text-inherit appearance-none bg-none',
130
- selectIcon: 'flex p-[3px] shrink-0 w-5 mr-2 -ml-[1.5em] h-full pointer-events-none dark:text-text-gray-700',
131
- option: 'formkit-multiple:p-3 formkit-multiple:text-sm text-gray-700'
132
- },
133
- textarea: {
134
- inner: 'flex rounded mb-1 ring-1 ring-gray-400 formkit-invalid:ring-red focus-within:ring-primary-400 [&>label:first-child]:focus-within:text-primary-500',
135
- input: 'block w-full h-32 px-3 py-3 border-none text-base text-gray-700 placeholder-gray-400 focus:shadow-outline',
136
- },
137
- }
@@ -1,33 +0,0 @@
1
- import { de } from '@formkit/i18n';
2
- import { genesisIcons } from '@formkit/icons';
3
- import { generateClasses } from '@formkit/themes';
4
- import formkitTheme from './formkit-theme.js'
5
- import { addAsteriskPlugin } from '~/forms/plugins/asterisk-plugin';
6
- import { scrollToErrors } from '~/forms/plugins/scroll-error-plugin';
7
- import { createInput } from '@formkit/vue';
8
- import { valueChangesPlugin } from "~/forms/plugins/value-changes-plugin";
9
-
10
- import InputImage from "~/forms/inputs/InputImage.vue";
11
- import InputTags from "~/forms/inputs/InputTags.vue";
12
- import InputFreeTags from "~/forms/inputs/InputFreeTags.vue";
13
- import InputCheckbox from "~/forms/inputs/InputCheckbox.vue";
14
- import InputToggle from "~/forms/inputs/InputToggle.vue";
15
-
16
- export default {
17
- locales: { de },
18
- locale: 'de',
19
- plugins: [addAsteriskPlugin, scrollToErrors, valueChangesPlugin],
20
- inputs: {
21
- image: createInput(InputImage),
22
- tags: createInput(InputTags),
23
- freeTags: createInput(InputFreeTags),
24
- checkbox: createInput(InputCheckbox),
25
- toggle: createInput(InputToggle),
26
- },
27
- icons: {
28
- ...genesisIcons,
29
- },
30
- config: {
31
- classes: generateClasses(formkitTheme),
32
- },
33
- };