@authdog/react-elements 0.0.49 → 0.2.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 (81) hide show
  1. package/dist/components/ui/alert.d.mts +12 -0
  2. package/dist/components/ui/alert.d.ts +12 -0
  3. package/dist/components/ui/avatar.d.mts +8 -0
  4. package/dist/components/ui/avatar.d.ts +8 -0
  5. package/dist/components/ui/badge.d.mts +12 -0
  6. package/dist/components/ui/badge.d.ts +12 -0
  7. package/dist/components/ui/button.d.mts +14 -0
  8. package/dist/components/ui/button.d.ts +14 -0
  9. package/dist/components/ui/button.js.map +1 -1
  10. package/dist/components/ui/button.mjs.map +1 -1
  11. package/dist/components/ui/card.d.mts +11 -0
  12. package/dist/components/ui/card.d.ts +11 -0
  13. package/dist/components/ui/dropdown-menu.d.mts +27 -0
  14. package/dist/components/ui/dropdown-menu.d.ts +27 -0
  15. package/dist/components/ui/input.d.mts +5 -0
  16. package/dist/components/ui/input.d.ts +5 -0
  17. package/dist/components/ui/label.d.mts +6 -0
  18. package/dist/components/ui/label.d.ts +6 -0
  19. package/dist/components/ui/separator.d.mts +6 -0
  20. package/dist/components/ui/separator.d.ts +6 -0
  21. package/dist/components/ui/sheet.d.mts +15 -0
  22. package/dist/components/ui/sheet.d.ts +15 -0
  23. package/dist/components/ui/theme-toggle.d.mts +5 -0
  24. package/dist/components/ui/theme-toggle.d.ts +5 -0
  25. package/dist/components/ui/theme-toggle.js.map +1 -1
  26. package/dist/components/ui/theme-toggle.mjs.map +1 -1
  27. package/dist/index.d.mts +12 -21
  28. package/dist/index.d.ts +12 -21
  29. package/dist/index.js +1 -1
  30. package/dist/index.js.map +1 -1
  31. package/dist/index.mjs +1 -1
  32. package/dist/index.mjs.map +1 -1
  33. package/dist/lib/utils.d.mts +5 -0
  34. package/dist/lib/utils.d.ts +5 -0
  35. package/dist/styles.css +1 -4
  36. package/package.json +40 -25
  37. package/.eslintrc.js +0 -9
  38. package/.storybook/main.ts +0 -21
  39. package/.storybook/preview.ts +0 -17
  40. package/.storybook/vitest.setup.ts +0 -7
  41. package/.turbo/turbo-build.log +0 -77
  42. package/CHANGELOG.md +0 -286
  43. package/components.json +0 -20
  44. package/postcss.config.mjs +0 -11
  45. package/src/components/core/client-only.tsx +0 -15
  46. package/src/components/core/navbar.tsx +0 -307
  47. package/src/components/core/placeholder-alert.tsx +0 -23
  48. package/src/components/core/user-dropdown.tsx +0 -160
  49. package/src/components/core/user-profile.tsx +0 -521
  50. package/src/components/flow/login.tsx +0 -167
  51. package/src/components/flow/totp-validator.tsx +0 -252
  52. package/src/components/icons.tsx +0 -30
  53. package/src/components/ui/alert.tsx +0 -66
  54. package/src/components/ui/avatar.tsx +0 -53
  55. package/src/components/ui/badge.tsx +0 -46
  56. package/src/components/ui/button.tsx +0 -56
  57. package/src/components/ui/card.tsx +0 -92
  58. package/src/components/ui/dropdown-menu.tsx +0 -265
  59. package/src/components/ui/input.tsx +0 -21
  60. package/src/components/ui/label.tsx +0 -24
  61. package/src/components/ui/separator.tsx +0 -28
  62. package/src/components/ui/sheet.tsx +0 -142
  63. package/src/components/ui/theme-toggle.tsx +0 -56
  64. package/src/global.css +0 -81
  65. package/src/index.ts +0 -8
  66. package/src/lib/utils.ts +0 -6
  67. package/src/stories/core/Navbar.stories.tsx +0 -51
  68. package/src/stories/core/PlaceholderAlert.stories.tsx +0 -23
  69. package/src/stories/core/UserDropdown.stories.tsx +0 -56
  70. package/src/stories/core/UserProfile.stories.tsx +0 -47
  71. package/src/stories/flow/LoginForm.stories.tsx +0 -20
  72. package/src/stories/flow/TotpValidator.stories.tsx +0 -23
  73. package/src/stories/showcase/Landing.stories.tsx +0 -376
  74. package/src/stories/ui/Button.stories.tsx +0 -45
  75. package/src/types.ts +0 -0
  76. package/tailwind.config.ts +0 -82
  77. package/tsconfig.json +0 -11
  78. package/tsup.config.ts +0 -31
  79. package/vitest.config.ts +0 -39
  80. package/vitest.shims.d.ts +0 -1
  81. package/wrangler.prod.toml +0 -4
