mother-mask 3.40.0 → 3.41.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
@@ -90,6 +90,85 @@ Binding does not format the initial value or fire an initial callback. Use the
90
90
  Assignments to `input.value` do not dispatch an input event, so format programmatic
91
91
  updates yourself as well.
92
92
 
93
+ ## React
94
+
95
+ Import the React 19.2 components from the separate `mother-mask/react` entry:
96
+
97
+ ```tsx
98
+ import { useState } from 'react'
99
+ import { InputMask, InputDecimal, formatDecimalValue } from 'mother-mask/react'
100
+
101
+ // Keep option objects and mask arrays stable across renders.
102
+ const currency = { decimalPlaces: 2, prefix: '$', allowNegative: true }
103
+
104
+ export function Form() {
105
+ const [phone, setPhone] = useState('')
106
+ const [amount, setAmount] = useState('')
107
+
108
+ return (
109
+ <>
110
+ <label htmlFor="phone">Phone</label>
111
+ <InputMask
112
+ id="phone"
113
+ name="phone"
114
+ mask="(99) 99999-9999"
115
+ inputMode="tel"
116
+ value={phone}
117
+ onValueChange={setPhone}
118
+ />
119
+ <label htmlFor="amount">Amount</label>
120
+ <InputDecimal
121
+ id="amount"
122
+ name="amount"
123
+ options={currency}
124
+ value={amount}
125
+ onValueChange={setAmount}
126
+ />
127
+ <button type="button" onClick={() => setAmount(formatDecimalValue(1234.5, currency))}>
128
+ Set amount
129
+ </button>
130
+ </>
131
+ )
132
+ }
133
+ ```
134
+
135
+ `mother-mask/react` re-exports every core function, class, and type as well as
136
+ `InputMask`, `InputDecimal`, `InputMaskProps`, and `InputDecimalProps`. Its core
137
+ exports are aliases to `mother-mask`, sharing the same implementation and caches.
138
+ The core ESM, CommonJS, and UMD builds remain independent of React. React is an
139
+ optional peer dependency required only when importing `mother-mask/react`;
140
+ the supported version range is `^19.2.0`. React is not bundled.
141
+
142
+ - Both components accept controlled string `value` or uncontrolled string
143
+ `defaultValue`. Update controlled state synchronously in `onValueChange`;
144
+ assigning `''` clears the input. Use `formatDecimalValue` to convert JS numbers
145
+ to decimal strings using the same options as the field.
146
+ - `InputMask` accepts `mask` and optional `BindOptions` (without `onChange`).
147
+ `onValueChange(value)` reports the formatted string.
148
+ - `InputDecimal` accepts optional `BindDecimalOptions` (without `onChange`).
149
+ `onValueChange(value, numericValue)` reports the formatted string and parsed
150
+ JS number; empty input reports `('', 0)`. Decimal strings use the configured
151
+ decimal separator. Preserve the string in state so intermediate edits work.
152
+ - Native input props and `ref` are forwarded. Both use `type="text"`;
153
+ `InputDecimal` defaults to `inputMode="decimal"`. Use `onValueChange` instead
154
+ of React's `onChange`. Initial formatting and parent updates do not fire it.
155
+ - The components dispose listeners and pending frames on replacement, unmount,
156
+ and Activity hide. Controlled echoes preserve the mask's caret and editing
157
+ state. Keep options and mask arrays stable to avoid unnecessary rebinding.
158
+ - IME composition drafts survive unrelated renders. Configuration changes made
159
+ while an Activity is hidden are applied when it becomes visible again.
160
+ Native attributes supplied through React are preserved across rebinding.
161
+ - Native form reset keeps a controlled field's current value. An uncontrolled
162
+ field resets to its original `defaultValue`, formatted with its current
163
+ options. Canceled resets are respected; reset does not fire `onValueChange`.
164
+ This also works for inputs associated with a form through the `form` prop.
165
+ Readonly and disabled fields do not emit change callbacks.
166
+
167
+ The React entry preserves a `use client` directive for React Server Components;
168
+ server-only code can continue importing pure helpers from `mother-mask`.
169
+ See the [React example](https://github.com/dan2dev/mother-mask/tree/main/examples/react-simple)
170
+ for a runnable app and browser lifecycle/memory tests.
171
+
93
172
  ## Decimal Inputs
94
173
 
95
174
  Use `bindDecimal` for numbers, currency fields, and values where the integer part should grow freely.
@@ -208,6 +287,34 @@ The user decides how wide a ranged segment is, using the separator:
208
287
  Reaching `min` alone never inserts anything: after `"3"` the value is `"3"`,
209
288
  because the next keystroke could still be a second digit.
210
289
 
290
+ **Any separator ends the segment, and the mask prints its own.** A ranged
291
+ segment is the one place a mask cannot work out its own boundary, so a person
292
+ saying "this field is done" gets to say it with whichever divider is under
293
+ their thumb — a keypad `.`, a `-`, a space — not only the one the pattern
294
+ happens to spell:
295
+
296
+ ```ts
297
+ bind(date, '9{1,2}/9{1,2}/9{4}')
298
+ // type "3.4.1986" → "3/4/1986"
299
+ // type "3-4-1986" → "3/4/1986"
300
+ // type "3 4 1986" → "3/4/1986"
301
+ ```
302
+
303
+ Any Unicode punctuation, symbol, or space works, and each one behaves exactly
304
+ as the mask's own separator does — same value, same caret. Letters, digits,
305
+ and other scripts do not: a mistyped `"a"` in a date field is a typo, not a
306
+ decision, so it stays the noise it always was. Neither does a character this
307
+ mask's own alphabet accepts — a custom token matching `"."` makes `"."` content
308
+ in that mask, never a boundary.
309
+
310
+ The rule reaches exactly as far as the ambiguity it resolves. A segment only
311
+ reads a separator this way once it is at or past its `min` and still short of
312
+ its `max`; everywhere else the mask owns where its dividers go, and a segment
313
+ that reaches its width reveals the next divider by itself (see
314
+ [Eager Mode](#eager-mode)). So a pattern with no `{min,max}` segment is
315
+ completely unaffected — under `'99/99/9999'`, `"4."` and `"4/"` alike give
316
+ `"4"`, since one digit is short of the day's width either way.
317
+
211
318
  Closing a segment early retires the slots it did not use, so a finished value
212
319
  can be shorter than the pattern's maximum: `"3/4/1986"` is complete at eight
213
320
  characters even though `getMaxLength` reports `10`. Anything typed past that
package/dist/react.cjs ADDED
@@ -0,0 +1,2 @@
1
+ "use client";Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});let e=require("mother-mask"),t=require("react"),n=require("react/jsx-runtime");function r(e,n){let r=(0,t.useRef)(!1),i=(0,t.useRef)(!1);return(0,t.useLayoutEffect)(()=>{let t=e.current,a=!0,o=()=>{r.current=!0},s=()=>{r.current=!1,n()},c=e=>{e.target===t.form&&queueMicrotask(()=>{a&&!e.defaultPrevented&&(r.current=!1,i.current=!0,n())})};return t.addEventListener(`compositionstart`,o,!0),t.addEventListener(`compositionend`,s,!0),t.ownerDocument.addEventListener(`reset`,c,!0),()=>{a=!1,r.current=!1,i.current=!1,t.removeEventListener(`compositionstart`,o,!0),t.removeEventListener(`compositionend`,s,!0),t.ownerDocument.removeEventListener(`reset`,c,!0)}},[e,n]),{composing:r,resetRequested:i}}function i(e,t,n){let r=[[`autocomplete`,`autoComplete`],[`autocorrect`,`autoCorrect`],[`autocapitalize`,`autoCapitalize`],[`spellcheck`,`spellCheck`],[`maxlength`,`maxLength`]].flatMap(([n,r])=>{let i=e.getAttribute(n);return t[r]!=null&&i!==null?[[n,i]]:[]});n();for(let[t,n]of r)e.setAttribute(t,n)}function a({mask:a,options:o,value:s,defaultValue:c=``,onValueChange:l,ref:u,...d}){let f=(0,t.useRef)(null),p=(0,t.useRef)(null),m=(0,t.useRef)({mask:a,options:o}),[,h]=(0,t.useReducer)(e=>e+1,0),g=r(f,h),[_]=(0,t.useState)(()=>({value:(0,e.process)(s??c,a,o),defaultValue:c}));(0,t.useImperativeHandle)(u,()=>f.current,[]);let v=(0,t.useEffectEvent)(e=>{!d.readOnly&&!d.disabled&&l?.(e),s!==void 0&&h()}),y=(0,t.useEffectEvent)(()=>{let e=p.current;e&&i(e.input,d,e.dispose),p.current=null});return(0,t.useLayoutEffect)(()=>()=>{y()},[]),(0,t.useLayoutEffect)(()=>{let t=f.current;if(!t||g.composing.current)return;let n=p.current,r=m.current.mask!==a||m.current.options!==o,i=s!==void 0&&s!==t.value,c=r||i?(0,e.process)(s??t.value,a,o):t.value;(!n||r||c!==t.value||g.resetRequested.current)&&(y(),c!==t.value&&(t.value=c),p.current={input:t,dispose:(0,e.bind)(t,a,{...o,onChange:e=>v(e)})}),m.current={mask:a,options:o},g.resetRequested.current=!1,t.defaultValue=s===void 0?(0,e.process)(_.defaultValue,a,o):t.value}),(0,n.jsx)(`input`,{...d,ref:f,type:`text`,defaultValue:_.value})}function o({options:a,value:o,defaultValue:s=``,onValueChange:c,ref:l,...u}){let d=(0,t.useRef)(null),f=(0,t.useRef)(null),p=(0,t.useRef)(a),[,m]=(0,t.useReducer)(e=>e+1,0),h=r(d,m),[g]=(0,t.useState)(()=>({value:(0,e.processDecimal)(o??s,a),defaultValue:s}));(0,t.useImperativeHandle)(l,()=>d.current,[]);let _=(0,t.useEffectEvent)((e,t)=>{!u.readOnly&&!u.disabled&&c?.(e,t),o!==void 0&&m()}),v=(0,t.useEffectEvent)(()=>{let e=f.current;e&&i(e.input,u,e.dispose),f.current=null});return(0,t.useLayoutEffect)(()=>()=>{v()},[]),(0,t.useLayoutEffect)(()=>{let t=d.current;if(!t||h.composing.current)return;let n=f.current,r=p.current!==a,i=o!==void 0&&o!==t.value,s=r||i?(0,e.processDecimal)(o??t.value,a):t.value;(!n||r||s!==t.value||h.resetRequested.current)&&(v(),s!==t.value&&(t.value=s),f.current={input:t,dispose:(0,e.bindDecimal)(t,{...a,onChange:(e,t)=>_(e,t)})}),p.current=a,h.resetRequested.current=!1,t.defaultValue=o===void 0?(0,e.processDecimal)(g.defaultValue,a):t.value}),(0,n.jsx)(`input`,{inputMode:`decimal`,...u,ref:d,type:`text`,defaultValue:g.value})}exports.InputDecimal=o,exports.InputMask=a,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
+ //# sourceMappingURL=react.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"react.cjs","names":["useRef","useRef","useReducer","useState","process","useEffectEvent","bind","useRef","useReducer","useState","processDecimal","useEffectEvent","bindDecimal"],"sources":["../src/react/input-lifecycle.ts","../src/react/InputMask.tsx","../src/react/InputDecimal.tsx"],"sourcesContent":["import { useLayoutEffect, useRef } from 'react'\nimport type { ComponentPropsWithRef, RefObject } from 'react'\n\n/** Observe native events before the binder and React's delegated handlers. */\nexport function useInputLifecycle(inputRef: RefObject<HTMLInputElement | null>, reconcile: () => void) {\n const composing = useRef(false)\n const resetRequested = useRef(false)\n\n useLayoutEffect(() => {\n const input = inputRef.current!\n let active = true\n const onCompositionStart = () => { composing.current = true }\n const onCompositionEnd = () => {\n composing.current = false\n reconcile()\n }\n const onReset = (event: Event) => {\n if (event.target !== input.form) return\n // reset fires before its default action. Wait for it, and honor a\n // preventDefault from any React/native handler later in propagation.\n queueMicrotask(() => {\n if (!active || event.defaultPrevented) return\n composing.current = false\n resetRequested.current = true\n reconcile()\n })\n }\n input.addEventListener('compositionstart', onCompositionStart, true)\n input.addEventListener('compositionend', onCompositionEnd, true)\n // Capture also covers form=\"id\" and a stopped bubbling reset event.\n input.ownerDocument.addEventListener('reset', onReset, true)\n return () => {\n active = false\n composing.current = false\n resetRequested.current = false\n input.removeEventListener('compositionstart', onCompositionStart, true)\n input.removeEventListener('compositionend', onCompositionEnd, true)\n input.ownerDocument.removeEventListener('reset', onReset, true)\n }\n }, [inputRef, reconcile])\n\n return { composing, resetRequested }\n}\n\n/** A binder may own an attribute that React has since explicitly updated. */\nexport function disposePreservingProps(\n input: HTMLInputElement,\n props: ComponentPropsWithRef<'input'>,\n dispose: () => void,\n) {\n const names = [\n ['autocomplete', 'autoComplete'], ['autocorrect', 'autoCorrect'],\n ['autocapitalize', 'autoCapitalize'], ['spellcheck', 'spellCheck'],\n ['maxlength', 'maxLength'],\n ] as const\n const attributes = names.flatMap(([attribute, prop]) => {\n const value = input.getAttribute(attribute)\n return props[prop] != null && value !== null ? [[attribute, value] as const] : []\n })\n dispose()\n for (const [name, value] of attributes) input.setAttribute(name, value)\n}\n","'use client'\n\nimport { useEffectEvent, useImperativeHandle, useLayoutEffect, useReducer, useRef, useState } from 'react'\nimport type { ComponentPropsWithRef } from 'react'\nimport { bind, process } from 'mother-mask'\nimport type { BindOptions, MaskPattern } from 'mother-mask'\nimport { disposePreservingProps, useInputLifecycle } from './input-lifecycle'\n\nexport type InputMaskProps = Omit<\n ComponentPropsWithRef<'input'>,\n 'value' | 'defaultValue' | 'onChange' | 'type'\n> & {\n mask: MaskPattern\n options?: Omit<BindOptions, 'onChange'>\n value?: string\n defaultValue?: string\n onValueChange?: (value: string) => void\n}\n\nexport function InputMask({\n mask,\n options,\n value,\n defaultValue = '',\n onValueChange,\n ref,\n ...inputProps\n}: InputMaskProps) {\n const inputRef = useRef<HTMLInputElement>(null)\n const bindingRef = useRef<{\n input: HTMLInputElement\n dispose: () => void\n } | null>(null)\n const configurationRef = useRef({ mask, options })\n const [, reconcile] = useReducer((revision: number) => revision + 1, 0)\n const lifecycle = useInputLifecycle(inputRef, reconcile)\n const [initial] = useState(() => ({ value: process(value ?? defaultValue, mask, options), defaultValue }))\n\n useImperativeHandle(ref, () => inputRef.current!, [])\n\n // Read the latest committed props without recreating the binding for a new\n // callback identity. This event is only called by the effect-owned binder.\n const handleChange = useEffectEvent((maskedValue: string) => {\n if (!inputProps.readOnly && !inputProps.disabled) onValueChange?.(maskedValue)\n if (value !== undefined) reconcile()\n })\n\n const releaseBinding = useEffectEvent(() => {\n const binding = bindingRef.current\n if (binding) disposePreservingProps(binding.input, inputProps, binding.dispose)\n bindingRef.current = null\n })\n\n // Register cleanup before acquiring a binding, including StrictMode replay\n // and React Activity hide/show. Disposed bindings must release their refs.\n useLayoutEffect(() => () => {\n releaseBinding()\n }, [])\n\n // Reconcile after every commit, including when a parent rejects an edit.\n useLayoutEffect(() => {\n const input = inputRef.current\n if (!input || lifecycle.composing.current) return\n\n const binding = bindingRef.current\n const maskChanged = configurationRef.current.mask !== mask || configurationRef.current.options !== options\n const valueChanged = value !== undefined && value !== input.value\n const nextValue = maskChanged || valueChanged\n ? process(value ?? input.value, mask, options)\n : input.value\n\n // Leave echoed edits untouched: reformatting can restore a separator the\n // user just deleted, and assigning .value can move the caret to the end.\n if (!binding || maskChanged || nextValue !== input.value || lifecycle.resetRequested.current) {\n releaseBinding()\n if (nextValue !== input.value) input.value = nextValue\n\n // Rebind after external updates so the mask's editing history starts\n // from the new value, rather than the value before the parent update.\n bindingRef.current = {\n input,\n dispose: bind(input, mask, {\n ...options,\n onChange: (maskedValue) => handleChange(maskedValue),\n }),\n }\n }\n configurationRef.current = { mask, options }\n lifecycle.resetRequested.current = false\n // Native reset reads defaultValue synchronously, before its reset event\n // has finished. Controlled fields reset to their current displayed value.\n input.defaultValue = value !== undefined ? input.value : process(initial.defaultValue, mask, options)\n })\n\n // The wrapper controls the DOM through the mask; React must not overwrite\n // the mask's intermediate edits through a native input value prop.\n return <input {...inputProps} ref={inputRef} type=\"text\" defaultValue={initial.value} />\n}\n","'use client'\n\nimport { useEffectEvent, useImperativeHandle, useLayoutEffect, useReducer, useRef, useState } from 'react'\nimport type { ComponentPropsWithRef } from 'react'\nimport { bindDecimal, processDecimal } from 'mother-mask'\nimport type { BindDecimalOptions } from 'mother-mask'\nimport { disposePreservingProps, useInputLifecycle } from './input-lifecycle'\n\nexport type InputDecimalProps = Omit<\n ComponentPropsWithRef<'input'>,\n 'value' | 'defaultValue' | 'onChange' | 'type'\n> & {\n options?: Omit<BindDecimalOptions, 'onChange'>\n value?: string\n defaultValue?: string\n onValueChange?: (value: string, numericValue: number) => void\n}\n\nexport function InputDecimal({\n options,\n value,\n defaultValue = '',\n onValueChange,\n ref,\n ...inputProps\n}: InputDecimalProps) {\n const inputRef = useRef<HTMLInputElement>(null)\n const bindingRef = useRef<{\n input: HTMLInputElement\n dispose: () => void\n } | null>(null)\n const configurationRef = useRef(options)\n const [, reconcile] = useReducer((revision: number) => revision + 1, 0)\n const lifecycle = useInputLifecycle(inputRef, reconcile)\n const [initial] = useState(() => ({ value: processDecimal(value ?? defaultValue, options), defaultValue }))\n\n useImperativeHandle(ref, () => inputRef.current!, [])\n\n // Read the latest committed props without recreating the binding for a new\n // callback identity. This event is only called by the effect-owned binder.\n const handleChange = useEffectEvent((maskedValue: string, numericValue: number) => {\n if (!inputProps.readOnly && !inputProps.disabled) onValueChange?.(maskedValue, numericValue)\n if (value !== undefined) reconcile()\n })\n\n const releaseBinding = useEffectEvent(() => {\n const binding = bindingRef.current\n if (binding) disposePreservingProps(binding.input, inputProps, binding.dispose)\n bindingRef.current = null\n })\n\n // Register cleanup before acquiring a binding, including StrictMode replay\n // and React Activity hide/show. Disposed bindings must release their refs.\n useLayoutEffect(() => () => {\n releaseBinding()\n }, [])\n\n // Reconcile after every commit, including when a parent rejects an edit.\n useLayoutEffect(() => {\n const input = inputRef.current\n if (!input || lifecycle.composing.current) return\n\n const binding = bindingRef.current\n const optionsChanged = configurationRef.current !== options\n const valueChanged = value !== undefined && value !== input.value\n const nextValue = optionsChanged || valueChanged\n ? processDecimal(value ?? input.value, options)\n : input.value\n\n // An echoed edit already has the mask's value and caret. Leave it alone\n // so typing in the fraction or in the middle of the integer stays natural.\n if (!binding || optionsChanged || nextValue !== input.value || lifecycle.resetRequested.current) {\n releaseBinding()\n if (nextValue !== input.value) input.value = nextValue\n\n // External updates start a fresh binding and cancel pending edit frames.\n bindingRef.current = {\n input,\n dispose: bindDecimal(input, {\n ...options,\n onChange: (maskedValue, numericValue) => handleChange(maskedValue, numericValue),\n }),\n }\n }\n configurationRef.current = options\n lifecycle.resetRequested.current = false\n input.defaultValue = value !== undefined ? input.value : processDecimal(initial.defaultValue, options)\n })\n\n // The wrapper synchronizes value through the mask instead of letting React\n // overwrite the native input's intermediate edits.\n return <input inputMode=\"decimal\" {...inputProps} ref={inputRef} type=\"text\" defaultValue={initial.value} />\n}\n"],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ import { BindDecimalOptions, BindOptions, MaskPattern } from "mother-mask";
2
+ import { ComponentPropsWithRef } from "react";
3
+ export * from "mother-mask";
4
+ //#region src/react/InputMask.d.ts
5
+ type InputMaskProps = Omit<ComponentPropsWithRef<'input'>, 'value' | 'defaultValue' | 'onChange' | 'type'> & {
6
+ mask: MaskPattern;
7
+ options?: Omit<BindOptions, 'onChange'>;
8
+ value?: string;
9
+ defaultValue?: string;
10
+ onValueChange?: (value: string) => void;
11
+ };
12
+ declare function InputMask({ mask, options, value, defaultValue, onValueChange, ref, ...inputProps }: InputMaskProps): import("react").JSX.Element;
13
+ //#endregion
14
+ //#region src/react/InputDecimal.d.ts
15
+ type InputDecimalProps = Omit<ComponentPropsWithRef<'input'>, 'value' | 'defaultValue' | 'onChange' | 'type'> & {
16
+ options?: Omit<BindDecimalOptions, 'onChange'>;
17
+ value?: string;
18
+ defaultValue?: string;
19
+ onValueChange?: (value: string, numericValue: number) => void;
20
+ };
21
+ declare function InputDecimal({ options, value, defaultValue, onValueChange, ref, ...inputProps }: InputDecimalProps): import("react").JSX.Element;
22
+ //#endregion
23
+ export { InputDecimal, type InputDecimalProps, InputMask, type InputMaskProps };
24
+ //# sourceMappingURL=react.d.cts.map
@@ -0,0 +1,24 @@
1
+ import { BindDecimalOptions, BindOptions, MaskPattern } from "mother-mask";
2
+ import { ComponentPropsWithRef } from "react";
3
+ export * from "mother-mask";
4
+ //#region src/react/InputMask.d.ts
5
+ type InputMaskProps = Omit<ComponentPropsWithRef<'input'>, 'value' | 'defaultValue' | 'onChange' | 'type'> & {
6
+ mask: MaskPattern;
7
+ options?: Omit<BindOptions, 'onChange'>;
8
+ value?: string;
9
+ defaultValue?: string;
10
+ onValueChange?: (value: string) => void;
11
+ };
12
+ declare function InputMask({ mask, options, value, defaultValue, onValueChange, ref, ...inputProps }: InputMaskProps): import("react").JSX.Element;
13
+ //#endregion
14
+ //#region src/react/InputDecimal.d.ts
15
+ type InputDecimalProps = Omit<ComponentPropsWithRef<'input'>, 'value' | 'defaultValue' | 'onChange' | 'type'> & {
16
+ options?: Omit<BindDecimalOptions, 'onChange'>;
17
+ value?: string;
18
+ defaultValue?: string;
19
+ onValueChange?: (value: string, numericValue: number) => void;
20
+ };
21
+ declare function InputDecimal({ options, value, defaultValue, onValueChange, ref, ...inputProps }: InputDecimalProps): import("react").JSX.Element;
22
+ //#endregion
23
+ export { InputDecimal, type InputDecimalProps, InputMask, type InputMaskProps };
24
+ //# sourceMappingURL=react.d.mts.map
package/dist/react.mjs ADDED
@@ -0,0 +1,2 @@
1
+ "use client";import{bind as e,bindDecimal as t,process as n,processDecimal as r}from"mother-mask";import{useEffectEvent as i,useImperativeHandle as a,useLayoutEffect as o,useReducer as s,useRef as c,useState as l}from"react";import{jsx as u}from"react/jsx-runtime";export*from"mother-mask";function d(e,t){let n=c(!1),r=c(!1);return o(()=>{let i=e.current,a=!0,o=()=>{n.current=!0},s=()=>{n.current=!1,t()},c=e=>{e.target===i.form&&queueMicrotask(()=>{a&&!e.defaultPrevented&&(n.current=!1,r.current=!0,t())})};return i.addEventListener(`compositionstart`,o,!0),i.addEventListener(`compositionend`,s,!0),i.ownerDocument.addEventListener(`reset`,c,!0),()=>{a=!1,n.current=!1,r.current=!1,i.removeEventListener(`compositionstart`,o,!0),i.removeEventListener(`compositionend`,s,!0),i.ownerDocument.removeEventListener(`reset`,c,!0)}},[e,t]),{composing:n,resetRequested:r}}function f(e,t,n){let r=[[`autocomplete`,`autoComplete`],[`autocorrect`,`autoCorrect`],[`autocapitalize`,`autoCapitalize`],[`spellcheck`,`spellCheck`],[`maxlength`,`maxLength`]].flatMap(([n,r])=>{let i=e.getAttribute(n);return t[r]!=null&&i!==null?[[n,i]]:[]});n();for(let[t,n]of r)e.setAttribute(t,n)}function p({mask:t,options:r,value:p,defaultValue:m=``,onValueChange:h,ref:g,..._}){let v=c(null),y=c(null),b=c({mask:t,options:r}),[,x]=s(e=>e+1,0),S=d(v,x),[C]=l(()=>({value:n(p??m,t,r),defaultValue:m}));a(g,()=>v.current,[]);let w=i(e=>{!_.readOnly&&!_.disabled&&h?.(e),p!==void 0&&x()}),T=i(()=>{let e=y.current;e&&f(e.input,_,e.dispose),y.current=null});return o(()=>()=>{T()},[]),o(()=>{let i=v.current;if(!i||S.composing.current)return;let a=y.current,o=b.current.mask!==t||b.current.options!==r,s=p!==void 0&&p!==i.value,c=o||s?n(p??i.value,t,r):i.value;(!a||o||c!==i.value||S.resetRequested.current)&&(T(),c!==i.value&&(i.value=c),y.current={input:i,dispose:e(i,t,{...r,onChange:e=>w(e)})}),b.current={mask:t,options:r},S.resetRequested.current=!1,i.defaultValue=p===void 0?n(C.defaultValue,t,r):i.value}),u(`input`,{..._,ref:v,type:`text`,defaultValue:C.value})}function m({options:e,value:n,defaultValue:p=``,onValueChange:m,ref:h,...g}){let _=c(null),v=c(null),y=c(e),[,b]=s(e=>e+1,0),x=d(_,b),[S]=l(()=>({value:r(n??p,e),defaultValue:p}));a(h,()=>_.current,[]);let C=i((e,t)=>{!g.readOnly&&!g.disabled&&m?.(e,t),n!==void 0&&b()}),w=i(()=>{let e=v.current;e&&f(e.input,g,e.dispose),v.current=null});return o(()=>()=>{w()},[]),o(()=>{let i=_.current;if(!i||x.composing.current)return;let a=v.current,o=y.current!==e,s=n!==void 0&&n!==i.value,c=o||s?r(n??i.value,e):i.value;(!a||o||c!==i.value||x.resetRequested.current)&&(w(),c!==i.value&&(i.value=c),v.current={input:i,dispose:t(i,{...e,onChange:(e,t)=>C(e,t)})}),y.current=e,x.resetRequested.current=!1,i.defaultValue=n===void 0?r(S.defaultValue,e):i.value}),u(`input`,{inputMode:`decimal`,...g,ref:_,type:`text`,defaultValue:S.value})}export{m as InputDecimal,p as InputMask};
2
+ //# sourceMappingURL=react.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"react.mjs","names":[],"sources":["../src/react/input-lifecycle.ts","../src/react/InputMask.tsx","../src/react/InputDecimal.tsx"],"sourcesContent":["import { useLayoutEffect, useRef } from 'react'\nimport type { ComponentPropsWithRef, RefObject } from 'react'\n\n/** Observe native events before the binder and React's delegated handlers. */\nexport function useInputLifecycle(inputRef: RefObject<HTMLInputElement | null>, reconcile: () => void) {\n const composing = useRef(false)\n const resetRequested = useRef(false)\n\n useLayoutEffect(() => {\n const input = inputRef.current!\n let active = true\n const onCompositionStart = () => { composing.current = true }\n const onCompositionEnd = () => {\n composing.current = false\n reconcile()\n }\n const onReset = (event: Event) => {\n if (event.target !== input.form) return\n // reset fires before its default action. Wait for it, and honor a\n // preventDefault from any React/native handler later in propagation.\n queueMicrotask(() => {\n if (!active || event.defaultPrevented) return\n composing.current = false\n resetRequested.current = true\n reconcile()\n })\n }\n input.addEventListener('compositionstart', onCompositionStart, true)\n input.addEventListener('compositionend', onCompositionEnd, true)\n // Capture also covers form=\"id\" and a stopped bubbling reset event.\n input.ownerDocument.addEventListener('reset', onReset, true)\n return () => {\n active = false\n composing.current = false\n resetRequested.current = false\n input.removeEventListener('compositionstart', onCompositionStart, true)\n input.removeEventListener('compositionend', onCompositionEnd, true)\n input.ownerDocument.removeEventListener('reset', onReset, true)\n }\n }, [inputRef, reconcile])\n\n return { composing, resetRequested }\n}\n\n/** A binder may own an attribute that React has since explicitly updated. */\nexport function disposePreservingProps(\n input: HTMLInputElement,\n props: ComponentPropsWithRef<'input'>,\n dispose: () => void,\n) {\n const names = [\n ['autocomplete', 'autoComplete'], ['autocorrect', 'autoCorrect'],\n ['autocapitalize', 'autoCapitalize'], ['spellcheck', 'spellCheck'],\n ['maxlength', 'maxLength'],\n ] as const\n const attributes = names.flatMap(([attribute, prop]) => {\n const value = input.getAttribute(attribute)\n return props[prop] != null && value !== null ? [[attribute, value] as const] : []\n })\n dispose()\n for (const [name, value] of attributes) input.setAttribute(name, value)\n}\n","'use client'\n\nimport { useEffectEvent, useImperativeHandle, useLayoutEffect, useReducer, useRef, useState } from 'react'\nimport type { ComponentPropsWithRef } from 'react'\nimport { bind, process } from 'mother-mask'\nimport type { BindOptions, MaskPattern } from 'mother-mask'\nimport { disposePreservingProps, useInputLifecycle } from './input-lifecycle'\n\nexport type InputMaskProps = Omit<\n ComponentPropsWithRef<'input'>,\n 'value' | 'defaultValue' | 'onChange' | 'type'\n> & {\n mask: MaskPattern\n options?: Omit<BindOptions, 'onChange'>\n value?: string\n defaultValue?: string\n onValueChange?: (value: string) => void\n}\n\nexport function InputMask({\n mask,\n options,\n value,\n defaultValue = '',\n onValueChange,\n ref,\n ...inputProps\n}: InputMaskProps) {\n const inputRef = useRef<HTMLInputElement>(null)\n const bindingRef = useRef<{\n input: HTMLInputElement\n dispose: () => void\n } | null>(null)\n const configurationRef = useRef({ mask, options })\n const [, reconcile] = useReducer((revision: number) => revision + 1, 0)\n const lifecycle = useInputLifecycle(inputRef, reconcile)\n const [initial] = useState(() => ({ value: process(value ?? defaultValue, mask, options), defaultValue }))\n\n useImperativeHandle(ref, () => inputRef.current!, [])\n\n // Read the latest committed props without recreating the binding for a new\n // callback identity. This event is only called by the effect-owned binder.\n const handleChange = useEffectEvent((maskedValue: string) => {\n if (!inputProps.readOnly && !inputProps.disabled) onValueChange?.(maskedValue)\n if (value !== undefined) reconcile()\n })\n\n const releaseBinding = useEffectEvent(() => {\n const binding = bindingRef.current\n if (binding) disposePreservingProps(binding.input, inputProps, binding.dispose)\n bindingRef.current = null\n })\n\n // Register cleanup before acquiring a binding, including StrictMode replay\n // and React Activity hide/show. Disposed bindings must release their refs.\n useLayoutEffect(() => () => {\n releaseBinding()\n }, [])\n\n // Reconcile after every commit, including when a parent rejects an edit.\n useLayoutEffect(() => {\n const input = inputRef.current\n if (!input || lifecycle.composing.current) return\n\n const binding = bindingRef.current\n const maskChanged = configurationRef.current.mask !== mask || configurationRef.current.options !== options\n const valueChanged = value !== undefined && value !== input.value\n const nextValue = maskChanged || valueChanged\n ? process(value ?? input.value, mask, options)\n : input.value\n\n // Leave echoed edits untouched: reformatting can restore a separator the\n // user just deleted, and assigning .value can move the caret to the end.\n if (!binding || maskChanged || nextValue !== input.value || lifecycle.resetRequested.current) {\n releaseBinding()\n if (nextValue !== input.value) input.value = nextValue\n\n // Rebind after external updates so the mask's editing history starts\n // from the new value, rather than the value before the parent update.\n bindingRef.current = {\n input,\n dispose: bind(input, mask, {\n ...options,\n onChange: (maskedValue) => handleChange(maskedValue),\n }),\n }\n }\n configurationRef.current = { mask, options }\n lifecycle.resetRequested.current = false\n // Native reset reads defaultValue synchronously, before its reset event\n // has finished. Controlled fields reset to their current displayed value.\n input.defaultValue = value !== undefined ? input.value : process(initial.defaultValue, mask, options)\n })\n\n // The wrapper controls the DOM through the mask; React must not overwrite\n // the mask's intermediate edits through a native input value prop.\n return <input {...inputProps} ref={inputRef} type=\"text\" defaultValue={initial.value} />\n}\n","'use client'\n\nimport { useEffectEvent, useImperativeHandle, useLayoutEffect, useReducer, useRef, useState } from 'react'\nimport type { ComponentPropsWithRef } from 'react'\nimport { bindDecimal, processDecimal } from 'mother-mask'\nimport type { BindDecimalOptions } from 'mother-mask'\nimport { disposePreservingProps, useInputLifecycle } from './input-lifecycle'\n\nexport type InputDecimalProps = Omit<\n ComponentPropsWithRef<'input'>,\n 'value' | 'defaultValue' | 'onChange' | 'type'\n> & {\n options?: Omit<BindDecimalOptions, 'onChange'>\n value?: string\n defaultValue?: string\n onValueChange?: (value: string, numericValue: number) => void\n}\n\nexport function InputDecimal({\n options,\n value,\n defaultValue = '',\n onValueChange,\n ref,\n ...inputProps\n}: InputDecimalProps) {\n const inputRef = useRef<HTMLInputElement>(null)\n const bindingRef = useRef<{\n input: HTMLInputElement\n dispose: () => void\n } | null>(null)\n const configurationRef = useRef(options)\n const [, reconcile] = useReducer((revision: number) => revision + 1, 0)\n const lifecycle = useInputLifecycle(inputRef, reconcile)\n const [initial] = useState(() => ({ value: processDecimal(value ?? defaultValue, options), defaultValue }))\n\n useImperativeHandle(ref, () => inputRef.current!, [])\n\n // Read the latest committed props without recreating the binding for a new\n // callback identity. This event is only called by the effect-owned binder.\n const handleChange = useEffectEvent((maskedValue: string, numericValue: number) => {\n if (!inputProps.readOnly && !inputProps.disabled) onValueChange?.(maskedValue, numericValue)\n if (value !== undefined) reconcile()\n })\n\n const releaseBinding = useEffectEvent(() => {\n const binding = bindingRef.current\n if (binding) disposePreservingProps(binding.input, inputProps, binding.dispose)\n bindingRef.current = null\n })\n\n // Register cleanup before acquiring a binding, including StrictMode replay\n // and React Activity hide/show. Disposed bindings must release their refs.\n useLayoutEffect(() => () => {\n releaseBinding()\n }, [])\n\n // Reconcile after every commit, including when a parent rejects an edit.\n useLayoutEffect(() => {\n const input = inputRef.current\n if (!input || lifecycle.composing.current) return\n\n const binding = bindingRef.current\n const optionsChanged = configurationRef.current !== options\n const valueChanged = value !== undefined && value !== input.value\n const nextValue = optionsChanged || valueChanged\n ? processDecimal(value ?? input.value, options)\n : input.value\n\n // An echoed edit already has the mask's value and caret. Leave it alone\n // so typing in the fraction or in the middle of the integer stays natural.\n if (!binding || optionsChanged || nextValue !== input.value || lifecycle.resetRequested.current) {\n releaseBinding()\n if (nextValue !== input.value) input.value = nextValue\n\n // External updates start a fresh binding and cancel pending edit frames.\n bindingRef.current = {\n input,\n dispose: bindDecimal(input, {\n ...options,\n onChange: (maskedValue, numericValue) => handleChange(maskedValue, numericValue),\n }),\n }\n }\n configurationRef.current = options\n lifecycle.resetRequested.current = false\n input.defaultValue = value !== undefined ? input.value : processDecimal(initial.defaultValue, options)\n })\n\n // The wrapper synchronizes value through the mask instead of letting React\n // overwrite the native input's intermediate edits.\n return <input inputMode=\"decimal\" {...inputProps} ref={inputRef} type=\"text\" defaultValue={initial.value} />\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "mother-mask",
3
3
  "private": false,
4
- "version": "3.40.0",
4
+ "version": "3.41.0",
5
5
  "type": "module",
6
6
  "description": "Lightweight input mask library for browsers",
7
7
  "author": "Danilo Celestino de Castro <dan2dev>",
@@ -26,11 +26,29 @@
26
26
  "default": "./dist/mother-mask.cjs"
27
27
  }
