@oussemasahbeni/keycloakify-login-shadcn 250004.0.2

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 (211) hide show
  1. package/keycloak-theme/components/langauges.tsx +58 -0
  2. package/keycloak-theme/components/lib/utils.ts +6 -0
  3. package/keycloak-theme/components/theme-provider.tsx +72 -0
  4. package/keycloak-theme/components/theme-toggle.tsx +23 -0
  5. package/keycloak-theme/components/ui/alert.tsx +82 -0
  6. package/keycloak-theme/components/ui/button.tsx +57 -0
  7. package/keycloak-theme/components/ui/card.tsx +73 -0
  8. package/keycloak-theme/components/ui/checkbox.tsx +29 -0
  9. package/keycloak-theme/components/ui/dropdown-menu.tsx +182 -0
  10. package/keycloak-theme/components/ui/field.tsx +240 -0
  11. package/keycloak-theme/components/ui/input-otp.tsx +70 -0
  12. package/keycloak-theme/components/ui/input.tsx +25 -0
  13. package/keycloak-theme/components/ui/label.tsx +25 -0
  14. package/keycloak-theme/components/ui/radio-group.tsx +43 -0
  15. package/keycloak-theme/components/ui/select.tsx +142 -0
  16. package/keycloak-theme/components/ui/separator.tsx +27 -0
  17. package/keycloak-theme/components/ui/tooltip.tsx +31 -0
  18. package/keycloak-theme/login/KcContext.ts +19 -0
  19. package/keycloak-theme/login/KcPage.tsx +60 -0
  20. package/keycloak-theme/login/assets/fonts/geist/Geist-Black.woff2 +0 -0
  21. package/keycloak-theme/login/assets/fonts/geist/Geist-Bold.woff2 +0 -0
  22. package/keycloak-theme/login/assets/fonts/geist/Geist-Light.woff2 +0 -0
  23. package/keycloak-theme/login/assets/fonts/geist/Geist-Medium.woff2 +0 -0
  24. package/keycloak-theme/login/assets/fonts/geist/Geist-Regular.woff2 +0 -0
  25. package/keycloak-theme/login/assets/fonts/geist/Geist-SemiBold.woff2 +0 -0
  26. package/keycloak-theme/login/assets/fonts/geist/Geist-Thin.woff2 +0 -0
  27. package/keycloak-theme/login/assets/fonts/geist/Geist-UltraBlack.woff2 +0 -0
  28. package/keycloak-theme/login/assets/fonts/geist/Geist-UltraLight.woff2 +0 -0
  29. package/keycloak-theme/login/assets/fonts/geist/GeistVariableVF.woff2 +0 -0
  30. package/keycloak-theme/login/assets/fonts/geist/index.css +79 -0
  31. package/keycloak-theme/login/assets/fonts/inter/Inter-italic.var.woff2 +0 -0
  32. package/keycloak-theme/login/assets/fonts/inter/Inter-roman.var.woff2 +0 -0
  33. package/keycloak-theme/login/assets/fonts/inter/inter.css +15 -0
  34. package/keycloak-theme/login/assets/img/auth-logo.svg +101 -0
  35. package/keycloak-theme/login/assets/img/shape.svg +71 -0
  36. package/keycloak-theme/login/components/LogoutOtherSessions.tsx +26 -0
  37. package/keycloak-theme/login/components/PasswordWrapper.tsx +35 -0
  38. package/keycloak-theme/login/components/Template/Template.tsx +226 -0
  39. package/keycloak-theme/login/components/Template/index.ts +1 -0
  40. package/keycloak-theme/login/components/Template/useInitializeTemplate.ts +61 -0
  41. package/keycloak-theme/login/components/UserProfileFormFields/AddRemoveButtonsMultiValuedAttribute.tsx +61 -0
  42. package/keycloak-theme/login/components/UserProfileFormFields/DO_MAKE_USER_CONFIRM_PASSWORD.ts +2 -0
  43. package/keycloak-theme/login/components/UserProfileFormFields/FieldErrors.tsx +29 -0
  44. package/keycloak-theme/login/components/UserProfileFormFields/GroupLabel.tsx +71 -0
  45. package/keycloak-theme/login/components/UserProfileFormFields/InputFieldByType.tsx +59 -0
  46. package/keycloak-theme/login/components/UserProfileFormFields/InputLabel.tsx +22 -0
  47. package/keycloak-theme/login/components/UserProfileFormFields/InputTag.tsx +116 -0
  48. package/keycloak-theme/login/components/UserProfileFormFields/InputTagSelects.tsx +136 -0
  49. package/keycloak-theme/login/components/UserProfileFormFields/SelectTag.tsx +114 -0
  50. package/keycloak-theme/login/components/UserProfileFormFields/TextareaTag.tsx +43 -0
  51. package/keycloak-theme/login/components/UserProfileFormFields/UserProfileFormFields.tsx +127 -0
  52. package/keycloak-theme/login/components/UserProfileFormFields/index.ts +1 -0
  53. package/keycloak-theme/login/i18n.ts +51 -0
  54. package/keycloak-theme/login/index.css +191 -0
  55. package/keycloak-theme/login/mocks/KcPageStory.tsx +22 -0
  56. package/keycloak-theme/login/mocks/getKcContextMock.ts +18 -0
  57. package/keycloak-theme/login/pages/PageIndex.tsx +134 -0
  58. package/keycloak-theme/login/pages/code/Page.stories.tsx +103 -0
  59. package/keycloak-theme/login/pages/code/Page.tsx +89 -0
  60. package/keycloak-theme/login/pages/code/index.ts +3 -0
  61. package/keycloak-theme/login/pages/delete-account-confirm/Page.stories.tsx +69 -0
  62. package/keycloak-theme/login/pages/delete-account-confirm/Page.tsx +63 -0
  63. package/keycloak-theme/login/pages/delete-account-confirm/index.ts +3 -0
  64. package/keycloak-theme/login/pages/delete-credential/Page.stories.tsx +49 -0
  65. package/keycloak-theme/login/pages/delete-credential/Page.tsx +51 -0
  66. package/keycloak-theme/login/pages/delete-credential/index.ts +3 -0
  67. package/keycloak-theme/login/pages/error/Page.stories.tsx +85 -0
  68. package/keycloak-theme/login/pages/error/Page.tsx +42 -0
  69. package/keycloak-theme/login/pages/error/index.ts +3 -0
  70. package/keycloak-theme/login/pages/frontchannel-logout/Page.stories.tsx +51 -0
  71. package/keycloak-theme/login/pages/frontchannel-logout/Page.tsx +84 -0
  72. package/keycloak-theme/login/pages/frontchannel-logout/index.ts +3 -0
  73. package/keycloak-theme/login/pages/idp-review-user-profile/Page.stories.tsx +85 -0
  74. package/keycloak-theme/login/pages/idp-review-user-profile/Page.tsx +52 -0
  75. package/keycloak-theme/login/pages/idp-review-user-profile/index.ts +3 -0
  76. package/keycloak-theme/login/pages/info/Page.stories.tsx +86 -0
  77. package/keycloak-theme/login/pages/info/Page.tsx +92 -0
  78. package/keycloak-theme/login/pages/info/index.ts +14 -0
  79. package/keycloak-theme/login/pages/link-idp-action/Page.stories.tsx +34 -0
  80. package/keycloak-theme/login/pages/link-idp-action/Page.tsx +43 -0
  81. package/keycloak-theme/login/pages/link-idp-action/index.ts +3 -0
  82. package/keycloak-theme/login/pages/login/Form.tsx +242 -0
  83. package/keycloak-theme/login/pages/login/Info.tsx +29 -0
  84. package/keycloak-theme/login/pages/login/Page.stories.tsx +365 -0
  85. package/keycloak-theme/login/pages/login/Page.tsx +44 -0
  86. package/keycloak-theme/login/pages/login/SocialProviders.tsx +107 -0
  87. package/keycloak-theme/login/pages/login/index.ts +3 -0
  88. package/keycloak-theme/login/pages/login/providers/apple.svg +4 -0
  89. package/keycloak-theme/login/pages/login/providers/bitbucket.svg +11 -0
  90. package/keycloak-theme/login/pages/login/providers/discord.svg +4 -0
  91. package/keycloak-theme/login/pages/login/providers/facebook.svg +5 -0
  92. package/keycloak-theme/login/pages/login/providers/github.svg +4 -0
  93. package/keycloak-theme/login/pages/login/providers/gitlab.svg +7 -0
  94. package/keycloak-theme/login/pages/login/providers/google.svg +7 -0
  95. package/keycloak-theme/login/pages/login/providers/instagram.svg +32 -0
  96. package/keycloak-theme/login/pages/login/providers/linkedin.svg +4 -0
  97. package/keycloak-theme/login/pages/login/providers/microsoft.svg +7 -0
  98. package/keycloak-theme/login/pages/login/providers/oidc.svg +6 -0
  99. package/keycloak-theme/login/pages/login/providers/openshift.svg +8 -0
  100. package/keycloak-theme/login/pages/login/providers/paypal.svg +6 -0
  101. package/keycloak-theme/login/pages/login/providers/slack.svg +11 -0
  102. package/keycloak-theme/login/pages/login/providers/stackoverflow.svg +5 -0
  103. package/keycloak-theme/login/pages/login/providers/x.svg +4 -0
  104. package/keycloak-theme/login/pages/login/useProviderLogos.tsx +39 -0
  105. package/keycloak-theme/login/pages/login/useScript.tsx +62 -0
  106. package/keycloak-theme/login/pages/login-config-totp/Page.stories.tsx +89 -0
  107. package/keycloak-theme/login/pages/login-config-totp/Page.tsx +240 -0
  108. package/keycloak-theme/login/pages/login-config-totp/index.ts +3 -0
  109. package/keycloak-theme/login/pages/login-idp-link-confirm/Page.stories.tsx +78 -0
  110. package/keycloak-theme/login/pages/login-idp-link-confirm/Page.tsx +43 -0
  111. package/keycloak-theme/login/pages/login-idp-link-confirm/index.ts +3 -0
  112. package/keycloak-theme/login/pages/login-idp-link-confirm-override/Page.stories.tsx +43 -0
  113. package/keycloak-theme/login/pages/login-idp-link-confirm-override/Page.tsx +47 -0
  114. package/keycloak-theme/login/pages/login-idp-link-confirm-override/index.ts +3 -0
  115. package/keycloak-theme/login/pages/login-idp-link-email/Page.stories.tsx +130 -0
  116. package/keycloak-theme/login/pages/login-idp-link-email/Page.tsx +54 -0
  117. package/keycloak-theme/login/pages/login-idp-link-email/index.ts +3 -0
  118. package/keycloak-theme/login/pages/login-oauth-grant/Page.stories.tsx +111 -0
  119. package/keycloak-theme/login/pages/login-oauth-grant/Page.tsx +126 -0
  120. package/keycloak-theme/login/pages/login-oauth-grant/index.ts +3 -0
  121. package/keycloak-theme/login/pages/login-oauth2-device-verify-user-code/Page.stories.tsx +86 -0
  122. package/keycloak-theme/login/pages/login-oauth2-device-verify-user-code/Page.tsx +58 -0
  123. package/keycloak-theme/login/pages/login-oauth2-device-verify-user-code/index.ts +3 -0
  124. package/keycloak-theme/login/pages/login-otp/Page.stories.tsx +151 -0
  125. package/keycloak-theme/login/pages/login-otp/Page.tsx +108 -0
  126. package/keycloak-theme/login/pages/login-otp/index.ts +3 -0
  127. package/keycloak-theme/login/pages/login-page-expired/Page.stories.tsx +64 -0
  128. package/keycloak-theme/login/pages/login-page-expired/Page.tsx +47 -0
  129. package/keycloak-theme/login/pages/login-page-expired/index.ts +3 -0
  130. package/keycloak-theme/login/pages/login-passkeys-conditional-authenticate/Page.stories.tsx +16 -0
  131. package/keycloak-theme/login/pages/login-passkeys-conditional-authenticate/Page.tsx +233 -0
  132. package/keycloak-theme/login/pages/login-passkeys-conditional-authenticate/index.ts +3 -0
  133. package/keycloak-theme/login/pages/login-passkeys-conditional-authenticate/useScript.tsx +63 -0
  134. package/keycloak-theme/login/pages/login-password/Page.stories.tsx +81 -0
  135. package/keycloak-theme/login/pages/login-password/Page.tsx +149 -0
  136. package/keycloak-theme/login/pages/login-password/index.ts +3 -0
  137. package/keycloak-theme/login/pages/login-password/useScript.tsx +63 -0
  138. package/keycloak-theme/login/pages/login-recovery-authn-code-config/Page.stories.tsx +65 -0
  139. package/keycloak-theme/login/pages/login-recovery-authn-code-config/Page.tsx +181 -0
  140. package/keycloak-theme/login/pages/login-recovery-authn-code-config/index.ts +3 -0
  141. package/keycloak-theme/login/pages/login-recovery-authn-code-config/useScript.tsx +145 -0
  142. package/keycloak-theme/login/pages/login-recovery-authn-code-input/Page.stories.tsx +42 -0
  143. package/keycloak-theme/login/pages/login-recovery-authn-code-input/Page.tsx +70 -0
  144. package/keycloak-theme/login/pages/login-recovery-authn-code-input/index.ts +3 -0
  145. package/keycloak-theme/login/pages/login-reset-otp/Page.stories.tsx +120 -0
  146. package/keycloak-theme/login/pages/login-reset-otp/Page.tsx +86 -0
  147. package/keycloak-theme/login/pages/login-reset-otp/index.ts +3 -0
  148. package/keycloak-theme/login/pages/login-reset-password/Form.tsx +68 -0
  149. package/keycloak-theme/login/pages/login-reset-password/Page.stories.tsx +84 -0
  150. package/keycloak-theme/login/pages/login-reset-password/Page.tsx +27 -0
  151. package/keycloak-theme/login/pages/login-reset-password/index.ts +3 -0
  152. package/keycloak-theme/login/pages/login-update-password/Page.stories.tsx +87 -0
  153. package/keycloak-theme/login/pages/login-update-password/Page.tsx +111 -0
  154. package/keycloak-theme/login/pages/login-update-password/index.ts +3 -0
  155. package/keycloak-theme/login/pages/login-update-profile/Page.stories.tsx +64 -0
  156. package/keycloak-theme/login/pages/login-update-profile/Page.tsx +68 -0
  157. package/keycloak-theme/login/pages/login-update-profile/index.ts +3 -0
  158. package/keycloak-theme/login/pages/login-username/Page.stories.tsx +67 -0
  159. package/keycloak-theme/login/pages/login-username/Page.tsx +246 -0
  160. package/keycloak-theme/login/pages/login-username/index.ts +3 -0
  161. package/keycloak-theme/login/pages/login-username/useScript.tsx +62 -0
  162. package/keycloak-theme/login/pages/login-verify-email/Page.stories.tsx +128 -0
  163. package/keycloak-theme/login/pages/login-verify-email/Page.tsx +38 -0
  164. package/keycloak-theme/login/pages/login-verify-email/index.ts +3 -0
  165. package/keycloak-theme/login/pages/login-x509-info/Page.stories.tsx +66 -0
  166. package/keycloak-theme/login/pages/login-x509-info/Page.tsx +75 -0
  167. package/keycloak-theme/login/pages/login-x509-info/index.ts +3 -0
  168. package/keycloak-theme/login/pages/logout-confirm/Page.stories.tsx +70 -0
  169. package/keycloak-theme/login/pages/logout-confirm/Page.tsx +53 -0
  170. package/keycloak-theme/login/pages/logout-confirm/index.ts +3 -0
  171. package/keycloak-theme/login/pages/register/Form.tsx +106 -0
  172. package/keycloak-theme/login/pages/register/Page.stories.tsx +258 -0
  173. package/keycloak-theme/login/pages/register/Page.tsx +26 -0
  174. package/keycloak-theme/login/pages/register/TermsAcceptance.tsx +56 -0
  175. package/keycloak-theme/login/pages/register/index.ts +3 -0
  176. package/keycloak-theme/login/pages/saml-post-form/Page.stories.tsx +41 -0
  177. package/keycloak-theme/login/pages/saml-post-form/Page.tsx +66 -0
  178. package/keycloak-theme/login/pages/saml-post-form/index.ts +3 -0
  179. package/keycloak-theme/login/pages/select-authenticator/Page.stories.tsx +131 -0
  180. package/keycloak-theme/login/pages/select-authenticator/Page.tsx +100 -0
  181. package/keycloak-theme/login/pages/select-authenticator/index.ts +3 -0
  182. package/keycloak-theme/login/pages/select-organization/Page.stories.tsx +90 -0
  183. package/keycloak-theme/login/pages/select-organization/Page.tsx +126 -0
  184. package/keycloak-theme/login/pages/select-organization/index.ts +3 -0
  185. package/keycloak-theme/login/pages/terms/Page.stories.tsx +78 -0
  186. package/keycloak-theme/login/pages/terms/Page.tsx +51 -0
  187. package/keycloak-theme/login/pages/terms/index.ts +3 -0
  188. package/keycloak-theme/login/pages/update-email/Page.stories.tsx +63 -0
  189. package/keycloak-theme/login/pages/update-email/Page.tsx +62 -0
  190. package/keycloak-theme/login/pages/update-email/index.ts +3 -0
  191. package/keycloak-theme/login/pages/webauthn-authenticate/Page.stories.tsx +182 -0
  192. package/keycloak-theme/login/pages/webauthn-authenticate/Page.tsx +202 -0
  193. package/keycloak-theme/login/pages/webauthn-authenticate/index.ts +3 -0
  194. package/keycloak-theme/login/pages/webauthn-authenticate/useScript.tsx +55 -0
  195. package/keycloak-theme/login/pages/webauthn-error/Page.stories.tsx +112 -0
  196. package/keycloak-theme/login/pages/webauthn-error/Page.tsx +73 -0
  197. package/keycloak-theme/login/pages/webauthn-error/index.ts +3 -0
  198. package/keycloak-theme/login/pages/webauthn-register/Page.stories.tsx +86 -0
  199. package/keycloak-theme/login/pages/webauthn-register/Page.tsx +78 -0
  200. package/keycloak-theme/login/pages/webauthn-register/index.ts +3 -0
  201. package/keycloak-theme/login/pages/webauthn-register/useScript.tsx +62 -0
  202. package/keycloak-theme/login/shared/redirectUrlOrigin.ts +31 -0
  203. package/keycloak-theme/login/styleLevelCustomization.tsx +17 -0
  204. package/keycloak-theme/public/android-chrome-192x192.png +0 -0
  205. package/keycloak-theme/public/android-chrome-512x512.png +0 -0
  206. package/keycloak-theme/public/apple-touch-icon.png +0 -0
  207. package/keycloak-theme/public/favicon-16x16.png +0 -0
  208. package/keycloak-theme/public/favicon-32x32.png +0 -0
  209. package/keycloak-theme/public/favicon.ico +0 -0
  210. package/keycloak-theme/public/site.webmanifest +1 -0
  211. package/package.json +37 -0
