@@ -54,7 +54,18 @@ this.ckan.module('spatial-query', function ($, _) {
5454 '<h4 class="modal-title"></h4>' ,
5555 '<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>' ,
5656 '</div>' ,
57- '<div class="modal-body"><div id="draw-map-container"></div></div>' ,
57+ `<div class="modal-body">
58+ <input class="rounded-2" id="search-address-box" type="text" placeholder="Search for an address.">
59+ <button class="btn btn-primary" type="button" id="search-address-button" disabled>Search address</button>
60+ <div id="search-dropdown" class="dropdown d-inline-flex d-none" style="width: fit-content">
61+ <button class="btn btn-secondary dropdown-toggle" type="button" id="dropdownMenu2" data-bs-toggle="dropdown" aria-expanded="false">
62+ View results
63+ </button>
64+ <ul id="search-dropdown-list" class="dropdown-menu" style="z-index: 1001;" aria-labelledby="dropdownMenu2"></ul>
65+ </div>
66+ <span id="no-results-text" class="d-none text-danger">No results found.</span>
67+ <div id="draw-map-container">
68+ </div></div>` ,
5869 '<div class="modal-footer">' ,
5970 '<button type="button" class="btn btn-secondary btn-cancel" data-bs-dismiss="modal"></button>' ,
6071 '<button type="button" class="btn btn-primary apply disabled"></button>' ,
@@ -83,6 +94,54 @@ this.ckan.module('spatial-query', function ($, _) {
8394 this . el . ready ( this . _onReady ) ;
8495 } ,
8596
97+ async runAddressSearch ( search_query ) {
98+ this . searchAddressButton . innerText = "Searching..." ;
99+ const nominatimEndpoint = `https://nominatim.openstreetmap.org/search?addressdetails=1&q=${ search_query } &format=jsonv2&limit=10` ;
100+ fetch ( nominatimEndpoint , {
101+ headers : {
102+ "User-Agent" : "Texas Water Development Hub"
103+ } ,
104+ signal : AbortSignal . timeout ( 5000 )
105+ } ) . then ( ( res ) => res . json ( ) . then ( ( data ) => {
106+ if ( data && data . length > 1 ) {
107+ this . searchResults = data . map ( ( entry ) => { return { "display_name" : entry . display_name , "boundingbox" : entry . boundingbox } ; } )
108+ // Jump to first result.
109+ const firstBoundingBox = this . searchResults [ 0 ] [ "boundingbox" ] ;
110+ this . drawMap . fitBounds ( [ [ firstBoundingBox [ 0 ] , firstBoundingBox [ 2 ] ] , [ firstBoundingBox [ 1 ] , firstBoundingBox [ 3 ] ] ] ) ;
111+ const searchDropdownList = document . getElementById ( "search-dropdown-list" ) ;
112+ // Remove previous search results
113+ while ( searchDropdownList . hasChildNodes ( ) ) {
114+ searchDropdownList . removeChild ( searchDropdownList . firstChild )
115+ }
116+ // Add search results to the dropdown
117+ for ( const entry of this . searchResults ) {
118+ const entryLi = document . createElement ( "li" ) ;
119+ const entryButton = document . createElement ( "button" ) ;
120+ entryButton . classList . add ( "dropdown-item" ) ;
121+ entryButton . type = "button" ;
122+ // entryButton.innerText = `${entry["display_name"]} | (${entry["lat"]}, ${entry["lon"]})`;
123+ entryButton . innerText = entry [ "display_name" ] ;
124+ entryButton . onclick = ( ) => {
125+ const boundingbox = entry [ "boundingbox" ] ;
126+ this . drawMap . fitBounds ( [ [ boundingbox [ 0 ] , boundingbox [ 2 ] ] , [ boundingbox [ 1 ] , boundingbox [ 3 ] ] ] ) ;
127+ }
128+ entryLi . appendChild ( entryButton ) ;
129+ searchDropdownList . appendChild ( entryLi ) ;
130+ } ;
131+ this . searchDropdown . classList . remove ( "d-none" ) ;
132+ }
133+ else if ( data && data . length > 0 ) {
134+ const boundingBox = data [ 0 ] [ "boundingbox" ] ;
135+ this . drawMap . fitBounds ( [ [ boundingBox [ 0 ] , boundingBox [ 2 ] ] , [ boundingBox [ 1 ] , boundingBox [ 3 ] ] ] ) ;
136+ } else {
137+ this . noResultsText . classList . remove ( "d-none" ) ;
138+ }
139+ } ) ) . finally ( ( ) => {
140+ this . searchAddressButton . removeAttribute ( "disabled" )
141+ this . searchAddressButton . innerText = "Search address" ;
142+ } ) ;
143+ } ,
144+
86145 _getBootstrapVersion : function ( ) {
87146 return $ . fn . modal . Constructor . VERSION . split ( "." ) [ 0 ] ;
88147 } ,
@@ -95,7 +154,7 @@ this.ckan.module('spatial-query', function ($, _) {
95154 element . modal ( { show : false } ) ;
96155
97156 element . find ( '.modal-title' ) . text ( this . _ ( 'Please draw query extent in the map:' ) ) ;
98- element . find ( '.btn-primary ' ) . text ( this . _ ( 'Apply' ) ) ;
157+ element . find ( '.apply ' ) . text ( this . _ ( 'Apply' ) ) ;
99158 element . find ( '.btn-cancel' ) . text ( this . _ ( 'Cancel' ) ) ;
100159
101160 var module = this ;
@@ -145,6 +204,41 @@ this.ckan.module('spatial-query', function ($, _) {
145204
146205 $ ( 'a.leaflet-draw-draw-rectangle>span' , element ) . trigger ( 'click' ) ;
147206 element . find ( '.btn-primary' ) . focus ( )
207+
208+ // Search address feature
209+ module . searchAddressBox = document . getElementById ( 'search-address-box' ) ;
210+ module . searchAddressButton = document . getElementById ( 'search-address-button' ) ;
211+ module . searchDropdown = document . getElementById ( "search-dropdown" ) ;
212+ module . noResultsText = document . getElementById ( "no-results-text" ) ;
213+ // Disable default enter key behavior when pressing enter in the searchbox
214+ module . searchAddressBox . onkeydown = ( e ) => {
215+ module . noResultsText . classList . add ( "d-none" ) ;
216+ module . searchDropdown . classList . add ( "d-none" ) ;
217+ if ( e . key === "Enter" && ( ! module . searchAddressButton . getAttribute ( "disabled" ) || module . searchAddressButton . getAttribute ( "disabled" ) === "false" ) ) {
218+ e ?. preventDefault ( ) ;
219+ }
220+ }
221+ module . searchAddressBox . onkeyup = ( e ) => {
222+ e ?. preventDefault ( ) ;
223+ // When there is a value in the searchbox, enable the search button
224+ if ( module . searchAddressBox . value ) {
225+ module . searchAddressButton . removeAttribute ( "disabled" )
226+ }
227+ // When the searchbox is empty, disable the search button
228+ else {
229+ module . searchAddressButton . setAttribute ( "disabled" , true )
230+ }
231+ // If the user presses the Enter key in the searchbox and the search button is not disabled, run the search
232+ if ( e . key === "Enter" && ( ! module . searchAddressButton . getAttribute ( "disabled" ) || module . searchAddressButton . getAttribute ( "disabled" ) === "false" ) ) {
233+ module . searchAddressButton . click ( ) ;
234+ }
235+ }
236+ // If the search button is clicked, disable the search button and run the search
237+ module . searchAddressButton . onclick = ( e ) => {
238+ e ?. preventDefault ( ) ;
239+ module . searchAddressButton . setAttribute ( "disabled" , true ) ;
240+ module . runAddressSearch ( module . searchAddressBox . value ) ;
241+ }
148242 } )
149243
150244 this . modal . on ( 'hidden.bs.modal' , function ( ) {
0 commit comments