Compare commits
9 Commits
lighthouse
...
examples-d
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3cf945a194 | ||
|
|
fe225715e9 | ||
|
|
a801baf34d | ||
|
|
f8ef07646f | ||
|
|
0225eb49ba | ||
|
|
b68c52ce36 | ||
|
|
c80cae27a4 | ||
|
|
b14500b213 | ||
|
|
062e817f3a |
@@ -1,5 +0,0 @@
|
|||||||
---
|
|
||||||
'@portaljs/components': patch
|
|
||||||
---
|
|
||||||
|
|
||||||
Fix missing CSS styles for PDF component
|
|
||||||
@@ -40,25 +40,16 @@ export default function Home({
|
|||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<main className="flex min-h-screen flex-col items-center p-24 bg-zinc-900">
|
||||||
<main className="py-12 bg-zinc-900">
|
<DatasetSearchForm
|
||||||
<DatasetSearchForm
|
options={options}
|
||||||
options={options}
|
setOptions={setOptions}
|
||||||
setOptions={setOptions}
|
groups={groups}
|
||||||
groups={groups}
|
orgs={orgs}
|
||||||
orgs={orgs}
|
/>
|
||||||
/>
|
<div className="bg-white p-8 my-4 rounded-lg">
|
||||||
<div
|
<ListOfDatasets options={options} setOptions={setOptions} ckan={ckan} />{' '}
|
||||||
className="bg-white p-8 mx-auto my-4 rounded-lg"
|
</div>
|
||||||
style={{ width: 'min(1100px, 95vw)' }}
|
</main>
|
||||||
>
|
|
||||||
<ListOfDatasets
|
|
||||||
options={options}
|
|
||||||
setOptions={setOptions}
|
|
||||||
ckan={ckan}
|
|
||||||
/>{' '}
|
|
||||||
</div>
|
|
||||||
</main>
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +0,0 @@
|
|||||||
const nextConfig = {
|
|
||||||
swcMinify: false
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = nextConfig
|
|
||||||
@@ -7,21 +7,13 @@ import { Mermaid } from '@portaljs/core';
|
|||||||
// to handle import statements. Instead, you must include components in scope
|
// to handle import statements. Instead, you must include components in scope
|
||||||
// here.
|
// here.
|
||||||
const components = {
|
const components = {
|
||||||
Table: dynamic(() => import('@portaljs/components').then((mod) => mod.Table)),
|
Table: dynamic(() => import('@portaljs/components').then(mod => mod.Table)),
|
||||||
Catalog: dynamic(() =>
|
Catalog: dynamic(() => import('@portaljs/components').then(mod => mod.Catalog)),
|
||||||
import('@portaljs/components').then((mod) => mod.Catalog)
|
|
||||||
),
|
|
||||||
mermaid: Mermaid,
|
mermaid: Mermaid,
|
||||||
Vega: dynamic(() => import('@portaljs/components').then((mod) => mod.Vega)),
|
Vega: dynamic(() => import('@portaljs/components').then(mod => mod.Vega)),
|
||||||
VegaLite: dynamic(() =>
|
VegaLite: dynamic(() => import('@portaljs/components').then(mod => mod.VegaLite)),
|
||||||
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)),
|
||||||
LineChart: dynamic(() =>
|
|
||||||
import('@portaljs/components').then((mod) => mod.LineChart)
|
|
||||||
),
|
|
||||||
FlatUiTable: dynamic(() =>
|
|
||||||
import('@portaljs/components').then((mod) => mod.FlatUiTable)
|
|
||||||
),
|
|
||||||
} as any;
|
} as any;
|
||||||
|
|
||||||
export default function DRD({ source }: { source: any }) {
|
export default function DRD({ source }: { source: any }) {
|
||||||
|
|||||||
@@ -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.
|
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:
|
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
|
```markdown
|
||||||
|
|||||||
@@ -156,8 +156,8 @@ the key to a prosperous future.
|
|||||||
title: '',
|
title: '',
|
||||||
field: 'label',
|
field: 'label',
|
||||||
scale: {
|
scale: {
|
||||||
domain: ['Education'],
|
domain: ["Education"],
|
||||||
range: ['#64b5f6'],
|
range: ['#64b5f6']
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
tooltip: [
|
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.
|
One can only hope that more cities follow suit.
|
||||||
|
|
||||||
[^1]: https://worldpopulationreview.com/world-cities/frankfurt-population
|
[^1]: https://worldpopulationreview.com/world-cities/frankfurt-population
|
||||||
|
|
||||||
|
|||||||
@@ -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.
|
|
||||||
@@ -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.
|
|
||||||
313
examples/openspending/package-lock.json
generated
@@ -14,7 +14,7 @@
|
|||||||
"@heroicons/react": "^2.0.18",
|
"@heroicons/react": "^2.0.18",
|
||||||
"@octokit/plugin-throttling": "^5.2.2",
|
"@octokit/plugin-throttling": "^5.2.2",
|
||||||
"@portaljs/ckan": "^0.0.2",
|
"@portaljs/ckan": "^0.0.2",
|
||||||
"@portaljs/components": "0.1.12",
|
"@portaljs/components": "^0.1.8",
|
||||||
"@portaljs/core": "^1.0.5",
|
"@portaljs/core": "^1.0.5",
|
||||||
"@portaljs/remark-callouts": "^1.0.5",
|
"@portaljs/remark-callouts": "^1.0.5",
|
||||||
"@portaljs/remark-embed": "^1.0.4",
|
"@portaljs/remark-embed": "^1.0.4",
|
||||||
@@ -1971,40 +1971,6 @@
|
|||||||
"@lit-labs/ssr-dom-shim": "^1.0.0"
|
"@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": {
|
"node_modules/@mapbox/node-pre-gyp": {
|
||||||
"version": "1.0.10",
|
"version": "1.0.10",
|
||||||
"resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.10.tgz",
|
"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",
|
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
|
||||||
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
|
"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": {
|
"node_modules/@mdx-js/mdx": {
|
||||||
"version": "2.3.0",
|
"version": "2.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/@mdx-js/mdx/-/mdx-2.3.0.tgz",
|
"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",
|
"resolved": "https://registry.npmjs.org/@octokit/webhooks-types/-/webhooks-types-6.11.0.tgz",
|
||||||
"integrity": "sha512-AanzbulOHljrku1NGfafxdpTCfw2ENaWzH01N2vqQM+cUFbk868Cgh0xylz0JIM9BoKbfI++bdD6EYX0Q/UTEw=="
|
"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": {
|
"node_modules/@pkgr/utils": {
|
||||||
"version": "2.4.1",
|
"version": "2.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/@pkgr/utils/-/utils-2.4.1.tgz",
|
"resolved": "https://registry.npmjs.org/@pkgr/utils/-/utils-2.4.1.tgz",
|
||||||
@@ -2725,14 +2676,6 @@
|
|||||||
"url": "https://opencollective.com/unts"
|
"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": {
|
"node_modules/@portaljs/ckan": {
|
||||||
"version": "0.0.2",
|
"version": "0.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/@portaljs/ckan/-/ckan-0.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/@portaljs/ckan/-/ckan-0.0.2.tgz",
|
||||||
@@ -2748,26 +2691,22 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@portaljs/components": {
|
"node_modules/@portaljs/components": {
|
||||||
"version": "0.1.12",
|
"version": "0.1.11",
|
||||||
"resolved": "file:../../packages/components/portaljs-components-0.1.12.tgz",
|
"resolved": "https://registry.npmjs.org/@portaljs/components/-/components-0.1.11.tgz",
|
||||||
"integrity": "sha512-Cr+RQ7tkbIqtBNq5D8zeZEB2dOejxD0V78l/I4AbdjYI8jvQ4Evx6APEfhPs3im6LDEGrT28LslVTZJ6luslnw==",
|
"integrity": "sha512-ojiZ2dtnORAlo2YycpwK+mxrseqVxrD/Z/FMejHrZ9V6pAdlLLpjMU724WTvGBbjwO/RkoVFc4x6H6ykyvMOig==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@githubocto/flat-ui": "^0.14.1",
|
"@githubocto/flat-ui": "^0.14.1",
|
||||||
"@heroicons/react": "^2.0.17",
|
"@heroicons/react": "^2.0.17",
|
||||||
"@planet/maps": "^8.1.0",
|
|
||||||
"@tanstack/react-table": "^8.8.5",
|
"@tanstack/react-table": "^8.8.5",
|
||||||
"chroma-js": "^2.4.2",
|
|
||||||
"flexsearch": "0.7.21",
|
"flexsearch": "0.7.21",
|
||||||
"leaflet": "^1.9.4",
|
|
||||||
"next-mdx-remote": "^4.4.1",
|
"next-mdx-remote": "^4.4.1",
|
||||||
"ol": "^7.4.0",
|
|
||||||
"papaparse": "^5.4.1",
|
"papaparse": "^5.4.1",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
"react-hook-form": "^7.43.9",
|
"react-hook-form": "^7.43.9",
|
||||||
"react-leaflet": "^4.2.1",
|
|
||||||
"react-query": "^3.39.3",
|
"react-query": "^3.39.3",
|
||||||
"react-vega": "^7.6.0",
|
"react-vega": "^7.6.0",
|
||||||
|
"rollup-plugin-re": "^1.0.7",
|
||||||
"vega": "5.25.0",
|
"vega": "5.25.0",
|
||||||
"vega-lite": "5.1.0",
|
"vega-lite": "5.1.0",
|
||||||
"vitest": "^0.31.4"
|
"vitest": "^0.31.4"
|
||||||
@@ -2970,16 +2909,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/@reach/observe-rect/-/observe-rect-1.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/@reach/observe-rect/-/observe-rect-1.2.0.tgz",
|
||||||
"integrity": "sha512-Ba7HmkFgfQxZqqaeIWWkNK0rEhpxVQHIoVyW1YDSkGsGIXzcaW4deC8B0pZrNSSyLTdIk7y+5olKt5+g0GmFIQ=="
|
"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": {
|
"node_modules/@rushstack/eslint-patch": {
|
||||||
"version": "1.3.2",
|
"version": "1.3.2",
|
||||||
"resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.3.2.tgz",
|
"resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.3.2.tgz",
|
||||||
@@ -4495,11 +4424,6 @@
|
|||||||
"node": ">=10"
|
"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": {
|
"node_modules/clean-set": {
|
||||||
"version": "1.1.2",
|
"version": "1.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/clean-set/-/clean-set-1.1.2.tgz",
|
"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",
|
"resolved": "https://registry.npmjs.org/css-unit-converter/-/css-unit-converter-1.1.2.tgz",
|
||||||
"integrity": "sha512-IiJwMC8rdZE0+xiEZHeru6YoONC4rfPMqGm2W85jMIbkFvv5nFTwJVFHam2eFrN6txmoUYFAFXiv8ICVeTO0MA=="
|
"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": {
|
"node_modules/cssesc": {
|
||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
|
||||||
@@ -5939,11 +5858,6 @@
|
|||||||
"node": ">=4"
|
"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": {
|
"node_modules/ecdsa-sig-formatter": {
|
||||||
"version": "1.0.11",
|
"version": "1.0.11",
|
||||||
"resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz",
|
"resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz",
|
||||||
@@ -7241,39 +7155,6 @@
|
|||||||
"node": ">=6.9.0"
|
"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": {
|
"node_modules/get-caller-file": {
|
||||||
"version": "2.0.5",
|
"version": "2.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
|
||||||
@@ -7950,7 +7831,9 @@
|
|||||||
"type": "consulting",
|
"type": "consulting",
|
||||||
"url": "https://feross.org/support"
|
"url": "https://feross.org/support"
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
|
"optional": true,
|
||||||
|
"peer": true
|
||||||
},
|
},
|
||||||
"node_modules/ignore": {
|
"node_modules/ignore": {
|
||||||
"version": "5.2.4",
|
"version": "5.2.4",
|
||||||
@@ -8901,16 +8784,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/layout-base/-/layout-base-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/layout-base/-/layout-base-1.0.2.tgz",
|
||||||
"integrity": "sha512-8h2oVEZNktL4BH2JCOI90iD1yXwL6iNW7KcCKT2QZgQJR2vbqDsldCTPRU9NifTCqHZci57XvQQ15YTu+sTYPg=="
|
"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": {
|
"node_modules/levn": {
|
||||||
"version": "0.4.1",
|
"version": "0.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
|
"resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
|
||||||
@@ -9083,6 +8956,14 @@
|
|||||||
"yallist": "^3.0.2"
|
"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": {
|
"node_modules/make-dir": {
|
||||||
"version": "3.1.0",
|
"version": "3.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz",
|
||||||
@@ -9142,11 +9023,6 @@
|
|||||||
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
|
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
|
||||||
"optional": true
|
"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": {
|
"node_modules/markdown-extensions": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/markdown-extensions/-/markdown-extensions-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/markdown-extensions/-/markdown-extensions-1.1.1.tgz",
|
||||||
@@ -11616,32 +11492,6 @@
|
|||||||
"node": ">= 14"
|
"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": {
|
"node_modules/once": {
|
||||||
"version": "1.4.0",
|
"version": "1.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
||||||
@@ -11788,11 +11638,6 @@
|
|||||||
"url": "https://github.com/sponsors/wooorm"
|
"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": {
|
"node_modules/parse-json": {
|
||||||
"version": "5.2.0",
|
"version": "5.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz",
|
||||||
@@ -11890,18 +11735,6 @@
|
|||||||
"node": "*"
|
"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": {
|
"node_modules/pend": {
|
||||||
"version": "1.2.0",
|
"version": "1.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz",
|
||||||
@@ -12222,11 +12055,6 @@
|
|||||||
"url": "https://github.com/sponsors/wooorm"
|
"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": {
|
"node_modules/proxy-from-env": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
|
"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"
|
"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": {
|
"node_modules/react": {
|
||||||
"version": "18.2.0",
|
"version": "18.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz",
|
"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",
|
"resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
|
||||||
"integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w=="
|
"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": {
|
"node_modules/react-markdown": {
|
||||||
"version": "8.0.7",
|
"version": "8.0.7",
|
||||||
"resolved": "https://registry.npmjs.org/react-markdown/-/react-markdown-8.0.7.tgz",
|
"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"
|
"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": {
|
"node_modules/react-refresh": {
|
||||||
"version": "0.14.0",
|
"version": "0.14.0",
|
||||||
"resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.0.tgz",
|
"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"
|
"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": {
|
"node_modules/retext": {
|
||||||
"version": "8.1.0",
|
"version": "8.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/retext/-/retext-8.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/retext/-/retext-8.1.0.tgz",
|
||||||
@@ -13170,6 +12949,28 @@
|
|||||||
"fsevents": "~2.3.2"
|
"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": {
|
"node_modules/rtl-css-js": {
|
||||||
"version": "1.16.1",
|
"version": "1.16.1",
|
||||||
"resolved": "https://registry.npmjs.org/rtl-css-js/-/rtl-css-js-1.16.1.tgz",
|
"resolved": "https://registry.npmjs.org/rtl-css-js/-/rtl-css-js-1.16.1.tgz",
|
||||||
@@ -13513,34 +13314,6 @@
|
|||||||
"node": ">= 10"
|
"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": {
|
"node_modules/source-map": {
|
||||||
"version": "0.5.7",
|
"version": "0.5.7",
|
||||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
|
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
|
||||||
@@ -16191,6 +15964,11 @@
|
|||||||
"node": ">=12"
|
"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": {
|
"node_modules/warning": {
|
||||||
"version": "4.0.3",
|
"version": "4.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz",
|
"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": {
|
"node_modules/xtend": {
|
||||||
"version": "4.0.2",
|
"version": "4.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
|
||||||
|
|||||||
@@ -18,7 +18,7 @@
|
|||||||
"@heroicons/react": "^2.0.18",
|
"@heroicons/react": "^2.0.18",
|
||||||
"@octokit/plugin-throttling": "^5.2.2",
|
"@octokit/plugin-throttling": "^5.2.2",
|
||||||
"@portaljs/ckan": "^0.0.2",
|
"@portaljs/ckan": "^0.0.2",
|
||||||
"@portaljs/components": "0.1.12",
|
"@portaljs/components": "^0.1.8",
|
||||||
"@portaljs/core": "^1.0.5",
|
"@portaljs/core": "^1.0.5",
|
||||||
"@portaljs/remark-callouts": "^1.0.5",
|
"@portaljs/remark-callouts": "^1.0.5",
|
||||||
"@portaljs/remark-embed": "^1.0.4",
|
"@portaljs/remark-embed": "^1.0.4",
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import { AppProps } from 'next/app';
|
import { AppProps } from 'next/app';
|
||||||
import '@portaljs/components/styles.css';
|
|
||||||
import './styles.css';
|
import './styles.css';
|
||||||
import { NextSeo } from 'next-seo';
|
import { NextSeo } from 'next-seo';
|
||||||
|
|
||||||
|
|||||||
@@ -4,12 +4,7 @@ import { GetStaticProps } from 'next';
|
|||||||
import Layout from '../../components/_shared/Layout';
|
import Layout from '../../components/_shared/Layout';
|
||||||
import { formatDate } from '@/utils/formatDate';
|
import { formatDate } from '@/utils/formatDate';
|
||||||
import parse from '../../lib/markdown';
|
import parse from '../../lib/markdown';
|
||||||
import dynamic from 'next/dynamic';
|
import DataRichDocument from '../../components/DataRichDocument';
|
||||||
|
|
||||||
const DataRichDocument = dynamic(
|
|
||||||
() => import('../../components/DataRichDocument'),
|
|
||||||
{ ssr: false }
|
|
||||||
);
|
|
||||||
|
|
||||||
export default function Page({ source, meta }) {
|
export default function Page({ source, meta }) {
|
||||||
return (
|
return (
|
||||||
@@ -51,10 +46,7 @@ export const getStaticProps: GetStaticProps = async ({ params }) => {
|
|||||||
|
|
||||||
export async function getStaticPaths() {
|
export async function getStaticPaths() {
|
||||||
const mddb = await clientPromise;
|
const mddb = await clientPromise;
|
||||||
let allDocuments = await mddb.getFiles({
|
let allDocuments = await mddb.getFiles({ extensions: ['mdx'], folder: 'stories' });
|
||||||
extensions: ['mdx'],
|
|
||||||
folder: 'stories',
|
|
||||||
});
|
|
||||||
|
|
||||||
const paths = allDocuments
|
const paths = allDocuments
|
||||||
.filter((page) => page.metadata?.isDraft !== true)
|
.filter((page) => page.metadata?.isDraft !== true)
|
||||||
|
|||||||
1720
package-lock.json
generated
@@ -5,6 +5,7 @@
|
|||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"changeset": "changeset",
|
"changeset": "changeset",
|
||||||
|
"prerelease": "nx run-many --targets=lint,test,build --projects=tag:test",
|
||||||
"release": "changeset publish"
|
"release": "changeset publish"
|
||||||
},
|
},
|
||||||
"private": true,
|
"private": true,
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import 'tailwindcss/tailwind.css'
|
import 'tailwindcss/tailwind.css'
|
||||||
import '../src/index.css'
|
import '../src/index.css'
|
||||||
|
|
||||||
|
|
||||||
import type { Preview } from '@storybook/react';
|
import type { Preview } from '@storybook/react';
|
||||||
|
|
||||||
const preview: Preview = {
|
const preview: Preview = {
|
||||||
|
|||||||
@@ -1,25 +1,5 @@
|
|||||||
# @portaljs/components
|
# @portaljs/components
|
||||||
|
|
||||||
## 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
|
## 0.1.11
|
||||||
|
|
||||||
### Patch Changes
|
### Patch Changes
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@portaljs/components",
|
"name": "@portaljs/components",
|
||||||
"version": "0.3.0",
|
"version": "0.1.11",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"description": "https://portaljs.org",
|
"description": "https://portaljs.org",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
@@ -12,14 +12,12 @@
|
|||||||
],
|
],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "npm run storybook",
|
"dev": "npm run storybook",
|
||||||
"example": "vite",
|
"build": "tsc && vite build && npm run build-tailwind",
|
||||||
"build": "tsc && vite build && npm run build-tailwind && npm run fix-leaflet",
|
|
||||||
"lint": "eslint src --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
|
"lint": "eslint src --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
|
||||||
"storybook": "storybook dev -p 6006",
|
"storybook": "storybook dev -p 6006",
|
||||||
"build-storybook": "storybook build",
|
"build-storybook": "storybook build",
|
||||||
"build-tailwind": "NODE_ENV=production npx tailwindcss --postcss -c tailwind.config.js -i src/index.css -o ./dist/style.css --minify",
|
"build-tailwind": "NODE_ENV=production npx tailwindcss -o ./dist/styles.css --minify",
|
||||||
"prepare": "npm run build",
|
"prepare": "npm run build"
|
||||||
"fix-leaflet": "node ./scripts/fix-leaflet.cjs"
|
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
@@ -28,29 +26,19 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@githubocto/flat-ui": "^0.14.1",
|
"@githubocto/flat-ui": "^0.14.1",
|
||||||
"@heroicons/react": "^2.0.17",
|
"@heroicons/react": "^2.0.17",
|
||||||
"@planet/maps": "^8.1.0",
|
|
||||||
"@tanstack/react-table": "^8.8.5",
|
"@tanstack/react-table": "^8.8.5",
|
||||||
"ag-grid-react": "^30.0.4",
|
|
||||||
"chroma-js": "^2.4.2",
|
|
||||||
"flexsearch": "0.7.21",
|
"flexsearch": "0.7.21",
|
||||||
"leaflet": "^1.9.4",
|
|
||||||
"next-mdx-remote": "^4.4.1",
|
"next-mdx-remote": "^4.4.1",
|
||||||
"ol": "^7.4.0",
|
|
||||||
"papaparse": "^5.4.1",
|
"papaparse": "^5.4.1",
|
||||||
"postcss-url": "^10.1.3",
|
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
"react-hook-form": "^7.43.9",
|
"react-hook-form": "^7.43.9",
|
||||||
"react-leaflet": "^4.2.1",
|
|
||||||
"react-query": "^3.39.3",
|
"react-query": "^3.39.3",
|
||||||
"react-vega": "^7.6.0",
|
"react-vega": "^7.6.0",
|
||||||
|
"rollup-plugin-re": "^1.0.7",
|
||||||
"vega": "5.25.0",
|
"vega": "5.25.0",
|
||||||
"vega-lite": "5.1.0",
|
"vega-lite": "5.1.0",
|
||||||
"vitest": "^0.31.4",
|
"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"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@storybook/addon-essentials": "^7.0.7",
|
"@storybook/addon-essentials": "^7.0.7",
|
||||||
@@ -60,16 +48,13 @@
|
|||||||
"@storybook/react": "^7.0.7",
|
"@storybook/react": "^7.0.7",
|
||||||
"@storybook/react-vite": "^7.0.7",
|
"@storybook/react-vite": "^7.0.7",
|
||||||
"@storybook/testing-library": "^0.0.14-next.2",
|
"@storybook/testing-library": "^0.0.14-next.2",
|
||||||
"@swc/core": "^1.3.68",
|
|
||||||
"@types/flexsearch": "^0.7.3",
|
"@types/flexsearch": "^0.7.3",
|
||||||
"@types/leaflet": "^1.9.3",
|
|
||||||
"@types/papaparse": "^5.3.7",
|
"@types/papaparse": "^5.3.7",
|
||||||
"@types/react": "^18.0.28",
|
"@types/react": "^18.0.28",
|
||||||
"@types/react-dom": "^18.0.11",
|
"@types/react-dom": "^18.0.11",
|
||||||
"@typescript-eslint/eslint-plugin": "^5.57.1",
|
"@typescript-eslint/eslint-plugin": "^5.57.1",
|
||||||
"@typescript-eslint/parser": "^5.57.1",
|
"@typescript-eslint/parser": "^5.57.1",
|
||||||
"@vitejs/plugin-react": "^4.0.0",
|
"@vitejs/plugin-react": "^4.0.0",
|
||||||
"@vitejs/plugin-react-swc": "^3.3.2",
|
|
||||||
"autoprefixer": "^10.4.14",
|
"autoprefixer": "^10.4.14",
|
||||||
"eslint": "^8.38.0",
|
"eslint": "^8.38.0",
|
||||||
"eslint-plugin-react-hooks": "^4.6.0",
|
"eslint-plugin-react-hooks": "^4.6.0",
|
||||||
@@ -77,15 +62,12 @@
|
|||||||
"eslint-plugin-storybook": "^0.6.11",
|
"eslint-plugin-storybook": "^0.6.11",
|
||||||
"json": "^11.0.0",
|
"json": "^11.0.0",
|
||||||
"postcss": "^8.4.23",
|
"postcss": "^8.4.23",
|
||||||
"postcss-import": "^15.1.0",
|
|
||||||
"postcss-import-url": "^7.2.0",
|
|
||||||
"prop-types": "^15.8.1",
|
"prop-types": "^15.8.1",
|
||||||
"storybook": "^7.0.7",
|
"storybook": "^7.0.7",
|
||||||
"tailwindcss": "^3.3.2",
|
"tailwindcss": "^3.3.2",
|
||||||
"typescript": "^5.0.2",
|
"typescript": "^5.0.2",
|
||||||
"vite": "^4.3.2",
|
"vite": "^4.3.2",
|
||||||
"vite-plugin-dts": "^2.3.0",
|
"vite-plugin-dts": "^2.3.0"
|
||||||
"vite-plugin-swc-only": "^0.1.18"
|
|
||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
"dist"
|
"dist"
|
||||||
@@ -99,7 +81,7 @@
|
|||||||
"require": "./dist/components.umd.js"
|
"require": "./dist/components.umd.js"
|
||||||
},
|
},
|
||||||
"./styles.css": {
|
"./styles.css": {
|
||||||
"import": "./dist/style.css"
|
"import": "./dist/styles.css"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"publishConfig": {
|
"publishConfig": {
|
||||||
|
|||||||
@@ -1,10 +1,6 @@
|
|||||||
console.log('PostCSS');
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
plugins: {
|
plugins: {
|
||||||
'postcss-import': {},
|
|
||||||
'postcss-url': { url: 'inline' },
|
|
||||||
tailwindcss: {},
|
tailwindcss: {},
|
||||||
autoprefixer: {},
|
autoprefixer: {},
|
||||||
},
|
},
|
||||||
};
|
}
|
||||||
|
|||||||
@@ -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 });
|
|
||||||
@@ -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>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -35,7 +35,7 @@ export function LineChart({
|
|||||||
const spec = {
|
const spec = {
|
||||||
$schema: 'https://vega.github.io/schema/vega-lite/v5.json',
|
$schema: 'https://vega.github.io/schema/vega-lite/v5.json',
|
||||||
title,
|
title,
|
||||||
width: 'container',
|
width: 600,
|
||||||
height: 300,
|
height: 300,
|
||||||
mark: {
|
mark: {
|
||||||
type: 'line',
|
type: 'line',
|
||||||
|
|||||||
@@ -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='© <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>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -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>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@@ -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;
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
export const Layers = ({ children }) => {
|
|
||||||
return <div>{children}</div>;
|
|
||||||
};
|
|
||||||
@@ -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;
|
|
||||||
@@ -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>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -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;
|
|
||||||
@@ -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;
|
|
||||||
@@ -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>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,11 +1,10 @@
|
|||||||
|
@tailwind base;
|
||||||
|
@tailwind components;
|
||||||
|
@tailwind utilities;
|
||||||
|
|
||||||
@import "tailwindcss/base";
|
/* Temporary fix for a size issue with FlatUiTable loading indicator on Firefox */
|
||||||
@import "tailwindcss/components";
|
@layer base {
|
||||||
@import "include";
|
svg[tw^='animate-pulse w-12'] {
|
||||||
@import "leaflet";
|
max-width: 100px;
|
||||||
@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";
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,6 @@
|
|||||||
export * from './components/Table';
|
export * from "./components/Table";
|
||||||
export * from './components/Catalog';
|
export * from "./components/Catalog";
|
||||||
export * from './components/LineChart';
|
export * from "./components/LineChart";
|
||||||
export * from './components/Vega';
|
export * from "./components/Vega";
|
||||||
export * from './components/VegaLite';
|
export * from "./components/VegaLite";
|
||||||
export * from './components/FlatUiTable';
|
export * from "./components/FlatUiTable";
|
||||||
export * from './components/OpenLayers/OpenLayers';
|
|
||||||
export * from './components/Map';
|
|
||||||
export * from './components/PdfViewer';
|
|
||||||
export * from "./components/Excel";
|
|
||||||
|
|||||||
@@ -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[];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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',
|
|
||||||
},
|
|
||||||
};
|
|
||||||
@@ -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,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
@@ -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,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
};
|
|
||||||
@@ -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,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
@@ -1,10 +1,8 @@
|
|||||||
/** @type {import('tailwindcss').Config} */
|
/** @type {import('tailwindcss').Config} */
|
||||||
export default {
|
export default {
|
||||||
content: [
|
content: ['./index.html', './src/**/*.{js,ts,jsx,tsx}'],
|
||||||
'./index.html',
|
theme: {
|
||||||
'./src/**/*.{js,ts,jsx,tsx}',
|
extend: {},
|
||||||
'./stories/*.{js,ts,jsx,tsx}',
|
},
|
||||||
],
|
|
||||||
theme: {},
|
|
||||||
plugins: [],
|
plugins: [],
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,14 +1,29 @@
|
|||||||
import react from '@vitejs/plugin-react';
|
import react from '@vitejs/plugin-react'
|
||||||
import path from 'node:path';
|
import path from 'node:path'
|
||||||
import { defineConfig } from 'vitest/config';
|
import { defineConfig } from 'vitest/config'
|
||||||
import dts from 'vite-plugin-dts';
|
import dts from 'vite-plugin-dts'
|
||||||
import tailwindcss from 'tailwindcss';
|
import tailwindcss from 'tailwindcss'
|
||||||
import { UserConfigExport } from 'vite';
|
import { UserConfigExport } from 'vite'
|
||||||
|
import replace from "rollup-plugin-re"
|
||||||
|
|
||||||
const app = async (): Promise<UserConfigExport> => {
|
const app = async (): Promise<UserConfigExport> => {
|
||||||
return defineConfig({
|
return defineConfig({
|
||||||
plugins: [
|
plugins: [
|
||||||
react(),
|
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({
|
dts({
|
||||||
insertTypesEntry: true,
|
insertTypesEntry: true,
|
||||||
}),
|
}),
|
||||||
@@ -19,7 +34,6 @@ const app = async (): Promise<UserConfigExport> => {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
build: {
|
build: {
|
||||||
target: 'es2020',
|
|
||||||
lib: {
|
lib: {
|
||||||
entry: path.resolve(__dirname, 'src/index.ts'),
|
entry: path.resolve(__dirname, 'src/index.ts'),
|
||||||
name: 'components',
|
name: 'components',
|
||||||
@@ -27,27 +41,12 @@ const app = async (): Promise<UserConfigExport> => {
|
|||||||
fileName: (format) => `components.${format}.js`,
|
fileName: (format) => `components.${format}.js`,
|
||||||
},
|
},
|
||||||
rollupOptions: {
|
rollupOptions: {
|
||||||
external: [
|
external: ['react', 'react-dom', 'tailwindcss', 'vega-lite', 'vega', 'react-vega'],
|
||||||
'react',
|
|
||||||
'ol-mapbox-style',
|
|
||||||
'react-dom',
|
|
||||||
'tailwindcss',
|
|
||||||
'vega-lite',
|
|
||||||
'vega',
|
|
||||||
'react-vega',
|
|
||||||
'ol',
|
|
||||||
'leaflet',
|
|
||||||
],
|
|
||||||
output: {
|
output: {
|
||||||
manualChunks: undefined,
|
|
||||||
globals: {
|
globals: {
|
||||||
react: 'React',
|
react: 'React',
|
||||||
ol: 'ol',
|
|
||||||
'ol-mapbox-style': 'ol-mapbox-style',
|
|
||||||
'react-vega': 'react-vega',
|
|
||||||
'react-dom': 'ReactDOM',
|
'react-dom': 'ReactDOM',
|
||||||
tailwindcss: 'tailwindcss',
|
tailwindcss: 'tailwindcss',
|
||||||
leaflet: 'leaflet',
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -56,7 +55,7 @@ const app = async (): Promise<UserConfigExport> => {
|
|||||||
globals: true,
|
globals: true,
|
||||||
environment: 'jsdom',
|
environment: 'jsdom',
|
||||||
},
|
},
|
||||||
});
|
})
|
||||||
};
|
}
|
||||||
// https://vitejs.dev/config/
|
// https://vitejs.dev/config/
|
||||||
export default app;
|
export default app
|
||||||
|
|||||||
@@ -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
|
|
||||||
@@ -1,11 +1,5 @@
|
|||||||
# @portaljs/core
|
# @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
|
## 1.0.5
|
||||||
|
|
||||||
### Patch Changes
|
### Patch Changes
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@portaljs/core",
|
"name": "@portaljs/core",
|
||||||
"version": "1.0.6",
|
"version": "1.0.5",
|
||||||
"description": "Core Portal.JS components, configs and utils.",
|
"description": "Core Portal.JS components, configs and utils.",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
|||||||
@@ -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">→</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>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
export { Hero } from "./Hero"
|
|
||||||
@@ -18,5 +18,5 @@ export { BlogLayout } from "./BlogLayout";
|
|||||||
export { Mermaid } from "./Mermaid";
|
export { Mermaid } from "./Mermaid";
|
||||||
export { SiteToc, NavItem, NavGroup } from "./SiteToc";
|
export { SiteToc, NavItem, NavGroup } from "./SiteToc";
|
||||||
export { Comments, CommentsConfig } from "./Comments";
|
export { Comments, CommentsConfig } from "./Comments";
|
||||||
|
|
||||||
export { AuthorConfig } from "./types";
|
export { AuthorConfig } from "./types";
|
||||||
export { Hero } from "./Hero";
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { useRef } from 'react';
|
|||||||
import ButtonLink from './ButtonLink';
|
import ButtonLink from './ButtonLink';
|
||||||
import NewsletterForm from './NewsletterForm';
|
import NewsletterForm from './NewsletterForm';
|
||||||
import Image from 'next/image';
|
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 codeLanguage = 'javascript';
|
||||||
const code = `export default {
|
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 */}
|
{/* 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="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 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">
|
<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
|
The JavaScript framework for data portals
|
||||||
</h1>
|
</h1>
|
||||||
@@ -72,11 +72,9 @@ export function Hero() {
|
|||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
>
|
>
|
||||||
<Image
|
<img
|
||||||
src="/images/datopian_logo.png"
|
src="/images/datopian_logo.png"
|
||||||
alt="Datopian"
|
alt="Datopian"
|
||||||
width={24}
|
|
||||||
height={20}
|
|
||||||
className="mx-2 mb-1 h-6 inline bg-black rounded-full"
|
className="mx-2 mb-1 h-6 inline bg-black rounded-full"
|
||||||
/>
|
/>
|
||||||
<span>Datopian</span>
|
<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="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 -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" />
|
<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
|
<Image src={DatahubExampleImg} alt="opendata.datahub.io" />
|
||||||
height={400}
|
|
||||||
width={600}
|
|
||||||
src={DatahubExampleImg}
|
|
||||||
alt="opendata.datahub.io"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,21 +1,20 @@
|
|||||||
import { MDXRemote } from 'next-mdx-remote';
|
import { MDXRemote } from 'next-mdx-remote';
|
||||||
import { NextSeo } from 'next-seo';
|
|
||||||
import layouts from 'layouts';
|
import layouts from 'layouts';
|
||||||
import DocsPagination from './DocsPagination';
|
import DocsPagination from './DocsPagination';
|
||||||
import { Hero } from "@portaljs/core";
|
import { NextSeo } from 'next-seo';
|
||||||
|
|
||||||
export default function MDXPage({ source, frontMatter }) {
|
export default function MDXPage({ source, frontMatter }) {
|
||||||
const Layout = ({ children }) => {
|
const Layout = ({ children }) => {
|
||||||
const layoutName = frontMatter?.layout || 'default';
|
const layoutName = frontMatter?.layout || 'default';
|
||||||
const LayoutComponent = layouts[layoutName];
|
const LayoutComponent = layouts[layoutName];
|
||||||
|
|
||||||
return <LayoutComponent {...frontMatter}>{children}</LayoutComponent>;
|
return <LayoutComponent {...frontMatter}>{children}</LayoutComponent>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Layout>
|
<Layout>
|
||||||
<MDXRemote {...source} components={{ DocsPagination, NextSeo, Hero }} />
|
<MDXRemote {...source} components={{ DocsPagination, NextSeo }} />
|
||||||
</Layout>
|
</Layout>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ export default function NavItem({ item }) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Menu as="div" role="menu" className="relative">
|
<Menu as="div" className="relative">
|
||||||
<Menu.Item>
|
<Menu.Item>
|
||||||
{Object.prototype.hasOwnProperty.call(item, 'href') ? (
|
{Object.prototype.hasOwnProperty.call(item, 'href') ? (
|
||||||
<Link
|
<Link
|
||||||
|
|||||||
@@ -3,6 +3,10 @@ import Script from 'next/script';
|
|||||||
export default function NewsletterForm() {
|
export default function NewsletterForm() {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
|
<link
|
||||||
|
rel="stylesheet"
|
||||||
|
href="https://sibforms.com/forms/end-form/build/sib-styles.css"
|
||||||
|
/>
|
||||||
<div
|
<div
|
||||||
id="sib-form-container"
|
id="sib-form-container"
|
||||||
className="mt-8 sm:mx-auto sm:text-center lg:text-left lg:mx-0"
|
className="mt-8 sm:mx-auto sm:text-center lg:text-left lg:mx-0"
|
||||||
@@ -115,7 +119,6 @@ export default function NewsletterForm() {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Script
|
<Script
|
||||||
strategy="worker"
|
|
||||||
id="newsletter-submit-form"
|
id="newsletter-submit-form"
|
||||||
src="https://sibforms.com/forms/end-form/build/main.js"
|
src="https://sibforms.com/forms/end-form/build/main.js"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -5,26 +5,26 @@ const items = [
|
|||||||
{
|
{
|
||||||
title: 'Open Data Northern Ireland',
|
title: 'Open Data Northern Ireland',
|
||||||
href: 'https://www.opendatani.gov.uk/',
|
href: 'https://www.opendatani.gov.uk/',
|
||||||
image: '/images/showcases/odni.webp',
|
image: '/images/showcases/odni.png',
|
||||||
description: 'Government Open Data Portal',
|
description: 'Government Open Data Portal',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'Birmingham City Observatory',
|
title: 'Birmingham City Observatory',
|
||||||
href: 'https://www.cityobservatory.birmingham.gov.uk/',
|
href: 'https://www.cityobservatory.birmingham.gov.uk/',
|
||||||
image: '/images/showcases/birmingham.webp',
|
image: '/images/showcases/birmingham.png',
|
||||||
description: 'Government Open Data Portal',
|
description: 'Government Open Data Portal',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'UAE Open Data',
|
title: 'UAE Open Data',
|
||||||
href: 'https://opendata.fcsc.gov.ae/',
|
href: 'https://opendata.fcsc.gov.ae/',
|
||||||
image: '/images/showcases/uae.webp',
|
image: '/images/showcases/uae.png',
|
||||||
description: 'Government Open Data Portal',
|
description: 'Government Open Data Portal',
|
||||||
sourceUrl: 'https://github.com/FCSCOpendata/frontend',
|
sourceUrl: 'https://github.com/FCSCOpendata/frontend',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'Datahub Open Data',
|
title: 'Datahub Open Data',
|
||||||
href: 'https://opendata.datahub.io/',
|
href: 'https://opendata.datahub.io/',
|
||||||
image: '/images/showcases/datahub.webp',
|
image: '/images/showcases/datahub.png',
|
||||||
description: 'Demo Data Portal by DataHub',
|
description: 'Demo Data Portal by DataHub',
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { defaultConfig } from "@portaljs/core";
|
import { defaultConfig } from "@flowershow/core";
|
||||||
import userConfig from "../content/config";
|
import userConfig from "../content/config";
|
||||||
|
|
||||||
export const siteConfig: any = {
|
export const siteConfig: any = {
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 2.0 MiB |
|
Before Width: | Height: | Size: 792 KiB |
|
Before Width: | Height: | Size: 218 KiB |
|
Before Width: | Height: | Size: 192 KiB |
|
Before Width: | Height: | Size: 212 KiB |
|
Before Width: | Height: | Size: 237 KiB |
|
Before Width: | Height: | Size: 233 KiB |
|
Before Width: | Height: | Size: 168 KiB |
|
Before Width: | Height: | Size: 227 KiB |
|
Before Width: | Height: | Size: 223 KiB |
|
Before Width: | Height: | Size: 23 KiB |
|
Before Width: | Height: | Size: 276 KiB |
|
Before Width: | Height: | Size: 277 KiB |
|
Before Width: | Height: | Size: 2.2 MiB |
|
Before Width: | Height: | Size: 1.4 MiB |
|
Before Width: | Height: | Size: 296 KiB |
|
Before Width: | Height: | Size: 213 KiB |
|
Before Width: | Height: | Size: 276 KiB |
|
Before Width: | Height: | Size: 210 KiB |
|
Before Width: | Height: | Size: 212 KiB |
|
Before Width: | Height: | Size: 260 KiB |
|
Before Width: | Height: | Size: 208 KiB |
|
Before Width: | Height: | Size: 183 KiB |
|
Before Width: | Height: | Size: 271 KiB |
|
Before Width: | Height: | Size: 208 KiB |
|
Before Width: | Height: | Size: 236 KiB |
|
Before Width: | Height: | Size: 254 KiB |
|
Before Width: | Height: | Size: 280 KiB |
|
Before Width: | Height: | Size: 205 KiB |
|
Before Width: | Height: | Size: 255 KiB |
|
Before Width: | Height: | Size: 195 KiB |
|
Before Width: | Height: | Size: 105 KiB |
|
Before Width: | Height: | Size: 57 KiB |
|
Before Width: | Height: | Size: 64 KiB |
|
Before Width: | Height: | Size: 578 KiB |
|
Before Width: | Height: | Size: 305 KiB |
|
Before Width: | Height: | Size: 277 KiB |
|
Before Width: | Height: | Size: 444 KiB |
@@ -1,201 +0,0 @@
|
|||||||
---
|
|
||||||
title: Create a catalog of anything using Markdown files in Obsidian
|
|
||||||
date: 2023-05-30
|
|
||||||
authors: ['Ola Rubaj']
|
|
||||||
filetype: 'blog'
|
|
||||||
---
|
|
||||||
|
|
||||||
![[dataview.gif]]
|
|
||||||
|
|
||||||
Have you ever wanted to create a catalog of stuff? Maybe it's a list of personal projects, maybe favourite books or movies, or perhaps the options for the next smartphone you'll buy. But you've found yourself deterred by expensive software, lack of flexibility in capturing and modifying the structure of the data, or lack of control over it?
|
|
||||||
|
|
||||||
Markdown files, with their unique combination of 1) rich content, including text, images, links, and more, with 2) structured metadata allowing for data retrieval, are an excellent tool for this task. These features, coupled with the user-friendly interface of Obsidian and the analytical power of the Dataview plugin, make creating a data catalog an easy task. And it's free!
|
|
||||||
|
|
||||||
This tutorial will provide you with an easy yet extendable approach to creating your personal data catalogs using Markdown, in Obsidian.
|
|
||||||
|
|
||||||
## What are we going to build?
|
|
||||||
|
|
||||||
In this tutorial, we're going to create a catalog of characters from the Harry Potter series. Each character will have their own markdown file containing some unstructured data like character's quotes, as well as structured metadata, such as the character's name, house, creature they own, and status (alive or dead).
|
|
||||||
|
|
||||||
## Step 1: Setup
|
|
||||||
|
|
||||||
Here are the steps to get you started:
|
|
||||||
|
|
||||||
1. Download and install Obsidian from their [official website](https://obsidian.md/).
|
|
||||||
2. Create a new Obsidian vault.
|
|
||||||
1. Open the app.
|
|
||||||
2. Click the "New" button to create a new vault.
|
|
||||||
3. Choose a name and a location for your vault. For this tutorial, you might name it "Harry Potter Characters".
|
|
||||||
4. Click "Create" to create the vault.
|
|
||||||
3. Install the ["Dataview" plugin](https://github.com/blacksmithgu/obsidian-dataview) from Obsidian's community plugins.
|
|
||||||
1. Open the newly created vault in Obsidian.
|
|
||||||
2. Click on the settings icon ⚙️ in the left-hand pane to open the Settings view.
|
|
||||||
3. In the Settings view, find the "Community plugins" section and click on it.
|
|
||||||
4. Click on the button "Turn on community plugins".
|
|
||||||
5. Click on "Browse" and search for "Dataview" in the plugin browser.
|
|
||||||
6. Click on "Install" to install the Dataview plugin.
|
|
||||||
7. After the plugin is installed, click on "Enable" to enable the plugin in your vault.
|
|
||||||
|
|
||||||
## Step 2: Add some data about the characters
|
|
||||||
|
|
||||||
Let's start by creating a subfolder in our Obsidian vault, that will store all the markdown files with our characters data. Let's name it e.g. `/characters`. Then, we're going to create three markdown files in it for data about Harry Potter, Hermione, and Malfoy.
|
|
||||||
|
|
||||||
Here's an example file for Harry Potter:
|
|
||||||
|
|
||||||
```md
|
|
||||||
---
|
|
||||||
name: Harry Potter
|
|
||||||
house: Gryffindor
|
|
||||||
status: Alive
|
|
||||||
---
|
|
||||||
|
|
||||||
## Quotes
|
|
||||||
|
|
||||||
"I solemnly swear I am up to no good."
|
|
||||||
```
|
|
||||||
|
|
||||||
Here's an example file for Hermione:
|
|
||||||
|
|
||||||
```md
|
|
||||||
---
|
|
||||||
name: Hermione Granger
|
|
||||||
house: Gryffindor
|
|
||||||
status: Alive
|
|
||||||
---
|
|
||||||
|
|
||||||
## Quotes
|
|
||||||
|
|
||||||
"Books! And cleverness! There are more important things - friendship and bravery."
|
|
||||||
```
|
|
||||||
|
|
||||||
Here's an example file for Malfoy:
|
|
||||||
|
|
||||||
```md
|
|
||||||
---
|
|
||||||
name: Draco Malfoy
|
|
||||||
house: Slytherin
|
|
||||||
status: Alive
|
|
||||||
---
|
|
||||||
|
|
||||||
Draco Malfoy is Harry's rival and a member of Slytherin House.
|
|
||||||
|
|
||||||
## Quotes
|
|
||||||
|
|
||||||
"My father will hear about this!"
|
|
||||||
```
|
|
||||||
|
|
||||||
By the end of this step you should end up with the following folder structure:
|
|
||||||
|
|
||||||
```
|
|
||||||
Harry Potter Characters
|
|
||||||
└── characters
|
|
||||||
├── Harry Potter.md
|
|
||||||
├── Hermione.md
|
|
||||||
└── Malfoy.md
|
|
||||||
```
|
|
||||||
|
|
||||||
## Step 3: Create a data catalog
|
|
||||||
|
|
||||||
Now let's create a data catalog of our Harry Potter characters!
|
|
||||||
|
|
||||||
In the root of our vault, let's create a file called `Catalog` (but you can name it whatever you want) that's going to display a table with all our characters and their metadata. Inside it, we're going to write the following code block with a simple Dataview query:
|
|
||||||
|
|
||||||
````md
|
|
||||||
```dataview
|
|
||||||
table name, house, status
|
|
||||||
from "characters"
|
|
||||||
sort name asc
|
|
||||||
```
|
|
||||||
````
|
|
||||||
|
|
||||||
Let's break it down:
|
|
||||||
|
|
||||||
- `table name, house, status`: This line instructs Dataview to create a table that includes columns for "name", "house", and "status". These are fields that you should have defined in your markdown files' frontmatter, but you don't have to. For any missing field Dataview will just display `-`.
|
|
||||||
- `from "characters"`: This tells Dataview to pull the data from all markdown files located in the `/characters` folder in your Obsidian vault.
|
|
||||||
- `sort name asc`: This command sorts the data based on the "name" field, in ascending order.
|
|
||||||
|
|
||||||
After you click somewhere outside of this code block, you should see the following table:
|
|
||||||
|
|
||||||
![[table1.png]]
|
|
||||||
|
|
||||||
## Step 4: Add more metadata about characters
|
|
||||||
|
|
||||||
We can continue to add more information about our characters. For example, let's add creatures each character owns:
|
|
||||||
|
|
||||||
```md=
|
|
||||||
---
|
|
||||||
name: Harry Potter
|
|
||||||
...
|
|
||||||
creature: Hedwig (Owl)
|
|
||||||
---
|
|
||||||
|
|
||||||
...
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
name: Hermione Granger
|
|
||||||
...
|
|
||||||
creature: Crookshanks (Cat)
|
|
||||||
---
|
|
||||||
|
|
||||||
...
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
Now, if we want to show this field in our Dataview table, we need to add it to the first line of our dataview code block:
|
|
||||||
|
|
||||||
````md
|
|
||||||
```dataview
|
|
||||||
table name, house, status, creature
|
|
||||||
from "characters"
|
|
||||||
sort name asc
|
|
||||||
```
|
|
||||||
````
|
|
||||||
|
|
||||||
After clicking somewhere outside the code block, you should see an updated table that includes the column `creature`.
|
|
||||||
|
|
||||||
![[table2.png]]
|
|
||||||
|
|
||||||
## Step 5: Enrich content with images and links
|
|
||||||
|
|
||||||
To enhance your catalog, you can add images of characters, and links to other characters in your markdown content.
|
|
||||||
|
|
||||||
#### Add images
|
|
||||||
|
|
||||||
To add an image of a character, right-click on the image you want to embed and copy it to clipboard - you can use the image below - and paste it directly in your markdown file.
|
|
||||||
|
|
||||||
![[harry_original.png]]
|
|
||||||
|
|
||||||
Obsidian will automatically save the file to the root of your vault and create a link to it in your content, so you'll end up with a link similar to this one:
|
|
||||||
|
|
||||||
```md
|
|
||||||
![[Pasted image 20230525212302.png]]
|
|
||||||
```
|
|
||||||
|
|
||||||
Which will render as:
|
|
||||||
|
|
||||||
![[harry.png]]
|
|
||||||
|
|
||||||
#### Add links to other pages
|
|
||||||
|
|
||||||
Obsidian allows you to create links between different notes (or in this case, characters). To add a link to another character, use the double bracket `[[]]` syntax.
|
|
||||||
|
|
||||||
For example, if you're writing about Harry Potter and want to mention that he is friends with Hermione, you can link to Hermione's markdown file like this:
|
|
||||||
|
|
||||||
```md
|
|
||||||
He is a friend of [[Hermione]].
|
|
||||||
```
|
|
||||||
|
|
||||||
When you click on Hermione's name in the rendered link, Obsidian will take you to Hermione's file in your vault. If you hover over it, you'll see a preview of Hermione's file:
|
|
||||||
|
|
||||||
![[link-preview.png]]
|
|
||||||
|
|
||||||
## Summary
|
|
||||||
|
|
||||||
And there you have it! We've walked through the process of creating a personal data catalog using markdown files, the Obsidian application, and its Dataview plugin. We've transformed scattered data into a well-organized, easy-to-navigate, and visually pleasing catalog. Whether you've used our example of a Harry Potter character catalog or applied these steps to a different topic of your choosing, we hope you've found this tutorial helpful and empowering.
|
|
||||||
|
|
||||||
Happy data cataloging!
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
You can find the [vault created in this tutorial here](https://github.com/datopian/markdowndb/tree/main/examples/obsidian-dataview).
|
|
||||||
@@ -1,190 +0,0 @@
|
|||||||
---
|
|
||||||
title: 'Tutorial 1: Create a website from scratch using Markdown and Flowershow'
|
|
||||||
date: 2023-06-20
|
|
||||||
authors: ['Ola Rubaj']
|
|
||||||
filetype: 'blog'
|
|
||||||
---
|
|
||||||
|
|
||||||
In this tutorial we will walk you through creating an elegant, fully functional website written in simple markdown and published with Flowershow.
|
|
||||||
|
|
||||||
By the end of this tutorial you will:
|
|
||||||
|
|
||||||
- have a working markdown-based website powered by Flowershow.
|
|
||||||
- be able to edit the text and add pages, all from an online interface without installing anything.
|
|
||||||
|
|
||||||
Below is a screenshot of how the final website will look like:
|
|
||||||
![[tutorial-1-result.png]]
|
|
||||||
|
|
||||||
## Prerequisites
|
|
||||||
|
|
||||||
- A [GitHub](https://github.com/) account.
|
|
||||||
- A [Vercel](https://vercel.com/) account. You can set it up using your GitHub account.
|
|
||||||
|
|
||||||
## Setting up a sandbox website
|
|
||||||
|
|
||||||
### 1. Navigate to the [datopian/flowershow repository](https://github.com/datopian/flowershow).
|
|
||||||
|
|
||||||
### 2. Scroll down and click on the "Deploy" button
|
|
||||||
|
|
||||||
After clicking on it, you'll be redirected to Vercel's "Create Git Repository" page.
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
### 3. Select your GitHub account in "Git Scope"
|
|
||||||
|
|
||||||
Click on "Select Git Scope" dropdown and select your GitHub account name from the list if it's there.
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
If your GitHub account is not available in the dropdown list, click on "Add GitHub Account"...
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
...and authorize Vercel to access your GitHub repositories by clicking "Install".
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
Now you can select your GitHub account.
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
### 4. Give your repository a name
|
|
||||||
|
|
||||||
A good practice is to use lowercase and dashes.
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
### 5. Click on "Create" and wait until the site deploys
|
|
||||||
|
|
||||||
After you click "Create", Vercel will create a new repository on your GitHub account, using the `datopian/flowershow` repository as a template. Then, it will immediately start buidling the initial version of your website. This may take about 1-2 minutes.
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
### 6. See your published website!
|
|
||||||
|
|
||||||
And voila! Your site is up and running. Once on the "Congratulations" screen, navigate to the project dashboard...
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
... and click on "Visit" to see your published website!
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
## Editing a page on your website
|
|
||||||
|
|
||||||
Once your site is up and running, the next step is to customize it to your liking. Let's start by editing our home page.
|
|
||||||
|
|
||||||
### 1. Navigate to the repository of your website on GitHub
|
|
||||||
|
|
||||||
You can get there by going to GitHub, clicking on your profile icon, and going to "Your repositories".
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
Or, you can navigate to [your Vercel dashboard](https://vercel.com/dashboard), select your project in the "Overview" tab...
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
...and click on "Git Repository". You'll be redirected to the repository of your website on GitHub.
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
### 2. Navigate to the "content" folder
|
|
||||||
|
|
||||||
This is where all the Markdown-based pages live in a Flowershow-based project.
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
### 3. Edit the "index.md" file
|
|
||||||
|
|
||||||
The homepage on your website is built with the "index.md" file in the root of the "content" folder. Click on it to open.
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
Then, click on the "Edit this file" icon...
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
... and add some content.
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
### 4. Save your changes
|
|
||||||
|
|
||||||
To see your changes live, you need to "commit" them. Click on "Commit changes..." buttom in the top-tight corner.
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
In the "Commit message" field add a concise description of your changes. Optionally, if the commit message is not enough, you can add more info in the "Extended description" field.
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
Leave "Commit directly to `main` branch" selected and click on "Commit changes". Doing that will trigger rebuilding of your site on Vercel.
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
### 5. See your site getting rebuild
|
|
||||||
|
|
||||||
If you want to see the current progress of rebuilding your website after you've commited the changes, click on the dot next to your commit message.
|
|
||||||
|
|
||||||
> [!note]
|
|
||||||
> It will be either a dot (if the site is currently being rebuilt after your changes), a check mark (if the site has finished building) or a cross (if something went wrong when rebuilding it).
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
Click on "Details" to see your project's deployment status on Vercel.
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
### 6. Preview your site after changes
|
|
||||||
|
|
||||||
Once the site has been rebuilt, click the preview to see your changes live.
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
## Add a single Markdown-based page
|
|
||||||
|
|
||||||
### 1. Navigate to the "content" folder in your website's repository
|
|
||||||
|
|
||||||
See how to find it in the previous section.
|
|
||||||
|
|
||||||
### 2. Create new file
|
|
||||||
|
|
||||||
Click on "Add file"...
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
...and "Create new file".
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
### 3. Type the name of the new file you want to create
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
### 4. Write the content of the file
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
### 5. Save your changes
|
|
||||||
|
|
||||||
To see your changes live, you need to "commit" them. Click on "Commit changes..." buttom in the top-tight corner.
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
In the "Commit message" field add a concise description of your changes. Optionally, if the commit message is not enough, you can add more info in the "Extended description" field.
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
Leave "Commit directly to `main` branch" selected and click on "Commit changes". Doing that will trigger rebuilding of your site on Vercel.
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
### 6. Preview your site after changes
|
|
||||||
|
|
||||||
As you already know, Vercel will now start rebuilding your website. When it's done, you can navigate to `/about` url on your website to see the new file we've just added.
|
|
||||||
|
|
||||||
## What's next?
|
|
||||||
|
|
||||||
While editing on GitHub UI is acceptable, it has its limitations – it doesn't support working offline, adding multiple files simultaneously, or previewing many markdown syntax elements supported by Flowershow-based websites. We'll delve into these issues and solutions to overcome them in our next tutorial. Stay tuned!
|
|
||||||
@@ -1,250 +0,0 @@
|
|||||||
---
|
|
||||||
title: 'Tutorial 2: Edit your Flowershow website locally on your computer'
|
|
||||||
date: 2023-06-22
|
|
||||||
authors: ['Ola Rubaj']
|
|
||||||
filetype: 'blog'
|
|
||||||
---
|
|
||||||
|
|
||||||
In this tutorial, we will walk you through the process of editing your Flowershow website locally on your computer.
|
|
||||||
|
|
||||||
By the end of this tutorial, you will:
|
|
||||||
|
|
||||||
- Understand what is Markdown and why you should use Obsidian to edit content on your Flowershow websites in most cases.
|
|
||||||
- Gain a deeper understanding of working with Git and GitHub Desktop.
|
|
||||||
- Learn how to clone your website's repository to your computer.
|
|
||||||
- Learn how to edit content using Obsidian and what are the benefits of it.
|
|
||||||
- Learn how to commit (save) your changes locally and push them back to the GitHub repository.
|
|
||||||
|
|
||||||
Below is a screenshot of how the final website will look like:
|
|
||||||
|
|
||||||
![[tutorial-2-result.png]]
|
|
||||||
|
|
||||||
Let's start by understanding why using GitHub UI as we did in tutorial 1 is not always a good choice.
|
|
||||||
|
|
||||||
## What is Markdown and why use Obsidian for editing it in Flowershow-based websites
|
|
||||||
|
|
||||||
While editing on the GitHub UI is convenient, there are certain limitations and drawbacks to consider. For example, you can't work offline, can't add or edit multiple files simultaneously, and the GitHub UI's preview doesn't render all the Markdown syntax elements that are supported by Flowershow-based websites.
|
|
||||||
|
|
||||||
Ok, but, what exactly is Markdown?
|
|
||||||
|
|
||||||
[Markdown](https://en.wikipedia.org/wiki/Markdown) is a simple and intuitive syntax, that allows you to structure your text documents. It's widely used for creating content on the web. As Wikipedia puts it:
|
|
||||||
|
|
||||||
> *Markdown is a lightweight [**markup language**](https://en.wikipedia.org/wiki/Markup_language) for creating [**formatted text**](https://en.wikipedia.org/wiki/Formatted_text) using a **plain-text editor**.*
|
|
||||||
|
|
||||||
The key term here is "markup language". In simple terms, it is a way to annotate or "mark up" a plain text document with symbols that describe how the document is structured and so, how it should be understood, processed, or displayed. These tags can tell a computer program that supports this syntax (e.g. a website) how to format the rendered text, for example: which words should be bold or italic, where to insert images, when to start new paragraphs, how to create tables, and so forth. It's important to note, that even though Markdown symbols change how the text is displayed when it's rendered by a Markdown-compatible viewer, the underlying document in still just a plain text. For instance, if you want to create a heading in Markdown, you use the "#" symbol before your heading text, like this:
|
|
||||||
|
|
||||||
```md
|
|
||||||
# This is a Heading
|
|
||||||
```
|
|
||||||
|
|
||||||
BUT, there is no single version of Markdown. It comes in different "flavours", with CommonMark and GitHub Flavoured Markdown (GFM) being the most popular ones. And different tools supporting Markdown may use their own specific versions of Markdown. The two tools relevant in the context of this guide are Obsidian and Flowershow.
|
|
||||||
|
|
||||||
Obsidian supports an extended version of Markdown that includes majority of elements from CommonMark and GFM, while also introducing its own unique features, like [wiki-links](https://help.obsidian.md/Linking+notes+and+files/Internal+links) or [callouts](https://help.obsidian.md/Editing+and+formatting/Callouts). And Flowershow template is Obsidian-compatible, meaning it supports (or aims to support) all of the Obsidian Markdown features.
|
|
||||||
|
|
||||||
To make things a bit clearer, there are:
|
|
||||||
|
|
||||||
- CommonMark
|
|
||||||
- GitHub Flavored Markdown (GFM) - superset of CommonMark
|
|
||||||
- "Obsidian flavoured Markdown" - majority of the GFM + some extra elements, like wiki-links or callouts
|
|
||||||
- "Flowershow flavoured Markdown" - majority of Obsidian-supported syntax
|
|
||||||
|
|
||||||
As you might have guessed by now, GitHub UI will only preview GitHub Flavoured Markdown. And while it's a majority of Markdown you would write, it won't be able to render Obsidian-only (or Flowershow-only) syntax elements, like callouts, wiki-links or inline table of contents.
|
|
||||||
|
|
||||||
Another drawback of GitHub UI, is that it doesn't allow you to make changes (or to add) multiple files at once. Changes to each file will have to be commited (saved) separately, which can introduce a bit of a mess to your GitHub history, and may be cumbersome (e.g. if you want to update a link to some page on 10 other pages...).
|
|
||||||
|
|
||||||
Ok, now we have this sorted, let's dive in and start editing your Flowershow website locally!
|
|
||||||
|
|
||||||
## Prerequisites
|
|
||||||
|
|
||||||
- A [GitHub](https://github.com/) account.
|
|
||||||
- A [Vercel](https://vercel.com/) account. You can set it up using your GitHub account.
|
|
||||||
- [Obsidian](https://obsidian.md/) installed
|
|
||||||
- [GitHub Desktop](https://desktop.github.com/) installed
|
|
||||||
- [[create-a-website-from-scratch|Tutorial 1: Create a website from scratch using Markdown and PortalJS]] completed - this one is recommended so that you have the same sandbox website we're working on in this tutorial.
|
|
||||||
|
|
||||||
## Clone the GitHub repository on your computer
|
|
||||||
|
|
||||||
### 1. Open GitHub Desktop app
|
|
||||||
|
|
||||||
### 2. Click on "Clone a Repository from the Internet..."
|
|
||||||
|
|
||||||
Or click on "File" -> "Clone repository".
|
|
||||||
|
|
||||||
![[gh-desktop-starting-screen.png]]
|
|
||||||
|
|
||||||
If this is the first time you're using GitHub Desktop, it will prompt you to log in to your GitHub account. Click "Sign In" and follow the prompts.
|
|
||||||
|
|
||||||
![[gh-desktop-clone-signin.png]]
|
|
||||||
|
|
||||||
Once you're done and you've authorised GitHub Desktop to access your repositories, go back to GitHub Desktop. You should now see a list of all your GitHub repositories.
|
|
||||||
|
|
||||||
### 3. Select the repository you want to clone
|
|
||||||
|
|
||||||
![[gh-desktop-clone.png]]
|
|
||||||
|
|
||||||
### 4. Choose where your repository should be saved
|
|
||||||
|
|
||||||
Type the path manually or click "Choose..." to find it using file explorer.
|
|
||||||
|
|
||||||
![[gh-desktop-clone-path-select.png]]
|
|
||||||
|
|
||||||
### 5. Click "Clone" and wait for the process to complete
|
|
||||||
|
|
||||||
You should now see the following screen:
|
|
||||||
|
|
||||||
![[gh-desktop-no-local-changes.png]]
|
|
||||||
|
|
||||||
Done! You've successfully cloned your website's repository on your computer! 🎉
|
|
||||||
|
|
||||||
## Edit your site in Obsidian
|
|
||||||
|
|
||||||
### 1. Open Obsidian
|
|
||||||
|
|
||||||
### 2. Open your repository's `/content` folder as vault
|
|
||||||
|
|
||||||
Click on "Open" in "Open folder as vault" section and select the path to the `/content` of the cloned repository.
|
|
||||||
|
|
||||||
![[obsidian-starting.png]]
|
|
||||||
Now you're ready to edit your site! In the left-hand side panel you should see the two files we created in [[create-a-website-from-scratch|tutorial 1]]: `index.md` and `about.md`.
|
|
||||||
|
|
||||||
![[obsidian-content-start.png]]
|
|
||||||
|
|
||||||
### 3. Edit your site's content
|
|
||||||
|
|
||||||
Now, let's add some more stuff to the home page.
|
|
||||||
|
|
||||||
Click on `index.md` to open it and replace the dummy text with "About Me" section, .e.g.:
|
|
||||||
|
|
||||||
```md
|
|
||||||
## About Me
|
|
||||||
|
|
||||||
Hey there! I'm Your Name, a passionate learner and explorer of ideas.
|
|
||||||
```
|
|
||||||
|
|
||||||
![[obsidian-content-index-edit.png]]
|
|
||||||
|
|
||||||
Now, let's say for more information about you and your site, you want to add link to the about page. You can do so, by creating a wiki-link to that page, like so:
|
|
||||||
|
|
||||||
```md
|
|
||||||
[[about]]
|
|
||||||
```
|
|
||||||
|
|
||||||
When you start typing, after writing empty double brackets `[[]]`, Obsidian will suggest all the available pages you can link to, and after you select one it will create the link automatically for you.
|
|
||||||
|
|
||||||
![[obsidian-content-index-add-link.png]]
|
|
||||||
![[obsidian-content-index-add-link-2.png]]
|
|
||||||
|
|
||||||
Now, let's say you want to show people what books you've read and share your reviews and other information on each one. And let's say information on each book should be available at `/books/abc` path on our website. To achieve this, you need to create a folder called `books` in your vault and add all the project-related files in there.
|
|
||||||
|
|
||||||
To create a new folder in Obsidian, click on the "New folder" icon, and give your folder a name:
|
|
||||||
|
|
||||||
![[obsidian-content-add-folder.png]]
|
|
||||||
|
|
||||||
![[obsidian-content-add-folder-2.png]]
|
|
||||||
|
|
||||||
Now, let's write some book reviews. You can do this by right-clicking on the `/books` folder, and selecting "New note" option. Rename the newly created `Untitled.md` file and add some review in it. Then add some other reviews.
|
|
||||||
|
|
||||||
![[obsidian-content-book.png]]
|
|
||||||
|
|
||||||
Ok, now let's make a page that will list all your books reviews - our Bookshelf! It would be nice to have it available under `/books` path on the website, since each of our books will be available under `/books/abc`. To achieve this, we have to create an index page **inside** our `/books` folder, like so:
|
|
||||||
|
|
||||||
![[obsidian-content-book-index.png]]
|
|
||||||
|
|
||||||
Then, let's list our book reviews with wiki-links to their pages:
|
|
||||||
|
|
||||||
![[obsidian-content-book-index-2.png]]
|
|
||||||
|
|
||||||
![[obsidian-content-book-index-3.png]]
|
|
||||||
|
|
||||||
Now, let's add a link to our Bookshelf page on our home page, so that it's easy to find.
|
|
||||||
|
|
||||||
![[obsidian-content-index-bookshelf-link.png]]
|
|
||||||
|
|
||||||
Now, if you want to have your link say something different than the raw `books/index`, you can do this by typing `|` after the path and specifying an alternative name, .e.g:
|
|
||||||
|
|
||||||
```md
|
|
||||||
[[books/index|Bookshelf]]
|
|
||||||
```
|
|
||||||
|
|
||||||
Let's also do this for the about page:
|
|
||||||
|
|
||||||
```md
|
|
||||||
[[about|About me]]
|
|
||||||
```
|
|
||||||
|
|
||||||
![[obsidian-content-index-link-aliases.png]]
|
|
||||||
|
|
||||||
That's better!
|
|
||||||
|
|
||||||
Now, let's maybe add a short info at the bottom, that this site is new and is currently being worked on, in form of an Obsidian callout:
|
|
||||||
|
|
||||||
```md
|
|
||||||
> [!info]
|
|
||||||
> 🚧 This site it pretty new and I'm enhancing it every day. Stay tuned!
|
|
||||||
```
|
|
||||||
|
|
||||||
![[obsidian-content-index-callout.png]]
|
|
||||||
|
|
||||||
Great, our updated site is ready to be published! 🔥
|
|
||||||
|
|
||||||
## Save and publish your changes
|
|
||||||
|
|
||||||
### 1. Navigate to GitHub Desktop app
|
|
||||||
|
|
||||||
In the "Changes" tab, you'll see all the changes that have been made to the repository.
|
|
||||||
|
|
||||||
![[github-desktop-all-changed-files.png]]
|
|
||||||
|
|
||||||
All the new files will have `[+]` sign next to them, and all the edited files will have `[•]`.
|
|
||||||
|
|
||||||
### 2. Commit your changes
|
|
||||||
|
|
||||||
Now, to save these changes we need to "commit" them, which is a fancy term for making a checkpoint of the state at which your repository is currently in.
|
|
||||||
|
|
||||||
Let's make this checkpoint! In the bottom left corner there is a "Summary (required)" field, which is the place for a commit message - a concise description of the changes you made. The "Description" field is optional, and it's only needed if you need to add some more information about your changes that doesn't fit in the commit message.
|
|
||||||
|
|
||||||
![[github-desktop-add-commit-message.png]]
|
|
||||||
|
|
||||||
Now, hit the "Commit to main" button, and done! Now GitHub Desktop should say there are no local changes again. And that's correct, as all the changes we made have successfully been saved, and no other changes have been made since then.
|
|
||||||
|
|
||||||
You should see the last commit message under the button:
|
|
||||||
|
|
||||||
![[github-desktop-commit-message.png]]
|
|
||||||
|
|
||||||
You can also inspect the whole history of past commits in the "History tab".
|
|
||||||
|
|
||||||
![[github-desktop-history.png]]
|
|
||||||
|
|
||||||
The very fist commit on top is the commit we've just made, but you can also see all the commits to the repository we made in [[create-a-website-from-scratch|tutorial 1]], via GitHub UI.
|
|
||||||
|
|
||||||
### 3. Push your changes to the remote repository
|
|
||||||
|
|
||||||
The commit we've just crated has ↑ sign next to it. It means it hasn't yet been pushed to our remote version of the repository - the changes you made has been saved, but only locally on your computer. In order to push them to the cloud copy of the repository (aka "remote"), click "↑ Push origin (1 ↑)" tab.
|
|
||||||
|
|
||||||
When the "push" is complete, the arrow next to the last commit message should disappear, and there should be no `(1 )` indicator next to "Push origin" button.
|
|
||||||
|
|
||||||
![[github-desktop-history-after-push.png]]
|
|
||||||
|
|
||||||
### 4. See updated site live!
|
|
||||||
|
|
||||||
Navigate to your [vercel dashboard](https://vercel.com/dashboard).
|
|
||||||
|
|
||||||
![[vercel-dashboard.png]]
|
|
||||||
|
|
||||||
Click on the project repository to go to its dashboard.
|
|
||||||
|
|
||||||
You may have to wait a bit until the site builds, but once it's ready, you should see the preview with our latest changes.
|
|
||||||
|
|
||||||
![[vercel-project-dashboard.png]]
|
|
||||||
|
|
||||||
Note, that under "SOURCE" section (next to the preview) there is also our last commit message, indicating that the latest deployment has been triggered by this commit.
|
|
||||||
|
|
||||||
Click on the preview to see the updated site live!
|
|
||||||
|
|
||||||
![[tutorial-2-result.png]]
|
|
||||||
![[live-book-home-page.png]]
|
|
||||||
![[live-book.png]]
|
|
||||||
|
|
||||||
Congratulations!
|
|
||||||
|
|
||||||
You have successfully completed the tutorial on editing your Flowershow website locally on your computer. By utilising Obsidian and GitHub Desktop, you have unlocked powerful editing capabilities and improved the overall publishing process. Now, you can enjoy the benefits of offline editing, simultaneous file editing, and previewing the extended Markdown syntax provided by Obsidian.
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
---
|
---
|
||||||
title: "Setup Guide: How to create a full-featured custom data portal frontend for CKAN with PortalJS"
|
title: "PortalJS example 1: Create a full-featured custom data portal frontend for CKAN with PortalJS"
|
||||||
authors: ['Luccas Mateus']
|
authors: ['Luccas Mateus']
|
||||||
date: 2021-04-20
|
date: 2021-04-20
|
||||||
---
|
---
|
||||||
|
|||||||
@@ -142,7 +142,7 @@ process.exit(0);
|
|||||||
|
|
||||||
Since we're using ES6 modules, we also need to add `"type": "module"` to our `package.json` file.
|
Since we're using ES6 modules, we also need to add `"type": "module"` to our `package.json` file.
|
||||||
|
|
||||||
Before we run the above script, we need to make sure that the `dbPath` variable is pointing to our `markdown.db` file. If you want to store the database outside of your project folder, you can update the `dbPath` variable to point to the correct location. If you want to have it inside your project folder, you can copy it there, or simply re-run the `npx mddb <path-to-markdown-folder>` command from within your project folder.
|
Before we run the above script, we need to make sure that the `dbPath` variable is pointing to our `markdown.db` file. If you want to store the database outside of your project folder, you can update the `dbPath` variable to point to the correct location. If you want to have it inside your project folder, you can copy it there, or simply re-run the `npx mddb` {'<'}path-to-markdown-folder{'>'}` command from within your project folder.
|
||||||
|
|
||||||
Now, let's run the script:
|
Now, let's run the script:
|
||||||
|
|
||||||
|
|||||||
@@ -1,68 +1,60 @@
|
|||||||
const config = {
|
const config = {
|
||||||
title: 'PortalJS',
|
title:
|
||||||
|
"PortalJS",
|
||||||
description:
|
description:
|
||||||
'PortalJS is a framework for rapidly building rich data portal frontends using a modern frontend approach. PortalJS can be used to present a single dataset or build a full-scale data catalog/portal.',
|
"PortalJS is a framework for rapidly building rich data portal frontends using a modern frontend approach. PortalJS can be used to present a single dataset or build a full-scale data catalog/portal.",
|
||||||
theme: {
|
theme: {
|
||||||
default: 'dark',
|
default: "dark",
|
||||||
toggleIcon: '/images/theme-button.svg',
|
toggleIcon: "/images/theme-button.svg",
|
||||||
},
|
},
|
||||||
author: 'Datopian',
|
author: "Datopian",
|
||||||
authorLogo: '/datopian-logo.webp',
|
authorLogo: "/datopian-logo.png",
|
||||||
authorUrl: 'https://datopian.com/',
|
authorUrl: "https://datopian.com/",
|
||||||
navbarTitle: {
|
navbarTitle: {
|
||||||
// logo: "/images/logo.svg",
|
// logo: "/images/logo.svg",
|
||||||
text: '🌀 PortalJS',
|
text: "🌀 PortalJS",
|
||||||
// version: "Alpha",
|
// version: "Alpha",
|
||||||
},
|
},
|
||||||
navLinks: [
|
navLinks: [
|
||||||
{ name: 'Docs', href: '/docs' },
|
{ name: "Docs", href: "/docs" },
|
||||||
// { name: "Components", href: "/docs/components" },
|
// { name: "Components", href: "/docs/components" },
|
||||||
{ name: 'Blog', href: '/blog' },
|
{ name: "Blog", href: "/blog" },
|
||||||
{ name: 'Showcases', href: '/#showcases' },
|
{ name: "Showcases", href: "/#showcases" },
|
||||||
{ name: 'Howtos', href: '/howtos' },
|
{ name: "Howtos", href: "/howto" },
|
||||||
{ name: 'Guide', href: '/guide' },
|
{ name: "Examples", href: "https://github.com/datopian/portaljs/tree/main/examples", target: "_blank" },
|
||||||
{
|
{ name: "Components", href: "https://storybook.portaljs.org", target: "_blank" },
|
||||||
name: 'Examples',
|
|
||||||
href: 'https://github.com/datopian/portaljs/tree/main/examples',
|
|
||||||
target: '_blank',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Components',
|
|
||||||
href: 'https://storybook.portaljs.org',
|
|
||||||
target: '_blank',
|
|
||||||
},
|
|
||||||
// { name: "DL Demo", href: "/data-literate/demo" },
|
// { name: "DL Demo", href: "/data-literate/demo" },
|
||||||
// { name: "Excel Viewer", href: "/excel-viewer" },
|
// { name: "Excel Viewer", href: "/excel-viewer" },
|
||||||
],
|
],
|
||||||
footerLinks: [],
|
footerLinks: [],
|
||||||
nextSeo: {
|
nextSeo: {
|
||||||
openGraph: {
|
openGraph: {
|
||||||
type: 'website',
|
type: "website",
|
||||||
title:
|
title:
|
||||||
'PortalJS - rapidly build rich data portals using a modern frontend framework.',
|
"PortalJS - rapidly build rich data portals using a modern frontend framework.",
|
||||||
description:
|
description:
|
||||||
'PortalJS is a framework for rapidly building rich data portal frontends using a modern frontend approach. PortalJS can be used to present a single dataset or build a full-scale data catalog and portal.',
|
"PortalJS is a framework for rapidly building rich data portal frontends using a modern frontend approach. PortalJS can be used to present a single dataset or build a full-scale data catalog and portal.",
|
||||||
locale: 'en_US',
|
locale: "en_US",
|
||||||
images: [
|
images: [
|
||||||
{
|
{
|
||||||
url: '/homepage-screenshot.png', // TODO
|
url: "/homepage-screenshot.png", // TODO
|
||||||
alt: 'PortalJS - rapidly build rich data portals using a modern frontend framework.',
|
alt: "PortalJS - rapidly build rich data portals using a modern frontend framework.",
|
||||||
width: 1280,
|
width: 1280,
|
||||||
height: 720,
|
height: 720,
|
||||||
type: 'image/jpg',
|
type: "image/jpg",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
twitter: {
|
twitter: {
|
||||||
handle: '@datopian',
|
handle: "@datopian",
|
||||||
site: 'https://datopian.com/',
|
site: "https://datopian.com/",
|
||||||
cardType: 'summary_large_image',
|
cardType: "summary_large_image",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
github: 'https://github.com/datopian/portaljs',
|
github: "https://github.com/datopian/portaljs",
|
||||||
discord: 'https://discord.gg/EeyfGrGu4U',
|
discord: "https://discord.gg/EeyfGrGu4U",
|
||||||
tableOfContents: true,
|
tableOfContents: true,
|
||||||
analytics: 'G-96GWZHMH57',
|
analytics: "G-96GWZHMH57",
|
||||||
// editLinkShow: true,
|
// editLinkShow: true,
|
||||||
};
|
};
|
||||||
export default config;
|
export default config;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
---
|
---
|
||||||
title: "Setup Guide: Create a data catalog with PortalJS and CKAN to share your data stories"
|
title: "Example: Data catalog with data on CKAN"
|
||||||
authors: ['Luccas Mateus']
|
authors: ['Luccas Mateus']
|
||||||
date: 2023-04-20
|
date: 2023-04-20
|
||||||
filetype: blog
|
filetype: blog
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
---
|
---
|
||||||
title: "Setup Guide: Create a data catalog to display and share your GitHub datasets with PortalJS"
|
title: "Example: Data catalog with data on GitHub"
|
||||||
authors: ['Luccas Mateus']
|
authors: ['Luccas Mateus']
|
||||||
date: 2023-04-20
|
date: 2023-04-20
|
||||||
filetype: blog
|
filetype: blog
|
||||||
@@ -63,7 +63,7 @@ Congratulations, your new app is now running at http://localhost:3000.
|
|||||||
|
|
||||||
[](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Fdatopian%2Fportaljs%2Ftree%2Fmain%2Fexamples%2Fgithub-backed-catalog)
|
[](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Fdatopian%2Fportaljs%2Ftree%2Fmain%2Fexamples%2Fgithub-backed-catalog)
|
||||||
|
|
||||||
By clicking on this button, you will be redirected to a page which will allow you to clone the example into your own GitHub/GitLab/BitBucket account and automatically deploy it.
|
By clicking on this button, you will be redirected to a page which will allows you to clone the example into your own GitHub/GitLab/BitBucket account and automatically deploy it.
|
||||||
|
|
||||||
### GitHub token
|
### GitHub token
|
||||||
|
|
||||||
@@ -101,7 +101,7 @@ It has
|
|||||||
You can also add
|
You can also add
|
||||||
|
|
||||||
- A `description` which is useful if you have more than one dataset for each repo, if not provided we are just going to use the repo description
|
- A `description` which is useful if you have more than one dataset for each repo, if not provided we are just going to use the repo description
|
||||||
- A `Name` which is useful if you want to give your dataset a nice name, if not provided we are going to use the junction of the `owner` the `repo` + the path of the README, in the example above it will be `fivethirtyeight/data/nba-raptor`
|
- A `Name` which is useful if you want to give your dataset a nice name, if not provided we are going to use the junction of the `owner` the `repo` + the path of the README, in the exaple above it will be `fivethirtyeight/data/nba-raptor`
|
||||||
|
|
||||||
## Extra commands
|
## Extra commands
|
||||||
|
|
||||||
|
|||||||