Layout Demonstration - All Types

Single form showcasing Vertical, Horizontal, Tabbed, and List layouts Async

Debug panel (development only)
<form id=""
      class="pydantic-form needs-validation"
      style=""
      method="POST"
      action="/layouts?style=bootstrap"
      novalidate>
    
    
<div class="tab-layout nav-tabs-wrapper layout-tabbed-section" style="">
    <ul class="nav nav-tabs" role="tablist">
        
<li class="nav-item" role="presentation">
    <button class="nav-link active"
            id="tab-0-tab"
            data-bs-toggle="tab"
            data-bs-target="#tab-0"
            type="button"
            role="tab"
            aria-controls="tab-0"
            aria-selected="true">
        Personal Info
    </button>
</li>


<li class="nav-item" role="presentation">
    <button class="nav-link"
            id="tab-1-tab"
            data-bs-toggle="tab"
            data-bs-target="#tab-1"
            type="button"
            role="tab"
            aria-controls="tab-1"
            aria-selected="false">
        Contact Info
    </button>
</li>


<li class="nav-item" role="presentation">
    <button class="nav-link"
            id="tab-2-tab"
            data-bs-toggle="tab"
            data-bs-target="#tab-2"
            type="button"
            role="tab"
            aria-controls="tab-2"
            aria-selected="false">
        Preferences
    </button>
</li>


<li class="nav-item" role="presentation">
    <button class="nav-link"
            id="tab-3-tab"
            data-bs-toggle="tab"
            data-bs-target="#tab-3"
            type="button"
            role="tab"
            aria-controls="tab-3"
            aria-selected="false">
        Task List
    </button>
</li>

    </ul>
    <div class="tab-content">
        
<div class="tab-pane fade show active"
     id="tab-0"
     role="tabpanel"
     aria-labelledby="tab-0-tab">
    
<section class="layout-field-section card shadow-sm mb-4">
    <div class="card-header bg-body-tertiary">
        <h3 class="card-title h5 mb-0">Personal Info</h3>
    </div>
    <div class="card-body">
        
<p class="text-muted mb-2 layout-field-help">Vertical layout demonstration</p>

        <div class="layout-field-content">
            <div class="mb-3"><label for="first_name"><i class="bi bi-person"></i> First Name *</label>
<input name="first_name" id="first_name" class="form-control" value="Alex" required="required" minlength="2" maxlength="11" placeholder="Enter your first name" pattern="\d{3}-\d{2}-\d{4}" inputmode="numeric" autocomplete="off" type="text" />
<div class="form-text">Your given name</div></div>
<div class="mb-3"><label for="last_name"><i class="bi bi-person"></i> Last Name *</label>
<input name="last_name" id="last_name" class="form-control" value="Johnson" required="required" minlength="2" maxlength="11" placeholder="Enter your last name" pattern="\d{3}-\d{2}-\d{4}" inputmode="numeric" autocomplete="off" type="text" />
<div class="form-text">Your family name</div></div>
<div class="mb-3"><label for="email"><i class="bi bi-envelope"></i> Email Address *</label>
<input name="email" id="email" class="form-control" value="alex.johnson@example.com" required="required" placeholder="your.email@example.com" inputmode="email" type="email" />
<div class="form-text">Your email address</div></div>
<div class="mb-3"><label for="birth_date"><i class="bi bi-calendar-date"></i> Date of Birth</label>
<div class="birthdate-input-group"><input name="birth_date" id="birth_date" class="form-control" value="1990-05-15" max="2026-01-07" min="1876-01-01" type="date" />
            <div class="age-display" id="birth_date_age" style="margin-top: 5px; font-size: 0.9em; color: #666;"></div>
            <script>
            document.addEventListener('DOMContentLoaded', function() {
                const birthdateField = document.getElementById('birth_date');
                const ageDisplay = document.getElementById('birth_date_age');

                function calculateAge() {
                    if (birthdateField.value && ageDisplay) {
                        const birthDate = new Date(birthdateField.value);
                        const today = new Date();
                        let age = today.getFullYear() - birthDate.getFullYear();
                        const monthDiff = today.getMonth() - birthDate.getMonth();

                        if (monthDiff < 0 || (monthDiff === 0 && today.getDate() < birthDate.getDate())) {
                            age--;
                        }

                        ageDisplay.textContent = age >= 0 ? `Age: ${age} years` : '';
                    }
                }

                if (birthdateField) {
                    birthdateField.addEventListener('change', calculateAge);
                    calculateAge(); // Calculate on load if value is set
                }
            });
            </script>
            </div>
