Merge pull request #81 from unix/customization

docs: add customization page
This commit is contained in:
witt
2020-04-11 13:24:07 +08:00
committed by GitHub
39 changed files with 809 additions and 62 deletions

View File

@@ -3,19 +3,21 @@ import withDefaults from '../utils/with-defaults'
import AutoCompleteSearch from './auto-complete-searching'
interface Props {
hidden?: boolean
className?: string
}
const defaultProps = {
hidden: false,
className: '',
}
export type AutoCompleteEmptyProps = Props & typeof defaultProps & React.HTMLAttributes<any>
const AutoCompleteEmpty: React.FC<React.PropsWithChildren<AutoCompleteEmptyProps>> = ({
children, className,
children, hidden, className,
}) => {
if (hidden) return null
return <AutoCompleteSearch className={className}>{children}</AutoCompleteSearch>
}

View File

@@ -14,7 +14,7 @@ export type AutoCompleteOption = {
value: string
}
export type AutoCompleteOptions = Array<AutoCompleteOption | typeof AutoCompleteItem>
export type AutoCompleteOptions = Array<typeof AutoCompleteItem | AutoCompleteOption>
interface Props {
options: AutoCompleteOptions
@@ -32,7 +32,7 @@ interface Props {
}
const defaultProps = {
options: [] as AutoCompleteOptions[],
options: [] as AutoCompleteOptions,
initialValue: '',
disabled: false,
clearable: false,
@@ -43,7 +43,7 @@ const defaultProps = {
type NativeAttrs = Omit<React.InputHTMLAttributes<any>, keyof Props>
export type AutoCompleteProps = Props & typeof defaultProps & NativeAttrs
const childrenToOptionsNode = (options: AutoCompleteOptions) => {
const childrenToOptionsNode = (options: Array<AutoCompleteOption>) => {
if (options.length === 0) return null
return options.map((item, index) => {
@@ -85,7 +85,7 @@ const AutoComplete: React.FC<React.PropsWithChildren<AutoCompleteProps>> = ({
if (state === '') return null
return hasEmptyChild ? emptyChild : <AutoCompleteEmpty>No Options</AutoCompleteEmpty>
}
return childrenToOptionsNode(options)
return childrenToOptionsNode(options as Array<AutoCompleteOption>)
}, [searching, options])
const showClearIcon = useMemo(
() => clearable && searching === undefined,

View File

@@ -28,7 +28,7 @@ const Link = React.memo(React.forwardRef<HTMLAnchorElement, React.PropsWithChild
href, color, underline, pure, children, className, block, ...props
}, ref: React.Ref<HTMLAnchorElement>) => {
const theme = useTheme()
const linkColor = color || block ? theme.palette.success : 'inherit'
const linkColor = color || block ? theme.palette.link : 'inherit'
const padding = block ? theme.layout.gapQuarter : '0'
const decoration = underline ? 'underline' : 'none'

View File

@@ -54,7 +54,7 @@ type PopoverComponent<P = {}> = React.FC<P> & {
type ComponentProps = Partial<typeof defaultProps>
& Omit<Props, keyof typeof defaultProps>
& Omit<TooltipProps, keyof ExcludeTooltipProps>
& Partial<Omit<TooltipProps, keyof ExcludeTooltipProps>>
(Popover as PopoverComponent<ComponentProps>).defaultProps = defaultProps

View File

@@ -71,6 +71,7 @@ const Dropdown: React.FC<React.PropsWithChildren<Props>> = React.memo(({
width: ${rect.width}px;
top: ${rect.top + 2}px;
left: ${rect.left}px;
z-index: 100;
}
`}</style>
</div>

View File

@@ -80,7 +80,7 @@ const CSSBaseline: React.FC<React.PropsWithChildren<{}>> = React.memo(({
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
-webkit-box-align: center;
align-items: center;
color: ${theme.expressiveness.linkColor};
color: ${theme.palette.link};
text-decoration: ${theme.expressiveness.linkStyle};
}

View File

@@ -14,13 +14,13 @@ export interface Props {
theme?: ThemeParam
}
interface MergeObject {
export interface MergeObject {
[key: string]: any
}
const isObject = (target: any) => target && typeof target === 'object'
export const isObject = (target: any) => target && typeof target === 'object'
const deepMergeObject = <T extends MergeObject,>(source: T, target: T): T => {
export const deepMergeObject = <T extends MergeObject,>(source: T, target: T): T => {
if (!isObject(target) || !isObject(source)) return source
const sourceKeys = Object.keys(source) as Array<keyof T>
@@ -67,7 +67,7 @@ const ThemeProvider: React.FC<PropsWithChildren<Props>> = ({ children, theme })
const currentTheme = useTheme()
const merged = mergeTheme(currentTheme, customTheme)
const userTheme = currentTheme.type !== merged.type ? switchTheme(merged) : merged
return (
<ThemeContext.Provider value={userTheme}>{children}</ThemeContext.Provider>
)

View File

@@ -36,14 +36,13 @@ export const palette: ZeitUIThemesPalette = {
purple: '#f81ce5',
alert: '#ff0080',
violet: '#7928ca',
link: '#fff',
}
export const expressiveness: ZeitUIThemesExpressiveness = {
linkColor: '#fff',
linkStyle: 'none',
linkHoverStyle: 'none',
dropdownBoxShadow: '0 0 0 1px #333',
dropdownTriangleStroke: '#fff',
scrollerStart: 'rgba(255, 255, 255, 1)',
scrollerEnd: 'rgba(255, 255, 255, 0)',
shadowSmall: '0 0 0 1px #333',
@@ -62,7 +61,7 @@ export const layout: ZeitUIThemesLayout = {
pageMargin: '16pt',
pageWidth: '750pt',
pageWidthWithMargin: '782pt',
breakpointMobile: '600px',
breakpointMobile: '720px',
breakpointTablet: '960px',
radius: '5px',
}

View File

@@ -36,14 +36,13 @@ export const palette: ZeitUIThemesPalette = {
purple: '#f81ce5',
alert: '#ff0080',
violet: '#7928ca',
link: '#0070f3',
}
export const expressiveness: ZeitUIThemesExpressiveness = {
linkColor: '#0070f3',
linkStyle: 'none',
linkHoverStyle: 'none',
dropdownBoxShadow: '0 4px 4px 0 rgba(0, 0, 0, 0.02)',
dropdownTriangleStroke: '#fff',
scrollerStart: 'rgba(255, 255, 255, 1)',
scrollerEnd: 'rgba(255, 255, 255, 0)',
shadowSmall: '0 5px 10px rgba(0, 0, 0, 0.12)',
@@ -62,7 +61,7 @@ export const layout: ZeitUIThemesLayout = {
pageMargin: '16pt',
pageWidth: '750pt',
pageWidthWithMargin: '782pt',
breakpointMobile: '600px',
breakpointMobile: '720px',
breakpointTablet: '960px',
radius: '5px',
}

View File

@@ -30,14 +30,13 @@ export interface ZeitUIThemesPalette {
purple: string
alert: string
violet: string
link: string
}
export interface ZeitUIThemesExpressiveness {
linkColor: string
linkStyle: string
linkHoverStyle: string
dropdownBoxShadow: string
dropdownTriangleStroke: string
scrollerStart: string
scrollerEnd: string
shadowSmall: string

View File

@@ -105,7 +105,7 @@ const Attributes: React.FC<React.PropsWithChildren<AttributesProps>> = React.mem
border-left: 1px solid transparent;
}
@media only screen and (max-width: 767px) {
@media only screen and (max-width: ${theme.layout.breakpointMobile}) {
.attr {
overflow-x: scroll;
}

View File

@@ -9,7 +9,7 @@ import SunIcon from './icons/sun'
const Controls: React.FC<{}> = React.memo(({
}) => {
const theme = useTheme()
const { onChange, updateChineseState } = useConfigs()
const { updateCustomTheme, updateChineseState } = useConfigs()
const { pathname } = useRouter()
const { locale } = useLocale()
const isChinese = useMemo(() => locale === 'zh-cn', [locale])
@@ -19,9 +19,8 @@ const Controls: React.FC<{}> = React.memo(({
return pathname.replace(locale, nextLocale)
}, [locale, pathname])
const switchThemes = (val: string) => {
const isDark = val === 'dark'
onChange && onChange(isDark)
const switchThemes = (type: 'dark' | 'light') => {
updateCustomTheme({ type })
}
const switchLanguages = () => {
updateChineseState(!isChinese)
@@ -87,7 +86,7 @@ const Controls: React.FC<{}> = React.memo(({
align-items: center;
}
@media only screen and (max-width: 767px) {
@media only screen and (max-width: ${theme.layout.breakpointMobile}) {
.controls {
display: none;
pointer-events: none;

View File

@@ -0,0 +1,138 @@
import React, { useMemo } from 'react'
import { Text, Spacer, useTheme, Code, useToasts } from 'components'
import DefaultTheme from 'components/styles/themes/default'
import { isObject, MergeObject } from 'components/styles/theme-provider/theme-provider'
import { LiveEditor, LiveProvider } from 'react-live'
import makeCodeTheme from 'lib/components/playground/code-theme'
import useClipboard from 'components/utils/use-clipboard'
import CopyIcon from 'components/snippet/snippet-icon'
import { useConfigs } from 'lib/config-context'
export const getDeepDifferents = <T extends MergeObject,>(source: T, target: T): T => {
if (!isObject(target) || !isObject(source)) return target
const sourceKeys = Object.keys(source) as Array<keyof T>
let result = {} as T
for (const key of sourceKeys) {
const sourceValue = source[key]
const targetValue = target[key]
if (isObject(sourceValue) && isObject(targetValue)) {
const childrenDiff = getDeepDifferents(sourceValue, { ...targetValue })
if (Object.keys(childrenDiff).length !== 0) {
result[key] = childrenDiff
}
} else if (sourceValue !== targetValue) {
result[key] = targetValue
}
}
return result
}
const CustomizationCodes = () => {
const theme = useTheme()
const { isChinese } = useConfigs()
const codeTheme = makeCodeTheme(theme)
const { copy } = useClipboard()
const [, setToast] = useToasts()
const deepDifferents = useMemo(
() => getDeepDifferents(DefaultTheme, theme),
[DefaultTheme, theme],
)
const userCodes = useMemo(() => {
return `const myTheme = ${JSON.stringify(deepDifferents, null, 2)}
/***
* Usage::
* export const App = () => {
* return (
* <ZEITUIProvider theme={myTheme}>
* <CSSBaseline />
* <YourComponent />
* </ZEITUIProvider>
* )
* }
**/`
}, [deepDifferents])
const copyCode = () => {
copy(userCodes)
setToast({ text: 'Theme code copied.' })
}
return (
<div className="custom-codes">
<h3 className="title">{isChinese ? '主题代码' : 'Theme Codes'}</h3>
{isChinese ? (
<Text> <Code>copy</Code> 使</Text>
) : (
<Text>This is all your changes, click <Code>copy</Code> to use it in your own project.</Text>
)}
<Spacer y={2} />
<div className="codes">
<div className="copy" onClick={copyCode}><CopyIcon /></div>
<LiveProvider code={userCodes} disabled theme={codeTheme}>
<LiveEditor />
</LiveProvider>
</div>
<Spacer y={5} />
<style jsx>{`
.custom-codes {
display: flex;
flex-direction: column;
flex: 1;
margin: 4.5rem auto 2.5rem;
text-align: center;
}
.title {
text-align: center;
width: 80%;
margin: 0 auto;
display: inline-block;
background: ${theme.palette.foreground};
color: ${theme.palette.background};
font-size: 1rem;
line-height: 1rem;
padding: ${theme.layout.gap} 0;
text-transform: uppercase;
letter-spacing: 1.5px;
}
.codes {
width: 80%;
margin: 0 auto;
border: 1px solid ${theme.palette.border};
border-radius: ${theme.layout.radius};
overflow: hidden;
padding: calc(.6 * ${theme.layout.gap}) ${theme.layout.gap};
position: relative;
}
.copy {
position: absolute;
right: 1rem;
top: 1rem;
z-index: 2000;
color: ${theme.palette.accents_3};
cursor: pointer;
user-select: none;
transition: color 200ms ease;
}
.copy:hover {
color: ${theme.palette.accents_6};
}
@media only screen and (max-width: ${theme.layout.breakpointMobile}) {
.title, .codes {
width: 90vw;
}
}
`}</style>
</div>
)
}
export default CustomizationCodes

View File

@@ -0,0 +1,37 @@
import React from 'react'
import { Card, useTheme } from 'components'
import { CardTypes } from 'components/utils/prop-types'
const types = ['secondary', 'success', 'warning', 'error',
'dark', 'alert', 'purple', 'violet', 'cyan', 'lite']
const Colors: React.FC<React.PropsWithChildren<{}>> = () => {
const theme = useTheme()
return (
<div className="colors">
{types.map((type, index) => {
return (
<div key={`${type}-${index}`} className="color-card">
<Card type={type as CardTypes}>{type}</Card>
</div>
)
})}
<style jsx>{`
.colors {
display: flex;
flex-wrap: wrap;
}
.color-card {
display: flex;
width: 9rem;
margin-right: ${theme.layout.gapHalf};
margin-bottom: ${theme.layout.gapHalf};
}
`}</style>
</div>
)
}
export default Colors

View File

@@ -0,0 +1,86 @@
import React from 'react'
import Colors from './colors'
import { useTheme, Button, Text, Code, Spacer, Link, Select } from 'components'
import { useConfigs } from 'lib/config-context'
const Demo: React.FC<React.PropsWithChildren<{}>> = () => {
const theme = useTheme()
const { isChinese } = useConfigs()
return (
<div className="demo">
<div className="content">
{isChinese ? (
<>
<Text h3></Text>
<Text></Text>
<Text></Text>
</>
) : (
<>
<Text h3>Preview</Text>
<Text>Here&#39;s a preview of your changes to the Theme. When you set the changes,
the entire document site will change with the theme.</Text>
<Text>You can copy automatically generated codes or share your custom theme with anyone.</Text>
</>
)}
<Spacer y={1.7} />
<Text h4>{isChinese ? '色彩' : 'Colors'}</Text>
<Colors />
<Spacer y={1.7} />
<Text h4>{isChinese ? '排版' : 'Typography'}</Text>
<Text><Code>inline codes</Code></Text>
<Text><a>Hyperlink Text</a> </Text>
<Text><Link href="#" color>Link Component</Link></Text>
<Text>Our mission is to make cloud computing accessible to everyone. We build products for developers and designers. And those who aspire to become one.</Text>
<Text h6>Heading</Text>
<Text h5>Heading</Text>
<Text h4>Heading</Text>
<Text h3>Heading</Text>
<Text h2>Heading</Text>
<Text h1>Heading</Text>
<Spacer y={1.7} />
<Text h4>{isChinese ? '基础组件' : 'Basic Components'}</Text>
<Select placeholder="Choose one" initialValue="1">
<Select.Option value="1">Option 1</Select.Option>
<Select.Option value="2">Option 2</Select.Option>
</Select>
<Spacer y={1} />
<Button disabled auto size="small">Action</Button>
<Spacer inline x={.5} />
<Button auto size="small">Action</Button>
<Spacer inline x={.5} />
<Button auto type="secondary" size="small">Action</Button>
<Spacer y={.5} />
<Button>Action</Button>
</div>
<style jsx>{`
.demo {
width: 34%;
margin-top: calc(${theme.layout.gap} * 2);
margin-right: ${theme.layout.gap};
padding-right: ${theme.layout.gapQuarter};
position: relative;
border-right: 1px solid ${theme.palette.border};
height: 100%;
transition: width 200ms ease;
}
.content {
width: 100%;
}
@media only screen and (max-width: ${theme.layout.breakpointMobile}) {
.demo {
display: none;
}
}
`}</style>
</div>
)
}
export default Demo

View File

@@ -0,0 +1,118 @@
import React, { useMemo } from 'react'
import { useTheme, ZeitUIThemesPalette, Popover } from 'components'
import { ColorResult, TwitterPicker } from 'react-color'
import { useConfigs } from 'lib/config-context'
import DefaultTheme from 'components/styles/themes/default'
interface Props {
value?: string
keyName: keyof ZeitUIThemesPalette
}
const getRandomColor = () => {
const hex = `00000${(Math.random() * 0x1000000 << 0).toString(16)}`
return `#${hex.substr(-6)}`
}
const getRandomColors = () => {
const kyes = Object.keys(DefaultTheme.palette) as Array<keyof ZeitUIThemesPalette>
const basicColors = new Array(5).fill('')
.map(() => {
const index = Math.round(Math.random() * (kyes.length)) + kyes.length
return DefaultTheme.palette[kyes[index]]
})
const deduplicatedColors = [...new Set(...basicColors)]
const randomColors = new Array(10 - deduplicatedColors.length)
.fill('')
.map(() => getRandomColor())
return deduplicatedColors.concat(randomColors)
}
const EditorColorItem: React.FC<React.PropsWithChildren<Props>> = ({
keyName,
}) => {
const theme = useTheme()
const { updateCustomTheme } = useConfigs()
const label = `${keyName}`
const mainColor = useMemo(() => theme.palette[keyName], [theme.palette, keyName])
const randomColors = useMemo(() => getRandomColors(), [])
const colorChangeHandler = ({ hex }: ColorResult) => {
updateCustomTheme({
palette: { [keyName]: hex }
})
}
const popoverContent = (color: string) => (
<TwitterPicker triangle="hide" color={color}
onChangeComplete={colorChangeHandler}
colors={randomColors} />
)
return (
<Popover content={() => popoverContent(mainColor)} portalClassName="editor-popover" offset={3}>
<div className="editor-item">
<div className="dot-box">
<span className="dot" />
</div>
{label}
<style jsx>{`
.editor-item {
background-color: transparent;
width: auto;
padding: 0 ${theme.layout.gapHalf};
line-height: 2rem;
display: inline-flex;
align-items: center;
border: 1px solid ${theme.palette.border};
border-radius: ${theme.layout.radius};
color: ${theme.palette.accents_5};
margin-right: .75rem;
margin-bottom: .5rem;
cursor: pointer;
transition: color 200ms ease;
}
:global(.editor-popover .inner) {
padding: 0 !important;
}
:global(.editor-popover .twitter-picker) {
box-shadow: none !important;
border: 0 !important;
background: transparent !important;
}
.editor-item:hover {
color: ${theme.palette.accents_8};
}
.editor-item:hover .dot {
transform: scale(1);
}
.dot-box, .dot {
display: inline-flex;
justify-content: center;
align-items: center;
}
.dot-box {
width: 1rem;
height: 1rem;
margin-right: .75rem;
}
.dot {
width: 100%;
height: 100%;
border-radius: 50%;
background-color: ${mainColor};
transform: scale(.8);
transition: transform 200ms ease;
}
`}</style>
</div>
</Popover>
)
}
export default EditorColorItem

View File

@@ -0,0 +1,57 @@
import React, { useMemo } from 'react'
import { useTheme, Input, ZeitUIThemes } from 'components'
import { useConfigs } from 'lib/config-context'
type Props = {
value?: string
groupName: keyof ZeitUIThemes
keyName: string
}
const EditorInputItem: React.FC<React.PropsWithChildren<Props>> = ({
groupName, keyName
}) => {
const theme = useTheme()
const { updateCustomTheme } = useConfigs()
const currentVal = useMemo(() => {
const group = theme[groupName]
const key = keyName as keyof typeof group
return theme[groupName][key]
}, [theme.expressiveness, keyName])
const width = useMemo(() => `${currentVal}`.length > 15 ? '350px' : 'auto', [])
const changeHandler = (event: React.ChangeEvent<HTMLInputElement>) => {
updateCustomTheme({
[groupName]: { [keyName]: event.target.value },
})
}
return (
<div className="editor-item">
<Input value={currentVal as string} label={keyName}
onChange={changeHandler}
className="editor-input" />
<style jsx>{`
.editor-item {
background-color: transparent;
width: auto;
padding: 0;
line-height: 2rem;
display: inline-flex;
align-items: center;
color: ${theme.palette.accents_5};
margin-right: .75rem;
margin-bottom: .5rem;
cursor: pointer;
transition: color 200ms ease;
}
.editor-item :global(.editor-input) {
width: ${width};
}
`}</style>
</div>
)
}
export default EditorInputItem

View File

@@ -0,0 +1,120 @@
import React from 'react'
import { Text, Button, useTheme, ZeitUIThemesPalette, ZeitUIThemesExpressiveness, ZeitUIThemesLayout } from 'components'
import EditorColorItem from './editor-color-item'
import EditorInputItem from './editor-input-item'
import DefaultTheme from 'components/styles/themes/default'
import { useConfigs } from 'lib/config-context'
const basicColors: Array<keyof ZeitUIThemesPalette> = [
'accents_1', 'accents_2', 'accents_3', 'accents_4', 'accents_5', 'accents_6',
'accents_7', 'accents_8', 'foreground', 'background',
]
const statusColors: Array<keyof ZeitUIThemesPalette> = [
'success', 'successLight', 'successDark', 'error', 'errorLight', 'errorDark',
'warning', 'warningLight', 'warningDark',
]
const otherColors: Array<keyof ZeitUIThemesPalette> = [
'selection', 'secondary', 'link', 'border', 'code', 'cyan', 'purple', 'alert', 'violet'
]
const expressiveness: Array<keyof ZeitUIThemesExpressiveness> = [
'linkStyle', 'linkHoverStyle', 'dropdownBoxShadow', 'shadowSmall',
'shadowMedium', 'shadowLarge',
]
const pageLayout: Array<keyof ZeitUIThemesLayout> = [
'pageWidth', 'pageWidthWithMargin', 'pageMargin', 'radius',
]
const gapLayout: Array<keyof ZeitUIThemesLayout> = [
'gap', 'gapNegative', 'gapHalf', 'gapHalfNegative', 'gapQuarter', 'gapQuarterNegative',
]
const Editor = () => {
const theme = useTheme()
const { updateCustomTheme, isChinese } = useConfigs()
const resetLayout = () => updateCustomTheme({ layout: DefaultTheme.layout })
const restColors = () => updateCustomTheme({ palette: DefaultTheme.palette })
const resetExpressiveness = () => {
updateCustomTheme({ expressiveness: DefaultTheme.expressiveness })
}
return (
<div className="editor">
<Text h3>{isChinese ? '色彩' : 'Colors'}
<Button type="abort" auto size="mini" onClick={restColors}>{isChinese ? '重置' : 'Reset'}</Button>
</Text>
<p className="subtitle">{isChinese ? '基础' : 'basic'}</p>
<div className="content">
{basicColors.map((item, index) => (
<EditorColorItem key={`${item}-${index}`} keyName={item} />
))}
</div>
<p className="subtitle">{isChinese ? '状态' : 'status'}</p>
<div className="content">
{statusColors.map((item, index) => (
<EditorColorItem key={`${item}-${index}`} keyName={item} />
))}
</div>
<p className="subtitle">{isChinese ? '其他' : 'others'}</p>
<div className="content">
{otherColors.map((item, index) => (
<EditorColorItem key={`${item}-${index}`} keyName={item} />
))}
</div>
<Text h3>{isChinese ? '表现力' : 'Expressiveness'}
<Button type="abort" auto size="mini" onClick={resetExpressiveness}>{isChinese ? '重置' : 'Reset'}</Button>
</Text>
<p className="subtitle">{isChinese ? '基础' : 'basic'}</p>
<div className="content">
{expressiveness.map((item, index) => (
<EditorInputItem key={`${item}-${index}`} groupName="expressiveness" keyName={item} />
))}
</div>
<Text h3>{isChinese ? '布局' : 'Layout'}
<Button type="abort" auto size="mini" onClick={resetLayout}>{isChinese ? '重置' : 'Reset'}</Button>
</Text>
{isChinese ? (
<p></p>
) : (
<p>Most layout spacing depends on these variables, unreasonable changes may cause layout imbalance.</p>
)}
<p className="subtitle">{isChinese ? '基础' : 'basic'}</p>
<div className="content">
{pageLayout.map((item, index) => (
<EditorInputItem key={`${item}-${index}`} groupName="layout" keyName={item} />
))}
</div>
<p className="subtitle">{isChinese ? '间距' : 'gaps'}</p>
<div className="content">
{gapLayout.map((item, index) => (
<EditorInputItem key={`${item}-${index}`} groupName="layout" keyName={item} />
))}
</div>
<style jsx>{`
.content {
display: flex;
justify-content: flex-start;
align-items: center;
flex-wrap: wrap;
width: auto;
margin: 0 auto;
padding-left: ${theme.layout.gapQuarter};
}
.editor :global(h3) {
margin: 2rem 0 1rem 0;
}
.subtitle {
color: ${theme.palette.accents_4};
text-transform: uppercase;
font-size: .75rem;
margin-top: 2rem;
}
`}</style>
</div>
)
}
export default Editor

View File

@@ -0,0 +1,41 @@
import React from 'react'
import { useTheme, Row } from 'components'
import CustomizationCodes from './codes'
import Demo from './demo'
const CustomizationLayout: React.FC<React.PropsWithChildren<{}>> = ({
children,
}) => {
const theme = useTheme()
return (
<div className="layout">
<Row>
<Demo />
<div className="content">{children}</div>
</Row>
<Row>
<CustomizationCodes />
</Row>
<style jsx>{`
.layout {
min-height: calc(100vh - 108px);
max-width: ${theme.layout.pageWidthWithMargin};
margin: 0 auto;
padding: 0 ${theme.layout.gap};
display: flex;
flex-direction: column;
box-sizing: border-box;
}
.content {
flex: 1;
overflow: hidden;
}
`}</style>
</div>
)
}
export default CustomizationLayout

View File

@@ -0,0 +1,28 @@
import React from 'react'
import withDefaults from 'components/utils/with-defaults'
interface Props {
width: number
height: number
}
const defaultProps = {
width: 20,
height: 20,
}
export type FullScreenCloseIconProps = Props & typeof defaultProps & React.SVGAttributes<any>
const FullScreenCloseIcon: React.FC<React.PropsWithChildren<FullScreenCloseIconProps>> = ({
width, height, ...props
}) => {
return (
<svg viewBox="0 0 24 24" width={width} height={height} {...props} stroke="currentColor" strokeWidth="1.5"
strokeLinecap="round" strokeLinejoin="round" fill="none" shapeRendering="geometricPrecision"
style={{ color: 'currentColor' }}>
<path d="M4 14h6m0 0v6m0-6l-7 7m17-11h-6m0 0V4m0 6l7-7m-7 17v-6m0 0h6m-6 0l7 7M10 4v6m0 0H4m6 0L3 3"/>
</svg>
)
}
export default withDefaults(FullScreenCloseIcon, defaultProps)

View File

@@ -0,0 +1,27 @@
import React from 'react'
import withDefaults from 'components/utils/with-defaults'
interface Props {
width: number
height: number
}
const defaultProps = {
width: 20,
height: 20,
}
export type FullScreenIconProps = Props & typeof defaultProps & React.SVGAttributes<any>
const FullScreenIcon: React.FC<React.PropsWithChildren<FullScreenIconProps>> = ({
width, height, ...props
}) => {
return (
<svg viewBox="0 0 24 24" width={width} height={height} {...props} stroke="currentColor" strokeWidth="1.5" strokeLinecap="round"
strokeLinejoin="round" fill="none" shapeRendering="geometricPrecision" style={{ color: 'currentColor' }}>
<path d="M15 3h6m0 0v6m0-6l-7 7M9 21H3m0 0v-6m0 6l7-7M3 9V3m0 0h6M3 3l7 7m11 5v6m0 0h-6m6 0l-7-7"/>
</svg>
)
}
export default withDefaults(FullScreenIcon, defaultProps)

View File

@@ -37,7 +37,7 @@ export const Layout: React.FC<React.PropsWithChildren<Props>> = React.memo(({ ch
<style jsx>{`
.layout {
min-height: calc(100vh - 108px);
max-width: 1000px;
max-width: ${theme.layout.pageWidthWithMargin};
margin: 0 auto;
padding: 0 ${theme.layout.gap};
display: flex;
@@ -74,7 +74,7 @@ export const Layout: React.FC<React.PropsWithChildren<Props>> = React.memo(({ ch
padding-bottom: 150px;
}
@media only screen and (max-width: 767px) {
@media only screen and (max-width: ${theme.layout.breakpointMobile}) {
.layout {
max-width: 100%;
width: 100%;

View File

@@ -98,7 +98,7 @@ const MenuSticker = () => {
}
.inner {
max-width: 1000px;
max-width: ${theme.layout.pageWidth};
padding: 0 ${theme.layout.gap};
width: 100%;
display: flex;

View File

@@ -71,7 +71,7 @@ export const Sidebar: React.FC<Props> = React.memo(() => {
margin-bottom: ${theme.layout.gap};
}
@media only screen and (max-width: 767px) {
@media only screen and (max-width: ${theme.layout.breakpointMobile}) {
.box {
padding: calc(3.5 * ${theme.layout.gap}) 15vw;
width: 100vw;

View File

@@ -55,7 +55,7 @@ const SideItem: React.FC<React.PropsWithChildren<SideItemProps>> = React.memo(({
font-weight: bold;
}
@media only screen and (max-width: 767px) {
@media only screen and (max-width: ${theme.layout.breakpointMobile}) {
.link {
border-bottom: 1px solid ${theme.palette.border};
height: 3.5rem;

View File

@@ -51,7 +51,7 @@ const TabbarMobile:React.FC<Props> = ({ onClick }) => {
text-transform: capitalize;
}
@media only screen and (min-width: 767px) {
@media only screen and (min-width: ${theme.layout.breakpointMobile}) {
.tabbar {
display: none;
visibility: hidden;

View File

@@ -1,7 +1,9 @@
import React from 'react'
import { ZeitUIThemes } from 'components/styles/themes'
import { DeepPartial } from 'components/utils/types'
export interface Configs {
onChange?: Function
onThemeChange?: Function
isChinese?: boolean
updateChineseState: Function
sidebarScrollHeight: number
@@ -9,6 +11,9 @@ export interface Configs {
tabbarFixed: boolean
updateTabbarFixed: Function
customTheme: DeepPartial<ZeitUIThemes>
updateCustomTheme: (theme: DeepPartial<ZeitUIThemes>) => void
}
export const defaultConfigs: Configs = {
@@ -18,6 +23,10 @@ export const defaultConfigs: Configs = {
tabbarFixed: false,
updateTabbarFixed: () => {},
customTheme: {},
updateCustomTheme: () => {},
onThemeChange: () => {},
}
export const ConfigContext = React.createContext<Configs>(defaultConfigs)

View File

@@ -2,9 +2,14 @@ import React, { useMemo, useState } from 'react'
import withDefaults from 'components/utils/with-defaults'
import { ConfigContext, Configs } from 'lib/config-context'
import { useRouter } from 'next/router'
import { DeepPartial } from 'components/utils/types'
import { ZeitUIThemes } from 'components/styles/themes'
import { deepMergeObject } from 'components/styles/theme-provider/theme-provider'
import useCurrentState from 'components/utils/use-current-state'
import { useTheme } from 'components'
interface Props {
onChange?: Function
onThemeChange?: Function
}
const defaultProps = {
@@ -13,23 +18,33 @@ const defaultProps = {
export type ConfigProviderProps = Props & typeof defaultProps
const ConfigProvider: React.FC<React.PropsWithChildren<ConfigProviderProps>> = React.memo(({
onChange, children,
onThemeChange, children,
}) => {
const theme = useTheme()
const { pathname } = useRouter()
const [isChinese, setIsChinese] = useState<boolean>(() => pathname.includes('zh-cn'))
const [scrollHeight, setScrollHeight] = useState<number>(0)
const [tabbarFixed, setTabbarFixed] = useState<boolean>(false)
const [customTheme, setCustomTheme, customThemeRef] = useCurrentState<DeepPartial<ZeitUIThemes>>(theme)
const updateSidebarScrollHeight = (height: number) => setScrollHeight(height)
const updateChineseState = (state: boolean) => setIsChinese(state)
const updateTabbarFixed = (state: boolean) => setTabbarFixed(state)
const updateCustomTheme = (nextTheme: DeepPartial<ZeitUIThemes>) => {
const mergedTheme = deepMergeObject(customThemeRef.current, nextTheme)
setCustomTheme(mergedTheme)
onThemeChange && onThemeChange(mergedTheme)
}
const initialValue = useMemo<Configs>(() => ({
onChange, isChinese, tabbarFixed,
onThemeChange, isChinese, tabbarFixed,
customTheme,
updateCustomTheme,
updateTabbarFixed,
updateChineseState,
sidebarScrollHeight: scrollHeight,
updateSidebarScrollHeight,
}), [onChange, scrollHeight, tabbarFixed, isChinese])
}), [onThemeChange, scrollHeight, tabbarFixed, isChinese])
return (
<ConfigContext.Provider value={initialValue}>

View File

@@ -1 +1 @@
[{"name":"guide","children":[{"name":"getting-started","children":[{"name":"introduction","url":"/en-us/guide/introduction","index":5,"group":"getting-started"},{"name":"installation","url":"/en-us/guide/installation","index":10,"group":"getting-started"},{"name":"Server Render","url":"/en-us/guide/server-render","index":15,"group":"getting-started"}]},{"name":"customization","children":[{"name":"Colors","url":"/en-us/guide/colors","index":100,"group":"customization"},{"name":"Themes","url":"/en-us/guide/themes","index":100,"group":"customization"}]}]},{"name":"components","children":[{"name":"General","children":[{"name":"button","url":"/en-us/components/button","index":100,"group":"General"},{"name":"Code","url":"/en-us/components/code","index":100,"group":"General"},{"name":"Spacer","url":"/en-us/components/spacer","index":100,"group":"General"},{"name":"text","url":"/en-us/components/text","index":100,"group":"General"}]},{"name":"layout","children":[{"name":"layout","url":"/en-us/components/layout","index":100,"group":"layout"}]},{"name":"Surfaces","children":[{"name":"card","url":"/en-us/components/card","index":100,"group":"Surfaces"},{"name":"collapse","url":"/en-us/components/collapse","index":100,"group":"Surfaces"},{"name":"fieldset","url":"/en-us/components/fieldset","index":100,"group":"Surfaces"}]},{"name":"Data Entry","children":[{"name":"Auto-Complete","url":"/en-us/components/auto-complete","index":100,"group":"Data Entry"},{"name":"checkbox","url":"/en-us/components/checkbox","index":100,"group":"Data Entry"},{"name":"Input","url":"/en-us/components/input","index":100,"group":"Data Entry"},{"name":"radio","url":"/en-us/components/radio","index":100,"group":"Data Entry"},{"name":"select","url":"/en-us/components/select","index":100,"group":"Data Entry"},{"name":"textarea","url":"/en-us/components/textarea","index":100,"group":"Data Entry"},{"name":"Toggle","url":"/en-us/components/toggle","index":100,"group":"Data Entry"}]},{"name":"Data Display","children":[{"name":"avatar","url":"/en-us/components/avatar","index":100,"group":"Data Display"},{"name":"Badge","url":"/en-us/components/badge","index":100,"group":"Data Display"},{"name":"Capacity","url":"/en-us/components/capacity","index":100,"group":"Data Display"},{"name":"Description","url":"/en-us/components/description","index":100,"group":"Data Display"},{"name":"Display","url":"/en-us/components/display","index":100,"group":"Data Display"},{"name":"Dot","url":"/en-us/components/dot","index":100,"group":"Data Display"},{"name":"File-Tree","url":"/en-us/components/file-tree","index":100,"group":"Data Display"},{"name":"Image","url":"/en-us/components/image","index":100,"group":"Data Display"},{"name":"keyboard","url":"/en-us/components/keyboard","index":100,"group":"Data Display"},{"name":"Popover","url":"/en-us/components/popover","index":100,"group":"Data Display"},{"name":"Table","url":"/en-us/components/table","index":100,"group":"Data Display"},{"name":"Tag","url":"/en-us/components/tag","index":100,"group":"Data Display"},{"name":"Tooltip","url":"/en-us/components/tooltip","index":100,"group":"Data Display"}]},{"name":"Feedback","children":[{"name":"Loading","url":"/en-us/components/loading","index":100,"group":"Feedback"},{"name":"modal","url":"/en-us/components/modal","index":100,"group":"Feedback"},{"name":"note","url":"/en-us/components/note","index":100,"group":"Feedback"},{"name":"Progress","url":"/en-us/components/progress","index":100,"group":"Feedback"},{"name":"Spinner","url":"/en-us/components/spinner","index":100,"group":"Feedback"},{"name":"toast","url":"/en-us/components/toast","index":100,"group":"Feedback"}]},{"name":"Navigation","children":[{"name":"link","url":"/en-us/components/link","index":100,"group":"Navigation"},{"name":"tabs","url":"/en-us/components/tabs","index":100,"group":"Navigation"},{"name":"button-dropdown","url":"/en-us/components/button-dropdown","index":101,"group":"Navigation"}]},{"name":"Others","children":[{"name":"Snippet","url":"/en-us/components/snippet","index":100,"group":"Others"}]}]}]
[{"name":"guide","children":[{"name":"getting-started","children":[{"name":"introduction","url":"/en-us/guide/introduction","index":5,"group":"getting-started"},{"name":"installation","url":"/en-us/guide/installation","index":10,"group":"getting-started"},{"name":"Server Render","url":"/en-us/guide/server-render","index":15,"group":"getting-started"}]},{"name":"customization","children":[{"name":"Colors","url":"/en-us/guide/colors","index":100,"group":"customization"},{"name":"Themes","url":"/en-us/guide/themes","index":100,"group":"customization"}]}]},{"name":"components","children":[{"name":"General","children":[{"name":"button","url":"/en-us/components/button","index":100,"group":"General"},{"name":"Code","url":"/en-us/components/code","index":100,"group":"General"},{"name":"Spacer","url":"/en-us/components/spacer","index":100,"group":"General"},{"name":"text","url":"/en-us/components/text","index":100,"group":"General"}]},{"name":"layout","children":[{"name":"layout","url":"/en-us/components/layout","index":100,"group":"layout"}]},{"name":"Surfaces","children":[{"name":"card","url":"/en-us/components/card","index":100,"group":"Surfaces"},{"name":"collapse","url":"/en-us/components/collapse","index":100,"group":"Surfaces"},{"name":"fieldset","url":"/en-us/components/fieldset","index":100,"group":"Surfaces"}]},{"name":"Data Entry","children":[{"name":"Auto-Complete","url":"/en-us/components/auto-complete","index":100,"group":"Data Entry"},{"name":"checkbox","url":"/en-us/components/checkbox","index":100,"group":"Data Entry"},{"name":"Input","url":"/en-us/components/input","index":100,"group":"Data Entry"},{"name":"radio","url":"/en-us/components/radio","index":100,"group":"Data Entry"},{"name":"select","url":"/en-us/components/select","index":100,"group":"Data Entry"},{"name":"textarea","url":"/en-us/components/textarea","index":100,"group":"Data Entry"},{"name":"Toggle","url":"/en-us/components/toggle","index":100,"group":"Data Entry"}]},{"name":"Data Display","children":[{"name":"avatar","url":"/en-us/components/avatar","index":100,"group":"Data Display"},{"name":"Badge","url":"/en-us/components/badge","index":100,"group":"Data Display"},{"name":"Capacity","url":"/en-us/components/capacity","index":100,"group":"Data Display"},{"name":"Description","url":"/en-us/components/description","index":100,"group":"Data Display"},{"name":"Display","url":"/en-us/components/display","index":100,"group":"Data Display"},{"name":"Dot","url":"/en-us/components/dot","index":100,"group":"Data Display"},{"name":"File-Tree","url":"/en-us/components/file-tree","index":100,"group":"Data Display"},{"name":"Image","url":"/en-us/components/image","index":100,"group":"Data Display"},{"name":"keyboard","url":"/en-us/components/keyboard","index":100,"group":"Data Display"},{"name":"Popover","url":"/en-us/components/popover","index":100,"group":"Data Display"},{"name":"Table","url":"/en-us/components/table","index":100,"group":"Data Display"},{"name":"Tag","url":"/en-us/components/tag","index":100,"group":"Data Display"},{"name":"Tooltip","url":"/en-us/components/tooltip","index":100,"group":"Data Display"}]},{"name":"Feedback","children":[{"name":"Loading","url":"/en-us/components/loading","index":100,"group":"Feedback"},{"name":"modal","url":"/en-us/components/modal","index":100,"group":"Feedback"},{"name":"note","url":"/en-us/components/note","index":100,"group":"Feedback"},{"name":"Progress","url":"/en-us/components/progress","index":100,"group":"Feedback"},{"name":"Spinner","url":"/en-us/components/spinner","index":100,"group":"Feedback"},{"name":"toast","url":"/en-us/components/toast","index":100,"group":"Feedback"}]},{"name":"Navigation","children":[{"name":"link","url":"/en-us/components/link","index":100,"group":"Navigation"},{"name":"tabs","url":"/en-us/components/tabs","index":100,"group":"Navigation"},{"name":"button-dropdown","url":"/en-us/components/button-dropdown","index":101,"group":"Navigation"}]},{"name":"Others","children":[{"name":"Snippet","url":"/en-us/components/snippet","index":100,"group":"Others"}]}]},{"name":"customization","children":[]}]

View File

@@ -1 +1 @@
[{"name":"guide","children":[{"name":"快速上手","children":[{"name":"什么是 ZEIT UI","url":"/zh-cn/guide/introduction","index":5,"group":"快速上手"},{"name":"安装","url":"/zh-cn/guide/installation","index":10,"group":"快速上手"},{"name":"服务端渲染","url":"/zh-cn/guide/server-render","index":15,"group":"快速上手"}]},{"name":"定制化","children":[{"name":"色彩","url":"/zh-cn/guide/colors","index":100,"group":"定制化"},{"name":"主题","url":"/zh-cn/guide/themes","index":100,"group":"定制化"}]}],"localeName":"上手指南"},{"name":"components","children":[{"name":"通用","children":[{"name":"按钮 Button","url":"/zh-cn/components/button","index":100,"group":"通用"},{"name":"代码 Code","url":"/zh-cn/components/code","index":100,"group":"通用"},{"name":"间距 Spacer","url":"/zh-cn/components/spacer","index":100,"group":"通用"},{"name":"文本 Text","url":"/zh-cn/components/text","index":100,"group":"通用"}]},{"name":"布局","children":[{"name":"布局 Layout","url":"/zh-cn/components/layout","index":100,"group":"布局"}]},{"name":"表面","children":[{"name":"卡片 Card","url":"/zh-cn/components/card","index":100,"group":"表面"},{"name":"折叠框 Collapse","url":"/zh-cn/components/collapse","index":100,"group":"表面"},{"name":"控件组 Fieldset","url":"/zh-cn/components/fieldset","index":100,"group":"表面"}]},{"name":"数据录入","children":[{"name":"复选框 Checkbox","url":"/zh-cn/components/checkbox","index":100,"group":"数据录入"},{"name":"输入框 Input","url":"/zh-cn/components/input","index":100,"group":"数据录入"},{"name":"单选框 Radio","url":"/zh-cn/components/radio","index":100,"group":"数据录入"},{"name":"选择器 Select","url":"/zh-cn/components/select","index":100,"group":"数据录入"},{"name":"文本输入框 Textarea","url":"/zh-cn/components/textarea","index":100,"group":"数据录入"},{"name":"开关 Toggle","url":"/zh-cn/components/toggle","index":100,"group":"数据录入"},{"name":"自动完成 Auto-Complete","url":"/zh-cn/components/auto-complete","index":104,"group":"数据录入"}]},{"name":"数据展示","children":[{"name":"头像 Avatar","url":"/zh-cn/components/avatar","index":100,"group":"数据展示"},{"name":"徽章 Badge","url":"/zh-cn/components/badge","index":100,"group":"数据展示"},{"name":"容量 Capacity","url":"/zh-cn/components/capacity","index":100,"group":"数据展示"},{"name":"描述 Description","url":"/zh-cn/components/description","index":100,"group":"数据展示"},{"name":"陈列框 Display","url":"/zh-cn/components/display","index":100,"group":"数据展示"},{"name":"点 Dot","url":"/zh-cn/components/dot","index":100,"group":"数据展示"},{"name":"文件树 File Tree","url":"/zh-cn/components/file-tree","index":100,"group":"数据展示"},{"name":"图片 Image","url":"/zh-cn/components/image","index":100,"group":"数据展示"},{"name":"键盘 keyboard","url":"/zh-cn/components/keyboard","index":100,"group":"数据展示"},{"name":"气泡卡片 Popover","url":"/zh-cn/components/popover","index":100,"group":"数据展示"},{"name":"表格 Table","url":"/zh-cn/components/table","index":100,"group":"数据展示"},{"name":"标签 Tag","url":"/zh-cn/components/tag","index":100,"group":"数据展示"},{"name":"文字提示 Tooltip","url":"/zh-cn/components/tooltip","index":100,"group":"数据展示"}]},{"name":"反馈","children":[{"name":"加载中 Loading","url":"/zh-cn/components/loading","index":100,"group":"反馈"},{"name":"对话框 Modal","url":"/zh-cn/components/modal","index":100,"group":"反馈"},{"name":"提示 Note","url":"/zh-cn/components/note","index":100,"group":"反馈"},{"name":"进度条 Progress","url":"/zh-cn/components/progress","index":100,"group":"反馈"},{"name":"指示器 Spinner","url":"/zh-cn/components/spinner","index":100,"group":"反馈"},{"name":"通知 Toast","url":"/zh-cn/components/toast","index":100,"group":"反馈"}]},{"name":"导航","children":[{"name":"链接 Link","url":"/zh-cn/components/link","index":100,"group":"导航"},{"name":"选项卡 Tabs","url":"/zh-cn/components/tabs","index":100,"group":"导航"},{"name":"下拉按钮 Btn Dropdown","url":"/zh-cn/components/button-dropdown","index":105,"group":"导航"}]},{"name":"其他","children":[{"name":"片段 Snippet","url":"/zh-cn/components/snippet","index":100,"group":"其他"}]}],"localeName":"所有组件"}]
[{"name":"guide","children":[{"name":"快速上手","children":[{"name":"什么是 ZEIT UI","url":"/zh-cn/guide/introduction","index":5,"group":"快速上手"},{"name":"安装","url":"/zh-cn/guide/installation","index":10,"group":"快速上手"},{"name":"服务端渲染","url":"/zh-cn/guide/server-render","index":15,"group":"快速上手"}]},{"name":"定制化","children":[{"name":"色彩","url":"/zh-cn/guide/colors","index":100,"group":"定制化"},{"name":"主题","url":"/zh-cn/guide/themes","index":100,"group":"定制化"}]}],"localeName":"上手指南"},{"name":"components","children":[{"name":"通用","children":[{"name":"按钮 Button","url":"/zh-cn/components/button","index":100,"group":"通用"},{"name":"代码 Code","url":"/zh-cn/components/code","index":100,"group":"通用"},{"name":"间距 Spacer","url":"/zh-cn/components/spacer","index":100,"group":"通用"},{"name":"文本 Text","url":"/zh-cn/components/text","index":100,"group":"通用"}]},{"name":"布局","children":[{"name":"布局 Layout","url":"/zh-cn/components/layout","index":100,"group":"布局"}]},{"name":"表面","children":[{"name":"卡片 Card","url":"/zh-cn/components/card","index":100,"group":"表面"},{"name":"折叠框 Collapse","url":"/zh-cn/components/collapse","index":100,"group":"表面"},{"name":"控件组 Fieldset","url":"/zh-cn/components/fieldset","index":100,"group":"表面"}]},{"name":"数据录入","children":[{"name":"复选框 Checkbox","url":"/zh-cn/components/checkbox","index":100,"group":"数据录入"},{"name":"输入框 Input","url":"/zh-cn/components/input","index":100,"group":"数据录入"},{"name":"单选框 Radio","url":"/zh-cn/components/radio","index":100,"group":"数据录入"},{"name":"选择器 Select","url":"/zh-cn/components/select","index":100,"group":"数据录入"},{"name":"文本输入框 Textarea","url":"/zh-cn/components/textarea","index":100,"group":"数据录入"},{"name":"开关 Toggle","url":"/zh-cn/components/toggle","index":100,"group":"数据录入"},{"name":"自动完成 Auto-Complete","url":"/zh-cn/components/auto-complete","index":104,"group":"数据录入"}]},{"name":"数据展示","children":[{"name":"头像 Avatar","url":"/zh-cn/components/avatar","index":100,"group":"数据展示"},{"name":"徽章 Badge","url":"/zh-cn/components/badge","index":100,"group":"数据展示"},{"name":"容量 Capacity","url":"/zh-cn/components/capacity","index":100,"group":"数据展示"},{"name":"描述 Description","url":"/zh-cn/components/description","index":100,"group":"数据展示"},{"name":"陈列框 Display","url":"/zh-cn/components/display","index":100,"group":"数据展示"},{"name":"点 Dot","url":"/zh-cn/components/dot","index":100,"group":"数据展示"},{"name":"文件树 File Tree","url":"/zh-cn/components/file-tree","index":100,"group":"数据展示"},{"name":"图片 Image","url":"/zh-cn/components/image","index":100,"group":"数据展示"},{"name":"键盘 keyboard","url":"/zh-cn/components/keyboard","index":100,"group":"数据展示"},{"name":"气泡卡片 Popover","url":"/zh-cn/components/popover","index":100,"group":"数据展示"},{"name":"表格 Table","url":"/zh-cn/components/table","index":100,"group":"数据展示"},{"name":"标签 Tag","url":"/zh-cn/components/tag","index":100,"group":"数据展示"},{"name":"文字提示 Tooltip","url":"/zh-cn/components/tooltip","index":100,"group":"数据展示"}]},{"name":"反馈","children":[{"name":"加载中 Loading","url":"/zh-cn/components/loading","index":100,"group":"反馈"},{"name":"对话框 Modal","url":"/zh-cn/components/modal","index":100,"group":"反馈"},{"name":"提示 Note","url":"/zh-cn/components/note","index":100,"group":"反馈"},{"name":"进度条 Progress","url":"/zh-cn/components/progress","index":100,"group":"反馈"},{"name":"指示器 Spinner","url":"/zh-cn/components/spinner","index":100,"group":"反馈"},{"name":"通知 Toast","url":"/zh-cn/components/toast","index":100,"group":"反馈"}]},{"name":"导航","children":[{"name":"链接 Link","url":"/zh-cn/components/link","index":100,"group":"导航"},{"name":"选项卡 Tabs","url":"/zh-cn/components/tabs","index":100,"group":"导航"},{"name":"下拉按钮 Btn Dropdown","url":"/zh-cn/components/button-dropdown","index":105,"group":"导航"}]},{"name":"其他","children":[{"name":"片段 Snippet","url":"/zh-cn/components/snippet","index":100,"group":"其他"}]}],"localeName":"所有组件"},{"name":"customization","children":[],"localeName":"定制化"}]

View File

@@ -39,21 +39,11 @@ const nextConfig = {
permanent: true,
destination: '/en-us/guide/:path*'
},
{
source: '/en-us/customization/:path*',
permanent: true,
destination: '/en-us/guide/:path*'
},
{
source: '/zh-cn/getting-started/:path*',
permanent: true,
destination: '/zh-cn/guide/:path*'
},
{
source: '/zh-cn/customization/:path*',
permanent: true,
destination: '/zh-cn/guide/:path*'
},
{
source: '/zh-cn/',
permanent: true,

View File

@@ -49,6 +49,7 @@
"@mdx-js/loader": "^1.5.7",
"@next/mdx": "^9.2.2",
"@types/react": "^16.9.22",
"@types/react-color": "^3.0.1",
"@types/react-dom": "^16.9.5",
"@types/styled-jsx": "^2.2.8",
"@typescript-eslint/eslint-plugin": "^2.24.0",
@@ -61,6 +62,7 @@
"fs-extra": "^8.1.0",
"next": "^9.3.2",
"react": "^16.13.0",
"react-color": "^2.18.0",
"react-dom": "^16.13.0",
"react-live": "^2.2.2",
"styled-jsx": "^3.2.4",
@@ -69,4 +71,4 @@
"webpack-cli": "^3.3.11"
},
"dependencies": {}
}
}

View File

@@ -1,17 +1,18 @@
import Head from 'next/head'
import { NextPage } from 'next'
import { AppProps } from 'next/app'
import { useCallback, useState } from 'react'
import { CSSBaseline, ZEITUIProvider, useTheme } from 'components'
import { useState } from 'react'
import { CSSBaseline, ZEITUIProvider, useTheme, ZeitUIThemes } from 'components'
import Menu from 'lib/components/menu'
import ConfigContext from 'lib/config-provider'
import { DeepPartial } from 'components/utils/types'
const Application: NextPage<AppProps> = ({ Component, pageProps }) => {
const theme = useTheme()
const [themeType, setThemeType] = useState<typeof theme.type>(theme.type)
const changeHandle = useCallback((isDark: boolean) => {
setThemeType(isDark ? 'dark' : 'light')
}, [])
const [customTheme, setCustomTheme] = useState<DeepPartial<ZeitUIThemes>>({})
const themeChangeHandle = (theme: DeepPartial<ZeitUIThemes>) => {
setCustomTheme(theme)
}
return (
<>
@@ -32,9 +33,9 @@ const Application: NextPage<AppProps> = ({ Component, pageProps }) => {
<meta property="twitter:image" content="https://user-images.githubusercontent.com/11304944/76085431-fd036480-5fec-11ea-8412-9e581425344a.png" />
<meta name="viewport" content="initial-scale=1, maximum-scale=1, minimum-scale=1, viewport-fit=cover" />
</Head>
<ZEITUIProvider theme={{ type: themeType }}>
<ZEITUIProvider theme={customTheme}>
<CSSBaseline />
<ConfigContext onChange={changeHandle}>
<ConfigContext onThemeChange={themeChangeHandle}>
<Menu />
<Component {...pageProps} />
</ConfigContext>

View File

@@ -251,6 +251,7 @@ AutoComplete control of input field.
| Attribute | Description | Type | Accepted values | Default
| ---------- | ---------- | ---- | -------------- | ------ |
| hidden | hide empty box | `boolean` | - | `false` |
| ... | native props | `HTMLAttributes` | `'id', 'className', ...` | - |
<Attributes.Title>type AutoCompleteOptions</Attributes.Title>

View File

@@ -0,0 +1,18 @@
import React from 'react'
import { Text, Spacer } from 'components'
import CustomizationLayout from 'lib/components/customization/layout'
import CustomizationEditor from 'lib/components/customization/editor'
const Customization = () => {
return (
<CustomizationLayout>
<Spacer y={1.2} />
<Text h2>Customization</Text>
<Text>Custom themes is a very simple thing in ZEIT UI, click change, copy or share.</Text>
<CustomizationEditor />
<Spacer y={2} />
</CustomizationLayout>
)
}
export default Customization

View File

@@ -252,6 +252,7 @@ export const meta = {
| 属性 | 描述 | 类型 | 推荐值 | 默认
| ---------- | ---------- | ---- | -------------- | ------ |
| hidden | 隐藏无结果提示框 | `boolean` | - | `false` |
| ... | 原生属性 | `HTMLAttributes` | `'id', 'className', ...` | - |
<Attributes.Title>type AutoCompleteOptions</Attributes.Title>

View File

@@ -0,0 +1,18 @@
import React from 'react'
import { Text, Spacer } from 'components'
import CustomizationLayout from 'lib/components/customization/layout'
import CustomizationEditor from 'lib/components/customization/editor'
const Customization = () => {
return (
<CustomizationLayout>
<Spacer y={1.2} />
<Text h2></Text>
<Text> ZEIT UI </Text>
<CustomizationEditor />
<Spacer y={2} />
</CustomizationLayout>
)
}
export default Customization

View File

@@ -11,8 +11,8 @@ const weights = {
'guide': 1,
'docs': 2,
'getting-started': 3,
'customization': 4,
'components': 5,
'customization': 10,
}
const groupWeights = {
'快速上手': 1,

View File

@@ -1020,6 +1020,11 @@
resolved "https://registry.yarnpkg.com/@csstools/convert-colors/-/convert-colors-1.4.0.tgz#ad495dc41b12e75d588c6db8b9834f08fa131eb7"
integrity sha512-5a6wqoJV/xEdbRNKVo6I4hO3VjyDq//8q2f9I6PBAvMesJHFauXDorcNCsr9RzvsZnaWi5NYCcfyqP1QeFHFbw==
"@icons/material@^0.2.4":
version "0.2.4"
resolved "https://registry.yarnpkg.com/@icons/material/-/material-0.2.4.tgz#e90c9f71768b3736e76d7dd6783fc6c2afa88bc8"
integrity sha512-QPcGmICAPbGLGb6F/yNf/KzKqvFx8z5qx3D1yFqVAjoFmXK35EgyW+cJ57Te3CNsmzblwtzakLGFqHPqrfb4Tw==
"@mapbox/rehype-prism@^0.4.0":
version "0.4.0"
resolved "https://registry.yarnpkg.com/@mapbox/rehype-prism/-/rehype-prism-0.4.0.tgz#58714b345ec01256aa74c24762a341f6a771494e"
@@ -1125,6 +1130,13 @@
resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.3.tgz#2ab0d5da2e5815f94b0b9d4b95d1e5f243ab2ca7"
integrity sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw==
"@types/react-color@^3.0.1":
version "3.0.1"
resolved "https://registry.yarnpkg.com/@types/react-color/-/react-color-3.0.1.tgz#5433e2f503ea0e0831cbc6fd0c20f8157d93add0"
integrity sha512-J6mYm43Sid9y+OjZ7NDfJ2VVkeeuTPNVImNFITgQNXodHteKfl/t/5pAR5Z9buodZ2tCctsZjgiMlQOpfntakw==
dependencies:
"@types/react" "*"
"@types/react-dom@^16.9.5":
version "16.9.5"
resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-16.9.5.tgz#5de610b04a35d07ffd8f44edad93a71032d9aaa7"
@@ -4728,7 +4740,7 @@ lodash.uniq@4.5.0:
resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773"
integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=
lodash@^4.17.11, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.4:
lodash@^4.0.1, lodash@^4.17.11, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.4:
version "4.17.15"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548"
integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==
@@ -4824,6 +4836,11 @@ markdown-escapes@^1.0.0:
resolved "https://registry.yarnpkg.com/markdown-escapes/-/markdown-escapes-1.0.4.tgz#c95415ef451499d7602b91095f3c8e8975f78535"
integrity sha512-8z4efJYk43E0upd0NbVXwgSTQs6cT3T06etieCMEg7dRbzCbxUCK/GHlX8mhHRDcp+OLlHkPKsvqQTCvsRl2cg==
material-colors@^1.2.1:
version "1.2.6"
resolved "https://registry.yarnpkg.com/material-colors/-/material-colors-1.2.6.tgz#6d1958871126992ceecc72f4bcc4d8f010865f46"
integrity sha512-6qE4B9deFBIa9YSpOc9O0Sgc43zTeVYbgDT5veRKSlB2+ZuHNoVVxA1L/ckMUayV9Ay9y7Z/SZCLcGteW9i7bg==
md5.js@^1.3.4:
version "1.3.5"
resolved "https://registry.yarnpkg.com/md5.js/-/md5.js-1.3.5.tgz#b5d07b8e3216e3e27cd728d72f70d1e6a342005f"
@@ -6239,7 +6256,7 @@ prop-types-exact@1.2.0:
object.assign "^4.1.0"
reflect.ownkeys "^0.2.0"
prop-types@15.7.2, prop-types@^15.5.8, prop-types@^15.6.2, prop-types@^15.7.2:
prop-types@15.7.2, prop-types@^15.5.10, prop-types@^15.5.8, prop-types@^15.6.2, prop-types@^15.7.2:
version "15.7.2"
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5"
integrity sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==
@@ -6360,6 +6377,18 @@ raw-body@2.4.0:
iconv-lite "0.4.24"
unpipe "1.0.0"
react-color@^2.18.0:
version "2.18.0"
resolved "https://registry.yarnpkg.com/react-color/-/react-color-2.18.0.tgz#34956f0bac394f6c3bc01692fd695644cc775ffd"
integrity sha512-FyVeU1kQiSokWc8NPz22azl1ezLpJdUyTbWL0LPUpcuuYDrZ/Y1veOk9rRK5B3pMlyDGvTk4f4KJhlkIQNRjEA==
dependencies:
"@icons/material" "^0.2.4"
lodash "^4.17.11"
material-colors "^1.2.1"
prop-types "^15.5.10"
reactcss "^1.2.0"
tinycolor2 "^1.4.1"
react-dom@^16.13.0:
version "16.13.0"
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.13.0.tgz#cdde54b48eb9e8a0ca1b3dc9943d9bb409b81866"
@@ -6413,6 +6442,13 @@ react@^16.13.0:
object-assign "^4.1.1"
prop-types "^15.6.2"
reactcss@^1.2.0:
version "1.2.3"
resolved "https://registry.yarnpkg.com/reactcss/-/reactcss-1.2.3.tgz#c00013875e557b1cf0dfd9a368a1c3dab3b548dd"
integrity sha512-KiwVUcFu1RErkI97ywr8nvx8dNOpT03rbnma0SSalTYjkrPYaEajR4a/MRt6DZ46K6arDRbWMNHF+xH7G7n/8A==
dependencies:
lodash "^4.0.1"
read-pkg@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-2.0.0.tgz#8ef1c0623c6a6db0dc6713c4bfac46332b2368f8"
@@ -7443,6 +7479,11 @@ tiny-emitter@^2.0.0:
resolved "https://registry.yarnpkg.com/tiny-emitter/-/tiny-emitter-2.1.0.tgz#1d1a56edfc51c43e863cbb5382a72330e3555423"
integrity sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==
tinycolor2@^1.4.1:
version "1.4.1"
resolved "https://registry.yarnpkg.com/tinycolor2/-/tinycolor2-1.4.1.tgz#f4fad333447bc0b07d4dc8e9209d8f39a8ac77e8"
integrity sha1-9PrTM0R7wLB9TcjpIJ2POaisd+g=
title-case@^2.1.0:
version "2.1.1"
resolved "https://registry.yarnpkg.com/title-case/-/title-case-2.1.1.tgz#3e127216da58d2bc5becf137ab91dae3a7cd8faa"