diff --git a/docma-config.json b/docma-config.json index b8498bf5b0..97552b51e2 100644 --- a/docma-config.json +++ b/docma-config.json @@ -145,6 +145,9 @@ "web/client/plugins/GlobeViewSwitcher.jsx", "web/client/plugins/GoFull.jsx", "web/client/plugins/Map.jsx", + "web/client/plugins/Measure.jsx", + "web/client/plugins/MeasurePanel.jsx", + "web/client/plugins/MeasureResults.jsx", "web/client/plugins/FullScreen.jsx", "web/client/plugins/Identify.jsx", "web/client/plugins/Login.jsx", diff --git a/web/client/components/mapcontrols/locate/LocateBtn.jsx b/web/client/components/mapcontrols/locate/LocateBtn.jsx index 66e077b261..c7020cb22e 100644 --- a/web/client/components/mapcontrols/locate/LocateBtn.jsx +++ b/web/client/components/mapcontrols/locate/LocateBtn.jsx @@ -17,6 +17,7 @@ let geoLocationAllowed = false; const LocateBtn = React.createClass({ propTypes: { id: React.PropTypes.string, + hide: React.PropTypes.bool, btnConfig: React.PropTypes.object, text: React.PropTypes.oneOfType([React.PropTypes.string, React.PropTypes.element]), help: React.PropTypes.oneOfType([React.PropTypes.string, React.PropTypes.element]), diff --git a/web/client/components/mapcontrols/measure/MeasureComponent.jsx b/web/client/components/mapcontrols/measure/MeasureComponent.jsx index 1a9057ef47..52e3f7b29b 100644 --- a/web/client/components/mapcontrols/measure/MeasureComponent.jsx +++ b/web/client/components/mapcontrols/measure/MeasureComponent.jsx @@ -9,11 +9,8 @@ const React = require('react'); const {Panel, ButtonGroup, Tooltip, Glyphicon, Button} = require('react-bootstrap'); const ToggleButton = require('../../buttons/ToggleButton'); -var NumberFormat = require('../../I18N/Number'); - -const lineRuleIcon = require('./img/line-ruler.png'); -const areaRuleIcon = require('./img/area-ruler.png'); -const bearingRuleIcon = require('./img/bearing-ruler.png'); +const NumberFormat = require('../../I18N/Number'); +const Message = require('../../I18N/Message'); const measureUtils = require('../../../utils/MeasureUtils'); const localeUtils = require('../../../utils/LocaleUtils'); @@ -49,12 +46,15 @@ const MeasureComponent = React.createClass({ lineMeasureEnabled: React.PropTypes.bool, areaMeasureEnabled: React.PropTypes.bool, bearingMeasureEnabled: React.PropTypes.bool, + showButtons: React.PropTypes.bool, showResults: React.PropTypes.bool, lineGlyph: React.PropTypes.string, areaGlyph: React.PropTypes.string, bearingGlyph: React.PropTypes.string, useButtonGroup: React.PropTypes.bool, withReset: React.PropTypes.bool, + showButtonsLabels: React.PropTypes.bool, + inlineGlyph: React.PropTypes.bool, formatLength: React.PropTypes.func, formatArea: React.PropTypes.func, formatBearing: React.PropTypes.func @@ -64,7 +64,6 @@ const MeasureComponent = React.createClass({ }, getDefaultProps() { return { - icon: , columnProperties: { xs: 4, sm: 4, @@ -75,12 +74,17 @@ const MeasureComponent = React.createClass({ length: {unit: 'm', label: 'm'}, area: {unit: 'sqm', label: 'm²'} }, + showButtons: true, showResults: true, useButtonGroup: false, withReset: false, lineGlyph: "1-measure-lenght", areaGlyph: "1-measure-area", bearingGlyph: "1-bearing", + showButtonsLabels: true, + lengthLabel: , + areaLabel: , + bearingLabel: , formatLength: (uom, value) => measureUtils.getFormattedLength(uom, value), formatArea: (uom, value) => measureUtils.getFormattedArea(uom, value), formatBearing: (value) => measureUtils.getFormattedBearingValue(round(value || 0, 6)) @@ -135,33 +139,41 @@ const MeasureComponent = React.createClass({ } return null; }, + renderLabel(msgId) { + if (this.props.showButtonsLabels) { + return {localeUtils.getMessageById(this.context.messages, msgId)}; + } + }, + renderText(glyph, labelId) { + if (glyph) { + return ( + + {this.renderLabel(labelId)} + ); + } + return this.renderLabel(labelId); + }, renderButtons() { let {lineToolTip, areaToolTip, bearingToolTip} = this.getToolTips(); return (
- {this.props.lineGlyph ? : } - {localeUtils.getMessageById(this.context.messages, "measureComponent.MeasureLength")} - } + text={this.renderText(this.props.inlineGlyph && this.props.lineGlyph, "measureComponent.MeasureLength")} + glyphicon={!this.props.inlineGlyph && this.props.lineGlyph} style={{"width": "100%", textAlign: "left"}} pressed={this.props.lineMeasureEnabled} onClick={this.onLineClick} tooltip={lineToolTip} /> - {this.props.areaGlyph ? : } - {localeUtils.getMessageById(this.context.messages, "measureComponent.MeasureArea")} - } + text={this.renderText(this.props.inlineGlyph && this.props.areaGlyph, "measureComponent.MeasureArea")} + glyphicon={!this.props.inlineGlyph && this.props.areaGlyph} style={{"width": "100%", textAlign: "left"}} pressed={this.props.areaMeasureEnabled} onClick={this.onAreaClick} tooltip={areaToolTip} /> - {this.props.bearingGlyph ? : } - {localeUtils.getMessageById(this.context.messages, "measureComponent.MeasureBearing")} - } + text={this.renderText(this.props.inlineGlyph && this.props.bearingGlyph, "measureComponent.MeasureBearing")} + glyphicon={!this.props.inlineGlyph && this.props.bearingGlyph} style={{"width": "100%", textAlign: "left"}} pressed={this.props.bearingMeasureEnabled} onClick={this.onBearingClick} @@ -185,7 +197,7 @@ const MeasureComponent = React.createClass({ render() { return ( - {this.props.useButtonGroup ? this.renderButtonGroup() : this.renderButtons() } + {this.props.showButtons && (this.props.useButtonGroup ? this.renderButtonGroup() : this.renderButtons()) } {this.renderPanel()} ); diff --git a/web/client/components/mapcontrols/measure/MeasureDialog.jsx b/web/client/components/mapcontrols/measure/MeasureDialog.jsx new file mode 100644 index 0000000000..97f2ab0c7a --- /dev/null +++ b/web/client/components/mapcontrols/measure/MeasureDialog.jsx @@ -0,0 +1,45 @@ +/* + * Copyright 2017, 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. + */ + +const React = require('react'); +const {Glyphicon} = require('react-bootstrap'); +const MeasureComponent = require('./MeasureComponent'); +const Message = require('../../I18N/Message'); +const Dialog = require('../../misc/Dialog'); + + +const MeasureDialog = React.createClass({ + propTypes: { + show: React.PropTypes.bool, + closeGlyph: React.PropTypes.string, + onClose: React.PropTypes.func + }, + getDefaultProps() { + return { + show: false, + closeGlyph: "1-close" + }; + }, + onClose() { + this.props.onClose(false); + }, + render() { + return this.props.show ? ( +
+   + +
+
+ +
+
) : null; + } +}); +module.exports = MeasureDialog; diff --git a/web/client/components/mapcontrols/measure/MeasureResults.jsx b/web/client/components/mapcontrols/measure/MeasureResults.jsx deleted file mode 100644 index 6d933e53ac..0000000000 --- a/web/client/components/mapcontrols/measure/MeasureResults.jsx +++ /dev/null @@ -1,136 +0,0 @@ -/** - * Copyright 2016, 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. - */ - -const React = require('react'); -const {Panel, Glyphicon} = require('react-bootstrap'); -const Draggable = require('react-draggable'); -const Dialog = require('../../misc/Dialog'); - -const Message = require('../../../plugins/locale/Message'); -const NumberFormat = require('../../I18N/Number'); -const measureUtils = require('../../../utils/MeasureUtils'); - -const {isEqual} = require('lodash'); - -const MeasureResults = React.createClass({ - propTypes: { - id: React.PropTypes.string, - name: React.PropTypes.string, - columnProperties: React.PropTypes.object, - propertiesChangeHandler: React.PropTypes.func, - lengthLabel: React.PropTypes.oneOfType([React.PropTypes.string, React.PropTypes.element]), - areaLabel: React.PropTypes.oneOfType([React.PropTypes.string, React.PropTypes.element]), - bearingLabel: React.PropTypes.oneOfType([React.PropTypes.string, React.PropTypes.element]), - uom: React.PropTypes.shape({ - length: React.PropTypes.shape({ - unit: React.PropTypes.string.isRequired, - label: React.PropTypes.string.isRequired - }), - area: React.PropTypes.shape({ - unit: React.PropTypes.string.isRequired, - label: React.PropTypes.string.isRequired - }) - }), - toggleMeasure: React.PropTypes.func, - measurement: React.PropTypes.object, - lineMeasureEnabled: React.PropTypes.bool, - areaMeasureEnabled: React.PropTypes.bool, - bearingMeasureEnabled: React.PropTypes.bool, - separatePanel: React.PropTypes.bool, - title: React.PropTypes.object, - panelStyle: React.PropTypes.object, - panelClassName: React.PropTypes.string, - style: React.PropTypes.object, - withPanelAsContainer: React.PropTypes.bool, - closeGlyph: React.PropTypes.string - }, - getDefaultProps() { - return { - columnProperties: { - xs: 4, - sm: 4, - md: 4 - }, - id: "measure-result-panel", - uom: { - length: {unit: 'm', label: 'm'}, - area: {unit: 'sqm', label: 'm²'} - }, - separatePanel: true, - title: , - panelStyle: { - minWidth: "300px", - left: "302px", - zIndex: 100, - position: "absolute", - overflow: "auto" - }, - panelClassName: "drawer-menu-panel", - withPanelAsContainer: false, - closeGlyph: "1-close" - }; - }, - shouldComponentUpdate(nextProps) { - return !isEqual(nextProps, this.props); - }, - onModalHiding() { - const newMeasureState = { - lineMeasureEnabled: false, - areaMeasureEnabled: false, - bearingMeasureEnabled: false, - geomType: null, - // reset old measurements - len: 0, - area: 0, - bearing: 0 - }; - this.props.toggleMeasure(newMeasureState); - }, - renderHeader() { - return ( - - - - - ); - }, - renderBody() { - let decimalFormat = {style: "decimal", minimumIntegerDigits: 1, maximumFractionDigits: 2, minimumFractionDigits: 2}; - return ( -
-

{this.props.lengthLabel}: {this.props.uom.length.label}

-

{this.props.areaLabel}: {this.props.uom.area.label}

-

{this.props.bearingLabel}: {measureUtils.getFormattedBearingValue(this.props.measurement.bearing)}

-
- ); - }, - render() { - if (this.props.lineMeasureEnabled || this.props.areaMeasureEnabled || this.props.bearingMeasureEnabled ) { - // compatibility with old theme - if (this.props.withPanelAsContainer) { - return ( - - - {this.renderBody()} - - - ); - } - // else use the new theme - return ( - - {this.renderHeader()} - {this.renderBody()} - - ); - } - return null; - } -}); - -module.exports = MeasureResults; diff --git a/web/client/components/mapcontrols/measure/__tests__/MeasureComponent-test.jsx b/web/client/components/mapcontrols/measure/__tests__/MeasureComponent-test.jsx index d5cc548bb9..7992f0ff0f 100644 --- a/web/client/components/mapcontrols/measure/__tests__/MeasureComponent-test.jsx +++ b/web/client/components/mapcontrols/measure/__tests__/MeasureComponent-test.jsx @@ -34,7 +34,7 @@ describe("test the MeasureComponent", () => { it('test creation of button UIs ', () => { let measurement = {}; - const mc = ReactDOM.render(, document.getElementById("container")); + const mc = ReactDOM.render(, document.getElementById("container")); expect(mc).toExist(); const domNode = ReactDOM.findDOMNode(mc); expect(domNode).toExist(); @@ -43,6 +43,17 @@ describe("test the MeasureComponent", () => { expect(domButtons.length).toBe(3); }); + it('test creation of button UIs useButtonGroup option ', () => { + let measurement = {}; + const mc = ReactDOM.render(, document.getElementById("container")); + expect(mc).toExist(); + const domNode = ReactDOM.findDOMNode(mc); + expect(domNode).toExist(); + const domButtons = domNode.getElementsByClassName('btn-block'); + expect(domButtons).toExist(); + expect(domButtons.length).toBe(1); + }); + it('test creation of measurement result panel UI ', () => { let measurement = {}; const mc = ReactDOM.render(, document.getElementById("container")); diff --git a/web/client/components/mapcontrols/measure/__tests__/MeasureDialog-test.jsx b/web/client/components/mapcontrols/measure/__tests__/MeasureDialog-test.jsx new file mode 100644 index 0000000000..5e2c69c94b --- /dev/null +++ b/web/client/components/mapcontrols/measure/__tests__/MeasureDialog-test.jsx @@ -0,0 +1,47 @@ +/** + * Copyright 2015, 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. + */ +var expect = require('expect'); + +var React = require('react'); +var ReactDOM = require('react-dom'); +const ReactTestUtils = require('react-addons-test-utils'); +var MeasureDialog = require('../MeasureDialog'); + + +describe("test the MeasureComponent", () => { + beforeEach((done) => { + document.body.innerHTML = '
'; + setTimeout(done); + }); + + afterEach((done) => { + ReactDOM.unmountComponentAtNode(document.getElementById("container")); + document.body.innerHTML = ''; + setTimeout(done); + }); + + it('test component creation', () => { + let measurement = {}; + const mc = ReactDOM.render(, document.getElementById("container")); + expect(mc).toExist(); + }); + it('test close', () => { + let measurement = {}; + const handlers = { + onClose() {} + }; + let spy = expect.spyOn(handlers, "onClose"); + const mc = ReactDOM.render(, document.getElementById("container")); + expect(mc).toExist(); + const dom = ReactDOM.findDOMNode(mc); + const closeBtn = dom.getElementsByClassName('close')[0]; + expect(closeBtn).toExist(); + ReactTestUtils.Simulate.click(closeBtn); + expect(spy.calls.length).toBe(1); + }); +}); diff --git a/web/client/localConfig.json b/web/client/localConfig.json index ad40e9e39c..7a6ca49270 100644 --- a/web/client/localConfig.json +++ b/web/client/localConfig.json @@ -137,11 +137,8 @@ "activateQueryTool": true } }, "Tutorial", "BackgroundSwitcher", { - "name": "Measure", - "cfg": { - "showResults": false - } - }, "MeasureResults", "Print", "ShapeFile", { + "name": "Measure" + }, "Print", "ShapeFile", { "name": "Settings", "cfg": { "wrap": true diff --git a/web/client/plugins/Measure.jsx b/web/client/plugins/Measure.jsx index b2dc0f7d83..e68e8d90aa 100644 --- a/web/client/plugins/Measure.jsx +++ b/web/client/plugins/Measure.jsx @@ -1,4 +1,4 @@ -/** +/* * Copyright 2016, GeoSolutions Sas. * All rights reserved. * @@ -11,65 +11,54 @@ const {Glyphicon} = require('react-bootstrap'); const Message = require('./locale/Message'); -const lineRuleIcon = require('./toolbar/assets/img/line-ruler.png'); - const assign = require('object-assign'); - +const {createSelector} = require('reselect'); const {changeMeasurement} = require('../actions/measurement'); +const {toggleControl} = require('../actions/controls'); +const {MeasureDialog} = require('./measure'); -const Measure = require('../components/mapcontrols/measure/MeasureComponent'); - -const MeasureComponent = React.createClass({ - render() { - const labels = { - lengthButtonText: , - areaButtonText: , - resetButtonText: , - lengthLabel: , - areaLabel: , - bearingLabel: - }; - return ; - } -}); - -const MeasurePlugin = connect((state) => { +const selector = (state) => { return { measurement: state.measurement || {}, lineMeasureEnabled: state.measurement && state.measurement.lineMeasureEnabled || false, areaMeasureEnabled: state.measurement && state.measurement.areaMeasureEnabled || false, bearingMeasureEnabled: state.measurement && state.measurement.bearingMeasureEnabled || false }; -}, { - toggleMeasure: changeMeasurement -}, null, {pure: false})(MeasureComponent); +}; +const toggleMeasureTool = toggleControl.bind(null, 'measure', null); +/** + * Measure plugin. Allows to show the tool to measure dinstances, areas and bearing. + * @class + * @name Measure + * @memberof plugins + * @prop {boolean} showResults shows the measure in the panel itself. + */ +const Measure = connect( + createSelector([ + selector, + (state) => state && state.controls && state.controls.measure && state.controls.measure.enabled + ], + (measure, show) => ({ + show, + ...measure + } + )), + { + toggleMeasure: changeMeasurement, + onClose: toggleMeasureTool + }, null, {pure: false})(MeasureDialog); module.exports = { - MeasurePlugin: assign(MeasurePlugin, { - Toolbar: { + MeasurePlugin: assign(Measure, { + BurgerMenu: { name: 'measurement', position: 9, - panel: true, - exclusive: true, - wrap: true, + panel: false, help: , tooltip: "measureComponent.tooltip", - icon: , - title: "measureComponent.title", - priority: 1 - }, - DrawerMenu: { - name: 'measurement', - position: 3, - glyph: "1-stilo", - icon: , - title: 'measureComponent.title', - showPanel: false, - buttonConfig: { - buttonClassName: "square-button no-border", - tooltip: "toc.measure" - }, - priority: 2 + text: , + icon: , + action: toggleMeasureTool } }), reducers: {measurement: require('../reducers/measurement')} diff --git a/web/client/plugins/MeasurePanel.jsx b/web/client/plugins/MeasurePanel.jsx new file mode 100644 index 0000000000..a004b9effd --- /dev/null +++ b/web/client/plugins/MeasurePanel.jsx @@ -0,0 +1,66 @@ +/* + * Copyright 2016, 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. + */ +const React = require('react'); +const {connect} = require('react-redux'); +const {Glyphicon} = require('react-bootstrap'); + +const Message = require('./locale/Message'); + +const assign = require('object-assign'); +const {changeMeasurement} = require('../actions/measurement'); +const {MeasureComponent} = require('./measure'); + +const selector = (state) => { + return { + measurement: state.measurement || {}, + lineMeasureEnabled: state.measurement && state.measurement.lineMeasureEnabled || false, + areaMeasureEnabled: state.measurement && state.measurement.areaMeasureEnabled || false, + bearingMeasureEnabled: state.measurement && state.measurement.bearingMeasureEnabled || false + }; +}; +/** + * MeasurePanel plugin. Shows the measure tool in the TOC. This is an old version of measure tool that will be removed soon. + * @class + * @name MeasurePanel + * @memberof plugins + * @deprecated since version 2017.03.01 + * @prop {boolean} showResults shows the measure in the panel itself. It can be disabled if you are using a plugin like MeasureResults to show the results + */ +const MeasurePanelPlugin = connect(selector, { + toggleMeasure: changeMeasurement +}, null, {pure: false})(MeasureComponent); + +module.exports = { + MeasurePanelPlugin: assign(MeasurePanelPlugin, { + Toolbar: { + name: 'measurement', + position: 9, + panel: true, + exclusive: true, + wrap: true, + help: , + tooltip: "measureComponent.tooltip", + icon: , + title: "measureComponent.title", + priority: 1 + }, + DrawerMenu: { + name: 'measurement', + position: 3, + glyph: "1-ruler", + title: 'measureComponent.title', + showPanel: false, + buttonConfig: { + buttonClassName: "square-button no-border", + tooltip: "toc.measure" + }, + priority: 2 + } + }), + reducers: {measurement: require('../reducers/measurement')} +}; diff --git a/web/client/plugins/MeasureResults.jsx b/web/client/plugins/MeasureResults.jsx index 3de34bed9d..b189bd7943 100644 --- a/web/client/plugins/MeasureResults.jsx +++ b/web/client/plugins/MeasureResults.jsx @@ -1,4 +1,4 @@ -/** +/* * Copyright 2016, GeoSolutions Sas. * All rights reserved. * @@ -12,19 +12,45 @@ const Message = require('./locale/Message'); const {changeMeasurement} = require('../actions/measurement'); -const MeasureRes = require('../components/mapcontrols/measure/MeasureResults'); +const {MeasureDialog} = require('./measure'); const MeasureComponent = React.createClass({ + propTypes: { + lineMeasureEnabled: React.PropTypes.bool, + areaMeasureEnabled: React.PropTypes.bool, + bearingMeasureEnabled: React.PropTypes.bool, + toggleMeasure: React.PropTypes.func + }, + onModalHiding() { + const newMeasureState = { + lineMeasureEnabled: false, + areaMeasureEnabled: false, + bearingMeasureEnabled: false, + geomType: null, + // reset old measurements + len: 0, + area: 0, + bearing: 0 + }; + this.props.toggleMeasure(newMeasureState); + }, render() { const labels = { lengthLabel: , areaLabel: , bearingLabel: }; - return ; + return ; } }); - +/** + * MeasureResults plugin. Shows the measure results. This is an old version of measure tool that will be removed soon. + * It should be used with the MeasurePanel plugin + * @class + * @name MeasureResults + * @memberof plugins + * @deprecated since version 2017.03.01 + */ const MeasureResultsPlugin = connect((state) => { return { measurement: state.measurement || {}, diff --git a/web/client/plugins/measure/index.js b/web/client/plugins/measure/index.js new file mode 100644 index 0000000000..48cb850bc8 --- /dev/null +++ b/web/client/plugins/measure/index.js @@ -0,0 +1,15 @@ +/* + * Copyright 2017, 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. + */ + +const MeasureComponent = require('../../components/mapcontrols/measure/MeasureComponent'); +const MeasureDialog = require('../../components/mapcontrols/measure/MeasureDialog'); + +module.exports = { + MeasureComponent, + MeasureDialog +}; diff --git a/web/client/product/plugins.js b/web/client/product/plugins.js index b7e1dd1372..926749e6a0 100644 --- a/web/client/product/plugins.js +++ b/web/client/product/plugins.js @@ -14,7 +14,6 @@ module.exports = { TOCPlugin: require('../plugins/TOC'), BackgroundSwitcherPlugin: require('../plugins/BackgroundSwitcher'), MeasurePlugin: require('../plugins/Measure'), - MeasureResultsPlugin: require('../plugins/MeasureResults'), MapPlugin: require('../plugins/Map'), ToolbarPlugin: require('../plugins/Toolbar'), DrawerMenuPlugin: require('../plugins/DrawerMenu'),