diff --git a/package-lock.json b/package-lock.json index 1b4341a..c449be2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -27,6 +27,7 @@ "@types/react-router-dom": "^5.1.7", "docuri": "^4.2.2", "ionicons": "^6.0.3", + "jotai": "^1.8.6", "lodash": "^4.17.21", "react": "^18.2.0", "react-dom": "^18.2.0", @@ -10584,6 +10585,55 @@ "url": "https://github.com/chalk/supports-color?sponsor=1" } }, + "node_modules/jotai": { + "version": "1.8.6", + "resolved": "https://registry.npmjs.org/jotai/-/jotai-1.8.6.tgz", + "integrity": "sha512-6JXNd2ewR6Ur0hGY0kRJebQ++p7lN/8kNdGNXv7XT11FDPxAtPN2XLM+m0w4bV22f1knQlG8ZpVLfY6BEGF+UQ==", + "engines": { + "node": ">=12.7.0" + }, + "peerDependencies": { + "@babel/core": "*", + "@babel/template": "*", + "@tanstack/query-core": "*", + "@urql/core": "*", + "immer": "*", + "optics-ts": "*", + "react": ">=16.8", + "valtio": "*", + "wonka": "*", + "xstate": "*" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + }, + "@babel/template": { + "optional": true + }, + "@tanstack/query-core": { + "optional": true + }, + "@urql/core": { + "optional": true + }, + "immer": { + "optional": true + }, + "optics-ts": { + "optional": true + }, + "valtio": { + "optional": true + }, + "wonka": { + "optional": true + }, + "xstate": { + "optional": true + } + } + }, "node_modules/js-sdsl": { "version": "4.1.5", "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.1.5.tgz", @@ -24804,6 +24854,12 @@ } } }, + "jotai": { + "version": "1.8.6", + "resolved": "https://registry.npmjs.org/jotai/-/jotai-1.8.6.tgz", + "integrity": "sha512-6JXNd2ewR6Ur0hGY0kRJebQ++p7lN/8kNdGNXv7XT11FDPxAtPN2XLM+m0w4bV22f1knQlG8ZpVLfY6BEGF+UQ==", + "requires": {} + }, "js-sdsl": { "version": "4.1.5", "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.1.5.tgz", diff --git a/package.json b/package.json index eb90656..d123da6 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,7 @@ "@types/react-router-dom": "^5.1.7", "docuri": "^4.2.2", "ionicons": "^6.0.3", + "jotai": "^1.8.6", "lodash": "^4.17.21", "react": "^18.2.0", "react-dom": "^18.2.0", diff --git a/src/components/map/Handlers.test.tsx b/src/components/map/Handlers.test.tsx index 8f32817..99298bb 100644 --- a/src/components/map/Handlers.test.tsx +++ b/src/components/map/Handlers.test.tsx @@ -8,7 +8,7 @@ describe('The Handlers component ', () => { deltaZoom: number | null, zoomCenter: Point | null ) => {}; - render(); + render(); // screen.debug(); const handlers = screen.getByRole('presentation'); // screen.debug(); @@ -19,7 +19,7 @@ describe('The Handlers component ', () => { /> `); }); - test('handle mouseDown / mouseMove events', () => { +/* test('handle mouseDown / mouseMove events', () => { var transformMapParams: any; function transformMap( deltaShift: Point | null, @@ -242,5 +242,5 @@ Arguments [ null, ] `); - }); + }); */ }); diff --git a/src/components/map/Handlers.tsx b/src/components/map/Handlers.tsx index 5bc4c9e..76c725d 100644 --- a/src/components/map/Handlers.tsx +++ b/src/components/map/Handlers.tsx @@ -1,19 +1,12 @@ import react, { useRef, useState } from 'react'; +import { atom, useAtom } from 'jotai'; + import { Point } from './types'; import './Handler.css'; import { handlersConfig } from './config'; +import { Transformation, relativeCoordinateSystemAtom } from './Map'; -export interface HandlersProperties { - /**Transform the map: - * * Shift the map by a number of pixel - * * Multiply the zoom by deltaZoom around the zoomCenter - * */ - transformMap: ( - deltaShift: Point | null, - deltaZoom: number | null, - zoomCenter: Point | null - ) => void; -} +export interface HandlersProperties {} /** * * @param props @@ -29,6 +22,8 @@ export interface HandlersProperties { export const Handlers: react.FC = ( props: HandlersProperties ) => { + const [, transformMap] = useAtom(relativeCoordinateSystemAtom); + const genericHandler = (event: any) => { // console.log(`Log - Event: ${event.type}`); // if (event.clientX !== undefined) { @@ -84,14 +79,14 @@ export const Handlers: react.FC = ( ) { genericHandler(event); if (mouseState.current.down) { - props.transformMap( - { + transformMap({ + deltaShift: { x: event.clientX - mouseState.current.starting.x, y: event.clientY - mouseState.current.starting.y, }, - null, - null - ); + deltaZoom: null, + zoomCenter: null, + }); mouseState.current = { down: true, starting: { @@ -112,9 +107,13 @@ export const Handlers: react.FC = ( const doubleClickHandler = (event: any) => { genericHandler(event); - props.transformMap(null, Math.SQRT2, { - x: event.clientX, - y: event.clientY, + transformMap({ + deltaShift: null, + deltaZoom: Math.SQRT2, + zoomCenter: { + x: event.clientX, + y: event.clientY, + }, }); }; @@ -137,9 +136,13 @@ export const Handlers: react.FC = ( Date.now() - wheelState.current.timestamp > handlersConfig.wheelThrottleDelay ) { - props.transformMap(null, event.deltaY < 0 ? Math.SQRT2 : Math.SQRT1_2, { - x: event.clientX, - y: event.clientY, + transformMap({ + deltaShift: null, + deltaZoom: event.deltaY < 0 ? Math.SQRT2 : Math.SQRT1_2, + zoomCenter: { + x: event.clientX, + y: event.clientY, + }, }); wheelState.current = { timestamp: Date.now(), @@ -238,17 +241,17 @@ export const Handlers: react.FC = ( x: (event.touches[0].screenX + event.touches[1].screenX) / 2, y: (event.touches[0].screenY + event.touches[1].screenY) / 2, }; - props.transformMap( - { + transformMap({ + deltaShift: { x: currentCenter.x - previousCenter.x, y: currentCenter.y - previousCenter.y, }, - factor, - { + deltaZoom: factor, + zoomCenter: { x: currentCenter.x - previousCenter.x, y: currentCenter.y - previousCenter.y, - } - ); + }, + }); } } else if ( touchState.current.state === 'pointer' && @@ -257,14 +260,14 @@ export const Handlers: react.FC = ( ) { if (event.touches.length === 1) { genericHandler(event); - props.transformMap( - { + transformMap({ + deltaShift: { x: event.touches[0].screenX - touchState.current.touches[0].x, y: event.touches[0].screenY - touchState.current.touches[0].y, }, - null, - null - ); + deltaZoom: null, + zoomCenter: null, + }); touchState.current = { state: 'pointer', touches: [ diff --git a/src/components/map/Map.tsx b/src/components/map/Map.tsx index b97ce21..4951dff 100644 --- a/src/components/map/Map.tsx +++ b/src/components/map/Map.tsx @@ -1,4 +1,6 @@ import react, { useCallback, useState } from 'react'; +import { atom, useAtom } from 'jotai'; + import Handlers from './Handlers'; import Tile from './Tile'; import TiledLayer from './TiledLayer'; @@ -6,33 +8,28 @@ import { Point, TileFactory } from './types'; export interface MapProperties {} -/** - * - * @returns A Map component - */ -export const Map: react.FC = (props: MapProperties) => { - const [coordinateSystem, setCoordinateSystem] = useState({ - zoom: 1, - shift: { x: 0, y: 0 }, - }); +const initialCoordinateSystem = { + zoom: 1, + shift: { x: 0, y: 0 }, +}; - const simpleTileFactory: TileFactory = useCallback( - (keyObject) => ( - - ), - [] - ); +const coordinateSystemAtom = atom(initialCoordinateSystem); - const transformMap = ( - deltaShift: Point | null, - deltaZoom: number | null, - zoomCenter: Point | null - ) => { - const actualDeltaShift = deltaShift === null ? { x: 0, y: 0 } : deltaShift; - const actualDeltaZoom = deltaZoom === null ? 1 : deltaZoom; - const actualZoomCenter = zoomCenter === null ? { x: 0, y: 0 } : zoomCenter; +export interface Transformation { + deltaShift: Point | null; + deltaZoom: number | null; + zoomCenter: Point | null; +} + +export const relativeCoordinateSystemAtom = atom( + null, + (get, set, t: Transformation) => { + const actualDeltaShift = + t.deltaShift === null ? { x: 0, y: 0 } : t.deltaShift; + const actualDeltaZoom = t.deltaZoom === null ? 1 : t.deltaZoom; + const actualZoomCenter = + t.zoomCenter === null ? { x: 0, y: 0 } : t.zoomCenter; + const coordinateSystem = get(coordinateSystemAtom); var newCoordinateSystem = { shift: { x: @@ -48,8 +45,25 @@ export const Map: react.FC = (props: MapProperties) => { }, zoom: coordinateSystem.zoom * actualDeltaZoom, }; - setCoordinateSystem(newCoordinateSystem); - }; + set(coordinateSystemAtom, newCoordinateSystem); + } +); + +/** + * + * @returns A Map component + */ +export const Map: react.FC = (props: MapProperties) => { + const [coordinateSystem, setCoordinateSystem] = useAtom(coordinateSystemAtom); + + const simpleTileFactory: TileFactory = useCallback( + (keyObject) => ( + + ), + [] + ); const viewPort = { topLeft: { @@ -72,7 +86,7 @@ export const Map: react.FC = (props: MapProperties) => { return ( <> - +