mother-mask 3.42.0 → 3.44.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -171,14 +171,14 @@ for a runnable app and browser lifecycle/memory tests.
171
171
 
172
172
  ## Vue
173
173
 
174
- Import the Vue 3.5 Composition API components from the separate `mother-mask/vue` entry:
174
+ Import the custom directives from the separate `mother-mask/vue` entry:
175
175
 
176
176
  ```vue
177
177
  <script setup lang="ts">
178
178
  import { ref } from 'vue'
179
- import { InputMask, InputDecimal, formatDecimalValue } from 'mother-mask/vue'
179
+ import { vMotherMask, vMotherMaskDecimal, formatDecimalValue } from 'mother-mask/vue'
180
180
 
181
- // Keep option objects stable across renders.
181
+ // Keep the options object stable across renders.
182
182
  const currency = { decimalPlaces: 2, prefix: '$', allowNegative: true }
183
183
 
184
184
  const phone = ref('')
@@ -187,10 +187,19 @@ const amount = ref('')
187
187
 
188
188
  <template>
189
189
  <label for="phone">Phone</label>
190
- <InputMask id="phone" name="phone" mask="(99) 99999-9999" inputmode="tel" v-model="phone" />
190
+ <input
191
+ id="phone"
192
+ name="phone"
193
+ inputmode="tel"
194
+ v-mother-mask="{ mask: '(99) 99999-9999', value: phone, onValueChange: (v) => (phone = v) }"
195
+ />
191
196
 
192
197
  <label for="amount">Amount</label>
193
- <InputDecimal id="amount" name="amount" :options="currency" v-model="amount" />
198
+ <input
199
+ id="amount"
200
+ name="amount"
201
+ v-mother-mask-decimal="{ options: currency, value: amount, onValueChange: (v) => (amount = v) }"
202
+ />
194
203
 
195
204
  <button type="button" @click="amount = formatDecimalValue(1234.5, currency)">
196
205
  Set amount
@@ -199,25 +208,33 @@ const amount = ref('')
199
208
  ```
200
209
 
201
210
  `mother-mask/vue` re-exports every core function, class, and type as well as
202
- `InputMask` and `InputDecimal`. Its core exports are aliases to `mother-mask`,
203
- sharing the same implementation and caches. The core ESM, CommonJS, and UMD
204
- builds remain independent of Vue. Vue is an optional peer dependency required
205
- only when importing `mother-mask/vue`; the supported version range is
206
- `^3.5.0`. Vue is not bundled.
207
-
208
- - Both components support `v-model` (a `modelValue` prop plus an
209
- `update:modelValue` emit). `InputMask` accepts `mask` and optional
210
- `BindOptions` (without `onChange`); `InputDecimal` accepts optional
211
- `BindDecimalOptions` and emits `(value, numericValue)`.
212
- - Binding happens in `onMounted` a lifecycle hook that Vue never invokes
213
- during SSR so these components render safely on the server without a
214
- manual `typeof window` check; hydration binds on the client as usual.
215
- `onBeforeUnmount` disposes the binding, and it is disposed and recreated
216
- whenever `mask`/`options` change or `modelValue` is updated externally.
217
- - Native attributes (`id`, `name`, `class`, ...) fall through via
218
- `inheritAttrs: false` plus `v-bind="$attrs"` semantics; `InputDecimal`
219
- always sets `inputmode="decimal"`. Both expose their `inputRef` via
220
- `defineExpose` for `useTemplateRef` access to the underlying `<input>`.
211
+ the `vMotherMask` and `vMotherMaskDecimal` directives. Its core exports are
212
+ aliases to `mother-mask`, sharing the same implementation and caches. The
213
+ core ESM, CommonJS, and UMD builds remain independent of Vue. Vue is an
214
+ optional peer dependency required only when importing `mother-mask/vue`; the
215
+ supported version range is `^3.5.0`. Vue is not bundled.
216
+
217
+ - `vMotherMask` takes `{ mask, options?, value?, onValueChange? }`;
218
+ `vMotherMaskDecimal` takes `{ options?, value?, onValueChange? }` and calls
219
+ `onValueChange(value, numericValue)`. `vMotherMaskDecimal` always sets
220
+ `inputmode="decimal"` on mount.
221
+ - Both are exported pre-named `v` + PascalCase Vue's own convention for a
222
+ local directive in `<script setup>` so `import { vMotherMask } from
223
+ 'mother-mask/vue'` alone makes `v-mother-mask="..."` available in the
224
+ template with no import rename. Register it globally instead with
225
+ `app.directive('mother-mask', vMotherMask)` to make `v-mother-mask`
226
+ available in every component.
227
+ - `mounted` runs only once Vue mounts the element to the live DOM — Vue
228
+ never invokes directive hooks during server-side rendering, so this is
229
+ SSR-safe with no `typeof window` guard needed. `updated` runs on every
230
+ re-render of the owning component (not only when the bound value actually
231
+ changes), so it rebinds only if the mask, options, or an externally-set
232
+ value actually changed — an echo of the directive's own `onValueChange` is
233
+ ignored. `beforeUnmount` guarantees `dispose()` runs exactly once.
234
+ - A `declare module 'vue' { interface GlobalDirectives { ... } }`
235
+ augmentation ships alongside each directive, so a globally-registered
236
+ `v-mother-mask`/`v-mother-mask-decimal` also type-checks in templates
237
+ without extra setup.
221
238
 
222
239
  See the [Vue example](https://github.com/dan2dev/mother-mask/tree/main/examples/vue-simple)
223
240
  for a runnable app.
package/dist/vue.cjs CHANGED
@@ -1,2 +1,2 @@
1
- Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});let e=require("mother-mask"),t=require("vue");const n=(0,t.defineComponent)({name:`InputMask`,inheritAttrs:!1,props:{mask:{type:[String,Array,Object,Function],required:!0},options:{type:Object,default:void 0},modelValue:{type:String,default:void 0}},emits:[`update:modelValue`],setup(n,{attrs:r,emit:i,expose:a}){let o=(0,t.ref)(null),s=null,c=()=>{s?.(),s=null},l=()=>{let t=o.value;if(!t)return;c();let r=n.modelValue??t.value,a=(0,e.process)(r,n.mask,n.options);t.value!==a&&(t.value=a),s=(0,e.bind)(t,n.mask,{...n.options,onChange:e=>i(`update:modelValue`,e)})};return(0,t.onMounted)(l),(0,t.onBeforeUnmount)(c),(0,t.watch)(()=>[n.mask,n.options],()=>l()),(0,t.watch)(()=>n.modelValue,t=>{let r=o.value;r&&t!==void 0&&t!==r.value&&(0,e.process)(t,n.mask,n.options)!==r.value&&l()}),a({inputRef:o}),()=>(0,t.h)(`input`,{...r,ref:o,type:`text`})}}),r=(0,t.defineComponent)({name:`InputDecimal`,inheritAttrs:!1,props:{options:{type:Object,default:void 0},modelValue:{type:String,default:void 0}},emits:[`update:modelValue`],setup(n,{attrs:r,emit:i,expose:a}){let o=(0,t.ref)(null),s=null,c=()=>{s?.(),s=null},l=()=>{let t=o.value;if(!t)return;c();let r=n.modelValue??t.value,a=(0,e.processDecimal)(r,n.options);t.value!==a&&(t.value=a),s=(0,e.bindDecimal)(t,{...n.options,onChange:(e,t)=>i(`update:modelValue`,e,t)})};return(0,t.onMounted)(l),(0,t.onBeforeUnmount)(c),(0,t.watch)(()=>n.options,()=>l()),(0,t.watch)(()=>n.modelValue,t=>{let r=o.value;r&&t!==void 0&&t!==r.value&&(0,e.processDecimal)(t,n.options)!==r.value&&l()}),a({inputRef:o}),()=>(0,t.h)(`input`,{inputmode:`decimal`,...r,ref:o,type:`text`})}});exports.InputDecimal=r,exports.InputMask=n,Object.keys(e).forEach(function(t){t!=="default"&&!Object.prototype.hasOwnProperty.call(exports,t)&&Object.defineProperty(exports,t,{enumerable:!0,get:function(){return e[t]}})});
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});let e=require("mother-mask");const t=new WeakMap;function n(e){e.dispose?.(),e.dispose=null}function r(t,r){n(r);let{mask:i,options:a,value:o}=r.current,s=o??t.value,c=(0,e.process)(s,i,a);t.value!==c&&(t.value=c),r.dispose=(0,e.bind)(t,i,{...a,onChange:e=>{r.lastEmitted=e,r.current.onValueChange?.(e)}})}function i(e,t,n){let i=t.current,a=n.mask!==i.mask||n.options!==i.options,o=n.value!==void 0&&n.value!==t.lastEmitted&&n.value!==e.value;t.current=n,(a||o)&&r(e,t)}const a={mounted(e,n){let i={current:n.value,dispose:null,lastEmitted:null};t.set(e,i),r(e,i)},updated(e,n){i(e,t.get(e),n.value)},beforeUnmount(e){n(t.get(e)),t.delete(e)}},o=new WeakMap;function s(e){e.dispose?.(),e.dispose=null}function c(t,n){s(n);let{options:r,value:i}=n.current,a=i??t.value,o=(0,e.processDecimal)(a,r);t.value!==o&&(t.value=o),n.dispose=(0,e.bindDecimal)(t,{...r,onChange:(e,t)=>{n.lastEmitted=e,n.current.onValueChange?.(e,t)}})}function l(e,t,n){let r=t.current,i=n.options!==r.options,a=n.value!==void 0&&n.value!==t.lastEmitted&&n.value!==e.value;t.current=n,(i||a)&&c(e,t)}const u={mounted(e,t){e.setAttribute(`inputmode`,`decimal`);let n={current:t.value,dispose:null,lastEmitted:null};o.set(e,n),c(e,n)},updated(e,t){l(e,o.get(e),t.value)},beforeUnmount(e){s(o.get(e)),o.delete(e)}};exports.vMotherMask=a,exports.vMotherMaskDecimal=u,Object.keys(e).forEach(function(t){t!=="default"&&!Object.prototype.hasOwnProperty.call(exports,t)&&Object.defineProperty(exports,t,{enumerable:!0,get:function(){return e[t]}})});
2
2
  //# sourceMappingURL=vue.cjs.map
