Detent web components
By Shane Logsdon

Data & tables

Bulk Action Bar

Implemented

Floating bulk-action toolbar for multi-item batch operations and export flows

detent-bulk-action-bar

Port status
Catalog status
implemented
Origin
upstream
Phase
3
Wave
phase-3-data
Group
Data & tables

4.1 KB gzip for the class module

Variants

A local demo of a three-of-twelve selection. The toolbar only exists while selectedCount > 0; it shows All (12) (or Select none when everything is selected) and a horizontally scrollable row of batch actions. Arrow keys move focus between the controls, Home/End jump to the ends. detent-select-all and detent-clear-selection carry an empty detail: the element never mutates the selection, so the consumer applies it. Set selectedCount = 0 to remove the toolbar from the tree.

No bulk intent has been emitted yet.

Recorded states

  • selected recorded state; no setter

Usage

Import

import '/components/bulk-action-bar/define.js';

// or register it yourself
import { DetentBulkActionBar } from '/components/bulk-action-bar/bulk-action-bar.js';

Attributes

  • mode
  • selected-count
  • total-count

Properties

  • actions
  • onClearSelection
  • onSelectAll

Slots

No slots are recorded for this component.

Events

  • detent-clear-selection — {}
  • detent-select-all — {}

Example

Runnable example: site/examples/bulk-action-bar.ts. The snippet below is generated from the catalogued attributes.

<detent-bulk-action-bar
  mode
  selected-count="0"
  total-count
></detent-bulk-action-bar>

Property-only values

const element = document.querySelector('detent-bulk-action-bar');
if (element !== null) {
  // actions: SelectionActionItem[]
  element.actions = [];
  // onClearSelection: () => void
  element.onClearSelection = () => {};
  // onSelectAll: () => void
  element.onSelectAll = () => {};
}

Source

The pinned upstream source for this row:

  • src/components/ui/SelectionBasket.tsx sha256 d87d4d838791…

Native files

The native module and its artifacts:

  • src/components/bulk-action-bar/bulk-action-bar.ts
  • src/components/bulk-action-bar/styles.css
  • src/components/bulk-action-bar/define.ts
  • docs/components/bulk-action-bar.md
  • site/examples/bulk-action-bar.ts
  • tests/browser/bulk-action-bar.spec.ts
  • evidence/components/bulk-action-bar.md

API

Boolean attributes follow HTML presence semantics: present means true, absent means false, and disabled="false" still means disabled.

Attributes

Attributes
AttributePropertyTypeDefaultNotes
mode———Recorded in the catalog as an attribute.
selected-countselectedCountnumber0Kebab-case attribute; camelCase property.
total-counttotalCountnumberundefinedKebab-case attribute; camelCase property.

Properties

Properties
PropertyTypeDefaultNotes
actionsSelectionActionItem[][...]Property-only: assign from script; not accepted as an attribute.
onClearSelection() => voidundefinedProperty-only: assign from script; not accepted as an attribute.
onSelectAll() => voidundefinedProperty-only: assign from script; not accepted as an attribute.

Events

Events
EventUpstream propPayload typeNotes
detent-clear-selectiononClearSelection() => voidComposed, bubbles; fires only for a user action.
detent-select-allonSelectAll() => voidComposed, bubbles; fires only for a user action.

Slots

No slots are recorded for this component.

CSS customisation

  • React callback props become typed composed CustomEvents; programmatic state assignment emits none.
  • Framer Motion springs/AnimatePresence ported to CSS transitions, WAAPI or the shared rAF spring helper.
  • React state/hooks replaced by element-owned state with pre-upgrade property preservation.
  • onClearSelection / onSelectAll become the composed events detent-clear-selection / detent-select-all with an empty detail. Per-action onClick stays a property-only function hook, because the catalog row records no action event; inventing one would be an unrecorded public API (PC-6.7).
  • The element never mutates selectedCount: clearing emits the intent and the consumer decides.
  • Arrow/Home/End keyboard navigation inside the toolbar is new; upstream only made the controls tabbable.
  • The four default actions are presentational (no onClick), so nothing happens until the consumer supplies handlers.
  • The select-all control is absent when totalCount is unknown (not 0/false), so it is removed from the tree rather than rendered disabled.
  • Upstream's dark-only palette, backdrop-blur, and lucide-react icons become theme tokens plus aria-hidden text glyphs; the Framer Motion springResponsive rise becomes a CSS transition.
  • mode is the documented attribute name and also a property that reflects (upstream was a React prop only); className is replaced by parts and custom properties.

What it does

The element is a projection of your selection: it never mutates it, never fetches anything and never performs a batch operation itself.

Upstream behaviour
  • Appears naturally with spring rise physics as soon as selected count > 0
  • Responsive horizontal scrolling action container preventing mobile cutoff
  • Integrated batch action buttons (Delete, Move, Export, Share)
  • Dynamic select all / clear all selection toggle synchronization
  • Accessible role="toolbar" keyboard navigation and focus rings
Differences from upstream
  • React callback props become typed composed CustomEvents; programmatic state assignment emits none.
  • Framer Motion springs/AnimatePresence ported to CSS transitions, WAAPI or the shared rAF spring helper.
  • React state/hooks replaced by element-owned state with pre-upgrade property preservation.
  • onClearSelection / onSelectAll become the composed events detent-clear-selection / detent-select-all with an empty detail. Per-action onClick stays a property-only function hook, because the catalog row records no action event; inventing one would be an unrecorded public API (PC-6.7).
  • The element never mutates selectedCount: clearing emits the intent and the consumer decides.
  • Arrow/Home/End keyboard navigation inside the toolbar is new; upstream only made the controls tabbable.
  • The four default actions are presentational (no onClick), so nothing happens until the consumer supplies handlers.
  • The select-all control is absent when totalCount is unknown (not 0/false), so it is removed from the tree rather than rendered disabled.
  • Upstream's dark-only palette, backdrop-blur, and lucide-react icons become theme tokens plus aria-hidden text glyphs; the Framer Motion springResponsive rise becomes a CSS transition.
  • mode is the documented attribute name and also a property that reflects (upstream was a React prop only); className is replaced by parts and custom properties.
Previous: Scroll Velocity Text Next: SettleModal

Docs

Components

Project