Harmonize marker / popup rendering

This commit is contained in:
2026-05-02 00:16:37 +02:00
parent 560b22c039
commit 95ebc96484
9 changed files with 169 additions and 232 deletions

1
.gitignore vendored
View File

@@ -5,3 +5,4 @@
/node_modules/ /node_modules/
/log.html /log.html
/dist/ /dist/
.codex

View File

@@ -82,7 +82,6 @@ module.exports = {
from: path.resolve(LIB, 'index.php'), from: path.resolve(LIB, 'index.php'),
to: 'index.php' to: 'index.php'
}, },
{ from: 'src/images/footprint_mapbox.png', to: 'images' },
{ from: 'src/images/logo_black.png', to: 'images' }, { from: 'src/images/logo_black.png', to: 'images' },
{ from: 'src/images/spot-logo-only.svg', to: 'images' } { from: 'src/images/spot-logo-only.svg', to: 'images' }
], ],

View File

@@ -280,6 +280,12 @@ class Spot extends Main
usort($asMessages, function($a, $b){return $a['unix_time'] > $b['unix_time'];}); usort($asMessages, function($a, $b){return $a['unix_time'] > $b['unix_time'];});
$bHasMsg = !empty($asMessages); $bHasMsg = !empty($asMessages);
foreach($asMessages as &$asMessage) {
$asMessage['id'] = $asMessage[Db::getId(Feed::MSG_TABLE)];
$asMessage['type'] = 'message';
$asMessage['subtype'] = 'message';
}
//Add medias //Add medias
$asMedias = $this->getMedias('taken_on', $asMediaIds); $asMedias = $this->getMedias('taken_on', $asMediaIds);
usort($asMedias, function($a, $b){return $a['unix_time'] > $b['unix_time'];}); usort($asMedias, function($a, $b){return $a['unix_time'] > $b['unix_time'];});
@@ -289,11 +295,13 @@ class Spot extends Main
$iMaxIndex = count($asMessages) - 1; $iMaxIndex = count($asMessages) - 1;
foreach($asMedias as $asMedia) { foreach($asMedias as $asMedia) {
if($asMedia['latitude']!='' && $asMedia['longitude']!='') { if($asMedia['latitude']!='' && $asMedia['longitude']!='') {
$asMedia['id'] = $asMedia[Db::getId(Media::MEDIA_TABLE)];
$asMedia['type'] = 'media';
$asMedia['lat_dms'] = self::decToDms($asMedia['latitude'], 'lat'); $asMedia['lat_dms'] = self::decToDms($asMedia['latitude'], 'lat');
$asMedia['lon_dms'] = self::decToDms($asMedia['longitude'], 'lon'); $asMedia['lon_dms'] = self::decToDms($asMedia['longitude'], 'lon');
$asGeoMedias[] = $asMedia; $asGeoMedias[] = $asMedia;
} }
elseif($bHasMsg) { if($bHasMsg) {
while($iIndex <= $iMaxIndex && $asMedia['unix_time'] > $asMessages[$iIndex]['unix_time']) $iIndex++; while($iIndex <= $iMaxIndex && $asMedia['unix_time'] > $asMessages[$iIndex]['unix_time']) $iIndex++;
//All medias before first message or after last message are assigned to first/last message respectively //All medias before first message or after last message are assigned to first/last message respectively
@@ -308,13 +316,15 @@ class Spot extends Main
} }
} }
$asMarkers = [...$asMessages, ...$asGeoMedias];
usort($asMarkers, function($a, $b){return $a['unix_time'] > $b['unix_time'];});
//Spot Last Update //Spot Last Update
$asLastUpdate = array(); $asLastUpdate = array();
$this->addTimeStamp($asLastUpdate, $this->oProject->getLastUpdate()); $this->addTimeStamp($asLastUpdate, $this->oProject->getLastUpdate());
$asResult = array( $asResult = array(
'messages' => $asMessages, 'markers' => $asMarkers,
'medias' => $asGeoMedias,
'maps' => $this->oMap->getProjectMaps($this->oProject->getProjectId()), 'maps' => $this->oMap->getProjectMaps($this->oProject->getProjectId()),
'last_update' => $asLastUpdate 'last_update' => $asLastUpdate
); );

