@genspectrum/dashboard-components 0.6.11 → 0.6.13

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.
Files changed (35) hide show
  1. package/dist/dashboard-components.js +1079 -873
  2. package/dist/dashboard-components.js.map +1 -1
  3. package/dist/genspectrum-components.d.ts +3 -3
  4. package/dist/style.css +117 -24
  5. package/package.json +2 -2
  6. package/src/preact/components/checkbox-selector.stories.tsx +93 -11
  7. package/src/preact/components/checkbox-selector.tsx +19 -0
  8. package/src/preact/components/color-scale-selector-dropdown.tsx +5 -3
  9. package/src/preact/components/dropdown.tsx +3 -3
  10. package/src/preact/components/loading-display.tsx +8 -1
  11. package/src/preact/components/mutation-type-selector.stories.tsx +115 -0
  12. package/src/preact/components/mutation-type-selector.tsx +33 -8
  13. package/src/preact/components/percent-input.stories.tsx +93 -0
  14. package/src/preact/components/percent-intput.tsx +4 -0
  15. package/src/preact/components/proportion-selector-dropdown.stories.tsx +2 -2
  16. package/src/preact/components/proportion-selector-dropdown.tsx +9 -7
  17. package/src/preact/components/proportion-selector.stories.tsx +4 -4
  18. package/src/preact/components/proportion-selector.tsx +46 -12
  19. package/src/preact/components/segment-selector.stories.tsx +151 -0
  20. package/src/preact/components/{SegmentSelector.tsx → segment-selector.tsx} +29 -20
  21. package/src/preact/mutationComparison/mutation-comparison.stories.tsx +1 -1
  22. package/src/preact/mutationComparison/mutation-comparison.tsx +1 -1
  23. package/src/preact/mutationComparison/queryMutationData.ts +1 -1
  24. package/src/preact/mutations/mutations-grid.tsx +5 -1
  25. package/src/preact/mutations/mutations.tsx +1 -1
  26. package/src/preact/mutations/queryMutations.ts +1 -1
  27. package/src/preact/mutationsOverTime/getFilteredMutationsOverTime.spec.ts +4 -4
  28. package/src/preact/mutationsOverTime/getFilteredMutationsOverTimeData.ts +3 -2
  29. package/src/preact/mutationsOverTime/mutations-over-time.tsx +1 -1
  30. package/src/preact/numberSequencesOverTime/number-sequences-over-time.tsx +3 -2
  31. package/src/preact/useQuery.ts +1 -1
  32. package/src/query/queryMutationsOverTime.ts +3 -3
  33. package/src/utils/map2d.spec.ts +83 -22
  34. package/src/utils/map2d.ts +158 -0
  35. package/src/utils/Map2d.ts +0 -75
@@ -25877,6 +25877,249 @@ function filterBySegmentAndMutationType(data, displayedSegments, displayedMutati
25877
25877
  return data.filter(byDisplayedSegments).filter(byDisplayedMutationTypes);
25878
25878
  }
25879
25879
  const LapisUrlContext = G$1("");
