Skip to content

Commit

Permalink
add typeahead component & tests
Browse files Browse the repository at this point in the history
  • Loading branch information
wxsms committed Mar 27, 2017
1 parent 92ecc65 commit eda97c3
Show file tree
Hide file tree
Showing 12 changed files with 1,061 additions and 9 deletions.
5 changes: 4 additions & 1 deletion src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
<tabs-doc></tabs-doc>
<time-picker-doc></time-picker-doc>
<tooltip-doc></tooltip-doc>
<typeahead-doc></typeahead-doc>
</div>
<page-footer></page-footer>
</section>
Expand All @@ -37,6 +38,7 @@
import TooltipDoc from './docs/TooltipDoc.vue'
import TimePickerDoc from './docs/TimePickerDoc.vue'
import PopoverDoc from './docs/PopoverDoc.vue'
import TypeaheadDoc from './docs/TypeaheadDoc.vue'
export default {
name: 'app',
components: {
Expand All @@ -54,7 +56,8 @@
PaginationDoc,
TooltipDoc,
TimePickerDoc,
PopoverDoc
PopoverDoc,
TypeaheadDoc
},
mounted () {
// highlight code blocks
Expand Down
9 changes: 3 additions & 6 deletions src/components/datepicker/DatePicker.vue
Original file line number Diff line number Diff line change
Expand Up @@ -105,12 +105,9 @@
this.currentYear = this.now.getFullYear()
},
watch: {
value: {
handler (val) {
this.currentMonth = val.getMonth()
this.currentYear = val.getFullYear()
},
deep: true
value (val) {
this.currentMonth = val.getMonth()
this.currentYear = val.getFullYear()
}
},
methods: {
Expand Down
8 changes: 6 additions & 2 deletions src/components/dropdown/Dropdown.vue
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,12 @@
window.removeEventListener('click', this.windowClicked)
},
methods: {
toggle () {
this.show = !this.show
toggle (show) {
if (typeof show === 'boolean') {
this.show = show
} else {
this.show = !this.show
}
if (this.appendToBody && this.$slots.dropdown && this.$slots.dropdown.length) {
if (this.show) {
this.appendDropdownToBody()
Expand Down
171 changes: 171 additions & 0 deletions src/components/typeahead/Typeahead.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
<template>
<div>
<slot></slot>
<dropdown ref="dropdown" :append-to-body="appendToBody">
<button data-role="trigger" type="button" class="hidden"></button>
<ul slot="dropdown" class="dropdown-menu">
<li v-for="(item,index) in items" :class="{active:activeIndex===index}">
<a href="javascript:void(0)" @click="selectItem(item)">
<slot name="item" :item="item">
<span v-html="highlight(item)"></span>
</slot>
</a>
</li>
</ul>
</dropdown>
</div>
</template>

<script>
import utils from './../../utils/httpUtils'
import Dropdown from './../dropdown/Dropdown.vue'
export default {
components: {Dropdown},
props: {
value: {},
data: {
type: Array
},
itemKey: {
type: String
},
appendToBody: {
type: Boolean,
'default': false
},
ignoreCase: {
type: Boolean,
'default': true
},
matchStart: {
type: Boolean,
'default': false
},
forceSelect: {
type: Boolean,
'default': false
},
limit: {
type: Number,
'default': 10
},
asyncSrc: {
type: String
},
asyncKey: {
type: String
},
asyncDelay: {
type: Number,
'default': 500
}
},
data () {
return {
inputEl: null,
items: [],
activeIndex: 0,
timeout: 0
}
},
computed: {
regexOptions () {
let options = ''
if (this.ignoreCase) {
options += 'i'
}
if (!this.matchStart) {
options += 'g'
}
return options
}
},
mounted () {
this.inputEl = this.$el.querySelector('[data-role="input"]')
if (this.inputEl) {
this.inputEl.addEventListener('input', this.inputChanged)
this.inputEl.addEventListener('keydown', this.inputKeyPressed)
}
},
beforeDestroy () {
if (this.inputEl) {
this.inputEl.removeEventListener('input', this.inputChanged)
this.inputEl.removeEventListener('keydown', this.inputKeyPressed)
}
},
methods: {
prepareItems (data) {
this.items = []
this.activeIndex = 0
for (let i = 0, l = data.length; i < l; i++) {
let item = data[i]
let key = this.itemKey ? item[this.itemKey] : item
let index = -1
if (this.ignoreCase) {
index = key.toLowerCase().indexOf(this.inputEl.value.toLowerCase())
} else {
index = key.indexOf(this.inputEl.value)
}
if ((this.matchStart && index === 0) || (!this.matchStart && index >= 0)) {
this.items.push(item)
}
if (this.items.length >= this.limit) {
break
}
}
},
inputChanged () {
let value = this.inputEl.value
if (value) {
if (this.data) {
this.prepareItems(this.data)
this.$refs.dropdown.toggle(!!this.items.length)
} else if (this.asyncSrc) {
clearTimeout(this.timeout)
this.timeout = setTimeout(() => {
utils.get(this.asyncSrc + value)
.then(data => {
this.prepareItems(this.asyncKey ? data[this.asyncKey] : data)
this.$refs.dropdown.toggle(!!this.items.length)
})
}, this.asyncDelay)
}
} else {
clearTimeout(this.timeout)
this.$refs.dropdown.toggle(false)
}
this.$emit('input', this.forceSelect ? null : value)
},
inputKeyPressed (event) {
if (this.$refs.dropdown.show) {
switch (event.keyCode) {
case 13:
this.selectItem(this.items[this.activeIndex])
break
case 38:
this.activeIndex = this.activeIndex > 0 ? this.activeIndex - 1 : 0
break
case 40:
let maxIndex = this.items.length - 1
this.activeIndex = this.activeIndex < maxIndex ? this.activeIndex + 1 : maxIndex
break
}
}
},
selectItem (item) {
this.inputEl.value = this.itemKey ? item[this.itemKey] : item
this.$emit('input', item)
this.$refs.dropdown.toggle(false)
},
highlight (item) {
let value = this.itemKey ? item[this.itemKey] : item
return value.replace(new RegExp(`${this.inputEl.value}`, this.regexOptions), '<b>$&</b>')
}
}
}
</script>

<style lang="less" rel="stylesheet/less" scoped>
</style>
6 changes: 6 additions & 0 deletions src/docs/DatePickerDoc.vue
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,12 @@
<h4>Note</h4>
<ul>
<li><p>Use <code>v-model: Date</code> to bind or change the selected date.</p></li>
<li>
<p>
Make sure to update the date object reference when try to change it from outside the component. E.g.
<code>model = new Date(model)</code>
</p>
</li>
</ul>
<h4>Props</h4>
<ul>
Expand Down

0 comments on commit eda97c3

Please sign in to comment.