[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',
|
||||
href: '/#datasets',
|
||||
},
|
||||
{
|
||||
title: 'Data Stories',
|
||||
href: '/stories',
|
||||
},
|
||||
{
|
||||
title: 'Blog',
|
||||
href: '/blog',
|
||||
@ -157,7 +161,7 @@ function Dropdown({ navItem }: { navItem: any }) {
|
||||
>
|
||||
<div className="py-1">
|
||||
{navItem.children.map((item) => (
|
||||
<Menu.Item>
|
||||
<Menu.Item key={item.href}>
|
||||
{({ active }) => (
|
||||
<a
|
||||
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"
|
||||
},
|
||||
"dependencies": {
|
||||
"@flowershow/core": "^0.4.13",
|
||||
"@flowershow/markdowndb": "^0.1.5",
|
||||
"@heroicons/react": "^2.0.18",
|
||||
"@octokit/plugin-throttling": "^5.2.2",
|
||||
"@portaljs/components": "0.1.4",
|
||||
"@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",
|
||||
"clsx": "^1.2.1",
|
||||
"datapackage": "^1.1.10",
|
||||
"eslint": "8.39.0",
|
||||
"eslint-config-next": "13.3.1",
|
||||
"flexsearch": "0.7.21",
|
||||
"next": "13.3.0",
|
||||
"next-seo": "^6.0.0",
|
||||
"octokit": "^2.0.14",
|
||||
"prettier": "^2.8.8",
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0",
|
||||
"react-hook-form": "^7.43.9",
|
||||
"react-markdown": "^8.0.7",
|
||||
"react-timeago": "^7.1.0",
|
||||
@ -40,6 +28,38 @@
|
||||
"remark": "^14.0.3",
|
||||
"remark-gfm": "^3.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"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@ -53,6 +53,7 @@ export async function getStaticPaths() {
|
||||
|
||||
const paths = allDocuments
|
||||
.filter((page) => page.metadata?.isDraft !== true)
|
||||
.filter((page) => !page.file_path.startsWith('content/stories/'))
|
||||
.map((page) => {
|
||||
const parts = page.url_path!.split('/');
|
||||
return { params: { slug: parts } };
|
||||
|
||||
@ -57,7 +57,7 @@ function CustomApp({ Component, pageProps }: AppProps) {
|
||||
<NextSeo title="OpenSpending" />
|
||||
<Script
|
||||
strategy="afterInteractive"
|
||||
src={`https://www.googletagmanager.com/gtag/js?id=${GA_TOKEN}`}
|
||||
src="https://www.googletagmanager.com/gtag/js?id=G-GXZF7NRXX6"
|
||||
/>
|
||||
<Script
|
||||
id="gtag-init"
|
||||
@ -67,7 +67,7 @@ function CustomApp({ Component, pageProps }: AppProps) {
|
||||
window.dataLayer = window.dataLayer || [];
|
||||
function gtag(){dataLayer.push(arguments);}
|
||||
gtag('js', new Date());
|
||||
gtag('config', ${GA_TOKEN}, {
|
||||
gtag('config', 'G-GXZF7NRXX6', {
|
||||
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 {
|
||||
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