# GridForm

A flexible CSS Grid-based form layout system that provides precise control over field positioning, spanning, and complex layout arrangements.

## Features

- **CSS Grid Layout**: True CSS Grid implementation for complex form layouts
- **Precise Positioning**: Explicit row and column positioning with `row` and `col` props
- **Row/Column Spanning**: Fields can span multiple rows and columns
- **Configurable Columns**: Set any number of columns (default 6)
- **Layout Modes**: Supports vertical and horizontal field layouts
- **FormField Integration**: Built-in FormField integration for labels and validation
- **Complex Layouts**: Perfect for forms with images, cards, and non-uniform layouts

## Installation

```bash
npm install @ticatec/uniface-element
```

## Usage

### Basic Usage

```svelte
<script>
  import GridForm, { GridField } from '@ticatec/uniface-element/grid-form';
  import TextEditor from '@ticatec/uniface-element/text-editor';
  
  let formData = {};
</script>

<GridForm columns={3}>
  <GridField row={1} col={1} label="First Name" required>
    <TextEditor bind:value={formData.firstName} variant="filled" />
  </GridField>
  
  <GridField row={1} col={2} label="Last Name" required>
    <TextEditor bind:value={formData.lastName} variant="filled" />
  </GridField>
  
  <GridField row={1} col={3} label="Email">
    <TextEditor bind:value={formData.email} variant="filled" />
  </GridField>
  
  <GridField row={2} col={1} colSpan={3} label="Address">
    <TextEditor bind:value={formData.address} variant="filled" />
  </GridField>
</GridForm>
```

### Complex Layout with Image

```svelte
<script>
  import GridForm, { GridField, EmptyCell } from '@ticatec/uniface-element/grid-form';
  import TextEditor from '@ticatec/uniface-element/text-editor';
  import DatePicker from '@ticatec/uniface-element/date-picker';
  import OptionsSelect from '@ticatec/uniface-element/options-select';
  import MemoEditor from '@ticatec/uniface-element/memo-editor';
  
  let formData = {};
  let errors = {};
  
  const genderOptions = [
    { code: 'F', text: 'Female' },
    { code: 'M', text: 'Male' }
  ];
</script>

<GridForm columns={3} style="border: 1px solid #e1e1e1; padding: 20px;">
  <!-- Profile Image spanning 2 columns and 4 rows -->
  <GridField 
    row={1} 
    col={1} 
    colSpan={2} 
    rowSpan={4} 
    label="Profile Photo" 
    required 
    error={errors.photo}
  >
    <div style="width: 100%; height: 180px; border: 2px dashed #ccc; display: flex; align-items: center; justify-content: center;">
      <img 
        src={formData.photo || 'https://via.placeholder.com/200x180'} 
        alt="Profile" 
        style="width: 100%; height: 100%; object-fit: cover;"
      />
    </div>
  </GridField>
  
  <!-- Personal details in the right column -->
  <GridField row={1} col={3} label="Full Name" required error={errors.name}>
    <TextEditor 
      bind:value={formData.name} 
      variant="filled"
      placeholder="Enter full name"
    />
  </GridField>
  
  <GridField row={2} col={3} label="Date of Birth" required error={errors.dob}>
    <DatePicker 
      bind:value={formData.dob} 
      variant="filled"
      max={new Date()}
    />
  </GridField>
  
  <GridField row={3} col={3} label="Gender" required error={errors.gender}>
    <OptionsSelect 
      bind:value={formData.gender} 
      variant="filled"
      options={genderOptions}
    />
  </GridField>
  
  <GridField row={4} col={3} label="Phone Number" error={errors.phone}>
    <TextEditor 
      bind:value={formData.phone} 
      variant="filled"
      placeholder="Enter phone number"
    />
  </GridField>
  
  <!-- Full-width notes field -->
  <GridField row={5} col={1} colSpan={3} label="Additional Notes" error={errors.notes}>
    <MemoEditor 
      bind:value={formData.notes} 
      variant="filled"
      input$rows={4}
      input$maxLength={300}
      showIndicator
    />
  </GridField>
</GridForm>
```

### Card-Based Layout

```svelte
<GridForm columns={4}>
  <!-- Header spanning full width -->
  <GridField row={1} col={1} colSpan={4} label="Project Information">
    <h3 style="margin: 0; padding: 1rem; background: #f8f9fa; border-radius: 4px;">
      Project Details
    </h3>
  </GridField>
  
  <!-- Main content area -->
  <GridField row={2} col={1} colSpan={3} rowSpan={2} label="Project Description">
    <MemoEditor 
      bind:value={project.description}
      variant="filled"
      input$rows={6}
    />
  </GridField>
  
  <!-- Sidebar information -->
  <GridField row={2} col={4} label="Priority">
    <OptionsSelect bind:value={project.priority} options={priorities} />
  </GridField>
  
  <GridField row={3} col={4} label="Status">
    <OptionsSelect bind:value={project.status} options={statuses} />
  </GridField>
  
  <!-- Footer details -->
  <GridField row={4} col={1} colSpan={2} label="Start Date">
    <DatePicker bind:value={project.startDate} variant="filled" />
  </GridField>
  
  <GridField row={4} col={3} colSpan={2} label="End Date">
    <DatePicker bind:value={project.endDate} variant="filled" />
  </GridField>
</GridForm>
```

## API Reference

### GridForm

Main container component for CSS Grid-based form layout.

#### Props

| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `style` | `string` | `''` | Custom CSS styles for the form container |
| `columns` | `number` | `6` | Number of columns in the grid |
| `fieldLayout` | `'vertical' \| 'horizontal'` | `'vertical'` | Layout mode for form fields |

#### Slots

| Slot | Description |
|------|-------------|
| default | Form content containing GridField and EmptyCell components |

### GridField

A form field component that can be precisely positioned and span multiple grid cells.

#### Props

| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `label` | `string` | `"Label:"` | Label text for the form field |
| `label$style` | `string` | `''` | Custom CSS styles for the label |
| `required` | `boolean` | `false` | Whether the field is required |
| `error` | `string \| null` | `null` | Error message to display |
| `arrangement` | `'vertical' \| 'horizontal'` | `'vertical'` | Field arrangement mode |
| `rowSpan` | `number` | `1` | Number of rows to span |
| `colSpan` | `number` | `1` | Number of columns to span |
| `row` | `number \| null` | `null` | Specific row position (1-based) |
| `col` | `number \| null` | `null` | Specific column position (1-based) |
| `height` | `string \| undefined` | `undefined` | Custom height for the field |
| `labelSuffix` | `string` | `':'` | Text to append to the label |

#### Slots

| Slot | Description |
|------|-------------|
| default | Form control content (input, select, etc.) |

### EmptyCell

A placeholder cell for spacing and layout control.

#### Props

| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `rowSpan` | `number` | `1` | Number of rows to span |
| `colSpan` | `number` | `1` | Number of columns to span |
| `row` | `number` | - | Specific row position (1-based) |
| `col` | `number` | - | Specific column position (1-based) |

## Grid System

The GridForm uses CSS Grid with configurable columns:

- **columns={3}**: 3-column grid (33.33% each)
- **columns={4}**: 4-column grid (25% each)
- **columns={6}**: 6-column grid (16.67% each) - default
- **columns={12}**: 12-column grid (8.33% each)

### Positioning

- **row/col**: 1-based positioning (row={1} col={1} is top-left)
- **rowSpan/colSpan**: Span multiple cells
- **Auto-placement**: Fields without explicit positioning flow naturally

## Layout Examples

### Dashboard-Style Form

```svelte
<GridForm columns={4}>
  <!-- Header -->
  <GridField row={1} col={1} colSpan={4} label="Dashboard Title">
    <h2 style="margin: 0;">System Configuration</h2>
  </GridField>
  
  <!-- Main settings panel -->
  <GridField row={2} col={1} colSpan={2} rowSpan={3} label="Server Settings">
    <div style="padding: 1rem; border: 1px solid #ddd; border-radius: 4px;">
      <GridForm columns={2}>
        <GridField label="Host">
          <TextEditor bind:value={config.host} />
        </GridField>
        <GridField label="Port">
          <TextEditor bind:value={config.port} />
        </GridField>
        <GridField colSpan={2} label="Database URL">
          <TextEditor bind:value={config.dbUrl} />
        </GridField>
      </GridForm>
    </div>
  </GridField>
  
  <!-- Side panels -->
  <GridField row={2} col={3} colSpan={2} label="System Status">
    <div style="padding: 1rem; background: #f0f9ff; border-radius: 4px;">
      <p>System Online</p>
      <p>Last Updated: {new Date().toLocaleString()}</p>
    </div>
  </GridField>
  
  <GridField row={3} col={3} label="Environment">
    <OptionsSelect bind:value={config.env} options={environments} />
  </GridField>
  
  <GridField row={3} col={4} label="Debug Mode">
    <OptionsSelect bind:value={config.debug} options={[{code: true, text: 'On'}, {code: false, text: 'Off'}]} />
  </GridField>
  
  <GridField row={4} col={3} colSpan={2} label="Actions">
    <div style="display: flex; gap: 1rem;">
      <button>Save Config</button>
      <button>Reset to Defaults</button>
    </div>
  </GridField>
  
  <!-- Footer -->
  <GridField row={5} col={1} colSpan={4} label="Additional Notes">
    <MemoEditor bind:value={config.notes} variant="filled" />
  </GridField>
</GridForm>
```