<div class="form-text">Your birth date (optional)</div></div>
        </div>
    </div>
</section>

</div>


<div class="tab-pane fade"
     id="tab-1"
     role="tabpanel"
     aria-labelledby="tab-1-tab">
    
<section class="layout-field-section card shadow-sm mb-4">
    <div class="card-header bg-body-tertiary">
        <h3 class="card-title h5 mb-0">Contact Info</h3>
    </div>
    <div class="card-body">
        
<p class="text-muted mb-2 layout-field-help">Horizontal layout demonstration</p>

        <div class="layout-field-content">
            <div class="mb-3"><label for="phone"><i class="bi bi-telephone"></i> Phone Number</label>
<input name="phone" id="phone" class="form-control" value="+1 (555) 987-6543" placeholder="+1 (555) 123-4567" inputmode="tel" autocomplete="tel" type="tel" />
<div class="form-text">Your contact phone number</div></div>
<div class="mb-3"><label for="address"><i class="bi bi-house"></i> Street Address *</label>
<input name="address" id="address" class="form-control" value="456 Demo Street" required="required" maxlength="11" placeholder="123 Main Street" pattern="\d{3}-\d{2}-\d{4}" inputmode="numeric" autocomplete="off" type="text" />
<div class="form-text">Your street address</div></div>
<div class="mb-3"><label for="city"><i class="bi bi-building"></i> City *</label>
<input name="city" id="city" class="form-control" value="San Francisco" required="required" maxlength="11" placeholder="Your city" pattern="\d{3}-\d{2}-\d{4}" inputmode="numeric" autocomplete="off" type="text" />
<div class="form-text">City where you live</div></div>
<div class="mb-3"><label for="postal_code"><i class="bi bi-mailbox"></i> Postal Code</label>
<input name="postal_code" id="postal_code" class="form-control" value="94102" placeholder="12345" pattern="\d{3}-\d{2}-\d{4}" maxlength="11" inputmode="numeric" autocomplete="off" type="text" />
<div class="form-text">ZIP or postal code</div></div>
        </div>
    </div>
</section>

</div>


<div class="tab-pane fade"
     id="tab-2"
     role="tabpanel"
     aria-labelledby="tab-2-tab">
    
<section class="layout-field-section card shadow-sm mb-4">
    <div class="card-header bg-body-tertiary">
        <h3 class="card-title h5 mb-0">Preferences</h3>
    </div>
    <div class="card-body">
        
<p class="text-muted mb-2 layout-field-help">Tabbed layout demonstration</p>

        <div class="layout-field-content">
            <div class="mb-3"><label for="notification_email"><i class="bi bi-envelope"></i> Email Notifications</label>
<input name="notification_email" id="notification_email" class="form-check-input" checked="checked" value="1" type="checkbox" />
<div class="form-text">Receive notifications via email</div></div>
<div class="mb-3"><label for="notification_sms"><i class="bi bi-phone"></i> SMS Notifications</label>
<input name="notification_sms" id="notification_sms" class="form-check-input" value="1" type="checkbox" />
<div class="form-text">Receive notifications via SMS</div></div>
<div class="mb-3"><div class="mb-3">
<label for="theme" class="form-label">UI Theme</label>
<div class="input-group">
<span class="input-group-text"><i class="bi bi-bi bi-palette"></i></span>
<select name="theme" id="theme" class="form-select"><option value="light">β˜€οΈ Light Theme</option>
<option value="dark" selected>πŸŒ™ Dark Theme</option>
<option value="auto">πŸ”„ Auto (System)</option></select>
</div>
<div class="form-text">Choose your preferred theme</div>
</div></div>
<div class="mb-3"><div class="mb-3">
<label for="language" class="form-label">Language</label>
<div class="input-group">
<span class="input-group-text"><i class="bi bi-bi bi-globe"></i></span>
<select name="language" id="language" class="form-select"><option value="en" selected>πŸ‡ΊπŸ‡Έ English</option>
<option value="es">πŸ‡ͺπŸ‡Έ Spanish</option>
<option value="fr">πŸ‡«πŸ‡· French</option>
<option value="de">πŸ‡©πŸ‡ͺ German</option></select>
</div>
<div class="form-text">Select your preferred language</div>
</div></div>
        </div>
    </div>
