mirror of
https://github.com/toeverything/AFFiNE.git
synced 2024-12-23 10:05:02 +03:00
commit
edaa0a5125
@ -9,7 +9,7 @@ import {
|
||||
StyledMoreMenuItem,
|
||||
IconButton,
|
||||
} from './styles';
|
||||
import { Popover } from '@/components/popover';
|
||||
import { Popover } from '@/ui/popover';
|
||||
import { useEditor } from '@/components/editor-provider';
|
||||
import EditorModeSwitch from '@/components/editor-mode-switch';
|
||||
import { EdgelessIcon, PaperIcon } from '../editor-mode-switch/icons';
|
||||
|
@ -13,7 +13,7 @@ export const StyledHeader = styled('div')({
|
||||
zIndex: '10',
|
||||
});
|
||||
|
||||
export const StyledTitle = styled('div')({
|
||||
export const StyledTitle = styled('div')(({ theme }) => ({
|
||||
width: '720px',
|
||||
height: '100%',
|
||||
position: 'absolute',
|
||||
@ -23,8 +23,8 @@ export const StyledTitle = styled('div')({
|
||||
margin: 'auto',
|
||||
|
||||
...displayFlex('center', 'center'),
|
||||
fontSize: '20px',
|
||||
});
|
||||
fontSize: theme.font.base,
|
||||
}));
|
||||
|
||||
export const StyledTitleWrapper = styled('div')({
|
||||
maxWidth: '720px',
|
||||
|
@ -25,7 +25,7 @@ import {
|
||||
StyledLogo,
|
||||
StyledModalHeader,
|
||||
StyledModalHeaderLeft,
|
||||
CloseButton,
|
||||
StyledCloseButton,
|
||||
StyledModalFooter,
|
||||
} from './style';
|
||||
|
||||
@ -87,13 +87,13 @@ export const ContactModal = ({ open, onClose }: TransitionsModalProps) => {
|
||||
<StyledLogo src={logo.src} alt="" />
|
||||
<span>Alpha</span>
|
||||
</StyledModalHeaderLeft>
|
||||
<CloseButton
|
||||
<StyledCloseButton
|
||||
onClick={() => {
|
||||
onClose();
|
||||
}}
|
||||
>
|
||||
<CloseIcon width={12} height={12} />
|
||||
</CloseButton>
|
||||
</StyledCloseButton>
|
||||
</StyledModalHeader>
|
||||
|
||||
<StyledContent>
|
||||
@ -129,7 +129,13 @@ export const ContactModal = ({ open, onClose }: TransitionsModalProps) => {
|
||||
|
||||
<StyledModalFooter>
|
||||
<p>
|
||||
<a href="">How is AFFiNE Alpha different?</a>
|
||||
<a
|
||||
href="https://affine.pro/content/blog/affine-alpha-is-coming/index"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
How is AFFiNE Alpha different?
|
||||
</a>
|
||||
</p>
|
||||
<p>Copyright © 2022 Toeverything</p>
|
||||
</StyledModalFooter>
|
||||
|
@ -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,
|
||||
},
|
||||
};
|
||||
});
|
||||
|
@ -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: <TextIcon />,
|
||||
toolTip: 'Text(coming soon)',
|
||||
toolTip: 'Text (coming soon)',
|
||||
disable: true,
|
||||
},
|
||||
{
|
||||
flavor: 'shape',
|
||||
icon: <ShapeIcon />,
|
||||
toolTip: 'Shape(coming soon)',
|
||||
toolTip: 'Shape (coming soon)',
|
||||
disable: true,
|
||||
},
|
||||
{
|
||||
flavor: 'sticky',
|
||||
icon: <StickerIcon />,
|
||||
toolTip: 'Sticky(coming soon)',
|
||||
toolTip: 'Sticky (coming soon)',
|
||||
disable: true,
|
||||
},
|
||||
{
|
||||
flavor: 'pen',
|
||||
icon: <PenIcon />,
|
||||
toolTip: 'Pen(coming soon)',
|
||||
toolTip: 'Pen (coming soon)',
|
||||
disable: true,
|
||||
},
|
||||
|
||||
{
|
||||
flavor: 'connector',
|
||||
icon: <ConnectorIcon />,
|
||||
toolTip: 'Connector(coming soon)',
|
||||
toolTip: 'Connector (coming soon)',
|
||||
disable: true,
|
||||
},
|
||||
];
|
||||
@ -88,7 +88,7 @@ const UndoRedo = () => {
|
||||
|
||||
return (
|
||||
<StyledToolbarWrapper>
|
||||
<Tooltip content="undo" placement="right-start">
|
||||
<Tooltip content="Undo" placement="right-start">
|
||||
<StyledToolbarItem
|
||||
disable={!canUndo}
|
||||
onClick={() => {
|
||||
@ -98,7 +98,7 @@ const UndoRedo = () => {
|
||||
<UndoIcon />
|
||||
</StyledToolbarItem>
|
||||
</Tooltip>
|
||||
<Tooltip content="redo" placement="right-start">
|
||||
<Tooltip content="Redo" placement="right-start">
|
||||
<StyledToolbarItem
|
||||
disable={!canRedo}
|
||||
onClick={() => {
|
||||
|
@ -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',
|
||||
|
@ -38,20 +38,25 @@ const EdgelessItem = ({ active }: { active?: boolean }) => {
|
||||
const AnimateRadioItem = ({
|
||||
active,
|
||||
status,
|
||||
icon,
|
||||
icon: propsIcon,
|
||||
label,
|
||||
isLeft,
|
||||
...props
|
||||
}: AnimateRadioItemProps) => {
|
||||
const icon = (
|
||||
<StyledIcon shrink={status === 'shrink'} isLeft={isLeft}>
|
||||
{cloneElement(propsIcon, {
|
||||
active,
|
||||
})}
|
||||
</StyledIcon>
|
||||
);
|
||||
return (
|
||||
<StyledRadioItem active={active} status={status} {...props}>
|
||||
<StyledIcon shrink={status === 'shrink'} isLeft={isLeft}>
|
||||
{cloneElement(icon, {
|
||||
active,
|
||||
})}
|
||||
</StyledIcon>
|
||||
|
||||
<StyledLabel shrink={status !== 'stretch'}>{label}</StyledLabel>
|
||||
{isLeft ? icon : null}
|
||||
<StyledLabel shrink={status !== 'stretch'} isLeft={isLeft}>
|
||||
{label}
|
||||
</StyledLabel>
|
||||
{isLeft ? null : icon}
|
||||
</StyledRadioItem>
|
||||
);
|
||||
};
|
||||
|
@ -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,
|
||||
};
|
||||
});
|
||||
|
@ -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';
|
||||
|
@ -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,
|
||||
},
|
||||
};
|
||||
});
|
||||
|
@ -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<PopoverContainerProps['direction'], string> = {
|
||||
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<PopoverContainerProps, 'direction'>
|
||||
>(({ direction, style }) => {
|
||||
const borderRadius =
|
||||
border_radius_map[direction] || border_radius_map['left-top'];
|
||||
return {
|
||||
borderRadius: borderRadius,
|
||||
...style,
|
||||
};
|
||||
});
|
@ -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<PopperProps & Omit<TooltipProps, 'title'>>
|
||||
) => {
|
||||
const { content, placement = 'top-start' } = props;
|
||||
const style = useTooltipStyle();
|
||||
// If there is no content, hide forever
|
||||
const visibleProp = content ? {} : { visible: false };
|
||||
return (
|
||||
<Popper
|
||||
{...visibleProp}
|
||||
placement="top"
|
||||
{...props}
|
||||
showArrow={false}
|
||||
content={
|
||||
<PopoverContainer
|
||||
style={style}
|
||||
direction={placementToContainerDirection[placement]}
|
||||
>
|
||||
{content}
|
||||
</PopoverContainer>
|
||||
}
|
||||
/>
|
||||
);
|
||||
};
|
@ -1,9 +0,0 @@
|
||||
export type TooltipProps = {
|
||||
showArrow?: boolean;
|
||||
};
|
||||
export type PopoverDirection =
|
||||
| 'none'
|
||||
| 'left-top'
|
||||
| 'left-bottom'
|
||||
| 'right-top'
|
||||
| 'right-bottom';
|
@ -17,13 +17,13 @@ function MyApp({ Component, pageProps }: AppProps) {
|
||||
return (
|
||||
<>
|
||||
<Logger />
|
||||
<ThemeProvider>
|
||||
<ModalProvider>
|
||||
<EditorProvider>
|
||||
<EditorProvider>
|
||||
<ThemeProvider>
|
||||
<ModalProvider>
|
||||
<Component {...pageProps} />
|
||||
</EditorProvider>
|
||||
</ModalProvider>
|
||||
</ThemeProvider>
|
||||
</ModalProvider>
|
||||
</ThemeProvider>
|
||||
</EditorProvider>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
@ -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';
|
||||
|
@ -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,
|
||||
};
|
||||
};
|
||||
|
@ -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<ThemeProviderValue>({
|
||||
mode: 'light',
|
||||
changeMode: () => {},
|
||||
theme: lightTheme,
|
||||
theme: getLightTheme('page'),
|
||||
});
|
||||
|
||||
export const ThemeProvider = ({
|
||||
@ -26,8 +27,9 @@ export const ThemeProvider = ({
|
||||
}: PropsWithChildren<ThemeProviderProps>) => {
|
||||
const [theme, setTheme] = useState<Theme>(defaultTheme);
|
||||
const [mode, setMode] = useState<ThemeMode>('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
|
||||
|
@ -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' {
|
||||
|
@ -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<PopoverProps>) => {
|
||||
const [show, setShow] = useState(false);
|
||||
return (
|
||||
<StyledPopoverContainer
|
||||
onClick={() => {
|
||||
setShow(!show);
|
||||
}}
|
||||
onMouseEnter={() => {
|
||||
setShow(true);
|
||||
}}
|
||||
onMouseLeave={() => {
|
||||
<ClickAwayListener
|
||||
onClickAway={() => {
|
||||
setShow(false);
|
||||
}}
|
||||
style={style}
|
||||
>
|
||||
{children}
|
||||
<Grow in={show}>
|
||||
<StyledPopoverWrapper>
|
||||
<StyledPopover>{popoverContent}</StyledPopover>
|
||||
</StyledPopoverWrapper>
|
||||
</Grow>
|
||||
</StyledPopoverContainer>
|
||||
<StyledPopoverContainer
|
||||
onClick={() => {
|
||||
setShow(!show);
|
||||
}}
|
||||
style={style}
|
||||
>
|
||||
{children}
|
||||
<Grow in={show}>
|
||||
<StyledPopoverWrapper>
|
||||
<StyledPopover>{popoverContent}</StyledPopover>
|
||||
</StyledPopoverWrapper>
|
||||
</Grow>
|
||||
</StyledPopoverContainer>
|
||||
</ClickAwayListener>
|
||||
);
|
||||
};
|
53
packages/app/src/ui/shared/Container.tsx
Normal file
53
packages/app/src/ui/shared/Container.tsx
Normal file
@ -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<PopperDirection, string> = {
|
||||
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;
|
32
packages/app/src/ui/tooltip/Tooltip.tsx
Normal file
32
packages/app/src/ui/tooltip/Tooltip.tsx
Normal file
@ -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<PopperProps & Omit<TooltipProps, 'title'>>
|
||||
) => {
|
||||
const { content, placement = 'top-start' } = props;
|
||||
// If there is no content, hide forever
|
||||
return content ? (
|
||||
<Popper
|
||||
{...props}
|
||||
showArrow={false}
|
||||
content={<StyledTooltip placement={placement}>{content}</StyledTooltip>}
|
||||
/>
|
||||
) : null;
|
||||
};
|
Loading…
Reference in New Issue
Block a user