UNPKG

tablefilter

Version:

A Javascript library making HTML tables filterable and a bit more

341 lines (291 loc) • 10.1 kB
import {Feature} from '../feature'; import {root} from '../root'; import {createElm, createText, elm, removeElm} from '../dom'; import {EMPTY_FN, isNull} from '../types'; import {defaultsStr, defaultsFn} from '../settings'; import {LEFT} from './toolbar'; const EVENTS = [ 'after-filtering', 'after-populating-filter', 'after-page-change', 'after-clearing-filters', 'after-page-length-change', 'after-reset-page', 'after-reset-page-length', 'after-loading-extensions', 'after-loading-themes' ]; /** * Status bar UI component * @export * @class StatusBar * @extends {Feature} */ export class StatusBar extends Feature { /** * Creates an instance of StatusBar * @param {TableFilter} tf TableFilter instance */ constructor(tf) { super(tf, StatusBar); // Configuration object let f = this.config.status_bar || {}; /** * ID of custom container element * @type {String} */ this.targetId = defaultsStr(f.target_id, null); /** * Container DOM element * @type {DOMElement} * @private */ this.container = null; /** * Message container DOM element * @type {DOMElement} * @private */ this.msgContainer = null; /** * Label container DOM element * @type {DOMElement} * @private */ this.labelContainer = null; /** * Text preceding status message * @type {String} */ this.text = defaultsStr(f.text, ''); /** * Css class for container element * @type {String} */ this.cssClass = defaultsStr(f.css_class, 'status'); /** * Message visibility duration in milliseconds * @type {Number} * @private */ this.delay = 250; /** * Callback fired before the message is displayed * @type {Function} */ this.onBeforeShowMsg = defaultsFn(f.on_before_show_msg, EMPTY_FN); /** * Callback fired after the message is displayed * @type {Function} */ this.onAfterShowMsg = defaultsFn(f.on_after_show_msg, EMPTY_FN); /** * Message appearing upon filtering * @type {String} */ this.msgFilter = defaultsStr(f.msg_filter, 'Filtering data...'); /** * Message appearing when a drop-down filter is populated * @type {String} */ this.msgPopulate = defaultsStr(f.msg_populate, 'Populating filter...'); /** * Message appearing when a checklist filter is populated * @type {String} */ this.msgPopulateCheckList = defaultsStr(f.msg_populate_checklist, 'Populating list...'); /** * Message appearing when a pagination page is changed * @type {String} */ this.msgChangePage = defaultsStr(f.msg_change_page, 'Collecting paging data...'); /** * Message appearing when filters are cleared * @type {String} */ this.msgClear = defaultsStr(f.msg_clear, 'Clearing filters...'); /** * Message appearing when the page length is changed * @type {String} */ this.msgChangeResults = defaultsStr(f.msg_change_results, 'Changing results per page...'); /** * Message appearing when the page is re-set * @type {String} */ this.msgResetPage = defaultsStr(f.msg_reset_page, 'Re-setting page...'); /** * Message appearing when the page length is re-set * @type {String} */ this.msgResetPageLength = defaultsStr(f.msg_reset_page_length, 'Re-setting page length...'); /** * Message appearing upon column sorting * @type {String} */ this.msgSort = defaultsStr(f.msg_sort, 'Sorting data...'); /** * Message appearing when extensions are loading * @type {String} */ this.msgLoadExtensions = defaultsStr(f.msg_load_extensions, 'Loading extensions...'); /** * Message appearing when themes are loading * @type {String} */ this.msgLoadThemes = defaultsStr(f.msg_load_themes, 'Loading theme(s)...'); /** * Default position in toolbar ('left'|'center'|'right') * @type {String} */ this.toolbarPosition = defaultsStr(f.toolbar_position, LEFT); } /** * Initializes StatusBar instance */ init() { if (this.initialized) { return; } let tf = this.tf; let emitter = this.emitter; emitter.emit('initializing-feature', this, !isNull(this.targetId)); // Bridge to <tf-status> (scaffold #905, live region #906). // If the custom element is registered (Vite / module load) use it; // otherwise fall back to the legacy div/span structure (#908). // TODO(#965): restore static import once namespace migration lands. // init/initLegacy are split so the legacy path is a clean deletion // boundary when #965 lands. const targetEl = (!this.targetId) ? tf.feature('toolbar').container(this.toolbarPosition) : elm(this.targetId); if (typeof customElements !== 'undefined' && customElements.get('tf-status')) { this.initCE(targetEl); } else { this.initLegacy(targetEl); } // subscribe to events emitter.on(['before-filtering'], () => this.message(this.msgFilter)); emitter.on(['before-populating-filter'], () => this.message(this.msgPopulate)); emitter.on(['before-page-change'], () => this.message(this.msgChangePage)); emitter.on(['before-clearing-filters'], () => this.message(this.msgClear)); emitter.on(['before-page-length-change'], () => this.message(this.msgChangeResults)); emitter.on(['before-reset-page'], () => this.message(this.msgResetPage)); emitter.on(['before-reset-page-length'], () => this.message(this.msgResetPageLength)); emitter.on(['before-loading-extensions'], () => this.message(this.msgLoadExtensions)); emitter.on(['before-loading-themes'], () => this.message(this.msgLoadThemes)); emitter.on(EVENTS, () => this.message('')); /** @inherited */ this.initialized = true; emitter.emit('feature-initialized', this); } /** * Initialize using the <tf-status> custom element * @param {DOMElement} targetEl Target container element * @private */ initCE(targetEl) { const statusEl = document.createElement('tf-status'); statusEl.className = this.cssClass; if (this.text) { statusEl.setAttribute('text', this.text); } targetEl.appendChild(statusEl); this.container = statusEl; this.statusEl = statusEl; this.msgContainer = statusEl.shadowRoot .querySelector('[part=message]'); this.labelContainer = statusEl.shadowRoot .querySelector('[part=label]'); } /** * Initialize using the legacy div/span structure * @param {DOMElement} targetEl Target container element * @private */ initLegacy(targetEl) { const statusDiv = createElm('div'); statusDiv.className = this.cssClass; const msgSpan = createElm('span'); const labelSpan = createElm('span'); labelSpan.appendChild(createText(this.text)); statusDiv.appendChild(labelSpan); statusDiv.appendChild(msgSpan); targetEl.appendChild(statusDiv); this.container = statusDiv; this.statusEl = statusDiv; this.msgContainer = msgSpan; this.labelContainer = labelSpan; } /** * Display status message * @param {String} [t=''] Message to be displayed */ message(t = '') { if (!this.isEnabled()) { return; } this.onBeforeShowMsg(this.tf, t); let d = t === '' ? this.delay : 1; root.setTimeout(() => { if (!this.initialized) { return; } if (this.statusEl.setMessage) { this.statusEl.setMessage(t); } else { this.msgContainer.innerHTML = t; } this.onAfterShowMsg(this.tf, t); }, d); } /** * Destroy StatusBar instance */ destroy() { if (!this.initialized) { return; } let emitter = this.emitter; removeElm(this.container); this.statusEl = null; this.msgContainer = null; this.labelContainer = null; this.container = null; // Unsubscribe to events emitter.off(['before-filtering'], () => this.message(this.msgFilter)); emitter.off(['before-populating-filter'], () => this.message(this.msgPopulate)); emitter.off(['before-page-change'], () => this.message(this.msgChangePage)); emitter.off(['before-clearing-filters'], () => this.message(this.msgClear)); emitter.off(['before-page-length-change'], () => this.message(this.msgChangeResults)); emitter.off(['before-reset-page'], () => this.message(this.msgResetPage)); emitter.off(['before-reset-page-length'], () => this.message(this.msgResetPageLength)); emitter.off(['before-loading-extensions'], () => this.message(this.msgLoadExtensions)); emitter.off(['before-loading-themes'], () => this.message(this.msgLoadThemes)); emitter.off(EVENTS, () => this.message('')); this.initialized = false; } }