@majapisoftwares/ui 5.0.0 → 6.0.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 (66) 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 +158 -53
  53. package/src/patterns/data-table.tsx +2441 -180
  54. package/src/patterns/details-sheet.tsx +8 -6
  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/styles.css +41 -341
  61. package/src/templates/admin-dashboard.tsx +14 -33
  62. package/src/templates/auth-layout.tsx +18 -24
  63. package/src/templates/login-form.tsx +97 -65
  64. package/src/templates/signup-form.tsx +139 -99
  65. package/src/components/ui/native-select.tsx +0 -66
  66. package/src/vite-env.d.ts +0 -1
@@ -8,7 +8,7 @@ function Empty({ className, ...props }: React.ComponentProps<"div">) {
8
8
  <div
9
9
  data-slot="empty"
10
10
  className={cn(
11
- "flex w-full min-w-0 flex-1 flex-col items-center justify-center gap-4 rounded-xl border-dashed p-6 text-center text-balance",
11
+ "flex w-full min-w-0 flex-1 flex-col items-center justify-center gap-4 rounded-2xl border-dashed p-12 text-center text-balance",
12
12
  className,
13
13
  )}
14
14
  {...props}
@@ -32,7 +32,7 @@ const emptyMediaVariants = cva(
32
32
  variants: {
33
33
  variant: {
34
34
  default: "bg-transparent",
35
- icon: "flex size-8 shrink-0 items-center justify-center rounded-lg bg-muted text-foreground [&_svg:not([class*='size-'])]:size-4",
35
+ icon: "flex size-10 shrink-0 items-center justify-center rounded-xl bg-muted text-foreground [&_svg:not([class*='size-'])]:size-5",
36
36
  },
37
37
  },
38
38
  defaultVariants: {
@@ -61,7 +61,7 @@ function EmptyTitle({ className, ...props }: React.ComponentProps<"div">) {
61
61
  <div
62
62
  data-slot="empty-title"
63
63
  className={cn(
64
- "font-heading text-sm font-medium tracking-tight",
64
+ "font-heading text-lg font-medium tracking-tight",
65
65
  className,
66
66
  )}
67
67
  {...props}
@@ -87,7 +87,7 @@ function EmptyContent({ className, ...props }: React.ComponentProps<"div">) {
87
87
  <div
88
88
  data-slot="empty-content"
89
89
  className={cn(
90
- "flex w-full max-w-sm min-w-0 flex-col items-center gap-2.5 text-sm text-balance",
90
+ "flex w-full max-w-sm min-w-0 flex-col items-center gap-4 text-sm text-balance",
91
91
  className,
92
92
  )}
93
93
  {...props}
@@ -12,7 +12,7 @@ function FieldSet({ className, ...props }: React.ComponentProps<"fieldset">) {
12
12
  <fieldset
13
13
  data-slot="field-set"
14
14
  className={cn(
15
- "flex flex-col gap-4 has-[>[data-slot=checkbox-group]]:gap-3 has-[>[data-slot=radio-group]]:gap-3",
15
+ "flex flex-col gap-6 has-[>[data-slot=checkbox-group]]:gap-3 has-[>[data-slot=radio-group]]:gap-3",
16
16
  className,
17
17
  )}
18
18
  {...props}
@@ -30,7 +30,7 @@ function FieldLegend({
30
30
  data-slot="field-legend"
31
31
  data-variant={variant}
32
32
  className={cn(
33
- "mb-1.5 font-medium data-[variant=label]:text-sm data-[variant=legend]:text-base",
33
+ "mb-3 font-medium data-[variant=label]:text-sm data-[variant=legend]:text-base",
34
34
  className,
35
35
  )}
36
36
  {...props}
@@ -43,7 +43,7 @@ function FieldGroup({ className, ...props }: React.ComponentProps<"div">) {
43
43
  <div
44
44
  data-slot="field-group"
45
45
  className={cn(
46
- "group/field-group @container/field-group flex w-full flex-col gap-5 data-[slot=checkbox-group]:gap-3 *:data-[slot=field-group]:gap-4",
46
+ "group/field-group @container/field-group flex w-full flex-col gap-7 data-[slot=checkbox-group]:gap-3 *:data-[slot=field-group]:gap-4",
47
47
  className,
48
48
  )}
49
49
  {...props}
@@ -52,7 +52,7 @@ function FieldGroup({ className, ...props }: React.ComponentProps<"div">) {
52
52
  }
53
53
 
54
54
  const fieldVariants = cva(
55
- "group/field flex w-full gap-2 data-[invalid=true]:text-destructive",
55
+ "group/field flex w-full gap-3 data-[invalid=true]:text-destructive",
56
56
  {
57
57
  variants: {
58
58
  orientation: {
@@ -90,7 +90,7 @@ function FieldContent({ className, ...props }: React.ComponentProps<"div">) {
90
90
  <div
91
91
  data-slot="field-content"
92
92
  className={cn(
93
- "group/field-content flex flex-1 flex-col gap-0.5 leading-snug",
93
+ "group/field-content flex flex-1 flex-col gap-1 leading-snug",
94
94
  className,
95
95
  )}
96
96
  {...props}
@@ -106,7 +106,7 @@ function FieldLabel({
106
106
  <Label
107
107
  data-slot="field-label"
108
108
  className={cn(
109
- "group/field-label peer/field-label flex w-fit gap-2 leading-snug group-data-[disabled=true]/field:opacity-50 has-data-checked:border-primary/30 has-data-checked:bg-primary/5 has-[>[data-slot=field]]:rounded-lg has-[>[data-slot=field]]:border has-[>[data-slot=field]]:not-has-[:disabled,[data-disabled]]:hover:bg-muted/50 has-[>[data-slot=field]]:has-[:focus-visible]:border-ring has-[>[data-slot=field]]:has-[:focus-visible]:ring-3 has-[>[data-slot=field]]:has-[:focus-visible]:ring-ring/50 *:data-[slot=field]:p-2.5 dark:has-data-checked:border-primary/20 dark:has-data-checked:bg-primary/10",
109
+ "group/field-label peer/field-label flex w-fit gap-2 leading-snug group-data-[disabled=true]/field:opacity-50 has-data-checked:bg-input/30 has-[>[data-slot=field]]:rounded-2xl has-[>[data-slot=field]]:border has-[>[data-slot=field]]:not-has-[:disabled,[data-disabled]]:hover:bg-input/40 has-[>[data-slot=field]]:has-[:focus-visible]:border-ring has-[>[data-slot=field]]:has-[:focus-visible]:ring-3 has-[>[data-slot=field]]:has-[:focus-visible]:ring-ring/50 *:data-[slot=field]:p-4",
110
110
  "has-[>[data-slot=field]]:w-full has-[>[data-slot=field]]:flex-col",
111
111
  className,
112
112
  )}
@@ -0,0 +1,50 @@
1
+ "use client";
2
+
3
+ import { PreviewCard as PreviewCardPrimitive } from "@base-ui/react/preview-card";
4
+ import { cn } from "cn";
5
+
6
+ function HoverCard({ ...props }: PreviewCardPrimitive.Root.Props) {
7
+ return <PreviewCardPrimitive.Root data-slot="hover-card" {...props} />;
8
+ }
9
+
10
+ function HoverCardTrigger({ ...props }: PreviewCardPrimitive.Trigger.Props) {
11
+ return (
12
+ <PreviewCardPrimitive.Trigger data-slot="hover-card-trigger" {...props} />
13
+ );
14
+ }
15
+
16
+ function HoverCardContent({
17
+ className,
18
+ side = "bottom",
19
+ sideOffset = 4,
20
+ align = "center",
21
+ alignOffset = 4,
22
+ ...props
23
+ }: PreviewCardPrimitive.Popup.Props &
24
+ Pick<
25
+ PreviewCardPrimitive.Positioner.Props,
26
+ "align" | "alignOffset" | "side" | "sideOffset"
27
+ >) {
28
+ return (
29
+ <PreviewCardPrimitive.Portal data-slot="hover-card-portal">
30
+ <PreviewCardPrimitive.Positioner
31
+ align={align}
32
+ alignOffset={alignOffset}
33
+ side={side}
34
+ sideOffset={sideOffset}
35
+ className="isolate z-50"
36
+ >
37
+ <PreviewCardPrimitive.Popup
38
+ data-slot="hover-card-content"
39
+ className={cn(
40
+ "z-50 w-72 origin-(--transform-origin) rounded-3xl bg-popover p-4 text-sm text-popover-foreground shadow-lg ring-1 ring-foreground/5 outline-hidden duration-100 data-[side=bottom]:slide-in-from-top-2 data-[side=inline-end]:slide-in-from-left-2 data-[side=inline-start]:slide-in-from-right-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 dark:ring-foreground/10 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95",
41
+ className,
42
+ )}
43
+ {...props}
44
+ />
45
+ </PreviewCardPrimitive.Positioner>
46
+ </PreviewCardPrimitive.Portal>
47
+ );
48
+ }
49
+
50
+ export { HoverCard, HoverCardTrigger, HoverCardContent };
@@ -0,0 +1,155 @@
1
+ "use client";
2
+
3
+ import * as React from "react";
4
+ import { cva, type VariantProps } from "class-variance-authority";
5
+ import { cn } from "cn";
6
+
7
+ import { Button } from "./button";
8
+ import { Input } from "./input";
9
+ import { Textarea } from "./textarea";
10
+
11
+ function InputGroup({ className, ...props }: React.ComponentProps<"div">) {
12
+ return (
13
+ <div
14
+ data-slot="input-group"
15
+ role="group"
16
+ className={cn(
17
+ "group/input-group relative flex h-9 w-full min-w-0 items-center rounded-4xl border border-transparent bg-input/50 transition-[color,box-shadow,background-color] outline-none in-data-[slot=combobox-content]:focus-within:border-inherit in-data-[slot=combobox-content]:focus-within:ring-0 has-data-[align=block-end]:rounded-3xl has-data-[align=block-start]:rounded-3xl has-[[data-slot=input-group-control]:focus-visible]:border-ring has-[[data-slot=input-group-control]:focus-visible]:ring-3 has-[[data-slot=input-group-control]:focus-visible]:ring-ring/30 has-[[data-slot][aria-invalid=true]]:border-destructive has-[[data-slot][aria-invalid=true]]:ring-3 has-[[data-slot][aria-invalid=true]]:ring-destructive/20 has-[textarea]:rounded-2xl has-[>[data-align=block-end]]:h-auto has-[>[data-align=block-end]]:flex-col has-[>[data-align=block-start]]:h-auto has-[>[data-align=block-start]]:flex-col has-[>textarea]:h-auto dark:has-[[data-slot][aria-invalid=true]]:ring-destructive/40 has-[>[data-align=block-end]]:[&>input]:pt-3 has-[>[data-align=block-start]]:[&>input]:pb-3 has-[>[data-align=inline-end]]:[&>input]:pr-1.5 has-[>[data-align=inline-start]]:[&>input]:pl-1.5",
18
+ className,
19
+ )}
20
+ {...props}
21
+ />
22
+ );
23
+ }
24
+
25
+ const inputGroupAddonVariants = cva(
26
+ "flex h-auto cursor-text items-center justify-center gap-2 py-2 text-sm font-medium text-muted-foreground select-none group-data-[disabled=true]/input-group:opacity-50 **:data-[slot=kbd]:rounded-3xl **:data-[slot=kbd]:bg-muted-foreground/10 **:data-[slot=kbd]:px-1.5 [&>svg:not([class*='size-'])]:size-4",
27
+ {
28
+ variants: {
29
+ align: {
30
+ "inline-start": "order-first pl-3 has-[>button]:-ml-1 has-[>kbd]:-ml-1",
31
+ "inline-end": "order-last pr-3 has-[>button]:-mr-1 has-[>kbd]:-mr-1",
32
+ "block-start":
33
+ "order-first w-full justify-start px-3 pt-3 group-has-[>input]/input-group:pt-3.5 [.border-b]:pb-3.5",
34
+ "block-end":
35
+ "order-last w-full justify-start px-3 pb-3 group-has-[>input]/input-group:pb-3.5 [.border-t]:pt-3.5",
36
+ },
37
+ },
38
+ defaultVariants: {
39
+ align: "inline-start",
40
+ },
41
+ },
42
+ );
43
+
44
+ function InputGroupAddon({
45
+ className,
46
+ align = "inline-start",
47
+ ...props
48
+ }: React.ComponentProps<"div"> & VariantProps<typeof inputGroupAddonVariants>) {
49
+ return (
50
+ <div
51
+ role="group"
52
+ data-slot="input-group-addon"
53
+ data-align={align}
54
+ className={cn(inputGroupAddonVariants({ align }), className)}
55
+ onClick={(e) => {
56
+ if ((e.target as HTMLElement).closest("button")) {
57
+ return;
58
+ }
59
+ e.currentTarget.parentElement?.querySelector("input")?.focus();
60
+ }}
61
+ {...props}
62
+ />
63
+ );
64
+ }
65
+
66
+ const inputGroupButtonVariants = cva(
67
+ "flex items-center gap-2 rounded-4xl text-sm shadow-none",
68
+ {
69
+ variants: {
70
+ size: {
71
+ xs: "h-6 gap-1 rounded-xl px-1.5 [&>svg:not([class*='size-'])]:size-3.5",
72
+ sm: "",
73
+ "icon-xs": "size-6 rounded-xl p-0 has-[>svg]:p-0",
74
+ "icon-sm": "size-8 p-0 has-[>svg]:p-0",
75
+ },
76
+ },
77
+ defaultVariants: {
78
+ size: "xs",
79
+ },
80
+ },
81
+ );
82
+
83
+ function InputGroupButton({
84
+ className,
85
+ type = "button",
86
+ variant = "ghost",
87
+ size = "xs",
88
+ ...props
89
+ }: Omit<React.ComponentProps<typeof Button>, "size" | "type"> &
90
+ VariantProps<typeof inputGroupButtonVariants> & {
91
+ type?: "button" | "submit" | "reset";
92
+ }) {
93
+ return (
94
+ <Button
95
+ type={type}
96
+ data-size={size}
97
+ variant={variant}
98
+ className={cn(inputGroupButtonVariants({ size }), className)}
99
+ {...props}
100
+ />
101
+ );
102
+ }
103
+
104
+ function InputGroupText({ className, ...props }: React.ComponentProps<"span">) {
105
+ return (
106
+ <span
107
+ className={cn(
108
+ "flex items-center gap-2 text-sm text-muted-foreground [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4",
109
+ className,
110
+ )}
111
+ {...props}
112
+ />
113
+ );
114
+ }
115
+
116
+ function InputGroupInput({
117
+ className,
118
+ ...props
119
+ }: React.ComponentProps<"input">) {
120
+ return (
121
+ <Input
122
+ data-slot="input-group-control"
123
+ className={cn(
124
+ "flex-1 rounded-none border-0 bg-transparent shadow-none ring-0 focus-visible:ring-0 aria-invalid:ring-0 dark:bg-transparent",
125
+ className,
126
+ )}
127
+ {...props}
128
+ />
129
+ );
130
+ }
131
+
132
+ function InputGroupTextarea({
133
+ className,
134
+ ...props
135
+ }: React.ComponentProps<"textarea">) {
136
+ return (
137
+ <Textarea
138
+ data-slot="input-group-control"
139
+ className={cn(
140
+ "flex-1 resize-none rounded-none border-0 bg-transparent py-2.5 shadow-none ring-0 focus-visible:ring-0 aria-invalid:ring-0 dark:bg-transparent",
141
+ className,
142
+ )}
143
+ {...props}
144
+ />
145
+ );
146
+ }
147
+
148
+ export {
149
+ InputGroup,
150
+ InputGroupAddon,
151
+ InputGroupButton,
152
+ InputGroupText,
153
+ InputGroupInput,
154
+ InputGroupTextarea,
155
+ };
@@ -10,7 +10,7 @@ function Input({ className, type, ...props }: React.ComponentProps<"input">) {
10
10
  type={type}
11
11
  data-slot="input"
12
12
  className={cn(
13
- "h-8 w-full min-w-0 rounded-lg border border-input bg-transparent px-2.5 py-1 text-base transition-colors outline-none file:inline-flex file:h-6 file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:pointer-events-none disabled:cursor-not-allowed disabled:bg-input/50 disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 md:text-sm dark:bg-input/30 dark:disabled:bg-input/80 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40",
13
+ "h-9 w-full min-w-0 rounded-3xl border border-transparent bg-input/50 px-3 py-1 text-base transition-[color,box-shadow,background-color] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/30 disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 md:text-sm dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40",
14
14
  className,
15
15
  )}
16
16
  {...props}
@@ -0,0 +1,28 @@
1
+ "use client";
2
+
3
+ import { cn } from "cn";
4
+
5
+ function Kbd({ className, ...props }: React.ComponentProps<"kbd">) {
6
+ return (
7
+ <kbd
8
+ data-slot="kbd"
9
+ className={cn(
10
+ "pointer-events-none inline-flex h-5.5 w-fit min-w-5.5 items-center justify-center gap-1 rounded-lg bg-muted px-1.5 font-sans text-xs font-medium text-muted-foreground select-none in-data-[slot=input-group]:bg-input in-data-[slot=tooltip-content]:bg-background/20 in-data-[slot=tooltip-content]:text-background dark:in-data-[slot=tooltip-content]:bg-background/10 [&_svg:not([class*='size-'])]:size-3",
11
+ className,
12
+ )}
13
+ {...props}
14
+ />
15
+ );
16
+ }
17
+
18
+ function KbdGroup({ className, ...props }: React.ComponentProps<"div">) {
19
+ return (
20
+ <kbd
21
+ data-slot="kbd-group"
22
+ className={cn("inline-flex items-center gap-1", className)}
23
+ {...props}
24
+ />
25
+ );
26
+ }
27
+
28
+ export { Kbd, KbdGroup };