aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/dumbymap.mjs
diff options
context:
space:
mode:
Diffstat (limited to 'src/dumbymap.mjs')
-rw-r--r--src/dumbymap.mjs97
1 files changed, 50 insertions, 47 deletions
diff --git a/src/dumbymap.mjs b/src/dumbymap.mjs
index 8c1b216..56375f7 100644
--- a/src/dumbymap.mjs
+++ b/src/dumbymap.mjs
@@ -191,13 +191,13 @@ export const generateMaps = (container, {delay, mapCallback}) => {
191 .filter(l => createGeoLink(l, geoLinkCallback)) 191 .filter(l => createGeoLink(l, geoLinkCallback))
192 192
193 const isAnchorPointedBy = (link) => (anchor) => { 193 const isAnchorPointedBy = (link) => (anchor) => {
194 const mapContainer = anchor.closest('.map-container') 194 const mapContainer = anchor.closest('.mapclay')
195 const isTarget = !link.targets || link.targets.includes(mapContainer.id) 195 const isTarget = !link.targets || link.targets.includes(mapContainer.id)
196 return anchor.title === link.url.pathname && isTarget 196 return anchor.title === link.url.pathname && isTarget
197 } 197 }
198 198
199 const isAnchorVisible = (anchor) => { 199 const isAnchorVisible = (anchor) => {
200 const mapContainer = anchor.closest('.map-container') 200 const mapContainer = anchor.closest('.mapclay')
201 return insideWindow(anchor) && insideParent(anchor, mapContainer) 201 return insideWindow(anchor) && insideParent(anchor, mapContainer)
202 } 202 }
203 203
@@ -227,7 +227,7 @@ export const generateMaps = (container, {delay, mapCallback}) => {
227 } 227 }
228 228
229 const updateMapByMarker = (xy) => (marker) => { 229 const updateMapByMarker = (xy) => (marker) => {
230 const renderer = marker.closest('.map-container')?.renderer 230 const renderer = marker.closest('.mapclay')?.renderer
231 renderer.updateCamera({ center: xy }, true) 231 renderer.updateCamera({ center: xy }, true)
232 } 232 }
233 233
@@ -275,7 +275,7 @@ export const generateMaps = (container, {delay, mapCallback}) => {
275 // Placeholder for map in Showcase, it should has the same DOMRect 275 // Placeholder for map in Showcase, it should has the same DOMRect
276 const placeholder = target.cloneNode(true) 276 const placeholder = target.cloneNode(true)
277 placeholder.removeAttribute('id') 277 placeholder.removeAttribute('id')
278 placeholder.classList.remove('map-container', 'focus') 278 placeholder.classList.remove('mapclay', 'focus')
279 target.parentElement.replaceChild(placeholder, target) 279 target.parentElement.replaceChild(placeholder, target)
280 280
281 // FIXME Maybe use @start-style for CSS 281 // FIXME Maybe use @start-style for CSS
@@ -345,8 +345,8 @@ export const generateMaps = (container, {delay, mapCallback}) => {
345 345
346 // Since layout change may show/hide showcase, the current focused map should do something 346 // Since layout change may show/hide showcase, the current focused map should do something
347 // Reset attribute triggers MutationObserver which is observing it 347 // Reset attribute triggers MutationObserver which is observing it
348 const focusMap = container.querySelector('.map-container.focus') 348 const focusMap = container.querySelector('.mapclay.focus')
349 ?? container.querySelector('.map-container') 349 ?? container.querySelector('.mapclay')
350 focusMap?.classList?.add('focus') 350 focusMap?.classList?.add('focus')
351 }); 351 });
352 layoutObserver.observe(container, { 352 layoutObserver.observe(container, {
@@ -361,19 +361,32 @@ export const generateMaps = (container, {delay, mapCallback}) => {
361 //}}} 361 //}}}
362 // Render Maps {{{ 362 // Render Maps {{{
363 363
364 const afterEachMapLoaded = (renderMap) => { 364 const afterMapRendered = (renderer) => {
365 const mapContainer = renderMap.target 365 const mapElement = renderer.target
366 mapCache[mapContainer.id] = renderMap 366 mapElement.setAttribute('tabindex', "-1")
367 mapContainer.setAttribute('tabindex', "-1") 367 if (mapElement.getAttribute('data-render') === 'fulfilled') {
368 mapContainer.setAttribute('data-state', "rendered") 368 mapCache[mapElement.id] = renderer
369 }
370
371 // Execute callback from caller
372 mapCallback?.call(this, mapElement)
373 const markers = geoLinks
374 .filter(link => !link.targets || link.targets.includes(mapElement.id))
375 .map(link => ({ xy: link.xy, title: link.url.pathname }))
369 376
377 // Add markers with Geolinks
378 renderer.addMarkers(markers)
379 mapElement.querySelectorAll('.marker')
380 .forEach(marker => htmlHolder.anchors.push(marker))
381
382 // Work with Mutation Observer
370 const observer = mapFocusObserver() 383 const observer = mapFocusObserver()
371 mapFocusObserver().observe(mapContainer, { 384 mapFocusObserver().observe(mapElement, {
372 attributes: true, 385 attributes: true,
373 attributeFilter: ["class"], 386 attributeFilter: ["class"],
374 attributeOldValue: true 387 attributeOldValue: true
375 }); 388 });
376 onRemove(mapContainer, () => observer.disconnect()) 389 onRemove(mapElement, () => observer.disconnect())
377 } 390 }
378 391
379 // Set unique ID for map container 392 // Set unique ID for map container
@@ -397,6 +410,7 @@ export const generateMaps = (container, {delay, mapCallback}) => {
397 const elementsWithMapConfig = Array.from(container.querySelectorAll('pre:has(.language-map)') ?? []) 410 const elementsWithMapConfig = Array.from(container.querySelectorAll('pre:has(.language-map)') ?? [])
398 // Add default aliases into each config 411 // Add default aliases into each config
399 const configConverter = (config => ({ 412 const configConverter = (config => ({
413 use: config.use ?? 'Leaflet',
400 width: "100%", 414 width: "100%",
401 ...config, 415 ...config,
402 aliases: { 416 aliases: {
@@ -419,47 +433,36 @@ export const generateMaps = (container, {delay, mapCallback}) => {
419 configList = parseConfigsFromYaml(configText).map(assignMapId) 433 configList = parseConfigsFromYaml(configText).map(assignMapId)
420 } catch (_) { 434 } catch (_) {
421 console.warn('Fail to parse yaml config for element', target) 435 console.warn('Fail to parse yaml config for element', target)
436 return
422 } 437 }
423 438
424 // If map in cache has the same ID, and its config is the same, 439 // If map in cache has the same ID, just put it into target
425 // then don't render them again 440 // So user won't feel anything changes when editing markdown
426 configList.forEach(config => { 441 configList.forEach(config => {
427 const cache = mapCache[config.id] 442 const cache = mapCache[config.id]
428 if (cache && JSON.stringify(cache.config) === JSON.stringify(configConverter(config))) { 443 if (!cache) return;
429 target.appendChild(cache.target)
430 config.render = () => null
431 }
432 })
433 444
434 // Render maps 445 target.appendChild(cache.target)
435 render(target, configList).forEach(renderMap => { 446 config.target = cache.target
436 renderMaps.push(renderMap)
437 renderMap.promise
438 .then(_ => afterEachMapLoaded(renderMap))
439 .catch(err => console.error('Fail to render target element with ID:', renderMap.target.id, err))
440 }) 447 })
441 })
442 448
443 Promise.allSettled(renderMaps.map(r => r.promise)) 449 // trivial: if map cache is applied, do not show yaml text
444 .then(() => { 450 if (target.querySelector('.mapclay')) {
445 console.info('Finish Rendering') 451 target.querySelectorAll(':scope > :not([data-render=fulfilled])')
446 452 .forEach(e => e.remove())
447 renderMaps 453 }
448 .map(r => r.target) 454
449 .filter(target => target.getAttribute('data-state') === 'rendered') 455 // Render maps with delay
450 .forEach(ele => { 456 const timer = setTimeout(() =>
451 mapCallback(ele) 457 render(target, configList).forEach(renderMap => {
452 const markers = geoLinks 458 renderMaps.push(renderMap)
453 .filter(link => !link.targets || link.targets.includes(ele.id)) 459 renderMap.then(afterMapRendered)
454 .map(link => ({ 460 }),
455 xy: link.xy, 461 delay ?? 1000
456 title: link.url.pathname 462 )
457 })) 463 onRemove(htmlHolder, () => {
458 ele?.renderer?.addMarkers(markers) 464 clearTimeout(timer)
459 }) 465 })
460
461 htmlHolder.querySelectorAll('.marker')
462 .forEach(marker => htmlHolder.anchors.push(marker))
463 }) 466 })
464 467
465 //}}} 468 //}}}