215 lines
8.2 KiB
JavaScript
215 lines
8.2 KiB
JavaScript
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)
|