From d80d131a928fa915df439872364302fd57f1bf51 Mon Sep 17 00:00:00 2001
From: Eric Kwoka <43540491+ekwoka@users.noreply.github.com>
Date: Sun, 9 Jun 2024 20:20:03 +0400
Subject: [PATCH 1/4] :recycle: Refactors away complexity
---
packages/mask/builds/module.js | 4 +-
packages/mask/src/index.js | 149 ++++--------------
.../cypress/integration/plugins/mask.spec.js | 12 +-
3 files changed, 35 insertions(+), 130 deletions(-)
diff --git a/packages/mask/builds/module.js b/packages/mask/builds/module.js
index e09509cde..c49f3a1c7 100644
--- a/packages/mask/builds/module.js
+++ b/packages/mask/builds/module.js
@@ -1,5 +1,5 @@
-import mask, { stripDown, buildUp } from '../src/index.js'
+import mask, { stripDown } from '../src/index.js'
export default mask
-export { mask, stripDown, buildUp }
+export { mask, stripDown }
diff --git a/packages/mask/src/index.js b/packages/mask/src/index.js
index 3d14e43e0..5d451c15d 100644
--- a/packages/mask/src/index.js
+++ b/packages/mask/src/index.js
@@ -74,7 +74,7 @@ export default function (Alpine) {
}
let setInput = () => {
- lastInputValue = el.value = formatInput(input, template)
+ lastInputValue = el.value = formatInput(template,input)
}
if (shouldRestoreCursor) {
@@ -88,16 +88,6 @@ export default function (Alpine) {
setInput()
}
}
-
- function formatInput(input, template) {
- // Let empty inputs be empty inputs.
- if (input === '') return ''
-
- let strippedDownInput = stripDown(template, input)
- let rebuiltInput = buildUp(template, strippedDownInput)
-
- return rebuiltInput
- }
}).before('model')
}
@@ -109,137 +99,52 @@ export function restoreCursorPosition(el, template, callback) {
let beforeLeftOfCursorBeforeFormatting = unformattedValue.slice(0, cursorPosition)
- let newPosition = buildUp(
- template, stripDown(
+ let newPosition = formatInput(
template, beforeLeftOfCursorBeforeFormatting
- )
).length
el.setSelectionRange(newPosition, newPosition)
}
+let regexes = {
+ '9': /[0-9]/,
+ 'a': /[a-zA-Z]/,
+ '*': /[a-zA-Z0-9]/,
+};
export function stripDown(template, input) {
- let inputToBeStripped = input
let output = ''
- let regexes = {
- '9': /[0-9]/,
- '¤': /[0-9]/,
- 'a': /[a-zA-Z]/,
- '*': /[a-zA-Z0-9]/,
- }
- let wildcardTemplate = "";
-
- // Check for money format and skip over autocomplete cases (as sufficiently handled by formatMoney)
- if (!template.includes('¤')) {
-
- // Compile wildcard template
- for (let i = 0; i < template.length; i++) {
- if (["9", "a", "*"].includes(template[i])) {
- wildcardTemplate += template[i];
- }
- }
-
- //Case 1: template and input are the same length (Autocomplete case 1)
- if (template.length === input.length) {
- for (let i = 0; i < template.length; i++) {
-
- if (["9", "a", "*"].includes(template[i]) && regexes[template[i]].test(input[i])) {
-
- output += input[i];
- }
- }
-
- if(output.length === wildcardTemplate.length) {
- return output;
- }
-
- }
-
- // Case 2: We have a match on the wildcard template length to the input length (Autocomplete case 2)
-
- if (wildcardTemplate.length === input.length) {
-
- for (let i = 0; i < wildcardTemplate.length; i++) {
-
- if (regexes[wildcardTemplate[i]].test(input[i])) {
-
- output += input[i];
-
- continue;
-
- }
-
- // If any character does not match, the autocomplete does not match the template
- break;
- }
-
- if(output.length === wildcardTemplate.length) {
- return output;
- }
-
- }
- }
-
- wildcardTemplate = ''
- output = ''
-
-
- // Strip away non wildcard template characters.
for (let i = 0; i < template.length; i++) {
- if (['9', 'a', '*', '¤'].includes(template[i])) {
- wildcardTemplate += template[i]
- continue;
- }
-
- for (let j = 0; j < inputToBeStripped.length; j++) {
- if (inputToBeStripped[j] === template[i]) {
- inputToBeStripped = inputToBeStripped.slice(0, j) + inputToBeStripped.slice(j+1)
-
- break;
- }
- }
- }
-
- for (let i = 0; i < wildcardTemplate.length; i++) {
- let found = false
-
- for (let j = 0; j < inputToBeStripped.length; j++) {
- if (regexes[wildcardTemplate[i]].test(inputToBeStripped[j])) {
- output += inputToBeStripped[j]
- inputToBeStripped = inputToBeStripped.slice(j+1)
-
- found = true
- break;
- }
- }
-
- if (! found) break;
+ if (template[i] in regexes) output += input[i]
}
return output
}
-export function buildUp(template, input) {
- let clean = Array.from(input)
+export function formatInput (template, input) {
let output = ''
-
- for (let i = 0; i < template.length; i++) {
- if (!['9', 'a', '*', '¤'].includes(template[i])) {
- output += template[i]
- continue;
+ let imark = 0
+ let tmark = 0
+ while (tmark < template.length && imark < input.length) {
+ const char = template[tmark]
+ const ichar = input[imark]
+ if (char in regexes) {
+ if (regexes[char].test(ichar)) {
+ output += ichar
+ tmark++
+ }
+ imark++
+ } else {
+ output += char
+ tmark++
+ if (char === input[imark]) imark++
}
-
- if (clean.length === 0) break;
-
- output += clean.shift()
}
-
return output
}
export function formatMoney(input, delimiter = '.', thousands, precision = 2) {
if (input === '-') return '-'
- if (/^\D+$/.test(input)) return '¤'
+ if (/^\D+$/.test(input)) return '9'
if (thousands === null || thousands === undefined) {
thousands = delimiter === "," ? "." : ","
@@ -266,12 +171,12 @@ export function formatMoney(input, delimiter = '.', thousands, precision = 2) {
let minus = input.startsWith('-') ? '-' : ''
let strippedInput = input.replaceAll(new RegExp(`[^0-9\\${delimiter}]`, 'g'), '')
- let template = Array.from({length: strippedInput.split(delimiter)[0].length}).fill('¤').join('')
+ let template = Array.from({length: strippedInput.split(delimiter)[0].length}).fill('9').join('')
template = `${minus}${addThousands(template, thousands)}`
if (precision > 0 && input.includes(delimiter))
- template += `${delimiter}` + '¤'.repeat(precision)
+ template += `${delimiter}` + '9'.repeat(precision)
queueMicrotask(() => {
if (this.el.value.endsWith(delimiter)) return
diff --git a/tests/cypress/integration/plugins/mask.spec.js b/tests/cypress/integration/plugins/mask.spec.js
index 1046e3e0b..847be90b3 100644
--- a/tests/cypress/integration/plugins/mask.spec.js
+++ b/tests/cypress/integration/plugins/mask.spec.js
@@ -5,7 +5,7 @@ test('x-mask',
({ get }) => {
// Type a phone number:
get('input').type('12').should(haveValue('(12'))
- get('input').type('3').should(haveValue('(123) '))
+ get('input').type('3 ').should(haveValue('(123) '))
get('input').type('4567890').should(haveValue('(123) 456-7890'))
// Clear it & paste formatted version in:
get('input').type('{selectAll}{backspace}')
@@ -43,7 +43,7 @@ test('x-mask with x-model',
// Type a phone number:
get('#1').type('12').should(haveValue('(12'))
get('#2').should(haveValue('(12'))
- get('#1').type('3').should(haveValue('(123) '))
+ get('#1').type('3 ').should(haveValue('(123) '))
get('#2').should(haveValue('(123) '))
get('#1').type('4567890').should(haveValue('(123) 456-7890'))
get('#2').should(haveValue('(123) 456-7890'))
@@ -119,15 +119,15 @@ test('x-mask with non wildcard alpha-numeric characters (b)',
get('input').type('a').should(haveValue('ba'))
get('input').type('a').should(haveValue('ba'))
get('input').type('3').should(haveValue('ba3'))
- get('input').type('z').should(haveValue('ba3zb'))
- get('input').type('{backspace}{backspace}4').should(haveValue('ba34b'))
+ get('input').type('z ').should(haveValue('ba3zb'))
+ get('input').type('{backspace}{backspace}4 ').should(haveValue('ba34b'))
}
)
test('x-mask:dynamic',
[html``],
({ get }) => {
- get('input').type('123').should(haveValue('(123)'))
+ get('input').type('123 ').should(haveValue('(123)'))
}
)
@@ -146,7 +146,7 @@ test('$money',
get('input').type('{leftArrow}7').should(haveValue('1,234,567.87'))
get('input').type('{leftArrow}{leftArrow}{leftArrow}89').should(haveValue('123,456,789.87'))
get('input').type('{leftArrow}{leftArrow}{leftArrow}{leftArrow}12').should(haveValue('12,345,612,789.87'))
- get('input').type('{leftArrow}3').should(haveValue('123,456,123,789.87'))
+ get('input').type('3').should(haveValue('123,456,123,789.87'))
// Clear it & paste formatted version in:
get('input').type('{selectAll}{backspace}')
get('input').invoke('val', '123,456,132,789.87').trigger('blur')
From 2a3b933fde5f9a34d72884bf9217f387e71e84bf Mon Sep 17 00:00:00 2001
From: Robert Marney
Date: Tue, 11 Jun 2024 08:02:24 -0600
Subject: [PATCH 2/4] =?UTF-8?q?=E2=9C=85=20-=20Define=20new=20jest=20spec?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
tests/jest/mask.spec.js | 145 ++++++++++++----------------------------
1 file changed, 43 insertions(+), 102 deletions(-)
diff --git a/tests/jest/mask.spec.js b/tests/jest/mask.spec.js
index 90c217825..ee844d784 100644
--- a/tests/jest/mask.spec.js
+++ b/tests/jest/mask.spec.js
@@ -1,104 +1,45 @@
-let { stripDown, formatMoney, buildUp } = require('../../packages/mask/dist/module.cjs');
+let { formatInput } = require('../../packages/mask/dist/module.cjs');
+
+test('format-input functionality', async () => {
+
+ expect(formatInput('(***) ***-****', '7162256108')).toEqual('(716) 225-6108')
+ expect(formatInput('(999) 999-9999', '7162256108')).toEqual('(716) 225-6108')
+ expect(formatInput('999) 999-9999', '7162256108')).toEqual('716) 225-6108')
+ expect(formatInput('999 999-9999', '7162256108')).toEqual('716 225-6108')
+ expect(formatInput('999999-9999', '7162256108')).toEqual('716225-6108')
+ expect(formatInput('9999999999', '7162256108')).toEqual('7162256108')
+ expect(formatInput('(999) 999-9999', '716 2256108')).toEqual('(716) 225-6108')
+ expect(formatInput('(999) 999-9999', '(716) 2256108')).toEqual('(716) 225-6108')
+ expect(formatInput('(999) 999-9999', '(716) 2-25--6108')).toEqual('(716) 225-6108')
+ expect(formatInput('+1 (999) 999-9999', '7162256108')).toEqual('+1 (716) 225-6108')
+ expect(formatInput('+1 (999) 999-9999', '+1 (716) 225-6108')).toEqual('+1 (716) 225-6108')
+ expect(formatInput('ABC (999) 999-9999', '7162256108')).toEqual('ABC (716) 225-6108')
+ expect(formatInput('ABC (999) 999-9999', 'ABC (716) 225-6108')).toEqual('ABC (716) 225-6108')
+ expect(formatInput('999.999.9999', '7162256108')).toEqual('716.225.6108')
+ expect(formatInput('999.999.9999', '716.2256108')).toEqual('716.225.6108')
+ expect(formatInput('999.999.9999', '716.225 6108')).toEqual('716.225.6108')
+
+ expect(formatInput('aaaa aaaa aaaa aaaa', 'abcd9abcd9abcd9abcd9')).toEqual('abcd abcd abcd abcd')
+ expect(formatInput('aaaa aaaa aaaa aaaa', 'abcd abcd abcd abcd')).toEqual('abcd abcd abcd abcd')
+ expect(formatInput('aaaa aaaa aaaa aaaa', 'abcdabcdabcdabcd')).toEqual('abcd abcd abcd abcd')
+ expect(formatInput('aaaa aaaa aaaa aaaa', 'abcd9abcd9abcd9abc')).toEqual('abcd abcd abcd abc')
+ expect(formatInput('### aaaa aaaa aaaa aaaa', 'abcd abcd abcd abcd')).toEqual('### abcd abcd abcd abcd')
+ expect(formatInput('### aaaa aaaa aaaa aaaa', '### abcd abcd abcd abcd')).toEqual('### abcd abcd abcd abcd')
+ expect(formatInput('### aaaa aaaa aaaa aaaa', '### abcd abcd #### abcd')).toEqual('### abcd abcd abcd')
+
+ expect(formatInput('#### #### #### 9999', '1234 5678 9101 2345')).toEqual('#### #### #### 1234')
+
+ expect(formatInput('ba9*b', 'a')).toEqual('ba')
+ expect(formatInput('ba9*b', 'aa')).toEqual('ba')
+ expect(formatInput('ba9*b', 'aa3')).toEqual('ba3')
+ expect(formatInput('ba9*b', 'aa3z')).toEqual('ba3z')
+ expect(formatInput('ba9*b', 'aa3z4')).toEqual('ba3zb')
+
+ expect(formatInput('a', 'a9a')).toEqual('a')
+ expect(formatInput('aa', 'b9a')).toEqual('ba')
+ expect(formatInput('aa', 'bb')).toEqual('bb')
+ expect(formatInput('aab', 'aba')).toEqual('abb')
+ expect(formatInput('abb', 'ab')).toEqual('ab')
+ expect(formatInput('abb', 'ab9')).toEqual('abb')
-test('strip-down functionality', async () => {
- expect(stripDown('(***) ***-****', '7162256108')).toEqual('7162256108')
- expect(stripDown('(999) 999-9999', '7162256108')).toEqual('7162256108')
- expect(stripDown('999) 999-9999', '7162256108')).toEqual('7162256108')
- expect(stripDown('999 999-9999', '7162256108')).toEqual('7162256108')
- expect(stripDown('999999-9999', '7162256108')).toEqual('7162256108')
- expect(stripDown('9999999999', '7162256108')).toEqual('7162256108')
- expect(stripDown('9999999999', '7162256108')).toEqual('7162256108')
- expect(stripDown('(999) 999-9999', '716 2256108')).toEqual('7162256108')
- expect(stripDown('(999) 999-9999', '(716) 2256108')).toEqual('7162256108')
- expect(stripDown('(999) 999-9999', '(716) 2-25--6108')).toEqual('7162256108')
- expect(stripDown('+1 (999) 999-9999', '7162256108')).toEqual('7162256108')
- expect(stripDown('+1 (999) 999-9999', '+1 (716) 225-6108')).toEqual('7162256108')
- expect(stripDown('ABC (999) 999-9999', '7162256108')).toEqual('7162256108')
- expect(stripDown('ABC (999) 999-9999', 'ABC (716) 225-6108')).toEqual('7162256108')
- expect(stripDown('999.999.9999', '7162256108')).toEqual('7162256108')
- expect(stripDown('999.999.9999', '716.2256108')).toEqual('7162256108')
- expect(stripDown('999.999.9999', '716.225 6108')).toEqual('7162256108')
-
- expect(stripDown('aaaa aaaa aaaa aaaa', 'abcd9abcd9abcd9abcd9')).toEqual('abcdabcdabcdabcd')
- expect(stripDown('aaaa aaaa aaaa aaaa', 'abcd abcd abcd abcd')).toEqual('abcdabcdabcdabcd')
- expect(stripDown('aaaa aaaa aaaa aaaa', 'abcdabcdabcdabcd')).toEqual('abcdabcdabcdabcd')
- expect(stripDown('aaaa aaaa aaaa aaaa', 'abcd9abcd9abcd9abc')).toEqual('abcdabcdabcdabc')
- expect(stripDown('### aaaa aaaa aaaa aaaa', 'abcd abcd abcd abcd')).toEqual('abcdabcdabcdabcd')
- expect(stripDown('### aaaa aaaa aaaa aaaa', '### abcd abcd abcd abcd')).toEqual('abcdabcdabcdabcd')
- expect(stripDown('### aaaa aaaa aaaa aaaa', '### abcd abcd #### abcd')).toEqual('abcdabcdabcd')
- expect(stripDown('#### #### #### 9999', '1234 5678 9101 2345')).toEqual('2345')
-
- expect(stripDown('ba9*b', 'a')).toEqual('a')
- expect(stripDown('ba9*b', 'aa')).toEqual('a')
- expect(stripDown('ba9*b', 'aa3')).toEqual('a3')
- expect(stripDown('ba9*b', 'aa3z')).toEqual('a3z')
- expect(stripDown('ba9*b', 'aa3z4')).toEqual('a3z')
-
- expect(stripDown('a', 'a9a')).toEqual('a')
- expect(stripDown('aa', 'b9a')).toEqual('ba')
- expect(stripDown('aa', 'bb')).toEqual('bb')
- expect(stripDown('aab', 'aba')).toEqual('ab')
- expect(stripDown('abb', 'ab')).toEqual('a')
- expect(stripDown('abb', 'ab9')).toEqual('a')
- expect(stripDown('abcd', 'aba')).toEqual('a')
- expect(stripDown('baba', 'a9a')).toEqual('aa')
- expect(stripDown('baba', 'aa')).toEqual('aa')
- expect(stripDown('bbbba', 'a')).toEqual('a')
- expect(stripDown('bbbba', 'aa')).toEqual('a')
-})
-
-test('build-up functionality', async () => {
- expect(buildUp('(***) ***-****', '7162256108')).toEqual('(716) 225-6108')
- expect(buildUp('(999) 999-9999', '7162256108')).toEqual('(716) 225-6108')
- expect(buildUp('999) 999-9999', '7162256108')).toEqual('716) 225-6108')
- expect(buildUp('999 999-9999', '7162256108')).toEqual('716 225-6108')
- expect(buildUp('999999-9999', '7162256108')).toEqual('716225-6108')
- expect(buildUp('9999999999', '7162256108')).toEqual('7162256108')
-
- expect(buildUp('ba9*b', 'a')).toEqual('ba')
- expect(buildUp('ba9*b', 'a3')).toEqual('ba3')
- expect(buildUp('ba9*b', 'a3z')).toEqual('ba3zb')
-
-})
-
-test('teardown and build-up functionality', async () => {
- let template = 'ba9*b';
-
- expect(buildUp(template, stripDown(template, 'a'))).toEqual('ba')
- expect(buildUp(template, stripDown(template, 'aa'))).toEqual('ba')
- expect(buildUp(template, stripDown(template, 'aa3'))).toEqual('ba3')
- expect(buildUp(template, stripDown(template, 'aa3z'))).toEqual('ba3zb')
- expect(buildUp(template, stripDown(template, 'aa4'))).toEqual('ba4')
-
-});
-
-test('formatMoney functionality', async () => {
- // Default arguments implicit and explicit
- expect(formatMoney('123456')).toEqual('123,456');
- expect(formatMoney('9900900')).toEqual('9,900,900');
- expect(formatMoney('5600.40')).toEqual('5,600.40');
- expect(formatMoney('123456', '.')).toEqual('123,456');
- expect(formatMoney('9900900', '.')).toEqual('9,900,900');
- expect(formatMoney('5600.40', '.')).toEqual('5,600.40');
- expect(formatMoney('123456', '.', ',')).toEqual('123,456');
- expect(formatMoney('9900900', '.', ',')).toEqual('9,900,900');
- expect(formatMoney('5600.40', '.', ',')).toEqual('5,600.40');
-
- // Switch decimal separator
- expect(formatMoney('123456', ',')).toEqual('123.456');
- expect(formatMoney('9900900', ',')).toEqual('9.900.900');
- expect(formatMoney('5600.40', ',')).toEqual('5.600,40');
- expect(formatMoney('123456', '/')).toEqual('123.456');
- expect(formatMoney('9900900', '/')).toEqual('9.900.900');
- expect(formatMoney('5600.40', '/')).toEqual('5.600/40');
-
- // Switch thousands separator
- expect(formatMoney('123456', '.', ' ')).toEqual('123 456');
- expect(formatMoney('9900900', '.', ' ')).toEqual('9 900 900');
- expect(formatMoney('5600.40', '.', ' ')).toEqual('5 600.40');
-
- // Switch decimal and thousands separator
- expect(formatMoney('123456', '#', ' ')).toEqual('123 456');
- expect(formatMoney('9900900', '#', ' ')).toEqual('9 900 900');
- expect(formatMoney('5600.40', '#', ' ')).toEqual('5 600#40');
});
From 827b7b01dbb4fd43e1440d5f614d5be9fd070821 Mon Sep 17 00:00:00 2001
From: Robert Marney
Date: Tue, 11 Jun 2024 08:08:42 -0600
Subject: [PATCH 3/4] =?UTF-8?q?=E2=9A=B0=EF=B8=8F=20Remove=20stripDown=20f?=
=?UTF-8?q?unctionality?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
packages/mask/builds/module.js | 4 ++--
packages/mask/src/index.js | 9 ---------
2 files changed, 2 insertions(+), 11 deletions(-)
diff --git a/packages/mask/builds/module.js b/packages/mask/builds/module.js
index c49f3a1c7..1bd83bbaf 100644
--- a/packages/mask/builds/module.js
+++ b/packages/mask/builds/module.js
@@ -1,5 +1,5 @@
-import mask, { stripDown } from '../src/index.js'
+import mask, { formatInput } from '../src/index.js'
export default mask
-export { mask, stripDown }
+export { mask, formatInput }
diff --git a/packages/mask/src/index.js b/packages/mask/src/index.js
index 5d451c15d..f80ee8bc3 100644
--- a/packages/mask/src/index.js
+++ b/packages/mask/src/index.js
@@ -111,15 +111,6 @@ let regexes = {
'a': /[a-zA-Z]/,
'*': /[a-zA-Z0-9]/,
};
-export function stripDown(template, input) {
- let output = ''
- for (let i = 0; i < template.length; i++) {
- if (template[i] in regexes) output += input[i]
- }
-
- return output
-}
-
export function formatInput (template, input) {
let output = ''
let imark = 0
From ed461b30723ae138daeecda7f2e3259471954ba5 Mon Sep 17 00:00:00 2001
From: Robert Marney
Date: Tue, 11 Jun 2024 08:25:25 -0600
Subject: [PATCH 4/4] =?UTF-8?q?=E2=9C=85Add=20additional=20test=20case?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../cypress/integration/plugins/mask.spec.js | 24 +++++++++++++++++++
1 file changed, 24 insertions(+)
diff --git a/tests/cypress/integration/plugins/mask.spec.js b/tests/cypress/integration/plugins/mask.spec.js
index 847be90b3..716a13a19 100644
--- a/tests/cypress/integration/plugins/mask.spec.js
+++ b/tests/cypress/integration/plugins/mask.spec.js
@@ -32,6 +32,30 @@ test('x-mask',
},
)
+test('x-mask autocomplete',
+ [html``],
+ ({ get }) => {
+ // Type a phone number:
+ get('input').type('21').should(haveValue('+1 (21'))
+ get('input').type('3 ').should(haveValue('+1 (213) '))
+ get('input').type('4567890').should(haveValue('+1 (213) 456-7890'))
+ // Clear it & paste formatted version in:
+ get('input').type('{selectAll}{backspace}')
+ get('input').invoke('val', '+1 (213) 456-7890').trigger('blur')
+ get('input').should(haveValue('+1 (213) 456-7890'))
+ // Clear it & paste un-formatted version in:
+ get('input').type('{selectAll}{backspace}')
+ get('input').invoke('val', '2134567890').trigger('blur')
+ get('input').should(haveValue('+1 (213) 456-7890'))
+ // Clear it and start with an area code starting with 1:
+ get('input').type('{selectAll}{backspace}')
+ get('input').type('1 ').should(haveValue('+1 '))
+ get('input').type('2').should(haveValue('+1 (2'))
+ get('input').type('13 ').should(haveValue('+1 (213) '))
+ get('input').type('456 78-90').should(haveValue('+1 (213) 456-7890'))
+ },
+)
+
test('x-mask with x-model',
[html`