feat(reseaux): /agences branché sur VueFiches ; recherche, tags, famille et carte= dans l'URL (B11-M6)
Hashtags désormais en OU dans leur dimension (ET avant), ET avec la famille. 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
2e82d412ed
commit
a98342c5f1
+45
-10
@@ -21,10 +21,14 @@ const FAMILLE_COLORS: Record<number, string> = {
|
||||
const props = defineProps<{
|
||||
structures: StructureV2[]
|
||||
selectedId?: string | null
|
||||
/** Fiche survolée dans la liste (B11) : épingle cerclée de safran, sans recentrer. */
|
||||
survolId?: string | null
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
'select-structure': [id: string]
|
||||
/** Survol d'une épingle (B11) : la liste allume la carte-fiche. */
|
||||
survol: [id: string | null]
|
||||
}>()
|
||||
|
||||
const mapContainer = ref<HTMLElement | null>(null)
|
||||
@@ -32,17 +36,18 @@ let mapInstance: Map | null = null
|
||||
let clusterGroup: any = null
|
||||
const markers = new Map<string, Marker>()
|
||||
let tileLayerInstance: any = null
|
||||
let resizeObserver: ResizeObserver | null = null
|
||||
|
||||
function getFamilleColor(famille: number): string {
|
||||
return FAMILLE_COLORS[famille] ?? '#888888'
|
||||
}
|
||||
|
||||
function createPinIcon(famille: number, isSelected = false): DivIcon {
|
||||
function createPinIcon(famille: number, isSelected = false, isSurvol = false): DivIcon {
|
||||
const L = (window as any).L
|
||||
const bg = getFamilleColor(famille)
|
||||
const size = isSelected ? 20 : 14
|
||||
const border = isSelected ? 'white' : 'rgba(255,255,255,0.7)'
|
||||
const shadow = isSelected ? `0 0 0 4px ${bg}55` : 'none'
|
||||
const size = isSelected || isSurvol ? 20 : 14
|
||||
const border = isSurvol ? '#f5b342' : isSelected ? 'white' : 'rgba(255,255,255,0.7)'
|
||||
const shadow = isSurvol ? '0 0 0 4px rgba(245,179,66,0.6)' : isSelected ? `0 0 0 4px ${bg}55` : 'none'
|
||||
|
||||
return L.divIcon({
|
||||
className: '',
|
||||
@@ -76,6 +81,7 @@ async function initMap() {
|
||||
;(window as any).L = L
|
||||
// @ts-ignore
|
||||
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, {
|
||||
@@ -121,6 +127,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(s: StructureV2): DivIcon {
|
||||
return createPinIcon(s.famille_principale, s.id === props.selectedId, s.id === props.survolId)
|
||||
}
|
||||
|
||||
function updateMarkers(L?: any) {
|
||||
@@ -136,10 +150,7 @@ function updateMarkers(L?: any) {
|
||||
)
|
||||
|
||||
structuresWithCoords.forEach((structure) => {
|
||||
const isSelected = structure.id === props.selectedId
|
||||
const icon = createPinIcon(structure.famille_principale, isSelected)
|
||||
|
||||
const marker = leaflet.marker([structure.latitude!, structure.longitude!], { icon })
|
||||
const marker = leaflet.marker([structure.latitude!, structure.longitude!], { icon: iconeDe(structure), title: structure.nom })
|
||||
|
||||
const hashtagsHtml = structure.hashtags.slice(0, 2)
|
||||
.map(h => `<span style="font-size:10px;color:var(--nav-text-muted);">${h}</span>`)
|
||||
@@ -165,6 +176,8 @@ function updateMarkers(L?: any) {
|
||||
`, { maxWidth: 260 })
|
||||
|
||||
marker.on('click', () => emit('select-structure', structure.id))
|
||||
marker.on('mouseover', () => emit('survol', structure.id))
|
||||
marker.on('mouseout', () => emit('survol', null))
|
||||
|
||||
markers.set(structure.id, marker)
|
||||
clusterGroup.addLayer(marker)
|
||||
@@ -193,14 +206,14 @@ watch(
|
||||
const oldMarker = markers.get(oldId)
|
||||
const oldStructure = props.structures.find(s => s.id === oldId)
|
||||
if (oldMarker && oldStructure) {
|
||||
oldMarker.setIcon(createPinIcon(oldStructure.famille_principale, false))
|
||||
oldMarker.setIcon(iconeDe(oldStructure))
|
||||
}
|
||||
}
|
||||
if (newId != null) {
|
||||
const newMarker = markers.get(newId)
|
||||
const newStructure = props.structures.find(s => s.id === newId)
|
||||
if (newMarker && newStructure) {
|
||||
newMarker.setIcon(createPinIcon(newStructure.famille_principale, true))
|
||||
newMarker.setIcon(iconeDe(newStructure))
|
||||
const latLng = newMarker.getLatLng()
|
||||
mapInstance.panTo(latLng, { animate: true })
|
||||
}
|
||||
@@ -208,6 +221,27 @@ watch(
|
||||
}
|
||||
)
|
||||
|
||||
// Survol depuis la liste : épingle cerclée de 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 s = props.structures.find(x => x.id === id)
|
||||
if (m && s) m.setIcon(iconeDe(s))
|
||||
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' : ''
|
||||
}
|
||||
|
||||
function updateTileTheme(dark: boolean) {
|
||||
if (!mapInstance || !tileLayerInstance) return
|
||||
const L = (window as any).L
|
||||
@@ -232,6 +266,7 @@ onMounted(() => {
|
||||
onUnmounted(() => {
|
||||
document.removeEventListener('nav-v2-select', onNavV2Select as EventListener)
|
||||
themeObserver?.disconnect()
|
||||
resizeObserver?.disconnect()
|
||||
if (mapInstance) {
|
||||
mapInstance.remove()
|
||||
mapInstance = null
|
||||
|
||||
Reference in New Issue
Block a user