Files
vant/src/number-keyboard/index.js
T

266 lines
6.0 KiB
JavaScript

import { Teleport, Transition } from 'vue';
import { createNamespace } from '../utils';
import { stopPropagation } from '../utils/dom/event';
import { BindEventMixin } from '../mixins/bind-event';
import Key from './Key';
const [createComponent, bem] = createNamespace('number-keyboard');
export default createComponent({
mixins: [
BindEventMixin(function (bind) {
if (this.hideOnClickOutside) {
bind(document.body, 'touchstart', this.onBlur);
}
}),
],
props: {
show: Boolean,
title: String,
zIndex: [Number, String],
teleport: [String, Object],
closeButtonText: String,
deleteButtonText: String,
closeButtonLoading: Boolean,
theme: {
type: String,
default: 'default',
},
modelValue: {
type: String,
default: '',
},
extraKey: {
type: [String, Array],
default: '',
},
maxlength: {
type: [Number, String],
default: Number.MAX_VALUE,
},
transition: {
type: Boolean,
default: true,
},
showDeleteKey: {
type: Boolean,
default: true,
},
hideOnClickOutside: {
type: Boolean,
default: true,
},
safeAreaInsetBottom: {
type: Boolean,
default: true,
},
},
emits: [
'show',
'hide',
'blur',
'input',
'close',
'delete',
'update:modelValue',
],
watch: {
show(val) {
if (!this.transition) {
this.$emit(val ? 'show' : 'hide');
}
},
},
computed: {
keys() {
if (this.theme === 'custom') {
return this.genCustomKeys();
}
return this.genDefaultKeys();
},
},
methods: {
genBasicKeys() {
const keys = [];
for (let i = 1; i <= 9; i++) {
keys.push({ text: i });
}
return keys;
},
genDefaultKeys() {
return [
...this.genBasicKeys(),
{ text: this.extraKey, type: 'extra' },
{ text: 0 },
{
text: this.showDeleteKey ? this.deleteButtonText : '',
type: this.showDeleteKey ? 'delete' : '',
},
];
},
genCustomKeys() {
const keys = this.genBasicKeys();
const { extraKey } = this;
const extraKeys = Array.isArray(extraKey) ? extraKey : [extraKey];
if (extraKeys.length === 1) {
keys.push(
{ text: 0, wider: true },
{ text: extraKey[0], type: 'extra' }
);
} else if (extraKeys.length === 2) {
keys.push(
{ text: extraKey[0], type: 'extra' },
{ text: 0 },
{ text: extraKey[1], type: 'extra' }
);
}
return keys;
},
onBlur() {
this.show && this.$emit('blur');
},
onClose() {
this.$emit('close');
this.onBlur();
},
onAnimationEnd() {
this.$emit(this.show ? 'show' : 'hide');
},
onPress(text, type) {
if (text === '') {
if (type === 'extra') {
this.onBlur();
}
return;
}
const value = this.modelValue;
if (type === 'delete') {
this.$emit('delete');
this.$emit('update:modelValue', value.slice(0, value.length - 1));
} else if (type === 'close') {
this.onClose();
} else if (value.length < this.maxlength) {
this.$emit('input', text);
this.$emit('update:modelValue', value + text);
}
},
genTitle() {
const { title, theme, closeButtonText } = this;
const titleLeft = this.$slots['title-left'];
const showClose = closeButtonText && theme === 'default';
const showTitle = title || showClose || titleLeft;
if (!showTitle) {
return;
}
return (
<div class={bem('header')}>
{titleLeft && <span class={bem('title-left')}>{titleLeft}</span>}
{title && <h2 class={bem('title')}>{title}</h2>}
{showClose && (
<button type="button" class={bem('close')} onClick={this.onClose}>
{closeButtonText}
</button>
)}
</div>
);
},
genKeys() {
return this.keys.map((key) => {
const slots = {};
if (key.type === 'delete') {
slots.default = this.$slots.delete;
}
if (key.type === 'extra') {
slots.default = this.$slots['extra-key'];
}
return (
<Key
v-slots={slots}
key={key.text}
text={key.text}
type={key.type}
wider={key.wider}
color={key.color}
onPress={this.onPress}
/>
);
});
},
genSidebar() {
if (this.theme === 'custom') {
return (
<div class={bem('sidebar')}>
{this.showDeleteKey && (
<Key
v-slots={{ delete: this.$slots.delete }}
large
text={this.deleteButtonText}
type="delete"
onPress={this.onPress}
/>
)}
<Key
large
text={this.closeButtonText}
type="close"
color="blue"
loading={this.closeButtonLoading}
onPress={this.onPress}
/>
</div>
);
}
},
},
render() {
const Title = this.genTitle();
const Content = (
<Transition name={this.transition ? 'van-slide-up' : ''}>
<div
vShow={this.show}
style={{ zIndex: this.zIndex }}
class={bem({ unfit: !this.safeAreaInsetBottom, 'with-title': Title })}
onTouchstart={stopPropagation}
onAnimationend={this.onAnimationEnd}
onWebkitAnimationEnd={this.onAnimationEnd}
>
{Title}
<div class={bem('body')}>
<div class={bem('keys')}>{this.genKeys()}</div>
{this.genSidebar()}
</div>
</div>
</Transition>
);
if (this.teleport) {
return <Teleport to={this.teleport}>{Content}</Teleport>;
}
return Content;
},
});