Compare commits
131 Commits
drd-storie
...
feature/si
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
709b10cef3 | ||
|
|
ec8c5d27e8 | ||
|
|
cf6fab2c4f | ||
|
|
5088afe69a | ||
|
|
95af903642 | ||
|
|
9fee284097 | ||
|
|
11a4272f16 | ||
|
|
349f5bea66 | ||
|
|
6aef860a81 | ||
|
|
e908cb9344 | ||
|
|
1a22e54d5b | ||
|
|
172b4b71d4 | ||
|
|
3873852567 | ||
|
|
5e349855a2 | ||
|
|
40bd9e0311 | ||
|
|
b437b58d06 | ||
|
|
c3137ba1cb | ||
|
|
2e13c1b738 | ||
|
|
122870a23e | ||
|
|
4e282e0d86 | ||
|
|
6020f76adb | ||
|
|
f3c2a2ffa7 | ||
|
|
11659a838b | ||
|
|
58b7b4e753 | ||
|
|
7cf8c31e53 | ||
|
|
df000b9e8f | ||
|
|
77e9f58899 | ||
|
|
0737aaafb2 | ||
|
|
d798f402f6 | ||
|
|
80c6221a05 | ||
|
|
f04b86dda4 | ||
|
|
0fd3ee9912 | ||
|
|
cb0b9b1f14 | ||
|
|
9ee4376abf | ||
|
|
5a0ddd91ce | ||
|
|
d097bc765b | ||
|
|
b283fc1e99 | ||
|
|
0511e00d83 | ||
|
|
c8afa775d4 | ||
|
|
7ba9b5157e | ||
|
|
6c2a1ea125 | ||
|
|
343faf72cf | ||
|
|
1eb3f7367b | ||
|
|
8cdf54397f | ||
|
|
fb94cb9ce9 | ||
|
|
4595cd2231 | ||
|
|
aa2c8aac04 | ||
|
|
f2e5459297 | ||
|
|
e111adfe73 | ||
|
|
492c21ca4e | ||
|
|
0581357df8 | ||
|
|
15ceeec035 | ||
|
|
1caabcf6b4 | ||
|
|
b548dfd113 | ||
|
|
c5ee257d48 | ||
|
|
8d83f3a900 | ||
|
|
add2f6d0f3 | ||
|
|
dfab6aa318 | ||
|
|
f96fb562fe | ||
|
|
c4bf5bd054 | ||
|
|
c706575ae4 | ||
|
|
ed8de380a9 | ||
|
|
33521916d6 | ||
|
|
04206457a4 | ||
|
|
8a5acb7012 | ||
|
|
32493a2014 | ||
|
|
b34220cac7 | ||
|
|
44b37e27d9 | ||
|
|
6d3e571151 | ||
|
|
0e997f71e5 | ||
|
|
4a41d517ee | ||
|
|
c79b69ffe6 | ||
|
|
2ad6551a44 | ||
|
|
5de9888c02 | ||
|
|
5a517d714a | ||
|
|
746c77de11 | ||
|
|
9e256b9bf1 | ||
|
|
4bfcd4373b | ||
|
|
6649f78459 | ||
|
|
6f0da8c3a3 | ||
|
|
5b1238cc27 | ||
|
|
17803f1f5d | ||
|
|
81f50bb9a2 | ||
|
|
f1aee6a93e | ||
|
|
053005d784 | ||
|
|
2f5dd4d0f7 | ||
|
|
fb7ce8723a | ||
|
|
7636c3d26c | ||
|
|
6bf6c8faf4 | ||
|
|
095eba606e | ||
|
|
1097b5077d | ||
|
|
aa365cbb0d | ||
|
|
038427874a | ||
|
|
bdfdb2e6a5 | ||
|
|
95b3fc03d3 | ||
|
|
6aeadd71de | ||
|
|
affca05058 | ||
|
|
f54d238795 | ||
|
|
e82e2ae021 | ||
|
|
c3246ee7f8 | ||
|
|
40d80d2282 | ||
|
|
e0e720338f | ||
|
|
4f8b1b1e96 | ||
|
|
362afcc133 | ||
|
|
c165b3cc44 | ||
|
|
261a2a081e | ||
|
|
d27857f490 | ||
|
|
b3ba263bd8 | ||
|
|
cb774d0ad0 | ||
|
|
b48f71ecef | ||
|
|
07b3235647 | ||
|
|
d0c2ee1e71 | ||
|
|
bc180189cb | ||
|
|
39c862627d | ||
|
|
b7158a5be6 | ||
|
|
ee87c4f623 | ||
|
|
4141af0e82 | ||
|
|
7d36d22671 | ||
|
|
eab2d65113 | ||
|
|
51d0a7692e | ||
|
|
cdd90ac384 | ||
|
|
dcf6400304 | ||
|
|
247b2412d6 | ||
|
|
1ad9b85e02 | ||
|
|
af134cac8b | ||
|
|
0b8c56bcac | ||
|
|
20c64222c1 | ||
|
|
683159da02 | ||
|
|
c0681fdc7f | ||
|
|
ec1910e016 | ||
|
|
fc70f6ec66 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -4,6 +4,7 @@
|
||||
dist
|
||||
tmp
|
||||
/out-tsc
|
||||
**/*.tgz
|
||||
|
||||
# dependencies
|
||||
node_modules
|
||||
|
||||
160
examples/ckan-ssg/package-lock.json
generated
160
examples/ckan-ssg/package-lock.json
generated
@@ -10,6 +10,7 @@
|
||||
"dependencies": {
|
||||
"@heroicons/react": "^2.0.17",
|
||||
"@portaljs/ckan": "^0.0.2",
|
||||
"@portaljs/remark-wiki-link": "^1.0.4",
|
||||
"next": "13.3.1",
|
||||
"next-seo": "^6.0.0",
|
||||
"octokit": "^2.0.14",
|
||||
@@ -35,7 +36,6 @@
|
||||
"version": "7.21.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.21.0.tgz",
|
||||
"integrity": "sha512-xwII0//EObnq89Ji5AKYQaRYiW/nZ3llSv29d49IuxPhKbtJoLP+9QUUZ4nVragQVtaVGeZrpB+ZtG/Pdy/POw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"regenerator-runtime": "^0.13.11"
|
||||
},
|
||||
@@ -732,6 +732,16 @@
|
||||
"react-dom": "^18.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@portaljs/remark-wiki-link": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@portaljs/remark-wiki-link/-/remark-wiki-link-1.0.4.tgz",
|
||||
"integrity": "sha512-cp6vlssDAPawcBI0vUPRpcPdORql3RbK1Q+t1LuRvMQ+yiRb+9DnSPBUviDXKqwsBMOOXP/ePAdYohaeXGt0lQ==",
|
||||
"dependencies": {
|
||||
"mdast-util-to-markdown": "^1.5.0",
|
||||
"mdast-util-wiki-link": "^0.0.2",
|
||||
"micromark-util-symbol": "^1.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@rushstack/eslint-patch": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.2.0.tgz",
|
||||
@@ -1420,6 +1430,24 @@
|
||||
"url": "https://github.com/sponsors/wooorm"
|
||||
}
|
||||
},
|
||||
"node_modules/character-entities-legacy": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-1.1.4.tgz",
|
||||
"integrity": "sha512-3Xnr+7ZFS1uxeiUDvV02wQ+QDbc55o97tIV5zHScSPJpcLm/r0DFPcoY3tYRp+VZukxuMeKgXYmsXQHO05zQeA==",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/wooorm"
|
||||
}
|
||||
},
|
||||
"node_modules/character-reference-invalid": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-1.1.4.tgz",
|
||||
"integrity": "sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg==",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/wooorm"
|
||||
}
|
||||
},
|
||||
"node_modules/chokidar": {
|
||||
"version": "3.5.3",
|
||||
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz",
|
||||
@@ -2888,6 +2916,28 @@
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/is-alphabetical": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-1.0.4.tgz",
|
||||
"integrity": "sha512-DwzsA04LQ10FHTZuL0/grVDk4rFoVH1pjAToYwBrHSxcrBIGQuXrQMtD5U1b0U2XVgKZCTLLP8u2Qxqhy3l2Vg==",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/wooorm"
|
||||
}
|
||||
},
|
||||
"node_modules/is-alphanumerical": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-1.0.4.tgz",
|
||||
"integrity": "sha512-UzoZUr+XfVz3t3v4KyGEniVL9BDRoQtY7tOyrRybkVNjDFWyo1yhXNGrrBTQxp3ib9BLAWs7k2YKBQsFRkZG9A==",
|
||||
"dependencies": {
|
||||
"is-alphabetical": "^1.0.0",
|
||||
"is-decimal": "^1.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/wooorm"
|
||||
}
|
||||
},
|
||||
"node_modules/is-arguments": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz",
|
||||
@@ -3019,6 +3069,15 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/is-decimal": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-1.0.4.tgz",
|
||||
"integrity": "sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw==",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/wooorm"
|
||||
}
|
||||
},
|
||||
"node_modules/is-docker": {
|
||||
"version": "2.2.1",
|
||||
"resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz",
|
||||
@@ -3055,6 +3114,15 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/is-hexadecimal": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-1.0.4.tgz",
|
||||
"integrity": "sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw==",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/wooorm"
|
||||
}
|
||||
},
|
||||
"node_modules/is-map": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.2.tgz",
|
||||
@@ -3727,6 +3795,59 @@
|
||||
"url": "https://opencollective.com/unified"
|
||||
}
|
||||
},
|
||||
"node_modules/mdast-util-wiki-link": {
|
||||
"version": "0.0.2",
|
||||
"resolved": "https://registry.npmjs.org/mdast-util-wiki-link/-/mdast-util-wiki-link-0.0.2.tgz",
|
||||
"integrity": "sha512-lSsR10/dPuYIxzjGZIGA4oYzsnEnqcsD6DTXL0pqdbBzNB9teKVZB2aIzZcUsdg31v/NoHOstkVwzbN6VrQLtw==",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.12.1",
|
||||
"mdast-util-to-markdown": "^0.6.5"
|
||||
}
|
||||
},
|
||||
"node_modules/mdast-util-wiki-link/node_modules/longest-streak": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-2.0.4.tgz",
|
||||
"integrity": "sha512-vM6rUVCVUJJt33bnmHiZEvr7wPT78ztX7rojL+LW51bHtLh6HTjx84LA5W4+oa6aKEJA7jJu5LR6vQRBpA5DVg==",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/wooorm"
|
||||
}
|
||||
},
|
||||
"node_modules/mdast-util-wiki-link/node_modules/mdast-util-to-markdown": {
|
||||
"version": "0.6.5",
|
||||
"resolved": "https://registry.npmjs.org/mdast-util-to-markdown/-/mdast-util-to-markdown-0.6.5.tgz",
|
||||
"integrity": "sha512-XeV9sDE7ZlOQvs45C9UKMtfTcctcaj/pGwH8YLbMHoMOXNNCn2LsqVQOqrF1+/NU8lKDAqozme9SCXWyo9oAcQ==",
|
||||
"dependencies": {
|
||||
"@types/unist": "^2.0.0",
|
||||
"longest-streak": "^2.0.0",
|
||||
"mdast-util-to-string": "^2.0.0",
|
||||
"parse-entities": "^2.0.0",
|
||||
"repeat-string": "^1.0.0",
|
||||
"zwitch": "^1.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/unified"
|
||||
}
|
||||
},
|
||||
"node_modules/mdast-util-wiki-link/node_modules/mdast-util-to-string": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-2.0.0.tgz",
|
||||
"integrity": "sha512-AW4DRS3QbBayY/jJmD8437V1Gombjf8RSOUCMFBuo5iHi58AGEgVCKQ+ezHkZZDpAQS75hcBMpLqjpJTjtUL7w==",
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/unified"
|
||||
}
|
||||
},
|
||||
"node_modules/mdast-util-wiki-link/node_modules/zwitch": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/zwitch/-/zwitch-1.0.5.tgz",
|
||||
"integrity": "sha512-V50KMwwzqJV0NpZIZFwfOD5/lyny3WlSzRiXgA0G7VUnRlqttta1L6UQIHzd6EuBY/cHGfwTIck7w1yH6Q5zUw==",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/wooorm"
|
||||
}
|
||||
},
|
||||
"node_modules/merge2": {
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
|
||||
@@ -4714,6 +4835,32 @@
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/parse-entities": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-2.0.0.tgz",
|
||||
"integrity": "sha512-kkywGpCcRYhqQIchaWqZ875wzpS/bMKhz5HnN3p7wveJTkTtyAB/AlnS0f8DFSqYW1T82t6yEAkEcB+A1I3MbQ==",
|
||||
"dependencies": {
|
||||
"character-entities": "^1.0.0",
|
||||
"character-entities-legacy": "^1.0.0",
|
||||
"character-reference-invalid": "^1.0.0",
|
||||
"is-alphanumerical": "^1.0.0",
|
||||
"is-decimal": "^1.0.0",
|
||||
"is-hexadecimal": "^1.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/wooorm"
|
||||
}
|
||||
},
|
||||
"node_modules/parse-entities/node_modules/character-entities": {
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/character-entities/-/character-entities-1.2.4.tgz",
|
||||
"integrity": "sha512-iBMyeEHxfVnIakwOuDXpVkc54HijNgCyQB2w0VfGQThle6NXn50zU6V/u+LDhxHcDUPojn6Kpga3PTAD8W1bQw==",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/wooorm"
|
||||
}
|
||||
},
|
||||
"node_modules/path-exists": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
|
||||
@@ -5083,8 +5230,7 @@
|
||||
"node_modules/regenerator-runtime": {
|
||||
"version": "0.13.11",
|
||||
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz",
|
||||
"integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==",
|
||||
"dev": true
|
||||
"integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg=="
|
||||
},
|
||||
"node_modules/regexp.prototype.flags": {
|
||||
"version": "1.5.0",
|
||||
@@ -5147,6 +5293,14 @@
|
||||
"url": "https://opencollective.com/unified"
|
||||
}
|
||||
},
|
||||
"node_modules/repeat-string": {
|
||||
"version": "1.6.1",
|
||||
"resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz",
|
||||
"integrity": "sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==",
|
||||
"engines": {
|
||||
"node": ">=0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/resolve": {
|
||||
"version": "1.22.2",
|
||||
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.2.tgz",
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
"dependencies": {
|
||||
"@heroicons/react": "^2.0.17",
|
||||
"@portaljs/ckan": "^0.0.2",
|
||||
"@portaljs/remark-wiki-link": "^1.0.4",
|
||||
"next": "13.3.1",
|
||||
"next-seo": "^6.0.0",
|
||||
"octokit": "^2.0.14",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { MDXRemote } from 'next-mdx-remote';
|
||||
import dynamic from 'next/dynamic';
|
||||
import { Mermaid } from '@flowershow/core';
|
||||
import { Mermaid } from '@portaljs/core';
|
||||
|
||||
// Custom components/renderers to pass to MDX.
|
||||
// Since the MDX files aren't loaded by webpack, they have no knowledge of how
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import matter from "gray-matter";
|
||||
import mdxmermaid from "mdx-mermaid";
|
||||
import { h } from "hastscript";
|
||||
import remarkCallouts from "@flowershow/remark-callouts";
|
||||
import remarkEmbed from "@flowershow/remark-embed";
|
||||
import remarkCallouts from "@portaljs/remark-callouts";
|
||||
import remarkEmbed from "@portaljs/remark-embed";
|
||||
import remarkGfm from "remark-gfm";
|
||||
import remarkMath from "remark-math";
|
||||
import remarkSmartypants from "remark-smartypants";
|
||||
import remarkToc from "remark-toc";
|
||||
import remarkWikiLink from "@flowershow/remark-wiki-link";
|
||||
import remarkWikiLink from "@portaljs/remark-wiki-link";
|
||||
import rehypeAutolinkHeadings from "rehype-autolink-headings";
|
||||
import rehypeKatex from "rehype-katex";
|
||||
import rehypeSlug from "rehype-slug";
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { MarkdownDB } from "@flowershow/markdowndb";
|
||||
import { MarkdownDB } from "mddb";
|
||||
|
||||
const dbPath = "markdown.db";
|
||||
|
||||
|
||||
1099
examples/ckan/package-lock.json
generated
1099
examples/ckan/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -11,14 +11,14 @@
|
||||
"mddb": "mddb ./content"
|
||||
},
|
||||
"dependencies": {
|
||||
"@flowershow/core": "^0.4.13",
|
||||
"@flowershow/markdowndb": "^0.1.5",
|
||||
"@flowershow/remark-callouts": "^1.0.0",
|
||||
"@flowershow/remark-embed": "^1.0.0",
|
||||
"@githubocto/flat-ui": "^0.14.1",
|
||||
"@heroicons/react": "^2.0.18",
|
||||
"@portaljs/ckan": "^0.0.2",
|
||||
"@portaljs/components": "0.1.6",
|
||||
"@portaljs/core": "^1.0.5",
|
||||
"@portaljs/remark-callouts": "^1.0.5",
|
||||
"@portaljs/remark-embed": "^1.0.4",
|
||||
"@portaljs/remark-wiki-link": "^1.0.4",
|
||||
"@tailwindcss/typography": "^0.5.9",
|
||||
"@types/node": "20.2.3",
|
||||
"@types/react": "18.2.6",
|
||||
@@ -27,6 +27,7 @@
|
||||
"eslint": "8.41.0",
|
||||
"eslint-config-next": "13.4.3",
|
||||
"isomorphic-unfetch": "^4.0.2",
|
||||
"mddb": "^0.1.9",
|
||||
"next": "13.4.3",
|
||||
"next-mdx-remote": "^4.4.1",
|
||||
"papaparse": "^5.4.1",
|
||||
|
||||
@@ -40,16 +40,25 @@ export default function Home({
|
||||
});
|
||||
|
||||
return (
|
||||
<main className="flex min-h-screen flex-col items-center p-24 bg-zinc-900">
|
||||
<DatasetSearchForm
|
||||
options={options}
|
||||
setOptions={setOptions}
|
||||
groups={groups}
|
||||
orgs={orgs}
|
||||
/>
|
||||
<div className="bg-white p-8 my-4 rounded-lg">
|
||||
<ListOfDatasets options={options} setOptions={setOptions} ckan={ckan} />{' '}
|
||||
</div>
|
||||
</main>
|
||||
<div>
|
||||
<main className="py-12 bg-zinc-900">
|
||||
<DatasetSearchForm
|
||||
options={options}
|
||||
setOptions={setOptions}
|
||||
groups={groups}
|
||||
orgs={orgs}
|
||||
/>
|
||||
<div
|
||||
className="bg-white p-8 mx-auto my-4 rounded-lg"
|
||||
style={{ width: 'min(1100px, 95vw)' }}
|
||||
>
|
||||
<ListOfDatasets
|
||||
options={options}
|
||||
setOptions={setOptions}
|
||||
ckan={ckan}
|
||||
/>{' '}
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
@import "@flowershow/remark-callouts/styles.css";
|
||||
@import "@portaljs/remark-callouts/styles.css";
|
||||
|
||||
/* mathjax */
|
||||
.math-inline > mjx-container > svg {
|
||||
|
||||
2071
examples/fivethirtyeight/package-lock.json
generated
2071
examples/fivethirtyeight/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -9,10 +9,11 @@
|
||||
"lint": "next lint"
|
||||
},
|
||||
"dependencies": {
|
||||
"@flowershow/core": "^0.4.13",
|
||||
"@headlessui/react": "^1.7.14",
|
||||
"@heroicons/react": "^2.0.18",
|
||||
"@portaljs/components": "^0.1.7",
|
||||
"@portaljs/components": "^0.1.8",
|
||||
"@portaljs/core": "^1.0.5",
|
||||
"@portaljs/remark-wiki-link": "^1.0.4",
|
||||
"@tailwindcss/typography": "^0.5.9",
|
||||
"@types/node": "20.1.1",
|
||||
"@types/react": "18.2.6",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import '@/styles/globals.css';
|
||||
import '@portaljs/components/styles.css';
|
||||
import { useEffect } from 'react';
|
||||
import { pageview } from '@flowershow/core';
|
||||
import { pageview } from '@portaljs/core';
|
||||
import Script from 'next/script';
|
||||
import Head from 'next/head';
|
||||
import { useRouter } from 'next/router';
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { MDXRemote } from 'next-mdx-remote';
|
||||
import dynamic from 'next/dynamic';
|
||||
import { Mermaid } from '@flowershow/core';
|
||||
import { Mermaid } from '@portaljs/core';
|
||||
|
||||
// Custom components/renderers to pass to MDX.
|
||||
// Since the MDX files aren't loaded by webpack, they have no knowledge of how
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import matter from "gray-matter";
|
||||
import mdxmermaid from "mdx-mermaid";
|
||||
import { h } from "hastscript";
|
||||
import remarkCallouts from "@flowershow/remark-callouts";
|
||||
import remarkEmbed from "@flowershow/remark-embed";
|
||||
import remarkCallouts from "@portaljs/remark-callouts";
|
||||
import remarkEmbed from "@portaljs/remark-embed";
|
||||
import remarkGfm from "remark-gfm";
|
||||
import remarkMath from "remark-math";
|
||||
import remarkSmartypants from "remark-smartypants";
|
||||
import remarkToc from "remark-toc";
|
||||
import remarkWikiLink from "@flowershow/remark-wiki-link";
|
||||
import remarkWikiLink from "@portaljs/remark-wiki-link";
|
||||
import rehypeAutolinkHeadings from "rehype-autolink-headings";
|
||||
import rehypeKatex from "rehype-katex";
|
||||
import rehypeSlug from "rehype-slug";
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { MarkdownDB } from "@flowershow/markdowndb";
|
||||
import { MarkdownDB } from "mddb";
|
||||
|
||||
const dbPath = "markdown.db";
|
||||
|
||||
|
||||
586
examples/github-backed-catalog/package-lock.json
generated
586
examples/github-backed-catalog/package-lock.json
generated
@@ -8,16 +8,17 @@
|
||||
"name": "my-app",
|
||||
"version": "0.1.0",
|
||||
"dependencies": {
|
||||
"@flowershow/core": "^0.4.13",
|
||||
"@flowershow/markdowndb": "^0.1.5",
|
||||
"@flowershow/remark-callouts": "^1.0.0",
|
||||
"@flowershow/remark-embed": "^1.0.0",
|
||||
"@portaljs/components": "^0.1.6",
|
||||
"@portaljs/core": "^1.0.5",
|
||||
"@portaljs/remark-callouts": "^1.0.5",
|
||||
"@portaljs/remark-embed": "^1.0.4",
|
||||
"@portaljs/remark-wiki-link": "^1.0.4",
|
||||
"@types/node": "18.16.0",
|
||||
"@types/react": "18.0.38",
|
||||
"@types/react-dom": "18.0.11",
|
||||
"eslint": "8.39.0",
|
||||
"eslint-config-next": "13.3.1",
|
||||
"mddb": "^0.1.9",
|
||||
"next": "13.4.3",
|
||||
"next-mdx-remote": "^4.4.1",
|
||||
"next-seo": "^6.0.0",
|
||||
@@ -45,19 +46,31 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@algolia/autocomplete-core": {
|
||||
"version": "1.8.2",
|
||||
"resolved": "https://registry.npmjs.org/@algolia/autocomplete-core/-/autocomplete-core-1.8.2.tgz",
|
||||
"integrity": "sha512-mTeshsyFhAqw/ebqNsQpMtbnjr+qVOSKXArEj4K0d7sqc8It1XD0gkASwecm9mF/jlOQ4Z9RNg1HbdA8JPdRwQ==",
|
||||
"version": "1.9.2",
|
||||
"resolved": "https://registry.npmjs.org/@algolia/autocomplete-core/-/autocomplete-core-1.9.2.tgz",
|
||||
"integrity": "sha512-hkG80c9kx9ClVAEcUJbTd2ziVC713x9Bji9Ty4XJfKXlxlsx3iXsoNhAwfeR4ulzIUg7OE5gez0UU1zVDdG7kg==",
|
||||
"dependencies": {
|
||||
"@algolia/autocomplete-shared": "1.8.2"
|
||||
"@algolia/autocomplete-plugin-algolia-insights": "1.9.2",
|
||||
"@algolia/autocomplete-shared": "1.9.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@algolia/autocomplete-plugin-algolia-insights": {
|
||||
"version": "1.9.2",
|
||||
"resolved": "https://registry.npmjs.org/@algolia/autocomplete-plugin-algolia-insights/-/autocomplete-plugin-algolia-insights-1.9.2.tgz",
|
||||
"integrity": "sha512-2LVsf4W66hVHQ3Ua/8k15oPlxjELCztbAkQm/hP42Sw+GLkHAdY1vaVRYziaWq64+Oljfg6FKkZHCdgXH+CGIA==",
|
||||
"dependencies": {
|
||||
"@algolia/autocomplete-shared": "1.9.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"search-insights": ">= 1 < 3"
|
||||
}
|
||||
},
|
||||
"node_modules/@algolia/autocomplete-preset-algolia": {
|
||||
"version": "1.8.2",
|
||||
"resolved": "https://registry.npmjs.org/@algolia/autocomplete-preset-algolia/-/autocomplete-preset-algolia-1.8.2.tgz",
|
||||
"integrity": "sha512-J0oTx4me6ZM9kIKPuL3lyU3aB8DEvpVvR6xWmHVROx5rOYJGQcZsdG4ozxwcOyiiu3qxMkIbzntnV1S1VWD8yA==",
|
||||
"version": "1.9.2",
|
||||
"resolved": "https://registry.npmjs.org/@algolia/autocomplete-preset-algolia/-/autocomplete-preset-algolia-1.9.2.tgz",
|
||||
"integrity": "sha512-pqgIm2GNqtCT59Y1ICctIPrYTi34+wNPiNWEclD/yDzp5uDUUsyGe5XrUjCNyQRTKonAlmYxoaEHOn8FWgmBHA==",
|
||||
"dependencies": {
|
||||
"@algolia/autocomplete-shared": "1.8.2"
|
||||
"@algolia/autocomplete-shared": "1.9.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@algolia/client-search": ">= 4.9.1 < 6",
|
||||
@@ -65,123 +78,127 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@algolia/autocomplete-shared": {
|
||||
"version": "1.8.2",
|
||||
"resolved": "https://registry.npmjs.org/@algolia/autocomplete-shared/-/autocomplete-shared-1.8.2.tgz",
|
||||
"integrity": "sha512-b6Z/X4MczChMcfhk6kfRmBzPgjoPzuS9KGR4AFsiLulLNRAAqhP+xZTKtMnZGhLuc61I20d5WqlId02AZvcO6g=="
|
||||
"version": "1.9.2",
|
||||
"resolved": "https://registry.npmjs.org/@algolia/autocomplete-shared/-/autocomplete-shared-1.9.2.tgz",
|
||||
"integrity": "sha512-XxX6YDn+7LG+SmdpXEOnj7fc3TjiVpQ0CbGhjLwrd2tYr6LVY2D4Iiu/iuYJ4shvVDWWnpwArSk0uIWC/8OPUA==",
|
||||
"peerDependencies": {
|
||||
"@algolia/client-search": ">= 4.9.1 < 6",
|
||||
"algoliasearch": ">= 4.9.1 < 6"
|
||||
}
|
||||
},
|
||||
"node_modules/@algolia/cache-browser-local-storage": {
|
||||
"version": "4.17.1",
|
||||
"resolved": "https://registry.npmjs.org/@algolia/cache-browser-local-storage/-/cache-browser-local-storage-4.17.1.tgz",
|
||||
"integrity": "sha512-e91Jpu93X3t3mVdQwF3ZDjSFMFIfzSc+I76G4EX8nl9RYXgqcjframoL05VTjcD2YCsI18RIHAWVCBoCXVZnrw==",
|
||||
"version": "4.17.2",
|
||||
"resolved": "https://registry.npmjs.org/@algolia/cache-browser-local-storage/-/cache-browser-local-storage-4.17.2.tgz",
|
||||
"integrity": "sha512-ZkVN7K/JE+qMQbpR6h3gQOGR6yCJpmucSBCmH5YDxnrYbp2CbrVCu0Nr+FGVoWzMJNznj1waShkfQ9awERulLw==",
|
||||
"dependencies": {
|
||||
"@algolia/cache-common": "4.17.1"
|
||||
"@algolia/cache-common": "4.17.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@algolia/cache-common": {
|
||||
"version": "4.17.1",
|
||||
"resolved": "https://registry.npmjs.org/@algolia/cache-common/-/cache-common-4.17.1.tgz",
|
||||
"integrity": "sha512-fvi1WT8aSiGAKrcTw8Qg3RYgcwW8GZMHcqEm4AyDBEy72JZlFBSY80cTQ75MslINjCHXLDT+9EN8AGI9WVY7uA=="
|
||||
"version": "4.17.2",
|
||||
"resolved": "https://registry.npmjs.org/@algolia/cache-common/-/cache-common-4.17.2.tgz",
|
||||
"integrity": "sha512-fojbhYIS8ovfYs6hwZpy1O4mBfVRxNgAaZRqsdVQd54hU4MxYDYFCxagYX28lOBz7btcDHld6BMoWXvjzkx6iQ=="
|
||||
},
|
||||
"node_modules/@algolia/cache-in-memory": {
|
||||
"version": "4.17.1",
|
||||
"resolved": "https://registry.npmjs.org/@algolia/cache-in-memory/-/cache-in-memory-4.17.1.tgz",
|
||||
"integrity": "sha512-NbBt6eBWlsXc5geSpfPRC5dkIB/0Ptthw8r0yM5Z7D3sPlYdnTZSO9y9XWXIptRMwmZe4cM8iBMN8y0tzbcBkA==",
|
||||
"version": "4.17.2",
|
||||
"resolved": "https://registry.npmjs.org/@algolia/cache-in-memory/-/cache-in-memory-4.17.2.tgz",
|
||||
"integrity": "sha512-UYQcMzPurNi+cPYkuPemTZkjKAjdgAS1hagC5irujKbrYnN4yscK4TkOI5tX+O8/KegtJt3kOK07OIrJ2QDAAw==",
|
||||
"dependencies": {
|
||||
"@algolia/cache-common": "4.17.1"
|
||||
"@algolia/cache-common": "4.17.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@algolia/client-account": {
|
||||
"version": "4.17.1",
|
||||
"resolved": "https://registry.npmjs.org/@algolia/client-account/-/client-account-4.17.1.tgz",
|
||||
"integrity": "sha512-3rL/6ofJvyL+q8TiWM3qoM9tig+SY4gB1Vbsj+UeJPnJm8Khm+7OS+r+mFraqR6pTehYqN8yGYoE7x4diEn4aA==",
|
||||
"version": "4.17.2",
|
||||
"resolved": "https://registry.npmjs.org/@algolia/client-account/-/client-account-4.17.2.tgz",
|
||||
"integrity": "sha512-doSk89pBPDpDyKJSHFADIGa2XSGrBCj3QwPvqtRJXDADpN+OjW+eTR8r4hEs/7X4GGfjfAOAES8JgDx+fZntYw==",
|
||||
"dependencies": {
|
||||
"@algolia/client-common": "4.17.1",
|
||||
"@algolia/client-search": "4.17.1",
|
||||
"@algolia/transporter": "4.17.1"
|
||||
"@algolia/client-common": "4.17.2",
|
||||
"@algolia/client-search": "4.17.2",
|
||||
"@algolia/transporter": "4.17.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@algolia/client-analytics": {
|
||||
"version": "4.17.1",
|
||||
"resolved": "https://registry.npmjs.org/@algolia/client-analytics/-/client-analytics-4.17.1.tgz",
|
||||
"integrity": "sha512-Bepr2w249vODqeBtM7i++tPmUsQ9B81aupUGbDWmjA/FX+jzQqOdhW8w1CFO5kWViNKTbz2WBIJ9U3x8hOa4bA==",
|
||||
"version": "4.17.2",
|
||||
"resolved": "https://registry.npmjs.org/@algolia/client-analytics/-/client-analytics-4.17.2.tgz",
|
||||
"integrity": "sha512-V+DcXbOtD/hKwAR3qGQrtlrJ3q2f9OKfx843q744o4m3xHv5ueCAvGXB1znPsdaUrVDNAImcgEgqwI9x7EJbDw==",
|
||||
"dependencies": {
|
||||
"@algolia/client-common": "4.17.1",
|
||||
"@algolia/client-search": "4.17.1",
|
||||
"@algolia/requester-common": "4.17.1",
|
||||
"@algolia/transporter": "4.17.1"
|
||||
"@algolia/client-common": "4.17.2",
|
||||
"@algolia/client-search": "4.17.2",
|
||||
"@algolia/requester-common": "4.17.2",
|
||||
"@algolia/transporter": "4.17.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@algolia/client-common": {
|
||||
"version": "4.17.1",
|
||||
"resolved": "https://registry.npmjs.org/@algolia/client-common/-/client-common-4.17.1.tgz",
|
||||
"integrity": "sha512-+r7kg4EgbFnGsDnoGSVNtXZO8xvZ0vzf1WAOV7sqV9PMf1bp6cpJP/3IuPrSk4t5w2KVl+pC8jfTM7HcFlfBEQ==",
|
||||
"version": "4.17.2",
|
||||
"resolved": "https://registry.npmjs.org/@algolia/client-common/-/client-common-4.17.2.tgz",
|
||||
"integrity": "sha512-gKBUnjxi0ukJYIJxVREYGt1Dmj1B3RBYbfGWi0dIPp1BC1VvQm+BOuNwsIwmq/x3MPO+sGuK978eKiP3tZDvag==",
|
||||
"dependencies": {
|
||||
"@algolia/requester-common": "4.17.1",
|
||||
"@algolia/transporter": "4.17.1"
|
||||
"@algolia/requester-common": "4.17.2",
|
||||
"@algolia/transporter": "4.17.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@algolia/client-personalization": {
|
||||
"version": "4.17.1",
|
||||
"resolved": "https://registry.npmjs.org/@algolia/client-personalization/-/client-personalization-4.17.1.tgz",
|
||||
"integrity": "sha512-gJku9DG/THJpfsSlG/az0a3QIn+VVff9kKh8PG8+7ZfxOHS+C+Y5YSeZVsC+c2cfoKLPo3CuHIiJ/p86erR3bA==",
|
||||
"version": "4.17.2",
|
||||
"resolved": "https://registry.npmjs.org/@algolia/client-personalization/-/client-personalization-4.17.2.tgz",
|
||||
"integrity": "sha512-wc4UgOWxSYWz5wpuelNmlt895jA9twjZWM2ms17Ws8qCvBHF7OVGdMGgbysPB8790YnfvvDnSsWOv3CEj26Eow==",
|
||||
"dependencies": {
|
||||
"@algolia/client-common": "4.17.1",
|
||||
"@algolia/requester-common": "4.17.1",
|
||||
"@algolia/transporter": "4.17.1"
|
||||
"@algolia/client-common": "4.17.2",
|
||||
"@algolia/requester-common": "4.17.2",
|
||||
"@algolia/transporter": "4.17.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@algolia/client-search": {
|
||||
"version": "4.17.1",
|
||||
"resolved": "https://registry.npmjs.org/@algolia/client-search/-/client-search-4.17.1.tgz",
|
||||
"integrity": "sha512-Q5YfT5gVkx60PZDQBqp/zH9aUbBdC7HVvxupiHUgnCKqRQsRZjOhLest7AI6FahepuZLBZS62COrO7v+JvKY7w==",
|
||||
"version": "4.17.2",
|
||||
"resolved": "https://registry.npmjs.org/@algolia/client-search/-/client-search-4.17.2.tgz",
|
||||
"integrity": "sha512-FUjIs+gRe0upJC++uVs4sdxMw15JxfkT86Gr/kqVwi9kcqaZhXntSbW/Fw959bIYXczjmeVQsilYvBWW4YvSZA==",
|
||||
"dependencies": {
|
||||
"@algolia/client-common": "4.17.1",
|
||||
"@algolia/requester-common": "4.17.1",
|
||||
"@algolia/transporter": "4.17.1"
|
||||
"@algolia/client-common": "4.17.2",
|
||||
"@algolia/requester-common": "4.17.2",
|
||||
"@algolia/transporter": "4.17.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@algolia/logger-common": {
|
||||
"version": "4.17.1",
|
||||
"resolved": "https://registry.npmjs.org/@algolia/logger-common/-/logger-common-4.17.1.tgz",
|
||||
"integrity": "sha512-Us28Ot+fLEmX9M96sa65VZ8EyEEzhYPxfhV9aQyKDjfXbUdJlJxKt6wZpoEg9RAPSdO8IjK9nmuW2P8au3rRsg=="
|
||||
"version": "4.17.2",
|
||||
"resolved": "https://registry.npmjs.org/@algolia/logger-common/-/logger-common-4.17.2.tgz",
|
||||
"integrity": "sha512-EfXuweUE+1HiSMsQidaDWA5Lv4NnStYIlh7PO5pLkI+sdhbMX0e5AO5nUAMIFM1VkEANes70RA8fzhP6OqCqQQ=="
|
||||
},
|
||||
"node_modules/@algolia/logger-console": {
|
||||
"version": "4.17.1",
|
||||
"resolved": "https://registry.npmjs.org/@algolia/logger-console/-/logger-console-4.17.1.tgz",
|
||||
"integrity": "sha512-iKGQTpOjHiE64W3JIOu6dmDvn+AfYIElI9jf/Nt6umRPmP/JI9rK+OHUoW4pKrBtdG0DPd62ppeNXzSnLxY6/g==",
|
||||
"version": "4.17.2",
|
||||
"resolved": "https://registry.npmjs.org/@algolia/logger-console/-/logger-console-4.17.2.tgz",
|
||||
"integrity": "sha512-JuG8HGVlJ+l/UEDK4h2Y8q/IEmRjQz1J0aS9tf6GPNbGYiSvMr1DDdZ+hqV3bb1XE6wU8Ypex56HisWMSpnG0A==",
|
||||
"dependencies": {
|
||||
"@algolia/logger-common": "4.17.1"
|
||||
"@algolia/logger-common": "4.17.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@algolia/requester-browser-xhr": {
|
||||
"version": "4.17.1",
|
||||
"resolved": "https://registry.npmjs.org/@algolia/requester-browser-xhr/-/requester-browser-xhr-4.17.1.tgz",
|
||||
"integrity": "sha512-W5mGfGDsyfVR+r4pUFrYLGBEM18gs38+GNt5PE5uPULy4uVTSnnVSkJkWeRkmLBk9zEZ/Nld8m4zavK6dtEuYg==",
|
||||
"version": "4.17.2",
|
||||
"resolved": "https://registry.npmjs.org/@algolia/requester-browser-xhr/-/requester-browser-xhr-4.17.2.tgz",
|
||||
"integrity": "sha512-FKI2lYWwksALfRt2OETFmGb5+P7WVc4py2Ai3H7k8FSfTLwVvs9WVVmtlx6oANQ8RFEK4B85h8DQJTJ29TDfmA==",
|
||||
"dependencies": {
|
||||
"@algolia/requester-common": "4.17.1"
|
||||
"@algolia/requester-common": "4.17.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@algolia/requester-common": {
|
||||
"version": "4.17.1",
|
||||
"resolved": "https://registry.npmjs.org/@algolia/requester-common/-/requester-common-4.17.1.tgz",
|
||||
"integrity": "sha512-HggXdjvVFQR0I5l7hM5WdHgQ1tqcRWeyXZz8apQ7zPWZhirmY2E9D6LVhDh/UnWQNEm7nBtM+eMFONJ3bZccIQ=="
|
||||
"version": "4.17.2",
|
||||
"resolved": "https://registry.npmjs.org/@algolia/requester-common/-/requester-common-4.17.2.tgz",
|
||||
"integrity": "sha512-Rfim23ztAhYpE9qm+KCfCRo+YLJCjiiTG+IpDdzUjMpYPhUtirQT0A35YEd/gKn86YNyydxS9w8iRSjwKh+L0A=="
|
||||
},
|
||||
"node_modules/@algolia/requester-node-http": {
|
||||
"version": "4.17.1",
|
||||
"resolved": "https://registry.npmjs.org/@algolia/requester-node-http/-/requester-node-http-4.17.1.tgz",
|
||||
"integrity": "sha512-NzFWecXT6d0PPsQY9L+/qoK2deF74OLcpvqCH+Vh3mh+QzPsFafcBExdguAjZsAWDn1R6JEeFW7/fo/p0SE57w==",
|
||||
"version": "4.17.2",
|
||||
"resolved": "https://registry.npmjs.org/@algolia/requester-node-http/-/requester-node-http-4.17.2.tgz",
|
||||
"integrity": "sha512-E0b0kyCDMvUIhQmDNd/mH4fsKJdEEX6PkMKrYJjzm6moo+rP22tqpq4Rfe7DZD8OB6/LsDD3zs3Kvd+L+M5wwQ==",
|
||||
"dependencies": {
|
||||
"@algolia/requester-common": "4.17.1"
|
||||
"@algolia/requester-common": "4.17.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@algolia/transporter": {
|
||||
"version": "4.17.1",
|
||||
"resolved": "https://registry.npmjs.org/@algolia/transporter/-/transporter-4.17.1.tgz",
|
||||
"integrity": "sha512-ZM+qhX47Vh46mWH8/U9ihvy98HdTYpYQDSlqBD7IbiUbbyoCMke+qmdSX2MGhR2FCcXBSxejsJKKVAfbpaLVgg==",
|
||||
"version": "4.17.2",
|
||||
"resolved": "https://registry.npmjs.org/@algolia/transporter/-/transporter-4.17.2.tgz",
|
||||
"integrity": "sha512-m8pXlz5OnNzjD1rcw+duCN4jG4yEzkJBsvKYMoN22Oq6rQwy1AY5muZ+IQUs4dL+A364CYkRMLRWhvXpCZ1x+g==",
|
||||
"dependencies": {
|
||||
"@algolia/cache-common": "4.17.1",
|
||||
"@algolia/logger-common": "4.17.1",
|
||||
"@algolia/requester-common": "4.17.1"
|
||||
"@algolia/cache-common": "4.17.2",
|
||||
"@algolia/logger-common": "4.17.2",
|
||||
"@algolia/requester-common": "4.17.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/code-frame": {
|
||||
@@ -353,18 +370,18 @@
|
||||
"integrity": "sha512-Tbsj02wXCbqGmzdnXNk0SOF19ChhRU70BsroIi4Pm6Ehp56in6vch94mfbdQ17DozxkL3BAVjbZ4Qc1a0HFRAg=="
|
||||
},
|
||||
"node_modules/@docsearch/css": {
|
||||
"version": "3.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@docsearch/css/-/css-3.4.0.tgz",
|
||||
"integrity": "sha512-Hg8Xfma+rFwRi6Y/pfei4FJoQ1hdVURmmNs/XPoMTCPAImU+d5yxj+M+qdLtNjWRpfWziU4dQcqY94xgFBn2dg=="
|
||||
"version": "3.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@docsearch/css/-/css-3.5.0.tgz",
|
||||
"integrity": "sha512-Ob5FQLubplcBNihAVtriR59FRBeP8u69F6mu4L4yIr60KfsPc10bOV0DoPErJw0zF9IBN2cNLW9qdmt8zWPxyg=="
|
||||
},
|
||||
"node_modules/@docsearch/react": {
|
||||
"version": "3.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@docsearch/react/-/react-3.4.0.tgz",
|
||||
"integrity": "sha512-ufrp5879XYGojgS30ZAp8H4qIMbahRHB9M85VDBP36Xgz5QjYM54i1URKj5e219F7gqTtOivfztFTij6itc0MQ==",
|
||||
"version": "3.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@docsearch/react/-/react-3.5.0.tgz",
|
||||
"integrity": "sha512-3IG8mmSMzSHNGy2S1VuPyYU9tFCxFpj5Ov8SYwsSHM4yMvFsaO9oFxXocA5lSenliIELhuOuS5+BdxHa/Qlf2A==",
|
||||
"dependencies": {
|
||||
"@algolia/autocomplete-core": "1.8.2",
|
||||
"@algolia/autocomplete-preset-algolia": "1.8.2",
|
||||
"@docsearch/css": "3.4.0",
|
||||
"@algolia/autocomplete-core": "1.9.2",
|
||||
"@algolia/autocomplete-preset-algolia": "1.9.2",
|
||||
"@docsearch/css": "3.5.0",
|
||||
"algoliasearch": "^4.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
@@ -573,16 +590,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@floating-ui/core": {
|
||||
"version": "1.2.6",
|
||||
"resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.2.6.tgz",
|
||||
"integrity": "sha512-EvYTiXet5XqweYGClEmpu3BoxmsQ4hkj3QaYA6qEnigCWffTP3vNRwBReTdrwDwo7OoJ3wM8Uoe9Uk4n+d4hfg=="
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.3.0.tgz",
|
||||
"integrity": "sha512-vX1WVAdPjZg9DkDkC+zEx/tKtnST6/qcNpwcjeBgco3XRNHz5PUA+ivi/yr6G3o0kMR60uKBJcfOdfzOFI7PMQ=="
|
||||
},
|
||||
"node_modules/@floating-ui/dom": {
|
||||
"version": "1.2.8",
|
||||
"resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.2.8.tgz",
|
||||
"integrity": "sha512-XLwhYV90MxiHDq6S0rzFZj00fnDM+A1R9jhSioZoMsa7G0Q0i+Q4x40ajR8FHSdYDE1bgjG45mIWe6jtv9UPmg==",
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.3.0.tgz",
|
||||
"integrity": "sha512-qIAwejE3r6NeA107u4ELDKkH8+VtgRKdXqtSPaKflL2S2V+doyN+Wt9s5oHKXPDo4E8TaVXaHT3+6BbagH31xw==",
|
||||
"dependencies": {
|
||||
"@floating-ui/core": "^1.2.6"
|
||||
"@floating-ui/core": "^1.3.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@floating-ui/react-dom": {
|
||||
@@ -612,103 +629,6 @@
|
||||
"react-dom": ">=16.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@flowershow/core": {
|
||||
"version": "0.4.13",
|
||||
"resolved": "https://registry.npmjs.org/@flowershow/core/-/core-0.4.13.tgz",
|
||||
"integrity": "sha512-UeDb70pjUGgILUXGQKFkytKL9ilwsTIM3jYsNNi55PKHXAaHEQA480Y+m2yNPgC5SzFlGj0FIP/zhH0JfBdNiw==",
|
||||
"dependencies": {
|
||||
"@docsearch/react": "^3.3.3",
|
||||
"@floating-ui/react-dom": "^1.3.0",
|
||||
"@floating-ui/react-dom-interactions": "^0.13.3",
|
||||
"@giscus/react": "^2.2.6",
|
||||
"@headlessui/react": "^1.7.12",
|
||||
"clsx": "^1.2.1",
|
||||
"disqus-react": "^1.1.5",
|
||||
"framer-motion": "^10.0.1",
|
||||
"kbar": "0.1.0-beta.40",
|
||||
"mdx-mermaid": "2.0.0-rc7",
|
||||
"mermaid": "10.0.1-rc.2",
|
||||
"prop-types": "^15.8.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"next": "^13.2.1",
|
||||
"next-themes": "^0.2.1",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@flowershow/core/node_modules/dagre-d3-es": {
|
||||
"version": "7.0.8",
|
||||
"resolved": "https://registry.npmjs.org/dagre-d3-es/-/dagre-d3-es-7.0.8.tgz",
|
||||
"integrity": "sha512-eykdoYQ4FwCJinEYS0gPL2f2w+BPbSLvnQSJ3Ye1vAoPjdkq6xIMKBv+UkICd3qZE26wBKIn3p+6n0QC7R1LyA==",
|
||||
"dependencies": {
|
||||
"d3": "^7.8.2",
|
||||
"lodash-es": "^4.17.21"
|
||||
}
|
||||
},
|
||||
"node_modules/@flowershow/core/node_modules/dompurify": {
|
||||
"version": "2.4.3",
|
||||
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.4.3.tgz",
|
||||
"integrity": "sha512-q6QaLcakcRjebxjg8/+NP+h0rPfatOgOzc46Fst9VAA3jF2ApfKBNKMzdP4DYTqtUMXSCd5pRS/8Po/OmoCHZQ=="
|
||||
},
|
||||
"node_modules/@flowershow/core/node_modules/mermaid": {
|
||||
"version": "10.0.1-rc.2",
|
||||
"resolved": "https://registry.npmjs.org/mermaid/-/mermaid-10.0.1-rc.2.tgz",
|
||||
"integrity": "sha512-THnhraO9F6pQZY9Nv91QuWv35QdmZy/UW2FpadRN/1cLuqCyihNKIEOxuVLEPrgtFawt2nL+BpUoJKC4div3UQ==",
|
||||
"dependencies": {
|
||||
"@braintree/sanitize-url": "^6.0.0",
|
||||
"cytoscape": "^3.23.0",
|
||||
"cytoscape-cose-bilkent": "^4.1.0",
|
||||
"cytoscape-fcose": "^2.1.0",
|
||||
"d3": "^7.4.0",
|
||||
"dagre-d3-es": "7.0.8",
|
||||
"dompurify": "2.4.3",
|
||||
"elkjs": "^0.8.2",
|
||||
"khroma": "^2.0.0",
|
||||
"lodash-es": "^4.17.21",
|
||||
"moment-mini": "^2.29.4",
|
||||
"non-layered-tidy-tree-layout": "^2.0.2",
|
||||
"stylis": "^4.1.2",
|
||||
"ts-dedent": "^2.2.0",
|
||||
"uuid": "^9.0.0",
|
||||
"web-worker": "^1.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@flowershow/markdowndb": {
|
||||
"version": "0.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@flowershow/markdowndb/-/markdowndb-0.1.5.tgz",
|
||||
"integrity": "sha512-AfmkvgitNb7z150bTcCn/1lqwUhPRD3IhCbIwYxE5IXtL57+WwewJUNOU3SOKXNWg6eDVlfEq0H2GICSLAC4bw==",
|
||||
"dependencies": {
|
||||
"@flowershow/remark-wiki-link": "^1.1.2",
|
||||
"gray-matter": "^4.0.3",
|
||||
"knex": "^2.4.2",
|
||||
"remark-gfm": "^3.0.1",
|
||||
"remark-parse": "^10.0.1",
|
||||
"sqlite3": "^5.1.6",
|
||||
"unist-util-select": "^4.0.3"
|
||||
},
|
||||
"bin": {
|
||||
"mddb": "src/bin/index.js"
|
||||
}
|
||||
},
|
||||
"node_modules/@flowershow/remark-callouts": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@flowershow/remark-callouts/-/remark-callouts-1.0.0.tgz",
|
||||
"integrity": "sha512-zzHDpw1bswTTf+cbhNh2Bogf4ghpIcxAypFxYZxZ/afeGdN3NNSQwnamL8StY9uu6aQ/miq4Egbuof3xk2ksWA==",
|
||||
"dependencies": {
|
||||
"mdast-util-from-markdown": "^1.2.0",
|
||||
"svg-parser": "^2.0.4",
|
||||
"unist-util-visit": "^4.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@flowershow/remark-embed": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@flowershow/remark-embed/-/remark-embed-1.0.0.tgz",
|
||||
"integrity": "sha512-tADovIrhbmNWJj72C+pgT921+BuApmO6xxKU3HlWciRHkD6S4KqsikLjsopalK+VJmqluoHhhST0kf0S6xUKIA==",
|
||||
"dependencies": {
|
||||
"unist-util-visit": "^4.1.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@flowershow/remark-wiki-link": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@flowershow/remark-wiki-link/-/remark-wiki-link-1.2.0.tgz",
|
||||
@@ -1701,9 +1621,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@headlessui/react": {
|
||||
"version": "1.7.14",
|
||||
"resolved": "https://registry.npmjs.org/@headlessui/react/-/react-1.7.14.tgz",
|
||||
"integrity": "sha512-znzdq9PG8rkwcu9oQ2FwIy0ZFtP9Z7ycS+BAqJ3R5EIqC/0bJGvhT7193rFf+45i9nnPsYvCQVW4V/bB9Xc+gA==",
|
||||
"version": "1.7.15",
|
||||
"resolved": "https://registry.npmjs.org/@headlessui/react/-/react-1.7.15.tgz",
|
||||
"integrity": "sha512-OTO0XtoRQ6JPB1cKNFYBZv2Q0JMqMGNhYP1CjPvcJvjz8YGokz8oAj89HIYZGN0gZzn/4kk9iUpmMF4Q21Gsqw==",
|
||||
"dependencies": {
|
||||
"client-only": "^0.0.1"
|
||||
},
|
||||
@@ -1807,9 +1727,9 @@
|
||||
"integrity": "sha512-kXOeFbfCm4fFf2A3WwVEeQj55tMZa8c8/f9AKHMobQMkzNUfUj+antR3fRPaZJawsa1aZiP/Da3ndpZrwEe4rQ=="
|
||||
},
|
||||
"node_modules/@lit/reactive-element": {
|
||||
"version": "1.6.1",
|
||||
"resolved": "https://registry.npmjs.org/@lit/reactive-element/-/reactive-element-1.6.1.tgz",
|
||||
"integrity": "sha512-va15kYZr7KZNNPZdxONGQzpUr+4sxVu7V/VG7a8mRfPPXUyhEYj5RzXCQmGrlP3tAh0L3HHm5AjBMFYRqlM9SA==",
|
||||
"version": "1.6.2",
|
||||
"resolved": "https://registry.npmjs.org/@lit/reactive-element/-/reactive-element-1.6.2.tgz",
|
||||
"integrity": "sha512-rDfl+QnCYjuIGf5xI2sVJWdYIi56CTCwWa+nidKYX6oIuBYwUbT/vX4qbUDlHiZKJ/3FRNQ/tWJui44p6/stSA==",
|
||||
"dependencies": {
|
||||
"@lit-labs/ssr-dom-shim": "^1.0.0"
|
||||
}
|
||||
@@ -2444,6 +2364,60 @@
|
||||
"react-dom": "^18.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@portaljs/core": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@portaljs/core/-/core-1.0.5.tgz",
|
||||
"integrity": "sha512-HJ3eYjyKL7wrPlBwcEHzNd/SW2PwLu49nAsRTz8r7tgyRINDVkHTLVjZ5V6jCSYtuX7LSlxk0WMIZ9aDOocXDA==",
|
||||
"dependencies": {
|
||||
"@docsearch/react": "^3.3.3",
|
||||
"@floating-ui/react-dom": "^1.3.0",
|
||||
"@floating-ui/react-dom-interactions": "^0.13.3",
|
||||
"@giscus/react": "^2.2.6",
|
||||
"@headlessui/react": "^1.7.12",
|
||||
"clsx": "^1.2.1",
|
||||
"core-js": "^3.30.2",
|
||||
"disqus-react": "^1.1.5",
|
||||
"framer-motion": "^10.0.1",
|
||||
"kbar": "0.1.0-beta.40",
|
||||
"mermaid": "^10.2.2",
|
||||
"prop-types": "^15.8.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"mdx-mermaid": "2.0.0-rc7",
|
||||
"next": "^13.2.1",
|
||||
"next-themes": "^0.2.1",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@portaljs/remark-callouts": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@portaljs/remark-callouts/-/remark-callouts-1.0.5.tgz",
|
||||
"integrity": "sha512-KMjr44isEvQzpNBBCP3s5/3TCmI/ce4xRvbOk6h9xicVZqE6BPTH9rhfOGvop9cchyAWgj9gmJXhQk+Bd+t5bg==",
|
||||
"dependencies": {
|
||||
"mdast-util-from-markdown": "^1.2.0",
|
||||
"svg-parser": "^2.0.4",
|
||||
"unist-util-visit": "^4.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@portaljs/remark-embed": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@portaljs/remark-embed/-/remark-embed-1.0.4.tgz",
|
||||
"integrity": "sha512-BvRMC1iBd5M93u9WS0mvunHoBMWgQQTCR7RdqSS9sRav5pqquiNG7il12Yza4vFpFBXwLkzEG0NwIVXzzutT8w==",
|
||||
"dependencies": {
|
||||
"unist-util-visit": "^4.1.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@portaljs/remark-wiki-link": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@portaljs/remark-wiki-link/-/remark-wiki-link-1.0.4.tgz",
|
||||
"integrity": "sha512-cp6vlssDAPawcBI0vUPRpcPdORql3RbK1Q+t1LuRvMQ+yiRb+9DnSPBUviDXKqwsBMOOXP/ePAdYohaeXGt0lQ==",
|
||||
"dependencies": {
|
||||
"mdast-util-to-markdown": "^1.5.0",
|
||||
"mdast-util-wiki-link": "^0.0.2",
|
||||
"micromark-util-symbol": "^1.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@primer/octicons-react": {
|
||||
"version": "12.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@primer/octicons-react/-/octicons-react-12.1.0.tgz",
|
||||
@@ -2474,27 +2448,43 @@
|
||||
"integrity": "sha512-Z8R0kdAZui8eYTuGY5oQUA0SU4jYq43m4bZW6Dw0B35fUp+U3r+pCrkj0EADJAPv1UaKNskSv/lrfRdC7719Rg=="
|
||||
},
|
||||
"node_modules/@radix-ui/react-compose-refs": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.0.0.tgz",
|
||||
"integrity": "sha512-0KaSv6sx787/hK3eF53iOkiSLwAGlFMx5lotrqD2pTjB18KbybKoEIgkNZTKC60YECDQTKGTRcDBILwZVqVKvA==",
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.0.1.tgz",
|
||||
"integrity": "sha512-fDSBgd44FKHa1FRMU59qBMPFcl2PZE+2nmqunj+BWFyYYjnhIDWL2ItDs3rrbJDQOtzt5nIebLCQc4QRfz6LJw==",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.13.10"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-portal": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.0.2.tgz",
|
||||
"integrity": "sha512-swu32idoCW7KA2VEiUZGBSu9nB6qwGdV6k6HYhUoOo3M1FFpD+VgLzUqtt3mwL1ssz7r2x8MggpLSQach2Xy/Q==",
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.0.3.tgz",
|
||||
"integrity": "sha512-xLYZeHrWoPmA5mEKEfZZevoVRK/Q43GfzRXkWV6qawIWWK8t6ifIiLQdd7rmQ4Vk1bmI21XhqF9BN3jWf+phpA==",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.13.10",
|
||||
"@radix-ui/react-primitive": "1.0.2"
|
||||
"@radix-ui/react-primitive": "1.0.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"@types/react-dom": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0",
|
||||
"react-dom": "^16.8 || ^17.0 || ^18.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
},
|
||||
"@types/react-dom": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-presence": {
|
||||
@@ -2517,28 +2507,44 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-primitive": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-1.0.2.tgz",
|
||||
"integrity": "sha512-zY6G5Qq4R8diFPNwtyoLRZBxzu1Z+SXMlfYpChN7Dv8gvmx9X3qhDqiLWvKseKVJMuedFeU/Sa0Sy/Ia+t06Dw==",
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-1.0.3.tgz",
|
||||
"integrity": "sha512-yi58uVyoAcK/Nq1inRY56ZSjKypBNKTa/1mcL8qdl6oJeEaDbOldlzrGn7P6Q3Id5d+SYNGc5AJgc4vGhjs5+g==",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.13.10",
|
||||
"@radix-ui/react-slot": "1.0.1"
|
||||
"@radix-ui/react-slot": "1.0.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"@types/react-dom": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0",
|
||||
"react-dom": "^16.8 || ^17.0 || ^18.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
},
|
||||
"@types/react-dom": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-slot": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.0.1.tgz",
|
||||
"integrity": "sha512-avutXAFL1ehGvAXtPquu0YK5oz6ctS474iM3vNGQIkswrVhdrS52e3uoMQBzZhNRAIE0jBnUyXWNmSjGHhCFcw==",
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.0.2.tgz",
|
||||
"integrity": "sha512-YeTpuq4deV+6DusvVUW4ivBgnkHwECUu0BiN43L5UCDFgdhsRUWAghhTF5MbvNTPzmiFOx90asDSUjWuCNapwg==",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.13.10",
|
||||
"@radix-ui/react-compose-refs": "1.0.0"
|
||||
"@radix-ui/react-compose-refs": "1.0.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/rect": {
|
||||
@@ -2862,6 +2868,7 @@
|
||||
"resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.0.tgz",
|
||||
"integrity": "sha512-Cn6WYCm0tXv8p6k+A8PvbDG763EDpBoTzHdA+Q/MF6H3sapGjCm9NzoaJncJS9tUKSuCoDs9XHxYYsQDgxR6kw==",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
@@ -3078,24 +3085,24 @@
|
||||
}
|
||||
},
|
||||
"node_modules/algoliasearch": {
|
||||
"version": "4.17.1",
|
||||
"resolved": "https://registry.npmjs.org/algoliasearch/-/algoliasearch-4.17.1.tgz",
|
||||
"integrity": "sha512-4GDQ1RhP2qUR3x8PevFRbEdqZqIARNViZYjgTJmA1T7wRNtFA3W4Aqc/RsODqa1J8IO/QDla5x4tWuUS8NV8wA==",
|
||||
"version": "4.17.2",
|
||||
"resolved": "https://registry.npmjs.org/algoliasearch/-/algoliasearch-4.17.2.tgz",
|
||||
"integrity": "sha512-VFu43JJNYIW74awp7oeQcQsPcxOhd8psqBDTfyNO2Zt6L1NqnNMTVnaIdQ+8dtKqUDBqQZp0szPxECvX8CK2Fg==",
|
||||
"dependencies": {
|
||||
"@algolia/cache-browser-local-storage": "4.17.1",
|
||||
"@algolia/cache-common": "4.17.1",
|
||||
"@algolia/cache-in-memory": "4.17.1",
|
||||
"@algolia/client-account": "4.17.1",
|
||||
"@algolia/client-analytics": "4.17.1",
|
||||
"@algolia/client-common": "4.17.1",
|
||||
"@algolia/client-personalization": "4.17.1",
|
||||
"@algolia/client-search": "4.17.1",
|
||||
"@algolia/logger-common": "4.17.1",
|
||||
"@algolia/logger-console": "4.17.1",
|
||||
"@algolia/requester-browser-xhr": "4.17.1",
|
||||
"@algolia/requester-common": "4.17.1",
|
||||
"@algolia/requester-node-http": "4.17.1",
|
||||
"@algolia/transporter": "4.17.1"
|
||||
"@algolia/cache-browser-local-storage": "4.17.2",
|
||||
"@algolia/cache-common": "4.17.2",
|
||||
"@algolia/cache-in-memory": "4.17.2",
|
||||
"@algolia/client-account": "4.17.2",
|
||||
"@algolia/client-analytics": "4.17.2",
|
||||
"@algolia/client-common": "4.17.2",
|
||||
"@algolia/client-personalization": "4.17.2",
|
||||
"@algolia/client-search": "4.17.2",
|
||||
"@algolia/logger-common": "4.17.2",
|
||||
"@algolia/logger-console": "4.17.2",
|
||||
"@algolia/requester-browser-xhr": "4.17.2",
|
||||
"@algolia/requester-common": "4.17.2",
|
||||
"@algolia/requester-node-http": "4.17.2",
|
||||
"@algolia/transporter": "4.17.2"
|
||||
}
|
||||
},
|
||||
"node_modules/ansi-regex": {
|
||||
@@ -3402,7 +3409,8 @@
|
||||
"url": "https://feross.org/support"
|
||||
}
|
||||
],
|
||||
"optional": true
|
||||
"optional": true,
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/before-after-hook": {
|
||||
"version": "2.2.3",
|
||||
@@ -3430,6 +3438,7 @@
|
||||
"resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz",
|
||||
"integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"buffer": "^5.5.0",
|
||||
"inherits": "^2.0.4",
|
||||
@@ -3532,6 +3541,7 @@
|
||||
}
|
||||
],
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"base64-js": "^1.3.1",
|
||||
"ieee754": "^1.1.13"
|
||||
@@ -3542,6 +3552,7 @@
|
||||
"resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz",
|
||||
"integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": "*"
|
||||
}
|
||||
@@ -3905,6 +3916,16 @@
|
||||
"toggle-selection": "^1.0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/core-js": {
|
||||
"version": "3.31.0",
|
||||
"resolved": "https://registry.npmjs.org/core-js/-/core-js-3.31.0.tgz",
|
||||
"integrity": "sha512-NIp2TQSGfR6ba5aalZD+ZQ1fSxGhDo/s1w0nx3RYzf2pnJxt7YynxFlFScP6eV7+GZsKO95NSjGxyJsU3DZgeQ==",
|
||||
"hasInstallScript": true,
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/core-js"
|
||||
}
|
||||
},
|
||||
"node_modules/cose-base": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/cose-base/-/cose-base-1.0.3.tgz",
|
||||
@@ -3933,6 +3954,7 @@
|
||||
"resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.5.tgz",
|
||||
"integrity": "sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw==",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"node-fetch": "2.6.7"
|
||||
}
|
||||
@@ -3942,6 +3964,7 @@
|
||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz",
|
||||
"integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"whatwg-url": "^5.0.0"
|
||||
},
|
||||
@@ -4080,9 +4103,9 @@
|
||||
"integrity": "sha512-dp3s92+uNI1hWIpPGH3jK2kxE2lMjdXdr+DH8ynZHpd6PUlH6x6cbuXnoMmiNumznqaNO31xu9e79F0uuZ0JFg=="
|
||||
},
|
||||
"node_modules/d3": {
|
||||
"version": "7.8.4",
|
||||
"resolved": "https://registry.npmjs.org/d3/-/d3-7.8.4.tgz",
|
||||
"integrity": "sha512-q2WHStdhiBtD8DMmhDPyJmXUxr6VWRngKyiJ5EfXMxPw+tqT6BhNjhJZ4w3BHsNm3QoVfZLY8Orq/qPFczwKRA==",
|
||||
"version": "7.8.5",
|
||||
"resolved": "https://registry.npmjs.org/d3/-/d3-7.8.5.tgz",
|
||||
"integrity": "sha512-JgoahDG51ncUfJu6wX/1vWQEqOflgXyl4MaHqlcSruTez7yhaRKR9i8VjjcQGeS2en/jnFivXuaIMnseMMt0XA==",
|
||||
"dependencies": {
|
||||
"d3-array": "3",
|
||||
"d3-axis": "3",
|
||||
@@ -4489,7 +4512,6 @@
|
||||
"version": "7.0.10",
|
||||
"resolved": "https://registry.npmjs.org/dagre-d3-es/-/dagre-d3-es-7.0.10.tgz",
|
||||
"integrity": "sha512-qTCQmEhcynucuaZgY5/+ti3X/rnszKZhEQH/ZdWdtP1tA/y3VoHJzcVrO9pjjJCNpigfscAtoUB5ONcd2wNn0A==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"d3": "^7.8.2",
|
||||
"lodash-es": "^4.17.21"
|
||||
@@ -4516,10 +4538,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/dayjs": {
|
||||
"version": "1.11.7",
|
||||
"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.7.tgz",
|
||||
"integrity": "sha512-+Yw9U6YO5TQohxLcIkrXBeY73WP3ejHWVvx8XCk3gxvQDCTEmS48ZrSZCKciI7Bhl/uCMyxYtE9UqRILmFphkQ==",
|
||||
"peer": true
|
||||
"version": "1.11.8",
|
||||
"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.8.tgz",
|
||||
"integrity": "sha512-LcgxzFoWMEPO7ggRv1Y2N31hUf2R0Vj7fuy/m+Bg1K8rr+KAs1AEy4y9jd5DXe8pbHgX+srkHNS7TH6Q6ZhYeQ=="
|
||||
},
|
||||
"node_modules/debug": {
|
||||
"version": "4.3.4",
|
||||
@@ -4685,7 +4706,8 @@
|
||||
"version": "0.0.1045489",
|
||||
"resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1045489.tgz",
|
||||
"integrity": "sha512-D+PTmWulkuQW4D1NTiCRCFxF7pQPn0hgp4YyX4wAQ6xYXKOadSWPR3ENGDQ47MW/Ewc9v2rpC/UEEGahgBYpSQ==",
|
||||
"optional": true
|
||||
"optional": true,
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/didyoumean": {
|
||||
"version": "1.2.2",
|
||||
@@ -4739,8 +4761,7 @@
|
||||
"node_modules/dompurify": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.0.3.tgz",
|
||||
"integrity": "sha512-axQ9zieHLnAnHh0sfAamKYiqXMJAVwu+LM/alQ7WDagoWessyWvMSFyW65CqF3owufNu8HBcE4cM2Vflu7YWcQ==",
|
||||
"peer": true
|
||||
"integrity": "sha512-axQ9zieHLnAnHh0sfAamKYiqXMJAVwu+LM/alQ7WDagoWessyWvMSFyW65CqF3owufNu8HBcE4cM2Vflu7YWcQ=="
|
||||
},
|
||||
"node_modules/downshift": {
|
||||
"version": "6.1.12",
|
||||
@@ -4807,6 +4828,7 @@
|
||||
"resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
|
||||
"integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"once": "^1.4.0"
|
||||
}
|
||||
@@ -5521,6 +5543,7 @@
|
||||
"resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz",
|
||||
"integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"debug": "^4.1.1",
|
||||
"get-stream": "^5.1.0",
|
||||
@@ -5615,6 +5638,7 @@
|
||||
"resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz",
|
||||
"integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"pend": "~1.2.0"
|
||||
}
|
||||
@@ -5749,7 +5773,8 @@
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz",
|
||||
"integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==",
|
||||
"optional": true
|
||||
"optional": true,
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/fs-extra": {
|
||||
"version": "10.1.0",
|
||||
@@ -5884,6 +5909,7 @@
|
||||
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz",
|
||||
"integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"pump": "^3.0.0"
|
||||
},
|
||||
@@ -6456,7 +6482,8 @@
|
||||
"url": "https://feross.org/support"
|
||||
}
|
||||
],
|
||||
"optional": true
|
||||
"optional": true,
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/ignore": {
|
||||
"version": "5.2.4",
|
||||
@@ -7350,9 +7377,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/lit": {
|
||||
"version": "2.7.4",
|
||||
"resolved": "https://registry.npmjs.org/lit/-/lit-2.7.4.tgz",
|
||||
"integrity": "sha512-cgD7xrZoYr21mbrkZIuIrj98YTMw/snJPg52deWVV4A8icLyNHI3bF70xsJeAgwTuiq5Kkd+ZR8gybSJDCPB7g==",
|
||||
"version": "2.7.5",
|
||||
"resolved": "https://registry.npmjs.org/lit/-/lit-2.7.5.tgz",
|
||||
"integrity": "sha512-i/cH7Ye6nBDUASMnfwcictBnsTN91+aBjXoTHF2xARghXScKxpD4F4WYI+VLXg9lqbMinDfvoI7VnZXjyHgdfQ==",
|
||||
"dependencies": {
|
||||
"@lit/reactive-element": "^1.6.0",
|
||||
"lit-element": "^3.3.0",
|
||||
@@ -7993,6 +8020,23 @@
|
||||
"url": "https://github.com/sponsors/wooorm"
|
||||
}
|
||||
},
|
||||
"node_modules/mddb": {
|
||||
"version": "0.1.9",
|
||||
"resolved": "https://registry.npmjs.org/mddb/-/mddb-0.1.9.tgz",
|
||||
"integrity": "sha512-d3B5zArnWDqKN1Blq6hGtfr3HTHUKK/GS95b4OZqcyDPvUP4JnYuWHDjyoJ0tSgDKH6ybB6WdnnI3tcHQvRTyw==",
|
||||
"dependencies": {
|
||||
"@flowershow/remark-wiki-link": "^1.1.2",
|
||||
"gray-matter": "^4.0.3",
|
||||
"knex": "^2.4.2",
|
||||
"remark-gfm": "^3.0.1",
|
||||
"remark-parse": "^10.0.1",
|
||||
"sqlite3": "^5.1.6",
|
||||
"unist-util-select": "^4.0.3"
|
||||
},
|
||||
"bin": {
|
||||
"mddb": "dist/src/bin/index.js"
|
||||
}
|
||||
},
|
||||
"node_modules/mdn-data": {
|
||||
"version": "2.0.14",
|
||||
"resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz",
|
||||
@@ -8002,6 +8046,7 @@
|
||||
"version": "2.0.0-rc7",
|
||||
"resolved": "https://registry.npmjs.org/mdx-mermaid/-/mdx-mermaid-2.0.0-rc7.tgz",
|
||||
"integrity": "sha512-AMy3138EsvcGwE4cGqUVytj4mLpJ3TJ2nek82+67Qi4GSOXoelAqmQudHSk26IsP091c3qX4d/4wi9CqLwFl9Q==",
|
||||
"peer": true,
|
||||
"optionalDependencies": {
|
||||
"estree-util-to-js": "^1.1.0",
|
||||
"estree-util-visit": "^1.2.0",
|
||||
@@ -8032,10 +8077,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/mermaid": {
|
||||
"version": "10.2.0",
|
||||
"resolved": "https://registry.npmjs.org/mermaid/-/mermaid-10.2.0.tgz",
|
||||
"integrity": "sha512-mYKXlH9ngKdMsJ87VYMdlDZXS+MXDAGKPf3XzDf2vvAPnmRoFm7GFebemOAOWYI1bWSECDyoWTGwesWe6mW1Cw==",
|
||||
"peer": true,
|
||||
"version": "10.2.3",
|
||||
"resolved": "https://registry.npmjs.org/mermaid/-/mermaid-10.2.3.tgz",
|
||||
"integrity": "sha512-cMVE5s9PlQvOwfORkyVpr5beMsLdInrycAosdr+tpZ0WFjG4RJ/bUHST7aTgHNJbujHkdBRAm+N50P3puQOfPw==",
|
||||
"dependencies": {
|
||||
"@braintree/sanitize-url": "^6.0.2",
|
||||
"cytoscape": "^3.23.0",
|
||||
@@ -8899,7 +8943,8 @@
|
||||
"version": "0.5.3",
|
||||
"resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz",
|
||||
"integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==",
|
||||
"optional": true
|
||||
"optional": true,
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/modern-normalize": {
|
||||
"version": "1.1.0",
|
||||
@@ -8912,11 +8957,6 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/moment-mini": {
|
||||
"version": "2.29.4",
|
||||
"resolved": "https://registry.npmjs.org/moment-mini/-/moment-mini-2.29.4.tgz",
|
||||
"integrity": "sha512-uhXpYwHFeiTbY9KSgPPRoo1nt8OxNVdMVoTBYHfSEKeRkIkwGpO+gERmhuhBtzfaeOyTkykSrm2+noJBgqt3Hg=="
|
||||
},
|
||||
"node_modules/mri": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz",
|
||||
@@ -9641,7 +9681,8 @@
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz",
|
||||
"integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==",
|
||||
"optional": true
|
||||
"optional": true,
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/periscopic": {
|
||||
"version": "3.1.0",
|
||||
@@ -9850,6 +9891,7 @@
|
||||
"resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz",
|
||||
"integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=0.4.0"
|
||||
}
|
||||
@@ -9896,13 +9938,15 @@
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
|
||||
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==",
|
||||
"optional": true
|
||||
"optional": true,
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/pump": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
|
||||
"integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"end-of-stream": "^1.1.0",
|
||||
"once": "^1.3.1"
|
||||
@@ -9923,6 +9967,7 @@
|
||||
"deprecated": "< 19.4.0 is no longer supported",
|
||||
"hasInstallScript": true,
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"https-proxy-agent": "5.0.1",
|
||||
"progress": "2.0.3",
|
||||
@@ -9938,6 +9983,7 @@
|
||||
"resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-18.2.1.tgz",
|
||||
"integrity": "sha512-MRtTAZfQTluz3U2oU/X2VqVWPcR1+94nbA2V6ZrSZRVEwLqZ8eclZ551qGFQD/vD2PYqHJwWOW/fpC721uznVw==",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"cross-fetch": "3.1.5",
|
||||
"debug": "4.3.4",
|
||||
@@ -10804,6 +10850,15 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/search-insights": {
|
||||
"version": "2.6.0",
|
||||
"resolved": "https://registry.npmjs.org/search-insights/-/search-insights-2.6.0.tgz",
|
||||
"integrity": "sha512-vU2/fJ+h/Mkm/DJOe+EaM5cafJv/1rRTZpGJTuFPf/Q5LjzgMDsqPdSaZsAe+GAWHHsfsu+rQSAn6c8IGtBEVw==",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=8.16.0"
|
||||
}
|
||||
},
|
||||
"node_modules/section-matter": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/section-matter/-/section-matter-1.0.0.tgz",
|
||||
@@ -11403,6 +11458,7 @@
|
||||
"resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz",
|
||||
"integrity": "sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"chownr": "^1.1.1",
|
||||
"mkdirp-classic": "^0.5.2",
|
||||
@@ -11414,13 +11470,15 @@
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz",
|
||||
"integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==",
|
||||
"optional": true
|
||||
"optional": true,
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/tar-stream": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz",
|
||||
"integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"bl": "^4.0.3",
|
||||
"end-of-stream": "^1.4.1",
|
||||
@@ -11484,7 +11542,8 @@
|
||||
"version": "2.3.8",
|
||||
"resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
|
||||
"integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==",
|
||||
"optional": true
|
||||
"optional": true,
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/tildify": {
|
||||
"version": "2.0.0",
|
||||
@@ -11867,6 +11926,7 @@
|
||||
"resolved": "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.4.3.tgz",
|
||||
"integrity": "sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg==",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"buffer": "^5.2.1",
|
||||
"through": "^2.3.8"
|
||||
@@ -12880,6 +12940,7 @@
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.9.0.tgz",
|
||||
"integrity": "sha512-Ja7nszREasGaYUYCI2k4lCKIRTt+y7XuqVoHR44YpI49TtryyqbqvDMn5eqfW7e6HzTukDRIsXqzVHScqRcafg==",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
},
|
||||
@@ -12955,6 +13016,7 @@
|
||||
"resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz",
|
||||
"integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"buffer-crc32": "~0.2.3",
|
||||
"fd-slicer": "~1.1.0"
|
||||
|
||||
@@ -10,16 +10,17 @@
|
||||
"prettier": "prettier --write ."
|
||||
},
|
||||
"dependencies": {
|
||||
"@flowershow/core": "^0.4.13",
|
||||
"@flowershow/markdowndb": "^0.1.5",
|
||||
"@flowershow/remark-callouts": "^1.0.0",
|
||||
"@flowershow/remark-embed": "^1.0.0",
|
||||
"@portaljs/components": "^0.1.6",
|
||||
"@portaljs/core": "^1.0.5",
|
||||
"@portaljs/remark-callouts": "^1.0.5",
|
||||
"@portaljs/remark-embed": "^1.0.4",
|
||||
"@portaljs/remark-wiki-link": "^1.0.4",
|
||||
"@types/node": "18.16.0",
|
||||
"@types/react": "18.0.38",
|
||||
"@types/react-dom": "18.0.11",
|
||||
"eslint": "8.39.0",
|
||||
"eslint-config-next": "13.3.1",
|
||||
"mddb": "^0.1.9",
|
||||
"next": "13.4.3",
|
||||
"next-mdx-remote": "^4.4.1",
|
||||
"next-seo": "^6.0.0",
|
||||
|
||||
@@ -79,7 +79,7 @@ pre {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
@import "@flowershow/remark-callouts/styles.css";
|
||||
@import "@portaljs/remark-callouts/styles.css";
|
||||
|
||||
/* mathjax */
|
||||
.math-inline > mjx-container > svg {
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
import { MDXRemote } from 'next-mdx-remote';
|
||||
import dynamic from 'next/dynamic';
|
||||
import { Mermaid } from '@flowershow/core';
|
||||
import { Mermaid } from '@portaljs/core';
|
||||
|
||||
// Custom components/renderers to pass to MDX.
|
||||
// Since the MDX files aren't loaded by webpack, they have no knowledge of how
|
||||
// to handle import statements. Instead, you must include components in scope
|
||||
// here.
|
||||
const components = {
|
||||
Table: dynamic(() => import('../portaljs/components/Table').then(mod => mod.Table)),
|
||||
Catalog: dynamic(() => import('../portaljs/components/Catalog').then(mod => mod.Catalog)),
|
||||
Table: dynamic(() => import('@portaljs/components').then(mod => mod.Table)),
|
||||
Catalog: dynamic(() => import('@portaljs/components').then(mod => mod.Catalog)),
|
||||
mermaid: Mermaid,
|
||||
Vega: dynamic(() => import('../portaljs/components/Vega').then(mod => mod.Vega)),
|
||||
VegaLite: dynamic(() => import('../portaljs/components/VegaLite').then(mod => mod.VegaLite)),
|
||||
LineChart: dynamic(() => import('../portaljs/components/LineChart').then(mod => mod.LineChart)),
|
||||
FlatUiTable: dynamic(() => import('../portaljs/components/FlatUiTable').then(mod => mod.FlatUiTable)),
|
||||
Vega: dynamic(() => import('@portaljs/components').then(mod => mod.Vega)),
|
||||
VegaLite: dynamic(() => import('@portaljs/components').then(mod => mod.VegaLite)),
|
||||
LineChart: dynamic(() => import('@portaljs/components').then(mod => mod.LineChart)),
|
||||
FlatUiTable: dynamic(() => import('@portaljs/components').then(mod => mod.FlatUiTable)),
|
||||
} as any;
|
||||
|
||||
export default function DRD({ source }: { source: any }) {
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import matter from "gray-matter";
|
||||
import mdxmermaid from "mdx-mermaid";
|
||||
import { h } from "hastscript";
|
||||
import remarkCallouts from "@flowershow/remark-callouts";
|
||||
import remarkEmbed from "@flowershow/remark-embed";
|
||||
import remarkCallouts from "@portaljs/remark-callouts";
|
||||
import remarkEmbed from "@portaljs/remark-embed";
|
||||
import remarkGfm from "remark-gfm";
|
||||
import remarkMath from "remark-math";
|
||||
import remarkSmartypants from "remark-smartypants";
|
||||
import remarkToc from "remark-toc";
|
||||
import remarkWikiLink from "@flowershow/remark-wiki-link";
|
||||
import remarkWikiLink from "@portaljs/remark-wiki-link";
|
||||
import rehypeAutolinkHeadings from "rehype-autolink-headings";
|
||||
import rehypeKatex from "rehype-katex";
|
||||
import rehypeSlug from "rehype-slug";
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { MarkdownDB } from "@flowershow/markdowndb";
|
||||
import { MarkdownDB } from "mddb";
|
||||
|
||||
const dbPath = "markdown.db";
|
||||
|
||||
|
||||
5
examples/learn/next.config.js
Normal file
5
examples/learn/next.config.js
Normal file
@@ -0,0 +1,5 @@
|
||||
const nextConfig = {
|
||||
swcMinify: false
|
||||
}
|
||||
|
||||
module.exports = nextConfig
|
||||
2636
examples/learn/package-lock.json
generated
2636
examples/learn/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -12,17 +12,28 @@
|
||||
"mddb": "mddb ./content"
|
||||
},
|
||||
"dependencies": {
|
||||
"@flowershow/core": "^0.4.10",
|
||||
"@flowershow/markdowndb": "^0.1.1",
|
||||
"@flowershow/remark-callouts": "^1.0.0",
|
||||
"@flowershow/remark-embed": "^1.0.0",
|
||||
"@flowershow/remark-wiki-link": "^1.1.2",
|
||||
"@githubocto/flat-ui": "^0.14.1",
|
||||
"@heroicons/react": "^2.0.17",
|
||||
"@opentelemetry/api": "^1.4.0",
|
||||
"@portaljs/components": "^0.1.0",
|
||||
"@portaljs/components": "^0.1.8",
|
||||
"@portaljs/core": "^1.0.5",
|
||||
"@portaljs/remark-callouts": "^1.0.5",
|
||||
"@portaljs/remark-embed": "^1.0.4",
|
||||
"@portaljs/remark-wiki-link": "^1.0.4",
|
||||
"@tanstack/react-table": "^8.8.5",
|
||||
"flexsearch": "0.7.21",
|
||||
"gray-matter": "^4.0.3",
|
||||
"hastscript": "^7.2.0",
|
||||
"mddb": "^0.1.9",
|
||||
"mdx-mermaid": "2.0.0-rc7",
|
||||
"next": "13.2.1",
|
||||
"next-mdx-remote": "^4.4.1",
|
||||
"papaparse": "^5.4.1",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-hook-form": "^7.43.9",
|
||||
"react-query": "^3.39.3",
|
||||
"react-vega": "^7.6.0",
|
||||
"rehype-autolink-headings": "^6.1.1",
|
||||
"rehype-katex": "^6.0.3",
|
||||
"rehype-prism-plus": "^1.5.1",
|
||||
@@ -32,17 +43,6 @@
|
||||
"remark-smartypants": "^2.0.0",
|
||||
"remark-toc": "^8.0.1",
|
||||
"typescript": "5.0.4",
|
||||
"@githubocto/flat-ui": "^0.14.1",
|
||||
"@heroicons/react": "^2.0.17",
|
||||
"@tanstack/react-table": "^8.8.5",
|
||||
"flexsearch": "0.7.21",
|
||||
"next-mdx-remote": "^4.4.1",
|
||||
"papaparse": "^5.4.1",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-hook-form": "^7.43.9",
|
||||
"react-query": "^3.39.3",
|
||||
"react-vega": "^7.6.0",
|
||||
"vega": "5.25.0",
|
||||
"vega-lite": "5.1.0"
|
||||
},
|
||||
|
||||
@@ -1,119 +0,0 @@
|
||||
import { Index } from 'flexsearch';
|
||||
import { useState } from 'react';
|
||||
import DebouncedInput from './DebouncedInput';
|
||||
import { useForm } from 'react-hook-form';
|
||||
|
||||
export function Catalog({
|
||||
datasets,
|
||||
facets,
|
||||
}: {
|
||||
datasets: any[];
|
||||
facets: string[];
|
||||
}) {
|
||||
const [indexFilter, setIndexFilter] = useState('');
|
||||
const index = new Index({ tokenize: 'full' });
|
||||
datasets.forEach((dataset) =>
|
||||
index.add(
|
||||
dataset._id,
|
||||
//This will join every metadata value + the url_path into one big string and index that
|
||||
Object.entries(dataset.metadata).reduce(
|
||||
(acc, curr) => acc + ' ' + curr[1].toString(),
|
||||
''
|
||||
) +
|
||||
' ' +
|
||||
dataset.url_path
|
||||
)
|
||||
);
|
||||
|
||||
const facetValues = facets
|
||||
? facets.reduce((acc, facet) => {
|
||||
const possibleValues = datasets.reduce((acc, curr) => {
|
||||
const facetValue = curr.metadata[facet];
|
||||
if (facetValue) {
|
||||
return Array.isArray(facetValue)
|
||||
? acc.concat(facetValue)
|
||||
: acc.concat([facetValue]);
|
||||
}
|
||||
return acc;
|
||||
}, []);
|
||||
acc[facet] = {
|
||||
possibleValues: [...new Set(possibleValues)],
|
||||
selectedValue: null,
|
||||
};
|
||||
return acc;
|
||||
}, {})
|
||||
: [];
|
||||
|
||||
const { register, watch } = useForm(facetValues);
|
||||
|
||||
const filteredDatasets = datasets
|
||||
// First filter by flex search
|
||||
.filter((dataset) =>
|
||||
indexFilter !== ''
|
||||
? index.search(indexFilter).includes(dataset._id)
|
||||
: true
|
||||
)
|
||||
//Then check if the selectedValue for the given facet is included in the dataset metadata
|
||||
.filter((dataset) => {
|
||||
//Avoids a server rendering breakage
|
||||
if (!watch() || Object.keys(watch()).length === 0) return true
|
||||
//This will filter only the key pairs of the metadata values that were selected as facets
|
||||
const datasetFacets = Object.entries(dataset.metadata).filter((entry) =>
|
||||
facets.includes(entry[0])
|
||||
);
|
||||
//Check if the value present is included in the selected value in the form
|
||||
return datasetFacets.every((elem) =>
|
||||
watch()[elem[0]].selectedValue
|
||||
? (elem[1] as string | string[]).includes(
|
||||
watch()[elem[0]].selectedValue
|
||||
)
|
||||
: true
|
||||
);
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
<DebouncedInput
|
||||
value={indexFilter ?? ''}
|
||||
onChange={(value) => setIndexFilter(String(value))}
|
||||
className="p-2 text-sm shadow border border-block mr-1"
|
||||
placeholder="Search all datasets..."
|
||||
/>
|
||||
{Object.entries(facetValues).map((elem) => (
|
||||
<select
|
||||
key={elem[0]}
|
||||
defaultValue=""
|
||||
className="p-2 ml-1 text-sm shadow border border-block"
|
||||
{...register(elem[0] + '.selectedValue')}
|
||||
>
|
||||
<option value="">
|
||||
Filter by {elem[0]}
|
||||
</option>
|
||||
{(elem[1] as { possibleValues: string[] }).possibleValues.map(
|
||||
(val) => (
|
||||
<option
|
||||
key={val}
|
||||
className="dark:bg-white dark:text-black"
|
||||
value={val}
|
||||
>
|
||||
{val}
|
||||
</option>
|
||||
)
|
||||
)}
|
||||
</select>
|
||||
))}
|
||||
<ul className='mb-5 pl-6 mt-5 list-disc'>
|
||||
{filteredDatasets.map((dataset) => (
|
||||
<li className='py-2' key={dataset._id}>
|
||||
<a className='font-medium underline' href={dataset.url_path}>
|
||||
{dataset.metadata.title
|
||||
? dataset.metadata.title
|
||||
: dataset.url_path}
|
||||
</a>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
const DebouncedInput = ({
|
||||
value: initialValue,
|
||||
onChange,
|
||||
debounce = 500,
|
||||
...props
|
||||
}) => {
|
||||
const [value, setValue] = useState(initialValue);
|
||||
|
||||
useEffect(() => {
|
||||
setValue(initialValue);
|
||||
}, [initialValue]);
|
||||
|
||||
useEffect(() => {
|
||||
const timeout = setTimeout(() => {
|
||||
onChange(value);
|
||||
}, debounce);
|
||||
|
||||
return () => clearTimeout(timeout);
|
||||
}, [value]);
|
||||
|
||||
return (
|
||||
<input
|
||||
{...props}
|
||||
value={value}
|
||||
onChange={(e) => setValue(e.target.value)}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default DebouncedInput;
|
||||
@@ -1,113 +0,0 @@
|
||||
import { QueryClient, QueryClientProvider, useQuery } from 'react-query';
|
||||
import Papa from 'papaparse';
|
||||
import { Grid } from '@githubocto/flat-ui';
|
||||
|
||||
const queryClient = new QueryClient();
|
||||
|
||||
export async function getCsv(url: string, corsProxy?: string) {
|
||||
if (corsProxy) {
|
||||
url = corsProxy + url
|
||||
}
|
||||
const response = await fetch(url, {
|
||||
headers: {
|
||||
Range: 'bytes=0-5132288',
|
||||
},
|
||||
});
|
||||
const data = await response.text();
|
||||
return data;
|
||||
}
|
||||
|
||||
export async function parseCsv(file: string): Promise<any> {
|
||||
return new Promise((resolve, reject) => {
|
||||
Papa.parse(file, {
|
||||
header: true,
|
||||
dynamicTyping: true,
|
||||
skipEmptyLines: true,
|
||||
transform: (value: string): string => {
|
||||
return value.trim();
|
||||
},
|
||||
complete: (results: any) => {
|
||||
return resolve(results);
|
||||
},
|
||||
error: (error: any) => {
|
||||
return reject(error);
|
||||
},
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
const Spinning = () => {
|
||||
return (
|
||||
<div role="status w-fit mx-auto">
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
className="w-8 h-8 mr-2 text-gray-200 animate-spin dark:text-gray-600 fill-emerald-600"
|
||||
viewBox="0 0 100 101"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M100 50.5908C100 78.2051 77.6142 100.591 50 100.591C22.3858 100.591 0 78.2051 0 50.5908C0 22.9766 22.3858 0.59082 50 0.59082C77.6142 0.59082 100 22.9766 100 50.5908ZM9.08144 50.5908C9.08144 73.1895 27.4013 91.5094 50 91.5094C72.5987 91.5094 90.9186 73.1895 90.9186 50.5908C90.9186 27.9921 72.5987 9.67226 50 9.67226C27.4013 9.67226 9.08144 27.9921 9.08144 50.5908Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
<path
|
||||
d="M93.9676 39.0409C96.393 38.4038 97.8624 35.9116 97.0079 33.5539C95.2932 28.8227 92.871 24.3692 89.8167 20.348C85.8452 15.1192 80.8826 10.7238 75.2124 7.41289C69.5422 4.10194 63.2754 1.94025 56.7698 1.05124C51.7666 0.367541 46.6976 0.446843 41.7345 1.27873C39.2613 1.69328 37.813 4.19778 38.4501 6.62326C39.0873 9.04874 41.5694 10.4717 44.0505 10.1071C47.8511 9.54855 51.7191 9.52689 55.5402 10.0491C60.8642 10.7766 65.9928 12.5457 70.6331 15.2552C75.2735 17.9648 79.3347 21.5619 82.5849 25.841C84.9175 28.9121 86.7997 32.2913 88.1811 35.8758C89.083 38.2158 91.5421 39.6781 93.9676 39.0409Z"
|
||||
fill="currentFill"
|
||||
/>
|
||||
</svg>
|
||||
<span className="sr-only">Loading...</span>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export interface FlatUiTableProps {
|
||||
url?: string;
|
||||
data?: { [key: string]: number | string }[];
|
||||
rawCsv?: string;
|
||||
corsProxy?: string;
|
||||
}
|
||||
export const FlatUiTable: React.FC<FlatUiTableProps> = ({
|
||||
url,
|
||||
data,
|
||||
rawCsv,
|
||||
corsProxy,
|
||||
}) => {
|
||||
return (
|
||||
// Provide the client to your App
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<TableInner corsProxy={corsProxy} url={url} data={data} rawCsv={rawCsv} />
|
||||
</QueryClientProvider>
|
||||
);
|
||||
};
|
||||
|
||||
const TableInner: React.FC<FlatUiTableProps> = ({ url, data, rawCsv, corsProxy }) => {
|
||||
if (data) {
|
||||
return (
|
||||
<div className="w-full" style={{height: '500px'}}>
|
||||
<Grid data={data} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
const { data: csvString, isLoading: isDownloadingCSV } = useQuery(
|
||||
['dataCsv', url],
|
||||
() => getCsv(url as string, corsProxy),
|
||||
{ enabled: !!url }
|
||||
);
|
||||
const { data: parsedData, isLoading: isParsing } = useQuery(
|
||||
['dataPreview', csvString],
|
||||
() => parseCsv(rawCsv ? rawCsv as string : csvString as string),
|
||||
{ enabled: rawCsv ? true : !!csvString }
|
||||
);
|
||||
if (isParsing || isDownloadingCSV)
|
||||
<div className="w-full">
|
||||
<Spinning />
|
||||
</div>;
|
||||
if (parsedData)
|
||||
return (
|
||||
<div className="w-full" style={{height: '500px'}}>
|
||||
<Grid data={parsedData.data} />
|
||||
</div>
|
||||
);
|
||||
return <Spinning />
|
||||
};
|
||||
|
||||
@@ -1,63 +0,0 @@
|
||||
import { VegaLite } from './VegaLite';
|
||||
|
||||
export type LineChartProps = {
|
||||
data: Array<Array<string | number>> | string | { x: string; y: number }[];
|
||||
title?: string;
|
||||
xAxis?: string;
|
||||
yAxis?: string;
|
||||
fullWidth?: boolean;
|
||||
};
|
||||
|
||||
export function LineChart({
|
||||
data = [],
|
||||
fullWidth = false,
|
||||
title = '',
|
||||
xAxis = 'x',
|
||||
yAxis = 'y',
|
||||
}: LineChartProps) {
|
||||
var tmp = data;
|
||||
if (Array.isArray(data)) {
|
||||
tmp = data.map((r) => {
|
||||
return { x: r[0], y: r[1] };
|
||||
});
|
||||
}
|
||||
const vegaData = { table: tmp };
|
||||
const spec = {
|
||||
$schema: 'https://vega.github.io/schema/vega-lite/v5.json',
|
||||
title,
|
||||
width: 600,
|
||||
height: 300,
|
||||
mark: {
|
||||
type: 'line',
|
||||
color: 'black',
|
||||
strokeWidth: 1,
|
||||
tooltip: true,
|
||||
},
|
||||
data: {
|
||||
name: 'table',
|
||||
},
|
||||
selection: {
|
||||
grid: {
|
||||
type: 'interval',
|
||||
bind: 'scales',
|
||||
},
|
||||
},
|
||||
encoding: {
|
||||
x: {
|
||||
field: xAxis,
|
||||
timeUnit: 'year',
|
||||
type: 'temporal',
|
||||
},
|
||||
y: {
|
||||
field: yAxis,
|
||||
type: 'quantitative',
|
||||
},
|
||||
},
|
||||
};
|
||||
if (typeof data === 'string') {
|
||||
spec.data = { url: data } as any;
|
||||
return <VegaLite fullWidth={fullWidth} spec={spec} />;
|
||||
}
|
||||
|
||||
return <VegaLite fullWidth={fullWidth} data={vegaData} spec={spec} />;
|
||||
}
|
||||
@@ -1,195 +0,0 @@
|
||||
import {
|
||||
createColumnHelper,
|
||||
FilterFn,
|
||||
flexRender,
|
||||
getCoreRowModel,
|
||||
getFilteredRowModel,
|
||||
getPaginationRowModel,
|
||||
getSortedRowModel,
|
||||
useReactTable,
|
||||
} from '@tanstack/react-table';
|
||||
|
||||
import {
|
||||
ArrowDownIcon,
|
||||
ArrowUpIcon,
|
||||
ChevronDoubleLeftIcon,
|
||||
ChevronDoubleRightIcon,
|
||||
ChevronLeftIcon,
|
||||
ChevronRightIcon,
|
||||
} from '@heroicons/react/24/solid';
|
||||
|
||||
import React, { useEffect, useMemo, useState } from 'react';
|
||||
|
||||
import parseCsv from '../lib/parseCsv';
|
||||
import DebouncedInput from './DebouncedInput';
|
||||
import loadData from '../lib/loadData';
|
||||
|
||||
export type TableProps = {
|
||||
data?: Array<{ [key: string]: number | string }>;
|
||||
cols?: Array<{ [key: string]: string }>;
|
||||
csv?: string;
|
||||
url?: string;
|
||||
fullWidth?: boolean;
|
||||
};
|
||||
|
||||
export const Table = ({
|
||||
data: ogData = [],
|
||||
cols: ogCols = [],
|
||||
csv = '',
|
||||
url = '',
|
||||
fullWidth = false,
|
||||
}: TableProps) => {
|
||||
if (csv) {
|
||||
const out = parseCsv(csv);
|
||||
ogData = out.rows;
|
||||
ogCols = out.fields;
|
||||
}
|
||||
|
||||
const [data, setData] = React.useState(ogData);
|
||||
const [cols, setCols] = React.useState(ogCols);
|
||||
// const [error, setError] = React.useState(""); // TODO: add error handling
|
||||
|
||||
const tableCols = useMemo(() => {
|
||||
const columnHelper = createColumnHelper();
|
||||
return cols.map((c) =>
|
||||
columnHelper.accessor<any, string>(c.key, {
|
||||
header: () => c.name,
|
||||
cell: (info) => info.getValue(),
|
||||
})
|
||||
);
|
||||
}, [data, cols]);
|
||||
|
||||
const [globalFilter, setGlobalFilter] = useState('');
|
||||
|
||||
const table = useReactTable({
|
||||
data,
|
||||
columns: tableCols,
|
||||
getCoreRowModel: getCoreRowModel(),
|
||||
state: {
|
||||
globalFilter,
|
||||
},
|
||||
globalFilterFn: globalFilterFn,
|
||||
onGlobalFilterChange: setGlobalFilter,
|
||||
getFilteredRowModel: getFilteredRowModel(),
|
||||
getPaginationRowModel: getPaginationRowModel(),
|
||||
getSortedRowModel: getSortedRowModel(),
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (url) {
|
||||
loadData(url).then((data) => {
|
||||
const { rows, fields } = parseCsv(data);
|
||||
setData(rows);
|
||||
setCols(fields);
|
||||
});
|
||||
}
|
||||
}, [url]);
|
||||
|
||||
return (
|
||||
<div className={`${fullWidth ? 'w-[90vw] ml-[calc(50%-45vw)]' : 'w-full'}`}>
|
||||
<DebouncedInput
|
||||
value={globalFilter ?? ''}
|
||||
onChange={(value: any) => setGlobalFilter(String(value))}
|
||||
className="p-2 text-sm shadow border border-block"
|
||||
placeholder="Search all columns..."
|
||||
/>
|
||||
<table className="w-full mt-10">
|
||||
<thead className="text-left border-b border-b-slate-300">
|
||||
{table.getHeaderGroups().map((hg) => (
|
||||
<tr key={hg.id}>
|
||||
{hg.headers.map((h) => (
|
||||
<th key={h.id} className="pr-2 pb-2">
|
||||
<div
|
||||
{...{
|
||||
className: h.column.getCanSort()
|
||||
? 'cursor-pointer select-none'
|
||||
: '',
|
||||
onClick: h.column.getToggleSortingHandler(),
|
||||
}}
|
||||
>
|
||||
{flexRender(h.column.columnDef.header, h.getContext())}
|
||||
{{
|
||||
asc: (
|
||||
<ArrowUpIcon className="inline-block ml-2 h-4 w-4" />
|
||||
),
|
||||
desc: (
|
||||
<ArrowDownIcon className="inline-block ml-2 h-4 w-4" />
|
||||
),
|
||||
}[h.column.getIsSorted() as string] ?? (
|
||||
<div className="inline-block ml-2 h-4 w-4" />
|
||||
)}
|
||||
</div>
|
||||
</th>
|
||||
))}
|
||||
</tr>
|
||||
))}
|
||||
</thead>
|
||||
<tbody>
|
||||
{table.getRowModel().rows.map((r) => (
|
||||
<tr key={r.id} className="border-b border-b-slate-200">
|
||||
{r.getVisibleCells().map((c) => (
|
||||
<td key={c.id} className="py-2">
|
||||
{flexRender(c.column.columnDef.cell, c.getContext())}
|
||||
</td>
|
||||
))}
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
<div className="flex gap-2 items-center justify-center mt-10">
|
||||
<button
|
||||
className={`w-6 h-6 ${
|
||||
!table.getCanPreviousPage() ? 'opacity-25' : 'opacity-100'
|
||||
}`}
|
||||
onClick={() => table.setPageIndex(0)}
|
||||
disabled={!table.getCanPreviousPage()}
|
||||
>
|
||||
<ChevronDoubleLeftIcon />
|
||||
</button>
|
||||
<button
|
||||
className={`w-6 h-6 ${
|
||||
!table.getCanPreviousPage() ? 'opacity-25' : 'opacity-100'
|
||||
}`}
|
||||
onClick={() => table.previousPage()}
|
||||
disabled={!table.getCanPreviousPage()}
|
||||
>
|
||||
<ChevronLeftIcon />
|
||||
</button>
|
||||
<span className="flex items-center gap-1">
|
||||
<div>Page</div>
|
||||
<strong>
|
||||
{table.getState().pagination.pageIndex + 1} of{' '}
|
||||
{table.getPageCount()}
|
||||
</strong>
|
||||
</span>
|
||||
<button
|
||||
className={`w-6 h-6 ${
|
||||
!table.getCanNextPage() ? 'opacity-25' : 'opacity-100'
|
||||
}`}
|
||||
onClick={() => table.nextPage()}
|
||||
disabled={!table.getCanNextPage()}
|
||||
>
|
||||
<ChevronRightIcon />
|
||||
</button>
|
||||
<button
|
||||
className={`w-6 h-6 ${
|
||||
!table.getCanNextPage() ? 'opacity-25' : 'opacity-100'
|
||||
}`}
|
||||
onClick={() => table.setPageIndex(table.getPageCount() - 1)}
|
||||
disabled={!table.getCanNextPage()}
|
||||
>
|
||||
<ChevronDoubleRightIcon />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const globalFilterFn: FilterFn<any> = (row, columnId, filterValue: string) => {
|
||||
const search = filterValue.toLowerCase();
|
||||
|
||||
let value = row.getValue(columnId) as string;
|
||||
if (typeof value === 'number') value = String(value);
|
||||
|
||||
return value?.toLowerCase().includes(search);
|
||||
};
|
||||
@@ -1,6 +0,0 @@
|
||||
// Wrapper for the Vega component
|
||||
import { Vega as VegaOg } from "react-vega";
|
||||
|
||||
export function Vega(props) {
|
||||
return <VegaOg {...props} />;
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
// Wrapper for the Vega Lite component
|
||||
import { VegaLite as VegaLiteOg } from "react-vega";
|
||||
import applyFullWidthDirective from "../lib/applyFullWidthDirective";
|
||||
|
||||
export function VegaLite(props) {
|
||||
const Component = applyFullWidthDirective({ Component: VegaLiteOg });
|
||||
|
||||
return <Component {...props} />;
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
export default function applyFullWidthDirective({
|
||||
Component,
|
||||
defaultWFull = true,
|
||||
}) {
|
||||
return (props) => {
|
||||
const newProps = { ...props };
|
||||
|
||||
let newClassName = newProps.className || "";
|
||||
if (newProps.fullWidth === true) {
|
||||
newClassName += " w-[90vw] ml-[calc(50%-45vw)] max-w-none";
|
||||
} else if (defaultWFull) {
|
||||
// So that charts and tables will have the
|
||||
// same width as the text content, but images
|
||||
// can have its width set using the width prop
|
||||
newClassName += " w-full";
|
||||
}
|
||||
newProps.className = newClassName;
|
||||
|
||||
return <Component {...newProps} />;
|
||||
};
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
export default async function loadData(url: string) {
|
||||
const response = await fetch(url)
|
||||
const data = await response.text()
|
||||
return data
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
import papa from "papaparse";
|
||||
|
||||
const parseCsv = (csv: string) => {
|
||||
csv = csv.trim();
|
||||
const rawdata = papa.parse(csv, { header: true });
|
||||
|
||||
let cols: any[] = [];
|
||||
if(rawdata.meta.fields) {
|
||||
cols = rawdata.meta.fields.map((r: string) => {
|
||||
return { key: r, name: r };
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
rows: rawdata.data as any,
|
||||
fields: cols,
|
||||
};
|
||||
};
|
||||
|
||||
export default parseCsv;
|
||||
@@ -1,7 +1,7 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
@import "@flowershow/remark-callouts/styles.css";
|
||||
@import "@portaljs/remark-callouts/styles.css";
|
||||
|
||||
.w-5 {
|
||||
width: 1.25rem
|
||||
|
||||
29
examples/openspending/components/DataRichDocument.tsx
Normal file
29
examples/openspending/components/DataRichDocument.tsx
Normal file
@@ -0,0 +1,29 @@
|
||||
import { MDXRemote } from 'next-mdx-remote';
|
||||
import dynamic from 'next/dynamic';
|
||||
import { Mermaid } from '@portaljs/core';
|
||||
|
||||
// Custom components/renderers to pass to MDX.
|
||||
// Since the MDX files aren't loaded by webpack, they have no knowledge of how
|
||||
// to handle import statements. Instead, you must include components in scope
|
||||
// here.
|
||||
const components = {
|
||||
Table: dynamic(() => import('@portaljs/components').then((mod) => mod.Table)),
|
||||
Catalog: dynamic(() =>
|
||||
import('@portaljs/components').then((mod) => mod.Catalog)
|
||||
),
|
||||
mermaid: Mermaid,
|
||||
Vega: dynamic(() => import('@portaljs/components').then((mod) => mod.Vega)),
|
||||
VegaLite: dynamic(() =>
|
||||
import('@portaljs/components').then((mod) => mod.VegaLite)
|
||||
),
|
||||
LineChart: dynamic(() =>
|
||||
import('@portaljs/components').then((mod) => mod.LineChart)
|
||||
),
|
||||
FlatUiTable: dynamic(() =>
|
||||
import('@portaljs/components').then((mod) => mod.FlatUiTable)
|
||||
),
|
||||
} as any;
|
||||
|
||||
export default function DRD({ source }: { source: any }) {
|
||||
return <MDXRemote {...source} components={components} />;
|
||||
}
|
||||
@@ -23,6 +23,10 @@ export function Header() {
|
||||
title: 'Datasets',
|
||||
href: '/#datasets',
|
||||
},
|
||||
{
|
||||
title: 'Data Stories',
|
||||
href: '/stories',
|
||||
},
|
||||
{
|
||||
title: 'Blog',
|
||||
href: '/blog',
|
||||
@@ -49,6 +53,10 @@ export function Header() {
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'Contributing',
|
||||
href: '/contributing',
|
||||
},
|
||||
{
|
||||
title: 'Resources',
|
||||
href: '/resources',
|
||||
@@ -157,7 +165,7 @@ function Dropdown({ navItem }: { navItem: any }) {
|
||||
>
|
||||
<div className="py-1">
|
||||
{navItem.children.map((item) => (
|
||||
<Menu.Item>
|
||||
<Menu.Item key={item.href}>
|
||||
{({ active }) => (
|
||||
<a
|
||||
key={item.href}
|
||||
|
||||
38
examples/openspending/content/contributing.md
Normal file
38
examples/openspending/content/contributing.md
Normal file
@@ -0,0 +1,38 @@
|
||||
# How to contribute
|
||||
|
||||
OpenSpending is a project that aims to make public financial data more accessible, understandable, and usable. It is powered by PortalJS, a framework for building data portals that are fast, secure, and easy to customize.
|
||||
|
||||
If you have any questions, the best place to get answers is to reach to us on [Discord](https://discord.gg/xJrxCbkP)
|
||||
|
||||
We welcome contributions from anyone who is interested in improving OpenSpending and making it more useful for the public. There are many ways you can contribute to the project, such as:
|
||||
|
||||
- Submitting datasets for evaluation and inclusion in OpenSpending
|
||||
- Reporting bugs or issues with the website or the data
|
||||
- Suggesting new features or enhancements
|
||||
- Providing feedback or ideas
|
||||
- Helping other users or answering questions
|
||||
|
||||
## Submitting a contribution
|
||||
|
||||
The main platform for communication and collaboration for OpenSpending is Github, if you want to interact with us you can o so by submitting an issue.
|
||||
|
||||
**Repo for issues:** [https://github.com/os-data/registry/issues](https://github.com/os-data/registry/issues)
|
||||
|
||||
If you want to submit a dataset for evaluation and inclusion in OpenSpending, you will need to create an issue on Github using this template:
|
||||
|
||||
```markdown
|
||||
Title: [Dataset Submission] Name of the dataset (This will be the name of the repo where your datasets will be stored)
|
||||
Readme: A description of your data to include in the new repo that will be created
|
||||
Datapackage: All our datasets require a datapackage following the [frictionless spec](https://specs.frictionlessdata.io/) more specific the [fiscal version](https://specs.frictionlessdata.io/fiscal-data-package/)
|
||||
```
|
||||
|
||||
Please make sure that the dataset meets the following criteria before submitting it:
|
||||
|
||||
- The dataset contains public financial data (e.g., budgets, expenditures, revenues, contracts)
|
||||
- The dataset is open and free to use (e.g., no restrictions on access or reuse)
|
||||
- The dataset is structured and machine-readable (e.g., no scanned images or PDFs)
|
||||
- The dataset has a datapackage containing metadata and documentation (e.g., descriptions, definitions, schemas)
|
||||
|
||||
If you have any questions or doubts about submitting a dataset, please contact us on Github or join our [Discord server](https://discord.gg/xJrxCbkP), where you can chat with other contributors and get support from the OpenSpending team.
|
||||
|
||||
We appreciate your interest and involvement in OpenSpending, and we look forward to working with you on making public financial data more open and transparent. Thank you for your contribution! 😊
|
||||
@@ -0,0 +1,191 @@
|
||||
---
|
||||
title: Frankfurt is Investing in Education, and Your City Should Too
|
||||
date: 06/12/2023
|
||||
authors: ['Michael Polidori']
|
||||
---
|
||||
|
||||
_Note: The currency in this post has been converted to USD using an average exchange rate of 1 EUR = 1.20 USD._
|
||||
|
||||
Between 2008 and 2016, the population of Frankfurt, Germany grew from approximately 670k to 740k[^1], while
|
||||
its spending on education climbed from \$465 million to a staggering \$814 million, a 75% increase. That's a
|
||||
significant surge, bumping education to around 20% of the total budget, only second to social issues.
|
||||
|
||||
The stacked bar chart below (using the [City of Frankfurt Expenditure dataset](https://www.openspending.org/@os-data/city-of-frankfurt-expenditure))
|
||||
helps to bring this data to life. It maps out the city's annual spending across various areas. As you can see,
|
||||
the education slice has grown noticeably over the years, indicating Frankfurt's intention to prioritize this space.
|
||||
The city's commitment to education is abundantly clear.
|
||||
|
||||
<VegaLite
|
||||
spec={{
|
||||
$schema: 'https://vega.github.io/schema/vega-lite/v5.json',
|
||||
data: {
|
||||
url: 'https://storage.openspending.org/city-of-frankfurt-expenditure/frankfurt-sums.csv',
|
||||
},
|
||||
transform: [
|
||||
{
|
||||
calculate: "format(datum.amount / 1e6 * 1.20, '$,.1f') + 'M'",
|
||||
as: 'formattedAmountMillion',
|
||||
},
|
||||
{ calculate: 'datum.amount / 1e9 * 1.20', as: 'amountBillion' },
|
||||
],
|
||||
mark: 'bar',
|
||||
width: 350,
|
||||
height: 400,
|
||||
encoding: {
|
||||
x: {
|
||||
field: 'date',
|
||||
type: 'ordinal',
|
||||
title: 'Year',
|
||||
},
|
||||
y: {
|
||||
field: 'amount',
|
||||
type: 'quantitative',
|
||||
title: 'Spending (USD in billions)',
|
||||
axis: {
|
||||
format: '~s',
|
||||
labelExpr: "format(datum.value / 1e9 * 1.20, '$,.0f') + 'B'",
|
||||
},
|
||||
},
|
||||
color: {
|
||||
field: 'label',
|
||||
type: 'nominal',
|
||||
title: 'Area',
|
||||
scale: {
|
||||
domain: [
|
||||
'Building supervision and monument protection',
|
||||
'Business development agency',
|
||||
'Central finance economy',
|
||||
'Central services',
|
||||
'Culture, leisure and sport',
|
||||
'Education',
|
||||
'Elections and superordinate matters',
|
||||
'Environment',
|
||||
'Finance',
|
||||
'Fire protection and emergency services',
|
||||
'Geoinformation and property regulations',
|
||||
'Head of the city administration',
|
||||
'Health',
|
||||
'Local transport and public transport',
|
||||
'Order and security',
|
||||
'Personnel and organization',
|
||||
'Property and building management',
|
||||
'Reside',
|
||||
'Revision and law',
|
||||
'Social issue',
|
||||
'Town planning',
|
||||
],
|
||||
range: [
|
||||
'#EF5350',
|
||||
'#F06292',
|
||||
'#BA68C8',
|
||||
'#9575CD',
|
||||
'#7986CB',
|
||||
'#64B5F6',
|
||||
'#4FC3F7',
|
||||
'#4DD0E1',
|
||||
'#4DB6AC',
|
||||
'#81C784',
|
||||
'#AED581',
|
||||
'#DCE775',
|
||||
'#FFF176',
|
||||
'#FFD54F',
|
||||
'#FFB74D',
|
||||
'#FF8A65',
|
||||
'#A1887F',
|
||||
'#90A4AE',
|
||||
'#78909C',
|
||||
'#546E7A',
|
||||
'#455A64',
|
||||
],
|
||||
},
|
||||
},
|
||||
tooltip: [
|
||||
{ field: 'label', type: 'nominal', title: 'Area' },
|
||||
{
|
||||
field: 'formattedAmountMillion',
|
||||
type: 'ordinal',
|
||||
title: 'Spending (USD in millions)',
|
||||
},
|
||||
{ field: 'date', type: 'ordinal', title: 'Year' },
|
||||
],
|
||||
},
|
||||
}}
|
||||
/>
|
||||
|
||||
Now, let's look at a line chart that traces only the trajectory of education spending through these eight
|
||||
years (using the same dataset). It's not just a line; it's a testament to Frankfurt's growing conviction that investing in education is
|
||||
the key to a prosperous future.
|
||||
|
||||
<VegaLite
|
||||
spec={{
|
||||
$schema: 'https://vega.github.io/schema/vega-lite/v5.json',
|
||||
description: 'Frankfurt Education Spending.',
|
||||
data: {
|
||||
url: 'https://storage.openspending.org/city-of-frankfurt-expenditure/frankfurt-sums.csv',
|
||||
},
|
||||
transform: [
|
||||
{ filter: "datum.label==='Education'" },
|
||||
{
|
||||
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: 'ordinal',
|
||||
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: '',
|
||||
field: 'label',
|
||||
scale: {
|
||||
domain: ['Education'],
|
||||
range: ['#64b5f6'],
|
||||
},
|
||||
},
|
||||
tooltip: [
|
||||
{ field: 'label', type: 'nominal', title: 'Area' },
|
||||
{
|
||||
field: 'formattedAmountMillion',
|
||||
type: 'ordinal',
|
||||
title: 'Spending (USD in millions)',
|
||||
},
|
||||
{ field: 'date', type: 'ordinal', title: 'Year' },
|
||||
],
|
||||
},
|
||||
}}
|
||||
/>
|
||||
|
||||
Despite Frankfurt's population experiencing an increase of less than 100k during this period, the significant
|
||||
investment in education tells a different story. It sends a clear message: education isn't merely a line item
|
||||
in their budget—it's their future.
|
||||
|
||||
During this period, it's likely that schools underwent major overhauls, more teachers got hired, new educational
|
||||
programs launched, etc., to give every child in Frankfurt a chance to have an impactful and quality education.
|
||||
|
||||
The beauty of good data is that it's not just numbers—it's narratives. And the narrative here is that Frankfurt
|
||||
is leveraging the power of long-term investment in education for a better, brighter future. This investment in
|
||||
education is not just about the dollars and cents. It's about recognizing the value of quality education to drive
|
||||
social and economic progress.
|
||||
|
||||
Frankfurt is a city that's recognized the power of education, and it's using that power to redefine its future.
|
||||
One can only hope that more cities follow suit.
|
||||
|
||||
[^1]: https://worldpopulationreview.com/world-cities/frankfurt-population
|
||||
284
examples/openspending/content/stories/moers-anomaly-2016.mdx
Normal file
284
examples/openspending/content/stories/moers-anomaly-2016.mdx
Normal file
@@ -0,0 +1,284 @@
|
||||
---
|
||||
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.
|
||||
@@ -0,0 +1,105 @@
|
||||
---
|
||||
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.
|
||||
@@ -0,0 +1,130 @@
|
||||
---
|
||||
title: Where the European Structural and Investment Funds go?
|
||||
date: 2023-06-08
|
||||
authors: ['Anuar Ustayev']
|
||||
---
|
||||
|
||||
European Structural and Investment Funds (ESIF) is a financial instrument used by the European Union (EU) to support regional development and economic cohesion among its member states. The ESIF combines several funds to provide financial assistance to regions and cities in the EU with the aim of reducing economic disparities and promoting sustainable growth.
|
||||
|
||||
In this data story, our objective is to determine which country benefits the most from the ESIF funds and identify the region within the EU that receives the highest amount of funding.
|
||||
|
||||
To begin our analysis, we are using data on the allocation of ESIF funds across EU member states and their respective regions. These datasets are provided by previous work at OpenSpending project. See available datasets:
|
||||
|
||||
- Full dataset: https://www.openspending.org/@os-data/complete-european-esif-funds-beneficiaries-2007-2020
|
||||
- By country, e.g., this is for Austria: https://www.openspending.org/@os-data/complete-european-esif-funds-beneficiaries-2007-2020-filtered-by-at
|
||||
|
||||
The data provide insights into the financial assistance provided by the European Union to support regional development and economic cohesion:
|
||||
|
||||
<FlatUiTable url="https://storage.openspending.org/complete-european-esif-funds-beneficiaries-2007-2020/eu-esif-funds-beneficiaries-2007-2020-full.csv" />
|
||||
|
||||
After processing the available datasets we have derived an aggregated data resource that groups data by country which enables us to understand where the most funding went between 2007 and 2020. Notice that maximum amounts in each column are highlighted in the table but they might be in local currencies so check the 'currency' field:
|
||||
|
||||
<FlatUiTable url="https://storage.openspending.org/eu-esif-funds-beneficiaries-2007-2020-full-aggregated-by-country.csv" />
|
||||
|
||||
Quick observations:
|
||||
|
||||
- The Czech Republic has the EU cofinancing amount of CZK 2,731,165,279,234 which is approximately EUR 115 billions.
|
||||
- Poland has the EU cofinancing amount of PLN 302,958,765,476 which is approximately EUR 67 billions.
|
||||
- Other countries that use local currencies do not have significant amounts when comparing to above two nations.
|
||||
|
||||
So let's convert all currencies to ~EUR but only include 'eu_cofinancing_amount' values as we will use it for comparison:
|
||||
|
||||
<FlatUiTable url="https://storage.openspending.org/eu-esif-funds-beneficiaries-2007-2020--country-code-confinancing-amount.csv" />
|
||||
|
||||
With this data table we can easily see top countries by funding. Note that you can use the table component above to sort values by values in 'eu_cofinancing_amount' column which helps you to quickly see top countries. Let's build a quick visualization to make it even more obvious:
|
||||
|
||||
<Vega
|
||||
data={{
|
||||
table: [
|
||||
{
|
||||
x: "LU",
|
||||
y: 30852529.89
|
||||
},
|
||||
{
|
||||
x: "DK",
|
||||
y: 492017672
|
||||
},
|
||||
{
|
||||
x: "BE",
|
||||
y: 858388537.42
|
||||
},
|
||||
{
|
||||
x: "DE",
|
||||
y: 912803359.21
|
||||
},
|
||||
{
|
||||
x: "FI",
|
||||
y: 2858282488
|
||||
},
|
||||
{
|
||||
x: "SI",
|
||||
y: 4576151396.24
|
||||
},
|
||||
{
|
||||
x: "EE",
|
||||
y: 5419641772
|
||||
},
|
||||
{
|
||||
x: "LT",
|
||||
y: 6750675528.74
|
||||
},
|
||||
{
|
||||
x: "SK",
|
||||
y: 12295960193.92
|
||||
},
|
||||
{
|
||||
x: "UK",
|
||||
y: 12829255012.1
|
||||
},
|
||||
{
|
||||
x: "FR",
|
||||
y: 15027864492.94
|
||||
},
|
||||
{
|
||||
x: "RO",
|
||||
y: 19970864938.12
|
||||
},
|
||||
{
|
||||
x: "PT",
|
||||
y: 23800568868.02
|
||||
},
|
||||
{
|
||||
x: "IT",
|
||||
y: 34860253672.98
|
||||
},
|
||||
{
|
||||
x: "PL",
|
||||
y: 67537688711
|
||||
},
|
||||
{
|
||||
x: "CZ",
|
||||
y: 115591108113
|
||||
}
|
||||
]
|
||||
}}
|
||||
spec={{
|
||||
$schema: 'https://vega.github.io/schema/vega-lite/v4.json',
|
||||
data: {
|
||||
name: 'table'
|
||||
},
|
||||
encoding: {
|
||||
x: {
|
||||
field: 'x',
|
||||
type: 'ordinal'
|
||||
},
|
||||
y: {
|
||||
field: 'y',
|
||||
type: 'quantitative'
|
||||
}
|
||||
},
|
||||
mark: 'bar'
|
||||
}}
|
||||
/>
|
||||
|
||||
Based on the bar chart above we can conclude that the following 3 countries have received the most amounts from ESIF fund:
|
||||
|
||||
1. Czech Republic - EUR ~116b.
|
||||
2. Poland - EUR ~68b.
|
||||
3. Italy - EUR ~35b.
|
||||
|
||||
_This data story was created by using Datopian's PortalJS framework. You can learn more about the framework by visiting https://portaljs.org/_
|
||||
@@ -2,7 +2,7 @@
|
||||
import { remark } from "remark";
|
||||
import stripMarkdown, { Options } from "strip-markdown";
|
||||
|
||||
import { defaultConfig as siteConfig } from '@flowershow/core'
|
||||
import { defaultConfig as siteConfig } from '@portaljs/core'
|
||||
|
||||
// TODO return type
|
||||
const sluggify = (urlPath: string) => {
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import matter from "gray-matter";
|
||||
import mdxmermaid from "mdx-mermaid";
|
||||
import { h } from "hastscript";
|
||||
import remarkCallouts from "@flowershow/remark-callouts";
|
||||
import remarkEmbed from "@flowershow/remark-embed";
|
||||
import remarkCallouts from "@portaljs/remark-callouts";
|
||||
import remarkEmbed from "@portaljs/remark-embed";
|
||||
import remarkGfm from "remark-gfm";
|
||||
import remarkMath from "remark-math";
|
||||
import remarkSmartypants from "remark-smartypants";
|
||||
import remarkToc from "remark-toc";
|
||||
import remarkWikiLink from "@flowershow/remark-wiki-link";
|
||||
import remarkWikiLink from "@portaljs/remark-wiki-link";
|
||||
import rehypeAutolinkHeadings from "rehype-autolink-headings";
|
||||
import rehypeKatex from "rehype-katex";
|
||||
import rehypeSlug from "rehype-slug";
|
||||
@@ -1,4 +1,4 @@
|
||||
import { MarkdownDB } from "@flowershow/markdowndb";
|
||||
import { MarkdownDB } from "mddb";
|
||||
// import config from "./markdowndb.config.js";
|
||||
|
||||
// TODO get this path from markdowndb.config.js or something
|
||||
|
||||
5162
examples/openspending/package-lock.json
generated
5162
examples/openspending/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -12,34 +12,56 @@
|
||||
"test": "vitest"
|
||||
},
|
||||
"dependencies": {
|
||||
"@flowershow/core": "^0.4.13",
|
||||
"@flowershow/markdowndb": "^0.1.5",
|
||||
"@flowershow/markdowndb": "^0.1.8",
|
||||
"@githubocto/flat-ui": "^0.14.1",
|
||||
"@headlessui/react": "^1.7.14",
|
||||
"@heroicons/react": "^2.0.18",
|
||||
"@octokit/plugin-throttling": "^5.2.2",
|
||||
"@portaljs/components": "0.1.4",
|
||||
"@portaljs/ckan": "^0.0.2",
|
||||
"@portaljs/components": "0.1.12",
|
||||
"@portaljs/core": "^1.0.5",
|
||||
"@portaljs/remark-callouts": "^1.0.5",
|
||||
"@portaljs/remark-embed": "^1.0.4",
|
||||
"@portaljs/remark-wiki-link": "^1.0.4",
|
||||
"@tailwindcss/typography": "^0.5.9",
|
||||
"@types/flexsearch": "^0.7.3",
|
||||
"@types/node": "18.16.0",
|
||||
"@types/react": "18.0.38",
|
||||
"@types/react-dom": "18.0.11",
|
||||
"@types/node": "20.2.3",
|
||||
"@types/react": "18.2.6",
|
||||
"@types/react-dom": "18.2.4",
|
||||
"@vitejs/plugin-react": "^4.0.0",
|
||||
"autoprefixer": "10.4.14",
|
||||
"clsx": "^1.2.1",
|
||||
"datapackage": "^1.1.10",
|
||||
"eslint": "8.39.0",
|
||||
"eslint-config-next": "13.3.1",
|
||||
"eslint": "8.41.0",
|
||||
"eslint-config-next": "13.4.3",
|
||||
"flexsearch": "0.7.21",
|
||||
"next": "13.3.0",
|
||||
"isomorphic-unfetch": "^4.0.2",
|
||||
"mddb": "^0.1.9",
|
||||
"next": "13.4.3",
|
||||
"next-mdx-remote": "^4.4.1",
|
||||
"next-seo": "^6.0.0",
|
||||
"octokit": "^2.0.14",
|
||||
"papaparse": "^5.4.1",
|
||||
"postcss": "8.4.23",
|
||||
"prettier": "^2.8.8",
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0",
|
||||
"react-hook-form": "^7.43.9",
|
||||
"react-markdown": "^8.0.7",
|
||||
"react-query": "^3.39.3",
|
||||
"react-timeago": "^7.1.0",
|
||||
"rehype-autolink-headings": "^6.1.1",
|
||||
"rehype-katex": "^6.0.3",
|
||||
"rehype-prism-plus": "^1.5.1",
|
||||
"rehype-raw": "^6.1.1",
|
||||
"rehype-slug": "^5.1.0",
|
||||
"remark": "^14.0.3",
|
||||
"remark-gfm": "^3.0.1",
|
||||
"remark-math": "^5.1.1",
|
||||
"remark-smartypants": "^2.0.0",
|
||||
"remark-toc": "^8.0.1",
|
||||
"strip-markdown": "^5.0.1",
|
||||
"tailwindcss": "3.3.2",
|
||||
"typescript": "5.0.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -53,6 +53,7 @@ export async function getStaticPaths() {
|
||||
|
||||
const paths = allDocuments
|
||||
.filter((page) => page.metadata?.isDraft !== true)
|
||||
.filter((page) => !page.file_path.startsWith('content/stories/'))
|
||||
.map((page) => {
|
||||
const parts = page.url_path!.split('/');
|
||||
return { params: { slug: parts } };
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { AppProps } from 'next/app';
|
||||
import '@portaljs/components/styles.css';
|
||||
import './styles.css';
|
||||
import { NextSeo } from 'next-seo';
|
||||
|
||||
@@ -13,7 +14,7 @@ import {
|
||||
ThemeProvider,
|
||||
NavItem,
|
||||
NavGroup,
|
||||
} from '@flowershow/core';
|
||||
} from '@portaljs/core';
|
||||
|
||||
export interface CustomAppProps {
|
||||
meta: {
|
||||
@@ -57,7 +58,7 @@ function CustomApp({ Component, pageProps }: AppProps) {
|
||||
<NextSeo title="OpenSpending" />
|
||||
<Script
|
||||
strategy="afterInteractive"
|
||||
src={`https://www.googletagmanager.com/gtag/js?id=${GA_TOKEN}`}
|
||||
src="https://www.googletagmanager.com/gtag/js?id=G-GXZF7NRXX6"
|
||||
/>
|
||||
<Script
|
||||
id="gtag-init"
|
||||
@@ -67,7 +68,7 @@ function CustomApp({ Component, pageProps }: AppProps) {
|
||||
window.dataLayer = window.dataLayer || [];
|
||||
function gtag(){dataLayer.push(arguments);}
|
||||
gtag('js', new Date());
|
||||
gtag('config', ${GA_TOKEN}, {
|
||||
gtag('config', 'G-GXZF7NRXX6', {
|
||||
page_path: window.location.pathname,
|
||||
});
|
||||
`,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import fs from 'fs';
|
||||
import React from 'react';
|
||||
import { GetStaticProps } from 'next';
|
||||
import { BlogsList, SimpleLayout } from '@flowershow/core';
|
||||
import { BlogsList, SimpleLayout } from '@portaljs/core';
|
||||
import clientPromise from '../lib/mddb';
|
||||
import type { CustomAppProps } from './_app';
|
||||
import Layout from '@/components/_shared/Layout';
|
||||
|
||||
70
examples/openspending/pages/stories/[fileName].tsx
Normal file
70
examples/openspending/pages/stories/[fileName].tsx
Normal file
@@ -0,0 +1,70 @@
|
||||
import fs from 'fs';
|
||||
import clientPromise from '@/lib/mddb';
|
||||
import { GetStaticProps } from 'next';
|
||||
import Layout from '../../components/_shared/Layout';
|
||||
import { formatDate } from '@/utils/formatDate';
|
||||
import parse from '../../lib/markdown';
|
||||
import dynamic from 'next/dynamic';
|
||||
|
||||
const DataRichDocument = dynamic(
|
||||
() => import('../../components/DataRichDocument'),
|
||||
{ ssr: false }
|
||||
);
|
||||
|
||||
export default function Page({ source, meta }) {
|
||||
return (
|
||||
<Layout>
|
||||
<article className="docs prose-a:text-primary dark:prose-a:text-primary-dark prose-strong:text-primary dark:prose-strong:text-primary-dark prose-code:text-primary dark:prose-code:text-primary-dark prose-headings:text-primary dark:prose-headings:text-primary-dark prose text-primary dark:text-primary-dark prose-headings:font-headings dark:prose-invert prose-a:break-words mx-auto p-6">
|
||||
<header>
|
||||
<div className="mb-4 flex-col items-center">
|
||||
{meta.title && (
|
||||
<h1 className="flex justify-center">{meta.title}</h1>
|
||||
)}
|
||||
{meta.date && (
|
||||
<p className="text-sm text-zinc-400 dark:text-zinc-500 flex justify-center">
|
||||
<time dateTime={meta.date}>{formatDate(meta.date)}</time>
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</header>
|
||||
<section>
|
||||
<DataRichDocument source={source} />
|
||||
</section>
|
||||
</article>
|
||||
</Layout>
|
||||
);
|
||||
}
|
||||
|
||||
export const getStaticProps: GetStaticProps = async ({ params }) => {
|
||||
const mddb = await clientPromise;
|
||||
const dbFile = await mddb.getFileByUrl('stories/' + params?.fileName);
|
||||
|
||||
let source = fs.readFileSync(dbFile.file_path, { encoding: 'utf-8' });
|
||||
let { mdxSource } = await parse(source, '.mdx', {});
|
||||
return {
|
||||
props: {
|
||||
source: mdxSource,
|
||||
meta: dbFile.metadata,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export async function getStaticPaths() {
|
||||
const mddb = await clientPromise;
|
||||
let allDocuments = await mddb.getFiles({
|
||||
extensions: ['mdx'],
|
||||
folder: 'stories',
|
||||
});
|
||||
|
||||
const paths = allDocuments
|
||||
.filter((page) => page.metadata?.isDraft !== true)
|
||||
.map((page) => {
|
||||
const parts = page.url_path!.split('/').slice(-1)[0];
|
||||
return { params: { fileName: parts } };
|
||||
});
|
||||
|
||||
return {
|
||||
paths,
|
||||
fallback: false,
|
||||
};
|
||||
}
|
||||
57
examples/openspending/pages/stories/index.tsx
Normal file
57
examples/openspending/pages/stories/index.tsx
Normal file
@@ -0,0 +1,57 @@
|
||||
import fs from 'fs';
|
||||
import React from 'react';
|
||||
import { GetStaticProps } from 'next';
|
||||
import { BlogsList, SimpleLayout } from '@portaljs/core';
|
||||
import clientPromise from '../../lib/mddb';
|
||||
import type { CustomAppProps } from '../_app';
|
||||
import Layout from '@/components/_shared/Layout';
|
||||
|
||||
interface BlogIndexPageProps extends CustomAppProps {
|
||||
blogs: any[]; // TODO types
|
||||
}
|
||||
|
||||
export default function StoriesList({
|
||||
stories,
|
||||
meta: { title, description },
|
||||
}: BlogIndexPageProps) {
|
||||
return (
|
||||
<Layout>
|
||||
<div className="blog-list">
|
||||
<SimpleLayout title={title} description={description}>
|
||||
<BlogsList blogs={stories} />
|
||||
</SimpleLayout>
|
||||
</div>
|
||||
</Layout>
|
||||
);
|
||||
}
|
||||
|
||||
export const getStaticProps: GetStaticProps = async () => {
|
||||
const mddb = await clientPromise;
|
||||
const storiesFiles = await mddb.getFiles({ folder: 'stories' });
|
||||
|
||||
const stories = storiesFiles.map((item) => ({
|
||||
_id: item._id,
|
||||
file_path: item.file_path,
|
||||
urlPath: item.url_path,
|
||||
date: item.metadata.date,
|
||||
...item.metadata,
|
||||
}));
|
||||
|
||||
return {
|
||||
props: {
|
||||
meta: {
|
||||
title: 'Data Stories',
|
||||
showSidebar: false,
|
||||
showToc: false,
|
||||
showComments: false,
|
||||
showEditLink: false,
|
||||
urlPath: '/stories',
|
||||
},
|
||||
stories: stories.sort((a, b) => {
|
||||
const bDate = new Date(b.date);
|
||||
const aDate = new Date(a.date);
|
||||
return bDate.getTime() - aDate.getTime();
|
||||
}),
|
||||
},
|
||||
};
|
||||
};
|
||||
@@ -82,3 +82,99 @@ pre {
|
||||
.blog-list button {
|
||||
color: black;
|
||||
}
|
||||
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
@import "@portaljs/remark-callouts/styles.css";
|
||||
|
||||
.w-5 {
|
||||
width: 1.25rem
|
||||
}
|
||||
|
||||
.h-5 {
|
||||
height: 1.25rem
|
||||
}
|
||||
|
||||
/* mathjax */
|
||||
.math-inline > mjx-container > svg {
|
||||
display: inline;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
/* smooth scrolling in modern browsers */
|
||||
html {
|
||||
scroll-behavior: smooth !important;
|
||||
}
|
||||
|
||||
/* tooltip fade-out clip */
|
||||
.tooltip-body::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 3.6rem; /* multiple of $line-height used on the tooltip body (defined in tooltipBodyStyle) */
|
||||
height: 1.2rem; /* ($top + $height)/$line-height is the number of lines we want to clip tooltip text at*/
|
||||
width: 10rem;
|
||||
background: linear-gradient(
|
||||
to right,
|
||||
rgba(255, 255, 255, 0),
|
||||
rgba(255, 255, 255, 1) 100%
|
||||
);
|
||||
}
|
||||
|
||||
:is(h2, h3, h4, h5, h6):not(.blogitem-title) {
|
||||
margin-left: -2rem !important;
|
||||
padding-left: 2rem !important;
|
||||
scroll-margin-top: 4.5rem;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.heading-link {
|
||||
padding: 1px;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
margin: auto 0;
|
||||
border-radius: 5px;
|
||||
background: #1e293b;
|
||||
opacity: 0;
|
||||
transition: opacity 0.2s;
|
||||
}
|
||||
|
||||
.light .heading-link {
|
||||
/* border: 1px solid #ab2b65; */
|
||||
/* background: none; */
|
||||
background: #e2e8f0;
|
||||
}
|
||||
|
||||
:is(h2, h3, h4, h5, h6):not(.blogitem-title):hover .heading-link {
|
||||
opacity: 100;
|
||||
}
|
||||
|
||||
.heading-link svg {
|
||||
transform: scale(0.75);
|
||||
}
|
||||
|
||||
@media screen and (max-width: 640px) {
|
||||
.heading-link {
|
||||
visibility: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen,
|
||||
Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
|
||||
}
|
||||
|
||||
a {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
@@ -4,8 +4,8 @@ module.exports = {
|
||||
"./app/**/*.{js,ts,jsx,tsx,mdx}",
|
||||
"./pages/**/*.{js,ts,jsx,tsx,mdx}",
|
||||
"./components/**/*.{js,ts,jsx,tsx,mdx}",
|
||||
"./node_modules/@flowershow/core/dist/*.js",
|
||||
"./node_modules/@flowershow/core/*.js",
|
||||
"./node_modules/@portaljs/core/dist/*.js",
|
||||
"./node_modules/@portaljs/core/*.js",
|
||||
],
|
||||
darkMode: "class",
|
||||
theme: {
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import matter from "gray-matter";
|
||||
import mdxmermaid from "mdx-mermaid";
|
||||
import { h } from "hastscript";
|
||||
import remarkCallouts from "@flowershow/remark-callouts";
|
||||
import remarkEmbed from "@flowershow/remark-embed";
|
||||
import remarkCallouts from "@portaljs/remark-callouts";
|
||||
import remarkEmbed from "@portaljs/remark-embed";
|
||||
import remarkGfm from "remark-gfm";
|
||||
import remarkMath from "remark-math";
|
||||
import remarkSmartypants from "remark-smartypants";
|
||||
import remarkToc from "remark-toc";
|
||||
import remarkWikiLink from "@flowershow/remark-wiki-link";
|
||||
import remarkWikiLink from "@portaljs/remark-wiki-link";
|
||||
import rehypeAutolinkHeadings from "rehype-autolink-headings";
|
||||
import rehypeKatex from "rehype-katex";
|
||||
import rehypeSlug from "rehype-slug";
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { MarkdownDB } from "@flowershow/markdowndb";
|
||||
import { MarkdownDB } from "mddb";
|
||||
|
||||
const dbPath = "markdown.db";
|
||||
|
||||
|
||||
1252
examples/turing/package-lock.json
generated
1252
examples/turing/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -12,11 +12,6 @@
|
||||
},
|
||||
"browserslist": "defaults, not ie <= 11",
|
||||
"dependencies": {
|
||||
"@flowershow/core": "^0.4.10",
|
||||
"@flowershow/markdowndb": "^0.1.1",
|
||||
"@flowershow/remark-callouts": "^1.0.0",
|
||||
"@flowershow/remark-embed": "^1.0.0",
|
||||
"@flowershow/remark-wiki-link": "^1.1.2",
|
||||
"@headlessui/react": "^1.7.13",
|
||||
"@heroicons/react": "^2.0.17",
|
||||
"@mapbox/rehype-prism": "^0.8.0",
|
||||
@@ -24,6 +19,10 @@
|
||||
"@mdx-js/react": "^2.1.5",
|
||||
"@next/mdx": "^13.0.2",
|
||||
"@opentelemetry/api": "^1.4.0",
|
||||
"@portaljs/core": "^1.0.5",
|
||||
"@portaljs/remark-callouts": "^1.0.5",
|
||||
"@portaljs/remark-embed": "^1.0.4",
|
||||
"@portaljs/remark-wiki-link": "^1.0.4",
|
||||
"@tailwindcss/forms": "^0.5.3",
|
||||
"@tailwindcss/typography": "^0.5.4",
|
||||
"@tanstack/react-table": "^8.8.5",
|
||||
@@ -35,6 +34,7 @@
|
||||
"focus-visible": "^5.2.0",
|
||||
"gray-matter": "^4.0.3",
|
||||
"hastscript": "^7.2.0",
|
||||
"mddb": "^0.1.9",
|
||||
"mdx-mermaid": "^2.0.0-rc7",
|
||||
"mermaid": "^10.1.0",
|
||||
"next": "13.2.1",
|
||||
@@ -60,12 +60,12 @@
|
||||
"tailwindcss": "^3.3.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "18.16.0",
|
||||
"@types/react": "18.2.0",
|
||||
"@types/react-dom": "18.2.0",
|
||||
"eslint": "8.26.0",
|
||||
"eslint-config-next": "13.0.2",
|
||||
"prettier": "^2.8.7",
|
||||
"prettier-plugin-tailwindcss": "^0.2.6",
|
||||
"@types/node": "18.16.0",
|
||||
"@types/react": "18.2.0",
|
||||
"@types/react-dom": "18.2.0"
|
||||
"prettier-plugin-tailwindcss": "^0.2.6"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ import { MDXRemote } from 'next-mdx-remote'
|
||||
import { Card } from '../components/Card'
|
||||
import Head from 'next/head'
|
||||
import parse from '../lib/markdown'
|
||||
import { Mermaid } from '@flowershow/core';
|
||||
import { Mermaid } from '@portaljs/core';
|
||||
|
||||
export const getStaticProps = async ({ params }) => {
|
||||
const urlPath = params.slug ? params.slug.join('/') : ''
|
||||
|
||||
8853
package-lock.json
generated
8853
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
21
package.json
21
package.json
@@ -1,11 +1,12 @@
|
||||
{
|
||||
"name": "portaljs",
|
||||
"workspaces": ["./packages/*"],
|
||||
"workspaces": [
|
||||
"./packages/*"
|
||||
],
|
||||
"version": "0.0.0",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"changeset": "changeset",
|
||||
"prerelease": "nx affected --targets=lint,test",
|
||||
"release": "changeset publish"
|
||||
},
|
||||
"private": true,
|
||||
@@ -14,11 +15,11 @@
|
||||
"@changesets/changelog-github": "^0.4.8",
|
||||
"@changesets/cli": "^2.26.1",
|
||||
"@nrwl/cypress": "15.9.2",
|
||||
"@nrwl/eslint-plugin-nx": "^16.0.2",
|
||||
"@nrwl/eslint-plugin-nx": "15.9.2",
|
||||
"@nrwl/jest": "15.9.2",
|
||||
"@nrwl/js": "15.9.2",
|
||||
"@nrwl/linter": "15.9.2",
|
||||
"@nrwl/next": "^15.9.2",
|
||||
"@nrwl/next": "15.9.2",
|
||||
"@nrwl/react": "15.9.2",
|
||||
"@nrwl/rollup": "15.9.2",
|
||||
"@nrwl/workspace": "15.9.2",
|
||||
@@ -28,13 +29,16 @@
|
||||
"@swc/helpers": "~0.5.0",
|
||||
"@swc/jest": "0.2.20",
|
||||
"@testing-library/react": "14.0.0",
|
||||
"@types/chai": "^4.3.5",
|
||||
"@types/jest": "^29.4.0",
|
||||
"@types/mocha": "^10.0.1",
|
||||
"@types/node": "18.14.2",
|
||||
"@types/react": "18.0.28",
|
||||
"@types/react-dom": "18.0.11",
|
||||
"@typescript-eslint/eslint-plugin": "^5.36.1",
|
||||
"@typescript-eslint/parser": "^5.36.1",
|
||||
"babel-jest": "^29.4.1",
|
||||
"chai": "^4.3.7",
|
||||
"cypress": "^12.2.0",
|
||||
"eslint": "~8.15.0",
|
||||
"eslint-config-next": "13.1.1",
|
||||
@@ -44,14 +48,21 @@
|
||||
"eslint-plugin-jsx-a11y": "6.7.1",
|
||||
"eslint-plugin-react": "7.32.2",
|
||||
"eslint-plugin-react-hooks": "4.6.0",
|
||||
"htmlparser2": "^9.0.0",
|
||||
"jest": "^29.4.1",
|
||||
"jest-environment-jsdom": "^29.4.1",
|
||||
"micromark": "^3.2.0",
|
||||
"mocha": "^10.2.0",
|
||||
"nx": "15.9.2",
|
||||
"prettier": "^2.6.2",
|
||||
"react-test-renderer": "18.2.0",
|
||||
"rehype-stringify": "^9.0.3",
|
||||
"remark": "^14.0.3",
|
||||
"swc-loader": "0.1.15",
|
||||
"ts-jest": "^29.0.5",
|
||||
"ts-node": "10.9.1",
|
||||
"typescript": "~4.9.5"
|
||||
"typescript": "~4.9.5",
|
||||
"unist-util-select": "^4.0.3",
|
||||
"unist-util-visit": "^4.1.2"
|
||||
}
|
||||
}
|
||||
|
||||
7
packages/ckan/CHANGELOG.md
Normal file
7
packages/ckan/CHANGELOG.md
Normal file
@@ -0,0 +1,7 @@
|
||||
# @portaljs/ckan
|
||||
|
||||
## 0.0.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [`e82e2ae`](https://github.com/datopian/portaljs/commit/e82e2ae0211ea3e4701703d353b44cf1001434ef) Thanks [@olayway](https://github.com/olayway)! - Fix: replace deprecated `prepublish` script with `prepare`
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@portaljs/ckan",
|
||||
"version": "0.0.2",
|
||||
"version": "0.0.3",
|
||||
"type": "module",
|
||||
"description": "https://portaljs.org",
|
||||
"keywords": [
|
||||
@@ -13,7 +13,8 @@
|
||||
"scripts": {
|
||||
"build": "tsc && vite build && npm run build-tailwind",
|
||||
"lint": "eslint src --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
|
||||
"build-tailwind": "NODE_ENV=production npx tailwindcss -o ./dist/styles.css --minify"
|
||||
"build-tailwind": "NODE_ENV=production npx tailwindcss -o ./dist/styles.css --minify",
|
||||
"prepare": "npm run build"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^18.2.0",
|
||||
|
||||
1
packages/components/.gitignore
vendored
1
packages/components/.gitignore
vendored
@@ -9,6 +9,7 @@ lerna-debug.log*
|
||||
|
||||
node_modules
|
||||
dist
|
||||
storybook-static
|
||||
dist-ssr
|
||||
*.local
|
||||
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import 'tailwindcss/tailwind.css'
|
||||
import '../src/index.css'
|
||||
|
||||
|
||||
import type { Preview } from '@storybook/react';
|
||||
|
||||
|
||||
@@ -1,6 +1,50 @@
|
||||
# @portaljs/components
|
||||
|
||||
## 0.1.7
|
||||
## 0.3.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#980](https://github.com/datopian/portaljs/pull/980) [`38738525`](https://github.com/datopian/portaljs/commit/3873852567b1aab4827a716bd588bd5de3223e2b) Thanks [@demenech](https://github.com/demenech)! - Fix missing CSS styles for PDF component
|
||||
|
||||
## 0.3.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- [`2e13c1b7`](https://github.com/datopian/portaljs/commit/2e13c1b738ddac91a9419f5c0484406328bd1cd3) Thanks [@demenech](https://github.com/demenech)! - PDF and Excel components
|
||||
|
||||
- [#973](https://github.com/datopian/portaljs/pull/973) [`f3c2a2ff`](https://github.com/datopian/portaljs/commit/f3c2a2ffa7dcf9693bd25318c719ce58d27070b8) Thanks [@demenech](https://github.com/demenech)! - Implement <Excel /> component
|
||||
|
||||
## 0.2.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- [`80c6221a`](https://github.com/datopian/portaljs/commit/80c6221a05733f8c1dd0431bed4d72b1f9d7d636) Thanks [@luccasmmg](https://github.com/luccasmmg)! - Added the OpenLayers and the Map(Leaflet based) component
|
||||
|
||||
## 0.1.12
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#960](https://github.com/datopian/portaljs/pull/960) [`add2f6d0`](https://github.com/datopian/portaljs/commit/add2f6d0f372434eb996d59d6faf5cd06530c932) Thanks [@demenech](https://github.com/demenech)! - Fix width of the <LineChart /> component
|
||||
|
||||
## 0.1.11
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [`e82e2ae`](https://github.com/datopian/portaljs/commit/e82e2ae0211ea3e4701703d353b44cf1001434ef) Thanks [@olayway](https://github.com/olayway)! - Fix: replace deprecated `prepublish` script with `prepare`
|
||||
|
||||
## 0.1.10
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [`40d80d2`](https://github.com/datopian/portaljs/commit/40d80d2282bf8464c1aafb393975065078ad9ea3) Thanks [@olayway](https://github.com/olayway)! - Fix: missing files in the published package.
|
||||
|
||||
## 0.1.9
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#933](https://github.com/datopian/portaljs/pull/933) [`683159d`](https://github.com/datopian/portaljs/commit/683159da02708ce520e9aad9707f2b96b598ec8b) Thanks [@demenech](https://github.com/demenech)! - More params added to <LineChart />, loading spinners added to <Table /> and <LineChart />, minor fixes
|
||||
|
||||
## 0.1.8
|
||||
|
||||
### Patch Changes
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@portaljs/components",
|
||||
"version": "0.1.7",
|
||||
"version": "0.3.1",
|
||||
"type": "module",
|
||||
"description": "https://portaljs.org",
|
||||
"keywords": [
|
||||
@@ -12,11 +12,14 @@
|
||||
],
|
||||
"scripts": {
|
||||
"dev": "npm run storybook",
|
||||
"build": "tsc && vite build && npm run build-tailwind",
|
||||
"example": "vite",
|
||||
"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",
|
||||
"storybook": "storybook dev -p 6006",
|
||||
"build-storybook": "storybook build",
|
||||
"build-tailwind": "NODE_ENV=production npx tailwindcss -o ./dist/styles.css --minify"
|
||||
"build-tailwind": "NODE_ENV=production npx tailwindcss --postcss -c tailwind.config.js -i src/index.css -o ./dist/style.css --minify",
|
||||
"prepare": "npm run build",
|
||||
"fix-leaflet": "node ./scripts/fix-leaflet.cjs"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^18.2.0",
|
||||
@@ -25,19 +28,29 @@
|
||||
"dependencies": {
|
||||
"@githubocto/flat-ui": "^0.14.1",
|
||||
"@heroicons/react": "^2.0.17",
|
||||
"@planet/maps": "^8.1.0",
|
||||
"@tanstack/react-table": "^8.8.5",
|
||||
"ag-grid-react": "^30.0.4",
|
||||
"chroma-js": "^2.4.2",
|
||||
"flexsearch": "0.7.21",
|
||||
"leaflet": "^1.9.4",
|
||||
"next-mdx-remote": "^4.4.1",
|
||||
"ol": "^7.4.0",
|
||||
"papaparse": "^5.4.1",
|
||||
"postcss-url": "^10.1.3",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-hook-form": "^7.43.9",
|
||||
"react-leaflet": "^4.2.1",
|
||||
"react-query": "^3.39.3",
|
||||
"react-vega": "^7.6.0",
|
||||
"rollup-plugin-re": "^1.0.7",
|
||||
"vega": "5.25.0",
|
||||
"vega-lite": "5.1.0",
|
||||
"vitest": "^0.31.4"
|
||||
"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": {
|
||||
"@storybook/addon-essentials": "^7.0.7",
|
||||
@@ -47,13 +60,16 @@
|
||||
"@storybook/react": "^7.0.7",
|
||||
"@storybook/react-vite": "^7.0.7",
|
||||
"@storybook/testing-library": "^0.0.14-next.2",
|
||||
"@swc/core": "^1.3.68",
|
||||
"@types/flexsearch": "^0.7.3",
|
||||
"@types/leaflet": "^1.9.3",
|
||||
"@types/papaparse": "^5.3.7",
|
||||
"@types/react": "^18.0.28",
|
||||
"@types/react-dom": "^18.0.11",
|
||||
"@typescript-eslint/eslint-plugin": "^5.57.1",
|
||||
"@typescript-eslint/parser": "^5.57.1",
|
||||
"@vitejs/plugin-react": "^4.0.0",
|
||||
"@vitejs/plugin-react-swc": "^3.3.2",
|
||||
"autoprefixer": "^10.4.14",
|
||||
"eslint": "^8.38.0",
|
||||
"eslint-plugin-react-hooks": "^4.6.0",
|
||||
@@ -61,12 +77,15 @@
|
||||
"eslint-plugin-storybook": "^0.6.11",
|
||||
"json": "^11.0.0",
|
||||
"postcss": "^8.4.23",
|
||||
"postcss-import": "^15.1.0",
|
||||
"postcss-import-url": "^7.2.0",
|
||||
"prop-types": "^15.8.1",
|
||||
"storybook": "^7.0.7",
|
||||
"tailwindcss": "^3.3.2",
|
||||
"typescript": "^5.0.2",
|
||||
"vite": "^4.3.2",
|
||||
"vite-plugin-dts": "^2.3.0"
|
||||
"vite-plugin-dts": "^2.3.0",
|
||||
"vite-plugin-swc-only": "^0.1.18"
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
@@ -80,7 +99,7 @@
|
||||
"require": "./dist/components.umd.js"
|
||||
},
|
||||
"./styles.css": {
|
||||
"import": "./dist/styles.css"
|
||||
"import": "./dist/style.css"
|
||||
}
|
||||
},
|
||||
"publishConfig": {
|
||||
|
||||
BIN
packages/components/portaljs-components-0.1.12.tgz
Normal file
BIN
packages/components/portaljs-components-0.1.12.tgz
Normal file
Binary file not shown.
@@ -1,6 +1,10 @@
|
||||
console.log('PostCSS');
|
||||
|
||||
export default {
|
||||
plugins: {
|
||||
'postcss-import': {},
|
||||
'postcss-url': { url: 'inline' },
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
6
packages/components/scripts/fix-leaflet.cjs
Normal file
6
packages/components/scripts/fix-leaflet.cjs
Normal file
@@ -0,0 +1,6 @@
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const leafletPath = path.join(require.resolve('leaflet'), '../')
|
||||
|
||||
fs.cpSync(`${leafletPath}images`,'./dist/images', { recursive: true });
|
||||
99
packages/components/src/components/Excel.tsx
Normal file
99
packages/components/src/components/Excel.tsx
Normal file
@@ -0,0 +1,99 @@
|
||||
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>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -1,12 +1,13 @@
|
||||
import { QueryClient, QueryClientProvider, useQuery } from 'react-query';
|
||||
import Papa from 'papaparse';
|
||||
import { Grid } from '@githubocto/flat-ui';
|
||||
import LoadingSpinner from './LoadingSpinner';
|
||||
|
||||
const queryClient = new QueryClient();
|
||||
|
||||
export async function getCsv(url: string, corsProxy?: string) {
|
||||
if (corsProxy) {
|
||||
url = corsProxy + url
|
||||
url = corsProxy + url;
|
||||
}
|
||||
const response = await fetch(url, {
|
||||
headers: {
|
||||
@@ -36,30 +37,6 @@ export async function parseCsv(file: string): Promise<any> {
|
||||
});
|
||||
}
|
||||
|
||||
const Spinning = () => {
|
||||
return (
|
||||
<div role="status w-fit mx-auto">
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
className="w-8 h-8 mr-2 text-gray-200 animate-spin dark:text-gray-600 fill-emerald-600"
|
||||
viewBox="0 0 100 101"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M100 50.5908C100 78.2051 77.6142 100.591 50 100.591C22.3858 100.591 0 78.2051 0 50.5908C0 22.9766 22.3858 0.59082 50 0.59082C77.6142 0.59082 100 22.9766 100 50.5908ZM9.08144 50.5908C9.08144 73.1895 27.4013 91.5094 50 91.5094C72.5987 91.5094 90.9186 73.1895 90.9186 50.5908C90.9186 27.9921 72.5987 9.67226 50 9.67226C27.4013 9.67226 9.08144 27.9921 9.08144 50.5908Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
<path
|
||||
d="M93.9676 39.0409C96.393 38.4038 97.8624 35.9116 97.0079 33.5539C95.2932 28.8227 92.871 24.3692 89.8167 20.348C85.8452 15.1192 80.8826 10.7238 75.2124 7.41289C69.5422 4.10194 63.2754 1.94025 56.7698 1.05124C51.7666 0.367541 46.6976 0.446843 41.7345 1.27873C39.2613 1.69328 37.813 4.19778 38.4501 6.62326C39.0873 9.04874 41.5694 10.4717 44.0505 10.1071C47.8511 9.54855 51.7191 9.52689 55.5402 10.0491C60.8642 10.7766 65.9928 12.5457 70.6331 15.2552C75.2735 17.9648 79.3347 21.5619 82.5849 25.841C84.9175 28.9121 86.7997 32.2913 88.1811 35.8758C89.083 38.2158 91.5421 39.6781 93.9676 39.0409Z"
|
||||
fill="currentFill"
|
||||
/>
|
||||
</svg>
|
||||
<span className="sr-only">Loading...</span>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export interface FlatUiTableProps {
|
||||
url?: string;
|
||||
data?: { [key: string]: number | string }[];
|
||||
@@ -80,10 +57,15 @@ export const FlatUiTable: React.FC<FlatUiTableProps> = ({
|
||||
);
|
||||
};
|
||||
|
||||
const TableInner: React.FC<FlatUiTableProps> = ({ url, data, rawCsv, corsProxy }) => {
|
||||
const TableInner: React.FC<FlatUiTableProps> = ({
|
||||
url,
|
||||
data,
|
||||
rawCsv,
|
||||
corsProxy,
|
||||
}) => {
|
||||
if (data) {
|
||||
return (
|
||||
<div className="w-full" style={{height: '500px'}}>
|
||||
<div className="w-full" style={{ height: '500px' }}>
|
||||
<Grid data={data} />
|
||||
</div>
|
||||
);
|
||||
@@ -95,19 +77,22 @@ const TableInner: React.FC<FlatUiTableProps> = ({ url, data, rawCsv, corsProxy }
|
||||
);
|
||||
const { data: parsedData, isLoading: isParsing } = useQuery(
|
||||
['dataPreview', csvString],
|
||||
() => parseCsv(rawCsv ? rawCsv as string : csvString as string),
|
||||
() => parseCsv(rawCsv ? (rawCsv as string) : (csvString as string)),
|
||||
{ enabled: rawCsv ? true : !!csvString }
|
||||
);
|
||||
if (isParsing || isDownloadingCSV)
|
||||
<div className="w-full">
|
||||
<Spinning />
|
||||
<div className="w-full flex justify-center items-center h-[500px]">
|
||||
<LoadingSpinner />
|
||||
</div>;
|
||||
if (parsedData)
|
||||
return (
|
||||
<div className="w-full" style={{height: '500px'}}>
|
||||
<div className="w-full" style={{ height: '500px' }}>
|
||||
<Grid data={parsedData.data} />
|
||||
</div>
|
||||
);
|
||||
return <Spinning />
|
||||
return (
|
||||
<div className="w-full flex justify-center items-center h-[500px]">
|
||||
<LoadingSpinner />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -1,10 +1,19 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import LoadingSpinner from './LoadingSpinner';
|
||||
import { VegaLite } from './VegaLite';
|
||||
import loadData from '../lib/loadData';
|
||||
|
||||
type AxisType = 'quantitative' | 'temporal';
|
||||
type TimeUnit = 'year' | undefined; // or ...
|
||||
|
||||
export type LineChartProps = {
|
||||
data: Array<Array<string | number>> | string | { x: string; y: number }[];
|
||||
title?: string;
|
||||
xAxis?: string;
|
||||
xAxisType?: AxisType;
|
||||
xAxisTimeUnit: TimeUnit;
|
||||
yAxis?: string;
|
||||
yAxisType?: AxisType;
|
||||
fullWidth?: boolean;
|
||||
};
|
||||
|
||||
@@ -13,19 +22,20 @@ export function LineChart({
|
||||
fullWidth = false,
|
||||
title = '',
|
||||
xAxis = 'x',
|
||||
xAxisType = 'temporal',
|
||||
xAxisTimeUnit = 'year', // TODO: defaults to undefined would probably work better... keeping it as it's for compatibility purposes
|
||||
yAxis = 'y',
|
||||
yAxisType = 'quantitative',
|
||||
}: LineChartProps) {
|
||||
var tmp = data;
|
||||
if (Array.isArray(data)) {
|
||||
tmp = data.map((r) => {
|
||||
return { x: r[0], y: r[1] };
|
||||
});
|
||||
}
|
||||
const vegaData = { table: tmp };
|
||||
const [isLoading, setIsLoading] = useState<boolean>(false);
|
||||
|
||||
// By default, assumes data is an Array...
|
||||
const [specData, setSpecData] = useState<any>({ name: 'table' });
|
||||
|
||||
const spec = {
|
||||
$schema: 'https://vega.github.io/schema/vega-lite/v5.json',
|
||||
title,
|
||||
width: 600,
|
||||
width: 'container',
|
||||
height: 300,
|
||||
mark: {
|
||||
type: 'line',
|
||||
@@ -33,9 +43,7 @@ export function LineChart({
|
||||
strokeWidth: 1,
|
||||
tooltip: true,
|
||||
},
|
||||
data: {
|
||||
name: 'table',
|
||||
},
|
||||
data: specData,
|
||||
selection: {
|
||||
grid: {
|
||||
type: 'interval',
|
||||
@@ -45,19 +53,44 @@ export function LineChart({
|
||||
encoding: {
|
||||
x: {
|
||||
field: xAxis,
|
||||
timeUnit: 'year',
|
||||
type: 'temporal',
|
||||
timeUnit: xAxisTimeUnit,
|
||||
type: xAxisType,
|
||||
},
|
||||
y: {
|
||||
field: yAxis,
|
||||
type: 'quantitative',
|
||||
type: yAxisType,
|
||||
},
|
||||
},
|
||||
};
|
||||
if (typeof data === 'string') {
|
||||
spec.data = { url: data } as any;
|
||||
return <VegaLite fullWidth={fullWidth} spec={spec} />;
|
||||
} as any;
|
||||
|
||||
useEffect(() => {
|
||||
// If data is string, assume it's a URL
|
||||
if (typeof data === 'string') {
|
||||
setIsLoading(true);
|
||||
|
||||
// Manualy loading the data allows us to do other kinds
|
||||
// of stuff later e.g. load a file partially
|
||||
loadData(data).then((res: any) => {
|
||||
setSpecData({ values: res, format: { type: 'csv' } });
|
||||
setIsLoading(false);
|
||||
});
|
||||
}
|
||||
}, []);
|
||||
|
||||
var vegaData = {};
|
||||
if (Array.isArray(data)) {
|
||||
var dataObj;
|
||||
dataObj = data.map((r) => {
|
||||
return { x: r[0], y: r[1] };
|
||||
});
|
||||
vegaData = { table: dataObj };
|
||||
}
|
||||
|
||||
return <VegaLite fullWidth={fullWidth} data={vegaData} spec={spec} />;
|
||||
return isLoading ? (
|
||||
<div className="w-full flex items-center justify-center w-[600px] h-[300px]">
|
||||
<LoadingSpinner />
|
||||
</div>
|
||||
) : (
|
||||
<VegaLite fullWidth={fullWidth} data={vegaData} spec={spec} />
|
||||
);
|
||||
}
|
||||
|
||||
23
packages/components/src/components/LoadingSpinner.tsx
Normal file
23
packages/components/src/components/LoadingSpinner.tsx
Normal file
@@ -0,0 +1,23 @@
|
||||
export default function LoadingSpinner() {
|
||||
return (
|
||||
<div role="status w-fit mx-auto">
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
className="w-16 h-16 mr-2 text-gray-200 animate-spin dark:text-gray-600 fill-slate-600"
|
||||
viewBox="0 0 100 101"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M100 50.5908C100 78.2051 77.6142 100.591 50 100.591C22.3858 100.591 0 78.2051 0 50.5908C0 22.9766 22.3858 0.59082 50 0.59082C77.6142 0.59082 100 22.9766 100 50.5908ZM9.08144 50.5908C9.08144 73.1895 27.4013 91.5094 50 91.5094C72.5987 91.5094 90.9186 73.1895 90.9186 50.5908C90.9186 27.9921 72.5987 9.67226 50 9.67226C27.4013 9.67226 9.08144 27.9921 9.08144 50.5908Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
<path
|
||||
d="M93.9676 39.0409C96.393 38.4038 97.8624 35.9116 97.0079 33.5539C95.2932 28.8227 92.871 24.3692 89.8167 20.348C85.8452 15.1192 80.8826 10.7238 75.2124 7.41289C69.5422 4.10194 63.2754 1.94025 56.7698 1.05124C51.7666 0.367541 46.6976 0.446843 41.7345 1.27873C39.2613 1.69328 37.813 4.19778 38.4501 6.62326C39.0873 9.04874 41.5694 10.4717 44.0505 10.1071C47.8511 9.54855 51.7191 9.52689 55.5402 10.0491C60.8642 10.7766 65.9928 12.5457 70.6331 15.2552C75.2735 17.9648 79.3347 21.5619 82.5849 25.841C84.9175 28.9121 86.7997 32.2913 88.1811 35.8758C89.083 38.2158 91.5421 39.6781 93.9676 39.0409Z"
|
||||
fill="currentFill"
|
||||
/>
|
||||
</svg>
|
||||
<span className="sr-only">Loading...</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
233
packages/components/src/components/Map.tsx
Normal file
233
packages/components/src/components/Map.tsx
Normal file
@@ -0,0 +1,233 @@
|
||||
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>
|
||||
);
|
||||
}
|
||||
84
packages/components/src/components/OpenLayers/Controls.jsx
Normal file
84
packages/components/src/components/OpenLayers/Controls.jsx
Normal file
@@ -0,0 +1,84 @@
|
||||
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>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,29 @@
|
||||
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;
|
||||
4
packages/components/src/components/OpenLayers/Layers.jsx
Normal file
4
packages/components/src/components/OpenLayers/Layers.jsx
Normal file
@@ -0,0 +1,4 @@
|
||||
import React from 'react';
|
||||
export const Layers = ({ children }) => {
|
||||
return <div>{children}</div>;
|
||||
};
|
||||
50
packages/components/src/components/OpenLayers/Map.jsx
Normal file
50
packages/components/src/components/OpenLayers/Map.jsx
Normal file
@@ -0,0 +1,50 @@
|
||||
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;
|
||||
136
packages/components/src/components/OpenLayers/OpenLayers.tsx
Normal file
136
packages/components/src/components/OpenLayers/OpenLayers.tsx
Normal file
@@ -0,0 +1,136 @@
|
||||
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>
|
||||
);
|
||||
}
|
||||
23
packages/components/src/components/OpenLayers/TileLayer.jsx
Normal file
23
packages/components/src/components/OpenLayers/TileLayer.jsx
Normal file
@@ -0,0 +1,23 @@
|
||||
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;
|
||||
@@ -0,0 +1,33 @@
|
||||
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;
|
||||
5210
packages/components/src/components/OpenLayers/colors.js
Normal file
5210
packages/components/src/components/OpenLayers/colors.js
Normal file
File diff suppressed because it is too large
Load Diff
32
packages/components/src/components/PdfViewer.tsx
Normal file
32
packages/components/src/components/PdfViewer.tsx
Normal file
@@ -0,0 +1,32 @@
|
||||
// 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>
|
||||
);
|
||||
}
|
||||
@@ -23,6 +23,7 @@ import React, { useEffect, useMemo, useState } from 'react';
|
||||
import parseCsv from '../lib/parseCsv';
|
||||
import DebouncedInput from './DebouncedInput';
|
||||
import loadData from '../lib/loadData';
|
||||
import LoadingSpinner from './LoadingSpinner';
|
||||
|
||||
export type TableProps = {
|
||||
data?: Array<{ [key: string]: number | string }>;
|
||||
@@ -39,6 +40,8 @@ export const Table = ({
|
||||
url = '',
|
||||
fullWidth = false,
|
||||
}: TableProps) => {
|
||||
const [isLoading, setIsLoading] = useState<boolean>(false);
|
||||
|
||||
if (csv) {
|
||||
const out = parseCsv(csv);
|
||||
ogData = out.rows;
|
||||
@@ -77,15 +80,22 @@ export const Table = ({
|
||||
|
||||
useEffect(() => {
|
||||
if (url) {
|
||||
setIsLoading(true);
|
||||
// TODO: exception handling. What if the file doesn't exist? What if fetching was not possible?
|
||||
loadData(url).then((data) => {
|
||||
const { rows, fields } = parseCsv(data);
|
||||
setData(rows);
|
||||
setCols(fields);
|
||||
setIsLoading(false);
|
||||
});
|
||||
}
|
||||
}, [url]);
|
||||
|
||||
return (
|
||||
return isLoading ? (
|
||||
<div className="w-full h-full min-h-[500px] flex items-center justify-center">
|
||||
<LoadingSpinner />
|
||||
</div>
|
||||
) : (
|
||||
<div className={`${fullWidth ? 'w-[90vw] ml-[calc(50%-45vw)]' : 'w-full'}`}>
|
||||
<DebouncedInput
|
||||
value={globalFilter ?? ''}
|
||||
|
||||
6
packages/components/src/include.css
Normal file
6
packages/components/src/include.css
Normal file
@@ -0,0 +1,6 @@
|
||||
/* Temporary fix for a size issue with FlatUiTable loading indicator on Firefox */
|
||||
@layer base {
|
||||
svg[tw^='animate-pulse w-12'] {
|
||||
max-width: 100px;
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,11 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
@import "tailwindcss/base";
|
||||
@import "tailwindcss/components";
|
||||
@import "include";
|
||||
@import "leaflet";
|
||||
@import '@react-pdf-viewer/core/lib/styles/index.css';
|
||||
@import '@react-pdf-viewer/default-layout/lib/styles/index.css';
|
||||
@import 'ag-grid-community/styles/ag-grid.css';
|
||||
@import 'ag-grid-community/styles/ag-theme-alpine.css';
|
||||
@import "tailwindcss/utilities";
|
||||
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
export * from "./components/Table";
|
||||
export * from "./components/Catalog";
|
||||
export * from "./components/LineChart";
|
||||
export * from "./components/Vega";
|
||||
export * from "./components/VegaLite";
|
||||
export * from "./components/FlatUiTable";
|
||||
export * from './components/Table';
|
||||
export * from './components/Catalog';
|
||||
export * from './components/LineChart';
|
||||
export * from './components/Vega';
|
||||
export * from './components/VegaLite';
|
||||
export * from './components/FlatUiTable';
|
||||
export * from './components/OpenLayers/OpenLayers';
|
||||
export * from './components/Map';
|
||||
export * from './components/PdfViewer';
|
||||
export * from "./components/Excel";
|
||||
|
||||
171
packages/components/src/types/GeoJSON.tsx
Normal file
171
packages/components/src/types/GeoJSON.tsx
Normal file
@@ -0,0 +1,171 @@
|
||||
/**
|
||||
* 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[];
|
||||
}
|
||||
}
|
||||
34
packages/components/stories/Excel.stories.ts
Normal file
34
packages/components/stories/Excel.stories.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
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',
|
||||
},
|
||||
};
|
||||
@@ -19,10 +19,19 @@ const meta: Meta = {
|
||||
description:
|
||||
'Name of the X axis on the data. Required when the "data" parameter is an URL.',
|
||||
},
|
||||
xAxisType: {
|
||||
description: 'Type of the X axis',
|
||||
},
|
||||
xAxisTimeUnit: {
|
||||
description: 'Time unit of the X axis (optional)',
|
||||
},
|
||||
yAxis: {
|
||||
description:
|
||||
'Name of the Y axis on the data. Required when the "data" parameter is an URL.',
|
||||
},
|
||||
yAxisType: {
|
||||
description: 'Type of the Y axis',
|
||||
},
|
||||
fullWidth: {
|
||||
description:
|
||||
'Whether the component should be rendered as full bleed or not',
|
||||
|
||||
91
packages/components/stories/Map.stories.ts
Normal file
91
packages/components/stories/Map.stories.ts
Normal file
@@ -0,0 +1,91 @@
|
||||
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,
|
||||
},
|
||||
};
|
||||
136
packages/components/stories/OpenLayers.stories.tsx
Normal file
136
packages/components/stories/OpenLayers.stories.tsx
Normal file
@@ -0,0 +1,136 @@
|
||||
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,
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
50
packages/components/stories/PdfViewer.stories.ts
Normal file
50
packages/components/stories/PdfViewer.stories.ts
Normal file
@@ -0,0 +1,50 @@
|
||||
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,
|
||||
},
|
||||
};
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,2 +0,0 @@
|
||||
import{j as p}from"./jsx-runtime-94f6e698.js";import{r as u}from"./index-8db94870.js";const a=({value:e,onChange:o,debounce:s=500,...c})=>{const[t,r]=u.useState(e);return u.useEffect(()=>{r(e)},[e]),u.useEffect(()=>{const n=setTimeout(()=>{o(t)},s);return()=>clearTimeout(n)},[t]),p.jsx("input",{...c,value:t,onChange:n=>r(n.target.value)})};try{a.displayName="DebouncedInput",a.__docgenInfo={description:"",displayName:"DebouncedInput",props:{value:{defaultValue:null,description:"",name:"value",required:!0,type:{name:"any"}},onChange:{defaultValue:null,description:"",name:"onChange",required:!0,type:{name:"any"}},debounce:{defaultValue:{value:"500"},description:"",name:"debounce",required:!1,type:{name:"number"}}}}}catch{}export{a as D};
|
||||
//# sourceMappingURL=DebouncedInput-c720676c.js.map
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user