dockview-core
Version:
Zero dependency layout manager supporting tabs, groups, grids and splitviews for vanilla TypeScript
152 lines (151 loc) • 7.46 kB
JavaScript
;
var __extends = (this && this.__extends) || (function () {
var extendStatics = function (d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
return extendStatics(d, b);
};
return function (d, b) {
if (typeof b !== "function" && b !== null)
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.LongPressDetector = void 0;
var events_1 = require("../../events");
var lifecycle_1 = require("../../lifecycle");
var DEFAULT_DELAY = 500;
var DEFAULT_TOLERANCE = 8;
/**
* Passive — does not consume the pointer; movement past `tolerance`
* cancels silently so a sibling `PointerDragSource` can take over.
*/
var LongPressDetector = /** @class */ (function (_super) {
__extends(LongPressDetector, _super);
function LongPressDetector(element, options) {
var _this = _super.call(this) || this;
_this.element = element;
_this.options = options;
_this._startX = 0;
_this._startY = 0;
_this.addDisposables((0, events_1.addDisposableListener)(_this.element, 'pointerdown', function (e) {
_this._onPointerDown(e);
}));
return _this;
}
LongPressDetector.prototype._onPointerDown = function (event) {
var _this = this;
var _a, _b, _c, _d, _e;
var touchOnly = (_a = this.options.touchOnly) !== null && _a !== void 0 ? _a : true;
if (touchOnly &&
event.pointerType !== 'touch' &&
event.pointerType !== 'pen') {
return;
}
// Defensive — supersede any in-flight press.
this._cancelPending();
this._pointerId = event.pointerId;
this._startX = event.clientX;
this._startY = event.clientY;
var delay = (_b = this.options.delay) !== null && _b !== void 0 ? _b : DEFAULT_DELAY;
var tolerance = (_c = this.options.tolerance) !== null && _c !== void 0 ? _c : DEFAULT_TOLERANCE;
// Source's owning window — popout drags fire on their own window.
var targetWindow = (_e = (_d = this.element.ownerDocument) === null || _d === void 0 ? void 0 : _d.defaultView) !== null && _e !== void 0 ? _e : window;
this._timer = setTimeout(function () {
_this._timer = undefined;
_this._cancelPending();
// Touch browsers synthesize a compatibility `contextmenu` event
// for long-press. preventDefault on the original pointerdown is
// too late (already dispatched), so install a one-shot
// capture-phase guard for the next contextmenu. Without this,
// consumers that don't preventDefault inside their onLongPress
// (or that early-return before doing so) leak the browser's
// native menu on top of theirs.
_this._installContextMenuGuard(targetWindow);
// Same idea for `click`: when the user releases their finger
// after the long-press, touch browsers dispatch a `click` to
// the element the touch ended on (the source). Consumers
// typically wire click to a primary action (e.g. tab activate,
// tab-group chip collapse-toggle). Without this guard, the
// long-press immediately fires both the context menu AND the
// primary action — and the action's side effects (e.g. a chip
// collapse animation) read as a screen wobble while the menu
// is supposed to be open. Scoped to the source element so
// clicks on menu items elsewhere remain effective.
_this._installClickGuard(targetWindow);
_this.options.onLongPress(event);
}, delay);
this._moveListener = (0, events_1.addDisposableListener)(targetWindow, 'pointermove', function (moveEvent) {
if (moveEvent.pointerId !== _this._pointerId) {
return;
}
var dx = moveEvent.clientX - _this._startX;
var dy = moveEvent.clientY - _this._startY;
if (Math.hypot(dx, dy) > tolerance) {
_this._cancelPending();
}
});
this._upListener = (0, events_1.addDisposableListener)(targetWindow, 'pointerup', function (upEvent) {
if (upEvent.pointerId !== _this._pointerId) {
return;
}
_this._cancelPending();
});
this._cancelListener = (0, events_1.addDisposableListener)(targetWindow, 'pointercancel', function (cancelEvent) {
if (cancelEvent.pointerId !== _this._pointerId) {
return;
}
_this._cancelPending();
});
};
LongPressDetector.prototype._installContextMenuGuard = function (targetWindow) {
var guard;
var timeout = setTimeout(function () { return guard === null || guard === void 0 ? void 0 : guard.dispose(); }, 500);
guard = (0, events_1.addDisposableListener)(targetWindow, 'contextmenu', function (event) {
event.preventDefault();
clearTimeout(timeout);
guard === null || guard === void 0 ? void 0 : guard.dispose();
}, { capture: true });
};
LongPressDetector.prototype._installClickGuard = function (targetWindow) {
var _this = this;
var guard;
var timeout = setTimeout(function () { return guard === null || guard === void 0 ? void 0 : guard.dispose(); }, 500);
guard = (0, events_1.addDisposableListener)(targetWindow, 'click', function (event) {
// Only suppress clicks targeted at the long-pressed element
// or its descendants. A user tap on a context menu item (or
// anywhere else) still gets through unchanged.
var target = event.target;
if (target && _this.element.contains(target)) {
event.preventDefault();
event.stopPropagation();
}
clearTimeout(timeout);
guard === null || guard === void 0 ? void 0 : guard.dispose();
}, { capture: true });
};
LongPressDetector.prototype._cancelPending = function () {
var _a, _b, _c;
if (this._timer !== undefined) {
clearTimeout(this._timer);
this._timer = undefined;
}
this._pointerId = undefined;
(_a = this._moveListener) === null || _a === void 0 ? void 0 : _a.dispose();
(_b = this._upListener) === null || _b === void 0 ? void 0 : _b.dispose();
(_c = this._cancelListener) === null || _c === void 0 ? void 0 : _c.dispose();
this._moveListener = undefined;
this._upListener = undefined;
this._cancelListener = undefined;
};
LongPressDetector.prototype.dispose = function () {
this._cancelPending();
_super.prototype.dispose.call(this);
};
return LongPressDetector;
}(lifecycle_1.CompositeDisposable));
exports.LongPressDetector = LongPressDetector;