chore: change to monorepo

This commit is contained in:
alt0
2022-10-14 13:26:06 +08:00
parent 7f9e7efb67
commit 0279dc44b7
36 changed files with 305 additions and 167 deletions

View File

@@ -0,0 +1,4 @@
import { useContext } from 'react';
import { ThemeContext } from './themeProvider';
export const useTheme = () => useContext(ThemeContext);

View File

@@ -0,0 +1,6 @@
export type { ThemeMode, ThemeProviderProps, AffineTheme } from './types';
export { styled } from './styled';
export { ThemeProvider } from './themeProvider';
export { lightTheme, darkTheme } from './theme';
export { useTheme } from './hooks';

View File

@@ -0,0 +1,3 @@
import emotionStyled from '@emotion/styled';
export const styled = emotionStyled;

View File

@@ -0,0 +1,55 @@
import '@emotion/react';
import { AffineTheme, ThemeMode } from './types';
export const lightTheme: AffineTheme = {
colors: {
primary: '#0070f3',
},
};
export const darkTheme: AffineTheme = {
colors: {
primary: '#000',
},
};
export const globalThemeConstant = (mode: ThemeMode, theme: AffineTheme) => {
const isDark = mode === 'dark';
return {
'--color-primary': theme.colors.primary,
'--page-background-color': isDark ? '#3d3c3f' : '#fff',
'--page-text-color': isDark ? '#fff' : '#3a4c5c',
// editor style variables
'--affine-primary-color': isDark ? '#fff' : '#3a4c5c',
'--affine-muted-color': '#a6abb7',
'--affine-highlight-color': '#6880ff',
'--affine-placeholder-color': '#c7c7c7',
'--affine-selected-color': 'rgba(104, 128, 255, 0.1)',
'--affine-font-family':
'Avenir Next, 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',
'--affine-font-family2':
'Roboto Mono, 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',
};
};
const editorStyleVariable = {
'--affine-primary-color': '#3a4c5c',
'--affine-muted-color': '#a6abb7',
'--affine-highlight-color': '#6880ff',
'--affine-placeholder-color': '#c7c7c7',
'--affine-selected-color': 'rgba(104, 128, 255, 0.1)',
'--affine-font-family':
'Avenir Next, 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',
'--affine-font-family2':
'Roboto Mono, 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',
};
const pageStyleVariable = {
'--page-background-color': '#fff',
'--page-text-color': '#3a4c5c',
};

View File

@@ -0,0 +1,79 @@
import {
ThemeProvider as EmotionThemeProvider,
Global,
css,
} from '@emotion/react';
import { createContext, useEffect, useState } from 'react';
import type { PropsWithChildren } from 'react';
import {
Theme,
ThemeMode,
ThemeProviderProps,
ThemeProviderValue,
} from './types';
import { lightTheme, darkTheme, globalThemeConstant } from './theme';
import { SystemThemeHelper, localStorageThemeHelper } from './utils';
export const ThemeContext = createContext<ThemeProviderValue>({
mode: 'light',
changeMode: () => {},
theme: lightTheme,
});
export const ThemeProvider = ({
defaultTheme = 'light',
children,
}: PropsWithChildren<ThemeProviderProps>) => {
const [theme, setTheme] = useState<Theme>(defaultTheme);
const [mode, setMode] = useState<ThemeMode>('auto');
const themeStyle = theme === 'light' ? lightTheme : darkTheme;
const changeMode = (themeMode: ThemeMode) => {
themeMode !== mode && setMode(themeMode);
// Remember the theme mode which user selected for next time
localStorageThemeHelper.set(themeMode);
};
useEffect(() => {
setMode(localStorageThemeHelper.get() || 'auto');
}, []);
useEffect(() => {
const systemThemeHelper = new SystemThemeHelper();
const selectedThemeMode = localStorageThemeHelper.get();
const themeMode = selectedThemeMode || mode;
if (themeMode === 'auto') {
setTheme(systemThemeHelper.get());
} else {
setTheme(themeMode);
}
// When system theme changed, change the theme mode
systemThemeHelper.onChange(() => {
// TODO: There may be should be provided a way to let user choose whether to
if (mode === 'auto') {
setTheme(systemThemeHelper.get());
}
});
return () => {
systemThemeHelper.dispose();
};
}, [mode]);
return (
<ThemeContext.Provider value={{ mode, changeMode, theme: themeStyle }}>
<Global
styles={css`
:root {
${globalThemeConstant(mode, themeStyle)}
}
`}
/>
<EmotionThemeProvider theme={themeStyle}>{children}</EmotionThemeProvider>
</ThemeContext.Provider>
);
};
export default ThemeProvider;

View File

@@ -0,0 +1,22 @@
export type Theme = 'light' | 'dark';
export type ThemeMode = Theme | 'auto';
export type ThemeProviderProps = {
defaultTheme?: Theme;
};
export type ThemeProviderValue = {
theme: AffineTheme;
mode: ThemeMode;
changeMode: (newMode: ThemeMode) => void;
};
export interface AffineTheme {
colors: {
primary: string;
};
}
declare module '@emotion/react' {
export interface Theme extends AffineTheme {}
}

View File

@@ -0,0 +1,2 @@
export * from './systemThemeHelper';
export * from './localStorageThemeHelper';

View File

@@ -0,0 +1,13 @@
import { ThemeMode } from '../types';
export class LocalStorageThemeHelper {
name = 'Affine-theme-mode';
get = (): ThemeMode | null => {
return localStorage.getItem(this.name) as ThemeMode | null;
};
set = (mode: ThemeMode) => {
localStorage.setItem(this.name, mode);
};
}
export const localStorageThemeHelper = new LocalStorageThemeHelper();

View File

@@ -0,0 +1,29 @@
import { Theme } from '../types';
export class SystemThemeHelper {
media: MediaQueryList = window.matchMedia('(prefers-color-scheme: light)');
eventList: Array<(e: Event) => void> = [];
eventHandler = (e: Event) => {
this.eventList.forEach(fn => fn(e));
};
constructor() {
this.media.addEventListener('change', this.eventHandler);
}
get = (): Theme => {
if (typeof window === 'undefined') {
return 'light';
}
return this.media.matches ? 'light' : 'dark';
};
onChange = (callback: () => void) => {
this.eventList.push(callback);
};
dispose = () => {
this.eventList = [];
this.media.removeEventListener('change', this.eventHandler);
};
}