aboutsummaryrefslogtreecommitdiffhomepage
path: root/src
diff options
context:
space:
mode:
authorHsieh Chin Fan <pham@topo.tw>2024-10-01 12:27:59 +0800
committerHsieh Chin Fan <pham@topo.tw>2024-10-01 12:32:04 +0800
commit5072cf2805e7815524c9320ddd7970dd9625f024 (patch)
tree10f0a37dab29e9265cfffcfe9cbefb8d6e592776 /src
parent312da714f8fc56d2f4ddbbc0a9759bf19b9c81d4 (diff)
refactor: menu-item only call methods from dumbymap.utils
Diffstat (limited to 'src')
-rw-r--r--src/MenuItem.mjs87
-rw-r--r--src/dumbyUtils.mjs69
-rw-r--r--src/dumbymap.mjs77
3 files changed, 108 insertions, 125 deletions
diff --git a/src/MenuItem.mjs b/src/MenuItem.mjs
index b4d4650..1ab7d5f 100644
--- a/src/MenuItem.mjs
+++ b/src/MenuItem.mjs
@@ -1,6 +1,3 @@
1import { createGeoLink } from './dumbymap';
2import { scrollToBlock } from './dumbyUtils';
3
4class Item extends HTMLDivElement { 1class Item extends HTMLDivElement {
5 constructor({ text, innerHTML, onclick }) { 2 constructor({ text, innerHTML, onclick }) {
6 super(); 3 super();
@@ -35,10 +32,10 @@ class Folder extends HTMLDivElement {
35} 32}
36window.customElements.define('menu-folder', Folder, { extends: 'div' }); 33window.customElements.define('menu-folder', Folder, { extends: 'div' });
37 34
38export const pickMapItem = dumbymap => 35export const pickMapItem = ({ utils }) =>
39 new Folder({ 36 new Folder({
40 innerHTML: '<span>Maps<span><span class="info">(Tab)</span>', 37 innerHTML: '<span>Maps<span><span class="info">(Tab)</span>',
41 items: dumbymap.utils.renderedMaps().map( 38 items: utils.renderedMaps().map(
42 map => 39 map =>
43 new Item({ 40 new Item({
44 text: map.id, 41 text: map.id,
@@ -50,10 +47,10 @@ export const pickMapItem = dumbymap =>
50 ), 47 ),
51 }); 48 });
52 49
53export const pickBlockItem = dumbymap => 50export const pickBlockItem = ({ blocks, utils }) =>
54 new Folder({ 51 new Folder({
55 innerHTML: '<span>Blocks<span><span class="info">(n/p)</span>', 52 innerHTML: '<span>Blocks<span><span class="info">(n/p)</span>',
56 items: dumbymap.blocks.map( 53 items: blocks.map(
57 (block, index) => 54 (block, index) =>
58 new Item({ 55 new Item({
59 text: 56 text:
@@ -64,67 +61,54 @@ export const pickBlockItem = dumbymap =>
64 .concat(' ...'), 61 .concat(' ...'),
65 onclick: () => { 62 onclick: () => {
66 block.classList.add('focus'); 63 block.classList.add('focus');
67 scrollToBlock(block); 64 utils.scrollToBlock(block);
68 }, 65 },
69 }), 66 }),
70 ), 67 ),
71 }); 68 });
72 69
73export const pickLayoutItem = dumbymap => 70export const pickLayoutItem = ({ container, layouts }) =>
74 new Folder({ 71 new Folder({
75 innerHTML: '<span>Layouts<span><span class="info">(x)</span>', 72 innerHTML: '<span>Layouts<span><span class="info">(x)</span>',
76 items: [ 73 items: [
77 new Item({ 74 new Item({
78 text: 'EDIT', 75 text: 'EDIT',
79 onclick: () => 76 onclick: () =>
80 dumbymap.container 77 container.closest('[data-mode]').setAttribute('data-mode', 'editing'),
81 .closest('[data-mode]')
82 .setAttribute('data-mode', 'editing'),
83 }), 78 }),
84 ...dumbymap.layouts.map( 79 ...layouts.map(
85 layout => 80 layout =>
86 new Item({ 81 new Item({
87 text: layout.name, 82 text: layout.name,
88 onclick: () => 83 onclick: () => container.setAttribute('data-layout', layout.name),
89 dumbymap.container.setAttribute('data-layout', layout.name),
90 }), 84 }),
91 ), 85 ),
92 ], 86 ],
93 }); 87 });
94 88
95export class GeoLink { 89export const addGeoLink = ({ utils }, range) =>
96 constructor({ range }) { 90 new Item({
97 this.range = range; 91 text: 'Add GeoLink',
98 } 92 onclick: () => {
99 93 const content = range.toString();
100 createElement = () => { 94 // FIXME Apply geolink only on matching sub-range
101 const element = document.createElement('div'); 95 const match = content.match(/(^\D*[\d.]+)\D+([\d.]+)\D*$/);
102 element.className = 'menu-item'; 96 if (!match) return false;
103 element.innerText = 'Add GeoLink'; 97
104 element.onclick = this.addGeoLinkbyRange; 98 const [x, y] = match.slice(1);
105 99 const anchor = document.createElement('a');
106 return element; 100 anchor.textContent = content;
107 }; 101 // FIXME apply WGS84
108 102 anchor.href = `geo:${y},${x}?xy=${x},${y}`;
109 addGeoLinkbyRange = () => { 103
110 const range = this.range; 104 // FIXME
111 const content = range.toString(); 105 if (utils.createGeoLink(anchor)) {
112 // FIXME Apply geolink only on matching sub-range 106 range.deleteContents();
113 const match = content.match(/(^\D*[\d.]+)\D+([\d.]+)\D*$/); 107 range.insertNode(anchor);
114 if (!match) return false; 108 }
115 109 },
116 const [x, y] = match.slice(1); 110 });
117 const anchor = document.createElement('a');
118 anchor.textContent = content;
119 // FIXME apply WGS84
120 anchor.href = `geo:${y},${x}?xy=${x},${y}`;
121 111
122 if (createGeoLink(anchor)) {
123 range.deleteContents();
124 range.insertNode(anchor);
125 }
126 };
127}
128export class Suggestion { 112export class Suggestion {
129 constructor({ text, replace }) { 113 constructor({ text, replace }) {
130 this.text = text; 114 this.text = text;
@@ -161,11 +145,10 @@ export class Suggestion {
161 } 145 }
162} 146}
163 147
164export const renderResults = (dumbymap, map) => 148export const renderResults = ({ modal, modalContent }, map) =>
165 new Item({ 149 new Item({
166 text: 'Render Results', 150 text: 'Render Results',
167 onclick: e => { 151 onclick: () => {
168 const modal = dumbymap.modal;
169 modal.open(); 152 modal.open();
170 modal.overlayBlur = 3; 153 modal.overlayBlur = 3;
171 modal.closeByEscKey = false; 154 modal.closeByEscKey = false;
@@ -175,7 +158,7 @@ export const renderResults = (dumbymap, map) =>
175 map.renderer.results.forEach(result => 158 map.renderer.results.forEach(result =>
176 printObject( 159 printObject(
177 result, 160 result,
178 dumbymap.modalContent, 161 modalContent,
179 `${result.func.name} (${result.state})`, 162 `${result.func.name} (${result.state})`,
180 ), 163 ),
181 ); 164 );
@@ -195,7 +178,7 @@ function printObject(obj, parentElement, name) {
195 if (typeof value === 'object') { 178 if (typeof value === 'object') {
196 printObject(value, detailsEle, key); 179 printObject(value, detailsEle, key);
197 } else { 180 } else {
198 let valueString = 181 const valueString =
199 typeof value === 'function' 182 typeof value === 'function'
200 ? `<pre>${value}</pre>` 183 ? `<pre>${value}</pre>`
201 : value ?? typeof value; 184 : value ?? typeof value;
diff --git a/src/dumbyUtils.mjs b/src/dumbyUtils.mjs
index e7edee3..71dc290 100644
--- a/src/dumbyUtils.mjs
+++ b/src/dumbyUtils.mjs
@@ -1,3 +1,5 @@
1import LeaderLine from 'leader-line';
2
1export function focusNextMap(reverse = false) { 3export function focusNextMap(reverse = false) {
2 const renderedList = this.utils.renderedMaps(); 4 const renderedList = this.utils.renderedMaps();
3 const index = renderedList.findIndex(e => e.classList.contains('focus')); 5 const index = renderedList.findIndex(e => e.classList.contains('focus'));
@@ -56,3 +58,70 @@ export function switchToNextLayout(reverse = false) {
56export function removeBlockFocus() { 58export function removeBlockFocus() {
57 this.blocks.forEach(b => b.classList.remove('focus')); 59 this.blocks.forEach(b => b.classList.remove('focus'));
58} 60}
61
62/**
63 * Create geolinks, which points to map by geo schema and id
64 *
65 * @param {HTMLElement} Elements contains anchor elements for doclinks
66 * @returns {Boolean} ture is link is created, false if coordinates are invalid
67 */
68export const createGeoLink = (link, callback = null) => {
69 const url = new URL(link.href);
70 const xyInParams = url.searchParams.get('xy');
71 const xy = xyInParams
72 ? xyInParams.split(',')?.map(Number)
73 : url?.href
74 ?.match(/^geo:([0-9.,]+)/)
75 ?.at(1)
76 ?.split(',')
77 ?.reverse()
78 ?.map(Number);
79
80 if (!xy || isNaN(xy[0]) || isNaN(xy[1])) return false;
81
82 // Geo information in link
83 link.url = url;
84 link.xy = xy;
85 link.classList.add('with-leader-line', 'geolink');
86 link.targets = link.url.searchParams.get('id')?.split(',') ?? null;
87
88 // LeaderLine
89 link.lines = [];
90 callback?.call(this, link);
91
92 return true;
93};
94
95/**
96 * CreateDocLink.
97 *
98 * @param {HTMLElement} Elements contains anchor elements for doclinks
99 */
100export const createDocLink = link => {
101 link.classList.add('with-leader-line', 'doclink');
102 link.lines = [];
103
104 link.onmouseover = () => {
105 const label = decodeURIComponent(link.href.split('#')[1]);
106 const selector = link.title.split('=>')[1] ?? '#' + label;
107 const target = document.querySelector(selector);
108 if (!target?.checkVisibility()) return;
109
110 const line = new LeaderLine({
111 start: link,
112 end: target,
113 middleLabel: LeaderLine.pathLabel({
114 text: label,
115 fontWeight: 'bold',
116 }),
117 hide: true,
118 path: 'magnet',
119 });
120 link.lines.push(line);
121 line.show('draw', { duration: 300 });
122 };
123 link.onmouseout = () => {
124 link.lines.forEach(line => line.remove());
125 link.lines.length = 0;
126 };
127};
diff --git a/src/dumbymap.mjs b/src/dumbymap.mjs
index c7bdc92..72936ec 100644
--- a/src/dumbymap.mjs
+++ b/src/dumbymap.mjs
@@ -21,76 +21,6 @@ const layouts = [
21]; 21];
22const mapCache = {}; 22const mapCache = {};
23 23
24// FUNCTION: Get DocLinks from special anchor element {{{
25/**
26 * CreateDocLink.
27 *
28 * @param {HTMLElement} Elements contains anchor elements for doclinks
29 */
30export const createDocLink = link => {
31 link.classList.add('with-leader-line', 'doclink');
32 link.lines = [];
33
34 link.onmouseover = () => {
35 const label = decodeURIComponent(link.href.split('#')[1]);
36 const selector = link.title.split('=>')[1] ?? '#' + label;
37 const target = document.querySelector(selector);
38 if (!target?.checkVisibility()) return;
39
40 const line = new LeaderLine({
41 start: link,
42 end: target,
43 middleLabel: LeaderLine.pathLabel({
44 text: label,
45 fontWeight: 'bold',
46 }),
47 hide: true,
48 path: 'magnet',
49 });
50 link.lines.push(line);
51 line.show('draw', { duration: 300 });
52 };
53 link.onmouseout = () => {
54 link.lines.forEach(line => line.remove());
55 link.lines.length = 0;
56 };
57};
58// }}}
59// FUNCTION: Get GeoLinks from special anchor element {{{
60/**
61 * Create geolinks, which points to map by geo schema and id
62 *
63 * @param {HTMLElement} Elements contains anchor elements for doclinks
64 * @returns {Boolean} ture is link is created, false if coordinates are invalid
65 */
66export const createGeoLink = (link, callback = null) => {
67 const url = new URL(link.href);
68 const xyInParams = url.searchParams.get('xy');
69 const xy = xyInParams
70 ? xyInParams.split(',')?.map(Number)
71 : url?.href
72 ?.match(/^geo:([0-9.,]+)/)
73 ?.at(1)
74 ?.split(',')
75 ?.reverse()
76 ?.map(Number);
77
78 if (!xy || isNaN(xy[0]) || isNaN(xy[1])) return false;
79
80 // Geo information in link
81 link.url = url;
82 link.xy = xy;
83 link.classList.add('with-leader-line', 'geolink');
84 link.targets = link.url.searchParams.get('id')?.split(',') ?? null;
85
86 // LeaderLine
87 link.lines = [];
88 callback?.call(this, link);
89
90 return true;
91};
92// }}}
93
94export const markdown2HTML = (container, mdContent) => { 24export const markdown2HTML = (container, mdContent) => {
95 // Render: Markdown -> HTML {{{ 25 // Render: Markdown -> HTML {{{
96 container.replaceChildren(); 26 container.replaceChildren();
@@ -184,7 +114,9 @@ export const generateMaps = (container, { delay, mapCallback }) => {
184 114
185 // LeaderLine {{{ 115 // LeaderLine {{{
186 116
187 Array.from(container.querySelectorAll(docLinkSelector)).filter(createDocLink); 117 Array.from(container.querySelectorAll(docLinkSelector)).filter(
118 utils.createDocLink,
119 );
188 120
189 // Get anchors with "geo:" scheme 121 // Get anchors with "geo:" scheme
190 htmlHolder.anchors = []; 122 htmlHolder.anchors = [];
@@ -202,7 +134,7 @@ export const generateMaps = (container, { delay, mapCallback }) => {
202 }; 134 };
203 const geoLinks = Array.from( 135 const geoLinks = Array.from(
204 container.querySelectorAll(geoLinkSelector), 136 container.querySelectorAll(geoLinkSelector),
205 ).filter(l => createGeoLink(l, geoLinkCallback)); 137 ).filter(l => utils.createGeoLink(l, geoLinkCallback));
206 138
207 const isAnchorPointedBy = link => anchor => { 139 const isAnchorPointedBy = link => anchor => {
208 const mapContainer = anchor.closest('.mapclay'); 140 const mapContainer = anchor.closest('.mapclay');
@@ -527,7 +459,6 @@ export const generateMaps = (container, { delay, mapCallback }) => {
527 }); 459 });
528 }); 460 });
529 // }}} 461 // }}}
530
531 // Menu {{{ 462 // Menu {{{
532 const menu = document.createElement('div'); 463 const menu = document.createElement('div');
533 menu.className = 'menu'; 464 menu.className = 'menu';