Files
grungnet/src/components/map/MapNote.js

142 lines
4.0 KiB
JavaScript
Raw Normal View History

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;