Merge pull request #84 from unix/themes

docs: keep the theme perferences for visitors
This commit is contained in:
witt
2020-04-11 16:50:23 +08:00
committed by GitHub
14 changed files with 127 additions and 1621 deletions

View File

@@ -1,4 +1,4 @@
import React, { useMemo, useRef, useState } from 'react'
import React, { useEffect, useMemo, useRef, useState } from 'react'
import { NormalSizes } from '../utils/prop-types'
import useClickAway from '../utils/use-click-away'
import { pickChildByProps, pickChildrenFirst } from '../utils/collections'
@@ -12,6 +12,7 @@ import { getSizes } from './styles'
interface Props {
disabled?: boolean
size?: NormalSizes
value?: string
initialValue?: string
placeholder?: React.ReactNode | string
icon?: React.ReactNode
@@ -32,8 +33,8 @@ type NativeAttrs = Omit<React.HTMLAttributes<any>, keyof Props>
export type SelectProps = Props & typeof defaultProps & NativeAttrs
const Select: React.FC<React.PropsWithChildren<SelectProps>> = ({
children, size, disabled, initialValue: init, placeholder,
icon: Icon, onChange, className, pure, ...props
children, size, disabled, initialValue: init, value: customValue,
icon: Icon, onChange, className, pure, placeholder, ...props
}) => {
const theme = useTheme()
const ref = useRef<HTMLDivElement>(null)
@@ -63,6 +64,11 @@ const Select: React.FC<React.PropsWithChildren<SelectProps>> = ({
useClickAway(ref, () => setVisible(false))
useEffect(() => {
if (customValue === undefined) return
setValue(customValue)
}, [customValue])
const selectedChild = useMemo(() => {
const [, optionChildren] = pickChildByProps(children, 'value', value)
const child = pickChildrenFirst(optionChildren)

View File

@@ -1,4 +1,4 @@
import React, { MutableRefObject, useState } from 'react'
import React, { MutableRefObject, useEffect, useState } from 'react'
import { createPortal } from 'react-dom'
import usePortal from '../utils/use-portal'
import useResize from '../utils/use-resize'
@@ -54,6 +54,14 @@ const Dropdown: React.FC<React.PropsWithChildren<Props>> = React.memo(({
if (!shouldUpdatePosition) return
updateRect()
})
useEffect(() => {
if (!parent || !parent.current) return
parent.current.addEventListener('mouseenter', updateRect)
return () => {
if (!parent || !parent.current) return
parent.current.removeEventListener('mouseenter', updateRect)
}
}, [parent])
const clickHandler = (event: React.MouseEvent<HTMLDivElement>) => {
event.stopPropagation()

View File

@@ -21,6 +21,8 @@ const Controls: React.FC<{}> = React.memo(({
const switchThemes = (type: 'dark' | 'light') => {
updateCustomTheme({ type })
if (typeof window === 'undefined' || !window.localStorage) return
window.localStorage.setItem('theme', type)
}
const switchLanguages = () => {
updateChineseState(!isChinese)
@@ -41,7 +43,7 @@ const Controls: React.FC<{}> = React.memo(({
onClick={redirectGithub}
title={isChinese? '代码仓库' : 'Github Repository'}>{isChinese ? '代码仓库' : 'Github'}</Button>
<Spacer x={.75} />
<Select size="small" pure onChange={switchThemes} initialValue={isDark ? 'dark' : 'light'}
<Select size="small" pure onChange={switchThemes} value={isDark ? 'dark' : 'light'}
title={isChinese ? '切换主题' : 'Switch Themes'}>
<Select.Option value="light">
<div className="select-content">

View File

@@ -1,4 +1,4 @@
import React, { useState } from 'react'
import React, { useEffect, useState } from 'react'
import { useTheme } from 'components'
import Sidebar from './sidebar'
import TabbarMobile from './sidebar/tabbar-mobile'
@@ -16,6 +16,7 @@ export interface Props {
export const Layout: React.FC<React.PropsWithChildren<Props>> = React.memo(({ children }) => {
const theme = useTheme()
const [showAfterRender, setShowAfterRender] = useState<boolean>(false)
const { tabbarFixed } = useConfigs()
const [, setBodyScroll] = useBodyScroll(null, { scrollLayer: true })
const [expanded, setExpanded] = useState<boolean>(false)
@@ -24,6 +25,9 @@ export const Layout: React.FC<React.PropsWithChildren<Props>> = React.memo(({ ch
setBodyScroll(!expanded)
}
useEffect(() => setShowAfterRender(true), [])
if (!showAfterRender) return null
return (
<div className="layout">
<TabbarMobile onClick={mobileTabbarClickHandler} />

View File

@@ -1,8 +1,12 @@
import React from 'react'
import React, { useEffect, useState } from 'react'
import MenuLinks from './menu-links'
import MenuSticker from './menu-sticker'
const Menu = () => {
const Menu: React.FC<{}> = () => {
const [showAfterRender, setShowAfterRender] = useState<boolean>(false)
useEffect(() => setShowAfterRender(true), [])
if (!showAfterRender) return null
return (
<div>
<MenuLinks />

View File

@@ -57,6 +57,7 @@ const MenuSticker = () => {
opacity: 0;
visibility: hidden;
pointer-events: none;
background-color: ${theme.palette.background};
}
.nav-fill.active {

10
lib/use-dom-clean.ts Normal file
View File

@@ -0,0 +1,10 @@
import { useEffect } from 'react'
const useDomClean = (): void => {
useEffect(() => {
document.documentElement.removeAttribute('style')
document.body.removeAttribute('style')
}, [])
}
export default useDomClean

View File

@@ -60,7 +60,7 @@
"eslint-plugin-react": "^7.19.0",
"extract-mdx-metadata": "^1.0.0",
"fs-extra": "^8.1.0",
"next": "^9.3.2",
"next": "^9.3.4",
"react": "^16.13.0",
"react-color": "^2.18.0",
"react-dom": "^16.13.0",
@@ -71,4 +71,4 @@
"webpack-cli": "^3.3.11"
},
"dependencies": {}
}
}

View File

@@ -1,18 +1,26 @@
import Head from 'next/head'
import { NextPage } from 'next'
import { AppProps } from 'next/app'
import { useState } from 'react'
import React, { useEffect, useState } from 'react'
import { CSSBaseline, ZEITUIProvider, useTheme, ZeitUIThemes } from 'components'
import Menu from 'lib/components/menu'
import ConfigContext from 'lib/config-provider'
import useDomClean from 'lib/use-dom-clean'
import { DeepPartial } from 'components/utils/types'
const Application: NextPage<AppProps> = ({ Component, pageProps }) => {
const Application: NextPage<AppProps<{}>> = ({ Component, pageProps }) => {
const theme = useTheme()
const [customTheme, setCustomTheme] = useState<DeepPartial<ZeitUIThemes>>({})
const themeChangeHandle = (theme: DeepPartial<ZeitUIThemes>) => {
setCustomTheme(theme)
}
useEffect(() => {
const theme = window.localStorage.getItem('theme')
if (theme !== 'dark') return
themeChangeHandle({ type: 'dark' })
}, [])
useDomClean()
return (
<>

View File

@@ -22,20 +22,29 @@ class MyDocument extends Document {
<Html>
<Head />
<body>
<Main />
<NextScript />
<script async src={`https://www.googletagmanager.com/gtag/js?id=UA-110371817-12`} />
<script
async
dangerouslySetInnerHTML={{
__html: `
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'UA-110371817-12');
`
}}
/>
<script dangerouslySetInnerHTML={{ __html: `
(function(){
if (!window.localStorage) return;
if (window.localStorage.getItem('theme') === 'dark') {
document.documentElement.style.background = '#000';
document.body.style.background = '#000';
};
})()
`}} />
<Main />
<NextScript />
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-110371817-12" />
<script
async
dangerouslySetInnerHTML={{
__html: `
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'UA-110371817-12');
`
}}
/>
</body>
</Html>
)

View File

@@ -167,7 +167,7 @@ Retrieve text input from a user.
| Attribute | Description | Type | Accepted values | Default
| ---------- | ---------- | ---- | -------------- | ------ |
| **value** | input value | `string` | - | - |
| **initialValue** | inital value | `string` | - | - |
| **initialValue** | initial value | `string` | - | - |
| **placeholder** | placeholder | `string` | - | - |
| **size** | input size | `NormalSizes` | `'mini', 'small', 'medium', 'large'` | `medium` |
| **status** | current status | `NormalTypes` | `'default', 'secondary', 'success', 'warning', 'error'` | `default` |

View File

@@ -75,7 +75,8 @@ Display a dropdown list of items.
| Attribute | Description | Type | Accepted values | Default
| ---------- | ---------- | ---- | -------------- | ------ |
| **initialValue** | selected value | `string` | - | - |
| **value** | selected value | `string` | - | - |
| **initialValue** | initial value | `string` | - | - |
| **placeholder** | placeholder string | `string` | - | - |
| **size** | select component size | `NormalSizes` | `'mini', 'small', 'medium', 'large'` | `medium` |
| **icon** | icon component | `ReactNode` | - | `SVG Component` |

View File

@@ -74,6 +74,7 @@ export const meta = {
| 属性 | 描述 | 类型 | 推荐值 | 默认
| ---------- | ---------- | ---- | -------------- | ------ |
| **value** | 手动设置选择器的值 | `string` | - | - |
| **initialValue** | 选择器初始值 | `string` | - | - |
| **placeholder** | 占位文本内容 | `string` | - | - |
| **size** | 选择器组件大小 | `NormalSizes` | `'mini', 'small', 'medium', 'large'` | `medium` |

1638
yarn.lock

File diff suppressed because it is too large Load Diff