Compare commits
3 Commits
main
...
fivethirty
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e6a8ba114e | ||
|
|
d49abb1161 | ||
|
|
fe1850fdba |
23
examples/fiverthirtyeight-example/components/Breadcrumbs.tsx
Normal file
23
examples/fiverthirtyeight-example/components/Breadcrumbs.tsx
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import Link from "next/link";
|
||||||
|
|
||||||
|
function HomeIcon({ className = "" }) {
|
||||||
|
return <div className={`inline-block w-4 ${className}`}><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"> <path d="M 12 2 A 1 1 0 0 0 11.289062 2.296875 L 1.203125 11.097656 A 0.5 0.5 0 0 0 1 11.5 A 0.5 0.5 0 0 0 1.5 12 L 4 12 L 4 20 C 4 20.552 4.448 21 5 21 L 9 21 C 9.552 21 10 20.552 10 20 L 10 14 L 14 14 L 14 20 C 14 20.552 14.448 21 15 21 L 19 21 C 19.552 21 20 20.552 20 20 L 20 12 L 22.5 12 A 0.5 0.5 0 0 0 23 11.5 A 0.5 0.5 0 0 0 22.796875 11.097656 L 12.716797 2.3027344 A 1 1 0 0 0 12.710938 2.296875 A 1 1 0 0 0 12 2 z"/></svg></div>
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function Breadcrumbs({ links }: { links: { title: string, href?: string, target?: string }[] }) {
|
||||||
|
const current = links.at(-1);
|
||||||
|
|
||||||
|
return <div className="flex items-center uppercase font-black text-xs">
|
||||||
|
<Link className="flex items-center" href='/'><HomeIcon /></Link>
|
||||||
|
|
||||||
|
{/* {links.length > 1 && links.slice(0, -1).map((link) => {
|
||||||
|
return <>
|
||||||
|
<span className="mx-4">/</span>
|
||||||
|
<Link href={link.href}>{link.title}</Link>
|
||||||
|
</>
|
||||||
|
})} */}
|
||||||
|
|
||||||
|
<span className="mx-4">/</span>
|
||||||
|
<span>{current?.title}</span>
|
||||||
|
</div >
|
||||||
|
}
|
||||||
@ -211,18 +211,6 @@
|
|||||||
"https://projects.fivethirtyeight.com/nfl-api/nfl_elo_latest.csv"
|
"https://projects.fivethirtyeight.com/nfl-api/nfl_elo_latest.csv"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"url": "https://github.com/fivethirtyeight/checking-our-work-data",
|
|
||||||
"name": "checking-our-work-data",
|
|
||||||
"displayName": "checking-our-work-<span class=\"lastword\">data</span>",
|
|
||||||
"articles": [
|
|
||||||
{
|
|
||||||
"date": "2023-02-02T16:30:00.000Z",
|
|
||||||
"title": "How Good Are FiveThirtyEight Forecasts?",
|
|
||||||
"url": "https://projects.fivethirtyeight.com/checking-our-work/"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"url": "https://github.com/fivethirtyeight/data/tree/master/world-cup-2022",
|
"url": "https://github.com/fivethirtyeight/data/tree/master/world-cup-2022",
|
||||||
"name": "world-cup-2022",
|
"name": "world-cup-2022",
|
||||||
@ -239,18 +227,6 @@
|
|||||||
"https://projects.fivethirtyeight.com/soccer-api/international/2022/wc_forecasts.csv"
|
"https://projects.fivethirtyeight.com/soccer-api/international/2022/wc_forecasts.csv"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"url": "https://github.com/fivethirtyeight/covid-19-polls",
|
|
||||||
"name": "covid-19-polls",
|
|
||||||
"displayName": "covid-19-<span class=\"lastword\">polls</span>",
|
|
||||||
"articles": [
|
|
||||||
{
|
|
||||||
"date": "2022-11-29T20:20:08.000Z",
|
|
||||||
"title": "How Americans View Biden’s Response To The Coronavirus Crisis",
|
|
||||||
"url": "https://projects.fivethirtyeight.com/coronavirus-polls/"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"url": "https://github.com/fivethirtyeight/data/tree/master/election-deniers",
|
"url": "https://github.com/fivethirtyeight/data/tree/master/election-deniers",
|
||||||
"name": "election-deniers",
|
"name": "election-deniers",
|
||||||
@ -371,18 +347,6 @@
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"url": "https://github.com/fivethirtyeight/nfl-elo-game",
|
|
||||||
"name": "nfl-elo-game",
|
|
||||||
"displayName": "nfl-elo-<span class=\"lastword\">game</span>",
|
|
||||||
"articles": [
|
|
||||||
{
|
|
||||||
"date": "2022-02-14T03:03:26.000Z",
|
|
||||||
"title": "Can You Beat FiveThirtyEight’s NFL Forecasts?",
|
|
||||||
"url": "https://projects.fivethirtyeight.com/2021-nfl-forecasting-game/"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"url": "https://github.com/fivethirtyeight/data/tree/master/redlining",
|
"url": "https://github.com/fivethirtyeight/data/tree/master/redlining",
|
||||||
"name": "redlining",
|
"name": "redlining",
|
||||||
@ -407,42 +371,6 @@
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"url": "https://github.com/fivethirtyeight/negro-leagues-player-ratings",
|
|
||||||
"name": "negro-leagues-player-ratings",
|
|
||||||
"displayName": "negro-leagues-player-<span class=\"lastword\">ratings</span>",
|
|
||||||
"articles": [
|
|
||||||
{
|
|
||||||
"date": "2021-02-25T11:00:00.000Z",
|
|
||||||
"title": "The Negro League Stars That MLB Kept Out — And Is Finally Recognizing",
|
|
||||||
"url": "https://projects.fivethirtyeight.com/negro-leagues-mlb/"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"url": "https://github.com/fivethirtyeight/police-settlements",
|
|
||||||
"name": "police-settlements",
|
|
||||||
"displayName": "police-<span class=\"lastword\">settlements</span>",
|
|
||||||
"articles": [
|
|
||||||
{
|
|
||||||
"date": "2021-02-22T11:00:49.000Z",
|
|
||||||
"title": "Cities Spend Millions On Police Misconduct Every Year. Here’s Why It’s So Difficult to Hold Departments Accountable.",
|
|
||||||
"url": "https://fivethirtyeight.com/features/police-misconduct-costs-cities-millions-every-year-but-thats-where-the-accountability-ends/"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"url": "https://github.com/fivethirtyeight/superbowl-ads",
|
|
||||||
"name": "superbowl-ads",
|
|
||||||
"displayName": "superbowl-<span class=\"lastword\">ads</span>",
|
|
||||||
"articles": [
|
|
||||||
{
|
|
||||||
"date": "2021-02-04T16:11:00.000Z",
|
|
||||||
"title": "According To Super Bowl Ads, Americans Love America, Animals And Sex",
|
|
||||||
"url": "https://projects.fivethirtyeight.com/super-bowl-ads/"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"url": "https://github.com/fivethirtyeight/data/tree/master/trump-approval-ratings",
|
"url": "https://github.com/fivethirtyeight/data/tree/master/trump-approval-ratings",
|
||||||
"name": "trump-approval-ratings",
|
"name": "trump-approval-ratings",
|
||||||
@ -475,18 +403,6 @@
|
|||||||
"https://projects.fivethirtyeight.com/congress-tracker-data/csv/vote_predictions.csv"
|
"https://projects.fivethirtyeight.com/congress-tracker-data/csv/vote_predictions.csv"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"url": "https://github.com/fivethirtyeight/election-results",
|
|
||||||
"name": "election-results",
|
|
||||||
"displayName": "election-<span class=\"lastword\">results</span>",
|
|
||||||
"articles": [
|
|
||||||
{
|
|
||||||
"date": "2020-11-03T05:33:43.000Z",
|
|
||||||
"title": "2020 Election Forecast",
|
|
||||||
"url": "https://projects.fivethirtyeight.com/2020-election-forecast/"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"url": "https://github.com/fivethirtyeight/data/tree/master/election-forecasts-2020",
|
"url": "https://github.com/fivethirtyeight/data/tree/master/election-forecasts-2020",
|
||||||
"name": "election-forecasts-2020",
|
"name": "election-forecasts-2020",
|
||||||
@ -557,18 +473,6 @@
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"url": "https://github.com/fivethirtyeight/WNBA-stats",
|
|
||||||
"name": "WNBA-stats",
|
|
||||||
"displayName": "WNBA-<span class=\"lastword\">stats</span>",
|
|
||||||
"articles": [
|
|
||||||
{
|
|
||||||
"date": "2020-05-27T19:06:43.000Z",
|
|
||||||
"title": "It’s Time To Give Basketball’s Other GOAT Her Due",
|
|
||||||
"url": "https://fivethirtyeight.com/features/its-time-to-give-basketballs-other-goat-her-due/"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"url": "https://github.com/fivethirtyeight/data/tree/master/covid-geography",
|
"url": "https://github.com/fivethirtyeight/data/tree/master/covid-geography",
|
||||||
"name": "covid-geography",
|
"name": "covid-geography",
|
||||||
@ -608,18 +512,6 @@
|
|||||||
"https://projects.fivethirtyeight.com/endorsements-2020-data/endorsements-2020.csv"
|
"https://projects.fivethirtyeight.com/endorsements-2020-data/endorsements-2020.csv"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"url": "https://github.com/fivethirtyeight/nba-player-advanced-metrics",
|
|
||||||
"name": "nba-player-advanced-metrics",
|
|
||||||
"displayName": "nba-player-advanced-<span class=\"lastword\">metrics</span>",
|
|
||||||
"articles": [
|
|
||||||
{
|
|
||||||
"date": "2020-03-09T15:27:37.000Z",
|
|
||||||
"title": "Luka Dončić And The Mavs Are Pushing The Limits Of Offensive Efficiency",
|
|
||||||
"url": "https://fivethirtyeight.com/features/luka-doncic-and-the-mavs-are-pushing-the-limits-of-offensive-efficiency/"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"url": "https://github.com/fivethirtyeight/data/tree/master/impeachment-polls",
|
"url": "https://github.com/fivethirtyeight/data/tree/master/impeachment-polls",
|
||||||
"name": "impeachment-polls",
|
"name": "impeachment-polls",
|
||||||
@ -711,18 +603,6 @@
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"url": "https://github.com/fivethirtyeight/candidate-emails",
|
|
||||||
"name": "candidate-emails",
|
|
||||||
"displayName": "candidate-<span class=\"lastword\">emails</span>",
|
|
||||||
"articles": [
|
|
||||||
{
|
|
||||||
"date": "2019-07-11T15:33:15.000Z",
|
|
||||||
"title": "What Our Inbox Tells Us About How Democrats Are Tackling Trump",
|
|
||||||
"url": "https://fivethirtyeight.com/features/which-democrats-are-campaigning-on-trump/"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"url": "https://github.com/fivethirtyeight/data/tree/master/nba-draymond",
|
"url": "https://github.com/fivethirtyeight/data/tree/master/nba-draymond",
|
||||||
"name": "nba-draymond",
|
"name": "nba-draymond",
|
||||||
@ -763,18 +643,6 @@
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"url": "https://github.com/fivethirtyeight/twitter-overlap",
|
|
||||||
"name": "twitter-overlap",
|
|
||||||
"displayName": "twitter-<span class=\"lastword\">overlap</span>",
|
|
||||||
"articles": [
|
|
||||||
{
|
|
||||||
"date": "2019-06-12T15:24:07.000Z",
|
|
||||||
"title": "Which 2020 Candidates Have The Most In Common … On Twitter?",
|
|
||||||
"url": "https://fivethirtyeight.com/features/which-2020-candidates-have-the-most-in-common-on-twitter/"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"url": "https://github.com/fivethirtyeight/data/tree/master/trump-lawsuits",
|
"url": "https://github.com/fivethirtyeight/data/tree/master/trump-lawsuits",
|
||||||
"name": "trump-lawsuits",
|
"name": "trump-lawsuits",
|
||||||
@ -957,18 +825,6 @@
|
|||||||
"https://projects.fivethirtyeight.com/congress-model-2018/governor_state_forecast.csv"
|
"https://projects.fivethirtyeight.com/congress-model-2018/governor_state_forecast.csv"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"url": "https://github.com/fivethirtyeight/actblue-analysis",
|
|
||||||
"name": "actblue-analysis",
|
|
||||||
"displayName": "actblue-<span class=\"lastword\">analysis</span>",
|
|
||||||
"articles": [
|
|
||||||
{
|
|
||||||
"date": "2018-10-25T17:31:42.000Z",
|
|
||||||
"title": "How ActBlue Is Trying To Turn Small Donations Into A Blue Wave",
|
|
||||||
"url": "https://fivethirtyeight.com/features/how-actblue-is-trying-to-turn-small-donations-into-a-blue-wave"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"url": "https://github.com/fivethirtyeight/data/tree/master/primary-candidates-2018",
|
"url": "https://github.com/fivethirtyeight/data/tree/master/primary-candidates-2018",
|
||||||
"name": "primary-candidates-2018",
|
"name": "primary-candidates-2018",
|
||||||
@ -1179,18 +1035,6 @@
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"url": "https://github.com/fivethirtyeight/redistricting-atlas-data",
|
|
||||||
"name": "redistricting-atlas-data",
|
|
||||||
"displayName": "redistricting-atlas-<span class=\"lastword\">data</span>",
|
|
||||||
"articles": [
|
|
||||||
{
|
|
||||||
"date": "2018-01-25T11:00:00.000Z",
|
|
||||||
"title": "The Atlas Of Redistricting",
|
|
||||||
"url": "https://projects.fivethirtyeight.com/redistricting-maps/"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"url": "https://github.com/fivethirtyeight/data/tree/master/next-bechdel",
|
"url": "https://github.com/fivethirtyeight/data/tree/master/next-bechdel",
|
||||||
"name": "next-bechdel",
|
"name": "next-bechdel",
|
||||||
@ -1620,37 +1464,6 @@
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"url": "https://github.com/fivethirtyeight/uber-tlc-foil-response",
|
|
||||||
"name": "uber-tlc-foil-response",
|
|
||||||
"displayName": "uber-tlc-foil-<span class=\"lastword\">response</span>",
|
|
||||||
"articles": [
|
|
||||||
{
|
|
||||||
"date": "2015-12-09T16:19:40.000Z",
|
|
||||||
"title": "Is Uber Making NYC Rush-Hour Traffic Worse?",
|
|
||||||
"rowspan": 4,
|
|
||||||
"url": "https://fivethirtyeight.com/features/is-uber-making-nyc-rush-hour-traffic-worse/"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"date": "2015-10-13T20:44:12.000Z",
|
|
||||||
"title": "Uber Is Taking Millions Of Manhattan Rides Away From Taxis",
|
|
||||||
"rowspan": 0,
|
|
||||||
"url": "https://fivethirtyeight.com/features/uber-is-taking-millions-of-manhattan-rides-away-from-taxis/"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"date": "2015-08-28T10:30:36.000Z",
|
|
||||||
"title": "Public Transit Should Be Uber’s New Best Friend",
|
|
||||||
"rowspan": 0,
|
|
||||||
"url": "https://fivethirtyeight.com/features/public-transit-should-be-ubers-new-best-friend/"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"date": "2015-08-10T18:06:17.000Z",
|
|
||||||
"title": "Uber Is Serving New York’s Outer Boroughs More Than Taxis Are",
|
|
||||||
"rowspan": 0,
|
|
||||||
"url": "https://fivethirtyeight.com/features/uber-is-serving-new-yorks-outer-boroughs-more-than-taxis-are/"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"url": "https://github.com/fivethirtyeight/data/tree/master/tarantino",
|
"url": "https://github.com/fivethirtyeight/data/tree/master/tarantino",
|
||||||
"name": "tarantino",
|
"name": "tarantino",
|
||||||
@ -2169,18 +1982,6 @@
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"url": "https://github.com/fivethirtyeight/data/tree/master/comma-survey-data",
|
|
||||||
"name": "comma-survey-data",
|
|
||||||
"displayName": "comma-survey-<span class=\"lastword\">data</span>",
|
|
||||||
"articles": [
|
|
||||||
{
|
|
||||||
"date": "2014-06-17T16:28:55.000Z",
|
|
||||||
"title": "Elitist, Superfluous, Or Popular? We Polled Americans on the Oxford Comma",
|
|
||||||
"url": "http://fivethirtyeight.com/features/elitist-superfluous-or-popular-we-polled-americans-on-the-oxford-comma/"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"url": "https://github.com/fivethirtyeight/data/tree/master/world-cup-predictions",
|
"url": "https://github.com/fivethirtyeight/data/tree/master/world-cup-predictions",
|
||||||
"name": "world-cup-predictions",
|
"name": "world-cup-predictions",
|
||||||
|
|||||||
38
examples/fiverthirtyeight-example/lib/octokit.ts
Normal file
38
examples/fiverthirtyeight-example/lib/octokit.ts
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
import { Octokit } from 'octokit';
|
||||||
|
|
||||||
|
export interface GithubProject {
|
||||||
|
owner: string;
|
||||||
|
repo: string;
|
||||||
|
branch: string;
|
||||||
|
files: string[];
|
||||||
|
readme: string;
|
||||||
|
description?: string;
|
||||||
|
name?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getProjectReadme(
|
||||||
|
owner: string,
|
||||||
|
repo: string,
|
||||||
|
branch: string,
|
||||||
|
readme: string,
|
||||||
|
github_pat?: string
|
||||||
|
) {
|
||||||
|
const octokit = new Octokit({ auth: github_pat });
|
||||||
|
try {
|
||||||
|
const response = await octokit.rest.repos.getContent({
|
||||||
|
owner,
|
||||||
|
repo,
|
||||||
|
path: readme,
|
||||||
|
ref: branch,
|
||||||
|
});
|
||||||
|
const data = response.data as { content?: string };
|
||||||
|
const fileContent = data.content ? data.content : '';
|
||||||
|
if (fileContent === '') {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const decodedContent = Buffer.from(fileContent, 'base64').toString();
|
||||||
|
return decodedContent;
|
||||||
|
} catch (error) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,6 +1,9 @@
|
|||||||
/** @type {import('next').NextConfig} */
|
/** @type {import('next').NextConfig} */
|
||||||
const nextConfig = {
|
const nextConfig = {
|
||||||
reactStrictMode: true,
|
reactStrictMode: true,
|
||||||
|
serverRuntimeConfig: {
|
||||||
|
github_pat: process.env.GITHUB_PAT ? process.env.GITHUB_PAT : null,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = nextConfig
|
module.exports = nextConfig
|
||||||
|
|||||||
2590
examples/fiverthirtyeight-example/package-lock.json
generated
2590
examples/fiverthirtyeight-example/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -9,6 +9,8 @@
|
|||||||
"lint": "next lint"
|
"lint": "next lint"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@portaljs/components": "^0.1.0",
|
||||||
|
"@tailwindcss/typography": "^0.5.9",
|
||||||
"@types/node": "20.1.1",
|
"@types/node": "20.1.1",
|
||||||
"@types/react": "18.2.6",
|
"@types/react": "18.2.6",
|
||||||
"@types/react-dom": "18.2.4",
|
"@types/react-dom": "18.2.4",
|
||||||
@ -16,9 +18,17 @@
|
|||||||
"eslint": "8.40.0",
|
"eslint": "8.40.0",
|
||||||
"eslint-config-next": "13.4.1",
|
"eslint-config-next": "13.4.1",
|
||||||
"next": "13.4.1",
|
"next": "13.4.1",
|
||||||
|
"next-mdx-remote": "^4.4.1",
|
||||||
|
"next-seo": "^6.0.0",
|
||||||
|
"octokit": "^2.0.14",
|
||||||
"postcss": "8.4.23",
|
"postcss": "8.4.23",
|
||||||
"react": "18.2.0",
|
"react": "18.2.0",
|
||||||
"react-dom": "18.2.0",
|
"react-dom": "18.2.0",
|
||||||
|
"react-markdown": "^8.0.7",
|
||||||
|
"remark-code-frontmatter": "^1.0.0",
|
||||||
|
"remark-extract-frontmatter": "^3.2.0",
|
||||||
|
"remark-frontmatter": "^4.0.1",
|
||||||
|
"remark-gfm": "^3.0.1",
|
||||||
"tailwindcss": "3.3.2",
|
"tailwindcss": "3.3.2",
|
||||||
"timeago.js": "^4.0.2",
|
"timeago.js": "^4.0.2",
|
||||||
"typescript": "5.0.4"
|
"typescript": "5.0.4"
|
||||||
|
|||||||
@ -1,4 +1,6 @@
|
|||||||
import '@/styles/globals.css'
|
import '@/styles/globals.css'
|
||||||
|
import '@portaljs/components/styles.css'
|
||||||
|
|
||||||
import type { AppProps } from 'next/app'
|
import type { AppProps } from 'next/app'
|
||||||
|
|
||||||
export default function App({ Component, pageProps }: AppProps) {
|
export default function App({ Component, pageProps }: AppProps) {
|
||||||
|
|||||||
@ -11,6 +11,25 @@ export default function Document() {
|
|||||||
/>
|
/>
|
||||||
</Head>
|
</Head>
|
||||||
<body>
|
<body>
|
||||||
|
<header className="max-w-5xl mx-auto mt-8 w-full">
|
||||||
|
<div className="border-b-2 pb-2.5 mx-2 border-zinc-800">
|
||||||
|
<h1>
|
||||||
|
<span className="sr-only">FiveThirtyEight</span>
|
||||||
|
<a
|
||||||
|
className="flex gap-x-2 items-center"
|
||||||
|
href="http://fivethirtyeight.com"
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
width="197"
|
||||||
|
height="25"
|
||||||
|
alt="FiveThirtyEight"
|
||||||
|
src="data:image/svg+xml;base64,PHN2ZyBpZD0iTGF5ZXJfMSIgZGF0YS1uYW1lPSJMYXllciAxIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA0MjEgNTMuNzYiPjxkZWZzPjxzdHlsZT4uY2xzLTF7ZmlsbDojMDEwMTAxO308L3N0eWxlPjwvZGVmcz48dGl0bGU+QXJ0Ym9hcmQgOTU8L3RpdGxlPjxwYXRoIGNsYXNzPSJjbHMtMSIgZD0iTTAgMGgyNXY4SDl2MTBoMTV2OEg5djE3SDBWMHpNMzEgMzZoNVYxOGgtNXYtOGgxM3YyNmg0djdIMzF6bTUtMzZoOHY4aC04ek0xNzkgMzZoNVYxOGgtNXYtOGgxM3YyNmg0djdoLTE3em01LTM2aDh2OGgtOHpNMzE2IDM2aDVWMThoLTV2LThoMTN2MjZoNHY3aC0xN3ptNS0zNmg4djhoLTh6TTU0IDI3VjEwaDh2MTVsNCA5Ljk4aDFMNzEgMjVWMTBoOHYxN2wtNyAxNkg2MWwtNy0xNnpNMTExIDQzSDk3LjQyQzg5LjIzIDQzIDg1IDM5LjE5IDg1IDMxLjE3VjIyYzAtNy41NyA0LjMtMTMgMTMtMTMgOS4zMyAwIDEzIDUuMDcgMTMgMTR2N0g5NHYxLjc0YzAgMi42MiAxIDQuMjYgMy40MiA0LjI2SDExMXpNOTQgMjNoOHYtMS41NWMwLTIuNjItMS4wNi01LjQ1LTQuMTMtNS40NS0yLjc5IDAtMy44NyAyLjItMy44NyA1LjQ1ek0xMjUgOGgtMTBWMGgyOXY4aC0xMHYzNWgtOVY4ek0yMDIgNDNWMTBoOHY0YzEuMTQtMi40NSAzLjc1LTQgNy4yMi00SDIyMHY4aC02Yy0yLjg0IDAtNCAuOTQtNCAzLjlWNDN6TTI0NSA0M2gtNC44NEMyMzMuMDUgNDMgMjMwIDM5LjMxIDIzMCAzMS44NVYxOGgtNnYtOGg2VjNoOHY3aDd2OGgtN2wtLjA3IDEzLjkzYzAgMi4yMi45MyA0LjA3IDMuNjYgNC4wN0gyNDV6TTQyMSA0M2gtNC44NEM0MDkuMDUgNDMgNDA2IDM5LjMxIDQwNiAzMS44NVYxOGgtNnYtOGg2VjNoOHY3aDd2OGgtN2wtLjA3IDEzLjkzYzAgMi4yMi45MyA0LjA3IDMuNjYgNC4wN0g0MjF6TTI1NC4yNiA1My43Nmw0LjYxLTkuNUwyNTEgMjdWMTBoOHYxNWw0IDEwaDFsNC0xMFYxMGg4djE3bC0xMi4zIDI2Ljc2aC05LjQ0ek0yODQgMGgyNXY4aC0xNnY5aDE1djhoLTE1djEwaDE2djhoLTI1VjB6TTMzNyA0OHYtMmgxNi4xYzIgMCAyLjktLjE4IDIuOS0xLjI3di0uMzRjMC0xLjA4LS45MS0xLjM5LTIuOS0xLjM5SDM0MHYtNWw1LTVjLTUuMjktMS40OC04LTUuNDMtOC0xMXYtMWMwLTcuNTYgNC40NC0xMiAxNC0xMmEyMS45MyAyMS45MyAwIDAgMSA1Ljk1IDFMMzYxIDRsNSAzLTQgNmMxLjM3IDEuOTMgMyA0LjkzIDMgOHYxYzAgNy0zLjMgMTAuNjYtMTIgMTFsLTMgNGg2YzUuOTIgMCA5IDIuNjIgOSA3LjY4di4xMWMwIDUuMDYtMi43MSA4LjIxLTguNjIgOC4yMWgtMTNjLTQuMjkgMC02LjM4LTEuODQtNi4zOC01em0xOS0yNXYtM2MwLTMuMy0xLjMzLTQtNS00cy01IC43LTUgNHYzYzAgMy4zIDEuMzkgNCA1IDRzNS0uNyA1LTR6TTM4MCA0M2gtOFYwaDh2MTRjMS4xNC0yLjY3IDMuNC00IDctNCA2LjI2IDAgOSAzLjA4IDkgMTAuNzZWNDNoLThWMjJjMC0zLjEzLTEuMDctNS00LTVzLTQgMS44Ny00IDV6TTE1NyA0M2gtOFYwaDh2MTRjMS4xNC0yLjY3IDMuOTEtNCA3LjQ5LTQgNi4yNiAwIDguNTEgMy4xMyA4LjUxIDEwLjgxVjQzaC04VjIxYzAtMy4xMy0xLjA3LTQuNDQtNC00LjQ0cy00IDIuMjYtNCA1LjM5eiIvPjwvc3ZnPg=="
|
||||||
|
/>{' '}
|
||||||
|
by PortalJS
|
||||||
|
</a>
|
||||||
|
</h1>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
<Main />
|
<Main />
|
||||||
<NextScript />
|
<NextScript />
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
@ -0,0 +1,131 @@
|
|||||||
|
import { NextSeo } from 'next-seo';
|
||||||
|
import { promises as fs } from 'fs';
|
||||||
|
import path from 'path';
|
||||||
|
import getConfig from 'next/config';
|
||||||
|
import { getProjectReadme, GithubProject } from '@/lib/octokit';
|
||||||
|
import remarkGfm from 'remark-gfm';
|
||||||
|
import extract from 'remark-extract-frontmatter';
|
||||||
|
import { Dataset } from '..';
|
||||||
|
import { GetStaticProps } from 'next';
|
||||||
|
import { Table } from '@portaljs/components';
|
||||||
|
import Breadcrumbs from '@/components/Breadcrumbs';
|
||||||
|
import { ReactMarkdown } from 'react-markdown/lib/react-markdown';
|
||||||
|
import remarkFrontmatter from 'remark-frontmatter';
|
||||||
|
|
||||||
|
export default function DatasetPage({
|
||||||
|
dataset,
|
||||||
|
}: {
|
||||||
|
dataset: Dataset & {
|
||||||
|
readme: string | null;
|
||||||
|
};
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<NextSeo title={`${dataset.name} page`} />
|
||||||
|
<main className="max-w-5xl px-2 prose mx-auto my-8 prose-thead:border-b-4 prose-table:max-w-5xl prose-table:overflow-scroll prose-thead:overflow-scroll prose-tbody:overflow-scroll prose-thead:pb-2 prose-thead:border-zinc-900 prose-th:uppercase prose-th:text-left prose-th:font-light prose-th:text-xs">
|
||||||
|
<Breadcrumbs links={[{ title: dataset.name, href: '' }]} />
|
||||||
|
<h1 className="uppercase mb-0 mt-16">{dataset.name}</h1>
|
||||||
|
<p className="mb-8">
|
||||||
|
<span className="font-semibold">Repository:</span>{' '}
|
||||||
|
<a target="_blank" href={dataset.url}>
|
||||||
|
{dataset.url}
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h2 className="mb-0 mt-10">FILES</h2>
|
||||||
|
<div className="inline-block min-w-full py-2 align-middle">
|
||||||
|
<table className="min-w-full divide-y divide-gray-300">
|
||||||
|
<thead className="border-b-4 pb-2 border-zinc-900">
|
||||||
|
<tr>
|
||||||
|
<th
|
||||||
|
className="uppercase text-left font-light text-xs pb-3"
|
||||||
|
scope="col"
|
||||||
|
>
|
||||||
|
Name
|
||||||
|
</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody className="divide-y divide-gray-200">
|
||||||
|
{dataset.files?.map((file) => (
|
||||||
|
<tr key={file}>
|
||||||
|
<td className="whitespace-nowrap text-left py-4 text-sm text-gray-500">
|
||||||
|
<a href={file}>{file.split('/').slice(-1)}</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
))}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
{dataset.files && dataset.files.length > 0 && (
|
||||||
|
<>
|
||||||
|
<h2 className="mb-0 mt-10">DATA PREVIEWS</h2>
|
||||||
|
{dataset.files?.map((file) => (
|
||||||
|
<div key={file} className="preview-table my-8">
|
||||||
|
<h3>{file.split('/').slice(-1)}</h3>
|
||||||
|
<Table url={file} />
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
{dataset.readme && (
|
||||||
|
<>
|
||||||
|
<h2 className="uppercase font-black">Readme</h2>
|
||||||
|
{dataset.readme && (
|
||||||
|
<ReactMarkdown
|
||||||
|
remarkPlugins={[
|
||||||
|
remarkFrontmatter,
|
||||||
|
remarkGfm,
|
||||||
|
[extract, { remove: true }],
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
{dataset.readme}
|
||||||
|
</ReactMarkdown>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</main>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getStaticPaths() {
|
||||||
|
const datasetsFile = path.join(process.cwd(), 'datasets.json');
|
||||||
|
const datasets = await fs.readFile(datasetsFile, 'utf8');
|
||||||
|
|
||||||
|
return {
|
||||||
|
paths: JSON.parse(datasets).map((dataset: Dataset) => {
|
||||||
|
return {
|
||||||
|
params: { datasetName: dataset.name },
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
fallback: false, // can also be true or 'blocking'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getStaticProps: GetStaticProps = async ({ params }) => {
|
||||||
|
const datasetsFile = path.join(process.cwd(), 'datasets.json');
|
||||||
|
const datasetsString = await fs.readFile(datasetsFile, 'utf8');
|
||||||
|
const datasets: Dataset[] = JSON.parse(datasetsString);
|
||||||
|
const dataset: Dataset | undefined = datasets.find(
|
||||||
|
(_dataset) => _dataset.name === params?.datasetName
|
||||||
|
);
|
||||||
|
const github_pat = getConfig().serverRuntimeConfig.github_pat;
|
||||||
|
const readmes = await Promise.all(['/README.md', '/readme.md', '/Readme.md'].map(async (readme) => await getProjectReadme(
|
||||||
|
'fivethirtyeight',
|
||||||
|
'data',
|
||||||
|
'master',
|
||||||
|
dataset?.name + readme,
|
||||||
|
github_pat
|
||||||
|
)));
|
||||||
|
const readme = readmes.find(item => item !== null)
|
||||||
|
if (!readme) console.log('Readme not found for ' + dataset?.name)
|
||||||
|
return {
|
||||||
|
props: {
|
||||||
|
dataset: {
|
||||||
|
...dataset,
|
||||||
|
readme,
|
||||||
|
files: dataset && dataset.files ? dataset.files : null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
@ -1,33 +1,36 @@
|
|||||||
import Image from 'next/image';
|
import Image from 'next/image';
|
||||||
import { Inter } from 'next/font/google';
|
import { Inter } from 'next/font/google';
|
||||||
import { format } from 'timeago.js'
|
import { format } from 'timeago.js';
|
||||||
import { promises as fs } from 'fs';
|
import { promises as fs } from 'fs';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
|
|
||||||
const inter = Inter({ subsets: ['latin'] });
|
const inter = Inter({ subsets: ['latin'] });
|
||||||
|
|
||||||
interface Article {
|
export interface Article {
|
||||||
date: string;
|
date: string;
|
||||||
title: string;
|
title: string;
|
||||||
url: string;
|
url: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Dataset {
|
export interface Dataset {
|
||||||
url: string;
|
url: string;
|
||||||
name: string;
|
name: string;
|
||||||
displayName: string;
|
displayName: string;
|
||||||
articles: Article[];
|
articles: Article[];
|
||||||
|
files?: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export function MobileItem({dataset} : { dataset: Dataset}) {
|
export function MobileItem({ dataset }: { dataset: Dataset }) {
|
||||||
return (
|
return (
|
||||||
<div className="flex gap-x-2 pb-2 py-4 items-center justify-between border-b border-zinc-600">
|
<div className="flex gap-x-2 pb-2 py-4 items-center justify-between border-b border-zinc-600">
|
||||||
<div className="flex flex-col">
|
<div className="flex flex-col">
|
||||||
<span className="font-light">{dataset.name}</span>
|
<span className="font-light">{dataset.name}</span>
|
||||||
{dataset.articles.map((article) => (
|
{dataset.articles.map((article) => (
|
||||||
<div key={article.title} className='py-1 flex flex-col'>
|
<div key={article.title} className="py-1 flex flex-col">
|
||||||
<span className="font-bold hover:underline">{article.title}</span>
|
<span className="font-bold hover:underline">{article.title}</span>
|
||||||
<span className="font-light text-base">{format(article.date)}</span>{' '}
|
<span className="font-light text-base">
|
||||||
|
{format(article.date)}
|
||||||
|
</span>{' '}
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
@ -39,6 +42,12 @@ export function MobileItem({dataset} : { dataset: Dataset}) {
|
|||||||
>
|
>
|
||||||
info
|
info
|
||||||
</a>
|
</a>
|
||||||
|
<a
|
||||||
|
className="ml-2 border border-zinc-900 font-light px-4 py-1 text-sm transition hover:bg-zinc-900 hover:text-white"
|
||||||
|
href={`/datasets/${dataset.name}`}
|
||||||
|
>
|
||||||
|
explore
|
||||||
|
</a>
|
||||||
{/*
|
{/*
|
||||||
<button>
|
<button>
|
||||||
<svg
|
<svg
|
||||||
@ -59,18 +68,25 @@ export function MobileItem({dataset} : { dataset: Dataset}) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function DesktopItem({dataset} : { dataset: Dataset}) {
|
export function DesktopItem({ dataset }: { dataset: Dataset }) {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{dataset.articles.map((article, index) => (
|
{dataset.articles.map((article, index) => (
|
||||||
<tr key={article.url} className={`${index === (dataset.articles.length - 1) ? 'border-b' : ''} border-zinc-400`}>
|
<tr
|
||||||
|
key={article.url}
|
||||||
|
className={`${
|
||||||
|
index === dataset.articles.length - 1 ? 'border-b' : ''
|
||||||
|
} border-zinc-400`}
|
||||||
|
>
|
||||||
<td className="py-8 font-light">{index === 0 ? dataset.name : ''}</td>
|
<td className="py-8 font-light">{index === 0 ? dataset.name : ''}</td>
|
||||||
<td>
|
<td>
|
||||||
<a className="py-8 font-bold hover:underline" href={article.url}>
|
<a className="py-8 font-bold hover:underline" href={article.url}>
|
||||||
{article.title}
|
{article.title}
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
<td className="py-8 font-light text-base min-w-[120px]">{format(article.date)}</td>
|
<td className="py-8 font-light text-base min-w-[120px]">
|
||||||
|
{format(article.date)}
|
||||||
|
</td>
|
||||||
<td className="py-8">
|
<td className="py-8">
|
||||||
{index === 0 && (
|
{index === 0 && (
|
||||||
<a
|
<a
|
||||||
@ -82,6 +98,16 @@ export function DesktopItem({dataset} : { dataset: Dataset}) {
|
|||||||
</a>
|
</a>
|
||||||
)}
|
)}
|
||||||
</td>
|
</td>
|
||||||
|
<td className="py-8">
|
||||||
|
{index === 0 && (
|
||||||
|
<a
|
||||||
|
className="ml-2 border border-zinc-900 font-light px-[25px] py-2.5 text-sm transition hover:bg-zinc-900 hover:text-white"
|
||||||
|
href={`/datasets/${dataset.name}`}
|
||||||
|
>
|
||||||
|
explore
|
||||||
|
</a>
|
||||||
|
)}
|
||||||
|
</td>
|
||||||
{/*
|
{/*
|
||||||
<td>
|
<td>
|
||||||
<button>
|
<button>
|
||||||
@ -106,35 +132,17 @@ export function DesktopItem({dataset} : { dataset: Dataset}) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function getStaticProps() {
|
export async function getStaticProps() {
|
||||||
const jsonDirectory = path.join(
|
const jsonDirectory = path.join(process.cwd(), '/datasets.json');
|
||||||
process.cwd(),
|
|
||||||
'/datasets.json'
|
|
||||||
);
|
|
||||||
const datasetString = await fs.readFile(jsonDirectory, 'utf8');
|
const datasetString = await fs.readFile(jsonDirectory, 'utf8');
|
||||||
const datasets = JSON.parse(datasetString)
|
const datasets = JSON.parse(datasetString);
|
||||||
return {
|
return {
|
||||||
props: { datasets },
|
props: { datasets },
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function Home( { datasets }: { datasets: Dataset[] }) {
|
export default function Home({ datasets }: { datasets: Dataset[] }) {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<header className="max-w-5xl mx-auto mt-8 w-full">
|
|
||||||
<div className="border-b-2 pb-2.5 mx-2 border-zinc-800">
|
|
||||||
<h1>
|
|
||||||
<span className="sr-only">FiveThirtyEight</span>
|
|
||||||
<a className='flex gap-x-2 items-center' href="http://fivethirtyeight.com">
|
|
||||||
<img
|
|
||||||
width="197"
|
|
||||||
height="25"
|
|
||||||
alt="FiveThirtyEight"
|
|
||||||
src="data:image/svg+xml;base64,PHN2ZyBpZD0iTGF5ZXJfMSIgZGF0YS1uYW1lPSJMYXllciAxIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA0MjEgNTMuNzYiPjxkZWZzPjxzdHlsZT4uY2xzLTF7ZmlsbDojMDEwMTAxO308L3N0eWxlPjwvZGVmcz48dGl0bGU+QXJ0Ym9hcmQgOTU8L3RpdGxlPjxwYXRoIGNsYXNzPSJjbHMtMSIgZD0iTTAgMGgyNXY4SDl2MTBoMTV2OEg5djE3SDBWMHpNMzEgMzZoNVYxOGgtNXYtOGgxM3YyNmg0djdIMzF6bTUtMzZoOHY4aC04ek0xNzkgMzZoNVYxOGgtNXYtOGgxM3YyNmg0djdoLTE3em01LTM2aDh2OGgtOHpNMzE2IDM2aDVWMThoLTV2LThoMTN2MjZoNHY3aC0xN3ptNS0zNmg4djhoLTh6TTU0IDI3VjEwaDh2MTVsNCA5Ljk4aDFMNzEgMjVWMTBoOHYxN2wtNyAxNkg2MWwtNy0xNnpNMTExIDQzSDk3LjQyQzg5LjIzIDQzIDg1IDM5LjE5IDg1IDMxLjE3VjIyYzAtNy41NyA0LjMtMTMgMTMtMTMgOS4zMyAwIDEzIDUuMDcgMTMgMTR2N0g5NHYxLjc0YzAgMi42MiAxIDQuMjYgMy40MiA0LjI2SDExMXpNOTQgMjNoOHYtMS41NWMwLTIuNjItMS4wNi01LjQ1LTQuMTMtNS40NS0yLjc5IDAtMy44NyAyLjItMy44NyA1LjQ1ek0xMjUgOGgtMTBWMGgyOXY4aC0xMHYzNWgtOVY4ek0yMDIgNDNWMTBoOHY0YzEuMTQtMi40NSAzLjc1LTQgNy4yMi00SDIyMHY4aC02Yy0yLjg0IDAtNCAuOTQtNCAzLjlWNDN6TTI0NSA0M2gtNC44NEMyMzMuMDUgNDMgMjMwIDM5LjMxIDIzMCAzMS44NVYxOGgtNnYtOGg2VjNoOHY3aDd2OGgtN2wtLjA3IDEzLjkzYzAgMi4yMi45MyA0LjA3IDMuNjYgNC4wN0gyNDV6TTQyMSA0M2gtNC44NEM0MDkuMDUgNDMgNDA2IDM5LjMxIDQwNiAzMS44NVYxOGgtNnYtOGg2VjNoOHY3aDd2OGgtN2wtLjA3IDEzLjkzYzAgMi4yMi45MyA0LjA3IDMuNjYgNC4wN0g0MjF6TTI1NC4yNiA1My43Nmw0LjYxLTkuNUwyNTEgMjdWMTBoOHYxNWw0IDEwaDFsNC0xMFYxMGg4djE3bC0xMi4zIDI2Ljc2aC05LjQ0ek0yODQgMGgyNXY4aC0xNnY5aDE1djhoLTE1djEwaDE2djhoLTI1VjB6TTMzNyA0OHYtMmgxNi4xYzIgMCAyLjktLjE4IDIuOS0xLjI3di0uMzRjMC0xLjA4LS45MS0xLjM5LTIuOS0xLjM5SDM0MHYtNWw1LTVjLTUuMjktMS40OC04LTUuNDMtOC0xMXYtMWMwLTcuNTYgNC40NC0xMiAxNC0xMmEyMS45MyAyMS45MyAwIDAgMSA1Ljk1IDFMMzYxIDRsNSAzLTQgNmMxLjM3IDEuOTMgMyA0LjkzIDMgOHYxYzAgNy0zLjMgMTAuNjYtMTIgMTFsLTMgNGg2YzUuOTIgMCA5IDIuNjIgOSA3LjY4di4xMWMwIDUuMDYtMi43MSA4LjIxLTguNjIgOC4yMWgtMTNjLTQuMjkgMC02LjM4LTEuODQtNi4zOC01em0xOS0yNXYtM2MwLTMuMy0xLjMzLTQtNS00cy01IC43LTUgNHYzYzAgMy4zIDEuMzkgNCA1IDRzNS0uNyA1LTR6TTM4MCA0M2gtOFYwaDh2MTRjMS4xNC0yLjY3IDMuNC00IDctNCA2LjI2IDAgOSAzLjA4IDkgMTAuNzZWNDNoLThWMjJjMC0zLjEzLTEuMDctNS00LTVzLTQgMS44Ny00IDV6TTE1NyA0M2gtOFYwaDh2MTRjMS4xNC0yLjY3IDMuOTEtNCA3LjQ5LTQgNi4yNiAwIDguNTEgMy4xMyA4LjUxIDEwLjgxVjQzaC04VjIxYzAtMy4xMy0xLjA3LTQuNDQtNC00LjQ0cy00IDIuMjYtNCA1LjM5eiIvPjwvc3ZnPg=="
|
|
||||||
/> by PortalJS
|
|
||||||
</a>
|
|
||||||
</h1>
|
|
||||||
</div>
|
|
||||||
</header>
|
|
||||||
<main
|
<main
|
||||||
className={`flex min-h-screen flex-col items-center max-w-5xl mx-auto pt-20 px-2.5 ${inter.className}`}
|
className={`flex min-h-screen flex-col items-center max-w-5xl mx-auto pt-20 px-2.5 ${inter.className}`}
|
||||||
>
|
>
|
||||||
@ -148,7 +156,11 @@ export default function Home( { datasets }: { datasets: Dataset[] }) {
|
|||||||
stories and visualizations of your own.
|
stories and visualizations of your own.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<article className="w-full px-2 md:hidden py-4">{datasets.map(dataset => <MobileItem key={dataset.name} dataset={dataset} />)}</article>
|
<article className="w-full px-2 md:hidden py-4">
|
||||||
|
{datasets.map((dataset) => (
|
||||||
|
<MobileItem key={dataset.name} dataset={dataset} />
|
||||||
|
))}
|
||||||
|
</article>
|
||||||
<table className="w-full mt-10 mb-4 hidden md:table">
|
<table className="w-full mt-10 mb-4 hidden md:table">
|
||||||
<thead className="border-b-4 pb-2 border-zinc-900">
|
<thead className="border-b-4 pb-2 border-zinc-900">
|
||||||
<tr>
|
<tr>
|
||||||
@ -163,7 +175,11 @@ export default function Home( { datasets }: { datasets: Dataset[] }) {
|
|||||||
</th>
|
</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>{datasets.map(dataset => <DesktopItem key={dataset.name} dataset={dataset} />)}</tbody>
|
<tbody>
|
||||||
|
{datasets.map((dataset) => (
|
||||||
|
<DesktopItem key={dataset.name} dataset={dataset} />
|
||||||
|
))}
|
||||||
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
<p className="text-[13px] py-8">
|
<p className="text-[13px] py-8">
|
||||||
Unless otherwise noted, our data sets are available under the{' '}
|
Unless otherwise noted, our data sets are available under the{' '}
|
||||||
|
|||||||
@ -1,3 +1,8 @@
|
|||||||
@tailwind base;
|
@tailwind base;
|
||||||
@tailwind components;
|
@tailwind components;
|
||||||
@tailwind utilities;
|
@tailwind utilities;
|
||||||
|
|
||||||
|
.preview-table > div {
|
||||||
|
overflow-x: scroll;
|
||||||
|
overflow-y: hidden;
|
||||||
|
}
|
||||||
|
|||||||
@ -14,5 +14,5 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
plugins: [],
|
plugins: [require('@tailwindcss/typography')],
|
||||||
}
|
};
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user