UNPKG

@ckeditor/ckeditor5-ui

Version:

The UI framework and standard UI library of CKEditor 5.

145 lines (144 loc) 5.76 kB
/** * @license Copyright (c) 2003-2025, CKSource Holding sp. z o.o. All rights reserved. * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options */ import { global } from '@ckeditor/ckeditor5-utils'; /** * A mixin that brings the possibility to observe dragging of the view element. * The view has to implement the {@link ~DraggableView} interface to use it: * * ```js * export default class MyDraggableView extends DraggableViewMixin( View ) implements DraggableView { * // ... * } * ``` * * Creating a class extending it attaches a set of mouse and touch listeners allowing to observe dragging of the view element: * * `mousedown` and `touchstart` on the view element - starting the dragging. * * `mousemove` and `touchmove` on the document - updating the view coordinates. * * `mouseup` and `touchend` on the document - stopping the dragging. * * The mixin itself does not provide a visual feedback (that is, the dragged element does not change its position) - * it is up to the developer to implement it. */ export default function DraggableViewMixin(view) { class DraggableMixin extends view { /** * A bound version of {@link #_onDrag}. */ _onDragBound = this._onDrag.bind(this); /** * A bound version of {@link #_onDragEnd}. */ _onDragEndBound = this._onDragEnd.bind(this); /** * The last coordinates of the view. It is updated on every mouse move. */ _lastDraggingCoordinates = { x: 0, y: 0 }; /** * @inheritDoc */ constructor(...args) { super(...args); this.on('render', () => { this._attachListeners(); }); this.set('isDragging', false); } /** * Attaches the listeners for the drag start. */ _attachListeners() { this.listenTo(this.element, 'mousedown', this._onDragStart.bind(this)); this.listenTo(this.element, 'touchstart', this._onDragStart.bind(this)); } /** * Attaches the listeners for the dragging and drag end. */ _attachDragListeners() { this.listenTo(global.document, 'mouseup', this._onDragEndBound); this.listenTo(global.document, 'touchend', this._onDragEndBound); this.listenTo(global.document, 'mousemove', this._onDragBound); this.listenTo(global.document, 'touchmove', this._onDragBound); } /** * Detaches the listeners after the drag end. */ _detachDragListeners() { this.stopListening(global.document, 'mouseup', this._onDragEndBound); this.stopListening(global.document, 'touchend', this._onDragEndBound); this.stopListening(global.document, 'mousemove', this._onDragBound); this.stopListening(global.document, 'touchmove', this._onDragBound); } /** * Starts the dragging listeners and sets the initial view coordinates. */ _onDragStart(evt, domEvt) { if (!this._isHandleElementPressed(domEvt)) { return; } this._attachDragListeners(); let x = 0; let y = 0; // If dragging is performed with a mouse, there is only one set of coordinates available. // But when using a touch device, there may be many of them, so use the coordinates from the first touch. if (domEvt instanceof MouseEvent) { x = domEvt.clientX; y = domEvt.clientY; } else { x = domEvt.touches[0].clientX; y = domEvt.touches[0].clientY; } this._lastDraggingCoordinates = { x, y }; this.isDragging = true; } /** * Updates the view coordinates and fires the `drag` event. */ _onDrag(evt, domEvt) { // If dragging was stopped by some external intervention, stop listening. if (!this.isDragging) { this._detachDragListeners(); return; } let newX = 0; let newY = 0; // If dragging is performed with a mouse, there is only one set of coordinates available. // But when using a touch device, there may be many of them, so use the coordinates from the first touch. if (domEvt instanceof MouseEvent) { newX = domEvt.clientX; newY = domEvt.clientY; } else { newX = domEvt.touches[0].clientX; newY = domEvt.touches[0].clientY; } // Prevents selection of text while dragging on Safari. domEvt.preventDefault(); this.fire('drag', { deltaX: Math.round(newX - this._lastDraggingCoordinates.x), deltaY: Math.round(newY - this._lastDraggingCoordinates.y) }); this._lastDraggingCoordinates = { x: newX, y: newY }; } /** * Stops the dragging and detaches the listeners. */ _onDragEnd() { this._detachDragListeners(); this.isDragging = false; } /** * Checks if the drag handle element was pressed. */ _isHandleElementPressed(domEvt) { if (!this.dragHandleElement) { return false; } return this.dragHandleElement === domEvt.target || (domEvt.target instanceof HTMLElement && this.dragHandleElement.contains(domEvt.target)); } } return DraggableMixin; }