diff options
author | Hsieh Chin Fan <pham@topo.tw> | 2024-09-30 17:59:05 +0800 |
---|---|---|
committer | Hsieh Chin Fan <pham@topo.tw> | 2024-10-01 11:35:11 +0800 |
commit | f4fe9ff6c1ce2811fe9aa0272333b4732a6ebcdc (patch) | |
tree | 6bde54a5c2afe37fb4bea6a00b492e74a0720e16 | |
parent | 193ba872c455296bff55e5f6a7d6dbbbb85c265e (diff) |
refactor: move menu feature from editor to dumbymap
-rw-r--r-- | src/MenuItem.mjs | 5 | ||||
-rw-r--r-- | src/css/dumbymap.css | 69 | ||||
-rw-r--r-- | src/css/index.css | 67 | ||||
-rw-r--r-- | src/dumbymap.mjs | 51 | ||||
-rw-r--r-- | src/editor.mjs | 34 |
5 files changed, 123 insertions, 103 deletions
diff --git a/src/MenuItem.mjs b/src/MenuItem.mjs index f4cd518..74138ec 100644 --- a/src/MenuItem.mjs +++ b/src/MenuItem.mjs | |||
@@ -89,7 +89,10 @@ export const pickLayoutItem = dumbymap => | |||
89 | items: [ | 89 | items: [ |
90 | new Item({ | 90 | new Item({ |
91 | text: 'EDIT', | 91 | text: 'EDIT', |
92 | onclick: () => document.body.setAttribute('data-mode', 'editing'), | 92 | onclick: () => |
93 | dumbymap.container | ||
94 | .closest('.playground') | ||
95 | .setAttribute('data-mode', 'editing'), | ||
93 | }).element, | 96 | }).element, |
94 | ...dumbymap.layouts.map( | 97 | ...dumbymap.layouts.map( |
95 | layout => | 98 | layout => |
diff --git a/src/css/dumbymap.css b/src/css/dumbymap.css index f6275ab..822353f 100644 --- a/src/css/dumbymap.css +++ b/src/css/dumbymap.css | |||
@@ -226,7 +226,7 @@ root { | |||
226 | padding: 0; | 226 | padding: 0; |
227 | 227 | ||
228 | position: relative; | 228 | position: relative; |
229 | overflow-x: auto; | 229 | overflow: visible; |
230 | 230 | ||
231 | &::after { | 231 | &::after { |
232 | content: 'Layout: ' attr(data-layout); | 232 | content: 'Layout: ' attr(data-layout); |
@@ -609,3 +609,70 @@ root { | |||
609 | .bold-options { | 609 | .bold-options { |
610 | font-weight: bold; | 610 | font-weight: bold; |
611 | } | 611 | } |
612 | |||
613 | .menu { | ||
614 | display: none; | ||
615 | width: fit-content; | ||
616 | |||
617 | position: absolute; | ||
618 | z-index: 9999; | ||
619 | |||
620 | border: 2px solid gray; | ||
621 | border-radius: 6px; | ||
622 | |||
623 | background: white; | ||
624 | min-width: 10rem; | ||
625 | max-height: 40vh; | ||
626 | overflow-y: scroll; | ||
627 | } | ||
628 | |||
629 | .menu-item { | ||
630 | display: flex; | ||
631 | box-sizing: border-box; | ||
632 | justify-content: space-between; | ||
633 | padding: 0.5rem; | ||
634 | |||
635 | z-index: 9999; | ||
636 | |||
637 | border: 2px solid transparent; | ||
638 | border-radius: 5px; | ||
639 | |||
640 | cursor: pointer; | ||
641 | text-wrap: nowrap; | ||
642 | |||
643 | &:hover { | ||
644 | background: rgb(226 232 240); | ||
645 | } | ||
646 | |||
647 | .info { | ||
648 | padding-inline: 1em; | ||
649 | |||
650 | color: steelblue; | ||
651 | font-weight: bold; | ||
652 | } | ||
653 | } | ||
654 | |||
655 | .folder::after { | ||
656 | content: '⏵'; | ||
657 | } | ||
658 | |||
659 | .sub-menu { | ||
660 | overflow: scroll; | ||
661 | width: fit-content; | ||
662 | |||
663 | position: absolute; | ||
664 | z-index: 100; | ||
665 | |||
666 | border: 2px solid gray; | ||
667 | border-radius: 6px; | ||
668 | |||
669 | background: white; | ||
670 | min-width: 6rem; | ||
671 | max-height: 40vh; | ||
672 | |||
673 | .menu-item { | ||
674 | margin: 0 auto; | ||
675 | padding-inline: 0.5em; | ||
676 | min-width: 5em; | ||
677 | } | ||
678 | } | ||
diff --git a/src/css/index.css b/src/css/index.css index 069bf85..b13a45d 100644 --- a/src/css/index.css +++ b/src/css/index.css | |||
@@ -104,22 +104,6 @@ body { | |||
104 | } | 104 | } |
105 | } | 105 | } |
106 | 106 | ||
107 | #menu { | ||
108 | display: none; | ||
109 | width: fit-content; | ||
110 | |||
111 | position: absolute; | ||
112 | z-index: 9999; | ||
113 | |||
114 | border: 2px solid gray; | ||
115 | border-radius: 6px; | ||
116 | |||
117 | background: white; | ||
118 | min-width: 10rem; | ||
119 | max-height: 40vh; | ||
120 | overflow-y: scroll; | ||
121 | } | ||
122 | |||
123 | .container__suggestion { | 107 | .container__suggestion { |
124 | display: flex; | 108 | display: flex; |
125 | overflow: hidden; | 109 | overflow: hidden; |
@@ -161,54 +145,3 @@ body { | |||
161 | } | 145 | } |
162 | } | 146 | } |
163 | } | 147 | } |
164 | |||
165 | .menu-item { | ||
166 | display: flex; | ||
167 | box-sizing: border-box; | ||
168 | justify-content: space-between; | ||
169 | padding: 0.5rem; | ||
170 | |||
171 | z-index: 9999; | ||
172 | |||
173 | border: 2px solid transparent; | ||
174 | border-radius: 5px; | ||
175 | |||
176 | cursor: pointer; | ||
177 | text-wrap: nowrap; | ||
178 | |||
179 | &:hover { | ||
180 | background: rgb(226 232 240); | ||
181 | } | ||
182 | |||
183 | .info { | ||
184 | padding-inline: 1em; | ||
185 | |||
186 | color: steelblue; | ||
187 | font-weight: bold; | ||
188 | } | ||
189 | } | ||
190 | |||
191 | .folder::after { | ||
192 | content: '⏵'; | ||
193 | } | ||
194 | |||
195 | .sub-menu { | ||
196 | overflow: scroll; | ||
197 | width: fit-content; | ||
198 | |||
199 | position: absolute; | ||
200 | z-index: 100; | ||
201 | |||
202 | border: 2px solid gray; | ||
203 | border-radius: 6px; | ||
204 | |||
205 | background: white; | ||
206 | min-width: 6rem; | ||
207 | max-height: 40vh; | ||
208 | |||
209 | .menu-item { | ||
210 | margin: 0 auto; | ||
211 | padding-inline: 0.5em; | ||
212 | min-width: 5em; | ||
213 | } | ||
214 | } | ||
diff --git a/src/dumbymap.mjs b/src/dumbymap.mjs index 67376c7..d365712 100644 --- a/src/dumbymap.mjs +++ b/src/dumbymap.mjs | |||
@@ -8,6 +8,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 | import * as utils from './dumbyUtils'; |
11 | import * as menuItem from './MenuItem'; | ||
11 | 12 | ||
12 | const docLinkSelector = 'a[href^="#"][title^="=>"]'; | 13 | const docLinkSelector = 'a[href^="#"][title^="=>"]'; |
13 | const geoLinkSelector = 'a[href^="geo:"]'; | 14 | const geoLinkSelector = 'a[href^="geo:"]'; |
@@ -520,7 +521,57 @@ export const generateMaps = (container, { delay, mapCallback }) => { | |||
520 | clearTimeout(timer); | 521 | clearTimeout(timer); |
521 | }); | 522 | }); |
522 | }); | 523 | }); |
524 | // }}} | ||
525 | |||
526 | // Menu {{{ | ||
527 | const menu = document.createElement('div'); | ||
528 | menu.className = 'menu'; | ||
529 | menu.onclick = () => (menu.style.display = 'none'); | ||
530 | new MutationObserver(() => { | ||
531 | if (menu.style.display === 'none') { | ||
532 | menu.style.cssText = ''; | ||
533 | menu.replaceChildren(); | ||
534 | } | ||
535 | }).observe(menu, { | ||
536 | attributes: true, | ||
537 | attributeFilter: ['style'], | ||
538 | }); | ||
539 | container.appendChild(menu); | ||
540 | |||
541 | // Menu Items | ||
542 | container.oncontextmenu = e => { | ||
543 | const selection = document.getSelection(); | ||
544 | const range = selection.getRangeAt(0); | ||
545 | if (selection) { | ||
546 | e.preventDefault(); | ||
547 | menu.innerHTML = ''; | ||
548 | const addGeoLink = new menuItem.GeoLink({ range }); | ||
549 | menu.appendChild(addGeoLink.createElement()); | ||
550 | } | ||
551 | menu.style.cssText = `overflow: visible; display: block; left: ${e.clientX + 10}px; top: ${e.clientY + 5}px;`; | ||
552 | menu.appendChild(menuItem.modal); | ||
553 | menu.appendChild(menuItem.pickMapItem(dumbymap)); | ||
554 | menu.appendChild(menuItem.pickBlockItem(dumbymap)); | ||
555 | menu.appendChild(menuItem.pickLayoutItem(dumbymap)); | ||
556 | }; | ||
523 | 557 | ||
558 | // Remove menu when click outside | ||
559 | const actionOutsideMenu = e => { | ||
560 | if (menu.style.display === 'none') return; | ||
561 | const rect = menu.getBoundingClientRect(); | ||
562 | if ( | ||
563 | e.clientX < rect.left || | ||
564 | e.clientX > rect.left + rect.width || | ||
565 | e.clientY < rect.top || | ||
566 | e.clientY > rect.top + rect.height | ||
567 | ) { | ||
568 | menu.style.display = 'none'; | ||
569 | } | ||
570 | }; | ||
571 | document.addEventListener('click', actionOutsideMenu); | ||
572 | onRemove(htmlHolder, () => | ||
573 | document.removeEventListener('click', actionOutsideMenu), | ||
574 | ); | ||
524 | //}}} | 575 | //}}} |
525 | return Object.seal(dumbymap); | 576 | return Object.seal(dumbymap); |
526 | }; | 577 | }; |
diff --git a/src/editor.mjs b/src/editor.mjs index 9946686..760e2e1 100644 --- a/src/editor.mjs +++ b/src/editor.mjs | |||
@@ -661,39 +661,5 @@ layoutObserver.observe(HtmlContainer, { | |||
661 | attributeOldValue: true, | 661 | attributeOldValue: true, |
662 | }); | 662 | }); |
663 | // }}} | 663 | // }}} |
664 | // ContextMenu {{{ | ||
665 | document.oncontextmenu = e => { | ||
666 | if (cm.hasFocus()) return; | ||
667 | |||
668 | const selection = document.getSelection(); | ||
669 | const range = selection.getRangeAt(0); | ||
670 | if (selection) { | ||
671 | e.preventDefault(); | ||
672 | menu.innerHTML = ''; | ||
673 | const addGeoLink = new menuItem.GeoLink({ range }); | ||
674 | menu.appendChild(addGeoLink.createElement()); | ||
675 | } | ||
676 | menu.style.cssText = `overflow: visible; display: block; left: ${e.clientX + 10}px; top: ${e.clientY + 5}px;`; | ||
677 | menu.appendChild(menuItem.pickMapItem(dumbymap)); | ||
678 | menu.appendChild(menuItem.pickBlockItem(dumbymap)); | ||
679 | menu.appendChild(menuItem.pickLayoutItem(dumbymap)); | ||
680 | }; | ||
681 | |||
682 | const actionOutsideMenu = e => { | ||
683 | if (menu.style.display === 'none' || cm.hasFocus()) return; | ||
684 | const rect = menu.getBoundingClientRect(); | ||
685 | if ( | ||
686 | e.clientX < rect.left || | ||
687 | e.clientX > rect.left + rect.width || | ||
688 | e.clientY < rect.top || | ||
689 | e.clientY > rect.top + rect.height | ||
690 | ) { | ||
691 | menu.style.display = 'none'; | ||
692 | } | ||
693 | }; | ||
694 | |||
695 | document.addEventListener('click', actionOutsideMenu); | ||
696 | |||
697 | // }}} | ||
698 | 664 | ||
699 | // vim: sw=2 ts=2 foldmethod=marker foldmarker={{{,}}} | 665 | // vim: sw=2 ts=2 foldmethod=marker foldmarker={{{,}}} |