Transfer: add chalk theme

pull/7006/head
Leopoldthecoder 2017-09-13 11:12:55 +08:00 committed by 杨奕
parent 9aea008b25
commit 2df521482b
7 changed files with 131 additions and 60 deletions

View File

@ -1,7 +1,7 @@
<style> <style>
.demo-transfer { .demo-transfer {
.transfer-footer { .transfer-footer {
margin-left: 20px; margin-left: 15px;
padding: 6px 5px; padding: 6px 5px;
} }
} }
@ -155,7 +155,7 @@ You can search and filter data items.
You can customize list titles, button texts, render function for data items, checking status texts in list footer and list footer contents. You can customize list titles, button texts, render function for data items, checking status texts in list footer and list footer contents.
:::demo Use `titles`, `button-texts`, `render-content` and `footer-format` to respectively customize list titles, button texts, render function for data items, checking status texts in list footer. For list footer contents, two named slots are provided: `left-footer` and `right-footer`. Plus, if you want some items initially checked, you can use `left-default-checked` and `right-default-checked`. Finally, this example demonstrate the `change` event. Note that this demo can't run in jsfiddle because it doesn't support JSX syntax. In a real project, `render-content` will work if relevant dependencies are correctly configured. :::demo Use `titles`, `button-texts`, `render-content` and `format` to respectively customize list titles, button texts, render function for data items, checking status texts in list header. For list footer contents, two named slots are provided: `left-footer` and `right-footer`. Plus, if you want some items initially checked, you can use `left-default-checked` and `right-default-checked`. Finally, this example demonstrate the `change` event. Note that this demo can't run in jsfiddle because it doesn't support JSX syntax. In a real project, `render-content` will work if relevant dependencies are correctly configured.
```html ```html
<template> <template>
<el-transfer <el-transfer
@ -166,7 +166,7 @@ You can customize list titles, button texts, render function for data items, che
:render-content="renderFunc" :render-content="renderFunc"
:titles="['Source', 'Target']" :titles="['Source', 'Target']"
:button-texts="['To left', 'To right']" :button-texts="['To left', 'To right']"
:footer-format="{ :format="{
noChecked: '${total}', noChecked: '${total}',
hasChecked: '${checked}/${total}' hasChecked: '${checked}/${total}'
}" }"
@ -267,7 +267,7 @@ By default, Transfer looks for `key`, `label` and `disabled` in a data item. If
| titles | custom list titles | array | — | ['List 1', 'List 2'] | | titles | custom list titles | array | — | ['List 1', 'List 2'] |
| button-texts | custom button texts | array | — | [ ] | | button-texts | custom button texts | array | — | [ ] |
| render-content | custom render function for data items | function(h, option) | — | — | | render-content | custom render function for data items | function(h, option) | — | — |
| footer-format | texts for checking status in list footer | object{noChecked, hasChecked} | — | { noChecked: '${total} items', hasChecked: '${checked}/${total} checked' } | | format | texts for checking status in list header | object{noChecked, hasChecked} | — | { noChecked: '${checked}/${total}', hasChecked: '${checked}/${total}' } |
| props | prop aliases for data source | object{key, label, disabled} | — | — | | props | prop aliases for data source | object{key, label, disabled} | — | — |
| left-default-checked | key array of initially checked data items of the left list | array | — | [ ] | | left-default-checked | key array of initially checked data items of the left list | array | — | [ ] |
| right-default-checked | key array of initially checked data items of the right list | array | — | [ ] | | right-default-checked | key array of initially checked data items of the right list | array | — | [ ] |

View File

