From 8fa70107a008de4bd9ff62763e5a242ce2a60277 Mon Sep 17 00:00:00 2001 From: Braks <78412429+bcakmakoglu@users.noreply.github.com> Date: Tue, 10 Jun 2025 12:22:24 +0200 Subject: [PATCH] feat(core): add dom attributes option for nodes and edges (#1869) * feat(core): add dom attributes option for nodes and edges (#1861) Signed-off-by: braks <78412429+bcakmakoglu@users.noreply.github.com> * chore(changeset): add * chore: cleanup Signed-off-by: braks <78412429+bcakmakoglu@users.noreply.github.com> --------- Signed-off-by: braks <78412429+bcakmakoglu@users.noreply.github.com> Co-authored-by: Kirill Zaytsev --- .changeset/tender-jobs-search.md | 5 +++++ .../core/src/components/Edges/EdgeWrapper.ts | 16 ++++++++------ .../core/src/components/Nodes/NodeWrapper.ts | 7 +++--- packages/core/src/store/state.ts | 2 -- packages/core/src/types/edge.ts | 20 ++++++++++++++++- packages/core/src/types/flow.ts | 2 -- packages/core/src/types/node.ts | 22 +++++++++++++++++-- 7 files changed, 56 insertions(+), 18 deletions(-) create mode 100644 .changeset/tender-jobs-search.md diff --git a/.changeset/tender-jobs-search.md b/.changeset/tender-jobs-search.md new file mode 100644 index 00000000..d48cefca --- /dev/null +++ b/.changeset/tender-jobs-search.md @@ -0,0 +1,5 @@ +--- +"@vue-flow/core": minor +--- + +Add `domAttributes` prop to nodes / edges that allows passing any sort of dom attributes to the node/edge wrappers (like aria attributes etc.). diff --git a/packages/core/src/components/Edges/EdgeWrapper.ts b/packages/core/src/components/Edges/EdgeWrapper.ts index 10d466ba..b5825ead 100644 --- a/packages/core/src/components/Edges/EdgeWrapper.ts +++ b/packages/core/src/components/Edges/EdgeWrapper.ts @@ -192,6 +192,15 @@ const EdgeWrapper = defineComponent({ inactive: !isSelectable.value && !hooks.value.edgeClick.hasListeners(), }, ], + 'tabIndex': isFocusable.value ? 0 : undefined, + 'aria-label': + edge.value.ariaLabel === null + ? undefined + : edge.value.ariaLabel ?? `Edge from ${edge.value.source} to ${edge.value.target}`, + 'aria-describedby': isFocusable.value ? `${ARIA_EDGE_DESC_KEY}-${vueFlowId}` : undefined, + 'aria-roledescription': 'edge', + 'role': isFocusable.value ? 'group' : 'img', + ...edge.value.domAttributes, 'onClick': onEdgeClick, 'onContextmenu': onEdgeContextMenu, 'onDblclick': onDoubleClick, @@ -199,13 +208,6 @@ const EdgeWrapper = defineComponent({ 'onMousemove': onEdgeMouseMove, 'onMouseleave': onEdgeMouseLeave, 'onKeyDown': isFocusable.value ? onKeyDown : undefined, - 'tabIndex': isFocusable.value ? 0 : undefined, - 'aria-label': - edge.value.ariaLabel === null - ? undefined - : edge.value.ariaLabel || `Edge from ${edge.value.source} to ${edge.value.target}`, - 'aria-describedby': isFocusable.value ? `${ARIA_EDGE_DESC_KEY}-${vueFlowId}` : undefined, - 'role': isFocusable.value ? 'button' : 'img', }, [ updating.value diff --git a/packages/core/src/components/Nodes/NodeWrapper.ts b/packages/core/src/components/Nodes/NodeWrapper.ts index daef1096..afcd8d8e 100644 --- a/packages/core/src/components/Nodes/NodeWrapper.ts +++ b/packages/core/src/components/Nodes/NodeWrapper.ts @@ -61,7 +61,6 @@ const NodeWrapper = defineComponent({ elementsSelectable, nodesConnectable, nodesFocusable, - nodesAttrs, hooks, } = useVueFlow() @@ -284,11 +283,11 @@ const NodeWrapper = defineComponent({ ...getStyle.value, }, 'tabIndex': isFocusable.value ? 0 : undefined, - 'role': isFocusable.value ? 'button' : undefined, + 'role': isFocusable.value ? 'group' : undefined, 'aria-describedby': disableKeyboardA11y.value ? undefined : `${ARIA_NODE_DESC_KEY}-${vueFlowId}`, 'aria-label': node.ariaLabel, - ...nodesAttrs?.value, - ...node.attrs, + 'aria-roledescription': 'node', + ...node.domAttributes, 'onMouseenter': onMouseEnter, 'onMousemove': onMouseMove, 'onMouseleave': onMouseLeave, diff --git a/packages/core/src/store/state.ts b/packages/core/src/store/state.ts index 410ab25a..2198b946 100644 --- a/packages/core/src/store/state.ts +++ b/packages/core/src/store/state.ts @@ -114,8 +114,6 @@ export function useState(): State { disableKeyboardA11y: false, ariaLiveMessage: '', - - nodesAttrs: {}, } } diff --git a/packages/core/src/types/edge.ts b/packages/core/src/types/edge.ts index da775fd2..1842f666 100644 --- a/packages/core/src/types/edge.ts +++ b/packages/core/src/types/edge.ts @@ -1,4 +1,4 @@ -import type { CSSProperties, Component, VNode } from 'vue' +import type { CSSProperties, Component, SVGAttributes, VNode } from 'vue' import type { ClassFunc, ElementData, Position, StyleFunc, Styles } from './flow' import type { GraphNode } from './node' import type { EdgeComponent, EdgeTextProps } from './components' @@ -108,6 +108,24 @@ export interface DefaultEdge< /** Aria label for edge (a11y) */ zIndex?: number ariaLabel?: string | null + /** + * General escape hatch for adding custom attributes to the edge's DOM element. + */ + domAttributes?: Omit< + SVGAttributes, + | 'id' + | 'style' + | 'className' + | 'role' + | 'aria-label' + | 'onClick' + | 'onMouseenter' + | 'onMousemove' + | 'onMouseleave' + | 'onContextmenu' + | 'onDblclick' + | 'onKeyDown' + > } export interface SmoothStepPathOptions { diff --git a/packages/core/src/types/flow.ts b/packages/core/src/types/flow.ts index 5c6a0661..4ea8134a 100644 --- a/packages/core/src/types/flow.ts +++ b/packages/core/src/types/flow.ts @@ -229,8 +229,6 @@ export interface FlowProps { autoPanOnConnect?: boolean autoPanOnNodeDrag?: boolean autoPanSpeed?: number - - nodesAttrs?: Record } /** diff --git a/packages/core/src/types/node.ts b/packages/core/src/types/node.ts index 56ee9d66..422adede 100644 --- a/packages/core/src/types/node.ts +++ b/packages/core/src/types/node.ts @@ -1,4 +1,4 @@ -import type { Component, VNode } from 'vue' +import type { Component, HTMLAttributes, VNode } from 'vue' import type { ClassFunc, Dimensions, ElementData, Position, StyleFunc, Styles, XYPosition, XYZPosition } from './flow' import type { NodeComponent } from './components' import type { HandleConnectable, HandleElement, ValidConnectionFunc } from './handle' @@ -109,7 +109,25 @@ export interface Node> zIndex?: number ariaLabel?: string - attrs?: Record + + /** + * General escape hatch for adding custom attributes to the node's DOM element. + */ + domAttributes?: Omit< + HTMLAttributes, + | 'id' + | 'style' + | 'className' + | 'draggable' + | 'aria-label' + | 'onMouseenter' + | 'onMousemove' + | 'onMouseleave' + | 'onContextmenu' + | 'onClick' + | 'onDblclick' + | 'onKeydown' + > } export interface GraphNode<