Skip to content

Commit

Permalink
feat: refactor code into useDraw hook
Browse files Browse the repository at this point in the history
  • Loading branch information
simonseyock committed May 6, 2024
1 parent 4e25f56 commit cd657fe
Showing 1 changed file with 40 additions and 155 deletions.
195 changes: 40 additions & 155 deletions src/Button/DrawButton/DrawButton.tsx
Original file line number Diff line number Diff line change
@@ -1,45 +1,20 @@
import useMap from '@terrestris/react-util/dist/Hooks/useMap/useMap';
import { DigitizeUtil } from '@terrestris/react-util/dist/Util/DigitizeUtil';
import { EventsKey } from 'ol/events';
import * as OlEventConditions from 'ol/events/condition';
import { useDraw, UseDrawProps } from '@terrestris/react-util/dist/Hooks/useDraw/useDraw';
import OlFeature from 'ol/Feature';
import OlGeometry from 'ol/geom/Geometry';
import OlInteractionDraw, { createBox, DrawEvent as OlDrawEvent, Options as OlDrawOptions } from 'ol/interaction/Draw';
import OlVectorLayer from 'ol/layer/Vector';
import { unByKey } from 'ol/Observable';
import OlVectorSource from 'ol/source/Vector';
import { StyleLike as OlStyleLike } from 'ol/style/Style';
import {
DrawEvent
} from 'ol/interaction/Draw';
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 ToggleButton, { ToggleButtonProps } from '../ToggleButton/ToggleButton';

type DrawType = 'Point' | 'LineString' | 'Polygon' | 'Circle' | 'Rectangle' | 'Text';
type ButtonDrawType = 'Point' | 'LineString' | 'Polygon' | 'Circle' | 'Rectangle' | 'Text';

interface OwnProps {
/**
* Whether the line, point, polygon, circle, rectangle or text shape should
* be drawn.
*/
drawType: DrawType;
/**
* Style object / style function for drawn feature.
*/
drawStyle?: OlStyleLike;
/**
* Listener function for the 'drawend' event of an ol.interaction.Draw.
* See https://openlayers.org/en/latest/apidoc/module-ol_interaction_Draw-DrawEvent.html
* for more information.
*/
onDrawEnd?: (event: OlDrawEvent) => void;
/**
* Listener function for the 'drawstart' event of an ol.interaction.Draw.
* See https://openlayers.org/en/latest/apidoc/module-ol_interaction_Draw-DrawEvent.html
* for more information.
*/
onDrawStart?: (event: OlDrawEvent) => void;
drawType: ButtonDrawType;
/**
* Callback function that will be called when the ok-button of the modal was clicked
*/
Expand All @@ -54,11 +29,6 @@ interface OwnProps {
* If exceeded label will be divided into multiple lines. Optional.
*/
maxLabelLineLength?: number;
/**
* The vector layer which will be used for digitize features.
* The standard digitizeLayer can be retrieved via `DigitizeUtil.getDigitizeLayer(map)`.
*/
digitizeLayer?: OlVectorLayer<OlVectorSource>;
/**
* Title for modal used for input of labels for digitize features.
*/
Expand All @@ -71,19 +41,9 @@ interface OwnProps {
* Text string for `Cancel` button of the modal.
*/
modalPromptCancelButtonText?: string;
/**
* Additional configuration object to apply to the ol.interaction.Draw.
* See https://openlayers.org/en/latest/apidoc/module-ol_interaction_Draw-Draw.html
* for more information
*
* Note: The keys source, type, geometryFunction, style and freehandCondition
* are handled internally and shouldn't be overwritten without any
* specific cause.
*/
drawInteractionConfig?: Omit<OlDrawOptions, 'source'|'type'|'geometryFunction'|'style'|'freehandCondition'>;
}

export type DrawButtonProps = OwnProps & Partial<ToggleButtonProps>;
export type DrawButtonProps = OwnProps & Omit<UseDrawProps, 'drawType'> & Partial<ToggleButtonProps>;

/**
* The className added to this component.
Expand All @@ -110,108 +70,28 @@ const DrawButton: React.FC<DrawButtonProps> = ({
pressed,
...passThroughProps
}) => {

const [drawInteraction, setDrawInteraction] = useState<OlInteractionDraw>();
const [layer, setLayer] = useState<OlVectorLayer<OlVectorSource> | null>(null);

/**
* Currently drawn feature which should be represented as label or post-it.
*/
const [digitizeTextFeature, setDigitizeTextFeature] = useState<OlFeature<OlGeometry> | null>(null);

const map = useMap();

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

if (digitizeLayer) {
setLayer(digitizeLayer);
} else {
setLayer(DigitizeUtil.getDigitizeLayer(map));
}
}, [map, digitizeLayer]);

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

let geometryFunction;
let type: 'Point' | 'Circle' | 'LineString' | 'Polygon';

if (drawType === 'Rectangle') {
geometryFunction = createBox();
type = 'Circle';
} else if (drawType === 'Text') {
type = 'Point';
} else {
type = drawType;
}

const newInteraction = new OlInteractionDraw({
source: layer.getSource() || undefined,
type: type,
geometryFunction: geometryFunction,
style: drawStyle ?? DigitizeUtil.defaultDigitizeStyleFunction,
freehandCondition: OlEventConditions.never,
...(drawInteractionConfig ?? {})
});

newInteraction.set('name', `react-geo-draw-interaction-${drawType}`);

newInteraction.setActive(false);

map.addInteraction(newInteraction);

setDrawInteraction(newInteraction);

let key: EventsKey;

const onDrawEndInternal = useCallback((evt: DrawEvent) => {
if (drawType === 'Text') {
key = newInteraction.on('drawend', evt => {
evt.feature.set('isLabel', true);
setDigitizeTextFeature(evt.feature);
});
}

return () => {
unByKey(key);
map.removeInteraction(newInteraction);
};
}, [drawType, layer, drawInteractionConfig, drawStyle, map]);

useEffect(() => {
if (!drawInteraction) {
return undefined;
evt.feature.set('isLabel', true);
setDigitizeTextFeature(evt.feature);
}

const endKey = drawInteraction.on('drawend', (evt) => {
onDrawEnd?.(evt);
});

const startKey = drawInteraction.on('drawstart', (evt) => {
onDrawStart?.(evt);
});

return () => {
unByKey(startKey);
unByKey(endKey);
};
}, [drawInteraction, onDrawStart, onDrawEnd]);

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

drawInteraction.setActive(!!pressed);
}, [drawInteraction, pressed]);

if (!drawInteraction || !layer) {
return null;
}
onDrawEnd?.(evt);
}, [drawType, onDrawEnd]);

useDraw({
onDrawEnd: onDrawEndInternal,
digitizeLayer,
drawInteractionConfig,
drawStyle,
drawType: drawType === 'Text' ? 'Point' : drawType,
onDrawStart,
active: !!pressed
});

const finalClassName = className
? `${defaultClassName} ${className}`
Expand All @@ -228,19 +108,21 @@ const DrawButton: React.FC<DrawButtonProps> = ({

const onModalLabelCancelInternal = () => {
onModalLabelCancel?.();
layer.getSource()?.removeFeature(digitizeTextFeature);
digitizeLayer?.getSource()?.removeFeature(digitizeTextFeature);
setDigitizeTextFeature(null);
};

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

return (
Expand All @@ -250,8 +132,11 @@ const DrawButton: React.FC<DrawButtonProps> = ({
pressed={pressed}
{...passThroughProps}
/>
{modal}
</span>);
{
modal
}
</span>
);
};

export default DrawButton;

0 comments on commit cd657fe

Please sign in to comment.