refactor: unify theme (#1303)

This commit is contained in:
Himself65
2023-03-04 01:36:20 -06:00
committed by GitHub
parent fe0d78b2d6
commit 4e9f0c97a1
43 changed files with 779 additions and 543 deletions

View File

@@ -44,6 +44,7 @@ const getRedirectConfig = profile => {
const nextConfig = {
productionBrowserSourceMaps: true,
compiler: {
styledComponents: true,
removeConsole: {
exclude: ['error', 'log', 'warn', 'info'],
},
@@ -85,6 +86,7 @@ const nextConfig = {
loader: 'raw-loader',
});
config.resolve.alias['yjs'] = require.resolve('yjs');
config.resolve.alias['@mui/styled-engine'] = '@mui/styled-engine-sc';
if (!isServer && !dev) {
config.devtool = 'hidden-nosources-source-map';

View File

@@ -20,18 +20,22 @@
"@blocksuite/react": "0.5.0-20230303192351-13b0dd7",
"@blocksuite/store": "0.5.0-20230303192351-13b0dd7",
"@emotion/cache": "^11.10.5",
"@emotion/css": "^11.10.6",
"@emotion/react": "^11.10.6",
"@emotion/server": "^11.10.0",
"@emotion/styled": "^11.10.6",
"@mui/material": "^5.11.11",
"@mui/styled-engine-sc": "^5.11.11",
"cmdk": "^0.1.22",
"css-spring": "^4.1.0",
"dayjs": "^1.11.7",
"jotai": "^2.0.3",
"jotai-devtools": "^0.2.0",
"lit": "^2.6.1",
"next-themes": "^0.2.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-helmet-async": "^1.3.0",
"styled-components": "^5.3.8",
"swr": "^2.0.4",
"y-indexeddb": "^9.0.9",
"y-protocols": "^1.0.5",

View File

@@ -98,4 +98,4 @@ export const StyledMoreVerticalDiv = styled('div')(() => {
};
});
export const StyledMoreVerticalButton = styled(StyledMoreVerticalDiv)();
export const StyledMoreVerticalButton = styled(StyledMoreVerticalDiv)``;

View File

@@ -9,7 +9,7 @@ export const StyledInput = styled(Input)(({ theme }) => {
};
});
export const StyledWorkspaceInfo = styled.div(({ theme }) => {
export const StyledWorkspaceInfo = styled('div')(({ theme }) => {
return {
...displayFlex('flex-start', 'center'),
fontSize: '20px',

View File

@@ -43,7 +43,7 @@ export const WorkspaceSettingTagItem = styled('li')<{ isActive?: boolean }>(
}
);
export const StyledSettingKey = styled.div(({ theme }) => {
export const StyledSettingKey = styled('div')(({ theme }) => {
return {
width: '140px',
fontSize: theme.font.base,
@@ -65,7 +65,7 @@ export const StyledWorkspaceName = styled('span')(({ theme }) => {
};
});
export const StyledIndicator = styled.div(({ theme }) => {
export const StyledIndicator = styled('div')(({ theme }) => {
return {
height: '2px',
background: theme.colors.primaryColor,
@@ -76,14 +76,14 @@ export const StyledIndicator = styled.div(({ theme }) => {
};
});
export const StyledTabButtonWrapper = styled.div(() => {
export const StyledTabButtonWrapper = styled('div')(() => {
return {
display: 'flex',
position: 'relative',
};
});
// export const StyledDownloadCard = styled.div<{ active?: boolean }>(
// export const StyledDownloadCard = styled('div')<{ active?: boolean }>(
// ({ theme, active }) => {
// return {
// width: '240px',
@@ -105,7 +105,7 @@ export const StyledTabButtonWrapper = styled.div(() => {
// };
// }
// );
// export const StyledDownloadCardDes = styled.div(({ theme }) => {
// export const StyledDownloadCardDes = styled('div')(({ theme }) => {
// return {
// fontSize: theme.font.sm,
// color: theme.colors.iconColor,

View File

@@ -14,14 +14,17 @@ import {
PaperIcon,
} from '@blocksuite/icons';
import { PageMeta } from '@blocksuite/store';
import { useMediaQuery, useTheme as useMuiTheme } from '@mui/material';
import {
useMediaQuery,
useTheme as useMuiTheme,
useTheme,
} from '@mui/material';
import React, { useMemo } from 'react';
import {
usePageMeta,
usePageMetaHelper,
} from '../../../../hooks/use-page-meta';
import { useTheme } from '../../../../providers/ThemeProvider';
import { BlockSuiteWorkspace } from '../../../../shared';
import DateCell from './DateCell';
import Empty from './Empty';
@@ -41,7 +44,7 @@ const FavoriteTag: React.FC<FavoriteTagProps> = ({
pageMeta: { favorite },
onClick,
}) => {
const { theme } = useTheme();
const theme = useTheme();
const { t } = useTranslation();
return (
<Tooltip

View File

@@ -1,14 +1,14 @@
import { displayFlex, styled } from '@affine/component';
import { TableRow } from '@affine/component';
export const StyledTableContainer = styled.div(() => {
export const StyledTableContainer = styled('div')(() => {
return {
height: 'calc(100vh - 60px)',
padding: '78px 72px',
overflowY: 'auto',
};
});
export const StyledTitleWrapper = styled.div(({ theme }) => {
export const StyledTitleWrapper = styled('div')(({ theme }) => {
return {
...displayFlex('flex-start', 'center'),
a: {
@@ -22,7 +22,7 @@ export const StyledTitleWrapper = styled.div(({ theme }) => {
},
};
});
export const StyledTitleLink = styled.div(({ theme }) => {
export const StyledTitleLink = styled('div')(({ theme }) => {
return {
maxWidth: '80%',
marginRight: '18px',

View File

@@ -1,5 +1,6 @@
import { useTranslation } from '@affine/i18n';
import { assertExists } from '@blocksuite/store';
import { useTheme } from '@mui/material';
import React, { cloneElement, CSSProperties, useEffect, useState } from 'react';
import {
@@ -7,7 +8,6 @@ import {
usePageMetaHelper,
} from '../../../../hooks/use-page-meta';
// todo(himself65): remove `useTheme` hook
import { useTheme } from '../../../../providers/ThemeProvider';
import { BlockSuiteWorkspace } from '../../../../shared';
import { EdgelessIcon, PaperIcon } from './Icons';
import {
@@ -20,9 +20,7 @@ import {
import type { AnimateRadioItemProps, RadioItemStatus } from './type';
const PaperItem = ({ active }: { active?: boolean }) => {
const {
theme: {
colors: { iconColor, primaryColor },
},
colors: { iconColor, primaryColor },
} = useTheme();
return <PaperIcon style={{ color: active ? primaryColor : iconColor }} />;
@@ -30,9 +28,7 @@ const PaperItem = ({ active }: { active?: boolean }) => {
const EdgelessItem = ({ active }: { active?: boolean }) => {
const {
theme: {
colors: { iconColor, primaryColor },
},
colors: { iconColor, primaryColor },
} = useTheme();
return <EdgelessIcon style={{ color: active ? primaryColor : iconColor }} />;
@@ -78,7 +74,7 @@ export const EditorModeSwitch: React.FC<EditorModeSwitchProps> = ({
blockSuiteWorkspace,
pageId,
}) => {
const { mode: themeMode } = useTheme();
const theme = useTheme();
const { setPageMeta } = usePageMetaHelper(blockSuiteWorkspace);
const pageMeta = usePageMeta(blockSuiteWorkspace).find(
meta => meta.id === pageId
@@ -139,7 +135,10 @@ export const EditorModeSwitch: React.FC<EditorModeSwitchProps> = ({
setRadioItemStatus(modifyRadioItemStatus());
}}
/>
<StyledMiddleLine hidden={!isHover} dark={themeMode === 'dark'} />
<StyledMiddleLine
hidden={!isHover}
dark={theme.palette.mode === 'dark'}
/>
<AnimateRadioItem
isLeft={false}
label={t('Edgeless')}

View File

@@ -1,7 +1,8 @@
import { displayFlex, keyframes, styled } from '@affine/component';
import { css, displayFlex, keyframes, styled } from '@affine/component';
// @ts-ignore
import spring, { toString } from 'css-spring';
// @ts-ignore
import type { ItemStatus } from './type';
const ANIMATE_DURATION = 500;
@@ -10,37 +11,48 @@ export const StyledAnimateRadioContainer = styled('div')<{
shrink: boolean;
disabled: boolean;
}>(({ shrink, theme, disabled }) => {
const animateScaleStretch = keyframes`${toString(
const animateScaleStretch = toString(
spring({ width: '36px' }, { width: '160px' }, { preset: 'gentle' })
)}`;
const animateScaleShrink = keyframes(
`${toString(
spring({ width: '160px' }, { width: '36px' }, { preset: 'gentle' })
)}`
);
const shrinkStyle = shrink
const animateScaleShrink = toString(
spring({ width: '160px' }, { width: '36px' }, { preset: 'gentle' })
);
const shrinkStyle: any = shrink
? {
animation: `${animateScaleShrink} ${ANIMATE_DURATION}ms forwards`,
animation: css`
${keyframes`${animateScaleShrink}`} ${ANIMATE_DURATION}ms forwards
`,
background: 'transparent',
}
: {
animation: `${animateScaleStretch} ${ANIMATE_DURATION}ms forwards`,
animation: css`
${keyframes`${animateScaleStretch}`} ${ANIMATE_DURATION}ms forwards
`,
};
return css`
height: 36px;
border-radius: 18px;
background: ${disabled ? 'transparent' : theme.colors.hoverBackground}
position: relative;
display: flex;
transition: background ${ANIMATE_DURATION}ms, border ${ANIMATE_DURATION}ms;
border: 1px solid transparent;
${
disabled
? css`
pointer-events: none;
`
: css`
animation: ${shrinkStyle.animation};
background: ${shrinkStyle.background};
`
}
return {
height: '36px',
borderRadius: '18px',
background: disabled ? 'transparent' : theme.colors.hoverBackground,
position: 'relative',
display: 'flex',
transition: `background ${ANIMATE_DURATION}ms, border ${ANIMATE_DURATION}ms`,
border: '1px solid transparent',
...(disabled ? { pointerEvents: 'none' } : shrinkStyle),
':hover': {
border: disabled ? '' : `1px solid ${theme.colors.primaryColor}`,
},
};
//...(disabled ? { pointerEvents: 'none' } : shrinkStyle),
:hover {
border: ${disabled ? '' : `1px solid ${theme.colors.primaryColor}`}
}
`;
});
export const StyledMiddleLine = styled('div')<{
@@ -62,83 +74,94 @@ export const StyledRadioItem = styled('div')<{
status: ItemStatus;
active: boolean;
}>(({ status, active, theme }) => {
const animateScaleStretch = keyframes`${toString(
const animateScaleStretch = toString(
spring({ width: '44px' }, { width: '112px' })
)}`;
const animateScaleOrigin = keyframes(
`${toString(spring({ width: '112px' }, { width: '44px' }))}`
);
const animateScaleShrink = keyframes(
`${toString(spring({ width: '0px' }, { width: '36px' }))}`
const animateScaleOrigin = toString(
spring({ width: '112px' }, { width: '44px' })
);
const animateScaleShrink = toString(
spring({ width: '0px' }, { width: '36px' })
);
const dynamicStyle =
status === 'stretch'
? {
animation: `${animateScaleStretch} ${ANIMATE_DURATION}ms forwards`,
animation: css`
${keyframes`${animateScaleStretch}`} ${ANIMATE_DURATION}ms forwards
`,
flexShrink: '0',
}
: status === 'shrink'
? {
animation: `${animateScaleShrink} ${ANIMATE_DURATION}ms forwards`,
animation: css`
${keyframes`${animateScaleShrink}`} ${ANIMATE_DURATION}ms forwards
`,
}
: status === 'normal'
? { animation: `${animateScaleOrigin} ${ANIMATE_DURATION}ms forwards` }
? {
animation: css`
${keyframes`${animateScaleOrigin}`} ${ANIMATE_DURATION}ms forwards
`,
}
: {};
const {
colors: { iconColor, primaryColor },
} = theme;
return {
width: '0',
height: '100%',
display: 'flex',
cursor: 'pointer',
overflow: 'hidden',
color: active ? primaryColor : iconColor,
...dynamicStyle,
};
return css`
width: 0;
height: 100%;
display: flex;
cursor: pointer;
overflow: hidden;
color: ${active ? primaryColor : iconColor};
animation: ${dynamicStyle.animation};
flex-shrink: ${dynamicStyle.flexShrink};
`;
});
export const StyledLabel = styled('div')<{
shrink: boolean;
isLeft: boolean;
}>(({ shrink, isLeft }) => {
const animateScaleStretch = keyframes`${toString(
const animateScaleStretch = toString(
spring(
{ width: '0px' },
{ width: isLeft ? '65px' : '75px' },
{ preset: 'gentle' }
)
)}`;
const animateScaleShrink = keyframes(
`${toString(
spring(
{ width: isLeft ? '65px' : '75px' },
{ width: '0px' },
{ preset: 'gentle' }
)
)}`
);
const animateScaleShrink = toString(
spring(
{ width: isLeft ? '65px' : '75px' },
{ width: '0px' },
{ preset: 'gentle' }
)
);
const shrinkStyle = shrink
? {
animation: `${animateScaleShrink} ${ANIMATE_DURATION}ms forwards`,
animation: css`
${keyframes`${animateScaleShrink}`} ${ANIMATE_DURATION}ms forwards
`,
}
: {
animation: `${animateScaleStretch} ${ANIMATE_DURATION}ms forwards`,
animation: css`
${keyframes`${animateScaleStretch}`} ${ANIMATE_DURATION}ms forwards
`,
};
return {
display: 'flex',
alignItems: 'center',
justifyContent: isLeft ? 'flex-start' : 'flex-end',
fontSize: '16px',
flexShrink: '0',
transition: `transform ${ANIMATE_DURATION}ms`,
fontWeight: 'normal',
overflow: 'hidden',
whiteSpace: 'nowrap',
...shrinkStyle,
};
return css`
display: flex;
align-items: center;
justify-content: ${isLeft ? 'flex-start' : 'flex-end'};
font-size: 16px;
flex-shrink: 0;
transition: transform ${ANIMATE_DURATION}ms;
font-weight: normal;
overflow: hidden;
white-space: nowrap;
animation: ${shrinkStyle.animation};
`;
});
export const StyledIcon = styled('div')<{

View File

@@ -33,7 +33,7 @@ const NoNetWorkIcon = () => {
</svg>
);
};
const IconWrapper = styled.div(() => {
const IconWrapper = styled('div')(() => {
return {
width: '32px',
height: '32px',

View File

@@ -1,10 +1,10 @@
import { useTheme } from 'next-themes';
import { useState } from 'react';
import { useTheme } from '../../../../../providers/ThemeProvider';
import { MoonIcon, SunIcon } from './Icons';
import { StyledSwitchItem, StyledThemeModeSwitch } from './style';
export const ThemeModeSwitch = () => {
const { mode, changeMode } = useTheme();
const { theme, setTheme } = useTheme();
const [isHover, setIsHover] = useState(false);
const [firstTrigger, setFirstTrigger] = useState(false);
return (
@@ -22,22 +22,22 @@ export const ThemeModeSwitch = () => {
>
<StyledSwitchItem
data-testid="change-theme-light"
active={mode === 'light'}
active={theme === 'light'}
isHover={isHover}
firstTrigger={firstTrigger}
onClick={() => {
changeMode('light');
setTheme('light');
}}
>
<SunIcon />
</StyledSwitchItem>
<StyledSwitchItem
data-testid="change-theme-dark"
active={mode === 'dark'}
active={theme === 'dark'}
isHover={isHover}
firstTrigger={firstTrigger}
onClick={() => {
changeMode('dark');
setTheme('dark');
}}
>
<MoonIcon />

View File

@@ -1,7 +1,6 @@
import { displayFlex, keyframes, styled } from '@affine/component';
import { css, displayFlex, keyframes, styled } from '@affine/component';
// @ts-ignore
import spring, { toString } from 'css-spring';
import { CSSProperties } from 'react';
const ANIMATE_DURATION = 400;
@@ -18,52 +17,60 @@ export const StyledSwitchItem = styled('div')<{
isHover: boolean;
firstTrigger: boolean;
}>(({ active, isHover, firstTrigger, theme }) => {
const activeRaiseAnimate = keyframes`${toString(
const activeRaiseAnimate = toString(
spring({ top: '0' }, { top: '-100%' }, { preset: 'gentle' })
)}`;
const raiseAnimate = keyframes`${toString(
);
const raiseAnimate = toString(
spring({ top: '100%' }, { top: '0' }, { preset: 'gentle' })
)}`;
const activeDeclineAnimate = keyframes`${toString(
);
const activeDeclineAnimate = toString(
spring({ top: '-100%' }, { top: '0' }, { preset: 'gentle' })
)}`;
const declineAnimate = keyframes`${toString(
);
const declineAnimate = toString(
spring({ top: '0' }, { top: '100%' }, { preset: 'gentle' })
)}`;
);
const activeStyle = active
? {
color: theme.colors.iconColor,
top: '0',
animation: firstTrigger
? `${
isHover ? activeRaiseAnimate : activeDeclineAnimate
} ${ANIMATE_DURATION}ms forwards`
? css`
${keyframes`${
isHover ? activeRaiseAnimate : activeDeclineAnimate
}`} ${ANIMATE_DURATION}ms forwards
`
: 'unset',
animationDirection: isHover ? 'normal' : 'alternate',
}
: ({
: {
top: '100%',
color: theme.colors.primaryColor,
backgroundColor: theme.colors.hoverBackground,
animation: firstTrigger
? `${
isHover ? raiseAnimate : declineAnimate
} ${ANIMATE_DURATION}ms forwards`
? css`
${keyframes`${
isHover ? raiseAnimate : declineAnimate
}`} ${ANIMATE_DURATION}ms forwards
`
: 'unset',
animationDirection: isHover ? 'normal' : 'alternate',
} as CSSProperties);
return {
width: '32px',
height: '32px',
position: 'absolute',
left: '0',
...displayFlex('center', 'center'),
cursor: 'pointer',
...activeStyle,
svg: {
width: '24px',
height: '24px',
};
return css`
${css(displayFlex('center', 'center'))}
width: 32px;
height: 32px;
position: absolute;
left: 0;
cursor: pointer;
color: ${activeStyle.color}
top: ${activeStyle.top};
background-color: ${activeStyle.backgroundColor};
animation: ${activeStyle.animation};
animation-direction: ${activeStyle.animationDirection};
svg {
width: 24px;
height: 24px;
},
};
`;
});

View File

@@ -1,6 +1,6 @@
import { displayFlex, styled } from '@affine/component';
export const StyledHeaderContainer = styled.div<{ hasWarning: boolean }>(
export const StyledHeaderContainer = styled('div')<{ hasWarning: boolean }>(
({ hasWarning }) => {
return {
height: hasWarning ? '96px' : '60px',
@@ -8,16 +8,18 @@ export const StyledHeaderContainer = styled.div<{ hasWarning: boolean }>(
};
}
);
export const StyledHeader = styled.div<{ hasWarning: boolean }>(({ theme }) => {
return {
height: '60px',
width: '100%',
...displayFlex('flex-end', 'center'),
background: theme.colors.pageBackground,
transition: 'background-color 0.5s',
zIndex: 99,
};
});
export const StyledHeader = styled('div')<{ hasWarning: boolean }>(
({ theme }) => {
return {
height: '60px',
width: '100%',
...displayFlex('flex-end', 'center'),
background: theme.colors.pageBackground,
transition: 'background-color 0.5s',
zIndex: 99,
};
}
);
export const StyledTitle = styled('div')(({ theme }) => ({
width: '720px',
@@ -45,7 +47,7 @@ export const StyledHeaderRightSide = styled('div')({
},
});
export const StyledBrowserWarning = styled.div<{ show: boolean }>(
export const StyledBrowserWarning = styled('div')<{ show: boolean }>(
({ theme, show }) => {
return {
backgroundColor: theme.colors.warningBackground,
@@ -63,7 +65,7 @@ export const StyledBrowserWarning = styled.div<{ show: boolean }>(
}
);
export const StyledCloseButton = styled.div(({ theme }) => {
export const StyledCloseButton = styled('div')(({ theme }) => {
return {
width: '36px',
height: '36px',
@@ -83,7 +85,7 @@ export const StyledCloseButton = styled.div(({ theme }) => {
};
});
export const StyledSwitchWrapper = styled.div(() => {
export const StyledSwitchWrapper = styled('div')(() => {
return {
position: 'absolute',
right: '100%',
@@ -94,7 +96,7 @@ export const StyledSwitchWrapper = styled.div(() => {
};
});
export const StyledSearchArrowWrapper = styled.div(() => {
export const StyledSearchArrowWrapper = styled('div')(() => {
return {
position: 'absolute',
left: 'calc(100% + 4px)',

View File

@@ -121,7 +121,7 @@ export const StyledModalFooter = styled('div')(({ theme }) => {
};
});
export const StyledPrivacyContainer = styled.div(({ theme }) => {
export const StyledPrivacyContainer = styled('div')(({ theme }) => {
return {
marginTop: '4px',
position: 'relative',

View File

@@ -6,7 +6,7 @@ import {
} from '@affine/component';
import { Button } from '@affine/component';
export const StyledSplitLine = styled.div(({ theme }) => {
export const StyledSplitLine = styled('div')(({ theme }) => {
return {
width: '1px',
height: '20px',
@@ -15,7 +15,7 @@ export const StyledSplitLine = styled.div(({ theme }) => {
};
});
export const StyleWorkspaceInfo = styled.div(({ theme }) => {
export const StyleWorkspaceInfo = styled('div')(({ theme }) => {
return {
marginLeft: '15px',
width: '202px',
@@ -37,7 +37,7 @@ export const StyleWorkspaceInfo = styled.div(({ theme }) => {
};
});
export const StyleWorkspaceTitle = styled.div(({ theme }) => {
export const StyleWorkspaceTitle = styled('div')(({ theme }) => {
return {
fontSize: theme.font.base,
fontWeight: 600,
@@ -48,7 +48,7 @@ export const StyleWorkspaceTitle = styled.div(({ theme }) => {
};
});
export const StyledCard = styled.div<{
export const StyledCard = styled('div')<{
active?: boolean;
}>(({ theme, active }) => {
const borderColor = active ? theme.colors.primaryColor : 'transparent';
@@ -63,7 +63,7 @@ export const StyledCard = styled.div<{
...displayFlex('flex-start', 'flex-start'),
marginBottom: '24px',
transition: 'background .2s',
background: theme.mode === 'light' ? '#FFF' : '#2C2C2C',
background: theme.palette.mode === 'light' ? '#FFF' : '#2C2C2C',
':hover': {
background: theme.colors.cardHoverBackground,
'.add-icon': {
@@ -81,7 +81,7 @@ export const StyledFooter = styled('div')({
...displayFlex('space-between', 'center'),
});
export const StyleUserInfo = styled.div(({ theme }) => {
export const StyleUserInfo = styled('div')(({ theme }) => {
return {
textAlign: 'left',
marginLeft: '16px',
@@ -97,17 +97,17 @@ export const StyleUserInfo = styled.div(({ theme }) => {
};
});
export const StyledModalHeaderLeft = styled.div(() => {
export const StyledModalHeaderLeft = styled('div')(() => {
return { ...displayFlex('flex-start', 'center') };
});
export const StyledModalTitle = styled.div(({ theme }) => {
export const StyledModalTitle = styled('div')(({ theme }) => {
return {
fontWeight: 600,
fontSize: theme.font.h6,
};
});
export const StyledHelperContainer = styled.div(({ theme }) => {
export const StyledHelperContainer = styled('div')(({ theme }) => {
return {
color: theme.colors.iconColor,
marginLeft: '15px',
@@ -125,13 +125,13 @@ export const StyledModalContent = styled('div')({
...displayFlex('space-between', 'flex-start', 'flex-start'),
flexWrap: 'wrap',
});
export const StyledOperationWrapper = styled.div(() => {
export const StyledOperationWrapper = styled('div')(() => {
return {
...displayFlex('flex-end', 'center'),
};
});
export const StyleWorkspaceAdd = styled.div(() => {
export const StyleWorkspaceAdd = styled('div')(() => {
return {
width: '58px',
height: '58px',

View File

@@ -3,7 +3,7 @@ import { styled } from '@affine/component';
// Inspired by https://codepen.io/graphilla/pen/rNvBMYY
export const StyledLoadingWrapper = styled('div', {
shouldForwardProp: prop => {
return !['size'].includes(prop);
return !['size'].includes(prop as string);
},
})<{ size?: number }>(({ size = 40 }) => {
return {
@@ -12,7 +12,7 @@ export const StyledLoadingWrapper = styled('div', {
position: 'relative',
};
});
export const StyledLoading = styled.div`
export const StyledLoading = styled('div')`
position: absolute;
left: 25%;
top: 50%;
@@ -31,7 +31,7 @@ export const StyledLoading = styled.div`
}
`;
export const StyledLoadingItem = styled.div<{ size: number }>(
export const StyledLoadingItem = styled('div')<{ size: number }>(
({ size = 40 }) => {
return `
position: absolute;

View File

@@ -123,7 +123,7 @@ export const StyledModalFooter = styled('div')(({ theme }) => {
},
};
});
export const StyledModalFooterContent = styled.button(({ theme }) => {
export const StyledModalFooterContent = styled('button')(({ theme }) => {
return {
width: '612px',
height: '32px',
@@ -140,7 +140,7 @@ export const StyledModalFooterContent = styled.button(({ theme }) => {
},
};
});
export const StyledListItem = styled.button(({ theme }) => {
export const StyledListItem = styled('button')(({ theme }) => {
return {
width: '612px',
height: '32px',

View File

@@ -1,6 +1,6 @@
import { displayFlex, styled } from '@affine/component';
export const StyledShortcutsModal = styled.div(({ theme }) => ({
export const StyledShortcutsModal = styled('div')(({ theme }) => ({
width: '288px',
height: '74vh',
paddingBottom: '28px',
@@ -17,7 +17,7 @@ export const StyledShortcutsModal = styled.div(({ theme }) => ({
margin: 'auto',
zIndex: theme.zIndex.modal,
}));
export const StyledTitle = styled.div(({ theme }) => ({
export const StyledTitle = styled('div')(({ theme }) => ({
color: theme.colors.textColor,
fontWeight: '500',
fontSize: theme.font.sm,
@@ -29,7 +29,7 @@ export const StyledTitle = styled.div(({ theme }) => ({
color: theme.colors.primaryColor,
},
}));
export const StyledSubTitle = styled.div(({ theme }) => ({
export const StyledSubTitle = styled('div')(({ theme }) => ({
color: theme.colors.popoverColor,
fontWeight: '500',
fontSize: theme.font.sm,
@@ -38,7 +38,7 @@ export const StyledSubTitle = styled.div(({ theme }) => ({
marginTop: '28px',
padding: '0 16px',
}));
export const StyledModalHeader = styled.div(() => ({
export const StyledModalHeader = styled('div')(() => ({
...displayFlex('space-between', 'center'),
paddingTop: '8px 4px 0 4px',
width: '100%',
@@ -51,7 +51,7 @@ export const StyledModalHeader = styled.div(() => ({
transition: 'background-color 0.5s',
}));
export const StyledListItem = styled.div(({ theme }) => ({
export const StyledListItem = styled('div')(({ theme }) => ({
height: '34px',
...displayFlex('space-between', 'center'),
fontSize: theme.font.sm,

View File

@@ -6,7 +6,7 @@ import {
} from '@affine/component';
import { Button } from '@affine/component';
export const StyledSplitLine = styled.div(({ theme }) => {
export const StyledSplitLine = styled('div')(({ theme }) => {
return {
width: '1px',
height: '20px',
@@ -15,7 +15,7 @@ export const StyledSplitLine = styled.div(({ theme }) => {
};
});
export const StyleWorkspaceInfo = styled.div(({ theme }) => {
export const StyleWorkspaceInfo = styled('div')(({ theme }) => {
return {
marginLeft: '15px',
width: '202px',
@@ -37,7 +37,7 @@ export const StyleWorkspaceInfo = styled.div(({ theme }) => {
};
});
export const StyleWorkspaceTitle = styled.div(({ theme }) => {
export const StyleWorkspaceTitle = styled('div')(({ theme }) => {
return {
fontSize: theme.font.base,
fontWeight: 600,
@@ -48,7 +48,7 @@ export const StyleWorkspaceTitle = styled.div(({ theme }) => {
};
});
export const StyledCard = styled.div<{
export const StyledCard = styled('div')<{
active?: boolean;
}>(({ theme, active }) => {
const borderColor = active ? theme.colors.primaryColor : 'transparent';
@@ -63,7 +63,7 @@ export const StyledCard = styled.div<{
...displayFlex('flex-start', 'flex-start'),
marginBottom: '24px',
transition: 'background .2s',
background: theme.mode === 'light' ? '#FFF' : '#2C2C2C',
background: theme.palette.mode === 'light' ? '#FFF' : '#2C2C2C',
':hover': {
background: theme.colors.cardHoverBackground,
'.add-icon': {
@@ -81,7 +81,7 @@ export const StyledFooter = styled('div')({
...displayFlex('space-between', 'center'),
});
export const StyleUserInfo = styled.div(({ theme }) => {
export const StyleUserInfo = styled('div')(({ theme }) => {
return {
textAlign: 'left',
marginLeft: '16px',
@@ -97,17 +97,17 @@ export const StyleUserInfo = styled.div(({ theme }) => {
};
});
export const StyledModalHeaderLeft = styled.div(() => {
export const StyledModalHeaderLeft = styled('div')(() => {
return { ...displayFlex('flex-start', 'center') };
});
export const StyledModalTitle = styled.div(({ theme }) => {
export const StyledModalTitle = styled('div')(({ theme }) => {
return {
fontWeight: 600,
fontSize: theme.font.h6,
};
});
export const StyledHelperContainer = styled.div(({ theme }) => {
export const StyledHelperContainer = styled('div')(({ theme }) => {
return {
color: theme.colors.iconColor,
marginLeft: '15px',
@@ -125,13 +125,13 @@ export const StyledModalContent = styled('div')({
...displayFlex('space-between', 'flex-start', 'flex-start'),
flexWrap: 'wrap',
});
export const StyledOperationWrapper = styled.div(() => {
export const StyledOperationWrapper = styled('div')(() => {
return {
...displayFlex('flex-end', 'center'),
};
});
export const StyleWorkspaceAdd = styled.div(() => {
export const StyleWorkspaceAdd = styled('div')(() => {
return {
width: '58px',
height: '58px',

View File

@@ -1,12 +1,6 @@
import {
displayFlex,
displayInlineFlex,
styled,
textEllipsis,
} from '@affine/component';
import { Button } from '@affine/component';
import { displayFlex, styled, textEllipsis } from '@affine/component';
export const StyledSplitLine = styled.div(({ theme }) => {
export const StyledSplitLine = styled('div')(({ theme }) => {
return {
width: '1px',
height: '20px',
@@ -15,7 +9,7 @@ export const StyledSplitLine = styled.div(({ theme }) => {
};
});
export const StyleWorkspaceInfo = styled.div(({ theme }) => {
export const StyleWorkspaceInfo = styled('div')(({ theme }) => {
return {
marginLeft: '15px',
width: '202px',
@@ -37,7 +31,7 @@ export const StyleWorkspaceInfo = styled.div(({ theme }) => {
};
});
export const StyleWorkspaceTitle = styled.div(({ theme }) => {
export const StyleWorkspaceTitle = styled('div')(({ theme }) => {
return {
fontSize: theme.font.base,
fontWeight: 600,
@@ -48,7 +42,7 @@ export const StyleWorkspaceTitle = styled.div(({ theme }) => {
};
});
export const StyledCard = styled.div<{
export const StyledCard = styled('div')<{
active?: boolean;
}>(({ theme, active }) => {
const borderColor = active ? theme.colors.primaryColor : 'transparent';
@@ -63,7 +57,7 @@ export const StyledCard = styled.div<{
...displayFlex('flex-start', 'flex-start'),
marginBottom: '24px',
transition: 'background .2s',
background: theme.mode === 'light' ? '#FFF' : '#2C2C2C',
background: theme.palette.mode === 'light' ? '#FFF' : '#2C2C2C',
':hover': {
background: theme.colors.cardHoverBackground,
'.add-icon': {
@@ -74,40 +68,17 @@ export const StyledCard = styled.div<{
};
});
export const StyledFooter = styled('div')({
height: '84px',
padding: '0 40px',
flexShrink: 0,
...displayFlex('space-between', 'center'),
});
export const StyleUserInfo = styled.div(({ theme }) => {
return {
textAlign: 'left',
marginLeft: '16px',
flex: 1,
p: {
lineHeight: '24px',
color: theme.colors.iconColor,
},
'p:first-of-type': {
color: theme.colors.textColor,
fontWeight: 600,
},
};
});
export const StyledModalHeaderLeft = styled.div(() => {
export const StyledModalHeaderLeft = styled('div')(() => {
return { ...displayFlex('flex-start', 'center') };
});
export const StyledModalTitle = styled.div(({ theme }) => {
export const StyledModalTitle = styled('div')(({ theme }) => {
return {
fontWeight: 600,
fontSize: theme.font.h6,
};
});
export const StyledHelperContainer = styled.div(({ theme }) => {
export const StyledHelperContainer = styled('div')(({ theme }) => {
return {
color: theme.colors.iconColor,
marginLeft: '15px',
@@ -125,13 +96,13 @@ export const StyledModalContent = styled('div')({
...displayFlex('space-between', 'flex-start', 'flex-start'),
flexWrap: 'wrap',
});
export const StyledOperationWrapper = styled.div(() => {
export const StyledOperationWrapper = styled('div')(() => {
return {
...displayFlex('flex-end', 'center'),
};
});
export const StyleWorkspaceAdd = styled.div(() => {
export const StyleWorkspaceAdd = styled('div')(() => {
return {
width: '58px',
height: '58px',
@@ -154,19 +125,3 @@ export const StyledModalHeader = styled('div')(() => {
...displayFlex('space-between', 'center'),
};
});
export const StyledSignInButton = styled(Button)(({ theme }) => {
return {
fontWeight: 700,
paddingLeft: 0,
'.circle': {
width: '40px',
height: '40px',
borderRadius: '20px',
backgroundColor: theme.colors.innerHoverBackground,
flexShrink: 0,
marginRight: '16px',
...displayInlineFlex('center', 'center'),
},
};
});

View File

@@ -1,7 +1,7 @@
import { displayFlex, styled, textEllipsis } from '@affine/component';
import Link from 'next/link';
export const StyledSliderBar = styled.div<{ show: boolean }>(
export const StyledSliderBar = styled('div')<{ show: boolean }>(
({ theme, show }) => {
return {
width: show ? '256px' : '0',
@@ -17,7 +17,7 @@ export const StyledSliderBar = styled.div<{ show: boolean }>(
};
}
);
export const StyledSliderBarWrapper = styled.div(() => {
export const StyledSliderBarWrapper = styled('div')(() => {
return {
height: '100%',
overflowX: 'hidden',
@@ -26,7 +26,7 @@ export const StyledSliderBarWrapper = styled.div(() => {
};
});
export const StyledArrowButton = styled.button<{ isShow: boolean }>(
export const StyledArrowButton = styled('button')<{ isShow: boolean }>(
({ theme, isShow }) => {
return {
width: '32px',
@@ -51,7 +51,7 @@ export const StyledArrowButton = styled.button<{ isShow: boolean }>(
}
);
export const StyledListItem = styled.div<{
export const StyledListItem = styled('div')<{
active?: boolean;
disabled?: boolean;
}>(({ theme, active, disabled }) => {
@@ -109,7 +109,7 @@ export const StyledNewPageButton = styled(StyledListItem)(() => {
};
});
export const StyledSubListItem = styled.button<{
export const StyledSubListItem = styled('button')<{
disable?: boolean;
active?: boolean;
}>(({ theme, disable, active }) => {

View File

@@ -7,7 +7,7 @@ import { Helmet } from 'react-helmet-async';
import ErrorImg from '../../public/imgs/invite-error.svg';
export const StyledContainer = styled.div(() => {
export const StyledContainer = styled('div')(() => {
return {
...displayFlex('center', 'center'),
flexDirection: 'column',

View File

@@ -3,7 +3,7 @@ import '../styles/globals.css';
import { config, setupGlobal } from '@affine/env';
import { createI18n, I18nextProvider } from '@affine/i18n';
import createCache from '@emotion/cache';
import { EmotionCache } from '@emotion/cache';
import { CacheProvider } from '@emotion/react';
import { Provider } from 'jotai';
import { useAtomsDebugValue } from 'jotai-devtools';
@@ -21,6 +21,7 @@ import { AffineSWRConfigProvider } from '../providers/AffineSWRConfigProvider';
import { ModalProvider } from '../providers/ModalProvider';
import { ThemeProvider } from '../providers/ThemeProvider';
import { NextPageWithLayout } from '../shared';
import createEmotionCache from '../utils/create-emotion-cache';
setupGlobal();
@@ -35,6 +36,7 @@ const DebugAtoms = memo(function DebugAtoms() {
return null;
});
const clientSideEmotionCache = createEmotionCache();
const helmetContext = {};
const defaultSWRConfig: SWRConfiguration = {
@@ -48,9 +50,13 @@ const defaultSWRConfig: SWRConfiguration = {
},
};
const cache = createCache({ key: 'affine' });
const App = function App({ Component, pageProps }: AppPropsWithLayout) {
const App = function App({
Component,
pageProps,
emotionCache = clientSideEmotionCache,
}: AppPropsWithLayout & {
emotionCache?: EmotionCache;
}) {
const getLayout = Component.getLayout || EmptyLayout;
const i18n = useMemo(() => createI18n(), []);
@@ -63,8 +69,8 @@ const App = function App({ Component, pageProps }: AppPropsWithLayout) {
}
return (
<I18nextProvider i18n={i18n}>
<CacheProvider value={cache}>
<CacheProvider value={emotionCache}>
<I18nextProvider i18n={i18n}>
<DebugAtoms />
<SWRConfig value={defaultSWRConfig}>
<AffineErrorBoundary router={useRouter()}>
@@ -83,6 +89,10 @@ const App = function App({ Component, pageProps }: AppPropsWithLayout) {
<HelmetProvider key="HelmetProvider" context={helmetContext}>
<Helmet>
<title>AFFiNE</title>
<meta
name="viewport"
content="initial-scale=1, width=device-width"
/>
</Helmet>
{getLayout(<Component {...pageProps} />)}
</HelmetProvider>
@@ -90,8 +100,8 @@ const App = function App({ Component, pageProps }: AppPropsWithLayout) {
</Suspense>
</AffineErrorBoundary>
</SWRConfig>
</CacheProvider>
</I18nextProvider>
</I18nextProvider>
</CacheProvider>
);
};

View File

@@ -1,7 +1,48 @@
import Document, { Head, Html, Main, NextScript } from 'next/document';
import type { EmotionJSX } from '@emotion/react/types/jsx-namespace';
import createEmotionServer from '@emotion/server/create-instance';
import Document, {
DocumentContext,
Head,
Html,
Main,
NextScript,
} from 'next/document';
import * as React from 'react';
export default class AppDocument extends Document {
import createEmotionCache from '../utils/create-emotion-cache';
export default class AppDocument extends Document<{
emotionStyleTags: EmotionJSX.Element[];
}> {
static getInitialProps = async (ctx: DocumentContext) => {
const originalRenderPage = ctx.renderPage;
const cache = createEmotionCache();
const { extractCriticalToChunks } = createEmotionServer(cache);
ctx.renderPage = () =>
originalRenderPage({
enhanceApp: (App: any) =>
function EnhanceApp(props) {
return <App emotionCache={cache} {...props} />;
},
});
const initialProps = await Document.getInitialProps(ctx);
const emotionStyles = extractCriticalToChunks(initialProps.html);
const emotionStyleTags = emotionStyles.styles.map(style => (
<style
data-emotion={`${style.key} ${style.ids.join(' ')}`}
key={style.key}
dangerouslySetInnerHTML={{ __html: style.css }}
/>
));
return {
...initialProps,
emotionStyleTags,
};
};
render() {
return (
<Html>
@@ -14,6 +55,8 @@ export default class AppDocument extends Document {
href="/apple-touch-icon.png"
/>
<link rel="icon" sizes="192x192" href="/chrome-192x192.png" />
<meta name="emotion-insertion-point" content="" />
{this.props.emotionStyleTags}
</Head>
<body>
<Main />

View File

@@ -22,7 +22,7 @@ import { PageLoading } from '../../../components/pure/loading';
import { WorkspaceLayout } from '../../../layouts';
import { NextPageWithLayout } from '../../../shared';
export const NavContainer = styled.div(({ theme }) => {
export const NavContainer = styled('div')(({ theme }) => {
return {
width: '100vw',
padding: '0 12px',

View File

@@ -1,111 +1,70 @@
import {
AffineTheme,
Theme,
ThemeMode,
ThemeProviderProps,
ThemeProviderValue,
} from '@affine/component';
import { AffineTheme, ThemeProviderProps } from '@affine/component';
import {
getDarkTheme,
getLightTheme,
globalThemeVariables,
ThemeProvider as ComponentThemeProvider,
ThemeProvider as AffineThemeProvider,
} from '@affine/component';
import { GlobalStyles } from '@mui/material';
import {
createTheme as MuiCreateTheme,
ThemeProvider as MuiThemeProvider,
} from '@mui/material/styles';
import { useAtom } from 'jotai';
import { atomWithStorage } from 'jotai/utils';
import { ThemeProvider as NextThemeProvider, useTheme } from 'next-themes';
import type { PropsWithChildren } from 'react';
import React, {
createContext,
useCallback,
useContext,
useEffect,
useMemo,
useRef,
} from 'react';
import React, { memo, useEffect, useMemo, useState } from 'react';
import { useCurrentPageId } from '../hooks/current/use-current-page-id';
import { useCurrentWorkspace } from '../hooks/current/use-current-workspace';
import { usePageMeta } from '../hooks/use-page-meta';
import { useSystemTheme } from '../hooks/use-system-theme';
export const ThemeContext = createContext<ThemeProviderValue>({
mode: 'light',
changeMode: () => {},
theme: getLightTheme('page'),
});
export const useTheme = () => useContext(ThemeContext);
const muiTheme = MuiCreateTheme();
const ThemeInjector = React.memo<{
theme: Theme;
themeStyle: AffineTheme;
}>(function ThemeInjector({ theme, themeStyle }) {
}>(function ThemeInjector({ themeStyle }) {
return (
<GlobalStyles
styles={{
':root': globalThemeVariables(theme, themeStyle) as any,
':root': globalThemeVariables(themeStyle) as any,
}}
/>
);
});
const themeAtom = atomWithStorage<ThemeMode>('affine-theme', 'auto');
const ThemeProviderInner = memo<React.PropsWithChildren>(
function ThemeProviderInner({ children }) {
const { theme } = useTheme();
const [currentWorkspace] = useCurrentWorkspace();
const [currentPage] = useCurrentPageId();
const pageMeta = usePageMeta(currentWorkspace?.blockSuiteWorkspace ?? null);
const editorMode =
pageMeta.find(page => page.id === currentPage)?.mode ?? 'page';
const themeStyle = useMemo(() => getLightTheme(editorMode), [editorMode]);
const darkThemeStyle = useMemo(
() => getDarkTheme(editorMode),
[editorMode]
);
// SSR will always render the light theme, so we need to defer the theme if user selected dark mode
const [deferTheme, setDeferTheme] = useState('light');
useEffect(() => {
setDeferTheme(theme === 'dark' ? 'dark' : 'light');
}, [theme]);
return (
<AffineThemeProvider
theme={deferTheme === 'dark' ? darkThemeStyle : themeStyle}
>
<ThemeInjector
themeStyle={deferTheme === 'dark' ? darkThemeStyle : themeStyle}
/>
{children}
</AffineThemeProvider>
);
}
);
const themes = ['dark', 'light'];
export const ThemeProvider = ({
children,
}: PropsWithChildren<ThemeProviderProps>) => {
const [theme, setTheme] = useAtom(themeAtom);
const systemTheme = useSystemTheme();
// fixme: use mode detect
const [currentWorkspace] = useCurrentWorkspace();
const [currentPage] = useCurrentPageId();
const pageMeta = usePageMeta(currentWorkspace?.blockSuiteWorkspace ?? null);
const editorMode =
pageMeta.find(page => page.id === currentPage)?.mode ?? 'page';
const themeStyle = useMemo(
() =>
theme === 'light' ? getLightTheme(editorMode) : getDarkTheme(editorMode),
[editorMode, theme]
);
const changeMode = useCallback(
(themeMode: Theme) => {
setTheme(themeMode);
},
[setTheme]
);
const onceRef = useRef(false);
useEffect(() => {
if (onceRef.current) {
return;
}
if (theme !== 'auto') {
setTheme(systemTheme);
}
onceRef.current = true;
}, [setTheme, systemTheme, theme]);
const realTheme: ThemeMode = theme === 'auto' ? systemTheme : theme;
return (
// Use MuiThemeProvider is just because some Transitions in Mui components need it
<MuiThemeProvider theme={muiTheme}>
<ThemeContext.Provider
value={{ mode: realTheme, changeMode, theme: themeStyle }}
>
<ThemeInjector theme={realTheme} themeStyle={themeStyle} />
<ComponentThemeProvider theme={themeStyle}>
{children}
</ComponentThemeProvider>
</ThemeContext.Provider>
</MuiThemeProvider>
<NextThemeProvider themes={themes}>
<ThemeProviderInner>{children}</ThemeProviderInner>
</NextThemeProvider>
);
};

View File

@@ -0,0 +1,17 @@
import { getEnvironment } from '@affine/env';
import createCache from '@emotion/cache';
const isBrowser = getEnvironment().isBrowser;
export default function createEmotionCache() {
let insertionPoint;
if (isBrowser) {
const emotionInsertionPoint = document.querySelector<HTMLMetaElement>(
'meta[name="emotion-insertion-point"]'
);
insertionPoint = emotionInsertionPoint ?? undefined;
}
return createCache({ key: 'affine-style', insertionPoint });
}