Home Reference Source

src/modules/noResults.js

import {Feature} from '../feature';
import {createElm, elm, removeElm} from '../dom';
import {isEmpty, EMPTY_FN} from '../types';
import {NONE} from '../const';
import {defaultsStr, defaultsFn} from '../settings';

/**
 * UI when filtering yields no matches
 * @export
 * @class NoResults
 * @extends {Feature}
 */
export class NoResults extends Feature {

    /**
     * Creates an instance of NoResults
     * @param {TableFilter} tf TableFilter instance
     */
    constructor(tf) {
        super(tf, 'noResults');

        //configuration object
        let f = this.config.no_results_message || {};

        /**
         * Text (accepts HTML)
         * @type {String}
         */
        this.content = defaultsStr(f.content, 'No results');

        /**
         * Custom container DOM element
         * @type {DOMElement}
         */
        this.customContainer = defaultsStr(f.custom_container, null);

        /**
         * ID of custom container element
         * @type {String}
         */
        this.customContainerId = defaultsStr(f.custom_container_id, null);

        /**
         * Indicates if UI is contained in a external element
         * @type {Boolean}
         * @private
         */
        this.isExternal = !isEmpty(this.customContainer) ||
            !isEmpty(this.customContainerId);

        /**
         * Css class assigned to container element
         * @type {String}
         */
        this.cssClass = defaultsStr(f.css_class, 'no-results');

        /**
         * Stores container DOM element
         * @type {DOMElement}
         */
        this.cont = null;

        /**
         * Callback fired before the message is displayed
         * @type {Function}
         */
        this.onBeforeShow = defaultsFn(f.on_before_show_msg, EMPTY_FN);

        /**
         * Callback fired after the message is displayed
         * @type {Function}
         */
        this.onAfterShow = defaultsFn(f.on_after_show_msg, EMPTY_FN);

        /**
         * Callback fired before the message is hidden
         * @type {Function}
         */
        this.onBeforeHide = defaultsFn(f.on_before_hide_msg, EMPTY_FN);

        /**
         * Callback fired after the message is hidden
         * @type {Function}
         */
        this.onAfterHide = defaultsFn(f.on_after_hide_msg, EMPTY_FN);
    }

    /**
     * Initializes NoResults instance
     */
    init() {
        if (this.initialized) {
            return;
        }
        let tf = this.tf;
        let target = this.customContainer || elm(this.customContainerId) ||
            tf.dom();

        //container
        let cont = createElm('div');
        cont.className = this.cssClass;
        cont.innerHTML = this.content;

        if (this.isExternal) {
            target.appendChild(cont);
        } else {
            target.parentNode.insertBefore(cont, target.nextSibling);
        }

        this.cont = cont;

        // subscribe to after-filtering event
        this.emitter.on(
            ['initialized', 'after-filtering'],
            () => this.toggle()
        );

        /** @inherited */
        this.initialized = true;
    }

    /**
     * Toggle no results message
     */
    toggle() {
        if (this.tf.getValidRowsNb() > 0) {
            this.hide();
        } else {
            this.show();
        }
    }

    /**
     * Show no results message
     */
    show() {
        if (!this.initialized || !this.isEnabled()) {
            return;
        }
        this.onBeforeShow(this.tf, this);

        this.setWidth();
        this.cont.style.display = 'block';

        this.onAfterShow(this.tf, this);
    }

    /**
     * Hide no results message
     */
    hide() {
        if (!this.initialized || !this.isEnabled()) {
            return;
        }
        this.onBeforeHide(this.tf, this);

        this.cont.style.display = NONE;

        this.onAfterHide(this.tf, this);
    }

    /**
     * Sets no results container width
     * @private
     */
    setWidth() {
        if (!this.initialized || this.isExternal || !this.isEnabled()) {
            return;
        }
        let tf = this.tf;
        if (tf.gridLayout) {
            let gridLayout = tf.feature('gridLayout');
            this.cont.style.width = gridLayout.headTbl.clientWidth + 'px';
        } else {
            this.cont.style.width = (tf.dom().tHead ?
                tf.dom().tHead.clientWidth :
                tf.dom().tBodies[0].clientWidth) + 'px';
        }
    }

    /** Remove feature */
    destroy() {
        if (!this.initialized) {
            return;
        }
        removeElm(this.cont);
        this.cont = null;
        // unsubscribe to after-filtering event
        this.emitter.off(['after-filtering'], () => this.toggle());

        this.initialized = false;
    }
}