cms/app-client/app/components/discourses-component.js
author ymh <ymh.work@gmail.com>
Tue, 20 Mar 2018 15:02:40 +0100
changeset 573 25f3d28f51b2
parent 537 d2e6ee099125
permissions -rw-r--r--
Added tag 0.0.25 for changeset 190ae1dee68d

import Ember from 'ember';
import * as d3 from 'd3-selection';
import * as d3h from 'd3-hierarchy';
import * as d3s from 'd3-scale';
import _ from 'lodash';

const MINIMUM_CIRCLE_WIDTH = 60.0;

export default Ember.Component.extend({

    classNames: ['discourses-component'],

    constants: Ember.inject.service(),
    filter: Ember.inject.service(),
    utils: Ember.inject.service(),

    discourseObserver: Ember.observer('filter.discourse', function() {
        Ember.$('.item').removeClass("selected");
        if(this.get('filter').get('discourse') !== null) {
            this.get('filter').get('discourse').forEach(function(e) {
                Ember.$('.item[data-id="' + e + '"]').addClass("selected");
            });
        }

    }).on('init'),

    didRender: function() {
        var previousDiv = d3.selectAll(".discourses");
        var self = this;

        var discourses = this.get('discourses');
        var array = discourses.map(function (d) { return d.get('count'); });

        var width = self.$().parent().width();
        var height = self.$().parent().height();
        self.$().siblings().each(function(elem) {
          height -= Ember.$(elem).outerHeight(true);
        });

        //Determine the minimum circle width
        var longerStr = _.max(
          [].concat(...(discourses.map(function(d) { return d.get('label').split(' ');}))),
          function(s) { return s.length; }
        );
        var w = this.get('utils').getWidthOfText(longerStr, '11px');
        // we try to take into account the fact that there is at least 2 lines
        var minimum_circle_width = Math.max( Math.sqrt((w*w)+Math.pow(11+11/2, 2)) + 10, MINIMUM_CIRCLE_WIDTH);

        // to avoid division by zero. In any case it makes no sense to consider dimensions
        // under MINIMUM_CIRCLE_WIDTH
        var scaleFactor = minimum_circle_width/Math.max(minimum_circle_width, Math.min(width, height));

        var min = Math.min(...array),
            max = Math.max(...array);

        var scale = d3s.scaleLinear();
        // The range is the range for font sizes
        var fontScale = d3s.scaleQuantize().domain([min, max]).range(_.range(10, 14));

        if((min/max) < scaleFactor) {
          scale = scale.domain([min, max]).range([scaleFactor, 1]);
        }

        var bubble = d3h.pack()
            .size([width, height])
            .padding(10);

        var element = d3.select('#' + self.get('elementId'))
          .append('div')
          .attr('class','discourses')
          .style('position','absolute')
          .style('left', -100000);

        var root = d3h.hierarchy(self.createNodes())
          .sum(function(d) {return scale(d.value);});

        var bubbles = bubble(root).descendants();
        var nodes = element
            .selectAll()
            .data(bubbles);

        nodes.enter().append("div")
            .attr("class", function(d) { return ( d.children ? "category": "item" ) + ( (self.get('filter').get('discourse') !== null && _.includes(self.get('filter').get('discourse'), d.id)) ? " selected" : "" ) ; });

        var item = element.selectAll(".item")
            .attr("data-id", function(d) { return d.data.id; })
            .style("left", function(d) { return ( d.x - d.r)  + "px"; })
            .style("top", function(d) { return ( d.y - d.r)  + "px"; })
            .style("width", function(d) { return (d.r * 2) + "px"; })
            .style("height", function(d) { return (d.r * 2) + "px"; })
            .style("background-color", function(d) { return d.data.fill; })
            .style("border-color", function(d) { return d.data.stroke; })
            .style("font-size", function(d) { return fontScale(d.data.count) + 'px'; })
            .on('click', function(d) {
                self.get('filter').setFilter('discourse', d.data.id);
            });

        item.append("span")
            .html(function(d) { return d.data.name + ' <span class="count">(' + d.data.count + ')</span>'; })
            .style("margin-left", function() { return ( Ember.$(this).width() > Ember.$(this).parent().width() ? - ( Ember.$(this).width() / 2 ) + ( Ember.$(this).parent().width() / 2 ) : 0 ) + 'px'; })
            .style("margin-top", function() { return Ember.$(this).parent().height() / 2 - Ember.$(this).height() / 2 + 'px'; });

        previousDiv.remove();
        element.style('position',null).style('left',null);

        this._super(...arguments);
    },

    createNodes: function() {
        var self = this;
        var children = [];
        var discourses = this.get('discourses');

        discourses.forEach(function(discourse) {
            var category_key = self.get('constants').DISCOURSE_CATEGORY_KEYS[discourse.get('id')];
            var category_value = self.get('constants').DISCOURSE_CATEGORY_VALUES[category_key];

            children.push({
                id: discourse.get('id'),
                name: discourse.get('label'),
                value: discourse.get('count'),
                count: discourse.get('count'),
                fill: category_value.fill,
                stroke: category_value.stroke
            });
        });

        return { 'children': children };
    }

});