@genspectrum/dashboard-components 0.6.11 → 0.6.12

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 (34) hide show
  1. package/dist/dashboard-components.js +1072 -853
  2. package/dist/dashboard-components.js.map +1 -1
  3. package/dist/genspectrum-components.d.ts +7 -7
  4. package/dist/style.css +113 -0
  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/mutation-type-selector.stories.tsx +115 -0
  11. package/src/preact/components/mutation-type-selector.tsx +33 -8
  12. package/src/preact/components/percent-input.stories.tsx +93 -0
  13. package/src/preact/components/percent-intput.tsx +4 -0
  14. package/src/preact/components/proportion-selector-dropdown.stories.tsx +2 -2
  15. package/src/preact/components/proportion-selector-dropdown.tsx +9 -7
  16. package/src/preact/components/proportion-selector.stories.tsx +4 -4
  17. package/src/preact/components/proportion-selector.tsx +46 -12
  18. package/src/preact/components/segment-selector.stories.tsx +151 -0
  19. package/src/preact/components/{SegmentSelector.tsx → segment-selector.tsx} +29 -20
  20. package/src/preact/mutationComparison/mutation-comparison.stories.tsx +1 -1
  21. package/src/preact/mutationComparison/mutation-comparison.tsx +1 -1
  22. package/src/preact/mutationComparison/queryMutationData.ts +1 -1
  23. package/src/preact/mutations/mutations-grid.tsx +5 -1
  24. package/src/preact/mutations/mutations.tsx +1 -1
  25. package/src/preact/mutations/queryMutations.ts +1 -1
  26. package/src/preact/mutationsOverTime/getFilteredMutationsOverTime.spec.ts +4 -4
  27. package/src/preact/mutationsOverTime/getFilteredMutationsOverTimeData.ts +3 -2
  28. package/src/preact/mutationsOverTime/mutations-over-time.tsx +1 -1
  29. package/src/preact/numberSequencesOverTime/number-sequences-over-time.tsx +3 -2
  30. package/src/preact/useQuery.ts +1 -1
  31. package/src/query/queryMutationsOverTime.ts +3 -3
  32. package/src/utils/map2d.spec.ts +83 -22
  33. package/src/utils/map2d.ts +158 -0
  34. package/src/utils/Map2d.ts +0 -75
@@ -25877,6 +25877,242 @@ 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("div", { "aria-label": "Loading", className: "h-full w-full skeleton" });
26115
+ };
25880
26116
  const min = Math.min;
25881
26117
  const max = Math.max;
25882
26118
  const round = Math.round;
@@ -27188,8 +27424,8 @@ const Dropdown = ({ children, buttonTitle, placement }) => {
27188
27424
  const toggle = () => {
27189
27425
  setShowContent(!showContent);
27190
27426
  };
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 }),
27427
+ return /* @__PURE__ */ u$2(k$1, { children: [
27428
+ /* @__PURE__ */ u$2("button", { type: "button", className: "btn btn-xs whitespace-nowrap w-full", onClick: toggle, ref: referenceRef, children: buttonTitle }),
27193
27429
  /* @__PURE__ */ u$2("div", { ref: floatingRef, className: `${dropdownClass} ${showContent ? "" : "hidden"}`, children })
27194
27430
  ] });
27195
27431
  };
