# Input Components

> **Props y API:** Disponibles vía MCP tool `widgets-get-component-props`. Este archivo documenta solo convenciones, gotchas y patrones específicos del proyecto.

---

## ⚠️ CRITICAL: Handler Convention — READ THIS FIRST

Dynamic UI input components pass **values directly**, NOT event objects.

```tsx
// ❌ HTML Standard (events)
<input value={value} onChange={(e) => setValue(e.target.value)} />

// ✅ Dynamic UI (direct values)
<DInput id="search" value={value} onChange={(value) => setValue(value)} iconStart="Search" />
```

### Handler Types Reference

| Component | Handler Type | Example |
|-----------|--------------|---------|
| **DInput** | `(value: string) => void` | `onChange={(value) => setValue(value)}` |
| **DInputSwitch** | `(checked: boolean) => void` | `onChange={(checked) => setEnabled(checked)}` |
| **DInputCurrency** | `(value: number) => void` | `onChange={(value) => setAmount(value)}` |
| **DInputCheck** | `(checked: boolean) => void` | `onChange={(checked) => setChecked(checked)}` |
| **DInputPin** | `(value: string) => void` | `onChange={(value) => setPin(value)}` |
| **DInputPhone** | `(value: string) => void` | `onChange={(value) => setPhone(value)}` |
| **DInputCounter** | `(value: number \| undefined) => void` | `onChange={(value) => setCount(value ?? 0)}` |
| **⚠️ DInputRange** | `ChangeEventHandler<HTMLInputElement>` | `onChange={(e) => setValue(Number(e.target.value))}` |
| **⚠️ DInputMask** | `ChangeEventHandler<HTMLInputElement>` | `onChange={(e) => setValue(e.target.value)}` |

### ❌ Common Mistakes

```tsx
// ❌ WRONG: Trying to access event object
<DInput id="search" value={query} onChange={(e) => setQuery(e.target.value)} />
// Error: Cannot read properties of undefined (reading 'value')

// ✅ CORRECT: Use value directly
<DInput id="search" type="search" value={query} onChange={(value) => setQuery(value)} iconStart="Search" />

// ❌ WRONG: Event on boolean input
<DInputSwitch checked={enabled} onChange={(e) => setEnabled(e.target.checked)} />

// ✅ CORRECT
<DInputSwitch checked={enabled} onChange={(checked) => setEnabled(checked)} />
```

---

## DInputSelect vs DSelect — When to Use Which

| Feature | DInputSelect | DSelect |
|---------|-------------|---------|
| **Rendering** | Native HTML `<select>` | react-select custom dropdown |
| **Search/filter** | No | Yes (`searchable`) |
| **Multi-select** | No | Yes (`multi`) |
| **Mobile UX** | Native picker | Custom dropdown |
| **onChange** | Receives full option object | Receives full option object |

**Use DInputSelect:** Simple dropdown, few options, mobile-friendly.
**Use DSelect:** Search, multi-select, custom rendering, many options.

### ⚠️ DInputSelect onChange — Receives Object, Not Value

```tsx
// ❌ WRONG
<DInputSelect options={options} onChange={(value) => setSelected(value)} />

// ✅ CORRECT — extract .value from object
<DInputSelect options={options} onChange={(option) => setSelected(option.value)} />

// Custom objects — use valueExtractor/labelExtractor
<DInputSelect<Country> label="Country" options={countries} value={selectedId}
  onChange={(c) => setSelectedId(c.id)}
  valueExtractor={(item) => item.id} labelExtractor={(item) => item.name} />
```

---

## Component-Specific Notes

- **DInputCurrency:** `value` is `number` (not string), `onChange` receives `number`
- **DInputRange:** Exception — `onChange` receives event: `onChange={(e) => setValue(Number(e.target.value))}`
- **DInputSearch:** Removed in v2.0 — use `<DInput id="search" type="search" iconStart="Search" />`

---

> See also: `modyo://docs/widgets/components-inputs-advanced` for DInputMask, DInputCounter, and DBoxFile.
