feat(modal): use Button to reconstrust Modal.Action (#332)

* feat(modal): use Button to reconstrust Modal.Action

* docs(modal): add example for action loading

* test: update snapshots
This commit is contained in:
witt
2020-07-15 18:11:11 +08:00
committed by unix
parent 98f701ec35
commit 194a70ae9b
5 changed files with 261 additions and 123 deletions

View File

@@ -103,53 +103,129 @@ exports[`Modal should render correctly 1`] = `
.content > :global(*:last-child) {
margin-bottom: 0;
}
</style><div></div><footer><button class=\\"\\">Cancel</button><style>
button {
font-size: 0.75rem;
text-transform: uppercase;
display: flex;
-webkit-box-align: center;
align-items: center;
-webkit-box-pack: center;
justify-content: center;
outline: none;
text-decoration: none;
transition: all 200ms ease-in-out 0s;
border: none;
color: #666;
background-color: #fff;
cursor: pointer;
flex: 1;
}
</style><div></div><footer><button type=\\"button\\" class=\\"btn mock \\"><div class=\\"text\\">Cancel</div><style>
.btn {
box-sizing: border-box;
display: inline-block;
padding: 0 1.375rem;
height: 2.5rem;
line-height: 2.5rem;
min-width: 12.5rem;
width: auto;
border-radius: 5px;
font-weight: 400;
font-size: .875rem;
user-select: none;
outline: none;
text-transform: capitalize;
justify-content: center;
text-align: center;
white-space: nowrap;
transition: background-color 200ms ease 0ms, box-shadow 200ms ease 0ms,
border 200ms ease 0ms, color 200ms ease 0ms;
position: relative;
overflow: hidden;
color: #666;
background-color: #fff;
border: 1px solid #eaeaea;
cursor: pointer;
pointer-events: auto;
box-shadow: none;
--zeit-ui-button-padding: 1.375rem;
--zeit-ui-button-height: 2.5rem;
--zeit-ui-button-color: #666;
--zeit-ui-button-bg: #fff;
}
button:hover {
color: #000;
background-color: #fafafa;
}
</style><button class=\\"\\">Submit</button><style>
button {
font-size: 0.75rem;
text-transform: uppercase;
display: flex;
-webkit-box-align: center;
align-items: center;
-webkit-box-pack: center;
justify-content: center;
outline: none;
text-decoration: none;
transition: all 200ms ease-in-out 0s;
border: none;
color: #000;
background-color: #fff;
cursor: pointer;
flex: 1;
}
.btn:hover {
color: #000;
--zeit-ui-button-color: #000;
background-color: #fff;
border-color: #000;
cursor: pointer;
pointer-events: auto;
box-shadow: none;
transform: translate3d(0px, 0px, 0px);
}
button:hover {
color: #000;
background-color: #fafafa;
}
</style></footer><style>
.btn :global(.text) {
position: relative;
z-index: 1;
display: inline-flex;
justify-content: center;
align-items: center;
text-align: center;
line-height: inherit;
top: -1px;
}
.btn :global(.text p),
.btn :global(.text pre),
.btn :global(.text div) {
margin: 0;
}
</style></button><button type=\\"button\\" class=\\"btn mock \\"><div class=\\"text\\">Submit</div><style>
.btn {
box-sizing: border-box;
display: inline-block;
padding: 0 1.375rem;
height: 2.5rem;
line-height: 2.5rem;
min-width: 12.5rem;
width: auto;
border-radius: 5px;
font-weight: 400;
font-size: .875rem;
user-select: none;
outline: none;
text-transform: capitalize;
justify-content: center;
text-align: center;
white-space: nowrap;
transition: background-color 200ms ease 0ms, box-shadow 200ms ease 0ms,
border 200ms ease 0ms, color 200ms ease 0ms;
position: relative;
overflow: hidden;
color: #666;
background-color: #fff;
border: 1px solid #eaeaea;
cursor: pointer;
pointer-events: auto;
box-shadow: none;
--zeit-ui-button-padding: 1.375rem;
--zeit-ui-button-height: 2.5rem;
--zeit-ui-button-color: #666;
--zeit-ui-button-bg: #fff;
}
.btn:hover {
color: #000;
--zeit-ui-button-color: #000;
background-color: #fff;
border-color: #000;
cursor: pointer;
pointer-events: auto;
box-shadow: none;
transform: translate3d(0px, 0px, 0px);
}
.btn :global(.text) {
position: relative;
z-index: 1;
display: inline-flex;
justify-content: center;
align-items: center;
text-align: center;
line-height: inherit;
top: -1px;
}
.btn :global(.text p),
.btn :global(.text pre),
.btn :global(.text div) {
margin: 0;
}
</style></button></footer><style>
footer {
display: flex;
overflow: hidden;
@@ -164,7 +240,7 @@ exports[`Modal should render correctly 1`] = `
border-bottom-right-radius: 5px;
}
footer > :global(button + button) {
footer > :global(button.btn + button.btn) {
border-left: 1px solid #eaeaea;
}

View File

@@ -1,7 +1,15 @@
import React, { MouseEvent, useMemo } from 'react'
import withDefaults from '../utils/with-defaults'
import React, {
MouseEvent,
PropsWithoutRef,
RefAttributes,
useImperativeHandle,
useMemo,
useRef,
} from 'react'
import css from 'styled-jsx/css'
import useTheme from '../styles/use-theme'
import { useModalContext } from './modal-context'
import Button, { ButtonProps } from '../button/button'
type ModalActionEvent = MouseEvent<HTMLButtonElement> & {
close: () => void
@@ -20,66 +28,82 @@ const defaultProps = {
disabled: false,
}
type NativeAttrs = Omit<React.ButtonHTMLAttributes<any>, keyof Props>
export type ModalActionProps = Props & typeof defaultProps & NativeAttrs
export type ModalActionProps = Props & typeof defaultProps & Omit<ButtonProps, keyof Props>
const ModalAction: React.FC<ModalActionProps> = ({
className,
children,
onClick,
passive,
disabled,
...props
}) => {
const theme = useTheme()
const { close } = useModalContext()
const clickHandler = (event: MouseEvent<HTMLButtonElement>) => {
if (disabled) return
const actionEvent = Object.assign({}, event, {
close: () => close && close(),
})
onClick && onClick(actionEvent)
}
const ModalAction = React.forwardRef<HTMLButtonElement, React.PropsWithChildren<ModalActionProps>>(
(
{ className, children, onClick, passive, disabled, ...props },
ref: React.Ref<HTMLButtonElement | null>,
) => {
const theme = useTheme()
const btnRef = useRef<HTMLButtonElement>(null)
const { close } = useModalContext()
useImperativeHandle(ref, () => btnRef.current)
const color = useMemo(() => {
return passive || disabled ? theme.palette.accents_5 : theme.palette.foreground
}, [theme.palette, passive, disabled])
const clickHandler = (event: MouseEvent<HTMLButtonElement>) => {
if (disabled) return
const actionEvent = Object.assign({}, event, {
close: () => close && close(),
})
onClick && onClick(actionEvent)
}
const bgColor = useMemo(() => {
return disabled ? theme.palette.accents_1 : theme.palette.background
}, [theme.palette, disabled])
const color = useMemo(() => {
return passive ? theme.palette.accents_5 : theme.palette.foreground
}, [theme.palette, passive, disabled])
return (
<>
<button className={className} onClick={clickHandler} {...props}>
const bgColor = useMemo(() => {
return disabled ? theme.palette.accents_1 : theme.palette.background
}, [theme.palette, disabled])
const { className: resolveClassName, styles } = css.resolve`
button.btn {
font-size: 0.75rem;
border: none;
color: ${color};
background-color: ${theme.palette.background};
display: flex;
-webkit-box-align: center;
align-items: center;
-webkit-box-pack: center;
justify-content: center;
flex: 1;
height: 100%;
border-radius: 0;
}
button.btn:hover {
color: ${disabled ? color : theme.palette.foreground};
background-color: ${disabled ? bgColor : theme.palette.accents_1};
}
`
const overrideProps = {
...props,
effect: false,
ref: btnRef,
}
return (
<Button
className={`${resolveClassName} ${className}`}
onClick={clickHandler}
disabled={disabled}
{...overrideProps}>
{children}
</button>
<style jsx>{`
button {
font-size: 0.75rem;
text-transform: uppercase;
display: flex;
-webkit-box-align: center;
align-items: center;
-webkit-box-pack: center;
justify-content: center;
outline: none;
text-decoration: none;
transition: all 200ms ease-in-out 0s;
border: none;
color: ${color};
background-color: ${bgColor};
cursor: ${disabled ? 'not-allowed' : 'pointer'};
flex: 1;
}
{styles}
</Button>
)
},
)
button:hover {
color: ${disabled ? color : theme.palette.foreground};
background-color: ${disabled ? bgColor : theme.palette.accents_1};
}
`}</style>
</>
)
}
type ModalActionComponent<T, P = {}> = React.ForwardRefExoticComponent<
PropsWithoutRef<P> & RefAttributes<T>
>
export default withDefaults(ModalAction, defaultProps)
type ComponentProps = Partial<typeof defaultProps> &
Omit<Props, keyof typeof defaultProps> &
Partial<Omit<ButtonProps, keyof Props>>
ModalAction.defaultProps = defaultProps
export default ModalAction as ModalActionComponent<ComponentProps>

View File

@@ -22,7 +22,7 @@ const ModalActions: React.FC<React.PropsWithChildren<{}>> = ({ children, ...prop
border-bottom-right-radius: ${theme.layout.radius};
}
footer > :global(button + button) {
footer > :global(button.btn + button.btn) {
border-left: 1px solid ${theme.palette.border};
}

View File

@@ -159,6 +159,30 @@ Display popup content that requires attention or provides additional information
`}
/>
<Playground
title="Loading"
scope={{ Modal, Button, useModal }}
code={`
() => {
const { visible, setVisible, bindings } = useModal()
return (
<>
<Button auto onClick={() => setVisible(true)}>Show Modal</Button>
<Modal {...bindings}>
<Modal.Title>Modal</Modal.Title>
<Modal.Subtitle>This is a modal</Modal.Subtitle>
<Modal.Content>
<p>Some content contained within the modal.</p>
</Modal.Content>
<Modal.Action passive onClick={() => setVisible(false)}>Cancel</Modal.Action>
<Modal.Action loading>Submit</Modal.Action>
</Modal>
</>
)
}
`}
/>
<Attributes edit="/pages/en-us/components/modal.mdx">
<Attributes.Title>Modal.Props</Attributes.Title>
@@ -190,12 +214,6 @@ Display popup content that requires attention or provides additional information
| --------- | ------------ | ---------------- | ------------------------ | ------- |
| ... | native props | `HTMLAttributes` | `'id', 'className', ...` | - |
<Attributes.Title>Modal.Actions.Props</Attributes.Title>
| Attribute | Description | Type | Accepted values | Default |
| --------- | ----------- | ---- | --------------- | ------- |
| - | - | - | - | - |
<Attributes.Title>Modal.Action.Props</Attributes.Title>
| Attribute | Description | Type | Accepted values | Default |
@@ -203,6 +221,7 @@ Display popup content that requires attention or provides additional information
| **passive** | display passive mode | `boolean` | - | `false` |
| **disabled** | disable current action | `boolean` | - | `false` |
| **onClick** | click handler | [(event: ModalActionEvent) => void](#modalactionevent) | - | - |
| **loading** | show loading | `boolean` | - | `false` |
| ... | native props | `ButtonHTMLAttributes` | `'id', 'className', ...` | - |
<Attributes.Title>useModal</Attributes.Title>

View File

@@ -40,8 +40,8 @@ export const meta = {
<Modal.Content>
<p> <Code>yield</Code> 关键字通常不能在箭头函数中使用(除非是嵌套在允许使用的函数内)。因此,箭头函数不能用作函数生成器。</p>
</Modal.Content>
<Modal.Action passive>放弃使用</Modal.Action>
<Modal.Action>明白了</Modal.Action>
<Modal.Action passive onClick={({ close }) => close()}>放弃使用</Modal.Action>
<Modal.Action onClick={({ close }) => close()}>明白了</Modal.Action>
</Modal>
</div>
)
@@ -69,8 +69,8 @@ export const meta = {
<Modal.Content>
<p> <Code>yield</Code> 关键字通常不能在箭头函数中使用(除非是嵌套在允许使用的函数内)。因此,箭头函数不能用作函数生成器。</p>
</Modal.Content>
<Modal.Action passive>放弃使用</Modal.Action>
<Modal.Action>明白了</Modal.Action>
<Modal.Action passive onClick={() => setVisible(false)}>放弃使用</Modal.Action>
<Modal.Action onClick={() => setVisible(false)}>明白了</Modal.Action>
</Modal>
</>
)
@@ -127,8 +127,8 @@ export const meta = {
<Modal.Content>
<p> <Code>yield</Code> 关键字通常不能在箭头函数中使用(除非是嵌套在允许使用的函数内)。因此,箭头函数不能用作函数生成器。</p>
</Modal.Content>
<Modal.Action passive>继续学习</Modal.Action>
<Modal.Action disabled>尝试箭头函数</Modal.Action>
<Modal.Action passive onClick={({ close }) => close()}>继续学习</Modal.Action>
<Modal.Action disabled onClick={close => close()}>尝试箭头函数</Modal.Action>
</Modal>
</>
)
@@ -158,6 +158,30 @@ export const meta = {
`}
/>
<Playground
title="加载中"
scope={{ Modal, Button, useModal, Code }}
code={`
() => {
const { visible, setVisible, bindings } = useModal()
return (
<>
<Button auto onClick={() => setVisible(true)}>Show Modal</Button>
<Modal {...bindings}>
<Modal.Title>箭头函数</Modal.Title>
<Modal.Subtitle>它不能用作构造函数</Modal.Subtitle>
<Modal.Content>
<p> <Code>yield</Code> 关键字通常不能在箭头函数中使用(除非是嵌套在允许使用的函数内)。因此,箭头函数不能用作函数生成器。</p>
</Modal.Content>
<Modal.Action passive onClick={() => setVisible(false)}>取消</Modal.Action>
<Modal.Action loading>提交</Modal.Action>
</Modal>
</>
)
}
`}
/>
<Attributes edit="/pages/zh-cn/components/modal.mdx">
<Attributes.Title>Modal.Props</Attributes.Title>
@@ -189,12 +213,6 @@ export const meta = {
| ---- | -------- | ---------------- | ------------------------ | ---- |
| ... | 原生属性 | `HTMLAttributes` | `'id', 'className', ...` | - |
<Attributes.Title>Modal.Actions.Props</Attributes.Title>
| 属性 | 描述 | 类型 | 推荐值 | 默认 |
| ---- | ---- | ---- | ------ | ---- |
| - | - | - | - | - |
<Attributes.Title>Modal.Action.Props</Attributes.Title>
| 属性 | 描述 | 类型 | 推荐值 | 默认 |
@@ -202,6 +220,7 @@ export const meta = {
| **passive** | 以消极的状态显示按钮 | `boolean` | - | `false` |
| **disabled** | 禁用按钮 | `boolean` | - | `false` |
| **onClick** | 按钮的点击事件 | [(event: ModalActionEvent) => void](#modalactionevent) | - | - |
| **loading** | 显示加载状态指示器 | `boolean` | - | `false` |
| ... | 原生属性 | `ButtonHTMLAttributes` | `'id', 'className', ...` | - |
<Attributes.Title>useModal</Attributes.Title>