Allocation Bar
Proportional weights bar with a legend of labels and mono percentages.
- AAPLx40%
- NVDAx30%
- TSLAx20%
- SPACEX-pre10%
- AAPLx 40%
- NVDAx 30%
- TSLAx 20%
- SPACEX-pre 10%
- AAPLx 40%
- NVDAx 30%
- TSLAx 20%
- SPACEX-pre 10%
import { AllocationBar } from "@/components/allocation-bar/allocation-bar";
export default function Demo() { return ( <AllocationBar className="max-w-md" segments={[ { label: "AAPLx", value: 40 }, { label: "NVDAx", value: 30 }, { label: "TSLAx", value: 20 }, { label: "SPACEX-pre", value: 10 }, ]} /> );}<script setup lang="ts">import { AllocationBar } from "@/components/ui/allocation-bar";
const segments = [ { label: "AAPLx", value: 40 }, { label: "NVDAx", value: 30 }, { label: "TSLAx", value: 20 }, { label: "SPACEX-pre", value: 10 },];</script>
<template> <AllocationBar class="max-w-md" :segments="segments" /></template><script lang="ts"> import { AllocationBar } from "$lib/components/ui/allocation-bar";
const segments = [ { label: "AAPLx", value: 40 }, { label: "NVDAx", value: 30 }, { label: "TSLAx", value: 20 }, { label: "SPACEX-pre", value: 10 }, ];</script>
<AllocationBar class="max-w-md" {segments} />Installation
Section titled “Installation”npx shadcn@latest add @edmi-ui/allocation-barpnpm dlx shadcn@latest add @edmi-ui/allocation-baryarn dlx shadcn@latest add @edmi-ui/allocation-barbunx shadcn@latest add @edmi-ui/allocation-barPick your package manager above; the choice is remembered. Add the @edmi-ui registry first, see Getting started.
Copy the code from Component source into your project and update the import paths to match your aliases.
npx shadcn-vue@latest add @edmi-ui/allocation-barpnpm dlx shadcn-vue@latest add @edmi-ui/allocation-baryarn dlx shadcn-vue@latest add @edmi-ui/allocation-barbunx shadcn-vue@latest add @edmi-ui/allocation-barPick your package manager above; the choice is remembered. Add the @edmi-ui registry first, see Getting started.
Copy the code from Component source into your project and update the import paths to match your aliases.
npx shadcn-svelte@latest add https://viandwi24.github.io/edmi-ui/r/svelte/allocation-bar.jsonpnpm dlx shadcn-svelte@latest add https://viandwi24.github.io/edmi-ui/r/svelte/allocation-bar.jsonyarn dlx shadcn-svelte@latest add https://viandwi24.github.io/edmi-ui/r/svelte/allocation-bar.jsonbunx --bun shadcn-svelte@latest add https://viandwi24.github.io/edmi-ui/r/svelte/allocation-bar.jsonPick your package manager above; the choice is remembered. Add the @edmi-ui registry first, see Getting started.
Copy the code from Component source into your project and update the import paths to match your aliases.
import { AllocationBar } from "@/components/allocation-bar/allocation-bar";
<AllocationBar className="max-w-md" segments={[ { label: "AAPLx", value: 40 }, { label: "NVDAx", value: 30 }, { label: "TSLAx", value: 20 }, { label: "SPACEX-pre", value: 10 }, ]}/><script setup lang="ts">import { AllocationBar } from "@/components/ui/allocation-bar";</script>
<template> <AllocationBar class="max-w-md" /></template><script lang="ts"> import { AllocationBar } from "$lib/components/ui/allocation-bar";</script>
<AllocationBar class="max-w-md" />Component source
Section titled “Component source”import { cn } from "cn";import type * as React from "react";
type AllocationSegment = { label: string; /** Weight in percent (any positive number; bars are proportional). */ value: number; /** Any CSS color; defaults cycle `--chart-1…5`. */ color?: string;};
// Weights bar with a legend of label + mono percentage.function AllocationBar({ className, segments, showLegend = true, size = "default", ...props}: Omit<React.ComponentProps<"div">, "children"> & { segments: AllocationSegment[]; showLegend?: boolean; /** ✦ `sm`: slimmer bar and a compact mono legend (cards). */ size?: "default" | "sm";}) { const sm = size === "sm"; const colored = segments.map((s, i) => ({ ...s, color: s.color ?? `var(--chart-${(i % 5) + 1})`, })); return ( <div data-slot="allocation-bar" className={cn("w-full", className)} {...props} > <div className="flex gap-[3px]" role="img" aria-label={colored.map((s) => `${s.label} ${s.value}%`).join(", ")} > {colored.map((s) => ( <span key={s.label} className={sm ? "h-2 rounded-[3px]" : "h-2.5 rounded-full"} style={{ flex: s.value, background: s.color }} /> ))} </div> {showLegend ? ( <ul className={cn( "flex flex-wrap items-center gap-x-4 gap-y-1.5", sm ? "mt-3 text-xs text-foreground-2" : "mt-2.5 text-[12.5px]", )} > {colored.map((s) => ( <li key={s.label} className={cn("flex items-center gap-1.5", sm && "font-mono")} > <span className={ sm ? "size-2 rounded-[2px]" : "size-[9px] rounded-[3px]" } style={{ background: s.color }} /> {s.label} <b className={cn( "font-mono", sm ? "font-semibold text-foreground" : "font-medium", )} > {s.value}% </b> </li> ))} </ul> ) : null} </div> );}
export type { AllocationSegment };export { AllocationBar };<script setup lang="ts">import type { HTMLAttributes } from 'vue'import type { AllocationSegment } from '.'import { computed } from 'vue'import { cn } from '@/registry/edmi/lib/utils'
const props = withDefaults(defineProps<{ segments: AllocationSegment[] showLegend?: boolean /** sm: slimmer bar and a compact mono legend (cards). */ size?: 'default' | 'sm' class?: HTMLAttributes['class']}>(), { showLegend: true, size: 'default',})
const colored = computed(() => props.segments.map((s, i) => ({ ...s, color: s.color ?? `var(--chart-${(i % 5) + 1})` })),)</script>
<template> <!-- Weights bar with a legend of label + mono percentage. --> <div data-slot="allocation-bar" :class="cn('w-full', props.class)"> <div class="flex gap-[3px]" role="img" :aria-label="colored.map(s => `${s.label} ${s.value}%`).join(', ')" > <span v-for="s in colored" :key="s.label" :class="size === 'sm' ? 'h-2 rounded-[3px]' : 'h-2.5 rounded-full'" :style="{ flex: s.value, background: s.color }" /> </div> <ul v-if="showLegend" :class="cn('flex flex-wrap items-center gap-x-4 gap-y-1.5', size === 'sm' ? 'mt-3 text-xs text-foreground-2' : 'mt-2.5 text-[12.5px]')" > <li v-for="s in colored" :key="s.label" :class="cn('flex items-center gap-1.5', size === 'sm' && 'font-mono')"> <span :class="size === 'sm' ? 'size-2 rounded-[2px]' : 'size-[9px] rounded-[3px]'" :style="{ background: s.color }" /> {{ s.label }} <b :class="cn('font-mono', size === 'sm' ? 'font-semibold text-foreground' : 'font-medium')">{{ s.value }}%</b> </li> </ul> </div></template>export type AllocationSegment = { label: string; /** Weight in percent (any positive number; bars are proportional). */ value: number; /** Any CSS color; defaults cycle `--chart-1…5`. */ color?: string;};
export { default as AllocationBar } from "./AllocationBar.vue";<script lang="ts" module> export type AllocationSegment = { label: string; /** Weight in percent (any positive number; bars are proportional). */ value: number; /** Any CSS color; defaults cycle `--chart-1…5`. */ color?: string; };</script>
<script lang="ts"> import { cn, type WithElementRef } from "$lib/utils.js"; import type { HTMLAttributes } from "svelte/elements";
let { ref = $bindable(null), class: className, segments, showLegend = true, size = "default", ...restProps }: WithElementRef<Omit<HTMLAttributes<HTMLDivElement>, "children">> & { segments: AllocationSegment[]; showLegend?: boolean; /** ✦ `sm`: slimmer bar and a compact mono legend (cards). */ size?: "default" | "sm"; } = $props();
const colored = $derived( segments.map((s, i) => ({ ...s, color: s.color ?? `var(--chart-${(i % 5) + 1})` })) );</script>
<!-- Weights bar with a legend of label + mono percentage. --><div bind:this={ref} data-slot="allocation-bar" class={cn("w-full", className)} {...restProps}> <div class="flex gap-[3px]" role="img" aria-label={colored.map((s) => `${s.label} ${s.value}%`).join(", ")}> {#each colored as s (s.label)} <span class={size === "sm" ? "h-2 rounded-[3px]" : "h-2.5 rounded-full"} style:flex={s.value} style:background={s.color}></span> {/each} </div> {#if showLegend} <ul class={cn("flex flex-wrap items-center gap-x-4 gap-y-1.5", size === "sm" ? "mt-3 text-xs text-foreground-2" : "mt-2.5 text-[12.5px]")}> {#each colored as s (s.label)} <li class={cn("flex items-center gap-1.5", size === "sm" && "font-mono")}> <span class={size === "sm" ? "size-2 rounded-[2px]" : "size-[9px] rounded-[3px]"} style:background={s.color}></span> {s.label} <b class={cn("font-mono", size === "sm" ? "font-semibold text-foreground" : "font-medium")}>{s.value}%</b> </li> {/each} </ul> {/if}</div>export { type AllocationSegment, default as AllocationBar,} from "./allocation-bar.svelte";Elevation ✦
Section titled “Elevation ✦”Flat only: the bar and legend render no surface, so there is no elevation prop and no elevation demo. See the Elevation guide.
| Part | Prop | Type | Default |
|---|---|---|---|
AllocationBar |
segments |
{ label, value, color? }[] |
required |
showLegend |
boolean |
true |
Colors default to --chart-1…5 in order.
✦ Edmi additions
Section titled “✦ Edmi additions”- Bars are proportional to
value; values need not sum to 100.