diff --git a/index.html b/index.html deleted file mode 100644 index 4f1958b..0000000 --- a/index.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - Vite + React + TS - - - - - \ No newline at end of file diff --git a/package.json b/package.json index c68f480..c368c81 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,7 @@ "lint:fix": "eslint . --fix" }, "dependencies": { + "@emotion/cache": "^11.10.5", "@emotion/react": "^11.10.6", "@emotion/styled": "^11.10.6", "@mui/icons-material": "^5.11.11", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 35cba18..3bcea97 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1,6 +1,7 @@ lockfileVersion: 5.4 specifiers: + '@emotion/cache': ^11.10.5 '@emotion/react': ^11.10.6 '@emotion/styled': ^11.10.6 '@mui/icons-material': ^5.11.11 @@ -17,6 +18,7 @@ specifiers: vite-plugin-monkey: 1.1.4 dependencies: + '@emotion/cache': 11.10.5 '@emotion/react': 11.10.6_pmekkgnqduwlme35zpnqhenc34 '@emotion/styled': 11.10.6_oouaibmszuch5k64ms7uxp2aia '@mui/icons-material': 5.11.11_4lyzeezzeeal3x6jtb4ni26w7u diff --git a/src/App.tsx b/src/App.tsx index a01ba26..abc7499 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,17 +1,18 @@ -/* eslint-disable no-undef */ import styled from '@emotion/styled'; -import { ContentCopy, ContentPaste, DeleteForever } from '@mui/icons-material'; import AutoFixHighIcon from '@mui/icons-material/AutoFixHigh'; -import { - Divider, - IconButton, - ListItemIcon, - ListItemText, - Menu, - MenuItem, -} from '@mui/material'; +import ContentCopy from '@mui/icons-material/ContentCopy'; +import ContentPaste from '@mui/icons-material/ContentPaste'; +import DeleteForever from '@mui/icons-material/DeleteForever'; +import Alert from '@mui/material/Alert'; +import Divider from '@mui/material/Divider'; +import IconButton from '@mui/material/IconButton'; +import ListItemIcon from '@mui/material/ListItemIcon'; +import ListItemText from '@mui/material/ListItemText'; +import Menu from '@mui/material/Menu'; +import MenuItem from '@mui/material/MenuItem'; +import Snackbar from '@mui/material/Snackbar'; import { useState } from 'react'; -import notification from './components/notification'; +import useToast from './hooks/useToast'; const Z_INDEX_MAX = 2 ** 31 - 1; @@ -36,6 +37,15 @@ const CustomMenu = styled(Menu)` const App = () => { const [anchorEl, setAnchorEl] = useState(null); const menuOpen = Boolean(anchorEl); + + const { + open: toastOpen, + message: toastMessage, + severity: toastSeverity, + handleOpen: handleToastOpen, + handleClose: handleToastClose, + } = useToast(); + const handleMenuOpen = (event: React.MouseEvent) => { setAnchorEl(event.currentTarget); }; @@ -89,9 +99,9 @@ const App = () => { const cookie = await getCookie(); const exportSessionData = JSON.stringify(cookie); copyToClipboard(exportSessionData); - notification.success({ message: 'Session 数据已复制到剪贴板' }); + handleToastOpen('Session 数据已复制到剪贴板', 'success'); } catch (error) { - notification.error({ message: '获取 Cookie 失败' }); + handleToastOpen('获取 Cookie 失败', 'error'); } }; @@ -103,18 +113,18 @@ const App = () => { cookie.forEach(async (item) => { await setCookie(item); }); - notification.success({ message: 'Session 数据已导入' }); + handleToastOpen('Session 数据已导入', 'success'); } catch (error) { - notification.error({ message: '导入 Cookie 失败' }); + handleToastOpen('导入 Cookie 失败', 'error'); } }; const deleteSession = async () => { try { await deleteCookie(); - notification.success({ message: 'Session 数据已删除' }); + handleToastOpen('Session 数据已删除', 'success'); } catch (error) { - notification.error({ message: '删除 Cookie 失败' }); + handleToastOpen('删除 Cookie 失败', 'error'); } }; @@ -134,7 +144,8 @@ const App = () => { }; return ( -
+ <> + {/* 工具按钮 */} { + {/* 工具菜单 */} { 清除 Session 数据 -
+ + {/* 提示 */} + + + {toastMessage} + + + ); }; diff --git a/src/components/notification.tsx b/src/components/notification.tsx deleted file mode 100644 index 4148d07..0000000 --- a/src/components/notification.tsx +++ /dev/null @@ -1,84 +0,0 @@ -import { Alert, Snackbar } from '@mui/material'; -import { useEffect, useState } from 'react'; -import ReactDOM from 'react-dom'; - -const defaultDuration = 2000; - -interface NotificationProps { - message: string; - type: 'success' | 'error' | 'info' | 'warning'; - duration?: number; -} - -interface ConfigProps { - message: string; - duration?: number; -} - -const Notification = (props: NotificationProps) => { - const { message, type, duration } = props; - const [open, setOpen] = useState(false); - - const handleClose = () => { - setOpen(false); - }; - - useEffect(() => { - setOpen(true); - }, []); - - return ( - - - {message} - - - ); -}; - -const notification = { - dom: null, - - success({ message, duration = defaultDuration }: ConfigProps) { - const dom = document.createElement('div'); - const JSXdom = ( - - ); - ReactDOM.render(JSXdom, dom); - document.body.appendChild(dom); - }, - - error({ message, duration = defaultDuration }: ConfigProps) { - const dom = document.createElement('div'); - const JSXdom = ( - - ); - ReactDOM.render(JSXdom, dom); - document.body.appendChild(dom); - }, - - warning({ message, duration = defaultDuration }: ConfigProps) { - const dom = document.createElement('div'); - const JSXdom = ( - - ); - ReactDOM.render(JSXdom, dom); - document.body.appendChild(dom); - }, - - info({ message, duration = defaultDuration }: ConfigProps) { - const dom = document.createElement('div'); - const JSXdom = ( - - ); - ReactDOM.render(JSXdom, dom); - document.body.appendChild(dom); - }, -}; - -export default notification; diff --git a/src/hooks/useToast.ts b/src/hooks/useToast.ts new file mode 100644 index 0000000..84f9808 --- /dev/null +++ b/src/hooks/useToast.ts @@ -0,0 +1,43 @@ +import { useState } from 'react'; + +type SeverityType = 'success' | 'error' | 'info' | 'warning'; + +interface ToastState { + open: boolean; + message: string; + severity: SeverityType; +} + +interface ToastActions { + handleOpen: (toastMessage: string, toastSeverity: SeverityType) => void; + handleClose: () => void; +} + +type Toast = ToastState & ToastActions; + +const useToast = (): Toast => { + const [state, setState] = useState({ + open: false, + message: '', + severity: 'success', + }); + + const handleOpen = (toastMessage: string, toastSeverity: SeverityType) => { + setState({ + open: true, + message: toastMessage, + severity: toastSeverity, + }); + }; + + const handleClose = () => { + setState({ + ...state, + open: false, + }); + }; + + return { ...state, handleOpen, handleClose }; +}; + +export default useToast; diff --git a/src/index.css b/src/index.css deleted file mode 100644 index e69de29..0000000 diff --git a/src/main.tsx b/src/main.tsx index 447e2ce..edb0853 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -1,16 +1,67 @@ +import createCache from '@emotion/cache'; +import { CacheProvider } from '@emotion/react'; +import createTheme from '@mui/material/styles/createTheme'; +import ThemeProvider from '@mui/material/styles/ThemeProvider'; import React from 'react'; import ReactDOM from 'react-dom/client'; import App from './App'; -import './index.css'; -ReactDOM.createRoot( - (() => { - const app = document.createElement('div'); - document.body.append(app); - return app; - })() -).render( +// 注入应用容器 +const container = document.createElement('div'); +container.id = 'monkey'; +container.style.all = 'initial'; +document.body.appendChild(container); + +// 创建 shadow DOM +const shadowContainer = container.attachShadow({ mode: 'closed' }); + +// 将应用添加到 shadow DOM 中 +const app = document.createElement('div'); +shadowContainer.appendChild(app); + +// 将 emotion 样式添加到 shadow DOM 中 +const emotionRoot = document.createElement('style'); +shadowContainer.appendChild(emotionRoot); + +const cache = createCache({ + key: 'css', + prepend: true, + container: emotionRoot, +}); + +// 获取浏览器的字体大小 +const { fontSize } = window.getComputedStyle(document.documentElement); +const htmlFontSize = parseFloat(fontSize); + +const theme = createTheme({ + components: { + MuiPopover: { + defaultProps: { + container: app, + }, + }, + MuiPopper: { + defaultProps: { + container: app, + }, + }, + MuiModal: { + defaultProps: { + container: app, + }, + }, + }, + typography: { + htmlFontSize, + }, +}); + +ReactDOM.createRoot(app).render( - + + + + + ); diff --git a/vite.config.ts b/vite.config.ts index 78477da..610ff5a 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -12,7 +12,7 @@ export default defineConfig({ name: 'Session Magician', namespace: 'https://www.imbytecat.com/', icon: 'https://vitejs.dev/logo.svg', - version: '2.2.3', + version: '3.1.0', description: 'Session Magician & Session Tools & Export/Import Sessions', author: 'imbytecat', match: ['*://*/*'],