Programmatic trellis layout

Use datamodel and muze api to programatically create trellis

main
run-button
run-button
reset-button
	const env = muze();
    const DataModel = muze.DataModel;
    const html = muze.Operators.html;
    const require = muze.utils.require;
    
    const formatter = (val) => {
        if (val > 1000000) {
            return `${(val / 1000000).toFixed(2)} M`
        } else if (val > 1000) {
            return `${(val / 1000).toFixed(2)} K`
        } return val.toFixed(2);
    }

    const rootData = new DataModel(data, schema);
    const canvases = [];

    const div = document.createElement('div')
    div.className = 'chart-header muze-header-cell';
    div.innerHTML = 'Charting the Great Twitter Bot Purge of 2018 (A Trellis Example)';
    document.getElementById('chart-container').appendChild(div);
    
	users.forEach((user, i) => {
    	const newDomNode = document.createElement('div')
        newDomNode.className = 'chart-div';
      	newDomNode.id =`chart${i + 1}`;
        document.getElementById('chart-container').appendChild(newDomNode);
        
        const canvas = env.canvas()
            .rows([[], ['followers']])
            .columns(['time'])
            .data(rootData.select(f => f.user.value === user))
            .width(250)
            .height(200)
            .transform({
                lastPoint: dt => {
                    const dataLength = dt.getData().data.length;
                    return dt.select((fields, i) => {
                        if (i === dataLength - 1) {
                            return true
                        } return false
                    })
                }
            })
            .layers([{
                mark: 'line',
                name: 'lineLayer'
            }, {
                mark: 'point',
                source: 'lastPoint'
            }, {
                mark: 'text',
                encoding: {
                    text: {
                        field: 'followers',
                        formatter: (val) => formatter(val)
                    },
                    color: {
                        value: () => "#858585"
                    }
                },
                encodingTransform: require('layers', ['lineLayer', () => {
                    return (points, layer, dep) => {
                        const width = layer.measurement().width;
                        const height = layer.measurement().height;
                        const smartlabel = dep.smartLabel;

                        return points.map(point => {

                            const size = smartlabel.getOriSize(point.text);
                            if (point.update.y + size.height > height) {
                                point.update.y -= size.height / 2;
                            } else {
                                point.update.y += size.height / 2;
                            }
                            if (point.update.x + size.width / 2 > width) {
                                point.update.x -= size.width / 2 + 1;
                            }
                            return point;
                        })
                    }
                }]),
                source: 'lastPoint'
            }])
            .config({
                border: {
                    showValueBorders: {
                        right: false,
                        bottom: false
                    }
                },
                gridLines: {
                    y: {
                        show: false
                    }
                },
                axes: {
                    y: {
                        tickFormat: (val, j, labels) => {
                            if (j === 0 || j === labels.length - 1) {
                                return formatter(val);
                            } return '';
                        },
                        showAxisName: false
                    },
                    x: {
                        show: false
                    }
                }
            })
            .subtitle(html`<a href = "https://www.twitter.com/@${user}" class= "twitter-link">@${user}</a>`, { position: 'bottom', align: 'center' })
            .mount(`#chart${i + 1}`);
        canvases.push(canvas);
    });