vueplotlib
This repository will serve as a place for reusable Vue plot components (built with D3). These components were developed for use in ExploSig.
Features
- Declarative: declare scales, data, and styling options
- Interactive: hover and click events, axis brushing, downloading (to PNG)
- Fast: canvas plots
- Linked: declarative API enables automatic linking of scales across plots
- History: record interactions for forward/backward navigation, import/export (via JSON)
- Composable: axes and legends are separate from plots, mix and match
- Customizable: extend the scale classes (see GenomeScale, BinaryScale). extend the history capabilities.
Installation
yarn add vueplotlib
Example Usage
Please see the vueplotlib-examples repository for minimal examples of usage.
Plot Components
- BarPlot
✅ - StackedBarPlot
✅ - ScatterPlot
✅ - TrackPlot
✅ - MultiTrackPlot
✅ - BoxPlot
✅ - MultiBoxPlot
✅ - HierarchicalMultiTrackPlot
✅
Stratified Plot Components
- StratifiedBoxPlot
✅ - StratifiedScatterPlot
✅ - StratifiedSinaPlot
✅ - StratifiedKaplanMeierPlot
✅
Genome Plot Components
- GenomeScatterPlot
✅ - GenomeStackedBarPlot
✅ - GenomeTrackPlot
✅ - GenomeMultiTrackPlot
✅
Axis Components
- Axis
✅ - GenomeAxis
✅ - DendrogramAxis
✅
Legend Components
- CategoricalLegend
✅ - ContinuousLegend
✅
Other Components
- PlotContainer
✅ - SortOptions
✅
Future Plans for Components
- GenomeBarPlot
- GroupedBarPlot
- ViolinPlot
- SinaPlot
- BeeswarmPlot
- JitterPlot
- HorizontalBarPlot
- HorizontalStackedBarPlot
- HorizontalBoxPlot
- HorizontalMultiBoxPlot
- LinePlot
- KaryotypePlot
- GenomeGenePlot
Development
Install dependencies:
yarn
Serve for development at http://localhost:8080:
yarn run serve
Build for production (generates /dist
and /examples
):
yarn run build
Run tests with jest:
yarn run test
Compile documentation with documentationjs:
yarn run docs
Goals:
- props should declare visual encodings of data
- example: the StackedBarPlot component will accept the following props:
x="sample_id" y="exposure" c="signature" // color
- example: the StackedBarPlot component will accept the following props:
- props should declare where to find the data
- data will never be passed directly to a plot - instead it will be globally accessible by a key
- for now, assume data is stored in a global JSON object with
<key>: <data>
mappings - example: for a dataset with key
exposures_data
, the following prop would specify to a plot component that this dataset should be used:data="exposures_data"
- props should declare where to find the scales
- scales will be ES6 classes with different APIs depending on whether categorical, continuous, etc...
- scales will always expose a domain
- scales will always expose a domain-var-to-color function
- the color scale (or even individual colors) should also be able to be changed programmatically
- scales will always expose a domain-var-to-human-readable-text function
- categorical scales will always expose a sort function that takes in a specification of the data that will define the ordering
- scales will always expose a filter function (and a corresponding filter-reset function)
- if categorical, this will accept an array of new values
- if continuous, this will accept a
[min, max]
array - if binary, this will accept a boolean value
- scales should contain all of the information necessary to draw a legend
- scales will never be passed directly to a plot - instead they will be globally accessible by a key
- for now, assume scales are stored in a global JSON object with
<variable>: <scale>
mappings - example: for a variable
sample_id
, the following prop would specify to a plot component that this scale object should be used for the x axis:x="sample_id"
- plots should assume which type of scale is on which axis
- for example, a bar plot (with vertical bars) would assume a continuous y scale and a categorical x
- events should alert plots when a scale has been mutated
- these alerts should specify which scale has been updated using a key
- plot components should listen for these updates and re-draw if necessary
- scales may be mutated upon filter or zoom
- data should be immutable
- even small variations of data sets should be stored in a separate
DataContainer
instance - however, plots may need to subscribe to data updates for asynchronous loading reasons
- even small variations of data sets should be stored in a separate
- plots should NOT draw their own axes
- axes should be independent of plots
- axes should be contained in their own components
- axes should accept props specifying which scale to use, and where to draw
- example:
variable="sample_id" side="bottom"
- example:
- axes should be brush-able
- plots and axes should accept
width
andheight
props- container components should be responsible for keeping plot and axis props in sync if they are dynamic
- example:
:pWidth="windowWidth" :pHeight="300"
- plots should accept margin props
- container components should be responsible for keeping margin props in sync if they are dynamic
- example:
:pMarginTop="10" :pMarginLeft="50" :pMarginRight="20" :pMarginBottom="0"
- plots should emit click events, specifying variables in a predefined order to a prop-supplied callback
- example:
:clickHandler="chooseSample" // will be called with chooseSample(x, y, c)
- plots should have tooltips
- tooltips should obtain human-readable variable names from the appropriate scale
- plots should dispatch applicable hover events
- dispatching should be done through the scale
- example:
- hovering on a section of a bar on a stacked bar plot would cause dispatches for (at least) the
x
andcolor
variables
- hovering on a section of a bar on a stacked bar plot would cause dispatches for (at least) the
- the internals of the drawing of the plots should be abstracted away as much as possible
- details of SVG, canvas, etc. implementation should be contained
- all meaningful interactions will be stored in a history stack
- meaningful interactions: scale filter/zoom/sort
- will allow backward(undo)/forward(redo) operations
- will allow "replaying" of the user's interactions
- will allow sharing of a user session, so that others may replay or step through the interactions
- but this should also be optional, for example if the user chooses not to supply the stack to a plot via prop
- because of the emphasis of linked scales across datasets, for now this implies that legends are NOT "attached" to plots
-
rather, legends should be contained in a separate container component that is fixed on the screen
- this container should house all of the legends for all of the plots, then based on highlighting, scroll to the specified inner legend
- for example, through a
ScrollingLegendContainer
component
- for example, through a
- could instead prioritize which legends are showing in the container based on what plots are currently visible on the screen
- for example, through a
PriorityLegendContainer
component
- for example, through a
- this container should house all of the legends for all of the plots, then based on highlighting, scroll to the specified inner legend
-
this will allow legend styles to be fixed no matter the height (font sizes, listing heights/widths, etc)
-
but the great thing is that because everything is decoupled, in the future this assumption could easily be relaxed through creation of components that act how the axes currently work (fill the space on one side of the plot through the
pMargin***
prop and theside
prop, then lay out based on available space)- for example, through a set of
AttachedLegend
components
- for example, through a set of
-
This was inspired by the following projects: