Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Attributes to Layer #660

Merged
merged 23 commits into from
Jan 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
d2f00ac
feat: adds drawer with basic functionality
FritzHoing Jan 26, 2023
79095cc
feat: zwischenschritt
FritzHoing Feb 27, 2023
d515e37
feat: set all properties
FritzHoing Feb 27, 2023
c851a93
feat: make code work
FritzHoing Feb 27, 2023
f887647
Merge branch 'main' of github.com:terrestris/shogun-gis-client into a…
FritzHoing Jun 16, 2023
4d9fcd2
fix: wip
FritzHoing Jul 20, 2023
a8c7795
Merge branch 'main' of github.com:terrestris/shogun-gis-client into a…
FritzHoing Jul 20, 2023
0ffbb30
Merge branch 'main' of github.com:terrestris/shogun-gis-client into a…
FritzHoing Jul 31, 2023
75e509a
Merge branch 'main' of github.com:terrestris/shogun-gis-client into a…
FritzHoing Sep 4, 2023
37c5a8e
fix: fix lifecycle problem
FritzHoing Sep 5, 2023
6ab6748
feat: attribution drawer
FritzHoing Sep 5, 2023
98805d8
fix: remove unneeded import
FritzHoing Sep 5, 2023
a30801b
feat: adds delete property functionality
FritzHoing Sep 5, 2023
820a818
fix: adds ts-ingore
FritzHoing Sep 5, 2023
0bc2eac
fix: lint
FritzHoing Sep 5, 2023
5afa63e
feat: adds translation and ts-types
FritzHoing Sep 6, 2023
3f4880a
fix: fix property handling
FritzHoing Dec 12, 2023
b1ee096
fix: rebase
FritzHoing Jan 26, 2024
1bbe300
fix: adds translation and linting
FritzHoing Jan 26, 2024
9d3dc18
fix: rename interaction
FritzHoing Jan 26, 2024
9f12963
fix: undo select-interaction changes
FritzHoing Jan 26, 2024
a4db5e3
fix: improve code
FritzHoing Jan 26, 2024
ea1c3c8
fix: improve translation
FritzHoing Jan 26, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.attribute-row {
display: flex;
}
72 changes: 72 additions & 0 deletions src/components/ToolMenu/Draw/Attributions/AttributionRow/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import React from 'react';

import {
Form, Input
} from 'antd';

import './index.less';
import { useTranslation } from 'react-i18next';

export type AttributionRowProps = {
keyName: string;
onChange: (evt: React.ChangeEvent<HTMLInputElement>) => void;
};

export type InputFields = {
[fields: string]: {
name: string;
value: any;
};
};

const AttributionRow: React.FC<AttributionRowProps> = ({
keyName,
onChange
}) => {

const {
t
} = useTranslation();

return (
<>
<Form.Item
name={['fields', keyName, 'name']}
rules={[{
required: true,
message: t('AttributionRow.missingKey')
}, ({ getFieldsValue }) => ({
validator(_, value: string) {
const fields: InputFields = getFieldsValue(true);
const filtered = Object.entries(fields.fields).filter(([key, val]) => val.name === value);

if (filtered.length > 1) {
return Promise.reject(new Error(t('AttributionRow.keyInUse')));
}

return Promise.resolve();
}
})]}
>
<Input
placeholder={t('AttributionRow.keyPlaceholder')}
onChange={onChange}
/>
</Form.Item>
<Form.Item
name={['fields', keyName, 'value']}
rules={[{
required: true,
message: t('AttributionRow.missingValue')
}]}
>
<Input
placeholder={t('AttributionRow.valuePlaceholder')}
onChange={onChange}
/>
</Form.Item>
</>
);
};

export default AttributionRow;
12 changes: 12 additions & 0 deletions src/components/ToolMenu/Draw/Attributions/index.less
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
.attribution-drawer {
position: absolute;
top: var(--headerHeight);
bottom: var(--footerHeight);
z-index: 499;

.anticon.anticon-minus-circle {
padding-bottom: 25px;
padding-left: 5px;
}
}

209 changes: 209 additions & 0 deletions src/components/ToolMenu/Draw/Attributions/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
import React, {
useEffect, useState
} from 'react';

