(function() {
/**
* @typedef {Help4.control2.container.Container.Params} Help4.widget.tourlist.View.Params
* @property {Help4.widget.tourlist.Widget} widget - the owner widget
*/
/**
* List view for tourlist widget.
* @augments Help4.control2.container.Container
* @property {Help4.widget.tourlist.Widget} __widget
* @property {Function} _domRefreshExecutor
* @property {Help4.jscore.MutualExclusion} _mutual
*/
Help4.widget.tourlist.View = class extends Help4.control2.container.Container {
/**
* @override
* @param {Help4.widget.tourlist.View.Params} params
*/
constructor(params) {
const {/** @type {Help4.widget.tourlist.Widget} */ widget} = params;
Help4.widget.companionCore.Core.addStandardViewParameters(widget, params, 'contentDiv');
const T = Help4.jscore.ControlBase.TYPES;
super(params, {
params: {
widget: {type: T.instance, mandatory: true, private: true, readonly: true},
visible: {init: false},
role: {init: 'list'}
},
statics: {
_domRefreshExecutor: {init: () => this.update(), destroy: false},
_mutual: {init: new Help4.jscore.MutualExclusion()}
},
config: {
css: 'widget-tourlist-view'
}
});
}
/**
* drop all unnecessary parameters from the tile control
* and convert to a 1-level object for easier comparison
* input is from {@link _updateStructuralCreate} or from {@link _updateStructuralExtract}
* @param {Help4.widget.tourlist.TileParams|Object} data
* @returns {Object}
*/
static updateStructuralUnify(data) {
const {projectId, catalogueKey, catalogueType, dataType} = data._metadata;
return {projectId, catalogueKey, catalogueType, dataType};
}
/**
* drop all unnecessary parameters from the tile control
* and convert to a 1-level object for easier comparison
* input is from {@link _updateStructuralCreate} or from {@link _updateStructuralExtract}
* @param {Help4.widget.tourlist.TileParams|Object} data
* @returns {Object}
*/
static updateVisualUnify(data) {
const {caption, hidden, status} = data;
return {caption, hidden, status};
}
/**
* update my view: change control properties
* input is from {@link _updateStructuralCreate}
* @param {Help4.widget.tourlist.View|Help4.widget.whatsnew.view.TileView} view
* @param {Help4.widget.tourlist.TileParams} tile
* @param {number} index
*/
static updateVisualUpdate(view, {caption, hidden, status}, index) {
// existing tiles and new tiles are in same order
const control = view._store[index];
if (!control) return;
control.caption = caption;
control.hidden = hidden;
control.status = status;
}
/**
* @override
* @returns {Help4.widget.tourlist.View}
*/
focus() {
this.count() > 0
? this.get(0).focus()
: this.focus();
return this;
}
/**
* focus handling - up/down arrow keys
* @param {string} direction
*/
focusListItem(direction) {
let count = this.count();
if (count > 0) {
const visibleControls = [];
this.forEach(control => control.visible && visibleControls.push(control));
const focussedElement = Help4.widget.getActiveElement();
let index = visibleControls.findIndex(control => control.getDom() === focussedElement);
if (index >= 0) visibleControls[direction === 'down' ? ++index : --index]?.focus();
}
}
/**
* updates the tour list
* @param {boolean} [isRefresh = true]
* @returns {Promise<boolean>}
*/
async update(isRefresh = true) {
/** see {@link Help4.jscore.MutualExclusion} for more information */
const {_mutual} = this;
const mutualToken = _mutual.start();
_mutual.access(mutualToken); // immediately access; to avoid double condition evaluation in create()
const {View} = Help4.widget.tourlist;
return await Help4.widget.companionCore.Core.updateTileListView({
isDestroyed: () => this.isDestroyed(),
isRefresh,
structural: {
create: () => this._updateStructuralCreate(mutualToken),
extract: () => this._updateStructuralExtract(),
unify: data => View.updateStructuralUnify(data),
update: {mutual: _mutual, mutualToken, container: this}
},
visual: {
unify: data => View.updateVisualUnify(data),
update: (tile, index) => View.updateVisualUpdate(this, tile, index)
}
});
}
/** @override */
_onBeforeDestroy() {
super._onBeforeDestroy();
const {_domRefreshExecutor, __widget} = this;
Help4.widget.companionCore.Core.disconnectDomRefresh(_domRefreshExecutor, __widget.getContext());
}
/**
* @override
* @param {HTMLElement} dom
* @returns {Promise<void>}
*/
async _onDomCreated(dom) {
const {/** @type {Help4.widget.tourlist.Widget} */ __widget} = this;
// do not fill list container immediately as system is still booting and might not be ready
// especially for condition element evaluation; wait a bit to let the boot finish
await Help4.widget.companionCore.Core.waitControllerPlaybackServiceReady(__widget, 100);
if (!this.isDestroyed()) {
// observe tile clicks for tour starts
this.addListener(['click', 'enter', 'space'], async ({target: [container, tile] = []}) => {
const {
/** @type {string} */ projectId,
/** @type {Help4.widget.help.CatalogueKeys} */ catalogueKey,
/** @type {Help4.widget.help.CatalogueTypes} */ catalogueType,
/** @type {Help4.widget.help.DataTypes} */ dataType
} = tile.getMetadata('projectId', 'catalogueKey', 'catalogueType', 'dataType');
this._fireEvent({type: 'startTour', tour: {projectId, catalogueKey, catalogueType, dataType}});
});
// add tiles
await this.update(false);
// observe dom refresh
const context = __widget.getContext();
Help4.widget.companionCore.Core.observeDomRefresh(this._domRefreshExecutor, context, this);
}
}
/**
* create new tile information
* @protected
* @param {number} mutualToken
* @returns {Promise<?Help4.widget.tourlist.TileParams[]>}
*/
async _updateStructuralCreate(mutualToken) {
const {Tour} = Help4.widget.companionCore.data;
const {_mutual} = this;
const sortedProjects = await Tour.getFilteredTourProjects();
if (this.isDestroyed() || !_mutual.access(mutualToken)) return null;
return Tour.tourProjectsToTileParams(sortedProjects);
}
/**
* extract current tile information
* @protected
* @returns {Object[]}
*/
_updateStructuralExtract() {
return this.map(control => control.dataFunctions.toObject());
}
}
})();