toolshed-8.x-1.x-dev/assets/widgets/Pane.es6.js
assets/widgets/Pane.es6.js
(({ Toolshed: ts }) => {
/**
* A Pane is floating DIV with manageable content.
*
* It is designed to be the base of dialogs, pop-ups or other elements
* that attach to other content.
*/
class Pane extends ts.Element {
static defaultOptions() {
return {
classes: [],
};
}
/**
* Create a new instance of the a display pane.
*
* @param {Object} options
* The configuration options for how to handle pane behaviors.
*/
constructor(options = {}) {
super('div', null, document.body);
this.opts = {
...Pane.defaultOptions(),
...options,
};
this.content = new ts.Element('div', { class: 'pane__content' }, this);
this.addClass(this.opts.classes.concat('ts-pane', 'pane'));
this.setStyles({
display: 'none',
position: 'absolute',
zIndex: this.opts.zIndex,
});
this.append = !this.opts.onAddItem ? this.appendItem : (item) => {
const el = this.opts.onAddItem(item);
this.appendItem(el || item);
};
this.remove = !this.opts.onRemoveItem ? this.removeItem : (item) => {
const el = this.opts.onRemoveItem(item);
this.removeItem(el || item);
};
}
/**
* Adds and element to the start of the content container.
*
* @param {Node} item
* Item to add to the start of the pane content.
*/
prependItem(item) {
this.content.prependChild(item);
return this;
}
/**
* Add an element to the content container.
*
* @param {Node} item
* The node to add to the content container.
*/
appendItem(item) {
this.content.appendChild(item);
return this;
}
/**
* Remove an element from the content container.
*
* @param {Node} item
* The node to remove from the content container.
*/
removeItem(item) {
this.content.removeChild(item);
return this;
}
/**
* Clear the content container of all nodes and elements.
*/
clear() {
this.content.empty();
return this;
}
/**
* Is the suggestion window open?
*
* @return {Boolean}
* TRUE if the suggestions window is open, otherwise FALSE.
*/
isVisible() {
return this.style.display !== 'none';
}
/**
* Position the pane before displaying it.
*/
// eslint-disable-next-line class-methods-use-this
positionPane() {
// TODO: position based on pane options.
}
/**
* Position and expose the suggestions window if it is not already open.
*
* @return {bool}
* Always returns true, to indicate that the dialog is open.
*/
open() {
if (!this.isVisible()) {
this.style.display = '';
this.positionPane();
}
return true;
}
/**
* Close the content pane.
*
* @return {bool}
* Always returns false, to indicate that the pane is closed.
*/
close() {
this.style.display = 'none';
return false;
}
/**
* Toggle to open and close status of the pane.
*
* @return {bool}
* Returns true if result is pane is open, and false if it is closed.
*/
toggle() {
return this.isVisible() ? this.close() : this.open();
}
/**
* Teardown and clean-up of Pane contents.
*/
destroy() {
this.clear();
super.destroy(true);
}
}
/**
* A pane, that attaches itself to another element in the document.
*
* AttachedPanes are a good base for pop-ups, toolbars or other elements
* that display content and anchor themselves to other screen elements.
*/
class AttachedPane extends Pane {
/**
* Create a new instance of an AttachedPane.
*
* @param {DOMElement} attachTo
* The element containing the pane content.
* @param {Object} options
* The configuration options for how to handle pane behaviors.
*/
constructor(attachTo, options = {}) {
super(options);
this.attachTo = attachTo;
}
/**
* @override
*/
positionPane() {
const rect = this.attachTo.getBoundingClientRect();
const scroll = {
x: document.documentElement.scrollLeft || document.body.scrollLeft,
y: document.documentElement.scrollTop || document.body.scrollTop,
};
this.style.top = (rect.top > this.el.clientHeight)
? `${(scroll.y - this.el.clientHeight) + rect.bottom}px`
: `${scroll.y + rect.top}px`;
this.style.left = `${scroll.x + rect.right}px`;
// Calculate the z-index of the attached button, this becomes important,
// when dealing with form elements in pop-up dialogs. Only calculate this
// if not already explicitly set by the options.
if (!this.opts.zIndex && !this.style.zIndex) {
let cur = this.attachTo;
let zIndex = 10;
while (cur) {
const z = (cur.style || {}).zIndex || window.getComputedStyle(cur).getPropertyValue('z-index');
if (Number.isInteger(z) || /\d+/.test(z)) {
const tz = (ts.isString(z) ? parseInt(z, 10) : z) + 10;
if (tz > zIndex) zIndex = tz;
}
cur = cur.parentElement;
}
this.style.zIndex = zIndex;
}
}
}
ts.Pane = Pane;
ts.AttachedPane = AttachedPane;
})(Drupal);
