Skip to content

fix(PinInput): paste only numeric text in numeric mode#2516

Merged
zernonia merged 3 commits into
unovue:v2from
kricsleo:feat/only-paste-numeric-text
Mar 16, 2026
Merged

fix(PinInput): paste only numeric text in numeric mode#2516
zernonia merged 3 commits into
unovue:v2from
kricsleo:feat/only-paste-numeric-text

Conversation

@kricsleo

@kricsleo kricsleo commented Mar 15, 2026

Copy link
Copy Markdown
Collaborator

🔗 Linked issue

resolves #2513

❓ Type of change

  • 📖 Documentation (updates to the documentation, readme or JSdoc annotations)
  • 🐞 Bug fix (a non-breaking change that fixes an issue)
  • 👌 Enhancement (improving an existing functionality like performance)
  • ✨ New feature (a non-breaking change that adds functionality)
  • 🧹 Chore (updates to the build process or auxiliary tools and libraries)
  • ⚠️ Breaking change (fix or feature that would cause existing functionality to change)

📚 Description

📸 Screenshots (if appropriate)

📝 Checklist

  • I have linked an issue or discussion.
  • I have updated the documentation accordingly.

Summary by CodeRabbit

  • Bug Fixes

    • Improved PinInput paste handling to strip non-numeric characters in numeric mode, correctly populate boxes, maintain caret/focus behavior, and emit completion events when fully filled.
  • Tests

    • Added comprehensive tests covering mixed alphanumeric pastes, numeric extraction, full-population completion events, caret/box population behavior, and both default and numeric input variants.

@coderabbitai

coderabbitai Bot commented Mar 15, 2026

Copy link
Copy Markdown
Contributor
📝 Walkthrough

Walkthrough

Sanitizes pasted text for PinInput when type="number" by stripping non-digits before populating inputs; updates input handling logic and adds tests covering mixed alphanumeric paste scenarios and complete-event behavior for both default and numeric variants.

Changes

Cohort / File(s) Summary
PinInput tests
packages/core/src/PinInput/PinInput.test.ts
Adds tests for paste handling: mixed alphanumeric paste fills only digits, full-population emits complete event, and pasting into subsequent inputs behaves correctly for default and type="number" variants.
PinInput input logic
packages/core/src/PinInput/PinInputInput.vue
Replaces Array.from with spread for inputElements; introduces NUMBER_REG and NON_NUMBER_REG; strips non-digits from clipboard data in numeric mode; updates handleInput, handlePaste, and handleMultipleCharacter flows to skip non-numeric characters without advancing input slots and to parse numeric values appropriately.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Poem

🐰 I nibbled through the paste tonight,

digits kept, the rest took flight.
Boxes filled in tidy rows,
no stray letters left to pose.
Hop, sanitize — and off I go! ✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: fixing paste behavior in PinInput to handle only numeric text when type="number", matching the core objective.
Linked Issues check ✅ Passed The PR implements the required fix: decoupling counters in handleMultipleCharacter to skip non-numeric characters without advancing input index, and stripping non-numeric characters during paste handling in numeric mode.
Out of Scope Changes check ✅ Passed All changes are directly related to fixing numeric paste handling in PinInput; the test additions validate the fix, and the implementation changes directly address issue #2513 requirements.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
📝 Coding Plan
  • Generate coding plan for human review comments

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@pkg-pr-new

pkg-pr-new Bot commented Mar 15, 2026

Copy link
Copy Markdown

Open in StackBlitz

npm i https://pkg.pr.new/reka-ui@2516

commit: 84d2168

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/core/src/PinInput/PinInputInput.vue (1)

147-147: ⚠️ Potential issue | 🟠 Major

Type mismatch: Strings stored instead of numbers in numeric mode.

handleMultipleCharacter stores string values directly into tempModelValue, but when type="number", the model value type is number[] per the PinInputValue<'number'> definition. Compare with updateModelValueAt (line 175) which correctly converts to numbers with tempModelValue[index] = num.

