diff options
author | Hsieh Chin Fan <pham@topo.tw> | 2024-10-31 17:11:55 +0800 |
---|---|---|
committer | Hsieh Chin Fan <pham@topo.tw> | 2024-11-03 14:01:05 +0800 |
commit | 1fc35b33eacf0f06370fc14798f65f5ec0972195 (patch) | |
tree | c745d7c1691d4baa92d208bcee26abacb5485d0c | |
parent | 11ff731337c6823e74821d06457972c6d0528c34 (diff) |
feat: patch 7ee1ad6
* add spinning circle for Networking UX
* display marker for each results
-rw-r--r-- | src/MenuItem.mjs | 75 | ||||
-rw-r--r-- | src/css/dumbymap.css | 18 | ||||
-rw-r--r-- | src/dumbymap.mjs | 13 |
3 files changed, 86 insertions, 20 deletions
diff --git a/src/MenuItem.mjs b/src/MenuItem.mjs index 84a1405..464dd2d 100644 --- a/src/MenuItem.mjs +++ b/src/MenuItem.mjs | |||
@@ -1,7 +1,7 @@ | |||
1 | import { shiftByWindow } from './utils.mjs' | 1 | import { shiftByWindow } from './utils.mjs' |
2 | import { addMarkerByPoint } from './dumbyUtils.mjs' | 2 | import { addMarkerByPoint } from './dumbyUtils.mjs' |
3 | /* eslint-disable-next-line no-unused-vars */ | 3 | /* eslint-disable-next-line no-unused-vars */ |
4 | import { GeoLink, getMarkersFromMaps, removeLeaderLines } from './Link.mjs' | 4 | import { GeoLink, getMarkersFromMaps, getMarkersByGeoLink, removeLeaderLines, updateMapCameraByMarker } from './Link.mjs' |
5 | import * as markers from './marker.mjs' | 5 | import * as markers from './marker.mjs' |
6 | import { parseConfigsFromYaml } from 'mapclay' | 6 | import { parseConfigsFromYaml } from 'mapclay' |
7 | 7 | ||
@@ -587,25 +587,66 @@ export const editMap = (map, dumbymap) => { | |||
587 | }) | 587 | }) |
588 | } | 588 | } |
589 | 589 | ||
590 | export const addLinkbyNominatim = (range) => { | 590 | /** |
591 | * addLinkbyGeocoding. | ||
592 | * | ||
593 | * @param {Range} range | ||
594 | */ | ||
595 | export const addLinkbyGeocoding = (range) => { | ||
591 | return Item({ | 596 | return Item({ |
592 | text: 'Add Link by Geocoding', | 597 | text: 'Add Link by Geocoding', |
598 | className: ['keep-menu'], | ||
599 | onclick: async (e) => { | ||
600 | /** Add spinning circle for Network */ | ||
601 | e.target.classList.add('with-spinning-circle') | ||
602 | const menu = e.target.closest('.dumby-menu') | ||
603 | |||
604 | if (!menu) return | ||
605 | /** Geocoding by Nominatim */ | ||
606 | // TODO Add more params like limit: | ||
607 | // https://nominatim.org/release-docs/latest/api/Search/ | ||
608 | const query = range.toString() | ||
609 | const response = await fetch(`https://nominatim.openstreetmap.org/search?q=${query.toString()}&format=json`) | ||
610 | const places = await response.json() | ||
611 | menu.replaceChildren() | ||
612 | |||
613 | // Show Message if no result found | ||
614 | if (places.length === 0) { | ||
615 | menu.appendChild(Item({ text: 'No Result Found' })) | ||
616 | return | ||
617 | } | ||
618 | |||
619 | // Add items for each results | ||
620 | const items = places.map(geocodingResult((a) => { | ||
621 | a.className = 'not-geolink from-geocoding' | ||
622 | a.textContent = query | ||
623 | range.deleteContents() | ||
624 | range.insertNode(a) | ||
625 | })) | ||
626 | menu.replaceChildren(...items) | ||
627 | }, | ||
628 | }) | ||
629 | } | ||
630 | |||
631 | export const geocodingResult = (callback) => (result) => { | ||
632 | const item = Item({ | ||
633 | text: result.display_name, | ||
593 | onclick: () => { | 634 | onclick: () => { |
594 | const place = range.toString() | 635 | const a = document.createElement('a') |
595 | fetch(`https://nominatim.openstreetmap.org/search?q=${place.toString()}&format=json`) | 636 | a.href = `geo:${result.lat},${result.lon}?name=${result.name}&osm=${result.osm_type}/${result.osm_id}` |
596 | .then(res => res.json()) | 637 | a.title = result.display_name |
597 | .then(places => { | 638 | callback(a) |
598 | if (places.length === 0) return | ||
599 | console.log('nomiatim', places) | ||
600 | range.deleteContents() | ||
601 | places.forEach(p => { | ||
602 | const a = document.createElement('a') | ||
603 | a.className = 'not-geolink from-geocoding' | ||
604 | a.href = `geo:${p.lat},${p.lon}?name=${p.name}&osm=${p.osm_type}/${p.osm_id}` | ||
605 | a.textContent = place | ||
606 | range.insertNode(a) | ||
607 | }) | ||
608 | }) | ||
609 | }, | 639 | }, |
610 | }) | 640 | }) |
641 | item.onmouseover = () => { | ||
642 | const markers = getMarkersFromMaps( | ||
643 | [result.lon, result.lat], | ||
644 | { type: 'circle', title: result.display_name }, | ||
645 | ) | ||
646 | markers.forEach(updateMapCameraByMarker([result.lon, result.lat])) | ||
647 | item.onmouseout = () => { | ||
648 | markers.forEach(m => m.remove()) | ||
649 | } | ||
650 | } | ||
651 | return item | ||
611 | } | 652 | } |
diff --git a/src/css/dumbymap.css b/src/css/dumbymap.css index f0b4575..4b3aa52 100644 --- a/src/css/dumbymap.css +++ b/src/css/dumbymap.css | |||
@@ -48,6 +48,16 @@ root { | |||
48 | } | 48 | } |
49 | } | 49 | } |
50 | 50 | ||
51 | @keyframes spin { | ||
52 | 0% { | ||
53 | transform: rotate(0deg); | ||
54 | } | ||
55 | |||
56 | 100% { | ||
57 | transform: rotate(360deg); | ||
58 | } | ||
59 | } | ||
60 | |||
51 | .external::after { | 61 | .external::after { |
52 | content: ''; | 62 | content: ''; |
53 | display: inline-block; | 63 | display: inline-block; |
@@ -747,3 +757,11 @@ textarea .edit-map { | |||
747 | font-size: 1.1rem; | 757 | font-size: 1.1rem; |
748 | line-height: 1.5; | 758 | line-height: 1.5; |
749 | } | 759 | } |
760 | |||
761 | .with-spinning-circle::after { | ||
762 | content: '\21BB'; | ||
763 | display: inline-block; | ||
764 | margin-left: 1rem; | ||
765 | |||
766 | animation: spin 2s linear infinite; | ||
767 | } | ||
diff --git a/src/dumbymap.mjs b/src/dumbymap.mjs index 77f3515..217c1b6 100644 --- a/src/dumbymap.mjs +++ b/src/dumbymap.mjs | |||
@@ -534,6 +534,7 @@ export const generateMaps = (container, { | |||
534 | 534 | ||
535 | /** MENU: Menu Items for Context Menu */ | 535 | /** MENU: Menu Items for Context Menu */ |
536 | container.oncontextmenu = e => { | 536 | container.oncontextmenu = e => { |
537 | /** Check if OK to show custom menu over context menu */ | ||
537 | if (container.dataset.menu === 'disabled') return | 538 | if (container.dataset.menu === 'disabled') return |
538 | 539 | ||
539 | container.querySelectorAll('.dumby-menu').forEach(m => m.remove()) | 540 | container.querySelectorAll('.dumby-menu').forEach(m => m.remove()) |
@@ -554,6 +555,10 @@ export const generateMaps = (container, { | |||
554 | container.appendChild(menu) | 555 | container.appendChild(menu) |
555 | const containerRect = container.getBoundingClientRect() | 556 | const containerRect = container.getBoundingClientRect() |
556 | new window.MutationObserver(() => { | 557 | new window.MutationObserver(() => { |
558 | if (menu.childElementCount === 0) { | ||
559 | menu.style.display = 'none' | ||
560 | return | ||
561 | } | ||
557 | menu.style.display = 'block' | 562 | menu.style.display = 'block' |
558 | menu.style.left = (e.pageX - containerRect.left + 10) + 'px' | 563 | menu.style.left = (e.pageX - containerRect.left + 10) + 'px' |
559 | menu.style.top = (e.pageY - containerRect.top + 5) + 'px' | 564 | menu.style.top = (e.pageY - containerRect.top + 5) + 'px' |
@@ -565,8 +570,10 @@ export const generateMaps = (container, { | |||
565 | if (rangeSelected) { | 570 | if (rangeSelected) { |
566 | // TODO check click is inside selection | 571 | // TODO check click is inside selection |
567 | const range = document.getSelection().getRangeAt(0) | 572 | const range = document.getSelection().getRangeAt(0) |
568 | menu.appendChild(menuItem.addLinkbyNominatim(range)) | 573 | menu.appendChild(menuItem.addLinkbyGeocoding(range)) |
574 | return menu | ||
569 | } | 575 | } |
576 | |||
570 | /** Menu Item for editing map */ | 577 | /** Menu Item for editing map */ |
571 | const mapEditor = e.target.closest('.edit-map') | 578 | const mapEditor = e.target.closest('.edit-map') |
572 | if (mapEditor) { | 579 | if (mapEditor) { |
@@ -574,7 +581,7 @@ export const generateMaps = (container, { | |||
574 | text: 'Finish Editig', | 581 | text: 'Finish Editig', |
575 | onclick: () => mapEditor.blur(), | 582 | onclick: () => mapEditor.blur(), |
576 | })) | 583 | })) |
577 | return | 584 | return menu |
578 | } | 585 | } |
579 | 586 | ||
580 | /** Menu Items for Links */ | 587 | /** Menu Items for Links */ |
@@ -621,7 +628,7 @@ export const generateMaps = (container, { | |||
621 | 628 | ||
622 | if (linkWithLine) { | 629 | if (linkWithLine) { |
623 | menu.appendChild(menuItem.setLeaderLineType(linkWithLine)) | 630 | menu.appendChild(menuItem.setLeaderLineType(linkWithLine)) |
624 | return | 631 | return menu |
625 | } | 632 | } |
626 | 633 | ||
627 | /** Menu Items for map */ | 634 | /** Menu Items for map */ |