blob: ad1a9b215e517e163069a440a2aa921b3d11bda3 [file] [log] [blame]
/*
* Copyright (C) 2013 Google Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/** @typedef {{size: number, formatter: function(number):string, showLegend: (boolean|undefined), chartName: string}} */
PerfUI.PieChartOptions;
/**
* @unrestricted
*/
PerfUI.PieChart = class {
/**
* @param {!PerfUI.PieChartOptions} options
*/
constructor(options) {
const {size, formatter, showLegend, chartName} = options;
this.element = createElement('div');
this._shadowRoot = UI.createShadowRootWithCoreStyles(this.element, 'perf_ui/pieChart.css');
const root = this._shadowRoot.createChild('div', 'root');
UI.ARIAUtils.markAsGroup(root);
UI.ARIAUtils.setAccessibleName(root, chartName);
this._chartRoot = root.createChild('div', 'chart-root');
const svg = this._createSVGChild(this._chartRoot, 'svg');
this._group = this._createSVGChild(svg, 'g');
this._innerR = 0.618;
const strokeWidth = 1 / size;
let circle = this._createSVGChild(this._group, 'circle');
circle.setAttribute('r', 1);
circle.setAttribute('stroke', 'hsl(0, 0%, 80%)');
circle.setAttribute('fill', 'transparent');
circle.setAttribute('stroke-width', strokeWidth);
circle = this._createSVGChild(this._group, 'circle');
circle.setAttribute('r', this._innerR);
circle.setAttribute('stroke', 'hsl(0, 0%, 80%)');
circle.setAttribute('fill', 'transparent');
circle.setAttribute('stroke-width', strokeWidth);
this._foregroundElement = this._chartRoot.createChild('div', 'pie-chart-foreground');
this._totalElement = this._foregroundElement.createChild('div', 'pie-chart-total');
this._formatter = formatter;
this._slices = [];
this._lastAngle = -Math.PI / 2;
if (showLegend) {
this._legend = root.createChild('div', 'pie-chart-legend');
}
this._setSize(size);
}
/**
* @param {number} totalValue
*/
setTotal(totalValue) {
for (let i = 0; i < this._slices.length; ++i) {
this._slices[i].remove();
}
this._slices = [];
this._totalValue = totalValue;
this._lastAngle = -Math.PI / 2;
let totalString;
if (totalValue) {
totalString = this._formatter ? this._formatter(totalValue) : totalValue;
} else {
totalString = '';
}
this._totalElement.textContent = totalString;
if (this._legend) {
this._legend.removeChildren();
this._addLegendItem(this._totalElement, totalValue, ls`Total`);
}
}
/**
* @param {number} value
*/
_setSize(value) {
this._group.setAttribute('transform', 'scale(' + (value / 2) + ') translate(1, 1) scale(0.99, 0.99)');
const size = value + 'px';
this._chartRoot.style.width = size;
this._chartRoot.style.height = size;
}
/**
* @param {number} value
* @param {string} color
* @param {string=} name
*/
addSlice(value, color, name) {
let sliceAngle = value / this._totalValue * 2 * Math.PI;
if (!isFinite(sliceAngle)) {
return;
}
sliceAngle = Math.min(sliceAngle, 2 * Math.PI * 0.9999);
const path = this._createSVGChild(this._group, 'path');
const x1 = Math.cos(this._lastAngle);
const y1 = Math.sin(this._lastAngle);
this._lastAngle += sliceAngle;
const x2 = Math.cos(this._lastAngle);
const y2 = Math.sin(this._lastAngle);
const r2 = this._innerR;
const x3 = x2 * r2;
const y3 = y2 * r2;
const x4 = x1 * r2;
const y4 = y1 * r2;
const largeArc = sliceAngle > Math.PI ? 1 : 0;
path.setAttribute('d',
`M${x1},${y1} A1,1,0,${largeArc},1,${x2},${y2} L${x3},${y3} A${r2},${r2},0,${largeArc},0,${x4},${y4} Z`);
path.setAttribute('fill', color);
this._slices.push(path);
if (this._legend) {
this._addLegendItem(path, value, name, color);
}
}
/**
* @param {!Element} parent
* @param {string} childType
* @return {!Element}
*/
_createSVGChild(parent, childType) {
const child = parent.ownerDocument.createElementNS('http://www.w3.org/2000/svg', childType);
parent.appendChild(child);
return child;
}
/**
* @param {!Element} figureElement
* @param {number} value
* @param {string=} name
* @param {string=} color
* @returns {!Element}
*/
_addLegendItem(figureElement, value, name, color) {
const node = this._legend.ownerDocument.createElement('div');
node.className = 'pie-chart-legend-row';
// make sure total always appears at the bottom
if (this._legend.childElementCount) {
this._legend.insertBefore(node, this._legend.lastElementChild);
} else {
this._legend.appendChild(node);
}
const sizeDiv = node.createChild('div', 'pie-chart-size');
const swatchDiv = node.createChild('div', 'pie-chart-swatch');
const nameDiv = node.createChild('div', 'pie-chart-name');
if (color) {
swatchDiv.style.backgroundColor = color;
} else {
swatchDiv.classList.add('pie-chart-empty-swatch');
}
nameDiv.textContent = name;
const size = this._formatter ? this._formatter(value) : value;
sizeDiv.textContent = size;
UI.ARIAUtils.setAccessibleName(figureElement, name + ' ' + size);
return node;
}
};