@majapisoftwares/ui 5.0.0 → 6.1.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.
Files changed (68) hide show
  1. package/AGENTS.md +11 -1
  2. package/README.md +19 -11
  3. package/THIRD_PARTY_NOTICES.md +25 -1
  4. package/components.json +1 -1
  5. package/docs/MIGRATION-6.0.md +33 -0
  6. package/docs/STANDARDS.md +11 -6
  7. package/docs/VERSIONING.md +28 -0
  8. package/package.json +19 -10
  9. package/scripts/check-standards.mjs +2 -0
  10. package/src/components/ui/accordion.tsx +82 -0
  11. package/src/components/ui/alert-dialog.tsx +187 -0
  12. package/src/components/ui/alert.tsx +2 -2
  13. package/src/components/ui/avatar.tsx +108 -0
  14. package/src/components/ui/badge.tsx +1 -1
  15. package/src/components/ui/breadcrumb.tsx +125 -0
  16. package/src/components/ui/button-group.tsx +89 -0
  17. package/src/components/ui/button.tsx +12 -16
  18. package/src/components/ui/card.tsx +101 -0
  19. package/src/components/ui/checkbox.tsx +27 -0
  20. package/src/components/ui/collapsible.tsx +21 -0
  21. package/src/components/ui/context-menu.tsx +271 -0
  22. package/src/components/ui/dialog.tsx +161 -0
  23. package/src/components/ui/dropdown-menu.tsx +271 -0
  24. package/src/components/ui/empty.tsx +4 -4
  25. package/src/components/ui/field.tsx +6 -6
  26. package/src/components/ui/hover-card.tsx +50 -0
  27. package/src/components/ui/input-group.tsx +155 -0
  28. package/src/components/ui/input.tsx +1 -1
  29. package/src/components/ui/kbd.tsx +28 -0
  30. package/src/components/ui/orb.css +1002 -0
  31. package/src/components/ui/orb.tsx +752 -0
  32. package/src/components/ui/popover.tsx +89 -0
  33. package/src/components/ui/progress.tsx +82 -0
  34. package/src/components/ui/radio-group.tsx +37 -0
  35. package/src/components/ui/scroll-area.tsx +54 -0
  36. package/src/components/ui/select.tsx +204 -0
  37. package/src/components/ui/sheet.tsx +8 -6
  38. package/src/components/ui/sidebar.tsx +1035 -0
  39. package/src/components/ui/skeleton.tsx +1 -1
  40. package/src/components/ui/slider.tsx +53 -0
  41. package/src/components/ui/sonner.tsx +44 -0
  42. package/src/components/ui/spinner.tsx +32 -0
  43. package/src/components/ui/switch.tsx +31 -0
  44. package/src/components/ui/table.tsx +10 -4
  45. package/src/components/ui/tabs.tsx +2 -2
  46. package/src/components/ui/textarea.tsx +19 -0
  47. package/src/components/ui/toggle-group.tsx +89 -0
  48. package/src/components/ui/toggle.tsx +44 -0
  49. package/src/components/ui/tooltip.tsx +65 -0
  50. package/src/hooks/use-mobile.ts +20 -0
  51. package/src/lib/i18n.tsx +109 -0
  52. package/src/patterns/app-shell.tsx +162 -53
  53. package/src/patterns/data-table.tsx +2441 -180
  54. package/src/patterns/details-sheet.tsx +10 -7
  55. package/src/patterns/filter-tabs.tsx +53 -0
  56. package/src/patterns/majapi-logo.tsx +32 -0
  57. package/src/patterns/page-header.tsx +44 -22
  58. package/src/patterns/preferences.tsx +172 -0
  59. package/src/patterns/status-badge.tsx +4 -3
  60. package/src/patterns/user-menu.tsx +218 -0
  61. package/src/patterns/workspace-switcher.tsx +173 -0
  62. package/src/styles.css +41 -341
  63. package/src/templates/admin-dashboard.tsx +14 -33
  64. package/src/templates/auth-layout.tsx +18 -24
  65. package/src/templates/login-form.tsx +97 -65
  66. package/src/templates/signup-form.tsx +139 -99
  67. package/src/components/ui/native-select.tsx +0 -66
  68. package/src/vite-env.d.ts +0 -1
