Skip to content

Commit 7f97af8

Browse files
Finish Video
1 parent 3bf1940 commit 7f97af8

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

57 files changed

+263
-1
lines changed

package-lock.json

+41
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+2
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
"@testing-library/jest-dom": "^5.14.1",
77
"@testing-library/react": "^11.2.7",
88
"@testing-library/user-event": "^12.8.3",
9+
"copy-to-clipboard": "^3.3.1",
10+
"js-cookie": "^3.0.1",
911
"lodash": "^4.17.21",
1012
"react": "^17.0.2",
1113
"react-dom": "^17.0.2",
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import { useRef, useState } from "react"
2+
import useClickOutside from "./useClickOutside"
3+
4+
export default function ClickOutsideComponent() {
5+
const [open, setOpen] = useState(false)
6+
const modalRef = useRef()
7+
8+
useClickOutside(modalRef, () => {
9+
if (open) setOpen(false)
10+
})
11+
12+
return (
13+
<>
14+
<button onClick={() => setOpen(true)}>Open</button>
15+
<div
16+
ref={modalRef}
17+
style={{
18+
display: open ? "block" : "none",
19+
backgroundColor: "blue",
20+
color: "white",
21+
width: "100px",
22+
height: "100px",
23+
position: "absolute",
24+
top: "calc(50% - 50px)",
25+
left: "calc(50% - 50px)",
26+
}}
27+
>
28+
<span>Modal</span>
29+
</div>
30+
</>
31+
)
32+
}
+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import useEventListener from "../13-useEventListener/useEventListener"
2+
3+
export default function useClickOutside(ref, cb) {
4+
useEventListener(
5+
"click",
6+
e => {
7+
if (ref.current == null || ref.current.contains(e.target)) return
8+
cb(e)
9+
},
10+
document
11+
)
12+
}
+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import useDarkMode from "./useDarkMode"
2+
import "./body.css"
3+
4+
export default function DarkModeComponent() {
5+
const [darkMode, setDarkMode] = useDarkMode()
6+
7+
return (
8+
<button
9+
onClick={() => setDarkMode(prevDarkMode => !prevDarkMode)}
10+
style={{
11+
border: `1px solid ${darkMode ? "white" : "black"}`,
12+
background: "none",
13+
color: darkMode ? "white" : "black",
14+
}}
15+
>
16+
Toggle Dark Mode
17+
</button>
18+
)
19+
}

src/22-useDarkMode/body.css

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
body.dark-mode {
2+
background-color: #333;
3+
}

