Compare commits

..

9 Commits

Author SHA1 Message Date
Luccas Mateus de Medeiros Gomes
3cf945a194 [examples/openspending][xs] - fix build 2023-06-16 09:06:53 -03:00
Luccas Mateus de Medeiros Gomes
fe225715e9 [examples][m] - fix builds 2023-06-16 08:44:51 -03:00
Luccas Mateus de Medeiros Gomes
a801baf34d [examples/github-backed-catalog][xs] - fix build 2023-06-16 07:36:09 -03:00
olayway
f8ef07646f [examples/github-backed-catalog][m]: rplc @flowershow with @portaljs packages 2023-06-14 11:54:10 +02:00
olayway
0225eb49ba [examples/learn][m]: rplc @flowershow with @portaljs packages 2023-06-14 11:54:10 +02:00
olayway
b68c52ce36 [examples/openspending][m]: rplc @flowershow with @portaljs packages 2023-06-14 11:54:10 +02:00
olayway
c80cae27a4 [examples/turing][m]: rplc @flowershow with @portaljs packages 2023-06-14 11:54:10 +02:00
olayway
b14500b213 [examples/fivethirtyeight][m]: rplc @flowershow with @portaljs packages 2023-06-14 11:54:10 +02:00
olayway
062e817f3a [examples/ckan][m]: rplc @flowershow with @portaljs packages 2023-06-14 11:54:10 +02:00
180 changed files with 2712 additions and 29231 deletions

View File

@@ -1,5 +0,0 @@
---
'@portaljs/remark-wiki-link': minor
---
Parse note embeds as regular wiki links (until we add proper support for note embeds).

View File

@@ -40,25 +40,16 @@ export default function Home({
});
return (
<div>
<main className="py-12 bg-zinc-900">
<DatasetSearchForm
options={options}
setOptions={setOptions}
groups={groups}
orgs={orgs}
/>
<div
className="bg-white p-8 mx-auto my-4 rounded-lg"
style={{ width: 'min(1100px, 95vw)' }}
>
<ListOfDatasets
options={options}
setOptions={setOptions}
ckan={ckan}
/>{' '}
</div>
</main>
</div>
<main className="flex min-h-screen flex-col items-center p-24 bg-zinc-900">
<DatasetSearchForm
options={options}
setOptions={setOptions}
groups={groups}
orgs={orgs}
/>
<div className="bg-white p-8 my-4 rounded-lg">
<ListOfDatasets options={options} setOptions={setOptions} ckan={ckan} />{' '}
</div>
</main>
);
}

View File

@@ -1,5 +0,0 @@
const nextConfig = {
swcMinify: false
}
module.exports = nextConfig

View File

