# Form Builder Node Creation Guide
This guide explains how to create new nodes/components for the form builder system. The form builder supports various types of components from simple text inputs to complex container components like repeating groups and layout grids.
## Table of Contents
1. [Overview](#overview)
2. [Component Types](#component-types)
3. [Basic Component Creation](#basic-component-creation)
4. [Container Component Creation](#container-component-creation)
5. [Complex Component Creation](#complex-component-creation)
6. [Component Registration](#component-registration)
7. [Settings Modal Integration](#settings-modal-integration)
8. [Component Preview Integration](#component-preview-integration)
9. [Drag & Drop Integration](#drag--drop-integration)
10. [Best Practices](#best-practices)
11. [Examples](#examples)
## Overview
The form builder system consists of several key files:
- **`FormBuilderComponents.vue`**: Defines available components and their metadata
- **`ComponentPreview.vue`**: Renders components in the form builder
- **`FormBuilderFieldSettingsModal.vue`**: Provides settings interface for components
- **`stores/formBuilder.js`**: Manages form state and component operations
## Component Types
### 1. Basic Components
Simple input components that collect data:
- Text inputs, textareas, numbers
- Select dropdowns, checkboxes, radio buttons
- Date/time pickers, file uploads
### 2. Container Components
Components that can hold other components:
- **Form Section**: Groups related fields
- **Repeating Group**: Allows multiple instances of the same fields
- **Layout Grid**: Custom grid layout with spanning capabilities
### 3. Advanced Components
Complex components with special functionality:
- Custom HTML with CSS/JS
- Conditional logic components
- API integration components
## Basic Component Creation
### Step 1: Define Component Metadata
Add your component to `FormBuilderComponents.vue`:
```javascript
{
type: 'my-component',
name: 'My Component',
category: 'Basic Inputs', // or 'Selection Inputs', 'Date and Time', 'Advanced', 'Layout'
icon: 'heroicons:document-text', // Use appropriate icon
description: 'Description of what this component does',
defaultProps: {
label: 'My Component',
name: 'my_component',
help: 'Help text for users',
required: false,
placeholder: 'Enter value...',
width: '100%',
gridColumn: 'span 6',
// Component-specific properties
myCustomProp: 'default value',
// Conditional Logic Properties
conditionalLogic: {
enabled: false,
conditions: [],
action: 'show',
operator: 'and'
}
}
}
```
### Step 2: Add Component Preview
Add rendering logic to `ComponentPreview.vue`:
```vue
{{ component.props.help }}
```
### Step 3: Add Settings Modal Support
Add settings to `FormBuilderFieldSettingsModal.vue`:
```javascript
// In the script section, add to getComponentTypeName function
getComponentTypeName(type) {
const typeNames = {
// ... existing types
'my-component': 'My Component'
}
return typeNames[type] || 'Unknown Component'
}
// Add to getComponentIcon function
getComponentIcon(type) {
const icons = {
// ... existing icons
'my-component': 'heroicons:document-text'
}
return icons[type] || 'heroicons:question-mark-circle'
}
// Add to getComponentDescription function
getComponentDescription(type) {
const descriptions = {
// ... existing descriptions
'my-component': 'A custom component for collecting specific data'
}
return descriptions[type] || 'Component description'
}
```
## Container Component Creation
Container components are more complex as they can hold other components.
### Example: Creating a Custom Container
```javascript
// In FormBuilderComponents.vue
{
type: 'custom-container',
name: 'Custom Container',
category: 'Layout',
icon: 'material-symbols:view-in-ar',
description: 'A custom container that can hold other components',
defaultProps: {
label: 'Custom Container',
name: 'custom_container',
help: 'Drag components here to add them',
showHeader: true,
headerBackground: '#f9fafb',
backgroundColor: '#ffffff',
showBorder: true,
borderStyle: 'solid', // 'solid', 'dashed', 'dotted'
spacing: 'normal', // 'compact', 'normal', 'relaxed'
children: [], // Array to hold nested components
// Conditional Logic Properties
conditionalLogic: {
enabled: false,
conditions: [],
action: 'show',
operator: 'and'
}
}
}
```
### Container Component Preview
```vue
{{ component.props.label || 'Custom Container' }}
{{ component.props.description }}
Drop Components Here
Drag form fields from the sidebar to add them to this container
```
## Complex Component Creation
### Example: Repeating Group Component
Repeating groups are complex container components that allow multiple instances of the same fields.
#### 1. Component Definition
```javascript
{
type: 'repeating-group',
name: 'Repeating Group',
category: 'Layout',
icon: 'material-symbols:view-in-ar',
description: 'Group of fields that can be repeated multiple times',
defaultProps: {
label: 'Repeating Group',
name: 'repeating_group',
help: 'Add multiple instances of the same fields',
buttonText: 'Add Item',
showPlaceholder: true,
children: [], // Array to hold nested components
// Conditional Logic Properties
conditionalLogic: {
enabled: false,
conditions: [],
action: 'show',
operator: 'and'
}
}
}
```
#### 2. Drag & Drop Integration
Container components need special drag & drop handling:
```javascript
// In ComponentPreview.vue, add these functions:
const handleSectionDrop = (event, containerId) => {
event.preventDefault();
event.stopPropagation();
// Reset drag state
if (sectionDropStates.value[containerId]) {
sectionDropStates.value[containerId].isDraggingOver = false;
}
try {
// Get the dropped component data
let componentData = null;
try {
componentData = JSON.parse(event.dataTransfer.getData('text/plain') || '{}');
} catch (parseError) {
componentData = window.__draggedComponentData || {};
}
if (!componentData.type) {
console.warn('No valid component data found in drop event');
return;
}
// Create a new component instance
const newComponent = {
id: `${componentData.type}_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
type: componentData.type,
name: componentData.name,
props: {
...componentData.defaultProps,
gridColumn: 'span 6',
width: '50%',
label: componentData.defaultProps.label || componentData.name || `${componentData.type.charAt(0).toUpperCase() + componentData.type.slice(1)} Field`,
name: componentData.defaultProps.name || `${componentData.type}_${Date.now()}`
}
};
// Find the target container
const container = findContainerRecursively(containerId);
if (container && (container.type === 'form-section' || container.type === 'repeating-group')) {
// Initialize children array if it doesn't exist
if (!container.props.children) {
container.props.children = [];
}
// Add the component to the container
container.props.children.push(newComponent);
// Update the container in the form store
formStore.updateComponent(container);
console.log('Component added to container:', newComponent);
} else {
console.warn('Container not found or invalid container type:', containerId);
}
} catch (error) {
console.error('Error dropping component into container:', error);
}
};
```
#### 3. Container Search Function
```javascript
// Helper function to find container at any nesting level
const findContainerRecursively = (containerId, components = formStore.formComponents, childId = null) => {
for (const component of components) {
// Check if this is the target container
if (containerId && component.id === containerId) {
return component;
}
// If searching for parent by child ID, check if this component contains the child
if (childId && component.props.children && Array.isArray(component.props.children)) {
const hasChild = component.props.children.some(child => child.id === childId);
if (hasChild) {
return component;
}
}
// If this component has children, search recursively
if (component.props.children && Array.isArray(component.props.children)) {
const found = findContainerRecursively(containerId, component.props.children, childId);
if (found) {
return found;
}
}
// Special handling for Layout Grid components - search in their cells
if (component.type === 'layout-grid' && component.props.cells) {
for (const cell of component.props.cells) {
if (cell.component) {
// Check if this cell's component is the target container
if (containerId && cell.component.id === containerId) {
return cell.component;
}
// If searching for parent by child ID, check if this cell's component contains the child
if (childId && cell.component.props.children && Array.isArray(cell.component.props.children)) {
const hasChild = cell.component.props.children.some(child => child.id === childId);
if (hasChild) {
return cell.component;
}
}
// Recursively search in the cell's component children
if (cell.component.props.children && Array.isArray(cell.component.props.children)) {
const found = findContainerRecursively(containerId, cell.component.props.children, childId);
if (found) {
return found;
}
}
}
}
}
}
return null;
};
```
## Component Registration
### 1. Add to Available Components
In `FormBuilderComponents.vue`, add your component to the `availableComponents` array:
```javascript
const availableComponents = [
// ... existing components
{
type: 'my-component',
name: 'My Component',
category: 'Basic Inputs',
icon: 'heroicons:document-text',
description: 'A custom component for collecting data',
defaultProps: {
// ... your default props
}
}
];
```
### 2. Add Category Support
If you're creating a new category, add it to the template:
```vue
My Category
{{ component.name }}
```
## Settings Modal Integration
### 1. Add Component Type Support
In `FormBuilderFieldSettingsModal.vue`:
```javascript
// Add to getComponentTypeName function
getComponentTypeName(type) {
const typeNames = {
// ... existing types
'my-component': 'My Component'
}
return typeNames[type] || 'Unknown Component'
}
// Add to getComponentIcon function
getComponentIcon(type) {
const icons = {
// ... existing icons
'my-component': 'heroicons:document-text'
}
return icons[type] || 'heroicons:question-mark-circle'
}
// Add to getComponentDescription function
getComponentDescription(type) {
const descriptions = {
// ... existing descriptions
'my-component': 'A custom component for collecting specific data'
}
return descriptions[type] || 'Component description'
}
```
### 2. Add Specific Settings
For components with specific settings, add them to the settings modal:
```vue
My Component Settings
Configure specific settings for this component
```
## Component Preview Integration
### 1. Add Preview Rendering
In `ComponentPreview.vue`, add your component's preview rendering:
```vue
{{ component.props.help }}
```
### 2. Handle Preview Mode
Make sure your component handles the `isPreview` prop correctly:
```vue