From 03cc14f6d8297720d2efd6a2fda3a2c99f70dc22 Mon Sep 17 00:00:00 2001 From: Lorenzo Pini Date: Mon, 14 Nov 2016 15:39:45 +0100 Subject: [PATCH 1/8] import query panel in main product --- package.json | 1 + web/client/actions/query.js | 135 ++ web/client/components/TOC/DefaultLayer.jsx | 14 + .../TOC/fragments/css/settingsModal.css | 2 +- .../data/featuregrid/DockedFeatureGrid.jsx | 383 +++++ web/client/localConfig.json | 1315 ++++++++--------- web/client/plugins/DrawerMenu.jsx | 3 +- web/client/plugins/FeatureGrid.jsx | 10 + web/client/plugins/QueryPanel.jsx | 375 +++++ web/client/plugins/TOC.jsx | 272 +++- web/client/plugins/drawer/drawer.css | 10 - web/client/product/plugins.js | 4 +- web/client/reducers/query.js | 117 ++ web/client/utils/FilterUtils.jsx | 22 + 14 files changed, 1983 insertions(+), 680 deletions(-) create mode 100644 web/client/actions/query.js create mode 100644 web/client/components/data/featuregrid/DockedFeatureGrid.jsx create mode 100644 web/client/plugins/FeatureGrid.jsx create mode 100644 web/client/plugins/QueryPanel.jsx create mode 100644 web/client/reducers/query.js diff --git a/package.json b/package.json index b87df76881..e55dbe6bab 100644 --- a/package.json +++ b/package.json @@ -108,6 +108,7 @@ "react-color": "2.4.0", "react-confirm-button": "0.0.2", "react-copy-to-clipboard": "4.1.0", + "react-dock": "0.2.3", "react-dom": "0.14.8", "react-draggable": "1.3.4", "react-dnd": "2.1.3", diff --git a/web/client/actions/query.js b/web/client/actions/query.js new file mode 100644 index 0000000000..a4ae56a0e7 --- /dev/null +++ b/web/client/actions/query.js @@ -0,0 +1,135 @@ +/** + * 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 FEATURE_TYPE_LOADED = 'FEATURE_TYPE_LOADED'; +const FEATURE_LOADED = 'FEATURE_LOADED'; +const FEATURE_TYPE_ERROR = 'FEATURE_TYPE_ERROR'; +const FEATURE_ERROR = 'FEATURE_ERROR'; +const QUERY_RESULT = 'QUERY_RESULT'; +const QUERY_ERROR = 'QUERY_ERROR'; +const RESET_QUERY = 'RESET_QUERY'; + +const axios = require('../libs/ajax'); + +function featureTypeLoaded(typeName, featureType) { + return { + type: FEATURE_TYPE_LOADED, + typeName, + featureType + }; +} + +function featureTypeError(typeName, error) { + return { + type: FEATURE_TYPE_ERROR, + typeName, + error + }; +} + +function featureLoaded(typeName, feature) { + return { + type: FEATURE_LOADED, + typeName, + feature + }; +} + +function featureError(typeName, error) { + return { + type: FEATURE_ERROR, + typeName, + error + }; +} + +function querySearchResponse(result) { + return { + type: QUERY_RESULT, + result + }; +} + +function queryError(error) { + return { + type: QUERY_ERROR, + error + }; +} + +function describeFeatureType(baseUrl, typeName) { + return (dispatch) => { + return axios.get(baseUrl + '?service=WFS&version=1.1.0&request=DescribeFeatureType&typeName=' + typeName + '&outputFormat=application/json').then((response) => { + if (typeof response.data === 'object') { + dispatch(featureTypeLoaded(typeName, response.data)); + } else { + try { + JSON.parse(response.data); + } catch(e) { + dispatch(featureTypeError(typeName, 'Error from WFS: ' + e.message)); + } + + } + + }).catch((e) => { + dispatch(featureTypeError(typeName, e)); + }); + }; +} + +function loadFeature(baseUrl, typeName) { + return (dispatch) => { + return axios.get(baseUrl + '?service=WFS&version=1.1.0&request=GetFeature&typeName=' + typeName + '&outputFormat=application/json').then((response) => { + if (typeof response.data === 'object') { + dispatch(featureLoaded(typeName, response.data)); + } else { + try { + JSON.parse(response.data); + } catch(e) { + dispatch(featureError(typeName, 'Error from WFS: ' + e.message)); + } + + } + + }).catch((e) => { + dispatch(featureError(typeName, e)); + }); + }; +} + +function query(seachURL, data) { + return (dispatch) => { + return axios.post(seachURL + '&outputFormat=json', data, { + timeout: 60000, + headers: {'Accept': 'application/json', 'Content-Type': 'application/json'} + }).then((response) => { + dispatch(querySearchResponse(response.data)); + }).catch((e) => { + dispatch(queryError(e)); + }); + }; +} + +function resetQuery() { + return { + type: RESET_QUERY + }; +} + +module.exports = { + FEATURE_TYPE_LOADED, + FEATURE_LOADED, + FEATURE_TYPE_ERROR, + FEATURE_ERROR, + QUERY_RESULT, + QUERY_ERROR, + RESET_QUERY, + describeFeatureType, + loadFeature, + query, + resetQuery +}; diff --git a/web/client/components/TOC/DefaultLayer.jsx b/web/client/components/TOC/DefaultLayer.jsx index 25767f74af..a13bc837ad 100644 --- a/web/client/components/TOC/DefaultLayer.jsx +++ b/web/client/components/TOC/DefaultLayer.jsx @@ -25,6 +25,7 @@ var DefaultLayer = React.createClass({ propertiesChangeHandler: React.PropTypes.func, retrieveLayerData: React.PropTypes.func, onToggle: React.PropTypes.func, + onToggleQuerypanel: React.PropTypes.func, onZoom: React.PropTypes.func, onSettings: React.PropTypes.func, style: React.PropTypes.object, @@ -36,6 +37,7 @@ var DefaultLayer = React.createClass({ activateLegendTool: React.PropTypes.bool, activateRemoveLayer: React.PropTypes.bool, activateSettingsTool: React.PropTypes.bool, + activateQueryTool: React.PropTypes.bool, activateZoomTool: React.PropTypes.bool, settingsText: React.PropTypes.oneOfType([React.PropTypes.string, React.PropTypes.element]), opacityText: React.PropTypes.oneOfType([React.PropTypes.string, React.PropTypes.element]), @@ -58,9 +60,11 @@ var DefaultLayer = React.createClass({ onZoom: () => {}, onSettings: () => {}, retrieveLayerData: () => {}, + onToggleQuerypanel: () => {}, activateRemoveLayer: false, activateLegendTool: false, activateSettingsTool: false, + activateQueryTool: true, activateZoomTool: false, includeDeleteButtonInSettings: false, modalOptions: {}, @@ -142,6 +146,16 @@ var DefaultLayer = React.createClass({ onClick={(node) => this.props.onToggle(node.id, node.expanded)}/> ); } + if (this.props.activateQueryTool) { + tools.push( + this.props.onToggleQuerypanel()}/> + ); + } if (this.props.activateZoomTool && this.props.node.bbox && !this.props.node.loadingError) { tools.push( { + return { + select: state.featuregrid && state.featuregrid.select || [], + selectAllActive: state.featuregrid && state.featuregrid.selectAll + }; +}, {})(require('./FeatureGrid')); + +const LocaleUtils = require('../../../utils/LocaleUtils'); +const I18N = require('../../../components/I18N/I18N'); +const {reactCellRendererFactory} = require('ag-grid-react'); +const GoToDetail = require('./ZoomToFeatureRenderer'); +const Spinner = require('react-spinkit'); +const assign = require('object-assign'); + +const DockedFeatureGrid = React.createClass({ + propTypes: { + open: React.PropTypes.bool, + detailOpen: React.PropTypes.bool, + expanded: React.PropTypes.bool, + header: React.PropTypes.string, + features: React.PropTypes.oneOfType([ React.PropTypes.array, React.PropTypes.object]), + detailsConfig: React.PropTypes.object, + columnsDef: React.PropTypes.array, + map: React.PropTypes.object, + loadingGrid: React.PropTypes.bool, + loadingGridError: React.PropTypes.oneOfType([ + React.PropTypes.string, + React.PropTypes.object + ]), + initWidth: React.PropTypes.number, + params: React.PropTypes.object, + // featureGrigConfigUrl: React.PropTypes.string, + profile: React.PropTypes.string, + onDetail: React.PropTypes.func, + onShowDetail: React.PropTypes.func, + toggleSiraControl: React.PropTypes.func, + changeMapView: React.PropTypes.func, + // loadFeatureGridConfig: React.PropTypes.func, + onExpandFilterPanel: React.PropTypes.func, + selectFeatures: React.PropTypes.func, + totalFeatures: React.PropTypes.number, + pagination: React.PropTypes.bool, + filterFields: React.PropTypes.array, + groupFields: React.PropTypes.array, + spatialField: React.PropTypes.object, + featureTypeName: React.PropTypes.string, + ogcVersion: React.PropTypes.string, + onQuery: React.PropTypes.func, + searchUrl: React.PropTypes.string, + dataSourceOptions: React.PropTypes.object, + withMap: React.PropTypes.bool.isRequired, + onConfigureQuery: React.PropTypes.func, + attributes: React.PropTypes.array, + cleanError: React.PropTypes.func, + selectAllToggle: React.PropTypes.func, + templateProfile: React.PropTypes.string, + zoomToFeatureAction: React.PropTypes.func + }, + contextTypes: { + messages: React.PropTypes.object + }, + getInitialState() { + return {}; + }, + getDefaultProps() { + return { + open: true, + detailOpen: true, + loadingGrid: false, + loadingGridError: null, + attributes: [], + profile: null, + expanded: true, + header: "featuregrid.header", + features: [], + featureTypeName: null, + ogcVersion: "2.0", + detailsConfig: {}, + columnsDef: [], + pagination: false, + params: {}, + groupFields: [], + filterFields: [], + spatialField: {}, + searchUrl: null, + dataSourceOptions: { + rowCount: -1, + pageSize: 10 + }, + initWidth: 600, + withMap: true, + templateProfile: 'default', + onDetail: () => {}, + onShowDetail: () => {}, + toggleSiraControl: () => {}, + changeMapView: () => {}, + // loadFeatureGridConfig: () => {}, + onExpandFilterPanel: () => {}, + selectFeatures: () => {}, + onQuery: () => {}, + onConfigureQuery: () => {}, + cleanError: () => {}, + selectAllToggle: () => {} + }; + }, + componentWillMount() { + let height = getWindowSize().maxHeight - 108; + this.setState({width: this.props.initWidth - 30, height}); + if (this.props.pagination && this.props.dataSourceOptions.pageSize) { + this.dataSource = this.getDataSource(this.props.dataSourceOptions); + }else if ( this.props.pagination && !this.props.dataSourceOptions.pageSize) { + let newFilter = FilterUtils.getOgcAllPropertyValue(this.props.featureTypeName, this.props.attributes[0].attribute); + this.props.onConfigureQuery(this.props.searchUrl, newFilter, this.props.params, { + "maxFeatures": 15, + "startIndex": 0 + }); + } + }, + shouldComponentUpdate(nextProps) { + return Object.keys(this.props).reduce((prev, prop) => { + if ( !prev && (prop !== 'map' && this.props[prop] !== nextProps[prop])) { + return true; + } + return prev; + }, false); + }, + componentWillUpdate(nextProps) { + if (nextProps.initWidth !== this.props.initWidth) { + let height = getWindowSize().maxHeight - 108; + this.setState({width: nextProps.initWidth - 30, height}); + } + if (!nextProps.loadingGrid && nextProps.pagination && (nextProps.dataSourceOptions !== this.props.dataSourceOptions)) { + this.dataSource = this.getDataSource(nextProps.dataSourceOptions); + } + if (!nextProps.loadingGrid && this.featureLoaded && nextProps.features !== this.props.features && Object.keys(nextProps.features).length > 0) { + let rowsThisPage = nextProps.features[this.getRequestId(this.featureLoaded)] || []; + this.featureLoaded.successCallback(rowsThisPage, nextProps.totalFeatures); + this.featureLoaded = null; + } + }, + onGridClose(filter) { + this.props.selectFeatures([]); + this.props.selectAllToggle(); + this.props.toggleSiraControl(); + if (filter) { + this.props.onExpandFilterPanel(true); + } + }, + onResize(event, resize) { + let size = resize.size; + this.setState({width: size.width, height: size.height}); + + }, + getRequestId(params) { + return `${params.startRow}_${params.endRow}_${params.sortModel.map((m) => `${m.colId}_${m.sort}` ).join('_')}`; + }, + getSortAttribute(colId) { + let col = this.props.columnsDef.find((c) => colId === `properties.${c.field}`); + return col && col.sortAttribute ? col.sortAttribute : ''; + }, + getSortOptions(params) { + return params.sortModel.reduce((o, m) => ({sortBy: this.getSortAttribute(m.colId), sortOrder: m.sort}), {}); + }, + getFeatures(params) { + if (!this.props.loadingGrid) { + let reqId = this.getRequestId(params); + let rowsThisPage = this.props.features[reqId]; + if (rowsThisPage) { + params.successCallback(rowsThisPage, this.props.totalFeatures); + }else { + let pagination = {startIndex: params.startRow, maxFeatures: params.endRow - params.startRow}; + let filterObj = { + groupFields: this.props.groupFields, + filterFields: this.props.filterFields.filter((field) => field.value), + spatialField: this.props.spatialField, + pagination + }; + let filter = FilterUtils.toOGCFilter(this.props.featureTypeName, filterObj, this.props.ogcVersion, this.getSortOptions(params)); + this.featureLoaded = params; + this.sortModel = params.sortModel; + this.props.onQuery(this.props.searchUrl, filter, this.props.params, reqId); + } + } + }, + getDataSource(dataSourceOptions) { + return { + rowCount: dataSourceOptions.rowCount, + getRows: this.getFeatures, + pageSize: dataSourceOptions.pageSize, + overflowSize: 20 + }; + }, + renderHeader() { + const header = LocaleUtils.getMessageById(this.context.messages, this.props.header); + + return ( +
+ + + + {header} + + + + + + +
+ ); + }, + renderLoadingException(loadingError, msg) { + let exception; + if (isObject(loadingError)) { + exception = loadingError.status + + "(" + loadingError.statusText + ")" + + ": " + loadingError.data; + } else { + exception = loadingError; + } + + return ( + { + this.props.cleanError(false); + // this.onGridClose(true); + }}> + + + + +
{exception}
+
+ + +
+ ); + }, + render() { + let loadingError = this.props.loadingGridError; + if (loadingError) { + return ( + this.renderLoadingException(loadingError, "queryform.query_request_exception") + ); + } + + const cols = this.props.columnsDef.map((column) => { + if (!column.profiles || (column.profiles && column.profiles.indexOf(this.props.profile) !== -1)) { + return assign({}, column, {field: "properties." + column.field}); + } + }).filter((c) => c); + const vCols = cols.filter((c) => !c.hide).length; + const defWidth = (this.state.width - 50) / vCols; + let columns = [{ + onCellClicked: this.goToDetail, + headerName: "", + cellRenderer: reactCellRendererFactory(GoToDetail), + suppressSorting: true, + suppressMenu: true, + pinned: true, + width: 25, + suppressResize: true + }, ...(cols.map((c) => assign({}, {width: defWidth}, c)))]; + if (this.sortModel && this.sortModel.length > 0) { + columns = columns.map((c) => { + let model = this.sortModel.find((m) => m.colId === c.field); + if ( model ) { + c.sort = model.sort; + } + return c; + }); + } + + let gridConf = this.props.pagination ? {dataSource: this.dataSource, features: []} : {features: this.props.features}; + + if (this.props.open || true) { + return ( + + +
+ +
Risultati - {this.props.totalFeatures !== -1 ? this.props.totalFeatures : ()}
+ + +
+ {this.props.loadingGrid ? (
+
+ +
+
) : null} +
+
+ ); + } + + return null; + }, + selectAll(select) { + if (select) { + let filterObj = { + groupFields: this.props.groupFields, + filterFields: this.props.filterFields.filter((field) => field.value), + spatialField: this.props.spatialField + }; + let SLD_BODY = FilterUtils.getSLD(this.props.featureTypeName, filterObj, '1.0'); + this.props.selectAllToggle(this.props.featureTypeName, SLD_BODY); + } else { + this.props.selectAllToggle(); + } + }, + selectFeatures(features) { + this.props.selectAllToggle(); + this.props.selectFeatures(features); + }, + goToDetail(params) { + let url = this.props.detailsConfig.service.url; + let urlParams = this.props.detailsConfig.service.params; + for (let param in urlParams) { + if (urlParams.hasOwnProperty(param)) { + url += "&" + param + "=" + urlParams[param]; + } + } + + let templateUrl = typeof this.props.detailsConfig.template === "string" ? this.props.detailsConfig.template : this.props.detailsConfig.template[this.props.templateProfile]; + this.props.onDetail( + templateUrl, + // this.props.detailsConfig.cardModelConfigUrl, + url + "&FEATUREID=" + params.data.id + (this.props.params.authkey ? "&authkey=" + this.props.params.authkey : "") + ); + + if (!this.props.detailOpen) { + this.props.onShowDetail(); + } + } +}); + +module.exports = DockedFeatureGrid; diff --git a/web/client/localConfig.json b/web/client/localConfig.json index 4fb0dbda1a..c2d4f96a1a 100644 --- a/web/client/localConfig.json +++ b/web/client/localConfig.json @@ -1,18 +1,18 @@ { - "proxyUrl": "/mapstore/proxy/?url=", - "geoStoreUrl": "/mapstore/rest/geostore/", + "proxyUrl": "/mapstore/proxy/?url=", + "geoStoreUrl": "/mapstore/rest/geostore/", "printUrl": "http://demo.geo-solutions.it/geoserver/pdf/info.json", "bingApiKey": "AhuXBu7ipR1gNbBfXhtUAyCZ6rkC5PkWpxs2MnMRZ1ZupxQfivjLCch22ozKSCAn", "mapquestApiKey": "__API_KEY_MAPQUEST__", - "initialMapFilter": "", + "initialMapFilter": "", "ignoreMobileCss" : true, "useAuthenticationRules": true, - "authenticationRules": [ - { - "urlPattern": ".*geostore.*", - "method": "basic" - } - ], + "authenticationRules": [ + { + "urlPattern": ".*geostore.*", + "method": "basic" + } + ], "plugins": { "mobile": [ { @@ -24,141 +24,141 @@ }, { "name": "DrawerMenu", - "cfg": { - "glyph": "1-stilo", - "buttonStyle": "primary", - "buttonClassName": "square-button", - "singleSection": true - } - }, - { - "name": "Identify", - "showIn": ["Settings"], - "cfg": { - "enableRevGeocode": true, - "wrapRevGeocode": false, - "panelClassName": "modal-dialog info-panel modal-content", - "headerClassName": "modal-header", - "bodyClassName": "modal-body info-wrap", - "headerGlyph": "", - "glyph": "map-marker", - "className": "square-button", - "closeGlyph": "1-close", - "style": { - "position": "absolute", - "width": "100%", - "bottom": "0px", - "zIndex": 1023, - "maxHeight": "70%", - "marginBottom": 0 - }, - "draggable": false, - "collapsible": true, - "viewerOptions": { - "container": "{context.ReactSwipe}", - "header": "{context.SwipeHeader}", - "collapsible": false - }, - "bodyClass": "mobile-feature-info" - } - }, + "cfg": { + "glyph": "1-stilo", + "buttonStyle": "primary", + "buttonClassName": "square-button", + "singleSection": true + } + }, + { + "name": "Identify", + "showIn": ["Settings"], + "cfg": { + "enableRevGeocode": true, + "wrapRevGeocode": false, + "panelClassName": "modal-dialog info-panel modal-content", + "headerClassName": "modal-header", + "bodyClassName": "modal-body info-wrap", + "headerGlyph": "", + "glyph": "map-marker", + "className": "square-button", + "closeGlyph": "1-close", + "style": { + "position": "absolute", + "width": "100%", + "bottom": "0px", + "zIndex": 1023, + "maxHeight": "70%", + "marginBottom": 0 + }, + "draggable": false, + "collapsible": true, + "viewerOptions": { + "container": "{context.ReactSwipe}", + "header": "{context.SwipeHeader}", + "collapsible": false + }, + "bodyClass": "mobile-feature-info" + } + }, { - "name": "Locate", - "cfg": { - "glyph": "1-position-1", + "name": "Locate", + "cfg": { + "glyph": "1-position-1", "btnConfig": { "className": "square-button" } - }, - "override": { - "Toolbar": { - "alwaysVisible": true - } - } - }, { - "name": "Home" - }, { - "name": "TOC", - "cfg": { - "visibilityCheckType": "glyph", - "settingsOptions": { - "includeCloseButton": false, - "closeGlyph": "1-close", - "asModal": false, - "buttonSize": "small" - } - }, - "override": { - "DrawerMenu": { - "glyph": "1-layer", + }, + "override": { + "Toolbar": { + "alwaysVisible": true + } + } + }, { + "name": "Home" + }, { + "name": "TOC", + "cfg": { + "visibilityCheckType": "glyph", + "settingsOptions": { + "includeCloseButton": false, + "closeGlyph": "1-close", + "asModal": false, + "buttonSize": "small" + } + }, + "override": { + "DrawerMenu": { + "glyph": "1-layer", "buttonConfig": { "buttonClassName": "square-button no-border" } - } - } - }, { - "name": "BackgroundSwitcher", - "cfg": { - "fluid": true, - "columnProperties": { + } + } + }, { + "name": "BackgroundSwitcher", + "cfg": { + "fluid": true, + "columnProperties": { "xs": 12, "sm": 12, "md": 12 } - }, - "override": { - "DrawerMenu": { - "glyph": "1-map", + }, + "override": { + "DrawerMenu": { + "glyph": "1-map", "buttonConfig": { "buttonClassName": "square-button no-border" } - } - } - }, { - "name": "Settings", - "cfg": { - "wrapWithPanel": false, - "closeGlyph": "1-close", + } + } + }, { + "name": "Settings", + "cfg": { + "wrapWithPanel": false, + "closeGlyph": "1-close", "overrideSettings": { "history": false }, - "wrap": true - } - }, { - "name": "About", - "hideFrom": "DrawerMenu", - "cfg": { - "modalConfig": { - "useModal": false, - "closeGlyph": "1-close" - } - } - }, + "wrap": true + } + }, { + "name": "About", + "hideFrom": "DrawerMenu", + "cfg": { + "modalConfig": { + "useModal": false, + "closeGlyph": "1-close" + } + } + }, { - "name": "MousePosition", - "cfg": { - "id": "mapstore-mouseposition-mobile" - } + "name": "MousePosition", + "cfg": { + "id": "mapstore-mouseposition-mobile" + } }, - { - "name": "Search", - "cfg": { - "withToggle": ["max-width: 768px", "min-width: 768px"] - } - }, { + { + "name": "Search", + "cfg": { + "withToggle": ["max-width: 768px", "min-width: 768px"] + } + }, { "name": "Toolbar", "id": "NavigationBar", "stateSelector": "toolbar", "cfg": { - "buttonStyle": "primary", + "buttonStyle": "primary", "id": "navigationBar" } }, { "name": "Toolbar", "id": "IdentifyBar", - "stateSelector": "identify", + "stateSelector": "identify", "cfg": { - "buttonStyle": "primary", + "buttonStyle": "primary", "pressedButtonStyle": "success", "id": "identifyBar" }, @@ -172,186 +172,185 @@ } }, { - "name": "MapLoading", - "hide": true, - "cfg": { - "className": "ms2-loading" - }, - "override": { - "Toolbar": { - "alwaysVisible": true - } - } - }, { - "name": "Login", - "hide": true, + "name": "MapLoading", + "hide": true, + "cfg": { + "className": "ms2-loading" + }, + "override": { + "Toolbar": { + "alwaysVisible": true + } + } + }, { + "name": "Login", + "hide": true, "cfg": { "nav": false, "menuProps": { "noCaret": true }, - "toolsCfg": [{ - "buttonSize": "small", - "includeCloseButton": false, - "useModal": false, - "closeGlyph": "1-close" - }, { - "buttonSize": "small", - "includeCloseButton": false, - "useModal": false, - "closeGlyph": "1-close" - }, { - "buttonSize": "small", - "includeCloseButton": false, - "useModal": false, - "closeGlyph": "1-close" - }] + "toolsCfg": [{ + "buttonSize": "small", + "includeCloseButton": false, + "useModal": false, + "closeGlyph": "1-close" + }, { + "buttonSize": "small", + "includeCloseButton": false, + "useModal": false, + "closeGlyph": "1-close" + }, { + "buttonSize": "small", + "includeCloseButton": false, + "useModal": false, + "closeGlyph": "1-close" + }] } - }, - "OmniBar", - { - "name": "BurgerMenu", - "hide": true - }, { - "name": "Expander", - "hide": true, + }, + "OmniBar", + { + "name": "BurgerMenu", + "hide": true + }, { + "name": "Expander", + "hide": true, "cfg":{ "className": "square-button" } - } + } ], - "desktop": [{ - "name": "Map", - "cfg": { - "toolsOptions": { - "scalebar": { - "leaflet": { - "position": "bottomright" - } - } - }, + "desktop": [{ + "name": "Map", + "cfg": { + "toolsOptions": { + "scalebar": { + "leaflet": { + "position": "bottomright" + } + } + }, "zoomControl": false - } - }, { - "name": "Help", - "cfg": { - "asPanel": false, - "closeGlyph": "1-close" - } - }, { - "name" : "Share", - "cfg": { - "closeGlyph" : "1-close" - } - }, { + } + }, { + "name": "Help", + "cfg": { + "asPanel": false, + "closeGlyph": "1-close" + } + }, { + "name" : "Share", + "cfg": { + "closeGlyph" : "1-close" + } + }, { "name": "DrawerMenu", - "cfg": { - "glyph": "1-stilo", - "buttonStyle": "primary", - "buttonClassName": "square-button", - "singleSection": true - } + "cfg": { + "glyph": "1-stilo", + "buttonStyle": "primary", + "buttonClassName": "square-button", + "singleSection": true + } }, { "name": "Identify", "showIn": ["IdentifyBar", "Settings"], - "cfg": { + "cfg": { "panelClassName": "modal-dialog info-panel modal-content", - "headerClassName": "modal-header", - "bodyClassName": "modal-body info-wrap", - "asPanel": false, - "headerGlyph": "", + "headerClassName": "modal-header", + "bodyClassName": "modal-body info-wrap", + "asPanel": false, + "headerGlyph": "", "glyph": "map-marker", - "className": "square-button", + "className": "square-button", "closeGlyph": "1-close", - "wrapRevGeocode": false, - "enableRevGeocode": true, - "viewerOptions": { - "container": "{context.ReactSwipe}", - "header": "{context.SwipeHeader}", - "headerOptions": { - "useButtons": true - }, - "containerProps": { - "continuous": false - }, - "collapsible": false - }, - "excludeOptions": [ - "links", - "allowedSRS", - "title", - "type", - "visibility", - "params", - "legend", - "statistics", - "columnDefs", - "advancedFilter", - "boundingBox", - "capabilities", - "describeCoverage", - "describeLayer", - "loading" - - ] - } + "wrapRevGeocode": false, + "enableRevGeocode": true, + "viewerOptions": { + "container": "{context.ReactSwipe}", + "header": "{context.SwipeHeader}", + "headerOptions": { + "useButtons": true + }, + "containerProps": { + "continuous": false + }, + "collapsible": false + }, + "excludeOptions": [ + "links", + "allowedSRS", + "title", + "type", + "visibility", + "params", + "legend", + "statistics", + "columnDefs", + "advancedFilter", + "boundingBox", + "capabilities", + "describeCoverage", + "describeLayer", + "loading" + ] + } }, "MadeWithLove", { - "name": "Locate", - "cfg": { - "glyph": "1-position-1", + "name": "Locate", + "cfg": { + "glyph": "1-position-1", "btnConfig": { "className": "square-button" } - }, - "override": { - "Toolbar": { - "alwaysVisible": true - } - } - }, { - "name": "Home" - }, { - "name": "TOC", - "cfg": { - "visibilityCheckType": "glyph", - "settingsOptions": { - "includeCloseButton": false, - "closeGlyph": "1-close", - "asModal": false, - "buttonSize": "small" - } - }, - "override": { - "DrawerMenu": { - "glyph": "1-layer", + }, + "override": { + "Toolbar": { + "alwaysVisible": true + } + } + }, { + "name": "Home" + }, { + "name": "TOC", + "cfg": { + "visibilityCheckType": "glyph", + "settingsOptions": { + "includeCloseButton": false, + "closeGlyph": "1-close", + "asModal": false, + "buttonSize": "small" + } + }, + "override": { + "DrawerMenu": { + "glyph": "1-layer", "buttonConfig": { "buttonClassName": "square-button no-border" } - } - } - }, { - "name": "BackgroundSwitcher", - "cfg": { - "fluid": true, - "columnProperties": { + } + } + }, { + "name": "BackgroundSwitcher", + "cfg": { + "fluid": true, + "columnProperties": { "xs": 12, "sm": 12, "md": 12 } - }, - "override": { - "DrawerMenu": { - "glyph": "1-map", + }, + "override": { + "DrawerMenu": { + "glyph": "1-map", "buttonConfig": { "buttonClassName": "square-button no-border" } - } - } - }, { - "name": "Measure", + } + } + }, { + "name": "Measure", "cfg":{ "showResults": false, "lineGlyph": "1-measure-lenght", @@ -359,103 +358,103 @@ "bearingGlyph": "1-bearing", "useButtonGroup": false }, - "override": { - "DrawerMenu": { - "glyph": "1-stilo", + "override": { + "DrawerMenu": { + "glyph": "1-stilo", "buttonConfig": { "buttonClassName": "square-button no-border" } - } - } - }, { - "name": "MeasureResults", + } + } + }, { + "name": "MeasureResults", "cfg":{ "closeGlyph": "1-close", "withPanelAsContainer": false } - }, { - "name": "Print", - "cfg": { - "useFixedScales": false, - "syncMapPreview": true, - "mapPreviewOptions": { - "enableScalebox": false, - "enableRefresh": false - }, - "closeGlyph": "1-close", - "submitConfig": { - "buttonConfig": { - "bsSize": "small", - "bsStyle": "primary" - }, - "glyph": "" - }, - "previewOptions": { - "buttonStyle": "primary" - }, - "withPanelAsContainer": false - } - }, { - "name": "ShapeFile", - "cfg": { - "wrap": true, - "wrapWithPanel": false, - "closeGlyph": "1-close", - "buttonSize": "small" - } - }, { - "name": "Settings", - "hideFrom": ["Toolbar", "DrawerMenu"], - "cfg": { - "wrapWithPanel": false, - "closeGlyph": "1-close", + }, { + "name": "Print", + "cfg": { + "useFixedScales": false, + "syncMapPreview": true, + "mapPreviewOptions": { + "enableScalebox": false, + "enableRefresh": false + }, + "closeGlyph": "1-close", + "submitConfig": { + "buttonConfig": { + "bsSize": "small", + "bsStyle": "primary" + }, + "glyph": "" + }, + "previewOptions": { + "buttonStyle": "primary" + }, + "withPanelAsContainer": false + } + }, { + "name": "ShapeFile", + "cfg": { + "wrap": true, + "wrapWithPanel": false, + "closeGlyph": "1-close", + "buttonSize": "small" + } + }, { + "name": "Settings", + "hideFrom": ["Toolbar", "DrawerMenu"], + "cfg": { + "wrapWithPanel": false, + "closeGlyph": "1-close", "overrideSettings": { "history": false }, - "wrap": true - } - }, { - "name": "MetadataExplorer", - "cfg": { - "showGetCapLinks": false, - "addAuthentication": false, - "wrap": true, - "searchOnStarup": false, - "wrapWithPanel": false, - "closeGlyph": "1-close", - "initialCatalogURL": { - "csw": "http://demo.geo-solutions.it/geoserver/csw", - "wms": "http://demo.geo-solutions.it/geoserver/wms" - } - } - }, { - "name": "About", - "hideFrom": ["DrawerMenu"], - "cfg": { - "modalConfig": { - "useModal": false, - "closeGlyph": "1-close" - } - } - }, "MousePosition", { - "name": "Search", - "cfg": { - "withToggle": ["max-width: 768px", "min-width: 768px"] - } - }, { + "wrap": true + } + }, { + "name": "MetadataExplorer", + "cfg": { + "showGetCapLinks": false, + "addAuthentication": false, + "wrap": true, + "searchOnStarup": false, + "wrapWithPanel": false, + "closeGlyph": "1-close", + "initialCatalogURL": { + "csw": "http://demo.geo-solutions.it/geoserver/csw", + "wms": "http://demo.geo-solutions.it/geoserver/wms" + } + } + }, { + "name": "About", + "hideFrom": ["DrawerMenu"], + "cfg": { + "modalConfig": { + "useModal": false, + "closeGlyph": "1-close" + } + } + }, "MousePosition", { + "name": "Search", + "cfg": { + "withToggle": ["max-width: 768px", "min-width: 768px"] + } + }, { "name": "Toolbar", "id": "NavigationBar", "stateSelector": "toolbar", "cfg": { - "buttonStyle": "primary", + "buttonStyle": "primary", "id": "navigationBar" } }, { "name": "Toolbar", "id": "IdentifyBar", - "stateSelector": "identify", + "stateSelector": "identify", "cfg": { - "buttonStyle": "primary", + "buttonStyle": "primary", "pressedButtonStyle": "success", "id": "identifyBar" }, @@ -469,86 +468,86 @@ } }, { - "name": "MapLoading", - "cfg": { - "className": "ms2-loading" - }, - "override": { - "Toolbar": { - "alwaysVisible": true - } - } - }, + "name": "MapLoading", + "cfg": { + "className": "ms2-loading" + }, + "override": { + "Toolbar": { + "alwaysVisible": true + } + } + }, { - "name": "Snapshot", - "cfg": { - "wrap": true, - "wrapWithPanel": false, - "closeGlyph": "1-close", - "buttonStyle": "primary" - } - }, + "name": "Snapshot", + "cfg": { + "wrap": true, + "wrapWithPanel": false, + "closeGlyph": "1-close", + "buttonStyle": "primary" + } + }, { "name":"ZoomIn", - "override": { - "Toolbar": { - "alwaysVisible": true - } - } + "override": { + "Toolbar": { + "alwaysVisible": true + } + } }, { "name":"ZoomOut", - "override": { - "Toolbar": { - "alwaysVisible": true - } - } + "override": { + "Toolbar": { + "alwaysVisible": true + } + } }, - "OmniBar", + "OmniBar", { - "name": "Login", + "name": "Login", "cfg": { "nav": false, "menuProps": { "noCaret": true }, - "toolsCfg": [{ - "buttonSize": "small", - "includeCloseButton": false, - "useModal": false, - "closeGlyph": "1-close" - }, { - "buttonSize": "small", - "includeCloseButton": false, - "useModal": false, - "closeGlyph": "1-close" - }, { - "buttonSize": "small", - "includeCloseButton": false, - "useModal": false, - "closeGlyph": "1-close" - }] + "toolsCfg": [{ + "buttonSize": "small", + "includeCloseButton": false, + "useModal": false, + "closeGlyph": "1-close" + }, { + "buttonSize": "small", + "includeCloseButton": false, + "useModal": false, + "closeGlyph": "1-close" + }, { + "buttonSize": "small", + "includeCloseButton": false, + "useModal": false, + "closeGlyph": "1-close" + }] } - }, - { - "name" : "Save", - "cfg": { - "closeGlyph" : "1-close" - } - },{ - "name" : "SaveAs", - "cfg": { - "closeGlyph" : "1-close" - } - }, - { - "name": "BurgerMenu" - }, { - "name": "Expander", + }, + { + "name" : "Save", + "cfg": { + "closeGlyph" : "1-close" + } + },{ + "name" : "SaveAs", + "cfg": { + "closeGlyph" : "1-close" + } + }, + { + "name": "BurgerMenu" + }, { + "name": "Expander", "cfg":{ "className": "square-button" } - }, { + }, { "name": "Undo", "cfg": { "glyph": "1-screen-backward", @@ -568,269 +567,269 @@ } }], "embedded": [ - { - "name": "Map", - "cfg": { - "zoomControl": false, - "tools": ["locate"] - } - }, { - "name": "Help" - }, { - "name": "DrawerMenu", - "cfg": { - "glyph": "1-stilo", - "buttonStyle": "primary", - "buttonClassName": "square-button", - "singleSection": true - } - }, - { - "name": "Identify", - "showIn": ["Settings"], - "cfg": { - "enableRevGeocode": true, - "wrapRevGeocode": false, - "panelClassName": "modal-dialog info-panel modal-content", - "headerClassName": "modal-header", - "bodyClassName": "modal-body info-wrap", - "headerGlyph": "", - "glyph": "map-marker", - "className": "square-button", - "closeGlyph": "1-close", - "style": { - "position": "absolute", - "width": "100%", - "bottom": "0px", - "zIndex": 1010, - "maxHeight": "70%", - "marginBottom": 0 - }, - "excludeOptions": [ - "links", - "allowedSRS", - "title", - "type", - "visibility", - "params", - "legend", - "statistics", - "columnDefs", - "advancedFilter", - "boundingBox", - "capabilities", - "describeCoverage", - "describeLayer", - "loading" + { + "name": "Map", + "cfg": { + "zoomControl": false, + "tools": ["locate"] + } + }, { + "name": "Help" + }, { + "name": "DrawerMenu", + "cfg": { + "glyph": "1-stilo", + "buttonStyle": "primary", + "buttonClassName": "square-button", + "singleSection": true + } + }, + { + "name": "Identify", + "showIn": ["Settings"], + "cfg": { + "enableRevGeocode": true, + "wrapRevGeocode": false, + "panelClassName": "modal-dialog info-panel modal-content", + "headerClassName": "modal-header", + "bodyClassName": "modal-body info-wrap", + "headerGlyph": "", + "glyph": "map-marker", + "className": "square-button", + "closeGlyph": "1-close", + "style": { + "position": "absolute", + "width": "100%", + "bottom": "0px", + "zIndex": 1010, + "maxHeight": "70%", + "marginBottom": 0 + }, + "excludeOptions": [ + "links", + "allowedSRS", + "title", + "type", + "visibility", + "params", + "legend", + "statistics", + "columnDefs", + "advancedFilter", + "boundingBox", + "capabilities", + "describeCoverage", + "describeLayer", + "loading" - ], - "draggable": false, - "collapsible": true, - "viewerOptions": { - "container": "{context.ReactSwipe}", - "header": "{context.SwipeHeader}", - "collapsible": false - }, - "bodyClass": "mobile-feature-info" - } - }, - { - "name": "Locate", - "cfg": { - "glyph": "1-position-1", - "btnConfig": { - "className": "square-button" - } - }, - "override": { - "Toolbar": { - "alwaysVisible": true - } - } - }, { - "name": "TOC", - "cfg": { - "visibilityCheckType": "glyph", - "settingsOptions": { - "includeCloseButton": false, - "closeGlyph": "1-close", - "asModal": false, - "buttonSize": "small" - } - }, - "override": { - "DrawerMenu": { - "glyph": "1-layer", - "buttonConfig": { - "buttonClassName": "square-button no-border" - } - } - } - }, { - "name": "BackgroundSwitcher", - "cfg": { - "fluid": true, - "columnProperties": { - "xs": 12, - "sm": 12, - "md": 12 - } - }, - "override": { - "DrawerMenu": { - "glyph": "1-map", - "buttonConfig": { - "buttonClassName": "square-button no-border" - } - } - } - }, { - "name": "Settings", - "cfg": { - "wrapWithPanel": false, - "closeGlyph": "1-close", - "overrideSettings": { - "history": false - }, - "wrap": true - } - }, { - "name": "About", - "hideFrom": "DrawerMenu", - "cfg": { - "modalConfig": { - "useModal": false, - "closeGlyph": "1-close" - } - } - }, - { - "name": "MousePosition", - "cfg": { - "id": "mapstore-mouseposition-mobile" - } - }, - { - "name": "Search", - "cfg": { - "withToggle": ["max-width: 768px", "min-width: 768px"] - } - }, { - "name": "Toolbar", - "id": "NavigationBar", - "stateSelector": "toolbar", - "cfg": { - "buttonStyle": "primary", - "id": "navigationBar" - } - }, { - "name": "Toolbar", - "id": "IdentifyBar", - "stateSelector": "identify", - "cfg": { - "buttonStyle": "primary", - "pressedButtonStyle": "success", - "id": "identifyBar" - }, - "isDefault": false - }, - { - "name": "MapLoading", - "hide": true, - "cfg": { - "className": "ms2-loading" - }, - "override": { - "Toolbar": { - "alwaysVisible": true - } - } - }, - "OmniBar", - { - "name": "BurgerMenu", - "hide": true - } - ], - "page": [{ - "name": "OmniBar", - "cfg": { - "className": "navbar shadow navbar-home" - } - }, { - "name":"ManagerMenu", - "hide": true, - "cfg": { - "entries": [{ - "msgId": "users.title", - "glyph": "1-group-mod", - "path": "/manager/usermanager" - }] - } - }, { - "name": "Login", - "hide": true, + ], + "draggable": false, + "collapsible": true, + "viewerOptions": { + "container": "{context.ReactSwipe}", + "header": "{context.SwipeHeader}", + "collapsible": false + }, + "bodyClass": "mobile-feature-info" + } + }, + { + "name": "Locate", + "cfg": { + "glyph": "1-position-1", + "btnConfig": { + "className": "square-button" + } + }, + "override": { + "Toolbar": { + "alwaysVisible": true + } + } + }, { + "name": "TOC", + "cfg": { + "visibilityCheckType": "glyph", + "settingsOptions": { + "includeCloseButton": false, + "closeGlyph": "1-close", + "asModal": false, + "buttonSize": "small" + } + }, + "override": { + "DrawerMenu": { + "glyph": "1-layer", + "buttonConfig": { + "buttonClassName": "square-button no-border" + } + } + } + }, { + "name": "BackgroundSwitcher", + "cfg": { + "fluid": true, + "columnProperties": { + "xs": 12, + "sm": 12, + "md": 12 + } + }, + "override": { + "DrawerMenu": { + "glyph": "1-map", + "buttonConfig": { + "buttonClassName": "square-button no-border" + } + } + } + }, { + "name": "Settings", + "cfg": { + "wrapWithPanel": false, + "closeGlyph": "1-close", + "overrideSettings": { + "history": false + }, + "wrap": true + } + }, { + "name": "About", + "hideFrom": "DrawerMenu", + "cfg": { + "modalConfig": { + "useModal": false, + "closeGlyph": "1-close" + } + } + }, + { + "name": "MousePosition", + "cfg": { + "id": "mapstore-mouseposition-mobile" + } + }, + { + "name": "Search", + "cfg": { + "withToggle": ["max-width: 768px", "min-width: 768px"] + } + }, { + "name": "Toolbar", + "id": "NavigationBar", + "stateSelector": "toolbar", + "cfg": { + "buttonStyle": "primary", + "id": "navigationBar" + } + }, { + "name": "Toolbar", + "id": "IdentifyBar", + "stateSelector": "identify", + "cfg": { + "buttonStyle": "primary", + "pressedButtonStyle": "success", + "id": "identifyBar" + }, + "isDefault": false + }, + { + "name": "MapLoading", + "hide": true, + "cfg": { + "className": "ms2-loading" + }, + "override": { + "Toolbar": { + "alwaysVisible": true + } + } + }, + "OmniBar", + { + "name": "BurgerMenu", + "hide": true + } + ], + "page": [{ + "name": "OmniBar", + "cfg": { + "className": "navbar shadow navbar-home" + } + }, { + "name":"ManagerMenu", + "hide": true, + "cfg": { + "entries": [{ + "msgId": "users.title", + "glyph": "1-group-mod", + "path": "/manager/usermanager" + }] + } + }, { + "name": "Login", + "hide": true, "cfg": { "nav": false, "menuProps": { "noCaret": true - }, - "toolsCfg": [{ - "buttonSize": "small", - "includeCloseButton": false, - "useModal": false, - "closeGlyph": "1-close" - }, { - "buttonSize": "small", - "includeCloseButton": false, - "useModal": false, - "closeGlyph": "1-close" - }, { - "buttonSize": "small", - "includeCloseButton": false, - "useModal": false, - "closeGlyph": "1-close" - }] + }, + "toolsCfg": [{ + "buttonSize": "small", + "includeCloseButton": false, + "useModal": false, + "closeGlyph": "1-close" + }, { + "buttonSize": "small", + "includeCloseButton": false, + "useModal": false, + "closeGlyph": "1-close" + }, { + "buttonSize": "small", + "includeCloseButton": false, + "useModal": false, + "closeGlyph": "1-close" + }] - } - }, {"name": "Language"}, { - "name" : "Attribution", - "hide": true - }, { - "name": "Footer", - "cfg": { - "bottom": true - } - }], + } + }, {"name": "Language"}, { + "name" : "Attribution", + "hide": true + }, { + "name": "Footer", + "cfg": { + "bottom": true + } + }], - "maps": [ - {"name": "Header"}, - {"name": "Fork"}, - {"name": "MapSearch"}, - {"name": "HomeDescription"}, - {"name": "MapType"}, - {"name": "CreateNewMap"}, - {"name": "Maps"}, - {"name": "Examples"} - ], - "manager": [{ - "name": "Header" - }, "Redirect",{ - "name": "Manager", - "cfg": { - "navStyle": { - "flex": "inherit" - } - } - }, { - "name": "Home", - "hide": true - }, { - "name": "UserManager", - "hide": true, - "cfg": { - "id": "usermanager" - } - }] - } + "maps": [ + {"name": "Header"}, + {"name": "Fork"}, + {"name": "MapSearch"}, + {"name": "HomeDescription"}, + {"name": "MapType"}, + {"name": "CreateNewMap"}, + {"name": "Maps"}, + {"name": "Examples"} + ], + "manager": [{ + "name": "Header" + }, "Redirect",{ + "name": "Manager", + "cfg": { + "navStyle": { + "flex": "inherit" + } + } + }, { + "name": "Home", + "hide": true + }, { + "name": "UserManager", + "hide": true, + "cfg": { + "id": "usermanager" + } + }] + } } diff --git a/web/client/plugins/DrawerMenu.jsx b/web/client/plugins/DrawerMenu.jsx index 2bd835570d..7f8ba9e2c0 100644 --- a/web/client/plugins/DrawerMenu.jsx +++ b/web/client/plugins/DrawerMenu.jsx @@ -23,7 +23,8 @@ const {partialRight} = require('lodash'); const Menu = connect((state) => ({ show: state.controls.drawer && state.controls.drawer.enabled, - activeKey: state.controls.drawer && state.controls.drawer.menu + activeKey: state.controls.drawer && state.controls.drawer.menu, + width: (state.controls.queryPanel && state.controls.queryPanel.enabled) ? 500 : 300 }), { onToggle: toggleControl.bind(null, 'drawer', null), onChoose: partialRight(setControlProperty.bind(null, 'drawer', 'menu'), true), diff --git a/web/client/plugins/FeatureGrid.jsx b/web/client/plugins/FeatureGrid.jsx new file mode 100644 index 0000000000..5c128aa32b --- /dev/null +++ b/web/client/plugins/FeatureGrid.jsx @@ -0,0 +1,10 @@ +/** + * 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. + */ +module.exports = { + FeatureGridPlugin: require('../components/data/featuregrid/DockedFeatureGrid') +}; diff --git a/web/client/plugins/QueryPanel.jsx b/web/client/plugins/QueryPanel.jsx new file mode 100644 index 0000000000..400f24ba88 --- /dev/null +++ b/web/client/plugins/QueryPanel.jsx @@ -0,0 +1,375 @@ +/** + * 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 Sidebar = require('react-sidebar').default; +const {createSelector} = require('reselect'); +const {changeLayerProperties, changeGroupProperties, toggleNode, + sortNode, showSettings, hideSettings, updateSettings, updateNode, removeNode, getLayerCapabilities} = require('../actions/layers'); +const {zoomToExtent} = require('../actions/map'); +const {toggleControl} = require('../actions/controls'); + +const {groupsSelector} = require('../selectors/layers'); + +const LayersUtils = require('../utils/LayersUtils'); + +const assign = require('object-assign'); + +// include application component +const QueryBuilder = require('../components/data/query/QueryBuilder'); + +const {bindActionCreators} = require('redux'); +const { + // QueryBuilder action functions + addGroupField, + addFilterField, + removeFilterField, + updateFilterField, + updateExceptionField, + updateLogicCombo, + removeGroupField, + changeCascadingValue, + expandAttributeFilterPanel, + expandSpatialFilterPanel, + selectSpatialMethod, + selectSpatialOperation, + removeSpatialSelection, + showSpatialSelectionDetails, + reset, + changeDwithinValue, + zoneGetValues, + zoneSearch, + zoneChange +} = require('../actions/queryform'); + +const {query} = require('../actions/query'); + +const { + changeDrawingStatus, + endDrawing +} = require('../actions/draw'); + + +let attrTest = [{ + "name": "the_geom", + "maxOccurs": 1, + "minOccurs": 0, + "nillable": true, + "type": "gml:MultiPolygon", + "localType": "MultiPolygon" + }, + { + "name": "STATE_NAME", + "maxOccurs": 1, + "minOccurs": 0, + "nillable": true, + "type": "xsd:string", + "localType": "string" + }, + { + "name": "STATE_FIPS", + "maxOccurs": 1, + "minOccurs": 0, + "nillable": true, + "type": "xsd:string", + "localType": "string" + }, + { + "name": "SUB_REGION", + "maxOccurs": 1, + "minOccurs": 0, + "nillable": true, + "type": "xsd:string", + "localType": "string" + }, + { + "name": "STATE_ABBR", + "maxOccurs": 1, + "minOccurs": 0, + "nillable": true, + "type": "xsd:string", + "localType": "string" + }, + { + "name": "LAND_KM", + "maxOccurs": 1, + "minOccurs": 0, + "nillable": true, + "type": "xsd:number", + "localType": "number" + }, + { + "name": "WATER_KM", + "maxOccurs": 1, + "minOccurs": 0, + "nillable": true, + "type": "xsd:number", + "localType": "number" + }, + { + "name": "PERSONS", + "maxOccurs": 1, + "minOccurs": 0, + "nillable": true, + "type": "xsd:number", + "localType": "number" + }, + { + "name": "FAMILIES", + "maxOccurs": 1, + "minOccurs": 0, + "nillable": true, + "type": "xsd:number", + "localType": "number" + }, + { + "name": "HOUSHOLD", + "maxOccurs": 1, + "minOccurs": 0, + "nillable": true, + "type": "xsd:number", + "localType": "number" + }, + { + "name": "MALE", + "maxOccurs": 1, + "minOccurs": 0, + "nillable": true, + "type": "xsd:number", + "localType": "number" + }, + { + "name": "FEMALE", + "maxOccurs": 1, + "minOccurs": 0, + "nillable": true, + "type": "xsd:number", + "localType": "number" + }, + { + "name": "WORKERS", + "maxOccurs": 1, + "minOccurs": 0, + "nillable": true, + "type": "xsd:number", + "localType": "number" + }, + { + "name": "DRVALONE", + "maxOccurs": 1, + "minOccurs": 0, + "nillable": true, + "type": "xsd:number", + "localType": "number" + }, + { + "name": "EMPLOYED", + "maxOccurs": 1, + "minOccurs": 0, + "nillable": true, + "type": "xsd:number", + "localType": "number" + }, + { + "name": "UNEMPLOY", + "maxOccurs": 1, + "minOccurs": 0, + "nillable": true, + "type": "xsd:number", + "localType": "number" + }]; + + +const attributesSelector = () => ( + attrTest.map((attribute) => { + return assign({}, attribute, {values: [attribute.name]}); + }) + ) || []; // && + +// connecting a Dumb component to the store +// makes it a smart component +// we both connect state => props +// and actions to event handlers +const SmartQueryForm = connect((state) => { + return { + // QueryBuilder props + useMapProjection: state.queryform.useMapProjection, + groupLevels: state.queryform.groupLevels, + groupFields: state.queryform.groupFields, + filterFields: state.queryform.filterFields, + attributes: attributesSelector(state), + spatialField: state.queryform.spatialField, + showDetailsPanel: state.queryform.showDetailsPanel, + toolbarEnabled: state.queryform.toolbarEnabled, + attributePanelExpanded: state.queryform.attributePanelExpanded, + spatialPanelExpanded: state.queryform.spatialPanelExpanded, + searchUrl: "http://demo.geo-solutions.it/geoserver/ows?service=WFS", + featureTypeName: "topp:states", + ogcVersion: "1.1.0", + resultTitle: "Query Result", + showGeneratedFilter: false + }; +}, dispatch => { + return { + attributeFilterActions: bindActionCreators({ + onAddGroupField: addGroupField, + onAddFilterField: addFilterField, + onRemoveFilterField: removeFilterField, + onUpdateFilterField: updateFilterField, + onUpdateExceptionField: updateExceptionField, + onUpdateLogicCombo: updateLogicCombo, + onRemoveGroupField: removeGroupField, + onChangeCascadingValue: changeCascadingValue, + onExpandAttributeFilterPanel: expandAttributeFilterPanel + }, dispatch), + spatialFilterActions: bindActionCreators({ + onExpandSpatialFilterPanel: expandSpatialFilterPanel, + onSelectSpatialMethod: selectSpatialMethod, + onSelectSpatialOperation: selectSpatialOperation, + onChangeDrawingStatus: changeDrawingStatus, + onRemoveSpatialSelection: removeSpatialSelection, + onShowSpatialSelectionDetails: showSpatialSelectionDetails, + onEndDrawing: endDrawing, + onChangeDwithinValue: changeDwithinValue, + zoneFilter: zoneGetValues, + zoneSearch, + zoneChange + }, dispatch), + queryToolbarActions: bindActionCreators({ + onQuery: query, + onReset: reset, + onChangeDrawingStatus: changeDrawingStatus + }, dispatch) + }; +})(QueryBuilder); + +const tocSelector = createSelector( + [ + (state) => state.controls && state.controls.toolbar && state.controls.toolbar.active === 'toc', + groupsSelector, + (state) => state.layers.settings || {expanded: false, options: {opacity: 1}}, + (state) => state.controls && state.controls.queryPanel && state.controls.queryPanel.enabled || false + ], (enabled, groups, settings, querypanelEnabled) => ({ + enabled, + groups, + settings, + querypanelEnabled + }) +); + +const LayerTree = React.createClass({ + propTypes: { + id: React.PropTypes.number, + buttonContent: React.PropTypes.node, + groups: React.PropTypes.array, + settings: React.PropTypes.object, + querypanelEnabled: React.PropTypes.bool, + groupStyle: React.PropTypes.object, + groupPropertiesChangeHandler: React.PropTypes.func, + layerPropertiesChangeHandler: React.PropTypes.func, + onToggleGroup: React.PropTypes.func, + onToggleLayer: React.PropTypes.func, + onToggleQuery: React.PropTypes.func, + onZoomToExtent: React.PropTypes.func, + retrieveLayerData: React.PropTypes.func, + onSort: React.PropTypes.func, + onSettings: React.PropTypes.func, + hideSettings: React.PropTypes.func, + updateSettings: React.PropTypes.func, + updateNode: React.PropTypes.func, + removeNode: React.PropTypes.func, + activateRemoveLayer: React.PropTypes.bool, + activateLegendTool: React.PropTypes.bool, + activateZoomTool: React.PropTypes.bool, + activateSettingsTool: React.PropTypes.bool, + visibilityCheckType: React.PropTypes.string, + settingsOptions: React.PropTypes.object + }, + getDefaultProps() { + return { + groupPropertiesChangeHandler: () => {}, + layerPropertiesChangeHandler: () => {}, + retrieveLayerData: () => {}, + onToggleGroup: () => {}, + onToggleLayer: () => {}, + onToggleQuery: () => {}, + onZoomToExtent: () => {}, + onSettings: () => {}, + updateNode: () => {}, + removeNode: () => {}, + activateLegendTool: true, + activateZoomTool: true, + activateSettingsTool: true, + activateRemoveLayer: true, + visibilityCheckType: "checkbox", + settingsOptions: {}, + querypanelEnabled: false + }; + }, + getNoBackgroundLayers(group) { + return group.name !== 'background'; + }, + renderSidebar() { + return ( + +
+ + ); + }, + renderQueryPanel() { + return (
+ +
); + }, + render() { + return this.renderSidebar(); + } +}); + +const QueryPanelPlugin = connect(tocSelector, { + groupPropertiesChangeHandler: changeGroupProperties, + layerPropertiesChangeHandler: changeLayerProperties, + retrieveLayerData: getLayerCapabilities, + onToggleGroup: LayersUtils.toggleByType('groups', toggleNode), + onToggleLayer: LayersUtils.toggleByType('layers', toggleNode), + onToggleQuery: toggleControl.bind(null, 'queryPanel', null), + onSort: LayersUtils.sortUsing(LayersUtils.sortLayers, sortNode), + onSettings: showSettings, + onZoomToExtent: zoomToExtent, + hideSettings, + updateSettings, + updateNode, + removeNode +})(LayerTree); + +module.exports = { + QueryPanelPlugin, + reducers: { + queryform: require('../reducers/queryform'), + query: require('../reducers/query') + } +}; diff --git a/web/client/plugins/TOC.jsx b/web/client/plugins/TOC.jsx index d6314b6dae..1a5de9b5db 100644 --- a/web/client/plugins/TOC.jsx +++ b/web/client/plugins/TOC.jsx @@ -8,9 +8,12 @@ const React = require('react'); const {connect} = require('react-redux'); const {createSelector} = require('reselect'); +const {Button, Glyphicon} = require('react-bootstrap'); + const {changeLayerProperties, changeGroupProperties, toggleNode, sortNode, showSettings, hideSettings, updateSettings, updateNode, removeNode, getLayerCapabilities} = require('../actions/layers'); const {zoomToExtent} = require('../actions/map'); +const {toggleControl} = require('../actions/controls'); const {groupsSelector} = require('../selectors/layers'); @@ -21,15 +24,245 @@ const assign = require('object-assign'); const layersIcon = require('./toolbar/assets/img/layers.png'); +// include application component +const QueryBuilder = require('../components/data/query/QueryBuilder'); + +const {bindActionCreators} = require('redux'); +const { + // QueryBuilder action functions + addGroupField, + addFilterField, + removeFilterField, + updateFilterField, + updateExceptionField, + updateLogicCombo, + removeGroupField, + changeCascadingValue, + expandAttributeFilterPanel, + expandSpatialFilterPanel, + selectSpatialMethod, + selectSpatialOperation, + removeSpatialSelection, + showSpatialSelectionDetails, + reset, + changeDwithinValue, + zoneGetValues, + zoneSearch, + zoneChange +} = require('../actions/queryform'); + +const {query} = require('../actions/query'); + +const { + changeDrawingStatus, + endDrawing +} = require('../actions/draw'); + + +let attrTest = [{ + "name": "the_geom", + "maxOccurs": 1, + "minOccurs": 0, + "nillable": true, + "type": "gml:MultiPolygon", + "localType": "MultiPolygon" + }, + { + "name": "STATE_NAME", + "maxOccurs": 1, + "minOccurs": 0, + "nillable": true, + "type": "xsd:string", + "localType": "string" + }, + { + "name": "STATE_FIPS", + "maxOccurs": 1, + "minOccurs": 0, + "nillable": true, + "type": "xsd:string", + "localType": "string" + }, + { + "name": "SUB_REGION", + "maxOccurs": 1, + "minOccurs": 0, + "nillable": true, + "type": "xsd:string", + "localType": "string" + }, + { + "name": "STATE_ABBR", + "maxOccurs": 1, + "minOccurs": 0, + "nillable": true, + "type": "xsd:string", + "localType": "string" + }, + { + "name": "LAND_KM", + "maxOccurs": 1, + "minOccurs": 0, + "nillable": true, + "type": "xsd:number", + "localType": "number" + }, + { + "name": "WATER_KM", + "maxOccurs": 1, + "minOccurs": 0, + "nillable": true, + "type": "xsd:number", + "localType": "number" + }, + { + "name": "PERSONS", + "maxOccurs": 1, + "minOccurs": 0, + "nillable": true, + "type": "xsd:number", + "localType": "number" + }, + { + "name": "FAMILIES", + "maxOccurs": 1, + "minOccurs": 0, + "nillable": true, + "type": "xsd:number", + "localType": "number" + }, + { + "name": "HOUSHOLD", + "maxOccurs": 1, + "minOccurs": 0, + "nillable": true, + "type": "xsd:number", + "localType": "number" + }, + { + "name": "MALE", + "maxOccurs": 1, + "minOccurs": 0, + "nillable": true, + "type": "xsd:number", + "localType": "number" + }, + { + "name": "FEMALE", + "maxOccurs": 1, + "minOccurs": 0, + "nillable": true, + "type": "xsd:number", + "localType": "number" + }, + { + "name": "WORKERS", + "maxOccurs": 1, + "minOccurs": 0, + "nillable": true, + "type": "xsd:number", + "localType": "number" + }, + { + "name": "DRVALONE", + "maxOccurs": 1, + "minOccurs": 0, + "nillable": true, + "type": "xsd:number", + "localType": "number" + }, + { + "name": "EMPLOYED", + "maxOccurs": 1, + "minOccurs": 0, + "nillable": true, + "type": "xsd:number", + "localType": "number" + }, + { + "name": "UNEMPLOY", + "maxOccurs": 1, + "minOccurs": 0, + "nillable": true, + "type": "xsd:number", + "localType": "number" + }]; + + +const attributesSelector = () => ( + attrTest.map((attribute) => { + return assign({}, attribute, {values: [attribute.name]}); + }) + ) || []; // && + +// connecting a Dumb component to the store +// makes it a smart component +// we both connect state => props +// and actions to event handlers +const SmartQueryForm = connect((state) => { + return { + // QueryBuilder props + useMapProjection: state.queryform.useMapProjection, + groupLevels: state.queryform.groupLevels, + groupFields: state.queryform.groupFields, + filterFields: state.queryform.filterFields, + attributes: attributesSelector(state), + spatialField: state.queryform.spatialField, + showDetailsPanel: state.queryform.showDetailsPanel, + toolbarEnabled: state.queryform.toolbarEnabled, + attributePanelExpanded: state.queryform.attributePanelExpanded, + spatialPanelExpanded: state.queryform.spatialPanelExpanded, + searchUrl: "http://demo.geo-solutions.it/geoserver/ows?service=WFS", + featureTypeName: "topp:states", + ogcVersion: "1.1.0", + resultTitle: "Query Result", + showGeneratedFilter: false + }; +}, dispatch => { + return { + attributeFilterActions: bindActionCreators({ + onAddGroupField: addGroupField, + onAddFilterField: addFilterField, + onRemoveFilterField: removeFilterField, + onUpdateFilterField: updateFilterField, + onUpdateExceptionField: updateExceptionField, + onUpdateLogicCombo: updateLogicCombo, + onRemoveGroupField: removeGroupField, + onChangeCascadingValue: changeCascadingValue, + onExpandAttributeFilterPanel: expandAttributeFilterPanel + }, dispatch), + spatialFilterActions: bindActionCreators({ + onExpandSpatialFilterPanel: expandSpatialFilterPanel, + onSelectSpatialMethod: selectSpatialMethod, + onSelectSpatialOperation: selectSpatialOperation, + onChangeDrawingStatus: changeDrawingStatus, + onRemoveSpatialSelection: removeSpatialSelection, + onShowSpatialSelectionDetails: showSpatialSelectionDetails, + onEndDrawing: endDrawing, + onChangeDwithinValue: changeDwithinValue, + zoneFilter: zoneGetValues, + zoneSearch, + zoneChange + }, dispatch), + queryToolbarActions: bindActionCreators({ + onQuery: query, + onReset: reset, + onChangeDrawingStatus: changeDrawingStatus + }, dispatch) + }; +})(QueryBuilder); + const tocSelector = createSelector( [ (state) => state.controls && state.controls.toolbar && state.controls.toolbar.active === 'toc', groupsSelector, - (state) => state.layers.settings || {expanded: false, options: {opacity: 1}} - ], (enabled, groups, settings) => ({ + (state) => state.layers.settings || {expanded: false, options: {opacity: 1}}, + (state) => state.controls && state.controls.queryPanel && state.controls.queryPanel.enabled || false + ], (enabled, groups, settings, querypanelEnabled) => ({ enabled, groups, - settings + settings, + querypanelEnabled }) ); @@ -43,11 +276,13 @@ const LayerTree = React.createClass({ buttonContent: React.PropTypes.node, groups: React.PropTypes.array, settings: React.PropTypes.object, + querypanelEnabled: React.PropTypes.bool, groupStyle: React.PropTypes.object, groupPropertiesChangeHandler: React.PropTypes.func, layerPropertiesChangeHandler: React.PropTypes.func, onToggleGroup: React.PropTypes.func, onToggleLayer: React.PropTypes.func, + onToggleQuery: React.PropTypes.func, onZoomToExtent: React.PropTypes.func, retrieveLayerData: React.PropTypes.func, onSort: React.PropTypes.func, @@ -70,6 +305,7 @@ const LayerTree = React.createClass({ retrieveLayerData: () => {}, onToggleGroup: () => {}, onToggleLayer: () => {}, + onToggleQuery: () => {}, onZoomToExtent: () => {}, onSettings: () => {}, updateNode: () => {}, @@ -79,16 +315,14 @@ const LayerTree = React.createClass({ activateSettingsTool: true, activateRemoveLayer: true, visibilityCheckType: "checkbox", - settingsOptions: {} + settingsOptions: {}, + querypanelEnabled: false }; }, getNoBackgroundLayers(group) { return group.name !== 'background'; }, - render() { - if (!this.props.groups) { - return
; - } + renderTOC() { return (
@@ -104,6 +338,7 @@ const LayerTree = React.createClass({
); + }, + renderQueryPanel() { + return (
+ + +
); + }, + render() { + if (!this.props.groups) { + return
; + } + if (this.props.querypanelEnabled) { + return this.renderQueryPanel(); + } + return this.renderTOC(); } }); @@ -136,6 +386,7 @@ const TOCPlugin = connect(tocSelector, { retrieveLayerData: getLayerCapabilities, onToggleGroup: LayersUtils.toggleByType('groups', toggleNode), onToggleLayer: LayersUtils.toggleByType('layers', toggleNode), + onToggleQuery: toggleControl.bind(null, 'queryPanel', null), onSort: LayersUtils.sortUsing(LayersUtils.sortLayers, sortNode), onSettings: showSettings, onZoomToExtent: zoomToExtent, @@ -167,5 +418,8 @@ module.exports = { priority: 2 } }), - reducers: {} + reducers: { + queryform: require('../reducers/queryform'), + query: require('../reducers/query') + } }; diff --git a/web/client/plugins/drawer/drawer.css b/web/client/plugins/drawer/drawer.css index 4104bc4dbc..939703339d 100644 --- a/web/client/plugins/drawer/drawer.css +++ b/web/client/plugins/drawer/drawer.css @@ -19,16 +19,6 @@ box-shadow: none; } -.nav-menu { - - max-width:300px; - -} -div.nav-menu{ - background: rgba(255,255,255,1); - left:-300px; -} - #mapstore-drawermenu > div > div:nth-child(2) { display: none; } diff --git a/web/client/product/plugins.js b/web/client/product/plugins.js index ac68ddcd33..ef9f8287c9 100644 --- a/web/client/product/plugins.js +++ b/web/client/product/plugins.js @@ -57,7 +57,9 @@ module.exports = { SharePlugin: require('../plugins/Share'), SavePlugin: require('../plugins/Save'), SaveAsPlugin: require('../plugins/SaveAs'), - CreateNewMapPlugin: require('../plugins/CreateNewMap') + CreateNewMapPlugin: require('../plugins/CreateNewMap'), + QueryPanelPlugin: require('../plugins/QueryPanel'), + FeatureGridPlugin: require('../plugins/FeatureGrid') }, requires: { ReactSwipe: require('react-swipeable-views').default, diff --git a/web/client/reducers/query.js b/web/client/reducers/query.js new file mode 100644 index 0000000000..e4feb8eb28 --- /dev/null +++ b/web/client/reducers/query.js @@ -0,0 +1,117 @@ +/** + * 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 { + FEATURE_TYPE_LOADED, + FEATURE_TYPE_ERROR, + FEATURE_LOADED, + FEATURE_ERROR, + QUERY_RESULT, + QUERY_ERROR, + RESET_QUERY +} = require('../actions/query'); + +const assign = require('object-assign'); + +const types = { + 'xsd:string': 'list', + 'xsd:dateTime': 'date', + 'xsd:number': 'number' +}; +const fieldConfig = {}; +const extractInfo = (featureType) => { + return { + attributes: featureType.featureTypes[0].properties + .filter((attribute) => attribute.type.indexOf('gml:') !== 0) + .map((attribute) => { + let conf = { + label: attribute.name, + attribute: attribute.name, + type: types[attribute.type], + valueId: "id", + valueLabel: "name", + values: [] + }; + conf = fieldConfig[attribute.name] ? {...conf, ...fieldConfig[attribute.name]} : conf; + return conf; + }) + }; +}; + +const extractData = (feature) => { + return ['STATE_NAME', 'STATE_ABBR', 'SUB_REGION', 'STATE_FIPS' ].map((attribute) => ({ + attribute, + values: feature.features.reduce((previous, current) => { + if (previous.indexOf(current.properties[attribute]) === -1) { + return [...previous, current.properties[attribute]].sort(); + } + return previous; + }, []) + })).reduce((previous, current) => { + return assign(previous, { + [current.attribute]: current.values.map((value) => ({ + id: value, + name: value + })) + }); + }, {}); +}; + +const initialState = { + featureTypes: {}, + data: {}, + result: '', + resultError: null +}; + +function query(state = initialState, action) { + switch (action.type) { + case FEATURE_TYPE_LOADED: { + return assign({}, state, { + featureTypes: assign({}, state.featureTypes, {[action.typeName]: extractInfo(action.featureType)}) + }); + } + case FEATURE_TYPE_ERROR: { + return assign({}, state, { + featureTypes: assign({}, state.featureTypes, {[action.typeName]: {error: action.error}}) + }); + } + case FEATURE_LOADED: { + return assign({}, state, { + data: assign({}, state.data, {[action.typeName]: extractData(action.feature)}) + }); + } + case FEATURE_ERROR: { + return assign({}, state, { + featureTypes: assign({}, state.data, {[action.typeName]: {error: action.error}}) + }); + } + case QUERY_RESULT: { + return assign({}, state, { + result: action.result, + resultError: null + }); + } + case QUERY_ERROR: { + return assign({}, state, { + result: '', + resultError: action.error + }); + } + case RESET_QUERY: { + return assign({}, state, { + result: '', + resultError: null + }); + } + default: + return state; + } +} + +module.exports = query; diff --git a/web/client/utils/FilterUtils.jsx b/web/client/utils/FilterUtils.jsx index f03576de06..3d3d93cd70 100644 --- a/web/client/utils/FilterUtils.jsx +++ b/web/client/utils/FilterUtils.jsx @@ -839,6 +839,28 @@ const FilterUtils = { } return (strFilter && strFilter.length > 0) ? strFilter : false; + }, + getOgcAllPropertyValue: function(featureTypeName, attribute) { + return ` + + `; + }, + getSLD: function(ftName, json, version) { + let filter = this.toOGCFilter(ftName, json, version); + let sIdx = filter.search( `<${this.nsplaceholder}:Filter>`); + if (sIdx !== -1) { + let eIndx = filter.search( ``); + filter = filter.substr(sIdx, eIndx - sIdx); + } else { + filter = ''; + } + return `${ftName}${filter}circle#0000FF20`; } }; From e6c2a65fd9588d2f2fc0513dd34150eb0941dd24 Mon Sep 17 00:00:00 2001 From: Lorenzo Natali Date: Mon, 28 Nov 2016 13:04:41 +0100 Subject: [PATCH 2/8] add style improvements --- .../components/data/query/GroupField.jsx | 49 ++++++++--------- .../components/data/query/queryform.css | 53 +++++++++++++++++++ 2 files changed, 74 insertions(+), 28 deletions(-) diff --git a/web/client/components/data/query/GroupField.jsx b/web/client/components/data/query/GroupField.jsx index 3f9c29db0e..5032345fc1 100644 --- a/web/client/components/data/query/GroupField.jsx +++ b/web/client/components/data/query/GroupField.jsx @@ -103,7 +103,7 @@ const GroupField = React.createClass({ let comboValues = this.getComboValues(selectedAttribute, this.props.attributes); return ( - + )}> - ) : ( - ) @@ -151,11 +151,9 @@ const GroupField = React.createClass({ renderGroupHeader(groupField) { const removeButton = groupField.groupId ? ( - - - ) : ( @@ -164,10 +162,12 @@ const GroupField = React.createClass({ return ( - {removeButton} - -
-
+ +
+ {removeButton} +
+
+ { @@ -175,17 +175,14 @@ const GroupField = React.createClass({ }) } fieldName="logic" - style={{width: "140px", marginTop: "3px"}} + style={{minWidth: "80px", display: "inline-block", position: "relative", top: "10px"}} fieldRowId={groupField.id} fieldValue={ LocaleUtils.getMessageById(this.context.messages, this.props.logicComboOptions.filter((opt) => groupField.logic === opt.logic)[0].name) } onUpdateField={this.updateLogicCombo}/> -
- - -
+
); @@ -207,7 +204,7 @@ const GroupField = React.createClass({ return element; }); - const removeButton = groupField.index <= this.props.groupLevels ? + const addButton = groupField.index <= this.props.groupLevels ? ( @@ -218,18 +215,14 @@ const GroupField = React.createClass({ return ( {this.renderGroupHeader(groupField)} - {container} - - - - - - {removeButton} - - +
{container}
+
+ + {addButton} +
); }, diff --git a/web/client/components/data/query/queryform.css b/web/client/components/data/query/queryform.css index 5804fe4143..40c36c2e21 100644 --- a/web/client/components/data/query/queryform.css +++ b/web/client/components/data/query/queryform.css @@ -13,6 +13,11 @@ margin-left: 1px; margin-right: 1px; } +.logicHeader .query-remove{ + float: right; + margin-right: 10px; + padding-top: 10px; +} #queryFormPanel .panel-title{ font-size: 16px; @@ -23,6 +28,54 @@ text-decoration:none; } +/*****************************/ +/* DRAWER MENU ***************/ +/*****************************/ +#mapstore-drawermenu #queryFormPanel .querypanel { + height: 100%; + max-height: 100%; +} +#mapstore-drawermenu #queryFormPanel .queryFormToolbar button:disabled{ + color: gray; +} +#mapstore-drawermenu #queryFormPanel .logicHeader { + margin: 0; + background-color: #078aa3; + color: white; +} +#mapstore-drawermenu #queryFormPanel .logicHeader .query-remove { + float: right; + padding-top: 0px; + top: 10px; + margin-right: 10px; +} + +#mapstore-drawermenu #queryFormPanel .btn.remove-filter-button { + padding: 3px 6px; +} + +#mapstore-drawermenu #queryFormPanel .logicHeader .group_label_a,#mapstore-drawermenu #queryFormPanel .logicHeader .group_label_b { + margin: auto 5px; +} + +/* nested groups */ +#mapstore-drawermenu #queryFormPanel .panel .panel .panel{ + margin-left: 10px; +} +#mapstore-drawermenu #queryFormPanel .panel .panel .panel > .panel-body{ + border: thin solid #078aa3; + margin-top: 1px; +} +#mapstore-drawermenu #queryFormPanel .query-content, +#mapstore-drawermenu #queryFormPanel .query-buttons +{ + margin-left: 3px; + margin-bottom: 3px; +} +/*****************************/ +/* DRAWER MENU END ***********/ +/*****************************/ + .detail_geom_button{ text-align: right; margin-top: 3px; From 0d324442765f3614c5837587a179ad0364606c5d Mon Sep 17 00:00:00 2001 From: Lorenzo Natali Date: Fri, 2 Dec 2016 18:49:07 +0100 Subject: [PATCH 3/8] wip state linking --- web/client/actions/query.js | 13 +- web/client/components/TOC/DefaultLayer.jsx | 2 +- .../data/featuregrid/DockedFeatureGrid.jsx | 50 +++--- .../components/data/query/GroupField.jsx | 2 +- web/client/localConfig.json | 4 +- web/client/plugins/DrawerMenu.jsx | 2 +- web/client/plugins/FeatureGrid.jsx | 17 +- web/client/plugins/TOC.jsx | 158 ++---------------- web/client/reducers/query.js | 9 +- 9 files changed, 83 insertions(+), 174 deletions(-) diff --git a/web/client/actions/query.js b/web/client/actions/query.js index a4ae56a0e7..24542e4a3e 100644 --- a/web/client/actions/query.js +++ b/web/client/actions/query.js @@ -5,6 +5,7 @@ * This source code is licensed under the BSD-style license found in the * LICENSE file in the root directory of this source tree. */ +const FEATURE_TYPE_SELECTED = 'FEATURE_TYPE_SELECTED'; const FEATURE_TYPE_LOADED = 'FEATURE_TYPE_LOADED'; const FEATURE_LOADED = 'FEATURE_LOADED'; const FEATURE_TYPE_ERROR = 'FEATURE_TYPE_ERROR'; @@ -14,7 +15,13 @@ const QUERY_ERROR = 'QUERY_ERROR'; const RESET_QUERY = 'RESET_QUERY'; const axios = require('../libs/ajax'); - +function featureTypeSelected(url, typeName) { + return { + type: FEATURE_TYPE_SELECTED, + url, + typeName + }; +} function featureTypeLoaded(typeName, featureType) { return { type: FEATURE_TYPE_LOADED, @@ -103,7 +110,7 @@ function loadFeature(baseUrl, typeName) { function query(seachURL, data) { return (dispatch) => { - return axios.post(seachURL + '&outputFormat=json', data, { + return axios.post(seachURL + '?service=WFS&&outputFormat=json', data, { timeout: 60000, headers: {'Accept': 'application/json', 'Content-Type': 'application/json'} }).then((response) => { @@ -121,6 +128,7 @@ function resetQuery() { } module.exports = { + FEATURE_TYPE_SELECTED, FEATURE_TYPE_LOADED, FEATURE_LOADED, FEATURE_TYPE_ERROR, @@ -128,6 +136,7 @@ module.exports = { QUERY_RESULT, QUERY_ERROR, RESET_QUERY, + featureTypeSelected, describeFeatureType, loadFeature, query, diff --git a/web/client/components/TOC/DefaultLayer.jsx b/web/client/components/TOC/DefaultLayer.jsx index a13bc837ad..fb853cce9c 100644 --- a/web/client/components/TOC/DefaultLayer.jsx +++ b/web/client/components/TOC/DefaultLayer.jsx @@ -153,7 +153,7 @@ var DefaultLayer = React.createClass({ ref="target" style={{"float": "right", cursor: "pointer"}} glyph="search" - onClick={() => this.props.onToggleQuerypanel()}/> + onClick={(node) => this.props.onToggleQuerypanel(node.url, node.name)}/> ); } if (this.props.activateZoomTool && this.props.node.bbox && !this.props.node.loadingError) { diff --git a/web/client/components/data/featuregrid/DockedFeatureGrid.jsx b/web/client/components/data/featuregrid/DockedFeatureGrid.jsx index 5ae05d274f..8293baf7b0 100644 --- a/web/client/components/data/featuregrid/DockedFeatureGrid.jsx +++ b/web/client/components/data/featuregrid/DockedFeatureGrid.jsx @@ -37,13 +37,12 @@ const DockedFeatureGrid = React.createClass({ React.PropTypes.string, React.PropTypes.object ]), - initWidth: React.PropTypes.number, + initWidth: React.PropTypes.oneOfType([ React.PropTypes.number, React.PropTypes.string ]), params: React.PropTypes.object, // featureGrigConfigUrl: React.PropTypes.string, profile: React.PropTypes.string, onDetail: React.PropTypes.func, onShowDetail: React.PropTypes.func, - toggleSiraControl: React.PropTypes.func, changeMapView: React.PropTypes.func, // loadFeatureGridConfig: React.PropTypes.func, onExpandFilterPanel: React.PropTypes.func, @@ -102,7 +101,6 @@ const DockedFeatureGrid = React.createClass({ templateProfile: 'default', onDetail: () => {}, onShowDetail: () => {}, - toggleSiraControl: () => {}, changeMapView: () => {}, // loadFeatureGridConfig: () => {}, onExpandFilterPanel: () => {}, @@ -115,7 +113,7 @@ const DockedFeatureGrid = React.createClass({ }, componentWillMount() { let height = getWindowSize().maxHeight - 108; - this.setState({width: this.props.initWidth - 30, height}); + this.setState({width: `calc( ${this.props.initWidth} - 30px)`, height}); if (this.props.pagination && this.props.dataSourceOptions.pageSize) { this.dataSource = this.getDataSource(this.props.dataSourceOptions); }else if ( this.props.pagination && !this.props.dataSourceOptions.pageSize) { @@ -137,7 +135,7 @@ const DockedFeatureGrid = React.createClass({ componentWillUpdate(nextProps) { if (nextProps.initWidth !== this.props.initWidth) { let height = getWindowSize().maxHeight - 108; - this.setState({width: nextProps.initWidth - 30, height}); + this.setState({width: `calc( ${this.props.initWidth} - 30px)`, height}); } if (!nextProps.loadingGrid && nextProps.pagination && (nextProps.dataSourceOptions !== this.props.dataSourceOptions)) { this.dataSource = this.getDataSource(nextProps.dataSourceOptions); @@ -151,7 +149,7 @@ const DockedFeatureGrid = React.createClass({ onGridClose(filter) { this.props.selectFeatures([]); this.props.selectAllToggle(); - this.props.toggleSiraControl(); + // TODO close if (filter) { this.props.onExpandFilterPanel(true); } @@ -252,25 +250,29 @@ const DockedFeatureGrid = React.createClass({ ); } - const cols = this.props.columnsDef.map((column) => { + let cols = this.props.columnsDef.map((column) => { if (!column.profiles || (column.profiles && column.profiles.indexOf(this.props.profile) !== -1)) { return assign({}, column, {field: "properties." + column.field}); } }).filter((c) => c); - const vCols = cols.filter((c) => !c.hide).length; - const defWidth = (this.state.width - 50) / vCols; - let columns = [{ - onCellClicked: this.goToDetail, - headerName: "", - cellRenderer: reactCellRendererFactory(GoToDetail), - suppressSorting: true, - suppressMenu: true, - pinned: true, - width: 25, - suppressResize: true - }, ...(cols.map((c) => assign({}, {width: defWidth}, c)))]; + + if (this.goToDetail) { + const vCols = cols.filter((c) => !c.hide).length; + const defWidth = (this.state.width - 50) / vCols; + cols = [{ + onCellClicked: this.goToDetail, + headerName: "", + cellRenderer: reactCellRendererFactory(GoToDetail), + suppressSorting: true, + suppressMenu: true, + pinned: true, + width: 25, + suppressResize: true + }, ...(cols.map((c) => assign({}, {width: defWidth}, c)))]; + } + if (this.sortModel && this.sortModel.length > 0) { - columns = columns.map((c) => { + cols = cols.map((c) => { let model = this.sortModel.find((m) => m.colId === c.field); if ( model ) { c.sort = model.sort; @@ -291,11 +293,11 @@ const DockedFeatureGrid = React.createClass({ onVisibleChange={this.handleVisibleChange} onSizeChange={this.handleSizeChange} fluid={true} - dimStyle={{ background: 'rgba(0, 0, 100, 0.2)' }} + dimStyle={{ background: 'rgba(0, 0, 100, 0.2)', position: "relative" }} dockStyle={null} dockHiddenStyle={null} > -
+