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 +107 -0
- package/dist/react.cjs +2 -0
- package/dist/react.cjs.map +1 -0
- package/dist/react.d.cts +24 -0
- package/dist/react.d.mts +24 -0
- package/dist/react.mjs +2 -0
- package/dist/react.mjs.map +1 -0
- package/package.json +24 -1
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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"}
|
package/dist/react.d.cts
ADDED
|
@@ -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
|
package/dist/react.d.mts
ADDED
|
@@ -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.
|
|
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"
|