2023-08-29 08:50:55 +00:00
|
|
|
/**
|
|
|
|
* @typedef {Object} TypeaheadItemGroupData
|
|
|
|
* @property {string} id
|
|
|
|
* @property {string} label
|
|
|
|
* @property {TypeaheadItemData[]} items
|
|
|
|
*/
|
|
|
|
|
2023-08-12 18:11:06 +00:00
|
|
|
/**
|
|
|
|
* @typedef {Object} TypeaheadItemData
|
|
|
|
* @property {string} id
|
|
|
|
* @property {string} type
|
|
|
|
* @property {string} link
|
|
|
|
* @property {string} icon
|
|
|
|
* @property {string} thumbnail
|
|
|
|
* @property {string} title
|
|
|
|
* @property {string} label
|
|
|
|
* @property {string} desc
|
|
|
|
*/
|
|
|
|
|
2023-08-29 02:20:44 +00:00
|
|
|
function htmlHelper() {
|
2023-08-12 18:11:06 +00:00
|
|
|
return {
|
2023-08-29 08:50:55 +00:00
|
|
|
/**
|
|
|
|
* Return the HTML of an item group
|
|
|
|
*
|
|
|
|
* @param {TypeaheadItemGroupData} data
|
|
|
|
* @return {HTMLElement|void}
|
|
|
|
*/
|
|
|
|
getItemGroupElement: function ( data ) {
|
|
|
|
const itemGroup = document.createElement( 'li' );
|
|
|
|
itemGroup.classList.add( 'citizen-typeahead-item-group' );
|
|
|
|
itemGroup.setAttribute( 'data-group', `${data.id}` );
|
|
|
|
itemGroup.setAttribute( 'role', 'presentation' );
|
|
|
|
|
|
|
|
if ( data.label ) {
|
|
|
|
const label = document.createElement( 'span' );
|
|
|
|
label.classList.add( 'citizen-typeahead-item-group-label' );
|
|
|
|
label.innerText = data.label;
|
|
|
|
itemGroup.append( label );
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( data.items ) {
|
|
|
|
const list = document.createElement( 'ol' );
|
|
|
|
list.classList.add( 'citizen-typeahead-item-group-list' );
|
|
|
|
list.setAttribute( 'role', 'presentation' );
|
|
|
|
data.items.forEach( ( itemData, index ) => {
|
|
|
|
itemData.id = `citizen-typeahead-${data.id}-${index}`;
|
|
|
|
const item = this.getItemElement( itemData );
|
|
|
|
list.append( item );
|
|
|
|
} );
|
|
|
|
itemGroup.append( list );
|
|
|
|
}
|
|
|
|
|
|
|
|
return itemGroup;
|
|
|
|
},
|
2023-08-12 18:11:06 +00:00
|
|
|
/**
|
|
|
|
* Generate menu item HTML using the existing template
|
|
|
|
*
|
|
|
|
* @param {TypeaheadItemData} data
|
|
|
|
* @return {HTMLElement|void}
|
|
|
|
*/
|
2023-08-29 02:20:44 +00:00
|
|
|
getItemElement: function ( data ) {
|
2023-08-12 18:11:06 +00:00
|
|
|
// TODO: Should we use template element or Mustache or just Javascript?
|
|
|
|
const template = document.getElementById( 'citizen-typeahead-template' );
|
|
|
|
|
|
|
|
// Shouldn't happen but just to be safe
|
|
|
|
if ( !( template instanceof HTMLTemplateElement ) ) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
const
|
|
|
|
fragment = template.content.cloneNode( true ),
|
|
|
|
item = fragment.querySelector( '.citizen-typeahead__item' );
|
|
|
|
|
2023-08-29 02:20:44 +00:00
|
|
|
this.updateItemElement( item, data );
|
2023-08-12 18:11:06 +00:00
|
|
|
return fragment;
|
|
|
|
},
|
|
|
|
/**
|
|
|
|
* Update typeahead item element
|
|
|
|
*
|
|
|
|
* @param {HTMLElement} item
|
|
|
|
* @param {TypeaheadItemData} data
|
|
|
|
*/
|
2023-08-29 02:20:44 +00:00
|
|
|
updateItemElement: function ( item, data ) {
|
2023-08-12 18:11:06 +00:00
|
|
|
if ( data.id ) {
|
|
|
|
item.setAttribute( 'id', data.id );
|
|
|
|
}
|
|
|
|
if ( data.type ) {
|
|
|
|
item.classList.add( `citizen-typeahead__item-${data.type}` );
|
2023-08-27 07:50:10 +00:00
|
|
|
|
|
|
|
if ( data.type !== 'placeholder' ) {
|
|
|
|
item.setAttribute( 'role', 'option' );
|
|
|
|
}
|
2023-08-12 18:11:06 +00:00
|
|
|
}
|
2023-08-29 08:50:55 +00:00
|
|
|
|
2023-08-12 18:11:06 +00:00
|
|
|
if ( data.size ) {
|
|
|
|
item.classList.add( `citizen-typeahead__item-${data.size}` );
|
|
|
|
}
|
|
|
|
if ( data.link ) {
|
2023-08-12 18:16:29 +00:00
|
|
|
item.querySelector( '.citizen-typeahead__content' ).setAttribute( 'href', data.link );
|
2023-08-12 18:11:06 +00:00
|
|
|
}
|
|
|
|
if ( data.icon || data.thumbnail ) {
|
|
|
|
const thumbnail = item.querySelector( '.citizen-typeahead__thumbnail' );
|
|
|
|
if ( data.thumbnail ) {
|
|
|
|
thumbnail.style.backgroundImage = `url('${data.thumbnail}')`;
|
|
|
|
} else {
|
|
|
|
thumbnail.classList.add(
|
|
|
|
'citizen-typeahead__thumbnail',
|
|
|
|
'citizen-ui-icon',
|
|
|
|
`mw-ui-icon-wikimedia-${data.icon}`
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if ( data.title ) {
|
2023-08-12 18:16:29 +00:00
|
|
|
item.querySelector( '.citizen-typeahead__title' ).innerHTML = data.title;
|
2023-08-12 18:11:06 +00:00
|
|
|
}
|
|
|
|
if ( data.label ) {
|
2023-08-12 18:16:29 +00:00
|
|
|
item.querySelector( '.citizen-typeahead__label' ).innerHTML = data.label;
|
2023-08-12 18:11:06 +00:00
|
|
|
}
|
|
|
|
if ( data.desc ) {
|
2023-08-12 18:16:29 +00:00
|
|
|
item.querySelector( '.citizen-typeahead__description' ).innerHTML = data.desc;
|
2023-08-12 18:11:06 +00:00
|
|
|
}
|
2023-08-29 08:50:55 +00:00
|
|
|
},
|
|
|
|
/**
|
|
|
|
* Remove item group from the typeahead DOM
|
|
|
|
*
|
|
|
|
* @param {HTMLElement} typeaheadEl
|
|
|
|
* @param {string} id
|
|
|
|
*/
|
|
|
|
removeItemGroup: function ( typeaheadEl, id ) {
|
|
|
|
typeaheadEl.querySelector( `.citizen-typeahead-item-group[data-group="${id}"]` )?.remove();
|
2023-08-12 18:11:06 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2023-08-29 02:20:44 +00:00
|
|
|
/** @module htmlHelper */
|
|
|
|
module.exports = htmlHelper;
|