From 0270c010277327f38bfd50ea899cb970d5a00ea2 Mon Sep 17 00:00:00 2001 From: Sovas Tiwari Date: Wed, 10 Dec 2025 14:19:57 +0000 Subject: [PATCH] Update the layout of the map viewer --- .../components/layout/MapViewerLayout.jsx | 37 ++++ web/client/configs/localConfig.json | 8 +- web/client/containers/MapViewer.jsx | 6 +- .../plugins/featuregrid/FeatureEditor.jsx | 159 ++++++++---------- .../plugins/featuregrid/hoc/withResize.jsx | 115 +++++++++++++ web/client/product/assets/css/viewer.css | 11 ++ web/client/reducers/featuregrid.js | 2 +- 7 files changed, 242 insertions(+), 96 deletions(-) create mode 100644 web/client/components/layout/MapViewerLayout.jsx create mode 100644 web/client/plugins/featuregrid/hoc/withResize.jsx diff --git a/web/client/components/layout/MapViewerLayout.jsx b/web/client/components/layout/MapViewerLayout.jsx new file mode 100644 index 0000000000..f9bfc428b1 --- /dev/null +++ b/web/client/components/layout/MapViewerLayout.jsx @@ -0,0 +1,37 @@ +import React from "react"; +import FlexBox, { FlexFill } from "./FlexBox"; + +export default ({ + id, + header, + footer, + background, + leftColumn, + rightColumn, + columns, + className, + top, + bottom, + children, + bodyClassName +}) => { + return ( + + {header} + +
{background}
+
{top}
+ +
{leftColumn}
+ + {children} + +
{rightColumn}
+
{columns}
+
+
{bottom}
+
+ {footer} +
+ ); +}; diff --git a/web/client/configs/localConfig.json b/web/client/configs/localConfig.json index 556e4dc4b6..6f391b7ba5 100644 --- a/web/client/configs/localConfig.json +++ b/web/client/configs/localConfig.json @@ -409,6 +409,7 @@ { "name": "Map", "cfg": { + "containerPosition": "background", "mapOptions": { "openlayers": { "interactions": { @@ -475,7 +476,12 @@ } }, "Home", - "FeatureEditor", + { + "name": "FeatureEditor", + "cfg": { + "containerPosition": "bottom" + } + }, "LayerDownload", { "name": "QueryPanel", diff --git a/web/client/containers/MapViewer.jsx b/web/client/containers/MapViewer.jsx index fe8e816f93..fda38f9902 100644 --- a/web/client/containers/MapViewer.jsx +++ b/web/client/containers/MapViewer.jsx @@ -16,9 +16,9 @@ const urlQuery = url.parse(window.location.href, true).query; import ConfigUtils from '../utils/ConfigUtils'; import { getMonitoredState } from '../utils/PluginsUtils'; import ModulePluginsContainer from "../product/pages/containers/ModulePluginsContainer"; -import { createShallowSelectorCreator } from '../utils/ReselectUtils'; -import BorderLayout from '../components/layout/BorderLayout'; +import MapViewerLayout from '../components/layout/MapViewerLayout'; +import { createShallowSelectorCreator } from '../utils/ReselectUtils'; const PluginsContainer = connect( createShallowSelectorCreator(isEqual)( state => state.plugins, @@ -66,7 +66,7 @@ class MapViewer extends React.Component { params={this.props.params} loaderComponent={this.props.loaderComponent} onLoaded={this.props.onLoaded} - component={this.props.component || BorderLayout} + component={this.props.component || MapViewerLayout} />); } } diff --git a/web/client/plugins/featuregrid/FeatureEditor.jsx b/web/client/plugins/featuregrid/FeatureEditor.jsx index 835d794049..72415d5181 100644 --- a/web/client/plugins/featuregrid/FeatureEditor.jsx +++ b/web/client/plugins/featuregrid/FeatureEditor.jsx @@ -7,37 +7,27 @@ */ import React, { useMemo } from 'react'; import {connect} from 'react-redux'; -import {createSelector, createStructuredSelector} from 'reselect'; +import {createStructuredSelector} from 'reselect'; import {bindActionCreators} from 'redux'; import { get, pick, isEqual } from 'lodash'; import {compose, lifecycle, defaultProps } from 'recompose'; -import ReactDock from 'react-dock'; import ContainerDimensions from 'react-container-dimensions'; import Grid from '../../components/data/featuregrid/FeatureGrid'; import BorderLayout from '../../components/layout/BorderLayout'; import { toChangesMap} from '../../utils/FeatureGridUtils'; import { sizeChange, setUp, setSyncTool } from '../../actions/featuregrid'; -import {mapLayoutValuesSelector} from '../../selectors/maplayout'; import {paginationInfo, describeSelector, attributesJSONSchemaSelector, wfsURLSelector, typeNameSelector, isSyncWmsActive} from '../../selectors/query'; -import {modeSelector, changesSelector, newFeaturesSelector, hasChangesSelector, selectedLayerFieldsSelector, selectedFeaturesSelector, getDockSize} from '../../selectors/featuregrid'; +import {modeSelector, changesSelector, newFeaturesSelector, hasChangesSelector, selectedLayerFieldsSelector, selectedFeaturesSelector} from '../../selectors/featuregrid'; import {getPanels, getHeader, getFooter, getDialogs, getEmptyRowsView, getFilterRenderers} from './panels/index'; import {gridTools, gridEvents, pageEvents, toolbarEvents} from './index'; import useFeatureValidation from './hooks/useFeatureValidation'; +import withResize from './hoc/withResize'; const EMPTY_ARR = []; const EMPTY_OBJ = {}; -const Dock = connect(createSelector( - getDockSize, - state => mapLayoutValuesSelector(state, {transform: true}), - (size, dockStyle) => ({ - size, - dockStyle - }) -) -)(ReactDock); /** * @name FeatureEditor * @memberof plugins @@ -171,24 +161,13 @@ const Dock = connect(createSelector( * ``` * */ -const FeatureDock = (props = { +const Editor = (props = { tools: EMPTY_OBJ, dialogs: EMPTY_OBJ, select: EMPTY_ARR }) => { const virtualScroll = props.virtualScroll ?? true; const maxZoom = props?.pluginCfg?.maxZoom; - const dockProps = { - dimMode: "none", - defaultSize: 0.35, - fluid: true, - isVisible: props.open, - maxDockSize: 0.7, - minDockSize: 0.1, - position: "bottom", - setDockSize: () => {}, - zIndex: 1060 - }; const items = props?.items ?? []; const toolbarItems = items.filter(({target}) => target === 'toolbar'); const filterRenderers = useMemo(() => { @@ -208,72 +187,70 @@ const FeatureDock = (props = { }); return ( -
- { props.onSizeChange(size, dockProps); }}> - {props.open && - ( - { ({ height }) => - // added height to solve resize issue in firefox, edge and ie - - {getDialogs(props.tools)} - - } - - ) - } - -
); + + { ({ height }) => + // added height to solve resize issue in firefox, edge and ie + + {getDialogs(props.tools)} + + } + + ); }; + +// Wrap Editor with resize HOC +const ResizableEditor = withResize(Editor); + export const selector = createStructuredSelector({ open: state => get(state, "featuregrid.open"), customEditorsOptions: state => get(state, "featuregrid.customEditorsOptions"), @@ -344,6 +321,6 @@ const EditorPlugin = compose( onSizeChange: (...params) => dispatch(sizeChange(...params)) }) ) -)(FeatureDock); +)(ResizableEditor); export default EditorPlugin; diff --git a/web/client/plugins/featuregrid/hoc/withResize.jsx b/web/client/plugins/featuregrid/hoc/withResize.jsx new file mode 100644 index 0000000000..18a070cb69 --- /dev/null +++ b/web/client/plugins/featuregrid/hoc/withResize.jsx @@ -0,0 +1,115 @@ +import React, { useEffect, useRef, useState } from "react"; + +/** + * HOC that wraps a component in a resizable container. + * @param {React.Component} Component - The component to wrap + * @returns {React.Component} A component wrapped in a resizable div + * + * Props: + * @prop {boolean} resizeContainer - If true, enables resize functionality (default: true) + * @prop {number} defaultHeight - Initial height in pixels (default: 300) + * @prop {number} minHeight - Minimum height in pixels (default: 75) + * @prop {number} maxHeight - Maximum height in pixels (default: 70% of the window inner height) + */ +const withResize = (Component) => { + return (props) => { + const { resizeContainer = true, defaultHeight = 300, minHeight = 75, maxHeight = '70%' } = props; + const [height, setHeight] = useState(defaultHeight); + const [isResizing, setIsResizing] = useState(false); + const containerRef = useRef(null); + const startYRef = useRef(0); + const startHeightRef = useRef(0); + + useEffect(() => { + const maxAllowedHeight = typeof maxHeight === 'number' + ? maxHeight + : (window.innerHeight * (maxHeight.replace('%', '')) / 100); + + const handleMouseMove = (e) => { + if (!isResizing) return; + + const deltaY = e.clientY - startYRef.current; + const newHeight = startHeightRef.current - deltaY; + const clampedHeight = Math.max(minHeight, Math.min(newHeight, maxAllowedHeight)); + setHeight(clampedHeight); + }; + + const handleMouseUp = () => { + setIsResizing(false); + }; + + if (isResizing) { + document.addEventListener('mousemove', handleMouseMove); + document.addEventListener('mouseup', handleMouseUp); + document.body.style.cursor = 'row-resize'; + document.body.style.userSelect = 'none'; + } + + return () => { + document.removeEventListener('mousemove', handleMouseMove); + document.removeEventListener('mouseup', handleMouseUp); + document.body.style.cursor = ''; + document.body.style.userSelect = ''; + }; + }, [isResizing, minHeight, maxHeight]); + + const handleMouseDown = (e) => { + e.preventDefault(); + setIsResizing(true); + startYRef.current = e.clientY; + startHeightRef.current = height; + }; + + // If resizeContainer is false, just render in a normal div + if (!resizeContainer) { + return ( +
+ +
+ ); + } + + // Render with resize functionality + return ( +
+
{ + e.currentTarget.style.borderTopColor = '#ccc'; + }} + onMouseLeave={(e) => { + if (!isResizing) { + e.currentTarget.style.borderTopColor = 'transparent'; + } + }} + /> +
+ +
+
+ ); + }; +}; + +export default withResize; diff --git a/web/client/product/assets/css/viewer.css b/web/client/product/assets/css/viewer.css index fb66444ef1..688935ae24 100644 --- a/web/client/product/assets/css/viewer.css +++ b/web/client/product/assets/css/viewer.css @@ -154,3 +154,14 @@ html, body, #container, .fill { max-height: 250px; overflow-y: auto; } + +/* Disable pointer events on the main content container but enable them for all its children */ +.ms2-layout-main-content { + pointer-events: none; +} +.ms2-layout-main-content .ms2-layout-content > *, +.ms2-layout-main-content .ms2-layout-left-column > *, +.ms2-layout-main-content .ms2-layout-right-column > *, +.ms2-layout-main-content .ms2-layout-columns > * { + pointer-events: auto; +} diff --git a/web/client/reducers/featuregrid.js b/web/client/reducers/featuregrid.js index 13a2013885..26e04a987b 100644 --- a/web/client/reducers/featuregrid.js +++ b/web/client/reducers/featuregrid.js @@ -77,7 +77,7 @@ const emptyResultsState = { drawing: false, newFeatures: [], features: [], - dockSize: 0.35, + dockSize: 0, customEditorsOptions: { "rules": [] },