mintaka
mintaka copied to clipboard
Mintaka
Mintaka is an open-source chart builder library for Vega-Lite written in React.
What makes it special is that it's ultra-configurable:
- Mintaka uses your React components. All labels, selectboxes, etc, that it draws are provided by you.
- You choose which Vega-Lite properties to expose to the user, and which are TMI.
- You can specify smart chart presets, so users don't need to know anything about Vega-Lite to get started.
- And much more! See below for more.
How to use it in your project
-
Install Mintaka:
npm install mintaka -
Import the
Mintakacomponent:import { Mintaka } from "mintaka" import { Vega } from "react-vega" -
Insert the
<Mintaka>component somewhere in your React project:const [ generatedSpec, setGeneratedSpec ] = useState(null) return ( ... <Mintaka columnTypes={columnTypes} // <-- Tell us what your data looks like setGeneratedSpec={setGeneratedSpec} initialSpec={null} // <-- Pass an initial VL spec for us to load into the builder ui={{ MintakaContainer: MyContainer, // <-- Bring your own UI components ChannelContainer: MyChannelContainer, GenericPickerWidget: MyGenericPickerWidget, ... }} config={{ ... // <-- Which VL properties to show, how to group them, etc. // This has a pretty rich structure, and it's still in motion, // so I'm leaving it out of this spec for the time being! }} presets={{ "Pie chart": {...}, // Specify presets. ... }} /> <Vega data={myDataGoesHere} spec={generatedSpec} /> // <-- Just pass the spec into VegaLite ... )
More info
-
The builder doesn’t draw the chart itself. It just outputs a VL spec, and which you draw by passing into your own Vega-Lite component. Or you can use one I provide. Up to you!
This allows you pass whatever settings you’d like to your VL component. Streamlit, for example, does some magic in order to support fast
.add_rows(). This component doesn’t interfere with that. -
The builder doesn’t actually need to access your data. It only needs to know which columns it has, and what kinds of columns they are, via the
columnTypesparameter. This allows it to work with any data formatFor example: Streamlit can continue to use Arrow, Snowsight can continue to use… whatever it uses today (it doesn’t matter what it is!)
-
The
<Mintaka>component doesn’t actually draw a single HTML element onscreen, like divs, buttons, etc. Instead, it tells React to use the UI components provided via theuiprop. This means:-
You can make it use your own widget library
For example: The Mintaka component could use Streamlit widgets when inside Streamlit, and Snowsight widgets when inside Worksheets.
-
You can customize it quite a bit!
For example: you could make the
markselectbox show little icons next to each mark, with a nice explanation below them. Or you could make thecolor -> valueinput use an actual color picker.
-
-
There’s a very powerful
configparameter in there, which allows you to do a few things:-
Specify how much of VL you’d like to expose to users.
For example: Don’t want to show the
aggregateoption? You can just omit it from theconfigobject!
-
-
For complete noobs, you can specify presets via a
presetsprop.For example: add a “Pie chart” preset that automatically builds a VL spec for a pie chart based on your data.
-
Here’s how this pie chart preset can be specified
presets={{ "Pie chart": { mark: { // Add this to the VL spec type: "arc", tooltip: true, }, findColumns: { // Search for matching columns, and name them A1, B1, etc. A1: { type: ["quantitative"] }, B1: { type: ["nominal", "ordinal"], maxUnique: 20 }, A2: {}, B2: {}, }, ifColumn: { A1: { // If A1 was found, add this to the VL spec encoding: { theta: { field: "A1", aggregate: "sum" }, } }, B1: { // If B1 was found, add this encoding: { color: { field: "B1", bin: null }, } }, A2: { // etc. encoding: { theta: { field: "A2", aggregate: "count" }, } }, B2: { encoding: { color: { field: "B2", bin: true }, } }, }, }, }}
-
Proper documentation is coming soon! In the meantime, check out the demo code for a full-featured example of how to use Mintaka. I also find the TypeScript type defs very useful in lieu of docs.
UI Structure
Below are the custom containers you should pass into <Mintaka>'s ui prop, and how they
get structured in the DOM:
<MintakaContainer>-
<TopUtilBlock>This is a utility component. It's a place for you to add whatever your want.
In particular, it's a good place to add a pagination widget for changing the editor's current "mode":
- modes: Record<string, any>
- currentMode: any
- setMode: (any) => void
...and it's also a good place to add a reset button:
- reset: () => void
-
<PresetsContainer>(if exists in current viewMode)-
title: "Chart type"
-
statePath: ["preset"]
-
groupName: null
-
viewMode: null
-
customState: any
-
setCustomState: (any) => void
-
<GenericPickerWidget>
-
-
<LayerBuilder>-
<MarkContainer>(if exists in current viewMode)-
title: "Mark"
-
statePath: ["mark"]
-
groupName: null
-
viewMode: string
-
customState: any
-
setCustomState: (any) => void
-
<GenericPickerWidget>x N- statePath: ["mark", propertyName]
- groupName: string
- widgetHint: string
- label: string
- value: any
- setValue: (any) => void
- items: Record<string, any>
- customState: any
- setCustomState: (any) => void
-
-
<EncodingContainer>(if exists in current viewMode)-
title: "Encoding"
-
statePath: ["encoding"]
-
groupName: null
-
viewMode: string
-
customState: any
-
setCustomState: (any) => void
-
<ChannelContainer>x N (if exists in current viewMode)-
title: "Encoding"
-
statePath: ["encoding", channelName]
-
groupName: null
-
viewMode: string
-
customState: any
-
setCustomState: (any) => void
-
<GenericPickerWidget>x N- statePath: ["encoding", channelName, propertyName]
- groupName: string
- widgetHint: string
- label: string
- value: any
- setValue: (any) => void
- items: Record<string, any>
- customState: any
- setCustomState: (any) => void
-
-
-
-
<BottomUtilBlock>This is another util component. Its props are identical to the TopUtilContainer, so you can choose where you want your mode picker and reset button to live.
- modes: Record<string, any>
- currentMode: any
- setMode: (any) => void
- reset: () => void
-