You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

118 lines
3.8 KiB

  1. "use client";
  2. import React, { FC, Fragment, ReactNode, useEffect, useState } from "react";
  3. import { Dialog, Transition } from "@headlessui/react";
  4. import ButtonClose from "@/shared/ButtonClose";
  5. import Button from "@/shared/Button";
  6. export interface NcModalProps {
  7. renderContent: () => ReactNode;
  8. renderTrigger?: (openModal: Function) => ReactNode;
  9. contentExtraClass?: string;
  10. contentPaddingClass?: string;
  11. triggerText?: ReactNode;
  12. modalTitle?: ReactNode;
  13. isOpenProp?: boolean;
  14. onCloseModal?: () => void;
  15. }
  16. const NcModal: FC<NcModalProps> = ({
  17. renderTrigger,
  18. renderContent,
  19. contentExtraClass = "max-w-screen-xl",
  20. contentPaddingClass = "py-4 px-6 md:py-5",
  21. triggerText = "Open Modal",
  22. modalTitle = "Modal title",
  23. isOpenProp,
  24. onCloseModal,
  25. }) => {
  26. let [isOpen, setIsOpen] = useState(!!isOpenProp);
  27. function closeModal() {
  28. if (typeof isOpenProp !== "boolean") {
  29. setIsOpen(false);
  30. }
  31. onCloseModal && onCloseModal();
  32. }
  33. function openModal() {
  34. if (typeof isOpenProp !== "boolean") {
  35. setIsOpen(true);
  36. }
  37. }
  38. useEffect(() => {
  39. setIsOpen(!!isOpenProp);
  40. }, [isOpenProp]);
  41. return (
  42. <div className="nc-NcModal">
  43. {renderTrigger ? (
  44. renderTrigger(openModal)
  45. ) : (
  46. <Button onClick={openModal}> {triggerText} </Button>
  47. )}
  48. <Transition appear show={isOpen} as={Fragment}>
  49. <Dialog
  50. as="div"
  51. className="fixed inset-0 z-50 overflow-y-auto"
  52. onClose={closeModal}
  53. >
  54. <div className="min-h-screen px-1 text-center md:px-4">
  55. <Transition.Child
  56. as={Fragment}
  57. enter="ease-out duration-75"
  58. enterFrom="opacity-0"
  59. enterTo="opacity-100"
  60. leave="ease-in duration-75"
  61. leaveFrom="opacity-100"
  62. leaveTo="opacity-0"
  63. >
  64. <Dialog.Overlay className="fixed inset-0 bg-neutral-900 bg-opacity-50 dark:bg-opacity-80" />
  65. </Transition.Child>
  66. {/* This element is to trick the browser into centering the modal contents. */}
  67. <span
  68. className="inline-block h-screen align-middle"
  69. aria-hidden="true"
  70. >
  71. &#8203;
  72. </span>
  73. <Transition.Child
  74. as={Fragment}
  75. enter="ease-out duration-75"
  76. enterFrom="opacity-0 scale-95"
  77. enterTo="opacity-100 scale-100"
  78. leave="ease-in duration-75"
  79. leaveFrom="opacity-100 scale-100"
  80. leaveTo="opacity-0 scale-95"
  81. >
  82. <div
  83. className={`inline-block w-full my-5 overflow-hidden text-left align-middle transition-all transform bg-white border border-black border-opacity-5 shadow-xl rounded-2xl sm:my-8 dark:bg-neutral-800 dark:border-neutral-700 text-neutral-900 dark:text-neutral-300 ${contentExtraClass}`}
  84. >
  85. <div className="py-4 px-6 text-center relative border-b border-neutral-100 dark:border-neutral-700 md:py-5">
  86. <ButtonClose
  87. onClick={closeModal}
  88. className="absolute left-2 top-1/2 transform -translate-y-1/2 sm:left-4"
  89. />
  90. {modalTitle && (
  91. <Dialog.Title
  92. as="h3"
  93. className="text-base font-semibold text-neutral-900 lg:text-xl dark:text-neutral-200 mx-10"
  94. >
  95. {modalTitle}
  96. </Dialog.Title>
  97. )}
  98. </div>
  99. <div className={contentPaddingClass}>{renderContent()}</div>
  100. </div>
  101. </Transition.Child>
  102. </div>
  103. </Dialog>
  104. </Transition>
  105. </div>
  106. );
  107. };
  108. export default NcModal;