package/dist/vue.cjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"vue.cjs","names":["defineComponent","ref","process","bind","h","defineComponent","ref","processDecimal","bindDecimal","h"],"sources":["../src/vue/InputMask.ts","../src/vue/InputDecimal.ts"],"sourcesContent":["import { defineComponent, h, onBeforeUnmount, onMounted, ref, watch } from 'vue'\nimport type { PropType } from 'vue'\nimport { bind, process } from 'mother-mask'\nimport type { BindOptions, MaskPattern } from 'mother-mask'\n\nexport type InputMaskEmits = {\n 'update:modelValue': [value: string]\n}\n\n/**\n * Vue wrapper around {@link bind}. Binds on `onMounted` (a client-only\n * lifecycle hook — it never runs during SSR, so this component is safe to\n * render on the server) and disposes on `onUnmounted`/before a rebind.\n */\nexport const InputMask = defineComponent({\n name: 'InputMask',\n inheritAttrs: false,\n props: {\n mask: { type: [String, Array, Object, Function] as PropType<MaskPattern>, required: true },\n options: { type: Object as PropType<Omit<BindOptions, 'onChange'>>, default: undefined },\n modelValue: { type: String, default: undefined },\n },\n emits: ['update:modelValue'],\n setup(props, { attrs, emit, expose }) {\n const inputRef = ref<HTMLInputElement | null>(null)\n let dispose: (() => void) | null = null\n\n const release = () => {\n dispose?.()\n dispose = null\n }\n\n const bindInput = () => {\n const input = inputRef.value\n if (!input) return\n release()\n const source = props.modelValue ?? input.value\n const next = process(source, props.mask, props.options)\n if (input.value !== next) input.value = next\n dispose = bind(input, props.mask, {\n ...props.options,\n onChange: (maskedValue) => emit('update:modelValue', maskedValue),\n })\n }\n\n // Runs client-side only; a no-op during SSR, so no `typeof window` guard\n // is needed here the component never touches the DOM off the client.\n onMounted(bindInput)\n onBeforeUnmount(release)\n\n // Mask/options changes need a fresh binding (handlers close over them).\n watch(\n () => [props.mask, props.options],\n () => bindInput(),\n )\n\n // An external `modelValue` update (v-model) reformats and rebinds so the\n // mask's internal edit history restarts from the new value.\n watch(\n () => props.modelValue,\n (value) => {\n const input = inputRef.value\n if (!input || value === undefined || value === input.value) return\n const next = process(value, props.mask, props.options)\n if (next === input.value) return\n bindInput()\n },\n )\n\n expose({ inputRef })\n\n return () => h('input', { ...attrs, ref: inputRef, type: 'text' })\n },\n})\n\nexport default InputMask\n","import { defineComponent, h, onBeforeUnmount, onMounted, ref, watch } from 'vue'\nimport type { PropType } from 'vue'\nimport { bindDecimal, processDecimal } from 'mother-mask'\nimport type { BindDecimalOptions } from 'mother-mask'\n\nexport type InputDecimalEmits = {\n 'update:modelValue': [value: string, numericValue: number]\n}\n\n/**\n * Vue wrapper around {@link bindDecimal}. Same lifecycle contract as\n * {@link InputMask}: bind in `onMounted`, dispose in `onBeforeUnmount`/before\n * every rebind. `onMounted` never runs during SSR, so no manual `window`\n * check is required.\n */\nexport const InputDecimal = defineComponent({\n name: 'InputDecimal',\n inheritAttrs: false,\n props: {\n options: { type: Object as PropType<Omit<BindDecimalOptions, 'onChange'>>, default: undefined },\n modelValue: { type: String, default: undefined },\n },\n emits: ['update:modelValue'],\n setup(props, { attrs, emit, expose }) {\n const inputRef = ref<HTMLInputElement | null>(null)\n let dispose: (() => void) | null = null\n\n const release = () => {\n dispose?.()\n dispose = null\n }\n\n const bindInput = () => {\n const input = inputRef.value\n if (!input) return\n release()\n const source = props.modelValue ?? input.value\n const next = processDecimal(source, props.options)\n if (input.value !== next) input.value = next\n dispose = bindDecimal(input, {\n ...props.options,\n onChange: (maskedValue, numericValue) => emit('update:modelValue', maskedValue, numericValue),\n })\n }\n\n onMounted(bindInput)\n onBeforeUnmount(release)\n\n watch(\n () => props.options,\n () => bindInput(),\n )\n\n watch(\n () => props.modelValue,\n (value) => {\n const input = inputRef.value\n if (!input || value === undefined || value === input.value) return\n const next = processDecimal(value, props.options)\n if (next === input.value) return\n bindInput()\n },\n )\n\n expose({ inputRef })\n\n return () => h('input', { inputmode: 'decimal', ...attrs, ref: inputRef, type: 'text' })\n },\n})\n\nexport default InputDecimal\n"],"mappings":"iHAcA,MAAa,GAAA,EAAYA,EAAAA,gBAAAA,CAAgB,CACvC,KAAM,YACN,aAAc,GACd,MAAO,CACL,KAAM,CAAE,KAAM,CAAC,OAAQ,MAAO,OAAQ,QAAQ,EAA4B,SAAU,EAAK,EACzF,QAAS,CAAE,KAAM,OAAmD,QAAS,IAAA,EAAU,EACvF,WAAY,CAAE,KAAM,OAAQ,QAAS,IAAA,EAAU,CACjD,EACA,MAAO,CAAC,mBAAmB,EAC3B,MAAM,EAAO,CAAE,QAAO,OAAM,UAAU,CACpC,IAAM,GAAA,EAAWC,EAAAA,IAAAA,CAA6B,IAAI,EAC9C,EAA+B,KAE7B,MAAgB,CACpB,IAAU,EACV,EAAU,IACZ,EAEM,MAAkB,CACtB,IAAM,EAAQ,EAAS,MACvB,GAAI,CAAC,EAAO,OACZ,EAAQ,EACR,IAAM,EAAS,EAAM,YAAc,EAAM,MACnC,GAAA,EAAOC,EAAAA,QAAAA,CAAQ,EAAQ,EAAM,KAAM,EAAM,OAAO,EAClD,EAAM,QAAU,IAAM,EAAM,MAAQ,GACxC,GAAA,EAAUC,EAAAA,KAAAA,CAAK,EAAO,EAAM,KAAM,CAChC,GAAG,EAAM,QACT,SAAW,GAAgB,EAAK,oBAAqB,CAAW,CAClE,CAAC,CACH,EA4BA,OAxBA,EAAA,EAAA,UAAA,CAAU,CAAS,GACnB,EAAA,EAAA,gBAAA,CAAgB,CAAO,GAGvB,EAAA,EAAA,MAAA,KACQ,CAAC,EAAM,KAAM,EAAM,OAAO,MAC1B,EAAU,CAClB,GAIA,EAAA,EAAA,MAAA,KACQ,EAAM,WACX,GAAU,CACT,IAAM,EAAQ,EAAS,MAClB,GAAS,IAAU,IAAA,IAAa,IAAU,EAAM,QAErD,EADaD,EAAAA,QAAAA,CAAQ,EAAO,EAAM,KAAM,EAAM,OACvC,IAAM,EAAM,OACnB,EAAU,CACZ,CACF,EAEA,EAAO,CAAE,UAAS,CAAC,OAEnB,EAAaE,EAAAA,EAAAA,CAAE,QAAS,CAAE,GAAG,EAAO,IAAK,EAAU,KAAM,MAAO,CAAC,CACnE,CACF,CAAC,EC1DY,GAAA,EAAeC,EAAAA,gBAAAA,CAAgB,CAC1C,KAAM,eACN,aAAc,GACd,MAAO,CACL,QAAS,CAAE,KAAM,OAA0D,QAAS,IAAA,EAAU,EAC9F,WAAY,CAAE,KAAM,OAAQ,QAAS,IAAA,EAAU,CACjD,EACA,MAAO,CAAC,mBAAmB,EAC3B,MAAM,EAAO,CAAE,QAAO,OAAM,UAAU,CACpC,IAAM,GAAA,EAAWC,EAAAA,IAAAA,CAA6B,IAAI,EAC9C,EAA+B,KAE7B,MAAgB,CACpB,IAAU,EACV,EAAU,IACZ,EAEM,MAAkB,CACtB,IAAM,EAAQ,EAAS,MACvB,GAAI,CAAC,EAAO,OACZ,EAAQ,EACR,IAAM,EAAS,EAAM,YAAc,EAAM,MACnC,GAAA,EAAOC,EAAAA,eAAAA,CAAe,EAAQ,EAAM,OAAO,EAC7C,EAAM,QAAU,IAAM,EAAM,MAAQ,GACxC,GAAA,EAAUC,EAAAA,YAAAA,CAAY,EAAO,CAC3B,GAAG,EAAM,QACT,UAAW,EAAa,IAAiB,EAAK,oBAAqB,EAAa,CAAY,CAC9F,CAAC,CACH,EAuBA,OArBA,EAAA,EAAA,UAAA,CAAU,CAAS,GACnB,EAAA,EAAA,gBAAA,CAAgB,CAAO,GAEvB,EAAA,EAAA,MAAA,KACQ,EAAM,YACN,EAAU,CAClB,GAEA,EAAA,EAAA,MAAA,KACQ,EAAM,WACX,GAAU,CACT,IAAM,EAAQ,EAAS,MAClB,GAAS,IAAU,IAAA,IAAa,IAAU,EAAM,QAErD,EADaD,EAAAA,eAAAA,CAAe,EAAO,EAAM,OAClC,IAAM,EAAM,OACnB,EAAU,CACZ,CACF,EAEA,EAAO,CAAE,UAAS,CAAC,OAEnB,EAAaE,EAAAA,EAAAA,CAAE,QAAS,CAAE,UAAW,UAAW,GAAG,EAAO,IAAK,EAAU,KAAM,MAAO,CAAC,CACzF,CACF,CAAC"}
1
+ {"version":3,"file":"vue.cjs","names":["states","release","rebind","process","bind","sync","processDecimal","bindDecimal"],"sources":["../src/vue/mask-directive.ts","../src/vue/decimal-directive.ts"],"sourcesContent":["import type { Directive } from 'vue'\nimport { bind, process } from 'mother-mask'\nimport type { BindOptions, MaskPattern } from 'mother-mask'\n\nexport interface MaskDirectiveParams {\n mask: MaskPattern\n options?: Omit<BindOptions, 'onChange'>\n value?: string\n onValueChange?: (value: string) => void\n}\n\ninterface MaskDirectiveState {\n current: MaskDirectiveParams\n dispose: (() => void) | null\n lastEmitted: string | null\n}\n\n// A Vue directive definition object is shared across every element it's\n// bound to (unlike a Svelte action or Solid directive, both invoked fresh\n// per element with their own closure) per-element state is keyed here by\n// the element instead.\nconst states = new WeakMap<HTMLInputElement, MaskDirectiveState>()\n\nfunction release(state: MaskDirectiveState) {\n state.dispose?.()\n state.dispose = null\n}\n\nfunction rebind(element: HTMLInputElement, state: MaskDirectiveState) {\n release(state)\n const { mask, options, value } = state.current\n const source = value ?? element.value\n const next = process(source, mask, options)\n if (element.value !== next) element.value = next\n state.dispose = bind(element, mask, {\n ...options,\n onChange: (maskedValue) => {\n state.lastEmitted = maskedValue\n state.current.onValueChange?.(maskedValue)\n },\n })\n}\n\nfunction sync(element: HTMLInputElement, state: MaskDirectiveState, next: MaskDirectiveParams) {\n const prev = state.current\n // An echo of our own onChange shows up as an update with an unchanged\n // mask/options and a value we just emitted leave the live binding and\n // its editing state alone.\n const configChanged = next.mask !== prev.mask || next.options !== prev.options\n const externalValueChange = next.value !== undefined && next.value !== state.lastEmitted && next.value !== element.value\n state.current = next\n if (configChanged || externalValueChange) rebind(element, state)\n}\n\n/**\n * Vue custom directive wrapping {@link bind}: `v-mother-mask=\"{ mask, options, value, onValueChange }\"`.\n *\n * Exported pre-named `vMotherMask` — Vue's own convention for a local\n * directive in `<script setup>` is a top-level binding whose name is\n * `v` + PascalCase, so `import { vMotherMask } from 'mother-mask/vue'`\n * makes `v-mother-mask` available with no import rename. Register it\n * globally instead with `app.directive('mother-mask', vMotherMask)` to make\n * `v-mother-mask` available in every component.\n *\n * `mounted` runs only once Vue mounts the element to the live DOM — Vue\n * never invokes directive hooks during server-side rendering, so this is\n * SSR-safe with no `typeof window` guard needed. `updated` runs on every\n * re-render of the owning component (not only when the bound value actually\n * changes), so it rebinds only if the mask, options, or an externally-set\n * value actually changed. `beforeUnmount` guarantees `dispose()` runs\n * exactly once.\n */\nexport const vMotherMask: Directive<HTMLInputElement, MaskDirectiveParams> = {\n mounted(element, binding) {\n const state: MaskDirectiveState = { current: binding.value, dispose: null, lastEmitted: null }\n states.set(element, state)\n rebind(element, state)\n },\n updated(element, binding) {\n // `mounted` always runs first and seeds this element's entry, so it's\n // never missing here — Vue never calls `updated` on an element it\n // hasn't mounted the directive on.\n sync(element, states.get(element)!, binding.value)\n },\n beforeUnmount(element) {\n const state = states.get(element)!\n release(state)\n states.delete(element)\n },\n}\n\ndeclare module 'vue' {\n interface GlobalDirectives {\n vMotherMask: Directive<HTMLInputElement, MaskDirectiveParams>\n }\n}\n","import type { Directive } from 'vue'\nimport { bindDecimal, processDecimal } from 'mother-mask'\nimport type { BindDecimalOptions } from 'mother-mask'\n\nexport interface DecimalDirectiveParams {\n options?: Omit<BindDecimalOptions, 'onChange'>\n value?: string\n onValueChange?: (value: string, numericValue: number) => void\n}\n\ninterface DecimalDirectiveState {\n current: DecimalDirectiveParams\n dispose: (() => void) | null\n lastEmitted: string | null\n}\n\nconst states = new WeakMap<HTMLInputElement, DecimalDirectiveState>()\n\nfunction release(state: DecimalDirectiveState) {\n state.dispose?.()\n state.dispose = null\n}\n\nfunction rebind(element: HTMLInputElement, state: DecimalDirectiveState) {\n release(state)\n const { options, value } = state.current\n const source = value ?? element.value\n const next = processDecimal(source, options)\n if (element.value !== next) element.value = next\n state.dispose = bindDecimal(element, {\n ...options,\n onChange: (maskedValue, numericValue) => {\n state.lastEmitted = maskedValue\n state.current.onValueChange?.(maskedValue, numericValue)\n },\n })\n}\n\nfunction sync(element: HTMLInputElement, state: DecimalDirectiveState, next: DecimalDirectiveParams) {\n const prev = state.current\n const configChanged = next.options !== prev.options\n const externalValueChange = next.value !== undefined && next.value !== state.lastEmitted && next.value !== element.value\n state.current = next\n if (configChanged || externalValueChange) rebind(element, state)\n}\n\n/**\n * Vue custom directive wrapping {@link bindDecimal}: `v-mother-mask-decimal=\"{ options, value, onValueChange }\"`.\n * Exported pre-named `vMotherMaskDecimal`, same naming rationale and\n * directive lifecycle contract as {@link vMotherMask}: `mounted` binds\n * once, `updated` rebinds reactively, `beforeUnmount` guarantees\n * `dispose()`. Always sets `inputmode=\"decimal\"` on mount.\n */\nexport const vMotherMaskDecimal: Directive<HTMLInputElement, DecimalDirectiveParams> = {\n mounted(element, binding) {\n element.setAttribute('inputmode', 'decimal')\n const state: DecimalDirectiveState = { current: binding.value, dispose: null, lastEmitted: null }\n states.set(element, state)\n rebind(element, state)\n },\n updated(element, binding) {\n // `mounted` always runs first and seeds this element's entry, so it's\n // never missing here — Vue never calls `updated` on an element it\n // hasn't mounted the directive on.\n sync(element, states.get(element)!, binding.value)\n },\n beforeUnmount(element) {\n const state = states.get(element)!\n release(state)\n states.delete(element)\n },\n}\n\ndeclare module 'vue' {\n interface GlobalDirectives {\n vMotherMaskDecimal: Directive<HTMLInputElement, DecimalDirectiveParams>\n }\n}\n"],"mappings":"gGAqBA,MAAMA,EAAS,IAAI,QAEnB,SAASC,EAAQ,EAA2B,CAC1C,EAAM,UAAU,EAChB,EAAM,QAAU,IAClB,CAEA,SAASC,EAAO,EAA2B,EAA2B,CACpE,EAAQ,CAAK,EACb,GAAM,CAAE,OAAM,UAAS,SAAU,EAAM,QACjC,EAAS,GAAS,EAAQ,MAC1B,GAAA,EAAOC,EAAAA,QAAAA,CAAQ,EAAQ,EAAM,CAAO,EACtC,EAAQ,QAAU,IAAM,EAAQ,MAAQ,GAC5C,EAAM,SAAA,EAAUC,EAAAA,KAAAA,CAAK,EAAS,EAAM,CAClC,GAAG,EACH,SAAW,GAAgB,CACzB,EAAM,YAAc,EACpB,EAAM,QAAQ,gBAAgB,CAAW,CAC3C,CACF,CAAC,CACH,CAEA,SAASC,EAAK,EAA2B,EAA2B,EAA2B,CAC7F,IAAM,EAAO,EAAM,QAIb,EAAgB,EAAK,OAAS,EAAK,MAAQ,EAAK,UAAY,EAAK,QACjE,EAAsB,EAAK,QAAU,IAAA,IAAa,EAAK,QAAU,EAAM,aAAe,EAAK,QAAU,EAAQ,MACnH,EAAM,QAAU,GACZ,GAAiB,IAAqB,EAAO,EAAS,CAAK,CACjE,CAoBA,MAAa,EAAgE,CAC3E,QAAQ,EAAS,EAAS,CACxB,IAAM,EAA4B,CAAE,QAAS,EAAQ,MAAO,QAAS,KAAM,YAAa,IAAK,EAC7F,EAAO,IAAI,EAAS,CAAK,EACzB,EAAO,EAAS,CAAK,CACvB,EACA,QAAQ,EAAS,EAAS,CAIxB,EAAK,EAASL,EAAO,IAAI,CAAO,EAAI,EAAQ,KAAK,CACnD,EACA,cAAc,EAAS,CAErB,EADcA,EAAO,IAAI,CACb,CAAC,EACb,EAAO,OAAO,CAAO,CACvB,CACF,ECzEM,EAAS,IAAI,QAEnB,SAAS,EAAQ,EAA8B,CAC7C,EAAM,UAAU,EAChB,EAAM,QAAU,IAClB,CAEA,SAAS,EAAO,EAA2B,EAA8B,CACvE,EAAQ,CAAK,EACb,GAAM,CAAE,UAAS,SAAU,EAAM,QAC3B,EAAS,GAAS,EAAQ,MAC1B,GAAA,EAAOM,EAAAA,eAAAA,CAAe,EAAQ,CAAO,EACvC,EAAQ,QAAU,IAAM,EAAQ,MAAQ,GAC5C,EAAM,SAAA,EAAUC,EAAAA,YAAAA,CAAY,EAAS,CACnC,GAAG,EACH,UAAW,EAAa,IAAiB,CACvC,EAAM,YAAc,EACpB,EAAM,QAAQ,gBAAgB,EAAa,CAAY,CACzD,CACF,CAAC,CACH,CAEA,SAAS,EAAK,EAA2B,EAA8B,EAA8B,CACnG,IAAM,EAAO,EAAM,QACb,EAAgB,EAAK,UAAY,EAAK,QACtC,EAAsB,EAAK,QAAU,IAAA,IAAa,EAAK,QAAU,EAAM,aAAe,EAAK,QAAU,EAAQ,MACnH,EAAM,QAAU,GACZ,GAAiB,IAAqB,EAAO,EAAS,CAAK,CACjE,CASA,MAAa,EAA0E,CACrF,QAAQ,EAAS,EAAS,CACxB,EAAQ,aAAa,YAAa,SAAS,EAC3C,IAAM,EAA+B,CAAE,QAAS,EAAQ,MAAO,QAAS,KAAM,YAAa,IAAK,EAChG,EAAO,IAAI,EAAS,CAAK,EACzB,EAAO,EAAS,CAAK,CACvB,EACA,QAAQ,EAAS,EAAS,CAIxB,EAAK,EAAS,EAAO,IAAI,CAAO,EAAI,EAAQ,KAAK,CACnD,EACA,cAAc,EAAS,CAErB,EADc,EAAO,IAAI,CACb,CAAC,EACb,EAAO,OAAO,CAAO,CACvB,CACF"}
package/dist/vue.d.cts CHANGED
@@ -1,86 +1,57 @@
1
1
  import { BindDecimalOptions, BindOptions, MaskPattern } from "mother-mask";