@ -1,7 +1,7 @@
<style> <style>
.demo-transfer { .demo-transfer {
.transfer-footer { .transfer-footer {
margin-left: 20px; margin-left: 15px;
padding: 6px 5px; padding: 6px 5px;
} }
} }
@ -152,7 +152,7 @@
可以对列表标题文案、按钮文案、数据项的渲染函数、列表底部的勾选状态文案、列表底部的内容区等进行自定义。 可以对列表标题文案、按钮文案、数据项的渲染函数、列表底部的勾选状态文案、列表底部的内容区等进行自定义。
:::demo 可以使用 `titles`、`button-texts`、`render-content` 和 `footer-format` 属性分别对列表标题文案、按钮文案、数据项的渲染函数和列表部的勾选状态文案进行自定义。对于列表底部的内容区,提供了两个具名 slot`left-footer` 和 `right-footer`。此外,如果希望某些数据项在初始化时就被勾选,可以使用 `left-default-checked``right-default-checked` 属性。最后,本例还展示了 `change` 事件的用法。注意:由于 jsfiddle 不支持 JSX 语法,所以本例在 jsfiddle 中无法运行。但是在实际的项目中,只要正确地配置了相关依赖,就可以正常运行。 :::demo 可以使用 `titles`、`button-texts`、`render-content` 和 `format` 属性分别对列表标题文案、按钮文案、数据项的渲染函数和列表部的勾选状态文案进行自定义。对于列表底部的内容区,提供了两个具名 slot`left-footer` 和 `right-footer`。此外,如果希望某些数据项在初始化时就被勾选,可以使用 `left-default-checked``right-default-checked` 属性。最后,本例还展示了 `change` 事件的用法。注意:由于 jsfiddle 不支持 JSX 语法,所以本例在 jsfiddle 中无法运行。但是在实际的项目中,只要正确地配置了相关依赖,就可以正常运行。
```html ```html
<template> <template>
<el-transfer <el-transfer
@ -163,7 +163,7 @@
:render-content="renderFunc" :render-content="renderFunc"
:titles="['Source', 'Target']" :titles="['Source', 'Target']"
:button-texts="['到左边', '到右边']" :button-texts="['到左边', '到右边']"
:footer-format="{ :format="{
noChecked: '${total}', noChecked: '${total}',
hasChecked: '${checked}/${total}' hasChecked: '${checked}/${total}'
}" }"
@ -264,7 +264,7 @@
| titles | 自定义列表标题 | array | — | ['列表 1', '列表 2'] | | titles | 自定义列表标题 | array | — | ['列表 1', '列表 2'] |
| button-texts | 自定义按钮文案 | array | — | [ ] | | button-texts | 自定义按钮文案 | array | — | [ ] |
| render-content | 自定义数据项渲染函数 | function(h, option) | — | — | | render-content | 自定义数据项渲染函数 | function(h, option) | — | — |
| footer-format | 列表底部勾选状态文案 | object{noChecked, hasChecked} | — | { noChecked: '共 ${total} 项', hasChecked: '已选 ${checked}/${total} 项' } | | format | 列表顶部勾选状态文案 | object{noChecked, hasChecked} | — | { noChecked: '${checked}/${total}', hasChecked: '${checked}/${total}' } |
| props | 数据源的字段别名 | object{key, label, disabled} | — | — | | props | 数据源的字段别名 | object{key, label, disabled} | — | — |
| left-default-checked | 初始状态下左侧列表的已勾选项的 key 数组 | array | — | [ ] | | left-default-checked | 初始状态下左侧列表的已勾选项的 key 数组 | array | — | [ ] |
| right-default-checked | 初始状态下右侧列表的已勾选项的 key 数组 | array | — | [ ] | | right-default-checked | 初始状态下右侧列表的已勾选项的 key 数组 | array | — | [ ] |

View File

@ -48,6 +48,10 @@ $--color-text-placeholder: #b4bccc;
$--link-color: $--color-primary-light-2; $--link-color: $--color-primary-light-2;
$--link-hover-color: $--color-primary; $--link-hover-color: $--color-primary;
/* Background
-------------------------- */
$--background-color-base: #f5f7fa;
/* Border /* Border
-------------------------- */ -------------------------- */
$--border-width-base: 1px; $--border-width-base: 1px;
@ -628,16 +632,16 @@ $--collapse-content-color: $--color-text-primary;
/* Transfer /* Transfer
--------------------------*/ --------------------------*/
$--transfer-border-color: $--color-text-regular; $--transfer-border-color: $--border-color-lighter;
$--transfer-box-shadow: $--box-shadow-base; $--transfer-border-radius: $--border-radius-base;
$--transfer-panel-width: 200px; $--transfer-panel-width: 200px;
$--transfer-panel-header-height: 36px; $--transfer-panel-header-height: 40px;
$--transfer-panel-header-background: $--color-text-primary; $--transfer-panel-header-background: $--background-color-base;
$--transfer-panel-footer-height: 36px; $--transfer-panel-footer-height: 40px;
$--transfer-panel-body-height: 246px; $--transfer-panel-body-height: 246px;
$--transfer-item-height: 32px; $--transfer-item-height: 30px;
$--transfer-item-hover-background: $--color-text-secondary; $--transfer-item-hover-background: $--color-text-secondary;
$--transfer-filter-height: 22px; $--transfer-filter-height: 32px;
/* Header /* Header
--------------------------*/ --------------------------*/

