diff options
| author | Hsieh Chin Fan <pham@topo.tw> | 2024-09-24 17:28:08 +0800 |
|---|---|---|
| committer | Hsieh Chin Fan <pham@topo.tw> | 2024-09-24 18:44:04 +0800 |
| commit | bd1c1973e31354d9effccae5f1bf7f4f16e13bdd (patch) | |
| tree | 8893bd05e79df67d63ee061869318263d9d323cc /src | |
| parent | 14803d63c82d95ad96e31222c3d853e1fe4931bc (diff) | |
refactor: move utils about dumbymap into module
Diffstat (limited to 'src')
| -rw-r--r-- | src/dumbyUtils.mjs | 57 | ||||
| -rw-r--r-- | src/dumbymap.mjs | 76 | ||||
| -rw-r--r-- | src/utils.mjs | 7 |
3 files changed, 73 insertions, 67 deletions
diff --git a/src/dumbyUtils.mjs b/src/dumbyUtils.mjs new file mode 100644 index 0000000..ba0bbf8 --- /dev/null +++ b/src/dumbyUtils.mjs | |||
| @@ -0,0 +1,57 @@ | |||
| 1 | export function focusNextMap(reverse = false) { | ||
| 2 | const renderedList = this.renderMaps | ||
| 3 | .map(render => render.target) | ||
| 4 | .filter(ele => ele.getAttribute('data-state') === 'rendered') | ||
| 5 | const mapNum = renderedList.length | ||
| 6 | if (mapNum === 0) return | ||
| 7 | |||
| 8 | // Get current focused map element | ||
| 9 | const currentFocus = this.container.querySelector('.map-container.focus') | ||
| 10 | |||
| 11 | // Remove class name of focus for ALL candidates | ||
| 12 | // This may trigger animation | ||
| 13 | renderedList.forEach(ele => ele.classList.remove('focus')) | ||
| 14 | |||
| 15 | // Get next existing map element | ||
| 16 | const padding = reverse ? -1 : 1 | ||
| 17 | let nextIndex = currentFocus ? renderedList.indexOf(currentFocus) + padding : 0 | ||
| 18 | nextIndex = (nextIndex + mapNum) % mapNum | ||
| 19 | const nextFocus = renderedList[nextIndex] | ||
| 20 | nextFocus.classList.add("focus") | ||
| 21 | |||
| 22 | return nextFocus | ||
| 23 | } | ||
| 24 | |||
| 25 | export function focusDelay() { | ||
| 26 | return window.getComputedStyle(this.showcase).display === 'none' ? 50 : 300 | ||
| 27 | } | ||
| 28 | |||
| 29 | export function switchToNextLayout(reverse = false) { | ||
| 30 | const layouts = this.layouts | ||
| 31 | const currentLayoutName = this.container.getAttribute('data-layout') | ||
| 32 | const currentIndex = layouts.map(l => l.name).indexOf(currentLayoutName) | ||
| 33 | const padding = reverse ? -1 : 1 | ||
| 34 | const nextIndex = currentIndex === -1 ? 0 : (currentIndex + padding + layouts.length) % layouts.length | ||
| 35 | const nextLayout = layouts[nextIndex] | ||
| 36 | this.container.setAttribute("data-layout", nextLayout.name) | ||
| 37 | } | ||
| 38 | |||
| 39 | export function focusNextBlock(reverse = false) { | ||
| 40 | const blocks = this.blocks.filter(b => b.checkVisibility({ | ||
| 41 | contentVisibilityAuto: true, | ||
| 42 | opacityProperty: true, | ||
| 43 | visibilityProperty: true, | ||
| 44 | })) | ||
| 45 | const currentBlock = blocks.find(b => b.classList.contains('focus')) | ||
| 46 | const currentIndex = blocks.indexOf(currentBlock) | ||
| 47 | const padding = reverse ? -1 : 1 | ||
| 48 | const nextIndex = currentIndex === -1 ? 0 : (currentIndex + padding + blocks.length) % blocks.length | ||
| 49 | const nextBlock = blocks[nextIndex] | ||
| 50 | blocks.forEach(b => b.classList.remove('focus')) | ||
| 51 | nextBlock?.classList?.add('focus') | ||
| 52 | nextBlock.scrollIntoView({ behavior: 'smooth', block: "nearest" }) | ||
| 53 | } | ||
| 54 | |||
| 55 | export function removeBlockFocus() { | ||
| 56 | this.blocks.forEach(b => b.classList.remove('focus')) | ||
| 57 | } | ||
diff --git a/src/dumbymap.mjs b/src/dumbymap.mjs index 59c656c..b5afb6a 100644 --- a/src/dumbymap.mjs +++ b/src/dumbymap.mjs | |||
| @@ -7,6 +7,7 @@ import LeaderLine from 'leader-line' | |||
| 7 | import { renderWith, defaultAliases, parseConfigsFromYaml } from 'mapclay' | 7 | import { renderWith, defaultAliases, parseConfigsFromYaml } from 'mapclay' |
| 8 | import { onRemove, animateRectTransition, throttle } from './utils' | 8 | import { onRemove, animateRectTransition, throttle } from './utils' |
| 9 | import { Layout, SideBySide, Overlay } from './Layout' | 9 | import { Layout, SideBySide, Overlay } from './Layout' |
| 10 | import * as utils from './dumbyUtils' | ||
| 10 | 11 | ||
| 11 | const docLinkSelector = 'a[href^="#"][title^="=>"]' | 12 | const docLinkSelector = 'a[href^="#"][title^="=>"]' |
| 12 | const geoLinkSelector = 'a[href^="geo:"]' | 13 | const geoLinkSelector = 'a[href^="geo:"]' |
| @@ -140,64 +141,6 @@ export const markdown2HTML = (container, mdContent) => { | |||
| 140 | return container | 141 | return container |
| 141 | //}}} | 142 | //}}} |
| 142 | } | 143 | } |
| 143 | // FIXME Don't use hard-coded CSS selector | ||
| 144 | // TODO Use UI to switch layouts | ||
| 145 | function focusNextMap(reverse = false) { | ||
| 146 | const renderedList = this.renderMaps | ||
| 147 | .map(render => render.target) | ||
| 148 | .filter(ele => ele.getAttribute('data-state') === 'rendered') | ||
| 149 | const mapNum = renderedList.length | ||
| 150 | if (mapNum === 0) return | ||
| 151 | |||
| 152 | // Get current focused map element | ||
| 153 | const currentFocus = this.container.querySelector('.map-container.focus') | ||
| 154 | |||
| 155 | // Remove class name of focus for ALL candidates | ||
| 156 | // This may trigger animation | ||
| 157 | renderedList.forEach(ele => ele.classList.remove('focus')) | ||
| 158 | |||
| 159 | // Get next existing map element | ||
| 160 | const padding = reverse ? -1 : 1 | ||
| 161 | let nextIndex = currentFocus ? renderedList.indexOf(currentFocus) + padding : 0 | ||
| 162 | nextIndex = (nextIndex + mapNum) % mapNum | ||
| 163 | const nextFocus = renderedList[nextIndex] | ||
| 164 | nextFocus.classList.add("focus") | ||
| 165 | |||
| 166 | return nextFocus | ||
| 167 | } | ||
| 168 | function focusDelay() { | ||
| 169 | return window.getComputedStyle(this.showcase).display === 'none' ? 50 : 300 | ||
| 170 | } | ||
| 171 | |||
| 172 | function switchToNextLayout(reverse = false) { | ||
| 173 | const currentLayoutName = this.container.getAttribute('data-layout') | ||
| 174 | const currentIndex = layouts.map(l => l.name).indexOf(currentLayoutName) | ||
| 175 | const padding = reverse ? -1 : 1 | ||
| 176 | const nextIndex = currentIndex === -1 ? 0 : (currentIndex + padding + layouts.length) % layouts.length | ||
| 177 | const nextLayout = layouts[nextIndex] | ||
| 178 | this.container.setAttribute("data-layout", nextLayout.name) | ||
| 179 | } | ||
| 180 | |||
| 181 | function focusNextBlock(reverse = false) { | ||
| 182 | const blocks = this.blocks.filter(b => b.checkVisibility({ | ||
| 183 | contentVisibilityAuto: true, | ||
| 184 | opacityProperty: true, | ||
| 185 | visibilityProperty: true, | ||
| 186 | })) | ||
| 187 | const currentBlock = blocks.find(b => b.classList.contains('focus')) | ||
| 188 | const currentIndex = blocks.indexOf(currentBlock) | ||
| 189 | const padding = reverse ? -1 : 1 | ||
| 190 | const nextIndex = currentIndex === -1 ? 0 : (currentIndex + padding + blocks.length) % blocks.length | ||
| 191 | const nextBlock = blocks[nextIndex] | ||
| 192 | blocks.forEach(b => b.classList.remove('focus')) | ||
| 193 | nextBlock?.classList?.add('focus') | ||
| 194 | nextBlock.scrollIntoView({ behavior: 'smooth', block: "nearest" }) | ||
| 195 | } | ||
| 196 | |||
| 197 | function removeBlockFocus() { | ||
| 198 | this.blocks.forEach(b => b.classList.remove('focus')) | ||
| 199 | } | ||
| 200 | |||
| 201 | export const generateMaps = (container, callback) => { | 144 | export const generateMaps = (container, callback) => { |
| 202 | container.classList.add('Dumby') | 145 | container.classList.add('Dumby') |
| 203 | const htmlHolder = container.querySelector('.SemanticHtml') ?? container | 146 | const htmlHolder = container.querySelector('.SemanticHtml') ?? container |
| @@ -208,18 +151,23 @@ export const generateMaps = (container, callback) => { | |||
| 208 | const renderMaps = [] | 151 | const renderMaps = [] |
| 209 | 152 | ||
| 210 | const dumbymap = { | 153 | const dumbymap = { |
| 154 | layouts, | ||
| 211 | container, | 155 | container, |
| 212 | htmlHolder, | 156 | htmlHolder, |
| 213 | showcase, | 157 | showcase, |
| 214 | blocks, | 158 | blocks, |
| 215 | renderMaps: renderMaps, | 159 | renderMaps: renderMaps, |
| 160 | utils: { | ||
| 161 | focusNextMap: throttle(utils.focusNextMap, utils.focusDelay), | ||
| 162 | switchToNextLayout: throttle(utils.switchToNextLayout, 300), | ||
| 163 | focusNextBlock: utils.focusNextBlock, | ||
| 164 | removeBlockFocus: utils.removeBlockFocus, | ||
| 165 | } | ||
| 216 | } | 166 | } |
| 217 | dumbymap.utils = { | 167 | Object.entries(dumbymap.utils) |
| 218 | focusNextMap: throttle(focusNextMap.bind(dumbymap), focusDelay.bind(dumbymap)), | 168 | .forEach(([util, func]) => { |
| 219 | switchToNextLayout: throttle(switchToNextLayout.bind(dumbymap), 300), | 169 | dumbymap.utils[util] = func.bind(dumbymap) |
| 220 | focusNextBlock: focusNextBlock.bind(dumbymap), | 170 | }) |
| 221 | removeBlockFocus: removeBlockFocus.bind(dumbymap), | ||
| 222 | } | ||
| 223 | 171 | ||
| 224 | // LeaderLine {{{ | 172 | // LeaderLine {{{ |
| 225 | 173 | ||
diff --git a/src/utils.mjs b/src/utils.mjs index e7d306e..46632ba 100644 --- a/src/utils.mjs +++ b/src/utils.mjs | |||
| @@ -74,14 +74,15 @@ export const animateRectTransition = (element, rect, options = {}) => { | |||
| 74 | export function throttle(func, delay) { | 74 | export function throttle(func, delay) { |
| 75 | let timerFlag = null; | 75 | let timerFlag = null; |
| 76 | 76 | ||
| 77 | return (...args) => { | 77 | return function(...args) { |
| 78 | const context = this | ||
| 78 | if (timerFlag !== null) return null | 79 | if (timerFlag !== null) return null |
| 79 | 80 | ||
| 80 | timerFlag = setTimeout( | 81 | timerFlag = setTimeout( |
| 81 | () => timerFlag = null, | 82 | () => timerFlag = null, |
| 82 | typeof delay === 'function' ? delay() : delay | 83 | typeof delay === 'function' ? delay.call(context) : delay |
| 83 | ); | 84 | ); |
| 84 | 85 | ||
| 85 | return func(...args); | 86 | return func.call(context, ...args); |
| 86 | }; | 87 | }; |
| 87 | } | 88 | } |