refactor(TreeSelect): refactor with sidebar (#2224)

BREAKING CHANGE: rename prop active of sidebar to activeKey
This commit is contained in:
rex 2019-11-01 16:14:52 +08:00 committed by GitHub
parent 50f7540a4e
commit e1f80b569d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 164 additions and 154 deletions

View File

@ -1,6 +1,5 @@
<demo-block title="单选模式"> <demo-block title="单选模式">
<van-tree-select <van-tree-select
content-item-class="content-item-class"
items="{{ items }}" items="{{ items }}"
main-active-index="{{ mainActiveIndex }}" main-active-index="{{ mainActiveIndex }}"
active-id="{{ activeId }}" active-id="{{ activeId }}"
@ -11,7 +10,6 @@
<demo-block title="多选模式"> <demo-block title="多选模式">
<van-tree-select <van-tree-select
content-item-class="content-item-class"
max="2" max="2"
items="{{ items }}" items="{{ items }}"
main-active-index="{{ mainActiveIndexMulti }}" main-active-index="{{ mainActiveIndexMulti }}"
@ -23,13 +21,26 @@
<demo-block title="自定义内容"> <demo-block title="自定义内容">
<van-tree-select <van-tree-select
content-item-class="content-item-class" items="{{ [{ text: '分组 1' }, { text: '分组 2' }] }}"
items="{{ items }}" height="55vw"
main-active-index="{{ mainActiveIndexMulti }}" main-active-index="{{ mainActiveIndex }}"
active-id="{{ activeIdMulti }}" active-id="{{ activeId }}"
bind:click-item="onClickItemMulti" bind:click-item="onClickItem"
bind:click-nav="onClickNavMulti" bind:click-nav="onClickNav"
> >
<image src="https://img.yzcdn.cn/vant/apple-1.jpg" slot="content" /> <van-image
wx:if="{{ mainActiveIndex === 0 }}"
src="https://img.yzcdn.cn/vant/apple-1.jpg"
width="100%"
height="100%"
slot="content"
/>
<van-image
wx:elif="{{ mainActiveIndex === 1 }}"
src="https://img.yzcdn.cn/vant/apple-2.jpg"
width="100%"
height="100%"
slot="content"
/>
</van-tree-select> </van-tree-select>
</demo-block> </demo-block>

View File

@ -282,6 +282,22 @@
// Search // Search
@search-background-color: #f7f8fA; @search-background-color: #f7f8fA;
// Sidebar
@sidebar-width: 85px;
// SidebarItem
@sidebar-font-size: @font-size-md;
@sidebar-line-height: 20px;
@sidebar-text-color: @text-color;
@sidebar-disabled-text-color: @gray;
@sidebar-padding: 20px @padding-sm 20px @padding-xs;
@sidebar-active-color: @active-color;
@sidebar-background-color: @background-color-light;
@sidebar-selected-font-weight: @font-weight-bold;
@sidebar-selected-text-color: @text-color;
@sidebar-selected-border-color: @red;
@sidebar-selected-background-color: @white;
// Stepper // Stepper
@stepper-active-color: #e8e8e8; @stepper-active-color: #e8e8e8;
@stepper-background-color: @active-color; @stepper-background-color: @active-color;
@ -346,6 +362,15 @@
@divider-content-left-width: 10%; @divider-content-left-width: 10%;
@divider-content-right-width: 10%; @divider-content-right-width: 10%;
// TreeSelect
@tree-select-font-size: @font-size-md;
@tree-select-nav-background-color: @background-color-light;
@tree-select-content-background-color: @white;
@tree-select-nav-item-padding: @padding-sm @padding-xs @padding-sm @padding-sm;
@tree-select-item-height: 44px;
@tree-select-item-active-color: @red;
@tree-select-item-disabled-color: @gray;
// Uploader // Uploader
@uploader-size: 80px; @uploader-size: 80px;
@uploader-icon-size: 24px; @uploader-icon-size: 24px;

View File

@ -1,6 +1,12 @@
@import '../common/style/var.less'; @import '../common/style/var.less';
@import '@vant/icons/src/index.less'; @import '@vant/icons/src/index.less';
:host {
display: flex;
align-items: center;
justify-content: center;
}
.van-icon { .van-icon {
&--image { &--image {
width: 1em; width: 1em;

View File

@ -16,7 +16,7 @@
bind:error="onError" bind:error="onError"
/> />
<div <view
wx:if="{{ loading && showLoading }}" wx:if="{{ loading && showLoading }}"
class="loading-class van-image__loading" class="loading-class van-image__loading"
> >
@ -29,8 +29,8 @@
name="photo-o" name="photo-o"
size="22" size="22"
/> />
</div> </view>
<div <view
wx:if="{{ error && showError }}" wx:if="{{ error && showError }}"
class="error-class van-image__error" class="error-class van-image__error"
> >
@ -43,5 +43,5 @@
name="warning-o" name="warning-o"
size="22" size="22"
/> />
</div> </view>
</view> </view>

View File

@ -4,40 +4,45 @@
.van-sidebar-item { .van-sidebar-item {
display: block; display: block;
box-sizing: border-box; box-sizing: border-box;
padding: 20px 12px 20px 9px;
overflow: hidden; overflow: hidden;
font-size: 14px;
line-height: 1.4;
word-wrap: break-word; word-wrap: break-word;
border-left: 3px solid transparent; border-left: 3px solid transparent;
user-select: none; user-select: none;
.theme(color, '@gray-darker'); .theme(padding, '@sidebar-padding');
.theme(background-color, '@background-color'); .theme(font-size, '@sidebar-font-size');
.theme(line-height, '@sidebar-line-height');
.theme(color, '@sidebar-text-color');
.theme(background-color, '@sidebar-background-color');
&--hover { &__text {
.theme(background-color, '@active-color'); position: relative;
display: inline-block;
}
&--hover:not(&--disabled) {
.theme(background-color, '@sidebar-active-color');
} }
&::after { &::after {
border-bottom-width: 1px; border-bottom-width: 1px;
} }
&--active { &--selected {
font-weight: bold; .theme(color, '@sidebar-selected-text-color');
.theme(color, '@text-color'); .theme(font-weight, '@sidebar-selected-font-weight');
.theme(border-color, '@red'); .theme(border-color, '@sidebar-selected-border-color');
&::after { &::after {
border-right-width: 1px; border-right-width: 1px;
} }
} }
&--active, &--selected,
&--active&--hover { &--selected&--hover {
.theme(background-color, '@white'); .theme(background-color, '@sidebar-selected-background-color');
} }
&__text { &--disabled {
position: relative; .theme(color, '@sidebar-disabled-text-color');
} }
} }

View File

@ -1,6 +1,11 @@
import { VantComponent } from '../common/component'; import { VantComponent } from '../common/component';
VantComponent({ VantComponent({
classes: [
'active-class',
'disabled-class',
],
relation: { relation: {
type: 'ancestor', type: 'ancestor',
name: 'sidebar', name: 'sidebar',
@ -12,18 +17,19 @@ VantComponent({
props: { props: {
dot: Boolean, dot: Boolean,
info: null, info: null,
title: String title: String,
disabled: Boolean
}, },
methods: { methods: {
onClick() { onClick() {
const { parent } = this; const { parent } = this;
if (!parent) { if (!parent || this.data.disabled) {
return; return;
} }
const index = parent.items.indexOf(this); const index = parent.children.indexOf(this);
parent.setActive(index).then(() => { parent.setActive(index).then(() => {
this.$emit('click', index); this.$emit('click', index);
@ -31,8 +37,8 @@ VantComponent({
}); });
}, },
setActive(active: boolean) { setActive(selected: boolean) {
return this.setData({ active }); return this.setData({ selected });
} }
} }
}); });

View File

@ -1,7 +1,7 @@
<wxs src="../wxs/utils.wxs" module="utils" /> <wxs src="../wxs/utils.wxs" module="utils" />
<view <view
class="{{ utils.bem('sidebar-item', { active }) }} van-hairline custom-class" class="{{ utils.bem('sidebar-item', { selected, disabled }) }} {{ selected ? 'active-class' : '' }} {{ disabled ? 'disabled-class' : '' }} custom-class"
hover-class="van-sidebar-item--hover" hover-class="van-sidebar-item--hover"
hover-stay-time="70" hover-stay-time="70"
bind:tap="onClick" bind:tap="onClick"

View File

@ -1,5 +1,6 @@
@import '../common/style/var.less'; @import '../common/style/var.less';
@import '../common/style/theme.less';
.van-sidebar { .van-sidebar {
width: 85px; .theme(width, '@sidebar-width');
} }

View File

@ -5,17 +5,19 @@ VantComponent({
name: 'sidebar-item', name: 'sidebar-item',
type: 'descendant', type: 'descendant',
linked(target) { linked(target) {
this.items.push(target); this.children.push(target);
this.setActive(this.data.active); this.setActive(this.data.activeKey);
}, },
unlinked(target) { unlinked(target) {
this.items = this.items.filter(item => item !== target); this.items = this.children.filter(
this.setActive(this.data.active); (item: WechatMiniprogram.Component.TrivialInstance) => item !== target
);
this.setActive(this.data.activeKey);
} }
}, },
props: { props: {
active: { activeKey: {
type: Number, type: Number,
value: 0, value: 0,
observer: 'setActive' observer: 'setActive'
@ -23,28 +25,28 @@ VantComponent({
}, },
beforeCreate() { beforeCreate() {
this.items = []; this.children = [];
this.currentActive = -1; this.currentActive = -1;
}, },
methods: { methods: {
setActive(active: number) { setActive(activeKey: number) {
const { items, currentActive } = this; const { children, currentActive } = this;
if (!items.length) { if (!children.length) {
return Promise.resolve(); return Promise.resolve();
} }
this.currentActive = active; this.currentActive = activeKey;
const stack = []; const stack = [];
if (currentActive !== active && items[currentActive]) { if (currentActive !== activeKey && children[currentActive]) {
stack.push(items[currentActive].setActive(false)); stack.push(children[currentActive].setActive(false));
} }
if (items[active]) { if (children[activeKey]) {
stack.push(items[active].setActive(true)); stack.push(children[activeKey].setActive(true));
} }
return Promise.all(stack); return Promise.all(stack);

View File

@ -1,6 +1,8 @@
{ {
"component": true, "component": true,
"usingComponents": { "usingComponents": {
"van-icon": "../icon/index" "van-icon": "../icon/index",
"van-sidebar": "../sidebar/index",
"van-sidebar-item": "../sidebar-item/index"
} }
} }

View File

@ -3,73 +3,47 @@
.van-tree-select { .van-tree-select {
position: relative; position: relative;
font-size: 14px; display: flex;
user-select: none; user-select: none;
.theme(font-size, '@tree-select-font-size');
&__nav { &__nav {
position: absolute; flex: 1;
top: 0; .theme(background-color, '@tree-select-nav-background-color');
bottom: 0;
left: 0;
width: 35%;
min-width: 120px;
.theme(background-color, '@background-color-light');
}
&__nitem { &__inner {
position: relative; width: 100% !important;
padding: 0 9px 0 15px; height: 100%;
line-height: 44px;
&--active::after {
position: absolute;
top: 0;
bottom: 0;
left: 0;
width: 3.6px;
content: '';
.theme(background-color, '@red');
} }
&--active { --sidebar-padding: @tree-select-nav-item-padding;
font-weight: bold;
.theme(background-color, '@white');
}
&--disabled {
.theme(color, '@gray-dark');
}
} }
&__content { &__content {
box-sizing: border-box; flex: 2;
width: 65%; .theme(background-color, '@tree-select-content-background-color');
margin-left: 35%;
padding-left: 15px;
.theme(background-color, '@white');
} }
&__item { &__item {
position: relative; position: relative;
font-weight: bold; font-weight: bold;
line-height: 44px;
.theme(padding, '0 32px 0 @padding-md');
.theme(line-height, '@tree-select-item-height');
&--active { &--active {
.theme(color, '@red'); .theme(color, '@tree-select-item-active-color');
} }
&--disabled { &--disabled {
.theme(color, '@gray-dark'); .theme(color, '@tree-select-item-disabled-color');
} }
} }
&__selected { &__selected {
position: absolute; position: absolute;
top: 0; top: 50%;
right: 15px; transform: translateY(-50%);
bottom: 0; .theme(right, '@padding-md');
height: 24px;
margin: auto 0;
line-height: 24px;
} }
} }

View File

@ -1,7 +1,6 @@
import { VantComponent } from '../common/component'; import { VantComponent } from '../common/component';
import { Weapp } from 'definitions/weapp'; import { Weapp } from 'definitions/weapp';
import { addUnit } from '../common/utils';
const ITEM_HEIGHT = 44;
VantComponent({ VantComponent({
classes: [ classes: [
@ -14,15 +13,20 @@ VantComponent({
], ],
props: { props: {
items: Array, items: {
type: Array,
observer: 'updateSubItems'
},
activeId: null, activeId: null,
mainActiveIndex: { mainActiveIndex: {
type: Number, type: Number,
value: 0 value: 0,
observer: 'updateSubItems'
}, },
maxHeight: { height: {
type: Number, type: [Number, String],
value: 300 value: 300,
observer: 'updateHeight'
}, },
max: { max: {
type: Number, type: Number,
@ -31,24 +35,11 @@ VantComponent({
}, },
data: { data: {
subItems: [], subItems: []
mainHeight: 0,
itemHeight: 0
}, },
watch: { created() {
items() { this.updateHeight();
this.updateSubItems().then(() => {
this.updateMainHeight();
});
},
maxHeight() {
this.updateItemHeight(this.data.subItems);
this.updateMainHeight();
},
mainActiveIndex: 'updateSubItems'
}, },
methods: { methods: {
@ -57,9 +48,11 @@ VantComponent({
const { item } = event.currentTarget.dataset; const { item } = event.currentTarget.dataset;
const isArray = Array.isArray(this.data.activeId); const isArray = Array.isArray(this.data.activeId);
// 判断有没有超出右侧选择的最大数 // 判断有没有超出右侧选择的最大数
const isOverMax = isArray && (this.data.activeId.length >= this.data.max); const isOverMax = isArray && this.data.activeId.length >= this.data.max;
// 判断该项有没有被选中, 如果有被选中,则忽视是否超出的条件 // 判断该项有没有被选中, 如果有被选中,则忽视是否超出的条件
const isSelected = isArray ? this.data.activeId.indexOf(item.id) > -1 : this.data.activeId === item.id; const isSelected = isArray
? this.data.activeId.indexOf(item.id) > -1
: this.data.activeId === item.id;
if (!item.disabled && (!isOverMax || isSelected)) { if (!item.disabled && (!isOverMax || isSelected)) {
this.$emit('click-item', item); this.$emit('click-item', item);
@ -68,7 +61,7 @@ VantComponent({
// 当一个导航被点击时 // 当一个导航被点击时
onClickNav(event: Weapp.Event) { onClickNav(event: Weapp.Event) {
const { index } = event.currentTarget.dataset; const index = event.detail;
const item = this.data.items[index]; const item = this.data.items[index];
if (!item.disabled) { if (!item.disabled) {
this.$emit('click-nav', { index }); this.$emit('click-nav', { index });
@ -80,26 +73,13 @@ VantComponent({
const { items, mainActiveIndex } = this.data; const { items, mainActiveIndex } = this.data;
const { children = [] } = items[mainActiveIndex] || {}; const { children = [] } = items[mainActiveIndex] || {};
this.updateItemHeight(children);
return this.set({ subItems: children }); return this.set({ subItems: children });
}, },
// 更新组件整体高度,根据最大高度和当前组件需要展示的高度来决定 updateHeight() {
updateMainHeight() { this.setData({
const { items = [], subItems = [] } = this.data; innerHeight: addUnit(this.data.height)
const maxHeight = Math.max( });
items.length * ITEM_HEIGHT,
subItems.length * ITEM_HEIGHT
);
this.setData({ mainHeight: Math.min(maxHeight, this.data.maxHeight) });
},
// 更新子项列表高度,根据可展示的最大高度和当前子项列表的高度决定
updateItemHeight(subItems) {
const itemHeight = Math.min(subItems.length * ITEM_HEIGHT, this.data.maxHeight);
return this.setData({ itemHeight });
} }
} }
}); });

View File

@ -3,29 +3,27 @@
<view <view
class="van-tree-select" class="van-tree-select"
style="height: {{ mainHeight }}px" style="height: {{ innerHeight }}"
> >
<scroll-view scroll-y class="van-tree-select__nav"> <scroll-view scroll-y class="van-tree-select__nav">
<view <van-sidebar bind:change="onClickNav" custom-class="van-tree-select__nav__inner">
wx:for="{{ items }}" <van-sidebar-item
wx:key="index" wx:for="{{ items }}"
class="van-ellipsis main-item-class {{ utils.bem('tree-select__nitem', { active: mainActiveIndex === index, disabled: item.disabled }) }} {{ mainActiveIndex === index ? 'main-active-class' : '' }} {{ item.disabled ? 'main-disabled-class' : '' }}" wx:key="index"
data-index="{{ index }}" custom-class="main-item-class"
bind:tap="onClickNav" active-class="main-active-class"
> disabled-class="main-disabled-class"
{{ item.text }} title="{{ item.text }}"
</view> disabled="{{ item.disabled }}"
/>
</van-sidebar>
</scroll-view> </scroll-view>
<scroll-view <scroll-view scroll-y class="van-tree-select__content">
scroll-y
class="van-tree-select__content"
style="height: {{ itemHeight }}px"
>
<slot name="content" /> <slot name="content" />
<view <view
wx:for="{{ subItems }}" wx:for="{{ subItems }}"
wx:key="id" wx:key="id"
class="van-ellipsis van-hairline--bottom content-item-class {{ utils.bem('tree-select__item', { active: wxs.isActive(activeId, item.id), disabled: item.disabled }) }} {{ wxs.isActive(activeId, item.id) ? 'content-active-class' : '' }} {{ item.disabled ? 'content-disabled-class' : '' }}" class="van-ellipsis content-item-class {{ utils.bem('tree-select__item', { active: wxs.isActive(activeId, item.id), disabled: item.disabled }) }} {{ wxs.isActive(activeId, item.id) ? 'content-active-class' : '' }} {{ item.disabled ? 'content-disabled-class' : '' }}"
data-item="{{ item }}" data-item="{{ item }}"
bind:tap="onSelectItem" bind:tap="onSelectItem"
> >