@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
@@ -1,70 +1,175 @@
1
1
  "use client";
2
2
 
3
3
  import type { ReactNode } from "react";
4
- import { Button } from "../components/ui/button";
4
+ import { cn } from "cn";
5
+ import {
6
+ Sidebar,
7
+ SidebarCollapse,
8
+ SidebarContent,
9
+ SidebarFooter,
10
+ SidebarGroup,
11
+ SidebarGroupLabel,
12
+ SidebarHeader,
13
+ SidebarInset,
14
+ SidebarMenu,
15
+ SidebarMenuBadge,
16
+ SidebarMenuButton,
17
+ SidebarMenuItem,
18
+ SidebarProvider,
19
+ SidebarRail,
20
+ SidebarTrigger,
21
+ type SidebarMode,
22
+ } from "../components/ui/sidebar";
23
+ import { Separator } from "../components/ui/separator";
24
+ import { TooltipProvider } from "../components/ui/tooltip";
5
25
 
6
26
  export interface NavigationItem {
7
27
  id: string;
8
28
  label: string;
9
- count?: number;
10
29
  icon?: ReactNode;
30
+ count?: number;
31
+ }
32
+ export interface NavigationGroup {
33
+ label?: string;
34
+ items: NavigationItem[];
35
+ }
36
+
37
+ export interface AppShellProps {
38
+ /** Square logo; stays in place in every sidebar mode. */
39
+ logo: ReactNode;
40
+ /** App name next to the logo; fades out when the sidebar is compact. */
41
+ name: string;
42
+ navigation: NavigationGroup[];
43
+ activeId: string;
44
+ onNavigate: (id: string) => void;
45
+ /** Rendered in the header next to the sidebar trigger, e.g. breadcrumbs. */
46
+ header?: ReactNode;
47
+ /** Rendered at the end of the header, e.g. locale or theme switchers. */
48
+ actions?: ReactNode;
49
+ /** Sidebar footer, e.g. a user menu. */
50
+ footer?: ReactNode;
51
+ defaultMode?: SidebarMode;
52
+ defaultWidth?: number;
53
+ /**
54
+ * Locks the shell to the viewport height so content (e.g. a `DataTable
55
+ * fillHeight`) fills the remaining space and scrolls internally.
56
+ */
57
+ fillHeight?: boolean;
58
+ children: ReactNode;
59
+ }
60
+
61
+ // Items without an icon still need something to show in compact mode.
62
+ function ItemIcon({ item }: { item: NavigationItem }) {
63
+ return (
64
+ item.icon ?? (
65
+ <span
66
+ aria-hidden="true"
67
+ className="flex size-4 items-center justify-center text-xs font-semibold"
68
+ >
69
+ {item.label.charAt(0).toUpperCase()}
70
+ </span>
71
+ )
72
+ );
73
+ }
74
+
75
+ // The collapse control lives inside the sidebar; reopening needs one outside.
76
+ // Shown on mobile and, on desktop, only while the sidebar is hidden (CSS, so
77
+ // it is right from the first paint).
78
+ function HeaderTrigger() {
79
+ return (
80
+ <div className="flex items-center md:hidden md:group-has-data-[collapsible=offcanvas]/sidebar-wrapper:flex">
81
+ <SidebarTrigger className="-ml-1" />
82
+ <Separator
83
+ orientation="vertical"
84
+ className="mr-2 ml-2 h-4 self-center!"
85
+ />
86
+ </div>
87
+ );
11
88
  }
