Skip to content

Commit 4713877

Browse files
authored
Revamp components inspection (Octane support and improved UX) (#1088)
* Add a polyfill for the `captureRenderTree` API * GlimmerTree -> RenderTree * [WIP] view inspecting * Correctly insert outlet nodes * Wire-up more of the UI * Get pinning working * Fix vertical-collection * Fix object inspection * Fix polyfill * Tooltip positionining Closes #1089 * Use polyfill for 3.14.0/3.14.1 * Set `EmberENV._DEBUG_RENDER_TREE` * Some improvements to the tooltip * Refactor render tree to use Octane idioms * Fix debouncing, don't cause a ton of user-runloops * Don't shadow methods (requested by @rwjblue) * Implement remaining features * Automatically scroll to the appropiate row * Make sure the view inspection tooltips and the inspector tab stays in sync * Fix pinning * Some performance improvements * General cleanup, ensure correct teardown * Revert to updating component tree with instrumentation While the backburner-based approach is technically more correct, it turns out that ember in practice always fire the instrumentation events due to the route templates (including the internal -top-level) being instrumented by default. This gives more favorable results since we start way too many runloops when we shouldn't have to. * Don't show empty rects * Fix app-picker tests * remove unused code * Fix component tests * Fix view debug tests * Fix style lint * PR comments * Fix 3.4 outlet template location * Fix @model arg on 3.14 * Skip debounce when requesting the first render tree * unify view debug and component tree tests * Refactor render-tree * Refactor view-inspection
1 parent ca4eedd commit 4713877

31 files changed

+2705
-1905
lines changed

app/components/scroll-container.js

+79
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
import Component from '@ember/component';
2+
import { debounce } from '@ember/runloop';
3+
import { htmlSafe } from '@ember/string';
4+
import { tracked } from '@glimmer/tracking';
5+
6+
export default class ScrollContainerComponent extends Component {
7+
attributeBindings = ['style'];
8+
9+
@tracked collection;
10+
@tracked currentItem;
11+
@tracked itemHeight;
12+
13+
lastIndex = -1;
14+
lastItem = undefined;
15+
16+
get style() {
17+
return htmlSafe(`
18+
position: relative;
19+
height: 100%;
20+
`);
21+
}
22+
23+
get scrollTargetStyle() {
24+
let { index, itemHeight } = this;
25+
26+
if (index === -1) {
27+
return htmlSafe('display: none;');
28+
} else {
29+
return htmlSafe(`
30+
position: absolute;
31+
width: 100%;
32+
height: ${itemHeight || 0}px;
33+
margin: 0px;
34+
padding: 0px;
35+
top: ${index * itemHeight || 0}px;
36+
left: 0px;
37+
z-index: -9999;
38+
pointer-events: none;
39+
`);
40+
}
41+
}
42+
43+
get index() {
44+
return this.collection.indexOf(this.currentItem);
45+
}
46+
47+
get scrollTarget() {
48+
return this.element.querySelector('.scroll-target');
49+
}
50+
51+
didRender() {
52+
let { index, lastIndex, currentItem, lastItem } = this;
53+
54+
if (index !== lastIndex || currentItem !== lastItem) {
55+
this.lastIndex = index;
56+
this.lastItem = currentItem;
57+
58+
debounce(this, this.scrollIntoViewIfNeeded, 50);
59+
}
60+
}
61+
62+
scrollIntoViewIfNeeded() {
63+
let { element, scrollTarget } = this;
64+
65+
if (needsScroll(element, scrollTarget)) {
66+
scrollTarget.scrollIntoView({
67+
behavior: 'auto',
68+
block: 'nearest',
69+
inline: 'nearest'
70+
});
71+
}
72+
}
73+
}
74+
75+
function needsScroll(container, target) {
76+
let { top: containerTop, bottom: containerBottom } = container.getBoundingClientRect();
77+
let { top: targetTop, bottom: targetBottom } = target.getBoundingClientRect();
78+
return targetTop < containerTop || targetBottom > containerBottom;
79+
}

app/controllers/application.js

+25-5
Original file line numberDiff line numberDiff line change
@@ -66,12 +66,32 @@ export default Controller.extend({
6666
this.port.send('objectInspector:releaseObject', { objectId: item.objectId });
6767
}),
6868

69+
showInspector: action(function() {
70+
if (this.inspectorExpanded === false) {
71+
this.set('inspectorExpanded', true);
72+
// Broadcast that tables have been resized (used by `x-list`).
73+
schedule('afterRender', () => {
74+
this.layoutService.trigger('resize', { source: 'object-inspector' });
75+
});
76+
}
77+
}),
78+
79+
hideInspector: action(function() {
80+
if (this.inspectorExpanded === true) {
81+
this.set('inspectorExpanded', false);
82+
// Broadcast that tables have been resized (used by `x-list`).
83+
schedule('afterRender', () => {
84+
this.layoutService.trigger('resize', { source: 'object-inspector' });
85+
});
86+
}
87+
}),
88+
6989
toggleInspector: action(function() {
70-
this.toggleProperty('inspectorExpanded');
71-
// Broadcast that tables have been resized (used by `x-list`).
72-
schedule('afterRender', () => {
73-
this.layoutService.trigger('resize', { source: 'object-inspector' });
74-
});
90+
if (this.inspectorExpanded) {
91+
this.hideInspector();
92+
} else {
93+
this.showInspector();
94+
}
7595
}),
7696

7797
setActive: action(function(bool) {

0 commit comments

Comments
 (0)