Skip to content

Commit e6375a9

Browse files
author
sw-yx
committed
add mvp
1 parent b69a5c0 commit e6375a9

File tree

11 files changed

+848
-157
lines changed

11 files changed

+848
-157
lines changed

.size-snapshot.json

+18-18
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,35 @@
11
{
22
"/Users/swyx/Netlify/react-netlify-identity-widget/dist/reactNetlifyIdentityWidget.cjs.development.js": {
3-
"bundled": 10793,
4-
"minified": 5037,
5-
"gzipped": 1648
3+
"bundled": 14407,
4+
"minified": 7244,
5+
"gzipped": 1917
66
},
77
"/Users/swyx/Netlify/react-netlify-identity-widget/dist/reactNetlifyIdentityWidget.cjs.production.js": {
8-
"bundled": 10793,
9-
"minified": 5037,
10-
"gzipped": 1648
8+
"bundled": 14407,
9+
"minified": 7244,
10+
"gzipped": 1917
1111
},
1212
"/Users/swyx/Netlify/react-netlify-identity-widget/dist/reactNetlifyIdentityWidget.umd.development.js": {
13-
"bundled": 11765,
14-
"minified": 4641,
15-
"gzipped": 1644
13+
"bundled": 15516,
14+
"minified": 6554,
15+
"gzipped": 1892
1616
},
1717
"/Users/swyx/Netlify/react-netlify-identity-widget/dist/reactNetlifyIdentityWidget.umd.production.js": {
18-
"bundled": 11765,
19-
"minified": 4641,
20-
"gzipped": 1644
18+
"bundled": 15516,
19+
"minified": 6554,
20+
"gzipped": 1892
2121
},
2222
"/Users/swyx/Netlify/react-netlify-identity-widget/dist/reactNetlifyIdentityWidget.es.production.js": {
23-
"bundled": 10589,
24-
"minified": 4832,
25-
"gzipped": 1586,
23+
"bundled": 14204,
24+
"minified": 7080,
25+
"gzipped": 1875,
2626
"treeshaked": {
2727
"rollup": {
28-
"code": 359,
29-
"import_statements": 105
28+
"code": 520,
29+
"import_statements": 125
3030
},
3131
"webpack": {
32-
"code": 1458
32+
"code": 1654
3333
}
3434
}
3535
}

example/src/App.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
import React from "react"
22
import logo from "./logo.svg"
33
import "./App.css"
4-
import { IdentityModal, IdentityContext } from "react-netlify-identity-widget"
4+
import { IdentityModal, useIdentityContext } from "react-netlify-identity-widget"
55
import "react-netlify-identity-widget/styles.css"
66

77
function App() {
88
const [dialog, setDialog] = React.useState(false)
9-
const identity = React.useContext(IdentityContext)
9+
const identity = useIdentityContext
1010
console.log("login status can be used anywhere in app", identity)
1111
return (
1212
<div className="App">

src/app.tsx

+31-26
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,9 @@
11
import React from "react"
2-
import { Login } from "./login"
2+
import { Login } from "./components/login"
33
import { Logout } from "./components/logout"
4-
5-
// import Modal from "./modal"
6-
// import SiteURLForm from "./forms/siteurl"
7-
// import LogoutForm from "./forms/logout"
8-
// import UserForm from "./forms/user"
9-
// import Providers from "./forms/providers"
10-
// import Message from "./forms/message"
11-
import { IdentityContext } from "./context"
4+
import { Signup } from "./components/signup"
5+
import { useIdentityContext } from "./context"
6+
import { Tabs, TabList, Tab, TabPanels, TabPanel } from "@reach/tabs"
127
// const pagesWithHeader = { login: true, signup: true }
138
// const pages = {
149
// login: {
@@ -61,14 +56,32 @@ import { IdentityContext } from "./context"
6156
function LoggedOutScreen() {
6257
// return
6358
return (
64-
<>
65-
<div className="header">
66-
<button className="btn btnHeader ">Sign up</button>
67-
<button className="btn btnHeader active">Log in</button>
59+
<div>
60+
<Tabs defaultIndex={0}>
61+
<TabList className="header">
62+
<Tab className="btn btnHeader ">Login</Tab>
63+
<Tab className="btn btnHeader ">Sign Up</Tab>
64+
</TabList>
65+
66+
<TabPanels>
67+
<TabPanel>
68+
<Login />
69+
</TabPanel>
70+
<TabPanel>
71+
<Signup />
72+
</TabPanel>
73+
</TabPanels>
74+
</Tabs>
75+
76+
<div className="providersGroup">
77+
<hr className="hr" />
78+
<button className="providerGoogle btn btnProvider">Continue with Google</button>
79+
<button className="providerGitHub btn btnProvider">Continue with GitHub</button>
80+
<button className="providerGitLab btn btnProvider">Continue with GitLab</button>
81+
<button className="providerBitBucket btn btnProvider">Continue with BitBucket</button>
6882
</div>
69-
<div>
70-
<Login />
71-
{/* <form className="form ">
83+
84+
{/* <form className="form ">
7285
<div className="formGroup">
7386
<label>
7487
<span className="visuallyHidden">Enter your email</span>
@@ -96,23 +109,15 @@ function LoggedOutScreen() {
96109
</form>
97110
<button className="btnLink forgotPasswordLink">Forgot password?</button>
98111
*/}
99-
</div>
100-
<div className="providersGroup">
101-
<hr className="hr" />
102-
<button className="providerGoogle btn btnProvider">Continue with Google</button>
103-
<button className="providerGitHub btn btnProvider">Continue with GitHub</button>
104-
<button className="providerGitLab btn btnProvider">Continue with GitLab</button>
105-
<button className="providerBitBucket btn btnProvider">Continue with BitBucket</button>
106-
</div>
107-
</>
112+
</div>
108113
)
109114
}
110115
function LoggedInScreen() {
111116
return <Logout />
112117
}
113118

114119
function Gate({ }: { onCloseDialog: Function }) {
115-
const identity = React.useContext(IdentityContext)
120+
const identity = useIdentityContext()
116121
const isLoggedIn = Boolean(identity && identity.user)
117122
return isLoggedIn ? <LoggedInScreen /> : <LoggedOutScreen />
118123
}

src/components/login.tsx

+69-13
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,74 @@
11
import React from "react"
2+
import { useIdentityContext } from "../context"
3+
import useLoading from "../useLoading"
4+
import VisuallyHidden from "@reach/visually-hidden"
25

3-
export function LogIn() {
6+
export function Login() {
7+
const { loginUser } = useIdentityContext()
8+
const formRef = React.useRef<HTMLFormElement>(null)
9+
const [msg, setMsg] = React.useState("")
10+
const [isLoading, load] = useLoading()
11+
// const signup = () => {
12+
// if (!formRef.current) return
13+
// const email = formRef.current.email.value
14+
// const password = formRef.current.password.value
15+
// const data = { signupSource: "react-netlify-identity-widget" }
16+
// load(signupUser(email, password, data))
17+
// .then(user => {
18+
// console.log("Success! Signed up", user)
19+
// // navigate("/dashboard")
20+
// })
21+
// .catch(err => void console.error(err) || setMsg("Error: " + err.message))
22+
// }
423
return (
5-
<div>
6-
<label>
7-
Email
8-
<input type="email" />
9-
</label>
10-
<label>
11-
Password
12-
<input type="password" />
13-
</label>
14-
<button>Login</button>
15-
<button type="button">Forgot Password</button>
16-
</div>
24+
<form
25+
ref={formRef}
26+
className="form"
27+
onSubmit={(e: React.SyntheticEvent) => {
28+
e.preventDefault()
29+
const target = e.target as typeof e.target & { email: { value: string }; password: { value: string } }
30+
const email = target.email.value
31+
const password = target.password.value
32+
load(loginUser(email, password, true))
33+
.then(user => {
34+
console.log("Success! Logged in", user)
35+
// navigate("/dashboard")
36+
})
37+
.catch(err => void console.error(err) || setMsg("Error: " + err.message))
38+
}}
39+
>
40+
<div className="formGroup">
41+
<label>
42+
<VisuallyHidden>Enter your email</VisuallyHidden>
43+
<input
44+
className="formControl"
45+
type="email"
46+
name="email"
47+
placeholder="Email"
48+
autoCapitalize="off"
49+
required={true}
50+
/>
51+
<div className="inputFieldIcon inputFieldEmail" />
52+
</label>
53+
</div>
54+
<div className="formGroup">
55+
<label>
56+
<VisuallyHidden>Enter your password</VisuallyHidden>
57+
<input className="formControl" type="password" name="password" placeholder="Password" required={true} />
58+
<div className="inputFieldIcon inputFieldPassword" />
59+
</label>
60+
</div>
61+
62+
<div>
63+
{isLoading && "loading..."}
64+
<button type="submit" className="btn">
65+
Log in
66+
</button>
67+
{msg && <pre>{msg}</pre>}
68+
</div>
69+
<button type="button" className="btnLink forgotPasswordLink">
70+
Forgot password?
71+
</button>
72+
</form>
1773
)
1874
}

src/components/logout.tsx

+3-4
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
import React from "react"
2-
import { IdentityContext } from "../context"
2+
import { useIdentityContext } from "../context"
33

44
export function Logout() {
5-
const identity = React.useContext(IdentityContext)
6-
if (!identity) throw new Error("bad context")
7-
5+
const identity = useIdentityContext()
6+
console.log({ identity })
87
return (
98
<>
109
<div className="header">

src/components/signup.tsx

+72-16
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,77 @@
11
import React from "react"
2+
import { useIdentityContext } from "../context"
3+
import useLoading from "../useLoading"
4+
import VisuallyHidden from "@reach/visually-hidden"
25

3-
export function SignUp() {
6+
export function Signup() {
7+
const { signupUser } = useIdentityContext()
8+
const formRef = React.useRef<HTMLFormElement>(null)
9+
const [msg, setMsg] = React.useState("")
10+
const [isLoading, load] = useLoading()
11+
const signup = () => {
12+
if (!formRef.current) return
13+
const email = formRef.current.email.value
14+
const password = formRef.current.password.value
15+
const data = { signupSource: "react-netlify-identity-widget" }
16+
load(signupUser(email, password, data))
17+
.then(user => {
18+
console.log("Success! Signed up", user)
19+
// navigate("/dashboard")
20+
})
21+
.catch(err => void console.error(err) || setMsg("Error: " + err.message))
22+
}
423
return (
5-
<div>
6-
<label>
7-
Name
8-
<input type="text" />
9-
</label>
10-
<label>
11-
Email
12-
<input type="email" />
13-
</label>
14-
<label>
15-
Password
16-
<input type="password" />
17-
</label>
18-
<button>Sign up</button>
19-
</div>
24+
<form
25+
ref={formRef}
26+
className="form"
27+
onSubmit={(e: React.SyntheticEvent) => {
28+
e.preventDefault()
29+
signup()
30+
}}
31+
>
32+
<div className="formGroup">
33+
<label>
34+
<VisuallyHidden>Enter your name</VisuallyHidden>
35+
<input
36+
className="formControl"
37+
type="name"
38+
name="name"
39+
placeholder="Name"
40+
autoCapitalize="off"
41+
required={true}
42+
/>
43+
<div className="inputFieldIcon inputFieldName" />
44+
</label>
45+
</div>
46+
<div className="formGroup">
47+
<label>
48+
<VisuallyHidden>Enter your email</VisuallyHidden>
49+
<input
50+
className="formControl"
51+
type="email"
52+
name="email"
53+
placeholder="Email"
54+
autoCapitalize="off"
55+
required={true}
56+
/>
57+
<div className="inputFieldIcon inputFieldEmail" />
58+
</label>
59+
</div>
60+
<div className="formGroup">
61+
<label>
62+
<VisuallyHidden>Enter your password</VisuallyHidden>
63+
<input className="formControl" type="password" name="password" placeholder="Password" required={true} />
64+
<div className="inputFieldIcon inputFieldPassword" />
65+
</label>
66+
</div>
67+
68+
<div>
69+
{isLoading && "loading..."}
70+
<button type="submit" className="btn">
71+
Sign Up
72+
</button>
73+
{msg && <pre>{msg}</pre>}
74+
</div>
75+
</form>
2076
)
2177
}

src/context.tsx

+17-9
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,29 @@
11
import React from "react"
22
import { useNetlifyIdentity } from "react-netlify-identity"
33

4-
export const IdentityContext = React.createContext<ReturnType<typeof useNetlifyIdentity> | undefined>(undefined) // not necessary but recommended
4+
export const [useIdentityContext, IdentityContextProvider] = createUsableCtx<ReturnType<typeof useNetlifyIdentity>>()
55

6-
const { ctx, Provider } = createCtx<"login" | "signup">("login")
6+
export const [FormStateContext, FormStateContextProvider] = createMutableCtx<"login" | "signup">("login")
77

8-
export const FormStateContext = ctx
9-
export const FormStateContextProvider = Provider
8+
// utils
109

11-
function createCtx<A>(defaultValue: A) {
10+
function createMutableCtx<A>(defaultValue: A) {
1211
type UpdateType = React.Dispatch<React.SetStateAction<typeof defaultValue>>
1312
const defaultUpdate: UpdateType = () => defaultValue
1413
const ctx = React.createContext({ state: defaultValue, update: defaultUpdate })
15-
function Provider({ children }: { children: React.ReactNode }) {
14+
function Provider(props: React.PropsWithChildren<{}>) {
1615
const [state, update] = React.useState(defaultValue)
17-
return <ctx.Provider value={{ state, update }}>{children}</ctx.Provider>
16+
return <ctx.Provider value={{ state, update }} {...props} />
1817
}
19-
// return [ctx, Provider] as [typeof ctx, typeof Provider]
20-
return { ctx, Provider }
18+
return [ctx, Provider] as const
19+
}
20+
21+
function createUsableCtx<A>() {
22+
const ctx = React.createContext<A | undefined>(undefined)
23+
function useCtx() {
24+
const c = React.useContext(ctx)
25+
if (!c) throw new Error("useCtx must be inside a Provider with a value")
26+
return c
27+
}
28+
return [useCtx, ctx.Provider] as const
2129
}

0 commit comments

Comments
 (0)