[examples/openspending][lg] - adding drd support (#928)
* [examples/openspending][lg] - adding drd support * [examples/openspending][sm] - add data stories to header
This commit is contained in:
parent
8e4428e2f8
commit
fc70f6ec66
21
examples/openspending/components/DataRichDocument.tsx
Normal file
21
examples/openspending/components/DataRichDocument.tsx
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
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} />;
|
||||||
|
}
|
||||||
@ -23,6 +23,10 @@ export function Header() {
|
|||||||
title: 'Datasets',
|
title: 'Datasets',
|
||||||
href: '/#datasets',
|
href: '/#datasets',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: 'Data Stories',
|
||||||
|
href: '/stories',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: 'Blog',
|
title: 'Blog',
|
||||||
href: '/blog',
|
href: '/blog',
|
||||||
@ -157,7 +161,7 @@ function Dropdown({ navItem }: { navItem: any }) {
|
|||||||
>
|
>
|
||||||
<div className="py-1">
|
<div className="py-1">
|
||||||
{navItem.children.map((item) => (
|
{navItem.children.map((item) => (
|
||||||
<Menu.Item>
|
<Menu.Item key={item.href}>
|
||||||
{({ active }) => (
|
{({ active }) => (
|
||||||
<a
|
<a
|
||||||
key={item.href}
|
key={item.href}
|
||||||
|
|||||||
34
examples/openspending/content/stories/about.mdx
Normal file
34
examples/openspending/content/stories/about.mdx
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
---
|
||||||
|
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;
|
||||||
|
```
|
||||||
943
examples/openspending/package-lock.json
generated
943
examples/openspending/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -12,27 +12,15 @@
|
|||||||
"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",
|
||||||
@ -40,6 +28,38 @@
|
|||||||
"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.7",
|
||||||
|
"@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": {
|
||||||
|
|||||||
@ -53,6 +53,7 @@ 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=${GA_TOKEN}`}
|
src="https://www.googletagmanager.com/gtag/js?id=G-GXZF7NRXX6"
|
||||||
/>
|
/>
|
||||||
<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', ${GA_TOKEN}, {
|
gtag('config', 'G-GXZF7NRXX6', {
|
||||||
page_path: window.location.pathname,
|
page_path: window.location.pathname,
|
||||||
});
|
});
|
||||||
`,
|
`,
|
||||||
|
|||||||
62
examples/openspending/pages/stories/[fileName].tsx
Normal file
62
examples/openspending/pages/stories/[fileName].tsx
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
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,
|
||||||
|
};
|
||||||
|
}
|
||||||
57
examples/openspending/pages/stories/index.tsx
Normal file
57
examples/openspending/pages/stories/index.tsx
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
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,3 +82,109 @@ 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-color-scheme: dark) {
|
||||||
|
html {
|
||||||
|
color-scheme: dark;
|
||||||
|
}
|
||||||
|
body {
|
||||||
|
color: white;
|
||||||
|
background: black;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user