[simple-example][m] - remove unused components
This commit is contained in:
parent
a9940a41fe
commit
a89dfaae38
@ -22,7 +22,7 @@ PROJECT_NAME=<app-name>
|
||||
- This project uses the github api, which for anonymous users will cap at 50 requests per hour, so you might want to get a [Personal Access Token](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token) and add it to your .env file like so
|
||||
|
||||
```
|
||||
PAT_TOKEN=<github token>
|
||||
GITHUB_PAT=<github token>
|
||||
```
|
||||
|
||||
- Edit the file `datasets.json` to your liking, some examples can be found inside this [repo](https://github.com/datasets)
|
||||
|
||||
@ -1,85 +0,0 @@
|
||||
import FrictionlessViewFactory from "./drd/FrictionlessView";
|
||||
import Table from "./drd/Table";
|
||||
|
||||
/* eslint import/no-default-export: off */
|
||||
function DatapackageLayout({ children, project, excerpt }) {
|
||||
const { metadata } = project;
|
||||
|
||||
const title = metadata.title;
|
||||
const resources = metadata.resources;
|
||||
const views = metadata.views;
|
||||
|
||||
const FrictionlessView = FrictionlessViewFactory({ views, resources });
|
||||
|
||||
return (
|
||||
<article className="docs prose text-primary dark:text-primary-dark dark:prose-invert prose-headings:font-headings prose-a:break-words mx-auto p-6">
|
||||
<header>
|
||||
{title && <h1 className="mb-4">{title}</h1>}
|
||||
<a
|
||||
className="font-semibold mb-4"
|
||||
target="_blank"
|
||||
href={project.github_repo}
|
||||
>
|
||||
@{project.owner} / {project.name}
|
||||
</a>
|
||||
{excerpt && <p className="text-md">{excerpt}</p>}
|
||||
</header>
|
||||
<section className="mt-10">
|
||||
{views.map((view, i) => {
|
||||
return (
|
||||
<div key={`visualization-${i}`}>
|
||||
<FrictionlessView viewId={i} />
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</section>
|
||||
<section className="mt-10">
|
||||
<h2>Data files</h2>
|
||||
<table className="table-auto">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>File</th>
|
||||
<th>Title</th>
|
||||
<th>Format</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{resources.map((r) => {
|
||||
return (
|
||||
<tr key={`resources-list-${r.name}`}>
|
||||
<td>
|
||||
<a
|
||||
target="_blank"
|
||||
href={`https://github.com/${project.owner}/${project.name}/blob/main/${r.path}`}
|
||||
>
|
||||
{r.path}
|
||||
</a>
|
||||
</td>
|
||||
<td>{r.title}</td>
|
||||
<td>{r.format.toUpperCase()}</td>
|
||||
</tr>
|
||||
);
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
{resources.slice(0, 5).map((resource) => {
|
||||
return (
|
||||
<div key={`resource-preview-${resource.name}`} className="mt-10">
|
||||
<h3>{resource.title || resource.name || resource.path}</h3>
|
||||
<Table url={resource.path} />
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</section>
|
||||
<hr />
|
||||
<section>
|
||||
<h2>Read me</h2>
|
||||
{children}
|
||||
</section>
|
||||
</article>
|
||||
);
|
||||
}
|
||||
|
||||
export default function MDLayout({ children, layout, ...props }) {
|
||||
return <DatapackageLayout project={props.project} excerpt={props.excerpt}>{children}</DatapackageLayout>;
|
||||
}
|
||||
@ -1,40 +0,0 @@
|
||||
import { MDXRemote } from "next-mdx-remote";
|
||||
import dynamic from "next/dynamic";
|
||||
import { Mermaid } from "@flowershow/core";
|
||||
|
||||
import FrictionlessViewFactory from "./FrictionlessView";
|
||||
|
||||
// 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,
|
||||
frictionless = {
|
||||
views: [],
|
||||
resources: [],
|
||||
},
|
||||
}: {
|
||||
source: any;
|
||||
frictionless?: any;
|
||||
}) {
|
||||
// dynamic() can't be used inside of React rendering
|
||||
// as it needs to be marked in the top level of the
|
||||
// module for preloading to work
|
||||
components.FrictionlessView = FrictionlessViewFactory({
|
||||
views: frictionless.views,
|
||||
resources: frictionless.resources,
|
||||
});
|
||||
|
||||
return <MDXRemote {...source} components={components} />;
|
||||
}
|
||||
@ -1,33 +0,0 @@
|
||||
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;
|
||||
|
||||
@ -1,55 +0,0 @@
|
||||
// FrictionlessView is a factory because we have to
|
||||
// set the views and resources lists before using it
|
||||
|
||||
import { convertSimpleToVegaLite } from "../../lib/viewSpecConversion";
|
||||
import VegaLite from "./VegaLite";
|
||||
|
||||
export default function FrictionlessViewFactory({
|
||||
views = [],
|
||||
resources = [],
|
||||
}): ({
|
||||
viewId,
|
||||
fullWidth,
|
||||
}: {
|
||||
viewId: number;
|
||||
fullWidth?: boolean;
|
||||
}) => JSX.Element {
|
||||
return ({ viewId, fullWidth = false }) => {
|
||||
if (!(viewId in views)) {
|
||||
console.error(`View ${viewId} not found`);
|
||||
return <></>;
|
||||
}
|
||||
const view = views[viewId];
|
||||
|
||||
let resource;
|
||||
if (resources.length > 1) {
|
||||
resource = resources.find((r) => r.name === view.resourceName);
|
||||
} else {
|
||||
resource = resources[0];
|
||||
}
|
||||
|
||||
if (!resource) {
|
||||
console.error(`Resource not found for view id ${viewId}`);
|
||||
return <></>;
|
||||
}
|
||||
|
||||
let vegaSpec;
|
||||
switch (view.specType) {
|
||||
case "simple":
|
||||
vegaSpec = convertSimpleToVegaLite(view, resource);
|
||||
break;
|
||||
// ... other conversions
|
||||
}
|
||||
|
||||
vegaSpec.data = { url: resource.path };
|
||||
|
||||
return (
|
||||
<VegaLite
|
||||
fullWidth={fullWidth}
|
||||
spec={vegaSpec}
|
||||
actions={{ editor: false }}
|
||||
downloadFileName={resource.name}
|
||||
/>
|
||||
);
|
||||
};
|
||||
}
|
||||
@ -1,49 +0,0 @@
|
||||
import { VegaLite } from "react-vega";
|
||||
|
||||
export default function LineChart({
|
||||
data = [],
|
||||
fullWidth = false,
|
||||
title = "",
|
||||
}) {
|
||||
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" as "container",
|
||||
height: 300,
|
||||
mark: {
|
||||
type: "line" as "line",
|
||||
color: "black",
|
||||
strokeWidth: 1,
|
||||
tooltip: true,
|
||||
},
|
||||
data: {
|
||||
name: "table",
|
||||
},
|
||||
selection: {
|
||||
grid: {
|
||||
type: "interval" as "interval",
|
||||
bind: "scales",
|
||||
},
|
||||
},
|
||||
encoding: {
|
||||
x: {
|
||||
field: "x",
|
||||
timeUnit: "year",
|
||||
type: "temporal" as "temporal",
|
||||
},
|
||||
y: {
|
||||
field: "y",
|
||||
type: "quantitative" as "temporal",
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
return <VegaLite data={vegaData} spec={spec} />;
|
||||
}
|
||||
@ -1,188 +0,0 @@
|
||||
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 loadUrlProxied from "../../lib/loadUrlProxied";
|
||||
import parseCsv from "../../lib/parseCsv";
|
||||
import DebouncedInput from "./DebouncedInput";
|
||||
|
||||
const Table = ({
|
||||
data: ogData = [],
|
||||
cols: ogCols = [],
|
||||
csv = "",
|
||||
url = "",
|
||||
}) => {
|
||||
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) {
|
||||
loadUrlProxied(url).then((data) => {
|
||||
const { rows, fields } = parseCsv(data);
|
||||
setData(rows);
|
||||
setCols(fields);
|
||||
});
|
||||
}
|
||||
}, [url]);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<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;
|
||||
@ -1,4 +0,0 @@
|
||||
import { Vega as VegaOg } from "react-vega";
|
||||
export default function Vega(props) {
|
||||
return <VegaOg className="w-full" {...props} />;
|
||||
}
|
||||
@ -1,4 +0,0 @@
|
||||
import { VegaLite as VegaOg } from "react-vega";
|
||||
export default function Vega(props) {
|
||||
return <VegaOg className="w-full" {...props} />;
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user