Merge pull request #143 from unix/blockquote

feat(text): add blockquote
This commit is contained in:
witt
2020-04-27 22:02:30 +08:00
committed by GitHub
8 changed files with 188 additions and 71 deletions

View File

@@ -19,6 +19,7 @@ module.exports = {
'!components/**/styles.{ts,tsx}',
'!components/styles/*',
'!components/index.ts',
'!components/utils/*',
],
"moduleNameMapper": {

View File

@@ -248,6 +248,23 @@ initialize {
list-style: none;
}
blockquote {
padding: calc(.667 * 16pt) 16pt;
color: #666;
background-color: #fafafa;
border-radius: 5px;
margin: 1.5rem 0;
border: 1px solid #eaeaea;
}
blockquote :global(*:first-child) {
margin-top: 0;
}
blockquote :global(*:last-child) {
margin-bottom: 0;
}
::selection {
background-color: #79ffe1;
color: #000;
@@ -541,6 +558,23 @@ initialize {
list-style: none;
}
blockquote {
padding: calc(.667 * 16pt) 16pt;
color: #888;
background-color: #111;
border-radius: 5px;
margin: 1.5rem 0;
border: 1px solid #333;
}
blockquote :global(*:first-child) {
margin-top: 0;
}
blockquote :global(*:last-child) {
margin-bottom: 0;
}
::selection {
background-color: #f81ce5;
color: #fff;

View File

@@ -253,6 +253,23 @@ const CSSBaseline: React.FC<React.PropsWithChildren<{}>> = React.memo(({
list-style: none;
}
blockquote {
padding: calc(.667 * ${theme.layout.gap}) ${theme.layout.gap};
color: ${theme.palette.accents_5};
background-color: ${theme.palette.accents_1};
border-radius: ${theme.layout.radius};
margin: 1.5rem 0;
border: 1px solid ${theme.palette.border};
}
blockquote :global(*:first-child) {
margin-top: 0;
}
blockquote :global(*:last-child) {
margin-bottom: 0;
}
::selection {
background-color: ${theme.palette.selection};
color: ${theme.palette.foreground};

View File

@@ -81,11 +81,7 @@ exports[`Text the specified element should be rendered 1`] = `
p {
color: inherit;
}
</style><small class=\\"\\"><small class=\\"\\">test-value</small><style>
small {
color: inherit;
}
</style></small><style>
</style><small class=\\"\\">test-value</small><style>
small {
color: inherit;
}

View File

@@ -4,20 +4,6 @@ import useTheme from '../styles/use-theme'
import { NormalTypes } from '../utils/prop-types'
import { ZeitUIThemesPalette } from '../styles/themes'
export const ComponentTypes: Array<keyof JSX.IntrinsicElements> = [
'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'p', 'small',
]
export const ComponentModifiedElements: { [key: string]: string } = {
bold: 'b',
b: 'b',
italic: 'i',
i: 'i',
desc: 'desc',
description: 'desc',
del: 'del',
}
export interface Props {
tag: keyof JSX.IntrinsicElements
type?: NormalTypes

View File

@@ -17,6 +17,7 @@ interface Props {
span?: boolean
del?: boolean
em?: boolean
blockquote?: boolean
className?: string
type?: NormalTypes
}
@@ -35,6 +36,7 @@ const defaultProps = {
span: false,
del: false,
em: false,
blockquote: false,
className: '',
type: 'default' as NormalTypes,
}
@@ -44,7 +46,9 @@ type ElementMap = { [key in (keyof JSX.IntrinsicElements)]?: boolean }
type NativeAttrs = Omit<React.HTMLAttributes<any>, keyof Props>
export type TextProps = Props & typeof defaultProps & NativeAttrs
const getModifierChild = (tags: Array<keyof JSX.IntrinsicElements>, children: ReactNode) => {
type TextRenderableElements = Array<keyof JSX.IntrinsicElements>
const getModifierChild = (tags: TextRenderableElements, children: ReactNode) => {
if (!tags.length) return children
const nextTag = tags.slice(1, tags.length)
return (
@@ -54,23 +58,16 @@ const getModifierChild = (tags: Array<keyof JSX.IntrinsicElements>, children: Re
)
}
const getModifiers = (inlineElements: ElementMap, children: ReactNode) => {
const names = Object.keys(inlineElements)
.filter((name: keyof JSX.IntrinsicElements) => inlineElements[name])
if (!names.length) return children
return getModifierChild(names as Array<keyof JSX.IntrinsicElements>, children)
}
const Text: React.FC<React.PropsWithChildren<TextProps>> = React.memo(({
h1, h2, h3, h4, h5, h6, p, b, small, i, span, del, em, children, className, ...props
h1, h2, h3, h4, h5, h6, p, b, small, i, span, del, em, blockquote,
children, className, ...props
}) => {
const elements: ElementMap = { h1, h2, h3, h4, h5, h6, p, small }
const inlineElements: ElementMap = { b, small, i, span, del, em }
const elements: ElementMap = { h1, h2, h3, h4, h5, h6, p, blockquote }
const inlineElements: ElementMap = { span, small, b, em, i, del }
const names = Object.keys(elements)
.filter((name: keyof JSX.IntrinsicElements) => elements[name])
.filter((name: keyof JSX.IntrinsicElements) => elements[name]) as TextRenderableElements
const inlineNames = Object.keys(inlineElements)
.filter((name: keyof JSX.IntrinsicElements) => inlineElements[name])
.filter((name: keyof JSX.IntrinsicElements) => inlineElements[name]) as TextRenderableElements
/**
* Render element "p" only if no element is found.
@@ -82,16 +79,20 @@ const Text: React.FC<React.PropsWithChildren<TextProps>> = React.memo(({
*
*/
const notSpecialElement = !names[0]
const defaultElement = (inlineNames[0] || 'p') as keyof JSX.IntrinsicElements
const tag = notSpecialElement ? defaultElement : (names[0]) as keyof JSX.IntrinsicElements
const modifers = useMemo(
() => {
if (notSpecialElement) return children
return getModifiers(inlineElements, children)
},
[inlineElements, children],
)
const tag = useMemo(() => {
if (names[0]) return names[0]
if (inlineNames[0]) return inlineNames[0]
return 'p' as keyof JSX.IntrinsicElements
}, [names, inlineNames])
const renderableChildElements = inlineNames
.filter((name: keyof JSX.IntrinsicElements) => name !== tag) as TextRenderableElements
const modifers = useMemo(() => {
if (!renderableChildElements.length) return children
return getModifierChild(renderableChildElements, children)
}, [renderableChildElements, children])
return (
<TextChild className={className} tag={tag} {...props}>{modifers}</TextChild>
)

View File

@@ -61,6 +61,38 @@ Display text using well-defined typographic styles.
</>
`} />
<Playground
title="Blockquote"
scope={{ Text }}
code={`
<Text blockquote>
Our mission is to make cloud computing accessible to everyone.
</Text>
`} />
<Playground
title="Types"
scope={{ Text }}
code={`
<>
<Text type="success">
Our mission is to make cloud computing accessible to everyone.
</Text>
<Text type="warning">
Our mission is to make cloud computing accessible to everyone.
</Text>
<Text type="secondary">
Our mission is to make cloud computing accessible to everyone.
</Text>
<Text type="error">
Our mission is to make cloud computing accessible to everyone.
</Text>
<Text style={{ color: '#ccc' }}>
Our mission is to make cloud computing accessible to everyone.
</Text>
</>
`} />
<Playground
title="Compose"
desc="Effect of multiple `Text` stacks"
@@ -91,9 +123,20 @@ Display text using well-defined typographic styles.
| **i** | component name | `boolean` | - | `false` |
| **em** | component name | `boolean` | - | `false` |
| **b** | component name | `boolean` | - | `false` |
| **type** | text type | `NormalTypes` | `'default', 'secondary', 'success', 'warning', 'error'` | `default` |
| **blockquote** | component name | `boolean` | - | `false` |
| **type** | text type | `NormalTypes` | [TextTypes](#texttypes) | `default` |
| ... | native props | `HTMLAttributes` | `'id', 'className', ...` | - |
<Attributes.Title>TextTypes</Attributes.Title>
```ts
type TextTypes = 'default'
| 'secondary'
| 'success'
| 'warning'
| 'error'
```
</Attributes>
export default ({ children }) => <Layout meta={meta}>{children}</Layout>

View File

@@ -14,19 +14,14 @@ export const meta = {
title="标题"
scope={{ Text }}
code={`
() => {
const text = 'JavaScript 是一门编程语言。'
return (
<>
<Text h1>{text}</Text>
<Text h2>{text}</Text>
<Text h3>{text}</Text>
<Text h4>{text}</Text>
<Text h5>{text}</Text>
<Text h6>{text}</Text>
</>
)
}
<>
<Text h1>JavaScript 是一门编程语言。</Text>
<Text h2>JavaScript 是一门编程语言。</Text>
<Text h3>JavaScript 是一门编程语言。</Text>
<Text h4>JavaScript 是一门编程语言。</Text>
<Text h5>JavaScript 是一门编程语言。</Text>
<Text h6>JavaScript 是一门编程语言。</Text>
</>
`} />
<Playground
@@ -51,11 +46,44 @@ export const meta = {
code={`
<>
<Text small>
函数 用来封装可复用的功能。如果没有函数,一段特定的操作过程用几次就要重复写几次,而使用函数则只需写下函数名和一些简短的信息。
函数用来封装可复用的功能。如果没有函数,一段特定的操作过程用几次就要重复写几次,而使用函数则只需写下函数名和一些简短的信息。
</Text>
<Text small i>
函数 用来封装可复用的功能。如果没有函数,一段特定的操作过程用几次就要重复写几次,而使用函数则只需写下函数名和一些简短的信息。
函数用来封装可复用的功能。如果没有函数,一段特定的操作过程用几次就要重复写几次,而使用函数则只需写下函数名和一些简短的信息。
</Text>
</>
`} />
<Playground
title="引用"
scope={{ Text }}
code={`
<Text blockquote>
事件能为网页添加真实的交互能力。
</Text>
`} />
<Playground
title="色彩"
desc="以不同的色彩区分文字的状态。"
scope={{ Text }}
code={`
<>
<Text type="success">
中间的代理服务器必须转发未经修改的端到端消息头,并且必须缓存它们。
</Text>
<Text type="warning">
中间的代理服务器必须转发未经修改的端到端消息头,并且必须缓存它们。
</Text>
<Text type="secondary">
中间的代理服务器必须转发未经修改的端到端消息头,并且必须缓存它们。
</Text>
<Text type="error">
中间的代理服务器必须转发未经修改的端到端消息头,并且必须缓存它们。
</Text>
<Text style={{ color: '#ccc' }}>
中间的代理服务器必须转发未经修改的端到端消息头,并且必须缓存它们。
</Text>
</>
`} />
@@ -67,12 +95,12 @@ export const meta = {
code={`
<>
<Text p>
函数 用来封装可复用的功能。如果没有函数,一段特定的操作过程用几次就要重复写几次,而使用函数则只需写下函数名和一些简短的信息。
函数用来封装可复用的功能。如果没有函数,一段特定的操作过程用几次就要重复写几次,而使用函数则只需写下函数名和一些简短的信息。
</Text>
<Text>
<Text small del>函数 用来封装可复用的功能。如果没有函数,一段特定的操作过程用几次就要重复写几次,而使用函数则只需写下函数名和一些简短的信息。</Text>
<Text small>函数 用来封装可复用的功能。如果没有函数,一段特定的操作过程用几次就要重复写几次,而使用函数则只需写下函数名和一些简短的信息。</Text>
<Text small del>函数用来封装可复用的功能。如果没有函数,一段特定的操作过程用几次就要重复写几次,而使用函数则只需写下函数名和一些简短的信息。</Text>
<Text small b>函数用来封装可复用的功能。如果没有函数,一段特定的操作过程用几次就要重复写几次,而使用函数则只需写下函数名和一些简短的信息。</Text>
</Text>
</>
`} />
@@ -95,17 +123,28 @@ export const meta = {
| 属性 | 描述 | 类型 | 推荐值 | 默认
| ---------- | ---------- | ---- | -------------- | ------ |
| **p** | component name | `boolean` | - | `true` |
| **h1 - h6** | component name | `boolean` | - | `false` |
| **small** | component name | `boolean` | - | `false` |
| **span** | component name | `boolean` | - | `false` |
| **del** | component name | `boolean` | - | `false` |
| **i** | component name | `boolean` | - | `false` |
| **em** | component name | `boolean` | - | `false` |
| **b** | component name | `boolean` | - | `false` |
| **type** | text type | `NormalTypes` | `'default', 'secondary', 'success', 'warning', 'error'` | `default` |
| **p** | 渲染的组件名 | `boolean` | - | `true` |
| **h1 - h6** | 渲染的组件名 | `boolean` | - | `false` |
| **small** | 渲染的组件名 | `boolean` | - | `false` |
| **span** | 渲染的组件名 | `boolean` | - | `false` |
| **del** | 渲染的组件名 | `boolean` | - | `false` |
| **i** | 渲染的组件名 | `boolean` | - | `false` |
| **em** | 渲染的组件名 | `boolean` | - | `false` |
| **b** | 渲染的组件名 | `boolean` | - | `false` |
| **blockquote** | 渲染的组件名 | `boolean` | - | `false` |
| **type** | 文字类型 | `NormalTypes` | [TextTypes](#texttypes) | `default` |
| ... | 原生属性 | `HTMLAttributes` | `'id', 'className', ...` | - |
<Attributes.Title>TextTypes</Attributes.Title>
```ts
type TextTypes = 'default'
| 'secondary'
| 'success'
| 'warning'
| 'error'
```
</Attributes>
export default ({ children }) => <Layout meta={meta}>{children}</Layout>