PlaceLookupInput
Find a place, like a city or an airport, through your own search.
SourcePackage @kerf-js/veneer
Preview
tsx
import { PlaceLookupInput } from '@kerf-js/veneer'import type { Place } from '@kerf-js/veneer'export function Basic() { return ( <PlaceLookupInput label="Office location" hint="Try Stockholm or Lima." search={findPlace} biases={PLACE_KINDS} /> )}const PLACES: Place[] = [ { id: 'sto', displayName: 'Stockholm, Sweden', category: 'City', lat: 59.3293, lng: 18.0686, timezone: 'Europe/Stockholm', }, { id: 'ber', displayName: 'Berlin, Germany', category: 'City', lat: 52.52, lng: 13.405, timezone: 'Europe/Berlin', }, { id: 'lis', displayName: 'Lisbon, Portugal', category: 'City', lat: 38.7223, lng: -9.1393, timezone: 'Europe/Lisbon', }, { id: 'lim', displayName: 'Lima, Peru', category: 'City', lat: -12.0464, lng: -77.0428, timezone: 'America/Lima', }, { id: 'tok', displayName: 'Tokyo, Japan', category: 'City', lat: 35.6762, lng: 139.6503, timezone: 'Asia/Tokyo', }, { id: 'arn', displayName: 'Stockholm Arlanda Airport', category: 'Airport', lat: 59.6498, lng: 17.9238, timezone: 'Europe/Stockholm', }, { id: 'lhr', displayName: 'London Heathrow Airport', category: 'Airport', lat: 51.47, lng: -0.4543, timezone: 'Europe/London', },]const PLACE_KINDS = [ { id: 'city', label: 'Cities', default: true }, { id: 'air', label: 'Airports' },]const findPlace = async ( q: string, o: { bias: Record<string, boolean> },) => { await new Promise((done) => { setTimeout(done, 200) }) const kinds = [ o.bias.city && 'City', o.bias.air && 'Airport', ] return PLACES.filter( (p) => kinds.includes(p.category) && p.displayName .toLowerCase() .includes(q.toLowerCase()), )}Installation
npm install @kerf-js/veneerUsage
tsx
import { PlaceLookupInput } from '@kerf-js/veneer'<PlaceLookupInput label="Office location" hint="Try Stockholm or Lima." search={findPlace} biases={PLACE_KINDS}/>Use what was picked
The value is the whole place, coordinates and time zone included.
tsx
import { state } from '@kerf-js/core'import { PlaceLookupInput, Stack, Text} from '@kerf-js/veneer'import type { Place } from '@kerf-js/veneer'export function Picked() { const place = state<Place | null>(PLACES[0] ?? null) return ( <Stack gap="related"> <PlaceLookupInput label="Nearest office" search={findPlace} value={place} /> <Text subtle> {place.value ? 'New deploys run close to ' + `${place.value.displayName}.` : 'Pick a place from the list.'} </Text> </Stack> )}const PLACES: Place[] = [ { id: 'sto', displayName: 'Stockholm, Sweden', category: 'City', lat: 59.3293, lng: 18.0686, timezone: 'Europe/Stockholm', }, { id: 'ber', displayName: 'Berlin, Germany', category: 'City', lat: 52.52, lng: 13.405, timezone: 'Europe/Berlin', }, { id: 'lis', displayName: 'Lisbon, Portugal', category: 'City', lat: 38.7223, lng: -9.1393, timezone: 'Europe/Lisbon', }, { id: 'lim', displayName: 'Lima, Peru', category: 'City', lat: -12.0464, lng: -77.0428, timezone: 'America/Lima', }, { id: 'tok', displayName: 'Tokyo, Japan', category: 'City', lat: 35.6762, lng: 139.6503, timezone: 'Asia/Tokyo', }, { id: 'arn', displayName: 'Stockholm Arlanda Airport', category: 'Airport', lat: 59.6498, lng: 17.9238, timezone: 'Europe/Stockholm', }, { id: 'lhr', displayName: 'London Heathrow Airport', category: 'Airport', lat: 51.47, lng: -0.4543, timezone: 'Europe/London', },]const findPlace = async ( q: string, o: { bias: Record<string, boolean> },) => { await new Promise((done) => { setTimeout(done, 200) }) const kinds = [ o.bias.city && 'City', o.bias.air && 'Airport', ] return PLACES.filter( (p) => kinds.includes(p.category) && p.displayName .toLowerCase() .includes(q.toLowerCase()), )}States
A place lookup can show a hint or an error, and can be disabled or readonly.
tsx
import { Grid, PlaceLookupInput, Stack, Text} from '@kerf-js/veneer'import type { Place } from '@kerf-js/veneer'export function States() { return ( <Grid cols={3} gap="group" > <Cell name="Empty"> <PlaceLookupInput label="Office location" search={findPlace} /> </Cell> <Cell name="Filled"> <PlaceLookupInput label="Office location" search={findPlace} defaultValue={PLACES[1]!} /> </Cell> <Cell name="Hint"> <PlaceLookupInput label="Office location" search={findPlace} hint="Used to pick a region." /> </Cell> <Cell name="Invalid"> <PlaceLookupInput label="Office location" search={findPlace} error="Pick a place from the list." /> </Cell> <Cell name="Disabled"> <PlaceLookupInput label="Office location" search={findPlace} defaultValue={PLACES[1]!} disabled /> </Cell> <Cell name="Read-only"> <PlaceLookupInput label="Office location" search={findPlace} defaultValue={PLACES[1]!} readonly /> </Cell> </Grid> )}const Cell = ({ name, children,}: { name: string children?: unknown}) => ( <Stack gap="tight"> <Text role="caption" subtle > {name} </Text> {children} </Stack>)const PLACES: Place[] = [ { id: 'sto', displayName: 'Stockholm, Sweden', category: 'City', lat: 59.3293, lng: 18.0686, timezone: 'Europe/Stockholm', }, { id: 'ber', displayName: 'Berlin, Germany', category: 'City', lat: 52.52, lng: 13.405, timezone: 'Europe/Berlin', }, { id: 'lis', displayName: 'Lisbon, Portugal', category: 'City', lat: 38.7223, lng: -9.1393, timezone: 'Europe/Lisbon', }, { id: 'lim', displayName: 'Lima, Peru', category: 'City', lat: -12.0464, lng: -77.0428, timezone: 'America/Lima', }, { id: 'tok', displayName: 'Tokyo, Japan', category: 'City', lat: 35.6762, lng: 139.6503, timezone: 'Asia/Tokyo', }, { id: 'arn', displayName: 'Stockholm Arlanda Airport', category: 'Airport', lat: 59.6498, lng: 17.9238, timezone: 'Europe/Stockholm', }, { id: 'lhr', displayName: 'London Heathrow Airport', category: 'Airport', lat: 51.47, lng: -0.4543, timezone: 'Europe/London', },]const findPlace = async ( q: string, o: { bias: Record<string, boolean> },) => { await new Promise((done) => { setTimeout(done, 200) }) const kinds = [ o.bias.city && 'City', o.bias.air && 'Airport', ] return PLACES.filter( (p) => kinds.includes(p.category) && p.displayName .toLowerCase() .includes(q.toLowerCase()), )}When to use
Use it for
- A location, not somewhere to send a letter
Reach for something else
- AddressField, when you need a postal address
Accessibility
- ↑↓
- Moves through the results
- Enter
- Picks the highlighted place
- Esc
- Closes the list
API Reference
| Prop | Type | Default | Description |
|---|---|---|---|
label | string | — | The visible name, and what screen readers announce. |
hint | unknown | — | A short line of help under the field. |
error | unknown | — | Replaces the hint and marks the field invalid. |
icon | IconRef | — | Leading icon: it sweeps with the border. |
prefix | unknown | — | Text or an element inside the field before the value, such as $. |
suffix | unknown | — | Text or an element inside the field after the value, such as kg. |
variant | "floating" | "classic" | — | floating (default, or the app's veneer({ fields })) · classic: the label above. |
appearance | "line" | — | line: only a bottom line under the value, no box or fill. The boxed field otherwise. |
size | "sm" | "md" | "lg" | 'md' | Control size. md by default. |
clearable | boolean | false | A clear button (shown once there is a value). |
counter | number | — | Maximum length, shown as a counter. |
id | string | — | The element id. One is made for you if you leave it out. |
disabled | boolean | false | Can't be focused or changed. |
readonly | boolean | false | Shows the value and lets it be selected and copied, but not changed. |
required | boolean | false | The form will not submit without it. |
name | string | — | Posts the value with a form. |
locale | string | — | The language and region used to format values. |
today | string | — | Pins "today" (server render, tests); the browser's clock otherwise. |
presentation | Presentation | — | auto opens a popover, or a sheet when the viewport is under 600px wide; popover and sheet force one. |
value | Place | State<Place | null> | null | — | The place, or a State to bind. |
defaultValue | Place | null | — | The starting value, when the component owns the state. |
search | (q: string, o: { bias: Record<string, boolean>; signal: AbortSignal; }) … | — | Your geocoder. Veneer ships none. Gets the text, the filters and an abort signal. |
biases | PlaceBias[] | — | Filter chips: { id, label, default }. |
minChars | number | 2 | How many letters before it searches. |
| Event | Type | Default | Description |
|---|---|---|---|
onInput | ((e: InputEvent & { currentTarget: HTMLInputElement & HTMLTextAreaElemen… | — | Called on each keystroke. |
onValueChange | ((v: Place | null, reason: ChangeReason) => void) | — | Hears the new place and why it changed. |
RelatedAddress field