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