浏览代码

add support for resizing rountangles

Jakob Pietron 2 年之前
父节点
当前提交
de2eac7cd0

+ 3 - 1
src/frontend/rountangleEditor/RountangleActions.ts

@@ -1,10 +1,12 @@
 
-interface CreateRountangle {tag: 'createRountangle', id: string, name: string, posX: number, posY: number}
+interface CreateRountangle {tag: 'createRountangle', id: string, name: string, posX: number, posY: number, width: number, height: number}
 interface MoveRountangle   {tag: 'moveRountangle',   id: string, newPosX: number, newPosY: number}
+interface ResizeRuntangle  {tag: 'resizeRountangle', id: string, width: number, height: number}
 interface DeleteRountangle {tag: 'deleteRountangle', id: string}
 
 export type RountangleAction =
     Readonly<CreateRountangle>
     | Readonly<MoveRountangle>
+    | Readonly<ResizeRuntangle>
     | Readonly<DeleteRountangle>
 ;

+ 18 - 2
src/frontend/rountangleEditor/RountangleComponent.tsx

@@ -1,15 +1,18 @@
 import * as React from "react";
 import {RountangleAction} from "./RountangleActions";
+import {RountangleResizeHandleComponent} from "./RountangleResizeHandleComponent";
 
 export interface RountangleProps {
     id:       string;
     name:     string;
     posX:     number;
     posY:     number;
+    width:    number;
+    height:   number;
     dispatch: (action: RountangleAction) => void;
 }
 
-interface RountangleState {
+export interface RountangleState {
     dragging:    boolean;
     moved:       boolean;
 }
@@ -18,6 +21,8 @@ export class RountangleComponent extends React.Component<RountangleProps, Rounta
     shouldComponentUpdate(nextProps: Readonly<RountangleProps>, nextState: Readonly<RountangleState>, nextContext: any): boolean {
         return this.props.posX !== nextProps.posX
             || this.props.posY !== nextProps.posY
+            || this.props.width !== nextProps.width
+            || this.props.height !== nextProps.height
             || this.state.dragging !== nextState.dragging;
     }
 
@@ -74,7 +79,12 @@ export class RountangleComponent extends React.Component<RountangleProps, Rounta
         return(
           <div
               className={`re-rountangle ${this.state.dragging ? 'dragging' : ''}`}
-              style={{ left: this.props.posX, top: this.props.posY }}
+              style={{
+                  left:   this.props.posX,
+                  top:    this.props.posY,
+                  width:  this.props.width,
+                  height: this.props.height
+                }}
               onPointerDown={this.onPointerDown}
               onPointerMove={this.onPointerMove}
               onPointerUp={this.onPointerUp}
@@ -82,6 +92,12 @@ export class RountangleComponent extends React.Component<RountangleProps, Rounta
               <div className={'re-ruontangle-name'}>
                   {this.props.name}
               </div>
+              <RountangleResizeHandleComponent
+                  id={this.props.id}
+                  dispatch={this.props.dispatch}
+                  height={this.props.height}
+                  width={this.props.width}
+              />
           </div>
         );
     }

+ 11 - 2
src/frontend/rountangleEditor/RountangleEditor.css

@@ -5,8 +5,6 @@
 }
 .re-rountangle {
     position: absolute;
-    width: 100px;
-    height: 66px;
     background: white;
     border: 1px solid black;
     border-radius: 5px;
@@ -14,6 +12,7 @@
     flex-direction: column;
     justify-content: center;
     text-align: center;
+    overflow: hidden;
 }
 .re-rountangle:hover {
     cursor: grab;
@@ -23,4 +22,14 @@
 }
 .re-ruontangle-name {
 
+}
+.re-rountangle-resize-handle {
+    width: 20px;
+    height: 20px;
+    position: absolute;
+    bottom: -10px;
+    right: -10px;
+    background: deeppink;
+    cursor: nwse-resize;
+    rotate: 45deg;
 }

+ 6 - 2
src/frontend/rountangleEditor/RountangleEditor.tsx

@@ -16,8 +16,8 @@ export class RountangleEditor extends React.Component<{}, RountangleEditorState>
         this.canvasRef = React.createRef<HTMLDivElement>();
 
         this.state = {store: new RountangleStore()};
-        this.state.store.dispatch({tag: 'createRountangle', id: generateRandomGUID(), posX: 10, posY: 50, name:'ABC'});
-        this.state.store.dispatch({tag: 'createRountangle', id: generateRandomGUID(), posX: 250, posY: 50, name:'DEF'});
+        this.state.store.dispatch({tag: 'createRountangle', id: generateRandomGUID(), posX: 10, posY: 50, width: 100, height: 66, name:'ABC'});
+        this.state.store.dispatch({tag: 'createRountangle', id: generateRandomGUID(), posX: 250, posY: 50, width: 100, height: 66, name:'DEF'});
 
     }
 
