fix(geo): AOI map blank — RAF poll до появления div + shrink-0 защита
DevTools на пользовательском скрине показал: - Map div СУЩЕСТВУЕТ в DOM (display:block, height:520 inline) - Реальный размер 974×254 — flex-shrink порезал до ~50% запрошенного потому что parent (Modal body) overflow-hidden + max-h:calc(100vh-2rem) на узком viewport (497px) не вмещал 520+ контента. - Класса 'leaflet-container' на div НЕТ — L.map() так и не вызвался. Корень: Modal от @nstart/ui рендерит children только после useAnimatedPresence фликает isMounted=true (на след. рендер после isOpen=true). Мой useEffect фаерится РАНЬШЕ — div ещё не в DOM, mapDivRef.current=null. Старый guard 'if (!div) return' выходил БЕЗ schedule RAF — больше никогда не пробовал. Фикс в tryInit: schedule RAF и для null div, и для 0-size. Cap 100 кадров (~1.6s). Это finally доводит до L.map() когда div появится и получит размер. Plus: shrink-0 + min-height: 520 на map div чтобы flex-shrink не ужимал до 254px на узких viewport. Visual quality 520px stable.
This commit is contained in:
@@ -62,14 +62,21 @@ export const AoiPickerDialog = ({ isOpen, onClose, onApply, initial }: Props) =>
|
|||||||
|
|
||||||
const tryInit = () => {
|
const tryInit = () => {
|
||||||
if (cancelled) return
|
if (cancelled) return
|
||||||
|
if (mapRef.current) return // уже init'нут
|
||||||
const div = mapDivRef.current
|
const div = mapDivRef.current
|
||||||
if (!div || mapRef.current) return
|
|
||||||
|
|
||||||
// Defer пока container не имеет реальный размер — RAF до 100 кадров
|
// Modal от @nstart/ui рендерит children только после useAnimatedPresence
|
||||||
// (~1.6s @ 60fps), потом сдаёмся и init'им как есть (fallback).
|
// флипает isMounted=true (на след. рендер после isOpen=true). Наш useEffect
|
||||||
if ((div.clientWidth === 0 || div.clientHeight === 0) && rafId < 100) {
|
// фаерится РАНЬШЕ — div ещё не в DOM. Plus modal transform animation
|
||||||
|
// изначально даёт 0×0 размер. Poll'им RAF до тех пор пока:
|
||||||
|
// - div существует (ref populated на render когда modal mounted'нул его)
|
||||||
|
// - clientWidth и clientHeight > 0 (после layout & transform settled)
|
||||||
|
// Cap на 100 кадров (~1.6s) — fallback если что-то совсем странное.
|
||||||
|
if (!div || div.clientWidth === 0 || div.clientHeight === 0) {
|
||||||
|
if (rafId < 100) {
|
||||||
rafId += 1
|
rafId += 1
|
||||||
requestAnimationFrame(tryInit)
|
requestAnimationFrame(tryInit)
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -180,12 +187,12 @@ export const AoiPickerDialog = ({ isOpen, onClose, onApply, initial }: Props) =>
|
|||||||
|
|
||||||
<div
|
<div
|
||||||
ref={mapDivRef}
|
ref={mapDivRef}
|
||||||
className="rounded-sm border border-regolith"
|
// shrink-0 защищает от flex-shrink в overflow-hidden parent body Modal'а:
|
||||||
// Hardcoded pixel height + block display — full decoupling от flex/vh.
|
// на узком viewport (height < 700px) panel max-h сжимал body, flex-shrink
|
||||||
// Leaflet требует non-zero containerSize в момент L.map() create().
|
// обрезал map с 520px до ~250px (DevTools confirmed). shrink-0 + min-h
|
||||||
// 60vh раньше иногда читался как 0 во время Modal transform animation;
|
// фиксируют 520 даже когда panel переполнен.
|
||||||
// 520px стабильно.
|
className="rounded-sm border border-regolith shrink-0"
|
||||||
style={{ display: 'block', width: '100%', height: 520 }}
|
style={{ display: 'block', width: '100%', height: 520, minHeight: 520 }}
|
||||||
aria-label={t('aoi.dialog.mapAriaLabel')}
|
aria-label={t('aoi.dialog.mapAriaLabel')}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user