dockview-core
Version:
Zero dependency layout manager supporting tabs, grids and splitviews
481 lines (480 loc) • 18.4 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 __());
};
})();
var __values = (this && this.__values) || function(o) {
var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
if (m) return m.call(o);
if (o && typeof o.length === "number") return {
next: function () {
if (o && i >= o.length) o = void 0;
return { value: o && o[i++], done: !o };
}
};
throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.onDidWindowResizeEnd = exports.onDidWindowMoveEnd = exports.isChildEntirelyVisibleWithinParent = exports.Classnames = exports.getDockviewTheme = exports.disableIframePointEvents = exports.addTestId = exports.isInDocument = exports.getDomNodePagePosition = exports.addStyles = exports.quasiDefaultPrevented = exports.quasiPreventDefault = exports.trackFocus = exports.getElementsByTagName = exports.isAncestor = exports.toggleClass = exports.addClasses = exports.removeClasses = exports.watchElementResize = exports.OverflowObserver = void 0;
var events_1 = require("./events");
var lifecycle_1 = require("./lifecycle");
var OverflowObserver = /** @class */ (function (_super) {
__extends(OverflowObserver, _super);
function OverflowObserver(el) {
var _this = _super.call(this) || this;
_this._onDidChange = new events_1.Emitter();
_this.onDidChange = _this._onDidChange.event;
_this._value = null;
_this.addDisposables(_this._onDidChange, watchElementResize(el, function (entry) {
var hasScrollX = entry.target.scrollWidth > entry.target.clientWidth;
var hasScrollY = entry.target.scrollHeight > entry.target.clientHeight;
_this._value = { hasScrollX: hasScrollX, hasScrollY: hasScrollY };
_this._onDidChange.fire(_this._value);
}));
return _this;
}
return OverflowObserver;
}(lifecycle_1.CompositeDisposable));
exports.OverflowObserver = OverflowObserver;
function watchElementResize(element, cb) {
var observer = new ResizeObserver(function (entires) {
/**
* Fast browser window resize produces Error: ResizeObserver loop limit exceeded.
* The error isn't visible in browser console, doesn't affect functionality, but degrades performance.
* See https://stackoverflow.com/questions/49384120/resizeobserver-loop-limit-exceeded/58701523#58701523
*/
requestAnimationFrame(function () {
var firstEntry = entires[0];
cb(firstEntry);
});
});
observer.observe(element);
return {
dispose: function () {
observer.unobserve(element);
observer.disconnect();
},
};
}
exports.watchElementResize = watchElementResize;
var removeClasses = function (element) {
var e_1, _a;
var classes = [];
for (var _i = 1; _i < arguments.length; _i++) {
classes[_i - 1] = arguments[_i];
}
try {
for (var classes_1 = __values(classes), classes_1_1 = classes_1.next(); !classes_1_1.done; classes_1_1 = classes_1.next()) {
var classname = classes_1_1.value;
if (element.classList.contains(classname)) {
element.classList.remove(classname);
}
}
}
catch (e_1_1) { e_1 = { error: e_1_1 }; }
finally {
try {
if (classes_1_1 && !classes_1_1.done && (_a = classes_1.return)) _a.call(classes_1);
}
finally { if (e_1) throw e_1.error; }
}
};
exports.removeClasses = removeClasses;
var addClasses = function (element) {
var e_2, _a;
var classes = [];
for (var _i = 1; _i < arguments.length; _i++) {
classes[_i - 1] = arguments[_i];
}
try {
for (var classes_2 = __values(classes), classes_2_1 = classes_2.next(); !classes_2_1.done; classes_2_1 = classes_2.next()) {
var classname = classes_2_1.value;
if (!element.classList.contains(classname)) {
element.classList.add(classname);
}
}
}
catch (e_2_1) { e_2 = { error: e_2_1 }; }
finally {
try {
if (classes_2_1 && !classes_2_1.done && (_a = classes_2.return)) _a.call(classes_2);
}
finally { if (e_2) throw e_2.error; }
}
};
exports.addClasses = addClasses;
var toggleClass = function (element, className, isToggled) {
var hasClass = element.classList.contains(className);
if (isToggled && !hasClass) {
element.classList.add(className);
}
if (!isToggled && hasClass) {
element.classList.remove(className);
}
};
exports.toggleClass = toggleClass;
function isAncestor(testChild, testAncestor) {
while (testChild) {
if (testChild === testAncestor) {
return true;
}
testChild = testChild.parentNode;
}
return false;
}
exports.isAncestor = isAncestor;
function getElementsByTagName(tag, document) {
return Array.prototype.slice.call(document.querySelectorAll(tag), 0);
}
exports.getElementsByTagName = getElementsByTagName;
function trackFocus(element) {
return new FocusTracker(element);
}
exports.trackFocus = trackFocus;
/**
* Track focus on an element. Ensure tabIndex is set when an HTMLElement is not focusable by default
*/
var FocusTracker = /** @class */ (function (_super) {
__extends(FocusTracker, _super);
function FocusTracker(element) {
var _this = _super.call(this) || this;
_this._onDidFocus = new events_1.Emitter();
_this.onDidFocus = _this._onDidFocus.event;
_this._onDidBlur = new events_1.Emitter();
_this.onDidBlur = _this._onDidBlur.event;
_this.addDisposables(_this._onDidFocus, _this._onDidBlur);
var hasFocus = isAncestor(document.activeElement, element);
var loosingFocus = false;
var onFocus = function () {
loosingFocus = false;
if (!hasFocus) {
hasFocus = true;
_this._onDidFocus.fire();
}
};
var onBlur = function () {
if (hasFocus) {
loosingFocus = true;
window.setTimeout(function () {
if (loosingFocus) {
loosingFocus = false;
hasFocus = false;
_this._onDidBlur.fire();
}
}, 0);
}
};
_this._refreshStateHandler = function () {
var currentNodeHasFocus = isAncestor(document.activeElement, element);
if (currentNodeHasFocus !== hasFocus) {
if (hasFocus) {
onBlur();
}
else {
onFocus();
}
}
};
_this.addDisposables((0, events_1.addDisposableListener)(element, 'focus', onFocus, true));
_this.addDisposables((0, events_1.addDisposableListener)(element, 'blur', onBlur, true));
return _this;
}
FocusTracker.prototype.refreshState = function () {
this._refreshStateHandler();
};
return FocusTracker;
}(lifecycle_1.CompositeDisposable));
// quasi: apparently, but not really; seemingly
var QUASI_PREVENT_DEFAULT_KEY = 'dv-quasiPreventDefault';
// mark an event directly for other listeners to check
function quasiPreventDefault(event) {
event[QUASI_PREVENT_DEFAULT_KEY] = true;
}
exports.quasiPreventDefault = quasiPreventDefault;
// check if this event has been marked
function quasiDefaultPrevented(event) {
return event[QUASI_PREVENT_DEFAULT_KEY];
}
exports.quasiDefaultPrevented = quasiDefaultPrevented;
function addStyles(document, styleSheetList) {
var e_3, _a, e_4, _b;
var styleSheets = Array.from(styleSheetList);
try {
for (var styleSheets_1 = __values(styleSheets), styleSheets_1_1 = styleSheets_1.next(); !styleSheets_1_1.done; styleSheets_1_1 = styleSheets_1.next()) {
var styleSheet = styleSheets_1_1.value;
if (styleSheet.href) {
var link = document.createElement('link');
link.href = styleSheet.href;
link.type = styleSheet.type;
link.rel = 'stylesheet';
document.head.appendChild(link);
}
var cssTexts = [];
try {
if (styleSheet.cssRules) {
cssTexts = Array.from(styleSheet.cssRules).map(function (rule) { return rule.cssText; });
}
}
catch (err) {
// security errors (lack of permissions), ignore
}
try {
for (var cssTexts_1 = (e_4 = void 0, __values(cssTexts)), cssTexts_1_1 = cssTexts_1.next(); !cssTexts_1_1.done; cssTexts_1_1 = cssTexts_1.next()) {
var rule = cssTexts_1_1.value;
var style = document.createElement('style');
style.appendChild(document.createTextNode(rule));
document.head.appendChild(style);
}
}
catch (e_4_1) { e_4 = { error: e_4_1 }; }
finally {
try {
if (cssTexts_1_1 && !cssTexts_1_1.done && (_b = cssTexts_1.return)) _b.call(cssTexts_1);
}
finally { if (e_4) throw e_4.error; }
}
}
}
catch (e_3_1) { e_3 = { error: e_3_1 }; }
finally {
try {
if (styleSheets_1_1 && !styleSheets_1_1.done && (_a = styleSheets_1.return)) _a.call(styleSheets_1);
}
finally { if (e_3) throw e_3.error; }
}
}
exports.addStyles = addStyles;
function getDomNodePagePosition(domNode) {
var _a = domNode.getBoundingClientRect(), left = _a.left, top = _a.top, width = _a.width, height = _a.height;
return {
left: left + window.scrollX,
top: top + window.scrollY,
width: width,
height: height,
};
}
exports.getDomNodePagePosition = getDomNodePagePosition;
/**
* Check whether an element is in the DOM (including the Shadow DOM)
* @see https://terodox.tech/how-to-tell-if-an-element-is-in-the-dom-including-the-shadow-dom/
*/
function isInDocument(element) {
var currentElement = element;
while (currentElement === null || currentElement === void 0 ? void 0 : currentElement.parentNode) {
if (currentElement.parentNode === document) {
return true;
}
else if (currentElement.parentNode instanceof DocumentFragment) {
// handle shadow DOMs
currentElement = currentElement.parentNode.host;
}
else {
currentElement = currentElement.parentNode;
}
}
return false;
}
exports.isInDocument = isInDocument;
function addTestId(element, id) {
element.setAttribute('data-testid', id);
}
exports.addTestId = addTestId;
/**
* Should be more efficient than element.querySelectorAll("*") since there
* is no need to store every element in-memory using this approach
*/
function allTagsNamesInclusiveOfShadowDoms(tagNames) {
var iframes = [];
function findIframesInNode(node) {
var e_5, _a;
if (node.nodeType === Node.ELEMENT_NODE) {
if (tagNames.includes(node.tagName)) {
iframes.push(node);
}
if (node.shadowRoot) {
findIframesInNode(node.shadowRoot);
}
try {
for (var _b = __values(node.children), _c = _b.next(); !_c.done; _c = _b.next()) {
var child = _c.value;
findIframesInNode(child);
}
}
catch (e_5_1) { e_5 = { error: e_5_1 }; }
finally {
try {
if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
}
finally { if (e_5) throw e_5.error; }
}
}
}
findIframesInNode(document.documentElement);
return iframes;
}
function disableIframePointEvents(rootNode) {
var e_6, _a;
if (rootNode === void 0) { rootNode = document; }
var iframes = allTagsNamesInclusiveOfShadowDoms(['IFRAME', 'WEBVIEW']);
var original = new WeakMap(); // don't hold onto HTMLElement references longer than required
try {
for (var iframes_1 = __values(iframes), iframes_1_1 = iframes_1.next(); !iframes_1_1.done; iframes_1_1 = iframes_1.next()) {
var iframe = iframes_1_1.value;
original.set(iframe, iframe.style.pointerEvents);
iframe.style.pointerEvents = 'none';
}
}
catch (e_6_1) { e_6 = { error: e_6_1 }; }
finally {
try {
if (iframes_1_1 && !iframes_1_1.done && (_a = iframes_1.return)) _a.call(iframes_1);
}
finally { if (e_6) throw e_6.error; }
}
return {
release: function () {
var e_7, _a;
var _b;
try {
for (var iframes_2 = __values(iframes), iframes_2_1 = iframes_2.next(); !iframes_2_1.done; iframes_2_1 = iframes_2.next()) {
var iframe = iframes_2_1.value;
iframe.style.pointerEvents = (_b = original.get(iframe)) !== null && _b !== void 0 ? _b : 'auto';
}
}
catch (e_7_1) { e_7 = { error: e_7_1 }; }
finally {
try {
if (iframes_2_1 && !iframes_2_1.done && (_a = iframes_2.return)) _a.call(iframes_2);
}
finally { if (e_7) throw e_7.error; }
}
iframes.splice(0, iframes.length); // don't hold onto HTMLElement references longer than required
},
};
}
exports.disableIframePointEvents = disableIframePointEvents;
function getDockviewTheme(element) {
function toClassList(element) {
var list = [];
for (var i = 0; i < element.classList.length; i++) {
list.push(element.classList.item(i));
}
return list;
}
var theme = undefined;
var parent = element;
while (parent !== null) {
theme = toClassList(parent).find(function (cls) {
return cls.startsWith('dockview-theme-');
});
if (typeof theme === 'string') {
break;
}
parent = parent.parentElement;
}
return theme;
}
exports.getDockviewTheme = getDockviewTheme;
var Classnames = /** @class */ (function () {
function Classnames(element) {
this.element = element;
this._classNames = [];
}
Classnames.prototype.setClassNames = function (classNames) {
var e_8, _a, e_9, _b;
try {
for (var _c = __values(this._classNames), _d = _c.next(); !_d.done; _d = _c.next()) {
var className = _d.value;
(0, exports.toggleClass)(this.element, className, false);
}
}
catch (e_8_1) { e_8 = { error: e_8_1 }; }
finally {
try {
if (_d && !_d.done && (_a = _c.return)) _a.call(_c);
}
finally { if (e_8) throw e_8.error; }
}
this._classNames = classNames
.split(' ')
.filter(function (v) { return v.trim().length > 0; });
try {
for (var _e = __values(this._classNames), _f = _e.next(); !_f.done; _f = _e.next()) {
var className = _f.value;
(0, exports.toggleClass)(this.element, className, true);
}
}
catch (e_9_1) { e_9 = { error: e_9_1 }; }
finally {
try {
if (_f && !_f.done && (_b = _e.return)) _b.call(_e);
}
finally { if (e_9) throw e_9.error; }
}
};
return Classnames;
}());
exports.Classnames = Classnames;
var DEBOUCE_DELAY = 100;
function isChildEntirelyVisibleWithinParent(child, parent) {
//
var childPosition = getDomNodePagePosition(child);
var parentPosition = getDomNodePagePosition(parent);
if (childPosition.left < parentPosition.left) {
return false;
}
if (childPosition.left + childPosition.width >
parentPosition.left + parentPosition.width) {
return false;
}
return true;
}
exports.isChildEntirelyVisibleWithinParent = isChildEntirelyVisibleWithinParent;
function onDidWindowMoveEnd(window) {
var emitter = new events_1.Emitter();
var previousScreenX = window.screenX;
var previousScreenY = window.screenY;
var timeout;
var checkMovement = function () {
if (window.closed) {
return;
}
var currentScreenX = window.screenX;
var currentScreenY = window.screenY;
if (currentScreenX !== previousScreenX ||
currentScreenY !== previousScreenY) {
clearTimeout(timeout);
timeout = setTimeout(function () {
emitter.fire();
}, DEBOUCE_DELAY);
previousScreenX = currentScreenX;
previousScreenY = currentScreenY;
}
requestAnimationFrame(checkMovement);
};
checkMovement();
return emitter;
}
exports.onDidWindowMoveEnd = onDidWindowMoveEnd;
function onDidWindowResizeEnd(element, cb) {
var resizeTimeout;
var disposable = new lifecycle_1.CompositeDisposable((0, events_1.addDisposableListener)(element, 'resize', function () {
clearTimeout(resizeTimeout);
resizeTimeout = setTimeout(function () {
cb();
}, DEBOUCE_DELAY);
}));
return disposable;
}
exports.onDidWindowResizeEnd = onDidWindowResizeEnd;
;