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="单选模式">
<van-tree-select
content-item-class="content-item-class"
items="{{ items }}"
main-active-index="{{ mainActiveIndex }}"
active-id="{{ activeId }}"
@ -11,7 +10,6 @@
<demo-block title="多选模式">
<van-tree-select
content-item-class="content-item-class"
max="2"
items="{{ items }}"
main-active-index="{{ mainActiveIndexMulti }}"
@ -23,13 +21,26 @@
<demo-block title="自定义内容">
<van-tree-select
content-item-class="content-item-class"
items="{{ items }}"
main-active-index="{{ mainActiveIndexMulti }}"
active-id="{{ activeIdMulti }}"
bind:click-item="onClickItemMulti"
bind:click-nav="onClickNavMulti"
items="{{ [{ text: '分组 1' }, { text: '分组 2' }] }}"
height="55vw"
main-active-index="{{ mainActiveIndex }}"
active-id="{{ activeId }}"
bind:click-item="onClickItem"
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>
</demo-block>

View File

@ -282,6 +282,22 @@
// Search
@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-active-color: #e8e8e8;
@stepper-background-color: @active-color;
@ -346,6 +362,15 @@
@divider-content-left-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-size: 80px;
@uploader-icon-size: 24px;

View File

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

View File

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

View File

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

View File

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

View File

@ -1,7 +1,7 @@
<wxs src="../wxs/utils.wxs" module="utils" />
<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-stay-time="70"
bind:tap="onClick"

View File

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

View File

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

View File

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

View File

@ -1,7 +1,6 @@
import { VantComponent } from '../common/component';
import { Weapp } from 'definitions/weapp';
const ITEM_HEIGHT = 44;
import { addUnit } from '../common/utils';
VantComponent({
classes: [
@ -14,15 +13,20 @@ VantComponent({
],
props: {
items: Array,
items: {
type: Array,
observer: 'updateSubItems'
},
activeId: null,
mainActiveIndex: {
type: Number,
value: 0
value: 0,
observer: 'updateSubItems'
},
maxHeight: {
type: Number,
value: 300
height: {
type: [Number, String],
value: 300,
observer: 'updateHeight'
},
max: {
type: Number,
@ -31,24 +35,11 @@ VantComponent({
},
data: {
subItems: [],
mainHeight: 0,
itemHeight: 0
subItems: []
},
watch: {
items() {
this.updateSubItems().then(() => {
this.updateMainHeight();
});
},
maxHeight() {
this.updateItemHeight(this.data.subItems);
this.updateMainHeight();
},
mainActiveIndex: 'updateSubItems'
created() {
this.updateHeight();
},
methods: {
@ -57,9 +48,11 @@ VantComponent({
const { item } = event.currentTarget.dataset;
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)) {
this.$emit('click-item', item);
@ -68,7 +61,7 @@ VantComponent({
// 当一个导航被点击时
onClickNav(event: Weapp.Event) {
const { index } = event.currentTarget.dataset;
const index = event.detail;
const item = this.data.items[index];
if (!item.disabled) {
this.$emit('click-nav', { index });
@ -80,26 +73,13 @@ VantComponent({
const { items, mainActiveIndex } = this.data;
const { children = [] } = items[mainActiveIndex] || {};
this.updateItemHeight(children);
return this.set({ subItems: children });
},
// 更新组件整体高度,根据最大高度和当前组件需要展示的高度来决定
updateMainHeight() {
const { items = [], subItems = [] } = this.data;
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 });
updateHeight() {
this.setData({
innerHeight: addUnit(this.data.height)
});
}
}
});

View File

@ -3,29 +3,27 @@
<view
class="van-tree-select"
style="height: {{ mainHeight }}px"
style="height: {{ innerHeight }}"
>
<scroll-view scroll-y class="van-tree-select__nav">
<view
wx:for="{{ items }}"
wx:key="index"
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' : '' }}"
data-index="{{ index }}"
bind:tap="onClickNav"
>
{{ item.text }}
</view>
<van-sidebar bind:change="onClickNav" custom-class="van-tree-select__nav__inner">
<van-sidebar-item
wx:for="{{ items }}"
wx:key="index"
custom-class="main-item-class"
active-class="main-active-class"
disabled-class="main-disabled-class"
title="{{ item.text }}"
disabled="{{ item.disabled }}"
/>
</van-sidebar>
</scroll-view>
<scroll-view
scroll-y
class="van-tree-select__content"
style="height: {{ itemHeight }}px"
>
<scroll-view scroll-y class="van-tree-select__content">
<slot name="content" />
<view
wx:for="{{ subItems }}"
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 }}"
bind:tap="onSelectItem"
>