Pause map autofit when user initiates pan/zoom (#26114)

* Pause map autofit when user initiates pan/zoom

* not a state

* a different approach
This commit is contained in:
karwosts 2025-07-09 07:32:20 -07:00 committed by GitHub
parent 5233086efb
commit e7e062a222
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 60 additions and 7 deletions

View File

@ -36,6 +36,8 @@ declare global {
} }
} }
const PROGRAMMITIC_FIT_DELAY = 250;
const getEntityId = (entity: string | HaMapEntity): string => const getEntityId = (entity: string | HaMapEntity): string =>
typeof entity === "string" ? entity : entity.entity_id; typeof entity === "string" ? entity : entity.entity_id;
@ -113,14 +115,33 @@ export class HaMap extends ReactiveElement {
private _clickCount = 0; private _clickCount = 0;
private _isProgrammaticFit = false;
private _pauseAutoFit = false;
public connectedCallback(): void { public connectedCallback(): void {
this._pauseAutoFit = false;
document.addEventListener("visibilitychange", this._handleVisibilityChange);
this._handleVisibilityChange();
super.connectedCallback(); super.connectedCallback();
this._loadMap(); this._loadMap();
this._attachObserver(); this._attachObserver();
} }
private _handleVisibilityChange = async () => {
if (!document.hidden) {
setTimeout(() => {
this._pauseAutoFit = false;
}, 500);
}
};
public disconnectedCallback(): void { public disconnectedCallback(): void {
super.disconnectedCallback(); super.disconnectedCallback();
document.removeEventListener(
"visibilitychange",
this._handleVisibilityChange
);
if (this.leafletMap) { if (this.leafletMap) {
this.leafletMap.remove(); this.leafletMap.remove();
this.leafletMap = undefined; this.leafletMap = undefined;
@ -145,7 +166,7 @@ export class HaMap extends ReactiveElement {
if (changedProps.has("_loaded") || changedProps.has("entities")) { if (changedProps.has("_loaded") || changedProps.has("entities")) {
this._drawEntities(); this._drawEntities();
autoFitRequired = true; autoFitRequired = !this._pauseAutoFit;
} else if (this._loaded && oldHass && this.entities) { } else if (this._loaded && oldHass && this.entities) {
// Check if any state has changed // Check if any state has changed
for (const entity of this.entities) { for (const entity of this.entities) {
@ -154,7 +175,7 @@ export class HaMap extends ReactiveElement {
this.hass!.states[getEntityId(entity)] this.hass!.states[getEntityId(entity)]
) { ) {
this._drawEntities(); this._drawEntities();
autoFitRequired = true; autoFitRequired = !this._pauseAutoFit;
break; break;
} }
} }
@ -178,7 +199,11 @@ export class HaMap extends ReactiveElement {
} }
if (changedProps.has("zoom")) { if (changedProps.has("zoom")) {
this._isProgrammaticFit = true;
this.leafletMap!.setZoom(this.zoom); this.leafletMap!.setZoom(this.zoom);
setTimeout(() => {
this._isProgrammaticFit = false;
}, PROGRAMMITIC_FIT_DELAY);
} }
if ( if (
@ -234,13 +259,30 @@ export class HaMap extends ReactiveElement {
} }
this._clickCount++; this._clickCount++;
}); });
this.leafletMap.on("zoomstart", () => {
if (!this._isProgrammaticFit) {
this._pauseAutoFit = true;
}
});
this.leafletMap.on("movestart", () => {
if (!this._isProgrammaticFit) {
this._pauseAutoFit = true;
}
});
this._loaded = true; this._loaded = true;
} finally { } finally {
this._loading = false; this._loading = false;
} }
} }
public fitMap(options?: { zoom?: number; pad?: number }): void { public fitMap(options?: {
zoom?: number;
pad?: number;
unpause_autofit?: boolean;
}): void {
if (options?.unpause_autofit) {
this._pauseAutoFit = false;
}
if (!this.leafletMap || !this.Leaflet || !this.hass) { if (!this.leafletMap || !this.Leaflet || !this.hass) {
return; return;
} }
@ -250,6 +292,7 @@ export class HaMap extends ReactiveElement {
!this._mapFocusZones.length && !this._mapFocusZones.length &&
!this.layers?.length !this.layers?.length
) { ) {
this._isProgrammaticFit = true;
this.leafletMap.setView( this.leafletMap.setView(
new this.Leaflet.LatLng( new this.Leaflet.LatLng(
this.hass.config.latitude, this.hass.config.latitude,
@ -257,6 +300,9 @@ export class HaMap extends ReactiveElement {
), ),
options?.zoom || this.zoom options?.zoom || this.zoom
); );
setTimeout(() => {
this._isProgrammaticFit = false;
}, PROGRAMMITIC_FIT_DELAY);
return; return;
} }
@ -277,8 +323,11 @@ export class HaMap extends ReactiveElement {
}); });
bounds = bounds.pad(options?.pad ?? 0.5); bounds = bounds.pad(options?.pad ?? 0.5);
this._isProgrammaticFit = true;
this.leafletMap.fitBounds(bounds, { maxZoom: options?.zoom || this.zoom }); this.leafletMap.fitBounds(bounds, { maxZoom: options?.zoom || this.zoom });
setTimeout(() => {
this._isProgrammaticFit = false;
}, PROGRAMMITIC_FIT_DELAY);
} }
public fitBounds( public fitBounds(
@ -291,7 +340,11 @@ export class HaMap extends ReactiveElement {
const bounds = this.Leaflet.latLngBounds(boundingbox).pad( const bounds = this.Leaflet.latLngBounds(boundingbox).pad(
options?.pad ?? 0.5 options?.pad ?? 0.5
); );
this._isProgrammaticFit = true;
this.leafletMap.fitBounds(bounds, { maxZoom: options?.zoom || this.zoom }); this.leafletMap.fitBounds(bounds, { maxZoom: options?.zoom || this.zoom });
setTimeout(() => {
this._isProgrammaticFit = false;
}, PROGRAMMITIC_FIT_DELAY);
} }
private _drawLayers(prevLayers: Layer[] | undefined): void { private _drawLayers(prevLayers: Layer[] | undefined): void {

View File

@ -239,7 +239,7 @@ class HuiMapCard extends LitElement implements LovelaceCard {
)} )}
.path=${mdiImageFilterCenterFocus} .path=${mdiImageFilterCenterFocus}
style=${isDarkMode ? "color:#ffffff" : "color:#000000"} style=${isDarkMode ? "color:#ffffff" : "color:#000000"}
@click=${this._fitMap} @click=${this._resetFocus}
tabindex="0" tabindex="0"
></ha-icon-button> ></ha-icon-button>
</div> </div>
@ -389,8 +389,8 @@ class HuiMapCard extends LitElement implements LovelaceCard {
: (root.style.paddingBottom = "100%"); : (root.style.paddingBottom = "100%");
} }
private _fitMap() { private _resetFocus() {
this._map?.fitMap(); this._map?.fitMap({ unpause_autofit: true });
} }
private _toggleClusterMarkers() { private _toggleClusterMarkers() {