Files
xen-orchestra/app/node_modules/xo-sunburst-d3/index.js
T

198 lines
5.8 KiB
JavaScript

'use strict'
import angular from 'angular'
import d3 from 'd3'
import clone from 'lodash.clonedeep'
module.exports = angular.module('xoWebApp.directives')
.directive('sunburstChart', function ($parse) {
function link(scope, element, attrs) {
var width, height, radius, color, b, breadcrumbs, svg, arc, partition
width = attrs.width ? parseInt(attrs.width, 10) : 2000
height = attrs.height ? parseInt(attrs.height, 10) : width + 50 /* breadcrumbs */
radius = Math.min(width, height) / 2
color = d3.scale.category20c()
b = {
w: 75, h: 30, s: 3, t: 10
}
breadcrumbs = d3.select(element[0])
.append('div')
.attr('class', 'breadcrumbs-container')
.append('svg:svg')
.attr('class', 'breadcrumbs')
.attr('width', width)
.attr('height', 50)
// took responsive svg trick from http://demosthenes.info/blog/744/Make-SVG-Responsive
svg = d3.select(element[0])
.append('div')
.attr('style', 'display: inline-block;position: relative;width: 80%;padding-bottom: 100%; vertical-align: middle;overflow: hidden;')
.append('svg')
.attr('style', 'display: block;position: absolute; top: 0;left: 0;')
.attr('width', '100%')
.attr('height', '100%')
.attr('preserveAspectRatio', 'xMinYMin meet')
.attr('viewBox', '0 0 2000 2000')
.append('g')
.attr('transform', 'translate(' + width / 2 + ',' + height * 0.52 + ')')
arc = d3.svg.arc()
.startAngle((d) => d.x)
.endAngle((d) => d.x + d.dx)
.innerRadius((d) => Math.sqrt(d.y))
.outerRadius((d) => Math.sqrt(d.y + d.dy))
scope.$watch('selected', function (newVal) {
if (newVal && newVal.id) {
highlight(newVal.id)
}
})
partition = d3.layout.partition()
.sort(null)
.size([2 * Math.PI, radius * radius])
.value((d) => d.size)
scope.$watch(() => scope.chartData, function (newVal) {
var g, chart_data
if (!scope.chartData || !scope.chartData.children || !scope.chartData.children.length) {
svg.selectAll('path').remove()
breadcrumbs.selectAll('g').remove()
return
}
//d3 add a lot aff data to nodex. It triggers watch without rest
chart_data = clone(scope.chartData)
g = svg
.datum(chart_data)
.selectAll('path')
.data(partition.nodes)
g.enter().append('path')
g.attr('display', (d) => d.depth ? null : 'none') // hide inner ring
.attr('d', arc)
.style('stroke', '#fff')
.style('fill', function (d) {
return d.color ? d.color : color((d.children ? d : d.parent).name)
})
.style('fill-rule', 'evenodd')
.on('mouseover', mouseover)
.on('click', click)
g.exit().remove()
}, true)
function mouseover(d) {
if (scope.over) {
scope.over.apply(null, [{ d: d }])
}
}
function click(d) {
if (scope.click) {
scope.click.apply(null, [{ d: d }])
}
}
function highlight(id) {
var sequenceArray = getAncestors(id)
updateBreadcrumbs(sequenceArray)
// Fade all the segments.
svg.selectAll('path')
.style('opacity', 0.3)
// Then highlight only those that are an ancestor of the current segment.
svg.selectAll('path')
.filter(function (node) {
return (sequenceArray.indexOf(node) >= 0)
})
.style('opacity', 1)
}
// Generate a string that describes the points of a breadcrumb polygon.
function breadcrumbPoints(d, i) {
var points = []
points.push('0,0')
points.push(b.w + ',0')
points.push(b.w + b.t + ',' + (b.h / 2))
points.push(b.w + ',' + b.h)
points.push('0,' + b.h)
if (i > 0) { // Leftmost breadcrumb don't include 6th vertex.
points.push(b.t + ',' + (b.h / 2))
}
return points.join(' ')
}
// Update the breadcrumb trail to show the current sequence and percentage.
function updateBreadcrumbs(nodeArray, percentageString) {
var g = breadcrumbs
.selectAll('g')
.data(nodeArray, function (d) {
return d.name + d.depth
})
// Add breadcrumb and label for entering nodes.
var entering = g.enter().append('svg:g')
entering.append('svg:polygon')
.attr('points', breadcrumbPoints)
.style('fill', function (d) {
return d.color ? d.color : color((d.children ? d : d.parent).name)
})
entering.append('svg:text')
.attr('x', (b.w + b.t) / 2)
.attr('y', b.h / 2)
.attr('dy', '0.35em')
.attr('text-anchor', 'middle')
.text((d) => d.name)
// Set position for entering and updating nodes.
g.attr('transform', function (d, i) {
return 'translate(' + i * (b.w + b.s) + ', 0)'
})
// Remove exiting nodes.
g.exit().remove()
// Make the breadcrumb trail visible, if it's hidden.
d3.select('.breadcrumbs')
.style('visibility', '')
}
function getAncestors(id) {
var path = []
svg.selectAll('path')
.each(function (node) {
if (node.id === id) {
var current = node
while (current.parent) {
path.unshift(current)
current = current.parent
}
}
})
return path
}
}
return {
resctict: 'E',
replace: false,
scope: {
chartData: '=',
selected: '=',
over: '&',
click: '&'
},
link: link
}
})
// A module exports its name.
.name