@empoweredvote/ev-ui 0.8.12 → 0.8.13
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 +589 -667
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +211 -289
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -1,6 +1,34 @@
|
|
|
1
1
|
// src/RadarChartCore.jsx
|
|
2
|
-
import React
|
|
2
|
+
import React from "react";
|
|
3
3
|
import { animated, useSpring } from "@react-spring/web";
|
|
4
|
+
import { useRef, useEffect as useEffect2 } from "react";
|
|
5
|
+
|
|
6
|
+
// src/useTheme.js
|
|
7
|
+
import { useState, useEffect } from "react";
|
|
8
|
+
function useTheme() {
|
|
9
|
+
const read = () => {
|
|
10
|
+
if (typeof document === "undefined") return false;
|
|
11
|
+
const root = document.documentElement;
|
|
12
|
+
const body = document.body;
|
|
13
|
+
const hasDarkClass = (el) => el && el.classList && el.classList.contains("dark");
|
|
14
|
+
const hasDarkAttr = (el) => el && el.getAttribute && el.getAttribute("data-theme") === "dark";
|
|
15
|
+
return hasDarkClass(root) || hasDarkAttr(root) || hasDarkClass(body) || hasDarkAttr(body);
|
|
16
|
+
};
|
|
17
|
+
const [isDark, setIsDark] = useState(read);
|
|
18
|
+
useEffect(() => {
|
|
19
|
+
if (typeof document === "undefined" || typeof MutationObserver === "undefined") return;
|
|
20
|
+
const update = () => setIsDark(read());
|
|
21
|
+
update();
|
|
22
|
+
const observer = new MutationObserver(update);
|
|
23
|
+
const opts = { attributes: true, attributeFilter: ["class", "data-theme"] };
|
|
24
|
+
observer.observe(document.documentElement, opts);
|
|
25
|
+
if (document.body) observer.observe(document.body, opts);
|
|
26
|
+
return () => observer.disconnect();
|
|
27
|
+
}, []);
|
|
28
|
+
return { isDark };
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// src/RadarChartCore.jsx
|
|
4
32
|
function RadarChartCore({
|
|
5
33
|
topics,
|
|
6
34
|
data,
|
|
@@ -12,95 +40,83 @@ function RadarChartCore({
|
|
|
12
40
|
onReplaceTopic = () => {
|
|
13
41
|
},
|
|
14
42
|
size = 400,
|
|
15
|
-
labelFontSize =
|
|
43
|
+
labelFontSize = 18,
|
|
16
44
|
padding = 80,
|
|
17
|
-
labelOffset =
|
|
18
|
-
tightFit = false
|
|
19
|
-
activeSpoke = null,
|
|
20
|
-
writeIns = {},
|
|
21
|
-
darkMode = false,
|
|
22
|
-
lineColor,
|
|
23
|
-
ringColor,
|
|
24
|
-
// replacedSpokes: short_titles of substitute spokes (not the user's original defaults)
|
|
25
|
-
replacedSpokes = {},
|
|
26
|
-
// boldOriginalSpokes: when true, bold spokes that are NOT replacements
|
|
27
|
-
boldOriginalSpokes = false,
|
|
28
|
-
highlightedSpoke = null
|
|
45
|
+
labelOffset = 20,
|
|
46
|
+
tightFit = false
|
|
29
47
|
}) {
|
|
30
|
-
|
|
48
|
+
const { isDark } = useTheme();
|
|
31
49
|
const radius = size / 2 - 40;
|
|
32
|
-
const extRadius = radius * 1.15;
|
|
33
50
|
const centerX = size / 2;
|
|
34
51
|
const centerY = size / 2;
|
|
35
|
-
const [tooltip, setTooltip] = useState(null);
|
|
36
52
|
const spokes = Object.entries(data);
|
|
37
53
|
const numSpokes = Math.max(spokes.length, 1);
|
|
38
54
|
const prevCountRef = useRef(numSpokes);
|
|
39
55
|
const countChanged = numSpokes !== prevCountRef.current;
|
|
40
|
-
|
|
56
|
+
useEffect2(() => {
|
|
41
57
|
prevCountRef.current = numSpokes;
|
|
42
58
|
}, [numSpokes]);
|
|
43
|
-
function calcAdjusted(value, shortTitle) {
|
|
44
|
-
var _a2;
|
|
45
|
-
if (!value || Number(value) === 0) return 0;
|
|
46
|
-
const topic = topics.find((t) => t.short_title === shortTitle);
|
|
47
|
-
const max = ((_a2 = topic == null ? void 0 : topic.stances) == null ? void 0 : _a2.length) || 10;
|
|
48
|
-
const pct = Number(value) / max * 10;
|
|
49
|
-
return invertedSpokes[shortTitle] ? (max + 1) * 10 / max - pct : pct;
|
|
50
|
-
}
|
|
51
59
|
const pointsArr = spokes.map(([shortTitle, value], index) => {
|
|
52
|
-
|
|
53
|
-
const
|
|
60
|
+
var _a;
|
|
61
|
+
const topic = topics.find((t) => t.short_title === shortTitle);
|
|
62
|
+
const max = ((_a = topic == null ? void 0 : topic.stances) == null ? void 0 : _a.length) || 10;
|
|
63
|
+
const pct = Math.min(value / max * 10, 10);
|
|
54
64
|
const angle = 2 * Math.PI * index / numSpokes;
|
|
55
|
-
|
|
65
|
+
const adjusted = value === 0 ? 0 : invertedSpokes[shortTitle] ? 11 - pct : pct;
|
|
66
|
+
const r = adjusted / 10 * radius;
|
|
67
|
+
const x = centerX + r * Math.sin(angle);
|
|
68
|
+
const y = centerY - r * Math.cos(angle);
|
|
69
|
+
return [x, y];
|
|
56
70
|
});
|
|
57
71
|
const targetPoints = pointsArr.map((p) => p.join(",")).join(" ");
|
|
58
|
-
const
|
|
72
|
+
const spring = useSpring({
|
|
59
73
|
to: { points: targetPoints },
|
|
60
74
|
immediate: countChanged,
|
|
75
|
+
reset: countChanged,
|
|
61
76
|
config: { tension: 300, friction: 30 }
|
|
62
|
-
};
|
|
63
|
-
if (countChanged) springBase.from = { points: targetPoints };
|
|
64
|
-
const spring = useSpring(springBase);
|
|
77
|
+
});
|
|
65
78
|
const hasCompareData = Object.keys(compareData).length > 0;
|
|
66
79
|
const centerPoints = spokes.map(() => `${centerX},${centerY}`).join(" ");
|
|
67
80
|
let comparePoints = null;
|
|
68
81
|
let compareCoords = null;
|
|
69
82
|
if (hasCompareData) {
|
|
70
83
|
const ccoords = spokes.map(([shortTitle], index) => {
|
|
71
|
-
var
|
|
72
|
-
const value = (
|
|
73
|
-
const
|
|
74
|
-
const
|
|
84
|
+
var _a, _b;
|
|
85
|
+
const value = (_a = compareData[shortTitle]) != null ? _a : 0;
|
|
86
|
+
const topic = topics.find((t) => t.short_title === shortTitle);
|
|
87
|
+
const max = ((_b = topic == null ? void 0 : topic.stances) == null ? void 0 : _b.length) || 10;
|
|
88
|
+
const pct = Math.min(value / max * 10, 10);
|
|
75
89
|
const angle = 2 * Math.PI * index / numSpokes;
|
|
76
|
-
|
|
90
|
+
const adjusted = value === 0 ? 0 : invertedSpokes[shortTitle] ? 11 - pct : pct;
|
|
91
|
+
const r = adjusted / 10 * radius;
|
|
92
|
+
const x = centerX + r * Math.sin(angle);
|
|
93
|
+
const y = centerY - r * Math.cos(angle);
|
|
94
|
+
return [x, y];
|
|
77
95
|
});
|
|
78
96
|
compareCoords = ccoords;
|
|
79
97
|
comparePoints = ccoords.map((p) => p.join(",")).join(" ");
|
|
80
98
|
}
|
|
81
99
|
const hadCompareDataRef = useRef(false);
|
|
82
100
|
const compareJustAppeared = hasCompareData && !hadCompareDataRef.current;
|
|
83
|
-
|
|
101
|
+
useEffect2(() => {
|
|
84
102
|
hadCompareDataRef.current = hasCompareData;
|
|
85
103
|
}, [hasCompareData]);
|
|
86
|
-
const
|
|
104
|
+
const compareSpring = useSpring({
|
|
87
105
|
to: { points: comparePoints || centerPoints || `${centerX},${centerY}` },
|
|
88
106
|
immediate: countChanged || compareJustAppeared,
|
|
107
|
+
reset: countChanged || compareJustAppeared,
|
|
89
108
|
config: { tension: 300, friction: 30 }
|
|
90
|
-
};
|
|
91
|
-
if (countChanged || compareJustAppeared) compareSpringBase.from = compareSpringBase.to;
|
|
92
|
-
const compareSpring = useSpring(compareSpringBase);
|
|
93
|
-
const spokeExtension = spokes.reduce((acc, [shortTitle]) => {
|
|
94
|
-
const userAdj = calcAdjusted(data[shortTitle], shortTitle);
|
|
95
|
-
const cmpAdj = hasCompareData && compareData[shortTitle] ? calcAdjusted(compareData[shortTitle], shortTitle) : 0;
|
|
96
|
-
acc[shortTitle] = userAdj > 10 || cmpAdj > 10;
|
|
97
|
-
return acc;
|
|
98
|
-
}, {});
|
|
109
|
+
});
|
|
99
110
|
const labelMeta = spokes.map(([shortTitle], i) => {
|
|
100
111
|
const angle = 2 * Math.PI * i / numSpokes;
|
|
112
|
+
const baseFSize = labelFontSize;
|
|
101
113
|
const lines = wrapLabel(shortTitle, 12);
|
|
102
|
-
const longestLineLen = lines.reduce(
|
|
103
|
-
|
|
114
|
+
const longestLineLen = lines.reduce(
|
|
115
|
+
(max, ln) => ln.length > max ? ln.length : max,
|
|
116
|
+
0
|
|
117
|
+
);
|
|
118
|
+
const fSize = adaptiveFontSize(longestLineLen, baseFSize);
|
|
119
|
+
const estimatedWidth = longestLineLen * fSize * 0.6;
|
|
104
120
|
const sinA = Math.sin(angle);
|
|
105
121
|
const side = sinA < -0.1 ? "left" : sinA > 0.1 ? "right" : "center";
|
|
106
122
|
return { estimatedWidth, side };
|
|
@@ -109,9 +125,23 @@ function RadarChartCore({
|
|
|
109
125
|
const rightLabelWidths = labelMeta.filter((m) => m.side === "right").map((m) => m.estimatedWidth);
|
|
110
126
|
const minPadding = 40;
|
|
111
127
|
const maxPadding = padding * 2;
|
|
112
|
-
const
|
|
113
|
-
|
|
114
|
-
|
|
128
|
+
const rawLeftPadding = Math.min(
|
|
129
|
+
Math.max(
|
|
130
|
+
leftLabelWidths.length ? Math.max(...leftLabelWidths) + labelOffset : 0,
|
|
131
|
+
minPadding
|
|
132
|
+
),
|
|
133
|
+
maxPadding
|
|
134
|
+
);
|
|
135
|
+
const rawRightPadding = Math.min(
|
|
136
|
+
Math.max(
|
|
137
|
+
rightLabelWidths.length ? Math.max(...rightLabelWidths) + labelOffset : 0,
|
|
138
|
+
minPadding
|
|
139
|
+
),
|
|
140
|
+
maxPadding
|
|
141
|
+
);
|
|
142
|
+
const symmetricPadding = Math.max(rawLeftPadding, rawRightPadding);
|
|
143
|
+
const leftPadding = symmetricPadding;
|
|
144
|
+
const rightPadding = symmetricPadding;
|
|
115
145
|
const verticalPadding = padding;
|
|
116
146
|
let tightTop = -verticalPadding;
|
|
117
147
|
let tightHeight = size + verticalPadding * 2;
|
|
@@ -119,12 +149,11 @@ function RadarChartCore({
|
|
|
119
149
|
let yMin = Infinity, yMax = -Infinity;
|
|
120
150
|
for (let i = 0; i < numSpokes; i++) {
|
|
121
151
|
const angle = 2 * Math.PI * i / numSpokes;
|
|
122
|
-
const
|
|
123
|
-
const y = centerY - effectiveR * Math.cos(angle);
|
|
152
|
+
const y = centerY - radius * Math.cos(angle);
|
|
124
153
|
if (y < yMin) yMin = y;
|
|
125
154
|
if (y > yMax) yMax = y;
|
|
126
155
|
}
|
|
127
|
-
const labelMargin = labelOffset + labelFontSize *
|
|
156
|
+
const labelMargin = labelOffset + labelFontSize * 1.4 + labelFontSize * 1.1;
|
|
128
157
|
tightTop = yMin - labelMargin;
|
|
129
158
|
tightHeight = yMax + labelMargin - tightTop;
|
|
130
159
|
}
|
|
@@ -134,93 +163,75 @@ function RadarChartCore({
|
|
|
134
163
|
const ring = [];
|
|
135
164
|
for (let i = 0; i < numSpokes; i++) {
|
|
136
165
|
const angle = 2 * Math.PI * i / numSpokes;
|
|
137
|
-
|
|
166
|
+
const x = centerX + radius * scale * Math.sin(angle);
|
|
167
|
+
const y = centerY - radius * scale * Math.cos(angle);
|
|
168
|
+
ring.push(`${x},${y}`);
|
|
138
169
|
}
|
|
139
170
|
guidePolygons.push(
|
|
140
|
-
/* @__PURE__ */ React.createElement("polygon", { key: level, points: ring.join(" "), fill: "none", stroke:
|
|
171
|
+
/* @__PURE__ */ React.createElement("polygon", { key: level, points: ring.join(" "), fill: "none", stroke: isDark ? "#374151" : "#ccc" })
|
|
141
172
|
);
|
|
142
173
|
}
|
|
143
|
-
const getStanceText = (shortTitle, value) => {
|
|
144
|
-
if (writeIns[shortTitle]) return writeIns[shortTitle];
|
|
145
|
-
const topic = topics.find((t) => t.short_title === shortTitle);
|
|
146
|
-
const stances = (topic == null ? void 0 : topic.stances) || [];
|
|
147
|
-
const idx = Math.round(Number(value)) - 1;
|
|
148
|
-
if (idx >= 0 && idx < stances.length) {
|
|
149
|
-
return stances[idx].text || shortTitle;
|
|
150
|
-
}
|
|
151
|
-
return shortTitle;
|
|
152
|
-
};
|
|
153
174
|
return /* @__PURE__ */ React.createElement(
|
|
154
175
|
"svg",
|
|
155
176
|
{
|
|
156
177
|
className: "w-full h-auto max-h-full",
|
|
157
|
-
viewBox: `-${
|
|
158
|
-
preserveAspectRatio: "xMidYMid meet"
|
|
159
|
-
onMouseLeave: () => setTooltip(null)
|
|
178
|
+
viewBox: `-${leftPadding} ${tightTop} ${size + leftPadding + rightPadding} ${tightHeight}`,
|
|
179
|
+
preserveAspectRatio: "xMidYMid meet"
|
|
160
180
|
},
|
|
161
181
|
guidePolygons,
|
|
162
182
|
spokes.map(([shortTitle], i) => {
|
|
163
183
|
const angle = 2 * Math.PI * i / numSpokes;
|
|
164
|
-
const
|
|
165
|
-
const
|
|
184
|
+
const x = centerX + radius * Math.sin(angle);
|
|
185
|
+
const y = centerY - radius * Math.cos(angle);
|
|
166
186
|
const isUnanswered = !!unansweredSpokes[shortTitle];
|
|
167
|
-
|
|
168
|
-
const extX = centerX + extRadius * Math.sin(angle);
|
|
169
|
-
const extY = centerY - extRadius * Math.cos(angle);
|
|
170
|
-
return /* @__PURE__ */ React.createElement("g", { key: `spoke-${shortTitle}` }, /* @__PURE__ */ React.createElement(
|
|
187
|
+
return /* @__PURE__ */ React.createElement(
|
|
171
188
|
"line",
|
|
172
189
|
{
|
|
190
|
+
key: `line-${shortTitle}`,
|
|
173
191
|
x1: centerX,
|
|
174
192
|
y1: centerY,
|
|
175
|
-
x2:
|
|
176
|
-
y2:
|
|
177
|
-
stroke: isUnanswered ? "#
|
|
193
|
+
x2: x,
|
|
194
|
+
y2: y,
|
|
195
|
+
stroke: isUnanswered ? isDark ? "#6b7280" : "#9ca3af" : isDark ? "#d1d5db" : "black",
|
|
178
196
|
strokeDasharray: isUnanswered ? "4 3" : void 0,
|
|
179
197
|
opacity: isUnanswered ? 0.6 : 1
|
|
180
198
|
}
|
|
181
|
-
)
|
|
182
|
-
"line",
|
|
183
|
-
{
|
|
184
|
-
x1: endX,
|
|
185
|
-
y1: endY,
|
|
186
|
-
x2: extX,
|
|
187
|
-
y2: extY,
|
|
188
|
-
stroke: "#aaa",
|
|
189
|
-
strokeDasharray: "4 4",
|
|
190
|
-
opacity: 0.4
|
|
191
|
-
}
|
|
192
|
-
));
|
|
199
|
+
);
|
|
193
200
|
}),
|
|
194
201
|
spokes.map(([shortTitle], i) => {
|
|
195
202
|
const angle = 2 * Math.PI * i / numSpokes;
|
|
203
|
+
const anchor = angle > Math.PI ? "end" : angle < Math.PI && angle > 0 ? "start" : "middle";
|
|
196
204
|
const cosA = Math.cos(angle);
|
|
197
|
-
const isBottom = cosA < -0.5;
|
|
198
205
|
const isTop = cosA > 0.5;
|
|
206
|
+
const isBottom = cosA < -0.5;
|
|
207
|
+
const baseFSize = labelFontSize;
|
|
199
208
|
const lines = wrapLabel(shortTitle, 12);
|
|
200
|
-
const longestLine = lines.reduce(
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
209
|
+
const longestLine = lines.reduce(
|
|
210
|
+
(max, ln) => ln.length > max ? ln.length : max,
|
|
211
|
+
0
|
|
212
|
+
);
|
|
213
|
+
const fSize = adaptiveFontSize(longestLine, baseFSize);
|
|
214
|
+
const lineHeight = fSize * 1.1;
|
|
215
|
+
const dynamicLabelOffset = lines.length > 1 ? labelOffset + 8 : labelOffset;
|
|
216
|
+
const offset2 = radius + dynamicLabelOffset;
|
|
204
217
|
const labelX = centerX + offset2 * Math.sin(angle);
|
|
205
218
|
let labelY = centerY - offset2 * Math.cos(angle);
|
|
206
|
-
if (isTop && lines.length > 1)
|
|
207
|
-
|
|
219
|
+
if (isTop && lines.length > 1) {
|
|
220
|
+
labelY -= (lines.length - 1) * lineHeight;
|
|
221
|
+
}
|
|
208
222
|
const isUnansweredLabel = !!unansweredSpokes[shortTitle];
|
|
209
|
-
const isReplaced = !!replacedSpokes[shortTitle];
|
|
210
|
-
const shouldBold = boldOriginalSpokes && !isReplaced;
|
|
211
|
-
const fSize = labelFontSize;
|
|
212
223
|
return /* @__PURE__ */ React.createElement(
|
|
213
224
|
"text",
|
|
214
225
|
{
|
|
215
226
|
key: `label-${shortTitle}`,
|
|
216
227
|
x: labelX,
|
|
217
228
|
y: labelY,
|
|
218
|
-
textAnchor:
|
|
229
|
+
textAnchor: anchor,
|
|
219
230
|
dominantBaseline: isBottom ? "hanging" : "auto",
|
|
220
231
|
onClick: () => onReplaceTopic(shortTitle),
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
style: { cursor: "pointer", userSelect: "none", fontSize: fSize
|
|
232
|
+
className: "text-xl font-medium mb-1 md:text-base md:font-normal",
|
|
233
|
+
fill: isUnansweredLabel ? isDark ? "#6b7280" : "#9ca3af" : isDark ? "#d1d5db" : void 0,
|
|
234
|
+
style: { cursor: "pointer", userSelect: "none", fontSize: fSize }
|
|
224
235
|
},
|
|
225
236
|
isUnansweredLabel && /* @__PURE__ */ React.createElement("title", null, "No stance on file for this topic."),
|
|
226
237
|
lines.map((ln, idx) => /* @__PURE__ */ React.createElement("tspan", { key: idx, x: labelX, dy: idx === 0 ? "0" : `${lineHeight}` }, ln))
|
|
@@ -231,14 +242,22 @@ function RadarChartCore({
|
|
|
231
242
|
{
|
|
232
243
|
key: "user-static",
|
|
233
244
|
points: targetPoints,
|
|
234
|
-
style: {
|
|
245
|
+
style: {
|
|
246
|
+
fill: "rgba(124, 107, 158, 0.4)",
|
|
247
|
+
stroke: "#7C6B9E",
|
|
248
|
+
strokeWidth: 3
|
|
249
|
+
}
|
|
235
250
|
}
|
|
236
251
|
) : /* @__PURE__ */ React.createElement(
|
|
237
252
|
animated.polygon,
|
|
238
253
|
{
|
|
239
254
|
key: "user-animated",
|
|
240
255
|
points: spring.points,
|
|
241
|
-
style: {
|
|
256
|
+
style: {
|
|
257
|
+
fill: "rgba(124, 107, 158, 0.4)",
|
|
258
|
+
stroke: "#7C6B9E",
|
|
259
|
+
strokeWidth: 3
|
|
260
|
+
}
|
|
242
261
|
}
|
|
243
262
|
),
|
|
244
263
|
pointsArr.map(([cx, cy], i) => {
|
|
@@ -253,11 +272,10 @@ function RadarChartCore({
|
|
|
253
272
|
key: `user-dot-${i}`,
|
|
254
273
|
cx,
|
|
255
274
|
cy,
|
|
256
|
-
r: matches ?
|
|
275
|
+
r: matches ? 8 : 7,
|
|
257
276
|
fill: matches ? "#fed12e" : "#7C6B9E",
|
|
258
277
|
stroke: "#FFFFFF",
|
|
259
|
-
strokeWidth:
|
|
260
|
-
style: { pointerEvents: "none" }
|
|
278
|
+
strokeWidth: 3
|
|
261
279
|
}
|
|
262
280
|
);
|
|
263
281
|
}),
|
|
@@ -267,10 +285,9 @@ function RadarChartCore({
|
|
|
267
285
|
key: "compare-static",
|
|
268
286
|
points: comparePoints,
|
|
269
287
|
style: {
|
|
270
|
-
fill:
|
|
271
|
-
stroke:
|
|
272
|
-
strokeWidth: 2
|
|
273
|
-
mixBlendMode: darkMode ? "normal" : "multiply"
|
|
288
|
+
fill: "rgba(90, 154, 110, 0.3)",
|
|
289
|
+
stroke: "#5A9A6E",
|
|
290
|
+
strokeWidth: 2
|
|
274
291
|
}
|
|
275
292
|
}
|
|
276
293
|
) : /* @__PURE__ */ React.createElement(
|
|
@@ -279,10 +296,9 @@ function RadarChartCore({
|
|
|
279
296
|
key: "compare-animated",
|
|
280
297
|
points: compareSpring.points,
|
|
281
298
|
style: {
|
|
282
|
-
fill:
|
|
283
|
-
stroke:
|
|
284
|
-
strokeWidth: 2
|
|
285
|
-
mixBlendMode: darkMode ? "normal" : "multiply"
|
|
299
|
+
fill: "rgba(90, 154, 110, 0.3)",
|
|
300
|
+
stroke: "#5A9A6E",
|
|
301
|
+
strokeWidth: 2
|
|
286
302
|
}
|
|
287
303
|
}
|
|
288
304
|
) : null,
|
|
@@ -291,25 +307,24 @@ function RadarChartCore({
|
|
|
291
307
|
const userVal = spokes[i][1];
|
|
292
308
|
const compareVal = compareData[shortTitle];
|
|
293
309
|
if (!compareVal || Number(compareVal) === 0) return null;
|
|
294
|
-
const matches = userVal !== 0 && compareVal != null && Number(userVal) === Number(compareVal);
|
|
310
|
+
const matches = userVal !== 0 && compareVal !== 0 && compareVal != null && Number(userVal) === Number(compareVal);
|
|
295
311
|
return /* @__PURE__ */ React.createElement(
|
|
296
312
|
"circle",
|
|
297
313
|
{
|
|
298
314
|
key: `compare-dot-${i}`,
|
|
299
315
|
cx,
|
|
300
316
|
cy,
|
|
301
|
-
r: matches ?
|
|
302
|
-
fill: matches ? "#fed12e" :
|
|
317
|
+
r: matches ? 8 : 7,
|
|
318
|
+
fill: matches ? "#fed12e" : "#5A9A6E",
|
|
303
319
|
stroke: "#FFFFFF",
|
|
304
|
-
strokeWidth:
|
|
305
|
-
style: { pointerEvents: "none" }
|
|
320
|
+
strokeWidth: 3
|
|
306
321
|
}
|
|
307
322
|
);
|
|
308
323
|
}),
|
|
309
324
|
spokes.map(([shortTitle], i) => {
|
|
310
325
|
const angle = 2 * Math.PI * i / numSpokes;
|
|
311
|
-
const
|
|
312
|
-
const
|
|
326
|
+
const x = centerX + radius * Math.sin(angle);
|
|
327
|
+
const y = centerY - radius * Math.cos(angle);
|
|
313
328
|
const isUnansweredHitbox = !!unansweredSpokes[shortTitle];
|
|
314
329
|
return /* @__PURE__ */ React.createElement(
|
|
315
330
|
"line",
|
|
@@ -317,91 +332,20 @@ function RadarChartCore({
|
|
|
317
332
|
key: `hitbox-${shortTitle}`,
|
|
318
333
|
x1: centerX,
|
|
319
334
|
y1: centerY,
|
|
320
|
-
x2:
|
|
321
|
-
y2:
|
|
335
|
+
x2: x,
|
|
336
|
+
y2: y,
|
|
322
337
|
stroke: "transparent",
|
|
323
338
|
strokeWidth: 14,
|
|
324
339
|
onClick: () => isUnansweredHitbox ? onReplaceTopic(shortTitle) : onToggleInversion(shortTitle),
|
|
325
340
|
style: { cursor: "pointer" }
|
|
326
341
|
}
|
|
327
342
|
);
|
|
328
|
-
})
|
|
329
|
-
pointsArr.map(([cx, cy], i) => {
|
|
330
|
-
const shortTitle = spokes[i][0];
|
|
331
|
-
const userVal = spokes[i][1];
|
|
332
|
-
if (!userVal || Number(userVal) === 0) return null;
|
|
333
|
-
return /* @__PURE__ */ React.createElement(
|
|
334
|
-
"circle",
|
|
335
|
-
{
|
|
336
|
-
key: `user-sensor-${i}`,
|
|
337
|
-
cx,
|
|
338
|
-
cy,
|
|
339
|
-
r: 14,
|
|
340
|
-
fill: "transparent",
|
|
341
|
-
stroke: "none",
|
|
342
|
-
style: { cursor: "default" },
|
|
343
|
-
onMouseEnter: () => setTooltip({ x: cx, y: cy, text: getStanceText(shortTitle, userVal) }),
|
|
344
|
-
onMouseLeave: () => setTooltip(null)
|
|
345
|
-
}
|
|
346
|
-
);
|
|
347
|
-
}),
|
|
348
|
-
hasCompareData && compareCoords && compareCoords.map(([cx, cy], i) => {
|
|
349
|
-
const shortTitle = spokes[i][0];
|
|
350
|
-
const compareVal = compareData[shortTitle];
|
|
351
|
-
if (!compareVal || Number(compareVal) === 0) return null;
|
|
352
|
-
return /* @__PURE__ */ React.createElement(
|
|
353
|
-
"circle",
|
|
354
|
-
{
|
|
355
|
-
key: `compare-sensor-${i}`,
|
|
356
|
-
cx,
|
|
357
|
-
cy,
|
|
358
|
-
r: 14,
|
|
359
|
-
fill: "transparent",
|
|
360
|
-
stroke: "none",
|
|
361
|
-
style: { cursor: "default" },
|
|
362
|
-
onMouseEnter: () => setTooltip({ x: cx, y: cy, text: getStanceText(shortTitle, compareVal) }),
|
|
363
|
-
onMouseLeave: () => setTooltip(null)
|
|
364
|
-
}
|
|
365
|
-
);
|
|
366
|
-
}),
|
|
367
|
-
tooltip && (() => {
|
|
368
|
-
const tipW = 190;
|
|
369
|
-
const tipH = 70;
|
|
370
|
-
const svgW = size + sidePadding * 2;
|
|
371
|
-
const tx = tooltip.x + sidePadding + 12 > svgW - tipW ? tooltip.x - tipW - 12 : tooltip.x + 12;
|
|
372
|
-
const ty = tooltip.y - 10;
|
|
373
|
-
return /* @__PURE__ */ React.createElement(
|
|
374
|
-
"foreignObject",
|
|
375
|
-
{
|
|
376
|
-
x: tx,
|
|
377
|
-
y: ty,
|
|
378
|
-
width: tipW,
|
|
379
|
-
height: tipH,
|
|
380
|
-
style: { overflow: "visible", pointerEvents: "none" }
|
|
381
|
-
},
|
|
382
|
-
/* @__PURE__ */ React.createElement(
|
|
383
|
-
"div",
|
|
384
|
-
{
|
|
385
|
-
xmlns: "http://www.w3.org/1999/xhtml",
|
|
386
|
-
style: {
|
|
387
|
-
background: darkMode ? "#2F3237" : "#ffffff",
|
|
388
|
-
border: darkMode ? "1px solid #59B0C4" : "1px solid #d1d5db",
|
|
389
|
-
borderRadius: 8,
|
|
390
|
-
padding: "7px 11px",
|
|
391
|
-
fontSize: 12,
|
|
392
|
-
lineHeight: 1.4,
|
|
393
|
-
color: darkMode ? "#EBEDEF" : "#111",
|
|
394
|
-
boxShadow: darkMode ? "0 2px 12px rgba(0,0,0,0.4)" : "0 2px 8px rgba(0,0,0,0.12)",
|
|
395
|
-
width: tipW + "px",
|
|
396
|
-
wordWrap: "break-word"
|
|
397
|
-
}
|
|
398
|
-
},
|
|
399
|
-
tooltip.text
|
|
400
|
-
)
|
|
401
|
-
);
|
|
402
|
-
})()
|
|
343
|
+
})
|
|
403
344
|
);
|
|
404
345
|
}
|
|
346
|
+
function adaptiveFontSize(longestLineLen, baseFSize) {
|
|
347
|
+
return Math.max(baseFSize, 10);
|
|
348
|
+
}
|
|
405
349
|
function wrapLabel(label, maxChars = 12) {
|
|
406
350
|
const str = String(label);
|
|
407
351
|
let rawWords;
|
|
@@ -435,7 +379,7 @@ function wrapLabel(label, maxChars = 12) {
|
|
|
435
379
|
}
|
|
436
380
|
|
|
437
381
|
// src/Header.jsx
|
|
438
|
-
import React2, { useState as useState3, useRef as useRef2, useEffect as
|
|
382
|
+
import React2, { useState as useState3, useRef as useRef2, useEffect as useEffect4 } from "react";
|
|
439
383
|
|
|
440
384
|
// src/tokens.js
|
|
441
385
|
var colorScales = {
|
|
@@ -831,13 +775,13 @@ var breakpoints = {
|
|
|
831
775
|
};
|
|
832
776
|
|
|
833
777
|
// src/useMediaQuery.js
|
|
834
|
-
import { useState as useState2, useEffect as
|
|
778
|
+
import { useState as useState2, useEffect as useEffect3 } from "react";
|
|
835
779
|
function useMediaQuery(query) {
|
|
836
780
|
const [matches, setMatches] = useState2(() => {
|
|
837
781
|
if (typeof window === "undefined") return false;
|
|
838
782
|
return window.matchMedia(query).matches;
|
|
839
783
|
});
|
|
840
|
-
|
|
784
|
+
useEffect3(() => {
|
|
841
785
|
if (typeof window === "undefined") return;
|
|
842
786
|
const mql = window.matchMedia(query);
|
|
843
787
|
const handler = (e) => setMatches(e.matches);
|
|
@@ -849,16 +793,6 @@ function useMediaQuery(query) {
|
|
|
849
793
|
}
|
|
850
794
|
|
|
851
795
|
// src/Header.jsx
|
|
852
|
-
var dark = {
|
|
853
|
-
bg: "#1C1F23",
|
|
854
|
-
bgElevated: "#2F3237",
|
|
855
|
-
bgHover: "#41454E",
|
|
856
|
-
border: "#41454E",
|
|
857
|
-
link: "#59B0C4",
|
|
858
|
-
text: "#D3D7DE",
|
|
859
|
-
textMuted: "#9CA3AF",
|
|
860
|
-
shadow: "0 10px 40px rgba(0,0,0,0.4)"
|
|
861
|
-
};
|
|
862
796
|
function Header({
|
|
863
797
|
logoSrc,
|
|
864
798
|
logoAlt = "Empowered Vote",
|
|
@@ -869,7 +803,6 @@ function Header({
|
|
|
869
803
|
currentPath,
|
|
870
804
|
onNavigate,
|
|
871
805
|
profileMenu,
|
|
872
|
-
darkMode = false,
|
|
873
806
|
style = {}
|
|
874
807
|
}) {
|
|
875
808
|
const [mobileMenuOpen, setMobileMenuOpen] = useState3(false);
|
|
@@ -877,7 +810,7 @@ function Header({
|
|
|
877
810
|
const [profileOpen, setProfileOpen] = useState3(false);
|
|
878
811
|
const isMobile = useMediaQuery("(max-width: 768px)");
|
|
879
812
|
const profileRef = useRef2(null);
|
|
880
|
-
|
|
813
|
+
useEffect4(() => {
|
|
881
814
|
if (!profileOpen) return;
|
|
882
815
|
const handleClickOutside = (e) => {
|
|
883
816
|
if (profileRef.current && !profileRef.current.contains(e.target)) {
|
|
@@ -893,16 +826,9 @@ function Header({
|
|
|
893
826
|
onNavigate(href);
|
|
894
827
|
}
|
|
895
828
|
};
|
|
896
|
-
const linkColor = darkMode ? dark.link : colors.evTeal;
|
|
897
|
-
const dropdownBg = darkMode ? dark.bgElevated : colors.bgWhite;
|
|
898
|
-
const dropdownShadow = darkMode ? dark.shadow : "0 10px 40px rgba(0,0,0,0.1)";
|
|
899
|
-
const hoverBg = darkMode ? dark.bgHover : colors.bgLight;
|
|
900
|
-
const borderColor = darkMode ? dark.border : colors.borderLight;
|
|
901
|
-
const mobileShadow = darkMode ? "0 4px 20px rgba(0,0,0,0.4)" : "0 4px 20px rgba(0,0,0,0.1)";
|
|
902
829
|
const styles2 = {
|
|
903
830
|
header: {
|
|
904
|
-
backgroundColor:
|
|
905
|
-
borderBottom: darkMode ? `1px solid ${dark.border}` : "none",
|
|
831
|
+
backgroundColor: colors.bgWhite,
|
|
906
832
|
position: "sticky",
|
|
907
833
|
top: 0,
|
|
908
834
|
zIndex: 50,
|
|
@@ -930,7 +856,7 @@ function Header({
|
|
|
930
856
|
fontFamily: fonts.primary,
|
|
931
857
|
fontWeight: fontWeights.bold,
|
|
932
858
|
fontSize: fontSizes.xl,
|
|
933
|
-
color:
|
|
859
|
+
color: colors.evTeal,
|
|
934
860
|
textDecoration: "none",
|
|
935
861
|
cursor: "pointer",
|
|
936
862
|
display: "flex",
|
|
@@ -953,12 +879,11 @@ function Header({
|
|
|
953
879
|
zIndex: 100
|
|
954
880
|
},
|
|
955
881
|
dropdownInner: {
|
|
956
|
-
backgroundColor:
|
|
882
|
+
backgroundColor: colors.bgWhite,
|
|
957
883
|
borderRadius: borderRadius.lg,
|
|
958
|
-
boxShadow:
|
|
884
|
+
boxShadow: "0 10px 40px rgba(0,0,0,0.1)",
|
|
959
885
|
minWidth: "200px",
|
|
960
|
-
padding: spacing[2]
|
|
961
|
-
border: darkMode ? `1px solid ${dark.border}` : "none"
|
|
886
|
+
padding: spacing[2]
|
|
962
887
|
},
|
|
963
888
|
dropdownItem: {
|
|
964
889
|
display: "block",
|
|
@@ -966,7 +891,7 @@ function Header({
|
|
|
966
891
|
fontFamily: fonts.primary,
|
|
967
892
|
fontWeight: fontWeights.medium,
|
|
968
893
|
fontSize: fontSizes.base,
|
|
969
|
-
color:
|
|
894
|
+
color: colors.evTeal,
|
|
970
895
|
textDecoration: "none",
|
|
971
896
|
borderRadius: borderRadius.md,
|
|
972
897
|
cursor: "pointer"
|
|
@@ -988,13 +913,13 @@ function Header({
|
|
|
988
913
|
display: "inline-flex",
|
|
989
914
|
alignItems: "center",
|
|
990
915
|
backgroundColor: "transparent",
|
|
991
|
-
color:
|
|
916
|
+
color: colors.evTeal,
|
|
992
917
|
fontFamily: fonts.primary,
|
|
993
918
|
fontWeight: fontWeights.bold,
|
|
994
919
|
fontSize: fontSizes.base,
|
|
995
920
|
padding: `${spacing[2]} ${spacing[5]}`,
|
|
996
921
|
borderRadius: borderRadius.full || "999px",
|
|
997
|
-
border: `1.5px solid ${
|
|
922
|
+
border: `1.5px solid ${colors.evTeal}`,
|
|
998
923
|
cursor: "pointer",
|
|
999
924
|
textDecoration: "none",
|
|
1000
925
|
lineHeight: 1.2,
|
|
@@ -1010,7 +935,7 @@ function Header({
|
|
|
1010
935
|
hamburger: {
|
|
1011
936
|
width: "24px",
|
|
1012
937
|
height: "2px",
|
|
1013
|
-
backgroundColor:
|
|
938
|
+
backgroundColor: colors.evTeal,
|
|
1014
939
|
position: "relative"
|
|
1015
940
|
},
|
|
1016
941
|
mobileMenu: {
|
|
@@ -1020,26 +945,25 @@ function Header({
|
|
|
1020
945
|
top: "100%",
|
|
1021
946
|
left: 0,
|
|
1022
947
|
right: 0,
|
|
1023
|
-
backgroundColor:
|
|
948
|
+
backgroundColor: colors.bgWhite,
|
|
1024
949
|
padding: spacing[4],
|
|
1025
|
-
boxShadow:
|
|
1026
|
-
borderTop: darkMode ? `1px solid ${dark.border}` : "none"
|
|
950
|
+
boxShadow: "0 4px 20px rgba(0,0,0,0.1)"
|
|
1027
951
|
},
|
|
1028
952
|
mobileNavItem: {
|
|
1029
953
|
fontFamily: fonts.primary,
|
|
1030
954
|
fontWeight: fontWeights.bold,
|
|
1031
955
|
fontSize: fontSizes.lg,
|
|
1032
|
-
color:
|
|
956
|
+
color: colors.evTeal,
|
|
1033
957
|
textDecoration: "none",
|
|
1034
958
|
padding: `${spacing[3]} 0`,
|
|
1035
|
-
borderBottom: `1px solid ${
|
|
959
|
+
borderBottom: `1px solid ${colors.borderLight}`
|
|
1036
960
|
},
|
|
1037
961
|
profileButton: {
|
|
1038
962
|
width: "40px",
|
|
1039
963
|
height: "40px",
|
|
1040
964
|
borderRadius: borderRadius.full,
|
|
1041
|
-
border: `2px solid ${
|
|
1042
|
-
backgroundColor:
|
|
965
|
+
border: `2px solid ${colors.evTeal}`,
|
|
966
|
+
backgroundColor: colors.bgLight,
|
|
1043
967
|
cursor: "pointer",
|
|
1044
968
|
display: "flex",
|
|
1045
969
|
alignItems: "center",
|
|
@@ -1054,12 +978,11 @@ function Header({
|
|
|
1054
978
|
zIndex: 100
|
|
1055
979
|
},
|
|
1056
980
|
profileDropdownInner: {
|
|
1057
|
-
backgroundColor:
|
|
981
|
+
backgroundColor: colors.bgWhite,
|
|
1058
982
|
borderRadius: borderRadius.lg,
|
|
1059
|
-
boxShadow:
|
|
983
|
+
boxShadow: "0 10px 40px rgba(0,0,0,0.1)",
|
|
1060
984
|
minWidth: "160px",
|
|
1061
|
-
padding: spacing[2]
|
|
1062
|
-
border: darkMode ? `1px solid ${dark.border}` : "none"
|
|
985
|
+
padding: spacing[2]
|
|
1063
986
|
},
|
|
1064
987
|
profileDropdownItem: {
|
|
1065
988
|
display: "block",
|
|
@@ -1068,7 +991,7 @@ function Header({
|
|
|
1068
991
|
fontFamily: fonts.primary,
|
|
1069
992
|
fontWeight: fontWeights.medium,
|
|
1070
993
|
fontSize: fontSizes.base,
|
|
1071
|
-
color:
|
|
994
|
+
color: colors.evTeal,
|
|
1072
995
|
textDecoration: "none",
|
|
1073
996
|
borderRadius: borderRadius.md,
|
|
1074
997
|
cursor: "pointer",
|
|
@@ -1078,7 +1001,7 @@ function Header({
|
|
|
1078
1001
|
},
|
|
1079
1002
|
mobileDivider: {
|
|
1080
1003
|
height: "1px",
|
|
1081
|
-
backgroundColor:
|
|
1004
|
+
backgroundColor: colors.borderLight,
|
|
1082
1005
|
margin: `${spacing[3]} 0`
|
|
1083
1006
|
},
|
|
1084
1007
|
profileLabel: {
|
|
@@ -1086,8 +1009,8 @@ function Header({
|
|
|
1086
1009
|
fontFamily: fonts.primary,
|
|
1087
1010
|
fontWeight: fontWeights.semibold,
|
|
1088
1011
|
fontSize: fontSizes.sm,
|
|
1089
|
-
color:
|
|
1090
|
-
borderBottom: `1px solid ${
|
|
1012
|
+
color: colors.textMuted,
|
|
1013
|
+
borderBottom: `1px solid ${colors.borderLight}`,
|
|
1091
1014
|
marginBottom: spacing[1],
|
|
1092
1015
|
overflow: "hidden",
|
|
1093
1016
|
textOverflow: "ellipsis",
|
|
@@ -1128,7 +1051,7 @@ function Header({
|
|
|
1128
1051
|
"path",
|
|
1129
1052
|
{
|
|
1130
1053
|
d: "M1 1L8 8L15 1",
|
|
1131
|
-
stroke:
|
|
1054
|
+
stroke: colors.evTeal,
|
|
1132
1055
|
strokeWidth: "2",
|
|
1133
1056
|
strokeLinecap: "round",
|
|
1134
1057
|
strokeLinejoin: "round"
|
|
@@ -1144,7 +1067,7 @@ function Header({
|
|
|
1144
1067
|
onClick: (e) => handleNavClick(e, dropdownItem.href),
|
|
1145
1068
|
style: styles2.dropdownItem,
|
|
1146
1069
|
onMouseEnter: (e) => {
|
|
1147
|
-
e.target.style.backgroundColor =
|
|
1070
|
+
e.target.style.backgroundColor = colors.bgLight;
|
|
1148
1071
|
},
|
|
1149
1072
|
onMouseLeave: (e) => {
|
|
1150
1073
|
e.target.style.backgroundColor = "transparent";
|
|
@@ -1168,7 +1091,7 @@ function Header({
|
|
|
1168
1091
|
fontFamily: fonts.primary,
|
|
1169
1092
|
fontWeight: fontWeights.bold,
|
|
1170
1093
|
fontSize: fontSizes["2xl"],
|
|
1171
|
-
color:
|
|
1094
|
+
color: colors.evTeal
|
|
1172
1095
|
}
|
|
1173
1096
|
},
|
|
1174
1097
|
"empowered.vote"
|
|
@@ -1187,12 +1110,12 @@ function Header({
|
|
|
1187
1110
|
style: styles2.secondaryAction,
|
|
1188
1111
|
className: "ev-header-secondary",
|
|
1189
1112
|
onMouseEnter: (e) => {
|
|
1190
|
-
e.currentTarget.style.backgroundColor =
|
|
1113
|
+
e.currentTarget.style.backgroundColor = colors.evTeal;
|
|
1191
1114
|
e.currentTarget.style.color = colors.textWhite;
|
|
1192
1115
|
},
|
|
1193
1116
|
onMouseLeave: (e) => {
|
|
1194
1117
|
e.currentTarget.style.backgroundColor = "transparent";
|
|
1195
|
-
e.currentTarget.style.color =
|
|
1118
|
+
e.currentTarget.style.color = colors.evTeal;
|
|
1196
1119
|
}
|
|
1197
1120
|
},
|
|
1198
1121
|
secondaryAction.label
|
|
@@ -1226,7 +1149,7 @@ function Header({
|
|
|
1226
1149
|
height: "20",
|
|
1227
1150
|
viewBox: "0 0 24 24",
|
|
1228
1151
|
fill: "none",
|
|
1229
|
-
stroke:
|
|
1152
|
+
stroke: colors.evTeal,
|
|
1230
1153
|
strokeWidth: "2",
|
|
1231
1154
|
strokeLinecap: "round",
|
|
1232
1155
|
strokeLinejoin: "round"
|
|
@@ -1250,7 +1173,7 @@ function Header({
|
|
|
1250
1173
|
},
|
|
1251
1174
|
style: styles2.profileDropdownItem,
|
|
1252
1175
|
onMouseEnter: (e) => {
|
|
1253
|
-
e.target.style.backgroundColor =
|
|
1176
|
+
e.target.style.backgroundColor = colors.bgLight;
|
|
1254
1177
|
},
|
|
1255
1178
|
onMouseLeave: (e) => {
|
|
1256
1179
|
e.target.style.backgroundColor = "transparent";
|
|
@@ -1270,7 +1193,7 @@ function Header({
|
|
|
1270
1193
|
},
|
|
1271
1194
|
style: styles2.profileDropdownItem,
|
|
1272
1195
|
onMouseEnter: (e) => {
|
|
1273
|
-
e.target.style.backgroundColor =
|
|
1196
|
+
e.target.style.backgroundColor = colors.bgLight;
|
|
1274
1197
|
},
|
|
1275
1198
|
onMouseLeave: (e) => {
|
|
1276
1199
|
e.target.style.backgroundColor = "transparent";
|
|
@@ -1296,7 +1219,7 @@ function Header({
|
|
|
1296
1219
|
left: 0,
|
|
1297
1220
|
width: "100%",
|
|
1298
1221
|
height: "2px",
|
|
1299
|
-
backgroundColor:
|
|
1222
|
+
backgroundColor: colors.evTeal
|
|
1300
1223
|
}
|
|
1301
1224
|
}
|
|
1302
1225
|
), /* @__PURE__ */ React2.createElement(
|
|
@@ -1308,7 +1231,7 @@ function Header({
|
|
|
1308
1231
|
left: 0,
|
|
1309
1232
|
width: "100%",
|
|
1310
1233
|
height: "2px",
|
|
1311
|
-
backgroundColor:
|
|
1234
|
+
backgroundColor: colors.evTeal
|
|
1312
1235
|
}
|
|
1313
1236
|
}
|
|
1314
1237
|
))
|
|
@@ -1561,7 +1484,6 @@ function SiteHeader({
|
|
|
1561
1484
|
profileMenu,
|
|
1562
1485
|
secondaryAction,
|
|
1563
1486
|
feedbackFeature,
|
|
1564
|
-
darkMode = false,
|
|
1565
1487
|
style = {}
|
|
1566
1488
|
}) {
|
|
1567
1489
|
const handleNavigate = (href) => {
|
|
@@ -1590,7 +1512,6 @@ function SiteHeader({
|
|
|
1590
1512
|
currentPath,
|
|
1591
1513
|
onNavigate: handleNavigate,
|
|
1592
1514
|
profileMenu,
|
|
1593
|
-
darkMode,
|
|
1594
1515
|
style
|
|
1595
1516
|
}
|
|
1596
1517
|
);
|
|
@@ -1800,7 +1721,7 @@ function FilterSidebar({
|
|
|
1800
1721
|
}
|
|
1801
1722
|
|
|
1802
1723
|
// src/PoliticianCard.jsx
|
|
1803
|
-
import React6, { useState as useState4, useEffect as
|
|
1724
|
+
import React6, { useState as useState4, useEffect as useEffect5 } from "react";
|
|
1804
1725
|
function PoliticianCard({
|
|
1805
1726
|
id,
|
|
1806
1727
|
imageSrc,
|
|
@@ -1996,7 +1917,7 @@ function PoliticianCard({
|
|
|
1996
1917
|
)
|
|
1997
1918
|
);
|
|
1998
1919
|
const [imgError, setImgError] = useState4(false);
|
|
1999
|
-
|
|
1920
|
+
useEffect5(() => {
|
|
2000
1921
|
setImgError(false);
|
|
2001
1922
|
}, [imageSrc]);
|
|
2002
1923
|
const getInitials = (n) => {
|
|
@@ -2062,7 +1983,7 @@ function PoliticianCard({
|
|
|
2062
1983
|
}
|
|
2063
1984
|
|
|
2064
1985
|
// src/CompassCardHorizontal.jsx
|
|
2065
|
-
import React11, { useState as useState6, useEffect as
|
|
1986
|
+
import React11, { useState as useState6, useEffect as useEffect6 } from "react";
|
|
2066
1987
|
|
|
2067
1988
|
// src/PlaceholderRadar.jsx
|
|
2068
1989
|
import React7 from "react";
|
|
@@ -2428,7 +2349,7 @@ function CompassCardHorizontal({
|
|
|
2428
2349
|
const [focused, setFocused] = useState6(false);
|
|
2429
2350
|
const [imgError, setImgError] = useState6(false);
|
|
2430
2351
|
const [emptyCtaHovered, setEmptyCtaHovered] = useState6(false);
|
|
2431
|
-
|
|
2352
|
+
useEffect6(() => {
|
|
2432
2353
|
setImgError(false);
|
|
2433
2354
|
}, [politician == null ? void 0 : politician.photo_origin_url]);
|
|
2434
2355
|
const cardStyle = {
|
|
@@ -2677,7 +2598,7 @@ function CompassCardHorizontal({
|
|
|
2677
2598
|
}
|
|
2678
2599
|
|
|
2679
2600
|
// src/CompassCardVertical.jsx
|
|
2680
|
-
import React12, { useState as useState7, useEffect as
|
|
2601
|
+
import React12, { useState as useState7, useEffect as useEffect7 } from "react";
|
|
2681
2602
|
var RADAR_SIZE2 = 400;
|
|
2682
2603
|
function CompassCardVertical({
|
|
2683
2604
|
politician,
|
|
@@ -2695,7 +2616,7 @@ function CompassCardVertical({
|
|
|
2695
2616
|
const [focused, setFocused] = useState7(false);
|
|
2696
2617
|
const [imgError, setImgError] = useState7(false);
|
|
2697
2618
|
const [emptyCtaHovered, setEmptyCtaHovered] = useState7(false);
|
|
2698
|
-
|
|
2619
|
+
useEffect7(() => {
|
|
2699
2620
|
setImgError(false);
|
|
2700
2621
|
}, [politician == null ? void 0 : politician.photo_origin_url]);
|
|
2701
2622
|
const cardStyle = {
|
|
@@ -3029,7 +2950,7 @@ function CompassCardVertical({
|
|
|
3029
2950
|
}
|
|
3030
2951
|
|
|
3031
2952
|
// src/CompassKey.jsx
|
|
3032
|
-
import React13, { useEffect as
|
|
2953
|
+
import React13, { useEffect as useEffect8, useState as useState8 } from "react";
|
|
3033
2954
|
|
|
3034
2955
|
// src/evContext.js
|
|
3035
2956
|
var DEFAULT_BROKER_URL = "https://ev-context.empowered.vote/";
|
|
@@ -3276,7 +3197,7 @@ var evContext = {
|
|
|
3276
3197
|
function CompassKey({ compact = false } = {}) {
|
|
3277
3198
|
const [dismissed, setDismissed] = useState8(false);
|
|
3278
3199
|
const [loaded, setLoaded] = useState8(false);
|
|
3279
|
-
|
|
3200
|
+
useEffect8(() => {
|
|
3280
3201
|
let cancelled = false;
|
|
3281
3202
|
evContext.get().then((shared) => {
|
|
3282
3203
|
if (cancelled) return;
|
|
@@ -3937,8 +3858,7 @@ function CommitteeTable({
|
|
|
3937
3858
|
committees = [],
|
|
3938
3859
|
title = "Committee Memberships",
|
|
3939
3860
|
maxVisible = 6,
|
|
3940
|
-
style = {}
|
|
3941
|
-
darkMode = false
|
|
3861
|
+
style = {}
|
|
3942
3862
|
}) {
|
|
3943
3863
|
const [showAll, setShowAll] = useState11(false);
|
|
3944
3864
|
if (committees.length === 0) {
|
|
@@ -3958,7 +3878,7 @@ function CommitteeTable({
|
|
|
3958
3878
|
fontFamily: fonts.primary,
|
|
3959
3879
|
fontWeight: fontWeights.semibold,
|
|
3960
3880
|
fontSize: "18px",
|
|
3961
|
-
color:
|
|
3881
|
+
color: "#101828",
|
|
3962
3882
|
margin: 0,
|
|
3963
3883
|
marginBottom: spacing[3]
|
|
3964
3884
|
},
|
|
@@ -3967,7 +3887,7 @@ function CommitteeTable({
|
|
|
3967
3887
|
borderCollapse: "collapse"
|
|
3968
3888
|
},
|
|
3969
3889
|
row: {
|
|
3970
|
-
borderBottom:
|
|
3890
|
+
borderBottom: "1px solid #F3F4F6"
|
|
3971
3891
|
},
|
|
3972
3892
|
cell: {
|
|
3973
3893
|
padding: `${spacing[2]} 0`,
|
|
@@ -3976,16 +3896,16 @@ function CommitteeTable({
|
|
|
3976
3896
|
verticalAlign: "top"
|
|
3977
3897
|
},
|
|
3978
3898
|
nameCell: {
|
|
3979
|
-
color:
|
|
3899
|
+
color: "#364153",
|
|
3980
3900
|
paddingRight: spacing[4]
|
|
3981
3901
|
},
|
|
3982
3902
|
positionCell: {
|
|
3983
|
-
color:
|
|
3903
|
+
color: "#6A7282",
|
|
3984
3904
|
textAlign: "right",
|
|
3985
3905
|
whiteSpace: "nowrap"
|
|
3986
3906
|
},
|
|
3987
3907
|
link: {
|
|
3988
|
-
color:
|
|
3908
|
+
color: "#364153",
|
|
3989
3909
|
textDecoration: "none"
|
|
3990
3910
|
},
|
|
3991
3911
|
toggle: {
|
|
@@ -3995,7 +3915,7 @@ function CommitteeTable({
|
|
|
3995
3915
|
fontFamily: fonts.primary,
|
|
3996
3916
|
fontSize: "14px",
|
|
3997
3917
|
fontWeight: fontWeights.medium,
|
|
3998
|
-
color:
|
|
3918
|
+
color: "#6A7282",
|
|
3999
3919
|
background: "none",
|
|
4000
3920
|
border: "none",
|
|
4001
3921
|
padding: `${spacing[2]} 0`,
|
|
@@ -4044,7 +3964,7 @@ function CommitteeTable({
|
|
|
4044
3964
|
}
|
|
4045
3965
|
|
|
4046
3966
|
// src/PoliticianProfile.jsx
|
|
4047
|
-
import React22, { useState as useState12, useEffect as
|
|
3967
|
+
import React22, { useState as useState12, useEffect as useEffect9 } from "react";
|
|
4048
3968
|
|
|
4049
3969
|
// src/LegislativeInlineSummary.jsx
|
|
4050
3970
|
import React20 from "react";
|
|
@@ -4329,10 +4249,12 @@ function stripRetain(s) {
|
|
|
4329
4249
|
return (s || "").replace(/\s*\(Retain\s+.+?\?\)/, "");
|
|
4330
4250
|
}
|
|
4331
4251
|
function qualifyLocalTitle(baseTitle, pol) {
|
|
4332
|
-
if (!
|
|
4252
|
+
if (!baseTitle) return baseTitle;
|
|
4333
4253
|
const dt = pol.district_type || "";
|
|
4334
4254
|
if (!dt.startsWith("LOCAL") && dt !== "COUNTY") return baseTitle;
|
|
4335
|
-
const
|
|
4255
|
+
const govRaw = pol.government_name || pol.representing_city || "";
|
|
4256
|
+
if (!govRaw) return baseTitle;
|
|
4257
|
+
const gov = govRaw.split(",")[0].trim();
|
|
4336
4258
|
const govCore = gov.replace(/^City of\s+/i, "").replace(/\s+County$/i, "").trim();
|
|
4337
4259
|
if (govCore && baseTitle.toLowerCase().includes(govCore.toLowerCase())) return baseTitle;
|
|
4338
4260
|
let prefix = dt === "COUNTY" ? gov : gov.replace(/^City of\s+/i, "");
|
|
@@ -4499,7 +4421,7 @@ function PoliticianProfile({
|
|
|
4499
4421
|
})();
|
|
4500
4422
|
const initials = [pol.first_name, pol.last_name].filter(Boolean).map((n) => n[0]).join("");
|
|
4501
4423
|
const [imgError, setImgError] = useState12(false);
|
|
4502
|
-
|
|
4424
|
+
useEffect9(() => {
|
|
4503
4425
|
setImgError(false);
|
|
4504
4426
|
}, [profileImageUrl]);
|
|
4505
4427
|
const getSocialHandle = (type) => {
|
|
@@ -4891,7 +4813,7 @@ function PoliticianProfile({
|
|
|
4891
4813
|
darkMode,
|
|
4892
4814
|
style: { marginTop: allWebsites.length > 0 ? "8px" : 0 }
|
|
4893
4815
|
}
|
|
4894
|
-
)))), committees.length > 0 && /* @__PURE__ */ React22.createElement(React22.Fragment, null, /* @__PURE__ */ React22.createElement("div", { style: divider }), /* @__PURE__ */ React22.createElement(CommitteeTable, { committees
|
|
4816
|
+
)))), committees.length > 0 && /* @__PURE__ */ React22.createElement(React22.Fragment, null, /* @__PURE__ */ React22.createElement("div", { style: divider }), /* @__PURE__ */ React22.createElement(CommitteeTable, { committees })), pol.is_judicial ? /* @__PURE__ */ React22.createElement(
|
|
4895
4817
|
JudicialScorecard,
|
|
4896
4818
|
{
|
|
4897
4819
|
judicialRecord,
|
|
@@ -6019,7 +5941,7 @@ function computeTierCoverage(topics) {
|
|
|
6019
5941
|
}
|
|
6020
5942
|
|
|
6021
5943
|
// src/useEvContextPromotion.js
|
|
6022
|
-
import { useEffect as
|
|
5944
|
+
import { useEffect as useEffect10, useState as useState16, useCallback, useRef as useRef3 } from "react";
|
|
6023
5945
|
function useEvContextPromotion({
|
|
6024
5946
|
domain,
|
|
6025
5947
|
isLoggedIn,
|
|
@@ -6083,10 +6005,10 @@ function useEvContextPromotion({
|
|
|
6083
6005
|
setPayload(null);
|
|
6084
6006
|
}
|
|
6085
6007
|
}, []);
|
|
6086
|
-
|
|
6008
|
+
useEffect10(() => {
|
|
6087
6009
|
detect();
|
|
6088
6010
|
}, [detect, isLoggedIn, userId, apiData, enabled, domain]);
|
|
6089
|
-
|
|
6011
|
+
useEffect10(() => {
|
|
6090
6012
|
const unsub = evContext.subscribe(() => {
|
|
6091
6013
|
detect();
|
|
6092
6014
|
});
|
|
@@ -6178,7 +6100,7 @@ function isGuestPopulated(domain, fullEvContext) {
|
|
|
6178
6100
|
}
|
|
6179
6101
|
|
|
6180
6102
|
// src/StanceAccordion.jsx
|
|
6181
|
-
import React30, { useState as useState17, useRef as useRef4, useCallback as useCallback2, useEffect as
|
|
6103
|
+
import React30, { useState as useState17, useRef as useRef4, useCallback as useCallback2, useEffect as useEffect11 } from "react";
|
|
6182
6104
|
|
|
6183
6105
|
// src/Favicon.jsx
|
|
6184
6106
|
import React29 from "react";
|
|
@@ -6319,7 +6241,7 @@ function StanceAccordion({
|
|
|
6319
6241
|
},
|
|
6320
6242
|
[expandedTopicId, politicianId, apiUrl]
|
|
6321
6243
|
);
|
|
6322
|
-
|
|
6244
|
+
useEffect11(() => {
|
|
6323
6245
|
if (initialExpandedTopicId && topics && topics.length > 0) {
|
|
6324
6246
|
handleToggle(String(initialExpandedTopicId));
|
|
6325
6247
|
}
|