</section>

</div>


<div class="tab-pane fade"
     id="tab-3"
     role="tabpanel"
     aria-labelledby="tab-3-tab">
    
<section class="layout-field-section card shadow-sm mb-4">
    <div class="card-header bg-body-tertiary">
        <h3 class="card-title h5 mb-0">Task List</h3>
    </div>
    <div class="card-body">
        
<p class="text-muted mb-2 layout-field-help">List layout demonstration</p>

        <div class="layout-field-content">
            <div class="mb-3"><label for="project_name"><i class="bi bi-folder"></i> Project Name *</label>
<input name="project_name" id="project_name" class="form-control" value="Website Redesign Project" required="required" minlength="2" maxlength="11" placeholder="Enter project name" pattern="\d{3}-\d{2}-\d{4}" inputmode="numeric" autocomplete="off" type="text" />
<div class="form-text">Name of the project or task collection</div></div>

<div class="mb-3 model-list-block" data-field-name="tasks" data-min-items="1" data-max-items="10">
    <label class="form-label fw-bold">Task List <span class="text-danger">*</span></label>
    <div class="model-list-container" data-field-name="tasks" data-min-items="1" data-max-items="10">
        <div class="model-list-items" id="tasks-items">
        <div class="model-list-item card border mb-3"
             data-index="0"
             data-title-template="Item #{index}"
             data-field-name="tasks">
            <div class="card-header d-flex justify-content-between align-items-center">
                <h6 class="mb-0">
                    <button class="btn btn-link text-decoration-none p-0 text-start"
                            type="button"
                            data-bs-toggle="collapse"
                            data-bs-target="#tasks_item_0_content"
                            aria-expanded="true"
                            aria-controls="tasks_item_0_content">
                        <i class="bi bi-chevron-down me-2"></i>
                        <i class="bi bi-card-list me-2"></i>
                        Item #1
                    </button>
                </h6>
                <button type="button"
                        class="btn btn-outline-danger btn-sm remove-item-btn"
                        data-index="0"
                        data-field-name="tasks"
                        title="Remove this item">
                    <i class="bi bi-trash"></i>
                </button>
            </div>
            <div class="collapse  show" id="tasks_item_0_content">
                <div class="card-body"><div class="row">
                <div class="col-md-6">
                    <div class="mb-3"><label for="tasks[0].task_name"><i class="bi bi-check-square"></i> Task Description</label>
<input name="tasks[0].task_name" id="tasks[0].task_name" class="form-control" value="Complete project setup and requirements gathering" minlength="1" maxlength="11" placeholder="Enter task description..." pattern="\d{3}-\d{2}-\d{4}" inputmode="numeric" autocomplete="off" type="text" />
<div class="form-text">What needs to be done?</div></div>
                </div>
                <div class="col-md-6">
                    <div class="mb-3"><div class="mb-3">
<label for="tasks[0].priority" class="form-label">Priority</label>
<div class="input-group">
<span class="input-group-text"><i class="bi bi-bi bi-exclamation-triangle"></i></span>
<select name="tasks[0].priority" id="tasks[0].priority" class="form-select"><option value="low">🟒 Low</option>
<option value="medium">🟑 Medium</option>
<option value="high" selected>🟠 High</option>
<option value="urgent">πŸ”΄ Urgent</option></select>
</div>
<div class="form-text">How important is this task?</div>
</div></div>
                </div>
                <div class="col-md-6">
                    <div class="mb-3"><label for="tasks[0].due_date"><i class="bi bi-calendar-date"></i> Due Date</label>
