From 0bf50bfc2e89ec3e2d0c7abaca9c3b90f1c42484 Mon Sep 17 00:00:00 2001 From: Mathieu DARTIGUES Date: Fri, 21 Apr 2017 10:12:38 +0200 Subject: [PATCH] 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... --- components.json | 1 + examples/docs/en-US/checkbox.md | 46 ++++- examples/docs/zh-CN/checkbox.md | 51 +++++- packages/checkbox-button/index.js | 8 + packages/checkbox/src/checkbox-button.vue | 161 +++++++++++++++++ packages/checkbox/src/checkbox-group.vue | 5 +- packages/theme-default/src/checkbox.css | 104 +++++++++++ packages/theme-default/src/common/var.css | 7 + src/index.js | 3 + test/unit/specs/checkbox.spec.js | 211 ++++++++++++++++++++++ 10 files changed, 591 insertions(+), 6 deletions(-) create mode 100644 packages/checkbox-button/index.js create mode 100644 packages/checkbox/src/checkbox-button.vue diff --git a/components.json b/components.json index bddb07f05..43f323a71 100644 --- a/components.json +++ b/components.json @@ -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", diff --git a/examples/docs/en-US/checkbox.md b/examples/docs/en-US/checkbox.md index 2125f931b..47dcdc387 100644 --- a/examples/docs/en-US/checkbox.md +++ b/examples/docs/en-US/checkbox.md @@ -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 `` element into `` element. We also provide `size` attribute for these buttons: `large` and `small`. +```html + + +``` +::: + ### 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 | — | — | diff --git a/examples/docs/zh-CN/checkbox.md b/examples/docs/zh-CN/checkbox.md index 1a5d36fb9..72d84bd5b 100644 --- a/examples/docs/zh-CN/checkbox.md +++ b/examples/docs/zh-CN/checkbox.md @@ -13,7 +13,10 @@ cities: cityOptions, checkedCities: ['上海', '北京'], checkedCities1: ['上海', '北京'], - isIndeterminate: true + isIndeterminate: true, + checkboxGroup1: ['上海'], + checkboxGroup2: ['北京'], + checkboxGroup3: ['广州'] }; }, methods: { @@ -191,7 +194,46 @@ }; ``` + ::: + +### Button style (to be translated) + +Checkbox with button styles. + +:::demo 只需要把`el-checkbox`元素换成`el-checkbox-button`元素即可,此外,Element 还提供了`size`属性给按钮组,支持`large`和`small`两种(如果不设定为默认) +```html + + +``` + +::: + ### 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 | — | — | diff --git a/packages/checkbox-button/index.js b/packages/checkbox-button/index.js new file mode 100644 index 000000000..e878f649c --- /dev/null +++ b/packages/checkbox-button/index.js @@ -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; diff --git a/packages/checkbox/src/checkbox-button.vue b/packages/checkbox/src/checkbox-button.vue new file mode 100644 index 000000000..e96a848d5 --- /dev/null +++ b/packages/checkbox/src/checkbox-button.vue @@ -0,0 +1,161 @@ + + diff --git a/packages/checkbox/src/checkbox-group.vue b/packages/checkbox/src/checkbox-group.vue index d40bef2d2..4e4835473 100644 --- a/packages/checkbox/src/checkbox-group.vue +++ b/packages/checkbox/src/checkbox-group.vue @@ -11,7 +11,10 @@ props: { value: {}, min: Number, - max: Number + max: Number, + size: String, + fill: String, + textColor: String }, watch: { diff --git a/packages/theme-default/src/checkbox.css b/packages/theme-default/src/checkbox.css index bd335b246..40528717b 100644 --- a/packages/theme-default/src/checkbox.css +++ b/packages/theme-default/src/checkbox.css @@ -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; + } + } + } } diff --git a/packages/theme-default/src/common/var.css b/packages/theme-default/src/common/var.css index 682757e51..4b0f24165 100644 --- a/packages/theme-default/src/common/var.css +++ b/packages/theme-default/src/common/var.css @@ -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; diff --git a/src/index.js b/src/index.js index 82096b035..5a627a811 100644 --- a/src/index.js +++ b/src/index.js @@ -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, diff --git a/test/unit/specs/checkbox.spec.js b/test/unit/specs/checkbox.spec.js index 5035559b1..1337535bf 100644 --- a/test/unit/specs/checkbox.spec.js +++ b/test/unit/specs/checkbox.spec.js @@ -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: ` + + + `, + 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: ` + + + `, + 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: ` + + + + + + + `, + 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: ` + + + + + + + `, + 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: ` + + + + + + + `, + 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: ` + + + + + + + + + `, + 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: ` + + `, + data() { + return { + checked: 'a' + }; + } + }, true); + vm.$el.click(); + vm.$nextTick(_ => { + expect(vm.checked === 3).to.be.true; + done(); + }); + }); + it('checked', () => { + vm = createVue({ + template: ` +
+ + + + +
+ `, + data() { + return { + checked: false, + checklist: [] + }; + } + }, true); + expect(vm.checked).to.be.true; + expect(vm.checklist.indexOf('a') !== -1).to.be.true; + }); + + }); });