igniteui-webcomponents
Version:
Ignite UI for Web Components is a complete library of UI components, giving you the ability to build modern web applications using encapsulation and the concept of reusable components in a dependency-free approach.
244 lines • 7.98 kB
JavaScript
import { getDefaultLayer } from '../../resize-container/default-ghost.js';
import { findElementFromEventPath, getRoot, roundByDPR } from '../util.js';
const additionalEvents = [
'pointermove',
'lostpointercapture',
'contextmenu',
];
class DragController {
get _hasSnapping() {
return Boolean(this._options.snapToCursor);
}
get _isDeferred() {
return this._options.mode === 'deferred';
}
get _element() {
return this._options.trigger?.() ?? this._host;
}
get _dragItem() {
return this._isDeferred ? this._ghost : this._host;
}
get _layer() {
if (!this._isDeferred) {
return this._host;
}
return this._options.layer?.() ?? this._host;
}
get _stateParameters() {
return {
...this._state,
ghost: this._ghost,
element: this._matchedElement,
};
}
constructor(host, options) {
this._options = {
enabled: true,
mode: 'deferred',
snapToCursor: false,
layer: getDefaultLayer,
};
this._id = -1;
this._hasPointerCapture = false;
this._ghost = null;
this._host = host;
this._host.addController(this);
this.set(options);
}
get enabled() {
return Boolean(this._options.enabled);
}
set(options) {
Object.assign(this._options, options);
}
dispose() {
this._matchedElement = null;
this._removeGhost();
this._setDragState(false);
}
hostConnected() {
this._host.addEventListener('dragstart', this);
this._host.addEventListener('touchstart', this, { passive: false });
this._host.addEventListener('pointerdown', this);
}
hostDisconnected() {
this._host.removeEventListener('dragstart', this);
this._host.removeEventListener('touchstart', this);
this._host.removeEventListener('pointerdown', this);
this._setDragCancelListener(false);
this._removeGhost();
}
handleEvent(event) {
if (!this.enabled) {
return;
}
switch (event.type) {
case 'touchstart':
case 'dragstart':
case 'contextmenu':
event.preventDefault();
break;
case 'keydown':
this._handleCancel(event);
break;
case 'pointerdown':
this._handlePointerDown(event);
break;
case 'pointermove':
this._handlePointerMove(event);
break;
case 'lostpointercapture':
this._handlePointerEnd(event);
break;
}
}
_handlePointerDown(event) {
if (this._shouldSkip(event)) {
return;
}
this._setInitialState(event);
this._createDragGhost();
this._updatePosition(event);
const parameters = { event, state: this._stateParameters };
if (this._options.start?.call(this._host, parameters) === false) {
this.dispose();
return;
}
this._assignPosition(this._dragItem);
this._setDragState();
}
_handlePointerMove(event) {
if (!this._hasPointerCapture) {
return;
}
this._updatePosition(event);
this._updateMatcher(event);
const parameters = { event, state: this._stateParameters };
this._options.move?.call(this._host, parameters);
this._assignPosition(this._dragItem);
}
_handlePointerEnd(event) {
this._options.end?.call(this._host, {
event,
state: this._stateParameters,
});
this.dispose();
}
_handleCancel(event) {
const key = event.key.toLowerCase();
if (this._hasPointerCapture && key === 'escape') {
this._options.cancel?.call(this._host, this._stateParameters);
}
}
_setDragCancelListener(enabled = true) {
enabled
? globalThis.addEventListener('keydown', this)
: globalThis.removeEventListener('keydown', this);
}
_setInitialState({ pointerId, clientX, clientY, }) {
const rect = this._host.getBoundingClientRect();
const position = { x: rect.x, y: rect.y };
const offset = { x: rect.x - clientX, y: rect.y - clientY };
this._id = pointerId;
this._state = {
initial: rect,
current: structuredClone(rect),
position,
offset,
};
}
_setDragState(enabled = true) {
this._hasPointerCapture = enabled;
const cssValue = enabled ? 'none' : '';
Object.assign(this._element.style, {
touchAction: cssValue,
userSelect: cssValue,
});
enabled
? this._element.setPointerCapture(this._id)
: this._element.releasePointerCapture(this._id);
this._setDragCancelListener(enabled);
for (const type of additionalEvents) {
enabled
? this._host.addEventListener(type, this)
: this._host.removeEventListener(type, this);
}
}
_updateMatcher(event) {
if (!this._options.matchTarget) {
return;
}
const match = getRoot(this._host)
.elementsFromPoint(event.clientX, event.clientY)
.find((element) => this._options.matchTarget.call(this._host, element));
if (match && !this._matchedElement) {
this._matchedElement = match;
this._options.enter?.call(this._host, {
event,
state: this._stateParameters,
});
return;
}
if (!match && this._matchedElement) {
this._options.leave?.call(this._host, {
event,
state: this._stateParameters,
});
this._matchedElement = null;
return;
}
if (match && match === this._matchedElement) {
this._options.over?.call(this._host, {
event,
state: this._stateParameters,
});
}
}
_updatePosition({ clientX, clientY }) {
const { x, y } = this._state.offset;
const { x: layerX, y: layerY } = this._isDeferred
? this._layer.getBoundingClientRect()
: this._state.initial;
const posX = this._hasSnapping ? clientX - layerX : clientX - layerX + x;
const posY = this._hasSnapping ? clientY - layerY : clientY - layerY + y;
Object.assign(this._state.position, { x: posX, y: posY });
}
_assignPosition(element) {
element.style.transform = `translate3d(${roundByDPR(this._state.position.x)}px,${roundByDPR(this._state.position.y)}px,0)`;
}
_createDragGhost() {
if (!this._isDeferred) {
return;
}
this._ghost =
this._options.ghost?.call(this._host) ??
createDefaultDragGhost(this._host.getBoundingClientRect());
this._layer.append(this._ghost);
}
_removeGhost() {
this._ghost?.remove();
this._ghost = null;
}
_shouldSkip(event) {
return (Boolean(event.button) ||
this._options.skip?.call(this._host, event) ||
!findElementFromEventPath((e) => e === this._element, event));
}
}
function createDefaultDragGhost({ x, y, width, height }) {
const element = document.createElement('div');
Object.assign(element.style, {
position: 'absolute',
left: `${x}px`,
top: `${y}px`,
width: `${width}px`,
height: `${height}px`,
zIndex: 1000,
background: 'gold',
});
return element;
}
export function addDragController(host, options) {
return new DragController(host, options);
}
//# sourceMappingURL=drag.js.map