@minutemailer/kit 1.1.6 → 1.2.0
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/components/donut-chart.d.ts +5 -0
- package/components/donut-chart.js +31 -0
- package/components/engagement-chart.d.ts +8 -0
- package/components/engagement-chart.js +41 -0
- package/components/image-slot.d.ts +22 -0
- package/components/image-slot.js +92 -0
- package/components/more.d.ts +1 -0
- package/components/more.js +4 -3
- package/components/ui/accordion.d.ts +7 -0
- package/components/ui/accordion.js +17 -0
- package/components/ui/badge.d.ts +9 -0
- package/components/ui/badge.js +22 -0
- package/components/ui/chart.d.ts +40 -0
- package/components/ui/chart.js +112 -0
- package/components/ui/pagination.d.ts +13 -0
- package/components/ui/pagination.js +25 -0
- package/components/ui/sonner.js +1 -3
- package/components/ui/table.d.ts +10 -0
- package/components/ui/table.js +27 -0
- package/components/ui/tooltip.js +1 -1
- package/package.json +7 -4
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Cell, Label, Pie, PieChart } from 'recharts';
|
|
3
|
+
import { ChartContainer, } from '../components/ui/chart.js';
|
|
4
|
+
export function DonutChart({ percentage, size = 120 }) {
|
|
5
|
+
const COLORS = {
|
|
6
|
+
filled: 'oklch(0.683 0.178 158.484)', // Primary green
|
|
7
|
+
unfilled: 'oklch(0.922 0 0 / 0.3)', // Light gray transparent
|
|
8
|
+
};
|
|
9
|
+
// Include both filled and unfilled portions in the same data
|
|
10
|
+
const chartData = [
|
|
11
|
+
{ name: 'filled', value: percentage },
|
|
12
|
+
{ name: 'unfilled', value: 100 - percentage },
|
|
13
|
+
];
|
|
14
|
+
const chartConfig = {
|
|
15
|
+
filled: {
|
|
16
|
+
label: 'Value',
|
|
17
|
+
color: COLORS.filled,
|
|
18
|
+
},
|
|
19
|
+
unfilled: {
|
|
20
|
+
label: 'Remaining',
|
|
21
|
+
color: COLORS.unfilled,
|
|
22
|
+
},
|
|
23
|
+
};
|
|
24
|
+
return (_jsx(ChartContainer, { config: chartConfig, className: "aspect-square", style: { width: `${size}px`, height: `${size}px` }, children: _jsx(PieChart, { children: _jsxs(Pie, { data: chartData, dataKey: "value", nameKey: "name", innerRadius: "60%", outerRadius: "80%", startAngle: 90, endAngle: 450, paddingAngle: 0, stroke: "none", isAnimationActive: false, children: [_jsx(Cell, { fill: COLORS.filled, stroke: "none" }), _jsx(Cell, { fill: COLORS.unfilled, stroke: "none" }), _jsx(Label, { content: ({ viewBox }) => {
|
|
25
|
+
if (viewBox && 'cx' in viewBox && 'cy' in viewBox) {
|
|
26
|
+
const centerX = Number(viewBox.cx);
|
|
27
|
+
const centerY = Number(viewBox.cy) + 4;
|
|
28
|
+
return (_jsx("text", { x: centerX, y: centerY, textAnchor: "middle", dominantBaseline: "middle", children: _jsxs("tspan", { x: centerX, y: centerY, fill: COLORS.filled, className: "text-base font-semibold", children: [percentage, "%"] }) }));
|
|
29
|
+
}
|
|
30
|
+
} })] }) }) }));
|
|
31
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Area, AreaChart, CartesianGrid, XAxis, YAxis, Tooltip } from 'recharts';
|
|
3
|
+
import { ChartContainer, ChartTooltipContent, } from '../components/ui/chart.js';
|
|
4
|
+
const defaultData = [
|
|
5
|
+
{ hour: '0h', opens: 245, clicks: 12 },
|
|
6
|
+
{ hour: '1h', opens: 520, clicks: 45 },
|
|
7
|
+
{ hour: '2h', opens: 890, clicks: 98 },
|
|
8
|
+
{ hour: '3h', opens: 1240, clicks: 156 },
|
|
9
|
+
{ hour: '4h', opens: 1580, clicks: 234 },
|
|
10
|
+
{ hour: '5h', opens: 1820, clicks: 298 },
|
|
11
|
+
{ hour: '6h', opens: 1950, clicks: 345 },
|
|
12
|
+
{ hour: '7h', opens: 2020, clicks: 378 },
|
|
13
|
+
{ hour: '8h', opens: 2050, clicks: 390 },
|
|
14
|
+
];
|
|
15
|
+
const chartConfig = {
|
|
16
|
+
opens: {
|
|
17
|
+
label: 'Opens',
|
|
18
|
+
color: 'oklch(0.683 0.178 158.484)', // Primary green
|
|
19
|
+
},
|
|
20
|
+
clicks: {
|
|
21
|
+
label: 'Clicks',
|
|
22
|
+
color: 'oklch(0.656 0.173 256.68)', // Secondary purple
|
|
23
|
+
},
|
|
24
|
+
};
|
|
25
|
+
export function EngagementChart({ data = defaultData }) {
|
|
26
|
+
return (_jsx(ChartContainer, { config: chartConfig, className: "h-[300px] w-full [&_svg]:outline-none", children: _jsxs(AreaChart, { data: data, margin: {
|
|
27
|
+
left: 0,
|
|
28
|
+
right: 0,
|
|
29
|
+
top: 12,
|
|
30
|
+
}, children: [_jsxs("defs", { children: [_jsxs("linearGradient", { id: "opensGradient", x1: "0", y1: "0", x2: "0", y2: "1", children: [_jsx("stop", { offset: "0%", stopColor: "var(--color-opens)", stopOpacity: 0.3 }), _jsx("stop", { offset: "100%", stopColor: "var(--color-opens)", stopOpacity: 0 })] }), _jsxs("linearGradient", { id: "clicksGradient", x1: "0", y1: "0", x2: "0", y2: "1", children: [_jsx("stop", { offset: "0%", stopColor: "var(--color-clicks)", stopOpacity: 0.3 }), _jsx("stop", { offset: "100%", stopColor: "var(--color-clicks)", stopOpacity: 0 })] })] }), _jsx(CartesianGrid, { vertical: false }), _jsx(XAxis, { dataKey: "hour", tickLine: false, axisLine: false, tickMargin: 8 }), _jsx(YAxis, { tickLine: false, axisLine: false, tickMargin: 8 }), _jsx(Area, { dataKey: "opens", type: "linear", fill: "url(#opensGradient)", stroke: "var(--color-opens)", strokeWidth: 2, dot: false, activeDot: {
|
|
31
|
+
r: 4,
|
|
32
|
+
fill: 'var(--color-opens)',
|
|
33
|
+
stroke: 'var(--color-opens)',
|
|
34
|
+
strokeWidth: 2,
|
|
35
|
+
} }), _jsx(Area, { dataKey: "clicks", type: "linear", fill: "url(#clicksGradient)", stroke: "var(--color-clicks)", strokeWidth: 2, dot: false, activeDot: {
|
|
36
|
+
r: 4,
|
|
37
|
+
fill: 'var(--color-clicks)',
|
|
38
|
+
stroke: 'var(--color-clicks)',
|
|
39
|
+
strokeWidth: 2,
|
|
40
|
+
} }), _jsx(Tooltip, { content: _jsx(ChartTooltipContent, {}) })] }) }));
|
|
41
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { type ReactNode } from 'react';
|
|
2
|
+
export interface ImageSlotAction {
|
|
3
|
+
name: string;
|
|
4
|
+
value: string;
|
|
5
|
+
icon?: ReactNode;
|
|
6
|
+
type?: 'action' | 'file';
|
|
7
|
+
}
|
|
8
|
+
export interface ImageSlotProps {
|
|
9
|
+
src?: string;
|
|
10
|
+
alt?: string;
|
|
11
|
+
width: number;
|
|
12
|
+
height: number;
|
|
13
|
+
actions?: ImageSlotAction[];
|
|
14
|
+
onAction?: (action: string) => void;
|
|
15
|
+
onRemove?: () => void;
|
|
16
|
+
onFile?: (file: File) => void;
|
|
17
|
+
loading?: boolean;
|
|
18
|
+
accept?: string[];
|
|
19
|
+
className?: string;
|
|
20
|
+
}
|
|
21
|
+
declare function ImageSlot({ src, alt, width, height, actions, onAction, onRemove, onFile, loading, accept, className, }: ImageSlotProps): import("react/jsx-runtime").JSX.Element;
|
|
22
|
+
export { ImageSlot };
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import * as React from 'react';
|
|
3
|
+
import { useState } from 'react';
|
|
4
|
+
import { cn } from '../utils/utils.js';
|
|
5
|
+
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, } from '../components/ui/dropdown-menu.js';
|
|
6
|
+
import { Button } from '../components/ui/button.js';
|
|
7
|
+
import { Spinner } from '../components/ui/spinner.js';
|
|
8
|
+
import { toast } from 'sonner';
|
|
9
|
+
import { ChevronDownIcon, ImageIcon, Trash2Icon } from 'lucide-react';
|
|
10
|
+
function ImageSlot({ src, alt = '', width, height, actions = [], onAction, onRemove, onFile, loading = false, accept = ['image/gif', 'image/png', 'image/jpeg'], className, }) {
|
|
11
|
+
const aspectRatio = width / height;
|
|
12
|
+
const [dragState, setDragState] = useState('idle');
|
|
13
|
+
const dragCounterRef = React.useRef(0);
|
|
14
|
+
const fileInputRef = React.useRef(null);
|
|
15
|
+
const handleAction = (action) => {
|
|
16
|
+
onAction?.(action);
|
|
17
|
+
};
|
|
18
|
+
const handleFileInputChange = (e) => {
|
|
19
|
+
const file = e.target.files?.[0];
|
|
20
|
+
if (!file)
|
|
21
|
+
return;
|
|
22
|
+
if (isValidFile(file)) {
|
|
23
|
+
onFile?.(file);
|
|
24
|
+
}
|
|
25
|
+
else {
|
|
26
|
+
toast.error('Unsupported file type', {
|
|
27
|
+
description: `Please upload one of: ${accept.map((type) => type.split('/')[1]).join(', ')}`,
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
// Reset input so the same file can be selected again
|
|
31
|
+
if (fileInputRef.current) {
|
|
32
|
+
fileInputRef.current.value = '';
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
const handleActionClick = (action) => {
|
|
36
|
+
if (action.type === 'file') {
|
|
37
|
+
fileInputRef.current?.click();
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
handleAction(action.value);
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
const isValidFile = (file) => {
|
|
44
|
+
return accept.includes(file.type);
|
|
45
|
+
};
|
|
46
|
+
const handleDragEnter = (e) => {
|
|
47
|
+
e.preventDefault();
|
|
48
|
+
e.stopPropagation();
|
|
49
|
+
setDragState('over');
|
|
50
|
+
};
|
|
51
|
+
const handleDragOver = (e) => {
|
|
52
|
+
e.preventDefault();
|
|
53
|
+
e.stopPropagation();
|
|
54
|
+
};
|
|
55
|
+
const handleDragLeave = (e) => {
|
|
56
|
+
if (e.currentTarget.contains(e.relatedTarget))
|
|
57
|
+
return;
|
|
58
|
+
dragCounterRef.current--;
|
|
59
|
+
if (dragCounterRef.current <= 0) {
|
|
60
|
+
dragCounterRef.current = 0;
|
|
61
|
+
setDragState('idle');
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
const handleDrop = (e) => {
|
|
65
|
+
e.preventDefault();
|
|
66
|
+
e.stopPropagation();
|
|
67
|
+
dragCounterRef.current = 0;
|
|
68
|
+
setDragState('idle');
|
|
69
|
+
const file = e.dataTransfer.files?.[0];
|
|
70
|
+
if (!file)
|
|
71
|
+
return;
|
|
72
|
+
if (isValidFile(file)) {
|
|
73
|
+
onFile?.(file);
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
toast.error('Unsupported file type', {
|
|
77
|
+
description: `Please upload one of: ${accept.map((type) => type.split('/')[1]).join(', ')}`,
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
if (src) {
|
|
82
|
+
return (_jsxs("div", { className: cn('relative group', className), style: { width, height }, children: [_jsx("img", { src: src, alt: alt, width: width, height: height, className: "block w-full h-full object-cover" }), onRemove && (_jsx("div", { className: "absolute inset-0 bg-black/50 opacity-0 group-hover:opacity-100 transition-opacity flex items-center justify-center", children: _jsx(Button, { variant: "outline", className: "rounded-full", size: "icon", onClick: onRemove, children: _jsx(Trash2Icon, {}) }) }))] }));
|
|
83
|
+
}
|
|
84
|
+
// Placeholder view
|
|
85
|
+
return (_jsx("div", { "data-slot": "image-slot", className: cn('bg-muted flex items-center justify-center @container transition-colors', dragState === 'over' && 'bg-green-100 dark:bg-green-950', className), style: {
|
|
86
|
+
width: width,
|
|
87
|
+
maxHeight: height,
|
|
88
|
+
maxWidth: '100%',
|
|
89
|
+
height: width / aspectRatio,
|
|
90
|
+
}, onDragEnter: handleDragEnter, onDragOver: handleDragOver, onDragLeave: handleDragLeave, onDrop: handleDrop, children: loading ? (_jsx(Spinner, { className: "size-8" })) : dragState === 'over' ? (_jsx("p", { className: "text-sm text-muted-foreground", children: "Drop image to upload" })) : (_jsxs(_Fragment, { children: [_jsx("input", { ref: fileInputRef, type: "file", accept: accept.join(','), onChange: handleFileInputChange, className: "hidden" }), _jsxs(DropdownMenu, { children: [_jsx(DropdownMenuTrigger, { asChild: true, children: _jsxs(Button, { variant: "outline", size: "sm", children: [_jsx(ImageIcon, { className: "@xs:hidden" }), _jsx("span", { className: "hidden @xs:inline", children: "Add image" }), _jsx(ChevronDownIcon, { className: "hidden @xs:inline" })] }) }), _jsx(DropdownMenuContent, { children: actions.map((action) => (_jsxs(DropdownMenuItem, { onClick: () => handleActionClick(action), children: [action.icon, action.name] }, action.value))) })] })] })) }));
|
|
91
|
+
}
|
|
92
|
+
export { ImageSlot };
|
package/components/more.d.ts
CHANGED
package/components/more.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import
|
|
2
|
+
import * as React from 'react';
|
|
3
|
+
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuTrigger, } from '../components/ui/dropdown-menu.js';
|
|
3
4
|
import { Button } from '../components/ui/button.js';
|
|
4
|
-
import
|
|
5
|
+
import MoreIcon from '../icons/More.js';
|
|
5
6
|
import Spinner from '../icons/Spinner.js';
|
|
6
7
|
export function More({ options, loading = false, onChange, disabled = false, }) {
|
|
7
8
|
const handleSelect = (value) => {
|
|
@@ -9,5 +10,5 @@ export function More({ options, loading = false, onChange, disabled = false, })
|
|
|
9
10
|
onChange(value);
|
|
10
11
|
}
|
|
11
12
|
};
|
|
12
|
-
return (_jsxs(DropdownMenu, { children: [_jsx(DropdownMenuTrigger, { asChild: true, children: _jsx(Button, { variant: "ghost", size: "icon", disabled: disabled || loading, "aria-label": "More options", children: loading ? _jsx(Spinner, {}) : _jsx(
|
|
13
|
+
return (_jsxs(DropdownMenu, { children: [_jsx(DropdownMenuTrigger, { asChild: true, children: _jsx(Button, { variant: "ghost", size: "icon", disabled: disabled || loading, "aria-label": "More options", children: loading ? _jsx(Spinner, {}) : _jsx(MoreIcon, {}) }) }), _jsx(DropdownMenuContent, { align: "end", children: options.map((option, index) => (_jsxs(React.Fragment, { children: [option.separator && _jsx(DropdownMenuSeparator, {}), _jsx(DropdownMenuItem, { variant: option.variant, disabled: option.disabled, onSelect: () => handleSelect(option.value), children: option.name })] }, option.value))) })] }));
|
|
13
14
|
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import * as AccordionPrimitive from '@radix-ui/react-accordion';
|
|
3
|
+
declare function Accordion({ ...props }: React.ComponentProps<typeof AccordionPrimitive.Root>): import("react/jsx-runtime").JSX.Element;
|
|
4
|
+
declare function AccordionItem({ className, ...props }: React.ComponentProps<typeof AccordionPrimitive.Item>): import("react/jsx-runtime").JSX.Element;
|
|
5
|
+
declare function AccordionTrigger({ className, children, ...props }: React.ComponentProps<typeof AccordionPrimitive.Trigger>): import("react/jsx-runtime").JSX.Element;
|
|
6
|
+
declare function AccordionContent({ className, children, ...props }: React.ComponentProps<typeof AccordionPrimitive.Content>): import("react/jsx-runtime").JSX.Element;
|
|
7
|
+
export { Accordion, AccordionItem, AccordionTrigger, AccordionContent };
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import * as AccordionPrimitive from '@radix-ui/react-accordion';
|
|
3
|
+
import { ChevronDown } from 'lucide-react';
|
|
4
|
+
import { cn } from '../../utils/utils.js';
|
|
5
|
+
function Accordion({ ...props }) {
|
|
6
|
+
return _jsx(AccordionPrimitive.Root, { "data-slot": "accordion", ...props });
|
|
7
|
+
}
|
|
8
|
+
function AccordionItem({ className, ...props }) {
|
|
9
|
+
return (_jsx(AccordionPrimitive.Item, { "data-slot": "accordion-item", className: cn('border-b', className), ...props }));
|
|
10
|
+
}
|
|
11
|
+
function AccordionTrigger({ className, children, ...props }) {
|
|
12
|
+
return (_jsx(AccordionPrimitive.Header, { className: "flex", "data-slot": "accordion-header", children: _jsxs(AccordionPrimitive.Trigger, { "data-slot": "accordion-trigger", className: cn('flex flex-1 cursor-pointer items-center justify-between rounded-md px-4 py-5 font-medium transition-all hover:bg-accent/50 [&[data-state=open]>svg]:rotate-180', className), ...props, children: [children, _jsx(ChevronDown, { className: "h-4 w-4 shrink-0 transition-transform duration-200" })] }) }));
|
|
13
|
+
}
|
|
14
|
+
function AccordionContent({ className, children, ...props }) {
|
|
15
|
+
return (_jsx(AccordionPrimitive.Content, { "data-slot": "accordion-content", className: "data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down overflow-hidden text-sm transition-all", ...props, children: _jsx("div", { className: cn('pb-4 pt-0', className), children: children }) }));
|
|
16
|
+
}
|
|
17
|
+
export { Accordion, AccordionItem, AccordionTrigger, AccordionContent };
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { type VariantProps } from 'class-variance-authority';
|
|
3
|
+
declare const badgeVariants: (props?: ({
|
|
4
|
+
variant?: "default" | "destructive" | "outline" | "secondary" | null | undefined;
|
|
5
|
+
} & import("class-variance-authority/types").ClassProp) | undefined) => string;
|
|
6
|
+
declare function Badge({ className, variant, asChild, ...props }: React.ComponentProps<'span'> & VariantProps<typeof badgeVariants> & {
|
|
7
|
+
asChild?: boolean;
|
|
8
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
9
|
+
export { Badge, badgeVariants };
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { Slot } from '@radix-ui/react-slot';
|
|
3
|
+
import { cva } from 'class-variance-authority';
|
|
4
|
+
import { cn } from '../../utils/utils.js';
|
|
5
|
+
const badgeVariants = cva('inline-flex items-center justify-center rounded-full border px-4 py-2 text-xs font-medium w-fit whitespace-nowrap shrink-0 [&>svg]:size-3 gap-1 [&>svg]:pointer-events-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive transition-[color,box-shadow] overflow-hidden', {
|
|
6
|
+
variants: {
|
|
7
|
+
variant: {
|
|
8
|
+
default: 'border-transparent bg-primary text-primary-foreground [a&]:hover:bg-primary/90',
|
|
9
|
+
secondary: 'border-transparent bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90',
|
|
10
|
+
destructive: 'border-transparent bg-destructive text-white [a&]:hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60',
|
|
11
|
+
outline: 'text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground',
|
|
12
|
+
},
|
|
13
|
+
},
|
|
14
|
+
defaultVariants: {
|
|
15
|
+
variant: 'default',
|
|
16
|
+
},
|
|
17
|
+
});
|
|
18
|
+
function Badge({ className, variant, asChild = false, ...props }) {
|
|
19
|
+
const Comp = asChild ? Slot : 'span';
|
|
20
|
+
return (_jsx(Comp, { "data-slot": "badge", className: cn(badgeVariants({ variant }), className), ...props }));
|
|
21
|
+
}
|
|
22
|
+
export { Badge, badgeVariants };
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import * as RechartsPrimitive from "recharts";
|
|
3
|
+
export type ChartConfig = {
|
|
4
|
+
[k in string]: {
|
|
5
|
+
label?: React.ReactNode;
|
|
6
|
+
icon?: React.ComponentType;
|
|
7
|
+
} & ({
|
|
8
|
+
color?: string;
|
|
9
|
+
theme?: never;
|
|
10
|
+
} | {
|
|
11
|
+
color?: never;
|
|
12
|
+
theme: Record<keyof typeof THEMES | "light" | "dark", string>;
|
|
13
|
+
});
|
|
14
|
+
};
|
|
15
|
+
declare const THEMES: {
|
|
16
|
+
readonly light: "";
|
|
17
|
+
readonly dark: "";
|
|
18
|
+
};
|
|
19
|
+
export interface ChartContainerProps extends React.ComponentProps<"div"> {
|
|
20
|
+
config: ChartConfig;
|
|
21
|
+
children: React.ComponentProps<typeof RechartsPrimitive.ResponsiveContainer>["children"];
|
|
22
|
+
}
|
|
23
|
+
export declare function ChartContainer({ id, className, children, config, ...props }: ChartContainerProps): import("react/jsx-runtime").JSX.Element;
|
|
24
|
+
export interface ChartTooltipProps extends React.ComponentProps<typeof RechartsPrimitive.Tooltip> {
|
|
25
|
+
hideLabel?: boolean;
|
|
26
|
+
hideIndicator?: boolean;
|
|
27
|
+
indicator?: "line" | "dot" | "dashed";
|
|
28
|
+
nameKey?: string;
|
|
29
|
+
labelKey?: string;
|
|
30
|
+
}
|
|
31
|
+
export declare function ChartTooltip({ ...props }: ChartTooltipProps): import("react/jsx-runtime").JSX.Element;
|
|
32
|
+
export interface ChartTooltipContentProps extends Omit<React.ComponentProps<typeof RechartsPrimitive.Tooltip>, "content">, React.ComponentProps<"div"> {
|
|
33
|
+
hideLabel?: boolean;
|
|
34
|
+
hideIndicator?: boolean;
|
|
35
|
+
indicator?: "line" | "dot" | "dashed";
|
|
36
|
+
nameKey?: string;
|
|
37
|
+
labelKey?: string;
|
|
38
|
+
}
|
|
39
|
+
export declare function ChartTooltipContent({ active, payload, className, indicator, hideLabel, hideIndicator, label, labelFormatter, labelClassName, formatter, color, nameKey, labelKey, }: ChartTooltipContentProps): import("react/jsx-runtime").JSX.Element | null;
|
|
40
|
+
export {};
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import * as RechartsPrimitive from "recharts";
|
|
5
|
+
import { cn } from "../../utils/utils.js";
|
|
6
|
+
const THEMES = { light: "", dark: "" };
|
|
7
|
+
export function ChartContainer({ id, className, children, config, ...props }) {
|
|
8
|
+
const uniqueId = React.useId();
|
|
9
|
+
const chartId = `chart-${id || uniqueId.replace(/:/g, "")}`;
|
|
10
|
+
return (_jsx(ChartContext.Provider, { value: { config }, children: _jsxs("div", { "data-chart": chartId, className: cn("flex aspect-video justify-center text-xs", className), ...props, children: [_jsx(ChartStyle, { id: chartId, config: config }), _jsx(RechartsPrimitive.ResponsiveContainer, { children: children })] }) }));
|
|
11
|
+
}
|
|
12
|
+
function ChartStyle({ id, config }) {
|
|
13
|
+
const colorConfig = Object.entries(config).filter(([_, config]) => config.color || config.theme);
|
|
14
|
+
if (!colorConfig.length) {
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
17
|
+
return (_jsx("style", { dangerouslySetInnerHTML: {
|
|
18
|
+
__html: `
|
|
19
|
+
[data-chart=${id}] {
|
|
20
|
+
${colorConfig
|
|
21
|
+
.map(([key, itemConfig]) => {
|
|
22
|
+
const color = itemConfig.theme?.light || itemConfig.color || `hsl(var(--chart-${key.charAt(key.length - 1)}))`;
|
|
23
|
+
return ` --color-${key}: ${color};`;
|
|
24
|
+
})
|
|
25
|
+
.join("\n")}
|
|
26
|
+
}
|
|
27
|
+
`,
|
|
28
|
+
} }));
|
|
29
|
+
}
|
|
30
|
+
export function ChartTooltip({ ...props }) {
|
|
31
|
+
return _jsx(RechartsPrimitive.Tooltip, { ...props });
|
|
32
|
+
}
|
|
33
|
+
export function ChartTooltipContent({ active, payload, className, indicator = "dot", hideLabel = false, hideIndicator = false, label, labelFormatter, labelClassName, formatter, color, nameKey, labelKey, }) {
|
|
34
|
+
const { config } = useChart();
|
|
35
|
+
const tooltipLabel = React.useMemo(() => {
|
|
36
|
+
if (hideLabel || !payload?.length) {
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
const [item] = payload;
|
|
40
|
+
const key = `${labelKey || item.dataKey || item.name || "value"}`;
|
|
41
|
+
const itemConfig = getPayloadConfigFromPayload(config, item, key);
|
|
42
|
+
const value = !labelKey && typeof label === "string"
|
|
43
|
+
? config[label]?.label || label
|
|
44
|
+
: itemConfig?.label;
|
|
45
|
+
if (labelFormatter) {
|
|
46
|
+
return (_jsx("div", { className: cn("font-medium", labelClassName), children: labelFormatter(value, payload) }));
|
|
47
|
+
}
|
|
48
|
+
if (!value) {
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
return _jsx("div", { className: cn("font-medium", labelClassName), children: value });
|
|
52
|
+
}, [
|
|
53
|
+
label,
|
|
54
|
+
labelFormatter,
|
|
55
|
+
payload,
|
|
56
|
+
hideLabel,
|
|
57
|
+
labelClassName,
|
|
58
|
+
config,
|
|
59
|
+
labelKey,
|
|
60
|
+
]);
|
|
61
|
+
if (!active || !payload?.length) {
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
const nestLabel = payload.length === 1 && indicator !== "dot";
|
|
65
|
+
return (_jsxs("div", { className: cn("grid min-w-[8rem] items-start gap-1.5 rounded-lg border border-border/50 bg-background px-2.5 py-1.5 text-xs shadow-xl", className), children: [!nestLabel ? tooltipLabel : null, _jsx("div", { className: "grid gap-1.5", children: payload.map((item, index) => {
|
|
66
|
+
const key = `${nameKey || item.name || item.dataKey || "value"}`;
|
|
67
|
+
const itemConfig = getPayloadConfigFromPayload(config, item, key);
|
|
68
|
+
const indicatorColor = color || item.payload.fill || item.color;
|
|
69
|
+
return (_jsx("div", { className: cn("flex w-full flex-wrap items-stretch gap-2 [&>svg]:h-2.5 [&>svg]:w-2.5 [&>svg]:text-muted-foreground", indicator === "dot" && "items-center"), children: formatter && item?.value !== undefined && item.name ? (formatter(item.value, item.name, item, index, item.payload)) : (_jsxs(_Fragment, { children: [itemConfig?.icon ? (_jsx(itemConfig.icon, {})) : (!hideIndicator && (_jsx("div", { className: cn("shrink-0 rounded-[2px] border-[--color-border] bg-[--color-bg]", {
|
|
70
|
+
"h-2.5 w-2.5": indicator === "dot",
|
|
71
|
+
"w-1": indicator === "line",
|
|
72
|
+
"w-0 border-[1.5px] border-dashed bg-transparent": indicator === "dashed",
|
|
73
|
+
"my-0.5": nestLabel && indicator === "dashed",
|
|
74
|
+
}), style: {
|
|
75
|
+
"--color-bg": indicatorColor,
|
|
76
|
+
"--color-border": indicatorColor,
|
|
77
|
+
} }))), _jsxs("div", { className: cn("flex flex-1 justify-between leading-none", nestLabel ? "items-end" : "items-center"), children: [_jsxs("div", { className: "grid gap-1.5", children: [nestLabel ? tooltipLabel : null, _jsx("span", { className: "text-muted-foreground", children: itemConfig?.label || item.name })] }), item.value && (_jsx("span", { className: "font-mono font-medium tabular-nums text-foreground", children: item.value.toLocaleString() }))] })] })) }, item.dataKey));
|
|
78
|
+
}) })] }));
|
|
79
|
+
}
|
|
80
|
+
// Chart Context
|
|
81
|
+
const ChartContext = React.createContext(null);
|
|
82
|
+
function useChart() {
|
|
83
|
+
const context = React.useContext(ChartContext);
|
|
84
|
+
if (!context) {
|
|
85
|
+
throw new Error("useChart must be used within a <ChartContainer />");
|
|
86
|
+
}
|
|
87
|
+
return context;
|
|
88
|
+
}
|
|
89
|
+
// Utility function
|
|
90
|
+
function getPayloadConfigFromPayload(config, payload, key) {
|
|
91
|
+
if (typeof payload !== "object" || payload === null) {
|
|
92
|
+
return undefined;
|
|
93
|
+
}
|
|
94
|
+
const payloadPayload = "payload" in payload &&
|
|
95
|
+
typeof payload.payload === "object" &&
|
|
96
|
+
payload.payload !== null
|
|
97
|
+
? payload.payload
|
|
98
|
+
: undefined;
|
|
99
|
+
let configLabelKey = key;
|
|
100
|
+
if (key in payload &&
|
|
101
|
+
typeof payload[key] === "string") {
|
|
102
|
+
configLabelKey = payload[key];
|
|
103
|
+
}
|
|
104
|
+
else if (payloadPayload &&
|
|
105
|
+
key in payloadPayload &&
|
|
106
|
+
typeof payloadPayload[key] === "string") {
|
|
107
|
+
configLabelKey = payloadPayload[key];
|
|
108
|
+
}
|
|
109
|
+
return configLabelKey in config
|
|
110
|
+
? config[configLabelKey]
|
|
111
|
+
: config[key];
|
|
112
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { Button } from '../../components/ui/button.js';
|
|
3
|
+
declare function Pagination({ className, ...props }: React.ComponentProps<'nav'>): import("react/jsx-runtime").JSX.Element;
|
|
4
|
+
declare function PaginationContent({ className, ...props }: React.ComponentProps<'ul'>): import("react/jsx-runtime").JSX.Element;
|
|
5
|
+
declare function PaginationItem({ className, ...props }: React.ComponentProps<'li'>): import("react/jsx-runtime").JSX.Element;
|
|
6
|
+
type PaginationLinkProps = {
|
|
7
|
+
isActive?: boolean;
|
|
8
|
+
} & Pick<React.ComponentProps<typeof Button>, 'size'> & React.ComponentProps<'a'>;
|
|
9
|
+
declare function PaginationLink({ className, isActive, size, ...props }: PaginationLinkProps): import("react/jsx-runtime").JSX.Element;
|
|
10
|
+
declare function PaginationPrevious({ className, ...props }: React.ComponentProps<typeof PaginationLink>): import("react/jsx-runtime").JSX.Element;
|
|
11
|
+
declare function PaginationNext({ className, ...props }: React.ComponentProps<typeof PaginationLink>): import("react/jsx-runtime").JSX.Element;
|
|
12
|
+
declare function PaginationEllipsis({ className, ...props }: React.ComponentProps<'span'>): import("react/jsx-runtime").JSX.Element;
|
|
13
|
+
export { Pagination, PaginationContent, PaginationEllipsis, PaginationItem, PaginationLink, PaginationNext, PaginationPrevious, };
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { ChevronLeft, ChevronRight, MoreHorizontal } from 'lucide-react';
|
|
3
|
+
import { cn } from '../../utils/utils.js';
|
|
4
|
+
function Pagination({ className, ...props }) {
|
|
5
|
+
return (_jsx("nav", { role: "navigation", "aria-label": "pagination", "data-slot": "pagination", className: cn('mx-auto flex w-full justify-center', className), ...props }));
|
|
6
|
+
}
|
|
7
|
+
function PaginationContent({ className, ...props }) {
|
|
8
|
+
return (_jsx("ul", { "data-slot": "pagination-content", className: cn('flex flex-row items-center gap-1', className), ...props }));
|
|
9
|
+
}
|
|
10
|
+
function PaginationItem({ className, ...props }) {
|
|
11
|
+
return (_jsx("li", { "data-slot": "pagination-item", className: cn('', className), ...props }));
|
|
12
|
+
}
|
|
13
|
+
function PaginationLink({ className, isActive, size = 'icon', ...props }) {
|
|
14
|
+
return (_jsx("a", { "aria-current": isActive ? 'page' : undefined, "data-slot": "pagination-link", className: cn('inline-flex h-10 w-10 items-center justify-center gap-1 whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors hover:bg-accent hover:text-accent-foreground focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50', isActive && 'bg-accent text-accent-foreground', className), ...props }));
|
|
15
|
+
}
|
|
16
|
+
function PaginationPrevious({ className, ...props }) {
|
|
17
|
+
return (_jsxs(PaginationLink, { "aria-label": "Go to previous page", "data-slot": "pagination-previous", size: "default", className: cn('w-auto gap-1 px-2.5', className), ...props, children: [_jsx(ChevronLeft, { className: "h-4 w-4" }), _jsx("span", { children: "Previous" })] }));
|
|
18
|
+
}
|
|
19
|
+
function PaginationNext({ className, ...props }) {
|
|
20
|
+
return (_jsxs(PaginationLink, { "aria-label": "Go to next page", "data-slot": "pagination-next", size: "default", className: cn('w-auto gap-1 px-2.5', className), ...props, children: [_jsx("span", { children: "Next" }), _jsx(ChevronRight, { className: "h-4 w-4" })] }));
|
|
21
|
+
}
|
|
22
|
+
function PaginationEllipsis({ className, ...props }) {
|
|
23
|
+
return (_jsxs("span", { "aria-hidden": true, "data-slot": "pagination-ellipsis", className: cn('flex h-9 w-9 items-center justify-center', className), ...props, children: [_jsx(MoreHorizontal, { className: "h-4 w-4" }), _jsx("span", { className: "sr-only", children: "More pages" })] }));
|
|
24
|
+
}
|
|
25
|
+
export { Pagination, PaginationContent, PaginationEllipsis, PaginationItem, PaginationLink, PaginationNext, PaginationPrevious, };
|
package/components/ui/sonner.js
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import { useTheme } from 'next-themes';
|
|
3
2
|
import { Toaster as Sonner } from 'sonner';
|
|
4
3
|
const Toaster = ({ ...props }) => {
|
|
5
|
-
|
|
6
|
-
return (_jsx(Sonner, { theme: theme, className: "toaster group", style: {
|
|
4
|
+
return (_jsx(Sonner, { theme: "light", className: "toaster group", style: {
|
|
7
5
|
'--normal-bg': 'var(--popover)',
|
|
8
6
|
'--normal-text': 'var(--popover-foreground)',
|
|
9
7
|
'--normal-border': 'var(--border)',
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
declare function Table({ className, ...props }: React.HTMLAttributes<HTMLTableElement>): import("react/jsx-runtime").JSX.Element;
|
|
3
|
+
declare function TableHeader({ className, ...props }: React.HTMLAttributes<HTMLTableSectionElement>): import("react/jsx-runtime").JSX.Element;
|
|
4
|
+
declare function TableBody({ className, ...props }: React.HTMLAttributes<HTMLTableSectionElement>): import("react/jsx-runtime").JSX.Element;
|
|
5
|
+
declare function TableFooter({ className, ...props }: React.HTMLAttributes<HTMLTableSectionElement>): import("react/jsx-runtime").JSX.Element;
|
|
6
|
+
declare function TableRow({ className, ...props }: React.HTMLAttributes<HTMLTableRowElement>): import("react/jsx-runtime").JSX.Element;
|
|
7
|
+
declare function TableHead({ className, ...props }: React.ThHTMLAttributes<HTMLTableCellElement>): import("react/jsx-runtime").JSX.Element;
|
|
8
|
+
declare function TableCell({ className, ...props }: React.TdHTMLAttributes<HTMLTableCellElement>): import("react/jsx-runtime").JSX.Element;
|
|
9
|
+
declare function TableCaption({ className, ...props }: React.HTMLAttributes<HTMLTableCaptionElement>): import("react/jsx-runtime").JSX.Element;
|
|
10
|
+
export { Table, TableHeader, TableBody, TableFooter, TableRow, TableHead, TableCell, TableCaption, };
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { cn } from '../../utils/utils.js';
|
|
3
|
+
function Table({ className, ...props }) {
|
|
4
|
+
return (_jsx("div", { className: "relative w-full overflow-auto", children: _jsx("table", { "data-slot": "table", className: cn('w-full caption-bottom text-sm', className), ...props }) }));
|
|
5
|
+
}
|
|
6
|
+
function TableHeader({ className, ...props }) {
|
|
7
|
+
return (_jsx("thead", { "data-slot": "table-header", className: cn('[&_tr]:border-b', className), ...props }));
|
|
8
|
+
}
|
|
9
|
+
function TableBody({ className, ...props }) {
|
|
10
|
+
return (_jsx("tbody", { "data-slot": "table-body", className: cn('[&_tr:last-child]:border-0', className), ...props }));
|
|
11
|
+
}
|
|
12
|
+
function TableFooter({ className, ...props }) {
|
|
13
|
+
return (_jsx("tfoot", { "data-slot": "table-footer", className: cn('border-t bg-muted/50 font-medium [&>tr]:last:border-b-0', className), ...props }));
|
|
14
|
+
}
|
|
15
|
+
function TableRow({ className, ...props }) {
|
|
16
|
+
return (_jsx("tr", { "data-slot": "table-row", className: cn('border-b transition-colors hover:bg-muted/50 data-[state=selected]:bg-muted', className), ...props }));
|
|
17
|
+
}
|
|
18
|
+
function TableHead({ className, ...props }) {
|
|
19
|
+
return (_jsx("th", { "data-slot": "table-head", className: cn('h-12 px-4 text-left align-middle font-medium text-muted-foreground [&:has([role=checkbox])]:pr-0', className), ...props }));
|
|
20
|
+
}
|
|
21
|
+
function TableCell({ className, ...props }) {
|
|
22
|
+
return (_jsx("td", { "data-slot": "table-cell", className: cn('p-4 align-middle [&:has([role=checkbox])]:pr-0', className), ...props }));
|
|
23
|
+
}
|
|
24
|
+
function TableCaption({ className, ...props }) {
|
|
25
|
+
return (_jsx("caption", { "data-slot": "table-caption", className: cn('mt-4 text-sm text-muted-foreground', className), ...props }));
|
|
26
|
+
}
|
|
27
|
+
export { Table, TableHeader, TableBody, TableFooter, TableRow, TableHead, TableCell, TableCaption, };
|
package/components/ui/tooltip.js
CHANGED
|
@@ -11,6 +11,6 @@ function TooltipTrigger({ ...props }) {
|
|
|
11
11
|
return _jsx(TooltipPrimitive.Trigger, { "data-slot": "tooltip-trigger", ...props });
|
|
12
12
|
}
|
|
13
13
|
function TooltipContent({ className, sideOffset = 0, children, ...props }) {
|
|
14
|
-
return (_jsx(TooltipPrimitive.Portal, { children: _jsxs(TooltipPrimitive.Content, { "data-slot": "tooltip-content", sideOffset: sideOffset, className: cn('bg-
|
|
14
|
+
return (_jsx(TooltipPrimitive.Portal, { children: _jsxs(TooltipPrimitive.Content, { "data-slot": "tooltip-content", sideOffset: sideOffset, className: cn('bg-gray-800 text-white animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-fit origin-(--radix-tooltip-content-transform-origin) rounded-md px-3 py-1.5 text-xs text-balance shadow-md', className), ...props, children: [children, _jsx(TooltipPrimitive.Arrow, { className: "bg-gray-800 fill-gray-800 z-50 size-2.5 translate-y-[calc(-50%_-_2px)] rotate-45 rounded-[2px]" })] }) }));
|
|
15
15
|
}
|
|
16
16
|
export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@minutemailer/kit",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "Minutemailer UI Kit",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Minutemailer",
|
|
@@ -22,18 +22,19 @@
|
|
|
22
22
|
"react-dom": "^19.0.0 || ^18.0.0"
|
|
23
23
|
},
|
|
24
24
|
"devDependencies": {
|
|
25
|
-
"@storybook/addon-docs": "^10.0.
|
|
26
|
-
"@storybook/react-vite": "^10.0.
|
|
25
|
+
"@storybook/addon-docs": "^10.0.8",
|
|
26
|
+
"@storybook/react-vite": "^10.0.8",
|
|
27
27
|
"@svgr/cli": "^8.1.0",
|
|
28
28
|
"@types/react": "^19.2.0",
|
|
29
29
|
"@types/react-dom": "^19.2.0",
|
|
30
30
|
"@vitest/coverage-v8": "^3.2.4",
|
|
31
|
+
"baseline-browser-mapping": "^2.8.29",
|
|
31
32
|
"prettier": "3.6.2",
|
|
32
33
|
"react": "^19.2.0",
|
|
33
34
|
"react-dom": "^19.2.0",
|
|
34
35
|
"sass": "^1.93.2",
|
|
35
36
|
"shadcn": "^3.3.1",
|
|
36
|
-
"storybook": "^10.0.
|
|
37
|
+
"storybook": "^10.0.8",
|
|
37
38
|
"tsc-alias": "^1.8.16",
|
|
38
39
|
"typedoc": "^0.28.13",
|
|
39
40
|
"typedoc-material-theme": "^1.4.0",
|
|
@@ -42,6 +43,7 @@
|
|
|
42
43
|
},
|
|
43
44
|
"dependencies": {
|
|
44
45
|
"@hookform/resolvers": "^5.2.2",
|
|
46
|
+
"@radix-ui/react-accordion": "^1.2.12",
|
|
45
47
|
"@radix-ui/react-avatar": "^1.1.10",
|
|
46
48
|
"@radix-ui/react-checkbox": "^1.3.3",
|
|
47
49
|
"@radix-ui/react-dialog": "^1.1.15",
|
|
@@ -59,6 +61,7 @@
|
|
|
59
61
|
"next-themes": "^0.4.6",
|
|
60
62
|
"react-day-picker": "^9.11.1",
|
|
61
63
|
"react-hook-form": "^7.63.0",
|
|
64
|
+
"recharts": "^2.15.4",
|
|
62
65
|
"sonner": "^2.0.7",
|
|
63
66
|
"tailwind-merge": "^3.3.1",
|
|
64
67
|
"tailwindcss": "^4.1.14",
|