@frank-auth/react
Version:
Flexible and customizable React UI components for Frank Authentication
1 lines • 15.6 kB
Source Map (JSON)
{"version":3,"file":"sign-in-button.cjs","sources":["../../../../../src/components/auth/sign-in/sign-in-button.tsx"],"sourcesContent":["/**\n * @frank-auth/react - Sign In Button Component\n *\n * Trigger button for sign-in that can work in navigation or modal mode.\n */\n\n\"use client\";\n\nimport { Button } from \"@/components/ui\";\nimport {\n\tArrowRightOnRectangleIcon,\n\tLockClosedIcon,\n\tUserIcon,\n} from \"@heroicons/react/24/outline\";\nimport type React from \"react\";\nimport { useCallback } from \"react\";\n\nimport { useAuth } from \"@/hooks\";\nimport { useConfig } from \"@/hooks\";\nimport { SignInModal, useSignInModal } from \"./sign-in-modal\";\n\n// ============================================================================\n// Sign In Button Types\n// ============================================================================\n\nexport interface SignInButtonProps {\n\t/**\n\t * Button text\n\t */\n\tchildren?: React.ReactNode;\n\n\t/**\n\t * Button variant\n\t */\n\tvariant?:\n\t\t| \"bordered\"\n\t\t| \"light\"\n\t\t| \"ghost\"\n\t\t| \"default\"\n\t\t| \"primary\"\n\t\t| \"secondary\"\n\t\t| \"link\"\n\t\t| \"destructive\"\n\t\t| \"outline\"\n\t\t| \"tertiary\"\n\t\t| \"quaternary\"\n\t\t| null\n\t\t| undefined;\n\n\t/**\n\t * Button color\n\t */\n\tcolor?:\n\t\t| \"default\"\n\t\t| \"primary\"\n\t\t| \"secondary\"\n\t\t| \"success\"\n\t\t| \"warning\"\n\t\t| \"danger\";\n\n\t/**\n\t * Button size\n\t */\n\tsize?: \"sm\" | \"md\" | \"lg\";\n\n\t/**\n\t * Full width button\n\t */\n\tfullWidth?: boolean;\n\n\t/**\n\t * Button icon\n\t */\n\tstartContent?: React.ReactNode;\n\tendContent?: React.ReactNode;\n\n\t/**\n\t * Custom className\n\t */\n\tclassName?: string;\n\n\t/**\n\t * Modal mode - opens sign-in in modal instead of navigation\n\t */\n\tmodalMode?: boolean;\n\n\t/**\n\t * Props to pass to the sign-in modal\n\t */\n\tmodalProps?: {\n\t\tmethods?: (\"password\" | \"oauth\" | \"magic-link\" | \"passkey\" | \"sso\")[];\n\t\temail?: string;\n\t\torganizationId?: string;\n\t\tredirectUrl?: string;\n\t\ttitle?: string;\n\t\tsubtitle?: string;\n\t\tsize?: \"sm\" | \"md\" | \"lg\";\n\t\tshowBranding?: boolean;\n\t\tmodalSize?: \"sm\" | \"md\" | \"lg\" | \"xl\" | \"full\";\n\t\tbackdrop?: \"opaque\" | \"blur\" | \"transparent\";\n\t\tplacement?: \"auto\" | \"top\" | \"center\" | \"bottom\";\n\t};\n\n\t/**\n\t * Navigation URL (when not in modal mode)\n\t */\n\thref?: string;\n\n\t/**\n\t * Custom onClick handler\n\t */\n\tonClick?: () => void;\n\n\t/**\n\t * Success callback\n\t */\n\tonSuccess?: (result: any) => void;\n\n\t/**\n\t * Error callback\n\t */\n\tonError?: (error: Error) => void;\n\n\t/**\n\t * Disabled state\n\t */\n\tdisabled?: boolean;\n\n\t/**\n\t * Loading state\n\t */\n\tloading?: boolean;\n\n\t/**\n\t * Show different states based on auth status\n\t */\n\tshowAuthenticatedState?: boolean;\n\n\t/**\n\t * Icon mode - show only icon\n\t */\n\ticonOnly?: boolean;\n\n\t/**\n\t * Redirect after sign-in\n\t */\n\tredirectAfterSignIn?: string;\n\n\t/**\n\t * Auto-redirect based on user type\n\t */\n\tautoRedirect?: boolean;\n}\n\n// ============================================================================\n// Sign In Button Component\n// ============================================================================\n\nexport function SignInButton({\n\tchildren,\n\tvariant = \"primary\",\n\tcolor,\n\tsize = \"md\",\n\tfullWidth = false,\n\tstartContent,\n\tendContent,\n\tclassName = \"\",\n\tmodalMode = false,\n\tmodalProps = {},\n\thref = \"/auth/sign-in\",\n\tonClick,\n\tonSuccess,\n\tonError,\n\tdisabled = false,\n\tloading = false,\n\tshowAuthenticatedState = true,\n\ticonOnly = false,\n\tredirectAfterSignIn,\n\tautoRedirect = true,\n}: SignInButtonProps) {\n\tconst { isSignedIn, user, isLoading } = useAuth();\n\tconst { components } = useConfig();\n\tconst { isOpen, open, close } = useSignInModal();\n\n\t// Custom component override\n\tconst CustomSignInButton = components.SignInButton;\n\tif (CustomSignInButton) {\n\t\treturn (\n\t\t\t<CustomSignInButton\n\t\t\t\t{...{\n\t\t\t\t\tchildren,\n\t\t\t\t\tvariant,\n\t\t\t\t\tcolor,\n\t\t\t\t\tsize,\n\t\t\t\t\tfullWidth,\n\t\t\t\t\tstartContent,\n\t\t\t\t\tendContent,\n\t\t\t\t\tclassName,\n\t\t\t\t\tmodalMode,\n\t\t\t\t\tmodalProps,\n\t\t\t\t\thref,\n\t\t\t\t\tonClick,\n\t\t\t\t\tonSuccess,\n\t\t\t\t\tonError,\n\t\t\t\t\tdisabled,\n\t\t\t\t\tloading,\n\t\t\t\t\tshowAuthenticatedState,\n\t\t\t\t\ticonOnly,\n\t\t\t\t\tredirectAfterSignIn,\n\t\t\t\t\tautoRedirect,\n\t\t\t\t}}\n\t\t\t/>\n\t\t);\n\t}\n\n\t// Handle sign-in success\n\tconst handleSuccess = useCallback(\n\t\t(result: any) => {\n\t\t\tonSuccess?.(result);\n\n\t\t\t// Auto-redirect logic\n\t\t\tif (autoRedirect && redirectAfterSignIn) {\n\t\t\t\tsetTimeout(() => {\n\t\t\t\t\twindow.location.href = redirectAfterSignIn;\n\t\t\t\t}, 1000);\n\t\t\t} else if (autoRedirect && result.user) {\n\t\t\t\t// Default redirects based on user type\n\t\t\t\tconst userType = result.user.userType || \"external\";\n\t\t\t\tconst defaultRedirects = {\n\t\t\t\t\tinternal: \"/admin/dashboard\",\n\t\t\t\t\texternal: \"/dashboard\",\n\t\t\t\t\tend_user: \"/app\",\n\t\t\t\t};\n\n\t\t\t\tconst redirectUrl =\n\t\t\t\t\tdefaultRedirects[userType as keyof typeof defaultRedirects] ||\n\t\t\t\t\t\"/dashboard\";\n\t\t\t\tsetTimeout(() => {\n\t\t\t\t\twindow.location.href = redirectUrl;\n\t\t\t\t}, 1000);\n\t\t\t}\n\t\t},\n\t\t[onSuccess, autoRedirect, redirectAfterSignIn],\n\t);\n\n\t// Handle button click\n\tconst handleClick = useCallback(() => {\n\t\tif (disabled || loading || isLoading) return;\n\n\t\t// Custom onClick handler\n\t\tif (onClick) {\n\t\t\tonClick();\n\t\t\treturn;\n\t\t}\n\n\t\t// Modal mode\n\t\tif (modalMode) {\n\t\t\topen();\n\t\t\treturn;\n\t\t}\n\n\t\t// Navigation mode\n\t\tif (href) {\n\t\t\twindow.location.href = href;\n\t\t}\n\t}, [disabled, loading, isLoading, onClick, modalMode, open, href]);\n\n\t// Show authenticated state\n\tif (showAuthenticatedState && isSignedIn && user) {\n\t\treturn (\n\t\t\t<Button\n\t\t\t\tvariant=\"light\"\n\t\t\t\tsize={size}\n\t\t\t\tclassName={`${className} text-success-600`}\n\t\t\t\tstartContent={<UserIcon className=\"w-4 h-4\" />}\n\t\t\t\tisDisabled\n\t\t\t>\n\t\t\t\t{iconOnly\n\t\t\t\t\t? null\n\t\t\t\t\t: `Welcome, ${user.firstName || user.primaryEmailAddress || \"User\"}`}\n\t\t\t</Button>\n\t\t);\n\t}\n\n\t// Default content\n\tconst defaultStartContent =\n\t\tstartContent ||\n\t\t(iconOnly ? (\n\t\t\t<ArrowRightOnRectangleIcon className=\"w-4 h-4\" />\n\t\t) : (\n\t\t\t<ArrowRightOnRectangleIcon className=\"w-4 h-4\" />\n\t\t));\n\n\tconst buttonContent = children || (iconOnly ? null : \"Sign In\");\n\n\treturn (\n\t\t<>\n\t\t\t<Button\n\t\t\t\tvariant={variant}\n\t\t\t\tcolor={color}\n\t\t\t\tsize={size}\n\t\t\t\tfullWidth={fullWidth}\n\t\t\t\tstartContent={defaultStartContent}\n\t\t\t\tendContent={endContent}\n\t\t\t\tclassName={className}\n\t\t\t\tonPress={handleClick}\n\t\t\t\tisDisabled={disabled || isLoading}\n\t\t\t\tisLoading={loading || isLoading}\n\t\t\t\tisIconOnly={iconOnly}\n\t\t\t>\n\t\t\t\t{buttonContent}\n\t\t\t</Button>\n\n\t\t\t{/* Modal */}\n\t\t\t{modalMode && (\n\t\t\t\t<SignInModal\n\t\t\t\t\tisOpen={isOpen}\n\t\t\t\t\tonClose={close}\n\t\t\t\t\tonSuccess={handleSuccess}\n\t\t\t\t\tonError={onError}\n\t\t\t\t\tredirectUrl={redirectAfterSignIn}\n\t\t\t\t\t{...modalProps}\n\t\t\t\t/>\n\t\t\t)}\n\t\t</>\n\t);\n}\n\n// ============================================================================\n// Sign In Button Variants\n// ============================================================================\n\n/**\n * Primary Sign In Button\n */\nexport function PrimarySignInButton(\n\tprops: Omit<SignInButtonProps, \"variant\" | \"color\">,\n) {\n\treturn <SignInButton {...props} variant=\"solid\" color=\"primary\" />;\n}\n\n/**\n * Secondary Sign In Button\n */\nexport function SecondarySignInButton(\n\tprops: Omit<SignInButtonProps, \"variant\" | \"color\">,\n) {\n\treturn <SignInButton {...props} variant=\"bordered\" color=\"default\" />;\n}\n\n/**\n * Ghost Sign In Button\n */\nexport function GhostSignInButton(props: Omit<SignInButtonProps, \"variant\">) {\n\treturn <SignInButton {...props} variant=\"ghost\" />;\n}\n\n/**\n * Icon-only Sign In Button\n */\nexport function IconSignInButton(props: Omit<SignInButtonProps, \"iconOnly\">) {\n\treturn <SignInButton {...props} iconOnly />;\n}\n\n// ============================================================================\n// Modal Sign In Button\n// ============================================================================\n\n/**\n * Sign In Button that always opens in modal\n */\nexport function ModalSignInButton(props: Omit<SignInButtonProps, \"modalMode\">) {\n\treturn <SignInButton {...props} modalMode />;\n}\n\n// ============================================================================\n// Specialized Sign In Buttons\n// ============================================================================\n\n/**\n * Navigation Sign In Button (for nav bars)\n */\nexport function NavSignInButton({\n\tclassName = \"\",\n\t...props\n}: SignInButtonProps) {\n\treturn (\n\t\t<SignInButton\n\t\t\t{...props}\n\t\t\tvariant=\"ghost\"\n\t\t\tsize=\"sm\"\n\t\t\tclassName={`font-medium ${className}`}\n\t\t\tmodalMode\n\t\t/>\n\t);\n}\n\n/**\n * Header Sign In Button (for headers)\n */\nexport function HeaderSignInButton({\n\tclassName = \"\",\n\t...props\n}: SignInButtonProps) {\n\treturn (\n\t\t<SignInButton\n\t\t\t{...props}\n\t\t\tvariant=\"bordered\"\n\t\t\tsize=\"sm\"\n\t\t\tclassName={`border-default-200 ${className}`}\n\t\t\tmodalMode\n\t\t/>\n\t);\n}\n\n/**\n * Hero Sign In Button (for landing pages)\n */\nexport function HeroSignInButton({\n\tsize = \"lg\",\n\tclassName = \"\",\n\t...props\n}: SignInButtonProps) {\n\treturn (\n\t\t<SignInButton\n\t\t\t{...props}\n\t\t\tvariant=\"solid\"\n\t\t\tcolor=\"primary\"\n\t\t\tsize={size}\n\t\t\tclassName={`font-semibold ${className}`}\n\t\t\tendContent={<ArrowRightOnRectangleIcon className=\"w-5 h-5\" />}\n\t\t/>\n\t);\n}\n\n/**\n * Quick Access Sign In Button (floating action)\n */\nexport function QuickSignInButton({\n\tclassName = \"\",\n\t...props\n}: SignInButtonProps) {\n\treturn (\n\t\t<SignInButton\n\t\t\t{...props}\n\t\t\tvariant=\"shadow\"\n\t\t\tcolor=\"secondary\"\n\t\t\ticonOnly\n\t\t\tclassName={`fixed bottom-4 right-4 z-50 rounded-full w-12 h-12 shadow-lg ${className}`}\n\t\t\tmodalMode\n\t\t\tstartContent={<UserIcon className=\"w-6 h-6\" />}\n\t\t/>\n\t);\n}\n\n// ============================================================================\n// Secure Sign In Button (with extra visual cues)\n// ============================================================================\n\nexport function SecureSignInButton({\n\tchildren,\n\tstartContent,\n\tclassName = \"\",\n\t...props\n}: SignInButtonProps) {\n\treturn (\n\t\t<SignInButton\n\t\t\t{...props}\n\t\t\tstartContent={startContent || <LockClosedIcon className=\"w-4 h-4\" />}\n\t\t\tclassName={`border-success-200 text-success-700 hover:bg-success-50 ${className}`}\n\t\t\tvariant=\"bordered\"\n\t\t>\n\t\t\t{children || \"Secure Sign In\"}\n\t\t</SignInButton>\n\t);\n}\n\n// ============================================================================\n// Organization-specific Sign In Button\n// ============================================================================\n\nexport interface OrganizationSignInButtonProps extends SignInButtonProps {\n\t/**\n\t * Organization ID\n\t */\n\torganizationId: string;\n\n\t/**\n\t * Organization name (for display)\n\t */\n\torganizationName?: string;\n\n\t/**\n\t * Organization logo\n\t */\n\torganizationLogo?: string;\n}\n\nexport function OrganizationSignInButton({\n\torganizationId,\n\torganizationName,\n\torganizationLogo,\n\tchildren,\n\tstartContent,\n\tmodalProps = {},\n\t...props\n}: OrganizationSignInButtonProps) {\n\tconst content =\n\t\tchildren ||\n\t\t(organizationName\n\t\t\t? `Sign in to ${organizationName}`\n\t\t\t: \"Sign in to Organization\");\n\n\tconst logoContent = organizationLogo ? (\n\t\t<img\n\t\t\tsrc={organizationLogo}\n\t\t\talt={organizationName}\n\t\t\tclassName=\"w-4 h-4 rounded\"\n\t\t/>\n\t) : (\n\t\t<UserIcon className=\"w-4 h-4\" />\n\t);\n\n\treturn (\n\t\t<SignInButton\n\t\t\t{...props}\n\t\t\tstartContent={startContent || logoContent}\n\t\t\tmodalProps={{\n\t\t\t\t...modalProps,\n\t\t\t\torganizationId,\n\t\t\t\ttitle: organizationName ? `Welcome to ${organizationName}` : \"Welcome\",\n\t\t\t\tshowBranding: true,\n\t\t\t}}\n\t\t>\n\t\t\t{content}\n\t\t</SignInButton>\n\t);\n}\n\n// ============================================================================\n// Export\n// ============================================================================\n\nexport default SignInButton;\n"],"names":["SignInButton","children","variant","color","size","fullWidth","startContent","endContent","className","modalMode","modalProps","href","onClick","onSuccess","onError","disabled","loading","showAuthenticatedState","iconOnly","redirectAfterSignIn","autoRedirect","isSignedIn","user","isLoading","useAuth","components","useConfig","isOpen","open","close","useSignInModal","CustomSignInButton","jsx","handleSuccess","useCallback","result","userType","redirectUrl","handleClick","Button","UserIcon","defaultStartContent","ArrowRightOnRectangleIcon","buttonContent","jsxs","Fragment","SignInModal"],"mappings":"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"}