diff --git a/packages/libs/components/.storybook/main.js b/packages/libs/components/.storybook/main.js index 9394c710da..ef29acc36f 100644 --- a/packages/libs/components/.storybook/main.js +++ b/packages/libs/components/.storybook/main.js @@ -26,6 +26,7 @@ module.exports = { loader: require.resolve('babel-loader'), options: { plugins: ['@babel/plugin-proposal-nullish-coalescing-operator'], + presets: ['@babel/preset-env', '@babel/preset-react'], }, }); diff --git a/packages/libs/components/package.json b/packages/libs/components/package.json index 13569565c5..f1a6511b96 100755 --- a/packages/libs/components/package.json +++ b/packages/libs/components/package.json @@ -11,6 +11,7 @@ "@typescript-eslint/eslint-plugin": "^5.46.0", "@typescript-eslint/parser": "^5.46.0", "@veupathdb/coreui": "workspace:^", + "@visx/axis": "^3.1.0", "@visx/gradient": "^1.0.0", "@visx/group": "^1.0.0", "@visx/hierarchy": "^1.0.0", @@ -18,6 +19,7 @@ "@visx/text": "^1.3.0", "@visx/tooltip": "^1.3.0", "@visx/visx": "^1.1.0", + "@visx/xychart": "^3.1.0", "bootstrap": "^4.5.2", "color-math": "^1.1.3", "d3": "^7.1.1", @@ -37,6 +39,8 @@ "react-leaflet": "^3.2.5", "react-leaflet-drift-marker": "^3.0.0", "react-plotly.js": "^2.4.0", + "react-spring": "^9.7.1", + "react-transition-group": "^4.4.1", "shape2geohash": "^1.2.5" }, "files": [ diff --git a/packages/libs/components/src/plots/VolcanoPlot.tsx b/packages/libs/components/src/plots/VolcanoPlot.tsx new file mode 100755 index 0000000000..ca2a6953c5 --- /dev/null +++ b/packages/libs/components/src/plots/VolcanoPlot.tsx @@ -0,0 +1,267 @@ +import { significanceColors } from '../types/plots'; +import { + VolcanoPlotData, + VolcanoPlotDataPoint, +} from '../types/plots/volcanoplot'; +import { NumberRange } from '../types/general'; +import { + XYChart, + Axis, + Grid, + GlyphSeries, + Annotation, + AnnotationLineSubject, +} from '@visx/xychart'; +import { Group } from '@visx/group'; +import { max, min } from 'lodash'; +import { + gridStyles, + thresholdLineStyles, + VisxPoint, + axisStyles, +} from './visxVEuPathDB'; + +export interface VolcanoPlotProps { + /** Data for the plot. An array of VolcanoPlotDataPoints */ + data: VolcanoPlotData; + /** + * Used to set the fold change thresholds. Will + * set two thresholds at +/- this number. Affects point colors + */ + log2FoldChangeThreshold: number; + /** Set the threshold for significance. Affects point colors */ + significanceThreshold: number; + /** x-axis range */ + independentAxisRange?: NumberRange; + /** y-axis range */ + dependentAxisRange?: NumberRange; + /** + * Array of size 2 that contains a label for the left and right side + * of the x axis. (Not yet implemented). Expect this to be passed by the viz based + * on the type of data we're using (genes vs taxa vs etc.) + */ + comparisonLabels?: Array; + /** Title of the plot */ + plotTitle?: string; + /** marker fill opacity: range from 0 to 1 */ + markerBodyOpacity?: number; + /** Height of plot */ + height?: number; + /** Width of plot */ + width?: number; +} + +const EmptyVolcanoPlotData: VolcanoPlotData = []; + +/** + * The Volcano Plot displays points on a (magnitude change) by (significance) xy axis. + * The standard volcano plot has -log2(Fold Change) as the x axis and -log10(raw p value) + * on the y axis. The volcano plot also colors the points based on their + * significance and magnitude change to make it easy to spot significantly up or down-regulated genes or taxa. + */ +function VolcanoPlot(props: VolcanoPlotProps) { + const { + data = EmptyVolcanoPlotData, + independentAxisRange, // not yet implemented - expect this to be set by user + dependentAxisRange, // not yet implemented - expect this to be set by user + significanceThreshold, + log2FoldChangeThreshold, + markerBodyOpacity, + height, + width, + } = props; + + /** + * Find mins and maxes of the data and for the plot. + * The standard x axis is the log2 fold change. The standard + * y axis is -log10 raw p value. + */ + + // Find maxes and mins of the data itself + const dataXMin = min(data.map((d) => Number(d.log2foldChange))); + const dataXMax = max(data.map((d) => Number(d.log2foldChange))); + const dataYMin = min(data.map((d) => Number(d.pValue))); + const dataYMax = max(data.map((d) => Number(d.pValue))); + + // Determine mins, maxes of axes in the plot. + // These are different than the data mins/maxes because + // of the log transform and the little bit of padding. The padding + // ensures we don't clip off part of the glyphs that represent + // the most extreme points + let xMin: number; + let xMax: number; + let yMin: number; + let yMax: number; + const AXIS_PADDING_FACTOR = 0.05; + + // X axis + if (dataXMin && dataXMax) { + // We can use the dataMin and dataMax here because we don't have a further transform + xMin = dataXMin; + xMax = dataXMax; + // Add a little padding to prevent clipping the glyph representing the extreme points + xMin = xMin - (xMax - xMin) * AXIS_PADDING_FACTOR; + xMax = xMax + (xMax - xMin) * AXIS_PADDING_FACTOR; + } else { + xMin = 0; + xMax = 0; + } + + // Y axis + if (dataYMin && dataYMax) { + // Standard volcano plots have -log10(raw p value) as the y axis + yMin = -Math.log10(dataYMax); + yMax = -Math.log10(dataYMin); + // Add a little padding to prevent clipping the glyph representing the extreme points + yMin = yMin - (yMax - yMin) * AXIS_PADDING_FACTOR; + yMax = yMax + (yMax - yMin) * AXIS_PADDING_FACTOR; + } else { + yMin = 0; + yMax = 0; + } + + /** + * Accessors - tell visx which value of the data point we should use and where. + */ + + const dataAccessors = { + xAccessor: (d: VolcanoPlotDataPoint) => { + return Number(d?.log2foldChange); + }, + yAccessor: (d: VolcanoPlotDataPoint) => { + return -Math.log10(Number(d?.pValue)); + }, + }; + + const thresholdLineAccessors = { + xAccessor: (d: VisxPoint) => { + return d?.x; + }, + yAccessor: (d: VisxPoint) => { + return d?.y; + }, + }; + + return ( + // Relative positioning so that tooltips are positioned correctly (tooltips are positioned absolutely) +
+ {/* The XYChart takes care of laying out the chart elements (children) appropriately. + It uses modularized React.context layers for data, events, etc. The following all becomes an svg, + so use caution when ordering the children (ex. draw axes before data). */} + + {/* Set up the axes and grid lines. XYChart magically lays them out correctly */} + + + + + {/* Draw threshold lines as annotations below the data points. The + annotations use XYChart's theme and dimension context. + The Annotation component holds the context for its children, which is why + we make a new Annotation component for each line. + Another option would be to make Line with LineSeries, but the default hover response + is on the points instead of the line connecting them. */} + + {/* Draw horizontal significance threshold */} + {significanceThreshold && ( + + + + )} + {/* Draw both vertical log2 fold change threshold lines */} + {log2FoldChangeThreshold && ( + <> + + + + + + + + )} + + {/* The data itself */} + {/* Wrapping in a group in order to change the opacity of points. The GlyphSeries is somehow + a bunch of glyphs which are so there should be a way to pass opacity + down to those elements, but I haven't found it yet */} + + { + return assignSignificanceColor( + Number(d.log2foldChange), + Number(d.pValue), + significanceThreshold, + log2FoldChangeThreshold, + significanceColors + ); + }} + /> + + +
+ ); +} + +/** + * Assign color to point based on significance and magnitude change thresholds + */ +function assignSignificanceColor( + log2foldChange: number, + pValue: number, + significanceThreshold: number, + log2FoldChangeThreshold: number, + significanceColors: string[] // Assuming the order is [insignificant, high (up regulated), low (down regulated)] +) { + // Name indices of the significanceColors array for easier accessing. + const INSIGNIFICANT = 0; + const HIGH = 1; + const LOW = 2; + + // Test 1. If the y value is higher than the significance threshold, just return not significant + if (pValue >= significanceThreshold) { + return significanceColors[INSIGNIFICANT]; + } + + // Test 2. So the y is significant. Is the x larger than the positive foldChange threshold? + if (log2foldChange >= log2FoldChangeThreshold) { + return significanceColors[HIGH]; + } + + // Test 3. Is the x value lower than the negative foldChange threshold? + if (log2foldChange <= -log2FoldChangeThreshold) { + return significanceColors[LOW]; + } + + // If we're still here, it must be a non significant point. + return significanceColors[INSIGNIFICANT]; +} + +export default VolcanoPlot; diff --git a/packages/libs/components/src/plots/visxVEuPathDB.tsx b/packages/libs/components/src/plots/visxVEuPathDB.tsx new file mode 100644 index 0000000000..cbe03bdd86 --- /dev/null +++ b/packages/libs/components/src/plots/visxVEuPathDB.tsx @@ -0,0 +1,29 @@ +/** Helpful styles and types for working with visx */ + +/** + * Types + */ + +// Basic x,y point. Comes in handy for annotations and other non-data plotted elements +export type VisxPoint = { + x?: number; + y?: number; +}; + +/** + * Plot styles + * (can eventually be moved to a visx theme) + */ +export const thresholdLineStyles = { + stroke: '#aaaaaa', + strokeWidth: 1, + strokeDasharray: 3, +}; +export const axisStyles = { + stroke: '#bbbbbb', + strokeWidth: 1, +}; +export const gridStyles = { + stroke: '#dddddd', + strokeWidth: 0.5, +}; diff --git a/packages/libs/components/src/stories/plots/ScatterPlot.storyData.ts b/packages/libs/components/src/stories/plots/ScatterPlot.storyData.ts index 4cb89f8787..5c455c7d50 100644 --- a/packages/libs/components/src/stories/plots/ScatterPlot.storyData.ts +++ b/packages/libs/components/src/stories/plots/ScatterPlot.storyData.ts @@ -1008,7 +1008,10 @@ function boxMullerTransform() { return { z0, z1 }; } -function getNormallyDistributedRandomNumber(mean: number, stddev: number) { +export function getNormallyDistributedRandomNumber( + mean: number, + stddev: number +) { const { z0, z1 } = boxMullerTransform(); return z0 * stddev + mean; diff --git a/packages/libs/components/src/stories/plots/VolcanoPlot.stories.tsx b/packages/libs/components/src/stories/plots/VolcanoPlot.stories.tsx new file mode 100755 index 0000000000..afa48c9023 --- /dev/null +++ b/packages/libs/components/src/stories/plots/VolcanoPlot.stories.tsx @@ -0,0 +1,148 @@ +import VolcanoPlot, { VolcanoPlotProps } from '../../plots/VolcanoPlot'; +import { Story, Meta } from '@storybook/react/types-6-0'; +import { range } from 'lodash'; +import { getNormallyDistributedRandomNumber } from './ScatterPlot.storyData'; +import { VolcanoPlotData } from '../../types/plots/volcanoplot'; + +export default { + title: 'Plots/VolcanoPlot', + component: VolcanoPlot, + argTypes: { + log2FoldChangeThreshold: { + control: { type: 'range', min: 0.5, max: 10, step: 0.01 }, + }, + significanceThreshold: { + control: { type: 'range', min: 0.0001, max: 0.2, step: 0.001 }, + }, + }, +} as Meta; + +// Currently going to assume that the backend will send us data like this. Then +// the volcano visualization will do some processing. Will discuss +// with Danielle if we can have the backend send us an array of objects +// instead of this object of arrays... +interface VEuPathDBVolcanoPlotData { + volcanoplot: { + log2foldChange: string[]; + pValue: string[]; + adjustedPValue: string[]; + pointId: string[]; + }; +} + +// Let's make some fake data! +const dataSetVolcano: VEuPathDBVolcanoPlotData = { + volcanoplot: { + log2foldChange: [ + '2', + '3', + '0.5', + '-0.1', + '1', + '-0.5', + '-1.2', + '4', + '0.2', + '-8', + '-4', + '-3', + ], + pValue: [ + '0.001', + '0.0001', + '0.01', + '0.001', + '0.98', + '1', + '0.8', + '1', + '0.6', + '0.001', + '0.0001', + '0.002', + ], + adjustedPValue: ['0.01', '0.001', '0.01', '0.001', '0.02'], + pointId: ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l'], + }, +}; + +// Make a fake dataset with lots of points! +const nPoints = 300; +const dataSetVolcanoManyPoints: VEuPathDBVolcanoPlotData = { + volcanoplot: { + log2foldChange: range(1, nPoints).map((p) => + String(Math.log2(Math.abs(getNormallyDistributedRandomNumber(0, 5)))) + ), + pValue: range(1, nPoints).map((p) => String(Math.random() / 2)), + adjustedPValue: range(1, nPoints).map((p) => + String(nPoints * Math.random()) + ), + pointId: range(1, nPoints).map((p) => String(p)), + }, +}; + +interface TemplateProps { + data: VEuPathDBVolcanoPlotData; + markerBodyOpacity: number; + log2FoldChangeThreshold: number; + significanceThreshold: number; + adjustedPValueGate: number; +} + +const Template: Story = (args) => { + // Eventually should be a Template prop. Not yet implemented in the component. + const comparisonLabels = ['up in group a', 'up in group b']; + + // Process input data. Take the object of arrays and turn it into + // an array of data points + const volcanoDataPoints: VolcanoPlotData = + args.data.volcanoplot.log2foldChange.map((l2fc, index) => { + return { + log2foldChange: l2fc, + pValue: args.data.volcanoplot.pValue[index], + adjustedPValue: args.data.volcanoplot.adjustedPValue[index], + pointId: args.data.volcanoplot.pointId[index], + }; + }); + + const volcanoPlotProps: VolcanoPlotProps = { + data: volcanoDataPoints, + significanceThreshold: args.significanceThreshold, + log2FoldChangeThreshold: args.log2FoldChangeThreshold, + markerBodyOpacity: args.markerBodyOpacity, + comparisonLabels: comparisonLabels, // currently does nothing. not yet implemented. + }; + + return ; +}; + +/** + * Stories + */ + +// A small volcano plot. Proof of concept +export const Simple = Template.bind({}); +Simple.args = { + data: dataSetVolcano, + markerBodyOpacity: 0.8, + log2FoldChangeThreshold: 1, + significanceThreshold: 0.01, +}; + +// Most volcano plots will have thousands of points, since each point +// represents a gene or taxa. Make a volcano plot with +// a lot of points. +export const ManyPoints = Template.bind({}); +ManyPoints.args = { + data: dataSetVolcanoManyPoints, + markerBodyOpacity: 0.5, + log2FoldChangeThreshold: 3, + significanceThreshold: 0.01, +}; + +// Add story for truncation +// export const Truncation = Template.bind({}) +// Truncation.args = { +// data: dataSetVolcano, +// independentAxisRange: [] +// } diff --git a/packages/libs/components/src/types/plots/addOns.ts b/packages/libs/components/src/types/plots/addOns.ts index 66ecb46639..4c40d47ac0 100644 --- a/packages/libs/components/src/types/plots/addOns.ts +++ b/packages/libs/components/src/types/plots/addOns.ts @@ -283,6 +283,9 @@ export const gradientConvergingColorscaleMap = scaleLinear() .range(ConvergingGradientColorscale) .interpolate(interpolateLab); +// Significance colors (not significant, high, low) +export const significanceColors = ['#B5B8B4', '#AC3B4E', '#0E8FAB']; + /** truncated axis flags */ export type AxisTruncationAddon = { /** truncation config (flags) to show truncated axis (true) or not (false) */ diff --git a/packages/libs/components/src/types/plots/volcanoplot.ts b/packages/libs/components/src/types/plots/volcanoplot.ts new file mode 100755 index 0000000000..01b1a0bd58 --- /dev/null +++ b/packages/libs/components/src/types/plots/volcanoplot.ts @@ -0,0 +1,13 @@ +export type VolcanoPlotDataPoint = { + // log2foldChange becomes the x axis. Also used for coloring points + log2foldChange: string; + // pValue will be negative log transformed for the y axis. Also + // needed as is (untransformed) in the tooltip and when coloring points + pValue: string; + // Used for thresholding and tooltip + adjustedPValue: string; + // Used for tooltip + pointId: string; +}; + +export type VolcanoPlotData = Array; diff --git a/yarn.lock b/yarn.lock index 630ea1e0b6..230da7597c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3689,6 +3689,137 @@ __metadata: languageName: node linkType: hard +"@react-spring/animated@npm:~9.7.2": + version: 9.7.2 + resolution: "@react-spring/animated@npm:9.7.2" + dependencies: + "@react-spring/shared": ~9.7.2 + "@react-spring/types": ~9.7.2 + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + checksum: 08f2f1e54468776b2abf76e3c3504129b8d965875781bc1cfd40adddff8f310869ecfb6d18e937df8ed63a01b3d0cb2335fb531be03ddf25f6f58583a91e08e7 + languageName: node + linkType: hard + +"@react-spring/core@npm:~9.7.1, @react-spring/core@npm:~9.7.2": + version: 9.7.2 + resolution: "@react-spring/core@npm:9.7.2" + dependencies: + "@react-spring/animated": ~9.7.2 + "@react-spring/rafz": ~9.7.2 + "@react-spring/shared": ~9.7.2 + "@react-spring/types": ~9.7.2 + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + checksum: 7d14369d6bc3cb4f51bce946a0499484bd03a06b2602edd1f0478806759fd2a5f0800892cdf8d38a3bd4ef949f3c61de65933c46784683ea757f16db710a04fd + languageName: node + linkType: hard + +"@react-spring/konva@npm:~9.7.1": + version: 9.7.2 + resolution: "@react-spring/konva@npm:9.7.2" + dependencies: + "@react-spring/animated": ~9.7.2 + "@react-spring/core": ~9.7.2 + "@react-spring/shared": ~9.7.2 + "@react-spring/types": ~9.7.2 + peerDependencies: + konva: ">=2.6" + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-konva: ^16.8.0 || ^16.8.7-0 || ^16.9.0-0 || ^16.10.1-0 || ^16.12.0-0 || ^16.13.0-0 || ^17.0.0-0 || ^17.0.1-0 || ^17.0.2-0 || ^18.0.0-0 + checksum: de1819722d578355a51b7e70509adde747650c22df7540a81b989a423292110b606e5675d41e70cce332b528bfe62e9b56571ed70acf72896014f8c852b2a777 + languageName: node + linkType: hard + +"@react-spring/native@npm:~9.7.1": + version: 9.7.2 + resolution: "@react-spring/native@npm:9.7.2" + dependencies: + "@react-spring/animated": ~9.7.2 + "@react-spring/core": ~9.7.2 + "@react-spring/shared": ~9.7.2 + "@react-spring/types": ~9.7.2 + peerDependencies: + react: ^16.8.0 || >=17.0.0 || >=18.0.0 + react-native: ">=0.58" + checksum: 11963f7c9993f1c34bd32f82a1425f9e50d83c86f28ff53940aed87c677732d11e379e596fd2728861b5d8978255496eb63769d54018f943f15f6d80628e17a0 + languageName: node + linkType: hard + +"@react-spring/rafz@npm:~9.7.2": + version: 9.7.2 + resolution: "@react-spring/rafz@npm:9.7.2" + checksum: 88ad6275ed172745c7cd309e6a06bb52b76b6f391510afcf8ad12d1fc50950e74f902f96bcaae0895ab75e47a1d809d36fe843ce8d3d0d2ea2d6700ff9e03d81 + languageName: node + linkType: hard + +"@react-spring/shared@npm:~9.7.2": + version: 9.7.2 + resolution: "@react-spring/shared@npm:9.7.2" + dependencies: + "@react-spring/rafz": ~9.7.2 + "@react-spring/types": ~9.7.2 + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + checksum: f3041a537dddad63c64e68890f3276b72c4e2d5828621a09dbd722edb7a12edc0026555118f8305b969253f44e409ddd411dd5b99ec4e04edea32bee5e5f57fd + languageName: node + linkType: hard + +"@react-spring/three@npm:~9.7.1": + version: 9.7.2 + resolution: "@react-spring/three@npm:9.7.2" + dependencies: + "@react-spring/animated": ~9.7.2 + "@react-spring/core": ~9.7.2 + "@react-spring/shared": ~9.7.2 + "@react-spring/types": ~9.7.2 + peerDependencies: + "@react-three/fiber": ">=6.0" + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + three: ">=0.126" + checksum: 970efd0fb7eee29edc0f468fde3ab590905cd9136e6f1a017dc40b9b94dc1bf1befe399ac08848994b1359f83339be93b09eb5b30f9f2ff97f9f0477bb4de45c + languageName: node + linkType: hard + +"@react-spring/types@npm:~9.7.2": + version: 9.7.2 + resolution: "@react-spring/types@npm:9.7.2" + checksum: 145a79d2f40dfc9f0a4b54db17890c6471b4e50563c01951ca13022977b0d8009865c1839cf07a762fd33ae0b83f2d6972e2d0fe50bd3105942b9cea5253612e + languageName: node + linkType: hard + +"@react-spring/web@npm:~9.7.1": + version: 9.7.2 + resolution: "@react-spring/web@npm:9.7.2" + dependencies: + "@react-spring/animated": ~9.7.2 + "@react-spring/core": ~9.7.2 + "@react-spring/shared": ~9.7.2 + "@react-spring/types": ~9.7.2 + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 + checksum: f872dfd9bbbfc27f32e4406076729a36169e2da3785709fbf981b7d3a77a1a430d7f295b300a9acc8cb4805d0965e6d9a54914a4263abfd96806b7ae53ba69cd + languageName: node + linkType: hard + +"@react-spring/zdog@npm:~9.7.1": + version: 9.7.2 + resolution: "@react-spring/zdog@npm:9.7.2" + dependencies: + "@react-spring/animated": ~9.7.2 + "@react-spring/core": ~9.7.2 + "@react-spring/shared": ~9.7.2 + "@react-spring/types": ~9.7.2 + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-zdog: ">=1.0" + zdog: ">=1.0" + checksum: 550edcb6a3632c97a3511d35b91bc8d9ef09542846572be40017ceeea404f4a6579e34b2888ae39abf8c00f97cee2152a8266d2fe5f60176c51ab680bdad93e1 + languageName: node + linkType: hard + "@restart/context@npm:^2.1.4": version: 2.1.4 resolution: "@restart/context@npm:2.1.4" @@ -6221,7 +6352,7 @@ __metadata: languageName: node linkType: hard -"@types/d3-interpolate@npm:*": +"@types/d3-interpolate@npm:*, @types/d3-interpolate@npm:^3.0.1": version: 3.0.1 resolution: "@types/d3-interpolate@npm:3.0.1" dependencies: @@ -6288,7 +6419,7 @@ __metadata: languageName: node linkType: hard -"@types/d3-scale@npm:*": +"@types/d3-scale@npm:*, @types/d3-scale@npm:^4.0.2": version: 4.0.3 resolution: "@types/d3-scale@npm:4.0.3" dependencies: @@ -7858,6 +7989,7 @@ __metadata: "@veupathdb/coreui": "workspace:^" "@veupathdb/eslint-config": "workspace:^" "@veupathdb/tsconfig": "workspace:^" + "@visx/axis": ^3.1.0 "@visx/gradient": ^1.0.0 "@visx/group": ^1.0.0 "@visx/hierarchy": ^1.0.0 @@ -7865,6 +7997,7 @@ __metadata: "@visx/text": ^1.3.0 "@visx/tooltip": ^1.3.0 "@visx/visx": ^1.1.0 + "@visx/xychart": ^3.1.0 babel-loader: ^8.1.0 bootstrap: ^4.5.2 color-math: ^1.1.3 @@ -7892,6 +8025,8 @@ __metadata: react-leaflet: ^3.2.5 react-leaflet-drift-marker: ^3.0.0 react-plotly.js: ^2.4.0 + react-spring: ^9.7.1 + react-transition-group: ^4.4.1 shape2geohash: ^1.2.5 stats-lite: ^2.2.0 storybook: ^6.5.14 @@ -8740,6 +8875,23 @@ __metadata: languageName: node linkType: hard +"@visx/annotation@npm:3.0.1": + version: 3.0.1 + resolution: "@visx/annotation@npm:3.0.1" + dependencies: + "@types/react": "*" + "@visx/drag": 3.0.1 + "@visx/group": 3.0.0 + "@visx/text": 3.0.0 + classnames: ^2.3.1 + prop-types: ^15.5.10 + react-use-measure: ^2.0.4 + peerDependencies: + react: ^16.0.0-0 || ^17.0.0-0 || ^18.0.0-0 + checksum: e1a3016b8b00c843b56a41c0b07c5509ea195948408c856f09fd2db3268fbc4641f1e6adf0022e60a042cd25d7f0334e7ac6128a367c92a27e42770573eef5f6 + languageName: node + linkType: hard + "@visx/axis@npm:1.17.1": version: 1.17.1 resolution: "@visx/axis@npm:1.17.1" @@ -8758,6 +8910,24 @@ __metadata: languageName: node linkType: hard +"@visx/axis@npm:3.1.0, @visx/axis@npm:^3.1.0": + version: 3.1.0 + resolution: "@visx/axis@npm:3.1.0" + dependencies: + "@types/react": "*" + "@visx/group": 3.0.0 + "@visx/point": 3.0.1 + "@visx/scale": 3.0.0 + "@visx/shape": 3.0.0 + "@visx/text": 3.0.0 + classnames: ^2.3.1 + prop-types: ^15.6.0 + peerDependencies: + react: ^16.3.0-0 || ^17.0.0-0 || ^18.0.0-0 + checksum: 9f69e37823bba06ffddf2126b93eb3b3468fa88a82e40c7a1a0678dfdb7950a2e430db06055d6e746504e2092107012e5a9d3815c4de16f87cc3cc3bbdba574b + languageName: node + linkType: hard + "@visx/bounds@npm:1.7.0": version: 1.7.0 resolution: "@visx/bounds@npm:1.7.0" @@ -8772,6 +8942,20 @@ __metadata: languageName: node linkType: hard +"@visx/bounds@npm:3.0.0": + version: 3.0.0 + resolution: "@visx/bounds@npm:3.0.0" + dependencies: + "@types/react": "*" + "@types/react-dom": "*" + prop-types: ^15.5.10 + peerDependencies: + react: ^16.0.0-0 || ^17.0.0-0 || ^18.0.0-0 + react-dom: ^16.0.0-0 || ^17.0.0-0 || ^18.0.0-0 + checksum: 3c50dad1c78b526bebe20a690360ae5ad3dd2a31c86150553071671e49309a9fcf3bca3303eab7f7ac2e28be8d7987ac9ce5daa28b8df32c647b0aa97f118fa9 + languageName: node + linkType: hard + "@visx/brush@npm:1.18.1": version: 1.18.1 resolution: "@visx/brush@npm:1.18.1" @@ -8810,6 +8994,16 @@ __metadata: languageName: node linkType: hard +"@visx/curve@npm:3.0.0": + version: 3.0.0 + resolution: "@visx/curve@npm:3.0.0" + dependencies: + "@types/d3-shape": ^1.3.1 + d3-shape: ^1.0.6 + checksum: 1707b2b0e002d35e77cec3c5fbf8a05e70f10881ffb123edf7e04e9ccfb05262e1cbbc0dfd45067e1c3ae5bc653325ef728fe880fecc7be9620ae267631f4149 + languageName: node + linkType: hard + "@visx/drag@npm:1.18.1": version: 1.18.1 resolution: "@visx/drag@npm:1.18.1" @@ -8823,6 +9017,20 @@ __metadata: languageName: node linkType: hard +"@visx/drag@npm:3.0.1": + version: 3.0.1 + resolution: "@visx/drag@npm:3.0.1" + dependencies: + "@types/react": "*" + "@visx/event": 3.0.1 + "@visx/point": 3.0.1 + prop-types: ^15.5.10 + peerDependencies: + react: ^16.8.0-0 || ^17.0.0-0 || ^18.0.0-0 + checksum: 256e4a7b649e29b2112f77a57a657cd3aedb76b605ebc8b49e6c3d4b152fea5d81de7820060dc569dda0c81a0e7b6668ce9fe071536198b26cf2ebd655a6e6a9 + languageName: node + linkType: hard + "@visx/event@npm:1.7.0": version: 1.7.0 resolution: "@visx/event@npm:1.7.0" @@ -8833,6 +9041,16 @@ __metadata: languageName: node linkType: hard +"@visx/event@npm:3.0.1": + version: 3.0.1 + resolution: "@visx/event@npm:3.0.1" + dependencies: + "@types/react": "*" + "@visx/point": 3.0.1 + checksum: 0cb4dd578bbe54bd428a0cfae9e64db141b039c2c6c1412d1cd1d04e1613d193212b031124948ca3b7eed877cdea87d161cc3a008b973c6f217e21a8a2dd27b0 + languageName: node + linkType: hard + "@visx/geo@npm:1.17.1": version: 1.17.1 resolution: "@visx/geo@npm:1.17.1" @@ -8866,6 +9084,22 @@ __metadata: languageName: node linkType: hard +"@visx/glyph@npm:3.0.0": + version: 3.0.0 + resolution: "@visx/glyph@npm:3.0.0" + dependencies: + "@types/d3-shape": ^1.3.1 + "@types/react": "*" + "@visx/group": 3.0.0 + classnames: ^2.3.1 + d3-shape: ^1.2.0 + prop-types: ^15.6.2 + peerDependencies: + react: ^16.3.0-0 || ^17.0.0-0 || ^18.0.0-0 + checksum: 0a944f2594c519e66e66dbc806e9ff2e53bbc64bfd8c2106420d97f0c47824d22b6e733f7bed3591dd94fb00b32ead68c34fd3d342fc42968182d511ac9996c5 + languageName: node + linkType: hard + "@visx/gradient@npm:1.7.0, @visx/gradient@npm:^1.0.0": version: 1.7.0 resolution: "@visx/gradient@npm:1.7.0" @@ -8896,6 +9130,24 @@ __metadata: languageName: node linkType: hard +"@visx/grid@npm:3.0.1": + version: 3.0.1 + resolution: "@visx/grid@npm:3.0.1" + dependencies: + "@types/react": "*" + "@visx/curve": 3.0.0 + "@visx/group": 3.0.0 + "@visx/point": 3.0.1 + "@visx/scale": 3.0.0 + "@visx/shape": 3.0.0 + classnames: ^2.3.1 + prop-types: ^15.6.2 + peerDependencies: + react: ^16.0.0-0 || ^17.0.0-0 || ^18.0.0-0 + checksum: 2ff960b180f0ac1960170f3628a49667f3da58751242638af7844eb49a30273e52bba380103c14bb5b4829ca4bef0c072a8a78696abf2b41d4a49fcdbc02a263 + languageName: node + linkType: hard + "@visx/group@npm:1.17.1, @visx/group@npm:^1.0.0": version: 1.17.1 resolution: "@visx/group@npm:1.17.1" @@ -8909,6 +9161,19 @@ __metadata: languageName: node linkType: hard +"@visx/group@npm:3.0.0": + version: 3.0.0 + resolution: "@visx/group@npm:3.0.0" + dependencies: + "@types/react": "*" + classnames: ^2.3.1 + prop-types: ^15.6.2 + peerDependencies: + react: ^16.0.0-0 || ^17.0.0-0 || ^18.0.0-0 + checksum: f52b946fb39cfcd4798bd5c77b729ceb41b2041e0464edeef2f15955c4022d51ed9f7626f2a7ac331f8430c84037254be5dd38f8282881a63d1680b27f41d04d + languageName: node + linkType: hard + "@visx/heatmap@npm:1.17.1": version: 1.17.1 resolution: "@visx/heatmap@npm:1.17.1" @@ -9013,6 +9278,13 @@ __metadata: languageName: node linkType: hard +"@visx/point@npm:3.0.1": + version: 3.0.1 + resolution: "@visx/point@npm:3.0.1" + checksum: 727f314826eb85a608b4088a9e2fedb7e6996a5f0fdddaeb40702a51aa04da07fdbc5fdcf2d595d914dfa083389c644f769fc69b3a247b4da274936dd58c7bb8 + languageName: node + linkType: hard + "@visx/react-spring@npm:1.17.1": version: 1.17.1 resolution: "@visx/react-spring@npm:1.17.1" @@ -9031,6 +9303,24 @@ __metadata: languageName: node linkType: hard +"@visx/react-spring@npm:3.1.0": + version: 3.1.0 + resolution: "@visx/react-spring@npm:3.1.0" + dependencies: + "@types/react": "*" + "@visx/axis": 3.1.0 + "@visx/grid": 3.0.1 + "@visx/scale": 3.0.0 + "@visx/text": 3.0.0 + classnames: ^2.3.1 + prop-types: ^15.6.2 + peerDependencies: + "@react-spring/web": ^9.4.5 + react: ^16.3.0-0 || ^17.0.0 || ^18.0.0 + checksum: 5d727dd8053c101d6473580feba5b8c05d70fa6311a0dc2f9c1d420c6fce4ce497f7f191b89bb10c53eedd986aa3006cd7a7f389035c02a2a847f8c3c38fcafb + languageName: node + linkType: hard + "@visx/responsive@npm:1.10.1": version: 1.10.1 resolution: "@visx/responsive@npm:1.10.1" @@ -9046,6 +9336,20 @@ __metadata: languageName: node linkType: hard +"@visx/responsive@npm:3.0.0": + version: 3.0.0 + resolution: "@visx/responsive@npm:3.0.0" + dependencies: + "@types/lodash": ^4.14.172 + "@types/react": "*" + lodash: ^4.17.21 + prop-types: ^15.6.1 + peerDependencies: + react: ^16.0.0-0 || ^17.0.0-0 || ^18.0.0-0 + checksum: 0a547c7cec9f2f61931d1de6ee8955b76904ea0bcc1c55d18d991067f63579c6ca1d1b976a0ada21d3b3818551b3d5749d8b11ab50dc472f38bd94f21b56b8ec + languageName: node + linkType: hard + "@visx/scale@npm:1.14.0": version: 1.14.0 resolution: "@visx/scale@npm:1.14.0" @@ -9060,6 +9364,20 @@ __metadata: languageName: node linkType: hard +"@visx/scale@npm:3.0.0": + version: 3.0.0 + resolution: "@visx/scale@npm:3.0.0" + dependencies: + "@types/d3-interpolate": ^3.0.1 + "@types/d3-scale": ^4.0.2 + "@types/d3-time": ^2.0.0 + d3-interpolate: ^3.0.1 + d3-scale: ^4.0.2 + d3-time: ^2.1.1 + checksum: 3740fd0f6844b2297847f48f34afa9772e35d8b6faf26fb7b18c78fcd83634de57a44cd4aa9263dfb3a62bec609cf24b36db26f6db8ba36908dbdb83f359d9ed + languageName: node + linkType: hard + "@visx/shape@npm:1.17.1, @visx/shape@npm:^1.4.0": version: 1.17.1 resolution: "@visx/shape@npm:1.17.1" @@ -9082,6 +9400,28 @@ __metadata: languageName: node linkType: hard +"@visx/shape@npm:3.0.0": + version: 3.0.0 + resolution: "@visx/shape@npm:3.0.0" + dependencies: + "@types/d3-path": ^1.0.8 + "@types/d3-shape": ^1.3.1 + "@types/lodash": ^4.14.172 + "@types/react": "*" + "@visx/curve": 3.0.0 + "@visx/group": 3.0.0 + "@visx/scale": 3.0.0 + classnames: ^2.3.1 + d3-path: ^1.0.5 + d3-shape: ^1.2.0 + lodash: ^4.17.21 + prop-types: ^15.5.10 + peerDependencies: + react: ^16.3.0-0 || ^17.0.0-0 || ^18.0.0-0 + checksum: d6e71e0c5dd90957a38b1404e8357cd2192f741f8c7b4f73fac3c007e4b0af398f5cf648f5f1ecc5ca72759e729cfa58fff4f8a409f95a344219309881e58b37 + languageName: node + linkType: hard + "@visx/text@npm:1.17.1, @visx/text@npm:^1.3.0": version: 1.17.1 resolution: "@visx/text@npm:1.17.1" @@ -9098,6 +9438,22 @@ __metadata: languageName: node linkType: hard +"@visx/text@npm:3.0.0": + version: 3.0.0 + resolution: "@visx/text@npm:3.0.0" + dependencies: + "@types/lodash": ^4.14.172 + "@types/react": "*" + classnames: ^2.3.1 + lodash: ^4.17.21 + prop-types: ^15.7.2 + reduce-css-calc: ^1.3.0 + peerDependencies: + react: ^16.3.0-0 || ^17.0.0-0 || ^18.0.0-0 + checksum: a8456fceb785db17487103ee3c2c5acb5988f0ec883735ecaf60fa734d8876c0d72eab6862dc8e272cf87644694bc21aff5c9cb9eccf4e948bd0899926513efd + languageName: node + linkType: hard + "@visx/tooltip@npm:1.17.1, @visx/tooltip@npm:^1.3.0": version: 1.17.1 resolution: "@visx/tooltip@npm:1.17.1" @@ -9114,6 +9470,22 @@ __metadata: languageName: node linkType: hard +"@visx/tooltip@npm:3.1.2": + version: 3.1.2 + resolution: "@visx/tooltip@npm:3.1.2" + dependencies: + "@types/react": "*" + "@visx/bounds": 3.0.0 + classnames: ^2.3.1 + prop-types: ^15.5.10 + react-use-measure: ^2.0.4 + peerDependencies: + react: ^16.8.0-0 || ^17.0.0-0 || ^18.0.0-0 + react-dom: ^16.8.0-0 || ^17.0.0-0 || ^18.0.0-0 + checksum: 4c2127068f22766e984ed3ed1452dcece919bacb0297b17ba862a6815bb7f04c4fc462a100d3934e0cb56a9c7962b82f7c4184748ef4b2dbef3b4b0d5b717fdd + languageName: node + linkType: hard + "@visx/visx@npm:^1.1.0": version: 1.18.1 resolution: "@visx/visx@npm:1.18.1" @@ -9168,6 +9540,21 @@ __metadata: languageName: node linkType: hard +"@visx/voronoi@npm:3.0.0": + version: 3.0.0 + resolution: "@visx/voronoi@npm:3.0.0" + dependencies: + "@types/d3-voronoi": ^1.1.9 + "@types/react": "*" + classnames: ^2.3.1 + d3-voronoi: ^1.1.2 + prop-types: ^15.6.1 + peerDependencies: + react: ^16.3.0-0 || ^17.0.0-0 || ^18.0.0-0 + checksum: 7c0f4ff846401bae337bf3e961e3807a1ae7b6e50da3817dd7bfba0c8b40017079ef81baa6145bab61b29368d27d2852b5fcf17ee52b372ab17ff2d81a087b9a + languageName: node + linkType: hard + "@visx/xychart@npm:1.18.1": version: 1.18.1 resolution: "@visx/xychart@npm:1.18.1" @@ -9200,6 +9587,38 @@ __metadata: languageName: node linkType: hard +"@visx/xychart@npm:^3.1.0": + version: 3.1.2 + resolution: "@visx/xychart@npm:3.1.2" + dependencies: + "@types/lodash": ^4.14.172 + "@types/react": "*" + "@visx/annotation": 3.0.1 + "@visx/axis": 3.1.0 + "@visx/event": 3.0.1 + "@visx/glyph": 3.0.0 + "@visx/grid": 3.0.1 + "@visx/react-spring": 3.1.0 + "@visx/responsive": 3.0.0 + "@visx/scale": 3.0.0 + "@visx/shape": 3.0.0 + "@visx/text": 3.0.0 + "@visx/tooltip": 3.1.2 + "@visx/voronoi": 3.0.0 + classnames: ^2.3.1 + d3-array: ^2.6.0 + d3-interpolate-path: 2.2.1 + d3-shape: ^2.0.0 + lodash: ^4.17.21 + mitt: ^2.1.0 + prop-types: ^15.6.2 + peerDependencies: + "@react-spring/web": ^9.4.5 + react: ^16.8.0 || ^17.0.0 || ^ 18.0.0 + checksum: f6776a4da8000811c7f71e2285610dfe26b159f43904ba8e124225219a6791a89b9ded2f71c4f04f38da03f10b59e5f0d63a6b2b7f8c688aebc9107be4ec2e57 + languageName: node + linkType: hard + "@visx/zoom@npm:1.14.1": version: 1.14.1 resolution: "@visx/zoom@npm:1.14.1" @@ -15022,7 +15441,7 @@ __metadata: languageName: node linkType: hard -"d3-scale@npm:4": +"d3-scale@npm:4, d3-scale@npm:^4.0.2": version: 4.0.2 resolution: "d3-scale@npm:4.0.2" dependencies: @@ -30597,6 +31016,23 @@ __metadata: languageName: node linkType: hard +"react-spring@npm:^9.7.1": + version: 9.7.1 + resolution: "react-spring@npm:9.7.1" + dependencies: + "@react-spring/core": ~9.7.1 + "@react-spring/konva": ~9.7.1 + "@react-spring/native": ~9.7.1 + "@react-spring/three": ~9.7.1 + "@react-spring/web": ~9.7.1 + "@react-spring/zdog": ~9.7.1 + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 + checksum: 75f31601d9a8e94e93e66ccfed2ced8cc71399835cf4d509eae6b373315f182f3b5ca0d96da7cd132f01faa6b1cf89dcf087ca43bf40e51c3202c6713f730c28 + languageName: node + linkType: hard + "react-table@npm:^7.7.0": version: 7.8.0 resolution: "react-table@npm:7.8.0"