mirror of
https://github.com/react/react-native-devtools-frontend.git
synced 2026-10-12 17:45:49 +08:00
DevTools: Move legend into the PieChart control.
Today, each tool that uses the PieChart control has to implement its own legend control. This change moves the legend into PieChart and standardizes the look and feel. Screenshots: Application Storage tab: https://imgur.com/laaPFRO Heap statistics: https://imgur.com/SNwpGXt Profiler: https://imgur.com/FiKZtTx Change-Id: I61a8adab0b58fbe49298804f97208da6a6bbbce6 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1607140 Commit-Queue: Jeff Fisher <jeffish@microsoft.com> Reviewed-by: Joel Einbinder <einbinder@chromium.org> Reviewed-by: Alexei Filippov <alph@chromium.org> Cr-Original-Commit-Position: refs/heads/master@{#661950} Cr-Mirrored-From: https://chromium.googlesource.com/chromium/src Cr-Mirrored-Commit: 9ef252cf698f59acd24f683cd5dcad9d34d33e88
This commit is contained in:
@@ -42,7 +42,8 @@ LayerViewer.PaintProfilerView = class extends UI.HBox {
|
||||
this._canvasContainer = this.contentElement.createChild('div', 'paint-profiler-canvas-container');
|
||||
this._progressBanner = this.contentElement.createChild('div', 'full-widget-dimmed-banner hidden');
|
||||
this._progressBanner.textContent = Common.UIString('Profiling\u2026');
|
||||
this._pieChart = new PerfUI.PieChart(55, this._formatPieChartTime.bind(this), true);
|
||||
this._pieChart = new PerfUI.PieChart(
|
||||
{chartName: ls`Profiling Results`, size: 55, formatter: this._formatPieChartTime.bind(this)});
|
||||
this._pieChart.element.classList.add('paint-profiler-pie-chart');
|
||||
this.contentElement.appendChild(this._pieChart.element);
|
||||
|
||||
|
||||
@@ -28,20 +28,25 @@
|
||||
* 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 {number} size
|
||||
* @param {function(number):string=} formatter
|
||||
* @param {boolean=} showTotal
|
||||
* @param {!PerfUI.PieChartOptions} options
|
||||
*/
|
||||
constructor(size, formatter, showTotal) {
|
||||
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');
|
||||
const svg = this._createSVGChild(root, 'svg');
|
||||
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;
|
||||
@@ -55,12 +60,13 @@ PerfUI.PieChart = class {
|
||||
circle.setAttribute('stroke', 'hsl(0, 0%, 80%)');
|
||||
circle.setAttribute('fill', 'transparent');
|
||||
circle.setAttribute('stroke-width', strokeWidth);
|
||||
this._foregroundElement = root.createChild('div', 'pie-chart-foreground');
|
||||
if (showTotal)
|
||||
this._totalElement = this._foregroundElement.createChild('div', 'pie-chart-total');
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -78,8 +84,12 @@ PerfUI.PieChart = class {
|
||||
totalString = this._formatter ? this._formatter(totalValue) : totalValue;
|
||||
else
|
||||
totalString = '';
|
||||
if (this._totalElement)
|
||||
this._totalElement.textContent = totalString;
|
||||
this._totalElement.textContent = totalString;
|
||||
if (this._legend) {
|
||||
this._legend.removeChildren();
|
||||
const legendItem = this._addLegendItem(totalValue, ls`Total`);
|
||||
UI.ARIAUtils.setLabelledBy(this._totalElement, legendItem);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -88,15 +98,16 @@ PerfUI.PieChart = class {
|
||||
_setSize(value) {
|
||||
this._group.setAttribute('transform', 'scale(' + (value / 2) + ') translate(1, 1) scale(0.99, 0.99)');
|
||||
const size = value + 'px';
|
||||
this.element.style.width = size;
|
||||
this.element.style.height = size;
|
||||
this._chartRoot.style.width = size;
|
||||
this._chartRoot.style.height = size;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {number} value
|
||||
* @param {string} color
|
||||
* @param {string=} name
|
||||
*/
|
||||
addSlice(value, color) {
|
||||
addSlice(value, color, name) {
|
||||
let sliceAngle = value / this._totalValue * 2 * Math.PI;
|
||||
if (!isFinite(sliceAngle))
|
||||
return;
|
||||
@@ -117,6 +128,10 @@ PerfUI.PieChart = class {
|
||||
`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) {
|
||||
const legendItem = this._addLegendItem(value, name, color);
|
||||
UI.ARIAUtils.setLabelledBy(path, legendItem);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -129,4 +144,30 @@ PerfUI.PieChart = class {
|
||||
parent.appendChild(child);
|
||||
return child;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {number} value
|
||||
* @param {string=} name
|
||||
* @param {string=} color
|
||||
* @returns {!Element}
|
||||
*/
|
||||
_addLegendItem(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;
|
||||
sizeDiv.textContent = this._formatter ? this._formatter(value) : value;
|
||||
return node;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -5,9 +5,15 @@
|
||||
*/
|
||||
|
||||
.root {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
min-width: fit-content;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.chart-root {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.pie-chart-foreground {
|
||||
@@ -17,10 +23,45 @@
|
||||
z-index: 10;
|
||||
top: 0;
|
||||
display: flex;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.pie-chart-total {
|
||||
margin: auto;
|
||||
padding: 2px 5px;
|
||||
background-color: rgba(255, 255, 255, 0.6);
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
.pie-chart-legend {
|
||||
margin-left: 30px;
|
||||
}
|
||||
|
||||
.pie-chart-legend-row {
|
||||
margin: 5px auto;
|
||||
padding-right: 25px;
|
||||
}
|
||||
|
||||
.pie-chart-swatch {
|
||||
display: inline-block;
|
||||
width: 11px;
|
||||
height: 11px;
|
||||
margin: 0 6px;
|
||||
top: 1px;
|
||||
position: relative;
|
||||
border: 1px solid rgba(100, 100, 100, 0.2);
|
||||
}
|
||||
|
||||
.pie-chart-swatch.pie-chart-empty-swatch {
|
||||
border: none;
|
||||
}
|
||||
|
||||
.pie-chart-name {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.pie-chart-size {
|
||||
display: inline-block;
|
||||
text-align: right;
|
||||
width: 70px;
|
||||
}
|
||||
|
||||
@@ -292,7 +292,6 @@ Profiler.HeapSnapshotView = class extends UI.SimpleView {
|
||||
this._statisticsView.addRecord(statistics.jsArrays, Common.UIString('JS Arrays'), '#7af');
|
||||
this._statisticsView.addRecord(statistics.native, Common.UIString('Typed Arrays'), '#fc5');
|
||||
this._statisticsView.addRecord(statistics.system, Common.UIString('System Objects'), '#98f');
|
||||
this._statisticsView.addRecord(statistics.total, Common.UIString('Total'));
|
||||
return statistics;
|
||||
}
|
||||
|
||||
@@ -1611,10 +1610,14 @@ Profiler.HeapSnapshotStatisticsView = class extends UI.VBox {
|
||||
constructor() {
|
||||
super();
|
||||
this.element.classList.add('heap-snapshot-statistics-view');
|
||||
this._pieChart = new PerfUI.PieChart(150, Profiler.HeapSnapshotStatisticsView._valueFormatter, true);
|
||||
this._pieChart = new PerfUI.PieChart({
|
||||
chartName: ls`Heap memory usage`,
|
||||
size: 150,
|
||||
formatter: Profiler.HeapSnapshotStatisticsView._valueFormatter,
|
||||
showLegend: true
|
||||
});
|
||||
this._pieChart.element.classList.add('heap-snapshot-stats-pie-chart');
|
||||
this.element.appendChild(this._pieChart.element);
|
||||
this._labels = this.element.createChild('div', 'heap-snapshot-stats-legend');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1635,22 +1638,10 @@ Profiler.HeapSnapshotStatisticsView = class extends UI.VBox {
|
||||
/**
|
||||
* @param {number} value
|
||||
* @param {string} name
|
||||
* @param {string=} color
|
||||
* @param {string} color
|
||||
*/
|
||||
addRecord(value, name, color) {
|
||||
if (color)
|
||||
this._pieChart.addSlice(value, color);
|
||||
|
||||
const node = this._labels.createChild('div');
|
||||
const swatchDiv = node.createChild('div', 'heap-snapshot-stats-swatch');
|
||||
const nameDiv = node.createChild('div', 'heap-snapshot-stats-name');
|
||||
const sizeDiv = node.createChild('div', 'heap-snapshot-stats-size');
|
||||
if (color)
|
||||
swatchDiv.style.backgroundColor = color;
|
||||
else
|
||||
swatchDiv.classList.add('heap-snapshot-stats-empty-swatch');
|
||||
nameDiv.textContent = name;
|
||||
sizeDiv.textContent = Profiler.HeapSnapshotStatisticsView._valueFormatter(value);
|
||||
this._pieChart.addSlice(value, color, name);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -182,38 +182,6 @@
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.heap-snapshot-stats-legend {
|
||||
margin-left: 24px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.heap-snapshot-stats-legend > div {
|
||||
margin-top: 1px;
|
||||
width: 170px;
|
||||
}
|
||||
|
||||
.heap-snapshot-stats-swatch {
|
||||
display: inline-block;
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
border: 1px solid rgba(100, 100, 100, 0.3);
|
||||
}
|
||||
|
||||
.heap-snapshot-stats-swatch.heap-snapshot-stats-empty-swatch {
|
||||
border: none;
|
||||
}
|
||||
|
||||
.heap-snapshot-stats-name,
|
||||
.heap-snapshot-stats-size {
|
||||
display: inline-block;
|
||||
margin-left: 6px;
|
||||
}
|
||||
|
||||
.heap-snapshot-stats-size {
|
||||
float: right;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.heap-allocation-stack .stack-frame {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
||||
@@ -40,12 +40,11 @@ Resources.ClearStorageView = class extends UI.ThrottledWidget {
|
||||
ls`Learn more`);
|
||||
learnMoreRow.appendChild(learnMore);
|
||||
this._quotaUsage = null;
|
||||
this._pieChart = new PerfUI.PieChart(110, Number.bytesToString, true);
|
||||
this._pieChartLegend = createElement('div');
|
||||
this._pieChart = new PerfUI.PieChart(
|
||||
{chartName: ls`Storage Usage`, size: 110, formatter: Number.bytesToString, showLegend: true});
|
||||
const usageBreakdownRow = quota.appendRow();
|
||||
usageBreakdownRow.classList.add('usage-breakdown-row');
|
||||
usageBreakdownRow.appendChild(this._pieChart.element);
|
||||
usageBreakdownRow.appendChild(this._pieChartLegend);
|
||||
|
||||
const clearButtonSection = this._reportView.appendSection('', 'clear-storage-button').appendRow();
|
||||
this._clearButton = UI.createTextButton(ls`Clear site data`, this._clear.bind(this));
|
||||
@@ -234,11 +233,7 @@ Resources.ClearStorageView = class extends UI.ThrottledWidget {
|
||||
continue;
|
||||
const title = this._getStorageTypeName(usageForType.storageType);
|
||||
const color = this._pieColors.get(usageForType.storageType) || '#ccc';
|
||||
this._pieChart.addSlice(value, color);
|
||||
const rowElement = this._pieChartLegend.createChild('div', 'usage-breakdown-legend-row');
|
||||
rowElement.createChild('span', 'usage-breakdown-legend-value').textContent = Number.bytesToString(value);
|
||||
rowElement.createChild('span', 'usage-breakdown-legend-swatch').style.backgroundColor = color;
|
||||
rowElement.createChild('span', 'usage-breakdown-legend-title').textContent = title;
|
||||
this._pieChart.addSlice(value, color, title);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -251,7 +246,6 @@ Resources.ClearStorageView = class extends UI.ThrottledWidget {
|
||||
*/
|
||||
_resetPieChart(total) {
|
||||
this._pieChart.setTotal(total);
|
||||
this._pieChartLegend.removeChildren();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -26,27 +26,3 @@
|
||||
.usage-breakdown-row {
|
||||
min-width: fit-content;
|
||||
}
|
||||
|
||||
.usage-breakdown-legend-row {
|
||||
margin: 5px auto;
|
||||
}
|
||||
|
||||
.usage-breakdown-legend-title {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.usage-breakdown-legend-value {
|
||||
display: inline-block;
|
||||
width: 70px;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.usage-breakdown-legend-swatch {
|
||||
display: inline-block;
|
||||
width: 11px;
|
||||
height: 11px;
|
||||
margin: 0 6px;
|
||||
position: relative;
|
||||
top: 1px;
|
||||
border: 1px solid rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
@@ -1523,12 +1523,16 @@ Timeline.TimelineUIUtils = class {
|
||||
total += aggregatedStats[categoryName];
|
||||
|
||||
const element = createElementWithClass('div', 'timeline-details-view-pie-chart-wrapper hbox');
|
||||
const pieChart = new PerfUI.PieChart(110, value => Number.preciseMillisToString(value), true);
|
||||
const pieChart = new PerfUI.PieChart({
|
||||
chartName: ls`Time spent in rendering`,
|
||||
size: 110,
|
||||
formatter: value => Number.preciseMillisToString(value),
|
||||
showLegend: true,
|
||||
});
|
||||
pieChart.element.classList.add('timeline-details-view-pie-chart');
|
||||
pieChart.setTotal(total);
|
||||
const pieChartContainer = element.createChild('div', 'vbox');
|
||||
pieChartContainer.appendChild(pieChart.element);
|
||||
const footerElement = element.createChild('div', 'timeline-aggregated-info-legend');
|
||||
|
||||
/**
|
||||
* @param {string} name
|
||||
@@ -1539,12 +1543,7 @@ Timeline.TimelineUIUtils = class {
|
||||
function appendLegendRow(name, title, value, color) {
|
||||
if (!value)
|
||||
return;
|
||||
pieChart.addSlice(value, color);
|
||||
const rowElement = footerElement.createChild('div');
|
||||
rowElement.createChild('span', 'timeline-aggregated-legend-value').textContent =
|
||||
Number.preciseMillisToString(value, 1);
|
||||
rowElement.createChild('span', 'timeline-aggregated-legend-swatch').style.backgroundColor = color;
|
||||
rowElement.createChild('span', 'timeline-aggregated-legend-title').textContent = title;
|
||||
pieChart.addSlice(value, color, title);
|
||||
}
|
||||
|
||||
// In case of self time, first add self, then children of the same category.
|
||||
|
||||
@@ -172,26 +172,6 @@
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.timeline-aggregated-legend-title {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.timeline-aggregated-legend-value {
|
||||
display: inline-block;
|
||||
width: 70px;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.timeline-aggregated-legend-swatch {
|
||||
display: inline-block;
|
||||
width: 11px;
|
||||
height: 11px;
|
||||
margin: 0 6px;
|
||||
position: relative;
|
||||
top: 1px;
|
||||
border: 1px solid rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
.popover ul {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
@@ -385,12 +365,6 @@
|
||||
line-height: 12px;
|
||||
}
|
||||
|
||||
.timeline-aggregated-info-legend > div {
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.timeline-flamechart {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
@@ -109,6 +109,14 @@ UI.ARIAUtils.markAsPresentation = function(element) {
|
||||
element.setAttribute('role', 'presentation');
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {!Element} element
|
||||
*/
|
||||
UI.ARIAUtils.ensureId = function(element) {
|
||||
if (!element.id)
|
||||
element.id = UI.ARIAUtils.nextId('ariaElement');
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {string} prefix
|
||||
* @return {string}
|
||||
@@ -186,6 +194,15 @@ UI.ARIAUtils.setAccessibleName = function(element, name) {
|
||||
element.setAttribute('aria-label', name);
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {!Element} element
|
||||
* @param {!Element} labelElement
|
||||
*/
|
||||
UI.ARIAUtils.setLabelledBy = function(element, labelElement) {
|
||||
UI.ARIAUtils.ensureId(labelElement);
|
||||
element.setAttribute('aria-labelledby', labelElement.id);
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {string} message
|
||||
* @param {!Element} element
|
||||
|
||||
Reference in New Issue
Block a user