feat(nodes-edges): add nodesFocusable, edgesFocusable, cleanup keyboard controls
This commit is contained in:
@@ -51,7 +51,7 @@ export default (EdgeComponent: ComponentType<EdgeProps>) => {
|
||||
markerStart,
|
||||
rfId,
|
||||
ariaLabel,
|
||||
disableKeyboardA11y,
|
||||
isFocusable,
|
||||
pathOptions,
|
||||
interactionWidth,
|
||||
}: WrapEdgeProps): JSX.Element | null => {
|
||||
@@ -158,12 +158,12 @@ export default (EdgeComponent: ComponentType<EdgeProps>) => {
|
||||
onMouseEnter={onEdgeMouseEnter}
|
||||
onMouseMove={onEdgeMouseMove}
|
||||
onMouseLeave={onEdgeMouseLeave}
|
||||
onKeyDown={disableKeyboardA11y ? undefined : onKeyDown}
|
||||
tabIndex={disableKeyboardA11y ? undefined : 0}
|
||||
role={disableKeyboardA11y ? undefined : 'button'}
|
||||
onKeyDown={isFocusable ? onKeyDown : undefined}
|
||||
tabIndex={isFocusable ? 0 : undefined}
|
||||
role={isFocusable ? 'button' : undefined}
|
||||
data-testid={`rf__edge-${id}`}
|
||||
aria-label={ariaLabel === null ? undefined : ariaLabel ? ariaLabel : `Edge from ${source} to ${target}`}
|
||||
aria-describedby={disableKeyboardA11y ? undefined : `${ARIA_EDGE_DESC_KEY}-${rfId}`}
|
||||
aria-describedby={isFocusable ? `${ARIA_EDGE_DESC_KEY}-${rfId}` : undefined}
|
||||
ref={edgeRef}
|
||||
>
|
||||
{!updating && (
|
||||
|
||||
@@ -11,10 +11,10 @@ import { NodeProps, WrapNodeProps, XYPosition } from '../../types';
|
||||
import { elementSelectionKeys } from '../../utils';
|
||||
|
||||
export const arrowKeyDiffs: Record<string, XYPosition> = {
|
||||
ArrowUp: { x: 0, y: -10 },
|
||||
ArrowDown: { x: 0, y: 10 },
|
||||
ArrowLeft: { x: -10, y: 0 },
|
||||
ArrowRight: { x: 10, y: 0 },
|
||||
ArrowUp: { x: 0, y: -1 },
|
||||
ArrowDown: { x: 0, y: 1 },
|
||||
ArrowLeft: { x: -1, y: 0 },
|
||||
ArrowRight: { x: 1, y: 0 },
|
||||
};
|
||||
|
||||
export default (NodeComponent: ComponentType<NodeProps>) => {
|
||||
@@ -38,6 +38,7 @@ export default (NodeComponent: ComponentType<NodeProps>) => {
|
||||
isDraggable,
|
||||
isSelectable,
|
||||
isConnectable,
|
||||
isFocusable,
|
||||
selectNodesOnDrag,
|
||||
sourcePosition,
|
||||
targetPosition,
|
||||
@@ -82,6 +83,7 @@ export default (NodeComponent: ComponentType<NodeProps>) => {
|
||||
};
|
||||
|
||||
const onKeyDown = (event: KeyboardEvent) => {
|
||||
const { snapGrid, snapToGrid } = store.getState();
|
||||
if (elementSelectionKeys.includes(event.key) && isSelectable) {
|
||||
const unselect = event.key === 'Escape';
|
||||
if (unselect) {
|
||||
@@ -92,14 +94,28 @@ export default (NodeComponent: ComponentType<NodeProps>) => {
|
||||
store,
|
||||
unselect,
|
||||
});
|
||||
} else if (selected && Object.prototype.hasOwnProperty.call(arrowKeyDiffs, event.key)) {
|
||||
} else if (
|
||||
!disableKeyboardA11y &&
|
||||
isDraggable &&
|
||||
selected &&
|
||||
Object.prototype.hasOwnProperty.call(arrowKeyDiffs, event.key)
|
||||
) {
|
||||
store.setState({
|
||||
ariaLiveMessage: `Moved selected node ten pixels ${event.key
|
||||
ariaLiveMessage: `Moved selected node ${event.key
|
||||
.replace('Arrow', '')
|
||||
.toLowerCase()}. New position, x: ${~~xPos}, y: ${~~yPos}`,
|
||||
});
|
||||
|
||||
updatePositions(arrowKeyDiffs[event.key]);
|
||||
// by default a node moves 5px on each key press, or 20px if shift is pressed
|
||||
// if snap grid is enabled, we use that for the velocity.
|
||||
const xVelo = snapToGrid ? snapGrid[0] : 5;
|
||||
const yVelo = snapToGrid ? snapGrid[1] : 5;
|
||||
const factor = event.shiftKey ? 4 : 1;
|
||||
|
||||
updatePositions({
|
||||
x: arrowKeyDiffs[event.key].x * xVelo * factor,
|
||||
y: arrowKeyDiffs[event.key].y * yVelo * factor,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@@ -179,9 +195,9 @@ export default (NodeComponent: ComponentType<NodeProps>) => {
|
||||
onContextMenu={onContextMenuHandler}
|
||||
onClick={onSelectNodeHandler}
|
||||
onDoubleClick={onDoubleClickHandler}
|
||||
onKeyDown={disableKeyboardA11y ? undefined : onKeyDown}
|
||||
tabIndex={disableKeyboardA11y ? undefined : 0}
|
||||
role={disableKeyboardA11y ? undefined : 'button'}
|
||||
onKeyDown={isFocusable ? onKeyDown : undefined}
|
||||
tabIndex={isFocusable ? 0 : undefined}
|
||||
role={isFocusable ? 'button' : undefined}
|
||||
aria-describedby={disableKeyboardA11y ? undefined : `${ARIA_NODE_DESC_KEY}-${rfId}`}
|
||||
aria-label={ariaLabel}
|
||||
>
|
||||
|
||||
@@ -18,6 +18,8 @@ type StoreUpdaterProps = Pick<
|
||||
| 'onClickConnectEnd'
|
||||
| 'nodesDraggable'
|
||||
| 'nodesConnectable'
|
||||
| 'nodesFocusable'
|
||||
| 'edgesFocusable'
|
||||
| 'minZoom'
|
||||
| 'maxZoom'
|
||||
| 'nodeExtent'
|
||||
@@ -89,6 +91,8 @@ const StoreUpdater = ({
|
||||
onClickConnectEnd,
|
||||
nodesDraggable,
|
||||
nodesConnectable,
|
||||
nodesFocusable,
|
||||
edgesFocusable,
|
||||
minZoom,
|
||||
maxZoom,
|
||||
nodeExtent,
|
||||
@@ -145,6 +149,8 @@ const StoreUpdater = ({
|
||||
useDirectStoreUpdater('onClickConnectEnd', onClickConnectEnd, store.setState);
|
||||
useDirectStoreUpdater('nodesDraggable', nodesDraggable, store.setState);
|
||||
useDirectStoreUpdater('nodesConnectable', nodesConnectable, store.setState);
|
||||
useDirectStoreUpdater('nodesFocusable', nodesFocusable, store.setState);
|
||||
useDirectStoreUpdater('edgesFocusable', edgesFocusable, store.setState);
|
||||
useDirectStoreUpdater('elementsSelectable', elementsSelectable, store.setState);
|
||||
useDirectStoreUpdater('snapToGrid', snapToGrid, store.setState);
|
||||
useDirectStoreUpdater('snapGrid', snapGrid, store.setState);
|
||||
|
||||
Reference in New Issue
Block a user