import {
MinusCircleOutlined, PlusOutlined
} from '@ant-design/icons';
import {
Button, Drawer, DrawerProps, Form, Row
} from 'antd';
import _cloneDeep from 'lodash/cloneDeep';
import OlFeature from 'ol/Feature';
import Select from 'ol/interaction/Select';

import { useTranslation } from 'react-i18next';

import {
useMap
} from '@terrestris/react-geo/dist/Hook/useMap';

import './index.less';
import AttributionRow, { InputFields } from './AttributionRow';

export interface AttributionDrawerProps extends DrawerProps {
onCustomClose?: (open: boolean) => void;
}

const AttributionDrawer: React.FC<AttributionDrawerProps> = ({
onCustomClose,
onClose,
...passThroughProps
}) => {
const [selectedFeature, setSelectedFeature] = useState<OlFeature>();
const [isFormValid, setIsFormIsValid] = useState(true);
const [currentProperties, setCurrentProperties] = useState<Record<string, any>>({});

const [form] = Form.useForm();

const map = useMap();

const {
t
} = useTranslation();

useEffect(() => {
const properties = selectedFeature?.getProperties();

setCurrentProperties({ ...properties });

const fs: any = {};
if (properties) {
Object.entries(properties).forEach(([key, value]) => {
fs[key] = {
name: key,
value: value
};
});
}

form.setFieldValue('fields', {});
form.setFieldsValue({
fields: fs
});
}, [selectedFeature, form]);

// todo revisit react-geo to make name of the slect-interaction configurable
const selectInteraction = map?.getInteractions().getArray().filter(interaction => {
if (interaction.get('active') === true && interaction.get('name') === 'react-geo-select-interaction') {
FritzHoing marked this conversation as resolved.
Show resolved Hide resolved
return true;
} else {
return false;
}
})[0] as Select;

if (selectInteraction) {
selectInteraction.on('select', () => {
setSelectedFeature(selectInteraction.getFeatures().getArray()[0]);
});
}

const handleClose = () => {
if (onCustomClose) {
onCustomClose(false);
}
};

const onFinish = (input: InputFields) => {
if (!selectedFeature) {
return;
}

if (Object.keys(currentProperties).length > 0) {
Object.entries(input.fields).forEach(([key, value]) => {
selectedFeature.set(value.name, value.value);
});
} else {
selectedFeature.set('', '');
}
};

const onPropertyAdd = () => {
const newProps = {...currentProperties};
newProps[''] = '';
setCurrentProperties(newProps);
};

const remove = (keyToRemove: string) => {
const updatedProperties = {...currentProperties};

delete updatedProperties[keyToRemove];

selectedFeature?.unset(keyToRemove);

setCurrentProperties(updatedProperties);
};

const onKeyChange = async () => {
try {
await form.validateFields();
setIsFormIsValid(true);
} catch (error) {
setIsFormIsValid(false);
}
};

const getFormItems = () => {
const filteredProperties = currentProperties;

if (filteredProperties.geometry) {
delete filteredProperties.geometry;
}

return Object.entries(filteredProperties).map(([key, value]) => {
return (
<div
key={key}
className='attribute-row'
>
<AttributionRow
keyName={key}
key={key}
onChange={onKeyChange}
/>
<MinusCircleOutlined
onClick={() => remove(key)}
/>
</div>
);
});
};

return (
<>
<Drawer
title={t('Attribution.title')}
className='attribution-drawer'
placement="right"
mask={false}
maskClosable={false}
onClose={handleClose}
{...passThroughProps}
>
<>
{!selectedFeature &&
<>
{t('Attribution.select')}
</>
}
</>
<Row>
<Form
name="dynamic_form_nest_item"
onFinish={onFinish}
style={{ maxWidth: 600 }}
autoComplete="off"
form={form}
>
{
getFormItems()
}
<Form.Item>
{selectedFeature ?
<Button
type="dashed"
onClick={onPropertyAdd}
block
icon={<PlusOutlined />}
>
{t('Attribution.add')}
</Button> :
<></>}
</Form.Item>
<Form.Item>
<Button
type="primary"
htmlType="submit"
disabled={!isFormValid}
>
{t('Attribution.submit')}
</Button>
</Form.Item>
</Form>
</Row>
</Drawer>
</>
);
};

export default AttributionDrawer;
Loading
Loading