goog.module('gep.components.AbstractLayer');
const {Component} = goog.require('clulib.cm');
const ResizeProvider = goog.require('nbsrc.provider.ResizeProvider');
const {LayerProvider,LayerModel,LayerEvent,LayerEventType} = goog.require('gep.provider.LayerProvider');
const AutoResetProvider = goog.require('gep.provider.AutoResetProvider');
const CursorProvider = goog.require('gep.provider.CursorProvider');
const {listen,unlisten,Event,EventType} = goog.require('goog.events');
const classlist = goog.require('goog.dom.classlist');
const dataset = goog.require('goog.dom.dataset');
/**
* Abstract class for all layer components. Provides methods and variables that are valid for all layers, such as open, close, update, change content, etc.
* Component is used from {@Link https://www.npmjs.com/package/clulib}
* @extends {Component}
*/
class AbstractLayer extends Component
{
/**
* @param {string} type Use {@Link LayerType}
*/
constructor(type)
{
super();
/**
* Defines which type this layer component is. Use {@Link LayerType}.
* @type {string}
* @protected
*/
this.type_ = type;
/**
* Reference to ResizeProvider for monitoring device size changes
* @type {ResizeProvider}
* @protected
*/
this.resizeProvider_ = ResizeProvider.getInstance();
/**
* Reference to LayerProvider for monitoring and controlling layer appearance and disappearance.
* @type {LayerProvider}
* @protected
*/
this.layerProvider_ = LayerProvider.getInstance();
/**
* Reference to AutoResetProvider to monitor and control the automatic reset of the page when the user is inactive for a specified period of time.
* @type {AutoResetProvider}
* @protected
*/
this.autoResetProvider_ = AutoResetProvider.getInstance();
/**
* Reference to CursorProvider for monitoring and controlling the visibility of the custom mouse cursor object.
* @type {CursorProvider}
* @protected
*/
this.cursorProvider_ = CursorProvider.getInstance();
/**
* Layer model which is passed by the {@Link LayerEvent} from the {@Link LayerProvider} to this component.
* @type {LayerModel}
* @protected
*/
this.layerModel_ = null;
/**
* Data object which holds the values passed by the {@Link LayerEvent} from the {@Link LayerProvider} to this component.
* @type {*}
* @protected
*/
this.layerData_ = null;
/**
* List of all layer content elements
* @type {NodeList}
* @protected
*/
this.contents_ = null;
/**
* Active layer content id. All layer content dom elements (class `layer-content`) have to include the attribute `data-content=id`.
* @type {string|null}
* @protected
*/
this.activeContentId_ = null;
/**
* Active layer content element
* @type {Element}
* @protected
*/
this.content_ = null;
/**
* List of dom elements defined by the query selector `button.layer-content-switcher`.
* @type {NodeList}
* @protected
*/
this.contentSwitchers_ = null;
/**
* Dom element of the close button
* @type {NodeList}
* @protected
*/
this.closeButtons_ = null;
}
/**
* Component is ready and had loaded all dependencies (inherit method waitFor and sub components).
* Init the main listeners for monitoring layer state and window size changes.
* @inheritDoc
*/
onInit()
{
super.onInit();
this.initContentElements_();
listen(this.layerProvider_, LayerEventType.SHOW_LAYER, this.handleShowLayer_, false, this);
listen(this.layerProvider_, LayerEventType.UPDATE_LAYER, this.handleUpdateLayer_, false, this);
listen(this.layerProvider_, LayerEventType.HIDE_LAYER, this.handleHideLayer_, false, this);
listen(this.resizeProvider_, EventType.RESIZE, this.handleResize_, false, this);
}
/**
* Builds/finds in the dom of the component the layer contents and selects/displays the active content.
* @protected
*/
initContentElements_()
{
this.contents_ = /** @type {NodeList} */ (this.getElement().querySelectorAll('.layer-content'));
this.activeContentId_ = dataset.has(this.getElement(), 'content') ? dataset.get(this.getElement(), 'content') : this.contents_.length > 0 && dataset.has(this.contents_[0], 'content') ? dataset.get(this.contents_[0], 'content') : null;
this.content_ = this.activeContentId_ ? this.getElement().querySelector('.layer-content[data-content="'+this.activeContentId_+'"]') : this.contents_[0];
this.contentSwitchers_ = /** @type {NodeList} */ (this.getElement().querySelectorAll('button.layer-content-switcher'));
this.closeButtons_ = /** @type {NodeList} */ (this.getElement().querySelectorAll('.layer-close'));
}
/**
* Handle layer close click
* @param {Event} event
* @protected
*/
handleCloseClick_(event)
{
this.layerProvider_.hide(this.layerModel_.name);
this.autoResetProvider_.start();
}
/**
* Hides and deactivates the layer by the {@Link LayerEvent} `SHOW_LAYER` from the {@Link LayerProvider}.
* Initializes dynamic content creation if the content does not already represent the desired content.
* @param {LayerEvent} event
* @protected
*/
handleShowLayer_(event)
{
if(event.layer.type != this.type_)
return;
if(this.checkLayerContentInitalisation_(event))
{
if(this.layerModel_)
{
this.deactivate_();
this.deactivateContent_();
}
this.initContent_(event.layer, event.data).then(() => {
this.activate_();
this.activateContent_();
});
}
else
{
this.activate_();
this.activateContent_();
}
setTimeout(() => {
classlist.add(this.getElement(), 'is-visible');
}, 0);
}
/**
* Takes care of changes triggered by the {@Link LayerEventType} `UPDATE_LAYER` event of the {@Link LayerProvider}.
* @param {LayerEvent} event
* @protected
*/
handleUpdateLayer_(event)
{
}
/**
* Checks whether a content change and thus an update of
* the content should take place when the layer is called up again.
* @param {LayerEvent} event
* @protected
*/
checkLayerContentInitalisation_(event)
{
return !(this.layerModel_ && this.layerModel_.name == event.layer.name)
}
/**
* Hides and deactivates the layer by the {@Link LayerEvent} `HIDE_LAYER` from the {@Link LayerProvider}.
* @param {LayerEvent} event
* @protected
*/
handleHideLayer_(event)
{
if(event.layer && event.layer.type != this.type_)
return;
this.deactivate_();
this.deactivateContent_();
classlist.remove(this.getElement(), 'is-visible');
}
/**
* Init a switch of the layer content by layer navigation elements.
* @param {LayerModel} layer
* @param {*=} data
* @return {Promise}
* @protected
*/
initContent_(layer, data = null)
{
this.layerModel_ = layer;
this.layerData_ = data;
return Promise.resolve();
}
/**
*
* @param {Event} event
* @protected
*/
handleContentSwitch_(event)
{
let contentId = dataset.get(/** @type {Element} */ (event.currentTarget), 'content');
if(contentId)
this.switchContent_(contentId);
}
/**
* Switches the display of the current layer content element.
* Id is assigned to a dom element width class `layer-content` and with the attribute `data-content=id`.
* @param {string} id
* @protected
*/
switchContent_(id)
{
if(id == this.activeContentId_)
return;
let newContent = this.getElement().querySelector('.layer-content[data-content="'+id+'"]');
if(!newContent)
throw new Error('Couldn\'t init layer with content id "'+id+'".');
this.deactivateContent_();
classlist.add(newContent, 'is-visible');
classlist.remove(this.content_, 'is-visible');
this.content_ = newContent;
this.activeContentId_ = id;
this.autoResetProvider_.start();
this.activateContent_();
}
/**
* Activates all interactive elements in the layer.
* Enable all known buttons (defined by the query selector `button.layer-content-switcher`) that trigger a content switch.
* @protected
*/
activate_()
{
setTimeout(() => {classlist.add(this.getElement(), 'can-animate-contents');}, 100);
if(this.contentSwitchers_)
{
for(let i=0; i<this.contentSwitchers_.length; i++)
{
listen(this.contentSwitchers_[i], EventType.CLICK, this.handleContentSwitch_, false, this);
}
}
if(this.closeButtons_)
{
for(let i=0; i<this.closeButtons_.length; i++)
{
listen(this.closeButtons_[i], EventType.CLICK, this.handleCloseClick_, false, this);
}
}
}
/**
* Dectivates all interactive elements in the layer.
* @protected
*/
deactivate_()
{
classlist.remove(this.getElement(), 'can-animate-contents');
if(this.contentSwitchers_)
{
for(let i=0; i<this.contentSwitchers_.length; i++)
{
unlisten(this.contentSwitchers_[i], EventType.CLICK, this.handleContentSwitch_, false, this);
}
}
if(this.closeButtons_)
{
for(let i=0; i<this.closeButtons_.length; i++)
{
unlisten(this.closeButtons_[i], EventType.CLICK, this.handleCloseClick_, false, this);
}
}
}
/**
* Activates all interactive elements in the active layer content.
* @protected
*/
activateContent_()
{
this.cursorProvider_.update();
this.autoResetProvider_.start();
}
/**
* Dectivates all interactive elements in the active layer content.
* @protected
*/
deactivateContent_()
{
}
/**
* Handle window resize
* @protected
*/
handleResize_()
{
}
}
exports = AbstractLayer;