View File

@@ -28,8 +28,14 @@ export default {
lastUpdate: { unix_time: 0, relative_time: '', formatted_time: ''}, lastUpdate: { unix_time: 0, relative_time: '', formatted_time: ''},
feedPanelOpen: false, feedPanelOpen: false,
settingsPanelOpen: false, settingsPanelOpen: false,
markers: {messages: null, medias: null}, track: null,
markers: [],
markerSize: {width: 32, height: 32}, markerSize: {width: 32, height: 32},
markerProps: {
image: {mainClasses: 'media', iconMain: 'marker', iconSub: 'image'},
video: {mainClasses: 'media', iconMain: 'marker', iconSub: 'video'},
message: {mainClasses: 'message', iconMain: 'marker', iconSub: 'footprint', iconSubTransform: 'rotate-270'}
},
currProject: {}, currProject: {},
modeHisto: false, modeHisto: false,
posts: [], posts: [],
@@ -38,6 +44,7 @@ export default {
baseMaps: {}, baseMaps: {},
baseMap: null, baseMap: null,
map: null, map: null,
mapReady: false,
hikes: { hikes: {
colors:{'main':'#00ff78', 'off-track':'#0000ff', 'hitchhiking':'#FF7814'}, colors:{'main':'#00ff78', 'off-track':'#0000ff', 'hitchhiking':'#FF7814'},
width: 4 width: 4
@@ -49,7 +56,8 @@ export default {
projectClasses() { projectClasses() {
return [ return [
this.feedPanelOpen?'with-feed':'', this.feedPanelOpen?'with-feed':'',
this.settingsPanelOpen?'with-settings':'' this.settingsPanelOpen?'with-settings':'',
this.mapReady?'map-ready':''
].filter(n => n).join(' '); ].filter(n => n).join(' ');
}, },
nlClasses() { nlClasses() {
@@ -75,6 +83,7 @@ export default {
'hash.items.0'(newProjectCodename, oldProjectCodename) { 'hash.items.0'(newProjectCodename, oldProjectCodename) {
if(newProjectCodename && newProjectCodename != oldProjectCodename) { if(newProjectCodename && newProjectCodename != oldProjectCodename) {
this.hash.items = [newProjectCodename]; this.hash.items = [newProjectCodename];
this.toggleSettingsPanel(false, 'none');
this.init(); this.init();
} }
} }
@@ -122,8 +131,10 @@ export default {
this.initMap() this.initMap()
]); ]);
//Direct link: Scroll to post //Direct link or default project positioning
if(this.hash.items.length == 3) this.findPost({type: this.hash.items[1], id: this.hash.items[2]}); await this.setInitialMapPosition();
this.mapReady = true;
}, },
quit() { quit() {
lightbox.end(); lightbox.end();
@@ -152,6 +163,7 @@ export default {
this.modeHisto = (this.currProject.mode == this.consts.modes.histo); this.modeHisto = (this.currProject.mode == this.consts.modes.histo);
this.feed = {loading:false, updatable:true, outOfData:false, refIdFirst:0, refIdLast:0, firstChunk:true}; this.feed = {loading:false, updatable:true, outOfData:false, refIdFirst:0, refIdLast:0, firstChunk:true};
this.posts = []; this.posts = [];
this.mapReady = false;
//this.baseMap = null; //this.baseMap = null;
this.baseMaps = {}; this.baseMaps = {};
}, },
@@ -166,7 +178,7 @@ export default {
hasVideo: true, hasVideo: true,
onMediaChange: (oMedia) => { onMediaChange: (oMedia) => {
this.hash.items = [this.currProject.codename, 'media', oMedia.id]; this.hash.items = [this.currProject.codename, 'media', oMedia.id];
if(oMedia.set == 'post-medias') this.goToPost({type: 'media', id: oMedia.id}); if(oMedia.set == 'post-medias') this.goToPost('media', oMedia.id);
}, },
onClosing: () => {this.hash.items = [this.hash.items[0]];} onClosing: () => {this.hash.items = [this.hash.items[0]];}
}); });
@@ -178,6 +190,8 @@ export default {
//Mobile Touchscreen Events //Mobile Touchscreen Events
//TODO //TODO
this.toggleFeedPanel(!this.isMobile(), 'none');
//Add post Event handling //Add post Event handling
//TODO //TODO
@@ -209,8 +223,7 @@ export default {
const aoMarkers = await oMarkersPromise; const aoMarkers = await oMarkersPromise;
this.baseMaps = aoMarkers.maps; this.baseMaps = aoMarkers.maps;
this.baseMap = this.baseMaps.find((asBM) => asBM.default_map)?.codename ?? null; this.baseMap = this.baseMaps.find((asBM) => asBM.default_map)?.codename ?? null;
this.markers.messages = aoMarkers.messages; this.markers = aoMarkers.markers;
this.markers.medias = aoMarkers.medias;
this.lastUpdate = aoMarkers.last_update; this.lastUpdate = aoMarkers.last_update;
//Force wait for load event //Force wait for load event
@@ -237,21 +250,19 @@ export default {
} }
//Add track //Add track
const oTrack = await oTrackPromise; this.addTrack(await oTrackPromise);
this.addTrack(oTrack);
//Centering map
await this.positionMap(oTrack);
//Add Markers //Add Markers
this.addMarkers(); this.markers.forEach(oMarker => this.addMarker(oMarker));
//Force wait for idle event //Force wait for idle event
await new Promise((resolve) => { await new Promise((resolve) => {
this.map.once('idle', resolve); if(this.map.loaded() && this.map.areTilesLoaded()) resolve();
else this.map.once('idle', resolve);
}); });
}, },
addTrack(oTrack) { addTrack(oTrack, bCenter=false) {
this.track = oTrack;
this.map.addSource('track', { this.map.addSource('track', {
'type': 'geojson', 'type': 'geojson',
'data': oTrack 'data': oTrack
@@ -280,167 +291,37 @@ export default {
} }
}); });
}, },
async addMarkers() { addMarker(oMarker) {
this.map.addImage('markerIcon', (await this.map.loadImage('images/footprint_mapbox.png')).data);
this.map.addSource('markers', {
type:'geojson',
data: {
type: 'FeatureCollection',
features: this.convertMsgToFeatures(this.markers.messages)
}
});
this.map.addLayer({
'id': 'markers',
'type': 'symbol',
'source': 'markers',
'layout': {'icon-image': 'markerIcon'}
});
this.map.on('click', 'markers', (e) => {
this.openMarkerPopup(e.features[0]);
});
/*
this.markers.messages.forEach(msg => {
const el = document.createElement('div');
const app = createApp(SpotIconStack, {iconMain: 'message', iconSub:'message-in', iconSubRotation: 270});
app.mount(el);
new Marker({element: el, anchor: 'bottom'})
.setLngLat([msg.longitude, msg.latitude])
.addTo(this.map);
});
*/
//Medias
//TODO Use same way of displaying markers (so that openMarkerPopup works on all markers)
this.markers.medias.forEach(msg => {
const $Marker = document.createElement('div'); const $Marker = document.createElement('div');
createApp(SpotIconStack, {mainClasses: 'media', iconMain: 'marker', iconSub: msg.subtype}).mount($Marker); createApp(SpotIconStack, this.markerProps[oMarker.subtype]).mount($Marker);
const marker = new Marker({element: $Marker, anchor: 'bottom'}) new Marker({element: $Marker, anchor: 'bottom'})
.setLngLat([msg.longitude, msg.latitude]) .setLngLat([oMarker.longitude, oMarker.latitude])
.addTo(this.map) .addTo(this.map)
.getElement().addEventListener('click', (oEvent) => { .getElement()
.addEventListener('click', (oEvent) => {
oEvent.preventDefault(); oEvent.preventDefault();
oEvent.stopPropagation(); oEvent.stopPropagation();
this.openMediaPopup(msg); this.openMarkerPopup(oMarker.id, oMarker.type);
});
}); });
}, },
async positionMap(oTrack) { openMarkerPopup(iMarkerId, sMarkerType) {
let bOpenFeedPanel = !this.isMobile();
let oBounds = new LngLatBounds();
if( //Blog Mode: Fit to last message
this.currProject.mode == this.consts.modes.blog &&
this.markers.messages.length > 0 &&
this.hash.items[2] != 'message'
) {
let oLastMsg = this.markers.messages[this.markers.messages.length - 1];
oBounds.extend(new LngLat(oLastMsg.longitude, oLastMsg.latitude));
}
else { //Pre/Histo Mode: Fit to track
for(const iFeatureId in oTrack.features) {
oBounds = oTrack.features[iFeatureId].geometry.coordinates.reduce(
(bounds, coord) => {
return bounds.extend(coord);
},
oBounds
);
}
}
const iFeedPanelPadding = bOpenFeedPanel?(getOuterWidth(this.$refs.feed)/2):0;
await this.map.fitBounds(
oBounds,
{
padding: {
top: 20,
bottom: 20,
left: (20 + iFeedPanelPadding),
right: (20 + iFeedPanelPadding)
},
animate: false,
maxZoom: 15
}
);
//Toggle only when map is ready, for the tilt effet
this.toggleFeedPanel(bOpenFeedPanel);
},
convertMsgToFeatures(oMsg) {
return oMsg.map(oMsg => ({
'type': 'Feature',
'geometry': {
'type': 'Point',
'coordinates': [oMsg.longitude, oMsg.latitude]
},
'properties': {
...oMsg,
...{'description': ''}
}
}));
},
openMediaPopup(oMedia) {
this.closeMarkerPopup(); this.closeMarkerPopup();
let oMarker = this.markers.find((oCandidate) => oCandidate.id == iMarkerId && oCandidate.type == sMarkerType);
const $Popup = document.createElement('div'); const $Popup = document.createElement('div');
this.popup.element = new Popup({ this.popup.element = new Popup({
anchor: 'bottom', anchor: 'bottom',
offset: [0, this.markerSize.height * -1], offset: [0, this.markerSize.height * -1], //FIXME
closeButton: false closeButton: false
}) })
.setDOMContent($Popup) .setDOMContent($Popup)
.setLngLat([oMedia.longitude, oMedia.latitude]) .setLngLat([oMarker.longitude, oMarker.latitude])
.setMaxWidth(300)
.addTo(this.map); .addTo(this.map);
this.popup.content = createApp(ProjectPopup, { this.popup.content = createApp(ProjectPopup, {
type: 'media', options: oMarker,
options: oMedia,
medias: [oMedia],
project: this.currProject
});
this.popup.content
.provide('spot', this.spot)
.provide('lang', this.lang)
.provide('consts', this.consts)
.mount($Popup);
},
openMarkerPopup(oFeature) {
this.closeMarkerPopup();
//Convert ID Message to feature
if(typeof oFeature == 'number') {
const oMatchingFeatures = this.map.querySourceFeatures('markers', {
filter: ['==', ['get', 'id_message'], oFeature]
});
if(!oMatchingFeatures.length) {
console.warn('Marker not found: ', oFeature);
return;
}
else oFeature = oMatchingFeatures[0];
}
const $Popup = document.createElement('div');
this.popup.element = new Popup({
anchor: 'bottom',
offset: [0, this.markerSize.height * -1],
closeButton: false
})
.setDOMContent($Popup)
.setLngLat(oFeature.geometry.coordinates)
.setMaxWidth(300)
.addTo(this.map);
this.popup.content = createApp(ProjectPopup, {
type: 'message',
options: oFeature.properties,
medias: JSON.parse(oFeature.properties.medias || '[]'),
project: this.currProject project: this.currProject
}); });
this.popup.content this.popup.content
@@ -459,6 +340,75 @@ export default {
this.popup.element = null; this.popup.element = null;
} }
}, },
async setInitialMapPosition() {
if(this.hash.items.length == 3) {
await this.findPost(this.hash.items[1], this.hash.items[2]);
}
else {
let oBounds = new LngLatBounds();
if( //Blog Mode: Fit to last message
this.currProject.mode == this.consts.modes.blog &&
this.markers.length > 0
) {
let oLastMsg = this.markers.at(-1);
oBounds.extend(new LngLat(oLastMsg.longitude, oLastMsg.latitude));
}
else { //Pre/Histo Mode: Fit to track
for(const iFeatureId in this.track.features) {
oBounds = this.track.features[iFeatureId].geometry.coordinates.reduce(
(bounds, coord) => {
return bounds.extend(coord);
},
oBounds
);
}
}
const iFeedPanelPadding = this.feedPanelOpen?(getOuterWidth(this.$refs.feed)):0;
await this.map.fitBounds(
oBounds,
{
padding: {
top: 20,
bottom: 20,
left: 20,
right: 20 + iFeedPanelPadding
},
animate: false,
maxZoom: 15
}
);
}
},
async findPost(sPostType, iPostId) {
let oRef = this.goToPost(sPostType, iPostId);
if(oRef) {
await oRef.executeMainAction(0);
}
else if(!this.feed.outOfData) {
await this.getNextFeed();
await this.findPost(sPostType, iPostId);
}
else console.log('Missing element ID "'+iPostId+'" of type "'+sPostType+'"');
},
goToPost(sPostType, iPostId) {
let bFound = false;
let aoRefs = this.$refs.posts.filter((post) => {return post.postId == sPostType+'-'+iPostId;});
if(aoRefs.length > 0) {
let oRef = aoRefs[0];
this.$refs.feedSimpleBar.scrollElement.scrollTop += Math.round(
oRef.$el.getBoundingClientRect().top
+ window.pageYOffset
- parseFloat(getComputedStyle(this.$refs.feedSimpleBar.$el).paddingTop)
);
//this.hash.items = [this.hash.items[0]];
return oRef;
}
},
async getNextFeed() { async getNextFeed() {
if(!this.feed.outOfData && !this.feed.loading) { if(!this.feed.outOfData && !this.feed.loading) {
//Get next chunk //Get next chunk
@@ -504,31 +454,28 @@ export default {
} }
//Add new Markers //Add new Markers
if(Object.keys(aoData.messages).length > 0) { if(Object.keys(aoData.markers).length > 0) {
const oMarkerSource = this.map.getSource('markers'); this.markers.push(...aoData.markers);
oMarkerSource.setData({ aoData.messages.forEach(oMarker => this.addMarker(oMarker));
type: 'FeatureCollection',
features: [...oMarkerSource._data.features, ...this.convertMsgToFeatures(aoData.messages)]
});
} }
//TODO medias
//Message Last Update //Message Last Update
this.lastUpdate = aoData.last_update; this.lastUpdate = aoData.last_update;
//Reschedule //Reschedule
this.setFeedUpdateTimer(this.refreshRate); this.setFeedUpdateTimer(this.refreshRate);
}, },
panToBetweenPanels(oLngLat, iZoom, fCallback) { panToBetweenPanels(oLngLat, iZoom, iAnimDuration=500) {
const iXOffset = (this.settingsPanelOpen?getOuterWidth(this.$refs.settings):0) - (this.feedPanelOpen?getOuterWidth(this.$refs.feed):0); const iXOffset = (this.settingsPanelOpen?getOuterWidth(this.$refs.settings):0) - (this.feedPanelOpen?getOuterWidth(this.$refs.feed):0);
this.map.once('moveend', fCallback); return new Promise((resolve) => {
this.map.once('moveend', resolve);
this.map.easeTo({ this.map.easeTo({
center: oLngLat, center: oLngLat,
zoom: iZoom, zoom: iZoom,
offset: [iXOffset / 2, 0], offset: [iXOffset / 2, 0],
duration: 500 duration: iAnimDuration
});
}); });
}, },
isMarkerVisible(oLngLat){ isMarkerVisible(oLngLat){
@@ -610,33 +557,6 @@ export default {
break; break;
} }
} }
},
async findPost(oPost) {
let oRef = this.goToPost(oPost);
if(oRef) {
oRef.executeMainAction();
}
else if(!this.feed.outOfData) {
await this.getNextFeed();
this.findPost(oPost);
}
else console.log('Missing element ID "'+oPost.id+'" of type "'+oPost.type+'"');
},
goToPost(oPost) {
let bFound = false;
let aoRefs = this.$refs.posts.filter((post) => {return post.postId == oPost.type+'-'+oPost.id;});
if(aoRefs.length == 1) {
let oRef = aoRefs[0];
this.$refs.feedSimpleBar.scrollElement.scrollTop += Math.round(
oRef.$el.getBoundingClientRect().top
+ window.pageYOffset
- parseFloat(getComputedStyle(this.$refs.feedSimpleBar.$el).paddingTop)
);
//this.hash.items = [this.hash.items[0]];
return oRef;
}
} }
} }
} }

