完成input组件
parent
b6a8996c69
commit
97db6283eb
|
@ -5,7 +5,9 @@
|
||||||
<wc-icon class="icon"></wc-icon>
|
<wc-icon class="icon"></wc-icon>
|
||||||
<slot class="append" name="append"></slot>
|
<slot class="append" name="append"></slot>
|
||||||
|
|
||||||
<ul class="suggestion"></ul>
|
<div class="suggestion">
|
||||||
|
<ul class="list"></ul>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -98,12 +100,19 @@ li {
|
||||||
top: 50px;
|
top: 50px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: auto;
|
height: auto;
|
||||||
|
max-height: 200px;
|
||||||
min-height: 46px;
|
min-height: 46px;
|
||||||
padding: 8px 0;
|
padding: 8px 0;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
background: #fff;
|
background: #fff;
|
||||||
box-shadow: 0 0 3px rgba(0, 0, 0, 0.2);
|
box-shadow: 0 0 3px rgba(0, 0, 0, 0.2);
|
||||||
|
|
||||||
|
.list {
|
||||||
|
overflow: hidden;
|
||||||
|
overflow-y: auto;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
&::after {
|
&::after {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 30px;
|
left: 30px;
|
||||||
|
@ -116,7 +125,7 @@ li {
|
||||||
content: '';
|
content: '';
|
||||||
}
|
}
|
||||||
&.show {
|
&.show {
|
||||||
display: block;
|
display: flex;
|
||||||
}
|
}
|
||||||
|
|
||||||
li {
|
li {
|
||||||
|
@ -129,7 +138,8 @@ li {
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
||||||
&:hover {
|
&:hover,
|
||||||
|
&[focus] {
|
||||||
background: nth($cp, 1);
|
background: nth($cp, 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -225,6 +235,7 @@ export default class Input {
|
||||||
type: 'text',
|
type: 'text',
|
||||||
label: '',
|
label: '',
|
||||||
placeholder: '',
|
placeholder: '',
|
||||||
|
mvidx: null, //下拉列表光标的索引ID
|
||||||
autofocus: false,
|
autofocus: false,
|
||||||
readonly: false,
|
readonly: false,
|
||||||
disabled: false
|
disabled: false
|
||||||
|
@ -311,20 +322,43 @@ export default class Input {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_parseSuggestion() {
|
// 移动光标选择下拉选项
|
||||||
var { list } = this.props
|
_moveSelect(ev) {
|
||||||
if (list && list.length) {
|
ev.preventDefault()
|
||||||
var html = list
|
var step = ev.keyCode === 38 ? -1 : 1
|
||||||
.map((it, i) => `<li data-idx="${i}">${it.value}</li>`)
|
var items = Array.from(this.__LIST__.firstElementChild.children)
|
||||||
.join('')
|
if (this.props.mvidx === null) {
|
||||||
this.__LIST__.innerHTML = html
|
this.props.mvidx = 0
|
||||||
this.__LIST__.classList.toggle('show', true)
|
|
||||||
} else {
|
} else {
|
||||||
this.__LIST__.classList.toggle('show', false)
|
this.props.mvidx += step
|
||||||
}
|
}
|
||||||
|
if (this.props.mvidx < 0) {
|
||||||
|
this.props.mvidx = 0
|
||||||
|
} else if (this.props.mvidx > items.length - 1) {
|
||||||
|
this.props.mvidx = items.length - 1
|
||||||
|
}
|
||||||
|
items.forEach((it, i) => {
|
||||||
|
if (i === this.props.mvidx) {
|
||||||
|
it.setAttribute('focus', '')
|
||||||
|
} else {
|
||||||
|
it.removeAttribute('focus')
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
_moveSelect() {}
|
// 触发列表选择
|
||||||
|
_fetchSelect(idx, ev) {
|
||||||
|
var item = this.props.list[idx]
|
||||||
|
this.value = item.value
|
||||||
|
this.dispatchEvent(
|
||||||
|
new CustomEvent('select', {
|
||||||
|
detail: item
|
||||||
|
})
|
||||||
|
)
|
||||||
|
this._handleChange(ev)
|
||||||
|
this.__LIST__.classList.remove('show')
|
||||||
|
this.props.mvidx = null
|
||||||
|
}
|
||||||
|
|
||||||
mounted() {
|
mounted() {
|
||||||
var prepend = this.__PREPEND__.assignedNodes()
|
var prepend = this.__PREPEND__.assignedNodes()
|
||||||
|
@ -347,23 +381,31 @@ export default class Input {
|
||||||
|
|
||||||
var { type } = this.props
|
var { type } = this.props
|
||||||
|
|
||||||
|
// 回车事件
|
||||||
this._handleSubmit = ev => {
|
this._handleSubmit = ev => {
|
||||||
if (this.disabled || this.readonly) {
|
if (this.disabled || this.readonly) {
|
||||||
ev.cancelBubble = true
|
ev.cancelBubble = true
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// up: 38, down: 40
|
// up: 38, down: 40
|
||||||
log(ev.keyCode)
|
|
||||||
|
|
||||||
if (ev.keyCode === 38 || ev.keyCode === 40) {
|
if (ev.keyCode === 38 || ev.keyCode === 40) {
|
||||||
ev.preventDefault()
|
// 仅普通文本表单, 密码和多行文本框不做响应
|
||||||
return
|
if (this.type === 'text') {
|
||||||
|
return this._moveSelect(ev)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// 回车触发submit事件
|
// 回车触发submit事件
|
||||||
// textarea 要按Ctrl Or Cmd键, 才会触发
|
// textarea 要按Ctrl Or Cmd键, 才会触发
|
||||||
|
if (ev.keyCode === 13) {
|
||||||
|
// 如果是输入建议存在,则第1次回车的时候, 不触发提交
|
||||||
|
if (this.type === 'text' && this.props.mvidx !== null) {
|
||||||
|
return this._fetchSelect(this.props.mvidx, ev)
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
if (
|
if (
|
||||||
ev.keyCode === 13 &&
|
type === 'text' ||
|
||||||
(type === 'text' || (type === 'textarea' && (ev.ctrlKey || ev.metaKey)))
|
(type === 'textarea' && (ev.ctrlKey || ev.metaKey))
|
||||||
) {
|
) {
|
||||||
this.dispatchEvent(
|
this.dispatchEvent(
|
||||||
new CustomEvent('submit', {
|
new CustomEvent('submit', {
|
||||||
|
@ -372,14 +414,16 @@ export default class Input {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 输入状态事件
|
||||||
this._handleChange = ev => {
|
this._handleChange = ev => {
|
||||||
|
ev.preventDefault()
|
||||||
this.dispatchEvent(
|
this.dispatchEvent(
|
||||||
new CustomEvent('fetch-suggestions', {
|
new CustomEvent('fetch-suggestions', {
|
||||||
detail: {
|
detail: {
|
||||||
value: this.value,
|
value: this.value,
|
||||||
send: list => {
|
send: list => {
|
||||||
log('----', list)
|
|
||||||
this.props.list = list
|
this.props.list = list
|
||||||
this._parseSuggestion()
|
this._parseSuggestion()
|
||||||
}
|
}
|
||||||
|
@ -388,49 +432,51 @@ export default class Input {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 渲染建议列表
|
||||||
|
this._parseSuggestion = ev => {
|
||||||
|
var { list } = this.props
|
||||||
|
if (list && list.length) {
|
||||||
|
var html = list
|
||||||
|
.map((it, i) => `<li data-idx="${i}">${it.value}</li>`)
|
||||||
|
.join('')
|
||||||
|
this.__LIST__.firstElementChild.innerHTML = html
|
||||||
|
this.__LIST__.classList.toggle('show', true)
|
||||||
|
} else {
|
||||||
|
this.__LIST__.classList.toggle('show', false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 失去焦点
|
||||||
|
this._handleBlur = ev => {
|
||||||
|
setTimeout(() => {
|
||||||
|
this.__LIST__.classList.remove('show')
|
||||||
|
}, 50)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 选择建议
|
||||||
|
this._handleSelect = ev => {
|
||||||
|
if (ev.target.tagName === 'LI') {
|
||||||
|
this._fetchSelect(ev.target.dataset.idx, ev)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
this.__INPUT__.addEventListener('keydown', this._handleSubmit, false)
|
this.__INPUT__.addEventListener('keydown', this._handleSubmit, false)
|
||||||
|
|
||||||
// 非textarea, 可做输入建议功能
|
// 非textarea, 可做输入建议功能
|
||||||
if (type === 'text') {
|
if (type === 'text') {
|
||||||
this.__INPUT__.addEventListener('input', this._handleChange, false)
|
this.__INPUT__.addEventListener('input', this._handleChange, false)
|
||||||
this.__INPUT__.addEventListener(
|
this.__INPUT__.addEventListener('focus', this._parseSuggestion, false)
|
||||||
'focus',
|
// this.__INPUT__.addEventListener('blur', this._handleBlur, false)
|
||||||
this._parseSuggestion.bind(this),
|
this.__LIST__.addEventListener('click', this._handleSelect, false)
|
||||||
false
|
|
||||||
)
|
|
||||||
this.__INPUT__.addEventListener(
|
|
||||||
'blur',
|
|
||||||
ev => {
|
|
||||||
setTimeout(() => {
|
|
||||||
this.__LIST__.classList.remove('show')
|
|
||||||
}, 50)
|
|
||||||
},
|
|
||||||
false
|
|
||||||
)
|
|
||||||
|
|
||||||
this.__LIST__.addEventListener(
|
|
||||||
'click',
|
|
||||||
ev => {
|
|
||||||
this.__LIST__.classList.remove('show')
|
|
||||||
if (ev.target.tagName === 'LI') {
|
|
||||||
var idx = ev.target.dataset.idx
|
|
||||||
var item = this.props.list[idx]
|
|
||||||
this.value = item.value
|
|
||||||
this.dispatchEvent(
|
|
||||||
new CustomEvent('select', {
|
|
||||||
detail: item
|
|
||||||
})
|
|
||||||
)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
false
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unmount() {
|
unmount() {
|
||||||
this.__INPUT__.removeEventListener('keydown', this._handleSubmit)
|
this.__INPUT__.removeEventListener('keydown', this._handleSubmit)
|
||||||
this.__INPUT__.removeEventListener('input', this._handleChange)
|
this.__INPUT__.removeEventListener('input', this._handleChange)
|
||||||
|
this.__INPUT__.removeEventListener('focus', this._parseSuggestion)
|
||||||
|
this.__INPUT__.removeEventListener('blur', this._handleBlur)
|
||||||
|
this.__LIST__.removeEventListener('click', this._handleSelect)
|
||||||
}
|
}
|
||||||
|
|
||||||
watch(name, old, val) {
|
watch(name, old, val) {
|
||||||
|
@ -472,6 +518,10 @@ export default class Input {
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
|
|
||||||
|
case 'value':
|
||||||
|
this.value = val
|
||||||
|
break
|
||||||
|
|
||||||
case 'readonly':
|
case 'readonly':
|
||||||
case 'disabled':
|
case 'disabled':
|
||||||
if (val === '') {
|
if (val === '') {
|
||||||
|
|
Reference in New Issue