@serenity-is/sleekgrid
Version:
A modern Data Grid / Spreadsheet component
145 lines (115 loc) • 4.63 kB
text/typescript
import { EventEmitter, EventSubscriber, H } from "../core";
import { Grid, IPlugin } from "../grid";
export interface RowMoveManagerOptions {
cancelEditOnDrag?: boolean;
}
interface RowMoveManagerDragData {
selectedRows: number[],
selectionProxy: HTMLDivElement,
guide: HTMLDivElement,
insertBefore: number,
canMove: boolean
}
interface ArgsMoveRows {
rows: number[];
insertBefore: number;
}
export class RowMoveManager implements IPlugin {
declare private grid: Grid;
declare private options: RowMoveManagerOptions;
declare private dragging: boolean;
private handler = new EventSubscriber();
onBeforeMoveRows = new EventEmitter<ArgsMoveRows>();
onMoveRows = new EventEmitter<ArgsMoveRows>();
constructor(options?: RowMoveManagerOptions) {
this.options = Object.assign({}, RowMoveManager.defaults, options);
}
public static readonly defaults: RowMoveManagerOptions = {
cancelEditOnDrag: false
}
init(grid: Grid) {
this.grid = grid;
this.handler.subscribe(grid.onDragInit, this.handleDragInit.bind(this))
.subscribe(grid.onDragStart, this.handleDragStart.bind(this))
.subscribe(grid.onDrag, this.handleDrag.bind(this))
.subscribe(grid.onDragEnd, this.handleDragEnd.bind(this));
}
destroy() {
this.handler?.unsubscribeAll();
}
private handleDragInit(e: UIEvent) {
// prevent the grid from cancelling drag'n'drop by default
e.stopImmediatePropagation();
}
private handleDragStart(e: UIEvent, dd: RowMoveManagerDragData) {
let cell = this.grid.getCellFromEvent(e);
if (this.options.cancelEditOnDrag && this.grid.getEditorLock().isActive()) {
this.grid.getEditorLock().cancelCurrentEdit();
}
if (this.grid.getEditorLock().isActive() ||
!/move|selectAndMove/.test(this.grid.getColumns()[cell.cell].behavior)) {
return false;
}
this.dragging = true;
e.stopImmediatePropagation();
let selectedRows = this.grid.getSelectedRows();
if (selectedRows.length == 0 || selectedRows.indexOf(cell.row) == -1) {
selectedRows = [cell.row];
this.grid.setSelectedRows(selectedRows);
}
let rowHeight = this.grid.getOptions().rowHeight;
dd.selectedRows = selectedRows;
let canvas = this.grid.getCanvasNode();
dd.selectionProxy = canvas.appendChild(H('div', {
'class': 'slick-reorder-proxy',
'style': `position: absolute; z-index: 9999; width: ${canvas.clientWidth}px; height: ${rowHeight * selectedRows.length}px`
}));
dd.guide = canvas.appendChild(H('div', {
'class': 'slick-reorder-guide',
'style': `position: absolute; z-index: 99998; width: ${canvas.clientWidth}px; top: -1000`
}));
dd.insertBefore = -1;
}
private handleDrag(e: UIEvent, dd: RowMoveManagerDragData) {
if (!this.dragging)
return;
e.stopImmediatePropagation();
let canvas = this.grid.getCanvasNode();
let box = canvas.getBoundingClientRect();
let docElem = document.documentElement;
let canvasTop = box.top + window.scrollY - docElem.clientTop;
let top = (e as any).pageY - canvasTop;
dd.selectionProxy.style.top = (top - 5) + 'px';
let insertBefore = Math.max(0, Math.min(Math.round(top / this.grid.getOptions().rowHeight), this.grid.getDataLength()));
if (insertBefore !== dd.insertBefore) {
let eventData = {
rows: dd.selectedRows,
insertBefore: insertBefore
};
if (this.onBeforeMoveRows.notify(eventData) === false) {
dd.guide.style.top = "-1000";
dd.canMove = false;
} else {
dd.guide.style.top = (insertBefore * this.grid.getOptions().rowHeight) + 'px';
dd.canMove = true;
}
dd.insertBefore = insertBefore;
}
}
private handleDragEnd(e: UIEvent, dd: RowMoveManagerDragData) {
if (!this.dragging)
return;
this.dragging = false;
e.stopImmediatePropagation();
dd.guide.remove();
dd.selectionProxy.remove();
if (dd.canMove) {
let eventData = {
rows: dd.selectedRows,
insertBefore: dd.insertBefore
};
// TODO: _grid.remapCellCssClasses ?
this.onMoveRows.notify(eventData);
}
}
}