11import { bootstrap as bundled_bootstrap } from "@tabler/core" ;
2+ import type * as Leaflet from "leaflet" ;
23import { add_init_fn } from "./init.ts" ;
34
45// A page may load its own Bootstrap; prefer it over the bundled copy.
@@ -186,6 +187,15 @@ function sqlpage_table() {
186187 }
187188}
188189
190+ // Leaflet is loaded from a CDN by sqlpage_map, so it is a global, not an import.
191+ declare const L : typeof Leaflet ;
192+
193+ type MarkerStyle = Leaflet . MarkerOptions &
194+ Leaflet . PathOptions & {
195+ /** A GeoJSON feature may size its own icon, overriding the SVG's width. */
196+ size ?: number | string ;
197+ } ;
198+
189199let is_leaflet_injected = false ;
190200let is_leaflet_loaded = false ;
191201
@@ -214,11 +224,9 @@ function sqlpage_map() {
214224 if ( first_map && is_leaflet_loaded ) {
215225 onLeafletLoad ( ) ;
216226 }
217- /**
218- */
219227 function parseCoords (
220228 coords : string | undefined ,
221- ) : [ number , number ] | undefined {
229+ ) : Leaflet . LatLngTuple | undefined {
222230 if ( ! coords ) return undefined ;
223231 const parsed = coords . split ( "," , 2 ) . map ( ( c ) => Number . parseFloat ( c ) ) ;
224232 if ( parsed . length !== 2 || ! parsed . every ( Number . isFinite ) ) {
@@ -241,76 +249,98 @@ function sqlpage_map() {
241249 const center = parseCoords ( m . dataset . center ) ;
242250 if ( tile_source )
243251 L . tileLayer ( tile_source , { attribution, maxZoom } ) . addTo ( map ) ;
244- map . _sqlpage_markers = [ ] ;
245- for ( const marker_elem of m . getElementsByClassName ( "marker" ) ) {
246- setTimeout ( addMarker , 0 , marker_elem , map ) ;
252+ const markers : ( Leaflet . Marker | Leaflet . GeoJSON ) [ ] = [ ] ;
253+ for ( const marker_elem of m . querySelectorAll < HTMLElement > ( ".marker" ) ) {
254+ setTimeout ( ( ) => {
255+ const marker = addMarker ( marker_elem , map ) ;
256+ if ( marker ) markers . push ( marker ) ;
257+ } , 0 ) ;
247258 }
248259 setTimeout ( ( ) => {
249260 if ( center ) map . setView ( center , zoom ) ;
250261 else {
251- const markerBounds = ( m ) =>
252- m . getLatLng ? m . getLatLng ( ) : m . getBounds ( ) ;
253- const bounds = map . _sqlpage_markers . map ( markerBounds ) ;
254- if ( bounds . length > 0 ) map . fitBounds ( bounds ) ;
262+ const bounds = L . latLngBounds ( [ ] ) ;
263+ for ( const marker of markers )
264+ bounds . extend (
265+ marker instanceof L . Marker
266+ ? marker . getLatLng ( )
267+ : marker . getBounds ( ) ,
268+ ) ;
269+ if ( markers . length > 0 ) map . fitBounds ( bounds ) ;
255270 else map . setView ( [ 51.505 , 10 ] , zoom ) ;
256271 if ( ! Number . isNaN ( zoom ) ) map . setZoom ( zoom ) ;
257272 }
258273 } , 100 ) ;
259274 m . removeAttribute ( "data-pre-init" ) ;
260- m . getElementsByClassName ( " spinner-border") [ 0 ] ?. remove ( ) ;
275+ m . querySelector ( ". spinner-border") ?. remove ( ) ;
261276 }
262277 }
263278
264- function addMarker ( marker_elem , map ) {
265- const { dataset } = marker_elem ;
266- const options = {
267- color : marker_elem . dataset . color ,
268- title : marker_elem . getElementsByTagName ( "h3" ) [ 0 ] . textContent . trim ( ) ,
279+ function addMarker ( marker_elem : HTMLElement , map : Leaflet . Map ) {
280+ const { color , coords , geojson , link } = marker_elem . dataset ;
281+ const options : MarkerStyle = {
282+ color,
283+ title : marker_elem . querySelector ( "h3" ) ? .textContent ? .trim ( ) ,
269284 } ;
270- const marker = dataset . coords
285+ const marker = coords
271286 ? createMarker ( marker_elem , options )
272- : createGeoJSONMarker ( marker_elem , options ) ;
273- if ( ! marker ) return ;
287+ : geojson && createGeoJSONMarker ( marker_elem , geojson , options ) ;
288+ if ( ! marker ) return undefined ;
274289 marker . addTo ( map ) ;
275- map . _sqlpage_markers . push ( marker ) ;
276- if ( marker_elem . textContent . trim ( ) ) marker . bindPopup ( marker_elem ) ;
277- else if ( marker_elem . dataset . link ) {
290+ if ( marker_elem . textContent ?. trim ( ) ) marker . bindPopup ( marker_elem ) ;
291+ else if ( link ) {
278292 marker . on ( "click" , ( ) => {
279- window . location . href = marker_elem . dataset . link ;
293+ window . location . href = link ;
280294 } ) ;
281295 }
296+ return marker ;
282297 }
283- function createMarker ( marker_elem , options ) {
298+ function createMarker ( marker_elem : HTMLElement , options : MarkerStyle ) {
284299 const coords = parseCoords ( marker_elem . dataset . coords ) ;
285- if ( ! coords ) return undefined ;
286- const icon_obj = marker_elem . getElementsByClassName ( "mapicon" ) [ 0 ] ;
287- if ( icon_obj ) {
288- const size =
289- 1.5 *
290- + ( options . size || icon_obj . firstChild ?. getAttribute ( "width" ) || 24 ) ;
291- options . icon = L . divIcon ( {
292- html : icon_obj ,
293- className : `border-0 bg-${ options . color || "primary" } bg-gradient text-white rounded-circle shadow d-flex justify-content-center align-items-center` ,
294- iconSize : [ size , size ] ,
295- iconAnchor : [ size / 2 , size / 2 ] ,
296- } ) ;
297- }
298- return L . marker ( coords , options ) ;
300+ return coords && createMarkerAt ( marker_elem , coords , options ) ;
299301 }
300- function createGeoJSONMarker ( marker_elem , options ) {
301- const geojson = JSON . parse ( marker_elem . dataset . geojson ) ;
302+ function createMarkerAt (
303+ marker_elem : HTMLElement ,
304+ coords : Leaflet . LatLngTuple ,
305+ options : MarkerStyle ,
306+ ) {
307+ const icon_obj = marker_elem . querySelector < HTMLElement > ( ".mapicon" ) ;
308+ if ( ! icon_obj ) return L . marker ( coords , options ) ;
309+ const size =
310+ 1.5 *
311+ + (
312+ options . size ||
313+ icon_obj . firstElementChild ?. getAttribute ( "width" ) ||
314+ 24
315+ ) ;
316+ const icon = L . divIcon ( {
317+ html : icon_obj ,
318+ className : `border-0 bg-${ options . color || "primary" } bg-gradient text-white rounded-circle shadow d-flex justify-content-center align-items-center` ,
319+ iconSize : [ size , size ] ,
320+ iconAnchor : [ size / 2 , size / 2 ] ,
321+ } ) ;
322+ return L . marker ( coords , { ...options , icon } ) ;
323+ }
324+ function createGeoJSONMarker (
325+ marker_elem : HTMLElement ,
326+ geojson : string ,
327+ options : MarkerStyle ,
328+ ) {
302329 if ( options . color ) {
303330 options . color = get_tabler_color ( options . color ) || options . color ;
304331 }
305- function style ( { properties } ) {
306- if ( typeof properties !== "object" ) return options ;
307- return { ...options , ...properties } ;
308- }
309- function pointToLayer ( feature , latlng ) {
310- marker_elem . dataset . coords = `${ latlng . lat } ,${ latlng . lng } ` ;
311- return createMarker ( marker_elem , { ...options , ...feature . properties } ) ;
312- }
313- return L . geoJSON ( geojson , { style, pointToLayer } ) ;
332+ return L . geoJSON ( JSON . parse ( geojson ) , {
333+ style : ( feature ) => {
334+ const properties = feature ?. properties ;
335+ if ( typeof properties !== "object" ) return options ;
336+ return { ...options , ...properties } ;
337+ } ,
338+ pointToLayer : ( feature , latlng ) =>
339+ createMarkerAt ( marker_elem , [ latlng . lat , latlng . lng ] , {
340+ ...options ,
341+ ...feature . properties ,
342+ } ) ,
343+ } ) ;
314344 }
315345}
316346
0 commit comments