diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/dumbyUtils.mjs | 6 | ||||
-rw-r--r-- | src/dumbymap.mjs | 97 |
2 files changed, 52 insertions, 51 deletions
diff --git a/src/dumbyUtils.mjs b/src/dumbyUtils.mjs index fc9eab9..c878e48 100644 --- a/src/dumbyUtils.mjs +++ b/src/dumbyUtils.mjs | |||
@@ -1,12 +1,10 @@ | |||
1 | export function focusNextMap(reverse = false) { | 1 | export function focusNextMap(reverse = false) { |
2 | const renderedList = this.renderMaps | 2 | const renderedList = Array.from(this.htmlHolder.querySelectorAll('[data-render=fulfilled]')) |
3 | .map(render => render.target) | ||
4 | .filter(ele => ele.getAttribute('data-state') === 'rendered') | ||
5 | const mapNum = renderedList.length | 3 | const mapNum = renderedList.length |
6 | if (mapNum === 0) return | 4 | if (mapNum === 0) return |
7 | 5 | ||
8 | // Get current focused map element | 6 | // Get current focused map element |
9 | const currentFocus = this.container.querySelector('.map-container.focus') | 7 | const currentFocus = this.container.querySelector('.mapclay.focus') |
10 | 8 | ||
11 | // Remove class name of focus for ALL candidates | 9 | // Remove class name of focus for ALL candidates |
12 | // This may trigger animation | 10 | // This may trigger animation |
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 | //}}} |