From 4c17298432157b2d6d113a279ea4f48b6675813d Mon Sep 17 00:00:00 2001 From: "Kilu.He" <108015703+qinluhe@users.noreply.github.com> Date: Fri, 7 Jul 2023 21:58:15 +0800 Subject: [PATCH] feat: support dark mode (#2945) --- .../appflowy_tauri/src/appflowy_app/App.tsx | 32 +---- .../src/appflowy_app/AppMain.hooks.ts | 43 ++++++ .../src/appflowy_app/AppMain.tsx | 43 ++++++ .../components/_shared/Button.tsx | 13 +- .../_shared/EditRow/ChangeFieldTypePopup.tsx | 2 +- .../_shared/EditRow/CheckList/CheckList.tsx | 7 +- .../EditRow/CheckList/CheckListOption.tsx | 3 +- .../EditRow/CheckList/EditCheckListPopup.tsx | 8 +- .../_shared/EditRow/Date/DateFormatPopup.tsx | 4 +- .../_shared/EditRow/Date/DateTypeOptions.tsx | 16 +-- .../EditRow/Date/NumberFormatPopup.tsx | 4 +- .../_shared/EditRow/Date/TimeFormatPopup.tsx | 8 +- .../_shared/EditRow/EditCellWrapper.tsx | 19 +-- .../_shared/EditRow/EditFieldPopup.tsx | 15 ++- .../components/_shared/EditRow/EditRow.tsx | 14 +- .../_shared/EditRow/Options/CellOption.tsx | 6 +- .../_shared/EditRow/Options/CellOptions.tsx | 7 +- .../EditRow/Options/CellOptionsPopup.tsx | 14 +- .../EditRow/Options/EditCellOptionPopup.tsx | 21 +-- .../EditRow/Options/SelectedOption.tsx | 2 +- .../_shared/EditRow/PropertiesPanel.tsx | 33 +++-- .../components/_shared/PopupSelect.tsx | 5 +- .../components/_shared/PopupWindow.tsx | 5 +- .../components/_shared/PromptWindow.tsx | 2 +- .../components/_shared/SearchInput.tsx | 4 +- .../useUserSettingControllerContext.ts | 9 ++ .../_shared/svg/AppflowyLogoDark.tsx | 77 +++++++++++ .../_shared/svg/AppflowyLogoLight.tsx | 53 ++++++++ .../components/_shared/svg/EditorCheckSvg.tsx | 9 +- .../_shared/svg/EditorUncheckSvg.tsx | 2 +- .../components/_shared/svg/FullView.tsx | 8 +- .../components/_shared/svg/GroupBySvg.tsx | 30 ++++- .../auth/ConfirmAccount/ConfirmAccount.tsx | 2 +- .../components/auth/GetStarted/GetStarted.tsx | 7 +- .../components/auth/Login/Login.tsx | 11 +- .../components/auth/SignUp/SignUp.tsx | 5 +- .../components/auth/auth.hooks.ts | 1 + .../components/board/BoardCard.tsx | 7 +- .../components/board/BoardFieldsPopup.tsx | 5 +- .../components/board/BoardGroup.tsx | 10 +- .../board/BoardGroupFieldsPopup.tsx | 5 +- .../components/board/BoardOptionsCell.tsx | 2 +- .../BlockRectSelection.hooks.ts | 14 +- .../BlockSelection/BlockRectSelection.tsx | 2 +- .../document/CalloutBlock/index.tsx | 2 +- .../components/document/CodeBlock/index.tsx | 7 +- .../document/DividerBlock/index.tsx | 2 +- .../document/EquationBlock/index.tsx | 4 +- .../document/ImageBlock/ImageAlign.tsx | 5 +- .../document/ImageBlock/ImagePlaceholder.tsx | 2 +- .../document/ImageBlock/ImageRender.tsx | 2 +- .../document/ImageBlock/ImageToolbar.tsx | 4 +- .../components/document/ImageBlock/index.tsx | 4 +- .../components/document/Node/index.tsx | 2 +- .../components/document/QuoteBlock/index.tsx | 2 +- .../components/document/Root/index.tsx | 2 +- .../document/TextActionMenu/index.tsx | 2 +- .../TextActionMenu/menu/FormatButton.tsx | 6 +- .../TextActionMenu/menu/MenuTooltip.tsx | 6 +- .../TextActionMenu/menu/TurnIntoSelect.tsx | 12 +- .../document/TextActionMenu/menu/index.tsx | 2 +- .../document/TodoListBlock/index.tsx | 2 +- .../_shared/SlateEditor/CodeEditor.tsx | 5 +- .../_shared/SlateEditor/TextEditor.tsx | 2 +- .../document/_shared/SlateEditor/TextLeaf.tsx | 6 +- .../_shared/SlateEditor/decorateCode.ts | 30 ++++- .../TemporaryInput/EquationEditContent.tsx | 12 +- .../TemporaryInput/TemporaryEquation.tsx | 4 +- .../document/_shared/TextLink/EditLink.tsx | 27 ++-- .../_shared/TextLink/EditLinkToolbar.tsx | 7 +- .../document/_shared/TextLink/LinkButton.tsx | 22 ---- .../_shared/TextLink/LinkEditPopover.tsx | 26 ++-- .../_shared/TextLink/LinkHighLight.tsx | 8 +- .../document/_shared/TextLink/index.tsx | 7 +- .../document/_shared/TurnInto/index.tsx | 2 +- .../document/_shared/UploadImage/index.tsx | 4 +- .../components/error/ErrorModal.tsx | 2 +- .../grid/GridAddView/GridAddView.tsx | 2 +- .../grid/GridTableHeader/GridTableHeader.tsx | 6 +- .../GridTableHeader/GridTableHeaderItem.tsx | 8 +- .../grid/GridTableRows/GridAddRow.tsx | 3 +- .../grid/GridTableRows/GridTableRow.tsx | 4 +- .../grid/GridTitle/GridTitleOptionsPopup.tsx | 9 +- .../components/layout/AppLogo.tsx | 13 +- .../components/layout/FooterPanel.tsx | 4 +- .../layout/HeaderPanel/Breadcrumbs.tsx | 15 ++- .../layout/HeaderPanel/HeaderPanel.tsx | 2 +- .../layout/HeaderPanel/LanguageButton.tsx | 7 +- .../layout/HeaderPanel/PageOptions.tsx | 6 +- .../components/layout/MainPanel.tsx | 3 +- .../layout/NavigationPanel/NavItem.hooks.ts | 7 +- .../layout/NavigationPanel/NavItem.tsx | 57 ++++---- .../NavigationPanel/NavItemOptionsPopup.tsx | 6 +- .../NavigationPanel/NavigationPanel.tsx | 19 +-- .../layout/NavigationPanel/NewPagePopup.tsx | 6 +- .../layout/NavigationPanel/NewViewButton.tsx | 6 +- .../layout/NavigationPanel/PluginsButton.tsx | 2 +- .../layout/NavigationPanel/TrashButton.tsx | 11 +- .../appflowy_app/components/layout/Screen.tsx | 2 +- .../layout/UserSetting/AppearanceSetting.tsx | 122 ++++++++++++++++++ .../layout/UserSetting/LanguageSetting.tsx | 7 + .../components/layout/UserSetting/Menu.tsx | 48 +++++++ .../layout/UserSetting/SettingPanel.tsx | 37 ++++++ .../components/layout/UserSetting/index.tsx | 61 +++++++++ .../components/layout/WorkspaceUser.tsx | 37 +++++- .../components/tests/ColorPalette.tsx | 34 ++--- .../src/appflowy_app/interfaces/document.ts | 1 + .../src/appflowy_app/interfaces/index.ts | 16 ++- .../stores/effects/user/user_bd_svc.ts | 22 ++++ .../effects/user/user_setting_controller.ts | 39 ++++++ .../stores/reducers/current-user/slice.ts | 25 +++- .../reducers/document/async-actions/link.ts | 2 +- .../stores/reducers/document/slice.ts | 5 +- .../src/appflowy_app/utils/mui.ts | 51 ++++++++ .../src/appflowy_app/views/DocumentPage.tsx | 25 +--- .../appflowy_tauri/src/styles/Calendar.css | 16 +-- .../appflowy_tauri/src/styles/color/dark.css | 21 +++ .../src/styles/color/default.css | 54 ++++++++ .../appflowy_tauri/src/styles/color/light.css | 21 +++ frontend/appflowy_tauri/src/styles/mui.css | 37 ++++++ frontend/appflowy_tauri/src/styles/switch.css | 4 +- .../appflowy_tauri/src/styles/template.css | 18 ++- frontend/appflowy_tauri/tailwind.config.cjs | 94 ++++++++------ 123 files changed, 1351 insertions(+), 465 deletions(-) create mode 100644 frontend/appflowy_tauri/src/appflowy_app/AppMain.hooks.ts create mode 100644 frontend/appflowy_tauri/src/appflowy_app/AppMain.tsx create mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/_shared/app-hooks/useUserSettingControllerContext.ts create mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/AppflowyLogoDark.tsx create mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/AppflowyLogoLight.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/TextLink/LinkButton.tsx create mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/layout/UserSetting/AppearanceSetting.tsx create mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/layout/UserSetting/LanguageSetting.tsx create mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/layout/UserSetting/Menu.tsx create mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/layout/UserSetting/SettingPanel.tsx create mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/layout/UserSetting/index.tsx create mode 100644 frontend/appflowy_tauri/src/appflowy_app/stores/effects/user/user_setting_controller.ts create mode 100644 frontend/appflowy_tauri/src/appflowy_app/utils/mui.ts create mode 100644 frontend/appflowy_tauri/src/styles/color/dark.css create mode 100644 frontend/appflowy_tauri/src/styles/color/default.css create mode 100644 frontend/appflowy_tauri/src/styles/color/light.css create mode 100644 frontend/appflowy_tauri/src/styles/mui.css diff --git a/frontend/appflowy_tauri/src/appflowy_app/App.tsx b/frontend/appflowy_tauri/src/appflowy_app/App.tsx index 9589cfa2d6..36f2d64e63 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/App.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/App.tsx @@ -1,21 +1,14 @@ -import { Routes, Route, BrowserRouter } from 'react-router-dom'; +import { BrowserRouter } from 'react-router-dom'; -import { ColorPalette } from './components/tests/ColorPalette'; import { Provider } from 'react-redux'; import { store } from './stores/store'; -import { DocumentPage } from './views/DocumentPage'; -import { BoardPage } from './views/BoardPage'; -import { GridPage } from './views/GridPage'; -import { LoginPage } from './views/LoginPage'; -import { ProtectedRoutes } from './components/auth/ProtectedRoutes'; -import { SignUpPage } from './views/SignUpPage'; -import { ConfirmAccountPage } from './views/ConfirmAccountPage'; + import { ErrorHandlerPage } from './components/error/ErrorHandlerPage'; import initializeI18n from './stores/i18n/initializeI18n'; -import { TestAPI } from './components/tests/TestAPI'; -import { GetStarted } from './components/auth/GetStarted/GetStarted'; + import { ErrorBoundary } from 'react-error-boundary'; -import { AllIcons } from '$app/components/tests/AllIcons'; + +import AppMain from '$app/AppMain'; initializeI18n(); @@ -24,20 +17,7 @@ const App = () => { - - }> - } /> - } /> - } /> - } /> - } /> - } /> - - }> - }> - }> - }> - + diff --git a/frontend/appflowy_tauri/src/appflowy_app/AppMain.hooks.ts b/frontend/appflowy_tauri/src/appflowy_app/AppMain.hooks.ts new file mode 100644 index 0000000000..4d1b3d3f17 --- /dev/null +++ b/frontend/appflowy_tauri/src/appflowy_app/AppMain.hooks.ts @@ -0,0 +1,43 @@ +import { useAppDispatch, useAppSelector } from '$app/stores/store'; +import { useEffect, useMemo } from 'react'; +import { UserSettingController } from '$app/stores/effects/user/user_setting_controller'; +import { currentUserActions } from '$app_reducers/current-user/slice'; +import { Theme as ThemeType, Theme, ThemeMode } from '$app/interfaces'; +import { createTheme } from '@mui/material/styles'; +import { getDesignTokens } from '$app/utils/mui'; + +export function useUserSetting() { + const dispatch = useAppDispatch(); + const currentUser = useAppSelector((state) => state.currentUser); + const userSettingController = useMemo(() => { + if (!currentUser?.id) return; + const controller = new UserSettingController(currentUser.id); + + return controller; + }, [currentUser?.id]); + + useEffect(() => { + userSettingController?.getAppearanceSetting().then((res) => { + if (!res) return; + dispatch( + currentUserActions.setUserSetting({ + themeMode: res.theme_mode, + theme: res.theme as Theme, + }) + ); + }); + }, [dispatch, userSettingController]); + + const { themeMode = ThemeMode.Light, theme: themeType = ThemeType.Default } = useAppSelector((state) => { + return state.currentUser.userSetting || {}; + }); + + const muiTheme = useMemo(() => createTheme(getDesignTokens(themeMode)), [themeMode]); + + return { + muiTheme, + themeMode, + themeType, + userSettingController, + }; +} diff --git a/frontend/appflowy_tauri/src/appflowy_app/AppMain.tsx b/frontend/appflowy_tauri/src/appflowy_app/AppMain.tsx new file mode 100644 index 0000000000..1dd2cbc3aa --- /dev/null +++ b/frontend/appflowy_tauri/src/appflowy_app/AppMain.tsx @@ -0,0 +1,43 @@ +import React from 'react'; +import { Route, Routes } from 'react-router-dom'; +import { ProtectedRoutes } from '$app/components/auth/ProtectedRoutes'; +import { AllIcons } from '$app/components/tests/AllIcons'; +import { ColorPalette } from '$app/components/tests/ColorPalette'; +import { TestAPI } from '$app/components/tests/TestAPI'; +import { DocumentPage } from '$app/views/DocumentPage'; +import { BoardPage } from '$app/views/BoardPage'; +import { GridPage } from '$app/views/GridPage'; +import { LoginPage } from '$app/views/LoginPage'; +import { GetStarted } from '$app/components/auth/GetStarted/GetStarted'; +import { SignUpPage } from '$app/views/SignUpPage'; +import { ConfirmAccountPage } from '$app/views/ConfirmAccountPage'; +import { ThemeProvider } from '@mui/material'; +import { useUserSetting } from '$app/AppMain.hooks'; +import { UserSettingControllerContext } from '$app/components/_shared/app-hooks/useUserSettingControllerContext'; + +function AppMain() { + const { muiTheme, userSettingController } = useUserSetting(); + + return ( + + + + }> + } /> + } /> + } /> + } /> + } /> + } /> + + }> + }> + }> + }> + + + + ); +} + +export default AppMain; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/Button.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/Button.tsx index cb637e7f1c..fb6783bc4a 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/Button.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/Button.tsx @@ -10,24 +10,27 @@ export const Button = ({ onClick?: MouseEventHandler; }) => { const [cls, setCls] = useState(''); + useEffect(() => { switch (size) { case 'primary': - setCls('w-[340px] h-[48px] flex items-center justify-center rounded-lg bg-main-accent text-white'); + setCls('w-[340px] h-[48px] flex items-center justify-center rounded-lg bg-content-default text-content-onfill'); break; case 'medium': - setCls('w-[170px] h-[48px] flex items-center justify-center rounded-lg bg-main-accent text-white'); + setCls('w-[170px] h-[48px] flex items-center justify-center rounded-lg bg-content-default text-content-onfill'); break; case 'small': - setCls('w-[68px] h-[32px] flex items-center justify-center rounded-lg bg-main-accent text-white text-xs'); + setCls( + 'w-[68px] h-[32px] flex items-center justify-center rounded-lg bg-content-default text-content-onfill text-xs hover:bg-content-hover' + ); break; case 'medium-transparent': setCls( - 'w-[170px] h-[48px] flex items-center justify-center rounded-lg border border-main-accent text-main-accent transition-colors duration-300 hover:bg-main-hovered hover:text-white' + 'w-[170px] h-[48px] flex items-center justify-center rounded-lg border border-content-default text-content-default transition-colors duration-300 hover:bg-content-hover hover:text-content-onfill' ); break; case 'box-small-transparent': - setCls('text-black hover:text-main-accent w-[24px] h-[24px]'); + setCls('text-icon-default w-[24px] h-[24px] rounded hover:bg-fill-hover'); break; } }, [size]); diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/ChangeFieldTypePopup.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/ChangeFieldTypePopup.tsx index e55b6bf70d..78ad14cee2 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/ChangeFieldTypePopup.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/ChangeFieldTypePopup.tsx @@ -32,7 +32,7 @@ export const ChangeFieldTypePopup = ({ -
+
-
+
-
-
{t('grid.selectOption.colorPanelTitle')}
+
+
{t('grid.selectOption.colorPanelTitle')}
{ return (
onClick()} >
diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/Options/SelectedOption.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/Options/SelectedOption.tsx index 51cfdaa0f0..00968fb0b6 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/Options/SelectedOption.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/Options/SelectedOption.tsx @@ -20,7 +20,7 @@ export const SelectedOption = ({ }; return ( -
+
{option?.name ?? ''}
setShowAdvancedProperties(!showAdvancedProperties)} - className={'flex cursor-pointer items-center justify-between gap-8 rounded-lg px-2 py-2 hover:bg-shade-6'} + className={'flex cursor-pointer items-center justify-between gap-8 rounded-lg px-2 py-2 hover:bg-fill-hover'} >
Advanced Properties
@@ -182,25 +187,19 @@ export const PropertiesPanel = ({
{showAdvancedProperties && (
- - -
diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/auth/Login/Login.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/auth/Login/Login.tsx index 1c38818722..a98681f46d 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/auth/Login/Login.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/auth/Login/Login.tsx @@ -18,7 +18,7 @@ export const Login = () => { return ( <> e.preventDefault()} method='POST'> -
+
@@ -61,7 +61,7 @@ export const Login = () => {
{/* Forget password link */} - {t('signIn.forgotPassword')} + {t('signIn.forgotPassword')}
@@ -76,7 +76,7 @@ export const Login = () => { {t('signIn.dontHaveAnAccount')} - {t('signUp.buttonText')} + {t('signUp.buttonText')}
@@ -84,7 +84,10 @@ export const Login = () => {
- {showLanguagePopup && ( diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/auth/SignUp/SignUp.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/auth/SignUp/SignUp.tsx index 7f21984b70..1f347ef327 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/auth/SignUp/SignUp.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/auth/SignUp/SignUp.tsx @@ -112,7 +112,10 @@ export const SignUp = () => {
- {showLanguagePopup && ( diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/auth/auth.hooks.ts b/frontend/appflowy_tauri/src/appflowy_app/components/auth/auth.hooks.ts index 6fcd7cbb1c..ebf9924be9 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/auth/auth.hooks.ts +++ b/frontend/appflowy_tauri/src/appflowy_app/components/auth/auth.hooks.ts @@ -16,6 +16,7 @@ export const useAuth = () => { if (result.ok) { const userProfile = result.val; + const workspaceSetting = await _openWorkspace().then((r) => { if (r.ok) { return r.val; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/board/BoardCard.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/board/BoardCard.tsx index 98b9ec95eb..c6c4778940 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/board/BoardCard.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/board/BoardCard.tsx @@ -44,6 +44,7 @@ export const BoardCard = ({ } const { right: left, top } = target.getBoundingClientRect(); + setCardPopupLeft(left); setCardPopupTop(top); setShowCardPopup(true); @@ -63,9 +64,9 @@ export const BoardCard = ({ {...provided.draggableProps} {...provided.dragHandleProps} onClick={() => onOpenRow(rowInfo)} - className={`relative cursor-pointer select-none rounded-lg border border-shade-6 bg-white px-3 py-2 transition-transform duration-100 hover:bg-main-selector `} + className={`relative cursor-pointer select-none rounded-lg border border-line-border bg-bg-body px-3 py-2 transition-transform duration-100 hover:bg-fill-selector `} > -
@@ -94,7 +95,7 @@ export const BoardCard = ({ > -
@@ -84,7 +86,7 @@ export const BoardGroup = ({
+
+ {node.type} + +
group.length > 0 && ( -
+
{group.map((item) => (
{renderNode(item)} diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/document/TodoListBlock/index.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/document/TodoListBlock/index.tsx index 5fb331b82d..d6612168a9 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/document/TodoListBlock/index.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/document/TodoListBlock/index.tsx @@ -32,7 +32,7 @@ export default function TodoListBlock({ />
-
+
diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/SlateEditor/CodeEditor.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/SlateEditor/CodeEditor.tsx index ed476f83d9..68ca2b9ad1 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/SlateEditor/CodeEditor.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/SlateEditor/CodeEditor.tsx @@ -6,7 +6,7 @@ import { decorateCode } from '$app/components/document/_shared/SlateEditor/decor import { CodeBlockElement } from '$app/components/document/_shared/SlateEditor/CodeElements'; import TextLeaf from '$app/components/document/_shared/SlateEditor/TextLeaf'; -function CodeEditor({ language, ...props }: CodeEditorProps) { +function CodeEditor({ language, isDark, ...props }: CodeEditorProps) { const { editor, onChange, value, ref, ...editableProps } = useEditor({ ...props, isCodeBlock: true, @@ -18,8 +18,9 @@ function CodeEditor({ language, ...props }: CodeEditorProps) { { - const codeRange = decorateCode(entry, language); + const codeRange = decorateCode(entry, language, isDark); const range = editableProps.decorate?.(entry) || []; + return [...range, ...codeRange]; }} renderLeaf={(leafProps) => } diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/SlateEditor/TextEditor.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/SlateEditor/TextEditor.tsx index 2a87497053..2fe64d2992 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/SlateEditor/TextEditor.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/SlateEditor/TextEditor.tsx @@ -9,7 +9,7 @@ function TextEditor({ placeholder = "Type '/' for commands", ...props }: EditorP const { editor, onChange, value, ref, ...editableProps } = useEditor(props); return ( -
+
} diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/SlateEditor/TextLeaf.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/SlateEditor/TextLeaf.tsx index 526452dcd4..b4ea311565 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/SlateEditor/TextLeaf.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/SlateEditor/TextLeaf.tsx @@ -40,7 +40,7 @@ const TextLeaf = (props: TextLeafProps) => { if (leaf.code) { newChildren = ( { isCodeBlock && 'token', leaf.prism_token && leaf.prism_token, leaf.strikethrough && 'line-through', - leaf.selection_high_lighted && 'bg-main-secondary', - leaf.link_selection_lighted && 'text-link bg-main-secondary', + leaf.selection_high_lighted && 'bg-fill-selector', + leaf.link_selection_lighted && 'text-text-link-selector bg-fill-selector', leaf.code && 'inline-code', leaf.bold && 'font-bold', leaf.italic && 'italic', diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/SlateEditor/decorateCode.ts b/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/SlateEditor/decorateCode.ts index b772bd622a..563115c88a 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/SlateEditor/decorateCode.ts +++ b/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/SlateEditor/decorateCode.ts @@ -1,5 +1,5 @@ import Prism from 'prismjs'; -import 'prismjs/themes/prism.css'; + import 'prismjs/components/prism-javascript'; import 'prismjs/components/prism-json'; import 'prismjs/components/prism-typescript'; @@ -15,6 +15,7 @@ const push_string = ( token_type = 'text' ) => { let newStart = start; + ranges.push({ // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore @@ -38,13 +39,16 @@ const recurseTokenize = ( if (typeof token === 'string') { return push_string(token, path, start, ranges, parent_tag); } + if ('content' in token) { if (token.content instanceof Array) { // Calls recurseTokenize on nested Tokens in content let newStart = start; + for (const subToken of token.content) { newStart = recurseTokenize(subToken, path, ranges, newStart, token.type) || 0; } + return newStart; } @@ -52,8 +56,28 @@ const recurseTokenize = ( } }; -export const decorateCode = ([node, path]: NodeEntry, language: string) => { +function switchCodeTheme(isDark: boolean) { + const link = document.getElementById('prism-css'); + + if (link) { + document.head.removeChild(link); + } + + const newLink = document.createElement('link'); + + newLink.rel = 'stylesheet'; + newLink.href = isDark + ? 'https://cdnjs.cloudflare.com/ajax/libs/prism/1.24.1/themes/prism-dark.min.css' + : 'https://cdnjs.cloudflare.com/ajax/libs/prism/1.24.1/themes/prism.min.css'; + newLink.id = 'prism-css'; + document.head.appendChild(newLink); +} + +export const decorateCode = ([node, path]: NodeEntry, language: string, isDark: boolean) => { + switchCodeTheme(isDark); + const ranges: BaseRange[] = []; + if (!Text.isText(node)) { return ranges; } @@ -62,9 +86,11 @@ export const decorateCode = ([node, path]: NodeEntry, language: string) => { const tokens = Prism.tokenize(node.text, Prism.languages[language]); let start = 0; + for (const token of tokens) { start = recurseTokenize(token, path, ranges, start) || 0; } + return ranges; } catch { return ranges; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/TemporaryInput/EquationEditContent.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/TemporaryInput/EquationEditContent.tsx index a9ced76a14..71a74fbb13 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/TemporaryInput/EquationEditContent.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/TemporaryInput/EquationEditContent.tsx @@ -17,7 +17,7 @@ function EquationEditContent({ multiline?: boolean; }) { return ( -
+
- - + +
diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/TemporaryInput/TemporaryEquation.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/TemporaryInput/TemporaryEquation.tsx index 6808caa4bb..53332f31a2 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/TemporaryInput/TemporaryEquation.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/TemporaryInput/TemporaryEquation.tsx @@ -4,11 +4,11 @@ import KatexMath from '$app/components/document/_shared/KatexMath'; function TemporaryEquation({ latex }: { latex: string }) { return ( - + {latex ? ( ) : ( - + {'New equation'} )} diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/TextLink/EditLink.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/TextLink/EditLink.tsx index 94da916a1a..5a4c579aa4 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/TextLink/EditLink.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/TextLink/EditLink.tsx @@ -1,4 +1,5 @@ import React, { useEffect, useState } from 'react'; +import TextField from '@mui/material/TextField'; function EditLink({ autoFocus, @@ -18,19 +19,19 @@ function EditLink({ }, [val, onChange]); return ( -
-
{text}
-
- { - const newValue = e.target.value; - setVal(newValue); - }} - value={val} - /> -
+
+ { + const newValue = e.target.value; + + setVal(newValue); + }} + value={val} + />
); } diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/TextLink/EditLinkToolbar.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/TextLink/EditLinkToolbar.tsx index 8455635b28..f6eb7d62c3 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/TextLink/EditLinkToolbar.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/TextLink/EditLinkToolbar.tsx @@ -10,6 +10,7 @@ const iconSize = { width: '1rem', height: '1rem', }; + function EditLinkToolbar({ blockId, linkElement, @@ -29,16 +30,20 @@ function EditLinkToolbar({ }) { const { show, contentHolder } = useMessage(); const ref = useRef(null); + useEffect(() => { const toolbarDom = ref.current; + if (!toolbarDom) return; const linkRect = linkElement.getBoundingClientRect(); const node = getNode(blockId); + if (!node) return; const nodeRect = node.getBoundingClientRect(); const top = linkRect.top - nodeRect.top + linkRect.height + 4; const left = linkRect.left - nodeRect.left; + toolbarDom.style.top = `${top}px`; toolbarDom.style.left = `${left}px`; toolbarDom.style.opacity = '1'; @@ -54,7 +59,7 @@ function EditLinkToolbar({ style={{ opacity: 0, }} - className='absolute z-10 inline-flex h-[32px] min-w-[200px] max-w-[400px] items-stretch overflow-hidden rounded-[8px] bg-white leading-tight text-black shadow-md transition-opacity duration-100' + className='absolute z-10 inline-flex h-[32px] min-w-[200px] max-w-[400px] items-stretch overflow-hidden rounded-[8px] bg-bg-body leading-tight text-text-title shadow-md transition-opacity duration-100' >
diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/TextLink/LinkButton.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/TextLink/LinkButton.tsx deleted file mode 100644 index 369dd25867..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/TextLink/LinkButton.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import React from 'react'; -import Button from '@mui/material/Button'; -import { DeleteOutline } from '@mui/icons-material'; - -function LinkButton({ icon, title, onClick }: { icon: React.ReactNode; title: string; onClick: () => void }) { - return ( -
- -
- ); -} - -export default LinkButton; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/TextLink/LinkEditPopover.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/TextLink/LinkEditPopover.tsx index 70134a5636..c10900c156 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/TextLink/LinkEditPopover.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/TextLink/LinkEditPopover.tsx @@ -1,14 +1,13 @@ -import React, { useCallback, useContext } from 'react'; +import React, { useCallback } from 'react'; import Popover from '@mui/material/Popover'; -import { Divider } from '@mui/material'; import { DeleteOutline, Done } from '@mui/icons-material'; import EditLink from '$app/components/document/_shared/TextLink/EditLink'; -import { useAppDispatch, useAppSelector } from '$app/stores/store'; +import { useAppDispatch } from '$app/stores/store'; import { linkPopoverActions, rangeActions } from '$app_reducers/document/slice'; import { formatLinkThunk } from '$app_reducers/document/async-actions/link'; -import LinkButton from '$app/components/document/_shared/TextLink/LinkButton'; import { useSubscribeDocument } from '$app/components/document/_shared/SubscribeDoc.hooks'; import { useSubscribeLinkPopover } from '$app/components/document/_shared/SubscribeLinkPopover.hooks'; +import Button from '@mui/material/Button'; function LinkEditPopover() { const dispatch = useAppDispatch(); @@ -27,6 +26,7 @@ function LinkEditPopover() { index: selection.index, length: title.length, }; + dispatch( rangeActions.setRange({ docId, @@ -120,18 +120,12 @@ function LinkEditPopover() { }) } /> - - } - onClick={() => { - onChange({ - title, - }); - onDone(); - }} - /> - } onClick={onDone} /> +
+ +
); diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/TextLink/LinkHighLight.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/TextLink/LinkHighLight.tsx index fae19cadcf..b4d6904c04 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/TextLink/LinkHighLight.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/TextLink/LinkHighLight.tsx @@ -8,13 +8,7 @@ function LinkHighLight({ children, leaf, title }: { leaf: { text: string }; titl {title} ) : null} - - {children} - + {children} ); } diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/TextLink/index.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/TextLink/index.tsx index c378fc59a5..c8e9d8111a 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/TextLink/index.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/TextLink/index.tsx @@ -28,8 +28,10 @@ function TextLink({ const onEdit = useCallback(() => { if (!ref.current) return; const selection = getSelection(ref.current); + if (!selection) return; const rect = ref.current?.getBoundingClientRect(); + if (!rect) return; dispatch( linkPopoverActions.setLinkPopover({ @@ -48,6 +50,7 @@ function TextLink({ }) ); }, [blockId, dispatch, docId, getSelection, href, ref, title]); + if (!blockId) return null; return ( @@ -59,9 +62,9 @@ function TextLink({ href={href} target='_blank' rel='noopener noreferrer' - className='cursor-pointer text-main-hovered' + className='cursor-pointer text-text-link-default' > - {children} + {children} {ref.current && ( -
+
{options.map((option) => { return (
The maximum file size is 5MB. Supported formats: JPG, PNG, GIF, SVG.
diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/error/ErrorModal.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/error/ErrorModal.tsx index 216dd872ec..30b9822fa4 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/error/ErrorModal.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/error/ErrorModal.tsx @@ -11,7 +11,7 @@ export const ErrorModal = ({ message, onClose }: { message: string; onClose: () > )} {iconToShow === 'show' && ( - +
); diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/layout/HeaderPanel/Breadcrumbs.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/layout/HeaderPanel/Breadcrumbs.tsx index 864ca0548b..1c0cb5e7b9 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/layout/HeaderPanel/Breadcrumbs.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/layout/HeaderPanel/Breadcrumbs.tsx @@ -2,6 +2,9 @@ import { ShowMenuSvg } from '../../_shared/svg/ShowMenuSvg'; import { useEffect, useState } from 'react'; import { useAppSelector } from '$app/stores/store'; import { useLocation } from 'react-router-dom'; +import { ArrowLeft, ArrowRight } from '@mui/icons-material'; +import { ArrowLeftSvg } from '$app/components/_shared/svg/ArrowLeftSvg'; +import { ArrowRightSvg } from '$app/components/_shared/svg/ArrowRightSvg'; export const Breadcrumbs = ({ menuHidden, onShowMenuClick }: { menuHidden: boolean; onShowMenuClick: () => void }) => { const [folderName, setFolderName] = useState(''); @@ -14,11 +17,13 @@ export const Breadcrumbs = ({ menuHidden, onShowMenuClick }: { menuHidden: boole const { pathname } = currentLocation; const parts = pathname.split('/'); const pageId = parts[parts.length - 1]; + setActivePageId(pageId); }, [currentLocation]); useEffect(() => { const page = pagesStore.find((p) => p.id === activePageId); + // const folder = foldersStore.find((f) => f.id === page?.parentPageId); // setFolderName(folder?.title ?? ''); setPageName(page?.title ?? ''); @@ -28,16 +33,16 @@ export const Breadcrumbs = ({ menuHidden, onShowMenuClick }: { menuHidden: boole
{menuHidden && ( - )} - -
diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/layout/HeaderPanel/HeaderPanel.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/layout/HeaderPanel/HeaderPanel.tsx index f870d6fa2d..1dbeb8b125 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/layout/HeaderPanel/HeaderPanel.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/layout/HeaderPanel/HeaderPanel.tsx @@ -3,7 +3,7 @@ import { PageOptions } from './PageOptions'; export const HeaderPanel = ({ menuHidden, onShowMenuClick }: { menuHidden: boolean; onShowMenuClick: () => void }) => { return ( -
+
diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/layout/HeaderPanel/LanguageButton.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/layout/HeaderPanel/LanguageButton.tsx index fc11c20c6b..b347ddda0a 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/layout/HeaderPanel/LanguageButton.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/layout/HeaderPanel/LanguageButton.tsx @@ -1,13 +1,14 @@ -import { EarthSvg } from '$app/components/_shared/svg/EarthSvg'; import { useState } from 'react'; import { LanguageSelectPopup } from '$app/components/_shared/LanguageSelectPopup'; +import { LanguageOutlined } from '@mui/icons-material'; export const LanguageButton = () => { const [showPopup, setShowPopup] = useState(false); + return ( <> - {showPopup && setShowPopup(false)}>} diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/layout/HeaderPanel/PageOptions.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/layout/HeaderPanel/PageOptions.tsx index 72ff8a5cfe..9b136e9d73 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/layout/HeaderPanel/PageOptions.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/layout/HeaderPanel/PageOptions.tsx @@ -16,7 +16,11 @@ export const PageOptions = () => { -
diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/layout/MainPanel.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/layout/MainPanel.tsx index d69ecb90e5..4e6638ff60 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/layout/MainPanel.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/layout/MainPanel.tsx @@ -15,6 +15,7 @@ export const MainPanel = ({ children: ReactNode; }) => { const [animation, setAnimation] = useState(false); + useEffect(() => { if (!menuHidden) { setTimeout(() => { @@ -27,7 +28,7 @@ export const MainPanel = ({ return (
{ const loadInsidePages = async () => { const result = await service.getChildViews(); + if (!result.ok) return; const views = result.val; const updatedPages: IPage[] = views.map((view) => ({ @@ -42,6 +43,7 @@ export const useNavItem = (page: IPage) => { title: view.name, showPagesInside: false, })); + appDispatch(pagesActions.addInsidePages({ currentPageId: page.id, insidePages: updatedPages })); }; @@ -62,6 +64,7 @@ export const useNavItem = (page: IPage) => { const { pathname } = currentLocation; const parts = pathname.split('/'); const pageId = parts[parts.length - 1]; + setActivePageId(pageId); }, [currentLocation]); @@ -76,6 +79,7 @@ export const useNavItem = (page: IPage) => { // recursively get all unfolded child pages const getChildCount: (startPage: IPage) => number = (startPage: IPage) => { let count = 0; + count = pages.filter((p) => p.parentPageId === startPage.id).length; pages .filter((p) => p.parentPageId === startPage.id) @@ -92,7 +96,7 @@ export const useNavItem = (page: IPage) => { }; const onPageOptionsClick = () => { - setShowPageOptions(!showPageOptions); + setShowPageOptions((prevState) => !prevState); }; const startPageRename = () => { @@ -181,6 +185,7 @@ export const useNavItem = (page: IPage) => { if (newViewResult.ok) { const newView = newViewResult.val; + if (!page.showPagesInside) { appDispatch(pagesActions.toggleShowPages({ id: page.id })); } diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/layout/NavigationPanel/NavItem.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/layout/NavigationPanel/NavItem.tsx index de37ef8b64..e1578faf38 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/layout/NavigationPanel/NavItem.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/layout/NavigationPanel/NavItem.tsx @@ -46,6 +46,7 @@ export const NavItem = ({ page }: { page: IPage }) => { useEffect(() => { if (el.current) { const { top } = el.current.getBoundingClientRect(); + setPopupY(top); } }, [showPageOptions, showNewPageOptions, showRenamePopup]); @@ -56,35 +57,31 @@ export const NavItem = ({ page }: { page: IPage }) => { className={`overflow-hidden transition-all`} style={{ height: folderHeight, transitionDuration: `${ANIMATION_DURATION}ms` }} > -
-
- -
onPageClick(page)} - className={ - 'flex h-full min-w-0 flex-1 items-center overflow-hidden overflow-ellipsis whitespace-nowrap text-left' - } - > - {page.title} +
+
+
+ +
onPageClick(page)} className={'mr-1 flex h-full min-w-0 items-center text-left'}> + {page.title} +
+
+
+ +
-
-
- -
@@ -101,7 +98,7 @@ export const NavItem = ({ page }: { page: IPage }) => { onDeleteClick={() => deletePage()} onDuplicateClick={() => duplicatePage()} onClose={() => closePopup()} - top={popupY - 124 + 40} + top={popupY - 124 + 58} > )} {showNewPageOptions && ( @@ -110,7 +107,7 @@ export const NavItem = ({ page }: { page: IPage }) => { onBoardClick={() => onAddNewPage(ViewLayoutPB.Board)} onGridClick={() => onAddNewPage(ViewLayoutPB.Grid)} onClose={() => closePopup()} - top={popupY - 124 + 40} + top={popupY - 124 + 58} > )} {showRenamePopup && ( diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/layout/NavigationPanel/NavItemOptionsPopup.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/layout/NavigationPanel/NavItemOptionsPopup.tsx index 283bdc62bd..f8a0a64191 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/layout/NavigationPanel/NavItemOptionsPopup.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/layout/NavigationPanel/NavItemOptionsPopup.tsx @@ -19,7 +19,7 @@ export const NavItemOptionsPopup = ({ const items: IPopupItem[] = [ { icon: ( - + ), @@ -28,7 +28,7 @@ export const NavItemOptionsPopup = ({ }, { icon: ( - + ), @@ -37,7 +37,7 @@ export const NavItemOptionsPopup = ({ }, { icon: ( - + ), diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/layout/NavigationPanel/NavigationPanel.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/layout/NavigationPanel/NavigationPanel.tsx index bd3c7b9cdb..4a1cda6985 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/layout/NavigationPanel/NavigationPanel.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/layout/NavigationPanel/NavigationPanel.tsx @@ -30,6 +30,7 @@ export const NavigationPanel = ({ const { pathname } = currentLocation; const parts = pathname.split('/'); const pageId = parts[parts.length - 1]; + setActivePageId(pageId); }, [currentLocation]); @@ -46,7 +47,7 @@ export const NavigationPanel = ({ return ( <>
-
+
{/**/} - - - + {/**/} + {/**/} + {/**/} {/*Trash Button*/} @@ -100,10 +101,11 @@ const WorkspaceApps: React.FC<{ pages: IPage[] }> = ({ pages }) => ( export const TestBackendButton = () => { const navigate = useNavigate(); + return ( @@ -116,7 +118,7 @@ export const DesignSpec = () => { return ( @@ -125,10 +127,11 @@ export const DesignSpec = () => { export const AllIcons = () => { const navigate = useNavigate(); + return ( diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/layout/NavigationPanel/NewPagePopup.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/layout/NavigationPanel/NewPagePopup.tsx index 47ad2f8e9b..f37b890c68 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/layout/NavigationPanel/NewPagePopup.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/layout/NavigationPanel/NewPagePopup.tsx @@ -19,7 +19,7 @@ export const NewPagePopup = ({ const items: IPopupItem[] = [ { icon: ( - + ), @@ -28,7 +28,7 @@ export const NewPagePopup = ({ }, { icon: ( - + ), @@ -37,7 +37,7 @@ export const NewPagePopup = ({ }, { icon: ( - + ), diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/layout/NavigationPanel/NewViewButton.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/layout/NavigationPanel/NewViewButton.tsx index 7a51e17855..b47d126d95 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/layout/NavigationPanel/NewViewButton.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/layout/NavigationPanel/NewViewButton.tsx @@ -10,10 +10,10 @@ export const NewViewButton = ({ scrollDown }: { scrollDown: () => void }) => { void onNewRootView(); scrollDown(); }} - className={'flex h-[50px] w-full items-center px-6 hover:bg-surface-2'} + className={'flex h-[50px] w-full items-center px-6 hover:bg-fill-active'} > -
-
+
+
diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/layout/NavigationPanel/PluginsButton.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/layout/NavigationPanel/PluginsButton.tsx index 48ec3239d8..ee74aa1cb2 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/layout/NavigationPanel/PluginsButton.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/layout/NavigationPanel/PluginsButton.tsx @@ -1,6 +1,6 @@ export const PluginsButton = () => { return ( - diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/layout/NavigationPanel/TrashButton.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/layout/NavigationPanel/TrashButton.tsx index ed2d34f0fa..7cf58f7859 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/layout/NavigationPanel/TrashButton.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/layout/NavigationPanel/TrashButton.tsx @@ -1,8 +1,13 @@ +import { DeleteForeverOutlined } from '@mui/icons-material'; +import { TrashSvg } from '$app/components/_shared/svg/TrashSvg'; + export const TrashButton = () => { return ( - ); }; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/layout/Screen.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/layout/Screen.tsx index 950f960921..7ea57083ce 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/layout/Screen.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/layout/Screen.tsx @@ -10,7 +10,7 @@ export const Screen = ({ children }: { children: ReactNode }) => { const { width, onHideMenuClick, onShowMenuClick, menuHidden } = useNavigationPanelHooks(); return ( -
+
diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/layout/UserSetting/AppearanceSetting.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/layout/UserSetting/AppearanceSetting.tsx new file mode 100644 index 0000000000..0ae8cece53 --- /dev/null +++ b/frontend/appflowy_tauri/src/appflowy_app/components/layout/UserSetting/AppearanceSetting.tsx @@ -0,0 +1,122 @@ +import React, { useCallback, useEffect, useMemo } from 'react'; +import Select from '@mui/material/Select'; +import { Theme, ThemeMode, UserSetting } from '$app/interfaces'; +import MenuItem from '@mui/material/MenuItem'; + +function AppearanceSetting({ + theme = Theme.Default, + themeMode = ThemeMode.Light, + onChange, +}: { + theme?: Theme; + themeMode?: ThemeMode; + onChange: (setting: UserSetting) => void; +}) { + useEffect(() => { + const html = document.documentElement; + + html?.setAttribute('data-dark-mode', String(themeMode === ThemeMode.Dark)); + html?.setAttribute('data-theme', theme); + }, [theme, themeMode]); + + const themeModeOptions = useMemo( + () => [ + { + value: ThemeMode.Light, + content: 'Light', + }, + { + value: ThemeMode.Dark, + content: 'Dark', + }, + ], + [] + ); + + const themeOptions = useMemo( + () => [ + { + value: Theme.Default, + content: 'Default', + }, + { + value: Theme.Dandelion, + content: 'Dandelion', + }, + { + value: Theme.Lavender, + content: 'Lavender', + }, + ], + [] + ); + + const renderSelect = useCallback( + ( + items: { + options: { value: ThemeMode | Theme; content: string }[]; + label: string; + value: ThemeMode | Theme; + onChange: (newValue: ThemeMode | Theme) => void; + }[] + ) => { + return items.map((item) => { + const { value, options, label, onChange } = item; + + return ( +
+
{label}
+
+ +
+
+ ); + }); + }, + [] + ); + + return ( +
+ {renderSelect([ + { + options: themeModeOptions, + label: 'Theme Mode', + value: themeMode, + onChange: (newValue) => { + onChange({ + themeMode: newValue as ThemeMode, + }); + }, + }, + { + options: themeOptions, + label: 'Theme', + value: theme, + onChange: (newValue) => { + onChange({ + theme: newValue as Theme, + }); + }, + }, + ])} +
+ ); +} + +export default AppearanceSetting; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/layout/UserSetting/LanguageSetting.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/layout/UserSetting/LanguageSetting.tsx new file mode 100644 index 0000000000..cf4ad172a1 --- /dev/null +++ b/frontend/appflowy_tauri/src/appflowy_app/components/layout/UserSetting/LanguageSetting.tsx @@ -0,0 +1,7 @@ +import React from 'react'; + +function LanguageSetting() { + return
; +} + +export default LanguageSetting; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/layout/UserSetting/Menu.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/layout/UserSetting/Menu.tsx new file mode 100644 index 0000000000..19ac2dfa97 --- /dev/null +++ b/frontend/appflowy_tauri/src/appflowy_app/components/layout/UserSetting/Menu.tsx @@ -0,0 +1,48 @@ +import React, { useMemo } from 'react'; +import LanguageIcon from '@mui/icons-material/Language'; +import PaletteOutlined from '@mui/icons-material/PaletteOutlined'; + +export enum MenuItem { + Appearance = 'Appearance', + Language = 'Language', +} + +function UserSettingMenu({ selected, onSelect }: { onSelect: (selected: MenuItem) => void; selected: MenuItem }) { + const options = useMemo(() => { + return [ + { + label: 'Appearance', + value: MenuItem.Appearance, + icon: , + }, + { + label: 'Language', + value: MenuItem.Language, + icon: , + }, + ]; + }, []); + + return ( +
+ {options.map((option) => { + return ( +
{ + onSelect(option.value); + }} + className={`my-1 flex h-10 w-full cursor-pointer items-center justify-start rounded-md px-4 py-2 text-text-title ${ + selected === option.value ? 'bg-fill-hover' : 'hover:bg-fill-hover' + }`} + > +
{option.icon}
+
{option.label}
+
+ ); + })} +
+ ); +} + +export default UserSettingMenu; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/layout/UserSetting/SettingPanel.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/layout/UserSetting/SettingPanel.tsx new file mode 100644 index 0000000000..e5285fe5d4 --- /dev/null +++ b/frontend/appflowy_tauri/src/appflowy_app/components/layout/UserSetting/SettingPanel.tsx @@ -0,0 +1,37 @@ +import React, { useMemo } from 'react'; +import { MenuItem } from './Menu'; +import AppearanceSetting from '$app/components/layout/UserSetting/AppearanceSetting'; +import LanguageSetting from '$app/components/layout/UserSetting/LanguageSetting'; + +import { UserSetting } from '$app/interfaces'; + +function UserSettingPanel({ + selected, + userSettingState = {}, + onChange, +}: { + selected: MenuItem; + userSettingState?: UserSetting; + onChange: (setting: Partial) => void; +}) { + const { theme, themeMode } = userSettingState; + + const options = useMemo(() => { + return [ + { + value: MenuItem.Appearance, + content: , + }, + { + value: MenuItem.Language, + icon: , + }, + ]; + }, [onChange, theme, themeMode]); + + const option = options.find((option) => option.value === selected); + + return
{option?.content}
; +} + +export default UserSettingPanel; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/layout/UserSetting/index.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/layout/UserSetting/index.tsx new file mode 100644 index 0000000000..73d9ba4d36 --- /dev/null +++ b/frontend/appflowy_tauri/src/appflowy_app/components/layout/UserSetting/index.tsx @@ -0,0 +1,61 @@ +import React, { useCallback, useState } from 'react'; +import Dialog from '@mui/material/Dialog'; +import DialogContent from '@mui/material/DialogContent'; +import DialogTitle from '@mui/material/DialogTitle'; +import Slide, { SlideProps } from '@mui/material/Slide'; +import UserSettingMenu, { MenuItem } from '$app/components/layout/UserSetting/Menu'; +import UserSettingPanel from '$app/components/layout/UserSetting/SettingPanel'; +import { Theme, UserSetting } from '$app/interfaces'; +import { useAppDispatch, useAppSelector } from '$app/stores/store'; +import { currentUserActions } from '$app_reducers/current-user/slice'; +import { useUserSettingControllerContext } from '$app/components/_shared/app-hooks/useUserSettingControllerContext'; +import { ThemeModePB } from '@/services/backend'; + +const SlideTransition = React.forwardRef((props: SlideProps, ref) => { + return ; +}); + +function UserSettings({ open, onClose }: { open: boolean; onClose: () => void }) { + const userSettingState = useAppSelector((state) => state.currentUser.userSetting); + const dispatch = useAppDispatch(); + const userSettingController = useUserSettingControllerContext(); + + const [selected, setSelected] = useState(MenuItem.Appearance); + const handleChange = useCallback( + (setting: Partial) => { + const newSetting = { ...userSettingState, ...setting }; + + dispatch(currentUserActions.setUserSetting(newSetting)); + if (userSettingController) { + userSettingController.setAppearanceSetting({ + theme: newSetting.theme || Theme.Default, + mode: newSetting.themeMode || ThemeModePB.Light, + }); + } + }, + [dispatch, userSettingController, userSettingState] + ); + + return ( + e.stopPropagation()} + open={open} + TransitionComponent={SlideTransition} + keepMounted + onClose={onClose} + > + {'Settings'} + + { + setSelected(selected); + }} + selected={selected} + /> + + + + ); +} + +export default UserSettings; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/layout/WorkspaceUser.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/layout/WorkspaceUser.tsx index 84e8de5fb4..0924ed5a75 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/layout/WorkspaceUser.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/layout/WorkspaceUser.tsx @@ -1,17 +1,40 @@ import { useAppSelector } from '$app/stores/store'; +import UserSetting from '$app/components/layout/UserSetting'; +import { useState } from 'react'; +import PersonOutline from '@mui/icons-material/PersonOutline'; +import { Avatar, IconButton } from '@mui/material'; +import ArrowDropDown from '@mui/icons-material/ArrowDropDown'; export const WorkspaceUser = () => { const currentUser = useAppSelector((state) => state.currentUser); + const [showUserSetting, setShowUserSetting] = useState(false); return (
- - +
{ + e.stopPropagation(); + setShowUserSetting(!showUserSetting); + }} + className={'flex cursor-pointer items-center pl-4 text-text-title'} + > + + + + {currentUser.displayName} + +
+ + setShowUserSetting(false)} />
); }; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/tests/ColorPalette.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/tests/ColorPalette.tsx index c683898029..30ef2efa22 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/tests/ColorPalette.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/tests/ColorPalette.tsx @@ -4,16 +4,16 @@ export const ColorPalette = () => {

Colors

Main

-
-
-
-
-
-
-
+
+
+
+
+
+
+

Tint

-
+
@@ -26,18 +26,18 @@ export const ColorPalette = () => {

Shades

-
-
-
-
-
-
+
+
+
+
+
+

Surface

-
-
-
+
+
+
diff --git a/frontend/appflowy_tauri/src/appflowy_app/interfaces/document.ts b/frontend/appflowy_tauri/src/appflowy_app/interfaces/document.ts index 9052a156f5..777ba33231 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/interfaces/document.ts +++ b/frontend/appflowy_tauri/src/appflowy_app/interfaces/document.ts @@ -289,6 +289,7 @@ export interface RangeStaticNoId { export interface CodeEditorProps extends EditorProps { language: string; + isDark: boolean; } export interface EditorProps { isCodeBlock?: boolean; diff --git a/frontend/appflowy_tauri/src/appflowy_app/interfaces/index.ts b/frontend/appflowy_tauri/src/appflowy_app/interfaces/index.ts index db6c7f48b3..fe3c209491 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/interfaces/index.ts +++ b/frontend/appflowy_tauri/src/appflowy_app/interfaces/index.ts @@ -1 +1,15 @@ -export interface Document {} \ No newline at end of file +import { ThemeModePB as ThemeMode } from '@/services/backend'; + +export { ThemeMode }; +export interface Document {} + +export interface UserSetting { + theme?: Theme; + themeMode?: ThemeMode; +} + +export enum Theme { + Default = 'default', + Dandelion = 'dandelion', + Lavender = 'lavender', +} diff --git a/frontend/appflowy_tauri/src/appflowy_app/stores/effects/user/user_bd_svc.ts b/frontend/appflowy_tauri/src/appflowy_app/stores/effects/user/user_bd_svc.ts index d19d3eb55d..b473550bcb 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/stores/effects/user/user_bd_svc.ts +++ b/frontend/appflowy_tauri/src/appflowy_app/stores/effects/user/user_bd_svc.ts @@ -1,8 +1,13 @@ import { nanoid } from '@reduxjs/toolkit'; import { + AppearanceSettingsPB, AuthTypePB, SignOutPB, + ThemeModePB, + UserEventGetAppearanceSetting, UserEventGetUserProfile, + UserEventGetUserSetting, + UserEventSetAppearanceSetting, UserEventSignIn, UserEventSignOut, UserEventSignUp, @@ -89,6 +94,23 @@ export class UserBackendService { return UserEventSignOut(payload); }; + + setAppearanceSettings = (params: { theme: string; mode: ThemeModePB }) => { + const payload = AppearanceSettingsPB.fromObject({ + theme: params.theme, + theme_mode: params.mode, + }); + + return UserEventSetAppearanceSetting(payload); + }; + + getAppearanceSettings = () => { + return UserEventGetAppearanceSetting(); + }; + + getStorageSettings = () => { + return UserEventGetUserSetting(); + }; } export class AuthBackendService { diff --git a/frontend/appflowy_tauri/src/appflowy_app/stores/effects/user/user_setting_controller.ts b/frontend/appflowy_tauri/src/appflowy_app/stores/effects/user/user_setting_controller.ts new file mode 100644 index 0000000000..6c3c668ab5 --- /dev/null +++ b/frontend/appflowy_tauri/src/appflowy_app/stores/effects/user/user_setting_controller.ts @@ -0,0 +1,39 @@ +import { UserBackendService } from '$app/stores/effects/user/user_bd_svc'; +import { AppearanceSettingsPB, ThemeModePB } from '@/services/backend'; + +export class UserSettingController { + private readonly backendService: UserBackendService; + constructor(private userId: number) { + this.backendService = new UserBackendService(userId); + } + + getStorageSettings = async () => { + const userSetting = await this.backendService.getStorageSettings(); + + if (userSetting.ok) { + return userSetting.val; + } + + return {}; + }; + + getAppearanceSetting = async (): Promise => { + const appearanceSetting = await this.backendService.getAppearanceSettings(); + + if (appearanceSetting.ok) { + return appearanceSetting.val; + } + + return; + }; + + setAppearanceSetting = async (params: { theme: string; mode: ThemeModePB }) => { + const res = await this.backendService.setAppearanceSettings(params); + + if (res.ok) { + return res.val; + } + + return Promise.reject(res.err); + }; +} diff --git a/frontend/appflowy_tauri/src/appflowy_app/stores/reducers/current-user/slice.ts b/frontend/appflowy_tauri/src/appflowy_app/stores/reducers/current-user/slice.ts index be68ddf6c9..f7a245ad60 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/stores/reducers/current-user/slice.ts +++ b/frontend/appflowy_tauri/src/appflowy_app/stores/reducers/current-user/slice.ts @@ -1,6 +1,7 @@ import { createSlice, PayloadAction } from '@reduxjs/toolkit'; import { nanoid } from 'nanoid'; import { WorkspaceSettingPB } from '@/services/backend/models/flowy-folder2/workspace'; +import { UserSetting } from '$app/interfaces'; export interface ICurrentUser { id?: number; @@ -9,25 +10,37 @@ export interface ICurrentUser { token?: string; isAuthenticated: boolean; workspaceSetting?: WorkspaceSettingPB; + userSetting: UserSetting; } const initialState: ICurrentUser | null = { isAuthenticated: false, + userSetting: {}, }; export const currentUserSlice = createSlice({ name: 'currentUser', initialState: initialState, reducers: { - checkUser: (state, action: PayloadAction) => { - return action.payload; + checkUser: (state, action: PayloadAction>) => { + return { + ...state, + ...action.payload, + }; }, - updateUser: (state, action: PayloadAction) => { - return action.payload; + updateUser: (state, action: PayloadAction>) => { + return { + ...state, + ...action.payload, + }; }, logout: () => { - return { - isAuthenticated: false, + return initialState; + }, + setUserSetting: (state, action: PayloadAction>) => { + state.userSetting = { + ...state.userSetting, + ...action.payload, }; }, }, diff --git a/frontend/appflowy_tauri/src/appflowy_app/stores/reducers/document/async-actions/link.ts b/frontend/appflowy_tauri/src/appflowy_app/stores/reducers/document/async-actions/link.ts index 6e5fab053e..42f04b51b3 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/stores/reducers/document/async-actions/link.ts +++ b/frontend/appflowy_tauri/src/appflowy_app/stores/reducers/document/async-actions/link.ts @@ -28,7 +28,7 @@ export const formatLinkThunk = createAsyncThunk< const length = selection.length || 0; const regex = new RegExp(/^(https?:\/\/)?([\da-z.-]+)\.([a-z.]{2,6})([/\w .-]*)*\/?$/); - if (href !== undefined && !regex.test(href)) { + if (href && !regex.test(href)) { return false; } diff --git a/frontend/appflowy_tauri/src/appflowy_app/stores/reducers/document/slice.ts b/frontend/appflowy_tauri/src/appflowy_app/stores/reducers/document/slice.ts index 2b095a024c..238e7f4fe6 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/stores/reducers/document/slice.ts +++ b/frontend/appflowy_tauri/src/appflowy_app/stores/reducers/document/slice.ts @@ -409,7 +409,10 @@ export const linkPopoverSlice = createSlice({ const { id } = linkState; if (!state[docId].open || state[docId].id !== id) return; - state[docId] = linkState; + state[docId] = { + ...state[docId], + ...linkState, + }; }, closeLinkPopover: (state, action: PayloadAction) => { const docId = action.payload; diff --git a/frontend/appflowy_tauri/src/appflowy_app/utils/mui.ts b/frontend/appflowy_tauri/src/appflowy_app/utils/mui.ts new file mode 100644 index 0000000000..f31a6dd280 --- /dev/null +++ b/frontend/appflowy_tauri/src/appflowy_app/utils/mui.ts @@ -0,0 +1,51 @@ +import { ThemeMode } from '$app/interfaces'; +import { ThemeOptions } from '@mui/material'; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore +export const getDesignTokens = (mode: ThemeMode): ThemeOptions => { + const isDark = mode === ThemeMode.Dark; + + return { + typography: { + fontFamily: ['Poppins'].join(','), + fontSize: 12, + button: { + textTransform: 'none', + }, + }, + palette: { + mode: isDark ? 'dark' : 'light', + primary: { + main: '#00BCF0', + dark: '#00BCF0', + }, + error: { + main: '#FB006D', + dark: '#D32772', + }, + warning: { + main: '#FFC107', + dark: '#E9B320', + }, + info: { + main: '#00BCF0', + dark: '#2E9DBB', + }, + success: { + main: '#66CF80', + dark: '#3BA856', + }, + text: { + primary: isDark ? '#E2E9F2' : '#333333', + secondary: isDark ? '#7B8A9D' : '#828282', + disabled: isDark ? '#363D49' : '#F2F2F2', + }, + divider: isDark ? '#59647A' : '#BDBDBD', + background: { + default: isDark ? '#1A202C' : '#FFFFFF', + paper: isDark ? '#1A202C' : '#FFFFFF', + }, + }, + }; +}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/views/DocumentPage.tsx b/frontend/appflowy_tauri/src/appflowy_app/views/DocumentPage.tsx index 8b5fd9d30a..a02a7ec6ce 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/views/DocumentPage.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/views/DocumentPage.tsx @@ -1,33 +1,14 @@ import { useDocument } from './DocumentPage.hooks'; -import { createTheme, ThemeProvider } from '@mui/material'; import Root from '../components/document/Root'; import { DocumentControllerContext } from '../stores/effects/document/document_controller'; -const muiTheme = createTheme({ - typography: { - fontFamily: ['Poppins'].join(','), - fontSize: 12, - button: { - textTransform: 'none', - }, - }, - palette: { - primary: { - main: '#00BCF0', - light: '#00BCF0', - }, - }, -}); - export const DocumentPage = () => { const { documentId, documentData, controller } = useDocument(); if (!documentId || !documentData || !controller) return null; return ( - - - - - + + + ); }; diff --git a/frontend/appflowy_tauri/src/styles/Calendar.css b/frontend/appflowy_tauri/src/styles/Calendar.css index 324590d2f2..e680d957ff 100644 --- a/frontend/appflowy_tauri/src/styles/Calendar.css +++ b/frontend/appflowy_tauri/src/styles/Calendar.css @@ -59,7 +59,7 @@ } .react-calendar__month-view__weekdays { - @apply mb-2 text-center text-xs uppercase text-shade-3; + @apply mb-2 text-center text-xs uppercase text-text-caption; } .react-calendar__month-view__weekdays abbr { @@ -79,11 +79,11 @@ } .react-calendar__month-view__days__day--weekend { - @apply text-main-alert; + @apply text-text-link-default; } .react-calendar__month-view__days__day--neighboringMonth { - @apply text-shade-4; + @apply text-text-caption; } .react-calendar__year-view .react-calendar__tile, @@ -110,12 +110,12 @@ } .react-calendar__tile--now { - @apply bg-shade-6; + @apply bg-bg-base; } .react-calendar__tile--now:enabled:hover, .react-calendar__tile--now:enabled:focus { - @apply bg-shade-6; + @apply bg-bg-base; } .react-calendar__tile--hasActive { @@ -128,14 +128,14 @@ } .react-calendar__tile--active { - @apply bg-main-accent text-white; + @apply bg-fill-default text-text-title; } .react-calendar__tile--active:enabled:hover, .react-calendar__tile--active:enabled:focus { - @apply bg-main-hovered; + @apply bg-fill-hover; } .react-calendar--selectRange .react-calendar__tile--hover { - @apply bg-shade-4; + @apply bg-bg-base; } diff --git a/frontend/appflowy_tauri/src/styles/color/dark.css b/frontend/appflowy_tauri/src/styles/color/dark.css new file mode 100644 index 0000000000..77c2f886a6 --- /dev/null +++ b/frontend/appflowy_tauri/src/styles/color/dark.css @@ -0,0 +1,21 @@ +:root[data-dark-mode=true] { + --B50: #024165; + --B100: #00709F; + --B200: #A6ECFF; + --B300: #52D1F4; + --B400: #00BCF0; + --B500: #05ADE2; + --B600: #009FD1; + --N00: #1A202C; + --N50: #232B38; + --N100: #3A465A; + --N200: #313C51; + --N300: #363D49; + --N400: #525A69; + --N500: #59647A; + --N600: #7B8A9D; + --N700: #99A6B8; + --N800: #E2E9F2; + --N900: #EFF4FB; + --N1000: #FFFFFF; +} \ No newline at end of file diff --git a/frontend/appflowy_tauri/src/styles/color/default.css b/frontend/appflowy_tauri/src/styles/color/default.css new file mode 100644 index 0000000000..8e478d983c --- /dev/null +++ b/frontend/appflowy_tauri/src/styles/color/default.css @@ -0,0 +1,54 @@ +@import './dark.css'; +@import './light.css'; + +:root { + --color-text-title: var(--N800); + --color-text-caption: var(--N600); + --color-text-placeholder: var(--N500); + --color-text-disabled: var(--N300); + --color-text-link-default: var(--B400); + --color-text-link-hover: var(--B300); + --color-text-link-pressed: var(--B600); + --color-text-link-disabled: var(--B100); + --color-icon-default: var(--N800); + --color-icon-secondary: var(--N500); + --color-icon-disabled: var(--N400); + --color-fill-default: var(--B400); + --color-fill-hover: var(--B100); + --color-fill-selector: var(--B50); + --color-fill-active: var(--N200); + --color-line-divider: var(--N500); + --color-line-border: var(--N100); + --color-bg-body: var(--N00); + --color-bg-base: var(--N50); + --color-bg-mask: rgba(0, 0, 0, 0.6); + --color-bg-tips: var(--B100); + --color-bg-brand: #24144B; + --color-function-error: #FB006D; + --color-function-warning: #FFD667; + --color-function-success: #66CF80; + --color-function-info: #00BCF0; + --color-content-onfill: var(--N00); + --color-content-default: var(--B400); + --color-content-disabled: var(--B100); + --color-content-hover: var(--B300); + --color-content-pressed: var(--B600); + + --color-tint-1: #E8E0FF; + --color-tint-2: #FFE7FD; + --color-tint-3: #FFE7EE; + --color-tint-4: #FFEFE3; + --color-tint-5: #FFF2CD; + --color-tint-6: #F5FFDC; + --color-tint-7: #DDFFD6; + --color-tint-8: #DEFFF1; + --color-tint-9: #E1FBFF; +} + +:root[data-dark-mode=true] { + --color-bg-mask: rgba(0, 0, 0, 0.7); + --color-function-error: #D32772; + --color-function-warning: #E9B320; + --color-function-success: #3BA856; + --color-function-info: #2E9DBB; +} diff --git a/frontend/appflowy_tauri/src/styles/color/light.css b/frontend/appflowy_tauri/src/styles/color/light.css new file mode 100644 index 0000000000..54c05d6339 --- /dev/null +++ b/frontend/appflowy_tauri/src/styles/color/light.css @@ -0,0 +1,21 @@ +:root { + --B50: #F2FCFF; + --B100: #E0F8FF; + --B200: #A6ECFF; + --B300: #52D1F4; + --B400: #00BCF0; + --B500: #05ADE2; + --B600: #009FD1; + --N00: #FFFFFF; + --N50: #F9FAFD; + --N100: #EDEEF2; + --N200: #E2E4EB; + --N300: #F2F2F2; + --N400: #D4D4D5; + --N500: #BDBDBD; + --N600: #828282; + --N700: #4F4F4F; + --N800: #333333; + --N900: #1F2329; + --N1000: #000000; +} \ No newline at end of file diff --git a/frontend/appflowy_tauri/src/styles/mui.css b/frontend/appflowy_tauri/src/styles/mui.css new file mode 100644 index 0000000000..a00bb3bb1d --- /dev/null +++ b/frontend/appflowy_tauri/src/styles/mui.css @@ -0,0 +1,37 @@ + +.MuiDialog-root [class$="-MuiBackdrop-root-MuiDialog-backdrop"] { + background-color: var(--color-bg-mask); +} + +[class$="-MuiPaper-root-MuiPopover-paper"], [class$="-MuiPaper-root-MuiDialog-paper"] { + background-image: none !important; +} + +[class$='-MuiButtonBase-root-MuiMenuItem-root'].MuiButtonBase-root.Mui-selected { + background-color: var(--color-fill-hover); +} + +[class$='-MuiButtonBase-root-MuiMenuItem-root'].MuiButtonBase-root:hover { + background-color: var(--color-fill-hover); +} + +.MuiButtonBase-root.MuiIconButton-root.MuiIconButton-sizeMedium { + color: var(--color-icon-default); + border-radius: 4px; +} + +.MuiButtonBase-root.MuiIconButton-root:hover { + background: var(--color-fill-hover); +} + +.MuiTooltip-tooltip { + background: var(--color-fill-hover) !important; + color: var(--color-text-title) !important; + font-size: 0.85rem !important; + border-radius: 8px !important; + font-weight: 400 !important; +} + +.MuiInput-input[class$='-MuiSelect-select-MuiInputBase-input-MuiInput-input']:focus { + background: transparent; +} \ No newline at end of file diff --git a/frontend/appflowy_tauri/src/styles/switch.css b/frontend/appflowy_tauri/src/styles/switch.css index 6b0592c801..7af7142fe7 100644 --- a/frontend/appflowy_tauri/src/styles/switch.css +++ b/frontend/appflowy_tauri/src/styles/switch.css @@ -9,7 +9,7 @@ margin-right: 0.5rem; width: 46px; height: 26px; - @apply bg-shade-6; + @apply bg-bg-base; border-radius: 23px; vertical-align: text-bottom; transition: all 0.3s linear; @@ -48,7 +48,7 @@ display: none; } .form-switch input:checked + i { - @apply bg-main-accent; + @apply bg-fill-default; } .form-switch input:checked + i::before { transform: translate3d(18px, 2px, 0) scale3d(0, 0, 0); diff --git a/frontend/appflowy_tauri/src/styles/template.css b/frontend/appflowy_tauri/src/styles/template.css index 5887901b76..020474631a 100644 --- a/frontend/appflowy_tauri/src/styles/template.css +++ b/frontend/appflowy_tauri/src/styles/template.css @@ -1,3 +1,6 @@ +@import './color/default.css'; +@import './mui.css'; + /* stop body from scrolling */ html, body { @@ -17,7 +20,7 @@ body { } ::selection { - @apply bg-[#E0F8FF] + @apply bg-fill-hover; } div[role="textbox"] ::selection { @@ -25,21 +28,22 @@ div[role="textbox"] ::selection { } .btn { - @apply rounded-xl border border-gray-500 px-4 py-3; + @apply rounded-xl border border-line-border px-4 py-3; } .btn-primary { - @apply bg-main-accent text-white hover:bg-main-hovered; + @apply bg-fill-default text-text-title hover:bg-fill-hover; } .input { - @apply rounded-xl border border-gray-300 px-[18px] py-[14px] text-sm; + @apply rounded-xl border border-line-border px-[18px] py-[14px] text-sm; } -.input.error { - @apply border-main-alert bg-main-alert/10; -} th { @apply text-left font-normal; } + +span[data-slate-placeholder="true"] { + @apply text-text-placeholder; +} \ No newline at end of file diff --git a/frontend/appflowy_tauri/tailwind.config.cjs b/frontend/appflowy_tauri/tailwind.config.cjs index f72417dcdb..9cf8921d7e 100644 --- a/frontend/appflowy_tauri/tailwind.config.cjs +++ b/frontend/appflowy_tauri/tailwind.config.cjs @@ -9,46 +9,64 @@ module.exports = { theme: { extend: { colors: { - white: '#ffffff', - black: '#000000', - main: { - accent: '#00BCF0', - hovered: '#00B7EA', - secondary: '#E0F8FF', - selector: '#F2FCFF', - alert: '#FB006D', - warning: '#FFD667', - success: '#66CF80', + text: { + title: 'var(--color-text-title)', + caption: 'var(--color-text-caption)', + placeholder: 'var(--color-text-placeholder)', + disabled: 'var(--color-text-disabled)', + link: { + default: 'var(--color-text-link-default)', + hover: 'var(--color-text-link-hover)', + pressed: 'var(--color-text-link-pressed)', + disabled: 'var(--color-text-link-disabled)', + } }, + content: { + default: 'var(--color-content-default)', + hover: 'var(--color-content-hover)', + pressed: 'var(--color-content-pressed)', + disabled: 'var(--color-content-disabled)', + onfill: 'var(--color-content-onfill)', + }, + icon: { + default: 'var(--color-icon-default)', + secondary: 'var(--color-icon-secondary)', + disabled: 'var(--color-icon-disabled)', + }, + fill: { + default: 'var(--color-fill-default)', + hover: 'var(--color-fill-hover)', + selector: 'var(--color-fill-selector)', + active: 'var(--color-fill-active)', + }, + line: { + divider: 'var(--color-line-divider)', + border: 'var(--color-line-border)', + }, + bg: { + body: 'var(--color-bg-body)', + base: 'var(--color-bg-base)', + mask: 'var(--color-bg-mask)', + brand: 'var(--color-bg-brand)', + tips: 'var(--color-bg-tips)', + }, + function: { + success: 'var(--color-function-success)', + warning: 'var(--color-function-warning)', + error: 'var(--color-function-error)', + info: 'var(--color-function-info)', + }, + tint: { - 1: '#E8E0FF', - 2: '#FFE7FD', - 3: '#FFE7EE', - 4: '#FFEFE3', - 5: '#FFF2CD', - 6: '#F5FFDC', - 7: '#DDFFD6', - 8: '#DEFFF1', - 9: '#E1FBFF', - }, - shade: { - 1: '#333333', - 2: '#4F4F4F', - 3: '#828282', - 4: '#BDBDBD', - 5: '#E0E0E0', - 6: '#F2F2F2', - 7: '#FFFFFF', - }, - surface: { - 1: '#F7F8FC', - 2: '#EDEEF2', - 3: '#E2E4EB', - fiol: '#2C144B', - }, - custom: { - code: 'rgba(221, 221, 221, 0.4)', - caret: 'rgb(55, 53, 47)' + 1: 'var(--color-tint-1)', + 2: 'var(--color-tint-2)', + 3: 'var(--color-tint-3)', + 4: 'var(--color-tint-4)', + 5: 'var(--color-tint-5)', + 6: 'var(--color-tint-6)', + 7: 'var(--color-tint-7)', + 8: 'var(--color-tint-8)', + 9: 'var(--color-tint-9)', } }, boxShadow: {