src/22-useDarkMode/useDarkMode.js

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { useEffect } from "react"
2+
import useMediaQuery from "../16-useMediaQuery/useMediaQuery"
3+
import { useLocalStorage } from "../8-useStorage/useStorage"
4+
5+
export default function useDarkMode() {
6+
const [darkMode, setDarkMode] = useLocalStorage("useDarkMode")
7+
const prefersDarkMode = useMediaQuery("(prefers-color-scheme: dark)")
8+
const enabled = darkMode ?? prefersDarkMode
9+
10+
useEffect(() => {
11+
document.body.classList.toggle("dark-mode", enabled)
12+
}, [enabled])
13+
14+
return [enabled, setDarkMode]
15+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import useCopyToClipboard from "./useCopyToClipboard"
2+
3+
export default function CopyToClipboardComponent() {
4+
const [copyToClipboard, { success }] = useCopyToClipboard()
5+
6+
return (
7+
<>
8+
<button onClick={() => copyToClipboard("This was copied")}>
9+
{success ? "Copied" : "Copy Text"}
10+
</button>
11+
<input type="text" />
12+
</>
13+
)
14+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { useState } from "react"
2+
import copy from "copy-to-clipboard"
3+
4+
export default function useCopyToClipboard() {
5+
const [value, setValue] = useState()
6+
const [success, setSuccess] = useState()
7+
8+
const copyToClipboard = (text, options) => {
9+
const result = copy(text, options)
10+
if (result) setValue(text)
11+
setSuccess(result)
12+
}
13+
14+
return [copyToClipboard, { value, success }]
15+
}

src/24-useCookie/CookieComponent.js

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import useCookie from "./useCookie"
2+
3+
export default function CookieComponent() {
4+
const [value, update, remove] = useCookie("name", "John")
5+
6+
return (
7+
<>
8+
<div>{value}</div>
9+
<button onClick={() => update("Sally")}>Change Name To Sally</button>
10+
<button onClick={remove}>Delete Name</button>
11+
</>
12+
)
13+
}

src/24-useCookie/useCookie.js

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import { useState, useCallback } from "react"
2+
import Cookies from "js-cookie"
3+
4+
export default function useCookie(name, defaultValue) {
5+
const [value, setValue] = useState(() => {
6+
const cookie = Cookies.get(name)
7+
if (cookie) return cookie
8+
Cookies.set(name, defaultValue)
9+
return defaultValue
10+
})
11+
12+
const updateCookie = useCallback(
13+
(newValue, options) => {
14+
Cookies.set(name, newValue, options)
15+
setValue(newValue)
16+
},
17+
[name]
18+
)
19+
20+
const deleteCookie = useCallback(() => {
21+
Cookies.remove(name)
22+
setValue(null)
23+
}, [name])
24+
25+
return [value, updateCookie, deleteCookie]
26+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import useTranslation from "./useTranslation"
2+
3+
export default function TranslationComponent() {
4+
const { language, setLanguage, setFallbackLanguage, t } = useTranslation()
5+
6+
return (
7+
<>
8+
<div>{language}</div>
9+
<div>{t("hi")}</div>
10+
<div>{t("bye")}</div>
11+
<div>{t("nested.value")}</div>
12+
<button onClick={() => setLanguage("sp")}>Change To Spanish</button>
13+
<button onClick={() => setLanguage("en")}>Change To English</button>
14+
</>
15+
)
16+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"hi": "Hello",
3+
"bye": "Goodbye",
4+
"nested": { "value": "Test" }
5+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export * as en from "./en.json"
2+
export * as sp from "./sp.json"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"hi": "Hola"
3+
}
+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import { useLocalStorage } from "../8-useStorage/useStorage"
2+
import * as translations from "./translations"
3+
4+
export default function useTranslation() {
5+
const [language, setLanguage] = useLocalStorage("language", "en")
6+
const [fallbackLanguage, setFallbackLanguage] = useLocalStorage(
7+
"fallbackLanguage",
8+
"en"
9+
)
10+
11+
const translate = key => {
12+
const keys = key.split(".")
13+
14+
return (
15+
getNestedTranslation(language, keys) ??
16+
getNestedTranslation(fallbackLanguage, keys) ??
17+
key
18+
)
19+
}
20+
21+
return {
22+
language,
23+
setLanguage,
24+
fallbackLanguage,
25+
setFallbackLanguage,
26+
t: translate,
27+
}
28+
}
29+
30+
function getNestedTranslation(language, keys) {
31+
return keys.reduce((obj, key) => {
32+
return obj?.[key]
33+
}, translations[language])
34+
}

src/App.js

+11-1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,11 @@ import GeolocationComponent from "./17-useGeolocation/GeolocationComponent"
1818
import StateWithValidationComponent from "./18-useStateWithValidation/StateWithValidationComponent"
1919
import SizeComponent from "./19-useSize/SizeComponent"
2020
import EffectOnceComponent from "./20-useEffectOnce/EffectOnceComponent"
21+
import ClickOutsideComponent from "./21-useClickOutside/ClickOutsideComponent"
22+
import DarkModeComponent from "./22-useDarkMode/DarkModeComponent"
23+
import CopyToClipboardComponent from "./23-useCopyToClipboard/CopyToClipboardComponent"
24+
import CookieComponent from "./24-useCookie/CookieComponent"
25+
import TranslationComponent from "./25-useTranslation/TranslationComponent"
2126

2227
function App() {
2328
// return <ToggleComponent />
@@ -33,13 +38,18 @@ function App() {
3338
// return <ScriptComponent />
3439
// return <DeepCompareEffectComponent />
3540
// return <EventListenerComponent />
36-
return <OnScreenComponentComponent />
41+
// return <OnScreenComponentComponent />
3742
// return <WindowSizeComponent />
3843
// return <MediaQueryComponent />
3944
// return <GeolocationComponent />
4045
// return <StateWithValidationComponent />
4146
// return <SizeComponent />
4247
// return <EffectOnceComponent />
48+
// return <ClickOutsideComponent />
49+
// return <DarkModeComponent />
50+
// return <CopyToClipboardComponent />
51+
// return <CookieComponent />
52+
return <TranslationComponent />
4353
}
4454

4555
export default App

0 commit comments

Comments
 (0)