@@ -0,0 +1,58 @@
1
+ import {
2
+ DropdownMenu,
3
+ DropdownMenuContent,
4
+ DropdownMenuItem,
5
+ DropdownMenuTrigger
6
+ } from "@/components/ui/dropdown-menu";
7
+ import { useI18n } from '@/login/i18n';
8
+ import { IoCheckmark, IoLanguage } from "react-icons/io5";
9
+ import { Button } from "./ui/button";
10
+
11
+ export function Languages() {
12
+ const { msgStr, currentLanguage, enabledLanguages } = useI18n();
13
+
14
+ return (
15
+ <div>
16
+ <DropdownMenu>
17
+ <DropdownMenuTrigger asChild>
18
+ <Button
19
+ tabIndex={1}
20
+ variant="outline"
21
+ size="icon"
22
+ aria-label={msgStr("languages")}
23
+ aria-haspopup="true"
24
+ aria-expanded="false"
25
+ aria-controls="language-switch1"
26
+ >
27
+ <IoLanguage />
28
+ </Button>
29
+ </DropdownMenuTrigger>
30
+ <DropdownMenuContent
31
+ id="language-switch1"
32
+ role="menu"
33
+ className="max-h-72 overflow-y-auto"
34
+ >
35
+ {enabledLanguages.map(({ languageTag, label, href }, i) => {
36
+ const isActive = languageTag === currentLanguage.languageTag;
37
+
38
+ return (
39
+ <DropdownMenuItem key={languageTag} asChild>
40
+ <a
41
+ role="menuitem"
42
+ id={`language-${i + 1}`}
43
+ href={href}
44
+ className="flex w-full items-center justify-between cursor-pointer"
45
+ >
46
+ {label}
47
+ {isActive && (
48
+ <IoCheckmark className="h-4 w-4 opacity-50" />
49
+ )}
50
+ </a>
51
+ </DropdownMenuItem>
52
+ );
53
+ })}
54
+ </DropdownMenuContent>
55
+ </DropdownMenu>
56
+ </div>
57
+ );
58
+ }
@@ -0,0 +1,6 @@
1
+ import { clsx, type ClassValue } from "clsx";
2
+ import { twMerge } from "tailwind-merge";
3
+
4
+ export function cn(...inputs: ClassValue[]) {
5
+ return twMerge(clsx(inputs));
6
+ }
@@ -0,0 +1,72 @@
1
+ import { createContext, useContext, useEffect, useState } from "react";
2
+
3
+ type Theme = "dark" | "light" | "system";
4
+
5
+ type ThemeProviderProps = {
6
+ children: React.ReactNode;
7
+ defaultTheme?: Theme;
8
+ storageKey?: string;
9
+ };
10
+
11
+ type ThemeProviderState = {
12
+ theme: Theme;
13
+ setTheme: (theme: Theme) => void;
14
+ };
15
+
16
+ const initialState: ThemeProviderState = {
17
+ theme: "system",
18
+ setTheme: () => null
19
+ };
20
+
21
+ const ThemeProviderContext = createContext<ThemeProviderState>(initialState);
22
+
23
+ export function ThemeProvider({
24
+ children,
25
+ defaultTheme = "system",
26
+ storageKey = "isDarkMode",
27
+ ...props
28
+ }: ThemeProviderProps) {
29
+ const [theme, setTheme] = useState<Theme>(
30
+ () => (localStorage.getItem(storageKey) as Theme) || defaultTheme
31
+ );
32
+
33
+ useEffect(() => {
34
+ const root = window.document.documentElement;
35
+
36
+ root.classList.remove("light", "dark");
37
+
38
+ if (theme === "system") {
39
+ const systemTheme = window.matchMedia("(prefers-color-scheme: dark)").matches
40
+ ? "dark"
41
+ : "light";
42
+
43
+ root.classList.add(systemTheme);
44
+ return;
45
+ }
46
+
47
+ root.classList.add(theme);
48
+ }, [theme]);
49
+
50
+ const value = {
51
+ theme,
52
+ setTheme: (theme: Theme) => {
53
+ localStorage.setItem(storageKey, theme);
54
+ setTheme(theme);
55
+ }
56
+ };
57
+
58
+ return (
59
+ <ThemeProviderContext.Provider {...props} value={value}>
60
+ {children}
61
+ </ThemeProviderContext.Provider>
62
+ );
63
+ }
64
+
65
+ export const useTheme = () => {
66
+ const context = useContext(ThemeProviderContext);
67
+
68
+ if (context === undefined)
69
+ throw new Error("useTheme must be used within a ThemeProvider");
70
+
71
+ return context;
72
+ };
@@ -0,0 +1,23 @@
1
+ import { Moon, Sun } from "lucide-react";
2
+ import { useTheme } from "./theme-provider";
3
+ import { Button } from "./ui/button";
4
+
5
+ export function ModeToggle() {
6
+ const { theme, setTheme } = useTheme();
7
+
8
+ const toggleTheme = () => {
9
+ if (theme === "light") {
10
+ setTheme("dark");
11
+ } else {
12
+ setTheme("light");
13
+ }
14
+ };
15
+
16
+ return (
17
+ <Button variant="outline" size="icon" onClick={toggleTheme}>
18
+ <Sun className="h-[1.2rem] w-[1.2rem] rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0" />
19
+ <Moon className="absolute h-[1.2rem] w-[1.2rem] rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100" />
20
+ <span className="sr-only">Toggle theme</span>
21
+ </Button>
22
+ );
23
+ }
@@ -0,0 +1,82 @@
1
+ import { cva, type VariantProps } from "class-variance-authority";
2
+ import { AlertTriangle, Info, XCircle } from "lucide-react";
3
+ import * as React from "react";
4
+ import { MdCheckCircle } from "react-icons/md";
5
+
6
+ import { cn } from "@/components/lib/utils";
7
+
8
+ const alertVariants = cva(
9
+ "relative w-full rounded-lg border p-4 text-sm [&>svg+div]:translate-y-[-3px] [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg~*]:pl-7",
10
+ {
11
+ variants: {
12
+ variant: {
13
+ info: "bg-card text-card-foreground",
14
+ error: "border-red-200 bg-red-50 text-red-800 dark:border-red-800 dark:bg-red-950/50 dark:text-red-300 [&>svg]:text-red-800 dark:[&>svg]:text-red-300",
15
+ warning:
16
+ "border-amber-200 bg-amber-50 text-amber-800 dark:border-amber-800 dark:bg-amber-950/50 dark:text-amber-300 [&>svg]:text-amber-800 dark:[&>svg]:text-amber-300",
17
+ success:
18
+ "border-green-200 bg-green-50 text-green-800 dark:border-green-800 dark:bg-green-950/50 dark:text-green-300 [&>svg]:text-green-800 dark:[&>svg]:text-green-300"
19
+ }
20
+ },
21
+ defaultVariants: {
22
+ variant: "info"
23
+ }
24
+ }
25
+ );
26
+
27
+ const Alert = React.forwardRef<
28
+ HTMLDivElement,
29
+ React.HTMLAttributes<HTMLDivElement> &
30
+ VariantProps<typeof alertVariants> & { showIcon?: boolean }
31
+ >(({ className, showIcon = true, variant, ...props }, ref) => (
32
+ <div
33
+ ref={ref}
34
+ role="alert"
35
+ className={cn(alertVariants({ variant }), className)}
36
+ {...props}
37
+ >
38
+ <div className="flex items-start gap-3">
39
+ {showIcon && (
40
+ <>
41
+ {variant === "info" && <Info className="h-5 w-5 flex-shrink-0" />}
42
+ {variant === "error" && <XCircle className="h-5 w-5 flex-shrink-0" />}
43
+ {variant === "warning" && (
44
+ <AlertTriangle className="h-5 w-5 flex-shrink-0" />
45
+ )}
46
+ {variant === "success" && (
47
+ <MdCheckCircle className="h-5 w-5 flex-shrink-0" />
48
+ )}
49
+ </>
50
+ )}
51
+ {props.children}
52
+ </div>
53
+ </div>
54
+ ));
55
+ Alert.displayName = "Alert";
56
+
57
+ const AlertTitle = React.forwardRef<
58
+ HTMLParagraphElement,
59
+ React.HTMLAttributes<HTMLHeadingElement>
60
+ >(({ className, ...props }, ref) => (
61
+ <h5
62
+ ref={ref}
63
+ className={cn("mb-1 font-medium leading-none tracking-tight", className)}
64
+ {...props}
65
+ />
66
+ ));
67
+ AlertTitle.displayName = "AlertTitle";
68
+
69
+ const AlertDescription = React.forwardRef<
70
+ HTMLParagraphElement,
71
+ React.HTMLAttributes<HTMLParagraphElement>
72
+ >(({ className, ...props }, ref) => (
73
+ <div
74
+ ref={ref}
75
+ className={cn("text-sm [&_p]:leading-relaxed", className)}
76
+ {...props}
77
+ />
78
+ ));
79
+ AlertDescription.displayName = "AlertDescription";
80
+
81
+ export { Alert, AlertDescription, AlertTitle };
82
+
@@ -0,0 +1,57 @@
1
+ import { Slot } from "@radix-ui/react-slot";
2
+ import { cva, type VariantProps } from "class-variance-authority";
3
+ import * as React from "react";
4
+
5
+ import { cn } from "@/components/lib/utils";
6
+
7
+ const buttonVariants = cva(
8
+ "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
9
+ {
10
+ variants: {
11
+ variant: {
12
+ default: "bg-primary text-primary-foreground shadow hover:bg-primary/90",
13
+ destructive:
14
+ "bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90",
15
+ outline:
16
+ "border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground",
17
+ secondary:
18
+ "bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80",
19
+ ghost: "hover:bg-accent hover:text-accent-foreground",
20
+ link: "text-primary underline-offset-4 hover:underline"
21
+ },
22
+ size: {
23
+ default: "h-9 px-4 py-2",
24
+ sm: "h-8 rounded-md px-3 text-xs",
25
+ lg: "h-10 rounded-md px-8",
26
+ icon: "h-9 w-9"
27
+ }
28
+ },
29
+ defaultVariants: {
30
+ variant: "default",
31
+ size: "default"
32
+ }
33
+ }
34
+ );
35
+
36
+ export interface ButtonProps
37
+ extends React.ButtonHTMLAttributes<HTMLButtonElement>,
38
+ VariantProps<typeof buttonVariants> {
39
+ asChild?: boolean;
40
+ }
41
+
42
+ const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
43
+ ({ className, variant, size, asChild = false, ...props }, ref) => {
44
+ const Comp = asChild ? Slot : "button";
45
+ return (
46
+ <Comp
47
+ className={cn(buttonVariants({ variant, size, className }))}
48
+ ref={ref}
49
+ {...props}
50
+ />
51
+ );
52
+ }
53
+ );
54
+ Button.displayName = "Button";
55
+
56
+ export { Button, buttonVariants };
57
+
@@ -0,0 +1,73 @@
1
+ import * as React from "react";
2
+
3
+ import { cn } from "@/components/lib/utils";
4
+
5
+ const Card = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
6
+ ({ className, ...props }, ref) => (
7
+ <div
8
+ ref={ref}
9
+ className={cn(
10
+ "rounded-xl border bg-card text-card-foreground shadow",
11
+ className
12
+ )}
13
+ {...props}
14
+ />
15
+ )
16
+ );
17
+ Card.displayName = "Card";
18
+
19
+ const CardHeader = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
20
+ ({ className, ...props }, ref) => (
21
+ <div
22
+ ref={ref}
23
+ className={cn("flex flex-col space-y-1.5 p-6", className)}
24
+ {...props}
25
+ />
26
+ )
27
+ );
28
+ CardHeader.displayName = "CardHeader";
29
+
30
+ const CardTitle = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
31
+ ({ className, ...props }, ref) => (
32
+ <div
33
+ ref={ref}
34
+ className={cn("font-semibold leading-none tracking-tight", className)}
35
+ {...props}
36
+ />
37
+ )
38
+ );
39
+ CardTitle.displayName = "CardTitle";
40
+
41
+ const CardDescription = React.forwardRef<
42
+ HTMLDivElement,
43
+ React.HTMLAttributes<HTMLDivElement>
44
+ >(({ className, ...props }, ref) => (
45
+ <div
46
+ ref={ref}
47
+ className={cn("text-sm text-muted-foreground", className)}
48
+ {...props}
49
+ />
50
+ ));
51
+ CardDescription.displayName = "CardDescription";
52
+
53
+ const CardContent = React.forwardRef<
54
+ HTMLDivElement,
55
+ React.HTMLAttributes<HTMLDivElement>
56
+ >(({ className, ...props }, ref) => (
57
+ <div ref={ref} className={cn("p-6 pt-0", className)} {...props} />
58
+ ));
59
+ CardContent.displayName = "CardContent";
60
+
61
+ const CardFooter = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
62
+ ({ className, ...props }, ref) => (
63
+ <div
64
+ ref={ref}
65
+ className={cn("flex items-center p-6 pt-0", className)}
66
+ {...props}
67
+ />
68
+ )
69
+ );
70
+ CardFooter.displayName = "CardFooter";
71
+
72
+ export { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle };
73
+
@@ -0,0 +1,29 @@
1
+ import * as CheckboxPrimitive from "@radix-ui/react-checkbox";
2
+ import { Check } from "lucide-react";
3
+ import * as React from "react";
4
+
5
+ import { cn } from "@/components/lib/utils";
6
+
7
+ const Checkbox = React.forwardRef<
8
+ React.ElementRef<typeof CheckboxPrimitive.Root>,
9
+ React.ComponentPropsWithoutRef<typeof CheckboxPrimitive.Root>
10
+ >(({ className, ...props }, ref) => (
11
+ <CheckboxPrimitive.Root
12
+ ref={ref}
13
+ className={cn(
14
+ "grid place-content-center peer h-4 w-4 shrink-0 rounded-sm border border-primary shadow focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground",
15
+ className
16
+ )}
17
+ {...props}
18
+ >
19
+ <CheckboxPrimitive.Indicator
20
+ className={cn("grid place-content-center text-current")}
21
+ >
22
+ <Check className="h-4 w-4" />
23
+ </CheckboxPrimitive.Indicator>
24
+ </CheckboxPrimitive.Root>
25
+ ));
26
+ Checkbox.displayName = CheckboxPrimitive.Root.displayName;
27
+
28
+ export { Checkbox };
29
+
@@ -0,0 +1,182 @@
1
+ import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu";
2
+ import { Check, ChevronRight, Circle } from "lucide-react";
3
+ import * as React from "react";
4
+
5
+ import { cn } from "@/components/lib/utils";
6
+
7
+ const DropdownMenu = DropdownMenuPrimitive.Root;
8
+
9
+ const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger;
10
+
11
+ const DropdownMenuGroup = DropdownMenuPrimitive.Group;
12
+
13
+ const DropdownMenuPortal = DropdownMenuPrimitive.Portal;
14
+
15
+ const DropdownMenuSub = DropdownMenuPrimitive.Sub;
16
+
17
+ const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup;
18
+
19
+ const DropdownMenuSubTrigger = React.forwardRef<
20
+ React.ElementRef<typeof DropdownMenuPrimitive.SubTrigger>,
21
+ React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubTrigger> & {
22
+ inset?: boolean;
23
+ }
24
+ >(({ className, inset, children, ...props }, ref) => (
25
+ <DropdownMenuPrimitive.SubTrigger
26
+ ref={ref}
27
+ className={cn(
28
+ "flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent data-[state=open]:bg-accent [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
29
+ inset && "pl-8",
30
+ className
31
+ )}
32
+ {...props}
33
+ >
34
+ {children}
35
+ <ChevronRight className="ml-auto" />
36
+ </DropdownMenuPrimitive.SubTrigger>
37
+ ));
38
+ DropdownMenuSubTrigger.displayName = DropdownMenuPrimitive.SubTrigger.displayName;
39
+
40
+ const DropdownMenuSubContent = React.forwardRef<
41
+ React.ElementRef<typeof DropdownMenuPrimitive.SubContent>,
42
+ React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubContent>
43
+ >(({ className, ...props }, ref) => (
44
+ <DropdownMenuPrimitive.SubContent
45
+ ref={ref}
46
+ className={cn(
47
+ "z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-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 origin-[--radix-dropdown-menu-content-transform-origin]",
48
+ className
49
+ )}
50
+ {...props}
51
+ />
52
+ ));
53
+ DropdownMenuSubContent.displayName = DropdownMenuPrimitive.SubContent.displayName;
54
+
55
+ const DropdownMenuContent = React.forwardRef<
56
+ React.ElementRef<typeof DropdownMenuPrimitive.Content>,
57
+ React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Content>
58
+ >(({ className, sideOffset = 4, ...props }, ref) => (
59
+ <DropdownMenuPrimitive.Portal>
60
+ <DropdownMenuPrimitive.Content
61
+ ref={ref}
62
+ sideOffset={sideOffset}
63
+ className={cn(
64
+ "z-50 max-h-[var(--radix-dropdown-menu-content-available-height)] min-w-[8rem] overflow-y-auto overflow-x-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md",
65
+ "data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-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 origin-[--radix-dropdown-menu-content-transform-origin]",
66
+ className
67
+ )}
68
+ {...props}
69
+ />
70
+ </DropdownMenuPrimitive.Portal>
71
+ ));
72
+ DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName;
73
+
74
+ const DropdownMenuItem = React.forwardRef<
75
+ React.ElementRef<typeof DropdownMenuPrimitive.Item>,
76
+ React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Item> & {
77
+ inset?: boolean;
78
+ }
79
+ >(({ className, inset, ...props }, ref) => (
80
+ <DropdownMenuPrimitive.Item
81
+ ref={ref}
82
+ className={cn(
83
+ "relative flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&>svg]:size-4 [&>svg]:shrink-0",
84
+ inset && "pl-8",
85
+ className
86
+ )}
87
+ {...props}
88
+ />
89
+ ));
90
+ DropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName;
91
+
92
+ const DropdownMenuCheckboxItem = React.forwardRef<
93
+ React.ElementRef<typeof DropdownMenuPrimitive.CheckboxItem>,
94
+ React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.CheckboxItem>
95
+ >(({ className, children, checked, ...props }, ref) => (
96
+ <DropdownMenuPrimitive.CheckboxItem
97
+ ref={ref}
98
+ className={cn(
99
+ "relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
100
+ className
101
+ )}
102
+ checked={checked}
103
+ {...props}
104
+ >
105
+ <span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
106
+ <DropdownMenuPrimitive.ItemIndicator>
107
+ <Check className="h-4 w-4" />
108
+ </DropdownMenuPrimitive.ItemIndicator>
109
+ </span>
110
+ {children}
111
+ </DropdownMenuPrimitive.CheckboxItem>
112
+ ));
113
+ DropdownMenuCheckboxItem.displayName = DropdownMenuPrimitive.CheckboxItem.displayName;
114
+
115
+ const DropdownMenuRadioItem = React.forwardRef<
116
+ React.ElementRef<typeof DropdownMenuPrimitive.RadioItem>,
117
+ React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.RadioItem>
118
+ >(({ className, children, ...props }, ref) => (
119
+ <DropdownMenuPrimitive.RadioItem
120
+ ref={ref}
121
+ className={cn(
122
+ "relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
123
+ className
124
+ )}
125
+ {...props}
126
+ >
127
+ <span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
128
+ <DropdownMenuPrimitive.ItemIndicator>
129
+ <Circle className="h-2 w-2 fill-current" />
130
+ </DropdownMenuPrimitive.ItemIndicator>
131
+ </span>
132
+ {children}
133
+ </DropdownMenuPrimitive.RadioItem>
134
+ ));
135
+ DropdownMenuRadioItem.displayName = DropdownMenuPrimitive.RadioItem.displayName;
136
+
137
+ const DropdownMenuLabel = React.forwardRef<
138
+ React.ElementRef<typeof DropdownMenuPrimitive.Label>,
139
+ React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Label> & {
140
+ inset?: boolean;
141
+ }
142
+ >(({ className, inset, ...props }, ref) => (
143
+ <DropdownMenuPrimitive.Label
144
+ ref={ref}
145
+ className={cn("px-2 py-1.5 text-sm font-semibold", inset && "pl-8", className)}
146
+ {...props}
147
+ />
148
+ ));
149
+ DropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName;
150
+
151
+ const DropdownMenuSeparator = React.forwardRef<
152
+ React.ElementRef<typeof DropdownMenuPrimitive.Separator>,
153
+ React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Separator>
154
+ >(({ className, ...props }, ref) => (
155
+ <DropdownMenuPrimitive.Separator
156
+ ref={ref}
157
+ className={cn("-mx-1 my-1 h-px bg-muted", className)}
158
+ {...props}
159
+ />
160
+ ));
161
+ DropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName;
162
+
163
+ const DropdownMenuShortcut = ({
164
+ className,
165
+ ...props
166
+ }: React.HTMLAttributes<HTMLSpanElement>) => {
167
+ return (
168
+ <span
169
+ className={cn("ml-auto text-xs tracking-widest opacity-60", className)}
170
+ {...props}
171
+ />
172
+ );
173
+ };
174
+ DropdownMenuShortcut.displayName = "DropdownMenuShortcut";
175
+
176
+ export {
177
+ DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator,
178
+ DropdownMenuShortcut, DropdownMenuSub,
179
+ DropdownMenuSubContent,
180
+ DropdownMenuSubTrigger, DropdownMenuTrigger
181
+ };
182
+