Add button style to checkbox (#3697)

* Add button style to checkbox

* Implement min/max for checkbox button group

* Fixing a display bug when chekbox is ':checked'

* Update docs to integrate example button+min/max option

* Register correctly checkbox-button
Last fixes after cherry pick and bad rebase...
pull/4379/head
Mathieu DARTIGUES 2017-04-21 10:12:38 +02:00 committed by 杨奕
parent 8e1d8329aa
commit 0bf50bfc2e
10 changed files with 591 additions and 6 deletions

View File

@ -15,6 +15,7 @@
"radio-group": "./packages/radio-group/index.js",
"radio-button": "./packages/radio-button/index.js",
"checkbox": "./packages/checkbox/index.js",
"checkbox-button": "./packages/checkbox-button/index.js",
"checkbox-group": "./packages/checkbox-group/index.js",
"switch": "./packages/switch/index.js",
"select": "./packages/select/index.js",

View File

@ -13,7 +13,10 @@
cities: cityOptions,
checkedCities: ['Shanghai', 'Beijing'],
checkedCities1: ['Shanghai', 'Beijing'],
isIndeterminate: true
isIndeterminate: true,
checkboxGroup1: ['Shanghai'],
checkboxGroup2: ['Beijing'],
checkboxGroup3: ['Guangzhou']
};
},
methods: {
@ -154,7 +157,6 @@ The `indeterminate` property can help you to achieve a 'check all' effect.
```
:::
### Minimum / Maximum items checked
The `min` and `max` properties can help you to limit the number of checked items.
@ -184,6 +186,43 @@ The `min` and `max` properties can help you to limit the number of checked items
```
:::
### Button style
Checkbox with button styles.
:::demo You just need to change `<el-checkbox>` element into `<el-checkbox-button>` element. We also provide `size` attribute for these buttons: `large` and `small`.
```html
<template>
<div style="margin: 15px 0;"></div>
<el-checkbox-group v-model="checkboxGroup1">
<el-checkbox-button v-for="city in cities" :label="city">{{city}}</el-checkbox-button>
</el-checkbox-group>
<div style="margin: 15px 0;"></div>
<el-checkbox-group v-model="checkboxGroup2" size="small">
<el-checkbox-button v-for="city in cities" :label="city" :disabled="city === 'Shenzhen'">{{city}}</el-checkbox-button>
</el-checkbox-group>
<div style="margin: 15px 0;"></div>
<el-checkbox-group v-model="checkboxGroup3" size="large" fill="#324057" text-color="#a4aebd" :min="1" :max="3">
<el-checkbox-button v-for="city in cities" :label="city">{{city}}</el-checkbox-button>
</el-checkbox-group>
</template>
<script>
const cityOptions = ['Shanghai', 'Beijing', 'Guangzhou', 'Shenzhen'];
export default {
data () {
return {
checkboxGroup1: ['Shanghai'],
checkboxGroup2: ['Beijing'],
checkboxGroup3: ['Guangzhou'],
cities: cityOptions
};
}
}
</script>
```
:::
### Checkbox Attributes
| Attribute | Description | Type | Options | Default|
|---------- |-------- |---------- |------------- |-------- |
@ -198,6 +237,9 @@ The `min` and `max` properties can help you to limit the number of checked items
### Checkbox-group Attributes
| Attribute | Description | Type | Options | Default|
|---------- |-------- |---------- |------------- |-------- |
|size | the size of checkbox buttons | string | large/small | —
|fill | border and background color when button is active | string | — | #20a0ff |
|text-color | font color when button is active | string | — | #ffffff |
| min | minimum number of checkbox checked | number | — | — |
| max | maximum number of checkbox checked | number | — | — |

View File

@ -13,7 +13,10 @@
cities: cityOptions,
checkedCities: ['上海', '北京'],
checkedCities1: ['上海', '北京'],
isIndeterminate: true
isIndeterminate: true,
checkboxGroup1: ['上海'],
checkboxGroup2: ['北京'],
checkboxGroup3: ['广州']
};
},
methods: {
@ -191,7 +194,46 @@
};
</script>
```
:::
### Button style (to be translated)
Checkbox with button styles.
:::demo 只需要把`el-checkbox`元素换成`el-checkbox-button`元素即可此外Element 还提供了`size`属性给按钮组,支持`large`和`small`两种(如果不设定为默认)
```html
<template>
<div style="margin: 15px 0;"></div>
<el-checkbox-group v-model="checkboxGroup1">
<el-checkbox-button v-for="city in cities" :label="city">{{city}}</el-checkbox-button>
</el-checkbox-group>
<div style="margin: 15px 0;"></div>
<el-checkbox-group v-model="checkboxGroup2" size="small">
<el-checkbox-button v-for="city in cities" :label="city" :disabled="city === '深圳'">{{city}}</el-checkbox-button>
</el-checkbox-group>
<div style="margin: 15px 0;"></div>
<el-checkbox-group v-model="checkboxGroup3" size="large" fill="#324057" text-color="#a4aebd" :min="1" :max="3">
<el-checkbox-button v-for="city in cities" :label="city">{{city}}</el-checkbox-button>
</el-checkbox-group>
</template>
<script>
const cityOptions = ['上海', '北京', '广州', '深圳'];
export default {
data () {
return {
checkboxGroup1: ['上海'],
checkboxGroup2: ['北京'],
checkboxGroup3: ['广州'],
cities: cityOptions
};
}
}
</script>
```
:::
### Checkbox Attributes
| 参数 | 说明 | 类型 | 可选值 | 默认值 |
|---------- |-------- |---------- |------------- |-------- |
@ -203,9 +245,12 @@
| checked | 当前是否勾选 | boolean | — | false |
| indeterminate | 设置 indeterminate 状态,只负责样式控制 | boolean | — | false |
### Checkbox-group Attributes
### Checkbox-group Attributes (to be translated)
| 参数 | 说明 | 类型 | 可选值 | 默认值 |
|---------- |-------- |---------- |------------- |-------- |
| size | Checkbox 按钮组尺寸 | string | large, small | — |
| fill | 按钮激活时的填充色和边框色 | string | — | #20a0ff |
| text-color | 按钮激活时的文本颜色 | string | — | #ffffff |
| min | 可被勾选的 checkbox 的最大数量 | number | — | — |
| max | 可被勾选的 checkbox 的最小数量 | number | — | — |

View File

@ -0,0 +1,8 @@
import ElCheckboxButton from '../checkbox/src/checkbox-button.vue';
/* istanbul ignore next */
ElCheckboxButton.install = function(Vue) {
Vue.component(ElCheckboxButton.name, ElCheckboxButton);
};
export default ElCheckboxButton;

View File

@ -0,0 +1,161 @@
<template>
<label
class="el-checkbox-button"
:class="[
size ? 'el-checkbox-button--' + size : '',
{ 'is-disabled': disabled },
{ 'is-checked': isChecked },
{ 'is-focus': focus },
]"
>
<input
v-if="trueLabel || falseLabel"
class="el-checkbox-button__original"
type="checkbox"
:name="name"
:disabled="disabled"
:true-value="trueLabel"
:false-value="falseLabel"
v-model="model"
@change="handleChange"
@focus="focus = true"
@blur="focus = false">
<input
v-else
class="el-checkbox-button__original"
type="checkbox"
:name="name"
:disabled="disabled"
:value="label"
v-model="model"
@change="handleChange"
@focus="focus = true"
@blur="focus = false">
<span class="el-checkbox-button__inner"
v-if="$slots.default || label"
:style="isChecked ? activeStyle : null">
<slot>{{label}}</slot>
</span>
</label>
</template>
<script>
import Emitter from 'element-ui/src/mixins/emitter';
export default {
name: 'ElCheckboxButton',
mixins: [Emitter],
data() {
return {
selfModel: false,
focus: false
};
},
props: {
value: {},
label: {},
disabled: Boolean,
checked: Boolean,
name: String,
trueLabel: [String, Number],
falseLabel: [String, Number]
},
computed: {
model: {
get() {
return this._checkboxGroup
? this.store : this.value !== undefined
? this.value : this.selfModel;
},
set(val) {
if (this._checkboxGroup) {
let isLimitExceeded = false;
(this._checkboxGroup.min !== undefined &&
val.length < this._checkboxGroup.min &&
(isLimitExceeded = true));
(this._checkboxGroup.max !== undefined &&
val.length > this._checkboxGroup.max &&
(isLimitExceeded = true));
isLimitExceeded === false &&
this.dispatch('ElCheckboxGroup', 'input', [val]);
} else if (this.value !== undefined) {
this.$emit('input', val);
} else {
this.selfModel = val;
}
}
},
isChecked() {
if ({}.toString.call(this.model) === '[object Boolean]') {
return this.model;
} else if (Array.isArray(this.model)) {
return this.model.indexOf(this.label) > -1;
} else if (this.model !== null && this.model !== undefined) {
return this.model === this.trueLabel;
}
},
_checkboxGroup() {
let parent = this.$parent;
while (parent) {
if (parent.$options.componentName !== 'ElCheckboxGroup') {
parent = parent.$parent;
} else {
return parent;
}
}
return false;
},
store() {
return this._checkboxGroup ? this._checkboxGroup.value : this.value;
},
activeStyle() {
return {
backgroundColor: this._checkboxGroup.fill || '',
borderColor: this._checkboxGroup.fill || '',
color: this._checkboxGroup.textColor || '',
'box-shadow': '-1px 0 0 0 ' + this._checkboxGroup.fill
};
},
size() {
return this._checkboxGroup.size;
}
},
methods: {
addToStore() {
if (
Array.isArray(this.model) &&
this.model.indexOf(this.label) === -1
) {
this.model.push(this.label);
} else {
this.model = this.trueLabel || true;
}
},
handleChange(ev) {
this.$emit('change', ev);
if (this._checkboxGroup) {
this.$nextTick(_ => {
this.dispatch('ElCheckboxGroup', 'change', [this._checkboxGroup.value]);
});
}
}
},
created() {
this.checked && this.addToStore();
}
};
</script>

View File

@ -11,7 +11,10 @@
props: {
value: {},
min: Number,
max: Number
max: Number,
size: String,
fill: String,
textColor: String
},
watch: {

View File

@ -147,4 +147,108 @@
margin-left: 15px;
}
}
@b checkbox-button {
position: relative;
display: inline-block;
@e inner {
display: inline-block;
line-height: 1;
white-space: nowrap;
vertical-align: middle;
cursor: pointer;
background: var(--button-default-fill);
border: var(--border-base);
border-left: 0;
color: var(--button-default-color);
-webkit-appearance: none;
text-align: center;
box-sizing: border-box;
outline: none;
margin: 0;
position: relative;
cursor: pointer;
transition: var(--all-transition);
@utils-user-select none;
@mixin button-size var(--button-padding-vertical), var(--button-padding-horizontal), var(--button-font-size), 0;
&:hover {
color: var(--color-primary);
}
& [class*="el-icon-"] {
line-height: 0.9;
& + span {
margin-left: 5px;
}
}
}
@e original {
opacity: 0;
outline: none;
position: absolute;
margin: 0;
visibility: hidden;
left: -999px;
}
&.is-checked {
& .el-checkbox-button__inner {
color: var(--checkbox-button-checked-color);
background-color: var(--checkbox-button-checked-fill);
border-color: var(--checkbox-button-checked-border-color);
box-shadow: -1px 0 0 0 var(--checkbox-button-checked-border-color);
}
}
&.is-disabled {
& .el-checkbox-button__inner {
color: var(--button-disabled-color);
cursor: not-allowed;
background-image: none;
background-color: var(--button-disabled-fill);
border-color: var(--button-disabled-border);
box-shadow: none;
}
}
@.is-focus {
& .el-checkbox-button__inner {
border-color: var(--checkbox-button-checked-border-color);
}
}
&:first-child {
.el-checkbox-button__inner {
border-left: var(--border-base);
border-radius: var(--border-radius-base) 0 0 var(--border-radius-base);
box-shadow: none !important;
}
}
&:last-child {
.el-checkbox-button__inner {
border-radius: 0 var(--border-radius-base) var(--border-radius-base) 0;
}
}
@m large {
& .el-checkbox-button__inner {
@mixin button-size var(--button-large-padding-vertical), var(--button-large-padding-horizontal), var(--button-large-font-size), 0;
}
}
@m small {
& .el-checkbox-button__inner {
@mixin button-size var(--button-small-padding-vertical), var(--button-small-padding-horizontal), var(--button-small-font-size), 0;
}
}
@m mini {
& .el-checkbox-button__inner {
@mixin button-size var(--button-mini-padding-vertical), var(--button-mini-padding-horizontal), var(--button-mini-font-size), 0;
}
}
}
}

View File

@ -112,6 +112,13 @@
--checkbox-input-border-color-hover: var(--color-primary);
--checkbox-button-font-size: var(--font-size-base);
--checkbox-button-checked-fill: var(--color-primary);
--checkbox-button-checked-color: var(--color-white);
--checkbox-button-checked-border-color: var(--color-primary);
/* Radio
-------------------------- */
--radio-font-size: 14px;

View File

@ -16,6 +16,7 @@ import Radio from '../packages/radio/index.js';
import RadioGroup from '../packages/radio-group/index.js';
import RadioButton from '../packages/radio-button/index.js';
import Checkbox from '../packages/checkbox/index.js';
import CheckboxButton from '../packages/checkbox-button/index.js';
import CheckboxGroup from '../packages/checkbox-group/index.js';
import Switch from '../packages/switch/index.js';
import Select from '../packages/select/index.js';
@ -82,6 +83,7 @@ const components = [
RadioGroup,
RadioButton,
Checkbox,
CheckboxButton,
CheckboxGroup,
Switch,
Select,
@ -176,6 +178,7 @@ module.exports = {
RadioGroup,
RadioButton,
Checkbox,
CheckboxButton,
CheckboxGroup,
Switch,
Select,

View File

@ -170,4 +170,215 @@ describe('Checkbox', () => {
expect(vm.checked).to.be.true;
expect(vm.checklist.indexOf('a') !== -1).to.be.true;
});
describe('checkbox-button', () => {
let vm;
afterEach(() => {
destroyVM(vm);
});
it('create', done => {
vm = createVue({
template: `
<el-checkbox-button v-model="checked">
</el-checkbox-button>
`,
data() {
return {
checked: false
};
}
}, true);
let checkboxElm = vm.$el;
expect(checkboxElm.classList.contains('el-checkbox-button')).to.be.true;
checkboxElm.click();
vm.$nextTick(_ => {
expect(checkboxElm.classList.contains('is-checked')).to.be.ok;
done();
});
});
it('disabled', () => {
vm = createVue({
template: `
<el-checkbox-button
v-model="checked"
disabled
>
</el-checkbox-button>
`,
data() {
return {
checked: false
};
}
}, true);
let checkboxElm = vm.$el;
expect(checkboxElm.classList.contains('is-disabled')).to.be.ok;
});
it('checkbox group', done => {
vm = createVue({
template: `
<el-checkbox-group v-model="checkList">
<el-checkbox-button label="a" ref="a"></el-checkbox-button>
<el-checkbox-button label="b" ref="b"></el-checkbox-button>
<el-checkbox-button label="c" ref="c"></el-checkbox-button>
<el-checkbox-button label="d" ref="d"></el-checkbox-button>
</el-checkbox-group>
`,
data() {
return {
checkList: []
};
}
}, true);
expect(vm.checkList.length === 0).to.be.true;
vm.$refs.a.$el.click();
vm.$nextTick(_ => {
expect(vm.checkList.indexOf('a') !== -1).to.be.true;
vm.$refs.b.$el.click();
vm.$nextTick(_ => {
expect(vm.checkList.indexOf('a') !== -1).to.be.true;
expect(vm.checkList.indexOf('b') !== -1).to.be.true;
done();
});
});
});
it('checkbox group props', () => {
vm = createVue({
template: `
<el-checkbox-group v-model="checkList" size="large" fill="#FF0000" text-color="#000">
<el-checkbox-button label="a" ref="a"></el-checkbox-button>
<el-checkbox-button label="b" ref="b"></el-checkbox-button>
<el-checkbox-button label="c" ref="c"></el-checkbox-button>
<el-checkbox-button label="d" ref="d"></el-checkbox-button>
</el-checkbox-group>
`,
data() {
return {
checkList: ['a', 'd']
};
}
}, true);
expect(vm.checkList.length === 2).to.be.true;
expect(vm.$refs.a.$el.classList.contains('is-checked')).to.be.true;
expect(vm.$refs.a.$el.classList.contains('el-checkbox-button--large')).to.be.true;
expect(vm.$refs.a.$el.querySelector('.el-checkbox-button__inner').style.backgroundColor).to.be.eql('rgb(255, 0, 0)');
expect(vm.$refs.a.$el.querySelector('.el-checkbox-button__inner').style.boxShadow).to.be.eql('rgb(255, 0, 0) -1px 0px 0px 0px');
expect(vm.$refs.a.$el.querySelector('.el-checkbox-button__inner').style.borderColor).to.be.eql('rgb(255, 0, 0)');
expect(vm.$refs.a.$el.querySelector('.el-checkbox-button__inner').style.color).to.be.eql('rgb(0, 0, 0)');
expect(vm.$refs.b.$el.classList.contains('is-checked')).to.be.false;
expect(vm.$refs.c.$el.classList.contains('is-checked')).to.be.false;
expect(vm.$refs.d.$el.classList.contains('is-checked')).to.be.true;
});
it('checkbox group minimum and maximum', done => {
vm = createVue({
template: `
<el-checkbox-group
v-model="checkList"
:min="1"
:max="2"
>
<el-checkbox-button label="a" ref="a"></el-checkbox-button>
<el-checkbox-button label="b" ref="b"></el-checkbox-button>
<el-checkbox-button label="c" ref="c"></el-checkbox-button>
<el-checkbox-button label="d" ref="d"></el-checkbox-button>
</el-checkbox-group>
`,
data() {
return {
checkList: ['a'],
lastEvent: null
};
}
}, true);
expect(vm.checkList.length === 1).to.be.true;
vm.$refs.a.$el.click();
vm.$nextTick(() => {
expect(vm.checkList.indexOf('a') !== -1).to.be.true;
vm.$refs.b.$el.click();
vm.$nextTick(() => {
expect(vm.checkList.indexOf('a') !== -1).to.be.true;
expect(vm.checkList.indexOf('b') !== -1).to.be.true;
vm.$refs.c.$el.click();
vm.$nextTick(() => {
expect(vm.checkList.indexOf('c') !== -1).to.be.false;
expect(vm.checkList.indexOf('d') !== -1).to.be.false;
done();
});
});
});
});
it('nested group', done => {
vm = createVue({
template: `
<el-checkbox-group v-model="checkList">
<el-row>
<el-checkbox-button label="a" ref="a"></el-checkbox-button>
<el-checkbox-button label="b" ref="b"></el-checkbox-button>
<el-checkbox-button label="c" ref="c"></el-checkbox-button>
<el-checkbox-button label="d" ref="d"></el-checkbox-button>
</el-row>
</el-checkbox-group>
`,
data() {
return {
checkList: []
};
}
}, true);
expect(vm.checkList.length === 0).to.be.true;
vm.$refs.a.$el.click();
vm.$nextTick(_ => {
expect(vm.checkList.indexOf('a') !== -1).to.be.true;
done();
});
});
it('true false label', done => {
vm = createVue({
template: `
<el-checkbox-button
true-label="a"
:false-label="3"
v-model="checked"
></el-checkbox-button>
`,
data() {
return {
checked: 'a'
};
}
}, true);
vm.$el.click();
vm.$nextTick(_ => {
expect(vm.checked === 3).to.be.true;
done();
});
});
it('checked', () => {
vm = createVue({
template: `
<div>
<el-checkbox-button v-model="checked" checked></el-checkbox-button>
<el-checkbox-group v-model="checklist">
<el-checkbox-button checked label="a"></el-checkbox-button>
</el-checkbox-group>
</div>
`,
data() {
return {
checked: false,
checklist: []
};
}
}, true);
expect(vm.checked).to.be.true;
expect(vm.checklist.indexOf('a') !== -1).to.be.true;
});
});
});