Quick-Pick Pattern
The Quick-Pick pattern pairs a select dropdown with preset values alongside a number (or text) input for manual override — both sharing the same binding.path. The user can choose a common value from the dropdown, or type any value directly. Last-write-wins.
The Core Idea
Many numeric fields have common values that users pick most of the time, plus occasional custom values. Forcing users to type common values is slow; a pure dropdown prevents custom values. Quick-Pick solves this with two controls sharing one binding path:
- Select control — offers preset values as fast-click options
- Number control — allows any value to be typed directly
Because both controls bind to the same path, changing one immediately updates the other. Whoever wrote last is the authoritative value.
Basic Implementation — Duration Field
// Both controls share binding.path: "task.durationMinutes"
// Control 1: Preset quick-picks
{
"id": "duration-preset",
"type": "select",
"label": "Duration (preset)",
"width": "half",
"order": 1,
"settings": {
"clearable": true,
"placeholder": "Quick pick...",
"options": [
{ "value": 15, "label": "15 minutes" },
{ "value": 30, "label": "30 minutes" },
{ "value": 60, "label": "1 hour" },
{ "value": 90, "label": "1.5 hours" },
{ "value": 120, "label": "2 hours" },
{ "value": 480, "label": "Full day (8h)" }
]
},
"binding": {
"source": "$json",
"path": "task.durationMinutes"
}
},
// Control 2: Manual override
{
"id": "duration-manual",
"type": "number",
"label": "Duration (minutes)",
"width": "half",
"order": 2,
"settings": {
"min": 1,
"max": 1440,
"step": 5,
"suffix": "min",
"placeholder": "Or type any value"
},
"binding": {
"source": "$json",
"path": "task.durationMinutes"
}
}
How Last-Write-Wins Works
The FormEngine treats both controls as writing to the same field. When the user selects "1 hour" from the dropdown, the engine sets task.durationMinutes = 60 and the number input immediately shows 60. When the user then types 75 in the number input, the engine sets task.durationMinutes = 75 and the dropdown de-selects (no matching option).
Quick-Pick for Currency Amount
// Shared path: "payment.amount"
{
"id": "amount-preset",
"type": "select",
"label": "Amount",
"width": "third",
"order": 1,
"settings": {
"placeholder": "Common amounts",
"options": [
{ "value": 25, "label": "$25" },
{ "value": 50, "label": "$50" },
{ "value": 100, "label": "$100" },
{ "value": 250, "label": "$250" },
{ "value": 500, "label": "$500" }
]
},
"binding": { "source": "$json", "path": "payment.amount" }
},
{
"id": "amount-custom",
"type": "number",
"label": "Custom Amount",
"width": "third",
"order": 2,
"settings": {
"format": "currency",
"currency": "USD",
"min": 1,
"prefix": "$"
},
"binding": { "source": "$json", "path": "payment.amount" }
}
Quick-Pick for Text Values
The pattern works with text fields too. A common example is an email subject line with templates plus a custom option:
{
"id": "subject-preset",
"type": "select",
"label": "Email Subject",
"width": "half",
"settings": {
"clearable": true,
"placeholder": "Use a template...",
"options": [
{ "value": "Invoice #{{invoiceId}} from ACME Corp", "label": "Invoice notification" },
{ "value": "Your order has shipped", "label": "Order shipped" },
{ "value": "Action required: Please review", "label": "Action required" }
]
},
"binding": { "source": "$json", "path": "email.subject" }
},
{
"id": "subject-custom",
"type": "text",
"label": "Or type a custom subject",
"width": "half",
"settings": { "maxLength": 150 },
"binding": { "source": "$json", "path": "email.subject" }
}
Validation on the Shared Path
Apply validation to one of the two controls — whichever is the "primary" control in your mental model. The validation engine evaluates the actual field value regardless of which control wrote it:
// Apply required validation to the manual override control
// (it has the same binding.path, so the FormEngine validates the shared value)
{
"id": "duration-manual",
"type": "number",
"label": "Duration (minutes)",
"validation": {
"required": true,
"min": 1,
"max": 1440
},
"binding": { "source": "$json", "path": "task.durationMinutes" }
}
Layout Recommendations
| Pattern | Select Width | Number/Text Width | Combined Width |
|---|---|---|---|
| Full-row duration | half | half | full row |
| Compact amount | third | third | two-thirds |
| Subject line | half | half | full row |