From c39dcc9b5a9055fec9a58ce833bd9535ba19b086 Mon Sep 17 00:00:00 2001 From: Hsieh Chin Fan Date: Tue, 15 Oct 2024 00:10:39 +0800 Subject: feat: patch 2c9ffa2 * addAnchorByPoint -> addMarkerByPoint just return newly created marker * Add pointByArrow as general element in container --- src/css/dumbymap.css | 15 ++++++++++++ src/dumbyUtils.mjs | 46 ++++++++++++------------------------ src/dumbymap.mjs | 67 ++++++++++++++++++++++++---------------------------- src/editor.mjs | 16 ++++++++++--- 4 files changed, 74 insertions(+), 70 deletions(-) diff --git a/src/css/dumbymap.css b/src/css/dumbymap.css index 6c581fa..dc1f59b 100644 --- a/src/css/dumbymap.css +++ b/src/css/dumbymap.css @@ -391,6 +391,10 @@ root { cursor: crosshair !important; } } + + .point-by-arrow { + display: block; + } } @@ -811,3 +815,14 @@ root { background: #E4E4E7; } } + +.point-by-arrow { + display: none; + padding: 5px; + + position: absolute; + + transform: translate(-50%, -50%); + cursor: crosshair; + pointer-events: none; +} diff --git a/src/dumbyUtils.mjs b/src/dumbyUtils.mjs index 7b06840..be0da43 100644 --- a/src/dumbyUtils.mjs +++ b/src/dumbyUtils.mjs @@ -190,11 +190,14 @@ export const createGeoLink = (link) => { if (link.dataset.valid === 'false') return removeLeaderLines(link) - getMarkersFromMaps(link) - .forEach(updateMapCameraByMarker([ + getMarkersFromMaps(link).forEach(marker => { + const map = marker.closest('.mapclay') + map.scrollIntoView({ behavior: 'smooth' }) + updateMapCameraByMarker([ Number(link.dataset.lon), Number(link.dataset.lat), - ])) + ])(marker) + }) } // Use middle click to remove markers @@ -296,43 +299,24 @@ const isAnchorVisible = anchor => { } /** - * addAnchorByPoint. + * addMarkerByPoint. * - * @param {point} options.point - object has {x, y} for window coordinates + * @param {Number[]} options.point - page XY * @param {HTMLElement} options.map - * @param {Function} options.validateAnchorName - validate anchor name is OK to use */ -export const addAnchorByPoint = ({ - defaultName, - point, - map, - validateAnchorName = () => true, -}) => { +export const addMarkerByPoint = ({ point, map }) => { const rect = map.getBoundingClientRect() - const [x, y] = map.renderer - .unproject([point.x - rect.left, point.y - rect.top]) - .map(coord => parseFloat(coord.toFixed(6))) - - let prompt - let anchorName - - do { - prompt = prompt ? 'Anchor name exists' : 'Name this anchor' - anchorName = window.prompt(prompt, defaultName ?? '') - } - while (anchorName !== null && !validateAnchorName(anchorName)) - if (anchorName === null) return - - const desc = window.prompt('Description', anchorName) ?? anchorName + const [lon, lat] = map.renderer + .unproject([point[0] - rect.left, point[1] - rect.top]) + .map(value => parseFloat(value.toFixed(6))) - const link = `geo:${y},${x}?xy=${x},${y}&id=${map.id}&type=circle` const marker = map.renderer.addMarker({ - xy: [x, y], + xy: [lon, lat], type: 'circle', }) - marker.dataset.xy = `${x},${y}` + marker.dataset.xy = `${lon},${lat}` - return { ref: anchorName, link, title: desc } + return marker } /** diff --git a/src/dumbymap.mjs b/src/dumbymap.mjs index 4329bbf..a35a2d6 100644 --- a/src/dumbymap.mjs +++ b/src/dumbymap.mjs @@ -563,84 +563,79 @@ export const generateMaps = (container, { ) /** Drag/Drop on map for new GeoLink */ + const pointByArrow = document.createElement('div') + pointByArrow.className = 'point-by-arrow' + container.appendChild(pointByArrow) + container.ondragstart = () => false container.onmousedown = (e) => { // Check should start drag event for GeoLink const selection = document.getSelection() if (e.which !== 1 || selection.type !== 'Range') return + // Check if click is inside selection const range = selection.getRangeAt(0) - const originContent = range.cloneContents() const rect = range.getBoundingClientRect() - const mouseInRange = e.x < rect.right && e.x > rect.left && e.y < rect.bottom && e.y > rect.top + const mouseInRange = e.clientX < rect.right && e.clientX > rect.left && e.clientY < rect.bottom && e.clientY > rect.top if (!mouseInRange) return + // link placeholder when dragging + container.classList.add('dragging-geolink') const geoLink = document.createElement('a') geoLink.textContent = range.toString() geoLink.classList.add('with-leader-line', 'geolink', 'drag') + + // Replace current content with link + const originContent = range.cloneContents() + const resumeContent = () => { + range.deleteContents() + range.insertNode(originContent) + } range.deleteContents() range.insertNode(geoLink) - const lineEnd = document.createElement('div') - lineEnd.className = 'arrow-head' - const offsetRect = container.getBoundingClientRect() - lineEnd.style.cssText = ` - position: absolute; - padding: 5px; - left: ${e.clientX}px; - top: ${e.clientY}px; - transform: translate(-${offsetRect.left + 5}px, -${offsetRect.top + 5}px);` - container.appendChild(lineEnd) - + // Add leader-line const line = new LeaderLine({ start: geoLink, - end: lineEnd, + end: pointByArrow, path: 'magnet', }) - function onMouseMove (event) { - lineEnd.style.left = event.clientX + 'px' - lineEnd.style.top = event.clientY + 'px' + // Update leader-line with mouse move + container.onmousemove = (event) => { + const rect = container.getBoundingClientRect() + pointByArrow.style.left = `${event.clientX - rect.left}px` + pointByArrow.style.top = `${event.clientY - rect.top}px` line.position() // TODO Scroll dumbymap.htmlHolder when cursor is at upper/lower side } + container.onmousemove(e) - container.classList.add('dragging-geolink') - container.onmousemove = onMouseMove - container.onmouseup = function (e) { + // Handler for dragend + container.onmouseup = (e) => { container.classList.remove('dragging-geolink') container.onmousemove = null container.onmouseup = null geoLink.classList.remove('drag') - line?.remove() - lineEnd.remove() - const resumeContent = () => { - range.deleteContents() - range.insertNode(originContent) - } + line.remove() const map = document.elementFromPoint(e.clientX, e.clientY) .closest('.mapclay[data-render="fulfilled"]') if (!map) { - resumeContent('map/selection') + resumeContent() return } - const refLink = utils.addAnchorByPoint({ - defaultName: geoLink.textContent, - point: e, - map, - }) - if (!refLink) { - resumeContent('reflink') + const marker = utils.addMarkerByPoint({ point: [e.clientX, e.clientY], map }) + if (!marker) { + resumeContent() return } - geoLink.href = refLink.link + geoLink.href = `geo:${marker.dataset.xy.split(',').reverse()}` utils.createGeoLink(geoLink) } } - container.ondragstart = () => false return Object.seal(dumbymap) } diff --git a/src/editor.mjs b/src/editor.mjs index e2d3d6d..c258c3d 100644 --- a/src/editor.mjs +++ b/src/editor.mjs @@ -2,7 +2,7 @@ import { markdown2HTML, generateMaps } from './dumbymap' import { defaultAliases, parseConfigsFromYaml } from 'mapclay' import * as menuItem from './MenuItem' -import { addAnchorByPoint } from './dumbyUtils.mjs' +import { addMarkerByPoint } from './dumbyUtils.mjs' import { shiftByWindow } from './utils.mjs' import * as tutorial from './tutorial' @@ -463,8 +463,18 @@ const menuForEditor = (event, menu) => { if (map) { const item = new menuItem.Item({ text: 'Add Anchor', - onclick: (event) => { - const refLink = addAnchorByPoint({ point: event, map, validateAnchorName }) + onclick: () => { + let anchorName + do { + anchorName = window.prompt(anchorName ? 'Name exists' : 'Name of Anchor') + } while (refLinks.find(ref => ref === anchorName)) + if (anchorName === null) return + + const marker = addMarkerByPoint({ point: [event.clientX, event.clientY], map }) + const refLink = { + ref: anchorName, + link: `geo:${marker.dataset.xy.split(',').reverse()}`, + } appendRefLink(cm, refLink) }, }) -- cgit v1.2.3-70-g09d2