aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/dumbyUtils.mjs
diff options
context:
space:
mode:
authorHsieh Chin Fan <pham@topo.tw>2024-10-04 21:17:13 +0800
committerHsieh Chin Fan <pham@topo.tw>2024-10-04 21:17:14 +0800
commit9a0fdb914d50c32f062de58704b40ea1ceb23203 (patch)
tree32c429bcdefede5a19694d914c5bb9aa588b1ce9 /src/dumbyUtils.mjs
parentcb0056b995c7e458d11ef363cb5ef452b214e735 (diff)
refactor: add GeoLinks
* In current implementation, marks are generated in the first time link is hovered. This prevent complexity since rendering maps is async.
Diffstat (limited to 'src/dumbyUtils.mjs')
-rw-r--r--src/dumbyUtils.mjs153
1 files changed, 122 insertions, 31 deletions
diff --git a/src/dumbyUtils.mjs b/src/dumbyUtils.mjs
index 5182a8f..c7b45f4 100644
--- a/src/dumbyUtils.mjs
+++ b/src/dumbyUtils.mjs
@@ -1,11 +1,12 @@
1import LeaderLine from 'leader-line' 1import LeaderLine from 'leader-line'
2import { insideWindow, insideParent } from './utils'
2 3
3/** 4/**
4 * focusNextMap. 5 * focusNextMap.
5 * 6 *
6 * @param {Boolean} reverse -- focus previous map 7 * @param {Boolean} reverse -- focus previous map
7 */ 8 */
8export function focusNextMap (reverse = false) { 9export function focusNextMap(reverse = false) {
9 const renderedList = this.utils.renderedMaps() 10 const renderedList = this.utils.renderedMaps()
10 const index = renderedList.findIndex(e => e.classList.contains('focus')) 11 const index = renderedList.findIndex(e => e.classList.contains('focus'))
11 const nextIndex = (index + (reverse ? -1 : 1)) % renderedList.length 12 const nextIndex = (index + (reverse ? -1 : 1)) % renderedList.length
@@ -20,7 +21,7 @@ export function focusNextMap (reverse = false) {
20 * 21 *
21 * @param {Boolean} reverse -- focus previous block 22 * @param {Boolean} reverse -- focus previous block
22 */ 23 */
23export function focusNextBlock (reverse = false) { 24export function focusNextBlock(reverse = false) {
24 const blocks = this.blocks.filter(b => 25 const blocks = this.blocks.filter(b =>
25 b.checkVisibility({ 26 b.checkVisibility({
26 contentVisibilityAuto: true, 27 contentVisibilityAuto: true,
@@ -55,7 +56,7 @@ export const scrollToBlock = block => {
55/** 56/**
56 * focusDelay. Delay of throttle, value changes by cases 57 * focusDelay. Delay of throttle, value changes by cases
57 */ 58 */
58export function focusDelay () { 59export function focusDelay() {
59 return window.window.getComputedStyle(this.showcase).display === 'none' ? 50 : 300 60 return window.window.getComputedStyle(this.showcase).display === 'none' ? 50 : 300
60} 61}
61 62
@@ -64,7 +65,7 @@ export function focusDelay () {
64 * 65 *
65 * @param {Boolean} reverse -- Switch to previous one 66 * @param {Boolean} reverse -- Switch to previous one
66 */ 67 */
67export function switchToNextLayout (reverse = false) { 68export function switchToNextLayout(reverse = false) {
68 const layouts = this.layouts 69 const layouts = this.layouts
69 const currentLayoutName = this.container.getAttribute('data-layout') 70 const currentLayoutName = this.container.getAttribute('data-layout')
70 const currentIndex = layouts.map(l => l.name).indexOf(currentLayoutName) 71 const currentIndex = layouts.map(l => l.name).indexOf(currentLayoutName)
@@ -80,27 +81,68 @@ export function switchToNextLayout (reverse = false) {
80/** 81/**
81 * removeBlockFocus. 82 * removeBlockFocus.
82 */ 83 */
83export function removeBlockFocus () { 84export function removeBlockFocus() {
84 this.blocks.forEach(b => b.classList.remove('focus')) 85 this.blocks.forEach(b => b.classList.remove('focus'))
85} 86}
86 87
87/** 88/**
89 * getMarkersFromMaps. Get marker elements by GeoLink
90 *
91 * @param {HTMLAnchorElement} link
92 * @return {HTMLElement[]} markers
93 */
94const getMarkersFromMaps = link => {
95 const maps = Array.from(
96 link.closest('.Dumby')
97 .querySelectorAll('.mapclay[data-render="fulfilled"]')
98 )
99 return maps
100 .filter(map => link.targets ? link.targets.includes(map.id) : true)
101 .map(map => {
102 const renderer = map.renderer
103
104 return map.querySelector(`.marker[title="${link.title}"]`) ??
105 renderer.addMarker({
106 xy: link.xy,
107 title: link.title
108 })
109 })
110}
111
112/**
113 * addLeaderLine, from link element to target element
114 *
115 * @param {HTMLAnchorElement} link
116 * @param {Element} target
117 */
118const addLeaderLine = (link, target) => {
119 const line = new LeaderLine({
120 start: link,
121 end: target,
122 hide: true,
123 middleLabel: link.url.searchParams.get('text'),
124 path: 'magnet'
125 })
126 line.show('draw', { duration: 300 })
127
128 return line
129}
130
131/**
88 * Create geolinks, which points to map by geo schema and id 132 * Create geolinks, which points to map by geo schema and id
89 * 133 *
90 * @param {HTMLElement} Elements contains anchor elements for doclinks 134 * @param {HTMLElement} Elements contains anchor elements for doclinks
91 * @returns {Boolean} ture is link is created, false if coordinates are invalid 135 * @returns {Boolean} ture is link is created, false if coordinates are invalid
92 */ 136 */
93export const createGeoLink = (link, callback = null) => { 137export const createGeoLink = (link) => {
94 const url = new URL(link.href) 138 const url = new URL(link.href)
95 const xyInParams = url.searchParams.get('xy') 139 const xyInParams = url.searchParams.get('xy')?.split(',')?.map(Number)
96 const xy = xyInParams 140 const xy = xyInParams ?? url?.href
97 ? xyInParams.split(',')?.map(Number) 141 ?.match(/^geo:([0-9.,]+)/)
98 : url?.href 142 ?.at(1)
99 ?.match(/^geo:([0-9.,]+)/) 143 ?.split(',')
100 ?.at(1) 144 ?.reverse()
101 ?.split(',') 145 ?.map(Number)
102 ?.reverse()
103 ?.map(Number)
104 146
105 if (!xy || isNaN(xy[0]) || isNaN(xy[1])) return false 147 if (!xy || isNaN(xy[0]) || isNaN(xy[1])) return false
106 148
@@ -110,10 +152,23 @@ export const createGeoLink = (link, callback = null) => {
110 link.classList.add('with-leader-line', 'geolink') 152 link.classList.add('with-leader-line', 'geolink')
111 link.targets = link.url.searchParams.get('id')?.split(',') ?? null 153 link.targets = link.url.searchParams.get('id')?.split(',') ?? null
112 154
113 // LeaderLine
114 link.lines = [] 155 link.lines = []
115 callback?.call(this, link)
116 156
157 // LeaderLine
158 link.onmouseover = () => {
159 const anchors = getMarkersFromMaps(link)
160 anchors
161 .filter(isAnchorVisible)
162 .forEach(anchor => {
163 const line = addLeaderLine(link, anchor)
164 link.lines.push(line)
165 })
166 }
167 link.onmouseout = () => removeLeaderLines(link)
168 link.onclick = (event) => {
169 event.preventDefault()
170 getMarkersFromMaps(link).forEach(updateMapCameraByMarker(link.xy))
171 }
117 return true 172 return true
118} 173}
119 174
@@ -129,24 +184,60 @@ export const createDocLink = link => {
129 link.onmouseover = () => { 184 link.onmouseover = () => {
130 const label = decodeURIComponent(link.href.split('#')[1]) 185 const label = decodeURIComponent(link.href.split('#')[1])
131 const selector = link.title.split('=>')[1] ?? '#' + label 186 const selector = link.title.split('=>')[1] ?? '#' + label
132 const target = document.querySelector(selector) 187 const targets = document.querySelectorAll(selector)
133 if (!target?.checkVisibility()) return 188
134 189 targets.forEach(target => {
135 const line = new LeaderLine({ 190 if (!target?.checkVisibility()) return
136 start: link, 191
137 end: target, 192 const line = new LeaderLine({
138 middleLabel: LeaderLine.pathLabel({ 193 start: link,
139 text: label, 194 end: target,
140 fontWeight: 'bold' 195 middleLabel: LeaderLine.pathLabel({
141 }), 196 text: label,
142 hide: true, 197 fontWeight: 'bold'
143 path: 'magnet' 198 }),
199 hide: true,
200 path: 'magnet'
201 })
202 link.lines.push(line)
203 line.show('draw', { duration: 300 })
144 }) 204 })
145 link.lines.push(line)
146 line.show('draw', { duration: 300 })
147 } 205 }
148 link.onmouseout = () => { 206 link.onmouseout = () => {
149 link.lines.forEach(line => line.remove()) 207 link.lines.forEach(line => line.remove())
150 link.lines.length = 0 208 link.lines.length = 0
151 } 209 }
152} 210}
211
212/**
213 * removeLeaderLines. clean lines start from link
214 *
215 * @param {HTMLAnchorElement} link
216 */
217const removeLeaderLines = link => {
218 if (!link.lines) return
219 link.lines.forEach(line => line.remove())
220 link.lines = []
221}
222
223/**
224 * updateMapByMarker. get function for updating map camera by marker
225 *
226 * @param {Number[]} xy
227 * @return {Function} function
228 */
229const updateMapCameraByMarker = xy => marker => {
230 console.log('update')
231 const renderer = marker.closest('.mapclay')?.renderer
232 renderer.updateCamera({ center: xy }, true)
233}
234
235/**
236 * isAnchorVisible. check anchor(marker) is visible for current map camera
237 *
238 * @param {Element} anchor
239 */
240const isAnchorVisible = anchor => {
241 const mapContainer = anchor.closest('.mapclay')
242 return insideWindow(anchor) && insideParent(anchor, mapContainer)
243}