import React, { useContext, useEffect, useState, useRef } from "react"; import { Group, Rect, Text } from "react-konva"; import AuthContext from "../../contexts/AuthContext"; import MapInteractionContext from "../../contexts/MapInteractionContext"; import * as Vector2 from "../../helpers/vector2"; import colors from "../../helpers/colors"; const snappingThreshold = 1 / 5; const textPadding = 4; function MapNote({ note, map, onNoteChange, onNoteMenuOpen, draggable }) { const { userId } = useContext(AuthContext); const { mapWidth, mapHeight } = useContext(MapInteractionContext); const noteWidth = map && (mapWidth / map.grid.size.x) * note.size; const noteHeight = map && (mapHeight / map.grid.size.y) * note.size; function handleClick(event) { if (draggable) { const noteNode = event.target; onNoteMenuOpen && onNoteMenuOpen(note.id, noteNode); } } function handleDragMove(event) { const noteGroup = event.target; // Snap to corners of grid if (map.snapToGrid) { const offset = Vector2.multiply(map.grid.inset.topLeft, { x: mapWidth, y: mapHeight, }); const position = { x: noteGroup.x() + noteGroup.width() / 2, y: noteGroup.y() + noteGroup.height() / 2, }; const gridSize = { x: (mapWidth * (map.grid.inset.bottomRight.x - map.grid.inset.topLeft.x)) / map.grid.size.x, y: (mapHeight * (map.grid.inset.bottomRight.y - map.grid.inset.topLeft.y)) / map.grid.size.y, }; // Transform into offset space, round, then transform back const gridSnap = Vector2.add( Vector2.roundTo(Vector2.subtract(position, offset), gridSize), offset ); const gridDistance = Vector2.length(Vector2.subtract(gridSnap, position)); const minGrid = Vector2.min(gridSize); if (gridDistance < minGrid * snappingThreshold) { noteGroup.x(gridSnap.x - noteGroup.width() / 2); noteGroup.y(gridSnap.y - noteGroup.height() / 2); } } } function handleDragEnd(event) { const noteGroup = event.target; onNoteChange && onNoteChange({ ...note, x: noteGroup.x() / mapWidth, y: noteGroup.y() / mapHeight, lastModifiedBy: userId, lastModified: Date.now(), }); } const [fontSize, setFontSize] = useState(1); useEffect(() => { const text = textRef.current; function findFontSize() { // Create an array from 4 to 18 scaled to the note size const sizes = Array.from( { length: 14 * note.size }, (_, i) => i + 4 * note.size ); return sizes.reduce((prev, curr) => { text.fontSize(curr); const width = text.getTextWidth() + textPadding * 2; if (width < noteWidth) { return curr; } else { return prev; } }); } setFontSize(findFontSize()); }, [note, noteWidth]); const textRef = useRef(); return ( {/* Use an invisible text block to work out text sizing */} ); } export default MapNote;