@@ -37,6 +37,8 @@ export class RountangleEditor extends React.Component<{}, RountangleEditorState>
                     id: generateRandomGUID(),
                     posX: event.pageX - this.canvasRef.current.offsetLeft,
                     posY: event.pageY - this.canvasRef.current.offsetTop,
+                    width: 100,
+                    height: 66,
                     name: newRountangleName});
             }
         }
@@ -61,6 +63,8 @@ export class RountangleEditor extends React.Component<{}, RountangleEditorState>
                                 name={rountangle.name}
                                 posX={rountangle.posX}
                                 posY={rountangle.posY}
+                                width={rountangle.width}
+                                height={rountangle.height}
                                 dispatch={action => {
                                     this.state.store.dispatch(action);
                                 }}

+ 75 - 0
src/frontend/rountangleEditor/RountangleResizeHandleComponent.tsx

@@ -0,0 +1,75 @@
+import * as React from "react";
+import {RountangleAction} from "./RountangleActions";
+import {RountangleProps, RountangleState} from "./RountangleComponent";
+
+interface RountangleResizeHandleProps {
+    id:       string;
+    dispatch: (action: RountangleAction) => void;
+    width:    number;
+    height:   number;
+}
+
+export class RountangleResizeHandleComponent extends React.Component<RountangleResizeHandleProps, RountangleState> {
+    shouldComponentUpdate(nextProps: Readonly<RountangleResizeHandleProps>, nextState: Readonly<RountangleState>, nextContext: any): boolean {
+        return false;
+    }
+
+    constructor(props: RountangleProps) {
+        super(props);
+        this.state = {
+            dragging: false,
+            moved:    false
+        }
+    }
+
+    onPointerDown = (event: React.PointerEvent<HTMLDivElement>) => {
+        // only left mouse button
+        if (event.button !== 0) return;
+
+        event.currentTarget.setPointerCapture(event.pointerId);
+
+        this.setState({
+            dragging:       true,
+            moved:          false
+        });
+
+        event.stopPropagation();
+        event.preventDefault();
+    }
+
+    onPointerMove = (event: React.PointerEvent<HTMLDivElement>) => {
+        if (!this.state.dragging) return;
+        this.setState({moved: true});
+        if (event.movementX !== 0 || event.movementY !== 0) {
+            this.props.dispatch({
+                tag:    'resizeRountangle',
+                id:     this.props.id,
+                width:  this.props.width + event.movementX,
+                height: this.props.height + event.movementY
+            });
+        }
+
+        event.stopPropagation();
+        event.preventDefault();
+    }
+
+    onPointerUp = (event: React.PointerEvent<HTMLDivElement>) => {
+        event.currentTarget.releasePointerCapture(event.pointerId);
+        event.stopPropagation();
+        event.preventDefault();
+
+        // if left button, complete dragging
+        if (event.button === 0) {
+            this.setState({dragging: false, moved: false});
+        }
+    }
+
+    render() {
+        return <div
+            className={'re-rountangle-resize-handle'}
+            onPointerDown={this.onPointerDown}
+            onPointerMove={this.onPointerMove}
+            onPointerUp={this.onPointerUp}
+        />
+    }
+}

+ 12 - 4
src/frontend/rountangleEditor/RountangleStore.ts

@@ -2,9 +2,11 @@ import {RountangleAction} from "./RountangleActions";
 import {assertNever} from "../../util/assert";
 
 interface Rountangle {
-    name: string;
-    posX: number;
-    posY: number;
+    name:   string;
+    posX:   number;
+    posY:   number;
+    width:  number;
+    height: number;
 }
 
 export class RountangleStore {
@@ -22,7 +24,7 @@ export class RountangleStore {
             case "createRountangle":
                 this.state = {
                     ...this.state,
-                    [action.id]: {name: action.name, posX: action.posX, posY: action.posY}
+                    [action.id]: {name: action.name, posX: action.posX, posY: action.posY, width: action.width, height: action.height}
                 };
                 break;
             case 'moveRountangle':
@@ -36,6 +38,12 @@ export class RountangleStore {
                     ...(delete this.state[action.id] && this.state)
                 }
                 break;
+            case 'resizeRountangle':
+                this.state = {
+                    ...this.state,
+                    [action.id]: {...this.state[action.id], width: action.width, height: action.height}
+                }
+                break;
             default: assertNever(action);
         }
         this.onDispatchListeners.forEach(listener => listener(action));