Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/add touch support #67

Closed
wants to merge 14 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 31 additions & 2 deletions src/Diagram/Link/Link.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ const useContextRefs = () => {
const Link = (props) => {
const { input, output, link, onDelete } = props;
const pathRef = useRef();
const longPressTimeout = useRef(0);
const [labelPosition, setLabelPosition] = useState();
const { canvas, portRefs, nodeRefs } = useContextRefs();
const inputPoint = useMemo(() => getCoords(input, portRefs, nodeRefs, canvas), [input, portRefs, nodeRefs, canvas]);
Expand Down Expand Up @@ -54,10 +55,38 @@ const Link = (props) => {
}
}, [link.readonly, onDelete]);

const onTouchStart = useCallback(() => {
const now = Date.now();

if (now - longPressTimeout.current < 1000) {
if (onDelete && !link.readonly) {
onDelete(link);
}
longPressTimeout.current = 0;
return;
}
longPressTimeout.current = Date.now();
}, [link.readonly, onDelete]);

return (
<g className={classList}>
{!link.readonly && (<path d={path} className="bi-link-ghost" onDoubleClick={onDoubleClick} />)}
<path d={path} ref={pathRef} className="bi-link-path" onDoubleClick={onDoubleClick} />
{
!link.readonly && (
<path
d={path}
className="bi-link-ghost"
onDoubleClick={onDoubleClick}
onTouchStart={onTouchStart}
/>
)
}
<path
d={path}
ref={pathRef}
className="bi-link-path"
onDoubleClick={onDoubleClick}
onTouchStart={onTouchStart}
/>
{link.label && labelPosition && (<LinkLabel position={labelPosition} label={link.label} />)}
</g>
);
Expand Down
18 changes: 15 additions & 3 deletions src/Diagram/Port/Port.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@ import useDrag from '../../shared/internal_hooks/useDrag';
import useCanvas from '../../shared/internal_hooks/useCanvas';
import getRelativePoint from '../../shared/functions/getRelativePoint';

const getTouchEndTarget = (event) => document.elementFromPoint(
event.changedTouches[0].clientX,
event.changedTouches[0].clientY,
);

/**
* Port
* @param props
Expand All @@ -20,15 +25,22 @@ const Port = (props) => {
event.stopImmediatePropagation();
event.stopPropagation();
const from = getRelativePoint(info.start, [canvas.x, canvas.y]);
const to = getRelativePoint([event.clientX, event.clientY], [canvas.x, canvas.y]);
let to;
if (event.touches) {
to = getRelativePoint([event.touches[0].clientX, event.touches[0].clientY], [canvas.x, canvas.y]);
} else {
to = getRelativePoint([event.clientX, event.clientY], [canvas.x, canvas.y]);
}

onDragNewSegment(id, from, to, alignment);
}
});

onDragEnd((event) => {
const targetPort = event.target.getAttribute('data-port-id');
if (targetPort && event.target !== ref.current && canLink(id, targetPort, type) && onSegmentConnect) {
// There is no target defined for a touchend event and it must be computed
const target = event.changedTouches ? getTouchEndTarget(event) : event.target;
const targetPort = target.getAttribute('data-port-id');
if (targetPort && target !== ref.current && canLink(id, targetPort, type) && onSegmentConnect) {
const args = type === 'input' ? [id, targetPort, type] : [targetPort, id, type];

onSegmentConnect(...args);
Expand Down
2 changes: 2 additions & 0 deletions src/Diagram/diagram.scss
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
background-color: #f8fafc;
position: relative;
overflow: hidden;
// Prevent touch dragging inside the diagram from scrolling the page
touch-action: none;

&.enlarge-diagram-canvas {
width: 312rem;
Expand Down
40 changes: 37 additions & 3 deletions src/shared/internal_hooks/useDrag.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,15 @@ const defaultOptions = {
* @param event
* @returns {*[]}
*/
const getEventCoordinates = (event) => [event.clientX, event.clientY];
const getEventCoordinates = (event) => {
if (event.touches) {
return [event.touches[0].clientX, event.touches[0].clientY];
}

return [event.clientX, event.clientY];
};

const getTouchEndCoordinates = (event) => [event.changedTouches[0].pageX, event.changedTouches[0].pageY];

/**
* Create a persistent callback reference that will live trough a component lifecycle
Expand Down Expand Up @@ -78,6 +86,8 @@ const createCallbackRef = (ref) => useCallback((callback) => {
* }
* ```
*/
const isTouchEnabled = ('ontouchstart' in window) || (navigator.maxTouchPoints > 0) || (navigator.msMaxTouchPoints > 0);

const useDrag = (options = defaultOptions) => {
const targetRef = options.ref || useRef(); // the target draggable element
const dragStartHandlerRef = useRef(); // a ref to user's onDragStart handler
Expand Down Expand Up @@ -107,7 +117,12 @@ const useDrag = (options = defaultOptions) => {
*/
const onDrag = useCallback(throttle((event) => {
if (info.isDragging) {
info.offset = [info.start[0] - event.clientX, info.start[1] - event.clientY];
if (event.touches) {
// Using single touch support for now, just go with the first touch event
info.offset = [info.start[0] - event.touches[0].clientX, info.start[1] - event.touches[0].clientY];
} else {
info.offset = [info.start[0] - event.clientX, info.start[1] - event.clientY];
}

if (dragHandlerRef.current) {
dragHandlerRef.current(event, { ...info });
Expand All @@ -121,7 +136,12 @@ const useDrag = (options = defaultOptions) => {
const onDragEnd = useCallback((event) => {
if (info.isDragging) {
info.isDragging = false;
info.end = getEventCoordinates(event);

if (event.touches) {
info.end = getTouchEndCoordinates(event);
} else {
info.end = getEventCoordinates(event);
}

if (dragEndHandlerRef.current) {
dragEndHandlerRef.current(event, { ...info });
Expand All @@ -143,13 +163,27 @@ const useDrag = (options = defaultOptions) => {
targetRef.current.addEventListener('mousedown', _onDragStart);
document.addEventListener('mousemove', _onDrag);
document.addEventListener('mouseup', _onDragEnd);

// Support touch events. Keep mouse events active to support touch screen laptops.
if (isTouchEnabled) {
targetRef.current.addEventListener('touchstart', _onDragStart);
document.addEventListener('touchmove', _onDrag);
document.addEventListener('touchend', _onDragEnd);
}
}

return () => {
if (targetRef.current) {
targetRef.current.removeEventListener('mousedown', _onDragStart);
document.removeEventListener('mousemove', _onDrag);
document.removeEventListener('mouseup', _onDragEnd);

// Support touch events. Keep mouse events active to support touch screen laptops.
if (isTouchEnabled) {
targetRef.current.addEventListener('touchstart', _onDragStart);
document.addEventListener('touchmove', _onDrag);
document.addEventListener('touchend', _onDragEnd);
}
}
};
}, [targetRef.current]);
Expand Down