@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.
Files changed (260) hide show
  1. package/dist/cn.d.ts +2 -1
  2. package/dist/cn.js +4 -2
  3. package/dist/hooks/use-mobile.d.ts +1 -0
  4. package/dist/hooks/use-mobile.js +15 -0
  5. package/dist/index.d.ts +48 -37
  6. package/dist/index.js +48 -37
  7. package/dist/ui/accordion.d.ts +7 -0
  8. package/dist/ui/accordion.js +17 -0
  9. package/dist/ui/alert-dialog.d.ts +18 -0
  10. package/dist/ui/alert-dialog.js +42 -0
  11. package/dist/ui/alert.d.ts +9 -0
  12. package/dist/ui/alert.js +24 -0
  13. package/dist/ui/aspect-ratio.d.ts +3 -0
  14. package/dist/ui/aspect-ratio.js +6 -0
  15. package/dist/ui/avatar.d.ts +11 -0
  16. package/dist/ui/avatar.js +23 -0
  17. package/dist/ui/badge.d.ts +9 -0
  18. package/dist/ui/badge.js +24 -0
  19. package/dist/ui/breadcrumb.d.ts +11 -0
  20. package/dist/ui/breadcrumb.js +27 -0
  21. package/dist/ui/button.d.ts +10 -0
  22. package/dist/ui/button.js +35 -0
  23. package/dist/ui/calendar.d.ts +8 -0
  24. package/dist/ui/calendar.js +75 -0
  25. package/dist/ui/card.d.ts +9 -0
  26. package/dist/ui/card.js +24 -0
  27. package/dist/ui/carousel.d.ts +19 -0
  28. package/dist/ui/carousel.js +91 -0
  29. package/dist/ui/chart.d.ts +44 -0
  30. package/dist/ui/chart.js +132 -0
  31. package/dist/ui/checkbox.d.ts +4 -0
  32. package/dist/ui/checkbox.js +8 -0
  33. package/dist/ui/collapsible.d.ts +5 -0
  34. package/dist/ui/collapsible.js +13 -0
  35. package/dist/ui/command.d.ts +18 -0
  36. package/dist/ui/command.js +34 -0
  37. package/dist/ui/context-menu.d.ts +25 -0
  38. package/dist/ui/context-menu.js +51 -0
  39. package/dist/ui/dialog.d.ts +17 -0
  40. package/dist/ui/dialog.js +36 -0
  41. package/dist/ui/drawer.d.ts +13 -0
  42. package/dist/ui/drawer.js +35 -0
  43. package/dist/ui/dropdown-menu.d.ts +25 -0
  44. package/dist/ui/dropdown-menu.js +50 -0
  45. package/dist/ui/form.d.ts +24 -0
  46. package/dist/ui/form.js +58 -0
  47. package/dist/ui/hover-card.d.ts +6 -0
  48. package/dist/ui/hover-card.js +13 -0
  49. package/dist/ui/input-otp.d.ts +11 -0
  50. package/dist/ui/input-otp.js +21 -0
  51. package/dist/ui/input.d.ts +3 -0
  52. package/dist/ui/input.js +6 -0
  53. package/dist/ui/label.d.ts +4 -0
  54. package/dist/ui/label.js +8 -0
  55. package/dist/ui/menubar.d.ts +26 -0
  56. package/dist/ui/menubar.js +53 -0
  57. package/dist/ui/navigation-menu.d.ts +14 -0
  58. package/dist/ui/navigation-menu.js +31 -0
  59. package/dist/ui/pagination.d.ts +13 -0
  60. package/dist/ui/pagination.js +29 -0
  61. package/dist/ui/popover.d.ts +10 -0
  62. package/dist/ui/popover.js +26 -0
  63. package/dist/ui/progress.d.ts +4 -0
  64. package/dist/ui/progress.js +7 -0
  65. package/dist/ui/radio-group.d.ts +5 -0
  66. package/dist/ui/radio-group.js +12 -0
  67. package/dist/ui/resizable.d.ts +7 -0
  68. package/dist/ui/resizable.js +14 -0
  69. package/dist/ui/scroll-area.d.ts +5 -0
  70. package/dist/ui/scroll-area.js +12 -0
  71. package/dist/ui/select.d.ts +15 -0
  72. package/dist/ui/select.js +38 -0
  73. package/dist/ui/separator.d.ts +4 -0
  74. package/dist/ui/separator.js +7 -0
  75. package/dist/ui/sheet.d.ts +14 -0
  76. package/dist/ui/sheet.js +40 -0
  77. package/dist/ui/sidebar.d.ts +69 -0
  78. package/dist/ui/sidebar.js +211 -0
  79. package/dist/ui/skeleton.d.ts +2 -0
  80. package/dist/ui/skeleton.js +6 -0
  81. package/dist/ui/slider.d.ts +4 -0
  82. package/dist/ui/slider.js +13 -0
  83. package/dist/ui/sonner.d.ts +3 -0
  84. package/dist/ui/sonner.js +21 -0
  85. package/dist/ui/switch.d.ts +6 -0
  86. package/dist/ui/switch.js +7 -0
  87. package/dist/ui/table.d.ts +10 -0
  88. package/dist/ui/table.js +28 -0
  89. package/dist/ui/tabs.d.ts +11 -0
  90. package/dist/ui/tabs.js +28 -0
  91. package/dist/ui/textarea.d.ts +3 -0
  92. package/dist/ui/textarea.js +6 -0
  93. package/dist/ui/toggle-group.d.ts +9 -0
  94. package/dist/ui/toggle-group.js +22 -0
  95. package/dist/ui/toggle.d.ts +9 -0
  96. package/dist/ui/toggle.js +26 -0
  97. package/dist/ui/tooltip.d.ts +7 -0
  98. package/dist/ui/tooltip.js +16 -0
  99. package/package.json +28 -4
  100. package/src/cn.ts +5 -2
  101. package/src/hooks/use-mobile.ts +19 -0
  102. package/src/index.css +158 -0
  103. package/src/index.ts +48 -37
  104. package/src/ui/accordion.tsx +63 -0
  105. package/src/ui/alert-dialog.tsx +196 -0
  106. package/src/ui/alert.tsx +65 -0
  107. package/src/ui/aspect-ratio.tsx +9 -0
  108. package/src/ui/avatar.tsx +108 -0
  109. package/src/ui/badge.tsx +47 -0
  110. package/src/ui/breadcrumb.tsx +108 -0
  111. package/src/ui/button.tsx +63 -0
  112. package/src/ui/calendar.tsx +218 -0
  113. package/src/ui/card.tsx +91 -0
  114. package/src/ui/carousel.tsx +241 -0
  115. package/src/ui/chart.tsx +371 -0
  116. package/src/ui/checkbox.tsx +29 -0
  117. package/src/ui/collapsible.tsx +33 -0
  118. package/src/ui/command.tsx +184 -0
  119. package/src/ui/context-menu.tsx +251 -0
  120. package/src/ui/dialog.tsx +156 -0
  121. package/src/ui/drawer.tsx +134 -0
  122. package/src/ui/dropdown-menu.tsx +254 -0
  123. package/src/ui/form.tsx +165 -0
  124. package/src/ui/hover-card.tsx +41 -0
  125. package/src/ui/input-otp.tsx +76 -0
  126. package/src/ui/input.tsx +20 -0
  127. package/src/ui/label.tsx +23 -0
  128. package/src/ui/menubar.tsx +273 -0
  129. package/src/ui/navigation-menu.tsx +167 -0
  130. package/src/ui/pagination.tsx +127 -0
  131. package/src/ui/popover.tsx +88 -0
  132. package/src/ui/progress.tsx +28 -0
  133. package/src/ui/radio-group.tsx +44 -0
  134. package/src/ui/resizable.tsx +50 -0
  135. package/src/ui/scroll-area.tsx +55 -0
  136. package/src/ui/select.tsx +189 -0
  137. package/src/ui/separator.tsx +25 -0
  138. package/src/ui/sheet.tsx +142 -0
  139. package/src/ui/sidebar.tsx +724 -0
  140. package/src/ui/skeleton.tsx +13 -0
  141. package/src/ui/slider.tsx +60 -0
  142. package/src/ui/sonner.tsx +40 -0
  143. package/src/ui/switch.tsx +32 -0
  144. package/src/ui/table.tsx +115 -0
  145. package/src/ui/tabs.tsx +88 -0
  146. package/src/ui/textarea.tsx +17 -0
  147. package/src/ui/toggle-group.tsx +83 -0
  148. package/src/ui/toggle.tsx +46 -0
  149. package/src/ui/tooltip.tsx +54 -0
  150. package/dist/Accordion.d.ts +0 -10
  151. package/dist/Accordion.js +0 -12
  152. package/dist/AlertBanner.d.ts +0 -9
  153. package/dist/AlertBanner.js +0 -21
  154. package/dist/AppBar.d.ts +0 -7
  155. package/dist/AppBar.js +0 -5
  156. package/dist/Avatar.d.ts +0 -6
  157. package/dist/Avatar.js +0 -20
  158. package/dist/Badge.d.ts +0 -6
  159. package/dist/Badge.js +0 -11
  160. package/dist/BottomSheet.d.ts +0 -8
  161. package/dist/BottomSheet.js +0 -24
  162. package/dist/Breadcrumb.d.ts +0 -8
  163. package/dist/Breadcrumb.js +0 -9
  164. package/dist/Button.d.ts +0 -9
  165. package/dist/Button.js +0 -18
  166. package/dist/Card.d.ts +0 -7
  167. package/dist/Card.js +0 -6
  168. package/dist/Checkbox.d.ts +0 -5
  169. package/dist/Checkbox.js +0 -5
  170. package/dist/Chip.d.ts +0 -7
  171. package/dist/Chip.js +0 -15
  172. package/dist/DataTable.d.ts +0 -15
  173. package/dist/DataTable.js +0 -7
  174. package/dist/DatePicker.d.ts +0 -5
  175. package/dist/DatePicker.js +0 -11
  176. package/dist/Divider.d.ts +0 -5
  177. package/dist/Divider.js +0 -11
  178. package/dist/Drawer.d.ts +0 -10
  179. package/dist/Drawer.js +0 -24
  180. package/dist/EmptyState.d.ts +0 -7
  181. package/dist/EmptyState.js +0 -6
  182. package/dist/Input.d.ts +0 -5
  183. package/dist/Input.js +0 -7
  184. package/dist/Link.d.ts +0 -4
  185. package/dist/Link.js +0 -6
  186. package/dist/Menu.d.ts +0 -14
  187. package/dist/Menu.js +0 -29
  188. package/dist/Modal.d.ts +0 -9
  189. package/dist/Modal.js +0 -24
  190. package/dist/NavigationBar.d.ts +0 -14
  191. package/dist/NavigationBar.js +0 -22
  192. package/dist/Pagination.d.ts +0 -7
  193. package/dist/Pagination.js +0 -24
  194. package/dist/Popover.d.ts +0 -7
  195. package/dist/Popover.js +0 -9
  196. package/dist/Progress.d.ts +0 -8
  197. package/dist/Progress.js +0 -15
  198. package/dist/Radio.d.ts +0 -15
  199. package/dist/Radio.js +0 -8
  200. package/dist/SegmentedControl.d.ts +0 -13
  201. package/dist/SegmentedControl.js +0 -18
  202. package/dist/Select.d.ts +0 -5
  203. package/dist/Select.js +0 -7
  204. package/dist/Sidebar.d.ts +0 -16
  205. package/dist/Sidebar.js +0 -19
  206. package/dist/Slider.d.ts +0 -5
  207. package/dist/Slider.js +0 -5
  208. package/dist/Snackbar.d.ts +0 -12
  209. package/dist/Snackbar.js +0 -20
  210. package/dist/Spinner.d.ts +0 -3
  211. package/dist/Spinner.js +0 -5
  212. package/dist/Switch.d.ts +0 -4
  213. package/dist/Switch.js +0 -5
  214. package/dist/Tabs.d.ts +0 -14
  215. package/dist/Tabs.js +0 -24
  216. package/dist/Tag.d.ts +0 -6
  217. package/dist/Tag.js +0 -11
  218. package/dist/Textarea.d.ts +0 -5
  219. package/dist/Textarea.js +0 -7
  220. package/dist/Tooltip.d.ts +0 -7
  221. package/dist/Tooltip.js +0 -5
  222. package/dist/TreeView.d.ts +0 -12
  223. package/dist/TreeView.js +0 -34
  224. package/src/Accordion.tsx +0 -58
  225. package/src/AlertBanner.tsx +0 -62
  226. package/src/AppBar.tsx +0 -29
  227. package/src/Avatar.tsx +0 -42
  228. package/src/Badge.tsx +0 -27
  229. package/src/BottomSheet.tsx +0 -60
  230. package/src/Breadcrumb.tsx +0 -48
  231. package/src/Button.tsx +0 -54
  232. package/src/Card.tsx +0 -39
  233. package/src/Checkbox.tsx +0 -31
  234. package/src/Chip.tsx +0 -44
  235. package/src/DataTable.tsx +0 -64
  236. package/src/DatePicker.tsx +0 -44
  237. package/src/Divider.tsx +0 -33
  238. package/src/Drawer.tsx +0 -71
  239. package/src/EmptyState.tsx +0 -37
  240. package/src/Input.tsx +0 -30
  241. package/src/Link.tsx +0 -22
  242. package/src/Menu.tsx +0 -73
  243. package/src/Modal.tsx +0 -62
  244. package/src/NavigationBar.tsx +0 -68
  245. package/src/Pagination.tsx +0 -69
  246. package/src/Popover.tsx +0 -34
  247. package/src/Progress.tsx +0 -53
  248. package/src/Radio.tsx +0 -49
  249. package/src/SegmentedControl.tsx +0 -63
  250. package/src/Select.tsx +0 -32
  251. package/src/Sidebar.tsx +0 -73
  252. package/src/Slider.tsx +0 -33
  253. package/src/Snackbar.tsx +0 -72
  254. package/src/Spinner.tsx +0 -19
  255. package/src/Switch.tsx +0 -40
  256. package/src/Tabs.tsx +0 -77
  257. package/src/Tag.tsx +0 -29
  258. package/src/Textarea.tsx +0 -30
  259. package/src/Tooltip.tsx +0 -25
  260. package/src/TreeView.tsx +0 -105
