diff --git a/package.json b/package.json index e4dfe90..33ea53c 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "@emotion/styled": "^11.10.6", "@mui/material": "^5.11.14", "@mui/styled-engine-sc": "^5.11.11", + "@radix-ui/react-dialog": "^1.0.3", "@radix-ui/react-select": "^1.2.1", "@radix-ui/react-tooltip": "^1.0.5", "@vercel/analytics": "^0.1.11", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c4a037e..86131a6 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -13,6 +13,9 @@ dependencies: '@mui/styled-engine-sc': specifier: ^5.11.11 version: 5.11.11(styled-components@5.3.9) + '@radix-ui/react-dialog': + specifier: ^1.0.3 + version: 1.0.3(@types/react@18.0.28)(react-dom@18.2.0)(react@18.2.0) '@radix-ui/react-select': specifier: ^1.2.1 version: 1.2.1(@types/react@18.0.28)(react-dom@18.2.0)(react@18.2.0) @@ -876,6 +879,33 @@ packages: react: 18.2.0 dev: false + /@radix-ui/react-dialog@1.0.3(@types/react@18.0.28)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-owNhq36kNPqC2/a+zJRioPg6HHnTn5B/sh/NjTY8r4W9g1L5VJlrzZIVcBr7R9Mg8iLjVmh6MGgMlfoVf/WO/A==} + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + dependencies: + '@babel/runtime': 7.21.0 + '@radix-ui/primitive': 1.0.0 + '@radix-ui/react-compose-refs': 1.0.0(react@18.2.0) + '@radix-ui/react-context': 1.0.0(react@18.2.0) + '@radix-ui/react-dismissable-layer': 1.0.3(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-focus-guards': 1.0.0(react@18.2.0) + '@radix-ui/react-focus-scope': 1.0.2(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-id': 1.0.0(react@18.2.0) + '@radix-ui/react-portal': 1.0.2(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-presence': 1.0.0(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-primitive': 1.0.2(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-slot': 1.0.1(react@18.2.0) + '@radix-ui/react-use-controllable-state': 1.0.0(react@18.2.0) + aria-hidden: 1.2.3 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + react-remove-scroll: 2.5.5(@types/react@18.0.28)(react@18.2.0) + transitivePeerDependencies: + - '@types/react' + dev: false + /@radix-ui/react-direction@1.0.0(react@18.2.0): resolution: {integrity: sha512-2HV05lGUgYcA6xgLQ4BKPDmtL+QbIZYH5fCOTAOOcJ5O0QbWS3i9lKaurLzliYUDhORI2Qr3pyjhJh44lKA3rQ==} peerDependencies: diff --git a/src/components/ActionConfirmModal.tsx b/src/components/ActionConfirmModal.tsx index b6d1e1e..c328c7f 100644 --- a/src/components/ActionConfirmModal.tsx +++ b/src/components/ActionConfirmModal.tsx @@ -1,5 +1,5 @@ import { useTranslation } from "react-i18next"; -import Icon from "./Icon"; +import Dialog from "./kit/Dialog"; export interface ActionConfirmModalProps { title: string; @@ -14,31 +14,25 @@ const ActionConfirmModal = (props: ActionConfirmModalProps) => { const { t } = useTranslation(); return ( -
-
-

{title}

- -
-

{content}

-
-
- - -
+ +
+

{content}

-
+
+ + +
+ ); }; diff --git a/src/components/ClearDataButton.tsx b/src/components/ClearDataButton.tsx index d81d3b7..30ba78f 100644 --- a/src/components/ClearDataButton.tsx +++ b/src/components/ClearDataButton.tsx @@ -1,5 +1,4 @@ import { useState } from "react"; -import { createPortal } from "react-dom"; import { useTranslation } from "react-i18next"; import ClearDataConfirmModal from "./ClearDataConfirmModal"; @@ -13,8 +12,7 @@ const ClearDataButton = () => { {t("common.clear")} - {showClearDataConfirmModal && - createPortal( setShowClearDataConfirmModal(false)} />, document.body)} + {showClearDataConfirmModal && setShowClearDataConfirmModal(false)} />} ); }; diff --git a/src/components/ClearDataConfirmModal.tsx b/src/components/ClearDataConfirmModal.tsx index c885f6f..8bfb7fe 100644 --- a/src/components/ClearDataConfirmModal.tsx +++ b/src/components/ClearDataConfirmModal.tsx @@ -1,6 +1,6 @@ import { toast } from "react-hot-toast"; import { useTranslation } from "react-i18next"; -import Icon from "./Icon"; +import Dialog from "./kit/Dialog"; interface Props { close: () => void; @@ -20,25 +20,19 @@ const ClearDataConfirmModal = (props: Props) => { }; return ( -
-
-

Clear all data

- -
-

SQL Chat saves all your data in your local browser. Are you sure to clear all of them?

-
-
- - -
+ +
+

SQL Chat saves all your data in your local browser. Are you sure to clear all of them?

-
+
+ + +
+ ); }; diff --git a/src/components/ConnectionSidebar.tsx b/src/components/ConnectionSidebar.tsx index 04a8042..69c5f6a 100644 --- a/src/components/ConnectionSidebar.tsx +++ b/src/components/ConnectionSidebar.tsx @@ -1,7 +1,6 @@ import { Drawer } from "@mui/material"; import { head } from "lodash-es"; import { useEffect, useState } from "react"; -import { createPortal } from "react-dom"; import { useTranslation } from "react-i18next"; import { useConversationStore, useConnectionStore, useLayoutStore, ResponsiveWidth } from "@/store"; import { Conversation, Connection } from "@/types"; @@ -290,26 +289,18 @@ const ConnectionSidebar = () => {
- {createPortal( - toggleCreateConnectionModal(false)} - />, - document.body + {state.showCreateConnectionModal && ( + toggleCreateConnectionModal(false)} /> )} - {createPortal( toggleSettingModal(false)} />, document.body)} + {state.showSettingModal && toggleSettingModal(false)} />} - {state.showEditConversationTitleModal && - editConversationTitleModalContext && - createPortal( - toggleEditConversationTitleModal(false)} - conversation={editConversationTitleModalContext} - />, - document.body - )} + {state.showEditConversationTitleModal && editConversationTitleModalContext && ( + toggleEditConversationTitleModal(false)} + conversation={editConversationTitleModalContext} + /> + )} ); }; diff --git a/src/components/CreateConnectionModal.tsx b/src/components/CreateConnectionModal.tsx index 14aeda6..f03573c 100644 --- a/src/components/CreateConnectionModal.tsx +++ b/src/components/CreateConnectionModal.tsx @@ -1,18 +1,17 @@ import { cloneDeep, head } from "lodash-es"; import { ChangeEvent, useEffect, useState } from "react"; -import { createPortal } from "react-dom"; import { toast } from "react-hot-toast"; import TextareaAutosize from "react-textarea-autosize"; import { useConnectionStore } from "@/store"; import { Connection, Engine, ResponseObject } from "@/types"; import Select from "./kit/Select"; +import TextField from "./kit/TextField"; +import Dialog from "./kit/Dialog"; import Icon from "./Icon"; import DataStorageBanner from "./DataStorageBanner"; import ActionConfirmModal from "./ActionConfirmModal"; -import TextField from "./kit/TextField"; interface Props { - show: boolean; connection?: Connection; close: () => void; } @@ -47,7 +46,7 @@ const defaultConnection: Connection = { }; const CreateConnectionModal = (props: Props) => { - const { show, connection: editConnection, close } = props; + const { connection: editConnection, close } = props; const connectionStore = useConnectionStore(); const [connection, setConnection] = useState(defaultConnection); const [showDeleteConnectionModal, setShowDeleteConnectionModal] = useState(false); @@ -59,23 +58,21 @@ const CreateConnectionModal = (props: Props) => { const allowSave = connection.host !== "" && connection.username !== ""; useEffect(() => { - if (show) { - const connection = isEditing ? editConnection : defaultConnection; - setConnection(connection); - if (connection.ssl) { - if (connection.ssl.ca && connection.ssl.cert && connection.ssl.key) { - setSSLType("full"); - } else { - setSSLType("ca-only"); - } + const connection = isEditing ? editConnection : defaultConnection; + setConnection(connection); + if (connection.ssl) { + if (connection.ssl.ca && connection.ssl.cert && connection.ssl.key) { + setSSLType("full"); } else { - setSSLType("none"); + setSSLType("ca-only"); } - setSelectedSSLField("ca"); - setIsRequesting(false); - setShowDeleteConnectionModal(false); + } else { + setSSLType("none"); } - }, [show]); + setSelectedSSLField("ca"); + setIsRequesting(false); + setShowDeleteConnectionModal(false); + }, []); useEffect(() => { let ssl = undefined; @@ -209,164 +206,156 @@ const CreateConnectionModal = (props: Props) => { return ( <> -
-
-

{isEditing ? "Edit Connection" : "Create Connection"}

- -
- -
- - setSSLType(e.target.value as SSLType)} - /> - {option.label} - - ))} -
- {sslType !== "none" && ( - <> -
- setSelectedSSLField("ca")} - > - CA Certificate - - {sslType === "full" && ( - <> - setSelectedSSLField("key")} - > - Client Key - - setSelectedSSLField("cert")} - > - Client Certificate - - - )} -
-
- -
- Input or - -
-
- - )} -
+ +
+ +
+ + setSSLType(e.target.value as SSLType)} + /> + {option.label} + + ))}
+ {sslType !== "none" && ( + <> +
+ setSelectedSSLField("ca")} + > + CA Certificate + + {sslType === "full" && ( + <> + setSelectedSSLField("key")} + > + Client Key + + setSelectedSSLField("cert")} + > + Client Certificate + + + )} +
+
+ +
+ Input or + +
+
+ + )}
-
+
+
+ {isEditing && ( + + )} +
+
+ + +
+
+ - {showDeleteConnectionModal && - createPortal( - setShowDeleteConnectionModal(false)} - confirm={() => handleDeleteConnection()} - />, - document.body - )} + {showDeleteConnectionModal && ( + setShowDeleteConnectionModal(false)} + confirm={() => handleDeleteConnection()} + /> + )} ); }; diff --git a/src/components/EditConversationTitleModal.tsx b/src/components/EditConversationTitleModal.tsx index 10889a7..d142aeb 100644 --- a/src/components/EditConversationTitleModal.tsx +++ b/src/components/EditConversationTitleModal.tsx @@ -3,8 +3,8 @@ import { toast } from "react-hot-toast"; import { useTranslation } from "react-i18next"; import { useConversationStore } from "@/store"; import { Conversation } from "@/types"; -import Icon from "./Icon"; import TextField from "./kit/TextField"; +import Dialog from "./kit/Dialog"; interface Props { conversation: Conversation; @@ -32,25 +32,19 @@ const EditConversationTitleModal = (props: Props) => { }; return ( -
-
-

{t("conversation.edit-title")}

- -
- setTitle(value)} /> -
-
- - -
+ +
+ setTitle(value)} />
-
+
+ + +
+ ); }; diff --git a/src/components/SettingModal.tsx b/src/components/SettingModal.tsx index d5a41f2..dd66139 100644 --- a/src/components/SettingModal.tsx +++ b/src/components/SettingModal.tsx @@ -1,4 +1,5 @@ import { useTranslation } from "react-i18next"; +import Dialog from "./kit/Dialog"; import Icon from "./Icon"; import WeChatQRCodeView from "./WeChatQRCodeView"; import ClearDataButton from "./ClearDataButton"; @@ -6,54 +7,47 @@ import LocaleSelector from "./LocaleSelector"; import OpenAIApiConfigView from "./OpenAIApiConfigView"; interface Props { - show: boolean; close: () => void; } const SettingModal = (props: Props) => { - const { show, close } = props; + const { close } = props; const { t } = useTranslation(); return ( -
-
-

{t("setting.self")}

- -
-
- - - {t("social.join-discord-channel")} - - + +
+ + +

{t("setting.basic.self")}

+
+
+ {t("setting.basic.language")} +
+
-

{t("setting.basic.self")}

-
-
- {t("setting.basic.language")} - -
-
+ - - -

{t("setting.data.self")}

-
-
- {t("setting.data.clear-all-data")} - -
+

{t("setting.data.self")}

+
+
+ {t("setting.data.clear-all-data")} +
-
+
); }; diff --git a/src/components/kit/Dialog.tsx b/src/components/kit/Dialog.tsx new file mode 100644 index 0000000..115be35 --- /dev/null +++ b/src/components/kit/Dialog.tsx @@ -0,0 +1,34 @@ +import * as DialogUI from "@radix-ui/react-dialog"; +import React, { ReactNode } from "react"; +import Icon from "../Icon"; + +interface Props { + title: string; + children: ReactNode; + onClose: () => void; +} + +const Dialog = (props: Props) => { + const { children, title, onClose } = props; + + return ( + + + + + {title} + + + +
{children}
+
+
+
+ ); +}; + +export default Dialog;