Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add x-mask:dynamic instead of :function #2858

Merged
merged 1 commit into from Apr 20, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
34 changes: 24 additions & 10 deletions packages/docs/src/en/plugins/mask.md
Expand Up @@ -87,57 +87,71 @@ The following wildcard characters are supported in masks:
| `9` | Only numeric characters (0-9) |

<a name="mask-functions"></a>
## Custom Mask Functions
## Dynamic Masks

Sometimes simple mask literals (i.e. `(999) 999-9999`) are not sufficient. In these cases, `x-mask:function` allows you to dynamically generate masks on the fly based on user input.
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.

Here's an example of a credit card input that needs to change it's mask based on if the number starts with the numbers "34" or "37" (which means it's an Amex card and therefore has a different format).

```alpine
<input x-mask:function="
<input x-mask:dynamic="
$input.startsWith('34') || $input.startsWith('37')
? '9999 999999 99999' : '9999 9999 9999 9999'
">
```

As you can see in the above example, every time a user types in the input, that value is passed to the function as `$input`. Based on the `$input`, a different mask is utilized in the field.
As you can see in the above example, every time a user types in the input, that value is passed to the expression as `$input`. Based on the `$input`, a different mask is utilized in the field.

Try it for yourself by typing a number that starts with "34" and one that doesn't.

<!-- START_VERBATIM -->
<div class="demo">
<input x-data x-mask:function="
<input x-data x-mask:dynamic="
$input.startsWith('34') || $input.startsWith('37')
? '9999 999999 99999' : '9999 9999 9999 9999'
">
</div>
<!-- END_VERBATIM -->

`x-mask:dynamic` also accepts a function as a result of the expression and will automatically pass it the `$input` as the the first paramter. For example:

```alpine
<input x-mask:dynamic="creditCardMask">

<script>
function creditCardMask(input) {
return input.startsWith('34') || input.startsWith('37')
? '9999 999999 99999'
: '9999 9999 9999 9999'
}
</script>
```

<a name="money-inputs"></a>
## Money Inputs

Because writing your own custom mask function for money inputs is fairly complex, Alpine offers a prebuilt one and makes it available as `$money()`.
Because writing your own dynamic mask expression for money inputs is fairly complex, Alpine offers a prebuilt one and makes it available as `$money()`.

Here is a fully functioning money input mask:

```alpine
<input x-mask:function="$money($input)">
<input x-mask:dynamic="$money($input)">
```

<!-- START_VERBATIM -->
<div class="demo" x-data>
<input type="text" x-mask:function="$money($input)" placeholder="0.00">
<input type="text" x-mask:dynamic="$money($input)" placeholder="0.00">
</div>
<!-- END_VERBATIM -->

If you wish to swap the periods for commas and vice versa (as is required in certain currencies), you can do so using the second optional parameter:

```alpine
<input x-mask:function="$money($input, ',')">
<input x-mask:dynamic="$money($input, ',')">
```

<!-- START_VERBATIM -->
<div class="demo" x-data>
<input type="text" x-mask:function="$money($input, ',')" placeholder="0.00">
<input type="text" x-mask:dynamic="$money($input, ',')" placeholder="0,00">
</div>
<!-- END_VERBATIM -->
2 changes: 1 addition & 1 deletion packages/mask/src/index.js
Expand Up @@ -4,7 +4,7 @@ export default function (Alpine) {
let templateFn = () => expression
let lastInputValue = ''

if (['function'].includes(value)) {
if (['function', 'dynamic'].includes(value)) {
// This is an x-mask:function directive.

let evaluator = evaluateLater(expression)
Expand Down
35 changes: 35 additions & 0 deletions tests/cypress/integration/plugins/mask.spec.js
Expand Up @@ -32,6 +32,34 @@ test('x-mask',
},
)

test('x-mask with x-model',
[html`
<div x-data="{ value: '' }">
<input x-mask="(999) 999-9999" x-model="value" id="1">
<input id="2" x-model="value">
</div>
`],
({ get }) => {
// Type a phone number:
get('#1').type('12').should(haveValue('(12'))
get('#2').should(haveValue('(12'))
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'))
// Clear it & paste formatted version in:
get('#1').type('{selectAll}{backspace}')
get('#1').invoke('val', '(123) 456-7890').trigger('input')
get('#1').should(haveValue('(123) 456-7890'))
get('#2').should(haveValue('(123) 456-7890'))
// Clear it & paste un-formatted version in:
get('#1').type('{selectAll}{backspace}')
get('#1').invoke('val', '1234567890').trigger('input')
get('#1').should(haveValue('(123) 456-7890'))
get('#2').should(haveValue('(123) 456-7890'))
},
)

test('x-mask with non wildcard alpha-numeric characters (b)',
[html`<input x-data x-mask="ba9*b">`],
({ get }) => {
Expand All @@ -43,6 +71,13 @@ test('x-mask with non wildcard alpha-numeric characters (b)',
},
)

test('x-mask:dynamic',
[html`<input x-data x-mask:dynamic="'(999)'">`],
({ get }) => {
get('input').type('123').should(haveValue('(123)'))
}
)

test('$money',
[html`<input x-data x-mask:function="$money">`],
({ get }) => {
Expand Down