Skip to content
Snippets Groups Projects
Commit 922da3fc authored by Miłosz Grocholewski's avatar Miłosz Grocholewski
Browse files

feat(layer-image): add zIndex editing for layer image objects

parent d9f08711
No related branches found
No related tags found
1 merge request!361feat(layer-image): add zIndex editing for layer image objects
Showing
with 191 additions and 33 deletions
...@@ -140,7 +140,7 @@ describe('LayerImageObjectEditFactoryModal - component', () => { ...@@ -140,7 +140,7 @@ describe('LayerImageObjectEditFactoryModal - component', () => {
}; };
const getGlyphDataMock = jest.fn(() => glyphData); const getGlyphDataMock = jest.fn(() => glyphData);
jest.spyOn(layerObjectFeature, 'get').mockImplementation(key => { jest.spyOn(layerObjectFeature, 'get').mockImplementation(key => {
if (key === 'setGlyph') return (): void => {}; if (key === 'update') return (): void => {};
if (key === 'getGlyphData') return getGlyphDataMock; if (key === 'getGlyphData') return getGlyphDataMock;
return undefined; return undefined;
}); });
......
...@@ -14,7 +14,8 @@ import { showToast } from '@/utils/showToast'; ...@@ -14,7 +14,8 @@ import { showToast } from '@/utils/showToast';
import { closeModal } from '@/redux/modal/modal.slice'; import { closeModal } from '@/redux/modal/modal.slice';
import { SerializedError } from '@reduxjs/toolkit'; import { SerializedError } from '@reduxjs/toolkit';
import { useMapInstance } from '@/utils/context/mapInstanceContext'; import { useMapInstance } from '@/utils/context/mapInstanceContext';
import VectorSource from 'ol/source/Vector'; import updateGlyph from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/Glyph/updateGlyph';
import { mapEditToolsSetLayerObject } from '@/redux/mapEditTools/mapEditTools.slice';
export const LayerImageObjectEditFactoryModal: React.FC = () => { export const LayerImageObjectEditFactoryModal: React.FC = () => {
const layerImageObject = useAppSelector(mapEditToolsLayerImageObjectSelector); const layerImageObject = useAppSelector(mapEditToolsLayerImageObjectSelector);
...@@ -55,19 +56,8 @@ export const LayerImageObjectEditFactoryModal: React.FC = () => { ...@@ -55,19 +56,8 @@ export const LayerImageObjectEditFactoryModal: React.FC = () => {
).unwrap(); ).unwrap();
if (layerImage) { if (layerImage) {
dispatch(layerUpdateImage({ modelId: currentModelId, layerId: activeLayer, layerImage })); dispatch(layerUpdateImage({ modelId: currentModelId, layerId: activeLayer, layerImage }));
mapInstance?.getAllLayers().forEach(layer => { dispatch(mapEditToolsSetLayerObject(layerImage));
if (layer.get('id') === activeLayer) { updateGlyph(mapInstance, activeLayer, layerImage);
const source = layer.getSource();
if (source instanceof VectorSource) {
const feature = source.getFeatureById(layerImage.id);
const setGlyph = feature?.get('setGlyph');
if (setGlyph && setGlyph instanceof Function) {
setGlyph(layerImage.glyph);
feature.changed();
}
}
}
});
} }
showToast({ showToast({
type: 'success', type: 'success',
......
...@@ -4,6 +4,13 @@ import { mapEditToolsLayerImageObjectSelector } from '@/redux/mapEditTools/mapEd ...@@ -4,6 +4,13 @@ import { mapEditToolsLayerImageObjectSelector } from '@/redux/mapEditTools/mapEd
import { useAppDispatch } from '@/redux/hooks/useAppDispatch'; import { useAppDispatch } from '@/redux/hooks/useAppDispatch';
import { MapDrawActionsButton } from '@/components/Map/MapDrawActions/MapDrawActionsButton.component'; import { MapDrawActionsButton } from '@/components/Map/MapDrawActions/MapDrawActionsButton.component';
import { openLayerImageObjectEditFactoryModal } from '@/redux/modal/modal.slice'; import { openLayerImageObjectEditFactoryModal } from '@/redux/modal/modal.slice';
import { updateLayerImageObject } from '@/redux/layers/layers.thunks';
import { mapModelIdSelector } from '@/redux/map/map.selectors';
import { layersActiveLayerSelector } from '@/redux/layers/layers.selectors';
import { layerUpdateImage } from '@/redux/layers/layers.slice';
import { useMapInstance } from '@/utils/context/mapInstanceContext';
import { mapEditToolsSetLayerObject } from '@/redux/mapEditTools/mapEditTools.slice';
import updateGlyph from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/Glyph/updateGlyph';
type MapDrawEditActionsComponentProps = { type MapDrawEditActionsComponentProps = {
toggleMapEditAction: () => void; toggleMapEditAction: () => void;
...@@ -14,13 +21,35 @@ export const MapDrawEditActionsComponent = ({ ...@@ -14,13 +21,35 @@ export const MapDrawEditActionsComponent = ({
toggleMapEditAction, toggleMapEditAction,
isActive, isActive,
}: MapDrawEditActionsComponentProps): React.JSX.Element => { }: MapDrawEditActionsComponentProps): React.JSX.Element => {
const currentModelId = useAppSelector(mapModelIdSelector);
const activeLayer = useAppSelector(layersActiveLayerSelector);
const layerImageObject = useAppSelector(mapEditToolsLayerImageObjectSelector); const layerImageObject = useAppSelector(mapEditToolsLayerImageObjectSelector);
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const { mapInstance } = useMapInstance();
const editMapObject = (): void => { const editMapObject = (): void => {
dispatch(openLayerImageObjectEditFactoryModal()); dispatch(openLayerImageObjectEditFactoryModal());
}; };
const updateZIndex = async (value: number): Promise<void> => {
if (!activeLayer || !layerImageObject) {
return;
}
const layerImage = await dispatch(
updateLayerImageObject({
modelId: currentModelId,
layerId: activeLayer,
...layerImageObject,
z: layerImageObject.z + value,
}),
).unwrap();
if (layerImage) {
dispatch(layerUpdateImage({ modelId: currentModelId, layerId: activeLayer, layerImage }));
dispatch(mapEditToolsSetLayerObject(layerImage));
updateGlyph(mapInstance, activeLayer, layerImage);
}
};
return ( return (
<div className="flex flex-row-reverse gap-4"> <div className="flex flex-row-reverse gap-4">
<MapDrawActionsButton <MapDrawActionsButton
...@@ -43,6 +72,18 @@ export const MapDrawEditActionsComponent = ({ ...@@ -43,6 +72,18 @@ export const MapDrawEditActionsComponent = ({
icon="trash" icon="trash"
title="Remove image" title="Remove image"
/> />
<MapDrawActionsButton
isActive={false}
toggleMapEditAction={() => updateZIndex(1)}
icon="arrow-double-up"
title="Remove image"
/>
<MapDrawActionsButton
isActive={false}
toggleMapEditAction={() => updateZIndex(-1)}
icon="arrow-double-down"
title="Remove image"
/>
</> </>
)} )}
</div> </div>
......
...@@ -3,7 +3,7 @@ import MapElement from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/ ...@@ -3,7 +3,7 @@ import MapElement from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/
import CompartmentCircle from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/CompartmentCircle'; import CompartmentCircle from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/CompartmentCircle';
import CompartmentSquare from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/CompartmentSquare'; import CompartmentSquare from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/CompartmentSquare';
import CompartmentPathway from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/CompartmentPathway'; import CompartmentPathway from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/CompartmentPathway';
import Glyph from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/Glyph'; import Glyph from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/Glyph/Glyph';
import { import {
HorizontalAlign, HorizontalAlign,
VerticalAlign, VerticalAlign,
......
...@@ -21,7 +21,7 @@ import { getModelElementsForModel } from '@/redux/modelElements/modelElements.th ...@@ -21,7 +21,7 @@ import { getModelElementsForModel } from '@/redux/modelElements/modelElements.th
import { useAppDispatch } from '@/redux/hooks/useAppDispatch'; import { useAppDispatch } from '@/redux/hooks/useAppDispatch';
import CompartmentSquare from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/CompartmentSquare'; import CompartmentSquare from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/CompartmentSquare';
import CompartmentCircle from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/CompartmentCircle'; import CompartmentCircle from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/CompartmentCircle';
import Glyph from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/Glyph'; import Glyph from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/Glyph/Glyph';
import CompartmentPathway from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/CompartmentPathway'; import CompartmentPathway from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/CompartmentPathway';
import Reaction from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/reaction/Reaction'; import Reaction from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/reaction/Reaction';
import { import {
......
...@@ -4,7 +4,7 @@ import { Style } from 'ol/style'; ...@@ -4,7 +4,7 @@ import { Style } from 'ol/style';
import { UsePointToProjectionResult } from '@/utils/map/usePointToProjection'; import { UsePointToProjectionResult } from '@/utils/map/usePointToProjection';
import Glyph, { import Glyph, {
GlyphProps, GlyphProps,
} from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/Glyph'; } from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/Glyph/Glyph';
import { MapInstance } from '@/types/map'; import { MapInstance } from '@/types/map';
import Polygon from 'ol/geom/Polygon'; import Polygon from 'ol/geom/Polygon';
......
...@@ -154,15 +154,12 @@ export default class Glyph { ...@@ -154,15 +154,12 @@ export default class Glyph {
this.feature.set('setCoordinates', this.setCoordinates.bind(this)); this.feature.set('setCoordinates', this.setCoordinates.bind(this));
this.feature.set('getGlyphData', this.getGlyphData.bind(this)); this.feature.set('getGlyphData', this.getGlyphData.bind(this));
this.feature.set('reset', this.reset.bind(this)); this.feature.set('refreshPolygon', this.refreshPolygon.bind(this));
this.feature.set('setGlyph', this.setGlyph.bind(this)); this.feature.set('update', this.update.bind(this));
this.feature.setId(this.elementId); this.feature.setId(this.elementId);
this.feature.setStyle(this.getStyle.bind(this)); this.feature.setStyle(this.getStyle.bind(this));
if (!this.glyphId) { this.drawImage();
return;
}
this.setGlyph(this.glyphId);
} }
private drawPolygon(): void { private drawPolygon(): void {
...@@ -177,12 +174,34 @@ export default class Glyph { ...@@ -177,12 +174,34 @@ export default class Glyph {
]); ]);
} }
private reset(): void { private refreshPolygon(): void {
this.drawPolygon(); this.drawPolygon();
this.polygonStyle.setGeometry(this.polygon); this.polygonStyle.setGeometry(this.polygon);
this.feature.setGeometry(this.polygon); this.feature.setGeometry(this.polygon);
} }
private refreshZIndex(): void {
this.polygonStyle.setZIndex(this.zIndex);
this.noGlyphStyle.setZIndex(this.zIndex);
this.style.setZIndex(this.zIndex);
this.feature.changed();
}
private update(imageObject: LayerImage): void {
this.elementId = imageObject.id;
this.x = imageObject.x;
this.y = imageObject.y;
this.zIndex = imageObject.z;
this.width = imageObject.width;
this.height = imageObject.height;
this.glyphId = imageObject.glyph;
this.refreshPolygon();
this.refreshZIndex();
this.drawImage();
this.feature.changed();
}
protected setImageScaleAndDimensions(height: number, width: number): void { protected setImageScaleAndDimensions(height: number, width: number): void {
this.widthOnMap = width; this.widthOnMap = width;
this.heightOnMap = height; this.heightOnMap = height;
...@@ -209,7 +228,10 @@ export default class Glyph { ...@@ -209,7 +228,10 @@ export default class Glyph {
} }
} }
private setGlyph(glyph: number): void { private drawImage(): void {
if (!this.glyphId) {
return;
}
const img = new Image(); const img = new Image();
img.onload = (): void => { img.onload = (): void => {
this.imageWidth = img.naturalWidth; this.imageWidth = img.naturalWidth;
...@@ -228,8 +250,7 @@ export default class Glyph { ...@@ -228,8 +250,7 @@ export default class Glyph {
zIndex: this.zIndex, zIndex: this.zIndex,
}); });
}; };
img.src = `${BASE_NEW_API_URL}${apiPath.getGlyphImage(glyph)}`; img.src = `${BASE_NEW_API_URL}${apiPath.getGlyphImage(this.glyphId)}`;
this.glyphId = glyph;
} }
private getGlyphData(): LayerImage { private getGlyphData(): LayerImage {
......
import VectorSource from 'ol/source/Vector';
import { LayerImage } from '@/types/models';
import { MapInstance } from '@/types/map';
export default function updateGlyph(
mapInstance: MapInstance,
layerId: number,
layerImage: LayerImage,
): void {
mapInstance?.getAllLayers().forEach(layer => {
if (layer.get('id') === layerId) {
const source = layer.getSource();
if (source instanceof VectorSource) {
const feature = source.getFeatureById(layerImage.id);
const update = feature?.get('update');
if (update && update instanceof Function) {
update(layerImage);
feature.changed();
}
}
}
});
}
...@@ -26,7 +26,7 @@ import { ...@@ -26,7 +26,7 @@ import {
} from '@/components/Map/MapViewer/MapViewerVector/MapViewerVector.constants'; } from '@/components/Map/MapViewer/MapViewerVector/MapViewerVector.constants';
import getScaledElementStyle from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/style/getScaledElementStyle'; import getScaledElementStyle from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/style/getScaledElementStyle';
import { Stroke } from 'ol/style'; import { Stroke } from 'ol/style';
import Glyph from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/Glyph'; import Glyph from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/Glyph/Glyph';
import { MapSize } from '@/redux/map/map.types'; import { MapSize } from '@/redux/map/map.types';
export interface LayerProps { export interface LayerProps {
......
...@@ -108,7 +108,7 @@ export default function getTransformImageInteraction( ...@@ -108,7 +108,7 @@ export default function getTransformImageInteraction(
const { feature } = transformEvent; const { feature } = transformEvent;
const setCoordinates = feature.get('setCoordinates'); const setCoordinates = feature.get('setCoordinates');
const getGlyphData = feature.get('getGlyphData'); const getGlyphData = feature.get('getGlyphData');
const reset = feature.get('reset'); const refreshPolygon = feature.get('refreshPolygon');
const geometry = feature.getGeometry(); const geometry = feature.getGeometry();
if (geometry && getGlyphData instanceof Function) { if (geometry && getGlyphData instanceof Function) {
const glyphData = getGlyphData(); const glyphData = getGlyphData();
...@@ -119,14 +119,15 @@ export default function getTransformImageInteraction( ...@@ -119,14 +119,15 @@ export default function getTransformImageInteraction(
).unwrap(); ).unwrap();
if (layerImage) { if (layerImage) {
dispatch(layerUpdateImage({ modelId, layerId: activeLayer, layerImage })); dispatch(layerUpdateImage({ modelId, layerId: activeLayer, layerImage }));
dispatch(mapEditToolsSetLayerObject(layerImage));
} }
if (geometry instanceof Polygon && setCoordinates instanceof Function) { if (geometry instanceof Polygon && setCoordinates instanceof Function) {
setCoordinates(geometry.getCoordinates()); setCoordinates(geometry.getCoordinates());
geometry.changed(); geometry.changed();
} }
} catch { } catch {
if (reset instanceof Function) { if (refreshPolygon instanceof Function) {
reset(); refreshPolygon();
} }
} }
} }
......
...@@ -23,6 +23,8 @@ import { ResizeImageIcon } from '@/shared/Icon/Icons/ResizeImageIcon'; ...@@ -23,6 +23,8 @@ import { ResizeImageIcon } from '@/shared/Icon/Icons/ResizeImageIcon';
import { PencilIcon } from '@/shared/Icon/Icons/PencilIcon'; import { PencilIcon } from '@/shared/Icon/Icons/PencilIcon';
import { EditImageIcon } from '@/shared/Icon/Icons/EditImageIcon'; import { EditImageIcon } from '@/shared/Icon/Icons/EditImageIcon';
import { TrashIcon } from '@/shared/Icon/Icons/TrashIcon'; import { TrashIcon } from '@/shared/Icon/Icons/TrashIcon';
import { ArrowDoubleUpIcon } from '@/shared/Icon/Icons/ArrowDoubleUpIcon';
import { ArrowDoubleDownIcon } from '@/shared/Icon/Icons/ArrowDoubleDownIcon';
import { LocationIcon } from './Icons/LocationIcon'; import { LocationIcon } from './Icons/LocationIcon';
import { MaginfierZoomInIcon } from './Icons/MagnifierZoomIn'; import { MaginfierZoomInIcon } from './Icons/MagnifierZoomIn';
import { MaginfierZoomOutIcon } from './Icons/MagnifierZoomOut'; import { MaginfierZoomOutIcon } from './Icons/MagnifierZoomOut';
...@@ -69,6 +71,8 @@ const icons: Record<IconTypes, IconComponentType> = { ...@@ -69,6 +71,8 @@ const icons: Record<IconTypes, IconComponentType> = {
'edit-image': EditImageIcon, 'edit-image': EditImageIcon,
trash: TrashIcon, trash: TrashIcon,
pencil: PencilIcon, pencil: PencilIcon,
'arrow-double-up': ArrowDoubleUpIcon,
'arrow-double-down': ArrowDoubleDownIcon,
} as const; } as const;
export const Icon = ({ name, className = '', ...rest }: IconProps): JSX.Element => { export const Icon = ({ name, className = '', ...rest }: IconProps): JSX.Element => {
......
interface ArrowDoubleDownIconProps {
className?: string;
}
export const ArrowDoubleDownIcon = ({ className }: ArrowDoubleDownIconProps): JSX.Element => (
<svg
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
className={className}
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M8 8L8 18M8 18L5 15M8 18L11 15"
stroke="currentColor"
strokeWidth="1.5"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
d="M16 8L16 18M16 18L13 15M16 18L19 15"
stroke="currentColor"
strokeWidth="1.5"
strokeLinecap="round"
strokeLinejoin="round"
/>
<line
x1="4"
y1="6"
x2="20"
y2="6"
stroke="currentColor"
strokeWidth="1.5"
strokeLinecap="round"
/>
</svg>
);
interface ArrowDoubleUpIconProps {
className?: string;
}
export const ArrowDoubleUpIcon = ({ className }: ArrowDoubleUpIconProps): JSX.Element => (
<svg
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
className={className}
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M8 16L8 6M8 6L5 9M8 6L11 9"
stroke="currentColor"
strokeWidth="1.5"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
d="M16 16L16 6M16 6L13 9M16 6L19 9"
stroke="currentColor"
strokeWidth="1.5"
strokeLinecap="round"
strokeLinejoin="round"
/>
<line
x1="4"
y1="18"
x2="20"
y2="18"
stroke="currentColor"
strokeWidth="1.5"
strokeLinecap="round"
/>
</svg>
);
...@@ -29,6 +29,8 @@ export type IconTypes = ...@@ -29,6 +29,8 @@ export type IconTypes =
| 'resize-image' | 'resize-image'
| 'edit-image' | 'edit-image'
| 'trash' | 'trash'
| 'pencil'; | 'pencil'
| 'arrow-double-up'
| 'arrow-double-down';
export type IconComponentType = ({ className }: { className: string }) => JSX.Element; export type IconComponentType = ({ className }: { className: string }) => JSX.Element;
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment