ChillCMS Design System Component
Ark Combobox Popup Input
Thành phần chọn dạng nút bấm kích hoạt (Trigger Button) kết hợp @ark-ui/react/popover và ô tìm kiếm chuyên dụng bên trong popup. Hỗ trợ tìm kiếm tiếng Việt không dấu, tìm kiếm ngữ nghĩa theo mảng keywords, điều hướng phím tắt và tùy biến toàn diện cả nút bấm kích hoạt lẫn từng dòng gợi ý.
1. Ví dụ cơ bản (Basic Popup Input)Phổ biến nhất
Nút bấm gọn gàng hiển thị lựa chọn hiện tại, bấm mở popover tìm kiếm chuyên dụng
Việt Nam (Hà Nội, TP.HCM) [GMT+7]
Thái Lan (Bangkok) [GMT+7]
Singapore [GMT+8]
Nhật Bản (Tokyo) [GMT+9]
Hàn Quốc (Seoul) [GMT+9]
Úc (Sydney) [GMT+10]
Vương Quốc Anh (London) [GMT+0]
Pháp (Paris) [GMT+1]
Đức (Berlin) [GMT+1]
Hoa Kỳ (New York) [GMT-5]
Hoa Kỳ (Los Angeles) [GMT-8]
Bấm vào nút trigger để mở hộp tìm kiếm, nhập từ khóa lọc và nhấn Enter hoặc click để chọn.
Múi giờ đang chọn:Asia/Ho_Chi_Minh
Mô hình Trigger Button + Internal Search
Khác với Combobox truyền thống (người dùng gõ trực tiếp vào ô input), ArkComboboxPopupInput giữ giao diện gọn gàng như một nút bấm Select chuẩn. Khi mở ra, một ô tìm kiếm chuyên dụng tự động nhận tiêu điểm (focus), giúp giao diện thanh thoát và tiết kiệm không gian toolbar.
<ArkComboboxPopupInput
options={timezones}
value={timezone}
onChange={(val) => setTimezone(val)}
placeholder="Chọn múi giờ..."
searchPlaceholder="Tìm kiếm..."
/>2. Tìm kiếm tiếng Việt không dấu & Từ khóa mở rộngTiện lợi cao
Tìm kiếm chính xác với tiếng Việt không dấu và hỗ trợ từ khóa ngữ nghĩa keywords
Hà Nội
TP. Hồ Chí Minh
Đà Nẵng
Hải Phòng
Cần Thơ
Quảng Ninh
Khánh Hòa
Lâm Đồng
Thừa Thiên Huế
Bình Dương
Bà Rịa - Vũng Tàu
Hỗ trợ tìm kiếm theo cả tiếng Việt có dấu, không dấu và các từ khóa phụ trong mảng keywords.
Tỉnh/thành đã chọn:TP. Hồ Chí Minh
Mã hành chính:79
Sức mạnh tìm kiếm ngữ nghĩa
Mỗi option có thể chứa mảng keywords. Hàm lọc nội bộ filterOption sẽ nối label, value và các từ khóa lại, chuẩn hóa bằng tiện ích removeAccents() giúp trải nghiệm gõ phím cực kỳ tự nhiên.
const options = [
{
label: 'TP. Hồ Chí Minh',
value: '79',
keywords: ['SG', 'TPHCM', 'Sai Gon', 'Mien Nam', '028']
},
{
label: 'Hà Nội',
value: '01',
keywords: ['HN', 'Thu do', 'Mien Bac', '024']
},
];3. Tùy biến giao diện mục hiển thị (renderItemContent)
Hiển thị avatar, tên, email và badge phòng ban sinh động cho từng mục
VA
Nguyễn Văn An
Lead Architect•Kỹ thuật
TB
Trần Thị Bích
Senior Editor•Nội dung
HC
Lê Hoàng Cường
DevOps Engineer•Hạ tầng
MD
Phạm Minh Dũng
Product Designer•Sản phẩm
TG
Đặng Thu Giang
QA Specialist•Kiểm thử
Thành viên đang chọn:
Nguyễn Văn An(Lead Architect - Kỹ thuật)
Tùy biến với `renderItemContent`
Thuộc tính renderItemContent(option) cho phép bạn trả về bất kỳ cấu trúc JSX nào cho từng dòng mục gợi ý trong popup, bao gồm avatar, icon, badge hoặc thông tin phụ nhiều dòng.
<ArkComboboxPopupInput
options={members}
value={selectedId}
renderItemContent={(opt) => (
<div className="flex items-center gap-3">
<Avatar initials={opt.initials} />
<div>
<p className="font-semibold">{opt.name}</p>
<p className="text-2xs">{opt.role}</p>
</div>
</div>
)}
/>4. Tùy biến nút kích hoạt (renderTriggerLabel)Tùy biến cao
Biến nút trigger thành pill trạng thái màu sắc phong cách Notion / Linear
Chưa bắt đầu
Đang thực hiện
Đang đánh giá QA
Hoàn thành
Đã hủy bỏ
Mã trạng thái:in_progress
Biến hóa nút bấm với `renderTriggerLabel`
Nhờ renderTriggerLabel(selectedOption), bạn có thể biến nút kích hoạt thành thẻ pill màu sắc, biểu tượng trạng thái hoặc badge chuyên nghiệp phong cách Notion / Linear mà không làm thay đổi logic bên trong.
<ArkComboboxPopupInput
options={taskStatuses}
value={status}
renderTriggerLabel={(selected) => (
<span className={selected?.badgeClass}>
<span className={selected?.dotClass} />
{selected?.label}
</span>
)}
/>5. Điều khiển bằng bàn phím (Keyboard Navigation)
Hỗ trợ phím mũi tên lên/xuống duyệt nhanh danh sách và phím Enter để chọn
Trợ năng & Phím tắt tương tác
ArrowDown (↓)
Di chuyển highlight xuống mục tiếp theo.
ArrowUp (↑)
Di chuyển highlight lên mục phía trên.
Enter (↵)
Chọn mục đang được highlight và đóng popover.
Việt Nam (Hà Nội, TP.HCM) [GMT+7]
Thái Lan (Bangkok) [GMT+7]
Singapore [GMT+8]
Nhật Bản (Tokyo) [GMT+9]
Hàn Quốc (Seoul) [GMT+9]
Úc (Sydney) [GMT+10]
Vương Quốc Anh (London) [GMT+0]
Pháp (Paris) [GMT+1]
Đức (Berlin) [GMT+1]
Hoa Kỳ (New York) [GMT-5]
Hoa Kỳ (Los Angeles) [GMT-8]
6. Phân loại lớp CSS tùy biến & Mục vô hiệu hóa
Gán class màu sắc riêng cho từng tùy chọn và vô hiệu hóa các mục không khả dụng
Chỉnh sửa thông tin bài viết
Sao chép thành bản nháp mới
Tải xuống tệp PDF sao lưu
Xuất bản tự động lên Facebook (Chưa cấu hình API)
Lưu trữ vào thùng rác
Xóa vĩnh viễn khỏi hệ thống (Nguy hiểm)
Mục Xuất bản Facebook có disabled: true và không thể click. Mục Xóa vĩnh viễn có class màu đỏ riêng biệt.
Thao tác đã chọn:null
Sử dụng `getItemClassName`
Hàm callback getItemClassName(option) cho phép gán class Tailwind tùy biến dựa trên thuộc tính của từng option, rất hữu ích cho các menu ngữ cảnh chứa hành động cảnh báo hoặc nguy hiểm.
<ArkComboboxPopupInput
options={actions}
getItemClassName={(opt) =>
opt.value === 'delete_permanent'
? 'text-destructive hover:bg-destructive/10'
: undefined
}
/>7. Trạng thái điều khiển (Controlled, Readonly, Disabled)
Đồng bộ hai chiều với React state và hỗ trợ các trạng thái chỉ đọc, vô hiệu hóa
Việt Nam (Hà Nội, TP.HCM) [GMT+7]
Thái Lan (Bangkok) [GMT+7]
Singapore [GMT+8]
Nhật Bản (Tokyo) [GMT+9]
Hàn Quốc (Seoul) [GMT+9]
Úc (Sydney) [GMT+10]
Vương Quốc Anh (London) [GMT+0]
Pháp (Paris) [GMT+1]
Đức (Berlin) [GMT+1]
Hoa Kỳ (New York) [GMT-5]
Hoa Kỳ (Los Angeles) [GMT-8]
State hiện thời:Asia/Tokyo
Đồng bộ 2 chiều (2-way Binding)
Khi truyền prop value, thành phần hoạt động ở chế độ Controlled hoàn toàn. Mọi thay đổi từ click, phím Enter đều kích hoạt onChange(nextValue, selectedOption) để cập nhật state đồng bộ.
8. Kích thước chuẩn ControlSize (Size Variants: xs, sm, md, lg)ControlSize Token
Hỗ trợ cả 4 kích thước chuẩn của ChillCMS, căn chỉnh thẳng hàng hoàn hảo với Input và Button
Chọn kích thước tương tác:
Chiều cao: 36pxCỡ chữ: 14px (text-sm)
Căn chỉnh thẳng hàng hoàn hảo (ControlSize: MD)
36pxKhi đặt cùng hàng với Input và Button có cùng size="md", tất cả đều có chiều cao, khoảng đệm và bo góc đồng bộ 100%.
Việt Nam (Hà Nội, TP.HCM) [GMT+7]
Thái Lan (Bangkok) [GMT+7]
Singapore [GMT+8]
Nhật Bản (Tokyo) [GMT+9]
Hàn Quốc (Seoul) [GMT+9]
Úc (Sydney) [GMT+10]
Vương Quốc Anh (London) [GMT+0]
Pháp (Paris) [GMT+1]
Đức (Berlin) [GMT+1]
Hoa Kỳ (New York) [GMT-5]
Hoa Kỳ (Los Angeles) [GMT-8]
Size xs28px
Kích thước siêu gọn, thích hợp cho toolbar, ô lọc nhỏ gọn hoặc bảng dữ liệu dày đặc.
Việt Nam (Hà Nội, TP.HCM) [GMT+7]
Thái Lan (Bangkok) [GMT+7]
Singapore [GMT+8]
Nhật Bản (Tokyo) [GMT+9]
Hàn Quốc (Seoul) [GMT+9]
Úc (Sydney) [GMT+10]
Vương Quốc Anh (London) [GMT+0]
Pháp (Paris) [GMT+1]
Đức (Berlin) [GMT+1]
Hoa Kỳ (New York) [GMT-5]
Hoa Kỳ (Los Angeles) [GMT-8]
Size sm32px
Thích hợp cho khu vực bộ lọc (filter bar), thanh công cụ bảng hoặc form thu nhỏ.
Việt Nam (Hà Nội, TP.HCM) [GMT+7]
Thái Lan (Bangkok) [GMT+7]
Singapore [GMT+8]
Nhật Bản (Tokyo) [GMT+9]
Hàn Quốc (Seoul) [GMT+9]
Úc (Sydney) [GMT+10]
Vương Quốc Anh (London) [GMT+0]
Pháp (Paris) [GMT+1]
Đức (Berlin) [GMT+1]
Hoa Kỳ (New York) [GMT-5]
Hoa Kỳ (Los Angeles) [GMT-8]
Size mdDefault36px
Kích thước chuẩn cho phần lớn form nhập liệu, modal và giao diện quản trị.
Việt Nam (Hà Nội, TP.HCM) [GMT+7]
Thái Lan (Bangkok) [GMT+7]
Singapore [GMT+8]
Nhật Bản (Tokyo) [GMT+9]
Hàn Quốc (Seoul) [GMT+9]
Úc (Sydney) [GMT+10]
Vương Quốc Anh (London) [GMT+0]
Pháp (Paris) [GMT+1]
Đức (Berlin) [GMT+1]
Hoa Kỳ (New York) [GMT-5]
Hoa Kỳ (Los Angeles) [GMT-8]
Size lg44px
Kích thước lớn nổi bật, tối ưu cho hero section, form tìm kiếm chính hoặc màn hình cảm ứng.
Việt Nam (Hà Nội, TP.HCM) [GMT+7]
Thái Lan (Bangkok) [GMT+7]
Singapore [GMT+8]
Nhật Bản (Tokyo) [GMT+9]
Hàn Quốc (Seoul) [GMT+9]
Úc (Sydney) [GMT+10]
Vương Quốc Anh (London) [GMT+0]
Pháp (Paris) [GMT+1]
Đức (Berlin) [GMT+1]
Hoa Kỳ (New York) [GMT-5]
Hoa Kỳ (Los Angeles) [GMT-8]
9. Chọn nhiều phần tử (Multiple Selection)Nâng cao
Chọn nhiều mục với thẻ chip trực quan trên nút bấm, hỗ trợ xóa nhanh và giữ popup mở khi chọn
React
TypeScript
Tailwind CSS
Next.js
React Router v7
Node.js
PostgreSQL
Prisma ORM
Docker
Redis
GraphQL
Ark UI
Số lượng đã chọn:3 / 12
ReactTypeScriptTailwind CSS
React
TypeScript
Tailwind CSS
Next.js
React Router v7
Node.js
PostgreSQL
Prisma ORM
Docker
Redis
GraphQL
Ark UI
Dùng renderTriggerLabel để thay thế danh sách chip bằng badge số lượng gọn gàng nếu trigger cần kích thước cố định.
Cơ chế hoạt động khi chọn nhiều (Multiple = true)
- Click vào mục chưa chọn sẽ thêm vào mảng; click vào mục đã chọn sẽ bỏ chọn (toggle).
- Popup vẫn mở liên tục để bạn lọc từ khóa và chọn nhiều mục kế tiếp một cách nhanh chóng mà không bị gián đoạn.
- Người dùng có thể xóa trực tiếp từng thẻ chip ngay trên trigger bằng icon × mà không cần mở popup.
<ArkComboboxPopupInput
multiple
options={skills}
value={selectedSkills}
onChange={(values, options) => setSelectedSkills(values)}
placeholder="Chọn kỹ năng..."
/>10. Bảng tra cứu thuộc tính (Props Reference)
| Thuộc tính | Kiểu dữ liệu | Mặc định | Mô tả |
|---|---|---|---|
| options | ArkComboboxPopupInputOption[] | Bắt buộc | Danh sách các tùy chọn gồm label, value, keywords, disabled. |
| value | OptionValue | OptionValue[] | null | undefined | Giá trị hiện tại ở chế độ Controlled (dạng đơn hoặc mảng khi multiple). |
| defaultValue | OptionValue | OptionValue[] | null | null / [] | Giá trị khởi tạo ban đầu ở chế độ Uncontrolled. |
| size | 'xs' | 'sm' | 'md' | 'lg' | 'md' | Kích thước chuẩn theo hệ thống token ControlSize (xs: 28px, sm: 32px, md: 36px, lg: 44px). |
| multiple | boolean | false | Bật chế độ chọn nhiều phần tử. Giá trị nhận và trả về dạng mảng. |
| closeOnSelect | boolean | !multiple | Tự động đóng popup sau khi chọn một mục (mặc định true cho đơn chọn, false cho đa chọn). |
| chipClassName | string | undefined | Class CSS tùy biến cho thẻ chip/tag hiển thị trên nút trigger khi ở chế độ multiple. |
| placeholder | string | 'Chọn...' | Văn bản gợi ý hiển thị trên nút trigger khi chưa chọn mục nào. |
| searchPlaceholder | string | 'Tìm kiếm...' | Văn bản gợi ý hiển thị bên trong ô tìm kiếm của popover. |
| renderTriggerLabel | (selectedOption) => ReactNode | undefined | Hàm tùy biến nội dung JSX hiển thị trên nút trigger khi đã chọn (nhận mảng khi multiple). |
| renderItemContent | (option) => ReactNode | undefined | Hàm tùy biến nội dung JSX cho từng mục trong danh sách gợi ý. |
| getItemClassName | (option) => string | undefined | undefined | Hàm gán class CSS động cho từng dòng tùy chọn theo dữ liệu item. |
| autoHighlight | boolean | true | Tự động highlight mục đang chọn hoặc mục đầu tiên khi mở popover. |
| disabled | boolean | false | Vô hiệu hóa toàn bộ tương tác mở popover của nút trigger. |
| readOnly | boolean | false | Khóa không cho phép chọn mục mới khi đang mở popup. |
| positioning | PopoverRootProps['positioning'] | { placement: 'bottom-start', sameWidth: true, gutter: 4 } | Tùy biến cấu hình vị trí thả xuống (placement, offset, sameWidth, fitViewport). |
| onChange | ArkComboboxPopupInputChangeHandler | undefined | Callback khi giá trị thay đổi, nhận (value, selectedOption, eventDetails). |