Skip to content

Commit

Permalink
Add very, very slow drag+drop.
Browse files Browse the repository at this point in the history
We push all state changes through Replicache to gain access to its
conflict resolution. If another client is moving an object at the
same time, we should merge their movements, or something otherwise
reasonable should happen.

Replicache is not yet optimized for this type of usage - all writes
go to disk. So it's not very pretty right now, but it's correct.

Will fix Replicache subsequently.
  • Loading branch information
aboodman committed Feb 16, 2021
1 parent 6cf46c1 commit e04f2fa
Show file tree
Hide file tree
Showing 3 changed files with 79 additions and 14 deletions.
59 changes: 55 additions & 4 deletions src/Designer2.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, {CSSProperties, useState} from 'react';
import React, {CSSProperties, MouseEvent, useState} from 'react';
import {Rect2} from './objects/Rect2';
import {Handler} from './Handler2';
import {Data} from './data';
Expand All @@ -9,12 +9,63 @@ export function Designer2({data}: {data: Data}) {
// TODO: This should be stored in Replicache too, since we will be rendering
// other users' selections.
const [selectedID, setSelectedID] = useState('');
const [lastDrag, setLastDrag] = useState(null);

return <div style={styles.container}>
<Handler {...{data, selectedID, onMouseLeave: () => setSelectedID('')}}/>
const onMouseEnter = (e: MouseEvent, id: string) => {
if (fromClass(e, 'shape')) {
if (lastDrag == null) {
setSelectedID(id);
}
}
};

const onMouseLeave = (e: MouseEvent) => {
if (fromClass(e, 'handler')) {
if (lastDrag == null) {
setSelectedID('');
}
}
};

const onMouseDown = (e: MouseEvent) => {
updateLastDrag(e);
};

const onMouseMove = (e: MouseEvent) => {
if (lastDrag == null) {
return;
}
data.moveShape({
id: selectedID,
dx: e.clientX - lastDrag.x,
dy: e.clientY - lastDrag.y
});
updateLastDrag(e);
};

const onMouseUp = (e: MouseEvent) => {
setLastDrag(null);
};

const fromClass = (e: MouseEvent, className: string) => {
const target = e.target as HTMLElement;
return target.classList.contains(className);
};

const updateLastDrag = (e: MouseEvent) => {
setLastDrag({x: e.clientX, y: e.clientY});
}

return <div {...{className: 'container', style: styles.container, onMouseMove, onMouseUp}}>
<Handler {...{
data,
selectedID,
onMouseDown,
onMouseLeave,
}}/>
<svg style={styles.svg} width={350} height={400}>
{ids.map(
id => <Rect2 key={id} {...{data, id, onMouseEnter: () => setSelectedID(id)}}/>)}
id => <Rect2 key={id} {...{data, id, onMouseEnter: (e) => onMouseEnter(e, id)}}/>)}
</svg>
</div>;
}
Expand Down
22 changes: 15 additions & 7 deletions src/Handler2.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,19 @@
import React, {CSSProperties} from 'react';
import React, {CSSProperties, MouseEventHandler} from 'react';
import {Data} from './data';

export function Handler({data, selectedID, onMouseLeave}: {data: Data, selectedID: string, onMouseLeave: () => void}) {
export function Handler({data, selectedID, onMouseDown, onMouseLeave}:
{
data: Data,
selectedID: string,
onMouseDown: MouseEventHandler,
onMouseLeave: MouseEventHandler,
}) {
const shape = data.useShapeByID(selectedID);
if (!shape) {
return null;
}

let handlerStyle = {
const handlerStyle = {
...styles.handler,
width: shape.width + 4,
height: shape.height + 4,
Expand All @@ -16,10 +22,12 @@ export function Handler({data, selectedID, onMouseLeave}: {data: Data, selectedI
transform: `rotate(${shape.rotate}deg)`
};

return <div
style={handlerStyle}
onMouseLeave={onMouseLeave}>
</div>;
return <div {...{
className: 'handler',
style: handlerStyle,
onMouseDown,
onMouseLeave,
}}/>;
}

const styles = {
Expand Down
12 changes: 9 additions & 3 deletions src/objects/Rect2.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
import React from 'react';
import React, { MouseEventHandler } from 'react';
import {Data} from '../data';
import {getObjectAttributes} from './attribs';

export function Rect2({data, id, onMouseEnter}: {data: Data, id: string, onMouseEnter: () => void}) {
export function Rect2(
{data, id, onMouseEnter}: {
data: Data,
id: string,
onMouseEnter: MouseEventHandler
})
{
const shape = data.useShapeByID(id);
if (!shape) {
return null;
}
return <rect {...{...getObjectAttributes(shape), onMouseEnter}} />;
return <rect {...{...getObjectAttributes(shape), className: 'shape', onMouseEnter}} />;
}

0 comments on commit e04f2fa

Please sign in to comment.