fix: corrects textbox selection not displaying correctly

This commit is contained in:
Mark Mankarious
2023-09-02 09:54:31 +01:00
parent cb36408173
commit fe7a2ed211
11 changed files with 107 additions and 64 deletions

View File

@@ -1,41 +1,23 @@
import React, { useMemo } from 'react';
import { Box, Typography } from '@mui/material';
import { CoordsUtils, getTextWidth, toPx } from 'src/utils';
import { toPx, CoordsUtils } from 'src/utils';
import { TextBox as TextBoxI } from 'src/types';
import { DEFAULT_FONT_FAMILY } from 'src/config';
import { useIsoProjection } from 'src/hooks/useIsoProjection';
import { useTileSize } from 'src/hooks/useTileSize';
import { useTextBoxProps } from 'src/hooks/useTextBoxProps';
interface Props {
textBox: TextBoxI;
isSelected?: boolean;
}
export const TextBox = ({ textBox, isSelected }: Props) => {
const { unprojectedTileSize } = useTileSize();
const fontProps = useMemo(() => {
return {
fontSize: toPx(unprojectedTileSize * textBox.fontSize),
fontFamily: DEFAULT_FONT_FAMILY,
fontWeight: 'bold'
};
}, [unprojectedTileSize, textBox.fontSize]);
const paddingX = useMemo(() => {
return unprojectedTileSize * 0.2;
}, [unprojectedTileSize]);
const textWidth = useMemo(() => {
return getTextWidth(textBox.text, fontProps) + paddingX * 2;
}, [textBox.text, fontProps, paddingX]);
export const TextBox = ({ textBox }: Props) => {
const { paddingX, fontProps } = useTextBoxProps(textBox);
const to = useMemo(() => {
return CoordsUtils.add(textBox.tile, {
x: Math.ceil(textWidth / unprojectedTileSize),
x: textBox.size.width,
y: 0
});
}, [textBox.tile, textWidth, unprojectedTileSize]);
}, [textBox.tile, textBox.size.width]);
const { css } = useIsoProjection({
from: textBox.tile,
@@ -45,20 +27,6 @@ export const TextBox = ({ textBox, isSelected }: Props) => {
return (
<Box sx={css}>
{isSelected && (
<Box
sx={{
position: 'absolute',
top: 0,
left: 0,
width: '100%',
height: '100%',
border: 1,
borderRadius: 2,
borderStyle: 'dashed'
}}
/>
)}
<Box
sx={{
position: 'absolute',

View File

@@ -12,9 +12,6 @@ export const TextBoxes = () => {
const mouse = useUiStateStore((state) => {
return state.mouse;
});
const itemControls = useUiStateStore((state) => {
return state.itemControls;
});
const textBoxes = useSceneStore((state) => {
return state.textBoxes;
});
@@ -22,14 +19,7 @@ export const TextBoxes = () => {
return (
<>
{textBoxes.map((textBox) => {
return (
<TextBox
textBox={textBox}
isSelected={
itemControls?.type === 'TEXTBOX' && itemControls.id === textBox.id
}
/>
);
return <TextBox textBox={textBox} />;
})}
{mode.type === 'TEXTBOX' && (
<TextBox
@@ -38,7 +28,6 @@ export const TextBoxes = () => {
...TEXTBOX_DEFAULTS,
tile: mouse.position.tile
})}
isSelected
/>
)}
</>

View File

@@ -0,0 +1,29 @@
import React, { useMemo } from 'react';
import { ProjectionOrientationEnum } from 'src/types';
import { CoordsUtils } from 'src/utils';
import { useTextBox } from 'src/hooks/useTextBox';
import { TransformControls } from './TransformControls';
interface Props {
id: string;
}
export const TextBoxTransformControls = ({ id }: Props) => {
const textBox = useTextBox(id);
const to = useMemo(() => {
if (textBox.orientation === ProjectionOrientationEnum.X) {
return CoordsUtils.add(textBox.tile, {
x: textBox.size.width,
y: 0
});
}
return CoordsUtils.add(textBox.tile, {
x: 0,
y: -textBox.size.width
});
}, [textBox.orientation, textBox.size.width, textBox.tile]);
return <TransformControls from={textBox.tile} to={to} />;
};

View File

@@ -23,7 +23,6 @@ export const TransformControls = ({
onMouseOver,
showCornerAnchors
}: Props) => {
const theme = useTheme();
const { css, pxSize } = useIsoProjection({
from,
to

View File

@@ -1,6 +1,7 @@
import React from 'react';
import { useUiStateStore } from 'src/stores/uiStateStore';
import { RectangleTransformControls } from './RectangleTransformControls';
import { TextBoxTransformControls } from './TextBoxTransformControls';
export const TransformControlsManager = () => {
const itemControls = useUiStateStore((state) => {
@@ -10,6 +11,8 @@ export const TransformControlsManager = () => {
switch (itemControls?.type) {
case 'RECTANGLE':
return <RectangleTransformControls id={itemControls.id} />;
case 'TEXTBOX':
return <TextBoxTransformControls id={itemControls.id} />;
default:
return null;
}

View File

@@ -37,7 +37,9 @@ export const CONNECTOR_DEFAULTS: ConnectorDefaults = {
export const TEXTBOX_DEFAULTS = {
fontSize: 0.6,
text: 'Text'
paddingX: 0.2,
text: 'Text',
fontWeight: 'bold'
};
export const ZOOM_INCREMENT = 0.2;

View File

@@ -0,0 +1,22 @@
import { useMemo } from 'react';
import { TextBox } from 'src/types';
import { useTileSize } from 'src/hooks/useTileSize';
import { DEFAULT_FONT_FAMILY, TEXTBOX_DEFAULTS } from 'src/config';
export const useTextBoxProps = (textBox: TextBox) => {
const { unprojectedTileSize } = useTileSize();
const fontProps = useMemo(() => {
return {
fontSize: unprojectedTileSize * textBox.fontSize,
fontFamily: DEFAULT_FONT_FAMILY,
fontWeight: TEXTBOX_DEFAULTS.fontWeight
};
}, [unprojectedTileSize, textBox.fontSize]);
const paddingX = useMemo(() => {
return unprojectedTileSize * TEXTBOX_DEFAULTS.paddingX;
}, [unprojectedTileSize]);
return { paddingX, fontProps };
};

View File

@@ -2,6 +2,7 @@ import React, { createContext, useRef, useContext } from 'react';
import { createStore, useStore } from 'zustand';
import { produce } from 'immer';
import { SceneStore } from 'src/types';
import { DEFAULT_FONT_FAMILY, TEXTBOX_DEFAULTS } from 'src/config';
import { sceneInput } from 'src/validation/scene';
import {
getItemById,
@@ -10,7 +11,8 @@ import {
connectorInputToConnector,
textBoxInputToTextBox,
sceneInputToScene,
nodeInputToNode
nodeInputToNode,
getTextWidth
} from 'src/utils';
const initialState = () => {
@@ -149,6 +151,17 @@ const initialState = () => {
id
);
if (updates.text || updates.fontSize) {
draftState.textBoxes[index].size = {
width: getTextWidth(updates.text ?? textBox.text, {
fontSize: updates.fontSize ?? textBox.fontSize,
fontFamily: DEFAULT_FONT_FAMILY,
fontWeight: TEXTBOX_DEFAULTS.fontWeight
}),
height: 1
};
}
draftState.textBoxes[index] = {
...textBox,
...updates

View File

@@ -7,8 +7,7 @@ import {
NodeInput,
ConnectorStyleEnum
} from 'src/types/inputs';
import { ProjectionOrientationEnum } from 'src/types/common';
import { Coords, Rect } from './common';
import { ProjectionOrientationEnum, Coords, Rect, Size } from 'src/types';
export enum TileOriginEnum {
CENTER = 'CENTER',
@@ -66,6 +65,7 @@ export interface TextBox {
tile: Coords;
text: string;
orientation: ProjectionOrientationEnum;
size: Size;
}
export interface Rectangle {

View File

@@ -19,9 +19,11 @@ import {
NODE_DEFAULTS,
DEFAULT_COLOR,
CONNECTOR_DEFAULTS,
TEXTBOX_DEFAULTS
TEXTBOX_DEFAULTS,
DEFAULT_FONT_FAMILY,
UNPROJECTED_TILE_SIZE
} from 'src/config';
import { getConnectorPath } from './renderer';
import { getConnectorPath, getTextWidth } from 'src/utils';
export const nodeInputToNode = (nodeInput: NodeInput): Node => {
return {
@@ -88,13 +90,23 @@ export const connectorInputToConnector = (
};
export const textBoxInputToTextBox = (textBoxInput: TextBoxInput): TextBox => {
const fontSize = textBoxInput.fontSize ?? TEXTBOX_DEFAULTS.fontSize;
return {
type: SceneItemTypeEnum.TEXTBOX,
id: textBoxInput.id,
orientation: textBoxInput.orientation ?? ProjectionOrientationEnum.X,
fontSize: textBoxInput.fontSize ?? TEXTBOX_DEFAULTS.fontSize,
fontSize,
tile: textBoxInput.tile,
text: textBoxInput.text
text: textBoxInput.text,
size: {
width: getTextWidth(textBoxInput.text, {
fontSize,
fontFamily: DEFAULT_FONT_FAMILY,
fontWeight: TEXTBOX_DEFAULTS.fontWeight
}),
height: 1
}
};
};

View File

@@ -5,7 +5,8 @@ import {
ZOOM_INCREMENT,
MAX_ZOOM,
MIN_ZOOM,
CONNECTOR_DEFAULTS
CONNECTOR_DEFAULTS,
TEXTBOX_DEFAULTS
} from 'src/config';
import {
Coords,
@@ -26,7 +27,8 @@ import {
CoordsUtils,
clamp,
roundToOneDecimalPlace,
findPath
findPath,
toPx
} from 'src/utils';
interface GetProjectedTileSize {
@@ -479,11 +481,13 @@ export const getItemAtTile = ({
interface FontProps {
fontWeight: number | string;
fontSize: string;
fontSize: number;
fontFamily: string;
}
export const getTextWidth = (text: string, fontProps: FontProps) => {
const paddingX = TEXTBOX_DEFAULTS.paddingX * UNPROJECTED_TILE_SIZE;
const fontSizePx = toPx(fontProps.fontSize * UNPROJECTED_TILE_SIZE);
const canvas: HTMLCanvasElement = document.createElement('canvas');
const context = canvas.getContext('2d');
@@ -491,10 +495,12 @@ export const getTextWidth = (text: string, fontProps: FontProps) => {
throw new Error('Could not get canvas context');
}
context.font = `${fontProps.fontWeight} ${fontProps.fontSize} ${fontProps.fontFamily}`;
context.font = `${fontProps.fontWeight} ${fontSizePx} ${fontProps.fontFamily}`;
const metrics = context.measureText(text);
return metrics.width;
canvas.remove();
return Math.ceil((metrics.width + paddingX * 2) / UNPROJECTED_TILE_SIZE);
};
export const outermostCornerPositions = [