]> git.localhorst.tv Git - alttp.git/blobdiff - resources/js/components/tracker/Canvas.js
draggable tracker icons
[alttp.git] / resources / js / components / tracker / Canvas.js
index 392f2a7dc63f35ca27af8bfc71476e96d9eecae3..fab194219c7d56ec9dee81d34c27b5cce9a2c340 100644 (file)
@@ -1,8 +1,12 @@
+import { drag } from 'd3-drag';
+import { select } from 'd3-selection';
 import React from 'react';
 
 import Dungeons from './Dungeons';
 import Items from './Items';
 import Map from './Map';
+import ToggleIcon from './ToggleIcon';
+import ZeldaIcon from '../common/ZeldaIcon';
 import { shouldShowDungeonItem } from '../../helpers/tracker';
 import { useTracker } from '../../hooks/tracker';
 
@@ -34,7 +38,8 @@ const LAYOUTS = {
 };
 
 const Canvas = () => {
-       const { config } = useTracker();
+       const [dragging, setDragging] = React.useState(null);
+       const { addPin, config, pins, removePin } = useTracker();
 
        const layout = React.useMemo(() => {
                if (config.mapLayout === 'vertical') {
@@ -57,6 +62,62 @@ const Canvas = () => {
                }
        }, [config]);
 
+       React.useEffect(() => {
+               const canvas = select('.canvas');
+               const bbox = canvas.select('.background');
+               const start = { x: 0, y: 0 };
+               const onStart = function (e) {
+                       const bounds = bbox.node().getBoundingClientRect();
+                       start.x = e.x;
+                       start.y = e.y;
+                       setDragging({
+                               icon: this.dataset['icon'],
+                               x: (e.x - bounds.x) / bounds.width,
+                               y: (e.y - bounds.y) / bounds.height,
+                       });
+               };
+               const onDrag = function (e) {
+                       const bounds = bbox.node().getBoundingClientRect();
+                       setDragging({
+                               icon: this.dataset['icon'],
+                               x: (e.x - bounds.x) / bounds.width,
+                               y: (e.y - bounds.y) / bounds.height,
+                       });
+               };
+               const onEnd = function (e) {
+                       const bounds = bbox.node().getBoundingClientRect();
+                       setDragging(null);
+                       const distance = Math.max(Math.abs(e.x - start.x), Math.abs(e.y - start.y));
+                       if (distance > 5) {
+                               addPin({
+                                       icon: this.dataset['icon'],
+                                       x: (e.x - bounds.x) / bounds.width,
+                                       y: (e.y - bounds.y) / bounds.height,
+                               });
+                               if (this.classList.contains('map-pin')) {
+                                       let id = 0;
+                                       this.classList.forEach(name => {
+                                               if (name.startsWith('map-pin-')) {
+                                                       id = parseInt(name.substr(8), 10);
+                                               }
+                                       });
+                                       removePin({ id });
+                               }
+                       }
+               };
+               const selection = canvas.selectAll('.toggle-icon');
+               const draggable = drag()
+                       .container(bbox)
+                       .clickDistance(5)
+                       .on('start', onStart)
+                       .on('drag', onDrag)
+                       .on('end', onEnd);
+               selection.call(draggable);
+               return () => {
+                       selection.on('.drag', null);
+               };
+       }, [pins, removePin]);
+
        return <svg
                xmlns="http://www.w3.org/2000/svg"
                className="canvas"
@@ -68,6 +129,13 @@ const Canvas = () => {
                        e.stopPropagation();
                }}
        >
+               <rect
+                       className="background"
+                       fill="transparent"
+                       x="0" y="0"
+                       width={layout.width}
+                       height={layout.height}
+               />
                <g className="items" transform={layout.itemsTransform}>
                        <Items />
                </g>
@@ -77,6 +145,30 @@ const Canvas = () => {
                <g className="tracker-map" transform={layout.mapTransform}>
                        <Map />
                </g>
+               <g className="pins">
+                       {pins.map(pin =>
+                               <g
+                                       key={pin.id}
+                                       transform={
+                                               `translate(${pin.x * layout.width} ${pin.y * layout.height}) scale(3)`
+                                       }
+                               >
+                                       <ToggleIcon
+                                               className={`map-pin map-pin-${pin.id}`}
+                                               controller={ToggleIcon.pinController(pin, removePin)}
+                                               icons={[pin.icon]}
+                                               svg
+                                       />
+                               </g>
+                       )}
+               </g>
+               {dragging ?
+                       <g transform={
+                               `translate(${dragging.x * layout.width} ${dragging.y * layout.height}) scale(4)`
+                       }>
+                               <ZeldaIcon name={dragging.icon} svg />
+                       </g>
+               : null}
        </svg>;
 };