import { render, html } from '@lion/core';
import '@lion/core/src/differentKeyEventNamesShimIE.js';
import { containFocus } from './utils/contain-focus.js';
/**
* This is the interface for a controller
*/
export class BaseOverlayController {
get _showHideMode() {
return this.__showHideMode; // dom, css
}
get isShown() {
return this.__isShown;
}
set isShown(value) {
this.__isShown = value;
}
get content() {
return this.__content;
}
set content(value) {
this.__content = value;
}
get contentTemplate() {
return this.__contentTemplate;
}
set contentTemplate(templateFunction) {
if (typeof templateFunction !== 'function') {
throw new Error('.contentTemplate needs to be a function');
}
const tmp = document.createElement('div');
render(templateFunction(this.contentData), tmp);
if (tmp.children.length !== 1) {
throw new Error('The .contentTemplate needs to always return exactly one child node');
}
this.__contentTemplate = templateFunction;
this.__showHideViaDom();
}
get contentData() {
return this.__contentData;
}
set contentData(value) {
if (!this.contentTemplate) {
throw new Error('.contentData can only be used if there is a .contentTemplate function');
}
this.__contentData = value;
this.__showHideViaDom();
}
get contentNode() {
return this.__contentNode;
}
set contentNode(node) {
this.__contentNode = node;
this.content = node;
// setting a contentNode means hide/show with css
this.__showHideMode = 'css';
if (this.isShown === false) {
this.contentNode.style.display = 'none';
}
}
constructor(params = {}) {
this.__fakeExtendsEventTarget();
this.__firstContentTemplateRender = false;
this.__showHideMode = 'dom';
this.isShown = false;
this.__setupContent(params);
// Features initial state
this.__hasActiveTrapsKeyboardFocus = false;
this.__hasActiveHidesOnEsc = false;
}
// TODO: add an ctrl.updateComplete e.g. when async show is done?
async show() {
if (this.manager) {
this.manager.show(this);
}
if (this.isShown === true) {
return;
}
this.isShown = true;
this.__handleShowChange();
this.dispatchEvent(new Event('show'));
}
async hide() {
if (this.manager) {
this.manager.hide(this);
}
if (this.isShown === false) {
return;
}
this.isShown = false;
if (!this.hideDone) {
this.defaultHideDone();
}
}
defaultHideDone() {
this.__handleShowChange();
this.dispatchEvent(new Event('hide'));
}
/**
* Toggles the overlay.
*/
async toggle() {
if (this.isShown === true) {
await this.hide();
} else {
await this.show();
}
}
// eslint-disable-next-line class-methods-use-this
switchIn() {}
// eslint-disable-next-line class-methods-use-this
switchOut() {}
// eslint-disable-next-line class-methods-use-this
onContentUpdated() {}
__setupContent(params) {
if (params.contentTemplate && params.contentNode) {
throw new Error('You can only provide a .contentTemplate or a .contentNode but not both');
}
if (!params.contentTemplate && !params.contentNode) {
throw new Error('You need to provide a .contentTemplate or a .contentNode');
}
if (params.contentTemplate) {
this.contentTemplate = params.contentTemplate;
}
if (params.contentNode) {
this.contentNode = params.contentNode;
}
}
__handleShowChange() {
if (this._showHideMode === 'dom') {
this.__showHideViaDom();
}
if (this._showHideMode === 'css') {
if (this.contentTemplate && !this.__firstContentTemplateRender) {
this.__showHideViaDom();
this.__firstContentTemplateRender = true;
}
this.__showHideViaCss();
}
}
__showHideViaDom() {
if (!this.contentTemplate) {
return;
}
if (!this.content) {
this.content = document.createElement('div');
}
if (this.isShown) {
render(this.contentTemplate(this.contentData), this.content);
this.__contentNode = this.content.firstElementChild;
this.onContentUpdated();
} else {
render(html``, this.content);
this.__contentNode = undefined;
}
}
// eslint-disable-next-line class-methods-use-this
__showHideViaCss() {}
// TODO: this method has to be removed when EventTarget polyfill is available on IE11
__fakeExtendsEventTarget() {
const delegate = document.createDocumentFragment();
['addEventListener', 'dispatchEvent', 'removeEventListener'].forEach(funcName => {
this[funcName] = (...args) => delegate[funcName](...args);
});
}
__enableFeatures() {
if (this.trapsKeyboardFocus) {
this.enableTrapsKeyboardFocus();
}
if (this.hidesOnEsc) {
this.enableHidesOnEsc();
}
}
__disableFeatures() {
if (this.trapsKeyboardFocus) {
this.disableTrapsKeyboardFocus();
}
if (this.hidesOnEsc) {
this.disableHidesOnEsc();
}
}
// **********************************************************************************************
// FEATURE - TrapsKeyboardFocus
// **********************************************************************************************
get hasActiveTrapsKeyboardFocus() {
return this.__hasActiveTrapsKeyboardFocus;
}
enableTrapsKeyboardFocus() {
if (this.__hasActiveTrapsKeyboardFocus === true) {
return;
}
if (this.manager) {
this.manager.disableTrapsKeyboardFocusForAll();
}
this._containFocusHandler = containFocus(this.contentNode);
this.__hasActiveTrapsKeyboardFocus = true;
if (this.manager) {
this.manager.informTrapsKeyboardFocusGotEnabled();
}
}
disableTrapsKeyboardFocus({ findNewTrap = true } = {}) {
if (this.__hasActiveTrapsKeyboardFocus === false) {
return;
}
this._containFocusHandler.disconnect();
this._containFocusHandler = undefined;
this.__hasActiveTrapsKeyboardFocus = false;
if (this.manager) {
this.manager.informTrapsKeyboardFocusGotDisabled({ disabledCtrl: this, findNewTrap });
}
}
// **********************************************************************************************
// FEATURE - hideOnEsc
// **********************************************************************************************
get hasActiveHidesOnEsc() {
return this.__hasActiveHidesOnEsc;
}
enableHidesOnEsc() {
if (this.__hasHidesOnEsc === true) {
return;
}
this.__escKeyHandler = ev => {
if (ev.key === 'Escape') {
this.hide();
}
};
this.contentNode.addEventListener('keyup', this.__escKeyHandler);
this.__hasActiveHidesOnEsc = true;
}
disableHidesOnEsc() {
if (this.__hasHidesOnEsc === false) {
return;
}
if (this.contentNode) {
this.contentNode.removeEventListener('keyup', this.__escKeyHandler);
}
this.__hasActiveHidesOnEsc = false;
}
}