Grouped Bar
Basic Configuration
The Grouped Bar component has been designed to work together with XY Container. The minimal Grouped Bar configuration looks like:
- React
- Angular
- Svelte
- Vue
- Solid
- TypeScript
import { VisXYContainer, VisGroupedBar } from '@unovis/react'
function Component(props) {
const data: DataRecord[] = props.data
const x = (d: DataRecord) => d.x
const y = [
(d: DataRecord) => d.y,
(d: DataRecord) => d.y1,
(d: DataRecord) => d.y2
]
return (
<VisXYContainer data={data}>
<VisGroupedBar x={x} y={y}/>
</VisXYContainer>
)
}
@Component({
templateUrl: 'template.html'
})
export class Component {
@Input data: DataRecord[];
x = (d: DataRecord) => d.x
y = [
(d: DataRecord) => d.y,
(d: DataRecord) => d.y1,
(d: DataRecord) => d.y2
]
}
<vis-xy-container [data]="data">
<vis-grouped-bar [x]="x" [y]="y"></vis-grouped-bar>
</vis-xy-container>
<script lang='ts'>
import { VisXYContainer, VisGroupedBar } from '@unovis/svelte'
export let data: DataRecord[]
const x = (d: DataRecord) => d.x
const y = [
(d: DataRecord) => d.y,
(d: DataRecord) => d.y1,
(d: DataRecord) => d.y2
]
</script>
<VisXYContainer {data}>
<VisGroupedBar {x} {y}/>
</VisXYContainer>
<script setup lang="ts">
import { VisXYContainer, VisGroupedBar } from '@unovis/vue'
const props = defineProps<{ data: DataRecord[] }>()
const x = (d: DataRecord) => d.x
const y = [
(d: DataRecord) => d.y,
(d: DataRecord) => d.y1,
(d: DataRecord) => d.y2
]
</script>
<template>
<VisXYContainer :data="data">
<VisGroupedBar :x="x" :y="y" />
</VisXYContainer>
</template>
import { VisXYContainer, VisGroupedBar } from '@unovis/solid'
function Component(props) {
const data: DataRecord[] = () => props.data
const x = (d: DataRecord) => d.x
const y = [
(d: DataRecord) => d.y,
(d: DataRecord) => d.y1,
(d: DataRecord) => d.y2
]
return (
<VisXYContainer data={data()}>
<VisGroupedBar x={x} y={y}/>
</VisXYContainer>
)
}
import { XYContainer, GroupedBar } from '@unovis/ts'
import { data, DataRecord } from './data'
const container = new XYContainer<DataRecord>(node, {
components: [
new GroupedBar<DataRecord>({
x: (d: DataRecord) => d.x,
y: [
(d: DataRecord) => d.y,
(d: DataRecord) => d.y1,
(d: DataRecord) => d.y2
]
})
]
}, data)
Orientation
Grouped Bar supports horizontal and vertical orientation.
- React
- Angular
- Svelte
- Vue
- Solid
- TypeScript
<VisGroupedBar x={x} y={y} orientation="horizontal"/>
<vis-grouped-bar
[x]="x"
[y]="y"
orientation="horizontal"
></vis-grouped-bar>
<VisGroupedBar {x} {y} orientation="horizontal"/>
<VisGroupedBar :x="x" :y="y" orientation="horizontal" />
<VisGroupedBar x={x} y={y} orientation="horizontal"/>
const groupedBar = new GroupedBar<DataRecord>({ x, y, orientation: "horizontal" })
Bar Colors
Set the color of the bar by assigning the color property to a hex string and/or by assigning the color property to a function evaluated per each bar. In this example, each bar's color is assigned based on its index:
- React
- Angular
- Svelte
- Vue
- Solid
- TypeScript
function Component(props) {
const data: DataRecord[] = props.data
const x = (d: DataRecord) => d.x
const y = [
(d: DataRecord) => d.y,
(d: DataRecord) => d.y1,
(d: DataRecord) => d.y2
]
const color = (d: DataRecord, i: number) => ['#04c0c7', '#5144d3', '#da348f'][i]
return (
<VisGroupedBar x={x} y={y} color={color}/>
)
}
@Component({
template: '<vis-grouped-bar [x]="x" [y]="y" [color]="color"></vis-grouped-bar>'
})
export class Component {
@Input data: DataRecord[];
x = (d: DataRecord) => d.x
y = [
(d: DataRecord) => d.y,
(d: DataRecord) => d.y1,
(d: DataRecord) => d.y2
]
color = (d: DataRecord, i: number) => ['#04c0c7', '#5144d3', '#da348f'][i]
}
<script lang='ts'>
import { VisXYContainer, VisGroupedBar } from '@unovis/svelte'
export let data: DataRecord[]
const x = (d: DataRecord) => d.x
const y = [
(d: DataRecord) => d.y,
(d: DataRecord) => d.y1,
(d: DataRecord) => d.y2
]
const color = (d: DataRecord, i: number) => ['#04c0c7', '#5144d3', '#da348f'][i]
</script>
<VisGroupedBar {x} {y} {color}/>
<script setup lang="ts">
import { VisXYContainer, VisGroupedBar } from '@unovis/vue'
const props = defineProps<{ data: DataRecord[] }>()
const x = (d: DataRecord) => d.x
const y = [
(d: DataRecord) => d.y,
(d: DataRecord) => d.y1,
(d: DataRecord) => d.y2
]
const color = (d: DataRecord, i: number) => ['#04c0c7', '#5144d3', '#da348f'][i]
</script>
<template>
<VisGroupedBar :x="x" :y="y" :color="color" />
</template>
function Component(props) {
const data: DataRecord[] = () => props.data
const x = (d: DataRecord) => d.x
const y = [
(d: DataRecord) => d.y,
(d: DataRecord) => d.y1,
(d: DataRecord) => d.y2
]
const color = (d: DataRecord, i: number) => ['#04c0c7', '#5144d3', '#da348f'][i]
return (
<VisGroupedBar x={x} y={y} color={color}/>
)
}
const groupedBar = new GroupedBar<DataRecord>({
x: (d: DataRecord) => d.x,
y: [
(d: DataRecord) => d.y,
(d: DataRecord) => d.y1,
(d: DataRecord) => d.y2
],
color: (d: DataRecord, i: number) => ['#04c0c7', '#5144d3', '#da348f'][i]
})
Custom Bar Styles
When color alone isn't enough, the barStyle accessor lets you style individual bars. It's called for
every bar with the datum and the bar's index within the group, and the returned object is applied to the
bar as inline styles. Property names can be camelCase or kebab-case, and CSS custom properties (--*)
are supported. A common use case is rendering projected or estimated values differently from actuals:
const barStyle = (d: DataRecord, i: number) => d.x > 5
? { strokeDasharray: '4 3', strokeWidth: 1, fillOpacity: 0.2 }
: undefined
A few things to keep in mind:
- The properties the component manages itself —
fill,cursorandmask— take precedence over their regular sources (like thecoloraccessor) and stay animated; all other properties are applied instantly. - Properties that are no longer returned for a bar are removed on the next render, so conditional styles clean up after themselves.
- Since inline styles bypass the CSS cascade, use CSS variable references (e.g.
stroke: 'var(--my-color)') for values that should respond to theming.
Rounded Corners
You can apply rounded corners to the top bar in your Grouped Bar component using the roundedCorners property, which accepts
either a number (in pixels) or boolean argument.
- React
- Angular
- Svelte
- Vue
- Solid
- TypeScript
<VisGroupedBar x={x} y={y} roundedCorners={true}/>
<vis-grouped-bar
[x]="x"
[y]="y"
[roundedCorners]="true"
></vis-grouped-bar>
<VisGroupedBar {x} {y} roundedCorners={true}/>
<VisGroupedBar :x="x" :y="y" :roundedCorners="true" />
<VisGroupedBar x={x} y={y} roundedCorners={true}/>
const groupedBar = new GroupedBar<DataRecord>({ x, y, roundedCorners: true })
Bar Sizing
There are multiple configuration properties that contribute to the size of you bars in Grouped Bar. Some are on the group level while others on the individual.
Group Width
By default, the width of the bars is calculated automatically based on their count. But you can also strictly set the bar's width in pixels using the barWidth property:
- React
- Angular
- Svelte
- Vue
- Solid
- TypeScript
<VisGroupedBar x={x} y={y} groupWidth={50}/>
<vis-grouped-bar [x]="x" [y]="y" [groupWidth]="50"></vis-grouped-bar>
<VisGroupedBar {x} {y} groupWidth={50}/>
<VisGroupedBar :x="x" :y="y" :groupWidth="50" />
<VisGroupedBar x={x} y={y} groupWidth={50}/>
const groupedBar = new GroupedBar<DataRecord>({ x, y, groupWidth: 50 })
Limiting Dynamic Group Width
When you don't know the number of bars in advance, and you're relying on automatic bar width calculation, you might want to limit the maximum bar width to prevent the bars from being too wide when there are just a few of them. That can be achieved by setting the groupMaxWidth property.
- React
- Angular
- Svelte
- Vue
- Solid
- TypeScript
<VisGroupedBar x={x} y={y}/>
<vis-grouped-bar [x]="x" [y]="y"></vis-grouped-bar>
<VisGroupedBar {x} {y}/>
<VisGroupedBar :x="x" :y="y" />
<VisGroupedBar x={x} y={y}/>
const groupedBar = new GroupedBar<DataRecord>({ x, y })
<VisGroupedBar x={x} y={y} groupMaxWidth={25}/>
<vis-grouped-bar
[x]="x"
[y]="y"
[groupMaxWidth]="25"
></vis-grouped-bar>
<VisGroupedBar {x} {y} groupMaxWidth={25}/>
<VisGroupedBar :x="x" :y="y" :groupMaxWidth="25" />
<VisGroupedBar x={x} y={y} groupMaxWidth={25}/>
const groupedBar = new GroupedBar<DataRecord>({ x, y, groupMaxWidth: 25 })
Group vs. Bar Padding
Another way to control bar width is by adding padding to bar groups (groupPadding property) or
individual bars (`barPadding). The value which specifies how much of the available sector should be
empty in the range of [0,1].
- React
- Angular
- Svelte
- Vue
- Solid
- TypeScript
<VisGroupedBar x={x} y={y} groupPadding={0.5}/>
<vis-grouped-bar
[x]="x"
[y]="y"
[groupPadding]="0.5"
></vis-grouped-bar>
<VisGroupedBar {x} {y} groupPadding={0.5}/>
<VisGroupedBar :x="x" :y="y" :groupPadding="0.5" />
<VisGroupedBar x={x} y={y} groupPadding={0.5}/>
const groupedBar = new GroupedBar<DataRecord>({ x, y, groupPadding: 0.5 })
- React
- Angular
- Svelte
- Vue
- Solid
- TypeScript
<VisGroupedBar x={x} y={y} barPadding={0.5}/>
<vis-grouped-bar [x]="x" [y]="y" [barPadding]="0.5"></vis-grouped-bar>
<VisGroupedBar {x} {y} barPadding={0.5}/>
<VisGroupedBar :x="x" :y="y" :barPadding="0.5" />
<VisGroupedBar x={x} y={y} barPadding={0.5}/>
const groupedBar = new GroupedBar<DataRecord>({ x, y, barPadding: 0.5 })
Minimum Bar Height
When you have highly scattered data with very low and high values, the bars corresponding to the lower values can be so small, so they become invisible.
If you want to prevent that you can set the minimum bar height to 1 pixel using the barMinHeight boolean property.
- React
- Angular
- Svelte
- Vue
- Solid
- TypeScript
<VisGroupedBar x={x} y={y} barMinHeight={1}/>
<vis-grouped-bar [x]="x" [y]="y" [barMinHeight]="1"></vis-grouped-bar>
<VisGroupedBar {x} {y} barMinHeight={1}/>
<VisGroupedBar :x="x" :y="y" :barMinHeight="1" />
<VisGroupedBar x={x} y={y} barMinHeight={1}/>
const groupedBar = new GroupedBar<DataRecord>({ x, y, barMinHeight: 1 })
Preventing Overlaps with dataStep
When your data has gaps, it's impossible to do calculate of the bar width automatically. The visualization will still try to do that, but most likely the result will be wrong,
and you'll see wide overlapping bars. However, you can help the calculation by setting your data step implicitly using the dataStep property.
Consider the following example, with data mainly clumped in the domain 0 < x < 1:
- React
- Angular
- Svelte
- Vue
- Solid
- TypeScript
<VisGroupedBar x={x} y={y}/>
<vis-grouped-bar [x]="x" [y]="y"></vis-grouped-bar>
<VisGroupedBar {x} {y}/>
<VisGroupedBar :x="x" :y="y" />
<VisGroupedBar x={x} y={y}/>
const groupedBar = new GroupedBar<DataRecord>({ x, y })
<VisGroupedBar x={x} y={y} dataStep={0.1}/>
<vis-grouped-bar [x]="x" [y]="y" [dataStep]="0.1"></vis-grouped-bar>
<VisGroupedBar {x} {y} dataStep={0.1}/>
<VisGroupedBar :x="x" :y="y" :dataStep="0.1" />
<VisGroupedBar x={x} y={y} dataStep={0.1}/>
const groupedBar = new GroupedBar<DataRecord>({ x, y, dataStep: 0.1 })
Using Ordinal Data
Read our guide about using ordinal/categorical values with XY Components here
Events
import { GroupedBar } from '@unovis/ts'
...
events = {
[GroupedBar.selectors.bar]: {
click: (d: DataRecord) => {},
...
},
[GroupedBar.selectors.barGroup]: {
mouseover: (d: DataRecord[]) => {},
...
},
}
- React
- Angular
- Svelte
- Vue
- Solid
- TypeScript
<VisGroupedBar x={x} y={y} events={events}/>
<vis-grouped-bar [x]="x" [y]="y" [events]="events"></vis-grouped-bar>
<VisGroupedBar {x} {y} {events}/>
<VisGroupedBar :x="x" :y="y" :events="events" />
<VisGroupedBar x={x} y={y} events={events}/>
const groupedBar = new GroupedBar<DataRecord>({ x, y, events })
CSS Variables
The Grouped Bar component supports additional styling via CSS variables that you can define for your visualization container. For example:
.custom-vis {
--vis-grouped-bar-stroke-width: 2px;
--vis-grouped-bar-stroke-color: #000;
--vis-grouped-bar-cursor: crosshair;
}
Supported CSS variables and their default values
--vis-grouped-bar-cursor: default;
--vis-grouped-bar-fill-color: var(--vis-color-main);
--vis-grouped-bar-stroke-color: none;
--vis-grouped-bar-stroke-width: 0px;
--vis-grouped-bar-hover-stroke-width: 1px;
--vis-grouped-bar-hover-stroke-color: none;
Component Props
| Name | Type | Description |
|---|---|---|
| * required property |