blob: f760f020b92c1ac26b35ce671c74e289bbbb953f [file] [log] [blame]
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import { css, JsonValue, styled, t } from '@superset-ui/core';
// eslint-disable-next-line no-restricted-imports
import { Button } from '@superset-ui/core/components/Button';
import { Form } from '@superset-ui/core/components/Form';
import Tabs from '@superset-ui/core/components/Tabs';
import { Data as GsData } from 'geostyler-data';
import { Style as GsStyle } from 'geostyler-style';
import WfsDataParser, {
RequestParams1_1_0,
RequestParams2_0_0,
} from 'geostyler-wfs-parser';
import { FC, useEffect, useState } from 'react';
import { isWfsLayerConf, isWmsLayerConf, isXyzLayerConf } from './typeguards';
import {
BaseLayerConf,
LayerConf,
LayerConfigsPopoverContentProps,
WfsLayerConf,
WmsLayerConf,
XyzLayerConf,
} from './types';
import { getServiceVersions, hasAllRequiredWfsParams } from './serviceUtil';
import { ControlFormItem } from '../ColumnConfigControl/ControlForm';
import GeoStylerWrapper from './GeoStylerWrapper';
// Enum for the different tabs
const LAYER_CONFIG_TABS = {
LAYER: '1',
GEOSTYLER: '2',
};
export const StyledButtonContainer = styled.div`
display: flex;
margin: 8px;
`;
export const StyledCloseButton = styled(Button)`
${({ theme }) => css`
flex: 1;
margin-right: 4px;
line-height: 1.5715;
border-radius: ${theme.borderRadius}px;
background-color: ${theme.colorPrimaryBg};
color: ${theme.colorPrimaryText};
font-size: ${theme.fontSizeSM}px;
font-weight: ${theme.fontWeightStrong};
text-transform: uppercase;
min-width: ${theme.sizeUnit * 36};
min-height: ${theme.sizeUnit * 8};
box-shadow: none;
border-width: 0px;
border-style: none;
border-color: transparent;
&:hover {
background-color: ${theme.colorPrimaryBgHover};
color: ${theme.colorPrimaryText};
}
`}
`;
export const StyledControlFormItem = styled(ControlFormItem)`
${({ theme }) => css`
border-radius: ${theme.borderRadius}px;
`}
`;
export const StyledControlNumberFormItem = styled(ControlFormItem)`
${({ theme }) => css`
border-radius: ${theme.borderRadius}px;
width: 100%;
`}
`;
export const StyledGeoStyler = styled(GeoStylerWrapper)`
${({ theme }) => css`
h2 {
font-weight: ${theme.fontWeightNormal};
font-size: ${theme.fontSizeXL}px;
}
.ant-form-item-control {
flex: unset;
}
`}
`;
export const StyledSaveButton = styled(Button)`
${({ theme }) => css`
flex: 1;
margin-left: 4px;
line-height: 1.5715;
border-radius: ${theme.borderRadius}px;
background-color: ${theme.colorPrimary};
color: ${theme.colorTextLightSolid};
font-size: ${theme.fontSizeSM}px;
font-weight: ${theme.fontWeightStrong};
text-transform: uppercase;
min-width: ${theme.sizeUnit * 36};
min-height: ${theme.sizeUnit * 8};
box-shadow: none;
border-width: 0px;
border-style: none;
border-color: transparent;
&:hover {
background-color: ${theme.colorPrimaryText};
}
`}
`;
export const LayerConfigsPopoverContent: FC<
LayerConfigsPopoverContentProps
> = ({ onClose = () => {}, onSave = () => {}, layerConf }) => {
const [currentLayerConf, setCurrentLayerConf] =
useState<LayerConf>(layerConf);
const initialWmsVersion =
layerConf.type === 'WMS' ? layerConf.version : undefined;
const [wmsVersion, setWmsVersion] = useState<string | undefined>(
initialWmsVersion,
);
const initialWfsVersion =
layerConf.type === 'WFS' ? layerConf.version : undefined;
const [wfsVersion, setWfsVersion] = useState<string | undefined>(
initialWfsVersion,
);
const [geostylerData, setGeoStylerData] = useState<GsData | undefined>(
undefined,
);
const serviceVersions = getServiceVersions();
// This is needed to force mounting the form every time
// we get a new layerConf prop. Otherwise the input fields
// will not be updated properly, since ControlFormItem only
// recognises the `value` property once and then handles the
// values in its on state. Remounting creates a new component
// and thereby starts with a fresh state.
const [formKey, setFormKey] = useState<number>(0);
useEffect(() => {
setCurrentLayerConf({ ...layerConf });
setFormKey(oldFormKey => oldFormKey + 1);
}, [layerConf]);
const onFieldValueChange = (value: JsonValue, key: string) => {
setCurrentLayerConf({
...currentLayerConf,
[key]: value,
});
};
const onLayerTypeChange = (value: LayerConf['type']) => {
if (value === 'WFS') {
setCurrentLayerConf({
...currentLayerConf,
type: value,
version: serviceVersions[value][0],
style: {
name: 'Default Style',
rules: [
{
name: 'Default Rule',
symbolizers: [
{
kind: 'Line',
// eslint-disable-next-line theme-colors/no-literal-colors
color: '#000000',
width: 2,
},
{
kind: 'Mark',
wellKnownName: 'circle',
// eslint-disable-next-line theme-colors/no-literal-colors
color: '#000000',
},
{
kind: 'Fill',
// eslint-disable-next-line theme-colors/no-literal-colors
color: '#000000',
},
],
},
],
},
} as WfsLayerConf);
} else if (value === 'XYZ') {
setCurrentLayerConf({
...currentLayerConf,
type: value,
} as XyzLayerConf);
} else {
setCurrentLayerConf({
...currentLayerConf,
type: value,
version: serviceVersions[value][0],
} as WmsLayerConf);
}
};
const onLayerTitleChange = (fieldValue: string) => {
onFieldValueChange(fieldValue, 'title');
};
const onLayerUrlChange = (fieldValue: string) => {
onFieldValueChange(fieldValue, 'url');
};
const onLayersParamChange = (fieldValue: string) => {
onFieldValueChange(fieldValue, 'layersParam');
};
const onTypeNameChange = (fieldValue: string) => {
onFieldValueChange(fieldValue, 'typeName');
};
const onWmsVersionChange = (fieldValue: string) => {
onFieldValueChange(fieldValue, 'version');
setWmsVersion(fieldValue);
};
const onWfsVersionChange = (fieldValue: string) => {
onFieldValueChange(fieldValue, 'version');
setWfsVersion(fieldValue);
};
const onMaxFeaturesChange = (fieldValue: number) => {
onFieldValueChange(fieldValue, 'maxFeatures');
};
const onStyleChange = (fieldValue: GsStyle) => {
onFieldValueChange(fieldValue, 'style');
};
const onAttributionChange = (fieldValue: string) => {
onFieldValueChange(fieldValue, 'attribution');
};
const onCloseClick = () => {
onClose();
};
const onSaveClick = () => {
const baseConfs: BaseLayerConf = {
title: currentLayerConf.title,
url: currentLayerConf.url,
type: currentLayerConf.type,
attribution: currentLayerConf.attribution,
};
let conf: LayerConf;
if (isWmsLayerConf(currentLayerConf)) {
conf = {
...baseConfs,
version: currentLayerConf.version,
type: currentLayerConf.type,
layersParam: currentLayerConf.layersParam,
};
} else if (isXyzLayerConf(currentLayerConf)) {
conf = {
...baseConfs,
type: currentLayerConf.type,
};
} else {
conf = {
...baseConfs,
type: currentLayerConf.type,
version: currentLayerConf.version,
typeName: currentLayerConf.typeName,
maxFeatures: currentLayerConf.maxFeatures,
style: currentLayerConf.style,
};
}
onSave(conf);
};
useEffect(() => {
if (
!isWfsLayerConf(currentLayerConf) ||
!hasAllRequiredWfsParams(currentLayerConf)
) {
setGeoStylerData(undefined);
return undefined;
}
const readWfsData = async (conf: WfsLayerConf) => {
const wfsParser = new WfsDataParser();
try {
let requestParams: RequestParams1_1_0 | RequestParams2_0_0 = {} as
| RequestParams1_1_0
| RequestParams2_0_0;
if (conf.version.startsWith('1.')) {
requestParams = {
version: conf.version as RequestParams1_1_0['version'],
maxFeatures: conf.maxFeatures,
typeName: conf.typeName,
};
}
if (conf.version.startsWith('2.')) {
requestParams = {
version: conf.version as RequestParams2_0_0['version'],
count: conf.maxFeatures,
typeNames: conf.typeName,
};
}
const gsData = await wfsParser.readData({
url: conf.url,
requestParams,
});
setGeoStylerData(gsData);
} catch {
console.warn('Could not read geostyler data');
setGeoStylerData(undefined);
}
};
// debounce function
const timer = setTimeout(() => readWfsData(currentLayerConf), 500);
return () => {
clearTimeout(timer);
};
}, [currentLayerConf]);
const layerTabLabel = t('Layer');
const styleTabLabel = t('Style');
const layerTypeLabel = t('Layer type');
const layerTypeDescription = t('The type of the layer');
const serviceVersionLabel = t('Service version');
const serviceVersionDescription = t('The version of the service');
const layersParamLabel = t('Layer Name');
const layersParamDescription = t(
'The name of the layer as described in GetCapabilities',
);
const layersParamPlaceholder = t('Layer Name');
const layerTitleLabel = t('Layer title');
const layerTitleDescription = t('The visible title of the layer');
const layerTitlePlaceholder = t('Insert Layer title');
const layerUrlLabel = t('Layer URL');
const layerUrlDescription = t('The service url of the layer');
const layerUrlPlaceholder = t('Insert Layer URL');
const maxFeaturesLabel = t('Max. features');
const maxFeaturesDescription = t(
'Maximum number of features to fetch from service',
);
const maxFeaturesPlaceholder = t('10000');
const attributionLabel = t('Attribution');
const attributionDescription = t('The layer attribution');
const attributionPlaceholder = t('© Layer attribution');
const wmsVersionOptions: { value: any; label: string }[] =
serviceVersions.WMS.map(version => ({ value: version, label: version }));
const wfsVersionOptions: { value: any; label: string }[] =
serviceVersions.WFS.map(version => ({ value: version, label: version }));
return (
<div>
<Form key={JSON.stringify(formKey)}>
<Tabs
defaultActiveKey={LAYER_CONFIG_TABS.LAYER}
items={[
{
key: LAYER_CONFIG_TABS.LAYER,
label: layerTabLabel,
children: (
<>
<StyledControlFormItem
controlType="Input"
label={layerUrlLabel}
description={layerUrlDescription}
placeholder={layerUrlPlaceholder}
value={currentLayerConf.url}
name="url"
onChange={onLayerUrlChange}
/>
<StyledControlFormItem
controlType="Select"
label={layerTypeLabel}
description={layerTypeDescription}
options={[
{ value: 'WMS', label: t('WMS') },
{ value: 'WFS', label: t('WFS') },
{ value: 'XYZ', label: t('XYZ') },
]}
value={currentLayerConf.type}
defaultValue={currentLayerConf.type}
name="type"
onChange={onLayerTypeChange}
/>
{isWmsLayerConf(currentLayerConf) && (
<StyledControlFormItem
controlType="Select"
label={serviceVersionLabel}
description={serviceVersionDescription}
options={wmsVersionOptions}
value={wmsVersion}
defaultValue={wmsVersionOptions[0].value as string}
name="wmsVersion"
onChange={onWmsVersionChange}
/>
)}
{isWfsLayerConf(currentLayerConf) && (
<StyledControlFormItem
controlType="Select"
label={serviceVersionLabel}
description={serviceVersionDescription}
options={wfsVersionOptions}
value={wfsVersion}
defaultValue={wfsVersionOptions[0].value as string}
name="wfsVersion"
onChange={onWfsVersionChange}
/>
)}
{isWmsLayerConf(currentLayerConf) && (
<StyledControlFormItem
controlType="Input"
label={layersParamLabel}
description={layersParamDescription}
placeholder={layersParamPlaceholder}
value={currentLayerConf.layersParam}
name="layersParam"
onChange={onLayersParamChange}
/>
)}
{isWfsLayerConf(currentLayerConf) && (
<StyledControlFormItem
controlType="Input"
label={layersParamLabel}
description={layersParamDescription}
placeholder={layersParamPlaceholder}
value={currentLayerConf.typeName}
name="typeName"
onChange={onTypeNameChange}
/>
)}
<StyledControlFormItem
controlType="Input"
label={layerTitleLabel}
description={layerTitleDescription}
placeholder={layerTitlePlaceholder}
value={currentLayerConf.title}
name="title"
onChange={onLayerTitleChange}
/>
{isWfsLayerConf(currentLayerConf) && (
<StyledControlNumberFormItem
controlType="InputNumber"
label={maxFeaturesLabel}
description={maxFeaturesDescription}
placeholder={maxFeaturesPlaceholder}
value={currentLayerConf.maxFeatures}
name="maxFeatures"
onChange={onMaxFeaturesChange}
/>
)}
<StyledControlFormItem
controlType="Input"
label={attributionLabel}
description={attributionDescription}
placeholder={attributionPlaceholder}
value={currentLayerConf.attribution}
name="attribution"
onChange={onAttributionChange}
/>
</>
),
},
{
key: LAYER_CONFIG_TABS.GEOSTYLER,
label: styleTabLabel,
disabled: !isWfsLayerConf(currentLayerConf),
children: isWfsLayerConf(currentLayerConf) && (
<StyledGeoStyler
style={currentLayerConf.style}
onStyleChange={onStyleChange}
data={geostylerData}
/>
),
},
]}
/>
<StyledButtonContainer>
<StyledCloseButton type="default" onClick={onCloseClick}>
{t('Close')}
</StyledCloseButton>
<StyledSaveButton type="primary" onClick={onSaveClick}>
{t('Save')}
</StyledSaveButton>
</StyledButtonContainer>
</Form>
</div>
);
};
export default LayerConfigsPopoverContent;