From 6c711f80b47f30801ed3471923500240bd27370d Mon Sep 17 00:00:00 2001
From: l5y <220195275+l5yth@users.noreply.github.com>
Date: Wed, 31 Dec 2025 12:42:53 +0100
Subject: [PATCH] web: hide legend by default (#582)
* web: hide legend my default
* web: run rufo
---
.../__tests__/map-legend-visibility.test.js | 41 +++++++++++++++++++
web/public/assets/js/app/main.js | 11 ++++-
.../assets/js/app/map-legend-visibility.js | 26 ++++++++++++
web/views/index.erb | 2 +-
web/views/map.erb | 2 +-
web/views/shared/_map_panel.erb | 8 +++-
6 files changed, 85 insertions(+), 5 deletions(-)
create mode 100644 web/public/assets/js/app/__tests__/map-legend-visibility.test.js
create mode 100644 web/public/assets/js/app/map-legend-visibility.js
diff --git a/web/public/assets/js/app/__tests__/map-legend-visibility.test.js b/web/public/assets/js/app/__tests__/map-legend-visibility.test.js
new file mode 100644
index 0000000..4348166
--- /dev/null
+++ b/web/public/assets/js/app/__tests__/map-legend-visibility.test.js
@@ -0,0 +1,41 @@
+/*
+ * Copyright © 2025-26 l5yth & contributors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import test from 'node:test';
+import assert from 'node:assert/strict';
+
+import { resolveLegendVisibility } from '../map-legend-visibility.js';
+
+test('resolveLegendVisibility hides when a default collapse is requested', () => {
+ assert.equal(resolveLegendVisibility({ defaultCollapsed: true, mediaQueryMatches: false }), false);
+ assert.equal(resolveLegendVisibility({ defaultCollapsed: true, mediaQueryMatches: true }), false);
+});
+
+test('resolveLegendVisibility hides for dashboard and map views', () => {
+ assert.equal(
+ resolveLegendVisibility({ defaultCollapsed: false, mediaQueryMatches: false, viewMode: 'dashboard' }),
+ false
+ );
+ assert.equal(
+ resolveLegendVisibility({ defaultCollapsed: false, mediaQueryMatches: false, viewMode: 'map' }),
+ false
+ );
+});
+
+test('resolveLegendVisibility follows the media query when not forced', () => {
+ assert.equal(resolveLegendVisibility({ defaultCollapsed: false, mediaQueryMatches: false }), true);
+ assert.equal(resolveLegendVisibility({ defaultCollapsed: false, mediaQueryMatches: true }), false);
+});
diff --git a/web/public/assets/js/app/main.js b/web/public/assets/js/app/main.js
index 5b1944f..0d4c4c1 100644
--- a/web/public/assets/js/app/main.js
+++ b/web/public/assets/js/app/main.js
@@ -18,6 +18,7 @@ import { computeBoundingBox, computeBoundsForPoints, haversineDistanceKm } from
import { createMapAutoFitController } from './map-auto-fit-controller.js';
import { resolveAutoFitBoundsConfig } from './map-auto-fit-settings.js';
import { attachNodeInfoRefreshToMarker, overlayToPopupNode } from './map-marker-node-info.js';
+import { resolveLegendVisibility } from './map-legend-visibility.js';
import { createMapFocusHandler, DEFAULT_NODE_FOCUS_ZOOM } from './nodes-map-focus.js';
import { enhanceCoordinateCell } from './nodes-coordinate-links.js';
import { createShortInfoOverlayStack } from './short-info-overlay-manager.js';
@@ -116,6 +117,7 @@ export function initializeApp(config) {
: false;
const isDashboardView = bodyClassList ? bodyClassList.contains('view-dashboard') : false;
const isChatView = bodyClassList ? bodyClassList.contains('view-chat') : false;
+ const isMapView = bodyClassList ? bodyClassList.contains('view-map') : false;
const mapZoomOverride = Number.isFinite(config.mapZoom) ? Number(config.mapZoom) : null;
/**
* Column sorter configuration for the node table.
@@ -435,6 +437,7 @@ export function initializeApp(config) {
const mapFullscreenToggle = document.getElementById('mapFullscreenToggle');
const fullscreenContainer = mapPanel || mapContainer;
const isFederationView = bodyClassList ? bodyClassList.contains('view-federation') : false;
+ const legendDefaultCollapsed = mapPanel ? mapPanel.dataset.legendCollapsed === 'true' : false;
let mapStatusEl = null;
let map = null;
let mapCenterLatLng = null;
@@ -1526,8 +1529,14 @@ export function initializeApp(config) {
legendToggleControl.addTo(map);
const legendMediaQuery = window.matchMedia('(max-width: 1024px)');
- setLegendVisibility(!legendMediaQuery.matches);
+ const initialLegendVisible = resolveLegendVisibility({
+ defaultCollapsed: legendDefaultCollapsed,
+ mediaQueryMatches: legendMediaQuery.matches,
+ viewMode: isDashboardView ? 'dashboard' : (isMapView ? 'map' : undefined)
+ });
+ setLegendVisibility(initialLegendVisible);
legendMediaQuery.addEventListener('change', event => {
+ if (legendDefaultCollapsed || isDashboardView || isMapView) return;
setLegendVisibility(!event.matches);
});
} else if (mapContainer && !hasLeaflet) {
diff --git a/web/public/assets/js/app/map-legend-visibility.js b/web/public/assets/js/app/map-legend-visibility.js
new file mode 100644
index 0000000..7435802
--- /dev/null
+++ b/web/public/assets/js/app/map-legend-visibility.js
@@ -0,0 +1,26 @@
+/*
+ * Copyright © 2025-26 l5yth & contributors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Resolve the initial visibility of the map legend.
+ *
+ * @param {{ defaultCollapsed: boolean, mediaQueryMatches: boolean, viewMode?: string }} options
+ * @returns {boolean} True when the legend should be visible.
+ */
+export function resolveLegendVisibility({ defaultCollapsed, mediaQueryMatches, viewMode }) {
+ if (defaultCollapsed || viewMode === 'dashboard' || viewMode === 'map') return false;
+ return !mediaQueryMatches;
+}
diff --git a/web/views/index.erb b/web/views/index.erb
index e8d6298..c4d1be5 100644
--- a/web/views/index.erb
+++ b/web/views/index.erb
@@ -17,7 +17,7 @@
<% unless private_mode %>
<%= erb :"shared/_chat_panel", locals: { full_screen: false } %>
<% end %>
- <%= erb :"shared/_map_panel", locals: { full_screen: false } %>
+ <%= erb :"shared/_map_panel", locals: { full_screen: false, legend_collapsed: true } %>
<%= erb :"shared/_nodes_table", locals: { full_screen: false } %>
diff --git a/web/views/map.erb b/web/views/map.erb
index 87621c0..228b2db 100644
--- a/web/views/map.erb
+++ b/web/views/map.erb
@@ -14,5 +14,5 @@
limitations under the License.
-->