From b4a1ea356c8abd0d123ff0e95b92482955b75243 Mon Sep 17 00:00:00 2001 From: giswqs Date: Sat, 8 Aug 2026 09:13:55 -0400 Subject: [PATCH 1/2] feat: open the Style panel only when explicitly requested Selecting a layer no longer pops the Style panel open over the map, which made the workspace jump around during ordinary layer picking. The panel now expands only from the new "Open Style panel" item in a layer's menu. --- .../src/components/layout/DesktopShell.tsx | 8 ++++++ .../src/components/panels/LayerPanel.tsx | 12 ++++++++ .../src/components/panels/StylePanel.tsx | 28 +++++++------------ .../geolibre-desktop/src/i18n/locales/en.json | 1 + docs/tutorials/first-map.md | 2 +- 5 files changed, 32 insertions(+), 19 deletions(-) diff --git a/apps/geolibre-desktop/src/components/layout/DesktopShell.tsx b/apps/geolibre-desktop/src/components/layout/DesktopShell.tsx index ae656a8166..120efc7c96 100644 --- a/apps/geolibre-desktop/src/components/layout/DesktopShell.tsx +++ b/apps/geolibre-desktop/src/components/layout/DesktopShell.tsx @@ -866,6 +866,10 @@ export function DesktopShell({ useNetcdfIdentify(mapControllerRef, mapReadyGeneration); const [layerPanelWidth, setLayerPanelWidth] = useState(initialSidePanelWidth); const [stylePanelWidth, setStylePanelWidth] = useState(initialSidePanelWidth); + const [stylePanelOpenRequest, setStylePanelOpenRequest] = useState(0); + const openStylePanel = useCallback(() => { + setStylePanelOpenRequest((request) => request + 1); + }, []); const [notebookPanelWidth, setNotebookPanelWidth] = useState(DEFAULT_NOTEBOOK_PANEL_WIDTH); // Opening the notebook (Processing → Jupyter Notebook) splits the workspace // 50/50 between the map and the notebook: we size the notebook to half of the @@ -2245,6 +2249,7 @@ export function DesktopShell({ onOpenRasterStylePanel={() => openRasterLayerPanel(createAppAPI(mapControllerRef)) } + onOpenStylePanel={openStylePanel} onOpenRasterSubset={setRasterSubsetLayer} collapsed={collapsed} onCollapsedChange={onCollapsedChange} @@ -2268,6 +2273,7 @@ export function DesktopShell({ onOpenRasterStylePanel={() => openRasterLayerPanel(createAppAPI(mapControllerRef)) } + onOpenStylePanel={openStylePanel} onOpenRasterSubset={setRasterSubsetLayer} autoCollapse={ storymapPresenting || @@ -2493,6 +2499,7 @@ export function DesktopShell({ void; /** Open the floating Add Raster Layer panel for advanced raster styling. */ onOpenRasterStylePanel: () => void; + /** Select the target layer and expand the built-in Style panel. */ + onOpenStylePanel: () => void; /** * Open the floating Extract Subset panel for a COG/WMS/XYZ layer, letting the * user draw a bounding box and export a clipped GeoTIFF. @@ -608,6 +610,7 @@ export function LayerPanel({ onCancelGeometryEdit, onMaterializeDuckDBLayer, onOpenRasterStylePanel, + onOpenStylePanel, onOpenRasterSubset, autoCollapse = false, collapsed: controlledCollapsed, @@ -3364,6 +3367,15 @@ export function LayerPanel({ action item below has no such focus target, so each lets Radix dismiss the menu on select rather than leaving it pinned open. */} + { + selectLayer(layer.id); + onOpenStylePanel(); + }} + > + + {t("layers.openStylePanel")} + { addLayerGroup(undefined, moveIds); diff --git a/apps/geolibre-desktop/src/components/panels/StylePanel.tsx b/apps/geolibre-desktop/src/components/panels/StylePanel.tsx index 3a4da86b98..cbcd8c77da 100644 --- a/apps/geolibre-desktop/src/components/panels/StylePanel.tsx +++ b/apps/geolibre-desktop/src/components/panels/StylePanel.tsx @@ -173,6 +173,8 @@ function labelOverrideInvalid( interface StylePanelProps { mapControllerRef: RefObject; onResizeStart: (event: ReactPointerEvent) => void; + /** Incremented when another part of the UI explicitly requests this panel. */ + openRequest?: number; /** * When this flips to `true` the panel collapses to its thin rail (it is not * unmounted). Used to clear room when the notebook opens beside the map; the @@ -984,6 +986,7 @@ function RasterStyleSlider({ export function StylePanel({ mapControllerRef, onResizeStart, + openRequest = 0, autoCollapse = false, collapsed: controlledCollapsed, onCollapsedChange, @@ -998,8 +1001,8 @@ export function StylePanel({ const updateLayer = useAppStore((s) => s.updateLayer); const moveLayer = useAppStore((s) => s.moveLayer); const projectName = useAppStore((s) => s.projectName); - // Style starts on its rail on every platform. Selecting a real layer below - // expands it; selecting the special Background row does not. + // Style starts on its rail on every platform and remains there until the + // user explicitly expands it. const [internalCollapsed, setInternalCollapsed] = useState(true); // In the shared right-sidebar mode the parent owns collapse (controlled); // otherwise the panel manages it locally. `setIsCollapsed` routes to whichever @@ -1013,23 +1016,12 @@ export function StylePanel({ }, [isControlled, onCollapsedChange], ); - // Selecting a real layer expands the panel from its rail. Skipped while - // `autoCollapse` holds it closed (the notebook or a story-map presentation - // owns the workspace), so a selection made there cannot pop Style back open - // over them and defeat the auto-collapse below. - const previousSelectedLayerId = useRef(selectedLayerId); + const previousOpenRequest = useRef(openRequest); useEffect(() => { - const previous = previousSelectedLayerId.current; - previousSelectedLayerId.current = selectedLayerId; - if ( - !autoCollapse && - selectedLayerId && - selectedLayerId !== previous && - layers.some((candidate) => candidate.id === selectedLayerId) - ) { - setIsCollapsed(false); - } - }, [autoCollapse, layers, selectedLayerId, setIsCollapsed]); + if (openRequest === previousOpenRequest.current) return; + previousOpenRequest.current = openRequest; + setIsCollapsed(false); + }, [openRequest, setIsCollapsed]); // Collapse to the rail when `autoCollapse` flips on (e.g. the notebook opens), // and restore the prior expand/collapse state when it flips back off (notebook // closes). Both act only on the transition so the user can still toggle the diff --git a/apps/geolibre-desktop/src/i18n/locales/en.json b/apps/geolibre-desktop/src/i18n/locales/en.json index 70840f5307..a584167465 100644 --- a/apps/geolibre-desktop/src/i18n/locales/en.json +++ b/apps/geolibre-desktop/src/i18n/locales/en.json @@ -4892,6 +4892,7 @@ "bindWindowCumulative": "Everything up to the current step (cumulative)", "bindCancel": "Cancel", "bindConfirm": "Bind", + "openStylePanel": "Open Style panel", "openRasterStylePanel": "Edit raster style…", "exportRasterSuccess": "Raster exported.", "exportRasterError": "Could not export this raster.", diff --git a/docs/tutorials/first-map.md b/docs/tutorials/first-map.md index 6ac00df73f..481628947d 100644 --- a/docs/tutorials/first-map.md +++ b/docs/tutorials/first-map.md @@ -21,7 +21,7 @@ See [Adding Data](../user-guide/adding-data.md) for every supported source. ## 3. Style the layer -1. Select the `countries` layer in the Layers panel. The [Style panel](../user-guide/styling.md) opens on the right. +1. Select the `countries` layer in the Layers panel, then expand the [Style panel](../user-guide/styling.md) on the right if it is collapsed. 2. Adjust the **Fill color**, **Outline color**, and **Fill opacity** to taste. 3. To make a choropleth, set **Style type** to **Graduated**, pick a numeric field (for example a population or GDP column), choose a **Colormap**, and click **Apply style type**. From 06a4988bc05ef153332fdb22184146f92aa3f060 Mon Sep 17 00:00:00 2001 From: giswqs Date: Sat, 8 Aug 2026 09:22:06 -0400 Subject: [PATCH 2/2] Address review feedback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Gate the Style panel's open-request effect on `autoCollapse`, so a request made while the notebook or a story-map presentation owns the workspace can no longer force the panel open over them. The `autoCollapse` effect acts only on transitions, so such an expand would have stuck until the notebook was closed and reopened. - Hide the "Open Style panel" menu item when the Style panel is hidden (Settings → "Show Style panel"). The panel is not mounted then, so the request was dropped rather than queued and the item did nothing. --- .../src/components/layout/DesktopShell.tsx | 6 ++-- .../src/components/panels/LayerPanel.tsx | 29 ++++++++++++------- .../src/components/panels/StylePanel.tsx | 11 +++++-- 3 files changed, 31 insertions(+), 15 deletions(-) diff --git a/apps/geolibre-desktop/src/components/layout/DesktopShell.tsx b/apps/geolibre-desktop/src/components/layout/DesktopShell.tsx index 120efc7c96..90d197caf7 100644 --- a/apps/geolibre-desktop/src/components/layout/DesktopShell.tsx +++ b/apps/geolibre-desktop/src/components/layout/DesktopShell.tsx @@ -2249,7 +2249,9 @@ export function DesktopShell({ onOpenRasterStylePanel={() => openRasterLayerPanel(createAppAPI(mapControllerRef)) } - onOpenStylePanel={openStylePanel} + onOpenStylePanel={ + layoutOptions.stylePanelVisible ? openStylePanel : undefined + } onOpenRasterSubset={setRasterSubsetLayer} collapsed={collapsed} onCollapsedChange={onCollapsedChange} @@ -2273,7 +2275,7 @@ export function DesktopShell({ onOpenRasterStylePanel={() => openRasterLayerPanel(createAppAPI(mapControllerRef)) } - onOpenStylePanel={openStylePanel} + onOpenStylePanel={layoutOptions.stylePanelVisible ? openStylePanel : undefined} onOpenRasterSubset={setRasterSubsetLayer} autoCollapse={ storymapPresenting || diff --git a/apps/geolibre-desktop/src/components/panels/LayerPanel.tsx b/apps/geolibre-desktop/src/components/panels/LayerPanel.tsx index ae34e564a0..968b97fa0c 100644 --- a/apps/geolibre-desktop/src/components/panels/LayerPanel.tsx +++ b/apps/geolibre-desktop/src/components/panels/LayerPanel.tsx @@ -243,8 +243,13 @@ interface LayerPanelProps { onMaterializeDuckDBLayer: (layer: GeoLibreLayer) => void; /** Open the floating Add Raster Layer panel for advanced raster styling. */ onOpenRasterStylePanel: () => void; - /** Select the target layer and expand the built-in Style panel. */ - onOpenStylePanel: () => void; + /** + * Select the target layer and expand the built-in Style panel. Left undefined + * when that panel is hidden (Settings → "Show Style panel"), which also hides + * the menu item — the panel is not mounted then, so the request would be + * dropped rather than queued. + */ + onOpenStylePanel?: () => void; /** * Open the floating Extract Subset panel for a COG/WMS/XYZ layer, letting the * user draw a bounding box and export a clipped GeoTIFF. @@ -3367,15 +3372,17 @@ export function LayerPanel({ action item below has no such focus target, so each lets Radix dismiss the menu on select rather than leaving it pinned open. */} - { - selectLayer(layer.id); - onOpenStylePanel(); - }} - > - - {t("layers.openStylePanel")} - + {onOpenStylePanel && ( + { + selectLayer(layer.id); + onOpenStylePanel(); + }} + > + + {t("layers.openStylePanel")} + + )} { addLayerGroup(undefined, moveIds); diff --git a/apps/geolibre-desktop/src/components/panels/StylePanel.tsx b/apps/geolibre-desktop/src/components/panels/StylePanel.tsx index cbcd8c77da..39d6bcf950 100644 --- a/apps/geolibre-desktop/src/components/panels/StylePanel.tsx +++ b/apps/geolibre-desktop/src/components/panels/StylePanel.tsx @@ -1016,12 +1016,19 @@ export function StylePanel({ }, [isControlled, onCollapsedChange], ); + // An explicit request (Layers → "Open Style panel") expands the panel from its + // rail. Skipped while `autoCollapse` holds it closed (the notebook or a + // story-map presentation owns the workspace), so a request made there cannot + // pop Style back open over them: the `autoCollapse` effect below acts only on + // transitions, so an expand that slipped through would stick until the + // notebook was closed and reopened. The request is still consumed so it does + // not fire later. const previousOpenRequest = useRef(openRequest); useEffect(() => { if (openRequest === previousOpenRequest.current) return; previousOpenRequest.current = openRequest; - setIsCollapsed(false); - }, [openRequest, setIsCollapsed]); + if (!autoCollapse) setIsCollapsed(false); + }, [autoCollapse, openRequest, setIsCollapsed]); // Collapse to the rail when `autoCollapse` flips on (e.g. the notebook opens), // and restore the prior expand/collapse state when it flips back off (notebook // closes). Both act only on the transition so the user can still toggle the