Skip to content

Commit

Permalink
fix: update BarList (#1004)
Browse files Browse the repository at this point in the history
  • Loading branch information
severinlandolt authored Mar 27, 2024
1 parent 76bf497 commit 260fd86
Show file tree
Hide file tree
Showing 2 changed files with 104 additions and 91 deletions.
194 changes: 103 additions & 91 deletions src/components/vis-elements/BarList/BarList.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
"use client";

import React from "react";
import {
Color,
Expand All @@ -21,24 +23,13 @@ type Bar<T> = T & {
color?: Color;
};

const getWidthsFromValues = (dataValues: number[]) => {
let maxValue = -Infinity;
dataValues.forEach((value) => {
maxValue = Math.max(maxValue, value);
});

return dataValues.map((value) => {
if (value === 0) return 0;
return Math.max((value / maxValue) * 100, 1);
});
};

export interface BarListProps<T = any> extends React.HTMLAttributes<HTMLDivElement> {
data: Bar<T>[];
valueFormatter?: ValueFormatter;
color?: Color;
showAnimation?: boolean;
onValueChange?: (payload: Bar<T>) => void;
sortOrder?: "ascending" | "descending";
}

function BarListInner<T>(props: BarListProps<T>, ref: React.ForwardedRef<HTMLDivElement>) {
Expand All @@ -48,13 +39,29 @@ function BarListInner<T>(props: BarListProps<T>, ref: React.ForwardedRef<HTMLDiv
valueFormatter = defaultValueFormatter,
showAnimation = false,
onValueChange,
sortOrder = "descending",
className,
...other
} = props;

const widths = getWidthsFromValues(data.map((item) => item.value));
const Component = onValueChange ? "button" : "div";
const sortedData = React.useMemo(() => {
if (sortOrder) {
return [...data].sort((a, b) => {
return sortOrder === "ascending" ? a.value - b.value : b.value - a.value;
});
}
return data;
}, [data, sortOrder]);

const rowHeight = "h-9";
const widths = React.useMemo(() => {
const maxValue = Math.max(...sortedData.map((item) => item.value), 0);
return sortedData.map((item) =>
item.value === 0 ? 0 : Math.max((item.value / maxValue) * 100, 2),
);
}, [sortedData]);

const rowHeight = "h-8";

return (
<div
Expand All @@ -64,114 +71,119 @@ function BarListInner<T>(props: BarListProps<T>, ref: React.ForwardedRef<HTMLDiv
"flex justify-between space-x-6",
className,
)}
aria-sort={sortOrder}
{...other}
>
<div className={tremorTwMerge(makeBarListClassName("bars"), "relative w-full")}>
{data.map((item, idx) => {
<div className={tremorTwMerge(makeBarListClassName("bars"), "relative w-full space-y-1.5")}>
{sortedData.map((item, index) => {
const Icon = item.icon;

return (
<div
<Component
key={item.key ?? item.name}
onClick={() => {
onValueChange?.(item);
}}
className={tremorTwMerge(
makeBarListClassName("bar"),
// common
"flex items-center rounded-tremor-small bg-opacity-30",
rowHeight,
item.color || color
? getColorClassNames(item.color ?? (color as Color), colorPalette.background)
.bgColor
: "bg-tremor-brand-subtle dark:bg-dark-tremor-brand-subtle dark:bg-opacity-30",
idx === data.length - 1 ? "mb-0" : "mb-2",
"group w-full flex items-center rounded-tremor-small",
onValueChange
? [
"cursor-pointer",
// hover
"hover:bg-tremor-background-muted dark:hover:bg-dark-tremor-background-subtle/40",
]
: "",
)}
style={{
width: `${widths[idx]}%`,
transition: showAnimation ? "all 1s" : "",
}}
>
<div
className={tremorTwMerge(
"absolute max-w-full flex left-2",
onValueChange ? "cursor-pointer" : "",
"flex items-center rounded transition-all bg-opacity-40",
rowHeight,
item.color || color
? getColorClassNames(item.color ?? (color as Color), colorPalette.background)
.bgColor
: "bg-tremor-brand-subtle dark:bg-dark-tremor-brand-subtle/60",
onValueChange && !(item.color || color)
? "group-hover:bg-tremor-brand-subtle/30 group-hover:dark:bg-dark-tremor-brand-subtle/70"
: "group-hover:bg-opacity-80",
// margin and duration
index === sortedData.length - 1 ? "mb-0" : "",
showAnimation ? "duration-500" : "",
)}
onClick={() => {
onValueChange?.(item);
}}
style={{ width: `${widths[index]}%`, transition: showAnimation ? "all 1s" : "" }}
>
{Icon ? (
<Icon
className={tremorTwMerge(
makeBarListClassName("barIcon"),
// common
"flex-none h-5 w-5 mr-2",
// light
"text-tremor-content",
// dark
"dark:text-dark-tremor-content",
)}
/>
) : null}
{item.href ? (
<a
href={item.href}
target={item.target ?? "_blank"}
rel="noreferrer"
className={tremorTwMerge(
makeBarListClassName("barLink"),
// common
"whitespace-nowrap hover:underline truncate text-tremor-default",
onValueChange ? "cursor-pointer" : "",
// light
"text-tremor-content-emphasis",
// dark
"dark:text-dark-tremor-content-emphasis",
)}
onClick={() => {
onValueChange?.(item);
}}
>
{item.name}
</a>
) : (
<p
className={tremorTwMerge(
makeBarListClassName("barText"),
// common
"whitespace-nowrap truncate text-tremor-default",
onValueChange ? "cursor-pointer" : "",
// light
"text-tremor-content-emphasis",
// dark
"dark:text-dark-tremor-content-emphasis",
)}
onClick={() => {
onValueChange?.(item);
}}
>
{item.name}
</p>
)}
<div className={tremorTwMerge("absolute left-2 pr-4 flex max-w-full")}>
{Icon ? (
<Icon
className={tremorTwMerge(
makeBarListClassName("barIcon"),
// common
"flex-none h-5 w-5 mr-2",
// light
"text-tremor-content",
// dark
"dark:text-dark-tremor-content",
)}
/>
) : null}
{item.href ? (
<a
href={item.href}
target={item.target ?? "_blank"}
rel="noreferrer"
className={tremorTwMerge(
makeBarListClassName("barLink"),
// common
"whitespace-nowrap hover:underline truncate text-tremor-default",
onValueChange ? "cursor-pointer" : "",
// light
"text-tremor-content-emphasis",
// dark
"dark:text-dark-tremor-content-emphasis",
)}
onClick={(event) => event.stopPropagation()}
>
{item.name}
</a>
) : (
<p
className={tremorTwMerge(
makeBarListClassName("barText"),
// common
"whitespace-nowrap truncate text-tremor-default",
// light
"text-tremor-content-emphasis",
// dark
"dark:text-dark-tremor-content-emphasis",
)}
>
{item.name}
</p>
)}
</div>
</div>
</div>
</Component>
);
})}
</div>
<div className={(makeBarListClassName("labels"), "text-right min-w-min")}>
{data.map((item, idx) => (
<div className={makeBarListClassName("labels")}>
{data.map((item, index) => (
<div
key={item.key ?? item.name}
className={tremorTwMerge(
makeBarListClassName("labelWrapper"),
"flex justify-end items-center",
rowHeight,
idx === data.length - 1 ? "mb-0" : "mb-2",
index === sortedData.length - 1 ? "mb-0" : "mb-1.5",
)}
>
<p
className={tremorTwMerge(
makeBarListClassName("labelText"),
// common
"whitespace-nowrap truncate text-tremor-default",
"whitespace-nowrap leading-none truncate text-tremor-default",
// light
"text-tremor-content-emphasis",
// dark
Expand Down
1 change: 1 addition & 0 deletions src/stories/vis-elements/BarList.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ export const Colors: Story = {
args: {
data: getData(Array(3).fill({ href: "https://www.tremor.so/" })),
valueFormatter: (value) => `${value} USD`,
onValueChange: (data) => alert(JSON.stringify(data)),
},
};

Expand Down

0 comments on commit 260fd86

Please sign in to comment.