diff --git a/packages/core/src/components/A11yDescriptions/index.tsx b/packages/core/src/components/A11yDescriptions/index.tsx index 5f6f420d..5b019009 100644 --- a/packages/core/src/components/A11yDescriptions/index.tsx +++ b/packages/core/src/components/A11yDescriptions/index.tsx @@ -1,12 +1,28 @@ -const style = { display: 'none' }; +import { CSSProperties } from 'react'; +import { useStore } from '../../hooks/useStore'; +import { ReactFlowState } from '../../types'; + +const style: CSSProperties = { display: 'none' }; +const ariaLiveStyle: CSSProperties = { + position: 'absolute', + width: 1, + height: 1, + margin: -1, + border: 0, + padding: 0, + overflow: 'hidden', + clip: 'rect(0px, 0px, 0px, 0px)', + clipPath: 'inset(100%)', +}; export const ARIA_NODE_DESC_KEY = 'react-flow__node-desc'; export const ARIA_EDGE_DESC_KEY = 'react-flow__edge-desc'; +export const ARIA_LIVE_MESSAGE = 'react-flow__arai-live'; -function A11yDescriptions({ rfId, disableKeyboardA11y }: { rfId: string; disableKeyboardA11y: boolean }) { - if (disableKeyboardA11y) { - return null; - } +const selector = (s: ReactFlowState) => s.ariaLiveMessage; + +function A11yDescriptions({ rfId }: { rfId: string }) { + const ariaLiveMessage = useStore(selector); return ( <> @@ -17,6 +33,9 @@ function A11yDescriptions({ rfId, disableKeyboardA11y }: { rfId: string; disable
Press enter or space to select an edge. You can then press delete to remove it or press escape to cancel.
+
+ {ariaLiveMessage} +
); } diff --git a/packages/core/src/components/Nodes/wrapNode.tsx b/packages/core/src/components/Nodes/wrapNode.tsx index 108a3ceb..bdca639b 100644 --- a/packages/core/src/components/Nodes/wrapNode.tsx +++ b/packages/core/src/components/Nodes/wrapNode.tsx @@ -91,6 +91,12 @@ export default (NodeComponent: ComponentType) => { unselect, }); } else if (selected && Object.prototype.hasOwnProperty.call(arrowKeyDiffs, event.key)) { + store.setState({ + ariaLiveMessage: `Move selected node ten pixels to the ${event.key + .replace('Arrow', '') + .toLowerCase()}. New position, x: ${xPos}, y: ${yPos}`, + }); + updatePositions(arrowKeyDiffs[event.key]); } }; diff --git a/packages/core/src/container/ReactFlow/index.tsx b/packages/core/src/container/ReactFlow/index.tsx index 4af5484f..224debc3 100644 --- a/packages/core/src/container/ReactFlow/index.tsx +++ b/packages/core/src/container/ReactFlow/index.tsx @@ -258,7 +258,7 @@ const ReactFlow = forwardRef( {onSelectionChange && } {children} - + {!disableKeyboardA11y && } ); diff --git a/packages/core/src/hooks/useGlobalKeyHandler.ts b/packages/core/src/hooks/useGlobalKeyHandler.ts index 01474a5d..151c9786 100644 --- a/packages/core/src/hooks/useGlobalKeyHandler.ts +++ b/packages/core/src/hooks/useGlobalKeyHandler.ts @@ -12,7 +12,6 @@ interface HookParams { export default ({ deleteKeyCode, multiSelectionKeyCode }: HookParams): void => { const store = useStoreApi(); - const deleteKeyPressed = useKeyPress(deleteKeyCode); const multiSelectionKeyPressed = useKeyPress(multiSelectionKeyCode); diff --git a/packages/core/src/hooks/useOnInitHandler.ts b/packages/core/src/hooks/useOnInitHandler.ts index 551ed107..8a3a119c 100644 --- a/packages/core/src/hooks/useOnInitHandler.ts +++ b/packages/core/src/hooks/useOnInitHandler.ts @@ -4,15 +4,15 @@ import useReactFlow from './useReactFlow'; import { OnInit } from '../types'; function useOnInitHandler(onInit: OnInit | undefined) { - const ReactFlowInstance = useReactFlow(); + const rfInstance = useReactFlow(); const isInitialized = useRef(false); useEffect(() => { - if (!isInitialized.current && ReactFlowInstance.viewportInitialized && onInit) { - setTimeout(() => onInit(ReactFlowInstance), 1); + if (!isInitialized.current && rfInstance.viewportInitialized && onInit) { + setTimeout(() => onInit(rfInstance), 1); isInitialized.current = true; } - }, [onInit, ReactFlowInstance.viewportInitialized]); + }, [onInit, rfInstance.viewportInitialized]); } export default useOnInitHandler; diff --git a/packages/core/src/hooks/useUpdateNodeInternals.ts b/packages/core/src/hooks/useUpdateNodeInternals.ts index bca6b416..dbd1a84d 100644 --- a/packages/core/src/hooks/useUpdateNodeInternals.ts +++ b/packages/core/src/hooks/useUpdateNodeInternals.ts @@ -1,16 +1,14 @@ import { useCallback } from 'react'; -import { useStore, useStoreApi } from '../hooks/useStore'; -import { UpdateNodeInternals, ReactFlowState } from '../types'; - -const selector = (state: ReactFlowState) => state.updateNodeDimensions; +import { useStoreApi } from '../hooks/useStore'; +import { UpdateNodeInternals } from '../types'; function useUpdateNodeInternals(): UpdateNodeInternals { const store = useStoreApi(); - const updateNodeDimensions = useStore(selector); return useCallback((id: string) => { - const nodeElement = store.getState().domNode?.querySelector(`.react-flow__node[data-id="${id}"]`) as HTMLDivElement; + const { domNode, updateNodeDimensions } = store.getState(); + const nodeElement = domNode?.querySelector(`.react-flow__node[data-id="${id}"]`) as HTMLDivElement; if (nodeElement) { requestAnimationFrame(() => updateNodeDimensions([{ id, nodeElement, forceUpdate: true }])); diff --git a/packages/core/src/store/initialState.ts b/packages/core/src/store/initialState.ts index a40093de..50e52f26 100644 --- a/packages/core/src/store/initialState.ts +++ b/packages/core/src/store/initialState.ts @@ -48,6 +48,8 @@ const initialState: ReactFlowStore = { connectionStartHandle: null, connectOnClick: true, + + ariaLiveMessage: '', }; export default initialState; diff --git a/packages/core/src/types/general.ts b/packages/core/src/types/general.ts index 44097968..b84e71c4 100644 --- a/packages/core/src/types/general.ts +++ b/packages/core/src/types/general.ts @@ -202,6 +202,8 @@ export type ReactFlowStore = { onViewportChangeEnd?: OnViewportChange; onSelectionChange?: OnSelectionChangeFunc; + + ariaLiveMessage: string; }; export type ReactFlowActions = {