@@ -7,21 +7,13 @@ import { Mermaid } from '@portaljs/core';
// 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)
),
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)
),
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 }) {

View File

@@ -16,8 +16,6 @@ We welcome contributions from anyone who is interested in improving OpenSpending
The main platform for communication and collaboration for OpenSpending is Github, if you want to interact with us you can o so by submitting an issue.
**Repo for issues:** [https://github.com/os-data/registry/issues](https://github.com/os-data/registry/issues)
If you want to submit a dataset for evaluation and inclusion in OpenSpending, you will need to create an issue on Github using this template:
```markdown

View File

@@ -156,8 +156,8 @@ the key to a prosperous future.
title: '',
field: 'label',
scale: {
domain: ['Education'],
range: ['#64b5f6'],
domain: ["Education"],
range: ['#64b5f6']
},
},
tooltip: [
@@ -189,3 +189,4 @@ Frankfurt is a city that's recognized the power of education, and it's using tha
One can only hope that more cities follow suit.
[^1]: https://worldpopulationreview.com/world-cities/frankfurt-population

View File

@@ -1,284 +0,0 @@
---
title: An Exploration of Moers' 2016 Budget Anomaly
date: 06/13/2023
---
_Note: The currency in this post has been converted to USD using an average exchange rate of 1 EUR = 1.20 USD.
Additionally, the text values for account groups have been loosely translated to English. You can find the
original dataset here: [Moers budget dataset](https://www.openspending.org/@os-data/moers-all)_
In the fiscal landscape of Moers, Germany, 2016 stands out as an anomaly. An examination of the budget allocations
for this particular year shows a significant surge in spending across various areas. After this anomalous
spike, spending patterns then seemed to resume their previous trajectory of a slow, steady climb in the
following years. While it's tempting to dismiss this as an irregularity, it's worth exploring the potential
underlying causes of this fiscal fluctuation.
<VegaLite
spec={{
$schema: 'https://vega.github.io/schema/vega-lite/v5.json',
title: 'Spending by Account Group',
description: 'Spending by Account Group',
data: {
url: 'https://storage.openspending.org/moers-all/moers-sums.csv',
},
transform: [
{
calculate: "format(datum.amount / 1e6 * 1.20, '$,.1f') + 'M'",
as: 'formattedAmountMillion',
},
{ calculate: 'datum.amount / 1e9 * 1.20', as: 'amountBillion' },
],
mark: 'bar',
height: 400,
width: 350,
encoding: {
x: {
field: 'date',
type: 'temporal',
timeUnit: 'utcyear',
title: 'Year',
},
y: {
field: 'amount',
type: 'quantitative',
title: 'Spending (USD in millions)',
axis: {
format: '~s',
labelExpr: "format(datum.value / 1e6 * 1.20, '$,.0f') + 'M'",
},
},
color: {
field: 'account group',
type: 'nominal',
title: 'Account Group',
scale: {
domain: [
'Activated Own Work',
'Balance Expenses',
'Care Expenses',
'Donations And General Levies',
'Expenses For Material And Services',
'Extraordinary Expenses',
'Extraordinary Income',
'Other Ordinary Expenses',
'Other Ordinary Income',
'Other Transfer Income',
'Personnel Expenses',
'Private Law Performance Fees',
'Public-Service Performance Fees',
'Taxes And Similar Charges',
'Transfer Expenses',
],
range: [
'#EF5350',
'#F06292',
'#BA68C8',
'#9575CD',
'#7986CB',
'#64B5F6',
'#4FC3F7',
'#4DD0E1',
'#4DB6AC',
'#81C784',
'#AED581',
'#DCE775',
'#FFF176',
'#FFD54F',
'#FFB74D',
],
},
},
tooltip: [
{ field: 'account group', type: 'nominal', title: 'Account Group' },
{
field: 'formattedAmountMillion',
type: 'ordinal',
title: 'Spending (USD)',
},
{ field: 'date', type: 'temporal', title: 'Year', format: '%Y' }
],
},
}}
/>
## Why?
One possible explanation could be the global refugee crisis that was unfolding around this time, particularly
in Germany. While we can't assert this connection definitively due to the lack of specific data, it's an angle
worth examining to better understand the budget anomaly of 2016.
For instance, the "Donations and General Levies" account group saw a distinct increase in 2016. This could be
reflective of Moers' attempts to provide immediate support to incoming refugees, although we can only speculate
at this point.
<VegaLite
spec={{
$schema: 'https://vega.github.io/schema/vega-lite/v5.json',
title: 'Donations and General Levies',
description: 'Donations and General Levies',
data: {
url: 'https://storage.openspending.org/moers-all/moers-sums.csv',
},
transform: [
{ filter: "datum['account group']==='Donations And General Levies'" },
{
calculate: "format(datum.amount / 1e6 * 1.20, '$,.1f') + 'M'",
as: 'formattedAmountMillion',
},
],
mark: {
type: 'area',
line: true,
point: true,
},
width: 400,
height: 400,
encoding: {
x: {
field: 'date',
type: 'temporal',
timeUnit: 'utcyear',
title: 'Year',
},
y: {
field: 'amount',
type: 'quantitative',
title: 'Spending (USD in millions)',
axis: {
format: '~s',
labelExpr: "format(datum.value / 1e6 * 1.20, '$,.0f') + 'M'",
},
},
color: {
title: 'Account Group',
field: 'account group',
scale: {
domain: ['Donations And General Levies'],
range: ['#9575CD'],
},
},
tooltip: [
{ field: 'account group', type: 'nominal', title: 'Area' },
{
field: 'formattedAmountMillion',
type: 'ordinal',
title: 'Spending (USD in millions)',
},
{ field: 'date', type: 'temporal', title: 'Year', format: '%Y' }
],
},
}}
/>
Similarly, "Expenses for Material and Services" also experienced an upswing. This could suggest
that the town was working to establish necessary infrastructure and resources for the newcomers. It's plausible
that procurement of essential supplies and services to aid the refugees was a factor contributing to this budget
increase.
The rise in "Personnel Expenses" may suggest an expansion of the town's workforce during this period. Additional
staffing in areas like interpretation, social work, healthcare, and administration would be necessary to handle
the influx of refugees.
The two account groups that experienced the most substantial raw monetary increases were "Taxes and Similar Charges"
and "Transfer Expenses". Both these categories more than doubled in size during the year, a significant surge
especially considering their already high baseline spending. This could be for a variety of reasons.
For "Taxes and Similar Charges", the hike could indicate the town adjusting local taxation to meet the additional
expenditures, or it could reflect an increase in income from other sources falling under this group. The influx
of refugees and the resulting uptick in economic activity may have also led to a rise in the collection of certain
local taxes or fees.
Concurrently, the "Transfer Expenses" spike could be an indication of heightened social welfare payments, costs
associated with refugee integration, and funds transferred to other entities managing the crisis. These financial
escalations are a testament to the extensive and multifaceted financial impact the refugee crisis had on the town's
budget.
<VegaLite
spec={{
$schema: 'https://vega.github.io/schema/vega-lite/v5.json',
title: 'Largest Spending by Account Group',
description: 'Largest Spending by Account Group',
data: {
url: 'https://storage.openspending.org/moers-all/moers-sums.csv',
},
transform: [
{
filter: {
field: 'account group',
oneOf: [
'Donations And General Levies',
'Expenses For Material And Services',
'Personnel Expenses',
'Taxes And Similar Charges',
'Transfer Expenses'
]
}
},
{
calculate: "format(datum.amount / 1e6 * 1.20, '$,.1f') + 'M'",
as: 'formattedAmountMillion',
}
],
mark: {
type: 'line',
point: true,
},
width: 400,
height: 400,
encoding: {
x: {
field: 'date',
type: 'temporal',
timeUnit: 'utcyear',
title: 'Year'
},
y: {
field: 'amount',
type: 'quantitative',
title: 'Spending (USD in millions)',
axis: {
format: '~s',
labelExpr: "format(datum.value / 1e6 * 1.20, '$,.0f') + 'M'",
}
},
color: {
field: 'account group',
type: 'nominal',
title: 'Account Group',
scale: {
domain: [
'Donations And General Levies',
'Expenses For Material And Services',
'Personnel Expenses',
'Taxes And Similar Charges',
'Transfer Expenses'
],
range: [
'#9575CD',
'#7986CB',
'#AED581',
'#FFD54F',
'#FFB74D'
],
},
},
tooltip: [
{ field: 'account group', type: 'nominal', title: 'Area' },
{
field: 'formattedAmountMillion',
type: 'ordinal',
title: 'Spending (USD in millions)',
},
{ field: 'date', type: 'temporal', title: 'Year', format: '%Y' }
],
},
}}
/>
By 2017, these budget items returned to their pre-2016 levels, which may suggest that the town had successfully
adapted to the new circumstances, perhaps by implementing more cost-effective strategies.
While this exploration offers a compelling perspective, it's important to note that these are theories derived
from the available budget data and the concurrent global events of the time. It underscores how global crises,
like the refugee crisis, may potentially impact not just national budgets, but also the fiscal management of
smaller towns like Moers.

View File

@@ -1,105 +0,0 @@
---
title: Where does Berlin spend its ERDF benefits?
date: 2023-06-12
authors: ['João Demenech']
---
In this data-driven story, let's analyze how the city of Berlin benefited from ERDF from 2008 to 2015.
## What is ERDF?
If you're not familiar with ERDF, it's the European Regional Development Fund. According to its [official website](https://ec.europa.eu/regional_policy/funding/erdf_en):
> The European Regional Development Fund (ERDF) aims to strengthen economic, social, and territorial cohesion in the European Union by correcting imbalances between its regions. In 2021-2027 it will enable investments in a smarter, greener, more connected, and more social Europe that is closer to its citizens.
## A look into the data
The dataset that will be used is this one: https://www.openspending.org/@os-data/de3-berlin-2007-2013-erdf (if you're looking for ERDF data for other cities/regions, take a look at the data catalog).
Here's a sample of this data:
<FlatUiTable url="https://storage.openspending.org/de3-berlin-2007-2013-erdf/concat.csv" />
Note that we will only use the rows where the "amount_kind" column is set to "total_amount".
## How has the total amount changed over the years?
The line chart below shows the sum of the total amount of EUR that was approved for each year:
<LineChart data="https://storage.openspending.org/de3-berlin-2007-2013-erdf/berlin-erdf-2007-2013-total-amount-apporved-per-year.csv" xAxis="approval_year" yAxis="total_amount" />
As you can see, the total increased significantly from 2007 to 2010, later fluctuating between €7 million and €9 million per year.
## Who were the top beneficiaries for each year?
Now, let's try to understand who were the top beneficiaries each year. To do that, let's filter the data to show only the two top beneficiaries for each year:
<FlatUiTable url="https://storage.openspending.org/de3-berlin-2007-2013-erdf/berlin-erdf-2007-2013-total-amount-approved-per-year-per-beneficiary--top-2.csv" />
Since there are many years in this range, let's split this analysis into two ranges: one for 2008-2010 and the other for 2011-2015.
<VegaLite spec={{
"data": { "url": "https://storage.openspending.org/de3-berlin-2007-2013-erdf/berlin-erdf-2007-2013-total-amount-approved-per-year-per-beneficiary--top-2.csv"},
"transform": [
{"filter": "datum.approval_year <= 2010"},
],
"title": "Total Amount (EUR) x Approval Year (2007-2010)",
"width": "container",
"mark": {"type": "bar", "tooltip": true },
"encoding": {
"y": {
"aggregate": "sum",
"field": "total_amount",
"title": "Total Amount (EUR)",
"stack": null
},
"x": {"field": "approval_year", "title": "Approval Year"},
"color": {
"type": "nominal",
"field": "beneficiary_name",
}
}
}} />
<VegaLite spec={{
"title": "Total Amount (EUR) x Approval Year (2011-2013)",
"data": { "url": "https://storage.openspending.org/de3-berlin-2007-2013-erdf/berlin-erdf-2007-2013-total-amount-approved-per-year-per-beneficiary--top-2.csv"},
"transform": [
{"filter": "datum.approval_year > 2010"},
],
"width": "container",
"mark": {"type": "bar", "tooltip": true },
"encoding": {
"y": {
"aggregate": "sum",
"field": "total_amount",
"title": "Total Amount (EUR)",
"stack": null
},
"x": {"field": "approval_year", "title": "Approval Year"},
"color": {
"type": "nominal",
"field": "beneficiary_name",
}
}
}} />
It's easy to spot some repeating colors. Now, let's see to which sector the top beneficiaries belong to:
<Table csv="
Beneficiary,Sector
D & B,Education
Stiftung Sozialpädagogisches Institut Berlin (SPI),Education/Social work
IB e.V.,Social work
gsub-Projektegesellschaft mbH,Consulting
BBW Berufsvorbereitungs-u.Ausbildungsgesellschaft,Education
Arbeit und Bildung e.V.,Consulting
Gesellschaft für soziale Unternehmensberatung GSUB,Consulting
SPI Consult GmbH,Consulting
Beuth Hochschule für Technik Berlin,Education
TU Berlin, Fakultät II,Education
Hochschule für Technik und Wirtschaft Berlin,Education
WeTeK gGmbH,Education
" />
Based on the sectors of the top beneficiaries, it's clear that education and professional training have been a major focus of ERDF investment in Berlin during that period of time.

View File

@@ -14,7 +14,7 @@
"@heroicons/react": "^2.0.18",
"@octokit/plugin-throttling": "^5.2.2",
"@portaljs/ckan": "^0.0.2",
"@portaljs/components": "0.1.12",
"@portaljs/components": "^0.1.8",
"@portaljs/core": "^1.0.5",
"@portaljs/remark-callouts": "^1.0.5",
"@portaljs/remark-embed": "^1.0.4",
@@ -1971,40 +1971,6 @@
"@lit-labs/ssr-dom-shim": "^1.0.0"
}
},
"node_modules/@mapbox/jsonlint-lines-primitives": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/@mapbox/jsonlint-lines-primitives/-/jsonlint-lines-primitives-2.0.2.tgz",
"integrity": "sha512-rY0o9A5ECsTQRVhv7tL/OyDpGAoUB4tTvLiW1DSzQGq4bvTPhNw1VpSNjDJc5GFZ2XuyOtSWSVN05qOtcD71qQ==",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/@mapbox/mapbox-gl-style-spec": {
"version": "13.28.0",
"resolved": "https://registry.npmjs.org/@mapbox/mapbox-gl-style-spec/-/mapbox-gl-style-spec-13.28.0.tgz",
"integrity": "sha512-B8xM7Fp1nh5kejfIl4SWeY0gtIeewbuRencqO3cJDrCHZpaPg7uY+V8abuR+esMeuOjRl5cLhVTP40v+1ywxbg==",
"dependencies": {
"@mapbox/jsonlint-lines-primitives": "~2.0.2",
"@mapbox/point-geometry": "^0.1.0",
"@mapbox/unitbezier": "^0.0.0",
"csscolorparser": "~1.0.2",
"json-stringify-pretty-compact": "^2.0.0",
"minimist": "^1.2.6",
"rw": "^1.3.3",
"sort-object": "^0.3.2"
},
"bin": {
"gl-style-composite": "bin/gl-style-composite.js",
"gl-style-format": "bin/gl-style-format.js",
"gl-style-migrate": "bin/gl-style-migrate.js",
"gl-style-validate": "bin/gl-style-validate.js"
}
},
"node_modules/@mapbox/mapbox-gl-style-spec/node_modules/json-stringify-pretty-compact": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/json-stringify-pretty-compact/-/json-stringify-pretty-compact-2.0.0.tgz",
"integrity": "sha512-WRitRfs6BGq4q8gTgOy4ek7iPFXjbra0H3PmDLKm2xnZ+Gh1HUhiKGgCZkSPNULlP7mvfu6FV/mOLhCarspADQ=="
},
"node_modules/@mapbox/node-pre-gyp": {
"version": "1.0.10",
"resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.10.tgz",
@@ -2054,16 +2020,6 @@
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
},
"node_modules/@mapbox/point-geometry": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/@mapbox/point-geometry/-/point-geometry-0.1.0.tgz",
"integrity": "sha512-6j56HdLTwWGO0fJPlrZtdU/B13q8Uwmo18Ck2GnGgN9PCFyKTZ3UbXeEdRFh18i9XQ92eH2VdtpJHpBD3aripQ=="
},
"node_modules/@mapbox/unitbezier": {
"version": "0.0.0",
"resolved": "https://registry.npmjs.org/@mapbox/unitbezier/-/unitbezier-0.0.0.tgz",
"integrity": "sha512-HPnRdYO0WjFjRTSwO3frz1wKaU649OBFPX3Zo/2WZvuRi6zMiRGui8SnPQiQABgqCf8YikDe5t3HViTVw1WUzA=="
},
"node_modules/@mdx-js/mdx": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/@mdx-js/mdx/-/mdx-2.3.0.tgz",
@@ -2701,11 +2657,6 @@
"resolved": "https://registry.npmjs.org/@octokit/webhooks-types/-/webhooks-types-6.11.0.tgz",
"integrity": "sha512-AanzbulOHljrku1NGfafxdpTCfw2ENaWzH01N2vqQM+cUFbk868Cgh0xylz0JIM9BoKbfI++bdD6EYX0Q/UTEw=="
},
"node_modules/@petamoriken/float16": {
"version": "3.8.1",
"resolved": "https://registry.npmjs.org/@petamoriken/float16/-/float16-3.8.1.tgz",
"integrity": "sha512-oj3dU9kuMy8AqrreIboVh3KCJGSQO5T+dJ8JQFl369961jTWvPLP1GIlLy0FVoWehXLoI9BXygu/yzuNiIHBlg=="
},
"node_modules/@pkgr/utils": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/@pkgr/utils/-/utils-2.4.1.tgz",
@@ -2725,14 +2676,6 @@
"url": "https://opencollective.com/unts"
}
},
"node_modules/@planet/maps": {
"version": "8.1.0",
"resolved": "https://registry.npmjs.org/@planet/maps/-/maps-8.1.0.tgz",
"integrity": "sha512-THvbooWXFZYyjSdoqi6MFNqnjrfnO5Oev1tKh6ORjxFru0N69gZwUfEduRjg99VNklmaAk2BPaCatgKs5qr0XA==",
"dependencies": {
"react-reconciler": "^0.29.0"
}
},
"node_modules/@portaljs/ckan": {
"version": "0.0.2",
"resolved": "https://registry.npmjs.org/@portaljs/ckan/-/ckan-0.0.2.tgz",
@@ -2748,26 +2691,22 @@
}
},
"node_modules/@portaljs/components": {
"version": "0.1.12",
"resolved": "file:../../packages/components/portaljs-components-0.1.12.tgz",
"integrity": "sha512-Cr+RQ7tkbIqtBNq5D8zeZEB2dOejxD0V78l/I4AbdjYI8jvQ4Evx6APEfhPs3im6LDEGrT28LslVTZJ6luslnw==",
"version": "0.1.11",
"resolved": "https://registry.npmjs.org/@portaljs/components/-/components-0.1.11.tgz",
"integrity": "sha512-ojiZ2dtnORAlo2YycpwK+mxrseqVxrD/Z/FMejHrZ9V6pAdlLLpjMU724WTvGBbjwO/RkoVFc4x6H6ykyvMOig==",
"dependencies": {
"@githubocto/flat-ui": "^0.14.1",
"@heroicons/react": "^2.0.17",
"@planet/maps": "^8.1.0",
"@tanstack/react-table": "^8.8.5",
"chroma-js": "^2.4.2",
"flexsearch": "0.7.21",
"leaflet": "^1.9.4",
"next-mdx-remote": "^4.4.1",
"ol": "^7.4.0",
"papaparse": "^5.4.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-hook-form": "^7.43.9",
"react-leaflet": "^4.2.1",
"react-query": "^3.39.3",
"react-vega": "^7.6.0",
"rollup-plugin-re": "^1.0.7",
"vega": "5.25.0",
"vega-lite": "5.1.0",
"vitest": "^0.31.4"
@@ -2970,16 +2909,6 @@
"resolved": "https://registry.npmjs.org/@reach/observe-rect/-/observe-rect-1.2.0.tgz",
"integrity": "sha512-Ba7HmkFgfQxZqqaeIWWkNK0rEhpxVQHIoVyW1YDSkGsGIXzcaW4deC8B0pZrNSSyLTdIk7y+5olKt5+g0GmFIQ=="
},
"node_modules/@react-leaflet/core": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/@react-leaflet/core/-/core-2.1.0.tgz",
"integrity": "sha512-Qk7Pfu8BSarKGqILj4x7bCSZ1pjuAPZ+qmRwH5S7mDS91VSbVVsJSrW4qA+GPrro8t69gFYVMWb1Zc4yFmPiVg==",
"peerDependencies": {
"leaflet": "^1.9.0",
"react": "^18.0.0",
"react-dom": "^18.0.0"
}
},
"node_modules/@rushstack/eslint-patch": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.3.2.tgz",
@@ -4495,11 +4424,6 @@
"node": ">=10"
}
},
"node_modules/chroma-js": {
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/chroma-js/-/chroma-js-2.4.2.tgz",
"integrity": "sha512-U9eDw6+wt7V8z5NncY2jJfZa+hUH8XEj8FQHgFJTrUFnJfXYf4Ml4adI2vXZOjqRDpFWtYVWypDfZwnJ+HIR4A=="
},
"node_modules/clean-set": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/clean-set/-/clean-set-1.1.2.tgz",
@@ -4811,11 +4735,6 @@
"resolved": "https://registry.npmjs.org/css-unit-converter/-/css-unit-converter-1.1.2.tgz",
"integrity": "sha512-IiJwMC8rdZE0+xiEZHeru6YoONC4rfPMqGm2W85jMIbkFvv5nFTwJVFHam2eFrN6txmoUYFAFXiv8ICVeTO0MA=="
},
"node_modules/csscolorparser": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/csscolorparser/-/csscolorparser-1.0.3.tgz",
"integrity": "sha512-umPSgYwZkdFoUrH5hIq5kf0wPSXiro51nPw0j2K/c83KflkPSTBGMz6NJvMB+07VlL0y7VPo6QJcDjcgKTTm3w=="
},
"node_modules/cssesc": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
@@ -5939,11 +5858,6 @@
"node": ">=4"
}
},
"node_modules/earcut": {
"version": "2.2.4",
"resolved": "https://registry.npmjs.org/earcut/-/earcut-2.2.4.tgz",
"integrity": "sha512-/pjZsA1b4RPHbeWZQn66SWS8nZZWLQQ23oE3Eam7aroEFGEvwKAsJfZ9ytiEMycfzXWpca4FA9QIOehf7PocBQ=="
},
"node_modules/ecdsa-sig-formatter": {
"version": "1.0.11",
"resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz",
@@ -7241,39 +7155,6 @@
"node": ">=6.9.0"
}
},
"node_modules/geotiff": {
"version": "2.0.7",
"resolved": "https://registry.npmjs.org/geotiff/-/geotiff-2.0.7.tgz",
"integrity": "sha512-FKvFTNowMU5K6lHYY2f83d4lS2rsCNdpUC28AX61x9ZzzqPNaWFElWv93xj0eJFaNyOYA63ic5OzJ88dHpoA5Q==",
"dependencies": {
"@petamoriken/float16": "^3.4.7",
"lerc": "^3.0.0",
"pako": "^2.0.4",
"parse-headers": "^2.0.2",
"quick-lru": "^6.1.1",
"web-worker": "^1.2.0",
"xml-utils": "^1.0.2"
},
"engines": {
"node": ">=10.19"
}
},
"node_modules/geotiff/node_modules/pako": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/pako/-/pako-2.1.0.tgz",
"integrity": "sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug=="
},
"node_modules/geotiff/node_modules/quick-lru": {
"version": "6.1.1",
"resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-6.1.1.tgz",
"integrity": "sha512-S27GBT+F0NTRiehtbrgaSE1idUAJ5bX8dPAQTdylEyNlrdcH5X4Lz7Edz3DYzecbsCluD5zO8ZNEe04z3D3u6Q==",
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/get-caller-file": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
@@ -7950,7 +7831,9 @@
"type": "consulting",
"url": "https://feross.org/support"
}
]
],
"optional": true,
"peer": true
},
"node_modules/ignore": {
"version": "5.2.4",
@@ -8901,16 +8784,6 @@
"resolved": "https://registry.npmjs.org/layout-base/-/layout-base-1.0.2.tgz",
"integrity": "sha512-8h2oVEZNktL4BH2JCOI90iD1yXwL6iNW7KcCKT2QZgQJR2vbqDsldCTPRU9NifTCqHZci57XvQQ15YTu+sTYPg=="
},
"node_modules/leaflet": {
"version": "1.9.4",
"resolved": "https://registry.npmjs.org/leaflet/-/leaflet-1.9.4.tgz",
"integrity": "sha512-nxS1ynzJOmOlHp+iL3FyWqK89GtNL8U8rvlMOsQdTTssxZwCXh8N2NB3GDQOL+YR3XnWyZAxwQixURb+FA74PA=="
},
"node_modules/lerc": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/lerc/-/lerc-3.0.0.tgz",
"integrity": "sha512-Rm4J/WaHhRa93nCN2mwWDZFoRVF18G1f47C+kvQWyHGEZxFpTUi73p7lMVSAndyxGt6lJ2/CFbOcf9ra5p8aww=="
},
"node_modules/levn": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
@@ -9083,6 +8956,14 @@
"yallist": "^3.0.2"
}
},
"node_modules/magic-string": {
"version": "0.16.0",
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.16.0.tgz",
"integrity": "sha512-c4BEos3y6G2qO0B9X7K0FVLOPT9uGrjYwYRLFmDqyl5YMboUviyecnXWp94fJTSMwPw2/sf+CEYt5AGpmklkkQ==",
"dependencies": {
"vlq": "^0.2.1"
}
},
"node_modules/make-dir": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz",
@@ -9142,11 +9023,6 @@
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
"optional": true
},
"node_modules/mapbox-to-css-font": {
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/mapbox-to-css-font/-/mapbox-to-css-font-2.4.2.tgz",
"integrity": "sha512-f+NBjJJY4T3dHtlEz1wCG7YFlkODEjFIYlxDdLIDMNpkSksqTt+l/d4rjuwItxuzkuMFvPyrjzV2lxRM4ePcIA=="
},
"node_modules/markdown-extensions": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/markdown-extensions/-/markdown-extensions-1.1.1.tgz",
@@ -11616,32 +11492,6 @@
"node": ">= 14"
}
},
"node_modules/ol": {
"version": "7.4.0",
"resolved": "https://registry.npmjs.org/ol/-/ol-7.4.0.tgz",
"integrity": "sha512-bgBbiah694HhC0jt8ptEFNRXwgO8d6xWH3G97PCg4bmn9Li5nLLbi59oSrvqUI6VPVwonPQF1YcqJymxxyMC6A==",
"dependencies": {
"earcut": "^2.2.3",
"geotiff": "^2.0.7",
"ol-mapbox-style": "^10.1.0",
"pbf": "3.2.1",
"rbush": "^3.0.1"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/openlayers"
}
},
"node_modules/ol-mapbox-style": {
"version": "10.6.0",
"resolved": "https://registry.npmjs.org/ol-mapbox-style/-/ol-mapbox-style-10.6.0.tgz",
"integrity": "sha512-s86QhCoyyKVRsYkvPzzdWd///bhYh3onWrBq4lNXnCd9G/hS6AoK023kn4zlDESVlTBDTWLz8vhOistp0M3TXA==",
"dependencies": {
"@mapbox/mapbox-gl-style-spec": "^13.23.1",
"mapbox-to-css-font": "^2.4.1",
"ol": "^7.3.0"
}
},
"node_modules/once": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
@@ -11788,11 +11638,6 @@
"url": "https://github.com/sponsors/wooorm"
}
},
"node_modules/parse-headers": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/parse-headers/-/parse-headers-2.0.5.tgz",
"integrity": "sha512-ft3iAoLOB/MlwbNXgzy43SWGP6sQki2jQvAyBg/zDFAgr9bfNWZIUj42Kw2eJIl8kEi4PbgE6U1Zau/HwI75HA=="
},
"node_modules/parse-json": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz",
@@ -11890,18 +11735,6 @@
"node": "*"
}
},
"node_modules/pbf": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/pbf/-/pbf-3.2.1.tgz",
"integrity": "sha512-ClrV7pNOn7rtmoQVF4TS1vyU0WhYRnP92fzbfF75jAIwpnzdJXf8iTd4CMEqO4yUenH6NDqLiwjqlh6QgZzgLQ==",
"dependencies": {
"ieee754": "^1.1.12",
"resolve-protobuf-schema": "^2.1.0"
},
"bin": {
"pbf": "bin/pbf"
}
},
"node_modules/pend": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz",
@@ -12222,11 +12055,6 @@
"url": "https://github.com/sponsors/wooorm"
}
},
"node_modules/protocol-buffers-schema": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/protocol-buffers-schema/-/protocol-buffers-schema-3.6.0.tgz",
"integrity": "sha512-TdDRD+/QNdrCGCE7v8340QyuXd4kIWIgapsE2+n/SaGiSSbomYl4TjHlvIoCWRpE7wFt02EpB35VVA2ImcBVqw=="
},
"node_modules/proxy-from-env": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
@@ -12345,19 +12173,6 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/quickselect": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/quickselect/-/quickselect-2.0.0.tgz",
"integrity": "sha512-RKJ22hX8mHe3Y6wH/N3wCM6BWtjaxIyyUIkpHOvfFnxdI4yD4tBXEBKSbriGujF6jnSVkJrffuo6vxACiSSxIw=="
},
"node_modules/rbush": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/rbush/-/rbush-3.0.1.tgz",
"integrity": "sha512-XRaVO0YecOpEuIvbhbpTrZgoiI6xBlz6hnlr6EHhd+0x9ase6EmeN+hdwwUaJvLcsFFQ8iWVF1GAK1yB0BWi0w==",
"dependencies": {
"quickselect": "^2.0.0"
}
},
"node_modules/react": {
"version": "18.2.0",
"resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz",
@@ -12406,19 +12221,6 @@
"resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
"integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w=="
},
"node_modules/react-leaflet": {
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/react-leaflet/-/react-leaflet-4.2.1.tgz",
"integrity": "sha512-p9chkvhcKrWn/H/1FFeVSqLdReGwn2qmiobOQGO3BifX+/vV/39qhY8dGqbdcPh1e6jxh/QHriLXr7a4eLFK4Q==",
"dependencies": {
"@react-leaflet/core": "^2.1.0"
},
"peerDependencies": {
"leaflet": "^1.9.0",
"react": "^18.0.0",
"react-dom": "^18.0.0"
}
},
"node_modules/react-markdown": {
"version": "8.0.7",
"resolved": "https://registry.npmjs.org/react-markdown/-/react-markdown-8.0.7.tgz",
@@ -12488,21 +12290,6 @@
"react-dom": "^16.8.0-0 || ^17.0.0-0 || ^18.0.0-0"
}
},
"node_modules/react-reconciler": {
"version": "0.29.0",
"resolved": "https://registry.npmjs.org/react-reconciler/-/react-reconciler-0.29.0.tgz",
"integrity": "sha512-wa0fGj7Zht1EYMRhKWwoo1H9GApxYLBuhoAuXN0TlltESAjDssB+Apf0T/DngVqaMyPypDmabL37vw/2aRM98Q==",
"dependencies": {
"loose-envify": "^1.1.0",
"scheduler": "^0.23.0"
},
"engines": {
"node": ">=0.10.0"
},
"peerDependencies": {
"react": "^18.2.0"
}
},
"node_modules/react-refresh": {
"version": "0.14.0",
"resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.0.tgz",
@@ -13041,14 +12828,6 @@
"url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1"
}
},
"node_modules/resolve-protobuf-schema": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/resolve-protobuf-schema/-/resolve-protobuf-schema-2.1.0.tgz",
"integrity": "sha512-kI5ffTiZWmJaS/huM8wZfEMer1eRd7oJQhDuxeCLe3t7N7mX3z94CN0xPxBQxFYQTSNz9T0i+v6inKqSdK8xrQ==",
"dependencies": {
"protocol-buffers-schema": "^3.3.1"
}
},
"node_modules/retext": {
"version": "8.1.0",
"resolved": "https://registry.npmjs.org/retext/-/retext-8.1.0.tgz",
@@ -13170,6 +12949,28 @@
"fsevents": "~2.3.2"
}
},
"node_modules/rollup-plugin-re": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/rollup-plugin-re/-/rollup-plugin-re-1.0.7.tgz",
"integrity": "sha512-TyFf3QaV/eJ/50k4wp5BM0SodGy0Idq0uOgvA1q3gHRwgXLPVX5y3CRKkBuHzKTZPC9CTZX7igKw5UvgjDls8w==",
"dependencies": {
"magic-string": "^0.16.0",
"rollup-pluginutils": "^2.0.1"
}
},
"node_modules/rollup-pluginutils": {
"version": "2.8.2",
"resolved": "https://registry.npmjs.org/rollup-pluginutils/-/rollup-pluginutils-2.8.2.tgz",
"integrity": "sha512-EEp9NhnUkwY8aif6bxgovPHMoMoNr2FulJziTndpt5H9RdwC47GSGuII9XxpSdzVGM0GWrNPHV6ie1LTNJPaLQ==",
"dependencies": {
"estree-walker": "^0.6.1"
}
},
"node_modules/rollup-pluginutils/node_modules/estree-walker": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-0.6.1.tgz",
"integrity": "sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w=="
},
"node_modules/rtl-css-js": {
"version": "1.16.1",
"resolved": "https://registry.npmjs.org/rtl-css-js/-/rtl-css-js-1.16.1.tgz",
@@ -13513,34 +13314,6 @@
"node": ">= 10"
}
},
"node_modules/sort-asc": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/sort-asc/-/sort-asc-0.1.0.tgz",
"integrity": "sha512-jBgdDd+rQ+HkZF2/OHCmace5dvpos/aWQpcxuyRs9QUbPRnkEJmYVo81PIGpjIdpOcsnJ4rGjStfDHsbn+UVyw==",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/sort-desc": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/sort-desc/-/sort-desc-0.1.1.tgz",
"integrity": "sha512-jfZacW5SKOP97BF5rX5kQfJmRVZP5/adDUTY8fCSPvNcXDVpUEe2pr/iKGlcyZzchRJZrswnp68fgk3qBXgkJw==",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/sort-object": {
"version": "0.3.2",
"resolved": "https://registry.npmjs.org/sort-object/-/sort-object-0.3.2.tgz",
"integrity": "sha512-aAQiEdqFTTdsvUFxXm3umdo04J7MRljoVGbBlkH7BgNsMvVNAJyGj7C/wV1A8wHWAJj/YikeZbfuCKqhggNWGA==",
"dependencies": {
"sort-asc": "^0.1.0",
"sort-desc": "^0.1.1"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/source-map": {
"version": "0.5.7",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
@@ -16191,6 +15964,11 @@
"node": ">=12"
}
},
"node_modules/vlq": {
"version": "0.2.3",
"resolved": "https://registry.npmjs.org/vlq/-/vlq-0.2.3.tgz",
"integrity": "sha512-DRibZL6DsNhIgYQ+wNdWDL2SL3bKPlVrRiBqV5yuMm++op8W4kGFtaQfCs4KEJn0wBZcHVHJ3eoywX8983k1ow=="
},
"node_modules/warning": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz",
@@ -16395,11 +16173,6 @@
}
}
},
"node_modules/xml-utils": {
"version": "1.7.0",
"resolved": "https://registry.npmjs.org/xml-utils/-/xml-utils-1.7.0.tgz",
"integrity": "sha512-bWB489+RQQclC7A9OW8e5BzbT8Tu//jtAOvkYwewFr+Q9T9KDGvfzC1lp0pYPEQPEoPQLDkmxkepSC/2gIAZGw=="
},
"node_modules/xtend": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",

View File

@@ -18,7 +18,7 @@
"@heroicons/react": "^2.0.18",
"@octokit/plugin-throttling": "^5.2.2",
"@portaljs/ckan": "^0.0.2",
"@portaljs/components": "0.1.12",
"@portaljs/components": "^0.1.8",
"@portaljs/core": "^1.0.5",
"@portaljs/remark-callouts": "^1.0.5",
"@portaljs/remark-embed": "^1.0.4",

View File

@@ -1,5 +1,4 @@
import { AppProps } from 'next/app';
import '@portaljs/components/styles.css';
import './styles.css';
import { NextSeo } from 'next-seo';

View File

@@ -4,12 +4,7 @@ import { GetStaticProps } from 'next';
import Layout from '../../components/_shared/Layout';
import { formatDate } from '@/utils/formatDate';
import parse from '../../lib/markdown';
import dynamic from 'next/dynamic';
const DataRichDocument = dynamic(
() => import('../../components/DataRichDocument'),
{ ssr: false }
);
import DataRichDocument from '../../components/DataRichDocument';
export default function Page({ source, meta }) {
return (
@@ -51,10 +46,7 @@ export const getStaticProps: GetStaticProps = async ({ params }) => {
export async function getStaticPaths() {
const mddb = await clientPromise;
let allDocuments = await mddb.getFiles({
extensions: ['mdx'],
folder: 'stories',
});
let allDocuments = await mddb.getFiles({ extensions: ['mdx'], folder: 'stories' });
const paths = allDocuments
.filter((page) => page.metadata?.isDraft !== true)

21
nx.json
View File

@@ -5,13 +5,7 @@
"default": {
"runner": "nx/tasks-runners/default",
"options": {
"cacheableOperations": [
"build",
"lint",
"test",
"e2e",
"build-storybook"
]
"cacheableOperations": ["build", "lint", "test", "e2e"]
}
}
},
@@ -36,14 +30,6 @@
"{workspaceRoot}/.eslintrc.json",
"{workspaceRoot}/.eslintignore"
]
},
"build-storybook": {
"inputs": [
"default",
"^production",
"{projectRoot}/.storybook/**/*",
"{projectRoot}/tsconfig.storybook.json"
]
}
},
"namedInputs": {
@@ -53,10 +39,7 @@
"!{projectRoot}/**/?(*.)+(spec|test).[jt]s?(x)?(.snap)",
"!{projectRoot}/tsconfig.spec.json",
"!{projectRoot}/jest.config.[jt]s",
"!{projectRoot}/.eslintrc.json",
"!{projectRoot}/**/*.stories.@(js|jsx|ts|tsx|mdx)",
"!{projectRoot}/.storybook/**/*",
"!{projectRoot}/tsconfig.storybook.json"
"!{projectRoot}/.eslintrc.json"
],
"sharedGlobals": ["{workspaceRoot}/babel.config.json"]
},

17033
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,12 +1,11 @@
{
"name": "portaljs",
"workspaces": [
"./packages/*"
],
"workspaces": ["./packages/*"],
"version": "0.0.0",
"license": "MIT",
"scripts": {
"changeset": "changeset",
"prerelease": "nx run-many --targets=lint,test,build --projects=tag:test",
"release": "changeset publish"
},
"private": true,
@@ -20,20 +19,10 @@
"@nrwl/js": "15.9.2",
"@nrwl/linter": "15.9.2",
"@nrwl/next": "15.9.2",
"@nrwl/react": "^15.9.2",
"@nrwl/react": "15.9.2",
"@nrwl/rollup": "15.9.2",
"@nrwl/storybook": "15.9.2",
"@nrwl/webpack": "15.9.2",
"@nrwl/workspace": "15.9.2",
"@nx/js": "16.6.0",
"@rollup/plugin-url": "^7.0.0",
"@storybook/addon-essentials": "7.0.18",
"@storybook/addon-interactions": "7.0.18",
"@storybook/core-server": "7.0.18",
"@storybook/jest": "~0.1.0",
"@storybook/react-webpack5": "^7.0.18",
"@storybook/test-runner": "^0.11.0",
"@storybook/testing-library": "~0.2.0",
"@svgr/rollup": "^6.1.2",
"@swc/core": "^1.2.173",
"@swc/helpers": "~0.5.0",
@@ -49,7 +38,6 @@
"@typescript-eslint/parser": "^5.36.1",
"babel-jest": "^29.4.1",
"chai": "^4.3.7",
"core-js": "^3.6.5",
"cypress": "^12.2.0",
"eslint": "~8.15.0",
"eslint-config-next": "13.1.1",
@@ -69,16 +57,11 @@
"react-test-renderer": "18.2.0",
"rehype-stringify": "^9.0.3",
"remark": "^14.0.3",
"storybook-tailwind-dark-mode": "^1.0.22",
"swc-loader": "0.1.15",
"ts-jest": "^29.0.5",
"ts-node": "10.9.1",
"typescript": "~4.9.5",
"unist-util-select": "^4.0.3",
"unist-util-visit": "^4.1.2"
},
"dependencies": {
"react": "^18.2.0",
"react-dom": "^18.2.0"
}
}

View File

@@ -1,7 +1,6 @@
import 'tailwindcss/tailwind.css'
import '../src/index.css'
import type { Preview } from '@storybook/react';
const preview: Preview = {

View File

@@ -1,3 +0,0 @@
User-agent: *
Allow: /$
Disallow: /

View File

@@ -1,31 +1,5 @@
# @portaljs/components
## 0.3.1
### Patch Changes
- [#980](https://github.com/datopian/portaljs/pull/980) [`38738525`](https://github.com/datopian/portaljs/commit/3873852567b1aab4827a716bd588bd5de3223e2b) Thanks [@demenech](https://github.com/demenech)! - Fix missing CSS styles for PDF component
## 0.3.0
### Minor Changes
- [`2e13c1b7`](https://github.com/datopian/portaljs/commit/2e13c1b738ddac91a9419f5c0484406328bd1cd3) Thanks [@demenech](https://github.com/demenech)! - PDF and Excel components
- [#973](https://github.com/datopian/portaljs/pull/973) [`f3c2a2ff`](https://github.com/datopian/portaljs/commit/f3c2a2ffa7dcf9693bd25318c719ce58d27070b8) Thanks [@demenech](https://github.com/demenech)! - Implement <Excel /> component
## 0.2.0
### Minor Changes
- [`80c6221a`](https://github.com/datopian/portaljs/commit/80c6221a05733f8c1dd0431bed4d72b1f9d7d636) Thanks [@luccasmmg](https://github.com/luccasmmg)! - Added the OpenLayers and the Map(Leaflet based) component
## 0.1.12
### Patch Changes
- [#960](https://github.com/datopian/portaljs/pull/960) [`add2f6d0`](https://github.com/datopian/portaljs/commit/add2f6d0f372434eb996d59d6faf5cd06530c932) Thanks [@demenech](https://github.com/demenech)! - Fix width of the <LineChart /> component
## 0.1.11
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "@portaljs/components",
"version": "0.3.1",
"version": "0.1.11",
"type": "module",
"description": "https://portaljs.org",
"keywords": [
@@ -12,14 +12,12 @@
],
"scripts": {
"dev": "npm run storybook",
"example": "vite",
"build": "tsc && vite build && npm run build-tailwind && npm run fix-leaflet",
"build": "tsc && vite build && npm run build-tailwind",
"lint": "eslint src --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
"storybook": "storybook dev -p 6006",
"build-storybook": "storybook build && cp ./.storybook/robots.txt ./storybook-static",
"build-tailwind": "NODE_ENV=production npx tailwindcss --postcss -c tailwind.config.js -i src/index.css -o ./dist/style.css --minify",
"prepare": "npm run build",
"fix-leaflet": "node ./scripts/fix-leaflet.cjs"
"build-storybook": "storybook build",
"build-tailwind": "NODE_ENV=production npx tailwindcss -o ./dist/styles.css --minify",
"prepare": "npm run build"
},
"peerDependencies": {
"react": "^18.2.0",
@@ -28,29 +26,19 @@
"dependencies": {
"@githubocto/flat-ui": "^0.14.1",
"@heroicons/react": "^2.0.17",
"@planet/maps": "^8.1.0",
"@tanstack/react-table": "^8.8.5",
"ag-grid-react": "^30.0.4",
"chroma-js": "^2.4.2",
"flexsearch": "0.7.21",
"leaflet": "^1.9.4",
"next-mdx-remote": "^4.4.1",
"ol": "^7.4.0",
"papaparse": "^5.4.1",
"postcss-url": "^10.1.3",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-hook-form": "^7.43.9",
"react-leaflet": "^4.2.1",
"react-query": "^3.39.3",
"react-vega": "^7.6.0",
"rollup-plugin-re": "^1.0.7",
"vega": "5.25.0",
"vega-lite": "5.1.0",
"vitest": "^0.31.4",
"@react-pdf-viewer/core": "3.6.0",
"@react-pdf-viewer/default-layout": "3.6.0",
"pdfjs-dist": "2.15.349",
"xlsx": "^0.18.5"
"vitest": "^0.31.4"
},
"devDependencies": {
"@storybook/addon-essentials": "^7.0.7",
@@ -60,16 +48,13 @@
"@storybook/react": "^7.0.7",
"@storybook/react-vite": "^7.0.7",
"@storybook/testing-library": "^0.0.14-next.2",
"@swc/core": "^1.3.68",
"@types/flexsearch": "^0.7.3",
"@types/leaflet": "^1.9.3",
"@types/papaparse": "^5.3.7",
"@types/react": "^18.0.28",
"@types/react-dom": "^18.0.11",
"@typescript-eslint/eslint-plugin": "^5.57.1",
"@typescript-eslint/parser": "^5.57.1",
"@vitejs/plugin-react": "^4.0.0",
"@vitejs/plugin-react-swc": "^3.3.2",
"autoprefixer": "^10.4.14",
"eslint": "^8.38.0",
"eslint-plugin-react-hooks": "^4.6.0",
@@ -77,15 +62,12 @@
"eslint-plugin-storybook": "^0.6.11",
"json": "^11.0.0",
"postcss": "^8.4.23",
"postcss-import": "^15.1.0",
"postcss-import-url": "^7.2.0",
"prop-types": "^15.8.1",
"storybook": "^7.0.7",
"tailwindcss": "^3.3.2",
"typescript": "^5.0.2",
"vite": "^4.3.2",
"vite-plugin-dts": "^2.3.0",
"vite-plugin-swc-only": "^0.1.18"
"vite-plugin-dts": "^2.3.0"
},
"files": [
"dist"
@@ -99,7 +81,7 @@
"require": "./dist/components.umd.js"
},
"./styles.css": {
"import": "./dist/style.css"
"import": "./dist/styles.css"
}
},
"publishConfig": {

View File

@@ -1,10 +1,6 @@
console.log('PostCSS');
export default {
plugins: {
'postcss-import': {},
'postcss-url': { url: 'inline' },
tailwindcss: {},
autoprefixer: {},
},
};
}

View File

@@ -1,6 +0,0 @@
const fs = require('fs');
const path = require('path');
const leafletPath = path.join(require.resolve('leaflet'), '../')
fs.cpSync(`${leafletPath}images`,'./dist/images', { recursive: true });

View File

@@ -1,99 +0,0 @@
import { useEffect, useState } from 'react';
import LoadingSpinner from './LoadingSpinner';
import { read, utils } from 'xlsx';
import { AgGridReact } from 'ag-grid-react';
import 'ag-grid-community/styles/ag-grid.css';
import 'ag-grid-community/styles/ag-theme-alpine.css';
export type ExcelProps = {
url: string;
};
export function Excel({ url }: ExcelProps) {
const [isLoading, setIsLoading] = useState<boolean>(false);
const [activeSheetName, setActiveSheetName] = useState<string>();
const [workbook, setWorkbook] = useState<any>();
const [rows, setRows] = useState<any>();
const [cols, setCols] = useState<any>();
const loadSpreadsheet = (wb: any, name: string) => {
setActiveSheetName(name);
const ws = wb.Sheets[name];
const range = utils.decode_range(ws['!ref'] || 'A1');
const columns = Array.from({ length: range.e.c + 1 }, (_, i) => ({
field: utils.encode_col(i),
}));
const rowsAr = utils.sheet_to_json(ws, { header: 1 });
const rows = rowsAr.map((row) => {
const obj = {};
columns.forEach((col, i) => {
obj[col.field] = row[i];
});
return obj;
});
setRows(rows);
setCols(columns);
};
useEffect(() => {
setIsLoading(true);
fetch(url)
.then((res) => res.arrayBuffer())
.then((f) => {
const wb = read(f);
setWorkbook(wb);
loadSpreadsheet(wb, wb.SheetNames[0]);
setIsLoading(false);
});
}, [url]);
return isLoading ? (
<div className="w-full flex items-center justify-center w-[600px] h-[300px]">
<LoadingSpinner />
</div>
) : (
<>
{cols && rows && (
<div>
<div
className="ag-theme-alpine"
style={{ height: 400, width: '100%' }}
>
<AgGridReact
rowData={rows}
columnDefs={cols}
defaultColDef={{
resizable: true,
minWidth: 200,
flex: 1,
sortable: true,
filter: true,
}}
></AgGridReact>
</div>
<div className="border-t">
{workbook.SheetNames.map((name: string, idx: number) => {
return (
<>
<button
key={idx}
className={`text-sm px-3 pb-2 pt-4 border-b border-l border-r ${
name == activeSheetName ? 'font-semibold' : ''
}`}
onClick={() => loadSpreadsheet(workbook, name)}
>
{name}
</button>
</>
);
})}
</div>
</div>
)}
</>
);
}

View File

@@ -35,7 +35,7 @@ export function LineChart({
const spec = {
$schema: 'https://vega.github.io/schema/vega-lite/v5.json',
title,
width: 'container',
width: 600,
height: 300,
mark: {
type: 'line',

View File

@@ -1,233 +0,0 @@
import { useEffect, useState } from 'react';
import LoadingSpinner from './LoadingSpinner';
import loadData from '../lib/loadData';
import chroma from 'chroma-js';
import {
MapContainer,
TileLayer,
GeoJSON as GeoJSONLayer,
LayersControl,
} from 'react-leaflet';
import 'leaflet/dist/leaflet.css';
import * as L from 'leaflet';
export type MapProps = {
layers: {
data: string | GeoJSON.GeoJSON;
name: string;
colorScale?: {
starting: string;
ending: string;
};
tooltip?:
| {
propNames: string[];
}
| boolean;
_id?: number;
}[];
title?: string;
center?: { latitude: number | undefined; longitude: number | undefined };
zoom?: number;
};
export function Map({
layers = [
{
data: null,
name: null,
colorScale: { starting: 'blue', ending: 'red' },
tooltip: true,
},
],
center = { latitude: 45, longitude: 45 },
zoom = 2,
title = '',
}: MapProps) {
const [isLoading, setIsLoading] = useState<boolean>(false);
const [layersData, setLayersData] = useState<any>([]);
useEffect(() => {
const loadDataPromises = layers.map(async (layer) => {
let layerData: any;
if (typeof layer.data === 'string') {
// If "data" is string, assume it's a URL
setIsLoading(true);
layerData = await loadData(layer.data).then((res: any) => {
return JSON.parse(res);
});
} else {
// Else, expect raw GeoJSON
layerData = layer.data;
}
if (layer.colorScale) {
const colorScaleAr = chroma
.scale([layer.colorScale.starting, layer.colorScale.ending])
.mode('lch')
.colors(layerData.features.length);
layerData.features.forEach((feature, i) => {
// Only style if the feature doesn't have a color prop
if (feature.color === undefined) {
feature.color = colorScaleAr[i];
}
});
}
return { name: layer.name, data: layerData };
});
Promise.all(loadDataPromises).then((values) => {
setLayersData(values);
setIsLoading(false);
});
}, []);
return isLoading ? (
<div className="w-full flex items-center justify-center w-[600px] h-[300px]">
<LoadingSpinner />
</div>
) : (
<MapContainer
center={[center.latitude, center.longitude]}
zoom={zoom}
scrollWheelZoom={false}
className="h-80 w-full"
// @ts-ignore
whenReady={(map: any) => {
// Enable zoom using scroll wheel
map.target.scrollWheelZoom.enable();
// Create the title box
var info = new L.Control() as any;
info.onAdd = function () {
this._div = L.DomUtil.create('div', 'info');
this.update();
return this._div;
};
info.update = function () {
this._div.innerHTML = `<h4 style="font-weight: 600; background: #f9f9f9; padding: 5px; border-radius: 5px; color: #464646;">${title}</h4>`;
};
if (title) info.addTo(map.target);
}}
>
<TileLayer
attribution='&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
/>
<LayersControl position="bottomright">
{layers.map((layer) => {
const data = layersData.find(
(layerData) => layerData.name === layer.name
)?.data;
return (
data && (
<LayersControl.Overlay key={layer.name} checked name={layer.name}>
<GeoJSONLayer
data={data}
// @ts-ignore
pointToLayer={(feature, latlng) => {
// This resolver an issue in which the bundled map was
// not finding the images
const leafletBase =
'https://unpkg.com/leaflet@1.9.4/dist/images/';
const icon = new L.Icon({
iconUrl: leafletBase + 'marker-icon.png',
iconRetinaUrl: leafletBase + 'marker-icon-2x.png',
shadowUrl: leafletBase + 'marker-shadow.png',
iconSize: [25, 41],
iconAnchor: [12, 41],
popupAnchor: [1, -34],
tooltipAnchor: [16, -28],
shadowSize: [41, 41],
});
const iconMarker = L.marker(latlng, { icon });
return iconMarker;
}}
style={(geoJsonFeature: any) => {
// Set the fill color of each feature when appliable
if (
!['Point', 'MultiPoint'].includes(geoJsonFeature.type)
) {
return { color: geoJsonFeature?.color };
}
}}
eventHandlers={{
add: (e) => {
const featureGroup = e.target;
const tooltip = layer.tooltip;
featureGroup.eachLayer((featureLayer) => {
const feature = featureLayer.feature;
const geometryType = feature.geometry.type;
if (tooltip) {
const featurePropNames = Object.keys(
feature.properties
);
let includedFeaturePropNames;
if (tooltip === true) {
includedFeaturePropNames = featurePropNames;
} else {
includedFeaturePropNames = tooltip.propNames.filter(
(name) => featurePropNames.includes(name)
);
}
if (includedFeaturePropNames) {
const tooltipContent = includedFeaturePropNames
.map(
(name) =>
`<b>${name}:</b> ${feature.properties[name]}`
)
.join('<br />');
featureLayer.bindTooltip(tooltipContent, {
direction: 'center',
});
}
}
featureLayer.on({
mouseover: (event) => {
if (
['Polygon', 'MultiPolygon'].includes(geometryType)
) {
event.target.setStyle({
fillOpacity: 0.5,
});
}
},
mouseout: (event) => {
if (
['Polygon', 'MultiPolygon'].includes(geometryType)
) {
event.target.setStyle({
fillOpacity: 0.2,
});
}
},
});
});
},
}}
/>
;
</LayersControl.Overlay>
)
);
})}
</LayersControl>
</MapContainer>
);
}

View File

@@ -1,84 +0,0 @@
import React, { useContext, useEffect, useState } from 'react';
export const Controls = ({ children }) => {
return <div>{children}</div>;
};
import { FullScreen, Zoom } from 'ol/control';
import { MapContext } from './Map';
export const FullScreenControl = () => {
const { map } = useContext(MapContext);
useEffect(() => {
if (!map) return;
let fullScreenControl = new FullScreen({
className: 'ml-1 flex flex-col w-8 items-center mt-2',
activeClassName:
'w-full inline-flex justify-center items-center rounded-t-md bg-white px-2 py-2 text-gray-400 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus:z-10 text-sm',
inactiveClassName:
'inline-flex w-full justify-center items-center rounded-t-md bg-white px-2 py-2 text-gray-400 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus:z-10 text-sm',
});
let zoomControl = new Zoom({
className: 'ml-1 flex flex-col w-8 items-center',
zoomInClassName:
'inline-flex w-full justify-center items-center bg-white px-2 py-2 text-gray-400 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus:z-10 text-sm',
zoomOutClassName:
'inline-flex w-full justify-center items-center rounded-b-md bg-white px-2 py-2 text-gray-400 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus:z-10 text-sm',
});
map.controls.push(fullScreenControl);
map.controls.push(zoomControl);
return () => {
map.controls.remove(zoomControl);
map.controls.remove(fullScreenControl);
};
}, [map]);
return null;
};
//build a list of checkboxes in react
export const ListOfCheckboxes = ({ layers, shownLayers, setShownLayers }) => {
//layers is an array of url and name
function addLayer(layer) {
setShownLayers([...shownLayers, layer.url]);
}
function removeLayer(layer) {
setShownLayers(shownLayers.filter((l) => l !== layer.url));
}
return (
<div>
<h3 className="mb-4 font-semibold text-gray-900 ">Layers</h3>
<ul className="w-48 text-sm font-medium text-gray-900 bg-white border border-gray-200 rounded-lg ">
{layers.map((layer, index) => (
<li
key={index}
className="w-full border-b border-gray-200 rounded-t-lg "
>
<div className="flex items-center pl-3">
<input
id={layer.name}
type="checkbox"
defaultChecked={shownLayers.includes(layer.url)}
onClick={() =>
shownLayers.includes(layer.url)
? removeLayer(layer)
: addLayer(layer)
}
value={true}
className="w-4 h-4 text-blue-600 bg-gray-100 border-gray-300 rounded focus:ring-blue-500 focus:ring-2 "
></input>
<label
htmlFor={layer.name}
className="w-full py-3 ml-2 text-sm font-medium text-gray-90"
>
{layer.name}
</label>
</div>
</li>
))}
</ul>
</div>
);
};

View File

@@ -1,29 +0,0 @@
import { useContext, useEffect, useState } from 'react';
import HeatMap from 'ol/layer/Heatmap';
import { MapContext } from './Map';
const HeatMapLayer = ({ source, style, zIndex = 0 }) => {
const { map } = useContext(MapContext);
const [heatMapLayer, setHeatMapLayer] = useState(null);
useEffect(() => {
if (!map) return;
let heatMapLayer = new HeatMap({
source,
style,
blur: parseInt(5, 10),
radius: parseInt(5, 10),
});
map.addLayer(heatMapLayer);
setHeatMapLayer(heatMapLayer);
heatMapLayer.setZIndex(zIndex);
return () => {
if (map) {
map.removeLayer(heatMapLayer);
}
};
}, [map]);
useEffect(() => {
heatMapLayer && heatMapLayer.setZIndex(zIndex);
}, [zIndex]);
return null;
};
export default HeatMapLayer;

View File

@@ -1,4 +0,0 @@
import React from 'react';
export const Layers = ({ children }) => {
return <div>{children}</div>;
};

View File

@@ -1,50 +0,0 @@
import React, { useRef, useState, useEffect } from 'react';
import * as ol from 'ol';
export const MapContext = new React.createContext();
const Map = ({ children, zoom, center, setSelected }) => {
const mapRef = useRef();
const [map, setMap] = useState(null);
// on component mount
useEffect(() => {
let options = {
view: new ol.View({ zoom, center }),
layers: [],
controls: [],
overlays: [],
};
let mapObject = new ol.Map(options);
mapObject.setTarget(mapRef.current);
setMap(mapObject);
return () => mapObject.setTarget(undefined);
}, []);
useEffect(() => {
if (map) {
if (setSelected !== null) {
let selected = null;
map.on('pointermove', function (e) {
map.forEachFeatureAtPixel(e.pixel, function (f) {
selected = f;
return true;
});
if (selected) {
setSelected(selected);
} else {
setSelected(null);
}
});
}
}
}, [map]);
return (
<MapContext.Provider value={{ map }}>
<div ref={mapRef} className="w-full" style={{height: '500px'}}>
{children}
</div>
</MapContext.Provider>
);
};
export default Map;

View File

@@ -1,136 +0,0 @@
import { useEffect, useState } from 'react';
import Map from './Map';
import { Layers } from './Layers';
import { Fill, Icon, Style } from 'ol/style';
import * as olSource from 'ol/source';
import TileLayer from './TileLayer';
import { fromLonLat } from 'ol/proj';
import VectorLayer from './VectorLayer';
import { Vector as VectorSource } from 'ol/source';
import GeoJSON from 'ol/format/GeoJSON';
import KML from 'ol/format/KML';
import { colors } from './colors';
import { FullScreenControl, Controls, ListOfCheckboxes } from './Controls';
import HeatMapLayer from './HeatMapLayer';
function osm() {
return new olSource.OSM();
}
const formats = {
geojson: new GeoJSON(),
kml: new KML(),
};
interface OpenLayersProps {
layers: {
url: string;
name?: string;
format?: string;
heatmap?: boolean;
}[];
center?: [number, number];
zoom?: number;
popup?: (selected: any) => JSX.Element;
}
export default function OpenLayers({
layers,
center = [0, 0],
zoom = 1,
popup,
}: OpenLayersProps) {
const [shownLayers, setShownLayers] = useState(
layers.map((layer) => layer.url)
);
const [selected, setSelected] = useState(null);
const [style, setStyle] = useState(null);
useEffect(() => {
const style = new Style({
fill: new Fill({
color: '#eeeeee',
}),
image: new Icon({
anchor: [0.5, 46],
anchorXUnits: 'fraction',
anchorYUnits: 'pixels',
width: 18,
height: 28,
src: 'https://upload.wikimedia.org/wikipedia/commons/thumb/a/aa/Google_Maps_icon_%282020%29.svg/418px-Google_Maps_icon_%282020%29.svg.png?20200218211225',
}),
});
setStyle(style);
}, []);
return (
<div className="relative">
<Map
center={fromLonLat(center)}
zoom={zoom}
setSelected={popup ? setSelected : null}
>
<Layers>
<TileLayer source={osm()} zIndex={0} />
{layers.map((layer, index) =>
!layer.heatmap ? (
<VectorLayer
key={index}
zIndex={shownLayers.includes(layer.url) ? 1 : -1}
source={
new VectorSource({
url: layer.url,
format: layer.format
? formats[layer.format]
: new GeoJSON(),
})
}
style={function (feature) {
const id = feature.getId();
const color = feature.get('COLOR') || colors[id % 1302].hex;
style.getFill().setColor(color);
return style;
}}
/>
) : (
<HeatMapLayer
key={index}
zIndex={shownLayers.includes(layer.url) ? 1 : -1}
source={
new VectorSource({
url: layer.url,
format: layer.format
? formats[layer.format]
: new GeoJSON(),
})
}
style={function (feature) {
const color =
feature.get('COLOR') || colors[feature.ol_uid % 1302].hex;
style.getFill().setColor(color);
return style;
}}
/>
)
)}
</Layers>
{/* add a floating pane that will output the ListOfCheckboxes component using tailwind*/}
<div className="absolute bottom-0 right-0 m-4 p-4 z-50 bg-white rounded-lg shadow-xl">
<ListOfCheckboxes
layers={layers}
shownLayers={shownLayers}
setShownLayers={setShownLayers}
/>
</div>
{popup && selected && (
<div className="absolute bottom-0 left-0 m-4 p-4 z-50 bg-white rounded-lg shadow-xl">
{popup(selected)}
</div>
)}
<Controls>
<FullScreenControl />
</Controls>
</Map>
</div>
);
}

View File

@@ -1,23 +0,0 @@
import { useContext, useEffect } from 'react';
import OLTileLayer from 'ol/layer/Tile';
import { MapContext } from './Map';
const TileLayer = ({ source, zIndex = 0 }) => {
const { map } = useContext(MapContext);
useEffect(() => {
if (!map) return;
let tileLayer = new OLTileLayer({
source,
zIndex,
});
map.addLayer(tileLayer);
tileLayer.setZIndex(zIndex);
return () => {
if (map) {
map.removeLayer(tileLayer);
}
};
}, [map]);
return null;
};
export default TileLayer;

View File

@@ -1,33 +0,0 @@
import { useContext, useEffect, useState } from 'react';
import OLVectorLayer from 'ol/layer/Vector';
import { MapContext } from './Map';
const VectorLayer = ({ source, style, zIndex = 0 }) => {
const { map } = useContext(MapContext);
const [vectorLayer, setVectorLayer] = useState(null);
useEffect(() => {
if (!map) return;
let vectorLayer = new OLVectorLayer({
source,
style,
});
const vectorSource = vectorLayer.getSource();
vectorSource.on('featuresloadend', function () {
vectorSource.getFeatures().forEach((feature, index) => {
feature.setId(index);
});
});
map.addLayer(vectorLayer);
setVectorLayer(vectorLayer);
vectorLayer.setZIndex(zIndex);
return () => {
if (map) {
map.removeLayer(vectorLayer);
}
};
}, [map]);
useEffect(() => {
vectorLayer && vectorLayer.setZIndex(zIndex);
}, [zIndex]);
return null;
};
export default VectorLayer;

File diff suppressed because it is too large Load Diff

View File

@@ -1,32 +0,0 @@
// Core viewer
import { Viewer, Worker, SpecialZoomLevel } from '@react-pdf-viewer/core';
import { defaultLayoutPlugin } from '@react-pdf-viewer/default-layout';
// Import styles
import '@react-pdf-viewer/core/lib/styles/index.css';
import '@react-pdf-viewer/default-layout/lib/styles/index.css';
export interface PdfViewerProps {
url: string;
layout: boolean;
parentClassName?: string;
}
export function PdfViewer({
url,
layout = false,
parentClassName,
}: PdfViewerProps) {
const defaultLayoutPluginInstance = defaultLayoutPlugin();
return (
<Worker workerUrl="https://unpkg.com/pdfjs-dist@2.15.349/build/pdf.worker.js">
<div className={parentClassName}>
<Viewer
defaultScale={SpecialZoomLevel.PageWidth}
fileUrl={url}
plugins={layout ? [defaultLayoutPluginInstance] : []}
/>
</div>
</Worker>
);
}

View File

@@ -1,6 +0,0 @@
/* Temporary fix for a size issue with FlatUiTable loading indicator on Firefox */
@layer base {
svg[tw^='animate-pulse w-12'] {
max-width: 100px;
}
}

View File

@@ -1,11 +1,10 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
@import "tailwindcss/base";
@import "tailwindcss/components";
@import "include";
@import "leaflet";
@import '@react-pdf-viewer/core/lib/styles/index.css';
@import '@react-pdf-viewer/default-layout/lib/styles/index.css';
@import 'ag-grid-community/styles/ag-grid.css';
@import 'ag-grid-community/styles/ag-theme-alpine.css';
@import "tailwindcss/utilities";
/* Temporary fix for a size issue with FlatUiTable loading indicator on Firefox */
@layer base {
svg[tw^='animate-pulse w-12'] {
max-width: 100px;
}
}

View File

@@ -1,10 +1,6 @@
export * from './components/Table';
export * from './components/Catalog';
export * from './components/LineChart';
export * from './components/Vega';
export * from './components/VegaLite';
export * from './components/FlatUiTable';
export * from './components/OpenLayers/OpenLayers';
export * from './components/Map';
export * from './components/PdfViewer';
export * from "./components/Excel";
export * from "./components/Table";
export * from "./components/Catalog";
export * from "./components/LineChart";
export * from "./components/Vega";
export * from "./components/VegaLite";
export * from "./components/FlatUiTable";

View File

@@ -1,171 +0,0 @@
/**
* Typescript types for the GeoJSON RFC7946 specification. This is not fully RFC-compliant due to lack of support for
* ranged number data types.
*
* See https://tools.ietf.org/html/rfc7946
*/
export declare namespace GeoJSON {
/**
* Inside this document, the term "geometry type" refers to seven case-sensitive strings: "Point", "MultiPoint",
* "LineString", "MultiLineString", "Polygon", "MultiPolygon", and "GeometryCollection".
*/
export type Geometry = Point | MultiPoint | LineString | MultiLineString | Polygon | MultiPolygon
| GeometryCollection;
export type GeometryType = Geometry["type"];
/**
* ...the term "GeoJSON types" refers to nine case-sensitive strings: "Feature", "FeatureCollection", and the
* geometry types listed above.
*/
export type GeoJson = Geometry | Feature | FeatureCollection;
export type GeoJsonType = GeoJson["type"];
// types
/**
* A position is an array of numbers. There MUST be two or more elements. The first two elements are longitude and
* latitude, or easting and northing, precisely in that order and using decimal numbers. Altitude or elevation MAY
* be included as an optional third element.
*
* Implementations SHOULD NOT extend positions beyond three elements because the semantics of extra elements are
* unspecified and ambiguous.
*/
export type Position = [longitude: number, latitude: number, elevation?: number]
export type Record = { [key in string | number]: unknown };
/**
* Properties inherit to all GeoJSON types
*/
export interface GeometryBase extends Record {
/**
* A GeoJSON object MAY have a member named "bbox" to include information on the coordinate range for its
* Geometries, Features, or FeatureCollections. The value of the bbox member MUST be an array of length 2*n
* where n is the number of dimensions represented in the contained geometries, with all axes of the most
* southwesterly point followed by all axes of the more northeasterly point. The axes order of a bbox follows
* the axes order of geometries.
*/
bbox?: number[];
/**
* A GeoJSON object MAY have other members.
*
* Members not described in this specification ("foreign members") MAY be used in a GeoJSON document. Note that
* support for foreign members can vary across implementations, and no normative processing model for foreign
* members is defined.
*/
}
// geometry types
export interface Point extends GeometryBase {
type: "Point";
/**
* For type "Point", the "coordinates" member is a single position.
*/
coordinates: Position;
}
export interface MultiPoint extends GeometryBase {
type: "MultiPoint";
/**
* For type "MultiPoint", the "coordinates" member is an array of positions.
*/
coordinates: Position[];
}
export interface LineString extends GeometryBase {
type: "LineString";
/**
* For type "LineString", the "coordinates" member is an array of two or more positions.
*/
coordinates: { 0: Position, 1: Position } & Position[]
}
export interface MultiLineString extends GeometryBase {
type: "MultiLineString";
/**
* For type "MultiLineString", the "coordinates" member is an array of LineString coordinate arrays.
*/
coordinates: LineString["coordinates"][];
}
/**
* To specify a constraint specific to Polygons, it is useful to introduce the concept of a linear ring:
* - A linear ring is a closed LineString with four or more positions.
* - The first and last positions are equivalent, and they MUST contain identical values; their representation
* SHOULD also be identical.
* - A linear ring is the boundary of a surface or the boundary of a hole in a surface.
* - A linear ring MUST follow the right-hand rule with respect to the area it bounds, i.e., exterior rings are
* counterclockwise, and holes are clockwise.
*/
export type LinearRing = { 0: Position, 1: Position, 2: Position, 3: Position } & Position[];
export interface Polygon extends GeometryBase {
type: "Polygon";
/**
* For type "Polygon", the "coordinates" member MUST be an array of linear ring coordinate arrays.
*
* For Polygons with more than one of these rings, the first MUST be the exterior ring, and any others MUST be
* interior rings. The exterior ring bounds the surface, and the interior rings (if present) bound holes within
* the surface.
*/
coordinates: LinearRing[];
}
export interface MultiPolygon extends GeometryBase {
type: "MultiPolygon";
/**
* For type "MultiPolygon", the "coordinates" member is an array of Polygon coordinate arrays.
*/
coordinates: Polygon["coordinates"][];
}
export interface GeometryCollection {
/**
* A GeoJSON object with type "GeometryCollection" is a Geometry object.
*/
type: "GeometryCollection";
/**
* A GeometryCollection has a member with the name "geometries". The value of "geometries" is an array. Each
* element of this array is a GeoJSON Geometry object. It is possible for this array to be empty.
*/
geometries: Geometry[];
}
// GeoJSON types
export interface Feature {
/**
* A Feature object has a "type" member with the value "Feature".
*/
type: "Feature";
/**
* If a Feature has a commonly used identifier, that identifier SHOULD be included as a member of the Feature object
* with the name "id", and the value of this member is either a JSON string or number.
*/
id?: string | number;
/**
* A Feature object has a member with the name "geometry". The value of the geometry member SHALL be either a
* Geometry object as defined above or, in the case that the Feature is unlocated, a JSON null value.
*/
geometry: Geometry | null;
/**
* A Feature object has a member with the name "properties". The value of the properties member is an object
* (any JSON object or a JSON null value).
*/
properties: Record | null;
}
export interface FeatureCollection {
/**
* A GeoJSON object with the type "FeatureCollection" is a FeatureCollection object.
*/
type: "FeatureCollection";
/**
* A FeatureCollection object has a member with the name "features". The value of "features" is a JSON array. Each
* element of the array is a Feature object as defined above. It is possible for this array to be empty.
*/
features: Feature[];
}
}

View File

@@ -1,34 +0,0 @@
import type { Meta, StoryObj } from '@storybook/react';
import { Excel, ExcelProps } from '../src/components/Excel';
// More on how to set up stories at: https://storybook.js.org/docs/react/writing-stories/introduction
const meta: Meta = {
title: 'Components/Excel',
component: Excel,
tags: ['autodocs'],
argTypes: {
url: {
description:
'Url of the file to be displayed e.g.: "https://url.to/data.csv"',
},
},
};
export default meta;
type Story = StoryObj<ExcelProps>;
export const SingleSheet: Story = {
name: 'Excel file with just one sheet',
args: {
url: 'https://sheetjs.com/pres.xlsx',
},
};
export const MultipleSheet: Story = {
name: 'Excel file with multiple sheets',
args: {
url: 'https://storage.portaljs.org/IC-Gantt-Chart-Project-Template-8857.xlsx',
},
};

View File

@@ -1,91 +0,0 @@
import type { Meta, StoryObj } from '@storybook/react';
import { Map, MapProps } from '../src/components/Map';
// More on how to set up stories at: https://storybook.js.org/docs/react/writing-stories/introduction
const meta: Meta = {
title: 'Components/Map',
component: Map,
tags: ['autodocs'],
argTypes: {
layers: {
description:
'Data to be displayed.\n\n GeoJSON Object \n\nOR\n\n URL to GeoJSON Object',
},
title: {
description: 'Title to display on the map. Optional.',
},
center: {
description: 'Initial coordinates of the center of the map',
},
zoom: {
description: 'Zoom level',
},
},
};
export default meta;
type Story = StoryObj<MapProps>;
// More on writing stories with args: https://storybook.js.org/docs/react/writing-stories/args
export const GeoJSONPolygons: Story = {
name: 'GeoJSON polygons map',
args: {
layers: [
{
data: 'https://d2ad6b4ur7yvpq.cloudfront.net/naturalearth-3.3.0/ne_10m_geography_marine_polys.geojson',
name: 'Polygons',
tooltip: { propNames: ['name'] },
colorScale: {
starting: '#ff0000',
ending: '#00ff00',
},
},
],
title: 'Seas and Oceans Map',
center: { latitude: 45, longitude: 0 },
zoom: 2,
},
};
export const GeoJSONPoints: Story = {
name: 'GeoJSON points map',
args: {
layers: [
{
data: 'https://opendata.arcgis.com/datasets/9c58741995174fbcb017cf46c8a42f4b_25.geojson',
name: 'Points',
tooltip: { propNames: ['Location'] },
},
],
title: 'Roads in York',
center: { latitude: 53.9614, longitude: -1.0739 },
zoom: 12,
},
};
export const GeoJSONMultipleLayers: Story = {
name: 'GeoJSON polygons and points map',
args: {
layers: [
{
data: 'https://opendata.arcgis.com/datasets/9c58741995174fbcb017cf46c8a42f4b_25.geojson',
name: 'Points',
tooltip: true,
},
{
data: 'https://d2ad6b4ur7yvpq.cloudfront.net/naturalearth-3.3.0/ne_10m_geography_marine_polys.geojson',
name: 'Polygons',
tooltip: true,
colorScale: {
starting: '#ff0000',
ending: '#00ff00',
},
},
],
title: 'Polygons and points',
center: { latitude: 45, longitude: 0 },
zoom: 2,
},
};

View File

@@ -1,136 +0,0 @@
import type { Meta, StoryObj } from '@storybook/react';
import React from 'react';
import OpenLayers from '../src/components/OpenLayers/OpenLayers';
const meta: Meta = {
title: 'Components/OpenLayers',
component: OpenLayers,
argTypes: {
layers: {
description: 'Layers to be added to the map',
control: {
type: 'array',
},
},
center: {
description: 'Center of the map',
defaultValue: [0, 0],
control: {
type: 'array',
},
},
zoom: {
description: 'Zoom level of the map',
defaultValue: 1,
control: {
type: 'number',
},
},
},
};
export default meta;
type Story = StoryObj<any>;
export const Secondary: Story = {
name: 'Map with OpenLayers',
args: {
layers: [
{
url: 'https://openlayers.org/data/vector/ecoregions.json',
name: 'Ecoregions',
},
],
},
};
export const Primary: Story = {
name: 'Map with OpenLayers 2',
args: {
layers: [
{
url: 'https://d2ad6b4ur7yvpq.cloudfront.net/naturalearth-3.3.0/ne_10m_geography_marine_polys.geojson',
name: 'Marine regions',
},
],
},
};
export const MapWithPopover: Story = {
name: 'Map with popup',
args: {
layers: [
{
url: 'https://openlayers.org/data/vector/ecoregions.json',
name: 'Ecoregions',
},
],
popup: (feature: any) => {
return (
<div className="flex flex-col gap-y-1" style={{ color: 'red' }}>
<span className="font-bold">Biome name</span>
<span className="text-sm">{feature.values_.BIOME_NAME}</span>
</div>
);
},
},
};
export const Third: Story = {
name: 'Map with two layers',
args: {
layers: [
{
url: 'https://d2ad6b4ur7yvpq.cloudfront.net/naturalearth-3.3.0/ne_10m_geography_marine_polys.geojson',
name: 'Marine regions',
},
{
url: 'https://openlayers.org/data/vector/ecoregions.json',
name: 'Ecoregions',
},
],
},
};
export const CustomCenter: Story = {
name: 'Map with custom center and zoom',
args: {
layers: [
{
url: 'https://openlayers.org/data/vector/ecoregions.json',
name: 'Ecoregions',
},
],
center: [-15, 20],
zoom: 4,
},
};
export const PointsOnMap: Story = {
name: 'Map with points on',
args: {
layers: [
{
url: 'https://opendata.arcgis.com/datasets/9c58741995174fbcb017cf46c8a42f4b_25.geojson',
name: 'E-Scooter Parking Bays',
},
],
center: [-1.055429957881787, 53.963900188025301],
zoom: 12,
},
};
export const KMLFile: Story = {
name: 'Map with KML File',
args: {
layers: [
{
url: 'https://openlayers.org/en/latest/examples/data/kml/2012_Earthquakes_Mag5.kml',
name: '2012 Earthquakes M5+',
format: 'kml',
heatmap: true,
},
],
},
};

View File

@@ -1,50 +0,0 @@
import type { Meta, StoryObj } from '@storybook/react';
import { PdfViewer, PdfViewerProps } from '../src/components/PdfViewer';
const meta: Meta = {
title: 'Components/PdfViewer',
component: PdfViewer,
tags: ['autodocs'],
argTypes: {
url: {
description: 'URL to PDF file',
},
parentClassName: {
description: 'Classname for the parent div of the pdf viewer',
},
layour: {
description:
'Set to true if you want to have a layout with zoom level, page count, printing button etc',
defaultValue: false,
},
},
};
export default meta;
type Story = StoryObj<PdfViewerProps>;
export const PdfViewerStory: Story = {
name: 'PdfViewer',
args: {
url: 'https://cdn.filestackcontent.com/wcrjf9qPTCKXV3hMXDwK',
},
};
export const PdfViewerStoryWithLayout: Story = {
name: 'PdfViewer with the default layout',
args: {
url: 'https://cdn.filestackcontent.com/wcrjf9qPTCKXV3hMXDwK',
layout: true,
},
};
export const PdfViewerStoryWithHeight: Story = {
name: 'PdfViewer with a custom height',
args: {
url: 'https://cdn.filestackcontent.com/wcrjf9qPTCKXV3hMXDwK',
parentClassName: 'h-96',
layout: true,
},
};

View File

@@ -1,10 +1,8 @@
/** @type {import('tailwindcss').Config} */
export default {
content: [
'./index.html',
'./src/**/*.{js,ts,jsx,tsx}',
'./stories/*.{js,ts,jsx,tsx}',
],
theme: {},
content: ['./index.html', './src/**/*.{js,ts,jsx,tsx}'],
theme: {
extend: {},
},
plugins: [],
};

View File

@@ -1,14 +1,29 @@
import react from '@vitejs/plugin-react';
import path from 'node:path';
import { defineConfig } from 'vitest/config';
import dts from 'vite-plugin-dts';
import tailwindcss from 'tailwindcss';
import { UserConfigExport } from 'vite';
import react from '@vitejs/plugin-react'
import path from 'node:path'
import { defineConfig } from 'vitest/config'
import dts from 'vite-plugin-dts'
import tailwindcss from 'tailwindcss'
import { UserConfigExport } from 'vite'
import replace from "rollup-plugin-re"
const app = async (): Promise<UserConfigExport> => {
return defineConfig({
plugins: [
react(),
replace({
patterns: [
{
match: /js-sha256/,
test: `eval("require('crypto')")`,
replace: `require('crypto')`,
},
{
match: /js-sha256/,
test: `eval("require('buffer').Buffer")`,
replace: `require('buffer').Buffer`,
},
],
}),
dts({
insertTypesEntry: true,
}),
@@ -19,7 +34,6 @@ const app = async (): Promise<UserConfigExport> => {
},
},
build: {
target: 'es2020',
lib: {
entry: path.resolve(__dirname, 'src/index.ts'),
name: 'components',
@@ -27,27 +41,12 @@ const app = async (): Promise<UserConfigExport> => {
fileName: (format) => `components.${format}.js`,
},
rollupOptions: {
external: [
'react',
'ol-mapbox-style',
'react-dom',
'tailwindcss',
'vega-lite',
'vega',
'react-vega',
'ol',
'leaflet',
],
external: ['react', 'react-dom', 'tailwindcss', 'vega-lite', 'vega', 'react-vega'],
output: {
manualChunks: undefined,
globals: {
react: 'React',
ol: 'ol',
'ol-mapbox-style': 'ol-mapbox-style',
'react-vega': 'react-vega',
'react-dom': 'ReactDOM',
tailwindcss: 'tailwindcss',
leaflet: 'leaflet',
},
},
},
@@ -56,7 +55,7 @@ const app = async (): Promise<UserConfigExport> => {
globals: true,
environment: 'jsdom',
},
});
};
})
}
// https://vitejs.dev/config/
export default app;
export default app

View File

@@ -1,64 +0,0 @@
// vite.config.ts
import react from "file:///home/urutu-branco/Projetos/portaljs/node_modules/@vitejs/plugin-react-swc/index.mjs";
import path from "node:path";
import { defineConfig } from "file:///home/urutu-branco/Projetos/portaljs/node_modules/vitest/dist/config.js";
import dts from "file:///home/urutu-branco/Projetos/portaljs/node_modules/vite-plugin-dts/dist/index.mjs";
import tailwindcss from "file:///home/urutu-branco/Projetos/portaljs/node_modules/tailwindcss/lib/index.js";
var __vite_injected_original_dirname = "/home/urutu-branco/Projetos/portaljs/packages/components";
var app = async () => {
return defineConfig({
plugins: [
react(),
dts({
insertTypesEntry: true
})
],
css: {
postcss: {
plugins: [tailwindcss]
}
},
build: {
target: "es2020",
lib: {
entry: path.resolve(__vite_injected_original_dirname, "src/index.ts"),
name: "components",
formats: ["es", "umd"],
fileName: (format) => `components.${format}.js`
},
rollupOptions: {
external: [
"react",
"ol-mapbox-style",
"react-dom",
"tailwindcss",
"vega-lite",
"vega",
"react-vega",
"ol",
"ol/dom.js"
],
output: {
manualChunks: void 0,
globals: {
react: "React",
ol: "ol",
"ol/dom.js": "ol/dom.js",
"react-vega": "react-vega",
"react-dom": "ReactDOM",
tailwindcss: "tailwindcss"
}
}
}
},
test: {
globals: true,
environment: "jsdom"
}
});
};
var vite_config_default = app;
export {
vite_config_default as default
};
//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsidml0ZS5jb25maWcudHMiXSwKICAic291cmNlc0NvbnRlbnQiOiBbImNvbnN0IF9fdml0ZV9pbmplY3RlZF9vcmlnaW5hbF9kaXJuYW1lID0gXCIvaG9tZS91cnV0dS1icmFuY28vUHJvamV0b3MvcG9ydGFsanMvcGFja2FnZXMvY29tcG9uZW50c1wiO2NvbnN0IF9fdml0ZV9pbmplY3RlZF9vcmlnaW5hbF9maWxlbmFtZSA9IFwiL2hvbWUvdXJ1dHUtYnJhbmNvL1Byb2pldG9zL3BvcnRhbGpzL3BhY2thZ2VzL2NvbXBvbmVudHMvdml0ZS5jb25maWcudHNcIjtjb25zdCBfX3ZpdGVfaW5qZWN0ZWRfb3JpZ2luYWxfaW1wb3J0X21ldGFfdXJsID0gXCJmaWxlOi8vL2hvbWUvdXJ1dHUtYnJhbmNvL1Byb2pldG9zL3BvcnRhbGpzL3BhY2thZ2VzL2NvbXBvbmVudHMvdml0ZS5jb25maWcudHNcIjtpbXBvcnQgcmVhY3QgZnJvbSAnQHZpdGVqcy9wbHVnaW4tcmVhY3Qtc3djJztcbmltcG9ydCBwYXRoIGZyb20gJ25vZGU6cGF0aCc7XG5pbXBvcnQgeyBkZWZpbmVDb25maWcgfSBmcm9tICd2aXRlc3QvY29uZmlnJztcbmltcG9ydCBkdHMgZnJvbSAndml0ZS1wbHVnaW4tZHRzJztcbmltcG9ydCB0YWlsd2luZGNzcyBmcm9tICd0YWlsd2luZGNzcyc7XG5pbXBvcnQgeyBVc2VyQ29uZmlnRXhwb3J0IH0gZnJvbSAndml0ZSc7XG5pbXBvcnQgcmVwbGFjZSBmcm9tICdyb2xsdXAtcGx1Z2luLXJlJztcblxuY29uc3QgYXBwID0gYXN5bmMgKCk6IFByb21pc2U8VXNlckNvbmZpZ0V4cG9ydD4gPT4ge1xuICByZXR1cm4gZGVmaW5lQ29uZmlnKHtcbiAgICBwbHVnaW5zOiBbXG4gICAgICByZWFjdCgpLFxuICAgICAgZHRzKHtcbiAgICAgICAgaW5zZXJ0VHlwZXNFbnRyeTogdHJ1ZSxcbiAgICAgIH0pLFxuICAgIF0sXG4gICAgY3NzOiB7XG4gICAgICBwb3N0Y3NzOiB7XG4gICAgICAgIHBsdWdpbnM6IFt0YWlsd2luZGNzc10sXG4gICAgICB9LFxuICAgIH0sXG4gICAgYnVpbGQ6IHtcbiAgICAgIHRhcmdldDogJ2VzMjAyMCcsXG4gICAgICBsaWI6IHtcbiAgICAgICAgZW50cnk6IHBhdGgucmVzb2x2ZShfX2Rpcm5hbWUsICdzcmMvaW5kZXgudHMnKSxcbiAgICAgICAgbmFtZTogJ2NvbXBvbmVudHMnLFxuICAgICAgICBmb3JtYXRzOiBbJ2VzJywgJ3VtZCddLFxuICAgICAgICBmaWxlTmFtZTogKGZvcm1hdCkgPT4gYGNvbXBvbmVudHMuJHtmb3JtYXR9LmpzYCxcbiAgICAgIH0sXG4gICAgICByb2xsdXBPcHRpb25zOiB7XG4gICAgICAgIGV4dGVybmFsOiBbXG4gICAgICAgICAgJ3JlYWN0JyxcbiAgICAgICAgICAnb2wtbWFwYm94LXN0eWxlJyxcbiAgICAgICAgICAncmVhY3QtZG9tJyxcbiAgICAgICAgICAndGFpbHdpbmRjc3MnLFxuICAgICAgICAgICd2ZWdhLWxpdGUnLFxuICAgICAgICAgICd2ZWdhJyxcbiAgICAgICAgICAncmVhY3QtdmVnYScsXG4gICAgICAgICAgJ29sJyxcbiAgICAgICAgICAnb2wvZG9tLmpzJyxcbiAgICAgICAgXSxcbiAgICAgICAgb3V0cHV0OiB7XG4gICAgICAgICAgbWFudWFsQ2h1bmtzOiB1bmRlZmluZWQsXG4gICAgICAgICAgZ2xvYmFsczoge1xuICAgICAgICAgICAgcmVhY3Q6ICdSZWFjdCcsXG4gICAgICAgICAgICBvbDogJ29sJyxcbiAgICAgICAgICAgICdvbC9kb20uanMnOiAnb2wvZG9tLmpzJyxcbiAgICAgICAgICAgICdyZWFjdC12ZWdhJzogJ3JlYWN0LXZlZ2EnLFxuICAgICAgICAgICAgJ3JlYWN0LWRvbSc6ICdSZWFjdERPTScsXG4gICAgICAgICAgICB0YWlsd2luZGNzczogJ3RhaWx3aW5kY3NzJyxcbiAgICAgICAgICB9LFxuICAgICAgICB9LFxuICAgICAgfSxcbiAgICB9LFxuICAgIHRlc3Q6IHtcbiAgICAgIGdsb2JhbHM6IHRydWUsXG4gICAgICBlbnZpcm9ubWVudDogJ2pzZG9tJyxcbiAgICB9LFxuICB9KTtcbn07XG4vLyBodHRwczovL3ZpdGVqcy5kZXYvY29uZmlnL1xuZXhwb3J0IGRlZmF1bHQgYXBwO1xuIl0sCiAgIm1hcHBpbmdzIjogIjtBQUEwVixPQUFPLFdBQVc7QUFDNVcsT0FBTyxVQUFVO0FBQ2pCLFNBQVMsb0JBQW9CO0FBQzdCLE9BQU8sU0FBUztBQUNoQixPQUFPLGlCQUFpQjtBQUp4QixJQUFNLG1DQUFtQztBQVF6QyxJQUFNLE1BQU0sWUFBdUM7QUFDakQsU0FBTyxhQUFhO0FBQUEsSUFDbEIsU0FBUztBQUFBLE1BQ1AsTUFBTTtBQUFBLE1BQ04sSUFBSTtBQUFBLFFBQ0Ysa0JBQWtCO0FBQUEsTUFDcEIsQ0FBQztBQUFBLElBQ0g7QUFBQSxJQUNBLEtBQUs7QUFBQSxNQUNILFNBQVM7QUFBQSxRQUNQLFNBQVMsQ0FBQyxXQUFXO0FBQUEsTUFDdkI7QUFBQSxJQUNGO0FBQUEsSUFDQSxPQUFPO0FBQUEsTUFDTCxRQUFRO0FBQUEsTUFDUixLQUFLO0FBQUEsUUFDSCxPQUFPLEtBQUssUUFBUSxrQ0FBVyxjQUFjO0FBQUEsUUFDN0MsTUFBTTtBQUFBLFFBQ04sU0FBUyxDQUFDLE1BQU0sS0FBSztBQUFBLFFBQ3JCLFVBQVUsQ0FBQyxXQUFXLGNBQWM7QUFBQSxNQUN0QztBQUFBLE1BQ0EsZUFBZTtBQUFBLFFBQ2IsVUFBVTtBQUFBLFVBQ1I7QUFBQSxVQUNBO0FBQUEsVUFDQTtBQUFBLFVBQ0E7QUFBQSxVQUNBO0FBQUEsVUFDQTtBQUFBLFVBQ0E7QUFBQSxVQUNBO0FBQUEsVUFDQTtBQUFBLFFBQ0Y7QUFBQSxRQUNBLFFBQVE7QUFBQSxVQUNOLGNBQWM7QUFBQSxVQUNkLFNBQVM7QUFBQSxZQUNQLE9BQU87QUFBQSxZQUNQLElBQUk7QUFBQSxZQUNKLGFBQWE7QUFBQSxZQUNiLGNBQWM7QUFBQSxZQUNkLGFBQWE7QUFBQSxZQUNiLGFBQWE7QUFBQSxVQUNmO0FBQUEsUUFDRjtBQUFBLE1BQ0Y7QUFBQSxJQUNGO0FBQUEsSUFDQSxNQUFNO0FBQUEsTUFDSixTQUFTO0FBQUEsTUFDVCxhQUFhO0FBQUEsSUFDZjtBQUFBLEVBQ0YsQ0FBQztBQUNIO0FBRUEsSUFBTyxzQkFBUTsiLAogICJuYW1lcyI6IFtdCn0K

View File

@@ -1,21 +0,0 @@
import type { StorybookConfig } from '@storybook/react-webpack5';
const config: StorybookConfig = {
stories: ['../src/**/*.stories.@(js|jsx|ts|tsx|mdx)'],
addons: [
'@storybook/addon-essentials',
'@storybook/addon-interactions',
'@nrwl/react/plugins/storybook',
'storybook-tailwind-dark-mode'
],
framework: {
name: '@storybook/react-webpack5',
options: {},
},
};
export default config;
// To customize your webpack configuration you can use the webpackFinal field.
// Check https://storybook.js.org/docs/react/builders/webpack#extending-storybooks-webpack-config
// and https://nx.dev/packages/storybook/documents/custom-builder-configs

View File

@@ -1,14 +0,0 @@
import './tailwind-imports.css';
const preview = {
globalTypes: {
darkMode: {
defaultValue: false, // Enable dark mode by default on all stories
},
className: {
defaultValue: 'dark', // Set your custom dark mode class name
},
},
};
export default preview;

View File

@@ -1,3 +0,0 @@
@tailwind base;
@tailwind components;
@tailwind utilities;

View File

@@ -1,31 +0,0 @@
{
"extends": "../tsconfig.json",
"compilerOptions": {
"emitDecoratorMetadata": true,
"outDir": ""
},
"files": [
"../../node_modules/@nx/react/typings/styled-jsx.d.ts",
"../../node_modules/@nx/react/typings/cssmodule.d.ts",
"../../node_modules/@nx/react/typings/image.d.ts"
],
"exclude": [
"src/**/*.spec.ts",
"src/**/*.test.ts",
"src/**/*.spec.js",
"src/**/*.test.js",
"src/**/*.spec.tsx",
"src/**/*.test.tsx",
"src/**/*.spec.jsx",
"src/**/*.test.js"
],
"include": [
"src/**/*.stories.ts",
"src/**/*.stories.js",
"src/**/*.stories.jsx",
"src/**/*.stories.tsx",
"src/**/*.stories.mdx",
".storybook/*.js",
".storybook/*.ts"
]
}

View File

@@ -1,11 +1,5 @@
# @portaljs/core
## 1.0.6
### Patch Changes
- [#958](https://github.com/datopian/portaljs/pull/958) [`c4bf5bd0`](https://github.com/datopian/portaljs/commit/c4bf5bd05401982125750e61a27ed31f9bb14a4d) Thanks [@olayway](https://github.com/olayway)! - Add basic Hero component.
## 1.0.5
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "@portaljs/core",
"version": "1.0.6",
"version": "1.0.5",
"description": "Core Portal.JS components, configs and utils.",
"repository": {
"type": "git",

View File

@@ -1,11 +0,0 @@
// libs/shared/ui/postcss.config.js
const { join } = require('path');
module.exports = {
plugins: {
tailwindcss: {
config: join(__dirname, 'tailwind.config.js'),
},
autoprefixer: {},
},
};

View File

@@ -34,37 +34,6 @@
"jestConfig": "packages/core/jest.config.ts",
"passWithNoTests": true
}
},
"storybook": {
"executor": "@nrwl/storybook:storybook",
"options": {
"port": 4400,
"configDir": "packages/core/.storybook"
},
"configurations": {
"ci": {
"quiet": true
}
}
},
"build-storybook": {
"executor": "@nrwl/storybook:build",
"outputs": ["{options.outputDir}"],
"options": {
"outputDir": "dist/storybook/core",
"configDir": "packages/core/.storybook"
},
"configurations": {
"ci": {
"quiet": true
}
}
},
"test-storybook": {
"executor": "nx:run-commands",
"options": {
"command": "test-storybook -c packages/core/.storybook --url=http://localhost:4400"
}
}
}
}

View File

@@ -1,48 +0,0 @@
import React from 'react';
import type { Meta, StoryObj } from '@storybook/react';
import { Card } from './Card';
const meta: Meta<typeof Card> = {
component: Card,
};
export default meta;
type Story = StoryObj<typeof Card>;
/*
*👇 Render functions are a framework specific feature to allow you control on how the component renders.
* See https://storybook.js.org/docs/react/api/csf
* to learn how to use render functions.
*/
const blog = {
urlPath: "#",
title: "Card title goes here",
date: "2021-01-01",
description: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed euismod, diam quis accumsan maximus, quam libero porttitor nisl, vita",
}
export const Primary: Story = {
render: () => (
<Card className="md:col-span-3">
<Card.Title href={`${blog.urlPath}`}>
{blog.title}
</Card.Title>
<Card.Eyebrow
as="time"
dateTime={blog.date}
className="md:hidden"
decorate
>
{blog.date}
</Card.Eyebrow>
{blog.description && (
<Card.Description>
{blog.description}
</Card.Description>
)}
<Card.Cta>Read article</Card.Cta>
</Card>
),
};

View File

@@ -1,90 +0,0 @@
interface Button {
title: string;
href: string;
type: 'filled' | 'text'
}
export interface HeroProps {
title: string;
subtitle?: string;
announcement?: {
title: string;
href?: string;
};
buttons?: Array<Button>
}
const buttonStyle = {
'filled': "rounded-md bg-secondary px-3.5 py-2.5 text-sm font-semibold text-white shadow-sm hover:bg-secondary/90 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600",
'text': "text-sm font-semibold leading-6"
}
export const Hero: React.FC<HeroProps> = ({ title, subtitle, announcement, buttons }) => {
return (
<div className="text-primary dark:text-primary-dark">
<div className="relative isolate px-6 pt-14 lg:px-8">
{/* <div
className="absolute inset-x-0 -top-40 -z-10 transform-gpu overflow-hidden blur-3xl sm:-top-80"
aria-hidden="true"
>
<div
className="relative left-[calc(50%-11rem)] aspect-[1155/678] w-[36.125rem] -translate-x-1/2 rotate-[30deg] bg-gradient-to-tr from-[#38bdf8] to-[#89a8fc] opacity-30 sm:left-[calc(50%-30rem)] sm:w-[72.1875rem]"
style={{
clipPath:
'polygon(74.1% 44.1%, 100% 61.6%, 97.5% 26.9%, 85.5% 0.1%, 80.7% 2%, 72.5% 32.5%, 60.2% 62.4%, 52.4% 68.1%, 47.5% 58.3%, 45.2% 34.5%, 27.5% 76.7%, 0.1% 64.9%, 17.9% 100%, 27.6% 76.8%, 76.1% 97.7%, 74.1% 44.1%)',
}}
/>
</div> */}
<div className="mx-auto max-w-2xl py-16 sm:py-32 lg:py-40">
{announcement && (
<div className="hidden sm:mb-8 sm:flex sm:justify-center">
<div className="relative rounded-full px-3 py-1 text-sm leading-6 ring-1 ring-primary/20 hover:ring-primary/30 dark:ring-primary-dark/30 dark:hover:ring-primary-dark/40">
{announcement.title}{' '}
<a href="#" className="font-semibold text-secondary">
<span className="absolute inset-0" aria-hidden="true" />
Read more <span aria-hidden="true">&rarr;</span>
</a>
</div>
</div>
)}
<div className="text-center">
<h1 className="text-4xl font-bold tracking-tight sm:text-6xl">
{title}
</h1>
{subtitle && (
<p className="mt-6 text-lg leading-8 text-primary/90 dark:text-primary-dark/80">
{subtitle}
</p>
)}
{buttons && buttons.length && (
<div className="mt-10 flex items-center justify-center gap-x-6">
{buttons.map((b) => (
<a
href={b.href}
className={buttonStyle[b.type]}
>
{b.title}
</a>
))}
</div>
)}
</div>
</div>
{/* <div
className="absolute inset-x-0 top-[calc(100%-13rem)] -z-10 transform-gpu overflow-hidden blur-3xl sm:top-[calc(100%-30rem)]"
aria-hidden="true"
>
<div
className="relative left-[calc(50%+3rem)] aspect-[1155/678] w-[36.125rem] -translate-x-1/2 bg-gradient-to-tr from-[#89a8fc] to-[#38bdf8] opacity-30 sm:left-[calc(50%+36rem)] sm:w-[72.1875rem]"
style={{
clipPath:
'polygon(74.1% 44.1%, 100% 61.6%, 97.5% 26.9%, 85.5% 0.1%, 80.7% 2%, 72.5% 32.5%, 60.2% 62.4%, 52.4% 68.1%, 47.5% 58.3%, 45.2% 34.5%, 27.5% 76.7%, 0.1% 64.9%, 17.9% 100%, 27.6% 76.8%, 76.1% 97.7%, 74.1% 44.1%)',
}}
/>
</div> */}
</div>
</div>
)
}

View File

@@ -1 +0,0 @@
export { Hero } from "./Hero"

View File

@@ -18,5 +18,5 @@ export { BlogLayout } from "./BlogLayout";
export { Mermaid } from "./Mermaid";
export { SiteToc, NavItem, NavGroup } from "./SiteToc";
export { Comments, CommentsConfig } from "./Comments";
export { AuthorConfig } from "./types";
export { Hero } from "./Hero";

View File

@@ -1,44 +0,0 @@
const { createGlobPatternsForDependencies } = require('@nrwl/next/tailwind');
const { join } = require('path');
// const defaultTheme = require("tailwindcss/defaultTheme");
const colors = require("tailwindcss/colors");
module.exports = {
content: [
join(__dirname, './src/**/*.{js,ts,jsx,tsx}'),
...createGlobPatternsForDependencies(__dirname),
],
darkMode: "class",
theme: {
extend: {
// support wider width for large screens >1440px eg. in hero
maxWidth: {
"8xl": "88rem",
},
// fontFamily: {
// sans: ["ui-sans-serif", ...defaultTheme.fontFamily.sans],
// serif: ["ui-serif", ...defaultTheme.fontFamily.serif],
// mono: ["ui-monospace", ...defaultTheme.fontFamily.mono],
// headings: ["-apple-system", ...defaultTheme.fontFamily.sans],
// },
colors: {
background: {
DEFAULT: colors.white,
dark: colors.slate[900],
},
primary: {
DEFAULT: colors.gray[700],
dark: colors.gray[300],
},
secondary: {
DEFAULT: colors.sky[400],
dark: colors.sky[400],
},
},
},
},
/* eslint global-require: off */
// plugins: [
// require("@tailwindcss/typography")
// ],
};

View File

@@ -1,6 +1,5 @@
{
"compilerOptions": {
"baseUrl": ".",
"jsx": "react-jsx",
"module": "es2020",
"moduleResolution": "node",
@@ -23,9 +22,6 @@
},
{
"path": "./tsconfig.spec.json"
},
{
"path": "./.storybook/tsconfig.json"
}
]
}

View File

@@ -17,11 +17,7 @@
"**/*.spec.js",
"**/*.test.js",
"**/*.spec.jsx",
"**/*.test.jsx",
"**/*.stories.ts",
"**/*.stories.js",
"**/*.stories.jsx",
"**/*.stories.tsx"
"**/*.test.jsx"
],
"include": ["**/*.js", "**/*.jsx", "**/*.ts", "**/*.tsx"]
}

View File

@@ -15,9 +15,9 @@ const defaultWikiLinkResolver = (target: string) => {
export interface FromMarkdownOptions {
pathFormat?:
| "raw" // default; use for regular relative or absolute paths
| "obsidian-absolute" // use for Obsidian-style absolute paths (with no leading slash)
| "obsidian-short"; // use for Obsidian-style shortened paths (shortest path possible)
| "raw" // default; use for regular relative or absolute paths
| "obsidian-absolute" // use for Obsidian-style absolute paths (with no leading slash)
| "obsidian-short"; // use for Obsidian-style shortened paths (shortest path possible)
permalinks?: string[]; // list of permalinks to match possible permalinks of a wiki link against
wikiLinkResolver?: (name: string) => string[]; // function to resolve wiki links to an array of possible permalinks
newClassName?: string; // class name to add to links that don't have a matching permalink
@@ -125,24 +125,13 @@ function fromMarkdown(opts: FromMarkdownOptions = {}) {
if (isEmbed) {
const [isSupportedFormat, format] = isSupportedFileFormat(target);
if (!isSupportedFormat) {
// Temporarily render note transclusion as a regular wiki link
if (!format) {
wikiLink.data.hName = "a";
wikiLink.data.hProperties = {
className: classNames + " " + "transclusion",
href: hrefTemplate(link) + headingId,
};
wikiLink.data.hChildren = [{ type: "text", value: displayName }];
} else {
wikiLink.data.hName = "p";
wikiLink.data.hChildren = [
{
type: "text",
value: `![[${target}]]`,
},
];
}
wikiLink.data.hName = "p";
wikiLink.data.hChildren = [
{
type: "text",
value: `![[${target}]]`,
},
];
} else if (format === "pdf") {
wikiLink.data.hName = "iframe";
wikiLink.data.hProperties = {

View File

@@ -15,9 +15,9 @@ const defaultWikiLinkResolver = (target: string) => {
export interface HtmlOptions {
pathFormat?:
| "raw" // default; use for regular relative or absolute paths
| "obsidian-absolute" // use for Obsidian-style absolute paths (with no leading slash)
| "obsidian-short"; // use for Obsidian-style shortened paths (shortest path possible)
| "raw" // default; use for regular relative or absolute paths
| "obsidian-absolute" // use for Obsidian-style absolute paths (with no leading slash)
| "obsidian-short"; // use for Obsidian-style shortened paths (shortest path possible)
permalinks?: string[]; // list of permalinks to match possible permalinks of a wiki link against
wikiLinkResolver?: (name: string) => string[]; // function to resolve wiki links to an array of possible permalinks
newClassName?: string; // class name to add to links that don't have a matching permalink
@@ -108,16 +108,7 @@ function html(opts: HtmlOptions = {}) {
if (isEmbed) {
const [isSupportedFormat, format] = isSupportedFileFormat(target);
if (!isSupportedFormat) {
// Temporarily render note transclusion as a regular wiki link
if (!format) {
this.tag(
`<a href="${hrefTemplate(link + headingId)}" class="${classNames} transclusion">`
);
this.raw(displayName);
this.tag("</a>");
} else {
this.raw(`![[${target}]]`);
}
this.raw(`![[${target}]]`);
} else if (format === "pdf") {
this.tag(
`<iframe width="100%" src="${hrefTemplate(

View File

@@ -309,16 +309,4 @@ describe("micromark-extension-wiki-link", () => {
expect(serialized).toBe('<p><a href="/" class="internal">/index</a></p>');
});
});
describe("transclusions", () => {
test("parsers a transclusion as a regular wiki link", () => {
const serialized = micromark("![[Some Page]]", "ascii", {
extensions: [syntax()],
htmlExtensions: [html() as any], // TODO type fix
});
expect(serialized).toBe(
'<p><a href="Some Page" class="internal new transclusion">Some Page</a></p>'
);
});
});
});

