diff options
| author | Hsieh Chin Fan <pham@topo.tw> | 2024-09-30 12:09:32 +0800 |
|---|---|---|
| committer | Hsieh Chin Fan <pham@topo.tw> | 2024-09-30 12:12:13 +0800 |
| commit | 9d4032abf1ab33849a7cbc83d51411d73dc8a727 (patch) | |
| tree | d762122bc3c597e776829850835059f204938666 /src | |
| parent | c0b1b2b030782e64ac076fb4c592501a969caa2b (diff) | |
feat: improve actions in sub-menu
* scroll to focus map/block
* refactor methods in dumbyUtils
* refactor MutationObserver for map
Diffstat (limited to 'src')
| -rw-r--r-- | src/MenuItem.mjs | 17 | ||||
| -rw-r--r-- | src/dumbyUtils.mjs | 71 | ||||
| -rw-r--r-- | src/dumbymap.mjs | 6 |
3 files changed, 47 insertions, 47 deletions
diff --git a/src/MenuItem.mjs b/src/MenuItem.mjs index ac082b2..9830bf4 100644 --- a/src/MenuItem.mjs +++ b/src/MenuItem.mjs | |||
| @@ -1,4 +1,5 @@ | |||
| 1 | import { createGeoLink } from './dumbymap'; | 1 | import { createGeoLink } from './dumbymap'; |
| 2 | import { scrollToBlock } from './dumbyUtils'; | ||
| 2 | 3 | ||
| 3 | class Item { | 4 | class Item { |
| 4 | constructor({ text, innerHTML, onclick }) { | 5 | constructor({ text, innerHTML, onclick }) { |
| @@ -55,7 +56,10 @@ export const pickMapItem = dumbymap => | |||
| 55 | map => | 56 | map => |
| 56 | new Item({ | 57 | new Item({ |
| 57 | text: map.id, | 58 | text: map.id, |
| 58 | onclick: () => map.classList.add('focus'), | 59 | onclick: () => { |
| 60 | map.classList.add('focus'); | ||
| 61 | map.scrollIntoView({ behavior: 'smooth' }); | ||
| 62 | }, | ||
| 59 | }).element, | 63 | }).element, |
| 60 | ), | 64 | ), |
| 61 | }).element; | 65 | }).element; |
| @@ -66,8 +70,15 @@ export const pickBlockItem = dumbymap => | |||
| 66 | items: dumbymap.blocks.map( | 70 | items: dumbymap.blocks.map( |
| 67 | (block, index) => | 71 | (block, index) => |
| 68 | new Item({ | 72 | new Item({ |
| 69 | text: `Block ${index}`, | 73 | text: |
| 70 | onclick: () => block.classList.add('focus'), | 74 | block |
| 75 | .querySelector('p') | ||
| 76 | ?.textContent.substring(0, 20) | ||
| 77 | .concat(' ...') ?? `Block ${index}`, | ||
| 78 | onclick: () => { | ||
| 79 | block.classList.add('focus'); | ||
| 80 | scrollToBlock(block); | ||
| 81 | }, | ||
| 71 | }).element, | 82 | }).element, |
| 72 | ), | 83 | ), |
| 73 | }).element; | 84 | }).element; |
diff --git a/src/dumbyUtils.mjs b/src/dumbyUtils.mjs index 140d671..e7edee3 100644 --- a/src/dumbyUtils.mjs +++ b/src/dumbyUtils.mjs | |||
| @@ -1,24 +1,41 @@ | |||
| 1 | export function focusNextMap(reverse = false) { | 1 | export function focusNextMap(reverse = false) { |
| 2 | const renderedList = this.utils.renderedMaps(); | 2 | const renderedList = this.utils.renderedMaps(); |
| 3 | const index = renderedList.findIndex(e => e.classList.contains('focus')); | ||
| 4 | const nextIndex = | ||
| 5 | index === -1 ? 0 : (index + (reverse ? -1 : 1)) % renderedList.length; | ||
| 3 | 6 | ||
| 4 | const mapNum = renderedList.length; | 7 | const nextMap = renderedList.at(nextIndex); |
| 5 | if (mapNum === 0) return; | 8 | nextMap.classList.add('focus'); |
| 6 | 9 | } | |
| 7 | // Get current focused map element | ||
| 8 | const currentFocus = this.container.querySelector('.mapclay.focus'); | ||
| 9 | 10 | ||
| 10 | // Get next existing map element | 11 | export function focusNextBlock(reverse = false) { |
| 11 | const padding = reverse ? -1 : 1; | 12 | const blocks = this.blocks.filter(b => |
| 12 | let nextIndex = currentFocus | 13 | b.checkVisibility({ |
| 13 | ? renderedList.indexOf(currentFocus) + padding | 14 | contentVisibilityAuto: true, |
| 14 | : 0; | 15 | opacityProperty: true, |
| 15 | nextIndex = (nextIndex + mapNum) % mapNum; | 16 | visibilityProperty: true, |
| 16 | const nextFocus = renderedList[nextIndex]; | 17 | }), |
| 17 | nextFocus.classList.add('focus'); | 18 | ); |
| 19 | const index = blocks.findIndex(e => e.classList.contains('focus')); | ||
| 20 | const nextIndex = | ||
| 21 | index === -1 ? 0 : (index + (reverse ? -1 : 1)) % blocks.length; | ||
| 18 | 22 | ||
| 19 | return nextFocus; | 23 | const nextBlock = blocks.at(nextIndex); |
| 24 | blocks.forEach(b => b.classList.remove('focus')); | ||
| 25 | nextBlock?.classList?.add('focus'); | ||
| 26 | scrollToBlock(nextBlock); | ||
| 20 | } | 27 | } |
| 21 | 28 | ||
| 29 | // Consider block is bigger then viewport height | ||
| 30 | export const scrollToBlock = block => { | ||
| 31 | const parentRect = block.parentElement.getBoundingClientRect(); | ||
| 32 | const scrollBlock = | ||
| 33 | block.getBoundingClientRect().height > parentRect.height * 0.8 | ||
| 34 | ? 'nearest' | ||
| 35 | : 'center'; | ||
| 36 | block.scrollIntoView({ behavior: 'smooth', block: scrollBlock }); | ||
| 37 | }; | ||
| 38 | |||
| 22 | export function focusDelay() { | 39 | export function focusDelay() { |
| 23 | return window.getComputedStyle(this.showcase).display === 'none' ? 50 : 300; | 40 | return window.getComputedStyle(this.showcase).display === 'none' ? 50 : 300; |
| 24 | } | 41 | } |
| @@ -36,32 +53,6 @@ export function switchToNextLayout(reverse = false) { | |||
| 36 | this.container.setAttribute('data-layout', nextLayout.name); | 53 | this.container.setAttribute('data-layout', nextLayout.name); |
| 37 | } | 54 | } |
| 38 | 55 | ||
| 39 | export function focusNextBlock(reverse = false) { | ||
| 40 | const blocks = this.blocks.filter(b => | ||
| 41 | b.checkVisibility({ | ||
| 42 | contentVisibilityAuto: true, | ||
| 43 | opacityProperty: true, | ||
| 44 | visibilityProperty: true, | ||
| 45 | }), | ||
| 46 | ); | ||
| 47 | const currentBlock = blocks.find(b => b.classList.contains('focus')); | ||
| 48 | const currentIndex = blocks.indexOf(currentBlock); | ||
| 49 | const padding = reverse ? -1 : 1; | ||
| 50 | const nextIndex = | ||
| 51 | currentIndex === -1 | ||
| 52 | ? 0 | ||
| 53 | : (currentIndex + padding + blocks.length) % blocks.length; | ||
| 54 | const nextBlock = blocks[nextIndex]; | ||
| 55 | blocks.forEach(b => b.classList.remove('focus')); | ||
| 56 | nextBlock?.classList?.add('focus'); | ||
| 57 | const scrollBlock = | ||
| 58 | nextBlock.getBoundingClientRect().height > | ||
| 59 | nextBlock.parentElement.getBoundingClientRect().height * 0.8 | ||
| 60 | ? 'nearest' | ||
| 61 | : 'center'; | ||
| 62 | nextBlock.scrollIntoView({ behavior: 'smooth', block: scrollBlock }); | ||
| 63 | } | ||
| 64 | |||
| 65 | export function removeBlockFocus() { | 56 | export function removeBlockFocus() { |
| 66 | this.blocks.forEach(b => b.classList.remove('focus')); | 57 | this.blocks.forEach(b => b.classList.remove('focus')); |
| 67 | } | 58 | } |
diff --git a/src/dumbymap.mjs b/src/dumbymap.mjs index faa0621..7a54d73 100644 --- a/src/dumbymap.mjs +++ b/src/dumbymap.mjs | |||
| @@ -270,9 +270,7 @@ export const generateMaps = (container, { delay, mapCallback }) => { | |||
| 270 | new MutationObserver(mutations => { | 270 | new MutationObserver(mutations => { |
| 271 | const mutation = mutations.at(-1); | 271 | const mutation = mutations.at(-1); |
| 272 | const target = mutation.target; | 272 | const target = mutation.target; |
| 273 | const focus = target | 273 | const focus = target.classList.contains('focus'); |
| 274 | .getAttribute(mutation.attributeName) | ||
| 275 | .includes('focus'); | ||
| 276 | const shouldBeInShowcase = | 274 | const shouldBeInShowcase = |
| 277 | focus && | 275 | focus && |
| 278 | showcase.checkVisibility({ | 276 | showcase.checkVisibility({ |
| @@ -284,7 +282,7 @@ export const generateMaps = (container, { delay, mapCallback }) => { | |||
| 284 | if (focus) { | 282 | if (focus) { |
| 285 | dumbymap.utils | 283 | dumbymap.utils |
| 286 | .renderedMaps() | 284 | .renderedMaps() |
| 287 | .filter(map => map !== target) | 285 | .filter(map => map.id !== target.id) |
| 288 | .forEach(map => map.classList.remove('focus')); | 286 | .forEach(map => map.classList.remove('focus')); |
| 289 | } | 287 | } |
| 290 | 288 | ||