เลือกที่อยู่แบบลำดับชั้น
เลือกจังหวัด อำเภอหรือเขต และตำบลหรือแขวงตามลำดับ
ThaiAddressCascadeSelect เหมาะกับฟอร์มที่ต้องการให้ผู้ใช้เลือกที่อยู่ตามลำดับ จังหวัด → อำเภอ/เขต → ตำบล/แขวง
'use client'
import { ThaiAddressCascadeSelect } from '@/components/thai-address-cascade-select'
export default function CascadeSelectDemo() {
return <ThaiAddressCascadeSelect onValueChange={(address) => console.log(address)} />
}ติดตั้ง
npx react-thaizip add cascade-selectCLI เลือกไฟล์ให้ตาม style ที่ init บันทึกไว้: โปรเจกต์ที่มี components.json ของ shadcn/ui จะได้ไฟล์ที่ประกอบจาก primitives ของ shadcn (Base UI, Radix หรือ React Aria) นอกนั้นได้แบบ vanilla
วิธีใช้
import { ThaiAddressCascadeSelect } from '@/components/thai-address-cascade-select'<ThaiAddressCascadeSelect onValueChange={(address) => console.log(address)} />onValueChange จะส่ง ResolvedThaiAddress | null เมื่อเลือกครบทั้งสามระดับ
เมื่อเปลี่ยนตัวเลือกหลัก
เมื่อเลือกที่อยู่ครบแล้ว การเปลี่ยนจังหวัดหรืออำเภอ/เขตจะเรียก onValueChange(null) และล้างตัวเลือกปลายทางที่ไม่สอดคล้องกัน ผู้ใช้จึงต้องเลือกตัวเลือกที่อยู่ถัดไปใหม่
ตัวอย่าง
เลือกข้อมูลภาษาอังกฤษ
ใช้ locale="en" เพื่อแสดงป้ายและตัวเลือกเป็นภาษาอังกฤษ
'use client'
import { ThaiAddressCascadeSelect } from '@/components/thai-address-cascade-select'
export default function CascadeSelectEn() {
return <ThaiAddressCascadeSelect locale="en" onValueChange={(address) => console.log(address)} />
}ควบคุมค่าจากภายนอก
ส่ง value เพื่อควบคุมค่าทั้งชุด หรือ defaultValue เพื่อตั้งลำดับที่เลือกไว้ตอนเริ่มต้น ตัวอย่างนี้แสดงค่า callback และมีปุ่มล้างค่า
ค่าปัจจุบัน
null
'use client'
import { useState } from 'react'
import type { ResolvedThaiAddress } from 'thaizip'
import { ThaiAddressCascadeSelect } from '@/components/thai-address-cascade-select'
export default function CascadeSelectControlled() {
const [address, setAddress] = useState<ResolvedThaiAddress | null>(null)
return (
<div className="flex flex-col gap-3">
<ThaiAddressCascadeSelect 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 เพื่อปิดทุก select
'use client'
import { ThaiAddressCascadeSelect } from '@/components/thai-address-cascade-select'
export default function CascadeSelectDisabled() {
return <ThaiAddressCascadeSelect disabled />
}ไม่ผ่านการตรวจสอบ
ส่ง aria-invalid เพื่อทำเครื่องหมายว่าไม่ผ่านการตรวจสอบทั้งสาม trigger
'use client'
import { ThaiAddressCascadeSelect } from '@/components/thai-address-cascade-select'
export default function CascadeSelectInvalid() {
return <ThaiAddressCascadeSelect aria-invalid />
}ปรับข้อความ
ใช้ texts เพื่อแทนที่ป้ายกำกับและ placeholder เช่นให้ตรงกับคำว่าเขต/แขวงของกรุงเทพฯ คีย์ที่ไม่ได้ส่งจะใช้ค่าเริ่มต้นตาม locale
import { ThaiAddressCascadeSelect } from '@/components/thai-address-cascade-select'
export default function CascadeSelectTexts() {
return (
<ThaiAddressCascadeSelect
texts={{
provinceLabel: 'จังหวัดที่จัดส่ง',
provincePlaceholder: 'เลือกจังหวัด',
districtLabel: 'เขต/อำเภอ',
subdistrictLabel: 'แขวง/ตำบล',
}}
/>
)
}ใช้ในฟอร์ม
ส่ง name เพื่อส่งค่าที่เลือกไปกับฟอร์มปกติ คอมโพเนนต์จะสร้าง hidden inputs สี่ช่อง (address-subdistrict, address-district, address-province, address-zipcode) และ required จะกันไม่ให้ส่งฟอร์มจนกว่าจะเลือกครบทั้งสามระดับ ดูรูปแบบอื่นได้ที่ การใช้งานร่วมกับฟอร์ม
'use client'
import { useRef, useState, type FormEvent } from 'react'
import { ThaiAddressCascadeSelect } from '@/components/thai-address-cascade-select'
export default function FormCascadeSelect() {
const ref = useRef<HTMLButtonElement>(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}>
<ThaiAddressCascadeSelect
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<ThaiAddressCascadeSelectTexts> | — | แทนที่ข้อความ *Label/*Placeholder ของแต่ละระดับ รวมถึง loadingText, errorText, retryLabel |
indexLoad | 'mount' | 'visible' | 'mount' | โหลดข้อมูลที่อยู่ตอน mount หรือรอจนช่องเลื่อนเข้ามาในจอ |
disabled | boolean | false | ปิดใช้งานทุก select |
required | boolean | — | กำหนดให้ทั้งสาม select ต้องมีค่า |
aria-invalid | React.AriaAttributes['aria-invalid'] | — | ทำเครื่องหมายว่าไม่ผ่านการตรวจสอบทั้งสาม trigger |
onBlur | React.FocusEventHandler<HTMLButtonElement> | — | ทำงานเมื่อ trigger ของจังหวัดสูญเสียโฟกัส |
onError | (error: Error) => void | — | ทำงานเมื่อโหลดดัชนีที่อยู่ไม่สำเร็จ |
className | string | — | grid wrapper หลัก |
labelClassName | string | — | ป้ายกำกับของทุกช่อง |
triggerClassName | string | — | trigger ของ select และช่องรหัสไปรษณีย์ |
popupClassName | string | — | กล่องตัวเลือก |
itemClassName | string | — | แต่ละตัวเลือก |
ref | React.Ref<HTMLButtonElement> | — | ส่งต่อไปยัง trigger ของจังหวัด ซึ่งเป็นตัวควบคุมหลักของ cascade |
ทั้ง ref และ onBlur ผูกกับ trigger ของจังหวัด ไม่ใช่ trigger ของอำเภอหรือตำบล
ดูเพิ่มเติม
ดู การใช้งานร่วมกับฟอร์ม สำหรับการส่งค่าที่อยู่ และ การปรับแต่ง สำหรับปรับหน้าตา ส่วนการค้นหาด้วยข้อความ ดู autocomplete