cms/app-client/app/components/discourses-component.js
changeset 467 762fc0eb4946
parent 466 a8effb60ccb6
child 511 fbf4e1afab01
equal deleted inserted replaced
466:a8effb60ccb6 467:762fc0eb4946
     1 import Ember from 'ember';
     1 import Ember from 'ember';
     2 import d3 from 'd3';
     2 import * as d3 from 'd3-selection';
       
     3 import * as d3h from 'd3-hierarchy';
       
     4 import * as d3s from 'd3-scale';
     3 import _ from 'lodash/lodash';
     5 import _ from 'lodash/lodash';
       
     6 
       
     7 const MINIMUM_CIRCLE_WIDTH = 60.0;
     4 
     8 
     5 export default Ember.Component.extend({
     9 export default Ember.Component.extend({
     6 
    10 
     7     classNames: ['discourses-component'],
    11     classNames: ['discourses-component'],
     8 
    12 
     9     constants: Ember.inject.service(),
    13     constants: Ember.inject.service(),
    10     filter: Ember.inject.service(),
    14     filter: Ember.inject.service(),
       
    15     utils: Ember.inject.service(),
    11 
    16 
    12     discourseObserver: Ember.observer('filter.discourse', function() {
    17     discourseObserver: Ember.observer('filter.discourse', function() {
    13         Ember.$('.item').removeClass("selected");
    18         Ember.$('.item').removeClass("selected");
    14         if(this.get('filter').get('discourse') !== null) {
    19         if(this.get('filter').get('discourse') !== null) {
    15             this.get('filter').get('discourse').forEach(function(e) {
    20             this.get('filter').get('discourse').forEach(function(e) {
    23         var previousDiv = d3.selectAll(".discourses");
    28         var previousDiv = d3.selectAll(".discourses");
    24         var self = this;
    29         var self = this;
    25 
    30 
    26         var discourses = this.get('discourses');
    31         var discourses = this.get('discourses');
    27         var array = discourses.map(function (d) { return d.get('count'); });
    32         var array = discourses.map(function (d) { return d.get('count'); });
    28         var oldMin = Math.min(...array),
       
    29             oldMax = Math.max(...array);
       
    30         var sum = array.reduce(function(a, b) { return a + b; });
       
    31         var average = sum / array.length;
       
    32         var newMin = Math.floor((average - oldMin)),
       
    33             newMax = Math.floor((oldMax - average));
       
    34 
    33 
    35         var width = self.$().parent().width();
    34         var width = self.$().parent().width();
    36         var height = self.$().parent().height() - self.$().siblings().outerHeight(true);
    35         var height = self.$().parent().height() - self.$().siblings().outerHeight(true);
    37 
    36 
    38         var bubble = d3.layout.pack()
    37         //Determine the minimum circle width
    39             .sort(function comparator(a, b) { return a.value + b.value; })
    38         var longerStr = _.max(
       
    39           [].concat(...(discourses.map(function(d) { return d.get('label').split(' ');}))),
       
    40           function(s) { return s.length; }
       
    41         );
       
    42         var w = this.get('utils').getWidthOfText(longerStr, '11px');
       
    43         // we try to take into account the fact that there is at least 2 lines
       
    44         var minimum_circle_width = Math.max( Math.sqrt((w*w)+Math.pow(11+11/2, 2)) + 10, MINIMUM_CIRCLE_WIDTH);
       
    45 
       
    46         // to avoid division by zero. In any case it makes no sense to consider dimensions
       
    47         // under MINIMUM_CIRCLE_WIDTH
       
    48         var scaleFactor = minimum_circle_width/Math.max(minimum_circle_width, Math.min(width, height));
       
    49 
       
    50         var min = Math.min(...array),
       
    51             max = Math.max(...array);
       
    52 
       
    53         var scale = d3s.scaleLinear();
       
    54         // The range is the range for font sizes
       
    55         var fontScale = d3s.scaleQuantize().domain([min, max]).range(_.range(10, 14));
       
    56 
       
    57         if((min/max) < scaleFactor) {
       
    58           scale = scale.domain([min, max]).range([scaleFactor, 1]);
       
    59         }
       
    60 
       
    61         var bubble = d3h.pack()
    40             .size([width, height])
    62             .size([width, height])
    41             .value(function(d){
       
    42                 return Math.floor((((d.value - oldMin) * (newMax - newMin)) / (oldMax - oldMin)) + newMin);
       
    43             })
       
    44             .padding(10);
    63             .padding(10);
    45 
    64 
    46         var element = d3.select('#' + self.get('elementId'))
    65         var element = d3.select('#' + self.get('elementId'))
    47           .append('div')
    66           .append('div')
    48           .attr('class','discourses')
    67           .attr('class','discourses')
    49           .style('position','absolute')
    68           .style('position','absolute')
    50           .style('left', -100000);
    69           .style('left', -100000);
    51 
    70 
    52         var bubbles = bubble.nodes(self.createNodes());
    71         var root = d3h.hierarchy(self.createNodes())
       
    72           .sum(function(d) {return scale(d.value);});
    53 
    73 
       
    74         var bubbles = bubble(root).descendants();
    54         var nodes = element
    75         var nodes = element
    55             .selectAll()
    76             .selectAll()
    56             .data(bubbles);
    77             .data(bubbles);
    57 
    78 
    58         nodes.enter().append("div")
    79         nodes.enter().append("div")
    59             .attr("class", function(d) { return ( d.children ? "category": "item" ) + ( (self.get('filter').get('discourse') !== null && _.contains(self.get('filter').get('discourse'), d.id)) ? " selected" : "" ) ; });
    80             .attr("class", function(d) { return ( d.children ? "category": "item" ) + ( (self.get('filter').get('discourse') !== null && _.contains(self.get('filter').get('discourse'), d.id)) ? " selected" : "" ) ; });
    60 
    81 
    61         var item = element.selectAll(".item")
    82         var item = element.selectAll(".item")
    62             .attr("data-id", function(d) { return d.id; })
    83             .attr("data-id", function(d) { return d.data.id; })
    63             .style("left", function(d) { return ( d.x - d.r)  + "px"; })
    84             .style("left", function(d) { return ( d.x - d.r)  + "px"; })
    64             .style("top", function(d) { return ( d.y - d.r)  + "px"; })
    85             .style("top", function(d) { return ( d.y - d.r)  + "px"; })
    65             .style("width", function(d) { return (d.r * 2) + "px"; })
    86             .style("width", function(d) { return (d.r * 2) + "px"; })
    66             .style("height", function(d) { return (d.r * 2) + "px"; })
    87             .style("height", function(d) { return (d.r * 2) + "px"; })
    67             .style("background-color", function(d) { return d.fill; })
    88             .style("background-color", function(d) { return d.data.fill; })
    68             .style("border-color", function(d) { return d.stroke; })
    89             .style("border-color", function(d) { return d.data.stroke; })
    69             .style("font-size", function(d) { return Math.floor((((d.value - oldMin) * (13 - 10)) / (oldMax - oldMin)) + 10) + 'px'; })
    90             .style("font-size", function(d) { return fontScale(d.data.count) + 'px'; })
    70             .on('click', function(d) {
    91             .on('click', function(d) {
    71                 self.get('filter').setFilter('discourse', d.id);
    92                 self.get('filter').setFilter('discourse', d.data.id);
    72             });
    93             });
    73 
    94 
    74         item.append("span")
    95         item.append("span")
    75             .html(function(d) { return d.name + ' <span class="count">(' + d.count + ')</span>'; })
    96             .html(function(d) { return d.data.name + ' <span class="count">(' + d.data.count + ')</span>'; })
    76             .style("margin-left", function() { return ( Ember.$(this).width() > Ember.$(this).parent().width() ? - ( Ember.$(this).width() / 2 ) + ( Ember.$(this).parent().width() / 2 ) : 0 ) + 'px'; })
    97             .style("margin-left", function() { return ( Ember.$(this).width() > Ember.$(this).parent().width() ? - ( Ember.$(this).width() / 2 ) + ( Ember.$(this).parent().width() / 2 ) : 0 ) + 'px'; })
    77             .style("margin-top", function() { return Ember.$(this).parent().height() / 2 - Ember.$(this).height() / 2 + 'px'; });
    98             .style("margin-top", function() { return Ember.$(this).parent().height() / 2 - Ember.$(this).height() / 2 + 'px'; });
    78 
    99 
    79         previousDiv.remove();
   100         previousDiv.remove();
    80         element.style('position',null).style('left',null);
   101         element.style('position',null).style('left',null);