@@ -1,307 +0,0 @@
1
- "use client";
2
-
3
- import type React from "react";
4
- import { useState } from "react";
5
- import { User, LogOut, Menu } from "lucide-react";
6
-
7
- import { cn } from "../../lib/utils";
8
- import {
9
- Avatar,
10
- AvatarFallback,
11
- AvatarImage,
12
- } from "../../components/ui/avatar";
13
- import { Button } from "../../components/ui/button";
14
- import {
15
- DropdownMenu,
16
- DropdownMenuContent,
17
- DropdownMenuGroup,
18
- DropdownMenuItem,
19
- DropdownMenuLabel,
20
- DropdownMenuSeparator,
21
- DropdownMenuTrigger,
22
- } from "../../components/ui/dropdown-menu";
23
- import { Sheet, SheetContent, SheetTrigger } from "../../components/ui/sheet";
24
- import { IconWrapper } from "../icons";
25
- import { ThemeToggle } from "../ui/theme-toggle";
26
-
27
- export interface NavItem {
28
- title: string;
29
- href: string;
30
- disabled?: boolean;
31
- }
32
-
33
- export interface DropdownMenuItem {
34
- name: string;
35
- uri: string;
36
- icon?: React.ComponentType<{ className?: string }>;
37
- disabled?: boolean;
38
- }
39
-
40
- interface NavbarProps {
41
- items?: NavItem[] | undefined;
42
- children?: React.ReactNode;
43
- className?: string;
44
- logoText?: string;
45
- logoSrc?: string;
46
- /**
47
- * Optional custom alt text element rendered next to the logo.
48
- * When not provided, the plain `logoText` string is rendered.
49
- * Use this to pass in custom typography / font classes.
50
- */
51
- altText?: React.ReactNode;
52
- isLoading?: boolean;
53
- user?: any;
54
- onNavigateHome?: () => void;
55
- onNavItemClick?: (href: string) => void;
56
- /**
57
- * Custom dropdown menu items that appear in the user avatar dropdown.
58
- * If not provided, defaults to a "Profile" link.
59
- */
60
- dropdownMenuItems?: DropdownMenuItem[];
61
- /**
62
- * Callback when a dropdown menu item is clicked.
63
- * Receives the URI of the clicked item.
64
- */
65
- onDropdownMenuItemClick?: (uri: string) => void;
66
- /**
67
- * @deprecated Use dropdownMenuItems with a custom item instead
68
- */
69
- onProfileSelected?: () => void;
70
- onLogout?: () => void;
71
- // signinUrl?: string;
72
- identityHost?: string;
73
- environmentId?: string;
74
- }
75
-
76
- export function Navbar({
77
- items = [
78
- // { title: "Dashboard", href: "/dashboard" },
79
- ],
80
- children,
81
- className,
82
- logoText = "ACME Corp",
83
- logoSrc,
84
- altText,
85
- user = {
86
- name: "John Doe",
87
- email: "john@example.com",
88
- image: "https://i.pravatar.cc/150?u=a042581f4e29026704d",
89
- },
90
- onNavigateHome = () => console.log("Navigating to home"),
91
- onNavItemClick = (href: string) => console.log(`Navigating to ${href}`),
92
- dropdownMenuItems = [
93
- { name: "Profile", uri: "/profile", icon: User },
94
- ],
95
- onDropdownMenuItemClick = (uri: string) => console.log(`Navigating to ${uri}`),
96
- onProfileSelected,
97
- onLogout = () => console.log("Logout clicked"),
98
- isLoading = false,
99
- identityHost = "https://stg-id.authdog.xyz",
100
- environmentId = "58be35b0-708f-49f6-84f0-6695d307d997",
101
- }: NavbarProps) {
102
- const [open, setOpen] = useState(false);
103
- const [logoFailed, setLogoFailed] = useState(false);
104
- const isAuthenticated =
105
- user !== null &&
106
- user !== undefined &&
107
- user.id !== null &&
108
- user.id !== undefined;
109
-
110
- return (
111
- <header
112
- className={cn(
113
- // Keep the navbar simple and predictable so host apps can style around it.
114
- // We avoid sticky positioning, backdrop filters, and custom width constraints.
115
- "w-full border-b bg-background",
116
- className,
117
- )}
118
- >
119
- <div
120
- className={cn(
121
- // Basic centered container with horizontal padding.
122
- "mx-auto flex h-16 w-full max-w-6xl items-center justify-between px-4 md:px-6",
123
- )}
124
- >
125
- <div className="flex items-center gap-3 md:gap-4">
126
- <button
127
- type="button"
128
- onClick={onNavigateHome}
129
- className={cn(
130
- "group inline-flex items-center gap-2 md:gap-3 rounded-md px-1 py-1 text-left cursor-pointer",
131
- "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background",
132
- )}
133
- aria-label="Go to homepage"
134
- >
135
- {logoSrc && !logoFailed && (
136
- <span className="inline-flex h-8 w-8 items-center justify-center overflow-hidden rounded-md bg-muted/80 ring-1 ring-border">
137
- <img
138
- src={logoSrc}
139
- alt={logoText}
140
- className="h-7 w-7 object-contain"
141
- onError={() => setLogoFailed(true)}
142
- />
143
- </span>
144
- )}
145
- {altText ?? (
146
- <span className="text-base font-semibold tracking-tight md:text-lg group-hover:text-primary">
147
- {logoText}
148
- </span>
149
- )}
150
- </button>
151
- {children}
152
- </div>
153
- <div className="flex flex-1 items-center justify-end gap-6">
154
- <nav className="hidden md:flex items-center gap-6">
155
- {items?.map((item, index) => (
156
- <span
157
- key={index}
158
- onClick={() => {
159
- if (!item.disabled) {
160
- onNavItemClick(item.href);
161
- }
162
- }}
163
- className={cn(
164
- "text-sm font-medium transition-colors hover:text-primary cursor-pointer",
165
- item.disabled && "cursor-not-allowed opacity-80",
166
- )}
167
- >
168
- {item.title}
169
- </span>
170
- ))}
171
- </nav>
172
- <div className="flex items-center gap-3">
173
- <Sheet open={open} onOpenChange={setOpen}>
174
- <SheetTrigger asChild>
175
- <Button
176
- variant="ghost"
177
- size="icon"
178
- className="md:hidden"
179
- aria-label="Open Menu"
180
- >
181
- <IconWrapper Icon={Menu} withMargin={false} />
182
- </Button>
183
- </SheetTrigger>
184
- <SheetContent side="left" className="pr-0">
185
- <nav className="grid gap-2 py-6">
186
- {items?.map((item, index) => (
187
- <a
188
- key={index}
189
- href={item.href}
190
- className={cn(
191
- "flex w-full items-center rounded-md px-3 py-2 text-sm font-medium hover:bg-accent cursor-pointer",
192
- item.disabled && "cursor-not-allowed opacity-80",
193
- )}
194
- onClick={() => setOpen(false)}
195
- >
196
- {item.title}
197
- </a>
198
- ))}
199
- </nav>
200
- </SheetContent>
201
- </Sheet>
202
- <ThemeToggle />
203
- {isAuthenticated ? (
204
- <DropdownMenu>
205
- <DropdownMenuTrigger asChild>
206
- <Button
207
- variant="ghost"
208
- className="relative h-8 w-8 rounded-full cursor-pointer"
209
- disabled={isLoading}
210
- >
211
- <Avatar className="h-8 w-8">
212
- {isLoading ? (
213
- <div className="h-8 w-8 animate-pulse bg-muted rounded-full" />
214
- ) : (
215
- <>
216
- <AvatarImage
217
- src={user.photos?.[0]?.value || "/placeholder.svg"}
218
- alt={user.displayName}
219
- />
220
- <AvatarFallback>
221
- {user.displayName?.charAt(0)}
222
- </AvatarFallback>
223
- </>
224
- )}
225
- </Avatar>
226
- </Button>
227
- </DropdownMenuTrigger>
228
- <DropdownMenuContent className="w-56" align="end" forceMount>
229
- {isLoading ? (
230
- <div className="p-4">
231
- <div className="h-4 w-3/4 animate-pulse bg-muted rounded mb-2" />
232
- <div className="h-3 w-1/2 animate-pulse bg-muted rounded" />
233
- </div>
234
- ) : (
235
- <>
236
- <DropdownMenuLabel className="font-normal">
237
- <div className="flex flex-col space-y-1">
238
- <p className="text-sm font-medium leading-none">
239
- {user.displayName}
240
- </p>
241
- <p className="text-xs leading-none text-muted-foreground">
242
- {user.emails?.[0]?.value}
243
- </p>
244
- </div>
245
- </DropdownMenuLabel>
246
- {dropdownMenuItems.length > 0 && (
247
- <>
248
- <DropdownMenuSeparator />
249
- <DropdownMenuGroup>
250
- {dropdownMenuItems.map((item, index) => (
251
- <DropdownMenuItem
252
- key={index}
253
- onClick={() => {
254
- if (!item.disabled) {
255
- // Backward compatibility: if it's the profile item and onProfileSelected exists
256
- if (item.uri === "/profile" && onProfileSelected) {
257
- onProfileSelected();
258
- }
259
- onDropdownMenuItemClick(item.uri);
260
- }
261
- }}
262
- disabled={item.disabled}
263
- className="cursor-pointer"
264
- >
265
- {item.icon && <IconWrapper Icon={item.icon} />}
266
- <span>{item.name}</span>
267
- </DropdownMenuItem>
268
- ))}
269
- </DropdownMenuGroup>
270
- <DropdownMenuSeparator />
271
- </>
272
- )}
273
- <DropdownMenuItem onClick={onLogout} className="cursor-pointer">
274
- <IconWrapper Icon={LogOut} />
275
- <span>Log out</span>
276
- </DropdownMenuItem>
277
- </>
278
- )}
279
- </DropdownMenuContent>
280
- </DropdownMenu>
281
- ) : (
282
- <Button
283
- variant="default"
284
- className="cursor-pointer"
285
- aria-label="Sign in"
286
- onClick={() => {
287
- if (!environmentId) {
288
- throw new Error("Environment ID is required");
289
- }
290
-
291
- if (!identityHost) {
292
- throw new Error("Identity Host is required");
293
- }
294
-
295
- const signinUrl = `${identityHost}/signin/${environmentId}`;
296
- window.open(signinUrl, "_blank");
297
- }}
298
- >
299
- Sign in
300
- </Button>
301
- )}
302
- </div>
303
- </div>
304
- </div>
305
- </header>
306
- );
307
- }
@@ -1,23 +0,0 @@
1
- import { CheckCircle2Icon } from "lucide-react";
2
- import { Alert, AlertDescription, AlertTitle } from "../../components/ui/alert";
3
- import { IconWrapper } from "../icons";
4
-
5
- interface PlaceholderAlertProps {
6
- title?: string;
7
- description?: string;
8
- }
9
-
10
- export const PlaceholderAlert = (props: PlaceholderAlertProps) => {
11
- const title = props.title ?? "Placeholder Alert";
12
- const description = props.description ?? "This is a placeholder alert.";
13
-
14
- return (
15
- <div className="grid w-full max-w-xl items-start gap-4">
16
- <Alert>
17
- <IconWrapper Icon={CheckCircle2Icon} />
18
- <AlertTitle>{title}</AlertTitle>
19
- <AlertDescription>{description}</AlertDescription>
20
- </Alert>
21
- </div>
22
- );
23
- };
@@ -1,160 +0,0 @@
1
- "use client";
2
-
3
- import {
4
- DropdownMenu,
5
- DropdownMenuContent,
6
- DropdownMenuItem,
7
- DropdownMenuSeparator,
8
- DropdownMenuTrigger,
9
- } from "../../components/ui/dropdown-menu";
10
- import {
11
- Avatar,
12
- AvatarFallback,
13
- AvatarImage,
14
- } from "../../components/ui/avatar";
15
- import { cn } from "../../lib/utils";
16
- import { LogOut, Settings, ExternalLink } from "lucide-react";
17
- import type { ComponentType } from "react";
18
-
19
- export type UserDropdownLink = {
20
- label: string;
21
- href?: string;
22
- onClick?: () => void;
23
- icon?: React.ComponentType<any>;
24
- };
25
-
26
- export interface UserDropdownProps {
27
- trigger: React.ReactElement;
28
- user: {
29
- displayName?: string;
30
- name?: string;
31
- email?: string;
32
- emails?: { value: string }[];
33
- photos?: { value: string }[];
34
- avatar?: string;
35
- };
36
- className?: string;
37
- onManageAccount?: () => void;
38
- onSignout?: () => void;
39
- links?: UserDropdownLink[];
40
- side?: "top" | "right" | "bottom" | "left";
41
- align?: "start" | "center" | "end";
42
- sideOffset?: number;
43
- modal?: boolean;
44
- triggerAsChild?: boolean;
45
- triggerWrapperClassName?: string;
46
- }
47
-
48
- const getInitials = (name?: string) => {
49
- if (!name) return "?";
50
- const parts = String(name).trim().split(/\s+/);
51
- const initials = parts
52
- .slice(0, 2)
53
- .map((p) => p[0]?.toUpperCase())
54
- .join("");
55
- return initials || "?";
56
- };
57
-
58
- export const UserDropdown = ({
59
- trigger,
60
- user,
61
- className,
62
- onManageAccount,
63
- onSignout,
64
- links = [],
65
- side = "bottom",
66
- align = "end",
67
- sideOffset = 8,
68
- modal = false,
69
- triggerAsChild = false,
70
- triggerWrapperClassName,
71
- }: UserDropdownProps) => {
72
- const primaryEmail = user?.emails?.[0]?.value || user?.email || "";
73
- const displayName = user?.displayName || user?.name || "";
74
- const avatar = user?.photos?.[0]?.value || user?.avatar || "";
75
-
76
- const handleLink = (item: UserDropdownLink) => {
77
- if (item.onClick) return item.onClick();
78
- if (item.href) {
79
- if (item.href.startsWith("http")) {
80
- window.open(item.href, "_blank");
81
- } else {
82
- window.location.assign(item.href);
83
- }
84
- }
85
- };
86
-
87
- const IconExternal = ExternalLink as any;
88
- const SettingsIcon = Settings as ComponentType<any>;
89
- const LogOutIcon = LogOut as ComponentType<any>;
90
-
91
- return (
92
- <DropdownMenu modal={modal}>
93
- {triggerAsChild ? (
94
- <DropdownMenuTrigger asChild>{trigger}</DropdownMenuTrigger>
95
- ) : (
96
- <DropdownMenuTrigger
97
- className={cn(
98
- "inline-flex items-center justify-center bg-transparent p-0 border-0 outline-none focus-visible:outline-none",
99
- triggerWrapperClassName,
100
- )}
101
- >
102
- {trigger}
103
- </DropdownMenuTrigger>
104
- )}
105
- <DropdownMenuContent
106
- align={align}
107
- side={side}
108
- sideOffset={sideOffset}
109
- className={cn(
110
- "w-72 p-2 overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md",
111
- className,
112
- )}
113
- >
114
- <div className="flex items-center gap-3 px-4 pt-4 pb-3">
115
- <Avatar className="h-9 w-9 rounded-full">
116
- <AvatarImage src={avatar} alt={displayName} />
117
- <AvatarFallback className="rounded-full">
118
- {getInitials(displayName)}
119
- </AvatarFallback>
120
- </Avatar>
121
- <div className="min-w-0">
122
- <div className="text-sm font-semibold truncate">{displayName}</div>
123
- <div className="text-xs text-muted-foreground truncate">
124
- {primaryEmail}
125
- </div>
126
- </div>
127
- </div>
128
- <DropdownMenuSeparator />
129
- <DropdownMenuItem
130
- className="cursor-pointer py-2"
131
- onClick={() => onManageAccount?.()}
132
- >
133
- <SettingsIcon className="mr-2 h-4 w-4" />
134
- <span>Manage account</span>
135
- </DropdownMenuItem>
136
- {links.map((item, idx) => {
137
- const Icon = (item.icon ?? IconExternal) as any;
138
- return (
139
- <DropdownMenuItem
140
- key={`${item.label}-${idx}`}
141
- className="cursor-pointer py-2"
142
- onClick={() => handleLink(item)}
143
- >
144
- <Icon className="mr-2 h-4 w-4" />
145
- <span>{item.label}</span>
146
- </DropdownMenuItem>
147
- );
148
- })}
149
- <DropdownMenuSeparator />
150
- <DropdownMenuItem
151
- className="cursor-pointer py-2 rounded-md font-semibold text-red-600 dark:text-red-300 hover:bg-red-50 hover:text-red-700 focus:bg-red-50 focus:text-red-700 dark:hover:bg-red-500/20 dark:focus:bg-red-500/25 dark:hover:text-red-100 dark:focus:text-red-100 border border-transparent dark:border-red-500/30 ring-0 focus-visible:ring-2 focus-visible:ring-red-400/40 dark:focus-visible:ring-red-400/40"
152
- onClick={() => onSignout?.()}
153
- >
154
- <LogOutIcon className="mr-2 h-4 w-4 text-red-600 dark:text-red-300" />
155
- <span>Sign out</span>
156
- </DropdownMenuItem>
157
- </DropdownMenuContent>
158
- </DropdownMenu>
159
- );
160
- };