create-nuxt-base 0.1.23 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintignore +14 -0
- package/.eslintrc +3 -0
- package/.github/workflows/release.yml +1 -1
- package/.prettierignore +5 -0
- package/.prettierrc +6 -0
- package/.vscode/settings.json +10 -10
- package/CHANGELOG.md +22 -33
- package/README.md +1 -0
- package/index.js +30 -29
- package/nuxt-base-template/.env.example +8 -0
- package/nuxt-base-template/.eslintrc +1 -1
- package/nuxt-base-template/.vscode/settings.json +6 -16
- package/nuxt-base-template/e2e/init.spec.ts +18 -0
- package/nuxt-base-template/nuxt.config.ts +27 -12
- package/nuxt-base-template/package-lock.json +6050 -4606
- package/nuxt-base-template/package.json +47 -29
- package/nuxt-base-template/playwright.config.ts +77 -0
- package/nuxt-base-template/src/app.vue +4 -4
- package/nuxt-base-template/src/assets/css/tailwind.css +42 -33
- package/nuxt-base-template/src/components/SocialMediaBubble.vue +1 -1
- package/nuxt-base-template/src/components/base/BaseAccordion.vue +16 -13
- package/nuxt-base-template/src/components/base/BaseButton.vue +10 -13
- package/nuxt-base-template/src/components/base/BaseContainer.vue +1 -1
- package/nuxt-base-template/src/components/base/BaseContextMenuContainer.vue +61 -0
- package/nuxt-base-template/src/components/base/BaseInfinityList.vue +1 -1
- package/nuxt-base-template/src/components/base/BaseProgressbar.vue +28 -30
- package/nuxt-base-template/src/components/base/BaseToggle.vue +17 -10
- package/nuxt-base-template/src/components/form/FormInput.vue +34 -0
- package/nuxt-base-template/src/components/form/FormPassword.vue +47 -0
- package/nuxt-base-template/src/components/form/FormSelect.vue +40 -0
- package/nuxt-base-template/src/components/form/FormSubmit.vue +18 -0
- package/nuxt-base-template/src/components/form/FormTextarea.vue +36 -0
- package/nuxt-base-template/src/components/form/FormToggle.vue +27 -0
- package/nuxt-base-template/src/components/modal/Modal.vue +48 -0
- package/nuxt-base-template/src/components/modal/ModalConfirm.vue +38 -0
- package/nuxt-base-template/src/components/{base/BaseModalContainer.vue → modal/ModalContainer.vue} +1 -1
- package/nuxt-base-template/src/components/modal/ModalInfo.vue +22 -0
- package/nuxt-base-template/src/components/{ModalShare.vue → modal/ModalShare.vue} +7 -11
- package/nuxt-base-template/src/components/{base/BaseNotification.vue → notification/Notification.vue} +8 -7
- package/nuxt-base-template/src/components/{base/BaseNotificationContainer.vue → notification/NotificationContainer.vue} +11 -5
- package/nuxt-base-template/src/components/pwa/pwa-install-banner.vue +224 -0
- package/nuxt-base-template/src/components/transition/TransitionFade.vue +10 -7
- package/nuxt-base-template/src/components/transition/TransitionFadeScale.vue +10 -7
- package/nuxt-base-template/src/composables/use-auth-fetch.ts +22 -8
- package/nuxt-base-template/src/composables/use-context-menu.ts +19 -0
- package/nuxt-base-template/src/composables/use-file.ts +1 -2
- package/nuxt-base-template/src/composables/use-modal.ts +1 -1
- package/nuxt-base-template/src/composables/use-notification.ts +2 -2
- package/nuxt-base-template/src/composables/use-share.ts +3 -3
- package/nuxt-base-template/src/error.vue +10 -14
- package/nuxt-base-template/src/layouts/default.vue +13 -0
- package/nuxt-base-template/src/middleware/auth.global.ts +2 -2
- package/nuxt-base-template/src/pages/index.vue +31 -6
- package/nuxt-base-template/src/plugins/auth.server.ts +72 -0
- package/nuxt-base-template/src/plugins/form.plugin.ts +21 -0
- package/nuxt-base-template/src/plugins/pwa.plugin.ts +110 -0
- package/nuxt-base-template/src/tests/init.test.ts +1 -1
- package/nuxt-base-template/tailwind.config.js +33 -23
- package/nuxt-base-template/vitest.config.js +3 -3
- package/package.json +3 -1
- package/nuxt-base-template/formkit-theme.js +0 -137
- package/nuxt-base-template/formkit.config.js +0 -33
- package/nuxt-base-template/src/components/hello-world.vue +0 -10
- package/nuxt-base-template/src/composables/use-form-helper.ts +0 -100
- package/nuxt-base-template/src/composables/use-helper.ts +0 -52
- package/nuxt-base-template/src/forms/inputs/InputCheckbox.vue +0 -29
- package/nuxt-base-template/src/forms/inputs/InputFreeTags.vue +0 -98
- package/nuxt-base-template/src/forms/inputs/InputImage.vue +0 -65
- package/nuxt-base-template/src/forms/inputs/InputTags.vue +0 -112
- package/nuxt-base-template/src/forms/inputs/InputToggle.vue +0 -18
- package/nuxt-base-template/src/forms/plugins/asterisk-plugin.ts +0 -29
- package/nuxt-base-template/src/forms/plugins/scroll-error-plugin.ts +0 -36
- package/nuxt-base-template/src/forms/plugins/value-changes-plugin.ts +0 -13
- package/nuxt-base-template/src/plugins/4.auth.server.ts +0 -70
|
@@ -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
|
-
};
|
|
@@ -1,100 +0,0 @@
|
|
|
1
|
-
import type { FormKitEventListener, FormKitNode } from '@formkit/core';
|
|
2
|
-
import { getNode, submitForm as submitFormkit } from '@formkit/core';
|
|
3
|
-
|
|
4
|
-
export function useFormHelper() {
|
|
5
|
-
function prepareNode(node: FormKitNode | undefined, listener: FormKitEventListener, eventName: string = 'input') {
|
|
6
|
-
if (node?.on) {
|
|
7
|
-
node.on(eventName, listener);
|
|
8
|
-
}
|
|
9
|
-
node?.children?.forEach((childNode) => {
|
|
10
|
-
prepareNode(childNode as FormKitNode, listener, eventName);
|
|
11
|
-
});
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
async function valueChanges(nodeId: string, listener: FormKitEventListener, eventName: string = 'input') {
|
|
15
|
-
// Puffer to load form
|
|
16
|
-
await new Promise<void>((resolve) => setTimeout(() => {
|
|
17
|
-
resolve();
|
|
18
|
-
}, 1));
|
|
19
|
-
prepareNode(getNode(nodeId), listener, eventName);
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
function submitForm(formId: string) {
|
|
23
|
-
submitFormkit(formId);
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
function isDirty(formId: string): boolean {
|
|
27
|
-
const form = getNode(formId);
|
|
28
|
-
|
|
29
|
-
return !!form?.context?.state?.dirty;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
async function getChangedValue(formId: string): Promise<null | object> {
|
|
33
|
-
await new Promise(f => setTimeout(f, 100));
|
|
34
|
-
const form = getNode(formId);
|
|
35
|
-
|
|
36
|
-
if (!form) {
|
|
37
|
-
return null;
|
|
38
|
-
}
|
|
39
|
-
return getDifferences(form?.props?.initial, form?.value);
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
function getDifferences(obj1: any, obj2: any): any {
|
|
43
|
-
if (obj1 === obj2) {
|
|
44
|
-
return undefined;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
if (typeof obj1 !== typeof obj2) {
|
|
48
|
-
return obj2;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
if (typeof obj1 !== 'object' || obj1 === null || obj2 === null) {
|
|
52
|
-
return obj2;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
if (Array.isArray(obj1) !== Array.isArray(obj2)) {
|
|
56
|
-
return obj2;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
if (Array.isArray(obj1)) {
|
|
60
|
-
if (obj1.length !== obj2.length) {
|
|
61
|
-
return obj2;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
const diffArray: any[] = [];
|
|
65
|
-
|
|
66
|
-
for (let i = 0; i < obj1.length; i++) {
|
|
67
|
-
const itemDiff = getDifferences(obj1[i], obj2[i]);
|
|
68
|
-
if (itemDiff !== undefined) {
|
|
69
|
-
diffArray[i] = itemDiff;
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
if (diffArray.length === 0) {
|
|
74
|
-
return undefined;
|
|
75
|
-
} else {
|
|
76
|
-
return diffArray;
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
const keys1 = Object.keys(obj1);
|
|
81
|
-
const keys2 = Object.keys(obj2);
|
|
82
|
-
|
|
83
|
-
const allKeys = new Set([...keys1, ...keys2]);
|
|
84
|
-
const diffObj: any = {};
|
|
85
|
-
for (const key of allKeys) {
|
|
86
|
-
const keyDiff = getDifferences(obj1[key], obj2[key]);
|
|
87
|
-
if (keyDiff !== undefined) {
|
|
88
|
-
diffObj[key] = keyDiff;
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
if (Object.keys(diffObj).length === 0) {
|
|
93
|
-
return undefined;
|
|
94
|
-
} else {
|
|
95
|
-
return diffObj;
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
return { getChangedValue, getDifferences, isDirty, submitForm, prepareNode, valueChanges };
|
|
100
|
-
}
|
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
export function useHelper() {
|
|
2
|
-
function removeFields(obj: any): any {
|
|
3
|
-
const ignoreFields = ['id', '__typename'];
|
|
4
|
-
if (Array.isArray(obj)) {
|
|
5
|
-
return obj.map(item => removeFields(item));
|
|
6
|
-
} else if (typeof obj === 'object' && obj !== null) {
|
|
7
|
-
const newObj: any = {};
|
|
8
|
-
for (const key in obj) {
|
|
9
|
-
if (!ignoreFields.includes(key)) {
|
|
10
|
-
newObj[key] = removeFields(obj[key]);
|
|
11
|
-
}
|
|
12
|
-
}
|
|
13
|
-
return newObj;
|
|
14
|
-
} else {
|
|
15
|
-
return obj;
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
function removeNullOrUndefined(obj: any): any {
|
|
20
|
-
if (Array.isArray(obj)) {
|
|
21
|
-
return obj.map(item => removeNullOrUndefined(item)).filter(item => item !== null && item !== undefined);
|
|
22
|
-
} else if (typeof obj === 'object' && obj !== null) {
|
|
23
|
-
const newObj: any = {};
|
|
24
|
-
for (const key in obj) {
|
|
25
|
-
const value = removeNullOrUndefined(obj[key]);
|
|
26
|
-
if (value !== null && value !== undefined) {
|
|
27
|
-
newObj[key] = value;
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
return newObj;
|
|
31
|
-
} else {
|
|
32
|
-
return obj;
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
function isValidMongoID(id: string): boolean {
|
|
37
|
-
const validHexChars = /^[0-9a-fA-F]{24}$/;
|
|
38
|
-
|
|
39
|
-
return validHexChars.test(id);
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
function groupBy<T>(arr: T[], fn: (item: T) => any) {
|
|
43
|
-
return arr.reduce<Record<string, T[]>>((prev, curr) => {
|
|
44
|
-
const groupKey = fn(curr);
|
|
45
|
-
const group = prev[groupKey] || [];
|
|
46
|
-
group.push(curr);
|
|
47
|
-
return { ...prev, [groupKey]: group };
|
|
48
|
-
}, {});
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
return { removeFields, removeNullOrUndefined, isValidMongoID, groupBy };
|
|
52
|
-
}
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
<script setup lang="ts">
|
|
2
|
-
const props = defineProps({
|
|
3
|
-
context: Object,
|
|
4
|
-
});
|
|
5
|
-
const name = props.context.name;
|
|
6
|
-
const label = props.context.attrs.text;
|
|
7
|
-
|
|
8
|
-
function setInput(e) {
|
|
9
|
-
props.context.node.input(e.target.checked);
|
|
10
|
-
}
|
|
11
|
-
</script>
|
|
12
|
-
|
|
13
|
-
<template>
|
|
14
|
-
<div :class="context.classes.container">
|
|
15
|
-
<label :id="props.context?.id" :class="context.classes.wrapper">
|
|
16
|
-
<div :class="context.classes.inner">
|
|
17
|
-
<input :class="context.classes.input" type="checkbox" :name="name" @input="setInput" />
|
|
18
|
-
<span :class="context.classes.decorator" aria-hidden="true">
|
|
19
|
-
<span :class="[context.classes.decoratorIcon, context.classes.icon]">
|
|
20
|
-
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 27">
|
|
21
|
-
<polygon fill="currentColor" points="26.99 0 10.13 17.17 4.69 11.63 0 16.41 10.4 27 15.05 22.27 15.09 22.31 32 5.1 26.99 0" />
|
|
22
|
-
</svg>
|
|
23
|
-
</span>
|
|
24
|
-
</span>
|
|
25
|
-
</div>
|
|
26
|
-
</label>
|
|
27
|
-
<span :class="context.classes.label" v-html="label"></span>
|
|
28
|
-
</div>
|
|
29
|
-
</template>
|
|
@@ -1,98 +0,0 @@
|
|
|
1
|
-
<script setup lang="ts">
|
|
2
|
-
const props = defineProps({
|
|
3
|
-
context: Object,
|
|
4
|
-
});
|
|
5
|
-
|
|
6
|
-
const inputRef = ref();
|
|
7
|
-
const inputValue = ref<string>();
|
|
8
|
-
const name = props.context?.name;
|
|
9
|
-
const placeholder = props.context?.attrs?.placeholder;
|
|
10
|
-
const defaultSuggestions = [...props.context?.attrs?.suggestions] || [];
|
|
11
|
-
const suggestions = ref<string[]>([...props.context?.attrs?.suggestions]);
|
|
12
|
-
const tags = ref<string[]>([]);
|
|
13
|
-
patch();
|
|
14
|
-
|
|
15
|
-
function patch() {
|
|
16
|
-
const values = props.context?._value;
|
|
17
|
-
if (values && Array.isArray(values)) {
|
|
18
|
-
for (let item of values) {
|
|
19
|
-
addTag(item);
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
function addTag(tag?: string) {
|
|
26
|
-
if (!inputValue.value && !tag) {
|
|
27
|
-
return;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
if (tag) {
|
|
31
|
-
tags.value.push(tag);
|
|
32
|
-
// remove tag from suggestions
|
|
33
|
-
if (suggestions.value.findIndex((e) => e === tag) >= 0) {
|
|
34
|
-
suggestions.value.splice(suggestions.value.findIndex((e) => e === tag), 1);
|
|
35
|
-
}
|
|
36
|
-
} else {
|
|
37
|
-
if (!tags.value.includes(inputValue.value as string)) {
|
|
38
|
-
tags.value.push(inputValue.value as string);
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
if (suggestions.value.includes(inputValue.value as string)) {
|
|
42
|
-
if (suggestions.value.findIndex((e) => e === tag) >= 0) {
|
|
43
|
-
suggestions.value.splice(suggestions.value.findIndex((e) => e === inputValue.value), 1);
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
inputValue.value = '';
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
props.context?.node.input(tags.value);
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
function removeTag(tag: string) {
|
|
54
|
-
tags.value.splice(tags.value.findIndex((e) => e === tag), 1);
|
|
55
|
-
|
|
56
|
-
if (defaultSuggestions.includes(tag) && !suggestions.value.includes(tag)) {
|
|
57
|
-
suggestions.value.push(tag);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
props.context?.node.input(tags.value);
|
|
61
|
-
}
|
|
62
|
-
</script>
|
|
63
|
-
|
|
64
|
-
<template>
|
|
65
|
-
<div class="flex flex-col">
|
|
66
|
-
<div class="flex flex-row flex-wrap gap-2">
|
|
67
|
-
<span v-for="(tag, index) of tags" :key="index" :class="context?.classes.tag">{{ tag }} <span class="i-bi-x" :class="context?.classes.tagIcon" @click="removeTag(tag)"></span> </span>
|
|
68
|
-
</div>
|
|
69
|
-
<div :class="context?.classes.inputWrapper">
|
|
70
|
-
<input
|
|
71
|
-
:id="props.context?.id"
|
|
72
|
-
ref="inputRef"
|
|
73
|
-
v-model="inputValue"
|
|
74
|
-
:name="name"
|
|
75
|
-
type="text"
|
|
76
|
-
:placeholder="placeholder"
|
|
77
|
-
:class="context?.classes.input"
|
|
78
|
-
@keydown.enter.prevent="addTag()"
|
|
79
|
-
/>
|
|
80
|
-
<span :class="context?.classes.inputIcon"></span>
|
|
81
|
-
</div>
|
|
82
|
-
<div>
|
|
83
|
-
<button type="button" :disabled="!inputValue" :class="context?.classes.button" @click="addTag()">
|
|
84
|
-
{{ context?.attrs.buttonText ?? context?.attrs['button-text'] }}
|
|
85
|
-
</button>
|
|
86
|
-
</div>
|
|
87
|
-
<div v-if="suggestions?.length" :class="context?.classes.suggestionsWrapper">
|
|
88
|
-
<p :class="context?.classes.suggestionsHeadline">
|
|
89
|
-
Beliebte Skills
|
|
90
|
-
</p>
|
|
91
|
-
<div class="flex flex-row flex-wrap gap-2">
|
|
92
|
-
<div v-for="(tag, index) of suggestions" :key="index" :class="context?.classes.suggestionsTag">
|
|
93
|
-
{{ tag }} <span :class="context?.classes.suggestionsIcon" @click="addTag(tag)"></span>
|
|
94
|
-
</div>
|
|
95
|
-
</div>
|
|
96
|
-
</div>
|
|
97
|
-
</div>
|
|
98
|
-
</template>
|
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
<script setup lang="ts">
|
|
2
|
-
const props = defineProps({
|
|
3
|
-
context: Object,
|
|
4
|
-
});
|
|
5
|
-
|
|
6
|
-
const value = ref('');
|
|
7
|
-
const label = props.context?.label;
|
|
8
|
-
const text = props.context?.attrs.text;
|
|
9
|
-
const name = props.context?.name;
|
|
10
|
-
const config = useRuntimeConfig();
|
|
11
|
-
await getPreviewUrl(props.context?._value);
|
|
12
|
-
|
|
13
|
-
watch(() => props.context?._value, async () => {
|
|
14
|
-
await getPreviewUrl(props.context?._value);
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
function handleInput(e: any) {
|
|
18
|
-
if (!e) {
|
|
19
|
-
props.context?.node.input(null);
|
|
20
|
-
return;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
props.context?.node.input(e.target.files[0]);
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
async function getPreviewUrl(src: string | File) {
|
|
27
|
-
if (!src) {
|
|
28
|
-
value.value = '';
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
const validHexChars = /^[0-9a-fA-F]{24}$/;
|
|
32
|
-
if (typeof src === 'object' && validHexChars.test(src?.id)) {
|
|
33
|
-
// return config.public.apiUrl + '/files/' + src.id;
|
|
34
|
-
const response = await useAuthFetch(config.public.apiUrl + '/files/' + src?.id, { method: 'GET' });
|
|
35
|
-
value.value = URL.createObjectURL(response as any);
|
|
36
|
-
}
|
|
37
|
-
else if (typeof src === 'string') {
|
|
38
|
-
value.value = src;
|
|
39
|
-
} else if (src instanceof File) {
|
|
40
|
-
value.value = URL.createObjectURL(src);
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
</script>
|
|
44
|
-
|
|
45
|
-
<template>
|
|
46
|
-
<div class="image-container" :class="props?.context?.classes.container">
|
|
47
|
-
<div v-if="!props.context?._value" :class="props?.context?.classes.uploader">
|
|
48
|
-
<label :class="context?.classes.placeholder">{{ text }}</label>
|
|
49
|
-
<input
|
|
50
|
-
:id="props.context?.id"
|
|
51
|
-
:name="name"
|
|
52
|
-
type="file"
|
|
53
|
-
:class="props?.context?.classes.input"
|
|
54
|
-
@input="handleInput"
|
|
55
|
-
/>
|
|
56
|
-
</div>
|
|
57
|
-
<div v-else :class="props?.context.classes.fileItem">
|
|
58
|
-
<img :src="value" :class="props?.context?.classes.fileItemImage" />
|
|
59
|
-
<span :class="props?.context.classes.fileItemImageName">{{ props.context?._value?.name ?? props.context?._value?.filename }}</span>
|
|
60
|
-
<button type="button" :class="props?.context.classes.fileItemRemove" @click="handleInput(null)">
|
|
61
|
-
Entfernen
|
|
62
|
-
</button>
|
|
63
|
-
</div>
|
|
64
|
-
</div>
|
|
65
|
-
</template>
|
|
@@ -1,112 +0,0 @@
|
|
|
1
|
-
<script setup lang="ts">
|
|
2
|
-
interface TagOption {
|
|
3
|
-
label: string;
|
|
4
|
-
value: string;
|
|
5
|
-
}
|
|
6
|
-
|
|
7
|
-
const props = defineProps({
|
|
8
|
-
context: Object,
|
|
9
|
-
});
|
|
10
|
-
|
|
11
|
-
const inputRef = ref();
|
|
12
|
-
const inputValue = ref<string>();
|
|
13
|
-
const inputFocus = ref();
|
|
14
|
-
const name = props.context?.name;
|
|
15
|
-
const placeholder = props.context?.attrs?.placeholder;
|
|
16
|
-
const tags = ref<TagOption[]>([]);
|
|
17
|
-
const defaultOptions: TagOption[] = [...props?.context?.attrs?.options] || [];
|
|
18
|
-
const selectOptions = ref<TagOption[]>([...props?.context?.attrs?.options]);
|
|
19
|
-
patch();
|
|
20
|
-
|
|
21
|
-
function patch() {
|
|
22
|
-
const values = props.context?._value;
|
|
23
|
-
if (!values) {
|
|
24
|
-
return;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
if (Array.isArray(values)) {
|
|
28
|
-
for (let item of values) {
|
|
29
|
-
handleSelect(item);
|
|
30
|
-
}
|
|
31
|
-
} else {
|
|
32
|
-
handleSelect(values);
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
function handleSelect(value: string) {
|
|
37
|
-
const tag = selectOptions.value.find((e) => e.value === value);
|
|
38
|
-
const included = tags.value?.find((e) => e.value === tag?.value) ?? false;
|
|
39
|
-
|
|
40
|
-
if (!tag) {
|
|
41
|
-
return;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
if (!included) {
|
|
45
|
-
tags.value.push(tag);
|
|
46
|
-
selectOptions.value = defaultOptions.filter((e) => e.value !== tag.value);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
props.context?.node.input(tags.value.map((e) => e.value));
|
|
50
|
-
inputValue.value = '';
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
function handleInput(event: any) {
|
|
54
|
-
const value = event.target.value.toLowerCase();
|
|
55
|
-
|
|
56
|
-
if (!value) {
|
|
57
|
-
selectOptions.value = defaultOptions.filter((e: TagOption) => !props.context?._value?.includes(e.value));
|
|
58
|
-
} else {
|
|
59
|
-
selectOptions.value = selectOptions.value.filter((e: TagOption) => e.label.toLowerCase().includes(value));
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
function removeTag(tag: TagOption) {
|
|
64
|
-
tags.value.splice(tags.value.findIndex((e) => e.value === tag.value), 1);
|
|
65
|
-
props.context?.node.input(tags.value.map((e) => e.value));
|
|
66
|
-
selectOptions.value = defaultOptions.filter((e: TagOption) => !props.context?._value?.includes(e.value));
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
function setFocus() {
|
|
70
|
-
inputFocus.value = true;
|
|
71
|
-
inputValue.value = '';
|
|
72
|
-
selectOptions.value = defaultOptions.filter((e: TagOption) => !props.context?._value?.includes(e.value));
|
|
73
|
-
}
|
|
74
|
-
</script>
|
|
75
|
-
|
|
76
|
-
<template>
|
|
77
|
-
<div class="flex flex-col">
|
|
78
|
-
<div class="flex flex-row flex-wrap gap-2">
|
|
79
|
-
<span v-for="tag of tags" :key="tag.value" :class="context?.classes.tag">{{ tag.label }} <span class="i-bi-x" :class="context?.classes.tagIcon" @click="removeTag(tag)"></span> </span>
|
|
80
|
-
</div>
|
|
81
|
-
<div v-if="selectOptions.length" class="relative" :class="context?.classes.inputWrapper">
|
|
82
|
-
<div class="relative w-full flex items-center">
|
|
83
|
-
<input
|
|
84
|
-
:id="props.context?.id"
|
|
85
|
-
ref="inputRef"
|
|
86
|
-
v-model="inputValue"
|
|
87
|
-
autocomplete="off"
|
|
88
|
-
:name="name" type="text" :placeholder="placeholder" :class="context?.classes.input" @focus="setFocus"
|
|
89
|
-
@blur="inputFocus = false" @input="handleInput"
|
|
90
|
-
/>
|
|
91
|
-
<span :class="context?.classes.selectIcon" class="formkit-select-icon flex p-[3px] shrink-0 w-5 mr-2 -ml-[1.5em] h-full pointer-events-none formkit-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 7"><path d="M8,6.5c-.13,0-.26-.05-.35-.15L3.15,1.85c-.2-.2-.2-.51,0-.71,.2-.2,.51-.2,.71,0l4.15,4.15L12.15,1.15c.2-.2,.51-.2,.71,0,.2,.2,.2,.51,0,.71l-4.5,4.5c-.1,.1-.23,.15-.35,.15Z" fill="currentColor" /></svg></span>
|
|
92
|
-
</div>
|
|
93
|
-
<TransitionFade :start-duration="200" :leave-duration="200">
|
|
94
|
-
<div
|
|
95
|
-
v-show="inputFocus && defaultOptions.length"
|
|
96
|
-
class="absolute right-0 left-0 z-50 h-fit max-h-12 flex flex-col justify-start items-start"
|
|
97
|
-
:style="`top: ${inputRef?.clientHeight + 1}px`"
|
|
98
|
-
:class="context?.classes.dropdown"
|
|
99
|
-
>
|
|
100
|
-
<div v-if="selectOptions.length" class="w-full">
|
|
101
|
-
<button v-for="option of selectOptions" :key="option.value" :class="context?.classes.dropdownItem" @click="handleSelect(option.value)">
|
|
102
|
-
{{ option.label }}
|
|
103
|
-
</button>
|
|
104
|
-
</div>
|
|
105
|
-
<div v-else :class="context?.classes.noItemsFound">
|
|
106
|
-
Keine Einträge gefunden
|
|
107
|
-
</div>
|
|
108
|
-
</div>
|
|
109
|
-
</TransitionFade>
|
|
110
|
-
</div>
|
|
111
|
-
</div>
|
|
112
|
-
</template>
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
<script setup lang="ts">
|
|
2
|
-
const props = defineProps({
|
|
3
|
-
context: Object,
|
|
4
|
-
});
|
|
5
|
-
const inputValue = ref(props?.context?._value ?? false);
|
|
6
|
-
|
|
7
|
-
watch(() => inputValue.value, () => {
|
|
8
|
-
props.context?.node.input(inputValue.value);
|
|
9
|
-
});
|
|
10
|
-
</script>
|
|
11
|
-
|
|
12
|
-
<template>
|
|
13
|
-
<div class="flex justify-between items-center" @click="inputValue = !inputValue">
|
|
14
|
-
<div class="w-8 h-5 flex items-center bg-gray-300 rounded-full p-1 duration-300 ease-in-out" :class="{ 'bg-green-400': inputValue }">
|
|
15
|
-
<div class="bg-white w-4 h-4 rounded-full shadow-md transform duration-300 ease-in-out" :class="{ 'translate-x-2': inputValue }"></div>
|
|
16
|
-
</div>
|
|
17
|
-
</div>
|
|
18
|
-
</template>
|