input组件增加补全功能
parent
58be54911b
commit
c192b78aa5
|
@ -4,15 +4,27 @@
|
||||||
* @date 2023/03/06 15:17:25
|
* @date 2023/03/06 15:17:25
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { nextTick, css, html, Component } from '@bd/core'
|
import {
|
||||||
|
nextTick,
|
||||||
|
css,
|
||||||
|
html,
|
||||||
|
Component,
|
||||||
|
classMap,
|
||||||
|
outsideClick
|
||||||
|
} from '@bd/core'
|
||||||
import '../icon/index.js'
|
import '../icon/index.js'
|
||||||
|
import '../scroll/index.js'
|
||||||
|
|
||||||
|
const ANIMATION = {
|
||||||
|
duration: 100,
|
||||||
|
custom: [{ transform: 'scaleY(0)' }, { transform: 'scaleY(1)' }]
|
||||||
|
}
|
||||||
class Input extends Component {
|
class Input extends Component {
|
||||||
static props = {
|
static props = {
|
||||||
readOnly: false,
|
readOnly: false,
|
||||||
autofocus: false,
|
autofocus: false,
|
||||||
disabled: false,
|
disabled: false,
|
||||||
closeable: false,
|
clearable: false,
|
||||||
icon: '',
|
icon: '',
|
||||||
size: 'l',
|
size: 'l',
|
||||||
placeholder: '',
|
placeholder: '',
|
||||||
|
@ -25,11 +37,16 @@ class Input extends Component {
|
||||||
},
|
},
|
||||||
lazy: 0 // 并发拦截时间, 单位毫秒
|
lazy: 0 // 并发拦截时间, 单位毫秒
|
||||||
}
|
}
|
||||||
|
#list = []
|
||||||
|
#originList = []
|
||||||
|
#isComposing = false
|
||||||
|
#selectIndex = -1
|
||||||
|
#listShowing = false
|
||||||
|
#stamp = 0
|
||||||
static styles = [
|
static styles = [
|
||||||
css`
|
css`
|
||||||
:host {
|
:host {
|
||||||
overflow: hidden;
|
position: relative;
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
min-width: 228px;
|
min-width: 228px;
|
||||||
height: 36px;
|
height: 36px;
|
||||||
|
@ -109,25 +126,28 @@ class Input extends Component {
|
||||||
|
|
||||||
.suggestion {
|
.suggestion {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
display: none;
|
position: absolute;
|
||||||
position: fixed;
|
z-index: 1;
|
||||||
z-index: 10240;
|
|
||||||
left: 0;
|
left: 0;
|
||||||
top: 0;
|
top: calc(100% + 4px);
|
||||||
width: 200px;
|
width: 100%;
|
||||||
height: auto;
|
height: auto;
|
||||||
max-height: 200px;
|
max-height: 200px;
|
||||||
min-height: 46px;
|
|
||||||
padding: 4px 0;
|
padding: 4px 0;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
background: var(--color-plain-1);
|
// background: var(--color-plain-1);
|
||||||
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.15);
|
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.15);
|
||||||
|
transform-origin: top;
|
||||||
|
// transition: transform linear 00s;
|
||||||
|
// transform: scaleY(0);
|
||||||
|
wc-scroll {
|
||||||
|
max-height: 200px;
|
||||||
|
}
|
||||||
.list {
|
.list {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
&.show {
|
&.hide {
|
||||||
display: flex;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
li {
|
li {
|
||||||
|
@ -349,6 +369,11 @@ class Input extends Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
let classes = classMap({
|
||||||
|
suggestion: true,
|
||||||
|
hide: !this.#list.length
|
||||||
|
})
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<div class="label">
|
<div class="label">
|
||||||
<slot class="prepend" name="prepend"></slot>
|
<slot class="prepend" name="prepend"></slot>
|
||||||
|
@ -356,7 +381,10 @@ class Input extends Component {
|
||||||
ref="input"
|
ref="input"
|
||||||
@input=${this.onInput}
|
@input=${this.onInput}
|
||||||
@change=${this.onChange}
|
@change=${this.onChange}
|
||||||
|
@compositionstart=${this.onCompositionstart}
|
||||||
|
@compositionend=${this.onCompositionend}
|
||||||
@keydown=${this.onKeyDown}
|
@keydown=${this.onKeyDown}
|
||||||
|
@focus=${this.onFocus}
|
||||||
placeholder=${this.placeholder}
|
placeholder=${this.placeholder}
|
||||||
maxlength=${this.maxlength}
|
maxlength=${this.maxlength}
|
||||||
minlength=${this.minlength}
|
minlength=${this.minlength}
|
||||||
|
@ -365,26 +393,129 @@ class Input extends Component {
|
||||||
autofocus=${this.autofocus}
|
autofocus=${this.autofocus}
|
||||||
:value=${this.value}
|
:value=${this.value}
|
||||||
/>
|
/>
|
||||||
${this.closeable && this.value ? this.renderClose() : ''}
|
${this.clearable && this.value ? this.renderClose() : ''}
|
||||||
${this.icon
|
${this.icon
|
||||||
? html`<wc-icon class="icon" name=${this.icon} />`
|
? html`<wc-icon class="icon" name=${this.icon} />`
|
||||||
: html`<slot class="append" name="append" />`}
|
: html`<slot class="append" name="append" />`}
|
||||||
|
<div class=${classes} ref="suggestion" #animation=${ANIMATION}>
|
||||||
|
<wc-scroll ref="scroller">
|
||||||
|
<ul class="list" @click=${this.onClick} ref="list">
|
||||||
|
${this.#list.map(
|
||||||
|
(li, idx) =>
|
||||||
|
html`<li
|
||||||
|
focus=${this.#selectIndex === idx ? true : null}
|
||||||
|
index=${idx}
|
||||||
|
>
|
||||||
|
${li.value}
|
||||||
|
</li>`
|
||||||
|
)}
|
||||||
|
</ul>
|
||||||
|
</wc-scroll>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
`
|
`
|
||||||
}
|
}
|
||||||
|
onCompositionstart() {
|
||||||
|
this.#isComposing = true
|
||||||
|
}
|
||||||
|
onCompositionend() {
|
||||||
|
this.#isComposing = false
|
||||||
|
this.filterSuggestList()
|
||||||
|
}
|
||||||
|
filterSuggestList() {
|
||||||
|
if (!this.#originList.length) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (!this.value) {
|
||||||
|
this.#list = this.#originList
|
||||||
|
} else {
|
||||||
|
this.#list = this.#originList.filter(li =>
|
||||||
|
li.value.startsWith(this.value)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if (!this.#listShowing) {
|
||||||
|
this.#listShowing = true
|
||||||
|
this.$refs.suggestion.$animate()
|
||||||
|
}
|
||||||
|
this.$requestUpdate()
|
||||||
|
}
|
||||||
onInput(e) {
|
onInput(e) {
|
||||||
|
let { lazy } = this
|
||||||
this.value = e.currentTarget.value
|
this.value = e.currentTarget.value
|
||||||
|
if (lazy && Date.now() - this.#stamp < lazy) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.#stamp = Date.now()
|
||||||
|
if (!this.#isComposing) {
|
||||||
|
this.filterSuggestList()
|
||||||
|
this.$emit('fetch-suggest', {
|
||||||
|
value: this.value,
|
||||||
|
send: list => {
|
||||||
|
this.#originList = this.#list = list
|
||||||
|
this.$requestUpdate()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onClick(e) {
|
||||||
|
let index = e.target.getAttribute('index')
|
||||||
|
this.value = this.#list[index].value
|
||||||
|
this.#list = [this.#list[index]]
|
||||||
|
this.$refs.suggestion.$animate(true)
|
||||||
|
this.#listShowing = false
|
||||||
}
|
}
|
||||||
onClickClose() {
|
onClickClose() {
|
||||||
this.$refs.input.value = ''
|
this.$refs.input.value = ''
|
||||||
this.value = ''
|
this.value = ''
|
||||||
|
if (this.#originList.length) {
|
||||||
|
this.filterSuggestList()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
onChange() {
|
onChange() {
|
||||||
this.$emit('change')
|
this.$emit('change')
|
||||||
}
|
}
|
||||||
onKeyDown(e) {
|
onKeyDown(e) {
|
||||||
|
let { lazy, minlength, value } = this
|
||||||
if (e.keyCode === 13) {
|
if (e.keyCode === 13) {
|
||||||
this.$emit('submit')
|
e.preventDefault()
|
||||||
|
if (this.#selectIndex > -1 && this.#listShowing) {
|
||||||
|
this.value = this.#list[this.#selectIndex].value
|
||||||
|
this.#list = [this.#list[this.#selectIndex]]
|
||||||
|
this.#selectIndex = 0
|
||||||
|
this.$requestUpdate()
|
||||||
|
this.$refs.suggestion.$animate(true)
|
||||||
|
this.#listShowing = false
|
||||||
|
return this.$emit('select') //输入建议存在,则第1次回车的时候, 不触发提交
|
||||||
|
}
|
||||||
|
if (lazy && Date.now() - this.#stamp < lazy) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.#stamp = Date.now()
|
||||||
|
if (minlength && value.length < minlength) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return this.$emit('submit')
|
||||||
|
}
|
||||||
|
if (e.keyCode === 38 || e.keyCode === 40) {
|
||||||
|
e.preventDefault()
|
||||||
|
let step = e.keyCode === 38 ? -1 : 1
|
||||||
|
|
||||||
|
this.#selectIndex += step
|
||||||
|
if (this.#selectIndex < 0) {
|
||||||
|
this.#selectIndex = 0
|
||||||
|
}
|
||||||
|
if (this.#selectIndex > this.#list.length - 1) {
|
||||||
|
this.#selectIndex = this.#list.length - 1
|
||||||
|
}
|
||||||
|
let target = this.$refs.list.children[this.#selectIndex]
|
||||||
|
this.$refs.scroller.scrollTop = target.offsetTop - 150
|
||||||
|
this.$requestUpdate()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onFocus() {
|
||||||
|
if (!this.#listShowing) {
|
||||||
|
this.#listShowing = true
|
||||||
|
this.$refs.suggestion.$animate()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mounted() {
|
mounted() {
|
||||||
|
@ -392,6 +523,10 @@ class Input extends Component {
|
||||||
// 火狐浏览器需要手动focus()才能聚焦成功
|
// 火狐浏览器需要手动focus()才能聚焦成功
|
||||||
nextTick(_ => this.$refs.input.focus())
|
nextTick(_ => this.$refs.input.focus())
|
||||||
}
|
}
|
||||||
|
outsideClick(this, () => {
|
||||||
|
this.#listShowing = false
|
||||||
|
this.$refs.suggestion.$animate(true)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue