Single form showcasing Vertical, Horizontal, Tabbed, and List layouts Async
<style data-schemaforms-layout-support>
.pydantic-form,
.md-form {
width: 100%;
max-width: none;
}
.pydantic-form > div,
.pydantic-form > fieldset,
.pydantic-form > [class*="row"],
.md-form > div,
.md-form > fieldset,
.md-form > [class*="row"] {
width: 100% !important;
max-width: none !important;
}
.pydantic-form [class*="col-"],
.md-form [class*="col-"] {
min-width: 0;
flex: 1 1 auto !important;
}
.pydantic-form [class*="section"],
.pydantic-form [data-schemaforms-section],
.pydantic-form fieldset,
.md-form [class*="section"],
.md-form [data-schemaforms-section],
.md-form fieldset {
width: 100% !important;
max-width: none !important;
box-sizing: border-box;
}
</style>
<form id=""
class="pydantic-form "
style=""
method="POST"
action="/layouts?style=none"
novalidate>
<div class="tab-layout layout-tabbed-section" style="">
<div class="tab-navigation" role="tablist">
<button class="tab-button active"
type="button"
role="tab"
aria-selected="true"
aria-controls="tab-0"
onclick="switchTab('tab-0', this)">
Personal Info
</button>
<button class="tab-button"
type="button"
role="tab"
aria-selected="false"
aria-controls="tab-1"
onclick="switchTab('tab-1', this)">
Contact Info
</button>
<button class="tab-button"
type="button"
role="tab"
aria-selected="false"
aria-controls="tab-2"
onclick="switchTab('tab-2', this)">
Preferences
</button>
<button class="tab-button"
type="button"
role="tab"
aria-selected="false"
aria-controls="tab-3"
onclick="switchTab('tab-3', this)">
Task List
</button>
</div>
<div class="tab-content">
<div id="tab-0"
class="tab-panel active"
role="tabpanel"
style="display: block;"
aria-hidden="false">
<section class="layout-field-section">
<h3 class="layout-field-title">Personal Info</h3>
<p class="layout-field-help">Vertical layout demonstration</p>
<div class="layout-field-content">
<div class="field"><label for="first_name">First Name *</label>
<input name="first_name" id="first_name" value="Alex" required="required" minlength="2" maxlength="50" placeholder="Enter your first name" type="text" />
<div class="help-text">Your given name</div></div>
<div class="field"><label for="last_name">Last Name *</label>
<input name="last_name" id="last_name" value="Johnson" required="required" minlength="2" maxlength="50" placeholder="Enter your last name" type="text" />
<div class="help-text">Your family name</div></div>
<div class="field"><label for="email">Email Address *</label>
<input name="email" id="email" value="alex.johnson@example.com" required="required" placeholder="your.email@example.com" inputmode="email" type="email" />
<div class="help-text">Your email address</div></div>
<div class="field"><label for="birth_date">Date of Birth</label>
<div class="birthdate-input-group"><input name="birth_date" id="birth_date" value="1990-05-15" max="2026-03-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="help-text">Your birth date (optional)</div></div>
</div>
</section>
</div>
<div id="tab-1"
class="tab-panel"
role="tabpanel"
style="display: none;"
aria-hidden="true">
<section class="layout-field-section">
<h3 class="layout-field-title">Contact Info</h3>
<p class="layout-field-help">Horizontal layout demonstration</p>
<div class="layout-field-content">
<div class="field"><label for="phone">Phone Number</label>
<input name="phone" id="phone" value="+1 (555) 987-6543" placeholder="+1 (555) 123-4567" inputmode="tel" autocomplete="tel" type="tel" />
<div class="help-text">Your contact phone number</div></div>
<div class="field"><label for="address">Street Address *</label>
<input name="address" id="address" value="456 Demo Street" required="required" maxlength="200" placeholder="123 Main Street" type="text" />
<div class="help-text">Your street address</div></div>
<div class="field"><label for="city">City *</label>
<input name="city" id="city" value="San Francisco" required="required" maxlength="100" placeholder="Your city" type="text" />
<div class="help-text">City where you live</div></div>
<div class="field"><label for="postal_code">Postal Code</label>
<input name="postal_code" id="postal_code" value="94102" placeholder="12345" type="text" />
<div class="help-text">ZIP or postal code</div></div>
</div>
</section>
</div>
<div id="tab-2"
class="tab-panel"
role="tabpanel"
style="display: none;"
aria-hidden="true">
<section class="layout-field-section">
<h3 class="layout-field-title">Preferences</h3>
<p class="layout-field-help">Tabbed layout demonstration</p>
<div class="layout-field-content">
<div class="field"><label for="notification_email">Email Notifications</label>
<input name="notification_email" id="notification_email" checked="checked" value="1" type="checkbox" />
<div class="help-text">Receive notifications via email</div></div>
<div class="field"><label for="notification_sms">SMS Notifications</label>
<input name="notification_sms" id="notification_sms" value="1" type="checkbox" />
<div class="help-text">Receive notifications via SMS</div></div>
<div class="field"><div class="field">
<label for="theme">UI Theme</label>
<div class="input-with-icon"><span class="input-icon">palette</span>
<select name="theme" id="theme"><option value="light">βοΈ Light Theme</option>
<option value="dark" selected>π Dark Theme</option>
<option value="auto">π Auto (System)</option></select>
</div>
<div class="help-text">Choose your preferred theme</div>
</div></div>
<div class="field"><div class="field">
<label for="language">Language</label>
<div class="input-with-icon"><span class="input-icon">globe</span>
<select name="language" id="language"><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="help-text">Select your preferred language</div>
</div></div>
</div>
</section>
</div>
<div id="tab-3"
class="tab-panel"
role="tabpanel"
style="display: none;"
aria-hidden="true">
<section class="layout-field-section">
<h3 class="layout-field-title">Task List</h3>
<p class="layout-field-help">List layout demonstration</p>
<div class="layout-field-content">
<div class="field"><label for="project_name">Project Name *</label>
<input name="project_name" id="project_name" value="Website Redesign Project" required="required" minlength="2" maxlength="100" placeholder="Enter project name" type="text" />
<div class="help-text">Name of the project or task collection</div></div>
<div class="model-list-block" data-field-name="tasks" data-min-items="1" data-max-items="10">
<div class="model-list-items" id="tasks-items"><template class="model-list-item-template">
<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="field"><label for="tasks[0].task_name">Task Description</label>
<input name="tasks[0].task_name" id="tasks[0].task_name" minlength="1" maxlength="100" placeholder="Enter task description..." type="text" />
<div class="help-text">What needs to be done?</div></div>
</div>
<div class="col-md-6">
<div class="field"><div class="field">
<label for="tasks[0].priority">Priority</label>
<div class="input-with-icon"><span class="input-icon">exclamation-triangle</span>
<select name="tasks[0].priority" id="tasks[0].priority"><option value="low">π’ Low</option>
<option value="medium">π‘ Medium</option>
<option value="high">π High</option>
<option value="urgent">π΄ Urgent</option></select>
</div>
<div class="help-text">How important is this task?</div>
</div></div>
</div>
<div class="col-md-6">
<div class="field"><label for="tasks[0].due_date">Due Date</label>
<div class="birthdate-input-group"><input name="tasks[0].due_date" id="tasks[0].due_date" max="2026-03-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="help-text">When should this be completed? (optional)</div></div>
</div>
<div class="col-md-6">
<div class="field"><label for="tasks[0].completed">Completed</label>
<input name="tasks[0].completed" id="tasks[0].completed" value="1" type="checkbox" />
<div class="help-text">Is this task done?</div></div>
</div></div>
</div>
</div>
</div></template>
<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="field"><label for="tasks[0].task_name">Task Description</label>
<input name="tasks[0].task_name" id="tasks[0].task_name" value="Complete project setup and requirements gathering" minlength="1" maxlength="100" placeholder="Enter task description..." type="text" />
<div class="help-text">What needs to be done?</div></div>
</div>
<div class="col-md-6">
<div class="field"><div class="field">
<label for="tasks[0].priority">Priority</label>
<div class="input-with-icon"><span class="input-icon">exclamation-triangle</span>
<select name="tasks[0].priority" id="tasks[0].priority"><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="help-text">How important is this task?</div>
</div></div>
</div>
<div class="col-md-6">
<div class="field"><label for="tasks[0].due_date">Due Date</label>
<div class="birthdate-input-group"><input name="tasks[0].due_date" id="tasks[0].due_date" value="2024-12-01" max="2026-03-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="help-text">When should this be completed? (optional)</div></div>
</div>
<div class="col-md-6">
<div class="field"><label for="tasks[0].completed">Completed</label>
<input name="tasks[0].completed" id="tasks[0].completed" value="1" type="checkbox" />
<div class="help-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="field"><label for="tasks[1].task_name">Task Description</label>
<input name="tasks[1].task_name" id="tasks[1].task_name" value="Design mockups and wireframes" minlength="1" maxlength="100" placeholder="Enter task description..." type="text" />
<div class="help-text">What needs to be done?</div></div>
</div>
<div class="col-md-6">
<div class="field"><div class="field">
<label for="tasks[1].priority">Priority</label>
<div class="input-with-icon"><span class="input-icon">exclamation-triangle</span>
<select name="tasks[1].priority" id="tasks[1].priority"><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="help-text">How important is this task?</div>
</div></div>
</div>
<div class="col-md-6">
<div class="field"><label for="tasks[1].due_date">Due Date</label>
<div class="birthdate-input-group"><input name="tasks[1].due_date" id="tasks[1].due_date" value="2024-12-15" max="2026-03-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="help-text">When should this be completed? (optional)</div></div>
</div>
<div class="col-md-6">
<div class="field"><label for="tasks[1].completed">Completed</label>
<input name="tasks[1].completed" id="tasks[1].completed" value="1" type="checkbox" />
<div class="help-text">Is this task done?</div></div>
</div></div>
</div>
</div>
</div></div>
<button type="button" class="add-item-btn" data-target="tasks">Add Item</button>
<div class="model-list-help">Manage your tasks (dynamic list demonstration)</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">Submit</button>
<div style="margin-top: 0.5rem; font-size: 0.75rem; color: #6c757d; text-align: right;">Rendered in 0.001s</div>
</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": {
"autofocus": false,
"default": {
"type": "VerticalFormLayout",
"layout": true
},
"description": "Vertical layout demonstration",
"disabled": false,
"help_text": "Vertical layout demonstration",
"input_type": "layout",
"readonly": false,
"title": "Personal Info"
},
"horizontal_tab": {
"autofocus": false,
"default": {
"type": "HorizontalFormLayout",
"layout": true
},
"description": "Horizontal layout demonstration",
"disabled": false,
"help_text": "Horizontal layout demonstration",
"input_type": "layout",
"readonly": false,
"title": "Contact Info"
},
"tabbed_tab": {
"autofocus": false,
"default": {
"type": "TabbedFormLayout",
"layout": true,
"tabs": []
},
"description": "Tabbed layout demonstration",
"disabled": false,
"help_text": "Tabbed layout demonstration",
"input_type": "layout",
"readonly": false,
"title": "Preferences"
},
"list_tab": {
"autofocus": false,
"default": {
"type": "ListFormLayout",
"layout": true
},
"description": "List layout demonstration",
"disabled": false,
"help_text": "List layout demonstration",
"input_type": "layout",
"readonly": false,
"title": "Task List"
}
},
"title": "LayoutDemonstrationForm",
"type": "object"
}{
"vertical_tab": {
"required": false
},
"horizontal_tab": {
"required": false
},
"tabbed_tab": {
"required": false
},
"list_tab": {
"required": false
}
}{
"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
}
]
}
}
}