UNPKG

react-dynamic-form-builder-reactquery

Version:

A flexible and powerful dynamic form builder for React with Ant Design - 23 components, responsive layout, React Query integration, field dependencies, conditional fields, full TypeScript support

578 lines (509 loc) โ€ข 15.2 kB
# ๐Ÿ“ Layout & Positioning Guide ## แƒžแƒแƒ–แƒ˜แƒชแƒ˜แƒแƒœแƒ˜แƒ แƒ”แƒ‘แƒ˜แƒกแƒ แƒ“แƒ แƒ’แƒแƒœแƒšแƒแƒ’แƒ”แƒ‘แƒ˜แƒก แƒกแƒ แƒฃแƒšแƒ˜ แƒ’แƒ–แƒแƒ›แƒ™แƒ•แƒšแƒ”แƒ•แƒ˜ ### ๐ŸŽฏ แƒซแƒ˜แƒ แƒ˜แƒ—แƒแƒ“แƒ˜ แƒ™แƒแƒœแƒชแƒ”แƒคแƒชแƒ˜แƒ แƒคแƒแƒ แƒ›แƒ แƒ˜แƒงแƒ”แƒœแƒ”แƒ‘แƒก **Ant Design Grid System**-แƒก (24 column layout). ## 1๏ธโƒฃ Form-Level Layout ### Layout Types ```typescript const formConfig: FormConfig = { layout: 'vertical', // Labels แƒ—แƒแƒ•แƒ–แƒ” // layout: 'horizontal', // Labels แƒ›แƒแƒ แƒชแƒฎแƒœแƒ˜แƒ• // layout: 'inline', // แƒงแƒ•แƒ”แƒšแƒแƒคแƒ”แƒ แƒ˜ แƒ”แƒ แƒ— แƒฎแƒแƒ–แƒ–แƒ” columns: 2, // แƒ แƒแƒ›แƒ“แƒ”แƒœ แƒกแƒ•แƒ”แƒขแƒแƒ“ แƒ“แƒแƒ˜แƒงแƒแƒก } ``` ## 2๏ธโƒฃ Field-Level Positioning ### Basic Span (แƒกแƒ•แƒ”แƒขแƒ”แƒ‘แƒ˜แƒก แƒ แƒแƒแƒ“แƒ”แƒœแƒแƒ‘แƒ) ```typescript { name: 'firstName', span: 12, // แƒœแƒแƒฎแƒ”แƒ•แƒแƒ แƒ˜ แƒกแƒ˜แƒ’แƒแƒœแƒ” (12/24) } { name: 'fullWidth', span: 24, // แƒกแƒ แƒฃแƒšแƒ˜ แƒกแƒ˜แƒ’แƒแƒœแƒ” } { name: 'third', span: 8, // แƒ”แƒ แƒ—แƒ˜ แƒ›แƒ”แƒกแƒแƒ›แƒ”แƒ“แƒ˜ (8/24) } ``` ### Offset (แƒชแƒแƒ แƒ˜แƒ”แƒšแƒ˜ แƒแƒ“แƒ’แƒ˜แƒšแƒ˜ แƒ›แƒแƒ แƒชแƒฎแƒœแƒ˜แƒ“แƒแƒœ) ```typescript { name: 'centered', span: 12, offset: 6, // 6 แƒกแƒ•แƒ”แƒขแƒ˜ แƒชแƒแƒ แƒ˜แƒ”แƒšแƒ˜แƒ แƒ›แƒแƒ แƒชแƒฎแƒœแƒ˜แƒ“แƒแƒœ, แƒจแƒ”แƒ›แƒ“แƒ”แƒ’ 12 แƒกแƒ•แƒ”แƒขแƒ˜ แƒ•แƒ”แƒšแƒ˜ } ``` ### Push & Pull (แƒ•แƒ”แƒšแƒ”แƒ‘แƒ˜แƒก แƒ’แƒแƒ“แƒแƒแƒ“แƒ’แƒ˜แƒšแƒ”แƒ‘แƒ) ```typescript { name: 'first', span: 12, push: 12, // แƒ’แƒแƒ“แƒแƒแƒแƒ“แƒ’แƒ˜แƒšแƒ” แƒ›แƒแƒ แƒฏแƒ•แƒœแƒ˜แƒ• 12 แƒกแƒ•แƒ”แƒขแƒ˜แƒ— }, { name: 'second', span: 12, pull: 12, // แƒ’แƒแƒ“แƒแƒแƒแƒ“แƒ’แƒ˜แƒšแƒ” แƒ›แƒแƒ แƒชแƒฎแƒœแƒ˜แƒ• 12 แƒกแƒ•แƒ”แƒขแƒ˜แƒ— } // แƒ”แƒก แƒ•แƒ”แƒšแƒ”แƒ‘แƒ˜ แƒ’แƒแƒชแƒ•แƒšแƒ˜แƒแƒœ แƒแƒ“แƒ’แƒ˜แƒšแƒ”แƒ‘แƒก! ``` ### Custom Styles ```typescript { name: 'customField', span: 12, style: { padding: '0 10px', background: '#f0f0f0', }, formItemStyle: { marginBottom: '32px', border: '1px solid #ddd', } } ``` ### Label & Wrapper Layout (Horizontal Layout-แƒ˜แƒกแƒ—แƒ•แƒ˜แƒก) ```typescript { name: 'horizontalField', label: 'Label', labelCol: { span: 6 }, // Label แƒ˜แƒ™แƒแƒ•แƒ”แƒ‘แƒก 6 แƒกแƒ•แƒ”แƒขแƒก wrapperCol: { span: 18 }, // Input แƒ˜แƒ™แƒแƒ•แƒ”แƒ‘แƒก 18 แƒกแƒ•แƒ”แƒขแƒก } ``` ## 3๏ธโƒฃ แƒžแƒ แƒแƒฅแƒขแƒ˜แƒ™แƒฃแƒšแƒ˜ แƒ›แƒแƒ’แƒแƒšแƒ˜แƒ—แƒ”แƒ‘แƒ˜ ### แƒ›แƒแƒ’แƒแƒšแƒ˜แƒ—แƒ˜ 1: 2-Column Layout ```typescript const formConfig = { columns: 2, fields: [ { name: 'firstName', span: 12 }, // แƒ›แƒแƒ แƒชแƒฎแƒ”แƒœแƒ แƒกแƒ•แƒ”แƒขแƒ˜ { name: 'lastName', span: 12 }, // แƒ›แƒแƒ แƒฏแƒ•แƒ”แƒœแƒ แƒกแƒ•แƒ”แƒขแƒ˜ { name: 'email', span: 24 }, // แƒกแƒ แƒฃแƒšแƒ˜ แƒกแƒ˜แƒ’แƒแƒœแƒ” ] } ``` ### แƒ›แƒแƒ’แƒแƒšแƒ˜แƒ—แƒ˜ 2: 3-Column Layout ```typescript const formConfig = { columns: 3, fields: [ { name: 'field1', span: 8 }, { name: 'field2', span: 8 }, { name: 'field3', span: 8 }, ] } ``` ### แƒ›แƒแƒ’แƒแƒšแƒ˜แƒ—แƒ˜ 3: แƒ™แƒแƒ›แƒžแƒšแƒ”แƒฅแƒกแƒฃแƒ แƒ˜ Layout ```typescript const formConfig = { fields: [ // แƒกแƒ แƒฃแƒšแƒ˜ แƒกแƒ˜แƒ’แƒแƒœแƒ” { name: 'title', span: 24 }, // 2 แƒ•แƒ”แƒšแƒ˜ แƒ—แƒแƒœแƒแƒ‘แƒ แƒแƒ“ { name: 'firstName', span: 12 }, { name: 'lastName', span: 12 }, // 3 แƒ•แƒ”แƒšแƒ˜ แƒ—แƒแƒœแƒแƒ‘แƒ แƒแƒ“ { name: 'day', span: 8 }, { name: 'month', span: 8 }, { name: 'year', span: 8 }, // 1/3 + 2/3 { name: 'code', span: 8 }, { name: 'description', span: 16 }, // แƒชแƒ”แƒœแƒขแƒ แƒจแƒ˜ แƒ’แƒแƒœแƒ—แƒแƒ•แƒกแƒ”แƒ‘แƒฃแƒšแƒ˜ แƒ•แƒ”แƒšแƒ˜ { name: 'centered', span: 12, offset: 6 }, ] } ``` ### แƒ›แƒแƒ’แƒแƒšแƒ˜แƒ—แƒ˜ 4: Responsive Layout (Mobile-Friendly) โญ ```typescript import { ResponsiveSpan } from 'react-dynamic-form-builder'; const formConfig = { fields: [ { name: 'responsiveField', label: 'Responsive Field', type: 'input', // Mobile (xs): แƒกแƒ แƒฃแƒšแƒ˜ แƒกแƒ˜แƒ’แƒแƒœแƒ” // Tablet (sm/md): แƒœแƒแƒฎแƒ”แƒ•แƒแƒ แƒ˜ // Desktop (lg+): แƒ›แƒ”แƒกแƒแƒ›แƒ”แƒ“แƒ˜ span: { xs: 24, sm: 12, md: 12, lg: 8 } as ResponsiveSpan, }, { name: 'firstName', span: { xs: 24, sm: 12, lg: 8 }, }, { name: 'lastName', span: { xs: 24, sm: 12, lg: 8 }, }, { name: 'email', span: { xs: 24, sm: 24, lg: 8 }, } ] } ``` ### แƒ›แƒแƒ’แƒแƒšแƒ˜แƒ—แƒ˜ 4.1: Responsive Offset แƒ“แƒ Push/Pull ```typescript const formConfig = { fields: [ { name: 'centeredOnDesktop', type: 'input', // Mobile-แƒ–แƒ” แƒกแƒ แƒฃแƒšแƒ˜ แƒกแƒ˜แƒ’แƒแƒœแƒ”, Desktop-แƒ–แƒ” แƒชแƒ”แƒœแƒขแƒ แƒจแƒ˜ span: { xs: 24, lg: 12 }, offset: { xs: 0, lg: 6 }, // Desktop-แƒ–แƒ” แƒชแƒ”แƒœแƒขแƒ แƒ˜แƒ แƒ”แƒ‘แƒ }, { name: 'pushPullExample', span: { xs: 24, md: 12 }, push: { md: 12 }, // Tablet-แƒ–แƒ” แƒ“แƒ แƒ–แƒ”แƒ›แƒแƒ— แƒ’แƒแƒ“แƒแƒแƒ“แƒ’แƒ˜แƒšแƒ”แƒ‘แƒ } ] } ``` ### แƒ›แƒแƒ’แƒแƒšแƒ˜แƒ—แƒ˜ 5: Form with Sections ```typescript const formConfig = { columns: 2, sections: [ { title: 'Section 1', fields: [ { name: 'field1', span: 12 }, { name: 'field2', span: 12 }, ] }, { title: 'Section 2 - Full Width', fields: [ { name: 'field3', span: 24 }, ] } ] } ``` ## 4๏ธโƒฃ Grid Examples ### 24-Column Grid แƒ•แƒ˜แƒ–แƒฃแƒแƒšแƒ˜แƒ–แƒแƒชแƒ˜แƒ: ``` |--1--|--2--|--3--|--4--|--5--|--6--|--7--|--8--|--9--|--10-|--11-|--12-|--13-|--14-|--15-|--16-|--17-|--18-|--19-|--20-|--21-|--22-|--23-|--24-| ``` **span: 24** - แƒกแƒ แƒฃแƒšแƒ˜ แƒกแƒ˜แƒ’แƒแƒœแƒ” ``` |============================================================================| ``` **span: 12** - แƒœแƒแƒฎแƒ”แƒ•แƒแƒ แƒ˜ ``` |=====================================| | ``` **span: 8** - แƒ”แƒ แƒ—แƒ˜ แƒ›แƒ”แƒกแƒแƒ›แƒ”แƒ“แƒ˜ ``` |========================| | | ``` **span: 6** - แƒ”แƒ แƒ—แƒ˜ แƒ›แƒ”แƒแƒ—แƒฎแƒ”แƒ“แƒ˜ ``` |==================| | | | ``` ## 5๏ธโƒฃ Advanced Techniques ### แƒแƒกแƒ˜แƒ›แƒ”แƒขแƒ แƒ˜แƒฃแƒšแƒ˜ Layout ```typescript { fields: [ { name: 'small', span: 6 }, { name: 'large', span: 18 }, ] } ``` ### แƒ•แƒ”แƒšแƒ”แƒ‘แƒ˜แƒก แƒ“แƒแƒฏแƒ’แƒฃแƒคแƒ”แƒ‘แƒ ```typescript { fields: [ { name: 'group1field1', span: 8, group: 'group1' }, { name: 'group1field2', span: 8, group: 'group1' }, { name: 'group1field3', span: 8, group: 'group1' }, { name: 'group2field1', span: 12, group: 'group2' }, { name: 'group2field2', span: 12, group: 'group2' }, ] } ``` ### CSS Grid Alternative ```typescript { name: 'customGrid', span: 24, style: { display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: '16px', } } ``` ## 6๏ธโƒฃ Best Practices ### โœ… DO: - แƒ’แƒแƒ›แƒแƒ˜แƒงแƒ”แƒœแƒ”แƒ— `columns` prop simple layouts-แƒ˜แƒกแƒ—แƒ•แƒ˜แƒก - แƒ’แƒแƒ›แƒแƒ˜แƒงแƒ”แƒœแƒ”แƒ— `span` แƒ—แƒ˜แƒ—แƒแƒ”แƒฃแƒšแƒ˜ แƒ•แƒ”แƒšแƒ˜แƒกแƒ—แƒ•แƒ˜แƒก individual control-แƒ˜แƒกแƒ—แƒ•แƒ˜แƒก - แƒ“แƒแƒ˜แƒชแƒแƒ•แƒ˜แƒ— consistency แƒ’แƒแƒœแƒšแƒแƒ’แƒ”แƒ‘แƒแƒจแƒ˜ - แƒ’แƒแƒ›แƒแƒ˜แƒงแƒ”แƒœแƒ”แƒ— `offset` centering-แƒ˜แƒกแƒ—แƒ•แƒ˜แƒก - แƒ’แƒแƒ›แƒแƒ˜แƒงแƒ”แƒœแƒ”แƒ— sections complex forms-แƒ˜แƒกแƒ—แƒ•แƒ˜แƒก ### โŒ DON'T: - แƒแƒ  แƒ’แƒแƒ›แƒแƒ˜แƒงแƒ”แƒœแƒแƒ— span > 24 - แƒแƒ  แƒ“แƒแƒ˜แƒ•แƒ˜แƒฌแƒงแƒแƒ— mobile responsiveness - แƒแƒ  แƒ’แƒแƒแƒ™แƒ”แƒ—แƒแƒ— แƒซแƒแƒšแƒ˜แƒแƒœ แƒ“แƒแƒขแƒ•แƒ˜แƒ แƒ—แƒฃแƒšแƒ˜ layouts ## 7๏ธโƒฃ Cheat Sheet | แƒ แƒแƒก แƒ’แƒ˜แƒœแƒ“แƒ | แƒ แƒแƒ’แƒแƒ  แƒ’แƒแƒแƒ™แƒ”แƒ—แƒ | |-----------|---------------| | 2 แƒ•แƒ”แƒšแƒ˜ แƒ—แƒแƒœแƒแƒ‘แƒ แƒแƒ“ | `span: 12` แƒแƒ แƒ˜แƒ•แƒ”แƒก | | 3 แƒ•แƒ”แƒšแƒ˜ แƒ—แƒแƒœแƒแƒ‘แƒ แƒแƒ“ | `span: 8` แƒงแƒ•แƒ”แƒšแƒแƒก | | 4 แƒ•แƒ”แƒšแƒ˜ แƒ—แƒแƒœแƒแƒ‘แƒ แƒแƒ“ | `span: 6` แƒงแƒ•แƒ”แƒšแƒแƒก | | แƒกแƒ แƒฃแƒšแƒ˜ แƒกแƒ˜แƒ’แƒแƒœแƒ” | `span: 24` | | แƒชแƒ”แƒœแƒขแƒ แƒ˜แƒ แƒ”แƒ‘แƒ | `span: 12, offset: 6` | | 1/3 + 2/3 | `span: 8` แƒ“แƒ `span: 16` | | 1/4 + 3/4 | `span: 6` แƒ“แƒ `span: 18` | ## 8๏ธโƒฃ Live Example ```typescript const complexForm: FormConfig = { layout: 'vertical', columns: 1, sections: [ { title: 'แƒžแƒ˜แƒ แƒแƒ“แƒ˜ แƒ˜แƒœแƒคแƒแƒ แƒ›แƒแƒชแƒ˜แƒ', fields: [ // แƒกแƒ แƒฃแƒšแƒ˜ แƒกแƒ˜แƒ’แƒแƒœแƒ” { name: 'avatar', type: 'upload', span: 24 }, // 2 แƒ•แƒ”แƒšแƒ˜ แƒ—แƒแƒœแƒแƒ‘แƒ แƒแƒ“ { name: 'firstName', type: 'input', span: 12 }, { name: 'lastName', type: 'input', span: 12 }, // 3 แƒ•แƒ”แƒšแƒ˜ (แƒ“แƒแƒ‘แƒแƒ“แƒ”แƒ‘แƒ˜แƒก แƒ—แƒแƒ แƒ˜แƒฆแƒ˜) { name: 'day', type: 'select', span: 8, placeholder: 'DD' }, { name: 'month', type: 'select', span: 8, placeholder: 'MM' }, { name: 'year', type: 'select', span: 8, placeholder: 'YYYY' }, ] }, { title: 'แƒกแƒแƒ™แƒแƒœแƒขแƒแƒฅแƒขแƒ', fields: [ // แƒกแƒ แƒฃแƒšแƒ˜ แƒกแƒ˜แƒ’แƒแƒœแƒ” email { name: 'email', type: 'email', span: 24 }, // 1/3 country code + 2/3 phone { name: 'countryCode', type: 'select', span: 8, style: { paddingRight: '8px' } }, { name: 'phone', type: 'phone', span: 16, style: { paddingLeft: '8px' } }, ] } ] }; ``` --- ## 9๏ธโƒฃ ๐Ÿ“ฑ Responsive Design - Mobile-Friendly ### Breakpoint แƒกแƒ˜แƒกแƒขแƒ”แƒ›แƒ Ant Design Grid แƒ˜แƒงแƒ”แƒœแƒ”แƒ‘แƒก แƒจแƒ”แƒ›แƒ“แƒ”แƒ’ breakpoint-แƒ”แƒ‘แƒก: | Breakpoint | แƒ”แƒ™แƒ แƒแƒœแƒ˜แƒก แƒ–แƒแƒ›แƒ | แƒ›แƒแƒฌแƒงแƒแƒ‘แƒ˜แƒšแƒแƒ‘แƒ | แƒ’แƒแƒ›แƒแƒงแƒ”แƒœแƒ”แƒ‘แƒ | |-----------|-------------|-------------|------------| | **xs** | <576px | Mobile | แƒกแƒ แƒฃแƒšแƒ˜ แƒกแƒ˜แƒ’แƒแƒœแƒ” (24) | | **sm** | โ‰ฅ576px | Large Mobile/Small Tablet | 12 แƒแƒœ 24 | | **md** | โ‰ฅ768px | Tablet | 12, 8 | | **lg** | โ‰ฅ992px | Desktop | 8, 6, 12 | | **xl** | โ‰ฅ1200px | Large Desktop | 6, 8 | | **xxl** | โ‰ฅ1600px | Extra Large | 6, 4 | ### Responsive Best Practices โญ #### 1. Mobile-First Approach ```typescript // โœ… แƒ™แƒแƒ แƒ’แƒ˜: Mobile-แƒ“แƒแƒœ Desktop-แƒ˜แƒกแƒ™แƒ”แƒœ { name: 'field', span: { xs: 24, sm: 12, lg: 8 } // xs: mobile - แƒกแƒ แƒฃแƒšแƒ˜ แƒกแƒ˜แƒ’แƒแƒœแƒ” // sm: tablet - แƒœแƒแƒฎแƒ”แƒ•แƒแƒ แƒ˜ // lg: desktop - แƒ›แƒ”แƒกแƒแƒ›แƒ”แƒ“แƒ˜ } // โŒ แƒชแƒฃแƒ“แƒ˜: แƒซแƒแƒšแƒ˜แƒแƒœ แƒ‘แƒ”แƒ•แƒ แƒ˜ breakpoint { name: 'field', span: { xs: 24, sm: 20, md: 16, lg: 12, xl: 10, xxl: 8 } } ``` #### 2. แƒกแƒแƒ”แƒ แƒ—แƒ Responsive แƒžแƒแƒขแƒ”แƒ แƒœแƒ”แƒ‘แƒ˜ **แƒžแƒแƒขแƒ”แƒ แƒœแƒ˜ A: แƒกแƒ แƒฃแƒšแƒ˜ แƒกแƒ˜แƒ’แƒแƒœแƒ” โ†’ แƒœแƒแƒฎแƒ”แƒ•แƒแƒ แƒ˜ โ†’ แƒ›แƒ”แƒกแƒแƒ›แƒ”แƒ“แƒ˜** ```typescript { fields: [ { name: 'name', span: { xs: 24, sm: 12, lg: 8 }, }, { name: 'email', span: { xs: 24, sm: 12, lg: 8 }, }, { name: 'phone', span: { xs: 24, sm: 12, lg: 8 }, }, ] } // Mobile: 3 แƒ•แƒ”แƒšแƒ˜ แƒ”แƒ แƒ—แƒ›แƒแƒœแƒ”แƒ—แƒ˜แƒก แƒฅแƒ•แƒ”แƒจ // Tablet: 2 แƒกแƒ•แƒ”แƒขแƒแƒ“ (name/email โ†’ phone) // Desktop: 3 แƒกแƒ•แƒ”แƒขแƒแƒ“ ``` **แƒžแƒแƒขแƒ”แƒ แƒœแƒ˜ B: แƒกแƒ แƒฃแƒšแƒ˜ แƒกแƒ˜แƒ’แƒแƒœแƒ” โ†’ แƒœแƒแƒฎแƒ”แƒ•แƒแƒ แƒ˜** ```typescript { fields: [ { name: 'firstName', span: { xs: 24, md: 12 }, }, { name: 'lastName', span: { xs: 24, md: 12 }, }, ] } // Mobile & Small Tablet: แƒ•แƒ”แƒ แƒขแƒ˜แƒ™แƒแƒšแƒฃแƒ แƒแƒ“ // Desktop: 2 แƒกแƒ•แƒ”แƒขแƒแƒ“ ``` **แƒžแƒแƒขแƒ”แƒ แƒœแƒ˜ C: Title แƒกแƒ แƒฃแƒšแƒ˜, แƒ•แƒ”แƒšแƒ”แƒ‘แƒ˜ responsive** ```typescript { fields: [ { name: 'title', type: 'input', span: 24, // แƒงแƒแƒ•แƒ”แƒšแƒ—แƒ•แƒ˜แƒก แƒกแƒ แƒฃแƒšแƒ˜ แƒกแƒ˜แƒ’แƒแƒœแƒ” }, { name: 'category', span: { xs: 24, sm: 12, lg: 6 }, }, { name: 'status', span: { xs: 24, sm: 12, lg: 6 }, }, { name: 'priority', span: { xs: 24, sm: 12, lg: 6 }, }, { name: 'assignee', span: { xs: 24, sm: 12, lg: 6 }, }, ] } ``` #### 3. แƒ™แƒแƒ›แƒžแƒšแƒ”แƒฅแƒกแƒฃแƒ แƒ˜ Responsive Form ```typescript import { FormConfig, ResponsiveSpan } from 'react-dynamic-form-builder'; const responsiveForm: FormConfig = { layout: 'vertical', // Mobile-แƒ–แƒ” vertical แƒฃแƒ™แƒ”แƒ—แƒ”แƒกแƒ˜แƒ sections: [ { title: 'แƒžแƒ˜แƒ แƒแƒ“แƒ˜ แƒ˜แƒœแƒคแƒแƒ แƒ›แƒแƒชแƒ˜แƒ', fields: [ { name: 'avatar', type: 'upload', span: 24, // แƒงแƒแƒ•แƒ”แƒšแƒ—แƒ•แƒ˜แƒก แƒกแƒ แƒฃแƒšแƒ˜ }, { name: 'firstName', type: 'input', span: { xs: 24, sm: 12 } as ResponsiveSpan, }, { name: 'lastName', type: 'input', span: { xs: 24, sm: 12 }, }, { name: 'email', type: 'email', span: { xs: 24, md: 12 }, }, { name: 'phone', type: 'phone', span: { xs: 24, md: 12 }, }, ] }, { title: 'แƒ›แƒ˜แƒกแƒแƒ›แƒแƒ แƒ—แƒ˜', fields: [ { name: 'country', type: 'select', span: { xs: 24, sm: 12, lg: 8 }, }, { name: 'city', type: 'select', span: { xs: 24, sm: 12, lg: 8 }, }, { name: 'district', type: 'select', span: { xs: 24, sm: 24, lg: 8 }, }, { name: 'street', type: 'input', span: 24, }, ] } ] }; ``` #### 4. Responsive Centering ```typescript { name: 'submitButton', type: 'custom', // Mobile: แƒกแƒ แƒฃแƒšแƒ˜ แƒกแƒ˜แƒ’แƒแƒœแƒ” // Desktop: แƒชแƒ”แƒœแƒขแƒ แƒจแƒ˜ 12 แƒกแƒ•แƒ”แƒขแƒ˜ span: { xs: 24, lg: 12 }, offset: { xs: 0, lg: 6 }, } ``` ### Mobile UX Tips ๐Ÿ“ฑ 1. **Mobile-แƒ–แƒ” แƒงแƒแƒ•แƒ”แƒšแƒ—แƒ•แƒ˜แƒก `span: 24`** - แƒ”แƒ แƒ—แƒ˜ แƒ•แƒ”แƒšแƒ˜ แƒ”แƒ แƒ—์ค„แƒ–แƒ” 2. **Tablet-แƒ“แƒแƒœ แƒ˜แƒฌแƒงแƒ” แƒ“แƒแƒงแƒแƒคแƒ** - `sm: 12` แƒแƒœ `md: 12` 3. **Desktop-แƒ–แƒ” 3-4 แƒกแƒ•แƒ”แƒขแƒ˜ แƒ›แƒแƒฅแƒกแƒ˜แƒ›แƒฃแƒ›** - `lg: 8` แƒแƒœ `lg: 6` 4. **Vertical layout Mobile-แƒ–แƒ”** - แƒฃแƒ™แƒ”แƒ—แƒ”แƒกแƒ˜ UX 5. **Horizontal layout Desktop-แƒ–แƒ”** - แƒ”แƒ™แƒแƒœแƒแƒ›แƒ˜แƒฃแƒ แƒ˜ แƒกแƒ˜แƒ•แƒ แƒชแƒ” ### แƒจแƒ”แƒฏแƒแƒ›แƒ”แƒ‘แƒ ```typescript // โœ… แƒ˜แƒ“แƒ”แƒแƒšแƒฃแƒ แƒ˜ Responsive แƒžแƒแƒขแƒ”แƒ แƒœแƒ˜ const perfectResponsive = { fields: [ { name: 'anyField', span: { xs: 24, // Mobile: แƒกแƒ แƒฃแƒšแƒ˜ แƒกแƒ˜แƒ’แƒแƒœแƒ” sm: 12, // Tablet: แƒœแƒแƒฎแƒ”แƒ•แƒแƒ แƒ˜ lg: 8, // Desktop: แƒ›แƒ”แƒกแƒแƒ›แƒ”แƒ“แƒ˜ } } ] } // Mobile First! // แƒ—แƒฅแƒ•แƒ”แƒœ แƒงแƒแƒ•แƒ”แƒšแƒ—แƒ•แƒ˜แƒก แƒฃแƒœแƒ“แƒ แƒ˜แƒคแƒ˜แƒฅแƒ แƒแƒ—: // 1. แƒ แƒแƒ’แƒแƒ  แƒ’แƒแƒ›แƒแƒ˜แƒงแƒฃแƒ แƒ”แƒ‘แƒ Mobile-แƒ–แƒ”? // 2. แƒ แƒแƒ’แƒแƒ  แƒ’แƒแƒ›แƒแƒ˜แƒงแƒฃแƒ แƒ”แƒ‘แƒ Tablet-แƒ–แƒ”? // 3. แƒ แƒแƒ’แƒแƒ  แƒ’แƒแƒ›แƒแƒ˜แƒงแƒฃแƒ แƒ”แƒ‘แƒ Desktop-แƒ–แƒ”? ``` --- แƒ”แƒก แƒกแƒ˜แƒกแƒขแƒ”แƒ›แƒ แƒ’แƒแƒซแƒšแƒ”แƒ•แƒ— **แƒกแƒ แƒฃแƒš แƒ™แƒแƒœแƒขแƒ แƒแƒšแƒก** แƒคแƒแƒ แƒ›แƒ˜แƒก แƒ’แƒแƒœแƒšแƒแƒ’แƒ”แƒ‘แƒแƒ–แƒ” แƒ“แƒ **แƒ›แƒแƒฅแƒกแƒ˜แƒ›แƒแƒšแƒฃแƒ  Responsive/Mobile-Friendly แƒคแƒฃแƒœแƒฅแƒชแƒ˜แƒแƒœแƒแƒšแƒก**! ๐ŸŽจ๐Ÿ“ฑ