Skip to content

Commit e27d533

Browse files
committed
Add JS for SQLite query display in QM 4.0 shadow DOM
Inject <details> elements into QM 4.0's Preact-rendered DB queries panel inside the shadow DOM. Waits for DOMContentLoaded so QM has attached the shadow root, then uses a debounced MutationObserver to re-inject after panel switches and re-renders.
1 parent 59553d6 commit e27d533

1 file changed

Lines changed: 81 additions & 0 deletions

File tree

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
const STYLE = `
2+
details.qm-sqlite {
3+
margin: 6px 0 0;
4+
}
5+
details.qm-sqlite summary {
6+
cursor: pointer;
7+
}
8+
details.qm-sqlite ol {
9+
margin: 6px 0 0;
10+
padding-left: 24px;
11+
list-style: decimal;
12+
}
13+
`;
14+
15+
const container = document.getElementById( 'query-monitor-container' );
16+
const sqliteData = window.QueryMonitorData?.data?.sqlite?.data?.queries;
17+
18+
if ( container && sqliteData ) {
19+
// QM attaches the shadow root in its own DOMContentLoaded listener.
20+
// Our module is loaded after QM's, so our listener fires after QM's.
21+
document.addEventListener( 'DOMContentLoaded', () => {
22+
const shadowRoot = container.shadowRoot;
23+
if ( ! shadowRoot ) {
24+
return;
25+
}
26+
inject( shadowRoot, sqliteData );
27+
28+
// Re-inject after Preact re-renders (panel switches, filters, etc.).
29+
// Debounced to avoid excessive work during rapid DOM updates.
30+
let timer;
31+
new MutationObserver( () => {
32+
clearTimeout( timer );
33+
timer = setTimeout( () => inject( shadowRoot, sqliteData ), 100 );
34+
} ).observe( shadowRoot, { childList: true, subtree: true } );
35+
} );
36+
}
37+
38+
function inject( shadowRoot, data ) {
39+
if ( ! shadowRoot.querySelector( 'style.qm-sqlite-style' ) ) {
40+
const style = document.createElement( 'style' );
41+
style.className = 'qm-sqlite-style';
42+
style.textContent = STYLE;
43+
shadowRoot.appendChild( style );
44+
}
45+
46+
for ( const [ index, row ] of shadowRoot.querySelectorAll( 'tbody tr' ).entries() ) {
47+
injectRow( row, data[ index ] );
48+
}
49+
}
50+
51+
function injectRow( row, queries ) {
52+
if ( ! queries?.length || row.querySelector( 'details.qm-sqlite' ) ) {
53+
return;
54+
}
55+
const sqlCode = row.querySelector( 'td code' );
56+
if ( ! sqlCode ) {
57+
return;
58+
}
59+
60+
const details = document.createElement( 'details' );
61+
details.className = 'qm-sqlite';
62+
63+
// Prevent QM's row click handlers from firing when toggling.
64+
details.addEventListener( 'click', ( e ) => e.stopPropagation() );
65+
66+
const summary = document.createElement( 'summary' );
67+
summary.textContent = `Executed ${ queries.length } SQLite ${ queries.length === 1 ? 'Query' : 'Queries' }`;
68+
69+
const ol = document.createElement( 'ol' );
70+
for ( const sql of queries ) {
71+
const li = document.createElement( 'li' );
72+
li.className = 'qm-sqlite-query';
73+
const code = document.createElement( 'code' );
74+
code.textContent = sql;
75+
li.append( code );
76+
ol.append( li );
77+
}
78+
79+
details.append( summary, ol );
80+
sqlCode.parentElement.append( details );
81+
}

0 commit comments

Comments
 (0)