@maheshbvv/react-auth-flow 0.1.0

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/README.md ADDED
@@ -0,0 +1,180 @@
1
+ # react-auth-flow
2
+
3
+ Drop-in authentication UI for React with **sign in**, **sign up**, and **forgot password** in one component.
4
+
5
+ It ports the feature set of the Flutter `flutter_auth_flow` package into a React + TypeScript npm package.
6
+
7
+ ## Features
8
+
9
+ - Enable only the auth modes you need
10
+ - Async callbacks for sign in, sign up, and forgot password
11
+ - Success callbacks for each mode
12
+ - Internal loading and error state, with external override support
13
+ - Built-in validation
14
+ - Animated mode transitions
15
+ - Theme tokens for colors, typography, radii, and motion
16
+ - Render-prop customization for header, footer, error, loading, submit button, and mode switcher
17
+ - Demo auth service and React provider for local demos/tests
18
+
19
+ ## Installation
20
+
21
+ ```bash
22
+ npm install react-auth-flow
23
+ ```
24
+
25
+ Import the shipped styles once in your app:
26
+
27
+ ```ts
28
+ import 'react-auth-flow/styles.css';
29
+ ```
30
+
31
+ ## Quick start
32
+
33
+ ```tsx
34
+ import { AuthFlow } from 'react-auth-flow';
35
+ import 'react-auth-flow/styles.css';
36
+
37
+ export function LoginScreen() {
38
+ return (
39
+ <AuthFlow
40
+ onSignIn={async (email, password) => {
41
+ await api.signIn({ email, password });
42
+ }}
43
+ onSignUp={async (email, password, name) => {
44
+ await api.signUp({ email, password, name });
45
+ }}
46
+ onForgotPassword={async (email) => {
47
+ await api.sendReset(email);
48
+ }}
49
+ onSignInSuccess={() => {
50
+ window.location.assign('/app');
51
+ }}
52
+ />
53
+ );
54
+ }
55
+ ```
56
+
57
+ ## Presets
58
+
59
+ ```tsx
60
+ import { AuthFlow, AuthFlowType } from 'react-auth-flow';
61
+
62
+ <AuthFlow authFlowType={AuthFlowType.signInOnly()} onSignIn={handleSignIn} />;
63
+
64
+ <AuthFlow
65
+ authFlowType={AuthFlowType.signInAndSignUp()}
66
+ onSignIn={handleSignIn}
67
+ onSignUp={handleSignUp}
68
+ />;
69
+
70
+ <AuthFlow
71
+ authFlowType={new AuthFlowType({ enabledModes: ['signIn', 'forgotPassword'] })}
72
+ onSignIn={handleSignIn}
73
+ onForgotPassword={handleForgotPassword}
74
+ />;
75
+ ```
76
+
77
+ ## API
78
+
79
+ | Prop | Type | Description |
80
+ | --- | --- | --- |
81
+ | `authFlowType` | `AuthFlowType` | Enabled auth modes. Defaults to all modes. |
82
+ | `onSignIn` | `(email, password) => Promise<void> \| void` | Required when sign-in is enabled. |
83
+ | `onSignUp` | `(email, password, name) => Promise<void> \| void` | Required when sign-up is enabled. |
84
+ | `onForgotPassword` | `(email) => Promise<void> \| void` | Required when forgot-password is enabled. |
85
+ | `onSignInSuccess` | `() => void` | Runs after a successful sign in. |
86
+ | `onSignUpSuccess` | `() => void` | Runs after a successful sign up. |
87
+ | `onForgotPasswordSuccess` | `() => void` | Runs after a successful password reset request. |
88
+ | `isLoading` | `boolean` | Overrides internal loading state. |
89
+ | `errorMessage` | `string \| null` | Overrides internal error messaging. |
90
+ | `initialMode` | `AuthMode` | Starting mode if enabled. |
91
+ | `theme` | `AuthFlowTheme` | Visual customization. |
92
+ | `headerRenderer` | `(props) => ReactNode` | Replaces the default header. |
93
+ | `footerRenderer` | `(props) => ReactNode` | Renders below the form. |
94
+ | `errorRenderer` | `(props) => ReactNode` | Replaces the default error UI. |
95
+ | `loadingRenderer` | `(props) => ReactNode` | Replaces the spinner inside the default submit button. |
96
+ | `submitButtonRenderer` | `(props) => ReactNode` | Replaces the default submit button. |
97
+ | `modeSwitcherRenderer` | `(props) => ReactNode` | Replaces the bottom mode switcher. |
98
+
99
+ ## Theming
100
+
101
+ ```tsx
102
+ <AuthFlow
103
+ onSignIn={handleSignIn}
104
+ onSignUp={handleSignUp}
105
+ onForgotPassword={handleForgotPassword}
106
+ theme={{
107
+ themeMode: 'system',
108
+ primaryColor: '#4f46e5',
109
+ backgroundColor: '#ffffff',
110
+ inputFillColor: '#f8fafc',
111
+ errorColor: '#dc2626',
112
+ titleStyle: { fontSize: '2rem' },
113
+ buttonTextStyle: { letterSpacing: '0.02em' },
114
+ inputBorderRadius: 14,
115
+ buttonBorderRadius: 14,
116
+ transitionDuration: 280,
117
+ transitionTimingFunction: 'ease-in-out',
118
+ }}
119
+ />
120
+ ```
121
+
122
+ ## Custom renderers
123
+
124
+ ```tsx
125
+ <AuthFlow
126
+ onSignIn={handleSignIn}
127
+ onSignUp={handleSignUp}
128
+ onForgotPassword={handleForgotPassword}
129
+ headerRenderer={({ mode }) => <BrandHeader mode={mode} />}
130
+ submitButtonRenderer={({ label, submit, isLoading }) => (
131
+ <MyButton onClick={submit} disabled={isLoading}>
132
+ {label}
133
+ </MyButton>
134
+ )}
135
+ modeSwitcherRenderer={({ links, switchMode }) => (
136
+ <nav>
137
+ {links.map((link) => (
138
+ <button key={link.mode} onClick={() => switchMode(link.mode)}>
139
+ {link.prompt} {link.label}
140
+ </button>
141
+ ))}
142
+ </nav>
143
+ )}
144
+ />
145
+ ```
146
+
147
+ ## Demo auth service
148
+
149
+ ```tsx
150
+ import {
151
+ AuthFlow,
152
+ DemoAuthService,
153
+ DemoAuthServiceProvider,
154
+ useDemoAuthService,
155
+ } from 'react-auth-flow';
156
+
157
+ const service = new DemoAuthService();
158
+
159
+ function DemoScreen() {
160
+ const auth = useDemoAuthService();
161
+
162
+ return (
163
+ <AuthFlow
164
+ onSignIn={(email, password) => auth.signIn(email, password).then(() => {})}
165
+ onSignUp={(email, password, name) =>
166
+ auth.signUp(email, password, name).then(() => {})
167
+ }
168
+ onForgotPassword={(email) => auth.forgotPassword(email)}
169
+ />
170
+ );
171
+ }
172
+
173
+ export function App() {
174
+ return (
175
+ <DemoAuthServiceProvider service={service}>
176
+ <DemoScreen />
177
+ </DemoAuthServiceProvider>
178
+ );
179
+ }
180
+ ```
package/dist/index.cjs ADDED
@@ -0,0 +1,6 @@
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});var e=(e,t)=>()=>(t||e((t={exports:{}}).exports,t),t.exports);let t=require(`react`);var n=[`signIn`,`signUp`,`forgotPassword`],r=class e{enabledModes;constructor(e){let t=new Set(e?.enabledModes??n);if(t.size===0)throw Error(`AuthFlowType requires at least one enabled mode.`);this.enabledModes=t}static all(){return new e}static signInOnly(){return new e({enabledModes:[`signIn`]})}static signUpOnly(){return new e({enabledModes:[`signUp`]})}static forgotPasswordOnly(){return new e({enabledModes:[`forgotPassword`]})}static signInAndSignUp(){return new e({enabledModes:[`signIn`,`signUp`]})}static signInAndForgotPassword(){return new e({enabledModes:[`signIn`,`forgotPassword`]})}static signUpAndForgotPassword(){return new e({enabledModes:[`signUp`,`forgotPassword`]})}hasMode(e){return this.enabledModes.has(e)}get isSingleMode(){return this.enabledModes.size===1}get singleMode(){return this.isSingleMode?this.defaultMode:null}get defaultMode(){return n.find(e=>this.enabledModes.has(e))??`forgotPassword`}},i=e((e=>{var t=Symbol.for(`react.transitional.element`),n=Symbol.for(`react.fragment`);function r(e,n,r){var i=null;if(r!==void 0&&(i=``+r),n.key!==void 0&&(i=``+n.key),`key`in n)for(var a in r={},n)a!==`key`&&(r[a]=n[a]);else r=n;return n=r.ref,{$$typeof:t,type:e,key:i,ref:n===void 0?null:n,props:r}}e.Fragment=n,e.jsx=r,e.jsxs=r})),a=e((e=>{process.env.NODE_ENV!==`production`&&(function(){function t(e){if(e==null)return null;if(typeof e==`function`)return e.$$typeof===T?null:e.displayName||e.name||null;if(typeof e==`string`)return e;switch(e){case g:return`Fragment`;case te:return`Profiler`;case _:return`StrictMode`;case b:return`Suspense`;case x:return`SuspenseList`;case w:return`Activity`}if(typeof e==`object`)switch(typeof e.tag==`number`&&console.error(`Received an unexpected object in getComponentNameFromType(). This is likely a bug in React. Please file an issue.`),e.$$typeof){case ee:return`Portal`;case y:return e.displayName||`Context`;case v:return(e._context.displayName||`Context`)+`.Consumer`;case ne:var n=e.render;return e=e.displayName,e||=(e=n.displayName||n.name||``,e===``?`ForwardRef`:`ForwardRef(`+e+`)`),e;case S:return n=e.displayName||null,n===null?t(e.type)||`Memo`:n;case C:n=e._payload,e=e._init;try{return t(e(n))}catch{}}return null}function n(e){return``+e}function r(e){try{n(e);var t=!1}catch{t=!0}if(t){t=console;var r=t.error,i=typeof Symbol==`function`&&Symbol.toStringTag&&e[Symbol.toStringTag]||e.constructor.name||`Object`;return r.call(t,`The provided key is an unsupported type %s. This value must be coerced to a string before using it here.`,i),n(e)}}function i(e){if(e===g)return`<>`;if(typeof e==`object`&&e&&e.$$typeof===C)return`<...>`;try{var n=t(e);return n?`<`+n+`>`:`<...>`}catch{return`<...>`}}function a(){var e=E.A;return e===null?null:e.getOwner()}function o(){return Error(`react-stack-top-frame`)}function s(e){if(D.call(e,`key`)){var t=Object.getOwnPropertyDescriptor(e,`key`).get;if(t&&t.isReactWarning)return!1}return e.key!==void 0}function c(e,t){function n(){A||(A=!0,console.error("%s: `key` is not a prop. Trying to access it will result in `undefined` being returned. If you need to access the same value within the child component, you should pass it as a different prop. (https://react.dev/link/special-props)",t))}n.isReactWarning=!0,Object.defineProperty(e,`key`,{get:n,configurable:!0})}function l(){var e=t(this.type);return j[e]||(j[e]=!0,console.error(`Accessing element.ref was removed in React 19. ref is now a regular prop. It will be removed from the JSX Element type in a future release.`)),e=this.props.ref,e===void 0?null:e}function u(e,t,n,r,i,a){var o=n.ref;return e={$$typeof:h,type:e,key:t,props:n,_owner:r},(o===void 0?null:o)===null?Object.defineProperty(e,`ref`,{enumerable:!1,value:null}):Object.defineProperty(e,`ref`,{enumerable:!1,get:l}),e._store={},Object.defineProperty(e._store,`validated`,{configurable:!1,enumerable:!1,writable:!0,value:0}),Object.defineProperty(e,`_debugInfo`,{configurable:!1,enumerable:!1,writable:!0,value:null}),Object.defineProperty(e,`_debugStack`,{configurable:!1,enumerable:!1,writable:!0,value:i}),Object.defineProperty(e,`_debugTask`,{configurable:!1,enumerable:!1,writable:!0,value:a}),Object.freeze&&(Object.freeze(e.props),Object.freeze(e)),e}function d(e,n,i,o,l,d){var p=n.children;if(p!==void 0)if(o)if(O(p)){for(o=0;o<p.length;o++)f(p[o]);Object.freeze&&Object.freeze(p)}else console.error(`React.jsx: Static children should always be an array. You are likely explicitly calling React.jsxs or React.jsxDEV. Use the Babel transform instead.`);else f(p);if(D.call(n,`key`)){p=t(e);var m=Object.keys(n).filter(function(e){return e!==`key`});o=0<m.length?`{key: someKey, `+m.join(`: ..., `)+`: ...}`:`{key: someKey}`,P[p+o]||(m=0<m.length?`{`+m.join(`: ..., `)+`: ...}`:`{}`,console.error(`A props object containing a "key" prop is being spread into JSX:
2
+ let props = %s;
3
+ <%s {...props} />
4
+ React keys must be passed directly to JSX without using spread:
5
+ let props = %s;
6
+ <%s key={someKey} {...props} />`,o,p,m,p),P[p+o]=!0)}if(p=null,i!==void 0&&(r(i),p=``+i),s(n)&&(r(n.key),p=``+n.key),`key`in n)for(var h in i={},n)h!==`key`&&(i[h]=n[h]);else i=n;return p&&c(i,typeof e==`function`?e.displayName||e.name||`Unknown`:e),u(e,p,i,a(),l,d)}function f(e){p(e)?e._store&&(e._store.validated=1):typeof e==`object`&&e&&e.$$typeof===C&&(e._payload.status===`fulfilled`?p(e._payload.value)&&e._payload.value._store&&(e._payload.value._store.validated=1):e._store&&(e._store.validated=1))}function p(e){return typeof e==`object`&&!!e&&e.$$typeof===h}var m=require(`react`),h=Symbol.for(`react.transitional.element`),ee=Symbol.for(`react.portal`),g=Symbol.for(`react.fragment`),_=Symbol.for(`react.strict_mode`),te=Symbol.for(`react.profiler`),v=Symbol.for(`react.consumer`),y=Symbol.for(`react.context`),ne=Symbol.for(`react.forward_ref`),b=Symbol.for(`react.suspense`),x=Symbol.for(`react.suspense_list`),S=Symbol.for(`react.memo`),C=Symbol.for(`react.lazy`),w=Symbol.for(`react.activity`),T=Symbol.for(`react.client.reference`),E=m.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE,D=Object.prototype.hasOwnProperty,O=Array.isArray,k=console.createTask?console.createTask:function(){return null};m={react_stack_bottom_frame:function(e){return e()}};var A,j={},M=m.react_stack_bottom_frame.bind(m,o)(),N=k(i(o)),P={};e.Fragment=g,e.jsx=function(e,t,n){var r=1e4>E.recentlyCreatedOwnerStacks++;return d(e,t,n,!1,r?Error(`react-stack-top-frame`):M,r?k(i(e)):N)},e.jsxs=function(e,t,n){var r=1e4>E.recentlyCreatedOwnerStacks++;return d(e,t,n,!0,r?Error(`react-stack-top-frame`):M,r?k(i(e)):N)}})()})),o=e(((e,t)=>{process.env.NODE_ENV===`production`?t.exports=i():t.exports=a()}))(),s={name:``,email:``,password:``,confirmPassword:``},c={background:`#ffffff`,inputFill:`#f6f8fb`,surfaceBorder:`rgba(15, 23, 42, 0.08)`,inputBorder:`rgba(15, 23, 42, 0.12)`,text:`#0f172a`,mutedText:`#64748b`,primary:`#2563eb`,error:`#dc2626`,shadow:`0 18px 40px rgba(15, 23, 42, 0.08)`},l={background:`#0f172a`,inputFill:`#172033`,surfaceBorder:`rgba(148, 163, 184, 0.16)`,inputBorder:`rgba(148, 163, 184, 0.22)`,text:`#e2e8f0`,mutedText:`#94a3b8`,primary:`#60a5fa`,error:`#f87171`,shadow:`0 18px 40px rgba(2, 6, 23, 0.36)`},u={signIn:{title:`Welcome back`,subtitle:`Sign in to your account`,submitLabel:`Sign In`},signUp:{title:`Create account`,subtitle:`Join us today`,submitLabel:`Create Account`},forgotPassword:{title:`Reset password`,subtitle:`We'll send you a reset link`,submitLabel:`Send Reset Link`}};function d(e,t){return t&&e.hasMode(t)?t:e.defaultMode}function f(e){return e instanceof Error?e.message.replace(/^(Exception|Error):\s*/u,``):String(e).replace(/^(Exception|Error):\s*/u,``)}function p(e){return/^[^@]+@[^@]+\.[^@]+$/u.test(e.trim())}function m(e,t){let n={};return e===`signUp`&&(t.name.trim()||(n.name=`Name is required`)),t.email.trim()?p(t.email)||(n.email=`Enter a valid email`):n.email=`Email is required`,e!==`forgotPassword`&&(t.password?e===`signUp`&&t.password.length<8&&(n.password=`Minimum 8 characters`):n.password=`Password is required`),e===`signUp`&&(t.confirmPassword?t.confirmPassword!==t.password&&(n.confirmPassword=`Passwords do not match`):n.confirmPassword=`Please confirm password`),n}function h(e,t){return e===`signIn`&&t.hasMode(`forgotPassword`)&&{label:`Forgot password?`,mode:`forgotPassword`}}function ee(e,t){return e===`signIn`&&t.hasMode(`signUp`)?[{prompt:`Don't have an account?`,label:`Sign up`,mode:`signUp`}]:e===`signUp`&&t.hasMode(`signIn`)?[{prompt:`Already have an account?`,label:`Sign in`,mode:`signIn`}]:e===`forgotPassword`&&t.hasMode(`signIn`)?[{prompt:`Remember your password?`,label:`Sign in`,mode:`signIn`}]:[]}function g(e){let[n,r]=(0,t.useState)(()=>typeof window>`u`||typeof window.matchMedia!=`function`?!1:window.matchMedia(`(prefers-color-scheme: dark)`).matches);return(0,t.useEffect)(()=>{if(e&&e!==`system`&&typeof window<`u`&&typeof window.matchMedia==`function`||typeof window>`u`||typeof window.matchMedia!=`function`)return;let t=window.matchMedia(`(prefers-color-scheme: dark)`),n=e=>{r(e.matches)};return r(t.matches),t.addEventListener(`change`,n),()=>t.removeEventListener(`change`,n)},[e]),e===`dark`?`dark`:e===`light`?`light`:n?`dark`:`light`}function _(e){return{borderRadius:e?.inputBorderRadius??12,...e?.inputStyle}}function te(){return(0,o.jsx)(`span`,{className:`raf__spinner`,"aria-hidden":`true`})}function v(e){let{name:t,label:n,value:r,type:i=`text`,autoComplete:a,onChange:s,onBlur:c,error:l,theme:u}=e;return(0,o.jsxs)(`label`,{className:`raf__field`,children:[(0,o.jsx)(`span`,{className:`raf__field-label`,children:n}),(0,o.jsx)(`input`,{className:`raf__input`,name:t,type:i,value:r,autoComplete:a,"aria-invalid":!!l,"aria-describedby":l?`${t}-error`:void 0,onChange:e=>s(e.target.value),onBlur:c,style:_(u)}),l?(0,o.jsx)(`span`,{id:`${t}-error`,className:`raf__field-error`,children:l}):null]})}function y(e){let{links:t,switchMode:n,theme:r}=e;if(t.length===0)return null;let[i]=t;return(0,o.jsxs)(`div`,{className:`raf__mode-switcher`,children:[(0,o.jsxs)(`span`,{children:[i.prompt,` `]}),(0,o.jsx)(`button`,{type:`button`,className:`raf__inline-button`,onClick:()=>n(i.mode),style:r?.linkStyle,children:i.label})]})}function ne(e,t){return t?`${e} ${t}`:e}function b(e){let{authFlowType:n=r.all(),onSignIn:i,onSignUp:a,onForgotPassword:p,onSignInSuccess:_,onSignUpSuccess:b,onForgotPasswordSuccess:x,isLoading:S,errorMessage:C,initialMode:w,theme:T,className:E,headerRenderer:D,footerRenderer:O,errorRenderer:k,loadingRenderer:A,submitButtonRenderer:j,modeSwitcherRenderer:M}=e,[N,P]=(0,t.useState)(()=>d(n,w)),[F,I]=(0,t.useState)(s),[re,L]=(0,t.useState)({}),[ie,R]=(0,t.useState)(!1),[ae,z]=(0,t.useState)(!1),[oe,B]=(0,t.useState)(null),V=g(T?.themeMode),H=S??ae,U=C??oe,se=(0,t.useMemo)(()=>m(N,F),[N,F]),W=h(N,n),G=ee(N,n);(0,t.useEffect)(()=>{n.hasMode(N)||(P(d(n,w)),I(s),L({}),R(!1),B(null))},[n,w,N]);let K=(0,t.useCallback)(e=>{n.hasMode(e)&&(P(e),I(s),L({}),R(!1),B(null))},[n]),q=(0,t.useMemo)(()=>({mode:N,switchMode:K,isLoading:H,errorMessage:U,theme:T,authFlowType:n}),[n,U,H,N,K,T]),J=(0,t.useCallback)((e,t)=>{I(n=>({...n,[e]:t}))},[]),Y=(0,t.useCallback)(e=>{if(!(!ie&&!re[e]))return se[e]},[se,ie,re]),X=(0,t.useCallback)(e=>{L(t=>({...t,[e]:!0}))},[]),ce=(0,t.useCallback)(e=>{switch(e){case`signIn`:if(!i)throw Error(`onSignIn is required when signIn mode is enabled.`);return i(F.email.trim(),F.password);case`signUp`:if(!a)throw Error(`onSignUp is required when signUp mode is enabled.`);return a(F.email.trim(),F.password,F.name.trim());case`forgotPassword`:if(!p)throw Error(`onForgotPassword is required when forgotPassword mode is enabled.`);return p(F.email.trim())}},[F.email,F.name,F.password,p,i,a]),le=(0,t.useCallback)(()=>{switch(N){case`signIn`:_?.();break;case`signUp`:b?.();break;case`forgotPassword`:x?.();break}},[N,x,_,b]),Z=(0,t.useCallback)(async()=>{R(!0);let e=m(N,F);if(!(Object.keys(e).length>0||H)){B(null),z(!0);try{await Promise.resolve(ce(N)),le()}catch(e){B(f(e))}finally{z(!1)}}},[H,F,N,ce,le]),ue=(0,t.useCallback)(e=>{e.preventDefault(),Z()},[Z]),Q=V===`dark`?l:c,de={"--raf-background":T?.backgroundColor??Q.background,"--raf-input-fill":T?.inputFillColor??Q.inputFill,"--raf-text":Q.text,"--raf-muted-text":Q.mutedText,"--raf-primary":T?.primaryColor??Q.primary,"--raf-error":T?.errorColor??Q.error,"--raf-surface-border":Q.surfaceBorder,"--raf-input-border":Q.inputBorder,"--raf-shadow":Q.shadow,"--raf-transition-duration":`${T?.transitionDuration??320}ms`,"--raf-transition-timing":T?.transitionTimingFunction??`cubic-bezier(0.4, 0, 0.2, 1)`},$=u[N],fe=A?A(q):(0,o.jsx)(te,{});return(0,o.jsx)(`div`,{className:ne(`raf raf--${V}`,E),style:de,children:(0,o.jsxs)(`div`,{className:`raf__card`,style:T?.cardStyle,children:[D?D(q):(0,o.jsxs)(`header`,{className:`raf__header`,children:[(0,o.jsx)(`h2`,{className:`raf__title`,style:T?.titleStyle,children:$.title}),(0,o.jsx)(`p`,{className:`raf__subtitle`,style:T?.subtitleStyle,children:$.subtitle})]}),U?(0,o.jsx)(`div`,{className:`raf__error-shell`,"aria-live":`polite`,children:k?k({...q,error:U}):(0,o.jsxs)(`div`,{className:`raf__error`,children:[(0,o.jsx)(`strong`,{className:`raf__error-icon`,"aria-hidden":`true`,children:`!`}),(0,o.jsx)(`span`,{children:U})]})}):null,(0,o.jsx)(`div`,{className:`raf__panel`,children:(0,o.jsxs)(`form`,{className:`raf__form`,onSubmit:ue,children:[N===`signUp`?(0,o.jsx)(v,{name:`name`,label:`Full name`,value:F.name,autoComplete:`name`,onChange:e=>J(`name`,e),onBlur:()=>X(`name`),error:Y(`name`),theme:T}):null,(0,o.jsx)(v,{name:`email`,label:`Email`,type:`email`,value:F.email,autoComplete:N===`signUp`?`email`:`username`,onChange:e=>J(`email`,e),onBlur:()=>X(`email`),error:Y(`email`),theme:T}),N===`forgotPassword`?null:(0,o.jsx)(v,{name:`password`,label:`Password`,type:`password`,value:F.password,autoComplete:N===`signUp`?`new-password`:`current-password`,onChange:e=>J(`password`,e),onBlur:()=>X(`password`),error:Y(`password`),theme:T}),N===`signUp`?(0,o.jsx)(v,{name:`confirmPassword`,label:`Confirm password`,type:`password`,value:F.confirmPassword,autoComplete:`new-password`,onChange:e=>J(`confirmPassword`,e),onBlur:()=>X(`confirmPassword`),error:Y(`confirmPassword`),theme:T}):null,W?(0,o.jsx)(`div`,{className:`raf__forgot-shell`,children:(0,o.jsx)(`button`,{type:`button`,className:`raf__inline-button`,onClick:()=>K(W.mode),style:T?.linkStyle,children:W.label})}):null,j?j({...q,label:$.submitLabel,submit:()=>{Z()}}):(0,o.jsx)(`button`,{type:`submit`,className:`raf__submit`,disabled:H,style:{borderRadius:T?.buttonBorderRadius??12,...T?.buttonStyle},children:H?fe:(0,o.jsx)(`span`,{style:T?.buttonTextStyle,children:$.submitLabel})}),G.length>0?M?M({...q,links:G}):(0,o.jsx)(y,{links:G,switchMode:K,theme:T}):null]})},N),O?O(q):null]})})}var x=class extends Error{constructor(e){super(e),this.name=`DemoAuthException`}};function S(e){let t=new TextEncoder().encode(e),n=``;for(let e of t)n+=String.fromCharCode(e);if(typeof globalThis.btoa==`function`)return globalThis.btoa(n);let r=globalThis.Buffer;return r?r.from(t).toString(`base64`):n}function C(){return 1e3+Date.now()%1e3}function w(e){return/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/.test(e)}function T(e){return e.trim().toLowerCase()}function E(e){return new Promise(t=>{globalThis.setTimeout(t,e)})}var D=class{users=new Map;async signIn(e,t){await E(C());let n=T(e),r=this.users.get(n);if(!r||r.passwordHash!==S(t))throw new x(`Invalid email or password`);return{email:r.email,name:r.name}}async signUp(e,t,n){await E(C());let r=T(e);if(!w(r))throw new x(`Please enter a valid email address`);if(t.length<8)throw new x(`Password must be at least 8 characters`);if(this.users.has(r))throw new x(`Email already in use`);let i={email:r,passwordHash:S(t),name:n.trim(),createdAt:new Date};return this.users.set(r,i),{email:i.email,name:i.name}}async forgotPassword(e){if(await E(1e3),!w(T(e)))throw new x(`Please enter a valid email address`)}clearUsers(){this.users.clear()}get userCount(){return this.users.size}getCreatedAt(e){return this.users.get(T(e))?.createdAt??null}},O=(0,t.createContext)(null);function k(e){let{service:t,children:n}=e;return(0,o.jsx)(O.Provider,{value:t,children:n})}function A(){let e=(0,t.useContext)(O);if(!e)throw Error(`useDemoAuthService() must be used inside a DemoAuthServiceProvider.`);return e}function j(){return(0,t.useContext)(O)}exports.AuthFlow=b,exports.AuthFlowType=r,exports.DemoAuthException=x,exports.DemoAuthService=D,exports.DemoAuthServiceProvider=k,exports.useDemoAuthService=A,exports.useMaybeDemoAuthService=j;
package/dist/index.js ADDED
@@ -0,0 +1,694 @@
1
+ import { createContext as e, useCallback as t, useContext as n, useEffect as r, useMemo as i, useState as a } from "react";
2
+ //#region \0rolldown/runtime.js
3
+ var o = (e, t) => () => (t || e((t = { exports: {} }).exports, t), t.exports), s = /* @__PURE__ */ ((e) => typeof require < "u" ? require : typeof Proxy < "u" ? new Proxy(e, { get: (e, t) => (typeof require < "u" ? require : e)[t] }) : e)(function(e) {
4
+ if (typeof require < "u") return require.apply(this, arguments);
5
+ throw Error("Calling `require` for \"" + e + "\" in an environment that doesn't expose the `require` function. See https://rolldown.rs/in-depth/bundling-cjs#require-external-modules for more details.");
6
+ }), c = [
7
+ "signIn",
8
+ "signUp",
9
+ "forgotPassword"
10
+ ], l = class e {
11
+ enabledModes;
12
+ constructor(e) {
13
+ let t = new Set(e?.enabledModes ?? c);
14
+ if (t.size === 0) throw Error("AuthFlowType requires at least one enabled mode.");
15
+ this.enabledModes = t;
16
+ }
17
+ static all() {
18
+ return new e();
19
+ }
20
+ static signInOnly() {
21
+ return new e({ enabledModes: ["signIn"] });
22
+ }
23
+ static signUpOnly() {
24
+ return new e({ enabledModes: ["signUp"] });
25
+ }
26
+ static forgotPasswordOnly() {
27
+ return new e({ enabledModes: ["forgotPassword"] });
28
+ }
29
+ static signInAndSignUp() {
30
+ return new e({ enabledModes: ["signIn", "signUp"] });
31
+ }
32
+ static signInAndForgotPassword() {
33
+ return new e({ enabledModes: ["signIn", "forgotPassword"] });
34
+ }
35
+ static signUpAndForgotPassword() {
36
+ return new e({ enabledModes: ["signUp", "forgotPassword"] });
37
+ }
38
+ hasMode(e) {
39
+ return this.enabledModes.has(e);
40
+ }
41
+ get isSingleMode() {
42
+ return this.enabledModes.size === 1;
43
+ }
44
+ get singleMode() {
45
+ return this.isSingleMode ? this.defaultMode : null;
46
+ }
47
+ get defaultMode() {
48
+ return c.find((e) => this.enabledModes.has(e)) ?? "forgotPassword";
49
+ }
50
+ }, u = /* @__PURE__ */ o(((e) => {
51
+ var t = Symbol.for("react.transitional.element"), n = Symbol.for("react.fragment");
52
+ function r(e, n, r) {
53
+ var i = null;
54
+ if (r !== void 0 && (i = "" + r), n.key !== void 0 && (i = "" + n.key), "key" in n) for (var a in r = {}, n) a !== "key" && (r[a] = n[a]);
55
+ else r = n;
56
+ return n = r.ref, {
57
+ $$typeof: t,
58
+ type: e,
59
+ key: i,
60
+ ref: n === void 0 ? null : n,
61
+ props: r
62
+ };
63
+ }
64
+ e.Fragment = n, e.jsx = r, e.jsxs = r;
65
+ })), d = /* @__PURE__ */ o(((e) => {
66
+ process.env.NODE_ENV !== "production" && (function() {
67
+ function t(e) {
68
+ if (e == null) return null;
69
+ if (typeof e == "function") return e.$$typeof === T ? null : e.displayName || e.name || null;
70
+ if (typeof e == "string") return e;
71
+ switch (e) {
72
+ case v: return "Fragment";
73
+ case b: return "Profiler";
74
+ case y: return "StrictMode";
75
+ case x: return "Suspense";
76
+ case S: return "SuspenseList";
77
+ case re: return "Activity";
78
+ }
79
+ if (typeof e == "object") switch (typeof e.tag == "number" && console.error("Received an unexpected object in getComponentNameFromType(). This is likely a bug in React. Please file an issue."), e.$$typeof) {
80
+ case _: return "Portal";
81
+ case te: return e.displayName || "Context";
82
+ case ee: return (e._context.displayName || "Context") + ".Consumer";
83
+ case ne:
84
+ var n = e.render;
85
+ return e = e.displayName, e ||= (e = n.displayName || n.name || "", e === "" ? "ForwardRef" : "ForwardRef(" + e + ")"), e;
86
+ case C: return n = e.displayName || null, n === null ? t(e.type) || "Memo" : n;
87
+ case w:
88
+ n = e._payload, e = e._init;
89
+ try {
90
+ return t(e(n));
91
+ } catch {}
92
+ }
93
+ return null;
94
+ }
95
+ function n(e) {
96
+ return "" + e;
97
+ }
98
+ function r(e) {
99
+ try {
100
+ n(e);
101
+ var t = !1;
102
+ } catch {
103
+ t = !0;
104
+ }
105
+ if (t) {
106
+ t = console;
107
+ var r = t.error, i = typeof Symbol == "function" && Symbol.toStringTag && e[Symbol.toStringTag] || e.constructor.name || "Object";
108
+ return r.call(t, "The provided key is an unsupported type %s. This value must be coerced to a string before using it here.", i), n(e);
109
+ }
110
+ }
111
+ function i(e) {
112
+ if (e === v) return "<>";
113
+ if (typeof e == "object" && e && e.$$typeof === w) return "<...>";
114
+ try {
115
+ var n = t(e);
116
+ return n ? "<" + n + ">" : "<...>";
117
+ } catch {
118
+ return "<...>";
119
+ }
120
+ }
121
+ function a() {
122
+ var e = E.A;
123
+ return e === null ? null : e.getOwner();
124
+ }
125
+ function o() {
126
+ return Error("react-stack-top-frame");
127
+ }
128
+ function c(e) {
129
+ if (D.call(e, "key")) {
130
+ var t = Object.getOwnPropertyDescriptor(e, "key").get;
131
+ if (t && t.isReactWarning) return !1;
132
+ }
133
+ return e.key !== void 0;
134
+ }
135
+ function l(e, t) {
136
+ function n() {
137
+ A || (A = !0, console.error("%s: `key` is not a prop. Trying to access it will result in `undefined` being returned. If you need to access the same value within the child component, you should pass it as a different prop. (https://react.dev/link/special-props)", t));
138
+ }
139
+ n.isReactWarning = !0, Object.defineProperty(e, "key", {
140
+ get: n,
141
+ configurable: !0
142
+ });
143
+ }
144
+ function u() {
145
+ var e = t(this.type);
146
+ return j[e] || (j[e] = !0, console.error("Accessing element.ref was removed in React 19. ref is now a regular prop. It will be removed from the JSX Element type in a future release.")), e = this.props.ref, e === void 0 ? null : e;
147
+ }
148
+ function d(e, t, n, r, i, a) {
149
+ var o = n.ref;
150
+ return e = {
151
+ $$typeof: g,
152
+ type: e,
153
+ key: t,
154
+ props: n,
155
+ _owner: r
156
+ }, (o === void 0 ? null : o) === null ? Object.defineProperty(e, "ref", {
157
+ enumerable: !1,
158
+ value: null
159
+ }) : Object.defineProperty(e, "ref", {
160
+ enumerable: !1,
161
+ get: u
162
+ }), e._store = {}, Object.defineProperty(e._store, "validated", {
163
+ configurable: !1,
164
+ enumerable: !1,
165
+ writable: !0,
166
+ value: 0
167
+ }), Object.defineProperty(e, "_debugInfo", {
168
+ configurable: !1,
169
+ enumerable: !1,
170
+ writable: !0,
171
+ value: null
172
+ }), Object.defineProperty(e, "_debugStack", {
173
+ configurable: !1,
174
+ enumerable: !1,
175
+ writable: !0,
176
+ value: i
177
+ }), Object.defineProperty(e, "_debugTask", {
178
+ configurable: !1,
179
+ enumerable: !1,
180
+ writable: !0,
181
+ value: a
182
+ }), Object.freeze && (Object.freeze(e.props), Object.freeze(e)), e;
183
+ }
184
+ function f(e, n, i, o, s, u) {
185
+ var f = n.children;
186
+ if (f !== void 0) if (o) if (O(f)) {
187
+ for (o = 0; o < f.length; o++) p(f[o]);
188
+ Object.freeze && Object.freeze(f);
189
+ } else console.error("React.jsx: Static children should always be an array. You are likely explicitly calling React.jsxs or React.jsxDEV. Use the Babel transform instead.");
190
+ else p(f);
191
+ if (D.call(n, "key")) {
192
+ f = t(e);
193
+ var m = Object.keys(n).filter(function(e) {
194
+ return e !== "key";
195
+ });
196
+ o = 0 < m.length ? "{key: someKey, " + m.join(": ..., ") + ": ...}" : "{key: someKey}", P[f + o] || (m = 0 < m.length ? "{" + m.join(": ..., ") + ": ...}" : "{}", console.error("A props object containing a \"key\" prop is being spread into JSX:\n let props = %s;\n <%s {...props} />\nReact keys must be passed directly to JSX without using spread:\n let props = %s;\n <%s key={someKey} {...props} />", o, f, m, f), P[f + o] = !0);
197
+ }
198
+ if (f = null, i !== void 0 && (r(i), f = "" + i), c(n) && (r(n.key), f = "" + n.key), "key" in n) for (var h in i = {}, n) h !== "key" && (i[h] = n[h]);
199
+ else i = n;
200
+ return f && l(i, typeof e == "function" ? e.displayName || e.name || "Unknown" : e), d(e, f, i, a(), s, u);
201
+ }
202
+ function p(e) {
203
+ m(e) ? e._store && (e._store.validated = 1) : typeof e == "object" && e && e.$$typeof === w && (e._payload.status === "fulfilled" ? m(e._payload.value) && e._payload.value._store && (e._payload.value._store.validated = 1) : e._store && (e._store.validated = 1));
204
+ }
205
+ function m(e) {
206
+ return typeof e == "object" && !!e && e.$$typeof === g;
207
+ }
208
+ var h = s("react"), g = Symbol.for("react.transitional.element"), _ = Symbol.for("react.portal"), v = Symbol.for("react.fragment"), y = Symbol.for("react.strict_mode"), b = Symbol.for("react.profiler"), ee = Symbol.for("react.consumer"), te = Symbol.for("react.context"), ne = Symbol.for("react.forward_ref"), x = Symbol.for("react.suspense"), S = Symbol.for("react.suspense_list"), C = Symbol.for("react.memo"), w = Symbol.for("react.lazy"), re = Symbol.for("react.activity"), T = Symbol.for("react.client.reference"), E = h.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE, D = Object.prototype.hasOwnProperty, O = Array.isArray, k = console.createTask ? console.createTask : function() {
209
+ return null;
210
+ };
211
+ h = { react_stack_bottom_frame: function(e) {
212
+ return e();
213
+ } };
214
+ var A, j = {}, M = h.react_stack_bottom_frame.bind(h, o)(), N = k(i(o)), P = {};
215
+ e.Fragment = v, e.jsx = function(e, t, n) {
216
+ var r = 1e4 > E.recentlyCreatedOwnerStacks++;
217
+ return f(e, t, n, !1, r ? Error("react-stack-top-frame") : M, r ? k(i(e)) : N);
218
+ }, e.jsxs = function(e, t, n) {
219
+ var r = 1e4 > E.recentlyCreatedOwnerStacks++;
220
+ return f(e, t, n, !0, r ? Error("react-stack-top-frame") : M, r ? k(i(e)) : N);
221
+ };
222
+ })();
223
+ })), f = (/* @__PURE__ */ o(((e, t) => {
224
+ process.env.NODE_ENV === "production" ? t.exports = u() : t.exports = d();
225
+ })))(), p = {
226
+ name: "",
227
+ email: "",
228
+ password: "",
229
+ confirmPassword: ""
230
+ }, m = {
231
+ background: "#ffffff",
232
+ inputFill: "#f6f8fb",
233
+ surfaceBorder: "rgba(15, 23, 42, 0.08)",
234
+ inputBorder: "rgba(15, 23, 42, 0.12)",
235
+ text: "#0f172a",
236
+ mutedText: "#64748b",
237
+ primary: "#2563eb",
238
+ error: "#dc2626",
239
+ shadow: "0 18px 40px rgba(15, 23, 42, 0.08)"
240
+ }, h = {
241
+ background: "#0f172a",
242
+ inputFill: "#172033",
243
+ surfaceBorder: "rgba(148, 163, 184, 0.16)",
244
+ inputBorder: "rgba(148, 163, 184, 0.22)",
245
+ text: "#e2e8f0",
246
+ mutedText: "#94a3b8",
247
+ primary: "#60a5fa",
248
+ error: "#f87171",
249
+ shadow: "0 18px 40px rgba(2, 6, 23, 0.36)"
250
+ }, g = {
251
+ signIn: {
252
+ title: "Welcome back",
253
+ subtitle: "Sign in to your account",
254
+ submitLabel: "Sign In"
255
+ },
256
+ signUp: {
257
+ title: "Create account",
258
+ subtitle: "Join us today",
259
+ submitLabel: "Create Account"
260
+ },
261
+ forgotPassword: {
262
+ title: "Reset password",
263
+ subtitle: "We'll send you a reset link",
264
+ submitLabel: "Send Reset Link"
265
+ }
266
+ };
267
+ function _(e, t) {
268
+ return t && e.hasMode(t) ? t : e.defaultMode;
269
+ }
270
+ function v(e) {
271
+ return e instanceof Error ? e.message.replace(/^(Exception|Error):\s*/u, "") : String(e).replace(/^(Exception|Error):\s*/u, "");
272
+ }
273
+ function y(e) {
274
+ return /^[^@]+@[^@]+\.[^@]+$/u.test(e.trim());
275
+ }
276
+ function b(e, t) {
277
+ let n = {};
278
+ return e === "signUp" && (t.name.trim() || (n.name = "Name is required")), t.email.trim() ? y(t.email) || (n.email = "Enter a valid email") : n.email = "Email is required", e !== "forgotPassword" && (t.password ? e === "signUp" && t.password.length < 8 && (n.password = "Minimum 8 characters") : n.password = "Password is required"), e === "signUp" && (t.confirmPassword ? t.confirmPassword !== t.password && (n.confirmPassword = "Passwords do not match") : n.confirmPassword = "Please confirm password"), n;
279
+ }
280
+ function ee(e, t) {
281
+ return e === "signIn" && t.hasMode("forgotPassword") && {
282
+ label: "Forgot password?",
283
+ mode: "forgotPassword"
284
+ };
285
+ }
286
+ function te(e, t) {
287
+ return e === "signIn" && t.hasMode("signUp") ? [{
288
+ prompt: "Don't have an account?",
289
+ label: "Sign up",
290
+ mode: "signUp"
291
+ }] : e === "signUp" && t.hasMode("signIn") ? [{
292
+ prompt: "Already have an account?",
293
+ label: "Sign in",
294
+ mode: "signIn"
295
+ }] : e === "forgotPassword" && t.hasMode("signIn") ? [{
296
+ prompt: "Remember your password?",
297
+ label: "Sign in",
298
+ mode: "signIn"
299
+ }] : [];
300
+ }
301
+ function ne(e) {
302
+ let [t, n] = a(() => typeof window > "u" || typeof window.matchMedia != "function" ? !1 : window.matchMedia("(prefers-color-scheme: dark)").matches);
303
+ return r(() => {
304
+ if (e && e !== "system" && typeof window < "u" && typeof window.matchMedia == "function" || typeof window > "u" || typeof window.matchMedia != "function") return;
305
+ let t = window.matchMedia("(prefers-color-scheme: dark)"), r = (e) => {
306
+ n(e.matches);
307
+ };
308
+ return n(t.matches), t.addEventListener("change", r), () => t.removeEventListener("change", r);
309
+ }, [e]), e === "dark" ? "dark" : e === "light" ? "light" : t ? "dark" : "light";
310
+ }
311
+ function x(e) {
312
+ return {
313
+ borderRadius: e?.inputBorderRadius ?? 12,
314
+ ...e?.inputStyle
315
+ };
316
+ }
317
+ function S() {
318
+ return /* @__PURE__ */ (0, f.jsx)("span", {
319
+ className: "raf__spinner",
320
+ "aria-hidden": "true"
321
+ });
322
+ }
323
+ function C(e) {
324
+ let { name: t, label: n, value: r, type: i = "text", autoComplete: a, onChange: o, onBlur: s, error: c, theme: l } = e;
325
+ return /* @__PURE__ */ (0, f.jsxs)("label", {
326
+ className: "raf__field",
327
+ children: [
328
+ /* @__PURE__ */ (0, f.jsx)("span", {
329
+ className: "raf__field-label",
330
+ children: n
331
+ }),
332
+ /* @__PURE__ */ (0, f.jsx)("input", {
333
+ className: "raf__input",
334
+ name: t,
335
+ type: i,
336
+ value: r,
337
+ autoComplete: a,
338
+ "aria-invalid": !!c,
339
+ "aria-describedby": c ? `${t}-error` : void 0,
340
+ onChange: (e) => o(e.target.value),
341
+ onBlur: s,
342
+ style: x(l)
343
+ }),
344
+ c ? /* @__PURE__ */ (0, f.jsx)("span", {
345
+ id: `${t}-error`,
346
+ className: "raf__field-error",
347
+ children: c
348
+ }) : null
349
+ ]
350
+ });
351
+ }
352
+ function w(e) {
353
+ let { links: t, switchMode: n, theme: r } = e;
354
+ if (t.length === 0) return null;
355
+ let [i] = t;
356
+ return /* @__PURE__ */ (0, f.jsxs)("div", {
357
+ className: "raf__mode-switcher",
358
+ children: [/* @__PURE__ */ (0, f.jsxs)("span", { children: [i.prompt, " "] }), /* @__PURE__ */ (0, f.jsx)("button", {
359
+ type: "button",
360
+ className: "raf__inline-button",
361
+ onClick: () => n(i.mode),
362
+ style: r?.linkStyle,
363
+ children: i.label
364
+ })]
365
+ });
366
+ }
367
+ function re(e, t) {
368
+ return t ? `${e} ${t}` : e;
369
+ }
370
+ function T(e) {
371
+ let { authFlowType: n = l.all(), onSignIn: o, onSignUp: s, onForgotPassword: c, onSignInSuccess: u, onSignUpSuccess: d, onForgotPasswordSuccess: y, isLoading: x, errorMessage: T, initialMode: E, theme: D, className: O, headerRenderer: k, footerRenderer: A, errorRenderer: j, loadingRenderer: M, submitButtonRenderer: N, modeSwitcherRenderer: P } = e, [F, I] = a(() => _(n, E)), [L, R] = a(p), [ie, z] = a({}), [ae, B] = a(!1), [oe, se] = a(!1), [ce, V] = a(null), le = ne(D?.themeMode), H = x ?? oe, U = T ?? ce, ue = i(() => b(F, L), [F, L]), W = ee(F, n), G = te(F, n);
372
+ r(() => {
373
+ n.hasMode(F) || (I(_(n, E)), R(p), z({}), B(!1), V(null));
374
+ }, [
375
+ n,
376
+ E,
377
+ F
378
+ ]);
379
+ let K = t((e) => {
380
+ n.hasMode(e) && (I(e), R(p), z({}), B(!1), V(null));
381
+ }, [n]), q = i(() => ({
382
+ mode: F,
383
+ switchMode: K,
384
+ isLoading: H,
385
+ errorMessage: U,
386
+ theme: D,
387
+ authFlowType: n
388
+ }), [
389
+ n,
390
+ U,
391
+ H,
392
+ F,
393
+ K,
394
+ D
395
+ ]), J = t((e, t) => {
396
+ R((n) => ({
397
+ ...n,
398
+ [e]: t
399
+ }));
400
+ }, []), Y = t((e) => {
401
+ if (!(!ae && !ie[e])) return ue[e];
402
+ }, [
403
+ ue,
404
+ ae,
405
+ ie
406
+ ]), X = t((e) => {
407
+ z((t) => ({
408
+ ...t,
409
+ [e]: !0
410
+ }));
411
+ }, []), de = t((e) => {
412
+ switch (e) {
413
+ case "signIn":
414
+ if (!o) throw Error("onSignIn is required when signIn mode is enabled.");
415
+ return o(L.email.trim(), L.password);
416
+ case "signUp":
417
+ if (!s) throw Error("onSignUp is required when signUp mode is enabled.");
418
+ return s(L.email.trim(), L.password, L.name.trim());
419
+ case "forgotPassword":
420
+ if (!c) throw Error("onForgotPassword is required when forgotPassword mode is enabled.");
421
+ return c(L.email.trim());
422
+ }
423
+ }, [
424
+ L.email,
425
+ L.name,
426
+ L.password,
427
+ c,
428
+ o,
429
+ s
430
+ ]), fe = t(() => {
431
+ switch (F) {
432
+ case "signIn":
433
+ u?.();
434
+ break;
435
+ case "signUp":
436
+ d?.();
437
+ break;
438
+ case "forgotPassword":
439
+ y?.();
440
+ break;
441
+ }
442
+ }, [
443
+ F,
444
+ y,
445
+ u,
446
+ d
447
+ ]), Z = t(async () => {
448
+ B(!0);
449
+ let e = b(F, L);
450
+ if (!(Object.keys(e).length > 0 || H)) {
451
+ V(null), se(!0);
452
+ try {
453
+ await Promise.resolve(de(F)), fe();
454
+ } catch (e) {
455
+ V(v(e));
456
+ } finally {
457
+ se(!1);
458
+ }
459
+ }
460
+ }, [
461
+ H,
462
+ L,
463
+ F,
464
+ de,
465
+ fe
466
+ ]), pe = t((e) => {
467
+ e.preventDefault(), Z();
468
+ }, [Z]), Q = le === "dark" ? h : m, me = {
469
+ "--raf-background": D?.backgroundColor ?? Q.background,
470
+ "--raf-input-fill": D?.inputFillColor ?? Q.inputFill,
471
+ "--raf-text": Q.text,
472
+ "--raf-muted-text": Q.mutedText,
473
+ "--raf-primary": D?.primaryColor ?? Q.primary,
474
+ "--raf-error": D?.errorColor ?? Q.error,
475
+ "--raf-surface-border": Q.surfaceBorder,
476
+ "--raf-input-border": Q.inputBorder,
477
+ "--raf-shadow": Q.shadow,
478
+ "--raf-transition-duration": `${D?.transitionDuration ?? 320}ms`,
479
+ "--raf-transition-timing": D?.transitionTimingFunction ?? "cubic-bezier(0.4, 0, 0.2, 1)"
480
+ }, $ = g[F], he = M ? M(q) : /* @__PURE__ */ (0, f.jsx)(S, {});
481
+ return /* @__PURE__ */ (0, f.jsx)("div", {
482
+ className: re(`raf raf--${le}`, O),
483
+ style: me,
484
+ children: /* @__PURE__ */ (0, f.jsxs)("div", {
485
+ className: "raf__card",
486
+ style: D?.cardStyle,
487
+ children: [
488
+ k ? k(q) : /* @__PURE__ */ (0, f.jsxs)("header", {
489
+ className: "raf__header",
490
+ children: [/* @__PURE__ */ (0, f.jsx)("h2", {
491
+ className: "raf__title",
492
+ style: D?.titleStyle,
493
+ children: $.title
494
+ }), /* @__PURE__ */ (0, f.jsx)("p", {
495
+ className: "raf__subtitle",
496
+ style: D?.subtitleStyle,
497
+ children: $.subtitle
498
+ })]
499
+ }),
500
+ U ? /* @__PURE__ */ (0, f.jsx)("div", {
501
+ className: "raf__error-shell",
502
+ "aria-live": "polite",
503
+ children: j ? j({
504
+ ...q,
505
+ error: U
506
+ }) : /* @__PURE__ */ (0, f.jsxs)("div", {
507
+ className: "raf__error",
508
+ children: [/* @__PURE__ */ (0, f.jsx)("strong", {
509
+ className: "raf__error-icon",
510
+ "aria-hidden": "true",
511
+ children: "!"
512
+ }), /* @__PURE__ */ (0, f.jsx)("span", { children: U })]
513
+ })
514
+ }) : null,
515
+ /* @__PURE__ */ (0, f.jsx)("div", {
516
+ className: "raf__panel",
517
+ children: /* @__PURE__ */ (0, f.jsxs)("form", {
518
+ className: "raf__form",
519
+ onSubmit: pe,
520
+ children: [
521
+ F === "signUp" ? /* @__PURE__ */ (0, f.jsx)(C, {
522
+ name: "name",
523
+ label: "Full name",
524
+ value: L.name,
525
+ autoComplete: "name",
526
+ onChange: (e) => J("name", e),
527
+ onBlur: () => X("name"),
528
+ error: Y("name"),
529
+ theme: D
530
+ }) : null,
531
+ /* @__PURE__ */ (0, f.jsx)(C, {
532
+ name: "email",
533
+ label: "Email",
534
+ type: "email",
535
+ value: L.email,
536
+ autoComplete: F === "signUp" ? "email" : "username",
537
+ onChange: (e) => J("email", e),
538
+ onBlur: () => X("email"),
539
+ error: Y("email"),
540
+ theme: D
541
+ }),
542
+ F === "forgotPassword" ? null : /* @__PURE__ */ (0, f.jsx)(C, {
543
+ name: "password",
544
+ label: "Password",
545
+ type: "password",
546
+ value: L.password,
547
+ autoComplete: F === "signUp" ? "new-password" : "current-password",
548
+ onChange: (e) => J("password", e),
549
+ onBlur: () => X("password"),
550
+ error: Y("password"),
551
+ theme: D
552
+ }),
553
+ F === "signUp" ? /* @__PURE__ */ (0, f.jsx)(C, {
554
+ name: "confirmPassword",
555
+ label: "Confirm password",
556
+ type: "password",
557
+ value: L.confirmPassword,
558
+ autoComplete: "new-password",
559
+ onChange: (e) => J("confirmPassword", e),
560
+ onBlur: () => X("confirmPassword"),
561
+ error: Y("confirmPassword"),
562
+ theme: D
563
+ }) : null,
564
+ W ? /* @__PURE__ */ (0, f.jsx)("div", {
565
+ className: "raf__forgot-shell",
566
+ children: /* @__PURE__ */ (0, f.jsx)("button", {
567
+ type: "button",
568
+ className: "raf__inline-button",
569
+ onClick: () => K(W.mode),
570
+ style: D?.linkStyle,
571
+ children: W.label
572
+ })
573
+ }) : null,
574
+ N ? N({
575
+ ...q,
576
+ label: $.submitLabel,
577
+ submit: () => {
578
+ Z();
579
+ }
580
+ }) : /* @__PURE__ */ (0, f.jsx)("button", {
581
+ type: "submit",
582
+ className: "raf__submit",
583
+ disabled: H,
584
+ style: {
585
+ borderRadius: D?.buttonBorderRadius ?? 12,
586
+ ...D?.buttonStyle
587
+ },
588
+ children: H ? he : /* @__PURE__ */ (0, f.jsx)("span", {
589
+ style: D?.buttonTextStyle,
590
+ children: $.submitLabel
591
+ })
592
+ }),
593
+ G.length > 0 ? P ? P({
594
+ ...q,
595
+ links: G
596
+ }) : /* @__PURE__ */ (0, f.jsx)(w, {
597
+ links: G,
598
+ switchMode: K,
599
+ theme: D
600
+ }) : null
601
+ ]
602
+ })
603
+ }, F),
604
+ A ? A(q) : null
605
+ ]
606
+ })
607
+ });
608
+ }
609
+ //#endregion
610
+ //#region src/demoAuthService.tsx
611
+ var E = class extends Error {
612
+ constructor(e) {
613
+ super(e), this.name = "DemoAuthException";
614
+ }
615
+ };
616
+ function D(e) {
617
+ let t = new TextEncoder().encode(e), n = "";
618
+ for (let e of t) n += String.fromCharCode(e);
619
+ if (typeof globalThis.btoa == "function") return globalThis.btoa(n);
620
+ let r = globalThis.Buffer;
621
+ return r ? r.from(t).toString("base64") : n;
622
+ }
623
+ function O() {
624
+ return 1e3 + Date.now() % 1e3;
625
+ }
626
+ function k(e) {
627
+ return /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/.test(e);
628
+ }
629
+ function A(e) {
630
+ return e.trim().toLowerCase();
631
+ }
632
+ function j(e) {
633
+ return new Promise((t) => {
634
+ globalThis.setTimeout(t, e);
635
+ });
636
+ }
637
+ var M = class {
638
+ users = /* @__PURE__ */ new Map();
639
+ async signIn(e, t) {
640
+ await j(O());
641
+ let n = A(e), r = this.users.get(n);
642
+ if (!r || r.passwordHash !== D(t)) throw new E("Invalid email or password");
643
+ return {
644
+ email: r.email,
645
+ name: r.name
646
+ };
647
+ }
648
+ async signUp(e, t, n) {
649
+ await j(O());
650
+ let r = A(e);
651
+ if (!k(r)) throw new E("Please enter a valid email address");
652
+ if (t.length < 8) throw new E("Password must be at least 8 characters");
653
+ if (this.users.has(r)) throw new E("Email already in use");
654
+ let i = {
655
+ email: r,
656
+ passwordHash: D(t),
657
+ name: n.trim(),
658
+ createdAt: /* @__PURE__ */ new Date()
659
+ };
660
+ return this.users.set(r, i), {
661
+ email: i.email,
662
+ name: i.name
663
+ };
664
+ }
665
+ async forgotPassword(e) {
666
+ if (await j(1e3), !k(A(e))) throw new E("Please enter a valid email address");
667
+ }
668
+ clearUsers() {
669
+ this.users.clear();
670
+ }
671
+ get userCount() {
672
+ return this.users.size;
673
+ }
674
+ getCreatedAt(e) {
675
+ return this.users.get(A(e))?.createdAt ?? null;
676
+ }
677
+ }, N = e(null);
678
+ function P(e) {
679
+ let { service: t, children: n } = e;
680
+ return /* @__PURE__ */ (0, f.jsx)(N.Provider, {
681
+ value: t,
682
+ children: n
683
+ });
684
+ }
685
+ function F() {
686
+ let e = n(N);
687
+ if (!e) throw Error("useDemoAuthService() must be used inside a DemoAuthServiceProvider.");
688
+ return e;
689
+ }
690
+ function I() {
691
+ return n(N);
692
+ }
693
+ //#endregion
694
+ export { T as AuthFlow, l as AuthFlowType, E as DemoAuthException, M as DemoAuthService, P as DemoAuthServiceProvider, F as useDemoAuthService, I as useMaybeDemoAuthService };
@@ -0,0 +1,2 @@
1
+ .raf{color:var(--raf-text);width:100%;font-family:Inter,ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,sans-serif}.raf__card{background:var(--raf-background);border:1px solid var(--raf-surface-border);box-shadow:var(--raf-shadow);border-radius:24px;padding:28px}.raf__header{margin-bottom:24px}.raf__title{margin:0;font-size:1.75rem;font-weight:700;line-height:1.2}.raf__subtitle{color:var(--raf-muted-text);margin:8px 0 0;font-size:.95rem}.raf__error-shell{margin-bottom:18px}.raf__error{background:color-mix(in srgb, var(--raf-error) 10%, transparent);border:1px solid color-mix(in srgb, var(--raf-error) 22%, transparent);color:var(--raf-error);border-radius:14px;align-items:center;gap:10px;padding:12px 14px;display:flex}.raf__error-icon{border:1px solid;border-radius:999px;justify-content:center;align-items:center;width:18px;height:18px;font-size:.8rem;display:inline-flex}.raf__panel{animation:raf-enter var(--raf-transition-duration) var(--raf-transition-timing)}.raf__form{flex-direction:column;gap:16px;display:flex}.raf__field{flex-direction:column;gap:8px;display:flex}.raf__field-label{font-size:.95rem;font-weight:600}.raf__input{appearance:none;background:var(--raf-input-fill);border:1px solid var(--raf-input-border);color:var(--raf-text);font:inherit;min-height:48px;transition:border-color var(--raf-transition-duration) ease, box-shadow var(--raf-transition-duration) ease, background-color var(--raf-transition-duration) ease;outline:none;padding:0 14px}.raf__input:focus{border-color:var(--raf-primary);box-shadow:0 0 0 4px color-mix(in srgb, var(--raf-primary) 18%, transparent)}.raf__input[aria-invalid=true]{border-color:var(--raf-error)}.raf__field-error{color:var(--raf-error);font-size:.875rem}.raf__forgot-shell{justify-content:flex-end;margin-top:-6px;display:flex}.raf__inline-button{color:var(--raf-primary);cursor:pointer;font:inherit;background:0 0;border:0;padding:0;font-weight:600}.raf__inline-button:hover{opacity:.88}.raf__submit{background:var(--raf-primary);color:#fff;cursor:pointer;font:inherit;min-height:50px;transition:opacity var(--raf-transition-duration) ease, transform var(--raf-transition-duration) ease;border:none;justify-content:center;align-items:center;padding:0 16px;font-weight:600;display:inline-flex}.raf__submit:hover:not(:disabled){opacity:.94;transform:translateY(-1px)}.raf__submit:disabled{cursor:not-allowed;opacity:.7}.raf__spinner{border:2.5px solid #fff6;border-top-color:#fff;border-radius:999px;width:20px;height:20px;animation:.8s linear infinite raf-spin;display:inline-block}.raf__mode-switcher{color:var(--raf-muted-text);justify-content:center;gap:4px;margin-top:8px;display:flex}@keyframes raf-enter{0%{opacity:0;transform:translateY(10px)}to{opacity:1;transform:translateY(0)}}@keyframes raf-spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}@media (width<=480px){.raf__card{border-radius:20px;padding:22px}}
2
+ /*$vite$:1*/
package/package.json ADDED
@@ -0,0 +1,58 @@
1
+ {
2
+ "name": "@maheshbvv/react-auth-flow",
3
+ "version": "0.1.0",
4
+ "description": "Drop-in React authentication flow with sign in, sign up, forgot password, theming, transitions, and render prop customization.",
5
+ "type": "module",
6
+ "main": "./dist/index.cjs",
7
+ "module": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "style": "./dist/react-auth-flow.css",
10
+ "exports": {
11
+ ".": {
12
+ "types": "./dist/index.d.ts",
13
+ "import": "./dist/index.js",
14
+ "require": "./dist/index.cjs"
15
+ },
16
+ "./styles.css": "./dist/react-auth-flow.css"
17
+ },
18
+ "files": [
19
+ "dist"
20
+ ],
21
+ "sideEffects": [
22
+ "**/*.css"
23
+ ],
24
+ "scripts": {
25
+ "build": "tsc -p tsconfig.build.json && vite build",
26
+ "test": "vitest run",
27
+ "test:watch": "vitest",
28
+ "typecheck": "tsc --noEmit"
29
+ },
30
+ "keywords": [
31
+ "react",
32
+ "authentication",
33
+ "auth flow",
34
+ "signin",
35
+ "signup",
36
+ "forgot password",
37
+ "component library"
38
+ ],
39
+ "license": "MIT",
40
+ "peerDependencies": {
41
+ "react": "^18.0.0 || ^19.0.0",
42
+ "react-dom": "^18.0.0 || ^19.0.0"
43
+ },
44
+ "devDependencies": {
45
+ "@testing-library/jest-dom": "^6.9.1",
46
+ "@testing-library/react": "^16.3.2",
47
+ "@testing-library/user-event": "^14.6.1",
48
+ "@types/react": "^19.2.14",
49
+ "@types/react-dom": "^19.2.3",
50
+ "@vitejs/plugin-react": "^6.0.1",
51
+ "jsdom": "^29.0.2",
52
+ "react": "^19.2.5",
53
+ "react-dom": "^19.2.5",
54
+ "typescript": "^6.0.2",
55
+ "vite": "^8.0.7",
56
+ "vitest": "^4.1.3"
57
+ }
58
+ }