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. -->
- <%= erb :"shared/_map_panel", locals: { full_screen: true } %> + <%= erb :"shared/_map_panel", locals: { full_screen: true, legend_collapsed: true } %>
diff --git a/web/views/shared/_map_panel.erb b/web/views/shared/_map_panel.erb index 6f5236c..6020075 100644 --- a/web/views/shared/_map_panel.erb +++ b/web/views/shared/_map_panel.erb @@ -14,8 +14,12 @@ limitations under the License. --> <% map_classes = ["map-panel"] - map_classes << "map-panel--full" if defined?(full_screen) && full_screen %> -
" id="mapPanel"> + map_classes << "map-panel--full" if defined?(full_screen) && full_screen + data_attrs = [] + if defined?(legend_collapsed) + data_attrs << "data-legend-collapsed=\\" #{legend_collapsed ? 'true' : 'false'}\\"" + end %> +
" id="mapPanel" <%= data_attrs.join(" ") %>>