feat(nodes-edges): add nodesFocusable, edgesFocusable, cleanup keyboard controls
This commit is contained in:
@@ -92,7 +92,7 @@ const BasicFlow = () => {
|
|||||||
defaultEdgeOptions={defaultEdgeOptions}
|
defaultEdgeOptions={defaultEdgeOptions}
|
||||||
selectNodesOnDrag={false}
|
selectNodesOnDrag={false}
|
||||||
>
|
>
|
||||||
<Background variant={BackgroundVariant.Lines} />
|
<Background variant={BackgroundVariant.Dots} />
|
||||||
<MiniMap />
|
<MiniMap />
|
||||||
<Controls />
|
<Controls />
|
||||||
|
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ export default (EdgeComponent: ComponentType<EdgeProps>) => {
|
|||||||
markerStart,
|
markerStart,
|
||||||
rfId,
|
rfId,
|
||||||
ariaLabel,
|
ariaLabel,
|
||||||
disableKeyboardA11y,
|
isFocusable,
|
||||||
pathOptions,
|
pathOptions,
|
||||||
interactionWidth,
|
interactionWidth,
|
||||||
}: WrapEdgeProps): JSX.Element | null => {
|
}: WrapEdgeProps): JSX.Element | null => {
|
||||||
@@ -158,12 +158,12 @@ export default (EdgeComponent: ComponentType<EdgeProps>) => {
|
|||||||
onMouseEnter={onEdgeMouseEnter}
|
onMouseEnter={onEdgeMouseEnter}
|
||||||
onMouseMove={onEdgeMouseMove}
|
onMouseMove={onEdgeMouseMove}
|
||||||
onMouseLeave={onEdgeMouseLeave}
|
onMouseLeave={onEdgeMouseLeave}
|
||||||
onKeyDown={disableKeyboardA11y ? undefined : onKeyDown}
|
onKeyDown={isFocusable ? onKeyDown : undefined}
|
||||||
tabIndex={disableKeyboardA11y ? undefined : 0}
|
tabIndex={isFocusable ? 0 : undefined}
|
||||||
role={disableKeyboardA11y ? undefined : 'button'}
|
role={isFocusable ? 'button' : undefined}
|
||||||
data-testid={`rf__edge-${id}`}
|
data-testid={`rf__edge-${id}`}
|
||||||
aria-label={ariaLabel === null ? undefined : ariaLabel ? ariaLabel : `Edge from ${source} to ${target}`}
|
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}
|
ref={edgeRef}
|
||||||
>
|
>
|
||||||
{!updating && (
|
{!updating && (
|
||||||
|
|||||||
@@ -11,10 +11,10 @@ import { NodeProps, WrapNodeProps, XYPosition } from '../../types';
|
|||||||
import { elementSelectionKeys } from '../../utils';
|
import { elementSelectionKeys } from '../../utils';
|
||||||
|
|
||||||
export const arrowKeyDiffs: Record<string, XYPosition> = {
|
export const arrowKeyDiffs: Record<string, XYPosition> = {
|
||||||
ArrowUp: { x: 0, y: -10 },
|
ArrowUp: { x: 0, y: -1 },
|
||||||
ArrowDown: { x: 0, y: 10 },
|
ArrowDown: { x: 0, y: 1 },
|
||||||
ArrowLeft: { x: -10, y: 0 },
|
ArrowLeft: { x: -1, y: 0 },
|
||||||
ArrowRight: { x: 10, y: 0 },
|
ArrowRight: { x: 1, y: 0 },
|
||||||
};
|
};
|
||||||
|
|
||||||
export default (NodeComponent: ComponentType<NodeProps>) => {
|
export default (NodeComponent: ComponentType<NodeProps>) => {
|
||||||
@@ -38,6 +38,7 @@ export default (NodeComponent: ComponentType<NodeProps>) => {
|
|||||||
isDraggable,
|
isDraggable,
|
||||||
isSelectable,
|
isSelectable,
|
||||||
isConnectable,
|
isConnectable,
|
||||||
|
isFocusable,
|
||||||
selectNodesOnDrag,
|
selectNodesOnDrag,
|
||||||
sourcePosition,
|
sourcePosition,
|
||||||
targetPosition,
|
targetPosition,
|
||||||
@@ -82,6 +83,7 @@ export default (NodeComponent: ComponentType<NodeProps>) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const onKeyDown = (event: KeyboardEvent) => {
|
const onKeyDown = (event: KeyboardEvent) => {
|
||||||
|
const { snapGrid, snapToGrid } = store.getState();
|
||||||
if (elementSelectionKeys.includes(event.key) && isSelectable) {
|
if (elementSelectionKeys.includes(event.key) && isSelectable) {
|
||||||
const unselect = event.key === 'Escape';
|
const unselect = event.key === 'Escape';
|
||||||
if (unselect) {
|
if (unselect) {
|
||||||
@@ -92,14 +94,28 @@ export default (NodeComponent: ComponentType<NodeProps>) => {
|
|||||||
store,
|
store,
|
||||||
unselect,
|
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({
|
store.setState({
|
||||||
ariaLiveMessage: `Moved selected node ten pixels ${event.key
|
ariaLiveMessage: `Moved selected node ${event.key
|
||||||
.replace('Arrow', '')
|
.replace('Arrow', '')
|
||||||
.toLowerCase()}. New position, x: ${~~xPos}, y: ${~~yPos}`,
|
.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}
|
onContextMenu={onContextMenuHandler}
|
||||||
onClick={onSelectNodeHandler}
|
onClick={onSelectNodeHandler}
|
||||||
onDoubleClick={onDoubleClickHandler}
|
onDoubleClick={onDoubleClickHandler}
|
||||||
onKeyDown={disableKeyboardA11y ? undefined : onKeyDown}
|
onKeyDown={isFocusable ? onKeyDown : undefined}
|
||||||
tabIndex={disableKeyboardA11y ? undefined : 0}
|
tabIndex={isFocusable ? 0 : undefined}
|
||||||
role={disableKeyboardA11y ? undefined : 'button'}
|
role={isFocusable ? 'button' : undefined}
|
||||||
aria-describedby={disableKeyboardA11y ? undefined : `${ARIA_NODE_DESC_KEY}-${rfId}`}
|
aria-describedby={disableKeyboardA11y ? undefined : `${ARIA_NODE_DESC_KEY}-${rfId}`}
|
||||||
aria-label={ariaLabel}
|
aria-label={ariaLabel}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -18,6 +18,8 @@ type StoreUpdaterProps = Pick<
|
|||||||
| 'onClickConnectEnd'
|
| 'onClickConnectEnd'
|
||||||
| 'nodesDraggable'
|
| 'nodesDraggable'
|
||||||
| 'nodesConnectable'
|
| 'nodesConnectable'
|
||||||
|
| 'nodesFocusable'
|
||||||
|
| 'edgesFocusable'
|
||||||
| 'minZoom'
|
| 'minZoom'
|
||||||
| 'maxZoom'
|
| 'maxZoom'
|
||||||
| 'nodeExtent'
|
| 'nodeExtent'
|
||||||
@@ -89,6 +91,8 @@ const StoreUpdater = ({
|
|||||||
onClickConnectEnd,
|
onClickConnectEnd,
|
||||||
nodesDraggable,
|
nodesDraggable,
|
||||||
nodesConnectable,
|
nodesConnectable,
|
||||||
|
nodesFocusable,
|
||||||
|
edgesFocusable,
|
||||||
minZoom,
|
minZoom,
|
||||||
maxZoom,
|
maxZoom,
|
||||||
nodeExtent,
|
nodeExtent,
|
||||||
@@ -145,6 +149,8 @@ const StoreUpdater = ({
|
|||||||
useDirectStoreUpdater('onClickConnectEnd', onClickConnectEnd, store.setState);
|
useDirectStoreUpdater('onClickConnectEnd', onClickConnectEnd, store.setState);
|
||||||
useDirectStoreUpdater('nodesDraggable', nodesDraggable, store.setState);
|
useDirectStoreUpdater('nodesDraggable', nodesDraggable, store.setState);
|
||||||
useDirectStoreUpdater('nodesConnectable', nodesConnectable, store.setState);
|
useDirectStoreUpdater('nodesConnectable', nodesConnectable, store.setState);
|
||||||
|
useDirectStoreUpdater('nodesFocusable', nodesFocusable, store.setState);
|
||||||
|
useDirectStoreUpdater('edgesFocusable', edgesFocusable, store.setState);
|
||||||
useDirectStoreUpdater('elementsSelectable', elementsSelectable, store.setState);
|
useDirectStoreUpdater('elementsSelectable', elementsSelectable, store.setState);
|
||||||
useDirectStoreUpdater('snapToGrid', snapToGrid, store.setState);
|
useDirectStoreUpdater('snapToGrid', snapToGrid, store.setState);
|
||||||
useDirectStoreUpdater('snapGrid', snapGrid, store.setState);
|
useDirectStoreUpdater('snapGrid', snapGrid, store.setState);
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ const selector = (s: ReactFlowState) => ({
|
|||||||
connectionNodeId: s.connectionNodeId,
|
connectionNodeId: s.connectionNodeId,
|
||||||
connectionHandleType: s.connectionHandleType,
|
connectionHandleType: s.connectionHandleType,
|
||||||
nodesConnectable: s.nodesConnectable,
|
nodesConnectable: s.nodesConnectable,
|
||||||
|
edgesFocusable: s.edgesFocusable,
|
||||||
elementsSelectable: s.elementsSelectable,
|
elementsSelectable: s.elementsSelectable,
|
||||||
width: s.width,
|
width: s.width,
|
||||||
height: s.height,
|
height: s.height,
|
||||||
@@ -58,6 +59,7 @@ const EdgeRenderer = (props: EdgeRendererProps) => {
|
|||||||
connectionNodeId,
|
connectionNodeId,
|
||||||
connectionHandleType,
|
connectionHandleType,
|
||||||
nodesConnectable,
|
nodesConnectable,
|
||||||
|
edgesFocusable,
|
||||||
elementsSelectable,
|
elementsSelectable,
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
@@ -119,6 +121,7 @@ const EdgeRenderer = (props: EdgeRendererProps) => {
|
|||||||
const targetHandle = getHandle(targetNodeHandles!, edge.targetHandle || null);
|
const targetHandle = getHandle(targetNodeHandles!, edge.targetHandle || null);
|
||||||
const sourcePosition = sourceHandle?.position || Position.Bottom;
|
const sourcePosition = sourceHandle?.position || Position.Bottom;
|
||||||
const targetPosition = targetHandle?.position || Position.Top;
|
const targetPosition = targetHandle?.position || Position.Top;
|
||||||
|
const isFocusable = !!(edge.focusable || (edgesFocusable && typeof edge.focusable === 'undefined'));
|
||||||
|
|
||||||
if (!sourceHandle || !targetHandle) {
|
if (!sourceHandle || !targetHandle) {
|
||||||
devWarn(
|
devWarn(
|
||||||
@@ -181,7 +184,7 @@ const EdgeRenderer = (props: EdgeRendererProps) => {
|
|||||||
onEdgeUpdateEnd={props.onEdgeUpdateEnd}
|
onEdgeUpdateEnd={props.onEdgeUpdateEnd}
|
||||||
rfId={props.rfId}
|
rfId={props.rfId}
|
||||||
ariaLabel={edge.ariaLabel}
|
ariaLabel={edge.ariaLabel}
|
||||||
disableKeyboardA11y={props.disableKeyboardA11y}
|
isFocusable={isFocusable}
|
||||||
pathOptions={'pathOptions' in edge ? edge.pathOptions : undefined}
|
pathOptions={'pathOptions' in edge ? edge.pathOptions : undefined}
|
||||||
interactionWidth={edge.interactionWidth}
|
interactionWidth={edge.interactionWidth}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -31,12 +31,16 @@ type NodeRendererProps = Pick<
|
|||||||
const selector = (s: ReactFlowState) => ({
|
const selector = (s: ReactFlowState) => ({
|
||||||
nodesDraggable: s.nodesDraggable,
|
nodesDraggable: s.nodesDraggable,
|
||||||
nodesConnectable: s.nodesConnectable,
|
nodesConnectable: s.nodesConnectable,
|
||||||
|
nodesFocusable: s.nodesFocusable,
|
||||||
elementsSelectable: s.elementsSelectable,
|
elementsSelectable: s.elementsSelectable,
|
||||||
updateNodeDimensions: s.updateNodeDimensions,
|
updateNodeDimensions: s.updateNodeDimensions,
|
||||||
});
|
});
|
||||||
|
|
||||||
const NodeRenderer = (props: NodeRendererProps) => {
|
const NodeRenderer = (props: NodeRendererProps) => {
|
||||||
const { nodesDraggable, nodesConnectable, elementsSelectable, updateNodeDimensions } = useStore(selector, shallow);
|
const { nodesDraggable, nodesConnectable, nodesFocusable, elementsSelectable, updateNodeDimensions } = useStore(
|
||||||
|
selector,
|
||||||
|
shallow
|
||||||
|
);
|
||||||
const nodes = useVisibleNodes(props.onlyRenderVisibleElements);
|
const nodes = useVisibleNodes(props.onlyRenderVisibleElements);
|
||||||
const resizeObserverRef = useRef<ResizeObserver>();
|
const resizeObserverRef = useRef<ResizeObserver>();
|
||||||
|
|
||||||
@@ -82,6 +86,8 @@ const NodeRenderer = (props: NodeRendererProps) => {
|
|||||||
const isDraggable = !!(node.draggable || (nodesDraggable && typeof node.draggable === 'undefined'));
|
const isDraggable = !!(node.draggable || (nodesDraggable && typeof node.draggable === 'undefined'));
|
||||||
const isSelectable = !!(node.selectable || (elementsSelectable && typeof node.selectable === 'undefined'));
|
const isSelectable = !!(node.selectable || (elementsSelectable && typeof node.selectable === 'undefined'));
|
||||||
const isConnectable = !!(node.connectable || (nodesConnectable && typeof node.connectable === 'undefined'));
|
const isConnectable = !!(node.connectable || (nodesConnectable && typeof node.connectable === 'undefined'));
|
||||||
|
const isFocusable = !!(node.focusable || (nodesFocusable && typeof node.focusable === 'undefined'));
|
||||||
|
|
||||||
const clampedPosition = props.nodeExtent
|
const clampedPosition = props.nodeExtent
|
||||||
? clampPosition(node.positionAbsolute, props.nodeExtent)
|
? clampPosition(node.positionAbsolute, props.nodeExtent)
|
||||||
: node.positionAbsolute;
|
: node.positionAbsolute;
|
||||||
@@ -122,6 +128,7 @@ const NodeRenderer = (props: NodeRendererProps) => {
|
|||||||
isDraggable={isDraggable}
|
isDraggable={isDraggable}
|
||||||
isSelectable={isSelectable}
|
isSelectable={isSelectable}
|
||||||
isConnectable={isConnectable}
|
isConnectable={isConnectable}
|
||||||
|
isFocusable={isFocusable}
|
||||||
resizeObserver={resizeObserver}
|
resizeObserver={resizeObserver}
|
||||||
dragHandle={node.dragHandle}
|
dragHandle={node.dragHandle}
|
||||||
zIndex={node[internalsSymbol]?.z ?? 0}
|
zIndex={node[internalsSymbol]?.z ?? 0}
|
||||||
|
|||||||
@@ -108,7 +108,9 @@ const ReactFlow = forwardRef<ReactFlowRefType, ReactFlowProps>(
|
|||||||
selectNodesOnDrag = true,
|
selectNodesOnDrag = true,
|
||||||
nodesDraggable,
|
nodesDraggable,
|
||||||
nodesConnectable,
|
nodesConnectable,
|
||||||
|
nodesFocusable,
|
||||||
nodeOrigin = initNodeOrigin,
|
nodeOrigin = initNodeOrigin,
|
||||||
|
edgesFocusable,
|
||||||
elementsSelectable,
|
elementsSelectable,
|
||||||
defaultViewport = initDefaultViewport,
|
defaultViewport = initDefaultViewport,
|
||||||
minZoom = 0.5,
|
minZoom = 0.5,
|
||||||
@@ -245,6 +247,8 @@ const ReactFlow = forwardRef<ReactFlowRefType, ReactFlowProps>(
|
|||||||
onClickConnectEnd={onClickConnectEnd}
|
onClickConnectEnd={onClickConnectEnd}
|
||||||
nodesDraggable={nodesDraggable}
|
nodesDraggable={nodesDraggable}
|
||||||
nodesConnectable={nodesConnectable}
|
nodesConnectable={nodesConnectable}
|
||||||
|
nodesFocusable={nodesFocusable}
|
||||||
|
edgesFocusable={edgesFocusable}
|
||||||
elementsSelectable={elementsSelectable}
|
elementsSelectable={elementsSelectable}
|
||||||
minZoom={minZoom}
|
minZoom={minZoom}
|
||||||
maxZoom={maxZoom}
|
maxZoom={maxZoom}
|
||||||
|
|||||||
@@ -9,17 +9,19 @@ function useUpdateNodePositions() {
|
|||||||
const store = useStoreApi();
|
const store = useStoreApi();
|
||||||
|
|
||||||
const updatePositions = useCallback((positionDiff: XYPosition) => {
|
const updatePositions = useCallback((positionDiff: XYPosition) => {
|
||||||
const { nodeInternals, nodeExtent, updateNodePositions } = store.getState();
|
const { nodeInternals, nodeExtent, updateNodePositions, snapToGrid, snapGrid } = store.getState();
|
||||||
const selectedNodes = Array.from(nodeInternals.values()).filter((n) => n.selected);
|
const selectedNodes = Array.from(nodeInternals.values()).filter((n) => n.selected);
|
||||||
|
|
||||||
const nodeUpdates = selectedNodes.map((n) => {
|
const nodeUpdates = selectedNodes.map((n) => {
|
||||||
if (n.positionAbsolute) {
|
if (n.positionAbsolute) {
|
||||||
const updatedPos = calcNextPosition(
|
const nextPosition = { x: n.positionAbsolute.x + positionDiff.x, y: n.positionAbsolute.y + positionDiff.y };
|
||||||
n,
|
|
||||||
{ x: n.positionAbsolute.x + positionDiff.x, y: n.positionAbsolute.y + positionDiff.y },
|
if (snapToGrid) {
|
||||||
nodeInternals,
|
nextPosition.x = snapGrid[0] * Math.round(nextPosition.x / snapGrid[0]);
|
||||||
nodeExtent
|
nextPosition.y = snapGrid[1] * Math.round(nextPosition.y / snapGrid[1]);
|
||||||
);
|
}
|
||||||
|
|
||||||
|
const updatedPos = calcNextPosition(n, nextPosition, nodeInternals, nodeExtent);
|
||||||
|
|
||||||
n.position = updatedPos.position;
|
n.position = updatedPos.position;
|
||||||
n.positionAbsolute = updatedPos.positionAbsolute;
|
n.positionAbsolute = updatedPos.positionAbsolute;
|
||||||
|
|||||||
@@ -40,6 +40,8 @@ const initialState: ReactFlowStore = {
|
|||||||
|
|
||||||
nodesDraggable: true,
|
nodesDraggable: true,
|
||||||
nodesConnectable: true,
|
nodesConnectable: true,
|
||||||
|
nodesFocusable: true,
|
||||||
|
edgesFocusable: true,
|
||||||
elementsSelectable: true,
|
elementsSelectable: true,
|
||||||
fitViewOnInit: false,
|
fitViewOnInit: false,
|
||||||
fitViewOnInitDone: false,
|
fitViewOnInitDone: false,
|
||||||
|
|||||||
@@ -92,7 +92,9 @@ export interface ReactFlowProps extends HTMLAttributes<HTMLDivElement> {
|
|||||||
onlyRenderVisibleElements?: boolean;
|
onlyRenderVisibleElements?: boolean;
|
||||||
nodesDraggable?: boolean;
|
nodesDraggable?: boolean;
|
||||||
nodesConnectable?: boolean;
|
nodesConnectable?: boolean;
|
||||||
|
nodesFocusable?: boolean;
|
||||||
nodeOrigin?: NodeOrigin;
|
nodeOrigin?: NodeOrigin;
|
||||||
|
edgesFocusable?: boolean;
|
||||||
initNodeOrigin?: NodeOrigin;
|
initNodeOrigin?: NodeOrigin;
|
||||||
elementsSelectable?: boolean;
|
elementsSelectable?: boolean;
|
||||||
selectNodesOnDrag?: boolean;
|
selectNodesOnDrag?: boolean;
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ type DefaultEdge<T = any> = {
|
|||||||
zIndex?: number;
|
zIndex?: number;
|
||||||
ariaLabel?: string;
|
ariaLabel?: string;
|
||||||
interactionWidth?: number;
|
interactionWidth?: number;
|
||||||
|
focusable?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type SmoothStepPathOptions = {
|
export type SmoothStepPathOptions = {
|
||||||
@@ -132,7 +133,7 @@ export type WrapEdgeProps<T = any> = Omit<Edge<T>, 'sourceHandle' | 'targetHandl
|
|||||||
onEdgeUpdateStart?: (event: React.MouseEvent, edge: Edge, handleType: HandleType) => void;
|
onEdgeUpdateStart?: (event: React.MouseEvent, edge: Edge, handleType: HandleType) => void;
|
||||||
onEdgeUpdateEnd?: (event: MouseEvent, edge: Edge, handleType: HandleType) => void;
|
onEdgeUpdateEnd?: (event: MouseEvent, edge: Edge, handleType: HandleType) => void;
|
||||||
rfId?: string;
|
rfId?: string;
|
||||||
disableKeyboardA11y: boolean;
|
isFocusable: boolean;
|
||||||
pathOptions?: BezierPathOptions | SmoothStepPathOptions;
|
pathOptions?: BezierPathOptions | SmoothStepPathOptions;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -167,6 +167,8 @@ export type ReactFlowStore = {
|
|||||||
|
|
||||||
nodesDraggable: boolean;
|
nodesDraggable: boolean;
|
||||||
nodesConnectable: boolean;
|
nodesConnectable: boolean;
|
||||||
|
nodesFocusable: boolean;
|
||||||
|
edgesFocusable: boolean;
|
||||||
elementsSelectable: boolean;
|
elementsSelectable: boolean;
|
||||||
|
|
||||||
multiSelectionActive: boolean;
|
multiSelectionActive: boolean;
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ export interface Node<T = any> {
|
|||||||
expandParent?: boolean;
|
expandParent?: boolean;
|
||||||
positionAbsolute?: XYPosition;
|
positionAbsolute?: XYPosition;
|
||||||
ariaLabel?: string;
|
ariaLabel?: string;
|
||||||
|
focusable?: boolean;
|
||||||
|
|
||||||
// only used internally
|
// only used internally
|
||||||
[internalsSymbol]?: {
|
[internalsSymbol]?: {
|
||||||
@@ -73,6 +74,7 @@ export interface WrapNodeProps<T = any> {
|
|||||||
initialized: boolean;
|
initialized: boolean;
|
||||||
isSelectable: boolean;
|
isSelectable: boolean;
|
||||||
isDraggable: boolean;
|
isDraggable: boolean;
|
||||||
|
isFocusable: boolean;
|
||||||
selectNodesOnDrag: boolean;
|
selectNodesOnDrag: boolean;
|
||||||
onClick?: NodeMouseHandler;
|
onClick?: NodeMouseHandler;
|
||||||
onDoubleClick?: NodeMouseHandler;
|
onDoubleClick?: NodeMouseHandler;
|
||||||
|
|||||||
Reference in New Issue
Block a user