diff options
-rw-r--r-- | src/MenuItem.mjs | 87 | ||||
-rw-r--r-- | src/dumbyUtils.mjs | 69 | ||||
-rw-r--r-- | src/dumbymap.mjs | 77 |
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 @@ | |||
1 | import { createGeoLink } from './dumbymap'; | ||
2 | import { scrollToBlock } from './dumbyUtils'; | ||
3 | |||
4 | class Item extends HTMLDivElement { | 1 | class 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 | } |
36 | window.customElements.define('menu-folder', Folder, { extends: 'div' }); | 33 | window.customElements.define('menu-folder', Folder, { extends: 'div' }); |
37 | 34 | ||
38 | export const pickMapItem = dumbymap => | 35 | export 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 | ||
53 | export const pickBlockItem = dumbymap => | 50 | export 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 | ||
73 | export const pickLayoutItem = dumbymap => | 70 | export 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 | ||
95 | export class GeoLink { | 89 | export 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 | } | ||
128 | export class Suggestion { | 112 | export 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 | ||
164 | export const renderResults = (dumbymap, map) => | 148 | export 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 @@ | |||
1 | import LeaderLine from 'leader-line'; | ||
2 | |||
1 | export function focusNextMap(reverse = false) { | 3 | export 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) { | |||
56 | export function removeBlockFocus() { | 58 | export 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 | */ | ||
68 | export 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 | */ | ||
100 | export 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 | ]; |
22 | const mapCache = {}; | 22 | const 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 | */ | ||
30 | export 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 | */ | ||
66 | export 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 | |||
94 | export const markdown2HTML = (container, mdContent) => { | 24 | export 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'; |