mirror of
https://github.com/zhigang1992/react.git
synced 2026-03-26 06:55:07 +08:00
feat(button): add light type support
test(button): update snapshots
This commit is contained in:
@@ -51,7 +51,7 @@ exports[`ButtonIcon should render correctly 1`] = `
|
||||
text-align: center;
|
||||
white-space: nowrap;
|
||||
transition: background-color 200ms ease 0ms, box-shadow 200ms ease 0ms,
|
||||
border 200ms ease 0ms;
|
||||
border 200ms ease 0ms, color 200ms ease 0ms;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
color: #666;
|
||||
@@ -146,7 +146,7 @@ exports[`ButtonIcon should work with right 1`] = `
|
||||
text-align: center;
|
||||
white-space: nowrap;
|
||||
transition: background-color 200ms ease 0ms, box-shadow 200ms ease 0ms,
|
||||
border 200ms ease 0ms;
|
||||
border 200ms ease 0ms, color 200ms ease 0ms;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
color: #666;
|
||||
|
||||
@@ -13,9 +13,13 @@ describe('Button', () => {
|
||||
const wrapper = mount(
|
||||
<div>
|
||||
<Button type="secondary" />
|
||||
<Button type="secondary-light" />
|
||||
<Button type="success" />
|
||||
<Button type="success-light" />
|
||||
<Button type="warning" />
|
||||
<Button type="warning-light" />
|
||||
<Button type="error" />
|
||||
<Button type="error-light" />
|
||||
<Button type="abort" />
|
||||
</div>,
|
||||
)
|
||||
|
||||
@@ -6,7 +6,13 @@ import ButtonLoading from '../loading'
|
||||
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'
|
||||
import {
|
||||
getButtonColors,
|
||||
getButtonCursor,
|
||||
getButtonDripColor,
|
||||
getButtonHoverColors,
|
||||
getButtonSize,
|
||||
} from './styles'
|
||||
|
||||
interface Props {
|
||||
type?: ButtonTypes
|
||||
@@ -66,16 +72,23 @@ const Button: React.FC<React.PropsWithChildren<ButtonProps>> = ({ ...btnProps })
|
||||
...props
|
||||
} = filteredProps
|
||||
|
||||
const { bg, border, color } = useMemo(() => getButtonColors(theme, filteredProps), [
|
||||
theme,
|
||||
const { bg, border, color } = useMemo(() => getButtonColors(theme.palette, filteredProps), [
|
||||
theme.palette,
|
||||
filteredProps,
|
||||
])
|
||||
const hover = useMemo(() => getButtonHoverColors(theme.palette, filteredProps), [
|
||||
theme.palette,
|
||||
filteredProps,
|
||||
])
|
||||
const hover = useMemo(() => getButtonHoverColors(theme, filteredProps), [theme, filteredProps])
|
||||
const { cursor, events } = useMemo(() => getButtonCursor(disabled, loading), [disabled, loading])
|
||||
const { height, minWidth, padding, width, fontSize } = useMemo(() => getButtonSize(size, auto), [
|
||||
size,
|
||||
auto,
|
||||
])
|
||||
const dripColor = useMemo(() => getButtonDripColor(theme.palette, filteredProps), [
|
||||
theme.palette,
|
||||
filteredProps,
|
||||
])
|
||||
|
||||
/* istanbul ignore next */
|
||||
const dripCompletedHandle = () => {
|
||||
@@ -117,12 +130,7 @@ const Button: React.FC<React.PropsWithChildren<ButtonProps>> = ({ ...btnProps })
|
||||
{...props}>
|
||||
{loading ? <ButtonLoading /> : childrenWithIcon}
|
||||
{dripShow && (
|
||||
<ButtonDrip
|
||||
x={dripX}
|
||||
y={dripY}
|
||||
color={theme.palette.accents_2}
|
||||
onCompleted={dripCompletedHandle}
|
||||
/>
|
||||
<ButtonDrip x={dripX} y={dripY} color={dripColor} onCompleted={dripCompletedHandle} />
|
||||
)}
|
||||
<style jsx>{`
|
||||
.btn {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { ZeitUIThemes } from '../styles/themes'
|
||||
import { ZeitUIThemesPalette } from '../styles/themes'
|
||||
import { NormalSizes, ButtonTypes } from '../utils/prop-types'
|
||||
import { ButtonProps } from 'components/button/button'
|
||||
import { ButtonProps } from './button'
|
||||
import { addColorAlpha } from '../utils/color'
|
||||
|
||||
export interface ButtonColorGroup {
|
||||
bg: string
|
||||
@@ -9,162 +10,196 @@ export interface ButtonColorGroup {
|
||||
}
|
||||
|
||||
export const getButtonGhostColors = (
|
||||
theme: ZeitUIThemes,
|
||||
palette: ZeitUIThemesPalette,
|
||||
type: ButtonTypes,
|
||||
): ButtonColorGroup | null => {
|
||||
const colors: { [key in ButtonTypes]?: ButtonColorGroup } = {
|
||||
secondary: {
|
||||
bg: theme.palette.background,
|
||||
border: theme.palette.foreground,
|
||||
color: theme.palette.foreground,
|
||||
bg: palette.background,
|
||||
border: palette.foreground,
|
||||
color: palette.foreground,
|
||||
},
|
||||
success: {
|
||||
bg: theme.palette.background,
|
||||
border: theme.palette.success,
|
||||
color: theme.palette.success,
|
||||
bg: palette.background,
|
||||
border: palette.success,
|
||||
color: palette.success,
|
||||
},
|
||||
warning: {
|
||||
bg: theme.palette.background,
|
||||
border: theme.palette.warning,
|
||||
color: theme.palette.warning,
|
||||
bg: palette.background,
|
||||
border: palette.warning,
|
||||
color: palette.warning,
|
||||
},
|
||||
error: {
|
||||
bg: theme.palette.background,
|
||||
border: theme.palette.error,
|
||||
color: theme.palette.error,
|
||||
bg: palette.background,
|
||||
border: palette.error,
|
||||
color: palette.error,
|
||||
},
|
||||
}
|
||||
|
||||
return colors[type] || null
|
||||
}
|
||||
|
||||
export const getButtonColors = (theme: ZeitUIThemes, props: ButtonProps): ButtonColorGroup => {
|
||||
export const getButtonColors = (
|
||||
palette: ZeitUIThemesPalette,
|
||||
props: ButtonProps,
|
||||
): ButtonColorGroup => {
|
||||
const { type, disabled, ghost } = props
|
||||
const colors: { [key in ButtonTypes]?: ButtonColorGroup } = {
|
||||
default: {
|
||||
bg: theme.palette.background,
|
||||
border: theme.palette.border,
|
||||
color: theme.palette.accents_5,
|
||||
bg: palette.background,
|
||||
border: palette.border,
|
||||
color: palette.accents_5,
|
||||
},
|
||||
secondary: {
|
||||
bg: theme.palette.foreground,
|
||||
border: theme.palette.foreground,
|
||||
color: theme.palette.background,
|
||||
bg: palette.foreground,
|
||||
border: palette.foreground,
|
||||
color: palette.background,
|
||||
},
|
||||
success: {
|
||||
bg: theme.palette.success,
|
||||
border: theme.palette.success,
|
||||
bg: palette.success,
|
||||
border: palette.success,
|
||||
color: '#fff',
|
||||
},
|
||||
warning: {
|
||||
bg: theme.palette.warning,
|
||||
border: theme.palette.warning,
|
||||
bg: palette.warning,
|
||||
border: palette.warning,
|
||||
color: '#fff',
|
||||
},
|
||||
error: {
|
||||
bg: theme.palette.error,
|
||||
border: theme.palette.error,
|
||||
bg: palette.error,
|
||||
border: palette.error,
|
||||
color: '#fff',
|
||||
},
|
||||
abort: {
|
||||
bg: 'transparent',
|
||||
border: 'transparent',
|
||||
color: theme.palette.accents_5,
|
||||
color: palette.accents_5,
|
||||
},
|
||||
}
|
||||
if (disabled)
|
||||
return {
|
||||
bg: theme.palette.accents_1,
|
||||
border: theme.palette.accents_2,
|
||||
bg: palette.accents_1,
|
||||
border: palette.accents_2,
|
||||
color: '#ccc',
|
||||
}
|
||||
|
||||
const defaultColor = colors['default'] as ButtonColorGroup
|
||||
/**
|
||||
* The '-light' type is the same color as the common type,
|
||||
* only hover's color is different.
|
||||
* e.g.
|
||||
* Color['success'] === Color['success-light']
|
||||
* Color['warning'] === Color['warning-light']
|
||||
*/
|
||||
const withoutLightType = type.replace('-light', '') as ButtonTypes
|
||||
const defaultColor = colors.default as ButtonColorGroup
|
||||
|
||||
if (ghost) return getButtonGhostColors(theme, type) || defaultColor
|
||||
return colors[type] || defaultColor
|
||||
if (ghost) return getButtonGhostColors(palette, withoutLightType) || defaultColor
|
||||
return colors[withoutLightType] || defaultColor
|
||||
}
|
||||
|
||||
export const getButtonGhostHoverColors = (
|
||||
theme: ZeitUIThemes,
|
||||
palette: ZeitUIThemesPalette,
|
||||
type: ButtonTypes,
|
||||
): ButtonColorGroup | null => {
|
||||
const colors: { [key in ButtonTypes]?: ButtonColorGroup } = {
|
||||
secondary: {
|
||||
bg: theme.palette.foreground,
|
||||
border: theme.palette.background,
|
||||
color: theme.palette.background,
|
||||
bg: palette.foreground,
|
||||
border: palette.background,
|
||||
color: palette.background,
|
||||
},
|
||||
success: {
|
||||
bg: theme.palette.success,
|
||||
border: theme.palette.background,
|
||||
bg: palette.success,
|
||||
border: palette.background,
|
||||
color: 'white',
|
||||
},
|
||||
warning: {
|
||||
bg: theme.palette.warning,
|
||||
border: theme.palette.background,
|
||||
bg: palette.warning,
|
||||
border: palette.background,
|
||||
color: 'white',
|
||||
},
|
||||
error: {
|
||||
bg: theme.palette.error,
|
||||
border: theme.palette.background,
|
||||
bg: palette.error,
|
||||
border: palette.background,
|
||||
color: 'white',
|
||||
},
|
||||
}
|
||||
|
||||
return colors[type] || null
|
||||
const withoutLightType = type.replace('-light', '') as ButtonTypes
|
||||
return colors[withoutLightType] || null
|
||||
}
|
||||
|
||||
export const getButtonHoverColors = (theme: ZeitUIThemes, props: ButtonProps): ButtonColorGroup => {
|
||||
export const getButtonHoverColors = (
|
||||
palette: ZeitUIThemesPalette,
|
||||
props: ButtonProps,
|
||||
): ButtonColorGroup => {
|
||||
const { type, disabled, loading, shadow, ghost } = props
|
||||
const colors: { [key in ButtonTypes]?: any } = {
|
||||
const defaultColor = getButtonColors(palette, props)
|
||||
const alphaBackground = addColorAlpha(defaultColor.bg, 0.85)
|
||||
const colors: {
|
||||
[key in ButtonTypes]: Omit<ButtonColorGroup, 'color'> & {
|
||||
color?: string
|
||||
}
|
||||
} = {
|
||||
default: {
|
||||
bg: theme.palette.background,
|
||||
border: theme.palette.foreground,
|
||||
color: theme.palette.foreground,
|
||||
bg: palette.background,
|
||||
border: palette.foreground,
|
||||
},
|
||||
secondary: {
|
||||
bg: theme.palette.background,
|
||||
border: theme.palette.foreground,
|
||||
color: theme.palette.foreground,
|
||||
bg: palette.background,
|
||||
border: palette.foreground,
|
||||
},
|
||||
success: {
|
||||
bg: theme.palette.background,
|
||||
border: theme.palette.success,
|
||||
color: theme.palette.success,
|
||||
bg: palette.background,
|
||||
border: palette.success,
|
||||
},
|
||||
warning: {
|
||||
bg: theme.palette.background,
|
||||
border: theme.palette.warning,
|
||||
color: theme.palette.warning,
|
||||
bg: palette.background,
|
||||
border: palette.warning,
|
||||
},
|
||||
error: {
|
||||
bg: theme.palette.background,
|
||||
border: theme.palette.error,
|
||||
color: theme.palette.error,
|
||||
bg: palette.background,
|
||||
border: palette.error,
|
||||
},
|
||||
abort: {
|
||||
bg: 'transparent',
|
||||
border: 'transparent',
|
||||
color: theme.palette.accents_5,
|
||||
color: palette.accents_5,
|
||||
},
|
||||
'secondary-light': {
|
||||
...defaultColor,
|
||||
bg: alphaBackground,
|
||||
},
|
||||
'success-light': {
|
||||
...defaultColor,
|
||||
bg: alphaBackground,
|
||||
},
|
||||
'warning-light': {
|
||||
...defaultColor,
|
||||
bg: alphaBackground,
|
||||
},
|
||||
'error-light': {
|
||||
...defaultColor,
|
||||
bg: alphaBackground,
|
||||
},
|
||||
}
|
||||
const defaultHover = colors['default']
|
||||
|
||||
if (disabled)
|
||||
return {
|
||||
bg: theme.palette.accents_1,
|
||||
border: theme.palette.accents_2,
|
||||
bg: palette.accents_1,
|
||||
border: palette.accents_2,
|
||||
color: '#ccc',
|
||||
}
|
||||
if (loading)
|
||||
return {
|
||||
...getButtonColors(theme, props),
|
||||
...defaultColor,
|
||||
color: 'transparent',
|
||||
}
|
||||
if (shadow) return getButtonColors(theme, props)
|
||||
if (ghost) return getButtonGhostHoverColors(theme, type) || defaultHover
|
||||
return colors[type] || defaultHover
|
||||
if (shadow) return defaultColor
|
||||
|
||||
const hoverColor =
|
||||
(ghost ? getButtonGhostHoverColors(palette, type) : colors[type]) || colors.default
|
||||
return {
|
||||
...hoverColor,
|
||||
color: hoverColor.color || hoverColor.border,
|
||||
}
|
||||
}
|
||||
|
||||
export interface ButtonCursorGroup {
|
||||
@@ -247,3 +282,10 @@ export const getButtonSize = (size: NormalSizes = 'medium', auto: boolean): Butt
|
||||
|
||||
return layouts[size] || defaultLayout
|
||||
}
|
||||
|
||||
export const getButtonDripColor = (palette: ZeitUIThemesPalette, props: ButtonProps) => {
|
||||
const { type } = props
|
||||
const isLightHover = type.endsWith('light')
|
||||
const hoverColors = getButtonHoverColors(palette, props)
|
||||
return isLightHover ? addColorAlpha(hoverColors.bg, 0.65) : palette.accents_2
|
||||
}
|
||||
|
||||
35
components/utils/color.ts
Normal file
35
components/utils/color.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
const hexToRgb = (color: string): [number, number, number] => {
|
||||
const fullReg = /^#?([a-f\d])([a-f\d])([a-f\d])$/i
|
||||
const full = color.replace(fullReg, (_, r, g, b) => `${r}${r}${g}${g}${b}${b}`)
|
||||
const values = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(full)
|
||||
if (!values) {
|
||||
throw new Error(`ZEIT UI: Unsupported ${color} color.`)
|
||||
}
|
||||
return [
|
||||
Number.parseInt(values[1], 16),
|
||||
Number.parseInt(values[2], 16),
|
||||
Number.parseInt(values[3], 16),
|
||||
]
|
||||
}
|
||||
|
||||
export const colorToRgbValues = (color: string) => {
|
||||
if (color.charAt(0) === '#') return hexToRgb(color)
|
||||
|
||||
const safeColor = color.replace(/ /g, '')
|
||||
const colorType = color.substr(0, 4)
|
||||
|
||||
const regArray = safeColor.match(/\((.+)\)/)
|
||||
if (!colorType.startsWith('rgb') || !regArray) {
|
||||
console.log(color)
|
||||
throw new Error(`ZEIT UI: Only support ["RGB", "RGBA", "HEX"] color.`)
|
||||
}
|
||||
|
||||
return regArray[1].split(',').map(str => Number.parseFloat(str))
|
||||
}
|
||||
|
||||
export const addColorAlpha = (color: string, alpha: number) => {
|
||||
if (!/^#|rgb|RGB/.test(color)) return color
|
||||
const [r, g, b] = colorToRgbValues(color)
|
||||
const safeAlpha = alpha > 1 ? 1 : alpha < 0 ? 0 : alpha
|
||||
return `rgba(${r}, ${g}, ${b}, ${safeAlpha})`
|
||||
}
|
||||
@@ -1,6 +1,17 @@
|
||||
export const tuple = <T extends string[]>(...args: T) => args
|
||||
|
||||
const buttonTypes = tuple('default', 'secondary', 'success', 'warning', 'error', 'abort')
|
||||
const buttonTypes = tuple(
|
||||
'default',
|
||||
'secondary',
|
||||
'success',
|
||||
'warning',
|
||||
'error',
|
||||
'abort',
|
||||
'secondary-light',
|
||||
'success-light',
|
||||
'warning-light',
|
||||
'error-light',
|
||||
)
|
||||
|
||||
const normalSizes = tuple('mini', 'small', 'medium', 'large')
|
||||
|
||||
|
||||
Reference in New Issue
Block a user