Skip to content

Commit

Permalink
c/ page linky headings, CountySelect
Browse files Browse the repository at this point in the history
  • Loading branch information
ryan-williams committed Mar 11, 2024
1 parent e070fd0 commit 3cefb61
Show file tree
Hide file tree
Showing 11 changed files with 143 additions and 89 deletions.
9 changes: 7 additions & 2 deletions www/pages/c/[county].tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { getUrls, Urls } from "@/src/urls";
import RegionPage from "@/src/region-page";
import { loadProps } from "@/server/njsp/plot";
import * as Njsp from "@/src/njsp/plot";
import useSetCounty from "@/src/use-set-county";

export type Params = {
county: string
Expand All @@ -31,15 +32,19 @@ export const getStaticProps: GetStaticProps<Props, Params> = async ({ params })
const cn = normalize(countyParam)
const cc = County2Code[cn]
const { mc2mn } = cc2mc2mn[cc]
const barProps = await loadProps({ county: denormalize(cn) })
const county = denormalize(cn)
const barProps = await loadProps({ county })
return { props: { urls, cc, cn, mc2mn, barProps } }
}

export default function CountyPage({ urls, cc, cn, mc2mn, barProps, }: Props) {
const setCounty = useSetCounty("/c")
const { county } = barProps
return <RegionPage
urls={urls}
cc={cc} cn={cn} mc2mn={mc2mn}
barProps={barProps}
title={`${denormalize(cn)} County`}
title={`${county} County`}
setCounty={setCounty}
/>
}
3 changes: 3 additions & 0 deletions www/pages/c/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { getUrls } from "@/src/urls";
import { loadProps } from "@/server/njsp/plot";
import RegionPage, { Props as RegionProps } from "@/src/region-page";
import { cc2mc2mn } from "@/server/county";
import useSetCounty from "@/src/use-set-county";

export type Props = Omit<RegionProps, 'title'>

Expand All @@ -13,12 +14,14 @@ export const getStaticProps: GetStaticProps<Props> = async () => {
}