2
- import { PropType } from "vue";
2
+ import { Directive } from "vue";
3
3
  export * from "mother-mask";
4
- //#region src/vue/InputMask.d.ts
5
- type InputMaskEmits = {
6
- 'update:modelValue': [value: string];
7
- };
4
+ //#region src/vue/mask-directive.d.ts
5
+ interface MaskDirectiveParams {
6
+ mask: MaskPattern;
7
+ options?: Omit<BindOptions, 'onChange'>;
8
+ value?: string;
9
+ onValueChange?: (value: string) => void;
10
+ }
8
11
  /**
9
- * Vue wrapper around {@link bind}. Binds on `onMounted` (a client-only
10
- * lifecycle hook — it never runs during SSR, so this component is safe to
11
- * render on the server) and disposes on `onUnmounted`/before a rebind.
12
+ * Vue custom directive wrapping {@link bind}: `v-mother-mask="{ mask, options, value, onValueChange }"`.
13
+ *
14
+ * Exported pre-named `vMotherMask` Vue's own convention for a local
15
+ * directive in `<script setup>` is a top-level binding whose name is
16
+ * `v` + PascalCase, so `import { vMotherMask } from 'mother-mask/vue'`
17
+ * makes `v-mother-mask` available with no import rename. Register it
18
+ * globally instead with `app.directive('mother-mask', vMotherMask)` to make
19
+ * `v-mother-mask` available in every component.
20
+ *
21
+ * `mounted` runs only once Vue mounts the element to the live DOM — Vue
22
+ * never invokes directive hooks during server-side rendering, so this is
23
+ * SSR-safe with no `typeof window` guard needed. `updated` runs on every
24
+ * re-render of the owning component (not only when the bound value actually
25
+ * changes), so it rebinds only if the mask, options, or an externally-set
26
+ * value actually changed. `beforeUnmount` guarantees `dispose()` runs
27
+ * exactly once.
12
28
  */
