Source: muze-tooltip/src/content.js

import {
    selectElement,
    mergeRecursive,
    makeElement,
    setAttrs,
    setStyles,
    getSymbol,
    isSimpleObject
} from 'muze-utils';
import { DEFAULT_STRATEGY, strategy } from './strategy';
import { defaultConfig } from './default-config';

/**
 * This class is used to manage the content of tooltip.
 */
export default class Content {
    /**
     * Creates an instance of content.
     */
    constructor () {
        this._model = null;
        this._strategy = DEFAULT_STRATEGY;
        this._formatter = null;
        this._config = this.constructor.defaultConfig();
    }
   /**
     * Returns the default configuration of tooltip
     * @return {Object} Configuration of tooltip.
     */
    static defaultConfig () {
        const config = defaultConfig.content;
        config.classPrefix = defaultConfig.classPrefix;
        return config;
    }

    config (...c) {
        if (c.length > 0) {
            this._config = mergeRecursive(this._config, c[0]);
            return this;
        }
        return this._config;
    }

    /**
     * Update model. The format contains presentation strategy which determines how to show the content.
     * If no strategy is mentioned then default is to show key value pair
    */
    update (item) {
        this._model = item.model;
        this._strategy = item.strategy !== undefined ? item.strategy : DEFAULT_STRATEGY;
        this._formatter = item.formatter;
        return this;
    }

    context (ctx) {
        this._context = ctx;
        return this;
    }

    render (mount) {
        let data;
        const config = this._config;
        const iconContainerSize = config.iconContainerSize;
        const formatter = this._formatter;
        const rowMargin = config.rowMargin;
        const model = this._model;

        this._mount = mount;
        if (model instanceof Array) {
            data = model;
        } else {
            data = formatter instanceof Function ? formatter(this._model, this._context) :
                strategy[this._strategy](this._model, this.config(), this._context);
        }

        if (data instanceof Function) {
            mount.html(data());
        } else {
            let content = data;
            let displayFormat = 'default';

            if (isSimpleObject(data)) {
                content = data.content;
                displayFormat = data.displayFormat;
            }

            const body = makeElement(mount, 'div', [displayFormat], `${config.classPrefix}-tooltip-content`, {},
                d => d);

            if (displayFormat === 'table') {
                const table = makeElement(body, 'table', [1], `${config.classPrefix}-tooltip-table`);
                const tbody = makeElement(table, 'tbody', [1], `${config.classPrefix}-tooltip-table-tbody`);
                const rows = makeElement(tbody, 'tr', content, `${config.classPrefix}-tooltip-table-row`);
                rows.each(function (d, i) {
                    selectElement(this).classed(`${config.classPrefix}-tooltip-table-row-${i}`, true);
                });
                const cells = makeElement(rows, 'td', d => d, `${config.classPrefix}-tooltip-table-cell`);
                cells.each(function (d) {
                    selectElement(this).html(d);
                });
            } else {
                const rows = makeElement(body, 'div', content, `${config.classPrefix}-tooltip-row`);
                const cells = makeElement(rows, 'span', d => d, `${config.classPrefix}-tooltip-content`);
                cells.attr('class', `${config.classPrefix}-tooltip-content`);
                setStyles(rows, {
                    margin: rowMargin
                });
                setStyles(cells, {
                    display: 'inline-block',
                    'margin-right': `${config.spacing}px`
                });

                cells.each(function (d) {
                    const el = selectElement(this);
                    el.html('');
                    if (d instanceof Object) {
                        if (d.type === 'icon') {
                            const svg = makeElement(el, 'svg', [1]);
                            const path = makeElement(svg, 'path', [1]);
                            const shape = d.shape instanceof Function ? d.shape : getSymbol(d.shape);

                            setAttrs(svg, {
                                x: 0,
                                y: 0,
                                width: iconContainerSize,
                                height: iconContainerSize
                            });
                            setAttrs(path, {
                                d: shape.size(d.size)(),
                                transform: `translate(${iconContainerSize / 2}, ${iconContainerSize / 2})`
                            });
                            setStyles(path, {
                                fill: d.color
                            });
                            setStyles(svg, {
                                width: `${iconContainerSize}px`,
                                height: `${iconContainerSize}px`
                            });
                        } else {
                            el.html(d.value);
                            d.className && el.classed(d.className, true);
                            setStyles(el, d.style);
                        }
                    } else {
                        el.html(d);
                    }
                });
            }
        }
        return this;
    }

    clear () {
        this._model = null;
        return this;
    }
}