refactor(base-picker): make keyboard events work

This commit is contained in:
07akioni 2019-08-26 15:41:44 +08:00
parent 00dfa036c3
commit 6bba332083
4 changed files with 200 additions and 110 deletions

View File

@ -7,17 +7,20 @@
'n-base-picker--disabled': disabled,
[`n-base-picker--${size}-size`]: true,
'n-base-picker--multiple': multiple,
'n-base-picker--focus': false
'n-base-picker--focus': patternInputFocused
}"
@click="handleActivatorClick"
@click="handleClick"
>
<template v-if="multiple && !filterable">
<!-- multiple -->
<div
class="n-base-picker-tags"
:class="{
'n-base-picker-tags--selected': selected
'n-base-picker-tags--selected': selected,
'n-base-picker-tags--focused': patternInputFocused
}"
:tabindex="disabled ? false : '0'"
@blur="handleBlur"
>
<div
v-for="option in selectedOptions"
@ -30,22 +33,64 @@
<n-icon
class="n-base-picker-tag__icon"
type="md-close"
@click.stop="handleOptionToggle(option)"
@click.stop="handleDeleteOption(option)"
/>
</div>
</div>
<div
class="n-base-picker__placeholder"
>
{{ placeholder }}
</div>
<n-base-cancel-mark
class="n-base-picker__mark"
arrow
:show="!remote"
:disabled="disabled"
:active="active"
:clearable="clearable && selected"
@clear="handleClear"
/>
</template>
<template v-if="multiple && filterable">
<!-- multiple filterable -->
<div
class="n-base-picker-tags"
:class="{
'n-base-picker-tags--selected': selected
}"
:tabindex="(disabled || active) ? false : '0'"
>
<div
v-for="option in selectedOptions"
:key="option.value"
class="n-base-picker-tag"
>
<div class="n-base-picker-tag__content">
{{ option.label }}
</div>
<n-icon
class="n-base-picker-tag__icon"
type="md-close"
@click.stop="handleDeleteOption(option)"
/>
</div>
<div
v-if="filterable && active"
class="n-base-picker-input-tag"
>
<input
ref="inputTagInput"
ref="patternInput"
tabindex="-1"
:disabled="disabled"
:value="pattern"
class="n-base-picker-input-tag__input"
@keydown.delete="handlePatternInputDelete"
@input="handlePatternInput"
@blur="handlePatternInputBlur"
@focus="handlePatternInputFocus"
@keydown.delete="handlePatternKeyDownDelete"
@input="handlePatternInputInput"
>
<span
ref="inputTagMirror"
ref="patternInputMirror"
class="n-base-picker-input-tag__mirror"
>{{ pattern ? pattern : '&nbsp;' }}</span>
</div>
@ -66,14 +111,24 @@
/>
</template>
<template v-else-if="!multiple && filterable">
<input
ref="singleInput"
:value="(active && filterable) ? pattern : (selectedOption && selectedOption.label)"
<!-- single filterable -->
<div
class="n-base-picker-label"
:placeholder="selectedOption ? selectedOption.label : placeholder"
:readonly="!disabled && filterable ? false : 'readonly'"
@input="handlePatternInput"
:tabindex="(!disabled && !active) ? '0' : false"
>
<input
ref="patternInput"
class="n-base-picker-label__input"
:value="(patternInputFocused && active) ? pattern : label"
:placeholder="selectedOption ? label : placeholder"
:readonly="!disabled && filterable && active ? false : 'readonly'"
:disabled="disabled"
tabindex="-1"
@focus="handlePatternInputFocus"
@blur="handlePatternInputBlur"
@input="handlePatternInputInput"
>
</div>
<n-base-cancel-mark
class="n-base-picker__mark"
arrow
@ -85,20 +140,25 @@
/>
</template>
<template v-else-if="!multiple && !filterable">
<!-- single -->
<div
ref="singleInput"
:tabindex="disabled ? false : '0'"
class="n-base-picker-label"
:class="{
'n-base-picker-label--placeholder': !(label && label.length)
}"
:tabindex="disabled ? false : '0'"
@blur="handleBlur"
>
<template v-if="label && label.length">
{{ label }}
</template>
<template v-else>
{{ labelPlaceholder }}
</template>
<div
class="n-base-picker-label__input"
:class="{
'n-base-picker-label__input--placeholder': !(label && label.length)
}"
>
<template v-if="label && label.length">
{{ label }}
</template>
<template v-else>
{{ labelPlaceholder }}
</template>
</div>
</div>
<n-base-cancel-mark
class="n-base-picker__mark"
@ -177,12 +237,17 @@ export default {
default: 'medium'
}
},
data () {
return {
patternInputFocused: false
}
},
computed: {
labelPlaceholder () {
return this.selectedOption ? this.selectedOption.label : this.placeholder
},
label () {
const label = (this.active && this.filterable) ? this.pattern : (this.selectedOption && this.selectedOption.label)
const label = (this.selectedOption && this.selectedOption.label) || ''
console.log(label)
return label
},
@ -195,54 +260,68 @@ export default {
},
watch: {
active (active) {
if (!active) {
if (active) {
this.$nextTick().then(() => {
this.blurSingleInput()
if (this.$refs.singleInput) {
this.$refs.singleInput.focus()
}
})
}
}
},
methods: {
handleOptionToggle (option) {
this.toggleOption(option)
handleBlur () {
this.$emit('blur')
},
handleClear (e) {
this.$emit('clear', e)
},
handleActivatorClick () {
this.$emit('activator-click')
if (this.multiple && this.filterable) {
this.$nextTick().then(() => {
this.focusInputTag()
})
handleClick () {
this.$emit('click')
if (this.filterable) {
this.focusPatternInput()
}
},
handlePatternInputDelete (e) {
handleDeleteOption (option) {
this.$emit('delete-option')
this.toggleOption(option)
},
handlePatternKeyDownDelete (option) {
if (!this.pattern.length) {
// console.log(e)
this.$emit('pattern-input-delete')
this.$emit('delete-last-option')
}
},
handlePatternInput (e) {
handlePatternInputInput (e) {
// console.log('NBasePicker, handlePatternInput', e)
if (this.multiple) {
this.$nextTick().then(() => {
const textWidth = this.$refs.inputTagMirror.getBoundingClientRect().width
this.$refs.inputTagInput.style.width = textWidth + 'px'
const textWidth = this.$refs.patternInputMirror.getBoundingClientRect().width
this.$refs.patternInput.style.width = textWidth + 'px'
this.$emit('pattern-input', e)
})
} else {
this.$emit('pattern-input', e)
}
},
blurSingleInput () {
if (this.$refs.singleInput) {
this.$refs.singleInput.blur()
handlePatternInputFocus (e) {
console.log('handlePatternInputFocus')
this.patternInputFocused = true
},
handlePatternInputBlur (e) {
console.log('handlePatternInputBlur')
this.patternInputFocused = false
this.handleBlur()
},
focusPatternInput () {
if (this.$refs.patternInput) {
console.log('focusPatternInput')
this.$refs.patternInput.focus()
}
},
focusInputTag () {
if (this.$refs.inputTagInput) {
this.$refs.inputTagInput.focus()
blurPatternInput () {
if (this.$refs.patternInput) {
console.log('blurPatternInput')
this.$refs.patternInput.blur()
}
}
}

View File

@ -5,9 +5,9 @@
[`n-base-select-menu--${size}-size`]: true,
'n-base-select-menu--multiple': multiple
}"
tabindex="0"
@keyup.up.stop="handleKeyUpUp"
@keyup.down.stop="handleKeyUpDown"
@mousedown.prevent="() => {}"
>
<n-scrollbar
ref="scrollbar"

View File

@ -4,7 +4,7 @@
class="n-select"
@keydown.up.prevent="() => {}"
@keydown.down.prevent="() => {}"
@keydown.space.prevent="() => {}"
@keydown.space="handleKeyDownSpace"
@keyup.up="handleKeyUpUp"
@keyup.down="handleKeyUpDown"
@keyup.enter="handleKeyUpEnter"
@ -26,10 +26,11 @@
:disabled="disabled"
:on-search="onSearch"
:size="size"
@activator-click="handleActivatorClick"
@pattern-input-delete="handlePatternInputDelete"
@click="handleActivatorClick"
@delete-last-option="handleDeleteLastOption"
@pattern-input="handlePatternInput"
@clear="handleClear"
@blur="handlePickerBlur"
/>
<div
ref="contentContainer"
@ -293,34 +294,34 @@ export default {
}
},
handleClickOutsideMenu (e) {
if (!this.$refs.activator.$el.contains(e.target) && !this.scrolling) {
this.deactivate()
if (this.filterable && !this.multiple) {
this.pattern = ''
if (this.active) {
if (!this.$refs.activator.$el.contains(e.target) && !this.scrolling) {
console.log('handleClickOutsideMenu')
this.closeMenu()
}
}
},
closeMenu () {
this.deactivate()
if (!this.multiple) {
this.$refs.activator.blurSingleInput()
openMenu () {
console.log('openMenu')
this.pattern = ''
this.activate()
if (this.filterable) {
this.$refs.activator.focusPatternInput()
}
},
closeMenu () {
console.log('closeMenu')
this.deactivate()
},
handleActivatorClick () {
if (this.disabled) return
if (!this.active) {
this.pattern = ''
this.activate()
this.openMenu()
} else {
if (!this.filterable) {
this.deactivate()
this.closeMenu()
}
}
if (this.multiple && this.filterable) {
this.$nextTick().then(() => {
this.$refs.activator.focusInputTag()
})
}
},
toggleOption (option) {
if (this.disabled) return
@ -342,12 +343,6 @@ export default {
// this.emitChangeEvent(item, true)
this.pattern = ''
}
this.$nextTick().then(() => {
if (this.filterable) {
// console.log('toggleOption inputTagInput')
this.$refs.activator.focusInputTag()
}
})
this.$emit('input', newValue)
this.emitChangeEvent(newValue)
} else {
@ -370,7 +365,7 @@ export default {
handleMenuScroll (e, scrollContainer, scrollContent) {
this.$emit('scroll', e, scrollContainer, scrollContent)
},
handlePatternInputDelete (e) {
handleDeleteLastOption (e) {
if (!this.pattern.length) {
const newValue = this.value
if (Array.isArray(newValue)) {
@ -385,15 +380,21 @@ export default {
this.onSearch(e.target.value)
}
},
handleKeyUpEnter () {
// console.log('keyup enter')
const pendingOption = this.$refs.contentInner && this.$refs.contentInner.pendingOption
if (pendingOption) {
this.toggleOption(pendingOption)
handleKeyUpEnter (e) {
if (this.active) {
const pendingOption = this.$refs.contentInner && this.$refs.contentInner.pendingOption
if (pendingOption) {
this.toggleOption(pendingOption)
} else {
this.closeMenu()
}
} else {
this.openMenu()
}
e.preventDefault()
},
handleKeyUpSpace () {
this.handleKeyUpEnter()
handleKeyUpSpace (e) {
this.handleKeyUpEnter(e)
},
handleKeyUpUp () {
// console.log('keyup up')
@ -409,11 +410,19 @@ export default {
this.$refs.contentInner.next()
}
},
handleKeyDownSpace (e) {
if (!this.filterable) {
e.preventDefault()
}
},
handleClear (e) {
e.stopPropagation()
// e.stopPropagation()
this.closeMenu()
this.$emit('input', null)
this.emitChangeEvent(null)
},
handlePickerBlur () {
this.closeMenu()
}
}
}

View File

@ -2,10 +2,9 @@
@import './theme/default.scss';
@include b(base-picker) {
outline: none;
display: inline-block;
position: relative;
box-shadow: none;
cursor: pointer;
border-radius: $select-border-radius;
width: 100%;
max-width: 100%;
@ -24,29 +23,32 @@
opacity: 1;
}
.n-base-picker-label, .n-base-picker-tags {
outline: none;
cursor: pointer;
// outline: none;
box-sizing: border-box;
position: relative;
transition: box-shadow .3s $default-cubic-bezier, background-color .3s $default-cubic-bezier;
box-sizing: border-box;
border-radius: $select-border-radius;
background-color: $select-background-color;
}
.n-base-picker-label {
text-overflow: ellipsis;
overflow: hidden;
vertical-align: baseline;
display: inline-block;
border:none;
width: 100%;
white-space: nowrap;
cursor: inherit;
color: $select-text-color;
padding: 0 32px 0 14px;
&::placeholder {
color: $select-placeholder-color;
}
&.n-base-picker-label--placeholder {
color: $select-placeholder-color;
.n-base-picker-label__input {
box-sizing: border-box;
text-overflow: ellipsis;
overflow: hidden;
border:none;
width: 100%;
white-space: nowrap;
color: $select-text-color;
padding: 0 32px 0 14px;
background-color: transparent;
height: 100%;
&::placeholder {
color: $select-placeholder-color;
}
&.n-base-picker-label__input--placeholder {
color: $select-placeholder-color;
}
}
}
.n-base-picker-tags {
@ -176,22 +178,22 @@
.n-base-picker__placeholder {
color: rgba(255, 255, 255, 0.20);
}
.n-base-picker-label {
.n-base-picker-label__input {
color: rgba(255, 255, 255, 0.20);
&::placeholder {
color: rgba(255, 255, 255, 0.20);
}
}
}
&.n-base-picker--focus {
.n-base-picker-label, .n-base-picker-tags {
box-shadow: inset 0 0 0 1px $select-border-color--active;
}
}
&:not(.n-base-picker--disabled) {
.n-base-picker-label:hover, .n-base-picker-tags:hover {
box-shadow: inset 0 0 0 1px $select-border-color--active;
}
.n-base-picker-label[readonly="readonly"] {
user-select: none;
}
}
&:not(.n-base-picker--disabled) {
.n-base-picker-label:focus, .n-base-picker-tags:focus {
box-shadow: inset 0 0 0 1px $select-border-color--active;
}
@ -207,7 +209,7 @@
.n-base-picker-input-tag__input {
padding: 0;
background-color: transparent;
outline: none;
// outline: none;
border: none;
max-width: 100%;
caret-color: $input-caret-color;