diff --git a/assets/index.json b/assets/index.json index fd1b6f5..4f4d86f 100644 --- a/assets/index.json +++ b/assets/index.json @@ -14,7 +14,7 @@ } }, "dependencies": [ - "BurgerMenu" + "SidebarMenu" ] } ] diff --git a/js/extension/actions/cadastrapp.js b/js/extension/actions/cadastrapp.js index e2d5f18..39f1211 100644 --- a/js/extension/actions/cadastrapp.js +++ b/js/extension/actions/cadastrapp.js @@ -109,10 +109,12 @@ export const tearDown = () => ({ /** * Toggles map selection in one of the modes available * @param {string} selectionType type of selection (constants.SELECTION_TYPES) + * @param {boolean} resetDraw should it reset draw status or not */ -export const toggleSelectionTool = (selectionType) => ({ +export const toggleSelectionTool = (selectionType, resetDraw = true) => ({ type: TOGGLE_SELECTION, - selectionType + selectionType, + resetDraw }); /** diff --git a/js/extension/cadastrapp.css b/js/extension/cadastrapp.css index 939484d..97ea19d 100644 --- a/js/extension/cadastrapp.css +++ b/js/extension/cadastrapp.css @@ -10,8 +10,8 @@ overflow: hidden; top: 0; right: 0; - height: calc(100% - 30px); - width: 660px; + height: 100%; + width: 100%; z-index: 1000; } .cadastrapp .top { diff --git a/js/extension/components/misc/panels/DockContainer.jsx b/js/extension/components/misc/panels/DockContainer.jsx new file mode 100644 index 0000000..bc14197 --- /dev/null +++ b/js/extension/components/misc/panels/DockContainer.jsx @@ -0,0 +1,33 @@ +import React from 'react'; +import classnames from "classnames"; + +/** + * Wrapper for DockablePanel with main intension to support applying of custom styling to make dock panels have proper + * offset depending on the sidebars presence on the page + * @memberof components.misc.panels + * @name DockContainer + * @param id {string} - id applied to the container + * @param children {JSX.Element} + * @param dockStyle {object} - style object obtained from mapLayoutValuesSelector and used to calculate offsets + * @param className {string} - class name + * @param style - style object to apply to the container. Can be used to overwrite styles applied by dockStyle calculations + * @returns {JSX.Element} + * @constructor + */ +const DockContainer = ({ id, children, dockStyle, className, style = {}}) => { + const persistentStyle = { + width: `calc(100% - ${(dockStyle?.right ?? 0) + (dockStyle?.left ?? 0)}px)`, + transform: `translateX(-${(dockStyle?.right ?? 0)}px)`, + pointerEvents: 'none' + }; + return ( +
+ {children} +
+ ); +}; + +export default DockContainer; diff --git a/js/extension/epics/mapSelection.js b/js/extension/epics/mapSelection.js index e5b43bd..aace814 100644 --- a/js/extension/epics/mapSelection.js +++ b/js/extension/epics/mapSelection.js @@ -34,7 +34,7 @@ import { } from '../selectors/cadastrapp'; import { getLayerJSONFeature } from '@mapstore/observables/wfs'; import { wrapStartStop } from '@mapstore/observables/epics'; -import { MOUSE_MOVE, MOUSE_OUT, registerEventListener, unRegisterEventListener } from '@mapstore/actions/map'; +import {MOUSE_MOVE, MOUSE_OUT, unRegisterEventListener} from '@mapstore/actions/map'; import { addPopup, cleanPopups } from '@mapstore/actions/mapPopups'; import { mapSelector } from '@mapstore/selectors/map'; @@ -46,11 +46,18 @@ const CLEAN_ACTION = changeDrawingStatus("clean"); const DEACTIVATE_ACTIONS = [ CLEAN_ACTION, changeDrawingStatus("stop"), - registerEventListener(MOUSE_EVENT, CONTROL_NAME), + unRegisterEventListener(MOUSE_EVENT, CONTROL_NAME), loading(0, "plotSelection") // reset loading if stopped due to close ]; +export const ON_DRAW_DEACTIVATE_ACTIONS = [ + unRegisterEventListener(MOUSE_EVENT, CONTROL_NAME), + loading(0, "plotSelection") // reset loading if stopped due to close +]; + const deactivate = () => Rx.Observable.from(DEACTIVATE_ACTIONS); +export const deactivateOnAnotherDraw = () => Rx.Observable.from(ON_DRAW_DEACTIVATE_ACTIONS); + /** * Extract the drawMethod for DrawSupport from the method * @param {string} selection the current tool selected @@ -123,7 +130,7 @@ const getGeometry = point => { * Handle map selection tools and events */ export const cadastrappMapSelection = (action$, {getState = () => {}}) => - action$.ofType(TOGGLE_SELECTION).switchMap(({selectionType}) => { + action$.ofType(TOGGLE_SELECTION).switchMap(({selectionType, resetDraw}) => { if (selectionType) { const startDrawingAction = changeDrawingStatus('start', drawMethod(selectionType), 'cadastrapp', [], { stopAfterDrawing: true }); return action$.ofType(END_DRAWING).flatMap( @@ -178,7 +185,7 @@ export const cadastrappMapSelection = (action$, {getState = () => {}}) => .concat(deactivate()); // on close, deactivate any draw session remaining } // if the selection type is not present, it means has been reset, so deactivate any drawing tool - return deactivate(); + return !resetDraw ? deactivateOnAnotherDraw() : deactivate(); }); /** * Retrieves the geometry of the ufFeature, when not present, and shows the LandedPropertyInformation with this data. diff --git a/js/extension/epics/setup.js b/js/extension/epics/setup.js index 2442eeb..d43a298 100644 --- a/js/extension/epics/setup.js +++ b/js/extension/epics/setup.js @@ -4,7 +4,7 @@ import { wrapStartStop } from '@mapstore/observables/epics'; import { error } from '@mapstore/actions/notifications'; import { updateAdditionalLayer, removeAdditionalLayer } from '@mapstore/actions/additionallayers'; import { hideMapinfoMarker, toggleMapInfoState } from '@mapstore/actions/mapInfo'; -import { UPDATE_MAP_LAYOUT, updateMapLayout } from '../../../MapStore2/web/client/actions/maplayout'; +import { UPDATE_MAP_LAYOUT, updateMapLayout } from '@mapstore/actions/maplayout'; import { registerEventListener, unRegisterEventListener } from '@mapstore/actions/map'; import { cleanPopups } from '@mapstore/actions/mapPopups'; @@ -18,8 +18,7 @@ import { } from '../constants'; import { getConfiguration } from '../api'; -import get from 'lodash/get'; -import { configurationSelector } from '../selectors/cadastrapp'; +import {findIndex, keys, get} from "lodash"; import { SETUP, @@ -27,12 +26,24 @@ import { setConfiguration, setupCompleted, loading, - toggleSelectionTool + toggleSelectionTool, TOGGLE_SELECTION } from '../actions/cadastrapp'; -import { SET_CONTROL_PROPERTIES, setControlProperty } from '@mapstore/actions/controls'; +import { + SET_CONTROL_PROPERTIES, + SET_CONTROL_PROPERTY, + TOGGLE_CONTROL, + setControlProperty +} from '@mapstore/actions/controls'; +import {closeFeatureGrid, OPEN_FEATURE_GRID} from "@mapstore/actions/featuregrid"; +import {resetCoordEditor, START_DRAWING} from "@mapstore/actions/annotations"; +import {configurationSelector, currentSelectionToolSelector} from '../selectors/cadastrapp'; +import {isFeatureGridOpen} from "@mapstore/selectors/featuregrid"; +import {coordinateEditorEnabledSelector} from "@mapstore/selectors/annotations"; +import {CHANGE_DRAWING_STATUS} from "@mapstore/actions/draw"; // size o -const OFFSET = 660; // size of cadastrapp. Maybe parametrize. Now in css + this constant +const OFFSET = 550; // size of cadastrapp. Maybe parametrize. Now in css + this constant +const shutdownList = ['metadataexplorer', 'measure', 'details', 'mapcatalog', 'maptemplates', 'userExtensions', 'FeatureEditor']; /** * utility function to check if the cadastrapp panel is open @@ -54,7 +65,8 @@ export const cadastrappSetup = (action$, store) => let initStream$ = Rx.Observable.defer(() => getConfiguration()) .switchMap(data => { return Rx.Observable.of(setConfiguration(data)); - }); + }) + .startWith(...(isFeatureGridOpen ? [closeFeatureGrid()] : [])); const mapInfoEnabled = get(store.getState(), "mapInfo.enabled"); return initStream$.concat( Rx.Observable.defer(() => { @@ -123,34 +135,79 @@ export const cadastrappMapLayout = (action$, store) => }) .map(({layout}) => { const action = updateMapLayout({ - layout, - right: OFFSET, + ...layout, + right: OFFSET + (layout?.right ?? 0), boundingMapRect: { ...(layout.boundingMapRect || {}), - right: OFFSET - } + right: OFFSET + (layout?.boundingSidebarRect?.right ?? 0) + }, + rightPanel: true }); return { ...action, source: 'cadastrapp' }; // add an argument to avoid infinite loop. }); + +export const cadastrappCloseAnnotationsOnToolToggledOn = (action$, store) => + action$.ofType(TOGGLE_SELECTION) + .filter(({ selectionType }) => !!selectionType && coordinateEditorEnabledSelector(store.getState()) + ) + .map(() => { + return resetCoordEditor(); + }); + /** - * Auto-closes cadastrapp when catalog is open + * Auto-closes cadastrapp when one of the shutdown-trigger tools is open or when Feature editor is open */ export const cadastrappAutoClose = (action$, store) => - action$.ofType(SET_CONTROL_PROPERTIES) + action$.ofType(SET_CONTROL_PROPERTIES, SET_CONTROL_PROPERTY, TOGGLE_CONTROL, OPEN_FEATURE_GRID) .filter(() => isCadastrappOpen(store)) - .filter(({ control, properties }) => control === "metadataexplorer" && properties?.enabled) // open the catalog from TOC - .map( () => setControlProperty(CONTROL_NAME, "enabled", false)); + .filter(({control, property, properties = [], type}) => { + const state = store.getState(); + const controlState = state.controls[control]?.enabled; + switch (type) { + case OPEN_FEATURE_GRID: + return true; + case SET_CONTROL_PROPERTY: + case TOGGLE_CONTROL: + return (property === 'enabled' || !property) && controlState && shutdownList.includes(control); + default: + return findIndex(keys(properties), prop => prop === 'enabled') > -1 && controlState && shutdownList.includes(control); + } + }) + .map( () => { + return setControlProperty(CONTROL_NAME, "enabled", false); + }); + +export const toggleCadastrapToolOnAnnotationsDrawing = (action$, store) => + action$.ofType(START_DRAWING, CHANGE_DRAWING_STATUS) + .filter(({type, status, owner}) => { + const currentSelectionTool = currentSelectionToolSelector(store.getState()); + switch (type) { + case CHANGE_DRAWING_STATUS: + return !!currentSelectionTool && status === 'drawOrEdit' && owner === 'annotations'; + case START_DRAWING: + default: + return !!currentSelectionTool; + } + }) + .switchMap( () => { + let actions = [ + toggleSelectionTool(null, false) + ]; + return Rx.Observable.from(actions); + }); /** * Intercept cadastrapp close event. * - Removes the cadastre layer from the map */ export const cadastrappTearDown = (action$, {getState = ()=>{}}) => - action$.ofType(TEAR_DOWN).switchMap(() => - Rx.Observable.from([ - toggleSelectionTool(), + action$.ofType(TEAR_DOWN).switchMap(() => { + const cadastrappIsDrawOwner = get(getState(), 'draw.drawOwner', false) === 'cadastrapp'; + return Rx.Observable.from([ + toggleSelectionTool(null, cadastrappIsDrawOwner), removeAdditionalLayer({id: CADASTRAPP_RASTER_LAYER_ID, owner: CADASTRAPP_OWNER}), removeAdditionalLayer({id: CADASTRAPP_VECTOR_LAYER_ID, owner: CADASTRAPP_OWNER}), cleanPopups(), unRegisterEventListener(MOUSE_EVENT, CONTROL_NAME) // Reset map's mouse event trigger - ]).concat([...(!get(getState(), "mapInfo.enabled") ? [toggleMapInfoState()] : [])])); + ]).concat([...(!get(getState(), "mapInfo.enabled") ? [toggleMapInfoState()] : [])]); + }); diff --git a/js/extension/plugins/Extension.jsx b/js/extension/plugins/Extension.jsx index ea2524d..05beae2 100644 --- a/js/extension/plugins/Extension.jsx +++ b/js/extension/plugins/Extension.jsx @@ -1,6 +1,6 @@ import React from "react"; import { connect } from "react-redux"; -import { setControlProperty, toggleControl } from "@mapstore/actions/controls"; +import { toggleControl } from "@mapstore/actions/controls"; import {Glyphicon} from 'react-bootstrap'; import Message from "@mapstore/components/I18N/Message"; @@ -13,6 +13,7 @@ import { CONTROL_NAME } from '../constants'; import {setUp, tearDown} from '../actions/cadastrapp'; import cadastrapp from '../reducers/cadastrapp'; import * as epics from '../epics/cadastrapp'; +import {mapLayoutValuesSelector} from "@js/extension/selectors/maplayout"; const compose = (...functions) => args => functions.reduceRight((arg, fn) => fn(arg), args); @@ -20,6 +21,8 @@ const compose = (...functions) => args => functions.reduceRight((arg, fn) => fn( const Cadastrapp = compose( connect((state) => ({ enabled: state.controls && state.controls[CONTROL_NAME] && state.controls[CONTROL_NAME].enabled || false, + dockStyle: mapLayoutValuesSelector(state, { right: true, height: true}, true), + dockWidth: 550, withButton: false }), { open: () => toggleControl(CONTROL_NAME, "enabled", true), @@ -48,7 +51,18 @@ export default { icon: , doNotHide: true, action: toggleControl.bind(null, CONTROL_NAME, null), - priority: 1 + priority: 2 + }, + SidebarMenu: { + name: "cadastrapp", + icon: , + tooltip: "cadastrapp.title", + text: , + doNotHide: true, + action: toggleControl.bind(null, CONTROL_NAME, null), + toggle: true, + priority: 1, + position: 1000 } } }; diff --git a/js/extension/plugins/cadastrapp/Main.jsx b/js/extension/plugins/cadastrapp/Main.jsx index 39f3ca2..8dcdb55 100644 --- a/js/extension/plugins/cadastrapp/Main.jsx +++ b/js/extension/plugins/cadastrapp/Main.jsx @@ -8,6 +8,9 @@ import Header from './Header'; import { CONTROL_NAME } from '../../constants'; import LandedProperty from './LandedProperty'; import {configurationSelector} from "@js/extension/selectors/cadastrapp"; +import DockPanel from "@mapstore/components/misc/panels/DockPanel"; +import ContainerDimensions from "react-container-dimensions"; +import DockContainer from "@js/extension/components/misc/panels/DockContainer"; /** * Main Container of Cadastrapp. @@ -16,14 +19,31 @@ import {configurationSelector} from "@js/extension/selectors/cadastrapp"; export default connect(state => ({ enabled: state.controls && state.controls[CONTROL_NAME] && state.controls[CONTROL_NAME].enabled || false, configuration: configurationSelector(state) -}))(function Main({ enabled, ...props }) { +}))(function Main({ enabled, dockStyle, dockWidth, ...props }) { if (!enabled) { return null; } - return (
-
- - - -
); + return ( + + + {({ width }) => ( 1 ? width : dockWidth} + position="right" + bsStyle="primary" + style={dockStyle}> +
+
+ + + +
+
)} +
+
+ ); }); diff --git a/js/extension/selectors/maplayout.js b/js/extension/selectors/maplayout.js new file mode 100644 index 0000000..2cf3867 --- /dev/null +++ b/js/extension/selectors/maplayout.js @@ -0,0 +1,29 @@ +import {mapLayoutSelector} from "@mapstore/selectors/maplayout"; +import {memoize} from "lodash"; + +export const boundingSidebarRectSelector = (state) => state.maplayout && state.maplayout.boundingSidebarRect || {}; + +/** + * Retrieve only specific attribute from map layout + * @function + * @memberof selectors.mapLayout + * @param {object} state the state + * @param {object} attributes attributes to retrieve, bool {left: true} + * @param {boolean} isDock flag to use dock paddings instead of toolbar paddings + * @return {object} selected attributes of layout of the map + */ +export const mapLayoutValuesSelector = memoize((state, attributes = {}, isDock = false) => { + const layout = mapLayoutSelector(state); + const boundingSidebarRect = boundingSidebarRectSelector(state); + return layout && Object.keys(layout).filter(key => + attributes[key]).reduce((a, key) => { + if (isDock) { + return ({...a, [key]: (boundingSidebarRect[key] ?? layout[key])}); + } + return ({...a, [key]: layout[key]}); + }, + {}) || {}; +}, (state, attributes, isDock) => + JSON.stringify(mapLayoutSelector(state)) + + JSON.stringify(boundingSidebarRectSelector(state)) + + JSON.stringify(attributes) + (isDock ? '_isDock' : ''));