DropIndicator
A thin bar rendered in the gap between two items of a reorderable list, marking where a dragged item will land.
- 1. Lint and typecheck
Runs on CI
- 2. Unit and integration tests
Runs on CI
- 3. Run pending migrations
Runs on Platform
- 4. Deploy to production
Runs on Platform
View code
let session = new DragSession();
let steps = [...STEPS];
// ref() fires on insert, so the subscription is set up in the browser and torn
// down with the node — the server renders the list without one.
let followDrag = ref((_node, signal) => {
session.addEventListener("change", () => void handle.update(), { signal });
});
function isGapActive(stepId: string, side: "before" | "after") {
return session.target?.key === stepId && session.target.position === side;
}
<div mix={[followDrag]}>
<Header>Release checklist</Header>
<Badge color="neutral">{session.active ? "Dragging" : "Drag a grip"}</Badge>
<ol
mix={[
// A pointer drag over text would select it instead, so the list opts out.
userSelect("none"),
dragReorder(session),
on<HTMLOListElement, "ui:reorder">("ui:reorder", (event) => {
steps = reorder(steps, event.sourceKey, event.targetKey, event.position, (s) => s.id);
void handle.update();
}),
]}
>
{steps.map((step, index) => (
<li key={step.id} data-rmx-key={step.id}>
{/* One bar per gap: the first row opens the list, and every row closes its own gap. */}
{index === 0 ? <DropIndicator isDropTarget={isGapActive(step.id, "before")} /> : null}
<Item>
<Item.Media>
<button
type="button"
data-drag-handle
aria-label={`Reorder ${step.name}`}
mix={[cursor("grab"), touchAction("none")]}
>
<GripVerticalIcon />
</button>
</Item.Media>
<Item.Content>
<Item.Title>
{index + 1}. {step.name}
</Item.Title>
<Item.Description>Runs on {step.owner}</Item.Description>
</Item.Content>
</Item>
<DropIndicator isDropTarget={isGapActive(step.id, "after")} />
</li>
))}
</ol>
</div>Installation
An ordinary dependency: install it, import the theme once, and import the component where it is used.
npm add @sdxc/uiimport "@sdxc/ui/theme.css";Usage
import { DropIndicator } from "@sdxc/ui";Renders a full-width, hairline-thick, fully rounded bar shaped for the gap
between two items in a reorderable list. The host's data-drop-target
attribute is what paints it: transparent without it, the primary solid color
with it.
Examples
<DropIndicator
isDropTarget={isCurrentTarget}
mix={css({ position: "absolute", insetInline: "0", insetBlockStart: "-1px" })}
/>Props
Read from the component's own types, so every prop, every default and every allowed value is listed.
| Prop | Type | Description |
|---|---|---|
isDropTarget? | boolean | Whether this bar marks the pointer's current drop target. Renders the host's data-drop-target attribute when true, which is what draws the bar. Defaults to DEFAULT_IS_DROP_TARGET. |
Also accepts everything in TagProps<"div">.