Skip to content

Commit

Permalink
handle custom toast (in progress)
Browse files Browse the repository at this point in the history
  • Loading branch information
widse committed Dec 10, 2023
1 parent b1a0e62 commit 8400244
Show file tree
Hide file tree
Showing 11 changed files with 182 additions and 46 deletions.
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,11 @@
"@types/node": "^16.7.13",
"@types/react": "^18.0.0",
"@types/react-dom": "^18.0.0",
"framer-motion": "^10.16.16",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-scripts": "5.0.1",
"recoil": "^0.7.7",
"typescript": "^4.4.2",
"web-vitals": "^2.1.0"
},
Expand Down
22 changes: 3 additions & 19 deletions src/App.css
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
.App {
text-align: center;
width: 100vw;
height: 100vh;
}

#toast-root {
Expand All @@ -13,26 +15,11 @@
gap: 16px;
}

.Toast {
box-sizing: border-box;

display: flex;
align-items: center;
width: 320px;
height: 64px;
padding: 12px 20px;

border-radius: 32px;
box-shadow: 0px 4px 16px rgba(0, 0, 0, 0.25);
background: #080a0d;
color: #fff;
}

/* toastSample3 */
.toast-container {
display: flex;
flex-direction: column;
gap: 10px;
gap: 16px;
position: fixed;
left: 16px;
top: 16px;
Expand All @@ -43,19 +30,16 @@

.toast {
will-change: transform;
transition: all 0.3s ease;
box-shadow: 0 3px 10px rgba(0, 0, 0, 0.1), 0 3px 3px rgba(0, 0, 0, 0.05);
width: 180px;
height: 45px;
border-radius: 8px;
border: 2px solid #FFF;
position: relative;
cursor: pointer;
display: flex;
flex-shrink: 0;
align-items: center;
justify-content: center;
bottom: 0px;
}

.toast.toast-success {
Expand Down
12 changes: 7 additions & 5 deletions src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
import { type } from "os";
import "./App.css";
import ToastContainer from "./components/toast/ToastContainer";
import useToast3 from "./components/toast/useToast";
import useToast from "./components/toast/useToast";

function App() {
// sample3
const { toasts, addToast, removeToast } = useToast3();
const { toasts, addToast, removeToast } = useToast();

return (
<div className="App">
<div>
<button onClick={() => addToast("Toast!!!")}>Show Toast</button>
<ToastContainer toasts = {toasts} removeToast = {removeToast}/>
<button onClick={() => addToast("Toast!!!", "info")}>Show Toast#1</button>
<button onClick={() => addToast("Toast!!!", "error")}>Show Toast#2</button>
<button onClick={() => addToast("Toast!!!", "success")}>Show Toast#3</button>
</div>
<ToastContainer toasts = {toasts} removeToast = {removeToast}/>
</div>
);
}
Expand Down
1 change: 0 additions & 1 deletion src/components/toast/Toast.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ const Toast = ({ toast, removeToast } : ToastProps) => {
`;

useEffect(() => {
console.log(toast)
const timer = setTimeout(() => {
setVisible(false);
setTimeout(() => {
Expand Down
17 changes: 0 additions & 17 deletions src/components/toast/ToastContainer.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,12 @@
import { Itoast } from "./useToast";
import Toast from "./Toast";
import { useEffect, useRef } from "react";

type ToastContainerType = {
toasts: Itoast[];
removeToast: (id: string) => void;
};

const ToastContainer = ({ toasts, removeToast }: ToastContainerType) => {
console.log(toasts);
const toastRefs = useRef<HTMLDivElement[]>([]);

// useEffect(() => {
// let yOffset = 0;
// // toastRefs.current?.style({})
// }, [toasts]);

return (
<div className="toast-container">
{toasts.map((toast, index) => (
Expand All @@ -28,14 +19,6 @@ const ToastContainer = ({ toasts, removeToast }: ToastContainerType) => {
key={toast.id}
>
<Toast
// ref={el => {
// if (!toastRefs.current[index]) {
// toastRefs.current[index] = React.createRef();
// }
// if (el) {
// toastRefs.current[index].current = el;
// }
// }}
toast={toast}
removeToast={removeToast}
/>
Expand Down
20 changes: 17 additions & 3 deletions src/components/toast/useToast.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,40 @@
import { useState } from 'react';

export type ToastActionType = 'success' | 'error' | 'info';
export type ToastActionType = 'success' | 'error' | 'info' | 'loading';
export type ToastPosition =
| 'top-left'
| 'top-center'
| 'top-right'
| 'bottom-left'
| 'bottom-center'
| 'bottom-right';

export interface Itoast {
id: string;
type: ToastActionType;
position: ToastPosition;
message: string;
duration: number;
}

const useToast = () => {
const [toasts, setToasts] = useState<Itoast[]>([]);

const addToast = (message: string, type: ToastActionType = 'info', duration = 3000) => {
const addToast = (
message: string,
type: ToastActionType = 'info',
position: ToastPosition = 'top-left',
duration = 3000) => {
const id = Math.random().toString(36).substr(2, 9);
setToasts([{ id, type, message, duration}, ...toasts]);
setToasts([{ id, type, position, message, duration}, ...toasts]);
};

const removeToast = (id: string) => {
setToasts(prev => prev.filter(toast => toast.id !== id));
};



return { toasts, addToast, removeToast };
};

Expand Down
12 changes: 12 additions & 0 deletions src/components/toastSample1/ToastA.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { TOAST_TYPE } from "./atoms";
import ToastContainer from "./ToastContainer";

const ToastA = () => {
return (
<ToastContainer toastType={TOAST_TYPE.toastA}>
<div className="Toast">Toast A</div>
</ToastContainer>
);
};

export default ToastA;
59 changes: 59 additions & 0 deletions src/components/toastSample1/ToastContainer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { ReactNode, useEffect } from "react";
import { createPortal } from "react-dom";
import { AnimatePresence, motion } from "framer-motion";
import { ToastType } from "./atoms";
import useToastList from "./useToastList";

interface Props {
children: ReactNode;
toastType: ToastType;
config?: {
duration: number;
};
}

const ToastContainer = ({children,toastType,config = { duration: 3000 }}: Props) => {
const ref = document.querySelector("#toast-root");
const { duration } = config;
const { toastList, close } = useToastList();

const isShown = toastList.includes(toastType);

useEffect(() => {
if (!isShown) {
return undefined;
}

const timer = setTimeout(() => {
close(toastType);
}, duration);

return () => {
clearTimeout(timer);
};
}, [close, duration, isShown, toastType]);

if (!ref) {
return null;
}

return createPortal(
<AnimatePresence>
{isShown && (
<motion.div
key={toastType}
layout
initial={{ opacity: 0.5, y: 24 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0.5, y: 12 }}
transition={{ ease: "easeOut" }}
>
{children}
</motion.div>
)}
</AnimatePresence>,
ref
);
};

export default ToastContainer;
14 changes: 14 additions & 0 deletions src/components/toastSample1/atoms.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { atom } from "recoil";

export const TOAST_TYPE = {
toastA: "toastA",
toastB: "toastB",
toastC: "toastC",
} as const;

export type ToastType = keyof typeof TOAST_TYPE;

export const ToastList = atom<ToastType[]>({
key: "ToastList",
default: [],
});
34 changes: 34 additions & 0 deletions src/components/toastSample1/useToastList.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { useCallback } from "react";
import { useRecoilState } from "recoil";
import { ToastList, ToastType } from "./atoms";

const useToastList = () => {
const [toastList, setToastList] = useRecoilState<ToastType[]>(ToastList);

const show = useCallback(
(toastType: ToastType) => {
setToastList((prev) => prev.concat(toastType));
},
[setToastList]
);

const close = useCallback(
(toastType: ToastType) => {
setToastList((prev) => prev.filter((t) => t !== toastType));
},
[setToastList]
);

const closeAll = () => {
setToastList([]);
};

return {
toastList,
show,
close,
closeAll,
};
};

export default useToastList;
35 changes: 34 additions & 1 deletion yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1304,6 +1304,18 @@
resolved "https://registry.yarnpkg.com/@emotion/hash/-/hash-0.9.1.tgz#4ffb0055f7ef676ebc3a5a91fb621393294e2f43"
integrity sha512-gJB6HLm5rYwSLI6PQa+X1t5CFGrv1J1TWG+sOyMCeKz2ojaj6Fnl/rZEspogG+cvqbt4AE/2eIyD2QfLKTBNlQ==

"@emotion/is-prop-valid@^0.8.2":
version "0.8.8"
resolved "https://registry.yarnpkg.com/@emotion/is-prop-valid/-/is-prop-valid-0.8.8.tgz#db28b1c4368a259b60a97311d6a952d4fd01ac1a"
integrity sha512-u5WtneEAr5IDG2Wv65yhunPSMLIpuKsbuOktRojfrEiEvRyC85LgPMZI63cr7NUqT8ZIGdSVg8ZKGxIug4lXcA==
dependencies:
"@emotion/memoize" "0.7.4"

"@emotion/[email protected]":
version "0.7.4"
resolved "https://registry.yarnpkg.com/@emotion/memoize/-/memoize-0.7.4.tgz#19bf0f5af19149111c40d98bb0cf82119f5d9eeb"
integrity sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw==

"@emotion/memoize@^0.8.1":
version "0.8.1"
resolved "https://registry.yarnpkg.com/@emotion/memoize/-/memoize-0.8.1.tgz#c1ddb040429c6d21d38cc945fe75c818cfb68e17"
Expand Down Expand Up @@ -4830,6 +4842,15 @@ fraction.js@^4.3.6:
resolved "https://registry.yarnpkg.com/fraction.js/-/fraction.js-4.3.7.tgz#06ca0085157e42fda7f9e726e79fefc4068840f7"
integrity sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==

framer-motion@^10.16.16:
version "10.16.16"
resolved "https://registry.yarnpkg.com/framer-motion/-/framer-motion-10.16.16.tgz#a10a03e1190a717109163cfff212a84c8ad11b0c"
integrity sha512-je6j91rd7NmUX7L1XHouwJ4v3R+SO4umso2LUcgOct3rHZ0PajZ80ETYZTajzEXEl9DlKyzjyt4AvGQ+lrebOw==
dependencies:
tslib "^2.4.0"
optionalDependencies:
"@emotion/is-prop-valid" "^0.8.2"

[email protected]:
version "0.5.2"
resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7"
Expand Down Expand Up @@ -5046,6 +5067,11 @@ gzip-size@^6.0.0:
dependencies:
duplexer "^0.1.2"

[email protected]:
version "1.0.2"
resolved "https://registry.yarnpkg.com/hamt_plus/-/hamt_plus-1.0.2.tgz#e21c252968c7e33b20f6a1b094cd85787a265601"
integrity sha512-t2JXKaehnMb9paaYA7J0BX8QQAY8lwfQ9Gjf4pg/mk4krt+cmwmU652HOoWonf+7+EQV97ARPMhhVgU1ra2GhA==

handle-thing@^2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/handle-thing/-/handle-thing-2.0.1.tgz#857f79ce359580c340d43081cc648970d0bb234e"
Expand Down Expand Up @@ -8011,6 +8037,13 @@ readdirp@~3.6.0:
dependencies:
picomatch "^2.2.1"

recoil@^0.7.7:
version "0.7.7"
resolved "https://registry.yarnpkg.com/recoil/-/recoil-0.7.7.tgz#c5f2c843224384c9c09e4a62c060fb4c1454dc8e"
integrity sha512-8Og5KPQW9LwC577Vc7Ug2P0vQshkv1y3zG3tSSkWMqkWSwHmE+by06L8JtnGocjW6gcCvfwB3YtrJG6/tWivNQ==
dependencies:
hamt_plus "1.0.2"

recursive-readdir@^2.2.2:
version "2.2.3"
resolved "https://registry.yarnpkg.com/recursive-readdir/-/recursive-readdir-2.2.3.tgz#e726f328c0d69153bcabd5c322d3195252379372"
Expand Down Expand Up @@ -9053,7 +9086,7 @@ tslib@^1.8.1:
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00"
integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==

tslib@^2.0.3:
tslib@^2.0.3, tslib@^2.4.0:
version "2.6.2"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae"
integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==
Expand Down

0 comments on commit 8400244

Please sign in to comment.