Home Reference Source

src/modules/markActiveColumns.js

import {Feature} from '../feature';
import {addClass, removeClass, hasClass} from '../dom';
import {EMPTY_FN} from '../types';
import {defaultsStr, defaultsFn} from '../settings';

/**
 * Visual indicator for filtered columns
 * @export
 * @class MarkActiveColumns
 * @extends {Feature}
 */
export class MarkActiveColumns extends Feature {

    /**
     * Create an instance of MarkActiveColumns
     * @param {TableFilter} tf TableFilter instance
     */
    constructor(tf) {
        super(tf, MarkActiveColumns);

        let config = this.config.mark_active_columns || {};

        /**
         * Css class for filtered (active) columns
         * @type {String}
         */
        this.headerCssClass = defaultsStr(config.header_css_class,
            'activeHeader');

        /**
         * Css class for filtered (active) column cells
         * @type {String}
         */
        this.cellCssClass = defaultsStr(config.cell_css_class,
            'activeCell');

        /**
         * Enable/disable column highlighting
         * @type {Boolean}
         */
        this.highlightColumn = Boolean(config.highlight_column);

        /**
         * Callback fired before a column is marked as filtered
         * @type {Function}
         */
        this.onBeforeActiveColumn = defaultsFn(config.on_before_active_column,
            EMPTY_FN);

        /**
         * Callback fired after a column is marked as filtered
         * @type {Function}
         */
        this.onAfterActiveColumn = defaultsFn(config.on_after_active_column,
            EMPTY_FN);
    }

    /**
     * Initialise MarkActiveColumns instance
     */
    init() {
        if (this.initialized) {
            return;
        }

        this.emitter.on(['before-filtering'], () => this.clearActiveColumns());
        this.emitter.on(
            ['cell-processed'],
            (tf, colIndex) => this.markActiveColumn(colIndex)
        );

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

    /**
     * Clear filtered columns visual indicator (background color)
     */
    clearActiveColumns() {
        let tf = this.tf;
        tf.eachCol((idx) => {
            removeClass(tf.getHeaderElement(idx), this.headerCssClass);

            if (this.highlightColumn) {
                this.eachColumnCell(idx,
                    (cell) => removeClass(cell, this.cellCssClass));
            }
        });
    }

    /**
     * Mark currently filtered column
     * @param  {Number} colIndex Column index
     */
    markActiveColumn(colIndex) {
        let tf = this.tf;
        let header = tf.getHeaderElement(colIndex);
        if (hasClass(header, this.headerCssClass)) {
            return;
        }

        this.onBeforeActiveColumn(this, colIndex);

        addClass(header, this.headerCssClass);

        if (this.highlightColumn) {
            this.eachColumnCell(colIndex,
                (cell) => addClass(cell, this.cellCssClass));
        }

        this.onAfterActiveColumn(this, colIndex);
    }

    /**
     * Column cells iterator
     * TODO: make public and move into TableFilter if used elsewhere
     * @param {Number} colIndex
     * @param {Function} fn
     * @param {DOMElement} tbl
     * @private
     */
    eachColumnCell(colIndex, fn = EMPTY_FN, tbl = this.tf.dom()) {
        // TODO: remove [].forEach when polyfill for PhanthomJs is available
        [].forEach.call(
            tbl.querySelectorAll(`tbody td:nth-child(${colIndex + 1})`), fn);
    }

    /**
     * Remove feature
     */
    destroy() {
        if (!this.initialized) {
            return;
        }

        this.clearActiveColumns();
        this.emitter.off(['before-filtering'], () => this.clearActiveColumns());
        this.emitter.off(
            ['cell-processed'],
            (tf, colIndex) => this.markActiveColumn(colIndex)
        );

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