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 <zaytsev.cmath10@gmail.com>
This commit is contained in:
Braks
2025-06-10 16:24:11 +02:00
co-authored by Kirill Zaytsev
parent 4c1b064230
commit 8fa70107a0
7 changed files with 56 additions and 18 deletions
+5
View File
@@ -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.).
@@ -192,6 +192,15 @@ const EdgeWrapper = defineComponent({
inactive: !isSelectable.value && !hooks.value.edgeClick.hasListeners(), 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, 'onClick': onEdgeClick,
'onContextmenu': onEdgeContextMenu, 'onContextmenu': onEdgeContextMenu,
'onDblclick': onDoubleClick, 'onDblclick': onDoubleClick,
@@ -199,13 +208,6 @@ const EdgeWrapper = defineComponent({
'onMousemove': onEdgeMouseMove, 'onMousemove': onEdgeMouseMove,
'onMouseleave': onEdgeMouseLeave, 'onMouseleave': onEdgeMouseLeave,
'onKeyDown': isFocusable.value ? onKeyDown : undefined, '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 updating.value
@@ -61,7 +61,6 @@ const NodeWrapper = defineComponent({
elementsSelectable, elementsSelectable,
nodesConnectable, nodesConnectable,
nodesFocusable, nodesFocusable,
nodesAttrs,
hooks, hooks,
} = useVueFlow() } = useVueFlow()
@@ -284,11 +283,11 @@ const NodeWrapper = defineComponent({
...getStyle.value, ...getStyle.value,
}, },
'tabIndex': isFocusable.value ? 0 : undefined, '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-describedby': disableKeyboardA11y.value ? undefined : `${ARIA_NODE_DESC_KEY}-${vueFlowId}`,
'aria-label': node.ariaLabel, 'aria-label': node.ariaLabel,
...nodesAttrs?.value, 'aria-roledescription': 'node',
...node.attrs, ...node.domAttributes,
'onMouseenter': onMouseEnter, 'onMouseenter': onMouseEnter,
'onMousemove': onMouseMove, 'onMousemove': onMouseMove,
'onMouseleave': onMouseLeave, 'onMouseleave': onMouseLeave,
-2
View File
@@ -114,8 +114,6 @@ export function useState(): State {
disableKeyboardA11y: false, disableKeyboardA11y: false,
ariaLiveMessage: '', ariaLiveMessage: '',
nodesAttrs: {},
} }
} }
+19 -1
View File
@@ -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 { ClassFunc, ElementData, Position, StyleFunc, Styles } from './flow'
import type { GraphNode } from './node' import type { GraphNode } from './node'
import type { EdgeComponent, EdgeTextProps } from './components' import type { EdgeComponent, EdgeTextProps } from './components'
@@ -108,6 +108,24 @@ export interface DefaultEdge<
/** Aria label for edge (a11y) */ /** Aria label for edge (a11y) */
zIndex?: number zIndex?: number
ariaLabel?: string | null 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 { export interface SmoothStepPathOptions {
-2
View File
@@ -229,8 +229,6 @@ export interface FlowProps {
autoPanOnConnect?: boolean autoPanOnConnect?: boolean
autoPanOnNodeDrag?: boolean autoPanOnNodeDrag?: boolean
autoPanSpeed?: number autoPanSpeed?: number
nodesAttrs?: Record<string, any>
} }
/** /**
+20 -2
View File
@@ -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 { ClassFunc, Dimensions, ElementData, Position, StyleFunc, Styles, XYPosition, XYZPosition } from './flow'
import type { NodeComponent } from './components' import type { NodeComponent } from './components'
import type { HandleConnectable, HandleElement, ValidConnectionFunc } from './handle' import type { HandleConnectable, HandleElement, ValidConnectionFunc } from './handle'
@@ -109,7 +109,25 @@ export interface Node<Data = ElementData, CustomEvents extends Record<string, Cu
events?: Partial<NodeEventsHandler<CustomEvents>> events?: Partial<NodeEventsHandler<CustomEvents>>
zIndex?: number zIndex?: number
ariaLabel?: string ariaLabel?: string
attrs?: Record<string, any>
/**
* 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< export interface GraphNode<