Một thanh chip lọc có thể cuộn, trong React
Dải chip bên dưới mọi thanh tìm kiếm — chủ đề YouTube, bộ lọc cửa hàng, bộ chọn tag — là một vùng chứa cuộn một dòng đầy các nút bật/tắt. 10% khó là những gì xảy ra ở các cạnh: chip mới xuất hiện ngoài màn hình, thao tác kéo không được bật/tắt bất cứ thứ gì, và mũi tên biết khi nào chúng vô nghĩa.
Thêm một bộ lọc — dải sẽ tự cuộn chip mới vào tầm nhìn.
Chỉnh sửa ví dụ này trực tiếp trong Storybook
Trường hợp biên chính là tính năng
Bất kỳ dải flex nào với overflow-x: auto đều cuộn được. Một thanh chip chứng minh giá trị của nó ở các chi tiết:
- Một chip được thêm ngoài màn hình phải tự thông báo. Demo cuộn đến mỗi chip mới bằng
apiRef.current.scrollToItem(el, 'smooth', 'end')sau khi render — ví dụ thêm item và cuộn đến nó chính là cách nối này. - Kéo để cuộn, click để bật/tắt — không bao giờ cả hai. Người dùng desktop kéo dải như một bề mặt cảm ứng; thả trên một chip không được lật trạng thái nó. Công thức kéo theo dõi cử chỉ và ngăn chính cú click đó.
- Mũi tên chỉ khi hữu ích.
useLeftArrowVisible/useRightArrowVisibleđược nối vào cùng một IntersectionObserver như mọi thứ khác, vì vậy mũi tên bị vô hiệu hóa ở các cạnh thật — kể cả sau khi chip được thêm hoặc xóa.
Trạng thái vẫn nằm trong tay bạn
Thư viện chỉ cuộn; nó không sở hữu lựa chọn. Chip là các nút của bạn — aria-pressed cho các nút bật/tắt đa lựa chọn, state thuần cho đơn lựa chọn — và menu chỉ cần mỗi chip mang một itemId. Điều đó nghĩa là trạng thái chip kết hợp được với bất cứ thứ gì bạn đã có: URL search params, một thư viện form, một mô hình lọc do server điều khiển. Xóa một chip là xóa một item; tạo hiệu ứng biến mất là ví dụ hoạt ảnh item.
Di động: một cảnh báo về cuộn body
Trên màn hình cảm ứng, một thao tác vuốt ngang bên trong thanh có thể kéo cả trang theo hướng ngang trên một số trình duyệt. Nếu bạn gặp điều đó, ví dụ ngăn cuộn body trình bày touch-action và overscroll containment để khóa nó lại — chỉ dùng CSS, không cần thư viện cử chỉ.
Mẫu hình, tối giản
Chip là các nút bật/tắt với itemId; một ref đến menu API sẽ cuộn chip vừa thêm vào tầm nhìn.
function ChipBar({ options }: { options: string[] }) {
const apiRef = React.useRef<publicApiType>(null);
const [selected, setSelected] = React.useState<string[]>([]);
const toggle = (id: string) =>
setSelected((cur) =>
cur.includes(id) ? cur.filter((c) => c !== id) : [...cur, id],
);
// A chip appended off-screen scrolls itself into view.
const addChip = (id: string) => {
toggle(id);
requestAnimationFrame(() => {
const el = apiRef.current?.getItemElementById(id);
if (el) apiRef.current?.scrollToItem(el, 'smooth', 'end');
});
};
return (
<ScrollMenu apiRef={apiRef}>
{options.map((id) => (
<Chip itemId={id} key={id} pressed={selected.includes(id)}
onToggle={() => toggle(id)} />
))}
</ScrollMenu>
);
}Hoặc cài đặt nó như một component shadcn
Registry item chip-bar cung cấp cái này như một controlled component — options, selected, onSelectedChange — được style bằng Tailwind trong components/ui/ của bạn:
npx shadcn@latest add https://react-horizontal-scrolling-menu.dev/r/chip-bar.json