<div class="birthdate-input-group"><input name="tasks[0].due_date" id="tasks[0].due_date" class="form-control" value="2024-12-01" max="2026-01-07" min="1876-01-01" type="date" />
            <div class="age-display" id="tasks[0].due_date_age" style="margin-top: 5px; font-size: 0.9em; color: #666;"></div>
            <script>
            document.addEventListener('DOMContentLoaded', function() {
                const birthdateField = document.getElementById('tasks[0].due_date');
                const ageDisplay = document.getElementById('tasks[0].due_date_age');

                function calculateAge() {
                    if (birthdateField.value && ageDisplay) {
                        const birthDate = new Date(birthdateField.value);
                        const today = new Date();
                        let age = today.getFullYear() - birthDate.getFullYear();
                        const monthDiff = today.getMonth() - birthDate.getMonth();

                        if (monthDiff < 0 || (monthDiff === 0 && today.getDate() < birthDate.getDate())) {
                            age--;
                        }

                        ageDisplay.textContent = age >= 0 ? `Age: ${age} years` : '';
                    }
                }

                if (birthdateField) {
                    birthdateField.addEventListener('change', calculateAge);
                    calculateAge(); // Calculate on load if value is set
                }
            });
            </script>
            </div>
<div class="form-text">When should this be completed? (optional)</div></div>
                </div>
                <div class="col-md-6">
                    <div class="mb-3"><label for="tasks[0].completed"><i class="bi bi-check"></i> Completed</label>
<input name="tasks[0].completed" id="tasks[0].completed" class="form-check-input" value="1" type="checkbox" />
<div class="form-text">Is this task done?</div></div>
                </div></div>
                </div>
            </div>
        </div>
        <div class="model-list-item card border mb-3"
             data-index="1"
             data-title-template="Item #{index}"
             data-field-name="tasks">
            <div class="card-header d-flex justify-content-between align-items-center">
                <h6 class="mb-0">
                    <button class="btn btn-link text-decoration-none p-0 text-start"
                            type="button"
                            data-bs-toggle="collapse"
                            data-bs-target="#tasks_item_1_content"
                            aria-expanded="true"
                            aria-controls="tasks_item_1_content">
                        <i class="bi bi-chevron-down me-2"></i>
                        <i class="bi bi-card-list me-2"></i>
                        Item #2
                    </button>
                </h6>
                <button type="button"
                        class="btn btn-outline-danger btn-sm remove-item-btn"
                        data-index="1"
                        data-field-name="tasks"
                        title="Remove this item">
                    <i class="bi bi-trash"></i>
                </button>
            </div>
            <div class="collapse  show" id="tasks_item_1_content">
                <div class="card-body"><div class="row">
                <div class="col-md-6">
                    <div class="mb-3"><label for="tasks[1].task_name"><i class="bi bi-check-square"></i> Task Description</label>
<input name="tasks[1].task_name" id="tasks[1].task_name" class="form-control" value="Design mockups and wireframes" minlength="1" maxlength="11" placeholder="Enter task description..." pattern="\d{3}-\d{2}-\d{4}" inputmode="numeric" autocomplete="off" type="text" />
<div class="form-text">What needs to be done?</div></div>
                </div>
                <div class="col-md-6">
                    <div class="mb-3"><div class="mb-3">
<label for="tasks[1].priority" class="form-label">Priority</label>
<div class="input-group">
<span class="input-group-text"><i class="bi bi-bi bi-exclamation-triangle"></i></span>
<select name="tasks[1].priority" id="tasks[1].priority" class="form-select"><option value="low">🟒 Low</option>
<option value="medium" selected>🟑 Medium</option>
<option value="high">🟠 High</option>
<option value="urgent">πŸ”΄ Urgent</option></select>
</div>
<div class="form-text">How important is this task?</div>
</div></div>
                </div>
                <div class="col-md-6">
                    <div class="mb-3"><label for="tasks[1].due_date"><i class="bi bi-calendar-date"></i> Due Date</label>
