2020-11-04 15:03:34 +11:00
|
|
|
import React, { useContext, useEffect, useState, useRef } from "react";
|
|
|
|
|
import { Group, Rect, Text } from "react-konva";
|
2020-11-03 17:15:39 +11:00
|
|
|
|
2020-11-04 15:03:34 +11:00
|
|
|
import AuthContext from "../../contexts/AuthContext";
|
2020-11-03 17:15:39 +11:00
|
|
|
import MapInteractionContext from "../../contexts/MapInteractionContext";
|
|
|
|
|
|
2020-11-04 15:03:34 +11:00
|
|
|
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);
|
2020-11-03 17:15:39 +11:00
|
|
|
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;
|
|
|
|
|
|
2020-11-04 15:03:34 +11:00
|
|
|
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();
|
|
|
|
|
|
2020-11-03 17:15:39 +11:00
|
|
|
return (
|
2020-11-04 15:03:34 +11:00
|
|
|
<Group
|
|
|
|
|
onClick={handleClick}
|
|
|
|
|
onTap={handleClick}
|
|
|
|
|
x={note.x * mapWidth}
|
|
|
|
|
y={note.y * mapHeight}
|
|
|
|
|
width={noteWidth}
|
|
|
|
|
height={noteHeight}
|
|
|
|
|
offsetX={noteWidth / 2}
|
|
|
|
|
offsetY={noteHeight / 2}
|
|
|
|
|
draggable={draggable}
|
|
|
|
|
onDragEnd={handleDragEnd}
|
|
|
|
|
onDragMove={handleDragMove}
|
|
|
|
|
>
|
2020-11-03 17:15:39 +11:00
|
|
|
<Rect
|
|
|
|
|
width={noteWidth}
|
|
|
|
|
height={noteHeight}
|
|
|
|
|
shadowColor="rgba(0, 0, 0, 0.16)"
|
|
|
|
|
shadowOffset={{ x: 0, y: 3 }}
|
|
|
|
|
shadowBlur={6}
|
2020-11-04 15:03:34 +11:00
|
|
|
cornerRadius={0.25}
|
|
|
|
|
fill={colors[note.color]}
|
|
|
|
|
/>
|
|
|
|
|
<Text
|
|
|
|
|
text={note.text}
|
|
|
|
|
fill="black"
|
|
|
|
|
align="center"
|
|
|
|
|
verticalAlign="middle"
|
|
|
|
|
padding={textPadding}
|
|
|
|
|
fontSize={fontSize}
|
|
|
|
|
wrap="word"
|
|
|
|
|
width={noteWidth}
|
|
|
|
|
height={noteHeight}
|
2020-11-03 17:15:39 +11:00
|
|
|
/>
|
2020-11-04 15:03:34 +11:00
|
|
|
{/* Use an invisible text block to work out text sizing */}
|
|
|
|
|
<Text visible={false} ref={textRef} text={note.text} wrap="none" />
|
2020-11-03 17:15:39 +11:00
|
|
|
</Group>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export default MapNote;
|