thaizip

ค้นหาที่อยู่แบบ 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 autocomplete

CLI เลือกไฟล์ให้ตาม 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 จะกันไม่ให้ส่งฟอร์มจนกว่าจะเลือกที่อยู่

FormData ที่ส่ง

null
'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>
  )
}

ชื่อพื้นที่ใน hidden inputs เป็นภาษาไทยเสมอ แม้ UI จะแสดงภาษาอังกฤษ ถ้า disabled hidden inputs จะถูก disabled ด้วยและไม่อยู่ใน FormData ถ้าใช้ไลบรารีฟอร์ม ให้เก็บ ResolvedThaiAddress | null ไว้ใน state ของฟอร์มแล้วส่งกลับผ่าน value เพื่อให้ validation, reset และ submit ใช้ค่าเดียวกัน

API Reference

PropTypeค่าเริ่มต้นรายละเอียด
valueResolvedThaiAddress | null—ค่าที่ควบคุมอยู่; ส่ง null เพื่อล้าง
defaultValueResolvedThaiAddress | null—ค่าเริ่มต้นของโหมด uncontrolled และเติมข้อความค้นหาเริ่มต้น
onValueChange(address: ResolvedThaiAddress | null) => void—เรียกเมื่อเลือกหรือล้างที่อยู่
namestring—สร้าง hidden inputs สี่ช่อง: <name>-subdistrict, -district, -province, -zipcode
locale'th' | 'en''th'ภาษาของผลลัพธ์และข้อความเริ่มต้น
textsPartial<ThaiAddressAutocompleteTexts>—แทนที่ placeholder, clearAriaLabel, loadingText, errorText, retryLabel, emptyText
indexLoad'mount' | 'visible''mount'โหลดข้อมูลที่อยู่ตอน mount หรือรอจนช่องเลื่อนเข้ามาในจอ
limitnumber10จำนวนคำแนะนำสูงสุด
debouncenumber200ระยะหน่วงก่อนค้นหา (มิลลิวินาที)
thresholdnumber0.4คะแนนความใกล้เคียงขั้นต่ำ (0–1) ที่คำแนะนำจะถูกแสดง
disabledbooleanfalseปิดใช้งานช่องค้นหา
requiredboolean—กำหนดให้ช่องต้องมีค่า
aria-invalidReact.AriaAttributes['aria-invalid']—ระบุสถานะไม่ผ่านการตรวจสอบ
onBlurReact.FocusEventHandler<HTMLInputElement>—ทำงานเมื่อ input สูญเสียโฟกัส
onError(error: Error) => void—ทำงานเมื่อโหลดดัชนีที่อยู่ไม่สำเร็จ
classNamestring—wrapper หลัก
inputClassNamestring—ช่องพิมพ์ค้นหา
popupClassNamestring—กล่องผลลัพธ์
itemClassNamestring—แต่ละผลลัพธ์
refReact.Ref<HTMLInputElement>—ส่งต่อไปยัง input

limit, debounce และ threshold ส่งต่อตรงไปยัง hook useThaiAddressAutocomplete ของ thaizip