KPI Label
10 352
GB
KPI Label
10 352
GB
KPI Label
10 352
GB
KPI Label
10 352
GB
import { HvCard, HvCardContent, HvColor, HvIconContainer, HvTypography, } from "@hitachivantara/uikit-react-core"; const colors = ["positive", "warning", "negative", "info"] satisfies HvColor[]; export default function Demo() { return ( <div className="grid gap-sm grid-cols-1 sm:grid-cols-2 md:grid-cols-4"> {colors.map((color) => ( <HvCard key={color} statusColor={color} bgcolor="bgContainer"> <Kpi title="KPI Label" value="10 352" unit="GB" /> </HvCard> ))} </div> ); } function Kpi({ title, value, unit, }: { title: React.ReactNode; value: React.ReactNode; unit: React.ReactNode; }) { return ( <HvCardContent className="grid gap-sm pb-xs!"> <div className="flex items-center gap-xxs"> <HvIconContainer size="xs" className="p-4px rounded-round bg-bgContainerSecondary border border-borderSubtle" > <div className="i-ph-copy-simple" /> </HvIconContainer> <span>{title}</span> </div> <div className="flex items-baseline gap-2px"> <HvTypography variant="title3">{value}</HvTypography> <HvTypography variant="caption2" className="text-textSubtle"> {unit} </HvTypography> </div> </HvCardContent> ); }
KPI Label
10 352
GB
KPI Label
10 352
GB
KPI Label
10 352
GB
KPI Label
10 352
GB
import { useId, useState } from "react"; import { HvCard, HvCardContent, HvColor, HvIconContainer, HvRadio, HvTypography, } from "@hitachivantara/uikit-react-core"; const colors = ["positive", "warning", "negative", "info"] satisfies HvColor[]; export default function Demo() { const [selectedIndex, setSelectedIndex] = useState(-1); return ( <div className="grid gap-sm grid-cols-1 sm:grid-cols-2 md:grid-cols-4"> {colors.map((color, i) => ( <HvCard key={color} statusColor={color} bgcolor="bgContainer" className="cursor-pointer" onClick={() => setSelectedIndex(i)} selectable selected={i === selectedIndex} > <Kpi title="KPI Label" value="10 352" unit="GB" selected={i === selectedIndex} onSelect={() => setSelectedIndex(i)} /> </HvCard> ))} </div> ); } function Kpi({ title, value, unit, selected, onSelect, }: { title: React.ReactNode; value: React.ReactNode; unit: React.ReactNode; selected: boolean; onSelect: () => void; }) { const titleId = useId(); return ( <HvCardContent className="grid gap-sm pb-xs!"> <div className="flex items-center gap-xxs"> <HvIconContainer size="xs" className="p-4px rounded-round bg-bgContainerSecondary border border-borderSubtle" > <div className="i-ph-copy-simple" /> </HvIconContainer> <span id={titleId}>{title}</span> </div> <div className="flex justify-between items-center"> <div className="flex items-baseline gap-2px"> <HvTypography variant="title3">{value}</HvTypography> <HvTypography variant="caption2" className="text-textSubtle"> {unit} </HvTypography> </div> <HvRadio name="kpi" aria-labelledby={titleId} checked={selected} onClick={onSelect} /> </div> </HvCardContent> ); }
Loading data...
import { useState } from "react"; import useSWR from "swr"; import { HvCard, HvCardContent, HvIconContainer, HvLoading, HvRadio, HvRadioGroup, HvTypography, } from "@hitachivantara/uikit-react-core"; export default function Demo() { const [endpoint, setEndpoint] = useState("1"); return ( <div className="grid gap-sm"> <HvRadioGroup orientation="horizontal" label="Choose endpoint" value={endpoint} onChange={(evt, val) => setEndpoint(val)} > <HvRadio label="Option 1" value="1" /> <HvRadio label="Option 2" value="2" /> <HvRadio label="Bad fetch" value="error" /> </HvRadioGroup> <HvCard statusColor="info" bgcolor="bgContainer" className="w-300px"> <HvCardContent className="grid gap-sm pb-xs! h-90px"> <KpiData id={endpoint} /> </HvCardContent> </HvCard> </div> ); } function KpiData({ id }: { id: string }) { const { data, isLoading, error } = useSWR(id, fetchData); if (isLoading) { return <HvLoading small label="Loading data..." className="flex-row" />; } if (error) { return ( <div className="flex gap-xs items-center justify-center text-negative"> <HvIconContainer className="rounded-4px bg-negativeDimmed border border-negativeBorder"> <div className="i-ph-warning-diamond" /> </HvIconContainer> <HvTypography>{error.message}</HvTypography> </div> ); } return ( <> <div className="flex items-center gap-xxs"> <HvIconContainer size="xs" className="p-4px rounded-round bg-bgContainerSecondary border border-borderSubtle" > <div className="i-ph-copy-simple" /> </HvIconContainer> <span>{data?.title}</span> </div> <div className="flex items-baseline gap-2px"> <HvTypography variant="title3">{data?.value}</HvTypography> <HvTypography variant="caption2" className="text-textSubtle"> {data?.unit} </HvTypography> </div> </> ); } function fetchData(id: string) { interface Data { title: string; value: number; unit: string; } return new Promise<Data>((resolve, reject) => { setTimeout(() => { if (Number.isNaN(Number(id))) { reject(new Error("Error fetching data")); return; } resolve({ title: `Title ${id}`, value: parseInt(id.repeat(6), 8) % 10 ** 5, unit: "GB", }); }, 4000); }); }