refactor(Checkbox): extract Checker component

This commit is contained in:
chenjiahan 2020-09-24 19:36:04 +08:00
parent f7397e399d
commit eb68632e50
3 changed files with 171 additions and 250 deletions

147
src/checkbox/Checker.js Normal file
View File

@ -0,0 +1,147 @@
import { addUnit } from '../utils';
import Icon from '../icon';
export const checkerProps = {
name: null,
disabled: Boolean,
iconSize: [Number, String],
modelValue: null,
checkedColor: String,
labelPosition: String,
labelDisabled: Boolean,
shape: {
type: String,
default: 'round',
},
bindGroup: {
type: Boolean,
default: true,
},
};
export default {
props: {
...checkerProps,
bem: Function,
role: String,
parent: Object,
checked: Boolean,
},
emits: ['click', 'toggle'],
computed: {
isDisabled() {
return (this.parent && this.parent.disabled) || this.disabled;
},
direction() {
return (this.parent && this.parent.direction) || null;
},
iconStyle() {
const checkedColor =
this.checkedColor || (this.parent && this.parent.checkedColor);
if (checkedColor && this.checked && !this.isDisabled) {
return {
borderColor: checkedColor,
backgroundColor: checkedColor,
};
}
},
tabindex() {
return this.isDisabled ? -1 : 0;
},
disableBindRelation() {
return !this.bindGroup;
},
},
methods: {
onClick(event) {
const { target } = event;
const { icon } = this.$refs;
const iconClicked = icon === target || icon.contains(target);
if (!this.isDisabled && (iconClicked || !this.labelDisabled)) {
this.$emit('toggle');
// wait for toggle method to complete
// so we can get the changed value in the click event listener
setTimeout(() => {
this.$emit('click', event);
});
} else {
this.$emit('click', event);
}
},
genIcon() {
const { checked } = this;
const iconSize = this.iconSize || (this.parent && this.parent.iconSize);
return (
<div
ref="icon"
class={this.bem('icon', [
this.shape,
{ disabled: this.isDisabled, checked },
])}
style={{ fontSize: addUnit(iconSize) }}
>
{this.$slots.icon ? (
this.$slots.icon({ checked })
) : (
<Icon name="success" style={this.iconStyle} />
)}
</div>
);
},
genLabel() {
if (this.$slots.default) {
return (
<span
class={this.bem('label', [
this.labelPosition,
{ disabled: this.isDisabled },
])}
>
{this.$slots.default()}
</span>
);
}
},
},
render() {
const Children = [this.genIcon()];
if (this.labelPosition === 'left') {
Children.unshift(this.genLabel());
} else {
Children.push(this.genLabel());
}
return (
<div
role={this.role}
class={this.bem([
{
disabled: this.isDisabled,
'label-disabled': this.labelDisabled,
},
this.direction,
])}
tabindex={this.tabindex}
aria-checked={String(this.checked)}
onClick={this.onClick}
>
{Children}
</div>
);
},
};

View File

