This commit is contained in:
metacryst
2026-04-28 20:05:00 -05:00
commit 0d6c7683ff
123 changed files with 20922 additions and 0 deletions

View File

@@ -0,0 +1,214 @@
class PoliticsRepresentatives extends Shadow {
constructor(reps, levelFilter) {
super()
this.reps = reps
this.levelFilter = levelFilter
}
get visible() {
if (this.levelFilter === "all") return this.reps
return this.reps.filter(r => r.level === this.levelFilter)
}
get grouped() {
const order = ["federal", "state", "local"]
const map = {}
order.forEach(l => { map[l] = [] })
this.visible.forEach(r => {
if (map[r.level]) map[r.level].push(r)
})
return order.map(l => ({ level: l, reps: map[l] })).filter(g => g.reps.length)
}
render() {
VStack(() => {
// Header
VStack(() => {
HStack(() => {
VStack(() => {
h2("Your Representatives")
.margin(0).fontSize(1.1, em).fontWeight("700").color("var(--headertext)")
p(`${this.visible.length} officials across your districts`)
.margin(0).marginTop(0.2, em).fontSize(0.78, em)
.color("var(--headertext)").opacity(0.42)
})
// Party breakdown pill
HStack(() => {
const counts = { R: 0, D: 0, I: 0 }
this.visible.forEach(r => { counts[r.party] = (counts[r.party] || 0) + 1 })
if (counts.R) this.partyCount(counts.R, "R")
if (counts.D) this.partyCount(counts.D, "D")
if (counts.I) this.partyCount(counts.I, "I")
})
.gap(0.5, em).alignItems("center")
})
.justifyContent("space-between").alignItems("flex-start")
})
.paddingHorizontal(1.75, em).paddingVertical(1.25, em)
.borderBottom("1px solid var(--divider)").flexShrink(0)
// Groups
VStack(() => {
this.grouped.forEach(group => {
VStack(() => {
// Level header
HStack(() => {
HStack(() => {})
.flex(1).height(1, px).background("var(--divider)")
p(this.levelLabel(group.level))
.margin(0).marginHorizontal(0.9, em)
.fontSize(0.68, em).fontWeight("700").letterSpacing("0.08em")
.color("var(--headertext)").opacity(0.35)
.whiteSpace("nowrap")
HStack(() => {})
.flex(1).height(1, px).background("var(--divider)")
})
.alignItems("center").marginBottom(1, em)
// Cards
HStack(() => {
group.reps.forEach(rep => this.renderCard(rep))
})
.gap(0.85, em).flexWrap("wrap")
})
.paddingTop(1.35, em).paddingBottom(0.5, em)
})
})
.paddingHorizontal(1.75, em)
.overflowY("auto").flex(1)
})
.height(100, pct).width(100, pct).overflow("hidden")
}
renderCard(rep) {
const partyColor = this.partyColor(rep.party)
const partyBg = this.partyBg(rep.party)
VStack(() => {
// Party accent bar
VStack(() => {})
.height(4, px).width(100, pct)
.background(partyColor).flexShrink(0)
VStack(() => {
// Avatar + status
ZStack(() => {
// Avatar circle
VStack(() => {
p(rep.initials)
.margin(0).fontSize(1.15, em).fontWeight("800")
.color("white").lineHeight("1")
})
.width(3.8, em).height(3.8, em).borderRadius(50, pct)
.background(partyColor)
.justifyContent("center").alignItems("center")
.flexShrink(0)
// Party badge bottom-right
VStack(() => {
p(rep.party)
.margin(0).fontSize(0.52, em).fontWeight("800")
.color("white").lineHeight("1")
})
.position("absolute").bottom(0).right(0)
.width(1.3, em).height(1.3, em).borderRadius(50, pct)
.background(partyColor)
.boxSizing("border-box")
.justifyContent("center").alignItems("center")
})
.position("relative")
.width(3.8, em).height(3.8, em).marginBottom(0.85, em)
// Name
p(rep.name)
.margin(0).fontSize(0.92, em).fontWeight("700")
.color("var(--headertext)").lineHeight("1.25").textAlign("center")
// Title
p(rep.title)
.margin(0).marginTop(0.22, em).fontSize(0.72, em)
.color("var(--headertext)").opacity(0.55)
.textAlign("center").lineHeight("1.35")
// District chip
if (rep.district) {
p(rep.district)
.margin(0).marginTop(0.55, em)
.paddingHorizontal(0.6, em).paddingVertical(0.18, em)
.background(partyBg).border(`1px solid ${partyColor}33`)
.borderRadius(100, px)
.fontSize(0.68, em).fontWeight("600")
.color(partyColor)
}
// Since
if (rep.since) {
p(`Since ${rep.since}`)
.margin(0).marginTop(0.42, em)
.fontSize(0.68, em).color("var(--headertext)").opacity(0.32)
}
// Contact row
HStack(() => {
if (rep.phone) {
this.contactBtn("📞", rep.phone)
}
if (rep.website) {
this.contactBtn("🌐", rep.website)
}
})
.gap(0.4, em).marginTop(0.75, em)
})
.padding(1.1, em).alignItems("center")
})
.width(200, px).minWidth(180, px)
.background("var(--main)")
.border("1px solid var(--divider)")
.borderRadius(0.65, em)
.overflow("hidden")
.boxSizing("border-box")
.flexShrink(0)
}
contactBtn(icon, value) {
button(icon)
.padding(0.35, em)
.background("var(--darkaccent)")
.border("1px solid var(--divider)")
.borderRadius(0.4, em)
.fontSize(0.8, em)
.cursor("pointer")
.attr({ title: value })
}
partyCount(count, party) {
HStack(() => {
VStack(() => {})
.width(0.5, em).height(0.5, em).borderRadius(50, pct)
.background(this.partyColor(party)).flexShrink(0)
p(`${count} ${party}`)
.margin(0).fontSize(0.75, em).fontWeight("600")
.color(this.partyColor(party))
})
.gap(0.3, em).alignItems("center")
.paddingHorizontal(0.6, em).paddingVertical(0.22, em)
.background(this.partyBg(party))
.border(`1px solid ${this.partyColor(party)}33`)
.borderRadius(100, px)
}
levelLabel(level) {
return { federal: "FEDERAL DELEGATION", state: "STATE DELEGATION", local: "LOCAL OFFICIALS" }[level] || level.toUpperCase()
}
partyColor(party) {
return { R: "#ef4444", D: "#3b82f6", I: "#8b5cf6", L: "#f59e0b", G: "#22c55e" }[party] || "#6b7280"
}
partyBg(party) {
return { R: "rgba(239,68,68,0.08)", D: "rgba(59,130,246,0.08)", I: "rgba(139,92,246,0.08)", L: "rgba(245,158,11,0.08)", G: "rgba(34,197,94,0.08)" }[party] || "var(--darkaccent)"
}
}
register(PoliticsRepresentatives)