13
- declare const InputMask: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
14
- mask: {
15
- type: PropType<MaskPattern>;
16
- required: true;
17
- };
18
- options: {
19
- type: PropType<Omit<BindOptions, 'onChange'>>;
20
- default: undefined;
21
- };
22
- modelValue: {
23
- type: StringConstructor;
24
- default: undefined;
25
- };
26
- }>, () => import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
27
- [key: string]: any;
28
- }>, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, "update:modelValue"[], "update:modelValue", import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
29
- mask: {
30
- type: PropType<MaskPattern>;
31
- required: true;
32
- };
33
- options: {
34
- type: PropType<Omit<BindOptions, 'onChange'>>;
35
- default: undefined;
36
- };
37
- modelValue: {
38
- type: StringConstructor;
39
- default: undefined;
40
- };
41
- }>> & Readonly<{
42
- "onUpdate:modelValue"?: ((...args: any[]) => any) | undefined;
43
- }>, {
44
- options: Omit<BindOptions, "onChange">;
45
- modelValue: string;
46
- }, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
29
+ declare const vMotherMask: Directive<HTMLInputElement, MaskDirectiveParams>;
30
+ declare module 'vue' {
31
+ interface GlobalDirectives {
32
+ vMotherMask: Directive<HTMLInputElement, MaskDirectiveParams>;
33
+ }
34
+ }
47
35
  //#endregion
