@genspectrum/dashboard-components 0.6.3 → 0.6.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1823,7 +1823,7 @@
1823
1823
  "declarations": [
1824
1824
  {
1825
1825
  "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.",
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).",
1827
1827
  "name": "MutationsOverTimeComponent",
1828
1828
  "members": [
1829
1829
  {
@@ -4585,6 +4585,9 @@ input.tab:checked + .tab-content,
4585
4585
  --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);
4586
4586
  --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(0px + var(--tw-ring-offset-width)) var(--tw-ring-color);
4587
4587
  box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000);
4588
+ }
4589
+ .peer:hover ~ .peer-hover\\:block {
4590
+ display: block;
4588
4591
  }`;
4589
4592
  var __defProp$c = Object.defineProperty;
4590
4593
  var __decorateClass$c = (decorators, target, key, kind) => {
@@ -5938,6 +5941,9 @@ class YearMonthDay {
5938
5941
  toString() {
5939
5942
  return this.text;
5940
5943
  }
5944
+ englishName() {
5945
+ return this.dayjs.format("dddd, MMMM D, YYYY");
5946
+ }
5941
5947
  get firstDay() {
5942
5948
  return this;
5943
5949
  }
@@ -5978,12 +5984,16 @@ class YearWeek {
5978
5984
  toString() {
5979
5985
  return this.text;
5980
5986
  }
5987
+ englishName() {
5988
+ return `Week ${this.isoWeekNumber}, ${this.isoYearNumber}`;
5989
+ }
5981
5990
  get firstDay() {
5982
5991
  const firstDay = dayjs().year(this.isoYearNumber).month(1).date(4).isoWeek(this.isoWeekNumber).startOf("isoWeek");
5983
5992
  return this.cache.getYearMonthDay(firstDay.format("YYYY-MM-DD"));
5984
5993
  }
5985
5994
  get lastDay() {
5986
- const lastDay = dayjs().year(this.isoYearNumber).month(12).date(31).isoWeek(this.isoWeekNumber).endOf("isoWeek");
5995
+ const firstDay = dayjs().year(this.isoYearNumber).startOf("year").add((this.isoWeekNumber - 1) * 7, "day").startOf("week").add(1, "day");
5996
+ const lastDay = firstDay.add(6, "day");
5987
5997
  return this.cache.getYearMonthDay(lastDay.format("YYYY-MM-DD"));
5988
5998
  }
5989
5999
  get year() {
@@ -6014,6 +6024,9 @@ class YearMonth {
6014
6024
  toString() {
6015
6025
  return this.text;
6016
6026
  }
6027
+ englishName() {
6028
+ return `${monthName(this.monthNumber)} ${this.yearNumber}`;
6029
+ }
6017
6030
  get firstDay() {
6018
6031
  return this.cache.getYearMonthDay(dayjs(`${this.yearNumber}-${this.monthNumber}-01`).format("YYYY-MM-DD"));
6019
6032
  }
@@ -6049,6 +6062,9 @@ class Year {
6049
6062
  toString() {
6050
6063
  return this.text;
6051
6064
  }
6065
+ englishName() {
6066
+ return this.year.toString();
6067
+ }
6052
6068
  get firstMonth() {
6053
6069
  return this.cache.getYearMonth(`${this.year}-01`);
6054
6070
  }
@@ -6074,6 +6090,9 @@ class Year {
6074
6090
  return new Year(year, cache);
6075
6091
  }
6076
6092
  }
6093
+ function monthName(month) {
6094
+ return dayjs().month(month - 1).format("MMMM");
6095
+ }
6077
6096
  function generateAllDaysInRange(start, end) {
6078
6097
  const days = [];
6079
6098
  const daysInBetween = end.minus(start);
@@ -7887,14 +7906,32 @@ function filterMutationTypes(displayedMutationTypes, data) {
7887
7906
  }
7888
7907
  function filterProportion(data, proportionInterval) {
7889
7908
  data.getFirstAxisKeys().forEach((mutation) => {
7890
- const row = data.getRow(mutation, 0);
7891
- if (!row.some((value) => value >= proportionInterval.min && value <= proportionInterval.max)) {
7909
+ const row = data.getRow(mutation, { count: 0, proportion: 0 });
7910
+ if (!row.some(
7911
+ (value) => value.proportion >= proportionInterval.min && value.proportion <= proportionInterval.max
7912
+ )) {
7892
7913
  data.deleteRow(mutation);
7893
7914
  }
7894
7915
  });
7895
7916
  }
7917
+ const Tooltip = ({ children, content }) => {
7918
+ const referenceRef = A(null);
7919
+ const floatingRef = A(null);
7920
+ useFloatingUi(referenceRef, floatingRef, [offset(5), shift(), flip()]);
7921
+ 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 })
7924
+ ] });
7925
+ };
7926
+ const MAX_NUMBER_OF_GRID_ROWS = 100;
7896
7927
  const MutationsOverTimeGrid = ({ data }) => {
7897
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
+ }
7898
7935
  const dates = data.getSecondAxisKeys().sort((a2, b3) => compareTemporal(a2, b3));
7899
7936
  return /* @__PURE__ */ u$1(
7900
7937
  "div",
@@ -7915,7 +7952,7 @@ const MutationsOverTimeGrid = ({ data }) => {
7915
7952
  `mutation-${mutation.toString()}`
7916
7953
  ),
7917
7954
  dates.map((date, j2) => {
7918
- const value = data.get(mutation, date) ?? 0;
7955
+ const value = data.get(mutation, date) ?? { proportion: 0, count: 0 };
7919
7956
  return /* @__PURE__ */ u$1(
7920
7957
  "div",
7921
7958
  {
@@ -7930,15 +7967,41 @@ const MutationsOverTimeGrid = ({ data }) => {
7930
7967
  }
7931
7968
  );
7932
7969
  };
7933
- const ProportionCell = ({ value }) => {
7934
- return /* @__PURE__ */ u$1(Fragment, { children: /* @__PURE__ */ u$1("div", { className: "py-1", children: /* @__PURE__ */ u$1(
7970
+ const ProportionCell = ({ value, mutation, date }) => {
7971
+ const tooltipContent = /* @__PURE__ */ u$1("div", { children: [
7972
+ /* @__PURE__ */ u$1("p", { children: [
7973
+ /* @__PURE__ */ u$1("span", { className: "font-bold", children: date.englishName() }),
7974
+ " (",
7975
+ timeIntervalDisplay(date),
7976
+ ")"
7977
+ ] }),
7978
+ /* @__PURE__ */ u$1("p", { children: mutation.code }),
7979
+ /* @__PURE__ */ u$1("p", { children: [
7980
+ "Proportion: ",
7981
+ formatProportion(value.proportion)
7982
+ ] }),
7983
+ /* @__PURE__ */ u$1("p", { children: [
7984
+ "Count: ",
7985
+ value.count
7986
+ ] })
7987
+ ] });
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(
7935
7989
  "div",
7936
7990
  {
7937
- style: { backgroundColor: backgroundColor(value), color: textColor(value) },
7991
+ style: {
7992
+ backgroundColor: backgroundColor(value.proportion),
7993
+ color: textColor(value.proportion)
7994
+ },
7938
7995
  className: "text-center hover:font-bold text-xs",
7939
- children: formatProportion(value, 0)
7996
+ children: formatProportion(value.proportion, 0)
7940
7997
  }
7941
- ) }) });
7998
+ ) }) }) });
7999
+ };
8000
+ const timeIntervalDisplay = (date) => {
8001
+ if (date instanceof YearMonthDay) {
8002
+ return date.toString();
8003
+ }
8004
+ return `${date.firstDay.toString()} - ${date.lastDay.toString()}`;
7942
8005
  };
7943
8006
  const backgroundColor = (proportion) => {
7944
8007
  const minAlpha = 0;
@@ -8014,8 +8077,15 @@ class Map2d {
8014
8077
  return copy;
8015
8078
  }
8016
8079
  }
8080
+ const MAX_NUMBER_OF_GRID_COLUMNS = 200;
8017
8081
  async function queryMutationsOverTimeData(lapisFilter, sequenceType, lapis, lapisDateField, granularity, signal) {
8018
8082
  const allDates = await getDatesInDataset(lapisFilter, lapis, granularity, lapisDateField, signal);
8083
+ if (allDates.length > MAX_NUMBER_OF_GRID_COLUMNS) {
8084
+ throw new UserFacingError(
8085
+ "Too many dates",
8086
+ `The dataset would contain ${allDates.length} date intervals. Please reduce the number to below ${MAX_NUMBER_OF_GRID_COLUMNS} to display the data. You can achieve this by either narrowing the date range in the provided LAPIS filter or by selecting a larger granularity.`
8087
+ );
8088
+ }
8019
8089
  const subQueries = allDates.map(async (date) => {
8020
8090
  const dateFrom = date.firstDay.toString();
8021
8091
  const dateTo = date.lastDay.toString();
@@ -8081,7 +8151,10 @@ function groupByMutation(data) {
8081
8151
  );
8082
8152
  data.forEach((mutationData) => {
8083
8153
  mutationData.mutations.forEach((mutationEntry) => {
8084
- dataArray.set(mutationEntry.mutation, mutationData.date, mutationEntry.proportion);
8154
+ dataArray.set(mutationEntry.mutation, mutationData.date, {
8155
+ count: mutationEntry.count,
8156
+ proportion: mutationEntry.proportion
8157
+ });
8085
8158
  });
8086
8159
  });
8087
8160
  addZeroValuesForDatesWithNoMutationData(dataArray, data);
@@ -8092,7 +8165,7 @@ function addZeroValuesForDatesWithNoMutationData(dataArray, data) {
8092
8165
  const someMutation = dataArray.getFirstAxisKeys()[0];
8093
8166
  data.forEach((mutationData) => {
8094
8167
  if (mutationData.mutations.length === 0) {
8095
- dataArray.set(someMutation, mutationData.date, 0);
8168
+ dataArray.set(someMutation, mutationData.date, { count: 0, proportion: 0 });
8096
8169
  }
8097
8170
  });
8098
8171
  }