add ability to edit and move form elements
This commit is contained in:
@ -1,10 +1,13 @@
|
||||
<template>
|
||||
<form @submit.prevent="handleSubmit" class="">
|
||||
<h3 v-if="!!formTitle" class="text-xl font-semibold mb-4">{{formTitle}}</h3>
|
||||
<div v-for="input in formInputs" :key="input.id" class="flex flex-col">
|
||||
<div v-for="(input, index) in formInputs" :key="input.id" class="flex flex-col">
|
||||
<!-- if showLabel is undefined or set to true then show the label-->
|
||||
<label v-if="input.showLabel === undefined || input.showLabel" :for="input.id" class="mb-2 font-semibold text-gray-700">{{ input.label }}</label>
|
||||
|
||||
<!-- Description -->
|
||||
<p v-if="input.type === 'description'" class="text-gray-600 mb-2">{{ input.defaultValue }}</p>
|
||||
|
||||
<!-- Text, Email, Password, Number -->
|
||||
<input
|
||||
v-if="['text', 'email', 'password', 'number', 'url', 'tel', 'search', 'color', 'date', 'datetime-local', 'month', 'time', 'week'].includes(input.type)"
|
||||
@ -94,6 +97,13 @@
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-row w-auto" v-if="isEditing">
|
||||
<button class="px-2 bg-gray-200 text-black font-semibold rounded-md shadow-md hover:bg-gray-300 focus:outline-none focus:ring-2 focus:ring-gray-300 mt-2 mr-2 " @click.prevent="() => {handleEditRequest(input.id)}">edit</button>
|
||||
<button class="px-2 bg-gray-200 text-black font-semibold rounded-md shadow-md hover:bg-gray-300 focus:outline-none focus:ring-2 focus:ring-gray-300 mt-2" @click.prevent="() => {handleDeleteRequest(input.id)}">delete</button>
|
||||
<button v-if="index !== 0" class="px-2 bg-gray-200 text-black font-semibold rounded-md shadow-md hover:bg-gray-300 focus:outline-none focus:ring-2 focus:ring-gray-300 mt-2 mr-2 ml-auto" @click.prevent="() => {handleMoveUp(input.id)}">move up</button>
|
||||
<button v-if="index !== formInputs.length -1" :class="index === 0 ? 'ml-auto': ''" class="px-2 bg-gray-200 text-black font-semibold rounded-md shadow-md hover:bg-gray-300 focus:outline-none focus:ring-2 focus:ring-gray-300 mt-2" @click.prevent="() => {handleMoveDown(input.id)}">move down</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<button type="submit" class="px-6 py-3 bg-blue-600 text-white font-semibold rounded-md shadow-md hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500">
|
||||
{{ !!submitLabel ? submitLabel : 'Submit' }}
|
||||
@ -102,7 +112,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { reactive, ref } from 'vue';
|
||||
import {reactive, ref, watchEffect} from 'vue';
|
||||
|
||||
export interface FormInput {
|
||||
id: string;
|
||||
@ -117,21 +127,23 @@ export interface FormInput {
|
||||
options?: { label: string, value: string }[]; // For select, radio, and checkbox types
|
||||
}
|
||||
|
||||
const props = defineProps<{ formInputs: FormInput[], submitLabel?: string, formTitle?: string }>();
|
||||
const emit = defineEmits(['send'])
|
||||
const props = defineProps<{ formInputs: FormInput[], submitLabel?: string, formTitle?: string, isEditing?: boolean }>();
|
||||
const emit = defineEmits(['send', 'edit', 'delete', 'move-up', 'move-down']);
|
||||
|
||||
const formData = reactive<Record<string, any>>({});
|
||||
|
||||
// Initialize formData with default values
|
||||
props.formInputs.forEach(input => {
|
||||
if (input.type === 'checkbox' || input.type === 'multi-input') {
|
||||
// if input.defaultValue is an array, use it,
|
||||
// otherwise create an empty array and
|
||||
// if input.defaultValue is a value that matches an option value, insert it into the array
|
||||
formData[input.id] = Array.isArray(input.defaultValue) ? input.defaultValue : (input.defaultValue ? [input.defaultValue] : []);
|
||||
} else {
|
||||
formData[input.id] = input.defaultValue || '';
|
||||
}
|
||||
watchEffect(() => {
|
||||
// Initialize formData with default values
|
||||
props.formInputs.forEach(input => {
|
||||
if (input.type === 'checkbox' || input.type === 'multi-input') {
|
||||
// if input.defaultValue is an array, use it,
|
||||
// otherwise create an empty array and
|
||||
// if input.defaultValue is a value that matches an option value, insert it into the array
|
||||
formData[input.id] = Array.isArray(input.defaultValue) ? input.defaultValue : (input.defaultValue ? [input.defaultValue] : []);
|
||||
} else {
|
||||
formData[input.id] = input.defaultValue || '';
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
const multiInputValue = ref<string>('');
|
||||
@ -141,6 +153,22 @@ const handleSubmit = () => {
|
||||
emit('send', formData);
|
||||
};
|
||||
|
||||
const handleEditRequest = (inputID: string) => {
|
||||
emit('edit', props.formInputs.find(input => input.id === inputID));
|
||||
};
|
||||
|
||||
const handleDeleteRequest = (inputID: string) => {
|
||||
emit('delete', inputID);
|
||||
};
|
||||
|
||||
const handleMoveUp = (inputID: string) => {
|
||||
emit('move-up', inputID);
|
||||
};
|
||||
|
||||
const handleMoveDown = (inputID: string) => {
|
||||
emit('move-down', inputID);
|
||||
};
|
||||
|
||||
const addMultiInputValue = (id: string) => {
|
||||
if (multiInputValue.value.trim()) {
|
||||
formData[id].push(multiInputValue.value.trim());
|
||||
|
@ -28,7 +28,15 @@
|
||||
:formInputs="generatedFormInputs"
|
||||
:submitLabel="submitLabel"
|
||||
:formTitle="formTitle"
|
||||
@edit="handleEditRequest"
|
||||
@delete="handleDeleteRequest"
|
||||
@move-up="handleMoveUp"
|
||||
@move-down="handleMoveDown"
|
||||
:is-editing="showEditOption"
|
||||
/>
|
||||
<button
|
||||
@click.prevent="showEditOption = !showEditOption"
|
||||
class="px-2 py-1 bg-gray-200 text-black font-semibold rounded-md shadow-md hover:bg-gray-300 focus:outline-none focus:ring-2 focus:ring-gray-300 mt-4">preview: {{showEditOption ? 'off' : 'on'}}</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@ -62,6 +70,7 @@ const formBuilderPropertiesInputs = ref<FormInput[]>([
|
||||
const formTitle = ref('');
|
||||
const formDescription = ref('');
|
||||
const submitLabel = ref('');
|
||||
const showEditOption = ref(true);
|
||||
|
||||
const formBuilderFieldInputs = ref<FormInput[]>([
|
||||
{
|
||||
@ -87,7 +96,8 @@ const formBuilderFieldInputs = ref<FormInput[]>([
|
||||
{ label: 'Datetime-local', value: 'datetime-local' },
|
||||
{ label: 'Month', value: 'month' },
|
||||
{ label: 'Time', value: 'time' },
|
||||
{ label: 'Week', value: 'week' }
|
||||
{ label: 'Week', value: 'week' },
|
||||
{ label: 'Description', value: 'description' }
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -151,6 +161,14 @@ const formBuilderFieldInputs = ref<FormInput[]>([
|
||||
}
|
||||
]);
|
||||
|
||||
const defaultFormInput: FormInput = {
|
||||
id: '',
|
||||
label: '',
|
||||
type: '',
|
||||
required: false,
|
||||
showLabel: true
|
||||
};
|
||||
|
||||
const generatedFormInputs = ref<FormInput[]>([]);
|
||||
|
||||
const handleFormPropertiesSubmit = (formData: Record<string, any>) => {
|
||||
@ -159,6 +177,78 @@ const handleFormPropertiesSubmit = (formData: Record<string, any>) => {
|
||||
submitLabel.value = formData.submitLabel;
|
||||
};
|
||||
|
||||
const handleEditRequest = (inputToEdit: FormInput) => {
|
||||
// set formBuilderFieldInputs to the input values
|
||||
console.log(inputToEdit);
|
||||
formBuilderFieldInputs.value.forEach((input: FormInput, i) => {
|
||||
// fieldtype
|
||||
if (input.id === 'fieldType') {
|
||||
input.defaultValue = inputToEdit.type;
|
||||
}
|
||||
// fieldlabel
|
||||
if (input.id === 'fieldLabel') {
|
||||
input.defaultValue = inputToEdit.label;
|
||||
}
|
||||
// fieldid
|
||||
if (input.id === 'fieldID') {
|
||||
input.defaultValue = inputToEdit.id;
|
||||
}
|
||||
// placeholder
|
||||
if (input.id === 'placeholder') {
|
||||
input.defaultValue = inputToEdit.placeholder;
|
||||
}
|
||||
// required
|
||||
if (input.id === 'required') {
|
||||
input.defaultValue = inputToEdit.required ? ['true'] : [];
|
||||
}
|
||||
// showlabel
|
||||
if (input.id === 'showLabel') {
|
||||
input.defaultValue = inputToEdit.showLabel ? ['true'] : [];
|
||||
}
|
||||
// defaultvalue
|
||||
if (input.id === 'defaultValue') {
|
||||
input.defaultValue = inputToEdit.defaultValue;
|
||||
}
|
||||
// min
|
||||
if (input.id === 'min') {
|
||||
input.defaultValue = inputToEdit.min;
|
||||
}
|
||||
// max
|
||||
if (input.id === 'max') {
|
||||
input.defaultValue = inputToEdit.max;
|
||||
}
|
||||
// options
|
||||
if (input.id === 'options') {
|
||||
input.defaultValue = inputToEdit.options ? inputToEdit.options.map(opt => opt.value) : [];
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const handleDeleteRequest = (inputID: string) => {
|
||||
const index = generatedFormInputs.value.findIndex(input => input.id === inputID);
|
||||
if (index > -1) {
|
||||
generatedFormInputs.value.splice(index, 1);
|
||||
}
|
||||
};
|
||||
|
||||
const handleMoveUp = (inputID: string) => {
|
||||
const index = generatedFormInputs.value.findIndex(input => input.id === inputID);
|
||||
if (index > 0) {
|
||||
const temp = generatedFormInputs.value[index];
|
||||
generatedFormInputs.value[index] = generatedFormInputs.value[index - 1];
|
||||
generatedFormInputs.value[index - 1] = temp;
|
||||
}
|
||||
};
|
||||
|
||||
const handleMoveDown = (inputID: string) => {
|
||||
const index = generatedFormInputs.value.findIndex(input => input.id === inputID);
|
||||
if (index < generatedFormInputs.value.length - 1) {
|
||||
const temp = generatedFormInputs.value[index];
|
||||
generatedFormInputs.value[index] = generatedFormInputs.value[index + 1];
|
||||
generatedFormInputs.value[index + 1] = temp;
|
||||
}
|
||||
};
|
||||
|
||||
const handleFormBuilderSubmit = (formData: Record<string, any>) => {
|
||||
const newField: FormInput = {
|
||||
id: formData.fieldID,
|
||||
@ -173,20 +263,62 @@ const handleFormBuilderSubmit = (formData: Record<string, any>) => {
|
||||
options: formData.options ? formData.options.map((opt: string) => ({ label: opt, value: opt })) : undefined
|
||||
};
|
||||
|
||||
generatedFormInputs.value.push(newField);
|
||||
// if field id already exists, replace the previous field otherwise add the new field
|
||||
const existingFieldIndex = generatedFormInputs.value.findIndex(field => field.id === newField.id);
|
||||
if (existingFieldIndex > -1) {
|
||||
generatedFormInputs.value.splice(existingFieldIndex, 1, newField);
|
||||
} else {
|
||||
generatedFormInputs.value.push(newField);
|
||||
}
|
||||
|
||||
// Reset form builder fields
|
||||
formBuilderFieldInputs.value.forEach(input => {
|
||||
if (input.type === 'checkbox' || input.type === 'multi-input') {
|
||||
formData[input.id] = [];
|
||||
if (input.id === 'showLabel') { // showLabel should default to true
|
||||
formData[input.id] = ['true'];
|
||||
}
|
||||
} else {
|
||||
formData[input.id] = '';
|
||||
resetBuilderFields();
|
||||
};
|
||||
|
||||
function resetBuilderFields() {
|
||||
formBuilderFieldInputs.value.forEach((input: FormInput, i) => {
|
||||
// fieldtype
|
||||
if (input.id === 'fieldType') {
|
||||
input.defaultValue = defaultFormInput.type;
|
||||
}
|
||||
// fieldlabel
|
||||
if (input.id === 'fieldLabel') {
|
||||
input.defaultValue = defaultFormInput.label;
|
||||
}
|
||||
// fieldid
|
||||
if (input.id === 'fieldID') {
|
||||
input.defaultValue = defaultFormInput.id;
|
||||
}
|
||||
// placeholder
|
||||
if (input.id === 'placeholder') {
|
||||
input.defaultValue = defaultFormInput.placeholder;
|
||||
}
|
||||
// required
|
||||
if (input.id === 'required') {
|
||||
input.defaultValue = defaultFormInput.required ? ['true'] : [];
|
||||
}
|
||||
// showlabel
|
||||
if (input.id === 'showLabel') {
|
||||
input.defaultValue = defaultFormInput.showLabel ? ['true'] : [];
|
||||
}
|
||||
// defaultvalue
|
||||
if (input.id === 'defaultValue') {
|
||||
input.defaultValue = defaultFormInput.defaultValue;
|
||||
}
|
||||
// min
|
||||
if (input.id === 'min') {
|
||||
input.defaultValue = defaultFormInput.min;
|
||||
}
|
||||
// max
|
||||
if (input.id === 'max') {
|
||||
input.defaultValue = defaultFormInput.max;
|
||||
}
|
||||
// options
|
||||
if (input.id === 'options') {
|
||||
input.defaultValue = defaultFormInput.options ? defaultFormInput.options.map(opt => opt.value) : [];
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
Reference in New Issue
Block a user