48
- //#region src/vue/InputDecimal.d.ts
49
- type InputDecimalEmits = {
50
- 'update:modelValue': [value: string, numericValue: number];
51
- };
36
+ //#region src/vue/decimal-directive.d.ts
37
+ interface DecimalDirectiveParams {
38
+ options?: Omit<BindDecimalOptions, 'onChange'>;
39
+ value?: string;
40
+ onValueChange?: (value: string, numericValue: number) => void;
41
+ }
52
42
  /**
53
- * Vue wrapper around {@link bindDecimal}. Same lifecycle contract as
54
- * {@link InputMask}: bind in `onMounted`, dispose in `onBeforeUnmount`/before
55
- * every rebind. `onMounted` never runs during SSR, so no manual `window`
56
- * check is required.
43
+ * Vue custom directive wrapping {@link bindDecimal}: `v-mother-mask-decimal="{ options, value, onValueChange }"`.
44
+ * Exported pre-named `vMotherMaskDecimal`, same naming rationale and
45
+ * directive lifecycle contract as {@link vMotherMask}: `mounted` binds
46
+ * once, `updated` rebinds reactively, `beforeUnmount` guarantees
47
+ * `dispose()`. Always sets `inputmode="decimal"` on mount.
57
48
  */
58
- declare const InputDecimal: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
59
- options: {
60
- type: PropType<Omit<BindDecimalOptions, 'onChange'>>;
61
- default: undefined;
62
- };
63
- modelValue: {
64
- type: StringConstructor;
65
- default: undefined;
66
- };
67
- }>, () => import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
68
- [key: string]: any;
69
- }>, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, "update:modelValue"[], "update:modelValue", import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
70
- options: {
71
- type: PropType<Omit<BindDecimalOptions, 'onChange'>>;
72
- default: undefined;
73
- };
74
- modelValue: {
75
- type: StringConstructor;
76
- default: undefined;
77
- };
78
- }>> & Readonly<{
79
- "onUpdate:modelValue"?: ((...args: any[]) => any) | undefined;
80
- }>, {
81
- options: Omit<BindDecimalOptions, "onChange">;
82
- modelValue: string;
83
- }, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
49
+ declare const vMotherMaskDecimal: Directive<HTMLInputElement, DecimalDirectiveParams>;
50
+ declare module 'vue' {
51
+ interface GlobalDirectives {
52
+ vMotherMaskDecimal: Directive<HTMLInputElement, DecimalDirectiveParams>;
53
+ }
54
+ }
84
55
  //#endregion
85
- export { InputDecimal, type InputDecimalEmits, InputMask, type InputMaskEmits };
56
+ export { type DecimalDirectiveParams, type MaskDirectiveParams, vMotherMask, vMotherMaskDecimal };
86
57
  //# sourceMappingURL=vue.d.cts.map
package/dist/vue.d.mts CHANGED
@@ -1,86 +1,57 @@
1
1
  import { BindDecimalOptions, BindOptions, MaskPattern } from "mother-mask";
2
- import { PropType } from "vue";
2
+ import { Directive } from "vue";
3
3
  export * from "mother-mask";
4
- //#region src/vue/InputMask.d.ts
5
- type InputMaskEmits = {
6
- 'update:modelValue': [value: string];
7
- };
4
+ //#region src/vue/mask-directive.d.ts
5
+ interface MaskDirectiveParams {
6
+ mask: MaskPattern;
7
+ options?: Omit<BindOptions, 'onChange'>;
8
+ value?: string;
9
+ onValueChange?: (value: string) => void;
10
+ }
8
11
  /**
9
- * Vue wrapper around {@link bind}. Binds on `onMounted` (a client-only
10
- * lifecycle hook — it never runs during SSR, so this component is safe to
11
- * render on the server) and disposes on `onUnmounted`/before a rebind.
12
+ * Vue custom directive wrapping {@link bind}: `v-mother-mask="{ mask, options, value, onValueChange }"`.
13
+ *
14
+ * Exported pre-named `vMotherMask` Vue's own convention for a local
15
+ * directive in `<script setup>` is a top-level binding whose name is
16
+ * `v` + PascalCase, so `import { vMotherMask } from 'mother-mask/vue'`
17
+ * makes `v-mother-mask` available with no import rename. Register it
18
+ * globally instead with `app.directive('mother-mask', vMotherMask)` to make
19
+ * `v-mother-mask` available in every component.
20
+ *
21
+ * `mounted` runs only once Vue mounts the element to the live DOM — Vue
22
+ * never invokes directive hooks during server-side rendering, so this is
23
+ * SSR-safe with no `typeof window` guard needed. `updated` runs on every
24
+ * re-render of the owning component (not only when the bound value actually
25
+ * changes), so it rebinds only if the mask, options, or an externally-set
26
+ * value actually changed. `beforeUnmount` guarantees `dispose()` runs
27
+ * exactly once.
12
28
  */
13
- declare const InputMask: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
14
- mask: {
15
- type: PropType<MaskPattern>;
16
- required: true;
17
- };
18
- options: {
19
- type: PropType<Omit<BindOptions, 'onChange'>>;
20
- default: undefined;
21
- };
22
- modelValue: {
23
- type: StringConstructor;
24
- default: undefined;
25
- };
26
- }>, () => import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
27
- [key: string]: any;
28
- }>, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, "update:modelValue"[], "update:modelValue", import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
29
- mask: {
30
- type: PropType<MaskPattern>;
31
- required: true;
32
- };
33
- options: {
34
- type: PropType<Omit<BindOptions, 'onChange'>>;
35
- default: undefined;
36
- };
37
- modelValue: {
38
- type: StringConstructor;
39
- default: undefined;
40
- };
41
- }>> & Readonly<{
42
- "onUpdate:modelValue"?: ((...args: any[]) => any) | undefined;
43
- }>, {
44
- options: Omit<BindOptions, "onChange">;
45
- modelValue: string;
46
- }, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
29
+ declare const vMotherMask: Directive<HTMLInputElement, MaskDirectiveParams>;
30
+ declare module 'vue' {
31
+ interface GlobalDirectives {
32
+ vMotherMask: Directive<HTMLInputElement, MaskDirectiveParams>;
33
+ }
34
+ }
47
35
  //#endregion
48
- //#region src/vue/InputDecimal.d.ts
49
- type InputDecimalEmits = {
50
- 'update:modelValue': [value: string, numericValue: number];
51
- };
36
+ //#region src/vue/decimal-directive.d.ts
37
+ interface DecimalDirectiveParams {
38
+ options?: Omit<BindDecimalOptions, 'onChange'>;
39
+ value?: string;
40
+ onValueChange?: (value: string, numericValue: number) => void;
41
+ }
52
42
  /**
53
- * Vue wrapper around {@link bindDecimal}. Same lifecycle contract as
54
- * {@link InputMask}: bind in `onMounted`, dispose in `onBeforeUnmount`/before
55
- * every rebind. `onMounted` never runs during SSR, so no manual `window`
56
- * check is required.
43
+ * Vue custom directive wrapping {@link bindDecimal}: `v-mother-mask-decimal="{ options, value, onValueChange }"`.
44
+ * Exported pre-named `vMotherMaskDecimal`, same naming rationale and
45
+ * directive lifecycle contract as {@link vMotherMask}: `mounted` binds
46
+ * once, `updated` rebinds reactively, `beforeUnmount` guarantees
47
+ * `dispose()`. Always sets `inputmode="decimal"` on mount.
57
48
  */
