diff --git a/svgmap/src/App.tsx b/svgmap/src/App.tsx
index 64f7bc0..4b2be5f 100644
--- a/svgmap/src/App.tsx
+++ b/svgmap/src/App.tsx
@@ -19,12 +19,14 @@ import '@ionic/react/css/display.css';
/* Theme variables */
import './theme/variables.css';
+import Map from './components/map/Map';
+
setupIonicReact();
const App: React.FC = () => (
- This works!
-
+
+
);
export default App;
diff --git a/svgmap/src/components/map/Map.css b/svgmap/src/components/map/Map.css
new file mode 100644
index 0000000..34812f7
--- /dev/null
+++ b/svgmap/src/components/map/Map.css
@@ -0,0 +1,3 @@
+.map {
+ overflow: hidden;
+}
diff --git a/svgmap/src/components/map/Map.tsx b/svgmap/src/components/map/Map.tsx
new file mode 100644
index 0000000..ba68acf
--- /dev/null
+++ b/svgmap/src/components/map/Map.tsx
@@ -0,0 +1,28 @@
+import { IonContent, IonApp } from '@ionic/react';
+import react, { useState } from 'react';
+
+import './Map.css';
+import MouseHandler from './MouseHandler';
+import Viewport from './Viewport';
+
+interface MapProperties {
+ height: number;
+ width: number;
+}
+
+const Map: react.FC = (props: MapProperties) => {
+ const boardSize = Math.max(props.width, props.height) * 2;
+
+ return (
+
+
+
+
+
+ );
+};
+
+export default Map;
diff --git a/svgmap/src/components/map/MouseHandler.tsx b/svgmap/src/components/map/MouseHandler.tsx
new file mode 100644
index 0000000..3b627a7
--- /dev/null
+++ b/svgmap/src/components/map/MouseHandler.tsx
@@ -0,0 +1,110 @@
+import react, { useState } from 'react';
+import Viewport from './Viewport';
+
+interface MouseHandlerProperties {
+ boardSize: number;
+ shift: { x: number; y: number };
+ zoom: number;
+}
+
+const MouseHandler: react.FC = (
+ props: MouseHandlerProperties
+) => {
+ const initialMouseState = {
+ down: false,
+ starting: { x: -1, y: -1 },
+ timestamp: 0,
+ };
+
+ const [mouseState, setMouseState] = useState(initialMouseState);
+
+ const [shift, setShift] = useState(props.shift);
+ const [zoom, setZoom] = useState(props.zoom);
+
+ console.log('MouseHandler, mouseState: ' + JSON.stringify(mouseState));
+ console.log(
+ 'MouseHandler, shift: ' + JSON.stringify(shift) + ', zoom:' + zoom
+ );
+
+ const genericHandler = (event: any) => {
+ console.log(`Log - Event: ${event.type}`);
+ if (event.pageX !== undefined) {
+ console.log(
+ `Mouse : ${event.pageX}, ${event.pageY}, target: ${event.target}`
+ );
+ console.log(
+ `mouseState: ' ${JSON.stringify(mouseState)} (+${
+ Date.now() - mouseState.timestamp
+ }ms) `
+ );
+ return;
+ }
+ };
+
+ const mouseLeaveHandler = (event: any) => {
+ genericHandler(event);
+ setMouseState(initialMouseState);
+ };
+
+ const mouseDownHandler = (event: any) => {
+ genericHandler(event);
+ setMouseState({
+ down: true,
+ starting: { x: event.pageX, y: event.pageY },
+ timestamp: Date.now(),
+ });
+ };
+
+ const mouseUpHandler = (event: any) => {
+ genericHandler(event);
+ setMouseState(initialMouseState);
+ };
+
+ const mouseMoveHandler = (event: any) => {
+ if (mouseState.down && Date.now() - mouseState.timestamp > 5) {
+ genericHandler(event);
+ console.log(
+ `dispatch ${JSON.stringify({
+ x: event.pageX - mouseState.starting.x,
+ y: event.pageY - mouseState.starting.y,
+ })}`
+ );
+ setShift({
+ x: shift.x + (event.pageX - mouseState.starting.x),
+ y: shift.y + (event.pageY - mouseState.starting.y),
+ });
+ setMouseState({
+ down: true,
+ starting: {
+ x: event.pageX,
+ y: event.pageY,
+ },
+ timestamp: Date.now(),
+ });
+ }
+ };
+
+ const doubleClickHandler = (event: any) => {
+ genericHandler(event);
+ const newZoom = zoom * Math.SQRT2;
+ setShift({
+ x: shift.x + (shift.x - event.pageX) * (newZoom / zoom - 1),
+ y: shift.y + (shift.y - event.pageY) * (newZoom / zoom - 1),
+ });
+ setZoom(newZoom);
+ };
+
+ return (
+
+
+
+ );
+};
+
+export default MouseHandler;
diff --git a/svgmap/src/components/map/Viewport.tsx b/svgmap/src/components/map/Viewport.tsx
new file mode 100644
index 0000000..9310aa4
--- /dev/null
+++ b/svgmap/src/components/map/Viewport.tsx
@@ -0,0 +1,63 @@
+import react from 'react';
+
+interface ViewportProperties {
+ boardSize: number;
+ shift: { x: number; y: number };
+ zoom: number;
+}
+
+/**
+ *
+ * Let's call:
+ * - x and y the SVG coordinates
+ * - X and Y the screen coordinates (pixels on screen)
+ *
+ * X0 = (x + shift.x * zoom) * zoom
+ * or
+ * x = X0 * zoom - shift.x
+ * id for Y
+ *
+ * To add a new shift of S screen pixels, we need to apply a zoom of S/zoom
+ *
+ * How can we zoom so that X and x stay constant ?
+ *
+ * Knowing X0, x0, zoom0, zoom1 and shift.x0,
+ *
+ *
+ * X0 = (x0 + shift.x0 *zoom0) * zoom0
+ * X1 = (x1 + shift.x1 *zoom1) * zoom1
+ * X0 = X1 (=X)
+ * x0 = x1 (=x)
+ * =>
+ * (x + shift.x1*zoom1) * zoom1 = (x + shift.x0 * zoom0) * zoom0
+ * =>
+ * (x + shift.x1*zoom1) = (x + shift.x0*zoom0) * zoom0 / zoom1
+ * shift.x1 = ((x + shift.x0) * zoom0 / zoom1 - x) / zoom1
+ *
+ * x = 333
+ * 282 => -25,5
+ *
+ */
+
+/**
+ *
+ * viewBox={`${-props.shift.x} ${-props.shift.y} ${
+ props.boardSize / props.zoom
+ } ${props.boardSize / props.zoom}`}
+
+ *
+ */
+
+const Viewport: react.FC = (props: ViewportProperties) => {
+ return (
+
+ );
+};
+
+export default Viewport;
diff --git a/svgmap/templates/Template.tsx b/svgmap/templates/Template.tsx
new file mode 100644
index 0000000..8349dcc
--- /dev/null
+++ b/svgmap/templates/Template.tsx
@@ -0,0 +1,9 @@
+import react from 'react';
+
+interface MapProperties {}
+
+const Map: react.FC = (props: MapProperties) => {
+ return <>>;
+};
+
+export default Map;