add form properties and tailwind script

This commit is contained in:
2024-12-30 23:19:22 -07:00
parent d95928f3e8
commit 3ccab8ebed
6 changed files with 169 additions and 78 deletions

View File

@ -1,7 +1,8 @@
<template>
<form @submit.prevent="handleSubmit" class="space-y-2 p-6 bg-white shadow-lg rounded-lg max-w-md mx-auto">
<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">
<!-- if showLabel is undefined or set to true then show the label-->
<!-- 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>
<!-- Text, Email, Password, Number -->
@ -95,13 +96,13 @@
</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">
Submit
{{ !!submitLabel ? submitLabel : 'Submit' }}
</button>
</form>
</template>
<script lang="ts" setup>
import { reactive, ref, defineEmits } from 'vue';
import { reactive, ref } from 'vue';
export interface FormInput {
id: string;
@ -116,7 +117,7 @@ export interface FormInput {
options?: { label: string, value: string }[]; // For select, radio, and checkbox types
}
const props = defineProps<{ formInputs: FormInput[] }>();
const props = defineProps<{ formInputs: FormInput[], submitLabel?: string, formTitle?: string }>();
const emit = defineEmits(['send'])
const formData = reactive<Record<string, any>>({});

View File

@ -2,12 +2,33 @@
<div class="min-h-screen bg-gray-100 flex flex-row items-start p-4">
<div class="flex-col flex-1 items-center justify-center p-4">
<h2 class="text-xl font-bold mb-4">Form Builder</h2>
<DynamicForm :formInputs="formBuilderInputs" @send="handleFormBuilderSubmit" />
<div>
<DynamicForm
class="space-y-2 p-6 bg-white shadow-lg rounded-lg max-w-md"
:formInputs="formBuilderPropertiesInputs"
@send="handleFormPropertiesSubmit"
form-title="Form Properties"
submit-label="Update Form"
/>
</div>
<div class="mt-6">
<DynamicForm
class="space-y-2 p-6 bg-white shadow-lg rounded-lg max-w-md"
:formInputs="formBuilderFieldInputs"
@send="handleFormBuilderSubmit"
form-title="Add a new field"
/>
</div>
</div>
<div v-if="generatedFormInputs.length" class="flex-col flex-1 items-center justify-center p-4">
<h2 class="text-xl font-bold mb-4">Generated Form</h2>
<DynamicForm :formInputs="generatedFormInputs" />
<DynamicForm
class="space-y-2 p-6 bg-white shadow-lg rounded-lg max-w-md"
:formInputs="generatedFormInputs"
:submitLabel="submitLabel"
:formTitle="formTitle"
/>
</div>
</div>
</template>
@ -17,7 +38,32 @@ import { ref } from 'vue';
import DynamicForm from './DynamicForm.vue';
import type {FormInput} from "@/components/DynamicForm.vue";
const formBuilderInputs = ref<FormInput[]>([
const formBuilderPropertiesInputs = ref<FormInput[]>([
{
id: 'formTitle',
label: 'Form Title',
type: 'text',
required: false
},
{
id: 'formDescription',
label: 'Form Description',
type: 'textarea',
required: false
},
{
id: 'submitLabel',
label: 'Submit Button Label',
type: 'text',
required: false
}
]);
const formTitle = ref('');
const formDescription = ref('');
const submitLabel = ref('');
const formBuilderFieldInputs = ref<FormInput[]>([
{
id: 'fieldType',
label: 'Field Type',
@ -107,6 +153,12 @@ const formBuilderInputs = ref<FormInput[]>([
const generatedFormInputs = ref<FormInput[]>([]);
const handleFormPropertiesSubmit = (formData: Record<string, any>) => {
formTitle.value = formData.formTitle;
formDescription.value = formData.formDescription;
submitLabel.value = formData.submitLabel;
};
const handleFormBuilderSubmit = (formData: Record<string, any>) => {
const newField: FormInput = {
id: formData.fieldID,
@ -124,7 +176,7 @@ const handleFormBuilderSubmit = (formData: Record<string, any>) => {
generatedFormInputs.value.push(newField);
// Reset form builder fields
formBuilderInputs.value.forEach(input => {
formBuilderFieldInputs.value.forEach(input => {
if (input.type === 'checkbox' || input.type === 'multi-input') {
formData[input.id] = [];
if (input.id === 'showLabel') { // showLabel should default to true

View File

@ -562,33 +562,20 @@ video {
position: relative;
}
.mx-auto {
margin-left: auto;
margin-right: auto;
}
.mb-2 {
margin-bottom: 0.5rem;
}
.ml-2 {
margin-left: 0.5rem;
}
.mb-4 {
margin-bottom: 1rem;
}
.mb-6 {
margin-bottom: 1.5rem;
.ml-2 {
margin-left: 0.5rem;
}
.mt-12 {
margin-top: 3rem;
}
.block {
display: block;
.mt-6 {
margin-top: 1.5rem;
}
.flex {
@ -615,10 +602,6 @@ video {
flex: 1 1 0%;
}
.flex-grow {
flex-grow: 1;
}
.flex-row {
flex-direction: row;
}
@ -627,10 +610,6 @@ video {
flex-direction: column;
}
.flex-wrap {
flex-wrap: wrap;
}
.items-start {
align-items: flex-start;
}
@ -639,20 +618,10 @@ video {
align-items: center;
}
.justify-start {
justify-content: flex-start;
}
.justify-center {
justify-content: center;
}
.space-y-6 > :not([hidden]) ~ :not([hidden]) {
--tw-space-y-reverse: 0;
margin-top: calc(1.5rem * calc(1 - var(--tw-space-y-reverse)));
margin-bottom: calc(1.5rem * var(--tw-space-y-reverse));
}
.space-x-2 > :not([hidden]) ~ :not([hidden]) {
--tw-space-x-reverse: 0;
margin-right: calc(0.5rem * var(--tw-space-x-reverse));
@ -687,36 +656,26 @@ video {
background-color: rgb(37 99 235 / var(--tw-bg-opacity, 1));
}
.bg-white {
--tw-bg-opacity: 1;
background-color: rgb(255 255 255 / var(--tw-bg-opacity, 1));
}
.bg-gray-100 {
--tw-bg-opacity: 1;
background-color: rgb(243 244 246 / var(--tw-bg-opacity, 1));
}
.bg-white {
--tw-bg-opacity: 1;
background-color: rgb(255 255 255 / var(--tw-bg-opacity, 1));
}
.p-3 {
padding: 0.75rem;
}
.p-6 {
padding: 1.5rem;
}
.p-4 {
padding: 1rem;
}
.px-6 {
padding-left: 1.5rem;
padding-right: 1.5rem;
}
.py-3 {
padding-top: 0.75rem;
padding-bottom: 0.75rem;
.p-6 {
padding: 1.5rem;
}
.px-3 {
@ -724,14 +683,19 @@ video {
padding-right: 0.75rem;
}
.px-6 {
padding-left: 1.5rem;
padding-right: 1.5rem;
}
.py-2 {
padding-top: 0.5rem;
padding-bottom: 0.5rem;
}
.text-2xl {
font-size: 1.5rem;
line-height: 2rem;
.py-3 {
padding-top: 0.75rem;
padding-bottom: 0.75rem;
}
.text-xl {
@ -739,22 +703,12 @@ video {
line-height: 1.75rem;
}
.font-semibold {
font-weight: 600;
}
.font-bold {
font-weight: 700;
}
.text-gray-700 {
--tw-text-opacity: 1;
color: rgb(55 65 81 / var(--tw-text-opacity, 1));
}
.text-white {
--tw-text-opacity: 1;
color: rgb(255 255 255 / var(--tw-text-opacity, 1));
.font-semibold {
font-weight: 600;
}
.text-blue-600 {
@ -762,11 +716,21 @@ video {
color: rgb(37 99 235 / var(--tw-text-opacity, 1));
}
.text-gray-700 {
--tw-text-opacity: 1;
color: rgb(55 65 81 / var(--tw-text-opacity, 1));
}
.text-red-600 {
--tw-text-opacity: 1;
color: rgb(220 38 38 / var(--tw-text-opacity, 1));
}
.text-white {
--tw-text-opacity: 1;
color: rgb(255 255 255 / var(--tw-text-opacity, 1));
}
.shadow-lg {
--tw-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
--tw-shadow-colored: 0 10px 15px -3px var(--tw-shadow-color), 0 4px 6px -4px var(--tw-shadow-color);