ค้นหาที่อยู่แบบ autocomplete
ค้นหาที่อยู่ไทยจากข้อความที่พิมพ์ พร้อมค่าที่อยู่แบบมีโครงสร้าง
ThaiAddressAutocomplete เหมาะกับหน้าฟอร์มที่ผู้ใช้ต้องการค้นหาที่อยู่ด้วยตำบล อำเภอ/เขต จังหวัด หรือรหัสไปรษณีย์
'use client'
import { ThaiAddressAutocomplete } from '@/components/thai-address-autocomplete'
export default function AutocompleteDemo() {
return <ThaiAddressAutocomplete onValueChange={(address) => console.log(address)} />
}ติดตั้ง
npx react-thaizip add autocompleteCLI เลือกไฟล์ให้ตาม style ที่ init บันทึกไว้: โปรเจกต์ที่มี components.json ของ shadcn/ui จะได้ไฟล์ที่ประกอบจาก primitives ของ shadcn (Base UI, Radix หรือ React Aria) นอกนั้นได้แบบ vanilla
วิธีใช้
import { ThaiAddressAutocomplete } from '@/components/thai-address-autocomplete'<ThaiAddressAutocomplete onValueChange={(address) => console.log(address)} />ค่าใน onValueChange คือ ResolvedThaiAddress | null เมื่อผู้ใช้เลือกคำแนะนำจะได้ที่อยู่แบบมีโครงสร้าง และการพิมพ์ให้ข้อความว่างจะล้างที่อยู่ที่เลือกอยู่แล้วด้วยค่า null
ตัวอย่าง
ค้นหาภาษาอังกฤษ
ใช้ locale="en" เพื่อแสดงป้ายกำกับ คำใบ้ และผลลัพธ์เป็นภาษาอังกฤษ ข้อมูลที่ส่งกลับยังมีชื่อทั้งไทยและอังกฤษ
'use client'
import { ThaiAddressAutocomplete } from '@/components/thai-address-autocomplete'
export default function AutocompleteEn() {
return <ThaiAddressAutocomplete locale="en" onValueChange={(address) => console.log(address)} />
}ป้ายกำกับ
คอมโพเนนต์ไม่มีป้ายกำกับในตัว ครอบด้วย <label> เพื่อให้ช่องมีชื่อที่ screen reader อ่านได้ และเพิ่มคำอธิบายไว้ด้านล่าง
พิมพ์ตำบล อำเภอ หรือรหัสไปรษณีย์ แล้วเลือกจากรายการ
import { ThaiAddressAutocomplete } from '@/components/thai-address-autocomplete'
export default function AutocompleteLabel() {
return (
<div className="flex flex-col gap-2">
<label className="flex flex-col gap-2 text-sm font-medium">
ที่อยู่จัดส่ง
<ThaiAddressAutocomplete />
</label>
<p className="text-sm text-muted-foreground">พิมพ์ตำบล อำเภอ หรือรหัสไปรษณีย์ แล้วเลือกจากรายการ</p>
</div>
)
}ควบคุมค่าจากภายนอก
ส่ง value เพื่อควบคุมค่า หรือส่ง defaultValue เพื่อตั้งค่าเริ่มต้นสำหรับโหมด uncontrolled ปุ่มในตัวอย่างนี้ล้างค่าที่ควบคุมอยู่และแสดงผลลัพธ์ที่ callback ส่งกลับมา
ค่าปัจจุบัน
null
'use client'
import { useState } from 'react'
import type { ResolvedThaiAddress } from 'thaizip'
import { ThaiAddressAutocomplete } from '@/components/thai-address-autocomplete'
export default function AutocompleteControlled() {
const [address, setAddress] = useState<ResolvedThaiAddress | null>(null)
return (
<div className="flex flex-col gap-3">
<ThaiAddressAutocomplete value={address} onValueChange={setAddress} />
<div>
<button type="button" className="rounded-md border border-input bg-background px-3 py-1.5 text-sm hover:bg-accent" onClick={() => setAddress(null)}>
ล้างค่า
</button>
</div>
<p className="text-sm font-medium">ค่าปัจจุบัน</p>
<pre className="max-h-72 overflow-auto rounded-md border bg-muted p-3 text-xs">{JSON.stringify(address, null, 2)}</pre>
</div>
)
}ปิดใช้งาน
ส่ง disabled เพื่อปิดช่องค้นหา
'use client'
import { ThaiAddressAutocomplete } from '@/components/thai-address-autocomplete'
export default function AutocompleteDisabled() {
return <ThaiAddressAutocomplete disabled />
}ไม่ผ่านการตรวจสอบ
ส่ง aria-invalid เพื่อแสดงว่าช่องนี้ไม่ผ่านการตรวจสอบ เช่นหลังตรวจฟอร์มไม่ผ่าน
'use client'
import { ThaiAddressAutocomplete } from '@/components/thai-address-autocomplete'
export default function AutocompleteInvalid() {
return <ThaiAddressAutocomplete aria-invalid />
}ปรับข้อความ
ใช้ texts เพื่อแทนที่ placeholder และข้อความตอนโหลด/ผิดพลาด/ไม่พบผลลัพธ์ คีย์ที่ไม่ได้ส่งจะใช้ค่าเริ่มต้นตาม locale
import { ThaiAddressAutocomplete } from '@/components/thai-address-autocomplete'
export default function AutocompleteTexts() {
return (
<ThaiAddressAutocomplete
texts={{
placeholder: 'ค้นหาจากรหัสไปรษณีย์ เช่น 10110',
emptyText: 'ไม่พบที่อยู่นี้ ลองพิมพ์ชื่ออำเภอแทน',
}}
/>
)
}ปรับสไตล์
แต่ละส่วนของคอมโพเนนต์มี class prop ของตัวเอง (ดู API Reference) class ที่ส่งจะรวมด้วย cn จึงทับค่าเริ่มต้นได้
import { ThaiAddressAutocomplete } from '@/components/thai-address-autocomplete'
export default function AutocompleteStyled() {
return (
<ThaiAddressAutocomplete
inputClassName="rounded-full"
popupClassName="rounded-2xl"
itemClassName="rounded-xl"
/>
)
}ใช้ในฟอร์ม
ส่ง name เพื่อส่งที่อยู่ที่เลือกไปกับฟอร์มปกติ คอมโพเนนต์จะสร้าง hidden inputs สี่ช่อง (address-subdistrict, address-district, address-province, address-zipcode) และ required จะกันไม่ให้ส่งฟอร์มจนกว่าจะเลือกที่อยู่ ดูรูปแบบอื่นได้ที่ การใช้งานร่วมกับฟอร์ม
'use client'
import { useRef, useState, type FormEvent } from 'react'
import { ThaiAddressAutocomplete } from '@/components/thai-address-autocomplete'
export default function FormAutocomplete() {
const ref = useRef<HTMLInputElement>(null)
const [payload, setPayload] = useState<Record<string, FormDataEntryValue> | null>(null)
function handleSubmit(event: FormEvent<HTMLFormElement>) {
event.preventDefault()
setPayload(Object.fromEntries(new FormData(event.currentTarget)))
}
return (
<form className="flex flex-col gap-3" onSubmit={handleSubmit}>
<ThaiAddressAutocomplete
ref={ref}
name="address"
required
/>
<div className="flex gap-2">
<button type="button" className="rounded-md border border-input bg-background px-3 py-1.5 text-sm hover:bg-accent" onClick={() => ref.current?.focus()}>
โฟกัสช่องที่อยู่
</button>
<button type="submit" className="rounded-md border border-input bg-background px-3 py-1.5 text-sm hover:bg-accent">
ส่งฟอร์ม
</button>
</div>
<p className="text-sm font-medium">FormData ที่ส่ง</p>
<pre className="max-h-72 overflow-auto rounded-md border bg-muted p-3 text-xs">{JSON.stringify(payload, null, 2)}</pre>
</form>
)
}API Reference
| Prop | Type | ค่าเริ่มต้น | รายละเอียด |
|---|---|---|---|
value | ResolvedThaiAddress | null | — | ค่าที่ควบคุมอยู่; ส่ง null เพื่อล้าง |
defaultValue | ResolvedThaiAddress | null | — | ค่าเริ่มต้นของโหมด uncontrolled และเติมข้อความค้นหาเริ่มต้น |
onValueChange | (address: ResolvedThaiAddress | null) => void | — | เรียกเมื่อเลือกหรือล้างที่อยู่ |
name | string | — | สร้าง hidden inputs สี่ช่อง: <name>-subdistrict, -district, -province, -zipcode |
locale | 'th' | 'en' | 'th' | ภาษาของผลลัพธ์และข้อความเริ่มต้น |
texts | Partial<ThaiAddressAutocompleteTexts> | — | แทนที่ placeholder, clearAriaLabel, loadingText, errorText, retryLabel, emptyText |
indexLoad | 'mount' | 'visible' | 'mount' | โหลดข้อมูลที่อยู่ตอน mount หรือรอจนช่องเลื่อนเข้ามาในจอ |
limit | number | 10 | จำนวนคำแนะนำสูงสุด |
debounce | number | 200 | ระยะหน่วงก่อนค้นหา (มิลลิวินาที) |
threshold | number | 0.4 | คะแนนความใกล้เคียงขั้นต่ำ (0–1) ที่คำแนะนำจะถูกแสดง |
disabled | boolean | false | ปิดใช้งานช่องค้นหา |
required | boolean | — | กำหนดให้ช่องต้องมีค่า |
aria-invalid | React.AriaAttributes['aria-invalid'] | — | ระบุสถานะไม่ผ่านการตรวจสอบ |
onBlur | React.FocusEventHandler | — | Vanilla: ทำงานเมื่อ input สูญเสียโฟกัส ส่วน shadcn/ui: ทำงานที่ปุ่ม trigger (HTMLButtonElement) |
onError | (error: Error) => void | — | ทำงานเมื่อโหลดดัชนีที่อยู่ไม่สำเร็จ |
className | string | — | wrapper หลัก |
inputClassName | string | — | ช่องพิมพ์ค้นหา |
popupClassName | string | — | กล่องผลลัพธ์ |
itemClassName | string | — | แต่ละผลลัพธ์ |
ref | React.Ref<HTMLInputElement> | — | ส่งต่อไปยัง input ใน style shadcn/ui นั้น input อยู่ใน popover จึงเป็น null ระหว่างที่ popover ปิดอยู่ |
limit, debounce และ threshold ส่งต่อตรงไปยัง hook useThaiAddressAutocomplete ของ thaizip
ดูเพิ่มเติม
ดู การใช้งานร่วมกับฟอร์ม สำหรับการส่งค่าที่อยู่ และ การปรับแต่ง สำหรับปรับหน้าตา ส่วน UI แบบเลือกตามลำดับ ดู cascade select