]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-5207 Treemap as a metric widget skeleton
authorStas Vilchik <vilchiks@gmail.com>
Fri, 4 Jul 2014 11:06:25 +0000 (17:06 +0600)
committerStas Vilchik <vilchiks@gmail.com>
Fri, 4 Jul 2014 11:06:34 +0000 (17:06 +0600)
12 files changed:
plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/CorePlugin.java
plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/dashboards/GlobalDefaultDashboard.java
plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/TreemapWidget.java
plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/measures/MeasureFilterAsTreemapWidget.java [new file with mode: 0644]
plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/measures/MeasureFilterTreemapWidget.java [deleted file]
plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/measure_filter_treemap.html.erb
plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/dashboards/GlobalDefaultDashboardTest.java
sonar-server/Gruntfile.coffee
sonar-server/src/main/coffee/widgets/base.coffee
sonar-server/src/main/coffee/widgets/treemap.coffee [new file with mode: 0644]
sonar-server/src/main/coffee/widgets/word-cloud.coffee
sonar-server/src/main/less/style.less

index 22e37e6bf346e5390b78db757022ce53024a58bd..484ff746d683be9cd7b14483147760af1aa7ce53 100644 (file)
@@ -216,7 +216,7 @@ public final class CorePlugin extends SonarPlugin {
       HotspotMetricWidget.class,
       TreemapWidget.class,
       MeasureFilterListWidget.class,
-      MeasureFilterTreemapWidget.class,
+      MeasureFilterAsTreemapWidget.class,
       WelcomeWidget.class,
       DocumentationCommentsWidget.class,
       DuplicationsWidget.class,
index f7933cfbbf4a6765e3f0b3ec5356467cd3de870f..5e204b185374ae095e199760385a0fff7b9fe15e 100644 (file)
@@ -27,8 +27,8 @@ import org.sonar.core.measure.db.MeasureFilterDto;
 import org.sonar.plugins.core.measurefilters.MyFavouritesFilter;
 import org.sonar.plugins.core.measurefilters.ProjectFilter;
 import org.sonar.plugins.core.widgets.WelcomeWidget;
+import org.sonar.plugins.core.widgets.measures.MeasureFilterAsTreemapWidget;
 import org.sonar.plugins.core.widgets.measures.MeasureFilterListWidget;
-import org.sonar.plugins.core.widgets.measures.MeasureFilterTreemapWidget;
 
 /**
  * Projects global dashboard for Sonar
@@ -75,10 +75,10 @@ public final class GlobalDefaultDashboard extends DashboardTemplate {
         .setProperty(MeasureFilterListWidget.PAGE_SIZE_PROPERTY, "20");
 
       dashboard
-        .addWidget(MeasureFilterTreemapWidget.ID, 2)
+        .addWidget(MeasureFilterAsTreemapWidget.ID, 2)
         .setProperty(MeasureFilterListWidget.FILTER_PROPERTY, filter.getId().toString())
-        .setProperty(MeasureFilterTreemapWidget.SIZE_METRIC_PROPERTY, "ncloc")
-        .setProperty(MeasureFilterTreemapWidget.COLOR_METRIC_PROPERTY, "coverage");
+        .setProperty(MeasureFilterAsTreemapWidget.SIZE_METRIC_PROPERTY, "ncloc")
+        .setProperty(MeasureFilterAsTreemapWidget.COLOR_METRIC_PROPERTY, "coverage");
     }
   }
 
index 6ce87b3be752fea477120573148d26d8392e1e80..c941ef8ca368e9c5ef1f5d52966d9d8e008941ac 100644 (file)
@@ -33,6 +33,6 @@ import org.sonar.api.web.WidgetPropertyType;
 public class TreemapWidget extends CoreWidget {
   public TreemapWidget() {
     // do not use the id "treemap" to avoid conflict with the same CSS class
-    super("treemap-widget", "Treemap of Components", "/org/sonar/plugins/core/widgets/treemap.html.erb");
+    super("treemap-widget", "Treemap of Components", "/Users/Stas/Projects/sonar/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/treemap.html.erb");
   }
 }
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/measures/MeasureFilterAsTreemapWidget.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/measures/MeasureFilterAsTreemapWidget.java
new file mode 100644 (file)
index 0000000..ae959dd
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.plugins.core.widgets.measures;
+
+import org.sonar.api.web.*;
+import org.sonar.plugins.core.widgets.CoreWidget;
+import org.sonar.plugins.core.widgets.WidgetConstants;
+
+import static org.sonar.api.web.WidgetScope.GLOBAL;
+
+@WidgetCategory({"Filters"})
+@WidgetScope(GLOBAL)
+@WidgetProperties({
+  @WidgetProperty(key = MeasureFilterAsTreemapWidget.FILTER_PROPERTY, type = WidgetPropertyType.FILTER,
+    optional = false),
+  @WidgetProperty(key = MeasureFilterAsTreemapWidget.CHART_TITLE_PROPERTY, type = WidgetPropertyType.STRING),
+  @WidgetProperty(key = MeasureFilterAsTreemapWidget.SIZE_METRIC_PROPERTY, type = WidgetPropertyType.METRIC,
+    optional = true, options = { WidgetConstants.FILTER_OUT_NEW_METRICS }),
+  @WidgetProperty(key = MeasureFilterAsTreemapWidget.COLOR_METRIC_PROPERTY, type = WidgetPropertyType.METRIC,
+    optional = true, options = { WidgetConstants.FILTER_OUT_NEW_METRICS, "type:PERCENT,RATING,LEVEL" }),
+  @WidgetProperty(key = MeasureFilterAsTreemapWidget.HEIGHT_PERCENTS_PROPERTY, type = WidgetPropertyType.INTEGER,
+    optional = true, defaultValue = "55", description = "Height in percents of width"),
+  @WidgetProperty(key = MeasureFilterAsTreemapWidget.MAX_ITEMS_PROPERTY, type = WidgetPropertyType.INTEGER,
+    defaultValue = "100")
+})
+public class MeasureFilterAsTreemapWidget extends CoreWidget {
+    public static final String FILTER_PROPERTY = "filter";
+    public static final String SIZE_METRIC_PROPERTY = "sizeMetric";
+    public static final String COLOR_METRIC_PROPERTY = "colorMetric";
+    public static final String HEIGHT_PERCENTS_PROPERTY = "heightInPercents";
+    public static final String CHART_TITLE_PROPERTY = "chartTitle";
+    public static final String MAX_ITEMS_PROPERTY = "maxItems";
+    public static final String ID = "measure_filter_treemap";
+
+  public MeasureFilterAsTreemapWidget() {
+    super(ID, "Measure Filter as Treemap", "/Users/Stas/Projects/sonar/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/measure_filter_treemap.html.erb");
+  }
+}
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/measures/MeasureFilterTreemapWidget.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/measures/MeasureFilterTreemapWidget.java
deleted file mode 100644 (file)
index 29bc578..0000000
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * SonarQube is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * SonarQube is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-package org.sonar.plugins.core.widgets.measures;
-
-import org.sonar.api.web.*;
-import org.sonar.plugins.core.widgets.CoreWidget;
-import org.sonar.plugins.core.widgets.WidgetConstants;
-
-import static org.sonar.api.web.WidgetScope.GLOBAL;
-
-@WidgetCategory({"Filters"})
-@WidgetScope(GLOBAL)
-@WidgetProperties({
-  @WidgetProperty(key = MeasureFilterTreemapWidget.FILTER_PROPERTY, type = WidgetPropertyType.FILTER, optional = false),
-  @WidgetProperty(key = MeasureFilterTreemapWidget.SIZE_METRIC_PROPERTY, type = WidgetPropertyType.METRIC, optional = true, options = {WidgetConstants.FILTER_OUT_NEW_METRICS}),
-  @WidgetProperty(key = MeasureFilterTreemapWidget.COLOR_METRIC_PROPERTY, type = WidgetPropertyType.METRIC, optional = true,
-    options = {WidgetConstants.FILTER_OUT_NEW_METRICS,"type:PERCENT,RATING,LEVEL"}),
-  @WidgetProperty(key = MeasureFilterTreemapWidget.HEIGHT_PERCENTS_PROPERTY, type = WidgetPropertyType.INTEGER, optional = true, defaultValue = "55",
-    description = "Height in percents of width"),
-  @WidgetProperty(key = MeasureFilterListWidget.DISPLAY_FILTER_DESCRIPTION, type = WidgetPropertyType.BOOLEAN, defaultValue = "false")
-})
-public class MeasureFilterTreemapWidget extends CoreWidget {
-  public static final String FILTER_PROPERTY = "filter";
-  public static final String SIZE_METRIC_PROPERTY = "sizeMetric";
-  public static final String COLOR_METRIC_PROPERTY = "colorMetric";
-  public static final String HEIGHT_PERCENTS_PROPERTY = "heightInPercents";
-  public static final String DISPLAY_FILTER_DESCRIPTION = "displayFilterDescription";
-  public static final String ID = "measure_filter_treemap";
-
-  public MeasureFilterTreemapWidget() {
-    super(ID, "Measure Filter as Treemap", "/org/sonar/plugins/core/widgets/measure_filter_treemap.html.erb");
-  }
-}
index 8d1021f5adecf55a42dffab2f2dbfa0589cb716c..67d2bd13cfb46594f10ee91a5cef04b15243cd06 100644 (file)
@@ -1,41 +1,56 @@
 <%
-   filter_id = widget_properties['filter']
-   size_metric = widget_properties['sizeMetric']
-   color_metric = widget_properties['colorMetric']
-   filter = MeasureFilter.find_by_id(filter_id.to_i) if filter_id
-   if filter
-     url_options = {:controller => 'measures', :action => 'filter', :id => filter.id}
-     filter.load_criteria_from_data
-     filter.set_criteria_value(:display, 'treemap')
-     if size_metric
-       filter.set_criteria_value(:tmSize, size_metric.key)
-       url_options[:tmSize]=size_metric.key
-     end
-     if color_metric
-       filter.set_criteria_value(:tmColor, color_metric.key)
-       url_options[:tmColor]=color_metric.key
-     end
-     filter.set_criteria_value(:tmHeight, widget_properties['heightInPercents'])
+   containerId = 'treemap-widget' + widget.id.to_s
+   chartTitle = widget_properties['chartTitle']
+   filterId = widget_properties['filter'].to_i
+   maxItems = widget_properties['maxItems'].to_i
 
-
-     if !filter.require_authentication? || logged_in?
-       filter.execute(self, :user => current_user)
-
-       @widget_title = link_to h(filter.name), url_options
+   filter = MeasureFilter.find_by_id(filterId.to_i)
+   @widget_title = link_to h(filter.name), {:controller => 'measures', :action => 'filter', :id => filter.id, :display => 'list'}
 %>
 
-  <% if widget_properties['displayFilterDescription'] && !filter.description.blank? %>
-    <div style="padding-bottom: 5px">
-      <span class="note"><%= h filter.description -%></span>
-    </div>
+<div class="treemap-widget" id="<%= containerId %>">
+  <!--[if lte IE 8 ]> <h3><%= message('widget.unsupported_browser_warning') -%></h3> <![endif]-->
+
+  <!--[if (gte IE 9)|!(IE)]><!-->
+  <% if chartTitle %>
+    <h3 style="margin-bottom: 5px;"><%= h(chartTitle) -%></h3>
   <% end %>
+  <!--<![endif]-->
+</div>
 
-    <%= render :partial => 'measures/display_treemap', :locals => {:edit_mode => false, :widget_id => widget.id, :filter => filter} %>
-<%
-     end
-  else
-%>
-  <p><i class="icon-alert-warn"></i> <%= message 'measure_filter.widget.unknown_filter_warning' -%></p>
-<%
-     end
-%>
+<!--[if (gte IE 9)|!(IE)]><!-->
+<script>
+  (function () {
+    var metrics = [
+          '<%= widget_properties['colorMetric'].name -%>',
+          '<%= widget_properties['sizeMetric'].name -%>'
+        ],
+        query = [
+          'filter=<%= filterId -%>',
+          'metrics=' + metrics.join(','),
+          'fields=name,longName,qualifier',
+          'pageSize=<%= maxItems -%>',
+          'page=1',
+          'sort=metric:' + metrics[1],
+          'asc=false'
+        ].join('&'),
+        widget = new SonarWidgets.Widget();
+
+    widget
+        .type('Treemap')
+        .source(baseUrl + '/measures/search_filter?' + query)
+        .metricsPriority(metrics)
+        .options({
+          heightInPercents: '<%= widget_properties['heightInPercents'] -%>',
+          maxItemsReachedMessage: '<%= message("widget.measure_filter_histogram.max_items_reached", :params => [maxItems]) -%>',
+          baseUrl: baseUrl + '/dashboard/index/',
+          noData: '<%= message('no_data') -%>'
+        })
+        .render('#<%= containerId -%>');
+
+    autoResize(500, function() {
+      widget.update('#<%= containerId -%>');
+    });
+  })();
+</script>
+<!--<![endif]-->
index 49770b62dd754b659570a770a71dd987d3523097..b5f79bb88de2349509dd1f04aee29a462091022c 100644 (file)
@@ -29,8 +29,8 @@ import org.sonar.plugins.core.CorePlugin;
 import org.sonar.plugins.core.measurefilters.MyFavouritesFilter;
 import org.sonar.plugins.core.measurefilters.ProjectFilter;
 import org.sonar.plugins.core.widgets.WelcomeWidget;
+import org.sonar.plugins.core.widgets.measures.MeasureFilterAsTreemapWidget;
 import org.sonar.plugins.core.widgets.measures.MeasureFilterListWidget;
-import org.sonar.plugins.core.widgets.measures.MeasureFilterTreemapWidget;
 
 import java.util.List;
 
@@ -77,7 +77,7 @@ public class GlobalDefaultDashboardTest {
     assertThat(secondColumn).hasSize(2);
     assertThat(secondColumn.get(0).getId()).isEqualTo(MeasureFilterListWidget.ID);
     assertThat(secondColumn.get(0).getProperty("filter")).isEqualTo("101");
-    assertThat(secondColumn.get(1).getId()).isEqualTo(MeasureFilterTreemapWidget.ID);
+    assertThat(secondColumn.get(1).getId()).isEqualTo(MeasureFilterAsTreemapWidget.ID);
     assertThat(secondColumn.get(1).getProperty("filter")).isEqualTo("101");
   }
 
index 0f06e25c577a74479de63da0b95bd790c12315a8..e49048f7eff44c3a460cb51b58ee3f86b4f27998 100644 (file)
@@ -94,6 +94,7 @@ module.exports = (grunt) ->
             '<%= pkg.assets %>js/widgets/pie-chart.js'
             '<%= pkg.assets %>js/widgets/histogram.js'
             '<%= pkg.assets %>js/widgets/word-cloud.js'
+            '<%= pkg.assets %>js/widgets/treemap.js'
             '<%= pkg.assets %>js/top-search.js'
             '<%= pkg.assets %>js/sortable.js'
             '<%= pkg.assets %>js/common/inputs.js'
@@ -125,6 +126,7 @@ module.exports = (grunt) ->
             '<%= pkg.assets %>js/widgets/pie-chart.js'
             '<%= pkg.assets %>js/widgets/histogram.js'
             '<%= pkg.assets %>js/widgets/word-cloud.js'
+            '<%= pkg.assets %>js/widgets/treemap.js'
             '<%= pkg.assets %>js/top-search.js'
             '<%= pkg.assets %>js/sortable.js'
             '<%= pkg.assets %>js/common/inputs.js'
index 133c15e464dbdf6ee2eeb217d7e65331c3996225..5531ceb2d9232072bc181011c8b7a4d3e209721a 100644 (file)
@@ -2,6 +2,9 @@ window.SonarWidgets ?= {}
 
 class BaseWidget
   lineHeight: 20
+  colorLow: '#d62728'
+  colorHigh: '#85bb43'
+  colorUnknown: '#777'
 
 
   constructor: ->
@@ -47,4 +50,11 @@ class BaseWidget
     @
 
 
-window.SonarWidgets.BaseWidget = BaseWidget
\ No newline at end of file
+  tooltip: (d) ->
+    title = d.longName
+    title += "\n#{@colorMetric.name}: #{@colorMetric.formattedValue d}" if @colorMetric.value(d)?
+    title += "\n#{@sizeMetric.name}: #{@sizeMetric.formattedValue d}" if @sizeMetric.value(d)?
+    title
+
+
+window.SonarWidgets.BaseWidget = BaseWidget
diff --git a/sonar-server/src/main/coffee/widgets/treemap.coffee b/sonar-server/src/main/coffee/widgets/treemap.coffee
new file mode 100644 (file)
index 0000000..7cb065d
--- /dev/null
@@ -0,0 +1,96 @@
+class Treemap extends window.SonarWidgets.BaseWidget
+  sizeLow: 10
+  sizeHigh: 24
+
+
+  constructor: ->
+    @addField 'width', null
+    @addField 'height', null
+    @addField 'maxResultsReached', false
+    super
+
+
+  getNodes: ->
+    @treemap.nodes(children: @components()).filter (d) -> !d.children
+
+
+  renderTreemap: ->
+    @treemap = d3.layout.treemap()
+    @treemap.value (d) =>
+      @sizeMetric.value d
+    @cells = @box.selectAll('.treemap-cell').data @getNodes()
+
+    cellsEnter = @cells.enter().append 'div'
+    cellsEnter.classed 'treemap-cell', true
+    cellsEnter.attr 'title', (d) => @tooltip d
+    cellsEnter.style 'color', (d) =>
+      if @colorMetric.value(d)? then @color @colorMetric.value(d) else @colorUnknown
+    cellsEnter.style 'font-size', (d) => "#{@size @sizeMetric.value d}px"
+
+    cellsLink = cellsEnter.append('a').classed 'treemap-detach', true
+    cellsLink.attr 'target', '_blank'
+    cellsLink.attr 'href', (d) =>
+      url = @options().baseUrl + encodeURIComponent(d.key)
+      url += '?metric=' + encodeURIComponent(@colorMetric.key) if d.qualifier == 'CLA' || d.qualifier == 'FIL'
+      url
+    cellsLink.append('i').attr 'class', (d) -> "icon-qualifier-#{d.qualifier.toLowerCase()}"
+
+    @cellsInner = cellsEnter.append('div').classed 'treemap-inner', true
+    @cellsInner.text (d) -> d.longName
+    @cellsInner.style 'border-color', (d) =>
+      if @colorMetric.value(d)? then @color @colorMetric.value(d) else @colorUnknown
+
+    @attachEvents cellsEnter
+
+
+  attachEvents: (cells) ->
+
+
+  positionCells: ->
+    @cells.style 'left', (d) -> "#{d.x}px"
+    @cells.style 'top', (d) -> "#{d.y}px"
+    @cells.style 'width', (d) -> "#{d.dx}px"
+    @cells.style 'height', (d) -> "#{d.dy}px"
+    @cells.classed 'treemap-cell-small', (d) -> d.dy < 60
+    @cellsInner.style 'line-height', (d) -> "#{d.dy}px"
+
+
+  render: (container) ->
+    box = d3.select(container).append('div')
+    box.classed 'sonar-d3', true
+    @box = box.append('div').classed 'treemap-container', true
+
+    # Configure metrics
+    @addMetric 'colorMetric', 0
+    @addMetric 'sizeMetric', 1
+
+    # Configure scales
+    @color = d3.scale.linear().domain([0, 100])
+    if @colorMetric.direction == 1
+      @color.range [@colorLow, @colorHigh]
+    else
+      @color.range [@colorHigh, @colorLow]
+
+    sizeDomain = d3.extent @components(), (d) => @sizeMetric.value d
+    @size = d3.scale.linear().domain(sizeDomain).range [@sizeLow, @sizeHigh]
+
+    # Show maxResultsReached message
+    if @maxResultsReached()
+      maxResultsReachedLabel = box.append('div').text @options().maxItemsReachedMessage
+      maxResultsReachedLabel.classed 'max-results-reached-message', true
+
+    @renderTreemap()
+    super
+
+
+  update: ->
+    @width @box.property 'offsetWidth'
+    @height (@width() / 100.0 * @options().heightInPercents)
+    @box.style 'height', "#{@height()}px"
+    @treemap.size [@width(), @height()]
+    @cells.data @getNodes()
+    @positionCells()
+
+
+
+window.SonarWidgets.Treemap = Treemap
index 1e09b55a6d5c925d695a6a2b73901b89e3126403..1d98fd1add0293facb33dde8dc8560114164a6d6 100644 (file)
@@ -1,7 +1,4 @@
 class WordCloud extends window.SonarWidgets.BaseWidget
-  colorLow: '#d62728'
-  colorHigh: '#1f77b4'
-  colorUnknown: '#777'
   sizeLow: 10
   sizeHigh: 24
 
@@ -22,11 +19,7 @@ class WordCloud extends window.SonarWidgets.BaseWidget
       url = @options().baseUrl + encodeURIComponent(d.key)
       url += '?metric=' + encodeURIComponent(@colorMetric.key) if d.qualifier == 'CLA' || d.qualifier == 'FIL'
       url
-    wordsEnter.attr 'title', (d) =>
-      title = d.longName
-      title += " | #{@colorMetric.name}: #{@colorMetric.formattedValue d}" if @colorMetric.value(d)?
-      title += " | #{@sizeMetric.name}: #{@sizeMetric.formattedValue d}" if @sizeMetric.value(d)?
-      title
+    wordsEnter.attr 'title', (d) => @tooltip d
 
     words.style 'color', (d) =>
       if @colorMetric.value(d)? then @color @colorMetric.value(d) else @colorUnknown
@@ -67,4 +60,4 @@ class WordCloud extends window.SonarWidgets.BaseWidget
 
 
 
-window.SonarWidgets.WordCloud = WordCloud
\ No newline at end of file
+window.SonarWidgets.WordCloud = WordCloud
index d359113276f5578c40057e9d461daa0ce5ccec98..aedda2cd0e9f338454e2eaf6913d1e99e19d850c 100644 (file)
@@ -2799,6 +2799,48 @@ div.rule-title {
   fill: #777;
 }
 
+.sonar-d3 .treemap-container {
+  position: relative;
+}
+
+.sonar-d3 .treemap-cell {
+  position: absolute;
+  border-right: 1px solid #fff;
+  border-bottom: 1px solid #fff;
+  .box-sizing(border-box);
+}
+
+.sonar-d3 .treemap-inner {
+  position: absolute;
+  z-index: 1;
+  top: 0; left: 0; bottom: 0; right: 0;
+  padding: 0 5px;
+  border: 1px solid;
+  text-align: center;
+  overflow: hidden;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+}
+
+.sonar-d3 .treemap-detach {
+  position: absolute;
+  z-index: 2;
+  top: 5px; right: 5px;
+  line-height: 16px;
+  color: inherit;
+  opacity: 0.5;
+
+  &:hover { opacity: 1; }
+}
+
+.sonar-d3 .treemap-cell-small {
+  .treemap-inner { text-indent: -9999px; }
+  .treemap-detach {
+    top: 50%; left: 50%;
+    margin: -8px 0 0 -8px;
+  }
+}
+
 
 /* ------------------- Admin pages ------------------- */