LyteNyte Grid logo for light mode. Links back to the documentation home page.
Rows

Row Dragging

Drag and drop rows within the same LyteNyte Grid, across multiple grids, or into external drop zones and applications.

Single Row Dragging

LyteNyte Grid provides the api.useRowDrag hook to enable row dragging. This hook returns drag props and handlers you can apply to a drag handle component. The handle can be any component that supports standard HTML drag-and-drop attributes, such as draggable and onDragStart.

The demo below demonstrates the basic row dragging setup. Users can drag rows to reorder them within the same grid. The demo uses LyteNyte Grid’s onRowDragEnter, onRowDragLeave, and onRowDrop properties to attach callbacks for drag events.

Single Row Dragging

Fork code on stack blitzFork code on code sandbox

The api.useRowDrag hook requires a rowIndex. LyteNyte Grid creates the necessary drag data when the drag operation begins. The code for the drag handle is shown below.

function MarkerCell({ api, rowIndex }: Grid.T.CellRendererParams<GridSpec>) {
const { props } = api.useRowDrag({ rowIndex });
return (
<div {...props}>
<DragHandleDots2Icon />
</div>
);
}

The onRowDragEnter, onRowDragLeave, and onRowDrop callbacks receive parameters describing the drag operation. The parameter object has two properties:

  • source: Data related to the row being dragged.
  • over: Data related to the row or viewport being dragged over.

Using these properties, you can perform actions when events occur, such as adding and removing drag indicators, or reordering rows.

Multi-Row Dragging

Combine row dragging with row selection to support dragging multiple rows. The drag event parameters include a source property that provides the grid API for the row being dragged. Use this API to retrieve the selected rows and move them together with the current row. This is shown in the demo below.

Multiple Row Dragging

Fork code on stack blitzFork code on code sandbox

In the demo code, p.source.api retrieves the selected rows, as shown below. The example ignores drags over the viewport, since the demo uses dragging to reorder rows.

setData((prev) => {
if (p.over.kind === "viewport") return prev;
const selected = p.source.api
.rowsSelected()
.rows.filter((x) => x.id !== p.source.row.id)
.map((x) => p.source.api.rowIdToRowIndex(x.id))
.filter((b) => b != null)
.sort((l, r) => l - r);
const next = moveRelative(prev, p.source.rowIndex, p.over.rowIndex, selected);
return next;
});

The moveRelative function is a utility provided by LyteNyte Grid. It moves items from one position to another. It performs a smart swap of rows using the indices provided.

Dragging Between Grids

You can drag rows between grids using the rowDropAccept property. A grid can always drag rows within itself. To allow dragging between different grids, assign each grid an ID using the gridId property. Then share those IDs with the grids that should accept drops using the rowDropAccept property.

In the demo below, the first grid is the primary grid and the second grid is the secondary grid. The grid IDs have been shared with each grid so rows can be dragged between them.

Grid-to-Grid Dragging

Fork code on stack blitzFork code on code sandbox

You can identify whether the drag operation comes from the same grid or another grid using the id property on the source and over parameters, as shown below.

onRowDrop={(p) => {
if (p.over.id === p.source.id) {
// Same grid
} else {
// Different grids
}
}}

Both the source and over properties include a reference to the api of their respective grids. This is useful when you need to retrieve information or perform actions across different grids.

Note

LyteNyte Grid cannot fully type the api property on the source and over parameters, because drag operations can come from any grid. Use a type assertion when you need access to API methods specific to your grid.

const api = source.api as Grid.API<MySpec>;

External Drop Zones

LyteNyte Grid’s row dragging leverages the native browser drag and drop API. This means you can implement external drop zones by handling onDrop on an element. The demo below demonstrates this in action.

External Drop Zones

Drag rows from the grid here.
Fork code on stack blitzFork code on code sandbox

The getRowDragData function is a helper provided by LyteNyte Grid. This function returns the current row drag data while a row drag is active. Use this function to retrieve the drag data for your own components. Do not call this function unless a row drag operation is in progress.

Row Drag Placeholders

The api.useRowDrag hook accepts an optional placeholder property. Use this property to provide a custom placeholder for drag operations. The placeholder can be one of the following:

  • String: A CSS selector targeting the HTML element to serve as the placeholder. The browser captures a snapshot of this element to generate the placeholder image.
  • Query Object: An object containing a query string and an optional offset tuple. This behaves like a string value but allows you to shift the placeholder image using the provided tuple.
  • Component Placeholder: A custom React component that renders the placeholder. This option disables browser drag-image snapshots. Use this option only when drag operations stay within the same page.

The demo below demonstrates a React placeholder in action. Notice that the placeholder component uses a portal to render into the body element.

Drag Placeholder

Fork code on stack blitzFork code on code sandbox

When you use a React placeholder, render the placeholder value returned by api.useRowDrag, as shown below.

function MarkerCell({ api, rowIndex }: Grid.T.CellRendererParams<GridSpec>) {
const { props, placeholder } = api.useRowDrag({ rowIndex, placeholder: Placeholder });
return (
<div className="flex h-full w-full cursor-grab items-center justify-center" {...props}>
<DragHandleDots2Icon />
{placeholder}
</div>
);
}

Drag To External Applications

Since LyteNyte Grid utilizes the browser’s native drag and drop API, you can drag rows into external applications. To enable this, set the data property in the api.useRowDrag hook parameters, as shown below:

function MarkerCell({ api, rowIndex, row }: Grid.T.CellRendererParams<GridSpec>) {
const { props } = api.useRowDrag({
rowIndex,
data: { "row-content": { kind: "dt", data: JSON.stringify(row.data), type: "text/plain" } },
});
return (
<div className="flex h-full w-full cursor-grab items-center justify-center" {...props}>
<DragHandleDots2Icon />
</div>
);
}

The data property is a record of drag data. Each entry must be one of the following types, shown below. The dt kind uses the native browser DataTransfer object. Use this kind when you want to support dragging data into external applications.

export type DragItemTransfer = {
readonly kind: "dt";
readonly type:
| "text/plain"
| "text/html"
| "text/uri-list"
| "application/json"
| "text/csv"
| "text/rtf"
| ({} & string);
readonly data: string;
};
export type DragItemSiteLocal = {
readonly kind: "site";
readonly data: unknown;
};

In the demo below, users can drag rows into an external application that accepts the text/plain MIME type. This includes most code editors. To try it out, open your text editor and drag a row into it.

Drag To External Applications

Fork code on stack blitzFork code on code sandbox

Next Steps