58
- declare const InputDecimal: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
59
- options: {
60
- type: PropType<Omit<BindDecimalOptions, 'onChange'>>;
61
- default: undefined;
62
- };
63
- modelValue: {
64
- type: StringConstructor;
65
- default: undefined;
66
- };
67
- }>, () => import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
68
- [key: string]: any;
69
- }>, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, "update:modelValue"[], "update:modelValue", import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
70
- options: {
71
- type: PropType<Omit<BindDecimalOptions, 'onChange'>>;
72
- default: undefined;
73
- };
74
- modelValue: {
75
- type: StringConstructor;
76
- default: undefined;
77
- };
78
- }>> & Readonly<{
79
- "onUpdate:modelValue"?: ((...args: any[]) => any) | undefined;
80
- }>, {
81
- options: Omit<BindDecimalOptions, "onChange">;
82
- modelValue: string;
83
- }, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
49
+ declare const vMotherMaskDecimal: Directive<HTMLInputElement, DecimalDirectiveParams>;
50
+ declare module 'vue' {
51
+ interface GlobalDirectives {
52
+ vMotherMaskDecimal: Directive<HTMLInputElement, DecimalDirectiveParams>;
53
+ }
54
+ }
84
55
  //#endregion
85
- export { InputDecimal, type InputDecimalEmits, InputMask, type InputMaskEmits };
56
+ export { type DecimalDirectiveParams, type MaskDirectiveParams, vMotherMask, vMotherMaskDecimal };
86
57
  //# sourceMappingURL=vue.d.mts.map
package/dist/vue.mjs CHANGED
@@ -1,2 +1,2 @@
1
- import{bind as e,bindDecimal as t,process as n,processDecimal as r}from"mother-mask";import{defineComponent as i,h as a,onBeforeUnmount as o,onMounted as s,ref as c,watch as l}from"vue";export*from"mother-mask";const u=i({name:`InputMask`,inheritAttrs:!1,props:{mask:{type:[String,Array,Object,Function],required:!0},options:{type:Object,default:void 0},modelValue:{type:String,default:void 0}},emits:[`update:modelValue`],setup(t,{attrs:r,emit:i,expose:u}){let d=c(null),f=null,p=()=>{f?.(),f=null},m=()=>{let r=d.value;if(!r)return;p();let a=t.modelValue??r.value,o=n(a,t.mask,t.options);r.value!==o&&(r.value=o),f=e(r,t.mask,{...t.options,onChange:e=>i(`update:modelValue`,e)})};return s(m),o(p),l(()=>[t.mask,t.options],()=>m()),l(()=>t.modelValue,e=>{let r=d.value;r&&e!==void 0&&e!==r.value&&n(e,t.mask,t.options)!==r.value&&m()}),u({inputRef:d}),()=>a(`input`,{...r,ref:d,type:`text`})}}),d=i({name:`InputDecimal`,inheritAttrs:!1,props:{options:{type:Object,default:void 0},modelValue:{type:String,default:void 0}},emits:[`update:modelValue`],setup(e,{attrs:n,emit:i,expose:u}){let d=c(null),f=null,p=()=>{f?.(),f=null},m=()=>{let n=d.value;if(!n)return;p();let a=e.modelValue??n.value,o=r(a,e.options);n.value!==o&&(n.value=o),f=t(n,{...e.options,onChange:(e,t)=>i(`update:modelValue`,e,t)})};return s(m),o(p),l(()=>e.options,()=>m()),l(()=>e.modelValue,t=>{let n=d.value;n&&t!==void 0&&t!==n.value&&r(t,e.options)!==n.value&&m()}),u({inputRef:d}),()=>a(`input`,{inputmode:`decimal`,...n,ref:d,type:`text`})}});export{d as InputDecimal,u as InputMask};
1
+ import{bind as e,bindDecimal as t,process as n,processDecimal as r}from"mother-mask";export*from"mother-mask";const i=new WeakMap;function a(e){e.dispose?.(),e.dispose=null}function o(t,r){a(r);let{mask:i,options:o,value:s}=r.current,c=s??t.value,l=n(c,i,o);t.value!==l&&(t.value=l),r.dispose=e(t,i,{...o,onChange:e=>{r.lastEmitted=e,r.current.onValueChange?.(e)}})}function s(e,t,n){let r=t.current,i=n.mask!==r.mask||n.options!==r.options,a=n.value!==void 0&&n.value!==t.lastEmitted&&n.value!==e.value;t.current=n,(i||a)&&o(e,t)}const c={mounted(e,t){let n={current:t.value,dispose:null,lastEmitted:null};i.set(e,n),o(e,n)},updated(e,t){s(e,i.get(e),t.value)},beforeUnmount(e){a(i.get(e)),i.delete(e)}},l=new WeakMap;function u(e){e.dispose?.(),e.dispose=null}function d(e,n){u(n);let{options:i,value:a}=n.current,o=a??e.value,s=r(o,i);e.value!==s&&(e.value=s),n.dispose=t(e,{...i,onChange:(e,t)=>{n.lastEmitted=e,n.current.onValueChange?.(e,t)}})}function f(e,t,n){let r=t.current,i=n.options!==r.options,a=n.value!==void 0&&n.value!==t.lastEmitted&&n.value!==e.value;t.current=n,(i||a)&&d(e,t)}const p={mounted(e,t){e.setAttribute(`inputmode`,`decimal`);let n={current:t.value,dispose:null,lastEmitted:null};l.set(e,n),d(e,n)},updated(e,t){f(e,l.get(e),t.value)},beforeUnmount(e){u(l.get(e)),l.delete(e)}};export{c as vMotherMask,p as vMotherMaskDecimal};
2
2
  //# sourceMappingURL=vue.mjs.map
