@@ -129,7 +129,13 @@ export const ContactModal = ({ open, onClose }: TransitionsModalProps) => {
- How is AFFiNE Alpha different?
+
+ How is AFFiNE Alpha different?
+
Copyright © 2022 Toeverything
diff --git a/packages/app/src/components/contact-modal/style.ts b/packages/app/src/components/contact-modal/style.ts
index e1bb82d2f..4b67fc98d 100644
--- a/packages/app/src/components/contact-modal/style.ts
+++ b/packages/app/src/components/contact-modal/style.ts
@@ -1,5 +1,7 @@
import { absoluteCenter, displayFlex, styled } from '@/styles';
import bg from './bg.png';
+import CloseIcon from '@mui/icons-material/Close';
+
export const StyledModalContainer = styled('div')(({ theme }) => {
return {
width: '100vw',
@@ -175,20 +177,36 @@ export const StyledModalHeaderLeft = styled('div')(({ theme }) => {
};
});
-export const CloseButton = styled('div')(({ theme }) => {
+export const StyledCloseButton = styled('div')(({ theme }) => {
return {
- width: '30px',
- height: '30px',
- borderRadius: '6px',
+ width: '60px',
+ height: '60px',
color: theme.colors.iconColor,
cursor: 'pointer',
...displayFlex('center', 'center'),
+ position: 'absolute',
+ right: '0',
+ top: '0',
+
+ // TODO: we need to add @emotion/babel-plugin
+ '::after': {
+ content: '""',
+ width: '30px',
+ height: '30px',
+ borderRadius: '6px',
+ ...absoluteCenter({ horizontal: true, vertical: true }),
+ },
':hover': {
- background: theme.colors.hoverBackground,
+ color: theme.colors.primaryColor,
+ '::after': {
+ background: theme.colors.hoverBackground,
+ },
},
svg: {
width: '20px',
height: '20px',
+ position: 'relative',
+ zIndex: 1,
},
};
});
diff --git a/packages/app/src/components/edgeless-toolbar/index.tsx b/packages/app/src/components/edgeless-toolbar/index.tsx
index 61eed107c..2865c2772 100644
--- a/packages/app/src/components/edgeless-toolbar/index.tsx
+++ b/packages/app/src/components/edgeless-toolbar/index.tsx
@@ -14,7 +14,7 @@ import {
UndoIcon,
RedoIcon,
} from './icons';
-import { Tooltip } from '@/components/tooltip';
+import { Tooltip } from '@/ui/tooltip';
import Slide from '@mui/material/Slide';
import { useEditor } from '@/components/editor-provider';
@@ -28,32 +28,32 @@ const toolbarList1 = [
{
flavor: 'text',
icon: ,
- toolTip: 'Text(coming soon)',
+ toolTip: 'Text (coming soon)',
disable: true,
},
{
flavor: 'shape',
icon: ,
- toolTip: 'Shape(coming soon)',
+ toolTip: 'Shape (coming soon)',
disable: true,
},
{
flavor: 'sticky',
icon: ,
- toolTip: 'Sticky(coming soon)',
+ toolTip: 'Sticky (coming soon)',
disable: true,
},
{
flavor: 'pen',
icon: ,
- toolTip: 'Pen(coming soon)',
+ toolTip: 'Pen (coming soon)',
disable: true,
},
{
flavor: 'connector',
icon: ,
- toolTip: 'Connector(coming soon)',
+ toolTip: 'Connector (coming soon)',
disable: true,
},
];
@@ -88,7 +88,7 @@ const UndoRedo = () => {
return (
-
+
{
@@ -98,7 +98,7 @@ const UndoRedo = () => {
-
+
{
diff --git a/packages/app/src/components/edgeless-toolbar/style.ts b/packages/app/src/components/edgeless-toolbar/style.ts
index 2cab9efc6..e8f0e26ef 100644
--- a/packages/app/src/components/edgeless-toolbar/style.ts
+++ b/packages/app/src/components/edgeless-toolbar/style.ts
@@ -1,4 +1,4 @@
-import { styled, displayFlex, fixedCenter } from '@/styles';
+import { styled, displayFlex } from '@/styles';
export const StyledEdgelessToolbar = styled.div(({ theme }) => ({
height: '320px',
@@ -26,7 +26,7 @@ export const StyledToolbarItem = styled.div<{
width: '36px',
height: '36px',
...displayFlex('center', 'center'),
- color: disable ? '#C0C0C0' : theme.colors.iconColor,
+ color: disable ? theme.colors.disableColor : theme.colors.iconColor,
cursor: 'pointer',
svg: {
width: '36px',
diff --git a/packages/app/src/components/editor-mode-switch/index.tsx b/packages/app/src/components/editor-mode-switch/index.tsx
index 94bc5c520..5154dbacb 100644
--- a/packages/app/src/components/editor-mode-switch/index.tsx
+++ b/packages/app/src/components/editor-mode-switch/index.tsx
@@ -38,20 +38,25 @@ const EdgelessItem = ({ active }: { active?: boolean }) => {
const AnimateRadioItem = ({
active,
status,
- icon,
+ icon: propsIcon,
label,
isLeft,
...props
}: AnimateRadioItemProps) => {
+ const icon = (
+
+ {cloneElement(propsIcon, {
+ active,
+ })}
+
+ );
return (
-
- {cloneElement(icon, {
- active,
- })}
-
-
- {label}
+ {isLeft ? icon : null}
+
+ {label}
+
+ {isLeft ? null : icon}
);
};
diff --git a/packages/app/src/components/editor-mode-switch/style.ts b/packages/app/src/components/editor-mode-switch/style.ts
index 5c7620092..82c455c24 100644
--- a/packages/app/src/components/editor-mode-switch/style.ts
+++ b/packages/app/src/components/editor-mode-switch/style.ts
@@ -99,12 +99,23 @@ export const StyledRadioItem = styled('div')<{
export const StyledLabel = styled('div')<{
shrink: boolean;
-}>(({ shrink }) => {
+ isLeft: boolean;
+}>(({ shrink, isLeft }) => {
const animateScaleStretch = keyframes`${toString(
- spring({ scale: 0 }, { scale: 1 }, { preset: 'gentle' })
+ spring(
+ { width: '0px' },
+ { width: isLeft ? '65px' : '75px' },
+ { preset: 'gentle' }
+ )
)}`;
const animateScaleShrink = keyframes(
- `${toString(spring({ scale: 1 }, { scale: 0 }, { preset: 'gentle' }))}`
+ `${toString(
+ spring(
+ { width: isLeft ? '65px' : '75px' },
+ { width: '0px' },
+ { preset: 'gentle' }
+ )
+ )}`
);
const shrinkStyle = shrink
? {
@@ -117,10 +128,12 @@ export const StyledLabel = styled('div')<{
return {
display: 'flex',
alignItems: 'center',
+ justifyContent: isLeft ? 'flex-start' : 'flex-end',
fontSize: '16px',
flexShrink: '0',
transition: `transform ${ANIMATE_DURATION}ms`,
fontWeight: 'normal',
+ overflow: 'hidden',
...shrinkStyle,
};
});
diff --git a/packages/app/src/components/faq/index.tsx b/packages/app/src/components/faq/index.tsx
index 96dddc4b4..e33a35f73 100644
--- a/packages/app/src/components/faq/index.tsx
+++ b/packages/app/src/components/faq/index.tsx
@@ -7,7 +7,7 @@ import {
} from './style';
import { CloseIcon, ContactIcon, HelpIcon, KeyboardIcon } from './icons';
import Grow from '@mui/material/Grow';
-import { Tooltip } from '../tooltip';
+import { Tooltip } from '@/ui/tooltip';
import { useEditor } from '@/components/editor-provider';
import { useModal } from '@/components/global-modal-provider';
import { useTheme } from '@/styles';
diff --git a/packages/app/src/components/shortcuts-modal/style.ts b/packages/app/src/components/shortcuts-modal/style.ts
index c9bf33e30..a38e9da25 100644
--- a/packages/app/src/components/shortcuts-modal/style.ts
+++ b/packages/app/src/components/shortcuts-modal/style.ts
@@ -6,6 +6,7 @@ export const StyledShortcutsModal = styled.div(({ theme }) => ({
paddingBottom: '28px',
backgroundColor: theme.colors.popoverBackground,
boxShadow: theme.shadow.popover,
+ borderRadius: `${theme.radius.popover} 0 ${theme.radius.popover} ${theme.radius.popover}`,
color: theme.colors.popoverColor,
overflow: 'auto',
boxRadius: '10px',
@@ -71,6 +72,7 @@ export const CloseButton = styled('div')(({ theme }) => {
},
':hover': {
background: theme.colors.hoverBackground,
+ color: theme.colors.primaryColor,
},
};
});
diff --git a/packages/app/src/components/tooltip/Container.tsx b/packages/app/src/components/tooltip/Container.tsx
deleted file mode 100644
index 9f1f19aee..000000000
--- a/packages/app/src/components/tooltip/Container.tsx
+++ /dev/null
@@ -1,29 +0,0 @@
-import { styled } from '@/styles';
-import type { ReactNode, CSSProperties } from 'react';
-import type { PopoverDirection } from './interface';
-export interface PopoverContainerProps {
- children?: ReactNode;
- /**
- * The pop-up window points to. The pop-up window has three rounded corners, one is a right angle, and the right angle is the direction of the pop-up window.
- */
- direction: PopoverDirection;
- style?: CSSProperties;
-}
-const border_radius_map: Record = {
- none: '10px',
- 'left-top': '0 10px 10px 10px',
- 'left-bottom': '10px 10px 10px 0',
- 'right-top': '10px 0 10px 10px',
- 'right-bottom': '10px 10px 0 10px',
-};
-
-export const PopoverContainer = styled('div')<
- Pick
->(({ direction, style }) => {
- const borderRadius =
- border_radius_map[direction] || border_radius_map['left-top'];
- return {
- borderRadius: borderRadius,
- ...style,
- };
-});
diff --git a/packages/app/src/components/tooltip/Tooltip.tsx b/packages/app/src/components/tooltip/Tooltip.tsx
deleted file mode 100644
index 6fabed3f6..000000000
--- a/packages/app/src/components/tooltip/Tooltip.tsx
+++ /dev/null
@@ -1,65 +0,0 @@
-import { type CSSProperties, type PropsWithChildren } from 'react';
-import { PopoverContainer } from './Container';
-import { Popper, type PopperProps } from '../popper';
-import { useTheme } from '@/styles';
-import type { PopperPlacementType, TooltipProps } from '@mui/material';
-import type { PopoverDirection } from './interface';
-export const placementToContainerDirection: Record<
- PopperPlacementType,
- PopoverDirection
-> = {
- top: 'none',
- 'top-start': 'left-bottom',
- 'top-end': 'right-bottom',
- right: 'none',
- 'right-start': 'left-top',
- 'right-end': 'left-bottom',
- bottom: 'none',
- 'bottom-start': 'left-top',
- 'bottom-end': 'right-top',
- left: 'none',
- 'left-start': 'right-top',
- 'left-end': 'right-bottom',
- auto: 'none',
- 'auto-start': 'none',
- 'auto-end': 'none',
-};
-
-const useTooltipStyle = (): CSSProperties => {
- const { theme, mode } = useTheme();
- return {
- boxShadow: '1px 1px 4px rgba(0, 0, 0, 0.14)',
- padding: '4px 12px',
- backgroundColor:
- mode === 'dark'
- ? theme.colors.popoverBackground
- : theme.colors.primaryColor,
- color: '#fff',
- fontSize: theme.font.xs,
- };
-};
-
-export const Tooltip = (
- props: PropsWithChildren>
-) => {
- const { content, placement = 'top-start' } = props;
- const style = useTooltipStyle();
- // If there is no content, hide forever
- const visibleProp = content ? {} : { visible: false };
- return (
-
- {content}
-
- }
- />
- );
-};
diff --git a/packages/app/src/components/tooltip/interface.ts b/packages/app/src/components/tooltip/interface.ts
deleted file mode 100644
index 73e08c5ad..000000000
--- a/packages/app/src/components/tooltip/interface.ts
+++ /dev/null
@@ -1,9 +0,0 @@
-export type TooltipProps = {
- showArrow?: boolean;
-};
-export type PopoverDirection =
- | 'none'
- | 'left-top'
- | 'left-bottom'
- | 'right-top'
- | 'right-bottom';
diff --git a/packages/app/src/pages/_app.tsx b/packages/app/src/pages/_app.tsx
index 7b009515c..f1ff7254e 100644
--- a/packages/app/src/pages/_app.tsx
+++ b/packages/app/src/pages/_app.tsx
@@ -17,13 +17,13 @@ function MyApp({ Component, pageProps }: AppProps) {
return (
<>
-
-
-
+
+
+
-
-
-
+
+
+
>
);
}
diff --git a/packages/app/src/styles/index.ts b/packages/app/src/styles/index.ts
index 7c6781815..d65281b88 100644
--- a/packages/app/src/styles/index.ts
+++ b/packages/app/src/styles/index.ts
@@ -2,6 +2,6 @@ export type { ThemeMode, ThemeProviderProps, AffineTheme } from './types';
export * from './styled';
export { ThemeProvider } from './themeProvider';
-export { lightTheme, darkTheme } from './theme';
+export * from './theme';
export { useTheme } from './hooks';
export * from './helper';
diff --git a/packages/app/src/styles/theme.ts b/packages/app/src/styles/theme.ts
index 253946632..885ddfce8 100644
--- a/packages/app/src/styles/theme.ts
+++ b/packages/app/src/styles/theme.ts
@@ -1,83 +1,108 @@
import '@emotion/react';
import { AffineTheme, AffineThemeCSSVariables, ThemeMode } from './types';
+import { EditorContainer } from '@blocksuite/editor';
const basicFontFamily =
'apple-system, BlinkMacSystemFont,Helvetica Neue, Tahoma, PingFang SC, Microsoft Yahei, Arial,Hiragino Sans GB, sans-serif, Apple Color Emoji, Segoe UI Emoji,Segoe UI Symbol, Noto Color Emoji';
-export const lightTheme: AffineTheme = {
- colors: {
- primaryColor: '#6880FF',
+export const getLightTheme = (
+ editorMode: EditorContainer['mode']
+): AffineTheme => {
+ return {
+ mode: 'light',
+ editorMode,
+ colors: {
+ primaryColor: '#6880FF',
- pageBackground: '#fff',
- hoverBackground: '#F1F3FF',
- popoverBackground: '#fff',
- codeBackground: '#f2f5f9',
+ pageBackground: '#fff',
+ hoverBackground: '#F1F3FF',
+ popoverBackground: '#fff',
+ codeBackground: '#f2f5f9',
- textColor: '#3A4C5C',
- edgelessTextColor: '#3A4C5C',
- iconColor: '#9096A5',
- linkColor: '#6880FF',
- linkColor2: '#6880FF',
- linkVisitedColor: '#ABB8FE',
- popoverColor: '#4C6275',
- codeColor: '#517ea6',
- quoteColor: '#4C6275',
- placeHolderColor: '#C7C7C7',
- selectedColor: 'rgba(104, 128, 255, 0.1)',
- borderColor: '#D0D7E3',
- },
- font: {
- xs: '12px',
- sm: '16px',
- base: '18px',
- family: `Avenir Next, ${basicFontFamily}`,
- family2: `Space Mono, ${basicFontFamily}`,
- lineHeightBase: '26px',
- },
- zIndex: {
- modal: 1000,
- popover: 100,
- },
- shadow: {
- popover:
- '4px 4px 7px rgba(58, 76, 92, 0.04), -4px -4px 13px rgba(58, 76, 92, 0.02), 6px 6px 36px rgba(58, 76, 92, 0.06);',
- modal:
- '4px 4px 7px rgba(58, 76, 92, 0.04), -4px -4px 13px rgba(58, 76, 92, 0.02), 6px 6px 36px rgba(58, 76, 92, 0.06);',
- },
- space: {
- paragraph: '8px',
- },
+ textColor: '#3A4C5C',
+ edgelessTextColor: '#3A4C5C',
+ iconColor: '#9096A5',
+ linkColor: '#6880FF',
+ linkColor2: '#6880FF',
+ linkVisitedColor: '#ABB8FE',
+ popoverColor: '#4C6275',
+ codeColor: '#517ea6',
+ quoteColor: '#4C6275',
+ placeHolderColor: '#C7C7C7',
+ selectedColor: 'rgba(104, 128, 255, 0.1)',
+ borderColor: '#D0D7E3',
+ disableColor: '#C0C0C0',
+ },
+ font: {
+ xs: '12px',
+ sm: '16px',
+ base: '18px',
+ family: `Avenir Next, ${basicFontFamily}`,
+ family2: `Space Mono, ${basicFontFamily}`,
+ lineHeightBase: '26px',
+ },
+ zIndex: {
+ modal: 1000,
+ popover: 100,
+ },
+ shadow: {
+ popover:
+ '4px 4px 7px rgba(58, 76, 92, 0.04), -4px -4px 13px rgba(58, 76, 92, 0.02), 6px 6px 36px rgba(58, 76, 92, 0.06);',
+ modal:
+ '4px 4px 7px rgba(58, 76, 92, 0.04), -4px -4px 13px rgba(58, 76, 92, 0.02), 6px 6px 36px rgba(58, 76, 92, 0.06);',
+ tooltip: '1px 1px 4px rgba(0, 0, 0, 0.14)',
+ },
+ space: {
+ paragraph: '8px',
+ },
+ radius: {
+ popover: '10px',
+ },
+ };
};
-export const darkTheme: AffineTheme = {
- ...lightTheme,
- colors: {
- primaryColor: '#6880FF',
+export const getDarkTheme = (
+ editorMode: EditorContainer['mode']
+): AffineTheme => {
+ const lightTheme = getLightTheme(editorMode);
- pageBackground: '#2c2c2c',
- hoverBackground: '#3C3C42',
- popoverBackground: '#1F2021',
- codeBackground: '#505662',
+ return {
+ ...lightTheme,
+ mode: 'dark',
+ colors: {
+ primaryColor: '#6880FF',
- textColor: '#fff',
- edgelessTextColor: '#3A4C5C',
- iconColor: '#9096A5',
- linkColor: '#7D91FF',
- linkColor2: '#6880FF',
- linkVisitedColor: '#505FAB',
- popoverColor: '#C6CBD9',
- codeColor: '#BDDBFD',
- quoteColor: '#C6CBD9',
- placeHolderColor: '#C7C7C7',
- selectedColor: 'rgba(240, 242, 255, 0.8)',
- borderColor: '#4D4C53',
- },
- shadow: {
- popover:
- '0px 1px 10px -6px rgba(24, 39, 75, 0.08), 0px 3px 16px -6px rgba(24, 39, 75, 0.04)',
- modal:
- '0px 1px 10px -6px rgba(24, 39, 75, 0.08), 0px 3px 16px -6px rgba(24, 39, 75, 0.04)',
- },
+ pageBackground: '#2c2c2c',
+ hoverBackground: '#3C3C42',
+ popoverBackground: '#1F2021',
+ codeBackground:
+ editorMode === 'edgeless'
+ ? lightTheme.colors.codeBackground
+ : '#505662',
+
+ textColor: '#fff',
+ edgelessTextColor: '#3A4C5C',
+ iconColor: '#9096A5',
+ linkColor: '#7D91FF',
+ linkColor2: '#6880FF',
+ linkVisitedColor: '#505FAB',
+ popoverColor: '#C6CBD9',
+ codeColor:
+ editorMode === 'edgeless' ? lightTheme.colors.codeColor : '#BDDBFD',
+ quoteColor: '#C6CBD9',
+ placeHolderColor: '#C7C7C7',
+ selectedColor: 'rgba(104, 128, 255, 0.1)',
+ borderColor: '#4D4C53',
+ disableColor: '#4b4b4b',
+ },
+ shadow: {
+ popover:
+ '0px 1px 10px -6px rgba(24, 39, 75, 0.08), 0px 3px 16px -6px rgba(24, 39, 75, 0.04)',
+ modal:
+ '0px 1px 10px -6px rgba(24, 39, 75, 0.08), 0px 3px 16px -6px rgba(24, 39, 75, 0.04)',
+ tooltip: '1px 1px 4px rgba(0, 0, 0, 0.14)',
+ },
+ };
};
export const globalThemeVariables: (
@@ -85,6 +110,9 @@ export const globalThemeVariables: (
theme: AffineTheme
) => AffineThemeCSSVariables = (mode, theme) => {
return {
+ '--affine-theme-mode': theme.mode,
+ '--affine-editor-mode': theme.editorMode,
+
'--affine-primary-color': theme.colors.primaryColor,
'--affine-page-background': theme.colors.pageBackground,
@@ -105,9 +133,11 @@ export const globalThemeVariables: (
'--affine-selected-color': theme.colors.selectedColor,
'--affine-placeholder-color': theme.colors.placeHolderColor,
'--affine-border-color': theme.colors.borderColor,
+ '--affine-disable-color': theme.colors.disableColor,
'--affine-modal-shadow': theme.shadow.modal,
'--affine-popover-shadow': theme.shadow.popover,
+ '--affine-tooltip-shadow': theme.shadow.tooltip,
'--affine-font-xs': theme.font.xs, // tiny
'--affine-font-sm': theme.font.sm, // small
@@ -121,5 +151,6 @@ export const globalThemeVariables: (
'--affine-font-family2': theme.font.family2,
'--affine-paragraph-space': theme.space.paragraph,
+ '--affine-popover-radius': theme.radius.popover,
};
};
diff --git a/packages/app/src/styles/themeProvider.tsx b/packages/app/src/styles/themeProvider.tsx
index 097e0d5c7..806a52ffe 100644
--- a/packages/app/src/styles/themeProvider.tsx
+++ b/packages/app/src/styles/themeProvider.tsx
@@ -11,13 +11,14 @@ import {
ThemeProviderProps,
ThemeProviderValue,
} from './types';
-import { lightTheme, darkTheme, globalThemeVariables } from './theme';
+import { getLightTheme, getDarkTheme, globalThemeVariables } from './theme';
import { SystemThemeHelper, localStorageThemeHelper } from './utils';
+import { useEditor } from '@/components/editor-provider';
export const ThemeContext = createContext({
mode: 'light',
changeMode: () => {},
- theme: lightTheme,
+ theme: getLightTheme('page'),
});
export const ThemeProvider = ({
@@ -26,8 +27,9 @@ export const ThemeProvider = ({
}: PropsWithChildren) => {
const [theme, setTheme] = useState(defaultTheme);
const [mode, setMode] = useState('auto');
-
- const themeStyle = theme === 'light' ? lightTheme : darkTheme;
+ const { mode: editorMode } = useEditor();
+ const themeStyle =
+ theme === 'light' ? getLightTheme(editorMode) : getDarkTheme(editorMode);
const changeMode = (themeMode: ThemeMode) => {
themeMode !== mode && setMode(themeMode);
// Remember the theme mode which user selected for next time
diff --git a/packages/app/src/styles/types.ts b/packages/app/src/styles/types.ts
index c8f33a0fd..41f5a2362 100644
--- a/packages/app/src/styles/types.ts
+++ b/packages/app/src/styles/types.ts
@@ -1,3 +1,5 @@
+import { EditorContainer } from '@blocksuite/editor';
+
export type Theme = 'light' | 'dark';
export type ThemeMode = Theme | 'auto';
@@ -12,6 +14,8 @@ export type ThemeProviderValue = {
};
export interface AffineTheme {
+ mode: Theme;
+ editorMode: EditorContainer['mode'];
colors: {
primaryColor: string;
@@ -35,6 +39,7 @@ export interface AffineTheme {
placeHolderColor: string;
selectedColor: string;
borderColor: string;
+ disableColor: string;
};
font: {
xs: string; // tiny
@@ -53,13 +58,20 @@ export interface AffineTheme {
shadow: {
modal: string;
popover: string;
+ tooltip: string;
};
space: {
paragraph: string;
};
+ radius: {
+ popover: string;
+ };
}
export interface AffineThemeCSSVariables {
+ '--affine-theme-mode': Theme;
+ '--affine-editor-mode': EditorContainer['mode'];
+
'--affine-primary-color': AffineTheme['colors']['primaryColor'];
'--affine-page-background': AffineTheme['colors']['pageBackground'];
'--affine-popover-background': AffineTheme['colors']['popoverBackground'];
@@ -79,9 +91,11 @@ export interface AffineThemeCSSVariables {
'--affine-placeholder-color': AffineTheme['colors']['placeHolderColor'];
'--affine-selected-color': AffineTheme['colors']['selectedColor'];
'--affine-border-color': AffineTheme['colors']['borderColor'];
+ '--affine-disable-color': AffineTheme['colors']['disableColor'];
- '--affine-modal-shadow': AffineTheme['shadow']['popover'];
- '--affine-popover-shadow': AffineTheme['shadow']['modal'];
+ '--affine-modal-shadow': AffineTheme['shadow']['modal'];
+ '--affine-popover-shadow': AffineTheme['shadow']['popover'];
+ '--affine-tooltip-shadow': AffineTheme['shadow']['tooltip'];
'--affine-font-xs': AffineTheme['font']['xs']; // tiny
'--affine-font-sm': AffineTheme['font']['sm']; // small
@@ -95,6 +109,8 @@ export interface AffineThemeCSSVariables {
'--affine-font-family2': AffineTheme['font']['family2'];
'--affine-paragraph-space': AffineTheme['space']['paragraph'];
+
+ '--affine-popover-radius': AffineTheme['radius']['popover'];
}
declare module '@emotion/react' {
diff --git a/packages/app/src/components/popover/index.tsx b/packages/app/src/ui/popover/index.tsx
similarity index 68%
rename from packages/app/src/components/popover/index.tsx
rename to packages/app/src/ui/popover/index.tsx
index 0d0f79098..29100d5b3 100644
--- a/packages/app/src/components/popover/index.tsx
+++ b/packages/app/src/ui/popover/index.tsx
@@ -1,6 +1,7 @@
import { useState } from 'react';
import type { CSSProperties, PropsWithChildren, ReactNode } from 'react';
import Grow from '@mui/material/Grow';
+import ClickAwayListener from '@mui/base/ClickAwayListener';
import { styled } from '@/styles';
@@ -41,24 +42,24 @@ export const Popover = ({
}: PropsWithChildren) => {
const [show, setShow] = useState(false);
return (
- {
- setShow(!show);
- }}
- onMouseEnter={() => {
- setShow(true);
- }}
- onMouseLeave={() => {
+ {
setShow(false);
}}
- style={style}
>
- {children}
-
-
- {popoverContent}
-
-
-
+ {
+ setShow(!show);
+ }}
+ style={style}
+ >
+ {children}
+
+
+ {popoverContent}
+
+
+
+
);
};
diff --git a/packages/app/src/components/popper/PopoverArrow.tsx b/packages/app/src/ui/popper/PopoverArrow.tsx
similarity index 100%
rename from packages/app/src/components/popper/PopoverArrow.tsx
rename to packages/app/src/ui/popper/PopoverArrow.tsx
diff --git a/packages/app/src/components/popper/Popper.tsx b/packages/app/src/ui/popper/Popper.tsx
similarity index 100%
rename from packages/app/src/components/popper/Popper.tsx
rename to packages/app/src/ui/popper/Popper.tsx
diff --git a/packages/app/src/components/popper/index.ts b/packages/app/src/ui/popper/index.ts
similarity index 100%
rename from packages/app/src/components/popper/index.ts
rename to packages/app/src/ui/popper/index.ts
diff --git a/packages/app/src/components/popper/interface.ts b/packages/app/src/ui/popper/interface.ts
similarity index 100%
rename from packages/app/src/components/popper/interface.ts
rename to packages/app/src/ui/popper/interface.ts
diff --git a/packages/app/src/ui/shared/Container.tsx b/packages/app/src/ui/shared/Container.tsx
new file mode 100644
index 000000000..37a4cbc80
--- /dev/null
+++ b/packages/app/src/ui/shared/Container.tsx
@@ -0,0 +1,53 @@
+import { styled } from '@/styles';
+import { PopperPlacementType } from '@mui/material';
+
+export type PopperDirection =
+ | 'none'
+ | 'left-top'
+ | 'left-bottom'
+ | 'right-top'
+ | 'right-bottom';
+
+const getBorderRadius = (direction: PopperDirection, radius = '0') => {
+ const map: Record = {
+ none: `${radius}`,
+ 'left-top': `0 ${radius} ${radius} ${radius}`,
+ 'left-bottom': `${radius} ${radius} ${radius} 0`,
+ 'right-top': `${radius} 0 ${radius} ${radius}`,
+ 'right-bottom': `${radius} ${radius} 0 ${radius}`,
+ };
+ return map[direction];
+};
+
+export const placementToContainerDirection: Record<
+ PopperPlacementType,
+ PopperDirection
+> = {
+ top: 'none',
+ 'top-start': 'left-bottom',
+ 'top-end': 'right-bottom',
+ right: 'none',
+ 'right-start': 'left-top',
+ 'right-end': 'left-bottom',
+ bottom: 'none',
+ 'bottom-start': 'left-top',
+ 'bottom-end': 'right-top',
+ left: 'none',
+ 'left-start': 'right-top',
+ 'left-end': 'right-bottom',
+ auto: 'none',
+ 'auto-start': 'none',
+ 'auto-end': 'none',
+};
+
+export const StyledPopperContainer = styled('div')<{
+ placement?: PopperPlacementType;
+}>(({ theme, placement = 'top' }) => {
+ const direction = placementToContainerDirection[placement];
+ const borderRadius = getBorderRadius(direction, theme.radius.popover);
+ return {
+ borderRadius,
+ };
+});
+
+export default StyledPopperContainer;
diff --git a/packages/app/src/ui/tooltip/Tooltip.tsx b/packages/app/src/ui/tooltip/Tooltip.tsx
new file mode 100644
index 000000000..0bf92323e
--- /dev/null
+++ b/packages/app/src/ui/tooltip/Tooltip.tsx
@@ -0,0 +1,32 @@
+import { type PropsWithChildren } from 'react';
+import StyledPopperContainer from '../shared/Container';
+import { Popper, type PopperProps } from '../popper';
+import { styled } from '@/styles';
+import type { TooltipProps } from '@mui/material';
+
+const StyledTooltip = styled(StyledPopperContainer)(({ theme }) => {
+ return {
+ boxShadow: theme.shadow.tooltip,
+ padding: '4px 12px',
+ backgroundColor:
+ theme.mode === 'dark'
+ ? theme.colors.popoverBackground
+ : theme.colors.primaryColor,
+ color: '#fff',
+ fontSize: theme.font.xs,
+ };
+});
+
+export const Tooltip = (
+ props: PropsWithChildren>
+) => {
+ const { content, placement = 'top-start' } = props;
+ // If there is no content, hide forever
+ return content ? (
+ {content}}
+ />
+ ) : null;
+};
diff --git a/packages/app/src/components/tooltip/index.tsx b/packages/app/src/ui/tooltip/index.tsx
similarity index 100%
rename from packages/app/src/components/tooltip/index.tsx
rename to packages/app/src/ui/tooltip/index.tsx