@@ -0,0 +1,108 @@
1
+ "use client";
2
+
3
+ import * as React from "react";
4
+ import { Avatar as AvatarPrimitive } from "@base-ui/react/avatar";
5
+ import { cn } from "cn";
6
+
7
+ function Avatar({
8
+ className,
9
+ size = "default",
10
+ ...props
11
+ }: AvatarPrimitive.Root.Props & {
12
+ size?: "default" | "sm" | "lg";
13
+ }) {
14
+ return (
15
+ <AvatarPrimitive.Root
16
+ data-slot="avatar"
17
+ data-size={size}
18
+ className={cn(
19
+ "group/avatar relative flex size-8 shrink-0 rounded-full select-none after:absolute after:inset-0 after:rounded-full after:border after:border-border after:mix-blend-darken data-[size=lg]:size-10 data-[size=sm]:size-6 dark:after:mix-blend-lighten",
20
+ className,
21
+ )}
22
+ {...props}
23
+ />
24
+ );
25
+ }
26
+
27
+ function AvatarImage({ className, ...props }: AvatarPrimitive.Image.Props) {
28
+ return (
29
+ <AvatarPrimitive.Image
30
+ data-slot="avatar-image"
31
+ className={cn(
32
+ "aspect-square size-full rounded-full object-cover",
33
+ className,
34
+ )}
35
+ {...props}
36
+ />
37
+ );
38
+ }
39
+
40
+ function AvatarFallback({
41
+ className,
42
+ ...props
43
+ }: AvatarPrimitive.Fallback.Props) {
44
+ return (
45
+ <AvatarPrimitive.Fallback
46
+ data-slot="avatar-fallback"
47
+ className={cn(
48
+ "flex size-full items-center justify-center rounded-full bg-muted text-sm text-muted-foreground group-data-[size=sm]/avatar:text-xs",
49
+ className,
50
+ )}
51
+ {...props}
52
+ />
53
+ );
54
+ }
55
+
56
+ function AvatarBadge({ className, ...props }: React.ComponentProps<"span">) {
57
+ return (
58
+ <span
59
+ data-slot="avatar-badge"
60
+ className={cn(
61
+ "absolute right-0 bottom-0 z-10 inline-flex items-center justify-center rounded-full bg-primary text-primary-foreground bg-blend-color ring-2 ring-background select-none",
62
+ "group-data-[size=sm]/avatar:size-2 group-data-[size=sm]/avatar:[&>svg]:hidden",
63
+ "group-data-[size=default]/avatar:size-2.5 group-data-[size=default]/avatar:[&>svg]:size-2",
64
+ "group-data-[size=lg]/avatar:size-3 group-data-[size=lg]/avatar:[&>svg]:size-2",
65
+ className,
66
+ )}
67
+ {...props}
68
+ />
69
+ );
70
+ }
71
+
72
+ function AvatarGroup({ className, ...props }: React.ComponentProps<"div">) {
73
+ return (
74
+ <div
75
+ data-slot="avatar-group"
76
+ className={cn(
77
+ "group/avatar-group flex -space-x-2 *:data-[slot=avatar]:ring-2 *:data-[slot=avatar]:ring-background",
78
+ className,
79
+ )}
80
+ {...props}
81
+ />
82
+ );
83
+ }
84
+
85
+ function AvatarGroupCount({
86
+ className,
87
+ ...props
88
+ }: React.ComponentProps<"div">) {
89
+ return (
90
+ <div
91
+ data-slot="avatar-group-count"
92
+ className={cn(
93
+ "relative flex size-8 shrink-0 items-center justify-center rounded-full bg-muted text-sm text-muted-foreground ring-2 ring-background group-has-data-[size=lg]/avatar-group:size-10 group-has-data-[size=sm]/avatar-group:size-6 [&>svg]:size-4 group-has-data-[size=lg]/avatar-group:[&>svg]:size-5 group-has-data-[size=sm]/avatar-group:[&>svg]:size-3",
94
+ className,
95
+ )}
96
+ {...props}
97
+ />
98
+ );
99
+ }
100
+
101
+ export {
102
+ Avatar,
103
+ AvatarImage,
104
+ AvatarFallback,
105
+ AvatarGroup,
106
+ AvatarGroupCount,
107
+ AvatarBadge,
108
+ };
@@ -6,7 +6,7 @@ import { cva, type VariantProps } from "class-variance-authority";
6
6
  import { cn } from "cn";
