@oussemasahbeni/keycloakify-login-shadcn 250004.0.2 → 250004.0.3

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.
@@ -38,13 +38,13 @@ const Alert = React.forwardRef<
38
38
  <div className="flex items-start gap-3">
39
39
  {showIcon && (
40
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" />}
41
+ {variant === "info" && <Info className="h-5 w-5 shrink-0" />}
42
+ {variant === "error" && <XCircle className="h-5 w-5 shrink-0" />}
43
43
  {variant === "warning" && (
44
- <AlertTriangle className="h-5 w-5 flex-shrink-0" />
44
+ <AlertTriangle className="h-5 w-5 shrink-0" />
45
45
  )}
46
46
  {variant === "success" && (
47
- <MdCheckCircle className="h-5 w-5 flex-shrink-0" />
47
+ <MdCheckCircle className="h-5 w-5 shrink-0" />
48
48
  )}
49
49
  </>
50
50
  )}
@@ -44,7 +44,7 @@ const DropdownMenuSubContent = React.forwardRef<
44
44
  <DropdownMenuPrimitive.SubContent
45
45
  ref={ref}
46
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]",
47
+ "z-50 min-w-32 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
48
  className
49
49
  )}
50
50
  {...props}
@@ -61,7 +61,7 @@ const DropdownMenuContent = React.forwardRef<
61
61
  ref={ref}
62
62
  sideOffset={sideOffset}
63
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",
64
+ "z-50 max-h-(--radix-dropdown-menu-content-available-height) min-w-32 overflow-y-auto overflow-x-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md",
65
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
66
  className
67
67
  )}
@@ -80,7 +80,7 @@ const DropdownMenuItem = React.forwardRef<
80
80
  <DropdownMenuPrimitive.Item
81
81
  ref={ref}
82
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",
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
84
  inset && "pl-8",
85
85
  className
86
86
  )}
@@ -96,7 +96,7 @@ const DropdownMenuCheckboxItem = React.forwardRef<
96
96
  <DropdownMenuPrimitive.CheckboxItem
97
97
  ref={ref}
98
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",
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
100
  className
101
101
  )}
102
102
  checked={checked}
@@ -119,7 +119,7 @@ const DropdownMenuRadioItem = React.forwardRef<
119
119
  <DropdownMenuPrimitive.RadioItem
120
120
  ref={ref}
121
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",
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
123
  className
124
124
  )}
125
125
  {...props}
