Tracker card
The tracker card summarizes a process in a single glance: what it is, where it stands, and how far along it is. The progress itself is declared as a row of Segments, one per step.
TIP
The card pairs well with a Tracker below it: the card gives the summary, the tracker gives the detail.
Props
title: string
The title of the card.
subtitle?: string
A secondary line shown under the title.
icon?: FluxIconName
An icon shown in a tinted box on the right of the header.
color?: FluxColor
The tint of the header icon.
Default: primary
labels?: top | bottom
Where the labels of the segments are rendered.
Default: bottom
Slots
start
Shown before the title, for example a FluxAvatar or a logo.
end
Shown on the right of the header. Holds the boxed icon of the `icon` prop unless you fill it yourself.
default
The segments of the card.
Examples
Live
The running segment fills up before it hands over to the next one, so the card reads as a process instead of a score.
<template>
<FluxFlex
direction="vertical"
:gap="18"
style="width: 100%; max-width: 450px">
<FluxStatisticsTrackerCard
title="Your Order is on the Way"
:subtitle="subtitle"
icon="truck">
<FluxStatisticsTrackerCardSegment
v-for="(step, index) of STEPS"
:key="step"
:label="step"
:state="stateOf(index)"
:min="0"
:max="1"
:value="valueOf(index)"/>
</FluxStatisticsTrackerCard>
<FluxSecondaryButton
icon-leading="play"
:label="running ? 'Running' : 'Run again'"
:is-loading="running"
:disabled="running"
@click="run"/>
</FluxFlex>
</template>
<script
setup
lang="ts">
import { FluxFlex, FluxSecondaryButton } from '@flux-ui/components';
import { FluxStatisticsTrackerCard, FluxStatisticsTrackerCardSegment } from '@flux-ui/statistics';
import { computed, onBeforeUnmount, onMounted, ref } from 'vue';
// Every step takes the same amount of time, so elapsed doubles as the index
// of the running step and as the progress within it.
const STEPS = ['Ordered', 'Packed', 'Shipped', 'Out for delivery', 'Delivered'];
const STEP_DURATION = 900;
const elapsed = ref(0);
const running = ref(false);
const subtitle = computed(() => {
const index = Math.min(Math.floor(elapsed.value), STEPS.length - 1);
return elapsed.value >= STEPS.length
? 'Delivered · Thank you for your order'
: `Step ${index + 1} of ${STEPS.length} · ${STEPS[index]}`;
});
let frame = 0;
onMounted(() => run());
onBeforeUnmount(() => cancelAnimationFrame(frame));
function clamp(value: number): number {
return Math.min(Math.max(value, 0), 1);
}
function stateOf(index: number): 'active' | 'done' | 'todo' {
if (elapsed.value >= index + 1) {
return 'done';
}
return elapsed.value > index ? 'active' : 'todo';
}
function valueOf(index: number): number {
return clamp(elapsed.value - index);
}
function run(): void {
cancelAnimationFrame(frame);
running.value = true;
elapsed.value = 0;
const start = performance.now();
const tick = (now: number) => {
elapsed.value = Math.min((now - start) / STEP_DURATION, STEPS.length);
if (elapsed.value < STEPS.length) {
frame = requestAnimationFrame(tick);
} else {
running.value = false;
}
};
frame = requestAnimationFrame(tick);
}
</script>Labels
Segments can carry a label. Set labels to top to render them above the bar instead of below it.
<template>
<FluxFlex
direction="vertical"
:gap="18"
style="width: 100%; max-width: 450px">
<FluxStatisticsTrackerCard
title="Onboarding"
subtitle="3 of 5 steps completed"
icon="user-plus"
labels="bottom">
<FluxStatisticsTrackerCardSegment
label="Account"
state="done"/>
<FluxStatisticsTrackerCardSegment
label="Profile"
state="done"/>
<FluxStatisticsTrackerCardSegment
label="Team"
state="done"/>
<FluxStatisticsTrackerCardSegment
label="Billing"
state="active"/>
<FluxStatisticsTrackerCardSegment label="Done"/>
</FluxStatisticsTrackerCard>
<FluxStatisticsTrackerCard
title="Onboarding"
subtitle="3 of 5 steps completed"
icon="user-plus"
labels="top">
<FluxStatisticsTrackerCardSegment
label="Account"
state="done"/>
<FluxStatisticsTrackerCardSegment
label="Profile"
state="done"/>
<FluxStatisticsTrackerCardSegment
label="Team"
state="done"/>
<FluxStatisticsTrackerCardSegment
label="Billing"
state="active"/>
<FluxStatisticsTrackerCardSegment label="Done"/>
</FluxStatisticsTrackerCard>
</FluxFlex>
</template>
<script
setup
lang="ts">
import { FluxFlex } from '@flux-ui/components';
import { FluxStatisticsTrackerCard, FluxStatisticsTrackerCardSegment } from '@flux-ui/statistics';
</script>Colors
The header icon and the segments are tinted separately, so a card can follow the color of the process it tracks.
<template>
<FluxFlex
direction="vertical"
:gap="18"
style="width: 100%; max-width: 450px">
<FluxStatisticsTrackerCard
color="success"
title="Deployment"
subtitle="Production · Build 482"
icon="rocket">
<FluxStatisticsTrackerCardSegment
color="success"
state="done"/>
<FluxStatisticsTrackerCardSegment
color="success"
state="done"/>
<FluxStatisticsTrackerCardSegment
color="success"
state="done"/>
<FluxStatisticsTrackerCardSegment
color="success"
state="done"/>
</FluxStatisticsTrackerCard>
<FluxStatisticsTrackerCard
color="warning"
title="Storage upgrade"
subtitle="Waiting for approval"
icon="database">
<FluxStatisticsTrackerCardSegment
color="warning"
state="done"/>
<FluxStatisticsTrackerCardSegment
color="warning"
state="active"/>
<FluxStatisticsTrackerCardSegment/>
<FluxStatisticsTrackerCardSegment/>
</FluxStatisticsTrackerCard>
</FluxFlex>
</template>
<script
setup
lang="ts">
import { FluxFlex } from '@flux-ui/components';
import { FluxStatisticsTrackerCard, FluxStatisticsTrackerCardSegment } from '@flux-ui/statistics';
</script>