Bladeren bron

x-mask: Allow overriding thousands separator (#3122)

Julian Wachholz 2 jaren geleden
bovenliggende
commit
7b5d26db9c

+ 21 - 5
packages/docs/src/en/plugins/mask.md

@@ -12,6 +12,7 @@ Alpine's Mask plugin allows you to automatically format a text input field as a
 This is useful for many different types of inputs: phone numbers, credit cards, dollar amounts, account numbers, dates, etc.
 
 <a name="installation"></a>
+
 ## Installation
 
 <div x-data="{ expanded: false }">
@@ -60,6 +61,7 @@ Alpine.plugin(mask)
  </div>
 
 <a name="x-mask"></a>
+
 ## x-mask
 
 The primary API for using this plugin is the `x-mask` directive.
@@ -80,13 +82,14 @@ Notice how the text you type into the input field must adhere to the format prov
 
 The following wildcard characters are supported in masks:
 
-| Wildcard                   | Description                 |
-| -------------------------- | --------------------------- |
-| `*` | Any character |
-| `a` | Only alpha characters (a-z, A-Z) |
-| `9` | Only numeric characters (0-9) |
+| Wildcard | Description                      |
+| -------- | -------------------------------- |
+| `*`      | Any character                    |
+| `a`      | Only alpha characters (a-z, A-Z) |
+| `9`      | Only numeric characters (0-9)    |
 
 <a name="mask-functions"></a>
+
 ## Dynamic Masks
 
 Sometimes simple mask literals (i.e. `(999) 999-9999`) are not sufficient. In these cases, `x-mask:dynamic` allows you to dynamically generate masks on the fly based on user input.
@@ -128,6 +131,7 @@ function creditCardMask(input) {
 ```
 
 <a name="money-inputs"></a>
+
 ## Money Inputs
 
 Because writing your own dynamic mask expression for money inputs is fairly complex, Alpine offers a prebuilt one and makes it available as `$money()`.
@@ -155,3 +159,15 @@ If you wish to swap the periods for commas and vice versa (as is required in cer
     <input type="text" x-mask:dynamic="$money($input, ',')"  placeholder="0,00">
 </div>
 <!-- END_VERBATIM -->
+
+You may also choose to override the thousands separator by supplying a third optional argument:
+
+```alpine
+<input x-mask:dynamic="$money($input, '.', ' ')">
+```
+
+<!-- START_VERBATIM -->
+<div class="demo" x-data>
+    <input type="text" x-mask:dynamic="$money($input, '.', ' ')"  placeholder="3 000.00">
+</div>
+<!-- END_VERBATIM -->

+ 7 - 8
packages/mask/src/index.js

@@ -163,9 +163,8 @@ export function buildUp(template, input) {
     return output
 }
 
-function formatMoney(input, delimeter = '.', thousands) {
-    thousands = (delimeter === ',' && thousands === undefined)
-        ? '.' : ','
+export function formatMoney(input, delimiter = '.', thousands) {
+    thousands = thousands ?? delimiter === "," ? "." : ","
 
     let addThousands = (input, thousands) => {
         let output = ''
@@ -186,17 +185,17 @@ function formatMoney(input, delimeter = '.', thousands) {
         return output
     }
 
-    let strippedInput = input.replaceAll(new RegExp(`[^0-9\\${delimeter}]`, 'g'), '')
-    let template = Array.from({ length: strippedInput.split(delimeter)[0].length }).fill('9').join('')
+    let strippedInput = input.replaceAll(new RegExp(`[^0-9\\${delimiter}]`, 'g'), '')
+    let template = Array.from({ length: strippedInput.split(delimiter)[0].length }).fill('9').join('')
 
     template = addThousands(template, thousands)
 
-    if (input.includes(delimeter)) template += `${delimeter}99`
+    if (input.includes(delimiter)) template += `${delimiter}99`
 
     queueMicrotask(() => {
-        if (this.el.value.endsWith(delimeter)) return
+        if (this.el.value.endsWith(delimiter)) return
 
-        if (this.el.value[this.el.selectionStart - 1] === delimeter) {
+        if (this.el.value[this.el.selectionStart - 1] === delimiter) {
             this.el.setSelectionRange(this.el.selectionStart - 1, this.el.selectionStart - 1)
         }
     })

+ 14 - 0
tests/cypress/integration/plugins/mask.spec.js

@@ -144,6 +144,20 @@ test('$money swapping commas and periods',
     },
 )
 
+test('$money with different thousands separator',
+    [html`<input x-data x-mask:function="$money($input, '.', ' ')" />`],
+    ({ get }) => {
+        get('input').type('3000').should(haveValue('3 000'));
+        get('input').type('{backspace}').blur().should(haveValue('300'));
+        get('input').type('5').should(haveValue('3 005'));
+        get('input').type('{selectAll}{backspace}').should(haveValue(''));
+        get('input').type('123').should(haveValue('123'));
+        get('input').type('4').should(haveValue('1 234'));
+        get('input').type('567').should(haveValue('1 234 567'));
+        get('input').type('.89').should(haveValue('1 234 567.89'));
+    }
+);
+
 test('$money works with permenant inserted at beginning',
     [html`<input x-data x-mask:dynamic="$money">`],
     ({ get }) => {

+ 32 - 1
tests/jest/mask.spec.js

@@ -1,4 +1,4 @@
-let { stripDown } = require('../../packages/mask/dist/module.cjs')
+let { stripDown, formatMoney } = require('../../packages/mask/dist/module.cjs');
 
 test('strip-down functionality', async () => {
     expect(stripDown('(***) ***-****', '7162256108')).toEqual('7162256108')
@@ -12,3 +12,34 @@ test('strip-down functionality', async () => {
     expect(stripDown('(999) 999-9999', '(716) 2256108')).toEqual('7162256108')
     expect(stripDown('(999) 999-9999', '(716) 2-25--6108')).toEqual('7162256108')
 })
+
+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');
+});