1- import { createEffect , createSignal , onCleanup } from 'solid-js'
1+ import { createEffect , createMemo , createSignal } from 'solid-js'
2+ import { createStore } from 'solid-js/store'
3+ import { createElementSize } from '@solid-primitives/resize-observer'
4+ import { useKeyDownList } from '@solid-primitives/keyboard'
5+ import { createMousePosition } from '@solid-primitives/mouse'
6+ import { createEventListener } from '@solid-primitives/event-listener'
27
38export const SourceInspector = ( ) => {
4- const [ isHighlighting , setIsHighlighting ] = createSignal ( false )
5- const [ currentElement , setCurrentElement ] = createSignal < HTMLElement | null > (
6- null ,
7- )
8- const [ currentElementBounding , setCurrentElementBounding ] = createSignal ( {
9- width : 0 ,
10- height : 0 ,
11- left : 0 ,
12- top : 0 ,
9+ const highlightStateInit = {
10+ element : null as HTMLElement | null ,
11+ bounding : { width : 0 , height : 0 , left : 0 , top : 0 } ,
12+ dataSource : '' ,
13+ }
14+
15+ const [ highlightState , setHighlightState ] = createStore ( {
16+ ...highlightStateInit ,
1317 } )
18+ const resetHighlight = ( ) => {
19+ setHighlightState ( { ...highlightStateInit } )
20+ }
1421
15- createEffect ( ( ) => {
16- const handleKeyDown = ( e : KeyboardEvent ) => {
17- const isShiftHeld = e . shiftKey
18- const isCtrlHeld = e . ctrlKey || e . metaKey
19- if ( isShiftHeld && isCtrlHeld ) {
20- setIsHighlighting ( true )
21- }
22- }
22+ const [ nameTagRef , setNameTagRef ] = createSignal < HTMLDivElement | null > ( null )
23+ const nameTagSize = createElementSize ( ( ) => nameTagRef ( ) )
2324
24- const handleKeyUp = ( e : KeyboardEvent ) => {
25- const isShiftHeld = e . shiftKey
26- const isCtrlHeld = e . ctrlKey || e . metaKey
27- if ( ! isShiftHeld || ! isCtrlHeld ) {
28- setIsHighlighting ( false )
29- setCurrentElement ( null )
30- }
25+ const mousePosition = createMousePosition ( )
26+
27+ const downList = useKeyDownList ( )
28+ const isHighlightingKeysHeld = createMemo ( ( ) => {
29+ const keys = downList ( )
30+ const isShiftHeld = keys . includes ( 'SHIFT' )
31+ const isCtrlHeld = keys . includes ( 'CONTROL' )
32+ const isMetaHeld = keys . includes ( 'META' )
33+ return isShiftHeld && ( isCtrlHeld || isMetaHeld )
34+ } )
35+
36+ createEffect ( ( ) => {
37+ if ( ! isHighlightingKeysHeld ( ) ) {
38+ resetHighlight ( )
39+ return
3140 }
32- const handleMouseMove = ( e : MouseEvent ) => {
33- if ( ! isHighlighting ( ) ) return
3441
35- const target = document . elementFromPoint ( e . clientX , e . clientY )
42+ const target = document . elementFromPoint ( mousePosition . x , mousePosition . y )
3643
37- if ( ! ( target instanceof HTMLElement ) ) {
38- return
39- }
44+ if ( ! ( target instanceof HTMLElement ) ) {
45+ resetHighlight ( )
46+ return
47+ }
4048
41- if ( target === currentElement ( ) ) {
42- return
43- }
49+ if ( target === highlightState . element ) {
50+ return
51+ }
4452
45- const dataSource = target . getAttribute ( 'data-tsd-source' )
46- if ( ! dataSource ) return
47-
48- setCurrentElement ( target )
49- const rect = target . getBoundingClientRect ( )
50- setCurrentElementBounding ( {
51- width : rect . width ,
52- height : rect . height ,
53- left : rect . left ,
54- top : rect . top ,
55- } )
53+ const dataSource = target . getAttribute ( 'data-tsd-source' )
54+ if ( ! dataSource ) {
55+ resetHighlight ( )
56+ return
5657 }
5758
58- const openSourceHandler = ( e : Event ) => {
59- if ( ! isHighlighting ( ) ) return
60-
61- if ( e . target instanceof HTMLElement ) {
62- const dataSource = e . target . getAttribute ( 'data-tsd-source' )
63- window . getSelection ( ) ?. removeAllRanges ( )
64- if ( dataSource ) {
65- e . preventDefault ( )
66- e . stopPropagation ( )
67- fetch (
68- `${ location . origin } /__tsd/open-source?source=${ encodeURIComponent (
69- dataSource ,
70- ) } `,
71- ) . catch ( ( ) => { } )
72- }
73- }
59+ const rect = target . getBoundingClientRect ( )
60+ const bounding = {
61+ width : rect . width ,
62+ height : rect . height ,
63+ left : rect . left ,
64+ top : rect . top ,
7465 }
7566
76- window . addEventListener ( 'keydown' , handleKeyDown )
77- window . addEventListener ( 'keyup' , handleKeyUp )
78- window . addEventListener ( 'mousemove' , handleMouseMove )
79- window . addEventListener ( 'click' , openSourceHandler )
80- onCleanup ( ( ) => {
81- window . removeEventListener ( 'keydown' , handleKeyDown )
82- window . removeEventListener ( 'keyup' , handleKeyUp )
83- window . removeEventListener ( 'mousemove' , handleMouseMove )
84- window . removeEventListener ( 'click' , openSourceHandler )
67+ setHighlightState ( {
68+ element : target ,
69+ bounding,
70+ dataSource,
8571 } )
8672 } )
8773
88- const currentElementBoxStyles = ( ) => {
89- const element = currentElement ( )
90- if ( element ) {
91- const bounding = currentElementBounding ( )
74+ createEventListener ( window , 'click' , ( e : Event ) => {
75+ if ( ! highlightState . element ) return
76+
77+ e . preventDefault ( )
78+ e . stopPropagation ( )
79+ fetch (
80+ `${ location . origin } /__tsd/open-source?source=${ encodeURIComponent (
81+ highlightState . dataSource ,
82+ ) } `,
83+ ) . catch ( ( ) => { } )
84+ } )
85+
86+ const currentElementBoxStyles = createMemo ( ( ) => {
87+ if ( highlightState . element ) {
9288 return {
9389 display : 'block' ,
94- width : `${ bounding . width } px` ,
95- height : `${ bounding . height } px` ,
96- left : `${ bounding . left } px` ,
97- top : `${ bounding . top } px` ,
90+ width : `${ highlightState . bounding . width } px` ,
91+ height : `${ highlightState . bounding . height } px` ,
92+ left : `${ highlightState . bounding . left } px` ,
93+ top : `${ highlightState . bounding . top } px` ,
9894
9995 'background-color' : 'oklch(55.4% 0.046 257.417 /0.25)' ,
10096 transition : 'all 0.05s linear' ,
@@ -105,9 +101,56 @@ export const SourceInspector = () => {
105101 return {
106102 display : 'none' ,
107103 }
108- }
104+ } )
105+
106+ const fileNameStyles = createMemo ( ( ) => {
107+ if ( highlightState . element && nameTagRef ( ) ) {
108+ const windowWidth = window . innerWidth
109+ const nameTagHeight = nameTagSize . height || 26
110+ const nameTagWidth = nameTagSize . width || 0
111+ let left = highlightState . bounding . left
112+ let top = highlightState . bounding . top - nameTagHeight - 4
113+
114+ if ( top < 0 ) {
115+ top = highlightState . bounding . top + highlightState . bounding . height + 4
116+ }
117+
118+ if ( left + nameTagWidth > windowWidth ) {
119+ left = windowWidth - nameTagWidth - 4
120+ }
121+
122+ if ( left < 0 ) {
123+ left = 4
124+ }
125+
126+ return {
127+ position : 'fixed' as const ,
128+ left : `${ left } px` ,
129+ top : `${ top } px` ,
130+ 'background-color' : 'oklch(55.4% 0.046 257.417 /0.80)' ,
131+ color : 'white' ,
132+ padding : '2px 4px' ,
133+ fontSize : '12px' ,
134+ 'border-radius' : '2px' ,
135+ 'z-index' : 10000 ,
136+ visibility : 'visible' as const ,
137+ transition : 'all 0.05s linear' ,
138+ }
139+ }
140+ return {
141+ display : 'none' ,
142+ }
143+ } )
109144
110145 return (
111- < div style = { { ...currentElementBoxStyles ( ) , 'pointer-events' : 'none' } } />
146+ < >
147+ < div
148+ ref = { setNameTagRef }
149+ style = { { ...fileNameStyles ( ) , 'pointer-events' : 'none' } }
150+ >
151+ { highlightState . dataSource . split ( ':' ) [ 0 ] }
152+ </ div >
153+ < div style = { { ...currentElementBoxStyles ( ) , 'pointer-events' : 'none' } } />
154+ </ >
112155 )
113156}
0 commit comments