-4

Tạo modal với Nextjs 14

  1. 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>
  );
};

  1. 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();
};

  1. 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


All Rights Reserved

Viblo
Let's register a Viblo Account to get more interesting posts.