@@ -27198,52 +27434,294 @@ const CheckboxSelector = ({
27198
27434
  label,
27199
27435
  setItems
27200
27436
  }) => {
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: [
27437
+ return /* @__PURE__ */ u$2(Dropdown, { buttonTitle: label, placement: "bottom-start", children: [
27202
27438
  /* @__PURE__ */ u$2(
27203
- "input",
27439
+ "button",
27204
27440
  {
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
- );
27441
+ className: "btn btn-xs btn-ghost",
27442
+ onClick: () => {
27443
+ const newItems = items.map((item) => ({ ...item, checked: true }));
27213
27444
  setItems(newItems);
27214
- }
27445
+ },
27446
+ children: "Select all"
27215
27447
  }
27216
27448
  ),
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 = ({
27232
- displayedSegments,
27233
- setDisplayedSegments,
27234
- prefix
27235
- }) => {
27236
- if (displayedSegments.length <= 1) {
27449
+ /* @__PURE__ */ u$2(
27450
+ "button",
27451
+ {
27452
+ className: "btn btn-xs btn-ghost",
27453
+ onClick: () => {
27454
+ const newItems = items.map((item) => ({ ...item, checked: false }));
27455
+ setItems(newItems);
27456
+ },
27457
+ children: "Select none"
27458
+ }
27459
+ ),
27460
+ /* @__PURE__ */ u$2("div", { className: "divider mt-0 mb-0" }),
27461
+ /* @__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: [
27462
+ /* @__PURE__ */ u$2(
27463
+ "input",
27464
+ {
27465
+ className: "mr-2",
27466
+ type: "checkbox",
27467
+ id: `item-${index2}`,
27468
+ checked: item.checked,
27469
+ onChange: () => {
27470
+ const newItems = items.map(
27471
+ (item2, i3) => i3 === index2 ? { ...item2, checked: !item2.checked } : item2
27472
+ );
27473
+ setItems(newItems);
27474
+ }
27475
+ }
27476
+ ),
27477
+ item.label
27478
+ ] }) }, item.label)) })
27479
+ ] });
27480
+ };
27481
+ const MutationTypeSelector = ({
27482
+ displayedMutationTypes,
27483
+ setDisplayedMutationTypes
27484
+ }) => {
27485
+ return /* @__PURE__ */ u$2("div", { className: "w-[6rem]", children: /* @__PURE__ */ u$2(
27486
+ CheckboxSelector,
27487
+ {
27488
+ items: displayedMutationTypes,
27489
+ label: getMutationTypesSelectorLabel(displayedMutationTypes),
27490
+ setItems: (items) => setDisplayedMutationTypes(items)
27491
+ }
27492
+ ) });
27493
+ };
27494
+ const getMutationTypesSelectorLabel = (displayedMutationTypes) => {
27495
+ const checkedLabels = displayedMutationTypes.filter((displayedMutationType) => displayedMutationType.checked);
27496
+ if (checkedLabels.length === 0) {
27497
+ return `No types`;
27498
+ }
27499
+ if (displayedMutationTypes.length === checkedLabels.length) {
27500
+ return displayedMutationTypes.map((type) => {
27501
+ switch (type.type) {
27502
+ case "substitution":
27503
+ return "Subst.";
27504
+ case "deletion":
27505
+ return "Del.";
27506
+ }
27507
+ }).join(", ");
27508
+ }
27509
+ return checkedLabels.map((type) => {
27510
+ return type.label;
27511
+ }).join(", ");
27512
+ };
27513
+ const NoDataDisplay = () => {
27514
+ 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." }) });
27515
+ };
27516
+ const MinMaxRangeSlider = ({
27517
+ min: min2,
27518
+ max: max2,
27519
+ setMin,
27520
+ setMax,
27521
+ rangeMin = 0,
27522
+ rangeMax = 100,
27523
+ step = 0.1
27524
+ }) => {
27525
+ const sliderColor = "#C6C6C6";
27526
+ const rangeColor = "#387bbe";
27527
+ const [zIndexTo, setZIndexTo] = h$1(0);
27528
+ const onMinChange = (event) => {
27529
+ const input = event.target;
27530
+ const minValue = Number(input.value);
27531
+ if (minValue > max2) {
27532
+ setMax(minValue);
27533
+ setMin(minValue);
27534
+ } else {
27535
+ setMin(minValue);
27536
+ }
27537
+ };
27538
+ const onMaxChange = (event) => {
27539
+ const input = event.target;
27540
+ const maxValue = Number(input.value);
27541
+ if (maxValue <= 0) {
27542
+ setZIndexTo(2);
27543
+ } else {
27544
+ setZIndexTo(0);
27545
+ }
27546
+ if (maxValue < min2) {
27547
+ setMin(maxValue);
27548
+ setMax(maxValue);
27549
+ } else {
27550
+ setMax(maxValue);
27551
+ }
27552
+ };
27553
+ const background = `
27554
+ linear-gradient(
27555
+ to right,
27556
+ ${sliderColor} 0%,
27557
+ ${sliderColor} ${min2}%,
27558
+ ${rangeColor} ${min2}%,
27559
+ ${rangeColor} ${max2}%,
27560
+ ${sliderColor} ${max2}%,
27561
+ ${sliderColor} 100%)
27562
+ `;
27563
+ return /* @__PURE__ */ u$2("div", { class: "my-4 relative w-full h-full", children: [
27564
+ /* @__PURE__ */ u$2(
27565
+ "input",
27566
+ {
27567
+ id: "fromSlider",
27568
+ type: "range",
27569
+ value: min2,
27570
+ onInput: onMinChange,
27571
+ min: rangeMin,
27572
+ max: rangeMax,
27573
+ step,
27574
+ style: { background, zIndex: 1, height: 0 }
27575
+ }
27576
+ ),
27577
+ /* @__PURE__ */ u$2(
27578
+ "input",
27579
+ {
27580
+ id: "toSlider",
27581
+ type: "range",
27582
+ value: max2,
27583
+ min: rangeMin,
27584
+ max: rangeMax,
27585
+ step,
27586
+ onInput: onMaxChange,
27587
+ style: { background, zIndex: zIndexTo }
27588
+ }
27589
+ )
27590
+ ] });
27591
+ };
27592
+ const percentageInRange = (percentage) => {
27593
+ return percentage <= 100 && percentage >= 0;
27594
+ };
27595
+ const PercentInput = ({ percentage, setPercentage }) => {
27596
+ const [internalPercentage, setInternalPercentage] = h$1(percentage);
27597
+ y$1(() => {
27598
+ setInternalPercentage(percentage);
27599
+ }, [percentage]);
27600
+ const handleInputChange = (event) => {
27601
+ const input = event.target;
27602
+ const value = Number(input.value);
27603
+ if (value === internalPercentage || input.value === "") {
27604
+ return;
27605
+ }
27606
+ const inRange2 = percentageInRange(value);
27607
+ if (inRange2) {
27608
+ setPercentage(value);
27609
+ }
27610
+ setInternalPercentage(value);
27611
+ };
27612
+ const isError = !percentageInRange(internalPercentage);
27613
+ return /* @__PURE__ */ u$2("label", { className: `input input-bordered flex items-center gap-2 w-32 ${isError ? "input-error" : ""}`, children: [
27614
+ /* @__PURE__ */ u$2(
27615
+ "input",
27616
+ {
27617
+ type: "number",
27618
+ step: 0.1,
27619
+ min: 0,
27620
+ max: 100,
27621
+ value: internalPercentage,
27622
+ onInput: handleInputChange,
27623
+ lang: "en",
27624
+ className: `grow w-16`
27625
+ }
27626
+ ),
27627
+ "%"
27628
+ ] });
27629
+ };
27630
+ function useUpdateExternalValueInIntervals(setExternalValue, updateIntervalInMs, internalValue) {
27631
+ const hasMounted = A$1(false);
27632
+ y$1(() => {
27633
+ if (!hasMounted.current) {
27634
+ hasMounted.current = true;
27635
+ return;
27636
+ }
27637
+ const minTimeout = setTimeout(() => {
27638
+ setExternalValue(internalValue);
27639
+ }, updateIntervalInMs);
27640
+ return () => clearTimeout(minTimeout);
27641
+ }, [internalValue]);
27642
+ }
27643
+ const ProportionSelector = ({
27644
+ proportionInterval,
27645
+ setMinProportion,
27646
+ setMaxProportion
27647
+ }) => {
27648
+ const updateIntervalInMs = 300;
27649
+ const { min: minProportion, max: maxProportion } = proportionInterval;
27650
+ const [internalMinProportion, setInternalMinProportion] = h$1(minProportion);
27651
+ const [internalMaxProportion, setInternalMaxProportion] = h$1(maxProportion);
27652
+ useUpdateExternalValueInIntervals(setMinProportion, updateIntervalInMs, internalMinProportion);
27653
+ const updateMinPercentage = (minPercentage) => {
27654
+ const newMinProportion = minPercentage / 100;
27655
+ setInternalMinProportion(newMinProportion);
27656
+ };
27657
+ useUpdateExternalValueInIntervals(setMaxProportion, updateIntervalInMs, internalMaxProportion);
27658
+ const updateMaxPercentage = (maxPercentage) => {
27659
+ const newMaxProportion = maxPercentage / 100;
27660
+ setInternalMaxProportion(newMaxProportion);
27661
+ };
27662
+ return /* @__PURE__ */ u$2("div", { class: "flex flex-col w-64 mb-2", children: [
27663
+ /* @__PURE__ */ u$2("div", { class: "flex items-center ", children: [
27664
+ /* @__PURE__ */ u$2(PercentInput, { percentage: internalMinProportion * 100, setPercentage: updateMinPercentage }),
27665
+ /* @__PURE__ */ u$2("div", { class: "m-2", children: "-" }),
27666
+ /* @__PURE__ */ u$2(PercentInput, { percentage: internalMaxProportion * 100, setPercentage: updateMaxPercentage })
27667
+ ] }),
27668
+ /* @__PURE__ */ u$2("div", { class: "my-1", children: /* @__PURE__ */ u$2(
27669
+ MinMaxRangeSlider,
27670
+ {
27671
+ min: internalMinProportion * 100,
27672
+ max: internalMaxProportion * 100,
27673
+ setMin: updateMinPercentage,
27674
+ setMax: updateMaxPercentage
27675
+ }
27676
+ ) })
27677
+ ] });
27678
+ };
27679
+ const ProportionSelectorDropdown = ({
27680
+ proportionInterval,
27681
+ setMinProportion,
27682
+ setMaxProportion
27683
+ }) => {
27684
+ const label = `${(proportionInterval.min * 100).toFixed(1)}% - ${(proportionInterval.max * 100).toFixed(1)}%`;
27685
+ return /* @__PURE__ */ u$2("div", { className: "w-44", children: /* @__PURE__ */ u$2(Dropdown, { buttonTitle: `Proportion ${label}`, placement: "bottom-start", children: /* @__PURE__ */ u$2(
27686
+ ProportionSelector,
27687
+ {
27688
+ proportionInterval,
27689
+ setMinProportion,
27690
+ setMaxProportion
27691
+ }
27692
+ ) }) });
27693
+ };
27694
+ const ReferenceGenomeContext = G$1({ nucleotideSequences: [], genes: [] });
27695
+ const SegmentSelector = ({
27696
+ displayedSegments,
27697
+ setDisplayedSegments
27698
+ }) => {
27699
+ if (displayedSegments.length <= 1) {
27237
27700
  return null;
27238
27701
  }
27239
- return /* @__PURE__ */ u$2(
27702
+ return /* @__PURE__ */ u$2("div", { className: "w-24", children: /* @__PURE__ */ u$2(
27240
27703
  CheckboxSelector,
27241
27704
  {
27242
27705
  items: displayedSegments,
27243
- label: getSegmentSelectorLabel(displayedSegments, prefix || "Segments: "),
27706
+ label: getSegmentSelectorLabel(displayedSegments),
27244
27707
  setItems: (items) => setDisplayedSegments(items)
27245
27708
  }
27246
- );
27709
+ ) });
27710
+ };
27711
+ const getSegmentSelectorLabel = (displayedSegments) => {
27712
+ const allSelectedSelected = displayedSegments.filter((segment) => segment.checked).map((segment) => segment.segment);
27713
+ if (allSelectedSelected.length === 0) {
27714
+ return `No segments`;
27715
+ }
27716
+ if (displayedSegments.length === allSelectedSelected.length) {
27717
+ return `All segments`;
27718
+ }
27719
+ const longestDisplayString = `All segments`;
27720
+ const allSelectedSelectedString = allSelectedSelected.join(", ");
27721
+ if (longestDisplayString.length >= allSelectedSelectedString.length) {
27722
+ return allSelectedSelectedString;
27723
+ }
27724
+ return `${allSelectedSelected.length} ${allSelectedSelected.length === 1 ? "segment" : "segments"}`;
27247
27725
  };
27248
27726
  function useDisplayedSegments(sequenceType) {
27249
27727
  const referenceGenome = x$1(ReferenceGenomeContext);
@@ -27254,512 +27732,97 @@ function useDisplayedSegments(sequenceType) {
27254
27732
  }));
27255
27733
  return h$1(displayedSegments);
27256
27734
  }
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];
27735
+ const Tabs = ({ tabs, toolbar }) => {
27736
+ const [activeTab, setActiveTab] = h$1(tabs[0].title);
27737
+ const [heightOfTabs, setHeightOfTabs] = h$1("3rem");
27738
+ const tabRef = A$1(null);
27739
+ const updateHeightOfTabs = () => {
27740
+ if (tabRef.current) {
27741
+ const heightOfTabs2 = tabRef.current.getBoundingClientRect().height;
27742
+ setHeightOfTabs(`${heightOfTabs2}px`);
27743
+ }
27287
27744
  };
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
27745
  y$1(() => {
27383
- const handleFullscreenChange = () => {
27384
- setIsFullscreen(document.fullscreenElement !== null);
27385
- };
27386
- document.addEventListener("fullscreenchange", handleFullscreenChange);
27746
+ updateHeightOfTabs();
27747
+ window.addEventListener("resize", updateHeightOfTabs);
27387
27748
  return () => {
27388
- document.removeEventListener("fullscreenchange", handleFullscreenChange);
27749
+ window.removeEventListener("resize", updateHeightOfTabs);
27389
27750
  };
27390
27751
  }, []);
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."
27752
+ const tabElements = /* @__PURE__ */ u$2("div", { className: "flex flex-row", children: tabs.map((tab) => {
27753
+ return /* @__PURE__ */ u$2(k$1, { children: /* @__PURE__ */ u$2(
27754
+ "button",
27755
+ {
27756
+ 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"}`,
27757
+ onClick: () => {
27758
+ setActiveTab(tab.title);
27759
+ },
27760
+ children: tab.title
27761
+ }
27762
+ ) }, tab.title);
27763
+ }) });
27764
+ const toolbarElement = typeof toolbar === "function" ? toolbar(activeTab) : toolbar;
27765
+ return /* @__PURE__ */ u$2("div", { className: "h-full w-full", children: [
27766
+ /* @__PURE__ */ u$2("div", { ref: tabRef, className: "flex flex-row justify-between flex-wrap", children: [
27767
+ tabElements,
27768
+ toolbar && /* @__PURE__ */ u$2("div", { className: "py-2 flex flex-wrap gap-y-1", children: toolbarElement })
27443
27769
  ] }),
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
- ] }) })
27770
+ /* @__PURE__ */ u$2(
27771
+ "div",
27772
+ {
27773
+ className: `p-2 border-2 border-gray-100 rounded-b-md rounded-tr-md ${activeTab === tabs[0].title ? "" : "rounded-tl-md"}`,
27774
+ style: { height: `calc(100% - ${heightOfTabs})` },
27775
+ children: tabs.map((tab) => /* @__PURE__ */ u$2("div", { className: "h-full overflow-auto", hidden: activeTab !== tab.title, children: tab.content }, tab.title))
27776
+ }
27777
+ )
27455
27778
  ] });