View File

@@ -535,28 +535,4 @@ describe("remark-wiki-link", () => {
});
});
});
describe("transclusions", () => {
test("replaces a transclusion with a regular wiki link", () => {
const processor = unified().use(markdown).use(wikiLinkPlugin);
let ast = processor.parse("![[Some Page]]");
ast = processor.runSync(ast);
expect(select("wikiLink", ast)).not.toEqual(null);
visit(ast, "wikiLink", (node: Node) => {
expect(node.data?.isEmbed).toEqual(true);
expect(node.data?.exists).toEqual(false);
expect(node.data?.permalink).toEqual("Some Page");
expect(node.data?.alias).toEqual(null);
expect(node.data?.hName).toEqual("a");
expect((node.data?.hProperties as any).className).toEqual(
"internal new transclusion"
);
expect((node.data?.hProperties as any).href).toEqual("Some Page");
expect((node.data?.hChildren as any)[0].value).toEqual("Some Page");
});
});
});
});

5
site/.gitignore vendored
View File

@@ -35,8 +35,3 @@ yarn-error.log*
# markdowndb
markdown.db
# seo
robots.txt
sitemap-0.xml
sitemap.xml

View File

@@ -10,7 +10,7 @@ import { useEffect, useState } from 'react';
const Stat = ({ title, value, ...props }) => {
return (
<div {...props}>
<span className="text-4xl sm:text-6xl font-bold text-secondary">{value}</span>
<span className="text-6xl font-bold text-secondary">{value}</span>
<p className="text-lg font-medium">{title}</p>
</div>
);

View File

@@ -58,7 +58,7 @@ export default function Features() {
>
<div className="absolute -inset-px rounded-xl border-2 border-transparent opacity-0 [background:linear-gradient(var(--quick-links-hover-bg,theme(colors.sky.50)),var(--quick-links-hover-bg,theme(colors.sky.50)))_padding-box,linear-gradient(to_top,theme(colors.blue.300),theme(colors.blue.400),theme(colors.blue.500))_border-box] group-hover:opacity-100 dark:[--quick-links-hover-bg:theme(colors.slate.800)]" />
<div className="relative overflow-hidden rounded-xl p-6">
<img src={feature.icon} alt={feature.title} className="h-24 w-auto" />
<img src={feature.icon} alt="" className="h-24 w-auto" />
<h2 className="mt-4 font-display text-base text-primary dark:text-primary-dark">
<span className="absolute -inset-px rounded-xl" />
{feature.title}

View File

@@ -2,7 +2,7 @@ import { useRef } from 'react';
import ButtonLink from './ButtonLink';
import NewsletterForm from './NewsletterForm';
import Image from 'next/image';
import DatahubExampleImg from '@/public/images/showcases/datahub.webp';
import DatahubExampleImg from "@/public/images/showcases/datahub.png"
const codeLanguage = 'javascript';
const code = `export default {
@@ -41,7 +41,7 @@ export function Hero() {
{/* Commented code on line 37, 39 and 113 will reenable the two columns hero */}
<div className="mx-auto grid max-w-2xl grid-cols-1 gap-y-16 gap-x-8 px-4 lg:max-w-8xl lg:grid-cols-2 lg:px-8 xl:gap-x-16 xl:px-12">
<div className="relative mb-10 lg:mb-0 md:text-center lg:text-left">
<div>
<div role="heading">
<h1 className="inline bg-gradient-to-r from-blue-500 via-blue-300 to-blue-500 bg-clip-text text-5xl tracking-tight text-transparent">
The JavaScript framework for data portals
</h1>
@@ -72,11 +72,9 @@ export function Hero() {
target="_blank"
rel="noopener noreferrer"
>
<Image
<img
src="/images/datopian_logo.png"
alt="Datopian"
width={24}
height={20}
className="mx-2 mb-1 h-6 inline bg-black rounded-full"
/>
<span>Datopian</span>
@@ -87,12 +85,7 @@ export function Hero() {
<div className="relative rounded-2xl bg-[#0A101F]/80 ring-1 ring-white/10 backdrop-blur">
<div className="absolute -top-px left-20 right-11 h-px bg-gradient-to-r from-sky-300/0 via-sky-300/70 to-sky-300/0" />
<div className="absolute -bottom-px left-11 right-20 h-px bg-gradient-to-r from-blue-400/0 via-blue-400 to-blue-400/0" />
<Image
height={400}
width={600}
src={DatahubExampleImg}
alt="opendata.datahub.io"
/>
<Image src={DatahubExampleImg} alt="opendata.datahub.io" />
</div>
</div>
</div>

View File

@@ -1,65 +0,0 @@
import { ArticleJsonLd } from 'next-seo';
import { useRouter } from 'next/router';
export default function JSONLD({
meta,
source,
}: {
meta: any;
source: string;
}): JSX.Element {
if (!source) {
return <></>;
}
const baseUrl = process.env.NEXT_PUBLIC_SITE_URL || 'https://portaljs.org';
const pageUrl = `${baseUrl}/${meta.urlPath}`;
const imageMatches = source.match(
/(?<=src: ")(.*)\.((png)|(jpg)|(jpeg))(?=")/g
);
let images = [];
if (imageMatches) {
images = [...imageMatches];
images = images.map((img) =>
img.startsWith('http')
? img
: `${baseUrl}${img.startsWith('/') ? '' : '/'}${img}`
);
}
let Component: JSX.Element;
const isBlog: boolean =
/^blog\/.*/.test(meta.urlPath) || meta.filetype === 'blog';
const isDoc: boolean = /^((docs)|(howtos\/)|(guide\/)).*/.test(meta.urlPath);
if (isBlog) {
Component = (
<ArticleJsonLd
type="BlogPosting"
url={pageUrl}
title={meta.title}
datePublished={meta.date}
dateModified={meta.date}
authorName={meta.authors.length ? meta.authors[0].name : 'PortalJS'}
description={meta.description}
images={images}
/>
);
} else if (isDoc) {
Component = (
<ArticleJsonLd
url={pageUrl}
title={meta.title}
images={images}
datePublished={meta.date}
dateModified={meta.date}
authorName={meta.authors.length ? meta.authors[0].name : 'PortalJS'}
description={meta.description}
/>
);
}
return Component;
}

View File

@@ -54,14 +54,12 @@ function useTableOfContents(tableOfContents) {
export default function Layout({
children,
title,
description,
tableOfContents = [],
isHomePage = false,
sidebarTree = [],
}: {
children;
title?: string;
description?: string;
tableOfContents?;
urlPath?: string;
sidebarTree?: [];
@@ -84,9 +82,9 @@ export default function Layout({
return (
<>
{title && <NextSeo title={title} description={description} />}
{title && <NextSeo title={title} />}
<Nav />
<div className="mx-auto p-2 sm:p-6 bg-background dark:bg-background-dark">
<div className="mx-auto p-6 bg-background dark:bg-background-dark">
{isHomePage && <Hero />}
<div className="relative mx-auto flex max-w-8xl justify-center sm:px-2 lg:px-8 xl:px-12">

View File

@@ -1,20 +1,20 @@
import { MDXRemote } from 'next-mdx-remote';
import { NextSeo } from 'next-seo';
import layouts from 'layouts';
import DocsPagination from './DocsPagination';
import { Hero } from "@portaljs/core";
import { NextSeo } from 'next-seo';
export default function MDXPage({ source, frontMatter }) {
const Layout = ({ children }) => {
const layoutName = frontMatter?.layout || 'default';
const LayoutComponent = layouts[layoutName];
const Layout = ({ children }) => {
const layoutName = frontMatter?.layout || 'default';
const LayoutComponent = layouts[layoutName];
return <LayoutComponent {...frontMatter}>{children}</LayoutComponent>;
};
return <LayoutComponent {...frontMatter}>{children}</LayoutComponent>;
};
return (
<Layout>
<MDXRemote {...source} components={{ DocsPagination, NextSeo, Hero }} />
</Layout>
);
return (
<Layout>
<MDXRemote {...source} components={{ DocsPagination, NextSeo }} />
</Layout>
);
}

View File

@@ -92,7 +92,7 @@ export default function MobileNavigation({ navigation }) {
>
{/* <Logomark className="h-9 w-9" /> */}
<div className="font-extrabold text-slate-900 dark:text-white text-2xl ml-6">
PortalJS
{siteConfig.title}
</div>
</Link>
</div>

View File

@@ -95,10 +95,7 @@ export default function Nav() {
<MobileNavigation navigation={siteConfig.navLinks} />
</div>
<div className="flex flex-none items-center">
<div className="hidden sm:block">
<NavbarTitle />
</div>
<NavbarTitle />
<div className="hidden lg:flex ml-8 mr-6 sm:mr-8 md:mr-0">
{siteConfig.navLinks.map((item) => (
<NavItem item={item} key={item.name} />
@@ -126,8 +123,9 @@ export default function Nav() {
)}
{siteConfig.github && (
<div className="mt-1">
{/* @ts-ignore */}
<GitHubButton
<
// @ts-ignore
GitHubButton
href={siteConfig.github}
data-color-scheme="no-preference: light; light: light; dark: dark;"
data-size="large"

View File

@@ -20,7 +20,7 @@ export default function NavItem({ item }) {
};
return (
<Menu as="div" role="menu" className="relative">
<Menu as="div" className="relative">
<Menu.Item>
{Object.prototype.hasOwnProperty.call(item, 'href') ? (
<Link

View File

@@ -3,6 +3,10 @@ import Script from 'next/script';
export default function NewsletterForm() {
return (
<div>
<link
rel="stylesheet"
href="https://sibforms.com/forms/end-form/build/sib-styles.css"
/>
<div
id="sib-form-container"
className="mt-8 sm:mx-auto sm:text-center lg:text-left lg:mx-0"
@@ -115,7 +119,6 @@ export default function NewsletterForm() {
}}
/>
<Script
strategy="worker"
id="newsletter-submit-form"
src="https://sibforms.com/forms/end-form/build/main.js"
/>

View File

@@ -5,26 +5,26 @@ const items = [
{
title: 'Open Data Northern Ireland',
href: 'https://www.opendatani.gov.uk/',
image: '/images/showcases/odni.webp',
image: '/images/showcases/odni.png',
description: 'Government Open Data Portal',
},
{
title: 'Birmingham City Observatory',
href: 'https://www.cityobservatory.birmingham.gov.uk/',
image: '/images/showcases/birmingham.webp',
image: '/images/showcases/birmingham.png',
description: 'Government Open Data Portal',
},
{
title: 'UAE Open Data',
href: 'https://opendata.fcsc.gov.ae/',
image: '/images/showcases/uae.webp',
image: '/images/showcases/uae.png',
description: 'Government Open Data Portal',
sourceUrl: 'https://github.com/FCSCOpendata/frontend',
},
{
title: 'Datahub Open Data',
href: 'https://opendata.datahub.io/',
image: '/images/showcases/datahub.webp',
image: '/images/showcases/datahub.png',
description: 'Demo Data Portal by DataHub',
},
];

View File

@@ -1,4 +1,4 @@
import { defaultConfig } from "@portaljs/core";
import { defaultConfig } from "@flowershow/core";
import userConfig from "../content/config";
export const siteConfig: any = {

Binary file not shown.

Before

Width:  |  Height:  |  Size: 416 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 187 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 467 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 792 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 218 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 192 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 212 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 237 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 233 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 168 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 227 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 223 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 276 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 277 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 296 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 213 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 276 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 210 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 212 KiB

Some files were not shown because too many files have changed in this diff Show More