From de3cfc866bb6c79ab431325cc91510123bc69699 Mon Sep 17 00:00:00 2001 From: Hsieh Chin Fan Date: Sat, 28 Sep 2024 17:42:09 +0800 Subject: refactor: mapclay v0.8.2 * class name from 'map-container' -> 'mapclay' * Just append rendered element when semantic HTML is generated from markdown. Reuse logic is handed by mapclay * Move logic about geolinks into afterMapRendered for each rendering (afterEachMapLoaded -> afterMapRendered) --- src/dumbyUtils.mjs | 6 ++-- src/dumbymap.mjs | 97 ++++++++++++++++++++++++++++-------------------------- 2 files changed, 52 insertions(+), 51 deletions(-) (limited to 'src') 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 @@ export function focusNextMap(reverse = false) { - const renderedList = this.renderMaps - .map(render => render.target) - .filter(ele => ele.getAttribute('data-state') === 'rendered') + const renderedList = Array.from(this.htmlHolder.querySelectorAll('[data-render=fulfilled]')) const mapNum = renderedList.length if (mapNum === 0) return // Get current focused map element - const currentFocus = this.container.querySelector('.map-container.focus') + const currentFocus = this.container.querySelector('.mapclay.focus') // Remove class name of focus for ALL candidates // 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}) => { .filter(l => createGeoLink(l, geoLinkCallback)) const isAnchorPointedBy = (link) => (anchor) => { - const mapContainer = anchor.closest('.map-container') + const mapContainer = anchor.closest('.mapclay') const isTarget = !link.targets || link.targets.includes(mapContainer.id) return anchor.title === link.url.pathname && isTarget } const isAnchorVisible = (anchor) => { - const mapContainer = anchor.closest('.map-container') + const mapContainer = anchor.closest('.mapclay') return insideWindow(anchor) && insideParent(anchor, mapContainer) } @@ -227,7 +227,7 @@ export const generateMaps = (container, {delay, mapCallback}) => { } const updateMapByMarker = (xy) => (marker) => { - const renderer = marker.closest('.map-container')?.renderer + const renderer = marker.closest('.mapclay')?.renderer renderer.updateCamera({ center: xy }, true) } @@ -275,7 +275,7 @@ export const generateMaps = (container, {delay, mapCallback}) => { // Placeholder for map in Showcase, it should has the same DOMRect const placeholder = target.cloneNode(true) placeholder.removeAttribute('id') - placeholder.classList.remove('map-container', 'focus') + placeholder.classList.remove('mapclay', 'focus') target.parentElement.replaceChild(placeholder, target) // FIXME Maybe use @start-style for CSS @@ -345,8 +345,8 @@ export const generateMaps = (container, {delay, mapCallback}) => { // Since layout change may show/hide showcase, the current focused map should do something // Reset attribute triggers MutationObserver which is observing it - const focusMap = container.querySelector('.map-container.focus') - ?? container.querySelector('.map-container') + const focusMap = container.querySelector('.mapclay.focus') + ?? container.querySelector('.mapclay') focusMap?.classList?.add('focus') }); layoutObserver.observe(container, { @@ -361,19 +361,32 @@ export const generateMaps = (container, {delay, mapCallback}) => { //}}} // Render Maps {{{ - const afterEachMapLoaded = (renderMap) => { - const mapContainer = renderMap.target - mapCache[mapContainer.id] = renderMap - mapContainer.setAttribute('tabindex', "-1") - mapContainer.setAttribute('data-state', "rendered") + const afterMapRendered = (renderer) => { + const mapElement = renderer.target + mapElement.setAttribute('tabindex', "-1") + if (mapElement.getAttribute('data-render') === 'fulfilled') { + mapCache[mapElement.id] = renderer + } + + // Execute callback from caller + mapCallback?.call(this, mapElement) + const markers = geoLinks + .filter(link => !link.targets || link.targets.includes(mapElement.id)) + .map(link => ({ xy: link.xy, title: link.url.pathname })) + // Add markers with Geolinks + renderer.addMarkers(markers) + mapElement.querySelectorAll('.marker') + .forEach(marker => htmlHolder.anchors.push(marker)) + + // Work with Mutation Observer const observer = mapFocusObserver() - mapFocusObserver().observe(mapContainer, { + mapFocusObserver().observe(mapElement, { attributes: true, attributeFilter: ["class"], attributeOldValue: true }); - onRemove(mapContainer, () => observer.disconnect()) + onRemove(mapElement, () => observer.disconnect()) } // Set unique ID for map container @@ -397,6 +410,7 @@ export const generateMaps = (container, {delay, mapCallback}) => { const elementsWithMapConfig = Array.from(container.querySelectorAll('pre:has(.language-map)') ?? []) // Add default aliases into each config const configConverter = (config => ({ + use: config.use ?? 'Leaflet', width: "100%", ...config, aliases: { @@ -419,47 +433,36 @@ export const generateMaps = (container, {delay, mapCallback}) => { configList = parseConfigsFromYaml(configText).map(assignMapId) } catch (_) { console.warn('Fail to parse yaml config for element', target) + return } - // If map in cache has the same ID, and its config is the same, - // then don't render them again + // If map in cache has the same ID, just put it into target + // So user won't feel anything changes when editing markdown configList.forEach(config => { const cache = mapCache[config.id] - if (cache && JSON.stringify(cache.config) === JSON.stringify(configConverter(config))) { - target.appendChild(cache.target) - config.render = () => null - } - }) + if (!cache) return; - // Render maps - render(target, configList).forEach(renderMap => { - renderMaps.push(renderMap) - renderMap.promise - .then(_ => afterEachMapLoaded(renderMap)) - .catch(err => console.error('Fail to render target element with ID:', renderMap.target.id, err)) + target.appendChild(cache.target) + config.target = cache.target }) - }) - Promise.allSettled(renderMaps.map(r => r.promise)) - .then(() => { - console.info('Finish Rendering') - - renderMaps - .map(r => r.target) - .filter(target => target.getAttribute('data-state') === 'rendered') - .forEach(ele => { - mapCallback(ele) - const markers = geoLinks - .filter(link => !link.targets || link.targets.includes(ele.id)) - .map(link => ({ - xy: link.xy, - title: link.url.pathname - })) - ele?.renderer?.addMarkers(markers) - }) - - htmlHolder.querySelectorAll('.marker') - .forEach(marker => htmlHolder.anchors.push(marker)) + // trivial: if map cache is applied, do not show yaml text + if (target.querySelector('.mapclay')) { + target.querySelectorAll(':scope > :not([data-render=fulfilled])') + .forEach(e => e.remove()) + } + + // Render maps with delay + const timer = setTimeout(() => + render(target, configList).forEach(renderMap => { + renderMaps.push(renderMap) + renderMap.then(afterMapRendered) + }), + delay ?? 1000 + ) + onRemove(htmlHolder, () => { + clearTimeout(timer) + }) }) //}}} -- cgit v1.2.3-70-g09d2