Skip to content

Commit 81e2c2d

Browse files
committed
fix(newfront): sankey chart agora move os nós
1 parent 25b1795 commit 81e2c2d

2 files changed

Lines changed: 65 additions & 44 deletions

File tree

‎public/styles/main.css‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,10 @@ body {
176176
text-shadow: 0 1px 0 #fff;
177177
}
178178

179+
.sankey-links {
180+
pointer-events: none;
181+
}
182+
179183
.link {
180184
fill: none;
181185
stroke: #000;

‎src/sankey/councilSankey.ts‎

Lines changed: 61 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -3,30 +3,22 @@ import {
33
sankey,
44
sankeyJustify,
55
sankeyLinkHorizontal,
6+
type SankeyLink,
7+
type SankeyNode,
68
} from "d3-sankey";
79
import { SANKEY_HEIGHT, SANKEY_WIDTH } from "../config";
810
import type { SankeyGraph as AppSankeyGraph } from "../types/election";
911
import { orientacaoFromSankeyNodeName, orientacaoToColor } from "../utils/colors";
1012

11-
interface LayoutNode {
12-
name: string;
13-
x0?: number;
14-
x1?: number;
15-
y0?: number;
16-
y1?: number;
17-
value?: number;
18-
}
19-
20-
interface LayoutLink {
21-
source: LayoutNode | number;
22-
target: LayoutNode | number;
23-
value: number;
24-
width?: number;
25-
}
13+
type LayoutNode = SankeyNode<{}, {}> & { name: string };
14+
type LayoutLink = SankeyLink<LayoutNode, {}>;
2615

2716
const UNITS = "Cadeiras";
2817
const MARGIN = { top: 20, right: 10, bottom: 50, left: 10 };
2918
const INNER_HEIGHT = SANKEY_HEIGHT - 100;
19+
/** Mesmo deslocamento do site legado (camara.js) */
20+
const CHART_OFFSET_X = 100;
21+
const CHART_OFFSET_Y = 55;
3022

3123
export class CouncilSankey {
3224
private readonly svg: Selection<SVGSVGElement, unknown, null, undefined>;
@@ -91,51 +83,50 @@ export class CouncilSankey {
9183
[SANKEY_WIDTH - MARGIN.right, INNER_HEIGHT],
9284
]);
9385

94-
const laidOut = layout(layoutGraph);
86+
const { nodes, links } = layout(layoutGraph);
87+
const linkPath = sankeyLinkHorizontal();
88+
const activeLinks = links.filter((l) => l.value > 0);
89+
const activeNodes = nodes.filter((n) => (n.value ?? 0) > 0);
90+
const graphForUpdate = { nodes: activeNodes, links: activeLinks };
91+
92+
const innerG = this.chartG
93+
.append("g")
94+
.attr("class", "sankey-inner")
95+
.attr("transform", `translate(${CHART_OFFSET_X},${CHART_OFFSET_Y})`);
96+
97+
const dragContainer = innerG.node()!;
9598

96-
const linkG = this.chartG
99+
const linkSelection = innerG
97100
.append("g")
98-
.attr("transform", "translate(100,55)")
101+
.attr("class", "sankey-links")
99102
.attr("fill", "none")
100103
.attr("stroke", "#000")
101104
.attr("stroke-opacity", 0.2)
105+
.style("pointer-events", "none")
102106
.selectAll<SVGPathElement, LayoutLink>("path")
103-
.data(laidOut.links.filter((l) => l.value > 0))
107+
.data(activeLinks)
104108
.join("path")
105-
.attr("class", "link")
106-
.attr("d", sankeyLinkHorizontal())
107-
.attr("stroke-width", (d) => Math.max(1, d.width ?? 0));
109+
.attr("class", (d) => (d.value ? "link" : "link zero"))
110+
.attr("d", linkPath)
111+
.attr("stroke-width", (d) => Math.max(1, d.width ?? 0))
112+
.sort((a, b) => (b.width ?? 0) - (a.width ?? 0));
108113

109-
linkG.append("title").text((d) => {
114+
linkSelection.append("title").text((d) => {
110115
const s = d.source as LayoutNode;
111116
const t = d.target as LayoutNode;
112117
return `${s.name} → ${t.name}\n${d.value}`;
113118
});
114119

115-
const nodeDrag = drag<SVGGElement, LayoutNode>()
116-
.subject((d) => d)
117-
.on("drag", (event, d) => {
118-
const height = (d.y1 ?? 0) - (d.y0 ?? 0);
119-
d.y0 = Math.max(0, Math.min(INNER_HEIGHT - height, event.y));
120-
d.y1 = (d.y0 ?? 0) + height;
121-
select(event.sourceEvent.target.parentNode as SVGGElement).attr(
122-
"transform",
123-
`translate(${d.x0 ?? 0},${d.y0 ?? 0})`,
124-
);
125-
linkG.attr("d", sankeyLinkHorizontal());
126-
});
127-
128-
const node = this.chartG
120+
const nodeSelection = innerG
129121
.append("g")
130-
.attr("transform", "translate(100,55)")
122+
.attr("class", "sankey-nodes")
131123
.selectAll<SVGGElement, LayoutNode>("g")
132-
.data(laidOut.nodes.filter((n) => (n.value ?? 0) > 0))
124+
.data(activeNodes)
133125
.join("g")
134-
.attr("class", "node")
135-
.attr("transform", (d) => `translate(${d.x0 ?? 0},${d.y0 ?? 0})`)
136-
.call(nodeDrag);
126+
.attr("class", (d) => ((d.value ?? 0) > 0 ? "node" : "node zero"))
127+
.attr("transform", (d) => `translate(${d.x0 ?? 0},${d.y0 ?? 0})`);
137128

138-
node
129+
nodeSelection
139130
.append("rect")
140131
.attr("height", (d) => Math.max(1, (d.y1 ?? 0) - (d.y0 ?? 0)))
141132
.attr("width", (d) => Math.max(1, (d.x1 ?? 0) - (d.x0 ?? 0)))
@@ -144,7 +135,7 @@ export class CouncilSankey {
144135
.append("title")
145136
.text((d) => `${d.name}\n${formatValue(d.value ?? 0)}`);
146137

147-
node
138+
nodeSelection
148139
.append("text")
149140
.attr("x", -6)
150141
.attr("y", (d) => ((d.y1 ?? 0) - (d.y0 ?? 0)) / 2)
@@ -155,6 +146,32 @@ export class CouncilSankey {
155146
.attr("x", (d) => (d.x1 ?? 0) - (d.x0 ?? 0) + 6)
156147
.attr("text-anchor", "start");
157148

149+
const nodeDrag = drag<SVGRectElement, LayoutNode>()
150+
.container(() => dragContainer)
151+
.subject(function (_event, d) {
152+
return { x: d.x0 ?? 0, y: d.y0 ?? 0 };
153+
})
154+
.on("start", function () {
155+
select(this.parentNode as SVGGElement).raise();
156+
})
157+
.on("drag", function (event, d) {
158+
const nodeHeight = (d.y1 ?? 0) - (d.y0 ?? 0);
159+
const y = Math.max(
160+
MARGIN.top,
161+
Math.min(INNER_HEIGHT - nodeHeight, event.y),
162+
);
163+
d.y0 = y;
164+
d.y1 = y + nodeHeight;
165+
select(this.parentNode as SVGGElement).attr(
166+
"transform",
167+
`translate(${d.x0 ?? 0},${y})`,
168+
);
169+
layout.update(graphForUpdate);
170+
linkSelection.attr("d", linkPath);
171+
});
172+
173+
nodeSelection.selectAll<SVGRectElement, LayoutNode>("rect").call(nodeDrag);
174+
158175
this.chartG
159176
.append("text")
160177
.attr("class", "chartTitle2")

0 commit comments

Comments
 (0)