mirror of
https://github.com/stan-smith/FossFLOW.git
synced 2026-04-23 08:31:16 -04:00
feat: implements rectangle controls
This commit is contained in:
@@ -8,15 +8,21 @@ interface Props {
|
||||
activeColor: string;
|
||||
}
|
||||
|
||||
export const ColorSelector = ({ colors, onChange, activeColor }: Props) => (
|
||||
<Box>
|
||||
{colors.map((color) => (
|
||||
<ColorSwatch
|
||||
key={color}
|
||||
hex={color}
|
||||
onClick={() => onChange(color)}
|
||||
isActive={activeColor === color}
|
||||
/>
|
||||
))}
|
||||
</Box>
|
||||
);
|
||||
export const ColorSelector = ({ colors, onChange, activeColor }: Props) => {
|
||||
return (
|
||||
<Box>
|
||||
{colors.map((color) => {
|
||||
return (
|
||||
<ColorSwatch
|
||||
key={color}
|
||||
hex={color}
|
||||
onClick={() => {
|
||||
return onChange(color);
|
||||
}}
|
||||
isActive={activeColor === color}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -3,7 +3,7 @@ import { Card, useTheme } from '@mui/material';
|
||||
import { useUiStateStore } from 'src/stores/uiStateStore';
|
||||
import { IconSelection } from 'src/components/ItemControls/IconSelection/IconSelection';
|
||||
import { NodeControls } from './NodeControls/NodeControls';
|
||||
import { ProjectControls } from './ProjectControls/ProjectControls';
|
||||
import { RectangleControls } from './RectangleControls/RectangleControls';
|
||||
|
||||
export const ItemControlsManager = () => {
|
||||
const itemControls = useUiStateStore((state) => {
|
||||
@@ -13,11 +13,11 @@ export const ItemControlsManager = () => {
|
||||
|
||||
const Controls = useMemo(() => {
|
||||
switch (itemControls?.type) {
|
||||
case 'SINGLE_NODE':
|
||||
return <NodeControls nodeId={itemControls.nodeId} />;
|
||||
case 'PROJECT_SETTINGS':
|
||||
return <ProjectControls />;
|
||||
case 'PLACE_ELEMENT':
|
||||
case 'NODE':
|
||||
return <NodeControls id={itemControls.id} />;
|
||||
case 'RECTANGLE':
|
||||
return <RectangleControls id={itemControls.id} />;
|
||||
case 'ADD_ITEM':
|
||||
return <IconSelection />;
|
||||
default:
|
||||
return null;
|
||||
|
||||
@@ -9,10 +9,10 @@ import { Header } from '../components/Header';
|
||||
import { NodeSettings } from './NodeSettings/NodeSettings';
|
||||
|
||||
interface Props {
|
||||
nodeId: string;
|
||||
id: string;
|
||||
}
|
||||
|
||||
export const NodeControls = ({ nodeId }: Props) => {
|
||||
export const NodeControls = ({ id }: Props) => {
|
||||
const [tab, setTab] = useState(0);
|
||||
const icons = useSceneStore((state) => {
|
||||
return state.icons;
|
||||
@@ -20,7 +20,7 @@ export const NodeControls = ({ nodeId }: Props) => {
|
||||
const sceneActions = useSceneStore((state) => {
|
||||
return state.actions;
|
||||
});
|
||||
const node = useNode(nodeId);
|
||||
const node = useNode(id);
|
||||
|
||||
const onTabChanged = (event: React.SyntheticEvent, newValue: number) => {
|
||||
setTab(newValue);
|
||||
@@ -28,13 +28,11 @@ export const NodeControls = ({ nodeId }: Props) => {
|
||||
|
||||
const onNodeUpdated = useCallback(
|
||||
(updates: Partial<Node>) => {
|
||||
sceneActions.updateNode(nodeId, updates);
|
||||
sceneActions.updateNode(id, updates);
|
||||
},
|
||||
[sceneActions, nodeId]
|
||||
[sceneActions, id]
|
||||
);
|
||||
|
||||
if (!node) return null;
|
||||
|
||||
return (
|
||||
<ControlsContainer
|
||||
header={
|
||||
|
||||
@@ -1,49 +0,0 @@
|
||||
import React, { useCallback } from 'react';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import TextField from '@mui/material/TextField';
|
||||
import Grid from '@mui/material/Grid';
|
||||
import { Header } from '../components/Header';
|
||||
import { Section } from '../components/Section';
|
||||
import { ControlsContainer } from '../components/ControlsContainer';
|
||||
|
||||
interface Values {
|
||||
name?: string;
|
||||
notes?: string;
|
||||
}
|
||||
|
||||
export const ProjectControls = () => {
|
||||
const { register, handleSubmit } = useForm<Values>({
|
||||
defaultValues: {
|
||||
name: '',
|
||||
notes: ''
|
||||
}
|
||||
});
|
||||
|
||||
const onSubmit = useCallback((values: Values) => {
|
||||
console.log(values);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<ControlsContainer header={<Header title="Project settings" />}>
|
||||
<Section>
|
||||
<form onSubmit={handleSubmit(onSubmit)}>
|
||||
<Grid container spacing={4}>
|
||||
<Grid item xs={12}>
|
||||
<TextField {...register('name')} label="Name" fullWidth />
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<TextField
|
||||
{...register('notes')}
|
||||
label="Notes"
|
||||
variant="outlined"
|
||||
rows={12}
|
||||
multiline
|
||||
fullWidth
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</form>
|
||||
</Section>
|
||||
</ControlsContainer>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,42 @@
|
||||
import React, { useCallback } from 'react';
|
||||
import { Rectangle } from 'src/types';
|
||||
import { useTheme } from '@mui/material';
|
||||
import { useSceneStore } from 'src/stores/sceneStore';
|
||||
import { useRectangle } from 'src/hooks/useRectangle';
|
||||
import { ColorSelector } from 'src/components/ColorSelector/ColorSelector';
|
||||
import { ControlsContainer } from '../components/ControlsContainer';
|
||||
import { Section } from '../components/Section';
|
||||
import { Header } from '../components/Header';
|
||||
|
||||
interface Props {
|
||||
id: string;
|
||||
}
|
||||
|
||||
export const RectangleControls = ({ id }: Props) => {
|
||||
const theme = useTheme();
|
||||
const sceneActions = useSceneStore((state) => {
|
||||
return state.actions;
|
||||
});
|
||||
const rectangle = useRectangle(id);
|
||||
|
||||
const onRectangleUpdated = useCallback(
|
||||
(updates: Partial<Rectangle>) => {
|
||||
sceneActions.updateRectangle(id, updates);
|
||||
},
|
||||
[sceneActions, id]
|
||||
);
|
||||
|
||||
return (
|
||||
<ControlsContainer header={<Header title="Rectangle settings" />}>
|
||||
<Section>
|
||||
<ColorSelector
|
||||
colors={Object.values(theme.customVars.diagramPalette)}
|
||||
onChange={(color) => {
|
||||
return onRectangleUpdated({ color });
|
||||
}}
|
||||
activeColor={rectangle.color}
|
||||
/>
|
||||
</Section>
|
||||
</ControlsContainer>
|
||||
);
|
||||
};
|
||||
@@ -1,6 +1,5 @@
|
||||
import React from 'react';
|
||||
import { Card, useTheme } from '@mui/material';
|
||||
import { ItemControlsTypeEnum } from 'src/types';
|
||||
import {
|
||||
PanToolOutlined as PanToolIcon,
|
||||
ZoomInOutlined as ZoomInIcon,
|
||||
@@ -44,7 +43,7 @@ export const ToolMenu = () => {
|
||||
Icon={<AddIcon />}
|
||||
onClick={() => {
|
||||
uiStateStoreActions.setItemControls({
|
||||
type: ItemControlsTypeEnum.PLACE_ELEMENT
|
||||
type: 'ADD_ITEM'
|
||||
});
|
||||
uiStateStoreActions.setMode({
|
||||
type: 'PLACE_ELEMENT',
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
import { useMemo } from 'react';
|
||||
import { useSceneStore } from 'src/stores/sceneStore';
|
||||
import { getItemById } from 'src/utils';
|
||||
|
||||
export const useNode = (nodeId: string) => {
|
||||
export const useNode = (id: string) => {
|
||||
const nodes = useSceneStore((state) => {
|
||||
return state.nodes;
|
||||
});
|
||||
|
||||
const node = useMemo(() => {
|
||||
return nodes.find((n) => {
|
||||
return n.id === nodeId;
|
||||
});
|
||||
}, [nodes, nodeId]);
|
||||
return getItemById(nodes, id).item;
|
||||
}, [nodes, id]);
|
||||
|
||||
return node;
|
||||
};
|
||||
|
||||
15
src/hooks/useRectangle.ts
Normal file
15
src/hooks/useRectangle.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import { useMemo } from 'react';
|
||||
import { useSceneStore } from 'src/stores/sceneStore';
|
||||
import { getItemById } from 'src/utils';
|
||||
|
||||
export const useRectangle = (id: string) => {
|
||||
const rectangles = useSceneStore((state) => {
|
||||
return state.rectangles;
|
||||
});
|
||||
|
||||
const node = useMemo(() => {
|
||||
return getItemById(rectangles, id).item;
|
||||
}, [rectangles, id]);
|
||||
|
||||
return node;
|
||||
};
|
||||
@@ -1,13 +1,12 @@
|
||||
import { produce } from 'immer';
|
||||
import { ItemControlsTypeEnum, ModeActions } from 'src/types';
|
||||
import { getItemAtTile, getItemById } from 'src/utils';
|
||||
import { ModeActions } from 'src/types';
|
||||
import { getItemAtTile, hasMovedTile } from 'src/utils';
|
||||
|
||||
export const Cursor: ModeActions = {
|
||||
mousemove: ({ uiState }) => {
|
||||
if (uiState.mode.type !== 'CURSOR') return;
|
||||
if (uiState.mode.type !== 'CURSOR' || !hasMovedTile(uiState.mouse)) return;
|
||||
|
||||
if (uiState.mode.mousedownItem) {
|
||||
// User is in dragging mode
|
||||
uiState.actions.setMode({
|
||||
type: 'DRAG_ITEMS',
|
||||
showCursor: true,
|
||||
@@ -23,42 +22,42 @@ export const Cursor: ModeActions = {
|
||||
scene
|
||||
});
|
||||
|
||||
const newMode = produce(uiState.mode, (draftState) => {
|
||||
draftState.mousedownItem = itemAtTile;
|
||||
});
|
||||
|
||||
uiState.actions.setMode(newMode);
|
||||
uiState.actions.setMode(
|
||||
produce(uiState.mode, (draftState) => {
|
||||
if (itemAtTile) {
|
||||
draftState.mousedownItem = {
|
||||
type: itemAtTile.type,
|
||||
id: itemAtTile.id
|
||||
};
|
||||
} else {
|
||||
draftState.mousedownItem = null;
|
||||
}
|
||||
})
|
||||
);
|
||||
},
|
||||
mouseup: ({ uiState, scene, isRendererInteraction }) => {
|
||||
mouseup: ({ uiState, isRendererInteraction }) => {
|
||||
if (uiState.mode.type !== 'CURSOR' || !isRendererInteraction) return;
|
||||
|
||||
if (uiState.mode.mousedownItem) {
|
||||
if (uiState.mode.mousedownItem.type === 'NODE') {
|
||||
const { item: node } = getItemById(
|
||||
scene.nodes,
|
||||
uiState.mode.mousedownItem.id
|
||||
);
|
||||
|
||||
uiState.actions.setContextMenu(node);
|
||||
uiState.actions.setItemControls({
|
||||
type: ItemControlsTypeEnum.SINGLE_NODE,
|
||||
nodeId: node.id
|
||||
type: 'NODE',
|
||||
id: uiState.mode.mousedownItem.id
|
||||
});
|
||||
} else if (uiState.mode.mousedownItem.type === 'RECTANGLE') {
|
||||
uiState.actions.setItemControls({
|
||||
type: 'RECTANGLE',
|
||||
id: uiState.mode.mousedownItem.id
|
||||
});
|
||||
}
|
||||
} else {
|
||||
// Empty tile selected
|
||||
uiState.actions.setContextMenu({
|
||||
type: 'EMPTY_TILE',
|
||||
position: uiState.mouse.position.tile
|
||||
});
|
||||
|
||||
uiState.actions.setItemControls(null);
|
||||
}
|
||||
|
||||
const newMode = produce(uiState.mode, (draftState) => {
|
||||
draftState.mousedownItem = null;
|
||||
});
|
||||
|
||||
uiState.actions.setMode(newMode);
|
||||
uiState.actions.setMode(
|
||||
produce(uiState.mode, (draftState) => {
|
||||
draftState.mousedownItem = null;
|
||||
})
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -2,23 +2,24 @@ import { Coords, Size } from './common';
|
||||
import { SceneItem, Connector, SceneItemReference } from './scene';
|
||||
import { IconInput } from './inputs';
|
||||
|
||||
export enum ItemControlsTypeEnum {
|
||||
SINGLE_NODE = 'SINGLE_NODE',
|
||||
PROJECT_SETTINGS = 'PROJECT_SETTINGS',
|
||||
PLACE_ELEMENT = 'PLACE_ELEMENT'
|
||||
interface NodeControls {
|
||||
type: 'NODE';
|
||||
id: string;
|
||||
}
|
||||
|
||||
interface RectangleControls {
|
||||
type: 'RECTANGLE';
|
||||
id: string;
|
||||
}
|
||||
|
||||
interface AddItemControls {
|
||||
type: 'ADD_ITEM';
|
||||
}
|
||||
|
||||
export type ItemControls =
|
||||
| {
|
||||
type: ItemControlsTypeEnum.SINGLE_NODE;
|
||||
nodeId: string;
|
||||
}
|
||||
| {
|
||||
type: ItemControlsTypeEnum.PROJECT_SETTINGS;
|
||||
}
|
||||
| {
|
||||
type: ItemControlsTypeEnum.PLACE_ELEMENT;
|
||||
}
|
||||
| NodeControls
|
||||
| RectangleControls
|
||||
| AddItemControls
|
||||
| null;
|
||||
|
||||
export interface Mouse {
|
||||
|
||||
Reference in New Issue
Block a user