11<template >
2- <Vue2LeafletMarkerCluster :options =" clusterOptions "
2+ <Vue2LeafletMarkerCluster
3+ ref="markerCluster"
4+ :options =" clusterOptions "
35 @clusterclick =" onClusterClick "
46 @clustercontextmenu =" onClusterRightClick "
57 @spiderfied =" onSpiderfied " >
6- <LMarker v-for =" (p , i ) in photoSuggestions "
7- :key =" i "
8+ <!-- <LMarker v-for="(p, i) in photoSuggestions"
89 v-if="p"
10+ :key="i"
911 :options="{ data: p }"
1012 :icon="getPhotoMarkerIcon(p, i)"
1113 :draggable="draggable"
3537 {{ t('maps', 'Display picture') }}
3638 </NcActionButton>
3739 </LPopup>
40+ </LMarker>-->
41+ <LMarker
42+ :lat-lng =" [0 , 0 ] "
43+ :visible =" false " >
44+ <LTooltip
45+ ref="markerTooltip"
46+ :class =" {
47+ ' tooltip-photo-suggestion-wrapper' : true ,
48+ ' photo-suggestion-marker-selected' : photoSuggestionsSelectedIndices .includes (currentSuggestion ?.i )
49+ } "
50+ :options =" { ... tooltipOptions , opacity: draggable ? 0 : 1 } " >
51+ <img class =" photo-suggestion-tooltip"
52+ :src =" getPreviewUrl(currentSuggestion)" >
53+ <p class =" tooltip-photo-suggestion-date" >
54+ {{ getPhotoFormattedDate(currentSuggestion) }}
55+ </p >
56+ <p class =" tooltip-photo-suggestion-name" >
57+ {{ currentSuggestion ? basename(currentSuggestion.path) : ''}}
58+ </p >
59+ </LTooltip >
60+ <LPopup
61+ ref="markerPopup"
62+ class="popup-photo-wrapper"
63+ :options =" popupOptions " >
64+ <NcActionButton icon="icon-toggle" @click =" viewPhoto (currentSuggestion )" >
65+ {{ t('maps', 'Display picture') }}
66+ </NcActionButton >
67+ </LPopup >
3868 </LMarker >
3969 <LMarker
4070 :lat-lng =" [0 , 0 ] "
@@ -68,6 +98,7 @@ import { LMarker, LTooltip, LPopup } from 'vue2-leaflet'
6898import Vue2LeafletMarkerCluster from ' vue2-leaflet-markercluster'
6999
70100import optionsController from ' ../../optionsController'
101+ import {binSearch } from " ../../utils/common" ;
71102
72103const PHOTO_MARKER_VIEW_SIZE = 40
73104
@@ -94,6 +125,18 @@ export default {
94125 type: Array ,
95126 required: true ,
96127 },
128+ dateFilterEnabled: {
129+ type: Boolean ,
130+ required: true ,
131+ },
132+ dateFilterStart: {
133+ type: Number ,
134+ required: true ,
135+ },
136+ dateFilterEnd: {
137+ type: Number ,
138+ required: true ,
139+ },
97140 draggable: {
98141 type: Boolean ,
99142 required: true ,
@@ -106,12 +149,17 @@ export default {
106149 clusterOptions: {
107150 iconCreateFunction: this .getClusterMarkerIcon ,
108151 spiderfyOnMaxZoom: false ,
152+ singleMarkerMode: true ,
109153 showCoverageOnHover: false ,
110154 zoomToBoundsOnClick: false ,
111155 maxClusterRadius: PHOTO_MARKER_VIEW_SIZE + 10 ,
112156 icon: {
113157 iconSize: [PHOTO_MARKER_VIEW_SIZE , PHOTO_MARKER_VIEW_SIZE ],
114158 },
159+ chunkedLoading: true ,
160+ chunkDelay: 50 ,
161+ chunkInterval: 250 ,
162+ chunkProgress: this .updateClusterLoadingProgress ,
115163 },
116164 tooltipOptions: {
117165 className: ' leaflet-marker-photo-suggestion-tooltip' ,
@@ -130,15 +178,101 @@ export default {
130178 },
131179 contextCluster: null ,
132180 spiderfied: false ,
181+ currentSuggestion: null ,
182+ suggestionMarkers: [],
183+ clustersLoading: false ,
133184 }
134185 },
135186
136187 computed: {
188+ suggestionsLastNullIndex () {
189+ return this .dateFilterEnabled ? binSearch (this .photoSuggestions , (p ) => ! p .dateTaken ) : - 1
190+ },
191+ suggestionsFirstShownIndex () {
192+ return this .dateFilterEnabled ? binSearch (this .photoSuggestions , (p ) => (p .dateTaken || 0 ) < this .dateFilterStart ) + 1 : 0
193+ },
194+ suggestionsLastShownIndex () {
195+ return this .dateFilterEnabled ? binSearch (this .photoSuggestions , (p ) => (p .dateTaken || 0 ) < this .dateFilterEnd ) : this .photoSuggestions .length - 1
196+ },
197+ },
198+
199+ watch: {
200+ photoSuggestions () {
201+ this .updateSuggestionMarkers ()
202+ },
203+ draggable () {
204+ this .updateSuggestionMarkersDraggable ()
205+ },
206+ dateFilterEnabled (newValue ) {
207+ if (newValue) {
208+ this .$refs .markerCluster .mapObject .removeLayers (
209+ this .suggestionMarkers .slice (
210+ this .suggestionsLastNullIndex + 1 ,
211+ this .suggestionsFirstShownIndex
212+ )
213+ )
214+ this .$refs .markerCluster .mapObject .removeLayers (
215+ this .suggestionMarkers .slice (
216+ this .suggestionsLastShownIndex + 1 ,
217+ )
218+ )
219+ } else {
220+ this .$refs .markerCluster .mapObject .addLayers (
221+ this .suggestionMarkers .slice (
222+ this .suggestionsLastNullIndex + 1 ,
223+ this .suggestionsFirstShownIndex
224+ )
225+ )
226+ this .$refs .markerCluster .mapObject .addLayers (
227+ this .suggestionMarkers .slice (
228+ this .suggestionsLastShownIndex + 1 ,
229+ )
230+ )
231+ }
232+ },
233+ suggestionsFirstShownIndex (newIndex , oldIndex ) {
234+ if (newIndex < oldIndex) {
235+ this .$refs .markerCluster .mapObject .addLayers (
236+ this .suggestionMarkers .slice (
237+ newIndex,
238+ oldIndex
239+ )
240+ )
241+ } else if (newIndex > oldIndex) {
242+ this .$refs .markerCluster .mapObject .removeLayers (
243+ this .suggestionMarkers .slice (
244+ oldIndex,
245+ newIndex
246+ )
247+ )
248+ }
249+ },
250+ suggestionsLastShownIndex (newIndex , oldIndex ) {
251+ if (newIndex < oldIndex) {
252+ this .$refs .markerCluster .mapObject .removeLayers (
253+ this .suggestionMarkers .slice (
254+ newIndex + 1 ,
255+ oldIndex + 1
256+ )
257+ )
258+ } else if (newIndex > oldIndex) {
259+ this .$refs .markerCluster .mapObject .addLayers (
260+ this .suggestionMarkers .slice (
261+ oldIndex + 1 ,
262+ newIndex + 1
263+ )
264+ )
265+ }
266+ },
137267 },
138268
139269 beforeMount () {
140270 },
141271
272+ mounted () {
273+ this .updateSuggestionMarkers ()
274+ },
275+
142276 methods: {
143277 basename (path ) {
144278 return basename (path)
@@ -184,7 +318,7 @@ export default {
184318 },
185319 displayCluster (cluster ) {
186320 const photoList = cluster .getAllChildMarkers ().map ((m ) => {
187- return m .options . data
321+ return m .data
188322 })
189323 photoList .sort ((a , b ) => {
190324 return a .dateTaken - b .dateTaken
@@ -194,20 +328,23 @@ export default {
194328 this .map .closePopup ()
195329 },
196330 getClusterMarkerIcon (cluster ) {
197- const photo = cluster .getAllChildMarkers ()[0 ].options .data
331+ const count = cluster .getChildCount ()
332+ const photo = cluster .getAllChildMarkers ()[0 ].data
333+ if (count === 1 ) {
334+ return this .getPhotoMarkerIcon (photo)
335+ }
198336 const iconUrl = this .getPreviewUrl (photo)
199- const label = cluster .getChildCount ()
200337 return new L.DivIcon (L .extend ({
201338 className: ' leaflet-marker-photo-suggestion cluster-suggestion-marker' ,
202- html: ' <div class="thumbnail" style="background-image: url(' + iconUrl + ' );"></div><span class="label">' + label + ' </span>' ,
339+ html: ' <div class="thumbnail" style="background-image: url(' + iconUrl + ' );"></div><span class="label">' + count + ' </span>' ,
203340 }, cluster, {
204341 iconSize: [PHOTO_MARKER_VIEW_SIZE , PHOTO_MARKER_VIEW_SIZE ],
205342 iconAnchor: [PHOTO_MARKER_VIEW_SIZE / 2 , PHOTO_MARKER_VIEW_SIZE ],
206343 }))
207344 },
208- getPhotoMarkerIcon (photo , i ) {
345+ getPhotoMarkerIcon (photo ) {
209346 const iconUrl = this .getPreviewUrl (photo)
210- const selectedClass = this .photoSuggestionsSelectedIndices .includes (i)
347+ const selectedClass = this .photoSuggestionsSelectedIndices .includes (photo . i )
211348 ? ' -selected'
212349 : ' '
213350 return L .divIcon (L .extend ({
@@ -219,14 +356,15 @@ export default {
219356 }))
220357 },
221358 getPreviewUrl (photo ) {
222- return photo .hasPreview
359+ return photo && photo .hasPreview
223360 ? generateUrl (' core' ) + ' /preview?fileId=' + photo .fileId + ' &x=341&y=256&a=1'
224361 : generateUrl (' /apps/theming/img/core/filetypes' ) + ' /image.svg?v=2'
225362 },
226363 getPhotoFormattedDate (photo ) {
227- return moment .unix (photo .dateTaken ).format (' LLL' )
364+ return photo ? moment .unix (photo .dateTaken ).format (' LLL' ) : ' '
228365 },
229- onPhotoClick (e , index ) {
366+ onPhotoClick (e ) {
367+ const index = e .target .i
230368 // we want popup to open on right click only
231369 this .$nextTick (() => {
232370 e .target .closePopup ()
@@ -239,35 +377,94 @@ export default {
239377 this .map .closePopup ()
240378 }
241379 },
242- onPhotoRightClick (e , photo ) {
380+ onPhotoRightClick (e ) {
381+ const photo = e .target .data
382+ this .currentSuggestion = photo
383+ const popup = this .$refs .markerPopup .mapObject
384+ popup .setLatLng ([photo .lat , photo .lng ])
385+ this .$nextTick (() => {
386+ popup .openOn (this .map )
387+ })
388+ },
389+ onPhotoMouseOver (e ) {
390+ const photo = e .target .data
391+ this .currentSuggestion = photo
392+ const tooltip = this .$refs .markerTooltip .mapObject
393+ tooltip .setLatLng ([photo .lat , photo .lng ])
243394 this .$nextTick (() => {
244- e . target . openPopup ( )
395+ tooltip . openOn ( this . map )
245396 })
246397 },
247- resetClusterPhotoCoords () {
248- const clusterSize = this .contextCluster .getChildCount ()
249- OC .dialogs .confirmDestructive (
250- ' ' ,
251- t (' maps' , ' Are you sure you want to remove geo data of {nb} photos?' , { nb: clusterSize }),
252- {
253- type: OC .dialogs .YES_NO_BUTTONS ,
254- confirm: t (' maps' , ' Yes' ),
255- confirmClasses: ' ' ,
256- cancel: t (' maps' , ' Cancel' ),
257- },
258- (result ) => {
259- if (result) {
260- const photos = this .contextCluster .getAllChildMarkers ().map ((m ) => {
261- return m .options .data
262- })
263- this .resetPhotosCoords (photos)
264- }
265- },
266- true
267- )
398+ onPhotoMouseOut (e ) {
399+ const tooltip = this .$refs .markerTooltip .mapObject
400+ tooltip .close ()
401+ },
402+ onPhotoMoved (e ) {
403+ this .$emit (' photo-suggestion-moved' , e .target .i , e .target .getLatLng ())
268404 },
269- onPhotoMoved (e , index ) {
270- this .$emit (' photo-suggestion-moved' , index, e .target .getLatLng ())
405+ updateClusterLoadingProgress (processed , total , elapsed , layersArray ) {
406+ if (elapsed > 100 && ! this .clustersLoading ) {
407+ this .clustersLoading = true
408+ }
409+ this .$emit (' cluster-loading' , processed, total)
410+
411+ if (processed === total) {
412+ this .clustersLoading = false
413+ // all markers processed - hide the progress bar:
414+ this .$emit (' cluster-loaded' )
415+ }
416+ },
417+
418+ async updateSuggestionMarkers () {
419+ this .$refs .markerCluster .mapObject .removeLayers (this .suggestionMarkers )
420+ this .suggestionMarkers = this .photoSuggestions .map ((p , i ) => {
421+ const m = new L.Marker ([p .lat , p .lng ],
422+ {
423+ draggable: this .draggable ,
424+ },
425+ )
426+ m .on (
427+ ' click' , this .onPhotoClick
428+ )
429+ m .on (
430+ ' contextmenu' , this .onPhotoRightClick
431+ )
432+ m .on (
433+ ' mouseover' , this .onPhotoMouseOver
434+ )
435+ m .on (
436+ ' mouseout' , this .onPhotoMouseOut
437+ )
438+ m .on (
439+ ' moveend' , this .onPhotoMoved
440+ )
441+ m .data = p
442+ m .i = i
443+ return m
444+ })
445+ this .$refs .markerCluster .mapObject .addLayers (this .suggestionMarkers )
446+ if (this .dateFilterEnabled ) {
447+ this .$refs .markerCluster .mapObject .removeLayers (
448+ this .suggestionMarkers .slice (
449+ this .suggestionsLastNullIndex + 1 ,
450+ this .suggestionsFirstShownIndex
451+ )
452+ )
453+ this .$refs .markerCluster .mapObject .removeLayers (
454+ this .suggestionMarkers .slice (
455+ this .suggestionsLastShownIndex + 1 ,
456+ )
457+ )
458+ }
459+ },
460+ async updateSuggestionMarkersDraggable () {
461+ this .suggestionMarkers .forEach ((m ) => {
462+ if (m .dragging ) {
463+ this .draggable ? m .dragging .enable () : m .dragging .disable ()
464+ }
465+ m .options .draggable = this .draggable
466+ m .update ()
467+ })
271468 },
272469 },
273470}
0 commit comments