mirror of
https://github.com/zhigang1992/react.git
synced 2026-02-11 17:21:06 +08:00
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:
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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};
|
||||
}
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user