@@ -109,9 +109,8 @@ function installFindShortcuts() {
109109 return
110110 }
111111
112- if ( isEditable ( event . target ) ) return
113-
114- const host = hostForNode ( document . activeElement ) ?? findTarget ?? Array . from ( findHosts ) [ 0 ]
112+ const host =
113+ hostForNode ( document . activeElement ) ?? hostForNode ( event . target ) ?? findTarget ?? Array . from ( findHosts ) [ 0 ]
115114 if ( ! host ) return
116115
117116 event . preventDefault ( )
@@ -126,9 +125,11 @@ export function Code<T>(props: CodeProps<T>) {
126125 let wrapper ! : HTMLDivElement
127126 let container ! : HTMLDivElement
128127 let findInput : HTMLInputElement | undefined
128+ let findBar : HTMLDivElement | undefined
129129 let findOverlay ! : HTMLDivElement
130130 let findOverlayFrame : number | undefined
131131 let findOverlayScroll : HTMLElement [ ] = [ ]
132+ let findPositionFrame : number | undefined
132133 let observer : MutationObserver | undefined
133134 let renderToken = 0
134135 let selectionFrame : number | undefined
@@ -290,6 +291,41 @@ export function Code<T>(props: CodeProps<T>) {
290291 setFindIndex ( 0 )
291292 }
292293
294+ const getScrollParent = ( el : HTMLElement ) : HTMLElement | null => {
295+ let parent = el . parentElement
296+ while ( parent ) {
297+ const style = getComputedStyle ( parent )
298+ if ( style . overflowY === "auto" || style . overflowY === "scroll" ) return parent
299+ parent = parent . parentElement
300+ }
301+ return null
302+ }
303+
304+ const positionFindBar = ( ) => {
305+ if ( ! findBar || ! wrapper ) return
306+ const scrollParent = getScrollParent ( wrapper )
307+ if ( ! scrollParent ) {
308+ findBar . style . position = "absolute"
309+ findBar . style . top = "8px"
310+ findBar . style . right = "8px"
311+ findBar . style . left = ""
312+ return
313+ }
314+ const scrollTop = scrollParent . scrollTop
315+ findBar . style . position = "absolute"
316+ findBar . style . top = `${ scrollTop + 8 } px`
317+ findBar . style . right = "8px"
318+ findBar . style . left = ""
319+ }
320+
321+ const scheduleFindPosition = ( ) => {
322+ if ( findPositionFrame !== undefined ) return
323+ findPositionFrame = requestAnimationFrame ( ( ) => {
324+ findPositionFrame = undefined
325+ positionFindBar ( )
326+ } )
327+ }
328+
293329 const scanFind = ( root : ShadowRoot , query : string ) => {
294330 const needle = query . toLowerCase ( )
295331 const out : Range [ ] = [ ]
@@ -458,6 +494,7 @@ export function Code<T>(props: CodeProps<T>) {
458494
459495 if ( ! findOpen ( ) ) setFindOpen ( true )
460496 requestAnimationFrame ( ( ) => {
497+ positionFindBar ( )
461498 findInput ?. focus ( )
462499 findInput ?. select ( )
463500 } )
@@ -482,6 +519,25 @@ export function Code<T>(props: CodeProps<T>) {
482519 } )
483520 } )
484521
522+ createEffect ( ( ) => {
523+ if ( ! findOpen ( ) ) return
524+ const scrollParent = getScrollParent ( wrapper )
525+ const target = scrollParent ?? window
526+
527+ const handler = ( ) => scheduleFindPosition ( )
528+ target . addEventListener ( "scroll" , handler , { passive : true } )
529+ window . addEventListener ( "resize" , handler , { passive : true } )
530+
531+ onCleanup ( ( ) => {
532+ target . removeEventListener ( "scroll" , handler )
533+ window . removeEventListener ( "resize" , handler )
534+ if ( findPositionFrame !== undefined ) {
535+ cancelAnimationFrame ( findPositionFrame )
536+ findPositionFrame = undefined
537+ }
538+ } )
539+ } )
540+
485541 const applyCommentedLines = ( ranges : SelectedLineRange [ ] ) => {
486542 const root = getRoot ( )
487543 if ( ! root ) return
@@ -862,6 +918,11 @@ export function Code<T>(props: CodeProps<T>) {
862918 dragFrame = undefined
863919 }
864920
921+ if ( findPositionFrame !== undefined ) {
922+ cancelAnimationFrame ( findPositionFrame )
923+ findPositionFrame = undefined
924+ }
925+
865926 dragStart = undefined
866927 dragEnd = undefined
867928 dragMoved = false
@@ -888,19 +949,18 @@ export function Code<T>(props: CodeProps<T>) {
888949 findTarget = host
889950 } }
890951 >
891- < div ref = { container } />
892- < div ref = { findOverlay } class = "pointer-events-none absolute inset-0 z-0" />
893952 < Show when = { findOpen ( ) } >
894953 < div
895- class = "absolute top-2 right-2 z-10 flex items-center gap-1 rounded-md border border-border-weak-base bg-surface-raised-base px-2 py-1 shadow-xs-border"
954+ ref = { findBar }
955+ class = "z-50 flex h-8 items-center gap-2 rounded-md border border-border-base bg-background-base px-3 shadow-md"
896956 onPointerDown = { ( e ) => e . stopPropagation ( ) }
897957 >
898- < Icon name = "magnifying-glass" size = "small" class = "text-text-weak" />
958+ < Icon name = "magnifying-glass" size = "small" class = "text-text-weak shrink-0 " />
899959 < input
900960 ref = { findInput }
901961 placeholder = "Find"
902962 value = { findQuery ( ) }
903- class = "w-48 bg-transparent outline-none text-12 -regular text-text-strong placeholder:text-text-weak"
963+ class = "w-40 bg-transparent outline-none text-14 -regular text-text-strong placeholder:text-text-weak"
904964 onInput = { ( e ) => {
905965 setFindQuery ( e . currentTarget . value )
906966 setFindIndex ( 0 )
@@ -917,37 +977,41 @@ export function Code<T>(props: CodeProps<T>) {
917977 stepFind ( e . shiftKey ? - 1 : 1 )
918978 } }
919979 />
920- < div class = "px-1 text-12-regular text-text-weak tabular-nums" >
980+ < div class = "shrink-0 text-12-regular text-text-weak tabular-nums" >
921981 { findCount ( ) ? `${ findIndex ( ) + 1 } /${ findCount ( ) } ` : "0/0" }
922982 </ div >
983+ < div class = "flex items-center" >
984+ < button
985+ type = "button"
986+ class = "size-6 grid place-items-center rounded text-text-weak hover:bg-surface-base-hover hover:text-text-strong disabled:opacity-40 disabled:pointer-events-none"
987+ disabled = { findCount ( ) === 0 }
988+ aria-label = "Previous match"
989+ onClick = { ( ) => stepFind ( - 1 ) }
990+ >
991+ < Icon name = "chevron-down" size = "small" class = "rotate-180" />
992+ </ button >
993+ < button
994+ type = "button"
995+ class = "size-6 grid place-items-center rounded text-text-weak hover:bg-surface-base-hover hover:text-text-strong disabled:opacity-40 disabled:pointer-events-none"
996+ disabled = { findCount ( ) === 0 }
997+ aria-label = "Next match"
998+ onClick = { ( ) => stepFind ( 1 ) }
999+ >
1000+ < Icon name = "chevron-down" size = "small" />
1001+ </ button >
1002+ </ div >
9231003 < button
9241004 type = "button"
9251005 class = "size-6 grid place-items-center rounded text-text-weak hover:bg-surface-base-hover hover:text-text-strong"
926- disabled = { findCount ( ) === 0 }
927- aria-label = "Previous match"
928- onClick = { ( ) => stepFind ( - 1 ) }
929- >
930- < Icon name = "chevron-down" size = "small" class = "rotate-180" />
931- </ button >
932- < button
933- type = "button"
934- class = "size-6 grid place-items-center rounded text-text-weak hover:bg-surface-base-hover hover:text-text-strong"
935- disabled = { findCount ( ) === 0 }
936- aria-label = "Next match"
937- onClick = { ( ) => stepFind ( 1 ) }
938- >
939- < Icon name = "chevron-down" size = "small" />
940- </ button >
941- < button
942- type = "button"
943- class = "ml-1 size-6 grid place-items-center rounded text-text-weak hover:bg-surface-base-hover hover:text-text-strong"
9441006 aria-label = "Close search"
9451007 onClick = { closeFind }
9461008 >
9471009 < Icon name = "close-small" size = "small" />
9481010 </ button >
9491011 </ div >
9501012 </ Show >
1013+ < div ref = { container } />
1014+ < div ref = { findOverlay } class = "pointer-events-none absolute inset-0 z-0" />
9511015 </ div >
9521016 )
9531017}
0 commit comments