@genspectrum/dashboard-components 0.11.2 → 0.11.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/custom-elements.json +16 -0
- package/dist/assets/mutationOverTimeWorker-Cr-NmYEs.js.map +1 -1
- package/dist/components.d.ts +14 -14
- package/dist/components.js +324 -279
- package/dist/components.js.map +1 -1
- package/dist/style.css +2 -11
- package/dist/util.d.ts +14 -14
- package/package.json +2 -2
- package/src/preact/aggregatedData/aggregate.tsx +1 -1
- package/src/preact/components/error-display.tsx +11 -16
- package/src/preact/components/info.tsx +5 -19
- package/src/preact/components/modal.stories.tsx +44 -0
- package/src/preact/components/modal.tsx +31 -0
- package/src/preact/map/__mockData__/aggregatedGermany.json +5 -1
- package/src/preact/map/{useGeoJsonMap.tsx → loadMapSource.tsx} +7 -24
- package/src/preact/map/sequences-by-location-map.tsx +37 -132
- package/src/preact/map/sequences-by-location-table.tsx +28 -5
- package/src/preact/map/sequences-by-location.tsx +32 -13
- package/src/preact/useQuery.ts +1 -1
- package/src/query/computeMapLocationData.spec.ts +103 -0
- package/src/query/computeMapLocationData.ts +136 -0
- package/src/query/querySequencesByLocationData.ts +18 -0
- package/src/utilEntrypoint.ts +1 -1
- package/src/web-components/visualization/gs-sequences-by-location.stories.ts +17 -0
- package/standalone-bundle/assets/mutationOverTimeWorker-DIQRmxvC.js.map +1 -1
- package/standalone-bundle/dashboard-components.js +4421 -4368
- package/standalone-bundle/dashboard-components.js.map +1 -1
- package/standalone-bundle/style.css +1 -1
package/dist/components.js
CHANGED
|
@@ -582,11 +582,11 @@ function GsAppError(error) {
|
|
|
582
582
|
var f$1 = 0;
|
|
583
583
|
function u$1(e2, t2, n3, o2, i2, u2) {
|
|
584
584
|
t2 || (t2 = {});
|
|
585
|
-
var a2, c2,
|
|
586
|
-
"ref" in
|
|
587
|
-
var
|
|
588
|
-
if ("function" == typeof e2 && (a2 = e2.defaultProps)) for (c2 in a2) void 0 ===
|
|
589
|
-
return options.vnode && options.vnode(
|
|
585
|
+
var a2, c2, p2 = t2;
|
|
586
|
+
if ("ref" in p2) for (c2 in p2 = {}, t2) "ref" == c2 ? a2 = t2[c2] : p2[c2] = t2[c2];
|
|
587
|
+
var l2 = { type: e2, props: p2, key: n3, ref: a2, __k: null, __: null, __b: 0, __e: null, __c: null, constructor: void 0, __v: --f$1, __i: -1, __u: 0, __source: i2, __self: u2 };
|
|
588
|
+
if ("function" == typeof e2 && (a2 = e2.defaultProps)) for (c2 in a2) void 0 === p2[c2] && (p2[c2] = a2[c2]);
|
|
589
|
+
return options.vnode && options.vnode(l2), l2;
|
|
590
590
|
}
|
|
591
591
|
var t, r, u, i, o = 0, f = [], c = options, e = c.__b, a = c.__r, v = c.diffed, l = c.__c, m = c.unmount, s = c.__;
|
|
592
592
|
function d(n3, t2) {
|
|
@@ -1242,6 +1242,109 @@ const CsvDownloadButton = ({
|
|
|
1242
1242
|
};
|
|
1243
1243
|
return /* @__PURE__ */ u$1("button", { className, onClick: download, children: label });
|
|
1244
1244
|
};
|
|
1245
|
+
function useModalRef() {
|
|
1246
|
+
return A(null);
|
|
1247
|
+
}
|
|
1248
|
+
const Modal = ({ children, modalRef }) => {
|
|
1249
|
+
return /* @__PURE__ */ u$1("dialog", { ref: modalRef, className: "modal modal-bottom sm:modal-middle", children: [
|
|
1250
|
+
/* @__PURE__ */ u$1("div", { className: "modal-box sm:max-w-5xl", children: [
|
|
1251
|
+
/* @__PURE__ */ u$1("form", { method: "dialog", children: /* @__PURE__ */ u$1("button", { className: "btn btn-sm btn-circle btn-ghost absolute right-2 top-2", children: "✕" }) }),
|
|
1252
|
+
/* @__PURE__ */ u$1("div", { className: "flex flex-col", children }),
|
|
1253
|
+
/* @__PURE__ */ u$1("div", { className: "modal-action", children: /* @__PURE__ */ u$1("form", { method: "dialog", children: /* @__PURE__ */ u$1("button", { className: "float-right underline text-sm hover:text-blue-700 mr-2", children: "Close" }) }) })
|
|
1254
|
+
] }),
|
|
1255
|
+
/* @__PURE__ */ u$1("form", { method: "dialog", className: "modal-backdrop", children: /* @__PURE__ */ u$1("button", { children: "Helper to close when clicked outside" }) })
|
|
1256
|
+
] });
|
|
1257
|
+
};
|
|
1258
|
+
const Info = ({ children }) => {
|
|
1259
|
+
const modalRef = useModalRef();
|
|
1260
|
+
const toggleHelp = () => {
|
|
1261
|
+
var _a;
|
|
1262
|
+
(_a = modalRef.current) == null ? void 0 : _a.showModal();
|
|
1263
|
+
};
|
|
1264
|
+
return /* @__PURE__ */ u$1("div", { className: "relative", children: [
|
|
1265
|
+
/* @__PURE__ */ u$1("button", { type: "button", className: "btn btn-xs", onClick: toggleHelp, children: "?" }),
|
|
1266
|
+
/* @__PURE__ */ u$1(Modal, { modalRef, children })
|
|
1267
|
+
] });
|
|
1268
|
+
};
|
|
1269
|
+
const InfoHeadline1 = ({ children }) => {
|
|
1270
|
+
return /* @__PURE__ */ u$1("h1", { className: "text-lg font-bold", children });
|
|
1271
|
+
};
|
|
1272
|
+
const InfoHeadline2 = ({ children }) => {
|
|
1273
|
+
return /* @__PURE__ */ u$1("h2", { className: "text-base font-bold mt-4", children });
|
|
1274
|
+
};
|
|
1275
|
+
const InfoParagraph = ({ children }) => {
|
|
1276
|
+
return /* @__PURE__ */ u$1("p", { className: "text-justify my-1", children });
|
|
1277
|
+
};
|
|
1278
|
+
const InfoLink = ({ children, href }) => {
|
|
1279
|
+
return /* @__PURE__ */ u$1("a", { className: "text-blue-600 hover:text-blue-800", href, target: "_blank", rel: "noopener noreferrer", children });
|
|
1280
|
+
};
|
|
1281
|
+
const InfoComponentCode = ({ componentName, params, lapisUrl }) => {
|
|
1282
|
+
const componentCode = componentParametersToCode(componentName, params, lapisUrl);
|
|
1283
|
+
const codePenData = {
|
|
1284
|
+
title: "GenSpectrum dashboard component",
|
|
1285
|
+
html: generateFullExampleCode(componentCode, componentName),
|
|
1286
|
+
layout: "left",
|
|
1287
|
+
editors: "100"
|
|
1288
|
+
};
|
|
1289
|
+
return /* @__PURE__ */ u$1(Fragment, { children: [
|
|
1290
|
+
/* @__PURE__ */ u$1(InfoHeadline2, { children: "Use this component yourself" }),
|
|
1291
|
+
/* @__PURE__ */ u$1(InfoParagraph, { children: [
|
|
1292
|
+
"This component was created using the following parameters:",
|
|
1293
|
+
/* @__PURE__ */ u$1("div", { className: "p-4 border border-gray-200 rounded-lg overflow-x-auto", children: /* @__PURE__ */ u$1("pre", { children: /* @__PURE__ */ u$1("code", { children: componentCode }) }) })
|
|
1294
|
+
] }),
|
|
1295
|
+
/* @__PURE__ */ u$1(InfoParagraph, { children: [
|
|
1296
|
+
"You can add this component to your own website using the",
|
|
1297
|
+
" ",
|
|
1298
|
+
/* @__PURE__ */ u$1(InfoLink, { href: "https://github.com/GenSpectrum/dashboard-components", children: "GenSpectrum dashboard components library" }),
|
|
1299
|
+
" ",
|
|
1300
|
+
"and the code from above."
|
|
1301
|
+
] }),
|
|
1302
|
+
/* @__PURE__ */ u$1(InfoParagraph, { children: /* @__PURE__ */ u$1("form", { action: "https://codepen.io/pen/define", method: "POST", target: "_blank", children: [
|
|
1303
|
+
/* @__PURE__ */ u$1(
|
|
1304
|
+
"input",
|
|
1305
|
+
{
|
|
1306
|
+
type: "hidden",
|
|
1307
|
+
name: "data",
|
|
1308
|
+
value: JSON.stringify(codePenData).replace(/"/g, """).replace(/'/g, "'")
|
|
1309
|
+
}
|
|
1310
|
+
),
|
|
1311
|
+
/* @__PURE__ */ u$1("button", { className: "text-blue-600 hover:text-blue-800", type: "submit", children: "Click here to try it out on CodePen." })
|
|
1312
|
+
] }) })
|
|
1313
|
+
] });
|
|
1314
|
+
};
|
|
1315
|
+
function componentParametersToCode(componentName, params, lapisUrl) {
|
|
1316
|
+
const stringifyIfNeeded = (value) => {
|
|
1317
|
+
return typeof value === "object" ? JSON.stringify(value) : value;
|
|
1318
|
+
};
|
|
1319
|
+
const attributes = indentLines(
|
|
1320
|
+
Object.entries(params).map(([key, value]) => `${key}='${stringifyIfNeeded(value)}'`).join("\n"),
|
|
1321
|
+
4
|
|
1322
|
+
);
|
|
1323
|
+
return `<gs-app lapis="${lapisUrl}">
|
|
1324
|
+
<gs-${componentName}
|
|
1325
|
+
${attributes}
|
|
1326
|
+
/>
|
|
1327
|
+
</gs-app>`;
|
|
1328
|
+
}
|
|
1329
|
+
function generateFullExampleCode(componentCode, componentName) {
|
|
1330
|
+
const storyBookPath = `/docs/visualization-${componentName}--docs`;
|
|
1331
|
+
return `<html>
|
|
1332
|
+
<head>
|
|
1333
|
+
<script type="module" src="https://unpkg.com/@genspectrum/dashboard-components@latest/standalone-bundle/dashboard-components.js"><\/script>
|
|
1334
|
+
<link rel="stylesheet" href="https://unpkg.com/@genspectrum/dashboard-components@latest/dist/style.css" />
|
|
1335
|
+
</head>
|
|
1336
|
+
|
|
1337
|
+
<body>
|
|
1338
|
+
<!-- Component documentation: https://genspectrum.github.io/dashboard-components/?path=${storyBookPath} -->
|
|
1339
|
+
${indentLines(componentCode, 2)}
|
|
1340
|
+
</body>
|
|
1341
|
+
</html>
|
|
1342
|
+
`;
|
|
1343
|
+
}
|
|
1344
|
+
function indentLines(text, numberSpaces) {
|
|
1345
|
+
const spaces = " ".repeat(numberSpaces);
|
|
1346
|
+
return text.split("\n").map((line) => spaces + line).join("\n");
|
|
1347
|
+
}
|
|
1245
1348
|
const GS_ERROR_EVENT_TYPE = "gs-error";
|
|
1246
1349
|
class ErrorEvent extends Event {
|
|
1247
1350
|
constructor(error) {
|
|
@@ -1270,7 +1373,7 @@ class InvalidPropsError extends Error {
|
|
|
1270
1373
|
const ErrorDisplay = ({ error, resetError, layout }) => {
|
|
1271
1374
|
console.error(error);
|
|
1272
1375
|
const containerRef = A(null);
|
|
1273
|
-
const
|
|
1376
|
+
const modalRef = useModalRef();
|
|
1274
1377
|
y(() => {
|
|
1275
1378
|
var _a;
|
|
1276
1379
|
(_a = containerRef.current) == null ? void 0 : _a.dispatchEvent(new ErrorEvent(error));
|
|
@@ -1288,17 +1391,20 @@ const ErrorDisplay = ({ error, resetError, layout }) => {
|
|
|
1288
1391
|
"Oops! Something went wrong.",
|
|
1289
1392
|
details !== void 0 && /* @__PURE__ */ u$1(Fragment, { children: [
|
|
1290
1393
|
" ",
|
|
1291
|
-
/* @__PURE__ */ u$1(
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1394
|
+
/* @__PURE__ */ u$1(
|
|
1395
|
+
"button",
|
|
1396
|
+
{
|
|
1397
|
+
className: "underline hover:text-gray-400",
|
|
1398
|
+
onClick: () => {
|
|
1399
|
+
var _a;
|
|
1400
|
+
return (_a = modalRef.current) == null ? void 0 : _a.showModal();
|
|
1401
|
+
},
|
|
1402
|
+
children: "Show details."
|
|
1403
|
+
}
|
|
1404
|
+
),
|
|
1405
|
+
/* @__PURE__ */ u$1(Modal, { modalRef, children: [
|
|
1406
|
+
/* @__PURE__ */ u$1(InfoHeadline1, { children: details.headline }),
|
|
1407
|
+
/* @__PURE__ */ u$1(InfoParagraph, { children: details.message })
|
|
1302
1408
|
] })
|
|
1303
1409
|
] })
|
|
1304
1410
|
] })
|
|
@@ -1465,103 +1571,6 @@ function useFullscreenStatus() {
|
|
|
1465
1571
|
}, []);
|
|
1466
1572
|
return isFullscreen;
|
|
1467
1573
|
}
|
|
1468
|
-
const Info = ({ children }) => {
|
|
1469
|
-
const dialogRef = A(null);
|
|
1470
|
-
const toggleHelp = () => {
|
|
1471
|
-
var _a;
|
|
1472
|
-
(_a = dialogRef.current) == null ? void 0 : _a.showModal();
|
|
1473
|
-
};
|
|
1474
|
-
return /* @__PURE__ */ u$1("div", { className: "relative", children: [
|
|
1475
|
-
/* @__PURE__ */ u$1("button", { type: "button", className: "btn btn-xs", onClick: toggleHelp, children: "?" }),
|
|
1476
|
-
/* @__PURE__ */ u$1("dialog", { ref: dialogRef, className: "modal modal-bottom sm:modal-middle", children: [
|
|
1477
|
-
/* @__PURE__ */ u$1("div", { className: "modal-box sm:max-w-5xl", children: [
|
|
1478
|
-
/* @__PURE__ */ u$1("form", { method: "dialog", children: /* @__PURE__ */ u$1("button", { className: "btn btn-sm btn-circle btn-ghost absolute right-2 top-2", children: "✕" }) }),
|
|
1479
|
-
/* @__PURE__ */ u$1("div", { className: "flex flex-col", children }),
|
|
1480
|
-
/* @__PURE__ */ u$1("div", { className: "modal-action", children: /* @__PURE__ */ u$1("form", { method: "dialog", children: /* @__PURE__ */ u$1("button", { className: "float-right underline text-sm hover:text-blue-700 mr-2", children: "Close" }) }) })
|
|
1481
|
-
] }),
|
|
1482
|
-
/* @__PURE__ */ u$1("form", { method: "dialog", className: "modal-backdrop", children: /* @__PURE__ */ u$1("button", { children: "Helper to close when clicked outside" }) })
|
|
1483
|
-
] })
|
|
1484
|
-
] });
|
|
1485
|
-
};
|
|
1486
|
-
const InfoHeadline1 = ({ children }) => {
|
|
1487
|
-
return /* @__PURE__ */ u$1("h1", { className: "text-lg font-bold", children });
|
|
1488
|
-
};
|
|
1489
|
-
const InfoHeadline2 = ({ children }) => {
|
|
1490
|
-
return /* @__PURE__ */ u$1("h2", { className: "text-base font-bold mt-4", children });
|
|
1491
|
-
};
|
|
1492
|
-
const InfoParagraph = ({ children }) => {
|
|
1493
|
-
return /* @__PURE__ */ u$1("p", { className: "text-justify my-1", children });
|
|
1494
|
-
};
|
|
1495
|
-
const InfoLink = ({ children, href }) => {
|
|
1496
|
-
return /* @__PURE__ */ u$1("a", { className: "text-blue-600 hover:text-blue-800", href, target: "_blank", rel: "noopener noreferrer", children });
|
|
1497
|
-
};
|
|
1498
|
-
const InfoComponentCode = ({ componentName, params, lapisUrl }) => {
|
|
1499
|
-
const componentCode = componentParametersToCode(componentName, params, lapisUrl);
|
|
1500
|
-
const codePenData = {
|
|
1501
|
-
title: "GenSpectrum dashboard component",
|
|
1502
|
-
html: generateFullExampleCode(componentCode, componentName),
|
|
1503
|
-
layout: "left",
|
|
1504
|
-
editors: "100"
|
|
1505
|
-
};
|
|
1506
|
-
return /* @__PURE__ */ u$1(Fragment, { children: [
|
|
1507
|
-
/* @__PURE__ */ u$1(InfoHeadline2, { children: "Use this component yourself" }),
|
|
1508
|
-
/* @__PURE__ */ u$1(InfoParagraph, { children: [
|
|
1509
|
-
"This component was created using the following parameters:",
|
|
1510
|
-
/* @__PURE__ */ u$1("div", { className: "p-4 border border-gray-200 rounded-lg overflow-x-auto", children: /* @__PURE__ */ u$1("pre", { children: /* @__PURE__ */ u$1("code", { children: componentCode }) }) })
|
|
1511
|
-
] }),
|
|
1512
|
-
/* @__PURE__ */ u$1(InfoParagraph, { children: [
|
|
1513
|
-
"You can add this component to your own website using the",
|
|
1514
|
-
" ",
|
|
1515
|
-
/* @__PURE__ */ u$1(InfoLink, { href: "https://github.com/GenSpectrum/dashboard-components", children: "GenSpectrum dashboard components library" }),
|
|
1516
|
-
" ",
|
|
1517
|
-
"and the code from above."
|
|
1518
|
-
] }),
|
|
1519
|
-
/* @__PURE__ */ u$1(InfoParagraph, { children: /* @__PURE__ */ u$1("form", { action: "https://codepen.io/pen/define", method: "POST", target: "_blank", children: [
|
|
1520
|
-
/* @__PURE__ */ u$1(
|
|
1521
|
-
"input",
|
|
1522
|
-
{
|
|
1523
|
-
type: "hidden",
|
|
1524
|
-
name: "data",
|
|
1525
|
-
value: JSON.stringify(codePenData).replace(/"/g, """).replace(/'/g, "'")
|
|
1526
|
-
}
|
|
1527
|
-
),
|
|
1528
|
-
/* @__PURE__ */ u$1("button", { className: "text-blue-600 hover:text-blue-800", type: "submit", children: "Click here to try it out on CodePen." })
|
|
1529
|
-
] }) })
|
|
1530
|
-
] });
|
|
1531
|
-
};
|
|
1532
|
-
function componentParametersToCode(componentName, params, lapisUrl) {
|
|
1533
|
-
const stringifyIfNeeded = (value) => {
|
|
1534
|
-
return typeof value === "object" ? JSON.stringify(value) : value;
|
|
1535
|
-
};
|
|
1536
|
-
const attributes = indentLines(
|
|
1537
|
-
Object.entries(params).map(([key, value]) => `${key}='${stringifyIfNeeded(value)}'`).join("\n"),
|
|
1538
|
-
4
|
|
1539
|
-
);
|
|
1540
|
-
return `<gs-app lapis="${lapisUrl}">
|
|
1541
|
-
<gs-${componentName}
|
|
1542
|
-
${attributes}
|
|
1543
|
-
/>
|
|
1544
|
-
</gs-app>`;
|
|
1545
|
-
}
|
|
1546
|
-
function generateFullExampleCode(componentCode, componentName) {
|
|
1547
|
-
const storyBookPath = `/docs/visualization-${componentName}--docs`;
|
|
1548
|
-
return `<html>
|
|
1549
|
-
<head>
|
|
1550
|
-
<script type="module" src="https://unpkg.com/@genspectrum/dashboard-components@latest/standalone-bundle/dashboard-components.js"><\/script>
|
|
1551
|
-
<link rel="stylesheet" href="https://unpkg.com/@genspectrum/dashboard-components@latest/dist/style.css" />
|
|
1552
|
-
</head>
|
|
1553
|
-
|
|
1554
|
-
<body>
|
|
1555
|
-
<!-- Component documentation: https://genspectrum.github.io/dashboard-components/?path=${storyBookPath} -->
|
|
1556
|
-
${indentLines(componentCode, 2)}
|
|
1557
|
-
</body>
|
|
1558
|
-
</html>
|
|
1559
|
-
`;
|
|
1560
|
-
}
|
|
1561
|
-
function indentLines(text, numberSpaces) {
|
|
1562
|
-
const spaces = " ".repeat(numberSpaces);
|
|
1563
|
-
return text.split("\n").map((line) => spaces + line).join("\n");
|
|
1564
|
-
}
|
|
1565
1574
|
const LoadingDisplay = () => {
|
|
1566
1575
|
return /* @__PURE__ */ u$1(
|
|
1567
1576
|
"div",
|
|
@@ -2028,7 +2037,7 @@ function useQuery(fetchDataCallback, dependencies) {
|
|
|
2028
2037
|
setIsLoading(false);
|
|
2029
2038
|
}
|
|
2030
2039
|
};
|
|
2031
|
-
fetchData();
|
|
2040
|
+
void fetchData();
|
|
2032
2041
|
}, [JSON.stringify(dependencies)]);
|
|
2033
2042
|
if (isLoading) {
|
|
2034
2043
|
return { isLoading: true };
|
|
@@ -2284,7 +2293,7 @@ const tailwindStyle = `*, ::before, ::after {
|
|
|
2284
2293
|
--tw-contain-paint: ;
|
|
2285
2294
|
--tw-contain-style: ;
|
|
2286
2295
|
}/*
|
|
2287
|
-
! tailwindcss v3.4.
|
|
2296
|
+
! tailwindcss v3.4.17 | MIT License | https://tailwindcss.com
|
|
2288
2297
|
*//*
|
|
2289
2298
|
1. Prevent padding and border from affecting element width. (https://github.com/mozdevs/cssremedy/issues/4)
|
|
2290
2299
|
2. Allow adding a border to an element by just adding a border-width. (https://github.com/tailwindcss/tailwindcss/pull/116)
|
|
@@ -2956,6 +2965,7 @@ html {
|
|
|
2956
2965
|
display: grid;
|
|
2957
2966
|
width: 100%;
|
|
2958
2967
|
overflow: hidden;
|
|
2968
|
+
direction: ltr;
|
|
2959
2969
|
container-type: inline-size;
|
|
2960
2970
|
grid-template-columns: auto 1fr;
|
|
2961
2971
|
}
|
|
@@ -4486,9 +4496,6 @@ input.tab:checked + .tab-content,
|
|
|
4486
4496
|
border-end-end-radius: inherit;
|
|
4487
4497
|
border-start-end-radius: inherit;
|
|
4488
4498
|
}
|
|
4489
|
-
.modal-middle {
|
|
4490
|
-
place-items: center;
|
|
4491
|
-
}
|
|
4492
4499
|
.modal-bottom {
|
|
4493
4500
|
place-items: end;
|
|
4494
4501
|
}
|
|
@@ -4933,9 +4940,6 @@ input.tab:checked + .tab-content,
|
|
|
4933
4940
|
.min-w-\\[7\\.5rem\\] {
|
|
4934
4941
|
min-width: 7.5rem;
|
|
4935
4942
|
}
|
|
4936
|
-
.max-w-3xl {
|
|
4937
|
-
max-width: 48rem;
|
|
4938
|
-
}
|
|
4939
4943
|
.max-w-screen-lg {
|
|
4940
4944
|
max-width: 1024px;
|
|
4941
4945
|
}
|
|
@@ -5109,10 +5113,6 @@ input.tab:checked + .tab-content,
|
|
|
5109
5113
|
padding-top: 0.5rem;
|
|
5110
5114
|
padding-bottom: 0.5rem;
|
|
5111
5115
|
}
|
|
5112
|
-
.py-4 {
|
|
5113
|
-
padding-top: 1rem;
|
|
5114
|
-
padding-bottom: 1rem;
|
|
5115
|
-
}
|
|
5116
5116
|
.pl-2 {
|
|
5117
5117
|
padding-left: 0.5rem;
|
|
5118
5118
|
}
|
|
@@ -8327,7 +8327,7 @@ const AggregateInner = (componentProps) => {
|
|
|
8327
8327
|
field: initialSortField,
|
|
8328
8328
|
direction: initialSortDirection
|
|
8329
8329
|
});
|
|
8330
|
-
}, [lapisFilter, fields, lapis]);
|
|
8330
|
+
}, [lapisFilter, fields, lapis, initialSortField, initialSortDirection]);
|
|
8331
8331
|
if (isLoading) {
|
|
8332
8332
|
return /* @__PURE__ */ u$1(LoadingDisplay, {});
|
|
8333
8333
|
}
|
|
@@ -10101,62 +10101,12 @@ svg.leaflet-image-layer.leaflet-interactive path {\r
|
|
|
10101
10101
|
}\r
|
|
10102
10102
|
`;
|
|
10103
10103
|
const leafletStyleModifications = ".leaflet-container {\n background: transparent;\n}\n";
|
|
10104
|
-
const mapSourceSchema = z$1.object({
|
|
10105
|
-
type: z$1.literal("topojson"),
|
|
10106
|
-
url: z$1.string().min(1),
|
|
10107
|
-
topologyObjectsKey: z$1.string().min(1)
|
|
10108
|
-
});
|
|
10109
|
-
function useGeoJsonMap(mapSource) {
|
|
10110
|
-
const {
|
|
10111
|
-
data: geojsonData,
|
|
10112
|
-
error,
|
|
10113
|
-
isLoading
|
|
10114
|
-
} = useQuery(async () => {
|
|
10115
|
-
switch (mapSource.type) {
|
|
10116
|
-
case "topojson":
|
|
10117
|
-
return await loadTopojsonMap(mapSource);
|
|
10118
|
-
}
|
|
10119
|
-
}, [mapSource]);
|
|
10120
|
-
if (isLoading) {
|
|
10121
|
-
return { isLoading };
|
|
10122
|
-
}
|
|
10123
|
-
if (error) {
|
|
10124
|
-
throw error;
|
|
10125
|
-
}
|
|
10126
|
-
return { geojsonData, isLoading: false };
|
|
10127
|
-
}
|
|
10128
|
-
async function loadTopojsonMap(mapSource) {
|
|
10129
|
-
var _a;
|
|
10130
|
-
const response = await fetch(mapSource.url);
|
|
10131
|
-
const topology = await response.json();
|
|
10132
|
-
if ((topology == null ? void 0 : topology.type) !== "Topology") {
|
|
10133
|
-
throw new UserFacingError(
|
|
10134
|
-
"Invalid map source",
|
|
10135
|
-
`JSON downloaded from ${mapSource.url} does not look like a topojson Topology definition: missing 'type: "Topology"', got '${JSON.stringify(topology).substring(0, 100)}'`
|
|
10136
|
-
);
|
|
10137
|
-
}
|
|
10138
|
-
const object = topology == null ? void 0 : topology.objects[mapSource.topologyObjectsKey];
|
|
10139
|
-
if ((object == null ? void 0 : object.type) !== "GeometryCollection") {
|
|
10140
|
-
throw new UserFacingError(
|
|
10141
|
-
"Invalid map source",
|
|
10142
|
-
`JSON downloaded from ${mapSource.url} does not have a GeometryCollection at key objects.${mapSource.topologyObjectsKey}, got '${(_a = JSON.stringify(topology)) == null ? void 0 : _a.substring(0, 100)}'`
|
|
10143
|
-
);
|
|
10144
|
-
}
|
|
10145
|
-
return topojson.feature(topology, object);
|
|
10146
|
-
}
|
|
10147
10104
|
const SequencesByLocationMap = ({
|
|
10148
|
-
|
|
10149
|
-
|
|
10150
|
-
|
|
10151
|
-
|
|
10152
|
-
|
|
10153
|
-
return /* @__PURE__ */ u$1(LoadingDisplay, {});
|
|
10154
|
-
}
|
|
10155
|
-
return /* @__PURE__ */ u$1(SequencesByLocationMapInner$1, { geojsonData, ...otherProps });
|
|
10156
|
-
};
|
|
10157
|
-
const SequencesByLocationMapInner$1 = ({
|
|
10158
|
-
geojsonData,
|
|
10159
|
-
locationData,
|
|
10105
|
+
locations,
|
|
10106
|
+
totalCount,
|
|
10107
|
+
countOfMatchedLocationData,
|
|
10108
|
+
nullCount,
|
|
10109
|
+
unmatchedLocations,
|
|
10160
10110
|
enableMapNavigation,
|
|
10161
10111
|
lapisLocationField,
|
|
10162
10112
|
zoom,
|
|
@@ -10164,22 +10114,7 @@ const SequencesByLocationMapInner$1 = ({
|
|
|
10164
10114
|
offsetY,
|
|
10165
10115
|
hasTableView
|
|
10166
10116
|
}) => {
|
|
10167
|
-
var _a;
|
|
10168
10117
|
const ref = A(null);
|
|
10169
|
-
const { locations, totalCount, countOfMatchedLocationData, unmatchedLocations } = T(() => {
|
|
10170
|
-
const countAndProportionByCountry = buildLookupByLocationField(locationData, lapisLocationField);
|
|
10171
|
-
const { locations: locations2, unmatchedLocations: unmatchedLocations2 } = matchLocationDataAndGeoJsonFeatures(
|
|
10172
|
-
geojsonData,
|
|
10173
|
-
countAndProportionByCountry,
|
|
10174
|
-
lapisLocationField
|
|
10175
|
-
);
|
|
10176
|
-
const totalCount2 = locationData.map((value) => value.count).reduce((sum, b3) => sum + b3, 0);
|
|
10177
|
-
const countOfMatchedLocationData2 = locations2.map((location) => {
|
|
10178
|
-
var _a2;
|
|
10179
|
-
return ((_a2 = location.properties.data) == null ? void 0 : _a2.count) ?? 0;
|
|
10180
|
-
}).reduce((sum, b3) => sum + b3, 0);
|
|
10181
|
-
return { locations: locations2, totalCount: totalCount2, countOfMatchedLocationData: countOfMatchedLocationData2, unmatchedLocations: unmatchedLocations2 };
|
|
10182
|
-
}, [geojsonData, locationData, lapisLocationField]);
|
|
10183
10118
|
y(() => {
|
|
10184
10119
|
if (!ref.current) {
|
|
10185
10120
|
return;
|
|
@@ -10195,11 +10130,11 @@ const SequencesByLocationMapInner$1 = ({
|
|
|
10195
10130
|
});
|
|
10196
10131
|
Leaflet.geoJson(locations, {
|
|
10197
10132
|
style: (feature) => {
|
|
10198
|
-
var
|
|
10133
|
+
var _a;
|
|
10199
10134
|
return {
|
|
10200
|
-
fillColor: getColor((
|
|
10135
|
+
fillColor: getColor((_a = feature == null ? void 0 : feature.properties.data) == null ? void 0 : _a.proportion),
|
|
10201
10136
|
fillOpacity: 1,
|
|
10202
|
-
color: "
|
|
10137
|
+
color: "#666666",
|
|
10203
10138
|
weight: 1
|
|
10204
10139
|
};
|
|
10205
10140
|
}
|
|
@@ -10208,7 +10143,6 @@ const SequencesByLocationMapInner$1 = ({
|
|
|
10208
10143
|
leafletMap.remove();
|
|
10209
10144
|
};
|
|
10210
10145
|
}, [ref, locations, enableMapNavigation, lapisLocationField, zoom, offsetX, offsetY]);
|
|
10211
|
-
const nullCount = ((_a = locationData.find((row) => row[lapisLocationField] === null)) == null ? void 0 : _a.count) ?? 0;
|
|
10212
10146
|
return /* @__PURE__ */ u$1("div", { className: "h-full", children: [
|
|
10213
10147
|
/* @__PURE__ */ u$1("div", { ref, className: "h-full" }),
|
|
10214
10148
|
/* @__PURE__ */ u$1("div", { className: "relative", children: /* @__PURE__ */ u$1(
|
|
@@ -10230,7 +10164,7 @@ const DataMatchInformation = ({
|
|
|
10230
10164
|
nullCount,
|
|
10231
10165
|
hasTableView
|
|
10232
10166
|
}) => {
|
|
10233
|
-
const
|
|
10167
|
+
const modalRef = useModalRef();
|
|
10234
10168
|
const proportion = formatProportion(countOfMatchedLocationData / totalCount);
|
|
10235
10169
|
return /* @__PURE__ */ u$1(Fragment, { children: [
|
|
10236
10170
|
/* @__PURE__ */ u$1(
|
|
@@ -10238,7 +10172,7 @@ const DataMatchInformation = ({
|
|
|
10238
10172
|
{
|
|
10239
10173
|
onClick: () => {
|
|
10240
10174
|
var _a;
|
|
10241
|
-
return (_a =
|
|
10175
|
+
return (_a = modalRef.current) == null ? void 0 : _a.showModal();
|
|
10242
10176
|
},
|
|
10243
10177
|
className: "text-sm absolute bottom-0 px-1 z-[1001] bg-white rounded border cursor-pointer tooltip",
|
|
10244
10178
|
"data-tip": "Click for detailed information",
|
|
@@ -10249,76 +10183,34 @@ const DataMatchInformation = ({
|
|
|
10249
10183
|
]
|
|
10250
10184
|
}
|
|
10251
10185
|
),
|
|
10252
|
-
/* @__PURE__ */ u$1(
|
|
10253
|
-
/* @__PURE__ */ u$1(
|
|
10254
|
-
|
|
10255
|
-
|
|
10256
|
-
|
|
10257
|
-
|
|
10258
|
-
|
|
10259
|
-
|
|
10260
|
-
|
|
10261
|
-
|
|
10262
|
-
") on locations on the map."
|
|
10263
|
-
] }),
|
|
10264
|
-
/* @__PURE__ */ u$1(InfoParagraph, { children: [
|
|
10265
|
-
unmatchedLocations.length > 0 && /* @__PURE__ */ u$1(Fragment, { children: [
|
|
10266
|
-
"The following locations from the data could not be matched on the map:",
|
|
10267
|
-
" ",
|
|
10268
|
-
unmatchedLocations.map((it) => `"${it}"`).join(", "),
|
|
10269
|
-
".",
|
|
10270
|
-
" "
|
|
10271
|
-
] }),
|
|
10272
|
-
nullCount > 0 && `${nullCount.toLocaleString("en-us")} matching sequences have no location information. `,
|
|
10273
|
-
hasTableView && "You can check the table view for more detailed information."
|
|
10274
|
-
] }),
|
|
10275
|
-
/* @__PURE__ */ u$1("div", { className: "modal-action", children: /* @__PURE__ */ u$1("form", { method: "dialog", children: /* @__PURE__ */ u$1("button", { className: "float-right underline text-sm hover:text-blue-700 mr-2", children: "Close" }) }) })
|
|
10186
|
+
/* @__PURE__ */ u$1(Modal, { modalRef, children: [
|
|
10187
|
+
/* @__PURE__ */ u$1(InfoHeadline1, { children: "Sequences By Location - Map View" }),
|
|
10188
|
+
/* @__PURE__ */ u$1(InfoParagraph, { children: [
|
|
10189
|
+
"The current filter has matched ",
|
|
10190
|
+
totalCount.toLocaleString("en-us"),
|
|
10191
|
+
" sequences. From these sequences, we were able to match ",
|
|
10192
|
+
countOfMatchedLocationData.toLocaleString("en-us"),
|
|
10193
|
+
" (",
|
|
10194
|
+
proportion,
|
|
10195
|
+
") on locations on the map."
|
|
10276
10196
|
] }),
|
|
10277
|
-
/* @__PURE__ */ u$1(
|
|
10197
|
+
/* @__PURE__ */ u$1(InfoParagraph, { children: [
|
|
10198
|
+
unmatchedLocations.length > 0 && /* @__PURE__ */ u$1(Fragment, { children: [
|
|
10199
|
+
"The following locations from the data could not be matched on the map:",
|
|
10200
|
+
" ",
|
|
10201
|
+
unmatchedLocations.map((it) => `"${it}"`).join(", "),
|
|
10202
|
+
".",
|
|
10203
|
+
" "
|
|
10204
|
+
] }),
|
|
10205
|
+
nullCount > 0 && `${nullCount.toLocaleString("en-us")} matching sequences have no location information. `,
|
|
10206
|
+
hasTableView && "You can check the table view for more detailed information."
|
|
10207
|
+
] })
|
|
10278
10208
|
] })
|
|
10279
10209
|
] });
|
|
10280
10210
|
};
|
|
10281
|
-
function buildLookupByLocationField(locationData, lapisLocationField) {
|
|
10282
|
-
return new Map(
|
|
10283
|
-
locationData.filter((row) => typeof row[lapisLocationField] === "string").map((row) => [row[lapisLocationField], row])
|
|
10284
|
-
);
|
|
10285
|
-
}
|
|
10286
|
-
function matchLocationDataAndGeoJsonFeatures(geojsonData, countAndProportionByCountry, lapisLocationField) {
|
|
10287
|
-
const matchedLocations = [];
|
|
10288
|
-
const locations = geojsonData.features.map(
|
|
10289
|
-
(feature) => {
|
|
10290
|
-
var _a;
|
|
10291
|
-
const name = (_a = feature == null ? void 0 : feature.properties) == null ? void 0 : _a.name;
|
|
10292
|
-
if (typeof name !== "string") {
|
|
10293
|
-
throw new Error(
|
|
10294
|
-
`GeoJSON feature with id '${feature.id}' does not have 'properties.name' of type string, was: '${name}'`
|
|
10295
|
-
);
|
|
10296
|
-
}
|
|
10297
|
-
const data = countAndProportionByCountry.get(name) ?? null;
|
|
10298
|
-
if (data !== null) {
|
|
10299
|
-
matchedLocations.push(name);
|
|
10300
|
-
}
|
|
10301
|
-
return {
|
|
10302
|
-
...feature,
|
|
10303
|
-
properties: {
|
|
10304
|
-
...feature.properties,
|
|
10305
|
-
data
|
|
10306
|
-
}
|
|
10307
|
-
};
|
|
10308
|
-
}
|
|
10309
|
-
);
|
|
10310
|
-
const unmatchedLocations = [...countAndProportionByCountry.keys()].filter(
|
|
10311
|
-
(name) => !matchedLocations.includes(name)
|
|
10312
|
-
);
|
|
10313
|
-
if (unmatchedLocations.length > 0) {
|
|
10314
|
-
const unmatchedLocationsWarning = `gs-map: Found location data from LAPIS (aggregated by "${lapisLocationField}") that could not be matched on locations on the given map. Unmatched location names are: ${unmatchedLocations.map((it) => `"${it}"`).join(", ")}`;
|
|
10315
|
-
console.warn(unmatchedLocationsWarning);
|
|
10316
|
-
}
|
|
10317
|
-
return { locations, unmatchedLocations };
|
|
10318
|
-
}
|
|
10319
10211
|
function getColor(value) {
|
|
10320
10212
|
if (value === void 0) {
|
|
10321
|
-
return "#
|
|
10213
|
+
return "#DDDDDD";
|
|
10322
10214
|
}
|
|
10323
10215
|
const thresholds = [
|
|
10324
10216
|
{ limit: 0.4, color: "#662506" },
|
|
@@ -10369,12 +10261,150 @@ function p({ innerText, className = "" }) {
|
|
|
10369
10261
|
return headline;
|
|
10370
10262
|
}
|
|
10371
10263
|
const SequencesByLocationTable = ({
|
|
10372
|
-
|
|
10264
|
+
tableData,
|
|
10373
10265
|
lapisLocationField,
|
|
10374
10266
|
pageSize
|
|
10375
10267
|
}) => {
|
|
10376
|
-
|
|
10268
|
+
const headers = [
|
|
10269
|
+
{
|
|
10270
|
+
name: lapisLocationField,
|
|
10271
|
+
sort: {
|
|
10272
|
+
compare: compareAscending
|
|
10273
|
+
}
|
|
10274
|
+
},
|
|
10275
|
+
{
|
|
10276
|
+
name: "count",
|
|
10277
|
+
sort: true
|
|
10278
|
+
},
|
|
10279
|
+
{
|
|
10280
|
+
name: "proportion",
|
|
10281
|
+
sort: true,
|
|
10282
|
+
formatter: (cell) => formatProportion(cell)
|
|
10283
|
+
},
|
|
10284
|
+
..."isShownOnMap" in tableData[0] ? [{ id: "isShownOnMap", name: "shown on map", sort: true, width: "20%" }] : []
|
|
10285
|
+
];
|
|
10286
|
+
return /* @__PURE__ */ u$1(Table, { data: tableData, columns: headers, pageSize });
|
|
10287
|
+
};
|
|
10288
|
+
const MapLocationDataType = {
|
|
10289
|
+
tableDataOnly: "tableDataOnly",
|
|
10290
|
+
tableAndMapData: "tableAndMapData"
|
|
10377
10291
|
};
|
|
10292
|
+
function computeMapLocationData(locationData, geojsonData, lapisLocationField) {
|
|
10293
|
+
var _a;
|
|
10294
|
+
if (geojsonData === void 0) {
|
|
10295
|
+
return { type: MapLocationDataType.tableDataOnly, tableData: locationData };
|
|
10296
|
+
}
|
|
10297
|
+
const countAndProportionByCountry = buildLookupByLocationField(locationData, lapisLocationField);
|
|
10298
|
+
const { locations, unmatchedLocations } = matchLocationDataAndGeoJsonFeatures(
|
|
10299
|
+
geojsonData,
|
|
10300
|
+
countAndProportionByCountry,
|
|
10301
|
+
lapisLocationField
|
|
10302
|
+
);
|
|
10303
|
+
const totalCount = locationData.map((value) => value.count).reduce((sum, b3) => sum + b3, 0);
|
|
10304
|
+
const countOfMatchedLocationData = locations.map((location) => {
|
|
10305
|
+
var _a2;
|
|
10306
|
+
return ((_a2 = location.properties.data) == null ? void 0 : _a2.count) ?? 0;
|
|
10307
|
+
}).reduce((sum, b3) => sum + b3, 0);
|
|
10308
|
+
const nullCount = ((_a = locationData.find((row) => row[lapisLocationField] === null)) == null ? void 0 : _a.count) ?? 0;
|
|
10309
|
+
const tableData = getSequencesByLocationTableData(locationData, unmatchedLocations, lapisLocationField);
|
|
10310
|
+
return {
|
|
10311
|
+
type: MapLocationDataType.tableAndMapData,
|
|
10312
|
+
locations,
|
|
10313
|
+
tableData,
|
|
10314
|
+
totalCount,
|
|
10315
|
+
countOfMatchedLocationData,
|
|
10316
|
+
unmatchedLocations,
|
|
10317
|
+
nullCount
|
|
10318
|
+
};
|
|
10319
|
+
}
|
|
10320
|
+
function buildLookupByLocationField(locationData, lapisLocationField) {
|
|
10321
|
+
return new Map(
|
|
10322
|
+
locationData.filter((row) => typeof row[lapisLocationField] === "string").map((row) => [row[lapisLocationField], row])
|
|
10323
|
+
);
|
|
10324
|
+
}
|
|
10325
|
+
function matchLocationDataAndGeoJsonFeatures(geojsonData, countAndProportionByCountry, lapisLocationField) {
|
|
10326
|
+
const matchedLocations = [];
|
|
10327
|
+
const locations = geojsonData.features.map(
|
|
10328
|
+
(feature) => {
|
|
10329
|
+
var _a;
|
|
10330
|
+
const name = (_a = feature == null ? void 0 : feature.properties) == null ? void 0 : _a.name;
|
|
10331
|
+
if (typeof name !== "string") {
|
|
10332
|
+
throw new Error(
|
|
10333
|
+
`GeoJSON feature with id '${feature.id}' does not have 'properties.name' of type string, was: '${name}'`
|
|
10334
|
+
);
|
|
10335
|
+
}
|
|
10336
|
+
const data = countAndProportionByCountry.get(name) ?? null;
|
|
10337
|
+
if (data !== null) {
|
|
10338
|
+
matchedLocations.push(name);
|
|
10339
|
+
}
|
|
10340
|
+
return {
|
|
10341
|
+
...feature,
|
|
10342
|
+
properties: {
|
|
10343
|
+
...feature.properties,
|
|
10344
|
+
data
|
|
10345
|
+
}
|
|
10346
|
+
};
|
|
10347
|
+
}
|
|
10348
|
+
);
|
|
10349
|
+
const unmatchedLocations = [...countAndProportionByCountry.keys()].filter(
|
|
10350
|
+
(name) => !matchedLocations.includes(name)
|
|
10351
|
+
);
|
|
10352
|
+
if (unmatchedLocations.length > 0) {
|
|
10353
|
+
const unmatchedLocationsWarning = `gs-map: Found location data from LAPIS (aggregated by "${lapisLocationField}") that could not be matched on locations on the given map. Unmatched location names are: ${unmatchedLocations.map((it) => `"${it}"`).join(", ")}`;
|
|
10354
|
+
console.warn(unmatchedLocationsWarning);
|
|
10355
|
+
}
|
|
10356
|
+
return { locations, unmatchedLocations };
|
|
10357
|
+
}
|
|
10358
|
+
function getSequencesByLocationTableData(locationData, unmatchedLocations, lapisLocationField) {
|
|
10359
|
+
return locationData.map((row) => ({
|
|
10360
|
+
...row,
|
|
10361
|
+
isShownOnMap: `${isShownOnMap(row, unmatchedLocations, lapisLocationField)}`
|
|
10362
|
+
}));
|
|
10363
|
+
}
|
|
10364
|
+
function isShownOnMap(row, unmatchedLocations, lapisLocationField) {
|
|
10365
|
+
const locationValue = row[lapisLocationField];
|
|
10366
|
+
if (locationValue === null) {
|
|
10367
|
+
return false;
|
|
10368
|
+
}
|
|
10369
|
+
return !unmatchedLocations.includes(locationValue);
|
|
10370
|
+
}
|
|
10371
|
+
const mapSourceSchema = z$1.object({
|
|
10372
|
+
type: z$1.literal("topojson"),
|
|
10373
|
+
url: z$1.string().min(1),
|
|
10374
|
+
topologyObjectsKey: z$1.string().min(1)
|
|
10375
|
+
});
|
|
10376
|
+
async function loadMapSource(mapSource) {
|
|
10377
|
+
switch (mapSource.type) {
|
|
10378
|
+
case "topojson":
|
|
10379
|
+
return await loadTopojsonMap(mapSource);
|
|
10380
|
+
}
|
|
10381
|
+
}
|
|
10382
|
+
async function loadTopojsonMap(mapSource) {
|
|
10383
|
+
var _a;
|
|
10384
|
+
const response = await fetch(mapSource.url);
|
|
10385
|
+
const topology = await response.json();
|
|
10386
|
+
if ((topology == null ? void 0 : topology.type) !== "Topology") {
|
|
10387
|
+
throw new UserFacingError(
|
|
10388
|
+
"Invalid map source",
|
|
10389
|
+
`JSON downloaded from ${mapSource.url} does not look like a topojson Topology definition: missing 'type: "Topology"', got '${JSON.stringify(topology).substring(0, 100)}'`
|
|
10390
|
+
);
|
|
10391
|
+
}
|
|
10392
|
+
const object = topology == null ? void 0 : topology.objects[mapSource.topologyObjectsKey];
|
|
10393
|
+
if ((object == null ? void 0 : object.type) !== "GeometryCollection") {
|
|
10394
|
+
throw new UserFacingError(
|
|
10395
|
+
"Invalid map source",
|
|
10396
|
+
`JSON downloaded from ${mapSource.url} does not have a GeometryCollection at key objects.${mapSource.topologyObjectsKey}, got '${(_a = JSON.stringify(topology)) == null ? void 0 : _a.substring(0, 100)}'`
|
|
10397
|
+
);
|
|
10398
|
+
}
|
|
10399
|
+
return topojson.feature(topology, object);
|
|
10400
|
+
}
|
|
10401
|
+
async function querySequencesByLocationData(lapisFilter, lapisLocationField, lapis, mapSource) {
|
|
10402
|
+
const [locationData, geojsonData] = await Promise.all([
|
|
10403
|
+
queryAggregateData(lapisFilter, [lapisLocationField], lapis),
|
|
10404
|
+
mapSource !== void 0 ? loadMapSource(mapSource) : void 0
|
|
10405
|
+
]);
|
|
10406
|
+
return computeMapLocationData(locationData, geojsonData, lapisLocationField);
|
|
10407
|
+
}
|
|
10378
10408
|
const sequencesByLocationViewSchema = z$1.union([z$1.literal(views.map), z$1.literal(views.table)]);
|
|
10379
10409
|
const sequencesByLocationPropsSchema = z$1.object({
|
|
10380
10410
|
lapisFilter: lapisFilterSchema,
|
|
@@ -10395,15 +10425,15 @@ const SequencesByLocation = (componentProps) => {
|
|
|
10395
10425
|
return /* @__PURE__ */ u$1(ErrorBoundary, { size, componentProps, schema: sequencesByLocationPropsSchema, children: /* @__PURE__ */ u$1(ResizeContainer, { size, children: /* @__PURE__ */ u$1(SequencesByLocationMapInner, { ...componentProps }) }) });
|
|
10396
10426
|
};
|
|
10397
10427
|
const SequencesByLocationMapInner = (props) => {
|
|
10398
|
-
const { lapisFilter, lapisLocationField } = props;
|
|
10428
|
+
const { lapisFilter, lapisLocationField, mapSource } = props;
|
|
10399
10429
|
const lapis = x(LapisUrlContext);
|
|
10400
10430
|
const {
|
|
10401
10431
|
data,
|
|
10402
10432
|
error,
|
|
10403
10433
|
isLoading: isLoadingLapisData
|
|
10404
10434
|
} = useQuery(
|
|
10405
|
-
async () =>
|
|
10406
|
-
[lapisFilter, lapisLocationField, lapis]
|
|
10435
|
+
async () => querySequencesByLocationData(lapisFilter, lapisLocationField, lapis, mapSource),
|
|
10436
|
+
[lapisFilter, lapisLocationField, lapis, mapSource]
|
|
10407
10437
|
);
|
|
10408
10438
|
if (isLoadingLapisData) {
|
|
10409
10439
|
return /* @__PURE__ */ u$1(LoadingDisplay, {});
|
|
@@ -10419,17 +10449,17 @@ const SequencesByLocationMapTabs = ({
|
|
|
10419
10449
|
}) => {
|
|
10420
10450
|
const getTab = (view) => {
|
|
10421
10451
|
switch (view) {
|
|
10422
|
-
case views.map:
|
|
10423
|
-
if (
|
|
10452
|
+
case views.map: {
|
|
10453
|
+
if (data.type !== MapLocationDataType.tableAndMapData) {
|
|
10424
10454
|
throw new Error("mapSource is required when using the map view");
|
|
10425
10455
|
}
|
|
10456
|
+
const { type: _type, tableData: _tableData, ...dataForMap } = data;
|
|
10426
10457
|
return {
|
|
10427
10458
|
title: "Map",
|
|
10428
10459
|
content: /* @__PURE__ */ u$1(
|
|
10429
10460
|
SequencesByLocationMap,
|
|
10430
10461
|
{
|
|
10431
|
-
|
|
10432
|
-
mapSource: originalComponentProps.mapSource,
|
|
10462
|
+
...dataForMap,
|
|
10433
10463
|
enableMapNavigation: originalComponentProps.enableMapNavigation,
|
|
10434
10464
|
lapisLocationField: originalComponentProps.lapisLocationField,
|
|
10435
10465
|
zoom: originalComponentProps.zoom,
|
|
@@ -10439,13 +10469,14 @@ const SequencesByLocationMapTabs = ({
|
|
|
10439
10469
|
}
|
|
10440
10470
|
)
|
|
10441
10471
|
};
|
|
10472
|
+
}
|
|
10442
10473
|
case views.table:
|
|
10443
10474
|
return {
|
|
10444
10475
|
title: "Table",
|
|
10445
10476
|
content: /* @__PURE__ */ u$1(
|
|
10446
10477
|
SequencesByLocationTable,
|
|
10447
10478
|
{
|
|
10448
|
-
|
|
10479
|
+
tableData: data.tableData,
|
|
10449
10480
|
lapisLocationField: originalComponentProps.lapisLocationField,
|
|
10450
10481
|
pageSize: originalComponentProps.pageSize
|
|
10451
10482
|
}
|
|
@@ -10454,10 +10485,24 @@ const SequencesByLocationMapTabs = ({
|
|
|
10454
10485
|
}
|
|
10455
10486
|
};
|
|
10456
10487
|
const tabs = originalComponentProps.views.map((view) => getTab(view));
|
|
10457
|
-
return /* @__PURE__ */ u$1(
|
|
10488
|
+
return /* @__PURE__ */ u$1(
|
|
10489
|
+
Tabs,
|
|
10490
|
+
{
|
|
10491
|
+
tabs,
|
|
10492
|
+
toolbar: /* @__PURE__ */ u$1(Toolbar, { originalComponentProps, tableData: data.tableData })
|
|
10493
|
+
}
|
|
10494
|
+
);
|
|
10458
10495
|
};
|
|
10459
|
-
const Toolbar = ({ originalComponentProps }) => {
|
|
10496
|
+
const Toolbar = ({ originalComponentProps, tableData }) => {
|
|
10460
10497
|
return /* @__PURE__ */ u$1("div", { class: "flex flex-row", children: [
|
|
10498
|
+
/* @__PURE__ */ u$1(
|
|
10499
|
+
CsvDownloadButton,
|
|
10500
|
+
{
|
|
10501
|
+
className: "mx-1 btn btn-xs",
|
|
10502
|
+
getData: () => tableData,
|
|
10503
|
+
filename: "sequences_by_location.csv"
|
|
10504
|
+
}
|
|
10505
|
+
),
|
|
10461
10506
|
/* @__PURE__ */ u$1(SequencesByLocationMapInfo, { originalComponentProps }),
|
|
10462
10507
|
/* @__PURE__ */ u$1(Fullscreen, {})
|
|
10463
10508
|
] });
|