diff options
| author | Hsieh Chin Fan <pham@topo.tw> | 2024-09-23 17:38:04 +0800 |
|---|---|---|
| committer | Hsieh Chin Fan <pham@topo.tw> | 2024-09-23 17:38:04 +0800 |
| commit | 6500301ce49107886315dd47d1f710ef1a3dc41d (patch) | |
| tree | 88d5243058634230f7d8e2871d8928d2b067a8c5 /src | |
| parent | d085eff1ac4a58408f5518d5cbabf076ffab055f (diff) | |
refactor: store maps as [HTMLElement, Promise]
Diffstat (limited to 'src')
| -rw-r--r-- | src/dumbymap.mjs | 64 |
1 files changed, 31 insertions, 33 deletions
diff --git a/src/dumbymap.mjs b/src/dumbymap.mjs index d642504..808ca06 100644 --- a/src/dumbymap.mjs +++ b/src/dumbymap.mjs | |||
| @@ -139,21 +139,24 @@ export const markdown2HTML = (container, mdContent) => { | |||
| 139 | // FIXME Don't use hard-coded CSS selector | 139 | // FIXME Don't use hard-coded CSS selector |
| 140 | // TODO Use UI to switch layouts | 140 | // TODO Use UI to switch layouts |
| 141 | function focusNextMap(reverse = false) { | 141 | function focusNextMap(reverse = false) { |
| 142 | const mapNum = this.renderedMaps.length | 142 | const renderedList = this.renderMaps |
| 143 | .map(render => render.target) | ||
| 144 | .filter(ele => ele.getAttribute('data-state') === 'rendered') | ||
| 145 | const mapNum = renderedList.length | ||
| 143 | if (mapNum === 0) return | 146 | if (mapNum === 0) return |
| 147 | |||
| 144 | // Get current focused map element | 148 | // Get current focused map element |
| 145 | const currentFocus = this.container.querySelector('.map-container[data-focus]') | 149 | const currentFocus = this.container.querySelector('.map-container[data-focus]') |
| 146 | 150 | ||
| 147 | // Remove class name of focus for ALL candidates | 151 | // Remove class name of focus for ALL candidates |
| 148 | // This may trigger animation | 152 | // This may trigger animation |
| 149 | Array.from(this.container.querySelectorAll('.map-container')) | 153 | renderedList.forEach(ele => ele.removeAttribute('data-focus')) |
| 150 | .forEach(ele => ele.removeAttribute('data-focus')) | ||
| 151 | 154 | ||
| 152 | // Get next existing map element | 155 | // Get next existing map element |
| 153 | const padding = reverse ? -1 : 1 | 156 | const padding = reverse ? -1 : 1 |
| 154 | let nextIndex = currentFocus ? this.renderedMaps.indexOf(currentFocus) + padding : 0 | 157 | let nextIndex = currentFocus ? renderedList.indexOf(currentFocus) + padding : 0 |
| 155 | nextIndex = (nextIndex + mapNum) % mapNum | 158 | nextIndex = (nextIndex + mapNum) % mapNum |
| 156 | const nextFocus = this.renderedMaps[nextIndex] | 159 | const nextFocus = renderedList[nextIndex] |
| 157 | nextFocus.setAttribute('data-focus', "true") | 160 | nextFocus.setAttribute('data-focus', "true") |
| 158 | 161 | ||
| 159 | return nextFocus | 162 | return nextFocus |
| @@ -162,10 +165,11 @@ function focusDelay() { | |||
| 162 | return window.getComputedStyle(this.showcase).display === 'none' ? 50 : 300 | 165 | return window.getComputedStyle(this.showcase).display === 'none' ? 50 : 300 |
| 163 | } | 166 | } |
| 164 | 167 | ||
| 165 | function switchToNextLayout() { | 168 | function switchToNextLayout(reverse = false) { |
| 166 | const currentLayoutName = this.container.getAttribute('data-layout') | 169 | const currentLayoutName = this.container.getAttribute('data-layout') |
| 167 | const currentIndex = layouts.map(l => l.name).indexOf(currentLayoutName) | 170 | const currentIndex = layouts.map(l => l.name).indexOf(currentLayoutName) |
| 168 | const nextIndex = currentIndex === -1 ? 0 : (currentIndex + 1) % layouts.length | 171 | const padding = reverse ? -1 : 1 |
| 172 | const nextIndex = currentIndex === -1 ? 0 : (currentIndex + padding + layouts.length) % layouts.length | ||
| 169 | const nextLayout = layouts[nextIndex] | 173 | const nextLayout = layouts[nextIndex] |
| 170 | this.container.setAttribute("data-layout", nextLayout.name) | 174 | this.container.setAttribute("data-layout", nextLayout.name) |
| 171 | } | 175 | } |
| @@ -177,14 +181,14 @@ export const generateMaps = (container, callback) => { | |||
| 177 | const showcase = document.createElement('div') | 181 | const showcase = document.createElement('div') |
| 178 | container.appendChild(showcase) | 182 | container.appendChild(showcase) |
| 179 | showcase.classList.add('Showcase') | 183 | showcase.classList.add('Showcase') |
| 180 | const renderedMaps = [] | 184 | const renderMaps = [] |
| 181 | 185 | ||
| 182 | const dumbymap = { | 186 | const dumbymap = { |
| 183 | container, | 187 | container, |
| 184 | htmlHolder, | 188 | htmlHolder, |
| 185 | showcase, | 189 | showcase, |
| 186 | blocks, | 190 | blocks, |
| 187 | renderedMaps, | 191 | renderMaps: renderMaps, |
| 188 | } | 192 | } |
| 189 | dumbymap.utils = { | 193 | dumbymap.utils = { |
| 190 | focusNextMap: throttle(focusNextMap.bind(dumbymap), focusDelay.bind(dumbymap)), | 194 | focusNextMap: throttle(focusNextMap.bind(dumbymap), focusDelay.bind(dumbymap)), |
| @@ -346,10 +350,9 @@ export const generateMaps = (container, callback) => { | |||
| 346 | } | 350 | } |
| 347 | 351 | ||
| 348 | Object.values(dumbymap) | 352 | Object.values(dumbymap) |
| 349 | .filter(e => e instanceof HTMLElement) | 353 | .flat() |
| 350 | .forEach(e => e.removeAttribute('style')) | 354 | .filter(ele => ele instanceof HTMLElement) |
| 351 | dumbymap.blocks | 355 | .forEach(ele => ele.removeAttribute('style')) |
| 352 | .forEach(e => e.removeAttribute('style')) | ||
| 353 | 356 | ||
| 354 | if (newLayout) { | 357 | if (newLayout) { |
| 355 | layouts.find(l => l.name === newLayout) | 358 | layouts.find(l => l.name === newLayout) |
| @@ -376,9 +379,8 @@ export const generateMaps = (container, callback) => { | |||
| 376 | // Render Maps {{{ | 379 | // Render Maps {{{ |
| 377 | 380 | ||
| 378 | const afterEachMapLoaded = (mapContainer) => { | 381 | const afterEachMapLoaded = (mapContainer) => { |
| 379 | renderedMaps.push(mapContainer) | ||
| 380 | renderedMaps.sort((a, b) => mapIdList.indexOf(a.id) - mapIdList.indexOf(b.id)) | ||
| 381 | mapContainer.setAttribute('tabindex', "-1") | 382 | mapContainer.setAttribute('tabindex', "-1") |
| 383 | mapContainer.setAttribute('data-state', "rendered") | ||
| 382 | 384 | ||
| 383 | const observer = mapFocusObserver() | 385 | const observer = mapFocusObserver() |
| 384 | mapFocusObserver().observe(mapContainer, { | 386 | mapFocusObserver().observe(mapContainer, { |
| @@ -416,7 +418,7 @@ export const generateMaps = (container, callback) => { | |||
| 416 | ...config.aliases ?? {} | 418 | ...config.aliases ?? {} |
| 417 | }, | 419 | }, |
| 418 | })) | 420 | })) |
| 419 | const renderTargets = elementsWithMapConfig | 421 | elementsWithMapConfig |
| 420 | .map(async (target) => { | 422 | .map(async (target) => { |
| 421 | // Get text in code block starts with '```map' | 423 | // Get text in code block starts with '```map' |
| 422 | const configText = target.querySelector('.language-map') | 424 | const configText = target.querySelector('.language-map') |
| @@ -433,25 +435,23 @@ export const generateMaps = (container, callback) => { | |||
| 433 | } | 435 | } |
| 434 | 436 | ||
| 435 | // Render maps | 437 | // Render maps |
| 436 | return render(target, configList) | 438 | return render(target, configList).map(renderMap => { |
| 437 | .then(results => { | 439 | renderMaps.push(renderMap) |
| 438 | results.forEach((mapByConfig) => { | 440 | renderMap.promise |
| 439 | if (mapByConfig.status === 'fulfilled') { | 441 | .then(_ => afterEachMapLoaded(renderMap.target)) |
| 440 | afterEachMapLoaded(mapByConfig.value) | 442 | .catch(err => console.error('Fail to render target element with ID:', renderMap.target.id, err)) |
| 441 | return mapByConfig.value | 443 | |
| 442 | } else { | 444 | return renderMap.promise |
| 443 | console.error('Fail to render target element', mapByConfig.reason) | 445 | }) |
| 444 | } | ||
| 445 | }) | ||
| 446 | }) | ||
| 447 | }) | 446 | }) |
| 448 | 447 | ||
| 449 | Promise.all(renderTargets) | 448 | Promise.allSettled(renderMaps.map(r => r.promise)) |
| 450 | .then(() => { | 449 | .then(() => { |
| 451 | console.info('Finish Rendering') | 450 | console.info('Finish Rendering') |
| 452 | 451 | ||
| 453 | const maps = htmlHolder.querySelectorAll('.map-container') ?? [] | 452 | renderMaps |
| 454 | Array.from(maps) | 453 | .map(r => r.target) |
| 454 | .filter(target => target.getAttribute('data-state') === 'rendered') | ||
| 455 | .forEach(ele => { | 455 | .forEach(ele => { |
| 456 | callback(ele) | 456 | callback(ele) |
| 457 | const markers = geoLinks | 457 | const markers = geoLinks |
| @@ -465,10 +465,8 @@ export const generateMaps = (container, callback) => { | |||
| 465 | 465 | ||
| 466 | htmlHolder.querySelectorAll('.marker') | 466 | htmlHolder.querySelectorAll('.marker') |
| 467 | .forEach(marker => htmlHolder.anchors.push(marker)) | 467 | .forEach(marker => htmlHolder.anchors.push(marker)) |
| 468 | |||
| 469 | return maps | ||
| 470 | }) | 468 | }) |
| 471 | 469 | ||
| 472 | //}}} | 470 | //}}} |
| 473 | return dumbymap | 471 | return Object.seal(dumbymap) |
| 474 | } | 472 | } |