Skip to content

UI State Metrics Panel

Three datasets, one derived store. $data picks the channel, and every binding in the panel reads from it, so the total, the seven bars, and the footnote all switch together.

Adding a channel is one more store and one more branch in $data. Nothing else in the markup changes.

Visitors
Mon
Tue
Wed
Thu
Fri
Sat
Sun

Code

The following example assumes that you have imported the component and set up the theme.

Metrics Panel
<sv-ui-state>
59 collapsed lines
<sv-store name="$channel" value="direct"></sv-store>
<sv-store type="json" name="$direct" value='{"total":"12.4k","best":"Saturday","bars":[46,58,39,72,64,88,55]}'></sv-store>
<sv-store type="json" name="$search" value='{"total":"31.9k","best":"Wednesday","bars":[62,70,94,81,77,40,33]}'></sv-store>
<sv-store type="json" name="$social" value='{"total":"7.2k","best":"Sunday","bars":[28,24,35,30,52,68,90]}'></sv-store>
<sv-store derive="$channel === 'direct' ? $direct : $channel === 'search' ? $search : $social" name="$data"></sv-store>
<div class="panel">
<div>
<span class="panel-title">Visitors</span>
<strong class="panel-total" bind-text="$data.total"></strong>
</div>
<div class="panel-chart">
<div class="panel-col">
<div class="panel-bar" bind-style="block-size:$data.bars[0] + '%'"></div>
<span>Mon</span>
</div>
<div class="panel-col">
<div class="panel-bar" bind-style="block-size:$data.bars[1] + '%'"></div>
<span>Tue</span>
</div>
<div class="panel-col">
<div class="panel-bar" bind-style="block-size:$data.bars[2] + '%'"></div>
<span>Wed</span>
</div>
<div class="panel-col">
<div class="panel-bar" bind-style="block-size:$data.bars[3] + '%'"></div>
<span>Thu</span>
</div>
<div class="panel-col">
<div class="panel-bar" bind-style="block-size:$data.bars[4] + '%'"></div>
<span>Fri</span>
</div>
<div class="panel-col">
<div class="panel-bar" bind-style="block-size:$data.bars[5] + '%'"></div>
<span>Sat</span>
</div>
<div class="panel-col">
<div class="panel-bar" bind-style="block-size:$data.bars[6] + '%'"></div>
<span>Sun</span>
</div>
</div>
<p class="panel-note" bind-text="'Best day: ' + $data.best"></p>
<div class="panel-channels">
<button type="button" bind-class-toggle="is-active:$channel === 'direct'" bind-event="click:$channel('direct')">
Direct
</button>
<button type="button" bind-class-toggle="is-active:$channel === 'search'" bind-event="click:$channel('search')">
Search
</button>
<button type="button" bind-class-toggle="is-active:$channel === 'social'" bind-event="click:$channel('social')">
Social
</button>
</div>
</div>
</sv-ui-state>
<style>
76 collapsed lines
.panel {
display: grid;
gap: 0.9em;
inline-size: min(24rem, 100%);
padding: 1.2em;
margin-inline: auto;
background-color: var(--sv-gray-6);
border-radius: var(--sv-bdr-rad-md);
.panel-title {
display: block;
font-size: 0.8em;
color: var(--sv-gray-3);
}
.panel-total {
font-size: 1.6em;
}
.panel-chart {
display: grid;
grid-auto-columns: 1fr;
grid-auto-flow: column;
gap: 0.4em;
block-size: 7rem;
}
.panel-col {
display: grid;
grid-template-rows: 1fr auto;
gap: 0.4em;
align-items: end;
span {
font-size: 0.7em;
color: var(--sv-gray-3);
text-align: center;
}
}
.panel-bar {
background-color: var(--sv-accent);
border-radius: var(--sv-bdr-rad-sm);
transition: block-size 0.25s;
}
.panel-note {
margin: 0;
font-size: 0.8em;
color: var(--sv-gray-3);
}
.panel-channels {
display: flex;
gap: 0.3em;
padding-block-start: 0.75em;
border-block-start: var(--sv-bdr-sz-sm) solid var(--sv-gray-5);
button {
flex: 1;
padding: 0.4em 0.5em;
font: inherit;
font-size: 0.85em;
color: var(--sv-gray-3);
cursor: pointer;
background-color: var(--sv-gray-7);
border: var(--sv-bdr-sz-sm) solid transparent;
border-radius: var(--sv-bdr-rad-sm);
&.is-active {
color: var(--sv-gray-0);
border-color: var(--sv-accent);
}
}
}
}
</style>