Spaces:
Running
Running
File size: 2,787 Bytes
e8db196 d6c9678 e8db196 d6c9678 e8db196 d6c9678 e8db196 d6c9678 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 | import React, { useCallback, useEffect, useMemo, useRef } from 'react'
function hashHtml(value) {
let hash = 0
const step = Math.max(1, Math.floor(value.length / 64))
for (let i = 0; i < value.length; i += step) {
hash = ((hash << 5) - hash) + value.charCodeAt(i)
hash |= 0
}
return `${value.length}-${Math.abs(hash)}`
}
export default function MapFrame({ html }) {
const iframeRef = useRef(null)
const timersRef = useRef([])
const frameKey = useMemo(() => hashHtml(html || ''), [html])
const clearTimers = useCallback(() => {
timersRef.current.forEach((id) => window.clearTimeout(id))
timersRef.current = []
}, [])
const recenterFromLayers = useCallback(() => {
const iframe = iframeRef.current
if (!iframe) return
try {
const win = iframe.contentWindow
const doc = iframe.contentDocument || win?.document
if (!win || !doc || !win.L) return
const maps = Object.values(win).filter(
(item) => item
&& typeof item.fitBounds === 'function'
&& typeof item.eachLayer === 'function'
&& typeof item.invalidateSize === 'function',
)
const map = maps[0]
if (!map) return
map.invalidateSize(true)
const bounds = win.L.latLngBounds([])
map.eachLayer((layer) => {
try {
if (typeof layer.getLatLng === 'function') {
const latlng = layer.getLatLng()
if (latlng && Number.isFinite(latlng.lat) && Number.isFinite(latlng.lng)) {
bounds.extend(latlng)
}
return
}
if (typeof layer.getBounds === 'function') {
const layerBounds = layer.getBounds()
if (layerBounds && typeof layerBounds.isValid === 'function' && layerBounds.isValid()) {
bounds.extend(layerBounds)
}
}
} catch {
// no-op
}
})
if (bounds.isValid()) {
map.fitBounds(bounds, { padding: [20, 20], maxZoom: 17, animate: false })
}
} catch {
// no-op
}
}, [])
const scheduleRecenter = useCallback(() => {
clearTimers()
;[40, 180, 520, 1100].forEach((delay) => {
const timerId = window.setTimeout(() => {
recenterFromLayers()
}, delay)
timersRef.current.push(timerId)
})
}, [clearTimers, recenterFromLayers])
useEffect(() => {
return () => {
clearTimers()
}
}, [clearTimers])
if (!html) {
return <div className="empty-box">Mapa indisponivel.</div>
}
return (
<iframe
key={frameKey}
ref={iframeRef}
title="mapa"
className="map-frame"
srcDoc={html}
sandbox="allow-scripts allow-same-origin allow-popups"
onLoad={scheduleRecenter}
/>
)
}
|