@@ -1,69 +0,0 @@
1
- import { ChevronLeft, ChevronRight } from 'lucide-react';
2
- import { cn } from './cn';
3
- import { Button } from './Button';
4
-
5
- export interface PaginationProps {
6
- page: number;
7
- totalPages: number;
8
- onChange: (page: number) => void;
9
- className?: string;
10
- }
11
-
12
- function pageList(page: number, total: number): (number | '…')[] {
13
- if (total <= 7) return Array.from({ length: total }, (_, i) => i + 1);
14
- const pages: (number | '…')[] = [1];
15
- const start = Math.max(2, page - 1);
16
- const end = Math.min(total - 1, page + 1);
17
- if (start > 2) pages.push('…');
18
- for (let i = start; i <= end; i++) pages.push(i);
19
- if (end < total - 1) pages.push('…');
20
- pages.push(total);
21
- return pages;
22
- }
23
-
24
- export function Pagination({ page, totalPages, onChange, className }: PaginationProps) {
25
- return (
26
- <nav aria-label="Pagination" className={cn('flex items-center gap-1', className)}>
27
- <Button
28
- variant="ghost"
29
- size="sm"
30
- disabled={page <= 1}
31
- onClick={() => onChange(page - 1)}
32
- aria-label="Previous page"
33
- >
34
- <ChevronLeft className="h-4 w-4" />
35
- </Button>
36
- {pageList(page, totalPages).map((p, i) =>
37
- p === '…' ? (
38
- <span key={`e-${i}`} className="px-1.5 text-sm text-slate-400">
39
- …
40
- </span>
41
- ) : (
42
- <button
43
- key={p}
44
- type="button"
45
- onClick={() => onChange(p)}
46
- aria-current={p === page ? 'page' : undefined}
47
- className={cn(
48
- 'h-8 min-w-8 rounded-lg px-2 text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-lime-500/40',
49
- p === page
50
- ? 'bg-lime-600 text-white'
51
- : 'text-slate-600 hover:bg-slate-100 hover:text-slate-900'
52
- )}
53
- >
54
- {p}
55
- </button>
56
- )
57
- )}
58
- <Button
59
- variant="ghost"
60
- size="sm"
61
- disabled={page >= totalPages}
62
- onClick={() => onChange(page + 1)}
63
- aria-label="Next page"
64
- >
65
- <ChevronRight className="h-4 w-4" />
66
- </Button>
67
- </nav>
68
- );
69
- }
package/src/Popover.tsx DELETED
@@ -1,34 +0,0 @@
1
- import { cn } from './cn';
2
-
3
- export interface PopoverProps {
4
- trigger: React.ReactNode;
5
- content: React.ReactNode;
6
- side?: 'top' | 'bottom' | 'left' | 'right';
7
- className?: string;
8
- }
9
-
10
- export function Popover({ trigger, content, side = 'bottom', className }: PopoverProps) {
11
- const isTop = side === 'top';
12
- const isBottom = side === 'bottom';
13
- const isLeft = side === 'left';
14
- const isRight = side === 'right';
15
-
16
- return (
17
- <span className="group relative inline-flex">
18
- {trigger}
19
- <span
20
- role="tooltip"
21
- className={cn(
22
- 'pointer-events-none absolute z-50 w-64 rounded-xl border border-slate-200 bg-white p-4 text-sm text-slate-700 opacity-0 shadow-lg transition-opacity duration-200 group-hover:opacity-100 group-focus-within:opacity-100',
23
- isBottom && 'left-1/2 top-full mt-2 -translate-x-1/2',
24
- isTop && 'bottom-full left-1/2 mb-2 -translate-x-1/2',
25
- isLeft && 'right-full top-1/2 mr-2 -translate-y-1/2',
26
- isRight && 'left-full top-1/2 ml-2 -translate-y-1/2',
27
- className
28
- )}
29
- >
30
- {content}
31
- </span>
32
- </span>
33
- );
34
- }
package/src/Progress.tsx DELETED
@@ -1,53 +0,0 @@
1
- import { cn } from './cn';
2
-
3
- export interface ProgressProps extends React.HTMLAttributes<HTMLDivElement> {
4
- value: number;
5
- max?: number;
6
- size?: 'sm' | 'md';
7
- tone?: 'primary' | 'accent' | 'danger';
8
- showLabel?: boolean;
9
- }
10
-
11
- const track: Record<NonNullable<ProgressProps['size']>, string> = {
12
- sm: 'h-1.5',
13
- md: 'h-2.5',
14
- };
15
-
16
- const fill: Record<NonNullable<ProgressProps['tone']>, string> = {
17
- primary: 'bg-lime-600',
18
- accent: 'bg-purple-600',
19
- danger: 'bg-red-600',
20
- };
21
-
22
- export function Progress({
23
- value,
24
- max = 100,
25
- size = 'md',
26
- tone = 'primary',
27
- showLabel = false,
28
- className,
29
- ...props
30
- }: ProgressProps) {
31
- const pct = max > 0 ? Math.min(100, Math.max(0, (value / max) * 100)) : 0;
32
- return (
33
- <div className={cn('flex items-center gap-3', className)} {...props}>
34
- <div
35
- role="progressbar"
36
- aria-valuenow={value}
37
- aria-valuemin={0}
38
- aria-valuemax={max}
39
- className={cn('w-full overflow-hidden rounded-full bg-slate-200', track[size])}
40
- >
41
- <div
42
- className={cn('h-full rounded-full transition-all duration-300', fill[tone])}
43
- style={{ width: `${pct}%` }}
44
- />
45
- </div>
46
- {showLabel && (
47
- <span className="w-10 shrink-0 text-right text-xs font-semibold tabular-nums text-slate-600">
48
- {Math.round(pct)}%
49
- </span>
50
- )}
51
- </div>
52
- );
53
- }
package/src/Radio.tsx DELETED
@@ -1,49 +0,0 @@
1
- import { cn } from './cn';
2
-
3
- export interface RadioProps
4
- extends Omit<React.InputHTMLAttributes<HTMLInputElement>, 'type'> {
5
- label?: string;
6
- }
7
-
8
- export function Radio({ label, id, className, ...props }: RadioProps) {
9
- return (
10
- <label
11
- htmlFor={id}
12
- className={cn('inline-flex cursor-pointer items-center gap-2.5 text-sm', className)}
13
- >
14
- <input
15
- id={id}
16
- type="radio"
17
- className="h-4 w-4 shrink-0 cursor-pointer accent-lime-600 focus:outline-none focus:ring-2 focus:ring-lime-500/40"
18
- {...props}
19
- />
20
- {label && <span className="font-medium text-slate-700">{label}</span>}
21
- </label>
22
- );
23
- }
24
-
25
- export interface RadioGroupProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'onChange'> {
26
- name: string;
27
- value?: string | number;
28
- onChange?: (value: string) => void;
29
- options: { value: string; label: string; disabled?: boolean }[];
30
- }
31
-
32
- export function RadioGroup({ name, value, onChange, options, className, ...props }: RadioGroupProps) {
33
- return (
34
- <div className={cn('flex flex-wrap gap-x-5 gap-y-2.5', className)} {...props}>
35
- {options.map(opt => (
36
- <Radio
37
- key={opt.value}
38
- id={`${name}-${opt.value}`}
39
- name={name}
40
- value={opt.value}
41
- label={opt.label}
42
- checked={value === opt.value}
43
- disabled={opt.disabled}
44
- onChange={e => onChange?.(e.target.value)}
45
- />
46
- ))}
47
- </div>
48
- );
49
- }
@@ -1,63 +0,0 @@
1
- import { useState } from 'react';
2
- import { cn } from './cn';
3
-
4
- export interface SegmentedOption<T extends string | number> {
5
- value: T;
6
- label: string;
7
- disabled?: boolean;
8
- }
9
-
10
- export interface SegmentedControlProps<T extends string | number> {
11
- options: SegmentedOption<T>[];
12
- value?: T;
13
- defaultValue?: T;
14
- onChange?: (value: T) => void;
15
- className?: string;
16
- }
17
-
18
- export function SegmentedControl<T extends string | number>({
19
- options,
20
- value,
21
- defaultValue,
22
- onChange,
23
- className,
24
- }: SegmentedControlProps<T>) {
25
- const isControlled = value !== undefined;
26
- const [inner, setInner] = useState<T | undefined>(defaultValue);
27
- const active = isControlled ? value : inner;
28
-
29
- return (
30
- <div
31
- role="tablist"
32
- className={cn(
33
- 'inline-flex items-center gap-1 rounded-lg border border-slate-200 bg-white p-1',
34
- className
35
- )}
36
- >
37
- {options.map(opt => {
38
- const selected = active === opt.value;
39
- return (
40
- <button
41
- key={String(opt.value)}
42
- type="button"
43
- role="tab"
44
- aria-selected={selected}
45
- disabled={opt.disabled}
46
- onClick={() => {
47
- if (!isControlled) setInner(opt.value);
48
- onChange?.(opt.value);
49
- }}
50
- className={cn(
51
- 'rounded-md px-4 py-1.5 text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-lime-500/40 disabled:opacity-50',
52
- selected
53
- ? 'bg-lime-600 text-white'
54
- : 'text-slate-600 hover:bg-slate-100 hover:text-slate-900'
55
- )}
56
- >
57
- {opt.label}
58
- </button>
59
- );
60
- })}
61
- </div>
62
- );
63
- }
package/src/Select.tsx DELETED
@@ -1,32 +0,0 @@
1
- import { cn } from './cn';
2
-
3
- export interface SelectProps extends React.SelectHTMLAttributes<HTMLSelectElement> {
4
- label?: string;
5
- error?: string;
6
- }
7
-
8
- export function Select({ label, error, className, id, children, ...props }: SelectProps) {
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
- <select
17
- id={id}
18
- className={cn(
19
- 'w-full rounded-lg border bg-white px-3 py-2.5 text-sm text-slate-900 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
- {children}
28
- </select>
29
- {error && <p className="mt-1 text-xs text-red-600">{error}</p>}
30
- </div>
31
- );
32
- }
package/src/Sidebar.tsx DELETED
@@ -1,73 +0,0 @@
1
- import { useState } from 'react';
2
- import { cn } from './cn';
3
-
4
- export interface SidebarItem {
5
- value: string;
6
- label: string;
7
- icon?: React.ReactNode;
8
- badge?: React.ReactNode;
9
- disabled?: boolean;
10
- }
11
-
12
- export interface SidebarProps extends Omit<React.HTMLAttributes<HTMLElement>, 'onChange'> {
13
- items: SidebarItem[];
14
- value?: string;
15
- defaultValue?: string;
16
- onChange?: (value: string) => void;
17
- header?: React.ReactNode;
18
- footer?: React.ReactNode;
19
- }
20
-
21
- export function Sidebar({
22
- items,
23
- value,
24
- defaultValue,
25
- onChange,
26
- header,
27
- footer,
28
- className,
29
- ...props
30
- }: SidebarProps) {
31
- const controlled = value !== undefined;
32
- const [active, setActive] = useState(defaultValue);
33
-
34
- return (
35
- <nav
36
- className={cn('flex w-64 flex-col gap-2 rounded-xl border border-slate-200 bg-white p-3', className)}
37
- {...props}
38
- >
39
- {header && <div className="border-b border-slate-100 pb-2">{header}</div>}
40
- <ul className="flex-1 space-y-0.5">
41
- {items.map(item => {
42
- const isActive = controlled ? value === item.value : active === item.value;
43
- return (
44
- <li key={item.value}>
45
- <button
46
- type="button"
47
- disabled={item.disabled}
48
- onClick={() => {
49
- if (!controlled) setActive(item.value);
50
- onChange?.(item.value);
51
- }}
52
- className={cn(
53
- 'flex w-full items-center gap-2.5 rounded-lg px-3 py-2 text-left text-sm font-medium transition-colors',
54
- 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-lime-500/40 disabled:cursor-not-allowed',
55
- item.disabled
56
- ? 'cursor-not-allowed text-slate-300'
57
- : isActive
58
- ? 'bg-lime-600 text-white'
59
- : 'text-slate-700 hover:bg-lime-50 hover:text-lime-800'
60
- )}
61
- >
62
- {item.icon}
63
- <span className="flex-1 truncate">{item.label}</span>
64
- {item.badge}
65
- </button>
66
- </li>
67
- );
68
- })}
69
- </ul>
70
- {footer && <div className="border-t border-slate-100 pt-2">{footer}</div>}
71
- </nav>
72
- );
73
- }
package/src/Slider.tsx DELETED
@@ -1,33 +0,0 @@
1
- import { cn } from './cn';
2
-
3
- export interface SliderProps extends Omit<React.InputHTMLAttributes<HTMLInputElement>, 'type'> {
4
- label?: string;
5
- valueLabel?: string;
6
- }
7
-
8
- export function Slider({ label, valueLabel, id, className, ...props }: SliderProps) {
9
- return (
10
- <div className={cn('w-full max-w-md', className)}>
11
- {(label || valueLabel) && (
12
- <div className="mb-2 flex items-center justify-between">
13
- {label && (
14
- <label htmlFor={id} className="text-sm font-medium text-slate-700">
15
- {label}
16
- </label>
17
- )}
18
- {valueLabel && (
19
- <span className="text-sm font-semibold tabular-nums text-slate-600">
20
- {valueLabel}
21
- </span>
22
- )}
23
- </div>
24
- )}
25
- <input
26
- id={id}
27
- type="range"
28
- className="h-2 w-full cursor-pointer appearance-none rounded-full bg-slate-200 accent-lime-600 focus:outline-none focus:ring-2 focus:ring-lime-500/40"
29
- {...props}
30
- />
31
- </div>
32
- );
33
- }
package/src/Snackbar.tsx DELETED
@@ -1,72 +0,0 @@
1
- import { CheckCircle2, XCircle, AlertTriangle, Info, X } from 'lucide-react';
2
- import { cn } from './cn';
3
-
4
- type SnackbarTone = 'success' | 'error' | 'warning' | 'info';
5
-
6
- export interface SnackbarProps {
7
- open?: boolean;
8
- tone?: SnackbarTone;
9
- message: string;
10
- actionLabel?: string;
11
- onAction?: () => void;
12
- onClose?: () => void;
13
- className?: string;
14
- }
15
-
16
- const toneIcon: Record<SnackbarTone, React.ReactNode> = {
17
- success: <CheckCircle2 className="h-5 w-5 shrink-0" />,
18
- error: <XCircle className="h-5 w-5 shrink-0" />,
19
- warning: <AlertTriangle className="h-5 w-5 shrink-0" />,
20
- info: <Info className="h-5 w-5 shrink-0" />,
21
- };
22
-
23
- const toneText: Record<SnackbarTone, string> = {
24
- success: 'text-lime-600',
25
- error: 'text-red-600',
26
- warning: 'text-purple-600',
27
- info: 'text-sky-600',
28
- };
29
-
30
- export function Snackbar({
31
- open = true,
32
- tone = 'info',
33
- message,
34
- actionLabel,
35
- onAction,
36
- onClose,
37
- className,
38
- }: SnackbarProps) {
39
- if (!open) return null;
40
- return (
41
- <div
42
- role="status"
43
- aria-live="polite"
44
- className={cn(
45
- 'pointer-events-auto flex items-center gap-3 rounded-xl border border-slate-200 bg-white px-4 py-3 shadow-lg',
46
- className
47
- )}
48
- >
49
- <span className={toneText[tone]}>{toneIcon[tone]}</span>
50
- <p className="min-w-0 flex-1 text-sm font-medium text-slate-800">{message}</p>
51
- {actionLabel && (
52
- <button
53
- type="button"
54
- onClick={onAction}
55
- className="shrink-0 text-sm font-semibold text-lime-700 hover:text-lime-800 hover:underline"
56
- >
57
- {actionLabel}
58
- </button>
59
- )}
60
- {onClose && (
61
- <button
62
- type="button"
63
- aria-label="Close"
64
- onClick={onClose}
65
- className="shrink-0 rounded-full p-1 text-slate-400 hover:bg-slate-100"
66
- >
67
- <X className="h-4 w-4" />
68
- </button>
69
- )}
70
- </div>
71
- );
72
- }
package/src/Spinner.tsx DELETED
@@ -1,19 +0,0 @@
1
- import { cn } from './cn';
2
-
3
- export function Spinner({ className }: { className?: string }) {
4
- return (
5
- <svg
6
- className={cn(
7
- 'h-5 w-5 animate-spin text-current',
8
- 'inline-flex shrink-0',
9
- className
10
- )}
11
- viewBox="0 0 24 24"
12
- fill="none"
13
- aria-hidden="true"
14
- >
15
- <circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4" />
16
- <path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 0 1 8-8v4a4 4 0 0 0-4 4H4z" />
17
- </svg>
18
- );
19
- }
package/src/Switch.tsx DELETED
@@ -1,40 +0,0 @@
1
- import { cn } from './cn';
2
-
3
- export interface SwitchProps
4
- extends Omit<React.InputHTMLAttributes<HTMLInputElement>, 'type'> {
5
- label?: string;
6
- }
7
-
8
- export function Switch({ label, id, className, checked, onChange, ...props }: SwitchProps) {
9
- return (
10
- <label
11
- htmlFor={id}
12
- className={cn('inline-flex cursor-pointer items-center gap-3', className)}
13
- >
14
- <input
15
- id={id}
16
- type="checkbox"
17
- role="switch"
18
- checked={checked}
19
- onChange={onChange}
20
- className="sr-only"
21
- {...props}
22
- />
23
- <span
24
- aria-hidden="true"
25
- className={cn(
26
- 'relative inline-flex h-6 w-11 shrink-0 items-center rounded-full transition-colors',
27
- checked ? 'bg-lime-600' : 'bg-slate-300'
28
- )}
29
- >
30
- <span
31
- className={cn(
32
- 'inline-block h-4 w-4 transform rounded-full bg-white shadow transition-transform',
33
- checked ? 'translate-x-6' : 'translate-x-1'
34
- )}
35
- />
36
- </span>
37
- {label && <span className="text-sm font-medium text-slate-700">{label}</span>}
38
- </label>
39
- );
40
- }
package/src/Tabs.tsx DELETED
@@ -1,77 +0,0 @@
1
- import { useState } from 'react';
2
- import { cn } from './cn';
3
-
4
- export interface Tab<T extends string | number> {
5
- value: T;
6
- label: string;
7
- disabled?: boolean;
8
- }
9
-
10
- export interface TabsProps<T extends string | number> {
11
- tabs: Tab<T>[];
12
- value?: T;
13
- defaultValue?: T;
14
- onChange?: (value: T) => void;
15
- variant?: 'underline' | 'pill';
16
- className?: string;
17
- }
18
-
19
- export function Tabs<T extends string | number>({
20
- tabs,
21
- value,
22
- defaultValue,
23
- onChange,
24
- variant = 'underline',
25
- className,
26
- }: TabsProps<T>) {
27
- const isControlled = value !== undefined;
28
- const [inner, setInner] = useState<T | undefined>(defaultValue);
29
- const active = isControlled ? value : inner;
30
-
31
- return (
32
- <div
33
- role="tablist"
34
- className={cn(
35
- variant === 'underline'
36
- ? 'flex gap-1 border-b border-slate-200'
37
- : 'inline-flex items-center gap-1 rounded-lg border border-slate-200 bg-white p-1',
38
- className
39
- )}
40
- >
41
- {tabs.map(tab => {
42
- const selected = active === tab.value;
43
- return (
44
- <button
45
- key={String(tab.value)}
46
- type="button"
47
- role="tab"
48
- aria-selected={selected}
49
- disabled={tab.disabled}
50
- onClick={() => {
51
- if (!isControlled) setInner(tab.value);
52
- onChange?.(tab.value);
53
- }}
54
- className={cn(
55
- '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',
56
- variant === 'underline'
57
- ? cn(
58
- '-mb-px border-b-2',
59
- selected
60
- ? 'border-lime-600 text-lime-700'
61
- : 'border-transparent text-slate-500 hover:border-slate-300 hover:text-slate-900'
62
- )
63
- : cn(
64
- 'rounded-md',
65
- selected
66
- ? 'bg-lime-600 text-white'
67
- : 'text-slate-600 hover:bg-slate-100 hover:text-slate-900'
68
- )
69
- )}
70
- >
71
- {tab.label}
72
- </button>
73
- );
74
- })}
75
- </div>
76
- );
77
- }
package/src/Tag.tsx DELETED
@@ -1,29 +0,0 @@
1
- import { cn } from './cn';
2
-
3
- type TagTone = 'primary' | 'accent' | 'neutral' | 'danger';
4
-
5
- export interface TagProps extends React.HTMLAttributes<HTMLSpanElement> {
6
- tone?: TagTone;
7
- }
8
-
9
- const toneClasses: Record<TagTone, string> = {
10
- primary: 'border-lime-300 bg-white text-lime-800',
11
- accent: 'border-purple-300 bg-white text-purple-900',
12
- neutral: 'border-slate-300 bg-white text-slate-700',
13
- danger: 'border-red-300 bg-white text-red-800',
14
- };
15
-
16
- export function Tag({ tone = 'neutral', className, children, ...props }: TagProps) {
17
- return (
18
- <span
19
- className={cn(
20
- 'inline-flex items-center gap-1 rounded-md border px-2 py-0.5 text-xs font-semibold',
21
- toneClasses[tone],
22
- className
23
- )}
24
- {...props}
25
- >
26
- {children}
27
- </span>
28
- );
29
- }
package/src/Textarea.tsx DELETED
@@ -1,30 +0,0 @@
1
- import { cn } from './cn';
2
-
3
- export interface TextareaProps extends React.TextareaHTMLAttributes<HTMLTextAreaElement> {
4
- label?: string;
5
- error?: string;
6
- }
7
-
8
- export function Textarea({ label, error, className, id, ...props }: TextareaProps) {
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
- <textarea
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
- }