Skip to content

Commit bed6511

Browse files
committed
feature #33535 [WebProfilerBundle] Assign automatic colors to custom Stopwatch categories (javiereguiluz)
This PR was merged into the 4.4 branch. Discussion ---------- [WebProfilerBundle] Assign automatic colors to custom Stopwatch categories | Q | A | ------------- | --- | Branch? | 4.4 | Bug fix? | no | New feature? | yes | BC breaks? | no | Deprecations? | no | Tests pass? | yes | Fixed tickets | #33514 | License | MIT | Doc PR | not needed ### Before ![image](https://user-images.githubusercontent.com/73419/64624345-d2907000-d3ea-11e9-9320-5b316768273d.png) ### After ![image](https://user-images.githubusercontent.com/73419/64624358-d6bc8d80-d3ea-11e9-875d-99396782d95a.png) - - - - - I'd appreciate reviews from JavaScript experts. Thanks! Commits ------- 329a74f [WebProfilerBundle] Assign automatic colors to custom Stopwatch categories
2 parents c403706 + 329a74f commit bed6511

File tree

3 files changed

+75
-65
lines changed

3 files changed

+75
-65
lines changed

src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/time.css.twig

Lines changed: 0 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,3 @@
1-
/* Variables */
2-
3-
.sf-profiler-timeline {
4-
--color-default: #777;
5-
--color-section: #999;
6-
--color-event-listener: #00B8F5;
7-
--color-template: #66CC00;
8-
--color-doctrine: #FF6633;
9-
--color-messenger-middleware: #BDB81E;
10-
--color-controller-argument-value-resolver: #8c5de6;
11-
}
12-
131
/* Legend */
142

153
.sf-profiler-timeline .legends .timeline-category {
@@ -31,14 +19,6 @@
3119
display: inline-block;
3220
}
3321

