feat(button): add light type support

test(button): update snapshots
This commit is contained in:
unix
2020-05-15 20:14:31 +08:00
parent bd4582c871
commit 542c98272e
6 changed files with 185 additions and 85 deletions

View File

@@ -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;

View File

@@ -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>,
)

View File

@@ -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 {

View File

@@ -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
View 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})`
}

View File

@@ -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')