React로 만드는 스크롤되는 필터 칩 바

검색 바 아래에 있는 칩 행 — YouTube 주제, 스토어 필터, 태그 선택기 — 은 토글 버튼으로 가득한 한 줄짜리 스크롤 컨테이너입니다. 어려운 10%는 가장자리에서 일어나는 일들입니다: 화면 밖에 새로 나타나는 칩, 아무것도 토글해서는 안 되는 드래그, 그리고 스스로 무의미해질 때를 아는 화살표.

필터를 추가해보세요 — 새 칩이 보이도록 행이 스스로 스크롤됩니다.

엣지 케이스가 곧 기능입니다

overflow-x: auto가 걸린 flex 행은 무엇이든 스크롤됩니다. 칩 바의 진가는 디테일에서 드러납니다:

상태는 여러분의 손안에 있습니다

라이브러리는 스크롤을 담당할 뿐 선택 상태를 소유하지 않습니다. 칩은 여러분의 버튼입니다 — 다중 선택 토글에는 aria-pressed, 단일 선택에는 일반 상태를 사용하세요 — 메뉴가 필요로 하는 것은 각 칩이 itemId를 갖는다는 것뿐입니다. 즉 칩 상태는 이미 가지고 있는 무엇과도 조합될 수 있습니다: URL 검색 매개변수, 폼 라이브러리, 서버 기반 필터 모델까지. 칩을 삭제하는 것은 항목 제거이고, 사라지는 애니메이션을 주는 것은 items-animation 예제입니다.

모바일: body 스크롤에 관한 한 가지 경고

터치 스크린에서는 바 안에서의 가로 스와이프가 일부 브라우저에서 페이지 전체를 함께 옆으로 끌고 갈 수 있습니다. 이런 현상이 보인다면 prevent-body-scroll 예제에서 이를 막는 touch-action과 오버스크롤 컨테인먼트를 확인하세요 — CSS만으로 해결되며 제스처 라이브러리는 필요 없습니다.

패턴, 최소 구성

칩은 itemId를 가진 토글 버튼이며, 메뉴 API에 대한 ref가 새로 추가된 칩을 화면에 보이도록 스크롤합니다.

ChipBar.tsx
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>
  );
}

또는 shadcn 컴포넌트로 설치하기

chip-bar 레지스트리 아이템은 이를 제어 컴포넌트로 제공합니다 — options, selected, onSelectedChangecomponents/ui/에 Tailwind로 스타일링되어 설치됩니다:

shadcn
npx shadcn@latest add https://react-horizontal-scrolling-menu.dev/r/chip-bar.json

관련 예제

예제 전체 21개