Compare commits
4 Commits
part-3-tut
...
facets-2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7205aeab77 | ||
|
|
a0620f9255 | ||
|
|
e5513f59a6 | ||
|
|
1782f23b84 |
@@ -1,40 +1,119 @@
|
|||||||
import { Index } from 'flexsearch';
|
import { Index } from 'flexsearch';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import DebouncedInput from './DebouncedInput';
|
import DebouncedInput from './DebouncedInput';
|
||||||
|
import { useForm } from 'react-hook-form';
|
||||||
|
|
||||||
export default function Catalog({ datasets }: { datasets: any[] }) {
|
export default function Catalog({
|
||||||
|
datasets,
|
||||||
|
facets,
|
||||||
|
}: {
|
||||||
|
datasets: any[];
|
||||||
|
facets: string[];
|
||||||
|
}) {
|
||||||
const [indexFilter, setIndexFilter] = useState('');
|
const [indexFilter, setIndexFilter] = useState('');
|
||||||
const index = new Index({ tokenize: "full"});
|
const index = new Index({ tokenize: 'full' });
|
||||||
datasets.forEach((dataset) =>
|
datasets.forEach((dataset) =>
|
||||||
index.add(
|
index.add(
|
||||||
dataset._id,
|
dataset._id,
|
||||||
|
//This will join every metadata value + the url_path into one big string and index that
|
||||||
Object.entries(dataset.metadata).reduce(
|
Object.entries(dataset.metadata).reduce(
|
||||||
(acc, curr) => acc + ' ' + curr.toString(),
|
(acc, curr) => acc + ' ' + curr[1].toString(),
|
||||||
''
|
''
|
||||||
) + ' ' + dataset.url_path
|
) +
|
||||||
|
' ' +
|
||||||
|
dataset.url_path
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
return (
|
|
||||||
<>
|
const facetValues = facets
|
||||||
<DebouncedInput
|
? facets.reduce((acc, facet) => {
|
||||||
value={indexFilter ?? ''}
|
const possibleValues = datasets.reduce((acc, curr) => {
|
||||||
onChange={(value) => setIndexFilter(String(value))}
|
const facetValue = curr.metadata[facet];
|
||||||
className="p-2 text-sm shadow border border-block"
|
if (facetValue) {
|
||||||
placeholder="Search all datasets..."
|
return Array.isArray(facetValue)
|
||||||
/>
|
? acc.concat(facetValue)
|
||||||
<ul>
|
: acc.concat([facetValue]);
|
||||||
{datasets
|
}
|
||||||
|
return acc;
|
||||||
|
}, []);
|
||||||
|
acc[facet] = {
|
||||||
|
possibleValues: [...new Set(possibleValues)],
|
||||||
|
selectedValue: null,
|
||||||
|
};
|
||||||
|
return acc;
|
||||||
|
}, {})
|
||||||
|
: [];
|
||||||
|
|
||||||
|
const { register, watch } = useForm(facetValues);
|
||||||
|
|
||||||
|
const filteredDatasets = datasets
|
||||||
|
// First filter by flex search
|
||||||
.filter((dataset) =>
|
.filter((dataset) =>
|
||||||
indexFilter !== ''
|
indexFilter !== ''
|
||||||
? index.search(indexFilter).includes(dataset._id)
|
? index.search(indexFilter).includes(dataset._id)
|
||||||
: true
|
: true
|
||||||
)
|
)
|
||||||
.map((dataset) => (
|
//Then check if the selectedValue for the given facet is included in the dataset metadata
|
||||||
<li key={dataset.id}>
|
.filter((dataset) => {
|
||||||
<a href={dataset.url_path}>{dataset.metadata.title ? dataset.metadata.title : dataset.url_path}</a>
|
//Avoids a server rendering breakage
|
||||||
|
if (!watch() || Object.keys(watch()).length === 0) return true
|
||||||
|
//This will filter only the key pairs of the metadata values that were selected as facets
|
||||||
|
const datasetFacets = Object.entries(dataset.metadata).filter((entry) =>
|
||||||
|
facets.includes(entry[0])
|
||||||
|
);
|
||||||
|
//Check if the value present is included in the selected value in the form
|
||||||
|
return datasetFacets.every((elem) =>
|
||||||
|
watch()[elem[0]].selectedValue
|
||||||
|
? (elem[1] as string | string[]).includes(
|
||||||
|
watch()[elem[0]].selectedValue
|
||||||
|
)
|
||||||
|
: true
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<DebouncedInput
|
||||||
|
value={indexFilter ?? ''}
|
||||||
|
onChange={(value) => setIndexFilter(String(value))}
|
||||||
|
className="p-2 text-sm shadow border border-block mr-1"
|
||||||
|
placeholder="Search all datasets..."
|
||||||
|
/>
|
||||||
|
{Object.entries(facetValues).map((elem) => (
|
||||||
|
<select
|
||||||
|
key={elem[0]}
|
||||||
|
defaultValue=""
|
||||||
|
className="p-2 ml-1 text-sm shadow border border-block"
|
||||||
|
{...register(elem[0] + '.selectedValue')}
|
||||||
|
>
|
||||||
|
<option value="">
|
||||||
|
Filter by {elem[0]}
|
||||||
|
</option>
|
||||||
|
{(elem[1] as { possibleValues: string[] }).possibleValues.map(
|
||||||
|
(val) => (
|
||||||
|
<option
|
||||||
|
key={val}
|
||||||
|
className="dark:bg-white dark:text-black"
|
||||||
|
value={val}
|
||||||
|
>
|
||||||
|
{val}
|
||||||
|
</option>
|
||||||
|
)
|
||||||
|
)}
|
||||||
|
</select>
|
||||||
|
))}
|
||||||
|
<ul>
|
||||||
|
{filteredDatasets.map((dataset) => (
|
||||||
|
<li key={dataset._id}>
|
||||||
|
<a href={dataset.url_path}>
|
||||||
|
{dataset.metadata.title
|
||||||
|
? dataset.metadata.title
|
||||||
|
: dataset.url_path}
|
||||||
|
</a>
|
||||||
</li>
|
</li>
|
||||||
))}
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,14 +7,12 @@ import { Mermaid } from '@flowershow/core';
|
|||||||
// to handle import statements. Instead, you must include components in scope
|
// to handle import statements. Instead, you must include components in scope
|
||||||
// here.
|
// here.
|
||||||
const components = {
|
const components = {
|
||||||
Table: dynamic(() => import('./Table')),
|
Table: dynamic(() => import('@portaljs/components').then(mod => mod.Table)),
|
||||||
Catalog: dynamic(() => import('./Catalog')),
|
Catalog: dynamic(() => import('./Catalog')),
|
||||||
mermaid: Mermaid,
|
mermaid: Mermaid,
|
||||||
// Excel: dynamic(() => import('../components/Excel')),
|
Vega: dynamic(() => import('@portaljs/components').then(mod => mod.Vega)),
|
||||||
// TODO: try and make these dynamic ...
|
VegaLite: dynamic(() => import('@portaljs/components').then(mod => mod.VegaLite)),
|
||||||
Vega: dynamic(() => import('./Vega')),
|
LineChart: dynamic(() => import('@portaljs/components').then(mod => mod.LineChart)),
|
||||||
VegaLite: dynamic(() => import('./VegaLite')),
|
|
||||||
LineChart: dynamic(() => import('./LineChart')),
|
|
||||||
} as any;
|
} as any;
|
||||||
|
|
||||||
export default function DRD({ source }: { source: any }) {
|
export default function DRD({ source }: { source: any }) {
|
||||||
@@ -1,55 +0,0 @@
|
|||||||
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} />;
|
|
||||||
}
|
|
||||||
@@ -1,189 +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 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;
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
// Wrapper for the Vega component
|
|
||||||
import { Vega as VegaOg } from "react-vega";
|
|
||||||
|
|
||||||
export default function Vega(props) {
|
|
||||||
return <VegaOg {...props} />;
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
// 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} />;
|
|
||||||
}
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
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} />;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
export default async function loadData(url: string) {
|
|
||||||
const response = await fetch(url)
|
|
||||||
const data = await response.text()
|
|
||||||
return data
|
|
||||||
}
|
|
||||||
@@ -91,7 +91,7 @@ const parse = async function (source, format, scope) {
|
|||||||
],
|
],
|
||||||
format,
|
format,
|
||||||
},
|
},
|
||||||
scope: { ...scope, ...data},
|
scope,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -1,16 +0,0 @@
|
|||||||
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;
|
|
||||||
39
examples/learn-example/package-lock.json
generated
39
examples/learn-example/package-lock.json
generated
@@ -15,14 +15,14 @@
|
|||||||
"@flowershow/remark-wiki-link": "^1.1.2",
|
"@flowershow/remark-wiki-link": "^1.1.2",
|
||||||
"@heroicons/react": "^2.0.17",
|
"@heroicons/react": "^2.0.17",
|
||||||
"@opentelemetry/api": "^1.4.0",
|
"@opentelemetry/api": "^1.4.0",
|
||||||
|
"@portaljs/components": "^0.0.3",
|
||||||
"@tanstack/react-table": "^8.8.5",
|
"@tanstack/react-table": "^8.8.5",
|
||||||
"@types/flexsearch": "^0.7.3",
|
|
||||||
"@types/node": "18.16.0",
|
"@types/node": "18.16.0",
|
||||||
"@types/react": "18.2.0",
|
"@types/react": "18.2.0",
|
||||||
"@types/react-dom": "18.2.0",
|
"@types/react-dom": "18.2.0",
|
||||||
"eslint": "8.39.0",
|
"eslint": "8.39.0",
|
||||||
"eslint-config-next": "13.3.1",
|
"eslint-config-next": "13.3.1",
|
||||||
"flexsearch": "^0.7.31",
|
"flexsearch": "0.7.21",
|
||||||
"gray-matter": "^4.0.3",
|
"gray-matter": "^4.0.3",
|
||||||
"hastscript": "^7.2.0",
|
"hastscript": "^7.2.0",
|
||||||
"mdx-mermaid": "2.0.0-rc7",
|
"mdx-mermaid": "2.0.0-rc7",
|
||||||
@@ -31,6 +31,7 @@
|
|||||||
"papaparse": "^5.4.1",
|
"papaparse": "^5.4.1",
|
||||||
"react": "18.2.0",
|
"react": "18.2.0",
|
||||||
"react-dom": "18.2.0",
|
"react-dom": "18.2.0",
|
||||||
|
"react-hook-form": "^7.43.9",
|
||||||
"react-vega": "^7.6.0",
|
"react-vega": "^7.6.0",
|
||||||
"rehype-autolink-headings": "^6.1.1",
|
"rehype-autolink-headings": "^6.1.1",
|
||||||
"rehype-katex": "^6.0.3",
|
"rehype-katex": "^6.0.3",
|
||||||
@@ -44,6 +45,7 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@tailwindcss/typography": "^0.5.9",
|
"@tailwindcss/typography": "^0.5.9",
|
||||||
|
"@types/flexsearch": "^0.7.3",
|
||||||
"autoprefixer": "^10.4.14",
|
"autoprefixer": "^10.4.14",
|
||||||
"postcss": "^8.4.23",
|
"postcss": "^8.4.23",
|
||||||
"tailwindcss": "^3.3.1"
|
"tailwindcss": "^3.3.1"
|
||||||
@@ -1976,6 +1978,15 @@
|
|||||||
"url": "https://opencollective.com/unts"
|
"url": "https://opencollective.com/unts"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@portaljs/components": {
|
||||||
|
"version": "0.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@portaljs/components/-/components-0.0.3.tgz",
|
||||||
|
"integrity": "sha512-SRinOO800oA58akBlni5ahs3PxE+AyqFqgtYN/3ZqYt3CT1JhXOfeXxb+Kbz2QHr/GsAZcUr3DMneB4EzvBx7g==",
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "^18.2.0",
|
||||||
|
"react-dom": "^18.2.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@protobufjs/aspromise": {
|
"node_modules/@protobufjs/aspromise": {
|
||||||
"version": "1.1.2",
|
"version": "1.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz",
|
||||||
@@ -2218,7 +2229,8 @@
|
|||||||
"node_modules/@types/flexsearch": {
|
"node_modules/@types/flexsearch": {
|
||||||
"version": "0.7.3",
|
"version": "0.7.3",
|
||||||
"resolved": "https://registry.npmjs.org/@types/flexsearch/-/flexsearch-0.7.3.tgz",
|
"resolved": "https://registry.npmjs.org/@types/flexsearch/-/flexsearch-0.7.3.tgz",
|
||||||
"integrity": "sha512-HXwADeHEP4exXkCIwy2n1+i0f1ilP1ETQOH5KDOugjkTFZPntWo0Gr8stZOaebkxsdx+k0X/K6obU/+it07ocg=="
|
"integrity": "sha512-HXwADeHEP4exXkCIwy2n1+i0f1ilP1ETQOH5KDOugjkTFZPntWo0Gr8stZOaebkxsdx+k0X/K6obU/+it07ocg==",
|
||||||
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/@types/geojson": {
|
"node_modules/@types/geojson": {
|
||||||
"version": "7946.0.10",
|
"version": "7946.0.10",
|
||||||
@@ -4959,9 +4971,9 @@
|
|||||||
"integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ=="
|
"integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ=="
|
||||||
},
|
},
|
||||||
"node_modules/flexsearch": {
|
"node_modules/flexsearch": {
|
||||||
"version": "0.7.31",
|
"version": "0.7.21",
|
||||||
"resolved": "https://registry.npmjs.org/flexsearch/-/flexsearch-0.7.31.tgz",
|
"resolved": "https://registry.npmjs.org/flexsearch/-/flexsearch-0.7.21.tgz",
|
||||||
"integrity": "sha512-XGozTsMPYkm+6b5QL3Z9wQcJjNYxp0CYn3U1gO7dwD6PAqU1SVWZxI9CCg3z+ml3YfqdPnrBehaBrnH2AGKbNA=="
|
"integrity": "sha512-W7cHV7Hrwjid6lWmy0IhsWDFQboWSng25U3VVywpHOTJnnAZNPScog67G+cVpeX9f7yDD21ih0WDrMMT+JoaYg=="
|
||||||
},
|
},
|
||||||
"node_modules/for-each": {
|
"node_modules/for-each": {
|
||||||
"version": "0.3.3",
|
"version": "0.3.3",
|
||||||
@@ -9290,6 +9302,21 @@
|
|||||||
"react": "^18.2.0"
|
"react": "^18.2.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/react-hook-form": {
|
||||||
|
"version": "7.43.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.43.9.tgz",
|
||||||
|
"integrity": "sha512-AUDN3Pz2NSeoxQ7Hs6OhQhDr6gtF9YRuutGDwPQqhSUAHJSgGl2VeY3qN19MG0SucpjgDiuMJ4iC5T5uB+eaNQ==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12.22.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/react-hook-form"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "^16.8.0 || ^17 || ^18"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/react-is": {
|
"node_modules/react-is": {
|
||||||
"version": "16.13.1",
|
"version": "16.13.1",
|
||||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
|
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
|
||||||
|
|||||||
@@ -19,14 +19,14 @@
|
|||||||
"@flowershow/remark-wiki-link": "^1.1.2",
|
"@flowershow/remark-wiki-link": "^1.1.2",
|
||||||
"@heroicons/react": "^2.0.17",
|
"@heroicons/react": "^2.0.17",
|
||||||
"@opentelemetry/api": "^1.4.0",
|
"@opentelemetry/api": "^1.4.0",
|
||||||
|
"@portaljs/components": "^0.0.3",
|
||||||
"@tanstack/react-table": "^8.8.5",
|
"@tanstack/react-table": "^8.8.5",
|
||||||
"@types/flexsearch": "^0.7.3",
|
|
||||||
"@types/node": "18.16.0",
|
"@types/node": "18.16.0",
|
||||||
"@types/react": "18.2.0",
|
"@types/react": "18.2.0",
|
||||||
"@types/react-dom": "18.2.0",
|
"@types/react-dom": "18.2.0",
|
||||||
"eslint": "8.39.0",
|
"eslint": "8.39.0",
|
||||||
"eslint-config-next": "13.3.1",
|
"eslint-config-next": "13.3.1",
|
||||||
"flexsearch": "^0.7.31",
|
"flexsearch": "0.7.21",
|
||||||
"gray-matter": "^4.0.3",
|
"gray-matter": "^4.0.3",
|
||||||
"hastscript": "^7.2.0",
|
"hastscript": "^7.2.0",
|
||||||
"mdx-mermaid": "2.0.0-rc7",
|
"mdx-mermaid": "2.0.0-rc7",
|
||||||
@@ -35,6 +35,7 @@
|
|||||||
"papaparse": "^5.4.1",
|
"papaparse": "^5.4.1",
|
||||||
"react": "18.2.0",
|
"react": "18.2.0",
|
||||||
"react-dom": "18.2.0",
|
"react-dom": "18.2.0",
|
||||||
|
"react-hook-form": "^7.43.9",
|
||||||
"react-vega": "^7.6.0",
|
"react-vega": "^7.6.0",
|
||||||
"rehype-autolink-headings": "^6.1.1",
|
"rehype-autolink-headings": "^6.1.1",
|
||||||
"rehype-katex": "^6.0.3",
|
"rehype-katex": "^6.0.3",
|
||||||
@@ -48,6 +49,7 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@tailwindcss/typography": "^0.5.9",
|
"@tailwindcss/typography": "^0.5.9",
|
||||||
|
"@types/flexsearch": "^0.7.3",
|
||||||
"autoprefixer": "^10.4.14",
|
"autoprefixer": "^10.4.14",
|
||||||
"postcss": "^8.4.23",
|
"postcss": "^8.4.23",
|
||||||
"tailwindcss": "^3.3.1"
|
"tailwindcss": "^3.3.1"
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
import { existsSync, promises as fs } from 'fs';
|
import { existsSync, promises as fs } from 'fs';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import parse from '../lib/markdown';
|
import parse from '../lib/markdown';
|
||||||
import DRD from '../components/DRD';
|
|
||||||
|
import DataRichDocument from '../components/DataRichDocument';
|
||||||
import clientPromise from '../lib/mddb';
|
import clientPromise from '../lib/mddb';
|
||||||
|
|
||||||
export const getStaticPaths = async () => {
|
export const getStaticPaths = async () => {
|
||||||
@@ -73,7 +74,7 @@ export default function DatasetPage({ mdxSource, frontMatter }) {
|
|||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
<main>
|
<main>
|
||||||
<DRD source={mdxSource} />
|
<DataRichDocument source={mdxSource} />
|
||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
import '../styles/globals.css'
|
import '../styles/globals.css'
|
||||||
|
import '@portaljs/components/styles.css'
|
||||||
|
|
||||||
import type { AppProps } from 'next/app'
|
import type { AppProps } from 'next/app'
|
||||||
|
|
||||||
export default function App({ Component, pageProps }: AppProps) {
|
export default function App({ Component, pageProps }: AppProps) {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"target": "es5",
|
"target": "es6",
|
||||||
"lib": ["dom", "dom.iterable", "esnext"],
|
"lib": ["dom", "dom.iterable", "esnext"],
|
||||||
"allowJs": true,
|
"allowJs": true,
|
||||||
"skipLibCheck": true,
|
"skipLibCheck": true,
|
||||||
|
|||||||
@@ -1,32 +1,29 @@
|
|||||||
# `components` package
|
# PortalJS React Components
|
||||||
|
|
||||||
**See live:** https://storybook.portaljs.org
|
**Storybook:** https://storybook.portaljs.org
|
||||||
|
**Docs**: https://portaljs.org/docs
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
To install this package on your project:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm i @portaljs/components
|
||||||
|
```
|
||||||
|
|
||||||
|
> Note: React 18 is required.
|
||||||
|
|
||||||
|
You'll also have to import the styles CSS file in your project:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
// E.g.: Next.js => pages/_app.tsx
|
||||||
|
import '@portaljs/components/styles.css'
|
||||||
|
```
|
||||||
|
|
||||||
## Dev
|
## Dev
|
||||||
|
|
||||||
Use Storybook to work on components by running:
|
Use Storybook to work on components by running:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
yarn storybook
|
npm run storybook
|
||||||
```
|
|
||||||
|
|
||||||
## Build
|
|
||||||
|
|
||||||
To build this project, run:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
yarn build
|
|
||||||
```
|
|
||||||
|
|
||||||
## Install
|
|
||||||
|
|
||||||
TODO: command to install the package
|
|
||||||
|
|
||||||
### Next.js
|
|
||||||
|
|
||||||
Add this line at the start of `_app.tsx`:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
// pages/_app.tsx
|
|
||||||
import "dist/styles.css";
|
|
||||||
```
|
```
|
||||||
24752
packages/components/package-lock.json
generated
Normal file
24752
packages/components/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,11 +1,18 @@
|
|||||||
{
|
{
|
||||||
"name": "components",
|
"name": "@portaljs/components",
|
||||||
"private": true,
|
"version": "0.0.3",
|
||||||
"version": "0.0.1",
|
|
||||||
"type": "module",
|
"type": "module",
|
||||||
|
"description": "https://portaljs.org",
|
||||||
|
"keywords": [
|
||||||
|
"data portal",
|
||||||
|
"data catalog",
|
||||||
|
"table",
|
||||||
|
"charts",
|
||||||
|
"visualization"
|
||||||
|
],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "yarn storybook",
|
"dev": "npm run storybook",
|
||||||
"build": "tsc && vite build && yarn build-tailwind",
|
"build": "tsc && vite build && npm run build-tailwind",
|
||||||
"lint": "eslint src --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
|
"lint": "eslint src --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
|
||||||
"prepack": "json -f package.json -I -e \"delete this.devDependencies; delete this.dependencies\"",
|
"prepack": "json -f package.json -I -e \"delete this.devDependencies; delete this.dependencies\"",
|
||||||
"storybook": "storybook dev -p 6006",
|
"storybook": "storybook dev -p 6006",
|
||||||
@@ -65,6 +72,12 @@
|
|||||||
".": {
|
".": {
|
||||||
"import": "./dist/components.es.js",
|
"import": "./dist/components.es.js",
|
||||||
"require": "./dist/components.umd.js"
|
"require": "./dist/components.umd.js"
|
||||||
}
|
},
|
||||||
|
"./styles.css": {
|
||||||
|
"import": "./dist/styles.css"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"publishConfig": {
|
||||||
|
"access": "public"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user