feat: enhance chart tooltip and legend components with improved type definitions and payload handling
This commit is contained in:
@@ -104,6 +104,15 @@ ${colorConfig
|
|||||||
|
|
||||||
const ChartTooltip = RechartsPrimitive.Tooltip
|
const ChartTooltip = RechartsPrimitive.Tooltip
|
||||||
|
|
||||||
|
type TooltipPayloadItem = {
|
||||||
|
dataKey?: string | number
|
||||||
|
name?: string
|
||||||
|
value?: number | string
|
||||||
|
color?: string
|
||||||
|
payload?: Record<string, unknown> & { fill?: string }
|
||||||
|
fill?: string
|
||||||
|
}
|
||||||
|
|
||||||
function ChartTooltipContent({
|
function ChartTooltipContent({
|
||||||
active,
|
active,
|
||||||
payload,
|
payload,
|
||||||
@@ -118,13 +127,15 @@ function ChartTooltipContent({
|
|||||||
color,
|
color,
|
||||||
nameKey,
|
nameKey,
|
||||||
labelKey,
|
labelKey,
|
||||||
}: React.ComponentProps<typeof RechartsPrimitive.Tooltip> &
|
}: Omit<React.ComponentProps<typeof RechartsPrimitive.Tooltip>, 'payload' | 'label'> &
|
||||||
React.ComponentProps<'div'> & {
|
React.ComponentProps<'div'> & {
|
||||||
hideLabel?: boolean
|
hideLabel?: boolean
|
||||||
hideIndicator?: boolean
|
hideIndicator?: boolean
|
||||||
indicator?: 'line' | 'dot' | 'dashed'
|
indicator?: 'line' | 'dot' | 'dashed'
|
||||||
nameKey?: string
|
nameKey?: string
|
||||||
labelKey?: string
|
labelKey?: string
|
||||||
|
payload?: TooltipPayloadItem[]
|
||||||
|
label?: string | number
|
||||||
}) {
|
}) {
|
||||||
const { config } = useChart()
|
const { config } = useChart()
|
||||||
|
|
||||||
@@ -179,10 +190,10 @@ function ChartTooltipContent({
|
|||||||
>
|
>
|
||||||
{!nestLabel ? tooltipLabel : null}
|
{!nestLabel ? tooltipLabel : null}
|
||||||
<div className="grid gap-1.5">
|
<div className="grid gap-1.5">
|
||||||
{payload.map((item, index) => {
|
{payload.map((item: TooltipPayloadItem, index: number) => {
|
||||||
const key = `${nameKey || item.name || item.dataKey || 'value'}`
|
const key = `${nameKey || item.name || item.dataKey || 'value'}`
|
||||||
const itemConfig = getPayloadConfigFromPayload(config, item, key)
|
const itemConfig = getPayloadConfigFromPayload(config, item, key)
|
||||||
const indicatorColor = color || item.payload.fill || item.color
|
const indicatorColor = color || item.payload?.fill || item.color
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
@@ -193,7 +204,7 @@ function ChartTooltipContent({
|
|||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{formatter && item?.value !== undefined && item.name ? (
|
{formatter && item?.value !== undefined && item.name ? (
|
||||||
formatter(item.value, item.name, item, index, item.payload)
|
formatter(item.value, item.name, item as never, index, item.payload as never)
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
{itemConfig?.icon ? (
|
{itemConfig?.icon ? (
|
||||||
@@ -250,16 +261,23 @@ function ChartTooltipContent({
|
|||||||
|
|
||||||
const ChartLegend = RechartsPrimitive.Legend
|
const ChartLegend = RechartsPrimitive.Legend
|
||||||
|
|
||||||
|
type LegendPayloadItem = {
|
||||||
|
value?: string
|
||||||
|
dataKey?: string | number
|
||||||
|
color?: string
|
||||||
|
}
|
||||||
|
|
||||||
function ChartLegendContent({
|
function ChartLegendContent({
|
||||||
className,
|
className,
|
||||||
hideIcon = false,
|
hideIcon = false,
|
||||||
payload,
|
payload,
|
||||||
verticalAlign = 'bottom',
|
verticalAlign = 'bottom',
|
||||||
nameKey,
|
nameKey,
|
||||||
}: React.ComponentProps<'div'> &
|
}: React.ComponentProps<'div'> & {
|
||||||
Pick<RechartsPrimitive.LegendProps, 'payload' | 'verticalAlign'> & {
|
|
||||||
hideIcon?: boolean
|
hideIcon?: boolean
|
||||||
nameKey?: string
|
nameKey?: string
|
||||||
|
payload?: LegendPayloadItem[]
|
||||||
|
verticalAlign?: 'top' | 'bottom' | 'middle'
|
||||||
}) {
|
}) {
|
||||||
const { config } = useChart()
|
const { config } = useChart()
|
||||||
|
|
||||||
@@ -275,7 +293,7 @@ function ChartLegendContent({
|
|||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{payload.map((item) => {
|
{payload.map((item: LegendPayloadItem) => {
|
||||||
const key = `${nameKey || item.dataKey || 'value'}`
|
const key = `${nameKey || item.dataKey || 'value'}`
|
||||||
const itemConfig = getPayloadConfigFromPayload(config, item, key)
|
const itemConfig = getPayloadConfigFromPayload(config, item, key)
|
||||||
|
|
||||||
|
|||||||
36
eslint.config.mjs
Normal file
36
eslint.config.mjs
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
import nextPlugin from '@next/eslint-plugin-next'
|
||||||
|
import reactPlugin from 'eslint-plugin-react'
|
||||||
|
import hooksPlugin from 'eslint-plugin-react-hooks'
|
||||||
|
import tseslint from 'typescript-eslint'
|
||||||
|
|
||||||
|
export default [
|
||||||
|
{
|
||||||
|
ignores: ['node_modules/**', '.next/**', 'out/**', 'build/**'],
|
||||||
|
},
|
||||||
|
...tseslint.configs.recommended,
|
||||||
|
{
|
||||||
|
files: ['**/*.{js,jsx,ts,tsx}'],
|
||||||
|
plugins: {
|
||||||
|
'@next/next': nextPlugin,
|
||||||
|
react: reactPlugin,
|
||||||
|
'react-hooks': hooksPlugin,
|
||||||
|
},
|
||||||
|
rules: {
|
||||||
|
...nextPlugin.configs.recommended.rules,
|
||||||
|
...nextPlugin.configs['core-web-vitals'].rules,
|
||||||
|
'react/react-in-jsx-scope': 'off',
|
||||||
|
'react-hooks/rules-of-hooks': 'error',
|
||||||
|
'react-hooks/exhaustive-deps': 'warn',
|
||||||
|
'@typescript-eslint/no-unused-vars': [
|
||||||
|
'warn',
|
||||||
|
{ argsIgnorePattern: '^_', varsIgnorePattern: '^_' },
|
||||||
|
],
|
||||||
|
'@typescript-eslint/no-explicit-any': 'warn',
|
||||||
|
},
|
||||||
|
settings: {
|
||||||
|
react: {
|
||||||
|
version: 'detect',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]
|
||||||
@@ -42,7 +42,7 @@ export function parseOFX(content: string): OFXAccount | null {
|
|||||||
amount: Number.parseFloat(amountStr),
|
amount: Number.parseFloat(amountStr),
|
||||||
name: cleanString(name),
|
name: cleanString(name),
|
||||||
memo: memo ? cleanString(memo) : undefined,
|
memo: memo ? cleanString(memo) : undefined,
|
||||||
checkNum,
|
checkNum: checkNum ?? undefined,
|
||||||
type,
|
type,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -71,15 +71,22 @@
|
|||||||
"zod": "3.25.76"
|
"zod": "3.25.76"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@eslint/eslintrc": "^3.3.1",
|
||||||
|
"@next/eslint-plugin-next": "^16.0.5",
|
||||||
"@tailwindcss/postcss": "^4.1.9",
|
"@tailwindcss/postcss": "^4.1.9",
|
||||||
"@types/node": "^22",
|
"@types/node": "^22",
|
||||||
"@types/react": "^19",
|
"@types/react": "^19",
|
||||||
"@types/react-dom": "^19",
|
"@types/react-dom": "^19",
|
||||||
"dotenv": "^17.2.3",
|
"dotenv": "^17.2.3",
|
||||||
|
"eslint": "^9.39.1",
|
||||||
|
"eslint-config-next": "^16.0.5",
|
||||||
|
"eslint-plugin-react": "^7.37.5",
|
||||||
|
"eslint-plugin-react-hooks": "^7.0.1",
|
||||||
"postcss": "^8.5",
|
"postcss": "^8.5",
|
||||||
"tailwindcss": "^4.1.9",
|
"tailwindcss": "^4.1.9",
|
||||||
"tsx": "^4.20.6",
|
"tsx": "^4.20.6",
|
||||||
"tw-animate-css": "1.3.3",
|
"tw-animate-css": "1.3.3",
|
||||||
"typescript": "^5"
|
"typescript": "^5",
|
||||||
|
"typescript-eslint": "^8.48.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
2344
pnpm-lock.yaml
generated
2344
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user