### Product Form with Image Gallery

```svelte
<GridForm columns={5}>
  <!-- Product images -->
  <GridField row={1} col={1} colSpan={2} rowSpan={3} label="Product Images">
    <div style="display: grid; grid-template-columns: 1fr 1fr; gap: 0.5rem;">
      {#each productImages as image, index}
        <img 
          src={image} 
          alt="Product {index + 1}"
          style="width: 100%; height: 80px; object-fit: cover; border-radius: 4px;"
        />
      {/each}
    </div>
  </GridField>
  
  <!-- Product details -->
  <GridField row={1} col={3} colSpan={3} label="Product Name" required>
    <TextEditor bind:value={product.name} variant="filled" />
  </GridField>
  
  <GridField row={2} col={3} label="Price" required>
    <TextEditor bind:value={product.price} variant="filled" />
  </GridField>
  
  <GridField row={2} col={4} label="Currency">
    <OptionsSelect bind:value={product.currency} options={currencies} />
  </GridField>
  
  <GridField row={2} col={5} label="Stock">
    <TextEditor bind:value={product.stock} variant="filled" />
  </GridField>
  
  <GridField row={3} col={3} colSpan={3} label="Category">
    <OptionsSelect bind:value={product.category} options={categories} />
  </GridField>
  
  <!-- Full-width description -->
  <GridField row={4} col={1} colSpan={5} label="Product Description">
    <MemoEditor bind:value={product.description} variant="filled" />
  </GridField>
</GridForm>
```

### Survey Form with Mixed Layouts

```svelte
<GridForm columns={6}>
  <!-- Question 1: Full width -->
  <GridField row={1} col={1} colSpan={6} label="1. How would you rate our service?">
    <OptionsSelect 
      bind:value={survey.rating} 
      options={ratingOptions}
      variant="filled"
    />
  </GridField>
  
  <!-- Question 2: Two columns -->
  <GridField row={2} col={1} colSpan={3} label="2. Your age group">
    <OptionsSelect bind:value={survey.ageGroup} options={ageGroups} />
  </GridField>
  
  <GridField row={2} col={4} colSpan={3} label="3. Your location">
    <OptionsSelect bind:value={survey.location} options={locations} />
  </GridField>
  
  <!-- Question 4: Three columns -->
  <GridField row={3} col={1} colSpan={2} label="4. Income level">
    <OptionsSelect bind:value={survey.income} options={incomeRanges} />
  </GridField>
  
  <GridField row={3} col={3} colSpan={2} label="5. Education">
    <OptionsSelect bind:value={survey.education} options={educationLevels} />
  </GridField>
  
  <GridField row={3} col={5} colSpan={2} label="6. Employment">
    <OptionsSelect bind:value={survey.employment} options={employmentTypes} />
  </GridField>
  
  <!-- Open-ended question -->
  <GridField row={4} col={1} colSpan={6} label="7. Additional comments" error={errors.comments}>
    <MemoEditor 
      bind:value={survey.comments}
      variant="filled"
      input$rows={4}
      placeholder="Please share any additional feedback..."
    />
  </GridField>
</GridForm>
```

## Validation and Error Handling

