typescript
This commit is contained in:
parent
68c1c6db0c
commit
d80bfa2f1e
@ -1,40 +1,31 @@
|
||||
// Load Diff for auto complete
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
import { Diff } from "deep-diff";
|
||||
|
||||
import { diff, revertChanges } from "../helpers/diff";
|
||||
import cloneDeep from "lodash.clonedeep";
|
||||
|
||||
/**
|
||||
* @callback ActionUpdate
|
||||
* @param {any} state
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implementation of the Command Pattern
|
||||
* Wraps an update function with internal state to support undo
|
||||
*/
|
||||
class Action {
|
||||
class Action<State> {
|
||||
/**
|
||||
* The update function called with the current state and should return the updated state
|
||||
* This is implemented in the child class
|
||||
*
|
||||
* @type {ActionUpdate}
|
||||
*/
|
||||
update;
|
||||
update(state: State): State {
|
||||
return state;
|
||||
}
|
||||
|
||||
/**
|
||||
* The changes caused by the last state update
|
||||
* @type {Diff}
|
||||
*/
|
||||
changes;
|
||||
changes: Diff<State, State>[] | undefined;
|
||||
|
||||
/**
|
||||
* Executes the action update on the state
|
||||
* @param {any} state The current state to update
|
||||
* @returns {any} The updated state
|
||||
* @param {State} state The current state to update
|
||||
*/
|
||||
execute(state) {
|
||||
execute(state: State): State {
|
||||
if (state && this.update) {
|
||||
let newState = this.update(cloneDeep(state));
|
||||
this.changes = diff(state, newState);
|
||||
@ -45,10 +36,10 @@ class Action {
|
||||
|
||||
/**
|
||||
* Reverts the changes caused by the last call of `execute`
|
||||
* @param {any} state The current state to perform the undo on
|
||||
* @returns {any} The state with the last changes reverted
|
||||
* @param {State} state The current state to perform the undo on
|
||||
* @returns {State} The state with the last changes reverted
|
||||
*/
|
||||
undo(state) {
|
||||
undo(state: State): State {
|
||||
if (state && this.changes) {
|
||||
let revertedState = cloneDeep(state);
|
||||
revertChanges(revertedState, this.changes);
|
@ -1,15 +0,0 @@
|
||||
import Action from "./Action";
|
||||
|
||||
class AddShapeAction extends Action {
|
||||
constructor(shapes) {
|
||||
super();
|
||||
this.update = (shapesById) => {
|
||||
for (let shape of shapes) {
|
||||
shapesById[shape.id] = shape;
|
||||
}
|
||||
return shapesById;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export default AddShapeAction;
|
21
src/actions/AddStatesAction.ts
Normal file
21
src/actions/AddStatesAction.ts
Normal file
@ -0,0 +1,21 @@
|
||||
import Action from "./Action";
|
||||
|
||||
import { ID } from "../types/Action";
|
||||
|
||||
class AddStatesAction<State extends ID> extends Action<Record<string, State>> {
|
||||
states: State[];
|
||||
|
||||
constructor(states: State[]) {
|
||||
super();
|
||||
this.states = states;
|
||||
}
|
||||
|
||||
update(statesById: Record<string, State>) {
|
||||
for (let state of this.states) {
|
||||
statesById[state.id] = state;
|
||||
}
|
||||
return statesById;
|
||||
}
|
||||
}
|
||||
|
||||
export default AddStatesAction;
|
41
src/actions/CutFogAction.ts
Normal file
41
src/actions/CutFogAction.ts
Normal file
@ -0,0 +1,41 @@
|
||||
import polygonClipping from "polygon-clipping";
|
||||
|
||||
import Action from "./Action";
|
||||
import {
|
||||
addPolygonDifferenceToFog,
|
||||
addPolygonIntersectionToFog,
|
||||
fogToGeometry,
|
||||
} from "../helpers/actions";
|
||||
|
||||
import { Fog, FogState } from "../types/Fog";
|
||||
|
||||
class CutFogAction extends Action<FogState> {
|
||||
fogs: Fog[];
|
||||
|
||||
constructor(fog: Fog[]) {
|
||||
super();
|
||||
this.fogs = fog;
|
||||
}
|
||||
|
||||
update(fogsById: FogState): FogState {
|
||||
let actionGeom = this.fogs.map(fogToGeometry);
|
||||
let cutFogs: FogState = {};
|
||||
for (let fog of Object.values(fogsById)) {
|
||||
const fogGeom = fogToGeometry(fog);
|
||||
try {
|
||||
const difference = polygonClipping.difference(fogGeom, ...actionGeom);
|
||||
const intersection = polygonClipping.intersection(
|
||||
fogGeom,
|
||||
...actionGeom
|
||||
);
|
||||
addPolygonDifferenceToFog(fog, difference, cutFogs);
|
||||
addPolygonIntersectionToFog(fog, intersection, cutFogs);
|
||||
} catch {
|
||||
console.error("Unable to find intersection for fogs");
|
||||
}
|
||||
}
|
||||
return cutFogs;
|
||||
}
|
||||
}
|
||||
|
||||
export default CutFogAction;
|
@ -1,38 +0,0 @@
|
||||
import polygonClipping from "polygon-clipping";
|
||||
|
||||
import Action from "./Action";
|
||||
import {
|
||||
addPolygonDifferenceToShapes,
|
||||
addPolygonIntersectionToShapes,
|
||||
shapeToGeometry,
|
||||
} from "../helpers/actions";
|
||||
|
||||
class CutShapeAction extends Action {
|
||||
constructor(shapes) {
|
||||
super();
|
||||
this.update = (shapesById) => {
|
||||
let actionGeom = shapes.map(shapeToGeometry);
|
||||
let cutShapes = {};
|
||||
for (let shape of Object.values(shapesById)) {
|
||||
const shapeGeom = shapeToGeometry(shape);
|
||||
try {
|
||||
const difference = polygonClipping.difference(
|
||||
shapeGeom,
|
||||
...actionGeom
|
||||
);
|
||||
const intersection = polygonClipping.intersection(
|
||||
shapeGeom,
|
||||
...actionGeom
|
||||
);
|
||||
addPolygonDifferenceToShapes(shape, difference, cutShapes);
|
||||
addPolygonIntersectionToShapes(shape, intersection, cutShapes);
|
||||
} catch {
|
||||
console.error("Unable to find intersection for shapes");
|
||||
}
|
||||
}
|
||||
return cutShapes;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export default CutShapeAction;
|
@ -1,17 +0,0 @@
|
||||
import Action from "./Action";
|
||||
|
||||
class EditShapeAction extends Action {
|
||||
constructor(shapes) {
|
||||
super();
|
||||
this.update = (shapesById) => {
|
||||
for (let edit of shapes) {
|
||||
if (edit.id in shapesById) {
|
||||
shapesById[edit.id] = { ...shapesById[edit.id], ...edit };
|
||||
}
|
||||
}
|
||||
return shapesById;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export default EditShapeAction;
|
23
src/actions/EditStatesAction.ts
Normal file
23
src/actions/EditStatesAction.ts
Normal file
@ -0,0 +1,23 @@
|
||||
import Action from "./Action";
|
||||
|
||||
import { ID } from "../types/Action";
|
||||
|
||||
class EditStatesAction<State extends ID> extends Action<Record<string, State>> {
|
||||
edits: Partial<State>[];
|
||||
|
||||
constructor(edits: Partial<State>[]) {
|
||||
super();
|
||||
this.edits = edits;
|
||||
}
|
||||
|
||||
update(statesById: Record<string, State>) {
|
||||
for (let edit of this.edits) {
|
||||
if (edit.id !== undefined && edit.id in statesById) {
|
||||
statesById[edit.id] = { ...statesById[edit.id], ...edit };
|
||||
}
|
||||
}
|
||||
return statesById;
|
||||
}
|
||||
}
|
||||
|
||||
export default EditStatesAction;
|
@ -1,13 +0,0 @@
|
||||
import Action from "./Action";
|
||||
import { omit } from "../helpers/shared";
|
||||
|
||||
class RemoveShapeAction extends Action {
|
||||
constructor(shapeIds) {
|
||||
super();
|
||||
this.update = (shapesById) => {
|
||||
return omit(shapesById, shapeIds);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export default RemoveShapeAction;
|
21
src/actions/RemoveStatesAction.ts
Normal file
21
src/actions/RemoveStatesAction.ts
Normal file
@ -0,0 +1,21 @@
|
||||
import Action from "./Action";
|
||||
import { omit } from "../helpers/shared";
|
||||
|
||||
import { ID } from "../types/Action";
|
||||
|
||||
class RemoveStatesAction<State extends ID> extends Action<
|
||||
Record<string, State>
|
||||
> {
|
||||
stateIds: string[];
|
||||
|
||||
constructor(stateIds: string[]) {
|
||||
super();
|
||||
this.stateIds = stateIds;
|
||||
}
|
||||
|
||||
update(statesById: Record<string, State>) {
|
||||
return omit(statesById, this.stateIds);
|
||||
}
|
||||
}
|
||||
|
||||
export default RemoveStatesAction;
|
32
src/actions/SubtractFogAction.ts
Normal file
32
src/actions/SubtractFogAction.ts
Normal file
@ -0,0 +1,32 @@
|
||||
import polygonClipping from "polygon-clipping";
|
||||
|
||||
import Action from "./Action";
|
||||
import { addPolygonDifferenceToFog, fogToGeometry } from "../helpers/actions";
|
||||
|
||||
import { Fog, FogState } from "../types/Fog";
|
||||
|
||||
class SubtractFogAction extends Action<FogState> {
|
||||
fogs: Fog[];
|
||||
|
||||
constructor(fogs: Fog[]) {
|
||||
super();
|
||||
this.fogs = fogs;
|
||||
}
|
||||
|
||||
update(fogsById: FogState): FogState {
|
||||
const actionGeom = this.fogs.map(fogToGeometry);
|
||||
let subtractedFogs: FogState = {};
|
||||
for (let fog of Object.values(fogsById)) {
|
||||
const fogGeom = fogToGeometry(fog);
|
||||
try {
|
||||
const difference = polygonClipping.difference(fogGeom, ...actionGeom);
|
||||
addPolygonDifferenceToFog(fog, difference, subtractedFogs);
|
||||
} catch {
|
||||
console.error("Unable to find difference for fogs");
|
||||
}
|
||||
}
|
||||
return subtractedFogs;
|
||||
}
|
||||
}
|
||||
|
||||
export default SubtractFogAction;
|
@ -1,32 +0,0 @@
|
||||
import polygonClipping from "polygon-clipping";
|
||||
|
||||
import Action from "./Action";
|
||||
import {
|
||||
addPolygonDifferenceToShapes,
|
||||
shapeToGeometry,
|
||||
} from "../helpers/actions";
|
||||
|
||||
class SubtractShapeAction extends Action {
|
||||
constructor(shapes) {
|
||||
super();
|
||||
this.update = (shapesById) => {
|
||||
const actionGeom = shapes.map(shapeToGeometry);
|
||||
let subtractedShapes = {};
|
||||
for (let shape of Object.values(shapesById)) {
|
||||
const shapeGeom = shapeToGeometry(shape);
|
||||
try {
|
||||
const difference = polygonClipping.difference(
|
||||
shapeGeom,
|
||||
...actionGeom
|
||||
);
|
||||
addPolygonDifferenceToShapes(shape, difference, subtractedShapes);
|
||||
} catch {
|
||||
console.error("Unable to find difference for shapes");
|
||||
}
|
||||
}
|
||||
return subtractedShapes;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export default SubtractShapeAction;
|
@ -1,13 +0,0 @@
|
||||
import AddShapeAction from "./AddShapeAction";
|
||||
import CutShapeAction from "./CutShapeAction";
|
||||
import EditShapeAction from "./EditShapeAction";
|
||||
import RemoveShapeAction from "./RemoveShapeAction";
|
||||
import SubtractShapeAction from "./SubtractShapeAction";
|
||||
|
||||
export {
|
||||
AddShapeAction,
|
||||
CutShapeAction,
|
||||
EditShapeAction,
|
||||
RemoveShapeAction,
|
||||
SubtractShapeAction,
|
||||
};
|
13
src/actions/index.ts
Normal file
13
src/actions/index.ts
Normal file
@ -0,0 +1,13 @@
|
||||
import AddStatesAction from "./AddStatesAction";
|
||||
import CutFogAction from "./CutFogAction";
|
||||
import EditStatesAction from "./EditStatesAction";
|
||||
import RemoveStatesAction from "./RemoveStatesAction";
|
||||
import SubtractFogAction from "./SubtractFogAction";
|
||||
|
||||
export {
|
||||
AddStatesAction,
|
||||
CutFogAction,
|
||||
EditStatesAction,
|
||||
RemoveStatesAction,
|
||||
SubtractFogAction,
|
||||
};
|
@ -78,7 +78,7 @@ Slider.defaultProps = {
|
||||
value: 0,
|
||||
ml: 0,
|
||||
mr: 0,
|
||||
labelFunc: (value: any) => value,
|
||||
labelFunc: (value: number) => value,
|
||||
};
|
||||
|
||||
export default Slider;
|
||||
|
@ -1,5 +1,7 @@
|
||||
import Modal from "react-modal";
|
||||
import { useThemeUI, Close } from "theme-ui";
|
||||
import { RequestCloseEventHandler } from "../../types/Events";
|
||||
import CSS from "csstype";
|
||||
|
||||
function Banner({
|
||||
isOpen,
|
||||
@ -8,11 +10,11 @@ function Banner({
|
||||
allowClose,
|
||||
backgroundColor,
|
||||
}: {
|
||||
isOpen: boolean,
|
||||
onRequestClose: any,
|
||||
children: any,
|
||||
allowClose: boolean,
|
||||
backgroundColor?: any
|
||||
isOpen: boolean;
|
||||
onRequestClose: RequestCloseEventHandler;
|
||||
children: React.ReactNode;
|
||||
allowClose: boolean;
|
||||
backgroundColor?: CSS.Property.Color;
|
||||
}) {
|
||||
const { theme } = useThemeUI();
|
||||
|
||||
@ -23,7 +25,8 @@ function Banner({
|
||||
style={{
|
||||
overlay: { bottom: "0", top: "initial", zIndex: 2000 },
|
||||
content: {
|
||||
backgroundColor: backgroundColor || theme.colors?.highlight,
|
||||
backgroundColor:
|
||||
backgroundColor || (theme.colors?.highlight as CSS.Property.Color),
|
||||
color: "hsl(210, 50%, 96%)",
|
||||
top: "initial",
|
||||
left: "50%",
|
||||
|
@ -2,7 +2,13 @@ import { Box, Text } from "theme-ui";
|
||||
|
||||
import Banner from "./Banner";
|
||||
|
||||
function ErrorBanner({ error, onRequestClose }: { error: Error | undefined, onRequestClose: any }) {
|
||||
function ErrorBanner({
|
||||
error,
|
||||
onRequestClose,
|
||||
}: {
|
||||
error: Error | undefined;
|
||||
onRequestClose;
|
||||
}) {
|
||||
return (
|
||||
<Banner isOpen={!!error} onRequestClose={onRequestClose}>
|
||||
<Box p={1}>
|
||||
|
@ -35,7 +35,7 @@ type DiceInteractionProps = {
|
||||
canvas: HTMLCanvasElement | WebGLRenderingContext;
|
||||
}) => void;
|
||||
onPointerDown: () => void;
|
||||
onPointerUp: () => any;
|
||||
onPointerUp: () => void;
|
||||
};
|
||||
|
||||
function DiceInteraction({
|
||||
|
@ -21,12 +21,21 @@ import NoteMenu from "../note/NoteMenu";
|
||||
import NoteDragOverlay from "../note/NoteDragOverlay";
|
||||
|
||||
import {
|
||||
AddShapeAction,
|
||||
CutShapeAction,
|
||||
EditShapeAction,
|
||||
RemoveShapeAction,
|
||||
AddStatesAction,
|
||||
CutFogAction,
|
||||
EditStatesAction,
|
||||
RemoveStatesAction,
|
||||
} from "../../actions";
|
||||
import Session from "../../network/Session";
|
||||
import { Drawing } from "../../types/Drawing";
|
||||
import { Fog } from "../../types/Fog";
|
||||
import { Map, MapToolId } from "../../types/Map";
|
||||
import { MapState } from "../../types/MapState";
|
||||
import { Settings } from "../../types/Settings";
|
||||
import {
|
||||
MapChangeEventHandler,
|
||||
MapResetEventHandler,
|
||||
} from "../../types/Events";
|
||||
|
||||
function Map({
|
||||
map,
|
||||
@ -51,43 +60,39 @@ function Map({
|
||||
disabledTokens,
|
||||
session,
|
||||
}: {
|
||||
map: any;
|
||||
map: Map;
|
||||
mapState: MapState;
|
||||
mapActions: any;
|
||||
onMapTokenStateChange: any;
|
||||
onMapTokenStateRemove: any;
|
||||
onMapChange: any;
|
||||
onMapReset: any;
|
||||
onMapDraw: any;
|
||||
onMapDrawUndo: any;
|
||||
onMapDrawRedo: any;
|
||||
onFogDraw: any;
|
||||
onFogDrawUndo: any;
|
||||
onFogDrawRedo: any;
|
||||
onMapNoteChange: any;
|
||||
onMapNoteRemove: any;
|
||||
mapActions: ;
|
||||
onMapTokenStateChange: ;
|
||||
onMapTokenStateRemove: ;
|
||||
onMapChange: MapChangeEventHandler;
|
||||
onMapReset: MapResetEventHandler;
|
||||
onMapDraw: ;
|
||||
onMapDrawUndo: ;
|
||||
onMapDrawRedo: ;
|
||||
onFogDraw: ;
|
||||
onFogDrawUndo: ;
|
||||
onFogDrawRedo: ;
|
||||
onMapNoteChange: ;
|
||||
onMapNoteRemove: ;
|
||||
allowMapDrawing: boolean;
|
||||
allowFogDrawing: boolean;
|
||||
allowMapChange: boolean;
|
||||
allowNoteEditing: boolean;
|
||||
disabledTokens: any;
|
||||
disabledTokens: ;
|
||||
session: Session;
|
||||
}) {
|
||||
const { addToast } = useToasts();
|
||||
|
||||
const { tokensById } = useTokenData();
|
||||
|
||||
const [selectedToolId, setSelectedToolId] = useState("move");
|
||||
const { settings, setSettings }: { settings: any; setSettings: any } =
|
||||
useSettings();
|
||||
const [selectedToolId, setSelectedToolId] = useState<MapToolId>("move");
|
||||
const { settings, setSettings } = useSettings();
|
||||
|
||||
function handleToolSettingChange(tool: any, change: any) {
|
||||
setSettings((prevSettings: any) => ({
|
||||
function handleToolSettingChange(change: Partial<Settings>) {
|
||||
setSettings((prevSettings) => ({
|
||||
...prevSettings,
|
||||
[tool]: {
|
||||
...prevSettings[tool],
|
||||
...change,
|
||||
},
|
||||
...change,
|
||||
}));
|
||||
}
|
||||
|
||||
@ -96,7 +101,7 @@ function Map({
|
||||
|
||||
function handleToolAction(action: string) {
|
||||
if (action === "eraseAll") {
|
||||
onMapDraw(new RemoveShapeAction(drawShapes.map((s) => s.id)));
|
||||
onMapDraw(new RemoveStatesAction(drawShapes.map((s) => s.id)));
|
||||
}
|
||||
if (action === "mapUndo") {
|
||||
onMapDrawUndo();
|
||||
@ -112,28 +117,28 @@ function Map({
|
||||
}
|
||||
}
|
||||
|
||||
function handleMapShapeAdd(shape: Shape) {
|
||||
onMapDraw(new AddShapeAction([shape]));
|
||||
function handleMapShapeAdd(shape: Drawing) {
|
||||
onMapDraw(new AddStatesAction([shape]));
|
||||
}
|
||||
|
||||
function handleMapShapesRemove(shapeIds: string[]) {
|
||||
onMapDraw(new RemoveShapeAction(shapeIds));
|
||||
onMapDraw(new RemoveStatesAction(shapeIds));
|
||||
}
|
||||
|
||||
function handleFogShapesAdd(shapes: Shape[]) {
|
||||
onFogDraw(new AddShapeAction(shapes));
|
||||
function handleFogShapesAdd(shapes: Fog[]) {
|
||||
onFogDraw(new AddStatesAction(shapes));
|
||||
}
|
||||
|
||||
function handleFogShapesCut(shapes: Shape[]) {
|
||||
onFogDraw(new CutShapeAction(shapes));
|
||||
function handleFogShapesCut(shapes: Fog[]) {
|
||||
onFogDraw(new CutFogAction(shapes));
|
||||
}
|
||||
|
||||
function handleFogShapesRemove(shapeIds: string[]) {
|
||||
onFogDraw(new RemoveShapeAction(shapeIds));
|
||||
onFogDraw(new RemoveStatesAction(shapeIds));
|
||||
}
|
||||
|
||||
function handleFogShapesEdit(shapes: Shape[]) {
|
||||
onFogDraw(new EditShapeAction(shapes));
|
||||
function handleFogShapesEdit(shapes: Partial<Fog>[]) {
|
||||
onFogDraw(new EditStatesAction(shapes));
|
||||
}
|
||||
|
||||
const disabledControls = [];
|
||||
@ -155,7 +160,10 @@ function Map({
|
||||
disabledControls.push("note");
|
||||
}
|
||||
|
||||
const disabledSettings: { fog: any[]; drawing: any[] } = {
|
||||
const disabledSettings: {
|
||||
fog: string[];
|
||||
drawing: string[];
|
||||
} = {
|
||||
fog: [],
|
||||
drawing: [],
|
||||
};
|
||||
@ -197,19 +205,10 @@ function Map({
|
||||
/>
|
||||
);
|
||||
|
||||
const [isTokenMenuOpen, setIsTokenMenuOpen]: [
|
||||
isTokenMenuOpen: boolean,
|
||||
setIsTokenMenuOpen: React.Dispatch<React.SetStateAction<boolean>>
|
||||
] = useState<boolean>(false);
|
||||
const [tokenMenuOptions, setTokenMenuOptions]: [
|
||||
tokenMenuOptions: any,
|
||||
setTokenMenuOptions: any
|
||||
] = useState({});
|
||||
const [tokenDraggingOptions, setTokenDraggingOptions]: [
|
||||
tokenDraggingOptions: any,
|
||||
setTokenDragginOptions: any
|
||||
] = useState();
|
||||
function handleTokenMenuOpen(tokenStateId: string, tokenImage: any) {
|
||||
const [isTokenMenuOpen, setIsTokenMenuOpen] = useState<boolean>(false);
|
||||
const [tokenMenuOptions, setTokenMenuOptions] = useState({});
|
||||
const [tokenDraggingOptions, setTokenDraggingOptions] = useState();
|
||||
function handleTokenMenuOpen(tokenStateId: string, tokenImage) {
|
||||
setTokenMenuOptions({ tokenStateId, tokenImage });
|
||||
setIsTokenMenuOpen(true);
|
||||
}
|
||||
@ -240,7 +239,7 @@ function Map({
|
||||
|
||||
const tokenDragOverlay = tokenDraggingOptions && (
|
||||
<TokenDragOverlay
|
||||
onTokenStateRemove={(state: any) => {
|
||||
onTokenStateRemove={(state) => {
|
||||
onMapTokenStateRemove(state);
|
||||
setTokenDraggingOptions(null);
|
||||
}}
|
||||
@ -292,14 +291,14 @@ function Map({
|
||||
);
|
||||
|
||||
const [isNoteMenuOpen, setIsNoteMenuOpen] = useState<boolean>(false);
|
||||
const [noteMenuOptions, setNoteMenuOptions] = useState<any>({});
|
||||
const [noteDraggingOptions, setNoteDraggingOptions] = useState<any>();
|
||||
function handleNoteMenuOpen(noteId: string, noteNode: any) {
|
||||
const [noteMenuOptions, setNoteMenuOptions] = useState({});
|
||||
const [noteDraggingOptions, setNoteDraggingOptions] = useState();
|
||||
function handleNoteMenuOpen(noteId: string, noteNode) {
|
||||
setNoteMenuOptions({ noteId, noteNode });
|
||||
setIsNoteMenuOpen(true);
|
||||
}
|
||||
|
||||
function sortNotes(a: any, b: any, noteDraggingOptions: any) {
|
||||
function sortNotes(a, b, noteDraggingOptions) {
|
||||
if (
|
||||
noteDraggingOptions &&
|
||||
noteDraggingOptions.dragging &&
|
||||
@ -338,7 +337,7 @@ function Map({
|
||||
allowNoteEditing &&
|
||||
(selectedToolId === "note" || selectedToolId === "move")
|
||||
}
|
||||
onNoteDragStart={(e: any, noteId: any) =>
|
||||
onNoteDragStart={(e, noteId) =>
|
||||
setNoteDraggingOptions({ dragging: true, noteId, noteGroup: e.target })
|
||||
}
|
||||
onNoteDragEnd={() =>
|
||||
@ -364,7 +363,7 @@ function Map({
|
||||
dragging={!!(noteDraggingOptions && noteDraggingOptions.dragging)}
|
||||
noteGroup={noteDraggingOptions && noteDraggingOptions.noteGroup}
|
||||
noteId={noteDraggingOptions && noteDraggingOptions.noteId}
|
||||
onNoteRemove={(noteId: any) => {
|
||||
onNoteRemove={(noteId) => {
|
||||
onMapNoteRemove(noteId);
|
||||
setNoteDraggingOptions(null);
|
||||
}}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import React, { useState, Fragment } from "react";
|
||||
import { useState, Fragment } from "react";
|
||||
import { IconButton, Flex, Box } from "theme-ui";
|
||||
|
||||
import RadioIconButton from "../RadioIconButton";
|
||||
@ -21,21 +21,26 @@ import FullScreenExitIcon from "../../icons/FullScreenExitIcon";
|
||||
import NoteToolIcon from "../../icons/NoteToolIcon";
|
||||
|
||||
import useSetting from "../../hooks/useSetting";
|
||||
import { Map } from "../../types/Map";
|
||||
import { Map, MapTool, MapToolId } from "../../types/Map";
|
||||
import { MapState } from "../../types/MapState";
|
||||
import {
|
||||
MapChangeEventHandler,
|
||||
MapResetEventHandler,
|
||||
} from "../../types/Events";
|
||||
import { Settings } from "../../types/Settings";
|
||||
|
||||
type MapControlsProps = {
|
||||
onMapChange: () => void;
|
||||
onMapReset: () => void;
|
||||
onMapChange: MapChangeEventHandler;
|
||||
onMapReset: MapResetEventHandler;
|
||||
currentMap?: Map;
|
||||
currentMapState?: MapState;
|
||||
selectedToolId: string;
|
||||
onSelectedToolChange: () => void;
|
||||
toolSettings: any;
|
||||
onToolSettingChange: () => void;
|
||||
onToolAction: () => void;
|
||||
selectedToolId: MapToolId;
|
||||
onSelectedToolChange: (toolId: MapToolId) => void;
|
||||
toolSettings: Settings;
|
||||
onToolSettingChange: (change: Partial<Settings>) => void;
|
||||
onToolAction: (actionId: string) => void;
|
||||
disabledControls: string[];
|
||||
disabledSettings: string[];
|
||||
disabledSettings: Partial<Record<keyof Settings, string[]>>;
|
||||
};
|
||||
|
||||
function MapContols({
|
||||
@ -54,7 +59,7 @@ function MapContols({
|
||||
const [isExpanded, setIsExpanded] = useState(true);
|
||||
const [fullScreen, setFullScreen] = useSetting("map.fullScreen");
|
||||
|
||||
const toolsById = {
|
||||
const toolsById: Record<string, MapTool> = {
|
||||
move: {
|
||||
id: "move",
|
||||
icon: <MoveToolIcon />,
|
||||
@ -89,7 +94,14 @@ function MapContols({
|
||||
title: "Note Tool (N)",
|
||||
},
|
||||
};
|
||||
const tools = ["move", "fog", "drawing", "measure", "pointer", "note"];
|
||||
const tools: MapToolId[] = [
|
||||
"move",
|
||||
"fog",
|
||||
"drawing",
|
||||
"measure",
|
||||
"pointer",
|
||||
"note",
|
||||
];
|
||||
|
||||
const sections = [
|
||||
{
|
||||
@ -174,32 +186,41 @@ function MapContols({
|
||||
|
||||
function getToolSettings() {
|
||||
const Settings = toolsById[selectedToolId].SettingsComponent;
|
||||
if (Settings) {
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
position: "absolute",
|
||||
top: "4px",
|
||||
left: "50%",
|
||||
transform: "translateX(-50%)",
|
||||
backgroundColor: "overlay",
|
||||
borderRadius: "4px",
|
||||
}}
|
||||
p={1}
|
||||
>
|
||||
<Settings
|
||||
settings={toolSettings[selectedToolId]}
|
||||
onSettingChange={(change) =>
|
||||
onToolSettingChange(selectedToolId, change)
|
||||
}
|
||||
onToolAction={onToolAction}
|
||||
disabledActions={disabledSettings[selectedToolId]}
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
} else {
|
||||
if (
|
||||
!Settings ||
|
||||
selectedToolId === "move" ||
|
||||
selectedToolId === "measure" ||
|
||||
selectedToolId === "note"
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
position: "absolute",
|
||||
top: "4px",
|
||||
left: "50%",
|
||||
transform: "translateX(-50%)",
|
||||
backgroundColor: "overlay",
|
||||
borderRadius: "4px",
|
||||
}}
|
||||
p={1}
|
||||
>
|
||||
<Settings
|
||||
settings={toolSettings[selectedToolId]}
|
||||
onSettingChange={(change) =>
|
||||
onToolSettingChange({
|
||||
[selectedToolId]: {
|
||||
...toolSettings[selectedToolId],
|
||||
...change,
|
||||
},
|
||||
})
|
||||
}
|
||||
onToolAction={onToolAction}
|
||||
disabledActions={disabledSettings[selectedToolId]}
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
|
@ -21,8 +21,17 @@ import GridOffIcon from "../../icons/GridOffIcon";
|
||||
|
||||
import MapGrid from "./MapGrid";
|
||||
import MapGridEditor from "./MapGridEditor";
|
||||
import { Map } from "../../types/Map";
|
||||
import { GridInset } from "../../types/Grid";
|
||||
|
||||
function MapEditor({ map, onSettingsChange }) {
|
||||
type MapSettingsChangeEventHandler = (change: Partial<Map>) => void;
|
||||
|
||||
type MapEditorProps = {
|
||||
map: Map;
|
||||
onSettingsChange: MapSettingsChangeEventHandler;
|
||||
};
|
||||
|
||||
function MapEditor({ map, onSettingsChange }: MapEditorProps) {
|
||||
const [mapImage] = useMapImage(map);
|
||||
|
||||
const [stageWidth, setStageWidth] = useState(1);
|
||||
@ -36,12 +45,17 @@ function MapEditor({ map, onSettingsChange }) {
|
||||
const mapLayerRef = useRef();
|
||||
const [preventMapInteraction, setPreventMapInteraction] = useState(false);
|
||||
|
||||
function handleResize(width, height) {
|
||||
setStageWidth(width);
|
||||
setStageHeight(height);
|
||||
function handleResize(width?: number, height?: number): void {
|
||||
if (width) {
|
||||
setStageWidth(width);
|
||||
}
|
||||
|
||||
if (height) {
|
||||
setStageHeight(height);
|
||||
}
|
||||
}
|
||||
|
||||
const containerRef = useRef();
|
||||
const containerRef = useRef(null);
|
||||
usePreventOverscroll(containerRef);
|
||||
|
||||
const [mapWidth, mapHeight] = useImageCenter(
|
||||
@ -67,17 +81,21 @@ function MapEditor({ map, onSettingsChange }) {
|
||||
preventMapInteraction
|
||||
);
|
||||
|
||||
function handleGridChange(inset) {
|
||||
onSettingsChange("grid", {
|
||||
...map.grid,
|
||||
inset,
|
||||
function handleGridChange(inset: GridInset) {
|
||||
onSettingsChange({
|
||||
grid: {
|
||||
...map.grid,
|
||||
inset,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function handleMapReset() {
|
||||
onSettingsChange("grid", {
|
||||
...map.grid,
|
||||
inset: defaultInset,
|
||||
onSettingsChange({
|
||||
grid: {
|
||||
...map.grid,
|
||||
inset: defaultInset,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@ -120,8 +138,9 @@ function MapEditor({ map, onSettingsChange }) {
|
||||
>
|
||||
<ReactResizeDetector handleWidth handleHeight onResize={handleResize}>
|
||||
<KonvaBridge
|
||||
stageRender={(children) => (
|
||||
stageRender={(children: React.ReactNode) => (
|
||||
<Stage
|
||||
// @ts-ignore https://github.com/konvajs/react-konva/issues/342
|
||||
width={stageWidth}
|
||||
height={stageHeight}
|
||||
scale={{ x: stageScale, y: stageScale }}
|
@ -31,6 +31,7 @@ import {
|
||||
getGuidesFromBoundingBoxes,
|
||||
getGuidesFromGridCell,
|
||||
findBestGuides,
|
||||
Guide,
|
||||
} from "../../helpers/drawing";
|
||||
import colors from "../../helpers/colors";
|
||||
import {
|
||||
@ -40,13 +41,35 @@ import {
|
||||
} from "../../helpers/konva";
|
||||
import { keyBy } from "../../helpers/shared";
|
||||
|
||||
import SubtractShapeAction from "../../actions/SubtractShapeAction";
|
||||
import CutShapeAction from "../../actions/CutShapeAction";
|
||||
import SubtractFogAction from "../../actions/SubtractFogAction";
|
||||
import CutFogAction from "../../actions/CutFogAction";
|
||||
|
||||
import useSetting from "../../hooks/useSetting";
|
||||
|
||||
import shortcuts from "../../shortcuts";
|
||||
|
||||
import { Map } from "../../types/Map";
|
||||
import { Fog, FogToolSettings } from "../../types/Fog";
|
||||
|
||||
type FogAddEventHandler = (fog: Fog[]) => void;
|
||||
type FogCutEventHandler = (fog: Fog[]) => void;
|
||||
type FogRemoveEventHandler = (fogId: string[]) => void;
|
||||
type FogEditEventHandler = (edit: Partial<Fog>[]) => void;
|
||||
type FogErrorEventHandler = (message: string) => void;
|
||||
|
||||
type MapFogProps = {
|
||||
map: Map;
|
||||
shapes: Fog[];
|
||||
onShapesAdd: FogAddEventHandler;
|
||||
onShapesCut: FogCutEventHandler;
|
||||
onShapesRemove: FogRemoveEventHandler;
|
||||
onShapesEdit: FogEditEventHandler;
|
||||
onShapeError: FogErrorEventHandler;
|
||||
active: boolean;
|
||||
toolSettings: FogToolSettings;
|
||||
editable: boolean;
|
||||
};
|
||||
|
||||
function MapFog({
|
||||
map,
|
||||
shapes,
|
||||
@ -58,7 +81,7 @@ function MapFog({
|
||||
active,
|
||||
toolSettings,
|
||||
editable,
|
||||
}) {
|
||||
}: MapFogProps) {
|
||||
const stageScale = useDebouncedStageScale();
|
||||
const mapWidth = useMapWidth();
|
||||
const mapHeight = useMapHeight();
|
||||
@ -76,7 +99,7 @@ function MapFog({
|
||||
const [editOpacity] = useSetting("fog.editOpacity");
|
||||
const mapStageRef = useMapStage();
|
||||
|
||||
const [drawingShape, setDrawingShape] = useState(null);
|
||||
const [drawingShape, setDrawingShape] = useState<Fog | null>(null);
|
||||
const [isBrushDown, setIsBrushDown] = useState(false);
|
||||
const [editingShapes, setEditingShapes] = useState([]);
|
||||
|
||||
@ -84,7 +107,7 @@ function MapFog({
|
||||
const [fogShapes, setFogShapes] = useState(shapes);
|
||||
// Bounding boxes for guides
|
||||
const [fogShapeBoundingBoxes, setFogShapeBoundingBoxes] = useState([]);
|
||||
const [guides, setGuides] = useState([]);
|
||||
const [guides, setGuides] = useState<Guide[]>([]);
|
||||
|
||||
const shouldHover =
|
||||
active &&
|
||||
@ -108,8 +131,14 @@ function MapFog({
|
||||
const mapStage = mapStageRef.current;
|
||||
|
||||
function getBrushPosition(snapping = true) {
|
||||
if (!mapStage) {
|
||||
return;
|
||||
}
|
||||
const mapImage = mapStage.findOne("#mapImage");
|
||||
let position = getRelativePointerPosition(mapImage);
|
||||
if (!position) {
|
||||
return;
|
||||
}
|
||||
if (shouldUseGuides && snapping) {
|
||||
for (let guide of guides) {
|
||||
if (guide.orientation === "vertical") {
|
||||
@ -129,6 +158,9 @@ function MapFog({
|
||||
function handleBrushDown() {
|
||||
if (toolSettings.type === "brush") {
|
||||
const brushPosition = getBrushPosition();
|
||||
if (!brushPosition) {
|
||||
return;
|
||||
}
|
||||
setDrawingShape({
|
||||
type: "fog",
|
||||
data: {
|
||||
@ -143,6 +175,9 @@ function MapFog({
|
||||
}
|
||||
if (toolSettings.type === "rectangle") {
|
||||
const brushPosition = getBrushPosition();
|
||||
if (!brushPosition) {
|
||||
return;
|
||||
}
|
||||
setDrawingShape({
|
||||
type: "fog",
|
||||
data: {
|
||||
@ -166,7 +201,13 @@ function MapFog({
|
||||
function handleBrushMove() {
|
||||
if (toolSettings.type === "brush" && isBrushDown && drawingShape) {
|
||||
const brushPosition = getBrushPosition();
|
||||
if (!brushPosition) {
|
||||
return;
|
||||
}
|
||||
setDrawingShape((prevShape) => {
|
||||
if (!prevShape) {
|
||||
return prevShape;
|
||||
}
|
||||
const prevPoints = prevShape.data.points;
|
||||
if (
|
||||
Vector2.compare(
|
||||
@ -193,7 +234,13 @@ function MapFog({
|
||||
if (toolSettings.type === "rectangle" && isBrushDown && drawingShape) {
|
||||
const prevPoints = drawingShape.data.points;
|
||||
const brushPosition = getBrushPosition();
|
||||
if (!brushPosition) {
|
||||
return;
|
||||
}
|
||||
setDrawingShape((prevShape) => {
|
||||
if (!prevShape) {
|
||||
return prevShape;
|
||||
}
|
||||
return {
|
||||
...prevShape,
|
||||
data: {
|
||||
@ -223,7 +270,7 @@ function MapFog({
|
||||
const shapesToSubtract = shapes.filter((shape) =>
|
||||
cut ? !shape.visible : shape.visible
|
||||
);
|
||||
const subtractAction = new SubtractShapeAction(shapesToSubtract);
|
||||
const subtractAction = new SubtractFogAction(shapesToSubtract);
|
||||
const state = subtractAction.execute({
|
||||
[drawingShape.id]: drawingShape,
|
||||
});
|
||||
@ -235,7 +282,7 @@ function MapFog({
|
||||
if (drawingShapes.length > 0) {
|
||||
if (cut) {
|
||||
// Run a pre-emptive cut action to check whether we've cut anything
|
||||
const cutAction = new CutShapeAction(drawingShapes);
|
||||
const cutAction = new CutFogAction(drawingShapes);
|
||||
const state = cutAction.execute(keyBy(shapes, "id"));
|
||||
|
||||
if (Object.keys(state).length === shapes.length) {
|
||||
@ -300,7 +347,7 @@ function MapFog({
|
||||
|
||||
function handlePointerMove() {
|
||||
if (shouldUseGuides) {
|
||||
let guides = [];
|
||||
let guides: Guide[] = [];
|
||||
const brushPosition = getBrushPosition(false);
|
||||
const absoluteBrushPosition = Vector2.multiply(brushPosition, {
|
||||
x: mapWidth,
|
||||
@ -393,7 +440,7 @@ function MapFog({
|
||||
const shapesToSubtract = shapes.filter((shape) =>
|
||||
cut ? !shape.visible : shape.visible
|
||||
);
|
||||
const subtractAction = new SubtractShapeAction(shapesToSubtract);
|
||||
const subtractAction = new SubtractFogAction(shapesToSubtract);
|
||||
const state = subtractAction.execute({
|
||||
[polygonShape.id]: polygonShape,
|
||||
});
|
||||
@ -405,7 +452,7 @@ function MapFog({
|
||||
if (polygonShapes.length > 0) {
|
||||
if (cut) {
|
||||
// Run a pre-emptive cut action to check whether we've cut anything
|
||||
const cutAction = new CutShapeAction(polygonShapes);
|
||||
const cutAction = new CutFogAction(polygonShapes);
|
||||
const state = cutAction.execute(keyBy(shapes, "id"));
|
||||
|
||||
if (Object.keys(state).length === shapes.length) {
|
@ -1,4 +1,4 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { useEffect, useState } from "react";
|
||||
import useImage from "use-image";
|
||||
|
||||
import { useDataURL } from "../../contexts/AssetsContext";
|
||||
@ -8,8 +8,9 @@ import { mapSources as defaultMapSources } from "../../maps";
|
||||
import { getImageLightness } from "../../helpers/image";
|
||||
|
||||
import Grid from "../Grid";
|
||||
import { Map } from "../../types/Map";
|
||||
|
||||
function MapGrid({ map }) {
|
||||
function MapGrid({ map }: { map: Map }) {
|
||||
let mapSourceMap = map;
|
||||
const mapURL = useDataURL(
|
||||
mapSourceMap,
|
||||
@ -17,13 +18,14 @@ function MapGrid({ map }) {
|
||||
undefined,
|
||||
map.type === "file"
|
||||
);
|
||||
const [mapImage, mapLoadingStatus] = useImage(mapURL);
|
||||
|
||||
const [mapImage, mapLoadingStatus] = useImage(mapURL || "");
|
||||
|
||||
const [isImageLight, setIsImageLight] = useState(true);
|
||||
|
||||
// When the map changes find the average lightness of its pixels
|
||||
useEffect(() => {
|
||||
if (mapLoadingStatus === "loaded") {
|
||||
if (mapLoadingStatus === "loaded" && mapImage) {
|
||||
setIsImageLight(getImageLightness(mapImage));
|
||||
}
|
||||
}, [mapImage, mapLoadingStatus]);
|
@ -1,5 +1,6 @@
|
||||
import React, { useRef } from "react";
|
||||
import { useRef } from "react";
|
||||
import { Group, Circle, Rect } from "react-konva";
|
||||
import { KonvaEventObject, Node } from "konva/types/Node";
|
||||
|
||||
import {
|
||||
useDebouncedStageScale,
|
||||
@ -12,8 +13,15 @@ import { useKeyboard } from "../../contexts/KeyboardContext";
|
||||
import Vector2 from "../../helpers/Vector2";
|
||||
|
||||
import shortcuts from "../../shortcuts";
|
||||
import { Map } from "../../types/Map";
|
||||
import { GridInset } from "../../types/Grid";
|
||||
|
||||
function MapGridEditor({ map, onGridChange }) {
|
||||
type MapGridEditorProps = {
|
||||
map: Map;
|
||||
onGridChange: (inset: GridInset) => void;
|
||||
};
|
||||
|
||||
function MapGridEditor({ map, onGridChange }: MapGridEditorProps) {
|
||||
const stageScale = useDebouncedStageScale();
|
||||
const mapWidth = useMapWidth();
|
||||
const mapHeight = useMapHeight();
|
||||
@ -39,21 +47,21 @@ function MapGridEditor({ map, onGridChange }) {
|
||||
}
|
||||
const handlePositions = getHandlePositions();
|
||||
|
||||
const handlePreviousPositionRef = useRef();
|
||||
const handlePreviousPositionRef = useRef<Vector2>();
|
||||
|
||||
function handleScaleCircleDragStart(event) {
|
||||
function handleScaleCircleDragStart(event: KonvaEventObject<MouseEvent>) {
|
||||
const handle = event.target;
|
||||
const position = getHandleNormalizedPosition(handle);
|
||||
handlePreviousPositionRef.current = position;
|
||||
}
|
||||
|
||||
function handleScaleCircleDragMove(event) {
|
||||
function handleScaleCircleDragMove(event: KonvaEventObject<MouseEvent>) {
|
||||
const handle = event.target;
|
||||
onGridChange(getHandleInset(handle));
|
||||
handlePreviousPositionRef.current = getHandleNormalizedPosition(handle);
|
||||
}
|
||||
|
||||
function handleScaleCircleDragEnd(event) {
|
||||
function handleScaleCircleDragEnd(event: KonvaEventObject<MouseEvent>) {
|
||||
onGridChange(getHandleInset(event.target));
|
||||
setPreventMapInteraction(false);
|
||||
}
|
||||
@ -66,11 +74,14 @@ function MapGridEditor({ map, onGridChange }) {
|
||||
setPreventMapInteraction(false);
|
||||
}
|
||||
|
||||
function getHandleInset(handle) {
|
||||
function getHandleInset(handle: Node): GridInset {
|
||||
const name = handle.name();
|
||||
|
||||
// Find distance and direction of dragging
|
||||
const previousPosition = handlePreviousPositionRef.current;
|
||||
if (!previousPosition) {
|
||||
return map.grid.inset;
|
||||
}
|
||||
const position = getHandleNormalizedPosition(handle);
|
||||
const distance = Vector2.distance(previousPosition, position);
|
||||
const direction = Vector2.normalize(
|
||||
@ -154,7 +165,7 @@ function MapGridEditor({ map, onGridChange }) {
|
||||
}
|
||||
}
|
||||
|
||||
function nudgeGrid(direction, scale) {
|
||||
function nudgeGrid(direction: Vector2, scale: number) {
|
||||
const inset = map.grid.inset;
|
||||
const gridSizeNormalized = Vector2.divide(
|
||||
Vector2.subtract(inset.bottomRight, inset.topLeft),
|
||||
@ -170,7 +181,7 @@ function MapGridEditor({ map, onGridChange }) {
|
||||
});
|
||||
}
|
||||
|
||||
function handleKeyDown(event) {
|
||||
function handleKeyDown(event: KeyboardEvent) {
|
||||
const nudgeAmount = event.shiftKey ? 2 : 0.5;
|
||||
if (shortcuts.gridNudgeUp(event)) {
|
||||
// Stop arrow up/down scrolling if overflowing
|
||||
@ -191,7 +202,7 @@ function MapGridEditor({ map, onGridChange }) {
|
||||
|
||||
useKeyboard(handleKeyDown);
|
||||
|
||||
function getHandleNormalizedPosition(handle) {
|
||||
function getHandleNormalizedPosition(handle: Node) {
|
||||
return Vector2.divide({ x: handle.x(), y: handle.y() }, mapSize);
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import React, { useState } from "react";
|
||||
import { useState } from "react";
|
||||
import { IconButton } from "theme-ui";
|
||||
|
||||
import SelectMapModal from "../../modals/SelectMapModal";
|
||||
@ -6,6 +6,20 @@ import SelectMapIcon from "../../icons/SelectMapIcon";
|
||||
|
||||
import { useMapData } from "../../contexts/MapDataContext";
|
||||
import { useUserId } from "../../contexts/UserIdContext";
|
||||
import {
|
||||
MapChangeEventHandler,
|
||||
MapResetEventHandler,
|
||||
} from "../../types/Events";
|
||||
import { Map } from "../../types/Map";
|
||||
import { MapState } from "../../types/MapState";
|
||||
|
||||
type SelectMapButtonProps = {
|
||||
onMapChange: MapChangeEventHandler;
|
||||
onMapReset: MapResetEventHandler;
|
||||
currentMap?: Map;
|
||||
currentMapState?: MapState;
|
||||
disabled: boolean;
|
||||
};
|
||||
|
||||
function SelectMapButton({
|
||||
onMapChange,
|
||||
@ -13,7 +27,7 @@ function SelectMapButton({
|
||||
currentMap,
|
||||
currentMapState,
|
||||
disabled,
|
||||
}) {
|
||||
}: SelectMapButtonProps) {
|
||||
const [isModalOpen, setIsModalOpen] = useState(false);
|
||||
|
||||
const { updateMapState } = useMapData();
|
||||
|
@ -4,7 +4,13 @@ import { IconButton } from "theme-ui";
|
||||
import ChangeNicknameModal from "../../modals/ChangeNicknameModal";
|
||||
import ChangeNicknameIcon from "../../icons/ChangeNicknameIcon";
|
||||
|
||||
function ChangeNicknameButton({ nickname, onChange }: { nickname: string, onChange: any}) {
|
||||
function ChangeNicknameButton({
|
||||
nickname,
|
||||
onChange,
|
||||
}: {
|
||||
nickname: string;
|
||||
onChange;
|
||||
}) {
|
||||
const [isChangeModalOpen, setIsChangeModalOpen] = useState(false);
|
||||
function openModal() {
|
||||
setIsChangeModalOpen(true);
|
||||
|
@ -1,12 +1,20 @@
|
||||
import { Flex, Box, Text } from "theme-ui";
|
||||
|
||||
function DiceRoll({ rolls, type, children }: { rolls: any, type: string, children: any}) {
|
||||
function DiceRoll({
|
||||
rolls,
|
||||
type,
|
||||
children,
|
||||
}: {
|
||||
rolls;
|
||||
type: string;
|
||||
children;
|
||||
}) {
|
||||
return (
|
||||
<Flex sx={{ flexWrap: "wrap" }}>
|
||||
<Box sx={{ transform: "scale(0.8)" }}>{children}</Box>
|
||||
{rolls
|
||||
.filter((d: any) => d.type === type && d.roll !== "unknown")
|
||||
.map((dice: any, index: string | number) => (
|
||||
.filter((d) => d.type === type && d.roll !== "unknown")
|
||||
.map((dice, index: string | number) => (
|
||||
<Text as="p" my={1} variant="caption" mx={1} key={index}>
|
||||
{dice.roll}
|
||||
</Text>
|
||||
|
@ -24,14 +24,14 @@ const diceIcons = [
|
||||
{ type: "d100", Icon: D100Icon },
|
||||
];
|
||||
|
||||
function DiceRolls({ rolls }: { rolls: any }) {
|
||||
function DiceRolls({ rolls }: { rolls }) {
|
||||
const total = getDiceRollTotal(rolls);
|
||||
|
||||
const [expanded, setExpanded] = useState<boolean>(false);
|
||||
|
||||
let expandedRolls = [];
|
||||
for (let icon of diceIcons) {
|
||||
if (rolls.some((roll: any) => roll.type === icon.type)) {
|
||||
if (rolls.some((roll) => roll.type === icon.type)) {
|
||||
expandedRolls.push(
|
||||
<DiceRoll rolls={rolls} type={icon.type} key={icon.type}>
|
||||
<icon.Icon />
|
||||
@ -45,29 +45,29 @@ function DiceRolls({ rolls }: { rolls: any }) {
|
||||