Skip to content

Commit 1cc6bfd

Browse files
committed
feat(FormEditor): First version
1 parent ea04b65 commit 1cc6bfd

File tree

8 files changed

+409
-216
lines changed

8 files changed

+409
-216
lines changed

dev/components.d.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ declare module 'vue' {
99
export interface GlobalComponents {
1010
AppFooter: typeof import('./components/app/AppFooter.vue')['default']
1111
AppTopbar: typeof import('./components/app/AppTopbar.vue')['default']
12+
PrimeEditor: typeof import('./components/demo/PrimeEditor.vue')['default']
13+
PrimeEditorInput: typeof import('./components/demo/PrimeEditorInput.vue')['default']
1214
PrimeInput: typeof import('./components/demo/PrimeInput.vue')['default']
1315
PrimeSchemaEditor: typeof import('./components/demo/PrimeSchemaEditor.vue')['default']
1416
RouterLink: typeof import('vue-router')['RouterLink']

dev/components/app/AppTopbar.vue

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ const items = ref([
9494
label: 'Samples',
9595
items: [
9696
{ label: 'Input Editor', icon: 'pi pi-fw pi-user-edit', route: '/samples/inputEditor' },
97+
{ label: 'Form Editor', icon: 'pi pi-fw pi-user-edit', route: '/samples/formEditor' },
9798
{ label: 'Grid', icon: 'pi pi-fw pi-user-edit', route: '/samples/grid' },
9899
{ label: 'Repeater', icon: 'pi pi-fw pi-user-edit', route: '/samples/repeater' },
99100
],

dev/modules/primevue.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ import Aura from '@primevue/themes/aura'
3636

3737
import PrimeVue from 'primevue/config'
3838
// directives
39+
import Tooltip from 'primevue/tooltip'
3940

4041
// services
4142
import ConfirmationService from 'primevue/confirmationservice'
@@ -48,6 +49,7 @@ import type { UserModule } from '@/types'
4849
export const install: UserModule = ({ app }) => {
4950
// directives
5051
app.directive('ripple', Ripple)
52+
app.directive('tooltip', Tooltip)
5153
app.component('Button', Button)
5254

5355
// input components

dev/pages/samples/FormEditor.vue

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
<script setup lang='ts'>
2+
import { useDragAndDrop } from '@formkit/drag-and-drop/vue'
3+
import { FormKitSchema } from '@formkit/vue'
4+
import { ref } from 'vue'
5+
import { useInputEditor, useInputEditorSchema } from 'my-library'
6+
import { blueprint, formkitPreset } from '../../utils/presets'
7+
8+
const { editorSchema } = useInputEditorSchema()
9+
const { generateSchemaItemId, schemaToEditorData, editorDataToSchema } = useInputEditor()
10+
11+
const formSchema = ref(editorSchema)
12+
const formData = ref(null)
13+
14+
function enhanceValuesForInputList(values: any[]): any[] {
15+
const result = []
16+
values.forEach((value) => {
17+
result.push({ ...value, editableItemId: generateSchemaItemId(), selectButton: 'showBasic' })
18+
})
19+
return result
20+
}
21+
22+
const [formInputRef, formInputList] = useDragAndDrop(enhanceValuesForInputList(formkitPreset), { dragHandle: '.p-drag-handle', plugins: [] })
23+
24+
function actionDelete(schema: any) {
25+
formInputList.value = formInputList.value.filter(input => input !== schema)
26+
}
27+
28+
function actionEdit(schema: any) {
29+
formData.value = schemaToEditorData({ ...schema })
30+
}
31+
32+
function actionInsert(schema: any) {
33+
const index = formInputList.value.indexOf(schema) + 1
34+
const schemaToInsert = { ...blueprint, editableItemId: generateSchemaItemId(), selectButton: 'showBasic' }
35+
formInputList.value.splice(index, 0, schemaToInsert)
36+
}
37+
38+
function actionCopy(schema: any) {
39+
const index = formInputList.value.indexOf(schema) + 1
40+
const copyOfSchema = { ...schema, editableItemId: generateSchemaItemId() }
41+
formInputList.value.splice(index, 0, copyOfSchema)
42+
}
43+
44+
function actionUpdateInputs() {
45+
const schema = formInputList.value.find(input => input.editableItemId === formData.value.editableItemId)
46+
const index = formInputList.value.indexOf(schema)
47+
formInputList.value.splice(index, 1, editorDataToSchema(formData.value))
48+
formData.value = null
49+
}
50+
51+
const schemaResult = computed(() => editorDataToSchema(formData.value))
52+
53+
const schemaItems = computed(() => editorDataToSchema(formInputList.value))
54+
</script>
55+
56+
<template>
57+
<div cl>
58+
<Toolbar class="mt-6 mb-4">
59+
<template #start>
60+
<Button icon="pi pi-plus" class="mr-2" severity="secondary" text />
61+
<Button icon="pi pi-print" class="mr-2" severity="secondary" text />
62+
<Button icon="pi pi-upload" severity="secondary" text />
63+
</template>
64+
65+
<template #center>
66+
Form Editor
67+
</template>
68+
<template #end />
69+
</Toolbar>
70+
<div class="grid grid-cols-2 xl:grid-cols-3 gap-10">
71+
<div>
72+
<ul ref="formInputRef" class="list-none">
73+
<li v-for="formInput in formInputList" :key="formInput" class="mb-1">
74+
<div class="" style="box-sizing: border-box;">
75+
<div class="min-w-100 mr-4 flex gap-2">
76+
<span class="block p-drag-handle"><i class="pi pi-bars text-[color:var(--primary-color)]" /></span>
77+
<i class="pi pi-file-edit text-[color:var(--primary-color)] mb-2" @click="actionEdit(formInput)" />
78+
<i class="pi pi-copy text-[color:var(--primary-color)] mb-2" @click="actionCopy(formInput)" />
79+
<i class="pi pi-plus text-[color:var(--primary-color)] mb-2" @click="actionInsert(formInput)" />
80+
<i class="pi pi-trash text-[color:var(--primary-color)]" @click="actionDelete(formInput)" />
81+
<span class="text-gray-700">|</span>
82+
<i
83+
v-if="formInput.if && formInput.if.length > 0" v-tooltip="`condition: ${formInput.if}`"
84+
class="pi pi-question-circle text-yellow-700"
85+
/>
86+
<span class="text-xs text-yellow-500">{{ formInput.name }}</span>
87+
</div>
88+
<div class="min-w-100">
89+
<FormKitSchema :schema="formInput" :data="{}" />
90+
</div>
91+
</div>
92+
</li>
93+
</ul>
94+
<div>
95+
<pre class="text-xs color-gray-400">{{ formInputList }}</pre>
96+
</div>
97+
</div>
98+
<div v-if="formData" class="mt-4">
99+
<FormKit
100+
101+
v-model="formData"
102+
type="form"
103+
:submit-attrs="{
104+
inputClass: 'p-button p-component',
105+
}"
106+
submit-label="Update Generated Input"
107+
@submit="actionUpdateInputs"
108+
>
109+
<FormKitSchema :schema="formSchema()" :data="formData" />
110+
</FormKit>
111+
<div>
112+
<pre class="text-xs color-gray-400">{{ schemaResult }}</pre>
113+
</div>
114+
</div>
115+
</div>
116+
</div>
117+
</template>
118+
119+
<style lang='scss' scoped>
120+
ul {
121+
display: block;
122+
padding-inline-start: 1em;
123+
}
124+
</style>

dev/utils/presets.ts

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
export const blueprint = {
2+
$formkit: 'primeInputText',
3+
name: 'field',
4+
options: [{ label: 'Option 1', value: 'option1' }, { label: 'Option 2', value: 'option2' }],
5+
}
6+
7+
export const formkitPreset = [{
8+
$formkit: 'primeInputText',
9+
name: 'email',
10+
label: 'Email',
11+
help: 'This will be used for your account.',
12+
validation: 'required|email',
13+
}, {
14+
$formkit: 'primeTextarea',
15+
name: 'myText',
16+
label: 'Text',
17+
validation: '',
18+
rows: '3',
19+
}, {
20+
$formkit: 'primeEditor',
21+
name: 'myEditor',
22+
label: 'Editor',
23+
style: 'height: 160px; margin-bottom:80px;',
24+
}, {
25+
$formkit: 'primePassword',
26+
name: 'password',
27+
label: 'Password',
28+
help: 'Enter your new password.',
29+
validation: 'required|length:5,16',
30+
feedback: true,
31+
}, {
32+
$formkit: 'primePassword',
33+
name: 'password_confirm',
34+
label: 'Confirm password',
35+
help: 'Enter your new password again.',
36+
validation: 'required|confirm',
37+
validationLabel: 'password confirmation',
38+
}, {
39+
$formkit: 'primeCheckbox',
40+
name: 'eu_citizen',
41+
id: 'eu',
42+
label: 'Are you a european citizen?',
43+
}, {
44+
$formkit: 'primeSelect',
45+
if: '$get(eu).value', // 👀 Oooo, conditionals!
46+
name: 'cookie_notice',
47+
label: 'Cookie notice frequency',
48+
optionLabel: 'label',
49+
optionValue: 'value',
50+
help: 'How often should we display a cookie notice?',
51+
}]

package.json

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -88,21 +88,22 @@
8888
},
8989
"dependencies": {
9090
"@formkit/addons": "^1.6.5",
91+
"@formkit/drag-and-drop": "^0.1.6",
9192
"@formkit/i18n": "^1.6.5",
9293
"@formkit/vue": "^1.6.5",
9394
"primeicons": "^7.0.0",
94-
"primevue": "4.0.0"
95+
"primevue": "4.0.1"
9596
},
9697
"devDependencies": {
97-
"@antfu/eslint-config": "2.23.0",
98+
"@antfu/eslint-config": "2.23.2",
9899
"@formkit/core": "^1.6.5",
99-
"@primevue/themes": "4.0.0",
100+
"@primevue/themes": "4.0.1",
100101
"@types/node": "^20.14.11",
101102
"@unocss/preset-icons": "0.61.5",
102103
"@unocss/preset-uno": "0.61.5",
103104
"@vitejs/plugin-vue": "^5.0.5",
104-
"@vitest/coverage-v8": "^2.0.3",
105-
"@vitest/ui": "^2.0.3",
105+
"@vitest/coverage-v8": "^2.0.4",
106+
"@vitest/ui": "^2.0.4",
106107
"@vue/compiler-sfc": "^3.4.33",
107108
"@vue/server-renderer": "^3.4.33",
108109
"@vue/test-utils": "^2.4.6",
@@ -119,7 +120,7 @@
119120
"json-editor-vue": "^0.15.1",
120121
"mkdist": "^1.5.4",
121122
"quill": "^2.0.2",
122-
"sass": "^1.77.8",
123+
"sass": "^1.76.0",
123124
"tslib": "^2.6.3",
124125
"typescript": "^5.5.3",
125126
"unbuild": "2.0.0",
@@ -133,10 +134,10 @@
133134
"vite-plugin-pages": "^0.32.3",
134135
"vite-ssg": "^0.23.8",
135136
"vitepress": "1.3.1",
136-
"vitest": "^2.0.3",
137+
"vitest": "^2.0.4",
137138
"vue": "^3.4.33",
138139
"vue-demi": "^0.14.8",
139140
"vue-router": "^4.4.0",
140-
"vue-tsc": "^2.0.26"
141+
"vue-tsc": "^2.0.28"
141142
}
142143
}

0 commit comments

Comments
 (0)