mirror of
https://github.com/zhigang1992/react.git
synced 2026-03-26 06:55:07 +08:00
docs: show and copy the theme codes of user changes
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
@@ -86,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;
|
||||
|
||||
132
lib/components/customization/codes.tsx
Normal file
132
lib/components/customization/codes.tsx
Normal file
@@ -0,0 +1,132 @@
|
||||
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'
|
||||
|
||||
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 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">Theme Codes</h3>
|
||||
<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: 2.5rem auto;
|
||||
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
|
||||
@@ -1,30 +1,18 @@
|
||||
import React, { useState } from 'react'
|
||||
import FullScreenIcon from 'lib/components/icons/full-screen'
|
||||
import FullScreenCloseIcon from 'lib/components/icons/full-screen-close'
|
||||
import React from 'react'
|
||||
import Colors from './colors'
|
||||
import { useTheme, Button, Text, Code, Spacer, Link, Select, Checkbox } from 'components'
|
||||
|
||||
const Demo: React.FC<React.PropsWithChildren<{}>> = () => {
|
||||
const theme = useTheme()
|
||||
const [fullScreen, setFullScreen] = useState<boolean>(false)
|
||||
|
||||
const showMoreOrLess = () => {
|
||||
setFullScreen(last => !last)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="demo">
|
||||
<div className="action" onClick={showMoreOrLess}>
|
||||
<Button type="abort" auto>
|
||||
{fullScreen ? <FullScreenIcon /> : <FullScreenCloseIcon />}
|
||||
</Button>
|
||||
</div>
|
||||
<div className="content">
|
||||
<Text h3>Preview</Text>
|
||||
<Text>Here's a preview of your changes to the Theme. When you save the changes,
|
||||
<Text>Here'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 download automatically generated code or share your custom theme with anyone.</Text>
|
||||
<Text>You can copy automatically generated codes or share your custom theme with anyone.</Text>
|
||||
|
||||
<Spacer y={1.7} />
|
||||
<Text h4>Colors</Text>
|
||||
@@ -32,7 +20,9 @@ const Demo: React.FC<React.PropsWithChildren<{}>> = () => {
|
||||
|
||||
<Spacer y={1.7} />
|
||||
<Text h4>Typography</Text>
|
||||
<Text><Code>inline codes</Code>, <Link href="#" color>Hyperlink</Link></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>
|
||||
@@ -60,48 +50,28 @@ const Demo: React.FC<React.PropsWithChildren<{}>> = () => {
|
||||
<Checkbox value="sydney">Sydney</Checkbox>
|
||||
<Checkbox value="beijing">Bei Jing</Checkbox>
|
||||
</Checkbox.Group>
|
||||
<Spacer y={4} />
|
||||
</div>
|
||||
<style jsx>{`
|
||||
.demo {
|
||||
width: ${fullScreen ? '100%' : '35%'};
|
||||
width: 34%;
|
||||
margin-top: calc(${theme.layout.gap} * 2);
|
||||
margin-right: 20px;
|
||||
padding-right: ${theme.layout.gapQuarter};
|
||||
position: relative;
|
||||
border-right: 1px solid ${theme.palette.border};
|
||||
height: 100%;
|
||||
transition: width 200ms ease;
|
||||
}
|
||||
|
||||
.action {
|
||||
position: absolute;
|
||||
right: .5rem;
|
||||
top: 0;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
transition: color 200ms ease-out;
|
||||
color: ${theme.palette.accents_3};
|
||||
}
|
||||
|
||||
.action:hover {
|
||||
color: ${theme.palette.accents_6};
|
||||
}
|
||||
|
||||
.action :global(button) {
|
||||
width: 2rem;
|
||||
height: 2rem;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 0;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.content {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: ${theme.layout.breakpointMobile}) {
|
||||
.demo {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
`}</style>
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -19,6 +19,7 @@ const EditorInputItem: React.FC<React.PropsWithChildren<Props>> = ({
|
||||
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 },
|
||||
@@ -27,7 +28,9 @@ const EditorInputItem: React.FC<React.PropsWithChildren<Props>> = ({
|
||||
|
||||
return (
|
||||
<div className="editor-item">
|
||||
<Input value={currentVal as string} label={keyName} onChange={changeHandler} className="editor-input" />
|
||||
<Input value={currentVal as string} label={keyName}
|
||||
onChange={changeHandler}
|
||||
className="editor-input" />
|
||||
<style jsx>{`
|
||||
.editor-item {
|
||||
background-color: transparent;
|
||||
@@ -46,7 +49,6 @@ const EditorInputItem: React.FC<React.PropsWithChildren<Props>> = ({
|
||||
.editor-item :global(.editor-input) {
|
||||
width: ${width};
|
||||
}
|
||||
|
||||
`}</style>
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -14,7 +14,7 @@ const statusColors: Array<keyof ZeitUIThemesPalette> = [
|
||||
'warning', 'warningLight', 'warningDark',
|
||||
]
|
||||
const otherColors: Array<keyof ZeitUIThemesPalette> = [
|
||||
'selection', 'secondary', 'border', 'code', 'cyan', 'purple', 'alert', 'violet'
|
||||
'selection', 'secondary', 'link', 'border', 'code', 'cyan', 'purple', 'alert', 'violet'
|
||||
]
|
||||
const expressiveness: Array<keyof ZeitUIThemesExpressiveness> = [
|
||||
'linkStyle', 'linkHoverStyle', 'dropdownBoxShadow', 'shadowSmall',
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import React from 'react'
|
||||
import { useTheme } from 'components'
|
||||
import { useTheme, Row } from 'components'
|
||||
import CustomizationCodes from './codes'
|
||||
import Demo from './demo'
|
||||
|
||||
const CustomizationLayout: React.FC<React.PropsWithChildren<{}>> = ({
|
||||
@@ -9,22 +10,30 @@ const CustomizationLayout: React.FC<React.PropsWithChildren<{}>> = ({
|
||||
|
||||
return (
|
||||
<div className="layout">
|
||||
<Demo />
|
||||
<div className="content">
|
||||
{children}
|
||||
</div>
|
||||
<Row>
|
||||
<Demo />
|
||||
<div className="content">
|
||||
{children}
|
||||
</div>
|
||||
</Row>
|
||||
<Row>
|
||||
<CustomizationCodes />
|
||||
</Row>
|
||||
|
||||
<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;
|
||||
flex-direction: column;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.content {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.demo {
|
||||
|
||||
@@ -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%;
|
||||
|
||||
@@ -98,7 +98,7 @@ const MenuSticker = () => {
|
||||
}
|
||||
|
||||
.inner {
|
||||
max-width: 1000px;
|
||||
max-width: ${theme.layout.pageWidth};
|
||||
padding: 0 ${theme.layout.gap};
|
||||
width: 100%;
|
||||
display: flex;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user