@majapisoftwares/ui 6.1.0 → 6.1.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 (73) hide show
  1. package/AGENTS.md +27 -27
  2. package/README.md +79 -79
  3. package/THIRD_PARTY_NOTICES.md +39 -39
  4. package/components.json +25 -25
  5. package/docs/MIGRATION-0.2.md +20 -20
  6. package/docs/MIGRATION-6.0.md +33 -33
  7. package/docs/NEXT-STARTER-VALIDATION.md +14 -14
  8. package/docs/STANDARDS.md +41 -41
  9. package/docs/VALIDATION.md +11 -11
  10. package/docs/VERSIONING.md +28 -28
  11. package/docs/validation/preference-titles.md +22 -0
  12. package/package.json +88 -84
  13. package/scripts/check-standards.mjs +41 -41
  14. package/src/components/ui/accordion.tsx +82 -82
  15. package/src/components/ui/alert-dialog.tsx +187 -187
  16. package/src/components/ui/alert.tsx +77 -77
  17. package/src/components/ui/avatar.tsx +108 -108
  18. package/src/components/ui/badge.tsx +55 -55
  19. package/src/components/ui/breadcrumb.tsx +125 -125
  20. package/src/components/ui/button-group.tsx +89 -89
  21. package/src/components/ui/button.tsx +59 -59
  22. package/src/components/ui/card.tsx +101 -101
  23. package/src/components/ui/checkbox.tsx +27 -27
  24. package/src/components/ui/collapsible.tsx +21 -21
  25. package/src/components/ui/context-menu.tsx +271 -271
  26. package/src/components/ui/dialog.tsx +161 -161
  27. package/src/components/ui/dropdown-menu.tsx +271 -271
  28. package/src/components/ui/empty.tsx +105 -105
  29. package/src/components/ui/field.tsx +238 -238
  30. package/src/components/ui/hover-card.tsx +50 -50
  31. package/src/components/ui/input-group.tsx +155 -155
  32. package/src/components/ui/input.tsx +21 -21
  33. package/src/components/ui/kbd.tsx +28 -28
  34. package/src/components/ui/label.tsx +19 -19
  35. package/src/components/ui/orb.css +1002 -1002
  36. package/src/components/ui/orb.tsx +752 -752
  37. package/src/components/ui/popover.tsx +89 -89
  38. package/src/components/ui/progress.tsx +82 -82
  39. package/src/components/ui/radio-group.tsx +37 -37
  40. package/src/components/ui/scroll-area.tsx +54 -54
  41. package/src/components/ui/select.tsx +204 -204
  42. package/src/components/ui/separator.tsx +24 -24
  43. package/src/components/ui/sheet.tsx +139 -139
  44. package/src/components/ui/sidebar.tsx +1039 -1035
  45. package/src/components/ui/skeleton.tsx +15 -15
  46. package/src/components/ui/slider.tsx +53 -53
  47. package/src/components/ui/sonner.tsx +44 -44
  48. package/src/components/ui/spinner.tsx +32 -32
  49. package/src/components/ui/switch.tsx +31 -31
  50. package/src/components/ui/table.tsx +121 -121
  51. package/src/components/ui/tabs.tsx +81 -81
  52. package/src/components/ui/textarea.tsx +19 -19
  53. package/src/components/ui/toggle-group.tsx +89 -89
  54. package/src/components/ui/toggle.tsx +44 -44
  55. package/src/components/ui/tooltip.tsx +65 -65
  56. package/src/hooks/use-mobile.ts +20 -20
  57. package/src/lib/i18n.tsx +109 -109
  58. package/src/lib/utils.ts +1 -1
  59. package/src/patterns/app-shell.tsx +177 -179
  60. package/src/patterns/data-table.tsx +2486 -2486
  61. package/src/patterns/details-sheet.tsx +54 -54
  62. package/src/patterns/filter-tabs.tsx +53 -53
  63. package/src/patterns/majapi-logo.tsx +32 -32
  64. package/src/patterns/page-header.tsx +62 -62
  65. package/src/patterns/preferences.tsx +15 -7
  66. package/src/patterns/status-badge.tsx +24 -24
  67. package/src/patterns/user-menu.tsx +218 -218
  68. package/src/patterns/workspace-switcher.tsx +176 -173
  69. package/src/styles.css +165 -165
  70. package/src/templates/admin-dashboard.tsx +44 -44
  71. package/src/templates/auth-layout.tsx +30 -30
  72. package/src/templates/login-form.tsx +129 -129
  73. package/src/templates/signup-form.tsx +177 -177