28
28
  },
29
+ "./react": {
30
+ "import": {
31
+ "types": "./dist/react.d.mts",
32
+ "default": "./dist/react.mjs"
33
+ },
34
+ "require": {
35
+ "types": "./dist/react.d.cts",
36
+ "default": "./dist/react.cjs"
37
+ }
38
+ },
29
39
  "./package.json": "./package.json"
30
40
  },
31
41
  "files": [
32
42
  "dist"
33
43
  ],
44
+ "peerDependencies": {
45
+ "react": "^19.2.0"
46
+ },
47
+ "peerDependenciesMeta": {
48
+ "react": {
49
+ "optional": true
50
+ }
51
+ },
34
52
  "scripts": {
35
53
  "build": "tsdown",
36
54
  "build:watch": "tsdown --watch",
@@ -38,15 +56,20 @@
38
56
  "clean": "rm -rf dist *.tsbuildinfo",
39
57
  "test": "vitest run --coverage",
40
58
  "test:watch": "vitest --watch --coverage",
59
+ "test:package": "bun run build && node --test tests/package.test.mjs",
41
60
  "typecheck": "tsc --noEmit",
42
61
  "release": "bumpp",
43
62
  "prepublishOnly": "bun run build"
44
63
  },
45
64
  "devDependencies": {
46
65
  "@types/node": "^26.4.1",
66
+ "@types/react": "^19.2.0",
67
+ "@types/react-dom": "^19.2.0",
47
68
  "@vitest/coverage-v8": "^4.1.11",
48
69
  "bumpp": "^12.3.0",
49
70
  "jsdom": "^29.1.1",
71
+ "react": "^19.2.0",
72
+ "react-dom": "^19.2.0",
50
73
  "tsdown": "^0.22.14",
51
74
  "typescript": "^7.0.2",
52
75
  "vitest": "^4.1.11"