Detent web components
By Shane Logsdon

Data & tables

Batch Gesture Tray

Implemented

Spring floating tray with multi-select batch actions

detent-batch-gesture-tray

Port status
Catalog status
implemented
Origin
upstream
Phase
4
Wave
phase-4-motion-controls
Group
Data & tables

6.7 KB gzip for the class module · inlines the shared spring

Variants

A local demo of four sample files. Select any row (or use Select all) and the tray rises with the bulk actions. Every row is a native checkbox, so Space toggles it and Enter works too; Esc, the close button and a tap outside the tray all clear the selection. Export is synchronous, Archive takes 600 ms (the button shows a spinner and every action is disabled while it runs) and Delete is a danger-toned demo. Nothing is uploaded, archived or deleted: the element only reports detent-selection-change with { ids } and detent-action-complete with { actionId }.

No selection yet.

Recorded states

  • loading recorded state; no setter
  • closed recorded state; no setter
  • selected recorded state; no setter

Usage

Import

import '/components/batch-gesture-tray/define.js';

// or register it yourself
import { DetentBatchGestureTray } from '/components/batch-gesture-tray/batch-gesture-tray.js';

Attributes

No attributes are recorded for this component.

Properties

  • actions
  • items
  • onActionComplete
  • onSelectionChange
  • selectedIds

Slots

No slots are recorded for this component.

Events

  • detent-selection-change — {}
  • detent-action-complete — {}

Example

Runnable example: site/examples/batch-gesture-tray.ts. The snippet below is generated from the catalogued attributes.

<detent-batch-gesture-tray></detent-batch-gesture-tray>

Property-only values

const element = document.querySelector('detent-batch-gesture-tray');
if (element !== null) {
  // items: BatchItem[]
  element.items = [];
  // actions: BatchAction[]
  element.actions = [];
  // selectedIds: string[]
  element.selectedIds = [];
  // onSelectionChange: (ids: string[]) => void
  element.onSelectionChange = () => {};
  // onActionComplete: (actionId: string) => void
  element.onActionComplete = () => {};
}

Source

The pinned upstream source for this row:

  • src/components/ui/BatchGestureTray.tsx sha256 0835b6e3a337…

Native files

The native module and its artifacts:

  • src/components/batch-gesture-tray/batch-gesture-tray.ts
  • src/components/batch-gesture-tray/styles.css
  • src/components/batch-gesture-tray/define.ts
  • docs/components/batch-gesture-tray.md
  • site/examples/batch-gesture-tray.ts
  • tests/browser/batch-gesture-tray.spec.ts
  • evidence/components/batch-gesture-tray.md

API

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

Attributes

No attributes are recorded for this component.

Properties

Properties
PropertyTypeDefaultNotes
actionsBatchAction[]—Property-only: assign from script; not accepted as an attribute.
itemsBatchItem[]—Property-only: assign from script; not accepted as an attribute.
onActionComplete(actionId: string) => voidundefinedProperty-only: assign from script; not accepted as an attribute.
onSelectionChange(ids: string[]) => voidundefinedProperty-only: assign from script; not accepted as an attribute.
selectedIdsstring[]undefinedProperty-only: assign from script; not accepted as an attribute.

Events

Events
EventUpstream propPayload typeNotes
detent-selection-changeonSelectionChange(ids: string[]) => voidComposed, bubbles; fires only for a user action.
detent-action-completeonActionComplete(actionId: string) => 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.
  • `lucide-react` icon components replaced with local SVG assets (ISC notice retained).
  • React state/hooks replaced by element-owned state with pre-upgrade property preservation.
  • items[].icon and actions[].icon are short glyph strings rendered aria-hidden; the row records no slots, so ReactNode icons have no native form.
  • Items are native input[type=checkbox] inside a label (upstream used role=checkbox divs with custom key handling); Enter is kept as an extra toggle key.
  • The aria-live region moved from the toolbar to a visually hidden role=status count.
  • Outside-tap dismissal uses a document pointerdown listener although the pinned source has no outside handler; the row feature list is the authority.
  • Escape and outside-tap listeners are per instance, so one outside tap clears every open tray.

What it does

A list of selectable items plus a floating tray that rises as soon as one item is selected and offers bulk actions for that selection.

Upstream behaviour
  • Spring slide-up bottom tray appearing automatically when 1 or more items are selected
  • Integrated selection counter badge with Select-All / Deselect-All triggers
  • Async action loading indicators preventing double-submission
  • Dismissal via close button, escape key, or outside tap
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.
  • `lucide-react` icon components replaced with local SVG assets (ISC notice retained).
  • React state/hooks replaced by element-owned state with pre-upgrade property preservation.
  • items[].icon and actions[].icon are short glyph strings rendered aria-hidden; the row records no slots, so ReactNode icons have no native form.
  • Items are native input[type=checkbox] inside a label (upstream used role=checkbox divs with custom key handling); Enter is kept as an extra toggle key.
  • The aria-live region moved from the toolbar to a visually hidden role=status count.
  • Outside-tap dismissal uses a document pointerdown listener although the pinned source has no outside handler; the row feature list is the authority.
  • Escape and outside-tap listeners are per instance, so one outside tap clears every open tray.
Previous: Bar Chart Next: Book Call Button

Docs

Components

Project