export default function StatePage({ urls, barProps, cc2mc2mn, }: Props) {
const setCounty = useSetCounty("/c")
return (
<RegionPage
urls={urls}
barProps={barProps}
cc2mc2mn={cc2mc2mn}
title={`NJ`}
setCounty={setCounty}
/>
)
}
1 change: 1 addition & 0 deletions www/pages/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ const Home = ({ plotsDict, njspProps }: Props) => {
{...njspProps}
county={county}
setCounty={setCounty}
includeMoreInfoLink={true}
/>
<hr/>
</div>
Expand Down
File renamed without changes.
57 changes: 57 additions & 0 deletions www/src/county-select.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import React, { useEffect, useRef } from "react";
import css from "@/src/county-select.module.scss";

export const getTextWidth = (text: string, font: string) => {
// Create a temporary span element
const span = document.createElement('span');
document.body.appendChild(span);

// Set the same font styling as your select options
span.style.font = font;
span.style.position = 'absolute'; // Position off-screen
span.style.height = 'auto';
span.style.width = 'auto';
span.style.whiteSpace = 'nowrap';
span.textContent = text;

// Measure the width
const width = Math.ceil(span.getBoundingClientRect().width);

document.body.removeChild(span);

return width;
};

export function CountySelect({ region, setRegion, counties }: {
region: string
setRegion: (region: string) => void
counties: string[]
}) {
const selectRef = useRef<HTMLSelectElement>(null)
useEffect(() => {
if (selectRef.current) {
const { fontSize, fontWeight, fontFamily } = window.getComputedStyle(selectRef.current)
const font = `${fontWeight} ${fontSize} ${fontFamily}`
// console.log("width font:", font, region)
const text = region === "NJ" ? "NJ" : `${region} County`
const textWidth = getTextWidth(text, font)
// console.log("setting width:", textWidth)
selectRef.current.style.width = `${textWidth + 30}px` // Add some padding
}
}, [ region ])
return (
<select
className={css.countySelect}
ref={selectRef}
value={region}
onChange={e => {
const select = e.target
const region = select.value
setRegion(region)
}}
>
<option value={"NJ"}>NJ</option>
{counties.map(cn => <option key={cn} value={cn}>{cn} County</option>)}
</select>
)
}
10 changes: 3 additions & 7 deletions www/src/njsp/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,15 @@ import { DefaultTitle, NjspPlot } from "@/src/njsp/plot";
import { url } from "@/src/site";
import Footer from "@/src/footer";
import * as Njsp from "@/src/njsp/plot";
import useSetCounty from "@/src/use-set-county";

export type Props = {
barProps: Njsp.Props
county: string | null
}

export default function NjspPlotPage({ barProps, county, }: Props) {
const router = useRouter()
const setCounty = useCallback(
(county: string | null) => {
router.push(`/njsp/${county ? normalize(county) : ""}`)
},
[router]
)
const setCounty = useSetCounty("/njsp")
return (
<div className={css.container}>
<Head
Expand All @@ -36,6 +31,7 @@ export default function NjspPlotPage({ barProps, county, }: Props) {
Heading={'h1'}
county={county}
setCounty={setCounty}
includeMoreInfoLink={true}
/>
<hr/>
</div>
Expand Down
78 changes: 18 additions & 60 deletions www/src/njsp/plot.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,8 @@ import * as Plotly from "react-plotly.js";
import { TableData, useCsvTable, useTable } from "@/src/tableData";
import { useDb, useQuery, } from "@rdub/duckdb/duckdb";
import { curYear, Data, njspPlotSpec, prvYear, YearTotalsMap } from "@/src/plotSpecs";
import React, { Dispatch, ReactNode, useCallback, useEffect, useRef, useState } from "react";
import css from "./plot.module.scss"
import { table, typeCountsQuery } from "./projections";
import React, { Dispatch, ReactNode, useCallback, useEffect, useState } from "react";
import { HasCounty, table, typeCountsQuery } from "./projections";
import { ProjectedCsv } from "@/src/paths";
import { ytcQuery } from "@/src/njsp/ytc";
import { repoWithOwner } from "@/src/github";
Expand All @@ -15,6 +14,7 @@ import { Plot, PlotSpec } from "@rdub/next-plotly/plot";
import { fromEntries } from "@rdub/base/objs";
import { NjspFatalAcc } from "@/src/urls";
import { normalize } from "../county";
import { CountySelect } from "../county-select";

export type PlotParams = { data: PlotData[] } & Omit<Plotly.PlotParams, "data">
export type Annotation = Partial<Annotations>
Expand Down Expand Up @@ -133,64 +133,20 @@ export const DefaultTitle = "Car Crash Deaths"
export const AllTypes: Type[] = ["Drivers", "Pedestrians", "Cyclists", "Passengers", "Projected"]
export type Type = "Drivers" | "Pedestrians" | "Cyclists" | "Passengers" | "Projected"

const getTextWidth = (text: string, font: string) => {
// Create a temporary span element
const span = document.createElement('span');
document.body.appendChild(span);

// Set the same font styling as your select options
span.style.font = font;
span.style.position = 'absolute'; // Position off-screen
span.style.height = 'auto';
span.style.width = 'auto';
span.style.whiteSpace = 'nowrap';
span.textContent = text;

// Measure the width
const width = Math.ceil(span.getBoundingClientRect().width);

document.body.removeChild(span);

return width;
};
export const estimationHref = `https://nbviewer.org/github/${repoWithOwner}/blob/main/njsp/update-projections.ipynb`

export function CountySelect({ region, setRegion, counties }: {
region: string
setRegion: (region: string) => void
counties: string[]
}) {
const selectRef = useRef<HTMLSelectElement>(null)
useEffect(() => {
if (selectRef.current) {
const { fontSize, fontWeight, fontFamily } = window.getComputedStyle(selectRef.current)
const font = `${fontWeight} ${fontSize} ${fontFamily}`
// console.log("width font:", font, region)
const text = region === "NJ" ? "NJ" : `${region} County`
const textWidth = getTextWidth(text, font)
// console.log("setting width:", textWidth)
selectRef.current.style.width = `${textWidth + 30}px` // Add some padding
}
}, [ region ])
return (
<select
className={css.countySelect}
ref={selectRef}
value={region}
onChange={e => {
const select = e.target
const region = select.value
setRegion(region)
}}
>
<option value={"NJ"}>NJ</option>
{counties.map(cn => <option key={cn} value={cn}>{cn} County</option>)}
</select>
)
export type MoreInfoLink = {
includeMoreInfoLink?: boolean
}

export const estimationHref = `https://nbviewer.org/github/${repoWithOwner}/blob/main/njsp/update-projections.ipynb`

export function NjspChildren({ rundate, yearTotalsMap, county, }: Data & { county: string | null }) {
export function NjspChildren(
{
rundate,
yearTotalsMap,
county,
includeMoreInfoLink,
}: Data & HasCounty & MoreInfoLink
) {
const total2021 = yearTotalsMap["2021"].total
const total2022 = yearTotalsMap["2022"].total
const prvYearTotal = yearTotalsMap[prvYear].total
Expand All @@ -206,7 +162,7 @@ export function NjspChildren({ rundate, yearTotalsMap, county, }: Data & { count
<p>Click/Double-click the legend labels to toggle or solo each type.</p>
<p>
<A href={`${GitHub.href}/commits/main`}>As of {shortDate}</A>, {curYear} has {curYearTotal} reported deaths, and <A href={estimationHref}>is on pace</A> for {curYearProjectedTotal}{curYearProjectedTotal > prvYearTotal ? `, exceeding ${prvYear}'s ${prvYearTotal}` : ""}.
{county ? <>{' '}<A href={`/c/${normalize(county)}`}>More {county} County data</A>.</> : null}
{includeMoreInfoLink ? <>{' '}<A href={`/c/${county ? normalize(county) : ""}`}>More {county ? `${county} County` : "state-wide"} data</A>.</> : null}
</p>
{county === null ? <p>2021 and 2022 were the worst years in the NJSP record (since 2008), with {total2021} and {total2022} deaths, resp.</p> : null}
<p>Data comes from <A title={"NJ State Police fatal crash data"} href={NjspFatalAcc}>NJ State Police</A>, and is updated daily (though crashes sometimes take weeks or months to show up).</p>
Expand All @@ -228,7 +184,8 @@ export function NjspPlot(
Heading = 'h2',
spec,
setCounty,
}: Props & {
includeMoreInfoLink,
}: Props & MoreInfoLink & {
subtitle?: ReactNode
setCounty?: Dispatch<string | null>
Heading?: keyof JSX.IntrinsicElements
Expand Down Expand Up @@ -350,6 +307,7 @@ export function NjspPlot(
rundate={rundate}
yearTotalsMap={yearTotalsMap}
county={county}
includeMoreInfoLink={includeMoreInfoLink}
/>
</Plot>
)
Expand Down
5 changes: 5 additions & 0 deletions www/src/region-page.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -59,3 +59,8 @@
font-style: italic;
}
}

.idTarget {
position: relative;
top: -3.5em;
}
50 changes: 35 additions & 15 deletions www/src/region-page.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useDatePaginationControls, usePaginationControls, useResultDatePagination, useResultPagination } from "@/src/pagination";
import { ReactNode, useState } from "react";
import { Dispatch, ReactNode, useState } from "react";
import { ColTitles, useYearStats, YearStatsDicts, yearStatsRows } from "@/src/use-year-stats";
import { useNjdotCrashRows } from "@/src/use-njdot-crashes";
import css from "./region-page.module.scss";
Expand All @@ -14,6 +14,7 @@ import * as Njsp from "@/src/njsp/plot";
import { NjspPlot } from "@/src/njsp/plot";
import { njspPlotSpec } from "@/src/plotSpecs";
import Footer from "./footer";
import { CountySelect } from "@/src/county-select";

export type Props = {
urls: Urls
Expand All @@ -23,13 +24,21 @@ export type Props = {
mc2mn?: MC2MN
cc2mc2mn?: CC2MC2MN
barProps?: Njsp.Props
setCounty?: Dispatch<string | null>
title: string
subtitle?: ReactNode
}

export const DOTStart = "2001-01-01"
export const DOTEnd = "2021-12-31"

export function H2({ id, children }: { id: string, children: ReactNode }) {
return <h2>
<span id={id} className={css.idTarget}/>
<A href={`#${id}`}>{children}</A>
</h2>
}

export default function RegionPage(
{
urls,
Expand All @@ -38,6 +47,7 @@ export default function RegionPage(
cc2mc2mn,
title, subtitle,
barProps,
setCounty,
}: Props
) {
const [ requestChunkSize, setRequestChunkSize ] = useState<number>(64 * 1024)
Expand All @@ -64,27 +74,37 @@ export default function RegionPage(
)

// NJSP plot
const plotTitle = `Deaths per year, by type`
const plotTitle = `Car crash deaths`
const county = barProps?.county ?? null

return (
<div className={css.body}>
<div className={css.container}>
<h1 className={css.title}>{title}</h1>
<h1 className={css.title}>{
barProps && setCounty
? <CountySelect
region={county ?? "NJ"}
setRegion={region => setCounty(region === "NJ" ? null : region)}
counties={barProps.counties}
/>
: title
}</h1>
{subtitle && <div className={css.subtitle}>{subtitle}</div>}
{
barProps &&
<div className={css.njspPlot}>
<NjspPlot
{...barProps}
heading={<h2>{plotTitle}</h2>}
title={plotTitle}
spec={njspPlotSpec}
/>
</div>
barProps
? <div className={css.njspPlot}>
<NjspPlot
{...barProps}
Heading={"h1"}
heading={<H2 id={"by-type"}>{plotTitle}</H2>}
spec={njspPlotSpec}
/>
</div>
: null
}
{
njspCrashes && <div className={css.section}>
<h2>Fatal crashes</h2>
<H2 id={"recent"}>Recent fatal crashes</H2>
<div className={css.sectionSubtitle}>2008 – present</div>
<ResultTable
className={css.crashesTable}
Expand All @@ -96,7 +116,7 @@ export default function RegionPage(
}
{
njdotCrashes && <div className={css.section}>
<h2>Fatal / Injury crash details</h2>
<H2 id={"dot"}>Fatal / Injury crash details</H2>
<div className={css.sectionSubtitle}>2001-2021</div>
<ResultTable
className={css.crashesTable}
Expand All @@ -108,7 +128,7 @@ export default function RegionPage(
}
{
yearStatsResult && <div className={css.section}>
<h2>Annual stats</h2>
<H2 id={"stats"}>Annual stats</H2>
<div className={css.sectionSubtitle}>2001-2021</div>
<ResultTable
className={`${css.crashesTable} ${css.withTotals}`}
Expand Down
Loading

0 comments on commit 3cefb61

Please sign in to comment.