diff --git a/src/form/input.js b/src/form/input.js index 8cefcaa..3ddb88a 100644 --- a/src/form/input.js +++ b/src/form/input.js @@ -4,15 +4,27 @@ * @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 '../scroll/index.js' +const ANIMATION = { + duration: 100, + custom: [{ transform: 'scaleY(0)' }, { transform: 'scaleY(1)' }] +} class Input extends Component { static props = { readOnly: false, autofocus: false, disabled: false, - closeable: false, + clearable: false, icon: '', size: 'l', placeholder: '', @@ -25,11 +37,16 @@ class Input extends Component { }, lazy: 0 // 并发拦截时间, 单位毫秒 } - + #list = [] + #originList = [] + #isComposing = false + #selectIndex = -1 + #listShowing = false + #stamp = 0 static styles = [ css` :host { - overflow: hidden; + position: relative; display: inline-flex; min-width: 228px; height: 36px; @@ -109,25 +126,28 @@ class Input extends Component { .suggestion { overflow: hidden; - display: none; - position: fixed; - z-index: 10240; + position: absolute; + z-index: 1; left: 0; - top: 0; - width: 200px; + top: calc(100% + 4px); + width: 100%; height: auto; max-height: 200px; - min-height: 46px; padding: 4px 0; border-radius: 4px; - background: var(--color-plain-1); + // background: var(--color-plain-1); 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 { width: 100%; } - &.show { - display: flex; + &.hide { + display: none; } li { @@ -349,6 +369,11 @@ class Input extends Component { } render() { + let classes = classMap({ + suggestion: true, + hide: !this.#list.length + }) + return html`
@@ -356,7 +381,10 @@ class Input extends Component { ref="input" @input=${this.onInput} @change=${this.onChange} + @compositionstart=${this.onCompositionstart} + @compositionend=${this.onCompositionend} @keydown=${this.onKeyDown} + @focus=${this.onFocus} placeholder=${this.placeholder} maxlength=${this.maxlength} minlength=${this.minlength} @@ -365,26 +393,129 @@ class Input extends Component { autofocus=${this.autofocus} :value=${this.value} /> - ${this.closeable && this.value ? this.renderClose() : ''} + ${this.clearable && this.value ? this.renderClose() : ''} ${this.icon ? html`` : html``} +
+ +
    + ${this.#list.map( + (li, idx) => + html`
  • + ${li.value} +
  • ` + )} +
+
+
` } + 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) { + let { lazy } = this 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() { this.$refs.input.value = '' this.value = '' + if (this.#originList.length) { + this.filterSuggestList() + } } onChange() { this.$emit('change') } onKeyDown(e) { + let { lazy, minlength, value } = this 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() { @@ -392,6 +523,10 @@ class Input extends Component { // 火狐浏览器需要手动focus()才能聚焦成功 nextTick(_ => this.$refs.input.focus()) } + outsideClick(this, () => { + this.#listShowing = false + this.$refs.suggestion.$animate(true) + }) } }