2
0
mirror of https://github.com/tenrok/maska.git synced 2026-05-15 11:59:38 +03:00

feat: simple mode for mask directive

Allow string passing to set a mask without data-maska attribute
This commit is contained in:
Alexander Shabunevich
2024-06-12 17:52:55 +03:00
parent cb87ea76c0
commit 488a52f760
16 changed files with 150 additions and 102 deletions
+21 -17
View File
@@ -24,38 +24,42 @@
<main>
<article>
<div x-data="{
maskedvalue: '123',
unmaskedvalue: '',
options: { eager: true }
}">
<input
x-model="maskedvalue"
x-maska:unmaskedvalue.unmasked="options"
x-on:maska="console.log($event.detail)"
data-maska="##-##"
>
<div><label><input type="checkbox" x-model="options.eager"> eager?</label></div>
<div>masked value: <span x-text="maskedvalue"></span></div>
<div>unmasked value: <span x-text="unmaskedvalue"></span></div>
<div x-data="{ enabled: true }">
<div><label><input type="checkbox" x-model="enabled"> enabled?</label></div>
<template x-if="enabled">
<div x-data="{
maskedvalue: '123',
unmaskedvalue: '',
options: { mask: '#-#', eager: true }
}">
<input
x-model="maskedvalue"
x-maska:unmaskedvalue.unmasked="options"
>
<div><label><input type="checkbox" x-model="options.eager"> eager?</label></div>
<div>masked value: <span x-text="maskedvalue"></span></div>
<div>unmasked value: <span x-text="unmaskedvalue"></span></div>
</div>
</template>
</div>
<div x-data="{
maskedvalue: '-1234.90',
unmaskedvalue: '',
options: { number: { locale: 'ru', fraction: 2 }}
}" style="margin-top: 1em">
<input
x-model="maskedvalue"
x-maska:unmaskedvalue.unmasked="options"
x-maska:unmaskedvalue.unmasked
x-on:maska="console.log($event.detail)"
data-maska-number-locale="ru"
data-maska-number-fraction="2"
>
<div>masked value: <span x-text="maskedvalue"></span></div>
<div>unmasked value: <span x-text="unmaskedvalue"></span></div>
</div>
<div x-data style="margin-top: 1em">
<input x-maska data-maska="+1 (###) ###-####" data-maska-eager value="1234567">
<input x-maska="'+1 (###) ###-####'" data-maska-eager value="1234567">
</div>
<script type="module">
+15 -6
View File
@@ -3,7 +3,7 @@
Maska provides custom Alpine.js directive for use with input:
```html
<input x-maska:argument.modifier="options">
<input x-maska:argument.modifier="value">
```
- `argument` is a name of the bound variable (see example below)
@@ -11,21 +11,24 @@ Maska provides custom Alpine.js directive for use with input:
- `masked` (default): variable will get a masked value (as in v-model)
- `unmasked`: variable will get an unmasked (raw) value
- `completed`: variable will be boolean, showing that mask is completed
- `options` is object with default options
- `value` could be one of:
- `string` for the mask value (should be enclosed in additional quotation marks: `"'#-#'"`)
- `object` with a default options
## Minimal example
Apply `xMaska` directive to the input along with `data-maska` attribite:
Apply `xMaska` directive to the input:
```html
<input x-maska data-maska="#-#">
<input x-maska="'#-#'">
```
?> Please note that the mask value is enclosed in additional quotation marks: `"'#-#'"`.
## Set mask options
To set default [options](/options) for the mask, pass options via **directive value**:
To set a default [options](/options) for the mask, pass options via **directive value**:
```html
<div x-data="{ options: { mask: '#-#', eager: true }}">
@@ -33,6 +36,12 @@ To set default [options](/options) for the mask, pass options via **directive va
</div>
```
Or you can pass an options object directly:
```html
<input x-maska="{ mask: '#-#', eager: true }" data-maska-reversed>
```
You can override default options with `data-maska-` attributes on the input. In the example above we set **eager** mode using options and **reversed** mode using `data-maska-reversed` attribute.
@@ -42,7 +51,7 @@ To get masked value you can use standard `x-model` directive on the input. But i
```html
<div x-data="{ maskedvalue: '', unmaskedvalue: '' }">
<input x-maska:unmaskedvalue.unmasked data-maska="#-#" x-model="maskedvalue">
<input x-maska:unmaskedvalue.unmasked="'#-#'" x-model="maskedvalue">
Masked value: <span x-text="maskedvalue"></span>
Unmasked value: <span x-text="unmaskedvalue"></span>
+15 -11
View File
@@ -12,7 +12,7 @@ npm install maska
And then import it in your code:
```js
import { Mask, MaskInput } from 'maska'
import { Mask, MaskInput } from "maska"
new MaskInput("[data-maska]") // for masked input
const mask = new Mask({ mask: "#-#" }) // for programmatic use
@@ -23,22 +23,26 @@ const mask = new Mask({ mask: "#-#" }) // for programmatic use
And then register it as custom plugin:
```js
import Alpine from 'alpinejs'
import { xMaska } from 'maska/alpine'
import Alpine from "alpinejs"
import { xMaska } from "maska/alpine"
Alpine.plugin(xMaska)
```
```html
<input x-maska="'#-#'">
```
### **Svelte**
And then use it in your `.svelte` component:
```svelte
<script>
import { maska } from 'maska/svelte'
import { maska } from "maska/svelte"
</script>
<input use:maska data-maska="#-#" />
<input use:maska={'#-#'}>
```
### **Vue**
@@ -47,11 +51,11 @@ And then use it in your `.vue` component:
```js
<script setup>
import { vMaska } from 'maska/vue'
import { vMaska } from "maska/vue"
</script>
<template>
<input v-maska data-maska="#-#" />
<input v-maska="'#-#'">
</template>
```
<!-- tabs:end -->
@@ -118,7 +122,7 @@ For modern browsers you can use ES modules build with (optional) [import maps](h
}
</script>
<script type="module">
import { Mask, MaskInput } from 'maska'
import { Mask, MaskInput } from "maska"
new MaskInput("[data-maska]") // for masked input
const mask = new Mask({ mask: "#-#" }) // for programmatic use
@@ -136,8 +140,8 @@ For modern browsers you can use ES modules build with (optional) [import maps](h
}
</script>
<script type="module">
import Alpine from 'alpinejs'
import { xMaska } from 'maska/alpine'
import Alpine from "alpinejs"
import { xMaska } from "maska/alpine"
Alpine.plugin(xMaska)
</script>
@@ -154,7 +158,7 @@ For modern browsers you can use ES modules build with (optional) [import maps](h
}
</script>
<script type="module">
import { vMaska } from 'maska/vue'
import { vMaska } from "maska/vue"
Vue.createApp({ directives: { maska: vMaska }}).mount('#app')
</script>
+12 -8
View File
@@ -3,34 +3,38 @@
Maska provides simple Svelte action for use with input:
```html
<input use:maska={options}>
<input use:maska={value}>
```
- `options` is object with default options
- `value` could be one of:
- `string` for the mask value (should be enclosed in additional quotation marks: `{'#-#'}`)
- `object` with a default options
## Minimal example
Apply `maska` action to the input along with `data-maska` attribite:
Apply `maska` action to the input:
```svelte
<script>
import { maska } from '@maskajs/svelte'
import { maska } from "maska/svelte"
</script>
<input use:maska data-maska="#-#">
<input use:maska={'#-#'}>
```
?> Please note that the mask value is enclosed in additional quotation marks: `"'#-#'"`.
## Set mask options
To set default [options](/options) for the mask, pass options via **directive value**:
```svelte
<script>
import { maska } from '@maskajs/svelte'
<script lang="ts">
import { maska } from "maska/svelte"
import type { MaskInputOptions } from "maska"
const options = {
const options: MaskInputOptions = {
mask: "#-#",
eager: true
}
+2 -2
View File
@@ -6,13 +6,13 @@ Maska v3 has different entries for framework-specific exports.
Import of vue directive in v2:
```js
import { vMaska } from 'maska'
import { vMaska } from "maska"
```
Now you should import vue directive from `maska/vue`:
```js
import { vMaska } from 'maska/vue'
import { vMaska } from "maska/vue"
```
!> Filenames have also been changed. Please see the [Installation](install) for more information.
+14 -10
View File
@@ -3,7 +3,7 @@
Maska provides custom Vue directive for use with input:
```html
<input v-maska:argument.modifier="options">
<input v-maska:argument.modifier="value">
```
- `argument` is a name of the bound variable (see example below)
@@ -11,11 +11,13 @@ Maska provides custom Vue directive for use with input:
- `masked` (default): variable will get a masked value (as in v-model)
- `unmasked`: variable will get an unmasked (raw) value
- `completed`: variable will be boolean, showing that mask is completed
- `options` is object with default options
- `value` could be one of:
- `string` for the mask value (should be enclosed in additional quotation marks: `"'#-#'"`)
- `object` with a default options
## Minimal example
Import `vMaska` directive and apply it to the input along with `data-maska` attribite:
Import `vMaska` directive and apply it to the input:
<!-- tabs:start -->
### **Composition API**
@@ -26,7 +28,7 @@ Import `vMaska` directive and apply it to the input along with `data-maska` attr
</script>
<template>
<input v-maska data-maska="#-#">
<input v-maska="'#-#'">
</template>
```
@@ -42,11 +44,12 @@ Import `vMaska` directive and apply it to the input along with `data-maska` attr
</script>
<template>
<input v-maska data-maska="#-#">
<input v-maska="'#-#'">
</template>
```
<!-- tabs:end -->
?> Please note that the mask value is enclosed in additional quotation marks: `"'#-#'"`.
## Set mask options
@@ -56,12 +59,13 @@ To set default [options](/options) for the mask, pass options via **directive va
### **Composition API**
```vue
<script setup>
<script setup lang="ts">
import { reactive } from "vue"
import { vMaska } from "maska/vue"
import type { MaskInputOptions } from "maska"
// could be plain object too
const options = reactive({
const options = reactive<MaskInputOptions>({
mask: "#-#",
eager: true
})
@@ -119,7 +123,7 @@ To get masked value you can use standard `v-model` directive on the input. But i
</script>
<template>
<input v-maska:unmaskedValue.unmasked data-maska="#-#" v-model="maskedValue">
<input v-maska:unmaskedValue.unmasked="'#-#'" v-model="maskedValue">
Masked value: {{ maskedValue }}
Unmasked value: {{ unmaskedValue }}
@@ -144,7 +148,7 @@ To get masked value you can use standard `v-model` directive on the input. But i
</script>
<template>
<input v-maska:unmaskedValue.unmasked data-maska="#-#" v-model="maskedValue">
<input v-maska:unmaskedValue.unmasked="'#-#'" v-model="maskedValue">
Masked value: {{ maskedValue }}
Unmasked value: {{ unmaskedValue }}
@@ -168,7 +172,7 @@ export default defineNuxtPlugin((nuxtApp) => {
Now you can use v-maska directive in your app:
```html
<input v-model="value" v-maska data-maska="#-#" />
<input v-maska="'#-#'" />
```
+9 -6
View File
@@ -9,11 +9,16 @@ export const xMaska = (Alpine: Alpine): void => {
if (input == null || input?.type === 'file') return
let opts: MaskInputOptions = {}
const evaluator = directive.expression !== ''
? utilities.evaluateLater<MaskInputOptions | string>(directive.expression)
: () => {}
utilities.effect(() => {
const opts =
directive.expression !== ''
? { ...utilities.evaluate<MaskInputOptions>(directive.expression) }
: {}
evaluator((expr) => {
opts = typeof expr === 'string' ? { mask: expr } : { ...expr }
})
if (directive.value != null) {
const updateArg = (detail: MaskaDetail): void => {
@@ -43,7 +48,5 @@ export const xMaska = (Alpine: Alpine): void => {
masks.set(input, new MaskInput(input, opts))
}
})
utilities.cleanup(() => masks.get(input)?.destroy())
}).before('model')
}
+12 -2
View File
@@ -3,19 +3,29 @@ import { MaskaDetail, MaskInput, MaskInputOptions } from '..'
const masks = new WeakMap<HTMLInputElement, MaskInput>()
type MaskaAction = Action<HTMLElement, MaskInputOptions | undefined, {
type MaskaAction = Action<HTMLElement, MaskInputOptions | string | undefined, {
'on:maska': (detail: CustomEvent<MaskaDetail>) => void
}>
export const maska: MaskaAction = (node, opts = {}) => {
export const maska: MaskaAction = (node, value = {}) => {
const input = node instanceof HTMLInputElement ? node : node.querySelector('input')
if (input == null || input?.type === 'file') return
let opts = value
if (typeof opts === 'string') {
opts = { mask: opts }
}
masks.set(input, new MaskInput(input, opts))
return {
update (opts) {
if (typeof opts === 'string') {
opts = { mask: opts }
}
masks.get(input)?.update(opts)
},
+7 -2
View File
@@ -1,7 +1,7 @@
import { Directive, DirectiveBinding } from 'vue'
import { MaskaDetail, MaskInput, MaskInputOptions } from '..'
type MaskaDirective = Directive<HTMLElement, MaskInputOptions | undefined>
type MaskaDirective = Directive<HTMLElement, MaskInputOptions | string | undefined>
const masks = new WeakMap<HTMLInputElement, MaskInput>()
@@ -20,10 +20,15 @@ const setArg = (binding: DirectiveBinding, value: string | boolean): void => {
export const vMaska: MaskaDirective = (el, binding) => {
const input = el instanceof HTMLInputElement ? el : el.querySelector('input')
const opts = binding.value != null ? { ...binding.value } : {}
if (input == null || input?.type === 'file') return
let opts: MaskInputOptions = {}
if (binding.value != null) {
opts = typeof binding.value === 'string' ? { mask: binding.value } : { ...binding.value }
}
if (binding.arg != null) {
const updateArg = (detail: MaskaDetail): void => {
const value = binding.modifiers.unmasked
+6
View File
@@ -20,6 +20,12 @@ const prepareInput = async (markup: string) => {
}
describe('init', () => {
test('with string', async () => {
input = await prepareInput(`<input x-maska="'#-#'" value="123">`)
expect(input).toHaveValue('1-2')
})
test('with data attr', async () => {
input = await prepareInput(`<input x-maska data-maska="#-#" value="123">`)
+7
View File
@@ -0,0 +1,7 @@
<script lang="ts">
import { maska } from '../../src/svelte'
</script>
<main>
<input use:maska data-maska="#-#" data-maska-eager />
</main>
+2
View File
@@ -1,5 +1,6 @@
<script lang="ts">
import BindValue from './BindValue.svelte'
import DataAttr from './DataAttr.svelte'
import Event from './Event.svelte'
import InitialValue from './InitialValue.svelte'
import Number from './Number.svelte'
@@ -12,6 +13,7 @@
<main>
<select bind:value={component}>
<option value={BindValue}>BindValue</option>
<option value={DataAttr}>DataAttr</option>
<option value={Event}>Event</option>
<option value={InitialValue}>InitialValue</option>
<option value={Number}>Number</option>
+1 -1
View File
@@ -8,6 +8,6 @@
</script>
<main>
<input type="checkbox" bind:checked={options.eager} />
<input type="checkbox" bind:checked={options.eager} /> Eager<br>
<input type="text" use:maska={options} />
</main>
+1 -1
View File
@@ -3,5 +3,5 @@
</script>
<main>
<input use:maska data-maska="#-#" />
<input use:maska={'#-#'} />
</main>
+1 -1
View File
@@ -3,5 +3,5 @@ import { vMaska } from '../../src/vue'
</script>
<template>
<input v-maska data-maska="#-#" />
<input v-maska="'#-#'" />
</template>
+25 -35
View File
@@ -1,62 +1,52 @@
<script setup>
<script setup lang="ts">
import { reactive, ref } from 'vue'
import { vMaska } from '../../src/vue'
import type { MaskInputOptions, MaskaDetail } from '../../src'
const mask = ref('+1 (###) ###-####')
const show = ref(true)
const eager = ref(true)
const valueMasked = ref('1234567')
const valueUnmasked = ref('1')
const onMaska = (e) => console.log(e.detail)
const onMaska = (e: CustomEvent<MaskaDetail>) => console.log(e.detail)
const options = reactive({
mask,
eager
const options = reactive<MaskInputOptions>({
mask: '+1 (###) ###-####',
eager: true
})
const options2 = {
preProcess: val => val.replace(/[$,]/g, ''),
postProcess: val => {
if (!val) return ''
const sub = 3 - (val.includes('.') ? val.length - val.indexOf('.') : 0)
return Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'USD'
}).format(val)
.slice(0, sub ? -sub : undefined)
}
}
defineExpose({ valueUnmasked })
</script>
<template>
<div class="row">
<p>
<div>
show: <input type="checkbox" v-model="show">
eager: <input type="checkbox" v-model="eager">
eager: <input type="checkbox" v-model="options.eager">
</div>
<div><input v-model="mask"></div>
<input v-model="options.mask">
<div v-if="show">
<input v-maska:valueUnmasked.unmasked="options" v-model="valueMasked" @maska="onMaska">
<div>Masked: {{ valueMasked }}</div>
<div>Unmasked: {{ valueUnmasked }}</div>
</div>
</div>
</p>
<div class="row"><input type="email" v-maska data-maska="##-##" data-maska-eager value="12"></div>
<p>
<input v-maska data-maska="##-##" data-maska-eager value="123">
</p>
<div class="row"><input v-maska data-maska="####" value="1234"></div>
<p>
<input v-maska="'####'" value="12345">
</p>
<div><input v-maska="options2" value="1234567" data-maska="0.99" data-maska-tokens="0:\d:multiple|9:\d:optional"></div>
<p>
<input
v-maska
value="1234567.89"
data-maska-number-locale="en"
data-maska-number-fraction="2"
>
</p>
</template>
<style scoped>
.row {
margin-bottom: 1em;
}
</style>