8000 [Workflow] List place or transition listeners in profiler · symfony/symfony@f8bb17c · GitHub
[go: up one dir, main page]

Skip to content

Commit f8bb17c

Browse files
committed
[Workflow] List place or transition listeners in profiler
1 parent 5929aa1 commit f8bb17c

File tree

6 files changed

+437
-1
lines changed

6 files changed

+437
-1
lines changed

src/Symfony/Bundle/FrameworkBundle/Resources/config/workflow_debug.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222
])
2323
->args([
2424
tagged_iterator('workflow', 'name'),
25+
service('event_dispatcher'),
26+
service('debug.file_link_formatter'),
2527
])
2628
;
2729
};

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

Lines changed: 209 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,102 @@
11
{% extends '@WebProfiler/Profiler/layout.html.twig' %}
22

3+
{% block stylesheets %}
4+
{{ parent() }}
5+
<style>
6+
dialog {
7+
border: none;
8+
border-radius: 6px;
9+
box-shadow: var(--settings-modal-shadow);
10+
max-width: 94%;
11+
width: 1200px;
12+
}
13+
14+
dialog::backdrop {
15+
background: linear-gradient(
16+
45deg,
17+
rgb(18, 18, 20, 0.4),
18+
rgb(17, 17, 20, 0.8)
19+
);
20+
}
21+
22+
dialog[open] {
23+
animation: scale 0.3s ease normal;
24+
}
25+
26+
dialog[open]::backdrop {
27+
animation: backdrop 0.3s ease normal;
28+
}
29+
30+
dialog.hide {
31+
animation-direction: reverse;
32+
}
33+
34+
dialog h2 {
35+
margin-top: 0.2em
36+
}
37+
38+
dialog i.cancel {
39+
cursor: pointer;
40+
padding: 0 5px;
41+
float: right;
42+
}
43+
44+
dialog table {
45+
border: 1px solid #ccc;
46+
border-collapse: collapse;
47+
margin: 0 0 1em 0;
48+
margin-bottom: 1em;
49+
padding: 0;
50+
table-layout: fixed;
51+
}
52+
53+
dialog table tr {
54+
background-color: #f8f8f8;
55+
border: 1px solid #ddd;
56+
padding: .35em;
57+
}
58+
59+
dialog table th,
60+
dialog table td {
61+
padding: .625em;
62+
text-align: center;
63+
}
64+
65+
dialog table th {
66+
font-size: .85em;
67+
letter-spacing: .1em;
68+
text-transform: uppercase;
69+
}
70+
71+
dialog menu {
72+
padding: 0;
73+
margin: 0;
74+
display: flex;
75+
align-items: center;
76+
flex-direction: row;
77+
vertical-align: middle;
78+
justify-content: center;
79+
}
80+
81+
dialog menu small {
82+
margin-right: auto;
83+
}
84+
dialog menu small i {
85+
margin-right: 3px;
86+
}
87+
88+
@keyframes scale {
89+
from { transform: scale(0); }
90+
to { transform: scale(1); }
91+
}
92+
93+
@keyframes backdrop {
94+
from { opacity: 0; }
95+
to { opacity: 1; }
96+
}
97+
</style>
98+
{% endblock %}
99+
3100
{% block menu %}
4101
<span class="label {{ collector.workflows|length == 0 ? 'disabled' }}">
5102
<span class="icon">
@@ -23,6 +120,93 @@
23120
flowchart: { useMaxWidth: false },
24121
securityLevel: 'loose',
25122
});
123+
124+
{% for name, data in collector.workflows %}
125+
window.showNodeDetails{{ collector.hash(name) }} = function (node) {
126+
const map = {{ data.listeners|json_encode|raw }};
127+
showNodeDetails(node, map);
128+
};
129+
{% endfor %}
130+
131+
const showNodeDetails = function (node, map) {
132+
const dialog = document.getElementById('detailsDialog');
133+
134+
dialog.querySelector('tbody').innerHTML = '';
135+
for (const [eventName, listeners] of Object.entries(map[node])) {
136+
listeners.forEach(listener => {
137+
const row = document.createElement('tr');
138+
139+
const eventNameCode = document.createElement('code');
140+
eventNameCode.textContent = eventName;
141+
142+
const eventNameCell = document.createElement('td');
143+
eventNameCell.appendChild(eventNameCode);
144+
row.appendChild(eventNameCell);
145+
146+
const listenerDetailsCell = document.createElement('td');
147+
row.appendChild(listenerDetailsCell);
148+
149+
let listenerDetails;
150+
const listenerDetailsCode = document.createElement('code');
151+
listenerDetailsCode.textContent = listener.title;
152+
if (listener.file) {
153+
const link = document.createElement('a');
154+
link.href = listener.file;
155+
link.appendChild(listenerDetailsCode);
156+
listenerDetails = link;
157+
} else {
158+
listenerDetails = listenerDetailsCode;
159+
}
160+
listenerDetailsCell.appendChild(listenerDetails);
161+
162+
if (typeof listener.guardExpressions === 'object') {
163+
listenerDetailsCell.appendChild(document.createElement('br'));
164+
165+
const guardExpressionsWrapper = document.createElement('span');
166+
guardExpressionsWrapper.appendChild(document.createTextNode('guard expressions: '));
167+
168+
listener.guardExpressions.forEach((expression, index) => {
169+
if (index > 0) {
170+
guardExpressionsWrapper.appendChild(document.createTextNode(', '));
171+
}
172+
173+
const expressionCode = document.createElement('code');
174+
expressionCode.textContent = expression;
175+
guardExpressionsWrapper.appendChild(expressionCode);
176+
});
177+
178+
listenerDetailsCell.appendChild(guardExpressionsWrapper);
179+
}
180+
181+
dialog.querySelector('tbody').appendChild(row);
182+
});
183+
};
184+
185+
if (dialog.dataset.processed) {
186+
dialog.showModal();
187+
return;
188+
}
189+
190+
dialog.addEventListener('click', (e) => {
191+
const rect = dialog.getBoundingClientRect();
192+
193+
const inDialog =
194+
rect.top <= e.clientY &&
195+
e.clientY <= rect.top + rect.height &&
196+
rect.left <= e.clientX &&
197+
e.clientX <= rect.left + rect.width;
198+
199+
!inDialog && dialog.close();
200+
});
201+
202+
dialog.querySelectorAll('.cancel').forEach(elt => {
203+
elt.addEventListener('click', () => dialog.close());
204+
});
205+
206+
dialog.showModal();
207+
208+
dialog.dataset.processed = true;
209+
};
26210
// We do not load all mermaid diagrams at once, but only when the tab is opened
27211
// This is because mermaid diagrams are in a tab, and cannot be renderer with a
28212
// "good size" if they are not visible
@@ -53,10 +237,35 @@
53237
<div class="tab-content">
54238
<pre class="sf-mermaid">
55239
{{ data.dump|raw }}
240+
{% for nodeId, events in data.listeners %}
241+
click {{ nodeId }} showNodeDetails{{ collector.hash(name) }}
242+
{% endfor %}
56243
</pre>
57244
</div>
58245
</div>
59246
{% endfor %}
60247
</div>
61248
{% endif %}
249+
250+
<dialog id="detailsDialog">
251+
<h2>
252+
Event listeners
253+
<i class="cancel">×</i>
254+
</h2>
255+
256+
<table>
257+
<thead>
258+
<tr>
259+
<th>event</th>
260+
<th>listener</th>
261+
</tr>
262+
</thead>
263+
<tbody>
264+
</tbody>
265+
</table>
266+
<menu>
267+
<small><i>⌨</i> <kbd>esc</kbd></small>
268+
<button class="btn btn-sm cancel">Close</button>
269+
</menu>
270+
</dialog>
62271
{% endblock %}
Lines changed: 8 additions & 1 deletion
Loading

0 commit comments

Comments
 (0)
0