@zentauri-ui/zentauri-components 1.8.1 → 1.8.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +21 -10
- package/cli/registry.json +10 -0
- package/dist/charts/area.js +9 -10
- package/dist/charts/area.js.map +1 -1
- package/dist/charts/area.mjs +2 -3
- package/dist/charts/area.mjs.map +1 -1
- package/dist/charts/bar.js +10 -95
- package/dist/charts/bar.js.map +1 -1
- package/dist/charts/bar.mjs +2 -95
- package/dist/charts/bar.mjs.map +1 -1
- package/dist/charts/bubble.js +8 -9
- package/dist/charts/bubble.js.map +1 -1
- package/dist/charts/bubble.mjs +2 -3
- package/dist/charts/bubble.mjs.map +1 -1
- package/dist/charts/funnel/Funnel.d.ts +6 -0
- package/dist/charts/funnel/Funnel.d.ts.map +1 -0
- package/dist/charts/funnel/index.d.ts +4 -0
- package/dist/charts/funnel/index.d.ts.map +1 -0
- package/dist/charts/funnel.js +102 -0
- package/dist/charts/funnel.js.map +1 -0
- package/dist/charts/funnel.mjs +89 -0
- package/dist/charts/funnel.mjs.map +1 -0
- package/dist/charts/line.js +8 -9
- package/dist/charts/line.js.map +1 -1
- package/dist/charts/line.mjs +2 -3
- package/dist/charts/line.mjs.map +1 -1
- package/dist/charts/pie/Pie.d.ts +1 -1
- package/dist/charts/pie/Pie.d.ts.map +1 -1
- package/dist/charts/pie.js +19 -6
- package/dist/charts/pie.js.map +1 -1
- package/dist/charts/pie.mjs +17 -4
- package/dist/charts/pie.mjs.map +1 -1
- package/dist/charts/radar/Radar.d.ts +6 -0
- package/dist/charts/radar/Radar.d.ts.map +1 -0
- package/dist/charts/radar/index.d.ts +4 -0
- package/dist/charts/radar/index.d.ts.map +1 -0
- package/dist/charts/radar.js +94 -0
- package/dist/charts/radar.js.map +1 -0
- package/dist/charts/radar.mjs +81 -0
- package/dist/charts/radar.mjs.map +1 -0
- package/dist/charts/scatter/Scatter.d.ts +6 -0
- package/dist/charts/scatter/Scatter.d.ts.map +1 -0
- package/dist/charts/scatter/index.d.ts +4 -0
- package/dist/charts/scatter/index.d.ts.map +1 -0
- package/dist/charts/scatter.js +116 -0
- package/dist/charts/scatter.js.map +1 -0
- package/dist/charts/scatter.mjs +103 -0
- package/dist/charts/scatter.mjs.map +1 -0
- package/dist/charts/shared/chart-frame.d.ts +2 -1
- package/dist/charts/shared/chart-frame.d.ts.map +1 -1
- package/dist/charts/shared/types.d.ts +22 -2
- package/dist/charts/shared/types.d.ts.map +1 -1
- package/dist/charts/stacked-bar/StackedBar.d.ts +6 -0
- package/dist/charts/stacked-bar/StackedBar.d.ts.map +1 -0
- package/dist/charts/stacked-bar/index.d.ts +4 -0
- package/dist/charts/stacked-bar/index.d.ts.map +1 -0
- package/dist/charts/stacked-bar.js +29 -0
- package/dist/charts/stacked-bar.js.map +1 -0
- package/dist/charts/stacked-bar.mjs +15 -0
- package/dist/charts/stacked-bar.mjs.map +1 -0
- package/dist/chunk-F3V4POW3.mjs +8 -0
- package/dist/chunk-F3V4POW3.mjs.map +1 -0
- package/dist/{chunk-G2WARVAM.mjs → chunk-HZIRD3SR.mjs} +35 -15
- package/dist/chunk-HZIRD3SR.mjs.map +1 -0
- package/dist/{chunk-G66SXATZ.js → chunk-IL4LH2XX.js} +50 -4
- package/dist/chunk-IL4LH2XX.js.map +1 -0
- package/dist/chunk-LREMK2XR.js +97 -0
- package/dist/chunk-LREMK2XR.js.map +1 -0
- package/dist/chunk-O2KM3ETC.mjs +95 -0
- package/dist/chunk-O2KM3ETC.mjs.map +1 -0
- package/dist/{chunk-ZIFMIS7D.mjs → chunk-OL3BJSRC.mjs} +51 -5
- package/dist/chunk-OL3BJSRC.mjs.map +1 -0
- package/dist/{chunk-QNUDODDX.js → chunk-PWPMKXEG.js} +36 -14
- package/dist/chunk-PWPMKXEG.js.map +1 -0
- package/dist/chunk-XRM7GOIE.js +10 -0
- package/dist/chunk-XRM7GOIE.js.map +1 -0
- package/dist/hooks/index.d.ts +2 -0
- package/dist/hooks/index.d.ts.map +1 -1
- package/dist/hooks/useIsomorphicLayoutEffect.js +6 -4
- package/dist/hooks/useIsomorphicLayoutEffect.js.map +1 -1
- package/dist/hooks/useIsomorphicLayoutEffect.mjs +1 -6
- package/dist/hooks/useIsomorphicLayoutEffect.mjs.map +1 -1
- package/dist/hooks/useTableFilter/index.d.ts +3 -0
- package/dist/hooks/useTableFilter/index.d.ts.map +1 -0
- package/dist/hooks/useTableFilter/types.d.ts +20 -0
- package/dist/hooks/useTableFilter/types.d.ts.map +1 -0
- package/dist/hooks/useTableFilter/useTableFilter.d.ts +3 -0
- package/dist/hooks/useTableFilter/useTableFilter.d.ts.map +1 -0
- package/dist/hooks/useTableFilter.js +124 -0
- package/dist/hooks/useTableFilter.js.map +1 -0
- package/dist/hooks/useTableFilter.mjs +122 -0
- package/dist/hooks/useTableFilter.mjs.map +1 -0
- package/dist/hooks/useTableSort/index.d.ts +3 -0
- package/dist/hooks/useTableSort/index.d.ts.map +1 -0
- package/dist/hooks/useTableSort/types.d.ts +15 -0
- package/dist/hooks/useTableSort/types.d.ts.map +1 -0
- package/dist/hooks/useTableSort/useTableSort.d.ts +3 -0
- package/dist/hooks/useTableSort/useTableSort.d.ts.map +1 -0
- package/dist/hooks/useTableSort.js +99 -0
- package/dist/hooks/useTableSort.js.map +1 -0
- package/dist/hooks/useTableSort.mjs +97 -0
- package/dist/hooks/useTableSort.mjs.map +1 -0
- package/dist/ui/marquee/marquee.d.ts.map +1 -1
- package/dist/ui/marquee.js +82 -21
- package/dist/ui/marquee.js.map +1 -1
- package/dist/ui/marquee.mjs +83 -22
- package/dist/ui/marquee.mjs.map +1 -1
- package/dist/ui/table/animated.js +8 -8
- package/dist/ui/table/animated.mjs +2 -2
- package/dist/ui/table/index.d.ts +1 -1
- package/dist/ui/table/index.d.ts.map +1 -1
- package/dist/ui/table/table-base.d.ts +2 -2
- package/dist/ui/table/table-base.d.ts.map +1 -1
- package/dist/ui/table/types.d.ts +9 -1
- package/dist/ui/table/types.d.ts.map +1 -1
- package/dist/ui/table.js +14 -14
- package/dist/ui/table.mjs +1 -1
- package/package.json +1 -1
- package/src/charts/charts.test.tsx +80 -0
- package/src/charts/funnel/Funnel.tsx +105 -0
- package/src/charts/funnel/index.ts +14 -0
- package/src/charts/pie/Pie.tsx +28 -1
- package/src/charts/radar/Radar.tsx +84 -0
- package/src/charts/radar/index.ts +16 -0
- package/src/charts/scatter/Scatter.tsx +104 -0
- package/src/charts/scatter/index.ts +16 -0
- package/src/charts/shared/chart-frame.tsx +4 -2
- package/src/charts/shared/types.ts +42 -2
- package/src/charts/stacked-bar/StackedBar.tsx +12 -0
- package/src/charts/stacked-bar/index.ts +16 -0
- package/src/hooks/index.ts +12 -0
- package/src/hooks/useTableFilter/index.ts +7 -0
- package/src/hooks/useTableFilter/types.ts +28 -0
- package/src/hooks/useTableFilter/useTableFilter.test.ts +141 -0
- package/src/hooks/useTableFilter/useTableFilter.ts +153 -0
- package/src/hooks/useTableSort/index.ts +5 -0
- package/src/hooks/useTableSort/types.ts +23 -0
- package/src/hooks/useTableSort/useTableSort.test.ts +150 -0
- package/src/hooks/useTableSort/useTableSort.ts +121 -0
- package/src/ui/divider/divider.test.tsx +55 -0
- package/src/ui/empty-state/empty-state.test.tsx +88 -0
- package/src/ui/marquee/marquee.test.tsx +45 -4
- package/src/ui/marquee/marquee.tsx +100 -18
- package/src/ui/skeleton/skeleton.test.tsx +85 -0
- package/src/ui/table/index.ts +3 -0
- package/src/ui/table/table-base.tsx +69 -4
- package/src/ui/table/table.test.tsx +207 -0
- package/src/ui/table/types.ts +13 -1
- package/dist/chunk-G2WARVAM.mjs.map +0 -1
- package/dist/chunk-G66SXATZ.js.map +0 -1
- package/dist/chunk-OULU7OC4.mjs +0 -21
- package/dist/chunk-OULU7OC4.mjs.map +0 -1
- package/dist/chunk-QNUDODDX.js.map +0 -1
- package/dist/chunk-Z6S36PDD.js +0 -24
- package/dist/chunk-Z6S36PDD.js.map +0 -1
- package/dist/chunk-ZIFMIS7D.mjs.map +0 -1
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
Legend,
|
|
5
|
+
PolarAngleAxis,
|
|
6
|
+
PolarGrid,
|
|
7
|
+
PolarRadiusAxis,
|
|
8
|
+
Radar,
|
|
9
|
+
RadarChart as RechartsRadarChart,
|
|
10
|
+
Tooltip,
|
|
11
|
+
} from "recharts";
|
|
12
|
+
|
|
13
|
+
import { ChartFrame } from "../shared/chart-frame";
|
|
14
|
+
import { getSeriesFill, resolveColor } from "../shared/colors";
|
|
15
|
+
import type { RadarChartProps } from "../shared/types";
|
|
16
|
+
|
|
17
|
+
export function RadarChart<
|
|
18
|
+
TDatum extends Record<string, number | string | null | undefined>,
|
|
19
|
+
>({
|
|
20
|
+
appearance,
|
|
21
|
+
className,
|
|
22
|
+
containerStyle,
|
|
23
|
+
data,
|
|
24
|
+
density,
|
|
25
|
+
emptyState = null,
|
|
26
|
+
height = 320,
|
|
27
|
+
series,
|
|
28
|
+
showGrid = true,
|
|
29
|
+
showLegend = false,
|
|
30
|
+
showTooltip = true,
|
|
31
|
+
tooltipColor = "#0f172a",
|
|
32
|
+
style,
|
|
33
|
+
xKey,
|
|
34
|
+
...props
|
|
35
|
+
}: RadarChartProps<TDatum>) {
|
|
36
|
+
const hasData = data.length > 0 && series.length > 0;
|
|
37
|
+
|
|
38
|
+
return (
|
|
39
|
+
<ChartFrame
|
|
40
|
+
appearance={appearance}
|
|
41
|
+
className={className}
|
|
42
|
+
containerStyle={containerStyle}
|
|
43
|
+
density={density}
|
|
44
|
+
emptyState={emptyState}
|
|
45
|
+
hasData={hasData}
|
|
46
|
+
height={height}
|
|
47
|
+
style={style}
|
|
48
|
+
{...props}
|
|
49
|
+
>
|
|
50
|
+
<RechartsRadarChart data={data}>
|
|
51
|
+
{showGrid ? <PolarGrid stroke="currentColor" opacity={0.18} /> : null}
|
|
52
|
+
<PolarAngleAxis
|
|
53
|
+
dataKey={String(xKey)}
|
|
54
|
+
tick={{ fill: "currentColor", fontSize: 12 }}
|
|
55
|
+
/>
|
|
56
|
+
<PolarRadiusAxis tick={{ fill: "currentColor", fontSize: 11 }} />
|
|
57
|
+
{showTooltip ? (
|
|
58
|
+
<Tooltip
|
|
59
|
+
contentStyle={{ color: tooltipColor }}
|
|
60
|
+
labelStyle={{ color: tooltipColor }}
|
|
61
|
+
itemStyle={{ color: tooltipColor }}
|
|
62
|
+
/>
|
|
63
|
+
) : null}
|
|
64
|
+
{showLegend ? <Legend /> : null}
|
|
65
|
+
{series.map((item, index) => {
|
|
66
|
+
const color = resolveColor(item.color, index);
|
|
67
|
+
const fill = getSeriesFill(item, index, 0.24);
|
|
68
|
+
return (
|
|
69
|
+
<Radar
|
|
70
|
+
key={item.dataKey}
|
|
71
|
+
dataKey={item.dataKey}
|
|
72
|
+
name={item.name}
|
|
73
|
+
stroke={item.stroke ?? color.stroke}
|
|
74
|
+
fill={fill ?? color.fill}
|
|
75
|
+
fillOpacity={0.72}
|
|
76
|
+
/>
|
|
77
|
+
);
|
|
78
|
+
})}
|
|
79
|
+
</RechartsRadarChart>
|
|
80
|
+
</ChartFrame>
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
RadarChart.displayName = "RadarChart";
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
export { RadarChart } from "./Radar";
|
|
4
|
+
export type {
|
|
5
|
+
ChartColor,
|
|
6
|
+
ChartDatum,
|
|
7
|
+
ChartMargin,
|
|
8
|
+
ChartSeries,
|
|
9
|
+
ChartSharedStatic,
|
|
10
|
+
RadarChartProps,
|
|
11
|
+
} from "../shared/types";
|
|
12
|
+
export {
|
|
13
|
+
chartColorValues,
|
|
14
|
+
chartPalette,
|
|
15
|
+
chartVariants,
|
|
16
|
+
} from "../shared/variants";
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
Scatter,
|
|
5
|
+
ScatterChart as RechartsScatterChart,
|
|
6
|
+
XAxis,
|
|
7
|
+
YAxis,
|
|
8
|
+
} from "recharts";
|
|
9
|
+
|
|
10
|
+
import {
|
|
11
|
+
ChartDecorators,
|
|
12
|
+
ChartFrame,
|
|
13
|
+
defaultChartMargin,
|
|
14
|
+
} from "../shared/chart-frame";
|
|
15
|
+
import { resolveColor } from "../shared/colors";
|
|
16
|
+
import type { ScatterChartProps } from "../shared/types";
|
|
17
|
+
|
|
18
|
+
export function ScatterChart<
|
|
19
|
+
TDatum extends Record<string, number | string | null | undefined>,
|
|
20
|
+
>({
|
|
21
|
+
appearance,
|
|
22
|
+
className,
|
|
23
|
+
containerStyle,
|
|
24
|
+
data,
|
|
25
|
+
density,
|
|
26
|
+
emptyState = null,
|
|
27
|
+
height = 320,
|
|
28
|
+
margin = defaultChartMargin,
|
|
29
|
+
series,
|
|
30
|
+
showGrid = true,
|
|
31
|
+
showLegend = false,
|
|
32
|
+
showTooltip = true,
|
|
33
|
+
tooltipColor = "#0f172a",
|
|
34
|
+
style,
|
|
35
|
+
syncId,
|
|
36
|
+
xKey,
|
|
37
|
+
...props
|
|
38
|
+
}: ScatterChartProps<TDatum>) {
|
|
39
|
+
const hasData = data.length > 0 && series.length > 0;
|
|
40
|
+
const xAxisKey = String(xKey);
|
|
41
|
+
const isNumericX = typeof data[0]?.[xAxisKey] === "number";
|
|
42
|
+
|
|
43
|
+
return (
|
|
44
|
+
<ChartFrame
|
|
45
|
+
appearance={appearance}
|
|
46
|
+
className={className}
|
|
47
|
+
containerStyle={containerStyle}
|
|
48
|
+
density={density}
|
|
49
|
+
emptyState={emptyState}
|
|
50
|
+
hasData={hasData}
|
|
51
|
+
height={height}
|
|
52
|
+
style={style}
|
|
53
|
+
{...props}
|
|
54
|
+
>
|
|
55
|
+
<RechartsScatterChart data={data} margin={margin} syncId={syncId}>
|
|
56
|
+
<ChartDecorators
|
|
57
|
+
axis={
|
|
58
|
+
<>
|
|
59
|
+
<XAxis
|
|
60
|
+
dataKey={xAxisKey}
|
|
61
|
+
type={isNumericX ? "number" : "category"}
|
|
62
|
+
minTickGap={24}
|
|
63
|
+
tickLine={false}
|
|
64
|
+
tickMargin={10}
|
|
65
|
+
axisLine={false}
|
|
66
|
+
fontSize={12}
|
|
67
|
+
/>
|
|
68
|
+
<YAxis
|
|
69
|
+
dataKey="__chartY"
|
|
70
|
+
type="number"
|
|
71
|
+
width={40}
|
|
72
|
+
tickLine={false}
|
|
73
|
+
tickMargin={8}
|
|
74
|
+
axisLine={false}
|
|
75
|
+
fontSize={12}
|
|
76
|
+
/>
|
|
77
|
+
</>
|
|
78
|
+
}
|
|
79
|
+
showGrid={showGrid}
|
|
80
|
+
showLegend={showLegend}
|
|
81
|
+
showTooltip={showTooltip}
|
|
82
|
+
tooltipColor={tooltipColor}
|
|
83
|
+
/>
|
|
84
|
+
{series.map((item, index) => {
|
|
85
|
+
const color = resolveColor(item.color, index);
|
|
86
|
+
return (
|
|
87
|
+
<Scatter
|
|
88
|
+
key={item.dataKey}
|
|
89
|
+
data={data.map((entry) => ({
|
|
90
|
+
...entry,
|
|
91
|
+
__chartY: entry[item.dataKey],
|
|
92
|
+
}))}
|
|
93
|
+
name={item.name}
|
|
94
|
+
fill={item.fill ?? color.stroke}
|
|
95
|
+
line={item.stroke ? { stroke: item.stroke } : false}
|
|
96
|
+
/>
|
|
97
|
+
);
|
|
98
|
+
})}
|
|
99
|
+
</RechartsScatterChart>
|
|
100
|
+
</ChartFrame>
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
ScatterChart.displayName = "ScatterChart";
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
export { ScatterChart } from "./Scatter";
|
|
4
|
+
export type {
|
|
5
|
+
ChartColor,
|
|
6
|
+
ChartDatum,
|
|
7
|
+
ChartMargin,
|
|
8
|
+
ChartSeries,
|
|
9
|
+
ChartSharedStatic,
|
|
10
|
+
ScatterChartProps,
|
|
11
|
+
} from "../shared/types";
|
|
12
|
+
export {
|
|
13
|
+
chartColorValues,
|
|
14
|
+
chartPalette,
|
|
15
|
+
chartVariants,
|
|
16
|
+
} from "../shared/variants";
|
|
@@ -39,6 +39,7 @@ type ChartFrameProps = HTMLAttributes<HTMLDivElement> & {
|
|
|
39
39
|
emptyState?: ReactNode;
|
|
40
40
|
hasData: boolean;
|
|
41
41
|
height: number;
|
|
42
|
+
overlay?: ReactNode;
|
|
42
43
|
style?: CSSProperties;
|
|
43
44
|
children: ReactNode;
|
|
44
45
|
};
|
|
@@ -54,6 +55,7 @@ export function ChartFrame({
|
|
|
54
55
|
emptyState = null,
|
|
55
56
|
hasData,
|
|
56
57
|
height,
|
|
58
|
+
overlay,
|
|
57
59
|
style,
|
|
58
60
|
...props
|
|
59
61
|
}: ChartFrameProps) {
|
|
@@ -63,8 +65,7 @@ export function ChartFrame({
|
|
|
63
65
|
"--chart-height": `${height}px`,
|
|
64
66
|
...style,
|
|
65
67
|
} as CSSProperties;
|
|
66
|
-
const canRenderChart =
|
|
67
|
-
(size?.width ?? 0) > 0 && (size?.height ?? 0) > 0;
|
|
68
|
+
const canRenderChart = (size?.width ?? 0) > 0 && (size?.height ?? 0) > 0;
|
|
68
69
|
|
|
69
70
|
if (!hasData) {
|
|
70
71
|
return (
|
|
@@ -99,6 +100,7 @@ export function ChartFrame({
|
|
|
99
100
|
</ResponsiveContainer>
|
|
100
101
|
) : null}
|
|
101
102
|
</div>
|
|
103
|
+
{overlay}
|
|
102
104
|
</div>
|
|
103
105
|
);
|
|
104
106
|
}
|
|
@@ -2,9 +2,18 @@ import type { VariantProps } from "class-variance-authority";
|
|
|
2
2
|
import type { CSSProperties, HTMLAttributes, ReactNode } from "react";
|
|
3
3
|
|
|
4
4
|
import type { chartPalette, chartVariants } from "./variants";
|
|
5
|
-
import { PieProps } from "recharts";
|
|
5
|
+
import type { PieProps } from "recharts";
|
|
6
6
|
|
|
7
|
-
export type ChartType =
|
|
7
|
+
export type ChartType =
|
|
8
|
+
| "area"
|
|
9
|
+
| "bar"
|
|
10
|
+
| "bubble"
|
|
11
|
+
| "funnel"
|
|
12
|
+
| "line"
|
|
13
|
+
| "pie"
|
|
14
|
+
| "radar"
|
|
15
|
+
| "scatter"
|
|
16
|
+
| "stacked-bar";
|
|
8
17
|
|
|
9
18
|
export type ChartColor = keyof typeof chartPalette;
|
|
10
19
|
|
|
@@ -61,6 +70,17 @@ export type LineChartProps<TDatum extends ChartDatum = ChartDatum> =
|
|
|
61
70
|
export type BubbleChartProps<TDatum extends ChartDatum = ChartDatum> =
|
|
62
71
|
BaseChartProps<TDatum>;
|
|
63
72
|
|
|
73
|
+
export type RadarChartProps<TDatum extends ChartDatum = ChartDatum> =
|
|
74
|
+
BaseChartProps<TDatum>;
|
|
75
|
+
|
|
76
|
+
export type ScatterChartProps<TDatum extends ChartDatum = ChartDatum> =
|
|
77
|
+
BaseChartProps<TDatum>;
|
|
78
|
+
|
|
79
|
+
export type StackedBarChartProps<TDatum extends ChartDatum = ChartDatum> = Omit<
|
|
80
|
+
BaseChartProps<TDatum>,
|
|
81
|
+
"stacked"
|
|
82
|
+
>;
|
|
83
|
+
|
|
64
84
|
export type PieChartProps<TDatum extends ChartDatum = ChartDatum> =
|
|
65
85
|
ChartSharedStatic &
|
|
66
86
|
Omit<HTMLAttributes<HTMLDivElement>, "children"> & {
|
|
@@ -75,6 +95,8 @@ export type PieChartProps<TDatum extends ChartDatum = ChartDatum> =
|
|
|
75
95
|
containerStyle?: CSSProperties;
|
|
76
96
|
paddingAngle?: PieProps["paddingAngle"];
|
|
77
97
|
cornerRadius?: PieProps["cornerRadius"];
|
|
98
|
+
center?: ReactNode;
|
|
99
|
+
colorKey?: keyof TDatum & string;
|
|
78
100
|
label?: boolean;
|
|
79
101
|
labelLine?: boolean;
|
|
80
102
|
labelColor?: string;
|
|
@@ -84,3 +106,21 @@ export type PieChartProps<TDatum extends ChartDatum = ChartDatum> =
|
|
|
84
106
|
outerRadius?: PieProps["outerRadius"];
|
|
85
107
|
shape?: PieProps["shape"];
|
|
86
108
|
};
|
|
109
|
+
|
|
110
|
+
export type FunnelChartProps<TDatum extends ChartDatum = ChartDatum> =
|
|
111
|
+
ChartSharedStatic &
|
|
112
|
+
Omit<HTMLAttributes<HTMLDivElement>, "children"> & {
|
|
113
|
+
data: TDatum[];
|
|
114
|
+
dataKey: keyof TDatum & string;
|
|
115
|
+
nameKey: keyof TDatum & string;
|
|
116
|
+
height?: number;
|
|
117
|
+
showLegend?: boolean;
|
|
118
|
+
showTooltip?: boolean;
|
|
119
|
+
tooltipColor?: string;
|
|
120
|
+
emptyState?: ReactNode;
|
|
121
|
+
containerStyle?: CSSProperties;
|
|
122
|
+
colorKey?: keyof TDatum & string;
|
|
123
|
+
label?: boolean;
|
|
124
|
+
stroke?: string;
|
|
125
|
+
fill?: string;
|
|
126
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { BarChart } from "../bar/Bar";
|
|
4
|
+
import type { StackedBarChartProps } from "../shared/types";
|
|
5
|
+
|
|
6
|
+
export function StackedBarChart<
|
|
7
|
+
TDatum extends Record<string, number | string | null | undefined>,
|
|
8
|
+
>(props: StackedBarChartProps<TDatum>) {
|
|
9
|
+
return <BarChart {...props} stacked />;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
StackedBarChart.displayName = "StackedBarChart";
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
export { StackedBarChart } from "./StackedBar";
|
|
4
|
+
export type {
|
|
5
|
+
ChartColor,
|
|
6
|
+
ChartDatum,
|
|
7
|
+
ChartMargin,
|
|
8
|
+
ChartSeries,
|
|
9
|
+
ChartSharedStatic,
|
|
10
|
+
StackedBarChartProps,
|
|
11
|
+
} from "../shared/types";
|
|
12
|
+
export {
|
|
13
|
+
chartColorValues,
|
|
14
|
+
chartPalette,
|
|
15
|
+
chartVariants,
|
|
16
|
+
} from "../shared/variants";
|
package/src/hooks/index.ts
CHANGED
|
@@ -49,6 +49,18 @@ export {
|
|
|
49
49
|
useSessionStorage,
|
|
50
50
|
type UseSessionStorageResult,
|
|
51
51
|
} from "./useSessionStorage";
|
|
52
|
+
export {
|
|
53
|
+
useTableFilter,
|
|
54
|
+
type TableFilterPredicate,
|
|
55
|
+
type TableFilterState,
|
|
56
|
+
type UseTableFilterParams,
|
|
57
|
+
type UseTableFilterResult,
|
|
58
|
+
} from "./useTableFilter";
|
|
59
|
+
export {
|
|
60
|
+
useTableSort,
|
|
61
|
+
type UseTableSortParams,
|
|
62
|
+
type UseTableSortResult,
|
|
63
|
+
} from "./useTableSort";
|
|
52
64
|
export { useThrottledCallback } from "./useThrottledCallback";
|
|
53
65
|
export { useToggle } from "./useToggle";
|
|
54
66
|
export { useWindowSize, type WindowSize } from "./useWindowSize";
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export type TableFilterState<TKey extends string = string> = Partial<
|
|
2
|
+
Record<TKey, string>
|
|
3
|
+
>;
|
|
4
|
+
|
|
5
|
+
export type TableFilterPredicate<TData, TKey extends string = string> = (
|
|
6
|
+
row: TData,
|
|
7
|
+
filterValue: string,
|
|
8
|
+
filterKey: TKey,
|
|
9
|
+
) => boolean;
|
|
10
|
+
|
|
11
|
+
export type UseTableFilterParams<TData, TKey extends string = string> = {
|
|
12
|
+
data: readonly TData[];
|
|
13
|
+
filters?: TableFilterState<TKey>;
|
|
14
|
+
defaultFilters?: TableFilterState<TKey>;
|
|
15
|
+
onFiltersChange?: (filters: TableFilterState<TKey>) => void;
|
|
16
|
+
getColumnValue?: (row: TData, filterKey: TKey) => unknown;
|
|
17
|
+
filterPredicate?: TableFilterPredicate<TData, TKey>;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export type UseTableFilterResult<TData, TKey extends string = string> = {
|
|
21
|
+
filters: TableFilterState<TKey>;
|
|
22
|
+
filteredData: TData[];
|
|
23
|
+
hasActiveFilters: boolean;
|
|
24
|
+
setFilter: (filterKey: TKey, value: string) => void;
|
|
25
|
+
setFilters: (filters: TableFilterState<TKey>) => void;
|
|
26
|
+
clearFilter: (filterKey: TKey) => void;
|
|
27
|
+
clearFilters: () => void;
|
|
28
|
+
};
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import { act, renderHook } from "@testing-library/react";
|
|
2
|
+
import { describe, expect, it, vi } from "vitest";
|
|
3
|
+
|
|
4
|
+
import { useTableFilter } from "./useTableFilter";
|
|
5
|
+
|
|
6
|
+
const rows = [
|
|
7
|
+
{ name: "Atlas", status: "active", seats: 12 },
|
|
8
|
+
{ name: "Beacon", status: "paused", seats: 4 },
|
|
9
|
+
{ name: "Comet", status: "active", seats: 8 },
|
|
10
|
+
] as const;
|
|
11
|
+
|
|
12
|
+
describe("useTableFilter", () => {
|
|
13
|
+
it("should return all rows when no filters are active", () => {
|
|
14
|
+
const { result } = renderHook(() => useTableFilter({ data: rows }));
|
|
15
|
+
expect(result.current.filteredData).toEqual(rows);
|
|
16
|
+
expect(result.current.hasActiveFilters).toBe(false);
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it("should filter rows by a string column value", () => {
|
|
20
|
+
const { result } = renderHook(() =>
|
|
21
|
+
useTableFilter({
|
|
22
|
+
data: rows,
|
|
23
|
+
defaultFilters: { status: "active" },
|
|
24
|
+
}),
|
|
25
|
+
);
|
|
26
|
+
expect(result.current.filteredData.map((row) => row.name)).toEqual([
|
|
27
|
+
"Atlas",
|
|
28
|
+
"Comet",
|
|
29
|
+
]);
|
|
30
|
+
expect(result.current.hasActiveFilters).toBe(true);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it("should combine multiple column filters", () => {
|
|
34
|
+
const { result } = renderHook(() =>
|
|
35
|
+
useTableFilter({
|
|
36
|
+
data: rows,
|
|
37
|
+
defaultFilters: { status: "active", name: "com" },
|
|
38
|
+
}),
|
|
39
|
+
);
|
|
40
|
+
expect(result.current.filteredData).toEqual([rows[2]]);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it("should update and clear filters in uncontrolled mode", () => {
|
|
44
|
+
const { result } = renderHook(() => useTableFilter({ data: rows }));
|
|
45
|
+
|
|
46
|
+
act(() => {
|
|
47
|
+
result.current.setFilter("name", "bea");
|
|
48
|
+
});
|
|
49
|
+
expect(result.current.filteredData).toEqual([rows[1]]);
|
|
50
|
+
|
|
51
|
+
act(() => {
|
|
52
|
+
result.current.clearFilter("name");
|
|
53
|
+
});
|
|
54
|
+
expect(result.current.filteredData).toEqual(rows);
|
|
55
|
+
|
|
56
|
+
act(() => {
|
|
57
|
+
result.current.setFilters({ status: "paused" });
|
|
58
|
+
});
|
|
59
|
+
expect(result.current.filteredData).toEqual([rows[1]]);
|
|
60
|
+
|
|
61
|
+
act(() => {
|
|
62
|
+
result.current.clearFilters();
|
|
63
|
+
});
|
|
64
|
+
expect(result.current.filteredData).toEqual(rows);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it("should preserve batched uncontrolled filter updates", () => {
|
|
68
|
+
const { result } = renderHook(() => useTableFilter({ data: rows }));
|
|
69
|
+
|
|
70
|
+
act(() => {
|
|
71
|
+
result.current.setFilter("status", "active");
|
|
72
|
+
result.current.setFilter("name", "atlas");
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
expect(result.current.filters).toEqual({
|
|
76
|
+
status: "active",
|
|
77
|
+
name: "atlas",
|
|
78
|
+
});
|
|
79
|
+
expect(result.current.filteredData).toEqual([rows[0]]);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it("should support controlled filters", () => {
|
|
83
|
+
const handleFiltersChange = vi.fn();
|
|
84
|
+
const { result, rerender } = renderHook(
|
|
85
|
+
({ filters }: { filters: Record<string, string> }) =>
|
|
86
|
+
useTableFilter({
|
|
87
|
+
data: rows,
|
|
88
|
+
filters,
|
|
89
|
+
onFiltersChange: handleFiltersChange,
|
|
90
|
+
}),
|
|
91
|
+
{ initialProps: { filters: { status: "active" } } },
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
expect(result.current.filteredData.length).toBe(2);
|
|
95
|
+
act(() => {
|
|
96
|
+
result.current.setFilter("name", "atlas");
|
|
97
|
+
});
|
|
98
|
+
expect(handleFiltersChange).toHaveBeenCalledWith({
|
|
99
|
+
status: "active",
|
|
100
|
+
name: "atlas",
|
|
101
|
+
});
|
|
102
|
+
expect(result.current.filters).toEqual({ status: "active" });
|
|
103
|
+
|
|
104
|
+
rerender({ filters: { status: "active", name: "atlas" } });
|
|
105
|
+
expect(result.current.filteredData).toEqual([rows[0]]);
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
it("should support custom value accessors and predicates", () => {
|
|
109
|
+
const { result } = renderHook(() =>
|
|
110
|
+
useTableFilter({
|
|
111
|
+
data: rows,
|
|
112
|
+
defaultFilters: { seats: "10" },
|
|
113
|
+
getColumnValue: (row, key) => row[key],
|
|
114
|
+
filterPredicate: (row, value, key) => Number(row[key]) >= Number(value),
|
|
115
|
+
}),
|
|
116
|
+
);
|
|
117
|
+
expect(result.current.filteredData).toEqual([rows[0]]);
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
it("should remove blank filter values", () => {
|
|
121
|
+
const { result } = renderHook(() =>
|
|
122
|
+
useTableFilter({
|
|
123
|
+
data: rows,
|
|
124
|
+
defaultFilters: { name: " " },
|
|
125
|
+
}),
|
|
126
|
+
);
|
|
127
|
+
expect(result.current.filters).toEqual({});
|
|
128
|
+
expect(result.current.hasActiveFilters).toBe(false);
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
it("should guard against null filters at runtime", () => {
|
|
132
|
+
const { result } = renderHook(() =>
|
|
133
|
+
useTableFilter({
|
|
134
|
+
data: rows,
|
|
135
|
+
filters: null as unknown as Record<string, string>,
|
|
136
|
+
}),
|
|
137
|
+
);
|
|
138
|
+
expect(result.current.filters).toEqual({});
|
|
139
|
+
expect(result.current.filteredData).toEqual(rows);
|
|
140
|
+
});
|
|
141
|
+
});
|