@authdog/react-elements 0.0.39 → 0.0.40

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 (54) hide show
  1. package/.turbo/turbo-build.log +29 -26
  2. package/CHANGELOG.md +6 -0
  3. package/dist/components/ui/alert.js.map +1 -1
  4. package/dist/components/ui/alert.mjs.map +1 -1
  5. package/dist/components/ui/avatar.js.map +1 -1
  6. package/dist/components/ui/avatar.mjs.map +1 -1
  7. package/dist/components/ui/badge.js.map +1 -1
  8. package/dist/components/ui/badge.mjs.map +1 -1
  9. package/dist/components/ui/card.js.map +1 -1
  10. package/dist/components/ui/card.mjs.map +1 -1
  11. package/dist/components/ui/dropdown-menu.js.map +1 -1
  12. package/dist/components/ui/dropdown-menu.mjs.map +1 -1
  13. package/dist/components/ui/input.js.map +1 -1
  14. package/dist/components/ui/input.mjs.map +1 -1
  15. package/dist/components/ui/label.js.map +1 -1
  16. package/dist/components/ui/label.mjs.map +1 -1
  17. package/dist/components/ui/separator.js.map +1 -1
  18. package/dist/components/ui/separator.mjs.map +1 -1
  19. package/dist/components/ui/sheet.js.map +1 -1
  20. package/dist/components/ui/sheet.mjs.map +1 -1
  21. package/dist/index.d.mts +1 -1
  22. package/dist/index.d.ts +1 -1
  23. package/dist/index.js.map +1 -1
  24. package/dist/index.mjs.map +1 -1
  25. package/dist/styles.css +1 -1
  26. package/package.json +5 -5
  27. package/src/components/core/client-only.tsx +10 -15
  28. package/src/components/core/navbar.tsx +81 -50
  29. package/src/components/core/placeholder-alert.tsx +7 -9
  30. package/src/components/core/user-dropdown.tsx +97 -55
  31. package/src/components/core/user-profile.tsx +180 -86
  32. package/src/components/flow/login.tsx +42 -29
  33. package/src/components/flow/totp-validator.tsx +94 -73
  34. package/src/components/icons.tsx +13 -13
  35. package/src/components/ui/alert.tsx +11 -11
  36. package/src/components/ui/avatar.tsx +10 -10
  37. package/src/components/ui/badge.tsx +9 -9
  38. package/src/components/ui/card.tsx +13 -13
  39. package/src/components/ui/dropdown-menu.tsx +39 -37
  40. package/src/components/ui/input.tsx +5 -5
  41. package/src/components/ui/label.tsx +7 -7
  42. package/src/components/ui/separator.tsx +7 -7
  43. package/src/components/ui/sheet.tsx +21 -21
  44. package/src/index.ts +6 -6
  45. package/src/main.tsx +4 -6
  46. package/src/preview.tsx +4 -8
  47. package/src/stories/Button._stories.tsx +15 -11
  48. package/src/stories/LoginForm.stories.tsx +6 -6
  49. package/src/stories/Navbar._stories.tsx +57 -19
  50. package/src/stories/PlaceholderAlert._stories.tsx +8 -8
  51. package/src/stories/TotpValidator.stories.tsx +10 -8
  52. package/src/stories/UserDropdown.stories.tsx +7 -9
  53. package/src/stories/UserProfile.stories.tsx +12 -12
  54. package/tsup.config.ts +6 -9
@@ -1,12 +1,16 @@
1
- "use client"
1
+ "use client";
2
2
 
3
- import type React from "react"
4
- import { useState } from "react"
5
- import { User, LogOut, Menu } from "lucide-react"
3
+ import type React from "react";
4
+ import { useState } from "react";
5
+ import { User, LogOut, Menu } from "lucide-react";
6
6
 
7
- import { cn } from "../../lib/utils"
8
- import { Avatar, AvatarFallback, AvatarImage } from "../../components/ui/avatar"
9
- import { Button } from "../../components/ui/button"
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";
10
14
  import {
11
15
  DropdownMenu,
12
16
  DropdownMenuContent,
@@ -15,22 +19,22 @@ import {
15
19
  DropdownMenuLabel,
16
20
  DropdownMenuSeparator,
17
21
  DropdownMenuTrigger,
18
- } from "../../components/ui/dropdown-menu"
19
- import { Sheet, SheetContent, SheetTrigger } from "../../components/ui/sheet"
20
- import { IconWrapper } from "../icons"
22
+ } from "../../components/ui/dropdown-menu";
23
+ import { Sheet, SheetContent, SheetTrigger } from "../../components/ui/sheet";
24
+ import { IconWrapper } from "../icons";
21
25
 
22
26
  interface NavItem {
23
- title: string
24
- href: string
25
- disabled?: boolean
27
+ title: string;
28
+ href: string;
29
+ disabled?: boolean;
26
30
  }
27
31
 
28
32
  interface NavbarProps {
29
33
  items?: NavItem[] | undefined;
30
- children?: React.ReactNode
31
- className?: string
32
- logoText?: string
33
- isLoading?: boolean
34
+ children?: React.ReactNode;
35
+ className?: string;
36
+ logoText?: string;
37
+ isLoading?: boolean;
34
38
  user?: any;
35
39
  onNavigateHome?: () => void;
36
40
  onNavItemClick?: (href: string) => void;
@@ -61,25 +65,34 @@ export function Navbar({
61
65
  identityHost = "https://stg-id.authdog.xyz",
62
66
  environmentId = "58be35b0-708f-49f6-84f0-6695d307d997",
63
67
  }: NavbarProps) {
64
- const [open, setOpen] = useState(false)
65
- const isAuthenticated = user !== null && user !== undefined && user.id !== null && user.id !== undefined;
68
+ const [open, setOpen] = useState(false);
69
+ const isAuthenticated =
70
+ user !== null &&
71
+ user !== undefined &&
72
+ user.id !== null &&
73
+ user.id !== undefined;
66
74
  return (
67
75
  <header className={cn("border-b bg-background", className)}>
68
76
  <div className="container flex h-16 items-center justify-between px-4 md:px-6">
69
77
  <div className="flex items-center gap-4">
70
- <span className="text-xl font-bold cursor-pointer" onClick={onNavigateHome}>{logoText}</span>
78
+ <span
79
+ className="text-xl font-bold cursor-pointer"
80
+ onClick={onNavigateHome}
81
+ >
82
+ {logoText}
83
+ </span>
71
84
  <nav className="hidden md:flex gap-6">
72
85
  {items?.map((item, index) => (
73
86
  <span
74
87
  key={index}
75
88
  onClick={() => {
76
89
  if (!item.disabled) {
77
- onNavItemClick(item.href)
90
+ onNavItemClick(item.href);
78
91
  }
79
92
  }}
80
93
  className={cn(
81
94
  "text-sm font-medium transition-colors hover:text-primary cursor-pointer",
82
- item.disabled && "cursor-not-allowed opacity-80"
95
+ item.disabled && "cursor-not-allowed opacity-80",
83
96
  )}
84
97
  >
85
98
  {item.title}
@@ -90,18 +103,26 @@ export function Navbar({
90
103
  <div className="flex items-center gap-4">
91
104
  {children}
92
105
 
93
- {
94
- isAuthenticated ? (
95
- <DropdownMenu>
106
+ {isAuthenticated ? (
107
+ <DropdownMenu>
96
108
  <DropdownMenuTrigger asChild>
97
- <Button variant="ghost" className="relative h-8 w-8 rounded-full" disabled={isLoading}>
109
+ <Button
110
+ variant="ghost"
111
+ className="relative h-8 w-8 rounded-full"
112
+ disabled={isLoading}
113
+ >
98
114
  <Avatar className="h-8 w-8">
99
115
  {isLoading ? (
100
116
  <div className="h-8 w-8 animate-pulse bg-muted rounded-full" />
101
117
  ) : (
102
118
  <>
103
- <AvatarImage src={user.photos?.[0]?.value || "/placeholder.svg"} alt={user.displayName} />
104
- <AvatarFallback>{user.displayName?.charAt(0)}</AvatarFallback>
119
+ <AvatarImage
120
+ src={user.photos?.[0]?.value || "/placeholder.svg"}
121
+ alt={user.displayName}
122
+ />
123
+ <AvatarFallback>
124
+ {user.displayName?.charAt(0)}
125
+ </AvatarFallback>
105
126
  </>
106
127
  )}
107
128
  </Avatar>
@@ -117,8 +138,12 @@ export function Navbar({
117
138
  <>
118
139
  <DropdownMenuLabel className="font-normal">
119
140
  <div className="flex flex-col space-y-1">
120
- <p className="text-sm font-medium leading-none">{user.displayName}</p>
121
- <p className="text-xs leading-none text-muted-foreground">{user.emails?.[0]?.value}</p>
141
+ <p className="text-sm font-medium leading-none">
142
+ {user.displayName}
143
+ </p>
144
+ <p className="text-xs leading-none text-muted-foreground">
145
+ {user.emails?.[0]?.value}
146
+ </p>
122
147
  </div>
123
148
  </DropdownMenuLabel>
124
149
  <DropdownMenuSeparator />
@@ -137,29 +162,35 @@ export function Navbar({
137
162
  )}
138
163
  </DropdownMenuContent>
139
164
  </DropdownMenu>
140
- )
141
-
142
- : (
143
- <Button variant="default" aria-label="Sign in" onClick={() => {
144
- if (!environmentId) {
145
- throw new Error("Environment ID is required");
146
- }
165
+ ) : (
166
+ <Button
167
+ variant="default"
168
+ aria-label="Sign in"
169
+ onClick={() => {
170
+ if (!environmentId) {
171
+ throw new Error("Environment ID is required");
172
+ }
173
+
174
+ if (!identityHost) {
175
+ throw new Error("Identity Host is required");
176
+ }
147
177
 
148
- if (!identityHost) {
149
- throw new Error("Identity Host is required");
150
- }
151
-
152
- const signinUrl = `${identityHost}/signin/${environmentId}`;
153
- window.open(signinUrl, "_blank");
154
- }}>
178
+ const signinUrl = `${identityHost}/signin/${environmentId}`;
179
+ window.open(signinUrl, "_blank");
180
+ }}
181
+ >
155
182
  Sign in
156
183
  </Button>
157
- )
158
- }
159
-
184
+ )}
185
+
160
186
  <Sheet open={open} onOpenChange={setOpen}>
161
187
  <SheetTrigger asChild>
162
- <Button variant="ghost" size="icon" className="md:hidden" aria-label="Open Menu">
188
+ <Button
189
+ variant="ghost"
190
+ size="icon"
191
+ className="md:hidden"
192
+ aria-label="Open Menu"
193
+ >
163
194
  <IconWrapper Icon={Menu} />
164
195
  </Button>
165
196
  </SheetTrigger>
@@ -171,7 +202,7 @@ export function Navbar({
171
202
  href={item.href}
172
203
  className={cn(
173
204
  "flex w-full items-center rounded-md px-3 py-2 text-sm font-medium hover:bg-accent",
174
- item.disabled && "cursor-not-allowed opacity-80"
205
+ item.disabled && "cursor-not-allowed opacity-80",
175
206
  )}
176
207
  onClick={() => setOpen(false)}
177
208
  >
@@ -184,5 +215,5 @@ export function Navbar({
184
215
  </div>
185
216
  </div>
186
217
  </header>
187
- )
218
+ );
188
219
  }
@@ -1,6 +1,6 @@
1
- import { CheckCircle2Icon } from "lucide-react"
2
- import { Alert, AlertDescription, AlertTitle } from "../../components/ui/alert"
3
- import { IconWrapper } from "../icons"
1
+ import { CheckCircle2Icon } from "lucide-react";
2
+ import { Alert, AlertDescription, AlertTitle } from "../../components/ui/alert";
3
+ import { IconWrapper } from "../icons";
4
4
 
5
5
  interface PlaceholderAlertProps {
6
6
  title?: string;
@@ -11,15 +11,13 @@ export const PlaceholderAlert = (props: PlaceholderAlertProps) => {
11
11
  const title = props.title ?? "Placeholder Alert";
12
12
  const description = props.description ?? "This is a placeholder alert.";
13
13
 
14
- return (
14
+ return (
15
15
  <div className="grid w-full max-w-xl items-start gap-4">
16
16
  <Alert>
17
17
  <IconWrapper Icon={CheckCircle2Icon} />
18
18
  <AlertTitle>{title}</AlertTitle>
19
- <AlertDescription>
20
- {description}
21
- </AlertDescription>
19
+ <AlertDescription>{description}</AlertDescription>
22
20
  </Alert>
23
21
  </div>
24
- )
25
- }
22
+ );
23
+ };
@@ -1,100 +1,142 @@
1
- "use client"
1
+ "use client";
2
2
 
3
- import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuTrigger } from "../../components/ui/dropdown-menu"
4
- import { Avatar, AvatarFallback, AvatarImage } from "../../components/ui/avatar"
5
- import { cn } from "@authdog/react-elements/lib/utils"
6
- import { LogOut, Settings, ExternalLink } from "lucide-react"
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 "@authdog/react-elements/lib/utils";
16
+ import { LogOut, Settings, ExternalLink } from "lucide-react";
7
17
 
8
18
  export type UserDropdownLink = {
9
- label: string
10
- href?: string
11
- onClick?: () => void
12
- icon?: React.ComponentType<any>
13
- }
19
+ label: string;
20
+ href?: string;
21
+ onClick?: () => void;
22
+ icon?: React.ComponentType<any>;
23
+ };
14
24
 
15
25
  export interface UserDropdownProps {
16
- trigger: React.ReactElement
26
+ trigger: React.ReactElement;
17
27
  user: {
18
- displayName?: string
19
- name?: string
20
- email?: string
21
- emails?: { value: string }[]
22
- photos?: { value: string }[]
23
- avatar?: string
24
- }
25
- className?: string
26
- onManageAccount?: () => void
27
- onSignout?: () => void
28
- links?: UserDropdownLink[]
29
- side?: "top" | "right" | "bottom" | "left"
30
- align?: "start" | "center" | "end"
31
- sideOffset?: number
32
- modal?: boolean
28
+ displayName?: string;
29
+ name?: string;
30
+ email?: string;
31
+ emails?: { value: string }[];
32
+ photos?: { value: string }[];
33
+ avatar?: string;
34
+ };
35
+ className?: string;
36
+ onManageAccount?: () => void;
37
+ onSignout?: () => void;
38
+ links?: UserDropdownLink[];
39
+ side?: "top" | "right" | "bottom" | "left";
40
+ align?: "start" | "center" | "end";
41
+ sideOffset?: number;
42
+ modal?: boolean;
33
43
  }
34
44
 
35
45
  const getInitials = (name?: string) => {
36
- if (!name) return "?"
37
- const parts = String(name).trim().split(/\s+/)
38
- const initials = parts.slice(0, 2).map((p) => p[0]?.toUpperCase()).join("")
39
- return initials || "?"
40
- }
46
+ if (!name) return "?";
47
+ const parts = String(name).trim().split(/\s+/);
48
+ const initials = parts
49
+ .slice(0, 2)
50
+ .map((p) => p[0]?.toUpperCase())
51
+ .join("");
52
+ return initials || "?";
53
+ };
41
54
 
42
- export const UserDropdown = ({ trigger, user, className, onManageAccount, onSignout, links = [], side = "bottom", align = "end", sideOffset = 8, modal = false }: UserDropdownProps) => {
43
- const primaryEmail = user?.emails?.[0]?.value || user?.email || ""
44
- const displayName = user?.displayName || user?.name || ""
45
- const avatar = user?.photos?.[0]?.value || user?.avatar || ""
55
+ export const UserDropdown = ({
56
+ trigger,
57
+ user,
58
+ className,
59
+ onManageAccount,
60
+ onSignout,
61
+ links = [],
62
+ side = "bottom",
63
+ align = "end",
64
+ sideOffset = 8,
65
+ modal = false,
66
+ }: UserDropdownProps) => {
67
+ const primaryEmail = user?.emails?.[0]?.value || user?.email || "";
68
+ const displayName = user?.displayName || user?.name || "";
69
+ const avatar = user?.photos?.[0]?.value || user?.avatar || "";
46
70
 
47
71
  const handleLink = (item: UserDropdownLink) => {
48
- if (item.onClick) return item.onClick()
72
+ if (item.onClick) return item.onClick();
49
73
  if (item.href) {
50
74
  if (item.href.startsWith("http")) {
51
- window.open(item.href, "_blank")
75
+ window.open(item.href, "_blank");
52
76
  } else {
53
- window.location.assign(item.href)
77
+ window.location.assign(item.href);
54
78
  }
55
79
  }
56
- }
80
+ };
57
81
 
58
- const IconExternal = ExternalLink as any
82
+ const IconExternal = ExternalLink as any;
59
83
 
60
84
  return (
61
85
  <DropdownMenu modal={modal}>
62
- <DropdownMenuTrigger asChild>
63
- {trigger}
64
- </DropdownMenuTrigger>
65
- <DropdownMenuContent align={align} side={side} sideOffset={sideOffset} className={cn("w-72 p-2 overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md", className)}>
86
+ <DropdownMenuTrigger asChild>{trigger}</DropdownMenuTrigger>
87
+ <DropdownMenuContent
88
+ align={align}
89
+ side={side}
90
+ sideOffset={sideOffset}
91
+ className={cn(
92
+ "w-72 p-2 overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md",
93
+ className,
94
+ )}
95
+ >
66
96
  <div className="flex items-center gap-3 px-4 pt-4 pb-3">
67
97
  <Avatar className="h-9 w-9 rounded-full">
68
98
  <AvatarImage src={avatar} alt={displayName} />
69
- <AvatarFallback className="rounded-full">{getInitials(displayName)}</AvatarFallback>
99
+ <AvatarFallback className="rounded-full">
100
+ {getInitials(displayName)}
101
+ </AvatarFallback>
70
102
  </Avatar>
71
103
  <div className="min-w-0">
72
104
  <div className="text-sm font-semibold truncate">{displayName}</div>
73
- <div className="text-xs text-muted-foreground truncate">{primaryEmail}</div>
105
+ <div className="text-xs text-muted-foreground truncate">
106
+ {primaryEmail}
107
+ </div>
74
108
  </div>
75
109
  </div>
76
110
  <DropdownMenuSeparator />
77
- <DropdownMenuItem className="cursor-pointer py-2" onClick={() => onManageAccount?.()}>
111
+ <DropdownMenuItem
112
+ className="cursor-pointer py-2"
113
+ onClick={() => onManageAccount?.()}
114
+ >
78
115
  <Settings className="mr-2 h-4 w-4" />
79
116
  <span>Manage account</span>
80
117
  </DropdownMenuItem>
81
118
  {links.map((item, idx) => {
82
- const Icon = (item.icon ?? IconExternal) as any
119
+ const Icon = (item.icon ?? IconExternal) as any;
83
120
  return (
84
- <DropdownMenuItem key={`${item.label}-${idx}`} className="cursor-pointer py-2" onClick={() => handleLink(item)}>
121
+ <DropdownMenuItem
122
+ key={`${item.label}-${idx}`}
123
+ className="cursor-pointer py-2"
124
+ onClick={() => handleLink(item)}
125
+ >
85
126
  <Icon className="mr-2 h-4 w-4" />
86
127
  <span>{item.label}</span>
87
128
  </DropdownMenuItem>
88
- )
129
+ );
89
130
  })}
90
131
  <DropdownMenuSeparator />
91
- <DropdownMenuItem 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" onClick={() => onSignout?.()}>
132
+ <DropdownMenuItem
133
+ 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"
134
+ onClick={() => onSignout?.()}
135
+ >
92
136
  <LogOut className="mr-2 h-4 w-4 text-red-600 dark:text-red-300" />
93
137
  <span>Sign out</span>
94
138
  </DropdownMenuItem>
95
139
  </DropdownMenuContent>
96
140
  </DropdownMenu>
97
- )
98
- }
99
-
100
-
141
+ );
142
+ };