89
+
12
90
  export function AppShell({
13
- title,
14
- context,
91
+ logo,
92
+ name,
15
93
  navigation,
16
94
  activeId,
17
95
  onNavigate,
96
+ header,
97
+ actions,
98
+ footer,
99
+ defaultMode,
100
+ defaultWidth,
101
+ fillHeight = false,
18
102
  children,
19
- footer = "Workspace local",
20
- brand = "workbench",
21
- brandMark = "w.",
22
- }: {
23
- title: string;
24
- context?: ReactNode;
25
- navigation: NavigationItem[];
26
- activeId: string;
27
- onNavigate: (id: string) => void;
28
- children: ReactNode;
29
- footer?: ReactNode;
30
- brand?: string;
31
- brandMark?: ReactNode;
32
- }) {
103
+ }: AppShellProps) {
33
104
  return (
34
- <div className="wb-shell">
35
- <aside className="wb-sidebar">
36
- <div className="wb-brand">
37
- <span>{brandMark}</span>
38
- {brand}
39
- </div>
40
- <nav aria-label="Navegação principal">
41
- {navigation.map((item) => (
42
- <Button
43
- key={item.id}
44
- variant="navigation"
45
- data-active={activeId === item.id}
46
- aria-current={activeId === item.id ? "page" : undefined}
47
- onClick={() => onNavigate(item.id)}
48
- >
49
- {item.icon}
50
- {item.label}
51
- {item.count !== undefined ? (
52
- <span className="ml-auto">{item.count}</span>
53
- ) : null}
54
- </Button>
55
- ))}
56
- </nav>
57
- <div className="wb-sidebar-footer">{footer}</div>
58
- </aside>
59
- <div className="min-w-0">
60
- <header className="wb-topbar">
61
- <span>
62
- {brand} / {title}
63
- </span>
64
- {context}
65
- </header>
66
- <main className="wb-main">{children}</main>
67
- </div>
68
- </div>
105
+ <TooltipProvider delay={600}>
106
+ <SidebarProvider defaultMode={defaultMode} defaultWidth={defaultWidth}>
107
+ <Sidebar>
108
+ {/* Same height as the page header so their controls line up. */}
109
+ <SidebarHeader className="min-h-14 justify-center">
110
+ {/* The logo keeps its position across modes; only the name fades,
111
+ and it is clipped (never squeezed) by the sidebar width. */}
112
+ <div className="flex h-8 items-center gap-2 overflow-hidden">
113
+ <div className="flex size-8 shrink-0 items-center justify-center [&>*]:shrink-0">
114
+ {logo}
115
+ </div>
116
+ <span className="flex-1 truncate font-semibold whitespace-nowrap transition-opacity duration-200 ease-linear group-data-[collapsible=icon]:opacity-0">
117
+ {name}
118
+ </span>
119
+ <SidebarCollapse className="group-data-[collapsible=icon]:hidden" />
120
+ </div>
121
+ <div className="hidden group-data-[collapsible=icon]:block">
122
+ <SidebarCollapse />
123
+ </div>
124
+ </SidebarHeader>
125
+ <SidebarContent>
126
+ {navigation.map((group, index) => (
127
+ <SidebarGroup key={group.label ?? index}>
128
+ {group.label ? (
129
+ <SidebarGroupLabel>{group.label}</SidebarGroupLabel>
130
+ ) : null}
131
+ <SidebarMenu>
132
+ {group.items.map((item) => (
133
+ <SidebarMenuItem key={item.id}>
134
+ <SidebarMenuButton
135
+ isActive={item.id === activeId}
136
+ tooltip={item.label}
137
+ aria-current={item.id === activeId ? "page" : undefined}
138
+ onClick={() => onNavigate(item.id)}
139
+ >
140
+ <ItemIcon item={item} />
141
+ <span>{item.label}</span>
142
+ </SidebarMenuButton>
143
+ {item.count !== undefined ? (
144
+ <SidebarMenuBadge>{item.count}</SidebarMenuBadge>
145
+ ) : null}
146
+ </SidebarMenuItem>
147
+ ))}
148
+ </SidebarMenu>
149
+ </SidebarGroup>
150
+ ))}
151
+ </SidebarContent>
152
+ {footer ? <SidebarFooter>{footer}</SidebarFooter> : null}
153
+ <SidebarRail />
154
+ </Sidebar>
155
+ <SidebarInset className={fillHeight ? "h-svh min-h-0" : undefined}>
156
+ <header className="sticky top-0 z-10 flex h-14 shrink-0 items-center gap-2 border-b bg-background px-4 md:px-6">
157
+ <HeaderTrigger />
158
+ <div className="min-w-0 flex-1">{header}</div>
159
+ {actions ? (
160
+ <div className="flex items-center gap-2">{actions}</div>
161
+ ) : null}
162
+ </header>
163
+ <div
164
+ className={cn(
165
+ "flex flex-1 flex-col gap-4 p-4 md:gap-6 md:p-6",
166
+ fillHeight && "min-h-0 overflow-hidden",
167
+ )}
168
+ >
169
+ {children}
170
+ </div>
171
+ </SidebarInset>
172
+ </SidebarProvider>
173
+ </TooltipProvider>
69
174
  );
70
175
  }