@aereamart/design-system 0.3.6 → 0.4.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/dist/cn.d.ts +2 -1
- package/dist/cn.js +4 -2
- package/dist/hooks/use-mobile.d.ts +1 -0
- package/dist/hooks/use-mobile.js +15 -0
- package/dist/index.d.ts +48 -37
- package/dist/index.js +48 -37
- package/dist/ui/accordion.d.ts +7 -0
- package/dist/ui/accordion.js +17 -0
- package/dist/ui/alert-dialog.d.ts +18 -0
- package/dist/ui/alert-dialog.js +42 -0
- package/dist/ui/alert.d.ts +9 -0
- package/dist/ui/alert.js +24 -0
- package/dist/ui/aspect-ratio.d.ts +3 -0
- package/dist/ui/aspect-ratio.js +6 -0
- package/dist/ui/avatar.d.ts +11 -0
- package/dist/ui/avatar.js +23 -0
- package/dist/ui/badge.d.ts +9 -0
- package/dist/ui/badge.js +24 -0
- package/dist/ui/breadcrumb.d.ts +11 -0
- package/dist/ui/breadcrumb.js +27 -0
- package/dist/ui/button.d.ts +10 -0
- package/dist/ui/button.js +35 -0
- package/dist/ui/calendar.d.ts +8 -0
- package/dist/ui/calendar.js +75 -0
- package/dist/ui/card.d.ts +9 -0
- package/dist/ui/card.js +24 -0
- package/dist/ui/carousel.d.ts +19 -0
- package/dist/ui/carousel.js +91 -0
- package/dist/ui/chart.d.ts +44 -0
- package/dist/ui/chart.js +132 -0
- package/dist/ui/checkbox.d.ts +4 -0
- package/dist/ui/checkbox.js +8 -0
- package/dist/ui/collapsible.d.ts +5 -0
- package/dist/ui/collapsible.js +13 -0
- package/dist/ui/command.d.ts +18 -0
- package/dist/ui/command.js +34 -0
- package/dist/ui/context-menu.d.ts +25 -0
- package/dist/ui/context-menu.js +51 -0
- package/dist/ui/dialog.d.ts +17 -0
- package/dist/ui/dialog.js +36 -0
- package/dist/ui/drawer.d.ts +13 -0
- package/dist/ui/drawer.js +35 -0
- package/dist/ui/dropdown-menu.d.ts +25 -0
- package/dist/ui/dropdown-menu.js +50 -0
- package/dist/ui/form.d.ts +24 -0
- package/dist/ui/form.js +58 -0
- package/dist/ui/hover-card.d.ts +6 -0
- package/dist/ui/hover-card.js +13 -0
- package/dist/ui/input-otp.d.ts +11 -0
- package/dist/ui/input-otp.js +21 -0
- package/dist/ui/input.d.ts +3 -0
- package/dist/ui/input.js +6 -0
- package/dist/ui/label.d.ts +4 -0
- package/dist/ui/label.js +8 -0
- package/dist/ui/menubar.d.ts +26 -0
- package/dist/ui/menubar.js +53 -0
- package/dist/ui/navigation-menu.d.ts +14 -0
- package/dist/ui/navigation-menu.js +31 -0
- package/dist/ui/pagination.d.ts +13 -0
- package/dist/ui/pagination.js +29 -0
- package/dist/ui/popover.d.ts +10 -0
- package/dist/ui/popover.js +26 -0
- package/dist/ui/progress.d.ts +4 -0
- package/dist/ui/progress.js +7 -0
- package/dist/ui/radio-group.d.ts +5 -0
- package/dist/ui/radio-group.js +12 -0
- package/dist/ui/resizable.d.ts +7 -0
- package/dist/ui/resizable.js +14 -0
- package/dist/ui/scroll-area.d.ts +5 -0
- package/dist/ui/scroll-area.js +12 -0
- package/dist/ui/select.d.ts +15 -0
- package/dist/ui/select.js +38 -0
- package/dist/ui/separator.d.ts +4 -0
- package/dist/ui/separator.js +7 -0
- package/dist/ui/sheet.d.ts +14 -0
- package/dist/ui/sheet.js +40 -0
- package/dist/ui/sidebar.d.ts +69 -0
- package/dist/ui/sidebar.js +211 -0
- package/dist/ui/skeleton.d.ts +2 -0
- package/dist/ui/skeleton.js +6 -0
- package/dist/ui/slider.d.ts +4 -0
- package/dist/ui/slider.js +13 -0
- package/dist/ui/sonner.d.ts +3 -0
- package/dist/ui/sonner.js +21 -0
- package/dist/ui/switch.d.ts +6 -0
- package/dist/ui/switch.js +7 -0
- package/dist/ui/table.d.ts +10 -0
- package/dist/ui/table.js +28 -0
- package/dist/ui/tabs.d.ts +11 -0
- package/dist/ui/tabs.js +28 -0
- package/dist/ui/textarea.d.ts +3 -0
- package/dist/ui/textarea.js +6 -0
- package/dist/ui/toggle-group.d.ts +9 -0
- package/dist/ui/toggle-group.js +22 -0
- package/dist/ui/toggle.d.ts +9 -0
- package/dist/ui/toggle.js +26 -0
- package/dist/ui/tooltip.d.ts +7 -0
- package/dist/ui/tooltip.js +16 -0
- package/package.json +28 -4
- package/src/cn.ts +5 -2
- package/src/hooks/use-mobile.ts +19 -0
- package/src/index.css +158 -0
- package/src/index.ts +48 -37
- package/src/ui/accordion.tsx +63 -0
- package/src/ui/alert-dialog.tsx +196 -0
- package/src/ui/alert.tsx +65 -0
- package/src/ui/aspect-ratio.tsx +9 -0
- package/src/ui/avatar.tsx +108 -0
- package/src/ui/badge.tsx +47 -0
- package/src/ui/breadcrumb.tsx +108 -0
- package/src/ui/button.tsx +63 -0
- package/src/ui/calendar.tsx +218 -0
- package/src/ui/card.tsx +91 -0
- package/src/ui/carousel.tsx +241 -0
- package/src/ui/chart.tsx +371 -0
- package/src/ui/checkbox.tsx +29 -0
- package/src/ui/collapsible.tsx +33 -0
- package/src/ui/command.tsx +184 -0
- package/src/ui/context-menu.tsx +251 -0
- package/src/ui/dialog.tsx +156 -0
- package/src/ui/drawer.tsx +134 -0
- package/src/ui/dropdown-menu.tsx +254 -0
- package/src/ui/form.tsx +165 -0
- package/src/ui/hover-card.tsx +41 -0
- package/src/ui/input-otp.tsx +76 -0
- package/src/ui/input.tsx +20 -0
- package/src/ui/label.tsx +23 -0
- package/src/ui/menubar.tsx +273 -0
- package/src/ui/navigation-menu.tsx +167 -0
- package/src/ui/pagination.tsx +127 -0
- package/src/ui/popover.tsx +88 -0
- package/src/ui/progress.tsx +28 -0
- package/src/ui/radio-group.tsx +44 -0
- package/src/ui/resizable.tsx +50 -0
- package/src/ui/scroll-area.tsx +55 -0
- package/src/ui/select.tsx +189 -0
- package/src/ui/separator.tsx +25 -0
- package/src/ui/sheet.tsx +142 -0
- package/src/ui/sidebar.tsx +724 -0
- package/src/ui/skeleton.tsx +13 -0
- package/src/ui/slider.tsx +60 -0
- package/src/ui/sonner.tsx +40 -0
- package/src/ui/switch.tsx +32 -0
- package/src/ui/table.tsx +115 -0
- package/src/ui/tabs.tsx +88 -0
- package/src/ui/textarea.tsx +17 -0
- package/src/ui/toggle-group.tsx +83 -0
- package/src/ui/toggle.tsx +46 -0
- package/src/ui/tooltip.tsx +54 -0
- package/dist/Accordion.d.ts +0 -10
- package/dist/Accordion.js +0 -12
- package/dist/AlertBanner.d.ts +0 -9
- package/dist/AlertBanner.js +0 -21
- package/dist/AppBar.d.ts +0 -7
- package/dist/AppBar.js +0 -5
- package/dist/Avatar.d.ts +0 -6
- package/dist/Avatar.js +0 -20
- package/dist/Badge.d.ts +0 -6
- package/dist/Badge.js +0 -11
- package/dist/BottomSheet.d.ts +0 -8
- package/dist/BottomSheet.js +0 -24
- package/dist/Breadcrumb.d.ts +0 -8
- package/dist/Breadcrumb.js +0 -9
- package/dist/Button.d.ts +0 -9
- package/dist/Button.js +0 -18
- package/dist/Card.d.ts +0 -7
- package/dist/Card.js +0 -6
- package/dist/Checkbox.d.ts +0 -5
- package/dist/Checkbox.js +0 -5
- package/dist/Chip.d.ts +0 -7
- package/dist/Chip.js +0 -15
- package/dist/DataTable.d.ts +0 -15
- package/dist/DataTable.js +0 -7
- package/dist/DatePicker.d.ts +0 -5
- package/dist/DatePicker.js +0 -11
- package/dist/Divider.d.ts +0 -5
- package/dist/Divider.js +0 -11
- package/dist/Drawer.d.ts +0 -10
- package/dist/Drawer.js +0 -24
- package/dist/EmptyState.d.ts +0 -7
- package/dist/EmptyState.js +0 -6
- package/dist/Input.d.ts +0 -5
- package/dist/Input.js +0 -7
- package/dist/Link.d.ts +0 -4
- package/dist/Link.js +0 -6
- package/dist/Menu.d.ts +0 -14
- package/dist/Menu.js +0 -29
- package/dist/Modal.d.ts +0 -9
- package/dist/Modal.js +0 -24
- package/dist/NavigationBar.d.ts +0 -14
- package/dist/NavigationBar.js +0 -22
- package/dist/Pagination.d.ts +0 -7
- package/dist/Pagination.js +0 -24
- package/dist/Popover.d.ts +0 -7
- package/dist/Popover.js +0 -9
- package/dist/Progress.d.ts +0 -8
- package/dist/Progress.js +0 -15
- package/dist/Radio.d.ts +0 -15
- package/dist/Radio.js +0 -8
- package/dist/SegmentedControl.d.ts +0 -13
- package/dist/SegmentedControl.js +0 -18
- package/dist/Select.d.ts +0 -5
- package/dist/Select.js +0 -7
- package/dist/Sidebar.d.ts +0 -16
- package/dist/Sidebar.js +0 -19
- package/dist/Slider.d.ts +0 -5
- package/dist/Slider.js +0 -5
- package/dist/Snackbar.d.ts +0 -12
- package/dist/Snackbar.js +0 -20
- package/dist/Spinner.d.ts +0 -3
- package/dist/Spinner.js +0 -5
- package/dist/Switch.d.ts +0 -4
- package/dist/Switch.js +0 -5
- package/dist/Tabs.d.ts +0 -14
- package/dist/Tabs.js +0 -24
- package/dist/Tag.d.ts +0 -6
- package/dist/Tag.js +0 -11
- package/dist/Textarea.d.ts +0 -5
- package/dist/Textarea.js +0 -7
- package/dist/Tooltip.d.ts +0 -7
- package/dist/Tooltip.js +0 -5
- package/dist/TreeView.d.ts +0 -12
- package/dist/TreeView.js +0 -34
- package/src/Accordion.tsx +0 -58
- package/src/AlertBanner.tsx +0 -62
- package/src/AppBar.tsx +0 -29
- package/src/Avatar.tsx +0 -42
- package/src/Badge.tsx +0 -27
- package/src/BottomSheet.tsx +0 -60
- package/src/Breadcrumb.tsx +0 -48
- package/src/Button.tsx +0 -54
- package/src/Card.tsx +0 -39
- package/src/Checkbox.tsx +0 -31
- package/src/Chip.tsx +0 -44
- package/src/DataTable.tsx +0 -64
- package/src/DatePicker.tsx +0 -44
- package/src/Divider.tsx +0 -33
- package/src/Drawer.tsx +0 -71
- package/src/EmptyState.tsx +0 -37
- package/src/Input.tsx +0 -30
- package/src/Link.tsx +0 -22
- package/src/Menu.tsx +0 -73
- package/src/Modal.tsx +0 -62
- package/src/NavigationBar.tsx +0 -68
- package/src/Pagination.tsx +0 -69
- package/src/Popover.tsx +0 -34
- package/src/Progress.tsx +0 -53
- package/src/Radio.tsx +0 -49
- package/src/SegmentedControl.tsx +0 -63
- package/src/Select.tsx +0 -32
- package/src/Sidebar.tsx +0 -73
- package/src/Slider.tsx +0 -33
- package/src/Snackbar.tsx +0 -72
- package/src/Spinner.tsx +0 -19
- package/src/Switch.tsx +0 -40
- package/src/Tabs.tsx +0 -77
- package/src/Tag.tsx +0 -29
- package/src/Textarea.tsx +0 -30
- package/src/Tooltip.tsx +0 -25
- package/src/TreeView.tsx +0 -105
package/src/Card.tsx
DELETED
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
import { cn } from './cn';
|
|
2
|
-
|
|
3
|
-
export interface CardProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
4
|
-
title?: string;
|
|
5
|
-
description?: string;
|
|
6
|
-
footer?: React.ReactNode;
|
|
7
|
-
hoverable?: boolean;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
export function Card({
|
|
11
|
-
title,
|
|
12
|
-
description,
|
|
13
|
-
footer,
|
|
14
|
-
hoverable = false,
|
|
15
|
-
className,
|
|
16
|
-
children,
|
|
17
|
-
...props
|
|
18
|
-
}: CardProps) {
|
|
19
|
-
return (
|
|
20
|
-
<div
|
|
21
|
-
className={cn(
|
|
22
|
-
'rounded-xl border border-slate-200 bg-white',
|
|
23
|
-
hoverable &&
|
|
24
|
-
'transition-all hover:-translate-y-0.5 hover:border-lime-300 hover:shadow-md',
|
|
25
|
-
className
|
|
26
|
-
)}
|
|
27
|
-
{...props}
|
|
28
|
-
>
|
|
29
|
-
{(title || description) && (
|
|
30
|
-
<div className="border-b border-slate-100 px-5 py-4">
|
|
31
|
-
{title && <h3 className="text-base font-bold text-slate-900">{title}</h3>}
|
|
32
|
-
{description && <p className="mt-0.5 text-sm text-slate-500">{description}</p>}
|
|
33
|
-
</div>
|
|
34
|
-
)}
|
|
35
|
-
<div className="px-5 py-4">{children}</div>
|
|
36
|
-
{footer && <div className="border-t border-slate-100 px-5 py-3">{footer}</div>}
|
|
37
|
-
</div>
|
|
38
|
-
);
|
|
39
|
-
}
|
package/src/Checkbox.tsx
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import { cn } from './cn';
|
|
2
|
-
|
|
3
|
-
export interface CheckboxProps
|
|
4
|
-
extends Omit<React.InputHTMLAttributes<HTMLInputElement>, 'type'> {
|
|
5
|
-
label?: string;
|
|
6
|
-
error?: string;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
export function Checkbox({ label, error, id, className, ...props }: CheckboxProps) {
|
|
10
|
-
return (
|
|
11
|
-
<div className="grid gap-1.5">
|
|
12
|
-
<label
|
|
13
|
-
htmlFor={id}
|
|
14
|
-
className={cn('inline-flex cursor-pointer items-start gap-2.5 text-sm', className)}
|
|
15
|
-
>
|
|
16
|
-
<input
|
|
17
|
-
id={id}
|
|
18
|
-
type="checkbox"
|
|
19
|
-
className="mt-0.5 h-4 w-4 shrink-0 cursor-pointer rounded border-slate-500 text-lime-600 accent-lime-600 focus:outline-none focus:ring-2 focus:ring-lime-500/40"
|
|
20
|
-
{...props}
|
|
21
|
-
/>
|
|
22
|
-
{label && (
|
|
23
|
-
<span className={cn('font-medium', error ? 'text-red-700' : 'text-slate-700')}>
|
|
24
|
-
{label}
|
|
25
|
-
</span>
|
|
26
|
-
)}
|
|
27
|
-
</label>
|
|
28
|
-
{error && <p className="pl-6.5 text-xs text-red-600">{error}</p>}
|
|
29
|
-
</div>
|
|
30
|
-
);
|
|
31
|
-
}
|
package/src/Chip.tsx
DELETED
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import { X } from 'lucide-react';
|
|
2
|
-
import { cn } from './cn';
|
|
3
|
-
|
|
4
|
-
type ChipVariant = 'primary' | 'accent' | 'neutral' | 'danger';
|
|
5
|
-
|
|
6
|
-
export interface ChipProps extends React.HTMLAttributes<HTMLSpanElement> {
|
|
7
|
-
variant?: ChipVariant;
|
|
8
|
-
onRemove?: () => void;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
const variantClasses: Record<ChipVariant, string> = {
|
|
12
|
-
primary: 'border-lime-300 bg-lime-50 text-lime-800',
|
|
13
|
-
accent: 'border-purple-300 bg-purple-50 text-purple-900',
|
|
14
|
-
neutral: 'border-slate-300 bg-slate-50 text-slate-700',
|
|
15
|
-
danger: 'border-red-300 bg-red-50 text-red-800',
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
export function Chip({ variant = 'neutral', onRemove, className, children, ...props }: ChipProps) {
|
|
19
|
-
return (
|
|
20
|
-
<span
|
|
21
|
-
className={cn(
|
|
22
|
-
'inline-flex items-center gap-1.5 rounded-full border px-3 py-1 text-sm font-medium',
|
|
23
|
-
variantClasses[variant],
|
|
24
|
-
className
|
|
25
|
-
)}
|
|
26
|
-
{...props}
|
|
27
|
-
>
|
|
28
|
-
{children}
|
|
29
|
-
{onRemove && (
|
|
30
|
-
<button
|
|
31
|
-
type="button"
|
|
32
|
-
onClick={e => {
|
|
33
|
-
e.stopPropagation();
|
|
34
|
-
onRemove();
|
|
35
|
-
}}
|
|
36
|
-
aria-label={`Remove ${typeof children === 'string' ? children : 'chip'}`}
|
|
37
|
-
className="-mr-1 rounded-full p-0.5 hover:bg-black/10 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-current"
|
|
38
|
-
>
|
|
39
|
-
<X aria-hidden="true" className="h-3 w-3" />
|
|
40
|
-
</button>
|
|
41
|
-
)}
|
|
42
|
-
</span>
|
|
43
|
-
);
|
|
44
|
-
}
|
package/src/DataTable.tsx
DELETED
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
import { useId } from 'react';
|
|
2
|
-
import { cn } from './cn';
|
|
3
|
-
|
|
4
|
-
export interface Column<T> {
|
|
5
|
-
key: string;
|
|
6
|
-
label: string;
|
|
7
|
-
render?: (row: T) => React.ReactNode;
|
|
8
|
-
className?: string;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export interface DataTableProps<T> {
|
|
12
|
-
columns: Column<T>[];
|
|
13
|
-
rows: T[];
|
|
14
|
-
rowKey: (row: T) => string;
|
|
15
|
-
caption?: string;
|
|
16
|
-
emptyText?: string;
|
|
17
|
-
className?: string;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export function DataTable<T>({
|
|
21
|
-
columns,
|
|
22
|
-
rows,
|
|
23
|
-
rowKey,
|
|
24
|
-
caption,
|
|
25
|
-
emptyText = 'No rows to show.',
|
|
26
|
-
className,
|
|
27
|
-
}: DataTableProps<T>) {
|
|
28
|
-
const captionId = useId();
|
|
29
|
-
|
|
30
|
-
return (
|
|
31
|
-
<div className={cn('overflow-x-auto rounded-xl border border-slate-200 bg-white', className)}>
|
|
32
|
-
<table className="w-full min-w-[480px] border-collapse text-left text-sm">
|
|
33
|
-
{caption && <caption className="sr-only" id={captionId}>{caption}</caption>}
|
|
34
|
-
<thead>
|
|
35
|
-
<tr className="border-b border-slate-200 bg-slate-50 text-xs uppercase tracking-wider text-slate-500">
|
|
36
|
-
{columns.map(col => (
|
|
37
|
-
<th key={col.key} scope="col" className="px-4 py-3 font-semibold">
|
|
38
|
-
{col.label}
|
|
39
|
-
</th>
|
|
40
|
-
))}
|
|
41
|
-
</tr>
|
|
42
|
-
</thead>
|
|
43
|
-
<tbody className="divide-y divide-slate-100">
|
|
44
|
-
{rows.map(row => (
|
|
45
|
-
<tr key={rowKey(row)} className="transition-colors hover:bg-slate-50">
|
|
46
|
-
{columns.map(col => (
|
|
47
|
-
<td key={col.key} className={cn('px-4 py-3 align-middle text-slate-700', col.className)}>
|
|
48
|
-
{col.render ? col.render(row) : String((row as Record<string, unknown>)[col.key] ?? '')}
|
|
49
|
-
</td>
|
|
50
|
-
))}
|
|
51
|
-
</tr>
|
|
52
|
-
))}
|
|
53
|
-
{rows.length === 0 && (
|
|
54
|
-
<tr>
|
|
55
|
-
<td colSpan={columns.length} className="px-4 py-10 text-center text-sm text-slate-400">
|
|
56
|
-
{emptyText}
|
|
57
|
-
</td>
|
|
58
|
-
</tr>
|
|
59
|
-
)}
|
|
60
|
-
</tbody>
|
|
61
|
-
</table>
|
|
62
|
-
</div>
|
|
63
|
-
);
|
|
64
|
-
}
|
package/src/DatePicker.tsx
DELETED
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import { useId } from 'react';
|
|
2
|
-
import { CalendarDays } from 'lucide-react';
|
|
3
|
-
import { cn } from './cn';
|
|
4
|
-
|
|
5
|
-
export interface DatePickerProps
|
|
6
|
-
extends Omit<React.InputHTMLAttributes<HTMLInputElement>, 'type'> {
|
|
7
|
-
label?: string;
|
|
8
|
-
error?: string;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export function DatePicker({ label, error, id, className, ...props }: DatePickerProps) {
|
|
12
|
-
const autoId = useId();
|
|
13
|
-
const inputId = id ?? autoId;
|
|
14
|
-
|
|
15
|
-
return (
|
|
16
|
-
<div>
|
|
17
|
-
{label && (
|
|
18
|
-
<label htmlFor={inputId} className="mb-1 block text-sm font-medium text-slate-700">
|
|
19
|
-
{label}
|
|
20
|
-
</label>
|
|
21
|
-
)}
|
|
22
|
-
<div className="relative">
|
|
23
|
-
<CalendarDays
|
|
24
|
-
aria-hidden="true"
|
|
25
|
-
className="pointer-events-none absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-slate-400"
|
|
26
|
-
/>
|
|
27
|
-
<input
|
|
28
|
-
id={inputId}
|
|
29
|
-
type="date"
|
|
30
|
-
className={cn(
|
|
31
|
-
'w-full rounded-lg border bg-white py-2.5 pl-9 pr-4 text-sm text-slate-700 shadow-xs',
|
|
32
|
-
'focus:outline-none focus:ring-2 focus:ring-lime-500/40 disabled:cursor-not-allowed disabled:bg-slate-100 disabled:text-slate-400',
|
|
33
|
-
error
|
|
34
|
-
? 'border-red-400 focus:border-red-500'
|
|
35
|
-
: 'border-slate-300 hover:border-slate-400',
|
|
36
|
-
className
|
|
37
|
-
)}
|
|
38
|
-
{...props}
|
|
39
|
-
/>
|
|
40
|
-
</div>
|
|
41
|
-
{error && <p className="mt-1 text-xs text-red-600">{error}</p>}
|
|
42
|
-
</div>
|
|
43
|
-
);
|
|
44
|
-
}
|
package/src/Divider.tsx
DELETED
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
import { cn } from './cn';
|
|
2
|
-
|
|
3
|
-
export interface DividerProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
4
|
-
orientation?: 'horizontal' | 'vertical';
|
|
5
|
-
label?: string;
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
export function Divider({ orientation = 'horizontal', label, className, ...props }: DividerProps) {
|
|
9
|
-
if (orientation === 'vertical') {
|
|
10
|
-
return (
|
|
11
|
-
<div
|
|
12
|
-
role="separator"
|
|
13
|
-
aria-orientation="vertical"
|
|
14
|
-
className={cn('mx-3 my-0 h-auto w-px shrink-0 self-stretch bg-slate-200', className)}
|
|
15
|
-
{...props}
|
|
16
|
-
/>
|
|
17
|
-
);
|
|
18
|
-
}
|
|
19
|
-
if (label) {
|
|
20
|
-
return (
|
|
21
|
-
<div className={cn('flex items-center gap-3', className)} {...props}>
|
|
22
|
-
<div className="h-px flex-1 bg-slate-200" />
|
|
23
|
-
<span className="text-xs font-medium uppercase tracking-wider text-slate-400">
|
|
24
|
-
{label}
|
|
25
|
-
</span>
|
|
26
|
-
<div className="h-px flex-1 bg-slate-200" />
|
|
27
|
-
</div>
|
|
28
|
-
);
|
|
29
|
-
}
|
|
30
|
-
return (
|
|
31
|
-
<div role="separator" className={cn('h-px w-full bg-slate-200', className)} {...props} />
|
|
32
|
-
);
|
|
33
|
-
}
|
package/src/Drawer.tsx
DELETED
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
import { useEffect } from 'react';
|
|
2
|
-
import { createPortal } from 'react-dom';
|
|
3
|
-
import { X } from 'lucide-react';
|
|
4
|
-
import { cn } from './cn';
|
|
5
|
-
|
|
6
|
-
export interface DrawerProps {
|
|
7
|
-
open: boolean;
|
|
8
|
-
onClose: () => void;
|
|
9
|
-
title?: string;
|
|
10
|
-
side?: 'right' | 'left';
|
|
11
|
-
children?: React.ReactNode;
|
|
12
|
-
footer?: React.ReactNode;
|
|
13
|
-
className?: string;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export function Drawer({
|
|
17
|
-
open,
|
|
18
|
-
onClose,
|
|
19
|
-
title,
|
|
20
|
-
side = 'right',
|
|
21
|
-
children,
|
|
22
|
-
footer,
|
|
23
|
-
className,
|
|
24
|
-
}: DrawerProps) {
|
|
25
|
-
useEffect(() => {
|
|
26
|
-
if (!open) return;
|
|
27
|
-
const onKey = (e: KeyboardEvent) => {
|
|
28
|
-
if (e.key === 'Escape') onClose();
|
|
29
|
-
};
|
|
30
|
-
document.body.style.overflow = 'hidden';
|
|
31
|
-
document.addEventListener('keydown', onKey);
|
|
32
|
-
return () => {
|
|
33
|
-
document.body.style.overflow = '';
|
|
34
|
-
document.removeEventListener('keydown', onKey);
|
|
35
|
-
};
|
|
36
|
-
}, [open, onClose]);
|
|
37
|
-
|
|
38
|
-
if (!open) return null;
|
|
39
|
-
|
|
40
|
-
return createPortal(
|
|
41
|
-
<div className="fixed inset-0 z-50">
|
|
42
|
-
<div className="absolute inset-0 bg-black/50" onClick={onClose} aria-hidden="true" />
|
|
43
|
-
<section
|
|
44
|
-
role="dialog"
|
|
45
|
-
aria-modal="true"
|
|
46
|
-
aria-label={title}
|
|
47
|
-
className={cn(
|
|
48
|
-
'absolute inset-y-0 flex w-full max-w-sm flex-col bg-white shadow-xl',
|
|
49
|
-
side === 'right' ? 'right-0' : 'left-0',
|
|
50
|
-
className
|
|
51
|
-
)}
|
|
52
|
-
>
|
|
53
|
-
{title && (
|
|
54
|
-
<div className="flex items-center justify-between border-b border-slate-100 px-6 py-4">
|
|
55
|
-
<h2 className="text-lg font-bold text-slate-900">{title}</h2>
|
|
56
|
-
<button
|
|
57
|
-
onClick={onClose}
|
|
58
|
-
className="rounded-lg p-1 text-slate-400 hover:bg-slate-100"
|
|
59
|
-
aria-label="Close"
|
|
60
|
-
>
|
|
61
|
-
<X className="h-5 w-5" />
|
|
62
|
-
</button>
|
|
63
|
-
</div>
|
|
64
|
-
)}
|
|
65
|
-
<div className="flex-1 overflow-y-auto px-6 py-4">{children}</div>
|
|
66
|
-
{footer && <div className="border-t border-slate-100 px-6 py-4">{footer}</div>}
|
|
67
|
-
</section>
|
|
68
|
-
</div>,
|
|
69
|
-
document.body
|
|
70
|
-
);
|
|
71
|
-
}
|
package/src/EmptyState.tsx
DELETED
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
import { Inbox } from 'lucide-react';
|
|
2
|
-
import { cn } from './cn';
|
|
3
|
-
|
|
4
|
-
export interface EmptyStateProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
5
|
-
title: string;
|
|
6
|
-
description?: string;
|
|
7
|
-
icon?: React.ReactNode;
|
|
8
|
-
action?: React.ReactNode;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export function EmptyState({
|
|
12
|
-
title,
|
|
13
|
-
description,
|
|
14
|
-
icon,
|
|
15
|
-
action,
|
|
16
|
-
className,
|
|
17
|
-
...props
|
|
18
|
-
}: EmptyStateProps) {
|
|
19
|
-
return (
|
|
20
|
-
<div
|
|
21
|
-
className={cn(
|
|
22
|
-
'flex flex-col items-center justify-center rounded-xl border-2 border-dashed border-slate-200 bg-white px-8 py-12 text-center',
|
|
23
|
-
className
|
|
24
|
-
)}
|
|
25
|
-
{...props}
|
|
26
|
-
>
|
|
27
|
-
<div className="mb-3 rounded-full bg-lime-50 p-3 text-lime-600">
|
|
28
|
-
{icon ?? <Inbox aria-hidden="true" className="h-8 w-8" />}
|
|
29
|
-
</div>
|
|
30
|
-
<h3 className="text-base font-bold text-slate-900">{title}</h3>
|
|
31
|
-
{description && (
|
|
32
|
-
<p className="mt-1 max-w-sm text-sm text-slate-500">{description}</p>
|
|
33
|
-
)}
|
|
34
|
-
{action && <div className="mt-5">{action}</div>}
|
|
35
|
-
</div>
|
|
36
|
-
);
|
|
37
|
-
}
|
package/src/Input.tsx
DELETED
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import { cn } from './cn';
|
|
2
|
-
|
|
3
|
-
export interface InputProps extends React.InputHTMLAttributes<HTMLInputElement> {
|
|
4
|
-
label?: string;
|
|
5
|
-
error?: string;
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
export function Input({ label, error, className, id, ...props }: InputProps) {
|
|
9
|
-
return (
|
|
10
|
-
<div className="w-full">
|
|
11
|
-
{label && (
|
|
12
|
-
<label htmlFor={id} className="mb-1 block text-sm font-medium text-slate-700">
|
|
13
|
-
{label}
|
|
14
|
-
</label>
|
|
15
|
-
)}
|
|
16
|
-
<input
|
|
17
|
-
id={id}
|
|
18
|
-
className={cn(
|
|
19
|
-
'w-full rounded-lg border bg-white px-4 py-2.5 text-sm text-slate-900 placeholder:text-slate-500 focus:outline-none focus:ring-2',
|
|
20
|
-
error
|
|
21
|
-
? 'border-red-600 focus:border-red-600 focus:ring-red-500/30'
|
|
22
|
-
: 'border-slate-500 focus:border-lime-600 focus:ring-lime-500/30',
|
|
23
|
-
className
|
|
24
|
-
)}
|
|
25
|
-
{...props}
|
|
26
|
-
/>
|
|
27
|
-
{error && <p className="mt-1 text-xs text-red-600">{error}</p>}
|
|
28
|
-
</div>
|
|
29
|
-
);
|
|
30
|
-
}
|
package/src/Link.tsx
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import { ExternalLink } from 'lucide-react';
|
|
2
|
-
import { cn } from './cn';
|
|
3
|
-
|
|
4
|
-
export interface LinkProps extends React.AnchorHTMLAttributes<HTMLAnchorElement> {
|
|
5
|
-
external?: boolean;
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
export function Link({ external = false, className, children, ...props }: LinkProps) {
|
|
9
|
-
return (
|
|
10
|
-
<a
|
|
11
|
-
className={cn(
|
|
12
|
-
'inline-flex items-center gap-1 font-medium text-lime-700 hover:text-lime-800 hover:underline focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-lime-500/40 rounded-sm',
|
|
13
|
-
className
|
|
14
|
-
)}
|
|
15
|
-
{...(external ? { target: '_blank', rel: 'noreferrer' } : {})}
|
|
16
|
-
{...props}
|
|
17
|
-
>
|
|
18
|
-
{children}
|
|
19
|
-
{external && <ExternalLink aria-hidden="true" className="h-3.5 w-3.5" />}
|
|
20
|
-
</a>
|
|
21
|
-
);
|
|
22
|
-
}
|
package/src/Menu.tsx
DELETED
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
import { useEffect, useRef, useState } from 'react';
|
|
2
|
-
import { cn } from './cn';
|
|
3
|
-
|
|
4
|
-
export interface MenuItem {
|
|
5
|
-
label: string;
|
|
6
|
-
onSelect?: () => void;
|
|
7
|
-
danger?: boolean;
|
|
8
|
-
disabled?: boolean;
|
|
9
|
-
icon?: React.ReactNode;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export interface MenuProps {
|
|
13
|
-
trigger: React.ReactNode;
|
|
14
|
-
items: MenuItem[];
|
|
15
|
-
align?: 'start' | 'end';
|
|
16
|
-
className?: string;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export function Menu({ trigger, items, align = 'end', className }: MenuProps) {
|
|
20
|
-
const [open, setOpen] = useState(false);
|
|
21
|
-
const ref = useRef<HTMLDivElement>(null);
|
|
22
|
-
|
|
23
|
-
useEffect(() => {
|
|
24
|
-
if (!open) return;
|
|
25
|
-
const onDown = (e: MouseEvent) => {
|
|
26
|
-
if (ref.current && !ref.current.contains(e.target as Node)) setOpen(false);
|
|
27
|
-
};
|
|
28
|
-
const onKey = (e: KeyboardEvent) => {
|
|
29
|
-
if (e.key === 'Escape') setOpen(false);
|
|
30
|
-
};
|
|
31
|
-
document.addEventListener('mousedown', onDown);
|
|
32
|
-
document.addEventListener('keydown', onKey);
|
|
33
|
-
return () => {
|
|
34
|
-
document.removeEventListener('mousedown', onDown);
|
|
35
|
-
document.removeEventListener('keydown', onKey);
|
|
36
|
-
};
|
|
37
|
-
}, [open]);
|
|
38
|
-
|
|
39
|
-
return (
|
|
40
|
-
<div ref={ref} className={cn('relative inline-flex', className)}>
|
|
41
|
-
<div onClick={() => setOpen(v => !v)}>{trigger}</div>
|
|
42
|
-
{open && (
|
|
43
|
-
<div
|
|
44
|
-
role="menu"
|
|
45
|
-
className={cn(
|
|
46
|
-
'absolute z-50 mt-1.5 min-w-40 overflow-hidden rounded-lg border border-slate-200 bg-white py-1 shadow-lg',
|
|
47
|
-
align === 'end' ? 'right-0' : 'left-0'
|
|
48
|
-
)}
|
|
49
|
-
>
|
|
50
|
-
{items.map((item, i) => (
|
|
51
|
-
<button
|
|
52
|
-
key={`${item.label}-${i}`}
|
|
53
|
-
type="button"
|
|
54
|
-
role="menuitem"
|
|
55
|
-
disabled={item.disabled}
|
|
56
|
-
onClick={() => {
|
|
57
|
-
setOpen(false);
|
|
58
|
-
item.onSelect?.();
|
|
59
|
-
}}
|
|
60
|
-
className={cn(
|
|
61
|
-
'flex w-full items-center gap-2.5 px-4 py-2 text-left text-sm transition-colors disabled:opacity-50 focus-visible:outline-none focus-visible:bg-slate-100',
|
|
62
|
-
item.danger ? 'text-red-700 hover:bg-red-50' : 'text-slate-700 hover:bg-slate-50'
|
|
63
|
-
)}
|
|
64
|
-
>
|
|
65
|
-
{item.icon}
|
|
66
|
-
<span className="flex-1">{item.label}</span>
|
|
67
|
-
</button>
|
|
68
|
-
))}
|
|
69
|
-
</div>
|
|
70
|
-
)}
|
|
71
|
-
</div>
|
|
72
|
-
);
|
|
73
|
-
}
|
package/src/Modal.tsx
DELETED
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
import { useEffect } from 'react';
|
|
2
|
-
import { createPortal } from 'react-dom';
|
|
3
|
-
import { X } from 'lucide-react';
|
|
4
|
-
import { cn } from './cn';
|
|
5
|
-
|
|
6
|
-
export interface ModalProps {
|
|
7
|
-
open: boolean;
|
|
8
|
-
onClose: () => void;
|
|
9
|
-
title?: string;
|
|
10
|
-
children?: React.ReactNode;
|
|
11
|
-
className?: string;
|
|
12
|
-
maxWidth?: string;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export function Modal({
|
|
16
|
-
open,
|
|
17
|
-
onClose,
|
|
18
|
-
title,
|
|
19
|
-
children,
|
|
20
|
-
className,
|
|
21
|
-
maxWidth = 'max-w-md',
|
|
22
|
-
}: ModalProps) {
|
|
23
|
-
useEffect(() => {
|
|
24
|
-
if (!open) return;
|
|
25
|
-
const onKey = (e: KeyboardEvent) => {
|
|
26
|
-
if (e.key === 'Escape') onClose();
|
|
27
|
-
};
|
|
28
|
-
document.body.style.overflow = 'hidden';
|
|
29
|
-
document.addEventListener('keydown', onKey);
|
|
30
|
-
return () => {
|
|
31
|
-
document.body.style.overflow = '';
|
|
32
|
-
document.removeEventListener('keydown', onKey);
|
|
33
|
-
};
|
|
34
|
-
}, [open, onClose]);
|
|
35
|
-
|
|
36
|
-
if (!open) return null;
|
|
37
|
-
|
|
38
|
-
return createPortal(
|
|
39
|
-
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/50 p-4">
|
|
40
|
-
<div
|
|
41
|
-
className={cn('w-full rounded-2xl bg-white shadow-xl', maxWidth, className)}
|
|
42
|
-
role="dialog"
|
|
43
|
-
aria-modal="true"
|
|
44
|
-
>
|
|
45
|
-
{title && (
|
|
46
|
-
<div className="flex items-center justify-between border-b border-slate-100 px-6 py-4">
|
|
47
|
-
<h2 className="text-lg font-bold text-slate-900">{title}</h2>
|
|
48
|
-
<button
|
|
49
|
-
onClick={onClose}
|
|
50
|
-
className="rounded-lg p-1 text-slate-400 hover:bg-slate-100"
|
|
51
|
-
aria-label="Close"
|
|
52
|
-
>
|
|
53
|
-
<X className="h-5 w-5" />
|
|
54
|
-
</button>
|
|
55
|
-
</div>
|
|
56
|
-
)}
|
|
57
|
-
{children}
|
|
58
|
-
</div>
|
|
59
|
-
</div>,
|
|
60
|
-
document.body
|
|
61
|
-
);
|
|
62
|
-
}
|
package/src/NavigationBar.tsx
DELETED
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
import { useState } from 'react';
|
|
2
|
-
import { cn } from './cn';
|
|
3
|
-
|
|
4
|
-
export interface NavItem {
|
|
5
|
-
value: string;
|
|
6
|
-
label: string;
|
|
7
|
-
disabled?: boolean;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
export interface NavigationBarProps {
|
|
11
|
-
items: NavItem[];
|
|
12
|
-
value?: string;
|
|
13
|
-
defaultValue?: string;
|
|
14
|
-
onChange?: (value: string) => void;
|
|
15
|
-
variant?: 'pills' | 'links';
|
|
16
|
-
className?: string;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export function NavigationBar({
|
|
20
|
-
items,
|
|
21
|
-
value,
|
|
22
|
-
defaultValue,
|
|
23
|
-
onChange,
|
|
24
|
-
variant = 'links',
|
|
25
|
-
className,
|
|
26
|
-
}: NavigationBarProps) {
|
|
27
|
-
const isControlled = value !== undefined;
|
|
28
|
-
const [inner, setInner] = useState<string | undefined>(defaultValue);
|
|
29
|
-
const active = isControlled ? value : inner;
|
|
30
|
-
|
|
31
|
-
return (
|
|
32
|
-
<nav aria-label="Primary" className={cn('flex items-center gap-1', className)}>
|
|
33
|
-
{items.map(item => {
|
|
34
|
-
const selected = active === item.value;
|
|
35
|
-
return (
|
|
36
|
-
<button
|
|
37
|
-
key={item.value}
|
|
38
|
-
type="button"
|
|
39
|
-
aria-current={selected ? 'page' : undefined}
|
|
40
|
-
disabled={item.disabled}
|
|
41
|
-
onClick={() => {
|
|
42
|
-
if (!isControlled) setInner(item.value);
|
|
43
|
-
onChange?.(item.value);
|
|
44
|
-
}}
|
|
45
|
-
className={cn(
|
|
46
|
-
'px-4 py-2 text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-lime-500/40 disabled:opacity-50',
|
|
47
|
-
variant === 'pills'
|
|
48
|
-
? cn(
|
|
49
|
-
'rounded-lg',
|
|
50
|
-
selected
|
|
51
|
-
? 'bg-lime-600 text-white'
|
|
52
|
-
: 'text-slate-600 hover:bg-slate-100 hover:text-slate-900'
|
|
53
|
-
)
|
|
54
|
-
: cn(
|
|
55
|
-
'border-b-2',
|
|
56
|
-
selected
|
|
57
|
-
? 'border-lime-600 text-lime-700'
|
|
58
|
-
: 'border-transparent text-slate-500 hover:text-slate-900'
|
|
59
|
-
)
|
|
60
|
-
)}
|
|
61
|
-
>
|
|
62
|
-
{item.label}
|
|
63
|
-
</button>
|
|
64
|
-
);
|
|
65
|
-
})}
|
|
66
|
-
</nav>
|
|
67
|
-
);
|
|
68
|
-
}
|