[improvement] Functional components be just functions (#2735)

This commit is contained in:
neverland 2019-02-14 11:56:02 +08:00 committed by GitHub
parent 166397dad4
commit 5a9143c736
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 704 additions and 674 deletions

View File

@ -4,75 +4,72 @@ import Loading from '../loading';
const [sfc, bem] = use('button'); const [sfc, bem] = use('button');
export default sfc({ function Button(h, props, slots, ctx) {
functional: true, const { type, disabled, loading, loadingText } = props;
props: { const onClick = event => {
text: String, if (!loading && !disabled) {
block: Boolean, emit(ctx, 'click', event);
plain: Boolean,
round: Boolean,
square: Boolean,
loading: Boolean,
disabled: Boolean,
nativeType: String,
loadingText: String,
bottomAction: Boolean,
tag: {
type: String,
default: 'button'
},
type: {
type: String,
default: 'default'
},
size: {
type: String,
default: 'normal'
} }
};
return (
<props.tag
type={props.nativeType}
disabled={disabled}
class={bem([
type,
props.size,
{
loading,
disabled,
block: props.block,
plain: props.plain,
round: props.round,
square: props.square,
'bottom-action': props.bottomAction
}
])}
onClick={onClick}
{...inherit(ctx)}
>
{loading ? (
[
<Loading size="20px" color={type === 'default' ? undefined : ''} />,
loadingText && <span class={bem('loading-text')}>{loadingText}</span>
]
) : (
<span class={bem('text')}>
{slots.default ? slots.default() : props.text}
</span>
)}
</props.tag>
);
}
Button.props = {
text: String,
block: Boolean,
plain: Boolean,
round: Boolean,
square: Boolean,
loading: Boolean,
disabled: Boolean,
nativeType: String,
loadingText: String,
bottomAction: Boolean,
tag: {
type: String,
default: 'button'
}, },
type: {
render(h, context) { type: String,
const { props } = context; default: 'default'
const { type, disabled, loading, loadingText } = props; },
size: {
const onClick = event => { type: String,
if (!loading && !disabled) { default: 'normal'
emit(context, 'click', event);
}
};
return (
<props.tag
type={props.nativeType}
disabled={disabled}
class={bem([
type,
props.size,
{
loading,
disabled,
block: props.block,
plain: props.plain,
round: props.round,
square: props.square,
'bottom-action': props.bottomAction
}
])}
onClick={onClick}
{...inherit(context)}
>
{loading ? (
[
<Loading size="20px" color={type === 'default' ? undefined : ''} />,
loadingText && (
<span class={bem('loading-text')}>{loadingText}</span>
)
]
) : (
<span class={bem('text')}>{context.children || props.text}</span>
)}
</props.tag>
);
} }
}); };
export default sfc(Button);

View File

@ -2,21 +2,22 @@ import { use } from '../utils';
const [sfc, bem] = use('cell-group'); const [sfc, bem] = use('cell-group');
export default sfc({ function CellGroup(h, props, slots, ctx) {
functional: true, return (
<div
class={[bem(), { 'van-hairline--top-bottom': props.border }]}
{...ctx.data}
>
{slots.default && slots.default()}
</div>
);
}
props: { CellGroup.props = {
border: { border: {
type: Boolean, type: Boolean,
default: true default: true
}
},
render(h, context) {
return (
<div class={[bem(), { 'van-hairline--top-bottom': context.props.border }]} {...context.data}>
{context.children}
</div>
);
} }
}); };
export default sfc(CellGroup);

View File

@ -1,84 +1,80 @@
import { use, isDef } from '../utils'; import { use, isDef } from '../utils';
import { cellProps } from './shared'; import { cellProps } from './shared';
import { emit, inherit, unifySlots } from '../utils/functional'; import { emit, inherit } from '../utils/functional';
import { routeProps, functionalRoute } from '../mixins/router'; import { routeProps, functionalRoute } from '../mixins/router';
import Icon from '../icon'; import Icon from '../icon';
const [sfc, bem] = use('cell'); const [sfc, bem] = use('cell');
export default sfc({ function Cell(h, props, slots, ctx) {
functional: true, const { icon, size, title, label, value, isLink, arrowDirection } = props;
props: { const showTitle = slots.title || isDef(title);
...cellProps, const showValue = slots.default || isDef(value);
...routeProps,
size: String,
clickable: Boolean,
arrowDirection: String
},
render(h, context) { const Title = showTitle && (
const slots = unifySlots(context); <div class={[bem('title'), props.titleClass]}>
const { props } = context; {slots.title ? slots.title() : <span>{title}</span>}
const { icon, size, title, label, value, isLink, arrowDirection } = props; {label && <div class={[bem('label'), props.labelClass]}>{label}</div>}
</div>
);
const showTitle = slots.title || isDef(title); const Value = showValue && (
const showValue = slots.default || isDef(value); <div
class={[
bem('value', { alone: !slots.title && !title }),
props.valueClass
]}
>
{slots.default ? slots.default() : <span>{value}</span>}
</div>
);
const Title = showTitle && ( const LeftIcon = slots.icon
<div class={[bem('title'), props.titleClass]}> ? slots.icon()
{slots.title ? slots.title() : <span>{title}</span>} : icon && <Icon class={bem('left-icon')} name={icon} />;
{label && <div class={[bem('label'), props.labelClass]}>{label}</div>}
</div> const RightIcon = slots['right-icon']
? slots['right-icon']()
: isLink && (
<Icon
class={bem('right-icon')}
name={arrowDirection ? `arrow-${arrowDirection}` : 'arrow'}
/>
); );
const Value = showValue && ( const onClick = event => {
<div emit(ctx, 'click', event);
class={[ functionalRoute(ctx);
bem('value', { alone: !slots.title && !title }), };
props.valueClass
]}
>
{slots.default ? slots.default() : <span>{value}</span>}
</div>
);
const LeftIcon = slots.icon return (
? slots.icon() <div
: icon && <Icon class={bem('left-icon')} name={icon} />; class={bem({
center: props.center,
required: props.required,
borderless: !props.border,
clickable: isLink || props.clickable,
[size]: size
})}
onClick={onClick}
{...inherit(ctx)}
>
{LeftIcon}
{Title}
{Value}
{RightIcon}
{slots.extra && slots.extra()}
</div>
);
}
const RightIcon = slots['right-icon'] Cell.props = {
? slots['right-icon']() ...cellProps,
: isLink && ( ...routeProps,
<Icon size: String,
class={bem('right-icon')} clickable: Boolean,
name={arrowDirection ? `arrow-${arrowDirection}` : 'arrow'} arrowDirection: String
/> };
);
const onClick = event => { export default sfc(Cell);
emit(context, 'click', event);
functionalRoute(context);
};
return (
<div
class={bem({
center: props.center,
required: props.required,
borderless: !props.border,
clickable: isLink || props.clickable,
[size]: size
})}
onClick={onClick}
{...inherit(context)}
>
{LeftIcon}
{Title}
{Value}
{RightIcon}
{slots.extra && slots.extra()}
</div>
);
}
});

View File

@ -4,46 +4,46 @@ import Cell from '../cell';
const [sfc, bem, t] = use('contact-card'); const [sfc, bem, t] = use('contact-card');
export default sfc({ function ContactCard(h, props, slots, ctx) {
functional: true, const { type, editable } = props;
props: { return (
tel: String, <Cell
name: String, center
addText: String, border={false}
editable: { isLink={editable}
type: Boolean, class={bem([type])}
default: true valueClass={bem('value')}
}, icon={type === 'edit' ? 'contact' : 'add-square'}
type: { onClick={event => {
type: String, if (editable) {
default: 'add' emit(ctx, 'click', event);
} }
}}
{...inherit(ctx)}
>
{type === 'add'
? props.addText || t('addText')
: [
<div>{`${t('name')}${props.name}`}</div>,
<div>{`${t('tel')}${props.tel}`}</div>
]}
</Cell>
);
}
ContactCard.props = {
tel: String,
name: String,
addText: String,
editable: {
type: Boolean,
default: true
}, },
type: {
render(h, context) { type: String,
const { props } = context; default: 'add'
const { type, editable } = props;
return (
<Cell
center
border={false}
isLink={editable}
class={bem([type])}
valueClass={bem('value')}
icon={type === 'edit' ? 'contact' : 'add-square'}
onClick={event => {
if (editable) {
emit(context, 'click', event);
}
}}
{...inherit(context)}
>
{type === 'add'
? props.addText || t('addText')
: [<div>{`${t('name')}${props.name}`}</div>, <div>{`${t('tel')}${props.tel}`}</div>]}
</Cell>
);
} }
}); };
export default sfc(ContactCard);

View File

@ -8,62 +8,58 @@ import RadioGroup from '../radio-group';
const [sfc, bem, t] = use('contact-list'); const [sfc, bem, t] = use('contact-list');
export default sfc({ function ContactList(h, props, slots, ctx) {
functional: true, const List = props.list.map((item, index) => (
<Cell
key={item.id}
isLink
class={bem('item')}
valueClass={bem('item-value')}
scopedSlots={{
default: () => (
<Radio name={item.id}>
<div class={bem('name')}>{`${item.name}${item.tel}`}</div>
</Radio>
),
'right-icon': () => (
<Icon
name="edit"
class={bem('edit')}
onClick={event => {
event.stopPropagation();
emit(ctx, 'edit', item, index);
}}
/>
)
}}
onClick={() => {
emit(ctx, 'input', item.id);
emit(ctx, 'select', item, index);
}}
/>
));
props: { return (
value: null, <div class={bem()} {...inherit(ctx)}>
list: Array, <RadioGroup value={props.value} class={bem('group')}>
addText: String {List}
}, </RadioGroup>
<Button
render(h, context) { square
const { props, listeners } = context; size="large"
type="danger"
const List = props.list.map((item, index) => ( class={bem('add')}
<Cell text={props.addText || t('addText')}
key={item.id} onClick={ctx.listeners.add || noop}
isLink
class={bem('item')}
valueClass={bem('item-value')}
scopedSlots={{
default: () => (
<Radio name={item.id}>
<div class={bem('name')}>{`${item.name}${item.tel}`}</div>
</Radio>
),
'right-icon': () => (
<Icon
name="edit"
class={bem('edit')}
onClick={event => {
event.stopPropagation();
emit(context, 'edit', item, index);
}}
/>
)
}}
onClick={() => {
emit(context, 'input', item.id);
emit(context, 'select', item, index);
}}
/> />
)); </div>
);
}
return ( ContactList.props = {
<div class={bem()} {...inherit(context)}> value: null,
<RadioGroup value={props.value} class={bem('group')}> list: Array,
{List} addText: String
</RadioGroup> };
<Button
square export default sfc(ContactList);
size="large"
type="danger"
class={bem('add')}
text={props.addText || t('addText')}
onClick={listeners.add || noop}
/>
</div>
);
}
});

View File

@ -1,43 +1,38 @@
import { use } from '../utils'; import { use } from '../utils';
import Button from '../button'; import Button from '../button';
import { emit, inherit, unifySlots } from '../utils/functional'; import { emit, inherit } from '../utils/functional';
import { functionalRoute, routeProps } from '../mixins/router'; import { functionalRoute, routeProps } from '../mixins/router';
const [sfc, bem] = use('goods-action-big-btn'); const [sfc, bem] = use('goods-action-big-btn');
export default sfc({ function GoodsActionBigBtn(h, props, slots, ctx) {
functional: true, const onClick = event => {
emit(ctx, 'click', event);
functionalRoute(ctx);
};
props: { return (
...routeProps, <Button
text: String, square
primary: Boolean, class={bem()}
loading: Boolean, size="large"
disabled: Boolean loading={props.loading}
}, disabled={props.disabled}
type={props.primary ? 'danger' : 'warning'}
onClick={onClick}
{...inherit(ctx)}
>
{slots.default ? slots.default() : props.text}
</Button>
);
}
render(h, context) { GoodsActionBigBtn.props = {
const { props } = context; ...routeProps,
const slots = unifySlots(context); text: String,
primary: Boolean,
loading: Boolean,
disabled: Boolean
};
const onClick = event => { export default sfc(GoodsActionBigBtn);
emit(context, 'click', event);
functionalRoute(context);
};
return (
<Button
square
class={bem()}
size="large"
loading={props.loading}
disabled={props.disabled}
type={props.primary ? 'danger' : 'warning'}
onClick={onClick}
{...inherit(context)}
>
{slots.default ? slots.default() : props.text}
</Button>
);
}
});

View File

@ -1,43 +1,38 @@
import { use } from '../utils'; import { use } from '../utils';
import Icon from '../icon'; import Icon from '../icon';
import { emit, inherit, unifySlots } from '../utils/functional'; import { emit, inherit } from '../utils/functional';
import { functionalRoute, routeProps } from '../mixins/router'; import { functionalRoute, routeProps } from '../mixins/router';
const [sfc, bem] = use('goods-action-mini-btn'); const [sfc, bem] = use('goods-action-mini-btn');
export default sfc({ function GoodsActionMiniBtn(h, props, slots, ctx) {
functional: true, const onClick = event => {
emit(ctx, 'click', event);
functionalRoute(ctx);
};
props: { return (
...routeProps, <div
text: String, class={[bem(), 'van-hairline']}
icon: String, onClick={onClick}
info: [String, Number], {...inherit(ctx)}
iconClass: String >
}, <Icon
class={[bem('icon'), props.iconClass]}
info={props.info}
name={props.icon}
/>
{slots.default ? slots.default() : props.text}
</div>
);
}
render(h, context) { GoodsActionMiniBtn.props = {
const { props } = context; ...routeProps,
const slots = unifySlots(context); text: String,
icon: String,
info: [String, Number],
iconClass: String
};
const onClick = event => { export default sfc(GoodsActionMiniBtn);
emit(context, 'click', event);
functionalRoute(context);
};
return (
<div
class={[bem(), 'van-hairline']}
onClick={onClick}
{...inherit(context)}
>
<Icon
class={[bem('icon'), props.iconClass]}
info={props.info}
name={props.icon}
/>
{slots.default ? slots.default() : props.text}
</div>
);
}
});

View File

@ -2,14 +2,12 @@ import { use } from '../utils';
const [sfc, bem] = use('goods-action'); const [sfc, bem] = use('goods-action');
export default sfc({ function GoodsAction(h, props, slots, ctx) {
functional: true, return (
<div class={bem()} {...ctx.data}>
{slots.default && slots.default()}
</div>
);
}
render(h, context) { export default sfc(GoodsAction);
return (
<div class={bem()} {...context.data}>
{context.children}
</div>
);
}
});

View File

@ -4,40 +4,37 @@ import isSrc from '../utils/validate/src';
const [sfc] = use('icon'); const [sfc] = use('icon');
export default sfc({ function Icon(h, props, slots, ctx) {
functional: true, const urlIcon = isSrc(props.name);
props: { return (
name: String, <i
size: String, class={[
color: String, props.classPrefix,
info: [String, Number], urlIcon ? 'van-icon--image' : `${props.classPrefix}-${props.name}`
classPrefix: { ]}
type: String, style={{
default: 'van-icon' color: props.color,
} fontSize: props.size
}, }}
{...ctx.data}
>
{ctx.default && ctx.default()}
{urlIcon && <img src={props.name} />}
<Info info={props.info} />
</i>
);
}
render(h, context) { Icon.props = {
const { props } = context; name: String,
const urlIcon = isSrc(props.name); size: String,
color: String,
return ( info: [String, Number],
<i classPrefix: {
class={[ type: String,
props.classPrefix, default: 'van-icon'
urlIcon ? 'van-icon--image' : `${props.classPrefix}-${props.name}`
]}
style={{
color: props.color,
fontSize: props.size
}}
{...context.data}
>
{context.children}
{urlIcon && <img src={props.name} />}
<Info info={props.info} />
</i>
);
} }
}); };
export default sfc(Icon);

View File

@ -2,20 +2,18 @@ import { use, isDef } from '../utils';
const [sfc, bem] = use('info'); const [sfc, bem] = use('info');
export default sfc({ function Info(h, props, slots, ctx) {
functional: true, return (
isDef(props.info) && (
<div class={bem()} {...ctx.data}>
{props.info}
</div>
)
);
}
props: { Info.props = {
info: [String, Number] info: [String, Number]
}, };
render(h, { props, data }) { export default sfc(Info);
return (
isDef(props.info) && (
<div class={bem()} {...data}>
{props.info}
</div>
)
);
}
});

View File

@ -3,52 +3,50 @@ import { use } from '../utils';
const [sfc, bem] = use('loading'); const [sfc, bem] = use('loading');
const DEFAULT_COLOR = '#c9c9c9'; const DEFAULT_COLOR = '#c9c9c9';
export default sfc({ function Loading(h, props, slots, ctx) {
functional: true, const { color, size, type } = props;
props: { const colorType = color === 'white' || color === 'black' ? color : '';
size: String,
type: { const style = {
type: String, color: color === 'black' ? DEFAULT_COLOR : color,
default: 'circular' width: size,
}, height: size
color: { };
type: String,
default: DEFAULT_COLOR const Spin = [];
if (type === 'spinner') {
for (let i = 0; i < 12; i++) {
Spin.push(<i />);
} }
},
render(h, context) {
const { color, size, type } = context.props;
const colorType = color === 'white' || color === 'black' ? color : '';
const style = {
color: color === 'black' ? DEFAULT_COLOR : color,
width: size,
height: size
};
const Spin = [];
if (type === 'spinner') {
for (let i = 0; i < 12; i++) {
Spin.push(<i />);
}
}
const Circular = type === 'circular' && (
<svg class={bem('circular')} viewBox="25 25 50 50">
<circle cx="50" cy="50" r="20" fill="none" />
</svg>
);
return (
<div class={bem([type, colorType])} style={style} {...context.data}>
<span class={bem('spinner', type)}>
{Spin}
{Circular}
</span>
</div>
);
} }
});
const Circular = type === 'circular' && (
<svg class={bem('circular')} viewBox="25 25 50 50">
<circle cx="50" cy="50" r="20" fill="none" />
</svg>
);
return (
<div class={bem([type, colorType])} style={style} {...ctx.data}>
<span class={bem('spinner', type)}>
{Spin}
{Circular}
</span>
</div>
);
}
Loading.props = {
size: String,
type: {
type: String,
default: 'circular'
},
color: {
type: String,
default: DEFAULT_COLOR
}
};
export default sfc(Loading);

View File

@ -4,46 +4,56 @@ import Icon from '../icon';
const [sfc, bem] = use('nav-bar'); const [sfc, bem] = use('nav-bar');
export default sfc({ function NavBar(h, props, slots, ctx) {
functional: true, return (
<div
props: { class={[
title: String, bem({ fixed: props.fixed }),
fixed: Boolean, { 'van-hairline--bottom': props.border }
leftText: String, ]}
rightText: String, style={{ zIndex: props.zIndex }}
leftArrow: Boolean, {...inherit(ctx)}
border: { >
type: Boolean, <div class={bem('left')} onClick={ctx.listeners['click-left'] || noop}>
default: true {slots.left
}, ? slots.left()
zIndex: { : [
type: Number, props.leftArrow && (
default: 1 <Icon class={bem('arrow')} name="arrow-left" />
} ),
}, props.leftText && (
<span class={bem('text')}>{props.leftText}</span>
render(h, context) { )
const { props, listeners } = context;
const slots = context.slots();
return (
<div
class={[bem({ fixed: props.fixed }), { 'van-hairline--bottom': props.border }]}
style={{ zIndex: props.zIndex }}
{...inherit(context)}
>
<div class={bem('left')} onClick={listeners['click-left'] || noop}>
{slots.left || [
props.leftArrow && <Icon class={bem('arrow')} name="arrow-left" />,
props.leftText && <span class={bem('text')}>{props.leftText}</span>
]} ]}
</div>
<div class={[bem('title'), 'van-ellipsis']}>{slots.title || props.title}</div>
<div class={bem('right')} onClick={listeners['click-right'] || noop}>
{slots.right || (props.rightText && <span class={bem('text')}>{props.rightText}</span>)}
</div>
</div> </div>
); <div class={[bem('title'), 'van-ellipsis']}>
{slots.title ? slots.title() : props.title}
</div>
<div class={bem('right')} onClick={ctx.listeners['click-right'] || noop}>
{slots.right
? slots.right()
: props.rightText && (
<span class={bem('text')}>{props.rightText}</span>
)}
</div>
</div>
);
}
NavBar.props = {
title: String,
fixed: Boolean,
leftText: String,
rightText: String,
leftArrow: Boolean,
border: {
type: Boolean,
default: true
},
zIndex: {
type: Number,
default: 1
} }
}); };
export default sfc(NavBar);

View File

@ -4,35 +4,34 @@ import CellGroup from '../cell-group';
const [sfc, bem] = use('panel'); const [sfc, bem] = use('panel');
export default sfc({ function Panel(h, props, slots, ctx) {
functional: true, return (
<CellGroup class={bem()} {...ctx.data}>
{slots.header ? (
slots.header()
) : (
<Cell
icon={props.icon}
label={props.desc}
title={props.title}
value={props.status}
class={bem('header')}
valueClass={bem('header-value')}
/>
)}
<div class={bem('content')}>{slots.default && slots.default()}</div>
{slots.footer && (
<div class={[bem('footer'), 'van-hairline--top']}>{slots.footer()}</div>
)}
</CellGroup>
);
}
props: { Panel.props = {
icon: String, icon: String,
desc: String, desc: String,
title: String, title: String,
status: String status: String
}, };
render(h, context) { export default sfc(Panel);
const { props } = context;
const slots = context.slots();
return (
<CellGroup class={bem()} {...context.data}>
{slots.header || (
<Cell
icon={props.icon}
label={props.desc}
title={props.title}
value={props.status}
class={bem('header')}
valueClass={bem('header-value')}
/>
)}
<div class={bem('content')}>{slots.default}</div>
{slots.footer && <div class={[bem('footer'), 'van-hairline--top']}>{slots.footer}</div>}
</CellGroup>
);
}
});

View File

@ -3,49 +3,48 @@ import { emit } from '../utils/functional';
const [sfc, bem] = use('password-input'); const [sfc, bem] = use('password-input');
export default sfc({ function PasswordInput(h, props, slots, ctx) {
functional: true, const info = props.errorInfo || props.info;
props: { const Points = [];
info: String, for (let i = 0; i < props.length; i++) {
errorInfo: String, Points.push(
value: { <li class="van-hairline">
type: String, <i style={{ visibility: props.value[i] ? 'visible' : 'hidden' }} />
default: '' </li>
},
length: {
type: Number,
default: 6
}
},
render(h, context) {
const { props } = context;
const info = props.errorInfo || props.info;
const Points = [];
for (let i = 0; i < props.length; i++) {
Points.push(
<li class="van-hairline">
<i style={{ visibility: props.value[i] ? 'visible' : 'hidden' }} />
</li>
);
}
return (
<div class={bem()}>
<ul
class={[bem('security'), 'van-hairline--surround']}
onTouchstart={event => {
event.stopPropagation();
emit(context, 'focus', event);
}}
{...context.data}
>
{Points}
</ul>
{info && <div class={bem(props.errorInfo ? 'error-info' : 'info')}>{info}</div>}
</div>
); );
} }
});
return (
<div class={bem()}>
<ul
class={[bem('security'), 'van-hairline--surround']}
onTouchstart={event => {
event.stopPropagation();
emit(ctx, 'focus', event);
}}
{...ctx.data}
>
{Points}
</ul>
{info && (
<div class={bem(props.errorInfo ? 'error-info' : 'info')}>{info}</div>
)}
</div>
);
}
PasswordInput.props = {
info: String,
errorInfo: String,
value: {
type: String,
default: ''
},
length: {
type: Number,
default: 6
}
};
export default sfc(PasswordInput);

View File

@ -4,63 +4,61 @@ import Button from '../button';
const [sfc, bem, t] = use('submit-bar'); const [sfc, bem, t] = use('submit-bar');
export default sfc({ function SubmitBar(h, props, slots, ctx) {
functional: true, const { tip, price } = props;
const hasPrice = typeof price === 'number';
props: { return (
tip: String, <div class={bem()} {...inherit(ctx)}>
label: String, {slots.top && slots.top()}
loading: Boolean, {(slots.tip || tip) && (
disabled: Boolean, <div class={bem('tip')}>
buttonText: String, {tip}
price: { {slots.tip && slots.tip()}
type: Number,
default: null
},
currency: {
type: String,
default: '¥'
},
buttonType: {
type: String,
default: 'danger'
}
},
render(h, context) {
const { props, listeners } = context;
const { tip, price } = props;
const slots = context.slots();
const hasPrice = typeof price === 'number';
return (
<div class={bem()} {...inherit(context)}>
{slots.top}
{(slots.tip || tip) && (
<div class={bem('tip')}>
{tip}
{slots.tip}
</div>
)}
<div class={bem('bar')}>
{slots.default}
<div class={bem('text')}>
{hasPrice && [
<span>{props.label || t('label')}</span>,
<span class={bem('price')}>{`${props.currency} ${(price / 100).toFixed(2)}`}</span>
]}
</div>
<Button
square
size="large"
type={props.buttonType}
loading={props.loading}
disabled={props.disabled}
text={props.loading ? '' : props.buttonText}
onClick={listeners.submit || noop}
/>
</div> </div>
)}
<div class={bem('bar')}>
{slots.default && slots.default()}
<div class={bem('text')}>
{hasPrice && [
<span>{props.label || t('label')}</span>,
<span class={bem('price')}>{`${props.currency} ${(
price / 100
).toFixed(2)}`}</span>
]}
</div>
<Button
square
size="large"
type={props.buttonType}
loading={props.loading}
disabled={props.disabled}
text={props.loading ? '' : props.buttonText}
onClick={ctx.listeners.submit || noop}
/>
</div> </div>
); </div>
);
}
SubmitBar.props = {
tip: String,
label: String,
loading: Boolean,
disabled: Boolean,
buttonText: String,
price: {
type: Number,
default: null
},
currency: {
type: String,
default: '¥'
},
buttonType: {
type: String,
default: 'danger'
} }
}); };
export default sfc(SubmitBar);

View File

@ -2,31 +2,32 @@ import { use } from '../utils';
import { inherit } from '../utils/functional'; import { inherit } from '../utils/functional';
import Cell from '../cell'; import Cell from '../cell';
import Switch from '../switch'; import Switch from '../switch';
import SwitchMixin from '../mixins/switch'; import { switchProps } from '../switch/shared';
const [sfc, bem] = use('switch-cell'); const [sfc, bem] = use('switch-cell');
export default sfc({ function SwitchCell(h, props, slots, ctx) {
functional: true, return (
<Cell
center
title={props.title}
border={props.border}
class={bem()}
{...inherit(ctx)}
>
<Switch {...{ props, on: ctx.listeners }} />
</Cell>
);
}
mixins: [SwitchMixin], SwitchCell.props = {
...switchProps,
props: { title: String,
title: String, border: Boolean,
border: Boolean, size: {
size: { type: String,
type: String, default: '24px'
default: '24px'
}
},
render(h, context) {
const { props } = context;
return (
<Cell center title={props.title} border={props.border} class={bem()} {...inherit(context)}>
<Switch {...{ props, on: context.listeners }} />
</Cell>
);
} }
}); };
export default sfc(SwitchCell);

View File

@ -1,11 +1,11 @@
import { use } from '../utils'; import { use } from '../utils';
import Loading from '../loading'; import Loading from '../loading';
import SwitchMixin from '../mixins/switch'; import { switchProps } from './shared';
const [sfc, bem] = use('switch'); const [sfc, bem] = use('switch');
export default sfc({ export default sfc({
mixins: [SwitchMixin], props: switchProps,
methods: { methods: {
onClick() { onClick() {

23
packages/switch/shared.ts Normal file
View File

@ -0,0 +1,23 @@
/**
* Common Switch Props
*/
export const switchProps = {
value: null,
loading: Boolean,
disabled: Boolean,
activeColor: String,
inactiveColor: String,
activeValue: {
type: null,
default: true
},
inactiveValue: {
type: null,
default: false
},
size: {
type: String,
default: '30px'
}
};

View File

@ -9,44 +9,41 @@ const COLOR_MAP = {
success: GREEN success: GREEN
}; };
export default sfc({ function Tag(h, props, slots, ctx) {
functional: true, const { mark, plain, round, size } = ctx.props;
props: { const color = props.color || COLOR_MAP[props.type] || GRAY_DARK;
size: String, const key = plain ? 'color' : 'backgroundColor';
type: String, const style = { [key]: color };
mark: Boolean,
color: String,
plain: Boolean,
round: Boolean,
textColor: String
},
render(h, context) { if (props.textColor) {
const { props } = context; style.color = props.textColor;
const { mark, plain, round, size } = context.props;
const color = props.color || COLOR_MAP[props.type] || GRAY_DARK;
const key = plain ? 'color' : 'backgroundColor';
const style = { [key]: color };
if (props.textColor) {
style.color = props.textColor;
}
return (
<span
style={style}
class={[
bem({ mark, plain, round, [size]: size }),
{
'van-hairline--surround': plain
}
]}
{...context.data}
>
{context.children}
</span>
);
} }
});
return (
<span
style={style}
class={[
bem({ mark, plain, round, [size]: size }),
{
'van-hairline--surround': plain
}
]}
{...ctx.data}
>
{slots.default && slots.default()}
</span>
);
}
Tag.props = {
size: String,
type: String,
mark: Boolean,
color: String,
plain: Boolean,
round: Boolean,
textColor: String
};
export default sfc(Tag);

View File

@ -1,5 +1,4 @@
import { RenderContext, VNodeData } from 'vue/types'; import { RenderContext, VNodeData } from 'vue/types';
import { ScopedSlot } from 'vue/types/vnode';
type ObjectIndex = { type ObjectIndex = {
[key: string]: any; [key: string]: any;
@ -47,17 +46,3 @@ export function emit(context: Context, eventName: string, ...args: any[]) {
} }
} }
} }
// unify slots & scopedSlots
export function unifySlots(context: Context) {
const { scopedSlots } = context;
const slots = context.slots();
Object.keys(slots).forEach(key => {
if (!scopedSlots[key]) {
scopedSlots[key] = () => slots[key];
}
});
return scopedSlots;
}

View File

@ -4,13 +4,29 @@
import '../../locale'; import '../../locale';
import { camelize } from '..'; import { camelize } from '..';
import SlotsMixin from '../../mixins/slots'; import SlotsMixin from '../../mixins/slots';
import Vue, { VueConstructor, ComponentOptions } from 'vue'; import Vue, {
VueConstructor,
ComponentOptions,
CreateElement,
RenderContext
} from 'vue/types';
import { VNode, ScopedSlot } from 'vue/types/vnode';
type VantComponentOptions = ComponentOptions<Vue> & { type VantComponentOptions = ComponentOptions<Vue> & {
functional?: boolean; functional?: boolean;
install?: (Vue: VueConstructor) => void; install?: (Vue: VueConstructor) => void;
}; };
type VantPureComponent = {
(
h: CreateElement,
props: { [key: string]: any },
slots: { [key: string]: ScopedSlot | undefined },
context: RenderContext
): VNode;
props: any;
};
const arrayProp = { const arrayProp = {
type: Array, type: Array,
default: () => [] default: () => []
@ -39,15 +55,46 @@ function install(this: ComponentOptions<Vue>, Vue: VueConstructor) {
} }
} }
export default (name: string) => (sfc: VantComponentOptions) => { // unify slots & scopedSlots
sfc.name = name; export function unifySlots(context: RenderContext) {
sfc.install = install; const { scopedSlots } = context;
sfc.mixins = sfc.mixins || []; const slots = context.slots();
sfc.mixins.push(SlotsMixin);
Object.keys(slots).forEach(key => {
if (!scopedSlots[key]) {
scopedSlots[key] = () => slots[key];
}
});
return scopedSlots;
}
function transformPureComponent(pure: VantPureComponent): VantComponentOptions {
return {
functional: true,
props: pure.props,
render: (h, context) => pure(h, context.props, unifySlots(context), context)
};
}
export default (name: string) => (
sfc: VantComponentOptions | VantPureComponent
) => {
if (typeof sfc === 'function') {
sfc = transformPureComponent(sfc);
}
if (!sfc.functional) {
sfc.mixins = sfc.mixins || [];
sfc.mixins.push(SlotsMixin);
}
if (sfc.props) { if (sfc.props) {
defaultProps(sfc.props); defaultProps(sfc.props);
} }
sfc.name = name;
sfc.install = install;
return sfc; return sfc;
}; };