This will cause the complete event to emit ['1', '2', '3', '4', '5'] (strings) instead of [1, 2, 3, 4, 5] (numbers) when pasting numeric characters.

🐛 Proposed fix to convert values in numeric mode
     if (context.isNumericMode.value && !/^\d*$/.test(value))
       continue

-    tempModelValue[i] = value
+    tempModelValue[i] = context.isNumericMode.value ? +value : value
     input.focus()
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/core/src/PinInput/PinInputInput.vue` at line 147,
handleMultipleCharacter currently assigns string characters into tempModelValue
(tempModelValue[i] = value), causing type mismatch when props.type === 'number';
change the assignment to convert the value to a number in numeric mode (e.g.,
tempModelValue[i] = props.type === 'number' ? Number(value) : value) following
the same conversion pattern used in updateModelValueAt so the emitted
model/complete events provide number[] for PinInputValue<'number'>.
🧹 Nitpick comments (1)
packages/core/src/PinInput/PinInputInput.vue (1)

144-145: Consider: residual coupled-counter logic for non-paste paths.

This check is now unreachable for paste operations (since handlePaste pre-filters non-digits). However, handleMultipleCharacter is also called from handleInput (line 36) for multi-character input events (e.g., autofill). If such input contains mixed characters in numeric mode, the same "counter coupling" bug from issue #2513 could manifest there.

Low priority since autofill typically sends appropriate data to numeric inputs, but consider applying consistent sanitization at the handleMultipleCharacter entry point for robustness.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/core/src/PinInput/PinInputInput.vue` around lines 144 - 145,
handleMultipleCharacter can receive multi-char input from handleInput (e.g.,
autofill) and currently relies on an unreachable numeric check used only for
paste paths; add explicit sanitization at the start of handleMultipleCharacter:
when context.isNumericMode.value is true, filter the incoming value to digits
only (same /^\d*/ logic) and early-return or transform the value before
proceeding, so the counter-coupling bug from mixed characters is avoided; update
references in handleInput and handlePaste to pass the sanitized value or rely on
the new guard in handleMultipleCharacter.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/core/src/PinInput/PinInput.test.ts`:
- Around line 287-289: Test is asserting string digits while PinInput with
type="number" should emit number[]; update the expectation in PinInput.test.ts
to assert numeric array [1, 2, 3, 4, 5] (i.e., change
expect(wrapper.emitted('complete')?.[0]?.[0]).toStrictEqual(['1','2','3','4','5'])
to expect numbers) and ensure this aligns with the fix in
handleMultipleCharacter so wrapper.emitted('complete') returns number[] for the
'complete' event.

---

Outside diff comments:
In `@packages/core/src/PinInput/PinInputInput.vue`:
- Line 147: handleMultipleCharacter currently assigns string characters into
tempModelValue (tempModelValue[i] = value), causing type mismatch when
props.type === 'number'; change the assignment to convert the value to a number
in numeric mode (e.g., tempModelValue[i] = props.type === 'number' ?
Number(value) : value) following the same conversion pattern used in
updateModelValueAt so the emitted model/complete events provide number[] for
PinInputValue<'number'>.

---

Nitpick comments:
In `@packages/core/src/PinInput/PinInputInput.vue`:
- Around line 144-145: handleMultipleCharacter can receive multi-char input from
handleInput (e.g., autofill) and currently relies on an unreachable numeric
check used only for paste paths; add explicit sanitization at the start of
handleMultipleCharacter: when context.isNumericMode.value is true, filter the
incoming value to digits only (same /^\d*/ logic) and early-return or transform
the value before proceeding, so the counter-coupling bug from mixed characters
is avoided; update references in handleInput and handlePaste to pass the
sanitized value or rely on the new guard in handleMultipleCharacter.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 24f2bb74-8f44-402d-b3d9-e7ef16fd8122

📥 Commits

Reviewing files that changed from the base of the PR and between 1c36b3c and bb58fb0.

📒 Files selected for processing (2)
  • packages/core/src/PinInput/PinInput.test.ts
  • packages/core/src/PinInput/PinInputInput.vue

Comment thread packages/core/src/PinInput/PinInput.test.ts

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/core/src/PinInput/PinInputInput.vue (1)

140-155: ⚠️ Potential issue | 🟠 Major

Decouple value cursor from input cursor to fully prevent numeric shifting

Line 144–150 still advances the input slot when a non-numeric character is skipped, so multi-character non-paste paths can still misalign digits in numeric mode.

Proposed fix
 function handleMultipleCharacter(values: string) {
   const tempModelValue = [...context.currentModelValue.value] as typeof context.currentModelValue.value
-  const initialIndex = values.length >= inputElements.value.length ? 0 : props.index
-  const lastIndex = Math.min(initialIndex + values.length, inputElements.value.length)
-  for (let i = initialIndex; i < lastIndex; i++) {
-    const input = inputElements.value[i]
-    const value = values[i - initialIndex]
-    if (context.isNumericMode.value) {
-      const num = Number.parseInt(value)
-      if (Number.isNaN(num))
-        continue
-      tempModelValue[i] = num
-    }
-    else {
-      tempModelValue[i] = value
-    }
-    input.focus()
-  }
+  const normalizedValues = context.isNumericMode.value
+    ? values.replace(NON_NUMBER_REG, '')
+    : values
+  const initialIndex = normalizedValues.length >= inputElements.value.length ? 0 : props.index
+
+  let inputIndex = initialIndex
+  let valueIndex = 0
+  while (inputIndex < inputElements.value.length && valueIndex < normalizedValues.length) {
+    const value = normalizedValues[valueIndex]
+    const input = inputElements.value[inputIndex]
+    tempModelValue[inputIndex] = context.isNumericMode.value
+      ? Number(value)
+      : value
+    input?.focus()
+    inputIndex++
+    valueIndex++
+  }
   context.modelValue.value = tempModelValue
-  inputElements.value[lastIndex]?.focus()
+  inputElements.value[inputIndex]?.focus()
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/core/src/PinInput/PinInputInput.vue` around lines 140 - 155,
handleMultipleCharacter currently advances the input slot even when a
non-numeric character is skipped, causing misalignment in numeric mode; change
the loop to decouple the value cursor from the input cursor by using two indices
(e.g., slotIndex for inputElements and valueIndex for values) and iterate with a
while (slotIndex < lastIndex && valueIndex < values.length) so that when
context.isNumericMode.value and Number.parseInt(value) is NaN you only advance
valueIndex (skip that character) and do NOT increment slotIndex, and only
increment slotIndex when you successfully write into tempModelValue; keep using
tempModelValue, inputElements, initialIndex/lastIndex and then write back to
context.currentModelValue as before.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@packages/core/src/PinInput/PinInputInput.vue`:
- Around line 140-155: handleMultipleCharacter currently advances the input slot
even when a non-numeric character is skipped, causing misalignment in numeric
mode; change the loop to decouple the value cursor from the input cursor by
using two indices (e.g., slotIndex for inputElements and valueIndex for values)
and iterate with a while (slotIndex < lastIndex && valueIndex < values.length)
so that when context.isNumericMode.value and Number.parseInt(value) is NaN you
only advance valueIndex (skip that character) and do NOT increment slotIndex,
and only increment slotIndex when you successfully write into tempModelValue;
keep using tempModelValue, inputElements, initialIndex/lastIndex and then write
back to context.currentModelValue as before.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: e5d9d0f0-6fa9-41cc-9da0-f7e8c4be9bab

📥 Commits

Reviewing files that changed from the base of the PR and between bb58fb0 and 84d2168.

📒 Files selected for processing (2)
  • packages/core/src/PinInput/PinInput.test.ts
  • packages/core/src/PinInput/PinInputInput.vue
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/core/src/PinInput/PinInput.test.ts

@zernonia zernonia merged commit bf7190a into unovue:v2 Mar 16, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature]: PinInputInput should sanitize pasted text based on input type

2 participants