Compare commits
6 Commits
@portaljs/
...
openspendi
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c6f8199cf0 | ||
|
|
4095247ca6 | ||
|
|
f526d3a833 | ||
|
|
a2ac8138c8 | ||
|
|
d6c130e1c6 | ||
|
|
b0b9631c8d |
@@ -1,8 +0,0 @@
|
|||||||
# Changesets
|
|
||||||
|
|
||||||
Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works
|
|
||||||
with multi-package repos, or single-package repos to help you version and publish your code. You can
|
|
||||||
find the full documentation for it [in our repository](https://github.com/changesets/changesets)
|
|
||||||
|
|
||||||
We have a quick list of common questions to get you started engaging with this project in
|
|
||||||
[our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md)
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
{
|
|
||||||
"$schema": "https://unpkg.com/@changesets/config@2.3.0/schema.json",
|
|
||||||
"changelog": [
|
|
||||||
"@changesets/changelog-github",
|
|
||||||
{ "repo": "datopian/portaljs" }
|
|
||||||
],
|
|
||||||
"commit": false,
|
|
||||||
"fixed": [],
|
|
||||||
"linked": [],
|
|
||||||
"access": "restricted",
|
|
||||||
"baseBranch": "main",
|
|
||||||
"updateInternalDependencies": "patch",
|
|
||||||
"ignore": []
|
|
||||||
}
|
|
||||||
39
.github/workflows/release.yml
vendored
39
.github/workflows/release.yml
vendored
@@ -1,39 +0,0 @@
|
|||||||
name: Release
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- main
|
|
||||||
|
|
||||||
concurrency: release-${{ github.ref }}
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
release:
|
|
||||||
name: Release
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Checkout Repo
|
|
||||||
uses: actions/checkout@v3
|
|
||||||
|
|
||||||
- name: Setup Node.js 16.x
|
|
||||||
uses: actions/setup-node@v3
|
|
||||||
with:
|
|
||||||
node-version: 16.x
|
|
||||||
|
|
||||||
- name: Install Dependencies
|
|
||||||
run: npm ci
|
|
||||||
|
|
||||||
- name: Create Release Pull Request or Publish to npm
|
|
||||||
id: changesets
|
|
||||||
uses: changesets/action@v1
|
|
||||||
with:
|
|
||||||
publish: npm run release
|
|
||||||
env:
|
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
|
|
||||||
|
|
||||||
# - name: Send a Discord notification if a publish happens
|
|
||||||
# if: steps.changesets.outputs.published == 'true'
|
|
||||||
# uses: Ilshidur/action-discord@0.3.2
|
|
||||||
# with:
|
|
||||||
# args: 'The project {{ EVENT_PAYLOAD.repository.full_name }} has been deployed.'
|
|
||||||
@@ -26,11 +26,10 @@ If you'd like to work on one of the issues you can:
|
|||||||
3. Clone the forked repository to your machine.
|
3. Clone the forked repository to your machine.
|
||||||
4. Create a feature branch (e.g. `50-update-readme`, where `50` is the number of the related issue).
|
4. Create a feature branch (e.g. `50-update-readme`, where `50` is the number of the related issue).
|
||||||
5. Commit your changes to the feature branch.
|
5. Commit your changes to the feature branch.
|
||||||
6. Add changeset file describing the changes. (See section below)
|
6. Push the feature branch to your forked repository.
|
||||||
7. Push the feature branch to your forked repository.
|
7. Create a Pull Request against the original repository.
|
||||||
8. Create a Pull Request against the original repository.
|
|
||||||
- add a short description of the changes included in the PR
|
- add a short description of the changes included in the PR
|
||||||
9. Address review comments if requested by our demanding reviewers 😜.
|
8. Address review comments if requested by our demanding reviewers 😜.
|
||||||
|
|
||||||
If you have an idea for improvement, and it doesn't have a corresponding issue yet, simply submit a new one.
|
If you have an idea for improvement, and it doesn't have a corresponding issue yet, simply submit a new one.
|
||||||
|
|
||||||
@@ -63,7 +62,6 @@ or you can use just:
|
|||||||
nx <target> <project>
|
nx <target> <project>
|
||||||
# e.g. npx nx serve ckan
|
# e.g. npx nx serve ckan
|
||||||
```
|
```
|
||||||
|
|
||||||
if you have the `nx` binary installed globally in your machine
|
if you have the `nx` binary installed globally in your machine
|
||||||
|
|
||||||
#### Running multiple tasks
|
#### Running multiple tasks
|
||||||
@@ -176,23 +174,3 @@ To learn more see this [offical docs page](https://nx.dev/reference/nx-json).
|
|||||||
Each project also has it's own configuration file - `project.json`, where you can define and configure it's targets (and more).
|
Each project also has it's own configuration file - `project.json`, where you can define and configure it's targets (and more).
|
||||||
|
|
||||||
To learn more see this [offical docs page](https://nx.dev/reference/project-configuration).
|
To learn more see this [offical docs page](https://nx.dev/reference/project-configuration).
|
||||||
|
|
||||||
## Changesets and publishing packages
|
|
||||||
|
|
||||||
> This monorepo is set up with changesets versioning tool. See their [github repository](https://github.com/changesets/changesets) to learn more.
|
|
||||||
|
|
||||||
### What are Changesets?
|
|
||||||
|
|
||||||
Changesets are files that describe the intention of a contributor to bump a version of the package according to their changes. Changeset file holds two key bits of information: a version type (following semver), and change information to be added to a changelog.
|
|
||||||
|
|
||||||
### Adding changesets
|
|
||||||
|
|
||||||
In the root directory of the repo, run:
|
|
||||||
|
|
||||||
```
|
|
||||||
npx changeset
|
|
||||||
```
|
|
||||||
|
|
||||||
Select the package that has been changed, the semver version that should be bumped with it and a description of your changes. Please make sure to add the most accurate but also concise information.
|
|
||||||
|
|
||||||
To learn about semantic versioning standards see [this semver doc page](https://semver.org/).
|
|
||||||
|
|||||||
@@ -1,99 +0,0 @@
|
|||||||
import { useState } from 'react';
|
|
||||||
import { XMarkIcon } from '@heroicons/react/20/solid';
|
|
||||||
import { Transition } from '@headlessui/react';
|
|
||||||
|
|
||||||
export default function Layout({ children }: { children: React.ReactNode }) {
|
|
||||||
const [isShowing, setShow] = useState(true);
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<Transition
|
|
||||||
show={isShowing}
|
|
||||||
enter="transition-opacity duration-75"
|
|
||||||
enterFrom="opacity-0"
|
|
||||||
enterTo="opacity-100"
|
|
||||||
leave="transition-opacity duration-150"
|
|
||||||
leaveFrom="opacity-100"
|
|
||||||
leaveTo="opacity-0"
|
|
||||||
>
|
|
||||||
<div className="flex items-center gap-x-6 bg-[#3c3c3c] px-6 py-2.5 sm:px-3.5 sm:before:flex-1">
|
|
||||||
<p className="text-sm leading-6 text-white">
|
|
||||||
This is a replica to the awesome{' '}
|
|
||||||
<a
|
|
||||||
className="hover:underline font-bold"
|
|
||||||
href="https://data.fivethirtyeight.com"
|
|
||||||
>
|
|
||||||
data.fivethirtyeight.com
|
|
||||||
</a>{' '}
|
|
||||||
website.{' '}
|
|
||||||
<a
|
|
||||||
className="hover:underline font-bold"
|
|
||||||
href="https://github.com/datopian/portaljs/tree/main/examples/fivethirtyeight#readme"
|
|
||||||
>
|
|
||||||
Read more here
|
|
||||||
</a>{' '}
|
|
||||||
</p>
|
|
||||||
<div className="flex flex-1 justify-end">
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
onClick={() => setShow(false)}
|
|
||||||
className="-m-3 p-3 focus-visible:outline-offset-[-4px]"
|
|
||||||
>
|
|
||||||
<span className="sr-only">Dismiss</span>
|
|
||||||
<XMarkIcon className="h-5 w-5 text-white" aria-hidden="true" />
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Transition>
|
|
||||||
<header className="max-w-5xl mx-auto mt-8 w-full">
|
|
||||||
<div className="border-b-2 pb-2.5 mx-2 border-zinc-800 flex justify-between">
|
|
||||||
<h1 className="flex gap-x-1 items-end">
|
|
||||||
<span className="sr-only">FiveThirtyEight</span>
|
|
||||||
<img
|
|
||||||
width="197"
|
|
||||||
height="25"
|
|
||||||
alt="FiveThirtyEight"
|
|
||||||
src="data:image/svg+xml;base64,PHN2ZyBpZD0iTGF5ZXJfMSIgZGF0YS1uYW1lPSJMYXllciAxIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA0MjEgNTMuNzYiPjxkZWZzPjxzdHlsZT4uY2xzLTF7ZmlsbDojMDEwMTAxO308L3N0eWxlPjwvZGVmcz48dGl0bGU+QXJ0Ym9hcmQgOTU8L3RpdGxlPjxwYXRoIGNsYXNzPSJjbHMtMSIgZD0iTTAgMGgyNXY4SDl2MTBoMTV2OEg5djE3SDBWMHpNMzEgMzZoNVYxOGgtNXYtOGgxM3YyNmg0djdIMzF6bTUtMzZoOHY4aC04ek0xNzkgMzZoNVYxOGgtNXYtOGgxM3YyNmg0djdoLTE3em01LTM2aDh2OGgtOHpNMzE2IDM2aDVWMThoLTV2LThoMTN2MjZoNHY3aC0xN3ptNS0zNmg4djhoLTh6TTU0IDI3VjEwaDh2MTVsNCA5Ljk4aDFMNzEgMjVWMTBoOHYxN2wtNyAxNkg2MWwtNy0xNnpNMTExIDQzSDk3LjQyQzg5LjIzIDQzIDg1IDM5LjE5IDg1IDMxLjE3VjIyYzAtNy41NyA0LjMtMTMgMTMtMTMgOS4zMyAwIDEzIDUuMDcgMTMgMTR2N0g5NHYxLjc0YzAgMi42MiAxIDQuMjYgMy40MiA0LjI2SDExMXpNOTQgMjNoOHYtMS41NWMwLTIuNjItMS4wNi01LjQ1LTQuMTMtNS40NS0yLjc5IDAtMy44NyAyLjItMy44NyA1LjQ1ek0xMjUgOGgtMTBWMGgyOXY4aC0xMHYzNWgtOVY4ek0yMDIgNDNWMTBoOHY0YzEuMTQtMi40NSAzLjc1LTQgNy4yMi00SDIyMHY4aC02Yy0yLjg0IDAtNCAuOTQtNCAzLjlWNDN6TTI0NSA0M2gtNC44NEMyMzMuMDUgNDMgMjMwIDM5LjMxIDIzMCAzMS44NVYxOGgtNnYtOGg2VjNoOHY3aDd2OGgtN2wtLjA3IDEzLjkzYzAgMi4yMi45MyA0LjA3IDMuNjYgNC4wN0gyNDV6TTQyMSA0M2gtNC44NEM0MDkuMDUgNDMgNDA2IDM5LjMxIDQwNiAzMS44NVYxOGgtNnYtOGg2VjNoOHY3aDd2OGgtN2wtLjA3IDEzLjkzYzAgMi4yMi45MyA0LjA3IDMuNjYgNC4wN0g0MjF6TTI1NC4yNiA1My43Nmw0LjYxLTkuNUwyNTEgMjdWMTBoOHYxNWw0IDEwaDFsNC0xMFYxMGg4djE3bC0xMi4zIDI2Ljc2aC05LjQ0ek0yODQgMGgyNXY4aC0xNnY5aDE1djhoLTE1djEwaDE2djhoLTI1VjB6TTMzNyA0OHYtMmgxNi4xYzIgMCAyLjktLjE4IDIuOS0xLjI3di0uMzRjMC0xLjA4LS45MS0xLjM5LTIuOS0xLjM5SDM0MHYtNWw1LTVjLTUuMjktMS40OC04LTUuNDMtOC0xMXYtMWMwLTcuNTYgNC40NC0xMiAxNC0xMmEyMS45MyAyMS45MyAwIDAgMSA1Ljk1IDFMMzYxIDRsNSAzLTQgNmMxLjM3IDEuOTMgMyA0LjkzIDMgOHYxYzAgNy0zLjMgMTAuNjYtMTIgMTFsLTMgNGg2YzUuOTIgMCA5IDIuNjIgOSA3LjY4di4xMWMwIDUuMDYtMi43MSA4LjIxLTguNjIgOC4yMWgtMTNjLTQuMjkgMC02LjM4LTEuODQtNi4zOC01em0xOS0yNXYtM2MwLTMuMy0xLjMzLTQtNS00cy01IC43LTUgNHYzYzAgMy4zIDEuMzkgNCA1IDRzNS0uNyA1LTR6TTM4MCA0M2gtOFYwaDh2MTRjMS4xNC0yLjY3IDMuNC00IDctNCA2LjI2IDAgOSAzLjA4IDkgMTAuNzZWNDNoLThWMjJjMC0zLjEzLTEuMDctNS00LTVzLTQgMS44Ny00IDV6TTE1NyA0M2gtOFYwaDh2MTRjMS4xNC0yLjY3IDMuOTEtNCA3LjQ5LTQgNi4yNiAwIDguNTEgMy4xMyA4LjUxIDEwLjgxVjQzaC04VjIxYzAtMy4xMy0xLjA3LTQuNDQtNC00LjQ0cy00IDIuMjYtNCA1LjM5eiIvPjwvc3ZnPg=="
|
|
||||||
/>{' '}
|
|
||||||
<span className="-mb-0.5 text-[#3c3c3c]">replica</span>
|
|
||||||
</h1>
|
|
||||||
<div className="md:flex items-center gap-x-3 text-[#3c3c3c] -mb-1 hidden">
|
|
||||||
<a
|
|
||||||
className="hover:opacity-75 transition"
|
|
||||||
href="https://portaljs.org"
|
|
||||||
>
|
|
||||||
Built with 🌀PortalJS
|
|
||||||
</a>
|
|
||||||
<hr className="h-[80%] border border-[#3c3c3c] opacity-75 my-2"></hr>
|
|
||||||
<a
|
|
||||||
className="hover:opacity-75 transition"
|
|
||||||
href="https://github.com/datopian/portaljs/tree/main/examples/fivethirtyeight"
|
|
||||||
>
|
|
||||||
Github
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="mx-2 py-1.5 text-[14px] text-[#3c3c3c] md:hidden">
|
|
||||||
<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>
|
|
||||||
{children}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,4 +1,33 @@
|
|||||||
[
|
[
|
||||||
|
{
|
||||||
|
"url": "https://github.com/fivethirtyeight/data/tree/master/polls",
|
||||||
|
"name": "polls",
|
||||||
|
"displayName": "<span class=\"lastword\">polls</span>",
|
||||||
|
"articles": [
|
||||||
|
{
|
||||||
|
"date": "2023-05-11T14:35:40.000Z",
|
||||||
|
"title": "Latest Polls",
|
||||||
|
"url": "https://projects.fivethirtyeight.com/polls/"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"files": [
|
||||||
|
"https://projects.fivethirtyeight.com/polls-page/data/president_primary_polls.csv",
|
||||||
|
"https://projects.fivethirtyeight.com/polls-page/data/president_primary_polls_historical.csv",
|
||||||
|
"https://projects.fivethirtyeight.com/polls-page/data/president_polls.csv",
|
||||||
|
"https://projects.fivethirtyeight.com/polls-page/data/president_polls_historical.csv",
|
||||||
|
"https://projects.fivethirtyeight.com/polls-page/data/senate_polls.csv",
|
||||||
|
"https://projects.fivethirtyeight.com/polls-page/data/senate_polls_historical.csv",
|
||||||
|
"https://projects.fivethirtyeight.com/polls-page/data/house_polls.csv",
|
||||||
|
"https://projects.fivethirtyeight.com/polls-page/data/house_polls_historical.csv",
|
||||||
|
"https://projects.fivethirtyeight.com/polls-page/data/governor_polls.csv",
|
||||||
|
"https://projects.fivethirtyeight.com/polls-page/data/governor_polls_historical.csv",
|
||||||
|
"https://projects.fivethirtyeight.com/polls-page/data/president_approval_polls.csv",
|
||||||
|
"https://projects.fivethirtyeight.com/polls-page/data/generic_ballot_polls.csv",
|
||||||
|
"https://projects.fivethirtyeight.com/polls-page/data/generic_ballot_polls_historical.csv",
|
||||||
|
"https://projects.fivethirtyeight.com/2020-primary-data/pres_primary_avgs_2020.csv",
|
||||||
|
"https://projects.fivethirtyeight.com/2020-general-data/presidential_poll_averages_2020.csv"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"url": "https://github.com/fivethirtyeight/data/tree/master/congress-generic-ballot",
|
"url": "https://github.com/fivethirtyeight/data/tree/master/congress-generic-ballot",
|
||||||
"name": "congress-generic-ballot",
|
"name": "congress-generic-ballot",
|
||||||
@@ -166,35 +195,6 @@
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"url": "https://github.com/fivethirtyeight/data/tree/master/polls",
|
|
||||||
"name": "polls",
|
|
||||||
"displayName": "<span class=\"lastword\">polls</span>",
|
|
||||||
"articles": [
|
|
||||||
{
|
|
||||||
"date": "2023-05-11T14:35:40.000Z",
|
|
||||||
"title": "Latest Polls",
|
|
||||||
"url": "https://projects.fivethirtyeight.com/polls/"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"files": [
|
|
||||||
"https://projects.fivethirtyeight.com/polls-page/data/president_primary_polls.csv",
|
|
||||||
"https://projects.fivethirtyeight.com/polls-page/data/president_primary_polls_historical.csv",
|
|
||||||
"https://projects.fivethirtyeight.com/polls-page/data/president_polls.csv",
|
|
||||||
"https://projects.fivethirtyeight.com/polls-page/data/president_polls_historical.csv",
|
|
||||||
"https://projects.fivethirtyeight.com/polls-page/data/senate_polls.csv",
|
|
||||||
"https://projects.fivethirtyeight.com/polls-page/data/senate_polls_historical.csv",
|
|
||||||
"https://projects.fivethirtyeight.com/polls-page/data/house_polls.csv",
|
|
||||||
"https://projects.fivethirtyeight.com/polls-page/data/house_polls_historical.csv",
|
|
||||||
"https://projects.fivethirtyeight.com/polls-page/data/governor_polls.csv",
|
|
||||||
"https://projects.fivethirtyeight.com/polls-page/data/governor_polls_historical.csv",
|
|
||||||
"https://projects.fivethirtyeight.com/polls-page/data/president_approval_polls.csv",
|
|
||||||
"https://projects.fivethirtyeight.com/polls-page/data/generic_ballot_polls.csv",
|
|
||||||
"https://projects.fivethirtyeight.com/polls-page/data/generic_ballot_polls_historical.csv",
|
|
||||||
"https://projects.fivethirtyeight.com/2020-primary-data/pres_primary_avgs_2020.csv",
|
|
||||||
"https://projects.fivethirtyeight.com/2020-general-data/presidential_poll_averages_2020.csv"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"url": "https://github.com/fivethirtyeight/data/tree/master/nfl-elo",
|
"url": "https://github.com/fivethirtyeight/data/tree/master/nfl-elo",
|
||||||
"name": "nfl-elo",
|
"name": "nfl-elo",
|
||||||
@@ -1169,6 +1169,18 @@
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"url": "https://github.com/fivethirtyeight/data/tree/master/undefeated-boxers",
|
||||||
|
"name": "undefeated-boxers",
|
||||||
|
"displayName": "undefeated-<span class=\"lastword\">boxers</span>",
|
||||||
|
"articles": [
|
||||||
|
{
|
||||||
|
"date": "2017-08-18T18:47:32.000Z",
|
||||||
|
"title": "Mayweather Is Defined By The Zero Next To His Name",
|
||||||
|
"url": "https://fivethirtyeight.com/features/mayweather-is-defined-by-the-zero-next-to-his-name/"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"url": "https://github.com/fivethirtyeight/data/tree/master/chess-transfers",
|
"url": "https://github.com/fivethirtyeight/data/tree/master/chess-transfers",
|
||||||
"name": "chess-transfers",
|
"name": "chess-transfers",
|
||||||
@@ -2127,18 +2139,6 @@
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"url": "https://github.com/fivethirtyeight/data/tree/master/undefeated-boxers",
|
|
||||||
"name": "undefeated-boxers",
|
|
||||||
"displayName": "undefeated-<span class=\"lastword\">boxers</span>",
|
|
||||||
"articles": [
|
|
||||||
{
|
|
||||||
"date": "2017-08-18T18:47:32.000Z",
|
|
||||||
"title": "Mayweather Is Defined By The Zero Next To His Name",
|
|
||||||
"url": "https://fivethirtyeight.com/features/mayweather-is-defined-by-the-zero-next-to-his-name/"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"url": "https://github.com/fivethirtyeight/data/tree/master/march-madness-predictions",
|
"url": "https://github.com/fivethirtyeight/data/tree/master/march-madness-predictions",
|
||||||
"name": "march-madness-predictions",
|
"name": "march-madness-predictions",
|
||||||
|
|||||||
7263
examples/fivethirtyeight/package-lock.json
generated
7263
examples/fivethirtyeight/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -9,10 +9,7 @@
|
|||||||
"lint": "next lint"
|
"lint": "next lint"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@flowershow/core": "^0.4.13",
|
"@portaljs/components": "^0.1.0",
|
||||||
"@headlessui/react": "^1.7.14",
|
|
||||||
"@heroicons/react": "^2.0.18",
|
|
||||||
"@portaljs/components": "^0.1.8",
|
|
||||||
"@tailwindcss/typography": "^0.5.9",
|
"@tailwindcss/typography": "^0.5.9",
|
||||||
"@types/node": "20.1.1",
|
"@types/node": "20.1.1",
|
||||||
"@types/react": "18.2.6",
|
"@types/react": "18.2.6",
|
||||||
@@ -29,15 +26,12 @@
|
|||||||
"react": "18.2.0",
|
"react": "18.2.0",
|
||||||
"react-dom": "18.2.0",
|
"react-dom": "18.2.0",
|
||||||
"react-markdown": "^8.0.7",
|
"react-markdown": "^8.0.7",
|
||||||
"remark": "^14.0.3",
|
|
||||||
"remark-code-frontmatter": "^1.0.0",
|
"remark-code-frontmatter": "^1.0.0",
|
||||||
"remark-excerpt": "^1.0.0-beta.1",
|
|
||||||
"remark-extract-frontmatter": "^3.2.0",
|
"remark-extract-frontmatter": "^3.2.0",
|
||||||
"remark-frontmatter": "^4.0.1",
|
"remark-frontmatter": "^4.0.1",
|
||||||
"remark-gfm": "^3.0.1",
|
"remark-gfm": "^3.0.1",
|
||||||
"tailwindcss": "3.3.2",
|
"tailwindcss": "3.3.2",
|
||||||
"timeago.js": "^4.0.2",
|
"timeago.js": "^4.0.2",
|
||||||
"to-vfile": "^7.2.4",
|
|
||||||
"typescript": "5.0.4"
|
"typescript": "5.0.4"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,49 +1,8 @@
|
|||||||
import '@/styles/globals.css';
|
import '@/styles/globals.css'
|
||||||
import '@portaljs/components/styles.css';
|
import '@portaljs/components/styles.css'
|
||||||
import { useEffect } from 'react';
|
|
||||||
import { pageview } from '@flowershow/core';
|
|
||||||
import Script from 'next/script';
|
|
||||||
import Head from 'next/head';
|
|
||||||
import { useRouter } from 'next/router';
|
|
||||||
|
|
||||||
import type { AppProps } from 'next/app';
|
import type { AppProps } from 'next/app'
|
||||||
|
|
||||||
export default function App({ Component, pageProps }: AppProps) {
|
export default function App({ Component, pageProps }: AppProps) {
|
||||||
const router = useRouter();
|
return <Component {...pageProps} />
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const handleRouteChange = (url: any) => {
|
|
||||||
pageview(url);
|
|
||||||
};
|
|
||||||
router.events.on('routeChangeComplete', handleRouteChange);
|
|
||||||
return () => {
|
|
||||||
router.events.off('routeChangeComplete', handleRouteChange);
|
|
||||||
};
|
|
||||||
}, [router.events]);
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<Head>
|
|
||||||
<link rel="shortcut icon" href="/squared_logo.png" />
|
|
||||||
</Head>
|
|
||||||
<Script
|
|
||||||
strategy="afterInteractive"
|
|
||||||
src="https://www.googletagmanager.com/gtag/js?id=G-3N9SXTC7GS"
|
|
||||||
/>
|
|
||||||
<Script
|
|
||||||
id="gtag-init"
|
|
||||||
strategy="afterInteractive"
|
|
||||||
dangerouslySetInnerHTML={{
|
|
||||||
__html: `
|
|
||||||
window.dataLayer = window.dataLayer || [];
|
|
||||||
function gtag(){dataLayer.push(arguments);}
|
|
||||||
gtag('js', new Date());
|
|
||||||
gtag('config', 'G-3N9SXTC7GS', {
|
|
||||||
page_path: window.location.pathname,
|
|
||||||
});
|
|
||||||
`,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<Component {...pageProps} />
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,9 +19,78 @@ export default function Document() {
|
|||||||
/>
|
/>
|
||||||
</Head>
|
</Head>
|
||||||
<body>
|
<body>
|
||||||
|
<div className="px-2 max-w-5xl mx-auto pb-2">
|
||||||
|
<div className="mt-2 px-2 bg-[#3c3c3c] text-white">
|
||||||
|
<div className="p-2 text-center">
|
||||||
|
This is a replica to the awesome{' '}
|
||||||
|
<a
|
||||||
|
className="hover:underline font-bold"
|
||||||
|
href="https://data.fivethirtyeight.com"
|
||||||
|
>
|
||||||
|
data.fivethirtyeight.com
|
||||||
|
</a>{' '}
|
||||||
|
website.{' '}
|
||||||
|
<a
|
||||||
|
className="hover:underline font-bold"
|
||||||
|
href="https://github.com/datopian/portaljs/tree/main/examples/fivethirtyeight"
|
||||||
|
>
|
||||||
|
Read more here
|
||||||
|
</a>{' '}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<header className="max-w-5xl mx-auto mt-8 w-full">
|
||||||
|
<div className="border-b-2 pb-2.5 mx-2 border-zinc-800 flex justify-between">
|
||||||
|
<h1 className="flex gap-x-1 items-end">
|
||||||
|
<span className="sr-only">FiveThirtyEight</span>
|
||||||
|
<img
|
||||||
|
width="197"
|
||||||
|
height="25"
|
||||||
|
alt="FiveThirtyEight"
|
||||||
|
src="data:image/svg+xml;base64,PHN2ZyBpZD0iTGF5ZXJfMSIgZGF0YS1uYW1lPSJMYXllciAxIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA0MjEgNTMuNzYiPjxkZWZzPjxzdHlsZT4uY2xzLTF7ZmlsbDojMDEwMTAxO308L3N0eWxlPjwvZGVmcz48dGl0bGU+QXJ0Ym9hcmQgOTU8L3RpdGxlPjxwYXRoIGNsYXNzPSJjbHMtMSIgZD0iTTAgMGgyNXY4SDl2MTBoMTV2OEg5djE3SDBWMHpNMzEgMzZoNVYxOGgtNXYtOGgxM3YyNmg0djdIMzF6bTUtMzZoOHY4aC04ek0xNzkgMzZoNVYxOGgtNXYtOGgxM3YyNmg0djdoLTE3em01LTM2aDh2OGgtOHpNMzE2IDM2aDVWMThoLTV2LThoMTN2MjZoNHY3aC0xN3ptNS0zNmg4djhoLTh6TTU0IDI3VjEwaDh2MTVsNCA5Ljk4aDFMNzEgMjVWMTBoOHYxN2wtNyAxNkg2MWwtNy0xNnpNMTExIDQzSDk3LjQyQzg5LjIzIDQzIDg1IDM5LjE5IDg1IDMxLjE3VjIyYzAtNy41NyA0LjMtMTMgMTMtMTMgOS4zMyAwIDEzIDUuMDcgMTMgMTR2N0g5NHYxLjc0YzAgMi42MiAxIDQuMjYgMy40MiA0LjI2SDExMXpNOTQgMjNoOHYtMS41NWMwLTIuNjItMS4wNi01LjQ1LTQuMTMtNS40NS0yLjc5IDAtMy44NyAyLjItMy44NyA1LjQ1ek0xMjUgOGgtMTBWMGgyOXY4aC0xMHYzNWgtOVY4ek0yMDIgNDNWMTBoOHY0YzEuMTQtMi40NSAzLjc1LTQgNy4yMi00SDIyMHY4aC02Yy0yLjg0IDAtNCAuOTQtNCAzLjlWNDN6TTI0NSA0M2gtNC44NEMyMzMuMDUgNDMgMjMwIDM5LjMxIDIzMCAzMS44NVYxOGgtNnYtOGg2VjNoOHY3aDd2OGgtN2wtLjA3IDEzLjkzYzAgMi4yMi45MyA0LjA3IDMuNjYgNC4wN0gyNDV6TTQyMSA0M2gtNC44NEM0MDkuMDUgNDMgNDA2IDM5LjMxIDQwNiAzMS44NVYxOGgtNnYtOGg2VjNoOHY3aDd2OGgtN2wtLjA3IDEzLjkzYzAgMi4yMi45MyA0LjA3IDMuNjYgNC4wN0g0MjF6TTI1NC4yNiA1My43Nmw0LjYxLTkuNUwyNTEgMjdWMTBoOHYxNWw0IDEwaDFsNC0xMFYxMGg4djE3bC0xMi4zIDI2Ljc2aC05LjQ0ek0yODQgMGgyNXY4aC0xNnY5aDE1djhoLTE1djEwaDE2djhoLTI1VjB6TTMzNyA0OHYtMmgxNi4xYzIgMCAyLjktLjE4IDIuOS0xLjI3di0uMzRjMC0xLjA4LS45MS0xLjM5LTIuOS0xLjM5SDM0MHYtNWw1LTVjLTUuMjktMS40OC04LTUuNDMtOC0xMXYtMWMwLTcuNTYgNC40NC0xMiAxNC0xMmEyMS45MyAyMS45MyAwIDAgMSA1Ljk1IDFMMzYxIDRsNSAzLTQgNmMxLjM3IDEuOTMgMyA0LjkzIDMgOHYxYzAgNy0zLjMgMTAuNjYtMTIgMTFsLTMgNGg2YzUuOTIgMCA5IDIuNjIgOSA3LjY4di4xMWMwIDUuMDYtMi43MSA4LjIxLTguNjIgOC4yMWgtMTNjLTQuMjkgMC02LjM4LTEuODQtNi4zOC01em0xOS0yNXYtM2MwLTMuMy0xLjMzLTQtNS00cy01IC43LTUgNHYzYzAgMy4zIDEuMzkgNCA1IDRzNS0uNyA1LTR6TTM4MCA0M2gtOFYwaDh2MTRjMS4xNC0yLjY3IDMuNC00IDctNCA2LjI2IDAgOSAzLjA4IDkgMTAuNzZWNDNoLThWMjJjMC0zLjEzLTEuMDctNS00LTVzLTQgMS44Ny00IDV6TTE1NyA0M2gtOFYwaDh2MTRjMS4xNC0yLjY3IDMuOTEtNCA3LjQ5LTQgNi4yNiAwIDguNTEgMy4xMyA4LjUxIDEwLjgxVjQzaC04VjIxYzAtMy4xMy0xLjA3LTQuNDQtNC00LjQ0cy00IDIuMjYtNCA1LjM5eiIvPjwvc3ZnPg=="
|
||||||
|
/>{' '}
|
||||||
|
<span className="-mb-0.5 text-[#3c3c3c]">replica</span>
|
||||||
|
</h1>
|
||||||
|
<div className="md:flex items-center gap-x-3 text-[#3c3c3c] -mb-1 hidden">
|
||||||
|
<a
|
||||||
|
className="hover:opacity-75 transition"
|
||||||
|
href="https://portaljs.org"
|
||||||
|
>
|
||||||
|
Built with 🌀PortalJS
|
||||||
|
</a>
|
||||||
|
<hr className="h-[80%] border border-[#3c3c3c] opacity-75 my-2"></hr>
|
||||||
|
<a
|
||||||
|
className="hover:opacity-75 transition"
|
||||||
|
href="https://github.com/datopian/portaljs/tree/main/examples/fivethirtyeight"
|
||||||
|
>
|
||||||
|
Github
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="mx-2 py-1.5 text-[14px] text-[#3c3c3c] md:hidden">
|
||||||
|
<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 />
|
<Main />
|
||||||
</body>
|
|
||||||
<NextScript />
|
<NextScript />
|
||||||
|
</body>
|
||||||
</Html>
|
</Html>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,19 +7,10 @@ import remarkGfm from 'remark-gfm';
|
|||||||
import extract from 'remark-extract-frontmatter';
|
import extract from 'remark-extract-frontmatter';
|
||||||
import { Dataset } from '..';
|
import { Dataset } from '..';
|
||||||
import { GetStaticProps } from 'next';
|
import { GetStaticProps } from 'next';
|
||||||
import { FlatUiTable } from '@portaljs/components';
|
import { Table } from '@portaljs/components';
|
||||||
import Breadcrumbs from '@/components/Breadcrumbs';
|
import Breadcrumbs from '@/components/Breadcrumbs';
|
||||||
import { ReactMarkdown } from 'react-markdown/lib/react-markdown';
|
import { ReactMarkdown } from 'react-markdown/lib/react-markdown';
|
||||||
import remarkFrontmatter from 'remark-frontmatter';
|
import remarkFrontmatter from 'remark-frontmatter';
|
||||||
import Layout from '@/components/Layout';
|
|
||||||
import { format } from 'timeago.js';
|
|
||||||
|
|
||||||
// Request a weekday along with a long date
|
|
||||||
const options = {
|
|
||||||
year: 'numeric',
|
|
||||||
month: 'long',
|
|
||||||
day: 'numeric',
|
|
||||||
} as const;
|
|
||||||
|
|
||||||
export default function DatasetPage({
|
export default function DatasetPage({
|
||||||
dataset,
|
dataset,
|
||||||
@@ -31,28 +22,54 @@ export default function DatasetPage({
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<NextSeo title={`${dataset.name} page`} />
|
<NextSeo title={`${dataset.name} page`} />
|
||||||
<Layout>
|
<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">
|
||||||
<main className="max-w-5xl px-2 prose mx-auto my-8 pb-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 prose-a:no-underline">
|
|
||||||
<Breadcrumbs links={[{ title: dataset.name, href: '' }]} />
|
<Breadcrumbs links={[{ title: dataset.name, href: '' }]} />
|
||||||
<h1 className="uppercase mb-0 mt-16">{dataset.name}</h1>
|
<h1 className="uppercase mb-0 mt-16">{dataset.name}</h1>
|
||||||
<table className="w-full my-10 mb-8 hidden md:table">
|
<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">
|
<thead className="border-b-4 pb-2 border-zinc-900">
|
||||||
<tr>
|
<tr>
|
||||||
<th className="uppercase text-left font-normal text-xs pb-3">
|
<th
|
||||||
related content
|
className="uppercase text-left font-light text-xs pb-3"
|
||||||
</th>
|
scope="col"
|
||||||
<th className="uppercase text-left font-normal text-xs pb-3">
|
>
|
||||||
last updated
|
Name
|
||||||
</th>
|
</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody className="divide-y divide-gray-200">
|
||||||
<DesktopItem key={dataset.name} dataset={dataset} />
|
{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>
|
</tbody>
|
||||||
</table>
|
</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 && (
|
{dataset.readme && (
|
||||||
<>
|
<>
|
||||||
|
<h2 className="uppercase font-black">Readme</h2>
|
||||||
{dataset.readme && (
|
{dataset.readme && (
|
||||||
<ReactMarkdown
|
<ReactMarkdown
|
||||||
remarkPlugins={[
|
remarkPlugins={[
|
||||||
@@ -66,111 +83,7 @@ export default function DatasetPage({
|
|||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<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>
|
|
||||||
<th
|
|
||||||
className="uppercase text-left font-light text-xs pb-3"
|
|
||||||
scope="col"
|
|
||||||
>
|
|
||||||
Download
|
|
||||||
</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.split('/').slice(-1)}`}>
|
|
||||||
{file.split('/').slice(-1)}
|
|
||||||
</a>
|
|
||||||
</td>
|
|
||||||
<td className="whitespace-nowrap py-4 text-sm text-gray-500">
|
|
||||||
<a href={file}>
|
|
||||||
<svg
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
fill="currentColor"
|
|
||||||
className="w-8 h-8 text-blue-400 hover:text-blue-300 transition mt-1 ml-3"
|
|
||||||
>
|
|
||||||
<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>
|
|
||||||
</a>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
))}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
{dataset.files && dataset.files.length > 0 && (
|
|
||||||
<>
|
|
||||||
<h2 className="mb-0 mt-8">Data Previews</h2>
|
|
||||||
{dataset.files?.map((file) => (
|
|
||||||
<div
|
|
||||||
key={file}
|
|
||||||
id={file.split('/').slice(-1).join('')}
|
|
||||||
className="preview-table my-8"
|
|
||||||
>
|
|
||||||
<h3>{file.split('/').slice(-1)}</h3>
|
|
||||||
<FlatUiTable url={file} />
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</main>
|
</main>
|
||||||
</Layout>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
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>
|
|
||||||
<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 text-end">
|
|
||||||
{index === 0 && (
|
|
||||||
<a
|
|
||||||
className="ml-auto border border-zinc-900 font-light px-[25px] py-2.5 text-sm transition hover:bg-zinc-900 hover:text-white"
|
|
||||||
href={dataset.url}
|
|
||||||
>
|
|
||||||
info
|
|
||||||
</a>
|
|
||||||
)}
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
))}
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -188,7 +101,6 @@ export async function getStaticPaths() {
|
|||||||
fallback: false, // can also be true or 'blocking'
|
fallback: false, // can also be true or 'blocking'
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
// change href base check datahub-next
|
|
||||||
|
|
||||||
export const getStaticProps: GetStaticProps = async ({ params }) => {
|
export const getStaticProps: GetStaticProps = async ({ params }) => {
|
||||||
const datasetsFile = path.join(process.cwd(), 'datasets.json');
|
const datasetsFile = path.join(process.cwd(), 'datasets.json');
|
||||||
@@ -198,20 +110,15 @@ export const getStaticProps: GetStaticProps = async ({ params }) => {
|
|||||||
(_dataset) => _dataset.name === params?.datasetName
|
(_dataset) => _dataset.name === params?.datasetName
|
||||||
);
|
);
|
||||||
const github_pat = getConfig().serverRuntimeConfig.github_pat;
|
const github_pat = getConfig().serverRuntimeConfig.github_pat;
|
||||||
const readmes = await Promise.all(
|
const readmes = await Promise.all(['/README.md', '/readme.md', '/Readme.md'].map(async (readme) => await getProjectReadme(
|
||||||
['/README.md', '/readme.md', '/Readme.md'].map(
|
|
||||||
async (readme) =>
|
|
||||||
await getProjectReadme(
|
|
||||||
'fivethirtyeight',
|
'fivethirtyeight',
|
||||||
'data',
|
'data',
|
||||||
'master',
|
'master',
|
||||||
dataset?.name + readme,
|
dataset?.name + readme,
|
||||||
github_pat
|
github_pat
|
||||||
)
|
)));
|
||||||
)
|
const readme = readmes.find(item => item !== null)
|
||||||
);
|
if (!readme) console.log('Readme not found for ' + dataset?.name)
|
||||||
const readme = readmes.find((item) => item !== null);
|
|
||||||
if (!readme) console.log('Readme not found for ' + dataset?.name);
|
|
||||||
return {
|
return {
|
||||||
props: {
|
props: {
|
||||||
dataset: {
|
dataset: {
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import { format } from 'timeago.js';
|
|||||||
import { promises as fs } from 'fs';
|
import { promises as fs } from 'fs';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import { NextSeo } from 'next-seo';
|
import { NextSeo } from 'next-seo';
|
||||||
import Layout from '@/components/Layout';
|
|
||||||
|
|
||||||
const inter = Inter({ subsets: ['latin'] });
|
const inter = Inter({ subsets: ['latin'] });
|
||||||
|
|
||||||
@@ -52,12 +51,21 @@ export function MobileItem({ dataset }: { dataset: Dataset }) {
|
|||||||
>
|
>
|
||||||
info
|
info
|
||||||
</a>
|
</a>
|
||||||
<a
|
{/*
|
||||||
className="ml-2 border border-[#3c3c3c] px-[25px] py-2.5 text-sm transition bg-[#3c3c3c] text-white hover:bg-zinc-900"
|
<button>
|
||||||
href={`/datasets/${dataset.name}`}
|
<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"
|
||||||
>
|
>
|
||||||
explore
|
<path
|
||||||
</a>
|
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>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@@ -89,16 +97,6 @@ export function DesktopItem({ dataset }: { dataset: Dataset }) {
|
|||||||
? new Date(article.date).toLocaleString('en-US', options)
|
? new Date(article.date).toLocaleString('en-US', options)
|
||||||
: format(article.date)}
|
: format(article.date)}
|
||||||
</td>
|
</td>
|
||||||
<td>
|
|
||||||
{index === 0 && (
|
|
||||||
<a
|
|
||||||
className="ml-2 border border-[#3c3c3c] px-[25px] py-2.5 text-sm transition bg-[#3c3c3c] text-white hover:bg-zinc-900"
|
|
||||||
href={`/datasets/${dataset.name}`}
|
|
||||||
>
|
|
||||||
explore
|
|
||||||
</a>
|
|
||||||
)}
|
|
||||||
</td>
|
|
||||||
<td className="py-8">
|
<td className="py-8">
|
||||||
{index === 0 && (
|
{index === 0 && (
|
||||||
<a
|
<a
|
||||||
@@ -109,6 +107,23 @@ export function DesktopItem({ dataset }: { dataset: Dataset }) {
|
|||||||
</a>
|
</a>
|
||||||
)}
|
)}
|
||||||
</td>
|
</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>
|
</tr>
|
||||||
))}
|
))}
|
||||||
</>
|
</>
|
||||||
@@ -128,7 +143,6 @@ export default function Home({ datasets }: { datasets: Dataset[] }) {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<NextSeo title="FiveThirtyEight tribute by PortalJS" />
|
<NextSeo title="FiveThirtyEight tribute by PortalJS" />
|
||||||
<Layout>
|
|
||||||
<main
|
<main
|
||||||
className={`flex min-h-screen flex-col items-center max-w-5xl mx-auto pt-20 px-2.5 ${inter.className}`}
|
className={`flex min-h-screen flex-col items-center max-w-5xl mx-auto pt-20 px-2.5 ${inter.className}`}
|
||||||
>
|
>
|
||||||
@@ -192,7 +206,6 @@ export default function Home({ datasets }: { datasets: Dataset[] }) {
|
|||||||
.
|
.
|
||||||
</p>
|
</p>
|
||||||
</main>
|
</main>
|
||||||
</Layout>
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,9 +3,6 @@
|
|||||||
@tailwind utilities;
|
@tailwind utilities;
|
||||||
|
|
||||||
.preview-table > div {
|
.preview-table > div {
|
||||||
|
overflow-x: scroll;
|
||||||
overflow-y: hidden;
|
overflow-y: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.prose h1 {
|
|
||||||
font-size: 1.5em !important;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -7,13 +7,13 @@ import { Mermaid } from '@flowershow/core';
|
|||||||
// to handle import statements. Instead, you must include components in scope
|
// to handle import statements. Instead, you must include components in scope
|
||||||
// here.
|
// here.
|
||||||
const components = {
|
const components = {
|
||||||
Table: dynamic(() => import('@portaljs/components').then(mod => mod.Table)),
|
Table: dynamic(() => import('../portaljs/components/Table').then(mod => mod.Table)),
|
||||||
Catalog: dynamic(() => import('@portaljs/components').then(mod => mod.Catalog)),
|
Catalog: dynamic(() => import('../portaljs/components/Catalog').then(mod => mod.Catalog)),
|
||||||
mermaid: Mermaid,
|
mermaid: Mermaid,
|
||||||
Vega: dynamic(() => import('@portaljs/components').then(mod => mod.Vega)),
|
Vega: dynamic(() => import('../portaljs/components/Vega').then(mod => mod.Vega)),
|
||||||
VegaLite: dynamic(() => import('@portaljs/components').then(mod => mod.VegaLite)),
|
VegaLite: dynamic(() => import('../portaljs/components/VegaLite').then(mod => mod.VegaLite)),
|
||||||
LineChart: dynamic(() => import('@portaljs/components').then(mod => mod.LineChart)),
|
LineChart: dynamic(() => import('../portaljs/components/LineChart').then(mod => mod.LineChart)),
|
||||||
FlatUiTable: dynamic(() => import('@portaljs/components').then(mod => mod.FlatUiTable)),
|
FlatUiTable: dynamic(() => import('../portaljs/components/FlatUiTable').then(mod => mod.FlatUiTable)),
|
||||||
} as any;
|
} as any;
|
||||||
|
|
||||||
export default function DRD({ source }: { source: any }) {
|
export default function DRD({ source }: { source: any }) {
|
||||||
|
|||||||
673
examples/learn/package-lock.json
generated
673
examples/learn/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -18,7 +18,7 @@
|
|||||||
"@flowershow/remark-embed": "^1.0.0",
|
"@flowershow/remark-embed": "^1.0.0",
|
||||||
"@flowershow/remark-wiki-link": "^1.1.2",
|
"@flowershow/remark-wiki-link": "^1.1.2",
|
||||||
"@opentelemetry/api": "^1.4.0",
|
"@opentelemetry/api": "^1.4.0",
|
||||||
"@portaljs/components": "^0.1.8",
|
"@portaljs/components": "^0.1.0",
|
||||||
"gray-matter": "^4.0.3",
|
"gray-matter": "^4.0.3",
|
||||||
"hastscript": "^7.2.0",
|
"hastscript": "^7.2.0",
|
||||||
"mdx-mermaid": "2.0.0-rc7",
|
"mdx-mermaid": "2.0.0-rc7",
|
||||||
|
|||||||
@@ -81,7 +81,7 @@ export default function DatasetPage({ mdxSource, frontMatter }) {
|
|||||||
<p className="my-0">
|
<p className="my-0">
|
||||||
<span className="font-semibold">Modified: </span>
|
<span className="font-semibold">Modified: </span>
|
||||||
<span className="description my-0">
|
<span className="description my-0">
|
||||||
{new Date(frontMatter.modified).toLocaleDateString("en-US")}
|
{new Date(frontMatter.modified).toLocaleDateString()}
|
||||||
</span>
|
</span>
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
|
|||||||
119
examples/learn/portaljs/components/Catalog.tsx
Normal file
119
examples/learn/portaljs/components/Catalog.tsx
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
import { Index } from 'flexsearch';
|
||||||
|
import { useState } from 'react';
|
||||||
|
import DebouncedInput from './DebouncedInput';
|
||||||
|
import { useForm } from 'react-hook-form';
|
||||||
|
|
||||||
|
export function Catalog({
|
||||||
|
datasets,
|
||||||
|
facets,
|
||||||
|
}: {
|
||||||
|
datasets: any[];
|
||||||
|
facets: string[];
|
||||||
|
}) {
|
||||||
|
const [indexFilter, setIndexFilter] = useState('');
|
||||||
|
const index = new Index({ tokenize: 'full' });
|
||||||
|
datasets.forEach((dataset) =>
|
||||||
|
index.add(
|
||||||
|
dataset._id,
|
||||||
|
//This will join every metadata value + the url_path into one big string and index that
|
||||||
|
Object.entries(dataset.metadata).reduce(
|
||||||
|
(acc, curr) => acc + ' ' + curr[1].toString(),
|
||||||
|
''
|
||||||
|
) +
|
||||||
|
' ' +
|
||||||
|
dataset.url_path
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
const facetValues = facets
|
||||||
|
? facets.reduce((acc, facet) => {
|
||||||
|
const possibleValues = datasets.reduce((acc, curr) => {
|
||||||
|
const facetValue = curr.metadata[facet];
|
||||||
|
if (facetValue) {
|
||||||
|
return Array.isArray(facetValue)
|
||||||
|
? acc.concat(facetValue)
|
||||||
|
: acc.concat([facetValue]);
|
||||||
|
}
|
||||||
|
return acc;
|
||||||
|
}, []);
|
||||||
|
acc[facet] = {
|
||||||
|
possibleValues: [...new Set(possibleValues)],
|
||||||
|
selectedValue: null,
|
||||||
|
};
|
||||||
|
return acc;
|
||||||
|
}, {})
|
||||||
|
: [];
|
||||||
|
|
||||||
|
const { register, watch } = useForm(facetValues);
|
||||||
|
|
||||||
|
const filteredDatasets = datasets
|
||||||
|
// First filter by flex search
|
||||||
|
.filter((dataset) =>
|
||||||
|
indexFilter !== ''
|
||||||
|
? index.search(indexFilter).includes(dataset._id)
|
||||||
|
: true
|
||||||
|
)
|
||||||
|
//Then check if the selectedValue for the given facet is included in the dataset metadata
|
||||||
|
.filter((dataset) => {
|
||||||
|
//Avoids a server rendering breakage
|
||||||
|
if (!watch() || Object.keys(watch()).length === 0) return true
|
||||||
|
//This will filter only the key pairs of the metadata values that were selected as facets
|
||||||
|
const datasetFacets = Object.entries(dataset.metadata).filter((entry) =>
|
||||||
|
facets.includes(entry[0])
|
||||||
|
);
|
||||||
|
//Check if the value present is included in the selected value in the form
|
||||||
|
return datasetFacets.every((elem) =>
|
||||||
|
watch()[elem[0]].selectedValue
|
||||||
|
? (elem[1] as string | string[]).includes(
|
||||||
|
watch()[elem[0]].selectedValue
|
||||||
|
)
|
||||||
|
: true
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<DebouncedInput
|
||||||
|
value={indexFilter ?? ''}
|
||||||
|
onChange={(value) => setIndexFilter(String(value))}
|
||||||
|
className="p-2 text-sm shadow border border-block mr-1"
|
||||||
|
placeholder="Search all datasets..."
|
||||||
|
/>
|
||||||
|
{Object.entries(facetValues).map((elem) => (
|
||||||
|
<select
|
||||||
|
key={elem[0]}
|
||||||
|
defaultValue=""
|
||||||
|
className="p-2 ml-1 text-sm shadow border border-block"
|
||||||
|
{...register(elem[0] + '.selectedValue')}
|
||||||
|
>
|
||||||
|
<option value="">
|
||||||
|
Filter by {elem[0]}
|
||||||
|
</option>
|
||||||
|
{(elem[1] as { possibleValues: string[] }).possibleValues.map(
|
||||||
|
(val) => (
|
||||||
|
<option
|
||||||
|
key={val}
|
||||||
|
className="dark:bg-white dark:text-black"
|
||||||
|
value={val}
|
||||||
|
>
|
||||||
|
{val}
|
||||||
|
</option>
|
||||||
|
)
|
||||||
|
)}
|
||||||
|
</select>
|
||||||
|
))}
|
||||||
|
<ul className='mb-5 pl-6 mt-5 list-disc'>
|
||||||
|
{filteredDatasets.map((dataset) => (
|
||||||
|
<li className='py-2' key={dataset._id}>
|
||||||
|
<a className='font-medium underline' href={dataset.url_path}>
|
||||||
|
{dataset.metadata.title
|
||||||
|
? dataset.metadata.title
|
||||||
|
: dataset.url_path}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
32
examples/learn/portaljs/components/DebouncedInput.tsx
Normal file
32
examples/learn/portaljs/components/DebouncedInput.tsx
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
import { useEffect, useState } from "react";
|
||||||
|
|
||||||
|
const DebouncedInput = ({
|
||||||
|
value: initialValue,
|
||||||
|
onChange,
|
||||||
|
debounce = 500,
|
||||||
|
...props
|
||||||
|
}) => {
|
||||||
|
const [value, setValue] = useState(initialValue);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setValue(initialValue);
|
||||||
|
}, [initialValue]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const timeout = setTimeout(() => {
|
||||||
|
onChange(value);
|
||||||
|
}, debounce);
|
||||||
|
|
||||||
|
return () => clearTimeout(timeout);
|
||||||
|
}, [value]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<input
|
||||||
|
{...props}
|
||||||
|
value={value}
|
||||||
|
onChange={(e) => setValue(e.target.value)}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default DebouncedInput;
|
||||||
113
examples/learn/portaljs/components/FlatUiTable.tsx
Normal file
113
examples/learn/portaljs/components/FlatUiTable.tsx
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
import { QueryClient, QueryClientProvider, useQuery } from 'react-query';
|
||||||
|
import Papa from 'papaparse';
|
||||||
|
import { Grid } from '@githubocto/flat-ui';
|
||||||
|
|
||||||
|
const queryClient = new QueryClient();
|
||||||
|
|
||||||
|
export async function getCsv(url: string, corsProxy?: string) {
|
||||||
|
if (corsProxy) {
|
||||||
|
url = corsProxy + url
|
||||||
|
}
|
||||||
|
const response = await fetch(url, {
|
||||||
|
headers: {
|
||||||
|
Range: 'bytes=0-5132288',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const data = await response.text();
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function parseCsv(file: string): Promise<any> {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
Papa.parse(file, {
|
||||||
|
header: true,
|
||||||
|
dynamicTyping: true,
|
||||||
|
skipEmptyLines: true,
|
||||||
|
transform: (value: string): string => {
|
||||||
|
return value.trim();
|
||||||
|
},
|
||||||
|
complete: (results: any) => {
|
||||||
|
return resolve(results);
|
||||||
|
},
|
||||||
|
error: (error: any) => {
|
||||||
|
return reject(error);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const Spinning = () => {
|
||||||
|
return (
|
||||||
|
<div role="status w-fit mx-auto">
|
||||||
|
<svg
|
||||||
|
aria-hidden="true"
|
||||||
|
className="w-8 h-8 mr-2 text-gray-200 animate-spin dark:text-gray-600 fill-emerald-600"
|
||||||
|
viewBox="0 0 100 101"
|
||||||
|
fill="none"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M100 50.5908C100 78.2051 77.6142 100.591 50 100.591C22.3858 100.591 0 78.2051 0 50.5908C0 22.9766 22.3858 0.59082 50 0.59082C77.6142 0.59082 100 22.9766 100 50.5908ZM9.08144 50.5908C9.08144 73.1895 27.4013 91.5094 50 91.5094C72.5987 91.5094 90.9186 73.1895 90.9186 50.5908C90.9186 27.9921 72.5987 9.67226 50 9.67226C27.4013 9.67226 9.08144 27.9921 9.08144 50.5908Z"
|
||||||
|
fill="currentColor"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M93.9676 39.0409C96.393 38.4038 97.8624 35.9116 97.0079 33.5539C95.2932 28.8227 92.871 24.3692 89.8167 20.348C85.8452 15.1192 80.8826 10.7238 75.2124 7.41289C69.5422 4.10194 63.2754 1.94025 56.7698 1.05124C51.7666 0.367541 46.6976 0.446843 41.7345 1.27873C39.2613 1.69328 37.813 4.19778 38.4501 6.62326C39.0873 9.04874 41.5694 10.4717 44.0505 10.1071C47.8511 9.54855 51.7191 9.52689 55.5402 10.0491C60.8642 10.7766 65.9928 12.5457 70.6331 15.2552C75.2735 17.9648 79.3347 21.5619 82.5849 25.841C84.9175 28.9121 86.7997 32.2913 88.1811 35.8758C89.083 38.2158 91.5421 39.6781 93.9676 39.0409Z"
|
||||||
|
fill="currentFill"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
<span className="sr-only">Loading...</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export interface FlatUiTableProps {
|
||||||
|
url?: string;
|
||||||
|
data?: { [key: string]: number | string }[];
|
||||||
|
rawCsv?: string;
|
||||||
|
corsProxy?: string;
|
||||||
|
}
|
||||||
|
export const FlatUiTable: React.FC<FlatUiTableProps> = ({
|
||||||
|
url,
|
||||||
|
data,
|
||||||
|
rawCsv,
|
||||||
|
corsProxy,
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
// Provide the client to your App
|
||||||
|
<QueryClientProvider client={queryClient}>
|
||||||
|
<TableInner corsProxy={corsProxy} url={url} data={data} rawCsv={rawCsv} />
|
||||||
|
</QueryClientProvider>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const TableInner: React.FC<FlatUiTableProps> = ({ url, data, rawCsv, corsProxy }) => {
|
||||||
|
if (data) {
|
||||||
|
return (
|
||||||
|
<div className="w-full" style={{height: '500px'}}>
|
||||||
|
<Grid data={data} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const { data: csvString, isLoading: isDownloadingCSV } = useQuery(
|
||||||
|
['dataCsv', url],
|
||||||
|
() => getCsv(url as string, corsProxy),
|
||||||
|
{ enabled: !!url }
|
||||||
|
);
|
||||||
|
const { data: parsedData, isLoading: isParsing } = useQuery(
|
||||||
|
['dataPreview', csvString],
|
||||||
|
() => parseCsv(rawCsv ? rawCsv as string : csvString as string),
|
||||||
|
{ enabled: rawCsv ? true : !!csvString }
|
||||||
|
);
|
||||||
|
if (isParsing || isDownloadingCSV)
|
||||||
|
<div className="w-full">
|
||||||
|
<Spinning />
|
||||||
|
</div>;
|
||||||
|
if (parsedData)
|
||||||
|
return (
|
||||||
|
<div className="w-full" style={{height: '500px'}}>
|
||||||
|
<Grid data={parsedData.data} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
return <Spinning />
|
||||||
|
};
|
||||||
|
|
||||||
63
examples/learn/portaljs/components/LineChart.tsx
Normal file
63
examples/learn/portaljs/components/LineChart.tsx
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
import { VegaLite } from './VegaLite';
|
||||||
|
|
||||||
|
export type LineChartProps = {
|
||||||
|
data: Array<Array<string | number>> | string | { x: string; y: number }[];
|
||||||
|
title?: string;
|
||||||
|
xAxis?: string;
|
||||||
|
yAxis?: string;
|
||||||
|
fullWidth?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function LineChart({
|
||||||
|
data = [],
|
||||||
|
fullWidth = false,
|
||||||
|
title = '',
|
||||||
|
xAxis = 'x',
|
||||||
|
yAxis = 'y',
|
||||||
|
}: LineChartProps) {
|
||||||
|
var tmp = data;
|
||||||
|
if (Array.isArray(data)) {
|
||||||
|
tmp = data.map((r) => {
|
||||||
|
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: 600,
|
||||||
|
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} />;
|
||||||
|
}
|
||||||
195
examples/learn/portaljs/components/Table.tsx
Normal file
195
examples/learn/portaljs/components/Table.tsx
Normal file
@@ -0,0 +1,195 @@
|
|||||||
|
import {
|
||||||
|
createColumnHelper,
|
||||||
|
FilterFn,
|
||||||
|
flexRender,
|
||||||
|
getCoreRowModel,
|
||||||
|
getFilteredRowModel,
|
||||||
|
getPaginationRowModel,
|
||||||
|
getSortedRowModel,
|
||||||
|
useReactTable,
|
||||||
|
} from '@tanstack/react-table';
|
||||||
|
|
||||||
|
import {
|
||||||
|
ArrowDownIcon,
|
||||||
|
ArrowUpIcon,
|
||||||
|
ChevronDoubleLeftIcon,
|
||||||
|
ChevronDoubleRightIcon,
|
||||||
|
ChevronLeftIcon,
|
||||||
|
ChevronRightIcon,
|
||||||
|
} from '@heroicons/react/24/solid';
|
||||||
|
|
||||||
|
import React, { useEffect, useMemo, useState } from 'react';
|
||||||
|
|
||||||
|
import parseCsv from '../lib/parseCsv';
|
||||||
|
import DebouncedInput from './DebouncedInput';
|
||||||
|
import loadData from '../lib/loadData';
|
||||||
|
|
||||||
|
export type TableProps = {
|
||||||
|
data?: Array<{ [key: string]: number | string }>;
|
||||||
|
cols?: Array<{ [key: string]: string }>;
|
||||||
|
csv?: string;
|
||||||
|
url?: string;
|
||||||
|
fullWidth?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Table = ({
|
||||||
|
data: ogData = [],
|
||||||
|
cols: ogCols = [],
|
||||||
|
csv = '',
|
||||||
|
url = '',
|
||||||
|
fullWidth = false,
|
||||||
|
}: TableProps) => {
|
||||||
|
if (csv) {
|
||||||
|
const out = parseCsv(csv);
|
||||||
|
ogData = out.rows;
|
||||||
|
ogCols = out.fields;
|
||||||
|
}
|
||||||
|
|
||||||
|
const [data, setData] = React.useState(ogData);
|
||||||
|
const [cols, setCols] = React.useState(ogCols);
|
||||||
|
// const [error, setError] = React.useState(""); // TODO: add error handling
|
||||||
|
|
||||||
|
const tableCols = useMemo(() => {
|
||||||
|
const columnHelper = createColumnHelper();
|
||||||
|
return cols.map((c) =>
|
||||||
|
columnHelper.accessor<any, string>(c.key, {
|
||||||
|
header: () => c.name,
|
||||||
|
cell: (info) => info.getValue(),
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}, [data, cols]);
|
||||||
|
|
||||||
|
const [globalFilter, setGlobalFilter] = useState('');
|
||||||
|
|
||||||
|
const table = useReactTable({
|
||||||
|
data,
|
||||||
|
columns: tableCols,
|
||||||
|
getCoreRowModel: getCoreRowModel(),
|
||||||
|
state: {
|
||||||
|
globalFilter,
|
||||||
|
},
|
||||||
|
globalFilterFn: globalFilterFn,
|
||||||
|
onGlobalFilterChange: setGlobalFilter,
|
||||||
|
getFilteredRowModel: getFilteredRowModel(),
|
||||||
|
getPaginationRowModel: getPaginationRowModel(),
|
||||||
|
getSortedRowModel: getSortedRowModel(),
|
||||||
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (url) {
|
||||||
|
loadData(url).then((data) => {
|
||||||
|
const { rows, fields } = parseCsv(data);
|
||||||
|
setData(rows);
|
||||||
|
setCols(fields);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, [url]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={`${fullWidth ? 'w-[90vw] ml-[calc(50%-45vw)]' : 'w-full'}`}>
|
||||||
|
<DebouncedInput
|
||||||
|
value={globalFilter ?? ''}
|
||||||
|
onChange={(value: any) => setGlobalFilter(String(value))}
|
||||||
|
className="p-2 text-sm shadow border border-block"
|
||||||
|
placeholder="Search all columns..."
|
||||||
|
/>
|
||||||
|
<table className="w-full mt-10">
|
||||||
|
<thead className="text-left border-b border-b-slate-300">
|
||||||
|
{table.getHeaderGroups().map((hg) => (
|
||||||
|
<tr key={hg.id}>
|
||||||
|
{hg.headers.map((h) => (
|
||||||
|
<th key={h.id} className="pr-2 pb-2">
|
||||||
|
<div
|
||||||
|
{...{
|
||||||
|
className: h.column.getCanSort()
|
||||||
|
? 'cursor-pointer select-none'
|
||||||
|
: '',
|
||||||
|
onClick: h.column.getToggleSortingHandler(),
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{flexRender(h.column.columnDef.header, h.getContext())}
|
||||||
|
{{
|
||||||
|
asc: (
|
||||||
|
<ArrowUpIcon className="inline-block ml-2 h-4 w-4" />
|
||||||
|
),
|
||||||
|
desc: (
|
||||||
|
<ArrowDownIcon className="inline-block ml-2 h-4 w-4" />
|
||||||
|
),
|
||||||
|
}[h.column.getIsSorted() as string] ?? (
|
||||||
|
<div className="inline-block ml-2 h-4 w-4" />
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</th>
|
||||||
|
))}
|
||||||
|
</tr>
|
||||||
|
))}
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{table.getRowModel().rows.map((r) => (
|
||||||
|
<tr key={r.id} className="border-b border-b-slate-200">
|
||||||
|
{r.getVisibleCells().map((c) => (
|
||||||
|
<td key={c.id} className="py-2">
|
||||||
|
{flexRender(c.column.columnDef.cell, c.getContext())}
|
||||||
|
</td>
|
||||||
|
))}
|
||||||
|
</tr>
|
||||||
|
))}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<div className="flex gap-2 items-center justify-center mt-10">
|
||||||
|
<button
|
||||||
|
className={`w-6 h-6 ${
|
||||||
|
!table.getCanPreviousPage() ? 'opacity-25' : 'opacity-100'
|
||||||
|
}`}
|
||||||
|
onClick={() => table.setPageIndex(0)}
|
||||||
|
disabled={!table.getCanPreviousPage()}
|
||||||
|
>
|
||||||
|
<ChevronDoubleLeftIcon />
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
className={`w-6 h-6 ${
|
||||||
|
!table.getCanPreviousPage() ? 'opacity-25' : 'opacity-100'
|
||||||
|
}`}
|
||||||
|
onClick={() => table.previousPage()}
|
||||||
|
disabled={!table.getCanPreviousPage()}
|
||||||
|
>
|
||||||
|
<ChevronLeftIcon />
|
||||||
|
</button>
|
||||||
|
<span className="flex items-center gap-1">
|
||||||
|
<div>Page</div>
|
||||||
|
<strong>
|
||||||
|
{table.getState().pagination.pageIndex + 1} of{' '}
|
||||||
|
{table.getPageCount()}
|
||||||
|
</strong>
|
||||||
|
</span>
|
||||||
|
<button
|
||||||
|
className={`w-6 h-6 ${
|
||||||
|
!table.getCanNextPage() ? 'opacity-25' : 'opacity-100'
|
||||||
|
}`}
|
||||||
|
onClick={() => table.nextPage()}
|
||||||
|
disabled={!table.getCanNextPage()}
|
||||||
|
>
|
||||||
|
<ChevronRightIcon />
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
className={`w-6 h-6 ${
|
||||||
|
!table.getCanNextPage() ? 'opacity-25' : 'opacity-100'
|
||||||
|
}`}
|
||||||
|
onClick={() => table.setPageIndex(table.getPageCount() - 1)}
|
||||||
|
disabled={!table.getCanNextPage()}
|
||||||
|
>
|
||||||
|
<ChevronDoubleRightIcon />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const globalFilterFn: FilterFn<any> = (row, columnId, filterValue: string) => {
|
||||||
|
const search = filterValue.toLowerCase();
|
||||||
|
|
||||||
|
let value = row.getValue(columnId) as string;
|
||||||
|
if (typeof value === 'number') value = String(value);
|
||||||
|
|
||||||
|
return value?.toLowerCase().includes(search);
|
||||||
|
};
|
||||||
6
examples/learn/portaljs/components/Vega.tsx
Normal file
6
examples/learn/portaljs/components/Vega.tsx
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
// Wrapper for the Vega component
|
||||||
|
import { Vega as VegaOg } from "react-vega";
|
||||||
|
|
||||||
|
export function Vega(props) {
|
||||||
|
return <VegaOg {...props} />;
|
||||||
|
}
|
||||||
9
examples/learn/portaljs/components/VegaLite.tsx
Normal file
9
examples/learn/portaljs/components/VegaLite.tsx
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
// Wrapper for the Vega Lite component
|
||||||
|
import { VegaLite as VegaLiteOg } from "react-vega";
|
||||||
|
import applyFullWidthDirective from "../lib/applyFullWidthDirective";
|
||||||
|
|
||||||
|
export function VegaLite(props) {
|
||||||
|
const Component = applyFullWidthDirective({ Component: VegaLiteOg });
|
||||||
|
|
||||||
|
return <Component {...props} />;
|
||||||
|
}
|
||||||
21
examples/learn/portaljs/lib/applyFullWidthDirective.tsx
Normal file
21
examples/learn/portaljs/lib/applyFullWidthDirective.tsx
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
export default function applyFullWidthDirective({
|
||||||
|
Component,
|
||||||
|
defaultWFull = true,
|
||||||
|
}) {
|
||||||
|
return (props) => {
|
||||||
|
const newProps = { ...props };
|
||||||
|
|
||||||
|
let newClassName = newProps.className || "";
|
||||||
|
if (newProps.fullWidth === true) {
|
||||||
|
newClassName += " w-[90vw] ml-[calc(50%-45vw)] max-w-none";
|
||||||
|
} else if (defaultWFull) {
|
||||||
|
// So that charts and tables will have the
|
||||||
|
// same width as the text content, but images
|
||||||
|
// can have its width set using the width prop
|
||||||
|
newClassName += " w-full";
|
||||||
|
}
|
||||||
|
newProps.className = newClassName;
|
||||||
|
|
||||||
|
return <Component {...newProps} />;
|
||||||
|
};
|
||||||
|
}
|
||||||
5
examples/learn/portaljs/lib/loadData.tsx
Normal file
5
examples/learn/portaljs/lib/loadData.tsx
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
export default async function loadData(url: string) {
|
||||||
|
const response = await fetch(url)
|
||||||
|
const data = await response.text()
|
||||||
|
return data
|
||||||
|
}
|
||||||
20
examples/learn/portaljs/lib/parseCsv.ts
Normal file
20
examples/learn/portaljs/lib/parseCsv.ts
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
import papa from "papaparse";
|
||||||
|
|
||||||
|
const parseCsv = (csv: string) => {
|
||||||
|
csv = csv.trim();
|
||||||
|
const rawdata = papa.parse(csv, { header: true });
|
||||||
|
|
||||||
|
let cols: any[] = [];
|
||||||
|
if(rawdata.meta.fields) {
|
||||||
|
cols = rawdata.meta.fields.map((r: string) => {
|
||||||
|
return { key: r, name: r };
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
rows: rawdata.data as any,
|
||||||
|
fields: cols,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default parseCsv;
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
import { MDXRemote } from 'next-mdx-remote';
|
|
||||||
import dynamic from 'next/dynamic';
|
|
||||||
import { Mermaid } from '@flowershow/core';
|
|
||||||
|
|
||||||
// Custom components/renderers to pass to MDX.
|
|
||||||
// Since the MDX files aren't loaded by webpack, they have no knowledge of how
|
|
||||||
// to handle import statements. Instead, you must include components in scope
|
|
||||||
// here.
|
|
||||||
const components = {
|
|
||||||
Table: dynamic(() => import('@portaljs/components').then(mod => mod.Table)),
|
|
||||||
Catalog: dynamic(() => import('@portaljs/components').then(mod => mod.Catalog)),
|
|
||||||
mermaid: Mermaid,
|
|
||||||
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)),
|
|
||||||
FlatUiTable: dynamic(() => import('@portaljs/components').then(mod => mod.FlatUiTable)),
|
|
||||||
} as any;
|
|
||||||
|
|
||||||
export default function DRD({ source }: { source: any }) {
|
|
||||||
return <MDXRemote {...source} components={components} />;
|
|
||||||
}
|
|
||||||
@@ -45,12 +45,12 @@ export default function DatasetCard({ dataset }: { dataset: Project }) {
|
|||||||
<dt className="text-gray-500">Fiscal Period</dt>
|
<dt className="text-gray-500">Fiscal Period</dt>
|
||||||
<dd className="text-gray-700">
|
<dd className="text-gray-700">
|
||||||
{dataset.fiscalPeriod?.start &&
|
{dataset.fiscalPeriod?.start &&
|
||||||
new Date(dataset.fiscalPeriod.start).getUTCFullYear()}
|
new Date(dataset.fiscalPeriod.start).getFullYear()}
|
||||||
{dataset.fiscalPeriod?.end &&
|
{dataset.fiscalPeriod?.end &&
|
||||||
dataset.fiscalPeriod?.start !== dataset.fiscalPeriod?.end && (
|
dataset.fiscalPeriod?.start !== dataset.fiscalPeriod?.end && (
|
||||||
<>
|
<>
|
||||||
{' - '}
|
{' - '}
|
||||||
{new Date(dataset.fiscalPeriod.end).getUTCFullYear()}
|
{new Date(dataset.fiscalPeriod.end).getFullYear()}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</dd>
|
</dd>
|
||||||
|
|||||||
@@ -4,13 +4,10 @@ import { Grid } from '@githubocto/flat-ui';
|
|||||||
|
|
||||||
const queryClient = new QueryClient();
|
const queryClient = new QueryClient();
|
||||||
|
|
||||||
export async function getCsv(url: string, corsProxy?: string, range?: string) {
|
export async function getCsv(url: string) {
|
||||||
if (corsProxy) {
|
|
||||||
url = corsProxy + url
|
|
||||||
}
|
|
||||||
const response = await fetch(url, {
|
const response = await fetch(url, {
|
||||||
headers: {
|
headers: {
|
||||||
Range: range ? `bytes=0-${range}` : 'bytes=0-512000',
|
Range: 'bytes=0-5132288',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
const data = await response.text();
|
const data = await response.text();
|
||||||
@@ -64,25 +61,21 @@ export interface FlatUiTableProps {
|
|||||||
url?: string;
|
url?: string;
|
||||||
data?: { [key: string]: number | string }[];
|
data?: { [key: string]: number | string }[];
|
||||||
rawCsv?: string;
|
rawCsv?: string;
|
||||||
range?: string;
|
|
||||||
corsProxy?: string;
|
|
||||||
}
|
}
|
||||||
export const FlatUiTable: React.FC<FlatUiTableProps> = ({
|
export const FlatUiTable: React.FC<FlatUiTableProps> = ({
|
||||||
url,
|
url,
|
||||||
data,
|
data,
|
||||||
rawCsv,
|
rawCsv,
|
||||||
corsProxy,
|
|
||||||
range
|
|
||||||
}) => {
|
}) => {
|
||||||
return (
|
return (
|
||||||
// Provide the client to your App
|
// Provide the client to your App
|
||||||
<QueryClientProvider client={queryClient}>
|
<QueryClientProvider client={queryClient}>
|
||||||
<TableInner range={range} corsProxy={corsProxy} url={url} data={data} rawCsv={rawCsv} />
|
<TableInner url={url} data={data} rawCsv={rawCsv} />
|
||||||
</QueryClientProvider>
|
</QueryClientProvider>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const TableInner: React.FC<FlatUiTableProps> = ({ url, data, rawCsv, corsProxy, range }) => {
|
const TableInner: React.FC<FlatUiTableProps> = ({ url, data, rawCsv }) => {
|
||||||
if (data) {
|
if (data) {
|
||||||
return (
|
return (
|
||||||
<div className="w-full" style={{height: '500px'}}>
|
<div className="w-full" style={{height: '500px'}}>
|
||||||
@@ -92,12 +85,12 @@ const TableInner: React.FC<FlatUiTableProps> = ({ url, data, rawCsv, corsProxy,
|
|||||||
}
|
}
|
||||||
const { data: csvString, isLoading: isDownloadingCSV } = useQuery(
|
const { data: csvString, isLoading: isDownloadingCSV } = useQuery(
|
||||||
['dataCsv', url],
|
['dataCsv', url],
|
||||||
() => getCsv(url as string, corsProxy, range),
|
() => getCsv(url),
|
||||||
{ enabled: !!url }
|
{ enabled: !!url }
|
||||||
);
|
);
|
||||||
const { data: parsedData, isLoading: isParsing } = useQuery(
|
const { data: parsedData, isLoading: isParsing } = useQuery(
|
||||||
['dataPreview', csvString],
|
['dataPreview', csvString],
|
||||||
() => parseCsv(rawCsv ? rawCsv as string : csvString as string),
|
() => parseCsv(rawCsv ? rawCsv : csvString),
|
||||||
{ enabled: rawCsv ? true : !!csvString }
|
{ enabled: rawCsv ? true : !!csvString }
|
||||||
);
|
);
|
||||||
if (isParsing || isDownloadingCSV)
|
if (isParsing || isDownloadingCSV)
|
||||||
@@ -110,6 +103,4 @@ const TableInner: React.FC<FlatUiTableProps> = ({ url, data, rawCsv, corsProxy,
|
|||||||
<Grid data={parsedData.data} />
|
<Grid data={parsedData.data} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
return <Spinning />
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -23,10 +23,6 @@ export function Header() {
|
|||||||
title: 'Datasets',
|
title: 'Datasets',
|
||||||
href: '/#datasets',
|
href: '/#datasets',
|
||||||
},
|
},
|
||||||
{
|
|
||||||
title: 'Data Stories',
|
|
||||||
href: '/stories',
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
title: 'Blog',
|
title: 'Blog',
|
||||||
href: '/blog',
|
href: '/blog',
|
||||||
@@ -161,7 +157,7 @@ function Dropdown({ navItem }: { navItem: any }) {
|
|||||||
>
|
>
|
||||||
<div className="py-1">
|
<div className="py-1">
|
||||||
{navItem.children.map((item) => (
|
{navItem.children.map((item) => (
|
||||||
<Menu.Item key={item.href}>
|
<Menu.Item>
|
||||||
{({ active }) => (
|
{({ active }) => (
|
||||||
<a
|
<a
|
||||||
key={item.href}
|
key={item.href}
|
||||||
|
|||||||
@@ -2,23 +2,23 @@ import Image from 'next/image';
|
|||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
export default function Footer() {
|
export default function Footer() {
|
||||||
return (
|
return (
|
||||||
<footer className="bg-slate-50 border-t border-slate-100 py-10">
|
<footer>
|
||||||
<div className="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8 flex flex-col items-center justify-between md:flex-row">
|
<div className="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8 flex flex-col items-center justify-between md:flex-row">
|
||||||
<div className="flex gap-x-2 items-center justify-center mx-auto">
|
<div className="flex gap-x-2 items-center">
|
||||||
<p className="text-base text-slate-500">Maintained by</p>
|
<p className="mt-8 text-base text-slate-500 md:mt-0">Maintained by</p>
|
||||||
<a href="https://www.datopian.com/" target="_blank">
|
<a href="https://www.datopian.com/">
|
||||||
<Image
|
<Image
|
||||||
alt="Datopian logo"
|
alt="Datopian logo"
|
||||||
className="mb-1"
|
className="mb-2"
|
||||||
src="/datopian-logotype.png"
|
src="/datopian-logotype.png"
|
||||||
width={120}
|
width={160}
|
||||||
height={30}
|
height={40}
|
||||||
/>
|
/>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex gap-x-2 items-center mx-auto h-20">
|
<p className="mt-6 text-base text-slate-500 md:mt-0">
|
||||||
<p className="mt-8 text-base text-slate-500 md:mt-0">Built with <a href="https://portaljs.org" target="_blank" className='text-xl font-medium'>🌀 PortalJS</a></p>
|
Copyright © 2023 Datopian, LLC. All rights reserved.
|
||||||
</div>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import Footer from './Footer';
|
|||||||
|
|
||||||
export default function Layout({ children }) {
|
export default function Layout({ children }) {
|
||||||
return (
|
return (
|
||||||
<div className="bg-white min-h-screen">
|
<div className="bg-white min-h-screen pb-12">
|
||||||
<Header />
|
<Header />
|
||||||
{children}
|
{children}
|
||||||
<Footer />
|
<Footer />
|
||||||
|
|||||||
@@ -1,34 +0,0 @@
|
|||||||
---
|
|
||||||
title: Sample Data Story
|
|
||||||
date: 06/06/2023
|
|
||||||
---
|
|
||||||
|
|
||||||
This is a sample data story, you can add charts
|
|
||||||
|
|
||||||
<LineChart
|
|
||||||
data="https://raw.githubusercontent.com/datasets/oil-prices/main/data/wti-year.csv"
|
|
||||||
title="Oil Price x Year"
|
|
||||||
xAxis="Date"
|
|
||||||
yAxis="Price"
|
|
||||||
/>
|
|
||||||
|
|
||||||
Or you can add previews
|
|
||||||
|
|
||||||
<FlatUiTable url="https://raw.githubusercontent.com/datasets/oil-prices/main/data/wti-year.csv" />
|
|
||||||
|
|
||||||
And you can of course add markdown
|
|
||||||
|
|
||||||
## Subtitles
|
|
||||||
|
|
||||||
- Lists
|
|
||||||
- Lists
|
|
||||||
|
|
||||||
You can also add mermaid charts
|
|
||||||
|
|
||||||
```mermaid
|
|
||||||
graph TD;
|
|
||||||
A-->B;
|
|
||||||
A-->C;
|
|
||||||
B-->D;
|
|
||||||
C-->D;
|
|
||||||
```
|
|
||||||
1160
examples/openspending/package-lock.json
generated
1160
examples/openspending/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -12,15 +12,27 @@
|
|||||||
"test": "vitest"
|
"test": "vitest"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@flowershow/core": "^0.4.13",
|
||||||
|
"@flowershow/markdowndb": "^0.1.5",
|
||||||
|
"@heroicons/react": "^2.0.18",
|
||||||
"@octokit/plugin-throttling": "^5.2.2",
|
"@octokit/plugin-throttling": "^5.2.2",
|
||||||
|
"@portaljs/components": "0.1.4",
|
||||||
"@types/flexsearch": "^0.7.3",
|
"@types/flexsearch": "^0.7.3",
|
||||||
|
"@types/node": "18.16.0",
|
||||||
|
"@types/react": "18.0.38",
|
||||||
|
"@types/react-dom": "18.0.11",
|
||||||
"@vitejs/plugin-react": "^4.0.0",
|
"@vitejs/plugin-react": "^4.0.0",
|
||||||
"clsx": "^1.2.1",
|
"clsx": "^1.2.1",
|
||||||
"datapackage": "^1.1.10",
|
"datapackage": "^1.1.10",
|
||||||
|
"eslint": "8.39.0",
|
||||||
|
"eslint-config-next": "13.3.1",
|
||||||
"flexsearch": "0.7.21",
|
"flexsearch": "0.7.21",
|
||||||
|
"next": "13.3.0",
|
||||||
"next-seo": "^6.0.0",
|
"next-seo": "^6.0.0",
|
||||||
"octokit": "^2.0.14",
|
"octokit": "^2.0.14",
|
||||||
"prettier": "^2.8.8",
|
"prettier": "^2.8.8",
|
||||||
|
"react": "18.2.0",
|
||||||
|
"react-dom": "18.2.0",
|
||||||
"react-hook-form": "^7.43.9",
|
"react-hook-form": "^7.43.9",
|
||||||
"react-markdown": "^8.0.7",
|
"react-markdown": "^8.0.7",
|
||||||
"react-timeago": "^7.1.0",
|
"react-timeago": "^7.1.0",
|
||||||
@@ -28,38 +40,6 @@
|
|||||||
"remark": "^14.0.3",
|
"remark": "^14.0.3",
|
||||||
"remark-gfm": "^3.0.1",
|
"remark-gfm": "^3.0.1",
|
||||||
"strip-markdown": "^5.0.1",
|
"strip-markdown": "^5.0.1",
|
||||||
"@flowershow/core": "^0.4.13",
|
|
||||||
"@flowershow/markdowndb": "^0.1.5",
|
|
||||||
"@flowershow/remark-callouts": "^1.0.0",
|
|
||||||
"@flowershow/remark-embed": "^1.0.0",
|
|
||||||
"@githubocto/flat-ui": "^0.14.1",
|
|
||||||
"@headlessui/react": "^1.7.14",
|
|
||||||
"@heroicons/react": "^2.0.18",
|
|
||||||
"@portaljs/ckan": "^0.0.2",
|
|
||||||
"@portaljs/components": "^0.1.8",
|
|
||||||
"@tailwindcss/typography": "^0.5.9",
|
|
||||||
"@types/node": "20.2.3",
|
|
||||||
"@types/react": "18.2.6",
|
|
||||||
"@types/react-dom": "18.2.4",
|
|
||||||
"autoprefixer": "10.4.14",
|
|
||||||
"eslint": "8.41.0",
|
|
||||||
"eslint-config-next": "13.4.3",
|
|
||||||
"isomorphic-unfetch": "^4.0.2",
|
|
||||||
"next": "13.4.3",
|
|
||||||
"next-mdx-remote": "^4.4.1",
|
|
||||||
"papaparse": "^5.4.1",
|
|
||||||
"postcss": "8.4.23",
|
|
||||||
"react": "18.2.0",
|
|
||||||
"react-dom": "18.2.0",
|
|
||||||
"react-query": "^3.39.3",
|
|
||||||
"rehype-autolink-headings": "^6.1.1",
|
|
||||||
"rehype-katex": "^6.0.3",
|
|
||||||
"rehype-prism-plus": "^1.5.1",
|
|
||||||
"rehype-slug": "^5.1.0",
|
|
||||||
"remark-math": "^5.1.1",
|
|
||||||
"remark-smartypants": "^2.0.0",
|
|
||||||
"remark-toc": "^8.0.1",
|
|
||||||
"tailwindcss": "3.3.2",
|
|
||||||
"typescript": "5.0.4"
|
"typescript": "5.0.4"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|||||||
@@ -184,15 +184,14 @@ export default function ProjectPage({
|
|||||||
return (
|
return (
|
||||||
<div key={file.name}>
|
<div key={file.name}>
|
||||||
{file.path && (
|
{file.path && (
|
||||||
<div className="lg:ml-[calc(50%-37vw)] lg:w-[74vw]">
|
<>
|
||||||
<h4>
|
<h4>
|
||||||
{file.name}
|
{file.name}
|
||||||
{file.format ? `.${file.format}` : ''}
|
{file.format ? `.${file.format}` : ''}
|
||||||
</h4>
|
</h4>
|
||||||
{file.bytes >= 512000 && (
|
{file.bytes >= 5132288 && (
|
||||||
<span>Previewing 500KB out of {size}</span>
|
<span>Previewing 5MB out of {size}</span>
|
||||||
)}
|
)}
|
||||||
<div className="mt-5">
|
|
||||||
<FlatUiTable
|
<FlatUiTable
|
||||||
url={
|
url={
|
||||||
file.path.startsWith('http')
|
file.path.startsWith('http')
|
||||||
@@ -200,8 +199,7 @@ export default function ProjectPage({
|
|||||||
: `https://raw.githubusercontent.com/${project.owner.name}/${project.repo.name}/main/${file.path}`
|
: `https://raw.githubusercontent.com/${project.owner.name}/${project.repo.name}/main/${file.path}`
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</div>
|
</>
|
||||||
</div>
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -53,7 +53,6 @@ export async function getStaticPaths() {
|
|||||||
|
|
||||||
const paths = allDocuments
|
const paths = allDocuments
|
||||||
.filter((page) => page.metadata?.isDraft !== true)
|
.filter((page) => page.metadata?.isDraft !== true)
|
||||||
.filter((page) => !page.file_path.startsWith('content/stories/'))
|
|
||||||
.map((page) => {
|
.map((page) => {
|
||||||
const parts = page.url_path!.split('/');
|
const parts = page.url_path!.split('/');
|
||||||
return { params: { slug: parts } };
|
return { params: { slug: parts } };
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ function CustomApp({ Component, pageProps }: AppProps) {
|
|||||||
<NextSeo title="OpenSpending" />
|
<NextSeo title="OpenSpending" />
|
||||||
<Script
|
<Script
|
||||||
strategy="afterInteractive"
|
strategy="afterInteractive"
|
||||||
src="https://www.googletagmanager.com/gtag/js?id=G-GXZF7NRXX6"
|
src={`https://www.googletagmanager.com/gtag/js?id=${GA_TOKEN}`}
|
||||||
/>
|
/>
|
||||||
<Script
|
<Script
|
||||||
id="gtag-init"
|
id="gtag-init"
|
||||||
@@ -67,7 +67,7 @@ function CustomApp({ Component, pageProps }: AppProps) {
|
|||||||
window.dataLayer = window.dataLayer || [];
|
window.dataLayer = window.dataLayer || [];
|
||||||
function gtag(){dataLayer.push(arguments);}
|
function gtag(){dataLayer.push(arguments);}
|
||||||
gtag('js', new Date());
|
gtag('js', new Date());
|
||||||
gtag('config', 'G-GXZF7NRXX6', {
|
gtag('config', ${GA_TOKEN}, {
|
||||||
page_path: window.location.pathname,
|
page_path: window.location.pathname,
|
||||||
});
|
});
|
||||||
`,
|
`,
|
||||||
|
|||||||
@@ -1,62 +0,0 @@
|
|||||||
import fs from 'fs';
|
|
||||||
import clientPromise from '@/lib/mddb';
|
|
||||||
import { GetStaticProps } from 'next';
|
|
||||||
import Layout from '../../components/_shared/Layout';
|
|
||||||
import { formatDate } from '@/utils/formatDate';
|
|
||||||
import parse from '../../lib/markdown';
|
|
||||||
import DataRichDocument from '../../components/DataRichDocument';
|
|
||||||
|
|
||||||
export default function Page({ source, meta }) {
|
|
||||||
return (
|
|
||||||
<Layout>
|
|
||||||
<article className="docs prose-a:text-primary dark:prose-a:text-primary-dark prose-strong:text-primary dark:prose-strong:text-primary-dark prose-code:text-primary dark:prose-code:text-primary-dark prose-headings:text-primary dark:prose-headings:text-primary-dark prose text-primary dark:text-primary-dark prose-headings:font-headings dark:prose-invert prose-a:break-words mx-auto p-6">
|
|
||||||
<header>
|
|
||||||
<div className="mb-4 flex-col items-center">
|
|
||||||
{meta.title && (
|
|
||||||
<h1 className="flex justify-center">{meta.title}</h1>
|
|
||||||
)}
|
|
||||||
{meta.date && (
|
|
||||||
<p className="text-sm text-zinc-400 dark:text-zinc-500 flex justify-center">
|
|
||||||
<time dateTime={meta.date}>{formatDate(meta.date)}</time>
|
|
||||||
</p>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</header>
|
|
||||||
<section>
|
|
||||||
<DataRichDocument source={source} />
|
|
||||||
</section>
|
|
||||||
</article>
|
|
||||||
</Layout>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export const getStaticProps: GetStaticProps = async ({ params }) => {
|
|
||||||
const mddb = await clientPromise;
|
|
||||||
const dbFile = await mddb.getFileByUrl('stories/' + params?.fileName);
|
|
||||||
|
|
||||||
let source = fs.readFileSync(dbFile.file_path, { encoding: 'utf-8' });
|
|
||||||
let { mdxSource } = await parse(source, '.mdx', {});
|
|
||||||
return {
|
|
||||||
props: {
|
|
||||||
source: mdxSource,
|
|
||||||
meta: dbFile.metadata,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export async function getStaticPaths() {
|
|
||||||
const mddb = await clientPromise;
|
|
||||||
let allDocuments = await mddb.getFiles({ extensions: ['mdx'], folder: 'stories' });
|
|
||||||
|
|
||||||
const paths = allDocuments
|
|
||||||
.filter((page) => page.metadata?.isDraft !== true)
|
|
||||||
.map((page) => {
|
|
||||||
const parts = page.url_path!.split('/').slice(-1)[0];
|
|
||||||
return { params: { fileName: parts } };
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
|
||||||
paths,
|
|
||||||
fallback: false,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -1,57 +0,0 @@
|
|||||||
import fs from 'fs';
|
|
||||||
import React from 'react';
|
|
||||||
import { GetStaticProps } from 'next';
|
|
||||||
import { BlogsList, SimpleLayout } from '@flowershow/core';
|
|
||||||
import clientPromise from '../../lib/mddb';
|
|
||||||
import type { CustomAppProps } from '../_app';
|
|
||||||
import Layout from '@/components/_shared/Layout';
|
|
||||||
|
|
||||||
interface BlogIndexPageProps extends CustomAppProps {
|
|
||||||
blogs: any[]; // TODO types
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function StoriesList({
|
|
||||||
stories,
|
|
||||||
meta: { title, description },
|
|
||||||
}: BlogIndexPageProps) {
|
|
||||||
return (
|
|
||||||
<Layout>
|
|
||||||
<div className="blog-list">
|
|
||||||
<SimpleLayout title={title} description={description}>
|
|
||||||
<BlogsList blogs={stories} />
|
|
||||||
</SimpleLayout>
|
|
||||||
</div>
|
|
||||||
</Layout>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export const getStaticProps: GetStaticProps = async () => {
|
|
||||||
const mddb = await clientPromise;
|
|
||||||
const storiesFiles = await mddb.getFiles({ folder: 'stories' });
|
|
||||||
|
|
||||||
const stories = storiesFiles.map((item) => ({
|
|
||||||
_id: item._id,
|
|
||||||
file_path: item.file_path,
|
|
||||||
urlPath: item.url_path,
|
|
||||||
date: item.metadata.date,
|
|
||||||
...item.metadata,
|
|
||||||
}));
|
|
||||||
|
|
||||||
return {
|
|
||||||
props: {
|
|
||||||
meta: {
|
|
||||||
title: 'Data Stories',
|
|
||||||
showSidebar: false,
|
|
||||||
showToc: false,
|
|
||||||
showComments: false,
|
|
||||||
showEditLink: false,
|
|
||||||
urlPath: '/stories',
|
|
||||||
},
|
|
||||||
stories: stories.sort((a, b) => {
|
|
||||||
const bDate = new Date(b.date);
|
|
||||||
const aDate = new Date(a.date);
|
|
||||||
return bDate.getTime() - aDate.getTime();
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
};
|
|
||||||
@@ -82,99 +82,3 @@ pre {
|
|||||||
.blog-list button {
|
.blog-list button {
|
||||||
color: black;
|
color: black;
|
||||||
}
|
}
|
||||||
|
|
||||||
@tailwind base;
|
|
||||||
@tailwind components;
|
|
||||||
@tailwind utilities;
|
|
||||||
@import "@flowershow/remark-callouts/styles.css";
|
|
||||||
|
|
||||||
.w-5 {
|
|
||||||
width: 1.25rem
|
|
||||||
}
|
|
||||||
|
|
||||||
.h-5 {
|
|
||||||
height: 1.25rem
|
|
||||||
}
|
|
||||||
|
|
||||||
/* mathjax */
|
|
||||||
.math-inline > mjx-container > svg {
|
|
||||||
display: inline;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* smooth scrolling in modern browsers */
|
|
||||||
html {
|
|
||||||
scroll-behavior: smooth !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* tooltip fade-out clip */
|
|
||||||
.tooltip-body::after {
|
|
||||||
content: "";
|
|
||||||
position: absolute;
|
|
||||||
right: 0;
|
|
||||||
top: 3.6rem; /* multiple of $line-height used on the tooltip body (defined in tooltipBodyStyle) */
|
|
||||||
height: 1.2rem; /* ($top + $height)/$line-height is the number of lines we want to clip tooltip text at*/
|
|
||||||
width: 10rem;
|
|
||||||
background: linear-gradient(
|
|
||||||
to right,
|
|
||||||
rgba(255, 255, 255, 0),
|
|
||||||
rgba(255, 255, 255, 1) 100%
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
:is(h2, h3, h4, h5, h6):not(.blogitem-title) {
|
|
||||||
margin-left: -2rem !important;
|
|
||||||
padding-left: 2rem !important;
|
|
||||||
scroll-margin-top: 4.5rem;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.heading-link {
|
|
||||||
padding: 1px;
|
|
||||||
position: absolute;
|
|
||||||
left: 0;
|
|
||||||
top: 50%;
|
|
||||||
transform: translateY(-50%);
|
|
||||||
margin: auto 0;
|
|
||||||
border-radius: 5px;
|
|
||||||
background: #1e293b;
|
|
||||||
opacity: 0;
|
|
||||||
transition: opacity 0.2s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.light .heading-link {
|
|
||||||
/* border: 1px solid #ab2b65; */
|
|
||||||
/* background: none; */
|
|
||||||
background: #e2e8f0;
|
|
||||||
}
|
|
||||||
|
|
||||||
:is(h2, h3, h4, h5, h6):not(.blogitem-title):hover .heading-link {
|
|
||||||
opacity: 100;
|
|
||||||
}
|
|
||||||
|
|
||||||
.heading-link svg {
|
|
||||||
transform: scale(0.75);
|
|
||||||
}
|
|
||||||
|
|
||||||
@media screen and (max-width: 640px) {
|
|
||||||
.heading-link {
|
|
||||||
visibility: hidden;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
html,
|
|
||||||
body {
|
|
||||||
padding: 0;
|
|
||||||
margin: 0;
|
|
||||||
font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen,
|
|
||||||
Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
|
|
||||||
}
|
|
||||||
|
|
||||||
a {
|
|
||||||
color: inherit;
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
* {
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|||||||
31275
package-lock.json
generated
31275
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
25
package.json
25
package.json
@@ -1,24 +1,17 @@
|
|||||||
{
|
{
|
||||||
"name": "portaljs",
|
"name": "portaljs",
|
||||||
"workspaces": ["./packages/*"],
|
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"scripts": {
|
"scripts": {},
|
||||||
"changeset": "changeset",
|
|
||||||
"prerelease": "nx affected --targets=lint,test,build",
|
|
||||||
"release": "changeset publish"
|
|
||||||
},
|
|
||||||
"private": true,
|
"private": true,
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/preset-react": "^7.14.5",
|
"@babel/preset-react": "^7.14.5",
|
||||||
"@changesets/changelog-github": "^0.4.8",
|
|
||||||
"@changesets/cli": "^2.26.1",
|
|
||||||
"@nrwl/cypress": "15.9.2",
|
"@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/jest": "15.9.2",
|
||||||
"@nrwl/js": "15.9.2",
|
"@nrwl/js": "15.9.2",
|
||||||
"@nrwl/linter": "15.9.2",
|
"@nrwl/linter": "15.9.2",
|
||||||
"@nrwl/next": "15.9.2",
|
"@nrwl/next": "^15.9.2",
|
||||||
"@nrwl/react": "15.9.2",
|
"@nrwl/react": "15.9.2",
|
||||||
"@nrwl/rollup": "15.9.2",
|
"@nrwl/rollup": "15.9.2",
|
||||||
"@nrwl/workspace": "15.9.2",
|
"@nrwl/workspace": "15.9.2",
|
||||||
@@ -28,16 +21,13 @@
|
|||||||
"@swc/helpers": "~0.5.0",
|
"@swc/helpers": "~0.5.0",
|
||||||
"@swc/jest": "0.2.20",
|
"@swc/jest": "0.2.20",
|
||||||
"@testing-library/react": "14.0.0",
|
"@testing-library/react": "14.0.0",
|
||||||
"@types/chai": "^4.3.5",
|
|
||||||
"@types/jest": "^29.4.0",
|
"@types/jest": "^29.4.0",
|
||||||
"@types/mocha": "^10.0.1",
|
|
||||||
"@types/node": "18.14.2",
|
"@types/node": "18.14.2",
|
||||||
"@types/react": "18.0.28",
|
"@types/react": "18.0.28",
|
||||||
"@types/react-dom": "18.0.11",
|
"@types/react-dom": "18.0.11",
|
||||||
"@typescript-eslint/eslint-plugin": "^5.36.1",
|
"@typescript-eslint/eslint-plugin": "^5.36.1",
|
||||||
"@typescript-eslint/parser": "^5.36.1",
|
"@typescript-eslint/parser": "^5.36.1",
|
||||||
"babel-jest": "^29.4.1",
|
"babel-jest": "^29.4.1",
|
||||||
"chai": "^4.3.7",
|
|
||||||
"cypress": "^12.2.0",
|
"cypress": "^12.2.0",
|
||||||
"eslint": "~8.15.0",
|
"eslint": "~8.15.0",
|
||||||
"eslint-config-next": "13.1.1",
|
"eslint-config-next": "13.1.1",
|
||||||
@@ -47,21 +37,14 @@
|
|||||||
"eslint-plugin-jsx-a11y": "6.7.1",
|
"eslint-plugin-jsx-a11y": "6.7.1",
|
||||||
"eslint-plugin-react": "7.32.2",
|
"eslint-plugin-react": "7.32.2",
|
||||||
"eslint-plugin-react-hooks": "4.6.0",
|
"eslint-plugin-react-hooks": "4.6.0",
|
||||||
"htmlparser2": "^9.0.0",
|
|
||||||
"jest": "^29.4.1",
|
"jest": "^29.4.1",
|
||||||
"jest-environment-jsdom": "^29.4.1",
|
"jest-environment-jsdom": "^29.4.1",
|
||||||
"micromark": "^3.2.0",
|
|
||||||
"mocha": "^10.2.0",
|
|
||||||
"nx": "15.9.2",
|
"nx": "15.9.2",
|
||||||
"prettier": "^2.6.2",
|
"prettier": "^2.6.2",
|
||||||
"react-test-renderer": "18.2.0",
|
"react-test-renderer": "18.2.0",
|
||||||
"rehype-stringify": "^9.0.3",
|
|
||||||
"remark": "^14.0.3",
|
|
||||||
"swc-loader": "0.1.15",
|
"swc-loader": "0.1.15",
|
||||||
"ts-jest": "^29.0.5",
|
"ts-jest": "^29.0.5",
|
||||||
"ts-node": "10.9.1",
|
"ts-node": "10.9.1",
|
||||||
"typescript": "~4.9.5",
|
"typescript": "~4.9.5"
|
||||||
"unist-util-select": "^4.0.3",
|
|
||||||
"unist-util-visit": "^4.1.2"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
1
packages/components/.gitignore
vendored
1
packages/components/.gitignore
vendored
@@ -9,7 +9,6 @@ lerna-debug.log*
|
|||||||
|
|
||||||
node_modules
|
node_modules
|
||||||
dist
|
dist
|
||||||
storybook-static
|
|
||||||
dist-ssr
|
dist-ssr
|
||||||
*.local
|
*.local
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import 'tailwindcss/tailwind.css'
|
import 'tailwindcss/tailwind.css'
|
||||||
import '../src/index.css'
|
|
||||||
|
|
||||||
import type { Preview } from '@storybook/react';
|
import type { Preview } from '@storybook/react';
|
||||||
|
|
||||||
|
|||||||
@@ -1,13 +0,0 @@
|
|||||||
# @portaljs/components
|
|
||||||
|
|
||||||
## 0.1.9
|
|
||||||
|
|
||||||
### Patch Changes
|
|
||||||
|
|
||||||
- [#933](https://github.com/datopian/portaljs/pull/933) [`683159d`](https://github.com/datopian/portaljs/commit/683159da02708ce520e9aad9707f2b96b598ec8b) Thanks [@demenech](https://github.com/demenech)! - More params added to <LineChart />, loading spinners added to <Table /> and <LineChart />, minor fixes
|
|
||||||
|
|
||||||
## 0.1.8
|
|
||||||
|
|
||||||
### Patch Changes
|
|
||||||
|
|
||||||
- [#924](https://github.com/datopian/portaljs/pull/924) [`959fe5a`](https://github.com/datopian/portaljs/commit/959fe5a588b12786e3f2e642c24009d77bceac46) Thanks [@luccasmmg](https://github.com/luccasmmg)! - Fixed redeclaration of formal parameter "x" error after next build
|
|
||||||
633
packages/components/package-lock.json
generated
633
packages/components/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@portaljs/components",
|
"name": "@portaljs/components",
|
||||||
"version": "0.1.9",
|
"version": "0.1.6",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"description": "https://portaljs.org",
|
"description": "https://portaljs.org",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
@@ -34,10 +34,8 @@
|
|||||||
"react-hook-form": "^7.43.9",
|
"react-hook-form": "^7.43.9",
|
||||||
"react-query": "^3.39.3",
|
"react-query": "^3.39.3",
|
||||||
"react-vega": "^7.6.0",
|
"react-vega": "^7.6.0",
|
||||||
"rollup-plugin-re": "^1.0.7",
|
|
||||||
"vega": "5.25.0",
|
"vega": "5.25.0",
|
||||||
"vega-lite": "5.1.0",
|
"vega-lite": "5.1.0"
|
||||||
"vitest": "^0.31.4"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@storybook/addon-essentials": "^7.0.7",
|
"@storybook/addon-essentials": "^7.0.7",
|
||||||
|
|||||||
@@ -1,13 +1,12 @@
|
|||||||
import { QueryClient, QueryClientProvider, useQuery } from 'react-query';
|
import { QueryClient, QueryClientProvider, useQuery } from 'react-query';
|
||||||
import Papa from 'papaparse';
|
import Papa from 'papaparse';
|
||||||
import { Grid } from '@githubocto/flat-ui';
|
import { Grid } from '@githubocto/flat-ui';
|
||||||
import LoadingSpinner from './LoadingSpinner';
|
|
||||||
|
|
||||||
const queryClient = new QueryClient();
|
const queryClient = new QueryClient();
|
||||||
|
|
||||||
export async function getCsv(url: string, corsProxy?: string) {
|
export async function getCsv(url: string, corsProxy?: string) {
|
||||||
if (corsProxy) {
|
if (corsProxy) {
|
||||||
url = corsProxy + url;
|
url = corsProxy + url
|
||||||
}
|
}
|
||||||
const response = await fetch(url, {
|
const response = await fetch(url, {
|
||||||
headers: {
|
headers: {
|
||||||
@@ -37,6 +36,30 @@ export async function parseCsv(file: string): Promise<any> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const Spinning = () => {
|
||||||
|
return (
|
||||||
|
<div role="status w-fit mx-auto">
|
||||||
|
<svg
|
||||||
|
aria-hidden="true"
|
||||||
|
className="w-8 h-8 mr-2 text-gray-200 animate-spin dark:text-gray-600 fill-emerald-600"
|
||||||
|
viewBox="0 0 100 101"
|
||||||
|
fill="none"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M100 50.5908C100 78.2051 77.6142 100.591 50 100.591C22.3858 100.591 0 78.2051 0 50.5908C0 22.9766 22.3858 0.59082 50 0.59082C77.6142 0.59082 100 22.9766 100 50.5908ZM9.08144 50.5908C9.08144 73.1895 27.4013 91.5094 50 91.5094C72.5987 91.5094 90.9186 73.1895 90.9186 50.5908C90.9186 27.9921 72.5987 9.67226 50 9.67226C27.4013 9.67226 9.08144 27.9921 9.08144 50.5908Z"
|
||||||
|
fill="currentColor"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M93.9676 39.0409C96.393 38.4038 97.8624 35.9116 97.0079 33.5539C95.2932 28.8227 92.871 24.3692 89.8167 20.348C85.8452 15.1192 80.8826 10.7238 75.2124 7.41289C69.5422 4.10194 63.2754 1.94025 56.7698 1.05124C51.7666 0.367541 46.6976 0.446843 41.7345 1.27873C39.2613 1.69328 37.813 4.19778 38.4501 6.62326C39.0873 9.04874 41.5694 10.4717 44.0505 10.1071C47.8511 9.54855 51.7191 9.52689 55.5402 10.0491C60.8642 10.7766 65.9928 12.5457 70.6331 15.2552C75.2735 17.9648 79.3347 21.5619 82.5849 25.841C84.9175 28.9121 86.7997 32.2913 88.1811 35.8758C89.083 38.2158 91.5421 39.6781 93.9676 39.0409Z"
|
||||||
|
fill="currentFill"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
<span className="sr-only">Loading...</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
export interface FlatUiTableProps {
|
export interface FlatUiTableProps {
|
||||||
url?: string;
|
url?: string;
|
||||||
data?: { [key: string]: number | string }[];
|
data?: { [key: string]: number | string }[];
|
||||||
@@ -57,12 +80,7 @@ export const FlatUiTable: React.FC<FlatUiTableProps> = ({
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const TableInner: React.FC<FlatUiTableProps> = ({
|
const TableInner: React.FC<FlatUiTableProps> = ({ url, data, rawCsv, corsProxy }) => {
|
||||||
url,
|
|
||||||
data,
|
|
||||||
rawCsv,
|
|
||||||
corsProxy,
|
|
||||||
}) => {
|
|
||||||
if (data) {
|
if (data) {
|
||||||
return (
|
return (
|
||||||
<div className="w-full" style={{height: '500px'}}>
|
<div className="w-full" style={{height: '500px'}}>
|
||||||
@@ -77,12 +95,12 @@ const TableInner: React.FC<FlatUiTableProps> = ({
|
|||||||
);
|
);
|
||||||
const { data: parsedData, isLoading: isParsing } = useQuery(
|
const { data: parsedData, isLoading: isParsing } = useQuery(
|
||||||
['dataPreview', csvString],
|
['dataPreview', csvString],
|
||||||
() => parseCsv(rawCsv ? (rawCsv as string) : (csvString as string)),
|
() => parseCsv(rawCsv ? rawCsv as string : csvString as string),
|
||||||
{ enabled: rawCsv ? true : !!csvString }
|
{ enabled: rawCsv ? true : !!csvString }
|
||||||
);
|
);
|
||||||
if (isParsing || isDownloadingCSV)
|
if (isParsing || isDownloadingCSV)
|
||||||
<div className="w-full flex justify-center items-center h-[500px]">
|
<div className="w-full">
|
||||||
<LoadingSpinner />
|
<Spinning />
|
||||||
</div>;
|
</div>;
|
||||||
if (parsedData)
|
if (parsedData)
|
||||||
return (
|
return (
|
||||||
@@ -90,9 +108,6 @@ const TableInner: React.FC<FlatUiTableProps> = ({
|
|||||||
<Grid data={parsedData.data} />
|
<Grid data={parsedData.data} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
return (
|
return <Spinning />
|
||||||
<div className="w-full flex justify-center items-center h-[500px]">
|
|
||||||
<LoadingSpinner />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,19 +1,10 @@
|
|||||||
import { useEffect, useState } from 'react';
|
|
||||||
import LoadingSpinner from './LoadingSpinner';
|
|
||||||
import { VegaLite } from './VegaLite';
|
import { VegaLite } from './VegaLite';
|
||||||
import loadData from '../lib/loadData';
|
|
||||||
|
|
||||||
type AxisType = 'quantitative' | 'temporal';
|
|
||||||
type TimeUnit = 'year' | undefined; // or ...
|
|
||||||
|
|
||||||
export type LineChartProps = {
|
export type LineChartProps = {
|
||||||
data: Array<Array<string | number>> | string | { x: string; y: number }[];
|
data: Array<Array<string | number>> | string | { x: string; y: number }[];
|
||||||
title?: string;
|
title?: string;
|
||||||
xAxis?: string;
|
xAxis?: string;
|
||||||
xAxisType?: AxisType;
|
|
||||||
xAxisTimeUnit: TimeUnit;
|
|
||||||
yAxis?: string;
|
yAxis?: string;
|
||||||
yAxisType?: AxisType;
|
|
||||||
fullWidth?: boolean;
|
fullWidth?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -22,16 +13,15 @@ export function LineChart({
|
|||||||
fullWidth = false,
|
fullWidth = false,
|
||||||
title = '',
|
title = '',
|
||||||
xAxis = 'x',
|
xAxis = 'x',
|
||||||
xAxisType = 'temporal',
|
|
||||||
xAxisTimeUnit = 'year', // TODO: defaults to undefined would probably work better... keeping it as it's for compatibility purposes
|
|
||||||
yAxis = 'y',
|
yAxis = 'y',
|
||||||
yAxisType = 'quantitative',
|
|
||||||
}: LineChartProps) {
|
}: LineChartProps) {
|
||||||
const [isLoading, setIsLoading] = useState<boolean>(false);
|
var tmp = data;
|
||||||
|
if (Array.isArray(data)) {
|
||||||
// By default, assumes data is an Array...
|
tmp = data.map((r) => {
|
||||||
const [specData, setSpecData] = useState<any>({ name: 'table' });
|
return { x: r[0], y: r[1] };
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const vegaData = { table: tmp };
|
||||||
const spec = {
|
const spec = {
|
||||||
$schema: 'https://vega.github.io/schema/vega-lite/v5.json',
|
$schema: 'https://vega.github.io/schema/vega-lite/v5.json',
|
||||||
title,
|
title,
|
||||||
@@ -43,7 +33,9 @@ export function LineChart({
|
|||||||
strokeWidth: 1,
|
strokeWidth: 1,
|
||||||
tooltip: true,
|
tooltip: true,
|
||||||
},
|
},
|
||||||
data: specData,
|
data: {
|
||||||
|
name: 'table',
|
||||||
|
},
|
||||||
selection: {
|
selection: {
|
||||||
grid: {
|
grid: {
|
||||||
type: 'interval',
|
type: 'interval',
|
||||||
@@ -53,44 +45,19 @@ export function LineChart({
|
|||||||
encoding: {
|
encoding: {
|
||||||
x: {
|
x: {
|
||||||
field: xAxis,
|
field: xAxis,
|
||||||
timeUnit: xAxisTimeUnit,
|
timeUnit: 'year',
|
||||||
type: xAxisType,
|
type: 'temporal',
|
||||||
},
|
},
|
||||||
y: {
|
y: {
|
||||||
field: yAxis,
|
field: yAxis,
|
||||||
type: yAxisType,
|
type: 'quantitative',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
} as any;
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
// If data is string, assume it's a URL
|
|
||||||
if (typeof data === 'string') {
|
if (typeof data === 'string') {
|
||||||
setIsLoading(true);
|
spec.data = { url: data } as any;
|
||||||
|
return <VegaLite fullWidth={fullWidth} spec={spec} />;
|
||||||
// Manualy loading the data allows us to do other kinds
|
|
||||||
// of stuff later e.g. load a file partially
|
|
||||||
loadData(data).then((res: any) => {
|
|
||||||
setSpecData({ values: res, format: { type: 'csv' } });
|
|
||||||
setIsLoading(false);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
var vegaData = {};
|
|
||||||
if (Array.isArray(data)) {
|
|
||||||
var dataObj;
|
|
||||||
dataObj = data.map((r) => {
|
|
||||||
return { x: r[0], y: r[1] };
|
|
||||||
});
|
|
||||||
vegaData = { table: dataObj };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return isLoading ? (
|
return <VegaLite fullWidth={fullWidth} data={vegaData} spec={spec} />;
|
||||||
<div className="w-full flex items-center justify-center w-[600px] h-[300px]">
|
|
||||||
<LoadingSpinner />
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<VegaLite fullWidth={fullWidth} data={vegaData} spec={spec} />
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,23 +0,0 @@
|
|||||||
export default function LoadingSpinner() {
|
|
||||||
return (
|
|
||||||
<div role="status w-fit mx-auto">
|
|
||||||
<svg
|
|
||||||
aria-hidden="true"
|
|
||||||
className="w-16 h-16 mr-2 text-gray-200 animate-spin dark:text-gray-600 fill-slate-600"
|
|
||||||
viewBox="0 0 100 101"
|
|
||||||
fill="none"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
d="M100 50.5908C100 78.2051 77.6142 100.591 50 100.591C22.3858 100.591 0 78.2051 0 50.5908C0 22.9766 22.3858 0.59082 50 0.59082C77.6142 0.59082 100 22.9766 100 50.5908ZM9.08144 50.5908C9.08144 73.1895 27.4013 91.5094 50 91.5094C72.5987 91.5094 90.9186 73.1895 90.9186 50.5908C90.9186 27.9921 72.5987 9.67226 50 9.67226C27.4013 9.67226 9.08144 27.9921 9.08144 50.5908Z"
|
|
||||||
fill="currentColor"
|
|
||||||
/>
|
|
||||||
<path
|
|
||||||
d="M93.9676 39.0409C96.393 38.4038 97.8624 35.9116 97.0079 33.5539C95.2932 28.8227 92.871 24.3692 89.8167 20.348C85.8452 15.1192 80.8826 10.7238 75.2124 7.41289C69.5422 4.10194 63.2754 1.94025 56.7698 1.05124C51.7666 0.367541 46.6976 0.446843 41.7345 1.27873C39.2613 1.69328 37.813 4.19778 38.4501 6.62326C39.0873 9.04874 41.5694 10.4717 44.0505 10.1071C47.8511 9.54855 51.7191 9.52689 55.5402 10.0491C60.8642 10.7766 65.9928 12.5457 70.6331 15.2552C75.2735 17.9648 79.3347 21.5619 82.5849 25.841C84.9175 28.9121 86.7997 32.2913 88.1811 35.8758C89.083 38.2158 91.5421 39.6781 93.9676 39.0409Z"
|
|
||||||
fill="currentFill"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
<span className="sr-only">Loading...</span>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -23,7 +23,6 @@ import React, { useEffect, useMemo, useState } from 'react';
|
|||||||
import parseCsv from '../lib/parseCsv';
|
import parseCsv from '../lib/parseCsv';
|
||||||
import DebouncedInput from './DebouncedInput';
|
import DebouncedInput from './DebouncedInput';
|
||||||
import loadData from '../lib/loadData';
|
import loadData from '../lib/loadData';
|
||||||
import LoadingSpinner from './LoadingSpinner';
|
|
||||||
|
|
||||||
export type TableProps = {
|
export type TableProps = {
|
||||||
data?: Array<{ [key: string]: number | string }>;
|
data?: Array<{ [key: string]: number | string }>;
|
||||||
@@ -40,8 +39,6 @@ export const Table = ({
|
|||||||
url = '',
|
url = '',
|
||||||
fullWidth = false,
|
fullWidth = false,
|
||||||
}: TableProps) => {
|
}: TableProps) => {
|
||||||
const [isLoading, setIsLoading] = useState<boolean>(false);
|
|
||||||
|
|
||||||
if (csv) {
|
if (csv) {
|
||||||
const out = parseCsv(csv);
|
const out = parseCsv(csv);
|
||||||
ogData = out.rows;
|
ogData = out.rows;
|
||||||
@@ -80,22 +77,15 @@ export const Table = ({
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (url) {
|
if (url) {
|
||||||
setIsLoading(true);
|
|
||||||
// TODO: exception handling. What if the file doesn't exist? What if fetching was not possible?
|
|
||||||
loadData(url).then((data) => {
|
loadData(url).then((data) => {
|
||||||
const { rows, fields } = parseCsv(data);
|
const { rows, fields } = parseCsv(data);
|
||||||
setData(rows);
|
setData(rows);
|
||||||
setCols(fields);
|
setCols(fields);
|
||||||
setIsLoading(false);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, [url]);
|
}, [url]);
|
||||||
|
|
||||||
return isLoading ? (
|
return (
|
||||||
<div className="w-full h-full min-h-[500px] flex items-center justify-center">
|
|
||||||
<LoadingSpinner />
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<div className={`${fullWidth ? 'w-[90vw] ml-[calc(50%-45vw)]' : 'w-full'}`}>
|
<div className={`${fullWidth ? 'w-[90vw] ml-[calc(50%-45vw)]' : 'w-full'}`}>
|
||||||
<DebouncedInput
|
<DebouncedInput
|
||||||
value={globalFilter ?? ''}
|
value={globalFilter ?? ''}
|
||||||
|
|||||||
@@ -1,10 +1,3 @@
|
|||||||
@tailwind base;
|
@tailwind base;
|
||||||
@tailwind components;
|
@tailwind components;
|
||||||
@tailwind utilities;
|
@tailwind utilities;
|
||||||
|
|
||||||
/* Temporary fix for a size issue with FlatUiTable loading indicator on Firefox */
|
|
||||||
@layer base {
|
|
||||||
svg[tw^='animate-pulse w-12'] {
|
|
||||||
max-width: 100px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -19,19 +19,10 @@ const meta: Meta = {
|
|||||||
description:
|
description:
|
||||||
'Name of the X axis on the data. Required when the "data" parameter is an URL.',
|
'Name of the X axis on the data. Required when the "data" parameter is an URL.',
|
||||||
},
|
},
|
||||||
xAxisType: {
|
|
||||||
description: 'Type of the X axis',
|
|
||||||
},
|
|
||||||
xAxisTimeUnit: {
|
|
||||||
description: 'Time unit of the X axis (optional)',
|
|
||||||
},
|
|
||||||
yAxis: {
|
yAxis: {
|
||||||
description:
|
description:
|
||||||
'Name of the Y axis on the data. Required when the "data" parameter is an URL.',
|
'Name of the Y axis on the data. Required when the "data" parameter is an URL.',
|
||||||
},
|
},
|
||||||
yAxisType: {
|
|
||||||
description: 'Type of the Y axis',
|
|
||||||
},
|
|
||||||
fullWidth: {
|
fullWidth: {
|
||||||
description:
|
description:
|
||||||
'Whether the component should be rendered as full bleed or not',
|
'Whether the component should be rendered as full bleed or not',
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1,2 @@
|
|||||||
|
import{j as p}from"./jsx-runtime-94f6e698.js";import{r as u}from"./index-8db94870.js";const a=({value:e,onChange:o,debounce:s=500,...c})=>{const[t,r]=u.useState(e);return u.useEffect(()=>{r(e)},[e]),u.useEffect(()=>{const n=setTimeout(()=>{o(t)},s);return()=>clearTimeout(n)},[t]),p.jsx("input",{...c,value:t,onChange:n=>r(n.target.value)})};try{a.displayName="DebouncedInput",a.__docgenInfo={description:"",displayName:"DebouncedInput",props:{value:{defaultValue:null,description:"",name:"value",required:!0,type:{name:"any"}},onChange:{defaultValue:null,description:"",name:"onChange",required:!0,type:{name:"any"}},debounce:{defaultValue:{value:"500"},description:"",name:"debounce",required:!1,type:{name:"number"}}}}}catch{}export{a as D};
|
||||||
|
//# sourceMappingURL=DebouncedInput-c720676c.js.map
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"file":"DebouncedInput-c720676c.js","sources":["../../src/components/DebouncedInput.tsx"],"sourcesContent":["import { useEffect, useState } from \"react\";\n\nconst DebouncedInput = ({\n value: initialValue,\n onChange,\n debounce = 500,\n ...props\n}) => {\n const [value, setValue] = useState(initialValue);\n\n useEffect(() => {\n setValue(initialValue);\n }, [initialValue]);\n\n useEffect(() => {\n const timeout = setTimeout(() => {\n onChange(value);\n }, debounce);\n\n return () => clearTimeout(timeout);\n }, [value]);\n\n return (\n <input\n {...props}\n value={value}\n onChange={(e) => setValue(e.target.value)}\n />\n );\n};\n\nexport default DebouncedInput;\n"],"names":["DebouncedInput","initialValue","onChange","debounce","props","value","setValue","useState","useEffect","timeout","jsx","e"],"mappings":"sFAEA,MAAAA,EAAA,CAAA,CAAwB,MAAAC,EACf,SAAAC,EACP,SAAAC,EAAA,IACW,GAAAC,CAEb,IAAA,CACE,KAAA,CAAAC,EAAAC,CAAA,EAAAC,EAAA,SAAAN,CAAA,EAEAO,OAAAA,EAAAA,UAAA,IAAA,CACEF,EAAAL,CAAA,CAAqB,EAAA,CAAAA,CAAA,CAAA,EAGvBO,EAAAA,UAAA,IAAA,CACE,MAAAC,EAAA,WAAA,IAAA,CACEP,EAAAG,CAAA,CAAc,EAAAF,CAAA,EAGhB,MAAA,IAAA,aAAAM,CAAA,CAAiC,EAAA,CAAAJ,CAAA,CAAA,EAGnCK,EAAA,IACE,QAAC,CAAA,GAAAN,EACK,MAAAC,EACJ,SAAAM,GAAAL,EAAAK,EAAA,OAAA,KAAA,CACwC,CAAA,CAG9C"}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
import{_ as p}from"./iframe-1eda5ccb.js";import{R as e,r as a}from"./index-8db94870.js";import{r as c,u}from"./react-18-ff2c0a32.js";import{C as h,A as l,H as E,D as d}from"./index-89936ab1.js";import"../sb-preview/runtime.js";import"./_commonjsHelpers-042e6b4d.js";import"./index-8ce4a492.js";import"./index-d475d2ea.js";import"./isNativeReflectConstruct-099dc9ad.js";import"./index-d37d4223.js";import"./index-6e6be2d5.js";import"./index-356e4a49.js";var x={code:h,a:l,...E},_=class extends a.Component{constructor(){super(...arguments),this.state={hasError:!1}}static getDerivedStateFromError(){return{hasError:!0}}componentDidCatch(t){let{showException:r}=this.props;r(t)}render(){let{hasError:t}=this.state,{children:r}=this.props;return t?null:r}},S=class{constructor(){this.render=async(t,r,o)=>{let n={...x,...r==null?void 0:r.components};return new Promise((s,m)=>{p(()=>import("./index-186b3228.js"),["./index-186b3228.js","./index-1d576ef5.js","./index-8db94870.js","./_commonjsHelpers-042e6b4d.js"],import.meta.url).then(({MDXProvider:i})=>c(e.createElement(_,{showException:m,key:Math.random()},e.createElement(i,{components:n},e.createElement(d,{context:t,docsParameter:r}))),o)).then(s)})},this.unmount=t=>{u(t)}}};export{S as DocsRenderer,x as defaultComponents};
|
||||||
|
//# sourceMappingURL=DocsRenderer-EYKKDMVH-b61c696a.js.map
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"mappings":"qcAIG,IAACA,EAAkB,CAAC,KAAKC,EAAgB,EAAEC,EAAU,GAAGC,CAAU,EAAEC,EAAc,cAAcC,WAAS,CAAC,aAAa,CAAC,MAAM,GAAG,SAAS,EAAE,KAAK,MAAM,CAAC,SAAS,EAAE,CAAE,CAAC,OAAO,0BAA0B,CAAC,MAAO,CAAC,SAAS,EAAE,CAAC,CAAC,kBAAkBC,EAAI,CAAC,GAAG,CAAC,cAAAC,CAAa,EAAE,KAAK,MAAMA,EAAcD,CAAG,CAAE,CAAC,QAAQ,CAAC,GAAG,CAAC,SAAAE,CAAQ,EAAE,KAAK,MAAM,CAAC,SAAAC,CAAQ,EAAE,KAAK,MAAM,OAAOD,EAAS,KAAKC,CAAQ,CAAC,EAAEC,EAAa,KAAK,CAAC,aAAa,CAAC,KAAK,OAAO,MAAMC,EAAQC,EAAcC,IAAU,CAAC,IAAIC,EAAW,CAAC,GAAGd,EAAkB,GAAGY,GAAA,YAAAA,EAAe,UAAU,EAAE,OAAO,IAAI,QAAQ,CAACG,EAAQC,IAAS,CAAAC,EAAA,IAAC,OAAO,qBAAe,EAAC,sHAAC,KAAK,CAAC,CAAC,YAAAC,CAAW,IAAIC,EAAcC,EAAM,cAAchB,EAAc,CAAC,cAAcY,EAAO,IAAI,KAAK,OAAM,CAAE,EAAEI,EAAM,cAAcF,EAAY,CAAC,WAAAJ,CAAU,EAAEM,EAAM,cAAcC,EAAK,CAAC,QAAAV,EAAQ,cAAAC,CAAa,CAAC,CAAC,CAAC,EAAEC,CAAO,CAAC,EAAE,KAAKE,CAAO,CAAE,CAAC,CAAC,EAAE,KAAK,QAAQF,GAAS,CAACS,EAAeT,CAAO,CAAE,CAAE,CAAC","names":["defaultComponents","CodeOrSourceMdx","AnchorMdx","HeadersMdx","ErrorBoundary","Component","err","showException","hasError","children","DocsRenderer","context","docsParameter","element","components","resolve","reject","__vitePreload","MDXProvider","renderElement","React","Docs","unmountElement"],"sources":["../../node_modules/@storybook/addon-docs/dist/chunk-PCJTTTQV.mjs"],"sourcesContent":["import React, { Component } from 'react';\nimport { renderElement, unmountElement } from '@storybook/react-dom-shim';\nimport { CodeOrSourceMdx, AnchorMdx, HeadersMdx, Docs } from '@storybook/blocks';\n\nvar defaultComponents={code:CodeOrSourceMdx,a:AnchorMdx,...HeadersMdx},ErrorBoundary=class extends Component{constructor(){super(...arguments);this.state={hasError:!1};}static getDerivedStateFromError(){return {hasError:!0}}componentDidCatch(err){let{showException}=this.props;showException(err);}render(){let{hasError}=this.state,{children}=this.props;return hasError?null:children}},DocsRenderer=class{constructor(){this.render=async(context,docsParameter,element)=>{let components={...defaultComponents,...docsParameter?.components};return new Promise((resolve,reject)=>{import('@mdx-js/react').then(({MDXProvider})=>renderElement(React.createElement(ErrorBoundary,{showException:reject,key:Math.random()},React.createElement(MDXProvider,{components},React.createElement(Docs,{context,docsParameter}))),element)).then(resolve);})},this.unmount=element=>{unmountElement(element);};}};\n\nexport { DocsRenderer, defaultComponents };\n"],"file":"assets/DocsRenderer-EYKKDMVH-b61c696a.js"}
|
||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1,6 @@
|
|||||||
|
import{j as o}from"./jsx-runtime-94f6e698.js";import{M as i}from"./index-89936ab1.js";import{u as s}from"./index-1d576ef5.js";import"./index-8db94870.js";import"./_commonjsHelpers-042e6b4d.js";import"./iframe-1eda5ccb.js";import"../sb-preview/runtime.js";import"./index-d475d2ea.js";import"./isNativeReflectConstruct-099dc9ad.js";import"./index-8ce4a492.js";import"./index-d37d4223.js";import"./index-6e6be2d5.js";import"./index-356e4a49.js";function M(t={}){const{wrapper:n}=Object.assign({},s(),t.components);return n?o.jsx(n,Object.assign({},t,{children:o.jsx(e,{})})):e();function e(){const r=Object.assign({h1:"h1",p:"p",strong:"strong",a:"a",br:"br"},s(),t.components);return o.jsxs(o.Fragment,{children:[o.jsx(i,{title:"Components/Introduction"}),`
|
||||||
|
`,o.jsx(r.h1,{id:"welcome-to-the-portaljs-components-guide",children:"Welcome to the PortalJS components guide"}),`
|
||||||
|
`,o.jsxs(r.p,{children:[o.jsx(r.strong,{children:"Official Website:"})," ",o.jsx(r.a,{href:"https://portaljs.org",target:"_blank",rel:"nofollow noopener noreferrer",children:"portaljs.org"}),o.jsx(r.br,{}),`
|
||||||
|
`,o.jsx(r.strong,{children:"Docs:"})," ",o.jsx(r.a,{href:"https://portaljs.org/docs",target:"_blank",rel:"nofollow noopener noreferrer",children:"portaljs.org/docs"}),o.jsx(r.br,{}),`
|
||||||
|
`,o.jsx(r.strong,{children:"GitHub:"})," ",o.jsx(r.a,{href:"https://github.com/datopian/portaljs",target:"_blank",rel:"nofollow noopener noreferrer",children:"github.com/datopian/portaljs"})]})]})}}export{M as default};
|
||||||
|
//# sourceMappingURL=Introduction-7f71f7c8.js.map
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"file":"Introduction-7f71f7c8.js","sources":["../../stories/Introduction.mdx"],"sourcesContent":["import { Meta } from '@storybook/blocks';\n\n<Meta title=\"Components/Introduction\" />\n\n# Welcome to the PortalJS components guide\n\n**Official Website:** [portaljs.org](https://portaljs.org) \n**Docs:** [portaljs.org/docs](https://portaljs.org/docs) \n**GitHub:** [github.com/datopian/portaljs](https://github.com/datopian/portaljs)"],"names":["MDXContent","props","MDXLayout","_provideComponents","_jsx","_createMdxContent","_components","_jsxs","_Fragment","Meta"],"mappings":"0bAIA,SAASA,EAAWC,EAAQ,GAAI,CAC9B,KAAM,CAAC,QAASC,CAAS,EAAI,OAAO,OAAO,CAAE,EAAEC,EAAoB,EAAEF,EAAM,UAAU,EACrF,OAAOC,EAAYE,EAAAA,IAAKF,EAAW,OAAO,OAAO,CAAE,EAAED,EAAO,CAC1D,SAAUG,EAAAA,IAAKC,EAAmB,EAAE,CACxC,CAAG,CAAC,EAAIA,IACN,SAASA,GAAoB,CAC3B,MAAMC,EAAc,OAAO,OAAO,CAChC,GAAI,KACJ,EAAG,IACH,OAAQ,SACR,EAAG,IACH,GAAI,IACL,EAAEH,EAAoB,EAAEF,EAAM,UAAU,EACzC,OAAOM,EAAAA,KAAMC,EAAAA,SAAW,CACtB,SAAU,CAACJ,EAAI,IAACK,EAAM,CACpB,MAAO,yBACR,CAAA,EAAG;AAAA,EAAML,MAAKE,EAAY,GAAI,CAC7B,GAAI,2CACJ,SAAU,0CACX,CAAA,EAAG;AAAA,EAAMC,OAAMD,EAAY,EAAG,CAC7B,SAAU,CAACF,EAAAA,IAAKE,EAAY,OAAQ,CAClC,SAAU,mBACX,CAAA,EAAG,IAAKF,MAAKE,EAAY,EAAG,CAC3B,KAAM,uBACN,OAAQ,SACR,IAAK,+BACL,SAAU,cACpB,CAAS,EAAGF,EAAAA,IAAKE,EAAY,GAAI,CAAA,CAAE,EAAG;AAAA,EAAMF,EAAAA,IAAKE,EAAY,OAAQ,CAC3D,SAAU,OACX,CAAA,EAAG,IAAKF,MAAKE,EAAY,EAAG,CAC3B,KAAM,4BACN,OAAQ,SACR,IAAK,+BACL,SAAU,mBACpB,CAAS,EAAGF,EAAAA,IAAKE,EAAY,GAAI,CAAA,CAAE,EAAG;AAAA,EAAMF,EAAAA,IAAKE,EAAY,OAAQ,CAC3D,SAAU,SACX,CAAA,EAAG,IAAKF,MAAKE,EAAY,EAAG,CAC3B,KAAM,uCACN,OAAQ,SACR,IAAK,+BACL,SAAU,8BACpB,CAAS,CAAC,CACV,CAAO,CAAC,CACR,CAAK,CACF,CACH"}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
import{j as d}from"./jsx-runtime-94f6e698.js";import{V as c}from"./VegaLite-30eeb950.js";import"./index-8db94870.js";import"./_commonjsHelpers-042e6b4d.js";import"./Vega-d925c94f.js";import"./index-1fc0ca9a.js";function i({data:e=[],fullWidth:n=!1,title:f="",xAxis:x="x",yAxis:g="y"}){var s=e;Array.isArray(e)&&(s=e.map(o=>({x:o[0],y:o[1]})));const v={table:s},r={$schema:"https://vega.github.io/schema/vega-lite/v5.json",title:f,width:"container",height:300,mark:{type:"line",color:"black",strokeWidth:1,tooltip:!0},data:{name:"table"},selection:{grid:{type:"interval",bind:"scales"}},encoding:{x:{field:x,timeUnit:"year",type:"temporal"},y:{field:g,type:"quantitative"}}};return typeof e=="string"?(r.data={url:e},d.jsx(c,{fullWidth:n,spec:r})):d.jsx(c,{fullWidth:n,data:v,spec:r})}try{i.displayName="LineChart",i.__docgenInfo={description:"",displayName:"LineChart",props:{data:{defaultValue:{value:"[]"},description:"",name:"data",required:!1,type:{name:"string | (string | number)[][] | { x: string; y: number; }[]"}},title:{defaultValue:{value:""},description:"",name:"title",required:!1,type:{name:"string"}},xAxis:{defaultValue:{value:"x"},description:"",name:"xAxis",required:!1,type:{name:"string"}},yAxis:{defaultValue:{value:"y"},description:"",name:"yAxis",required:!1,type:{name:"string"}},fullWidth:{defaultValue:{value:"false"},description:"",name:"fullWidth",required:!1,type:{name:"boolean"}}}}}catch{}const w={title:"Components/LineChart",component:i,tags:["autodocs"],argTypes:{data:{description:`Data to be displayed.
|
||||||
|
|
||||||
|
E.g.: [["1990", 1], ["1991", 2]]
|
||||||
|
|
||||||
|
OR
|
||||||
|
|
||||||
|
"https://url.to/data.csv"`},title:{description:"Title to display on the chart. Optional."},xAxis:{description:'Name of the X axis on the data. Required when the "data" parameter is an URL.'},yAxis:{description:'Name of the Y axis on the data. Required when the "data" parameter is an URL.'},fullWidth:{description:"Whether the component should be rendered as full bleed or not"}}},t={name:"Line chart from array of data points",args:{data:[["1850",-.41765878],["1851",-.2333498],["1852",-.22939907],["1853",-.27035445],["1854",-.29163003]]}},a={name:"Line chart from URL",args:{title:"Oil Price x Year",data:"https://raw.githubusercontent.com/datasets/oil-prices/main/data/wti-year.csv",xAxis:"Date",yAxis:"Price"}};var l,p,m;t.parameters={...t.parameters,docs:{...(l=t.parameters)==null?void 0:l.docs,source:{originalSource:`{
|
||||||
|
name: 'Line chart from array of data points',
|
||||||
|
args: {
|
||||||
|
data: [['1850', -0.41765878], ['1851', -0.2333498], ['1852', -0.22939907], ['1853', -0.27035445], ['1854', -0.29163003]]
|
||||||
|
}
|
||||||
|
}`,...(m=(p=t.parameters)==null?void 0:p.docs)==null?void 0:m.source}}};var u,h,y;a.parameters={...a.parameters,docs:{...(u=a.parameters)==null?void 0:u.docs,source:{originalSource:`{
|
||||||
|
name: 'Line chart from URL',
|
||||||
|
args: {
|
||||||
|
title: 'Oil Price x Year',
|
||||||
|
data: 'https://raw.githubusercontent.com/datasets/oil-prices/main/data/wti-year.csv',
|
||||||
|
xAxis: 'Date',
|
||||||
|
yAxis: 'Price'
|
||||||
|
}
|
||||||
|
}`,...(y=(h=a.parameters)==null?void 0:h.docs)==null?void 0:y.source}}};const U=["FromDataPoints","FromURL"];export{t as FromDataPoints,a as FromURL,U as __namedExportsOrder,w as default};
|
||||||
|
//# sourceMappingURL=LineChart.stories-de9527a1.js.map
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"file":"LineChart.stories-de9527a1.js","sources":["../../src/components/LineChart.tsx"],"sourcesContent":["import { VegaLite } from './VegaLite';\n\nexport type LineChartProps = {\n data: Array<Array<string | number>> | string | { x: string; y: number }[];\n title?: string;\n xAxis?: string;\n yAxis?: string;\n fullWidth?: boolean;\n};\n\nexport function LineChart({\n data = [],\n fullWidth = false,\n title = '',\n xAxis = 'x',\n yAxis = 'y',\n}: LineChartProps) {\n var tmp = data;\n if (Array.isArray(data)) {\n tmp = data.map((r) => {\n return { x: r[0], y: r[1] };\n });\n }\n const vegaData = { table: tmp };\n const spec = {\n $schema: 'https://vega.github.io/schema/vega-lite/v5.json',\n title,\n width: 'container',\n height: 300,\n mark: {\n type: 'line',\n color: 'black',\n strokeWidth: 1,\n tooltip: true,\n },\n data: {\n name: 'table',\n },\n selection: {\n grid: {\n type: 'interval',\n bind: 'scales',\n },\n },\n encoding: {\n x: {\n field: xAxis,\n timeUnit: 'year',\n type: 'temporal',\n },\n y: {\n field: yAxis,\n type: 'quantitative',\n },\n },\n };\n if (typeof data === 'string') {\n spec.data = { url: data } as any;\n return <VegaLite fullWidth={fullWidth} spec={spec} />;\n }\n\n return <VegaLite fullWidth={fullWidth} data={vegaData} spec={spec} />;\n}\n"],"names":["LineChart","data","fullWidth","title","xAxis","yAxis","tmp","r","vegaData","spec","jsx","VegaLite"],"mappings":"mNAUO,SAAAA,EAAA,CAAmB,KAAAC,EAAA,CAAA,EAChB,UAAAC,EAAA,GACI,MAAAC,EAAA,GACJ,MAAAC,EAAA,IACA,MAAAC,EAAA,GAEV,EAAA,CACE,IAAAC,EAAAL,EACA,MAAA,QAAAA,CAAA,IACEK,EAAAL,EAAA,IAAAM,IACE,CAAA,EAAAA,EAAA,CAAA,EAAA,EAAAA,EAAA,CAAA,GAA0B,GAG9B,MAAAC,EAAA,CAAA,MAAAF,GACAG,EAAA,CAAa,QAAA,kDACF,MAAAN,EACT,MAAA,YACO,OAAA,IACC,KAAA,CACF,KAAA,OACE,MAAA,QACC,YAAA,EACM,QAAA,EACJ,EACX,KAAA,CACM,KAAA,OACE,EACR,UAAA,CACW,KAAA,CACH,KAAA,WACE,KAAA,QACA,CACR,EACF,SAAA,CACU,EAAA,CACL,MAAAC,EACM,SAAA,OACG,KAAA,UACJ,EACR,EAAA,CACG,MAAAC,EACM,KAAA,cACD,CACR,CACF,EAEF,OAAA,OAAAJ,GAAA,UACEQ,EAAA,KAAA,CAAA,IAAAR,CAAA,EACAS,EAAA,IAAAC,EAAA,CAAA,UAAAT,EAAA,KAAAO,CAAA,CAAA,GAGFC,EAAAA,IAAAC,EAAA,CAAA,UAAAT,EAAA,KAAAM,EAAA,KAAAC,CAAA,CAAA,CACF;;;;;;;;;;;;;;;;;;;"}
|
||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
206
packages/components/storybook-static/assets/Vega-d925c94f.js
Normal file
206
packages/components/storybook-static/assets/Vega-d925c94f.js
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1,32 @@
|
|||||||
|
import{j as i}from"./jsx-runtime-94f6e698.js";import{V as s}from"./Vega-d925c94f.js";import"./index-8db94870.js";import"./_commonjsHelpers-042e6b4d.js";import"./index-1fc0ca9a.js";function e(t){return i.jsx(s,{...t})}try{e.displayName="Vega",e.__docgenInfo={description:"",displayName:"Vega",props:{}}}catch{}const l={title:"Components/Vega",component:e,tags:["autodocs"]},a={name:"Chart built with Vega",args:{data:{table:[{y:-.418,x:1850},{y:.923,x:2020}]},spec:{$schema:"https://vega.github.io/schema/vega-lite/v4.json",mark:"bar",data:{name:"table"},encoding:{x:{field:"x",type:"ordinal"},y:{field:"y",type:"quantitative"}}}}};var n,r,o;a.parameters={...a.parameters,docs:{...(n=a.parameters)==null?void 0:n.docs,source:{originalSource:`{
|
||||||
|
name: 'Chart built with Vega',
|
||||||
|
args: {
|
||||||
|
data: {
|
||||||
|
table: [{
|
||||||
|
y: -0.418,
|
||||||
|
x: 1850
|
||||||
|
}, {
|
||||||
|
y: 0.923,
|
||||||
|
x: 2020
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
spec: {
|
||||||
|
$schema: 'https://vega.github.io/schema/vega-lite/v4.json',
|
||||||
|
mark: 'bar',
|
||||||
|
data: {
|
||||||
|
name: 'table'
|
||||||
|
},
|
||||||
|
encoding: {
|
||||||
|
x: {
|
||||||
|
field: 'x',
|
||||||
|
type: 'ordinal'
|
||||||
|
},
|
||||||
|
y: {
|
||||||
|
field: 'y',
|
||||||
|
type: 'quantitative'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}`,...(o=(r=a.parameters)==null?void 0:r.docs)==null?void 0:o.source}}};const y=["Primary"];export{a as Primary,y as __namedExportsOrder,l as default};
|
||||||
|
//# sourceMappingURL=Vega.stories-751bd69b.js.map
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"file":"Vega.stories-751bd69b.js","sources":["../../src/components/Vega.tsx"],"sourcesContent":["// Wrapper for the Vega component\nimport { Vega as VegaOg } from \"react-vega\";\n\nexport function Vega(props) {\n return <VegaOg {...props} />;\n}\n"],"names":["Vega","props","jsx","VegaOg"],"mappings":"oLAGO,SAAAA,EAAAC,EAAA,CACL,OAAAC,EAAA,IAAAC,EAAA,CAAA,GAAAF,CAAA,CAAA,CACF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
import{j as p}from"./jsx-runtime-94f6e698.js";import{R as s}from"./index-8db94870.js";import{V as u}from"./Vega-d925c94f.js";function l(){return l=Object.assign||function(e){for(var r=1;r<arguments.length;r++){var a=arguments[r];for(var t in a)Object.prototype.hasOwnProperty.call(a,t)&&(e[t]=a[t])}return e},l.apply(this,arguments)}function c(e){return s.createElement(u,l({},e,{mode:"vega-lite"}))}function o({Component:e,defaultWFull:r=!0}){return a=>{const t={...a};let n=t.className||"";return t.fullWidth===!0?n+=" w-[90vw] ml-[calc(50%-45vw)] max-w-none":r&&(n+=" w-full"),t.className=n,p.jsx(e,{...t})}}try{o.displayName="applyFullWidthDirective",o.__docgenInfo={description:"",displayName:"applyFullWidthDirective",props:{Component:{defaultValue:null,description:"",name:"Component",required:!0,type:{name:"any"}},defaultWFull:{defaultValue:{value:"true"},description:"",name:"defaultWFull",required:!1,type:{name:"boolean"}}}}}catch{}function i(e){const r=o({Component:c});return p.jsx(r,{...e})}try{i.displayName="VegaLite",i.__docgenInfo={description:"",displayName:"VegaLite",props:{}}}catch{}export{i as V};
|
||||||
|
//# sourceMappingURL=VegaLite-30eeb950.js.map
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"file":"VegaLite-30eeb950.js","sources":["../../node_modules/react-vega/esm/VegaLite.js","../../src/lib/applyFullWidthDirective.tsx","../../src/components/VegaLite.tsx"],"sourcesContent":["function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }\n\nimport React from 'react';\nimport Vega from './Vega';\n\n/**\n * Syntactic sugar for using vega-lite with Vega\n * @param props\n */\nexport default function VegaLite(props) {\n return /*#__PURE__*/React.createElement(Vega, _extends({}, props, {\n mode: \"vega-lite\"\n }));\n}","export default function applyFullWidthDirective({\n Component,\n defaultWFull = true,\n}) {\n return (props) => {\n const newProps = { ...props };\n\n let newClassName = newProps.className || \"\";\n if (newProps.fullWidth === true) {\n newClassName += \" w-[90vw] ml-[calc(50%-45vw)] max-w-none\";\n } else if (defaultWFull) {\n // So that charts and tables will have the\n // same width as the text content, but images\n // can have its width set using the width prop\n newClassName += \" w-full\";\n }\n newProps.className = newClassName;\n\n return <Component {...newProps} />;\n };\n}\n","// Wrapper for the Vega Lite component\nimport { VegaLite as VegaLiteOg } from \"react-vega\";\nimport applyFullWidthDirective from \"../lib/applyFullWidthDirective\";\n\nexport function VegaLite(props) {\n const Component = applyFullWidthDirective({ Component: VegaLiteOg });\n\n return <Component {...props} />;\n}\n"],"names":["_extends","target","i","source","key","VegaLite","props","React","Vega","applyFullWidthDirective","Component","defaultWFull","newProps","newClassName","jsx","VegaLiteOg"],"mappings":"6HAAA,SAASA,GAAW,CAAE,OAAAA,EAAW,OAAO,QAAU,SAAUC,EAAQ,CAAE,QAASC,EAAI,EAAGA,EAAI,UAAU,OAAQA,IAAK,CAAE,IAAIC,EAAS,UAAUD,CAAC,EAAG,QAASE,KAAOD,EAAc,OAAO,UAAU,eAAe,KAAKA,EAAQC,CAAG,IAAKH,EAAOG,CAAG,EAAID,EAAOC,CAAG,GAAS,OAAOH,GAAkBD,EAAS,MAAM,KAAM,SAAS,CAAI,CAS9S,SAASK,EAASC,EAAO,CACtC,OAAoBC,EAAM,cAAcC,EAAMR,EAAS,CAAA,EAAIM,EAAO,CAChE,KAAM,WACP,CAAA,CAAC,CACJ,CCbA,SAAAG,EAAA,CAAgD,UAAAC,EAC9C,aAAAC,EAAA,EAEF,EAAA,CACE,OAAAL,GAAA,CACE,MAAAM,EAAA,CAAA,GAAAN,GAEA,IAAAO,EAAAD,EAAA,WAAA,GACA,OAAAA,EAAA,YAAA,GACEC,GAAA,2CAAgBF,IAKhBE,GAAA,WAEFD,EAAA,UAAAC,EAEAC,EAAA,IAAAJ,EAAA,CAAA,GAAAE,CAAA,CAAA,CAAgC,CAEpC,8UChBO,SAAAP,EAAAC,EAAA,CACL,MAAAI,EAAAD,EAAA,CAAA,UAAAM,CAAA,CAAA,EAEA,OAAAD,EAAA,IAAAJ,EAAA,CAAA,GAAAJ,CAAA,CAAA,CACF","x_google_ignoreList":[0]}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
import{V as i}from"./VegaLite-30eeb950.js";import"./jsx-runtime-94f6e698.js";import"./index-8db94870.js";import"./_commonjsHelpers-042e6b4d.js";import"./Vega-d925c94f.js";import"./index-1fc0ca9a.js";const c={title:"Components/VegaLite",component:i,tags:["autodocs"],argTypes:{data:{description:"Data to be used by Vega Lite. See the Vega Lite docs: https://vega.github.io/vega-lite/docs/data.html."},spec:{description:"Spec to be used by Vega Lite. See the Vega Lite docs: https://vega.github.io/vega-lite/docs/spec.html."}}},e={name:"Chart built with Vega Lite",args:{data:{table:[{y:-.418,x:1850},{y:.923,x:2020}]},spec:{$schema:"https://vega.github.io/schema/vega-lite/v4.json",mark:"bar",data:{name:"table"},encoding:{x:{field:"x",type:"ordinal"},y:{field:"y",type:"quantitative"}}}}};var t,a,n;e.parameters={...e.parameters,docs:{...(t=e.parameters)==null?void 0:t.docs,source:{originalSource:`{
|
||||||
|
name: 'Chart built with Vega Lite',
|
||||||
|
args: {
|
||||||
|
data: {
|
||||||
|
table: [{
|
||||||
|
y: -0.418,
|
||||||
|
x: 1850
|
||||||
|
}, {
|
||||||
|
y: 0.923,
|
||||||
|
x: 2020
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
spec: {
|
||||||
|
$schema: 'https://vega.github.io/schema/vega-lite/v4.json',
|
||||||
|
mark: 'bar',
|
||||||
|
data: {
|
||||||
|
name: 'table'
|
||||||
|
},
|
||||||
|
encoding: {
|
||||||
|
x: {
|
||||||
|
field: 'x',
|
||||||
|
type: 'ordinal'
|
||||||
|
},
|
||||||
|
y: {
|
||||||
|
field: 'y',
|
||||||
|
type: 'quantitative'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}`,...(n=(a=e.parameters)==null?void 0:a.docs)==null?void 0:n.source}}};const g=["Primary"];export{e as Primary,g as __namedExportsOrder,c as default};
|
||||||
|
//# sourceMappingURL=VegaLite.stories-a09b0c5c.js.map
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"file":"VegaLite.stories-a09b0c5c.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
import{b as W,b,d}from"./index-89936ab1.js";import"./iframe-1eda5ccb.js";import"../sb-preview/runtime.js";import"./index-8db94870.js";import"./_commonjsHelpers-042e6b4d.js";import"./index-d475d2ea.js";import"./isNativeReflectConstruct-099dc9ad.js";import"./index-8ce4a492.js";import"./index-d37d4223.js";import"./index-6e6be2d5.js";import"./index-356e4a49.js";export{W as WithToolTipState,b as WithTooltip,d as WithTooltipPure};
|
||||||
|
//# sourceMappingURL=WithTooltip-FBT32F6Q-518acee3.js.map
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"file":"WithTooltip-FBT32F6Q-518acee3.js","sources":[],"sourcesContent":[],"names":[],"mappings":""}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
var f=typeof globalThis<"u"?globalThis:typeof window<"u"?window:typeof global<"u"?global:typeof self<"u"?self:{};function l(e){return e&&e.__esModule&&Object.prototype.hasOwnProperty.call(e,"default")?e.default:e}function a(e){if(e.__esModule)return e;var r=e.default;if(typeof r=="function"){var o=function n(){if(this instanceof n){var t=[null];t.push.apply(t,arguments);var u=Function.bind.apply(r,t);return new u}return r.apply(this,arguments)};o.prototype=r.prototype}else o={};return Object.defineProperty(o,"__esModule",{value:!0}),Object.keys(e).forEach(function(n){var t=Object.getOwnPropertyDescriptor(e,n);Object.defineProperty(o,n,t.get?t:{enumerable:!0,get:function(){return e[n]}})}),o}export{a,f as c,l as g};
|
||||||
|
//# sourceMappingURL=_commonjsHelpers-042e6b4d.js.map
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"file":"_commonjsHelpers-042e6b4d.js","sources":[],"sourcesContent":[],"names":[],"mappings":""}
|
||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1,2 @@
|
|||||||
|
import{M as r,a,u as m,w as n}from"./index-1d576ef5.js";import"./index-8db94870.js";import"./_commonjsHelpers-042e6b4d.js";export{r as MDXContext,a as MDXProvider,m as useMDXComponents,n as withMDXComponents};
|
||||||
|
//# sourceMappingURL=index-186b3228.js.map
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"file":"index-186b3228.js","sources":[],"sourcesContent":[],"names":[],"mappings":""}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
import{R as o}from"./index-8db94870.js";const u=o.createContext({});function c(t){return e;function e(r){const n=a(r.components);return o.createElement(t,{...r,allComponents:n})}}function a(t){const e=o.useContext(u);return o.useMemo(()=>typeof t=="function"?t(e):{...e,...t},[e,t])}const i={};function f({components:t,children:e,disableParentContext:r}){let n;return r?n=typeof t=="function"?t({}):t||i:n=a(t),o.createElement(u.Provider,{value:n},e)}export{u as M,f as a,a as u,c as w};
|
||||||
|
//# sourceMappingURL=index-1d576ef5.js.map
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"file":"index-1d576ef5.js","sources":["../../node_modules/@mdx-js/react/lib/index.js"],"sourcesContent":["/**\n * @typedef {import('react').ReactNode} ReactNode\n * @typedef {import('mdx/types.js').MDXComponents} Components\n *\n * @typedef Props\n * Configuration.\n * @property {Components | MergeComponents | null | undefined} [components]\n * Mapping of names for JSX components to React components.\n * @property {boolean | null | undefined} [disableParentContext=false]\n * Turn off outer component context.\n * @property {ReactNode | null | undefined} [children]\n * Children.\n *\n * @callback MergeComponents\n * Custom merge function.\n * @param {Components} currentComponents\n * Current components from the context.\n * @returns {Components}\n * Merged components.\n */\n\nimport React from 'react'\n\n/**\n * @type {import('react').Context<Components>}\n * @deprecated\n * This export is marked as a legacy feature.\n * That means it’s no longer recommended for use as it might be removed\n * in a future major release.\n *\n * Please use `useMDXComponents` to get context based components and\n * `MDXProvider` to set context based components instead.\n */\nexport const MDXContext = React.createContext({})\n\n/**\n * @param {import('react').ComponentType<any>} Component\n * @deprecated\n * This export is marked as a legacy feature.\n * That means it’s no longer recommended for use as it might be removed\n * in a future major release.\n *\n * Please use `useMDXComponents` to get context based components instead.\n */\nexport function withMDXComponents(Component) {\n return boundMDXComponent\n\n /**\n * @param {Record<string, unknown> & {components?: Components | null | undefined}} props\n * @returns {JSX.Element}\n */\n function boundMDXComponent(props) {\n const allComponents = useMDXComponents(props.components)\n return React.createElement(Component, {...props, allComponents})\n }\n}\n\n/**\n * Get current components from the MDX Context.\n *\n * @param {Components | MergeComponents | null | undefined} [components]\n * Additional components to use or a function that takes the current\n * components and filters/merges/changes them.\n * @returns {Components}\n * Current components.\n */\nexport function useMDXComponents(components) {\n const contextComponents = React.useContext(MDXContext)\n\n // Memoize to avoid unnecessary top-level context changes\n return React.useMemo(() => {\n // Custom merge via a function prop\n if (typeof components === 'function') {\n return components(contextComponents)\n }\n\n return {...contextComponents, ...components}\n }, [contextComponents, components])\n}\n\n/** @type {Components} */\nconst emptyObject = {}\n\n/**\n * Provider for MDX context\n *\n * @param {Props} props\n * @returns {JSX.Element}\n */\nexport function MDXProvider({components, children, disableParentContext}) {\n /** @type {Components} */\n let allComponents\n\n if (disableParentContext) {\n allComponents =\n typeof components === 'function'\n ? components({})\n : components || emptyObject\n } else {\n allComponents = useMDXComponents(components)\n }\n\n return React.createElement(\n MDXContext.Provider,\n {value: allComponents},\n children\n )\n}\n"],"names":["MDXContext","React","withMDXComponents","Component","boundMDXComponent","props","allComponents","useMDXComponents","components","contextComponents","emptyObject","MDXProvider","children","disableParentContext"],"mappings":"wCAiCY,MAACA,EAAaC,EAAM,cAAc,EAAE,EAWzC,SAASC,EAAkBC,EAAW,CAC3C,OAAOC,EAMP,SAASA,EAAkBC,EAAO,CAChC,MAAMC,EAAgBC,EAAiBF,EAAM,UAAU,EACvD,OAAOJ,EAAM,cAAcE,EAAW,CAAC,GAAGE,EAAO,cAAAC,CAAa,CAAC,CAChE,CACH,CAWO,SAASC,EAAiBC,EAAY,CAC3C,MAAMC,EAAoBR,EAAM,WAAWD,CAAU,EAGrD,OAAOC,EAAM,QAAQ,IAEf,OAAOO,GAAe,WACjBA,EAAWC,CAAiB,EAG9B,CAAC,GAAGA,EAAmB,GAAGD,CAAU,EAC1C,CAACC,EAAmBD,CAAU,CAAC,CACpC,CAGA,MAAME,EAAc,CAAE,EAQf,SAASC,EAAY,CAAC,WAAAH,EAAY,SAAAI,EAAU,qBAAAC,CAAoB,EAAG,CAExE,IAAIP,EAEJ,OAAIO,EACFP,EACE,OAAOE,GAAe,WAClBA,EAAW,CAAA,CAAE,EACbA,GAAcE,EAEpBJ,EAAgBC,EAAiBC,CAAU,EAGtCP,EAAM,cACXD,EAAW,SACX,CAAC,MAAOM,CAAa,EACrBM,CACD,CACH","x_google_ignoreList":[0]}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
import{g as c}from"./_commonjsHelpers-042e6b4d.js";var p={exports:{}},i="SECRET_DO_NOT_PASS_THIS_OR_YOU_WILL_BE_FIRED",y=i,m=y;function n(){}function a(){}a.resetWarningCache=n;var T=function(){function e(f,h,l,P,g,s){if(s!==m){var o=new Error("Calling PropTypes validators directly is not supported by the `prop-types` package. Use PropTypes.checkPropTypes() to call them. Read more at http://fb.me/use-check-prop-types");throw o.name="Invariant Violation",o}}e.isRequired=e;function r(){return e}var t={array:e,bigint:e,bool:e,func:e,number:e,object:e,string:e,symbol:e,any:e,arrayOf:r,element:e,elementType:e,instanceOf:r,node:e,objectOf:r,oneOf:r,oneOfType:r,shape:r,exact:r,checkPropTypes:a,resetWarningCache:n};return t.PropTypes=t,t};p.exports=T();var u=p.exports;const R=c(u);export{R as P};
|
||||||
|
//# sourceMappingURL=index-1fc0ca9a.js.map
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"file":"index-1fc0ca9a.js","sources":["../../node_modules/prop-types/lib/ReactPropTypesSecret.js","../../node_modules/prop-types/factoryWithThrowingShims.js","../../node_modules/prop-types/index.js"],"sourcesContent":["/**\n * Copyright (c) 2013-present, Facebook, Inc.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n'use strict';\n\nvar ReactPropTypesSecret = 'SECRET_DO_NOT_PASS_THIS_OR_YOU_WILL_BE_FIRED';\n\nmodule.exports = ReactPropTypesSecret;\n","/**\n * Copyright (c) 2013-present, Facebook, Inc.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n'use strict';\n\nvar ReactPropTypesSecret = require('./lib/ReactPropTypesSecret');\n\nfunction emptyFunction() {}\nfunction emptyFunctionWithReset() {}\nemptyFunctionWithReset.resetWarningCache = emptyFunction;\n\nmodule.exports = function() {\n function shim(props, propName, componentName, location, propFullName, secret) {\n if (secret === ReactPropTypesSecret) {\n // It is still safe when called from React.\n return;\n }\n var err = new Error(\n 'Calling PropTypes validators directly is not supported by the `prop-types` package. ' +\n 'Use PropTypes.checkPropTypes() to call them. ' +\n 'Read more at http://fb.me/use-check-prop-types'\n );\n err.name = 'Invariant Violation';\n throw err;\n };\n shim.isRequired = shim;\n function getShim() {\n return shim;\n };\n // Important!\n // Keep this list in sync with production version in `./factoryWithTypeCheckers.js`.\n var ReactPropTypes = {\n array: shim,\n bigint: shim,\n bool: shim,\n func: shim,\n number: shim,\n object: shim,\n string: shim,\n symbol: shim,\n\n any: shim,\n arrayOf: getShim,\n element: shim,\n elementType: shim,\n instanceOf: getShim,\n node: shim,\n objectOf: getShim,\n oneOf: getShim,\n oneOfType: getShim,\n shape: getShim,\n exact: getShim,\n\n checkPropTypes: emptyFunctionWithReset,\n resetWarningCache: emptyFunction\n };\n\n ReactPropTypes.PropTypes = ReactPropTypes;\n\n return ReactPropTypes;\n};\n","/**\n * Copyright (c) 2013-present, Facebook, Inc.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\nif (process.env.NODE_ENV !== 'production') {\n var ReactIs = require('react-is');\n\n // By explicitly using `prop-types` you are opting into new development behavior.\n // http://fb.me/prop-types-in-prod\n var throwOnDirectAccess = true;\n module.exports = require('./factoryWithTypeCheckers')(ReactIs.isElement, throwOnDirectAccess);\n} else {\n // By explicitly using `prop-types` you are opting into new production behavior.\n // http://fb.me/prop-types-in-prod\n module.exports = require('./factoryWithThrowingShims')();\n}\n"],"names":["ReactPropTypesSecret","ReactPropTypesSecret_1","require$$0","emptyFunction","emptyFunctionWithReset","factoryWithThrowingShims","shim","props","propName","componentName","location","propFullName","secret","err","getShim","ReactPropTypes","propTypesModule"],"mappings":"sEASIA,EAAuB,+CAE3BC,EAAiBD,ECFbA,EAAuBE,EAE3B,SAASC,GAAgB,CAAE,CAC3B,SAASC,GAAyB,CAAE,CACpCA,EAAuB,kBAAoBD,EAE3C,IAAAE,EAAiB,UAAW,CAC1B,SAASC,EAAKC,EAAOC,EAAUC,EAAeC,EAAUC,EAAcC,EAAQ,CAC5E,GAAIA,IAAWZ,EAIf,KAAIa,EAAM,IAAI,MACZ,iLAGN,EACI,MAAAA,EAAI,KAAO,sBACLA,EACV,CACEP,EAAK,WAAaA,EAClB,SAASQ,GAAU,CACjB,OAAOR,CAEX,CAEE,IAAIS,EAAiB,CACnB,MAAOT,EACP,OAAQA,EACR,KAAMA,EACN,KAAMA,EACN,OAAQA,EACR,OAAQA,EACR,OAAQA,EACR,OAAQA,EAER,IAAKA,EACL,QAASQ,EACT,QAASR,EACT,YAAaA,EACb,WAAYQ,EACZ,KAAMR,EACN,SAAUQ,EACV,MAAOA,EACP,UAAWA,EACX,MAAOA,EACP,MAAOA,EAEP,eAAgBV,EAChB,kBAAmBD,CACvB,EAEE,OAAAY,EAAe,UAAYA,EAEpBA,CACT,EC/CEC,EAAc,QAAGd","x_google_ignoreList":[0,1,2]}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user