7
7
 
8
8
  const badgeVariants = cva(
9
- "group/badge inline-flex h-5 w-fit shrink-0 items-center justify-center gap-1 overflow-hidden rounded-4xl border border-transparent px-2 py-0.5 text-xs font-medium whitespace-nowrap transition-all focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&>svg]:pointer-events-none [&>svg]:size-3!",
9
+ "group/badge inline-flex h-5 w-fit shrink-0 items-center justify-center gap-1 overflow-hidden rounded-3xl border border-transparent px-2 py-0.5 text-xs font-medium whitespace-nowrap transition-all focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&>svg]:pointer-events-none [&>svg]:size-3!",
10
10
  {
11
11
  variants: {
12
12
  variant: {
@@ -0,0 +1,125 @@
1
+ "use client";
2
+
3
+ import * as React from "react";
4
+ import { mergeProps } from "@base-ui/react/merge-props";
5
+ import { useRender } from "@base-ui/react/use-render";
6
+ import { cn } from "cn";
7
+ import { useUiMessages } from "../../lib/i18n";
8
+ import { ChevronRightIcon, MoreHorizontalIcon } from "lucide-react";
9
+
10
+ function Breadcrumb({ className, ...props }: React.ComponentProps<"nav">) {
11
+ return (
12
+ <nav
13
+ aria-label="breadcrumb"
14
+ data-slot="breadcrumb"
15
+ className={cn(className)}
16
+ {...props}
17
+ />
18
+ );
19
+ }
20
+
21
+ function BreadcrumbList({ className, ...props }: React.ComponentProps<"ol">) {
22
+ return (
23
+ <ol
24
+ data-slot="breadcrumb-list"
25
+ className={cn(
26
+ "flex flex-wrap items-center gap-1.5 text-sm wrap-break-word text-muted-foreground sm:gap-2.5",
27
+ className,
28
+ )}
29
+ {...props}
30
+ />
31
+ );
32
+ }
33
+
34
+ function BreadcrumbItem({ className, ...props }: React.ComponentProps<"li">) {
35
+ return (
36
+ <li
37
+ data-slot="breadcrumb-item"
38
+ className={cn("inline-flex items-center gap-1.5", className)}
39
+ {...props}
40
+ />
41
+ );
42
+ }
43
+
44
+ function BreadcrumbLink({
45
+ className,
46
+ render,
47
+ ...props
48
+ }: useRender.ComponentProps<"a">) {
49
+ return useRender({
50
+ defaultTagName: "a",
51
+ props: mergeProps<"a">(
52
+ {
53
+ className: cn("transition-colors hover:text-foreground", className),
54
+ },
55
+ props,
56
+ ),
57
+ render,
58
+ state: {
59
+ slot: "breadcrumb-link",
60
+ },
61
+ });
62
+ }
63
+
64
+ function BreadcrumbPage({ className, ...props }: React.ComponentProps<"span">) {
65
+ return (
66
+ <span
67
+ data-slot="breadcrumb-page"
68
+ role="link"
69
+ aria-disabled="true"
70
+ aria-current="page"
71
+ className={cn("font-normal text-foreground", className)}
72
+ {...props}
73
+ />
74
+ );
75
+ }
76
+
77
+ function BreadcrumbSeparator({
78
+ children,
79
+ className,
80
+ ...props
81
+ }: React.ComponentProps<"li">) {
82
+ return (
83
+ <li
84
+ data-slot="breadcrumb-separator"
85
+ role="presentation"
86
+ aria-hidden="true"
87
+ className={cn("[&>svg]:size-3.5", className)}
88
+ {...props}
89
+ >
90
+ {children ?? <ChevronRightIcon />}
91
+ </li>
92
+ );
93
+ }
94
+
95
+ function BreadcrumbEllipsis({
96
+ className,
97
+ ...props
98
+ }: React.ComponentProps<"span">) {
99
+ const messages = useUiMessages();
100
+ return (
101
+ <span
102
+ data-slot="breadcrumb-ellipsis"
103
+ role="presentation"
104
+ aria-hidden="true"
105
+ className={cn(
106
+ "flex size-5 items-center justify-center [&>svg]:size-4",
107
+ className,
108
+ )}
109
+ {...props}
110
+ >
111
+ <MoreHorizontalIcon />
112
+ <span className="sr-only">{messages.more}</span>
113
+ </span>
114
+ );
115
+ }
116
+
117
+ export {
118
+ Breadcrumb,
119
+ BreadcrumbList,
120
+ BreadcrumbItem,
121
+ BreadcrumbLink,
122
+ BreadcrumbPage,
123
+ BreadcrumbSeparator,
124
+ BreadcrumbEllipsis,
125
+ };
@@ -0,0 +1,89 @@
1
+ "use client";
2
+
3
+ import { mergeProps } from "@base-ui/react/merge-props";
4
+ import { useRender } from "@base-ui/react/use-render";
5
+ import { cva, type VariantProps } from "class-variance-authority";
6
+ import { cn } from "cn";
7
+
8
+ import { Separator } from "./separator";
9
+
10
+ const buttonGroupVariants = cva(
11
+ "flex w-fit items-stretch *:focus-visible:relative *:focus-visible:z-10 has-[>[data-slot=button-group]]:gap-2 has-[>[data-variant=outline]]:*:data-[slot=input-group]:border-border has-[>[data-variant=outline]]:*:data-[slot=select-trigger]:border-border has-[>[data-variant=outline]]:[&>[data-slot=input-group]:has(:focus-visible)]:border-ring has-[>[data-variant=outline]]:[&>[data-slot=select-trigger]:focus-visible]:border-ring has-[select[aria-hidden=true]:last-child]:[&>[data-slot=select-trigger]:last-of-type]:rounded-r-4xl [&>[data-slot=select-trigger]:not([class*='w-'])]:w-fit [&>input]:flex-1 has-[>[data-variant=outline]]:[&>input]:border-border has-[>[data-variant=outline]]:[&>input:focus-visible]:border-ring",
12
+ {
13
+ variants: {
14
+ orientation: {
15
+ horizontal:
16
+ "*:data-slot:rounded-r-none [&>[data-slot]:not(:has(~[data-slot]))]:rounded-r-4xl! [&>[data-slot]~[data-slot]]:rounded-l-none [&>[data-slot]~[data-slot]]:border-l-0",
17
+ vertical:
18
+ "flex-col *:data-slot:rounded-b-none [&>[data-slot]:not(:has(~[data-slot]))]:rounded-b-4xl! [&>[data-slot]~[data-slot]]:rounded-t-none [&>[data-slot]~[data-slot]]:border-t-0",
19
+ },
20
+ },
21
+ defaultVariants: {
22
+ orientation: "horizontal",
23
+ },
24
+ },
25
+ );
26
+
27
+ function ButtonGroup({
28
+ className,
29
+ orientation,
30
+ ...props
31
+ }: React.ComponentProps<"div"> & VariantProps<typeof buttonGroupVariants>) {
32
+ return (
33
+ <div
34
+ role="group"
35
+ data-slot="button-group"
36
+ data-orientation={orientation}
37
+ className={cn(buttonGroupVariants({ orientation }), className)}
38
+ {...props}
39
+ />
40
+ );
41
+ }
42
+
43
+ function ButtonGroupText({
44
+ className,
45
+ render,
46
+ ...props
47
+ }: useRender.ComponentProps<"div">) {
48
+ return useRender({
49
+ defaultTagName: "div",
50
+ props: mergeProps<"div">(
51
+ {
52
+ className: cn(
53
+ "flex items-center gap-2 rounded-4xl border bg-muted px-2.5 text-sm font-medium [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4",
54
+ className,
55
+ ),
56
+ },
57
+ props,
58
+ ),
59
+ render,
60
+ state: {
61
+ slot: "button-group-text",
62
+ },
63
+ });
64
+ }
65
+
66
+ function ButtonGroupSeparator({
67
+ className,
68
+ orientation = "vertical",
69
+ ...props
70
+ }: React.ComponentProps<typeof Separator>) {
71
+ return (
72
+ <Separator
73
+ data-slot="button-group-separator"
74
+ orientation={orientation}
75
+ className={cn(
76
+ "relative self-stretch bg-input data-horizontal:mx-px data-horizontal:w-auto data-vertical:my-px data-vertical:h-auto",
77
+ className,
78
+ )}
79
+ {...props}
80
+ />
81
+ );
82
+ }
83
+
84
+ export {
85
+ ButtonGroup,
86
+ ButtonGroupSeparator,
87
+ ButtonGroupText,
88
+ buttonGroupVariants,
89
+ };
@@ -5,15 +5,13 @@ import { cva, type VariantProps } from "class-variance-authority";
5
5
  import { cn } from "cn";
6
6
 
7
7
  const buttonVariants = cva(
8
- "group/button inline-flex shrink-0 items-center justify-center rounded-lg border border-transparent bg-clip-padding text-sm font-medium whitespace-nowrap transition-all outline-none select-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 active:not-aria-[haspopup]:translate-y-px disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
8
+ "group/button inline-flex shrink-0 items-center justify-center rounded-4xl border border-transparent bg-clip-padding text-sm font-medium whitespace-nowrap transition-all outline-none select-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/30 active:not-aria-[haspopup]:translate-y-px disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
9
9
  {
10
10
  variants: {
11
11
  variant: {
12
- navigation:
13
- "bg-transparent text-sidebar-foreground hover:bg-sidebar-accent hover:text-sidebar-accent-foreground data-[active=true]:bg-sidebar-accent data-[active=true]:text-sidebar-accent-foreground",
14
12
  default: "bg-primary text-primary-foreground hover:bg-primary/80",
15
13
  outline:
16
- "border-border bg-background hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:border-input dark:bg-input/30 dark:hover:bg-input/50",
14
+ "border-border bg-background hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:bg-transparent dark:hover:bg-input/30",
17
15
  secondary:
18
16
  "bg-secondary text-secondary-foreground hover:bg-[color-mix(in_oklch,var(--secondary),var(--foreground)_5%)] aria-expanded:bg-secondary aria-expanded:text-secondary-foreground",
19
17
  ghost:
@@ -24,16 +22,14 @@ const buttonVariants = cva(
24
22
  },
25
23
  size: {
26
24
  default:
27
- "h-8 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2",
28
- xs: "h-6 gap-1 rounded-[min(var(--radius-md),10px)] px-2 text-xs in-data-[slot=button-group]:rounded-lg has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3",
29
- sm: "h-7 gap-1 rounded-[min(var(--radius-md),12px)] px-2.5 text-[0.8rem] in-data-[slot=button-group]:rounded-lg has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3.5",
30
- lg: "h-9 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2",
31
- icon: "size-8",
32
- "icon-xs":
33
- "size-6 rounded-[min(var(--radius-md),10px)] in-data-[slot=button-group]:rounded-lg [&_svg:not([class*='size-'])]:size-3",
34
- "icon-sm":
35
- "size-7 rounded-[min(var(--radius-md),12px)] in-data-[slot=button-group]:rounded-lg",
36
- "icon-lg": "size-9",
25
+ "h-9 gap-1.5 px-3 has-data-[icon=inline-end]:pr-2.5 has-data-[icon=inline-start]:pl-2.5",
26
+ xs: "h-6 gap-1 px-2.5 text-xs has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2 [&_svg:not([class*='size-'])]:size-3",
27
+ sm: "h-8 gap-1 px-3 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2",
28
+ lg: "h-10 gap-1.5 px-4 has-data-[icon=inline-end]:pr-3 has-data-[icon=inline-start]:pl-3",
29
+ icon: "size-9",
30
+ "icon-xs": "size-6 [&_svg:not([class*='size-'])]:size-3",
31
+ "icon-sm": "size-8",
32
+ "icon-lg": "size-10",
37
33
  },
38
34
  },
39
35
  defaultVariants: {
@@ -47,13 +43,13 @@ function Button({
47
43
  className,
48
44
  variant = "default",
49
45
  size = "default",
50
- type = "button",
51
46
  ...props
52
47
  }: ButtonPrimitive.Props & VariantProps<typeof buttonVariants>) {
53
48
  return (
54
49
  <ButtonPrimitive
55
50
  data-slot="button"
56
- type={type}
51
+ // Avoid accidental form submits; forms opt in with type="submit".
52
+ type="button"
57
53
  className={cn(buttonVariants({ variant, size, className }))}
58
54
  {...props}
59
55
  />
@@ -0,0 +1,101 @@
1
+ "use client";
2
+
3
+ import * as React from "react";
4
+ import { cn } from "cn";
5
+
6
+ function Card({
7
+ className,
8
+ size = "default",
9
+ ...props
10
+ }: React.ComponentProps<"div"> & { size?: "default" | "sm" }) {
11
+ return (
12
+ <div
13
+ data-slot="card"
14
+ data-size={size}
15
+ className={cn(
16
+ "group/card flex flex-col gap-(--card-spacing) overflow-hidden rounded-4xl bg-card py-(--card-spacing) text-sm text-card-foreground shadow-md ring-1 ring-foreground/5 [--card-spacing:--spacing(6)] has-[>img:first-child]:pt-0 data-[size=sm]:[--card-spacing:--spacing(4)] dark:ring-foreground/10 *:[img:first-child]:rounded-t-4xl *:[img:last-child]:rounded-b-4xl",
17
+ className,
18
+ )}
19
+ {...props}
20
+ />
21
+ );
22
+ }
23
+
24
+ function CardHeader({ className, ...props }: React.ComponentProps<"div">) {
25
+ return (
26
+ <div
27
+ data-slot="card-header"
28
+ className={cn(
29
+ "group/card-header @container/card-header grid auto-rows-min items-start gap-1.5 rounded-t-4xl px-(--card-spacing) has-data-[slot=card-action]:grid-cols-[1fr_auto] has-data-[slot=card-description]:grid-rows-[auto_auto] [.border-b]:pb-(--card-spacing)",
30
+ className,
31
+ )}
32
+ {...props}
33
+ />
34
+ );
35
+ }
36
+
37
+ function CardTitle({ className, ...props }: React.ComponentProps<"div">) {
38
+ return (
39
+ <div
40
+ data-slot="card-title"
41
+ className={cn("font-heading text-base font-medium", className)}
42
+ {...props}
43
+ />
44
+ );
45
+ }
46
+
47
+ function CardDescription({ className, ...props }: React.ComponentProps<"div">) {
48
+ return (
49
+ <div
50
+ data-slot="card-description"
51
+ className={cn("text-sm text-muted-foreground", className)}
52
+ {...props}
53
+ />
54
+ );
55
+ }
56
+
57
+ function CardAction({ className, ...props }: React.ComponentProps<"div">) {
58
+ return (
59
+ <div
60
+ data-slot="card-action"
61
+ className={cn(
62
+ "col-start-2 row-span-2 row-start-1 self-start justify-self-end",
63
+ className,
64
+ )}
65
+ {...props}
66
+ />
67
+ );
68
+ }
69
+
70
+ function CardContent({ className, ...props }: React.ComponentProps<"div">) {
71
+ return (
72
+ <div
73
+ data-slot="card-content"
74
+ className={cn("px-(--card-spacing)", className)}
75
+ {...props}
76
+ />
77
+ );
78
+ }
79
+
80
+ function CardFooter({ className, ...props }: React.ComponentProps<"div">) {
81
+ return (
82
+ <div
83
+ data-slot="card-footer"
84
+ className={cn(
85
+ "flex items-center rounded-b-4xl px-(--card-spacing) [.border-t]:pt-(--card-spacing)",
86
+ className,
87
+ )}
88
+ {...props}
89
+ />
90
+ );
91
+ }
92
+
93
+ export {
94
+ Card,
95
+ CardHeader,
96
+ CardFooter,
97
+ CardTitle,
98
+ CardAction,
99
+ CardDescription,
100
+ CardContent,
101
+ };
@@ -0,0 +1,27 @@
1
+ "use client";
2
+
3
+ import { Checkbox as CheckboxPrimitive } from "@base-ui/react/checkbox";
4
+ import { cn } from "cn";
5
+ import { CheckIcon } from "lucide-react";
6
+
7
+ function Checkbox({ className, ...props }: CheckboxPrimitive.Root.Props) {
8
+ return (
9
+ <CheckboxPrimitive.Root
10
+ data-slot="checkbox"
11
+ className={cn(
12
+ "peer relative flex size-4 shrink-0 items-center justify-center rounded-[5px] border border-transparent bg-input/90 transition-shadow outline-none group-has-disabled/field:opacity-50 group-has-[:focus-visible]/field-label:ring-0 group-has-[:focus-visible]/field-label:not-data-checked:border-transparent after:absolute after:-inset-x-3 after:-inset-y-2 focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/30 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 aria-invalid:aria-checked:border-primary dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 data-checked:border-primary data-checked:bg-primary data-checked:text-primary-foreground group-has-[:focus-visible]/field-label:data-checked:border-primary dark:data-checked:bg-primary",
13
+ className,
14
+ )}
15
+ {...props}
16
+ >
17
+ <CheckboxPrimitive.Indicator
18
+ data-slot="checkbox-indicator"
19
+ className="grid place-content-center text-current transition-none [&>svg]:size-3.5"
20
+ >
21
+ <CheckIcon />
22
+ </CheckboxPrimitive.Indicator>
23
+ </CheckboxPrimitive.Root>
24
+ );
25
+ }
26
+
27
+ export { Checkbox };
@@ -0,0 +1,21 @@
1
+ "use client";
2
+
3
+ import { Collapsible as CollapsiblePrimitive } from "@base-ui/react/collapsible";
4
+
5
+ function Collapsible({ ...props }: CollapsiblePrimitive.Root.Props) {
6
+ return <CollapsiblePrimitive.Root data-slot="collapsible" {...props} />;
7
+ }
8
+
9
+ function CollapsibleTrigger({ ...props }: CollapsiblePrimitive.Trigger.Props) {
10
+ return (
11
+ <CollapsiblePrimitive.Trigger data-slot="collapsible-trigger" {...props} />
12
+ );
13
+ }
14
+
15
+ function CollapsibleContent({ ...props }: CollapsiblePrimitive.Panel.Props) {
16
+ return (
17
+ <CollapsiblePrimitive.Panel data-slot="collapsible-content" {...props} />
18
+ );
19
+ }
20
+
21
+ export { Collapsible, CollapsibleTrigger, CollapsibleContent };