diff options
| author | Hsieh Chin Fan <pham@topo.tw> | 2024-09-30 23:00:20 +0800 |
|---|---|---|
| committer | Hsieh Chin Fan <pham@topo.tw> | 2024-10-01 12:30:07 +0800 |
| commit | 5f26194f7a285a2743a84056deb3b494bde9f7f5 (patch) | |
| tree | ab65736db18f320d8a0755368a1f2ebf52e0609a | |
| parent | d58215ba1770923c2d1b7e7fb5be845e44dc79f2 (diff) | |
feat: improve menu items
* fix editing mode, make it just like layout changes
* add plain-modal for renderer results
* check selection is within text range for geolink
| -rw-r--r-- | package.json | 4 | ||||
| -rw-r--r-- | src/MenuItem.mjs | 35 | ||||
| -rw-r--r-- | src/dumbymap.mjs | 25 | ||||
| -rw-r--r-- | src/editor.mjs | 18 |
4 files changed, 53 insertions, 29 deletions
diff --git a/package.json b/package.json index 9ee1a77..3af08b8 100644 --- a/package.json +++ b/package.json | |||
| @@ -50,6 +50,7 @@ | |||
| 50 | "stylelint-order": "^6.0.4" | 50 | "stylelint-order": "^6.0.4" |
| 51 | }, | 51 | }, |
| 52 | "dependencies": { | 52 | "dependencies": { |
| 53 | "cssprefix": "^2.0.17", | ||
| 53 | "easymde": "^2.18.0", | 54 | "easymde": "^2.18.0", |
| 54 | "leader-line": "^1.0.7", | 55 | "leader-line": "^1.0.7", |
| 55 | "mapclay": "^0.8.3", | 56 | "mapclay": "^0.8.3", |
| @@ -58,7 +59,8 @@ | |||
| 58 | "markdown-it-footnote": "^4.0.0", | 59 | "markdown-it-footnote": "^4.0.0", |
| 59 | "markdown-it-front-matter": "^0.2.4", | 60 | "markdown-it-front-matter": "^0.2.4", |
| 60 | "markdown-it-toc-done-right": "^4.2.0", | 61 | "markdown-it-toc-done-right": "^4.2.0", |
| 61 | "plain-draggable": "^2.5.14" | 62 | "plain-draggable": "^2.5.14", |
| 63 | "plain-modal": "^1.0.34" | ||
| 62 | }, | 64 | }, |
| 63 | "author": "Hsiehg Chin Fan <pham@topo.tw>", | 65 | "author": "Hsiehg Chin Fan <pham@topo.tw>", |
| 64 | "homepage": "https://outdoorsafetylab.github.io/dumbymap", | 66 | "homepage": "https://outdoorsafetylab.github.io/dumbymap", |
diff --git a/src/MenuItem.mjs b/src/MenuItem.mjs index 98f3bae..5604ef1 100644 --- a/src/MenuItem.mjs +++ b/src/MenuItem.mjs | |||
| @@ -1,6 +1,5 @@ | |||
| 1 | import { createGeoLink } from './dumbymap'; | 1 | import { createGeoLink } from './dumbymap'; |
| 2 | import { scrollToBlock } from './dumbyUtils'; | 2 | import { scrollToBlock } from './dumbyUtils'; |
| 3 | import { default as PlainModal } from 'plain-modal'; | ||
| 4 | 3 | ||
| 5 | class Item extends HTMLDivElement { | 4 | class Item extends HTMLDivElement { |
| 6 | constructor({ text, innerHTML, onclick }) { | 5 | constructor({ text, innerHTML, onclick }) { |
| @@ -38,7 +37,7 @@ window.customElements.define('menu-folder', Folder, { extends: 'div' }); | |||
| 38 | 37 | ||
| 39 | export const pickMapItem = dumbymap => | 38 | export const pickMapItem = dumbymap => |
| 40 | new Folder({ | 39 | new Folder({ |
| 41 | innerHTML: '<span>Focus a Map<span><span class="info">(Tab)</span>', | 40 | innerHTML: '<span>Maps<span><span class="info">(Tab)</span>', |
| 42 | items: dumbymap.utils.renderedMaps().map( | 41 | items: dumbymap.utils.renderedMaps().map( |
| 43 | map => | 42 | map => |
| 44 | new Item({ | 43 | new Item({ |
| @@ -53,15 +52,16 @@ export const pickMapItem = dumbymap => | |||
| 53 | 52 | ||
| 54 | export const pickBlockItem = dumbymap => | 53 | export const pickBlockItem = dumbymap => |
| 55 | new Folder({ | 54 | new Folder({ |
| 56 | innerHTML: '<span>Focus Block<span><span class="info">(n/p)</span>', | 55 | innerHTML: '<span>Blocks<span><span class="info">(n/p)</span>', |
| 57 | items: dumbymap.blocks.map( | 56 | items: dumbymap.blocks.map( |
| 58 | (block, index) => | 57 | (block, index) => |
| 59 | new Item({ | 58 | new Item({ |
| 60 | text: | 59 | text: |
| 60 | `<strong>(${index})</strong>` + | ||
| 61 | block | 61 | block |
| 62 | .querySelector('p') | 62 | .querySelector('p') |
| 63 | ?.textContent.substring(0, 20) | 63 | ?.textContent.substring(0, 15) |
| 64 | .concat(' ...') ?? `Block ${index}`, | 64 | .concat(' ...'), |
| 65 | onclick: () => { | 65 | onclick: () => { |
| 66 | block.classList.add('focus'); | 66 | block.classList.add('focus'); |
| 67 | scrollToBlock(block); | 67 | scrollToBlock(block); |
| @@ -72,13 +72,13 @@ export const pickBlockItem = dumbymap => | |||
| 72 | 72 | ||
| 73 | export const pickLayoutItem = dumbymap => | 73 | export const pickLayoutItem = dumbymap => |
| 74 | new Folder({ | 74 | new Folder({ |
| 75 | innerHTML: '<span>Switch Layout<span><span class="info">(x)</span>', | 75 | innerHTML: '<span>Layouts<span><span class="info">(x)</span>', |
| 76 | items: [ | 76 | items: [ |
| 77 | new Item({ | 77 | new Item({ |
| 78 | text: 'EDIT', | 78 | text: 'EDIT', |
| 79 | onclick: () => | 79 | onclick: () => |
| 80 | dumbymap.container | 80 | dumbymap.container |
| 81 | .closest('.playground') | 81 | .closest('[data-mode]') |
| 82 | .setAttribute('data-mode', 'editing'), | 82 | .setAttribute('data-mode', 'editing'), |
| 83 | }), | 83 | }), |
| 84 | ...dumbymap.layouts.map( | 84 | ...dumbymap.layouts.map( |
| @@ -161,10 +161,17 @@ export class Suggestion { | |||
| 161 | } | 161 | } |
| 162 | } | 162 | } |
| 163 | 163 | ||
| 164 | export const modal = new Item({ | 164 | export const renderResults = (dumbymap, map) => |
| 165 | text: 'Render Results', | 165 | new Item({ |
| 166 | onclick: () => { | 166 | text: 'Render Results', |
| 167 | const modal = new PlainModal(); | 167 | onclick: e => { |
| 168 | modal.open(); | 168 | const modal = dumbymap.modal; |
| 169 | }, | 169 | modal.open(); |
| 170 | }); | 170 | modal.overlayBlur = 3; |
| 171 | modal.closeByEscKey = false; | ||
| 172 | // HACK find another way to override inline style | ||
| 173 | document.querySelector('.plainmodal-overlay-force').style.position = | ||
| 174 | 'static'; | ||
| 175 | console.log(map.renderer.results) | ||
| 176 | }, | ||
| 177 | }); | ||
diff --git a/src/dumbymap.mjs b/src/dumbymap.mjs index d365712..b9b9fa2 100644 --- a/src/dumbymap.mjs +++ b/src/dumbymap.mjs | |||
| @@ -9,6 +9,7 @@ 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 | import * as utils from './dumbyUtils'; |
| 11 | import * as menuItem from './MenuItem'; | 11 | import * as menuItem from './MenuItem'; |
| 12 | import { default as PlainModal } from 'plain-modal'; | ||
| 12 | 13 | ||
| 13 | const docLinkSelector = 'a[href^="#"][title^="=>"]'; | 14 | const docLinkSelector = 'a[href^="#"][title^="=>"]'; |
| 14 | const geoLinkSelector = 'a[href^="geo:"]'; | 15 | const geoLinkSelector = 'a[href^="geo:"]'; |
| @@ -155,6 +156,9 @@ export const generateMaps = (container, { delay, mapCallback }) => { | |||
| 155 | container.appendChild(showcase); | 156 | container.appendChild(showcase); |
| 156 | showcase.classList.add('Showcase'); | 157 | showcase.classList.add('Showcase'); |
| 157 | const renderPromises = []; | 158 | const renderPromises = []; |
| 159 | const modalContent = document.createElement('div'); | ||
| 160 | container.appendChild(modalContent); | ||
| 161 | const modal = new PlainModal(modalContent); | ||
| 158 | 162 | ||
| 159 | const dumbymap = { | 163 | const dumbymap = { |
| 160 | layouts, | 164 | layouts, |
| @@ -162,15 +166,15 @@ export const generateMaps = (container, { delay, mapCallback }) => { | |||
| 162 | htmlHolder, | 166 | htmlHolder, |
| 163 | showcase, | 167 | showcase, |
| 164 | blocks, | 168 | blocks, |
| 169 | modal, | ||
| 165 | utils: { | 170 | utils: { |
| 171 | ...utils, | ||
| 166 | renderedMaps: () => | 172 | renderedMaps: () => |
| 167 | Array.from( | 173 | Array.from( |
| 168 | container.querySelectorAll('.mapclay[data-render=fulfilled]'), | 174 | container.querySelectorAll('.mapclay[data-render=fulfilled]'), |
| 169 | ), | 175 | ), |
| 170 | focusNextMap: throttle(utils.focusNextMap, utils.focusDelay), | 176 | focusNextMap: throttle(utils.focusNextMap, utils.focusDelay), |
| 171 | switchToNextLayout: throttle(utils.switchToNextLayout, 300), | 177 | switchToNextLayout: throttle(utils.switchToNextLayout, 300), |
| 172 | focusNextBlock: utils.focusNextBlock, | ||
| 173 | removeBlockFocus: utils.removeBlockFocus, | ||
| 174 | }, | 178 | }, |
| 175 | }; | 179 | }; |
| 176 | Object.entries(dumbymap.utils).forEach(([util, func]) => { | 180 | Object.entries(dumbymap.utils).forEach(([util, func]) => { |
| @@ -540,16 +544,25 @@ export const generateMaps = (container, { delay, mapCallback }) => { | |||
| 540 | 544 | ||
| 541 | // Menu Items | 545 | // Menu Items |
| 542 | container.oncontextmenu = e => { | 546 | container.oncontextmenu = e => { |
| 547 | e.preventDefault(); | ||
| 548 | |||
| 549 | // GeoLinks | ||
| 543 | const selection = document.getSelection(); | 550 | const selection = document.getSelection(); |
| 544 | const range = selection.getRangeAt(0); | 551 | if (selection.type === 'Range') { |
| 545 | if (selection) { | 552 | const range = selection.getRangeAt(0); |
| 546 | e.preventDefault(); | ||
| 547 | menu.innerHTML = ''; | 553 | menu.innerHTML = ''; |
| 548 | const addGeoLink = new menuItem.GeoLink({ range }); | 554 | const addGeoLink = new menuItem.GeoLink({ range }); |
| 549 | menu.appendChild(addGeoLink.createElement()); | 555 | menu.appendChild(addGeoLink.createElement()); |
| 550 | } | 556 | } |
| 551 | menu.style.cssText = `overflow: visible; display: block; left: ${e.clientX + 10}px; top: ${e.clientY + 5}px;`; | 557 | menu.style.cssText = `overflow: visible; display: block; left: ${e.clientX + 10}px; top: ${e.clientY + 5}px;`; |
| 552 | menu.appendChild(menuItem.modal); | 558 | |
| 559 | // Print Map Results | ||
| 560 | const map = e.target.closest('.mapclay'); | ||
| 561 | if (map) { | ||
| 562 | menu.appendChild(menuItem.renderResults(dumbymap, map)); | ||
| 563 | } | ||
| 564 | |||
| 565 | // Dumby Utils | ||
| 553 | menu.appendChild(menuItem.pickMapItem(dumbymap)); | 566 | menu.appendChild(menuItem.pickMapItem(dumbymap)); |
| 554 | menu.appendChild(menuItem.pickBlockItem(dumbymap)); | 567 | menu.appendChild(menuItem.pickBlockItem(dumbymap)); |
| 555 | menu.appendChild(menuItem.pickLayoutItem(dumbymap)); | 568 | menu.appendChild(menuItem.pickLayoutItem(dumbymap)); |
diff --git a/src/editor.mjs b/src/editor.mjs index 760e2e1..a7bdd77 100644 --- a/src/editor.mjs +++ b/src/editor.mjs | |||
| @@ -11,7 +11,10 @@ const textArea = document.querySelector('.editor textarea'); | |||
| 11 | let dumbymap; | 11 | let dumbymap; |
| 12 | 12 | ||
| 13 | new MutationObserver(() => { | 13 | new MutationObserver(() => { |
| 14 | if (document.body.getAttribute('data-mode') === 'editing') { | 14 | if ( |
| 15 | document.body.getAttribute('data-mode') === 'editing' && | ||
| 16 | HtmlContainer.getAttribute('data-layout') !== 'normal' | ||
| 17 | ) { | ||
| 15 | HtmlContainer.setAttribute('data-layout', 'normal'); | 18 | HtmlContainer.setAttribute('data-layout', 'normal'); |
| 16 | } | 19 | } |
| 17 | }).observe(document.body, { | 20 | }).observe(document.body, { |
| @@ -276,7 +279,7 @@ window.onhashchange = () => { | |||
| 276 | // Completion in Code Blok {{{ | 279 | // Completion in Code Blok {{{ |
| 277 | // Elements about suggestions {{{ | 280 | // Elements about suggestions {{{ |
| 278 | const menu = document.createElement('div'); | 281 | const menu = document.createElement('div'); |
| 279 | menu.id = 'menu'; | 282 | menu.className = 'menu'; |
| 280 | menu.onclick = () => (menu.style.display = 'none'); | 283 | menu.onclick = () => (menu.style.display = 'none'); |
| 281 | new MutationObserver(() => { | 284 | new MutationObserver(() => { |
| 282 | if (menu.style.display === 'none') { | 285 | if (menu.style.display === 'none') { |
| @@ -648,14 +651,13 @@ document.onkeydown = e => { | |||
| 648 | // }}} | 651 | // }}} |
| 649 | // }}} | 652 | // }}} |
| 650 | // Layout Switch {{{ | 653 | // Layout Switch {{{ |
| 651 | const layoutObserver = new MutationObserver(() => { | 654 | new MutationObserver(mutaions => { |
| 655 | const mutation = mutaions.at(-1); | ||
| 652 | const layout = HtmlContainer.getAttribute('data-layout'); | 656 | const layout = HtmlContainer.getAttribute('data-layout'); |
| 653 | if (layout !== 'normal') { | 657 | if (layout !== 'normal' || mutation.oldValue === 'normal') { |
| 654 | document.body.removeAttribute('data-mode'); | 658 | document.body.setAttribute('data-mode', ''); |
| 655 | } | 659 | } |
| 656 | }); | 660 | }).observe(HtmlContainer, { |
| 657 | |||
| 658 | layoutObserver.observe(HtmlContainer, { | ||
| 659 | attributes: true, | 661 | attributes: true, |
| 660 | attributeFilter: ['data-layout'], | 662 | attributeFilter: ['data-layout'], |
| 661 | attributeOldValue: true, | 663 | attributeOldValue: true, |