diff --git a/docs/developer-guide/mapstore-migration-guide.md b/docs/developer-guide/mapstore-migration-guide.md index 367ea2c0d0..8ecfca3373 100644 --- a/docs/developer-guide/mapstore-migration-guide.md +++ b/docs/developer-guide/mapstore-migration-guide.md @@ -22,6 +22,12 @@ This is a list of things to check if you want to update from a previous version ## Migration from 2024.02.00 to 2025.01.00 +### New width variable for side panel plugins + +Existing projects may need to update the width size of plugins implemented as right side panels. + +The new width value is 420 px and is stored in a constant property called `DEFAULT_PANEL_WIDTH` available inside the `web/client/utils/LayoutUtils.js` file. + ### Removing Header from the Admin section and deprecating the old UserManager and GroupManagerPlugin The old `UserManager` and `GroupManager` plugin has been removed and replace with new plugins under the `web/client/plugins/ResourcesCatalog/` folder. Also the `Header` plugin has been removed from Admin/Manager so the configuration in `localConfig.json` should be updated as follow: diff --git a/web/client/components/data/identify/coordinates/Viewer.jsx b/web/client/components/data/identify/coordinates/Viewer.jsx index 2ca4474c92..a5ba3546e6 100644 --- a/web/client/components/data/identify/coordinates/Viewer.jsx +++ b/web/client/components/data/identify/coordinates/Viewer.jsx @@ -51,7 +51,7 @@ export default ({ ?
Lat: - Long:
:
Lat: - - + - Long:
} diff --git a/web/client/components/details/DetailsPanel.jsx b/web/client/components/details/DetailsPanel.jsx index b619e97526..094bd5ba59 100644 --- a/web/client/components/details/DetailsPanel.jsx +++ b/web/client/components/details/DetailsPanel.jsx @@ -12,7 +12,7 @@ import Message from '../I18N/Message'; import { Panel } from 'react-bootstrap'; import BorderLayout from '../layout/BorderLayout'; import ResponsivePanel from "../misc/panels/ResponsivePanel"; - +import { DEFAULT_PANEL_WIDTH } from '../../utils/LayoutUtils'; class DetailsPanel extends React.Component { static propTypes = { id: PropTypes.string, @@ -42,7 +42,7 @@ class DetailsPanel extends React.Component { }, active: false, panelClassName: "details-panel", - width: 550, + width: DEFAULT_PANEL_WIDTH, isDashboard: false }; diff --git a/web/client/components/misc/panels/DockPanel.jsx b/web/client/components/misc/panels/DockPanel.jsx index c92681d142..e15eac3df0 100644 --- a/web/client/components/misc/panels/DockPanel.jsx +++ b/web/client/components/misc/panels/DockPanel.jsx @@ -12,6 +12,7 @@ import Dock from 'react-dock'; import BorderLayout from '../../layout/BorderLayout'; import { withState } from 'recompose'; import PanelHeader from './PanelHeader'; +import { DEFAULT_PANEL_WIDTH } from '../../../utils/LayoutUtils'; /** * Component for rendering a DockPanel @@ -41,7 +42,7 @@ export default withState('fullscreen', 'onFullscreen', false)( fullscreen = false, position, open, - size = 550, + size = DEFAULT_PANEL_WIDTH, style = {}, zIndex = 1030, onClose, diff --git a/web/client/epics/__tests__/maplayout-test.js b/web/client/epics/__tests__/maplayout-test.js index a94a733718..39d7d29a8b 100644 --- a/web/client/epics/__tests__/maplayout-test.js +++ b/web/client/epics/__tests__/maplayout-test.js @@ -15,6 +15,7 @@ import { updateMapLayoutEpic } from '../maplayout'; import { testEpic, addTimeoutEpic, TEST_TIMEOUT } from './epicTestUtils'; import ConfigUtils from '../../utils/ConfigUtils'; import { openFeatureGrid } from "../../actions/featuregrid"; +import { DEFAULT_PANEL_WIDTH } from '../../utils/LayoutUtils'; describe('map layout epics', () => { afterEach(() => { @@ -27,11 +28,11 @@ describe('map layout epics', () => { actions.map((action) => { expect(action.type).toBe(UPDATE_MAP_LAYOUT); expect(action.layout).toEqual( - { left: 600, right: 548, bottom: 0, transform: 'none', height: 'calc(100% - 30px)', + { left: 600, right: DEFAULT_PANEL_WIDTH, bottom: 0, transform: 'none', height: 'calc(100% - 30px)', boundingMapRect: { bottom: 0, left: 600, - right: 548 + right: DEFAULT_PANEL_WIDTH }, boundingSidebarRect: { right: 0, left: 0, bottom: 0 }, leftPanel: true, @@ -55,11 +56,11 @@ describe('map layout epics', () => { actions.map((action) => { expect(action.type).toBe(UPDATE_MAP_LAYOUT); expect(action.layout).toEqual( - { left: 600, right: 588, bottom: 0, transform: 'none', height: 'calc(100% - 30px)', + { left: 600, right: DEFAULT_PANEL_WIDTH + 40, bottom: 0, transform: 'none', height: 'calc(100% - 30px)', boundingMapRect: { bottom: 0, left: 600, - right: 588 + right: DEFAULT_PANEL_WIDTH + 40 }, boundingSidebarRect: { right: 40, left: 0, bottom: 0 }, leftPanel: true, @@ -188,7 +189,7 @@ describe('map layout epics', () => { }); describe('tests layout updated for right panels', () => { - const epicResult = (done, right = 548) => actions => { + const epicResult = (done, right = DEFAULT_PANEL_WIDTH) => actions => { try { expect(actions.length).toBe(1); actions.map((action) => { diff --git a/web/client/epics/geoProcessing.js b/web/client/epics/geoProcessing.js index 5f4e7db85a..ba190cbf76 100644 --- a/web/client/epics/geoProcessing.js +++ b/web/client/epics/geoProcessing.js @@ -145,8 +145,9 @@ import {getFeatureInfo} from "../api/identify"; import {getFeatureSimple} from '../api/WFS'; import {findNonGeometryProperty, findGeometryProperty} from '../utils/ogc/WFS/base'; import toWKT from '../utils/ogc/WKT/toWKT'; +import { DEFAULT_PANEL_WIDTH } from '../utils/LayoutUtils'; -const OFFSET = 550; +const OFFSET = DEFAULT_PANEL_WIDTH; const DEACTIVATE_ACTIONS = [ changeDrawingStatus("stop"), changeDrawingStatus("clean", '', GPT_CONTROL_NAME) diff --git a/web/client/epics/identify.js b/web/client/epics/identify.js index a8d58686a0..2beffbc4fc 100644 --- a/web/client/epics/identify.js +++ b/web/client/epics/identify.js @@ -52,7 +52,8 @@ import { createControlEnabledSelector, measureSelector } from '../selectors/cont import { localizedLayerStylesEnvSelector } from '../selectors/localizedLayerStyles'; import { mouseOutSelector } from '../selectors/mousePosition'; import { hideEmptyPopupSelector } from '../selectors/mapPopups'; -import {getBbox, getCurrentResolution, parseLayoutValue} from '../utils/MapUtils'; +import {getBbox, getCurrentResolution} from '../utils/MapUtils'; +import { parseLayoutValue } from '../utils/LayoutUtils'; import {buildIdentifyRequest, defaultQueryableFilter, filterRequestParams} from '../utils/MapInfoUtils'; import { IDENTIFY_POPUP } from '../components/map/popups'; diff --git a/web/client/epics/longitudinalProfile.js b/web/client/epics/longitudinalProfile.js index e372a2a11b..15ee4fbd77 100644 --- a/web/client/epics/longitudinalProfile.js +++ b/web/client/epics/longitudinalProfile.js @@ -89,8 +89,9 @@ import {selectLineFeature} from "../utils/LongitudinalProfileUtils"; import {buildIdentifyRequest} from "../utils/MapInfoUtils"; import {getFeatureInfo} from "../api/identify"; import { drawerOwnerSelector } from "../selectors/draw"; +import { DEFAULT_PANEL_WIDTH } from '../utils/LayoutUtils'; -const OFFSET = 550; +const OFFSET = DEFAULT_PANEL_WIDTH; const DEACTIVATE_ACTIONS = [ changeDrawingStatus("stop"), diff --git a/web/client/epics/maplayout.js b/web/client/epics/maplayout.js index 81f597ccab..05b9067e44 100644 --- a/web/client/epics/maplayout.js +++ b/web/client/epics/maplayout.js @@ -33,7 +33,7 @@ import { mapInfoDetailsSettingsFromIdSelector, isMouseMoveIdentifyActiveSelector import {head, get, findIndex, keys} from 'lodash'; import { isFeatureGridOpen, getDockSize } from '../selectors/featuregrid'; -import {DEFAULT_MAP_LAYOUT} from "../utils/MapUtils"; +import {DEFAULT_MAP_LAYOUT} from "../utils/LayoutUtils"; import {dockPanelsSelector} from "../selectors/maplayout"; /** diff --git a/web/client/plugins/Details.jsx b/web/client/plugins/Details.jsx index a285c57311..0170d19565 100644 --- a/web/client/plugins/Details.jsx +++ b/web/client/plugins/Details.jsx @@ -32,6 +32,7 @@ import details from '../reducers/details'; import * as epics from '../epics/details'; import {createStructuredSelector} from "reselect"; import { getDashboardId } from '../selectors/dashboard'; +import { DEFAULT_PANEL_WIDTH } from '../utils/LayoutUtils'; /** * Allow to show details for the map. @@ -73,7 +74,7 @@ const DetailsPlugin = ({ : active && diff --git a/web/client/plugins/GeoProcessing/Panel.jsx b/web/client/plugins/GeoProcessing/Panel.jsx index 63fb75a759..cac72a20f1 100644 --- a/web/client/plugins/GeoProcessing/Panel.jsx +++ b/web/client/plugins/GeoProcessing/Panel.jsx @@ -18,6 +18,7 @@ import { toggleControl } from '../../actions/controls'; import { initPlugin } from '../../actions/geoProcessing'; import { isGeoProcessingEnabledSelector } from '../../selectors/controls'; import { dockStyleSelector } from '../../selectors/maplayout'; +import { DEFAULT_PANEL_WIDTH } from '../../utils/LayoutUtils'; const PanelComp = ({ dockStyle, @@ -36,7 +37,7 @@ const PanelComp = ({ containerClassName="dock-container" containerId="GeoProcessing-root" open={enabled} - size={550} + size={DEFAULT_PANEL_WIDTH} dock position="right" title={} diff --git a/web/client/plugins/Identify.jsx b/web/client/plugins/Identify.jsx index 9934212f27..ee54ad03eb 100644 --- a/web/client/plugins/Identify.jsx +++ b/web/client/plugins/Identify.jsx @@ -76,6 +76,7 @@ import { getDefaultInfoFormatValue, getValidator } from '../utils/MapInfoUtils'; import getFeatureButtons from './identify/featureButtons'; import getToolButtons from './identify/toolButtons'; import Message from './locale/Message'; +import { DEFAULT_PANEL_WIDTH } from '../utils/LayoutUtils'; const selector = createStructuredSelector({ enabled: (state) => mapInfoEnabledSelector(state) || state.controls && state.controls.info && state.controls.info.enabled || false, @@ -165,7 +166,7 @@ const identifyDefaultProps = defaultProps({ showMoreInfo: true, showEdit: false, position: 'right', - size: 550, + size: DEFAULT_PANEL_WIDTH, getToolButtons, getFeatureButtons, showFullscreen: false, diff --git a/web/client/plugins/MapCatalog.jsx b/web/client/plugins/MapCatalog.jsx index 0ed71ed563..07b4aefaf9 100644 --- a/web/client/plugins/MapCatalog.jsx +++ b/web/client/plugins/MapCatalog.jsx @@ -34,6 +34,7 @@ import * as epics from '../epics/mapcatalog'; import {mapLayoutValuesSelector} from "../selectors/maplayout"; import * as PropTypes from "prop-types"; import ResponsivePanel from "../components/misc/panels/ResponsivePanel"; +import { DEFAULT_PANEL_WIDTH } from '../utils/LayoutUtils'; /** * Allows users to existing maps directly on the map. @@ -62,7 +63,7 @@ class MapCatalogComponent extends React.Component { }, onDelete: () => { }, onSave: () => { }, dockStyle: {}, - size: 550 + size: DEFAULT_PANEL_WIDTH }; render() { diff --git a/web/client/plugins/MapTemplates.jsx b/web/client/plugins/MapTemplates.jsx index d902cd84d7..b4d5f22031 100644 --- a/web/client/plugins/MapTemplates.jsx +++ b/web/client/plugins/MapTemplates.jsx @@ -26,6 +26,7 @@ import * as epics from '../epics/maptemplates'; import {mapLayoutValuesSelector} from "../selectors/maplayout"; import PropTypes from "prop-types"; import ResponsivePanel from "../components/misc/panels/ResponsivePanel"; +import { DEFAULT_PANEL_WIDTH } from '../utils/LayoutUtils'; /** * Provides a list of map templates available inside of a currently loaded context. @@ -62,7 +63,7 @@ class MapTemplatesComponent extends React.Component { onReplaceTemplate: () => {}, onToggleFavourite: () => {}, onSetAllowedTemplates: () => {}, - size: 550 + size: DEFAULT_PANEL_WIDTH }; componentDidUpdate(prevProps) { diff --git a/web/client/plugins/MetadataExplorer.jsx b/web/client/plugins/MetadataExplorer.jsx index 082f7ee677..08474334a0 100644 --- a/web/client/plugins/MetadataExplorer.jsx +++ b/web/client/plugins/MetadataExplorer.jsx @@ -85,6 +85,7 @@ import { isLocalizedLayerStylesEnabledSelector } from '../selectors/localizedLay import { projectionSelector } from '../selectors/map'; import { mapLayoutValuesSelector } from '../selectors/maplayout'; import ResponsivePanel from "../components/misc/panels/ResponsivePanel"; +import { DEFAULT_PANEL_WIDTH } from '../utils/LayoutUtils'; import usePluginItems from '../hooks/usePluginItems'; import { setProtectedServices, setShowModalStatus } from '../actions/security'; @@ -205,7 +206,7 @@ class MetadataExplorerComponent extends React.Component { zoomToLayer: true, // side panel properties - width: 550, + width: DEFAULT_PANEL_WIDTH, dockProps: { dimMode: "none", fluid: false, diff --git a/web/client/plugins/UserExtensions.jsx b/web/client/plugins/UserExtensions.jsx index a4155de03b..f3cd0a3d91 100644 --- a/web/client/plugins/UserExtensions.jsx +++ b/web/client/plugins/UserExtensions.jsx @@ -22,7 +22,7 @@ import { setControlProperty, toggleControl } from '../actions/controls'; import * as epics from '../epics/userextensions'; import {mapLayoutValuesSelector} from "../selectors/maplayout"; import ResponsivePanel from "../components/misc/panels/ResponsivePanel"; - +import { DEFAULT_PANEL_WIDTH } from '../utils/LayoutUtils'; class Extensions extends React.Component { static propTypes = { @@ -36,7 +36,7 @@ class Extensions extends React.Component { active: false, onClose: () => {}, dockStyle: {}, - size: 550 + size: DEFAULT_PANEL_WIDTH } render() { diff --git a/web/client/plugins/longitudinalProfile/Dock.jsx b/web/client/plugins/longitudinalProfile/Dock.jsx index 0bdd243ff7..428dd6a096 100644 --- a/web/client/plugins/longitudinalProfile/Dock.jsx +++ b/web/client/plugins/longitudinalProfile/Dock.jsx @@ -27,6 +27,7 @@ import Toolbar from "../../components/misc/toolbar/Toolbar"; import Chart from "../../components/charts/WidgetChart"; import { reprojectGeoJson } from '../../utils/CoordinatesUtils'; import { getMessageById } from '../../utils/LocaleUtils'; +import { DEFAULT_PANEL_WIDTH } from '../../utils/LayoutUtils'; const NavItemT = tooltip(NavItem); const Button = tooltip(ButtonRB); @@ -453,7 +454,7 @@ const Dock = ({ position="right" title={} glyph="1-line" - size={550} + size={DEFAULT_PANEL_WIDTH} open={showDock} onClose={onCloseDock} style={dockStyle} diff --git a/web/client/selectors/maplayout.js b/web/client/selectors/maplayout.js index c0c35126a8..229f344895 100644 --- a/web/client/selectors/maplayout.js +++ b/web/client/selectors/maplayout.js @@ -8,7 +8,7 @@ import {head, memoize} from 'lodash'; import { mapSelector } from './map'; -import {DEFAULT_MAP_LAYOUT, parseLayoutValue} from '../utils/MapUtils'; +import {DEFAULT_MAP_LAYOUT, parseLayoutValue} from '../utils/LayoutUtils'; import ConfigUtils from "../utils/ConfigUtils"; diff --git a/web/client/themes/default/less/get-feature.less b/web/client/themes/default/less/get-feature.less index 660ed1f2b9..5e3433d821 100644 --- a/web/client/themes/default/less/get-feature.less +++ b/web/client/themes/default/less/get-feature.less @@ -96,6 +96,13 @@ } } } + .ms-coordinates-aeronautical { + display: flex; + flex-direction: column; + .ms-coordinates-aeronautical-separator { + display: none; + } + } .coordinateRow { .coordinate { .input-group { @@ -113,6 +120,11 @@ } } .coordinateRow { + &.aeronautical { + .input-group-container { + margin-right: 0; + } + } .coordinate { width: 100%; display: flex; @@ -126,7 +138,7 @@ } .input-group { .input-group-addon { - min-width: 45px; + padding: 4px; } width: 100%; .form-group{ diff --git a/web/client/utils/LayoutUtils.js b/web/client/utils/LayoutUtils.js new file mode 100644 index 0000000000..b84e82be4e --- /dev/null +++ b/web/client/utils/LayoutUtils.js @@ -0,0 +1,34 @@ +/* +* Copyright 2025, GeoSolutions Sas. +* All rights reserved. +* +* This source code is licensed under the BSD-style license found in the +* LICENSE file in the root directory of this source tree. +*/ + +import { + isString, + trim, + isNumber +} from 'lodash'; + +export const DEFAULT_PANEL_WIDTH = 420; + +export const DEFAULT_MAP_LAYOUT = {left: {sm: 300, md: 500, lg: 600}, right: { md: DEFAULT_PANEL_WIDTH }, bottom: {sm: 30}}; + +/** + * Return parsed number from layout value + * if percentage returns percentage of second argument that should be a number + * eg. 20% of map height parseLayoutValue(20%, map.size.height) + * but if value is stored as number it will return the number + * eg. parseLayoutValue(50, map.size.height) returns 50 + * @param value {number|string} number or percentage value string + * @param size {number} only in case of percentage + * @return {number} + */ +export const parseLayoutValue = (value, size = 0) => { + if (isString(value) && value.indexOf('%') !== -1) { + return parseFloat(trim(value)) * size / 100; + } + return isNumber(value) ? value : 0; +}; diff --git a/web/client/utils/MapUtils.js b/web/client/utils/MapUtils.js index 6b2bd82475..62bd67fd33 100644 --- a/web/client/utils/MapUtils.js +++ b/web/client/utils/MapUtils.js @@ -7,9 +7,6 @@ */ import { - isString, - trim, - isNumber, pick, get, find, @@ -46,8 +43,6 @@ import { } from './LayersUtils'; import assign from 'object-assign'; -export const DEFAULT_MAP_LAYOUT = {left: {sm: 300, md: 500, lg: 600}, right: {md: 548}, bottom: {sm: 30}}; - export const DEFAULT_SCREEN_DPI = 96; export const METERS_PER_UNIT = { @@ -821,23 +816,6 @@ export const getIdFromUri = (uri, regex = /data\/(\d+)/) => { return findDataDigit && findDataDigit.length && findDataDigit.length > 1 ? findDataDigit[1] : null; }; -/** - * Return parsed number from layout value - * if percentage returns percentage of second argument that should be a number - * eg. 20% of map height parseLayoutValue(20%, map.size.height) - * but if value is stored as number it will return the number - * eg. parseLayoutValue(50, map.size.height) returns 50 - * @param value {number|string} number or percentage value string - * @param size {number} only in case of percentage - * @return {number} - */ -export const parseLayoutValue = (value, size = 0) => { - if (isString(value) && value.indexOf('%') !== -1) { - return parseFloat(trim(value)) * size / 100; - } - return isNumber(value) ? value : 0; -}; - /** * Method for cleanup map object from uneseccary fields which * updated map contains and were set on map render @@ -1025,7 +1003,6 @@ export default { isSimpleGeomType, getSimpleGeomType, getIdFromUri, - parseLayoutValue, prepareMapObjectToCompare, updateObjectFieldKey, compareMapChanges, diff --git a/web/client/utils/__tests__/LayoutUtils-test.js b/web/client/utils/__tests__/LayoutUtils-test.js new file mode 100644 index 0000000000..39cce47e55 --- /dev/null +++ b/web/client/utils/__tests__/LayoutUtils-test.js @@ -0,0 +1,25 @@ +/* + * Copyright 2025, GeoSolutions Sas. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. + */ + +import expect from 'expect'; +import { + parseLayoutValue +} from '../LayoutUtils'; + +describe('LayoutUtils', () => { + it('parseLayoutValue', () => { + const percentageValue = parseLayoutValue('20%', 500); + expect(percentageValue).toBe(100); + + const numberValue = parseLayoutValue(20); + expect(numberValue).toBe(20); + + const noNumberValue = parseLayoutValue('value'); + expect(noNumberValue).toBe(0); + }); +}); diff --git a/web/client/utils/__tests__/MapUtils-test.js b/web/client/utils/__tests__/MapUtils-test.js index c647bfc759..2ed962c303 100644 --- a/web/client/utils/__tests__/MapUtils-test.js +++ b/web/client/utils/__tests__/MapUtils-test.js @@ -32,7 +32,6 @@ import { getIdFromUri, getSimpleGeomType, isSimpleGeomType, - parseLayoutValue, prepareMapObjectToCompare, updateObjectFieldKey, compareMapChanges, @@ -1806,16 +1805,7 @@ describe('Test the MapUtils', () => { expect(getSimpleGeomType(GEOMETRY_COLLECTION)).toBe(GEOMETRY_COLLECTION); }); - it('test parseLayoutValue', () => { - const percentageValue = parseLayoutValue('20%', 500); - expect(percentageValue).toBe(100); - const numberValue = parseLayoutValue(20); - expect(numberValue).toBe(20); - - const noNumberValue = parseLayoutValue('value'); - expect(noNumberValue).toBe(0); - }); it('test getSimpleGeomType', () => { expect(getSimpleGeomType("Point")).toBe("Point"); expect(getSimpleGeomType("Marker")).toBe("Point");