dockview
Version:
Zero dependency layout manager supporting tabs, grids and splitviews with ReactJS support
114 lines (113 loc) • 3.8 kB
JavaScript
import { Emitter, addDisposableListener, addDisposableWindowListener, } from './events';
import { CompositeDisposable } from './lifecycle';
export function watchElementResize(element, cb) {
const observer = new ResizeObserver((entires) => {
const firstEntry = entires[0];
cb(firstEntry);
});
observer.observe(element);
return {
dispose: () => {
observer.unobserve(element);
observer.disconnect();
},
};
}
export const removeClasses = (element, ...classes) => {
for (const classname of classes) {
if (element.classList.contains(classname)) {
element.classList.remove(classname);
}
}
};
export const addClasses = (element, ...classes) => {
for (const classname of classes) {
if (!element.classList.contains(classname)) {
element.classList.add(classname);
}
}
};
export const toggleClass = (element, className, isToggled) => {
const hasClass = element.classList.contains(className);
if (isToggled && !hasClass) {
element.classList.add(className);
}
if (!isToggled && hasClass) {
element.classList.remove(className);
}
};
export function isAncestor(testChild, testAncestor) {
while (testChild) {
if (testChild === testAncestor) {
return true;
}
testChild = testChild.parentNode;
}
return false;
}
export function getElementsByTagName(tag) {
return Array.prototype.slice.call(document.getElementsByTagName(tag), 0);
}
export function trackFocus(element) {
return new FocusTracker(element);
}
/**
* Track focus on an element. Ensure tabIndex is set when an HTMLElement is not focusable by default
*/
class FocusTracker extends CompositeDisposable {
constructor(element) {
super();
this._onDidFocus = new Emitter();
this.onDidFocus = this._onDidFocus.event;
this._onDidBlur = new Emitter();
this.onDidBlur = this._onDidBlur.event;
let hasFocus = isAncestor(document.activeElement, element);
let loosingFocus = false;
const onFocus = () => {
loosingFocus = false;
if (!hasFocus) {
hasFocus = true;
this._onDidFocus.fire();
}
};
const onBlur = () => {
if (hasFocus) {
loosingFocus = true;
window.setTimeout(() => {
if (loosingFocus) {
loosingFocus = false;
hasFocus = false;
this._onDidBlur.fire();
}
}, 0);
}
};
this._refreshStateHandler = () => {
const currentNodeHasFocus = isAncestor(document.activeElement, element);
if (currentNodeHasFocus !== hasFocus) {
if (hasFocus) {
onBlur();
}
else {
onFocus();
}
}
};
if (element instanceof HTMLElement) {
this.addDisposables(addDisposableListener(element, 'focus', onFocus, true));
this.addDisposables(addDisposableListener(element, 'blur', onBlur, true));
}
else {
this.addDisposables(addDisposableWindowListener(element, 'focus', onFocus, true));
this.addDisposables(addDisposableWindowListener(element, 'blur', onBlur, true));
}
}
refreshState() {
this._refreshStateHandler();
}
dispose() {
super.dispose();
this._onDidBlur.dispose();
this._onDidFocus.dispose();
}
}