Modal.tsx 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105
  1. import { JSX } from "preact";
  2. import { useEffect, useState } from "preact/hooks";
  3. import Button from "../components/form/Button.tsx";
  4. interface ModalAction {
  5. text: string;
  6. onClick?: (text: string) => void | Promise<void>;
  7. }
  8. interface ModalGlobalHook {
  9. show: (
  10. title: string,
  11. content: string | JSX.Element,
  12. actions: ModalAction[],
  13. ) => void;
  14. hide: () => void;
  15. }
  16. declare global {
  17. var $modal: ModalGlobalHook | undefined;
  18. }
  19. export default function Modal() {
  20. const [visible, setVisible] = useState(false);
  21. const [title, setTitle] = useState("");
  22. const [content, setContent] = useState<string | JSX.Element>("");
  23. const [actions, setActions] = useState<ModalAction[]>([]);
  24. const showModal = (
  25. newTitle: string,
  26. newContent: string | JSX.Element,
  27. newActions: ModalAction[],
  28. ) => {
  29. setTitle(newTitle || "");
  30. setContent(newContent || "");
  31. setActions(newActions || []);
  32. setVisible(true);
  33. };
  34. const hideModal = () => {
  35. setVisible(false);
  36. };
  37. useEffect(() => {
  38. globalThis.$modal = {
  39. show: (
  40. title: string,
  41. content: string | JSX.Element,
  42. actions: ModalAction[],
  43. ) => showModal(title, content, actions),
  44. hide: () => hideModal(),
  45. };
  46. return () => {
  47. delete globalThis.$modal;
  48. };
  49. }, []);
  50. return (
  51. <>
  52. <div
  53. className={`fixed inset-0 bg-black/60 z-[8] flex items-center justify-center ${
  54. !visible ? "hidden" : ""
  55. }`}
  56. >
  57. <div className="bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg w-[500px] max-w-[90%] max-h-[60%] relative text-base cursor-pointer">
  58. <i
  59. className="bi bi-x absolute right-4 top-3 text-2xl"
  60. onClick={() => {
  61. hideModal();
  62. }}
  63. />
  64. {title
  65. ? (
  66. <div className="p-4 border-b border-gray-200 dark:border-gray-700 font-medium">
  67. {title}
  68. </div>
  69. )
  70. : null}
  71. <div className="p-4">{content}</div>
  72. {actions.length > 0
  73. ? (
  74. <div className="flex justify-end border-t border-gray-200 dark:border-gray-700 p-4">
  75. {actions.map((action, index) => (
  76. <Button
  77. type="button"
  78. key={index}
  79. className="ml-2 first:ml-0"
  80. onClick={() => {
  81. action.onClick
  82. ? action.onClick(action.text)
  83. : hideModal();
  84. }}
  85. >
  86. {action.text}
  87. </Button>
  88. ))}
  89. </div>
  90. )
  91. : null}
  92. </div>
  93. </div>
  94. </>
  95. );
  96. }