@authdog/react-elements 0.0.42 → 0.0.43
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.
- package/.turbo/turbo-build.log +18 -14
- package/CHANGELOG.md +7 -0
- package/dist/components/ui/theme-toggle.js +3 -0
- package/dist/components/ui/theme-toggle.js.map +1 -0
- package/dist/components/ui/theme-toggle.mjs +3 -0
- package/dist/components/ui/theme-toggle.mjs.map +1 -0
- package/dist/index.d.mts +2 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/index.mjs.map +1 -1
- package/dist/styles.css +97 -13
- package/package.json +6 -6
- package/src/components/core/navbar.tsx +145 -112
- package/src/components/ui/theme-toggle.tsx +55 -0
- package/src/stories/core/Navbar.stories.tsx +1 -1
package/dist/styles.css
CHANGED
|
@@ -531,6 +531,39 @@ video {
|
|
|
531
531
|
|
|
532
532
|
--radius: 0.5rem;
|
|
533
533
|
}
|
|
534
|
+
|
|
535
|
+
.dark {
|
|
536
|
+
--background: 224 71% 4%;
|
|
537
|
+
--foreground: 213 31% 91%;
|
|
538
|
+
|
|
539
|
+
--muted: 223 47% 11%;
|
|
540
|
+
--muted-foreground: 215.4 16.3% 56.9%;
|
|
541
|
+
|
|
542
|
+
--popover: 224 71% 4%;
|
|
543
|
+
--popover-foreground: 215 20.2% 65.1%;
|
|
544
|
+
|
|
545
|
+
--card: 224 71% 4%;
|
|
546
|
+
--card-foreground: 213 31% 91%;
|
|
547
|
+
|
|
548
|
+
--border: 216 34% 17%;
|
|
549
|
+
--input: 216 34% 17%;
|
|
550
|
+
|
|
551
|
+
--primary: 210 40% 98%;
|
|
552
|
+
--primary-foreground: 222.2 47.4% 1.2%;
|
|
553
|
+
|
|
554
|
+
--secondary: 222.2 47.4% 11.2%;
|
|
555
|
+
--secondary-foreground: 210 40% 98%;
|
|
556
|
+
|
|
557
|
+
--accent: 216 34% 17%;
|
|
558
|
+
--accent-foreground: 210 40% 98%;
|
|
559
|
+
|
|
560
|
+
--destructive: 0 63% 31%;
|
|
561
|
+
--destructive-foreground: 210 40% 98%;
|
|
562
|
+
|
|
563
|
+
--ring: 216 34% 17%;
|
|
564
|
+
|
|
565
|
+
--radius: 0.5rem;
|
|
566
|
+
}
|
|
534
567
|
* {
|
|
535
568
|
border-color: hsl(var(--border));
|
|
536
569
|
}
|
|
@@ -539,19 +572,6 @@ video {
|
|
|
539
572
|
color: hsl(var(--foreground));
|
|
540
573
|
font-feature-settings: "rlig" 1, "calt" 1;
|
|
541
574
|
}
|
|
542
|
-
.container {
|
|
543
|
-
width: 100%;
|
|
544
|
-
margin-right: auto;
|
|
545
|
-
margin-left: auto;
|
|
546
|
-
padding-right: 2rem;
|
|
547
|
-
padding-left: 2rem;
|
|
548
|
-
}
|
|
549
|
-
@media (min-width: 1400px) {
|
|
550
|
-
|
|
551
|
-
.container {
|
|
552
|
-
max-width: 1400px;
|
|
553
|
-
}
|
|
554
|
-
}
|
|
555
575
|
.sr-only {
|
|
556
576
|
position: absolute;
|
|
557
577
|
width: 1px;
|
|
@@ -575,6 +595,9 @@ video {
|
|
|
575
595
|
.relative {
|
|
576
596
|
position: relative;
|
|
577
597
|
}
|
|
598
|
+
.sticky {
|
|
599
|
+
position: sticky;
|
|
600
|
+
}
|
|
578
601
|
.inset-0 {
|
|
579
602
|
inset: 0px;
|
|
580
603
|
}
|
|
@@ -610,6 +633,9 @@ video {
|
|
|
610
633
|
.top-4 {
|
|
611
634
|
top: 1rem;
|
|
612
635
|
}
|
|
636
|
+
.z-40 {
|
|
637
|
+
z-index: 40;
|
|
638
|
+
}
|
|
613
639
|
.z-50 {
|
|
614
640
|
z-index: 50;
|
|
615
641
|
}
|
|
@@ -814,6 +840,9 @@ video {
|
|
|
814
840
|
.w-6 {
|
|
815
841
|
width: 1.5rem;
|
|
816
842
|
}
|
|
843
|
+
.w-7 {
|
|
844
|
+
width: 1.75rem;
|
|
845
|
+
}
|
|
817
846
|
.w-72 {
|
|
818
847
|
width: 18rem;
|
|
819
848
|
}
|
|
@@ -1103,6 +1132,9 @@ video {
|
|
|
1103
1132
|
.bg-background {
|
|
1104
1133
|
background-color: hsl(var(--background));
|
|
1105
1134
|
}
|
|
1135
|
+
.bg-background\/80 {
|
|
1136
|
+
background-color: hsl(var(--background) / 0.8);
|
|
1137
|
+
}
|
|
1106
1138
|
.bg-black\/50 {
|
|
1107
1139
|
background-color: rgb(0 0 0 / 0.5);
|
|
1108
1140
|
}
|
|
@@ -1148,6 +1180,9 @@ video {
|
|
|
1148
1180
|
.bg-muted {
|
|
1149
1181
|
background-color: hsl(var(--muted));
|
|
1150
1182
|
}
|
|
1183
|
+
.bg-muted\/80 {
|
|
1184
|
+
background-color: hsl(var(--muted) / 0.8);
|
|
1185
|
+
}
|
|
1151
1186
|
.bg-popover {
|
|
1152
1187
|
background-color: hsl(var(--popover));
|
|
1153
1188
|
}
|
|
@@ -1191,6 +1226,10 @@ video {
|
|
|
1191
1226
|
.fill-current {
|
|
1192
1227
|
fill: currentColor;
|
|
1193
1228
|
}
|
|
1229
|
+
.object-contain {
|
|
1230
|
+
-o-object-fit: contain;
|
|
1231
|
+
object-fit: contain;
|
|
1232
|
+
}
|
|
1194
1233
|
.p-0 {
|
|
1195
1234
|
padding: 0px;
|
|
1196
1235
|
}
|
|
@@ -1218,6 +1257,10 @@ video {
|
|
|
1218
1257
|
.p-8 {
|
|
1219
1258
|
padding: 2rem;
|
|
1220
1259
|
}
|
|
1260
|
+
.px-1 {
|
|
1261
|
+
padding-left: 0.25rem;
|
|
1262
|
+
padding-right: 0.25rem;
|
|
1263
|
+
}
|
|
1221
1264
|
.px-2 {
|
|
1222
1265
|
padding-left: 0.5rem;
|
|
1223
1266
|
padding-right: 0.5rem;
|
|
@@ -1534,6 +1577,14 @@ video {
|
|
|
1534
1577
|
--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(0px + var(--tw-ring-offset-width)) var(--tw-ring-color);
|
|
1535
1578
|
box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000);
|
|
1536
1579
|
}
|
|
1580
|
+
.ring-1 {
|
|
1581
|
+
--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);
|
|
1582
|
+
--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);
|
|
1583
|
+
box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000);
|
|
1584
|
+
}
|
|
1585
|
+
.ring-border {
|
|
1586
|
+
--tw-ring-color: hsl(var(--border));
|
|
1587
|
+
}
|
|
1537
1588
|
.ring-offset-background {
|
|
1538
1589
|
--tw-ring-offset-color: hsl(var(--background));
|
|
1539
1590
|
}
|
|
@@ -1542,6 +1593,10 @@ video {
|
|
|
1542
1593
|
-webkit-backdrop-filter: var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);
|
|
1543
1594
|
backdrop-filter: var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);
|
|
1544
1595
|
}
|
|
1596
|
+
.backdrop-filter {
|
|
1597
|
+
-webkit-backdrop-filter: var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);
|
|
1598
|
+
backdrop-filter: var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);
|
|
1599
|
+
}
|
|
1545
1600
|
.transition {
|
|
1546
1601
|
transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, -webkit-backdrop-filter;
|
|
1547
1602
|
transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter;
|
|
@@ -1749,6 +1804,9 @@ video {
|
|
|
1749
1804
|
.focus-visible\:ring-offset-2:focus-visible {
|
|
1750
1805
|
--tw-ring-offset-width: 2px;
|
|
1751
1806
|
}
|
|
1807
|
+
.focus-visible\:ring-offset-background:focus-visible {
|
|
1808
|
+
--tw-ring-offset-color: hsl(var(--background));
|
|
1809
|
+
}
|
|
1752
1810
|
.disabled\:pointer-events-none:disabled {
|
|
1753
1811
|
pointer-events: none;
|
|
1754
1812
|
}
|
|
@@ -1758,6 +1816,9 @@ video {
|
|
|
1758
1816
|
.disabled\:opacity-50:disabled {
|
|
1759
1817
|
opacity: 0.5;
|
|
1760
1818
|
}
|
|
1819
|
+
.group:hover .group-hover\:text-primary {
|
|
1820
|
+
color: hsl(var(--primary));
|
|
1821
|
+
}
|
|
1761
1822
|
.peer:disabled ~ .peer-disabled\:cursor-not-allowed {
|
|
1762
1823
|
cursor: not-allowed;
|
|
1763
1824
|
}
|
|
@@ -1897,6 +1958,12 @@ video {
|
|
|
1897
1958
|
.group[data-disabled="true"] .group-data-\[disabled\=true\]\:opacity-50 {
|
|
1898
1959
|
opacity: 0.5;
|
|
1899
1960
|
}
|
|
1961
|
+
@supports ((-webkit-backdrop-filter: var(--tw)) or (backdrop-filter: var(--tw))) {
|
|
1962
|
+
|
|
1963
|
+
.supports-\[backdrop-filter\]\:bg-background\/60 {
|
|
1964
|
+
background-color: hsl(var(--background) / 0.6);
|
|
1965
|
+
}
|
|
1966
|
+
}
|
|
1900
1967
|
.dark\:border-amber-400\/40:is(.dark *) {
|
|
1901
1968
|
border-color: rgb(251 191 36 / 0.4);
|
|
1902
1969
|
}
|
|
@@ -2011,6 +2078,14 @@ video {
|
|
|
2011
2078
|
grid-template-columns: repeat(3, minmax(0, 1fr));
|
|
2012
2079
|
}
|
|
2013
2080
|
|
|
2081
|
+
.md\:gap-3 {
|
|
2082
|
+
gap: 0.75rem;
|
|
2083
|
+
}
|
|
2084
|
+
|
|
2085
|
+
.md\:gap-4 {
|
|
2086
|
+
gap: 1rem;
|
|
2087
|
+
}
|
|
2088
|
+
|
|
2014
2089
|
.md\:space-y-6 > :not([hidden]) ~ :not([hidden]) {
|
|
2015
2090
|
--tw-space-y-reverse: 0;
|
|
2016
2091
|
margin-top: calc(1.5rem * calc(1 - var(--tw-space-y-reverse)));
|
|
@@ -2030,6 +2105,11 @@ video {
|
|
|
2030
2105
|
padding-right: 1.5rem;
|
|
2031
2106
|
}
|
|
2032
2107
|
|
|
2108
|
+
.md\:text-lg {
|
|
2109
|
+
font-size: 1.125rem;
|
|
2110
|
+
line-height: 1.75rem;
|
|
2111
|
+
}
|
|
2112
|
+
|
|
2033
2113
|
.md\:text-sm {
|
|
2034
2114
|
font-size: 0.875rem;
|
|
2035
2115
|
line-height: 1.25rem;
|
|
@@ -2037,6 +2117,10 @@ video {
|
|
|
2037
2117
|
}
|
|
2038
2118
|
@media (min-width: 1024px) {
|
|
2039
2119
|
|
|
2120
|
+
.lg\:max-w-\[80vw\] {
|
|
2121
|
+
max-width: 80vw;
|
|
2122
|
+
}
|
|
2123
|
+
|
|
2040
2124
|
.lg\:grid-cols-4 {
|
|
2041
2125
|
grid-template-columns: repeat(4, minmax(0, 1fr));
|
|
2042
2126
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@authdog/react-elements",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.43",
|
|
4
4
|
"main": "./dist/index.js",
|
|
5
5
|
"module": "./dist/index.mjs",
|
|
6
6
|
"types": "./dist/index.d.mts",
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
"@storybook/react-vite": "^10.1.3",
|
|
16
16
|
"@types/node": "22.10.10",
|
|
17
17
|
"@types/react": "19.1.8",
|
|
18
|
-
"@types/react-dom": "19.
|
|
18
|
+
"@types/react-dom": "19.1.8",
|
|
19
19
|
"@vitejs/plugin-react": "^4.4.1",
|
|
20
20
|
"autoprefixer": "^10",
|
|
21
21
|
"css-loader": "^6.8.1",
|
|
@@ -33,10 +33,10 @@
|
|
|
33
33
|
"tailwindcss": "3.4.18",
|
|
34
34
|
"ts-loader": "^9.5.1",
|
|
35
35
|
"typescript": "5.7.3",
|
|
36
|
-
"vite": "^
|
|
36
|
+
"vite": "^5.0.0",
|
|
37
37
|
"webpack": "^5.89.0",
|
|
38
|
-
"@authdog/
|
|
39
|
-
"@authdog/
|
|
38
|
+
"@authdog/typescript-config": "0.0.0",
|
|
39
|
+
"@authdog/eslint-config": "0.0.0"
|
|
40
40
|
},
|
|
41
41
|
"dependencies": {
|
|
42
42
|
"@radix-ui/react-avatar": "^1.1.9",
|
|
@@ -84,6 +84,6 @@
|
|
|
84
84
|
"build": "pnpm tsup && pnpm build:styles",
|
|
85
85
|
"storybook": "storybook dev -p 7007",
|
|
86
86
|
"build-storybook": "storybook build",
|
|
87
|
-
"deploy-docs": "wrangler pages deploy"
|
|
87
|
+
"deploy-docs": "pnpm build-storybook && cp wrangler.prod.toml wrangler.toml && wrangler pages deploy"
|
|
88
88
|
}
|
|
89
89
|
}
|
|
@@ -22,6 +22,7 @@ import {
|
|
|
22
22
|
} from "../../components/ui/dropdown-menu";
|
|
23
23
|
import { Sheet, SheetContent, SheetTrigger } from "../../components/ui/sheet";
|
|
24
24
|
import { IconWrapper } from "../icons";
|
|
25
|
+
import { ThemeToggle } from "../ui/theme-toggle";
|
|
25
26
|
|
|
26
27
|
interface NavItem {
|
|
27
28
|
title: string;
|
|
@@ -34,6 +35,7 @@ interface NavbarProps {
|
|
|
34
35
|
children?: React.ReactNode;
|
|
35
36
|
className?: string;
|
|
36
37
|
logoText?: string;
|
|
38
|
+
logoSrc?: string;
|
|
37
39
|
isLoading?: boolean;
|
|
38
40
|
user?: any;
|
|
39
41
|
onNavigateHome?: () => void;
|
|
@@ -52,6 +54,7 @@ export function Navbar({
|
|
|
52
54
|
children,
|
|
53
55
|
className,
|
|
54
56
|
logoText = "ACME Corp",
|
|
57
|
+
logoSrc,
|
|
55
58
|
user = {
|
|
56
59
|
name: "John Doe",
|
|
57
60
|
email: "john@example.com",
|
|
@@ -66,22 +69,54 @@ export function Navbar({
|
|
|
66
69
|
environmentId = "58be35b0-708f-49f6-84f0-6695d307d997",
|
|
67
70
|
}: NavbarProps) {
|
|
68
71
|
const [open, setOpen] = useState(false);
|
|
72
|
+
const [logoFailed, setLogoFailed] = useState(false);
|
|
69
73
|
const isAuthenticated =
|
|
70
74
|
user !== null &&
|
|
71
75
|
user !== undefined &&
|
|
72
76
|
user.id !== null &&
|
|
73
77
|
user.id !== undefined;
|
|
78
|
+
|
|
74
79
|
return (
|
|
75
|
-
<header
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
+
<header
|
|
81
|
+
className={cn(
|
|
82
|
+
"sticky top-0 z-40 w-full border-b bg-background/80 backdrop-blur supports-[backdrop-filter]:bg-background/60",
|
|
83
|
+
className,
|
|
84
|
+
)}
|
|
85
|
+
>
|
|
86
|
+
<div
|
|
87
|
+
className={cn(
|
|
88
|
+
"flex h-16 items-center justify-between px-4 md:px-6",
|
|
89
|
+
"w-full lg:max-w-[80vw] mx-auto",
|
|
90
|
+
)}
|
|
91
|
+
>
|
|
92
|
+
<div className="flex items-center gap-3 md:gap-4">
|
|
93
|
+
<button
|
|
94
|
+
type="button"
|
|
80
95
|
onClick={onNavigateHome}
|
|
96
|
+
className={cn(
|
|
97
|
+
"group inline-flex items-center gap-2 md:gap-3 rounded-md px-1 py-1 text-left",
|
|
98
|
+
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background",
|
|
99
|
+
)}
|
|
100
|
+
aria-label="Go to homepage"
|
|
81
101
|
>
|
|
82
|
-
{
|
|
83
|
-
|
|
84
|
-
|
|
102
|
+
{logoSrc && !logoFailed && (
|
|
103
|
+
<span className="inline-flex h-8 w-8 items-center justify-center overflow-hidden rounded-md bg-muted/80 ring-1 ring-border">
|
|
104
|
+
<img
|
|
105
|
+
src={logoSrc}
|
|
106
|
+
alt={logoText}
|
|
107
|
+
className="h-7 w-7 object-contain"
|
|
108
|
+
onError={() => setLogoFailed(true)}
|
|
109
|
+
/>
|
|
110
|
+
</span>
|
|
111
|
+
)}
|
|
112
|
+
<span className="text-base font-semibold tracking-tight md:text-lg group-hover:text-primary">
|
|
113
|
+
{logoText}
|
|
114
|
+
</span>
|
|
115
|
+
</button>
|
|
116
|
+
{children}
|
|
117
|
+
</div>
|
|
118
|
+
<div className="flex flex-1 items-center justify-end gap-6">
|
|
119
|
+
<nav className="hidden md:flex items-center gap-6">
|
|
85
120
|
{items?.map((item, index) => (
|
|
86
121
|
<span
|
|
87
122
|
key={index}
|
|
@@ -99,119 +134,117 @@ export function Navbar({
|
|
|
99
134
|
</span>
|
|
100
135
|
))}
|
|
101
136
|
</nav>
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
{isAuthenticated ? (
|
|
107
|
-
<DropdownMenu>
|
|
108
|
-
<DropdownMenuTrigger asChild>
|
|
137
|
+
<div className="flex items-center gap-3">
|
|
138
|
+
<Sheet open={open} onOpenChange={setOpen}>
|
|
139
|
+
<SheetTrigger asChild>
|
|
109
140
|
<Button
|
|
110
141
|
variant="ghost"
|
|
111
|
-
|
|
112
|
-
|
|
142
|
+
size="icon"
|
|
143
|
+
className="md:hidden"
|
|
144
|
+
aria-label="Open Menu"
|
|
113
145
|
>
|
|
114
|
-
<
|
|
115
|
-
{isLoading ? (
|
|
116
|
-
<div className="h-8 w-8 animate-pulse bg-muted rounded-full" />
|
|
117
|
-
) : (
|
|
118
|
-
<>
|
|
119
|
-
<AvatarImage
|
|
120
|
-
src={user.photos?.[0]?.value || "/placeholder.svg"}
|
|
121
|
-
alt={user.displayName}
|
|
122
|
-
/>
|
|
123
|
-
<AvatarFallback>
|
|
124
|
-
{user.displayName?.charAt(0)}
|
|
125
|
-
</AvatarFallback>
|
|
126
|
-
</>
|
|
127
|
-
)}
|
|
128
|
-
</Avatar>
|
|
146
|
+
<IconWrapper Icon={Menu} />
|
|
129
147
|
</Button>
|
|
130
|
-
</
|
|
131
|
-
<
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
<
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
148
|
+
</SheetTrigger>
|
|
149
|
+
<SheetContent side="left" className="pr-0">
|
|
150
|
+
<nav className="grid gap-2 py-6">
|
|
151
|
+
{items?.map((item, index) => (
|
|
152
|
+
<a
|
|
153
|
+
key={index}
|
|
154
|
+
href={item.href}
|
|
155
|
+
className={cn(
|
|
156
|
+
"flex w-full items-center rounded-md px-3 py-2 text-sm font-medium hover:bg-accent",
|
|
157
|
+
item.disabled && "cursor-not-allowed opacity-80",
|
|
158
|
+
)}
|
|
159
|
+
onClick={() => setOpen(false)}
|
|
160
|
+
>
|
|
161
|
+
{item.title}
|
|
162
|
+
</a>
|
|
163
|
+
))}
|
|
164
|
+
</nav>
|
|
165
|
+
</SheetContent>
|
|
166
|
+
</Sheet>
|
|
167
|
+
<ThemeToggle />
|
|
168
|
+
{isAuthenticated ? (
|
|
169
|
+
<DropdownMenu>
|
|
170
|
+
<DropdownMenuTrigger asChild>
|
|
171
|
+
<Button
|
|
172
|
+
variant="ghost"
|
|
173
|
+
className="relative h-8 w-8 rounded-full"
|
|
174
|
+
disabled={isLoading}
|
|
175
|
+
>
|
|
176
|
+
<Avatar className="h-8 w-8">
|
|
177
|
+
{isLoading ? (
|
|
178
|
+
<div className="h-8 w-8 animate-pulse bg-muted rounded-full" />
|
|
179
|
+
) : (
|
|
180
|
+
<>
|
|
181
|
+
<AvatarImage
|
|
182
|
+
src={user.photos?.[0]?.value || "/placeholder.svg"}
|
|
183
|
+
alt={user.displayName}
|
|
184
|
+
/>
|
|
185
|
+
<AvatarFallback>
|
|
186
|
+
{user.displayName?.charAt(0)}
|
|
187
|
+
</AvatarFallback>
|
|
188
|
+
</>
|
|
189
|
+
)}
|
|
190
|
+
</Avatar>
|
|
191
|
+
</Button>
|
|
192
|
+
</DropdownMenuTrigger>
|
|
193
|
+
<DropdownMenuContent className="w-56" align="end" forceMount>
|
|
194
|
+
{isLoading ? (
|
|
195
|
+
<div className="p-4">
|
|
196
|
+
<div className="h-4 w-3/4 animate-pulse bg-muted rounded mb-2" />
|
|
197
|
+
<div className="h-3 w-1/2 animate-pulse bg-muted rounded" />
|
|
198
|
+
</div>
|
|
199
|
+
) : (
|
|
200
|
+
<>
|
|
201
|
+
<DropdownMenuLabel className="font-normal">
|
|
202
|
+
<div className="flex flex-col space-y-1">
|
|
203
|
+
<p className="text-sm font-medium leading-none">
|
|
204
|
+
{user.displayName}
|
|
205
|
+
</p>
|
|
206
|
+
<p className="text-xs leading-none text-muted-foreground">
|
|
207
|
+
{user.emails?.[0]?.value}
|
|
208
|
+
</p>
|
|
209
|
+
</div>
|
|
210
|
+
</DropdownMenuLabel>
|
|
211
|
+
<DropdownMenuSeparator />
|
|
212
|
+
<DropdownMenuGroup>
|
|
213
|
+
<DropdownMenuItem onClick={onProfileSelected}>
|
|
214
|
+
<IconWrapper Icon={User} />
|
|
215
|
+
<span>Profile</span>
|
|
216
|
+
</DropdownMenuItem>
|
|
217
|
+
</DropdownMenuGroup>
|
|
218
|
+
<DropdownMenuSeparator />
|
|
219
|
+
<DropdownMenuItem onClick={onLogout}>
|
|
220
|
+
<IconWrapper Icon={LogOut} />
|
|
221
|
+
<span>Log out</span>
|
|
154
222
|
</DropdownMenuItem>
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
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
|
-
}
|
|
223
|
+
</>
|
|
224
|
+
)}
|
|
225
|
+
</DropdownMenuContent>
|
|
226
|
+
</DropdownMenu>
|
|
227
|
+
) : (
|
|
228
|
+
<Button
|
|
229
|
+
variant="default"
|
|
230
|
+
aria-label="Sign in"
|
|
231
|
+
onClick={() => {
|
|
232
|
+
if (!environmentId) {
|
|
233
|
+
throw new Error("Environment ID is required");
|
|
234
|
+
}
|
|
177
235
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
>
|
|
182
|
-
Sign in
|
|
183
|
-
</Button>
|
|
184
|
-
)}
|
|
236
|
+
if (!identityHost) {
|
|
237
|
+
throw new Error("Identity Host is required");
|
|
238
|
+
}
|
|
185
239
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
variant="ghost"
|
|
190
|
-
size="icon"
|
|
191
|
-
className="md:hidden"
|
|
192
|
-
aria-label="Open Menu"
|
|
240
|
+
const signinUrl = `${identityHost}/signin/${environmentId}`;
|
|
241
|
+
window.open(signinUrl, "_blank");
|
|
242
|
+
}}
|
|
193
243
|
>
|
|
194
|
-
|
|
244
|
+
Sign in
|
|
195
245
|
</Button>
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
<nav className="grid gap-2 py-6">
|
|
199
|
-
{items?.map((item, index) => (
|
|
200
|
-
<a
|
|
201
|
-
key={index}
|
|
202
|
-
href={item.href}
|
|
203
|
-
className={cn(
|
|
204
|
-
"flex w-full items-center rounded-md px-3 py-2 text-sm font-medium hover:bg-accent",
|
|
205
|
-
item.disabled && "cursor-not-allowed opacity-80",
|
|
206
|
-
)}
|
|
207
|
-
onClick={() => setOpen(false)}
|
|
208
|
-
>
|
|
209
|
-
{item.title}
|
|
210
|
-
</a>
|
|
211
|
-
))}
|
|
212
|
-
</nav>
|
|
213
|
-
</SheetContent>
|
|
214
|
-
</Sheet>
|
|
246
|
+
)}
|
|
247
|
+
</div>
|
|
215
248
|
</div>
|
|
216
249
|
</div>
|
|
217
250
|
</header>
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useEffect, useState } from "react";
|
|
4
|
+
import { Sun, Moon } from "lucide-react";
|
|
5
|
+
|
|
6
|
+
import { Button } from "./button";
|
|
7
|
+
|
|
8
|
+
const STORAGE_KEY = "authdog-theme";
|
|
9
|
+
|
|
10
|
+
type ThemeMode = "light" | "dark";
|
|
11
|
+
|
|
12
|
+
const getPreferredTheme = (): ThemeMode => {
|
|
13
|
+
if (typeof window === "undefined") return "light";
|
|
14
|
+
const stored = window.localStorage.getItem(STORAGE_KEY);
|
|
15
|
+
if (stored === "light" || stored === "dark") {
|
|
16
|
+
return stored;
|
|
17
|
+
}
|
|
18
|
+
return window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
const applyTheme = (mode: ThemeMode) => {
|
|
22
|
+
document.documentElement.classList.toggle("dark", mode === "dark");
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export const ThemeToggle = () => {
|
|
26
|
+
const [mode, setMode] = useState<ThemeMode>("light");
|
|
27
|
+
|
|
28
|
+
useEffect(() => {
|
|
29
|
+
const initial = getPreferredTheme();
|
|
30
|
+
applyTheme(initial);
|
|
31
|
+
setMode(initial);
|
|
32
|
+
}, []);
|
|
33
|
+
|
|
34
|
+
const toggle = () => {
|
|
35
|
+
const nextMode: ThemeMode = mode === "dark" ? "light" : "dark";
|
|
36
|
+
applyTheme(nextMode);
|
|
37
|
+
try {
|
|
38
|
+
window.localStorage.setItem(STORAGE_KEY, nextMode);
|
|
39
|
+
} catch {
|
|
40
|
+
// ignore
|
|
41
|
+
}
|
|
42
|
+
setMode(nextMode);
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
return (
|
|
46
|
+
<Button
|
|
47
|
+
variant="ghost"
|
|
48
|
+
size="icon"
|
|
49
|
+
aria-label={mode === "dark" ? "Switch to light theme" : "Switch to dark theme"}
|
|
50
|
+
onClick={toggle}
|
|
51
|
+
>
|
|
52
|
+
{mode === "dark" ? <Sun className="h-4 w-4" /> : <Moon className="h-4 w-4" />}
|
|
53
|
+
</Button>
|
|
54
|
+
);
|
|
55
|
+
};
|