(function() {
/**
* @typedef {Help4.control2.container.Container.Params} Help4.control2.container.PanelWidgetContainer.Params
* @property {Object} panel - the panel control where this container lives in
*/
/**
* A container for widget tiles.
* @augments Help4.control2.container.Container
*/
Help4.control2.container.PanelWidgetContainer = class extends Help4.control2.container.SortedContainer {
/**
* @override
* @param {Help4.control2.container.PanelWidgetContainer.Params} [params]
*/
constructor(params) {
const panel = params.panel;
if (!panel) throw new Error('panel parameter is required!');
const T = Help4.jscore.ControlBase.TYPES;
super(params, {
params: {
type: {init: 'button.Button'},
panel: {type: T.instance, init: panel, mandatory: true, private: true}
},
statics: {
_widgetEngineObserver: {}
},
config: {
css: 'panel-widgets'
}
});
}
/**
* @override
* @returns {Help4.control2.container.PanelWidgetContainer}
*/
clean() {
const current = _getCurrentWidgetIds.call(this);
for (/** @type {Help4.widget.Widget} */ const widgetInstance of Help4.widget) {
const {id} = widgetInstance.getDescriptor();
current.indexOf(id) >= 0 && widgetInstance.registerPanel(null); // unregister panel
}
return super.clean();
}
/** @override */
_onReady() {
super._onReady();
this.addListener('click', event => {
const id = event.target[1]?.getMetadata('id'); // target: [<container>, <button>]
id && this._fireEvent({type: 'widget', id});
});
_startWidgetObservation.call(this);
}
}
/**
* @memberof Help4.control2.container.PanelWidgetContainer#
* @private
*/
function _startWidgetObservation() {
const {TYPES} = Help4.EventBus;
const eventBus = Help4.getController().getService('eventBus');
// get widgets that already run on startup of panel
for (/** @type {Help4.widget.Widget} */ const instance of Help4.widget) {
_updateWidgets.call(this, {type: TYPES.widgetStart, engine: instance, descriptor: instance.getDescriptor()});
}
// observe changes to mode engines
this._widgetEngineObserver = new Help4.observer.EventBusObserver(event => void _updateWidgets.call(this, event))
.observe(eventBus, {type: [TYPES.widgetStart, TYPES.widgetVisibility]});
}
/**
* @memberof Help4.control2.container.PanelWidgetContainer#
* @param {{type: string, engine: Help4.widget.Widget, descriptor: ?Object}} eventBusEvent
* @private
*/
function _updateWidgets({type, engine: widgetInstance, descriptor: widgetDescriptor} = {}) {
const {TYPES} = Help4.EventBus;
switch (type) {
case TYPES.widgetStart:
case TYPES.widgetVisibility:
widgetInstance.isStarted() && widgetInstance.isVisible()
? _addWidget.call(this, widgetInstance)
: _removeWidget.call(this, widgetInstance, widgetDescriptor);
break;
}
}
/**
* @memberof Help4.control2.container.PanelWidgetContainer#
* @returns {string[]}
* @private
*/
function _getCurrentWidgetIds() {
// get current widget IDs
const ids = [];
/** @type {Help4.control2.button.Button} */
for (const widgetButton of this) {
ids.push(widgetButton.getMetadata('id'));
}
return ids.sort();
}
/**
* @memberof Help4.control2.container.PanelWidgetContainer#
* @param {Help4.widget.Widget} widgetInstance
* @private
*/
function _addWidget(widgetInstance) {
widgetInstance.registerPanel(this.__panel);
_updateItemOrder.call(this);
const {id, tile} = /** @type {Help4.widget.Widget.Descriptor} */ widgetInstance.getDescriptor();
if (!tile) return; // some widgets have no tile, e.g. Help4.widget.tour
const {text, title, icon, color: css, visible = true} = tile;
if (this.get({byMetadata: {id}})) return; // already within panel
const {widget: appearance} = Help4.control2.button.APPEARANCES;
const role = 'listitem';
this.addSorted({_metadata: {id}, _order: id, text, title, icon, css, appearance, visible, role});
}
/**
* @memberof Help4.control2.container.PanelWidgetContainer#
* @param {Help4.widget.Widget} widgetInstance
* @param {Object} widgetDescriptor
* @private
*/
function _removeWidget(widgetInstance, widgetDescriptor) {
const {id} = widgetInstance?.getDescriptor() || widgetDescriptor || {};
id && this.remove({byMetadata: {id}});
_updateItemOrder.call(this);
}
/**
* @memberof Help4.control2.container.PanelWidgetContainer#
* @private
*/
function _updateItemOrder() {
const orderMap = {};
Help4.widget.forEach((/** @type {Help4.widget.Widget} */ widget) => {
const {id, tile: {position = -1} = {}} = widget.getDescriptor();
orderMap[position] ||= [];
orderMap[position].push(id);
});
const orderList = [];
const positions = Object.keys(orderMap).map(id => Number(id)).sort();
for (const position of positions) {
position >= 0 && orderList.push(...orderMap[position]); // skip unsorted ones
}
orderMap[-1] && orderList.push(...orderMap[-1]); // add unsorted ones to the end
this.itemOrder = orderList;
}
})();