ui · Primitive
BulkActionBar
@devalok/shilp-sutra/ui/bulk-action-barView in Storybook Hand-curated previews ship in rolling waves. See it live in Storybook →
Reference
Also re-exported from @devalok/shilp-sutra/composed/bulk-action-bar, which is
where it used to live. Both paths work. The implementation sits in ui/ because
DataTable uses it and ui/ may not import composed/.
Props
show: boolean (controls visibility)
count: number (number of selected items — displayed in badge)
onClearSelection: () => void
actions: BulkActionBarAction[]
totalCount: number (optional — total selectable items; enables "Select all")
onSelectAll: () => void (optional — called by the "Select all" control)
placement: "bottom" | "top" | "inline" (default: "bottom")
className: string
...div attributes (forwardRef to the toolbar div; HTMLAttributes spread)
BulkActionBarAction
label: string
icon: IconInput (optional — any icon component or element)
onClick: () => void
color: "accent" | "error" | "success" | "warning" | "info" | "neutral" (default: "accent")
disabled: boolean (optional)
loading: boolean (optional — pending spinner on the action)
requiresConfirmation: boolean (optional — inline confirm before executing)
confirmMessage: string (optional — default "Are you sure?")
Defaults
placement: "bottom"
Example
<BulkActionBar
show={selected.length > 0}
count={selected.length}
onClearSelection={() => setSelected([])}
actions={[
{ label: 'Archive', icon: IconArchive, onClick: archiveSelected },
{
label: 'Delete',
icon: IconTrash,
onClick: deleteSelected,
color: 'error',
requiresConfirmation: true,
},
]}
/>Inside a Dialog or Sheet, use placement="inline" so the bar stays within the
overlay:
<BulkActionBar placement="inline" show={n > 0} count={n} … />Composability
- Standalone floating toolbar — use with DataTable, TreeView, or any selection-capable UI.
- DataTable auto-integration: DataTable's
bulkActionsprop renders this internally, so you rarely render it directly alongside a DataTable.bulkActionsPositionmaps toplacement. - Data-driven actions:
{ label, icon, onClick, color, disabled, loading, requiresConfirmation }[]. Destructive confirmation is built in — setrequiresConfirmationrather than reaching for ConfirmDialog. - Placement decides portalling.
bottom/topportal todocument.bodyand pin to the viewport.inlinerenders in flow, in place.
Gotchas
placement="bottom"/"top"render viacreatePortalintodocument.body, so they do not appear during SSR — they mount client-side only.placement="inline"has no portal and renders server-side normally.- A portal escapes its ancestors' stacking context. Inside a Dialog or Sheet, a portalled bar floats over the page instead of belonging to the overlay — use
placement="inline"there. This is why placement and portalling are one prop rather than two. - Pinned placements use
z-sticky; ensure no other fixed element conflicts. - Follows the ARIA toolbar model: one tab stop, roving focus on Arrow / Home / End (mirrored under
dir="rtl"), Escape to clear or to cancel a pending confirmation. If you wrap the actions in anything that also handles arrow keys, the two will fight. - Uses Framer Motion AnimatePresence; respects
prefers-reduced-motion.