Moved konva context bridging to shared component
This commit is contained in:
parent
d1c62a51c0
commit
89fb803398
@ -1,4 +1,4 @@
|
||||
import React, { useState, useRef, useContext } from "react";
|
||||
import React, { useState, useRef } from "react";
|
||||
import { Box, IconButton } from "theme-ui";
|
||||
import { Stage, Layer, Image } from "react-konva";
|
||||
import ReactResizeDetector from "react-resize-detector";
|
||||
@ -10,9 +10,9 @@ import useImageCenter from "../../hooks/useImageCenter";
|
||||
import useResponsiveLayout from "../../hooks/useResponsiveLayout";
|
||||
|
||||
import { getGridDefaultInset, getGridMaxZoom } from "../../helpers/grid";
|
||||
import KonvaBridge from "../../helpers/KonvaBridge";
|
||||
|
||||
import { MapInteractionProvider } from "../../contexts/MapInteractionContext";
|
||||
import KeyboardContext from "../../contexts/KeyboardContext";
|
||||
import { GridProvider } from "../../contexts/GridContext";
|
||||
|
||||
import ResetMapIcon from "../../icons/ResetMapIcon";
|
||||
@ -90,11 +90,9 @@ function MapEditor({ map, onSettingsChange }) {
|
||||
setPreventMapInteraction,
|
||||
mapWidth,
|
||||
mapHeight,
|
||||
interactionEmitter: null,
|
||||
};
|
||||
|
||||
// Get keyboard context to pass to Konva
|
||||
const keyboardValue = useContext(KeyboardContext);
|
||||
|
||||
const canEditGrid = map.type !== "default";
|
||||
|
||||
const gridChanged =
|
||||
@ -106,77 +104,90 @@ function MapEditor({ map, onSettingsChange }) {
|
||||
const layout = useResponsiveLayout();
|
||||
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
width: "100%",
|
||||
height: layout.screenSize === "large" ? "500px" : "300px",
|
||||
cursor: "move",
|
||||
touchAction: "none",
|
||||
outline: "none",
|
||||
position: "relative",
|
||||
}}
|
||||
bg="muted"
|
||||
ref={containerRef}
|
||||
>
|
||||
<ReactResizeDetector handleWidth handleHeight onResize={handleResize}>
|
||||
<Stage
|
||||
width={stageWidth}
|
||||
height={stageHeight}
|
||||
scale={{ x: stageScale, y: stageScale }}
|
||||
ref={mapStageRef}
|
||||
<MapInteractionProvider value={mapInteraction}>
|
||||
<GridProvider grid={map?.grid} width={mapWidth} height={mapHeight}>
|
||||
<Box
|
||||
sx={{
|
||||
width: "100%",
|
||||
height: layout.screenSize === "large" ? "500px" : "300px",
|
||||
cursor: "move",
|
||||
touchAction: "none",
|
||||
outline: "none",
|
||||
position: "relative",
|
||||
}}
|
||||
bg="muted"
|
||||
ref={containerRef}
|
||||
>
|
||||
<Layer ref={mapLayerRef}>
|
||||
<Image image={mapImageSource} width={mapWidth} height={mapHeight} />
|
||||
<KeyboardContext.Provider value={keyboardValue}>
|
||||
<MapInteractionProvider value={mapInteraction}>
|
||||
<ReactResizeDetector handleWidth handleHeight onResize={handleResize}>
|
||||
<KonvaBridge
|
||||
stageRender={(children) => (
|
||||
<Stage
|
||||
width={stageWidth}
|
||||
height={stageHeight}
|
||||
scale={{ x: stageScale, y: stageScale }}
|
||||
ref={mapStageRef}
|
||||
>
|
||||
{children}
|
||||
</Stage>
|
||||
)}
|
||||
>
|
||||
<Layer ref={mapLayerRef}>
|
||||
<Image
|
||||
image={mapImageSource}
|
||||
width={mapWidth}
|
||||
height={mapHeight}
|
||||
/>
|
||||
{showGridControls && canEditGrid && (
|
||||
<GridProvider
|
||||
grid={map.grid}
|
||||
width={mapWidth}
|
||||
height={mapHeight}
|
||||
>
|
||||
<>
|
||||
<MapGrid map={map} />
|
||||
<MapGridEditor map={map} onGridChange={handleGridChange} />
|
||||
</GridProvider>
|
||||
</>
|
||||
)}
|
||||
</MapInteractionProvider>
|
||||
</KeyboardContext.Provider>
|
||||
</Layer>
|
||||
</Stage>
|
||||
</ReactResizeDetector>
|
||||
{gridChanged && (
|
||||
<IconButton
|
||||
title="Reset Grid"
|
||||
aria-label="Reset Grid"
|
||||
onClick={handleMapReset}
|
||||
bg="overlay"
|
||||
sx={{ borderRadius: "50%", position: "absolute", bottom: 0, left: 0 }}
|
||||
m={2}
|
||||
>
|
||||
<ResetMapIcon />
|
||||
</IconButton>
|
||||
)}
|
||||
{canEditGrid && (
|
||||
<IconButton
|
||||
title={showGridControls ? "Hide Grid Controls" : "Show Grid Controls"}
|
||||
aria-label={
|
||||
showGridControls ? "Hide Grid Controls" : "Show Grid Controls"
|
||||
}
|
||||
onClick={() => setShowGridControls(!showGridControls)}
|
||||
bg="overlay"
|
||||
sx={{
|
||||
borderRadius: "50%",
|
||||
position: "absolute",
|
||||
bottom: 0,
|
||||
right: 0,
|
||||
}}
|
||||
m={2}
|
||||
p="6px"
|
||||
>
|
||||
{showGridControls ? <GridOnIcon /> : <GridOffIcon />}
|
||||
</IconButton>
|
||||
)}
|
||||
</Box>
|
||||
</Layer>
|
||||
</KonvaBridge>
|
||||
</ReactResizeDetector>
|
||||
{gridChanged && (
|
||||
<IconButton
|
||||
title="Reset Grid"
|
||||
aria-label="Reset Grid"
|
||||
onClick={handleMapReset}
|
||||
bg="overlay"
|
||||
sx={{
|
||||
borderRadius: "50%",
|
||||
position: "absolute",
|
||||
bottom: 0,
|
||||
left: 0,
|
||||
}}
|
||||
m={2}
|
||||
>
|
||||
<ResetMapIcon />
|
||||
</IconButton>
|
||||
)}
|
||||
{canEditGrid && (
|
||||
<IconButton
|
||||
title={
|
||||
showGridControls ? "Hide Grid Controls" : "Show Grid Controls"
|
||||
}
|
||||
aria-label={
|
||||
showGridControls ? "Hide Grid Controls" : "Show Grid Controls"
|
||||
}
|
||||
onClick={() => setShowGridControls(!showGridControls)}
|
||||
bg="overlay"
|
||||
sx={{
|
||||
borderRadius: "50%",
|
||||
position: "absolute",
|
||||
bottom: 0,
|
||||
right: 0,
|
||||
}}
|
||||
m={2}
|
||||
p="6px"
|
||||
>
|
||||
{showGridControls ? <GridOnIcon /> : <GridOffIcon />}
|
||||
</IconButton>
|
||||
)}
|
||||
</Box>
|
||||
</GridProvider>
|
||||
</MapInteractionProvider>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -10,21 +10,12 @@ import useStageInteraction from "../../hooks/useStageInteraction";
|
||||
import useImageCenter from "../../hooks/useImageCenter";
|
||||
|
||||
import { getGridMaxZoom } from "../../helpers/grid";
|
||||
import KonvaBridge from "../../helpers/KonvaBridge";
|
||||
|
||||
import { MapInteractionProvider } from "../../contexts/MapInteractionContext";
|
||||
import { MapStageProvider, useMapStage } from "../../contexts/MapStageContext";
|
||||
import AuthContext, { useAuth } from "../../contexts/AuthContext";
|
||||
import SettingsContext, { useSettings } from "../../contexts/SettingsContext";
|
||||
import KeyboardContext from "../../contexts/KeyboardContext";
|
||||
import TokenDataContext, {
|
||||
useTokenData,
|
||||
} from "../../contexts/TokenDataContext";
|
||||
import { useMapStage } from "../../contexts/MapStageContext";
|
||||
import { GridProvider } from "../../contexts/GridContext";
|
||||
import { useKeyboard } from "../../contexts/KeyboardContext";
|
||||
import {
|
||||
ImageSourcesStateContext,
|
||||
ImageSourcesUpdaterContext,
|
||||
} from "../../contexts/ImageSourceContext";
|
||||
|
||||
function MapInteraction({
|
||||
map,
|
||||
@ -162,8 +153,6 @@ function MapInteraction({
|
||||
}
|
||||
|
||||
useKeyboard(handleKeyDown, handleKeyUp);
|
||||
// Get keyboard context to pass to Konva
|
||||
const keyboardValue = useContext(KeyboardContext);
|
||||
|
||||
function getCursorForTool(tool) {
|
||||
switch (tool) {
|
||||
@ -171,9 +160,7 @@ function MapInteraction({
|
||||
return "move";
|
||||
case "fog":
|
||||
case "drawing":
|
||||
return settings.settings[tool].type === "move"
|
||||
? "pointer"
|
||||
: "crosshair";
|
||||
return "crosshair";
|
||||
case "measure":
|
||||
case "pointer":
|
||||
case "note":
|
||||
@ -183,12 +170,6 @@ function MapInteraction({
|
||||
}
|
||||
}
|
||||
|
||||
const auth = useAuth();
|
||||
const settings = useSettings();
|
||||
const tokenData = useTokenData();
|
||||
const imageSources = useContext(ImageSourcesStateContext);
|
||||
const setImageSources = useContext(ImageSourcesUpdaterContext);
|
||||
|
||||
const mapInteraction = {
|
||||
stageScale,
|
||||
stageWidth,
|
||||
@ -200,69 +181,49 @@ function MapInteraction({
|
||||
};
|
||||
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
flexGrow: 1,
|
||||
position: "relative",
|
||||
cursor: getCursorForTool(selectedToolId),
|
||||
touchAction: "none",
|
||||
outline: "none",
|
||||
}}
|
||||
ref={containerRef}
|
||||
className="map"
|
||||
>
|
||||
<ReactResizeDetector handleWidth handleHeight onResize={handleResize}>
|
||||
<Stage
|
||||
width={stageWidth}
|
||||
height={stageHeight}
|
||||
scale={{ x: stageScale, y: stageScale }}
|
||||
ref={mapStageRef}
|
||||
<MapInteractionProvider value={mapInteraction}>
|
||||
<GridProvider grid={map?.grid} width={mapWidth} height={mapHeight}>
|
||||
<Box
|
||||
sx={{
|
||||
flexGrow: 1,
|
||||
position: "relative",
|
||||
cursor: getCursorForTool(selectedToolId),
|
||||
touchAction: "none",
|
||||
outline: "none",
|
||||
}}
|
||||
ref={containerRef}
|
||||
className="map"
|
||||
>
|
||||
<Layer ref={mapLayerRef}>
|
||||
<Image
|
||||
image={mapLoaded && mapImageSource}
|
||||
width={mapWidth}
|
||||
height={mapHeight}
|
||||
id="mapImage"
|
||||
ref={mapImageRef}
|
||||
/>
|
||||
{/* Forward auth context to konva elements */}
|
||||
<AuthContext.Provider value={auth}>
|
||||
<SettingsContext.Provider value={settings}>
|
||||
<KeyboardContext.Provider value={keyboardValue}>
|
||||
<MapInteractionProvider value={mapInteraction}>
|
||||
<GridProvider
|
||||
grid={map?.grid}
|
||||
width={mapWidth}
|
||||
height={mapHeight}
|
||||
>
|
||||
<MapStageProvider value={mapStageRef}>
|
||||
<TokenDataContext.Provider value={tokenData}>
|
||||
<ImageSourcesStateContext.Provider
|
||||
value={imageSources}
|
||||
>
|
||||
<ImageSourcesUpdaterContext.Provider
|
||||
value={setImageSources}
|
||||
>
|
||||
{mapLoaded && children}
|
||||
</ImageSourcesUpdaterContext.Provider>
|
||||
</ImageSourcesStateContext.Provider>
|
||||
</TokenDataContext.Provider>
|
||||
</MapStageProvider>
|
||||
</GridProvider>
|
||||
</MapInteractionProvider>
|
||||
</KeyboardContext.Provider>
|
||||
</SettingsContext.Provider>
|
||||
</AuthContext.Provider>
|
||||
</Layer>
|
||||
</Stage>
|
||||
</ReactResizeDetector>
|
||||
<MapInteractionProvider value={mapInteraction}>
|
||||
<GridProvider grid={map?.grid} width={mapWidth} height={mapHeight}>
|
||||
<ReactResizeDetector handleWidth handleHeight onResize={handleResize}>
|
||||
<KonvaBridge
|
||||
stageRender={(children) => (
|
||||
<Stage
|
||||
width={stageWidth}
|
||||
height={stageHeight}
|
||||
scale={{ x: stageScale, y: stageScale }}
|
||||
ref={mapStageRef}
|
||||
>
|
||||
{children}
|
||||
</Stage>
|
||||
)}
|
||||
>
|
||||
<Layer ref={mapLayerRef}>
|
||||
<Image
|
||||
image={mapLoaded && mapImageSource}
|
||||
width={mapWidth}
|
||||
height={mapHeight}
|
||||
id="mapImage"
|
||||
ref={mapImageRef}
|
||||
/>
|
||||
|
||||
{mapLoaded && children}
|
||||
</Layer>
|
||||
</KonvaBridge>
|
||||
</ReactResizeDetector>
|
||||
{controls}
|
||||
</GridProvider>
|
||||
</MapInteractionProvider>
|
||||
</Box>
|
||||
</Box>
|
||||
</GridProvider>
|
||||
</MapInteractionProvider>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -37,19 +37,21 @@ const defaultValue = {
|
||||
gridCellPixelOffset: new Vector2(0, 0),
|
||||
};
|
||||
|
||||
const GridContext = React.createContext(defaultValue.grid);
|
||||
const GridPixelSizeContext = React.createContext(defaultValue.gridPixelSize);
|
||||
const GridCellPixelSizeContext = React.createContext(
|
||||
export const GridContext = React.createContext(defaultValue.grid);
|
||||
export const GridPixelSizeContext = React.createContext(
|
||||
defaultValue.gridPixelSize
|
||||
);
|
||||
export const GridCellPixelSizeContext = React.createContext(
|
||||
defaultValue.gridCellPixelSize
|
||||
);
|
||||
const GridCellNormalizedSizeContext = React.createContext(
|
||||
export const GridCellNormalizedSizeContext = React.createContext(
|
||||
defaultValue.gridCellNormalizedSize
|
||||
);
|
||||
const GridOffsetContext = React.createContext(defaultValue.gridOffset);
|
||||
const GridStrokeWidthContext = React.createContext(
|
||||
export const GridOffsetContext = React.createContext(defaultValue.gridOffset);
|
||||
export const GridStrokeWidthContext = React.createContext(
|
||||
defaultValue.gridStrokeWidth
|
||||
);
|
||||
const GridCellPixelOffsetContext = React.createContext(
|
||||
export const GridCellPixelOffsetContext = React.createContext(
|
||||
defaultValue.gridCellPixelOffset
|
||||
);
|
||||
|
||||
|
@ -1,14 +1,14 @@
|
||||
import React, { useContext } from "react";
|
||||
import useDebounce from "../hooks/useDebounce";
|
||||
|
||||
const StageScaleContext = React.createContext();
|
||||
const DebouncedStageScaleContext = React.createContext();
|
||||
const StageWidthContext = React.createContext();
|
||||
const StageHeightContext = React.createContext();
|
||||
const SetPreventMapInteractionContext = React.createContext();
|
||||
const MapWidthContext = React.createContext();
|
||||
const MapHeightContext = React.createContext();
|
||||
const InteractionEmitterContext = React.createContext();
|
||||
export const StageScaleContext = React.createContext();
|
||||
export const DebouncedStageScaleContext = React.createContext();
|
||||
export const StageWidthContext = React.createContext();
|
||||
export const StageHeightContext = React.createContext();
|
||||
export const SetPreventMapInteractionContext = React.createContext();
|
||||
export const MapWidthContext = React.createContext();
|
||||
export const MapHeightContext = React.createContext();
|
||||
export const InteractionEmitterContext = React.createContext();
|
||||
|
||||
export function MapInteractionProvider({ value, children }) {
|
||||
const {
|
||||
@ -32,7 +32,7 @@ export function MapInteractionProvider({ value, children }) {
|
||||
<MapHeightContext.Provider value={mapHeight}>
|
||||
<StageScaleContext.Provider value={stageScale}>
|
||||
<DebouncedStageScaleContext.Provider
|
||||
value={debouncedStageScale}
|
||||
value={debouncedStageScale || 1}
|
||||
>
|
||||
{children}
|
||||
</DebouncedStageScaleContext.Provider>
|
||||
|
143
src/helpers/KonvaBridge.js
Normal file
143
src/helpers/KonvaBridge.js
Normal file
@ -0,0 +1,143 @@
|
||||
import React, { useContext } from "react";
|
||||
|
||||
import {
|
||||
InteractionEmitterContext,
|
||||
SetPreventMapInteractionContext,
|
||||
StageWidthContext,
|
||||
StageHeightContext,
|
||||
MapWidthContext,
|
||||
MapHeightContext,
|
||||
StageScaleContext,
|
||||
DebouncedStageScaleContext,
|
||||
useInteractionEmitter,
|
||||
useSetPreventMapInteraction,
|
||||
useStageWidth,
|
||||
useStageHeight,
|
||||
useMapWidth,
|
||||
useMapHeight,
|
||||
useStageScale,
|
||||
useDebouncedStageScale,
|
||||
} from "../contexts/MapInteractionContext";
|
||||
import { MapStageProvider, useMapStage } from "../contexts/MapStageContext";
|
||||
import AuthContext, { useAuth } from "../contexts/AuthContext";
|
||||
import SettingsContext, { useSettings } from "../contexts/SettingsContext";
|
||||
import KeyboardContext from "../contexts/KeyboardContext";
|
||||
import TokenDataContext, { useTokenData } from "../contexts/TokenDataContext";
|
||||
import {
|
||||
ImageSourcesStateContext,
|
||||
ImageSourcesUpdaterContext,
|
||||
} from "../contexts/ImageSourceContext";
|
||||
import {
|
||||
useGrid,
|
||||
useGridCellPixelSize,
|
||||
useGridCellNormalizedSize,
|
||||
useGridStrokeWidth,
|
||||
useGridCellPixelOffset,
|
||||
useGridOffset,
|
||||
useGridPixelSize,
|
||||
GridContext,
|
||||
GridPixelSizeContext,
|
||||
GridCellPixelSizeContext,
|
||||
GridCellNormalizedSizeContext,
|
||||
GridOffsetContext,
|
||||
GridStrokeWidthContext,
|
||||
GridCellPixelOffsetContext,
|
||||
} from "../contexts/GridContext";
|
||||
|
||||
/**
|
||||
* Provide a bridge for konva that forwards our contexts
|
||||
*/
|
||||
function KonvaBridge({ stageRender, children }) {
|
||||
const mapStageRef = useMapStage();
|
||||
const auth = useAuth();
|
||||
const settings = useSettings();
|
||||
const tokenData = useTokenData();
|
||||
const imageSources = useContext(ImageSourcesStateContext);
|
||||
const setImageSources = useContext(ImageSourcesUpdaterContext);
|
||||
const keyboardValue = useContext(KeyboardContext);
|
||||
|
||||
const stageScale = useStageScale();
|
||||
const stageWidth = useStageWidth();
|
||||
const stageHeight = useStageHeight();
|
||||
const setPreventMapInteraction = useSetPreventMapInteraction();
|
||||
const mapWidth = useMapWidth();
|
||||
const mapHeight = useMapHeight();
|
||||
const interactionEmitter = useInteractionEmitter();
|
||||
const debouncedStageScale = useDebouncedStageScale();
|
||||
|
||||
const grid = useGrid();
|
||||
const gridPixelSize = useGridPixelSize();
|
||||
const gridCellNormalizedSize = useGridCellNormalizedSize();
|
||||
const gridCellPixelSize = useGridCellPixelSize();
|
||||
const gridStrokeWidth = useGridStrokeWidth();
|
||||
const gridCellPixelOffset = useGridCellPixelOffset();
|
||||
const gridOffset = useGridOffset();
|
||||
|
||||
return stageRender(
|
||||
<AuthContext.Provider value={auth}>
|
||||
<SettingsContext.Provider value={settings}>
|
||||
<KeyboardContext.Provider value={keyboardValue}>
|
||||
<MapStageProvider value={mapStageRef}>
|
||||
<TokenDataContext.Provider value={tokenData}>
|
||||
<ImageSourcesStateContext.Provider value={imageSources}>
|
||||
<ImageSourcesUpdaterContext.Provider value={setImageSources}>
|
||||
<InteractionEmitterContext.Provider
|
||||
value={interactionEmitter}
|
||||
>
|
||||
<SetPreventMapInteractionContext.Provider
|
||||
value={setPreventMapInteraction}
|
||||
>
|
||||
<StageWidthContext.Provider value={stageWidth}>
|
||||
<StageHeightContext.Provider value={stageHeight}>
|
||||
<MapWidthContext.Provider value={mapWidth}>
|
||||
<MapHeightContext.Provider value={mapHeight}>
|
||||
<StageScaleContext.Provider value={stageScale}>
|
||||
<DebouncedStageScaleContext.Provider
|
||||
value={debouncedStageScale}
|
||||
>
|
||||
<GridContext.Provider value={grid}>
|
||||
<GridPixelSizeContext.Provider
|
||||
value={gridPixelSize}
|
||||
>
|
||||
<GridCellPixelSizeContext.Provider
|
||||
value={gridCellPixelSize}
|
||||
>
|
||||
<GridCellNormalizedSizeContext.Provider
|
||||
value={gridCellNormalizedSize}
|
||||
>
|
||||
<GridOffsetContext.Provider
|
||||
value={gridOffset}
|
||||
>
|
||||
<GridStrokeWidthContext.Provider
|
||||
value={gridStrokeWidth}
|
||||
>
|
||||
<GridCellPixelOffsetContext.Provider
|
||||
value={gridCellPixelOffset}
|
||||
>
|
||||
{children}
|
||||
</GridCellPixelOffsetContext.Provider>
|
||||
</GridStrokeWidthContext.Provider>
|
||||
</GridOffsetContext.Provider>
|
||||
</GridCellNormalizedSizeContext.Provider>
|
||||
</GridCellPixelSizeContext.Provider>
|
||||
</GridPixelSizeContext.Provider>
|
||||
</GridContext.Provider>
|
||||
</DebouncedStageScaleContext.Provider>
|
||||
</StageScaleContext.Provider>
|
||||
</MapHeightContext.Provider>
|
||||
</MapWidthContext.Provider>
|
||||
</StageHeightContext.Provider>
|
||||
</StageWidthContext.Provider>
|
||||
</SetPreventMapInteractionContext.Provider>
|
||||
</InteractionEmitterContext.Provider>
|
||||
</ImageSourcesUpdaterContext.Provider>
|
||||
</ImageSourcesStateContext.Provider>
|
||||
</TokenDataContext.Provider>
|
||||
</MapStageProvider>
|
||||
</KeyboardContext.Provider>
|
||||
</SettingsContext.Provider>
|
||||
</AuthContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
export default KonvaBridge;
|
Loading…
Reference in New Issue
Block a user