<div class="birthdate-input-group"><input name="tasks[1].due_date" id="tasks[1].due_date" class="form-control" value="2024-12-15" max="2026-01-07" min="1876-01-01" type="date" />
            <div class="age-display" id="tasks[1].due_date_age" style="margin-top: 5px; font-size: 0.9em; color: #666;"></div>
            <script>
            document.addEventListener('DOMContentLoaded', function() {
                const birthdateField = document.getElementById('tasks[1].due_date');
                const ageDisplay = document.getElementById('tasks[1].due_date_age');

                function calculateAge() {
                    if (birthdateField.value && ageDisplay) {
                        const birthDate = new Date(birthdateField.value);
                        const today = new Date();
                        let age = today.getFullYear() - birthDate.getFullYear();
                        const monthDiff = today.getMonth() - birthDate.getMonth();

                        if (monthDiff < 0 || (monthDiff === 0 && today.getDate() < birthDate.getDate())) {
                            age--;
                        }

                        ageDisplay.textContent = age >= 0 ? `Age: ${age} years` : '';
                    }
                }

                if (birthdateField) {
                    birthdateField.addEventListener('change', calculateAge);
                    calculateAge(); // Calculate on load if value is set
                }
            });
            </script>
            </div>
<div class="form-text">When should this be completed? (optional)</div></div>
                </div>
                <div class="col-md-6">
                    <div class="mb-3"><label for="tasks[1].completed"><i class="bi bi-check"></i> Completed</label>
<input name="tasks[1].completed" id="tasks[1].completed" class="form-check-input" value="1" type="checkbox" />
<div class="form-text">Is this task done?</div></div>
                </div></div>
                </div>
            </div>
        </div></div>
        <div class="model-list-controls mt-2">
            <button type="button" class="btn btn-outline-primary btn-sm add-item-btn" data-target="tasks">
                <i class="bi bi-plus-circle"></i> Add Item
            </button>
        </div>
    </div>
    
<div class="form-text text-muted model-list-help">Manage your tasks (dynamic list demonstration)</div>

    
</div>

        </div>
    </div>
</section>

</div>

    </div>
</div>

<script>
function switchTab(tabId, buttonElement) {
    const tabLayout = buttonElement.closest('.tab-layout');
    const panels = tabLayout.querySelectorAll('.tab-panel');
    const buttons = tabLayout.querySelectorAll('.tab-button');

    panels.forEach(panel => {
        panel.style.display = 'none';
        panel.setAttribute('aria-hidden', 'true');
    });

    buttons.forEach(button => {
        button.classList.remove('active');
        button.setAttribute('aria-selected', 'false');
    });

    const selectedPanel = document.getElementById(tabId);
    if (selectedPanel) {
        selectedPanel.style.display = 'block';
        selectedPanel.setAttribute('aria-hidden', 'false');
    }

    buttonElement.classList.add('active');
    buttonElement.setAttribute('aria-selected', 'true');
}
</script>
<style>
.tab-layout .tab-navigation {
    border-bottom: 2px solid #e0e0e0;
    margin-bottom: 1rem;
}
.tab-layout .tab-button {
    background: none;
    border: none;
    padding: 0.5rem 1rem;
    cursor: pointer;
    border-bottom: 2px solid transparent;
    margin-right: 0.5rem;
}
.tab-layout .tab-button:hover {
    background-color: #f5f5f5;
}
.tab-layout .tab-button.active {
    border-bottom-color: #007bff;
    color: #007bff;
}
.tab-layout .tab-panel {
    display: none;
}
.tab-layout .tab-panel.active {
    display: block;
}
</style>


    
<button type="submit" class="btn btn-primary">Submit</button>

</form>