27456
27779
  };
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");
27780
+ function useQuery(fetchDataCallback, dependencies) {
27781
+ const [data, setData] = h$1(null);
27782
+ const [error, setError] = h$1(null);
27783
+ const [isLoading, setIsLoading] = h$1(true);
27784
+ y$1(() => {
27785
+ const fetchData = async () => {
27786
+ setIsLoading(true);
27787
+ try {
27788
+ const result = await fetchDataCallback();
27789
+ setData(result);
27790
+ setError(null);
27791
+ } catch (error2) {
27792
+ setError(error2);
27793
+ } finally {
27794
+ setIsLoading(false);
27795
+ }
27796
+ };
27797
+ fetchData();
27798
+ }, [JSON.stringify(dependencies)]);
27799
+ return { data, error, isLoading };
27489
27800
  }
27490
- const LoadingDisplay = () => {
27491
- return /* @__PURE__ */ u$2("div", { "aria-label": "Loading", className: "h-full w-full skeleton" });
27801
+ const MutationComparison = ({ width, height, ...innerProps }) => {
27802
+ const size = { height, width };
27803
+ return /* @__PURE__ */ u$2(ErrorBoundary, { size, children: /* @__PURE__ */ u$2(ResizeContainer, { size, children: /* @__PURE__ */ u$2(MutationComparisonInner, { ...innerProps }) }) });
27492
27804
  };
27493
- const MutationTypeSelector = ({
27494
- displayedMutationTypes,
27495
- setDisplayedMutationTypes
27805
+ const MutationComparisonInner = ({
27806
+ lapisFilters,
27807
+ sequenceType,
27808
+ views,
27809
+ pageSize
27496
27810
  }) => {
27497
- const checkedLabels = displayedMutationTypes.filter((type) => type.checked).map((type) => type.label);
27498
- const mutationTypesSelectorLabel = `Types: ${checkedLabels.length > 0 ? checkedLabels.join(", ") : "None"}`;
27811
+ const lapis = x$1(LapisUrlContext);
27812
+ const { data, error, isLoading } = useQuery(async () => {
27813
+ return queryMutationData(lapisFilters, sequenceType, lapis);
27814
+ }, [lapisFilters, sequenceType, lapis]);
27815
+ if (isLoading) {
27816
+ return /* @__PURE__ */ u$2(LoadingDisplay, {});
27817
+ }
27818
+ if (error !== null) {
27819
+ return /* @__PURE__ */ u$2(ErrorDisplay, { error });
27820
+ }
27821
+ if (data === null) {
27822
+ return /* @__PURE__ */ u$2(NoDataDisplay, {});
27823
+ }
27499
27824
  return /* @__PURE__ */ u$2(
27500
- CheckboxSelector,
27501
- {
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,
27825
+ MutationComparisonTabs,
27763
27826
  {
27764
27827
  data: data.mutationData,
27765
27828
  sequenceType,
@@ -28479,19 +28542,52 @@ html {
28479
28542
  --tw-contain-paint: ;
28480
28543
  --tw-contain-style: ;
28481
28544
  }
28482
- .alert {
28483
- display: grid;
28545
+ .container {
28484
28546
  width: 100%;
28485
- grid-auto-flow: row;
28486
- align-content: flex-start;
28487
- align-items: center;
28488
- justify-items: center;
28489
- gap: 1rem;
28490
- text-align: center;
28491
- border-radius: var(--rounded-box, 1rem);
28492
- border-width: 1px;
28493
- --tw-border-opacity: 1;
28494
- border-color: var(--fallback-b2,oklch(var(--b2)/var(--tw-border-opacity)));
28547
+ }
28548
+ @media (min-width: 640px) {
28549
+
28550
+ .container {
28551
+ max-width: 640px;
28552
+ }
28553
+ }
28554
+ @media (min-width: 768px) {
28555
+
28556
+ .container {
28557
+ max-width: 768px;
28558
+ }
28559
+ }
28560
+ @media (min-width: 1024px) {
28561
+
28562
+ .container {
28563
+ max-width: 1024px;
28564
+ }
28565
+ }
28566
+ @media (min-width: 1280px) {
28567
+
28568
+ .container {
28569
+ max-width: 1280px;
28570
+ }
28571
+ }
28572
+ @media (min-width: 1536px) {
28573
+
28574
+ .container {
28575
+ max-width: 1536px;
28576
+ }
28577
+ }
28578
+ .alert {
28579
+ display: grid;
28580
+ width: 100%;
28581
+ grid-auto-flow: row;
28582
+ align-content: flex-start;
28583
+ align-items: center;
28584
+ justify-items: center;
28585
+ gap: 1rem;
28586
+ text-align: center;
28587
+ border-radius: var(--rounded-box, 1rem);
28588
+ border-width: 1px;
28589
+ --tw-border-opacity: 1;
28590
+ border-color: var(--fallback-b2,oklch(var(--b2)/var(--tw-border-opacity)));
28495
28591
  padding: 1rem;
28496
28592
  --tw-text-opacity: 1;
28497
28593
  color: var(--fallback-bc,oklch(var(--bc)/var(--tw-text-opacity)));
@@ -28520,6 +28616,15 @@ html {
28520
28616
  color: var(--fallback-bc,oklch(var(--bc)/var(--tw-text-opacity)));
28521
28617
  }
28522
28618
 
28619
+ .menu li > *:not(ul, .menu-title, details, .btn):active,
28620
+ .menu li > *:not(ul, .menu-title, details, .btn).active,
28621
+ .menu li > details > summary:active {
28622
+ --tw-bg-opacity: 1;
28623
+ background-color: var(--fallback-n,oklch(var(--n)/var(--tw-bg-opacity)));
28624
+ --tw-text-opacity: 1;
28625
+ color: var(--fallback-nc,oklch(var(--nc)/var(--tw-text-opacity)));
28626
+ }
28627
+
28523
28628
  .tab:hover {
28524
28629
  --tw-text-opacity: 1;
28525
28630
  }
@@ -28613,6 +28718,25 @@ html {
28613
28718
  container-type: inline-size;
28614
28719
  grid-template-columns: auto 1fr;
28615
28720
  }
28721
+ .divider {
28722
+ display: flex;
28723
+ flex-direction: row;
28724
+ align-items: center;
28725
+ align-self: stretch;
28726
+ margin-top: 1rem;
28727
+ margin-bottom: 1rem;
28728
+ height: 1rem;
28729
+ white-space: nowrap;
28730
+ }
28731
+ .divider:before,
28732
+ .divider:after {
28733
+ height: 0.125rem;
28734
+ width: 100%;
28735
+ flex-grow: 1;
28736
+ --tw-content: '';
28737
+ content: var(--tw-content);
28738
+ background-color: var(--fallback-bc,oklch(var(--bc)/0.1));
28739
+ }
28616
28740
  .dropdown {
28617
28741
  position: relative;
28618
28742
  display: inline-block;
@@ -29129,6 +29253,11 @@ input.tab:checked + .tab-content,
29129
29253
  --alert-bg: var(--fallback-er,oklch(var(--er)/1));
29130
29254
  --alert-bg-mix: var(--fallback-b1,oklch(var(--b1)/1));
29131
29255
  }
29256
+ .btm-nav > *:where(.active) {
29257
+ border-top-width: 2px;
29258
+ --tw-bg-opacity: 1;
29259
+ background-color: var(--fallback-b1,oklch(var(--b1)/var(--tw-bg-opacity)));
29260
+ }
29132
29261
  .btm-nav > *.disabled,
29133
29262
  .btm-nav > *[disabled] {
29134
29263
  pointer-events: none;
@@ -29281,6 +29410,9 @@ input.tab:checked + .tab-content,
29281
29410
  background-position-y: 0;
29282
29411
  }
29283
29412
  }
29413
+ .divider:not(:empty) {
29414
+ gap: 1rem;
29415
+ }
29284
29416
  .dropdown.dropdown-open .dropdown-content,
29285
29417
  .dropdown:focus .dropdown-content,
29286
29418
  .dropdown:focus-within .dropdown-content {
@@ -29396,6 +29528,14 @@ input.tab:checked + .tab-content,
29396
29528
  outline: 2px solid transparent;
29397
29529
  outline-offset: 2px;
29398
29530
  }
29531
+ .menu li > *:not(ul, .menu-title, details, .btn):active,
29532
+ .menu li > *:not(ul, .menu-title, details, .btn).active,
29533
+ .menu li > details > summary:active {
29534
+ --tw-bg-opacity: 1;
29535
+ background-color: var(--fallback-n,oklch(var(--n)/var(--tw-bg-opacity)));
29536
+ --tw-text-opacity: 1;
29537
+ color: var(--fallback-nc,oklch(var(--nc)/var(--tw-text-opacity)));
29538
+ }
29399
29539
  .mockup-phone .display {
29400
29540
  overflow: hidden;
29401
29541
  border-radius: 40px;
@@ -29892,6 +30032,12 @@ input.tab:checked + .tab-content,
29892
30032
  --tw-bg-opacity: 1;
29893
30033
  background-color: var(--fallback-b2,oklch(var(--b2)/var(--tw-bg-opacity)));
29894
30034
  }
30035
+ .table-zebra tr.active,
30036
+ .table-zebra tr.active:nth-child(even),
30037
+ .table-zebra-zebra tbody tr:nth-child(even) {
30038
+ --tw-bg-opacity: 1;
30039
+ background-color: var(--fallback-b3,oklch(var(--b3)/var(--tw-bg-opacity)));
30040
+ }
29895
30041
  .table :where(thead tr, tbody tr:not(:last-child), tbody tr:first-child:last-child) {
29896
30042
  border-bottom-width: 1px;
29897
30043
  --tw-border-opacity: 1;
@@ -29964,6 +30110,18 @@ input.tab:checked + .tab-content,
29964
30110
  --togglehandleborder: 0 0 0 3px var(--fallback-bc,oklch(var(--bc)/1)) inset,
29965
30111
  var(--handleoffsetcalculator) 0 0 3px var(--fallback-bc,oklch(var(--bc)/1)) inset;
29966
30112
  }
30113
+ .btm-nav-xs > *:where(.active) {
30114
+ border-top-width: 1px;
30115
+ }
30116
+ .btm-nav-sm > *:where(.active) {
30117
+ border-top-width: 2px;
30118
+ }
30119
+ .btm-nav-md > *:where(.active) {
30120
+ border-top-width: 2px;
30121
+ }
30122
+ .btm-nav-lg > *:where(.active) {
30123
+ border-top-width: 4px;
30124
+ }
29967
30125
  .btn-xs {
29968
30126
  height: 1.5rem;
29969
30127
  min-height: 1.5rem;
@@ -30383,6 +30541,9 @@ input.tab:checked + .tab-content,
30383
30541
  margin-top: 1rem;
30384
30542
  margin-bottom: 1rem;
30385
30543
  }
30544
+ .mb-0 {
30545
+ margin-bottom: 0px;
30546
+ }
30386
30547
  .mb-1 {
30387
30548
  margin-bottom: 0.25rem;
30388
30549
  }
@@ -30407,6 +30568,9 @@ input.tab:checked + .tab-content,
30407
30568
  .mr-2 {
30408
30569
  margin-right: 0.5rem;
30409
30570
  }
30571
+ .mt-0 {
30572
+ margin-top: 0px;
30573
+ }
30410
30574
  .mt-0\\.5 {
30411
30575
  margin-top: 0.125rem;
30412
30576
  }
@@ -30446,12 +30610,24 @@ input.tab:checked + .tab-content,
30446
30610
  .w-16 {
30447
30611
  width: 4rem;
30448
30612
  }
30613
+ .w-20 {
30614
+ width: 5rem;
30615
+ }
30616
+ .w-24 {
30617
+ width: 6rem;
30618
+ }
30449
30619
  .w-32 {
30450
30620
  width: 8rem;
30451
30621
  }
30622
+ .w-44 {
30623
+ width: 11rem;
30624
+ }
30452
30625
  .w-64 {
30453
30626
  width: 16rem;
30454
30627
  }
30628
+ .w-\\[6rem\\] {
30629
+ width: 6rem;
30630
+ }
30455
30631
  .w-\\[7\\.5rem\\] {
30456
30632
  width: 7.5rem;
30457
30633
  }
@@ -31041,7 +31217,10 @@ const MutationsGrid = ({
31041
31217
  }
31042
31218
  return {};
31043
31219
  };
31044
- const tableData = getMutationsGridData(data, sequenceType, proportionInterval).map((row) => Object.values(row));
31220
+ const tableData = T$1(
31221
+ () => getMutationsGridData(data, sequenceType, proportionInterval).map((row) => Object.values(row)),
31222
+ [data, proportionInterval, sequenceType]
31223
+ );
31045
31224
  return /* @__PURE__ */ u$2(Table, { data: tableData, columns: getHeaders(), pageSize });
31046
31225
  };
31047
31226
  const sortInsertions = (a2, b3) => {
@@ -34650,7 +34829,8 @@ const NumberSequencesOverTimeInner = ({
34650
34829
  }) => {
34651
34830
  const lapis = x$1(LapisUrlContext);
34652
34831
  const { data, error, isLoading } = useQuery(
34653
- () => queryNumberOfSequencesOverTime(lapis, lapisFilter, lapisDateField, granularity, smoothingWindow)
34832
+ () => queryNumberOfSequencesOverTime(lapis, lapisFilter, lapisDateField, granularity, smoothingWindow),
34833
+ [lapis, lapisFilter, lapisDateField, granularity, smoothingWindow]
34654
34834
  );
34655
34835
  if (isLoading) {
34656
34836
  return /* @__PURE__ */ u$2(LoadingDisplay, {});
@@ -34825,308 +35005,43 @@ __decorateClass$6([
34825
35005
  NumberSequencesOverTimeComponent = __decorateClass$6([
34826
35006
  t$2("gs-number-sequences-over-time")
34827
35007
  ], 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;
35008
+ function getDefaultExportFromCjs(x2) {
35009
+ return x2 && x2.__esModule && Object.prototype.hasOwnProperty.call(x2, "default") ? x2["default"] : x2;
34834
35010
  }
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
- });
35011
+ function commonjsRequire(path) {
35012
+ 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
35013
  }
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);
35014
+ var object_hash = { exports: {} };
35015
+ (function(module, exports) {
35016
+ !function(e3) {
35017
+ module.exports = e3();
35018
+ }(function() {
35019
+ return function r2(o2, i3, u2) {
35020
+ function s6(n3, e4) {
35021
+ if (!i3[n3]) {
35022
+ if (!o2[n3]) {
35023
+ var t2 = "function" == typeof commonjsRequire && commonjsRequire;
35024
+ if (!e4 && t2) return t2(n3, true);
35025
+ if (a2) return a2(n3, true);
35026
+ throw new Error("Cannot find module '" + n3 + "'");
35027
+ }
35028
+ e4 = i3[n3] = { exports: {} };
35029
+ o2[n3][0].call(e4.exports, function(e5) {
35030
+ var t3 = o2[n3][1][e5];
35031
+ return s6(t3 || e5);
35032
+ }, e4, e4.exports, r2, o2, i3, u2);
34852
35033
  }
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)
35034
+ return i3[n3].exports;
34884
35035
  }
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);
35036
+ for (var a2 = "function" == typeof commonjsRequire && commonjsRequire, e3 = 0; e3 < u2.length; e3++) s6(u2[e3]);
35037
+ return s6;
35038
+ }({ 1: [function(w2, b3, m2) {
35039
+ !(function(e3, n3, s6, c2, d2, h3, p2, g2, y2) {
35040
+ var r2 = w2("crypto");
35041
+ function t2(e4, t3) {
35042
+ t3 = u2(e4, t3);
35043
+ var n4;
35044
+ 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
35045
  }
35131
35046
  (m2 = b3.exports = t2).sha1 = function(e4) {
35132
35047
  return t2(e4);
@@ -35881,7 +35796,7 @@ var object_hash = { exports: {} };
35881
35796
  })(object_hash);
35882
35797
  var object_hashExports = object_hash.exports;
35883
35798
  const hash = /* @__PURE__ */ getDefaultExportFromCjs(object_hashExports);
35884
- class Map2d {
35799
+ class Map2dBase {
35885
35800
  constructor(serializeFirstAxis = (key) => typeof key === "string" ? key : hash(key), serializeSecondAxis = (key) => typeof key === "string" ? key : hash(key)) {
35886
35801
  this.serializeFirstAxis = serializeFirstAxis;
35887
35802
  this.serializeSecondAxis = serializeSecondAxis;
@@ -35931,18 +35846,322 @@ class Map2d {
35931
35846
  });
35932
35847
  });
35933
35848
  }
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);
35849
+ }
35850
+ class Map2dView {
35851
+ constructor(map2) {
35852
+ this.keysFirstAxis = new Map(map2.keysFirstAxis);
35853
+ this.keysSecondAxis = new Map(map2.keysSecondAxis);
35854
+ if (map2 instanceof Map2dView) {
35855
+ this.baseMap = map2.baseMap;
35856
+ }
35857
+ this.baseMap = map2;
35858
+ }
35859
+ serializeFirstAxis(key) {
35860
+ return this.baseMap.serializeFirstAxis(key);
35861
+ }
35862
+ serializeSecondAxis(key) {
35863
+ return this.baseMap.serializeSecondAxis(key);
35864
+ }
35865
+ deleteRow(key) {
35866
+ this.keysFirstAxis.delete(this.serializeFirstAxis(key));
35867
+ }
35868
+ get(keyFirstAxis, keySecondAxis) {
35869
+ const firstAxisKey = this.serializeFirstAxis(keyFirstAxis);
35870
+ const secondAxisKey = this.serializeSecondAxis(keySecondAxis);
35871
+ if (!this.keysFirstAxis.has(firstAxisKey) || !this.keysSecondAxis.has(secondAxisKey)) {
35872
+ return void 0;
35873
+ }
35874
+ return this.baseMap.get(keyFirstAxis, keySecondAxis);
35875
+ }
35876
+ set() {
35877
+ throw new Error("Cannot set value on a Map2dView");
35878
+ }
35879
+ getFirstAxisKeys() {
35880
+ return Array.from(this.keysFirstAxis.values());
35881
+ }
35882
+ getSecondAxisKeys() {
35883
+ return Array.from(this.keysSecondAxis.values());
35884
+ }
35885
+ getAsArray(fillEmptyWith) {
35886
+ return this.getFirstAxisKeys().map((firstAxisKey) => {
35887
+ return this.getSecondAxisKeys().map((secondAxisKey) => {
35888
+ return this.baseMap.get(firstAxisKey, secondAxisKey) ?? fillEmptyWith;
35941
35889
  });
35942
35890
  });
35943
- return copy;
35944
35891
  }
35892
+ getRow(key, fillEmptyWith) {
35893
+ const serializedKeyFirstAxis = this.serializeFirstAxis(key);
35894
+ if (!this.keysFirstAxis.has(serializedKeyFirstAxis)) {
35895
+ return [];
35896
+ }
35897
+ return this.baseMap.getRow(key, fillEmptyWith);
35898
+ }
35899
+ }
35900
+ function getFilteredMutationOverTimeData(data, overallMutationData, displayedSegments, displayedMutationTypes, proportionInterval) {
35901
+ const filteredData = new Map2dView(data);
35902
+ filterDisplayedSegments(displayedSegments, filteredData);
35903
+ filterMutationTypes(displayedMutationTypes, filteredData);
35904
+ filterProportion(filteredData, overallMutationData, proportionInterval);
35905
+ return filteredData;
35906
+ }
35907
+ function filterDisplayedSegments(displayedSegments, data) {
35908
+ displayedSegments.forEach((segment) => {
35909
+ if (!segment.checked) {
35910
+ data.getFirstAxisKeys().forEach((mutation) => {
35911
+ if (mutation.segment === segment.segment) {
35912
+ data.deleteRow(mutation);
35913
+ }
35914
+ });
35915
+ }
35916
+ });
35917
+ }
35918
+ function filterMutationTypes(displayedMutationTypes, data) {
35919
+ displayedMutationTypes.forEach((mutationType) => {
35920
+ if (!mutationType.checked) {
35921
+ data.getFirstAxisKeys().forEach((mutation) => {
35922
+ if (mutationType.type === mutation.type) {
35923
+ data.deleteRow(mutation);
35924
+ }
35925
+ });
35926
+ }
35927
+ });
35928
+ }
35929
+ function filterProportion(data, overallMutationData, proportionInterval) {
35930
+ const overallProportionsByMutation = overallMutationData.content.reduce(
35931
+ (acc, { mutation, proportion }) => ({
35932
+ ...acc,
35933
+ [mutation.toString()]: proportion
35934
+ }),
35935
+ {}
35936
+ );
35937
+ data.getFirstAxisKeys().forEach((mutation) => {
35938
+ const overallProportion = overallProportionsByMutation[mutation.toString()] || -1;
35939
+ if (overallProportion < proportionInterval.min || overallProportion > proportionInterval.max) {
35940
+ data.deleteRow(mutation);
35941
+ }
35942
+ });
35943
+ }
35944
+ const ColorScaleSelector = ({ colorScale, setColorScale }) => {
35945
+ const colorDisplayCss = `w-10 h-8 border border-gray-200 mx-2 text-xs flex items-center justify-center`;
35946
+ return /* @__PURE__ */ u$2("div", { className: "flex items-center", children: [
35947
+ /* @__PURE__ */ u$2(
35948
+ "div",
35949
+ {
35950
+ style: {
35951
+ backgroundColor: singleGraphColorRGBByName(colorScale.color, 0),
35952
+ color: "black"
35953
+ },
35954
+ className: colorDisplayCss,
35955
+ children: formatProportion(colorScale.min, 0)
35956
+ }
35957
+ ),
35958
+ /* @__PURE__ */ u$2("div", { className: "w-64", children: /* @__PURE__ */ u$2(
35959
+ MinMaxRangeSlider,
35960
+ {
35961
+ min: colorScale.min * 100,
35962
+ max: colorScale.max * 100,
35963
+ setMin: (percentage) => {
35964
+ setColorScale({ ...colorScale, min: percentage / 100 });
35965
+ },
35966
+ setMax: (percentage) => {
35967
+ setColorScale({ ...colorScale, max: percentage / 100 });
35968
+ }
35969
+ }
35970
+ ) }),
35971
+ /* @__PURE__ */ u$2(
35972
+ "div",
35973
+ {
35974
+ style: {
35975
+ backgroundColor: singleGraphColorRGBByName(colorScale.color, 1),
35976
+ color: "white"
35977
+ },
35978
+ className: colorDisplayCss,
35979
+ children: formatProportion(colorScale.max, 0)
35980
+ }
35981
+ )
35982
+ ] });
35983
+ };
35984
+ const getColorWithingScale = (value, colorScale) => {
35985
+ if (colorScale.min === colorScale.max) {
35986
+ return singleGraphColorRGBByName(colorScale.color, 0);
35987
+ }
35988
+ const colorRange = colorScale.max - colorScale.min;
35989
+ const alpha2 = (value - colorScale.min) / colorRange;
35990
+ return singleGraphColorRGBByName(colorScale.color, alpha2);
35991
+ };
35992
+ const getTextColorForScale = (value, colorScale) => {
35993
+ if (colorScale.min === colorScale.max) {
35994
+ return "black";
35995
+ }
35996
+ const colorRange = colorScale.max - colorScale.min;
35997
+ const alpha2 = (value - colorScale.min) / colorRange;
35998
+ return alpha2 <= 0.5 ? "black" : "white";
35999
+ };
36000
+ function getPositionCss(position) {
36001
+ switch (position) {
36002
+ case "top":
36003
+ return "bottom-full translate-x-[-50%] left-1/2 mb-1";
36004
+ case "top-start":
36005
+ return "bottom-full mr-1 mb-1";
36006
+ case "top-end":
36007
+ return "bottom-full right-0 ml-1 mb-1";
36008
+ case "bottom":
36009
+ return "top-full translate-x-[-50%] left-1/2 mt-1";
36010
+ case "bottom-start":
36011
+ return "mr-1 mt-1";
36012
+ case "bottom-end":
36013
+ return "right-0 ml-1 mt-1";
36014
+ case "left":
36015
+ return "right-full translate-y-[-50%] top-1/2 mr-1";
36016
+ case "right":
36017
+ return "left-full translate-y-[-50%] top-1/2 ml-1";
36018
+ case void 0:
36019
+ return "";
36020
+ }
36021
+ }
36022
+ const Tooltip = ({ children, content, position = "bottom" }) => {
36023
+ return /* @__PURE__ */ u$2("div", { className: "relative w-full h-full", children: [
36024
+ /* @__PURE__ */ u$2("div", { className: "peer w-full h-full", children }),
36025
+ /* @__PURE__ */ u$2(
36026
+ "div",
36027
+ {
36028
+ className: `absolute z-10 w-max bg-white p-4 border border-gray-200 rounded-md invisible peer-hover:visible ${getPositionCss(position)}`,
36029
+ children: content
36030
+ }
36031
+ )
36032
+ ] });
36033
+ };
36034
+ const MAX_NUMBER_OF_GRID_ROWS = 100;
36035
+ const MUTATION_CELL_WIDTH_REM = 8;
36036
+ const MutationsOverTimeGrid = ({ data, colorScale }) => {
36037
+ const allMutations = data.getFirstAxisKeys().sort(sortSubstitutionsAndDeletions);
36038
+ const shownMutations = allMutations.slice(0, MAX_NUMBER_OF_GRID_ROWS);
36039
+ const dates = data.getSecondAxisKeys().sort((a2, b3) => compareTemporal(a2, b3));
36040
+ const [showProportionText, setShowProportionText] = h$1(false);
36041
+ const gridRef = A$1(null);
36042
+ useShowProportion(gridRef, dates.length, setShowProportionText);
36043
+ return /* @__PURE__ */ u$2(k$1, { children: [
36044
+ allMutations.length > MAX_NUMBER_OF_GRID_ROWS && /* @__PURE__ */ u$2("div", { className: "pl-2", children: [
36045
+ "Showing ",
36046
+ MAX_NUMBER_OF_GRID_ROWS,
36047
+ " of ",
36048
+ allMutations.length,
36049
+ " mutations. You can narrow the filter to reduce the number of mutations."
36050
+ ] }),
36051
+ /* @__PURE__ */ u$2(
36052
+ "div",
36053
+ {
36054
+ ref: gridRef,
36055
+ style: {
36056
+ display: "grid",
36057
+ gridTemplateRows: `repeat(${shownMutations.length}, 24px)`,
36058
+ gridTemplateColumns: `${MUTATION_CELL_WIDTH_REM}rem repeat(${dates.length}, minmax(0.05rem, 1fr))`
36059
+ },
36060
+ children: shownMutations.map((mutation, rowIndex) => {
36061
+ return /* @__PURE__ */ u$2(k$1, { children: [
36062
+ /* @__PURE__ */ u$2(
36063
+ "div",
36064
+ {
36065
+ style: { gridRowStart: rowIndex + 1, gridColumnStart: 1 },
36066
+ children: /* @__PURE__ */ u$2(MutationCell, { mutation })
36067
+ },
36068
+ `mutation-${mutation.toString()}`
36069
+ ),
36070
+ dates.map((date, columnIndex) => {
36071
+ const value = data.get(mutation, date) ?? { proportion: 0, count: 0 };
36072
+ const tooltipPosition = getTooltipPosition(
36073
+ rowIndex,
36074
+ shownMutations.length,
36075
+ columnIndex,
36076
+ dates.length
36077
+ );
36078
+ return /* @__PURE__ */ u$2(
36079
+ "div",
36080
+ {
36081
+ style: { gridRowStart: rowIndex + 1, gridColumnStart: columnIndex + 2 },
36082
+ children: /* @__PURE__ */ u$2(
36083
+ ProportionCell,
36084
+ {
36085
+ value,
36086
+ date,
36087
+ mutation,
36088
+ tooltipPosition,
36089
+ showProportionText,
36090
+ colorScale
36091
+ }
36092
+ )
36093
+ },
36094
+ `${mutation.toString()}-${date.toString()}`
36095
+ );
36096
+ })
36097
+ ] }, `fragment-${mutation.toString()}`);
36098
+ })
36099
+ }
36100
+ )
36101
+ ] });
36102
+ };
36103
+ function useShowProportion(gridRef, girdColumns, setShowProportionText) {
36104
+ y$1(() => {
36105
+ const checkWidth = () => {
36106
+ if (gridRef.current) {
36107
+ const width = gridRef.current.getBoundingClientRect().width;
36108
+ const widthPerDate = (width - remToPx(MUTATION_CELL_WIDTH_REM)) / girdColumns;
36109
+ const maxWidthProportionText = 28;
36110
+ setShowProportionText(widthPerDate > maxWidthProportionText);
36111
+ }
36112
+ };
36113
+ checkWidth();
36114
+ window.addEventListener("resize", checkWidth);
36115
+ return () => {
36116
+ window.removeEventListener("resize", checkWidth);
36117
+ };
36118
+ }, [girdColumns, gridRef, setShowProportionText]);
36119
+ }
36120
+ const remToPx = (rem) => rem * parseFloat(getComputedStyle(document.documentElement).fontSize);
36121
+ function getTooltipPosition(rowIndex, rows, columnIndex, columns) {
36122
+ const tooltipX = rowIndex < rows / 2 ? "bottom" : "top";
36123
+ const tooltipY = columnIndex < columns / 2 ? "start" : "end";
36124
+ return `${tooltipX}-${tooltipY}`;
35945
36125
  }
36126
+ const ProportionCell = ({ value, mutation, date, tooltipPosition, showProportionText, colorScale }) => {
36127
+ const tooltipContent = /* @__PURE__ */ u$2("div", { children: [
36128
+ /* @__PURE__ */ u$2("p", { children: /* @__PURE__ */ u$2("span", { className: "font-bold", children: date.englishName() }) }),
36129
+ /* @__PURE__ */ u$2("p", { children: [
36130
+ "(",
36131
+ timeIntervalDisplay(date),
36132
+ ")"
36133
+ ] }),
36134
+ /* @__PURE__ */ u$2("p", { children: mutation.code }),
36135
+ /* @__PURE__ */ u$2("p", { children: [
36136
+ "Proportion: ",
36137
+ formatProportion(value.proportion)
36138
+ ] }),
36139
+ /* @__PURE__ */ u$2("p", { children: [
36140
+ "Count: ",
36141
+ value.count
36142
+ ] })
36143
+ ] });
36144
+ 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(
36145
+ "div",
36146
+ {
36147
+ style: {
36148
+ backgroundColor: getColorWithingScale(value.proportion, colorScale),
36149
+ color: getTextColorForScale(value.proportion, colorScale)
36150
+ },
36151
+ className: `w-full h-full text-center hover:font-bold text-xs group`,
36152
+ children: showProportionText ? formatProportion(value.proportion, 0) : void 0
36153
+ }
36154
+ ) }) });
36155
+ };
36156
+ const timeIntervalDisplay = (date) => {
36157
+ if (date instanceof YearMonthDay) {
36158
+ return date.toString();
36159
+ }
36160
+ return `${date.firstDay.toString()} - ${date.lastDay.toString()}`;
36161
+ };
36162
+ const MutationCell = ({ mutation }) => {
36163
+ return /* @__PURE__ */ u$2("div", { className: "text-center", children: mutation.toString() });
36164
+ };
35946
36165
  const MAX_NUMBER_OF_GRID_COLUMNS = 200;
35947
36166
  async function queryOverallMutationData(lapisFilter, sequenceType, lapis, signal) {
35948
36167
  return fetchAndPrepareSubstitutionsOrDeletions(lapisFilter, sequenceType).evaluate(lapis, signal);
@@ -36014,7 +36233,7 @@ function fetchAndPrepareSubstitutionsOrDeletions(filter, sequenceType) {
36014
36233
  return new FetchSubstitutionsOrDeletionsOperator(filter, sequenceType, 1e-3);
36015
36234
  }
36016
36235
  function groupByMutation(data) {
36017
- const dataArray = new Map2d(
36236
+ const dataArray = new Map2dBase(
36018
36237
  (mutation) => mutation.code,
36019
36238
  (date) => date.toString()
36020
36239
  );
@@ -36043,7 +36262,7 @@ const ColorScaleSelectorDropdown = ({
36043
36262
  colorScale,
36044
36263
  setColorScale
36045
36264
  }) => {
36046
- return /* @__PURE__ */ u$2(Dropdown, { buttonTitle: `Color scale`, placement: "bottom-start", children: /* @__PURE__ */ u$2(ColorScaleSelector, { colorScale, setColorScale }) });
36265
+ 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
36266
  };
36048
36267
  const MutationsOverTime = ({ width, height, ...innerProps }) => {
36049
36268
  const size = { height, width };