Compare commits
62 Commits
alan-turin
...
fivethirty
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e9620992e8 | ||
|
|
336ff819dc | ||
|
|
f610c953e7 | ||
|
|
3f350f8fcd | ||
|
|
714faf9986 | ||
|
|
a954575397 | ||
|
|
ca13e7b9c3 | ||
|
|
f12e007ce4 | ||
|
|
2edf488fe7 | ||
|
|
ce395b4c49 | ||
|
|
51828b85f1 | ||
|
|
d2e9c54c13 | ||
|
|
6705bc1e2d | ||
|
|
7dfde0935e | ||
|
|
3f76bea895 | ||
|
|
f17efce02e | ||
|
|
61b96c20ed | ||
|
|
4cadc50e46 | ||
|
|
684f473e62 | ||
|
|
b963cf2cbb | ||
|
|
43ac5cfb47 | ||
|
|
f6b8ef2190 | ||
|
|
e5c89308d1 | ||
|
|
8b51123290 | ||
|
|
53b64b81c9 | ||
|
|
9fe08fcd1b | ||
|
|
7150150db0 | ||
|
|
5cc312b55b | ||
|
|
5c8431bf39 | ||
|
|
0a1ede10e8 | ||
|
|
45c07f829a | ||
|
|
53ea7957c0 | ||
|
|
0c65a145c8 | ||
|
|
91caeff6c3 | ||
|
|
0f65e253da | ||
|
|
c390a21611 | ||
|
|
dac7d03d05 | ||
|
|
89ba260b70 | ||
|
|
ce847746d2 | ||
|
|
5328492575 | ||
|
|
e52e789314 | ||
|
|
0e8cac7d50 | ||
|
|
2e30c76a3d | ||
|
|
edb2354945 | ||
|
|
5834a4a470 | ||
|
|
90b93e6819 | ||
|
|
ad52721a38 | ||
|
|
cf2a93abfd | ||
|
|
8afb30c96b | ||
|
|
94a3c2a5f0 | ||
|
|
a0620f9255 | ||
|
|
e5513f59a6 | ||
|
|
d73bcc77f3 | ||
|
|
1782f23b84 | ||
|
|
72405162a1 | ||
|
|
982733737d | ||
|
|
ea5802a908 | ||
|
|
229a7b5324 | ||
|
|
014c4c043d | ||
|
|
016f3e20e9 | ||
|
|
169a92d313 | ||
|
|
9c25c71286 |
@@ -1,8 +1,6 @@
|
||||
## Intro
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
Its built on top of [PortalJS](https://portaljs.org/), it allows you to publish datasets, lists of offensive keywords and static pages, all of those are stored as markdown files inside the `content` folder.
|
||||
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>`
|
||||
|
||||
@@ -1,8 +1,41 @@
|
||||
import { useRef } from 'react'
|
||||
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
|
||||
@@ -35,6 +68,125 @@ function MoonIcon(props) {
|
||||
)
|
||||
}
|
||||
|
||||
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')
|
||||
@@ -70,11 +222,13 @@ function ModeToggle() {
|
||||
)
|
||||
}
|
||||
|
||||
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() {
|
||||
let isHomePage = useRouter().pathname === '/'
|
||||
|
||||
let headerRef = useRef()
|
||||
|
||||
return (
|
||||
<>
|
||||
<header
|
||||
@@ -85,7 +239,6 @@ export function Header() {
|
||||
}}
|
||||
>
|
||||
<div
|
||||
ref={headerRef}
|
||||
className="top-0 z-10 h-16 pt-6"
|
||||
style={{ position: 'var(--header-position)' }}
|
||||
>
|
||||
@@ -94,6 +247,10 @@ export function Header() {
|
||||
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 />
|
||||
@@ -103,7 +260,6 @@ export function Header() {
|
||||
</Container>
|
||||
</div>
|
||||
</header>
|
||||
{isHomePage && <div style={{ height: 'var(--content-offset)' }} />}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
---
|
||||
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, Bertie Vidgen, Hannah Rose Kirk, Pica Johansson, Yi-Ling Chung, Mads Guldborg Kjeldgaard Kongsbak, Laila Sprejer, and Philine Zeinert.
|
||||
|
||||
We provide a list of datasets and keywords. If you would like to contribute to our catalogue or add your dataset, please see the instructions for contributing.
|
||||
|
||||
If you use these resources, please cite (and read!) our paper: Directions in Abusive Language Training Data: Garbage In, Garbage Out. And if you would like to find other resources for researching online hate, visit The Alan Turing Institute’s Online Hate Research Hub or read The Alan Turing Institute’s Reading List on Online Hate and Abuse Research.
|
||||
|
||||
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’ by Poletto et al. in Language Resources and Evaluation.
|
||||
|
||||
## How to contribute
|
||||
|
||||
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!)
|
||||
|
||||
- Go to the repo url file and click the "Add file" dropdown and then click on "Create new file".
|
||||

|
||||
|
||||
- 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
|
||||

|
||||
|
||||
- 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
|
||||

|
||||
|
||||
- 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 the pull request on the next page when prompted.
|
||||
|
||||
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.
|
||||
|
||||
@@ -23,12 +23,7 @@ import { serialize } from "next-mdx-remote/serialize";
|
||||
* @returns: { mdxSource: mdxSource, frontMatter: ...}
|
||||
*/
|
||||
const parse = async function (source, format) {
|
||||
const { content, data, excerpt } = matter(source, {
|
||||
excerpt: (file, options) => {
|
||||
// Generate an excerpt for the file
|
||||
file.excerpt = file.content.split("\n\n")[0];
|
||||
},
|
||||
});
|
||||
const { content, data } = matter(source);
|
||||
|
||||
const mdxSource = await serialize(
|
||||
{ value: content, path: format },
|
||||
@@ -56,7 +51,7 @@ const parse = async function (source, format) {
|
||||
[
|
||||
rehypeAutolinkHeadings,
|
||||
{
|
||||
properties: { className: 'heading-link' },
|
||||
properties: { className: "heading-link" },
|
||||
test(element) {
|
||||
return (
|
||||
["h2", "h3", "h4", "h5", "h6"].includes(element.tagName) &&
|
||||
@@ -91,14 +86,12 @@ const parse = async function (source, format) {
|
||||
],
|
||||
format,
|
||||
},
|
||||
scope: data,
|
||||
}
|
||||
);
|
||||
|
||||
return {
|
||||
mdxSource: mdxSource,
|
||||
frontMatter: data,
|
||||
excerpt,
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
547
examples/alan-turing-portal/package-lock.json
generated
@@ -23,20 +23,16 @@
|
||||
"@tailwindcss/forms": "^0.5.3",
|
||||
"@tailwindcss/typography": "^0.5.4",
|
||||
"@tanstack/react-table": "^8.8.5",
|
||||
"@types/node": "18.16.0",
|
||||
"@types/react": "18.2.0",
|
||||
"@types/react-dom": "18.2.0",
|
||||
"autoprefixer": "^10.4.12",
|
||||
"clsx": "^1.2.1",
|
||||
"eslint": "8.39.0",
|
||||
"eslint-config-next": "13.3.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",
|
||||
"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",
|
||||
@@ -60,6 +56,9 @@
|
||||
"tailwindcss": "^3.3.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "18.16.0",
|
||||
"@types/react": "18.2.0",
|
||||
"@types/react-dom": "18.2.0",
|
||||
"eslint": "8.26.0",
|
||||
"eslint-config-next": "13.0.2",
|
||||
"prettier": "^2.8.7",
|
||||
@@ -551,9 +550,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@jridgewell/gen-mapping": {
|
||||
"version": "0.3.2",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz",
|
||||
"integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==",
|
||||
"version": "0.3.3",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz",
|
||||
"integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@jridgewell/set-array": "^1.0.1",
|
||||
@@ -583,9 +582,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@jridgewell/source-map": {
|
||||
"version": "0.3.2",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.2.tgz",
|
||||
"integrity": "sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==",
|
||||
"version": "0.3.3",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.3.tgz",
|
||||
"integrity": "sha512-b+fsZXeLYi9fEULmfBrhxn4IrPlINf8fiNarzTof004v3lFdntdwa9PF7vFJqm3mg7s+ScJMxXaE3Acp1irZcg==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@jridgewell/gen-mapping": "^0.3.0",
|
||||
@@ -599,13 +598,13 @@
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@jridgewell/trace-mapping": {
|
||||
"version": "0.3.14",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.14.tgz",
|
||||
"integrity": "sha512-bJWEfQ9lPTvm3SneWwRFVLzrh6nhjwqw7TUFFBEMzwvg7t7PCDenf2lDwqo4NQXzdpgBXyFgDWnQA+2vkruksQ==",
|
||||
"version": "0.3.18",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz",
|
||||
"integrity": "sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@jridgewell/resolve-uri": "^3.0.3",
|
||||
"@jridgewell/sourcemap-codec": "^1.4.10"
|
||||
"@jridgewell/resolve-uri": "3.1.0",
|
||||
"@jridgewell/sourcemap-codec": "1.4.14"
|
||||
}
|
||||
},
|
||||
"node_modules/@lit-labs/ssr-dom-shim": {
|
||||
@@ -1347,6 +1346,7 @@
|
||||
"version": "18.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.0.tgz",
|
||||
"integrity": "sha512-8yQrvS6sMpSwIovhPOwfyNf2Wz6v/B62LFSVYQ85+Rq3tLsBIG7rP5geMxaijTUxSkrO6RzN/IRuIAADYQsleA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/react": "*"
|
||||
}
|
||||
@@ -1477,148 +1477,148 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@webassemblyjs/ast": {
|
||||
"version": "1.11.1",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz",
|
||||
"integrity": "sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw==",
|
||||
"version": "1.11.6",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.6.tgz",
|
||||
"integrity": "sha512-IN1xI7PwOvLPgjcf180gC1bqn3q/QaOCwYUahIOhbYUu8KA/3tw2RT/T0Gidi1l7Hhj5D/INhJxiICObqpMu4Q==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@webassemblyjs/helper-numbers": "1.11.1",
|
||||
"@webassemblyjs/helper-wasm-bytecode": "1.11.1"
|
||||
"@webassemblyjs/helper-numbers": "1.11.6",
|
||||
"@webassemblyjs/helper-wasm-bytecode": "1.11.6"
|
||||
}
|
||||
},
|
||||
"node_modules/@webassemblyjs/floating-point-hex-parser": {
|
||||
"version": "1.11.1",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz",
|
||||
"integrity": "sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ==",
|
||||
"version": "1.11.6",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.6.tgz",
|
||||
"integrity": "sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw==",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@webassemblyjs/helper-api-error": {
|
||||
"version": "1.11.1",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz",
|
||||
"integrity": "sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg==",
|
||||
"version": "1.11.6",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.6.tgz",
|
||||
"integrity": "sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q==",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@webassemblyjs/helper-buffer": {
|
||||
"version": "1.11.1",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz",
|
||||
"integrity": "sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA==",
|
||||
"version": "1.11.6",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.6.tgz",
|
||||
"integrity": "sha512-z3nFzdcp1mb8nEOFFk8DrYLpHvhKC3grJD2ardfKOzmbmJvEf/tPIqCY+sNcwZIY8ZD7IkB2l7/pqhUhqm7hLA==",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@webassemblyjs/helper-numbers": {
|
||||
"version": "1.11.1",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz",
|
||||
"integrity": "sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ==",
|
||||
"version": "1.11.6",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.6.tgz",
|
||||
"integrity": "sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@webassemblyjs/floating-point-hex-parser": "1.11.1",
|
||||
"@webassemblyjs/helper-api-error": "1.11.1",
|
||||
"@webassemblyjs/floating-point-hex-parser": "1.11.6",
|
||||
"@webassemblyjs/helper-api-error": "1.11.6",
|
||||
"@xtuc/long": "4.2.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@webassemblyjs/helper-wasm-bytecode": {
|
||||
"version": "1.11.1",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz",
|
||||
"integrity": "sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q==",
|
||||
"version": "1.11.6",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.6.tgz",
|
||||
"integrity": "sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA==",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@webassemblyjs/helper-wasm-section": {
|
||||
"version": "1.11.1",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz",
|
||||
"integrity": "sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg==",
|
||||
"version": "1.11.6",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.6.tgz",
|
||||
"integrity": "sha512-LPpZbSOwTpEC2cgn4hTydySy1Ke+XEu+ETXuoyvuyezHO3Kjdu90KK95Sh9xTbmjrCsUwvWwCOQQNta37VrS9g==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@webassemblyjs/ast": "1.11.1",
|
||||
"@webassemblyjs/helper-buffer": "1.11.1",
|
||||
"@webassemblyjs/helper-wasm-bytecode": "1.11.1",
|
||||
"@webassemblyjs/wasm-gen": "1.11.1"
|
||||
"@webassemblyjs/ast": "1.11.6",
|
||||
"@webassemblyjs/helper-buffer": "1.11.6",
|
||||
"@webassemblyjs/helper-wasm-bytecode": "1.11.6",
|
||||
"@webassemblyjs/wasm-gen": "1.11.6"
|
||||
}
|
||||
},
|
||||
"node_modules/@webassemblyjs/ieee754": {
|
||||
"version": "1.11.1",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz",
|
||||
"integrity": "sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ==",
|
||||
"version": "1.11.6",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.6.tgz",
|
||||
"integrity": "sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@xtuc/ieee754": "^1.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@webassemblyjs/leb128": {
|
||||
"version": "1.11.1",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.1.tgz",
|
||||
"integrity": "sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw==",
|
||||
"version": "1.11.6",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.6.tgz",
|
||||
"integrity": "sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@xtuc/long": "4.2.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@webassemblyjs/utf8": {
|
||||
"version": "1.11.1",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.1.tgz",
|
||||
"integrity": "sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ==",
|
||||
"version": "1.11.6",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.6.tgz",
|
||||
"integrity": "sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA==",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@webassemblyjs/wasm-edit": {
|
||||
"version": "1.11.1",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz",
|
||||
"integrity": "sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA==",
|
||||
"version": "1.11.6",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.6.tgz",
|
||||
"integrity": "sha512-Ybn2I6fnfIGuCR+Faaz7YcvtBKxvoLV3Lebn1tM4o/IAJzmi9AWYIPWpyBfU8cC+JxAO57bk4+zdsTjJR+VTOw==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@webassemblyjs/ast": "1.11.1",
|
||||
"@webassemblyjs/helper-buffer": "1.11.1",
|
||||
"@webassemblyjs/helper-wasm-bytecode": "1.11.1",
|
||||
"@webassemblyjs/helper-wasm-section": "1.11.1",
|
||||
"@webassemblyjs/wasm-gen": "1.11.1",
|
||||
"@webassemblyjs/wasm-opt": "1.11.1",
|
||||
"@webassemblyjs/wasm-parser": "1.11.1",
|
||||
"@webassemblyjs/wast-printer": "1.11.1"
|
||||
"@webassemblyjs/ast": "1.11.6",
|
||||
"@webassemblyjs/helper-buffer": "1.11.6",
|
||||
"@webassemblyjs/helper-wasm-bytecode": "1.11.6",
|
||||
"@webassemblyjs/helper-wasm-section": "1.11.6",
|
||||
"@webassemblyjs/wasm-gen": "1.11.6",
|
||||
"@webassemblyjs/wasm-opt": "1.11.6",
|
||||
"@webassemblyjs/wasm-parser": "1.11.6",
|
||||
"@webassemblyjs/wast-printer": "1.11.6"
|
||||
}
|
||||
},
|
||||
"node_modules/@webassemblyjs/wasm-gen": {
|
||||
"version": "1.11.1",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz",
|
||||
"integrity": "sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA==",
|
||||
"version": "1.11.6",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.6.tgz",
|
||||
"integrity": "sha512-3XOqkZP/y6B4F0PBAXvI1/bky7GryoogUtfwExeP/v7Nzwo1QLcq5oQmpKlftZLbT+ERUOAZVQjuNVak6UXjPA==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@webassemblyjs/ast": "1.11.1",
|
||||
"@webassemblyjs/helper-wasm-bytecode": "1.11.1",
|
||||
"@webassemblyjs/ieee754": "1.11.1",
|
||||
"@webassemblyjs/leb128": "1.11.1",
|
||||
"@webassemblyjs/utf8": "1.11.1"
|
||||
"@webassemblyjs/ast": "1.11.6",
|
||||
"@webassemblyjs/helper-wasm-bytecode": "1.11.6",
|
||||
"@webassemblyjs/ieee754": "1.11.6",
|
||||
"@webassemblyjs/leb128": "1.11.6",
|
||||
"@webassemblyjs/utf8": "1.11.6"
|
||||
}
|
||||
},
|
||||
"node_modules/@webassemblyjs/wasm-opt": {
|
||||
"version": "1.11.1",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz",
|
||||
"integrity": "sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw==",
|
||||
"version": "1.11.6",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.6.tgz",
|
||||
"integrity": "sha512-cOrKuLRE7PCe6AsOVl7WasYf3wbSo4CeOk6PkrjS7g57MFfVUF9u6ysQBBODX0LdgSvQqRiGz3CXvIDKcPNy4g==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@webassemblyjs/ast": "1.11.1",
|
||||
"@webassemblyjs/helper-buffer": "1.11.1",
|
||||
"@webassemblyjs/wasm-gen": "1.11.1",
|
||||
"@webassemblyjs/wasm-parser": "1.11.1"
|
||||
"@webassemblyjs/ast": "1.11.6",
|
||||
"@webassemblyjs/helper-buffer": "1.11.6",
|
||||
"@webassemblyjs/wasm-gen": "1.11.6",
|
||||
"@webassemblyjs/wasm-parser": "1.11.6"
|
||||
}
|
||||
},
|
||||
"node_modules/@webassemblyjs/wasm-parser": {
|
||||
"version": "1.11.1",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz",
|
||||
"integrity": "sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA==",
|
||||
"version": "1.11.6",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.6.tgz",
|
||||
"integrity": "sha512-6ZwPeGzMJM3Dqp3hCsLgESxBGtT/OeCvCZ4TA1JUPYgmhAx38tTPR9JaKy0S5H3evQpO/h2uWs2j6Yc/fjkpTQ==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@webassemblyjs/ast": "1.11.1",
|
||||
"@webassemblyjs/helper-api-error": "1.11.1",
|
||||
"@webassemblyjs/helper-wasm-bytecode": "1.11.1",
|
||||
"@webassemblyjs/ieee754": "1.11.1",
|
||||
"@webassemblyjs/leb128": "1.11.1",
|
||||
"@webassemblyjs/utf8": "1.11.1"
|
||||
"@webassemblyjs/ast": "1.11.6",
|
||||
"@webassemblyjs/helper-api-error": "1.11.6",
|
||||
"@webassemblyjs/helper-wasm-bytecode": "1.11.6",
|
||||
"@webassemblyjs/ieee754": "1.11.6",
|
||||
"@webassemblyjs/leb128": "1.11.6",
|
||||
"@webassemblyjs/utf8": "1.11.6"
|
||||
}
|
||||
},
|
||||
"node_modules/@webassemblyjs/wast-printer": {
|
||||
"version": "1.11.1",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz",
|
||||
"integrity": "sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg==",
|
||||
"version": "1.11.6",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.6.tgz",
|
||||
"integrity": "sha512-JM7AhRcE+yW2GWYaKeHL5vt4xqee5N2WcezptmgyhNS+ScggqcT1OtXykhAb13Sn5Yas0j2uv9tHgrjwvzAP4A==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@webassemblyjs/ast": "1.11.1",
|
||||
"@webassemblyjs/ast": "1.11.6",
|
||||
"@xtuc/long": "4.2.2"
|
||||
}
|
||||
},
|
||||
@@ -3338,7 +3338,6 @@
|
||||
"version": "7.0.10",
|
||||
"resolved": "https://registry.npmjs.org/dagre-d3-es/-/dagre-d3-es-7.0.10.tgz",
|
||||
"integrity": "sha512-qTCQmEhcynucuaZgY5/+ti3X/rnszKZhEQH/ZdWdtP1tA/y3VoHJzcVrO9pjjJCNpigfscAtoUB5ONcd2wNn0A==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"d3": "^7.8.2",
|
||||
"lodash-es": "^4.17.21"
|
||||
@@ -3353,8 +3352,7 @@
|
||||
"node_modules/dayjs": {
|
||||
"version": "1.11.7",
|
||||
"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.7.tgz",
|
||||
"integrity": "sha512-+Yw9U6YO5TQohxLcIkrXBeY73WP3ejHWVvx8XCk3gxvQDCTEmS48ZrSZCKciI7Bhl/uCMyxYtE9UqRILmFphkQ==",
|
||||
"peer": true
|
||||
"integrity": "sha512-+Yw9U6YO5TQohxLcIkrXBeY73WP3ejHWVvx8XCk3gxvQDCTEmS48ZrSZCKciI7Bhl/uCMyxYtE9UqRILmFphkQ=="
|
||||
},
|
||||
"node_modules/debug": {
|
||||
"version": "4.3.4",
|
||||
@@ -3544,8 +3542,7 @@
|
||||
"node_modules/dompurify": {
|
||||
"version": "2.4.5",
|
||||
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.4.5.tgz",
|
||||
"integrity": "sha512-jggCCd+8Iqp4Tsz0nIvpcb22InKEBrGz5dw3EQJMs8HPJDsKbFIO3STYtAvCfDx26Muevn1MHVI0XxjgFfmiSA==",
|
||||
"peer": true
|
||||
"integrity": "sha512-jggCCd+8Iqp4Tsz0nIvpcb22InKEBrGz5dw3EQJMs8HPJDsKbFIO3STYtAvCfDx26Muevn1MHVI0XxjgFfmiSA=="
|
||||
},
|
||||
"node_modules/dot-prop": {
|
||||
"version": "5.3.0",
|
||||
@@ -3653,9 +3650,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/enhanced-resolve": {
|
||||
"version": "5.10.0",
|
||||
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.10.0.tgz",
|
||||
"integrity": "sha512-T0yTFjdpldGY8PmuXXR0PyQ1ufZpEGiHVrp7zHKB7jdR4qlmZHhONVM5AQOAWXuF/w3dnHbEQVrNptJgt7F+cQ==",
|
||||
"version": "5.14.0",
|
||||
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.14.0.tgz",
|
||||
"integrity": "sha512-+DCows0XNwLDcUhbFJPdlQEVnT2zXlCv7hPxemTz86/O+B/hCQ+mb7ydkPKiflpVraqLPCAfu7lDy+hBXueojw==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"graceful-fs": "^4.2.4",
|
||||
@@ -3730,9 +3727,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/es-module-lexer": {
|
||||
"version": "0.9.3",
|
||||
"resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz",
|
||||
"integrity": "sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==",
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.2.1.tgz",
|
||||
"integrity": "sha512-9978wrXM50Y4rTMmW5kXIC09ZdXQZqkE4mxhwkd8VbzsGkXGPgV4zWuqQJgCEzYngdo2dYDa0l8xhX4fkSwJSg==",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/es-shim-unscopables": {
|
||||
@@ -5412,9 +5409,9 @@
|
||||
"integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw=="
|
||||
},
|
||||
"node_modules/http-cache-semantics": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz",
|
||||
"integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ=="
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz",
|
||||
"integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ=="
|
||||
},
|
||||
"node_modules/http-proxy-agent": {
|
||||
"version": "4.0.1",
|
||||
@@ -6120,9 +6117,9 @@
|
||||
"integrity": "sha512-Rc2suX5meI0S3bfdZuA7JMFBGkJ875ApfVyq2WHELjBiiG22My/l7/8zPpH/CfFVQHuVLd8NLR0nv6vi0BYYKA=="
|
||||
},
|
||||
"node_modules/json5": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
|
||||
"integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==",
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz",
|
||||
"integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"minimist": "^1.2.0"
|
||||
@@ -7533,7 +7530,6 @@
|
||||
"version": "10.1.0",
|
||||
"resolved": "https://registry.npmjs.org/mermaid/-/mermaid-10.1.0.tgz",
|
||||
"integrity": "sha512-LYekSMNJygI1VnMizAPUddY95hZxOjwZxr7pODczILInO0dhQKuhXeu4sargtnuTwCilSuLS7Uiq/Qn7HTVrmA==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@braintree/sanitize-url": "^6.0.0",
|
||||
"@khanacademy/simple-markdown": "^0.8.6",
|
||||
@@ -7558,7 +7554,6 @@
|
||||
"version": "0.8.6",
|
||||
"resolved": "https://registry.npmjs.org/@khanacademy/simple-markdown/-/simple-markdown-0.8.6.tgz",
|
||||
"integrity": "sha512-mAUlR9lchzfqunR89pFvNI51jQKsMpJeWYsYWw0DQcUXczn/T/V6510utgvm7X0N3zN87j1SvuKk8cMbl9IAFw==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@types/react": ">=16.0.0"
|
||||
},
|
||||
@@ -10812,9 +10807,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/schema-utils": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz",
|
||||
"integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==",
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.2.tgz",
|
||||
"integrity": "sha512-pvjEHOgWc9OWA/f/DE3ohBWTD6EleVLf7iFUkoSwAxttdBhB9QUebQgxER2kWueOvRJXPHNnyrvvh9eZINB8Eg==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@types/json-schema": "^7.0.8",
|
||||
@@ -10880,9 +10875,9 @@
|
||||
"integrity": "sha512-EjnoLE5OGmDAVV/8YDoN5KiajNadjzIp9BAHOhYeQHt7j0UWxjmgsx4YD48wp4Ue1Qogq38F1GNUJNqF1kKKxA=="
|
||||
},
|
||||
"node_modules/serialize-javascript": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz",
|
||||
"integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==",
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.1.tgz",
|
||||
"integrity": "sha512-owoXEFjWRllis8/M1Q+Cw5k8ZH40e3zhp/ovX+Xr/vi1qj6QesbyXXViFbpNvWvPNAD62SutwEXavefrLJWj7w==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"randombytes": "^2.1.0"
|
||||
@@ -11500,9 +11495,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/terser": {
|
||||
"version": "5.14.2",
|
||||
"resolved": "https://registry.npmjs.org/terser/-/terser-5.14.2.tgz",
|
||||
"integrity": "sha512-oL0rGeM/WFQCUd0y2QrWxYnq7tfSuKBiqTjRPWrRgB46WD/kiwHwF8T23z78H6Q6kGCuuHcPB+KULHRdxvVGQA==",
|
||||
"version": "5.17.3",
|
||||
"resolved": "https://registry.npmjs.org/terser/-/terser-5.17.3.tgz",
|
||||
"integrity": "sha512-AudpAZKmZHkG9jueayypz4duuCFJMMNGRMwaPvQKWfxKedh8Z2x3OCoDqIIi1xx5+iwx1u6Au8XQcc9Lke65Yg==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@jridgewell/source-map": "^0.3.2",
|
||||
@@ -11518,16 +11513,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/terser-webpack-plugin": {
|
||||
"version": "5.3.3",
|
||||
"resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.3.tgz",
|
||||
"integrity": "sha512-Fx60G5HNYknNTNQnzQ1VePRuu89ZVYWfjRAeT5rITuCY/1b08s49e5kSQwHDirKZWuoKOBRFS98EUUoZ9kLEwQ==",
|
||||
"version": "5.3.8",
|
||||
"resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.8.tgz",
|
||||
"integrity": "sha512-WiHL3ElchZMsK27P8uIUh4604IgJyAW47LVXGbEoB21DbQcZ+OuMpGjVYnEUaqcWM6dO8uS2qUbA7LSCWqvsbg==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@jridgewell/trace-mapping": "^0.3.7",
|
||||
"@jridgewell/trace-mapping": "^0.3.17",
|
||||
"jest-worker": "^27.4.5",
|
||||
"schema-utils": "^3.1.1",
|
||||
"serialize-javascript": "^6.0.0",
|
||||
"terser": "^5.7.2"
|
||||
"serialize-javascript": "^6.0.1",
|
||||
"terser": "^5.16.8"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 10.13.0"
|
||||
@@ -12817,22 +12812,22 @@
|
||||
"integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="
|
||||
},
|
||||
"node_modules/webpack": {
|
||||
"version": "5.74.0",
|
||||
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.74.0.tgz",
|
||||
"integrity": "sha512-A2InDwnhhGN4LYctJj6M1JEaGL7Luj6LOmyBHjcI8529cm5p6VXiTIW2sn6ffvEAKmveLzvu4jrihwXtPojlAA==",
|
||||
"version": "5.82.0",
|
||||
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.82.0.tgz",
|
||||
"integrity": "sha512-iGNA2fHhnDcV1bONdUu554eZx+XeldsaeQ8T67H6KKHl2nUSwX8Zm7cmzOA46ox/X1ARxf7Bjv8wQ/HsB5fxBg==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@types/eslint-scope": "^3.7.3",
|
||||
"@types/estree": "^0.0.51",
|
||||
"@webassemblyjs/ast": "1.11.1",
|
||||
"@webassemblyjs/wasm-edit": "1.11.1",
|
||||
"@webassemblyjs/wasm-parser": "1.11.1",
|
||||
"@types/estree": "^1.0.0",
|
||||
"@webassemblyjs/ast": "^1.11.5",
|
||||
"@webassemblyjs/wasm-edit": "^1.11.5",
|
||||
"@webassemblyjs/wasm-parser": "^1.11.5",
|
||||
"acorn": "^8.7.1",
|
||||
"acorn-import-assertions": "^1.7.6",
|
||||
"browserslist": "^4.14.5",
|
||||
"chrome-trace-event": "^1.0.2",
|
||||
"enhanced-resolve": "^5.10.0",
|
||||
"es-module-lexer": "^0.9.0",
|
||||
"enhanced-resolve": "^5.13.0",
|
||||
"es-module-lexer": "^1.2.1",
|
||||
"eslint-scope": "5.1.1",
|
||||
"events": "^3.2.0",
|
||||
"glob-to-regexp": "^0.4.1",
|
||||
@@ -12841,9 +12836,9 @@
|
||||
"loader-runner": "^4.2.0",
|
||||
"mime-types": "^2.1.27",
|
||||
"neo-async": "^2.6.2",
|
||||
"schema-utils": "^3.1.0",
|
||||
"schema-utils": "^3.1.2",
|
||||
"tapable": "^2.1.1",
|
||||
"terser-webpack-plugin": "^5.1.3",
|
||||
"terser-webpack-plugin": "^5.3.7",
|
||||
"watchpack": "^2.4.0",
|
||||
"webpack-sources": "^3.2.3"
|
||||
},
|
||||
@@ -12880,12 +12875,6 @@
|
||||
"node": ">=10.13.0"
|
||||
}
|
||||
},
|
||||
"node_modules/webpack/node_modules/@types/estree": {
|
||||
"version": "0.0.51",
|
||||
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.51.tgz",
|
||||
"integrity": "sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ==",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/webpack/node_modules/eslint-scope": {
|
||||
"version": "5.1.1",
|
||||
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz",
|
||||
@@ -13712,9 +13701,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"@jridgewell/gen-mapping": {
|
||||
"version": "0.3.2",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz",
|
||||
"integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==",
|
||||
"version": "0.3.3",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz",
|
||||
"integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==",
|
||||
"peer": true,
|
||||
"requires": {
|
||||
"@jridgewell/set-array": "^1.0.1",
|
||||
@@ -13735,9 +13724,9 @@
|
||||
"peer": true
|
||||
},
|
||||
"@jridgewell/source-map": {
|
||||
"version": "0.3.2",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.2.tgz",
|
||||
"integrity": "sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==",
|
||||
"version": "0.3.3",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.3.tgz",
|
||||
"integrity": "sha512-b+fsZXeLYi9fEULmfBrhxn4IrPlINf8fiNarzTof004v3lFdntdwa9PF7vFJqm3mg7s+ScJMxXaE3Acp1irZcg==",
|
||||
"peer": true,
|
||||
"requires": {
|
||||
"@jridgewell/gen-mapping": "^0.3.0",
|
||||
@@ -13751,13 +13740,13 @@
|
||||
"peer": true
|
||||
},
|
||||
"@jridgewell/trace-mapping": {
|
||||
"version": "0.3.14",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.14.tgz",
|
||||
"integrity": "sha512-bJWEfQ9lPTvm3SneWwRFVLzrh6nhjwqw7TUFFBEMzwvg7t7PCDenf2lDwqo4NQXzdpgBXyFgDWnQA+2vkruksQ==",
|
||||
"version": "0.3.18",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz",
|
||||
"integrity": "sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA==",
|
||||
"peer": true,
|
||||
"requires": {
|
||||
"@jridgewell/resolve-uri": "^3.0.3",
|
||||
"@jridgewell/sourcemap-codec": "^1.4.10"
|
||||
"@jridgewell/resolve-uri": "3.1.0",
|
||||
"@jridgewell/sourcemap-codec": "1.4.14"
|
||||
}
|
||||
},
|
||||
"@lit-labs/ssr-dom-shim": {
|
||||
@@ -14283,6 +14272,7 @@
|
||||
"version": "18.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.0.tgz",
|
||||
"integrity": "sha512-8yQrvS6sMpSwIovhPOwfyNf2Wz6v/B62LFSVYQ85+Rq3tLsBIG7rP5geMxaijTUxSkrO6RzN/IRuIAADYQsleA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/react": "*"
|
||||
}
|
||||
@@ -14365,148 +14355,148 @@
|
||||
}
|
||||
},
|
||||
"@webassemblyjs/ast": {
|
||||
"version": "1.11.1",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz",
|
||||
"integrity": "sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw==",
|
||||
"version": "1.11.6",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.6.tgz",
|
||||
"integrity": "sha512-IN1xI7PwOvLPgjcf180gC1bqn3q/QaOCwYUahIOhbYUu8KA/3tw2RT/T0Gidi1l7Hhj5D/INhJxiICObqpMu4Q==",
|
||||
"peer": true,
|
||||
"requires": {
|
||||
"@webassemblyjs/helper-numbers": "1.11.1",
|
||||
"@webassemblyjs/helper-wasm-bytecode": "1.11.1"
|
||||
"@webassemblyjs/helper-numbers": "1.11.6",
|
||||
"@webassemblyjs/helper-wasm-bytecode": "1.11.6"
|
||||
}
|
||||
},
|
||||
"@webassemblyjs/floating-point-hex-parser": {
|
||||
"version": "1.11.1",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz",
|
||||
"integrity": "sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ==",
|
||||
"version": "1.11.6",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.6.tgz",
|
||||
"integrity": "sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw==",
|
||||
"peer": true
|
||||
},
|
||||
"@webassemblyjs/helper-api-error": {
|
||||
"version": "1.11.1",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz",
|
||||
"integrity": "sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg==",
|
||||
"version": "1.11.6",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.6.tgz",
|
||||
"integrity": "sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q==",
|
||||
"peer": true
|
||||
},
|
||||
"@webassemblyjs/helper-buffer": {
|
||||
"version": "1.11.1",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz",
|
||||
"integrity": "sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA==",
|
||||
"version": "1.11.6",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.6.tgz",
|
||||
"integrity": "sha512-z3nFzdcp1mb8nEOFFk8DrYLpHvhKC3grJD2ardfKOzmbmJvEf/tPIqCY+sNcwZIY8ZD7IkB2l7/pqhUhqm7hLA==",
|
||||
"peer": true
|
||||
},
|
||||
"@webassemblyjs/helper-numbers": {
|
||||
"version": "1.11.1",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz",
|
||||
"integrity": "sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ==",
|
||||
"version": "1.11.6",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.6.tgz",
|
||||
"integrity": "sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g==",
|
||||
"peer": true,
|
||||
"requires": {
|
||||
"@webassemblyjs/floating-point-hex-parser": "1.11.1",
|
||||
"@webassemblyjs/helper-api-error": "1.11.1",
|
||||
"@webassemblyjs/floating-point-hex-parser": "1.11.6",
|
||||
"@webassemblyjs/helper-api-error": "1.11.6",
|
||||
"@xtuc/long": "4.2.2"
|
||||
}
|
||||
},
|
||||
"@webassemblyjs/helper-wasm-bytecode": {
|
||||
"version": "1.11.1",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz",
|
||||
"integrity": "sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q==",
|
||||
"version": "1.11.6",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.6.tgz",
|
||||
"integrity": "sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA==",
|
||||
"peer": true
|
||||
},
|
||||
"@webassemblyjs/helper-wasm-section": {
|
||||
"version": "1.11.1",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz",
|
||||
"integrity": "sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg==",
|
||||
"version": "1.11.6",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.6.tgz",
|
||||
"integrity": "sha512-LPpZbSOwTpEC2cgn4hTydySy1Ke+XEu+ETXuoyvuyezHO3Kjdu90KK95Sh9xTbmjrCsUwvWwCOQQNta37VrS9g==",
|
||||
"peer": true,
|
||||
"requires": {
|
||||
"@webassemblyjs/ast": "1.11.1",
|
||||
"@webassemblyjs/helper-buffer": "1.11.1",
|
||||
"@webassemblyjs/helper-wasm-bytecode": "1.11.1",
|
||||
"@webassemblyjs/wasm-gen": "1.11.1"
|
||||
"@webassemblyjs/ast": "1.11.6",
|
||||
"@webassemblyjs/helper-buffer": "1.11.6",
|
||||
"@webassemblyjs/helper-wasm-bytecode": "1.11.6",
|
||||
"@webassemblyjs/wasm-gen": "1.11.6"
|
||||
}
|
||||
},
|
||||
"@webassemblyjs/ieee754": {
|
||||
"version": "1.11.1",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz",
|
||||
"integrity": "sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ==",
|
||||
"version": "1.11.6",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.6.tgz",
|
||||
"integrity": "sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg==",
|
||||
"peer": true,
|
||||
"requires": {
|
||||
"@xtuc/ieee754": "^1.2.0"
|
||||
}
|
||||
},
|
||||
"@webassemblyjs/leb128": {
|
||||
"version": "1.11.1",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.1.tgz",
|
||||
"integrity": "sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw==",
|
||||
"version": "1.11.6",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.6.tgz",
|
||||
"integrity": "sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ==",
|
||||
"peer": true,
|
||||
"requires": {
|
||||
"@xtuc/long": "4.2.2"
|
||||
}
|
||||
},
|
||||
"@webassemblyjs/utf8": {
|
||||
"version": "1.11.1",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.1.tgz",
|
||||
"integrity": "sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ==",
|
||||
"version": "1.11.6",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.6.tgz",
|
||||
"integrity": "sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA==",
|
||||
"peer": true
|
||||
},
|
||||
"@webassemblyjs/wasm-edit": {
|
||||
"version": "1.11.1",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz",
|
||||
"integrity": "sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA==",
|
||||
"version": "1.11.6",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.6.tgz",
|
||||
"integrity": "sha512-Ybn2I6fnfIGuCR+Faaz7YcvtBKxvoLV3Lebn1tM4o/IAJzmi9AWYIPWpyBfU8cC+JxAO57bk4+zdsTjJR+VTOw==",
|
||||
"peer": true,
|
||||
"requires": {
|
||||
"@webassemblyjs/ast": "1.11.1",
|
||||
"@webassemblyjs/helper-buffer": "1.11.1",
|
||||
"@webassemblyjs/helper-wasm-bytecode": "1.11.1",
|
||||
"@webassemblyjs/helper-wasm-section": "1.11.1",
|
||||
"@webassemblyjs/wasm-gen": "1.11.1",
|
||||
"@webassemblyjs/wasm-opt": "1.11.1",
|
||||
"@webassemblyjs/wasm-parser": "1.11.1",
|
||||
"@webassemblyjs/wast-printer": "1.11.1"
|
||||
"@webassemblyjs/ast": "1.11.6",
|
||||
"@webassemblyjs/helper-buffer": "1.11.6",
|
||||
"@webassemblyjs/helper-wasm-bytecode": "1.11.6",
|
||||
"@webassemblyjs/helper-wasm-section": "1.11.6",
|
||||
"@webassemblyjs/wasm-gen": "1.11.6",
|
||||
"@webassemblyjs/wasm-opt": "1.11.6",
|
||||
"@webassemblyjs/wasm-parser": "1.11.6",
|
||||
"@webassemblyjs/wast-printer": "1.11.6"
|
||||
}
|
||||
},
|
||||
"@webassemblyjs/wasm-gen": {
|
||||
"version": "1.11.1",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz",
|
||||
"integrity": "sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA==",
|
||||
"version": "1.11.6",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.6.tgz",
|
||||
"integrity": "sha512-3XOqkZP/y6B4F0PBAXvI1/bky7GryoogUtfwExeP/v7Nzwo1QLcq5oQmpKlftZLbT+ERUOAZVQjuNVak6UXjPA==",
|
||||
"peer": true,
|
||||
"requires": {
|
||||
"@webassemblyjs/ast": "1.11.1",
|
||||
"@webassemblyjs/helper-wasm-bytecode": "1.11.1",
|
||||
"@webassemblyjs/ieee754": "1.11.1",
|
||||
"@webassemblyjs/leb128": "1.11.1",
|
||||
"@webassemblyjs/utf8": "1.11.1"
|
||||
"@webassemblyjs/ast": "1.11.6",
|
||||
"@webassemblyjs/helper-wasm-bytecode": "1.11.6",
|
||||
"@webassemblyjs/ieee754": "1.11.6",
|
||||
"@webassemblyjs/leb128": "1.11.6",
|
||||
"@webassemblyjs/utf8": "1.11.6"
|
||||
}
|
||||
},
|
||||
"@webassemblyjs/wasm-opt": {
|
||||
"version": "1.11.1",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz",
|
||||
"integrity": "sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw==",
|
||||
"version": "1.11.6",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.6.tgz",
|
||||
"integrity": "sha512-cOrKuLRE7PCe6AsOVl7WasYf3wbSo4CeOk6PkrjS7g57MFfVUF9u6ysQBBODX0LdgSvQqRiGz3CXvIDKcPNy4g==",
|
||||
"peer": true,
|
||||
"requires": {
|
||||
"@webassemblyjs/ast": "1.11.1",
|
||||
"@webassemblyjs/helper-buffer": "1.11.1",
|
||||
"@webassemblyjs/wasm-gen": "1.11.1",
|
||||
"@webassemblyjs/wasm-parser": "1.11.1"
|
||||
"@webassemblyjs/ast": "1.11.6",
|
||||
"@webassemblyjs/helper-buffer": "1.11.6",
|
||||
"@webassemblyjs/wasm-gen": "1.11.6",
|
||||
"@webassemblyjs/wasm-parser": "1.11.6"
|
||||
}
|
||||
},
|
||||
"@webassemblyjs/wasm-parser": {
|
||||
"version": "1.11.1",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz",
|
||||
"integrity": "sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA==",
|
||||
"version": "1.11.6",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.6.tgz",
|
||||
"integrity": "sha512-6ZwPeGzMJM3Dqp3hCsLgESxBGtT/OeCvCZ4TA1JUPYgmhAx38tTPR9JaKy0S5H3evQpO/h2uWs2j6Yc/fjkpTQ==",
|
||||
"peer": true,
|
||||
"requires": {
|
||||
"@webassemblyjs/ast": "1.11.1",
|
||||
"@webassemblyjs/helper-api-error": "1.11.1",
|
||||
"@webassemblyjs/helper-wasm-bytecode": "1.11.1",
|
||||
"@webassemblyjs/ieee754": "1.11.1",
|
||||
"@webassemblyjs/leb128": "1.11.1",
|
||||
"@webassemblyjs/utf8": "1.11.1"
|
||||
"@webassemblyjs/ast": "1.11.6",
|
||||
"@webassemblyjs/helper-api-error": "1.11.6",
|
||||
"@webassemblyjs/helper-wasm-bytecode": "1.11.6",
|
||||
"@webassemblyjs/ieee754": "1.11.6",
|
||||
"@webassemblyjs/leb128": "1.11.6",
|
||||
"@webassemblyjs/utf8": "1.11.6"
|
||||
}
|
||||
},
|
||||
"@webassemblyjs/wast-printer": {
|
||||
"version": "1.11.1",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz",
|
||||
"integrity": "sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg==",
|
||||
"version": "1.11.6",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.6.tgz",
|
||||
"integrity": "sha512-JM7AhRcE+yW2GWYaKeHL5vt4xqee5N2WcezptmgyhNS+ScggqcT1OtXykhAb13Sn5Yas0j2uv9tHgrjwvzAP4A==",
|
||||
"peer": true,
|
||||
"requires": {
|
||||
"@webassemblyjs/ast": "1.11.1",
|
||||
"@webassemblyjs/ast": "1.11.6",
|
||||
"@xtuc/long": "4.2.2"
|
||||
}
|
||||
},
|
||||
@@ -15739,7 +15729,6 @@
|
||||
"version": "7.0.10",
|
||||
"resolved": "https://registry.npmjs.org/dagre-d3-es/-/dagre-d3-es-7.0.10.tgz",
|
||||
"integrity": "sha512-qTCQmEhcynucuaZgY5/+ti3X/rnszKZhEQH/ZdWdtP1tA/y3VoHJzcVrO9pjjJCNpigfscAtoUB5ONcd2wNn0A==",
|
||||
"peer": true,
|
||||
"requires": {
|
||||
"d3": "^7.8.2",
|
||||
"lodash-es": "^4.17.21"
|
||||
@@ -15754,8 +15743,7 @@
|
||||
"dayjs": {
|
||||
"version": "1.11.7",
|
||||
"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.7.tgz",
|
||||
"integrity": "sha512-+Yw9U6YO5TQohxLcIkrXBeY73WP3ejHWVvx8XCk3gxvQDCTEmS48ZrSZCKciI7Bhl/uCMyxYtE9UqRILmFphkQ==",
|
||||
"peer": true
|
||||
"integrity": "sha512-+Yw9U6YO5TQohxLcIkrXBeY73WP3ejHWVvx8XCk3gxvQDCTEmS48ZrSZCKciI7Bhl/uCMyxYtE9UqRILmFphkQ=="
|
||||
},
|
||||
"debug": {
|
||||
"version": "4.3.4",
|
||||
@@ -15894,8 +15882,7 @@
|
||||
"dompurify": {
|
||||
"version": "2.4.5",
|
||||
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.4.5.tgz",
|
||||
"integrity": "sha512-jggCCd+8Iqp4Tsz0nIvpcb22InKEBrGz5dw3EQJMs8HPJDsKbFIO3STYtAvCfDx26Muevn1MHVI0XxjgFfmiSA==",
|
||||
"peer": true
|
||||
"integrity": "sha512-jggCCd+8Iqp4Tsz0nIvpcb22InKEBrGz5dw3EQJMs8HPJDsKbFIO3STYtAvCfDx26Muevn1MHVI0XxjgFfmiSA=="
|
||||
},
|
||||
"dot-prop": {
|
||||
"version": "5.3.0",
|
||||
@@ -15992,9 +15979,9 @@
|
||||
}
|
||||
},
|
||||
"enhanced-resolve": {
|
||||
"version": "5.10.0",
|
||||
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.10.0.tgz",
|
||||
"integrity": "sha512-T0yTFjdpldGY8PmuXXR0PyQ1ufZpEGiHVrp7zHKB7jdR4qlmZHhONVM5AQOAWXuF/w3dnHbEQVrNptJgt7F+cQ==",
|
||||
"version": "5.14.0",
|
||||
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.14.0.tgz",
|
||||
"integrity": "sha512-+DCows0XNwLDcUhbFJPdlQEVnT2zXlCv7hPxemTz86/O+B/hCQ+mb7ydkPKiflpVraqLPCAfu7lDy+hBXueojw==",
|
||||
"peer": true,
|
||||
"requires": {
|
||||
"graceful-fs": "^4.2.4",
|
||||
@@ -16051,9 +16038,9 @@
|
||||
}
|
||||
},
|
||||
"es-module-lexer": {
|
||||
"version": "0.9.3",
|
||||
"resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz",
|
||||
"integrity": "sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==",
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.2.1.tgz",
|
||||
"integrity": "sha512-9978wrXM50Y4rTMmW5kXIC09ZdXQZqkE4mxhwkd8VbzsGkXGPgV4zWuqQJgCEzYngdo2dYDa0l8xhX4fkSwJSg==",
|
||||
"peer": true
|
||||
},
|
||||
"es-shim-unscopables": {
|
||||
@@ -17312,9 +17299,9 @@
|
||||
"integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw=="
|
||||
},
|
||||
"http-cache-semantics": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz",
|
||||
"integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ=="
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz",
|
||||
"integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ=="
|
||||
},
|
||||
"http-proxy-agent": {
|
||||
"version": "4.0.1",
|
||||
@@ -17801,9 +17788,9 @@
|
||||
"integrity": "sha512-Rc2suX5meI0S3bfdZuA7JMFBGkJ875ApfVyq2WHELjBiiG22My/l7/8zPpH/CfFVQHuVLd8NLR0nv6vi0BYYKA=="
|
||||
},
|
||||
"json5": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
|
||||
"integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==",
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz",
|
||||
"integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"minimist": "^1.2.0"
|
||||
@@ -18847,7 +18834,6 @@
|
||||
"version": "10.1.0",
|
||||
"resolved": "https://registry.npmjs.org/mermaid/-/mermaid-10.1.0.tgz",
|
||||
"integrity": "sha512-LYekSMNJygI1VnMizAPUddY95hZxOjwZxr7pODczILInO0dhQKuhXeu4sargtnuTwCilSuLS7Uiq/Qn7HTVrmA==",
|
||||
"peer": true,
|
||||
"requires": {
|
||||
"@braintree/sanitize-url": "^6.0.0",
|
||||
"@khanacademy/simple-markdown": "^0.8.6",
|
||||
@@ -18872,7 +18858,6 @@
|
||||
"version": "0.8.6",
|
||||
"resolved": "https://registry.npmjs.org/@khanacademy/simple-markdown/-/simple-markdown-0.8.6.tgz",
|
||||
"integrity": "sha512-mAUlR9lchzfqunR89pFvNI51jQKsMpJeWYsYWw0DQcUXczn/T/V6510utgvm7X0N3zN87j1SvuKk8cMbl9IAFw==",
|
||||
"peer": true,
|
||||
"requires": {
|
||||
"@types/react": ">=16.0.0"
|
||||
}
|
||||
@@ -21057,9 +21042,9 @@
|
||||
}
|
||||
},
|
||||
"schema-utils": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz",
|
||||
"integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==",
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.2.tgz",
|
||||
"integrity": "sha512-pvjEHOgWc9OWA/f/DE3ohBWTD6EleVLf7iFUkoSwAxttdBhB9QUebQgxER2kWueOvRJXPHNnyrvvh9eZINB8Eg==",
|
||||
"peer": true,
|
||||
"requires": {
|
||||
"@types/json-schema": "^7.0.8",
|
||||
@@ -21105,9 +21090,9 @@
|
||||
"integrity": "sha512-EjnoLE5OGmDAVV/8YDoN5KiajNadjzIp9BAHOhYeQHt7j0UWxjmgsx4YD48wp4Ue1Qogq38F1GNUJNqF1kKKxA=="
|
||||
},
|
||||
"serialize-javascript": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz",
|
||||
"integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==",
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.1.tgz",
|
||||
"integrity": "sha512-owoXEFjWRllis8/M1Q+Cw5k8ZH40e3zhp/ovX+Xr/vi1qj6QesbyXXViFbpNvWvPNAD62SutwEXavefrLJWj7w==",
|
||||
"peer": true,
|
||||
"requires": {
|
||||
"randombytes": "^2.1.0"
|
||||
@@ -21558,9 +21543,9 @@
|
||||
"integrity": "sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg=="
|
||||
},
|
||||
"terser": {
|
||||
"version": "5.14.2",
|
||||
"resolved": "https://registry.npmjs.org/terser/-/terser-5.14.2.tgz",
|
||||
"integrity": "sha512-oL0rGeM/WFQCUd0y2QrWxYnq7tfSuKBiqTjRPWrRgB46WD/kiwHwF8T23z78H6Q6kGCuuHcPB+KULHRdxvVGQA==",
|
||||
"version": "5.17.3",
|
||||
"resolved": "https://registry.npmjs.org/terser/-/terser-5.17.3.tgz",
|
||||
"integrity": "sha512-AudpAZKmZHkG9jueayypz4duuCFJMMNGRMwaPvQKWfxKedh8Z2x3OCoDqIIi1xx5+iwx1u6Au8XQcc9Lke65Yg==",
|
||||
"peer": true,
|
||||
"requires": {
|
||||
"@jridgewell/source-map": "^0.3.2",
|
||||
@@ -21570,16 +21555,16 @@
|
||||
}
|
||||
},
|
||||
"terser-webpack-plugin": {
|
||||
"version": "5.3.3",
|
||||
"resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.3.tgz",
|
||||
"integrity": "sha512-Fx60G5HNYknNTNQnzQ1VePRuu89ZVYWfjRAeT5rITuCY/1b08s49e5kSQwHDirKZWuoKOBRFS98EUUoZ9kLEwQ==",
|
||||
"version": "5.3.8",
|
||||
"resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.8.tgz",
|
||||
"integrity": "sha512-WiHL3ElchZMsK27P8uIUh4604IgJyAW47LVXGbEoB21DbQcZ+OuMpGjVYnEUaqcWM6dO8uS2qUbA7LSCWqvsbg==",
|
||||
"peer": true,
|
||||
"requires": {
|
||||
"@jridgewell/trace-mapping": "^0.3.7",
|
||||
"@jridgewell/trace-mapping": "^0.3.17",
|
||||
"jest-worker": "^27.4.5",
|
||||
"schema-utils": "^3.1.1",
|
||||
"serialize-javascript": "^6.0.0",
|
||||
"terser": "^5.7.2"
|
||||
"serialize-javascript": "^6.0.1",
|
||||
"terser": "^5.16.8"
|
||||
}
|
||||
},
|
||||
"text-table": {
|
||||
@@ -22613,22 +22598,22 @@
|
||||
"integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="
|
||||
},
|
||||
"webpack": {
|
||||
"version": "5.74.0",
|
||||
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.74.0.tgz",
|
||||
"integrity": "sha512-A2InDwnhhGN4LYctJj6M1JEaGL7Luj6LOmyBHjcI8529cm5p6VXiTIW2sn6ffvEAKmveLzvu4jrihwXtPojlAA==",
|
||||
"version": "5.82.0",
|
||||
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.82.0.tgz",
|
||||
"integrity": "sha512-iGNA2fHhnDcV1bONdUu554eZx+XeldsaeQ8T67H6KKHl2nUSwX8Zm7cmzOA46ox/X1ARxf7Bjv8wQ/HsB5fxBg==",
|
||||
"peer": true,
|
||||
"requires": {
|
||||
"@types/eslint-scope": "^3.7.3",
|
||||
"@types/estree": "^0.0.51",
|
||||
"@webassemblyjs/ast": "1.11.1",
|
||||
"@webassemblyjs/wasm-edit": "1.11.1",
|
||||
"@webassemblyjs/wasm-parser": "1.11.1",
|
||||
"@types/estree": "^1.0.0",
|
||||
"@webassemblyjs/ast": "^1.11.5",
|
||||
"@webassemblyjs/wasm-edit": "^1.11.5",
|
||||
"@webassemblyjs/wasm-parser": "^1.11.5",
|
||||
"acorn": "^8.7.1",
|
||||
"acorn-import-assertions": "^1.7.6",
|
||||
"browserslist": "^4.14.5",
|
||||
"chrome-trace-event": "^1.0.2",
|
||||
"enhanced-resolve": "^5.10.0",
|
||||
"es-module-lexer": "^0.9.0",
|
||||
"enhanced-resolve": "^5.13.0",
|
||||
"es-module-lexer": "^1.2.1",
|
||||
"eslint-scope": "5.1.1",
|
||||
"events": "^3.2.0",
|
||||
"glob-to-regexp": "^0.4.1",
|
||||
@@ -22637,19 +22622,13 @@
|
||||
"loader-runner": "^4.2.0",
|
||||
"mime-types": "^2.1.27",
|
||||
"neo-async": "^2.6.2",
|
||||
"schema-utils": "^3.1.0",
|
||||
"schema-utils": "^3.1.2",
|
||||
"tapable": "^2.1.1",
|
||||
"terser-webpack-plugin": "^5.1.3",
|
||||
"terser-webpack-plugin": "^5.3.7",
|
||||
"watchpack": "^2.4.0",
|
||||
"webpack-sources": "^3.2.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"@types/estree": {
|
||||
"version": "0.0.51",
|
||||
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.51.tgz",
|
||||
"integrity": "sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ==",
|
||||
"peer": true
|
||||
},
|
||||
"eslint-scope": {
|
||||
"version": "5.1.1",
|
||||
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz",
|
||||
|
||||
@@ -12,47 +12,41 @@
|
||||
},
|
||||
"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",
|
||||
"next-router-mock": "^0.9.3",
|
||||
"next-superjson-plugin": "^0.5.7",
|
||||
"postcss-focus-visible": "^6.0.4",
|
||||
"react-hook-form": "^7.43.9",
|
||||
"react-markdown": "^8.0.7",
|
||||
"superjson": "^1.12.3",
|
||||
"tailwindcss": "^3.3.0",
|
||||
"@flowershow/core": "^0.4.10",
|
||||
"@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",
|
||||
"@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",
|
||||
"gray-matter": "^4.0.3",
|
||||
"hastscript": "^7.2.0",
|
||||
"mdx-mermaid": "2.0.0-rc7",
|
||||
"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",
|
||||
@@ -61,12 +55,17 @@
|
||||
"remark-gfm": "^3.0.1",
|
||||
"remark-math": "^5.1.1",
|
||||
"remark-smartypants": "^2.0.0",
|
||||
"remark-toc": "^8.0.1"
|
||||
"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"
|
||||
"prettier-plugin-tailwindcss": "^0.2.6",
|
||||
"@types/node": "18.16.0",
|
||||
"@types/react": "18.2.0",
|
||||
"@types/react-dom": "18.2.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import { Container } from '../components/Container'
|
||||
import clientPromise from '../lib/mddb'
|
||||
import fs from 'fs'
|
||||
import { promises as fs } from 'fs';
|
||||
import { MDXRemote } from 'next-mdx-remote'
|
||||
import { serialize } from 'next-mdx-remote/serialize'
|
||||
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('/') : ''
|
||||
@@ -12,8 +14,8 @@ export const getStaticProps = async ({ params }) => {
|
||||
const mddb = await clientPromise
|
||||
const dbFile = await mddb.getFileByUrl(urlPath)
|
||||
|
||||
const source = fs.readFileSync(dbFile.file_path, { encoding: 'utf-8' })
|
||||
const mdxSource = await serialize(source, { parseFrontmatter: true })
|
||||
const source = await fs.readFile(dbFile.file_path,'utf-8')
|
||||
let mdxSource = await parse(source, '.mdx')
|
||||
|
||||
return {
|
||||
props: {
|
||||
@@ -74,16 +76,18 @@ const Meta = ({keyValuePairs}) => {
|
||||
}
|
||||
|
||||
export default function DRDPage({ mdxSource }) {
|
||||
const meta = mdxSource.frontmatter
|
||||
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">
|
||||
<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">
|
||||
@@ -94,7 +98,7 @@ export default function DRDPage({ mdxSource }) {
|
||||
</Card>
|
||||
</header>
|
||||
<div className="prose dark:prose-invert">
|
||||
<MDXRemote {...mdxSource} />
|
||||
<MDXRemote {...mdxSource.mdxSource} components={{mermaid: Mermaid}} />
|
||||
</div>
|
||||
</article>
|
||||
</Container>
|
||||
|
||||
@@ -1,25 +1,17 @@
|
||||
import { Head, Html, Main, NextScript } from 'next/document'
|
||||
|
||||
const modeScript = `
|
||||
let darkModeMediaQuery = window.matchMedia('(prefers-color-scheme: dark)')
|
||||
|
||||
updateMode()
|
||||
darkModeMediaQuery.addEventListener('change', updateModeWithoutTransitions)
|
||||
window.addEventListener('storage', updateModeWithoutTransitions)
|
||||
|
||||
function updateMode() {
|
||||
let isSystemDarkMode = darkModeMediaQuery.matches
|
||||
let isDarkMode = window.localStorage.isDarkMode === 'true' || (!('isDarkMode' in window.localStorage) && isSystemDarkMode)
|
||||
let isDarkMode = window.localStorage.isDarkMode === 'true'
|
||||
|
||||
if (isDarkMode) {
|
||||
document.documentElement.classList.add('dark')
|
||||
} else {
|
||||
document.documentElement.classList.remove('dark')
|
||||
}
|
||||
|
||||
if (isDarkMode === isSystemDarkMode) {
|
||||
delete window.localStorage.isDarkMode
|
||||
}
|
||||
}
|
||||
|
||||
function disableTransitionsTemporarily() {
|
||||
|
||||
@@ -13,7 +13,9 @@ 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.Title>
|
||||
<Link href={dataset.url}>{dataset.title}</Link>
|
||||
</Card.Title>
|
||||
<Card.Description>
|
||||
<span className="font-semibold">Link to publication: </span>{' '}
|
||||
<a
|
||||
@@ -75,7 +77,9 @@ function DatasetCard({ dataset }) {
|
||||
function ListOfAbusiveKeywordsCard({ list }) {
|
||||
return (
|
||||
<Card as="article">
|
||||
<Card.Title><Link href={list.url}>{list.title}</Link></Card.Title>
|
||||
<Card.Title>
|
||||
<Link href={list.url}>{list.title}</Link>
|
||||
</Card.Title>
|
||||
{list.description && (
|
||||
<Card.Description>
|
||||
<span className="font-semibold">List Description: </span>{' '}
|
||||
@@ -139,17 +143,23 @@ export default function Home({
|
||||
<h1 className="text-4xl font-bold tracking-tight text-zinc-800 dark:text-zinc-100 sm:text-5xl">
|
||||
{indexText.frontmatter.title}
|
||||
</h1>
|
||||
<article className="mt-6 index-text flex flex-col gap-y-2 text-base text-zinc-600 dark:text-zinc-400 prose dark:prose-invert">
|
||||
<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-24 md:mt-28">
|
||||
<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 className="text-xl font-bold tracking-tight text-zinc-800 dark:text-zinc-100 sm:text-5xl">
|
||||
<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">
|
||||
<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>
|
||||
@@ -198,7 +208,12 @@ export default function Home({
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
<button type='submit' className='inline-flex items-center gap-2 justify-center rounded-md py-2 px-3 text-sm 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 flex-none'>Clear filters</button>
|
||||
<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">
|
||||
@@ -225,7 +240,7 @@ export default function Home({
|
||||
</div>
|
||||
</Container>
|
||||
<Container className="mt-16">
|
||||
<h2 className="text-xl font-bold tracking-tight text-zinc-800 dark:text-zinc-100 sm:text-5xl">
|
||||
<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">
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
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}",
|
||||
],
|
||||
|
||||
@@ -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/fivethirtyeight/.eslintrc.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"extends": "next/core-web-vitals"
|
||||
}
|
||||
35
examples/fivethirtyeight/.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
|
||||
38
examples/fivethirtyeight/README.md
Normal file
@@ -0,0 +1,38 @@
|
||||
This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).
|
||||
|
||||
## Getting Started
|
||||
|
||||
First, run the development server:
|
||||
|
||||
```bash
|
||||
npm run dev
|
||||
# or
|
||||
yarn dev
|
||||
# or
|
||||
pnpm dev
|
||||
```
|
||||
|
||||
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
|
||||
|
||||
You can start editing the page by modifying `pages/index.tsx`. The page auto-updates as you edit the file.
|
||||
|
||||
[API routes](https://nextjs.org/docs/api-routes/introduction) can be accessed on [http://localhost:3000/api/hello](http://localhost:3000/api/hello). This endpoint can be edited in `pages/api/hello.ts`.
|
||||
|
||||
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:
|
||||
|
||||
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
|
||||
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
|
||||
|
||||
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!
|
||||
|
||||
## Deploy on Vercel
|
||||
|
||||
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
|
||||
|
||||
Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.
|
||||
23
examples/fivethirtyeight/components/Breadcrumbs.tsx
Normal file
@@ -0,0 +1,23 @@
|
||||
import Link from "next/link";
|
||||
|
||||
function HomeIcon({ className = "" }) {
|
||||
return <div className={`inline-block w-4 ${className}`}><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"> <path d="M 12 2 A 1 1 0 0 0 11.289062 2.296875 L 1.203125 11.097656 A 0.5 0.5 0 0 0 1 11.5 A 0.5 0.5 0 0 0 1.5 12 L 4 12 L 4 20 C 4 20.552 4.448 21 5 21 L 9 21 C 9.552 21 10 20.552 10 20 L 10 14 L 14 14 L 14 20 C 14 20.552 14.448 21 15 21 L 19 21 C 19.552 21 20 20.552 20 20 L 20 12 L 22.5 12 A 0.5 0.5 0 0 0 23 11.5 A 0.5 0.5 0 0 0 22.796875 11.097656 L 12.716797 2.3027344 A 1 1 0 0 0 12.710938 2.296875 A 1 1 0 0 0 12 2 z"/></svg></div>
|
||||
}
|
||||
|
||||
export default function Breadcrumbs({ links }: { links: { title: string, href?: string, target?: string }[] }) {
|
||||
const current = links.at(-1);
|
||||
|
||||
return <div className="flex items-center uppercase font-black text-xs">
|
||||
<Link className="flex items-center" href='/'><HomeIcon /></Link>
|
||||
|
||||
{/* {links.length > 1 && links.slice(0, -1).map((link) => {
|
||||
return <>
|
||||
<span className="mx-4">/</span>
|
||||
<Link href={link.href}>{link.title}</Link>
|
||||
</>
|
||||
})} */}
|
||||
|
||||
<span className="mx-4">/</span>
|
||||
<span>{current?.title}</span>
|
||||
</div >
|
||||
}
|
||||
2353
examples/fivethirtyeight/datasets.json
Normal file
38
examples/fivethirtyeight/lib/octokit.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
import { Octokit } from 'octokit';
|
||||
|
||||
export interface GithubProject {
|
||||
owner: string;
|
||||
repo: string;
|
||||
branch: string;
|
||||
files: string[];
|
||||
readme: string;
|
||||
description?: string;
|
||||
name?: string;
|
||||
}
|
||||
|
||||
export async function getProjectReadme(
|
||||
owner: string,
|
||||
repo: string,
|
||||
branch: string,
|
||||
readme: string,
|
||||
github_pat?: string
|
||||
) {
|
||||
const octokit = new Octokit({ auth: github_pat });
|
||||
try {
|
||||
const response = await octokit.rest.repos.getContent({
|
||||
owner,
|
||||
repo,
|
||||
path: readme,
|
||||
ref: branch,
|
||||
});
|
||||
const data = response.data as { content?: string };
|
||||
const fileContent = data.content ? data.content : '';
|
||||
if (fileContent === '') {
|
||||
return null;
|
||||
}
|
||||
const decodedContent = Buffer.from(fileContent, 'base64').toString();
|
||||
return decodedContent;
|
||||
} catch (error) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
9
examples/fivethirtyeight/next.config.js
Normal file
@@ -0,0 +1,9 @@
|
||||
/** @type {import('next').NextConfig} */
|
||||
const nextConfig = {
|
||||
reactStrictMode: true,
|
||||
serverRuntimeConfig: {
|
||||
github_pat: process.env.GITHUB_PAT ? process.env.GITHUB_PAT : null,
|
||||
},
|
||||
}
|
||||
|
||||
module.exports = nextConfig
|
||||
6878
examples/fivethirtyeight/package-lock.json
generated
Normal file
37
examples/fivethirtyeight/package.json
Normal file
@@ -0,0 +1,37 @@
|
||||
{
|
||||
"name": "fiverthirtyeight-example",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "next dev",
|
||||
"build": "next build",
|
||||
"start": "next start",
|
||||
"lint": "next lint"
|
||||
},
|
||||
"dependencies": {
|
||||
"@portaljs/components": "^0.1.0",
|
||||
"@tailwindcss/typography": "^0.5.9",
|
||||
"@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",
|
||||
"flexsearch": "^0.7.31",
|
||||
"next": "13.4.1",
|
||||
"next-mdx-remote": "^4.4.1",
|
||||
"next-seo": "^6.0.0",
|
||||
"octokit": "^2.0.14",
|
||||
"postcss": "8.4.23",
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0",
|
||||
"react-markdown": "^8.0.7",
|
||||
"remark-code-frontmatter": "^1.0.0",
|
||||
"remark-extract-frontmatter": "^3.2.0",
|
||||
"remark-frontmatter": "^4.0.1",
|
||||
"remark-gfm": "^3.0.1",
|
||||
"tailwindcss": "3.3.2",
|
||||
"timeago.js": "^4.0.2",
|
||||
"typescript": "5.0.4"
|
||||
}
|
||||
}
|
||||
8
examples/fivethirtyeight/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} />
|
||||
}
|
||||
48
examples/fivethirtyeight/pages/_document.tsx
Normal file
@@ -0,0 +1,48 @@
|
||||
import { Html, Head, Main, NextScript } from 'next/document';
|
||||
|
||||
export default function Document() {
|
||||
return (
|
||||
<Html lang="en">
|
||||
<Head>
|
||||
<link
|
||||
rel="icon"
|
||||
type="image/x-icon"
|
||||
href="https://projects.fivethirtyeight.com/shared/favicon.ico"
|
||||
/>
|
||||
</Head>
|
||||
<body>
|
||||
<header className="max-w-5xl mx-auto mt-8 w-full">
|
||||
<div className="border-b-2 pb-2.5 mx-2 border-zinc-800">
|
||||
<h1>
|
||||
<span className="sr-only">FiveThirtyEight</span>
|
||||
<a
|
||||
className="flex gap-x-2 items-center"
|
||||
href="http://fivethirtyeight.com"
|
||||
>
|
||||
<img
|
||||
width="197"
|
||||
height="25"
|
||||
alt="FiveThirtyEight"
|
||||
src="data:image/svg+xml;base64,PHN2ZyBpZD0iTGF5ZXJfMSIgZGF0YS1uYW1lPSJMYXllciAxIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA0MjEgNTMuNzYiPjxkZWZzPjxzdHlsZT4uY2xzLTF7ZmlsbDojMDEwMTAxO308L3N0eWxlPjwvZGVmcz48dGl0bGU+QXJ0Ym9hcmQgOTU8L3RpdGxlPjxwYXRoIGNsYXNzPSJjbHMtMSIgZD0iTTAgMGgyNXY4SDl2MTBoMTV2OEg5djE3SDBWMHpNMzEgMzZoNVYxOGgtNXYtOGgxM3YyNmg0djdIMzF6bTUtMzZoOHY4aC04ek0xNzkgMzZoNVYxOGgtNXYtOGgxM3YyNmg0djdoLTE3em01LTM2aDh2OGgtOHpNMzE2IDM2aDVWMThoLTV2LThoMTN2MjZoNHY3aC0xN3ptNS0zNmg4djhoLTh6TTU0IDI3VjEwaDh2MTVsNCA5Ljk4aDFMNzEgMjVWMTBoOHYxN2wtNyAxNkg2MWwtNy0xNnpNMTExIDQzSDk3LjQyQzg5LjIzIDQzIDg1IDM5LjE5IDg1IDMxLjE3VjIyYzAtNy41NyA0LjMtMTMgMTMtMTMgOS4zMyAwIDEzIDUuMDcgMTMgMTR2N0g5NHYxLjc0YzAgMi42MiAxIDQuMjYgMy40MiA0LjI2SDExMXpNOTQgMjNoOHYtMS41NWMwLTIuNjItMS4wNi01LjQ1LTQuMTMtNS40NS0yLjc5IDAtMy44NyAyLjItMy44NyA1LjQ1ek0xMjUgOGgtMTBWMGgyOXY4aC0xMHYzNWgtOVY4ek0yMDIgNDNWMTBoOHY0YzEuMTQtMi40NSAzLjc1LTQgNy4yMi00SDIyMHY4aC02Yy0yLjg0IDAtNCAuOTQtNCAzLjlWNDN6TTI0NSA0M2gtNC44NEMyMzMuMDUgNDMgMjMwIDM5LjMxIDIzMCAzMS44NVYxOGgtNnYtOGg2VjNoOHY3aDd2OGgtN2wtLjA3IDEzLjkzYzAgMi4yMi45MyA0LjA3IDMuNjYgNC4wN0gyNDV6TTQyMSA0M2gtNC44NEM0MDkuMDUgNDMgNDA2IDM5LjMxIDQwNiAzMS44NVYxOGgtNnYtOGg2VjNoOHY3aDd2OGgtN2wtLjA3IDEzLjkzYzAgMi4yMi45MyA0LjA3IDMuNjYgNC4wN0g0MjF6TTI1NC4yNiA1My43Nmw0LjYxLTkuNUwyNTEgMjdWMTBoOHYxNWw0IDEwaDFsNC0xMFYxMGg4djE3bC0xMi4zIDI2Ljc2aC05LjQ0ek0yODQgMGgyNXY4aC0xNnY5aDE1djhoLTE1djEwaDE2djhoLTI1VjB6TTMzNyA0OHYtMmgxNi4xYzIgMCAyLjktLjE4IDIuOS0xLjI3di0uMzRjMC0xLjA4LS45MS0xLjM5LTIuOS0xLjM5SDM0MHYtNWw1LTVjLTUuMjktMS40OC04LTUuNDMtOC0xMXYtMWMwLTcuNTYgNC40NC0xMiAxNC0xMmEyMS45MyAyMS45MyAwIDAgMSA1Ljk1IDFMMzYxIDRsNSAzLTQgNmMxLjM3IDEuOTMgMyA0LjkzIDMgOHYxYzAgNy0zLjMgMTAuNjYtMTIgMTFsLTMgNGg2YzUuOTIgMCA5IDIuNjIgOSA3LjY4di4xMWMwIDUuMDYtMi43MSA4LjIxLTguNjIgOC4yMWgtMTNjLTQuMjkgMC02LjM4LTEuODQtNi4zOC01em0xOS0yNXYtM2MwLTMuMy0xLjMzLTQtNS00cy01IC43LTUgNHYzYzAgMy4zIDEuMzkgNCA1IDRzNS0uNyA1LTR6TTM4MCA0M2gtOFYwaDh2MTRjMS4xNC0yLjY3IDMuNC00IDctNCA2LjI2IDAgOSAzLjA4IDkgMTAuNzZWNDNoLThWMjJjMC0zLjEzLTEuMDctNS00LTVzLTQgMS44Ny00IDV6TTE1NyA0M2gtOFYwaDh2MTRjMS4xNC0yLjY3IDMuOTEtNCA3LjQ5LTQgNi4yNiAwIDguNTEgMy4xMyA4LjUxIDEwLjgxVjQzaC04VjIxYzAtMy4xMy0xLjA3LTQuNDQtNC00LjQ0cy00IDIuMjYtNCA1LjM5eiIvPjwvc3ZnPg=="
|
||||
/>{' '}
|
||||
by PortalJS
|
||||
</a>
|
||||
</h1>
|
||||
</div>
|
||||
<div className="mx-2 py-1.5 text-[14px] text-[#3c3c3c]">
|
||||
<ul className='flex gap-x-4'>
|
||||
<li>
|
||||
<a className='hover:opacity-75 transition' href="https://portaljs.org">PortalJS</a>
|
||||
</li>
|
||||
<li>
|
||||
<a className='hover:opacity-75 transition' href="https://github.com/datopian/portaljs/tree/main/examples/fivethirtyeight">View on Github</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</header>
|
||||
<Main />
|
||||
<NextScript />
|
||||
</body>
|
||||
</Html>
|
||||
);
|
||||
}
|
||||
131
examples/fivethirtyeight/pages/datasets/[datasetName].tsx
Normal file
@@ -0,0 +1,131 @@
|
||||
import { NextSeo } from 'next-seo';
|
||||
import { promises as fs } from 'fs';
|
||||
import path from 'path';
|
||||
import getConfig from 'next/config';
|
||||
import { getProjectReadme, GithubProject } from '@/lib/octokit';
|
||||
import remarkGfm from 'remark-gfm';
|
||||
import extract from 'remark-extract-frontmatter';
|
||||
import { Dataset } from '..';
|
||||
import { GetStaticProps } from 'next';
|
||||
import { Table } from '@portaljs/components';
|
||||
import Breadcrumbs from '@/components/Breadcrumbs';
|
||||
import { ReactMarkdown } from 'react-markdown/lib/react-markdown';
|
||||
import remarkFrontmatter from 'remark-frontmatter';
|
||||
|
||||
export default function DatasetPage({
|
||||
dataset,
|
||||
}: {
|
||||
dataset: Dataset & {
|
||||
readme: string | null;
|
||||
};
|
||||
}) {
|
||||
return (
|
||||
<>
|
||||
<NextSeo title={`${dataset.name} page`} />
|
||||
<main className="max-w-5xl px-2 prose mx-auto my-8 prose-thead:border-b-4 prose-table:max-w-5xl prose-table:overflow-scroll prose-thead:overflow-scroll prose-tbody:overflow-scroll prose-thead:pb-2 prose-thead:border-zinc-900 prose-th:uppercase prose-th:text-left prose-th:font-light prose-th:text-xs">
|
||||
<Breadcrumbs links={[{ title: dataset.name, href: '' }]} />
|
||||
<h1 className="uppercase mb-0 mt-16">{dataset.name}</h1>
|
||||
<p className="mb-8">
|
||||
<span className="font-semibold">Repository:</span>{' '}
|
||||
<a target="_blank" href={dataset.url}>
|
||||
{dataset.url}
|
||||
</a>
|
||||
</p>
|
||||
|
||||
<h2 className="mb-0 mt-10">FILES</h2>
|
||||
<div className="inline-block min-w-full py-2 align-middle">
|
||||
<table className="min-w-full divide-y divide-gray-300">
|
||||
<thead className="border-b-4 pb-2 border-zinc-900">
|
||||
<tr>
|
||||
<th
|
||||
className="uppercase text-left font-light text-xs pb-3"
|
||||
scope="col"
|
||||
>
|
||||
Name
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="divide-y divide-gray-200">
|
||||
{dataset.files?.map((file) => (
|
||||
<tr key={file}>
|
||||
<td className="whitespace-nowrap text-left py-4 text-sm text-gray-500">
|
||||
<a href={file}>{file.split('/').slice(-1)}</a>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{dataset.files && dataset.files.length > 0 && (
|
||||
<>
|
||||
<h2 className="mb-0 mt-10">DATA PREVIEWS</h2>
|
||||
{dataset.files?.map((file) => (
|
||||
<div key={file} className="preview-table my-8">
|
||||
<h3>{file.split('/').slice(-1)}</h3>
|
||||
<Table url={file} />
|
||||
</div>
|
||||
))}
|
||||
</>
|
||||
)}
|
||||
{dataset.readme && (
|
||||
<>
|
||||
<h2 className="uppercase font-black">Readme</h2>
|
||||
{dataset.readme && (
|
||||
<ReactMarkdown
|
||||
remarkPlugins={[
|
||||
remarkFrontmatter,
|
||||
remarkGfm,
|
||||
[extract, { remove: true }],
|
||||
]}
|
||||
>
|
||||
{dataset.readme}
|
||||
</ReactMarkdown>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</main>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export async function getStaticPaths() {
|
||||
const datasetsFile = path.join(process.cwd(), 'datasets.json');
|
||||
const datasets = await fs.readFile(datasetsFile, 'utf8');
|
||||
|
||||
return {
|
||||
paths: JSON.parse(datasets).map((dataset: Dataset) => {
|
||||
return {
|
||||
params: { datasetName: dataset.name },
|
||||
};
|
||||
}),
|
||||
fallback: false, // can also be true or 'blocking'
|
||||
};
|
||||
}
|
||||
|
||||
export const getStaticProps: GetStaticProps = async ({ params }) => {
|
||||
const datasetsFile = path.join(process.cwd(), 'datasets.json');
|
||||
const datasetsString = await fs.readFile(datasetsFile, 'utf8');
|
||||
const datasets: Dataset[] = JSON.parse(datasetsString);
|
||||
const dataset: Dataset | undefined = datasets.find(
|
||||
(_dataset) => _dataset.name === params?.datasetName
|
||||
);
|
||||
const github_pat = getConfig().serverRuntimeConfig.github_pat;
|
||||
const readmes = await Promise.all(['/README.md', '/readme.md', '/Readme.md'].map(async (readme) => await getProjectReadme(
|
||||
'fivethirtyeight',
|
||||
'data',
|
||||
'master',
|
||||
dataset?.name + readme,
|
||||
github_pat
|
||||
)));
|
||||
const readme = readmes.find(item => item !== null)
|
||||
if (!readme) console.log('Readme not found for ' + dataset?.name)
|
||||
return {
|
||||
props: {
|
||||
dataset: {
|
||||
...dataset,
|
||||
readme,
|
||||
files: dataset && dataset.files ? dataset.files : null,
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
215
examples/fivethirtyeight/pages/index.tsx
Normal file
@@ -0,0 +1,215 @@
|
||||
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'] });
|
||||
|
||||
export interface Article {
|
||||
date: string;
|
||||
title: string;
|
||||
url: string;
|
||||
}
|
||||
|
||||
export interface Dataset {
|
||||
url: string;
|
||||
name: string;
|
||||
displayName: string;
|
||||
articles: Article[];
|
||||
files?: string[];
|
||||
}
|
||||
|
||||
// Request a weekday along with a long date
|
||||
const options = {
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
day: 'numeric',
|
||||
} as const;
|
||||
|
||||
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-mono font-light">
|
||||
<a className="underline" href={dataset.url} target="_blank">
|
||||
{dataset.name}
|
||||
</a>
|
||||
</span>
|
||||
{dataset.articles.map((article) => (
|
||||
<div key={article.title} className="py-1 flex flex-col">
|
||||
<span className="font-bold hover:underline">{article.title}</span>
|
||||
<span className="font-light text-base">
|
||||
{format(article.date).includes('years')
|
||||
? new Date(article.date).toLocaleString('en-US', options)
|
||||
: format(article.date)}
|
||||
</span>{' '}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<div className="flex flex-col justify-start">
|
||||
<a
|
||||
className="ml-2 border border-zinc-900 font-light px-4 py-1 text-sm transition hover:bg-zinc-900 hover:text-white"
|
||||
href={`/datasets/${dataset.name}`}
|
||||
>
|
||||
explore
|
||||
</a>
|
||||
{/*
|
||||
<button>
|
||||
<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
|
||||
key={article.url}
|
||||
className={`${
|
||||
index === dataset.articles.length - 1 ? 'border-b' : ''
|
||||
} border-zinc-400`}
|
||||
>
|
||||
<td className="py-8 font-light font-mono text-[14px] text-zinc-700">
|
||||
<a className="underline" href={dataset.url} target="_blank">
|
||||
{index === 0 ? dataset.name : ''}
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<a
|
||||
className="py-8 font-bold hover:underline pr-2"
|
||||
href={article.url}
|
||||
>
|
||||
{article.title}
|
||||
</a>
|
||||
</td>
|
||||
<td className="py-8 font-light text-[14px] min-w-[138px] font-mono text-[#999]">
|
||||
{format(article.date).includes('years')
|
||||
? new Date(article.date).toLocaleString('en-US', options)
|
||||
: format(article.date)}
|
||||
</td>
|
||||
<td className="py-8">
|
||||
{index === 0 && (
|
||||
<a
|
||||
className="ml-2 border border-zinc-900 font-light px-[25px] py-2.5 text-sm transition hover:bg-zinc-900 hover:text-white"
|
||||
href={`/datasets/${dataset.name}`}
|
||||
>
|
||||
explore
|
||||
</a>
|
||||
)}
|
||||
</td>
|
||||
{/*
|
||||
<td>
|
||||
<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 (
|
||||
<>
|
||||
<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-[600px] text-[17px] text-center text-[#6d6f71]">
|
||||
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 key={dataset.name} 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-normal text-xs pb-3">
|
||||
data set
|
||||
</th>
|
||||
<th className="uppercase text-left font-normal text-xs pb-3">
|
||||
related content
|
||||
</th>
|
||||
<th className="uppercase text-left font-normal text-xs pb-3">
|
||||
last updated
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{datasets.map((dataset) => (
|
||||
<DesktopItem key={dataset.name} 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>
|
||||
</>
|
||||
);
|
||||
}
|
||||
BIN
examples/fivethirtyeight/public/favicon.ico
Normal file
|
After Width: | Height: | Size: 25 KiB |
1
examples/fivethirtyeight/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/fivethirtyeight/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 |
8
examples/fivethirtyeight/styles/globals.css
Normal file
@@ -0,0 +1,8 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
.preview-table > div {
|
||||
overflow-x: scroll;
|
||||
overflow-y: hidden;
|
||||
}
|
||||
18
examples/fivethirtyeight/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: [require('@tailwindcss/typography')],
|
||||
};
|
||||
23
examples/fivethirtyeight/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"]
|
||||
}
|
||||
102
examples/github-backed-catalog/README.md
Normal file
@@ -0,0 +1,102 @@
|
||||
# A data catalog with data on GitHub
|
||||
|
||||
This example showcases a simple data catalog that get its data from a list of GitHub repos that serve as datasets.
|
||||
|
||||
A `datasets.json` file is used to specify which datasets are going to be part of the data catalog.
|
||||
|
||||
The application contains an index page, which lists all the datasets specified in the `datasets.json` file, and users can see more information about each dataset, such as the list of data files in it and the README, by clicking the "info" button on the list.
|
||||
|
||||
You can read more about it on the [Data catalog with data on GitHub](https://portaljs.org/docs/examples/github-backed-catalog) blog post.
|
||||
|
||||
## Demo
|
||||
|
||||
https://example.portaljs.org/
|
||||
|
||||
## Deploy your own
|
||||
|
||||
[](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Fdatopian%2Fportaljs%2Ftree%2Fmain%2Fexamples%2Fgithub-backed-catalog)
|
||||
|
||||
By clicking on this button, you will be redirected to a page which will allow you to clone the content into your own GitHub/GitLab/Bitbucket account and automatically deploy everything.
|
||||
|
||||
## How to use
|
||||
|
||||
### Install
|
||||
|
||||
Execute `create-next-app` to bootstrap the example:
|
||||
|
||||
```
|
||||
npx create-next-app <app-name> --example https://github.com/datopian/portaljs/tree/main/examples/github-backed-catalog
|
||||
cd <app-name>
|
||||
```
|
||||
|
||||
### Set environment variables
|
||||
|
||||
This project uses the GitHub API, which for anonymous users will cap at 50 requests per hour, so you might want to get a [Personal Access Token](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token) and add it to a `.env` file inside the folder like so
|
||||
|
||||
```
|
||||
GITHUB_PAT=<github token>
|
||||
```
|
||||
|
||||
### Change datasets
|
||||
|
||||
You can change the datasets that will be displayed in the data catalog by editing the file `datasets.json`. Some examples can be found inside [this repo](https://github.com/datasets).
|
||||
|
||||
### Run in development mode
|
||||
|
||||
Run the app using:
|
||||
|
||||
```
|
||||
npm run dev
|
||||
```
|
||||
|
||||
Open http://localhost:3000 from your browser. You should see something similar to this:
|
||||
|
||||

|
||||
|
||||
If click on the `info` button for a dataset you will see a page similar to this:
|
||||
|
||||

|
||||
|
||||
## Notes
|
||||
|
||||
### Structure of `datasets.json`
|
||||
|
||||
The `datasets.json` file is simply a list of datasets, below you can see a minimal example of a dataset:
|
||||
|
||||
```json
|
||||
{
|
||||
"owner": "fivethirtyeight",
|
||||
"repo": "data",
|
||||
"branch": "master",
|
||||
"files": ["nba-raptor/historical_RAPTOR_by_player.csv", "nba-raptor/historical_RAPTOR_by_team.csv"],
|
||||
"readme": "nba-raptor/README.md"
|
||||
}
|
||||
```
|
||||
|
||||
It has:
|
||||
|
||||
- A `owner` which is going to be the github repo owner
|
||||
- A `repo` which is going to be the github repo name
|
||||
- A `branch` which is going to be the branch to which we need to get the files and the readme
|
||||
- A list of `files` which is going to be a list of paths with files that you want to show to the world
|
||||
- A `readme` which is going to be the path to your data description, it can also be a subpath eg: `example/README.md`
|
||||
|
||||
You can also add:
|
||||
|
||||
- A `description` which is useful if you have more than one dataset for each repo, if not provided we are just going to use the repo description
|
||||
- A `Name` which is useful if you want to give your dataset a nice name, if not provided we are going to use the junction of the `owner` the `repo` + the path of the README, in the exaple above it will be `fivethirtyeight/data/nba-raptor`
|
||||
|
||||
### Extra commands
|
||||
|
||||
You can also build the project for production with:
|
||||
|
||||
```
|
||||
npm run build
|
||||
```
|
||||
|
||||
And run the production build with:
|
||||
|
||||
```
|
||||
npm run start
|
||||
```
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
import Link from "next/link";
|
||||
import HomeIcon from "../icons/HomeIcon";
|
||||
|
||||
export default function Breadcrumbs({ links }: { links: { title: string, href?: string, target?: string }[] }) {
|
||||
const current = links.at(-1);
|
||||
|
||||
return <div className="flex items-center uppercase font-black text-xs">
|
||||
<Link className="flex items-center" href='/'><HomeIcon /></Link>
|
||||
|
||||
{/* {links.length > 1 && links.slice(0, -1).map((link) => {
|
||||
return <>
|
||||
<span className="mx-4">/</span>
|
||||
<Link href={link.href}>{link.title}</Link>
|
||||
</>
|
||||
})} */}
|
||||
|
||||
<span className="mx-4">/</span>
|
||||
<span>{current.title}</span>
|
||||
</div >
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
export default function ExternalLinkIcon({ className = "" }) {
|
||||
return <div className={`inline-block w-4 ${className}`}><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" fill="currentColor"><path d="M 40 10 C 38.896 10 38 10.896 38 12 C 38 13.104 38.896 14 40 14 L 47.171875 14 L 30.585938 30.585938 C 29.804938 31.366938 29.804938 32.633063 30.585938 33.414062 C 30.976938 33.805063 31.488 34 32 34 C 32.512 34 33.023063 33.805062 33.414062 33.414062 L 50 16.828125 L 50 24 C 50 25.104 50.896 26 52 26 C 53.104 26 54 25.104 54 24 L 54 12 C 54 10.896 53.104 10 52 10 L 40 10 z M 18 12 C 14.691 12 12 14.691 12 18 L 12 46 C 12 49.309 14.691 52 18 52 L 46 52 C 49.309 52 52 49.309 52 46 L 52 34 C 52 32.896 51.104 32 50 32 C 48.896 32 48 32.896 48 34 L 48 46 C 48 47.103 47.103 48 46 48 L 18 48 C 16.897 48 16 47.103 16 46 L 16 18 C 16 16.897 16.897 16 18 16 L 30 16 C 31.104 16 32 15.104 32 14 C 32 12.896 31.104 12 30 12 L 18 12 z"/></svg></div>
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
export default function HomeIcon({ className = "" }) {
|
||||
return <div className={`inline-block w-4 ${className}`}><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"> <path d="M 12 2 A 1 1 0 0 0 11.289062 2.296875 L 1.203125 11.097656 A 0.5 0.5 0 0 0 1 11.5 A 0.5 0.5 0 0 0 1.5 12 L 4 12 L 4 20 C 4 20.552 4.448 21 5 21 L 9 21 C 9.552 21 10 20.552 10 20 L 10 14 L 14 14 L 14 20 C 14 20.552 14.448 21 15 21 L 19 21 C 19.552 21 20 20.552 20 20 L 20 12 L 22.5 12 A 0.5 0.5 0 0 0 23 11.5 A 0.5 0.5 0 0 0 22.796875 11.097656 L 12.716797 2.3027344 A 1 1 0 0 0 12.710938 2.296875 A 1 1 0 0 0 12 2 z"/></svg></div>
|
||||
}
|
||||
@@ -19,6 +19,7 @@
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0",
|
||||
"react-markdown": "^8.0.7",
|
||||
"react-timeago": "^7.1.0",
|
||||
"remark-gfm": "^3.0.1",
|
||||
"typescript": "5.0.4"
|
||||
},
|
||||
@@ -4797,6 +4798,14 @@
|
||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz",
|
||||
"integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w=="
|
||||
},
|
||||
"node_modules/react-timeago": {
|
||||
"version": "7.1.0",
|
||||
"resolved": "https://registry.npmjs.org/react-timeago/-/react-timeago-7.1.0.tgz",
|
||||
"integrity": "sha512-rouF7MiEm55fH791Y8cg+VobIJgx8gtNJ+gjr86R4ZqO1WKPkXiXjdT/lRzrvEkUzsxT1exHqV2V+Zdi114H3A==",
|
||||
"peerDependencies": {
|
||||
"react": "^16.0.0 || ^17.0.0 || ^18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/read-cache": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
|
||||
@@ -20,6 +20,7 @@
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0",
|
||||
"react-markdown": "^8.0.7",
|
||||
"react-timeago": "^7.1.0",
|
||||
"remark-gfm": "^3.0.1",
|
||||
"typescript": "5.0.4"
|
||||
},
|
||||
@@ -1,6 +1,3 @@
|
||||
import Head from 'next/head';
|
||||
import { useRouter } from 'next/router';
|
||||
|
||||
import { NextSeo } from 'next-seo';
|
||||
import { promises as fs } from 'fs';
|
||||
import path from 'path';
|
||||
@@ -8,15 +5,20 @@ import getConfig from 'next/config';
|
||||
import { getProject, GithubProject } from '../../../lib/octokit';
|
||||
import ReactMarkdown from 'react-markdown';
|
||||
import remarkGfm from 'remark-gfm';
|
||||
import Link from 'next/link';
|
||||
import Breadcrumbs from '../../../components/_shared/Breadcrumbs';
|
||||
|
||||
export default function ProjectPage({ project }) {
|
||||
const repoId = `@${project.repo_config.owner}/${project.repo_config.repo}`
|
||||
|
||||
return (
|
||||
<>
|
||||
<NextSeo title={`PortalJS - @${project.repo_config.owner}/${project.repo_config.repo}${project.base_path !== '/' ? '/' + project.base_path : ''}`} />
|
||||
<NextSeo title={`${repoId}${project.base_path !== '/' ? '/' + project.base_path : ''} - GitHub Datasets`} />
|
||||
<main className="prose mx-auto my-8">
|
||||
<Link href='/'>Back to homepage</Link>
|
||||
<h1 className="mb-0">Data</h1>
|
||||
<Breadcrumbs links={[{ title: repoId, href: "" }]} />
|
||||
<h1 className="mb-0 mt-16">{project.repo_config.name || repoId}</h1>
|
||||
<p className='mb-8'><span className='font-semibold'>Repository:</span> <a target="_blank" href={project.html_url}>{project.html_url}</a></p>
|
||||
|
||||
<h2 className="mb-0 mt-10">Files</h2>
|
||||
<div className="inline-block min-w-full py-2 align-middle">
|
||||
<table className="min-w-full divide-y divide-gray-300">
|
||||
<thead>
|
||||
@@ -50,7 +52,9 @@ export default function ProjectPage({ project }) {
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<h1>Readme</h1>
|
||||
<hr />
|
||||
|
||||
<h2 className='uppercase font-black'>Readme</h2>
|
||||
<ReactMarkdown remarkPlugins={[remarkGfm]}>
|
||||
{project.readmeContent}
|
||||
</ReactMarkdown>
|
||||
@@ -6,7 +6,7 @@ function CustomApp({ Component, pageProps }: AppProps) {
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<title>Welcome to simple-example!</title>
|
||||
<title>GitHub Datasets</title>
|
||||
</Head>
|
||||
<main className="app">
|
||||
<Component {...pageProps} />
|
||||
@@ -2,6 +2,9 @@ import { promises as fs } from 'fs';
|
||||
import path from 'path';
|
||||
import { getProject } from '../lib/octokit';
|
||||
import getConfig from 'next/config';
|
||||
import ExternalLinkIcon from '../components/icons/ExternalLinkIcon';
|
||||
import TimeAgo from 'react-timeago';
|
||||
import Link from 'next/link';
|
||||
|
||||
export async function getStaticProps() {
|
||||
const jsonDirectory = path.join(
|
||||
@@ -24,26 +27,18 @@ export async function getStaticProps() {
|
||||
};
|
||||
}
|
||||
|
||||
const formatter = new Intl.DateTimeFormat('en-US', {
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
day: 'numeric',
|
||||
hour: 'numeric',
|
||||
minute: 'numeric',
|
||||
second: 'numeric',
|
||||
timeZone: 'UTC',
|
||||
});
|
||||
|
||||
export function Datasets({ projects }) {
|
||||
return (
|
||||
<div className="bg-white">
|
||||
<div className="bg-white min-h-screen">
|
||||
<div className="mx-auto max-w-7xl px-6 py-16 sm:py-24 lg:px-8">
|
||||
<h2 className="text-2xl font-bold leading-10 tracking-tight">
|
||||
My Datasets
|
||||
</h2>
|
||||
<p className="mt-6 max-w-2xl text-base leading-7 text-gray-600">
|
||||
Here is a list of all my datasets for easy access and sharing
|
||||
</p>
|
||||
<div className='text-center'>
|
||||
<h2 className="text-3xl font-bold leading-10 tracking-tight">
|
||||
GitHub Datasets
|
||||
</h2>
|
||||
<p className="mt-3 mx-auto max-w-2xl text-base leading-7 text-gray-500">
|
||||
Data catalog with datasets hosted on GitHub by <Link target="_blank" className='underline' href="https://portaljs.org/">🌀 PortalJS</Link>
|
||||
</p>
|
||||
</div>
|
||||
<div className="mt-20">
|
||||
<div className="-mx-4 -my-2 overflow-x-auto sm:-mx-6 lg:-mx-8">
|
||||
<div className="inline-block min-w-full py-2 align-middle sm:px-6 lg:px-8">
|
||||
@@ -60,7 +55,7 @@ export function Datasets({ projects }) {
|
||||
scope="col"
|
||||
className="px-3 py-3.5 text-left text-sm font-semibold text-gray-900"
|
||||
>
|
||||
Repo
|
||||
Repository
|
||||
</th>
|
||||
<th
|
||||
scope="col"
|
||||
@@ -83,27 +78,28 @@ export function Datasets({ projects }) {
|
||||
<tbody className="divide-y divide-gray-200">
|
||||
{projects.map((project) => (
|
||||
<tr key={project.id}>
|
||||
<td className="whitespace-nowrap px-3 py-4 text-sm text-gray-500">
|
||||
<td className="whitespace-nowrap px-3 py-6 text-sm text-gray-500">
|
||||
{project.repo_config.name
|
||||
? project.repo_config.name
|
||||
: project.full_name + (project.base_path === '/' ? '' : '/' + project.base_path)}
|
||||
</td>
|
||||
<td className="whitespace-nowrap px-3 py-4 text-sm text-gray-500">
|
||||
<a href={project.html_url}>{project.full_name}</a>
|
||||
<td className="whitespace-nowrap px-3 py-6 text-sm group text-gray-500 hover:text-gray-900 transition-all duration-250">
|
||||
<a href={project.html_url} target="_blank" className='flex items-center'>@{project.full_name} <ExternalLinkIcon className='ml-1' /></a>
|
||||
</td>
|
||||
<td className="px-3 py-4 text-sm text-gray-500">
|
||||
{project.repo_config.description
|
||||
? project.repo_config.description
|
||||
: project.description}
|
||||
</td>
|
||||
<td className="whitespace-nowrap px-3 py-4 text-sm text-gray-500">
|
||||
{formatter.format(new Date(project.last_updated))}
|
||||
<td className="whitespace-nowrap px-3 py-6 text-sm text-gray-500">
|
||||
<TimeAgo date={new Date(project.last_updated)} />
|
||||
</td>
|
||||
<td className="relative whitespace-nowrap py-4 pl-3 pr-4 text-right text-sm font-medium sm:pr-0">
|
||||
<td className="relative whitespace-nowrap py-6 pl-3 pr-4 text-right text-sm font-medium sm:pr-0">
|
||||
<a
|
||||
href={`/@${project.repo_config.owner}/${project.repo_config.repo}/${project.base_path === '/' ? '' : project.base_path}`}
|
||||
className='border border-gray-900 text-gray-900 px-4 py-2 transition-all hover:bg-gray-900 hover:text-white'
|
||||
>
|
||||
More info
|
||||
info
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
80
examples/github-backed-catalog/pages/styles.css
Normal file
@@ -0,0 +1,80 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
html {
|
||||
-webkit-text-size-adjust: 100%;
|
||||
font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont,
|
||||
Segoe UI, Roboto, Helvetica Neue, Arial, Noto Sans, sans-serif,
|
||||
Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol, Noto Color Emoji;
|
||||
line-height: 1.5;
|
||||
tab-size: 4;
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
body {
|
||||
font-family: inherit;
|
||||
line-height: inherit;
|
||||
margin: 0;
|
||||
}
|
||||
h1,
|
||||
h2,
|
||||
p,
|
||||
pre {
|
||||
margin: 0;
|
||||
}
|
||||
*,
|
||||
::before,
|
||||
::after {
|
||||
box-sizing: border-box;
|
||||
border-width: 0;
|
||||
border-style: solid;
|
||||
border-color: currentColor;
|
||||
}
|
||||
h1,
|
||||
h2 {
|
||||
font-size: inherit;
|
||||
font-weight: inherit;
|
||||
}
|
||||
a {
|
||||
color: inherit;
|
||||
text-decoration: inherit;
|
||||
}
|
||||
pre {
|
||||
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,
|
||||
Liberation Mono, Courier New, monospace;
|
||||
}
|
||||
svg {
|
||||
display: block;
|
||||
vertical-align: middle;
|
||||
shape-rendering: auto;
|
||||
text-rendering: optimizeLegibility;
|
||||
}
|
||||
pre {
|
||||
background-color: rgba(55, 65, 81, 1);
|
||||
border-radius: 0.25rem;
|
||||
color: rgba(229, 231, 235, 1);
|
||||
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,
|
||||
Liberation Mono, Courier New, monospace;
|
||||
overflow: scroll;
|
||||
padding: 0.5rem 0.75rem;
|
||||
}
|
||||
|
||||
.shadow {
|
||||
box-shadow: 0 0 #0000, 0 0 #0000, 0 10px 15px -3px rgba(0, 0, 0, 0.1),
|
||||
0 4px 6px -2px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
.rounded {
|
||||
border-radius: 1.5rem;
|
||||
}
|
||||
.wrapper {
|
||||
width: 100%;
|
||||
}
|
||||
.container {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
max-width: 768px;
|
||||
padding-bottom: 3rem;
|
||||
padding-left: 1rem;
|
||||
padding-right: 1rem;
|
||||
color: rgba(55, 65, 81, 1);
|
||||
width: 100%;
|
||||
}
|
||||
6
examples/github-backed-catalog/postcss.config.js
Normal file
@@ -0,0 +1,6 @@
|
||||
module.exports = {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
}
|
||||
@@ -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 }) {
|
||||
@@ -1,55 +0,0 @@
|
||||
import VegaLite from "./VegaLite";
|
||||
|
||||
export default function LineChart({
|
||||
data = [],
|
||||
fullWidth = false,
|
||||
title = "",
|
||||
xAxis = "x",
|
||||
yAxis = "y",
|
||||
}) {
|
||||
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: "container",
|
||||
height: 300,
|
||||
mark: {
|
||||
type: "line",
|
||||
color: "black",
|
||||
strokeWidth: 1,
|
||||
tooltip: true,
|
||||
},
|
||||
data: {
|
||||
name: "table",
|
||||
},
|
||||
selection: {
|
||||
grid: {
|
||||
type: "interval",
|
||||
bind: "scales",
|
||||
},
|
||||
},
|
||||
encoding: {
|
||||
x: {
|
||||
field: xAxis,
|
||||
timeUnit: "year",
|
||||
type: "temporal",
|
||||
},
|
||||
y: {
|
||||
field: yAxis,
|
||||
type: "quantitative",
|
||||
},
|
||||
},
|
||||
};
|
||||
if (typeof data === 'string') {
|
||||
spec.data = { "url": data } as any
|
||||
return <VegaLite fullWidth={fullWidth} spec={spec} />;
|
||||
}
|
||||
|
||||
return <VegaLite fullWidth={fullWidth} data={vegaData} spec={spec} />;
|
||||
}
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
1261
examples/learn-example/package-lock.json
generated
@@ -7,21 +7,21 @@
|
||||
"build": "next build",
|
||||
"start": "next start",
|
||||
"lint": "next lint",
|
||||
"export": "npm run build && next export -o out"
|
||||
"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",
|
||||
@@ -30,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",
|
||||
@@ -43,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"
|
||||
}
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import { promises as fs } from 'fs';
|
||||
import { existsSync, promises as fs } from 'fs';
|
||||
import path from 'path';
|
||||
import parse from '../lib/markdown';
|
||||
import DRD from '../components/DRD';
|
||||
|
||||
import DataRichDocument from '../components/DataRichDocument';
|
||||
import clientPromise from '../lib/mddb';
|
||||
|
||||
export const getStaticPaths = async () => {
|
||||
const contentDir = path.join(process.cwd(), '/content/');
|
||||
@@ -23,37 +25,101 @@ export const getStaticProps = async (context) => {
|
||||
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');
|
||||
|
||||
let { mdxSource, frontMatter } = await parse(readme, '.mdx', { datasets });
|
||||
|
||||
return {
|
||||
props: {
|
||||
mdxSource,
|
||||
frontMatter,
|
||||
frontMatter: JSON.stringify(frontMatter),
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export default function DatasetPage({ mdxSource, frontMatter }) {
|
||||
frontMatter = JSON.parse(frontMatter);
|
||||
return (
|
||||
<div className="prose dark:prose-invert mx-auto">
|
||||
<div className="prose dark:prose-invert mx-auto py-8">
|
||||
<header>
|
||||
<div className="mb-6">
|
||||
<>
|
||||
<h1>{frontMatter.title}</h1>
|
||||
<h1 className="mb-2">{frontMatter.title}</h1>
|
||||
{frontMatter.author && (
|
||||
<div className="-mt-6">
|
||||
<p className="opacity-60 pl-1">{frontMatter.author}</p>
|
||||
</div>
|
||||
<p className="my-0">
|
||||
<span className="font-semibold">Author: </span>
|
||||
<span className="my-0">{frontMatter.author}</span>
|
||||
</p>
|
||||
)}
|
||||
{frontMatter.description && (
|
||||
<p className="description">{frontMatter.description}</p>
|
||||
<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>
|
||||
<DRD source={mdxSource} />
|
||||
<DataRichDocument source={mdxSource} />
|
||||
</main>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import '../styles/globals.css'
|
||||
import '@portaljs/components/styles.css'
|
||||
|
||||
import type { AppProps } from 'next/app'
|
||||
|
||||
export default function App({ Component, pageProps }: AppProps) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"target": "es6",
|
||||
"lib": ["dom", "dom.iterable", "esnext"],
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
|
||||
32
examples/openspending/.eslintrc.json
Normal file
@@ -0,0 +1,32 @@
|
||||
{
|
||||
"extends": [
|
||||
"next",
|
||||
"next/core-web-vitals"
|
||||
],
|
||||
"ignorePatterns": ["!**/*", ".next/**/*"],
|
||||
"overrides": [
|
||||
{
|
||||
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
|
||||
"rules": {
|
||||
"@next/next/no-html-link-for-pages": [
|
||||
"error",
|
||||
"examples/simple-example/pages"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"files": ["*.ts", "*.tsx"],
|
||||
"rules": {}
|
||||
},
|
||||
{
|
||||
"files": ["*.js", "*.jsx"],
|
||||
"rules": {}
|
||||
}
|
||||
],
|
||||
"rules": {
|
||||
"@next/next/no-html-link-for-pages": "off"
|
||||
},
|
||||
"env": {
|
||||
"jest": true
|
||||
}
|
||||
}
|
||||
35
examples/openspending/.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
|
||||
102
examples/openspending/README.md
Normal file
@@ -0,0 +1,102 @@
|
||||
# A data catalog with data on GitHub
|
||||
|
||||
This example showcases a simple data catalog that get its data from a list of GitHub repos that serve as datasets.
|
||||
|
||||
A `datasets.json` file is used to specify which datasets are going to be part of the data catalog.
|
||||
|
||||
The application contains an index page, which lists all the datasets specified in the `datasets.json` file, and users can see more information about each dataset, such as the list of data files in it and the README, by clicking the "info" button on the list.
|
||||
|
||||
You can read more about it on the [Data catalog with data on GitHub](https://portaljs.org/docs/examples/github-backed-catalog) blog post.
|
||||
|
||||
## Demo
|
||||
|
||||
https://example.portaljs.org/
|
||||
|
||||
## Deploy your own
|
||||
|
||||
[](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Fdatopian%2Fportaljs%2Ftree%2Fmain%2Fexamples%2Fgithub-backed-catalog)
|
||||
|
||||
By clicking on this button, you will be redirected to a page which will allow you to clone the content into your own GitHub/GitLab/Bitbucket account and automatically deploy everything.
|
||||
|
||||
## How to use
|
||||
|
||||
### Install
|
||||
|
||||
Execute `create-next-app` to bootstrap the example:
|
||||
|
||||
```
|
||||
npx create-next-app <app-name> --example https://github.com/datopian/portaljs/tree/main/examples/github-backed-catalog
|
||||
cd <app-name>
|
||||
```
|
||||
|
||||
### Set environment variables
|
||||
|
||||
This project uses the GitHub API, which for anonymous users will cap at 50 requests per hour, so you might want to get a [Personal Access Token](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token) and add it to a `.env` file inside the folder like so
|
||||
|
||||
```
|
||||
GITHUB_PAT=<github token>
|
||||
```
|
||||
|
||||
### Change datasets
|
||||
|
||||
You can change the datasets that will be displayed in the data catalog by editing the file `datasets.json`. Some examples can be found inside [this repo](https://github.com/datasets).
|
||||
|
||||
### Run in development mode
|
||||
|
||||
Run the app using:
|
||||
|
||||
```
|
||||
npm run dev
|
||||
```
|
||||
|
||||
Open http://localhost:3000 from your browser. You should see something similar to this:
|
||||
|
||||

|
||||
|
||||
If click on the `info` button for a dataset you will see a page similar to this:
|
||||
|
||||

|
||||
|
||||
## Notes
|
||||
|
||||
### Structure of `datasets.json`
|
||||
|
||||
The `datasets.json` file is simply a list of datasets, below you can see a minimal example of a dataset:
|
||||
|
||||
```json
|
||||
{
|
||||
"owner": "fivethirtyeight",
|
||||
"repo": "data",
|
||||
"branch": "master",
|
||||
"files": ["nba-raptor/historical_RAPTOR_by_player.csv", "nba-raptor/historical_RAPTOR_by_team.csv"],
|
||||
"readme": "nba-raptor/README.md"
|
||||
}
|
||||
```
|
||||
|
||||
It has:
|
||||
|
||||
- A `owner` which is going to be the github repo owner
|
||||
- A `repo` which is going to be the github repo name
|
||||
- A `branch` which is going to be the branch to which we need to get the files and the readme
|
||||
- A list of `files` which is going to be a list of paths with files that you want to show to the world
|
||||
- A `readme` which is going to be the path to your data description, it can also be a subpath eg: `example/README.md`
|
||||
|
||||
You can also add:
|
||||
|
||||
- A `description` which is useful if you have more than one dataset for each repo, if not provided we are just going to use the repo description
|
||||
- A `Name` which is useful if you want to give your dataset a nice name, if not provided we are going to use the junction of the `owner` the `repo` + the path of the README, in the exaple above it will be `fivethirtyeight/data/nba-raptor`
|
||||
|
||||
### Extra commands
|
||||
|
||||
You can also build the project for production with:
|
||||
|
||||
```
|
||||
npm run build
|
||||
```
|
||||
|
||||
And run the production build with:
|
||||
|
||||
```
|
||||
npm run start
|
||||
```
|
||||
|
||||
15
examples/openspending/components/Button.tsx
Normal file
@@ -0,0 +1,15 @@
|
||||
import Link from 'next/link'
|
||||
import clsx from 'clsx'
|
||||
|
||||
export function Button({ href, className = "", ...props }) {
|
||||
className = clsx(
|
||||
'inline-flex justify-center rounded-2xl bg-emerald-600 p-4 text-base font-semibold text-white hover:bg-emerald-500 focus:outline-none focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-emerald-500 active:text-white/70',
|
||||
className
|
||||
)
|
||||
|
||||
return href ? (
|
||||
<Link href={href} className={className} {...props} />
|
||||
) : (
|
||||
<button className={className} {...props} />
|
||||
)
|
||||
}
|
||||
10
examples/openspending/components/Container.tsx
Normal file
@@ -0,0 +1,10 @@
|
||||
import clsx from 'clsx'
|
||||
|
||||
export function Container({ className = "", ...props }) {
|
||||
return (
|
||||
<div
|
||||
className={clsx('mx-auto max-w-7xl px-4 sm:px-6 lg:px-8', className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
54
examples/openspending/components/Header.tsx
Normal file
@@ -0,0 +1,54 @@
|
||||
import Image from 'next/image'
|
||||
import { Button } from './Button'
|
||||
import { Container } from './Container'
|
||||
import logo from "../public/logo.svg"
|
||||
import Link from 'next/link'
|
||||
import { useRouter } from 'next/router'
|
||||
|
||||
export function Header() {
|
||||
const router = useRouter();
|
||||
|
||||
const isActive = (navLink) => {
|
||||
return router.asPath.split("?")[0] == navLink.href;
|
||||
}
|
||||
|
||||
const navLinks = [
|
||||
{
|
||||
title: "Home",
|
||||
href: "/#header"
|
||||
},
|
||||
{
|
||||
title: "Datasets",
|
||||
href: "/#datasets"
|
||||
},
|
||||
{
|
||||
title: "Community",
|
||||
href: "https://community.openspending.org/"
|
||||
}
|
||||
]
|
||||
|
||||
return (
|
||||
<header className="z-50 pb-5 lg:pt-11 sticky top-0 backdrop-blur" id="header">
|
||||
<Container className="flex flex-wrap items-center justify-center sm:justify-between lg:flex-nowrap">
|
||||
<div className="mt-10 lg:mt-0 lg:grow lg:basis-0 flex items-center">
|
||||
<Image src={logo} alt="OpenSpending" className="h-12 w-auto" />
|
||||
</div>
|
||||
<ul className='list-none flex gap-x-5 text-base font-medium'>
|
||||
{navLinks.map((link, i) => (
|
||||
<li key={`nav-link-${i}`}>
|
||||
<Link
|
||||
className={`text-emerald-900 hover:text-emerald-600 ${isActive(link) ? "text-emerald-600" : ""}`}
|
||||
href={link.href}
|
||||
scroll={false}
|
||||
>
|
||||
{link.title}
|
||||
</Link>
|
||||
</li>))}
|
||||
</ul>
|
||||
<div className="hidden sm:mt-10 sm:flex lg:mt-0 lg:grow lg:basis-0 lg:justify-end">
|
||||
<Button href="#">View on GitHub</Button>
|
||||
</div>
|
||||
</Container>
|
||||
</header >
|
||||
)
|
||||
}
|
||||
47
examples/openspending/components/Hero.tsx
Normal file
@@ -0,0 +1,47 @@
|
||||
import { Button } from './Button'
|
||||
import { Container } from './Container'
|
||||
|
||||
export function Hero() {
|
||||
return (
|
||||
<div className="relative pb-20 pt-10 sm:py-40">
|
||||
<div className="absolute inset-x-0 -bottom-14 -top-48 overflow-hidden bg-green-50 bg-opacity-50">
|
||||
<div className="absolute inset-x-0 top-0 h-40 bg-gradient-to-b from-white" />
|
||||
<div className="absolute inset-x-0 bottom-0 h-40 bg-gradient-to-t from-white" />
|
||||
</div>
|
||||
<Container className="relative">
|
||||
<div className="mx-auto max-w-2xl lg:max-w-4xl lg:px-12">
|
||||
<h1 className="font-display text-5xl font-bold tracking-tighter text-emerald-600 sm:text-7xl">
|
||||
It's our money!
|
||||
</h1>
|
||||
<div className="mt-6 space-y-6 font-display text-2xl tracking-tight text-emerald-900">
|
||||
<p>
|
||||
By understanding how governments spend money in our name can we have a say
|
||||
in how that money will affect our own lives. The journey starts here.
|
||||
</p>
|
||||
<p>
|
||||
OpenSpending is a free, open and global platform to search, visualise and analyse
|
||||
fiscal data in the public sphere.
|
||||
</p>
|
||||
</div>
|
||||
<Button href="#" className="mt-10 w-full sm:hidden">
|
||||
View on GitHub
|
||||
</Button>
|
||||
<dl className="mt-10 grid grid-cols-2 gap-x-10 gap-y-6 sm:mt-16 sm:gap-x-16 sm:gap-y-10 sm:text-center lg:auto-cols-auto lg:grid-flow-col lg:grid-cols-none lg:justify-start lg:text-left">
|
||||
{[
|
||||
['Countries', '75'],
|
||||
['Datasets', '2091'],
|
||||
['Files', '9230'],
|
||||
].map(([name, value]) => (
|
||||
<div key={name}>
|
||||
<dt className="font-mono text-sm text-emerald-600">{name}</dt>
|
||||
<dd className="mt-0.5 text-2xl font-semibold tracking-tight text-emerald-900">
|
||||
{value}
|
||||
</dd>
|
||||
</div>
|
||||
))}
|
||||
</dl>
|
||||
</div>
|
||||
</Container>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
20
examples/openspending/components/_shared/Breadcrumbs.tsx
Normal file
@@ -0,0 +1,20 @@
|
||||
import Link from "next/link";
|
||||
import HomeIcon from "../icons/HomeIcon";
|
||||
|
||||
export default function Breadcrumbs({ links }: { links: { title: string, href?: string, target?: string }[] }) {
|
||||
const current = links.at(-1);
|
||||
|
||||
return <div className="flex items-center uppercase font-black text-xs">
|
||||
<Link className="flex items-center" href='/'><HomeIcon /></Link>
|
||||
|
||||
{/* {links.length > 1 && links.slice(0, -1).map((link) => {
|
||||
return <>
|
||||
<span className="mx-4">/</span>
|
||||
<Link href={link.href}>{link.title}</Link>
|
||||
</>
|
||||
})} */}
|
||||
|
||||
<span className="mx-4">/</span>
|
||||
<span>{current.title}</span>
|
||||
</div >
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
export default function ExternalLinkIcon({ className = "" }) {
|
||||
return <div className={`inline-block w-4 ${className}`}><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" fill="currentColor"><path d="M 40 10 C 38.896 10 38 10.896 38 12 C 38 13.104 38.896 14 40 14 L 47.171875 14 L 30.585938 30.585938 C 29.804938 31.366938 29.804938 32.633063 30.585938 33.414062 C 30.976938 33.805063 31.488 34 32 34 C 32.512 34 33.023063 33.805062 33.414062 33.414062 L 50 16.828125 L 50 24 C 50 25.104 50.896 26 52 26 C 53.104 26 54 25.104 54 24 L 54 12 C 54 10.896 53.104 10 52 10 L 40 10 z M 18 12 C 14.691 12 12 14.691 12 18 L 12 46 C 12 49.309 14.691 52 18 52 L 46 52 C 49.309 52 52 49.309 52 46 L 52 34 C 52 32.896 51.104 32 50 32 C 48.896 32 48 32.896 48 34 L 48 46 C 48 47.103 47.103 48 46 48 L 18 48 C 16.897 48 16 47.103 16 46 L 16 18 C 16 16.897 16.897 16 18 16 L 30 16 C 31.104 16 32 15.104 32 14 C 32 12.896 31.104 12 30 12 L 18 12 z"/></svg></div>
|
||||
}
|
||||
3
examples/openspending/components/icons/HomeIcon.tsx
Normal file
@@ -0,0 +1,3 @@
|
||||
export default function HomeIcon({ className = "" }) {
|
||||
return <div className={`inline-block w-4 ${className}`}><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"> <path d="M 12 2 A 1 1 0 0 0 11.289062 2.296875 L 1.203125 11.097656 A 0.5 0.5 0 0 0 1 11.5 A 0.5 0.5 0 0 0 1.5 12 L 4 12 L 4 20 C 4 20.552 4.448 21 5 21 L 9 21 C 9.552 21 10 20.552 10 20 L 10 14 L 14 14 L 14 20 C 14 20.552 14.448 21 15 21 L 19 21 C 19.552 21 20 20.552 20 20 L 20 12 L 22.5 12 A 0.5 0.5 0 0 0 23 11.5 A 0.5 0.5 0 0 0 22.796875 11.097656 L 12.716797 2.3027344 A 1 1 0 0 0 12.710938 2.296875 A 1 1 0 0 0 12 2 z"/></svg></div>
|
||||
}
|
||||
25
examples/openspending/datasets.json
Normal file
@@ -0,0 +1,25 @@
|
||||
[
|
||||
{
|
||||
"owner": "os-data",
|
||||
"branch": "main",
|
||||
"repo": "mongolia-budget-2016-2017",
|
||||
"files": [
|
||||
"data/mongolia-2017.csv",
|
||||
"data/mongolia-2017__2017.csv"
|
||||
]
|
||||
},
|
||||
{
|
||||
"owner": "os-data",
|
||||
"branch": "main",
|
||||
"repo": "gb-country-regional-analysis",
|
||||
"files": [
|
||||
"data/cofog.csv",
|
||||
"data/cofog_dejargonise.csv",
|
||||
"data/cra.csv",
|
||||
"data/departments.csv",
|
||||
"data/nuts_pop.csv",
|
||||
"data/pogs.csv"
|
||||
],
|
||||
"readme": "README.md"
|
||||
}
|
||||
]
|
||||
6
examples/openspending/index.d.ts
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
declare module '*.svg' {
|
||||
const content: any;
|
||||
export const ReactComponent: any;
|
||||
export default content;
|
||||
}
|
||||
164
examples/openspending/lib/octokit.ts
Normal file
@@ -0,0 +1,164 @@
|
||||
import { Octokit } from 'octokit';
|
||||
|
||||
export interface GithubProject {
|
||||
owner: string;
|
||||
repo: string;
|
||||
branch: string;
|
||||
files: string[];
|
||||
readme: string;
|
||||
description?: string;
|
||||
name?: string;
|
||||
}
|
||||
|
||||
export async function getProjectReadme(
|
||||
owner: string,
|
||||
repo: string,
|
||||
branch: string,
|
||||
readme: string,
|
||||
github_pat?: string
|
||||
) {
|
||||
const octokit = new Octokit({ auth: github_pat });
|
||||
try {
|
||||
const response = await octokit.rest.repos.getContent({
|
||||
owner,
|
||||
repo,
|
||||
path: readme,
|
||||
ref: branch,
|
||||
});
|
||||
const data = response.data as { content?: string };
|
||||
const fileContent = data.content ? data.content : '';
|
||||
if (fileContent === '') {
|
||||
return null;
|
||||
}
|
||||
const decodedContent = Buffer.from(fileContent, 'base64').toString();
|
||||
return decodedContent;
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export async function getLastUpdated(
|
||||
owner: string,
|
||||
repo: string,
|
||||
branch: string,
|
||||
readme: string,
|
||||
github_pat?: string
|
||||
) {
|
||||
const octokit = new Octokit({ auth: github_pat });
|
||||
try {
|
||||
const response = await octokit.rest.repos.listCommits({
|
||||
owner,
|
||||
repo,
|
||||
path: readme,
|
||||
ref: branch,
|
||||
});
|
||||
return response.data[0].commit.committer.date;
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
export async function getProjectMetadata(
|
||||
owner: string,
|
||||
repo: string,
|
||||
github_pat?: string
|
||||
) {
|
||||
const octokit = new Octokit({ auth: github_pat });
|
||||
try {
|
||||
const response = await octokit.rest.repos.get({
|
||||
owner,
|
||||
repo,
|
||||
});
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export async function getRepoContents(
|
||||
owner: string,
|
||||
repo: string,
|
||||
branch: string,
|
||||
files: string[],
|
||||
github_pat?: string
|
||||
) {
|
||||
const octokit = new Octokit({ auth: github_pat });
|
||||
try {
|
||||
const contents = [];
|
||||
for (const path of files) {
|
||||
const response = await octokit.rest.repos.getContent({
|
||||
owner,
|
||||
repo,
|
||||
ref: branch,
|
||||
path: path,
|
||||
});
|
||||
const data = response.data as {
|
||||
download_url?: string;
|
||||
name: string;
|
||||
size: number;
|
||||
};
|
||||
contents.push({
|
||||
download_url: data.download_url,
|
||||
name: data.name,
|
||||
size: data.size,
|
||||
});
|
||||
}
|
||||
return contents;
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export async function getProject(project: GithubProject, github_pat?: string) {
|
||||
const projectMetadata = await getProjectMetadata(
|
||||
project.owner,
|
||||
project.repo,
|
||||
github_pat
|
||||
);
|
||||
if (!projectMetadata) {
|
||||
return null;
|
||||
}
|
||||
const projectReadme = await getProjectReadme(
|
||||
project.owner,
|
||||
project.repo,
|
||||
project.branch,
|
||||
project.readme,
|
||||
github_pat
|
||||
);
|
||||
|
||||
const projectData = await getRepoContents(
|
||||
project.owner,
|
||||
project.repo,
|
||||
project.branch,
|
||||
project.files,
|
||||
github_pat
|
||||
);
|
||||
if (!projectData) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let projectBase = "", last_updated = "";
|
||||
if (projectReadme) {
|
||||
projectBase =
|
||||
project.readme.split('/').length > 1
|
||||
? project.readme.split('/').slice(0, -1).join('/')
|
||||
: '/';
|
||||
last_updated = await getLastUpdated(
|
||||
project.owner,
|
||||
project.repo,
|
||||
project.branch,
|
||||
projectBase,
|
||||
github_pat
|
||||
);
|
||||
}
|
||||
return {
|
||||
...projectMetadata,
|
||||
files: projectData,
|
||||
readmeContent: projectReadme,
|
||||
last_updated,
|
||||
base_path: projectBase,
|
||||
};
|
||||
}
|
||||
17
examples/openspending/next.config.js
Normal file
@@ -0,0 +1,17 @@
|
||||
const nextConfig = {
|
||||
async rewrites() {
|
||||
return {
|
||||
beforeFiles: [
|
||||
{
|
||||
source: '/@:org/:project*',
|
||||
destination: '/@org/:org/:project*',
|
||||
},
|
||||
],
|
||||
};
|
||||
},
|
||||
serverRuntimeConfig: {
|
||||
github_pat: process.env.GITHUB_PAT ? process.env.GITHUB_PAT : null,
|
||||
},
|
||||
};
|
||||
|
||||
module.exports = nextConfig;
|
||||
5866
examples/openspending/package-lock.json
generated
Normal file
35
examples/openspending/package.json
Normal file
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"name": "my-app",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "next dev",
|
||||
"build": "next build",
|
||||
"start": "next start",
|
||||
"lint": "next lint"
|
||||
},
|
||||
"dependencies": {
|
||||
"@types/node": "18.16.0",
|
||||
"@types/react": "18.0.38",
|
||||
"@types/react-dom": "18.0.11",
|
||||
"clsx": "^1.2.1",
|
||||
"eslint": "8.39.0",
|
||||
"eslint-config-next": "13.3.1",
|
||||
"next": "13.3.1",
|
||||
"next-seo": "^6.0.0",
|
||||
"octokit": "^2.0.14",
|
||||
"prettier": "^2.8.8",
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0",
|
||||
"react-markdown": "^8.0.7",
|
||||
"react-timeago": "^7.1.0",
|
||||
"remark-gfm": "^3.0.1",
|
||||
"typescript": "5.0.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tailwindcss/typography": "^0.5.9",
|
||||
"autoprefixer": "^10.4.14",
|
||||
"postcss": "^8.4.23",
|
||||
"tailwindcss": "^3.3.1"
|
||||
}
|
||||
}
|
||||
126
examples/openspending/pages/@org/[org]/[...path].tsx
Normal file
@@ -0,0 +1,126 @@
|
||||
import { NextSeo } from 'next-seo';
|
||||
import { promises as fs } from 'fs';
|
||||
import path from 'path';
|
||||
import getConfig from 'next/config';
|
||||
import { getProject, GithubProject } from '../../../lib/octokit';
|
||||
import ReactMarkdown from 'react-markdown';
|
||||
import remarkGfm from 'remark-gfm';
|
||||
import Breadcrumbs from '../../../components/_shared/Breadcrumbs';
|
||||
|
||||
export default function ProjectPage({ project }) {
|
||||
const repoId = `@${project.repo_config.owner}/${project.repo_config.repo}`
|
||||
|
||||
return (
|
||||
<>
|
||||
<NextSeo title={`${repoId}${project.base_path !== '/' ? '/' + project.base_path : ''} - GitHub Datasets`} />
|
||||
<main className="prose mx-auto my-8">
|
||||
<Breadcrumbs links={[{ title: repoId, href: "" }]} />
|
||||
<h1 className="mb-0 mt-16">{project.repo_config.name || repoId}</h1>
|
||||
<p className='mb-8'><span className='font-semibold'>Repository:</span> <a target="_blank" href={project.html_url}>{project.html_url}</a></p>
|
||||
|
||||
<h2 className="mb-0 mt-10">Files</h2>
|
||||
<div className="inline-block min-w-full py-2 align-middle">
|
||||
<table className="min-w-full divide-y divide-gray-300">
|
||||
<thead>
|
||||
<tr>
|
||||
<th
|
||||
scope="col"
|
||||
className="px-3 py-3.5 text-left text-sm font-semibold text-gray-900"
|
||||
>
|
||||
Name
|
||||
</th>
|
||||
<th
|
||||
scope="col"
|
||||
className="px-3 py-3.5 text-left text-sm font-semibold text-gray-900"
|
||||
>
|
||||
Size
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="divide-y divide-gray-200">
|
||||
{project.files.map((file) => (
|
||||
<tr key={file.download_url}>
|
||||
<td className="whitespace-nowrap px-3 py-4 text-sm text-gray-500">
|
||||
<a href={file.download_url}>{file.name}</a>
|
||||
</td>
|
||||
<td className="whitespace-nowrap px-3 py-4 text-sm text-gray-500">
|
||||
{file.size} Bytes
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
{project.readmeContent && <>
|
||||
<hr />
|
||||
|
||||
<h2 className='uppercase font-black'>Readme</h2>
|
||||
<ReactMarkdown remarkPlugins={[remarkGfm]}>
|
||||
{project.readmeContent}
|
||||
</ReactMarkdown>
|
||||
</>}
|
||||
</main>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
// Generates `/posts/1` and `/posts/2`
|
||||
export async function getStaticPaths() {
|
||||
const jsonDirectory = path.join(
|
||||
process.cwd(),
|
||||
'datasets.json'
|
||||
);
|
||||
const repos = await fs.readFile(jsonDirectory, 'utf8');
|
||||
|
||||
return {
|
||||
paths: JSON.parse(repos).map((repo) => {
|
||||
const projectPath =
|
||||
repo.readme && repo.readme.split('/').length > 1
|
||||
? repo.readme.split('/').slice(0, -1)
|
||||
: null;
|
||||
let path = [repo.repo];
|
||||
if (projectPath) {
|
||||
projectPath.forEach((element) => {
|
||||
path.push(element);
|
||||
});
|
||||
}
|
||||
return {
|
||||
params: { org: repo.owner, path },
|
||||
};
|
||||
}),
|
||||
fallback: false, // can also be true or 'blocking'
|
||||
};
|
||||
}
|
||||
|
||||
export async function getStaticProps({ params }) {
|
||||
const jsonDirectory = path.join(
|
||||
process.cwd(),
|
||||
'datasets.json'
|
||||
);
|
||||
const reposFile = await fs.readFile(jsonDirectory, 'utf8');
|
||||
const repos: GithubProject[] = JSON.parse(reposFile);
|
||||
const repo = repos.find((_repo) => {
|
||||
const projectPath =
|
||||
_repo.readme && _repo.readme.split('/').length > 1
|
||||
? _repo.readme.split('/').slice(0, -1)
|
||||
: null;
|
||||
let path = [_repo.repo];
|
||||
if (projectPath) {
|
||||
projectPath.forEach((element) => {
|
||||
path.push(element);
|
||||
});
|
||||
}
|
||||
return (
|
||||
_repo.owner == params.org &&
|
||||
JSON.stringify(path) === JSON.stringify(params.path)
|
||||
);
|
||||
});
|
||||
const github_pat = getConfig().serverRuntimeConfig.github_pat;
|
||||
const project = await getProject(repo, github_pat);
|
||||
return {
|
||||
props: {
|
||||
project: { ...project, repo_config: repo },
|
||||
},
|
||||
};
|
||||
}
|
||||
18
examples/openspending/pages/_app.tsx
Normal file
@@ -0,0 +1,18 @@
|
||||
import { AppProps } from 'next/app';
|
||||
import Head from 'next/head';
|
||||
import './styles.css';
|
||||
|
||||
function CustomApp({ Component, pageProps }: AppProps) {
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<title>GitHub Datasets</title>
|
||||
</Head>
|
||||
<main className="app">
|
||||
<Component {...pageProps} />
|
||||
</main>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default CustomApp;
|
||||
130
examples/openspending/pages/index.tsx
Normal file
@@ -0,0 +1,130 @@
|
||||
import { promises as fs } from 'fs';
|
||||
import path from 'path';
|
||||
import { getProject } from '../lib/octokit';
|
||||
import getConfig from 'next/config';
|
||||
import ExternalLinkIcon from '../components/icons/ExternalLinkIcon';
|
||||
import TimeAgo from 'react-timeago';
|
||||
import Link from 'next/link';
|
||||
import { Hero } from '../components/Hero';
|
||||
import { Header } from '../components/Header';
|
||||
import { Container } from '../components/Container';
|
||||
|
||||
export async function getStaticProps() {
|
||||
const jsonDirectory = path.join(
|
||||
process.cwd(),
|
||||
'/datasets.json'
|
||||
);
|
||||
const repos = await fs.readFile(jsonDirectory, 'utf8');
|
||||
const github_pat = getConfig().serverRuntimeConfig.github_pat;
|
||||
|
||||
const projects = await Promise.all(
|
||||
(JSON.parse(repos)).map(async (repo) => {
|
||||
const project = await getProject(repo, github_pat);
|
||||
return { ...project, repo_config: repo };
|
||||
})
|
||||
);
|
||||
|
||||
return {
|
||||
props: {
|
||||
projects,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function Datasets({ projects }) {
|
||||
return (
|
||||
<div className="bg-white min-h-screen">
|
||||
<Header />
|
||||
<Hero />
|
||||
<section
|
||||
className="py-20 sm:py-32"
|
||||
>
|
||||
<Container>
|
||||
<div className="mx-auto max-w-2xl lg:mx-0">
|
||||
<h2
|
||||
id="datasets"
|
||||
className="font-display text-4xl font-medium tracking-tighter text-emerald-600 sm:text-5xl"
|
||||
>
|
||||
Datasets
|
||||
</h2>
|
||||
<p className="mt-4 font-display text-2xl tracking-tight text-emerald-900">
|
||||
Find spending data about countries all around the world.
|
||||
</p>
|
||||
</div>
|
||||
<div className="mt-5">
|
||||
<div className="-mx-4 -my-2 overflow-x-auto sm:-mx-6 lg:-mx-8">
|
||||
<div className="inline-block min-w-full py-2 align-middle sm:px-6 lg:px-8">
|
||||
<table className="min-w-full divide-y divide-gray-300">
|
||||
<thead>
|
||||
<tr>
|
||||
<th
|
||||
scope="col"
|
||||
className="px-3 py-3.5 text-left text-sm font-semibold text-gray-900"
|
||||
>
|
||||
Name
|
||||
</th>
|
||||
<th
|
||||
scope="col"
|
||||
className="px-3 py-3.5 text-left text-sm font-semibold text-gray-900"
|
||||
>
|
||||
Repository
|
||||
</th>
|
||||
<th
|
||||
scope="col"
|
||||
className="px-3 py-3.5 text-left text-sm font-semibold text-gray-900"
|
||||
>
|
||||
Description
|
||||
</th>
|
||||
<th
|
||||
scope="col"
|
||||
className="px-3 py-3.5 text-left text-sm font-semibold text-gray-900"
|
||||
>
|
||||
Last updated
|
||||
</th>
|
||||
<th
|
||||
scope="col"
|
||||
className="relative py-3.5 pl-3 pr-4 sm:pr-0"
|
||||
></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="divide-y divide-gray-200">
|
||||
{projects.map((project) => (
|
||||
<tr key={project.id}>
|
||||
<td className="whitespace-nowrap px-3 py-6 text-sm text-gray-500">
|
||||
{project.repo_config.name
|
||||
? project.repo_config.name
|
||||
: project.full_name + (project.base_path === '/' ? '' : '/' + project.base_path)}
|
||||
</td>
|
||||
<td className="whitespace-nowrap px-3 py-6 text-sm group text-gray-500 hover:text-gray-900 transition-all duration-250">
|
||||
<a href={project.html_url} target="_blank" className='flex items-center'>@{project.full_name} <ExternalLinkIcon className='ml-1' /></a>
|
||||
</td>
|
||||
<td className="px-3 py-4 text-sm text-gray-500">
|
||||
{project.repo_config.description
|
||||
? project.repo_config.description
|
||||
: project.description}
|
||||
</td>
|
||||
<td className="whitespace-nowrap px-3 py-6 text-sm text-gray-500">
|
||||
<TimeAgo date={new Date(project.last_updated)} />
|
||||
</td>
|
||||
<td className="relative whitespace-nowrap py-6 pl-3 pr-4 text-right text-sm font-medium sm:pr-0">
|
||||
<a
|
||||
href={`/@${project.repo_config.owner}/${project.repo_config.repo}/${project.base_path === '/' ? '' : project.base_path}`}
|
||||
className='border border-gray-900 text-gray-900 px-4 py-2 transition-all hover:bg-gray-900 hover:text-white'
|
||||
>
|
||||
info
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Container>
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default Datasets;
|
||||
80
examples/openspending/pages/styles.css
Normal file
@@ -0,0 +1,80 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
html {
|
||||
-webkit-text-size-adjust: 100%;
|
||||
font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont,
|
||||
Segoe UI, Roboto, Helvetica Neue, Arial, Noto Sans, sans-serif,
|
||||
Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol, Noto Color Emoji;
|
||||
line-height: 1.5;
|
||||
tab-size: 4;
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
body {
|
||||
font-family: inherit;
|
||||
line-height: inherit;
|
||||
margin: 0;
|
||||
}
|
||||
h1,
|
||||
h2,
|
||||
p,
|
||||
pre {
|
||||
margin: 0;
|
||||
}
|
||||
*,
|
||||
::before,
|
||||
::after {
|
||||
box-sizing: border-box;
|
||||
border-width: 0;
|
||||
border-style: solid;
|
||||
border-color: currentColor;
|
||||
}
|
||||
h1,
|
||||
h2 {
|
||||
font-size: inherit;
|
||||
font-weight: inherit;
|
||||
}
|
||||
a {
|
||||
color: inherit;
|
||||
text-decoration: inherit;
|
||||
}
|
||||
pre {
|
||||
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,
|
||||
Liberation Mono, Courier New, monospace;
|
||||
}
|
||||
svg {
|
||||
display: block;
|
||||
vertical-align: middle;
|
||||
shape-rendering: auto;
|
||||
text-rendering: optimizeLegibility;
|
||||
}
|
||||
pre {
|
||||
background-color: rgba(55, 65, 81, 1);
|
||||
border-radius: 0.25rem;
|
||||
color: rgba(229, 231, 235, 1);
|
||||
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,
|
||||
Liberation Mono, Courier New, monospace;
|
||||
overflow: scroll;
|
||||
padding: 0.5rem 0.75rem;
|
||||
}
|
||||
|
||||
.shadow {
|
||||
box-shadow: 0 0 #0000, 0 0 #0000, 0 10px 15px -3px rgba(0, 0, 0, 0.1),
|
||||
0 4px 6px -2px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
.rounded {
|
||||
border-radius: 1.5rem;
|
||||
}
|
||||
.wrapper {
|
||||
width: 100%;
|
||||
}
|
||||
.container {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
max-width: 768px;
|
||||
padding-bottom: 3rem;
|
||||
padding-left: 1rem;
|
||||
padding-right: 1rem;
|
||||
color: rgba(55, 65, 81, 1);
|
||||
width: 100%;
|
||||
}
|
||||
6
examples/openspending/postcss.config.js
Normal file
@@ -0,0 +1,6 @@
|
||||
module.exports = {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
}
|
||||
0
examples/openspending/public/.gitkeep
Normal file
18
examples/openspending/public/logo.svg
Normal file
|
After Width: | Height: | Size: 9.6 KiB |
15
examples/openspending/tailwind.config.js
Normal file
@@ -0,0 +1,15 @@
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
module.exports = {
|
||||
content: [
|
||||
"./app/**/*.{js,ts,jsx,tsx,mdx}",
|
||||
"./pages/**/*.{js,ts,jsx,tsx,mdx}",
|
||||
"./components/**/*.{js,ts,jsx,tsx,mdx}",
|
||||
],
|
||||
theme: {
|
||||
extend: {},
|
||||
},
|
||||
plugins: [
|
||||
require('@tailwindcss/typography')
|
||||
],
|
||||
}
|
||||
|
||||
20
examples/openspending/tsconfig.json
Normal file
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"lib": ["dom", "dom.iterable", "esnext"],
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"strict": false,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"noEmit": true,
|
||||
"esModuleInterop": true,
|
||||
"module": "esnext",
|
||||
"moduleResolution": "node",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"jsx": "preserve",
|
||||
"incremental": true
|
||||
},
|
||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||
@@ -1,75 +0,0 @@
|
||||
This is a repo intended to serve as a simple example of a data catalog that get its data from a series of github repos, you can init an example just like this one by.
|
||||
|
||||
- Creating a new project with `create-next-app` like so:
|
||||
|
||||
```
|
||||
npx create-next-app <app-name> --example https://github.com/datopian/portaljs/tree/main/examples/simple-example
|
||||
cd <app-name>
|
||||
```
|
||||
|
||||
- This project uses the github api, which for anonymous users will cap at 50 requests per hour, so you might want to get a [Personal Access Token](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token) and add it to a `.env` file inside the folder like so
|
||||
|
||||
```
|
||||
GITHUB_PAT=<github token>
|
||||
```
|
||||
|
||||
- Edit the file `datasets.json` to your liking, some examples can be found inside this [repo](https://github.com/datasets)
|
||||
- Run the app using:
|
||||
|
||||
```
|
||||
npm run dev
|
||||
```
|
||||
|
||||
Congratulations, you now have something similar to this running on `http://localhost:3000`
|
||||

|
||||
If yo go to any one of those pages by clicking on `More info` you will see something similar to this
|
||||

|
||||
|
||||
## Deployment
|
||||
|
||||
[](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Fdatopian%2Fportaljs%2Ftree%2Fmain%2Fexamples%2Fsimple-example)
|
||||
|
||||
By clicking on this button, you will be redirected to a page which will allow you to clone the content into your own github/gitlab/bitbucket account and automatically deploy everything.
|
||||
|
||||
|
||||
## Structure of `datasets.json`
|
||||
|
||||
The `datasets.json` file is simply a list of datasets, below you can see a minimal example of a dataset
|
||||
|
||||
```json
|
||||
{
|
||||
"owner": "fivethirtyeight",
|
||||
"repo": "data",
|
||||
"branch": "master",
|
||||
"files": ["nba-raptor/historical_RAPTOR_by_player.csv", "nba-raptor/historical_RAPTOR_by_team.csv"],
|
||||
"readme": "nba-raptor/README.md"
|
||||
}
|
||||
```
|
||||
|
||||
It has
|
||||
|
||||
- A `owner` which is going to be the github repo owner
|
||||
- A `repo` which is going to be the github repo name
|
||||
- A `branch` which is going to be the branch to which we need to get the files and the readme
|
||||
- A list of `files` which is going to be a list of paths with files that you want to show to the world
|
||||
- A `readme` which is going to be the path to your data description, it can also be a subpath eg: `example/README.md`
|
||||
|
||||
You can also add
|
||||
|
||||
- A `description` which is useful if you have more than one dataset for each repo, if not provided we are just going to use the repo description
|
||||
- A `Name` which is useful if you want to give your dataset a nice name, if not provided we are going to use the junction of the `owner` the `repo` + the path of the README, in the exaple above it will be `fivethirtyeight/data/nba-raptor`
|
||||
|
||||
## Extra commands
|
||||
|
||||
You can also build the project for production with
|
||||
|
||||
```
|
||||
npm run build
|
||||
```
|
||||
|
||||
And run using the production build like so:
|
||||
|
||||
```
|
||||
npm run start
|
||||
```
|
||||
|
||||
@@ -1,403 +0,0 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
html {
|
||||
-webkit-text-size-adjust: 100%;
|
||||
font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont,
|
||||
Segoe UI, Roboto, Helvetica Neue, Arial, Noto Sans, sans-serif,
|
||||
Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol, Noto Color Emoji;
|
||||
line-height: 1.5;
|
||||
tab-size: 4;
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
body {
|
||||
font-family: inherit;
|
||||
line-height: inherit;
|
||||
margin: 0;
|
||||
}
|
||||
h1,
|
||||
h2,
|
||||
p,
|
||||
pre {
|
||||
margin: 0;
|
||||
}
|
||||
*,
|
||||
::before,
|
||||
::after {
|
||||
box-sizing: border-box;
|
||||
border-width: 0;
|
||||
border-style: solid;
|
||||
border-color: currentColor;
|
||||
}
|
||||
h1,
|
||||
h2 {
|
||||
font-size: inherit;
|
||||
font-weight: inherit;
|
||||
}
|
||||
a {
|
||||
color: inherit;
|
||||
text-decoration: inherit;
|
||||
}
|
||||
pre {
|
||||
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,
|
||||
Liberation Mono, Courier New, monospace;
|
||||
}
|
||||
svg {
|
||||
display: block;
|
||||
vertical-align: middle;
|
||||
shape-rendering: auto;
|
||||
text-rendering: optimizeLegibility;
|
||||
}
|
||||
pre {
|
||||
background-color: rgba(55, 65, 81, 1);
|
||||
border-radius: 0.25rem;
|
||||
color: rgba(229, 231, 235, 1);
|
||||
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,
|
||||
Liberation Mono, Courier New, monospace;
|
||||
overflow: scroll;
|
||||
padding: 0.5rem 0.75rem;
|
||||
}
|
||||
|
||||
.shadow {
|
||||
box-shadow: 0 0 #0000, 0 0 #0000, 0 10px 15px -3px rgba(0, 0, 0, 0.1),
|
||||
0 4px 6px -2px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
.rounded {
|
||||
border-radius: 1.5rem;
|
||||
}
|
||||
.wrapper {
|
||||
width: 100%;
|
||||
}
|
||||
.container {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
max-width: 768px;
|
||||
padding-bottom: 3rem;
|
||||
padding-left: 1rem;
|
||||
padding-right: 1rem;
|
||||
color: rgba(55, 65, 81, 1);
|
||||
width: 100%;
|
||||
}
|
||||
#welcome {
|
||||
margin-top: 2.5rem;
|
||||
}
|
||||
#welcome h1 {
|
||||
font-size: 3rem;
|
||||
font-weight: 500;
|
||||
letter-spacing: -0.025em;
|
||||
line-height: 1;
|
||||
}
|
||||
#welcome span {
|
||||
display: block;
|
||||
font-size: 1.875rem;
|
||||
font-weight: 300;
|
||||
line-height: 2.25rem;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
#hero {
|
||||
align-items: center;
|
||||
background-color: hsla(214, 62%, 21%, 1);
|
||||
border: none;
|
||||
box-sizing: border-box;
|
||||
color: rgba(55, 65, 81, 1);
|
||||
display: grid;
|
||||
grid-template-columns: 1fr;
|
||||
margin-top: 3.5rem;
|
||||
}
|
||||
#hero .text-container {
|
||||
color: rgba(255, 255, 255, 1);
|
||||
padding: 3rem 2rem;
|
||||
}
|
||||
#hero .text-container h2 {
|
||||
font-size: 1.5rem;
|
||||
line-height: 2rem;
|
||||
position: relative;
|
||||
}
|
||||
#hero .text-container h2 svg {
|
||||
color: hsla(162, 47%, 50%, 1);
|
||||
height: 2rem;
|
||||
left: -0.25rem;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
width: 2rem;
|
||||
}
|
||||
#hero .text-container h2 span {
|
||||
margin-left: 2.5rem;
|
||||
}
|
||||
#hero .text-container a {
|
||||
background-color: rgba(255, 255, 255, 1);
|
||||
border-radius: 0.75rem;
|
||||
color: rgba(55, 65, 81, 1);
|
||||
display: inline-block;
|
||||
margin-top: 1.5rem;
|
||||
padding: 1rem 2rem;
|
||||
text-decoration: inherit;
|
||||
}
|
||||
#hero .logo-container {
|
||||
display: none;
|
||||
justify-content: center;
|
||||
padding-left: 2rem;
|
||||
padding-right: 2rem;
|
||||
}
|
||||
#hero .logo-container svg {
|
||||
color: rgba(255, 255, 255, 1);
|
||||
width: 66.666667%;
|
||||
}
|
||||
#middle-content {
|
||||
align-items: flex-start;
|
||||
display: grid;
|
||||
gap: 4rem;
|
||||
grid-template-columns: 1fr;
|
||||
margin-top: 3.5rem;
|
||||
}
|
||||
#learning-materials {
|
||||
padding: 2.5rem 2rem;
|
||||
}
|
||||
#learning-materials h2 {
|
||||
font-weight: 500;
|
||||
font-size: 1.25rem;
|
||||
letter-spacing: -0.025em;
|
||||
line-height: 1.75rem;
|
||||
padding-left: 1rem;
|
||||
padding-right: 1rem;
|
||||
}
|
||||
.list-item-link {
|
||||
align-items: center;
|
||||
border-radius: 0.75rem;
|
||||
display: flex;
|
||||
margin-top: 1rem;
|
||||
padding: 1rem;
|
||||
transition-property: background-color, border-color, color, fill, stroke,
|
||||
opacity, box-shadow, transform, filter, backdrop-filter,
|
||||
-webkit-backdrop-filter;
|
||||
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
|
||||
transition-duration: 150ms;
|
||||
width: 100%;
|
||||
}
|
||||
.list-item-link svg:first-child {
|
||||
margin-right: 1rem;
|
||||
height: 1.5rem;
|
||||
transition-property: background-color, border-color, color, fill, stroke,
|
||||
opacity, box-shadow, transform, filter, backdrop-filter,
|
||||
-webkit-backdrop-filter;
|
||||
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
|
||||
transition-duration: 150ms;
|
||||
width: 1.5rem;
|
||||
}
|
||||
.list-item-link > span {
|
||||
flex-grow: 1;
|
||||
font-weight: 400;
|
||||
transition-property: background-color, border-color, color, fill, stroke,
|
||||
opacity, box-shadow, transform, filter, backdrop-filter,
|
||||
-webkit-backdrop-filter;
|
||||
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
|
||||
transition-duration: 150ms;
|
||||
}
|
||||
.list-item-link > span > span {
|
||||
color: rgba(107, 114, 128, 1);
|
||||
display: block;
|
||||
flex-grow: 1;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 300;
|
||||
line-height: 1rem;
|
||||
transition-property: background-color, border-color, color, fill, stroke,
|
||||
opacity, box-shadow, transform, filter, backdrop-filter,
|
||||
-webkit-backdrop-filter;
|
||||
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
|
||||
transition-duration: 150ms;
|
||||
}
|
||||
.list-item-link svg:last-child {
|
||||
height: 1rem;
|
||||
transition-property: all;
|
||||
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
|
||||
transition-duration: 150ms;
|
||||
width: 1rem;
|
||||
}
|
||||
.list-item-link:hover {
|
||||
color: rgba(255, 255, 255, 1);
|
||||
background-color: hsla(162, 47%, 50%, 1);
|
||||
}
|
||||
.list-item-link:hover > span {
|
||||
}
|
||||
.list-item-link:hover > span > span {
|
||||
color: rgba(243, 244, 246, 1);
|
||||
}
|
||||
.list-item-link:hover svg:last-child {
|
||||
transform: translateX(0.25rem);
|
||||
}
|
||||
#other-links {
|
||||
}
|
||||
.button-pill {
|
||||
padding: 1.5rem 2rem;
|
||||
transition-duration: 300ms;
|
||||
transition-property: background-color, border-color, color, fill, stroke,
|
||||
opacity, box-shadow, transform, filter, backdrop-filter,
|
||||
-webkit-backdrop-filter;
|
||||
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
|
||||
align-items: center;
|
||||
display: flex;
|
||||
}
|
||||
.button-pill svg {
|
||||
transition-property: background-color, border-color, color, fill, stroke,
|
||||
opacity, box-shadow, transform, filter, backdrop-filter,
|
||||
-webkit-backdrop-filter;
|
||||
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
|
||||
transition-duration: 150ms;
|
||||
flex-shrink: 0;
|
||||
width: 3rem;
|
||||
}
|
||||
.button-pill > span {
|
||||
letter-spacing: -0.025em;
|
||||
font-weight: 400;
|
||||
font-size: 1.125rem;
|
||||
line-height: 1.75rem;
|
||||
padding-left: 1rem;
|
||||
padding-right: 1rem;
|
||||
}
|
||||
.button-pill span span {
|
||||
display: block;
|
||||
font-size: 0.875rem;
|
||||
font-weight: 300;
|
||||
line-height: 1.25rem;
|
||||
}
|
||||
.button-pill:hover svg,
|
||||
.button-pill:hover {
|
||||
color: rgba(255, 255, 255, 1) !important;
|
||||
}
|
||||
#nx-console:hover {
|
||||
background-color: rgba(0, 122, 204, 1);
|
||||
}
|
||||
#nx-console svg {
|
||||
color: rgba(0, 122, 204, 1);
|
||||
}
|
||||
#nx-repo:hover {
|
||||
background-color: rgba(24, 23, 23, 1);
|
||||
}
|
||||
#nx-repo svg {
|
||||
color: rgba(24, 23, 23, 1);
|
||||
}
|
||||
#nx-cloud {
|
||||
margin-bottom: 2rem;
|
||||
margin-top: 2rem;
|
||||
padding: 2.5rem 2rem;
|
||||
}
|
||||
#nx-cloud > div {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
}
|
||||
#nx-cloud > div svg {
|
||||
border-radius: 0.375rem;
|
||||
flex-shrink: 0;
|
||||
width: 3rem;
|
||||
}
|
||||
#nx-cloud > div h2 {
|
||||
font-size: 1.125rem;
|
||||
font-weight: 400;
|
||||
letter-spacing: -0.025em;
|
||||
line-height: 1.75rem;
|
||||
padding-left: 1rem;
|
||||
padding-right: 1rem;
|
||||
}
|
||||
#nx-cloud > div h2 span {
|
||||
display: block;
|
||||
font-size: 0.875rem;
|
||||
font-weight: 300;
|
||||
line-height: 1.25rem;
|
||||
}
|
||||
#nx-cloud p {
|
||||
font-size: 1rem;
|
||||
line-height: 1.5rem;
|
||||
margin-top: 1rem;
|
||||
}
|
||||
#nx-cloud pre {
|
||||
margin-top: 1rem;
|
||||
}
|
||||
#nx-cloud a {
|
||||
color: rgba(107, 114, 128, 1);
|
||||
display: block;
|
||||
font-size: 0.875rem;
|
||||
line-height: 1.25rem;
|
||||
margin-top: 1.5rem;
|
||||
text-align: right;
|
||||
}
|
||||
#nx-cloud a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
#commands {
|
||||
padding: 2.5rem 2rem;
|
||||
margin-top: 3.5rem;
|
||||
}
|
||||
#commands h2 {
|
||||
font-size: 1.25rem;
|
||||
font-weight: 400;
|
||||
letter-spacing: -0.025em;
|
||||
line-height: 1.75rem;
|
||||
padding-left: 1rem;
|
||||
padding-right: 1rem;
|
||||
}
|
||||
#commands p {
|
||||
font-size: 1rem;
|
||||
font-weight: 300;
|
||||
line-height: 1.5rem;
|
||||
margin-top: 1rem;
|
||||
padding-left: 1rem;
|
||||
padding-right: 1rem;
|
||||
}
|
||||
details {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
margin-top: 1rem;
|
||||
padding-left: 1rem;
|
||||
padding-right: 1rem;
|
||||
width: 100%;
|
||||
}
|
||||
details pre > span {
|
||||
color: rgba(181, 181, 181, 1);
|
||||
display: block;
|
||||
}
|
||||
summary {
|
||||
border-radius: 0.5rem;
|
||||
display: flex;
|
||||
font-weight: 400;
|
||||
padding: 0.5rem;
|
||||
cursor: pointer;
|
||||
transition-property: background-color, border-color, color, fill, stroke,
|
||||
opacity, box-shadow, transform, filter, backdrop-filter,
|
||||
-webkit-backdrop-filter;
|
||||
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
|
||||
transition-duration: 150ms;
|
||||
}
|
||||
summary:hover {
|
||||
background-color: rgba(243, 244, 246, 1);
|
||||
}
|
||||
summary svg {
|
||||
height: 1.5rem;
|
||||
margin-right: 1rem;
|
||||
width: 1.5rem;
|
||||
}
|
||||
#love {
|
||||
color: rgba(107, 114, 128, 1);
|
||||
font-size: 0.875rem;
|
||||
line-height: 1.25rem;
|
||||
margin-top: 3.5rem;
|
||||
opacity: 0.6;
|
||||
text-align: center;
|
||||
}
|
||||
#love svg {
|
||||
color: rgba(252, 165, 165, 1);
|
||||
width: 1.25rem;
|
||||
height: 1.25rem;
|
||||
display: inline;
|
||||
margin-top: -0.25rem;
|
||||
}
|
||||
@media screen and (min-width: 768px) {
|
||||
#hero {
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
}
|
||||
#hero .logo-container {
|
||||
display: flex;
|
||||
}
|
||||
#middle-content {
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
}
|
||||
}
|
||||
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 |
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 1.0 KiB |