@@ -9,7 +9,7 @@ const Input = React.forwardRef<HTMLInputElement, React.ComponentProps<"input">>(
9
9
  type={type}
10
10
  className={cn(
11
11
  "flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-base shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
12
- "group-[[data-invalid=true]]/field:border-destructive group-[[data-invalid=true]]/field:focus-visible:ring-destructive",
12
+ "group-data-[invalid=true]/field:border-destructive group-data-[invalid=true]/field:focus-visible:ring-destructive",
13
13
  "aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
14
14
  className
15
15
  )}
@@ -17,7 +17,7 @@ const SelectTrigger = React.forwardRef<
17
17
  <SelectPrimitive.Trigger
18
18
  ref={ref}
19
19
  className={cn(
20
- "flex h-9 w-full items-center justify-between whitespace-nowrap rounded-md border border-input bg-transparent px-3 py-2 text-sm shadow-sm ring-offset-background data-[placeholder]:text-muted-foreground focus:outline-none focus:ring-1 focus:ring-ring disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1",
20
+ "flex h-9 w-full items-center justify-between whitespace-nowrap rounded-md border border-input bg-transparent px-3 py-2 text-sm shadow-sm ring-offset-background data-placeholder:text-muted-foreground focus:outline-none focus:ring-1 focus:ring-ring disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1",
21
21
  className
22
22
  )}
23
23
  {...props}
@@ -66,7 +66,7 @@ const SelectContent = React.forwardRef<
66
66
  <SelectPrimitive.Content
67
67
  ref={ref}
68
68
  className={cn(
69
- "relative z-50 max-h-[--radix-select-content-available-height] min-w-[8rem] overflow-y-auto overflow-x-hidden rounded-md border bg-popover text-popover-foreground shadow-md 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-select-content-transform-origin]",
69
+ "relative z-50 max-h-[--radix-select-content-available-height] min-w-32 overflow-y-auto overflow-x-hidden rounded-md border bg-popover text-popover-foreground shadow-md 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-select-content-transform-origin]",
70
70
  position === "popper" &&
71
71
  "data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
72
72
  className
@@ -79,7 +79,7 @@ const SelectContent = React.forwardRef<
79
79
  className={cn(
80
80
  "p-1",
81
81
  position === "popper" &&
82
- "h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]"
82
+ "h-(--radix-select-trigger-height) w-full min-w-(--radix-select-trigger-width)"
83
83
  )}
84
84
  >
85
85
  {children}
@@ -109,7 +109,7 @@ const SelectItem = React.forwardRef<
109
109
  <SelectPrimitive.Item
110
110
  ref={ref}
111
111
  className={cn(
112
- "relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-2 pr-8 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
112
+ "relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-2 pr-8 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-disabled:pointer-events-none data-disabled:opacity-50",
113
113
  className
114
114
  )}
115
115
  {...props}
@@ -15,7 +15,7 @@ const Separator = React.forwardRef<
15
15
  orientation={orientation}
16
16
  className={cn(
17
17
  "shrink-0 bg-border",
18
- orientation === "horizontal" ? "h-[1px] w-full" : "h-full w-[1px]",
18
+ orientation === "horizontal" ? "h-px w-full" : "h-full w-px",
19
19
  className
20
20
  )}
21
21
  {...props}
@@ -9,6 +9,10 @@ export type KcContextExtension = {
9
9
  properties: Record<KcEnvName, string> & {};
10
10
  // NOTE: Here you can declare more properties to extend the KcContext
11
11
  // See: https://docs.keycloakify.dev/faq-and-help/some-values-you-need-are-missing-from-in-kccontext
12
+ client: {
13
+ baseUrl?: string;
14
+ };
15
+ darkMode?: boolean;
12
16
  };
13
17
 
14
18
  // eslint-disable-next-line @typescript-eslint/no-empty-object-type
@@ -1,4 +1,3 @@
1
- import { ThemeProvider } from '@/components/theme-provider';
2
1
  import { useExclusiveAppInstanceEffect } from "@keycloakify/login-ui/tools/useExclusiveAppInstanceEffect";
3
2
  import { KcClsxProvider } from "@keycloakify/login-ui/useKcClsx";
4
3
  import type { ReactNode } from "react";
@@ -10,28 +9,16 @@ import { PageIndex } from "./pages/PageIndex";
10
9
  import { useStyleLevelCustomization } from "./styleLevelCustomization";
11
10
 
12
11
 
12
+
13
+
13
14
  export default function KcPage(props: { kcContext: KcContext }) {
14
15
  const { kcContext } = props;
15
16
 
16
- const defaultTheme = (): "light" | "dark" | "system" => {
17
- if (kcContext.properties.ENABLE_THEME_TOGGLE !== "true") {
18
- // If theme toggle is disabled, use the default os theme setting
19
- return window.matchMedia &&
20
- window.matchMedia("(prefers-color-scheme: dark)").matches
21
- ? "dark"
22
- : "light";
23
- }
24
- return "system";
25
- };
26
-
27
17
  return (
28
18
  <KcContextProvider kcContext={kcContext}>
29
19
  <I18nProvider kcContext={kcContext}>
30
20
  <StyleLevelCustomization>
31
- <ThemeProvider defaultTheme={defaultTheme()} storageKey="theme">
32
- <PageIndex />
33
- </ThemeProvider>
34
-
21
+ <PageIndex />
35
22
  </StyleLevelCustomization>
36
23
  </I18nProvider>
37
24
  </KcContextProvider>
@@ -3,6 +3,7 @@ import { ModeToggle } from '@/components/theme-toggle';
3
3
  import { Alert, AlertDescription } from '@/components/ui/alert';
4
4
  import { Button } from '@/components/ui/button';
5
5
  import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
6
+ import { redirectUrlOrigin } from '@/login/shared/redirectUrlOrigin';
6
7
  import { kcSanitize } from "@keycloakify/login-ui/kcSanitize";
7
8
  import { useKcClsx } from "@keycloakify/login-ui/useKcClsx";
8
9
  import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@radix-ui/react-tooltip';
@@ -10,6 +11,7 @@ import { useSetClassName } from "keycloakify/tools/useSetClassName";
10
11
  import { RotateCcw } from 'lucide-react';
11
12
  import type { ReactNode } from "react";
12
13
  import { useEffect } from "react";
14
+ import { FiHome } from 'react-icons/fi';
13
15
  import { useI18n } from "../../i18n";
14
16
  import { useKcContext } from "../../KcContext";
15
17
  import companylogo from "./../../assets/img/auth-logo.svg";
@@ -77,20 +79,19 @@ export function Template(props: {
77
79
  <div className="flex flex-col gap-4 px-0 py-0 pb-6 lg:p-6 lg:md:p-10 lg:pt-10 min-h-screen lg:min-h-0">
78
80
  {/* navigation */}
79
81
  <div className="absolute top-4 right-4 lg:left-4 z-20 flex gap-2">
80
- {/* <Button variant="outline" size="sm" className="border-gray-400 self-center font-medium text-base">
81
- <a className="flex items-center gap-1" href={kcContext.client.baseUrl ?? redirectUrlOrigin}>
82
- <FiArrowLeft /> {msg("home")}
83
- </a>
84
- </Button> */}
82
+ <Button variant="outline" size="icon" >
83
+ <a href={kcContext.client.baseUrl ?? redirectUrlOrigin}>
84
+ <FiHome />
85
+ </a>
86
+ </Button>
87
+
85
88
  {enabledLanguages.length > 1 && (
86
- <div className="bg-white/10 dark:bg-white/10 backdrop-blur-sm rounded-md">
87
- <Languages />
88
- </div>
89
+ <Languages />
89
90
  )}
90
- {kcContext.properties.ENABLE_THEME_TOGGLE == "true" && (
91
- <div className="bg-white/10 dark:bg-white/10 backdrop-blur-sm rounded-md">
92
- <ModeToggle />
93
- </div>
91
+
92
+
93
+ {kcContext.darkMode !== false && (
94
+ <ModeToggle />
94
95
  )}
95
96
  </div>
96
97
 
@@ -14,7 +14,6 @@ const { I18nProvider, useI18n } = i18nBuilder
14
14
  enterCredentials: "Enter your credentials below to login",
15
15
  noAccount: "Don't have an account?",
16
16
  doRegister: "Sign up",
17
- home: "Home",
18
17
  "organization.selectTitle": "Choose Your Organization",
19
18
  "organization.pickPlaceholder": "Pick an organization to continue"
20
19
  },
@@ -26,8 +25,6 @@ const { I18nProvider, useI18n } = i18nBuilder
26
25
  enterCredentials: "أدخل بيانات الاعتماد الخاصة بك أدناه لتسجيل الدخول",
27
26
  doRegister: "إنشاء حساب",
28
27
  noAccount: "ليس لديك حساب؟",
29
- home: "الصفحة الرئيسية",
30
-
31
28
  "organization.selectTitle": "اختر مؤسستك",
32
29
  "organization.pickPlaceholder": "اختر مؤسسة للمتابعة"
33
30
  },
@@ -41,7 +38,6 @@ const { I18nProvider, useI18n } = i18nBuilder
41
38
  "Entrez vos informations d'identification ci-dessous pour vous connecter",
42
39
  doRegister: "S'inscrire",
43
40
  noAccount: "Vous n'avez pas de compte?",
44
- home: "Accueil",
45
41
  "organization.selectTitle": "Choisissez Votre Organisation",
46
42
  "organization.pickPlaceholder": "Sélectionnez une organisation pour continuer"
47
43
  }
@@ -1,9 +1,13 @@
1
1
  import { createGetKcContextMock } from "@keycloakify/login-ui/KcContext/getKcContextMock";
2
+ import { kcEnvDefaults, themeNames } from "../../kc.gen";
2
3
  import type { KcContextExtension, KcContextExtensionPerPage } from "../KcContext";
3
- import { themeNames, kcEnvDefaults } from "../../kc.gen";
4
4
 
5
5
  const kcContextExtension: KcContextExtension = {
6
6
  themeName: themeNames[0],
7
+ client: {
8
+ baseUrl: "https://my-theme.keycloakify.dev"
9
+ },
10
+ darkMode: true,
7
11
  properties: {
8
12
  ...kcEnvDefaults
9
13
  }
@@ -15,30 +15,26 @@ export const Default: Story = {
15
15
  render: () => <KcPageStory />
16
16
  };
17
17
 
18
- export const WithDarkModeDisabled: Story = {
19
- render: () => (
20
- <KcPageStory
21
- kcContext={{
22
- properties: {
23
- ENABLE_THEME_TOGGLE: "false"
24
- }
25
- }}
26
- />
27
- )
28
- };
29
18
 
30
- export const WithDarkModeEnabled: Story = {
19
+ /**
20
+ * This reflects the state when "Dark Theme" is set to "Disabled" in the realm settings
21
+ * (Theme configuration tab of the Keycloak Admin UI).
22
+ *
23
+ * You should enable this configuration if you want to hide the "dark mode switch"
24
+ * and ensure that the theme always renders in light mode, even if the user's system
25
+ * preference is set to dark.
26
+ */
27
+ export const WithDarkModeForbidden: Story = {
31
28
  render: () => (
32
29
  <KcPageStory
33
30
  kcContext={{
34
- properties: {
35
- ENABLE_THEME_TOGGLE: "true"
36
- }
31
+ darkMode: false
37
32
  }}
38
33
  />
39
34
  )
40
35
  };
41
36
 
37
+
42
38
  export const WithErrorCode: Story = {
43
39
  render: () => (
44
40
  <KcPageStory
@@ -0,0 +1,45 @@
1
+ const SESSION_STORAGE_KEY = "kc-color-scheme";
2
+
3
+ export function getTheme(
4
+ kcContextDarkMode: boolean | undefined
5
+ ): "dark" | "light" | "system" {
6
+ from_admin_policy: {
7
+ if (kcContextDarkMode === undefined || kcContextDarkMode === true) {
8
+ break from_admin_policy;
9
+ }
10
+ return "light";
11
+ }
12
+
13
+ from_url: {
14
+ const url = new URL(window.location.href);
15
+
16
+ const value = url.searchParams.get("dark");
17
+
18
+ if (value === null) {
19
+ break from_url;
20
+ }
21
+
22
+ {
23
+ url.searchParams.delete("dark");
24
+ window.history.replaceState({}, "", url.toString());
25
+ }
26
+
27
+ const isDark = value === "true";
28
+
29
+ sessionStorage.setItem(SESSION_STORAGE_KEY, `${isDark}`);
30
+
31
+ return isDark ? "dark" : "light";
32
+ }
33
+
34
+ from_session_storage: {
35
+ const value = sessionStorage.getItem(SESSION_STORAGE_KEY);
36
+
37
+ if (value === null) {
38
+ break from_session_storage;
39
+ }
40
+
41
+ return value === "true" ? "dark" : "light";
42
+ }
43
+
44
+ return "system";
45
+ }
@@ -1,5 +1,8 @@
1
+ import { ThemeProvider } from '@/components/theme-provider';
1
2
  import type { ClassKey } from "@keycloakify/login-ui/useKcClsx";
2
3
  import type { ReactNode } from "react";
4
+ import { useKcContext } from './KcContext';
5
+ import { getTheme } from './shared/getColorScheme';
3
6
 
4
7
  type Classes = { [key in ClassKey]?: string };
5
8
 
@@ -10,8 +13,24 @@ type StyleLevelCustomization = {
10
13
  Provider?: (props: { children: ReactNode }) => ReactNode;
11
14
  };
12
15
 
16
+ // eslint-disable-next-line react-refresh/only-export-components
17
+ function Provider(props: { children: ReactNode }) {
18
+ const { children } = props;
19
+
20
+ const { kcContext } = useKcContext();
21
+
22
+
23
+ return (
24
+ <ThemeProvider defaultTheme={getTheme(kcContext.darkMode)}>
25
+ {children}
26
+ </ThemeProvider>
27
+ );
28
+ }
29
+
30
+
13
31
  export function useStyleLevelCustomization(): StyleLevelCustomization {
14
32
  return {
15
- doUseDefaultCss: false
33
+ doUseDefaultCss: false,
34
+ Provider
16
35
  };
17
- }
36
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oussemasahbeni/keycloakify-login-shadcn",
3
- "version": "250004.0.2",
3
+ "version": "250004.0.3",
4
4
  "description": "Keycloakify Shadcn Theme extensions",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -8,9 +8,6 @@
8
8
  "files": [
9
9
  "keycloak-theme"
10
10
  ],
11
- "dependencies": {
12
- "@keycloakify/login-ui": "~250004.6.5"
13
- },
14
11
  "peerDependencies": {
15
12
  "@radix-ui/react-checkbox": "^1.3.3",
16
13
  "@radix-ui/react-dialog": "^1.1.15",
@@ -22,12 +19,12 @@
22
19
  "@radix-ui/react-separator": "^1.1.8",
23
20
  "@radix-ui/react-slot": "^1.2.4",
24
21
  "@radix-ui/react-tooltip": "^1.2.8",
25
- "@tailwindcss/postcss": "^4.1.18",
26
22
  "@tailwindcss/vite": "^4.1.18",
27
23
  "class-variance-authority": "^0.7.1",
28
24
  "clsx": "^2.1.1",
29
25
  "cmdk": "^1.1.1",
30
26
  "input-otp": "^1.4.2",
27
+ "@keycloakify/login-ui": "~250004.6.5",
31
28
  "lucide-react": "^0.473.0",
32
29
  "react-icons": "^5.5.0",
33
30
  "tailwind-merge": "^2.6.0",