chore(Field): extract utils (#8291)

This commit is contained in:
neverland 2021-03-05 17:17:57 +08:00 committed by GitHub
parent 9864dac170
commit 8d64c3b590
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 114 additions and 91 deletions

View File

@ -7,24 +7,27 @@ import {
reactive, reactive,
PropType, PropType,
onMounted, onMounted,
HTMLAttributes,
} from 'vue'; } from 'vue';
// Utils // Utils
import { import {
isDef, isDef,
trigger,
addUnit, addUnit,
isObject,
isPromise,
isFunction,
UnknownProp, UnknownProp,
resetScroll, resetScroll,
formatNumber, formatNumber,
preventDefault, preventDefault,
createNamespace, createNamespace,
} from '../utils'; } from '../utils';
import { runSyncRule } from './utils'; import {
runSyncRule,
endComposing,
mapInputType,
startComposing,
getRuleMessage,
resizeTextarea,
runRuleValidator,
} from './utils';
// Composables // Composables
import { useParent } from '@vant/use'; import { useParent } from '@vant/use';
@ -159,26 +162,6 @@ export default createComponent({
return props.modelValue; return props.modelValue;
}); });
const runValidator = (value: unknown, rule: FieldRule) =>
new Promise((resolve) => {
const returnVal = rule.validator!(value, rule);
if (isPromise(returnVal)) {
return returnVal.then(resolve);
}
resolve(returnVal);
});
const getRuleMessage = (value: unknown, rule: FieldRule) => {
const { message } = rule;
if (isFunction(message)) {
return message(value, rule);
}
return message || '';
};
const runRules = (rules: FieldRule[]) => const runRules = (rules: FieldRule[]) =>
rules.reduce( rules.reduce(
(promise, rule) => (promise, rule) =>
@ -200,7 +183,7 @@ export default createComponent({
} }
if (rule.validator) { if (rule.validator) {
return runValidator(value, rule).then((result) => { return runRuleValidator(value, rule).then((result) => {
if (result && typeof result === 'string') { if (result && typeof result === 'string') {
state.validateFailed = true; state.validateFailed = true;
state.validateMessage = result; state.validateMessage = result;
@ -370,46 +353,14 @@ export default createComponent({
emit('keypress', event); emit('keypress', event);
}; };
const onCompositionStart = (event: Event) => { const adjustTextareaSize = () => {
event.target!.composing = true;
};
const onCompositionEnd = (event: Event) => {
const { target } = event;
if (target!.composing) {
target!.composing = false;
trigger(target as Element, 'input');
}
};
const adjustSize = () => {
const input = inputRef.value; const input = inputRef.value;
if (props.type === 'textarea' && props.autosize && input) {
if (!(props.type === 'textarea' && props.autosize) || !input) { resizeTextarea(input, props.autosize);
return;
}
input.style.height = 'auto';
let height = input.scrollHeight;
if (isObject(props.autosize)) {
const { maxHeight, minHeight } = props.autosize;
if (maxHeight !== undefined) {
height = Math.min(height, maxHeight);
}
if (minHeight !== undefined) {
height = Math.max(height, minHeight);
}
}
if (height) {
input.style.height = `${height}px`;
} }
}; };
const renderInput = () => { const renderInput = () => {
const disabled = getProp('disabled');
const readonly = getProp('readonly');
const inputAlign = getProp('inputAlign'); const inputAlign = getProp('inputAlign');
if (slots.input) { if (slots.input) {
@ -423,48 +374,38 @@ export default createComponent({
); );
} }
const inputProps = { const inputAttrs = {
ref: inputRef, ref: inputRef,
name: props.name, name: props.name,
rows: props.rows !== undefined ? +props.rows : undefined, rows: props.rows !== undefined ? +props.rows : undefined,
class: bem('control', inputAlign), class: bem('control', inputAlign),
value: props.modelValue, value: props.modelValue,
disabled, disabled: getProp('disabled'),
readonly, readonly: getProp('readonly'),
placeholder: props.placeholder, placeholder: props.placeholder,
autocomplete: props.autocomplete, autocomplete: props.autocomplete,
onBlur, onBlur,
onFocus, onFocus,
onInput, onInput,
onClick: onClickInput, onClick: onClickInput,
onChange: onCompositionEnd, onChange: endComposing,
onKeypress, onKeypress,
onCompositionend: onCompositionEnd, onCompositionend: endComposing,
onCompositionstart: onCompositionStart, onCompositionstart: startComposing,
}; };
const { type } = props; if (props.type === 'textarea') {
return <textarea {...inputAttrs} />;
if (type === 'textarea') {
return <textarea {...inputProps} />;
} }
let inputType = type; return (
let inputMode: HTMLAttributes['inputmode']; <input
{...{
// type="number" is weired in iOS, and can't prevent dot in Android ...mapInputType(props.type),
// so use inputmode to set keyboard in mordern browers ...inputAttrs,
if (type === 'number') { }}
inputType = 'text'; />
inputMode = 'decimal'; );
}
if (type === 'digit') {
inputType = 'tel';
inputMode = 'numeric';
}
return <input type={inputType} inputmode={inputMode} {...inputProps} />;
}; };
const renderLeftIcon = () => { const renderLeftIcon = () => {
@ -556,13 +497,13 @@ export default createComponent({
updateValue(getModelValue()); updateValue(getModelValue());
resetValidation(); resetValidation();
validateWithTrigger('onChange'); validateWithTrigger('onChange');
nextTick(adjustSize); nextTick(adjustTextareaSize);
} }
); );
onMounted(() => { onMounted(() => {
updateValue(getModelValue(), props.formatTrigger); updateValue(getModelValue(), props.formatTrigger);
nextTick(adjustSize); nextTick(adjustTextareaSize);
}); });
return () => { return () => {

View File

@ -1,4 +1,6 @@
import type { FieldRule } from './types'; import { HTMLAttributes, InputHTMLAttributes } from 'vue';
import { trigger, isObject, isPromise, isFunction } from '../utils';
import type { FieldRule, FieldType, FieldAutosizeConfig } from './types';
function isEmptyValue(value: unknown) { function isEmptyValue(value: unknown) {
if (Array.isArray(value)) { if (Array.isArray(value)) {
@ -19,3 +21,83 @@ export function runSyncRule(value: unknown, rule: FieldRule) {
} }
return true; return true;
} }
export function runRuleValidator(value: unknown, rule: FieldRule) {
return new Promise((resolve) => {
const returnVal = rule.validator!(value, rule);
if (isPromise(returnVal)) {
return returnVal.then(resolve);
}
resolve(returnVal);
});
}
export function getRuleMessage(value: unknown, rule: FieldRule) {
const { message } = rule;
if (isFunction(message)) {
return message(value, rule);
}
return message || '';
}
export function startComposing(event: Event) {
event.target!.composing = true;
}
export function endComposing(event: Event) {
const { target } = event;
if (target!.composing) {
target!.composing = false;
trigger(target as Element, 'input');
}
}
export function resizeTextarea(
input: HTMLInputElement,
autosize: true | FieldAutosizeConfig
) {
input.style.height = 'auto';
let height = input.scrollHeight;
if (isObject(autosize)) {
const { maxHeight, minHeight } = autosize;
if (maxHeight !== undefined) {
height = Math.min(height, maxHeight);
}
if (minHeight !== undefined) {
height = Math.max(height, minHeight);
}
}
if (height) {
input.style.height = `${height}px`;
}
}
export function mapInputType(
type: FieldType
): {
type: InputHTMLAttributes['type'];
inputmode?: HTMLAttributes['inputmode'];
} {
// type="number" is weired in iOS, and can't prevent dot in Android
// so use inputmode to set keyboard in mordern browers
if (type === 'number') {
return {
type: 'text',
inputmode: 'decimal',
};
}
if (type === 'digit') {
return {
type: 'tel',
inputmode: 'numeric',
};
}
return { type };
}