count
`N개 선택됨` 문구를 tabular number로 표시하며 count가 0이면 기본적으로 렌더링하지 않습니다.
actions
각 action은 key, label, onClick, optional icon, variant, disabled, title을 갖습니다.
emptyHint
모바일 선택 모드처럼 count가 0이어도 bar가 필요할 때 안내 문구를 표시합니다.
placement
테이블 선택의 유일한 일괄 액션 표면입니다. 기본 floating은 sticky bottom, mx-auto, w-fit 배치와 safe-area 하단 여백을 포함합니다. inline은 툴바 아래 상태 줄 자리에 sticky top 띠로 들어가며, 목록보다 앞에 두어 키보드 순서상 행보다 먼저 닿습니다.
unit · summary
unit으로 `12명 선택됨`처럼 단위를 바꾸고(기본 `개`), summary 자리에 '이 조건의 N명 모두 선택' 같은 제안을 둡니다. inline에서 폭이 모자라면 summary는 줄바꿈하지 않고 잘리며, compactSummary(짧은 문구나 aria-label을 단 IconButton)를 주면 그것으로 바뀝니다.
idleContent
선택 0일 때 bar 대신 보여 줄 개수 표시(예: `100 / 3,214명`)입니다. emptyHint보다 우선하며, 둘 다 없으면 숨깁니다.
trailing
선택 해제(✕) 뒤에 붙는 자리입니다(0.17.1). 편집 모드 [편집]/[완료] 토글처럼 선택과 별개인 컨트롤을 둡니다. inline 띠·floating pill은 ✕ 바로 뒤, idle 상태 줄은 오른쪽 끝이며, 상태 줄에서는 role="status" 밖이라 개수만 다시 읽힙니다.

선택 상태

count, actions, onClear
코드 보기tsx
import { BulkActionBar } from "@olundot/ui";

export function SelectedBulkActionBar() {
  return (
    <BulkActionBar
      count={3}
      onClear={() => undefined}
      actions={[
        { key: "message", label: "메시지", onClick: () => undefined },
        { key: "archive", label: "보관", onClick: () => undefined },
        { key: "remove", label: "제외", variant: "destructive", onClick: () => undefined },
      ]}
    />
  );
}

비활성 액션

disabled, title
코드 보기tsx
import { BulkActionBar } from "@olundot/ui";

export function DisabledBulkActionBar() {
  return (
    <BulkActionBar
      count={2}
      onClear={() => undefined}
      actions={[
        { key: "publish", label: "게시", disabled: true, title: "검토 완료 후 사용할 수 있습니다", onClick: () => undefined },
        { key: "delete", label: "삭제", variant: "destructive", onClick: () => undefined },
      ]}
    />
  );
}

빈 선택 모드

emptyHint
코드 보기tsx
import { BulkActionBar } from "@olundot/ui";

export function EmptyModeBulkActionBar() {
  return (
    <BulkActionBar
      count={0}
      emptyHint="선택 모드"
      onClear={() => undefined}
      actions={[{ key: "message", label: "메시지", onClick: () => undefined }]}
    />
  );
}

상단 일괄 작업 띠

placement="inline", unit, summary, compactSummary, idleContent
12명 선택됨
코드 보기tsx
import { useState } from "react";
import { ListChecks } from "lucide-react";
import { BulkActionBar, Button, IconButton } from "@olundot/ui";

export function InlineBulkActionBar() {
  const [count, setCount] = useState(12);

  return (
    <div className="flex flex-col gap-3">
      <BulkActionBar
        placement="inline"
        count={count}
        unit="명"
        idleContent="100 / 3,214명"
        summary={
          <Button variant="link" size="sm" className="h-auto p-0">
            이 조건의 3,214명 모두 선택
          </Button>
        }
        compactSummary={
          <IconButton size="sm" variant="ghost" aria-label="이 조건의 3,214명 모두 선택" icon={ListChecks} />
        }
        onClear={() => setCount(0)}
        actions={[
          { key: "grade", label: "학년·분반 변경", onClick: () => undefined },
          { key: "delete", label: "삭제", variant: "destructive", onClick: () => undefined },
        ]}
      />
      {/* 목록은 띠 아래에 둔다 — 키보드 순서상 일괄 작업이 행보다 앞선다. */}
      <Button variant="outline" size="sm" className="self-start" onClick={() => setCount((value) => (value === 0 ? 12 : 0))}>
        {count === 0 ? "12명 선택" : "선택 비우기"}
      </Button>
    </div>
  );
}

편집 모드 토글

trailing, idleContent, placement="inline"
100 / 3,214명
코드 보기tsx
import { useState } from "react";
import { BulkActionBar, Button } from "@olundot/ui";

export function EditModeBulkActionBar() {
  const [editing, setEditing] = useState(false);
  const [count, setCount] = useState(0);

  return (
    <div className="flex flex-col gap-3">
      <BulkActionBar
        placement="inline"
        count={count}
        unit="명"
        idleContent="100 / 3,214명"
        // ✕ 뒤(선택 전에는 상태 줄 오른쪽 끝). 라이브 영역 밖이라 개수만 다시 읽힌다.
        trailing={
          <Button
            variant="outline"
            size="sm"
            aria-pressed={editing}
            onClick={() => {
              setEditing((value) => !value);
              setCount(0);
            }}
          >
            {editing ? "완료" : "편집"}
          </Button>
        }
        onClear={() => setCount(0)}
        actions={[{ key: "grade", label: "학년·분반 변경", onClick: () => undefined }]}
      />
      <Button
        variant="outline"
        size="sm"
        className="self-start"
        disabled={!editing}
        onClick={() => setCount((value) => (value === 0 ? 12 : 0))}
      >
        {count === 0 ? "12명 선택" : "선택 비우기"}
      </Button>
    </div>
  );
}