dockview-core
Version:
Zero dependency layout manager supporting tabs, grids and splitviews
472 lines (471 loc) • 20.2 kB
JavaScript
"use strict";
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.calculateQuadrantAsPixels = exports.calculateQuadrantAsPercentage = exports.Droptarget = exports.positionToDirection = exports.directionToPosition = exports.WillShowOverlayEvent = void 0;
var dom_1 = require("../dom");
var events_1 = require("../events");
var lifecycle_1 = require("../lifecycle");
var dnd_1 = require("./dnd");
var math_1 = require("../math");
var WillShowOverlayEvent = /** @class */ (function (_super) {
__extends(WillShowOverlayEvent, _super);
function WillShowOverlayEvent(options) {
var _this = _super.call(this) || this;
_this.options = options;
return _this;
}
Object.defineProperty(WillShowOverlayEvent.prototype, "nativeEvent", {
get: function () {
return this.options.nativeEvent;
},
enumerable: false,
configurable: true
});
Object.defineProperty(WillShowOverlayEvent.prototype, "position", {
get: function () {
return this.options.position;
},
enumerable: false,
configurable: true
});
return WillShowOverlayEvent;
}(events_1.DockviewEvent));
exports.WillShowOverlayEvent = WillShowOverlayEvent;
function directionToPosition(direction) {
switch (direction) {
case 'above':
return 'top';
case 'below':
return 'bottom';
case 'left':
return 'left';
case 'right':
return 'right';
case 'within':
return 'center';
default:
throw new Error("invalid direction '".concat(direction, "'"));
}
}
exports.directionToPosition = directionToPosition;
function positionToDirection(position) {
switch (position) {
case 'top':
return 'above';
case 'bottom':
return 'below';
case 'left':
return 'left';
case 'right':
return 'right';
case 'center':
return 'within';
default:
throw new Error("invalid position '".concat(position, "'"));
}
}
exports.positionToDirection = positionToDirection;
var DEFAULT_ACTIVATION_SIZE = {
value: 20,
type: 'percentage',
};
var DEFAULT_SIZE = {
value: 50,
type: 'percentage',
};
var SMALL_WIDTH_BOUNDARY = 100;
var SMALL_HEIGHT_BOUNDARY = 100;
var Droptarget = /** @class */ (function (_super) {
__extends(Droptarget, _super);
function Droptarget(element, options) {
var _this = _super.call(this) || this;
_this.element = element;
_this.options = options;
_this._onDrop = new events_1.Emitter();
_this.onDrop = _this._onDrop.event;
_this._onWillShowOverlay = new events_1.Emitter();
_this.onWillShowOverlay = _this._onWillShowOverlay.event;
_this._disabled = false;
// use a set to take advantage of #<set>.has
_this._acceptedTargetZonesSet = new Set(_this.options.acceptedTargetZones);
_this.dnd = new dnd_1.DragAndDropObserver(_this.element, {
onDragEnter: function () {
var _a, _b, _c;
(_c = (_b = (_a = _this.options).getOverrideTarget) === null || _b === void 0 ? void 0 : _b.call(_a)) === null || _c === void 0 ? void 0 : _c.getElements();
},
onDragOver: function (e) {
var _a, _b, _c, _d, _e, _f, _g;
Droptarget.ACTUAL_TARGET = _this;
var overrideTraget = (_b = (_a = _this.options).getOverrideTarget) === null || _b === void 0 ? void 0 : _b.call(_a);
if (_this._acceptedTargetZonesSet.size === 0) {
if (overrideTraget) {
return;
}
_this.removeDropTarget();
return;
}
var target = (_e = (_d = (_c = _this.options).getOverlayOutline) === null || _d === void 0 ? void 0 : _d.call(_c)) !== null && _e !== void 0 ? _e : _this.element;
var width = target.offsetWidth;
var height = target.offsetHeight;
if (width === 0 || height === 0) {
return; // avoid div!0
}
var rect = e.currentTarget.getBoundingClientRect();
var x = ((_f = e.clientX) !== null && _f !== void 0 ? _f : 0) - rect.left;
var y = ((_g = e.clientY) !== null && _g !== void 0 ? _g : 0) - rect.top;
var quadrant = _this.calculateQuadrant(_this._acceptedTargetZonesSet, x, y, width, height);
/**
* If the event has already been used by another DropTarget instance
* then don't show a second drop target, only one target should be
* active at any one time
*/
if (_this.isAlreadyUsed(e) || quadrant === null) {
// no drop target should be displayed
_this.removeDropTarget();
return;
}
if (!_this.options.canDisplayOverlay(e, quadrant)) {
if (overrideTraget) {
return;
}
_this.removeDropTarget();
return;
}
var willShowOverlayEvent = new WillShowOverlayEvent({
nativeEvent: e,
position: quadrant,
});
/**
* Provide an opportunity to prevent the overlay appearing and in turn
* any dnd behaviours
*/
_this._onWillShowOverlay.fire(willShowOverlayEvent);
if (willShowOverlayEvent.defaultPrevented) {
_this.removeDropTarget();
return;
}
_this.markAsUsed(e);
if (overrideTraget) {
//
}
else if (!_this.targetElement) {
_this.targetElement = document.createElement('div');
_this.targetElement.className = 'dv-drop-target-dropzone';
_this.overlayElement = document.createElement('div');
_this.overlayElement.className = 'dv-drop-target-selection';
_this._state = 'center';
_this.targetElement.appendChild(_this.overlayElement);
target.classList.add('dv-drop-target');
target.append(_this.targetElement);
// this.overlayElement.style.opacity = '0';
// requestAnimationFrame(() => {
// if (this.overlayElement) {
// this.overlayElement.style.opacity = '';
// }
// });
}
_this.toggleClasses(quadrant, width, height);
_this._state = quadrant;
},
onDragLeave: function () {
var _a, _b;
var target = (_b = (_a = _this.options).getOverrideTarget) === null || _b === void 0 ? void 0 : _b.call(_a);
if (target) {
return;
}
_this.removeDropTarget();
},
onDragEnd: function (e) {
var _a, _b;
var target = (_b = (_a = _this.options).getOverrideTarget) === null || _b === void 0 ? void 0 : _b.call(_a);
if (target && Droptarget.ACTUAL_TARGET === _this) {
if (_this._state) {
// only stop the propagation of the event if we are dealing with it
// which is only when the target has state
e.stopPropagation();
_this._onDrop.fire({
position: _this._state,
nativeEvent: e,
});
}
}
_this.removeDropTarget();
target === null || target === void 0 ? void 0 : target.clear();
},
onDrop: function (e) {
var _a, _b, _c;
e.preventDefault();
var state = _this._state;
_this.removeDropTarget();
(_c = (_b = (_a = _this.options).getOverrideTarget) === null || _b === void 0 ? void 0 : _b.call(_a)) === null || _c === void 0 ? void 0 : _c.clear();
if (state) {
// only stop the propagation of the event if we are dealing with it
// which is only when the target has state
e.stopPropagation();
_this._onDrop.fire({ position: state, nativeEvent: e });
}
},
});
_this.addDisposables(_this._onDrop, _this._onWillShowOverlay, _this.dnd);
return _this;
}
Object.defineProperty(Droptarget.prototype, "disabled", {
get: function () {
return this._disabled;
},
set: function (value) {
this._disabled = value;
},
enumerable: false,
configurable: true
});
Object.defineProperty(Droptarget.prototype, "state", {
get: function () {
return this._state;
},
enumerable: false,
configurable: true
});
Droptarget.prototype.setTargetZones = function (acceptedTargetZones) {
this._acceptedTargetZonesSet = new Set(acceptedTargetZones);
};
Droptarget.prototype.setOverlayModel = function (model) {
this.options.overlayModel = model;
};
Droptarget.prototype.dispose = function () {
this.removeDropTarget();
_super.prototype.dispose.call(this);
};
/**
* Add a property to the event object for other potential listeners to check
*/
Droptarget.prototype.markAsUsed = function (event) {
event[Droptarget.USED_EVENT_ID] = true;
};
/**
* Check is the event has already been used by another instance of DropTarget
*/
Droptarget.prototype.isAlreadyUsed = function (event) {
var value = event[Droptarget.USED_EVENT_ID];
return typeof value === 'boolean' && value;
};
Droptarget.prototype.toggleClasses = function (quadrant, width, height) {
var _a, _b, _c, _d, _e, _f, _g;
var target = (_b = (_a = this.options).getOverrideTarget) === null || _b === void 0 ? void 0 : _b.call(_a);
if (!target && !this.overlayElement) {
return;
}
var isSmallX = width < SMALL_WIDTH_BOUNDARY;
var isSmallY = height < SMALL_HEIGHT_BOUNDARY;
var isLeft = quadrant === 'left';
var isRight = quadrant === 'right';
var isTop = quadrant === 'top';
var isBottom = quadrant === 'bottom';
var rightClass = !isSmallX && isRight;
var leftClass = !isSmallX && isLeft;
var topClass = !isSmallY && isTop;
var bottomClass = !isSmallY && isBottom;
var size = 1;
var sizeOptions = (_d = (_c = this.options.overlayModel) === null || _c === void 0 ? void 0 : _c.size) !== null && _d !== void 0 ? _d : DEFAULT_SIZE;
if (sizeOptions.type === 'percentage') {
size = (0, math_1.clamp)(sizeOptions.value, 0, 100) / 100;
}
else {
if (rightClass || leftClass) {
size = (0, math_1.clamp)(0, sizeOptions.value, width) / width;
}
if (topClass || bottomClass) {
size = (0, math_1.clamp)(0, sizeOptions.value, height) / height;
}
}
if (target) {
var outlineEl = (_g = (_f = (_e = this.options).getOverlayOutline) === null || _f === void 0 ? void 0 : _f.call(_e)) !== null && _g !== void 0 ? _g : this.element;
var elBox = outlineEl.getBoundingClientRect();
var ta = target.getElements(undefined, outlineEl);
var el = ta.root;
var overlay_1 = ta.overlay;
var bigbox = el.getBoundingClientRect();
var rootTop = elBox.top - bigbox.top;
var rootLeft = elBox.left - bigbox.left;
var box_1 = {
top: rootTop,
left: rootLeft,
width: width,
height: height,
};
if (rightClass) {
box_1.left = rootLeft + width * (1 - size);
box_1.width = width * size;
}
else if (leftClass) {
box_1.width = width * size;
}
else if (topClass) {
box_1.height = height * size;
}
else if (bottomClass) {
box_1.top = rootTop + height * (1 - size);
box_1.height = height * size;
}
if (isSmallX && isLeft) {
box_1.width = 4;
}
if (isSmallX && isRight) {
box_1.left = rootLeft + width - 4;
box_1.width = 4;
}
var topPx = "".concat(Math.round(box_1.top), "px");
var leftPx = "".concat(Math.round(box_1.left), "px");
var widthPx = "".concat(Math.round(box_1.width), "px");
var heightPx = "".concat(Math.round(box_1.height), "px");
if (overlay_1.style.top === topPx &&
overlay_1.style.left === leftPx &&
overlay_1.style.width === widthPx &&
overlay_1.style.height === heightPx) {
return;
}
overlay_1.style.top = topPx;
overlay_1.style.left = leftPx;
overlay_1.style.width = widthPx;
overlay_1.style.height = heightPx;
overlay_1.style.visibility = 'visible';
overlay_1.className = "dv-drop-target-anchor".concat(this.options.className ? " ".concat(this.options.className) : '');
(0, dom_1.toggleClass)(overlay_1, 'dv-drop-target-left', isLeft);
(0, dom_1.toggleClass)(overlay_1, 'dv-drop-target-right', isRight);
(0, dom_1.toggleClass)(overlay_1, 'dv-drop-target-top', isTop);
(0, dom_1.toggleClass)(overlay_1, 'dv-drop-target-bottom', isBottom);
(0, dom_1.toggleClass)(overlay_1, 'dv-drop-target-center', quadrant === 'center');
if (ta.changed) {
(0, dom_1.toggleClass)(overlay_1, 'dv-drop-target-anchor-container-changed', true);
setTimeout(function () {
(0, dom_1.toggleClass)(overlay_1, 'dv-drop-target-anchor-container-changed', false);
}, 10);
}
return;
}
if (!this.overlayElement) {
return;
}
var box = { top: '0px', left: '0px', width: '100%', height: '100%' };
/**
* You can also achieve the overlay placement using the transform CSS property
* to translate and scale the element however this has the undesired effect of
* 'skewing' the element. Comment left here for anybody that ever revisits this.
*
* @see https://developer.mozilla.org/en-US/docs/Web/CSS/transform
*
* right
* translateX(${100 * (1 - size) / 2}%) scaleX(${scale})
*
* left
* translateX(-${100 * (1 - size) / 2}%) scaleX(${scale})
*
* top
* translateY(-${100 * (1 - size) / 2}%) scaleY(${scale})
*
* bottom
* translateY(${100 * (1 - size) / 2}%) scaleY(${scale})
*/
if (rightClass) {
box.left = "".concat(100 * (1 - size), "%");
box.width = "".concat(100 * size, "%");
}
else if (leftClass) {
box.width = "".concat(100 * size, "%");
}
else if (topClass) {
box.height = "".concat(100 * size, "%");
}
else if (bottomClass) {
box.top = "".concat(100 * (1 - size), "%");
box.height = "".concat(100 * size, "%");
}
this.overlayElement.style.top = box.top;
this.overlayElement.style.left = box.left;
this.overlayElement.style.width = box.width;
this.overlayElement.style.height = box.height;
(0, dom_1.toggleClass)(this.overlayElement, 'dv-drop-target-small-vertical', isSmallY);
(0, dom_1.toggleClass)(this.overlayElement, 'dv-drop-target-small-horizontal', isSmallX);
(0, dom_1.toggleClass)(this.overlayElement, 'dv-drop-target-left', isLeft);
(0, dom_1.toggleClass)(this.overlayElement, 'dv-drop-target-right', isRight);
(0, dom_1.toggleClass)(this.overlayElement, 'dv-drop-target-top', isTop);
(0, dom_1.toggleClass)(this.overlayElement, 'dv-drop-target-bottom', isBottom);
(0, dom_1.toggleClass)(this.overlayElement, 'dv-drop-target-center', quadrant === 'center');
};
Droptarget.prototype.calculateQuadrant = function (overlayType, x, y, width, height) {
var _a, _b;
var activationSizeOptions = (_b = (_a = this.options.overlayModel) === null || _a === void 0 ? void 0 : _a.activationSize) !== null && _b !== void 0 ? _b : DEFAULT_ACTIVATION_SIZE;
var isPercentage = activationSizeOptions.type === 'percentage';
if (isPercentage) {
return calculateQuadrantAsPercentage(overlayType, x, y, width, height, activationSizeOptions.value);
}
return calculateQuadrantAsPixels(overlayType, x, y, width, height, activationSizeOptions.value);
};
Droptarget.prototype.removeDropTarget = function () {
var _a;
if (this.targetElement) {
this._state = undefined;
(_a = this.targetElement.parentElement) === null || _a === void 0 ? void 0 : _a.classList.remove('dv-drop-target');
this.targetElement.remove();
this.targetElement = undefined;
this.overlayElement = undefined;
}
};
Droptarget.USED_EVENT_ID = '__dockview_droptarget_event_is_used__';
return Droptarget;
}(lifecycle_1.CompositeDisposable));
exports.Droptarget = Droptarget;
function calculateQuadrantAsPercentage(overlayType, x, y, width, height, threshold) {
var xp = (100 * x) / width;
var yp = (100 * y) / height;
if (overlayType.has('left') && xp < threshold) {
return 'left';
}
if (overlayType.has('right') && xp > 100 - threshold) {
return 'right';
}
if (overlayType.has('top') && yp < threshold) {
return 'top';
}
if (overlayType.has('bottom') && yp > 100 - threshold) {
return 'bottom';
}
if (!overlayType.has('center')) {
return null;
}
return 'center';
}
exports.calculateQuadrantAsPercentage = calculateQuadrantAsPercentage;
function calculateQuadrantAsPixels(overlayType, x, y, width, height, threshold) {
if (overlayType.has('left') && x < threshold) {
return 'left';
}
if (overlayType.has('right') && x > width - threshold) {
return 'right';
}
if (overlayType.has('top') && y < threshold) {
return 'top';
}
if (overlayType.has('bottom') && y > height - threshold) {
return 'bottom';
}
if (!overlayType.has('center')) {
return null;
}
return 'center';
}
exports.calculateQuadrantAsPixels = calculateQuadrantAsPixels;