ui-lib.tsx 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. import styles from "./ui-lib.module.scss";
  2. import LoadingIcon from "../icons/three-dots.svg";
  3. import CloseIcon from "../icons/close.svg";
  4. import { createRoot } from "react-dom/client";
  5. import React from "react";
  6. export function Popover(props: {
  7. children: JSX.Element;
  8. content: JSX.Element;
  9. open?: boolean;
  10. onClose?: () => void;
  11. }) {
  12. return (
  13. <div className={styles.popover}>
  14. {props.children}
  15. {props.open && (
  16. <div className={styles["popover-content"]}>
  17. <div className={styles["popover-mask"]} onClick={props.onClose}></div>
  18. {props.content}
  19. </div>
  20. )}
  21. </div>
  22. );
  23. }
  24. export function Card(props: { children: JSX.Element[]; className?: string }) {
  25. return (
  26. <div className={styles.card + " " + props.className}>{props.children}</div>
  27. );
  28. }
  29. export function ListItem(props: { children: JSX.Element[] }) {
  30. if (props.children.length > 2) {
  31. throw Error("Only Support Two Children");
  32. }
  33. return <div className={styles["list-item"]}>{props.children}</div>;
  34. }
  35. export function List(props: { children: JSX.Element[] | JSX.Element }) {
  36. return <div className={styles.list}>{props.children}</div>;
  37. }
  38. export function Loading() {
  39. return (
  40. <div
  41. style={{
  42. height: "100vh",
  43. width: "100vw",
  44. display: "flex",
  45. alignItems: "center",
  46. justifyContent: "center",
  47. }}
  48. >
  49. <LoadingIcon />
  50. </div>
  51. );
  52. }
  53. interface ModalProps {
  54. title: string;
  55. children?: JSX.Element;
  56. actions?: JSX.Element[];
  57. onClose?: () => void;
  58. }
  59. export function Modal(props: ModalProps) {
  60. return (
  61. <div className={styles["modal-container"]}>
  62. <div className={styles["modal-header"]}>
  63. <div className={styles["modal-title"]}>{props.title}</div>
  64. <div className={styles["modal-close-btn"]} onClick={props.onClose}>
  65. <CloseIcon />
  66. </div>
  67. </div>
  68. <div className={styles["modal-content"]}>{props.children}</div>
  69. <div className={styles["modal-footer"]}>
  70. <div className={styles["modal-actions"]}>
  71. {props.actions?.map((action, i) => (
  72. <div key={i} className={styles["modal-action"]}>
  73. {action}
  74. </div>
  75. ))}
  76. </div>
  77. </div>
  78. </div>
  79. );
  80. }
  81. export function showModal(props: ModalProps) {
  82. const div = document.createElement("div");
  83. div.className = "modal-mask";
  84. document.body.appendChild(div);
  85. const root = createRoot(div);
  86. const closeModal = () => {
  87. props.onClose?.();
  88. root.unmount();
  89. div.remove();
  90. };
  91. div.onclick = (e) => {
  92. if (e.target === div) {
  93. closeModal();
  94. }
  95. };
  96. root.render(<Modal {...props} onClose={closeModal}></Modal>);
  97. }
  98. export type ToastProps = { content: string };
  99. export function Toast(props: ToastProps) {
  100. return (
  101. <div className={styles["toast-container"]}>
  102. <div className={styles["toast-content"]}>{props.content}</div>
  103. </div>
  104. );
  105. }
  106. export function showToast(content: string, delay = 3000) {
  107. const div = document.createElement("div");
  108. div.className = styles.show;
  109. document.body.appendChild(div);
  110. const root = createRoot(div);
  111. const close = () => {
  112. div.classList.add(styles.hide);
  113. setTimeout(() => {
  114. root.unmount();
  115. div.remove();
  116. }, 300);
  117. };
  118. setTimeout(() => {
  119. close();
  120. }, delay);
  121. root.render(<Toast content={content} />);
  122. }
  123. export type InputProps = React.HTMLProps<HTMLTextAreaElement> & {
  124. autoHeight?: boolean;
  125. rows?: number;
  126. };
  127. export function Input(props: InputProps) {
  128. return (
  129. <textarea
  130. {...props}
  131. className={`${styles["input"]} ${props.className}`}
  132. ></textarea>
  133. );
  134. }