```svelte
<script>
  let formData = {
    name: '',
    email: '',
    phone: '',
    address: '',
    notes: ''
  };
  
  let errors = {};
  
  function validateForm() {
    errors = {};
    
    if (!formData.name?.trim()) {
      errors.name = 'Name is required';
    }
    
    if (!formData.email?.trim()) {
      errors.email = 'Email is required';
    } else if (!/\S+@\S+\.\S+/.test(formData.email)) {
      errors.email = 'Please enter a valid email address';
    }
    
    if (!formData.address?.trim()) {
      errors.address = 'Address is required';
    }
    
    return Object.keys(errors).length === 0;
  }
</script>

<GridForm columns={4}>
  <GridField row={1} col={1} colSpan={2} label="Full Name" required error={errors.name}>
    <TextEditor 
      bind:value={formData.name}
      variant="filled"
      placeholder="Enter your full name"
    />
  </GridField>
  
  <GridField row={1} col={3} colSpan={2} label="Email Address" required error={errors.email}>
    <TextEditor 
      bind:value={formData.email}
      variant="filled"
      placeholder="Enter email address"
    />
  </GridField>
  
  <GridField row={2} col={1} colSpan={3} label="Address" required error={errors.address}>
    <TextEditor 
      bind:value={formData.address}
      variant="filled"
      placeholder="Enter your address"
    />
  </GridField>
  
  <GridField row={2} col={4} label="Phone">
    <TextEditor 
      bind:value={formData.phone}
      variant="filled"
      placeholder="Phone number"
    />
  </GridField>
  
  <GridField row={3} col={1} colSpan={4} label="Additional Notes">
    <MemoEditor 
      bind:value={formData.notes}
      variant="filled"
      placeholder="Any additional information..."
    />
  </GridField>
  
  <GridField row={4} col={1} colSpan={4}>
    <div style="text-align: right; margin-top: 1rem;">
      <button type="button" on:click={validateForm}>
        Validate & Submit
      </button>
    </div>
  </GridField>
</GridForm>
```

## Styling

### Custom Grid Styling

```svelte
<GridForm 
  columns={3}
  style="
    max-width: 900px;
    margin: 2rem auto;
    padding: 2rem;
    border: 1px solid #e2e8f0;
    border-radius: 8px;
    background: white;
    box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
    gap: 1.5rem;
  "
>
  <!-- Grid content -->
</GridForm>
```

### CSS Classes

```css
.custom-grid-form {
  max-width: 1000px;
  margin: 0 auto;
  padding: 2rem;
  background: #fafbfc;
  border-radius: 8px;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}

.custom-grid-form .form-cell {
  padding: 0.5rem;
  border-radius: 4px;
  background: white;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
}

.custom-grid-form .form-cell.vertical {
  /* Vertical layout specific styles */
}

.custom-grid-form .form-cell.horizontal {
  /* Horizontal layout specific styles */
}
```

## Responsive Design

```svelte
<GridForm columns={4} class="responsive-grid">
  <GridField row={1} col={1} colSpan={2} label="Field 1">
    <TextEditor bind:value={field1} />
  </GridField>
  
  <GridField row={1} col={3} colSpan={2} label="Field 2">
    <TextEditor bind:value={field2} />
  </GridField>
  
  <GridField row={2} col={1} colSpan={4} label="Full Width Field">
    <MemoEditor bind:value={field3} />
  </GridField>
</GridForm>

<style>
  @media (max-width: 768px) {
    :global(.responsive-grid) {
      grid-template-columns: 1fr !important;
    }
    
    :global(.responsive-grid .form-cell) {
      grid-column: 1 !important;
      grid-row: auto !important;
    }
  }
</style>
```

## Best Practices

### 1. Plan Your Grid Layout

Sketch your form layout before implementing:

```svelte
<!-- 
Grid Layout Plan (3 columns):
Row 1: [Image  ] [Name     ] [Phone    ]
Row 2: [Image  ] [Email    ] [Country  ]
Row 3: [Image  ] [Address (spans 2 cols)]
Row 4: [Notes (spans all 3 columns)    ]
-->

<GridForm columns={3}>
  <GridField row={1} col={1} rowSpan={3} label="Photo">
    <!-- Image upload component -->
  </GridField>
  
  <GridField row={1} col={2} label="Name">
    <TextEditor bind:value={name} />
  </GridField>
  
  <GridField row={1} col={3} label="Phone">
    <TextEditor bind:value={phone} />
  </GridField>
  
  <!-- Continue with planned layout -->
</GridForm>
```

### 2. Use Meaningful Grid Dimensions