@@ -1,15 +1,15 @@
1
- "use client";
2
-
3
- import { cn } from "cn";
4
-
5
- function Skeleton({ className, ...props }: React.ComponentProps<"div">) {
6
- return (
7
- <div
8
- data-slot="skeleton"
9
- className={cn("animate-pulse rounded-2xl bg-muted", className)}
10
- {...props}
11
- />
12
- );
13
- }
14
-
15
- export { Skeleton };
1
+ "use client";
2
+
3
+ import { cn } from "cn";
4
+
5
+ function Skeleton({ className, ...props }: React.ComponentProps<"div">) {
6
+ return (
7
+ <div
8
+ data-slot="skeleton"
9
+ className={cn("animate-pulse rounded-2xl bg-muted", className)}
10
+ {...props}
11
+ />
12
+ );
13
+ }
14
+
15
+ export { Skeleton };
@@ -1,53 +1,53 @@
1
- "use client";
2
-
3
- import { Slider as SliderPrimitive } from "@base-ui/react/slider";
4
- import { cn } from "cn";
5
-
6
- function Slider({
7
- className,
8
- defaultValue,
9
- value,
10
- min = 0,
11
- max = 100,
12
- ...props
13
- }: SliderPrimitive.Root.Props) {
14
- const _values = Array.isArray(value)
15
- ? value
16
- : Array.isArray(defaultValue)
17
- ? defaultValue
18
- : [min, max];
19
-
20
- return (
21
- <SliderPrimitive.Root
22
- className={cn("data-horizontal:w-full data-vertical:h-full", className)}
23
- data-slot="slider"
24
- defaultValue={defaultValue}
25
- value={value}
26
- min={min}
27
- max={max}
28
- thumbAlignment="edge"
29
- {...props}
30
- >
31
- <SliderPrimitive.Control className="relative flex w-full touch-none items-center select-none data-disabled:opacity-50 data-vertical:h-full data-vertical:min-h-40 data-vertical:w-auto data-vertical:flex-col">
32
- <SliderPrimitive.Track
33
- data-slot="slider-track"
34
- className="relative grow overflow-hidden rounded-full bg-input/90 select-none data-horizontal:h-2 data-horizontal:w-full data-vertical:h-full data-vertical:w-2"
35
- >
36
- <SliderPrimitive.Indicator
37
- data-slot="slider-range"
38
- className="bg-primary select-none data-horizontal:h-full data-vertical:w-full"
39
- />
40
- </SliderPrimitive.Track>
41
- {Array.from({ length: _values.length }, (_, index) => (
42
- <SliderPrimitive.Thumb
43
- data-slot="slider-thumb"
44
- key={index}
45
- className="block h-4 w-6 shrink-0 rounded-full bg-white shadow-md ring-1 ring-black/10 transition-[color,box-shadow,background-color] select-none not-dark:bg-clip-padding hover:ring-4 hover:ring-ring/30 focus-visible:ring-4 focus-visible:ring-ring/30 focus-visible:outline-hidden disabled:pointer-events-none disabled:opacity-50 data-vertical:h-6 data-vertical:w-4"
46
- />
47
- ))}
48
- </SliderPrimitive.Control>
49
- </SliderPrimitive.Root>
50
- );
51
- }
52
-
53
- export { Slider };
1
+ "use client";
2
+
3
+ import { Slider as SliderPrimitive } from "@base-ui/react/slider";
4
+ import { cn } from "cn";
5
+
6
+ function Slider({
7
+ className,
8
+ defaultValue,
9
+ value,
10
+ min = 0,
11
+ max = 100,
12
+ ...props
13
+ }: SliderPrimitive.Root.Props) {
14
+ const _values = Array.isArray(value)
15
+ ? value
16
+ : Array.isArray(defaultValue)
17
+ ? defaultValue
18
+ : [min, max];
19
+
20
+ return (
21
+ <SliderPrimitive.Root
22
+ className={cn("data-horizontal:w-full data-vertical:h-full", className)}
23
+ data-slot="slider"
24
+ defaultValue={defaultValue}
25
+ value={value}
26
+ min={min}
27
+ max={max}
28
+ thumbAlignment="edge"
29
+ {...props}
30
+ >
31
+ <SliderPrimitive.Control className="relative flex w-full touch-none items-center select-none data-disabled:opacity-50 data-vertical:h-full data-vertical:min-h-40 data-vertical:w-auto data-vertical:flex-col">
32
+ <SliderPrimitive.Track
33
+ data-slot="slider-track"
34
+ className="relative grow overflow-hidden rounded-full bg-input/90 select-none data-horizontal:h-2 data-horizontal:w-full data-vertical:h-full data-vertical:w-2"
35
+ >
36
+ <SliderPrimitive.Indicator
37
+ data-slot="slider-range"
38
+ className="bg-primary select-none data-horizontal:h-full data-vertical:w-full"
39
+ />
40
+ </SliderPrimitive.Track>
41
+ {Array.from({ length: _values.length }, (_, index) => (
42
+ <SliderPrimitive.Thumb
43
+ data-slot="slider-thumb"
44
+ key={index}
45
+ className="block h-4 w-6 shrink-0 rounded-full bg-white shadow-md ring-1 ring-black/10 transition-[color,box-shadow,background-color] select-none not-dark:bg-clip-padding hover:ring-4 hover:ring-ring/30 focus-visible:ring-4 focus-visible:ring-ring/30 focus-visible:outline-hidden disabled:pointer-events-none disabled:opacity-50 data-vertical:h-6 data-vertical:w-4"
46
+ />
47
+ ))}
48
+ </SliderPrimitive.Control>
49
+ </SliderPrimitive.Root>
50
+ );
51
+ }
52
+
53
+ export { Slider };
@@ -1,44 +1,44 @@
1
- "use client";
2
-
3
- import { Orb } from "./orb";
4
- import { Toaster as Sonner, type ToasterProps } from "sonner";
5
- import {
6
- CircleCheckIcon,
7
- InfoIcon,
8
- TriangleAlertIcon,
9
- OctagonXIcon,
10
- } from "lucide-react";
11
-
12
- // Theme comes from the caller (Preferences passes the active theme).
13
- const Toaster = ({ theme = "system", ...props }: ToasterProps) => {
14
- return (
15
- <Sonner
16
- theme={theme}
17
- className="toaster group"
18
- icons={{
19
- success: <CircleCheckIcon className="size-4" />,
20
- info: <InfoIcon className="size-4" />,
21
- warning: <TriangleAlertIcon className="size-4" />,
22
- error: <OctagonXIcon className="size-4" />,
23
- loading: <Orb size={16} />,
24
- }}
25
- style={
26
- {
27
- "--normal-bg": "var(--popover)",
28
- "--normal-text": "var(--popover-foreground)",
29
- "--normal-border": "var(--border)",
30
- "--border-radius": "var(--radius)",
31
- } as React.CSSProperties
32
- }
33
- toastOptions={{
34
- classNames: {
35
- toast: "cn-toast",
36
- },
37
- }}
38
- {...props}
39
- />
40
- );
41
- };
42
-
43
- export { Toaster };
44
- export { toast } from "sonner";
1
+ "use client";
2
+
3
+ import { Orb } from "./orb";
4
+ import { Toaster as Sonner, type ToasterProps } from "sonner";
5
+ import {
6
+ CircleCheckIcon,
7
+ InfoIcon,
8
+ TriangleAlertIcon,
9
+ OctagonXIcon,
10
+ } from "lucide-react";
11
+
12
+ // Theme comes from the caller (Preferences passes the active theme).
13
+ const Toaster = ({ theme = "system", ...props }: ToasterProps) => {
14
+ return (
15
+ <Sonner
16
+ theme={theme}
17
+ className="toaster group"
18
+ icons={{
19
+ success: <CircleCheckIcon className="size-4" />,
20
+ info: <InfoIcon className="size-4" />,
21
+ warning: <TriangleAlertIcon className="size-4" />,
22
+ error: <OctagonXIcon className="size-4" />,
23
+ loading: <Orb size={16} />,
24
+ }}
25
+ style={
26
+ {
27
+ "--normal-bg": "var(--popover)",
28
+ "--normal-text": "var(--popover-foreground)",
29
+ "--normal-border": "var(--border)",
30
+ "--border-radius": "var(--radius)",
31
+ } as React.CSSProperties
32
+ }
33
+ toastOptions={{
34
+ classNames: {
35
+ toast: "cn-toast",
36
+ },
37
+ }}
38
+ {...props}
39
+ />
40
+ );
41
+ };
42
+
43
+ export { Toaster };
44
+ export { toast } from "sonner";
@@ -1,32 +1,32 @@
1
- "use client";
2
-
3
- import type * as React from "react";
4
- import { cn } from "cn";
5
- import { Orb, type OrbVariant } from "./orb";
6
- import { useUiMessages } from "../../lib/i18n";
7
-
8
- // Majapi UI: loading uses the animated Orb instead of a rotating icon.
9
- function Spinner({
10
- className,
11
- size = 16,
12
- variant = "S1",
13
- label,
14
- ...props
15
- }: React.ComponentProps<"span"> & {
16
- size?: number;
17
- variant?: OrbVariant;
18
- label?: string;
19
- }) {
20
- const messages = useUiMessages();
21
- return (
22
- <span
23
- data-slot="spinner"
24
- className={cn("inline-flex shrink-0", className)}
25
- {...props}
26
- >
27
- <Orb variant={variant} size={size} label={label ?? messages.loading} />
28
- </span>
29
- );
30
- }
31
-
32
- export { Spinner };
1
+ "use client";
2
+
3
+ import type * as React from "react";
4
+ import { cn } from "cn";
5
+ import { Orb, type OrbVariant } from "./orb";
6
+ import { useUiMessages } from "../../lib/i18n";
7
+
8
+ // Majapi UI: loading uses the animated Orb instead of a rotating icon.
9
+ function Spinner({
10
+ className,
11
+ size = 16,
12
+ variant = "S1",
13
+ label,
14
+ ...props
15
+ }: React.ComponentProps<"span"> & {
16
+ size?: number;
17
+ variant?: OrbVariant;
18
+ label?: string;
19
+ }) {
20
+ const messages = useUiMessages();
21
+ return (
22
+ <span
23
+ data-slot="spinner"
24
+ className={cn("inline-flex shrink-0", className)}
25
+ {...props}
26
+ >
27
+ <Orb variant={variant} size={size} label={label ?? messages.loading} />
28
+ </span>
29
+ );
30
+ }
31
+
32
+ export { Spinner };
@@ -1,31 +1,31 @@
1
- "use client";
2
-
3
- import { Switch as SwitchPrimitive } from "@base-ui/react/switch";
4
- import { cn } from "cn";
5
-
6
- function Switch({
7
- className,
8
- size = "default",
9
- ...props
10
- }: SwitchPrimitive.Root.Props & {
11
- size?: "sm" | "default";
12
- }) {
13
- return (
14
- <SwitchPrimitive.Root
15
- data-slot="switch"
16
- data-size={size}
17
- className={cn(
18
- "peer group/switch relative inline-flex shrink-0 items-center rounded-full border-2 transition-all outline-none group-has-[:focus-visible]/field-label:ring-0 after:absolute after:-inset-x-3 after:-inset-y-2 focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/30 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 data-[size=default]:h-5 data-[size=default]:w-11 data-[size=sm]:h-4 data-[size=sm]:w-7 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 data-checked:border-primary data-checked:bg-primary group-has-[:focus-visible]/field-label:data-checked:border-primary data-unchecked:border-transparent data-unchecked:bg-input/90 group-has-[:focus-visible]/field-label:data-unchecked:border-transparent data-disabled:cursor-not-allowed data-disabled:opacity-50",
19
- className,
20
- )}
21
- {...props}
22
- >
23
- <SwitchPrimitive.Thumb
24
- data-slot="switch-thumb"
25
- className="pointer-events-none block rounded-full bg-background shadow-sm ring-0 transition-transform not-dark:bg-clip-padding group-data-[size=default]/switch:h-4 group-data-[size=default]/switch:w-6 group-data-[size=sm]/switch:h-3 group-data-[size=sm]/switch:w-4 data-checked:translate-x-[calc(100%-8px)] dark:data-checked:bg-primary-foreground data-unchecked:translate-x-0 dark:data-unchecked:bg-foreground"
26
- />
27
- </SwitchPrimitive.Root>
28
- );
29
- }
30
-
31
- export { Switch };
1
+ "use client";
2
+
3
+ import { Switch as SwitchPrimitive } from "@base-ui/react/switch";
4
+ import { cn } from "cn";
5
+
6
+ function Switch({
7
+ className,
8
+ size = "default",
9
+ ...props
10
+ }: SwitchPrimitive.Root.Props & {
11
+ size?: "sm" | "default";
12
+ }) {
13
+ return (
14
+ <SwitchPrimitive.Root
15
+ data-slot="switch"
16
+ data-size={size}
17
+ className={cn(
18
+ "peer group/switch relative inline-flex shrink-0 items-center rounded-full border-2 transition-all outline-none group-has-[:focus-visible]/field-label:ring-0 after:absolute after:-inset-x-3 after:-inset-y-2 focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/30 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 data-[size=default]:h-5 data-[size=default]:w-11 data-[size=sm]:h-4 data-[size=sm]:w-7 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 data-checked:border-primary data-checked:bg-primary group-has-[:focus-visible]/field-label:data-checked:border-primary data-unchecked:border-transparent data-unchecked:bg-input/90 group-has-[:focus-visible]/field-label:data-unchecked:border-transparent data-disabled:cursor-not-allowed data-disabled:opacity-50",
19
+ className,
20
+ )}
21
+ {...props}
22
+ >
23
+ <SwitchPrimitive.Thumb
24
+ data-slot="switch-thumb"
25
+ className="pointer-events-none block rounded-full bg-background shadow-sm ring-0 transition-transform not-dark:bg-clip-padding group-data-[size=default]/switch:h-4 group-data-[size=default]/switch:w-6 group-data-[size=sm]/switch:h-3 group-data-[size=sm]/switch:w-4 data-checked:translate-x-[calc(100%-8px)] dark:data-checked:bg-primary-foreground data-unchecked:translate-x-0 dark:data-unchecked:bg-foreground"
26
+ />
27
+ </SwitchPrimitive.Root>
28
+ );
29
+ }
30
+
31
+ export { Switch };
@@ -1,121 +1,121 @@
1
- "use client";
2
-
3
- import * as React from "react";
4
- import { cn } from "cn";
5
-
6
- // Majapi UI: `containerClassName` styles the scroll container (e.g. to fill
7
- // the available height and scroll vertically).
8
- function Table({
9
- className,
10
- containerClassName,
11
- ...props
12
- }: React.ComponentProps<"table"> & { containerClassName?: string }) {
13
- return (
14
- <div
15
- data-slot="table-container"
16
- className={cn("relative w-full overflow-x-auto", containerClassName)}
17
- >
18
- <table
19
- data-slot="table"
20
- className={cn("w-full caption-bottom text-sm", className)}
21
- {...props}
22
- />
23
- </div>
24
- );
25
- }
26
-
27
- function TableHeader({ className, ...props }: React.ComponentProps<"thead">) {
28
- return (
29
- <thead
30
- data-slot="table-header"
31
- className={cn("[&_tr]:border-b", className)}
32
- {...props}
33
- />
34
- );
35
- }
36
-
37
- function TableBody({ className, ...props }: React.ComponentProps<"tbody">) {
38
- return (
39
- <tbody
40
- data-slot="table-body"
41
- className={cn("[&_tr:last-child]:border-0", className)}
42
- {...props}
43
- />
44
- );
45
- }
46
-
47
- function TableFooter({ className, ...props }: React.ComponentProps<"tfoot">) {
48
- return (
49
- <tfoot
50
- data-slot="table-footer"
51
- className={cn(
52
- "border-t bg-muted/50 font-medium [&>tr]:last:border-b-0",
53
- className,
54
- )}
55
- {...props}
56
- />
57
- );
58
- }
59
-
60
- function TableRow({ className, ...props }: React.ComponentProps<"tr">) {
61
- return (
62
- <tr
63
- data-slot="table-row"
64
- className={cn(
65
- "border-b transition-colors hover:bg-muted/50 has-aria-expanded:bg-muted/50 data-[state=selected]:bg-muted",
66
- className,
67
- )}
68
- {...props}
69
- />
70
- );
71
- }
72
-
73
- function TableHead({ className, ...props }: React.ComponentProps<"th">) {
74
- return (
75
- <th
76
- data-slot="table-head"
77
- className={cn(
78
- "h-12 px-3 text-left align-middle font-medium whitespace-nowrap text-foreground [&:has([role=checkbox])]:pr-0",
79
- className,
80
- )}
81
- {...props}
82
- />
83
- );
84
- }
85
-
86
- function TableCell({ className, ...props }: React.ComponentProps<"td">) {
87
- return (
88
- <td
89
- data-slot="table-cell"
90
- className={cn(
91
- "p-3 align-middle whitespace-nowrap [&:has([role=checkbox])]:pr-0",
92
- className,
93
- )}
94
- {...props}
95
- />
96
- );
97
- }
98
-
99
- function TableCaption({
100
- className,
101
- ...props
102
- }: React.ComponentProps<"caption">) {
103
- return (
104
- <caption
105
- data-slot="table-caption"
106
- className={cn("mt-4 text-sm text-muted-foreground", className)}
107
- {...props}
108
- />
109
- );
110
- }
111
-
112
- export {
113
- Table,
114
- TableHeader,
115
- TableBody,
116
- TableFooter,
117
- TableHead,
118
- TableRow,
119
- TableCell,
120
- TableCaption,
121
- };
1
+ "use client";
2
+
3
+ import * as React from "react";
4
+ import { cn } from "cn";
5
+
6
+ // Majapi UI: `containerClassName` styles the scroll container (e.g. to fill
7
+ // the available height and scroll vertically).
8
+ function Table({
9
+ className,
10
+ containerClassName,
11
+ ...props
12
+ }: React.ComponentProps<"table"> & { containerClassName?: string }) {
13
+ return (
14
+ <div
15
+ data-slot="table-container"
16
+ className={cn("relative w-full overflow-x-auto", containerClassName)}
17
+ >
18
+ <table
19
+ data-slot="table"
20
+ className={cn("w-full caption-bottom text-sm", className)}
21
+ {...props}
22
+ />
23
+ </div>
24
+ );
25
+ }
26
+
27
+ function TableHeader({ className, ...props }: React.ComponentProps<"thead">) {
28
+ return (
29
+ <thead
30
+ data-slot="table-header"
31
+ className={cn("[&_tr]:border-b", className)}
32
+ {...props}
33
+ />
34
+ );
35
+ }
36
+
37
+ function TableBody({ className, ...props }: React.ComponentProps<"tbody">) {
38
+ return (
39
+ <tbody
40
+ data-slot="table-body"
41
+ className={cn("[&_tr:last-child]:border-0", className)}
42
+ {...props}
43
+ />
44
+ );
45
+ }
46
+
47
+ function TableFooter({ className, ...props }: React.ComponentProps<"tfoot">) {
48
+ return (
49
+ <tfoot
50
+ data-slot="table-footer"
51
+ className={cn(
52
+ "border-t bg-muted/50 font-medium [&>tr]:last:border-b-0",
53
+ className,
54
+ )}
55
+ {...props}
56
+ />
57
+ );
58
+ }
59
+
60
+ function TableRow({ className, ...props }: React.ComponentProps<"tr">) {
61
+ return (
62
+ <tr
63
+ data-slot="table-row"
64
+ className={cn(
65
+ "border-b transition-colors hover:bg-muted/50 has-aria-expanded:bg-muted/50 data-[state=selected]:bg-muted",
66
+ className,
67
+ )}
68
+ {...props}
69
+ />
70
+ );
71
+ }
72
+
73
+ function TableHead({ className, ...props }: React.ComponentProps<"th">) {
74
+ return (
75
+ <th
76
+ data-slot="table-head"
77
+ className={cn(
78
+ "h-12 px-3 text-left align-middle font-medium whitespace-nowrap text-foreground [&:has([role=checkbox])]:pr-0",
79
+ className,
80
+ )}
81
+ {...props}
82
+ />
83
+ );
84
+ }
85
+
86
+ function TableCell({ className, ...props }: React.ComponentProps<"td">) {
87
+ return (
88
+ <td
89
+ data-slot="table-cell"
90
+ className={cn(
91
+ "p-3 align-middle whitespace-nowrap [&:has([role=checkbox])]:pr-0",
92
+ className,
93
+ )}
94
+ {...props}
95
+ />
96
+ );
97
+ }
98
+
99
+ function TableCaption({
100
+ className,
101
+ ...props
102
+ }: React.ComponentProps<"caption">) {
103
+ return (
104
+ <caption
105
+ data-slot="table-caption"
106
+ className={cn("mt-4 text-sm text-muted-foreground", className)}
107
+ {...props}
108
+ />
109
+ );
110
+ }
111
+
112
+ export {
113
+ Table,
114
+ TableHeader,
115
+ TableBody,
116
+ TableFooter,
117
+ TableHead,
118
+ TableRow,
119
+ TableCell,
120
+ TableCaption,
121
+ };