- Tạo context API: xử lý kèm logic:
"use client";
import { $ModalKeys } from "@/collects/modals";
import { Dispatch, PropsWithChildren, SetStateAction, createContext, useState,
} from "react"; type $ToggleFn = { key: $ModalKeys; data?: any }; type $Show = { [T in $ModalKeys]: { open: boolean; data?: any; };
}; type $_Modal = { onToggle: (props: $ToggleFn) => void; show: $Show; setShow: Dispatch<SetStateAction<$Show>>;
}; export let _$modal = {} as $_Modal; const useLogic = () => { const [show, setShow] = useState({} as $Show); const onToggle = (props: $ToggleFn) => { const { key, data } = props; setShow((prev) => ({ ...prev, [key]: { ...prev[key], open: !prev[key]?.open, data }, })); }; _$modal = { onToggle, show, setShow, }; return { onToggle, show, setShow };
}; type ValueCtx = ReturnType<typeof useLogic>; export const ModalCtx = createContext({} as ValueCtx); export const ModalProvider = ({ children }: PropsWithChildren) => { const valueCtx = useLogic(); return ( <ModalCtx.Provider value={{ ...valueCtx }}> <>{children}</> </ModalCtx.Provider> );
};
- Tạo Component Modal :
"use client";
import cx from "classnames";
import React, { useContext } from "react";
import ReactDOM from "react-dom";
import styles from "./styles.module.scss";
import { ModalCtx } from "./context"; export type $Modal = { open?: boolean; onToggle: () => void; data?: any;
}; type $Props = { set?: { open?: $Modal["open"]; isCloseOutside?: boolean; clsOverlay?: string; clsModal?: string; content?: (props: $Modal) => JSX.Element; header?: (props: $Modal) => JSX.Element; }; action: { onToggle: () => void; };
}; export default function Modal(props: $Props) { const { set, action } = props; const {} = useContext(ModalCtx); if (!set?.open) { return null; } return ReactDOM.createPortal( <div className={cx(styles.overlay, set?.clsOverlay)} onClick={set?.isCloseOutside ? action?.onToggle : undefined} > <div className={cx(styles.modal, set?.clsModal)} onClick={onStopPrpg}> {set?.header?.({ onToggle: action.onToggle })} {set.content?.({ onToggle: action.onToggle })} </div> </div>, document.body );
} const onStopPrpg = (event: React.MouseEvent<HTMLDivElement, MouseEvent>) => { event.stopPropagation(); event.preventDefault();
};
- Tạo SCSS : file styles.module.scss
.overlay { position: fixed; width: 100vw; height: 100vh; top: 0; display: flex; align-items: center; justify-content: center; .modal { background-color: lightblue; display: flex; flex-direction: column; width: fit-content; border-radius: 0.5rem; padding: 1rem; box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1); .header { display: flex; } }
}
Đơn giản đúng hông, có thể tuỳ chỉnh styles tuỳ thích. Phần tiếp theo là Dropbox