Skip to content

Commit

Permalink
feat: ✨ implement button component
Browse files Browse the repository at this point in the history
  • Loading branch information
huang-xiao-jian committed May 27, 2020
1 parent 8c0e260 commit 4422e66
Show file tree
Hide file tree
Showing 3 changed files with 380 additions and 0 deletions.
189 changes: 189 additions & 0 deletions packages/Button/Button.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
.van-button {
position: relative;
display: inline-flex;
align-items: center;
justify-content: center;
box-sizing: border-box;
padding: 0;
text-align: center;
vertical-align: middle;
-webkit-appearance: none;
-webkit-text-size-adjust: 100%;
height: 44px;
height: var(--button-default-height, 44px);
line-height: 20px;
line-height: var(--button-line-height, 20px);
font-size: 16px;
font-size: var(--button-default-font-size, 16px);
transition: opacity 0.2s;
transition: opacity var(--animation-duration-fast, 0.2s);
border-radius: 2px;
border-radius: var(--button-border-radius, 2px);
}
.van-button::before {
position: absolute;
top: 50%;
left: 50%;
width: 100%;
height: 100%;
border: inherit;
border-radius: inherit;
/* inherit parent's border radius */
transform: translate(-50%, -50%);
opacity: 0;
content: ' ';
background-color: #000;
background-color: var(--black, #000);
border-color: #000;
border-color: var(--black, #000);
}
.van-button::after {
border-width: 0;
}
.van-button--active::before {
opacity: 0.15;
}
.van-button--unclickable::after {
display: none;
}
.van-button--default {
color: #323233;
color: var(--button-default-color, #323233);
background: #fff;
background: var(--button-default-background-color, #fff);
border: 1px solid #ebedf0;
border: var(--button-border-width, 1px) solid
var(--button-default-border-color, #ebedf0);
}
.van-button--primary {
color: #fff;
color: var(--button-primary-color, #fff);
background: #07c160;
background: var(--button-primary-background-color, #07c160);
border: 1px solid #07c160;
border: var(--button-border-width, 1px) solid
var(--button-primary-border-color, #07c160);
}
.van-button--info {
color: #fff;
color: var(--button-info-color, #fff);
background: #1989fa;
background: var(--button-info-background-color, #1989fa);
border: 1px solid #1989fa;
border: var(--button-border-width, 1px) solid
var(--button-info-border-color, #1989fa);
}
.van-button--danger {
color: #fff;
color: var(--button-danger-color, #fff);
background: #ee0a24;
background: var(--button-danger-background-color, #ee0a24);
border: 1px solid #ee0a24;
border: var(--button-border-width, 1px) solid
var(--button-danger-border-color, #ee0a24);
}
.van-button--warning {
color: #fff;
color: var(--button-warning-color, #fff);
background: #ff976a;
background: var(--button-warning-background-color, #ff976a);
border: 1px solid #ff976a;
border: var(--button-border-width, 1px) solid
var(--button-warning-border-color, #ff976a);
}
.van-button--plain {
background: #fff;
background: var(--button-plain-background-color, #fff);
}
.van-button--plain.van-button--primary {
color: #07c160;
color: var(--button-primary-background-color, #07c160);
}
.van-button--plain.van-button--info {
color: #1989fa;
color: var(--button-info-background-color, #1989fa);
}
.van-button--plain.van-button--danger {
color: #ee0a24;
color: var(--button-danger-background-color, #ee0a24);
}
.van-button--plain.van-button--warning {
color: #ff976a;
color: var(--button-warning-background-color, #ff976a);
}
.van-button--large {
width: 100%;
height: 50px;
height: var(--button-large-height, 50px);
}
.van-button--normal {
padding: 0 15px;
font-size: 14px;
font-size: var(--button-normal-font-size, 14px);
}
.van-button--small {
min-width: 60px;
min-width: var(--button-small-min-width, 60px);
height: 30px;
height: var(--button-small-height, 30px);
padding: 0 8px;
padding: 0 var(--padding-xs, 8px);
font-size: 12px;
font-size: var(--button-small-font-size, 12px);
}
.van-button--mini {
display: inline-block;
min-width: 50px;
min-width: var(--button-mini-min-width, 50px);
height: 22px;
height: var(--button-mini-height, 22px);
font-size: 10px;
font-size: var(--button-mini-font-size, 10px);
}
.van-button--mini + .van-button--mini {
margin-left: 5px;
}
.van-button--block {
display: flex;
width: 100%;
}
.van-button--round {
border-radius: 999px;
border-radius: var(--button-round-border-radius, 999px);
}
.van-button--square {
border-radius: 0;
}
.van-button--disabled {
opacity: 0.5;
opacity: var(--button-disabled-opacity, 0.5);
}
.van-button__text {
display: inline;
}
.van-button__loading-text,
.van-button__icon + .van-button__text:not(:empty) {
margin-left: 4px;
}
.van-button__icon {
min-width: 1em;
line-height: inherit !important;
vertical-align: top;
}
.van-button--hairline {
padding-top: 1px;
border-width: 0;
}
.van-button--hairline::after {
border-color: inherit;
border-width: 1px;
border-radius: calc(2px * 2);
border-radius: calc(var(--button-border-radius, 2px) * 2);
}
.van-button--hairline.van-button--round::after {
border-radius: 999px;
border-radius: var(--button-round-border-radius, 999px);
}
.van-button--hairline.van-button--square::after {
border-radius: 0;
}
187 changes: 187 additions & 0 deletions packages/Button/Button.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
// packages
import React, {
FunctionComponent,
CSSProperties,
ReactElement,
cloneElement,
} from 'react';
import clsx from 'clsx';
import {
Button as WechatButton,
ButtonProps as WechatButtonProps,
View,
} from 'remax/wechat';
// internal
import Loading from '../Loading';
import withDefaultProps from '../tools/with-default-props-advance';
import './Button.css';

// TODO - 遗漏 id, business-id, dataset 三属性,功能位置,待确认

// 默认值填充属性
interface NeutralButtonProps {
// 移植属性
plain: boolean;
block: boolean;
round: boolean;
square: boolean;
loading: boolean;
hairline: boolean;
disabled: boolean;
type: 'default' | 'primary' | 'info' | 'warning' | 'danger';
size: 'normal' | 'large' | 'small' | 'mini';
// 直接传递 loading 组件,不再进行属性透传
loader: ReactElement;
}

// 不包含默认值属性
interface ExogenousButtonProps {
loadingText?: string;
color?: string;
// 新增属性
// 直接传递 icon,不再进行属性透传
icon?: ReactElement;
// host button property
hoverClassName?: string;
// 容器类名,用以覆盖内部
className?: string;
}

type HostButtonProps = Pick<
WechatButtonProps,
| 'lang'
| 'sessionFrom'
| 'sendMessageTitle'
| 'sendMessagePath'
| 'sendMessageImg'
| 'appParameter'
| 'showMessageCard'
| 'onGetUserInfo'
| 'onContact'
| 'onGetPhoneNumber'
| 'onError'
| 'onLaunchApp'
| 'onOpenSetting'
>;
type ButtonProps = NeutralButtonProps & ExogenousButtonProps & HostButtonProps;

// scope
const DefaultButtonProps: NeutralButtonProps = {
type: 'default',
size: 'normal',
plain: false,
block: false,
round: false,
square: false,
loading: false,
hairline: false,
disabled: false,
loader: <Loading type="circular" size="20px" />,
};

const deriveHostButtonStyle = (color: string, plain: boolean) => {
const style: CSSProperties = {};

if (color) {
style.color = plain ? color : '#ffffff';

// Use background instead of backgroundColor to make linear-gradient work
if (!plain) {
style.background = color;
}

// hide border when color is linear-gradient
if (color.indexOf('gradient') !== -1) {
style.border = 0;
} else {
style.borderColor = color;
}
}

return style;
};

const Button: FunctionComponent<ButtonProps> = (props) => {
const {
className,
hoverClassName,
type,
size,
block,
round,
plain,
square,
loading,
disabled,
hairline,
color,
icon,
loader,
loadingText,
children,
} = props;
const unclickable = disabled || loading;
const loadingColor = plain
? color ?? '#c9c9c9'
: type === 'default'
? '#c9c9c9'
: '#ffffff';
const loadingHolder =
loader &&
cloneElement(loader, {
color: loadingColor,
});
const loadingTextHolder = loadingText && (
<View className="van-button__loading-text">{loadingText}</View>
);
const iconHolder =
icon &&
cloneElement(icon, {
size: '1.2em',
className: 'van-button__icon',
});
const staticHolder = <View className="van-button__text">{children}</View>;

// UI property
const classnames = {
container: clsx(
className,
'van-button',
`van-button--${type}`,
`van-button--${size}`,
{
'van-button--block': block,
'van-button--round': round,
'van-button--plain': plain,
'van-button--square': square,
'van-button--loading': loading,
'van-button--disabled': disabled,
'van-button--hairline': hairline,
'van-button--unclickable': unclickable,
'van-hairline--surround': hairline,
}
),
hover: clsx(hoverClassName, 'van-button--active'),
};
const stylesheets: Record<'container', CSSProperties> = {
container: deriveHostButtonStyle(color, plain),
};

return (
<WechatButton
style={stylesheets.container}
className={classnames.container}
hoverClassName={classnames.hover}
>
{loading && loadingHolder}
{loading && loadingTextHolder}
{!loading && iconHolder}
{!loading && staticHolder}
</WechatButton>
);
};

export default withDefaultProps<
ExogenousButtonProps & HostButtonProps,
NeutralButtonProps
>(DefaultButtonProps)(Button);
4 changes: 4 additions & 0 deletions packages/Button/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
/**
* @description - nothing but export component
*/
export { default } from './Button';

0 comments on commit 4422e66

Please sign in to comment.