View File

@ -12,19 +12,47 @@
@include e(buttons) { @include e(buttons) {
display: inline-block; display: inline-block;
vertical-align: middle; vertical-align: middle;
padding: 0 10px; padding: 0 30px;
}
.el-button { @include e(button) {
display: block; display: block;
margin: 0 auto; margin: 0 auto;
padding: 8px 12px; padding: 10px;
border-radius: 50%;
color: $--color-white;
background-color: $--color-primary;
font-size: 0;
&:first-child { @include when(with-texts) {
margin-bottom: 6px; border-radius: $--border-radius-base;
}
@include when(disabled) {
border: $--border-base;
background-color: $--background-color-base;
color: $--color-text-placeholder;
&:hover {
border: $--border-base;
background-color: $--background-color-base;
color: $--color-text-placeholder;
} }
} }
.el-button [class*="el-icon-"] + span { &:first-child {
margin-bottom: 10px;
}
&:nth-child(2) {
margin: 0;
}
i, span {
font-size: 14px;
}
& [class*="el-icon-"] + span {
margin-left: 0; margin-left: 0;
} }
} }
@ -32,8 +60,9 @@
@include b(transfer-panel) { @include b(transfer-panel) {
border: 1px solid $--transfer-border-color; border: 1px solid $--transfer-border-color;
border-radius: $--transfer-border-radius;
overflow: hidden;
background: $--color-white; background: $--color-white;
box-shadow: $--transfer-box-shadow;
display: inline-block; display: inline-block;
vertical-align: middle; vertical-align: middle;
width: $--transfer-panel-width; width: $--transfer-panel-width;
@ -41,8 +70,11 @@
position: relative; position: relative;
@include e(body) { @include e(body) {
padding-bottom: $--transfer-panel-footer-height;
height: $--transfer-panel-body-height; height: $--transfer-panel-body-height;
@include when(with-footer) {
padding-bottom: $--transfer-panel-footer-height;
}
} }
@include e(list) { @include e(list) {
@ -54,14 +86,15 @@
box-sizing: border-box; box-sizing: border-box;
@include when(filterable) { @include when(filterable) {
height: #{$--transfer-panel-body-height - $--transfer-filter-height - 10px}; height: #{$--transfer-panel-body-height - $--transfer-filter-height - 20px};
padding-top: 0;
} }
} }
@include e(item) { @include e(item) {
height: $--transfer-item-height; height: $--transfer-item-height;
line-height: $--transfer-item-height; line-height: $--transfer-item-height;
padding-left: 20px; padding-left: 15px;
display: block; display: block;
& + .el-transfer-panel__item { & + .el-transfer-panel__item {
@ -69,11 +102,11 @@
} }
&.el-checkbox { &.el-checkbox {
color: $--color-black; color: $--color-text-regular;
} }
&:hover { &:hover {
background: $--transfer-item-hover-background; color: $--color-primary;
} }
&.el-checkbox .el-checkbox__label { &.el-checkbox .el-checkbox__label {
@ -81,19 +114,20 @@
@include utils-ellipsis; @include utils-ellipsis;
display: block; display: block;
box-sizing: border-box; box-sizing: border-box;
padding-left: 28px; padding-left: 24px;
} }
.el-checkbox__input { .el-checkbox__input {
position: absolute; position: absolute;
top: 9px; top: 8px;
} }
} }
@include e(filter) { @include e(filter) {
margin-top: 10px; margin-top: 15px;
margin-bottom: 5px;
text-align: center; text-align: center;
padding: 0 10px; padding: 0 15px;
width: 100%; width: 100%;
box-sizing: border-box; box-sizing: border-box;
@ -102,10 +136,14 @@
width: 100%; width: 100%;
display: inline-block; display: inline-block;
box-sizing: border-box; box-sizing: border-box;
border-radius: #{$--transfer-filter-height / 2};
padding-right: 10px;
padding-left: 30px;
} }
.el-input__icon { .el-input__icon {
right: 10px; right: auto;
left: 15px;
} }
.el-icon-circle-close { .el-icon-circle-close {
@ -118,10 +156,29 @@
line-height: $--transfer-panel-header-height; line-height: $--transfer-panel-header-height;
background: $--transfer-panel-header-background; background: $--transfer-panel-header-background;
margin: 0; margin: 0;
padding-left: 20px; padding-left: 15px;
border-bottom: 1px solid $--transfer-border-color; border-bottom: 1px solid $--transfer-border-color;
box-sizing: border-box; box-sizing: border-box;
color: $--color-black; color: $--color-black;
.el-checkbox {
display: block;
line-height: 40px;
.el-checkbox__label {
font-size: 16px;
color: $--color-text-primary;
font-weight: normal;
span {
float: right;
margin-right: 15px;
color: $--color-text-secondary;
font-size: 12px;
font-weight: normal;
}
}
}
} }
.el-transfer-panel__footer { .el-transfer-panel__footer {
@ -139,7 +196,7 @@
.el-checkbox { .el-checkbox {
padding-left: 20px; padding-left: 20px;
color: $--color-black; color: $--color-text-regular;
} }
} }
@ -147,12 +204,12 @@
margin: 0; margin: 0;
height: $--transfer-item-height; height: $--transfer-item-height;
line-height: $--transfer-item-height; line-height: $--transfer-item-height;
padding: 6px 20px 0; padding: 6px 15px 0;
color: $--color-black; color: $--color-text-secondary;
} }
.el-checkbox__label { .el-checkbox__label {
padding-left: 14px; padding-left: 8px;
} }
.el-checkbox__inner { .el-checkbox__inner {

View File

@ -12,7 +12,7 @@
<div class="el-transfer__buttons"> <div class="el-transfer__buttons">
<el-button <el-button
type="primary" type="primary"
size="small" :class="['el-transfer__button', hasButtonTexts ? 'is-with-texts' : '']"
@click.native="addToLeft" @click.native="addToLeft"
:disabled="rightChecked.length === 0"> :disabled="rightChecked.length === 0">
<i class="el-icon-arrow-left"></i> <i class="el-icon-arrow-left"></i>
@ -20,7 +20,7 @@
</el-button> </el-button>
<el-button <el-button
type="primary" type="primary"
size="small" :class="['el-transfer__button', hasButtonTexts ? 'is-with-texts' : '']"
@click.native="addToRight" @click.native="addToRight"
:disabled="leftChecked.length === 0"> :disabled="leftChecked.length === 0">
<span v-if="buttonTexts[1] !== undefined">{{ buttonTexts[1] }}</span> <span v-if="buttonTexts[1] !== undefined">{{ buttonTexts[1] }}</span>
@ -98,7 +98,7 @@
return []; return [];
} }
}, },
footerFormat: { format: {
type: Object, type: Object,
default() { default() {
return {}; return {};
@ -131,6 +131,10 @@
targetData() { targetData() {
return this.data.filter(item => this.value.indexOf(item[this.props.key]) > -1); return this.data.filter(item => this.value.indexOf(item[this.props.key]) > -1);
},
hasButtonTexts() {
return this.buttonTexts.length === 2;
} }
}, },

View File

@ -1,8 +1,16 @@
<template> <template>
<div class="el-transfer-panel"> <div class="el-transfer-panel">
<p class="el-transfer-panel__header">{{ title }}</p> <p class="el-transfer-panel__header">
<el-checkbox
v-model="allChecked"
@change="handleAllCheckedChange"
:indeterminate="isIndeterminate">
{{ title }}
<span>{{ checkedSummary }}</span>
</el-checkbox>
</p>
<div class="el-transfer-panel__body"> <div :class="['el-transfer-panel__body', hasFooter ? 'is-with-footer' : '']">
<el-input <el-input
class="el-transfer-panel__filter" class="el-transfer-panel__filter"
v-model="query" v-model="query"
@ -34,12 +42,7 @@
class="el-transfer-panel__empty" class="el-transfer-panel__empty"
v-show="data.length === 0 && !hasNoMatch">{{ t('el.transfer.noData') }}</p> v-show="data.length === 0 && !hasNoMatch">{{ t('el.transfer.noData') }}</p>
</div> </div>
<p class="el-transfer-panel__footer" v-if="hasFooter">
<p class="el-transfer-panel__footer">
<el-checkbox
v-model="allChecked"
@change="handleAllCheckedChange"
:indeterminate="isIndeterminate">{{ checkedSummary }}</el-checkbox>
<slot></slot> <slot></slot>
</p> </p>
</div> </div>
@ -95,7 +98,7 @@
placeholder: String, placeholder: String,
title: String, title: String,
filterable: Boolean, filterable: Boolean,
footerFormat: Object, format: Object,
filterMethod: Function, filterMethod: Function,
defaultChecked: Array, defaultChecked: Array,
props: Object props: Object
@ -167,15 +170,13 @@
checkedSummary() { checkedSummary() {
const checkedLength = this.checked.length; const checkedLength = this.checked.length;
const dataLength = this.data.length; const dataLength = this.data.length;
const { noChecked, hasChecked } = this.footerFormat; const { noChecked, hasChecked } = this.format;
if (noChecked && hasChecked) { if (noChecked && hasChecked) {
return checkedLength > 0 return checkedLength > 0
? hasChecked.replace(/\${checked}/g, checkedLength).replace(/\${total}/g, dataLength) ? hasChecked.replace(/\${checked}/g, checkedLength).replace(/\${total}/g, dataLength)
: noChecked.replace(/\${total}/g, dataLength); : noChecked.replace(/\${total}/g, dataLength);
} else { } else {
return checkedLength > 0 return `${ checkedLength }/${ dataLength }`;
? this.t('el.transfer.hasCheckedFormat', { total: dataLength, checked: checkedLength })
: this.t('el.transfer.noCheckedFormat', { total: dataLength });
} }
}, },
@ -204,6 +205,10 @@
disabledProp() { disabledProp() {
return this.props.disabled || 'disabled'; return this.props.disabled || 'disabled';
},
hasFooter() {
return !!this.$slots.default;
} }
}, },

