naive-ui
Version:
A Vue 3 Component Library. Fairly Complete, Theme Customizable, Uses TypeScript, Fast
322 lines • 10.5 kB
JavaScript
var __rest = this && this.__rest || function (s, e) {
var t = {};
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) t[p] = s[p];
if (s != null && typeof Object.getOwnPropertySymbols === "function") for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) t[p[i]] = s[p[i]];
}
return t;
};
import { pxfy, repeat } from 'seemly';
import { useMergedState } from 'vooks';
import { computed, defineComponent, h, ref, toRef } from 'vue';
import { useConfig, useFormItem, useRtl, useTheme, useThemeClass } from "../../_mixins/index.mjs";
import { call, createKey, isArrayShallowEqual, resolveSlotWithTypedProps } from "../../_utils/index.mjs";
import { NInput } from "../../input/index.mjs";
import inputOtpLight from "../styles/light.mjs";
import style from "./styles/index.cssr.mjs";
export const inputOtpProps = Object.assign(Object.assign({}, useTheme.props), {
defaultValue: {
type: Array,
default: []
},
value: Array,
length: {
type: Number,
default: 6
},
block: Boolean,
size: String,
disabled: Boolean,
mask: Boolean,
readonly: Boolean,
status: String,
gap: [String, Number],
placeholder: {
type: String,
default: ''
},
allowInput: Function,
onBlur: [Function, Array],
onFocus: [Function, Array],
'onUpdate:value': [Function, Array],
onUpdateValue: [Function, Array],
onFinish: [Function, Array]
});
export default defineComponent({
name: 'InputOtp',
props: inputOtpProps,
slots: Object,
setup(props) {
const {
mergedClsPrefixRef,
mergedRtlRef,
inlineThemeDisabled
} = useConfig(props);
const themeRef = useTheme('InputOtp', '-input-otp', style, inputOtpLight, props, mergedClsPrefixRef);
const rtlEnabledRef = useRtl('InputOtp', mergedRtlRef, mergedClsPrefixRef);
// form-item
const formItem = useFormItem(props);
const {
mergedSizeRef,
mergedDisabledRef,
mergedStatusRef
} = formItem;
const cssVarsRef = computed(() => {
const {
value: size
} = mergedSizeRef;
const {
gap: propGap
} = props;
const {
self: {
[createKey('inputWidth', size)]: inputWidth,
[createKey('gap', size)]: gap
}
} = themeRef.value;
return {
'--n-gap': propGap === undefined ? gap : typeof propGap === 'number' ? pxfy(propGap) : propGap,
'--n-input-width': inputWidth
};
});
const themeClassHandle = inlineThemeDisabled ? useThemeClass('input-otp', computed(() => {
const {
value: size
} = mergedSizeRef;
return size[0];
}), cssVarsRef, props) : undefined;
const uncontrolledValueRef = ref(props.defaultValue);
const controlledValueRef = toRef(props, 'value');
const mergedValueRef = useMergedState(controlledValueRef, uncontrolledValueRef);
const inputRefList = ref([]);
const inputTypeRef = computed(() => props.mask ? 'password' : 'text');
const handleFocus = (e, index) => {
// If it's focused from other input element inside the component, returns
if (inputRefList === null || inputRefList === void 0 ? void 0 : inputRefList.value.some(inputInst => inputInst.inputElRef === e.relatedTarget)) {
return;
}
const {
onFocus
} = props;
if (onFocus) {
call(onFocus, e, index);
}
const {
nTriggerFormFocus
} = formItem;
nTriggerFormFocus();
};
const handleBlur = (e, index) => {
// If it's blured to other input element inside the component, returns
if (inputRefList === null || inputRefList === void 0 ? void 0 : inputRefList.value.some(inputInst => inputInst.inputElRef === e.relatedTarget)) {
return;
}
const {
onBlur
} = props;
const {
nTriggerFormBlur
} = formItem;
if (onBlur) call(onBlur, e, index);
nTriggerFormBlur();
};
const focusOnChar = charIndex => {
if (charIndex >= props.length) return;
if (charIndex < 0) return;
inputRefList === null || inputRefList === void 0 ? void 0 : inputRefList.value[charIndex].focus();
inputRefList === null || inputRefList === void 0 ? void 0 : inputRefList.value[charIndex].select();
};
const focusOnNextChar = currentIndex => {
if (currentIndex >= props.length - 1) {
return;
}
focusOnChar(currentIndex + 1);
};
const focusOnPrevChar = currentIndex => {
if (currentIndex <= 0) {
return;
}
focusOnChar(currentIndex - 1);
};
const justifyValue = value => {
const justifiedValue = value ? Array.from(value) : [];
const length = props.length;
while (justifiedValue.length > length) {
justifiedValue.pop();
}
while (justifiedValue.length < length) {
justifiedValue.push('');
}
return justifiedValue;
};
function doUpdateValue(value, meta) {
const {
nTriggerFormInput,
nTriggerFormChange
} = formItem;
if (isArrayShallowEqual(value, mergedValueRef.value)) {
nTriggerFormInput();
return;
}
const {
'onUpdate:value': _onUpdateValue,
onUpdateValue,
length,
onFinish
} = props;
if (_onUpdateValue) call(_onUpdateValue, value, meta);
if (onUpdateValue) call(onUpdateValue, value, meta);
if (value.filter(v => v).length === length && onFinish) {
call(onFinish, value);
}
uncontrolledValueRef.value = value;
nTriggerFormInput();
nTriggerFormChange();
}
const handlePaste = (e, index) => {
if (props.readonly || mergedDisabledRef.value) {
return;
}
e.preventDefault();
const {
clipboardData
} = e;
const text = clipboardData === null || clipboardData === void 0 ? void 0 : clipboardData.getData('text');
if (!text) return;
const currentValue = justifyValue(mergedValueRef.value);
let startIndex = index;
const allowInput = props.allowInput;
let pasteApplied = false;
let appendedText = '';
for (let i = 0; i < text.length; ++i) {
if (allowInput && !allowInput(text[i], startIndex, currentValue)) {
continue;
}
pasteApplied = true;
currentValue[startIndex] = text[i];
appendedText += text[i];
startIndex++;
if (startIndex >= currentValue.length) {
break;
}
}
if (pasteApplied) {
focusOnChar(startIndex);
doUpdateValue(currentValue, {
diff: appendedText,
index: startIndex,
source: 'paste'
});
}
};
const handleKeydown = (e, index) => {
if (mergedDisabledRef.value) return;
const keyCode = e.code || e.key;
const currentValue = justifyValue(mergedValueRef.value);
if (keyCode === 'Backspace' && !props.readonly) {
e.preventDefault();
currentValue[Math.max(index, 0)] = '';
doUpdateValue(currentValue, {
diff: '',
index,
source: 'delete'
});
focusOnPrevChar(index);
} else if (keyCode === 'ArrowLeft') {
e.preventDefault();
focusOnPrevChar(index);
} else if (keyCode === 'ArrowRight') {
e.preventDefault();
focusOnNextChar(index);
}
};
const handleInput = (value, index) => {
const currentValue = justifyValue(mergedValueRef.value);
const currentValueAtIndex = currentValue[index];
const diff = value.replace(currentValueAtIndex, '');
const char = diff[diff.length - 1] || value[value.length - 1] || '';
const allowInput = props.allowInput;
if (allowInput && !allowInput(char, index, currentValue)) {
return;
}
currentValue[index] = char;
doUpdateValue(currentValue, {
diff: char,
index,
source: 'input'
});
focusOnNextChar(index);
};
const getTemplateEvents = index => {
return {
onInput: value => handleInput(value, index),
onPaste: event => handlePaste(event, index),
onKeydown: event => handleKeydown(event, index),
onFocus: event => handleFocus(event, index),
onBlur: event => handleBlur(event, index)
};
};
return {
mergedTheme: themeRef,
perItemValueArray: computed(() => justifyValue(mergedValueRef.value)),
mergedClsPrefix: mergedClsPrefixRef,
inputRefList,
inputType: inputTypeRef,
rtlEnabled: rtlEnabledRef,
mergedStatus: mergedStatusRef,
mergedDisabled: mergedDisabledRef,
cssVars: inlineThemeDisabled ? undefined : cssVarsRef,
themeClass: themeClassHandle === null || themeClassHandle === void 0 ? void 0 : themeClassHandle.themeClass,
getTemplateEvents,
onRender: themeClassHandle === null || themeClassHandle === void 0 ? void 0 : themeClassHandle.onRender
};
},
render() {
const {
mergedTheme,
mergedClsPrefix,
perItemValueArray,
size,
placeholder,
mergedDisabled,
mergedStatus,
readonly,
inputType,
$slots,
getTemplateEvents,
themeClass,
onRender
} = this;
onRender === null || onRender === void 0 ? void 0 : onRender();
return h("div", {
style: this.cssVars,
class: [`${mergedClsPrefix}-input-otp`, themeClass, this.rtlEnabled && `${mergedClsPrefix}-input-otp--rtl`, this.block && `${mergedClsPrefix}-input-otp--block`]
}, repeat(this.length, undefined).map((_, index) => resolveSlotWithTypedProps($slots.default, Object.assign({
index,
value: perItemValueArray[index],
type: inputType,
size,
placeholder,
disabled: mergedDisabled,
readonly,
status: mergedStatus,
builtinThemeOverrides: {
paddingTiny: '0',
paddingSmall: '0',
paddingMedium: '0',
paddingLarge: '0'
},
theme: mergedTheme.peers.Input,
themeOverrides: mergedTheme.peerOverrides.Input,
ref: el => this.inputRefList[index] = el
}, getTemplateEvents(index)), _a => {
var {
index
} = _a,
restProps = __rest(_a, ["index"]);
return [h(NInput, Object.assign({}, restProps, {
key: index
}))];
})));
}
});