feat: add theme change handler

This commit is contained in:
QiShaoXuan 2022-09-26 16:24:29 +08:00
parent 62d4b0a32d
commit 163f6cea08
9 changed files with 99 additions and 19 deletions

View File

@ -1,7 +1,6 @@
import { LitElement, css, html, unsafeCSS } from 'lit'; import { LitElement, css, html } from 'lit';
import { customElement, property, state } from 'lit/decorators.js'; import { customElement, property, state } from 'lit/decorators.js';
import * as React from 'react'; import * as React from 'react';
import { theme } from '@/styles';
export const tagName = 'simple-counter'; export const tagName = 'simple-counter';
@ -28,7 +27,7 @@ export class Counter extends LitElement {
static styles = css` static styles = css`
.counter-container { .counter-container {
display: flex; display: flex;
color: ${unsafeCSS(theme.colors.primary)}; color: var(--color-primary);
} }
button { button {
margin: 0 5px; margin: 0 5px;

View File

@ -1,12 +1,11 @@
import type { AppProps } from 'next/app'; import type { AppProps } from 'next/app';
import { ThemeProvider } from '@emotion/react'; import { ThemeProvider } from '@/styles';
import { theme } from '../styles';
import '../../public/globals.css'; import '../../public/globals.css';
function MyApp({ Component, pageProps }: AppProps) { function MyApp({ Component, pageProps }: AppProps) {
return ( return (
<ThemeProvider theme={theme}> <ThemeProvider>
<Component {...pageProps} /> <Component {...pageProps} />
</ThemeProvider> </ThemeProvider>
); );

View File

@ -1,6 +1,5 @@
import type { NextPage } from 'next'; import type { NextPage } from 'next';
import styled from '@emotion/styled'; import { styled, useTheme } from '@/styles';
import '@/components/simple-counter'; import '@/components/simple-counter';
const Button = styled('div')(({ theme }) => { const Button = styled('div')(({ theme }) => {
@ -10,10 +9,18 @@ const Button = styled('div')(({ theme }) => {
}); });
const Home: NextPage = () => { const Home: NextPage = () => {
const { changeMode, mode } = useTheme();
return ( return (
<div> <div>
<Button>A button use the theme styles</Button> <Button>A button use the theme styles</Button>
<simple-counter name="A counter created by web component" /> <simple-counter name="A counter created by web component" />
<button
onClick={() => {
changeMode(mode === 'dark' ? 'light' : 'dark');
}}
>
current theme mode :{mode}(click to change)
</button>
</div> </div>
); );
}; };

4
src/styles/hooks.ts Normal file
View File

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

View File

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

3
src/styles/styled.ts Normal file
View File

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

View File

@ -1,17 +1,20 @@
import '@emotion/react'; import '@emotion/react';
import { AffineTheme } from './types';
interface AffineTheme { export const lightTheme: AffineTheme = {
colors: {
primary: string;
};
}
export const theme: AffineTheme = {
colors: { colors: {
primary: '#0070f3', primary: '#0070f3',
}, },
}; };
declare module '@emotion/react' { export const darkTheme: AffineTheme = {
export interface Theme extends AffineTheme {} colors: {
} primary: '#000',
},
};
export const globalThemeConstant = (theme: AffineTheme) => {
return {
'--color-primary': theme.colors.primary,
};
};

View File

@ -0,0 +1,39 @@
import {
ThemeProvider as EmotionThemeProvider,
Global,
css,
} from '@emotion/react';
import { createContext, useState } from 'react';
import type { PropsWithChildren } from 'react';
import { ThemeMode, ThemeProviderProps, ThemeProviderValue } from './types';
import { lightTheme, darkTheme, globalThemeConstant } from './theme';
export const ThemeContext = createContext<ThemeProviderValue>({
mode: 'light',
changeMode: () => {},
theme: lightTheme,
});
export const ThemeProvider = ({
defaultMode = 'light',
children,
}: PropsWithChildren<ThemeProviderProps>) => {
const [mode, setMode] = useState<ThemeMode>(defaultMode);
const theme = mode === 'dark' ? darkTheme : lightTheme;
const changeMode = (themeMode: ThemeMode) => {
setMode(themeMode);
};
return (
<ThemeContext.Provider value={{ mode, changeMode, theme }}>
<Global
styles={css`
:root {
${globalThemeConstant(theme)}
}
`}
/>
<EmotionThemeProvider theme={theme}>{children}</EmotionThemeProvider>
</ThemeContext.Provider>
);
};

21
src/styles/types.ts Normal file
View File

@ -0,0 +1,21 @@
export type ThemeMode = 'light' | 'dark';
export type ThemeProviderProps = {
defaultMode?: ThemeMode;
};
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 {}
}