Skip to content

Instantly share code, notes, and snippets.

@reed-jones
Last active September 18, 2018 04:25
Show Gist options
  • Save reed-jones/9cb84f93a0cc87c20c6c1089b6c26006 to your computer and use it in GitHub Desktop.
Save reed-jones/9cb84f93a0cc87c20c6c1089b6c26006 to your computer and use it in GitHub Desktop.
// demo
// https://codesandbox.io/s/o3my2rxxy
<template lang="pug">
.wrapper(v-click-outside='_ => opened = false')
input.dropdown(
type='text'
ref='inputBox'
@click='opened = !opened'
@keydown.prevent=''
@keydown.tab='pressTab'
@keydown.up="navigate(highlighted - 1)"
@keydown.down="navigate(highlighted + 1)"
@keydown.enter="pressEnter"
:value='(value.label || value)'
)
.options(v-if='opened')
.option(
v-for='(option, idx) in options'
@click='selectOption(option)'
:class='{
active: (option.value || option) === value,
highlighted: idx === highlighted
}')
| {{ option.label || option }}
</template>
<script>
export default {
props: {
options: {
type: Array,
required: true,
validator: arr => arr.length
},
value: {
type: String|Number,
default: function() { return this.options[0] }
}
},
directives: {
clickOutside: {
bind(el, binding, vnode) {
window.addEventListener('click', e => {
if (!el.contains(e.target)) {
return binding.value(e)
}
})
}
}
},
data: _ => ({
opened: false,
highlighted: 0
}),
created() {
let value = this.options.find(o => o.value === (this.value.value || this.value)) || this.value
this.$emit('input', value)
},
methods: {
selectOption(option) {
this.$emit('input', option)
this.highlighted = this.options.indexOf(option)
this.opened = false
},
navigate(num) {
if (!this.opened) {
this.opened = true
} else {
this.highlighted = (this.options.length + num) % this.options.length
}
},
pressEnter() {
if (!this.opened) {
this.opened = true
this.highlighted = 0
} else {
this.selectOption(this.options[this.highlighted])
}
},
pressTab() {
if(!this.opened) {
this.$refs.inputBox.blur()
} else {
this.selectOption(this.options[this.highlighted]);
}
}
}
}
</script>
<style lang="stylus">
height = 2rem
half = .5rem
back = #000
border = 1px solid black
.wrapper
display flex
align-items flex-start
justify-content flex-start
position relative
width 150px
&:after
content ''
transition .15s
position absolute
right half
top .75rem
bottom 0
width half
height half
opacity 0
pointer-events none
border-left border
border-bottom border
transform rotate(-45deg)
&:focus-within:after,
&:hover:after
opacity 1
.dropdown
padding 0 half
border border
position relative
width 100%
height height
color transparent
text-shadow 0 0 0 black
cursor pointer
user-select none
&::selection
background transparent
.options
border border
width 100%
position absolute
top height
margin 0 auto
.option
padding half
border border
cursor pointer
color transparent
text-shadow 0 0 0 black
user-select none
&::selection
background transparent
&.active
background #eee
&.highlighted,
&:hover
background #888
</style>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment