feat: implement Dialog kit component

This commit is contained in:
Steven
2023-04-11 23:07:18 +08:00
parent 42a4194d10
commit bcd7bece81
10 changed files with 308 additions and 289 deletions

View File

@ -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",

30
pnpm-lock.yaml generated
View File

@ -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:

View File

@ -1,5 +1,5 @@
import { useTranslation } from "react-i18next";
import Icon from "./Icon";
import Dialog from "./kit/Dialog";
export interface ActionConfirmModalProps {
title: string;
@ -14,16 +14,11 @@ const ActionConfirmModal = (props: ActionConfirmModalProps) => {
const { t } = useTranslation();
return (
<div className="modal modal-middle modal-open">
<div className="modal-box relative">
<h3 className="font-bold text-lg">{title}</h3>
<button className="btn btn-sm btn-circle absolute right-4 top-4" onClick={close}>
<Icon.IoMdClose className="w-5 h-auto" />
</button>
<div className="w-full flex flex-col justify-start items-start space-y-3 pt-4">
<Dialog title={title} onClose={close}>
<div className="w-full flex flex-col justify-start items-start mt-2">
<p className="text-gray-500">{content}</p>
</div>
<div className="modal-action">
<div className="w-full flex flex-row justify-end items-center mt-4 space-x-2">
<button className="btn btn-outline" onClick={close}>
{t("common.close")}
</button>
@ -37,8 +32,7 @@ const ActionConfirmModal = (props: ActionConfirmModalProps) => {
{t("common.confirm")}
</button>
</div>
</div>
</div>
</Dialog>
);
};

View File

@ -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")}
</button>
{showClearDataConfirmModal &&
createPortal(<ClearDataConfirmModal close={() => setShowClearDataConfirmModal(false)} />, document.body)}
{showClearDataConfirmModal && <ClearDataConfirmModal close={() => setShowClearDataConfirmModal(false)} />}
</>
);
};

View File

@ -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,16 +20,11 @@ const ClearDataConfirmModal = (props: Props) => {
};
return (
<div className="modal modal-middle modal-open">
<div className="modal-box relative">
<h3 className="font-bold text-lg">Clear all data</h3>
<button className="btn btn-sm btn-circle absolute right-4 top-4" onClick={close}>
<Icon.IoMdClose className="w-5 h-auto" />
</button>
<div className="w-full flex flex-col justify-start items-start space-y-3 pt-4">
<Dialog title="Clear all data" onClose={close}>
<div className="w-full flex flex-col justify-start items-start mt-2">
<p className="text-gray-500">SQL Chat saves all your data in your local browser. Are you sure to clear all of them?</p>
</div>
<div className="modal-action">
<div className="w-full flex flex-row justify-end items-center mt-4 space-x-2">
<button className="btn btn-outline" onClick={close}>
{t("common.close")}
</button>
@ -37,8 +32,7 @@ const ClearDataConfirmModal = (props: Props) => {
{t("common.clear")}
</button>
</div>
</div>
</div>
</Dialog>
);
};

View File

@ -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,25 +289,17 @@ const ConnectionSidebar = () => {
</div>
</Drawer>
{createPortal(
<CreateConnectionModal
show={state.showCreateConnectionModal}
connection={editConnectionModalContext}
close={() => toggleCreateConnectionModal(false)}
/>,
document.body
{state.showCreateConnectionModal && (
<CreateConnectionModal connection={editConnectionModalContext} close={() => toggleCreateConnectionModal(false)} />
)}
{createPortal(<SettingModal show={state.showSettingModal} close={() => toggleSettingModal(false)} />, document.body)}
{state.showSettingModal && <SettingModal close={() => toggleSettingModal(false)} />}
{state.showEditConversationTitleModal &&
editConversationTitleModalContext &&
createPortal(
{state.showEditConversationTitleModal && editConversationTitleModalContext && (
<EditConversationTitleModal
close={() => toggleEditConversationTitleModal(false)}
conversation={editConversationTitleModalContext}
/>,
document.body
/>
)}
</>
);

View File

@ -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<Connection>(defaultConnection);
const [showDeleteConnectionModal, setShowDeleteConnectionModal] = useState(false);
@ -59,7 +58,6 @@ const CreateConnectionModal = (props: Props) => {
const allowSave = connection.host !== "" && connection.username !== "";
useEffect(() => {
if (show) {
const connection = isEditing ? editConnection : defaultConnection;
setConnection(connection);
if (connection.ssl) {
@ -74,8 +72,7 @@ const CreateConnectionModal = (props: Props) => {
setSelectedSSLField("ca");
setIsRequesting(false);
setShowDeleteConnectionModal(false);
}
}, [show]);
}, []);
useEffect(() => {
let ssl = undefined;
@ -209,12 +206,7 @@ const CreateConnectionModal = (props: Props) => {
return (
<>
<div className={`modal modal-middle ${show && "modal-open"}`}>
<div className="modal-box relative">
<h3 className="font-bold text-lg">{isEditing ? "Edit Connection" : "Create Connection"}</h3>
<button className="btn btn-sm btn-circle absolute right-4 top-4" onClick={close}>
<Icon.IoMdClose className="w-5 h-auto" />
</button>
<Dialog title={isEditing ? "Edit Connection" : "Create Connection"} onClose={close}>
<div className="w-full flex flex-col justify-start items-start space-y-3 pt-4">
<DataStorageBanner className="rounded-lg bg-white border py-2 !justify-start" alwaysShow={true} />
<div className="w-full flex flex-col">
@ -353,19 +345,16 @@ const CreateConnectionModal = (props: Props) => {
</button>
</div>
</div>
</div>
</div>
</Dialog>
{showDeleteConnectionModal &&
createPortal(
{showDeleteConnectionModal && (
<ActionConfirmModal
title="Delete Connection"
content="Are you sure you want to delete this connection?"
confirmButtonStyle="btn-error"
close={() => setShowDeleteConnectionModal(false)}
confirm={() => handleDeleteConnection()}
/>,
document.body
/>
)}
</>
);

View File

@ -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,16 +32,11 @@ const EditConversationTitleModal = (props: Props) => {
};
return (
<div className="modal modal-middle modal-open">
<div className="modal-box relative">
<h3 className="font-bold text-lg">{t("conversation.edit-title")}</h3>
<button className="btn btn-sm btn-circle absolute right-4 top-4" onClick={close}>
<Icon.IoMdClose className="w-5 h-auto" />
</button>
<div className="w-full flex flex-col justify-start items-start space-y-3 pt-4">
<Dialog title={t("conversation.edit-title")} onClose={close}>
<div className="w-full flex flex-col justify-start items-start mt-2">
<TextField placeholder={t("conversation.conversation-title") || ""} value={title} onChange={(value) => setTitle(value)} />
</div>
<div className="modal-action">
<div className="w-full flex flex-row justify-end items-center mt-4 space-x-2">
<button className="btn btn-outline" onClick={close}>
{t("common.close")}
</button>
@ -49,8 +44,7 @@ const EditConversationTitleModal = (props: Props) => {
{t("common.save")}
</button>
</div>
</div>
</div>
</Dialog>
);
};

View File

@ -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,21 +7,15 @@ 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 (
<div className={`modal modal-middle ${show && "modal-open"}`}>
<div className="modal-box relative">
<h3 className="font-bold text-lg">{t("setting.self")}</h3>
<button className="btn btn-sm btn-circle absolute right-4 top-4" onClick={close}>
<Icon.IoMdClose className="w-5 h-auto" />
</button>
<Dialog title={t("setting.self")} onClose={close}>
<div className="w-full flex flex-col justify-start items-start space-y-3 pt-4">
<div className="w-full flex flex-row justify-start items-start flex-wrap">
<a
@ -52,8 +47,7 @@ const SettingModal = (props: Props) => {
</div>
</div>
</div>
</div>
</div>
</Dialog>
);
};

View File

@ -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 (
<DialogUI.Root open={true}>
<DialogUI.Portal>
<DialogUI.Overlay className="fixed inset-0 bg-black bg-opacity-60 z-[9999]" />
<DialogUI.Content className="bg-white rounded-xl p-4 px-5 fixed top-[50%] left-[50%] max-h-[85vh] w-[90vw] max-w-[450px] translate-x-[-50%] translate-y-[-50%] z-[9999]">
<DialogUI.Title className="text-lg text-black font-medium mb-2">{title}</DialogUI.Title>
<DialogUI.Close
className="absolute top-3 right-3 outline-none w-8 h-8 p-1 bg-zinc-600 rounded-full text-white hover:opacity-80"
aria-label="Close"
onClick={onClose}
>
<Icon.IoClose className="w-full h-auto" />
</DialogUI.Close>
<div className="w-full flex flex-col justify-start items-start">{children}</div>
</DialogUI.Content>
</DialogUI.Portal>
</DialogUI.Root>
);
};
export default Dialog;