@genspectrum/dashboard-components 0.6.4 → 0.6.6

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 (31) hide show
  1. package/custom-elements.json +36 -4
  2. package/dist/dashboard-components.js +181 -55
  3. package/dist/dashboard-components.js.map +1 -1
  4. package/dist/genspectrum-components.d.ts +13 -7
  5. package/dist/style.css +53 -2
  6. package/package.json +1 -1
  7. package/src/constants.ts +1 -0
  8. package/src/preact/components/tooltip.stories.tsx +12 -2
  9. package/src/preact/components/tooltip.tsx +37 -13
  10. package/src/preact/mutationsOverTime/__mockData__/aggregated_byDay.json +38 -0
  11. package/src/preact/mutationsOverTime/__mockData__/aggregated_byWeek.json +122 -0
  12. package/src/preact/mutationsOverTime/__mockData__/aggregated_tooManyMutations.json +1470 -0
  13. package/src/preact/mutationsOverTime/__mockData__/aminoAcidMutations_20_01_2024.json +6778 -0
  14. package/src/preact/mutationsOverTime/__mockData__/aminoAcidMutations_21_01_2024.json +7129 -0
  15. package/src/preact/mutationsOverTime/__mockData__/aminoAcidMutations_22_01_2024.json +4681 -0
  16. package/src/preact/mutationsOverTime/__mockData__/aminoAcidMutations_23_01_2024.json +10738 -0
  17. package/src/preact/mutationsOverTime/__mockData__/aminoAcidMutations_24_01_2024.json +11710 -0
  18. package/src/preact/mutationsOverTime/__mockData__/aminoAcidMutations_25_01_2024.json +11557 -0
  19. package/src/preact/mutationsOverTime/__mockData__/aminoAcidMutations_26_01_2024.json +8596 -0
  20. package/src/preact/mutationsOverTime/__mockData__/nucleotideMutations_tooManyMutations.json +16453 -0
  21. package/src/preact/mutationsOverTime/__mockData__/nucleotideMutations_week3_2024.json +8812 -0
  22. package/src/preact/mutationsOverTime/__mockData__/nucleotideMutations_week4_2024.json +9730 -0
  23. package/src/preact/mutationsOverTime/__mockData__/nucleotideMutations_week5_2024.json +9865 -0
  24. package/src/preact/mutationsOverTime/__mockData__/nucleotideMutations_week6_2024.json +11314 -0
  25. package/src/preact/mutationsOverTime/mutations-over-time-grid.tsx +62 -43
  26. package/src/preact/mutationsOverTime/mutations-over-time.stories.tsx +62 -8
  27. package/src/preact/mutationsOverTime/mutations-over-time.tsx +32 -8
  28. package/src/utils/temporal.spec.ts +3 -4
  29. package/src/utils/temporal.ts +9 -4
  30. package/src/web-components/visualization/gs-mutations-over-time.stories.ts +254 -2
  31. package/src/web-components/visualization/gs-mutations-over-time.tsx +11 -5
@@ -1791,11 +1791,27 @@
1791
1791
  },
