[#814,tutorial,docs][xl]: add initial version of the second tutorial, rename basic-example to learn-example, clean up learn-example
This commit is contained in:
36
examples/learn-example/.gitignore
vendored
Normal file
36
examples/learn-example/.gitignore
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
/.pnp
|
||||
.pnp.js
|
||||
|
||||
# testing
|
||||
/coverage
|
||||
|
||||
# next.js
|
||||
/.next/
|
||||
/out/
|
||||
|
||||
# production
|
||||
/build
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
*.pem
|
||||
|
||||
# debug
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
.pnpm-debug.log*
|
||||
|
||||
# local env files
|
||||
.env*.local
|
||||
|
||||
# vercel
|
||||
.vercel
|
||||
|
||||
# typescript
|
||||
*.tsbuildinfo
|
||||
next-env.d.ts
|
||||
1
examples/learn-example/README.md
Normal file
1
examples/learn-example/README.md
Normal file
@@ -0,0 +1 @@
|
||||
PortalJS Learn Example - https://portaljs.org/docs
|
||||
21
examples/learn-example/components/DRD.tsx
Normal file
21
examples/learn-example/components/DRD.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('./Table')),
|
||||
mermaid: Mermaid,
|
||||
// Excel: dynamic(() => import('../components/Excel')),
|
||||
// TODO: try and make these dynamic ...
|
||||
Vega: dynamic(() => import('./Vega')),
|
||||
VegaLite: dynamic(() => import('./VegaLite')),
|
||||
LineChart: dynamic(() => import('./LineChart')),
|
||||
} as any;
|
||||
|
||||
export default function DRD({ source }: { source: any }) {
|
||||
return <MDXRemote {...source} components={components} />;
|
||||
}
|
||||
32
examples/learn-example/components/DebouncedInput.tsx
Normal file
32
examples/learn-example/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;
|
||||
55
examples/learn-example/components/LineChart.tsx
Normal file
55
examples/learn-example/components/LineChart.tsx
Normal file
@@ -0,0 +1,55 @@
|
||||
import VegaLite from "./VegaLite";
|
||||
|
||||
export default function LineChart({
|
||||
data = [],
|
||||
fullWidth = false,
|
||||
title = "",
|
||||
xAxis = "x",
|
||||
yAxis = "y",
|
||||
}) {
|
||||
var tmp = data;
|
||||
if (Array.isArray(data)) {
|
||||
tmp = data.map((r, i) => {
|
||||
return { x: r[0], y: r[1] };
|
||||
});
|
||||
}
|
||||
const vegaData = { table: tmp };
|
||||
const spec = {
|
||||
$schema: "https://vega.github.io/schema/vega-lite/v5.json",
|
||||
title,
|
||||
width: "container",
|
||||
height: 300,
|
||||
mark: {
|
||||
type: "line",
|
||||
color: "black",
|
||||
strokeWidth: 1,
|
||||
tooltip: true,
|
||||
},
|
||||
data: {
|
||||
name: "table",
|
||||
},
|
||||
selection: {
|
||||
grid: {
|
||||
type: "interval",
|
||||
bind: "scales",
|
||||
},
|
||||
},
|
||||
encoding: {
|
||||
x: {
|
||||
field: xAxis,
|
||||
timeUnit: "year",
|
||||
type: "temporal",
|
||||
},
|
||||
y: {
|
||||
field: yAxis,
|
||||
type: "quantitative",
|
||||
},
|
||||
},
|
||||
};
|
||||
if (typeof data === 'string') {
|
||||
spec.data = { "url": data } as any
|
||||
return <VegaLite fullWidth={fullWidth} spec={spec} />;
|
||||
}
|
||||
|
||||
return <VegaLite fullWidth={fullWidth} data={vegaData} spec={spec} />;
|
||||
}
|
||||
189
examples/learn-example/components/Table.tsx
Normal file
189
examples/learn-example/components/Table.tsx
Normal file
@@ -0,0 +1,189 @@
|
||||
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";
|
||||
|
||||
const Table = ({
|
||||
data: ogData = [],
|
||||
cols: ogCols = [],
|
||||
csv = "",
|
||||
url = "",
|
||||
fullWidth = false,
|
||||
}) => {
|
||||
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(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) => setGlobalFilter(String(value))}
|
||||
className="p-2 text-sm shadow border border-block"
|
||||
placeholder="Search all columns..."
|
||||
/>
|
||||
<table>
|
||||
<thead>
|
||||
{table.getHeaderGroups().map((hg) => (
|
||||
<tr key={hg.id}>
|
||||
{hg.headers.map((h) => (
|
||||
<th key={h.id}>
|
||||
<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}>
|
||||
{r.getVisibleCells().map((c) => (
|
||||
<td key={c.id}>
|
||||
{flexRender(c.column.columnDef.cell, c.getContext())}
|
||||
</td>
|
||||
))}
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
<div className="flex gap-2 items-center justify-center">
|
||||
<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);
|
||||
};
|
||||
|
||||
export default Table;
|
||||
6
examples/learn-example/components/Vega.tsx
Normal file
6
examples/learn-example/components/Vega.tsx
Normal file
@@ -0,0 +1,6 @@
|
||||
// Wrapper for the Vega component
|
||||
import { Vega as VegaOg } from "react-vega";
|
||||
|
||||
export default function Vega(props) {
|
||||
return <VegaOg {...props} />;
|
||||
}
|
||||
9
examples/learn-example/components/VegaLite.tsx
Normal file
9
examples/learn-example/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 default function VegaLite(props) {
|
||||
const Component = applyFullWidthDirective({ Component: VegaLiteOg });
|
||||
|
||||
return <Component {...props} />;
|
||||
}
|
||||
7
examples/learn-example/content/index.md
Normal file
7
examples/learn-example/content/index.md
Normal file
@@ -0,0 +1,7 @@
|
||||
# My Dataset
|
||||
|
||||
Built with PortalJS
|
||||
|
||||
## Table
|
||||
|
||||
<Table url="data.csv" />
|
||||
21
examples/learn-example/lib/applyFullWidthDirective.tsx
Normal file
21
examples/learn-example/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-example/lib/loadData.tsx
Normal file
5
examples/learn-example/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
|
||||
}
|
||||
105
examples/learn-example/lib/markdown.js
Normal file
105
examples/learn-example/lib/markdown.js
Normal file
@@ -0,0 +1,105 @@
|
||||
import matter from "gray-matter";
|
||||
import mdxmermaid from "mdx-mermaid";
|
||||
import { h } from "hastscript";
|
||||
import remarkCallouts from "@flowershow/remark-callouts";
|
||||
import remarkEmbed from "@flowershow/remark-embed";
|
||||
import remarkGfm from "remark-gfm";
|
||||
import remarkMath from "remark-math";
|
||||
import remarkSmartypants from "remark-smartypants";
|
||||
import remarkToc from "remark-toc";
|
||||
import remarkWikiLink from "@flowershow/remark-wiki-link";
|
||||
import rehypeAutolinkHeadings from "rehype-autolink-headings";
|
||||
import rehypeKatex from "rehype-katex";
|
||||
import rehypeSlug from "rehype-slug";
|
||||
import rehypePrismPlus from "rehype-prism-plus";
|
||||
|
||||
import { serialize } from "next-mdx-remote/serialize";
|
||||
|
||||
/**
|
||||
* Parse a markdown or MDX file to an MDX source form + front matter data
|
||||
*
|
||||
* @source: the contents of a markdown or mdx file
|
||||
* @format: used to indicate to next-mdx-remote which format to use (md or mdx)
|
||||
* @returns: { mdxSource: mdxSource, frontMatter: ...}
|
||||
*/
|
||||
const parse = async function (source, format) {
|
||||
const { content, data, excerpt } = matter(source, {
|
||||
excerpt: (file, options) => {
|
||||
// Generate an excerpt for the file
|
||||
file.excerpt = file.content.split("\n\n")[0];
|
||||
},
|
||||
});
|
||||
|
||||
const mdxSource = await serialize(
|
||||
{ value: content, path: format },
|
||||
{
|
||||
// Optionally pass remark/rehype plugins
|
||||
mdxOptions: {
|
||||
remarkPlugins: [
|
||||
remarkEmbed,
|
||||
remarkGfm,
|
||||
[remarkSmartypants, { quotes: false, dashes: "oldschool" }],
|
||||
remarkMath,
|
||||
remarkCallouts,
|
||||
remarkWikiLink,
|
||||
[
|
||||
remarkToc,
|
||||
{
|
||||
heading: "Table of contents",
|
||||
tight: true,
|
||||
},
|
||||
],
|
||||
[mdxmermaid, {}],
|
||||
],
|
||||
rehypePlugins: [
|
||||
rehypeSlug,
|
||||
[
|
||||
rehypeAutolinkHeadings,
|
||||
{
|
||||
properties: { className: 'heading-link' },
|
||||
test(element) {
|
||||
return (
|
||||
["h2", "h3", "h4", "h5", "h6"].includes(element.tagName) &&
|
||||
element.properties?.id !== "table-of-contents" &&
|
||||
element.properties?.className !== "blockquote-heading"
|
||||
);
|
||||
},
|
||||
content() {
|
||||
return [
|
||||
h(
|
||||
"svg",
|
||||
{
|
||||
xmlns: "http:www.w3.org/2000/svg",
|
||||
fill: "#ab2b65",
|
||||
viewBox: "0 0 20 20",
|
||||
className: "w-5 h-5",
|
||||
},
|
||||
[
|
||||
h("path", {
|
||||
fillRule: "evenodd",
|
||||
clipRule: "evenodd",
|
||||
d: "M9.493 2.853a.75.75 0 00-1.486-.205L7.545 6H4.198a.75.75 0 000 1.5h3.14l-.69 5H3.302a.75.75 0 000 1.5h3.14l-.435 3.148a.75.75 0 001.486.205L7.955 14h2.986l-.434 3.148a.75.75 0 001.486.205L12.456 14h3.346a.75.75 0 000-1.5h-3.14l.69-5h3.346a.75.75 0 000-1.5h-3.14l.435-3.147a.75.75 0 00-1.486-.205L12.045 6H9.059l.434-3.147zM8.852 7.5l-.69 5h2.986l.69-5H8.852z",
|
||||
}),
|
||||
]
|
||||
),
|
||||
];
|
||||
},
|
||||
},
|
||||
],
|
||||
[rehypeKatex, { output: "mathml" }],
|
||||
[rehypePrismPlus, { ignoreMissing: true }],
|
||||
],
|
||||
format,
|
||||
},
|
||||
scope: data,
|
||||
}
|
||||
);
|
||||
|
||||
return {
|
||||
mdxSource: mdxSource,
|
||||
frontMatter: data,
|
||||
excerpt,
|
||||
};
|
||||
};
|
||||
|
||||
export default parse;
|
||||
16
examples/learn-example/lib/parseCsv.ts
Normal file
16
examples/learn-example/lib/parseCsv.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import papa from "papaparse";
|
||||
|
||||
const parseCsv = (csv) => {
|
||||
csv = csv.trim();
|
||||
const rawdata = papa.parse(csv, { header: true });
|
||||
const cols = rawdata.meta.fields.map((r, i) => {
|
||||
return { key: r, name: r };
|
||||
});
|
||||
|
||||
return {
|
||||
rows: rawdata.data,
|
||||
fields: cols,
|
||||
};
|
||||
};
|
||||
|
||||
export default parseCsv;
|
||||
10959
examples/learn-example/package-lock.json
generated
Normal file
10959
examples/learn-example/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
49
examples/learn-example/package.json
Normal file
49
examples/learn-example/package.json
Normal file
@@ -0,0 +1,49 @@
|
||||
{
|
||||
"name": "basic-example",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "next dev",
|
||||
"build": "next build",
|
||||
"start": "next start",
|
||||
"lint": "next lint"
|
||||
},
|
||||
"dependencies": {
|
||||
"@flowershow/core": "^0.4.10",
|
||||
"@flowershow/remark-callouts": "^1.0.0",
|
||||
"@flowershow/remark-embed": "^1.0.0",
|
||||
"@flowershow/remark-wiki-link": "^1.1.2",
|
||||
"@heroicons/react": "^2.0.17",
|
||||
"@opentelemetry/api": "^1.4.0",
|
||||
"@tanstack/react-table": "^8.8.5",
|
||||
"@types/node": "18.16.0",
|
||||
"@types/react": "18.2.0",
|
||||
"@types/react-dom": "18.2.0",
|
||||
"eslint": "8.39.0",
|
||||
"eslint-config-next": "13.3.1",
|
||||
"gray-matter": "^4.0.3",
|
||||
"hastscript": "^7.2.0",
|
||||
"mdx-mermaid": "2.0.0-rc7",
|
||||
"next": "13.2.1",
|
||||
"next-mdx-remote": "^4.4.1",
|
||||
"papaparse": "^5.4.1",
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0",
|
||||
"react-vega": "^7.6.0",
|
||||
"rehype-autolink-headings": "^6.1.1",
|
||||
"rehype-katex": "^6.0.3",
|
||||
"rehype-prism-plus": "^1.5.1",
|
||||
"rehype-slug": "^5.1.0",
|
||||
"remark-gfm": "^3.0.1",
|
||||
"remark-math": "^5.1.1",
|
||||
"remark-smartypants": "^2.0.0",
|
||||
"remark-toc": "^8.0.1",
|
||||
"typescript": "5.0.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tailwindcss/typography": "^0.5.9",
|
||||
"autoprefixer": "^10.4.14",
|
||||
"postcss": "^8.4.23",
|
||||
"tailwindcss": "^3.3.1"
|
||||
}
|
||||
}
|
||||
46
examples/learn-example/pages/[[...path]].tsx
Normal file
46
examples/learn-example/pages/[[...path]].tsx
Normal file
@@ -0,0 +1,46 @@
|
||||
import { promises as fs } from 'fs';
|
||||
import path from 'path';
|
||||
import parse from '../lib/markdown';
|
||||
import DRD from '../components/DRD';
|
||||
|
||||
export const getServerSideProps = async (context) => {
|
||||
let pathToFile = 'index.md';
|
||||
if (context.params.path) {
|
||||
pathToFile = context.params.path.join('/') + '/index.md';
|
||||
}
|
||||
|
||||
const indexFile = path.join(process.cwd(), '/content/' + pathToFile);
|
||||
const readme = await fs.readFile(indexFile, 'utf8');
|
||||
let { mdxSource, frontMatter } = await parse(readme, '.mdx');
|
||||
return {
|
||||
props: {
|
||||
mdxSource,
|
||||
frontMatter,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export default function DatasetPage({ mdxSource, frontMatter }) {
|
||||
return (
|
||||
<div className="prose mx-auto">
|
||||
<header>
|
||||
<div className="mb-6">
|
||||
<>
|
||||
<h1>{frontMatter.title}</h1>
|
||||
{frontMatter.author && (
|
||||
<div className="-mt-6">
|
||||
<p className="opacity-60 pl-1">{frontMatter.author}</p>
|
||||
</div>
|
||||
)}
|
||||
{frontMatter.description && (
|
||||
<p className="description">{frontMatter.description}</p>
|
||||
)}
|
||||
</>
|
||||
</div>
|
||||
</header>
|
||||
<main>
|
||||
<DRD source={mdxSource} />
|
||||
</main>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
6
examples/learn-example/pages/_app.tsx
Normal file
6
examples/learn-example/pages/_app.tsx
Normal file
@@ -0,0 +1,6 @@
|
||||
import '../styles/globals.css'
|
||||
import type { AppProps } from 'next/app'
|
||||
|
||||
export default function App({ Component, pageProps }: AppProps) {
|
||||
return <Component {...pageProps} />
|
||||
}
|
||||
19
examples/learn-example/pages/_document.tsx
Normal file
19
examples/learn-example/pages/_document.tsx
Normal file
@@ -0,0 +1,19 @@
|
||||
import Document, { Html, Main, Head, NextScript } from 'next/document';
|
||||
|
||||
class MyDocument extends Document {
|
||||
render() {
|
||||
return (
|
||||
<Html>
|
||||
<Head>
|
||||
<link rel="icon" href="/favicon.png" />
|
||||
</Head>
|
||||
<body>
|
||||
<Main />
|
||||
<NextScript />
|
||||
</body>
|
||||
</Html>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default MyDocument;
|
||||
6
examples/learn-example/postcss.config.js
Normal file
6
examples/learn-example/postcss.config.js
Normal file
@@ -0,0 +1,6 @@
|
||||
module.exports = {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
}
|
||||
6
examples/learn-example/public/data.csv
Normal file
6
examples/learn-example/public/data.csv
Normal file
@@ -0,0 +1,6 @@
|
||||
Year,Rating
|
||||
2008,86
|
||||
2009,96
|
||||
2010,100
|
||||
2011,100
|
||||
2012,97
|
||||
|
BIN
examples/learn-example/public/favicon.png
Normal file
BIN
examples/learn-example/public/favicon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 37 KiB |
105
examples/learn-example/styles/globals.css
Normal file
105
examples/learn-example/styles/globals.css
Normal file
@@ -0,0 +1,105 @@
|
||||
@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;
|
||||
}
|
||||
}
|
||||
12
examples/learn-example/tailwind.config.js
Normal file
12
examples/learn-example/tailwind.config.js
Normal file
@@ -0,0 +1,12 @@
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
module.exports = {
|
||||
content: [
|
||||
'./app/**/*.{js,ts,jsx,tsx,mdx}',
|
||||
'./pages/**/*.{js,ts,jsx,tsx,mdx}',
|
||||
'./components/**/*.{js,ts,jsx,tsx,mdx}',
|
||||
],
|
||||
theme: {
|
||||
extend: {},
|
||||
},
|
||||
plugins: [require('@tailwindcss/typography')],
|
||||
};
|
||||
20
examples/learn-example/tsconfig.json
Normal file
20
examples/learn-example/tsconfig.json
Normal file
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"lib": ["dom", "dom.iterable", "esnext"],
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"strict": false,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"noEmit": true,
|
||||
"esModuleInterop": true,
|
||||
"module": "esnext",
|
||||
"moduleResolution": "node",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"jsx": "preserve",
|
||||
"incremental": true
|
||||
},
|
||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", "middleware.ts"],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||
Reference in New Issue
Block a user