mirror of
https://github.com/zhigang1992/react.git
synced 2026-03-27 22:54:38 +08:00
Merge pull request #197 from unix/group
feat(button-group): add component
This commit is contained in:
@@ -0,0 +1,493 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`ButtonGroup buttons should be displayed vertically 1`] = `
|
||||
"<div class=\\"btn-group vertical \\"><button type=\\"button\\" class=\\"btn \\"><div class=\\"text\\">action1</div><style>
|
||||
.btn {
|
||||
box-sizing: border-box;
|
||||
display: inline-block;
|
||||
padding: 0 1.25rem;
|
||||
height: 2.5rem;
|
||||
line-height: 2.5rem;
|
||||
min-width: min-content;
|
||||
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;
|
||||
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.25rem;
|
||||
--zeit-ui-button-height: 2.5rem;
|
||||
--zeit-ui-button-color: #666;
|
||||
}
|
||||
|
||||
.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><button type=\\"button\\" class=\\"btn \\"><div class=\\"text\\">action2</div><style>
|
||||
.btn {
|
||||
box-sizing: border-box;
|
||||
display: inline-block;
|
||||
padding: 0 1.25rem;
|
||||
height: 2.5rem;
|
||||
line-height: 2.5rem;
|
||||
min-width: min-content;
|
||||
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;
|
||||
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.25rem;
|
||||
--zeit-ui-button-height: 2.5rem;
|
||||
--zeit-ui-button-color: #666;
|
||||
}
|
||||
|
||||
.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><style>
|
||||
.btn-group {
|
||||
display: inline-flex;
|
||||
border-radius: 5px;
|
||||
margin: 4pt;
|
||||
border: 1px solid #eaeaea;
|
||||
background-color: transparent;
|
||||
overflow: hidden;
|
||||
height: min-content;
|
||||
}
|
||||
|
||||
.vertical {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.btn-group :global(.btn) {
|
||||
border: none;
|
||||
}
|
||||
|
||||
.btn-group :global(.btn .text) {
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.horizontal :global(.btn:not(:first-child)) {
|
||||
border-top-left-radius: 0;
|
||||
border-bottom-left-radius: 0;
|
||||
border-left: 1px solid #eaeaea;
|
||||
}
|
||||
|
||||
.horizontal :global(.btn:not(:last-child)) {
|
||||
border-top-right-radius: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
}
|
||||
|
||||
.vertical :global(.btn:not(:first-child)) {
|
||||
border-top-left-radius: 0;
|
||||
border-top-right-radius: 0;
|
||||
border-top: 1px solid #eaeaea;
|
||||
}
|
||||
|
||||
.vertical :global(.btn:not(:last-child)) {
|
||||
border-bottom-left-radius: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
}
|
||||
</style></div>"
|
||||
`;
|
||||
|
||||
exports[`ButtonGroup props should be passed to each button 1`] = `
|
||||
"<div class=\\"btn-group horizontal \\"><button type=\\"button\\" class=\\"btn \\"><div class=\\"text\\">action</div><style>
|
||||
.btn {
|
||||
box-sizing: border-box;
|
||||
display: inline-block;
|
||||
padding: 0 0.625rem;
|
||||
height: 1.5rem;
|
||||
line-height: 1.5rem;
|
||||
min-width: min-content;
|
||||
width: auto;
|
||||
border-radius: 5px;
|
||||
font-weight: 400;
|
||||
font-size: .75rem;
|
||||
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;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
color: #fff;
|
||||
background-color: #0070f3;
|
||||
border: 1px solid #0070f3;
|
||||
cursor: pointer;
|
||||
pointer-events: auto;
|
||||
box-shadow: none;
|
||||
--zeit-ui-button-padding: 0.625rem;
|
||||
--zeit-ui-button-height: 1.5rem;
|
||||
--zeit-ui-button-color: #fff;
|
||||
}
|
||||
|
||||
.btn:hover {
|
||||
color: #0070f3;
|
||||
--zeit-ui-button-color: #0070f3;
|
||||
background-color: #fff;
|
||||
border-color: #0070f3;
|
||||
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><style>
|
||||
.btn-group {
|
||||
display: inline-flex;
|
||||
border-radius: 5px;
|
||||
margin: 4pt;
|
||||
border: 1px solid #fff;
|
||||
background-color: transparent;
|
||||
overflow: hidden;
|
||||
height: min-content;
|
||||
}
|
||||
|
||||
.vertical {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.btn-group :global(.btn) {
|
||||
border: none;
|
||||
}
|
||||
|
||||
.btn-group :global(.btn .text) {
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.horizontal :global(.btn:not(:first-child)) {
|
||||
border-top-left-radius: 0;
|
||||
border-bottom-left-radius: 0;
|
||||
border-left: 1px solid #fff;
|
||||
}
|
||||
|
||||
.horizontal :global(.btn:not(:last-child)) {
|
||||
border-top-right-radius: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
}
|
||||
|
||||
.vertical :global(.btn:not(:first-child)) {
|
||||
border-top-left-radius: 0;
|
||||
border-top-right-radius: 0;
|
||||
border-top: 1px solid #fff;
|
||||
}
|
||||
|
||||
.vertical :global(.btn:not(:last-child)) {
|
||||
border-bottom-left-radius: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
}
|
||||
</style></div>"
|
||||
`;
|
||||
|
||||
exports[`ButtonGroup props should be passed to each button 2`] = `
|
||||
"<div class=\\"btn-group horizontal \\"><button type=\\"button\\" class=\\"btn \\"><div class=\\"text\\">action</div><style>
|
||||
.btn {
|
||||
box-sizing: border-box;
|
||||
display: inline-block;
|
||||
padding: 0 0.625rem;
|
||||
height: 1.5rem;
|
||||
line-height: 1.5rem;
|
||||
min-width: min-content;
|
||||
width: auto;
|
||||
border-radius: 5px;
|
||||
font-weight: 400;
|
||||
font-size: .75rem;
|
||||
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;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
color: #fff;
|
||||
background-color: #0070f3;
|
||||
border: 1px solid #0070f3;
|
||||
cursor: pointer;
|
||||
pointer-events: auto;
|
||||
box-shadow: none;
|
||||
--zeit-ui-button-padding: 0.625rem;
|
||||
--zeit-ui-button-height: 1.5rem;
|
||||
--zeit-ui-button-color: #fff;
|
||||
}
|
||||
|
||||
.btn:hover {
|
||||
color: #0070f3;
|
||||
--zeit-ui-button-color: #0070f3;
|
||||
background-color: #fff;
|
||||
border-color: #0070f3;
|
||||
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><style>
|
||||
.btn-group {
|
||||
display: inline-flex;
|
||||
border-radius: 5px;
|
||||
margin: 4pt;
|
||||
border: 1px solid #0070f3;
|
||||
background-color: transparent;
|
||||
overflow: hidden;
|
||||
height: min-content;
|
||||
}
|
||||
|
||||
.vertical {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.btn-group :global(.btn) {
|
||||
border: none;
|
||||
}
|
||||
|
||||
.btn-group :global(.btn .text) {
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.horizontal :global(.btn:not(:first-child)) {
|
||||
border-top-left-radius: 0;
|
||||
border-bottom-left-radius: 0;
|
||||
border-left: 1px solid #0070f3;
|
||||
}
|
||||
|
||||
.horizontal :global(.btn:not(:last-child)) {
|
||||
border-top-right-radius: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
}
|
||||
|
||||
.vertical :global(.btn:not(:first-child)) {
|
||||
border-top-left-radius: 0;
|
||||
border-top-right-radius: 0;
|
||||
border-top: 1px solid #0070f3;
|
||||
}
|
||||
|
||||
.vertical :global(.btn:not(:last-child)) {
|
||||
border-bottom-left-radius: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
}
|
||||
</style></div>"
|
||||
`;
|
||||
|
||||
exports[`ButtonGroup should render correctly 1`] = `
|
||||
"<div class=\\"btn-group horizontal \\"><button type=\\"button\\" class=\\"btn \\"><div class=\\"text\\">action</div><style>
|
||||
.btn {
|
||||
box-sizing: border-box;
|
||||
display: inline-block;
|
||||
padding: 0 1.25rem;
|
||||
height: 2.5rem;
|
||||
line-height: 2.5rem;
|
||||
min-width: min-content;
|
||||
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;
|
||||
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.25rem;
|
||||
--zeit-ui-button-height: 2.5rem;
|
||||
--zeit-ui-button-color: #666;
|
||||
}
|
||||
|
||||
.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><style>
|
||||
.btn-group {
|
||||
display: inline-flex;
|
||||
border-radius: 5px;
|
||||
margin: 4pt;
|
||||
border: 1px solid #eaeaea;
|
||||
background-color: transparent;
|
||||
overflow: hidden;
|
||||
height: min-content;
|
||||
}
|
||||
|
||||
.vertical {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.btn-group :global(.btn) {
|
||||
border: none;
|
||||
}
|
||||
|
||||
.btn-group :global(.btn .text) {
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.horizontal :global(.btn:not(:first-child)) {
|
||||
border-top-left-radius: 0;
|
||||
border-bottom-left-radius: 0;
|
||||
border-left: 1px solid #eaeaea;
|
||||
}
|
||||
|
||||
.horizontal :global(.btn:not(:last-child)) {
|
||||
border-top-right-radius: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
}
|
||||
|
||||
.vertical :global(.btn:not(:first-child)) {
|
||||
border-top-left-radius: 0;
|
||||
border-top-right-radius: 0;
|
||||
border-top: 1px solid #eaeaea;
|
||||
}
|
||||
|
||||
.vertical :global(.btn:not(:last-child)) {
|
||||
border-bottom-left-radius: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
}
|
||||
</style></div>"
|
||||
`;
|
||||
53
components/button-group/__tests__/index.test.tsx
Normal file
53
components/button-group/__tests__/index.test.tsx
Normal file
@@ -0,0 +1,53 @@
|
||||
import React from 'react'
|
||||
import { mount } from 'enzyme'
|
||||
import { ButtonGroup, Button } from 'components'
|
||||
import { nativeEvent } from 'tests/utils'
|
||||
|
||||
describe('ButtonGroup', () => {
|
||||
it('should render correctly', () => {
|
||||
const wrapper = mount(
|
||||
<ButtonGroup>
|
||||
<Button>action</Button>
|
||||
</ButtonGroup>,
|
||||
)
|
||||
expect(wrapper.html()).toMatchSnapshot()
|
||||
expect(() => wrapper.unmount()).not.toThrow()
|
||||
})
|
||||
|
||||
it('props should be passed to each button', () => {
|
||||
const wrapper = mount(
|
||||
<ButtonGroup size="mini" type="success">
|
||||
<Button>action</Button>
|
||||
</ButtonGroup>,
|
||||
)
|
||||
expect(wrapper.html()).toMatchSnapshot()
|
||||
wrapper.setProps({ ghost: true })
|
||||
expect(wrapper.html()).toMatchSnapshot()
|
||||
expect(() => wrapper.unmount()).not.toThrow()
|
||||
})
|
||||
|
||||
it('should ignore events when group disabled', () => {
|
||||
const handler = jest.fn()
|
||||
const wrapper = mount(
|
||||
<ButtonGroup>
|
||||
<Button onClick={handler}>action</Button>
|
||||
</ButtonGroup>,
|
||||
)
|
||||
wrapper.find('button').simulate('click', nativeEvent)
|
||||
expect(handler).toHaveBeenCalledTimes(1)
|
||||
wrapper.setProps({ disabled: true })
|
||||
wrapper.find('button').simulate('click', nativeEvent)
|
||||
expect(handler).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
|
||||
it('buttons should be displayed vertically', () => {
|
||||
const wrapper = mount(
|
||||
<ButtonGroup vertical>
|
||||
<Button>action1</Button>
|
||||
<Button>action2</Button>
|
||||
</ButtonGroup>,
|
||||
)
|
||||
expect(wrapper.html()).toMatchSnapshot()
|
||||
expect(() => wrapper.unmount()).not.toThrow()
|
||||
})
|
||||
})
|
||||
20
components/button-group/button-group-context.ts
Normal file
20
components/button-group/button-group-context.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import React from 'react'
|
||||
import { NormalSizes, ButtonTypes } from '../utils/prop-types'
|
||||
|
||||
export interface ButtonGroupConfig {
|
||||
size?: NormalSizes
|
||||
type?: ButtonTypes
|
||||
ghost?: boolean
|
||||
disabled?: boolean
|
||||
isButtonGroup: boolean
|
||||
}
|
||||
|
||||
const defaultContext = {
|
||||
isButtonGroup: false,
|
||||
disabled: false,
|
||||
}
|
||||
|
||||
export const ButtonGroupContext = React.createContext<ButtonGroupConfig>(defaultContext)
|
||||
|
||||
export const useButtonGroupContext = (): ButtonGroupConfig =>
|
||||
React.useContext<ButtonGroupConfig>(ButtonGroupContext)
|
||||
116
components/button-group/button-group.tsx
Normal file
116
components/button-group/button-group.tsx
Normal file
@@ -0,0 +1,116 @@
|
||||
import React, { useMemo } from 'react'
|
||||
import useTheme from '../styles/use-theme'
|
||||
import withDefaults from '../utils/with-defaults'
|
||||
import { NormalSizes, ButtonTypes } from '../utils/prop-types'
|
||||
import { ButtonGroupContext, ButtonGroupConfig } from './button-group-context'
|
||||
import { getButtonColors } from '../button/styles'
|
||||
|
||||
interface Props {
|
||||
disabled?: boolean
|
||||
vertical?: boolean
|
||||
ghost?: boolean
|
||||
size?: NormalSizes
|
||||
type?: ButtonTypes
|
||||
className?: string
|
||||
}
|
||||
|
||||
const defaultProps = {
|
||||
disabled: false,
|
||||
vertical: false,
|
||||
ghost: false,
|
||||
size: 'medium' as NormalSizes,
|
||||
type: 'default' as ButtonTypes,
|
||||
className: '',
|
||||
}
|
||||
|
||||
type NativeAttrs = Omit<React.HTMLAttributes<any>, keyof Props>
|
||||
export type ButtonGroupProps = Props & typeof defaultProps & NativeAttrs
|
||||
|
||||
const ButtonGroup: React.FC<React.PropsWithChildren<ButtonGroupProps>> = ({
|
||||
disabled,
|
||||
size,
|
||||
type,
|
||||
ghost,
|
||||
vertical,
|
||||
children,
|
||||
className,
|
||||
}) => {
|
||||
const theme = useTheme()
|
||||
const initialValue = useMemo<ButtonGroupConfig>(
|
||||
() => ({
|
||||
disabled,
|
||||
size,
|
||||
type,
|
||||
ghost,
|
||||
isButtonGroup: true,
|
||||
}),
|
||||
[disabled, size, type],
|
||||
)
|
||||
|
||||
const { border } = useMemo(() => {
|
||||
const results = getButtonColors(theme, type, disabled, ghost)
|
||||
if (!ghost && type !== 'default')
|
||||
return {
|
||||
...results,
|
||||
border: theme.palette.background,
|
||||
}
|
||||
return results
|
||||
}, [theme, type, disabled, ghost])
|
||||
|
||||
return (
|
||||
<ButtonGroupContext.Provider value={initialValue}>
|
||||
<div className={`btn-group ${vertical ? 'vertical' : 'horizontal'} ${className}`}>
|
||||
{children}
|
||||
<style jsx>{`
|
||||
.btn-group {
|
||||
display: inline-flex;
|
||||
border-radius: ${theme.layout.radius};
|
||||
margin: ${theme.layout.gapQuarter};
|
||||
border: 1px solid ${border};
|
||||
background-color: transparent;
|
||||
overflow: hidden;
|
||||
height: min-content;
|
||||
}
|
||||
|
||||
.vertical {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.btn-group :global(.btn) {
|
||||
border: none;
|
||||
}
|
||||
|
||||
.btn-group :global(.btn .text) {
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.horizontal :global(.btn:not(:first-child)) {
|
||||
border-top-left-radius: 0;
|
||||
border-bottom-left-radius: 0;
|
||||
border-left: 1px solid ${border};
|
||||
}
|
||||
|
||||
.horizontal :global(.btn:not(:last-child)) {
|
||||
border-top-right-radius: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
}
|
||||
|
||||
.vertical :global(.btn:not(:first-child)) {
|
||||
border-top-left-radius: 0;
|
||||
border-top-right-radius: 0;
|
||||
border-top: 1px solid ${border};
|
||||
}
|
||||
|
||||
.vertical :global(.btn:not(:last-child)) {
|
||||
border-bottom-left-radius: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
}
|
||||
`}</style>
|
||||
</div>
|
||||
</ButtonGroupContext.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
const MemoButtonGroup = React.memo(ButtonGroup)
|
||||
|
||||
export default withDefaults(MemoButtonGroup, defaultProps)
|
||||
3
components/button-group/index.ts
Normal file
3
components/button-group/index.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
import ButtonGroup from './button-group'
|
||||
|
||||
export default ButtonGroup
|
||||
@@ -1,7 +1,7 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`ButtonIcon should render correctly 1`] = `
|
||||
"<button class=\\"btn \\"><span class=\\"icon \\"><svg></svg><style>
|
||||
"<button type=\\"button\\" class=\\"btn \\"><span class=\\"icon \\"><svg></svg><style>
|
||||
.icon {
|
||||
position: absolute;
|
||||
left: var(--zeit-ui-button-padding);
|
||||
@@ -26,13 +26,13 @@ exports[`ButtonIcon should render correctly 1`] = `
|
||||
width: calc(var(--zeit-ui-button-height) / 2.35);
|
||||
}
|
||||
</style></span><div class=\\"text left\\">action<style>
|
||||
.left {
|
||||
padding-left: 0;
|
||||
}
|
||||
.right {
|
||||
padding-right: 0;
|
||||
}
|
||||
</style></div><style>
|
||||
.left {
|
||||
padding-left: 0;
|
||||
}
|
||||
.right {
|
||||
padding-right: 0;
|
||||
}
|
||||
</style></div><style>
|
||||
.btn {
|
||||
box-sizing: border-box;
|
||||
display: inline-block;
|
||||
@@ -50,7 +50,8 @@ exports[`ButtonIcon should render correctly 1`] = `
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
white-space: nowrap;
|
||||
transition: all 0.2s ease 0s;
|
||||
transition: background-color 200ms ease 0ms, box-shadow 200ms ease 0ms,
|
||||
border 200ms ease 0ms;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
color: #666;
|
||||
@@ -95,7 +96,7 @@ exports[`ButtonIcon should render correctly 1`] = `
|
||||
`;
|
||||
|
||||
exports[`ButtonIcon should work with right 1`] = `
|
||||
"<button class=\\"btn \\"><span class=\\"icon right \\"><svg></svg><style>
|
||||
"<button type=\\"button\\" class=\\"btn \\"><span class=\\"icon right \\"><svg></svg><style>
|
||||
.icon {
|
||||
position: absolute;
|
||||
left: var(--zeit-ui-button-padding);
|
||||
@@ -120,13 +121,13 @@ exports[`ButtonIcon should work with right 1`] = `
|
||||
width: calc(var(--zeit-ui-button-height) / 2.35);
|
||||
}
|
||||
</style></span><div class=\\"text right\\">action<style>
|
||||
.left {
|
||||
padding-left: 0;
|
||||
}
|
||||
.right {
|
||||
padding-right: 0;
|
||||
}
|
||||
</style></div><style>
|
||||
.left {
|
||||
padding-left: 0;
|
||||
}
|
||||
.right {
|
||||
padding-right: 0;
|
||||
}
|
||||
</style></div><style>
|
||||
.btn {
|
||||
box-sizing: border-box;
|
||||
display: inline-block;
|
||||
@@ -144,7 +145,8 @@ exports[`ButtonIcon should work with right 1`] = `
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
white-space: nowrap;
|
||||
transition: all 0.2s ease 0s;
|
||||
transition: background-color 200ms ease 0ms, box-shadow 200ms ease 0ms,
|
||||
border 200ms ease 0ms;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
color: #666;
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import React, { useRef, useState, MouseEvent, useMemo } from 'react'
|
||||
import withDefaults from '../utils/with-defaults'
|
||||
import useTheme from '../styles/use-theme'
|
||||
import { ButtonTypes, NormalSizes } from '../utils/prop-types'
|
||||
import withDefaults from '../utils/with-defaults'
|
||||
import ButtonDrip from './button.drip'
|
||||
import ButtonLoading from '../loading'
|
||||
import ButtonIcon from './button-icon'
|
||||
import { ButtonTypes, NormalSizes } from '../utils/prop-types'
|
||||
import { filterPropsWithGroup, getButtonChildrenWithIcon } from './utils'
|
||||
import { useButtonGroupContext } from '../button-group/button-group-context'
|
||||
import { getButtonColors, getButtonCursor, getButtonHoverColors, getButtonSize } from './styles'
|
||||
|
||||
interface Props {
|
||||
@@ -37,27 +38,30 @@ const defaultProps = {
|
||||
type NativeAttrs = Omit<React.ButtonHTMLAttributes<any>, keyof Props>
|
||||
export type ButtonProps = Props & typeof defaultProps & NativeAttrs
|
||||
|
||||
const Button: React.FC<React.PropsWithChildren<ButtonProps>> = ({
|
||||
children,
|
||||
disabled,
|
||||
type,
|
||||
loading,
|
||||
shadow,
|
||||
ghost,
|
||||
effect,
|
||||
onClick,
|
||||
auto,
|
||||
size,
|
||||
icon,
|
||||
iconRight,
|
||||
className,
|
||||
...props
|
||||
}) => {
|
||||
const Button: React.FC<React.PropsWithChildren<ButtonProps>> = ({ ...btnProps }) => {
|
||||
const theme = useTheme()
|
||||
const buttonRef = useRef<HTMLButtonElement>(null)
|
||||
const [dripShow, setDripShow] = useState<boolean>(false)
|
||||
const [dripX, setDripX] = useState<number>(0)
|
||||
const [dripY, setDripY] = useState<number>(0)
|
||||
const groupConfig = useButtonGroupContext()
|
||||
const {
|
||||
children,
|
||||
disabled,
|
||||
type,
|
||||
loading,
|
||||
shadow,
|
||||
ghost,
|
||||
effect,
|
||||
onClick,
|
||||
auto,
|
||||
size,
|
||||
icon,
|
||||
iconRight,
|
||||
className,
|
||||
...props
|
||||
} = filterPropsWithGroup(btnProps, groupConfig)
|
||||
|
||||
const { bg, border, color } = useMemo(() => getButtonColors(theme, type, disabled, ghost), [
|
||||
theme,
|
||||
type,
|
||||
@@ -99,35 +103,19 @@ const Button: React.FC<React.PropsWithChildren<ButtonProps>> = ({
|
||||
onClick && onClick(event)
|
||||
}
|
||||
|
||||
const childrenWithIcon = useMemo(() => {
|
||||
const hasIcon = icon || iconRight
|
||||
const isRight = Boolean(iconRight)
|
||||
const paddingForAutoMode =
|
||||
auto || size === 'mini'
|
||||
? `calc(var(--zeit-ui-button-height) / 2 + var(--zeit-ui-button-padding) * .5)`
|
||||
: 0
|
||||
if (!hasIcon) return <div className="text">{children}</div>
|
||||
return (
|
||||
<>
|
||||
<ButtonIcon isRight={isRight}>{hasIcon}</ButtonIcon>
|
||||
<div className={`text ${isRight ? 'right' : 'left'}`}>
|
||||
{children}
|
||||
<style jsx>{`
|
||||
.left {
|
||||
padding-left: ${paddingForAutoMode};
|
||||
}
|
||||
.right {
|
||||
padding-right: ${paddingForAutoMode};
|
||||
}
|
||||
`}</style>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}, [children, icon, auto, size])
|
||||
const childrenWithIcon = useMemo(
|
||||
() =>
|
||||
getButtonChildrenWithIcon(auto, size, children, {
|
||||
icon,
|
||||
iconRight,
|
||||
}),
|
||||
[auto, size, children, icon, iconRight],
|
||||
)
|
||||
|
||||
return (
|
||||
<button
|
||||
ref={buttonRef}
|
||||
type="button"
|
||||
className={`btn ${className}`}
|
||||
disabled={disabled}
|
||||
onClick={clickHandler}
|
||||
@@ -159,7 +147,8 @@ const Button: React.FC<React.PropsWithChildren<ButtonProps>> = ({
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
white-space: nowrap;
|
||||
transition: all 0.2s ease 0s;
|
||||
transition: background-color 200ms ease 0ms, box-shadow 200ms ease 0ms,
|
||||
border 200ms ease 0ms;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
color: ${color};
|
||||
|
||||
56
components/button/utils.tsx
Normal file
56
components/button/utils.tsx
Normal file
@@ -0,0 +1,56 @@
|
||||
import React, { ReactNode } from 'react'
|
||||
import { NormalSizes } from '../utils/prop-types'
|
||||
import ButtonIcon from './button-icon'
|
||||
import { ButtonProps } from 'components/button/button'
|
||||
import { ButtonGroupConfig } from 'components/button-group/button-group-context'
|
||||
|
||||
export const getButtonChildrenWithIcon = (
|
||||
auto: boolean,
|
||||
size: NormalSizes,
|
||||
children: ReactNode,
|
||||
icons: {
|
||||
icon?: React.ReactNode
|
||||
iconRight?: React.ReactNode
|
||||
},
|
||||
) => {
|
||||
const { icon, iconRight } = icons
|
||||
const hasIcon = icon || iconRight
|
||||
const isRight = Boolean(iconRight)
|
||||
const paddingForAutoMode =
|
||||
auto || size === 'mini'
|
||||
? `calc(var(--zeit-ui-button-height) / 2 + var(--zeit-ui-button-padding) * .5)`
|
||||
: 0
|
||||
if (!hasIcon) return <div className="text">{children}</div>
|
||||
return (
|
||||
<>
|
||||
<ButtonIcon isRight={isRight}>{hasIcon}</ButtonIcon>
|
||||
<div className={`text ${isRight ? 'right' : 'left'}`}>
|
||||
{children}
|
||||
<style jsx>{`
|
||||
.left {
|
||||
padding-left: ${paddingForAutoMode};
|
||||
}
|
||||
.right {
|
||||
padding-right: ${paddingForAutoMode};
|
||||
}
|
||||
`}</style>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export const filterPropsWithGroup = (
|
||||
props: React.PropsWithChildren<ButtonProps>,
|
||||
config: ButtonGroupConfig,
|
||||
): ButtonProps => {
|
||||
if (!config.isButtonGroup) return props
|
||||
return {
|
||||
...props,
|
||||
auto: true,
|
||||
shadow: false,
|
||||
ghost: config.ghost || props.ghost,
|
||||
size: config.size || props.size,
|
||||
type: config.type || props.type,
|
||||
disabled: config.disabled || props.disabled,
|
||||
}
|
||||
}
|
||||
@@ -53,3 +53,4 @@ export { default as Divider } from './divider'
|
||||
export { default as User } from './user'
|
||||
export { default as Page } from './page'
|
||||
export { default as Grid } from './grid'
|
||||
export { default as ButtonGroup } from './button-group'
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`UseToast should render different actions 1`] = `
|
||||
"<div class=\\"toast-container \\"><div class=\\"toast \\"><div class=\\"message\\">hello</div><div class=\\"action\\"><button class=\\"btn \\"><div class=\\"text\\">remove</div><style>
|
||||
"<div class=\\"toast-container \\"><div class=\\"toast \\"><div class=\\"message\\">hello</div><div class=\\"action\\"><button type=\\"button\\" class=\\"btn \\"><div class=\\"text\\">remove</div><style>
|
||||
.btn {
|
||||
box-sizing: border-box;
|
||||
display: inline-block;
|
||||
@@ -19,7 +19,8 @@ exports[`UseToast should render different actions 1`] = `
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
white-space: nowrap;
|
||||
transition: all 0.2s ease 0s;
|
||||
transition: background-color 200ms ease 0ms, box-shadow 200ms ease 0ms,
|
||||
border 200ms ease 0ms;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
color: #fff;
|
||||
@@ -60,7 +61,7 @@ exports[`UseToast should render different actions 1`] = `
|
||||
.btn :global(.text div) {
|
||||
margin: 0;
|
||||
}
|
||||
</style></button><button class=\\"btn \\"><div class=\\"text\\">remove</div><style>
|
||||
</style></button><button type=\\"button\\" class=\\"btn \\"><div class=\\"text\\">remove</div><style>
|
||||
.btn {
|
||||
box-sizing: border-box;
|
||||
display: inline-block;
|
||||
@@ -78,7 +79,8 @@ exports[`UseToast should render different actions 1`] = `
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
white-space: nowrap;
|
||||
transition: all 0.2s ease 0s;
|
||||
transition: background-color 200ms ease 0ms, box-shadow 200ms ease 0ms,
|
||||
border 200ms ease 0ms;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
color: #666;
|
||||
@@ -133,13 +135,13 @@ exports[`UseToast should render different actions 1`] = `
|
||||
border: 0;
|
||||
border-radius: 5px;
|
||||
padding: 16pt;
|
||||
box-shadow: 0 5px 10px rgba(0, 0, 0, 0.12);
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
opacity: 0;
|
||||
opacity: 1;
|
||||
box-shadow: 0 5px 10px rgba(0, 0, 0, 0.12);
|
||||
transform: translate3d(0, 100%, 0px) scale(1);
|
||||
transition: all 400ms ease;
|
||||
transition: transform 400ms ease 0ms, visibility 200ms ease 0ms, opacity 200ms ease 0ms;
|
||||
}
|
||||
|
||||
.toast.visible {
|
||||
@@ -191,7 +193,7 @@ exports[`UseToast should render different actions 1`] = `
|
||||
`;
|
||||
|
||||
exports[`UseToast should work with different types 1`] = `
|
||||
"<div class=\\"toast-container \\"><div class=\\"toast \\"><div class=\\"message\\">hello</div><div class=\\"action\\"></div><style>
|
||||
"<div class=\\"toast-container \\"><div class=\\"toast visible \\"><div class=\\"message\\">hello</div><div class=\\"action\\"></div><style>
|
||||
.toast {
|
||||
width: 420px;
|
||||
max-width: 90vw;
|
||||
@@ -205,13 +207,13 @@ exports[`UseToast should work with different types 1`] = `
|
||||
border: 0;
|
||||
border-radius: 5px;
|
||||
padding: 16pt;
|
||||
box-shadow: 0 5px 10px rgba(0, 0, 0, 0.12);
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
opacity: 0;
|
||||
opacity: 1;
|
||||
box-shadow: 0 5px 10px rgba(0, 0, 0, 0.12);
|
||||
transform: translate3d(0, 100%, 0px) scale(1);
|
||||
transition: all 400ms ease;
|
||||
transition: transform 400ms ease 0ms, visibility 200ms ease 0ms, opacity 200ms ease 0ms;
|
||||
}
|
||||
|
||||
.toast.visible {
|
||||
|
||||
@@ -61,7 +61,7 @@ describe('UseToast', () => {
|
||||
|
||||
expectToastIsHidden(wrapper)
|
||||
triggerToast(wrapper, { type: 'success', text: 'hello' })
|
||||
await updateWrapper(wrapper)
|
||||
await updateWrapper(wrapper, 100)
|
||||
expectToastIsShow(wrapper)
|
||||
expect(wrapper.find('.toast-container').html()).toMatchSnapshot()
|
||||
})
|
||||
|
||||
@@ -21,13 +21,7 @@ const ToastContainer: React.FC<React.PropsWithChildren<{}>> = () => {
|
||||
const toastElements = useMemo(
|
||||
() =>
|
||||
toasts.map((t, i) => (
|
||||
<ToastItem
|
||||
index={i}
|
||||
total={toasts.length}
|
||||
toast={t}
|
||||
onHover={hover}
|
||||
key={`toast-${t.id}-${i}`}
|
||||
/>
|
||||
<ToastItem index={i} total={toasts.length} toast={t} onHover={hover} key={`toast-${i}`} />
|
||||
)),
|
||||
[toasts, hover],
|
||||
)
|
||||
|
||||
@@ -61,21 +61,23 @@ const getColors = (palette: ZeitUIThemesPalette, type?: NormalTypes) => {
|
||||
|
||||
const ToastItem: React.FC<ToatItemProps> = React.memo(({ index, total, toast, onHover }) => {
|
||||
const theme = useTheme()
|
||||
const { color, bgColor } = getColors(theme.palette, toast.type)
|
||||
const { color, bgColor } = useMemo(() => getColors(theme.palette, toast.type), [
|
||||
theme.palette,
|
||||
toast.type,
|
||||
])
|
||||
const [visible, setVisible] = useState<boolean>(false)
|
||||
const [hide, setHide] = useState<boolean>(false)
|
||||
|
||||
const reverseIndex = useMemo(() => total - (index + 1), [total, index])
|
||||
const translate = useMemo(() => {
|
||||
const calc = `100% + -75px + -${20 * reverseIndex}px`
|
||||
if (reverseIndex > 5) return `translate3d(0, -75px, -${reverseIndex}px) scale(.01)`
|
||||
if (reverseIndex >= 4) return `translate3d(0, -75px, -${reverseIndex}px) scale(.7)`
|
||||
if (onHover) {
|
||||
return `translate3d(0, ${reverseIndex * -75}px, -${reverseIndex}px) scale(${
|
||||
total === 1 ? 1 : 0.98205
|
||||
})`
|
||||
}
|
||||
return `translate3d(0, calc(${calc}), -${reverseIndex}px) scale(${1 - 0.05 * reverseIndex})`
|
||||
}, [onHover, index, total])
|
||||
}, [onHover, index, total, reverseIndex])
|
||||
|
||||
useEffect(() => {
|
||||
const timer = setTimeout(() => {
|
||||
@@ -98,6 +100,8 @@ const ToastItem: React.FC<ToatItemProps> = React.memo(({ index, total, toast, on
|
||||
clearTimeout(timer)
|
||||
}
|
||||
}, [reverseIndex, toast.willBeDestroy])
|
||||
/* istanbul ignore next */
|
||||
if (reverseIndex > 10) return null
|
||||
|
||||
return (
|
||||
<div
|
||||
@@ -119,13 +123,13 @@ const ToastItem: React.FC<ToatItemProps> = React.memo(({ index, total, toast, on
|
||||
border: 0;
|
||||
border-radius: ${theme.layout.radius};
|
||||
padding: ${theme.layout.gap};
|
||||
box-shadow: ${theme.expressiveness.shadowSmall};
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
opacity: 0;
|
||||
opacity: ${reverseIndex > 4 ? 0 : 1};
|
||||
box-shadow: ${reverseIndex > 4 ? 'none' : theme.expressiveness.shadowSmall};
|
||||
transform: translate3d(0, 100%, 0px) scale(1);
|
||||
transition: all 400ms ease;
|
||||
transition: transform 400ms ease 0ms, visibility 200ms ease 0ms, opacity 200ms ease 0ms;
|
||||
}
|
||||
|
||||
.toast.visible {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useEffect, useRef } from 'react'
|
||||
import React, { useEffect } from 'react'
|
||||
import { NormalTypes } from '../utils/prop-types'
|
||||
import useCurrentState from '../utils/use-current-state'
|
||||
import { useZEITUIContext } from '../utils/use-zeit-ui-context'
|
||||
@@ -22,35 +22,33 @@ const defaultToast = {
|
||||
delay: 2000,
|
||||
}
|
||||
|
||||
let destoryStack: Array<string> = []
|
||||
let maxDestoryTime: number = 0
|
||||
let destoryTimer: number | undefined
|
||||
|
||||
const useToasts = (): [Array<Toast>, (t: Toast) => void] => {
|
||||
const { updateToasts, toastHovering, toasts } = useZEITUIContext()
|
||||
const destoryStack = useRef<Array<string>>([])
|
||||
const destoryTimer = useRef<number | undefined>()
|
||||
const maxDestoryTime = useRef<number>(0)
|
||||
const [, setHovering, hoveringRef] = useCurrentState<boolean>(toastHovering)
|
||||
|
||||
useEffect(() => setHovering(toastHovering), [toastHovering])
|
||||
|
||||
const destoryAll = (delay: number) => {
|
||||
// Wait for all components to display before destroying
|
||||
// The destory means direct remove all element, whether in animation or not.
|
||||
const nextDestoryTime = delay + 500
|
||||
const destoryAll = (delay: number, time: number) => {
|
||||
/* istanbul ignore next */
|
||||
if (nextDestoryTime < maxDestoryTime.current) return
|
||||
clearTimeout(destoryTimer.current)
|
||||
maxDestoryTime.current = nextDestoryTime
|
||||
if (time <= maxDestoryTime) return
|
||||
clearTimeout(destoryTimer)
|
||||
maxDestoryTime = time
|
||||
|
||||
destoryTimer.current = window.setTimeout(() => {
|
||||
destoryTimer = window.setTimeout(() => {
|
||||
/* istanbul ignore next */
|
||||
updateToasts((currentToasts: Array<ToastWithID>) => {
|
||||
if (destoryStack.current.length < currentToasts.length) {
|
||||
if (destoryStack.length < currentToasts.length) {
|
||||
return currentToasts
|
||||
}
|
||||
destoryStack.current = []
|
||||
destoryStack = []
|
||||
return []
|
||||
})
|
||||
clearTimeout(destoryTimer.current)
|
||||
}, maxDestoryTime.current)
|
||||
clearTimeout(destoryTimer)
|
||||
}, delay + 350)
|
||||
}
|
||||
|
||||
const setToast = (toast: Toast): void => {
|
||||
@@ -64,8 +62,8 @@ const useToasts = (): [Array<Toast>, (t: Toast) => void] => {
|
||||
return { ...item, willBeDestroy: true }
|
||||
})
|
||||
})
|
||||
destoryStack.current.push(id)
|
||||
destoryAll(delay)
|
||||
destoryStack.push(id)
|
||||
destoryAll(delay, performance.now())
|
||||
}
|
||||
|
||||
updateToasts((currentToasts: Array<ToastWithID>) => {
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
116
pages/en-us/components/button-group.mdx
Normal file
116
pages/en-us/components/button-group.mdx
Normal file
@@ -0,0 +1,116 @@
|
||||
import { Layout, Playground, Attributes } from 'lib/components'
|
||||
import { Button, Spacer, ButtonGroup } from 'components'
|
||||
|
||||
export const meta = {
|
||||
title: 'Button-Group',
|
||||
group: 'Data Entry',
|
||||
}
|
||||
|
||||
## Button Group
|
||||
|
||||
A set of related buttons.
|
||||
|
||||
<Playground
|
||||
title="Basic"
|
||||
scope={{ Button, ButtonGroup }}
|
||||
code={`
|
||||
<ButtonGroup>
|
||||
<Button>One</Button>
|
||||
<Button>Two</Button>
|
||||
<Button>Three</Button>
|
||||
</ButtonGroup>
|
||||
`} />
|
||||
|
||||
<Playground
|
||||
title="Variant"
|
||||
desc="set the type or styles of all buttons in the group."
|
||||
scope={{ Button, ButtonGroup }}
|
||||
code={`
|
||||
<>
|
||||
<ButtonGroup type="success">
|
||||
<Button>One</Button>
|
||||
<Button>Two</Button>
|
||||
<Button>Three</Button>
|
||||
</ButtonGroup>
|
||||
<ButtonGroup type="abort">
|
||||
<Button>Action1</Button>
|
||||
<Button>Action2</Button>
|
||||
</ButtonGroup>
|
||||
<ButtonGroup type="warning" ghost>
|
||||
<Button>Action1</Button>
|
||||
<Button>Action2</Button>
|
||||
</ButtonGroup>
|
||||
</>
|
||||
`} />
|
||||
|
||||
<Playground
|
||||
title="Sizes"
|
||||
scope={{ Button, ButtonGroup }}
|
||||
code={`
|
||||
<>
|
||||
<ButtonGroup size="small">
|
||||
<Button>One</Button>
|
||||
<Button>Two</Button>
|
||||
<Button>Three</Button>
|
||||
</ButtonGroup>
|
||||
<ButtonGroup size="mini">
|
||||
<Button>Action1</Button>
|
||||
<Button>Action2</Button>
|
||||
</ButtonGroup>
|
||||
</>
|
||||
`} />
|
||||
|
||||
<Playground
|
||||
title="Vertical"
|
||||
scope={{ Button, ButtonGroup }}
|
||||
code={`
|
||||
<>
|
||||
<ButtonGroup size="small" vertical>
|
||||
<Button>One</Button>
|
||||
<Button>Two</Button>
|
||||
<Button>Three</Button>
|
||||
<Button>Four</Button>
|
||||
</ButtonGroup>
|
||||
</>
|
||||
`} />
|
||||
|
||||
<Playground
|
||||
title="Disabled"
|
||||
desc="disable all buttons in the group."
|
||||
scope={{ Button, ButtonGroup }}
|
||||
code={`
|
||||
<>
|
||||
<ButtonGroup size="small" disabled>
|
||||
<Button>One</Button>
|
||||
<Button>Two</Button>
|
||||
<Button>Three</Button>
|
||||
</ButtonGroup>
|
||||
</>
|
||||
`} />
|
||||
<Attributes edit="/pages/en-us/components/button-group.mdx">
|
||||
<Attributes.Title>ButtonGroup.Props</Attributes.Title>
|
||||
|
||||
| Attribute | Description | Type | Accepted values | Default
|
||||
| ---------- | ---------- | ---- | -------------- | ------ |
|
||||
| **type** | button type | `ButtonTypes` | [ButtonTypes](#buttontypes) | `default` |
|
||||
| **size** | button size | `NormalSizes` | [NormalSizes](#normalsizes) | `medium` |
|
||||
| **ghost** | the opposite color | `boolean` | - | `false` |
|
||||
| **vertical** | show all buttons vertically | `boolean` | - | `false` |
|
||||
| **disabled** | disable all buttons | `boolean` | - | `false` |
|
||||
| ... | native props | `ButtonHTMLAttributes` | `'id', 'className', ...` | - |
|
||||
|
||||
<Attributes.Title>ButtonTypes</Attributes.Title>
|
||||
|
||||
```ts
|
||||
type ButtonTypes = 'default' | 'secondary' | 'success' | 'warning' | 'error' | 'abort'
|
||||
```
|
||||
|
||||
<Attributes.Title>NormalSizes</Attributes.Title>
|
||||
|
||||
```ts
|
||||
type NormalSizes = 'mini' | 'small' | 'medium' | 'large'
|
||||
```
|
||||
|
||||
</Attributes>
|
||||
|
||||
export default ({ children }) => <Layout meta={meta}>{children}</Layout>
|
||||
116
pages/zh-cn/components/button-group.mdx
Normal file
116
pages/zh-cn/components/button-group.mdx
Normal file
@@ -0,0 +1,116 @@
|
||||
import { Layout, Playground, Attributes } from 'lib/components'
|
||||
import { Button, Spacer, ButtonGroup } from 'components'
|
||||
|
||||
export const meta = {
|
||||
title: '按钮组 Button-Group',
|
||||
group: '数据录入',
|
||||
}
|
||||
|
||||
## Button Group / 按钮组
|
||||
|
||||
展示一组具备相关性的按钮。
|
||||
|
||||
<Playground
|
||||
title="基础"
|
||||
scope={{ Button, ButtonGroup }}
|
||||
code={`
|
||||
<ButtonGroup>
|
||||
<Button>One</Button>
|
||||
<Button>Two</Button>
|
||||
<Button>Three</Button>
|
||||
</ButtonGroup>
|
||||
`} />
|
||||
|
||||
<Playground
|
||||
title="变体"
|
||||
desc="设置组内所有按钮的类型或样式。"
|
||||
scope={{ Button, ButtonGroup }}
|
||||
code={`
|
||||
<>
|
||||
<ButtonGroup type="success">
|
||||
<Button>One</Button>
|
||||
<Button>Two</Button>
|
||||
<Button>Three</Button>
|
||||
</ButtonGroup>
|
||||
<ButtonGroup type="abort">
|
||||
<Button>Action1</Button>
|
||||
<Button>Action2</Button>
|
||||
</ButtonGroup>
|
||||
<ButtonGroup type="warning" ghost>
|
||||
<Button>Action1</Button>
|
||||
<Button>Action2</Button>
|
||||
</ButtonGroup>
|
||||
</>
|
||||
`} />
|
||||
|
||||
<Playground
|
||||
title="大小"
|
||||
scope={{ Button, ButtonGroup }}
|
||||
code={`
|
||||
<>
|
||||
<ButtonGroup size="small">
|
||||
<Button>One</Button>
|
||||
<Button>Two</Button>
|
||||
<Button>Three</Button>
|
||||
</ButtonGroup>
|
||||
<ButtonGroup size="mini">
|
||||
<Button>Action1</Button>
|
||||
<Button>Action2</Button>
|
||||
</ButtonGroup>
|
||||
</>
|
||||
`} />
|
||||
|
||||
<Playground
|
||||
title="垂直的"
|
||||
scope={{ Button, ButtonGroup }}
|
||||
code={`
|
||||
<>
|
||||
<ButtonGroup size="small" vertical>
|
||||
<Button>One</Button>
|
||||
<Button>Two</Button>
|
||||
<Button>Three</Button>
|
||||
<Button>Four</Button>
|
||||
</ButtonGroup>
|
||||
</>
|
||||
`} />
|
||||
|
||||
<Playground
|
||||
title="禁用"
|
||||
desc="禁用组内所有的按钮。"
|
||||
scope={{ Button, ButtonGroup }}
|
||||
code={`
|
||||
<>
|
||||
<ButtonGroup size="small" disabled>
|
||||
<Button>One</Button>
|
||||
<Button>Two</Button>
|
||||
<Button>Three</Button>
|
||||
</ButtonGroup>
|
||||
</>
|
||||
`} />
|
||||
<Attributes edit="/pages/zh-cn/components/button-group.mdx">
|
||||
<Attributes.Title>ButtonGroup.Props</Attributes.Title>
|
||||
|
||||
| 属性 | 描述 | 类型 | 推荐值 | 默认
|
||||
| ---------- | ---------- | ---- | -------------- | ------ |
|
||||
| **type** | 按钮类型 | `ButtonTypes` | [ButtonTypes](#buttontypes) | `default` |
|
||||
| **size** | 按钮大小 | `NormalSizes` | [NormalSizes](#normalsizes) | `medium` |
|
||||
| **ghost** | 相反色彩模式的按钮 | `boolean` | - | `false` |
|
||||
| **vertical** | 以垂直方式显示所有按钮 | `boolean` | - | `false` |
|
||||
| **disabled** | 是否禁用所有按钮 | `boolean` | - | `false` |
|
||||
| ... | 原生属性 | `ButtonHTMLAttributes` | `'id', 'className', ...` | - |
|
||||
|
||||
<Attributes.Title>ButtonTypes</Attributes.Title>
|
||||
|
||||
```ts
|
||||
type ButtonTypes = 'default' | 'secondary' | 'success' | 'warning' | 'error' | 'abort'
|
||||
```
|
||||
|
||||
<Attributes.Title>NormalSizes</Attributes.Title>
|
||||
|
||||
```ts
|
||||
type NormalSizes = 'mini' | 'small' | 'medium' | 'large'
|
||||
```
|
||||
|
||||
</Attributes>
|
||||
|
||||
export default ({ children }) => <Layout meta={meta}>{children}</Layout>
|
||||
Reference in New Issue
Block a user