Compare commits

...

2 Commits

Author SHA1 Message Date
Luccas Mateus de Medeiros Gomes
7fc199c438 [examples/openspending][sm] - add data stories to header 2023-06-06 11:42:40 -03:00
Luccas Mateus de Medeiros Gomes
f0042b7730 [examples/openspending][lg] - adding drd support 2023-06-06 11:36:26 -03:00
11 changed files with 1149 additions and 129 deletions

View 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} />;
}

View File

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

View 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;
```

File diff suppressed because it is too large Load Diff

View File

@ -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": {

View File

@ -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 } };

View File

@ -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,
}); });
`, `,

View 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,
};
}

View 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();
}),
},
};
};

View File

@ -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;
}
}