25880
+ const CsvDownloadButton = ({
25881
+ label = "Download",
25882
+ filename = "data.csv",
25883
+ getData,
25884
+ className
25885
+ }) => {
25886
+ const download = () => {
25887
+ const content = getDownloadContent();
25888
+ const blob = new Blob([content], { type: "text/csv" });
25889
+ const url = URL.createObjectURL(blob);
25890
+ const a2 = document.createElement("a");
25891
+ a2.href = url;
25892
+ a2.download = filename;
25893
+ a2.click();
25894
+ URL.revokeObjectURL(url);
25895
+ };
25896
+ const getDownloadContent = () => {
25897
+ const data = getData();
25898
+ const keys = getDataKeys(data);
25899
+ const header = `${keys.join(",")}
25900
+ `;
25901
+ const rows = data.map((row) => keys.map((key) => row[key]).join(",")).join("\n");
25902
+ return header + rows;
25903
+ };
25904
+ const getDataKeys = (data) => {
25905
+ const keysSet = data.map((row) => Object.keys(row)).reduce((accumulatedKeys, keys) => {
25906
+ keys.forEach((key) => accumulatedKeys.add(key));
25907
+ return accumulatedKeys;
25908
+ }, /* @__PURE__ */ new Set());
25909
+ return [...keysSet];
25910
+ };
25911
+ return /* @__PURE__ */ u$2("button", { className, onClick: download, children: label });
25912
+ };
25913
+ class UserFacingError extends Error {
25914
+ constructor(headline, message) {
25915
+ super(message);
25916
+ this.headline = headline;
25917
+ this.name = "UserFacingError";
25918
+ }
25919
+ }
25920
+ const ErrorDisplay = ({ error }) => {
25921
+ console.error(error);
25922
+ const ref = A$1(null);
25923
+ return /* @__PURE__ */ u$2("div", { className: "h-full w-full rounded-md border-2 border-gray-100 p-2 flex items-center justify-center flex-col", children: [
25924
+ /* @__PURE__ */ u$2("div", { className: "text-red-700 font-bold", children: "Error" }),
25925
+ /* @__PURE__ */ u$2("div", { children: [
25926
+ "Oops! Something went wrong.",
25927
+ error instanceof UserFacingError && /* @__PURE__ */ u$2(k$1, { children: [
25928
+ " ",
25929
+ /* @__PURE__ */ u$2(
25930
+ "button",
25931
+ {
25932
+ className: "text-sm text-gray-600 hover:text-gray-300",
25933
+ onClick: () => {
25934
+ var _a2;
25935
+ return (_a2 = ref.current) == null ? void 0 : _a2.showModal();
25936
+ },
25937
+ children: "Show details."
25938
+ }
25939
+ ),
25940
+ /* @__PURE__ */ u$2("dialog", { ref, class: "modal", children: [
25941
+ /* @__PURE__ */ u$2("div", { class: "modal-box", children: [
25942
+ /* @__PURE__ */ u$2("form", { method: "dialog", children: /* @__PURE__ */ u$2("button", { className: "btn btn-sm btn-circle btn-ghost absolute right-2 top-2", children: "✕" }) }),
25943
+ /* @__PURE__ */ u$2("h1", { class: "text-lg", children: error.headline }),
25944
+ /* @__PURE__ */ u$2("p", { class: "py-4", children: error.message })
25945
+ ] }),
25946
+ /* @__PURE__ */ u$2("form", { method: "dialog", class: "modal-backdrop", children: /* @__PURE__ */ u$2("button", { children: "close" }) })
25947
+ ] })
25948
+ ] })
25949
+ ] })
25950
+ ] });
25951
+ };
25952
+ const ResizeContainer = ({ children, size }) => {
25953
+ return /* @__PURE__ */ u$2("div", { style: size, className: "bg-white", children });
25954
+ };
25955
+ const ErrorBoundary = ({ size, children }) => {
25956
+ const [internalError] = b$1();
25957
+ if (internalError) {
25958
+ return /* @__PURE__ */ u$2(ResizeContainer, { size, children: /* @__PURE__ */ u$2(ErrorDisplay, { error: internalError }) });
25959
+ }
25960
+ return /* @__PURE__ */ u$2(k$1, { children });
25961
+ };
25962
+ const Fullscreen = () => {
25963
+ const element = A$1(null);
25964
+ const isFullscreen = useFullscreenStatus();
25965
+ return /* @__PURE__ */ u$2(
25966
+ "button",
25967
+ {
25968
+ ref: element,
25969
+ onClick: async () => {
25970
+ if (element.current) {
25971
+ if (isFullscreen) {
25972
+ await document.exitFullscreen();
25973
+ } else {
25974
+ const componentRoot = findComponentRoot(element.current);
25975
+ if (componentRoot) {
25976
+ await componentRoot.requestFullscreen();
25977
+ }
25978
+ }
25979
+ }
25980
+ },
25981
+ className: `mt-0.5 iconify text-2xl ${isFullscreen ? "mdi--fullscreen-exit hover:scale-90" : "mdi--fullscreen hover:scale-110"}`,
25982
+ title: isFullscreen ? "Exit fullscreen" : "Enter fullscreen"
25983
+ }
25984
+ );
25985
+ };
25986
+ function findComponentRoot(element) {
25987
+ var _a2;
25988
+ return (_a2 = findShadowRoot(element)) == null ? void 0 : _a2.children[0];
25989
+ }
25990
+ function findShadowRoot(element) {
25991
+ let current = element;
25992
+ while (current) {
25993
+ if (current instanceof ShadowRoot) {
25994
+ return current;
25995
+ }
25996
+ if (current.parentNode === null) {
25997
+ return null;
25998
+ }
25999
+ current = current.parentNode;
26000
+ }
26001
+ return null;
26002
+ }
26003
+ function useFullscreenStatus() {
26004
+ const [isFullscreen, setIsFullscreen] = h$1(document.fullscreenElement !== null);
26005
+ y$1(() => {
26006
+ const handleFullscreenChange = () => {
26007
+ setIsFullscreen(document.fullscreenElement !== null);
26008
+ };
26009
+ document.addEventListener("fullscreenchange", handleFullscreenChange);
26010
+ return () => {
26011
+ document.removeEventListener("fullscreenchange", handleFullscreenChange);
26012
+ };
26013
+ }, []);
26014
+ return isFullscreen;
26015
+ }
26016
+ const Info = ({ children }) => {
26017
+ const dialogRef = A$1(null);
26018
+ const toggleHelp = () => {
26019
+ var _a2;
26020
+ (_a2 = dialogRef.current) == null ? void 0 : _a2.showModal();
26021
+ };
26022
+ return /* @__PURE__ */ u$2("div", { className: "relative", children: [
26023
+ /* @__PURE__ */ u$2("button", { type: "button", className: "btn btn-xs", onClick: toggleHelp, children: "?" }),
26024
+ /* @__PURE__ */ u$2("dialog", { ref: dialogRef, className: "modal modal-bottom sm:modal-middle", children: [
26025
+ /* @__PURE__ */ u$2("div", { className: "modal-box sm:max-w-5xl", children: [
26026
+ /* @__PURE__ */ u$2("form", { method: "dialog", children: /* @__PURE__ */ u$2("button", { className: "btn btn-sm btn-circle btn-ghost absolute right-2 top-2", children: "✕" }) }),
26027
+ /* @__PURE__ */ u$2("div", { className: "flex flex-col", children }),
26028
+ /* @__PURE__ */ u$2("div", { className: "modal-action", children: /* @__PURE__ */ u$2("form", { method: "dialog", children: /* @__PURE__ */ u$2("button", { className: "float-right underline text-sm hover:text-blue-700 mr-2", children: "Close" }) }) })
26029
+ ] }),
26030
+ /* @__PURE__ */ u$2("form", { method: "dialog", className: "modal-backdrop", children: /* @__PURE__ */ u$2("button", { children: "Helper to close when clicked outside" }) })
26031
+ ] })
26032
+ ] });
26033
+ };
26034
+ const InfoHeadline1 = ({ children }) => {
26035
+ return /* @__PURE__ */ u$2("h1", { className: "text-lg font-bold", children });
26036
+ };
26037
+ const InfoHeadline2 = ({ children }) => {
26038
+ return /* @__PURE__ */ u$2("h2", { className: "text-base font-bold mt-4", children });
26039
+ };
26040
+ const InfoParagraph = ({ children }) => {
26041
+ return /* @__PURE__ */ u$2("p", { className: "text-justify my-1", children });
26042
+ };
26043
+ const InfoLink = ({ children, href }) => {
26044
+ return /* @__PURE__ */ u$2("a", { className: "text-blue-600 hover:text-blue-800", href, target: "_blank", rel: "noopener noreferrer", children });
26045
+ };
26046
+ const InfoComponentCode = ({ componentName, params, lapisUrl }) => {
26047
+ const componentCode = componentParametersToCode(componentName, params, lapisUrl);
26048
+ const codePenData = {
26049
+ title: "GenSpectrum dashboard component",
26050
+ html: generateFullExampleCode(componentCode, componentName),
26051
+ layout: "left",
26052
+ editors: "100"
26053
+ };
26054
+ return /* @__PURE__ */ u$2(k$1, { children: [
26055
+ /* @__PURE__ */ u$2(InfoHeadline2, { children: "Use this component yourself" }),
26056
+ /* @__PURE__ */ u$2(InfoParagraph, { children: [
26057
+ "This component was created using the following parameters:",
26058
+ /* @__PURE__ */ u$2("div", { className: "p-4 border border-gray-200 rounded-lg overflow-x-auto", children: /* @__PURE__ */ u$2("pre", { children: /* @__PURE__ */ u$2("code", { children: componentCode }) }) })
26059
+ ] }),
26060
+ /* @__PURE__ */ u$2(InfoParagraph, { children: [
26061
+ "You can add this component to your own website using the",
26062
+ " ",
26063
+ /* @__PURE__ */ u$2(InfoLink, { href: "https://github.com/GenSpectrum/dashboard-components", children: "GenSpectrum dashboard components library" }),
26064
+ " ",
26065
+ "and the code from above."
26066
+ ] }),
26067
+ /* @__PURE__ */ u$2(InfoParagraph, { children: /* @__PURE__ */ u$2("form", { action: "https://codepen.io/pen/define", method: "POST", target: "_blank", children: [
26068
+ /* @__PURE__ */ u$2(
26069
+ "input",
26070
+ {
26071
+ type: "hidden",
26072
+ name: "data",
26073
+ value: JSON.stringify(codePenData).replace(/"/g, """).replace(/'/g, "'")
26074
+ }
26075
+ ),
26076
+ /* @__PURE__ */ u$2("button", { className: "text-blue-600 hover:text-blue-800", type: "submit", children: "Click here to try it out on CodePen." })
26077
+ ] }) })
26078
+ ] });
26079
+ };
26080
+ function componentParametersToCode(componentName, params, lapisUrl) {
26081
+ const stringifyIfNeeded = (value) => {
26082
+ return typeof value === "object" ? JSON.stringify(value) : value;
26083
+ };
26084
+ const attributes = indentLines(
26085
+ Object.entries(params).map(([key, value]) => `${key}='${stringifyIfNeeded(value)}'`).join("\n"),
26086
+ 4
26087
+ );
26088
+ return `<gs-app lapis="${lapisUrl}">
26089
+ <gs-${componentName}
26090
+ ${attributes}
26091
+ />
26092
+ </gs-app>`;
26093
+ }
26094
+ function generateFullExampleCode(componentCode, componentName) {
26095
+ const storyBookPath = `/docs/visualization-${componentName}--docs`;
26096
+ return `<html>
26097
+ <head>
26098
+ <script type="module" src="https://unpkg.com/@genspectrum/dashboard-components@latest/dist/dashboard-components.js"><\/script>
26099
+ <link rel="stylesheet" href="https://unpkg.com/@genspectrum/dashboard-components@latest/dist/style.css" />
26100
+ </head>
26101
+
26102
+ <body>
26103
+ <!-- Component documentation: https://genspectrum.github.io/dashboard-components/?path=${storyBookPath} -->
26104
+ ${indentLines(componentCode, 2)}
26105
+ </body>
26106
+ </html>
26107
+ `;
26108
+ }
26109
+ function indentLines(text, numberSpaces) {
26110
+ const spaces = " ".repeat(numberSpaces);
26111
+ return text.split("\n").map((line) => spaces + line).join("\n");
26112
+ }
26113
+ const LoadingDisplay = () => {
26114
+ return /* @__PURE__ */ u$2(
26115
+ "div",
26116
+ {
26117
+ "aria-label": "Loading",
26118
+ className: "h-full w-full border-2 border-gray-100 rounded-md flex justify-center items-center",
26119
+ children: /* @__PURE__ */ u$2("div", { className: "loading loading-spinner loading-md text-neutral-500" })
26120
+ }
26121
+ );
26122
+ };
25880
26123
  const min = Math.min;
25881
26124
  const max = Math.max;
25882
26125
  const round = Math.round;
@@ -27188,8 +27431,8 @@ const Dropdown = ({ children, buttonTitle, placement }) => {
27188
27431
  const toggle = () => {
27189
27432
  setShowContent(!showContent);
27190
27433
  };
27191
- return /* @__PURE__ */ u$2("div", { children: [
27192
- /* @__PURE__ */ u$2("button", { type: "button", className: "btn btn-xs whitespace-nowrap", onClick: toggle, ref: referenceRef, children: buttonTitle }),
27434
+ return /* @__PURE__ */ u$2(k$1, { children: [
27435
+ /* @__PURE__ */ u$2("button", { type: "button", className: "btn btn-xs whitespace-nowrap w-full", onClick: toggle, ref: referenceRef, children: buttonTitle }),
27193
27436
  /* @__PURE__ */ u$2("div", { ref: floatingRef, className: `${dropdownClass} ${showContent ? "" : "hidden"}`, children })
27194
27437
  ] });
27195
27438
  };
@@ -27198,52 +27441,294 @@ const CheckboxSelector = ({
27198
27441
  label,
27199
27442
  setItems
27200
27443
  }) => {
27201
- return /* @__PURE__ */ u$2(Dropdown, { buttonTitle: label, placement: "bottom-start", children: /* @__PURE__ */ u$2("ul", { children: items.map((item, index2) => /* @__PURE__ */ u$2("li", { className: "flex flex-row items-center", children: /* @__PURE__ */ u$2("label", { children: [
27444
+ return /* @__PURE__ */ u$2(Dropdown, { buttonTitle: label, placement: "bottom-start", children: [
27202
27445
  /* @__PURE__ */ u$2(
27203
- "input",
27446
+ "button",
27204
27447
  {
27205
- className: "mr-2",
27206
- type: "checkbox",
27207
- id: `item-${index2}`,
27208
- checked: item.checked,
27209
- onChange: () => {
27210
- const newItems = items.map(
27211
- (item2, i3) => i3 === index2 ? { ...item2, checked: !item2.checked } : item2
27212
- );
27448
+ className: "btn btn-xs btn-ghost",
27449
+ onClick: () => {
27450
+ const newItems = items.map((item) => ({ ...item, checked: true }));
27213
27451
  setItems(newItems);
27214
- }
27452
+ },
27453
+ children: "Select all"
27215
27454
  }
27216
27455
  ),
27217
- item.label
27218
- ] }) }, item.label)) }) });
27219
- };
27220
- const ReferenceGenomeContext = G$1({ nucleotideSequences: [], genes: [] });
27221
- const getSegmentSelectorLabel = (displayedSegments, prefix) => {
27222
- const allSelectedSelected = displayedSegments.filter((segment) => segment.checked).map((segment) => segment.segment);
27223
- if (allSelectedSelected.length === 0) {
27224
- return `${prefix}none`;
27225
- }
27226
- if (displayedSegments.length === allSelectedSelected.length) {
27227
- return `${prefix}all`;
27228
- }
27229
- return prefix + allSelectedSelected.join(", ");
27230
- };
27231
- const SegmentSelector = ({
27456
+ /* @__PURE__ */ u$2(
27457
+ "button",
27458
+ {
27459
+ className: "btn btn-xs btn-ghost",
27460
+ onClick: () => {
27461
+ const newItems = items.map((item) => ({ ...item, checked: false }));
27462
+ setItems(newItems);
27463
+ },
27464
+ children: "Select none"
27465
+ }
27466
+ ),
27467
+ /* @__PURE__ */ u$2("div", { className: "divider mt-0 mb-0" }),
27468
+ /* @__PURE__ */ u$2("ul", { children: items.map((item, index2) => /* @__PURE__ */ u$2("li", { className: "flex flex-row items-center", children: /* @__PURE__ */ u$2("label", { children: [
27469
+ /* @__PURE__ */ u$2(
27470
+ "input",
27471
+ {
27472
+ className: "mr-2",
27473
+ type: "checkbox",
27474
+ id: `item-${index2}`,
27475
+ checked: item.checked,
27476
+ onChange: () => {
27477
+ const newItems = items.map(
27478
+ (item2, i3) => i3 === index2 ? { ...item2, checked: !item2.checked } : item2
27479
+ );
27480
+ setItems(newItems);
27481
+ }
27482
+ }
27483
+ ),
27484
+ item.label
27485
+ ] }) }, item.label)) })
27486
+ ] });
27487
+ };
27488
+ const MutationTypeSelector = ({
27489
+ displayedMutationTypes,
27490
+ setDisplayedMutationTypes
27491
+ }) => {
27492
+ return /* @__PURE__ */ u$2("div", { className: "w-[6rem]", children: /* @__PURE__ */ u$2(
27493
+ CheckboxSelector,
27494
+ {
27495
+ items: displayedMutationTypes,
27496
+ label: getMutationTypesSelectorLabel(displayedMutationTypes),
27497
+ setItems: (items) => setDisplayedMutationTypes(items)
27498
+ }
27499
+ ) });
27500
+ };
27501
+ const getMutationTypesSelectorLabel = (displayedMutationTypes) => {
27502
+ const checkedLabels = displayedMutationTypes.filter((displayedMutationType) => displayedMutationType.checked);
27503
+ if (checkedLabels.length === 0) {
27504
+ return `No types`;
27505
+ }
27506
+ if (displayedMutationTypes.length === checkedLabels.length) {
27507
+ return displayedMutationTypes.map((type) => {
27508
+ switch (type.type) {
27509
+ case "substitution":
27510
+ return "Subst.";
27511
+ case "deletion":
27512
+ return "Del.";
27513
+ }
27514
+ }).join(", ");
27515
+ }
27516
+ return checkedLabels.map((type) => {
27517
+ return type.label;
27518
+ }).join(", ");
27519
+ };
27520
+ const NoDataDisplay = () => {
27521
+ return /* @__PURE__ */ u$2("div", { className: "h-full w-full rounded-md border-2 border-gray-100 p-2 flex items-center justify-center", children: /* @__PURE__ */ u$2("div", { children: "No data available." }) });
27522
+ };
27523
+ const MinMaxRangeSlider = ({
27524
+ min: min2,
27525
+ max: max2,
27526
+ setMin,
27527
+ setMax,
27528
+ rangeMin = 0,
27529
+ rangeMax = 100,
27530
+ step = 0.1
27531
+ }) => {
27532
+ const sliderColor = "#C6C6C6";
27533
+ const rangeColor = "#387bbe";
27534
+ const [zIndexTo, setZIndexTo] = h$1(0);
27535
+ const onMinChange = (event) => {
27536
+ const input = event.target;
27537
+ const minValue = Number(input.value);
27538
+ if (minValue > max2) {
27539
+ setMax(minValue);
27540
+ setMin(minValue);
27541
+ } else {
27542
+ setMin(minValue);
27543
+ }
27544
+ };
27545
+ const onMaxChange = (event) => {
27546
+ const input = event.target;
27547
+ const maxValue = Number(input.value);
27548
+ if (maxValue <= 0) {
27549
+ setZIndexTo(2);
27550
+ } else {
27551
+ setZIndexTo(0);
27552
+ }
27553
+ if (maxValue < min2) {
27554
+ setMin(maxValue);
27555
+ setMax(maxValue);
27556
+ } else {
27557
+ setMax(maxValue);
27558
+ }
27559
+ };
27560
+ const background = `
27561
+ linear-gradient(
27562
+ to right,
27563
+ ${sliderColor} 0%,
27564
+ ${sliderColor} ${min2}%,
27565
+ ${rangeColor} ${min2}%,
27566
+ ${rangeColor} ${max2}%,
27567
+ ${sliderColor} ${max2}%,
27568
+ ${sliderColor} 100%)
27569
+ `;
27570
+ return /* @__PURE__ */ u$2("div", { class: "my-4 relative w-full h-full", children: [
27571
+ /* @__PURE__ */ u$2(
27572
+ "input",
27573
+ {
27574
+ id: "fromSlider",
27575
+ type: "range",
27576
+ value: min2,
27577
+ onInput: onMinChange,
27578
+ min: rangeMin,
27579
+ max: rangeMax,
27580
+ step,
27581
+ style: { background, zIndex: 1, height: 0 }
27582
+ }
27583
+ ),
27584
+ /* @__PURE__ */ u$2(
27585
+ "input",
27586
+ {
27587
+ id: "toSlider",
27588
+ type: "range",
27589
+ value: max2,
27590
+ min: rangeMin,
27591
+ max: rangeMax,
27592
+ step,
27593
+ onInput: onMaxChange,
27594
+ style: { background, zIndex: zIndexTo }
27595
+ }
27596
+ )
27597
+ ] });
27598
+ };
27599
+ const percentageInRange = (percentage) => {
27600
+ return percentage <= 100 && percentage >= 0;
27601
+ };
27602
+ const PercentInput = ({ percentage, setPercentage }) => {
27603
+ const [internalPercentage, setInternalPercentage] = h$1(percentage);
27604
+ y$1(() => {
27605
+ setInternalPercentage(percentage);
27606
+ }, [percentage]);
27607
+ const handleInputChange = (event) => {
27608
+ const input = event.target;
27609
+ const value = Number(input.value);
27610
+ if (value === internalPercentage || input.value === "") {
27611
+ return;
27612
+ }
27613
+ const inRange2 = percentageInRange(value);
27614
+ if (inRange2) {
27615
+ setPercentage(value);
27616
+ }
27617
+ setInternalPercentage(value);
27618
+ };
27619
+ const isError = !percentageInRange(internalPercentage);
27620
+ return /* @__PURE__ */ u$2("label", { className: `input input-bordered flex items-center gap-2 w-32 ${isError ? "input-error" : ""}`, children: [
27621
+ /* @__PURE__ */ u$2(
27622
+ "input",
27623
+ {
27624
+ type: "number",
27625
+ step: 0.1,
27626
+ min: 0,
27627
+ max: 100,
27628
+ value: internalPercentage,
27629
+ onInput: handleInputChange,
27630
+ lang: "en",
27631
+ className: `grow w-16`
27632
+ }
27633
+ ),
27634
+ "%"
27635
+ ] });
27636
+ };
27637
+ function useUpdateExternalValueInIntervals(setExternalValue, updateIntervalInMs, internalValue) {
27638
+ const hasMounted = A$1(false);
27639
+ y$1(() => {
27640
+ if (!hasMounted.current) {
27641
+ hasMounted.current = true;
27642
+ return;
27643
+ }
27644
+ const minTimeout = setTimeout(() => {
27645
+ setExternalValue(internalValue);
27646
+ }, updateIntervalInMs);
27647
+ return () => clearTimeout(minTimeout);
27648
+ }, [internalValue]);
27649
+ }
27650
+ const ProportionSelector = ({
27651
+ proportionInterval,
27652
+ setMinProportion,
27653
+ setMaxProportion
27654
+ }) => {
27655
+ const updateIntervalInMs = 300;
27656
+ const { min: minProportion, max: maxProportion } = proportionInterval;
27657
+ const [internalMinProportion, setInternalMinProportion] = h$1(minProportion);
27658
+ const [internalMaxProportion, setInternalMaxProportion] = h$1(maxProportion);
27659
+ useUpdateExternalValueInIntervals(setMinProportion, updateIntervalInMs, internalMinProportion);
27660
+ const updateMinPercentage = (minPercentage) => {
27661
+ const newMinProportion = minPercentage / 100;
27662
+ setInternalMinProportion(newMinProportion);
27663
+ };
27664
+ useUpdateExternalValueInIntervals(setMaxProportion, updateIntervalInMs, internalMaxProportion);
27665
+ const updateMaxPercentage = (maxPercentage) => {
27666
+ const newMaxProportion = maxPercentage / 100;
27667
+ setInternalMaxProportion(newMaxProportion);
27668
+ };
27669
+ return /* @__PURE__ */ u$2("div", { class: "flex flex-col w-64 mb-2", children: [
27670
+ /* @__PURE__ */ u$2("div", { class: "flex items-center ", children: [
27671
+ /* @__PURE__ */ u$2(PercentInput, { percentage: internalMinProportion * 100, setPercentage: updateMinPercentage }),
27672
+ /* @__PURE__ */ u$2("div", { class: "m-2", children: "-" }),
27673
+ /* @__PURE__ */ u$2(PercentInput, { percentage: internalMaxProportion * 100, setPercentage: updateMaxPercentage })
27674
+ ] }),
27675
+ /* @__PURE__ */ u$2("div", { class: "my-1", children: /* @__PURE__ */ u$2(
27676
+ MinMaxRangeSlider,
27677
+ {
27678
+ min: internalMinProportion * 100,
27679
+ max: internalMaxProportion * 100,
27680
+ setMin: updateMinPercentage,
27681
+ setMax: updateMaxPercentage
27682
+ }
27683
+ ) })
27684
+ ] });
27685
+ };
27686
+ const ProportionSelectorDropdown = ({
27687
+ proportionInterval,
27688
+ setMinProportion,
27689
+ setMaxProportion
27690
+ }) => {
27691
+ const label = `${(proportionInterval.min * 100).toFixed(1)}% - ${(proportionInterval.max * 100).toFixed(1)}%`;
27692
+ return /* @__PURE__ */ u$2("div", { className: "w-44", children: /* @__PURE__ */ u$2(Dropdown, { buttonTitle: `Proportion ${label}`, placement: "bottom-start", children: /* @__PURE__ */ u$2(
27693
+ ProportionSelector,
27694
+ {
27695
+ proportionInterval,
27696
+ setMinProportion,
27697
+ setMaxProportion
27698
+ }
27699
+ ) }) });
27700
+ };
27701
+ const ReferenceGenomeContext = G$1({ nucleotideSequences: [], genes: [] });
27702
+ const SegmentSelector = ({
27232
27703
  displayedSegments,
27233
- setDisplayedSegments,
27234
- prefix
27704
+ setDisplayedSegments
27235
27705
  }) => {
27236
27706
  if (displayedSegments.length <= 1) {
27237
27707
  return null;
27238
27708
  }
27239
- return /* @__PURE__ */ u$2(
27709
+ return /* @__PURE__ */ u$2("div", { className: "w-24", children: /* @__PURE__ */ u$2(
27240
27710
  CheckboxSelector,
27241
27711
  {
27242
27712
  items: displayedSegments,
27243
- label: getSegmentSelectorLabel(displayedSegments, prefix || "Segments: "),
27713
+ label: getSegmentSelectorLabel(displayedSegments),
27244
27714
  setItems: (items) => setDisplayedSegments(items)
27245
27715
  }
27246
- );
27716
+ ) });
27717
+ };
27718
+ const getSegmentSelectorLabel = (displayedSegments) => {
27719
+ const allSelectedSelected = displayedSegments.filter((segment) => segment.checked).map((segment) => segment.segment);
27720
+ if (allSelectedSelected.length === 0) {
27721
+ return `No segments`;
27722
+ }
27723
+ if (displayedSegments.length === allSelectedSelected.length) {
27724
+ return `All segments`;
27725
+ }
27726
+ const longestDisplayString = `All segments`;
27727
+ const allSelectedSelectedString = allSelectedSelected.join(", ");
27728
+ if (longestDisplayString.length >= allSelectedSelectedString.length) {
27729
+ return allSelectedSelectedString;
27730
+ }
27731
+ return `${allSelectedSelected.length} ${allSelectedSelected.length === 1 ? "segment" : "segments"}`;
27247
27732
  };
27248
27733
  function useDisplayedSegments(sequenceType) {
27249
27734
  const referenceGenome = x$1(ReferenceGenomeContext);
@@ -27254,517 +27739,102 @@ function useDisplayedSegments(sequenceType) {
27254
27739
  }));
27255
27740
  return h$1(displayedSegments);
27256
27741
  }
27257
- const CsvDownloadButton = ({
27258
- label = "Download",
27259
- filename = "data.csv",
27260
- getData,
27261
- className
27262
- }) => {
27263
- const download = () => {
27264
- const content = getDownloadContent();
27265
- const blob = new Blob([content], { type: "text/csv" });
27266
- const url = URL.createObjectURL(blob);
27267
- const a2 = document.createElement("a");
27268
- a2.href = url;
27269
- a2.download = filename;
27270
- a2.click();
27271
- URL.revokeObjectURL(url);
27272
- };
27273
- const getDownloadContent = () => {
27274
- const data = getData();
27275
- const keys = getDataKeys(data);
27276
- const header = `${keys.join(",")}
27277
- `;
27278
- const rows = data.map((row) => keys.map((key) => row[key]).join(",")).join("\n");
27279
- return header + rows;
27280
- };
27281
- const getDataKeys = (data) => {
27282
- const keysSet = data.map((row) => Object.keys(row)).reduce((accumulatedKeys, keys) => {
27283
- keys.forEach((key) => accumulatedKeys.add(key));
27284
- return accumulatedKeys;
27285
- }, /* @__PURE__ */ new Set());
27286
- return [...keysSet];
27742
+ const Tabs = ({ tabs, toolbar }) => {
27743
+ const [activeTab, setActiveTab] = h$1(tabs[0].title);
27744
+ const [heightOfTabs, setHeightOfTabs] = h$1("3rem");
27745
+ const tabRef = A$1(null);
27746
+ const updateHeightOfTabs = () => {
27747
+ if (tabRef.current) {
27748
+ const heightOfTabs2 = tabRef.current.getBoundingClientRect().height;
27749
+ setHeightOfTabs(`${heightOfTabs2}px`);
27750
+ }
27287
27751
  };
27288
- return /* @__PURE__ */ u$2("button", { className, onClick: download, children: label });
27289
- };
27290
- class UserFacingError extends Error {
27291
- constructor(headline, message) {
27292
- super(message);
27293
- this.headline = headline;
27294
- this.name = "UserFacingError";
27295
- }
27296
- }
27297
- const ErrorDisplay = ({ error }) => {
27298
- console.error(error);
27299
- const ref = A$1(null);
27300
- return /* @__PURE__ */ u$2("div", { className: "h-full w-full rounded-md border-2 border-gray-100 p-2 flex items-center justify-center flex-col", children: [
27301
- /* @__PURE__ */ u$2("div", { className: "text-red-700 font-bold", children: "Error" }),
27302
- /* @__PURE__ */ u$2("div", { children: [
27303
- "Oops! Something went wrong.",
27304
- error instanceof UserFacingError && /* @__PURE__ */ u$2(k$1, { children: [
27305
- " ",
27306
- /* @__PURE__ */ u$2(
27307
- "button",
27308
- {
27309
- className: "text-sm text-gray-600 hover:text-gray-300",
27310
- onClick: () => {
27311
- var _a2;
27312
- return (_a2 = ref.current) == null ? void 0 : _a2.showModal();
27313
- },
27314
- children: "Show details."
27315
- }
27316
- ),
27317
- /* @__PURE__ */ u$2("dialog", { ref, class: "modal", children: [
27318
- /* @__PURE__ */ u$2("div", { class: "modal-box", children: [
27319
- /* @__PURE__ */ u$2("form", { method: "dialog", children: /* @__PURE__ */ u$2("button", { className: "btn btn-sm btn-circle btn-ghost absolute right-2 top-2", children: "✕" }) }),
27320
- /* @__PURE__ */ u$2("h1", { class: "text-lg", children: error.headline }),
27321
- /* @__PURE__ */ u$2("p", { class: "py-4", children: error.message })
27322
- ] }),
27323
- /* @__PURE__ */ u$2("form", { method: "dialog", class: "modal-backdrop", children: /* @__PURE__ */ u$2("button", { children: "close" }) })
27324
- ] })
27325
- ] })
27326
- ] })
27327
- ] });
27328
- };
27329
- const ResizeContainer = ({ children, size }) => {
27330
- return /* @__PURE__ */ u$2("div", { style: size, className: "bg-white", children });
27331
- };
27332
- const ErrorBoundary = ({ size, children }) => {
27333
- const [internalError] = b$1();
27334
- if (internalError) {
27335
- return /* @__PURE__ */ u$2(ResizeContainer, { size, children: /* @__PURE__ */ u$2(ErrorDisplay, { error: internalError }) });
27336
- }
27337
- return /* @__PURE__ */ u$2(k$1, { children });
27338
- };
27339
- const Fullscreen = () => {
27340
- const element = A$1(null);
27341
- const isFullscreen = useFullscreenStatus();
27342
- return /* @__PURE__ */ u$2(
27343
- "button",
27344
- {
27345
- ref: element,
27346
- onClick: async () => {
27347
- if (element.current) {
27348
- if (isFullscreen) {
27349
- await document.exitFullscreen();
27350
- } else {
27351
- const componentRoot = findComponentRoot(element.current);
27352
- if (componentRoot) {
27353
- await componentRoot.requestFullscreen();
27354
- }
27355
- }
27356
- }
27357
- },
27358
- className: `mt-0.5 iconify text-2xl ${isFullscreen ? "mdi--fullscreen-exit hover:scale-90" : "mdi--fullscreen hover:scale-110"}`,
27359
- title: isFullscreen ? "Exit fullscreen" : "Enter fullscreen"
27360
- }
27361
- );
27362
- };
27363
- function findComponentRoot(element) {
27364
- var _a2;
27365
- return (_a2 = findShadowRoot(element)) == null ? void 0 : _a2.children[0];
27366
- }
27367
- function findShadowRoot(element) {
27368
- let current = element;
27369
- while (current) {
27370
- if (current instanceof ShadowRoot) {
27371
- return current;
27372
- }
27373
- if (current.parentNode === null) {
27374
- return null;
27375
- }
27376
- current = current.parentNode;
27377
- }
27378
- return null;
27379
- }
27380
- function useFullscreenStatus() {
27381
- const [isFullscreen, setIsFullscreen] = h$1(document.fullscreenElement !== null);
27382
27752
  y$1(() => {
27383
- const handleFullscreenChange = () => {
27384
- setIsFullscreen(document.fullscreenElement !== null);
27385
- };
27386
- document.addEventListener("fullscreenchange", handleFullscreenChange);
27753
+ updateHeightOfTabs();
27754
+ window.addEventListener("resize", updateHeightOfTabs);
27387
27755
  return () => {
27388
- document.removeEventListener("fullscreenchange", handleFullscreenChange);
27756
+ window.removeEventListener("resize", updateHeightOfTabs);
27389
27757
  };
27390
27758
  }, []);
27391
- return isFullscreen;
27392
- }
27393
- const Info = ({ children }) => {
27394
- const dialogRef = A$1(null);
27395
- const toggleHelp = () => {
27396
- var _a2;
27397
- (_a2 = dialogRef.current) == null ? void 0 : _a2.showModal();
27398
- };
27399
- return /* @__PURE__ */ u$2("div", { className: "relative", children: [
27400
- /* @__PURE__ */ u$2("button", { type: "button", className: "btn btn-xs", onClick: toggleHelp, children: "?" }),
27401
- /* @__PURE__ */ u$2("dialog", { ref: dialogRef, className: "modal modal-bottom sm:modal-middle", children: [
27402
- /* @__PURE__ */ u$2("div", { className: "modal-box sm:max-w-5xl", children: [
27403
- /* @__PURE__ */ u$2("form", { method: "dialog", children: /* @__PURE__ */ u$2("button", { className: "btn btn-sm btn-circle btn-ghost absolute right-2 top-2", children: "✕" }) }),
27404
- /* @__PURE__ */ u$2("div", { className: "flex flex-col", children }),
27405
- /* @__PURE__ */ u$2("div", { className: "modal-action", children: /* @__PURE__ */ u$2("form", { method: "dialog", children: /* @__PURE__ */ u$2("button", { className: "float-right underline text-sm hover:text-blue-700 mr-2", children: "Close" }) }) })
27406
- ] }),
27407
- /* @__PURE__ */ u$2("form", { method: "dialog", className: "modal-backdrop", children: /* @__PURE__ */ u$2("button", { children: "Helper to close when clicked outside" }) })
27408
- ] })
27409
- ] });
27410
- };
27411
- const InfoHeadline1 = ({ children }) => {
27412
- return /* @__PURE__ */ u$2("h1", { className: "text-lg font-bold", children });
27413
- };
27414
- const InfoHeadline2 = ({ children }) => {
27415
- return /* @__PURE__ */ u$2("h2", { className: "text-base font-bold mt-4", children });
27416
- };
27417
- const InfoParagraph = ({ children }) => {
27418
- return /* @__PURE__ */ u$2("p", { className: "text-justify my-1", children });
27419
- };
27420
- const InfoLink = ({ children, href }) => {
27421
- return /* @__PURE__ */ u$2("a", { className: "text-blue-600 hover:text-blue-800", href, target: "_blank", rel: "noopener noreferrer", children });
27422
- };
27423
- const InfoComponentCode = ({ componentName, params, lapisUrl }) => {
27424
- const componentCode = componentParametersToCode(componentName, params, lapisUrl);
27425
- const codePenData = {
27426
- title: "GenSpectrum dashboard component",
27427
- html: generateFullExampleCode(componentCode, componentName),
27428
- layout: "left",
27429
- editors: "100"
27430
- };
27431
- return /* @__PURE__ */ u$2(k$1, { children: [
27432
- /* @__PURE__ */ u$2(InfoHeadline2, { children: "Use this component yourself" }),
27433
- /* @__PURE__ */ u$2(InfoParagraph, { children: [
27434
- "This component was created using the following parameters:",
27435
- /* @__PURE__ */ u$2("div", { className: "p-4 border border-gray-200 rounded-lg overflow-x-auto", children: /* @__PURE__ */ u$2("pre", { children: /* @__PURE__ */ u$2("code", { children: componentCode }) }) })
27436
- ] }),
27437
- /* @__PURE__ */ u$2(InfoParagraph, { children: [
27438
- "You can add this component to your own website using the",
27439
- " ",
27440
- /* @__PURE__ */ u$2(InfoLink, { href: "https://github.com/GenSpectrum/dashboard-components", children: "GenSpectrum dashboard components library" }),
27441
- " ",
27442
- "and the code from above."
27759
+ const tabElements = /* @__PURE__ */ u$2("div", { className: "flex flex-row", children: tabs.map((tab) => {
27760
+ return /* @__PURE__ */ u$2(k$1, { children: /* @__PURE__ */ u$2(
27761
+ "button",
27762
+ {
27763
+ className: `px-4 py-2 text-sm font-medium leading-5 transition-colors duration-150 ${activeTab === tab.title ? "border-b-2 border-gray-400" : "text-gray-600 hover:bg-gray-100 hover:text-gray-700"}`,
27764
+ onClick: () => {
27765
+ setActiveTab(tab.title);
27766
+ },
27767
+ children: tab.title
27768
+ }
27769
+ ) }, tab.title);
27770
+ }) });
27771
+ const toolbarElement = typeof toolbar === "function" ? toolbar(activeTab) : toolbar;
27772
+ return /* @__PURE__ */ u$2("div", { className: "h-full w-full", children: [
27773
+ /* @__PURE__ */ u$2("div", { ref: tabRef, className: "flex flex-row justify-between flex-wrap", children: [
27774
+ tabElements,
27775
+ toolbar && /* @__PURE__ */ u$2("div", { className: "py-2 flex flex-wrap gap-y-1", children: toolbarElement })
27443
27776
  ] }),
27444
- /* @__PURE__ */ u$2(InfoParagraph, { children: /* @__PURE__ */ u$2("form", { action: "https://codepen.io/pen/define", method: "POST", target: "_blank", children: [
27445
- /* @__PURE__ */ u$2(
27446
- "input",
27447
- {
27448
- type: "hidden",
27449
- name: "data",
27450
- value: JSON.stringify(codePenData).replace(/"/g, "&quot;").replace(/'/g, "&apos;")
27451
- }
27452
- ),
27453
- /* @__PURE__ */ u$2("button", { className: "text-blue-600 hover:text-blue-800", type: "submit", children: "Click here to try it out on CodePen." })
27454
- ] }) })
27777
+ /* @__PURE__ */ u$2(
27778
+ "div",
27779
+ {
27780
+ className: `p-2 border-2 border-gray-100 rounded-b-md rounded-tr-md ${activeTab === tabs[0].title ? "" : "rounded-tl-md"}`,
27781
+ style: { height: `calc(100% - ${heightOfTabs})` },
27782
+ children: tabs.map((tab) => /* @__PURE__ */ u$2("div", { className: "h-full overflow-auto", hidden: activeTab !== tab.title, children: tab.content }, tab.title))
27783
+ }
27784
+ )
27455
27785
  ] });
27456
27786
  };
27457
- function componentParametersToCode(componentName, params, lapisUrl) {
27458
- const stringifyIfNeeded = (value) => {
27459
- return typeof value === "object" ? JSON.stringify(value) : value;
27460
- };
27461
- const attributes = indentLines(
27462
- Object.entries(params).map(([key, value]) => `${key}='${stringifyIfNeeded(value)}'`).join("\n"),
27463
- 4
27464
- );
27465
- return `<gs-app lapis="${lapisUrl}">
27466
- <gs-${componentName}
27467
- ${attributes}
27468
- />
27469
- </gs-app>`;
27470
- }
27471
- function generateFullExampleCode(componentCode, componentName) {
27472
- const storyBookPath = `/docs/visualization-${componentName}--docs`;
27473
- return `<html>
27474
- <head>
27475
- <script type="module" src="https://unpkg.com/@genspectrum/dashboard-components@latest/dist/dashboard-components.js"><\/script>
27476
- <link rel="stylesheet" href="https://unpkg.com/@genspectrum/dashboard-components@latest/dist/style.css" />
27477
- </head>
27478
-
27479
- <body>
27480
- <!-- Component documentation: https://genspectrum.github.io/dashboard-components/?path=${storyBookPath} -->
27481
- ${indentLines(componentCode, 2)}
27482
- </body>
27483
- </html>
27484
- `;
27485
- }
27486
- function indentLines(text, numberSpaces) {
27487
- const spaces = " ".repeat(numberSpaces);
27488
- return text.split("\n").map((line) => spaces + line).join("\n");
27787
+ function useQuery(fetchDataCallback, dependencies) {
27788
+ const [data, setData] = h$1(null);
27789
+ const [error, setError] = h$1(null);
27790
+ const [isLoading, setIsLoading] = h$1(true);
27791
+ y$1(() => {
27792
+ const fetchData = async () => {
27793
+ setIsLoading(true);
27794
+ try {
27795
+ const result = await fetchDataCallback();
27796
+ setData(result);
27797
+ setError(null);
27798
+ } catch (error2) {
27799
+ setError(error2);
27800
+ } finally {
27801
+ setIsLoading(false);
27802
+ }
27803
+ };
27804
+ fetchData();
27805
+ }, [JSON.stringify(dependencies)]);
27806
+ return { data, error, isLoading };
27489
27807
  }
27490
- const LoadingDisplay = () => {
27491
- return /* @__PURE__ */ u$2("div", { "aria-label": "Loading", className: "h-full w-full skeleton" });
27808
+ const MutationComparison = ({ width, height, ...innerProps }) => {
27809
+ const size = { height, width };
27810
+ return /* @__PURE__ */ u$2(ErrorBoundary, { size, children: /* @__PURE__ */ u$2(ResizeContainer, { size, children: /* @__PURE__ */ u$2(MutationComparisonInner, { ...innerProps }) }) });
27492
27811
  };
27493
- const MutationTypeSelector = ({
27494
- displayedMutationTypes,
27495
- setDisplayedMutationTypes
27812
+ const MutationComparisonInner = ({
27813
+ lapisFilters,
27814
+ sequenceType,
27815
+ views,
27816
+ pageSize
27496
27817
  }) => {
27497
- const checkedLabels = displayedMutationTypes.filter((type) => type.checked).map((type) => type.label);
27498
- const mutationTypesSelectorLabel = `Types: ${checkedLabels.length > 0 ? checkedLabels.join(", ") : "None"}`;
27818
+ const lapis = x$1(LapisUrlContext);
27819
+ const { data, error, isLoading } = useQuery(async () => {
27820
+ return queryMutationData(lapisFilters, sequenceType, lapis);
27821
+ }, [lapisFilters, sequenceType, lapis]);
27822
+ if (isLoading) {
27823
+ return /* @__PURE__ */ u$2(LoadingDisplay, {});
27824
+ }
27825
+ if (error !== null) {
27826
+ return /* @__PURE__ */ u$2(ErrorDisplay, { error });
27827
+ }
27828
+ if (data === null) {
27829
+ return /* @__PURE__ */ u$2(NoDataDisplay, {});
27830
+ }
27499
27831
  return /* @__PURE__ */ u$2(
27500
- CheckboxSelector,
27832
+ MutationComparisonTabs,
27501
27833
  {
27502
- items: displayedMutationTypes,
27503
- label: mutationTypesSelectorLabel,
27504
- setItems: (items) => setDisplayedMutationTypes(items)
27505
- }
27506
- );
27507
- };
27508
- const NoDataDisplay = () => {
27509
- return /* @__PURE__ */ u$2("div", { className: "h-full w-full rounded-md border-2 border-gray-100 p-2 flex items-center justify-center", children: /* @__PURE__ */ u$2("div", { children: "No data available." }) });
27510
- };
27511
- const MinMaxRangeSlider = ({
27512
- min: min2,
27513
- max: max2,
27514
- setMin,
27515
- setMax,
27516
- rangeMin = 0,
27517
- rangeMax = 100,
27518
- step = 0.1
27519
- }) => {
27520
- const sliderColor = "#C6C6C6";
27521
- const rangeColor = "#387bbe";
27522
- const [zIndexTo, setZIndexTo] = h$1(0);
27523
- const onMinChange = (event) => {
27524
- const input = event.target;
27525
- const minValue = Number(input.value);
27526
- if (minValue > max2) {
27527
- setMax(minValue);
27528
- setMin(minValue);
27529
- } else {
27530
- setMin(minValue);
27531
- }
27532
- };
27533
- const onMaxChange = (event) => {
27534
- const input = event.target;
27535
- const maxValue = Number(input.value);
27536
- if (maxValue <= 0) {
27537
- setZIndexTo(2);
27538
- } else {
27539
- setZIndexTo(0);
27540
- }
27541
- if (maxValue < min2) {
27542
- setMin(maxValue);
27543
- setMax(maxValue);
27544
- } else {
27545
- setMax(maxValue);
27546
- }
27547
- };
27548
- const background = `
27549
- linear-gradient(
27550
- to right,
27551
- ${sliderColor} 0%,
27552
- ${sliderColor} ${min2}%,
27553
- ${rangeColor} ${min2}%,
27554
- ${rangeColor} ${max2}%,
27555
- ${sliderColor} ${max2}%,
27556
- ${sliderColor} 100%)
27557
- `;
27558
- return /* @__PURE__ */ u$2("div", { class: "my-4 relative w-full h-full", children: [
27559
- /* @__PURE__ */ u$2(
27560
- "input",
27561
- {
27562
- id: "fromSlider",
27563
- type: "range",
27564
- value: min2,
27565
- onInput: onMinChange,
27566
- min: rangeMin,
27567
- max: rangeMax,
27568
- step,
27569
- style: { background, zIndex: 1, height: 0 }
27570
- }
27571
- ),
27572
- /* @__PURE__ */ u$2(
27573
- "input",
27574
- {
27575
- id: "toSlider",
27576
- type: "range",
27577
- value: max2,
27578
- min: rangeMin,
27579
- max: rangeMax,
27580
- step,
27581
- onInput: onMaxChange,
27582
- style: { background, zIndex: zIndexTo }
27583
- }
27584
- )
27585
- ] });
27586
- };
27587
- const percentageInRange = (percentage) => {
27588
- return percentage <= 100 && percentage >= 0;
27589
- };
27590
- const PercentInput = ({ percentage, setPercentage }) => {
27591
- const [internalPercentage, setInternalPercentage] = h$1(percentage);
27592
- y$1(() => {
27593
- setInternalPercentage(percentage);
27594
- }, [percentage]);
27595
- const handleInputChange = (event) => {
27596
- const input = event.target;
27597
- const value = Number(input.value);
27598
- const inRange2 = percentageInRange(value);
27599
- if (inRange2) {
27600
- setPercentage(value);
27601
- }
27602
- setInternalPercentage(value);
27603
- };
27604
- const isError = !percentageInRange(internalPercentage);
27605
- return /* @__PURE__ */ u$2("label", { className: `input input-bordered flex items-center gap-2 w-32 ${isError ? "input-error" : ""}`, children: [
27606
- /* @__PURE__ */ u$2(
27607
- "input",
27608
- {
27609
- type: "number",
27610
- step: 0.1,
27611
- min: 0,
27612
- max: 100,
27613
- value: internalPercentage,
27614
- onInput: handleInputChange,
27615
- lang: "en",
27616
- className: `grow w-16`
27617
- }
27618
- ),
27619
- "%"
27620
- ] });
27621
- };
27622
- const ProportionSelector = ({
27623
- proportionInterval,
27624
- setMinProportion,
27625
- setMaxProportion
27626
- }) => {
27627
- const { min: minProportion, max: maxProportion } = proportionInterval;
27628
- return /* @__PURE__ */ u$2("div", { class: "flex flex-col w-64 mb-2", children: [
27629
- /* @__PURE__ */ u$2("div", { class: "flex items-center ", children: [
27630
- /* @__PURE__ */ u$2(
27631
- PercentInput,
27632
- {
27633
- percentage: minProportion * 100,
27634
- setPercentage: (percentage) => setMinProportion(percentage / 100)
27635
- }
27636
- ),
27637
- /* @__PURE__ */ u$2("div", { class: "m-2", children: "-" }),
27638
- /* @__PURE__ */ u$2(
27639
- PercentInput,
27640
- {
27641
- percentage: maxProportion * 100,
27642
- setPercentage: (percentage) => setMaxProportion(percentage / 100)
27643
- }
27644
- )
27645
- ] }),
27646
- /* @__PURE__ */ u$2("div", { class: "my-1", children: /* @__PURE__ */ u$2(
27647
- MinMaxRangeSlider,
27648
- {
27649
- min: minProportion * 100,
27650
- max: maxProportion * 100,
27651
- setMin: (percentage) => setMinProportion(percentage / 100),
27652
- setMax: (percentage) => setMaxProportion(percentage / 100)
27653
- }
27654
- ) })
27655
- ] });
27656
- };
27657
- const ProportionSelectorDropdown = ({
27658
- proportionInterval,
27659
- setMinProportion,
27660
- setMaxProportion
27661
- }) => {
27662
- const label = `${(proportionInterval.min * 100).toFixed(1)}% - ${(proportionInterval.max * 100).toFixed(1)}%`;
27663
- return /* @__PURE__ */ u$2(Dropdown, { buttonTitle: `Proportion ${label}`, placement: "bottom-start", children: /* @__PURE__ */ u$2(
27664
- ProportionSelector,
27665
- {
27666
- proportionInterval,
27667
- setMinProportion,
27668
- setMaxProportion
27669
- }
27670
- ) });
27671
- };
27672
- const Tabs = ({ tabs, toolbar }) => {
27673
- const [activeTab, setActiveTab] = h$1(tabs[0].title);
27674
- const [heightOfTabs, setHeightOfTabs] = h$1("3rem");
27675
- const tabRef = A$1(null);
27676
- const updateHeightOfTabs = () => {
27677
- if (tabRef.current) {
27678
- const heightOfTabs2 = tabRef.current.getBoundingClientRect().height;
27679
- setHeightOfTabs(`${heightOfTabs2}px`);
27680
- }
27681
- };
27682
- y$1(() => {
27683
- updateHeightOfTabs();
27684
- window.addEventListener("resize", updateHeightOfTabs);
27685
- return () => {
27686
- window.removeEventListener("resize", updateHeightOfTabs);
27687
- };
27688
- }, []);
27689
- const tabElements = /* @__PURE__ */ u$2("div", { className: "flex flex-row", children: tabs.map((tab) => {
27690
- return /* @__PURE__ */ u$2(k$1, { children: /* @__PURE__ */ u$2(
27691
- "button",
27692
- {
27693
- className: `px-4 py-2 text-sm font-medium leading-5 transition-colors duration-150 ${activeTab === tab.title ? "border-b-2 border-gray-400" : "text-gray-600 hover:bg-gray-100 hover:text-gray-700"}`,
27694
- onClick: () => {
27695
- setActiveTab(tab.title);
27696
- },
27697
- children: tab.title
27698
- }
27699
- ) }, tab.title);
27700
- }) });
27701
- const toolbarElement = typeof toolbar === "function" ? toolbar(activeTab) : toolbar;
27702
- return /* @__PURE__ */ u$2("div", { className: "h-full w-full", children: [
27703
- /* @__PURE__ */ u$2("div", { ref: tabRef, className: "flex flex-row justify-between flex-wrap", children: [
27704
- tabElements,
27705
- toolbar && /* @__PURE__ */ u$2("div", { className: "py-2 flex flex-wrap gap-y-1", children: toolbarElement })
27706
- ] }),
27707
- /* @__PURE__ */ u$2(
27708
- "div",
27709
- {
27710
- className: `p-2 border-2 border-gray-100 rounded-b-md rounded-tr-md ${activeTab === tabs[0].title ? "" : "rounded-tl-md"}`,
27711
- style: { height: `calc(100% - ${heightOfTabs})` },
27712
- children: tabs.map((tab) => /* @__PURE__ */ u$2("div", { className: "h-full overflow-auto", hidden: activeTab !== tab.title, children: tab.content }, tab.title))
27713
- }
27714
- )
27715
- ] });
27716
- };
27717
- function useQuery(fetchDataCallback, dependencies = []) {
27718
- const [data, setData] = h$1(null);
27719
- const [error, setError] = h$1(null);
27720
- const [isLoading, setIsLoading] = h$1(true);
27721
- y$1(() => {
27722
- const fetchData = async () => {
27723
- setIsLoading(true);
27724
- try {
27725
- const result = await fetchDataCallback();
27726
- setData(result);
27727
- setError(null);
27728
- } catch (error2) {
27729
- setError(error2);
27730
- } finally {
27731
- setIsLoading(false);
27732
- }
27733
- };
27734
- fetchData();
27735
- }, [JSON.stringify(dependencies)]);
27736
- return { data, error, isLoading };
27737
- }
27738
- const MutationComparison = ({ width, height, ...innerProps }) => {
27739
- const size = { height, width };
27740
- return /* @__PURE__ */ u$2(ErrorBoundary, { size, children: /* @__PURE__ */ u$2(ResizeContainer, { size, children: /* @__PURE__ */ u$2(MutationComparisonInner, { ...innerProps }) }) });
27741
- };
27742
- const MutationComparisonInner = ({
27743
- lapisFilters,
27744
- sequenceType,
27745
- views,
27746
- pageSize
27747
- }) => {
27748
- const lapis = x$1(LapisUrlContext);
27749
- const { data, error, isLoading } = useQuery(async () => {
27750
- return queryMutationData(lapisFilters, sequenceType, lapis);
27751
- }, [lapisFilters, sequenceType, lapis]);
27752
- if (isLoading) {
27753
- return /* @__PURE__ */ u$2(LoadingDisplay, {});
27754
- }
27755
- if (error !== null) {
27756
- return /* @__PURE__ */ u$2(ErrorDisplay, { error });
27757
- }
27758
- if (data === null) {
27759
- return /* @__PURE__ */ u$2(NoDataDisplay, {});
27760
- }
27761
- return /* @__PURE__ */ u$2(
27762
- MutationComparisonTabs,
27763
- {
27764
- data: data.mutationData,
27765
- sequenceType,
27766
- views,
27767
- pageSize
27834
+ data: data.mutationData,
27835
+ sequenceType,
27836
+ views,
27837
+ pageSize
27768
27838
  }
27769
27839
  );
27770
27840
  };
@@ -28479,14 +28549,47 @@ html {
28479
28549
  --tw-contain-paint: ;
28480
28550
  --tw-contain-style: ;
28481
28551
  }
28482
- .alert {
28483
- display: grid;
28552
+ .container {
28484
28553
  width: 100%;
28485
- grid-auto-flow: row;
28486
- align-content: flex-start;
28487
- align-items: center;
28488
- justify-items: center;
28489
- gap: 1rem;
28554
+ }
28555
+ @media (min-width: 640px) {
28556
+
28557
+ .container {
28558
+ max-width: 640px;
28559
+ }
28560
+ }
28561
+ @media (min-width: 768px) {
28562
+
28563
+ .container {
28564
+ max-width: 768px;
28565
+ }
28566
+ }
28567
+ @media (min-width: 1024px) {
28568
+
28569
+ .container {
28570
+ max-width: 1024px;
28571
+ }
28572
+ }
28573
+ @media (min-width: 1280px) {
28574
+
28575
+ .container {
28576
+ max-width: 1280px;
28577
+ }
28578
+ }
28579
+ @media (min-width: 1536px) {
28580
+
28581
+ .container {
28582
+ max-width: 1536px;
28583
+ }
28584
+ }
28585
+ .alert {
28586
+ display: grid;
28587
+ width: 100%;
28588
+ grid-auto-flow: row;
28589
+ align-content: flex-start;
28590
+ align-items: center;
28591
+ justify-items: center;
28592
+ gap: 1rem;
28490
28593
  text-align: center;
28491
28594
  border-radius: var(--rounded-box, 1rem);
28492
28595
  border-width: 1px;
@@ -28520,6 +28623,15 @@ html {
28520
28623
  color: var(--fallback-bc,oklch(var(--bc)/var(--tw-text-opacity)));
28521
28624
  }
28522
28625
 
28626
+ .menu li > *:not(ul, .menu-title, details, .btn):active,
28627
+ .menu li > *:not(ul, .menu-title, details, .btn).active,
28628
+ .menu li > details > summary:active {
28629
+ --tw-bg-opacity: 1;
28630
+ background-color: var(--fallback-n,oklch(var(--n)/var(--tw-bg-opacity)));
28631
+ --tw-text-opacity: 1;
28632
+ color: var(--fallback-nc,oklch(var(--nc)/var(--tw-text-opacity)));
28633
+ }
28634
+
28523
28635
  .tab:hover {
28524
28636
  --tw-text-opacity: 1;
28525
28637
  }
@@ -28613,6 +28725,25 @@ html {
28613
28725
  container-type: inline-size;
28614
28726
  grid-template-columns: auto 1fr;
28615
28727
  }
28728
+ .divider {
28729
+ display: flex;
28730
+ flex-direction: row;
28731
+ align-items: center;
28732
+ align-self: stretch;
28733
+ margin-top: 1rem;
28734
+ margin-bottom: 1rem;
28735
+ height: 1rem;
28736
+ white-space: nowrap;
28737
+ }
28738
+ .divider:before,
28739
+ .divider:after {
28740
+ height: 0.125rem;
28741
+ width: 100%;
28742
+ flex-grow: 1;
28743
+ --tw-content: '';
28744
+ content: var(--tw-content);
28745
+ background-color: var(--fallback-bc,oklch(var(--bc)/0.1));
28746
+ }
28616
28747
  .dropdown {
28617
28748
  position: relative;
28618
28749
  display: inline-block;
@@ -29129,6 +29260,11 @@ input.tab:checked + .tab-content,
29129
29260
  --alert-bg: var(--fallback-er,oklch(var(--er)/1));
29130
29261
  --alert-bg-mix: var(--fallback-b1,oklch(var(--b1)/1));
29131
29262
  }
29263
+ .btm-nav > *:where(.active) {
29264
+ border-top-width: 2px;
29265
+ --tw-bg-opacity: 1;
29266
+ background-color: var(--fallback-b1,oklch(var(--b1)/var(--tw-bg-opacity)));
29267
+ }
29132
29268
  .btm-nav > *.disabled,
29133
29269
  .btm-nav > *[disabled] {
29134
29270
  pointer-events: none;
@@ -29281,6 +29417,9 @@ input.tab:checked + .tab-content,
29281
29417
  background-position-y: 0;
29282
29418
  }
29283
29419
  }
29420
+ .divider:not(:empty) {
29421
+ gap: 1rem;
29422
+ }
29284
29423
  .dropdown.dropdown-open .dropdown-content,
29285
29424
  .dropdown:focus .dropdown-content,
29286
29425
  .dropdown:focus-within .dropdown-content {
@@ -29396,6 +29535,14 @@ input.tab:checked + .tab-content,
29396
29535
  outline: 2px solid transparent;
29397
29536
  outline-offset: 2px;
29398
29537
  }
29538
+ .menu li > *:not(ul, .menu-title, details, .btn):active,
29539
+ .menu li > *:not(ul, .menu-title, details, .btn).active,
29540
+ .menu li > details > summary:active {
29541
+ --tw-bg-opacity: 1;
29542
+ background-color: var(--fallback-n,oklch(var(--n)/var(--tw-bg-opacity)));
29543
+ --tw-text-opacity: 1;
29544
+ color: var(--fallback-nc,oklch(var(--nc)/var(--tw-text-opacity)));
29545
+ }
29399
29546
  .mockup-phone .display {
29400
29547
  overflow: hidden;
29401
29548
  border-radius: 40px;
@@ -29631,30 +29778,6 @@ input.tab:checked + .tab-content,
29631
29778
  background-position: calc(0% + 12px) calc(1px + 50%),
29632
29779
  calc(0% + 16px) calc(1px + 50%);
29633
29780
  }
29634
- .skeleton {
29635
- border-radius: var(--rounded-box, 1rem);
29636
- --tw-bg-opacity: 1;
29637
- background-color: var(--fallback-b3,oklch(var(--b3)/var(--tw-bg-opacity)));
29638
- will-change: background-position;
29639
- animation: skeleton 1.8s ease-in-out infinite;
29640
- background-image: linear-gradient(
29641
- 105deg,
29642
- transparent 0%,
29643
- transparent 40%,
29644
- var(--fallback-b1,oklch(var(--b1)/1)) 50%,
29645
- transparent 60%,
29646
- transparent 100%
29647
- );
29648
- background-size: 200% auto;
29649
- background-repeat: no-repeat;
29650
- background-position-x: -50%;
29651
- }
29652
- @media (prefers-reduced-motion) {
29653
-
29654
- .skeleton {
29655
- animation-duration: 15s;
29656
- }
29657
- }
29658
29781
  @keyframes skeleton {
29659
29782
 
29660
29783
  from {
@@ -29892,6 +30015,12 @@ input.tab:checked + .tab-content,
29892
30015
  --tw-bg-opacity: 1;
29893
30016
  background-color: var(--fallback-b2,oklch(var(--b2)/var(--tw-bg-opacity)));
29894
30017
  }
30018
+ .table-zebra tr.active,
30019
+ .table-zebra tr.active:nth-child(even),
30020
+ .table-zebra-zebra tbody tr:nth-child(even) {
30021
+ --tw-bg-opacity: 1;
30022
+ background-color: var(--fallback-b3,oklch(var(--b3)/var(--tw-bg-opacity)));
30023
+ }
29895
30024
  .table :where(thead tr, tbody tr:not(:last-child), tbody tr:first-child:last-child) {
29896
30025
  border-bottom-width: 1px;
29897
30026
  --tw-border-opacity: 1;
@@ -29964,6 +30093,18 @@ input.tab:checked + .tab-content,
29964
30093
  --togglehandleborder: 0 0 0 3px var(--fallback-bc,oklch(var(--bc)/1)) inset,
29965
30094
  var(--handleoffsetcalculator) 0 0 3px var(--fallback-bc,oklch(var(--bc)/1)) inset;
29966
30095
  }
30096
+ .btm-nav-xs > *:where(.active) {
30097
+ border-top-width: 1px;
30098
+ }
30099
+ .btm-nav-sm > *:where(.active) {
30100
+ border-top-width: 2px;
30101
+ }
30102
+ .btm-nav-md > *:where(.active) {
30103
+ border-top-width: 2px;
30104
+ }
30105
+ .btm-nav-lg > *:where(.active) {
30106
+ border-top-width: 4px;
30107
+ }
29967
30108
  .btn-xs {
29968
30109
  height: 1.5rem;
29969
30110
  min-height: 1.5rem;
@@ -30383,6 +30524,9 @@ input.tab:checked + .tab-content,
30383
30524
  margin-top: 1rem;
30384
30525
  margin-bottom: 1rem;
30385
30526
  }
30527
+ .mb-0 {
30528
+ margin-bottom: 0px;
30529
+ }
30386
30530
  .mb-1 {
30387
30531
  margin-bottom: 0.25rem;
30388
30532
  }
@@ -30407,6 +30551,9 @@ input.tab:checked + .tab-content,
30407
30551
  .mr-2 {
30408
30552
  margin-right: 0.5rem;
30409
30553
  }
30554
+ .mt-0 {
30555
+ margin-top: 0px;
30556
+ }
30410
30557
  .mt-0\\.5 {
30411
30558
  margin-top: 0.125rem;
30412
30559
  }
@@ -30446,12 +30593,24 @@ input.tab:checked + .tab-content,
30446
30593
  .w-16 {
30447
30594
  width: 4rem;
30448
30595
  }
30596
+ .w-20 {
30597
+ width: 5rem;
30598
+ }
30599
+ .w-24 {
30600
+ width: 6rem;
30601
+ }
30449
30602
  .w-32 {
30450
30603
  width: 8rem;
30451
30604
  }
30605
+ .w-44 {
30606
+ width: 11rem;
30607
+ }
30452
30608
  .w-64 {
30453
30609
  width: 16rem;
30454
30610
  }
30611
+ .w-\\[6rem\\] {
30612
+ width: 6rem;
30613
+ }
30455
30614
  .w-\\[7\\.5rem\\] {
30456
30615
  width: 7.5rem;
30457
30616
  }
@@ -30689,6 +30848,10 @@ input.tab:checked + .tab-content,
30689
30848
  --tw-text-opacity: 1;
30690
30849
  color: rgb(75 85 99 / var(--tw-text-opacity));
30691
30850
  }
30851
+ .text-neutral-500 {
30852
+ --tw-text-opacity: 1;
30853
+ color: rgb(115 115 115 / var(--tw-text-opacity));
30854
+ }
30692
30855
  .text-red-700 {
30693
30856
  --tw-text-opacity: 1;
30694
30857
  color: rgb(185 28 28 / var(--tw-text-opacity));
@@ -31041,7 +31204,10 @@ const MutationsGrid = ({
31041
31204
  }
31042
31205
  return {};
31043
31206
  };
31044
- const tableData = getMutationsGridData(data, sequenceType, proportionInterval).map((row) => Object.values(row));
31207
+ const tableData = T$1(
31208
+ () => getMutationsGridData(data, sequenceType, proportionInterval).map((row) => Object.values(row)),
31209
+ [data, proportionInterval, sequenceType]
31210
+ );
31045
31211
  return /* @__PURE__ */ u$2(Table, { data: tableData, columns: getHeaders(), pageSize });
31046
31212
  };
31047
31213
  const sortInsertions = (a2, b3) => {
@@ -34650,7 +34816,8 @@ const NumberSequencesOverTimeInner = ({
34650
34816
  }) => {
34651
34817
  const lapis = x$1(LapisUrlContext);
34652
34818
  const { data, error, isLoading } = useQuery(
34653
- () => queryNumberOfSequencesOverTime(lapis, lapisFilter, lapisDateField, granularity, smoothingWindow)
34819
+ () => queryNumberOfSequencesOverTime(lapis, lapisFilter, lapisDateField, granularity, smoothingWindow),
34820
+ [lapis, lapisFilter, lapisDateField, granularity, smoothingWindow]
34654
34821
  );
34655
34822
  if (isLoading) {
34656
34823
  return /* @__PURE__ */ u$2(LoadingDisplay, {});
@@ -34825,308 +34992,43 @@ __decorateClass$6([
34825
34992
  NumberSequencesOverTimeComponent = __decorateClass$6([
34826
34993
  t$2("gs-number-sequences-over-time")
34827
34994
  ], NumberSequencesOverTimeComponent);
34828
- function getFilteredMutationOverTimeData(data, overallMutationData, displayedSegments, displayedMutationTypes, proportionInterval) {
34829
- const filteredData = data.copy();
34830
- filterDisplayedSegments(displayedSegments, filteredData);
34831
- filterMutationTypes(displayedMutationTypes, filteredData);
34832
- filterProportion(filteredData, overallMutationData, proportionInterval);
34833
- return filteredData;
34995
+ function getDefaultExportFromCjs(x2) {
34996
+ return x2 && x2.__esModule && Object.prototype.hasOwnProperty.call(x2, "default") ? x2["default"] : x2;
34834
34997
  }
34835
- function filterDisplayedSegments(displayedSegments, data) {
34836
- displayedSegments.forEach((segment) => {
34837
- if (!segment.checked) {
34838
- data.getFirstAxisKeys().forEach((mutation) => {
34839
- if (mutation.segment === segment.segment) {
34840
- data.deleteRow(mutation);
34841
- }
34842
- });
34843
- }
34844
- });
34998
+ function commonjsRequire(path) {
34999
+ throw new Error('Could not dynamically require "' + path + '". Please configure the dynamicRequireTargets or/and ignoreDynamicRequires option of @rollup/plugin-commonjs appropriately for this require call to work.');
34845
35000
  }
34846
- function filterMutationTypes(displayedMutationTypes, data) {
34847
- displayedMutationTypes.forEach((mutationType) => {
34848
- if (!mutationType.checked) {
34849
- data.getFirstAxisKeys().forEach((mutation) => {
34850
- if (mutationType.type === mutation.type) {
34851
- data.deleteRow(mutation);
35001
+ var object_hash = { exports: {} };
35002
+ (function(module, exports) {
35003
+ !function(e3) {
35004
+ module.exports = e3();
35005
+ }(function() {
35006
+ return function r2(o2, i3, u2) {
35007
+ function s6(n3, e4) {
35008
+ if (!i3[n3]) {
35009
+ if (!o2[n3]) {
35010
+ var t2 = "function" == typeof commonjsRequire && commonjsRequire;
35011
+ if (!e4 && t2) return t2(n3, true);
35012
+ if (a2) return a2(n3, true);
35013
+ throw new Error("Cannot find module '" + n3 + "'");
35014
+ }
35015
+ e4 = i3[n3] = { exports: {} };
35016
+ o2[n3][0].call(e4.exports, function(e5) {
35017
+ var t3 = o2[n3][1][e5];
35018
+ return s6(t3 || e5);
35019
+ }, e4, e4.exports, r2, o2, i3, u2);
34852
35020
  }
34853
- });
34854
- }
34855
- });
34856
- }
34857
- function filterProportion(data, overallMutationData, proportionInterval) {
34858
- const overallProportionsByMutation = overallMutationData.content.reduce(
34859
- (acc, { mutation, proportion }) => ({
34860
- ...acc,
34861
- [mutation.toString()]: proportion
34862
- }),
34863
- {}
34864
- );
34865
- data.getFirstAxisKeys().forEach((mutation) => {
34866
- const overallProportion = overallProportionsByMutation[mutation.toString()] || -1;
34867
- if (overallProportion < proportionInterval.min || overallProportion > proportionInterval.max) {
34868
- data.deleteRow(mutation);
34869
- }
34870
- });
34871
- }
34872
- const ColorScaleSelector = ({ colorScale, setColorScale }) => {
34873
- const colorDisplayCss = `w-10 h-8 border border-gray-200 mx-2 text-xs flex items-center justify-center`;
34874
- return /* @__PURE__ */ u$2("div", { className: "flex items-center", children: [
34875
- /* @__PURE__ */ u$2(
34876
- "div",
34877
- {
34878
- style: {
34879
- backgroundColor: singleGraphColorRGBByName(colorScale.color, 0),
34880
- color: "black"
34881
- },
34882
- className: colorDisplayCss,
34883
- children: formatProportion(colorScale.min, 0)
35021
+ return i3[n3].exports;
34884
35022
  }
34885
- ),
34886
- /* @__PURE__ */ u$2("div", { className: "w-64", children: /* @__PURE__ */ u$2(
34887
- MinMaxRangeSlider,
34888
- {
34889
- min: colorScale.min * 100,
34890
- max: colorScale.max * 100,
34891
- setMin: (percentage) => {
34892
- setColorScale({ ...colorScale, min: percentage / 100 });
34893
- },
34894
- setMax: (percentage) => {
34895
- setColorScale({ ...colorScale, max: percentage / 100 });
34896
- }
34897
- }
34898
- ) }),
34899
- /* @__PURE__ */ u$2(
34900
- "div",
34901
- {
34902
- style: {
34903
- backgroundColor: singleGraphColorRGBByName(colorScale.color, 1),
34904
- color: "white"
34905
- },
34906
- className: colorDisplayCss,
34907
- children: formatProportion(colorScale.max, 0)
34908
- }
34909
- )
34910
- ] });
34911
- };
34912
- const getColorWithingScale = (value, colorScale) => {
34913
- if (colorScale.min === colorScale.max) {
34914
- return singleGraphColorRGBByName(colorScale.color, 0);
34915
- }
34916
- const colorRange = colorScale.max - colorScale.min;
34917
- const alpha2 = (value - colorScale.min) / colorRange;
34918
- return singleGraphColorRGBByName(colorScale.color, alpha2);
34919
- };
34920
- const getTextColorForScale = (value, colorScale) => {
34921
- if (colorScale.min === colorScale.max) {
34922
- return "black";
34923
- }
34924
- const colorRange = colorScale.max - colorScale.min;
34925
- const alpha2 = (value - colorScale.min) / colorRange;
34926
- return alpha2 <= 0.5 ? "black" : "white";
34927
- };
34928
- function getPositionCss(position) {
34929
- switch (position) {
34930
- case "top":
34931
- return "bottom-full translate-x-[-50%] left-1/2 mb-1";
34932
- case "top-start":
34933
- return "bottom-full mr-1 mb-1";
34934
- case "top-end":
34935
- return "bottom-full right-0 ml-1 mb-1";
34936
- case "bottom":
34937
- return "top-full translate-x-[-50%] left-1/2 mt-1";
34938
- case "bottom-start":
34939
- return "mr-1 mt-1";
34940
- case "bottom-end":
34941
- return "right-0 ml-1 mt-1";
34942
- case "left":
34943
- return "right-full translate-y-[-50%] top-1/2 mr-1";
34944
- case "right":
34945
- return "left-full translate-y-[-50%] top-1/2 ml-1";
34946
- case void 0:
34947
- return "";
34948
- }
34949
- }
34950
- const Tooltip = ({ children, content, position = "bottom" }) => {
34951
- return /* @__PURE__ */ u$2("div", { className: "relative w-full h-full", children: [
34952
- /* @__PURE__ */ u$2("div", { className: "peer w-full h-full", children }),
34953
- /* @__PURE__ */ u$2(
34954
- "div",
34955
- {
34956
- className: `absolute z-10 w-max bg-white p-4 border border-gray-200 rounded-md invisible peer-hover:visible ${getPositionCss(position)}`,
34957
- children: content
34958
- }
34959
- )
34960
- ] });
34961
- };
34962
- const MAX_NUMBER_OF_GRID_ROWS = 100;
34963
- const MUTATION_CELL_WIDTH_REM = 8;
34964
- const MutationsOverTimeGrid = ({ data, colorScale }) => {
34965
- const allMutations = data.getFirstAxisKeys().sort(sortSubstitutionsAndDeletions);
34966
- const shownMutations = allMutations.slice(0, MAX_NUMBER_OF_GRID_ROWS);
34967
- const dates = data.getSecondAxisKeys().sort((a2, b3) => compareTemporal(a2, b3));
34968
- const [showProportionText, setShowProportionText] = h$1(false);
34969
- const gridRef = A$1(null);
34970
- useShowProportion(gridRef, dates.length, setShowProportionText);
34971
- return /* @__PURE__ */ u$2(k$1, { children: [
34972
- allMutations.length > MAX_NUMBER_OF_GRID_ROWS && /* @__PURE__ */ u$2("div", { className: "pl-2", children: [
34973
- "Showing ",
34974
- MAX_NUMBER_OF_GRID_ROWS,
34975
- " of ",
34976
- allMutations.length,
34977
- " mutations. You can narrow the filter to reduce the number of mutations."
34978
- ] }),
34979
- /* @__PURE__ */ u$2(
34980
- "div",
34981
- {
34982
- ref: gridRef,
34983
- style: {
34984
- display: "grid",
34985
- gridTemplateRows: `repeat(${shownMutations.length}, 24px)`,
34986
- gridTemplateColumns: `${MUTATION_CELL_WIDTH_REM}rem repeat(${dates.length}, minmax(0.05rem, 1fr))`
34987
- },
34988
- children: shownMutations.map((mutation, rowIndex) => {
34989
- return /* @__PURE__ */ u$2(k$1, { children: [
34990
- /* @__PURE__ */ u$2(
34991
- "div",
34992
- {
34993
- style: { gridRowStart: rowIndex + 1, gridColumnStart: 1 },
34994
- children: /* @__PURE__ */ u$2(MutationCell, { mutation })
34995
- },
34996
- `mutation-${mutation.toString()}`
34997
- ),
34998
- dates.map((date, columnIndex) => {
34999
- const value = data.get(mutation, date) ?? { proportion: 0, count: 0 };
35000
- const tooltipPosition = getTooltipPosition(
35001
- rowIndex,
35002
- shownMutations.length,
35003
- columnIndex,
35004
- dates.length
35005
- );
35006
- return /* @__PURE__ */ u$2(
35007
- "div",
35008
- {
35009
- style: { gridRowStart: rowIndex + 1, gridColumnStart: columnIndex + 2 },
35010
- children: /* @__PURE__ */ u$2(
35011
- ProportionCell,
35012
- {
35013
- value,
35014
- date,
35015
- mutation,
35016
- tooltipPosition,
35017
- showProportionText,
35018
- colorScale
35019
- }
35020
- )
35021
- },
35022
- `${mutation.toString()}-${date.toString()}`
35023
- );
35024
- })
35025
- ] }, `fragment-${mutation.toString()}`);
35026
- })
35027
- }
35028
- )
35029
- ] });
35030
- };
35031
- function useShowProportion(gridRef, girdColumns, setShowProportionText) {
35032
- y$1(() => {
35033
- const checkWidth = () => {
35034
- if (gridRef.current) {
35035
- const width = gridRef.current.getBoundingClientRect().width;
35036
- const widthPerDate = (width - remToPx(MUTATION_CELL_WIDTH_REM)) / girdColumns;
35037
- const maxWidthProportionText = 28;
35038
- setShowProportionText(widthPerDate > maxWidthProportionText);
35039
- }
35040
- };
35041
- checkWidth();
35042
- window.addEventListener("resize", checkWidth);
35043
- return () => {
35044
- window.removeEventListener("resize", checkWidth);
35045
- };
35046
- }, [girdColumns, gridRef, setShowProportionText]);
35047
- }
35048
- const remToPx = (rem) => rem * parseFloat(getComputedStyle(document.documentElement).fontSize);
35049
- function getTooltipPosition(rowIndex, rows, columnIndex, columns) {
35050
- const tooltipX = rowIndex < rows / 2 ? "bottom" : "top";
35051
- const tooltipY = columnIndex < columns / 2 ? "start" : "end";
35052
- return `${tooltipX}-${tooltipY}`;
35053
- }
35054
- const ProportionCell = ({ value, mutation, date, tooltipPosition, showProportionText, colorScale }) => {
35055
- const tooltipContent = /* @__PURE__ */ u$2("div", { children: [
35056
- /* @__PURE__ */ u$2("p", { children: /* @__PURE__ */ u$2("span", { className: "font-bold", children: date.englishName() }) }),
35057
- /* @__PURE__ */ u$2("p", { children: [
35058
- "(",
35059
- timeIntervalDisplay(date),
35060
- ")"
35061
- ] }),
35062
- /* @__PURE__ */ u$2("p", { children: mutation.code }),
35063
- /* @__PURE__ */ u$2("p", { children: [
35064
- "Proportion: ",
35065
- formatProportion(value.proportion)
35066
- ] }),
35067
- /* @__PURE__ */ u$2("p", { children: [
35068
- "Count: ",
35069
- value.count
35070
- ] })
35071
- ] });
35072
- return /* @__PURE__ */ u$2("div", { className: "py-1 w-full h-full", children: /* @__PURE__ */ u$2(Tooltip, { content: tooltipContent, position: tooltipPosition, children: /* @__PURE__ */ u$2(
35073
- "div",
35074
- {
35075
- style: {
35076
- backgroundColor: getColorWithingScale(value.proportion, colorScale),
35077
- color: getTextColorForScale(value.proportion, colorScale)
35078
- },
35079
- className: `w-full h-full text-center hover:font-bold text-xs group`,
35080
- children: showProportionText ? formatProportion(value.proportion, 0) : void 0
35081
- }
35082
- ) }) });
35083
- };
35084
- const timeIntervalDisplay = (date) => {
35085
- if (date instanceof YearMonthDay) {
35086
- return date.toString();
35087
- }
35088
- return `${date.firstDay.toString()} - ${date.lastDay.toString()}`;
35089
- };
35090
- const MutationCell = ({ mutation }) => {
35091
- return /* @__PURE__ */ u$2("div", { className: "text-center", children: mutation.toString() });
35092
- };
35093
- function getDefaultExportFromCjs(x2) {
35094
- return x2 && x2.__esModule && Object.prototype.hasOwnProperty.call(x2, "default") ? x2["default"] : x2;
35095
- }
35096
- function commonjsRequire(path) {
35097
- throw new Error('Could not dynamically require "' + path + '". Please configure the dynamicRequireTargets or/and ignoreDynamicRequires option of @rollup/plugin-commonjs appropriately for this require call to work.');
35098
- }
35099
- var object_hash = { exports: {} };
35100
- (function(module, exports) {
35101
- !function(e3) {
35102
- module.exports = e3();
35103
- }(function() {
35104
- return function r2(o2, i3, u2) {
35105
- function s6(n3, e4) {
35106
- if (!i3[n3]) {
35107
- if (!o2[n3]) {
35108
- var t2 = "function" == typeof commonjsRequire && commonjsRequire;
35109
- if (!e4 && t2) return t2(n3, true);
35110
- if (a2) return a2(n3, true);
35111
- throw new Error("Cannot find module '" + n3 + "'");
35112
- }
35113
- e4 = i3[n3] = { exports: {} };
35114
- o2[n3][0].call(e4.exports, function(e5) {
35115
- var t3 = o2[n3][1][e5];
35116
- return s6(t3 || e5);
35117
- }, e4, e4.exports, r2, o2, i3, u2);
35118
- }
35119
- return i3[n3].exports;
35120
- }
35121
- for (var a2 = "function" == typeof commonjsRequire && commonjsRequire, e3 = 0; e3 < u2.length; e3++) s6(u2[e3]);
35122
- return s6;
35123
- }({ 1: [function(w2, b3, m2) {
35124
- !(function(e3, n3, s6, c2, d2, h3, p2, g2, y2) {
35125
- var r2 = w2("crypto");
35126
- function t2(e4, t3) {
35127
- t3 = u2(e4, t3);
35128
- var n4;
35129
- return void 0 === (n4 = "passthrough" !== t3.algorithm ? r2.createHash(t3.algorithm) : new l2()).write && (n4.write = n4.update, n4.end = n4.update), f2(t3, n4).dispatch(e4), n4.update || n4.end(""), n4.digest ? n4.digest("buffer" === t3.encoding ? void 0 : t3.encoding) : (e4 = n4.read(), "buffer" !== t3.encoding ? e4.toString(t3.encoding) : e4);
35023
+ for (var a2 = "function" == typeof commonjsRequire && commonjsRequire, e3 = 0; e3 < u2.length; e3++) s6(u2[e3]);
35024
+ return s6;
35025
+ }({ 1: [function(w2, b3, m2) {
35026
+ !(function(e3, n3, s6, c2, d2, h3, p2, g2, y2) {
35027
+ var r2 = w2("crypto");
35028
+ function t2(e4, t3) {
35029
+ t3 = u2(e4, t3);
35030
+ var n4;
35031
+ return void 0 === (n4 = "passthrough" !== t3.algorithm ? r2.createHash(t3.algorithm) : new l2()).write && (n4.write = n4.update, n4.end = n4.update), f2(t3, n4).dispatch(e4), n4.update || n4.end(""), n4.digest ? n4.digest("buffer" === t3.encoding ? void 0 : t3.encoding) : (e4 = n4.read(), "buffer" !== t3.encoding ? e4.toString(t3.encoding) : e4);
35130
35032
  }
35131
35033
  (m2 = b3.exports = t2).sha1 = function(e4) {
35132
35034
  return t2(e4);
@@ -35881,7 +35783,7 @@ var object_hash = { exports: {} };
35881
35783
  })(object_hash);
35882
35784
  var object_hashExports = object_hash.exports;
35883
35785
  const hash = /* @__PURE__ */ getDefaultExportFromCjs(object_hashExports);
35884
- class Map2d {
35786
+ class Map2dBase {
35885
35787
  constructor(serializeFirstAxis = (key) => typeof key === "string" ? key : hash(key), serializeSecondAxis = (key) => typeof key === "string" ? key : hash(key)) {
35886
35788
  this.serializeFirstAxis = serializeFirstAxis;
35887
35789
  this.serializeSecondAxis = serializeSecondAxis;
@@ -35931,18 +35833,322 @@ class Map2d {
35931
35833
  });
35932
35834
  });
35933
35835
  }
35934
- copy() {
35935
- const copy = new Map2d(this.serializeFirstAxis, this.serializeSecondAxis);
35936
- this.data.forEach((value, key) => {
35937
- const keyFirstAxis = this.keysFirstAxis.get(key);
35938
- value.forEach((value2, key2) => {
35939
- const keySecondAxis = this.keysSecondAxis.get(key2);
35940
- copy.set(keyFirstAxis, keySecondAxis, value2);
35836
+ }
35837
+ class Map2dView {
35838
+ constructor(map2) {
35839
+ this.keysFirstAxis = new Map(map2.keysFirstAxis);
35840
+ this.keysSecondAxis = new Map(map2.keysSecondAxis);
35841
+ if (map2 instanceof Map2dView) {
35842
+ this.baseMap = map2.baseMap;
35843
+ }
35844
+ this.baseMap = map2;
35845
+ }
35846
+ serializeFirstAxis(key) {
35847
+ return this.baseMap.serializeFirstAxis(key);
35848
+ }
35849
+ serializeSecondAxis(key) {
35850
+ return this.baseMap.serializeSecondAxis(key);
35851
+ }
35852
+ deleteRow(key) {
35853
+ this.keysFirstAxis.delete(this.serializeFirstAxis(key));
35854
+ }
35855
+ get(keyFirstAxis, keySecondAxis) {
35856
+ const firstAxisKey = this.serializeFirstAxis(keyFirstAxis);
35857
+ const secondAxisKey = this.serializeSecondAxis(keySecondAxis);
35858
+ if (!this.keysFirstAxis.has(firstAxisKey) || !this.keysSecondAxis.has(secondAxisKey)) {
35859
+ return void 0;
35860
+ }
35861
+ return this.baseMap.get(keyFirstAxis, keySecondAxis);
35862
+ }
35863
+ set() {
35864
+ throw new Error("Cannot set value on a Map2dView");
35865
+ }
35866
+ getFirstAxisKeys() {
35867
+ return Array.from(this.keysFirstAxis.values());
35868
+ }
35869
+ getSecondAxisKeys() {
35870
+ return Array.from(this.keysSecondAxis.values());
35871
+ }
35872
+ getAsArray(fillEmptyWith) {
35873
+ return this.getFirstAxisKeys().map((firstAxisKey) => {
35874
+ return this.getSecondAxisKeys().map((secondAxisKey) => {
35875
+ return this.baseMap.get(firstAxisKey, secondAxisKey) ?? fillEmptyWith;
35941
35876
  });
35942
35877
  });
35943
- return copy;
35878
+ }
35879
+ getRow(key, fillEmptyWith) {
35880
+ const serializedKeyFirstAxis = this.serializeFirstAxis(key);
35881
+ if (!this.keysFirstAxis.has(serializedKeyFirstAxis)) {
35882
+ return [];
35883
+ }
35884
+ return this.baseMap.getRow(key, fillEmptyWith);
35885
+ }
35886
+ }
35887
+ function getFilteredMutationOverTimeData(data, overallMutationData, displayedSegments, displayedMutationTypes, proportionInterval) {
35888
+ const filteredData = new Map2dView(data);
35889
+ filterDisplayedSegments(displayedSegments, filteredData);
35890
+ filterMutationTypes(displayedMutationTypes, filteredData);
35891
+ filterProportion(filteredData, overallMutationData, proportionInterval);
35892
+ return filteredData;
35893
+ }
35894
+ function filterDisplayedSegments(displayedSegments, data) {
35895
+ displayedSegments.forEach((segment) => {
35896
+ if (!segment.checked) {
35897
+ data.getFirstAxisKeys().forEach((mutation) => {
35898
+ if (mutation.segment === segment.segment) {
35899
+ data.deleteRow(mutation);
35900
+ }
35901
+ });
35902
+ }
35903
+ });
35904
+ }
35905
+ function filterMutationTypes(displayedMutationTypes, data) {
35906
+ displayedMutationTypes.forEach((mutationType) => {
35907
+ if (!mutationType.checked) {
35908
+ data.getFirstAxisKeys().forEach((mutation) => {
35909
+ if (mutationType.type === mutation.type) {
35910
+ data.deleteRow(mutation);
35911
+ }
35912
+ });
35913
+ }
35914
+ });
35915
+ }
35916
+ function filterProportion(data, overallMutationData, proportionInterval) {
35917
+ const overallProportionsByMutation = overallMutationData.content.reduce(
35918
+ (acc, { mutation, proportion }) => ({
35919
+ ...acc,
35920
+ [mutation.toString()]: proportion
35921
+ }),
35922
+ {}
35923
+ );
35924
+ data.getFirstAxisKeys().forEach((mutation) => {
35925
+ const overallProportion = overallProportionsByMutation[mutation.toString()] || -1;
35926
+ if (overallProportion < proportionInterval.min || overallProportion > proportionInterval.max) {
35927
+ data.deleteRow(mutation);
35928
+ }
35929
+ });
35930
+ }
35931
+ const ColorScaleSelector = ({ colorScale, setColorScale }) => {
35932
+ const colorDisplayCss = `w-10 h-8 border border-gray-200 mx-2 text-xs flex items-center justify-center`;
35933
+ return /* @__PURE__ */ u$2("div", { className: "flex items-center", children: [
35934
+ /* @__PURE__ */ u$2(
35935
+ "div",
35936
+ {
35937
+ style: {
35938
+ backgroundColor: singleGraphColorRGBByName(colorScale.color, 0),
35939
+ color: "black"
35940
+ },
35941
+ className: colorDisplayCss,
35942
+ children: formatProportion(colorScale.min, 0)
35943
+ }
35944
+ ),
35945
+ /* @__PURE__ */ u$2("div", { className: "w-64", children: /* @__PURE__ */ u$2(
35946
+ MinMaxRangeSlider,
35947
+ {
35948
+ min: colorScale.min * 100,
35949
+ max: colorScale.max * 100,
35950
+ setMin: (percentage) => {
35951
+ setColorScale({ ...colorScale, min: percentage / 100 });
35952
+ },
35953
+ setMax: (percentage) => {
35954
+ setColorScale({ ...colorScale, max: percentage / 100 });
35955
+ }
35956
+ }
35957
+ ) }),
35958
+ /* @__PURE__ */ u$2(
35959
+ "div",
35960
+ {
35961
+ style: {
35962
+ backgroundColor: singleGraphColorRGBByName(colorScale.color, 1),
35963
+ color: "white"
35964
+ },
35965
+ className: colorDisplayCss,
35966
+ children: formatProportion(colorScale.max, 0)
35967
+ }
35968
+ )
35969
+ ] });
35970
+ };
35971
+ const getColorWithingScale = (value, colorScale) => {
35972
+ if (colorScale.min === colorScale.max) {
35973
+ return singleGraphColorRGBByName(colorScale.color, 0);
35974
+ }
35975
+ const colorRange = colorScale.max - colorScale.min;
35976
+ const alpha2 = (value - colorScale.min) / colorRange;
35977
+ return singleGraphColorRGBByName(colorScale.color, alpha2);
35978
+ };
35979
+ const getTextColorForScale = (value, colorScale) => {
35980
+ if (colorScale.min === colorScale.max) {
35981
+ return "black";
35982
+ }
35983
+ const colorRange = colorScale.max - colorScale.min;
35984
+ const alpha2 = (value - colorScale.min) / colorRange;
35985
+ return alpha2 <= 0.5 ? "black" : "white";
35986
+ };
35987
+ function getPositionCss(position) {
35988
+ switch (position) {
35989
+ case "top":
35990
+ return "bottom-full translate-x-[-50%] left-1/2 mb-1";
35991
+ case "top-start":
35992
+ return "bottom-full mr-1 mb-1";
35993
+ case "top-end":
35994
+ return "bottom-full right-0 ml-1 mb-1";
35995
+ case "bottom":
35996
+ return "top-full translate-x-[-50%] left-1/2 mt-1";
35997
+ case "bottom-start":
35998
+ return "mr-1 mt-1";
35999
+ case "bottom-end":
36000
+ return "right-0 ml-1 mt-1";
36001
+ case "left":
36002
+ return "right-full translate-y-[-50%] top-1/2 mr-1";
36003
+ case "right":
36004
+ return "left-full translate-y-[-50%] top-1/2 ml-1";
36005
+ case void 0:
36006
+ return "";
35944
36007
  }
35945
36008
  }
36009
+ const Tooltip = ({ children, content, position = "bottom" }) => {
36010
+ return /* @__PURE__ */ u$2("div", { className: "relative w-full h-full", children: [
36011
+ /* @__PURE__ */ u$2("div", { className: "peer w-full h-full", children }),
36012
+ /* @__PURE__ */ u$2(
36013
+ "div",
36014
+ {
36015
+ className: `absolute z-10 w-max bg-white p-4 border border-gray-200 rounded-md invisible peer-hover:visible ${getPositionCss(position)}`,
36016
+ children: content
36017
+ }
36018
+ )
36019
+ ] });
36020
+ };
36021
+ const MAX_NUMBER_OF_GRID_ROWS = 100;
36022
+ const MUTATION_CELL_WIDTH_REM = 8;
36023
+ const MutationsOverTimeGrid = ({ data, colorScale }) => {
36024
+ const allMutations = data.getFirstAxisKeys().sort(sortSubstitutionsAndDeletions);
36025
+ const shownMutations = allMutations.slice(0, MAX_NUMBER_OF_GRID_ROWS);
36026
+ const dates = data.getSecondAxisKeys().sort((a2, b3) => compareTemporal(a2, b3));
36027
+ const [showProportionText, setShowProportionText] = h$1(false);
36028
+ const gridRef = A$1(null);
36029
+ useShowProportion(gridRef, dates.length, setShowProportionText);
36030
+ return /* @__PURE__ */ u$2(k$1, { children: [
36031
+ allMutations.length > MAX_NUMBER_OF_GRID_ROWS && /* @__PURE__ */ u$2("div", { className: "pl-2", children: [
36032
+ "Showing ",
36033
+ MAX_NUMBER_OF_GRID_ROWS,
36034
+ " of ",
36035
+ allMutations.length,
36036
+ " mutations. You can narrow the filter to reduce the number of mutations."
36037
+ ] }),
36038
+ /* @__PURE__ */ u$2(
36039
+ "div",
36040
+ {
36041
+ ref: gridRef,
36042
+ style: {
36043
+ display: "grid",
36044
+ gridTemplateRows: `repeat(${shownMutations.length}, 24px)`,
36045
+ gridTemplateColumns: `${MUTATION_CELL_WIDTH_REM}rem repeat(${dates.length}, minmax(0.05rem, 1fr))`
36046
+ },
36047
+ children: shownMutations.map((mutation, rowIndex) => {
36048
+ return /* @__PURE__ */ u$2(k$1, { children: [
36049
+ /* @__PURE__ */ u$2(
36050
+ "div",
36051
+ {
36052
+ style: { gridRowStart: rowIndex + 1, gridColumnStart: 1 },
36053
+ children: /* @__PURE__ */ u$2(MutationCell, { mutation })
36054
+ },
36055
+ `mutation-${mutation.toString()}`
36056
+ ),
36057
+ dates.map((date, columnIndex) => {
36058
+ const value = data.get(mutation, date) ?? { proportion: 0, count: 0 };
36059
+ const tooltipPosition = getTooltipPosition(
36060
+ rowIndex,
36061
+ shownMutations.length,
36062
+ columnIndex,
36063
+ dates.length
36064
+ );
36065
+ return /* @__PURE__ */ u$2(
36066
+ "div",
36067
+ {
36068
+ style: { gridRowStart: rowIndex + 1, gridColumnStart: columnIndex + 2 },
36069
+ children: /* @__PURE__ */ u$2(
36070
+ ProportionCell,
36071
+ {
36072
+ value,
36073
+ date,
36074
+ mutation,
36075
+ tooltipPosition,
36076
+ showProportionText,
36077
+ colorScale
36078
+ }
36079
+ )
36080
+ },
36081
+ `${mutation.toString()}-${date.toString()}`
36082
+ );
36083
+ })
36084
+ ] }, `fragment-${mutation.toString()}`);
36085
+ })
36086
+ }
36087
+ )
36088
+ ] });
36089
+ };
36090
+ function useShowProportion(gridRef, girdColumns, setShowProportionText) {
36091
+ y$1(() => {
36092
+ const checkWidth = () => {
36093
+ if (gridRef.current) {
36094
+ const width = gridRef.current.getBoundingClientRect().width;
36095
+ const widthPerDate = (width - remToPx(MUTATION_CELL_WIDTH_REM)) / girdColumns;
36096
+ const maxWidthProportionText = 28;
36097
+ setShowProportionText(widthPerDate > maxWidthProportionText);
36098
+ }
36099
+ };
36100
+ checkWidth();
36101
+ window.addEventListener("resize", checkWidth);
36102
+ return () => {
36103
+ window.removeEventListener("resize", checkWidth);
36104
+ };
36105
+ }, [girdColumns, gridRef, setShowProportionText]);
36106
+ }
36107
+ const remToPx = (rem) => rem * parseFloat(getComputedStyle(document.documentElement).fontSize);
36108
+ function getTooltipPosition(rowIndex, rows, columnIndex, columns) {
36109
+ const tooltipX = rowIndex < rows / 2 ? "bottom" : "top";
36110
+ const tooltipY = columnIndex < columns / 2 ? "start" : "end";
36111
+ return `${tooltipX}-${tooltipY}`;
36112
+ }
36113
+ const ProportionCell = ({ value, mutation, date, tooltipPosition, showProportionText, colorScale }) => {
36114
+ const tooltipContent = /* @__PURE__ */ u$2("div", { children: [
36115
+ /* @__PURE__ */ u$2("p", { children: /* @__PURE__ */ u$2("span", { className: "font-bold", children: date.englishName() }) }),
36116
+ /* @__PURE__ */ u$2("p", { children: [
36117
+ "(",
36118
+ timeIntervalDisplay(date),
36119
+ ")"
36120
+ ] }),
36121
+ /* @__PURE__ */ u$2("p", { children: mutation.code }),
36122
+ /* @__PURE__ */ u$2("p", { children: [
36123
+ "Proportion: ",
36124
+ formatProportion(value.proportion)
36125
+ ] }),
36126
+ /* @__PURE__ */ u$2("p", { children: [
36127
+ "Count: ",
36128
+ value.count
36129
+ ] })
36130
+ ] });
36131
+ return /* @__PURE__ */ u$2("div", { className: "py-1 w-full h-full", children: /* @__PURE__ */ u$2(Tooltip, { content: tooltipContent, position: tooltipPosition, children: /* @__PURE__ */ u$2(
36132
+ "div",
36133
+ {
36134
+ style: {
36135
+ backgroundColor: getColorWithingScale(value.proportion, colorScale),
36136
+ color: getTextColorForScale(value.proportion, colorScale)
36137
+ },
36138
+ className: `w-full h-full text-center hover:font-bold text-xs group`,
36139
+ children: showProportionText ? formatProportion(value.proportion, 0) : void 0
36140
+ }
36141
+ ) }) });
36142
+ };
36143
+ const timeIntervalDisplay = (date) => {
36144
+ if (date instanceof YearMonthDay) {
36145
+ return date.toString();
36146
+ }
36147
+ return `${date.firstDay.toString()} - ${date.lastDay.toString()}`;
36148
+ };
36149
+ const MutationCell = ({ mutation }) => {
36150
+ return /* @__PURE__ */ u$2("div", { className: "text-center", children: mutation.toString() });
36151
+ };
35946
36152
  const MAX_NUMBER_OF_GRID_COLUMNS = 200;
35947
36153
  async function queryOverallMutationData(lapisFilter, sequenceType, lapis, signal) {
35948
36154
  return fetchAndPrepareSubstitutionsOrDeletions(lapisFilter, sequenceType).evaluate(lapis, signal);
@@ -36014,7 +36220,7 @@ function fetchAndPrepareSubstitutionsOrDeletions(filter, sequenceType) {
36014
36220
  return new FetchSubstitutionsOrDeletionsOperator(filter, sequenceType, 1e-3);
36015
36221
  }
36016
36222
  function groupByMutation(data) {
36017
- const dataArray = new Map2d(
36223
+ const dataArray = new Map2dBase(
36018
36224
  (mutation) => mutation.code,
36019
36225
  (date) => date.toString()
36020
36226
  );
@@ -36043,7 +36249,7 @@ const ColorScaleSelectorDropdown = ({
36043
36249
  colorScale,
36044
36250
  setColorScale
36045
36251
  }) => {
36046
- return /* @__PURE__ */ u$2(Dropdown, { buttonTitle: `Color scale`, placement: "bottom-start", children: /* @__PURE__ */ u$2(ColorScaleSelector, { colorScale, setColorScale }) });
36252
+ return /* @__PURE__ */ u$2("div", { className: "w-20", children: /* @__PURE__ */ u$2(Dropdown, { buttonTitle: `Color scale`, placement: "bottom-start", children: /* @__PURE__ */ u$2(ColorScaleSelector, { colorScale, setColorScale }) }) });
36047
36253
  };
36048
36254
  const MutationsOverTime = ({ width, height, ...innerProps }) => {
36049
36255
  const size = { height, width };