Skip to content

Commit 474629e

Browse files
committed
Add getRect to element interface
1 parent a401df0 commit 474629e

5 files changed

Lines changed: 76 additions & 57 deletions

File tree

packages/debugger/package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,6 @@
5252
"@nothing-but/utils": "~0.17.0",
5353
"@solid-devtools/shared": "workspace:^",
5454
"@solid-primitives/bounds": "^0.1.0",
55-
"@solid-primitives/cursor": "^0.1.0",
5655
"@solid-primitives/event-listener": "^2.4.0",
5756
"@solid-primitives/keyboard": "^1.3.0",
5857
"@solid-primitives/platform": "^0.2.0",

packages/debugger/src/locator/element-overlay.tsx

Lines changed: 62 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,70 @@
11
import * as s from 'solid-js'
22
import * as sweb from 'solid-js/web'
3-
import {createElementBounds} from '@solid-primitives/bounds'
4-
import {createElementCursor} from '@solid-primitives/cursor'
53
import {createRootPool} from '@solid-primitives/rootless'
64
import type {LocatorComponent} from './index.ts'
5+
import {UNKNOWN, type ElementInterface, type Rect} from '../types.ts'
76

8-
export function createElementsOverlay(selected: s.Accessor<LocatorComponent[]>) {
9-
const useElementOverlay = createRootPool((component: s.Accessor<LocatorComponent>, active) => (
10-
<ElementOverlay component={active() ? component() : null} />
11-
))
7+
export function createElementsOverlay<TEl extends object>(
8+
selected: s.Accessor<LocatorComponent<TEl>[]>,
9+
eli: ElementInterface<TEl>,
10+
) {
11+
12+
const useElementOverlay = createRootPool((componentRaw: s.Accessor<LocatorComponent<TEl>>, active) => {
13+
14+
const component = () => active() ? componentRaw() : null
15+
16+
const name = () => component()?.name
17+
18+
const rect = s.createMemo((prev: Rect) => {
19+
let comp = component()
20+
if (comp === null) return prev
21+
22+
let rect = eli.getRect(comp.element)
23+
if (rect === null) return prev
24+
25+
return rect
26+
}, {x: 0, y: 0, w: 0, h: 0})
27+
28+
const transform = () => `translate(${Math.round(rect().x)}px, ${Math.round(rect().y)}px)`
29+
const placeOnTop = () => rect().y > window.innerHeight / 2
30+
31+
const tag = () => {
32+
let comp = component()
33+
if (comp === null) return UNKNOWN
34+
35+
return eli.getName(comp.element) ?? UNKNOWN
36+
}
37+
38+
return (
39+
<>
40+
<style>{styles}</style>
41+
<div
42+
class="element-overlay"
43+
style={{
44+
transform: transform(),
45+
width: rect().w + 'px',
46+
height: rect().h + 'px',
47+
}}
48+
>
49+
<div class="border" />
50+
<s.Show when={name()}>
51+
<div class={`name-container ${placeOnTop() ? 'top' : 'bottom'}`}>
52+
<div class="name-animated-container">
53+
<div class="name-background"></div>
54+
<div class="name-text">
55+
{name()}: <span>{tag()}</span>
56+
</div>
57+
<div class="name-invisible">
58+
{name()}: {tag()}
59+
</div>
60+
</div>
61+
</div>
62+
</s.Show>
63+
</div>
64+
</>
65+
)
66+
67+
})
1268

1369
// wait a second to let the framework mess with the document before attaching the overlay
1470
const owner = s.getOwner()!
@@ -21,51 +77,6 @@ export function createElementsOverlay(selected: s.Accessor<LocatorComponent[]>)
2177
}, 1000)
2278
}
2379

24-
const ElementOverlay: s.Component<{component: LocatorComponent | null}> = props => {
25-
const element = () => props.component?.element
26-
// set pointer cursor to selected component
27-
createElementCursor(element, 'pointer')
28-
const tag = () => element()?.localName
29-
const name = () => props.component?.name
30-
31-
const bounds = createElementBounds(element)
32-
const left = s.createMemo<number>(prev => (bounds.left === null ? prev : bounds.left), 0)
33-
const top = s.createMemo<number>(prev => (bounds.top === null ? prev : bounds.top), 0)
34-
const width = s.createMemo<number>(prev => (bounds.width === null ? prev : bounds.width), 0)
35-
const height = s.createMemo<number>(prev => (bounds.height === null ? prev : bounds.height), 0)
36-
const transform = s.createMemo(() => `translate(${Math.round(left())}px, ${Math.round(top())}px)`)
37-
const placeOnTop = s.createMemo(() => top() > window.innerHeight / 2)
38-
39-
return (
40-
<>
41-
<style>{styles}</style>
42-
<div
43-
class="element-overlay"
44-
style={{
45-
transform: transform(),
46-
width: width() + 'px',
47-
height: height() + 'px',
48-
}}
49-
>
50-
<div class="border" />
51-
<s.Show when={name()}>
52-
<div class={`name-container ${placeOnTop() ? 'top' : 'bottom'}`}>
53-
<div class="name-animated-container">
54-
<div class="name-background"></div>
55-
<div class="name-text">
56-
{name()}: <span>{tag()}</span>
57-
</div>
58-
<div class="name-invisible">
59-
{name()}: {tag()}
60-
</div>
61-
</div>
62-
</div>
63-
</s.Show>
64-
</div>
65-
</>
66-
)
67-
}
68-
6980
const styles = /*css*/ `
7081
.element-overlay {
7182
position: fixed;

packages/debugger/src/locator/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ export function createLocator<TEl extends object>(props: {
124124
setHighlightedComponents(locator_components)
125125
})))
126126

127-
createElementsOverlay(highlightedComponents)
127+
createElementsOverlay(highlightedComponents, props.component_registry.eli)
128128

129129
// notify of component hovered by using the debugger
130130
s.createEffect((prev: NodeID | undefined) => {

packages/debugger/src/main/types.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,16 +136,24 @@ export type SourceLocation = {
136136
column: number
137137
}
138138

139+
export type Rect = {
140+
x: number
141+
y: number
142+
w: number
143+
h: number
144+
}
145+
139146
/**
140147
* When using a custom solid renderer, you should provide a custom element interface.
141148
* By default the debugger assumes that rendered elements are DOM elements.
142149
*/
143150
export type ElementInterface<T extends object> = {
144151
isElement: (obj: object | T) => obj is T,
145-
getName: (el: T) => string,
152+
getName: (el: T) => string | null,
146153
getChildren: (el: T) => Iterable<T>,
147154
getElementAt: (e: MouseEvent) => T | null,
148155
getLocation: (el: T) => SourceLocation | null,
156+
getRect: (el: T) => Rect | null,
149157
}
150158

151159
/**
@@ -161,6 +169,10 @@ export const dom_element_interface: ElementInterface<Element> = {
161169
if (attr == null) return null
162170
return locator.parseLocationString(attr) ?? null
163171
},
172+
getRect: el => {
173+
let rect = el.getBoundingClientRect()
174+
return {x: rect.x, y: rect.y, w: rect.width, h: rect.height}
175+
},
164176
}
165177

166178
//

pnpm-lock.yaml

Lines changed: 0 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)