Compare commits
64 Commits
basic-exam
...
fivethirty
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c85934d17a | ||
|
|
6705bc1e2d | ||
|
|
7dfde0935e | ||
|
|
3f76bea895 | ||
|
|
f17efce02e | ||
|
|
61b96c20ed | ||
|
|
4cadc50e46 | ||
|
|
684f473e62 | ||
|
|
b963cf2cbb | ||
|
|
43ac5cfb47 | ||
|
|
f6b8ef2190 | ||
|
|
e5c89308d1 | ||
|
|
8b51123290 | ||
|
|
53b64b81c9 | ||
|
|
9fe08fcd1b | ||
|
|
7150150db0 | ||
|
|
5cc312b55b | ||
|
|
5c8431bf39 | ||
|
|
45c07f829a | ||
|
|
53ea7957c0 | ||
|
|
0c65a145c8 | ||
|
|
91caeff6c3 | ||
|
|
0f65e253da | ||
|
|
c390a21611 | ||
|
|
dac7d03d05 | ||
|
|
89ba260b70 | ||
|
|
ce847746d2 | ||
|
|
5328492575 | ||
|
|
e52e789314 | ||
|
|
0e8cac7d50 | ||
|
|
2e30c76a3d | ||
|
|
edb2354945 | ||
|
|
5834a4a470 | ||
|
|
90b93e6819 | ||
|
|
ad52721a38 | ||
|
|
cf2a93abfd | ||
|
|
8afb30c96b | ||
|
|
94a3c2a5f0 | ||
|
|
a0620f9255 | ||
|
|
e5513f59a6 | ||
|
|
1782f23b84 | ||
|
|
72405162a1 | ||
|
|
982733737d | ||
|
|
ea5802a908 | ||
|
|
229a7b5324 | ||
|
|
014c4c043d | ||
|
|
ed3a26cd6d | ||
|
|
026059184a | ||
|
|
a041d69282 | ||
|
|
016f3e20e9 | ||
|
|
169a92d313 | ||
|
|
14abd5b768 | ||
|
|
4aaabba229 | ||
|
|
cc43597130 | ||
|
|
d9a6ea4ef1 | ||
|
|
f6b94ee254 | ||
|
|
04b05c0896 | ||
|
|
5b4d2d1990 | ||
|
|
b7e2e8e6b8 | ||
|
|
b6100546e3 | ||
|
|
58ca032d3f | ||
|
|
4b5329a93e | ||
|
|
298b59d291 | ||
|
|
41e7f8ad8d |
4
.gitignore
vendored
@@ -44,3 +44,7 @@ Thumbs.db
|
||||
# Env
|
||||
.env
|
||||
**/.env
|
||||
|
||||
# MarkdownDB
|
||||
*.db
|
||||
**/*.db
|
||||
|
||||
35
examples/alan-turing-portal/.gitignore
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
/.pnp
|
||||
.pnp.js
|
||||
|
||||
# testing
|
||||
/coverage
|
||||
|
||||
# next.js
|
||||
/.next/
|
||||
/out/
|
||||
|
||||
# production
|
||||
/build
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
*.pem
|
||||
|
||||
# debug
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
.pnpm-debug.log*
|
||||
|
||||
# local env files
|
||||
.env*.local
|
||||
|
||||
# vercel
|
||||
.vercel
|
||||
|
||||
# generated files
|
||||
/public/rss/
|
||||
129
examples/alan-turing-portal/LICENSE.md
Normal file
@@ -0,0 +1,129 @@
|
||||
# Tailwind UI License
|
||||
|
||||
## Personal License
|
||||
|
||||
Tailwind Labs Inc. grants you an on-going, non-exclusive license to use the Components and Templates.
|
||||
|
||||
The license grants permission to **one individual** (the Licensee) to access and use the Components and Templates.
|
||||
|
||||
You **can**:
|
||||
|
||||
- Use the Components and Templates to create unlimited End Products.
|
||||
- Modify the Components and Templates to create derivative components and templates. Those components and templates are subject to this license.
|
||||
- Use the Components and Templates to create unlimited End Products for unlimited Clients.
|
||||
- Use the Components and Templates to create End Products where the End Product is sold to End Users.
|
||||
- Use the Components and Templates to create End Products that are open source and freely available to End Users.
|
||||
|
||||
You **cannot**:
|
||||
|
||||
- Use the Components and Templates to create End Products that are designed to allow an End User to build their own End Products using the Components and Templates or derivatives of the Components and Templates.
|
||||
- Re-distribute the Components and Templates or derivatives of the Components and Templates separately from an End Product, neither in code or as design assets.
|
||||
- Share your access to the Components and Templates with any other individuals.
|
||||
- Use the Components and Templates to produce anything that may be deemed by Tailwind Labs Inc, in their sole and absolute discretion, to be competitive or in conflict with the business of Tailwind Labs Inc.
|
||||
|
||||
### Example usage
|
||||
|
||||
Examples of usage **allowed** by the license:
|
||||
|
||||
- Creating a personal website by yourself.
|
||||
- Creating a website or web application for a client that will be owned by that client.
|
||||
- Creating a commercial SaaS application (like an invoicing app for example) where end users have to pay a fee to use the application.
|
||||
- Creating a commercial self-hosted web application that is sold to end users for a one-time fee.
|
||||
- Creating a web application where the primary purpose is clearly not to simply re-distribute the components (like a conference organization app that uses the components for its UI for example) that is free and open source, where the source code is publicly available.
|
||||
|
||||
Examples of usage **not allowed** by the license:
|
||||
|
||||
- Creating a repository of your favorite Tailwind UI components or templates (or derivatives based on Tailwind UI components or templates) and publishing it publicly.
|
||||
- Creating a React or Vue version of Tailwind UI and making it available either for sale or for free.
|
||||
- Create a Figma or Sketch UI kit based on the Tailwind UI component designs.
|
||||
- Creating a "website builder" project where end users can build their own websites using components or templates included with or derived from Tailwind UI.
|
||||
- Creating a theme, template, or project starter kit using the components or templates and making it available either for sale or for free.
|
||||
- Creating an admin panel tool (like [Laravel Nova](https://nova.laravel.com/) or [ActiveAdmin](https://activeadmin.info/)) that is made available either for sale or for free.
|
||||
|
||||
In simple terms, use Tailwind UI for anything you like as long as it doesn't compete with Tailwind UI.
|
||||
|
||||
### Personal License Definitions
|
||||
|
||||
Licensee is the individual who has purchased a Personal License.
|
||||
|
||||
Components and Templates are the source code and design assets made available to the Licensee after purchasing a Tailwind UI license.
|
||||
|
||||
End Product is any artifact produced that incorporates the Components or Templates or derivatives of the Components or Templates.
|
||||
|
||||
End User is a user of an End Product.
|
||||
|
||||
Client is an individual or entity receiving custom professional services directly from the Licensee, produced specifically for that individual or entity. Customers of software-as-a-service products are not considered clients for the purpose of this document.
|
||||
|
||||
## Team License
|
||||
|
||||
Tailwind Labs Inc. grants you an on-going, non-exclusive license to use the Components and Templates.
|
||||
|
||||
The license grants permission for **up to 25 Employees and Contractors of the Licensee** to access and use the Components and Templates.
|
||||
|
||||
You **can**:
|
||||
|
||||
- Use the Components and Templates to create unlimited End Products.
|
||||
- Modify the Components and Templates to create derivative components and templates. Those components and templates are subject to this license.
|
||||
- Use the Components and Templates to create unlimited End Products for unlimited Clients.
|
||||
- Use the Components and Templates to create End Products where the End Product is sold to End Users.
|
||||
- Use the Components and Templates to create End Products that are open source and freely available to End Users.
|
||||
|
||||
You **cannot**:
|
||||
|
||||
- Use the Components or Templates to create End Products that are designed to allow an End User to build their own End Products using the Components or Templates or derivatives of the Components or Templates.
|
||||
- Re-distribute the Components or Templates or derivatives of the Components or Templates separately from an End Product.
|
||||
- Use the Components or Templates to create End Products that are the property of any individual or entity other than the Licensee or Clients of the Licensee.
|
||||
- Use the Components or Templates to produce anything that may be deemed by Tailwind Labs Inc, in their sole and absolute discretion, to be competitive or in conflict with the business of Tailwind Labs Inc.
|
||||
|
||||
### Example usage
|
||||
|
||||
Examples of usage **allowed** by the license:
|
||||
|
||||
- Creating a website for your company.
|
||||
- Creating a website or web application for a client that will be owned by that client.
|
||||
- Creating a commercial SaaS application (like an invoicing app for example) where end users have to pay a fee to use the application.
|
||||
- Creating a commercial self-hosted web application that is sold to end users for a one-time fee.
|
||||
- Creating a web application where the primary purpose is clearly not to simply re-distribute the components or templates (like a conference organization app that uses the components or a template for its UI for example) that is free and open source, where the source code is publicly available.
|
||||
|
||||
Examples of use **not allowed** by the license:
|
||||
|
||||
- Creating a repository of your favorite Tailwind UI components or template (or derivatives based on Tailwind UI components or templates) and publishing it publicly.
|
||||
- Creating a React or Vue version of Tailwind UI and making it available either for sale or for free.
|
||||
- Creating a "website builder" project where end users can build their own websites using components or templates included with or derived from Tailwind UI.
|
||||
- Creating a theme or template using the components or templates and making it available either for sale or for free.
|
||||
- Creating an admin panel tool (like [Laravel Nova](https://nova.laravel.com/) or [ActiveAdmin](https://activeadmin.info/)) that is made available either for sale or for free.
|
||||
- Creating any End Product that is not the sole property of either your company or a client of your company. For example your employees/contractors can't use your company Tailwind UI license to build their own websites or side projects.
|
||||
|
||||
### Team License Definitions
|
||||
|
||||
Licensee is the business entity who has purchased a Team License.
|
||||
|
||||
Components and Templates are the source code and design assets made available to the Licensee after purchasing a Tailwind UI license.
|
||||
|
||||
End Product is any artifact produced that incorporates the Components or Templates or derivatives of the Components or Templates.
|
||||
|
||||
End User is a user of an End Product.
|
||||
|
||||
Employee is a full-time or part-time employee of the Licensee.
|
||||
|
||||
Contractor is an individual or business entity contracted to perform services for the Licensee.
|
||||
|
||||
Client is an individual or entity receiving custom professional services directly from the Licensee, produced specifically for that individual or entity. Customers of software-as-a-service products are not considered clients for the purpose of this document.
|
||||
|
||||
## Enforcement
|
||||
|
||||
If you are found to be in violation of the license, access to your Tailwind UI account will be terminated, and a refund may be issued at our discretion. When license violation is blatant and malicious (such as intentionally redistributing the Components or Templates through private warez channels), no refund will be issued.
|
||||
|
||||
The copyright of the Components and Templates is owned by Tailwind Labs Inc. You are granted only the permissions described in this license; all other rights are reserved. Tailwind Labs Inc. reserves the right to pursue legal remedies for any unauthorized use of the Components or Templates outside the scope of this license.
|
||||
|
||||
## Liability
|
||||
|
||||
Tailwind Labs Inc.’s liability to you for costs, damages, or other losses arising from your use of the Components or Templates — including third-party claims against you — is limited to a refund of your license fee. Tailwind Labs Inc. may not be held liable for any consequential damages related to your use of the Components or Templates.
|
||||
|
||||
This Agreement is governed by the laws of the Province of Ontario and the applicable laws of Canada. Legal proceedings related to this Agreement may only be brought in the courts of Ontario. You agree to service of process at the e-mail address on your original order.
|
||||
|
||||
## Questions?
|
||||
|
||||
Unsure which license you need, or unsure if your use case is covered by our licenses?
|
||||
|
||||
Email us at [support@tailwindui.com](mailto:support@tailwindui.com) with your questions.
|
||||
25
examples/alan-turing-portal/README.md
Normal file
@@ -0,0 +1,25 @@
|
||||
This demo data portal is designed for https://hatespeechdata.com. It catalogs datasets annotated for hate speech, online abuse, and offensive language which are useful for training a natural language processing system to detect this online abuse.
|
||||
|
||||
The site is built on top of [PortalJS](https://portaljs.org/). It catalogs datasets and lists of offensive keywords. It also includes static pages. All of these are stored as markdown files inside the `content` folder.
|
||||
|
||||
- .md files inside `content/datasets/` will appear on the dataset list section of the homepage and be searchable as well as having a individual page in `datasets/<file name>`
|
||||
- .md files inside `content/keywords/` will appear on the list of offensive keywords section of the homepage as well as having a individual page in `keywords/<file name>`
|
||||
- .md files inside `content/` will be converted to static pages in the url `/<file name>` eg: `content/about.md` becomes `/about`
|
||||
|
||||
This is also a Next.JS project so you can use the following steps to run the website locally.
|
||||
|
||||
## Getting started
|
||||
|
||||
To get started first install the npm dependencies:
|
||||
|
||||
```bash
|
||||
npm install
|
||||
```
|
||||
|
||||
Next, run the development server:
|
||||
|
||||
```bash
|
||||
npm run dev
|
||||
```
|
||||
|
||||
Finally, open [http://localhost:3000](http://localhost:3000) in your browser to view the website.
|
||||
94
examples/alan-turing-portal/components/Card.jsx
Normal file
@@ -0,0 +1,94 @@
|
||||
import Link from 'next/link'
|
||||
import clsx from 'clsx'
|
||||
|
||||
function ChevronRightIcon(props) {
|
||||
return (
|
||||
<svg viewBox="0 0 16 16" fill="none" aria-hidden="true" {...props}>
|
||||
<path
|
||||
d="M6.75 5.75 9.25 8l-2.5 2.25"
|
||||
strokeWidth="1.5"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export function Card({ as: Component = 'div', className, children }) {
|
||||
return (
|
||||
<Component
|
||||
className={clsx(className, 'group relative flex flex-col items-start')}
|
||||
>
|
||||
{children}
|
||||
</Component>
|
||||
)
|
||||
}
|
||||
|
||||
Card.Link = function CardLink({ children, ...props }) {
|
||||
return (
|
||||
<>
|
||||
<div className="absolute -inset-x-4 -inset-y-6 z-0 scale-95 bg-zinc-50 opacity-0 transition group-hover:scale-100 group-hover:opacity-100 dark:bg-zinc-800/50 sm:-inset-x-6 sm:rounded-2xl" />
|
||||
<Link {...props}>
|
||||
<span className="absolute -inset-x-4 -inset-y-6 z-20 sm:-inset-x-6 sm:rounded-2xl" />
|
||||
<span className="relative z-10">{children}</span>
|
||||
</Link>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
Card.Title = function CardTitle({ as: Component = 'h2', href, children }) {
|
||||
return (
|
||||
<Component className="text-base font-semibold tracking-tight text-zinc-800 dark:text-zinc-100">
|
||||
{href ? <Card.Link href={href}>{children}</Card.Link> : children}
|
||||
</Component>
|
||||
)
|
||||
}
|
||||
|
||||
Card.Description = function CardDescription({ children }) {
|
||||
return (
|
||||
<p className="z-10 mt-2 text-sm text-zinc-600 dark:text-zinc-400">
|
||||
{children}
|
||||
</p>
|
||||
)
|
||||
}
|
||||
|
||||
Card.Cta = function CardCta({ children }) {
|
||||
return (
|
||||
<div
|
||||
aria-hidden="true"
|
||||
className="relative z-10 mt-4 flex items-center text-sm font-medium text-teal-500"
|
||||
>
|
||||
{children}
|
||||
<ChevronRightIcon className="ml-1 h-4 w-4 stroke-current" />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
Card.Eyebrow = function CardEyebrow({
|
||||
as: Component = 'p',
|
||||
decorate = false,
|
||||
className,
|
||||
children,
|
||||
...props
|
||||
}) {
|
||||
return (
|
||||
<Component
|
||||
className={clsx(
|
||||
className,
|
||||
'relative z-10 order-first mb-3 flex items-center text-sm text-zinc-400 dark:text-zinc-500',
|
||||
decorate && 'pl-3.5'
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
{decorate && (
|
||||
<span
|
||||
className="absolute inset-y-0 left-0 flex items-center"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<span className="h-4 w-0.5 rounded-full bg-zinc-200 dark:bg-zinc-500" />
|
||||
</span>
|
||||
)}
|
||||
{children}
|
||||
</Component>
|
||||
)
|
||||
}
|
||||
42
examples/alan-turing-portal/components/Container.jsx
Normal file
@@ -0,0 +1,42 @@
|
||||
import { forwardRef } from 'react'
|
||||
import clsx from 'clsx'
|
||||
|
||||
const OuterContainer = forwardRef(function OuterContainer(
|
||||
{ className, children, ...props },
|
||||
ref
|
||||
) {
|
||||
return (
|
||||
<div ref={ref} className={clsx('sm:px-8', className)} {...props}>
|
||||
<div className="mx-auto max-w-7xl lg:px-8">{children}</div>
|
||||
</div>
|
||||
)
|
||||
})
|
||||
|
||||
const InnerContainer = forwardRef(function InnerContainer(
|
||||
{ className, children, ...props },
|
||||
ref
|
||||
) {
|
||||
return (
|
||||
<div
|
||||
ref={ref}
|
||||
className={clsx('relative px-4 sm:px-8 lg:px-12', className)}
|
||||
{...props}
|
||||
>
|
||||
<div className="mx-auto max-w-2xl lg:max-w-5xl">{children}</div>
|
||||
</div>
|
||||
)
|
||||
})
|
||||
|
||||
export const Container = forwardRef(function Container(
|
||||
{ children, ...props },
|
||||
ref
|
||||
) {
|
||||
return (
|
||||
<OuterContainer ref={ref} {...props}>
|
||||
<InnerContainer>{children}</InnerContainer>
|
||||
</OuterContainer>
|
||||
)
|
||||
})
|
||||
|
||||
Container.Outer = OuterContainer
|
||||
Container.Inner = InnerContainer
|
||||
36
examples/alan-turing-portal/components/Footer.jsx
Normal file
@@ -0,0 +1,36 @@
|
||||
import Link from 'next/link'
|
||||
|
||||
import { Container } from '../components/Container'
|
||||
|
||||
function NavLink({ href, children }) {
|
||||
return (
|
||||
<Link
|
||||
href={href}
|
||||
className="transition hover:text-teal-500 dark:hover:text-teal-400"
|
||||
>
|
||||
{children}
|
||||
</Link>
|
||||
)
|
||||
}
|
||||
|
||||
export function Footer() {
|
||||
return (
|
||||
<footer className="mt-32">
|
||||
<Container.Outer>
|
||||
<div className="border-t border-zinc-100 pb-16 pt-10 dark:border-zinc-700/40">
|
||||
<Container.Inner>
|
||||
<div className="flex flex-col items-center justify-between gap-6 sm:flex-row">
|
||||
<p className="text-sm font-medium text-zinc-800 dark:text-zinc-200">
|
||||
Built with <a href='https://portaljs.org'>PortalJS 🌀</a>
|
||||
</p>
|
||||
<p className="text-sm text-zinc-400 dark:text-zinc-500">
|
||||
© {new Date().getFullYear()} Leon Derczynski. All rights
|
||||
reserved.
|
||||
</p>
|
||||
</div>
|
||||
</Container.Inner>
|
||||
</div>
|
||||
</Container.Outer>
|
||||
</footer>
|
||||
)
|
||||
}
|
||||
265
examples/alan-turing-portal/components/Header.jsx
Normal file
@@ -0,0 +1,265 @@
|
||||
import { Fragment, useEffect, useRef } from 'react'
|
||||
import Image from 'next/image'
|
||||
import Link from 'next/link'
|
||||
import { useRouter } from 'next/router'
|
||||
import { Popover, Transition } from '@headlessui/react'
|
||||
import clsx from 'clsx'
|
||||
|
||||
import { Container } from '../components/Container'
|
||||
|
||||
function CloseIcon(props) {
|
||||
return (
|
||||
<svg viewBox="0 0 24 24" aria-hidden="true" {...props}>
|
||||
<path
|
||||
d="m17.25 6.75-10.5 10.5M6.75 6.75l10.5 10.5"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="1.5"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
function ChevronDownIcon(props) {
|
||||
return (
|
||||
<svg viewBox="0 0 8 6" aria-hidden="true" {...props}>
|
||||
<path
|
||||
d="M1.75 1.75 4 4.25l2.25-2.5"
|
||||
fill="none"
|
||||
strokeWidth="1.5"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
function SunIcon(props) {
|
||||
return (
|
||||
<svg
|
||||
viewBox="0 0 24 24"
|
||||
strokeWidth="1.5"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
aria-hidden="true"
|
||||
{...props}
|
||||
>
|
||||
<path d="M8 12.25A4.25 4.25 0 0 1 12.25 8v0a4.25 4.25 0 0 1 4.25 4.25v0a4.25 4.25 0 0 1-4.25 4.25v0A4.25 4.25 0 0 1 8 12.25v0Z" />
|
||||
<path
|
||||
d="M12.25 3v1.5M21.5 12.25H20M18.791 18.791l-1.06-1.06M18.791 5.709l-1.06 1.06M12.25 20v1.5M4.5 12.25H3M6.77 6.77 5.709 5.709M6.77 17.73l-1.061 1.061"
|
||||
fill="none"
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
function MoonIcon(props) {
|
||||
return (
|
||||
<svg viewBox="0 0 24 24" aria-hidden="true" {...props}>
|
||||
<path
|
||||
d="M17.25 16.22a6.937 6.937 0 0 1-9.47-9.47 7.451 7.451 0 1 0 9.47 9.47ZM12.75 7C17 7 17 2.75 17 2.75S17 7 21.25 7C17 7 17 11.25 17 11.25S17 7 12.75 7Z"
|
||||
strokeWidth="1.5"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
function GithubIcon(props) {
|
||||
return (
|
||||
<svg
|
||||
viewBox="0 0 24 24"
|
||||
aria-hidden="true"
|
||||
className="h-6 w-6 fill-slate-900 dark:fill-zinc-200"
|
||||
>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M12 2C6.477 2 2 6.463 2 11.97c0 4.404 2.865 8.14 6.839 9.458.5.092.682-.216.682-.48 0-.236-.008-.864-.013-1.695-2.782.602-3.369-1.337-3.369-1.337-.454-1.151-1.11-1.458-1.11-1.458-.908-.618.069-.606.069-.606 1.003.07 1.531 1.027 1.531 1.027.892 1.524 2.341 1.084 2.91.828.092-.643.35-1.083.636-1.332-2.22-.251-4.555-1.107-4.555-4.927 0-1.088.39-1.979 1.029-2.675-.103-.252-.446-1.266.098-2.638 0 0 .84-.268 2.75 1.022A9.607 9.607 0 0 1 12 6.82c.85.004 1.705.114 2.504.336 1.909-1.29 2.747-1.022 2.747-1.022.546 1.372.202 2.386.1 2.638.64.696 1.028 1.587 1.028 2.675 0 3.83-2.339 4.673-4.566 4.92.359.307.678.915.678 1.846 0 1.332-.012 2.407-.012 2.734 0 .267.18.577.688.48 3.97-1.32 6.833-5.054 6.833-9.458C22 6.463 17.522 2 12 2Z"
|
||||
></path>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
function MobileNavItem({ href, children }) {
|
||||
return (
|
||||
<li>
|
||||
<Popover.Button
|
||||
as={Link}
|
||||
href={href}
|
||||
className="flex items-center gap-x-2 py-2"
|
||||
>
|
||||
{children}
|
||||
</Popover.Button>
|
||||
</li>
|
||||
)
|
||||
}
|
||||
|
||||
function MobileNavigation(props) {
|
||||
return (
|
||||
<Popover {...props}>
|
||||
<Popover.Button className="group flex items-center rounded-full bg-white/90 px-4 py-2 text-sm font-medium text-zinc-800 shadow-lg shadow-zinc-800/5 ring-1 ring-zinc-900/5 backdrop-blur dark:bg-zinc-800/90 dark:text-zinc-200 dark:ring-white/10 dark:hover:ring-white/20">
|
||||
Menu
|
||||
<ChevronDownIcon className="ml-3 h-auto w-2 stroke-zinc-500 group-hover:stroke-zinc-700 dark:group-hover:stroke-zinc-400" />
|
||||
</Popover.Button>
|
||||
<Transition.Root>
|
||||
<Transition.Child
|
||||
as={Fragment}
|
||||
enter="duration-150 ease-out"
|
||||
enterFrom="opacity-0"
|
||||
enterTo="opacity-100"
|
||||
leave="duration-150 ease-in"
|
||||
leaveFrom="opacity-100"
|
||||
leaveTo="opacity-0"
|
||||
>
|
||||
<Popover.Overlay className="fixed inset-0 z-50 bg-zinc-800/40 backdrop-blur-sm dark:bg-black/80" />
|
||||
</Transition.Child>
|
||||
<Transition.Child
|
||||
as={Fragment}
|
||||
enter="duration-150 ease-out"
|
||||
enterFrom="opacity-0 scale-95"
|
||||
enterTo="opacity-100 scale-100"
|
||||
leave="duration-150 ease-in"
|
||||
leaveFrom="opacity-100 scale-100"
|
||||
leaveTo="opacity-0 scale-95"
|
||||
>
|
||||
<Popover.Panel
|
||||
focus
|
||||
className="fixed inset-x-4 top-8 z-50 origin-top rounded-3xl bg-white p-8 ring-1 ring-zinc-900/5 dark:bg-zinc-900 dark:ring-zinc-800"
|
||||
>
|
||||
<div className="flex flex-row-reverse items-center justify-between">
|
||||
<Popover.Button aria-label="Close menu" className="-m-1 p-1">
|
||||
<CloseIcon className="h-6 w-6 text-zinc-500 dark:text-zinc-400" />
|
||||
</Popover.Button>
|
||||
<h2 className="text-sm font-medium text-zinc-600 dark:text-zinc-400">
|
||||
Navigation
|
||||
</h2>
|
||||
</div>
|
||||
<nav className="mt-6">
|
||||
<ul className="-my-2 divide-y divide-zinc-100 text-base text-zinc-800 dark:divide-zinc-100/5 dark:text-zinc-300">
|
||||
<MobileNavItem href="https://github.com/leondz/hatespeechdata">
|
||||
View on Github <GithubIcon />
|
||||
</MobileNavItem>
|
||||
</ul>
|
||||
</nav>
|
||||
</Popover.Panel>
|
||||
</Transition.Child>
|
||||
</Transition.Root>
|
||||
</Popover>
|
||||
)
|
||||
}
|
||||
|
||||
function NavItem({ href, children }) {
|
||||
let isActive = useRouter().pathname === href
|
||||
|
||||
return (
|
||||
<li>
|
||||
<Link
|
||||
href={href}
|
||||
className={clsx(
|
||||
'relative flex items-center gap-x-2 px-3 py-2 transition',
|
||||
isActive
|
||||
? 'text-teal-500 dark:text-teal-400'
|
||||
: 'hover:text-teal-500 dark:hover:text-teal-400'
|
||||
)}
|
||||
>
|
||||
{children}
|
||||
{isActive && (
|
||||
<span className="absolute inset-x-1 -bottom-px h-px bg-gradient-to-r from-teal-500/0 via-teal-500/40 to-teal-500/0 dark:from-teal-400/0 dark:via-teal-400/40 dark:to-teal-400/0" />
|
||||
)}
|
||||
</Link>
|
||||
</li>
|
||||
)
|
||||
}
|
||||
|
||||
function DesktopNavigation(props) {
|
||||
return (
|
||||
<nav {...props}>
|
||||
<ul className="flex rounded-full bg-white/90 px-3 text-sm font-medium text-zinc-800 shadow-lg shadow-zinc-800/5 ring-1 ring-zinc-900/5 backdrop-blur dark:bg-zinc-800/90 dark:text-zinc-200 dark:ring-white/10">
|
||||
<NavItem href="https://github.com/leondz/hatespeechdata">
|
||||
View on Github <GithubIcon />
|
||||
</NavItem>
|
||||
</ul>
|
||||
</nav>
|
||||
)
|
||||
}
|
||||
|
||||
function ModeToggle() {
|
||||
function disableTransitionsTemporarily() {
|
||||
document.documentElement.classList.add('[&_*]:!transition-none')
|
||||
window.setTimeout(() => {
|
||||
document.documentElement.classList.remove('[&_*]:!transition-none')
|
||||
}, 0)
|
||||
}
|
||||
|
||||
function toggleMode() {
|
||||
disableTransitionsTemporarily()
|
||||
|
||||
let darkModeMediaQuery = window.matchMedia('(prefers-color-scheme: dark)')
|
||||
let isSystemDarkMode = darkModeMediaQuery.matches
|
||||
let isDarkMode = document.documentElement.classList.toggle('dark')
|
||||
|
||||
if (isDarkMode === isSystemDarkMode) {
|
||||
delete window.localStorage.isDarkMode
|
||||
} else {
|
||||
window.localStorage.isDarkMode = isDarkMode
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
aria-label="Toggle dark mode"
|
||||
className="group rounded-full bg-white/90 px-3 py-2 shadow-lg shadow-zinc-800/5 ring-1 ring-zinc-900/5 backdrop-blur transition dark:bg-zinc-800/90 dark:ring-white/10 dark:hover:ring-white/20"
|
||||
onClick={toggleMode}
|
||||
>
|
||||
<SunIcon className="h-6 w-6 fill-zinc-100 stroke-zinc-500 transition group-hover:fill-zinc-200 group-hover:stroke-zinc-700 dark:hidden [@media(prefers-color-scheme:dark)]:fill-teal-50 [@media(prefers-color-scheme:dark)]:stroke-teal-500 [@media(prefers-color-scheme:dark)]:group-hover:fill-teal-50 [@media(prefers-color-scheme:dark)]:group-hover:stroke-teal-600" />
|
||||
<MoonIcon className="hidden h-6 w-6 fill-zinc-700 stroke-zinc-500 transition dark:block [@media(prefers-color-scheme:dark)]:group-hover:stroke-zinc-400 [@media_not_(prefers-color-scheme:dark)]:fill-teal-400/10 [@media_not_(prefers-color-scheme:dark)]:stroke-teal-500" />
|
||||
</button>
|
||||
)
|
||||
}
|
||||
|
||||
function clamp(number, a, b) {
|
||||
let min = Math.min(a, b)
|
||||
let max = Math.max(a, b)
|
||||
return Math.min(Math.max(number, min), max)
|
||||
}
|
||||
|
||||
export function Header() {
|
||||
return (
|
||||
<>
|
||||
<header
|
||||
className="pointer-events-none relative z-50 flex flex-col"
|
||||
style={{
|
||||
height: 'var(--header-height)',
|
||||
marginBottom: 'var(--header-mb)',
|
||||
}}
|
||||
>
|
||||
<div
|
||||
className="top-0 z-10 h-16 pt-6"
|
||||
style={{ position: 'var(--header-position)' }}
|
||||
>
|
||||
<Container
|
||||
className="top-[var(--header-top,theme(spacing.6))] w-full"
|
||||
style={{ position: 'var(--header-inner-position)' }}
|
||||
>
|
||||
<div className="relative flex gap-4">
|
||||
<div className="flex flex-1">
|
||||
<MobileNavigation className="pointer-events-auto md:hidden" />
|
||||
<DesktopNavigation className="pointer-events-auto hidden md:block" />
|
||||
</div>
|
||||
<div className="flex justify-end md:flex-1">
|
||||
<div className="pointer-events-auto">
|
||||
<ModeToggle />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Container>
|
||||
</div>
|
||||
</header>
|
||||
</>
|
||||
)
|
||||
}
|
||||
5
examples/alan-turing-portal/content/about.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
title: About
|
||||
---
|
||||
|
||||
This is an about page, left here as an example
|
||||
@@ -0,0 +1,14 @@
|
||||
---
|
||||
title: AbuseEval v1.0
|
||||
link-to-publication: http://www.lrec-conf.org/proceedings/lrec2020/pdf/2020.lrec-1.760.pdf
|
||||
link-to-data: https://github.com/tommasoc80/AbuseEval
|
||||
task-description: Explicitness annotation of offensive and abusive content
|
||||
details-of-task: "Enriched versions of the OffensEval/OLID dataset with the distinction of explicit/implicit offensive messages and the new dimension for abusive messages. Labels for offensive language: EXPLICIT, IMPLICT, NOT; Labels for abusive language: EXPLICIT, IMPLICT, NOTABU"
|
||||
size-of-dataset: 14100
|
||||
percentage-abusive: 20.75
|
||||
language: English
|
||||
level-of-annotation: ["Tweets"]
|
||||
platform: ["Twitter"]
|
||||
medium: ["Text"]
|
||||
reference: "Caselli, T., Basile, V., Jelena, M., Inga, K., and Michael, G. 2020. \"I feel offended, don’t be abusive! implicit/explicit messages in offensive and abusive language\". The 12th Language Resources and Evaluation Conference (pp. 6193-6202). European Language Resources Association."
|
||||
---
|
||||
@@ -0,0 +1,16 @@
|
||||
---
|
||||
title: "Abusive Language Detection on Arabic Social Media (Al Jazeera)"
|
||||
link-to-publication: https://www.aclweb.org/anthology/W17-3008
|
||||
link-to-data: http://alt.qcri.org/~hmubarak/offensive/AJCommentsClassification-CF.xlsx
|
||||
task-description: Ternary (Obscene, Offensive but not obscene, Clean)
|
||||
details-of-task: Incivility
|
||||
size-of-dataset: 32000
|
||||
percentage-abusive: 0.81
|
||||
language: Arabic
|
||||
level-of-annotation: ["Posts"]
|
||||
platform: ["AlJazeera"]
|
||||
medium: ["Text"]
|
||||
reference: "Mubarak, H., Darwish, K. and Magdy, W., 2017. Abusive Language Detection on Arabic Social Media. In: Proceedings of the First Workshop on Abusive Language Online. Vancouver, Canada: Association for Computational Linguistics, pp.52-56."
|
||||
---
|
||||
|
||||
SOMETHING TEST
|
||||
@@ -0,0 +1,14 @@
|
||||
---
|
||||
title: "CoRAL: a Context-aware Croatian Abusive Language Dataset"
|
||||
link-to-publication: https://aclanthology.org/2022.findings-aacl.21/
|
||||
link-to-data: https://github.com/shekharRavi/CoRAL-dataset-Findings-of-the-ACL-AACL-IJCNLP-2022
|
||||
task-description: Multi-class based on context dependency categories (CDC)
|
||||
details-of-task: Detectioning CDC from abusive comments
|
||||
size-of-dataset: 2240
|
||||
percentage-abusive: 100
|
||||
language: "Croatian"
|
||||
level-of-annotation: ["Posts"]
|
||||
platform: ["Posts"]
|
||||
medium: ["Newspaper Comments"]
|
||||
reference: "Ravi Shekhar, Mladen Karan and Matthew Purver (2022). CoRAL: a Context-aware Croatian Abusive Language Dataset. Findings of the ACL: AACL-IJCNLP."
|
||||
---
|
||||
@@ -0,0 +1,14 @@
|
||||
---
|
||||
title: Detecting Abusive Albanian
|
||||
link-to-publication: https://arxiv.org/abs/2107.13592
|
||||
link-to-data: https://doi.org/10.6084/m9.figshare.19333298.v1
|
||||
task-description: Hierarchical (offensive/not; untargeted/targeted; person/group/other)
|
||||
details-of-task: Detect and categorise abusive language in social media data
|
||||
size-of-dataset: 11874
|
||||
percentage-abusive: 13.2
|
||||
language: Albanian
|
||||
level-of-annotation: ["Posts"]
|
||||
platform: ["Instagram", "Youtube"]
|
||||
medium: ["Text"]
|
||||
reference: "Nurce, E., Keci, J., Derczynski, L., 2021. Detecting Abusive Albanian. arXiv:2107.13592"
|
||||
---
|
||||
@@ -0,0 +1,15 @@
|
||||
---
|
||||
title: "Hate Speech Detection in the Bengali language: A Dataset and its Baseline Evaluation"
|
||||
link-to-publication: https://arxiv.org/pdf/2012.09686.pdf
|
||||
link-to-data: https://www.kaggle.com/naurosromim/bengali-hate-speech-dataset
|
||||
task-description: Binary (hateful, not)
|
||||
details-of-task: "Several categories: sports, entertainment, crime, religion, politics, celebrity and meme"
|
||||
size-of-dataset: 30000
|
||||
percentage-abusive: 0.33
|
||||
language: Bengali
|
||||
level-of-annotation: ["Posts"]
|
||||
platform: ["Youtube", "Facebook"]
|
||||
medium: ["Text"]
|
||||
reference: "Romim, N., Ahmed, M., Talukder, H., & Islam, M. S. (2021). Hate speech detection in the bengali language: A dataset and its baseline evaluation. In Proceedings of International Joint Conference on Advances in Computational Intelligence (pp. 457-468). Springer, Singapore."
|
||||
---
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
---
|
||||
title: Large-Scale Hate Speech Detection with Cross-Domain Transfer
|
||||
link-to-publication: https://aclanthology.org/2022.lrec-1.238/
|
||||
link-to-data: https://github.com/avaapm/hatespeech
|
||||
task-description: Three-class (Hate speech, Offensive language, None)
|
||||
details-of-task: Hate speech detection on social media (Twitter) including 5 target groups (gender, race, religion, politics, sports)
|
||||
size-of-dataset: "100k English (27593 hate, 30747 offensive, 41660 none)"
|
||||
percentage-abusive: 58.3
|
||||
language: English
|
||||
level-of-annotation: ["Posts"]
|
||||
platform: ["Twitter"]
|
||||
medium: ["Text", "Image"]
|
||||
reference: "Cagri Toraman, Furkan Şahinuç, Eyup Yilmaz. 2022. Large-Scale Hate Speech Detection with Cross-Domain Transfer. In Proceedings of the Thirteenth Language Resources and Evaluation Conference, pages 2215–2225, Marseille, France. European Language Resources Association."
|
||||
---
|
||||
@@ -0,0 +1,14 @@
|
||||
---
|
||||
title: "Let-Mi: An Arabic Levantine Twitter Dataset for Misogynistic Language"
|
||||
link-to-publication: https://arxiv.org/abs/2103.10195
|
||||
link-to-data: https://drive.google.com/file/d/1mM2vnjsy7QfUmdVUpKqHRJjZyQobhTrW/view
|
||||
task-description: Binary (misogyny/none) and Multi-class (none, discredit, derailing, dominance, stereotyping & objectification, threat of violence, sexual harassment, damning)
|
||||
details-of-task: Introducing an Arabic Levantine Twitter dataset for Misogynistic language
|
||||
size-of-dataset: 6603
|
||||
percentage-abusive: 48.76
|
||||
language: Arabic
|
||||
level-of-annotation: ["Posts"]
|
||||
platform: ["Twitter"]
|
||||
medium: ["Text", "Images"]
|
||||
reference: "Hala Mulki and Bilal Ghanem. 2021. Let-Mi: An Arabic Levantine Twitter Dataset for Misogynistic Language. In Proceedings of the Sixth Arabic Natural Language Processing Workshop, pages 154–163, Kyiv, Ukraine (Virtual). Association for Computational Linguistics"
|
||||
---
|
||||
@@ -0,0 +1,14 @@
|
||||
---
|
||||
title: Measuring Hate Speech
|
||||
link-to-publication: https://arxiv.org/abs/2009.10277
|
||||
link-to-data: https://huggingface.co/datasets/ucberkeley-dlab/measuring-hate-speech
|
||||
task-description: 10 ordinal labels (sentiment, (dis)respect, insult, humiliation, inferior status, violence, dehumanization, genocide, attack/defense, hate speech), which are debiased and aggregated into a continuous hate speech severity score (hate_speech_score) that includes a region for counterspeech & supportive speeech. Includes 8 target identity groups (race/ethnicity, religion, national origin/citizenship, gender, sexual orientation, age, disability, political ideology) and 42 identity subgroups.
|
||||
details-of-task: Hate speech measurement on social media in English
|
||||
size-of-dataset: "39,565 comments annotated by 7,912 annotators on 10 ordinal labels, for 1,355,560 total labels."
|
||||
percentage-abusive: 25
|
||||
language: English
|
||||
level-of-annotation: ["Social media comment"]
|
||||
platform: ["Twitter", "Reddit", "Youtube"]
|
||||
medium: ["Text"]
|
||||
reference: "Kennedy, C. J., Bacon, G., Sahn, A., & von Vacano, C. (2020). Constructing interval variables via faceted Rasch measurement and multitask deep learning: a hate speech application. arXiv preprint arXiv:2009.10277."
|
||||
---
|
||||
@@ -0,0 +1,14 @@
|
||||
---
|
||||
title: Offensive Language and Hate Speech Detection for Danish
|
||||
link-to-publication: http://www.derczynski.com/papers/danish_hsd.pdf
|
||||
link-to-data: https://figshare.com/articles/Danish_Hate_Speech_Abusive_Language_data/12220805
|
||||
task-description: "Branching structure of tasks: Binary (Offensive, Not), Within Offensive (Target, Not), Within Target (Individual, Group, Other)"
|
||||
details-of-task: Group-directed + Person-directed
|
||||
size-of-dataset: 3600
|
||||
percentage-abusive: 0.12
|
||||
language: Danish
|
||||
level-of-annotation: ["Posts"]
|
||||
platform: ["Twitter", "Reddit", "Newspaper comments"]
|
||||
medium: ["Text"]
|
||||
reference: "Sigurbergsson, G. and Derczynski, L., 2019. Offensive Language and Hate Speech Detection for Danish. ArXiv."
|
||||
---
|
||||
52
examples/alan-turing-portal/content/index.mdx
Normal file
@@ -0,0 +1,52 @@
|
||||
---
|
||||
title: Hate Speech Dataset Catalogue
|
||||
---
|
||||
|
||||
This page catalogues datasets annotated for hate speech, online abuse, and offensive language. They may be useful for e.g. training a natural language processing system to detect this language.
|
||||
|
||||
The list is maintained by [Leon Derczynski](https://www.derczynski.com/), [Bertie Vidgen](https://www.turing.ac.uk/people/researchers/bertie-vidgen), [Hannah Rose Kirk](https://www.hannahrosekirk.com/), Pica Johansson, [Yi-Ling Chung](https://yilingchung.github.io/), Mads Guldborg Kjeldgaard Kongsbak, [Laila Sprejer](https://www.turing.ac.uk/people/researchers/laila-sprejer), and Philine Zeinert.
|
||||
|
||||
We provide a list of [datasets](#Datasets-header) and [keywords](#Keywords-header). If you would like to contribute to our catalogue or add your dataset, please see the [instructions for contributing](#Contributing-header).
|
||||
|
||||
If you use these resources, please cite (and read!) our paper: [Directions in Abusive Language Training Data: Garbage In, Garbage Out](https://journals.plos.org/plosone/article?id=10.1371/journal.pone.0243300). And if you would like to find other resources for researching online hate, visit The Alan Turing Institute's [Online Hate Research Hub](https://www.turing.ac.uk/research/research-programmes/public-policy/online-hate-research-hub) or read The Alan Turing Institute's [Reading List on Online Hate and Abuse Research](https://docs.google.com/document/d/1WVkVGp29Jt6d-4fBnZ5OWVYuFn_03rzz-KBqPsu6gTM/edit?usp=sharing).
|
||||
|
||||
If you're looking for a good paper on online hate training datasets (beyond our paper, of course!) then have a look at ['Resources and benchmark corpora for hate speech detection: a systematic review'](https://link.springer.com/article/10.1007/s10579-020-09502-8) by Poletto et al. in *Language Resources and Evaluation*.
|
||||
|
||||
Accompanying [data statements](https://www.mitpressjournals.org/doi/abs/10.1162/tacl_a_00041) preferred for all corpora.
|
||||
|
||||
<a href="#Datasets-header" className="w-fit mx-auto no-underline rounded-md py-3 px-6 outline-offset-2 transition !active:transition-none bg-zinc-800 !font-semibold !text-zinc-100 hover:bg-zinc-700 active:bg-zinc-800 active:text-zinc-100/70 dark:bg-zinc-700 dark:hover:bg-zinc-600 !dark:active:bg-zinc-700 dark:active:text-zinc-100/70">See datasets</a>
|
||||
|
||||
<h2 id="Contributing-header">How to contribute</h2>
|
||||
|
||||
We accept entries to our catalogue based on pull requests to the content folder. The dataset must be avaliable for download to be included in the list. If you want to add an entry, follow these steps!
|
||||
|
||||
Please send just one dataset addition/edit at a time - edit it in, then save. This will make everyone’s life easier (including yours!)
|
||||
|
||||
### Create file
|
||||
|
||||
Go to the repo url file and click the "Add file" dropdown and then click on "Create new file".
|
||||
|
||||

|
||||
|
||||
### Choose location
|
||||
|
||||
In the following page type `content/datasets/<name-of-the-file>.md`. if you want to add an entry to the datasets catalog or `content/keywords/<name-of-the-file>.md` if you want to add an entry to the lists of abusive keywords, if you want to just add an static page you can leave in the root of `content` it will automatically get assigned an url eg: `/content/about.md` becomes the `/about` page
|
||||
|
||||

|
||||
|
||||
### Fill in content
|
||||
|
||||
Copy the contents of `templates/dataset.md` or `templates/keywords.md` respectively to the camp below, filling out the fields with the correct data format
|
||||
|
||||

|
||||
|
||||
### Commit changes
|
||||
|
||||
Click on "Commit changes", on the popup make sure you give some brief detail on the proposed change. and then click on Propose changes
|
||||
|
||||
<img src='https://i.imgur.com/BxuxKEJ.png' style={{ maxWidth: '50%', margin: '0 auto' }}/>
|
||||
|
||||
### Submit PR
|
||||
|
||||
Submit the pull request on the next page when prompted.
|
||||
|
||||
10
examples/alan-turing-portal/content/keywords/hurtlex.md
Normal file
@@ -0,0 +1,10 @@
|
||||
---
|
||||
title: Hurtlex
|
||||
description: HurtLex is a lexicon of offensive, aggressive, and hateful words in over 50 languages. The words are divided into 17 categories, plus a macro-category indicating whether there is stereotype involved.
|
||||
data-link: https://github.com/valeriobasile/hurtlex
|
||||
reference: http://ceur-ws.org/Vol-2253/paper49.pdf, Proc. CLiC-it 2018
|
||||
---
|
||||
|
||||
## Markdown TEST
|
||||
|
||||
Some text
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
title: SexHateLex is a Chinese lexicon of hateful and sexist words.
|
||||
data-link: https://doi.org/10.5281/zenodo.4773875
|
||||
reference: http://ceur-ws.org/Vol-2253/paper49.pdf, Journal of OSNEM, Vol.27, 2022, 100182, ISSN 2468-6964.
|
||||
---
|
||||
11
examples/alan-turing-portal/jsconfig.json
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@/components/*": ["components/*"],
|
||||
"@/pages/*": ["pages/*"],
|
||||
"@/lib/*": ["lib/*"]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
8
examples/alan-turing-portal/lib/formatDate.js
Normal file
@@ -0,0 +1,8 @@
|
||||
export function formatDate(dateString) {
|
||||
return new Date(`${dateString}T00:00:00Z`).toLocaleDateString('en-US', {
|
||||
day: 'numeric',
|
||||
month: 'long',
|
||||
year: 'numeric',
|
||||
timeZone: 'UTC',
|
||||
})
|
||||
}
|
||||
98
examples/alan-turing-portal/lib/markdown.js
Normal file
@@ -0,0 +1,98 @@
|
||||
import matter from "gray-matter";
|
||||
import mdxmermaid from "mdx-mermaid";
|
||||
import { h } from "hastscript";
|
||||
import remarkCallouts from "@flowershow/remark-callouts";
|
||||
import remarkEmbed from "@flowershow/remark-embed";
|
||||
import remarkGfm from "remark-gfm";
|
||||
import remarkMath from "remark-math";
|
||||
import remarkSmartypants from "remark-smartypants";
|
||||
import remarkToc from "remark-toc";
|
||||
import remarkWikiLink from "@flowershow/remark-wiki-link";
|
||||
import rehypeAutolinkHeadings from "rehype-autolink-headings";
|
||||
import rehypeKatex from "rehype-katex";
|
||||
import rehypeSlug from "rehype-slug";
|
||||
import rehypePrismPlus from "rehype-prism-plus";
|
||||
|
||||
import { serialize } from "next-mdx-remote/serialize";
|
||||
|
||||
/**
|
||||
* Parse a markdown or MDX file to an MDX source form + front matter data
|
||||
*
|
||||
* @source: the contents of a markdown or mdx file
|
||||
* @format: used to indicate to next-mdx-remote which format to use (md or mdx)
|
||||
* @returns: { mdxSource: mdxSource, frontMatter: ...}
|
||||
*/
|
||||
const parse = async function (source, format) {
|
||||
const { content, data } = matter(source);
|
||||
|
||||
const mdxSource = await serialize(
|
||||
{ value: content, path: format },
|
||||
{
|
||||
// Optionally pass remark/rehype plugins
|
||||
mdxOptions: {
|
||||
remarkPlugins: [
|
||||
remarkEmbed,
|
||||
remarkGfm,
|
||||
[remarkSmartypants, { quotes: false, dashes: "oldschool" }],
|
||||
remarkMath,
|
||||
remarkCallouts,
|
||||
remarkWikiLink,
|
||||
[
|
||||
remarkToc,
|
||||
{
|
||||
heading: "Table of contents",
|
||||
tight: true,
|
||||
},
|
||||
],
|
||||
[mdxmermaid, {}],
|
||||
],
|
||||
rehypePlugins: [
|
||||
rehypeSlug,
|
||||
[
|
||||
rehypeAutolinkHeadings,
|
||||
{
|
||||
properties: { className: "heading-link" },
|
||||
test(element) {
|
||||
return (
|
||||
["h2", "h3", "h4", "h5", "h6"].includes(element.tagName) &&
|
||||
element.properties?.id !== "table-of-contents" &&
|
||||
element.properties?.className !== "blockquote-heading"
|
||||
);
|
||||
},
|
||||
content() {
|
||||
return [
|
||||
h(
|
||||
"svg",
|
||||
{
|
||||
xmlns: "http:www.w3.org/2000/svg",
|
||||
fill: "#ab2b65",
|
||||
viewBox: "0 0 20 20",
|
||||
className: "w-5 h-5",
|
||||
},
|
||||
[
|
||||
h("path", {
|
||||
fillRule: "evenodd",
|
||||
clipRule: "evenodd",
|
||||
d: "M9.493 2.853a.75.75 0 00-1.486-.205L7.545 6H4.198a.75.75 0 000 1.5h3.14l-.69 5H3.302a.75.75 0 000 1.5h3.14l-.435 3.148a.75.75 0 001.486.205L7.955 14h2.986l-.434 3.148a.75.75 0 001.486.205L12.456 14h3.346a.75.75 0 000-1.5h-3.14l.69-5h3.346a.75.75 0 000-1.5h-3.14l.435-3.147a.75.75 0 00-1.486-.205L12.045 6H9.059l.434-3.147zM8.852 7.5l-.69 5h2.986l.69-5H8.852z",
|
||||
}),
|
||||
]
|
||||
),
|
||||
];
|
||||
},
|
||||
},
|
||||
],
|
||||
[rehypeKatex, { output: "mathml" }],
|
||||
[rehypePrismPlus, { ignoreMissing: true }],
|
||||
],
|
||||
format,
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
return {
|
||||
mdxSource: mdxSource,
|
||||
frontMatter: data,
|
||||
};
|
||||
};
|
||||
|
||||
export default parse;
|
||||
14
examples/alan-turing-portal/lib/mddb.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import { MarkdownDB } from "@flowershow/markdowndb";
|
||||
|
||||
const dbPath = "markdown.db";
|
||||
|
||||
const client = new MarkdownDB({
|
||||
client: "sqlite3",
|
||||
connection: {
|
||||
filename: dbPath,
|
||||
},
|
||||
});
|
||||
|
||||
const clientPromise = client.init();
|
||||
|
||||
export default clientPromise;
|
||||
BIN
examples/alan-turing-portal/markdown.db
Normal file
5
examples/alan-turing-portal/next-env.d.ts
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
/// <reference types="next" />
|
||||
/// <reference types="next/image-types/global" />
|
||||
|
||||
// NOTE: This file should not be edited
|
||||
// see https://nextjs.org/docs/basic-features/typescript for more information.
|
||||
3
examples/alan-turing-portal/next.config.js
Normal file
@@ -0,0 +1,3 @@
|
||||
module.exports = {
|
||||
swcMinify: true,
|
||||
};
|
||||
22977
examples/alan-turing-portal/package-lock.json
generated
Normal file
71
examples/alan-turing-portal/package.json
Normal file
@@ -0,0 +1,71 @@
|
||||
{
|
||||
"name": "tailwindui-template",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "next dev",
|
||||
"build": "next build",
|
||||
"start": "next start",
|
||||
"lint": "next lint",
|
||||
"prebuild": "npm run mddb",
|
||||
"mddb": "mddb ./content"
|
||||
},
|
||||
"browserslist": "defaults, not ie <= 11",
|
||||
"dependencies": {
|
||||
"@flowershow/core": "^0.4.10",
|
||||
"@flowershow/markdowndb": "^0.1.1",
|
||||
"@flowershow/remark-callouts": "^1.0.0",
|
||||
"@flowershow/remark-embed": "^1.0.0",
|
||||
"@flowershow/remark-wiki-link": "^1.1.2",
|
||||
"@headlessui/react": "^1.7.13",
|
||||
"@heroicons/react": "^2.0.17",
|
||||
"@mapbox/rehype-prism": "^0.8.0",
|
||||
"@mdx-js/loader": "^2.1.5",
|
||||
"@mdx-js/react": "^2.1.5",
|
||||
"@next/mdx": "^13.0.2",
|
||||
"@opentelemetry/api": "^1.4.0",
|
||||
"@tailwindcss/forms": "^0.5.3",
|
||||
"@tailwindcss/typography": "^0.5.4",
|
||||
"@tanstack/react-table": "^8.8.5",
|
||||
"autoprefixer": "^10.4.12",
|
||||
"clsx": "^1.2.1",
|
||||
"fast-glob": "^3.2.11",
|
||||
"feed": "^4.2.2",
|
||||
"flexsearch": "^0.7.31",
|
||||
"focus-visible": "^5.2.0",
|
||||
"gray-matter": "^4.0.3",
|
||||
"hastscript": "^7.2.0",
|
||||
"mdx-mermaid": "^2.0.0-rc7",
|
||||
"mermaid": "^10.1.0",
|
||||
"next": "13.2.1",
|
||||
"next-mdx-remote": "^4.4.1",
|
||||
"next-router-mock": "^0.9.3",
|
||||
"next-superjson-plugin": "^0.5.7",
|
||||
"papaparse": "^5.4.1",
|
||||
"postcss-focus-visible": "^6.0.4",
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0",
|
||||
"react-hook-form": "^7.43.9",
|
||||
"react-markdown": "^8.0.7",
|
||||
"react-vega": "^7.6.0",
|
||||
"rehype-autolink-headings": "^6.1.1",
|
||||
"rehype-katex": "^6.0.3",
|
||||
"rehype-prism-plus": "^1.5.1",
|
||||
"rehype-slug": "^5.1.0",
|
||||
"remark-gfm": "^3.0.1",
|
||||
"remark-math": "^5.1.1",
|
||||
"remark-smartypants": "^2.0.0",
|
||||
"remark-toc": "^8.0.1",
|
||||
"superjson": "^1.12.3",
|
||||
"tailwindcss": "^3.3.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"eslint": "8.26.0",
|
||||
"eslint-config-next": "13.0.2",
|
||||
"prettier": "^2.8.7",
|
||||
"prettier-plugin-tailwindcss": "^0.2.6",
|
||||
"@types/node": "18.16.0",
|
||||
"@types/react": "18.2.0",
|
||||
"@types/react-dom": "18.2.0"
|
||||
}
|
||||
}
|
||||
107
examples/alan-turing-portal/pages/[...slug].jsx
Normal file
@@ -0,0 +1,107 @@
|
||||
import { Container } from '../components/Container'
|
||||
import clientPromise from '../lib/mddb'
|
||||
import { promises as fs } from 'fs';
|
||||
import { MDXRemote } from 'next-mdx-remote'
|
||||
import { Card } from '../components/Card'
|
||||
import Head from 'next/head'
|
||||
import parse from '../lib/markdown'
|
||||
import { Mermaid } from '@flowershow/core';
|
||||
import { Header } from '../components/Header';
|
||||
|
||||
export const getStaticProps = async ({ params }) => {
|
||||
const urlPath = params.slug ? params.slug.join('/') : ''
|
||||
|
||||
const mddb = await clientPromise
|
||||
const dbFile = await mddb.getFileByUrl(urlPath)
|
||||
|
||||
const source = await fs.readFile(dbFile.file_path,'utf-8')
|
||||
let mdxSource = await parse(source, '.mdx')
|
||||
|
||||
return {
|
||||
props: {
|
||||
mdxSource,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
export async function getStaticPaths() {
|
||||
const mddb = await clientPromise
|
||||
const allDocuments = await mddb.getFiles({ extensions: ['md', 'mdx'] })
|
||||
|
||||
const paths = allDocuments.filter(document => document.url_path !== '/').map((page) => {
|
||||
const parts = page.url_path.split('/')
|
||||
return { params: { slug: parts } }
|
||||
})
|
||||
|
||||
return {
|
||||
paths,
|
||||
fallback: false,
|
||||
}
|
||||
}
|
||||
|
||||
const isValidUrl = (urlString) => {
|
||||
try {
|
||||
return Boolean(new URL(urlString))
|
||||
} catch (e) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
const Meta = ({keyValuePairs}) => {
|
||||
const prettifyMetaValue = (value) => value.replaceAll('-',' ').charAt(0).toUpperCase() + value.replaceAll('-',' ').slice(1);
|
||||
return (
|
||||
<>
|
||||
{keyValuePairs.map((entry) => {
|
||||
return isValidUrl(entry[1]) ? (
|
||||
<Card.Description>
|
||||
<span className="font-semibold">
|
||||
{prettifyMetaValue(entry[0])}: {' '}
|
||||
</span>
|
||||
<a
|
||||
className="text-ellipsis underline transition hover:text-teal-400 dark:hover:text-teal-900"
|
||||
href={entry[1]}
|
||||
>
|
||||
{entry[1]}
|
||||
</a>
|
||||
</Card.Description>
|
||||
) : (
|
||||
<Card.Description>
|
||||
<span className="font-semibold">{prettifyMetaValue(entry[0])}: </span>
|
||||
{Array.isArray(entry[1]) ? entry[1].join(', ') : entry[1]}
|
||||
</Card.Description>
|
||||
)
|
||||
})}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default function DRDPage({ mdxSource }) {
|
||||
const meta = mdxSource.frontMatter
|
||||
const keyValuePairs = Object.entries(meta).filter(
|
||||
(entry) => entry[0] !== 'title'
|
||||
)
|
||||
return (
|
||||
<>
|
||||
<Header />
|
||||
<Head>
|
||||
<title>{meta.title}</title>
|
||||
</Head>
|
||||
<Container className="mt-16 lg:mt-32 relative">
|
||||
<Header />
|
||||
<article>
|
||||
<header className="flex flex-col">
|
||||
<h1 className="mt-6 text-4xl font-bold tracking-tight text-zinc-800 dark:text-zinc-100 sm:text-5xl">
|
||||
{meta.title}
|
||||
</h1>
|
||||
<Card as="article">
|
||||
<Meta keyValuePairs={keyValuePairs} />
|
||||
</Card>
|
||||
</header>
|
||||
<div className="prose dark:prose-invert">
|
||||
<MDXRemote {...mdxSource.mdxSource} components={{mermaid: Mermaid}} />
|
||||
</div>
|
||||
</article>
|
||||
</Container>
|
||||
</>
|
||||
)
|
||||
}
|
||||
38
examples/alan-turing-portal/pages/_app.jsx
Normal file
@@ -0,0 +1,38 @@
|
||||
import { useEffect, useRef } from 'react'
|
||||
|
||||
import { Footer } from '../components/Footer'
|
||||
import { Header } from '../components/Header'
|
||||
|
||||
import '../styles/tailwind.css'
|
||||
import 'focus-visible'
|
||||
|
||||
function usePrevious(value) {
|
||||
let ref = useRef()
|
||||
|
||||
useEffect(() => {
|
||||
ref.current = value
|
||||
}, [value])
|
||||
|
||||
return ref.current
|
||||
}
|
||||
|
||||
export default function App({ Component, pageProps, router }) {
|
||||
let previousPathname = usePrevious(router.pathname)
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="fixed inset-0 flex justify-center sm:px-8">
|
||||
<div className="flex w-full max-w-7xl lg:px-8">
|
||||
<div className="w-full bg-white ring-1 ring-zinc-100 dark:bg-zinc-900 dark:ring-zinc-300/20" />
|
||||
</div>
|
||||
</div>
|
||||
<div className="relative">
|
||||
<Header />
|
||||
<main>
|
||||
<Component previousPathname={previousPathname} {...pageProps} />
|
||||
</main>
|
||||
<Footer />
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
52
examples/alan-turing-portal/pages/_document.jsx
Normal file
@@ -0,0 +1,52 @@
|
||||
import { Head, Html, Main, NextScript } from 'next/document'
|
||||
|
||||
const modeScript = `
|
||||
updateMode()
|
||||
window.addEventListener('storage', updateModeWithoutTransitions)
|
||||
|
||||
function updateMode() {
|
||||
let isDarkMode = window.localStorage.isDarkMode === 'true'
|
||||
|
||||
if (isDarkMode) {
|
||||
document.documentElement.classList.add('dark')
|
||||
} else {
|
||||
document.documentElement.classList.remove('dark')
|
||||
}
|
||||
}
|
||||
|
||||
function disableTransitionsTemporarily() {
|
||||
document.documentElement.classList.add('[&_*]:!transition-none')
|
||||
window.setTimeout(() => {
|
||||
document.documentElement.classList.remove('[&_*]:!transition-none')
|
||||
}, 0)
|
||||
}
|
||||
|
||||
function updateModeWithoutTransitions() {
|
||||
disableTransitionsTemporarily()
|
||||
updateMode()
|
||||
}
|
||||
`
|
||||
|
||||
export default function Document() {
|
||||
return (
|
||||
<Html className="h-full antialiased" lang="en">
|
||||
<Head>
|
||||
<script dangerouslySetInnerHTML={{ __html: modeScript }} />
|
||||
<link
|
||||
rel="alternate"
|
||||
type="application/rss+xml"
|
||||
href={`${process.env.NEXT_PUBLIC_SITE_URL}/rss/feed.xml`}
|
||||
/>
|
||||
<link
|
||||
rel="alternate"
|
||||
type="application/feed+json"
|
||||
href={`${process.env.NEXT_PUBLIC_SITE_URL}/rss/feed.json`}
|
||||
/>
|
||||
</Head>
|
||||
<body className="flex h-full flex-col bg-zinc-50 dark:bg-black">
|
||||
<Main />
|
||||
<NextScript />
|
||||
</body>
|
||||
</Html>
|
||||
)
|
||||
}
|
||||
296
examples/alan-turing-portal/pages/index.jsx
Normal file
@@ -0,0 +1,296 @@
|
||||
import Head from 'next/head'
|
||||
import fs from 'fs'
|
||||
|
||||
import { Card } from '../components/Card'
|
||||
import { Container } from '../components/Container'
|
||||
import clientPromise from '../lib/mddb'
|
||||
import { Index } from 'flexsearch'
|
||||
import { useForm } from 'react-hook-form'
|
||||
import Link from 'next/link'
|
||||
import { serialize } from 'next-mdx-remote/serialize'
|
||||
import { MDXRemote } from 'next-mdx-remote'
|
||||
|
||||
function DatasetCard({ dataset }) {
|
||||
return (
|
||||
<Card as="article">
|
||||
<Card.Title>
|
||||
<Link href={dataset.url}>{dataset.title}</Link>
|
||||
</Card.Title>
|
||||
<Card.Description>
|
||||
<span className="font-semibold">Link to publication: </span>{' '}
|
||||
<a
|
||||
className="text-ellipsis underline transition hover:text-teal-400 dark:hover:text-teal-900"
|
||||
href={dataset['link-to-publication']}
|
||||
>
|
||||
{dataset['link-to-publication']}
|
||||
</a>
|
||||
</Card.Description>
|
||||
<Card.Description>
|
||||
<span className="font-semibold">Link to data: </span>
|
||||
<a
|
||||
className="text-ellipsis underline transition hover:text-teal-600 dark:hover:text-teal-900"
|
||||
href={dataset['link-to-data']}
|
||||
>
|
||||
{dataset['link-to-data']}
|
||||
</a>
|
||||
</Card.Description>
|
||||
<Card.Description>
|
||||
<span className="font-semibold">Task Description: </span>
|
||||
{dataset['task-description']}
|
||||
</Card.Description>
|
||||
<Card.Description>
|
||||
<span className="font-semibold">Details of Task: </span>{' '}
|
||||
{dataset['details-of-task']}
|
||||
</Card.Description>
|
||||
<Card.Description>
|
||||
<span className="font-semibold">Size of Dataset: </span>{' '}
|
||||
{dataset['size-of-dataset']}
|
||||
</Card.Description>
|
||||
<Card.Description>
|
||||
<span className="font-semibold">Percentage Abusive: </span>
|
||||
{dataset['percentage-abusive']}%
|
||||
</Card.Description>
|
||||
<Card.Description>
|
||||
<span className="font-semibold">Language: </span>
|
||||
{dataset['language']}
|
||||
</Card.Description>
|
||||
<Card.Description>
|
||||
<span className="font-semibold">Level of Annotation: </span>
|
||||
{dataset['level-of-annotation'].join(', ')}
|
||||
</Card.Description>
|
||||
<Card.Description>
|
||||
<span className="font-semibold">Platform: </span>
|
||||
{dataset['platform'].join(', ')}
|
||||
</Card.Description>
|
||||
<Card.Description>
|
||||
<span className="font-semibold">Medium: </span>
|
||||
{dataset['medium'].join(', ')}
|
||||
</Card.Description>
|
||||
<Card.Description>
|
||||
<span className="font-semibold">Reference: </span>
|
||||
{dataset['reference']}
|
||||
</Card.Description>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
||||
function ListOfAbusiveKeywordsCard({ list }) {
|
||||
return (
|
||||
<Card as="article">
|
||||
<Card.Title>
|
||||
<Link href={list.url}>{list.title}</Link>
|
||||
</Card.Title>
|
||||
{list.description && (
|
||||
<Card.Description>
|
||||
<span className="font-semibold">List Description: </span>{' '}
|
||||
{list.description}
|
||||
</Card.Description>
|
||||
)}
|
||||
<Card.Description>
|
||||
<span className="font-semibold">Data Link: </span>
|
||||
<a
|
||||
className="text-ellipsis underline transition hover:text-teal-600 dark:hover:text-teal-900"
|
||||
href={list['data-link']}
|
||||
>
|
||||
{list['data-link']}
|
||||
</a>
|
||||
</Card.Description>
|
||||
<Card.Description>
|
||||
<span className="font-semibold">Reference: </span>
|
||||
<a
|
||||
className="text-ellipsis underline transition hover:text-teal-600 dark:hover:text-teal-900"
|
||||
href={list.reference}
|
||||
>
|
||||
{list.reference}
|
||||
</a>
|
||||
</Card.Description>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
||||
export default function Home({
|
||||
datasets,
|
||||
indexText,
|
||||
listsOfKeywords,
|
||||
availableLanguages,
|
||||
availablePlatforms,
|
||||
}) {
|
||||
const index = new Index()
|
||||
datasets.forEach((dataset) =>
|
||||
index.add(
|
||||
dataset.id,
|
||||
`${dataset.title} ${dataset['task-description']} ${dataset['details-of-task']} ${dataset['reference']}`
|
||||
)
|
||||
)
|
||||
const { register, watch, handleSubmit, reset } = useForm({
|
||||
defaultValues: {
|
||||
searchTerm: '',
|
||||
lang: '',
|
||||
platform: '',
|
||||
},
|
||||
})
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<title>Hate Speech Dataset Catalogue</title>
|
||||
<meta
|
||||
name="description"
|
||||
content="Catalog of abusive language data (PLoS 2020)"
|
||||
/>
|
||||
</Head>
|
||||
<Container className="mt-9">
|
||||
<div className="max-w-2xl">
|
||||
<h1 className="text-4xl font-bold tracking-tight text-zinc-800 dark:text-zinc-100 sm:text-5xl">
|
||||
{indexText.frontmatter.title}
|
||||
</h1>
|
||||
<article className="index-text prose mt-6 flex flex-col gap-y-2 text-base text-zinc-600 dark:prose-invert prose-h3:mt-4 prose-a:font-normal prose-a:text-zinc-600 prose-a:decoration-inherit prose-img:rounded-none dark:text-zinc-400 prose-a:dark:text-zinc-400 hover:prose-a:text-teal-600 hover:prose-a:dark:text-teal-900">
|
||||
<MDXRemote {...indexText} />
|
||||
</article>
|
||||
</div>
|
||||
</Container>
|
||||
<Container className="mt-12 md:mt-14">
|
||||
<div className="mx-auto grid max-w-7xl grid-cols-1 gap-y-8 lg:max-w-none">
|
||||
<h2
|
||||
id="Datasets-header"
|
||||
className="text-xl font-bold tracking-tight text-zinc-800 dark:text-zinc-100 sm:text-5xl"
|
||||
>
|
||||
Datasets
|
||||
</h2>
|
||||
<form
|
||||
onSubmit={handleSubmit(() => reset())}
|
||||
className="rounded-2xl border border-zinc-100 px-4 py-6 dark:border-zinc-700/40 sm:p-6"
|
||||
>
|
||||
<p className="mt-2 text-lg font-semibold text-zinc-600 dark:text-zinc-100">
|
||||
Search for datasets
|
||||
</p>
|
||||
<div className="mt-6 flex flex-col gap-3 sm:flex-row">
|
||||
<input
|
||||
placeholder="Search here"
|
||||
aria-label="Hate speech on Twitter"
|
||||
{...register('searchTerm')}
|
||||
className="min-w-0 flex-auto appearance-none rounded-md border border-zinc-900/10 bg-white px-3 py-[calc(theme(spacing.2)-1px)] shadow-md shadow-zinc-800/5 placeholder:text-zinc-600 focus:border-teal-500 focus:outline-none focus:ring-4 focus:ring-teal-500/10 dark:border-zinc-700 dark:bg-zinc-700/[0.15] dark:text-zinc-200 dark:placeholder:text-zinc-200 dark:focus:border-teal-400 dark:focus:ring-teal-400/10 sm:text-sm"
|
||||
/>
|
||||
<select
|
||||
placeholder="Language"
|
||||
defaultValue=""
|
||||
className="min-w-0 flex-auto appearance-none rounded-md border border-zinc-900/10 bg-white px-3 py-[calc(theme(spacing.2)-1px)] text-zinc-600 shadow-md shadow-zinc-800/5 placeholder:text-zinc-400 focus:border-teal-500 focus:outline-none focus:ring-4 focus:ring-teal-500/10 dark:border-zinc-700 dark:bg-zinc-700/[0.15] dark:text-zinc-200 dark:placeholder:text-zinc-500 dark:focus:border-teal-400 dark:focus:ring-teal-400/10 sm:text-sm"
|
||||
{...register('lang')}
|
||||
>
|
||||
<option value="" disabled hidden>
|
||||
Filter by language
|
||||
</option>
|
||||
{availableLanguages.map((lang) => (
|
||||
<option
|
||||
key={lang}
|
||||
className="dark:bg-white dark:text-black"
|
||||
value={lang}
|
||||
>
|
||||
{lang}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
<select
|
||||
placeholder="Platform"
|
||||
defaultValue=""
|
||||
className="min-w-0 flex-auto appearance-none rounded-md border border-zinc-900/10 bg-white px-3 py-[calc(theme(spacing.2)-1px)] text-zinc-600 shadow-md shadow-zinc-800/5 placeholder:text-zinc-400 focus:border-teal-500 focus:outline-none focus:ring-4 focus:ring-teal-500/10 dark:border-zinc-700 dark:bg-zinc-700/[0.15] dark:text-zinc-200 dark:placeholder:text-zinc-500 dark:focus:border-teal-400 dark:focus:ring-teal-400/10 sm:text-sm"
|
||||
{...register('platform')}
|
||||
>
|
||||
<option value="" disabled hidden>
|
||||
Filter by platform
|
||||
</option>
|
||||
{availablePlatforms.map((platform) => (
|
||||
<option
|
||||
key={platform}
|
||||
className="dark:bg-white dark:text-black"
|
||||
value={platform}
|
||||
>
|
||||
{platform}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
<button
|
||||
type="submit"
|
||||
className="inline-flex flex-none items-center justify-center gap-2 rounded-md bg-zinc-800 px-3 py-2 text-sm font-semibold text-zinc-100 outline-offset-2 transition hover:bg-zinc-700 active:bg-zinc-800 active:text-zinc-100/70 active:transition-none dark:bg-zinc-700 dark:hover:bg-zinc-600 dark:active:bg-zinc-700 dark:active:text-zinc-100/70"
|
||||
>
|
||||
Clear filters
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
<div className="flex flex-col gap-16">
|
||||
{datasets
|
||||
.filter((dataset) =>
|
||||
watch().searchTerm && watch().searchTerm !== ''
|
||||
? index.search(watch().searchTerm).includes(dataset.id)
|
||||
: true
|
||||
)
|
||||
.filter((dataset) =>
|
||||
watch().lang && watch().lang !== ''
|
||||
? dataset.language === watch().lang
|
||||
: true
|
||||
)
|
||||
.filter((dataset) =>
|
||||
watch().platform && watch().platform !== ''
|
||||
? dataset.platform.includes(watch().platform)
|
||||
: true
|
||||
)
|
||||
.map((dataset) => (
|
||||
<DatasetCard key={dataset.title} dataset={dataset} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</Container>
|
||||
<Container className="mt-16">
|
||||
<h2 id="Keywords-header" className="text-xl font-bold tracking-tight text-zinc-800 dark:text-zinc-100 sm:text-5xl">
|
||||
Lists of Abusive Keywords
|
||||
</h2>
|
||||
<div className="mt-3 flex flex-col gap-16">
|
||||
{listsOfKeywords.map((list) => (
|
||||
<ListOfAbusiveKeywordsCard key={list.title} list={list} />
|
||||
))}
|
||||
</div>
|
||||
</Container>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export async function getStaticProps() {
|
||||
const mddb = await clientPromise
|
||||
const datasetPages = await mddb.getFiles({
|
||||
folder: 'datasets',
|
||||
extensions: ['md', 'mdx'],
|
||||
})
|
||||
const datasets = datasetPages.map((page) => ({
|
||||
...page.metadata,
|
||||
id: page._id,
|
||||
url: page.url_path,
|
||||
}))
|
||||
const listsOfKeywordsPages = await mddb.getFiles({
|
||||
folder: 'keywords',
|
||||
extensions: ['md', 'mdx'],
|
||||
})
|
||||
const listsOfKeywords = listsOfKeywordsPages.map((page) => ({
|
||||
...page.metadata,
|
||||
id: page._id,
|
||||
url: page.url_path,
|
||||
}))
|
||||
|
||||
const index = await mddb.getFileByUrl('/')
|
||||
let indexSource = fs.readFileSync(index.file_path, { encoding: 'utf-8' })
|
||||
indexSource = await serialize(indexSource, { parseFrontmatter: true })
|
||||
|
||||
const availableLanguages = [
|
||||
...new Set(datasets.map((dataset) => dataset.language)),
|
||||
]
|
||||
const availablePlatforms = [
|
||||
...new Set(datasets.map((dataset) => dataset.platform).flat()),
|
||||
]
|
||||
return {
|
||||
props: {
|
||||
datasets,
|
||||
listsOfKeywords,
|
||||
indexText: indexSource,
|
||||
availableLanguages,
|
||||
availablePlatforms,
|
||||
},
|
||||
}
|
||||
}
|
||||
9
examples/alan-turing-portal/postcss.config.js
Normal file
@@ -0,0 +1,9 @@
|
||||
module.exports = {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
'postcss-focus-visible': {
|
||||
replaceWith: '[data-focus-visible-added]',
|
||||
},
|
||||
autoprefixer: {},
|
||||
},
|
||||
}
|
||||
5
examples/alan-turing-portal/prettier.config.js
Normal file
@@ -0,0 +1,5 @@
|
||||
module.exports = {
|
||||
singleQuote: true,
|
||||
semi: false,
|
||||
plugins: [require('prettier-plugin-tailwindcss')],
|
||||
}
|
||||
BIN
examples/alan-turing-portal/public/favicon.ico
Normal file
|
After Width: | Height: | Size: 566 B |
47
examples/alan-turing-portal/styles/prism.css
Normal file
@@ -0,0 +1,47 @@
|
||||
pre[class*='language-'] {
|
||||
color: theme('colors.zinc.100');
|
||||
}
|
||||
|
||||
.token.tag,
|
||||
.token.class-name,
|
||||
.token.selector,
|
||||
.token.selector .class,
|
||||
.token.selector.class,
|
||||
.token.function {
|
||||
color: theme('colors.pink.400');
|
||||
}
|
||||
|
||||
.token.attr-name,
|
||||
.token.keyword,
|
||||
.token.rule,
|
||||
.token.pseudo-class,
|
||||
.token.important {
|
||||
color: theme('colors.zinc.300');
|
||||
}
|
||||
|
||||
.token.module {
|
||||
color: theme('colors.pink.400');
|
||||
}
|
||||
|
||||
.token.attr-value,
|
||||
.token.class,
|
||||
.token.string,
|
||||
.token.property {
|
||||
color: theme('colors.teal.300');
|
||||
}
|
||||
|
||||
.token.punctuation,
|
||||
.token.attr-equals {
|
||||
color: theme('colors.zinc.500');
|
||||
}
|
||||
|
||||
.token.unit,
|
||||
.language-css .token.function {
|
||||
color: theme('colors.sky.200');
|
||||
}
|
||||
|
||||
.token.comment,
|
||||
.token.operator,
|
||||
.token.combinator {
|
||||
color: theme('colors.zinc.400');
|
||||
}
|
||||
13
examples/alan-turing-portal/styles/tailwind.css
Normal file
@@ -0,0 +1,13 @@
|
||||
@import 'tailwindcss/base';
|
||||
@import 'tailwindcss/components';
|
||||
@import './prism.css';
|
||||
@import 'tailwindcss/utilities';
|
||||
|
||||
.index-text ul,
|
||||
.index-text p {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.index-text h2 {
|
||||
margin-top: 1rem;
|
||||
}
|
||||
308
examples/alan-turing-portal/tailwind.config.js
Normal file
@@ -0,0 +1,308 @@
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
module.exports = {
|
||||
content: [
|
||||
"./app/**/*.{js,ts,jsx,tsx,mdx}",
|
||||
"./content/**/*.{js,ts,jsx,tsx,mdx}",
|
||||
"./pages/**/*.{js,ts,jsx,tsx,mdx}",
|
||||
"./components/**/*.{js,ts,jsx,tsx,mdx}",
|
||||
],
|
||||
darkMode: 'class',
|
||||
plugins: [require('@tailwindcss/typography'), require('@tailwindcss/forms')],
|
||||
theme: {
|
||||
fontSize: {
|
||||
xs: ['0.8125rem', { lineHeight: '1.5rem' }],
|
||||
sm: ['0.875rem', { lineHeight: '1.5rem' }],
|
||||
base: ['1rem', { lineHeight: '1.75rem' }],
|
||||
lg: ['1.125rem', { lineHeight: '1.75rem' }],
|
||||
xl: ['1.25rem', { lineHeight: '2rem' }],
|
||||
'2xl': ['1.5rem', { lineHeight: '2rem' }],
|
||||
'3xl': ['1.875rem', { lineHeight: '2.25rem' }],
|
||||
'4xl': ['2rem', { lineHeight: '2.5rem' }],
|
||||
'5xl': ['3rem', { lineHeight: '3.5rem' }],
|
||||
'6xl': ['3.75rem', { lineHeight: '1' }],
|
||||
'7xl': ['4.5rem', { lineHeight: '1' }],
|
||||
'8xl': ['6rem', { lineHeight: '1' }],
|
||||
'9xl': ['8rem', { lineHeight: '1' }],
|
||||
},
|
||||
typography: (theme) => ({
|
||||
invert: {
|
||||
css: {
|
||||
'--tw-prose-body': 'var(--tw-prose-invert-body)',
|
||||
'--tw-prose-headings': 'var(--tw-prose-invert-headings)',
|
||||
'--tw-prose-links': 'var(--tw-prose-invert-links)',
|
||||
'--tw-prose-links-hover': 'var(--tw-prose-invert-links-hover)',
|
||||
'--tw-prose-underline': 'var(--tw-prose-invert-underline)',
|
||||
'--tw-prose-underline-hover':
|
||||
'var(--tw-prose-invert-underline-hover)',
|
||||
'--tw-prose-bold': 'var(--tw-prose-invert-bold)',
|
||||
'--tw-prose-counters': 'var(--tw-prose-invert-counters)',
|
||||
'--tw-prose-bullets': 'var(--tw-prose-invert-bullets)',
|
||||
'--tw-prose-hr': 'var(--tw-prose-invert-hr)',
|
||||
'--tw-prose-quote-borders': 'var(--tw-prose-invert-quote-borders)',
|
||||
'--tw-prose-captions': 'var(--tw-prose-invert-captions)',
|
||||
'--tw-prose-code': 'var(--tw-prose-invert-code)',
|
||||
'--tw-prose-code-bg': 'var(--tw-prose-invert-code-bg)',
|
||||
'--tw-prose-pre-code': 'var(--tw-prose-invert-pre-code)',
|
||||
'--tw-prose-pre-bg': 'var(--tw-prose-invert-pre-bg)',
|
||||
'--tw-prose-pre-border': 'var(--tw-prose-invert-pre-border)',
|
||||
'--tw-prose-th-borders': 'var(--tw-prose-invert-th-borders)',
|
||||
'--tw-prose-td-borders': 'var(--tw-prose-invert-td-borders)',
|
||||
},
|
||||
},
|
||||
DEFAULT: {
|
||||
css: {
|
||||
'--tw-prose-body': theme('colors.zinc.600'),
|
||||
'--tw-prose-headings': theme('colors.zinc.900'),
|
||||
'--tw-prose-links': theme('colors.teal.500'),
|
||||
'--tw-prose-links-hover': theme('colors.teal.600'),
|
||||
'--tw-prose-underline': theme('colors.teal.500 / 0.2'),
|
||||
'--tw-prose-underline-hover': theme('colors.teal.500'),
|
||||
'--tw-prose-bold': theme('colors.zinc.900'),
|
||||
'--tw-prose-counters': theme('colors.zinc.900'),
|
||||
'--tw-prose-bullets': theme('colors.zinc.900'),
|
||||
'--tw-prose-hr': theme('colors.zinc.100'),
|
||||
'--tw-prose-quote-borders': theme('colors.zinc.200'),
|
||||
'--tw-prose-captions': theme('colors.zinc.400'),
|
||||
'--tw-prose-code': theme('colors.zinc.700'),
|
||||
'--tw-prose-code-bg': theme('colors.zinc.300 / 0.2'),
|
||||
'--tw-prose-pre-code': theme('colors.zinc.100'),
|
||||
'--tw-prose-pre-bg': theme('colors.zinc.900'),
|
||||
'--tw-prose-pre-border': 'transparent',
|
||||
'--tw-prose-th-borders': theme('colors.zinc.200'),
|
||||
'--tw-prose-td-borders': theme('colors.zinc.100'),
|
||||
|
||||
'--tw-prose-invert-body': theme('colors.zinc.400'),
|
||||
'--tw-prose-invert-headings': theme('colors.zinc.200'),
|
||||
'--tw-prose-invert-links': theme('colors.teal.400'),
|
||||
'--tw-prose-invert-links-hover': theme('colors.teal.400'),
|
||||
'--tw-prose-invert-underline': theme('colors.teal.400 / 0.3'),
|
||||
'--tw-prose-invert-underline-hover': theme('colors.teal.400'),
|
||||
'--tw-prose-invert-bold': theme('colors.zinc.200'),
|
||||
'--tw-prose-invert-counters': theme('colors.zinc.200'),
|
||||
'--tw-prose-invert-bullets': theme('colors.zinc.200'),
|
||||
'--tw-prose-invert-hr': theme('colors.zinc.700 / 0.4'),
|
||||
'--tw-prose-invert-quote-borders': theme('colors.zinc.500'),
|
||||
'--tw-prose-invert-captions': theme('colors.zinc.500'),
|
||||
'--tw-prose-invert-code': theme('colors.zinc.300'),
|
||||
'--tw-prose-invert-code-bg': theme('colors.zinc.200 / 0.05'),
|
||||
'--tw-prose-invert-pre-code': theme('colors.zinc.100'),
|
||||
'--tw-prose-invert-pre-bg': 'rgb(0 0 0 / 0.4)',
|
||||
'--tw-prose-invert-pre-border': theme('colors.zinc.200 / 0.1'),
|
||||
'--tw-prose-invert-th-borders': theme('colors.zinc.700'),
|
||||
'--tw-prose-invert-td-borders': theme('colors.zinc.800'),
|
||||
|
||||
// Base
|
||||
color: 'var(--tw-prose-body)',
|
||||
lineHeight: theme('lineHeight.7'),
|
||||
'> *': {
|
||||
marginTop: theme('spacing.10'),
|
||||
marginBottom: theme('spacing.10'),
|
||||
},
|
||||
p: {
|
||||
marginTop: theme('spacing.7'),
|
||||
marginBottom: theme('spacing.7'),
|
||||
},
|
||||
|
||||
// Headings
|
||||
'h2, h3': {
|
||||
color: 'var(--tw-prose-headings)',
|
||||
fontWeight: theme('fontWeight.semibold'),
|
||||
},
|
||||
h2: {
|
||||
fontSize: theme('fontSize.xl')[0],
|
||||
lineHeight: theme('lineHeight.7'),
|
||||
marginTop: theme('spacing.20'),
|
||||
marginBottom: theme('spacing.4'),
|
||||
},
|
||||
h3: {
|
||||
fontSize: theme('fontSize.base')[0],
|
||||
lineHeight: theme('lineHeight.7'),
|
||||
marginTop: theme('spacing.16'),
|
||||
marginBottom: theme('spacing.4'),
|
||||
},
|
||||
':is(h2, h3) + *': {
|
||||
marginTop: 0,
|
||||
},
|
||||
|
||||
// Images
|
||||
img: {
|
||||
borderRadius: theme('borderRadius.3xl'),
|
||||
},
|
||||
|
||||
// Inline elements
|
||||
a: {
|
||||
color: 'var(--tw-prose-links)',
|
||||
fontWeight: theme('fontWeight.semibold'),
|
||||
textDecoration: 'underline',
|
||||
textDecorationColor: 'var(--tw-prose-underline)',
|
||||
transitionProperty: 'color, text-decoration-color',
|
||||
transitionDuration: theme('transitionDuration.150'),
|
||||
transitionTimingFunction: theme('transitionTimingFunction.in-out'),
|
||||
},
|
||||
'a:hover': {
|
||||
color: 'var(--tw-prose-links-hover)',
|
||||
textDecorationColor: 'var(--tw-prose-underline-hover)',
|
||||
},
|
||||
strong: {
|
||||
color: 'var(--tw-prose-bold)',
|
||||
fontWeight: theme('fontWeight.semibold'),
|
||||
},
|
||||
code: {
|
||||
display: 'inline-block',
|
||||
color: 'var(--tw-prose-code)',
|
||||
fontSize: theme('fontSize.sm')[0],
|
||||
fontWeight: theme('fontWeight.semibold'),
|
||||
backgroundColor: 'var(--tw-prose-code-bg)',
|
||||
borderRadius: theme('borderRadius.lg'),
|
||||
paddingLeft: theme('spacing.1'),
|
||||
paddingRight: theme('spacing.1'),
|
||||
},
|
||||
'a code': {
|
||||
color: 'inherit',
|
||||
},
|
||||
':is(h2, h3) code': {
|
||||
fontWeight: theme('fontWeight.bold'),
|
||||
},
|
||||
|
||||
// Quotes
|
||||
blockquote: {
|
||||
paddingLeft: theme('spacing.6'),
|
||||
borderLeftWidth: theme('borderWidth.2'),
|
||||
borderLeftColor: 'var(--tw-prose-quote-borders)',
|
||||
fontStyle: 'italic',
|
||||
},
|
||||
|
||||
// Figures
|
||||
figcaption: {
|
||||
color: 'var(--tw-prose-captions)',
|
||||
fontSize: theme('fontSize.sm')[0],
|
||||
lineHeight: theme('lineHeight.6'),
|
||||
marginTop: theme('spacing.3'),
|
||||
},
|
||||
'figcaption > p': {
|
||||
margin: 0,
|
||||
},
|
||||
|
||||
// Lists
|
||||
ul: {
|
||||
listStyleType: 'disc',
|
||||
},
|
||||
ol: {
|
||||
listStyleType: 'decimal',
|
||||
},
|
||||
'ul, ol': {
|
||||
paddingLeft: theme('spacing.6'),
|
||||
},
|
||||
li: {
|
||||
marginTop: theme('spacing.6'),
|
||||
marginBottom: theme('spacing.6'),
|
||||
paddingLeft: theme('spacing[3.5]'),
|
||||
},
|
||||
'li::marker': {
|
||||
fontSize: theme('fontSize.sm')[0],
|
||||
fontWeight: theme('fontWeight.semibold'),
|
||||
},
|
||||
'ol > li::marker': {
|
||||
color: 'var(--tw-prose-counters)',
|
||||
},
|
||||
'ul > li::marker': {
|
||||
color: 'var(--tw-prose-bullets)',
|
||||
},
|
||||
'li :is(ol, ul)': {
|
||||
marginTop: theme('spacing.4'),
|
||||
marginBottom: theme('spacing.4'),
|
||||
},
|
||||
'li :is(li, p)': {
|
||||
marginTop: theme('spacing.3'),
|
||||
marginBottom: theme('spacing.3'),
|
||||
},
|
||||
|
||||
// Code blocks
|
||||
pre: {
|
||||
color: 'var(--tw-prose-pre-code)',
|
||||
fontSize: theme('fontSize.sm')[0],
|
||||
fontWeight: theme('fontWeight.medium'),
|
||||
backgroundColor: 'var(--tw-prose-pre-bg)',
|
||||
borderRadius: theme('borderRadius.3xl'),
|
||||
padding: theme('spacing.8'),
|
||||
overflowX: 'auto',
|
||||
border: '1px solid',
|
||||
borderColor: 'var(--tw-prose-pre-border)',
|
||||
},
|
||||
'pre code': {
|
||||
display: 'inline',
|
||||
color: 'inherit',
|
||||
fontSize: 'inherit',
|
||||
fontWeight: 'inherit',
|
||||
backgroundColor: 'transparent',
|
||||
borderRadius: 0,
|
||||
padding: 0,
|
||||
},
|
||||
|
||||
// Horizontal rules
|
||||
hr: {
|
||||
marginTop: theme('spacing.20'),
|
||||
marginBottom: theme('spacing.20'),
|
||||
borderTopWidth: '1px',
|
||||
borderColor: 'var(--tw-prose-hr)',
|
||||
'@screen lg': {
|
||||
marginLeft: `calc(${theme('spacing.12')} * -1)`,
|
||||
marginRight: `calc(${theme('spacing.12')} * -1)`,
|
||||
},
|
||||
},
|
||||
|
||||
// Tables
|
||||
table: {
|
||||
width: '100%',
|
||||
tableLayout: 'auto',
|
||||
textAlign: 'left',
|
||||
fontSize: theme('fontSize.sm')[0],
|
||||
},
|
||||
thead: {
|
||||
borderBottomWidth: '1px',
|
||||
borderBottomColor: 'var(--tw-prose-th-borders)',
|
||||
},
|
||||
'thead th': {
|
||||
color: 'var(--tw-prose-headings)',
|
||||
fontWeight: theme('fontWeight.semibold'),
|
||||
verticalAlign: 'bottom',
|
||||
paddingBottom: theme('spacing.2'),
|
||||
},
|
||||
'thead th:not(:first-child)': {
|
||||
paddingLeft: theme('spacing.2'),
|
||||
},
|
||||
'thead th:not(:last-child)': {
|
||||
paddingRight: theme('spacing.2'),
|
||||
},
|
||||
'tbody tr': {
|
||||
borderBottomWidth: '1px',
|
||||
borderBottomColor: 'var(--tw-prose-td-borders)',
|
||||
},
|
||||
'tbody tr:last-child': {
|
||||
borderBottomWidth: 0,
|
||||
},
|
||||
'tbody td': {
|
||||
verticalAlign: 'baseline',
|
||||
},
|
||||
tfoot: {
|
||||
borderTopWidth: '1px',
|
||||
borderTopColor: 'var(--tw-prose-th-borders)',
|
||||
},
|
||||
'tfoot td': {
|
||||
verticalAlign: 'top',
|
||||
},
|
||||
':is(tbody, tfoot) td': {
|
||||
paddingTop: theme('spacing.2'),
|
||||
paddingBottom: theme('spacing.2'),
|
||||
},
|
||||
':is(tbody, tfoot) td:not(:first-child)': {
|
||||
paddingLeft: theme('spacing.2'),
|
||||
},
|
||||
':is(tbody, tfoot) td:not(:last-child)': {
|
||||
paddingRight: theme('spacing.2'),
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
},
|
||||
}
|
||||
14
examples/alan-turing-portal/templates/dataset.md
Normal file
@@ -0,0 +1,14 @@
|
||||
---
|
||||
title: string
|
||||
link-to-publication: url
|
||||
link-to-data: url
|
||||
task-description: string
|
||||
details-of-task: string
|
||||
size-of-dataset: number
|
||||
percentage-abusive: number
|
||||
language: string
|
||||
level-of-annotation: list eg: ["Posts", "Comments", ...]
|
||||
platform: list eg: ["Youtube", "Facebook", ...]
|
||||
medium: list eg: ["Text", "Emojis", "Images", ...]
|
||||
reference: string
|
||||
---
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
title: string
|
||||
data-link: url
|
||||
reference: string
|
||||
---
|
||||
28
examples/alan-turing-portal/tsconfig.json
Normal file
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"lib": [
|
||||
"dom",
|
||||
"dom.iterable",
|
||||
"esnext"
|
||||
],
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"strict": false,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"noEmit": true,
|
||||
"incremental": true,
|
||||
"esModuleInterop": true,
|
||||
"moduleResolution": "node",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"jsx": "preserve"
|
||||
},
|
||||
"include": [
|
||||
"next-env.d.ts",
|
||||
"**/*.ts",
|
||||
"**/*.tsx"
|
||||
],
|
||||
"exclude": [
|
||||
"node_modules"
|
||||
]
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
import VegaLite from "./VegaLite";
|
||||
|
||||
export default function LineChart({
|
||||
data = [],
|
||||
fullWidth = false,
|
||||
title = "",
|
||||
}) {
|
||||
var tmp = data;
|
||||
if (Array.isArray(data)) {
|
||||
tmp = data.map((r, i) => {
|
||||
return { x: r[0], y: r[1] };
|
||||
});
|
||||
}
|
||||
const vegaData = { table: tmp };
|
||||
const spec = {
|
||||
$schema: "https://vega.github.io/schema/vega-lite/v5.json",
|
||||
title,
|
||||
width: 500,
|
||||
height: 300,
|
||||
mark: {
|
||||
type: "line",
|
||||
color: "black",
|
||||
strokeWidth: 1,
|
||||
tooltip: true,
|
||||
},
|
||||
data: {
|
||||
name: "table",
|
||||
},
|
||||
selection: {
|
||||
grid: {
|
||||
type: "interval",
|
||||
bind: "scales",
|
||||
},
|
||||
},
|
||||
encoding: {
|
||||
x: {
|
||||
field: "x",
|
||||
timeUnit: "year",
|
||||
type: "temporal",
|
||||
},
|
||||
y: {
|
||||
field: "y",
|
||||
type: "quantitative",
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
return <VegaLite fullWidth={fullWidth} data={vegaData} spec={spec} />;
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
// Wrapper for the Vega Lite component
|
||||
import { VegaLite as VegaLiteOg } from "react-vega";
|
||||
|
||||
export default function VegaLite(props) {
|
||||
return <VegaLiteOg {...props} />;
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
# Data
|
||||
|
||||
This is the README.md this project.
|
||||
|
||||
## Table
|
||||
|
||||
<Table url="data_1.csv" />
|
||||
|
||||
## Vega Lite Line Chart from URL
|
||||
|
||||
<VegaLite spec={ { "$schema": "https://vega.github.io/schema/vega-lite/v5.json", "data": {"url": "data_2.csv"}, "width": 550, "height": 250, "mark": "line", "encoding": { "x": {"field": "Time", "type": "temporal"}, "y": {"field": "Anomaly (deg C)", "type": "quantitative"}, "tooltip": {"field": "Anomaly (deg C)", "type": "quantitative"} } } } />
|
||||
@@ -1,16 +0,0 @@
|
||||
import papa from "papaparse";
|
||||
|
||||
const parseCsv = (csv) => {
|
||||
csv = csv.trim();
|
||||
const rawdata = papa.parse(csv, { header: true });
|
||||
const cols = rawdata.meta.fields.map((r, i) => {
|
||||
return { key: r, name: r };
|
||||
});
|
||||
|
||||
return {
|
||||
rows: rawdata.data,
|
||||
fields: cols,
|
||||
};
|
||||
};
|
||||
|
||||
export default parseCsv;
|
||||
@@ -1,42 +0,0 @@
|
||||
import { GetStaticProps } from 'next';
|
||||
import { promises as fs } from 'fs';
|
||||
import path from 'path';
|
||||
import parse from '../lib/markdown';
|
||||
import DRD from '../components/DRD';
|
||||
|
||||
export const getServerSideProps = async (context) => {
|
||||
const indexFile = path.join(process.cwd(), '/content/' + context.params.path.join('/') + '/index.md');
|
||||
const readme = await fs.readFile(indexFile, 'utf8');
|
||||
let { mdxSource, frontMatter } = await parse(readme, '.mdx');
|
||||
return {
|
||||
props: {
|
||||
mdxSource,
|
||||
frontMatter,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export default function DatasetPage({ mdxSource, frontMatter }) {
|
||||
return (
|
||||
<div className="prose mx-auto">
|
||||
<header>
|
||||
<div className="mb-6">
|
||||
<>
|
||||
<h1>{frontMatter.title}</h1>
|
||||
{frontMatter.author && (
|
||||
<div className="-mt-6">
|
||||
<p className="opacity-60 pl-1">{frontMatter.author}</p>
|
||||
</div>
|
||||
)}
|
||||
{frontMatter.description && (
|
||||
<p className="description">{frontMatter.description}</p>
|
||||
)}
|
||||
</>
|
||||
</div>
|
||||
</header>
|
||||
<main>
|
||||
<DRD source={mdxSource} />
|
||||
</main>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
// Next.js API route support: https://nextjs.org/docs/api-routes/introduction
|
||||
import type { NextApiRequest, NextApiResponse } from 'next'
|
||||
import { promises as fs } from 'fs';
|
||||
import path from 'path';
|
||||
|
||||
export default async function handler(
|
||||
req: NextApiRequest,
|
||||
res: NextApiResponse<string>
|
||||
) {
|
||||
const contentDir = path.join(process.cwd(), '/content');
|
||||
const datasets = await fs.readdir(contentDir);
|
||||
const query = req.query;
|
||||
const { fileName } = query;
|
||||
const dataFile = path.join(
|
||||
process.cwd(),
|
||||
'/content/' + datasets[0] + '/' + fileName
|
||||
);
|
||||
const data = await fs.readFile(dataFile, 'utf8');
|
||||
res.status(200).send(data)
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
import { GetStaticProps } from 'next';
|
||||
import { promises as fs } from 'fs';
|
||||
import path from 'path';
|
||||
import parse from '../lib/markdown';
|
||||
import DRD from '../components/DRD';
|
||||
|
||||
export const getStaticProps = async (context) => {
|
||||
const indexFile = path.join(process.cwd(), '/content/index.md');
|
||||
const readme = await fs.readFile(indexFile, 'utf8');
|
||||
let { mdxSource, frontMatter } = await parse(readme, '.mdx');
|
||||
return {
|
||||
props: {
|
||||
mdxSource,
|
||||
frontMatter,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export default function DatasetPage({ mdxSource, frontMatter }) {
|
||||
return (
|
||||
<div className="prose mx-auto">
|
||||
<header>
|
||||
<div className="mb-6">
|
||||
<>
|
||||
<h1>{frontMatter.title}</h1>
|
||||
{frontMatter.author && (
|
||||
<div className="-mt-6">
|
||||
<p className="opacity-60 pl-1">{frontMatter.author}</p>
|
||||
</div>
|
||||
)}
|
||||
{frontMatter.description && (
|
||||
<p className="description">{frontMatter.description}</p>
|
||||
)}
|
||||
</>
|
||||
</div>
|
||||
</header>
|
||||
<main>
|
||||
<DRD source={mdxSource} />
|
||||
</main>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
Year,Temp Anomaly
|
||||
1850,-0.418
|
||||
2020,0.923
|
||||
|
@@ -1,173 +0,0 @@
|
||||
Time,Anomaly (deg C),Lower confidence limit (2.5%),Upper confidence limit (97.5%)
|
||||
1850,-0.41765878,-0.589203,-0.24611452
|
||||
1851,-0.2333498,-0.41186792,-0.054831687
|
||||
1852,-0.22939907,-0.40938243,-0.04941572
|
||||
1853,-0.27035445,-0.43000934,-0.110699534
|
||||
1854,-0.29163003,-0.43282393,-0.15043613
|
||||
1855,-0.2969512,-0.43935776,-0.15454465
|
||||
1856,-0.32035372,-0.46809322,-0.1726142
|
||||
1857,-0.46723005,-0.61632216,-0.31813794
|
||||
1858,-0.3887657,-0.53688604,-0.24064532
|
||||
1859,-0.28119546,-0.42384982,-0.13854107
|
||||
1860,-0.39016518,-0.5389766,-0.24135375
|
||||
1861,-0.42927712,-0.5972301,-0.26132414
|
||||
1862,-0.53639776,-0.7037096,-0.36908585
|
||||
1863,-0.3443432,-0.5341645,-0.1545219
|
||||
1864,-0.4654367,-0.6480974,-0.282776
|
||||
1865,-0.33258784,-0.5246526,-0.14052312
|
||||
1866,-0.34126064,-0.52183825,-0.16068307
|
||||
1867,-0.35696334,-0.55306214,-0.16086453
|
||||
1868,-0.35196072,-0.52965826,-0.17426313
|
||||
1869,-0.31657043,-0.47642276,-0.15671812
|
||||
1870,-0.32789087,-0.46867347,-0.18710826
|
||||
1871,-0.3685807,-0.5141493,-0.22301209
|
||||
1872,-0.32804197,-0.4630833,-0.19300064
|
||||
1873,-0.34133235,-0.4725396,-0.21012507
|
||||
1874,-0.3732512,-0.5071426,-0.2393598
|
||||
1875,-0.37562594,-0.514041,-0.23721085
|
||||
1876,-0.42410994,-0.56287116,-0.28534868
|
||||
1877,-0.101108834,-0.22982001,0.027602348
|
||||
1878,-0.011315193,-0.13121258,0.10858219
|
||||
1879,-0.30363432,-0.43406433,-0.1732043
|
||||
1880,-0.31583208,-0.44015095,-0.19151321
|
||||
1881,-0.23224552,-0.35793498,-0.10655605
|
||||
1882,-0.29553008,-0.4201501,-0.17091006
|
||||
1883,-0.3464744,-0.4608177,-0.23213111
|
||||
1884,-0.49232006,-0.6026686,-0.38197154
|
||||
1885,-0.47112358,-0.5830682,-0.35917896
|
||||
1886,-0.42090362,-0.5225382,-0.31926903
|
||||
1887,-0.49878576,-0.61655986,-0.3810117
|
||||
1888,-0.37937889,-0.49332377,-0.265434
|
||||
1889,-0.24989556,-0.37222093,-0.12757017
|
||||
1890,-0.50685817,-0.6324095,-0.3813068
|
||||
1891,-0.40131494,-0.5373699,-0.26525995
|
||||
1892,-0.5075585,-0.64432853,-0.3707885
|
||||
1893,-0.49461925,-0.6315314,-0.35770702
|
||||
1894,-0.48376393,-0.6255681,-0.34195974
|
||||
1895,-0.4487516,-0.58202064,-0.3154826
|
||||
1896,-0.28400728,-0.4174015,-0.15061308
|
||||
1897,-0.25980017,-0.39852425,-0.12107607
|
||||
1898,-0.48579213,-0.6176492,-0.35393503
|
||||
1899,-0.35543364,-0.48639694,-0.22447036
|
||||
1900,-0.23447904,-0.3669676,-0.10199049
|
||||
1901,-0.29342857,-0.42967388,-0.15718324
|
||||
1902,-0.43898427,-0.5754281,-0.30254042
|
||||
1903,-0.5333264,-0.66081935,-0.40583345
|
||||
1904,-0.5975614,-0.7288325,-0.46629035
|
||||
1905,-0.40775132,-0.5350291,-0.28047356
|
||||
1906,-0.3191393,-0.45052385,-0.18775477
|
||||
1907,-0.5041577,-0.6262818,-0.38203365
|
||||
1908,-0.5138707,-0.63748026,-0.3902612
|
||||
1909,-0.5357649,-0.6526296,-0.41890016
|
||||
1910,-0.5310242,-0.6556868,-0.40636164
|
||||
1911,-0.5392051,-0.66223973,-0.4161705
|
||||
1912,-0.47567302,-0.5893311,-0.36201498
|
||||
1913,-0.46715254,-0.5893755,-0.34492958
|
||||
1914,-0.2625924,-0.38276345,-0.1424214
|
||||
1915,-0.19184391,-0.32196194,-0.06172589
|
||||
1916,-0.42020997,-0.5588941,-0.28152588
|
||||
1917,-0.54301953,-0.6921192,-0.3939199
|
||||
1918,-0.42458433,-0.58198184,-0.26718682
|
||||
1919,-0.32551822,-0.48145813,-0.1695783
|
||||
1920,-0.2985808,-0.44860035,-0.14856121
|
||||
1921,-0.24067703,-0.38175339,-0.09960067
|
||||
1922,-0.33922812,-0.46610323,-0.21235302
|
||||
1923,-0.31793055,-0.444173,-0.1916881
|
||||
1924,-0.3120622,-0.4388317,-0.18529275
|
||||
1925,-0.28242525,-0.4147755,-0.15007503
|
||||
1926,-0.12283547,-0.25264767,0.006976739
|
||||
1927,-0.22940508,-0.35135695,-0.10745319
|
||||
1928,-0.20676155,-0.33881804,-0.074705064
|
||||
1929,-0.39275664,-0.52656746,-0.25894582
|
||||
1930,-0.1768054,-0.29041144,-0.06319936
|
||||
1931,-0.10339768,-0.2126916,0.0058962475
|
||||
1932,-0.14546166,-0.25195515,-0.0389682
|
||||
1933,-0.32234442,-0.4271004,-0.21758842
|
||||
1934,-0.17433685,-0.27400395,-0.07466974
|
||||
1935,-0.20605922,-0.30349734,-0.10862111
|
||||
1936,-0.16952093,-0.26351926,-0.07552261
|
||||
1937,-0.01919893,-0.11975875,0.08136089
|
||||
1938,-0.012200732,-0.11030374,0.08590227
|
||||
1939,-0.040797167,-0.14670466,0.065110326
|
||||
1940,0.07593584,-0.04194966,0.19382134
|
||||
1941,0.038129337,-0.16225387,0.23851255
|
||||
1942,0.0014060909,-0.1952124,0.19802457
|
||||
1943,0.0064140745,-0.19959097,0.21241911
|
||||
1944,0.14410514,-0.054494828,0.3427051
|
||||
1945,0.043088365,-0.15728289,0.24345961
|
||||
1946,-0.1188128,-0.2659574,0.028331792
|
||||
1947,-0.091205545,-0.23179041,0.04937931
|
||||
1948,-0.12466127,-0.25913337,0.009810844
|
||||
1949,-0.14380224,-0.2540775,-0.033526987
|
||||
1950,-0.22662179,-0.33265698,-0.12058662
|
||||
1951,-0.06115397,-0.15035024,0.028042298
|
||||
1952,0.015354565,-0.08293597,0.11364509
|
||||
1953,0.07763074,-0.020529618,0.1757911
|
||||
1954,-0.11675021,-0.20850271,-0.024997713
|
||||
1955,-0.19730993,-0.28442997,-0.1101899
|
||||
1956,-0.2631656,-0.33912563,-0.18720557
|
||||
1957,-0.035334926,-0.10056862,0.029898768
|
||||
1958,-0.017632553,-0.083074555,0.04780945
|
||||
1959,-0.048004825,-0.11036375,0.0143540995
|
||||
1960,-0.115487024,-0.17416587,-0.056808177
|
||||
1961,-0.019997388,-0.07078052,0.030785747
|
||||
1962,-0.06405444,-0.11731443,-0.010794453
|
||||
1963,-0.03680589,-0.09057008,0.016958294
|
||||
1964,-0.30586675,-0.34949213,-0.26224136
|
||||
1965,-0.2043879,-0.25357357,-0.15520222
|
||||
1966,-0.14888458,-0.19839221,-0.09937696
|
||||
1967,-0.11751631,-0.16062479,-0.07440783
|
||||
1968,-0.1686323,-0.21325313,-0.124011464
|
||||
1969,-0.031366713,-0.07186544,0.009132013
|
||||
1970,-0.08510657,-0.12608096,-0.04413217
|
||||
1971,-0.20593274,-0.24450706,-0.16735843
|
||||
1972,-0.0938271,-0.13171694,-0.05593726
|
||||
1973,0.04993336,0.013468528,0.086398184
|
||||
1974,-0.17253734,-0.21022376,-0.1348509
|
||||
1975,-0.11075424,-0.15130512,-0.07020335
|
||||
1976,-0.21586166,-0.25588378,-0.17583954
|
||||
1977,0.10308852,0.060056705,0.14612034
|
||||
1978,0.0052557723,-0.034576867,0.04508841
|
||||
1979,0.09085813,0.062358618,0.119357646
|
||||
1980,0.19607207,0.162804,0.22934014
|
||||
1981,0.25001204,0.21939126,0.28063282
|
||||
1982,0.034263328,-0.005104665,0.07363132
|
||||
1983,0.22383861,0.18807402,0.2596032
|
||||
1984,0.04800471,0.011560736,0.08444869
|
||||
1985,0.04972978,0.015663471,0.08379609
|
||||
1986,0.09568697,0.064408,0.12696595
|
||||
1987,0.2430264,0.21218552,0.27386728
|
||||
1988,0.28215173,0.2470353,0.31726816
|
||||
1989,0.17925027,0.14449838,0.21400215
|
||||
1990,0.36056247,0.32455227,0.39657268
|
||||
1991,0.33889654,0.30403617,0.3737569
|
||||
1992,0.124896795,0.09088206,0.15891153
|
||||
1993,0.16565846,0.12817313,0.2031438
|
||||
1994,0.23354977,0.19841294,0.2686866
|
||||
1995,0.37686616,0.34365577,0.41007656
|
||||
1996,0.2766894,0.24318004,0.31019878
|
||||
1997,0.4223085,0.39009082,0.4545262
|
||||
1998,0.57731646,0.54304415,0.6115888
|
||||
1999,0.32448497,0.29283476,0.35613516
|
||||
2000,0.3310848,0.29822788,0.36394167
|
||||
2001,0.48928034,0.4580683,0.5204924
|
||||
2002,0.5434665,0.51278186,0.57415116
|
||||
2003,0.5441702,0.5112426,0.5770977
|
||||
2004,0.46737072,0.43433833,0.5004031
|
||||
2005,0.60686255,0.5757053,0.6380198
|
||||
2006,0.5725527,0.541973,0.60313237
|
||||
2007,0.5917013,0.56135315,0.6220495
|
||||
2008,0.46564984,0.43265733,0.49864236
|
||||
2009,0.5967817,0.56525564,0.6283077
|
||||
2010,0.68037146,0.649076,0.7116669
|
||||
2011,0.53769773,0.5060012,0.5693943
|
||||
2012,0.5776071,0.5448553,0.6103589
|
||||
2013,0.6235754,0.5884838,0.6586669
|
||||
2014,0.67287165,0.63890487,0.7068384
|
||||
2015,0.82511437,0.79128706,0.8589417
|
||||
2016,0.93292713,0.90176356,0.96409065
|
||||
2017,0.84517425,0.81477475,0.87557375
|
||||
2018,0.762654,0.731052,0.79425603
|
||||
2019,0.8910726,0.85678726,0.92535794
|
||||
2020,0.9227938,0.8882121,0.9573755
|
||||
2021,0.6640137,0.5372486,0.79077876
|
||||
|
@@ -1,4 +0,0 @@
|
||||
<svg width="283" height="64" viewBox="0 0 283 64" fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M141.04 16c-11.04 0-19 7.2-19 18s8.96 18 20 18c6.67 0 12.55-2.64 16.19-7.09l-7.65-4.42c-2.02 2.21-5.09 3.5-8.54 3.5-4.79 0-8.86-2.5-10.37-6.5h28.02c.22-1.12.35-2.28.35-3.5 0-10.79-7.96-17.99-19-17.99zm-9.46 14.5c1.25-3.99 4.67-6.5 9.45-6.5 4.79 0 8.21 2.51 9.45 6.5h-18.9zM248.72 16c-11.04 0-19 7.2-19 18s8.96 18 20 18c6.67 0 12.55-2.64 16.19-7.09l-7.65-4.42c-2.02 2.21-5.09 3.5-8.54 3.5-4.79 0-8.86-2.5-10.37-6.5h28.02c.22-1.12.35-2.28.35-3.5 0-10.79-7.96-17.99-19-17.99zm-9.45 14.5c1.25-3.99 4.67-6.5 9.45-6.5 4.79 0 8.21 2.51 9.45 6.5h-18.9zM200.24 34c0 6 3.92 10 10 10 4.12 0 7.21-1.87 8.8-4.92l7.68 4.43c-3.18 5.3-9.14 8.49-16.48 8.49-11.05 0-19-7.2-19-18s7.96-18 19-18c7.34 0 13.29 3.19 16.48 8.49l-7.68 4.43c-1.59-3.05-4.68-4.92-8.8-4.92-6.07 0-10 4-10 10zm82.48-29v46h-9V5h9zM36.95 0L73.9 64H0L36.95 0zm92.38 5l-27.71 48L73.91 5H84.3l17.32 30 17.32-30h10.39zm58.91 12v9.69c-1-.29-2.06-.49-3.2-.49-5.81 0-10 4-10 10V51h-9V17h9v9.2c0-5.08 5.91-9.2 13.2-9.2z" fill="#000"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.1 KiB |
@@ -1,129 +0,0 @@
|
||||
.container {
|
||||
padding: 0 2rem;
|
||||
}
|
||||
|
||||
.main {
|
||||
min-height: 100vh;
|
||||
padding: 4rem 0;
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.footer {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
padding: 2rem 0;
|
||||
border-top: 1px solid #eaeaea;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.footer a {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.title a {
|
||||
color: #0070f3;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.title a:hover,
|
||||
.title a:focus,
|
||||
.title a:active {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.title {
|
||||
margin: 0;
|
||||
line-height: 1.15;
|
||||
font-size: 4rem;
|
||||
}
|
||||
|
||||
.title,
|
||||
.description {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.description {
|
||||
margin: 4rem 0;
|
||||
line-height: 1.5;
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
.code {
|
||||
background: #fafafa;
|
||||
border-radius: 5px;
|
||||
padding: 0.75rem;
|
||||
font-size: 1.1rem;
|
||||
font-family: Menlo, Monaco, Lucida Console, Liberation Mono, DejaVu Sans Mono,
|
||||
Bitstream Vera Sans Mono, Courier New, monospace;
|
||||
}
|
||||
|
||||
.grid {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-wrap: wrap;
|
||||
max-width: 800px;
|
||||
}
|
||||
|
||||
.card {
|
||||
margin: 1rem;
|
||||
padding: 1.5rem;
|
||||
text-align: left;
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
border: 1px solid #eaeaea;
|
||||
border-radius: 10px;
|
||||
transition: color 0.15s ease, border-color 0.15s ease;
|
||||
max-width: 300px;
|
||||
}
|
||||
|
||||
.card:hover,
|
||||
.card:focus,
|
||||
.card:active {
|
||||
color: #0070f3;
|
||||
border-color: #0070f3;
|
||||
}
|
||||
|
||||
.card h2 {
|
||||
margin: 0 0 1rem 0;
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
.card p {
|
||||
margin: 0;
|
||||
font-size: 1.25rem;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.logo {
|
||||
height: 1em;
|
||||
margin-left: 0.5rem;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.grid {
|
||||
width: 100%;
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.card,
|
||||
.footer {
|
||||
border-color: #222;
|
||||
}
|
||||
.code {
|
||||
background: #111;
|
||||
}
|
||||
.logo img {
|
||||
filter: invert(1);
|
||||
}
|
||||
}
|
||||
@@ -10,24 +10,24 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@heroicons/react": "^2.0.17",
|
||||
"@types/node": "18.16.0",
|
||||
"@types/react": "18.0.38",
|
||||
"@types/react-dom": "18.0.11",
|
||||
"eslint": "8.39.0",
|
||||
"eslint-config-next": "13.3.1",
|
||||
"next": "13.3.1",
|
||||
"next-seo": "^6.0.0",
|
||||
"octokit": "^2.0.14",
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0",
|
||||
"react-markdown": "^8.0.7",
|
||||
"remark-gfm": "^3.0.1",
|
||||
"typescript": "5.0.4"
|
||||
"remark-gfm": "^3.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tailwindcss/typography": "^0.5.9",
|
||||
"autoprefixer": "^10.4.14",
|
||||
"postcss": "^8.4.23",
|
||||
"tailwindcss": "^3.3.1"
|
||||
"tailwindcss": "^3.3.1",
|
||||
"eslint": "8.39.0",
|
||||
"eslint-config-next": "13.3.1",
|
||||
"typescript": "5.0.4",
|
||||
"@types/node": "18.16.0",
|
||||
"@types/react": "18.0.38",
|
||||
"@types/react-dom": "18.0.11"
|
||||
}
|
||||
}
|
||||
|
||||
3
examples/fiverthirtyeight-example/.eslintrc.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"extends": "next/core-web-vitals"
|
||||
}
|
||||
35
examples/fiverthirtyeight-example/.gitignore
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
/.pnp
|
||||
.pnp.js
|
||||
|
||||
# testing
|
||||
/coverage
|
||||
|
||||
# next.js
|
||||
/.next/
|
||||
/out/
|
||||
|
||||
# production
|
||||
/build
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
*.pem
|
||||
|
||||
# debug
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
# local env files
|
||||
.env*.local
|
||||
|
||||
# vercel
|
||||
.vercel
|
||||
|
||||
# typescript
|
||||
*.tsbuildinfo
|
||||
next-env.d.ts
|
||||
@@ -8,6 +8,8 @@ First, run the development server:
|
||||
npm run dev
|
||||
# or
|
||||
yarn dev
|
||||
# or
|
||||
pnpm dev
|
||||
```
|
||||
|
||||
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
|
||||
@@ -18,6 +20,8 @@ You can start editing the page by modifying `pages/index.tsx`. The page auto-upd
|
||||
|
||||
The `pages/api` directory is mapped to `/api/*`. Files in this directory are treated as [API routes](https://nextjs.org/docs/api-routes/introduction) instead of React pages.
|
||||
|
||||
This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font.
|
||||
|
||||
## Learn More
|
||||
|
||||
To learn more about Next.js, take a look at the following resources:
|
||||
2353
examples/fiverthirtyeight-example/datasets.json
Normal file
@@ -1,7 +1,6 @@
|
||||
/** @type {import('next').NextConfig} */
|
||||
const nextConfig = {
|
||||
reactStrictMode: true,
|
||||
swcMinify: true,
|
||||
}
|
||||
|
||||
module.exports = nextConfig
|
||||
4282
examples/fiverthirtyeight-example/package-lock.json
generated
Normal file
26
examples/fiverthirtyeight-example/package.json
Normal file
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"name": "fiverthirtyeight-example",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "next dev",
|
||||
"build": "next build",
|
||||
"start": "next start",
|
||||
"lint": "next lint"
|
||||
},
|
||||
"dependencies": {
|
||||
"@types/node": "20.1.1",
|
||||
"@types/react": "18.2.6",
|
||||
"@types/react-dom": "18.2.4",
|
||||
"autoprefixer": "10.4.14",
|
||||
"eslint": "8.40.0",
|
||||
"eslint-config-next": "13.4.1",
|
||||
"next": "13.4.1",
|
||||
"postcss": "8.4.23",
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0",
|
||||
"tailwindcss": "3.3.2",
|
||||
"timeago.js": "^4.0.2",
|
||||
"typescript": "5.0.4"
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import '../styles/globals.css'
|
||||
import '@/styles/globals.css'
|
||||
import type { AppProps } from 'next/app'
|
||||
|
||||
export default function App({ Component, pageProps }: AppProps) {
|
||||
13
examples/fiverthirtyeight-example/pages/_document.tsx
Normal file
@@ -0,0 +1,13 @@
|
||||
import { Html, Head, Main, NextScript } from 'next/document'
|
||||
|
||||
export default function Document() {
|
||||
return (
|
||||
<Html lang="en">
|
||||
<Head />
|
||||
<body>
|
||||
<Main />
|
||||
<NextScript />
|
||||
</body>
|
||||
</Html>
|
||||
)
|
||||
}
|
||||
13
examples/fiverthirtyeight-example/pages/api/hello.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
// Next.js API route support: https://nextjs.org/docs/api-routes/introduction
|
||||
import type { NextApiRequest, NextApiResponse } from 'next'
|
||||
|
||||
type Data = {
|
||||
name: string
|
||||
}
|
||||
|
||||
export default function handler(
|
||||
req: NextApiRequest,
|
||||
res: NextApiResponse<Data>
|
||||
) {
|
||||
res.status(200).json({ name: 'John Doe' })
|
||||
}
|
||||
195
examples/fiverthirtyeight-example/pages/index.tsx
Normal file
@@ -0,0 +1,195 @@
|
||||
import Image from 'next/image';
|
||||
import { Inter } from 'next/font/google';
|
||||
import { format } from 'timeago.js'
|
||||
import { promises as fs } from 'fs';
|
||||
import path from 'path';
|
||||
|
||||
const inter = Inter({ subsets: ['latin'] });
|
||||
|
||||
interface Article {
|
||||
date: string;
|
||||
title: string;
|
||||
url: string;
|
||||
}
|
||||
|
||||
interface Dataset {
|
||||
url: string;
|
||||
name: string;
|
||||
displayName: string;
|
||||
articles: Article[];
|
||||
}
|
||||
|
||||
export function MobileItem({dataset} : { dataset: Dataset}) {
|
||||
return (
|
||||
<div className="flex gap-x-2 pb-2 py-4 items-center justify-between border-b border-zinc-600">
|
||||
<div className="flex flex-col">
|
||||
<span className="font-light">{dataset.name}</span>
|
||||
{dataset.articles.map((article) => (
|
||||
<div className='py-1 flex flex-col'>
|
||||
<span className="font-bold hover:underline">{article.title}</span>
|
||||
<span className="font-light text-base">{format(article.date)}</span>{' '}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<div className="flex flex-col justify-start">
|
||||
<a
|
||||
className="border border-zinc-900 font-light px-4 py-1 text-sm transition hover:bg-zinc-900 hover:text-white"
|
||||
href={dataset.url}
|
||||
target="_blank"
|
||||
>
|
||||
info
|
||||
</a>
|
||||
{/*
|
||||
<button>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
fill="currentColor"
|
||||
className="w-12 h-12 text-blue-400 hover:text-blue-300 transition mt-1"
|
||||
>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
d="M12 2.25c-5.385 0-9.75 4.365-9.75 9.75s4.365 9.75 9.75 9.75 9.75-4.365 9.75-9.75S17.385 2.25 12 2.25zm-.53 14.03a.75.75 0 001.06 0l3-3a.75.75 0 10-1.06-1.06l-1.72 1.72V8.25a.75.75 0 00-1.5 0v5.69l-1.72-1.72a.75.75 0 00-1.06 1.06l3 3z"
|
||||
clipRule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</button> */}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function DesktopItem({dataset} : { dataset: Dataset}) {
|
||||
return (
|
||||
<>
|
||||
{dataset.articles.map((article, index) => (
|
||||
<tr className={`${index === (dataset.articles.length - 1) ? 'border-b' : ''} border-zinc-400`}>
|
||||
<td className="py-8 font-light">{index === 0 ? dataset.name : ''}</td>
|
||||
<td>
|
||||
<a className="py-8 font-bold hover:underline" href={article.url}>
|
||||
{article.title}
|
||||
</a>
|
||||
</td>
|
||||
<td className="py-8 font-light text-base min-w-[120px]">{format(article.date)}</td>
|
||||
<td className="py-8">
|
||||
{index === 0 && (
|
||||
<a
|
||||
className="border border-zinc-900 font-light px-[25px] py-2.5 text-sm transition hover:bg-zinc-900 hover:text-white"
|
||||
href={dataset.url}
|
||||
target="_blank"
|
||||
>
|
||||
info
|
||||
</a>
|
||||
)}
|
||||
</td>
|
||||
{/*
|
||||
<td>
|
||||
<button>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
fill="currentColor"
|
||||
className="w-12 h-12 text-blue-400 hover:text-blue-300 transition mt-1"
|
||||
>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
d="M12 2.25c-5.385 0-9.75 4.365-9.75 9.75s4.365 9.75 9.75 9.75 9.75-4.365 9.75-9.75S17.385 2.25 12 2.25zm-.53 14.03a.75.75 0 001.06 0l3-3a.75.75 0 10-1.06-1.06l-1.72 1.72V8.25a.75.75 0 00-1.5 0v5.69l-1.72-1.72a.75.75 0 00-1.06 1.06l3 3z"
|
||||
clipRule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</td>*/}
|
||||
</tr>
|
||||
))}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export async function getStaticProps() {
|
||||
const jsonDirectory = path.join(
|
||||
process.cwd(),
|
||||
'/datasets.json'
|
||||
);
|
||||
const datasetString = await fs.readFile(jsonDirectory, 'utf8');
|
||||
const datasets = JSON.parse(datasetString)
|
||||
return {
|
||||
props: { datasets },
|
||||
};
|
||||
}
|
||||
|
||||
export default function Home( { datasets }: { datasets: Dataset[] }) {
|
||||
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
|
||||
className={`flex min-h-screen flex-col items-center max-w-5xl mx-auto pt-20 px-2.5 ${inter.className}`}
|
||||
>
|
||||
<div>
|
||||
<h1 className="text-[40px] font-bold text-zinc-800 text-center">
|
||||
Our Data
|
||||
</h1>
|
||||
<p className="max-w-2xl text-lg text-center text-zinc-700">
|
||||
We’re sharing the data and code behind some of our articles and
|
||||
graphics. We hope you’ll use it to check our work and to create
|
||||
stories and visualizations of your own.
|
||||
</p>
|
||||
</div>
|
||||
<article className="w-full px-2 md:hidden py-4">{datasets.map(dataset => <MobileItem dataset={dataset} />)}</article>
|
||||
<table className="w-full mt-10 mb-4 hidden md:table">
|
||||
<thead className="border-b-4 pb-2 border-zinc-900">
|
||||
<tr>
|
||||
<th className="uppercase text-left font-light text-xs pb-3">
|
||||
data set
|
||||
</th>
|
||||
<th className="uppercase text-left font-light text-xs pb-3">
|
||||
related content
|
||||
</th>
|
||||
<th className="uppercase text-left font-light text-xs pb-3">
|
||||
last updated
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>{datasets.map(dataset => <DesktopItem dataset={dataset} />)}</tbody>
|
||||
</table>
|
||||
<p className="text-[13px] py-8">
|
||||
Unless otherwise noted, our data sets are available under the{' '}
|
||||
<a
|
||||
className="text-blue-400 hover:underline"
|
||||
href="http://creativecommons.org/licenses/by/4.0/"
|
||||
>
|
||||
Creative Commons Attribution 4.0 International license
|
||||
</a>
|
||||
, and the code is available under the{' '}
|
||||
<a
|
||||
className="text-blue-400 hover:underline"
|
||||
href="http://opensource.org/licenses/MIT"
|
||||
>
|
||||
MIT license
|
||||
</a>
|
||||
. If you find this information useful, please{' '}
|
||||
<a
|
||||
className="text-blue-400 hover:underline"
|
||||
href="mailto:data@fivethirtyeight.com"
|
||||
>
|
||||
let us know
|
||||
</a>
|
||||
.
|
||||
</p>
|
||||
</main>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 25 KiB |
1
examples/fiverthirtyeight-example/public/next.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 394 80"><path fill="#000" d="M262 0h68.5v12.7h-27.2v66.6h-13.6V12.7H262V0ZM149 0v12.7H94v20.4h44.3v12.6H94v21h55v12.6H80.5V0h68.7zm34.3 0h-17.8l63.8 79.4h17.9l-32-39.7 32-39.6h-17.9l-23 28.6-23-28.6zm18.3 56.7-9-11-27.1 33.7h17.8l18.3-22.7z"/><path fill="#000" d="M81 79.3 17 0H0v79.3h13.6V17l50.2 62.3H81Zm252.6-.4c-1 0-1.8-.4-2.5-1s-1.1-1.6-1.1-2.6.3-1.8 1-2.5 1.6-1 2.6-1 1.8.3 2.5 1a3.4 3.4 0 0 1 .6 4.3 3.7 3.7 0 0 1-3 1.8zm23.2-33.5h6v23.3c0 2.1-.4 4-1.3 5.5a9.1 9.1 0 0 1-3.8 3.5c-1.6.8-3.5 1.3-5.7 1.3-2 0-3.7-.4-5.3-1s-2.8-1.8-3.7-3.2c-.9-1.3-1.4-3-1.4-5h6c.1.8.3 1.6.7 2.2s1 1.2 1.6 1.5c.7.4 1.5.5 2.4.5 1 0 1.8-.2 2.4-.6a4 4 0 0 0 1.6-1.8c.3-.8.5-1.8.5-3V45.5zm30.9 9.1a4.4 4.4 0 0 0-2-3.3 7.5 7.5 0 0 0-4.3-1.1c-1.3 0-2.4.2-3.3.5-.9.4-1.6 1-2 1.6a3.5 3.5 0 0 0-.3 4c.3.5.7.9 1.3 1.2l1.8 1 2 .5 3.2.8c1.3.3 2.5.7 3.7 1.2a13 13 0 0 1 3.2 1.8 8.1 8.1 0 0 1 3 6.5c0 2-.5 3.7-1.5 5.1a10 10 0 0 1-4.4 3.5c-1.8.8-4.1 1.2-6.8 1.2-2.6 0-4.9-.4-6.8-1.2-2-.8-3.4-2-4.5-3.5a10 10 0 0 1-1.7-5.6h6a5 5 0 0 0 3.5 4.6c1 .4 2.2.6 3.4.6 1.3 0 2.5-.2 3.5-.6 1-.4 1.8-1 2.4-1.7a4 4 0 0 0 .8-2.4c0-.9-.2-1.6-.7-2.2a11 11 0 0 0-2.1-1.4l-3.2-1-3.8-1c-2.8-.7-5-1.7-6.6-3.2a7.2 7.2 0 0 1-2.4-5.7 8 8 0 0 1 1.7-5 10 10 0 0 1 4.3-3.5c2-.8 4-1.2 6.4-1.2 2.3 0 4.4.4 6.2 1.2 1.8.8 3.2 2 4.3 3.4 1 1.4 1.5 3 1.5 5h-5.8z"/></svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
1
examples/fiverthirtyeight-example/public/vercel.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 283 64"><path fill="black" d="M141 16c-11 0-19 7-19 18s9 18 20 18c7 0 13-3 16-7l-7-5c-2 3-6 4-9 4-5 0-9-3-10-7h28v-3c0-11-8-18-19-18zm-9 15c1-4 4-7 9-7s8 3 9 7h-18zm117-15c-11 0-19 7-19 18s9 18 20 18c6 0 12-3 16-7l-8-5c-2 3-5 4-8 4-5 0-9-3-11-7h28l1-3c0-11-8-18-19-18zm-10 15c2-4 5-7 10-7s8 3 9 7h-19zm-39 3c0 6 4 10 10 10 4 0 7-2 9-5l8 5c-3 5-9 8-17 8-11 0-19-7-19-18s8-18 19-18c8 0 14 3 17 8l-8 5c-2-3-5-5-9-5-6 0-10 4-10 10zm83-29v46h-9V5h9zM37 0l37 64H0L37 0zm92 5-27 48L74 5h10l18 30 17-30h10zm59 12v10l-3-1c-6 0-10 4-10 10v15h-9V17h9v9c0-5 6-9 13-9z"/></svg>
|
||||
|
After Width: | Height: | Size: 629 B |
3
examples/fiverthirtyeight-example/styles/globals.css
Normal file
@@ -0,0 +1,3 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
18
examples/fiverthirtyeight-example/tailwind.config.js
Normal file
@@ -0,0 +1,18 @@
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
module.exports = {
|
||||
content: [
|
||||
'./pages/**/*.{js,ts,jsx,tsx,mdx}',
|
||||
'./components/**/*.{js,ts,jsx,tsx,mdx}',
|
||||
'./app/**/*.{js,ts,jsx,tsx,mdx}',
|
||||
],
|
||||
theme: {
|
||||
extend: {
|
||||
backgroundImage: {
|
||||
'gradient-radial': 'radial-gradient(var(--tw-gradient-stops))',
|
||||
'gradient-conic':
|
||||
'conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))',
|
||||
},
|
||||
},
|
||||
},
|
||||
plugins: [],
|
||||
}
|
||||
23
examples/fiverthirtyeight-example/tsconfig.json
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"lib": ["dom", "dom.iterable", "esnext"],
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"strict": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"noEmit": true,
|
||||
"esModuleInterop": true,
|
||||
"module": "esnext",
|
||||
"moduleResolution": "node",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"jsx": "preserve",
|
||||
"incremental": true,
|
||||
"paths": {
|
||||
"@/*": ["./*"]
|
||||
}
|
||||
},
|
||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||
1
examples/learn-example/README.md
Normal file
@@ -0,0 +1 @@
|
||||
PortalJS Learn Example - https://portaljs.org/docs
|
||||
@@ -7,13 +7,12 @@ import { Mermaid } from '@flowershow/core';
|
||||
// to handle import statements. Instead, you must include components in scope
|
||||
// here.
|
||||
const components = {
|
||||
Table: dynamic(() => import('./Table')),
|
||||
Table: dynamic(() => import('@portaljs/components').then(mod => mod.Table)),
|
||||
Catalog: dynamic(() => import('@portaljs/components').then(mod => mod.Catalog)),
|
||||
mermaid: Mermaid,
|
||||
// Excel: dynamic(() => import('../components/Excel')),
|
||||
// TODO: try and make these dynamic ...
|
||||
Vega: dynamic(() => import('./Vega')),
|
||||
VegaLite: dynamic(() => import('./VegaLite')),
|
||||
LineChart: dynamic(() => import('./LineChart')),
|
||||
Vega: dynamic(() => import('@portaljs/components').then(mod => mod.Vega)),
|
||||
VegaLite: dynamic(() => import('@portaljs/components').then(mod => mod.VegaLite)),
|
||||
LineChart: dynamic(() => import('@portaljs/components').then(mod => mod.LineChart)),
|
||||
} as any;
|
||||
|
||||
export default function DRD({ source }: { source: any }) {
|
||||
@@ -4,5 +4,4 @@ Built with PortalJS
|
||||
|
||||
## Table
|
||||
|
||||
<Table url="data_1.csv" />
|
||||
|
||||
<Table url="data.csv" />
|
||||
@@ -22,7 +22,7 @@ import { serialize } from "next-mdx-remote/serialize";
|
||||
* @format: used to indicate to next-mdx-remote which format to use (md or mdx)
|
||||
* @returns: { mdxSource: mdxSource, frontMatter: ...}
|
||||
*/
|
||||
const parse = async function (source, format) {
|
||||
const parse = async function (source, format, scope) {
|
||||
const { content, data, excerpt } = matter(source, {
|
||||
excerpt: (file, options) => {
|
||||
// Generate an excerpt for the file
|
||||
@@ -91,7 +91,7 @@ const parse = async function (source, format) {
|
||||
],
|
||||
format,
|
||||
},
|
||||
scope: data,
|
||||
scope,
|
||||
}
|
||||
);
|
||||
|
||||
14
examples/learn-example/lib/mddb.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import { MarkdownDB } from "@flowershow/markdowndb";
|
||||
|
||||
const dbPath = "markdown.db";
|
||||
|
||||
const client = new MarkdownDB({
|
||||
client: "sqlite3",
|
||||
connection: {
|
||||
filename: dbPath,
|
||||
},
|
||||
});
|
||||
|
||||
const clientPromise = client.init();
|
||||
|
||||
export default clientPromise;
|
||||
@@ -6,21 +6,22 @@
|
||||
"dev": "next dev",
|
||||
"build": "next build",
|
||||
"start": "next start",
|
||||
"lint": "next lint"
|
||||
"lint": "next lint",
|
||||
"export": "npm run build && next export -o out",
|
||||
"prebuild": "npm run mddb",
|
||||
"mddb": "mddb ./content"
|
||||
},
|
||||
"dependencies": {
|
||||
"@flowershow/core": "^0.4.10",
|
||||
"@flowershow/markdowndb": "^0.1.1",
|
||||
"@flowershow/remark-callouts": "^1.0.0",
|
||||
"@flowershow/remark-embed": "^1.0.0",
|
||||
"@flowershow/remark-wiki-link": "^1.1.2",
|
||||
"@heroicons/react": "^2.0.17",
|
||||
"@opentelemetry/api": "^1.4.0",
|
||||
"@portaljs/components": "^0.1.0",
|
||||
"@tanstack/react-table": "^8.8.5",
|
||||
"@types/node": "18.16.0",
|
||||
"@types/react": "18.2.0",
|
||||
"@types/react-dom": "18.2.0",
|
||||
"eslint": "8.39.0",
|
||||
"eslint-config-next": "13.3.1",
|
||||
"flexsearch": "0.7.21",
|
||||
"gray-matter": "^4.0.3",
|
||||
"hastscript": "^7.2.0",
|
||||
"mdx-mermaid": "2.0.0-rc7",
|
||||
@@ -29,6 +30,7 @@
|
||||
"papaparse": "^5.4.1",
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0",
|
||||
"react-hook-form": "^7.43.9",
|
||||
"react-vega": "^7.6.0",
|
||||
"rehype-autolink-headings": "^6.1.1",
|
||||
"rehype-katex": "^6.0.3",
|
||||
@@ -42,7 +44,13 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tailwindcss/typography": "^0.5.9",
|
||||
"@types/flexsearch": "^0.7.3",
|
||||
"@types/node": "18.16.0",
|
||||
"@types/react": "18.2.0",
|
||||
"@types/react-dom": "18.2.0",
|
||||
"autoprefixer": "^10.4.14",
|
||||
"eslint": "8.39.0",
|
||||
"eslint-config-next": "13.3.1",
|
||||
"postcss": "^8.4.23",
|
||||
"tailwindcss": "^3.3.1"
|
||||
}
|
||||
126
examples/learn-example/pages/[[...path]].tsx
Normal file
@@ -0,0 +1,126 @@
|
||||
import { existsSync, promises as fs } from 'fs';
|
||||
import path from 'path';
|
||||
import parse from '../lib/markdown';
|
||||
|
||||
import DataRichDocument from '../components/DataRichDocument';
|
||||
import clientPromise from '../lib/mddb';
|
||||
|
||||
export const getStaticPaths = async () => {
|
||||
const contentDir = path.join(process.cwd(), '/content/');
|
||||
const contentFolders = await fs.readdir(contentDir, 'utf8');
|
||||
const paths = contentFolders.map((folder: string) =>
|
||||
folder === 'index.md'
|
||||
? { params: { path: [] } }
|
||||
: { params: { path: [folder] } }
|
||||
);
|
||||
return {
|
||||
paths,
|
||||
fallback: false,
|
||||
};
|
||||
};
|
||||
|
||||
export const getStaticProps = async (context) => {
|
||||
let pathToFile = 'index.md';
|
||||
if (context.params.path) {
|
||||
pathToFile = context.params.path.join('/') + '/index.md';
|
||||
}
|
||||
|
||||
let datasets = [];
|
||||
const mddbFileExists = existsSync('markdown.db');
|
||||
if (mddbFileExists) {
|
||||
const mddb = await clientPromise;
|
||||
const datasetsFiles = await mddb.getFiles({
|
||||
extensions: ['md', 'mdx'],
|
||||
});
|
||||
datasets = datasetsFiles
|
||||
.filter((dataset) => dataset.url_path !== '/')
|
||||
.map((dataset) => ({
|
||||
_id: dataset._id,
|
||||
url_path: dataset.url_path,
|
||||
file_path: dataset.file_path,
|
||||
metadata: dataset.metadata,
|
||||
}));
|
||||
}
|
||||
|
||||
const indexFile = path.join(process.cwd(), '/content/' + pathToFile);
|
||||
const readme = await fs.readFile(indexFile, 'utf8');
|
||||
|
||||
let { mdxSource, frontMatter } = await parse(readme, '.mdx', { datasets });
|
||||
|
||||
return {
|
||||
props: {
|
||||
mdxSource,
|
||||
frontMatter: JSON.stringify(frontMatter),
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export default function DatasetPage({ mdxSource, frontMatter }) {
|
||||
frontMatter = JSON.parse(frontMatter);
|
||||
return (
|
||||
<div className="prose dark:prose-invert mx-auto py-8">
|
||||
<header>
|
||||
<div className="mb-6">
|
||||
<>
|
||||
<h1 className="mb-2">{frontMatter.title}</h1>
|
||||
{frontMatter.author && (
|
||||
<p className="my-0">
|
||||
<span className="font-semibold">Author: </span>
|
||||
<span className="my-0">{frontMatter.author}</span>
|
||||
</p>
|
||||
)}
|
||||
{frontMatter.description && (
|
||||
<p className="my-0">
|
||||
<span className="font-semibold">Description: </span>
|
||||
<span className="description my-0">
|
||||
{frontMatter.description}
|
||||
</span>
|
||||
</p>
|
||||
)}
|
||||
{frontMatter.modified && (
|
||||
<p className="my-0">
|
||||
<span className="font-semibold">Modified: </span>
|
||||
<span className="description my-0">
|
||||
{new Date(frontMatter.modified).toLocaleDateString()}
|
||||
</span>
|
||||
</p>
|
||||
)}
|
||||
{frontMatter.files && (
|
||||
<section className="py-6">
|
||||
<h2 className="mt-0">Data files</h2>
|
||||
<table className="table-auto">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>File</th>
|
||||
<th>Format</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{frontMatter.files.map((f) => {
|
||||
const fileName = f.split('/').slice(-1);
|
||||
return (
|
||||
<tr key={`resources-list-${f}`}>
|
||||
<td>
|
||||
<a target="_blank" href={f}>
|
||||
{fileName}
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
{fileName[0].split('.').slice(-1)[0].toUpperCase()}
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
</section>
|
||||
)}
|
||||
</>
|
||||
</div>
|
||||
</header>
|
||||
<main>
|
||||
<DataRichDocument source={mdxSource} />
|
||||
</main>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
8
examples/learn-example/pages/_app.tsx
Normal file
@@ -0,0 +1,8 @@
|
||||
import '../styles/globals.css'
|
||||
import '@portaljs/components/styles.css'
|
||||
|
||||
import type { AppProps } from 'next/app'
|
||||
|
||||
export default function App({ Component, pageProps }: AppProps) {
|
||||
return <Component {...pageProps} />
|
||||
}
|
||||
19
examples/learn-example/pages/_document.tsx
Normal file
@@ -0,0 +1,19 @@
|
||||
import Document, { Html, Main, Head, NextScript } from 'next/document';
|
||||
|
||||
class MyDocument extends Document {
|
||||
render() {
|
||||
return (
|
||||
<Html>
|
||||
<Head>
|
||||
<link rel="icon" href="/favicon.png" />
|
||||
</Head>
|
||||
<body className='bg-white dark:bg-gray-900'>
|
||||
<Main />
|
||||
<NextScript />
|
||||
</body>
|
||||
</Html>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default MyDocument;
|
||||
6
examples/learn-example/postcss.config.js
Normal file
@@ -0,0 +1,6 @@
|
||||
module.exports = {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
}
|
||||
6
examples/learn-example/public/data.csv
Normal file
@@ -0,0 +1,6 @@
|
||||
Year,Rating
|
||||
2008,86
|
||||
2009,96
|
||||
2010,100
|
||||
2011,100
|
||||
2012,97
|
||||
|
BIN
examples/learn-example/public/favicon.png
Normal file
|
After Width: | Height: | Size: 37 KiB |
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"target": "es6",
|
||||
"lib": ["dom", "dom.iterable", "esnext"],
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
1424
package-lock.json
generated
@@ -4,11 +4,10 @@
|
||||
"license": "MIT",
|
||||
"scripts": {},
|
||||
"private": true,
|
||||
"dependencies": {},
|
||||
"devDependencies": {
|
||||
"@babel/preset-react": "^7.14.5",
|
||||
"@nrwl/cypress": "15.9.2",
|
||||
"@nrwl/eslint-plugin-nx": "15.9.2",
|
||||
"@nrwl/eslint-plugin-nx": "^16.0.2",
|
||||
"@nrwl/jest": "15.9.2",
|
||||
"@nrwl/js": "15.9.2",
|
||||
"@nrwl/linter": "15.9.2",
|
||||
|
||||
|
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 627 B After Width: | Height: | Size: 627 B |