提交 4e1db854 编写于 作者: H HuangYi 提交者: GitHub

Merge pull request #52 from didi/index-list-dev

Index-list: add item slot + get actual title height and sub title height + add data property shortcut
[ [
{ {
"name": "★Hot City", "name": "★ Hot City",
"shortcut": "★",
"items": [ "items": [
{ {
"name": "BEIJING", "name": "BEIJING",
...@@ -33,7 +34,7 @@ ...@@ -33,7 +34,7 @@
"value": 1 "value": 1
}, },
{ {
"name": "BAYINGUOLENGZHOU,", "name": "BAYINGUOLENGZHOU",
"value": 5 "value": 5
}, },
{ {
......
...@@ -4,7 +4,13 @@ ...@@ -4,7 +4,13 @@
<div slot="content"> <div slot="content">
<div class="view-wrapper"> <div class="view-wrapper">
<div class="index-list-wrapper"> <div class="index-list-wrapper">
<cube-index-list :data="cityData" :title="title" @select="selectItem" @title-click="clickTitle"></cube-index-list> <cube-index-list :data="cityData" :title="title" @title-click="clickTitle">
<cube-index-list-group v-for="group in cityData" :group="group">
<cube-index-list-item v-for="item in group.items" :item="item" @select="selectItem">
我是自定义{{item.name}}
</cube-index-list-item>
</cube-index-list-group>
</cube-index-list>
</div> </div>
</div> </div>
</div> </div>
......
<template>
<div class="cube-index-list-group">
<h2 class="cube-index-list-anchor">{{group.name}}</h2>
<ul>
<slot>
<cube-index-list-item v-for="(item, index) in group.items" :key="index" :item="item" @select="selectItem"></cube-index-list-item>
</slot>
</ul>
</div>
</template>
<script type="text/ecmascript-6">
const COMPONENT_NAME = 'cube-index-list-group'
const EVENT_SELECT = 'select'
export default {
name: COMPONENT_NAME,
props: {
group: {
type: Object,
default() {
return {}
}
}
},
methods: {
selectItem(item) {
this.$emit(EVENT_SELECT, item)
}
}
}
</script>
<style lang="stylus" rel="stylesheet/stylus">
.cube-index-list-anchor
padding: 16px 16px 10px 16px
line-height: 1
font-size: $fontsize-medium
color: $index-list-anchor-color
background: $index-list-anchor-bgc
</style>
<template>
<div
class="cube-index-list-item border-bottom-1px"
@touchstart="addActiveCls"
@touchend="removeActiveCls"
@click="selectItem()">
<slot>
{{item.name}}
</slot>
</div>
</template>
<script type="text/ecmascript-6">
import {
addClass,
removeClass
} from '../../common/helpers/dom'
const COMPONENT_NAME = 'cube-index-list-item'
const ACTIVE_CLS = 'cube-index-list-item_active'
const EVENT_SELECT = 'select'
export default {
name: COMPONENT_NAME,
props: {
item: {
type: Object,
default() {
return {}
}
}
},
methods: {
addActiveCls(e) {
addClass(e.currentTarget, ACTIVE_CLS)
},
removeActiveCls(e) {
removeClass(e.currentTarget, ACTIVE_CLS)
},
selectItem() {
this.$emit(EVENT_SELECT, this.item)
}
}
}
</script>
<style lang="stylus" rel="stylesheet/stylus">
.cube-index-list-item
position: relative
height: 50px
line-height: 50px
padding: 0 16px
font-size: $fontsize-medium
color: $index-list-item-color
&:last-child
border-none()
.cube-index-list-item_active
background: $index-list-item-active-bgc
</style>
...@@ -11,19 +11,10 @@ ...@@ -11,19 +11,10 @@
{{title}} {{title}}
</h1> </h1>
<ul ref="groups"> <ul ref="groups">
<li v-for="group in data" ref="listGroup"> <slot>
<h2 class="cube-index-list-anchor">{{group.name}}</h2> <cube-index-list-group v-for="(group, index) in data" :key="index" :group="group" @select="selectItem">
<ul> </cube-index-list-group>
<li </slot>
class="cube-index-list-item border-bottom-1px"
v-for="item in group.items"
@touchstart="addActiveCls"
@touchend="removeActiveCls"
@click="selectItem(item)">
{{item.name}}
</li>
</ul>
</li>
</ul> </ul>
</div> </div>
</cube-scroll> </cube-scroll>
...@@ -41,8 +32,7 @@ ...@@ -41,8 +32,7 @@
<script type="text/ecmascript-6"> <script type="text/ecmascript-6">
import { import {
getData, getData,
addClass, getRect
removeClass
} from '../../common/helpers/dom' } from '../../common/helpers/dom'
import CubeScroll from '../scroll/scroll.vue' import CubeScroll from '../scroll/scroll.vue'
...@@ -50,10 +40,7 @@ ...@@ -50,10 +40,7 @@
const COMPONENT_NAME = 'cube-index-list' const COMPONENT_NAME = 'cube-index-list'
const EVENT_SELECT = 'select' const EVENT_SELECT = 'select'
const EVENT_TITLE_CLICK = 'title-click' const EVENT_TITLE_CLICK = 'title-click'
const ACTIVE_CLS = 'cube-index-list-item_active'
const TITLE_HEIGHT = 50
const SUBTITLE_HEIGHT = 40
const ANCHOR_HEIGHT = window.innerHeight <= 480 ? 17 : 18 const ANCHOR_HEIGHT = window.innerHeight <= 480 ? 17 : 18
export default { export default {
...@@ -65,7 +52,9 @@ ...@@ -65,7 +52,9 @@
}, },
data: { data: {
type: Array, type: Array,
default: [] default() {
return []
}
} }
}, },
data() { data() {
...@@ -75,33 +64,41 @@ ...@@ -75,33 +64,41 @@
diff: -1, diff: -1,
options: { options: {
probeType: 3 probeType: 3
} },
titleHeight: null
} }
}, },
created() { created() {
this.listenScroll = true this.listenScroll = true
this.groupList = []
this.listHeight = [] this.listHeight = []
this.touch = {} this.touch = {}
this.subTitleHeight = 0
}, },
mounted() { mounted() {
setTimeout(() => { this.$nextTick(() => {
this.titleHeight = this.title && this.$refs.title ? getRect(this.$refs.title).height : 0
const subTitleEl = this.$el.querySelector('.cube-index-list-anchor')
this.subTitleHeight = subTitleEl ? getRect(subTitleEl).height : 0
this._calculateHeight() this._calculateHeight()
}, 20) })
}, },
computed: { computed: {
fixedTitle() { fixedTitle() {
if (this.scrollY > -TITLE_HEIGHT) { if (this.titleHeight === null || this.scrollY > -this.titleHeight) {
return '' return ''
} }
return this.data[this.currentIndex] ? this.data[this.currentIndex].name : '' return this.data[this.currentIndex] ? this.data[this.currentIndex].name : ''
}, },
shortcutList() { shortcutList() {
return this.data.map((group) => { return this.data.map((group) => {
return group.name.substr(0, 1) return group ? group.shortcut || group.name.substr(0, 1) : ''
}) })
} }
}, },
methods: { methods: {
/* TODO: remove refresh next minor version */
refresh() { refresh() {
this.$refs.indexList.refresh() this.$refs.indexList.refresh()
}, },
...@@ -130,22 +127,18 @@ ...@@ -130,22 +127,18 @@
this._scrollTo(anchorIndex) this._scrollTo(anchorIndex)
}, },
addActiveCls(e) {
addClass(e.currentTarget, ACTIVE_CLS)
},
removeActiveCls(e) {
removeClass(e.currentTarget, ACTIVE_CLS)
},
_calculateHeight() { _calculateHeight() {
const list = this.$refs.listGroup this.groupList = this.$el.querySelectorAll('.cube-index-list-group')
if (!list) {
if (!this.groupList) {
return return
} }
this.listHeight = [] this.listHeight = []
let height = TITLE_HEIGHT let height = this.titleHeight
this.listHeight.push(height) this.listHeight.push(height)
for (let i = 0; i < list.length; i++) { for (let i = 0; i < this.groupList.length; i++) {
let item = list[i] let item = this.groupList[i]
height += item.clientHeight height += item.clientHeight
this.listHeight.push(height) this.listHeight.push(height)
} }
...@@ -156,18 +149,24 @@ ...@@ -156,18 +149,24 @@
} else if (index > this.listHeight.length - 2) { } else if (index > this.listHeight.length - 2) {
index = this.listHeight.length - 2 index = this.listHeight.length - 2
} }
this.$refs.indexList.scrollToElement(this.$refs.listGroup[index], 0) this.$refs.indexList.scrollToElement(this.groupList[index], 0)
this.scrollY = this.$refs.indexList.scroll.y this.scrollY = this.$refs.indexList.scroll.y
} }
}, },
watch: { watch: {
data() { data() {
setTimeout(() => { this.$nextTick(() => {
this._calculateHeight() this._calculateHeight()
}, 20) })
},
title(newVal) {
this.$nextTick(() => {
this.titleHeight = newVal && this.$refs.title ? getRect(this.$refs.title).height : 0
this._calculateHeight()
})
}, },
diff(newVal) { diff(newVal) {
let fixedTop = (newVal > 0 && newVal < SUBTITLE_HEIGHT) ? newVal - SUBTITLE_HEIGHT : 0 let fixedTop = (newVal > 0 && newVal < this.subTitleHeight) ? newVal - this.subTitleHeight : 0
if (this.fixedTop === fixedTop) { if (this.fixedTop === fixedTop) {
return return
} }
...@@ -177,7 +176,7 @@ ...@@ -177,7 +176,7 @@
scrollY(newY) { scrollY(newY) {
const listHeight = this.listHeight const listHeight = this.listHeight
// top // top
if (newY > -TITLE_HEIGHT) { if (newY > -this.titleHeight) {
this.currentIndex = 0 this.currentIndex = 0
return return
} }
......
import IndexList from '../../components/index-list/index-list.vue' import IndexList from '../../components/index-list/index-list.vue'
import IndexListGroup from '../../components/index-list/index-list-group.vue'
import IndexListItem from '../../components/index-list/index-list-item.vue'
IndexList.install = function (Vue) { IndexList.install = function (Vue) {
Vue.component(IndexList.name, IndexList) Vue.component(IndexList.name, IndexList)
Vue.component(IndexListGroup.name, IndexListGroup)
Vue.component(IndexListItem.name, IndexListItem)
} }
IndexList.group = IndexListGroup
IndexList.item = IndexListItem
export default IndexList export default IndexList
...@@ -28,15 +28,17 @@ describe('IndexList', () => { ...@@ -28,15 +28,17 @@ describe('IndexList', () => {
vm = null vm = null
} }
}) })
it('use', () => { it('use', () => {
Vue.use(IndexList) Vue.use(IndexList)
expect(Vue.component(IndexList.name)) expect(Vue.component(IndexList.name))
.to.be.a('function') .to.be.a('function')
}) })
it('should render correct contents', () => { it('should render correct contents', () => {
vm = createIndexList() vm = createIndexList({
expect(vm.$el.querySelector('.cube-index-list-title').textContent.trim()) data
.to.equal('当前城市:北京市') })
const navItems = vm.$el.querySelectorAll('.cube-index-list-nav li') const navItems = vm.$el.querySelectorAll('.cube-index-list-nav li')
expect(navItems.length) expect(navItems.length)
.to.equal(9) .to.equal(9)
...@@ -60,6 +62,9 @@ describe('IndexList', () => { ...@@ -60,6 +62,9 @@ describe('IndexList', () => {
const titleClickHandler = sinon.spy() const titleClickHandler = sinon.spy()
vm = createIndexList({ vm = createIndexList({
title,
data
}, {
select: selectHandler, select: selectHandler,
'title-click': titleClickHandler 'title-click': titleClickHandler
}) })
...@@ -75,7 +80,7 @@ describe('IndexList', () => { ...@@ -75,7 +80,7 @@ describe('IndexList', () => {
it('should fixed title', function () { it('should fixed title', function () {
this.timeout(10000) this.timeout(10000)
vm = createIndexList({}, []) vm = createIndexList()
return new Promise((resolve) => { return new Promise((resolve) => {
setTimeout(() => { setTimeout(() => {
vm.$parent.updateRenderData({ vm.$parent.updateRenderData({
...@@ -125,9 +130,26 @@ describe('IndexList', () => { ...@@ -125,9 +130,26 @@ describe('IndexList', () => {
}) })
}) })
it('run normal when group or item undefined', () => {
vm = createIndexList({
data: [
undefined, // props group in index-list-group.vue
{
name: 'default',
items: [
undefined // props item in index-list-item.vue
]
}
]
})
})
it('should handle condition of unexpected param', function () { it('should handle condition of unexpected param', function () {
this.timeout(10000) this.timeout(10000)
vm = createIndexList() vm = createIndexList({
title,
data
})
return new Promise((resolve) => { return new Promise((resolve) => {
setTimeout(() => { setTimeout(() => {
const bEl = vm.$el.querySelector('.cube-index-list-nav li[data-index="2"]') const bEl = vm.$el.querySelector('.cube-index-list-nav li[data-index="2"]')
...@@ -171,8 +193,7 @@ describe('IndexList', () => { ...@@ -171,8 +193,7 @@ describe('IndexList', () => {
}) })
}) })
function createIndexList (events = {}, _data = data) { function createIndexList (props = {}, events = {}) {
const props = {title: title, data: _data}
return instantiateComponent(Vue, IndexList, { return instantiateComponent(Vue, IndexList, {
props: props, props: props,
on: events on: events
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册