1792
1792
  {
1793
1793
  "kind": "variable",
1794
- "name": "Default",
1794
+ "name": "ByMonth",
1795
1795
  "type": {
1796
1796
  "text": "StoryObj<Required<MutationsOverTimeProps>>"
1797
1797
  },
1798
1798
  "default": "{ ...Template, parameters: { fetchMock: { mocks: [ { matcher: { name: 'aggregated_dates', url: AGGREGATED_ENDPOINT, body: { dateFrom: '2024-01-15', dateTo: '2024-07-10', fields: ['date'], pangoLineage: 'JN.1*', }, }, response: { status: 200, body: aggregated_date, }, }, { matcher: { name: 'nucleotideMutations_01', url: NUCLEOTIDE_MUTATIONS_ENDPOINT, body: { pangoLineage: 'JN.1*', dateFrom: '2024-01-01', dateTo: '2024-01-31', minProportion: 0.001, }, }, response: { status: 200, body: nucleotideMutation_01, }, }, { matcher: { name: 'nucleotideMutations_02', url: NUCLEOTIDE_MUTATIONS_ENDPOINT, body: { pangoLineage: 'JN.1*', dateFrom: '2024-02-01', dateTo: '2024-02-29', minProportion: 0.001, }, }, response: { status: 200, body: nucleotideMutation_02, }, }, { matcher: { name: 'nucleotideMutations_03', url: NUCLEOTIDE_MUTATIONS_ENDPOINT, body: { pangoLineage: 'JN.1*', dateFrom: '2024-03-01', dateTo: '2024-03-31', minProportion: 0.001, }, response: { status: 200, body: nucleotideMutation_03, }, }, }, { matcher: { name: 'nucleotideMutations_04', url: NUCLEOTIDE_MUTATIONS_ENDPOINT, body: { pangoLineage: 'JN.1*', dateFrom: '2024-04-01', dateTo: '2024-04-30', minProportion: 0.001, }, response: { status: 200, body: nucleotideMutation_04, }, }, }, { matcher: { name: 'nucleotideMutations_05', url: NUCLEOTIDE_MUTATIONS_ENDPOINT, body: { pangoLineage: 'JN.1*', dateFrom: '2024-05-01', dateTo: '2024-05-31', minProportion: 0.001, }, response: { status: 200, body: nucleotideMutation_05, }, }, }, { matcher: { name: 'nucleotideMutations_06', url: NUCLEOTIDE_MUTATIONS_ENDPOINT, body: { pangoLineage: 'JN.1*', dateFrom: '2024-06-01', dateTo: '2024-06-30', minProportion: 0.001, }, response: { status: 200, body: nucleotideMutation_06, }, }, }, { matcher: { name: 'nucleotideMutations_07', url: NUCLEOTIDE_MUTATIONS_ENDPOINT, body: { pangoLineage: 'JN.1*', dateFrom: '2024-07-01', dateTo: '2024-07-31', minProportion: 0.001, }, response: { status: 200, body: nucleotideMutation_07, }, }, }, ], }, }, }"
1799
+ },
1800
+ {
1801
+ "kind": "variable",
1802
+ "name": "ByWeek",
1803
+ "type": {
1804
+ "text": "StoryObj<Required<MutationsOverTimeProps>>"
1805
+ },
1806
+ "default": "{ ...Template, args: { ...Template.args, lapisFilter: { pangoLineage: 'JN.1*', dateFrom: '2024-01-15', dateTo: '2024-02-11' }, granularity: 'week', }, parameters: { fetchMock: { mocks: [ { matcher: { name: 'aggregated_dates', url: AGGREGATED_ENDPOINT, body: { dateFrom: '2024-01-15', dateTo: '2024-02-11', fields: ['date'], pangoLineage: 'JN.1*', }, }, response: { status: 200, body: aggregated_byWeek, }, }, { matcher: { name: 'nucleotideMutation_week3', url: NUCLEOTIDE_MUTATIONS_ENDPOINT, body: { pangoLineage: 'JN.1*', dateFrom: '2024-01-15', dateTo: '2024-01-21', minProportion: 0.001, }, }, response: { status: 200, body: nucleotideMutation_week3, }, }, { matcher: { name: 'nucleotideMutation_week4', url: NUCLEOTIDE_MUTATIONS_ENDPOINT, body: { pangoLineage: 'JN.1*', dateFrom: '2024-01-22', dateTo: '2024-01-28', minProportion: 0.001, }, }, response: { status: 200, body: nucleotideMutation_week4, }, }, { matcher: { name: 'nucleotideMutation_week5', url: NUCLEOTIDE_MUTATIONS_ENDPOINT, body: { pangoLineage: 'JN.1*', dateFrom: '2024-01-29', dateTo: '2024-02-04', minProportion: 0.001, }, }, response: { status: 200, body: nucleotideMutation_week5, }, }, { matcher: { name: 'nucleotideMutation_week6', url: NUCLEOTIDE_MUTATIONS_ENDPOINT, body: { pangoLineage: 'JN.1*', dateFrom: '2024-02-05', dateTo: '2024-02-11', minProportion: 0.001, }, }, response: { status: 200, body: nucleotideMutation_week6, }, }, ], }, }, }"
1807
+ },
1808
+ {
1809
+ "kind": "variable",
1810
+ "name": "AminoAcidMutationsByDay",
1811
+ "type": {
1812
+ "text": "StoryObj<Required<MutationsOverTimeProps>>"
1813
+ },
1814
+ "default": "{ ...Template, args: { ...Template.args, lapisFilter: { pangoLineage: 'JN.1*', dateFrom: '2024-01-20', dateTo: '2024-01-26' }, granularity: 'day', sequenceType: 'amino acid', }, parameters: { fetchMock: { mocks: [ { matcher: { name: 'aggregated_byDay', url: AGGREGATED_ENDPOINT, body: { pangoLineage: 'JN.1*', dateFrom: '2024-01-20', dateTo: '2024-01-26', fields: ['date'] }, }, response: { status: 200, body: aggregated_byDay, }, }, { matcher: { name: 'aminoAcidMutations_20_01_2024', url: AMINO_ACID_MUTATIONS_ENDPOINT, body: { pangoLineage: 'JN.1*', dateFrom: '2024-01-20', dateTo: '2024-01-20', minProportion: 0.001, }, }, response: { status: 200, body: aminoAcidMutations_20_01_2024, }, }, { matcher: { name: 'aminoAcidMutations_21_01_2024', url: AMINO_ACID_MUTATIONS_ENDPOINT, body: { pangoLineage: 'JN.1*', dateFrom: '2024-01-21', dateTo: '2024-01-21', minProportion: 0.001, }, }, response: { status: 200, body: aminoAcidMutations_21_01_2024, }, }, { matcher: { name: 'aminoAcidMutations_22_01_2024', url: AMINO_ACID_MUTATIONS_ENDPOINT, body: { pangoLineage: 'JN.1*', dateFrom: '2024-01-22', dateTo: '2024-01-22', minProportion: 0.001, }, }, response: { status: 200, body: aminoAcidMutations_22_01_2024, }, }, { matcher: { name: 'aminoAcidMutations_23_01_2024', url: AMINO_ACID_MUTATIONS_ENDPOINT, body: { pangoLineage: 'JN.1*', dateFrom: '2024-01-23', dateTo: '2024-01-23', minProportion: 0.001, }, }, response: { status: 200, body: aminoAcidMutations_23_01_2024, }, }, { matcher: { name: 'aminoAcidMutations_24_01_2024', url: AMINO_ACID_MUTATIONS_ENDPOINT, body: { pangoLineage: 'JN.1*', dateFrom: '2024-01-24', dateTo: '2024-01-24', minProportion: 0.001, }, }, response: { status: 200, body: aminoAcidMutations_24_01_2024, }, }, { matcher: { name: 'aminoAcidMutations_25_01_2024', url: AMINO_ACID_MUTATIONS_ENDPOINT, body: { pangoLineage: 'JN.1*', dateFrom: '2024-01-25', dateTo: '2024-01-25', minProportion: 0.001, }, }, response: { status: 200, body: aminoAcidMutations_25_01_2024, }, }, { matcher: { name: 'aminoAcidMutations_26_01_2024', url: AMINO_ACID_MUTATIONS_ENDPOINT, body: { pangoLineage: 'JN.1*', dateFrom: '2024-01-26', dateTo: '2024-01-26', minProportion: 0.001, }, }, response: { status: 200, body: aminoAcidMutations_26_01_2024, }, }, ], }, }, }"
1799
1815
  }