@ -1,66 +1,18 @@
import { addUnit, createNamespace } from '../utils';
import { createNamespace, pick } from '../utils';
import { FieldMixin } from '../mixins/field';
import { ChildrenMixin } from '../mixins/relation';
import Icon from '../icon';
import Checker, { checkerProps } from './Checker';
const [createComponent, bem] = createNamespace('checkbox');
export default createComponent({
mixins: [FieldMixin, ChildrenMixin('vanCheckbox')],
props: {
name: null,
disabled: Boolean,
iconSize: [Number, String],
modelValue: null,
checkedColor: String,
labelPosition: String,
labelDisabled: Boolean,
shape: {
type: String,
default: 'round',
},
bindGroup: {
type: Boolean,
default: true,
},
},
props: checkerProps,
emits: ['click', 'change', 'update:modelValue'],
emits: ['change', 'update:modelValue'],
computed: {
disableBindRelation() {
return !this.bindGroup;
},
isDisabled() {
return (this.parent && this.parent.disabled) || this.disabled;
},
direction() {
return (this.parent && this.parent.direction) || null;
},
iconStyle() {
const checkedColor =
this.checkedColor || (this.parent && this.parent.checkedColor);
if (checkedColor && this.checked && !this.isDisabled) {
return {
borderColor: checkedColor,
backgroundColor: checkedColor,
};
}
},
tabindex() {
if (this.isDisabled) {
return -1;
}
return 0;
},
checked: {
get() {
if (this.parent) {
@ -86,61 +38,6 @@ export default createComponent({
},
methods: {
onClick(event) {
const { target } = event;
const { icon } = this.$refs;
const iconClicked = icon === target || icon.contains(target);
if (!this.isDisabled && (iconClicked || !this.labelDisabled)) {
this.toggle();
// wait for toggle method to complete
// so we can get the changed value in the click event listener
setTimeout(() => {
this.$emit('click', event);
});
} else {
this.$emit('click', event);
}
},
genIcon() {
const { checked } = this;
const iconSize = this.iconSize || (this.parent && this.parent.iconSize);
return (
<div
ref="icon"
class={bem('icon', [
this.shape,
{ disabled: this.isDisabled, checked },
])}
style={{ fontSize: addUnit(iconSize) }}
>
{this.$slots.icon ? (
this.$slots.icon({ checked })
) : (
<Icon name="success" style={this.iconStyle} />
)}
</div>
);
},
genLabel() {
if (this.$slots.default) {
return (
<span
class={bem('label', [
this.labelPosition,
{ disabled: this.isDisabled },
])}
>
{this.$slots.default()}
</span>
);
}
},
// @exposed-api
toggle(checked = !this.checked) {
// When toggle method is called multiple times at the same time,
@ -179,30 +76,16 @@ export default createComponent({
},
render() {
const Children = [this.genIcon()];
if (this.labelPosition === 'left') {
Children.unshift(this.genLabel());
} else {
Children.push(this.genLabel());
}
return (
<div
<Checker
v-slots={pick(this.$slots, ['default', 'icon'])}
bem={bem}
role="checkbox"
class={bem([
{
disabled: this.isDisabled,
'label-disabled': this.labelDisabled,
},
this.direction,
])}
tabindex={this.tabindex}
aria-checked={String(this.checked)}
onClick={this.onClick}
>
{Children}
</div>
parent={this.parent}
checked={this.checked}
onToggle={this.toggle}
{...this.$props}
/>
);
},
});

View File

@ -1,57 +1,17 @@
import { addUnit, createNamespace } from '../utils';
import { pick, createNamespace } from '../utils';
import { ChildrenMixin } from '../mixins/relation';
import Icon from '../icon';
import Checker, { checkerProps } from '../checkbox/Checker';
const [createComponent, bem] = createNamespace('radio');
export default createComponent({
mixins: [ChildrenMixin('vanRadio')],
props: {
name: null,
disabled: Boolean,
iconSize: [Number, String],
modelValue: null,
checkedColor: String,
labelPosition: String,
labelDisabled: Boolean,
shape: {
type: String,
default: 'round',
},
},
props: checkerProps,
emits: ['click', 'update:modelValue'],
emits: ['update:modelValue'],
computed: {
isDisabled() {
return (this.parent && this.parent.disabled) || this.disabled;
},
direction() {
return (this.parent && this.parent.direction) || null;
},
iconStyle() {
const checkedColor =
this.checkedColor || (this.parent && this.parent.checkedColor);
if (checkedColor && this.checked && !this.isDisabled) {
return {
borderColor: checkedColor,
backgroundColor: checkedColor,
};
}
},
tabindex() {
if (this.isDisabled || !this.checked) {
return -1;
}
return 0;
},
currentValue: {
get() {
return this.parent ? this.parent.modelValue : this.modelValue;
@ -71,88 +31,19 @@ export default createComponent({
toggle() {
this.currentValue = this.name;
},
onClick(event) {
const { target } = event;
const { icon } = this.$refs;
const iconClicked = icon === target || icon.contains(target);
if (!this.isDisabled && (iconClicked || !this.labelDisabled)) {
this.toggle();
// wait for toggle method to complete
// so we can get the changed value in the click event listener
setTimeout(() => {
this.$emit('click', event);
});
} else {
this.$emit('click', event);
}
},
genIcon() {
const { checked } = this;
const iconSize = this.iconSize || (this.parent && this.parent.iconSize);
return (
<div
ref="icon"
class={bem('icon', [
this.shape,
{ disabled: this.isDisabled, checked },
])}
style={{ fontSize: addUnit(iconSize) }}
>
{this.$slots.icon ? (
this.$slots.icon({ checked })
) : (
<Icon name="success" style={this.iconStyle} />
)}
</div>
);
},
genLabel() {
if (this.$slots.default) {
return (
<span
class={bem('label', [
this.labelPosition,
{ disabled: this.isDisabled },
])}
>
{this.$slots.default()}
</span>
);
}
},
},
render() {
const Children = [this.genIcon()];
if (this.labelPosition === 'left') {
Children.unshift(this.genLabel());
} else {
Children.push(this.genLabel());
}
return (
<div
<Checker
v-slots={pick(this.$slots, ['default', 'icon'])}
bem={bem}
role="radio"
class={bem([
{
disabled: this.isDisabled,
'label-disabled': this.labelDisabled,
},
this.direction,
])}
tabindex={this.tabindex}
aria-checked={String(this.checked)}
onClick={this.onClick}
>
{Children}
</div>
parent={this.parent}
checked={this.checked}
onToggle={this.toggle}
{...this.$props}
/>
);
},
});