legend item dot

This commit is contained in:
Nate Kelley 2025-02-26 14:39:27 -07:00
parent 1ca8ee048d
commit 7f8cfe4b4f
No known key found for this signature in database
GPG Key ID: FD90372AB8D98B4F
3 changed files with 146 additions and 108 deletions

View File

@ -1,19 +1,62 @@
import { createStyles } from 'antd-style';
import React, { useMemo } from 'react';
import { useMemoizedFn } from 'ahooks';
import { BusterChartLegendItem } from './interfaces';
import { type BusterChartLegendItem } from './interfaces';
import { ChartType } from '../interfaces';
import { Target } from '../../icons';
import { cn } from '@/lib/classMerge';
import { cva, type VariantProps } from 'class-variance-authority';
export const LegendItemDot: React.FC<{
color: string | undefined;
inactive: boolean;
type: BusterChartLegendItem['type'];
onFocusItem?: () => void;
size?: 'sm' | 'md';
}> = React.memo(({ color, type, inactive, onFocusItem, size = 'md' }) => {
const { styles, cx } = useStyles();
const itemVariants = cva(
'dot group relative flex items-center justify-center transition-all duration-300',
{
variants: {
size: {
sm: 'w-[8px] h-[12px]',
default: 'w-[18px] h-[12px]'
}
}
}
);
const dotVariants = cva('bg-border transition-colors duration-100', {
variants: {
size: {
sm: '',
default: ''
},
type: {
bar: 'w-[18px] h-[12px] rounded-sm',
line: 'w-[18px] h-[4px] rounded-sm',
scatter: 'w-[12px] h-[12px] rounded-full'
}
},
compoundVariants: [
{
size: 'sm',
type: 'bar',
className: 'w-[8px] h-[8px] rounded-[1.5px]'
},
{
size: 'sm',
type: 'line',
className: 'w-[8px] h-[2px] rounded-1.5px'
},
{
size: 'sm',
type: 'scatter',
className: 'w-[8px] h-[8px]'
}
]
});
export const LegendItemDot: React.FC<
{
color: string | undefined;
inactive: boolean;
type: BusterChartLegendItem['type'];
onFocusItem?: () => void;
} & VariantProps<typeof itemVariants>
> = React.memo(({ color, type, inactive, onFocusItem, size = 'default' }) => {
const hasFocusItem = onFocusItem !== undefined;
const onClick = useMemoizedFn((e: React.MouseEvent<HTMLDivElement>) => {
@ -32,34 +75,28 @@ export const LegendItemDot: React.FC<{
});
const dotStyle = useMemo(() => {
if (type === ChartType.Line) return styles.lineChartDot;
if (type === ChartType.Scatter) return styles.scatterChartDot;
return styles.barChartDot;
}, [type]);
if (type === ChartType.Line) return dotVariants({ size, type: 'line' });
if (type === ChartType.Scatter) return dotVariants({ size, type: 'scatter' });
return dotVariants({ size, type: 'bar' });
}, [type, size]);
return (
<div
className={cx(
styles.container,
size,
'dot group relative flex items-center justify-center transition-all duration-300'
)}>
<div className={cn(itemVariants({ size }))}>
<div
onClick={onClick}
className={cx(dotStyle, styles.dotcontainer, size, 'transition-colors duration-100', {
inactive,
className={cn(dotStyle, dotVariants({ size }), {
'group-hover:opacity-0': hasFocusItem
})}
style={{ backgroundColor: !inactive ? color : undefined }}></div>
{hasFocusItem && (
<div
onClick={onFocusItemPreflight}
className="absolute hidden w-full items-center justify-center overflow-hidden group-hover:flex">
<div className="focus-item flex h-full w-full items-center justify-center">
className="absolute hidden h-full w-full items-center justify-center overflow-hidden group-hover:flex">
<div className="focus-item group-hover:bg-item-hover flex h-full w-full items-center justify-center rounded-sm">
<div
className={cn(
'flex items-center justify-center',
size === 'sm' ? 'text-xxs' : 'text-sm'
'flex h-full w-full items-center justify-center overflow-hidden',
size === 'sm' ? 'text-xxs' : 'text-xxs'
)}>
<Target />
</div>
@ -70,66 +107,3 @@ export const LegendItemDot: React.FC<{
);
});
LegendItemDot.displayName = 'LegendItemDot';
const useStyles = createStyles(({ token, css }) => {
return {
container: css`
width: 18px;
height: 12px;
&.sm {
width: 8px;
height: 12px;
}
.focus-item {
border-radius: 4px;
&:hover {
background-color: ${token.colorBgElevated} !important;
}
}
`,
dotcontainer: css`
background: ${token.colorBorder};
`,
barChartDot: css`
width: 18px;
height: 12px;
border-radius: 4px;
&.sm {
width: 8px;
height: 8px;
border-radius: 1.5px;
}
`,
lineChartDot: css`
width: 18px;
height: 4px;
border-radius: 4px;
&.sm {
width: 8px;
height: 2px;
}
`,
scatterChartDot: css`
width: 12px;
height: 12px;
border-radius: 100%;
&.sm {
width: 8px;
height: 8px;
}
`,
focusDot: css`
height: 12px;
width: 12px;
border-radius: 4px;
&.sm {
height: 8px;
width: 8px;
}
`
};
});

View File

@ -127,7 +127,7 @@ const LegendItemStandard = React.memo(
clickable: clickable
})}>
<LegendItemDot
size={!hasHeadline ? 'md' : 'sm'}
size={!hasHeadline ? 'default' : 'sm'}
onFocusItem={onFocusItem}
color={item.color}
type={item.type}
@ -175,23 +175,5 @@ const useStyles = createStyles(({ token, css }) => {
border-color: ${token.colorBgContainerDisabled};
}
`
// legendItemHeadline: css`
// padding: 0px 8px;
// border-radius: 8px;
// height: 44px;
// overflow: hidden;
// &.clickable {
// cursor: pointer;
// transition: background 0.125s ease;
// &:hover {
// background: ${token.controlItemBgHover};
// }
// }
// &.inactive {
// border-color: ${token.colorBgContainerDisabled};
// }
// `
};
});

View File

@ -0,0 +1,82 @@
import type { Meta, StoryObj } from '@storybook/react';
import { LegendItemDot } from '../LegendDot';
import { ChartType } from '../../interfaces';
const meta = {
title: 'Base/Charts/LegendDot',
component: LegendItemDot,
parameters: {
layout: 'centered'
},
tags: ['autodocs'],
argTypes: {
color: { control: 'color' },
type: {
control: 'select',
options: [ChartType.Line, ChartType.Bar, ChartType.Scatter]
},
inactive: { control: 'boolean' },
size: {
control: 'select',
options: ['sm', 'default']
}
}
} satisfies Meta<typeof LegendItemDot>;
export default meta;
type Story = StoryObj<typeof meta>;
export const Bar: Story = {
args: {
color: '#1677ff',
type: ChartType.Bar,
inactive: false,
size: 'default'
}
};
export const Line: Story = {
args: {
color: '#1677ff',
type: ChartType.Line,
inactive: false,
size: 'default'
}
};
export const Scatter: Story = {
args: {
color: '#1677ff',
type: ChartType.Scatter,
inactive: false,
size: 'default'
}
};
export const Small: Story = {
args: {
color: '#1677ff',
type: ChartType.Bar,
inactive: false,
size: 'sm'
}
};
export const Inactive: Story = {
args: {
color: '#1677ff',
type: ChartType.Bar,
inactive: true,
size: 'default'
}
};
export const WithFocus: Story = {
args: {
color: '#1677ff',
type: ChartType.Bar,
inactive: false,
size: 'default',
onFocusItem: () => console.log('Focus item clicked')
}
};