Skip to content

Commit

Permalink
chore: add histogram component
Browse files Browse the repository at this point in the history
  • Loading branch information
adarshpastakia committed Aug 26, 2024
1 parent 8d1710c commit 1b91f8b
Show file tree
Hide file tree
Showing 4 changed files with 240 additions and 0 deletions.
53 changes: 53 additions & 0 deletions packages/data/src/histogram/Histogram.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/*
* React UI Framework
* @version: 1.0.0
*
*
* The MIT License (MIT)
* Copyright (c) 2024 Adarsh Pastakia
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software
* and associated documentation files (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial
* portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
* TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

@layer comp {
.histogram {
.histoLabel {
--tw-gradient-from: var(--color) var(--tw-gradient-from-position);
--tw-gradient-to: color-mix(in lab, var(--color) 20%, var(--fabric-bg))
var(--tw-gradient-to-position);
--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to);

background-color: color(from var(--color) srgb r g b / 5%);

&:hover {
background-color: color(from var(--color) srgb r g b / 15%);
}

&[data-disabled="true"] {
--color: theme(colors.tint.300);

color: theme(colors.tint.600);
}
}

.histoBar {
border-inline-end-color: var(--color);
}

.histoBadge {
background-color: color(from var(--color) srgb r g b / 15%);
}
}
}
129 changes: 129 additions & 0 deletions packages/data/src/histogram/Histogram.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
/*
* React UI Framework
* @version: 1.0.0
*
*
* The MIT License (MIT)
* Copyright (c) 2024 Adarsh Pastakia
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software
* and associated documentation files (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial
* portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
* TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

import {
type ColorType,
type PaletteType,
} from "@react-fabric/core/dist/types/types";
import { compareValues, Format } from "@react-fabric/utilities";
import classNames from "classnames";
import { useMemo } from "react";
import classes from "./Histogram.module.css";

interface HistogramItem {
id: string;
label: string;
count: number;
total?: number;
disabled?: boolean;
}

export interface HistogramProps {
/**
* histogram items list
*/
items: HistogramItem[];
/**
* total count
*/
total?: number;
/**
* histogram bar color
*/
color?: ColorType | PaletteType;
/**
* sorted list
*/
sortBy?: "label" | "count";
/**
* handle histo click
* @param id
* @returns
*/
onClick?: (id: string) => void;
}

export const Histogram = ({
items = [],
total,
color = "primary",
sortBy = "count",
onClick,
}: HistogramProps) => {
const maxTotal = useMemo(() => {
if (total) return total;
return Math.max(...items.map((item) => item.count));
}, [total, items]);

const sorted = useMemo(() => {
return items.sort(
compareValues(sortBy === "label" ? "asc" : "desc", sortBy),
);
}, [items, sortBy]);

return (
<div
className={classes.histogram}
style={
{
"--color": `var(--${color})`,
} as AnyObject
}
>
{sorted.map((item) => (
<div
role="none"
key={item.id}
data-disabled={item.disabled}
className={classNames(
"relative cursor-pointer flex flex-nowrap items-center select-none overflow-hidden px-2 py-1 my-px",
classes.histoLabel,
item.disabled && "pointer-events-none",
)}
onClick={(e) => [onClick?.(item.id), e.stopPropagation()]}
>
<div
className={classNames(
"absolute z-0 inset-0 opacity-50 ltr:bg-gradient-to-r rtl:bg-gradient-to-l from-0% to-100% border-e-2",
classes.histoBar,
)}
style={{
width: `${Math.min(item.count / maxTotal, 1) * 100}%`,
}}
/>
<span className="flex-1 truncate z-1 text-sm font-medium">
{item.label}
</span>
<span
className={classNames(
"z-1 rounded-full text-xs font-medium px-2 py-1 leading-none",
classes.histoBadge,
)}
>
{Format.number(item.count)}
</span>
</div>
))}
</div>
);
};
1 change: 1 addition & 0 deletions packages/data/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@

import "./i18n";

export { Histogram } from "./histogram/Histogram";
export { JsonViewer } from "./json/JsonViewer";
export { VirtualGallery, type VirtualGalleryRef } from "./virtual/Gallery";
export { VirtualList, type VirtualListRef } from "./virtual/List";
Expand Down
57 changes: 57 additions & 0 deletions packages/data/stories/histogram/Histogram.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/*
* React UI Framework
* @version: 1.0.0
*
*
* The MIT License (MIT)
* Copyright (c) 2024 Adarsh Pastakia
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software
* and associated documentation files (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial
* portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
* TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

import { faker } from "@faker-js/faker";
import type { Meta, StoryObj } from "@storybook/react";
import { Histogram } from "../../src";

const meta: Meta<typeof Histogram> = {
component: Histogram,
title: "@data/Histogram",
parameters: {
controls: { exclude: /^(children|as)/ },
},
decorators: [
(Story) => (
<div className="w-96 p-4">
<Story />
</div>
),
],
};

const items = new Array(18).fill(0).map(() => ({
id: faker.number.hex(2048),
label: faker.animal.cat(),
count: faker.number.int({ min: 0, max: 999 }),
}));

export default meta;
type Story = StoryObj<typeof Histogram>;

export const _Histogram: Story = {
render: (args) => {
return <Histogram {...args} items={items} />;
},
args: {},
};

0 comments on commit 1b91f8b

Please sign in to comment.