package/dist/vue.mjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"vue.mjs","names":[],"sources":["../src/vue/InputMask.ts","../src/vue/InputDecimal.ts"],"sourcesContent":["import { defineComponent, h, onBeforeUnmount, onMounted, ref, watch } from 'vue'\nimport type { PropType } from 'vue'\nimport { bind, process } from 'mother-mask'\nimport type { BindOptions, MaskPattern } from 'mother-mask'\n\nexport type InputMaskEmits = {\n 'update:modelValue': [value: string]\n}\n\n/**\n * Vue wrapper around {@link bind}. Binds on `onMounted` (a client-only\n * lifecycle hook — it never runs during SSR, so this component is safe to\n * render on the server) and disposes on `onUnmounted`/before a rebind.\n */\nexport const InputMask = defineComponent({\n name: 'InputMask',\n inheritAttrs: false,\n props: {\n mask: { type: [String, Array, Object, Function] as PropType<MaskPattern>, required: true },\n options: { type: Object as PropType<Omit<BindOptions, 'onChange'>>, default: undefined },\n modelValue: { type: String, default: undefined },\n },\n emits: ['update:modelValue'],\n setup(props, { attrs, emit, expose }) {\n const inputRef = ref<HTMLInputElement | null>(null)\n let dispose: (() => void) | null = null\n\n const release = () => {\n dispose?.()\n dispose = null\n }\n\n const bindInput = () => {\n const input = inputRef.value\n if (!input) return\n release()\n const source = props.modelValue ?? input.value\n const next = process(source, props.mask, props.options)\n if (input.value !== next) input.value = next\n dispose = bind(input, props.mask, {\n ...props.options,\n onChange: (maskedValue) => emit('update:modelValue', maskedValue),\n })\n }\n\n // Runs client-side only; a no-op during SSR, so no `typeof window` guard\n // is needed here the component never touches the DOM off the client.\n onMounted(bindInput)\n onBeforeUnmount(release)\n\n // Mask/options changes need a fresh binding (handlers close over them).\n watch(\n () => [props.mask, props.options],\n () => bindInput(),\n )\n\n // An external `modelValue` update (v-model) reformats and rebinds so the\n // mask's internal edit history restarts from the new value.\n watch(\n () => props.modelValue,\n (value) => {\n const input = inputRef.value\n if (!input || value === undefined || value === input.value) return\n const next = process(value, props.mask, props.options)\n if (next === input.value) return\n bindInput()\n },\n )\n\n expose({ inputRef })\n\n return () => h('input', { ...attrs, ref: inputRef, type: 'text' })\n },\n})\n\nexport default InputMask\n","import { defineComponent, h, onBeforeUnmount, onMounted, ref, watch } from 'vue'\nimport type { PropType } from 'vue'\nimport { bindDecimal, processDecimal } from 'mother-mask'\nimport type { BindDecimalOptions } from 'mother-mask'\n\nexport type InputDecimalEmits = {\n 'update:modelValue': [value: string, numericValue: number]\n}\n\n/**\n * Vue wrapper around {@link bindDecimal}. Same lifecycle contract as\n * {@link InputMask}: bind in `onMounted`, dispose in `onBeforeUnmount`/before\n * every rebind. `onMounted` never runs during SSR, so no manual `window`\n * check is required.\n */\nexport const InputDecimal = defineComponent({\n name: 'InputDecimal',\n inheritAttrs: false,\n props: {\n options: { type: Object as PropType<Omit<BindDecimalOptions, 'onChange'>>, default: undefined },\n modelValue: { type: String, default: undefined },\n },\n emits: ['update:modelValue'],\n setup(props, { attrs, emit, expose }) {\n const inputRef = ref<HTMLInputElement | null>(null)\n let dispose: (() => void) | null = null\n\n const release = () => {\n dispose?.()\n dispose = null\n }\n\n const bindInput = () => {\n const input = inputRef.value\n if (!input) return\n release()\n const source = props.modelValue ?? input.value\n const next = processDecimal(source, props.options)\n if (input.value !== next) input.value = next\n dispose = bindDecimal(input, {\n ...props.options,\n onChange: (maskedValue, numericValue) => emit('update:modelValue', maskedValue, numericValue),\n })\n }\n\n onMounted(bindInput)\n onBeforeUnmount(release)\n\n watch(\n () => props.options,\n () => bindInput(),\n )\n\n watch(\n () => props.modelValue,\n (value) => {\n const input = inputRef.value\n if (!input || value === undefined || value === input.value) return\n const next = processDecimal(value, props.options)\n if (next === input.value) return\n bindInput()\n },\n )\n\n expose({ inputRef })\n\n return () => h('input', { inputmode: 'decimal', ...attrs, ref: inputRef, type: 'text' })\n },\n})\n\nexport default InputDecimal\n"],"mappings":"mNAcA,MAAa,EAAY,EAAgB,CACvC,KAAM,YACN,aAAc,GACd,MAAO,CACL,KAAM,CAAE,KAAM,CAAC,OAAQ,MAAO,OAAQ,QAAQ,EAA4B,SAAU,EAAK,EACzF,QAAS,CAAE,KAAM,OAAmD,QAAS,IAAA,EAAU,EACvF,WAAY,CAAE,KAAM,OAAQ,QAAS,IAAA,EAAU,CACjD,EACA,MAAO,CAAC,mBAAmB,EAC3B,MAAM,EAAO,CAAE,QAAO,OAAM,UAAU,CACpC,IAAM,EAAW,EAA6B,IAAI,EAC9C,EAA+B,KAE7B,MAAgB,CACpB,IAAU,EACV,EAAU,IACZ,EAEM,MAAkB,CACtB,IAAM,EAAQ,EAAS,MACvB,GAAI,CAAC,EAAO,OACZ,EAAQ,EACR,IAAM,EAAS,EAAM,YAAc,EAAM,MACnC,EAAO,EAAQ,EAAQ,EAAM,KAAM,EAAM,OAAO,EAClD,EAAM,QAAU,IAAM,EAAM,MAAQ,GACxC,EAAU,EAAK,EAAO,EAAM,KAAM,CAChC,GAAG,EAAM,QACT,SAAW,GAAgB,EAAK,oBAAqB,CAAW,CAClE,CAAC,CACH,EA4BA,OAxBA,EAAU,CAAS,EACnB,EAAgB,CAAO,EAGvB,MACQ,CAAC,EAAM,KAAM,EAAM,OAAO,MAC1B,EAAU,CAClB,EAIA,MACQ,EAAM,WACX,GAAU,CACT,IAAM,EAAQ,EAAS,MAClB,GAAS,IAAU,IAAA,IAAa,IAAU,EAAM,OACxC,EAAQ,EAAO,EAAM,KAAM,EAAM,OACvC,IAAM,EAAM,OACnB,EAAU,CACZ,CACF,EAEA,EAAO,CAAE,UAAS,CAAC,MAEN,EAAE,QAAS,CAAE,GAAG,EAAO,IAAK,EAAU,KAAM,MAAO,CAAC,CACnE,CACF,CAAC,EC1DY,EAAe,EAAgB,CAC1C,KAAM,eACN,aAAc,GACd,MAAO,CACL,QAAS,CAAE,KAAM,OAA0D,QAAS,IAAA,EAAU,EAC9F,WAAY,CAAE,KAAM,OAAQ,QAAS,IAAA,EAAU,CACjD,EACA,MAAO,CAAC,mBAAmB,EAC3B,MAAM,EAAO,CAAE,QAAO,OAAM,UAAU,CACpC,IAAM,EAAW,EAA6B,IAAI,EAC9C,EAA+B,KAE7B,MAAgB,CACpB,IAAU,EACV,EAAU,IACZ,EAEM,MAAkB,CACtB,IAAM,EAAQ,EAAS,MACvB,GAAI,CAAC,EAAO,OACZ,EAAQ,EACR,IAAM,EAAS,EAAM,YAAc,EAAM,MACnC,EAAO,EAAe,EAAQ,EAAM,OAAO,EAC7C,EAAM,QAAU,IAAM,EAAM,MAAQ,GACxC,EAAU,EAAY,EAAO,CAC3B,GAAG,EAAM,QACT,UAAW,EAAa,IAAiB,EAAK,oBAAqB,EAAa,CAAY,CAC9F,CAAC,CACH,EAuBA,OArBA,EAAU,CAAS,EACnB,EAAgB,CAAO,EAEvB,MACQ,EAAM,YACN,EAAU,CAClB,EAEA,MACQ,EAAM,WACX,GAAU,CACT,IAAM,EAAQ,EAAS,MAClB,GAAS,IAAU,IAAA,IAAa,IAAU,EAAM,OACxC,EAAe,EAAO,EAAM,OAClC,IAAM,EAAM,OACnB,EAAU,CACZ,CACF,EAEA,EAAO,CAAE,UAAS,CAAC,MAEN,EAAE,QAAS,CAAE,UAAW,UAAW,GAAG,EAAO,IAAK,EAAU,KAAM,MAAO,CAAC,CACzF,CACF,CAAC"}
1
+ {"version":3,"file":"vue.mjs","names":["states","release","rebind","sync"],"sources":["../src/vue/mask-directive.ts","../src/vue/decimal-directive.ts"],"sourcesContent":["import type { Directive } from 'vue'\nimport { bind, process } from 'mother-mask'\nimport type { BindOptions, MaskPattern } from 'mother-mask'\n\nexport interface MaskDirectiveParams {\n mask: MaskPattern\n options?: Omit<BindOptions, 'onChange'>\n value?: string\n onValueChange?: (value: string) => void\n}\n\ninterface MaskDirectiveState {\n current: MaskDirectiveParams\n dispose: (() => void) | null\n lastEmitted: string | null\n}\n\n// A Vue directive definition object is shared across every element it's\n// bound to (unlike a Svelte action or Solid directive, both invoked fresh\n// per element with their own closure) per-element state is keyed here by\n// the element instead.\nconst states = new WeakMap<HTMLInputElement, MaskDirectiveState>()\n\nfunction release(state: MaskDirectiveState) {\n state.dispose?.()\n state.dispose = null\n}\n\nfunction rebind(element: HTMLInputElement, state: MaskDirectiveState) {\n release(state)\n const { mask, options, value } = state.current\n const source = value ?? element.value\n const next = process(source, mask, options)\n if (element.value !== next) element.value = next\n state.dispose = bind(element, mask, {\n ...options,\n onChange: (maskedValue) => {\n state.lastEmitted = maskedValue\n state.current.onValueChange?.(maskedValue)\n },\n })\n}\n\nfunction sync(element: HTMLInputElement, state: MaskDirectiveState, next: MaskDirectiveParams) {\n const prev = state.current\n // An echo of our own onChange shows up as an update with an unchanged\n // mask/options and a value we just emitted leave the live binding and\n // its editing state alone.\n const configChanged = next.mask !== prev.mask || next.options !== prev.options\n const externalValueChange = next.value !== undefined && next.value !== state.lastEmitted && next.value !== element.value\n state.current = next\n if (configChanged || externalValueChange) rebind(element, state)\n}\n\n/**\n * Vue custom directive wrapping {@link bind}: `v-mother-mask=\"{ mask, options, value, onValueChange }\"`.\n *\n * Exported pre-named `vMotherMask` — Vue's own convention for a local\n * directive in `<script setup>` is a top-level binding whose name is\n * `v` + PascalCase, so `import { vMotherMask } from 'mother-mask/vue'`\n * makes `v-mother-mask` available with no import rename. Register it\n * globally instead with `app.directive('mother-mask', vMotherMask)` to make\n * `v-mother-mask` available in every component.\n *\n * `mounted` runs only once Vue mounts the element to the live DOM — Vue\n * never invokes directive hooks during server-side rendering, so this is\n * SSR-safe with no `typeof window` guard needed. `updated` runs on every\n * re-render of the owning component (not only when the bound value actually\n * changes), so it rebinds only if the mask, options, or an externally-set\n * value actually changed. `beforeUnmount` guarantees `dispose()` runs\n * exactly once.\n */\nexport const vMotherMask: Directive<HTMLInputElement, MaskDirectiveParams> = {\n mounted(element, binding) {\n const state: MaskDirectiveState = { current: binding.value, dispose: null, lastEmitted: null }\n states.set(element, state)\n rebind(element, state)\n },\n updated(element, binding) {\n // `mounted` always runs first and seeds this element's entry, so it's\n // never missing here — Vue never calls `updated` on an element it\n // hasn't mounted the directive on.\n sync(element, states.get(element)!, binding.value)\n },\n beforeUnmount(element) {\n const state = states.get(element)!\n release(state)\n states.delete(element)\n },\n}\n\ndeclare module 'vue' {\n interface GlobalDirectives {\n vMotherMask: Directive<HTMLInputElement, MaskDirectiveParams>\n }\n}\n","import type { Directive } from 'vue'\nimport { bindDecimal, processDecimal } from 'mother-mask'\nimport type { BindDecimalOptions } from 'mother-mask'\n\nexport interface DecimalDirectiveParams {\n options?: Omit<BindDecimalOptions, 'onChange'>\n value?: string\n onValueChange?: (value: string, numericValue: number) => void\n}\n\ninterface DecimalDirectiveState {\n current: DecimalDirectiveParams\n dispose: (() => void) | null\n lastEmitted: string | null\n}\n\nconst states = new WeakMap<HTMLInputElement, DecimalDirectiveState>()\n\nfunction release(state: DecimalDirectiveState) {\n state.dispose?.()\n state.dispose = null\n}\n\nfunction rebind(element: HTMLInputElement, state: DecimalDirectiveState) {\n release(state)\n const { options, value } = state.current\n const source = value ?? element.value\n const next = processDecimal(source, options)\n if (element.value !== next) element.value = next\n state.dispose = bindDecimal(element, {\n ...options,\n onChange: (maskedValue, numericValue) => {\n state.lastEmitted = maskedValue\n state.current.onValueChange?.(maskedValue, numericValue)\n },\n })\n}\n\nfunction sync(element: HTMLInputElement, state: DecimalDirectiveState, next: DecimalDirectiveParams) {\n const prev = state.current\n const configChanged = next.options !== prev.options\n const externalValueChange = next.value !== undefined && next.value !== state.lastEmitted && next.value !== element.value\n state.current = next\n if (configChanged || externalValueChange) rebind(element, state)\n}\n\n/**\n * Vue custom directive wrapping {@link bindDecimal}: `v-mother-mask-decimal=\"{ options, value, onValueChange }\"`.\n * Exported pre-named `vMotherMaskDecimal`, same naming rationale and\n * directive lifecycle contract as {@link vMotherMask}: `mounted` binds\n * once, `updated` rebinds reactively, `beforeUnmount` guarantees\n * `dispose()`. Always sets `inputmode=\"decimal\"` on mount.\n */\nexport const vMotherMaskDecimal: Directive<HTMLInputElement, DecimalDirectiveParams> = {\n mounted(element, binding) {\n element.setAttribute('inputmode', 'decimal')\n const state: DecimalDirectiveState = { current: binding.value, dispose: null, lastEmitted: null }\n states.set(element, state)\n rebind(element, state)\n },\n updated(element, binding) {\n // `mounted` always runs first and seeds this element's entry, so it's\n // never missing here — Vue never calls `updated` on an element it\n // hasn't mounted the directive on.\n sync(element, states.get(element)!, binding.value)\n },\n beforeUnmount(element) {\n const state = states.get(element)!\n release(state)\n states.delete(element)\n },\n}\n\ndeclare module 'vue' {\n interface GlobalDirectives {\n vMotherMaskDecimal: Directive<HTMLInputElement, DecimalDirectiveParams>\n }\n}\n"],"mappings":"8GAqBA,MAAMA,EAAS,IAAI,QAEnB,SAASC,EAAQ,EAA2B,CAC1C,EAAM,UAAU,EAChB,EAAM,QAAU,IAClB,CAEA,SAASC,EAAO,EAA2B,EAA2B,CACpE,EAAQ,CAAK,EACb,GAAM,CAAE,OAAM,UAAS,SAAU,EAAM,QACjC,EAAS,GAAS,EAAQ,MAC1B,EAAO,EAAQ,EAAQ,EAAM,CAAO,EACtC,EAAQ,QAAU,IAAM,EAAQ,MAAQ,GAC5C,EAAM,QAAU,EAAK,EAAS,EAAM,CAClC,GAAG,EACH,SAAW,GAAgB,CACzB,EAAM,YAAc,EACpB,EAAM,QAAQ,gBAAgB,CAAW,CAC3C,CACF,CAAC,CACH,CAEA,SAASC,EAAK,EAA2B,EAA2B,EAA2B,CAC7F,IAAM,EAAO,EAAM,QAIb,EAAgB,EAAK,OAAS,EAAK,MAAQ,EAAK,UAAY,EAAK,QACjE,EAAsB,EAAK,QAAU,IAAA,IAAa,EAAK,QAAU,EAAM,aAAe,EAAK,QAAU,EAAQ,MACnH,EAAM,QAAU,GACZ,GAAiB,IAAqB,EAAO,EAAS,CAAK,CACjE,CAoBA,MAAa,EAAgE,CAC3E,QAAQ,EAAS,EAAS,CACxB,IAAM,EAA4B,CAAE,QAAS,EAAQ,MAAO,QAAS,KAAM,YAAa,IAAK,EAC7F,EAAO,IAAI,EAAS,CAAK,EACzB,EAAO,EAAS,CAAK,CACvB,EACA,QAAQ,EAAS,EAAS,CAIxB,EAAK,EAASH,EAAO,IAAI,CAAO,EAAI,EAAQ,KAAK,CACnD,EACA,cAAc,EAAS,CAErB,EADcA,EAAO,IAAI,CACb,CAAC,EACb,EAAO,OAAO,CAAO,CACvB,CACF,ECzEM,EAAS,IAAI,QAEnB,SAAS,EAAQ,EAA8B,CAC7C,EAAM,UAAU,EAChB,EAAM,QAAU,IAClB,CAEA,SAAS,EAAO,EAA2B,EAA8B,CACvE,EAAQ,CAAK,EACb,GAAM,CAAE,UAAS,SAAU,EAAM,QAC3B,EAAS,GAAS,EAAQ,MAC1B,EAAO,EAAe,EAAQ,CAAO,EACvC,EAAQ,QAAU,IAAM,EAAQ,MAAQ,GAC5C,EAAM,QAAU,EAAY,EAAS,CACnC,GAAG,EACH,UAAW,EAAa,IAAiB,CACvC,EAAM,YAAc,EACpB,EAAM,QAAQ,gBAAgB,EAAa,CAAY,CACzD,CACF,CAAC,CACH,CAEA,SAAS,EAAK,EAA2B,EAA8B,EAA8B,CACnG,IAAM,EAAO,EAAM,QACb,EAAgB,EAAK,UAAY,EAAK,QACtC,EAAsB,EAAK,QAAU,IAAA,IAAa,EAAK,QAAU,EAAM,aAAe,EAAK,QAAU,EAAQ,MACnH,EAAM,QAAU,GACZ,GAAiB,IAAqB,EAAO,EAAS,CAAK,CACjE,CASA,MAAa,EAA0E,CACrF,QAAQ,EAAS,EAAS,CACxB,EAAQ,aAAa,YAAa,SAAS,EAC3C,IAAM,EAA+B,CAAE,QAAS,EAAQ,MAAO,QAAS,KAAM,YAAa,IAAK,EAChG,EAAO,IAAI,EAAS,CAAK,EACzB,EAAO,EAAS,CAAK,CACvB,EACA,QAAQ,EAAS,EAAS,CAIxB,EAAK,EAAS,EAAO,IAAI,CAAO,EAAI,EAAQ,KAAK,CACnD,EACA,cAAc,EAAS,CAErB,EADc,EAAO,IAAI,CACb,CAAC,EACb,EAAO,OAAO,CAAO,CACvB,CACF"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "mother-mask",
3
3
  "private": false,
4
- "version": "3.42.0",
4
+ "version": "3.44.0",
5
5
  "type": "module",
6
6
  "description": "Lightweight input mask library for browsers",
7
7
  "author": "Danilo Celestino de Castro <dan2dev>",