1800
1816
  ],
1801
1817
  "exports": [
@@ -1809,9 +1825,25 @@
1809
1825
  },
1810
1826
  {
1811
1827
  "kind": "js",
1812
- "name": "Default",
1828
+ "name": "ByMonth",
1813
1829
  "declaration": {
1814
- "name": "Default",
1830
+ "name": "ByMonth",
1831
+ "module": "src/web-components/visualization/gs-mutations-over-time.stories.ts"
1832
+ }
1833
+ },
1834
+ {
1835
+ "kind": "js",
1836
+ "name": "ByWeek",
1837
+ "declaration": {
1838
+ "name": "ByWeek",
1839
+ "module": "src/web-components/visualization/gs-mutations-over-time.stories.ts"
1840
+ }
1841
+ },
1842
+ {
1843
+ "kind": "js",
1844
+ "name": "AminoAcidMutationsByDay",
1845
+ "declaration": {
1846
+ "name": "AminoAcidMutationsByDay",
1815
1847
  "module": "src/web-components/visualization/gs-mutations-over-time.stories.ts"
1816
1848
  }
1817
1849
  }
@@ -1823,7 +1855,7 @@
1823
1855
  "declarations": [
1824
1856
  {
1825
1857
  "kind": "class",
1826
- "description": "## Context\n\nThis component displays mutations (substitutions and deletions) over time for a dataset selected by a LAPIS filter.\nThe shown date range is determined by the date field in the LAPIS filter.\nIf the date field is not set, the date range is determined by all available dates in the dataset.\n\n## Views\n\n### Grid View\n\nThe grid view shows the proportion for each mutation over date ranges.\n\nThe grid will show at max 100 rows and 200 columns for browser performance reasons.\nMore data might make the browser unresponsive.\nIf the numbers are exceeded, an error message will be shown.\nIn both cases, the `lapisFilter` should be narrowed down to reduce the number of mutations or date ranges.\nThe number of date ranges can also be reduced by selecting a larger granularity (months instead of weeks).",
1858
+ "description": "## Context\n\nThis component displays mutations (substitutions and deletions) over time for a dataset selected by a LAPIS filter.\nThe shown date range is determined by the date field in the LAPIS filter.\nIf the date field is not set, the date range is determined by all available dates in the dataset.\n\n## Views\n\n### Grid View\n\nThe grid view shows the proportion for each mutation over date ranges.\n\nThe grid limits the number of rows columns for browser performance reasons.\nToo much data might make the browser unresponsive.\n\nThe number of columns is limited to 200.\nIf this number are exceeded, an error message will be shown.\nIt is your responsibility to make sure that this does not happen.\nDepending on the selected date range in the `lapisFilter`, you can adapt the granularity accordingly\n(e.g. use months instead of days).\n\nThe number of rows is limited to 100.\nIf there are more, the component will only show 100 mutations and notify the user.",
1827
1859
  "name": "MutationsOverTimeComponent",
1828
1860
  "members": [
1829
1861
  {
@@ -4202,6 +4202,9 @@ input.tab:checked + .tab-content,
4202
4202
  .visible {
4203
4203
  visibility: visible;
4204
4204
  }
4205
+ .invisible {
4206
+ visibility: hidden;
4207
+ }
4205
4208
  .static {
4206
4209
  position: static;
4207
4210
  }
@@ -4217,18 +4220,39 @@ input.tab:checked + .tab-content,
4217
4220
  .-top-3 {
4218
4221
  top: -0.75rem;
4219
4222
  }
4223
+ .bottom-full {
4224
+ bottom: 100%;
4225
+ }
4220
4226
  .left-0 {
4221
4227
  left: 0px;
4222
4228
  }
4229
+ .left-1\\/2 {
4230
+ left: 50%;
4231
+ }
4232
+ .left-full {
4233
+ left: 100%;
4234
+ }
4235
+ .right-0 {
4236
+ right: 0px;
4237
+ }
4223
4238
  .right-2 {
4224
4239
  right: 0.5rem;
4225
4240
  }
4241
+ .right-full {
4242
+ right: 100%;
4243
+ }
4226
4244
  .top-0 {
4227
4245
  top: 0px;
4228
4246
  }
4247
+ .top-1\\/2 {
4248
+ top: 50%;
4249
+ }
4229
4250
  .top-2 {
4230
4251
  top: 0.5rem;
4231
4252
  }
4253
+ .top-full {
4254
+ top: 100%;
4255
+ }
4232
4256
  .z-10 {
4233
4257
  z-index: 10;
4234
4258
  }
@@ -4257,18 +4281,30 @@ input.tab:checked + .tab-content,
4257
4281
  margin-top: 1rem;
4258
4282
  margin-bottom: 1rem;
4259
4283
  }
4284
+ .mb-1 {
4285
+ margin-bottom: 0.25rem;
4286
+ }
4260
4287
  .mb-2 {
4261
4288
  margin-bottom: 0.5rem;
4262
4289
  }
4290
+ .ml-1 {
4291
+ margin-left: 0.25rem;
4292
+ }
4263
4293
  .ml-2\\.5 {
4264
4294
  margin-left: 0.625rem;
4265
4295
  }
4266
4296
  .ml-3 {
4267
4297
  margin-left: 0.75rem;
4268
4298
  }
4299
+ .mr-1 {
4300
+ margin-right: 0.25rem;
4301
+ }
4269
4302
  .mr-2 {
4270
4303
  margin-right: 0.5rem;
4271
4304
  }
4305
+ .mt-1 {
4306
+ margin-top: 0.25rem;
4307
+ }
4272
4308
  .mt-4 {
4273
4309
  margin-top: 1rem;
4274
4310
  }
@@ -4327,6 +4363,14 @@ input.tab:checked + .tab-content,
4327
4363
  .grow {
4328
4364
  flex-grow: 1;
4329
4365
  }
4366
+ .translate-x-\\[-50\\%\\] {
4367
+ --tw-translate-x: -50%;
4368
+ transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
4369
+ }
4370
+ .translate-y-\\[-50\\%\\] {
4371
+ --tw-translate-y: -50%;
4372
+ transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
4373
+ }
4330
4374
  .resize {
4331
4375
  resize: both;
4332
4376
  }
@@ -4427,6 +4471,10 @@ input.tab:checked + .tab-content,
4427
4471
  --tw-border-opacity: 1;
4428
4472
  border-color: rgb(239 68 68 / var(--tw-border-opacity));
4429
4473
  }
4474
+ .bg-red-200 {
4475
+ --tw-bg-opacity: 1;
4476
+ background-color: rgb(254 202 202 / var(--tw-bg-opacity));
4477
+ }
4430
4478
  .bg-white {
4431
4479
  --tw-bg-opacity: 1;
4432
4480
  background-color: rgb(255 255 255 / var(--tw-bg-opacity));
@@ -4464,6 +4512,9 @@ input.tab:checked + .tab-content,
4464
4512
  padding-top: 1rem;
4465
4513
  padding-bottom: 1rem;
4466
4514
  }
4515
+ .pl-2 {
4516
+ padding-left: 0.5rem;
4517
+ }
4467
4518
  .text-center {
4468
4519
  text-align: center;
4469
4520
  }
@@ -4586,8 +4637,8 @@ input.tab:checked + .tab-content,
4586
4637
  --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(0px + var(--tw-ring-offset-width)) var(--tw-ring-color);
4587
4638
  box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000);
4588
4639
  }
4589
- .peer:hover ~ .peer-hover\\:block {
4590
- display: block;
4640
+ .peer:hover ~ .peer-hover\\:visible {
4641
+ visibility: visible;
4591
4642
  }`;
4592
4643
  var __defProp$c = Object.defineProperty;
4593
4644
  var __decorateClass$c = (decorators, target, key, kind) => {
@@ -5889,6 +5940,7 @@ const isoWeek = function(o2, c2, d2) {
5889
5940
  };
5890
5941
  dayjs.extend(isoWeek);
5891
5942
  dayjs.extend(advancedFormat);
5943
+ const FORMAT_ISO_WEEK_YEAR_WEEK = "GGGG-[W]WW";
5892
5944
  const _TemporalCache = class _TemporalCache {
5893
5945
  constructor() {
5894
5946
  this.yearMonthDayCache = /* @__PURE__ */ new Map();
@@ -5957,7 +6009,7 @@ class YearMonthDay {
5957
6009
  return this.cache.getYearMonth(this.dayjs.format("YYYY-MM"));
5958
6010
  }
5959
6011
  get week() {
5960
- return this.cache.getYearWeek(this.dayjs.format("GGGG-WW"));
6012
+ return this.cache.getYearWeek(this.dayjs.format(FORMAT_ISO_WEEK_YEAR_WEEK));
5961
6013
  }
5962
6014
  addDays(days) {
5963
6015
  const date = this.dayjs.add(days, "day");
@@ -5979,7 +6031,7 @@ class YearWeek {
5979
6031
  this.cache = cache;
5980
6032
  }
5981
6033
  get text() {
5982
- return this.firstDay.dayjs.format("YYYY-WW");
6034
+ return this.firstDay.dayjs.format(FORMAT_ISO_WEEK_YEAR_WEEK);
5983
6035
  }
5984
6036
  toString() {
5985
6037
  return this.text;
@@ -6001,14 +6053,14 @@ class YearWeek {
6001
6053
  }
6002
6054
  addWeeks(weeks) {
6003
6055
  const date = this.firstDay.dayjs.add(weeks, "week");
6004
- const s2 = date.format("YYYY-WW");
6056
+ const s2 = date.format(FORMAT_ISO_WEEK_YEAR_WEEK);
6005
6057
  return this.cache.getYearWeek(s2);
6006
6058
  }
6007
6059
  minus(other) {
6008
6060
  return this.firstDay.dayjs.diff(other.firstDay.dayjs, "week");
6009
6061
  }
6010
6062
  static parse(s2, cache) {
6011
- const [year, week] = s2.split("-").map((s22) => parseInt(s22, 10));
6063
+ const [year, week] = s2.split("-W").map((s22) => parseInt(s22, 10));
6012
6064
  return new YearWeek(year, week, cache);
6013
6065
  }
6014
6066
  }
@@ -7914,60 +7966,108 @@ function filterProportion(data, proportionInterval) {
7914
7966
  }
7915
7967
  });
7916
7968
  }
7917
- const Tooltip = ({ children, content }) => {
7918
- const referenceRef = A(null);
7919
- const floatingRef = A(null);
7920
- useFloatingUi(referenceRef, floatingRef, [offset(5), shift(), flip()]);
7969
+ function getPositionCss(position) {
7970
+ switch (position) {
7971
+ case "top":
7972
+ return "bottom-full translate-x-[-50%] left-1/2 mb-1";
7973
+ case "top-start":
7974
+ return "bottom-full mr-1 mb-1";
7975
+ case "top-end":
7976
+ return "bottom-full right-0 ml-1 mb-1";
7977
+ case "bottom":
7978
+ return "top-full translate-x-[-50%] left-1/2 mt-1";
7979
+ case "bottom-start":
7980
+ return "mr-1 mt-1";
7981
+ case "bottom-end":
7982
+ return "right-0 ml-1 mt-1";
7983
+ case "left":
7984
+ return "right-full translate-y-[-50%] top-1/2 mr-1";
7985
+ case "right":
7986
+ return "left-full translate-y-[-50%] top-1/2 ml-1";
7987
+ case void 0:
7988
+ return "";
7989
+ }
7990
+ }
7991
+ const Tooltip = ({ children, content, position = "bottom" }) => {
7921
7992
  return /* @__PURE__ */ u$1("div", { className: "relative", children: [
7922
- /* @__PURE__ */ u$1("div", { className: "peer", ref: referenceRef, children }),
7923
- /* @__PURE__ */ u$1("div", { ref: floatingRef, className: `${dropdownClass} hidden peer-hover:block`, children: content })
7993
+ /* @__PURE__ */ u$1("div", { className: "peer", children }),
7994
+ /* @__PURE__ */ u$1(
7995
+ "div",
7996
+ {
7997
+ className: `absolute z-10 w-max bg-white p-4 border border-gray-200 rounded-md invisible peer-hover:visible ${getPositionCss(position)}`,
7998
+ children: content
7999
+ }
8000
+ )
7924
8001
  ] });
7925
8002
  };
7926
8003
  const MAX_NUMBER_OF_GRID_ROWS = 100;
7927
8004
  const MutationsOverTimeGrid = ({ data }) => {
7928
- const mutations = data.getFirstAxisKeys();
7929
- if (mutations.length > MAX_NUMBER_OF_GRID_ROWS) {
7930
- throw new UserFacingError(
7931
- "Too many mutations",
7932
- `The dataset contains ${mutations.length} mutations. Please adapt the filters to reduce the number to below ${MAX_NUMBER_OF_GRID_ROWS}.`
7933
- );
7934
- }
8005
+ const allMutations = data.getFirstAxisKeys();
8006
+ const shownMutations = allMutations.slice(0, MAX_NUMBER_OF_GRID_ROWS);
7935
8007
  const dates = data.getSecondAxisKeys().sort((a2, b3) => compareTemporal(a2, b3));
7936
- return /* @__PURE__ */ u$1(
7937
- "div",
7938
- {
7939
- style: {
7940
- display: "grid",
7941
- gridTemplateRows: `repeat(${mutations.length}, 24px)`,
7942
- gridTemplateColumns: `8rem repeat(${dates.length}, minmax(1.5rem, 1fr))`
7943
- },
7944
- children: mutations.map((mutation, i2) => {
7945
- return /* @__PURE__ */ u$1(Fragment, { children: [
7946
- /* @__PURE__ */ u$1(
7947
- "div",
7948
- {
7949
- style: { gridRowStart: i2 + 1, gridColumnStart: 1 },
7950
- children: /* @__PURE__ */ u$1(MutationCell, { mutation })
7951
- },
7952
- `mutation-${mutation.toString()}`
7953
- ),
7954
- dates.map((date, j2) => {
7955
- const value = data.get(mutation, date) ?? { proportion: 0, count: 0 };
7956
- return /* @__PURE__ */ u$1(
8008
+ return /* @__PURE__ */ u$1(Fragment, { children: [
8009
+ allMutations.length > MAX_NUMBER_OF_GRID_ROWS && /* @__PURE__ */ u$1("div", { className: "pl-2", children: [
8010
+ "Showing ",
8011
+ MAX_NUMBER_OF_GRID_ROWS,
8012
+ " of ",
8013
+ allMutations.length,
8014
+ " mutations. You can narrow the filter to reduce the number of mutations."
8015
+ ] }),
8016
+ /* @__PURE__ */ u$1(
8017
+ "div",
8018
+ {
8019
+ style: {
8020
+ display: "grid",
8021
+ gridTemplateRows: `repeat(${shownMutations.length}, 24px)`,
8022
+ gridTemplateColumns: `8rem repeat(${dates.length}, minmax(1.5rem, 1fr))`
8023
+ },
8024
+ children: shownMutations.map((mutation, rowIndex) => {
8025
+ return /* @__PURE__ */ u$1(Fragment, { children: [
8026
+ /* @__PURE__ */ u$1(
7957
8027
  "div",
7958
8028
  {
7959
- style: { gridRowStart: i2 + 1, gridColumnStart: j2 + 2 },
7960
- children: /* @__PURE__ */ u$1(ProportionCell, { value, date, mutation })
8029
+ style: { gridRowStart: rowIndex + 1, gridColumnStart: 1 },
8030
+ children: /* @__PURE__ */ u$1(MutationCell, { mutation })
7961
8031
  },
7962
- `${mutation.toString()}-${date.toString()}`
7963
- );
7964
- })
7965
- ] }, `fragment-${mutation.toString()}`);
7966
- })
7967
- }
7968
- );
8032
+ `mutation-${mutation.toString()}`
8033
+ ),
8034
+ dates.map((date, columnIndex) => {
8035
+ const value = data.get(mutation, date) ?? { proportion: 0, count: 0 };
8036
+ const tooltipPosition = getTooltipPosition(
8037
+ rowIndex,
8038
+ shownMutations.length,
8039
+ columnIndex,
8040
+ dates.length
8041
+ );
8042
+ return /* @__PURE__ */ u$1(
8043
+ "div",
8044
+ {
8045
+ style: { gridRowStart: rowIndex + 1, gridColumnStart: columnIndex + 2 },
8046
+ children: /* @__PURE__ */ u$1(
8047
+ ProportionCell,
8048
+ {
8049
+ value,
8050
+ date,
8051
+ mutation,
8052
+ tooltipPosition
8053
+ }
8054
+ )
8055
+ },
8056
+ `${mutation.toString()}-${date.toString()}`
8057
+ );
8058
+ })
8059
+ ] }, `fragment-${mutation.toString()}`);
8060
+ })
8061
+ }
8062
+ )
8063
+ ] });
7969
8064
  };
7970
- const ProportionCell = ({ value, mutation, date }) => {
8065
+ function getTooltipPosition(rowIndex, rows, columnIndex, columns) {
8066
+ const tooltipX = rowIndex < rows / 2 ? "bottom" : "top";
8067
+ const tooltipY = columnIndex < columns / 2 ? "start" : "end";
8068
+ return `${tooltipX}-${tooltipY}`;
8069
+ }
8070
+ const ProportionCell = ({ value, mutation, date, tooltipPosition }) => {
7971
8071
  const tooltipContent = /* @__PURE__ */ u$1("div", { children: [
7972
8072
  /* @__PURE__ */ u$1("p", { children: [
7973
8073
  /* @__PURE__ */ u$1("span", { className: "font-bold", children: date.englishName() }),
@@ -7985,7 +8085,7 @@ const ProportionCell = ({ value, mutation, date }) => {
7985
8085
  value.count
7986
8086
  ] })
7987
8087
  ] });
7988
- return /* @__PURE__ */ u$1(Fragment, { children: /* @__PURE__ */ u$1("div", { className: "py-1", children: /* @__PURE__ */ u$1(Tooltip, { content: tooltipContent, children: /* @__PURE__ */ u$1(
8088
+ return /* @__PURE__ */ u$1(Fragment, { children: /* @__PURE__ */ u$1("div", { className: "py-1", children: /* @__PURE__ */ u$1(Tooltip, { content: tooltipContent, position: tooltipPosition, children: /* @__PURE__ */ u$1(
7989
8089
  "div",
7990
8090
  {
7991
8091
  style: {
@@ -8234,7 +8334,8 @@ const MutationsOverTimeTabs = ({
8234
8334
  displayedMutationTypes,
8235
8335
  setDisplayedMutationTypes,
8236
8336
  proportionInterval,
8237
- setProportionInterval
8337
+ setProportionInterval,
8338
+ filteredData
8238
8339
  }
8239
8340
  );
8240
8341
  return /* @__PURE__ */ u$1(Tabs, { tabs, toolbar });
@@ -8245,7 +8346,8 @@ const Toolbar = ({
8245
8346
  displayedMutationTypes,
8246
8347
  setDisplayedMutationTypes,
8247
8348
  proportionInterval,
8248
- setProportionInterval
8349
+ setProportionInterval,
8350
+ filteredData
8249
8351
  }) => {
8250
8352
  return /* @__PURE__ */ u$1(Fragment, { children: [
8251
8353
  /* @__PURE__ */ u$1(SegmentSelector, { displayedSegments, setDisplayedSegments }),
@@ -8256,17 +8358,41 @@ const Toolbar = ({
8256
8358
  displayedMutationTypes
8257
8359
  }
8258
8360
  ),
8259
- /* @__PURE__ */ u$1(Fragment, { children: /* @__PURE__ */ u$1(
8361
+ /* @__PURE__ */ u$1(
8260
8362
  ProportionSelectorDropdown,
8261
8363
  {
8262
8364
  proportionInterval,
8263
8365
  setMinProportion: (min) => setProportionInterval((prev) => ({ ...prev, min })),
8264
8366
  setMaxProportion: (max) => setProportionInterval((prev) => ({ ...prev, max }))
8265
8367
  }
8266
- ) }),
8368
+ ),
8369
+ /* @__PURE__ */ u$1(
8370
+ CsvDownloadButton,
8371
+ {
8372
+ className: "mx-1 btn btn-xs",
8373
+ getData: () => getDownloadData(filteredData),
8374
+ filename: "mutations_over_time.csv"
8375
+ }
8376
+ ),
8267
8377
  /* @__PURE__ */ u$1(Info, { height: "100px", children: "Info for mutations over time" })
8268
8378
  ] });
8269
8379
  };
8380
+ function getDownloadData(filteredData) {
8381
+ const dates = filteredData.getSecondAxisKeys().sort((a2, b3) => compareTemporal(a2, b3));
8382
+ return filteredData.getFirstAxisKeys().map((mutation) => {
8383
+ return dates.reduce(
8384
+ (accumulated, date) => {
8385
+ var _a;
8386
+ const proportion = ((_a = filteredData.get(mutation, date)) == null ? void 0 : _a.proportion) ?? 0;
8387
+ return {
8388
+ ...accumulated,
8389
+ [date.toString()]: proportion
8390
+ };
8391
+ },
8392
+ { mutation: mutation.toString() }
8393
+ );
8394
+ });
8395
+ }
8270
8396
  var __defProp$5 = Object.defineProperty;
8271
8397
  var __getOwnPropDesc$5 = Object.getOwnPropertyDescriptor;
8272
8398
  var __decorateClass$5 = (decorators, target, key, kind) => {