Choose column counts that match your content:

```svelte
<!-- For simple forms: 2-3 columns -->
<GridForm columns={3}>
  <!-- Standard 3-column layout -->
</GridForm>

<!-- For complex forms: 4-6 columns -->
<GridForm columns={6}>
  <!-- More flexibility for complex layouts -->
</GridForm>

<!-- For detailed forms: 12 columns (like Bootstrap) -->
<GridForm columns={12}>
  <!-- Maximum flexibility -->
</GridForm>
```

### 3. Group Related Fields

Keep related fields visually connected:

```svelte
<GridForm columns={4}>
  <!-- Personal Information Section -->
  <GridField row={1} col={1} colSpan={4} label="Personal Information">
    <h3 style="margin: 0; padding: 0.5rem; background: #f8f9fa;">
      Personal Details
    </h3>
  </GridField>
  
  <GridField row={2} col={1} colSpan={2} label="First Name">
    <TextEditor bind:value={firstName} />
  </GridField>
  
  <GridField row={2} col={3} colSpan={2} label="Last Name">
    <TextEditor bind:value={lastName} />
  </GridField>
  
  <!-- Contact Information Section -->
  <GridField row={3} col={1} colSpan={4} label="Contact Information">
    <h3 style="margin: 0; padding: 0.5rem; background: #f8f9fa;">
      Contact Details
    </h3>
  </GridField>
  
  <!-- Contact fields -->
</GridForm>
```

### 4. Handle Empty Space

Use EmptyCell for intentional spacing:

```svelte
<GridForm columns={5}>
  <GridField row={1} col={1} colSpan={2} label="Important Field">
    <TextEditor bind:value={important} />
  </GridField>
  
  <!-- Intentional empty space -->
  <EmptyCell row={1} col={3} />
  
  <GridField row={1} col={4} colSpan={2} label="Another Field">
    <TextEditor bind:value={another} />
  </GridField>
</GridForm>
```

## Advanced Patterns

### Dynamic Grid Content

```svelte
<script>
  let sections = [
    { id: 1, title: 'Section A', fields: ['field1', 'field2'] },
    { id: 2, title: 'Section B', fields: ['field3', 'field4', 'field5'] }
  ];
  
  let currentRow = 1;
</script>

<GridForm columns={4}>
  {#each sections as section}
    <GridField row={currentRow} col={1} colSpan={4} label={section.title}>
      <h3>{section.title}</h3>
    </GridField>
    
    {#each section.fields as field, index}
      <GridField 
        row={currentRow + 1 + Math.floor(index / 2)} 
        col={1 + (index % 2) * 2} 
        colSpan={2} 
        label={field}
      >
        <TextEditor bind:value={formData[field]} />
      </GridField>
    {/each}
    
    <!-- Update currentRow for next section -->
    {currentRow = currentRow + 2 + Math.ceil(section.fields.length / 2)}
  {/each}
</GridForm>
```

## Comparison with Other Form Layouts

| Feature | GridForm | FlexForm | FlexRowForm |
|---------|----------|----------|-------------|
| Layout Type | CSS Grid | Flexbox wrapping | Structured rows |
| Positioning | Explicit (row/col) | Auto-flow | Row-based |
| Spanning | Rows & columns | Columns only | Columns only |
| Complexity | Highest | Lowest | Medium |
| Use Case | Complex layouts | Simple forms | Structured forms |
| Learning Curve | Steeper | Gentle | Moderate |

## Accessibility Features

- **Logical Tab Order**: Fields flow in logical reading order
- **Screen Reader Support**: Proper grid structure announced
- **Keyboard Navigation**: Standard form navigation patterns
- **Error Association**: Clear error message positioning
- **Focus Management**: Proper focus flow through grid cells

## Browser Support

- **Modern Browsers**: Chrome, Firefox, Safari, Edge (latest versions)
- **Mobile Browsers**: iOS Safari, Chrome Mobile, Firefox Mobile
- **CSS Grid**: Required for layout functionality (IE11+ with prefixes)

## Related Components

- **FormField**: Base form field component with labels and validation
- **FlexForm**: Simpler automatic-wrapping form layout
- **FlexRowForm**: Structured row-based form layout
- **TextEditor**: Single-line text input component
- **DatePicker**: Date selection component

## License

MIT License - see LICENSE file for details.