feat(entraide): / branché sur VueFiches ; NavSidebar, FichesPanel, MobileSheet et onglets remplacés (B11-M5)
Survol fiche ↔ épingle (cluster compris), invalidateSize au redimensionnement, titre accessible des épingles, ?random=1 fiable au chargement direct. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SCt6e8wWiYqRXLJAuKqP5i
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
5a4c7af79c
commit
2e82d412ed
+46
-11
@@ -24,10 +24,14 @@ interface Org {
|
||||
const props = defineProps<{
|
||||
orgs: Org[]
|
||||
selectedId?: number | null
|
||||
/** Fiche survolée dans la liste (B11) : épingle safran, sans recentrer. */
|
||||
survolId?: number | null
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
'select-org': [id: number]
|
||||
/** Survol d'une épingle (B11) : la liste allume la carte-fiche. */
|
||||
survol: [id: number | null]
|
||||
}>()
|
||||
|
||||
const mapContainer = ref<HTMLElement | null>(null)
|
||||
@@ -35,14 +39,15 @@ let mapInstance: Map | null = null
|
||||
let clusterGroup: any = null
|
||||
const markers = new Map<number, Marker>()
|
||||
let tileLayerInstance: any = null
|
||||
let resizeObserver: ResizeObserver | null = null
|
||||
|
||||
// Créer une DivIcon pour les pins personnalisés
|
||||
function createPinIcon(isPrioritaire: boolean, isSelected = false): DivIcon {
|
||||
function createPinIcon(isPrioritaire: boolean, isSelected = false, isSurvol = false): DivIcon {
|
||||
const L = (window as any).L
|
||||
const bg = isPrioritaire ? '#f5b342' : 'rgba(26, 34, 56, 0.6)'
|
||||
const border = isPrioritaire ? '#1a2238' : '#ffffff'
|
||||
const size = isSelected ? 18 : 14
|
||||
const shadow = isSelected ? '0 0 0 4px rgba(245,179,66,0.5)' : 'none'
|
||||
const bg = isSurvol || isPrioritaire ? '#f5b342' : 'rgba(26, 34, 56, 0.6)'
|
||||
const border = isPrioritaire || isSurvol ? '#1a2238' : '#ffffff'
|
||||
const size = isSelected || isSurvol ? 18 : 14
|
||||
const shadow = isSelected || isSurvol ? '0 0 0 4px rgba(245,179,66,0.5)' : 'none'
|
||||
|
||||
return L.divIcon({
|
||||
className: '',
|
||||
@@ -76,6 +81,7 @@ async function initMap() {
|
||||
;(window as any).L = L
|
||||
// @ts-ignore — étend L.MarkerClusterGroup en side effect
|
||||
await import('leaflet.markercluster')
|
||||
if (!mapContainer.value?.isConnected) return // page quittée pendant le chargement (ex. ?random=1)
|
||||
const MarkerClusterGroup = L.MarkerClusterGroup
|
||||
|
||||
mapInstance = L.map(mapContainer.value, {
|
||||
@@ -125,6 +131,14 @@ async function initMap() {
|
||||
|
||||
mapInstance.addLayer(clusterGroup)
|
||||
updateMarkers(L)
|
||||
|
||||
// B11 : la carte change de largeur sans que la fenêtre bouge (Mixte ↔ Carte ↔ Fiches)
|
||||
resizeObserver = new ResizeObserver(() => mapInstance?.invalidateSize({ pan: false }))
|
||||
resizeObserver.observe(mapContainer.value)
|
||||
}
|
||||
|
||||
function iconeDe(org: Org): DivIcon {
|
||||
return createPinIcon(!!org.prioritaire, org.Id === props.selectedId, org.Id === props.survolId)
|
||||
}
|
||||
|
||||
function updateMarkers(L?: any) {
|
||||
@@ -141,10 +155,7 @@ function updateMarkers(L?: any) {
|
||||
)
|
||||
|
||||
orgsWithCoords.forEach((org) => {
|
||||
const isSelected = org.Id === props.selectedId
|
||||
const icon = createPinIcon(!!org.prioritaire, isSelected)
|
||||
|
||||
const marker = leaflet.marker([org.latitude!, org.longitude!], { icon })
|
||||
const marker = leaflet.marker([org.latitude!, org.longitude!], { icon: iconeDe(org), title: org.nom })
|
||||
|
||||
const fonctions = org.tags_fonction
|
||||
? org.tags_fonction.split(',').map((f: string) => f.trim()).filter(Boolean).slice(0, 2).join(', ')
|
||||
@@ -163,6 +174,8 @@ function updateMarkers(L?: any) {
|
||||
`, { maxWidth: 240 })
|
||||
|
||||
marker.on('click', () => emit('select-org', org.Id))
|
||||
marker.on('mouseover', () => emit('survol', org.Id))
|
||||
marker.on('mouseout', () => emit('survol', null))
|
||||
|
||||
markers.set(org.Id, marker)
|
||||
clusterGroup.addLayer(marker)
|
||||
@@ -189,7 +202,7 @@ watch(
|
||||
const oldMarker = markers.get(oldId)
|
||||
const oldOrg = props.orgs.find(o => o.Id === oldId)
|
||||
if (oldMarker && oldOrg) {
|
||||
oldMarker.setIcon(createPinIcon(!!oldOrg.prioritaire, false))
|
||||
oldMarker.setIcon(iconeDe(oldOrg))
|
||||
}
|
||||
}
|
||||
// Mettre en avant le nouveau marker
|
||||
@@ -197,7 +210,7 @@ watch(
|
||||
const newMarker = markers.get(newId)
|
||||
const newOrg = props.orgs.find(o => o.Id === newId)
|
||||
if (newMarker && newOrg) {
|
||||
newMarker.setIcon(createPinIcon(!!newOrg.prioritaire, true))
|
||||
newMarker.setIcon(iconeDe(newOrg))
|
||||
// Centrer si visible
|
||||
const latLng = newMarker.getLatLng()
|
||||
mapInstance.panTo(latLng, { animate: true })
|
||||
@@ -206,6 +219,27 @@ watch(
|
||||
}
|
||||
)
|
||||
|
||||
// Survol depuis la liste : épingle safran, sans recentrer la carte
|
||||
watch(
|
||||
() => props.survolId,
|
||||
(newId, oldId) => {
|
||||
for (const id of [oldId, newId]) {
|
||||
if (id == null) continue
|
||||
const m = markers.get(id)
|
||||
const o = props.orgs.find(x => x.Id === id)
|
||||
if (m && o) m.setIcon(iconeDe(o))
|
||||
allumerCluster(m, id === newId)
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
// Épingle regroupée dans un cluster : c'est le cluster visible qui s'allume
|
||||
function allumerCluster(m: Marker | undefined, on: boolean) {
|
||||
const parent = m && clusterGroup?.getVisibleParent(m)
|
||||
const el = parent && parent !== m ? (parent as any)._icon?.firstElementChild as HTMLElement | undefined : undefined
|
||||
if (el) el.style.boxShadow = on ? '0 0 0 4px #f5b342' : ''
|
||||
}
|
||||
|
||||
// Watcher dark mode — switch tuile CartoDB light_all ↔ dark_all
|
||||
function updateTileTheme(dark: boolean) {
|
||||
if (!mapInstance || !tileLayerInstance) return
|
||||
@@ -235,6 +269,7 @@ onMounted(() => {
|
||||
|
||||
onUnmounted(() => {
|
||||
themeObserver?.disconnect()
|
||||
resizeObserver?.disconnect()
|
||||
if (mapInstance) {
|
||||
mapInstance.remove()
|
||||
mapInstance = null
|
||||
|
||||
Reference in New Issue
Block a user