8383 background-color : var (--bulma-background );
8484 border-radius : var (--bulma-radius );
8585 }
86+ .search-container {
87+ position : sticky;
88+ top : 0 ;
89+ z-index : 100 ;
90+ }
91+ .search-dropdown {
92+ position : absolute;
93+ top : 100% ;
94+ left : 0 ;
95+ right : 0 ;
96+ z-index : 1000 ;
97+ border : 1px solid var (--bulma-border );
98+ border-radius : var (--bulma-radius );
99+ background : var (--bulma-scheme-main );
100+ box-shadow : var (--bulma-shadow );
101+ max-height : 400px ;
102+ overflow-y : auto;
103+ margin-top : 4px ;
104+ }
86105 </ style >
87106{% endblock %}
88107
98117
99118< div class ="resizable-container ">
100119 < div id ="left-pane " class ="left-pane px-2 ">
120+ < div class ="mb-3 search-container ">
121+ < div class ="field has-addons ">
122+ < div class ="control has-icons-left is-expanded ">
123+ < input
124+ id ="file-search-input "
125+ class ="input is-small "
126+ type ="text "
127+ placeholder ="Go to file... "
128+ autocomplete ="off "
129+ hx-get ="{% url 'codebase_resource_search' project.slug %} "
130+ hx-target ="#search-results "
131+ hx-trigger ="input changed "
132+ hx-include ="this "
133+ name ="search "
134+ >
135+ < span class ="icon is-small is-left ">
136+ < i class ="fas fa-search "> </ i >
137+ </ span >
138+ </ div >
139+ < div class ="control ">
140+ < button id ="clear-search " class ="button is-small " type ="button ">
141+ < span class ="icon is-small ">
142+ < i class ="fas fa-times "> </ i >
143+ </ span >
144+ </ button >
145+ </ div >
146+ </ div >
147+ < div id ="search-results " class ="search-dropdown is-hidden "> </ div >
148+ </ div >
149+
101150 < div id ="resource-tree ">
102151 {% include "scanpipe/panels/codebase_tree_panel.html" with children=children path=path %}
103152 </ div >
219268 document . body . style . userSelect = '' ;
220269 }
221270 } ) ;
271+
272+ const searchInput = document . getElementById ( 'file-search-input' ) ;
273+ const searchResults = document . getElementById ( 'search-results' ) ;
274+ const clearSearchBtn = document . getElementById ( 'clear-search' ) ;
275+
276+ function toggleSearchResults ( show = null ) {
277+ const shouldShow = show !== null ? show : searchInput . value . trim ( ) ;
278+ searchResults . classList . toggle ( 'is-hidden' , ! shouldShow ) ;
279+ }
280+
281+ function clearSearch ( ) {
282+ searchInput . value = '' ;
283+ toggleSearchResults ( false ) ;
284+ searchInput . focus ( ) ;
285+ }
286+
287+ function handleSearchResultClick ( searchResultItem ) {
288+ const path = searchResultItem . dataset . path ;
289+
290+ clearSearch ( ) ;
291+
292+ fetch ( `{% url 'codebase_resource_table' project.slug %}?path=${ encodeURIComponent ( path ) } ` )
293+ . then ( response => response . text ( ) )
294+ . then ( html => {
295+ document . getElementById ( 'right-pane' ) . innerHTML = html ;
296+ htmx . process ( document . getElementById ( 'right-pane' ) ) ;
297+ if ( typeof enableCopyToClipboard === 'function' ) {
298+ enableCopyToClipboard ( '.copy-to-clipboard' ) ;
299+ }
300+ const newUrl = `{% url 'codebase_resource_tree' project.slug %}?path=${ encodeURIComponent ( path ) } ` ;
301+ window . history . pushState ( null , '' , newUrl ) ;
302+ expandToPath ( path ) ;
303+ } ) ;
304+ }
305+
306+ searchInput . addEventListener ( 'focus' , ( ) => toggleSearchResults ( ) ) ;
307+ searchInput . addEventListener ( 'input' , ( ) => toggleSearchResults ( ) ) ;
308+
309+ clearSearchBtn . addEventListener ( 'click' , clearSearch ) ;
310+
311+ searchInput . addEventListener ( 'keydown' , function ( e ) {
312+ if ( e . key === 'Escape' ) {
313+ clearSearch ( ) ;
314+ }
315+ } ) ;
316+
317+ document . addEventListener ( 'click' , function ( e ) {
318+ const searchResultItem = e . target . closest ( '.dropdown-item' ) ;
319+ if ( searchResultItem ) {
320+ e . preventDefault ( ) ;
321+ handleSearchResultClick ( searchResultItem ) ;
322+ return ;
323+ }
324+
325+ if ( ! searchInput . contains ( e . target ) && ! searchResults . contains ( e . target ) ) {
326+ toggleSearchResults ( false ) ;
327+ }
328+ } ) ;
329+
330+ document . body . addEventListener ( 'htmx:afterSettle' , function ( evt ) {
331+ if ( evt . target === searchResults ) {
332+ toggleSearchResults ( ) ;
333+ }
334+ } ) ;
222335 } ) ;
223336 </ script >
224337{% endblock %}
0 commit comments