View File

@ -91,11 +91,11 @@ describe('Transfer', () => {
}); });
it('customize', () => { it('customize', () => {
vm = createTransfer('v-model="value" :titles="titles" :render-content="renderFunc" :footer-format="format"', { vm = createTransfer('v-model="value" :titles="titles" :render-content="renderFunc" :format="format"', {
data() { data() {
return { return {
value: [2], value: [2],
titles: ['1', '2'], titles: ['1', '2'],
format: { noChecked: 'no', hasChecked: 'has' }, format: { noChecked: 'no', hasChecked: 'has' },
renderFunc(h, option) { renderFunc(h, option) {
return <span>{ option.key } - { option.label }</span>; return <span>{ option.key } - { option.label }</span>;
@ -104,9 +104,10 @@ describe('Transfer', () => {
} }
}); });
const transfer = vm.$refs.transfer.$el; const transfer = vm.$refs.transfer.$el;
expect(transfer.querySelector('.el-transfer-panel__header').innerText).to.equal('1'); const label = transfer.querySelector('.el-transfer-panel__header .el-checkbox__label');
expect(transfer.querySelector('.el-checkbox__label span').innerText).to.equal('1 - 备选项 1'); expect(label.innerText.indexOf('表1') > -1).to.true;
expect(transfer.querySelector('.el-transfer-panel__footer .el-checkbox__label').innerText).to.equal('no'); expect(transfer.querySelector('.el-transfer-panel__list .el-checkbox__label span').innerText).to.equal('1 - 备选项 1');
expect(label.querySelector('span').innerText).to.equal('no');
}); });
it('check', () => { it('check', () => {