34-
.sf-profiler-timeline .legends .{{ classnames.default|raw }} { border-color: var(--color-default); }
35-
.sf-profiler-timeline .legends .{{ classnames.section|raw }} { border-color: var(--color-section); }
36-
.sf-profiler-timeline .legends .{{ classnames.event_listener|raw }} { border-color: var(--color-event-listener); }
37-
.sf-profiler-timeline .legends .{{ classnames.template|raw }} { border-color: var(--color-template); }
38-
.sf-profiler-timeline .legends .{{ classnames.doctrine|raw }} { border-color: var(--color-doctrine); }
39-
.sf-profiler-timeline .legends .{{ classnames['messenger.middleware']|raw }} { border-color: var(--color-messenger-middleware); }
40-
.sf-profiler-timeline .legends .{{ classnames['controller.argument_value_resolver']|raw }} { border-color: var(--color-controller-argument-value-resolver); }
41-
4222
.timeline-graph {
4323
margin: 1em 0;
4424
width: 100%;
@@ -82,24 +62,3 @@
8262
.timeline-graph .timeline-period {
8363
stroke-width: 0;
8464
}
85-
.timeline-graph .{{ classnames.default|raw }} .timeline-period {
86-
fill: var(--color-default);
87-
}
88-
.timeline-graph .{{ classnames.section|raw }} .timeline-period {
89-
fill: var(--color-section);
90-
}
91-
.timeline-graph .{{ classnames.event_listener|raw }} .timeline-period {
92-
fill: var(--color-event-listener);
93-
}
94-
.timeline-graph .{{ classnames.template|raw }} .timeline-period {
95-
fill: var(--color-template);
96-
}
97-
.timeline-graph .{{ classnames.doctrine|raw }} .timeline-period {
98-
fill: var(--color-doctrine);
99-
}
100-
.timeline-graph .{{ classnames['messenger.middleware']|raw }} .timeline-period {
101-
fill: var(--color-messenger-middleware);
102-
}
103-
.timeline-graph .{{ classnames['controller.argument_value_resolver']|raw }} .timeline-period {
104-
fill: var(--color-controller-argument-value-resolver);
105-
}

src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/time.html.twig

Lines changed: 8 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,6 @@
22

33
{% import _self as helper %}
44

5-
{% set classnames = {
6-
'default': 'timeline-category-default',
7-
'section': 'timeline-category-section',
8-
'event_listener': 'timeline-category-event-listener',
9-
'template': 'timeline-category-template',
10-
'doctrine': 'timeline-category-doctrine',
11-
'messenger.middleware': 'timeline-category-messenger-middleware',
12-
'controller.argument_value_resolver': 'timeline-category-controller-argument-value-resolver',
13-
} %}
14-
155
{% block toolbar %}
166
{% set has_time_events = collector.events|length > 0 %}
177
{% set total_time = has_time_events ? '%.0f'|format(collector.duration) : 'n/a' %}
@@ -128,7 +118,7 @@
128118
</h3>
129119
{% endif %}
130120

131-
{{ helper.display_timeline(token, classnames, collector.events, collector.events.__section__.origin) }}
121+
{{ helper.display_timeline(token, collector.events, collector.events.__section__.origin) }}
132122

133123
{% if profile.children|length %}
134124
<p class="help">Note: sections with a striped background correspond to sub-requests.</p>
@@ -142,7 +132,7 @@
142132
<small>{{ events.__section__.duration }} ms</small>
143133
</h4>
144134

145-
{{ helper.display_timeline(child.token, classnames, events, collector.events.__section__.origin) }}
135+
{{ helper.display_timeline(child.token, events, collector.events.__section__.origin) }}
146136
{% endfor %}
147137
{% endif %}
148138

@@ -154,7 +144,7 @@
154144
</defs>
155145
</svg>
156146
<style type="text/css">
157-
{% include '@WebProfiler/Collector/time.css.twig' with classnames %}
147+
{% include '@WebProfiler/Collector/time.css.twig' %}
158148
</style>
159149
<script>
160150
{% include '@WebProfiler/Collector/time.js' %}
@@ -202,16 +192,19 @@
202192
{% endautoescape %}
203193
{% endmacro %}
204194

205-
{% macro display_timeline(token, classnames, events, origin) %}
195+
{% macro display_timeline(token, events, origin) %}
206196
{% import _self as helper %}
207197
<div class="sf-profiler-timeline">
208198
<div id="legend-{{ token }}" class="legends"></div>
209199
<svg id="timeline-{{ token }}" class="timeline-graph"></svg>
210200
<script>{% autoescape 'js' %}
211201
window.addEventListener('load', function onLoad() {
202+
const theme = new Theme();
203+
212204
new TimelineEngine(
205+
theme,
213206
new SvgRenderer(document.getElementById('timeline-{{ token }}')),
214-
new Legend(document.getElementById('legend-{{ token }}'), {{ classnames|json_encode|raw }}),
207+
new Legend(document.getElementById('legend-{{ token }}'), theme),
215208
document.getElementById('threshold'),
216209
{{ helper.dump_request_data(token, events, origin) }}
217210
);

src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/time.js

Lines changed: 67 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,16 @@
22

33
class TimelineEngine {
44
/**
5+
* @param {Theme} theme
56
* @param {Renderer} renderer
67
* @param {Legend} legend
78
* @param {Element} threshold
89
* @param {Object} request
910
* @param {Number} eventHeight
1011
* @param {Number} horizontalMargin
1112
*/
12-
constructor(renderer, legend, threshold, request, eventHeight = 36, horizontalMargin = 10) {
13+
constructor(theme, renderer, legend, threshold, request, eventHeight = 36, horizontalMargin = 10) {
14+
this.theme = theme;
1315
this.renderer = renderer;
1416
this.legend = legend;
1517
this.threshold = threshold;
@@ -81,7 +83,7 @@ class TimelineEngine {
8183
const lines = periods.map(period => this.createPeriod(period, category));
8284
const label = this.createLabel(this.getShortName(name), duration, memory, periods[0]);
8385
const title = this.renderer.createTitle(name);
84-
const group = this.renderer.group([title, border, label].concat(lines), this.legend.getClassname(event.category));
86+
const group = this.renderer.group([title, border, label].concat(lines), this.theme.getCategoryColor(event.category));
8587

8688
event.elements = Object.assign(event.elements || {}, { group, label, border });
8789

@@ -100,7 +102,7 @@ class TimelineEngine {
100102
}
101103

102104
createPeriod(period, category) {
103-
const timeline = this.renderer.createPath(null, 'timeline-period');
105+
const timeline = this.renderer.createPath(null, 'timeline-period', this.theme.getCategoryColor(category));
104106

105107
period.draw = category === 'section' ? this.renderer.setSectionLine : this.renderer.setPeriodLine;
106108
period.elements = Object.assign(period.elements || {}, { timeline });
@@ -213,14 +215,14 @@ class TimelineEngine {
213215
}
214216

215217
class Legend {
216-
constructor(element, classnames) {
218+
constructor(element, theme) {
217219
this.element = element;
218-
this.classnames = classnames;
220+
this.theme = theme;
219221

220222
this.toggle = this.toggle.bind(this);
221223
this.createCategory = this.createCategory.bind(this);
222224

223-
this.categories = Array.from(Object.keys(classnames)).map(this.createCategory);
225+
this.categories = Array.from(this.theme.getDefaultCategories()).map(this.createCategory);
224226
}
225227

226228
add(category) {
@@ -229,8 +231,8 @@ class Legend {
229231

230232
createCategory(category) {
231233
const element = document.createElement('button');
232-
233-
element.className = `timeline-category ${this.getClassname(category)} active`;
234+
element.className = `timeline-category active`;
235+
element.style.borderColor = this.theme.getCategoryColor(category);
234236
element.innerText = category;
235237
element.value = category;
236238
element.type = 'button';
@@ -390,13 +392,17 @@ class SvgRenderer {
390392
return element;
391393
}
392394

393-
createPath(path = null, className = null) {
395+
createPath(path = null, className = null, color = null) {
394396
const element = this.create('path', className);
395397

396398
if (path) {
397399
element.setAttribute('d', path);
398400
}
399401

402+
if (color) {
403+
element.setAttribute('fill', color);
404+
}
405+
400406
return element;
401407
}
402408

@@ -410,3 +416,55 @@ class SvgRenderer {
410416
return element;
411417
}
412418
}
419+
420+
class Theme {
421+
constructor(element) {
422+
this.reservedCategoryColors = {
423+
'default': '#777',
424+
'section': '#999',
425+
'event_listener': '#00b8f5',
426+
'template': '#66cc00',
427+
'doctrine': '#ff6633',
428+
'messenger_middleware': '#bdb81e',
429+
'controller.argument_value_resolver': '#8c5de6',
430+
};
431+
432+
this.customCategoryColors = [
433+
'#dbab09', // dark yellow
434+
'#ea4aaa', // pink
435+
'#964b00', // brown
436+
'#22863a', // dark green
437+
'#0366d6', // dark blue
438+
'#17a2b8', // teal
439+
];
440+
441+
this.getCategoryColor = this.getCategoryColor.bind(this);
442+
this.getDefaultCategories = this.getDefaultCategories.bind(this);
443+
}
444+
445+
getDefaultCategories() {
446+
return Object.keys(this.reservedCategoryColors);
447+
}
448+
449+
getCategoryColor(category) {
450+
return this.reservedCategoryColors[category] || this.getRandomColor(category);
451+
}
452+
453+
getRandomColor(category) {
454+
// instead of pure randomness, colors are assigned deterministically based on the
455+
// category name, to ensure that each custom category always displays the same color
456+
return this.customCategoryColors[this.hash(category) % this.customCategoryColors.length];
457+
}
458+
459+
// copied from https://github.com/darkskyapp/string-hash
460+
hash(string) {
461+
var hash = 5381;
462+
var i = string.length;
463+
464+
while(i) {
465+
hash = (hash * 33) ^ string.charCodeAt(--i);
466+
}
467+
468+
return hash >>> 0;
469+
}
470+
}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy