Skip to content

Commit

Permalink
Improve style templates support (#684)
Browse files Browse the repository at this point in the history
  • Loading branch information
allyoucanmap authored Dec 21, 2021
1 parent 9252845 commit 805db2e
Show file tree
Hide file tree
Showing 12 changed files with 1,527 additions and 113 deletions.
12 changes: 11 additions & 1 deletion geonode_mapstore_client/client/js/api/geonode/config/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,17 @@ export const getNewGeoStoryConfig = (newGeoStoryUrl = '/static/mapstore/configs/
);
};

export const getStyleTemplates = (styleTemplatesUrl = '/static/mapstore/configs/styleTemplates.json') => {
return cache.styleTemplatesConfig
? new Promise((resolve) => resolve(cache.styleTemplatesConfig))
: axios.get(styleTemplatesUrl).then(({ data }) => {
cache.styleTemplatesConfig = data?.templates;
return cache.styleTemplatesConfig;
});
};

export default {
getNewMapConfiguration,
getNewGeoStoryConfig
getNewGeoStoryConfig,
getStyleTemplates
};
96 changes: 31 additions & 65 deletions geonode_mapstore_client/client/js/plugins/VisualStyleEditor.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,13 +45,10 @@ import {
selectedStyleSelector
} from '@mapstore/framework/selectors/styleeditor';
import Message from '@mapstore/framework/components/I18N/Message';
import SVGPreview from '@mapstore/framework/components/styleeditor/SVGPreview';
import Popover from '@mapstore/framework/components/styleeditor/Popover';
import GNButton from '@js/components/Button';
import Portal from '@mapstore/framework/components/misc/Portal';
import ResizableModal from '@mapstore/framework/components/misc/ResizableModal';
import StylesAPI from '@mapstore/framework/api/geoserver/Styles';
import { getStyleTemplates } from '@mapstore/framework/utils/StyleEditorUtils';
import { getResourcePerms, isNewResource } from '@js/selectors/resource';
import Spinner from '@js/components/Spinner';
import { mapLayoutValuesSelector } from '@mapstore/framework/selectors/maplayout';
Expand All @@ -60,8 +57,8 @@ import tooltip from '@mapstore/framework/components/misc/enhancers/tooltip';
import { getSelectedLayer } from '@mapstore/framework/selectors/layers';
const FormControl = localizedProps('placeholder')(FormControlRB);
import useLocalStorage from '@js/hooks/useLocalStorage';
import TemplateSelector from '@js/plugins/visualstyleeditor/TemplateSelector';

const defaultStyleTemplates = getStyleTemplates().filter((styleTemplate) => !['Base CSS', 'Base SLD'].includes(styleTemplate.title));
const Button = tooltip(GNButton);

function SaveStyleButton({
Expand All @@ -70,7 +67,8 @@ function SaveStyleButton({
size,
isStyleChanged,
error,
loading
loading,
msgId = 'save'
}) {
const isDisabled = !!(loading || error);
return (
Expand All @@ -81,7 +79,7 @@ function SaveStyleButton({
disabled={isDisabled}
onClick={isDisabled ? () => {} : () => onClick()}
>
<Message msgId="save"/>{' '}{loading && <Spinner />}
<Message msgId={msgId}/>{' '}{loading && <Spinner />}
</Button>
);
}
Expand All @@ -102,6 +100,29 @@ const ConnectedSaveStyleButton = connect(
}
)((SaveStyleButton));

const ConnectedTemplateSelector = connect(
createSelector([
getUpdatedLayer,
geometryTypeSelector,
selectedStyleFormatSelector,
codeStyleSelector,
selectedStyleSelector,
state => state?.controls?.visualStyleEditor?.tmpCode
], (layer, geometryType, format, code, selectedStyleName, tmpCode) => ({
geometryType,
format,
selectedStyle: layer?.availableStyles?.find(({ name }) => name === selectedStyleName),
code,
tmpCode
})),
{
onSelect: editStyleCode,
onUpdateMetadata: updateEditorMetadata,
onUpdate: updateStyleCode,
onStoreTmpCode: setControlProperty.bind(null, 'visualStyleEditor', 'tmpCode')
}
)((TemplateSelector));

function VisualStyleEditor({
layer,
editorConfig,
Expand All @@ -110,17 +131,12 @@ function VisualStyleEditor({
onReset,
temporaryStyleId,
showLayerProperties,
geometryType,
format,
onSelect,
onUpdateMetadata,
initialCode,
enabled,
onClose,
onZoomTo,
onCreate,
onDelete,
selectedStyle,
style: styleProp
}) {

Expand Down Expand Up @@ -155,11 +171,6 @@ function VisualStyleEditor({
};
}, []);

function handleSelect(styleTemplate) {
onSelect(styleTemplate);
onUpdateMetadata({ styleJSON: null });
}

function handleClose() {
onReset();
onClose();
Expand All @@ -171,22 +182,10 @@ function VisualStyleEditor({
onZoomTo(extent, layer.bbox.crs);
}

const styleTemplates = defaultStyleTemplates.filter((styleTemplate) => styleTemplate.types.includes(geometryType) && format === styleTemplate.format);

if (!enabled || !layer.id) {
return null;
}

const hasTemplates = !!(styleTemplates?.length > 0);

function replaceTemplateMetadata(code) {
const styleTitle = selectedStyle?.metadata?.title || selectedStyle?.label || selectedStyle?.title || selectedStyle?.name || '';
const styleDescription = selectedStyle?.metadata?.description || selectedStyle?._abstract || '';
return code
.replace(/\$\{styleTitle\}/, styleTitle)
.replace(/\$\{styleAbstract\}/, styleDescription);
}

function closeNotification() {
setNotificationClose(true);
}
Expand All @@ -207,35 +206,10 @@ function VisualStyleEditor({
<Glyphicon glyph="1-close"/>
</Button>
</div>}
{(hasTemplates || showLayerProperties) &&
<div className="gn-visual-style-editor-toolbar">
{showLayerProperties && <ConnectedSaveStyleButton variant="default" size="xs"/>}
{hasTemplates && <Popover
placement="right"
content={
<ul className="gn-visual-style-editor-templates" >
{styleTemplates.map((styleTemplate, idx) => {
return (
<li
key={idx}
className="gn-visual-style-editor-template"
onClick={() => handleSelect(replaceTemplateMetadata(styleTemplate.code))}
>
<div className="gn-visual-style-editor-template-preview">
{styleTemplate?.preview?.config
? <SVGPreview { ...styleTemplate.preview.config } />
: styleTemplate?.preview}
</div>
<div className="gn-visual-style-editor-template-title">{styleTemplate.title}</div>
</li>
);
})}
</ul>
}
>
<Button size="xs"><Message msgId="gnviewer.copyFrom"/></Button>
</Popover>}
</div>}
{showLayerProperties && <ConnectedSaveStyleButton variant="primary" size="xs" msgId="gnviewer.applyStyle"/>}
<ConnectedTemplateSelector />
</div>
{(!notificationClose && !dismissStyleNotification) && <div className="gn-visual-style-editor-alert alert-info">
<div className="gn-visual-style-editor-alert-message">
<Message msgId="gnviewer.stylesFirstClone" />
Expand Down Expand Up @@ -328,22 +302,16 @@ const VisualStyleEditorPlugin = connect(
getUpdatedLayer,
temporaryIdSelector,
styleServiceSelector,
selectedStyleFormatSelector,
geometryTypeSelector,
initialCodeStyleSelector,
state => state?.controls?.visualStyleEditor?.enabled,
selectedStyleSelector,
state => mapLayoutValuesSelector(state, { height: true }),
getSelectedLayer
], (layer, temporaryStyleId, styleService, format, geometryType, initialCode, enabled, selectedStyleName, style, originalLayer) => ({
], (layer, temporaryStyleId, styleService, initialCode, enabled, style, originalLayer) => ({
layer,
temporaryStyleId,
styleService,
format,
geometryType,
initialCode,
enabled,
selectedStyle: layer?.availableStyles?.find(({ name }) => name === selectedStyleName),
style,
originalStyle: originalLayer?.style
})),
Expand All @@ -352,8 +320,6 @@ const VisualStyleEditorPlugin = connect(
onUpdateParams: updateSettingsParams,
onInit: initStyleService,
onReset: toggleStyleEditor.bind(null, undefined, false),
onSelect: editStyleCode,
onUpdateMetadata: updateEditorMetadata,
onSave: updateStyleCode,
onClose: setControlProperty.bind(null, 'visualStyleEditor', 'enabled', false),
onZoomTo: zoomToExtent,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
/*
* Copyright 2021, GeoSolutions Sas.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/

import React, { useEffect, useRef, useState } from 'react';
import Message from '@mapstore/framework/components/I18N/Message';
import SVGPreview from '@mapstore/framework/components/styleeditor/SVGPreview';
import Popover from '@mapstore/framework/components/styleeditor/Popover';
import GNButton from '@js/components/Button';
import tooltip from '@mapstore/framework/components/misc/enhancers/tooltip';
import { getStyleParser } from '@mapstore/framework/utils/VectorStyleUtils';
import { getStyleTemplates } from '@js/api/geonode/config';
const Button = tooltip(GNButton);

function TemplateSelector({
code,
format,
geometryType,
onSelect,
selectedStyle,
onUpdateMetadata,
onUpdate,
tmpCode,
onStoreTmpCode
}) {

const isMounted = useRef();
const [templates, setTemplates] = useState([]);

useEffect(() => {
isMounted.current = true;
getStyleTemplates()
.then((response) => {
if (isMounted.current) {
setTemplates(response);
}
});
return () => {
isMounted.current = false;
};
}, []);

function handleApply() {
onStoreTmpCode(code);
onUpdateMetadata({ styleJSON: null });
onUpdate();
}

const storeTmpCode = useRef();
storeTmpCode.current = (isOpen) => {
if (isOpen) {
onStoreTmpCode(code);
} else {
if (tmpCode) {
onSelect(tmpCode);
}
}
};

function handleOpen(isOpen) {
storeTmpCode.current(isOpen);
}

useEffect(() => {
return () => {
onStoreTmpCode('');
};
}, []);

const styleTemplates = templates.filter((styleTemplate) => styleTemplate.types.includes(geometryType));
const hasTemplates = styleTemplates?.length > 0;

function handleSelect(styleTemplate) {
onSelect(styleTemplate);
}

function replaceTemplateMetadata(templateCode) {
const styleTitle = selectedStyle?.metadata?.title || selectedStyle?.label || selectedStyle?.title || selectedStyle?.name || '';
getStyleParser(format)
.writeStyle({
...templateCode,
name: styleTitle
})
.then((updateCode) => {
if (isMounted.current) {
handleSelect(updateCode);
}
});
}

if (!hasTemplates) {
return null;
}

return (
<Popover
placement="right"
onOpen={handleOpen}
content={
<div className="gn-visual-style-editor-templates" >
<ul >
{styleTemplates.map((styleTemplate, idx) => {
return (
<li
key={idx}
className="gn-visual-style-editor-template"
onClick={() => replaceTemplateMetadata(styleTemplate.code)}
>
<div className="gn-visual-style-editor-template-preview">
{styleTemplate?.preview?.config
? <SVGPreview { ...styleTemplate.preview.config } />
: styleTemplate?.preview}
</div>
<div className="gn-visual-style-editor-template-title">{styleTemplate.title}</div>
</li>
);
})}
</ul>
<div className="gn-visual-style-editor-templates-footer">
<Button size="xs" variant="primary" onClick={handleApply}><Message msgId="gnviewer.applyStyle"/></Button>
</div>
</div>
}
>
<Button size="xs"><Message msgId="gnviewer.copyFrom"/></Button>
</Popover>
);
}

export default TemplateSelector;
20 changes: 0 additions & 20 deletions geonode_mapstore_client/client/postCompile.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
const rimraf = require('rimraf');
const childProcess = require('child_process');
const fs = require('fs-extra');
const path = require('path');
const message = require('@mapstore/project/scripts/utils/message');
Expand All @@ -18,22 +17,3 @@ fs.renameSync(path.resolve(appDirectory, staticPath, 'translations'), path.resol
fs.writeFileSync(path.resolve(appDirectory, 'version.txt'), `${name}-v${version}-${commit}`);
fs.writeFileSync(path.resolve(appDirectory, staticPath, 'version.txt'), `${name}-v${version}-${commit}`);
message.title(`updated version -> version ${version} - commit ${commit}`);

const packageJSONPath = './package.json';
const rootPackageJSONPath = '../../package.json';

// copy dependencies to root package

const packageJSON = require(packageJSONPath);
const rootPackageJSON = require(rootPackageJSONPath);

const mapStoreCommit = childProcess
.execSync('git rev-parse @:./MapStore2')
.toString().trim();

const mapStorePackage = `git+https://github.com/geosolutions-it/MapStore2.git#${mapStoreCommit}`;

rootPackageJSON.dependencies = JSON.parse(JSON.stringify(packageJSON.dependencies));
rootPackageJSON.dependencies.mapstore = mapStorePackage;

fs.writeFileSync(rootPackageJSONPath, JSON.stringify(rootPackageJSON, null, 2), 'utf8');
Loading

0 comments on commit 805db2e

Please sign in to comment.