View File

@@ -11,6 +11,7 @@ export default {
options: Object, options: Object,
type: String type: String
}, },
emits: ['opening-lightbox'],
data() { data() {
return { return {
title:'' title:''
@@ -41,6 +42,7 @@ export default {
:data-id="options.id_media" :data-id="options.id_media"
:data-title="title" :data-title="title"
:data-orientation="options.rotate" :data-orientation="options.rotate"
@click="$emit('opening-lightbox', $event)"
ref="link" ref="link"
> >
<img <img

View File

@@ -13,23 +13,24 @@ export default {
projectRelTime projectRelTime
}, },
props: { props: {
type: String,
options: Object, options: Object,
medias: Array,
project: Object project: Object
}, },
inject: ['lang', 'consts'], inject: ['lang', 'consts'],
computed: { computed: {
timeIcon() { timeIcon() {
return (this.type == 'media')?'image-shot':'time'; return (this.options.type == 'media')?'image-shot':'time';
},
medias() {
return (this.options.type == 'media')?[this.options]:this.options?.medias;
} }
} }
} }
</script> </script>
<template> <template>
<div :class="type"> <div :class="options.type">
<div class="header" v-if="type=='message'"> <div class="header" v-if="options.type=='message'">
<h1> <h1>
<spotIcon :icon="'message'" size="lg" :text="lang.get('feed.counter', options.displayed_id)" width="auto" /> <spotIcon :icon="'message'" size="lg" :text="lang.get('feed.counter', options.displayed_id)" width="auto" />
<span class="message-type">({{ options.type }})</span> <span class="message-type">({{ options.type }})</span>
@@ -50,8 +51,8 @@ export default {
<div class="section weather" v-if="options.weather_icon && options.weather_icon!='unknown'" :title="options.weather_cond==''?'':lang.get('weather.'+options.weather_icon)"> <div class="section weather" v-if="options.weather_icon && options.weather_icon!='unknown'" :title="options.weather_cond==''?'':lang.get('weather.'+options.weather_icon)">
<spotIcon :icon="options.weather_icon" fixed-width size="lg" :text="options.weather_temp+'°C'" /> <spotIcon :icon="options.weather_icon" fixed-width size="lg" :text="options.weather_temp+'°C'" />
</div> </div>
<div v-if="medias.length > 0" class="section medias"> <div v-if="medias" class="section medias">
<spotIcon v-if="type=='message'" icon="media" fixed-width size="lg" :text="lang.get('media.nearby')" /> <spotIcon v-if="options.type=='message'" icon="media" fixed-width size="lg" :text="lang.get('media.nearby')" />
<div class="medias-list"> <div class="medias-list">
<projectMediaLink v-for="media in medias" :options="media" :type="'marker'" /> <projectMediaLink v-for="media in medias" :options="media" :type="'marker'" />
</div> </div>

View File

@@ -84,19 +84,19 @@
this.anchorIcon = 'link'; this.anchorIcon = 'link';
}, 5000); }, 5000);
}, },
panMapToMessage() { panMapToMarker(iAnimDuration=500) {
this.popupRequested = true; this.popupRequested = true;
if(this.isMobile()) this.project.toggleFeedPanel(false, 'panToInstant'); if(this.isMobile()) this.project.toggleFeedPanel(false, 'panToInstant');
this.map.panToBetweenPanels( this.hash.items = [this.hash.items[0], this.options.type, this.options.id];
this.lngLat,
this.focusZoomLevel, return this.map.panToBetweenPanels(this.lngLat, this.focusZoomLevel, iAnimDuration).then(() => {
() => {this.map.openMarkerPopup(this.options.id_message);} this.openMarkerPopup();
); });
this.hash.items = [this.hash.items[0], this.options.type, this.options.id_message];
}, },
openMarkerPopup() { openMarkerPopup() {
this.mouseOverDrill = true; this.mouseOverDrill = true;
if(this.map.isMarkerVisible(this.lngLat)) this.map.openMarkerPopup(this.options.id_message); if(this.map.isMarkerVisible(this.lngLat)) this.map.openMarkerPopup(this.options.id, this.options.type);
}, },
closeMarkerPopup() { closeMarkerPopup() {
this.mouseOverDrill = false; this.mouseOverDrill = false;
@@ -127,12 +127,12 @@
executeMainAction() { executeMainAction() {
switch(this.options.type) { switch(this.options.type) {
case 'message': case 'message':
this.panMapToMessage(); return this.panMapToMarker(0);
break;
case 'media': case 'media':
this.$refs.medialink.openMedia(); this.$refs.medialink.openMedia();
if(this.lngLat) this.map.panToBetweenPanels(this.lngLat, this.focusZoomLevel); if(this.lngLat) return this.panMapToMarker(0);
break; default:
return Promise.resolve();
} }
} }
}, },
@@ -161,12 +161,12 @@
</div> </div>
<div class="body"> <div class="body">
<div v-if="options.type == 'message'" class="body-box"> <div v-if="options.type == 'message'" class="body-box">
<div class="drill" @click.prevent="executeMainAction" @mouseenter="openMarkerPopup" @mouseleave="closeMarkerPopup"> <div class="drill" @click.prevent="() => {this.panMapToMarker();}" @mouseenter="openMarkerPopup" @mouseleave="closeMarkerPopup">
<span v-if="options.weather_icon && options.weather_icon!='unknown'" class="weather clickable" :title="lang.get('weather.'+options.weather_icon)"> <span v-if="options.weather_icon && options.weather_icon!='unknown'" class="weather clickable" :title="lang.get('weather.'+options.weather_icon)">
<spotIcon :icon="options.weather_icon" :text="Math.round(options.weather_temp)+'°C'" text-classes="temperature" /> <spotIcon :icon="options.weather_icon" :text="Math.round(options.weather_temp)+'°C'" text-classes="temperature" />
</span> </span>
<img class="staticmap clickable" :title="lang.get('media.click_zoom')" :src="options.static_img_url" /> <img class="staticmap clickable" :title="lang.get('media.click_zoom')" :src="options.static_img_url" />
<spotIconStack :mainClasses="'message drill-icon'" :iconMain="drillMainIcon" :iconSub="'footprint'" :icon-sub-transform="'rotate-270'" /> <spotIconStack :mainClasses="'message drill-icon'" :iconMain="drillMainIcon" iconSub="footprint" :icon-sub-transform="'rotate-270'" />
<div class="comment"> <div class="comment">
<p> <p>
<spotIcon :icon="'coords'" margin="right" size="lg" /> <spotIcon :icon="'coords'" margin="right" size="lg" />
@@ -179,7 +179,7 @@
</div> </div>
</div> </div>
<div v-else-if="options.type == 'media'" class="body-box"> <div v-else-if="options.type == 'media'" class="body-box">
<projectMediaLink :options="options" :type="'post'" ref="medialink" /> <projectMediaLink :options="options" :type="'post'" ref="medialink" @opening-lightbox="() => {if(this.lngLat) return this.panMapToMarker();}" />
</div> </div>
<div v-else-if="options.type == 'post'"> <div v-else-if="options.type == 'post'">
<p class="message">{{ options.content }}</p> <p class="message">{{ options.content }}</p>

View File

@@ -88,7 +88,6 @@ const ICONS = {
map: faMapLocationDot, map: faMapLocationDot,
marker: faLocationPin, marker: faLocationPin,
footprint: faShoePrints, footprint: faShoePrints,
'message-in': faShoePrints,
'track-off-track': faPersonHiking, 'track-off-track': faPersonHiking,
'track-main': faPersonHiking, 'track-main': faPersonHiking,
'track-hitchhiking': faCarSide, 'track-hitchhiking': faCarSide,

View File

@@ -10,6 +10,11 @@ $thumbnail-max-size: 60px;
top: 0; top: 0;
bottom: 0; bottom: 0;
width: 100%; width: 100%;
visibility: hidden;
.map-ready & {
visibility: visible;
}
.maplibregl-popup { .maplibregl-popup {
max-width: 300px; max-width: 300px;