@codefast/ui 0.3.16-canary.1 → 0.3.16-canary.3
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/CHANGELOG.md +30 -0
- package/README.md +1 -1
- package/dist/components/accordion.mjs +2 -2
- package/dist/components/alert-dialog.d.mts +1 -1
- package/dist/components/alert-dialog.mjs +4 -4
- package/dist/components/alert.d.mts +3 -13
- package/dist/components/alert.mjs +3 -23
- package/dist/components/badge.d.mts +3 -15
- package/dist/components/badge.mjs +2 -44
- package/dist/components/breadcrumb.mjs +1 -1
- package/dist/components/button-group.d.mts +3 -13
- package/dist/components/button-group.mjs +3 -24
- package/dist/components/button.d.mts +3 -25
- package/dist/components/button.mjs +2 -72
- package/dist/components/calendar.mjs +2 -1
- package/dist/components/carousel.d.mts +1 -2
- package/dist/components/chart.d.mts +2 -4
- package/dist/components/checkbox.mjs +2 -2
- package/dist/components/context-menu.mjs +2 -2
- package/dist/components/dialog.d.mts +1 -1
- package/dist/components/dialog.mjs +4 -4
- package/dist/components/drawer.d.mts +1 -1
- package/dist/components/drawer.mjs +2 -2
- package/dist/components/dropdown-menu.mjs +2 -2
- package/dist/components/empty.d.mts +3 -13
- package/dist/components/empty.mjs +3 -18
- package/dist/components/field.d.mts +3 -14
- package/dist/components/field.mjs +3 -32
- package/dist/components/form.d.mts +2 -4
- package/dist/components/hover-card.mjs +1 -1
- package/dist/components/input-group.d.mts +4 -31
- package/dist/components/input-group.mjs +3 -90
- package/dist/components/input-number.mjs +4 -4
- package/dist/components/input-otp.mjs +2 -2
- package/dist/components/input.mjs +1 -1
- package/dist/components/item.d.mts +4 -29
- package/dist/components/item.mjs +3 -56
- package/dist/components/menubar.mjs +2 -2
- package/dist/components/native-select.mjs +1 -1
- package/dist/components/navigation-menu.d.mts +1 -6
- package/dist/components/navigation-menu.mjs +8 -15
- package/dist/components/pagination.d.mts +1 -1
- package/dist/components/pagination.mjs +1 -1
- package/dist/components/popover.mjs +1 -1
- package/dist/components/progress-circle.d.mts +3 -47
- package/dist/components/progress-circle.mjs +2 -47
- package/dist/components/progress.mjs +1 -1
- package/dist/components/radio-group.mjs +1 -1
- package/dist/components/radio.mjs +1 -1
- package/dist/components/scroll-area.d.mts +3 -19
- package/dist/components/scroll-area.mjs +4 -61
- package/dist/components/select.d.mts +1 -1
- package/dist/components/select.mjs +3 -3
- package/dist/components/separator.d.mts +3 -18
- package/dist/components/separator.mjs +3 -23
- package/dist/components/sheet.d.mts +6 -18
- package/dist/components/sheet.mjs +6 -49
- package/dist/components/sidebar.d.mts +4 -19
- package/dist/components/sidebar.mjs +10 -46
- package/dist/components/skeleton.mjs +1 -1
- package/dist/components/slider.mjs +1 -1
- package/dist/components/spinner.mjs +1 -1
- package/dist/components/switch.mjs +2 -2
- package/dist/components/table.mjs +1 -1
- package/dist/components/tabs.mjs +1 -1
- package/dist/components/textarea.mjs +1 -1
- package/dist/components/toggle-group.d.mts +3 -2
- package/dist/components/toggle-group.mjs +1 -1
- package/dist/components/toggle.d.mts +2 -21
- package/dist/components/toggle.mjs +2 -39
- package/dist/components/tooltip.mjs +1 -1
- package/dist/index.d.mts +31 -16
- package/dist/index.mjs +30 -15
- package/dist/lib/utils.d.mts +1 -12
- package/dist/lib/utils.mjs +1 -9
- package/dist/primitives/checkbox-group.d.mts +1 -2
- package/dist/primitives/input-number.d.mts +1 -2
- package/dist/primitives/input.d.mts +1 -2
- package/dist/primitives/progress-circle.d.mts +1 -2
- package/dist/variants/alert.d.mts +18 -0
- package/dist/variants/alert.mjs +25 -0
- package/dist/variants/badge.d.mts +20 -0
- package/dist/variants/badge.mjs +46 -0
- package/dist/variants/button-group.d.mts +18 -0
- package/dist/variants/button-group.mjs +26 -0
- package/dist/variants/button.d.mts +30 -0
- package/dist/variants/button.mjs +76 -0
- package/dist/variants/empty.d.mts +18 -0
- package/dist/variants/empty.mjs +20 -0
- package/dist/variants/field.d.mts +19 -0
- package/dist/variants/field.mjs +34 -0
- package/dist/variants/input-group.d.mts +43 -0
- package/dist/variants/input-group.mjs +93 -0
- package/dist/variants/item.d.mts +37 -0
- package/dist/variants/item.mjs +60 -0
- package/dist/variants/navigation-menu.d.mts +13 -0
- package/dist/variants/navigation-menu.mjs +12 -0
- package/dist/variants/progress-circle.d.mts +52 -0
- package/dist/variants/progress-circle.mjs +49 -0
- package/dist/variants/scroll-area.d.mts +24 -0
- package/dist/variants/scroll-area.mjs +63 -0
- package/dist/variants/separator.d.mts +23 -0
- package/dist/variants/separator.mjs +25 -0
- package/dist/variants/sheet.d.mts +20 -0
- package/dist/variants/sheet.mjs +50 -0
- package/dist/variants/sidebar.d.mts +23 -0
- package/dist/variants/sidebar.mjs +42 -0
- package/dist/variants/toggle.d.mts +23 -0
- package/dist/variants/toggle.mjs +43 -0
- package/package.json +169 -21
- package/src/components/accordion.tsx +156 -0
- package/src/components/alert-dialog.tsx +314 -0
- package/src/components/alert.tsx +86 -0
- package/src/components/aspect-ratio.tsx +28 -0
- package/src/components/avatar.tsx +84 -0
- package/src/components/badge.tsx +38 -0
- package/src/components/breadcrumb.tsx +197 -0
- package/src/components/button-group.tsx +107 -0
- package/src/components/button.tsx +66 -0
- package/src/components/calendar.tsx +277 -0
- package/src/components/card.tsx +175 -0
- package/src/components/carousel.tsx +367 -0
- package/src/components/chart.tsx +587 -0
- package/src/components/checkbox-cards.tsx +92 -0
- package/src/components/checkbox-group.tsx +83 -0
- package/src/components/checkbox.tsx +65 -0
- package/src/components/collapsible.tsx +60 -0
- package/src/components/command.tsx +311 -0
- package/src/components/context-menu.tsx +489 -0
- package/src/components/dialog.tsx +295 -0
- package/src/components/drawer.tsx +271 -0
- package/src/components/dropdown-menu.tsx +498 -0
- package/src/components/empty.tsx +169 -0
- package/src/components/field.tsx +362 -0
- package/src/components/form.tsx +300 -0
- package/src/components/hover-card.tsx +116 -0
- package/src/components/input-group.tsx +224 -0
- package/src/components/input-number.tsx +161 -0
- package/src/components/input-otp.tsx +151 -0
- package/src/components/input-password.tsx +74 -0
- package/src/components/input-search.tsx +98 -0
- package/src/components/input.tsx +52 -0
- package/src/components/item.tsx +280 -0
- package/src/components/kbd.tsx +59 -0
- package/src/components/label.tsx +44 -0
- package/src/components/menubar.tsx +531 -0
- package/src/components/native-select.tsx +96 -0
- package/src/components/navigation-menu.tsx +295 -0
- package/src/components/pagination.tsx +204 -0
- package/src/components/popover.tsx +139 -0
- package/src/components/progress-circle.tsx +203 -0
- package/src/components/progress.tsx +54 -0
- package/src/components/radio-cards.tsx +85 -0
- package/src/components/radio-group.tsx +79 -0
- package/src/components/radio.tsx +61 -0
- package/src/components/resizable.tsx +99 -0
- package/src/components/scroll-area.tsx +115 -0
- package/src/components/select.tsx +319 -0
- package/src/components/separator.tsx +74 -0
- package/src/components/sheet.tsx +278 -0
- package/src/components/sidebar.tsx +1056 -0
- package/src/components/skeleton.tsx +37 -0
- package/src/components/slider.tsx +95 -0
- package/src/components/sonner.tsx +47 -0
- package/src/components/spinner.tsx +75 -0
- package/src/components/switch.tsx +66 -0
- package/src/components/table.tsx +200 -0
- package/src/components/tabs.tsx +128 -0
- package/src/components/textarea.tsx +49 -0
- package/src/components/toggle-group.tsx +141 -0
- package/src/components/toggle.tsx +39 -0
- package/src/components/tooltip.tsx +141 -0
- package/src/css/amber.css +59 -22
- package/src/css/blue.css +59 -22
- package/src/css/cyan.css +59 -22
- package/src/css/emerald.css +59 -22
- package/src/css/fuchsia.css +59 -22
- package/src/css/gray.css +59 -22
- package/src/css/green.css +59 -22
- package/src/css/indigo.css +59 -22
- package/src/css/lime.css +59 -22
- package/src/css/neutral.css +59 -22
- package/src/css/orange.css +59 -22
- package/src/css/pink.css +59 -22
- package/src/css/preset.css +32 -13
- package/src/css/purple.css +59 -22
- package/src/css/red.css +59 -22
- package/src/css/rose.css +59 -22
- package/src/css/sky.css +59 -22
- package/src/css/slate.css +59 -22
- package/src/css/stone.css +59 -22
- package/src/css/teal.css +59 -22
- package/src/css/violet.css +59 -22
- package/src/css/yellow.css +59 -22
- package/src/css/zinc.css +59 -22
- package/src/hooks/use-animated-value.ts +97 -0
- package/src/hooks/use-copy-to-clipboard.ts +63 -0
- package/src/hooks/use-is-mobile.ts +27 -0
- package/src/hooks/use-media-query.ts +71 -0
- package/src/hooks/use-mutation-observer.ts +54 -0
- package/src/hooks/use-pagination.ts +166 -0
- package/src/index.ts +720 -0
- package/src/lib/utils.ts +5 -0
- package/src/primitives/checkbox-group.tsx +360 -0
- package/src/primitives/input-number.tsx +1013 -0
- package/src/primitives/input.tsx +243 -0
- package/src/primitives/progress-circle.tsx +537 -0
- package/src/variants/alert.ts +45 -0
- package/src/variants/badge.ts +66 -0
- package/src/variants/button-group.ts +49 -0
- package/src/variants/button.ts +93 -0
- package/src/variants/empty.ts +43 -0
- package/src/variants/field.ts +50 -0
- package/src/variants/input-group.ts +132 -0
- package/src/variants/item.ts +90 -0
- package/src/variants/navigation-menu.ts +32 -0
- package/src/variants/progress-circle.ts +47 -0
- package/src/variants/scroll-area.ts +79 -0
- package/src/variants/separator.ts +41 -0
- package/src/variants/sheet.ts +70 -0
- package/src/variants/sidebar.ts +61 -0
- package/src/variants/toggle.ts +59 -0
- package/dist/node_modules/.pnpm/clsx@2.1.1/node_modules/clsx/clsx.d.mts +0 -6
|
@@ -0,0 +1,587 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import type { Scope } from "@radix-ui/react-context";
|
|
4
|
+
import type { ComponentProps, ComponentType, CSSProperties, JSX, ReactNode } from "react";
|
|
5
|
+
import type { NameType, Payload, ValueType } from "recharts/types/component/DefaultTooltipContent";
|
|
6
|
+
import type { TooltipContentProps, TooltipProps } from "recharts";
|
|
7
|
+
|
|
8
|
+
import { cn } from "#/lib/utils";
|
|
9
|
+
import { createContextScope } from "@radix-ui/react-context";
|
|
10
|
+
import { useId, useMemo } from "react";
|
|
11
|
+
import * as RechartsPrimitive from "recharts";
|
|
12
|
+
|
|
13
|
+
/* -----------------------------------------------------------------------------
|
|
14
|
+
* Type Definitions and Utilities
|
|
15
|
+
* -------------------------------------------------------------------------- */
|
|
16
|
+
|
|
17
|
+
type ExtractProps<T> = T extends (props: infer P) => ReactNode ? P : never;
|
|
18
|
+
|
|
19
|
+
type MakeOptional<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>;
|
|
20
|
+
|
|
21
|
+
/* -----------------------------------------------------------------------------
|
|
22
|
+
* Chart Configuration and Theme Constants
|
|
23
|
+
* --------------------------------------------------------------------------- */
|
|
24
|
+
|
|
25
|
+
const THEMES = { dark: ".dark", light: "" } as const;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* @since 0.3.16-canary.0
|
|
29
|
+
*/
|
|
30
|
+
type ChartConfig = Record<
|
|
31
|
+
string,
|
|
32
|
+
{
|
|
33
|
+
label?: ReactNode;
|
|
34
|
+
icon?: ComponentType;
|
|
35
|
+
} & (
|
|
36
|
+
| { color?: never; theme: Record<keyof typeof THEMES, string> }
|
|
37
|
+
| { color?: string; theme?: never }
|
|
38
|
+
)
|
|
39
|
+
>;
|
|
40
|
+
|
|
41
|
+
/* -----------------------------------------------------------------------------
|
|
42
|
+
* Context: ChartProvider
|
|
43
|
+
* --------------------------------------------------------------------------- */
|
|
44
|
+
|
|
45
|
+
const CHART_PROVIDER_NAME = "ChartProvider";
|
|
46
|
+
|
|
47
|
+
type ScopedProps<P> = P & { __scopeChart?: Scope };
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Value provided by Chart Context
|
|
51
|
+
*/
|
|
52
|
+
interface ChartContextValue {
|
|
53
|
+
/**
|
|
54
|
+
* Display configuration for the chart
|
|
55
|
+
*/
|
|
56
|
+
config: ChartConfig;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const [createChartContext, createChartScope] = createContextScope(CHART_PROVIDER_NAME);
|
|
60
|
+
|
|
61
|
+
const [ChartContextProvider, useChartContext] =
|
|
62
|
+
createChartContext<ChartContextValue>(CHART_PROVIDER_NAME);
|
|
63
|
+
|
|
64
|
+
/* -----------------------------------------------------------------------------
|
|
65
|
+
* Component: Chart
|
|
66
|
+
* -------------------------------------------------------------------------- */
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* @since 0.3.16-canary.0
|
|
70
|
+
*/
|
|
71
|
+
interface ChartContainerProps extends ComponentProps<"div"> {
|
|
72
|
+
children: ComponentProps<typeof RechartsPrimitive.ResponsiveContainer>["children"];
|
|
73
|
+
config: ChartConfig;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* @since 0.3.16-canary.0
|
|
78
|
+
*/
|
|
79
|
+
function ChartContainer({
|
|
80
|
+
__scopeChart,
|
|
81
|
+
children,
|
|
82
|
+
className,
|
|
83
|
+
config,
|
|
84
|
+
id,
|
|
85
|
+
...props
|
|
86
|
+
}: ScopedProps<ChartContainerProps>): JSX.Element {
|
|
87
|
+
const uniqueId = useId();
|
|
88
|
+
const chartId = `chart-${id ?? uniqueId}`;
|
|
89
|
+
|
|
90
|
+
return (
|
|
91
|
+
<ChartContextProvider config={config} scope={__scopeChart}>
|
|
92
|
+
<div
|
|
93
|
+
className={cn(
|
|
94
|
+
"flex aspect-video justify-center text-xs",
|
|
95
|
+
"[&_.recharts-cartesian-axis-tick_text]:fill-muted-foreground",
|
|
96
|
+
"[&_.recharts-cartesian-grid_line[stroke='#ccc']]:stroke-border/50",
|
|
97
|
+
"[&_.recharts-curve.recharts-tooltip-cursor]:stroke-border",
|
|
98
|
+
"[&_.recharts-dot[stroke='#fff']]:stroke-transparent",
|
|
99
|
+
"[&_.recharts-layer]:outline-hidden",
|
|
100
|
+
"[&_.recharts-polar-grid_[stroke='#ccc']]:stroke-border",
|
|
101
|
+
"[&_.recharts-radial-bar-background-sector]:fill-muted",
|
|
102
|
+
"[&_.recharts-rectangle.recharts-tooltip-cursor]:fill-muted",
|
|
103
|
+
"[&_.recharts-reference-line_[stroke='#ccc']]:stroke-border",
|
|
104
|
+
"[&_.recharts-sector]:outline-hidden",
|
|
105
|
+
"[&_.recharts-sector[stroke='#fff']]:stroke-transparent",
|
|
106
|
+
"[&_.recharts-surface]:outline-hidden",
|
|
107
|
+
className,
|
|
108
|
+
)}
|
|
109
|
+
data-chart={chartId}
|
|
110
|
+
data-slot="chart"
|
|
111
|
+
{...props}
|
|
112
|
+
>
|
|
113
|
+
<ChartStyle config={config} id={chartId} />
|
|
114
|
+
<RechartsPrimitive.ResponsiveContainer>{children}</RechartsPrimitive.ResponsiveContainer>
|
|
115
|
+
</div>
|
|
116
|
+
</ChartContextProvider>
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/* -----------------------------------------------------------------------------
|
|
121
|
+
* Component: ChartStyle
|
|
122
|
+
* -------------------------------------------------------------------------- */
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* @since 0.3.16-canary.0
|
|
126
|
+
*/
|
|
127
|
+
interface ChartStyleProps {
|
|
128
|
+
config: ChartConfig;
|
|
129
|
+
id: string;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* @since 0.3.16-canary.0
|
|
134
|
+
*/
|
|
135
|
+
function ChartStyle({ config, id }: ChartStyleProps): ReactNode {
|
|
136
|
+
const colorConfig = Object.entries(config).filter(
|
|
137
|
+
([, itemConfig]) => itemConfig.theme ?? itemConfig.color,
|
|
138
|
+
);
|
|
139
|
+
|
|
140
|
+
if (colorConfig.length === 0) {
|
|
141
|
+
return null;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
return (
|
|
145
|
+
<style
|
|
146
|
+
dangerouslySetInnerHTML={{
|
|
147
|
+
__html: generateChartStyles(id, colorConfig),
|
|
148
|
+
}}
|
|
149
|
+
/>
|
|
150
|
+
);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/* -----------------------------------------------------------------------------
|
|
154
|
+
* Component: ChartTooltip
|
|
155
|
+
* -------------------------------------------------------------------------- */
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* @since 0.3.16-canary.0
|
|
159
|
+
*/
|
|
160
|
+
type ChartTooltipProps<TValue extends ValueType, TName extends NameType> = TooltipProps<
|
|
161
|
+
TValue,
|
|
162
|
+
TName
|
|
163
|
+
>;
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* @since 0.3.16-canary.0
|
|
167
|
+
*/
|
|
168
|
+
const ChartTooltip = RechartsPrimitive.Tooltip;
|
|
169
|
+
|
|
170
|
+
/* -----------------------------------------------------------------------------
|
|
171
|
+
* Component: ChartTooltipContent
|
|
172
|
+
* -------------------------------------------------------------------------- */
|
|
173
|
+
|
|
174
|
+
const CHART_TOOLTIP_CONTENT_NAME = "ChartTooltipContent";
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* @since 0.3.16-canary.0
|
|
178
|
+
*/
|
|
179
|
+
type ChartTooltipContentProps<TValue extends ValueType, TName extends NameType> = Omit<
|
|
180
|
+
MakeOptional<
|
|
181
|
+
TooltipContentProps<TValue, TName>,
|
|
182
|
+
"accessibilityLayer" | "active" | "activeIndex" | "coordinate" | "payload"
|
|
183
|
+
>,
|
|
184
|
+
"payload"
|
|
185
|
+
> & {
|
|
186
|
+
hideIndicator?: boolean;
|
|
187
|
+
hideLabel?: boolean;
|
|
188
|
+
indicator?: "dashed" | "dot" | "line";
|
|
189
|
+
labelKey?: string;
|
|
190
|
+
nameKey?: string;
|
|
191
|
+
color?: string | undefined;
|
|
192
|
+
className?: string | undefined;
|
|
193
|
+
payload?: Array<Payload<TValue, TName>>;
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* @since 0.3.16-canary.0
|
|
198
|
+
*/
|
|
199
|
+
function ChartTooltipContent<TValue extends ValueType, TName extends NameType>({
|
|
200
|
+
__scopeChart,
|
|
201
|
+
active,
|
|
202
|
+
className,
|
|
203
|
+
color,
|
|
204
|
+
formatter,
|
|
205
|
+
hideIndicator = false,
|
|
206
|
+
hideLabel = false,
|
|
207
|
+
indicator = "dot",
|
|
208
|
+
label,
|
|
209
|
+
labelClassName,
|
|
210
|
+
labelFormatter,
|
|
211
|
+
labelKey,
|
|
212
|
+
nameKey,
|
|
213
|
+
payload = [],
|
|
214
|
+
}: ScopedProps<ChartTooltipContentProps<TValue, TName>>): ReactNode {
|
|
215
|
+
const { config } = useChartContext(CHART_TOOLTIP_CONTENT_NAME, __scopeChart);
|
|
216
|
+
|
|
217
|
+
const tooltipLabel = useMemo((): ReactNode => {
|
|
218
|
+
if (hideLabel || payload.length === 0) {
|
|
219
|
+
return null;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
const item = payload[0];
|
|
223
|
+
if (item === undefined) {
|
|
224
|
+
return null;
|
|
225
|
+
}
|
|
226
|
+
const key = safeToString(labelKey ?? item.dataKey ?? item.name ?? "value");
|
|
227
|
+
const itemConfig = getPayloadConfigFromPayload(config, item, key);
|
|
228
|
+
const configEntry = typeof label === "string" && label in config ? config[label] : undefined;
|
|
229
|
+
const value =
|
|
230
|
+
!labelKey && typeof label === "string"
|
|
231
|
+
? configEntry !== undefined
|
|
232
|
+
? (configEntry.label ?? label)
|
|
233
|
+
: label
|
|
234
|
+
: itemConfig?.label;
|
|
235
|
+
|
|
236
|
+
if (labelFormatter) {
|
|
237
|
+
return (
|
|
238
|
+
<div className={cn("font-medium", labelClassName)}>{labelFormatter(value, payload)}</div>
|
|
239
|
+
);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
if (!value) {
|
|
243
|
+
return null;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
return <div className={cn("font-medium", labelClassName)}>{value}</div>;
|
|
247
|
+
}, [label, labelFormatter, payload, hideLabel, labelClassName, config, labelKey]);
|
|
248
|
+
|
|
249
|
+
if (!active || payload.length === 0) {
|
|
250
|
+
return null;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
const nestLabel = payload.length === 1 && indicator !== "dot";
|
|
254
|
+
|
|
255
|
+
return (
|
|
256
|
+
<div
|
|
257
|
+
className={cn(
|
|
258
|
+
"grid min-w-[8rem] items-start gap-1.5 px-2.5 py-1.5",
|
|
259
|
+
"rounded-lg border border-border/50",
|
|
260
|
+
"bg-background text-xs shadow-xl",
|
|
261
|
+
className,
|
|
262
|
+
)}
|
|
263
|
+
>
|
|
264
|
+
{nestLabel ? null : tooltipLabel}
|
|
265
|
+
<div className="grid gap-1.5">
|
|
266
|
+
{payload.map((item, index) => {
|
|
267
|
+
const key = safeToString(nameKey ?? item.name ?? item.dataKey ?? "value");
|
|
268
|
+
const itemConfig = getPayloadConfigFromPayload(config, item, key);
|
|
269
|
+
const indicatorColor =
|
|
270
|
+
color ??
|
|
271
|
+
(isRecord(item.payload) &&
|
|
272
|
+
"fill" in item.payload &&
|
|
273
|
+
typeof item.payload.fill === "string"
|
|
274
|
+
? item.payload.fill
|
|
275
|
+
: undefined) ??
|
|
276
|
+
item.color;
|
|
277
|
+
|
|
278
|
+
return (
|
|
279
|
+
<div
|
|
280
|
+
key={key}
|
|
281
|
+
className={cn(
|
|
282
|
+
"flex w-full flex-wrap items-stretch gap-2",
|
|
283
|
+
"[&>svg]:size-2.5 [&>svg]:text-muted-foreground",
|
|
284
|
+
indicator === "dot" && "items-center",
|
|
285
|
+
)}
|
|
286
|
+
>
|
|
287
|
+
{formatter && item.value !== undefined && item.name ? (
|
|
288
|
+
formatter(item.value, item.name, item, index, [item])
|
|
289
|
+
) : (
|
|
290
|
+
<>
|
|
291
|
+
{itemConfig?.icon ? (
|
|
292
|
+
<itemConfig.icon />
|
|
293
|
+
) : (
|
|
294
|
+
!hideIndicator && (
|
|
295
|
+
<div
|
|
296
|
+
className={cn(
|
|
297
|
+
"shrink-0",
|
|
298
|
+
"rounded-xs border-(--color-border)",
|
|
299
|
+
"bg-(--color-bg)",
|
|
300
|
+
{
|
|
301
|
+
"h-2.5 w-2.5": indicator === "dot",
|
|
302
|
+
"my-0.5": nestLabel && indicator === "dashed",
|
|
303
|
+
"border-1.5 w-0 border-dashed bg-transparent": indicator === "dashed",
|
|
304
|
+
"w-1": indicator === "line",
|
|
305
|
+
},
|
|
306
|
+
)}
|
|
307
|
+
style={
|
|
308
|
+
{
|
|
309
|
+
"--color-bg": indicatorColor,
|
|
310
|
+
"--color-border": indicatorColor,
|
|
311
|
+
} as CSSProperties
|
|
312
|
+
}
|
|
313
|
+
/>
|
|
314
|
+
)
|
|
315
|
+
)}
|
|
316
|
+
<div
|
|
317
|
+
className={cn(
|
|
318
|
+
"flex flex-1 justify-between",
|
|
319
|
+
"leading-none",
|
|
320
|
+
nestLabel ? "items-end" : "items-center",
|
|
321
|
+
)}
|
|
322
|
+
>
|
|
323
|
+
<div className="grid gap-1.5">
|
|
324
|
+
{nestLabel ? tooltipLabel : null}
|
|
325
|
+
<span className="text-muted-foreground">
|
|
326
|
+
{itemConfig?.label ?? item.name}
|
|
327
|
+
</span>
|
|
328
|
+
</div>
|
|
329
|
+
{item.value != null && (
|
|
330
|
+
<span className={cn("font-mono font-medium text-foreground", "tabular-nums")}>
|
|
331
|
+
{typeof item.value === "number"
|
|
332
|
+
? item.value.toLocaleString()
|
|
333
|
+
: safeToString(item.value)}
|
|
334
|
+
</span>
|
|
335
|
+
)}
|
|
336
|
+
</div>
|
|
337
|
+
</>
|
|
338
|
+
)}
|
|
339
|
+
</div>
|
|
340
|
+
);
|
|
341
|
+
})}
|
|
342
|
+
</div>
|
|
343
|
+
</div>
|
|
344
|
+
);
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
/* -----------------------------------------------------------------------------
|
|
348
|
+
* Component: ChartLegend
|
|
349
|
+
* -------------------------------------------------------------------------- */
|
|
350
|
+
|
|
351
|
+
/**
|
|
352
|
+
* @since 0.3.16-canary.0
|
|
353
|
+
*/
|
|
354
|
+
type ChartLegendProps = ComponentProps<typeof RechartsPrimitive.Legend>;
|
|
355
|
+
|
|
356
|
+
/**
|
|
357
|
+
* @since 0.3.16-canary.0
|
|
358
|
+
*/
|
|
359
|
+
const ChartLegend: typeof RechartsPrimitive.Legend = RechartsPrimitive.Legend;
|
|
360
|
+
|
|
361
|
+
/* -----------------------------------------------------------------------------
|
|
362
|
+
* Component: ChartLegendContent
|
|
363
|
+
* -------------------------------------------------------------------------- */
|
|
364
|
+
|
|
365
|
+
const CHART_LEGEND_CONTENT_NAME = "ChartLegendContent";
|
|
366
|
+
|
|
367
|
+
/**
|
|
368
|
+
* @since 0.3.16-canary.0
|
|
369
|
+
*/
|
|
370
|
+
type ChartLegendContentProps = ComponentProps<"div"> &
|
|
371
|
+
ExtractProps<RechartsPrimitive.LegendProps["content"]> & {
|
|
372
|
+
hideIcon?: boolean;
|
|
373
|
+
nameKey?: string;
|
|
374
|
+
};
|
|
375
|
+
|
|
376
|
+
/**
|
|
377
|
+
* @since 0.3.16-canary.0
|
|
378
|
+
*/
|
|
379
|
+
function ChartLegendContent({
|
|
380
|
+
__scopeChart,
|
|
381
|
+
className,
|
|
382
|
+
hideIcon = false,
|
|
383
|
+
nameKey,
|
|
384
|
+
payload,
|
|
385
|
+
verticalAlign = "bottom",
|
|
386
|
+
}: ScopedProps<ChartLegendContentProps>): ReactNode {
|
|
387
|
+
const { config } = useChartContext(CHART_LEGEND_CONTENT_NAME, __scopeChart);
|
|
388
|
+
|
|
389
|
+
if (!payload?.length) {
|
|
390
|
+
return null;
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
return (
|
|
394
|
+
<div
|
|
395
|
+
className={cn(
|
|
396
|
+
"flex items-center justify-center gap-4",
|
|
397
|
+
verticalAlign === "top" ? "pb-3" : "pt-3",
|
|
398
|
+
className,
|
|
399
|
+
)}
|
|
400
|
+
>
|
|
401
|
+
{payload.map((item) => {
|
|
402
|
+
let key = "value";
|
|
403
|
+
|
|
404
|
+
if (nameKey) {
|
|
405
|
+
key = nameKey;
|
|
406
|
+
} else if (item.dataKey != null) {
|
|
407
|
+
key = safeToString(item.dataKey);
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
const itemConfig = getPayloadConfigFromPayload(config, item, key);
|
|
411
|
+
|
|
412
|
+
return (
|
|
413
|
+
<div
|
|
414
|
+
key={nameKey ? safeToString(itemConfig?.color ?? "") : safeToString(item.value ?? "")}
|
|
415
|
+
className={cn(
|
|
416
|
+
"flex items-center gap-1.5",
|
|
417
|
+
"[&>svg]:size-3 [&>svg]:text-muted-foreground",
|
|
418
|
+
)}
|
|
419
|
+
>
|
|
420
|
+
{itemConfig?.icon && !hideIcon ? (
|
|
421
|
+
<itemConfig.icon />
|
|
422
|
+
) : (
|
|
423
|
+
<div
|
|
424
|
+
className={cn("size-2 shrink-0", "rounded-md")}
|
|
425
|
+
style={{
|
|
426
|
+
backgroundColor: item.color,
|
|
427
|
+
}}
|
|
428
|
+
/>
|
|
429
|
+
)}
|
|
430
|
+
{itemConfig?.label}
|
|
431
|
+
</div>
|
|
432
|
+
);
|
|
433
|
+
})}
|
|
434
|
+
</div>
|
|
435
|
+
);
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
/* -----------------------------------------------------------------------------
|
|
439
|
+
* Helpers
|
|
440
|
+
* -------------------------------------------------------------------------- */
|
|
441
|
+
|
|
442
|
+
/**
|
|
443
|
+
* Type guard to check if an unknown value is a record with string keys
|
|
444
|
+
*/
|
|
445
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
446
|
+
return typeof value === "object" && value !== null;
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
/**
|
|
450
|
+
* Safely gets a string value from a record by key
|
|
451
|
+
*/
|
|
452
|
+
function getStringValue(record: Record<string, unknown>, key: string): string | undefined {
|
|
453
|
+
if (key in record) {
|
|
454
|
+
const value = record[key];
|
|
455
|
+
|
|
456
|
+
return typeof value === "string" ? value : undefined;
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
return undefined;
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
/**
|
|
463
|
+
* Safely converts a value to string without type coercion
|
|
464
|
+
*/
|
|
465
|
+
function safeToString(value: unknown): string {
|
|
466
|
+
if (typeof value === "string") {
|
|
467
|
+
return value;
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
if (typeof value === "number") {
|
|
471
|
+
return value.toString();
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
if (value === null || value === undefined) {
|
|
475
|
+
return "";
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
if (typeof value === "boolean") {
|
|
479
|
+
return value.toString();
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
if (typeof value === "bigint") {
|
|
483
|
+
return value.toString();
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
if (typeof value === "symbol") {
|
|
487
|
+
return value.toString();
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
// For objects, arrays, functions, and other complex types, return empty string to avoid [object Object]
|
|
491
|
+
return "";
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
function getPayloadConfigFromPayload(
|
|
495
|
+
config: ChartConfig,
|
|
496
|
+
payload: unknown,
|
|
497
|
+
key: string,
|
|
498
|
+
): ChartConfig[string] | undefined {
|
|
499
|
+
if (!isRecord(payload)) {
|
|
500
|
+
return undefined;
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
const payloadPayload = isRecord(payload.payload) ? payload.payload : undefined;
|
|
504
|
+
let configLabelKey: string = key;
|
|
505
|
+
|
|
506
|
+
// Try to get the config key from the payload first
|
|
507
|
+
const payloadValue = getStringValue(payload, key);
|
|
508
|
+
|
|
509
|
+
if (payloadValue) {
|
|
510
|
+
configLabelKey = payloadValue;
|
|
511
|
+
} else if (payloadPayload) {
|
|
512
|
+
// If not found in the payload, try the nested payload
|
|
513
|
+
const nestedValue = getStringValue(payloadPayload, key);
|
|
514
|
+
|
|
515
|
+
if (nestedValue) {
|
|
516
|
+
configLabelKey = nestedValue;
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
return configLabelKey in config ? config[configLabelKey] : config[key];
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
/**
|
|
524
|
+
* Generates CSS custom property for a specific theme and config item
|
|
525
|
+
*/
|
|
526
|
+
function generateCssVariable(
|
|
527
|
+
key: string,
|
|
528
|
+
itemConfig: ChartConfig[string],
|
|
529
|
+
theme: string,
|
|
530
|
+
): null | string {
|
|
531
|
+
const color = itemConfig.theme?.[theme as keyof typeof itemConfig.theme] ?? itemConfig.color;
|
|
532
|
+
|
|
533
|
+
return color ? ` --color-${key}: ${color};` : null;
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
/**
|
|
537
|
+
* Generates CSS rules for a specific theme
|
|
538
|
+
*/
|
|
539
|
+
function generateThemeStyles(
|
|
540
|
+
theme: string,
|
|
541
|
+
prefix: string,
|
|
542
|
+
id: string,
|
|
543
|
+
colorConfig: Array<[string, ChartConfig[string]]>,
|
|
544
|
+
): string {
|
|
545
|
+
const cssVariables = colorConfig
|
|
546
|
+
.map(([key, itemConfig]) => generateCssVariable(key, itemConfig, theme))
|
|
547
|
+
.filter(Boolean)
|
|
548
|
+
.join("\n");
|
|
549
|
+
|
|
550
|
+
return `${prefix} [data-chart=${id}] {\n${cssVariables}\n}`;
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
/**
|
|
554
|
+
* Generates complete CSS styles for all themes
|
|
555
|
+
*/
|
|
556
|
+
function generateChartStyles(
|
|
557
|
+
id: string,
|
|
558
|
+
colorConfig: Array<[string, ChartConfig[string]]>,
|
|
559
|
+
): string {
|
|
560
|
+
return Object.entries(THEMES)
|
|
561
|
+
.map(([theme, prefix]) => generateThemeStyles(theme, prefix, id, colorConfig))
|
|
562
|
+
.join("\n\n");
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
/* -----------------------------------------------------------------------------
|
|
566
|
+
* Exports
|
|
567
|
+
* -------------------------------------------------------------------------- */
|
|
568
|
+
|
|
569
|
+
export type {
|
|
570
|
+
ChartConfig,
|
|
571
|
+
ChartContainerProps,
|
|
572
|
+
ChartLegendContentProps,
|
|
573
|
+
ChartLegendProps,
|
|
574
|
+
ChartStyleProps,
|
|
575
|
+
ChartTooltipContentProps,
|
|
576
|
+
ChartTooltipProps,
|
|
577
|
+
};
|
|
578
|
+
|
|
579
|
+
export {
|
|
580
|
+
ChartContainer,
|
|
581
|
+
ChartLegend,
|
|
582
|
+
ChartLegendContent,
|
|
583
|
+
ChartStyle,
|
|
584
|
+
ChartTooltip,
|
|
585
|
+
ChartTooltipContent,
|
|
586
|
+
createChartScope,
|
|
587
|
+
};
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import type { ComponentProps, JSX } from "react";
|
|
4
|
+
|
|
5
|
+
import { cn } from "#/lib/utils";
|
|
6
|
+
import { CheckIcon } from "lucide-react";
|
|
7
|
+
|
|
8
|
+
import { Label } from "#/components/label";
|
|
9
|
+
import * as CheckboxGroupPrimitive from "#/primitives/checkbox-group";
|
|
10
|
+
|
|
11
|
+
/* -----------------------------------------------------------------------------
|
|
12
|
+
* Component: CheckboxCards
|
|
13
|
+
* -------------------------------------------------------------------------- */
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* @since 0.3.16-canary.0
|
|
17
|
+
*/
|
|
18
|
+
type CheckboxCardsProps = ComponentProps<typeof CheckboxGroupPrimitive.Root>;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* @since 0.3.16-canary.0
|
|
22
|
+
*/
|
|
23
|
+
function CheckboxCards(props: CheckboxCardsProps): JSX.Element {
|
|
24
|
+
return <CheckboxGroupPrimitive.Root data-slot="checkbox-cards" {...props} />;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/* -----------------------------------------------------------------------------
|
|
28
|
+
* Component: CheckboxCardsItem
|
|
29
|
+
* -------------------------------------------------------------------------- */
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* @since 0.3.16-canary.0
|
|
33
|
+
*/
|
|
34
|
+
interface CheckboxCardsItemProps extends ComponentProps<typeof CheckboxGroupPrimitive.Item> {
|
|
35
|
+
checkboxClassName?: string;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* @since 0.3.16-canary.0
|
|
40
|
+
*/
|
|
41
|
+
function CheckboxCardsItem({
|
|
42
|
+
checkboxClassName,
|
|
43
|
+
children,
|
|
44
|
+
className,
|
|
45
|
+
...props
|
|
46
|
+
}: CheckboxCardsItemProps): JSX.Element {
|
|
47
|
+
return (
|
|
48
|
+
<Label
|
|
49
|
+
className={cn(
|
|
50
|
+
"flex items-start gap-3",
|
|
51
|
+
"p-3",
|
|
52
|
+
"rounded-lg border border-input",
|
|
53
|
+
"transition",
|
|
54
|
+
"hover:not-has-disabled:not-has-aria-checked:bg-secondary",
|
|
55
|
+
"has-focus-visible:border-ring",
|
|
56
|
+
"has-disabled:opacity-50",
|
|
57
|
+
"has-aria-checked:border-primary has-aria-checked:bg-primary/10",
|
|
58
|
+
className,
|
|
59
|
+
)}
|
|
60
|
+
data-slot="checkbox-card"
|
|
61
|
+
>
|
|
62
|
+
<CheckboxGroupPrimitive.Item
|
|
63
|
+
className={cn(
|
|
64
|
+
"peer flex size-4 shrink-0",
|
|
65
|
+
"rounded-sm border border-input shadow-xs outline-hidden",
|
|
66
|
+
"text-primary-foreground",
|
|
67
|
+
"transition",
|
|
68
|
+
"focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50",
|
|
69
|
+
"aria-checked:border-primary aria-checked:bg-primary",
|
|
70
|
+
"focus-visible:aria-checked:ring-primary/20",
|
|
71
|
+
"dark:bg-input/30",
|
|
72
|
+
"dark:focus-visible:aria-checked:ring-primary/40",
|
|
73
|
+
checkboxClassName,
|
|
74
|
+
)}
|
|
75
|
+
data-slot="checkbox-card-item"
|
|
76
|
+
{...props}
|
|
77
|
+
>
|
|
78
|
+
<CheckboxGroupPrimitive.CheckboxGroupIndicator data-slot="checkbox-card-indicator">
|
|
79
|
+
<CheckIcon className="size-3.5" />
|
|
80
|
+
</CheckboxGroupPrimitive.CheckboxGroupIndicator>
|
|
81
|
+
</CheckboxGroupPrimitive.Item>
|
|
82
|
+
{children}
|
|
83
|
+
</Label>
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/* -----------------------------------------------------------------------------
|
|
88
|
+
* Exports
|
|
89
|
+
* -------------------------------------------------------------------------- */
|
|
90
|
+
|
|
91
|
+
export { CheckboxCards, CheckboxCardsItem };
|
|
92
|
+
export type { CheckboxCardsItemProps, CheckboxCardsProps };
|