From 96fafd33d7fa5cf75ba21b0598c8b1b354b54ac8 Mon Sep 17 00:00:00 2001 From: Afiq Date: Tue, 27 May 2025 00:00:09 +0800 Subject: [PATCH] Add Switch Component and Enhance Form Builder Functionality - Introduced a new Switch component for toggling options within forms, enhancing user interactivity. - Updated formkit-custom.js to include the Switch component with appropriate props. - Enhanced formkit-theme.js to define styles for the Switch component, ensuring consistent theming. - Added CSS styles for the Switch component to improve visual presentation and user experience. - Updated FormBuilderCanvas and FormBuilderComponents to support the new Switch component in the form builder interface. - Enhanced documentation to include details about the new Switch component and its usage within forms. --- assets/js/formkit-custom.js | 4 + assets/js/formkit-theme.js | 8 + assets/style/css/form/switch.css | 15 + components/ComponentPreview.vue | 478 +++- components/FormBuilderCanvas.vue | 34 +- components/FormBuilderComponents.vue | 326 ++- components/FormBuilderConfiguration.vue | 360 ++- components/FormBuilderFieldSettingsModal.vue | 1319 ++++++++++ components/FormTemplatesModal.vue | 2326 ++++++++++++++++++ components/RsModal.vue | 100 +- components/formkit/Switch.vue | 41 + doc/form-builder/TECHNICAL_GUIDE.md | 1380 +++++++---- doc/form-builder/USER_GUIDE.md | 502 ++-- doc/form-builder/grid-system.md | 587 +++++ pages/form-builder/index.vue | 2077 +++++++++++++++- public/images/form-template-placeholder.svg | 7 + stores/formBuilder.js | 231 +- 17 files changed, 8996 insertions(+), 799 deletions(-) create mode 100644 assets/style/css/form/switch.css create mode 100644 components/FormBuilderFieldSettingsModal.vue create mode 100644 components/FormTemplatesModal.vue create mode 100644 components/formkit/Switch.vue create mode 100644 doc/form-builder/grid-system.md create mode 100644 public/images/form-template-placeholder.svg diff --git a/assets/js/formkit-custom.js b/assets/js/formkit-custom.js index 93cdfb7..18c06fd 100644 --- a/assets/js/formkit-custom.js +++ b/assets/js/formkit-custom.js @@ -2,6 +2,7 @@ import { createInput } from "@formkit/vue"; import OneTimePassword from "~/components/formkit/OneTimePassword.vue"; import MaskText from "~/components/formkit/TextMask.vue"; import FileDropzone from "~/components/formkit/FileDropzone.vue"; +import Switch from "~/components/formkit/Switch.vue"; export default { otp: createInput(OneTimePassword, { @@ -13,4 +14,7 @@ export default { dropzone: createInput(FileDropzone, { props: ["accept", "multiple", "maxSize", "minSize", "maxFiles", "disabled"], }), + switch: createInput(Switch, { + props: ["value"], + }), }; diff --git a/assets/js/formkit-theme.js b/assets/js/formkit-theme.js index dd57e22..f664a52 100644 --- a/assets/js/formkit-theme.js +++ b/assets/js/formkit-theme.js @@ -29,6 +29,13 @@ const OtpClassification = { message: "formkit-message-otp", }; +const switchClassification = { + label: "formkit-label-switch", + inner: "formkit-inner-switch", + input: "formkit-input-switch", + message: "formkit-message-switch", +}; + const colorClassification = { label: "formkit-label-color", input: "formkit-input-color", @@ -86,6 +93,7 @@ export default { week: textClassification, otp: OtpClassification, mask: textClassification, + switch: switchClassification, dropzone: { ...textClassification, inner: "formkit-inner-dropzone", diff --git a/assets/style/css/form/switch.css b/assets/style/css/form/switch.css new file mode 100644 index 0000000..69b5eb8 --- /dev/null +++ b/assets/style/css/form/switch.css @@ -0,0 +1,15 @@ +.formkit-label-switch { + @apply block mb-2 font-semibold text-sm formkit-invalid:text-red-500 dark:formkit-invalid:text-danger; +} + +.formkit-inner-switch { + @apply flex items-center justify-between w-full; +} + +.formkit-input-switch { + @apply sr-only; +} + +.formkit-message-switch { + @apply formkit-invalid:text-red-500 dark:formkit-invalid:text-danger; +} \ No newline at end of file diff --git a/components/ComponentPreview.vue b/components/ComponentPreview.vue index 1252adf..8503ff0 100644 --- a/components/ComponentPreview.vue +++ b/components/ComponentPreview.vue @@ -5,9 +5,30 @@ :data-name="component.props.name" :data-type="component.type" > + +
+
+
+ + Hidden Field +
+
+
+ Name: {{ component.props.name || 'unnamed' }} +
+
+ Value: {{ component.props.value || '(empty)' }} +
+
+
+ {{ component.props.help }} +
+
+
+ + +
+
+ + +
+ +
+ +
+
+ +
+ {{ component.props.caption }} +
+ +
+ {{ component.props.help }} +
+
+
+ + +
+
+ {{ component.props.label || 'Group' }} + +
+ {{ component.props.help }} +
+ + +
+
+
+

Item 1

+ +
+ +
+ +
+
+ + +
+ + +
+
+
+

Item {{ groupIndex + 1 }}

+ +
+ +
+ +
+
+ + +
+
+
+ + +
+
+ + +
+ {{ component.props.help }} +
+ +
+
+
+ + +
+ + + +
+ +
+
+ + +
+ + +
+
+
+
+ + +
+ + + + {{ component.props.label || 'Button' }} + + +
+ {{ component.props.help }} +
+
+ + +
Unknown component type: {{ component.type }}
@@ -112,6 +358,9 @@ \ No newline at end of file diff --git a/components/FormBuilderConfiguration.vue b/components/FormBuilderConfiguration.vue index 918880d..7085561 100644 --- a/components/FormBuilderConfiguration.vue +++ b/components/FormBuilderConfiguration.vue @@ -116,6 +116,237 @@ help="Size of the heading" /> + + + + + + + + +
@@ -174,6 +405,61 @@ help="File types, e.g. '.jpg,.png,image/*'" /> + + + + + + + + + @@ -592,7 +878,7 @@ const showField = (fieldName) => { case 'name': return !['heading', 'paragraph', 'divider'].includes(componentType); case 'placeholder': - return ['text', 'textarea', 'email', 'password', 'number', 'select'].includes(componentType); + return ['text', 'textarea', 'email', 'password', 'number', 'select', 'dynamic-list'].includes(componentType); case 'help': return !['heading', 'paragraph', 'divider'].includes(componentType); case 'value': @@ -602,17 +888,54 @@ const showField = (fieldName) => { case 'options': return ['select', 'radio', 'checkbox'].includes(componentType); case 'accept': - return componentType === 'file'; + return componentType === 'file' || componentType === 'dropzone'; + case 'mask': + return componentType === 'mask'; + case 'digits': + return componentType === 'otp'; + case 'maxSize': + case 'maxFiles': + case 'multiple': + return componentType === 'dropzone'; case 'id': return true; // Always show component ID in advanced tab case 'width': return true; // Always show width in basic tab case 'title': return componentType === 'info-display'; + case 'imageUrl': + case 'altText': + case 'caption': + case 'showZoom': + case 'showCaption': + case 'maxWidth': + case 'height': + return componentType === 'image-preview'; + case 'minItems': + case 'maxItems': + return ['repeating-group', 'dynamic-list'].includes(componentType); + case 'buttonText': + case 'removeText': + return componentType === 'repeating-group' || componentType === 'dynamic-list'; + case 'defaultItems': + return componentType === 'dynamic-list'; + case 'fields': + return componentType === 'info-display' || componentType === 'repeating-group'; case 'layout': case 'showBorder': - case 'fields': return componentType === 'info-display'; + case 'min': + case 'max': + case 'step': + return componentType === 'range' || componentType === 'number'; + case 'buttonType': + case 'variant': + case 'size': + case 'disabled': + case 'onClick': + return componentType === 'button'; + case 'value': + return ['color', 'range', 'switch', 'hidden'].includes(componentType); default: return false; } @@ -653,28 +976,55 @@ const removeInfoField = (index) => { configModel.value.fields.splice(index, 1); }; +// Add a new field to repeating group component +const addGroupField = () => { + if (!configModel.value.fields) { + configModel.value.fields = []; + } + + configModel.value.fields.push({ + type: 'text', + name: `field_${configModel.value.fields.length + 1}`, + label: `Field ${configModel.value.fields.length + 1}`, + placeholder: 'Enter value' + }); +}; + // Inside the + + \ No newline at end of file diff --git a/components/FormTemplatesModal.vue b/components/FormTemplatesModal.vue new file mode 100644 index 0000000..ee8bda2 --- /dev/null +++ b/components/FormTemplatesModal.vue @@ -0,0 +1,2326 @@ + + + + + \ No newline at end of file diff --git a/components/RsModal.vue b/components/RsModal.vue index c3d8471..231be34 100644 --- a/components/RsModal.vue +++ b/components/RsModal.vue @@ -117,14 +117,8 @@ watch( >