mirror of
https://gitee.com/vant-contrib/vant.git
synced 2025-05-23 23:19:15 +08:00
chore(Field): extract utils (#8291)
This commit is contained in:
parent
9864dac170
commit
8d64c3b590
@ -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 () => {
|
||||||
|
@ -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 };
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user