Skip to content

Commit

Permalink
refactor: modify button uses useModify hook
Browse files Browse the repository at this point in the history
  • Loading branch information
simonseyock authored and dnlkoch committed Feb 20, 2024
1 parent a0f50af commit 5664810
Showing 1 changed file with 40 additions and 192 deletions.
232 changes: 40 additions & 192 deletions src/Button/ModifyButton/ModifyButton.tsx
Original file line number Diff line number Diff line change
@@ -1,76 +1,19 @@
import useMap from '@terrestris/react-util/dist/Hooks/useMap/useMap';
import { DigitizeUtil } from '@terrestris/react-util/dist/Util/DigitizeUtil';
import OlCollection from 'ol/Collection';
import { singleClick } from 'ol/events/condition';
import { useModify, UseModifyProps } from '@terrestris/react-util/dist/Hooks/useModify/useModify';
import OlFeature from 'ol/Feature';
import OlGeometry from 'ol/geom/Geometry';
import Modify, { ModifyEvent, Options as ModifyOptions } from 'ol/interaction/Modify';
import { SelectEvent as OlSelectEvent } from 'ol/interaction/Select';
import Translate, { Options as TranslateOptions, TranslateEvent } from 'ol/interaction/Translate';
import OlVectorLayer from 'ol/layer/Vector';
import { unByKey } from 'ol/Observable';
import OlVectorSource from 'ol/source/Vector';
import * as React from 'react';
import { ReactNode, useEffect, useState } from 'react';
import {ReactNode, useCallback, useState} from 'react';

import { CSS_PREFIX } from '../../constants';
import { FeatureLabelModal } from '../../FeatureLabelModal/FeatureLabelModal';
import SelectFeaturesButton, { SelectFeaturesButtonProps } from '../SelectFeaturesButton/SelectFeaturesButton';
import ToggleButton, {ToggleButtonProps} from '../ToggleButton/ToggleButton';

