WebberUI

Lasso Select Stage

Drag a lasso box across a grid; the items it catches lift and light up in sequence, and on release the count badge morphs into a floating batch action bar.

Loading preview…
npx shadcn@latest add https://webberui.com/r/lasso-select-stage.json

Playground

Tune the props live — the code snippet updates as you go, so you can dial in the look you want before copying it.

4
12
<LassoSelectStage />

Installation

npx shadcn@latest add https://webberui.com/r/lasso-select-stage.json

Or, once registries are configured in components.json, install it as @webberui/lasso-select-stage.

Usage

import {
  LassoSelectStage,
  LassoItem,
} from "@/components/ui/lasso-select-stage";

export function Example() {
  return (
    <LassoSelectStage
      columns={4}
      actions={[
        { id: "share", label: "Share" },
        { id: "delete", label: "Delete", destructive: true },
      ]}
      onSelectionChange={(ids) => console.log(ids)}
    >
      {items.map((item) => (
        <LassoItem key={item.id} id={item.id} label={item.name}>
          {/* any card content */}
        </LassoItem>
      ))}
    </LassoSelectStage>
  );
}

Press and drag anywhere on the stage to pull out a selection box; the LassoItems it catches lift and light up one after another. On release, the selection count badge morphs into a floating batch action bar; pressing the ✕ on that bar cancels, and the bar shatters into small dots that scatter back to each item. A single item can also be toggled by clicking it or with the keyboard (Enter / Space).

LassoSelectStage Props

PropTypeDefaultDescription
childrenReactNodePut LassoItem children here
columnsnumber4Number of grid columns
gapnumber12Gap between cells (px)
actionsLassoAction[][]Actions on the floating batch bar
selectedIdsstring[]Controlled array of selected ids
defaultSelectedIdsstring[][]Initial selected ids in uncontrolled mode
onSelectionChange(ids: string[]) => voidFires when the selection changes
aria-labelstring"可框選項目舞台"Accessible label for the stage

LassoItem Props

PropTypeDefaultDescription
idstringUnique key, used to pair selection and the scatter-back animation
labelstringidAccessible label
childrenReactNodeCard content

LassoAction

FieldTypeDescription
idstringUnique identifier for the action
labelstringButton text
iconReactNodeOptional icon, placed before the text
onSelect(ids: string[]) => voidFires on click with the current selection; the selection clears automatically afterwards
destructivebooleanWhether this is a destructive action (red accent)

How it works

  • Controlled and uncontrolled modes: passing selectedIds hands control to the outside; otherwise the component starts from defaultSelectedIds and maintains its own state.
  • Lasso detection: a drag only counts as a lasso once it travels more than 4px; a press without movement toggles a single item, and pressing on empty space clears the selection.
  • Sequential lift: the stagger delay is derived from each item's order in the grid, so the highlight rings light up one by one as the lasso sweeps.
  • Morphing aggregation: the in-drag count badge and the post-release action bar share a layoutId, so they morph smoothly into one another rather than cross-fading.
  • Scatter-back animation: on cancel, the action bar shatters into small dots that fly back along a trajectory to each item's original position.

Accessibility

  • The stage is role="listbox" aria-multiselectable, and items are role="option" with aria-selected.
  • Items are keyboard-focusable; Enter / Space toggles selection and Esc clears the whole selection.
  • The floating action bar is role="toolbar", and the selection count is announced through an aria-live region.
  • When the user has "reduce motion" enabled at the system level, the lift, morphing aggregation, and scatter-back animations are all disabled; only the instant state change remains.

On this page