@empoweredvote/ev-ui 0.8.1 → 0.8.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +88 -5
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +88 -5
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -12,7 +12,9 @@ function RadarChartCore({
|
|
|
12
12
|
onReplaceTopic = () => {
|
|
13
13
|
},
|
|
14
14
|
size = 400,
|
|
15
|
+
labelFontSize = 13,
|
|
15
16
|
padding = 80,
|
|
17
|
+
labelOffset = 32,
|
|
16
18
|
tightFit = false,
|
|
17
19
|
activeSpoke = null,
|
|
18
20
|
writeIns = {}
|
|
@@ -86,9 +88,25 @@ function RadarChartCore({
|
|
|
86
88
|
acc[shortTitle] = userAdj > 10 || cmpAdj > 10;
|
|
87
89
|
return acc;
|
|
88
90
|
}, {});
|
|
89
|
-
const
|
|
90
|
-
|
|
91
|
-
|
|
91
|
+
const labelMeta = spokes.map(([shortTitle], i) => {
|
|
92
|
+
const angle = 2 * Math.PI * i / numSpokes;
|
|
93
|
+
const lines = wrapLabel(shortTitle, 12);
|
|
94
|
+
const longestLineLen = lines.reduce((m, ln) => ln.length > m ? ln.length : m, 0);
|
|
95
|
+
const estimatedWidth = longestLineLen * labelFontSize * 0.55;
|
|
96
|
+
const sinA = Math.sin(angle);
|
|
97
|
+
const side = sinA < -0.1 ? "left" : sinA > 0.1 ? "right" : "center";
|
|
98
|
+
return { estimatedWidth, side };
|
|
99
|
+
});
|
|
100
|
+
const leftLabelWidths = labelMeta.filter((m) => m.side === "left").map((m) => m.estimatedWidth);
|
|
101
|
+
const rightLabelWidths = labelMeta.filter((m) => m.side === "right").map((m) => m.estimatedWidth);
|
|
102
|
+
const minPadding = 40;
|
|
103
|
+
const maxPadding = padding * 2;
|
|
104
|
+
const rawLeft = Math.min(Math.max(leftLabelWidths.length ? Math.max(...leftLabelWidths) + labelOffset : 0, minPadding), maxPadding);
|
|
105
|
+
const rawRight = Math.min(Math.max(rightLabelWidths.length ? Math.max(...rightLabelWidths) + labelOffset : 0, minPadding), maxPadding);
|
|
106
|
+
const sidePadding = Math.max(rawLeft, rawRight);
|
|
107
|
+
const verticalPadding = padding;
|
|
108
|
+
let tightTop = -verticalPadding;
|
|
109
|
+
let tightHeight = size + verticalPadding * 2;
|
|
92
110
|
if (tightFit && numSpokes > 0) {
|
|
93
111
|
let yMin = Infinity, yMax = -Infinity;
|
|
94
112
|
for (let i = 0; i < numSpokes; i++) {
|
|
@@ -98,8 +116,9 @@ function RadarChartCore({
|
|
|
98
116
|
if (y < yMin) yMin = y;
|
|
99
117
|
if (y > yMax) yMax = y;
|
|
100
118
|
}
|
|
101
|
-
|
|
102
|
-
|
|
119
|
+
const labelMargin = labelOffset + labelFontSize * 2.5;
|
|
120
|
+
tightTop = yMin - labelMargin;
|
|
121
|
+
tightHeight = yMax + labelMargin - tightTop;
|
|
103
122
|
}
|
|
104
123
|
const guidePolygons = [];
|
|
105
124
|
for (let level = 1; level <= 5; level++) {
|
|
@@ -164,6 +183,39 @@ function RadarChartCore({
|
|
|
164
183
|
}
|
|
165
184
|
));
|
|
166
185
|
}),
|
|
186
|
+
spokes.map(([shortTitle], i) => {
|
|
187
|
+
const angle = 2 * Math.PI * i / numSpokes;
|
|
188
|
+
const cosA = Math.cos(angle);
|
|
189
|
+
const isBottom = cosA < -0.5;
|
|
190
|
+
const isTop = cosA > 0.5;
|
|
191
|
+
const lines = wrapLabel(shortTitle, 12);
|
|
192
|
+
const longestLine = lines.reduce((m, ln) => ln.length > m ? ln.length : m, 0);
|
|
193
|
+
const lineHeight = labelFontSize * 1.2;
|
|
194
|
+
const dynamicOffset = lines.length > 1 ? labelOffset + 6 : labelOffset;
|
|
195
|
+
const offset2 = radius + dynamicOffset;
|
|
196
|
+
const labelX = centerX + offset2 * Math.sin(angle);
|
|
197
|
+
let labelY = centerY - offset2 * Math.cos(angle);
|
|
198
|
+
if (isTop && lines.length > 1) labelY -= (lines.length - 1) * lineHeight;
|
|
199
|
+
const isActive = shortTitle === activeSpoke;
|
|
200
|
+
const isUnansweredLabel = !!unansweredSpokes[shortTitle];
|
|
201
|
+
const fSize = isActive ? labelFontSize * 1.2 : labelFontSize;
|
|
202
|
+
return /* @__PURE__ */ React.createElement(
|
|
203
|
+
"text",
|
|
204
|
+
{
|
|
205
|
+
key: `label-${shortTitle}`,
|
|
206
|
+
x: labelX,
|
|
207
|
+
y: labelY,
|
|
208
|
+
textAnchor: "middle",
|
|
209
|
+
dominantBaseline: isBottom ? "hanging" : "auto",
|
|
210
|
+
onClick: () => onReplaceTopic(shortTitle),
|
|
211
|
+
fill: isActive ? "#E85D26" : isUnansweredLabel ? "#9ca3af" : "#555",
|
|
212
|
+
fontWeight: isActive ? "bold" : "normal",
|
|
213
|
+
style: { cursor: "pointer", userSelect: "none", fontSize: fSize, fontFamily: "sans-serif" }
|
|
214
|
+
},
|
|
215
|
+
isUnansweredLabel && /* @__PURE__ */ React.createElement("title", null, "No stance on file for this topic."),
|
|
216
|
+
lines.map((ln, idx) => /* @__PURE__ */ React.createElement("tspan", { key: idx, x: labelX, dy: idx === 0 ? "0" : `${lineHeight}` }, ln))
|
|
217
|
+
);
|
|
218
|
+
}),
|
|
167
219
|
countChanged ? /* @__PURE__ */ React.createElement(
|
|
168
220
|
"polygon",
|
|
169
221
|
{
|
|
@@ -296,6 +348,37 @@ function RadarChartCore({
|
|
|
296
348
|
})()
|
|
297
349
|
);
|
|
298
350
|
}
|
|
351
|
+
function wrapLabel(label, maxChars = 12) {
|
|
352
|
+
const str = String(label);
|
|
353
|
+
let rawWords;
|
|
354
|
+
if (str.includes("/")) {
|
|
355
|
+
rawWords = str.split("/").reduce((acc, segment, idx, arr) => {
|
|
356
|
+
const part = idx < arr.length - 1 ? segment + "/" : segment;
|
|
357
|
+
if (part.trim()) acc.push(part.trim());
|
|
358
|
+
return acc;
|
|
359
|
+
}, []);
|
|
360
|
+
} else {
|
|
361
|
+
rawWords = str.split(/\s+/);
|
|
362
|
+
}
|
|
363
|
+
const lines = [];
|
|
364
|
+
let line = "";
|
|
365
|
+
for (const word of rawWords) {
|
|
366
|
+
if (line === "") {
|
|
367
|
+
line = word;
|
|
368
|
+
} else if ((line + " " + word).length <= maxChars) {
|
|
369
|
+
line += " " + word;
|
|
370
|
+
} else {
|
|
371
|
+
lines.push(line);
|
|
372
|
+
line = word;
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
if (line) lines.push(line);
|
|
376
|
+
if (lines.length > 2) {
|
|
377
|
+
const overflow = lines.splice(2).join(" ");
|
|
378
|
+
lines[1] = lines[1] + " " + overflow;
|
|
379
|
+
}
|
|
380
|
+
return lines;
|
|
381
|
+
}
|
|
299
382
|
|
|
300
383
|
// src/Header.jsx
|
|
301
384
|
import React2, { useState as useState3, useRef as useRef2, useEffect as useEffect3 } from "react";
|