Creating an Interactive Bar Chart
This tutorial walks you through building this interactive bar chart from scratch.
By the end, you will have a chart that supports adding and removing bars and adjusting their heights with visual controls. The finished component can be embedded in notes and other components, or shared publicly via URL.
If you haven't built anything with CoCube yet, this is a great place to start. After the tutorial, you should be comfortable creating custom components for your own use cases.
The Quick Start Guide covers useful background concepts but is not required for this tutorial.
Getting started
Open the console and click Create Component to open the component editor. You should see a small green square in the center of the screen. This is what a component looks like when no cells have been defined.
How new components look.
This will be the top-level component for the rest of the tutorial. It will contain everything that makes up the chart.
Let's turn it into the backing box, the gray rounded background behind the bars.
The backing box
Cells
In CoCube, every component is defined by a set of named cells. Cells work like spreadsheet cells. Each one holds an expression that produces a value. Instead of being arranged in a grid, they are organized by name. Here is a component defined by two cells:
Cell Name ->
| Height | 250 |
| Width | 250 |
<- Expression
When Width or Height are not defined on a component, they default to 10. Setting them to 250 will make the component much larger.
Setting size
The cells panel on the right side of the editor shows all cells defined on the selected component. Since this component is new, the panel is empty.
Create the Width cell: click the insert field in the cells panel, type Width (capitalization matters), and press insert. If the component disappears, that is expected. New cells default to 0.
Click the new cell in the panel to open its editor, then change the value from 0 to 250.
Repeat for Height. The component should now appear as a large green square.
Setting color
Create the Color cell and click it in the cells panel to open its editor. By default, the cell holds a Number. We need a Color instead. Click the type dropdown next to the value and select Color. The value will change to black . Click it to open a color picker and choose a dark gray like .
Setting shape
The Display cell defines the visual shape of the component. Create it, change its type to Shape, and set it to RoundedRectangle(30) Shape to round the corners.
The backing box is looking good! Later, we will make it resize automatically based on the chart's data.
Attribute cells
Cells can be named anything, but certain names are special. These are called attribute cells. They control how the component looks and behaves. Every cell we have set so far (Width, Height, Color, Display) is an attribute cell.
A full list of attribute cells is available in the Quick Start Guide.
Displaying the bars
The only data needed for each bar is its height. An Array (an ordered list of values) of Number values is sufficient to store the data for all bars.
On the top-level component, create a cell named Data and set it to:
These three numbers represent the heights of the initial bars.
Creating a row layout
We need a layout component to arrange the bars in a horizontal row. DynamicRow is a built-in component that displays its children in a row and automatically resizes to fit them.
In the components panel, insert a DynamicRow and name it "Bars". You should see a few colored boxes appear in the center of the top-level component.
How subcomponents work
Inserting a DynamicRow created a Components cell on the top-level component. This attribute cell defines the subcomponents of a component. It is how smaller components are composed into larger ones. The Components cell expects a Map (a set of named values) of View and/or Component values. The components panel is a convenient way to insert into this cell.
Overriding cells
Click the Bars component in the components panel to select it. Selecting a subcomponent lets you override its cells. This means setting a new expression without changing the component's actual definition. This is how generic components are customized for specific use cases.
With Bars selected, click the Spacing cell and set it to 20. The gaps between the components in the row should grow.
Generating bars from data
With Bars still selected, click the Subcomponents cell. This cell holds the Array of components displayed in the row. Instead of hardcoding components, we want this cell to generate one bar per element in the Data array. That way, changing Data automatically changes how many bars are displayed.
Override the Subcomponents cell to:
Since this is a function, first change the cell's type to Fn using the type dropdown. Then select ForEachArray from the function list. The function's arguments will appear. Set each one to match the expression above.
ForEachArray takes an Array and a Component, and produces a new Array by repeating the component once per element. To complete this, we need to define the SingleBar cell in the parent component. Click the "Parent" button in the cells panel to navigate back up.
In the top-level component, create the SingleBar cell and set it to the following Component:
| Height | 30 |
| Width | 30 |
You should now see three green squares in a row. Each square corresponds to one element in the Data array. The last step is making the bars use the correct height.
Connecting bars to data
ForEachArray does more than just repeat a component. For each repetition, it automatically overrides two cells:
- ForEachIndex, the zero-based position in the array.
- ForEachValue, the value from the array at that position. In this case, the bar height.
Using this, we can set each bar's height from the data. In the SingleBar cell, change Height to:
The bars should now reflect their data heights. However, they are vertically centered. Let's align them to the bottom of the row.
In the SingleBar cell, add two more cells. Origin and AlignTo are FixPoint values that control where a component is positioned relative to its parent.
- Set Origin to BottomCenter FixPoint.
- Set AlignTo to BottomCenter FixPoint.
The bars should now be bottom-aligned. Set Color to to give them a distinct color.
The full component definition at this point:
| Color | |||||||||||
| Components |
| ||||||||||
| Data | [40,60,120] | ||||||||||
| Display | RoundedRectangle(30) Shape | ||||||||||
| Height | 250 | ||||||||||
| SingleBar |
| ||||||||||
| Width | 250 |
Adding an "Insert Bar" button
Now let's add a button that inserts new bars into the chart.
Create and position the button
Select the top-level component, then use the components panel to insert a Button named InsertButton. A button should appear in the center of the chart.
Let's move it to the right side of the backing box. Click InsertButton in the components panel to select it, and override these cells:
- Set Origin to CenterLeft FixPoint.
- Set AlignTo to CenterRight FixPoint.
- Set OffsetX to 30.
The button is now aligned to the right edge of the backing box, with some spacing.
Customizing button text
With the button selected, click the ButtonDisplay cell. By default, a Button displays a Padding component wrapping a Text component.
Click the View expression to open the component reference editor, then expand the overrides list. Increase the padding:
- Set PaddingMin to 4.
- Set PaddingLeft to 8.
- Set PaddingRight to 8.
You should see the button grow slightly. Now click the PaddedComponent cell inside the overrides to open the Text reference editor. Override its Text cell to "Insert Bar".
Every built-in component is created with the same cell system you are using now. This means you can create your own versions of any built-in component, and any component you create can be reused just like the built-ins.
Reacting to button presses
The button doesn't do anything yet. By default, when a Button is clicked, it emits an event with the tag ButtonClicked. We can react to this event using an EventHandler, which defines how a component responds to events.
Select the top-level component and create the EventHandler attribute cell. Set it to:
This reads as: "when a ButtonClicked event is matched, set Data to the existing array plus a new element of 50." Clicking the button should now insert a new bar of height 50 into the chart.
Adding buttons to remove bars
Bars can be added, but not yet removed. Let's add a remove button to each bar.
Defining the remove button
Create a new cell in the top-level component named RemoveButton and set it to:
A View is a reference to a component with optional cell overrides. Here, it references the built-in Button component.
Adding the remove button to each bar
Modify the SingleBar cell by setting its Components to:
| "RemoveButton" |
The 3 in the parent cell reference indicates how many levels up to look. The reference needs to resolve from where the component is displayed, not where it is defined. In this case, that is three levels up the component tree.
Configuring the remove button
If you click a remove button now, it adds a bar instead of removing one. That is because these buttons also emit the default ButtonClicked tag. Let's fix this by customizing the remove button.
Open the RemoveButton cell editor and set these overrides:
- Set AlignTo to BottomCenter FixPoint.
- Set Origin to TopCenter FixPoint.
- Set OffsetY to 3.
- Set EmitTag to "RemoveBar". This makes the button emit a custom tag instead of ButtonClicked.
- Set EmitData to . This sends the bar's array index with the event so the handler knows which bar to remove.
- Set ButtonDisplay to:
| "PaddingMin" | 2 | ||||||||
| "PaddedComponent" |
|
The alignment cells position the button below each bar. The EmitData value is evaluated when the button is clicked and attached to the emitted event. It can be retrieved by the event handler using the Matched function.
Handling remove events
On the top-level component, update the EventHandler cell to include a second matcher:
Clicking a remove button will now remove that bar from the chart.
The full component definition at this point:
| Color | |||||||||||||||||||||||||
| Components |
| ||||||||||||||||||||||||
| Data | [40,60,120] | ||||||||||||||||||||||||
| Display | RoundedRectangle(30) Shape | ||||||||||||||||||||||||
| EventHandler | On | ||||||||||||||||||||||||
| Height | 250 | ||||||||||||||||||||||||
| RemoveButton | Button View
| ||||||||||||||||||||||||
| SingleBar |
| ||||||||||||||||||||||||
| Width | 250 |
Adding height editors
Bars can be added and removed, but their heights cannot yet be changed. Let's add a NumberEditor, a built-in numeric input component, below each bar.
Defining the height editor
Create a cell in the top-level component called HeightEditor and set it to:
Keeping this in a top-level cell makes it easy to modify later.
Adding the editor to each bar
Open the SingleBar cell editor. Add the height editor to the Components cell alongside the existing remove button. The result should look like:
| "RemoveButton" | |
| "HeightEditor" |
A number editor should now be visible in the center of each bar. Editing a number won't do anything yet. We will handle those events shortly. First, let's position the editors below each bar.
Positioning the editors
Set the following overrides on the NumberEditor View in the HeightEditor cell:
- Set AlignTo to BottomCenter FixPoint.
- Set Origin to TopCenter FixPoint.
- Set OffsetY to 30.
- Set MinColumns to 1.
MinColumns defines the minimum width (in characters) of the editor. Now override the Input cell so each editor shows its bar's height:
The editor now displays the bar's height, but edits don't take effect yet. The edit event must be handled by the top-level component, not the bar itself, because each bar pulls its height from the shared Data cell.
Forwarding edit events
Open the HeightEditor cell and set its EventHandler override to:
This handler listens for NumberModified events from the number editor. When matched, it uses EvalContainer to build a Map containing both the bar's index and the new height. EvalContainer evaluates every expression inside a container and returns a new container with the results.
The handler then emits this data upward as a custom event. For example, editing bar 2 to height 80 would emit a Map like:
| "Index" | 2 |
| "Height" | 80 |
Handling height changes
On the top-level component, add a third matcher to the EventHandler cell that responds to BarHeightModified events:
This matcher uses Matched to extract the Index and Height from the event data, then updates the correct position in the Data array. Editing a number in a height editor will now change the corresponding bar.
Fitting the backing box
Let's make the backing box resize automatically based on the chart's data.
Dynamic height
The height should match the tallest bar, plus some padding. Set the Height cell to:
Dynamic width
The width should scale with the number of bars. Set the Width cell to:
Conclusion
Nice work! You now have a fully functional bar chart built from scratch. It can be shared via URL or embedded in other components using a View.
The patterns from this tutorial apply to everything you build in CoCube: composing components, overriding cells, using ForEachArray for data-driven layouts, and handling events. Use them to create tools that work exactly the way you need.