From 2c9ffa25df5913953553e70426f19cf47832bb45 Mon Sep 17 00:00:00 2001 From: Hsieh Chin Fan Date: Sun, 13 Oct 2024 21:30:43 +0800 Subject: feat: migrate drag feature to dumbymap --- src/css/dumbymap.css | 10 ++++++ src/dumbymap.mjs | 81 ++++++++++++++++++++++++++++++++++++++++++++ src/editor.mjs | 96 ++++------------------------------------------------ 3 files changed, 98 insertions(+), 89 deletions(-) (limited to 'src') diff --git a/src/css/dumbymap.css b/src/css/dumbymap.css index 7ddb21a..1eb607b 100644 --- a/src/css/dumbymap.css +++ b/src/css/dumbymap.css @@ -764,6 +764,16 @@ root { .dumby-block > :not(:has(.mapclay[data-render="fulfilled"])) { opacity: 0.3; } + + * { + cursor: not-allowed !important; + } + + .mapclay[data-render="fulfilled"] { + &, & canvas { + cursor: crosshair !important; + } + } } .Dumby[data-layout='sticky'] { diff --git a/src/dumbymap.mjs b/src/dumbymap.mjs index 4cfc484..81a65ca 100644 --- a/src/dumbymap.mjs +++ b/src/dumbymap.mjs @@ -11,6 +11,7 @@ import * as menuItem from './MenuItem' import PlainModal from 'plain-modal' import proj4 from 'proj4' import { register, fromEPSGCode } from 'ol/proj/proj4' +import LeaderLine from 'leader-line' /** CSS Selector for main components */ const mapBlockSelector = 'pre:has(.language-map)' @@ -587,5 +588,85 @@ export const generateMaps = (container, { document.removeEventListener('click', actionOutsideMenu), ) + /** Drag/Drop on map for new GeoLink */ + container.onmousedown = (e) => { + // Check should start drag event for GeoLink + const selection = document.getSelection() + if (e.which !== 1 || selection.type !== 'Range') return + + 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 + if (!mouseInRange) return + + const geoLink = document.createElement('a') + geoLink.textContent = range.toString() + geoLink.classList.add('with-leader-line', 'geolink', 'drag') + 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) + + const line = new LeaderLine({ + start: geoLink, + end: lineEnd, + path: 'magnet', + }) + + function onMouseMove (event) { + lineEnd.style.left = event.clientX + 'px' + lineEnd.style.top = event.clientY + 'px' + line.position() + + // TODO Scroll dumbymap.htmlHolder when cursor is at upper/lower side + } + + container.classList.add('dragging-geolink') + container.onmousemove = onMouseMove + container.onmouseup = function (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) + } + + const map = document.elementFromPoint(e.clientX, e.clientY) + .closest('.mapclay[data-render="fulfilled"]') + if (!map) { + resumeContent('map/selection') + return + } + + const refLink = utils.addAnchorByPoint({ + defaultName: geoLink.textContent, + point: e, + map, + }) + if (!refLink) { + resumeContent('reflink') + return + } + + geoLink.href = refLink.link + utils.createGeoLink(geoLink) + } + } + container.ondragstart = () => false + return Object.seal(dumbymap) } diff --git a/src/editor.mjs b/src/editor.mjs index a8e3e0d..df6e3bf 100644 --- a/src/editor.mjs +++ b/src/editor.mjs @@ -4,7 +4,6 @@ import { defaultAliases, parseConfigsFromYaml } from 'mapclay' import * as menuItem from './MenuItem' import { addAnchorByPoint, createGeoLink } from './dumbyUtils.mjs' import { shiftByWindow } from './utils.mjs' -import LeaderLine from 'leader-line' import * as tutorial from './tutorial' /** @@ -27,9 +26,11 @@ const context = document.querySelector('[data-mode]') const textArea = document.querySelector('.editor textarea') const dumbyContainer = document.querySelector('.DumbyMap') dumbyContainer.dataset.scrollLine = '' -/** Watch: Layout of DumbyMap */ -new window.MutationObserver(mutaions => { - const mutation = mutaions.at(-1) +/** Watch: DumbyMap */ +new window.MutationObserver(mutations => { + const mutation = mutations.at(-1) + + /** Handle layout change */ const layout = dumbyContainer.dataset.layout if (layout !== 'normal' || mutation.oldValue === 'normal') { context.dataset.mode = '' @@ -38,6 +39,8 @@ new window.MutationObserver(mutaions => { attributes: true, attributeFilter: ['data-layout'], attributeOldValue: true, + childList: true, + subtree: true, }) let dumbymap @@ -1070,88 +1073,3 @@ document.addEventListener('selectionchange', () => { cm.scrollIntoView(focus) } }) - -/** Drag/Drop on map for new reference style link */ -dumbyContainer.onmousedown = (e) => { - // Check should start drag event for GeoLink - if (e.which !== 1) return - const selection = document.getSelection() - if (cm.getSelection() === '' || selection.type !== 'Range') return - const range = selection.getRangeAt(0) - const rect = range.getBoundingClientRect() - const mouseInRange = e.x < rect.right && e.x > rect.left && e.y < rect.bottom && e.y > rect.top - if (!mouseInRange) return - - const geoLink = document.createElement('a') - geoLink.textContent = range.toString() - geoLink.classList.add('with-leader-line', 'geolink', 'drag') - const originContent = range.cloneContents() - range.deleteContents() - range.insertNode(geoLink) - - const lineEnd = document.createElement('div') - lineEnd.style.cssText = `position: absolute; left: ${e.clientX}px; top: ${e.clientY}px;` - document.body.appendChild(lineEnd) - - const line = new LeaderLine({ - start: geoLink, - end: lineEnd, - path: 'magnet', - }) - - function onMouseMove (event) { - lineEnd.style.left = event.clientX + 'px' - lineEnd.style.top = event.clientY + 'px' - line.position() - - // TODO Scroll dumbymap.htmlHolder when cursor is at upper/lower side - } - - context.classList.add('dragging-geolink') - dumbyContainer.onmousemove = onMouseMove - dumbymap.utils.renderedMaps().forEach(map => { map.style.cursor = 'crosshair' }) - dumbyContainer.onmouseup = function (e) { - context.classList.remove('dragging-geolink') - dumbyContainer.onmousemove = null - dumbyContainer.onmouseup = null - line?.remove() - lineEnd.remove() - dumbymap.utils.renderedMaps().forEach(map => map.style.removeProperty('cursor')) - const resumeContent = () => { - range.deleteContents() - range.insertNode(originContent) - } - - const map = document.elementFromPoint(e.clientX, e.clientY) - .closest('.mapclay[data-render="fulfilled"]') - const selection = cm.getSelection() - if (!map || !selection) { - resumeContent('map/selection') - return - } - - const refLink = addAnchorByPoint({ - defaultName: geoLink.textContent, - point: e, - map, - validateAnchorName, - }) - if (!refLink) { - resumeContent('reflink') - return - } - - const scrollTop = dumbymap.htmlHolder.scrollTop - geoLink.href = refLink.link - createGeoLink(geoLink) - appendRefLink(cm, refLink) - if (selection === refLink.ref) { - cm.replaceSelection(`[${selection}]`) - } else { - cm.replaceSelection(`[${selection}][${refLink.ref}]`) - } - dumbymap.htmlHolder.scrollBy(0, scrollTop) - } -} - -dumbyContainer.ondragstart = () => false -- cgit v1.2.3-70-g09d2