<script>
(function() {
    document.addEventListener('DOMContentLoaded', function() {
        // Prevent Enter key from submitting forms unless on submit button
        const forms = document.querySelectorAll('form.pydantic-form');
        forms.forEach(function(form) {
            form.addEventListener('keydown', function(e) {
                // Check if Enter key is pressed
                if (e.key === 'Enter' || e.keyCode === 13) {
                    const target = e.target;

                    // Allow Enter in textareas (for multi-line input)
                    if (target.tagName === 'TEXTAREA') {
                        return;
                    }

                    // Allow Enter on submit buttons
                    if (target.tagName === 'BUTTON' && target.type === 'submit') {
                        return;
                    }

                    // Allow Enter on input type="submit"
                    if (target.tagName === 'INPUT' && target.type === 'submit') {
                        return;
                    }

                    // Prevent form submission for all other cases
                    e.preventDefault();
                    return false;
                }
            });
        });
    });
})();
</script>
class LayoutDemonstrationForm(FormModel):
    """
    Create a tabbed layout using VerticalLayout, HorizontalLayout, TabbedLayout, and ListLayout as the tabs.

    This demonstrates how to use layout classes as field types in a Pydantic form.
    Each field represents a different layout type that can be rendered as a tab.
    """
    vertical_tab: VerticalFormLayout = FormField(
        VerticalFormLayout(),
        title="Personal Info",
        input_type="layout",
        help_text="Vertical layout demonstration"
    )

    horizontal_tab: HorizontalFormLayout = FormField(
        HorizontalFormLayout(),
        title="Contact Info",
        input_type="layout",
        help_text="Horizontal layout demonstration"
    )

    tabbed_tab: TabbedFormLayout = FormField(
        TabbedFormLayout(),
        title="Preferences",
        input_type="layout",
        help_text="Tabbed layout demonstration"
    )

    list_tab: ListFormLayout = FormField(
        ListFormLayout(),
        title="Task List",
        input_type="layout",
        help_text="List layout demonstration"
    )
{
  "description": "Create a tabbed layout using VerticalLayout, HorizontalLayout, TabbedLayout, and ListLayout as the tabs.\n\nThis demonstrates how to use layout classes as field types in a Pydantic form.\nEach field represents a different layout type that can be rendered as a tab.",
  "properties": {
    "vertical_tab": {
      "additionalProperties": true,
      "autofocus": false,
      "description": "Vertical layout demonstration",
      "disabled": false,
      "help_text": "Vertical layout demonstration",
      "input_type": "layout",
      "readonly": false,
      "title": "Personal Info",
      "type": "object"
    },
    "horizontal_tab": {
      "additionalProperties": true,
      "autofocus": false,
      "description": "Horizontal layout demonstration",
      "disabled": false,
      "help_text": "Horizontal layout demonstration",
      "input_type": "layout",
      "readonly": false,
      "title": "Contact Info",
      "type": "object"
    },
    "tabbed_tab": {
      "additionalProperties": true,
      "autofocus": false,
      "description": "Tabbed layout demonstration",
      "disabled": false,
      "help_text": "Tabbed layout demonstration",
      "input_type": "layout",
      "readonly": false,
      "title": "Preferences",
      "type": "object"
    },
    "list_tab": {
      "additionalProperties": true,
      "autofocus": false,
      "description": "List layout demonstration",
      "disabled": false,
      "help_text": "List layout demonstration",
      "input_type": "layout",
      "readonly": false,
      "title": "Task List",
      "type": "object"
    }
  },
  "title": "LayoutDemonstrationForm",
  "type": "object"
}
{
  "vertical_tab": {
    "required": false,
    "type": "object"
  },
  "horizontal_tab": {
    "required": false,
    "type": "object"
  },
  "tabbed_tab": {
    "required": false,
    "type": "object"
  },
  "list_tab": {
    "required": false,
    "type": "object"
  }
}
{
  "errors": {},
  "data": {
    "vertical_tab": {
      "first_name": "Alex",
      "last_name": "Johnson",
      "email": "alex.johnson@example.com",
      "birth_date": "1990-05-15"
    },
    "horizontal_tab": {
      "phone": "+1 (555) 987-6543",
      "address": "456 Demo Street",
      "city": "San Francisco",
      "postal_code": "94102"
    },
    "tabbed_tab": {
      "notification_email": true,
      "notification_sms": false,
      "theme": "dark",
      "language": "en"
    },
    "list_tab": {
      "project_name": "Website Redesign Project",
      "tasks": [
        {
          "task_name": "Complete project setup and requirements gathering",
          "priority": "high",
          "due_date": "2024-12-01",
          "completed": false
        },
        {
          "task_name": "Design mockups and wireframes",
          "priority": "medium",
          "due_date": "2024-12-15",
          "completed": false
        }
      ]
    }
  }
}
Try Different Styles
API Endpoints Available
Schema: GET /api/forms/layouts/schema
Render: GET /api/forms/layouts/render
Submit: POST /api/forms/layouts/submit