chore(labelconfig): cleanup

This commit is contained in:
moklick
2025-06-03 13:36:16 +02:00
parent 220cb7f8a3
commit 6543ddc31c
11 changed files with 59 additions and 65 deletions
+16 -13
View File
@@ -9,6 +9,7 @@ import {
Node, Node,
Edge, Edge,
OnNodeDrag, OnNodeDrag,
LabelConfig,
} from '@xyflow/react'; } from '@xyflow/react';
const onNodeDrag: OnNodeDrag = (_, node: Node, nodes: Node[]) => console.log('drag', node, nodes); const onNodeDrag: OnNodeDrag = (_, node: Node, nodes: Node[]) => console.log('drag', node, nodes);
@@ -43,6 +44,20 @@ const initialEdges: Edge[] = [
{ id: 'e1-3', source: '1', target: '3' }, { id: 'e1-3', source: '1', target: '3' },
]; ];
const labelConfig: Partial<LabelConfig> = {
'a11yDescription.node.default': 'Custom Node Desc.',
'a11yDescription.node.keyboardDisabled': 'Custom Keyboard Desc.',
'a11yDescription.edge.default': 'Custom Edge Desc.',
'a11yDescription.ariaLiveMessage': ({ direction, x, y }) =>
`Custom Moved selected node ${direction}. New position, x: ${x}, y: ${y}`,
'controls.ariaLabel': 'Custom Controls Aria Label',
'controls.zoomin.title': 'Custom Zoom in',
'controls.zoomout.title': 'Custom Zoom Out',
// 'controls.fitview.title': 'Custom Fit View',
'controls.interactive.title': 'Custom Toggle Interactivity',
'minimap.ariaLabel': 'Custom Aria Label',
};
const A11y = () => { const A11y = () => {
return ( return (
<ReactFlow <ReactFlow
@@ -61,19 +76,7 @@ const A11y = () => {
elevateEdgesOnSelect elevateEdgesOnSelect
elevateNodesOnSelect={false} elevateNodesOnSelect={false}
nodeDragThreshold={0} nodeDragThreshold={0}
labelConfig={{ labelConfig={labelConfig}
'a11yDescription.node.default': 'Custom Node Desc.',
'a11yDescription.node.keyboardDisabled': 'Custom Keyboard Desc.',
'a11yDescription.edge.default': 'Custom Edge Desc.',
'a11yDescription.ariaLiveMessage': (node: string, x: number, y: number) =>
`Custom Moved selected node ${node}. New position, x: ${x}, y: ${y}`,
'controls.ariaLabel': 'Custom Controls Aria Label',
'controls.zoomin.title': 'Custom Zoom in',
'controls.zoomout.title': 'Custom Zoom Out',
// 'controls.fitview.title': 'Custom Fit View',
'controls.interactive.title': 'Custom Toggle Interactivity',
'minimap.ariaLabel': 'Custom Aria Label',
}}
> >
<Background variant={BackgroundVariant.Dots} /> <Background variant={BackgroundVariant.Dots} />
<MiniMap /> <MiniMap />
@@ -1,24 +1,17 @@
<script lang="ts"> <script lang="ts">
import { import { SvelteFlow, Controls, Background, MiniMap } from '@xyflow/svelte';
SvelteFlow,
Controls,
Background,
MiniMap,
} from '@xyflow/svelte';
import '@xyflow/svelte/dist/style.css'; import '@xyflow/svelte/dist/style.css';
let nodes = $state.raw([ let nodes = $state.raw([
{ {
id: 'A', id: 'A',
position: { x: 0, y: 0 }, position: { x: 0, y: 0 },
data: { label: 'A' }, data: { label: 'A' }
}, },
{ id: 'B', position: { x: -100, y: 150 }, data: { label: 'B' } }, { id: 'B', position: { x: -100, y: 150 }, data: { label: 'B' } },
{ id: 'C', position: { x: 100, y: 150 }, data: { label: 'C' } }, { id: 'C', position: { x: 100, y: 150 }, data: { label: 'C' } },
{ id: 'D', position: { x: 0, y: 260 }, data: { label: 'D' } } { id: 'D', position: { x: 0, y: 260 }, data: { label: 'D' } }
]); ]);
let edges = $state.raw([ let edges = $state.raw([
@@ -26,7 +19,6 @@
{ id: 'A-C', source: 'A', target: 'C' }, { id: 'A-C', source: 'A', target: 'C' },
{ id: 'A-D', source: 'A', target: 'D' } { id: 'A-D', source: 'A', target: 'D' }
]); ]);
</script> </script>
<SvelteFlow <SvelteFlow
@@ -34,17 +26,17 @@
bind:edges bind:edges
fitView fitView
labelConfig={{ labelConfig={{
'a11yDescription.node.default': 'Svelte Custom Node Desc.', 'a11yDescription.node.default': 'Svelte Custom Node Desc.',
'a11yDescription.node.keyboardDisabled': 'Svelte Custom Keyboard Desc.', 'a11yDescription.node.keyboardDisabled': 'Svelte Custom Keyboard Desc.',
'a11yDescription.edge.default': 'Svelte Custom Edge Desc.', 'a11yDescription.edge.default': 'Svelte Custom Edge Desc.',
'a11yDescription.ariaLiveMessage': (node: string, x: number, y: number) => 'a11yDescription.ariaLiveMessage': ({ direction, x, y }) =>
`Custom Moved selected node ${node}. New position, x: ${x}, y: ${y}`, `Custom Moved selected node ${direction}. New position, x: ${x}, y: ${y}`,
'controls.ariaLabel': 'Svelte Custom Control Aria Label', 'controls.ariaLabel': 'Svelte Custom Control Aria Label',
'controls.zoomin.title': 'Svelte Custom Zoom in', 'controls.zoomin.title': 'Svelte Custom Zoom in',
'controls.zoomout.title': 'Svelte Custom Zoom Out', 'controls.zoomout.title': 'Svelte Custom Zoom Out',
// 'controls.fitview.title': 'Svelte Custom Fit View', // 'controls.fitview.title': 'Svelte Custom Fit View',
'controls.interactive.title': 'Svelte Custom Toggle Interactivity', 'controls.interactive.title': 'Svelte Custom Toggle Interactivity',
'minimap.ariaLabel': 'Svelte Custom Minimap', 'minimap.ariaLabel': 'Svelte Custom Minimap'
}} }}
> >
<Controls /> <Controls />
@@ -41,7 +41,6 @@ function ControlsComponent({
const store = useStoreApi(); const store = useStoreApi();
const { isInteractive, minZoomReached, maxZoomReached, labelConfig } = useStore(selector, shallow); const { isInteractive, minZoomReached, maxZoomReached, labelConfig } = useStore(selector, shallow);
const { zoomIn, zoomOut, fitView } = useReactFlow(); const { zoomIn, zoomOut, fitView } = useReactFlow();
const effectiveAriaLabel = ariaLabel ?? labelConfig['controls.ariaLabel'];
const onZoomInHandler = () => { const onZoomInHandler = () => {
zoomIn(); zoomIn();
@@ -75,7 +74,7 @@ function ControlsComponent({
position={position} position={position}
style={style} style={style}
data-testid="rf__controls" data-testid="rf__controls"
aria-label={effectiveAriaLabel} aria-label={ariaLabel ?? labelConfig['controls.ariaLabel']}
> >
{showZoom && ( {showZoom && (
<> <>
@@ -133,7 +133,7 @@ function MiniMapComponent<NodeType extends Node = Node>({
}, []) }, [])
: undefined; : undefined;
const effectiveAriaLabel = ariaLabel ?? labelConfig['minimap.ariaLabel']; const _ariaLabel = ariaLabel ?? labelConfig['minimap.ariaLabel'];
return ( return (
<Panel <Panel
@@ -164,7 +164,7 @@ function MiniMapComponent<NodeType extends Node = Node>({
ref={svg} ref={svg}
onClick={onSvgClick} onClick={onSvgClick}
> >
{effectiveAriaLabel && <title id={labelledBy}>{effectiveAriaLabel}</title>} {_ariaLabel && <title id={labelledBy}>{_ariaLabel}</title>}
<MiniMapNodes<NodeType> <MiniMapNodes<NodeType>
onClick={onSvgNodeClick} onClick={onSvgNodeClick}
@@ -144,11 +144,11 @@ export function NodeWrapper<NodeType extends Node>({
event.preventDefault(); event.preventDefault();
store.setState({ store.setState({
ariaLiveMessage: labelConfig['a11yDescription.ariaLiveMessage']( ariaLiveMessage: labelConfig['a11yDescription.ariaLiveMessage']({
event.key.replace('Arrow', '').toLowerCase(), direction: event.key.replace('Arrow', '').toLowerCase(),
~~internals.positionAbsolute.x, x: ~~internals.positionAbsolute.x,
~~internals.positionAbsolute.y y: ~~internals.positionAbsolute.y,
), }),
}); });
moveSelectedNodes({ moveSelectedNodes({
+1
View File
@@ -108,6 +108,7 @@ export {
type NoConnection, type NoConnection,
type NodeConnection, type NodeConnection,
type OnReconnect, type OnReconnect,
type LabelConfig,
} from '@xyflow/system'; } from '@xyflow/system';
// we need this workaround to prevent a duplicate identifier error // we need this workaround to prevent a duplicate identifier error
@@ -1,5 +1,4 @@
<script lang="ts" generics="NodeType extends Node = Node, EdgeType extends Edge = Edge"> <script lang="ts" generics="NodeType extends Node = Node, EdgeType extends Edge = Edge">
import { derived } from 'svelte/store';
import type { SvelteFlowStore } from '$lib/store/types'; import type { SvelteFlowStore } from '$lib/store/types';
import type { Node, Edge } from '$lib/types'; import type { Node, Edge } from '$lib/types';
import { ARIA_EDGE_DESC_KEY, ARIA_LIVE_MESSAGE, ARIA_NODE_DESC_KEY } from '.'; import { ARIA_EDGE_DESC_KEY, ARIA_LIVE_MESSAGE, ARIA_NODE_DESC_KEY } from '.';
@@ -8,12 +7,12 @@
</script> </script>
<div id={`${ARIA_NODE_DESC_KEY}-${store.flowId}`} class="a11y-hidden"> <div id={`${ARIA_NODE_DESC_KEY}-${store.flowId}`} class="a11y-hidden">
{store.disableKeyboardA11y {store.disableKeyboardA11y
? store.labelConfig['a11yDescription.node.default'] ? store.labelConfig['a11yDescription.node.default']
: store.labelConfig['a11yDescription.node.keyboardDisabled']} : store.labelConfig['a11yDescription.node.keyboardDisabled']}
</div> </div>
<div id={`${ARIA_EDGE_DESC_KEY}-${store.flowId}`} class="a11y-hidden"> <div id={`${ARIA_EDGE_DESC_KEY}-${store.flowId}`} class="a11y-hidden">
{store.labelConfig['a11yDescription.edge.default']} {store.labelConfig['a11yDescription.edge.default']}
</div> </div>
{#if !store.disableKeyboardA11y} {#if !store.disableKeyboardA11y}
@@ -189,12 +189,11 @@
) { ) {
// prevent default scrolling behavior on arrow key press when node is moved // prevent default scrolling behavior on arrow key press when node is moved
event.preventDefault(); event.preventDefault();
store.ariaLiveMessage = labelConfig['a11yDescription.ariaLiveMessage']( store.ariaLiveMessage = labelConfig['a11yDescription.ariaLiveMessage']({
event.key.replace('Arrow', '').toLowerCase(), direction: event.key.replace('Arrow', '').toLowerCase(),
~~node.internals.positionAbsolute.x, x: ~~node.internals.positionAbsolute.x,
~~node.internals.positionAbsolute.y y: ~~node.internals.positionAbsolute.y
), });
store.moveSelectedNodes(arrowKeyDiffs[event.key], event.shiftKey ? 4 : 1); store.moveSelectedNodes(arrowKeyDiffs[event.key], event.shiftKey ? 4 : 1);
} }
} }
@@ -476,5 +476,5 @@ export type SvelteFlowProps<
* Configuration for customizable labels, descriptions, and UI text. Provided keys will override the corresponding defaults. * Configuration for customizable labels, descriptions, and UI text. Provided keys will override the corresponding defaults.
* Allows localization, customization of ARIA descriptions, control labels, minimap labels, and other UI strings. * Allows localization, customization of ARIA descriptions, control labels, minimap labels, and other UI strings.
*/ */
labelConfig?: LabelConfig; labelConfig?: Partial<LabelConfig>;
}; };
+2 -1
View File
@@ -112,7 +112,8 @@ export {
type ResizeParamsWithDirection, type ResizeParamsWithDirection,
type ResizeDragEvent, type ResizeDragEvent,
type IsValidConnection, type IsValidConnection,
type NodeConnection type NodeConnection,
type LabelConfig
} from '@xyflow/system'; } from '@xyflow/system';
// system utils // system utils
+3 -3
View File
@@ -44,8 +44,8 @@ export const defaultLabelConfig = {
'Press enter or space to select a node. You can then use the arrow keys to move the node around. Press delete to remove it and escape to cancel.', 'Press enter or space to select a node. You can then use the arrow keys to move the node around. Press delete to remove it and escape to cancel.',
'a11yDescription.edge.default': 'a11yDescription.edge.default':
'Press enter or space to select an edge. You can then press delete to remove it or escape to cancel.', 'Press enter or space to select an edge. You can then press delete to remove it or escape to cancel.',
'a11yDescription.ariaLiveMessage': (node: string, x: number, y: number) => 'a11yDescription.ariaLiveMessage': ({ direction, x, y }: { direction: string; x: number; y: number }) =>
`Moved selected node ${node}. New position, x: ${x}, y: ${y}`, `Moved selected node ${direction}. New position, x: ${x}, y: ${y}`,
// Control elements // Control elements
'controls.ariaLabel': 'Control Panel', 'controls.ariaLabel': 'Control Panel',
@@ -61,4 +61,4 @@ export const defaultLabelConfig = {
'handle.ariaLabel': 'Handle', 'handle.ariaLabel': 'Handle',
}; };
export type LabelConfig = Required<typeof defaultLabelConfig>; export type LabelConfig = typeof defaultLabelConfig;