interface OwnProps {
/**
* Additional configuration object to apply to the ol.interaction.Modify.
* See https://openlayers.org/en/latest/apidoc/module-ol_interaction_Modify-Modify.html
* for more information
*
* Note: The keys features, deleteCondition and style are handled internally
* and shouldn't be overwritten without any specific cause.
* The className which should be added.
*/
modifyInteractionConfig?: Omit<ModifyOptions, 'features'|'source'|'deleteCondition'|'style'>;
/**
* Additional configuration object to apply to the ol.interaction.Translate.
* See https://openlayers.org/en/latest/apidoc/module-ol_interaction_Translate-Translate.html
* for more information
*
* Note: The key feature is handled internally and shouldn't be overwritten
* without any specific cause.
*/
translateInteractionConfig?: Omit<TranslateOptions, 'features'|'layers'>;
/**
* The vector layer which will be used for digitize features.
* The standard digitizeLayer can be retrieved via `DigitizeUtil.getDigitizeLayer(map)`.
*/
digitizeLayer?: OlVectorLayer<OlVectorSource<OlFeature>>;
/**
* Listener function for the 'modifystart' event of an ol.interaction.Modify.
* See https://openlayers.org/en/latest/apidoc/module-ol_interaction_Modify-ModifyEvent.html
* for more information.
*/
onModifyStart?: (event: ModifyEvent) => void;
/**
* Listener function for the 'modifyend' event of an ol.interaction.Modify.
* See https://openlayers.org/en/latest/apidoc/module-ol_interaction_Modify-ModifyEvent.html
* for more information.
*/
onModifyEnd?: (event: ModifyEvent) => void;
/**
* Listener function for the 'translatestart' event of an ol.interaction.Translate.
* See https://openlayers.org/en/latest/apidoc/module-ol_interaction_Translate-TranslateEvent.html
* for more information.
*/
onTranslateStart?: (event: TranslateEvent) => void;
/**
* Listener function for the 'translateend' event of an ol.interaction.Translate.
* See https://openlayers.org/en/latest/apidoc/module-ol_interaction_Translate-TranslateEvent.html
* for more information.
*/
onTranslateEnd?: (event: TranslateEvent) => void;
/**
* Listener function for the 'translating' event of an ol.interaction.Translate.
* See https://openlayers.org/en/latest/apidoc/module-ol_interaction_Translate-TranslateEvent.html
* for more information.
*/
onTranslating?: (event: TranslateEvent) => void;
className?: string;
/**
* Callback function that will be called when the ok-button of the modal was clicked
*/
Expand Down Expand Up @@ -103,8 +46,7 @@ interface OwnProps {
editLabel?: boolean;
}

export type ModifyButtonProps = OwnProps & Omit<SelectFeaturesButtonProps,
'layers'|'onFeatureSelect'|'featuresCollection'>;
export type ModifyButtonProps = OwnProps & Omit<UseModifyProps, ''> & Partial<ToggleButtonProps>;

/**
* The className added to this component.
Expand All @@ -113,13 +55,15 @@ const defaultClassName = `${CSS_PREFIX}modifybutton`;

export const ModifyButton: React.FC<ModifyButtonProps> = ({
className,
hitTolerance = 5,
onModifyStart,
onModifyEnd,
onTranslateStart,
onTranslateEnd,
onTranslating,
digitizeLayer,
selectStyle,
selectInteractionConfig,
modifyInteractionConfig,
translateInteractionConfig,
onModalLabelOk,
Expand All @@ -132,136 +76,36 @@ export const ModifyButton: React.FC<ModifyButtonProps> = ({
pressed,
...passThroughProps
}) => {
const [layers, setLayers] = useState<[OlVectorLayer<OlVectorSource<OlFeature>>]|null>(null);
const [modifyInteraction, setModifyInteraction] = useState<Modify|null>(null);
const [translateInteraction, setTranslateInteraction] = useState<Translate|null>(null);
const [features, setFeatures] = useState<OlCollection<OlFeature<OlGeometry>>|null>(null);

const map = useMap();

const [editLabelFeature, setEditLabelFeature] = useState<OlFeature<OlGeometry>|null>(null);

useEffect(() => {
if (!map) {
return;
}

setLayers([digitizeLayer ?? DigitizeUtil.getDigitizeLayer(map)]);
setFeatures(new OlCollection());
}, [map, digitizeLayer]);

useEffect(() => {
if (!map || !features) {
return undefined;
}

const newTranslateInteraction = new Translate({
features,
...translateInteractionConfig
});
newTranslateInteraction.set('name', 'react-geo-translate-interaction');
newTranslateInteraction.setActive(false);

map.addInteraction(newTranslateInteraction);
setTranslateInteraction(newTranslateInteraction);

const newModifyInteraction = new Modify({
features,
deleteCondition: singleClick,
style: selectStyle ?? DigitizeUtil.DEFAULT_SELECT_STYLE,
...modifyInteractionConfig
});
newModifyInteraction.set('name', 'react-geo-modify-interaction');
newModifyInteraction.setActive(false);

map.addInteraction(newModifyInteraction);
setModifyInteraction(newModifyInteraction);

return () => {
map.removeInteraction(newModifyInteraction);
map.removeInteraction(newTranslateInteraction);
};
}, [selectStyle, modifyInteractionConfig, translateInteractionConfig, features, map]);

useEffect(() => {
if (!modifyInteraction) {
return undefined;
}

const startKey = modifyInteraction.on('modifystart', e => {
onModifyStart?.(e);
});

const endKey = modifyInteraction.on('modifyend', e => {
onModifyEnd?.(e);
});

return () => {
unByKey(startKey);
unByKey(endKey);
};
}, [modifyInteraction, onModifyStart, onModifyEnd]);

useEffect(() => {
if (!translateInteraction) {
return undefined;
}

const startKey = translateInteraction.on('translatestart', e => {
onTranslateStart?.(e);
});

const endKey = translateInteraction.on('translateend', e => {
onTranslateEnd?.(e);
});

const translatingKey = translateInteraction.on('translating', e => {
onTranslating?.(e);
});

return () => {
unByKey(startKey);
unByKey(endKey);
unByKey(translatingKey);
};
}, [translateInteraction, onTranslateStart, onTranslateEnd, onTranslating]);

useEffect(() => {
if (!modifyInteraction || !translateInteraction) {
return;
}

modifyInteraction.setActive(!!pressed);
translateInteraction.setActive(!!pressed);
}, [modifyInteraction, translateInteraction, pressed]);

if (!layers || !features || !modifyInteraction || !translateInteraction) {
return null;
}

const onFeatureSelect = (event: OlSelectEvent) => {
const onFeatureSelect = useCallback((event: OlSelectEvent) => {
if (editLabel) {
const labeled = event.selected.find(f => f.get('isLabel'));
setEditLabelFeature(labeled || null);
}
};
}, [editLabel]);

useModify({
selectStyle,
selectInteractionConfig,
digitizeLayer,
onModifyStart,
onModifyEnd,
onTranslateStart,
onTranslateEnd,
onTranslating,
active: !!pressed,
modifyInteractionConfig,
translateInteractionConfig,
onFeatureSelect,
hitTolerance
});

const finalClassName = className
? `${defaultClassName} ${className}`
: defaultClassName;

const button = (
<SelectFeaturesButton
layers={layers}
selectStyle={selectStyle}
className={finalClassName}
featuresCollection={features}
clearAfterSelect={false}
onFeatureSelect={onFeatureSelect}
pressed={pressed}
{...passThroughProps}
/>
);
const btnWrapperClass = `${CSS_PREFIX}digitize-button-wrapper`;

let modal: ReactNode = null;
if (editLabelFeature) {
Expand All @@ -277,25 +121,29 @@ export const ModifyButton: React.FC<ModifyButtonProps> = ({

modal = (
<FeatureLabelModal
feature={editLabelFeature}
onOk={onModalLabelOkInternal}
onCancel={onModalLabelCancelInternal}
title={modalPromptTitle}
okText={modalPromptOkButtonText}
cancelText={modalPromptCancelButtonText}
maxLabelLineLength={maxLabelLineLength}
feature={editLabelFeature}
/>
);
}

if (!editLabel) {
return button;
} else {
return <>
{button}
{modal}
</>;
}
return (
<span className={btnWrapperClass}>
<ToggleButton
pressed={pressed}
className={finalClassName}
{...passThroughProps}
/>
{
modal
}
</span>
);
};

export default ModifyButton;

0 comments on commit 5664810

Please sign in to comment.