Skip to content

Commit

Permalink
Fixes text-mask#289: caretNumberMask caret positioning bug. (text-m…
Browse files Browse the repository at this point in the history
  • Loading branch information
mzedeler authored and lozjackson committed Mar 22, 2017
1 parent d63ad3d commit c714708
Show file tree
Hide file tree
Showing 4 changed files with 31 additions and 3 deletions.
13 changes: 13 additions & 0 deletions common/testParameters.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,19 @@ export default _.filter((t) => t, [{
}, {
line: getLineNumber(),

previousConformedValue: '$12,345',
previousPlaceholder: '$__,___',
rawValue: '$12,45',
mask: ['$', /\d/, ',', /\d/, /\d/, /\d/],
currentCaretPosition: 4,

conformedValue: '$1,245',
adjustedCaretPosition: 4,

// only: true
}, {
line: getLineNumber(),

previousConformedValue: '___',
rawValue: '1___',
mask: [/\d/, /\d/, /\d/],
Expand Down
14 changes: 13 additions & 1 deletion core/src/adjustCaretPosition.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ const emptyString = ''

export default function adjustCaretPosition({
previousConformedValue = emptyString,
previousPlaceholder = '',
currentCaretPosition = 0,
conformedValue,
rawValue,
Expand Down Expand Up @@ -53,6 +54,7 @@ export default function adjustCaretPosition({
)

let startingSearchIndex = 0
let targetIsMaskMovingLeft

if (possiblyHasRejectedChar) {
startingSearchIndex = currentCaretPosition - editLength
Expand All @@ -79,6 +81,15 @@ export default function adjustCaretPosition({
// value and the one we want to adjust the caret close to
const targetChar = intersection[intersection.length - 1]

// Detect if `targetChar` is a mask character and has moved to the left
targetIsMaskMovingLeft = (
previousPlaceholder[intersection.length - 1] !== undefined &&
placeholder[intersection.length - 2] !== undefined &&
previousPlaceholder[intersection.length - 1] !== placeholderChar &&
previousPlaceholder[intersection.length - 1] !== placeholder[intersection.length - 1] &&
previousPlaceholder[intersection.length - 1] === placeholder[intersection.length - 2]
)

// It is possible that `targetChar` will appear multiple times in the conformed value.
// We need to know not to select a character that looks like our target character from the placeholder or
// the piped characters, so we inspect the piped characters and the placeholder to see if they contain
Expand Down Expand Up @@ -169,11 +180,12 @@ export default function adjustCaretPosition({
}
} else {
// In case of deletion, we rewind.
for (let i = startingSearchIndex; i >= 0; i--) {
for (let i = startingSearchIndex + (targetIsMaskMovingLeft ? 1 : 0); i >= 0; i--) {
// If we're deleting, we stop the caret right before the placeholder character.
// For example, for mask `(111) 11`, current conformed input `(456) 86`. If user
// modifies input to `(456 86`. That is, they deleted the `)`, we place the caret
// right after the first `6`

if (
// If we're deleting, we can position the caret right before the placeholder character
placeholder[i - 1] === placeholderChar ||
Expand Down
6 changes: 4 additions & 2 deletions core/src/createTextMaskInputElement.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,8 @@ export default function createTextMaskInputElement(config) {
// `selectionStart` indicates to us where the caret position is after the user has typed into the input
const {selectionStart: currentCaretPosition} = inputElement

// We need to know what the `previousConformedValue` is from the previous `update` call
const {previousConformedValue} = state
// We need to know what the `previousConformedValue` and `previousPlaceholder` is from the previous `update` call
const {previousConformedValue, previousPlaceholder} = state

let caretTrapIndexes

Expand Down Expand Up @@ -142,6 +142,7 @@ export default function createTextMaskInputElement(config) {
// the caret position. `adjustCaretPosition` will tell us.
const adjustedCaretPosition = adjustCaretPosition({
previousConformedValue,
previousPlaceholder: previousPlaceholder,
conformedValue: finalConformedValue,
placeholder,
rawValue: safeRawValue,
Expand All @@ -156,6 +157,7 @@ export default function createTextMaskInputElement(config) {
const inputElementValue = (inputValueShouldBeEmpty) ? emptyString : finalConformedValue

state.previousConformedValue = inputElementValue // store value for access for next time
state.previousPlaceholder = placeholder

// In some cases, this `update` method will be repeatedly called with a raw value that has already been conformed
// and set to `inputElement.value`. The below check guards against needlessly readjusting the input state.
Expand Down
1 change: 1 addition & 0 deletions core/test/adjustCaretPosition.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ describe('adjustCaretPosition', () => {
body: () => {
expect(adjustCaretPosition({
previousConformedValue: test.previousConformedValue,
previousPlaceholder: test.previousPlaceholder,
conformedValue: test.conformedValue,
rawValue: test.rawValue,
placeholder: convertMaskToPlaceholder(test.mask),
Expand Down

0 comments on commit c714708

Please sign in to comment.