Skip to content
Snippets Groups Projects
Commit affe8fdc authored by Adrian Orłów's avatar Adrian Orłów :fire:
Browse files

Merge branch 'feature/overview-image-interactive' into 'development'

feat: add overview image interactive layer

See merge request !77
parents 4c1a0299 70c94800
No related branches found
No related tags found
2 merge requests!223reset the pin numbers before search results are fetch (so the results will be...,!77feat: add overview image interactive layer
Pipeline #83493 passed
Showing
with 851 additions and 63 deletions
import { OverviewImageLinkImage, OverviewImageLinkModel } from '@/types/models';
export interface OverviewImageSize {
width: number;
height: number;
......@@ -7,3 +9,20 @@ export interface ImageContainerSize {
width: number;
height: number;
}
export interface OverviewImageLinkConfigSize {
top: number;
left: number;
width: number;
height: number;
}
export interface OverviewImageLinkConfig {
idObject: number;
size: OverviewImageLinkConfigSize;
onClick(): void;
}
export type OverviewImageLinkImageHandler = (link: OverviewImageLinkImage) => void;
export type OverviewImageLinkModelHandler = (link: OverviewImageLinkModel) => void;
/* eslint-disable @next/next/no-img-element */
import * as React from 'react';
import { useCallback, useState } from 'react';
import { useCallback, useLayoutEffect, useState } from 'react';
import { useOverviewImage } from './utils/useOverviewImage';
export const OverviewImagesModal: React.FC = () => {
const [containerRect, setContainerRect] = useState<DOMRect>();
const { imageUrl, size } = useOverviewImage({ containerRect });
const { imageUrl, size, linkConfigs } = useOverviewImage({ containerRect });
const { width, height } = size;
const [containerNode, setContainerNode] = useState<HTMLDivElement | null>(null);
const handleRect = useCallback((node: HTMLDivElement | null) => {
if (!node) {
return;
const handleContainerRef = useCallback((node: HTMLDivElement | null) => {
if (node !== null) {
setContainerRect(node.getBoundingClientRect());
setContainerNode(node);
}
setContainerRect(node.getBoundingClientRect());
}, []);
useLayoutEffect(() => {
const updateContainerSize = (): void => {
if (containerNode !== null) {
setContainerRect(containerNode.getBoundingClientRect());
}
};
window.addEventListener('resize', updateContainerSize);
return () => window.removeEventListener('resize', updateContainerSize);
}, [containerNode]);
if (!imageUrl) {
return null;
}
......@@ -24,11 +35,27 @@ export const OverviewImagesModal: React.FC = () => {
<div
data-testid="overview-images-modal"
className="flex h-full w-full items-center justify-center bg-white"
ref={handleRect}
ref={handleContainerRef}
>
<div className="relative" style={{ width, height }}>
<img alt="overview" className="block h-full w-full" src={imageUrl} />
{/* TODO: interactions - clickable elements (in next task) */}
{linkConfigs.map(({ size: linkSize, onClick, idObject }) => (
// eslint-disable-next-line jsx-a11y/click-events-have-key-events
<div
role="button"
tabIndex={0}
key={idObject}
className="cursor-pointer"
style={{
height: linkSize.height,
width: linkSize.width,
top: linkSize.top,
left: linkSize.left,
position: 'absolute',
}}
onClick={onClick}
/>
))}
</div>
</div>
);
......
import { OverviewImageLinkConfigSize } from './OverviewImageModal.types';
export const DEFAULT_OVERVIEW_IMAGE_LINK_CONFIG: OverviewImageLinkConfigSize = {
top: 0,
left: 0,
width: 0,
height: 0,
};
import { OverviewImageLink } from '@/types/models';
import { OverviewImageLinkConfigSize } from '../OverviewImageModal.types';
import { DEFAULT_OVERVIEW_IMAGE_LINK_CONFIG } from '../OverviewImagesModal.constants';
import { getOverviewImageLinkSize } from './getOverviewImageLinkSize';
describe('getOverviewImageLinkSize - util', () => {
const cases: [
Pick<OverviewImageLink, 'polygon'>,
{
sizeFactor: number;
},
OverviewImageLinkConfigSize,
][] = [
// invalid polygon
[
{
polygon: [],
},
{
sizeFactor: 1,
},
DEFAULT_OVERVIEW_IMAGE_LINK_CONFIG,
],
// invalid polygon
[
{
polygon: [
{ x: 0, y: 0 },
{ x: 0, y: 0 },
{ x: 0, y: 0 },
],
},
{
sizeFactor: 1,
},
DEFAULT_OVERVIEW_IMAGE_LINK_CONFIG,
],
// valid polygon with size of 0x0
[
{
polygon: [
{ x: 0, y: 0 },
{ x: 0, y: 0 },
{ x: 0, y: 0 },
{ x: 0, y: 0 },
],
},
{
sizeFactor: 1,
},
{
top: 0,
left: 0,
width: 0,
height: 0,
},
],
// valid polygon with size of 20x50
[
{
polygon: [
{ x: 10, y: 0 },
{ x: 30, y: 0 },
{ x: 30, y: 50 },
{ x: 10, y: 50 },
],
},
{
sizeFactor: 1,
},
{
top: 0,
left: 10,
width: 20,
height: 50,
},
],
// valid polygon with size of 27x67.5 in scale of 1.35
[
{
polygon: [
{ x: 10, y: 0 },
{ x: 30, y: 0 },
{ x: 30, y: 50 },
{ x: 10, y: 50 },
],
},
{
sizeFactor: 1.35,
},
{
height: 67.5,
left: 13.5,
top: 0,
width: 27,
},
],
];
it.each(cases)(
'should return valid link config size',
(overviewImageWithPolygon, options, finalConfigSize) => {
expect(getOverviewImageLinkSize(overviewImageWithPolygon, options)).toStrictEqual(
finalConfigSize,
);
},
);
});
import { SIZE_OF_ARRAY_WITH_FOUR_ELEMENTS } from '@/constants/common';
import { OverviewImageLink } from '@/types/models';
import { OverviewImageLinkConfigSize } from '../OverviewImageModal.types';
import { DEFAULT_OVERVIEW_IMAGE_LINK_CONFIG } from '../OverviewImagesModal.constants';
export const getOverviewImageLinkSize = (
{ polygon }: Pick<OverviewImageLink, 'polygon'>,
{
sizeFactor,
}: {
sizeFactor: number;
},
): OverviewImageLinkConfigSize => {
// valid polygon needs to have four points
if (polygon.length < SIZE_OF_ARRAY_WITH_FOUR_ELEMENTS) {
return DEFAULT_OVERVIEW_IMAGE_LINK_CONFIG;
}
const polygonScaled = polygon.map(({ x, y }) => ({ x: x * sizeFactor, y: y * sizeFactor }));
const [pointTopLeft, , pointBottomRight] = polygonScaled;
const width = pointBottomRight.x - pointTopLeft.x;
const height = pointBottomRight.y - pointTopLeft.y;
const { x, y } = pointTopLeft;
return {
top: y,
left: x,
width,
height,
};
};
......@@ -35,6 +35,7 @@ describe('useOverviewImage - hook', () => {
expect(result.current).toStrictEqual({
imageUrl: '',
size: DEFAULT_OVERVIEW_IMAGE_SIZE,
linkConfigs: [],
});
});
});
......@@ -65,7 +66,7 @@ describe('useOverviewImage - hook', () => {
it('should return default size of image and valid imageUrl', () => {
const imageUrl = `${BASE_MAP_IMAGES_URL}/map_images/${PROJECT_OVERVIEW_IMAGE_MOCK.filename}`;
expect(result.current).toStrictEqual({
expect(result.current).toMatchObject({
imageUrl,
size: DEFAULT_OVERVIEW_IMAGE_SIZE,
});
......@@ -107,7 +108,7 @@ describe('useOverviewImage - hook', () => {
it('should return size of image and valid imageUrl', () => {
const imageUrl = `${BASE_MAP_IMAGES_URL}/map_images/${PROJECT_OVERVIEW_IMAGE_MOCK.filename}`;
expect(result.current).toStrictEqual({
expect(result.current).toMatchObject({
imageUrl,
size: { height: 100, width: 100, sizeFactor: 0.2 },
});
......
import { OverviewImageSize } from '../OverviewImageModal.types';
import { OverviewImageLinkConfig, OverviewImageSize } from '../OverviewImageModal.types';
import { useOverviewImageLinkConfigs } from './useOverviewImageLinkElements';
import { useOverviewImageSize } from './useOverviewImageSize';
import { useOverviewImageUrl } from './useOverviewImageUrl';
......@@ -9,6 +10,7 @@ interface UseOverviewImageArgs {
interface UseOverviewImageResults {
imageUrl: string;
size: OverviewImageSize;
linkConfigs: OverviewImageLinkConfig[];
}
export const useOverviewImage = ({
......@@ -16,9 +18,11 @@ export const useOverviewImage = ({
}: UseOverviewImageArgs): UseOverviewImageResults => {
const imageUrl = useOverviewImageUrl();
const size = useOverviewImageSize({ containerRect });
const linkConfigs = useOverviewImageLinkConfigs({ sizeFactor: size.sizeFactor });
return {
size,
imageUrl,
linkConfigs,
};
};
import { projectFixture } from '@/models/fixtures/projectFixture';
import { MODELS_MOCK_SHORT } from '@/models/mocks/modelsMock';
import {
OVERVIEW_LINK_IMAGE_MOCK,
OVERVIEW_LINK_MODEL_MOCK,
} from '@/models/mocks/overviewImageMocks';
import {
initialMapDataFixture,
openedMapsInitialValueFixture,
openedMapsThreeSubmapsFixture,
} from '@/redux/map/map.fixtures';
import { MODAL_INITIAL_STATE_MOCK } from '@/redux/modal/modal.mock';
import { PROJECT_OVERVIEW_IMAGE_MOCK } from '@/redux/project/project.mock';
import { OverviewImageLink } from '@/types/models';
import { getReduxStoreWithActionsListener } from '@/utils/testing/getReduxStoreActionsListener';
import { getReduxWrapperWithStore } from '@/utils/testing/getReduxWrapperWithStore';
import { renderHook } from '@testing-library/react';
import {
FIRST_ARRAY_ELEMENT,
NOOP,
SECOND_ARRAY_ELEMENT,
SIZE_OF_EMPTY_ARRAY,
THIRD_ARRAY_ELEMENT,
} from '../../../../../constants/common';
import { useOverviewImageLinkActions } from './useOverviewImageLinkActions';
jest.mock('../../../../../constants/common', () => ({
...jest.requireActual('../../../../../constants/common'),
NOOP: jest.fn(),
}));
describe('useOverviewImageLinkActions - hook', () => {
describe('when clicked on image link', () => {
describe('when image id is NOT valid', () => {
const { Wrapper, store } = getReduxStoreWithActionsListener({
project: {
data: {
...projectFixture,
overviewImageViews: [],
topOverviewImage: PROJECT_OVERVIEW_IMAGE_MOCK,
},
loading: 'succeeded',
error: { message: '', name: '' },
},
modal: {
...MODAL_INITIAL_STATE_MOCK,
overviewImagesState: {
imageId: 0,
},
},
map: {
data: {
...initialMapDataFixture,
modelId: 5053,
},
loading: 'succeeded',
error: { name: '', message: '' },
openedMaps: openedMapsThreeSubmapsFixture,
},
});
const {
result: {
current: { handleLinkClick },
},
} = renderHook(() => useOverviewImageLinkActions(), {
wrapper: Wrapper,
});
it('should NOT fire action set overview image id', () => {
handleLinkClick(OVERVIEW_LINK_IMAGE_MOCK);
const actions = store.getActions();
expect(actions.length).toEqual(SIZE_OF_EMPTY_ARRAY);
});
});
describe('when image id is valid', () => {
const { Wrapper, store } = getReduxStoreWithActionsListener({
project: {
data: {
...projectFixture,
overviewImageViews: [
{
...PROJECT_OVERVIEW_IMAGE_MOCK,
height: 500,
width: 500,
},
],
topOverviewImage: PROJECT_OVERVIEW_IMAGE_MOCK,
},
loading: 'succeeded',
error: { message: '', name: '' },
},
modal: {
...MODAL_INITIAL_STATE_MOCK,
overviewImagesState: {
imageId: PROJECT_OVERVIEW_IMAGE_MOCK.idObject,
},
},
map: {
data: {
...initialMapDataFixture,
modelId: 5053,
},
loading: 'succeeded',
error: { name: '', message: '' },
openedMaps: openedMapsThreeSubmapsFixture,
},
});
const {
result: {
current: { handleLinkClick },
},
} = renderHook(() => useOverviewImageLinkActions(), {
wrapper: Wrapper,
});
it('should fire action set overview image id', () => {
handleLinkClick(OVERVIEW_LINK_IMAGE_MOCK);
const actions = store.getActions();
expect(actions[FIRST_ARRAY_ELEMENT]).toStrictEqual({
payload: 440,
type: 'modal/setOverviewImageId',
});
});
});
});
describe('when clicked on model link', () => {
describe('when model is not available', () => {});
describe('when model is available', () => {
describe('when map is already opened', () => {
const { Wrapper, store } = getReduxStoreWithActionsListener({
project: {
data: {
...projectFixture,
overviewImageViews: [
{
...PROJECT_OVERVIEW_IMAGE_MOCK,
height: 500,
width: 500,
},
],
topOverviewImage: PROJECT_OVERVIEW_IMAGE_MOCK,
},
loading: 'succeeded',
error: { message: '', name: '' },
},
modal: {
...MODAL_INITIAL_STATE_MOCK,
overviewImagesState: {
imageId: PROJECT_OVERVIEW_IMAGE_MOCK.idObject,
},
},
map: {
data: {
...initialMapDataFixture,
modelId: 5053,
},
loading: 'succeeded',
error: { name: '', message: '' },
openedMaps: openedMapsThreeSubmapsFixture,
},
models: {
data: MODELS_MOCK_SHORT,
loading: 'succeeded',
error: { name: '', message: '' },
},
});
const {
result: {
current: { handleLinkClick },
},
} = renderHook(() => useOverviewImageLinkActions(), {
wrapper: Wrapper,
});
it('should set active map', () => {
handleLinkClick(OVERVIEW_LINK_MODEL_MOCK);
const actions = store.getActions();
expect(actions[FIRST_ARRAY_ELEMENT]).toStrictEqual({
payload: {
modelId: 5053,
},
type: 'map/setActiveMap',
});
});
it('should set map position', () => {
handleLinkClick(OVERVIEW_LINK_MODEL_MOCK);
const actions = store.getActions();
expect(actions[SECOND_ARRAY_ELEMENT]).toStrictEqual({
payload: { x: 15570, y: 3016, z: 7 },
type: 'map/setMapPosition',
});
});
it('should close modal', () => {
handleLinkClick(OVERVIEW_LINK_MODEL_MOCK);
const actions = store.getActions();
expect(actions[THIRD_ARRAY_ELEMENT]).toStrictEqual({
payload: undefined,
type: 'modal/closeModal',
});
});
});
describe('when map is not opened', () => {
const { Wrapper, store } = getReduxStoreWithActionsListener({
project: {
data: {
...projectFixture,
overviewImageViews: [
{
...PROJECT_OVERVIEW_IMAGE_MOCK,
height: 500,
width: 500,
},
],
topOverviewImage: PROJECT_OVERVIEW_IMAGE_MOCK,
},
loading: 'succeeded',
error: { message: '', name: '' },
},
modal: {
...MODAL_INITIAL_STATE_MOCK,
overviewImagesState: {
imageId: PROJECT_OVERVIEW_IMAGE_MOCK.idObject,
},
},
map: {
data: {
...initialMapDataFixture,
modelId: 5053,
},
loading: 'succeeded',
error: { name: '', message: '' },
openedMaps: openedMapsInitialValueFixture,
},
models: {
data: MODELS_MOCK_SHORT,
loading: 'succeeded',
error: { name: '', message: '' },
},
});
const {
result: {
current: { handleLinkClick },
},
} = renderHook(() => useOverviewImageLinkActions(), {
wrapper: Wrapper,
});
it('should open map and set as active', () => {
handleLinkClick(OVERVIEW_LINK_MODEL_MOCK);
const actions = store.getActions();
expect(actions[FIRST_ARRAY_ELEMENT]).toStrictEqual({
payload: {
modelId: 5053,
modelName: 'Core PD map',
},
type: 'map/openMapAndSetActive',
});
});
it('should set map position', () => {
handleLinkClick(OVERVIEW_LINK_MODEL_MOCK);
const actions = store.getActions();
expect(actions[SECOND_ARRAY_ELEMENT]).toStrictEqual({
payload: { x: 15570, y: 3016, z: 7 },
type: 'map/setMapPosition',
});
});
it('should close modal', () => {
handleLinkClick(OVERVIEW_LINK_MODEL_MOCK);
const actions = store.getActions();
expect(actions[THIRD_ARRAY_ELEMENT]).toStrictEqual({
payload: undefined,
type: 'modal/closeModal',
});
});
});
});
});
describe('when clicked on unsupported link', () => {
const { Wrapper } = getReduxWrapperWithStore();
const {
result: {
current: { handleLinkClick },
},
} = renderHook(() => useOverviewImageLinkActions(), {
wrapper: Wrapper,
});
it('should noop', () => {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore to simulate invalid link object
handleLinkClick({ link: {} as unknown as OverviewImageLink });
expect(NOOP).toBeCalled();
});
});
});
import { NOOP } from '@/constants/common';
import { useAppDispatch } from '@/redux/hooks/useAppDispatch';
import { useAppSelector } from '@/redux/hooks/useAppSelector';
import { mapOpenedMapsSelector } from '@/redux/map/map.selectors';
import { openMapAndSetActive, setActiveMap, setMapPosition } from '@/redux/map/map.slice';
import { closeModal, setOverviewImageId } from '@/redux/modal/modal.slice';
import { modelsDataSelector } from '@/redux/models/models.selectors';
import { projectOverviewImagesSelector } from '@/redux/project/project.selectors';
import { MapModel, OverviewImageLink, OverviewImageLinkModel } from '@/types/models';
import {
OverviewImageLinkImageHandler,
OverviewImageLinkModelHandler,
} from '../OverviewImageModal.types';
interface UseOverviewImageLinkActionsResult {
handleLinkClick(link: OverviewImageLink): void;
}
export const useOverviewImageLinkActions = (): UseOverviewImageLinkActionsResult => {
const dispatch = useAppDispatch();
const openedMaps = useAppSelector(mapOpenedMapsSelector);
const models = useAppSelector(modelsDataSelector);
const overviewImages = useAppSelector(projectOverviewImagesSelector);
const checkIfImageIsAvailable = (imageId: number): boolean =>
overviewImages.some(image => image.idObject === imageId);
const checkIfMapAlreadyOpened = (modelId: number): boolean =>
openedMaps.some(map => map.modelId === modelId);
const getModelById = (modelId: number): MapModel | undefined =>
models.find(map => map.idObject === modelId);
const handleOpenMap = (model: MapModel): void => {
const modelId = model.idObject;
const isMapOpened = checkIfMapAlreadyOpened(modelId);
if (isMapOpened) {
dispatch(setActiveMap({ modelId }));
return;
}
dispatch(openMapAndSetActive({ modelId, modelName: model.name }));
};
const handleSetMapPosition = (link: OverviewImageLinkModel, model: MapModel): void => {
dispatch(
setMapPosition({
x: link.modelPoint.x,
y: link.modelPoint.y,
z: link.zoomLevel + model.minZoom,
}),
);
};
const onSubmapClick: OverviewImageLinkModelHandler = link => {
const modelId = link.modelLinkId;
const model = getModelById(modelId);
if (!model) {
return;
}
handleOpenMap(model);
handleSetMapPosition(link, model);
dispatch(closeModal());
};
const onImageClick: OverviewImageLinkImageHandler = link => {
const isImageAvailable = checkIfImageIsAvailable(link.imageLinkId);
if (!isImageAvailable) {
return;
}
dispatch(setOverviewImageId(link.imageLinkId));
};
const handleLinkClick: UseOverviewImageLinkActionsResult['handleLinkClick'] = link => {
const isImageLink = 'imageLinkId' in link;
const isModelLink = 'modelLinkId' in link;
if (isImageLink) {
return onImageClick(link);
}
if (isModelLink) {
return onSubmapClick(link);
}
return NOOP();
};
return {
handleLinkClick,
};
};
import { ZERO } from '@/constants/common';
import { projectFixture } from '@/models/fixtures/projectFixture';
import { MODAL_INITIAL_STATE_MOCK } from '@/redux/modal/modal.mock';
import { PROJECT_OVERVIEW_IMAGE_MOCK } from '@/redux/project/project.mock';
import { getReduxWrapperWithStore } from '@/utils/testing/getReduxWrapperWithStore';
import { renderHook } from '@testing-library/react';
import { useOverviewImageLinkConfigs } from './useOverviewImageLinkElements';
describe('useOverviewImageLinkConfigs - hook', () => {
describe('when currentImage is undefined', () => {
const { Wrapper } = getReduxWrapperWithStore({
project: {
data: {
...projectFixture,
overviewImageViews: [],
topOverviewImage: PROJECT_OVERVIEW_IMAGE_MOCK,
},
loading: 'succeeded',
error: { message: '', name: '' },
},
modal: {
...MODAL_INITIAL_STATE_MOCK,
overviewImagesState: {
imageId: 0,
},
},
});
const {
result: { current: returnValue },
} = renderHook(
() =>
useOverviewImageLinkConfigs({
sizeFactor: 1,
}),
{
wrapper: Wrapper,
},
);
it('should return empty array', () => {
expect(returnValue).toStrictEqual([]);
});
});
describe('when sizeFactor is zero', () => {
const { Wrapper } = getReduxWrapperWithStore({
project: {
data: {
...projectFixture,
overviewImageViews: [PROJECT_OVERVIEW_IMAGE_MOCK],
topOverviewImage: PROJECT_OVERVIEW_IMAGE_MOCK,
},
loading: 'succeeded',
error: { message: '', name: '' },
},
modal: {
...MODAL_INITIAL_STATE_MOCK,
overviewImagesState: {
imageId: PROJECT_OVERVIEW_IMAGE_MOCK.idObject,
},
},
});
const {
result: { current: returnValue },
} = renderHook(
() =>
useOverviewImageLinkConfigs({
sizeFactor: ZERO,
}),
{
wrapper: Wrapper,
},
);
it('should return empty array', () => {
expect(returnValue).toStrictEqual([]);
});
});
describe('when all args are valid', () => {
const { Wrapper } = getReduxWrapperWithStore({
project: {
data: {
...projectFixture,
overviewImageViews: [PROJECT_OVERVIEW_IMAGE_MOCK],
topOverviewImage: PROJECT_OVERVIEW_IMAGE_MOCK,
},
loading: 'succeeded',
error: { message: '', name: '' },
},
modal: {
...MODAL_INITIAL_STATE_MOCK,
overviewImagesState: {
imageId: PROJECT_OVERVIEW_IMAGE_MOCK.idObject,
},
},
});
const {
result: { current: returnValue },
} = renderHook(
() =>
useOverviewImageLinkConfigs({
sizeFactor: 1,
}),
{
wrapper: Wrapper,
},
);
it('should return correct value', () => {
expect(returnValue).toStrictEqual([
{
idObject: 2062,
size: { top: 2187, left: 515, width: 558, height: 333 },
onClick: expect.any(Function),
},
{
idObject: 2063,
size: { top: 1360, left: 2410, width: 282, height: 210 },
onClick: expect.any(Function),
},
{
idObject: 2064,
size: { top: 497, left: 2830, width: 426, height: 335 },
onClick: expect.any(Function),
},
{
idObject: 2065,
size: { top: 2259, left: 3232, width: 288, height: 197 },
onClick: expect.any(Function),
},
{
idObject: 2066,
size: { top: 761, left: 4205, width: 420, height: 341 },
onClick: expect.any(Function),
},
{
idObject: 2067,
size: { top: 1971, left: 4960, width: 281, height: 192 },
onClick: expect.any(Function),
},
]);
});
});
});
import { ZERO } from '@/constants/common';
import { currentOverviewImageSelector } from '@/redux/project/project.selectors';
import { useSelector } from 'react-redux';
import { OverviewImageLinkConfig } from '../OverviewImageModal.types';
import { getOverviewImageLinkSize } from './getOverviewImageLinkSize';
import { useOverviewImageLinkActions } from './useOverviewImageLinkActions';
interface UseOverviewImageLinksArgs {
sizeFactor: number;
}
export const useOverviewImageLinkConfigs = ({
sizeFactor,
}: UseOverviewImageLinksArgs): OverviewImageLinkConfig[] => {
const { handleLinkClick } = useOverviewImageLinkActions();
const currentImage = useSelector(currentOverviewImageSelector);
if (!currentImage || sizeFactor === ZERO) return [];
return currentImage.links.map(link => ({
idObject: link.idObject,
size: getOverviewImageLinkSize(link, { sizeFactor }),
onClick: () => handleLinkClick(link),
}));
};
import { ZERO } from '@/constants/common';
import type { GetHex3ColorGradientColorWithAlpha } from '@/hooks/useTriColorLerp';
import { OverlayBioEntityRender } from '@/types/OLrendering';
import { convertDecimalToHex } from '@/utils/convert/convertDecimalToHex';
import { convertDecimalToHexColor } from '@/utils/convert/convertDecimalToHex';
export const getColorByAvailableProperties = (
entity: OverlayBioEntityRender,
......@@ -12,7 +12,7 @@ export const getColorByAvailableProperties = (
return getHexTricolorGradientColorWithAlpha(entity.value || ZERO);
}
if (entity.color) {
return convertDecimalToHex(entity.color.rgb);
return convertDecimalToHexColor(entity.color.rgb);
}
return defaultColor;
};
......@@ -53,12 +53,8 @@ describe('useOlMapView - util', () => {
await act(() => {
store.dispatch(
setMapPosition({
position: {
initial: {
x: 0,
y: 0,
},
},
x: 0,
y: 0,
}),
);
});
......
......@@ -16,13 +16,9 @@ export const onMapPositionChange =
dispatch(
setMapPosition({
position: {
last: {
x,
y,
z: Math.round(zoom),
}
}
x,
y,
z: Math.round(zoom),
}),
);
};
export const SIZE_OF_EMPTY_ARRAY = 0;
export const SIZE_OF_ARRAY_WITH_FOUR_ELEMENTS = 4;
export const SIZE_OF_ARRAY_WITH_ONE_ELEMENT = 1;
export const ZERO = 0;
......@@ -6,3 +7,7 @@ export const FIRST_ARRAY_ELEMENT = 0;
export const ONE = 1;
export const SECOND_ARRAY_ELEMENT = 1;
export const THIRD_ARRAY_ELEMENT = 2;
export const NOOP = (): void => {};
import { PROJECT_OVERVIEW_IMAGE_MOCK } from '@/redux/project/project.mock';
import { OverviewImageLinkImage, OverviewImageLinkModel } from '@/types/models';
export const OVERVIEW_LINK_IMAGE_MOCK: OverviewImageLinkImage = {
idObject: 1,
polygon: [],
imageLinkId: PROJECT_OVERVIEW_IMAGE_MOCK.idObject,
type: 'OverviewImageLink',
};
export const OVERVIEW_LINK_MODEL_MOCK: OverviewImageLinkModel = {
idObject: 1,
polygon: [],
zoomLevel: 5,
modelPoint: {
x: 15570.0,
y: 3016.0,
},
modelLinkId: 5053,
type: 'OverviewImageLink',
};
import { z } from 'zod';
import { positionSchema } from './positionSchema';
export const overviewImageLink = z.union([
z.object({
idObject: z.number(),
polygon: z.array(positionSchema),
imageLinkId: z.number(),
type: z.string(),
}),
z.object({
idObject: z.number(),
polygon: z.array(positionSchema),
zoomLevel: z.number(),
modelPoint: positionSchema,
modelLinkId: z.number(),
type: z.string(),
}),
]);
export const overviewImageLinkImage = z.object({
idObject: z.number(),
polygon: z.array(positionSchema),
imageLinkId: z.number(),
type: z.string(),
});
export const overviewImageLinkModel = z.object({
idObject: z.number(),
polygon: z.array(positionSchema),
zoomLevel: z.number(),
modelPoint: positionSchema,
modelLinkId: z.number(),
type: z.string(),
});
export const overviewImageLink = z.union([overviewImageLinkImage, overviewImageLinkModel]);
import { ActionReducerMapBuilder } from '@reduxjs/toolkit';
import { ZERO } from '@/constants/common';
import { ActionReducerMapBuilder } from '@reduxjs/toolkit';
import { getPointMerged } from '../../utils/object/getPointMerged';
import { MAIN_MAP } from './map.constants';
import {
initMapBackground,
initMapPosition,
initMapSizeAndModelId,
initOpenedMaps,
} from './map.thunks';
import {
CloseMapAction,
MapState,
......@@ -9,14 +17,6 @@ import {
SetMapDataAction,
SetMapPositionDataAction,
} from './map.types';
import { MAIN_MAP } from './map.constants';
import { getPointMerged } from '../../utils/object/getPointMerged';
import {
initMapBackground,
initMapPosition,
initMapSizeAndModelId,
initOpenedMaps,
} from './map.thunks';
export const setMapDataReducer = (state: MapState, action: SetMapDataAction): void => {
const payload = action.payload || {};
......@@ -28,15 +28,14 @@ export const setMapDataReducer = (state: MapState, action: SetMapDataAction): vo
};
export const setMapPositionReducer = (state: MapState, action: SetMapPositionDataAction): void => {
const payload = action.payload || {};
const payloadPosition = 'position' in payload ? payload.position : undefined;
const position = action.payload || {};
const statePosition = state.data.position;
state.data = {
...state.data,
position: {
initial: getPointMerged(payloadPosition?.initial || {}, statePosition.initial),
last: getPointMerged(payloadPosition?.last || {}, statePosition.last),
initial: getPointMerged(position || {}, statePosition.initial),
last: getPointMerged(position || {}, statePosition.last),
},
};
};
......
......@@ -80,9 +80,7 @@ export type GetUpdatedMapDataResult = Pick<
'modelId' | 'backgroundId' | 'size' | 'position'
>;
export type SetMapPositionDataActionPayload = GetUpdatedMapDataResult | object;
export type SetMapPositionDataAction = PayloadAction<SetMapPositionDataActionPayload>;
export type SetMapPositionDataAction = PayloadAction<Point>;
export type InitMapDataActionPayload = {
data: GetUpdatedMapDataResult | object;
......
......@@ -2,17 +2,17 @@ import { currentBackgroundSelector } from '@/redux/backgrounds/background.select
import type { AppListenerEffectAPI, AppStartListening } from '@/redux/store';
import { getUpdatedMapData } from '@/utils/map/getUpdatedMapData';
import { Action, createListenerMiddleware, isAnyOf } from '@reduxjs/toolkit';
import { mapOpenedMapPositionByIdSelector } from '../map.selectors';
import {
closeMapAndSetMainMapActive,
openMapAndSetActive,
setActiveMap,
setMapBackground,
setMapData,
setMapPosition,
closeMapAndSetMainMapActive,
setMapBackground,
} from '../map.slice';
import { checkIfIsMapUpdateActionValid } from './checkIfIsMapUpdateActionValid';
import { getUpdatedModel } from './getUpdatedModel';
import { mapOpenedMapPositionByIdSelector } from '../map.selectors';
export const mapListenerMiddleware = createListenerMiddleware();
......@@ -39,7 +39,7 @@ export const mapDataMiddlewareListener = async (
background,
});
dispatch(setMapData(updatedMapData));
dispatch(setMapPosition(updatedMapData));
dispatch(setMapPosition(updatedMapData.position.initial));
};
startListening({
......
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