mirror of
https://gitee.com/vant-contrib/vant-weapp.git
synced 2025-04-05 19:41:45 +08:00
fix(DropdownMenu): support set props dynamic & improve performance (#2454)
fix #2446
This commit is contained in:
parent
baac222b2f
commit
52e21213ae
@ -9,6 +9,7 @@ VantComponent({
|
||||
type: 'ancestor',
|
||||
linked(target) {
|
||||
this.parent = target;
|
||||
this.updateDataFromParent();
|
||||
},
|
||||
unlinked() {
|
||||
this.parent = null;
|
||||
@ -16,13 +17,23 @@ VantComponent({
|
||||
},
|
||||
|
||||
props: {
|
||||
value: null,
|
||||
title: String,
|
||||
value: {
|
||||
type: null,
|
||||
observer: 'rerender'
|
||||
},
|
||||
title: {
|
||||
type: String,
|
||||
observer: 'rerender'
|
||||
},
|
||||
disabled: Boolean,
|
||||
titleClass: String,
|
||||
titleClass: {
|
||||
type: String,
|
||||
observer: 'rerender'
|
||||
},
|
||||
options: {
|
||||
type: Array,
|
||||
value: []
|
||||
value: [],
|
||||
observer: 'rerender'
|
||||
}
|
||||
},
|
||||
|
||||
@ -33,21 +44,30 @@ VantComponent({
|
||||
displayTitle: ''
|
||||
},
|
||||
|
||||
created() {
|
||||
this.setData({ displayTitle: this.computedDisplayTitle(this.data.value) });
|
||||
},
|
||||
|
||||
methods: {
|
||||
computedDisplayTitle(curValue) {
|
||||
const { title, options } = this.data;
|
||||
rerender() {
|
||||
wx.nextTick(() => {
|
||||
this.parent && this.parent.updateItemListData();
|
||||
});
|
||||
},
|
||||
|
||||
if (title) {
|
||||
return title;
|
||||
updateDataFromParent() {
|
||||
if (this.parent) {
|
||||
const {
|
||||
overlay,
|
||||
duration,
|
||||
activeColor,
|
||||
closeOnClickOverlay,
|
||||
direction
|
||||
} = this.parent.data;
|
||||
this.setData({
|
||||
overlay,
|
||||
duration,
|
||||
activeColor,
|
||||
closeOnClickOverlay,
|
||||
direction
|
||||
});
|
||||
}
|
||||
|
||||
const match = options.filter(option => option.value === curValue);
|
||||
const displayTitle = match.length ? match[0].text : '';
|
||||
return displayTitle;
|
||||
},
|
||||
|
||||
onClickOverlay() {
|
||||
@ -56,30 +76,55 @@ VantComponent({
|
||||
},
|
||||
|
||||
onOptionTap(event: Weapp.Event) {
|
||||
let { value, displayTitle } = this.data;
|
||||
const { option } = event.currentTarget.dataset;
|
||||
const { value: optionValue } = option;
|
||||
const { value } = option;
|
||||
|
||||
if (optionValue !== value) {
|
||||
value = optionValue;
|
||||
displayTitle = this.computedDisplayTitle(optionValue);
|
||||
this.$emit('change', optionValue);
|
||||
}
|
||||
this.setData({ showPopup: false, value, displayTitle });
|
||||
const shouldEmitChange = this.data.value !== value;
|
||||
this.setData({ showPopup: false, value });
|
||||
|
||||
const time = this.data.duration || 0;
|
||||
setTimeout(() => {
|
||||
this.setData({ showWrapper: false });
|
||||
}, time);
|
||||
}, this.data.duration || 0);
|
||||
|
||||
// parent 中的 itemListData 是 children 上的数据的集合
|
||||
// 数据的更新由 children 各自维护,但是模板的更新需要额外触发 parent 的 setData
|
||||
this.parent.setData({ itemListData: this.parent.data.itemListData });
|
||||
this.rerender();
|
||||
|
||||
if (shouldEmitChange) {
|
||||
this.$emit('change', value);
|
||||
}
|
||||
},
|
||||
|
||||
toggle() {
|
||||
const { childIndex } = this.data;
|
||||
this.parent.toggleItem(childIndex);
|
||||
toggle(show, options = {}) {
|
||||
const { showPopup, duration } = this.data;
|
||||
|
||||
if (show == null) {
|
||||
show = !showPopup;
|
||||
}
|
||||
|
||||
if (show === showPopup) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!show) {
|
||||
const time = options.immediate ? 0 : duration;
|
||||
this.setData({ transition: !options.immediate, showPopup: show });
|
||||
|
||||
setTimeout(() => {
|
||||
this.setData({ showWrapper: false });
|
||||
}, time);
|
||||
|
||||
this.rerender();
|
||||
return;
|
||||
}
|
||||
|
||||
this.parent.getChildWrapperStyle().then((wrapperStyle: String = '') => {
|
||||
this.setData({
|
||||
transition: !options.immediate,
|
||||
showPopup: show,
|
||||
wrapperStyle,
|
||||
showWrapper: true
|
||||
});
|
||||
this.rerender();
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -2,11 +2,8 @@ import { VantComponent } from '../common/component';
|
||||
import { Weapp } from 'definitions/weapp';
|
||||
import { addUnit } from '../common/utils';
|
||||
|
||||
interface ToggleOptions {
|
||||
immediate?: Boolean;
|
||||
}
|
||||
|
||||
let ARRAY: WechatMiniprogram.Component.TrivialInstance[] = [];
|
||||
type TrivialInstance = WechatMiniprogram.Component.TrivialInstance;
|
||||
let ARRAY: TrivialInstance[] = [];
|
||||
|
||||
VantComponent({
|
||||
field: true,
|
||||
@ -15,35 +12,26 @@ VantComponent({
|
||||
name: 'dropdown-item',
|
||||
type: 'descendant',
|
||||
linked(target) {
|
||||
this.children = this.children || [];
|
||||
// 透传 props 给 dropdown-item
|
||||
const { overlay, duration, activeColor, closeOnClickOverlay, direction } = this.data;
|
||||
this.updateChildData(target, {
|
||||
overlay,
|
||||
duration,
|
||||
activeColor,
|
||||
closeOnClickOverlay,
|
||||
direction,
|
||||
childIndex: this.children.length
|
||||
});
|
||||
|
||||
this.children.push(target);
|
||||
// 收集 dorpdown-item 的 data 挂在 data 上
|
||||
target &&
|
||||
this.setData({
|
||||
itemListData: this.data.itemListData.concat([target.data])
|
||||
});
|
||||
this.updateItemListData();
|
||||
},
|
||||
unlinked(target) {
|
||||
this.children = this.children.filter((child: WechatMiniprogram.Component.TrivialInstance) => child !== target);
|
||||
this.children = this.children.filter(
|
||||
(child: TrivialInstance) => child !== target
|
||||
);
|
||||
this.updateItemListData();
|
||||
}
|
||||
},
|
||||
|
||||
props: {
|
||||
activeColor: String,
|
||||
activeColor: {
|
||||
type: String,
|
||||
observer: 'updateChildrenData'
|
||||
},
|
||||
overlay: {
|
||||
type: Boolean,
|
||||
value: true
|
||||
value: true,
|
||||
observer: 'updateChildrenData'
|
||||
},
|
||||
zIndex: {
|
||||
type: Number,
|
||||
@ -51,15 +39,18 @@ VantComponent({
|
||||
},
|
||||
duration: {
|
||||
type: Number,
|
||||
value: 200
|
||||
value: 200,
|
||||
observer: 'updateChildrenData'
|
||||
},
|
||||
direction: {
|
||||
type: String,
|
||||
value: 'down'
|
||||
value: 'down',
|
||||
observer: 'updateChildrenData'
|
||||
},
|
||||
closeOnClickOverlay: {
|
||||
type: Boolean,
|
||||
value: true
|
||||
value: true,
|
||||
observer: 'updateChildrenData'
|
||||
},
|
||||
closeOnClickOutside: {
|
||||
type: Boolean,
|
||||
@ -71,7 +62,10 @@ VantComponent({
|
||||
itemListData: []
|
||||
},
|
||||
|
||||
created() {
|
||||
beforeCreate() {
|
||||
const { windowHeight } = wx.getSystemInfoSync();
|
||||
this.windowHeight = windowHeight;
|
||||
this.children = [];
|
||||
ARRAY.push(this);
|
||||
},
|
||||
|
||||
@ -80,99 +74,67 @@ VantComponent({
|
||||
},
|
||||
|
||||
methods: {
|
||||
updateChildData(childItem: WechatMiniprogram.Component.TrivialInstance, newData, needRefreshList: Boolean = false) {
|
||||
childItem.setData(newData);
|
||||
|
||||
if (needRefreshList) {
|
||||
// dropdown-item data 更新,涉及到 title 的展示,触发模板更新
|
||||
this.setData({ itemListData: this.data.itemListData });
|
||||
}
|
||||
},
|
||||
|
||||
toggleItem(active: Number) {
|
||||
this.children.forEach((item: WechatMiniprogram.Component.TrivialInstance, index: Number) => {
|
||||
const { showPopup } = item.data;
|
||||
if (index === active) {
|
||||
this.toggleChildItem(item);
|
||||
} else if (showPopup) {
|
||||
this.toggleChildItem(item, false, { immediate: true });
|
||||
}
|
||||
updateItemListData() {
|
||||
this.setData({
|
||||
itemListData: this.children.map((child: TrivialInstance) => child.data)
|
||||
});
|
||||
},
|
||||
|
||||
toggleChildItem(childItem: WechatMiniprogram.Component.TrivialInstance, show: boolean, options: ToggleOptions = {}) {
|
||||
const { showPopup, duration } = childItem.data;
|
||||
updateChildrenData() {
|
||||
this.children.forEach((child: TrivialInstance) => {
|
||||
child.updateDataFromParent();
|
||||
});
|
||||
},
|
||||
|
||||
if (show === undefined) show = !showPopup;
|
||||
|
||||
if (show === showPopup) {
|
||||
return;
|
||||
}
|
||||
|
||||
const newChildData = { transition: !options.immediate, showPopup: show };
|
||||
|
||||
if (!show) {
|
||||
const time = options.immediate ? 0 : duration;
|
||||
this.updateChildData(childItem, { ...newChildData }, true);
|
||||
|
||||
setTimeout(() => {
|
||||
this.updateChildData(childItem, { showWrapper: false }, true);
|
||||
}, time);
|
||||
return;
|
||||
}
|
||||
|
||||
this.getChildWrapperStyle().then((wrapperStyle: String = '') => {
|
||||
this.updateChildData(
|
||||
childItem,
|
||||
{
|
||||
...newChildData,
|
||||
wrapperStyle,
|
||||
showWrapper: true
|
||||
},
|
||||
true
|
||||
);
|
||||
toggleItem(active: number) {
|
||||
this.children.forEach((item: TrivialInstance, index: number) => {
|
||||
const { showPopup } = item.data;
|
||||
if (index === active) {
|
||||
item.toggle();
|
||||
} else if (showPopup) {
|
||||
item.toggle(false, { immediate: true });
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
close() {
|
||||
this.children.forEach((item: WechatMiniprogram.Component.TrivialInstance) => {
|
||||
this.toggleChildItem(item, false, { immediate: true });
|
||||
this.children.forEach((child: TrivialInstance) => {
|
||||
child.toggle(false, { immediate: true });
|
||||
});
|
||||
},
|
||||
|
||||
getChildWrapperStyle() {
|
||||
const { windowHeight } = wx.getSystemInfoSync();
|
||||
const { zIndex, direction } = this.data;
|
||||
let offset = 0;
|
||||
|
||||
return this.getRect('.van-dropdown-menu').then(rect => {
|
||||
const { top = 0, bottom = 0 } = rect;
|
||||
if (direction === 'down') {
|
||||
offset = bottom;
|
||||
} else {
|
||||
offset = windowHeight - top;
|
||||
return this.getRect('.van-dropdown-menu').then(
|
||||
(rect: WechatMiniprogram.BoundingClientRectCallbackResult) => {
|
||||
const { top = 0, bottom = 0 } = rect;
|
||||
const offset = direction === 'down' ? bottom : this.windowHeight - top;
|
||||
|
||||
let wrapperStyle = `z-index: ${zIndex};`;
|
||||
|
||||
if (direction === 'down') {
|
||||
wrapperStyle += `top: ${addUnit(offset)};`;
|
||||
} else {
|
||||
wrapperStyle += `bottom: ${addUnit(offset)};`;
|
||||
}
|
||||
|
||||
return wrapperStyle;
|
||||
}
|
||||
|
||||
let wrapperStyle = `z-index: ${zIndex};`;
|
||||
|
||||
if (direction === 'down') {
|
||||
wrapperStyle += `top: ${addUnit(offset)};`;
|
||||
} else {
|
||||
wrapperStyle += `bottom: ${addUnit(offset)};`;
|
||||
}
|
||||
|
||||
return Promise.resolve(wrapperStyle);
|
||||
});
|
||||
);
|
||||
},
|
||||
|
||||
onTitleTap(event: Weapp.Event) {
|
||||
// item ---> dropdown-item
|
||||
const { item, index } = event.currentTarget.dataset;
|
||||
const { index } = event.currentTarget.dataset;
|
||||
const child = this.children[index];
|
||||
|
||||
if (!item.disabled) {
|
||||
// menuItem ---> dropdown-menu
|
||||
if (!child.data.disabled) {
|
||||
ARRAY.forEach(menuItem => {
|
||||
if (menuItem && menuItem.data.closeOnClickOutside && menuItem !== this) {
|
||||
if (
|
||||
menuItem &&
|
||||
menuItem.data.closeOnClickOutside &&
|
||||
menuItem !== this
|
||||
) {
|
||||
menuItem.close();
|
||||
}
|
||||
});
|
||||
|
@ -1,10 +1,10 @@
|
||||
<wxs src="../wxs/utils.wxs" module="utils" />
|
||||
<wxs src="./index.wxs" module="computed" />
|
||||
|
||||
<view class="van-dropdown-menu van-dropdown-menu--top-bottom">
|
||||
<view
|
||||
wx:for="{{ itemListData }}"
|
||||
wx:key="index"
|
||||
data-item="{{ item }}"
|
||||
data-index="{{ index }}"
|
||||
class="{{ utils.bem('dropdown-menu__item', { disabled: item.disabled }) }}"
|
||||
bind:tap="onTitleTap"
|
||||
@ -14,7 +14,7 @@
|
||||
style="{{ item.showPopup ? 'color:' + activeColor : '' }}"
|
||||
>
|
||||
<view class="van-ellipsis">
|
||||
{{item.displayTitle}}
|
||||
{{ computed.displayTitle(item) }}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
16
packages/dropdown-menu/index.wxs
Normal file
16
packages/dropdown-menu/index.wxs
Normal file
@ -0,0 +1,16 @@
|
||||
/* eslint-disable */
|
||||
function displayTitle(item) {
|
||||
if (item.title) {
|
||||
return item.title;
|
||||
}
|
||||
|
||||
var match = item.options.filter(function(option) {
|
||||
return option.value === item.value;
|
||||
});
|
||||
var displayTitle = match.length ? match[0].text : '';
|
||||
return displayTitle;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
displayTitle: displayTitle
|
||||
};
|
Loading…
x
Reference in New Issue
Block a user