diff --git a/README.md b/README.md index f19bec9..8a3b2e5 100644 --- a/README.md +++ b/README.md @@ -220,6 +220,13 @@ input::placeholder { false Disables all the input fields. + + paste-filter + boolean + false + false + When enabled, filters out invalid characters during paste operations instead of rejecting the entire paste. For "number" input type, only numeric characters are kept. For "letter-numeric" type, only alphanumeric characters are kept. + ## 🤺 Methods diff --git a/src/components/vue3-otp-input.vue b/src/components/vue3-otp-input.vue index 9d1e4d6..dd7bf71 100644 --- a/src/components/vue3-otp-input.vue +++ b/src/components/vue3-otp-input.vue @@ -28,6 +28,7 @@ type Props = { placeholder?: string[]; isDisabled?: boolean; shouldFocusOrder?: boolean; + pasteFilter?: boolean; }; const props = withDefaults(defineProps(), { @@ -40,6 +41,7 @@ const props = withDefaults(defineProps(), { isDisabled: false, placeholder: () => [], conditionalClass: () => [], + pasteFilter: false, }); const emit = defineEmits<{ @@ -112,20 +114,42 @@ const changeCodeAtFocus = (value: number | string) => { // Handle pasted OTP const handleOnPaste = (event: any) => { event.preventDefault(); - const pastedData = event.clipboardData + let pastedData = event.clipboardData .getData("text/plain") - .slice(0, props.numInputs - activeInput.value) .split(""); - if (props.inputType === "number" && !pastedData.join("").match(/^\d+$/)) { - return "Invalid pasted data"; - } - if ( - props.inputType === "letter-numeric" && - !pastedData.join("").match(/^\w+$/) - ) { - return "Invalid pasted data"; + if (props.pasteFilter) { + // Filter characters based on input type when pasteFilter is enabled + if (props.inputType === "number") { + pastedData = pastedData.filter((char: string) => /^\d$/.test(char)); + } else if (props.inputType === "letter-numeric") { + pastedData = pastedData.filter((char: string) => /^\w$/.test(char)); + } + + // If no valid characters remain after filtering, return early + if (pastedData.length === 0) { + return "No valid characters to paste"; + } + + // Apply length limit after filtering + pastedData = pastedData.slice(0, props.numInputs - activeInput.value); + } else { + // Apply length limit before validation for original behavior + pastedData = pastedData.slice(0, props.numInputs - activeInput.value); + + // Original validation behavior when pasteFilter is disabled + if (props.inputType === "number" && !pastedData.join("").match(/^\d+$/)) { + return "Invalid pasted data"; + } + + if ( + props.inputType === "letter-numeric" && + !pastedData.join("").match(/^\w+$/) + ) { + return "Invalid pasted data"; + } } + // Paste data from focused input onwards const currentCharsInOtp = otp.value.slice(0, activeInput.value); const combinedWithPastedData = currentCharsInOtp.concat(pastedData);