Skip to content

Commit a7c18b1

Browse files
committed
Improvement - VueUiFlow - Improve keyboard accessibility; add node borderRadius config option
1 parent 4d61e18 commit a7c18b1

File tree

4 files changed

+75
-21
lines changed

4 files changed

+75
-21
lines changed

TestingArena/ArenaVueUiFlow.vue

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,8 @@ const model = ref([
191191
{ key: 'style.chart.nodes.labels.abbreviation.length', def: 3, type: 'number', min: 1, max: 12 },
192192
{ key: 'style.chart.nodes.stroke', def: '#FFFFFF', type: 'color' },
193193
{ key: 'style.chart.nodes.strokeWidth', def: 1, type: 'number', min: 0, max: 12 },
194+
{ key: 'style.chart.nodes.borderRadius', def: 3, type: 'number', min: 0, max: 12},
195+
194196
{ key: 'style.chart.links.opacity', def: 0.8, type: 'number', min: 0, max: 1, step: 0.1 },
195197
{ key: 'style.chart.links.stroke', def: '#FFFFFF', type: 'color' },
196198
{ key: 'style.chart.links.strokeWidth', def: 1, type: 'number', min: 0, max: 12 },

src/components/vue-ui-flow.vue

Lines changed: 70 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ const observedEl = ref(null);
8282
const readyTeleport = ref(false);
8383
const tableUnit = ref(null);
8484
const userOptionsRef = ref(null);
85+
const tooltip = ref(null);
8586
8687
const isDataset = computed(() => {
8788
return !!props.dataset && props.dataset.length;
@@ -430,22 +431,23 @@ function computeSankeyCoordinates(ds) {
430431
let yCursor = Math.max(0, (innerH - used) / 2);
431432
432433
names.forEach((name, i) => {
433-
const h = heights[i];
434-
const x = p.left + levelIndex * colSpacing;
435-
const y = yCursor;
436-
437-
nodeCoordinates[name] = {
438-
x,
439-
y,
440-
absoluteY: y,
441-
height: h,
442-
i,
443-
color: nodes[name].color,
444-
value: nodes[name].value,
445-
};
434+
const h = heights[i];
435+
const x = p.left + levelIndex * colSpacing;
436+
const y = yCursor;
437+
438+
nodeCoordinates[name] = {
439+
x,
440+
y,
441+
absoluteY: y,
442+
height: h,
443+
i,
444+
color: nodes[name].color,
445+
value: nodes[name].value,
446+
id: createUid()
447+
};
446448
447-
yCursor += h;
448-
if (i < n - 1) yCursor += gapPx;
449+
yCursor += h;
450+
if (i < n - 1) yCursor += gapPx;
449451
});
450452
});
451453
@@ -581,11 +583,14 @@ const selectedNodes = ref(null);
581583
const selectedSource = ref(null);
582584
const dataTooltipSlot = ref(null);
583585
const useCustomFormat = ref(false);
586+
const selectedNodeId = ref(null);
587+
584588
585589
function selectNode(node, index) {
586590
segregated.value = [];
587591
selectedNodes.value = findConnectedNodes(node.name);
588592
selectedSource.value = node.name;
593+
selectedNodeId.value = node.id;
589594
590595
const nodeName = node.name;
591596
const dataset = sanitizedDataset.value;
@@ -712,6 +717,7 @@ function selectNode(node, index) {
712717
}
713718
714719
function unselectNode(index) {
720+
selectedNodeId.value = null;
715721
const datapoint = dataTooltipSlot.value;
716722
if (FINAL_CONFIG.value.events.datapointLeave) {
717723
FINAL_CONFIG.value.events.datapointLeave({ datapoint, seriesIndex: index });
@@ -1006,6 +1012,17 @@ async function generateSvg({ isCb }) {
10061012
}
10071013
}
10081014
1015+
async function focusNode(node, i) {
1016+
selectNode(node, i);
1017+
if (mutableConfig.value.showTooltip) {
1018+
await nextTick();
1019+
if (flowChart.value && tooltip.value) {
1020+
const { left, top } = flowChart.value.getBoundingClientRect();
1021+
tooltip.value.placeTooltip({x: (left ?? 0) + 12, y: (top ?? 0) + 12 });
1022+
}
1023+
}
1024+
}
1025+
10091026
defineExpose({
10101027
getData,
10111028
getImage,
@@ -1150,9 +1167,17 @@ defineExpose({
11501167
</linearGradient>
11511168
</defs>
11521169
1153-
<path data-cy="link" v-for="path in mutableDataset.links" class="vue-ui-flow-link" :d="path.path" stroke-linejoin="round" stroke-miterlimit="1"
1154-
:fill="`url(#${path.id})`" :stroke="FINAL_CONFIG.style.chart.links.stroke"
1155-
:stroke-width="FINAL_CONFIG.style.chart.links.strokeWidth" :style="`
1170+
<path
1171+
data-cy="link"
1172+
v-for="path in mutableDataset.links"
1173+
class="vue-ui-flow-link"
1174+
:d="path.path"
1175+
stroke-linejoin="round"
1176+
stroke-miterlimit="1"
1177+
:fill="`url(#${path.id})`"
1178+
:stroke="FINAL_CONFIG.style.chart.links.stroke"
1179+
:stroke-width="FINAL_CONFIG.style.chart.links.strokeWidth"
1180+
:style="`
11561181
opacity:${selectedNodes
11571182
? selectedNodes.includes(path.source) &&
11581183
selectedNodes.includes(path.target)
@@ -1164,7 +1189,8 @@ defineExpose({
11641189
: 0.3
11651190
: FINAL_CONFIG.style.chart.links.opacity
11661191
}
1167-
`" />
1192+
`"
1193+
/>
11681194
11691195
<rect
11701196
data-cy="node"
@@ -1177,9 +1203,32 @@ defineExpose({
11771203
:fill="node.color"
11781204
:stroke="FINAL_CONFIG.style.chart.nodes.stroke"
11791205
:stroke-width="FINAL_CONFIG.style.chart.nodes.strokeWidth"
1206+
:rx="FINAL_CONFIG.style.chart.nodes.borderRadius"
1207+
:style="{
1208+
opacity: selectedNodes ? (selectedNodes.includes(node.name) ? 1 : 0.3) : 1,
1209+
outline: selectedNodeId !== null && selectedNodeId === node.id ? '2px solid currentColor' : undefined,
1210+
}"
1211+
1212+
role="button"
1213+
tabindex="0"
1214+
:aria-label="`${node.name}: ${applyDataLabel(
1215+
FINAL_CONFIG.style.chart.nodes.labels.formatter,
1216+
node.value,
1217+
dataLabel({
1218+
p: FINAL_CONFIG.style.chart.nodes.labels.prefix,
1219+
v: node.value,
1220+
s: FINAL_CONFIG.style.chart.nodes.labels.suffix,
1221+
r: FINAL_CONFIG.style.chart.nodes.labels.rounding
1222+
})
1223+
)}`"
11801224
@mouseenter="selectNode(node, i)"
1181-
@mouseleave="unselectNode(i)" :style="`opacity:${selectedNodes ? (selectedNodes.includes(node.name) ? 1 : 0.3) : 1 }`"
1225+
@mouseleave="unselectNode(i)"
11821226
@click="clickNode(i)"
1227+
@keydown.enter.prevent="clickNode(i)"
1228+
@keydown.space.prevent="clickNode(i)"
1229+
@keydown.esc.prevent="unselectNode(i)"
1230+
@focus="focusNode(node, i)"
1231+
@blur="unselectNode(i)"
11831232
/>
11841233
11851234
<g v-if="FINAL_CONFIG.style.chart.nodes.labels.show">
@@ -1256,6 +1305,7 @@ defineExpose({
12561305
12571306
<!-- TOOLTIP -->
12581307
<Tooltip
1308+
ref="tooltip"
12591309
:show="mutableConfig.showTooltip && isTooltip"
12601310
:backgroundColor="FINAL_CONFIG.style.chart.tooltip.backgroundColor"
12611311
:color="FINAL_CONFIG.style.chart.tooltip.color"

src/useConfig.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4672,7 +4672,8 @@ export function useConfig() {
46724672
formatter: null
46734673
},
46744674
stroke: COLOR_WHITE,
4675-
strokeWidth: 1
4675+
strokeWidth: 1,
4676+
borderRadius: 0,
46764677
},
46774678
links: {
46784679
// width: 200, // v3 deprecated

types/vue-data-ui.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7347,6 +7347,7 @@ declare module "vue-data-ui" {
73477347
};
73487348
stroke?: string;
73497349
strokeWidth?: number;
7350+
borderRadius?: number;
73507351
};
73517352
links?: {
73527353
// width?: number; // v3 deprecated

0 commit comments

Comments
 (0)