UNPKG

@3mo/data-grid

Version:
174 lines (173 loc) 6.54 kB
import { AsyncDirective, Controller, directive, noChange, PartType } from '@a11d/lit'; class DragGhostImage extends Image { constructor() { super(...arguments); this.src = 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMSIgaGVpZ2h0PSIxIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjwvc3ZnPg=='; } static { this.instance = new DragGhostImage(); } } export var ReorderabilityState; (function (ReorderabilityState) { ReorderabilityState["Idle"] = "idle"; ReorderabilityState["Dragging"] = "dragging"; ReorderabilityState["DropBefore"] = "drop-before"; ReorderabilityState["DropAfter"] = "drop-after"; })(ReorderabilityState || (ReorderabilityState = {})); export class ReorderabilityController extends Controller { constructor(host, options) { super(host); this.host = host; this.options = options; this.items = new Map(); } get item() { const controller = this; return directive(class extends AsyncDirective { constructor(partInfo) { super(partInfo); if (partInfo.type !== PartType.ELEMENT) { throw new Error('This directive can only be used on an element'); } } render(options) { options; return noChange; } update(part, [options]) { this.part = part; this.options = options; const element = part.element; controller.items.set(element, options); element.draggable = !options.disabled; element.dataset.reorderability = this.state; this.addEventListeners(); return noChange; } get state() { const draggingItem = controller.draggingItem; switch (true) { case draggingItem === undefined: return ReorderabilityState.Idle; case draggingItem.source === this.options.index: return ReorderabilityState.Dragging; case draggingItem.destination !== this.options.index: return ReorderabilityState.Idle; case draggingItem.source > this.options.index: return ReorderabilityState.DropBefore; default: return ReorderabilityState.DropAfter; } } disconnected() { controller.items.delete(this.part.element); this.removeEventListeners(); } reconnected() { this.addEventListeners(); } addEventListeners() { if (!this.part) return; const element = this.part.element; element.addEventListener('dragstart', controller, { capture: true }); element.addEventListener('dragover', controller, { capture: true }); element.addEventListener('drop', controller, { capture: true }); element.addEventListener('dragend', controller, { capture: true }); } removeEventListeners() { if (!this.part) return; const element = this.part.element; element.removeEventListener('dragstart', controller); element.removeEventListener('dragover', controller); element.removeEventListener('drop', controller); element.removeEventListener('dragend', controller); } }); } handleEvent(e) { if (this.items.get(e.currentTarget)?.disabled) { return; } switch (e.type) { case 'dragstart': return this.handleDragStart(e); case 'dragover': return this.handleDropOver(e); case 'drop': return this.handleDrop(e); case 'dragend': return this.handleDragEnd(e); } } handleDragStart(e) { const index = this.items.get(e.currentTarget).index; this.draggingItem = { source: index, destination: index }; e.dataTransfer?.setDragImage(DragGhostImage.instance, 0, 0); e.dataTransfer.effectAllowed = 'move'; this.host.requestUpdate(); } handleDropOver(e) { if (this.draggingItem) { e.preventDefault(); e.dataTransfer.dropEffect = 'move'; this.draggingItem.destination = this.items.get(e.currentTarget).index; this.host.requestUpdate(); } } handleDrop(e) { e.preventDefault(); if (!this.draggingItem) { return; } this.handleReorder(this.draggingItem.source, this.draggingItem.destination); this.draggingItem = undefined; this.host.requestUpdate(); } handleDragEnd(e) { e; this.draggingItem = undefined; this.host.requestUpdate(); } handleReorder(source, destination) { this.options.handleReorder?.(source, destination); } } export class DataGridReorderabilityController extends ReorderabilityController { constructor(host) { super(host, {}); this.host = host; } get enabled() { return this.host.reorderability && this.host.sortingController.enabled === false && this.host.detailsController.hasDetails === false; } reorder(source, destination) { this.handleReorder(source, destination); } handleReorder(source, destination) { if (source === destination) { return; } const d = [...this.host.data]; const [movedItem] = d.splice(source, 1); d.splice(destination, 0, movedItem); this.host.data = d; const isMovingDown = source < destination; this.host.reorder.dispatch([ { record: this.host.dataRecords[destination], oldIndex: source, type: 'move', }, ...Array.from({ length: Math.abs(destination - source) }) .map((_, i) => isMovingDown ? source + i : destination + i + 1) .map(i => ({ record: this.host.dataRecords[i], oldIndex: isMovingDown ? i + 1 : i - 1, type: 'shift', })) ]); } }