diff --git a/column-mover.js b/column-mover.js new file mode 100644 index 0000000..8acdefe --- /dev/null +++ b/column-mover.js @@ -0,0 +1,120 @@ +import { TpTable } from "./tp-table"; + +export default class ColumnMover { + constructor(wrap, handleSelector) { + this._handleSelector = handleSelector; + this._mouseDown = this._mouseDown.bind(this); + this._dragging = this._dragging.bind(this); + this._draggingEnd = this._draggingEnd.bind(this); + this._trackingStarted = false; + + // Amount of pixels the handle must be moved before its registered as a tracking gesture. + this.threshold = 5; + + wrap.addEventListener('mousedown', this._mouseDown, true); + } + + _mouseDown(e) { + const handle = e.composedPath().find(node => node.matches && node.matches(this._handleSelector)); + if (!handle) return; + + e.stopPropagation(); + e.preventDefault(); + this._cHandle = handle; + this._startX = e.pageX; + this._watchDrag(); + } + + _watchDrag() { + window.addEventListener('mousemove', this._dragging); + window.addEventListener('mouseup', this._draggingEnd); + } + + _stopWatchDrag() { + window.removeEventListener('mousemove', this._dragging); + window.removeEventListener('mouseup', this._draggingEnd); + } + + _dragging(e) { + if (this._trackingStarted === false && Math.abs(e.pageX - this._startX) >= this.threshold) { + this._trackingStarted = true; + this._showClone(); + this._clearSelection(); + } + + if (this._trackingStarted === true) { + this._positionClone(e.pageX, e.pageY); + this._cHandle.dispatchEvent(new CustomEvent('track-move', { detail: { target: this._cHandle, state: 'track', x: e.pageX, y: e.pageX }, bubbles: true, composed: true })); + } + } + + _draggingEnd(e) { + this._trackingStarted = false; + this._cHandle.dispatchEvent(new CustomEvent('track-move', { detail: { target: this._cHandle, state: 'end', x: e.pageX, y: e.pageX }, bubbles: true, composed: true })); + this._stopWatchDrag(); + this._cHandle = null; + this._startX = 0; + this._clearSelection(); + this._clone.remove(); + this._clone = null; + if (this._indicator) { + this._indicator.remove(); + this._indicator = null; + } + } + + _clearSelection() { + const sel = window.getSelection ? window.getSelection() : document.selection; + if (sel) { + if (sel.removeAllRanges) { + sel.removeAllRanges(); + } else if (sel.empty) { + sel.empty(); + } + } + } + + /** + * Show indicator to visualize where a column would be dropped after releasing the mouse. + * @param {number} x X-coordinate + * @param {number} y Y-coordinate + */ + showIndicator(x, y) { + if (!this._indicator) { + const icon = document.createElement('tp-icon'); + icon.icon = TpTable.downIcon; + icon.style.position = 'fixed'; + icon.style.left = '0px'; + icon.style.right = '0px'; + icon.style.zIndex = '100000'; + document.body.appendChild(icon); + this._indicator = icon; + } + + const rect = this._indicator.getBoundingClientRect(); + this._indicator.style.transform = `translate(${x - ((rect.right - rect.left) / 2)}px, ${y - rect.height}px)`; + } + + _showClone() { + const div = document.createElement('div'); + div.style.background = 'rgba(255, 255, 255, 0.6)'; + div.style.border = 'solid 1px rgba(255, 255, 255, 0.8)'; + div.style.padding = '5px 10px'; + div.style.borderRadius = '2px'; + div.style.position = 'fixed'; + div.style.zIndex = '99999'; + div.style.left = '0px'; + div.style.top = '0px'; + div.style.display = 'flex'; + div.style.alignItems = 'center'; + div.style.justifyContent = 'center'; + div.innerHTML = this._cHandle.column.label; + + this._clone = div; + document.body.appendChild(div); + } + + _positionClone(x, y) { + this._clone.style.transform = `translate(${x + 10}px, ${y}px)`; + } +} diff --git a/column-resizer.js b/column-resizer.js index 05eb9e6..7190c53 100644 --- a/column-resizer.js +++ b/column-resizer.js @@ -1,4 +1,4 @@ -export default class ColumResizer { +export default class ColumnResizer { constructor(wrap, handleSelector) { this._handleSelector = handleSelector; this._mouseDown = this._mouseDown.bind(this); diff --git a/tp-table.js b/tp-table.js index 5eb1c78..6704ff9 100644 --- a/tp-table.js +++ b/tp-table.js @@ -12,7 +12,8 @@ import './tp-table-item.js'; import { DomQuery } from '@tp/helpers/dom-query.js'; import { closest } from '@tp/helpers/closest.js'; import { LitElement, html, css, svg } from 'lit'; -import ColumResizer from './column-resizer.js'; +import ColumnResizer from './column-resizer.js'; +import ColumnMover from './column-mover.js'; export class TpTable extends DomQuery(LitElement) { static get styles() { @@ -195,7 +196,7 @@ export class TpTable extends DomQuery(LitElement) { renderTableHeader(columns) { return html` -