use-mask-input 3.8.0 → 3.9.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/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  ## [3.6.0](https://github.com/eduardoborges/use-mask-input/compare/3.5.2...3.6.0) (2026-01-13)
2
2
 
3
+ ## 3.9.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 99e7a0a: Add TanStack Form integration: `useTanStackFormMask` and `withTanStackFormMask`, with types `TanStackFormInputProps` and `UseTanStackFormMaskReturn`. Documentation and a `tanstack-form-project` demo app were added; examples favor international-friendly masks (phone, email, postal).
8
+
3
9
  ## 3.8.0
4
10
 
5
11
  ### Minor Changes
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  <div align="center">
2
- <h1>use-mask-input</h1>
3
- <p>Input masks for React. Simple, lightweight, and framework-friendly.</p>
2
+ <h1>🥸 use-mask-input</h1>
3
+ <p>Input masks for React. Works with React Hook Form, <strong>TanStack Form</strong>, Ant Design, and plain inputs.</p>
4
4
 
5
5
  [![npm](https://img.shields.io/npm/v/use-mask-input)](https://www.npmjs.com/package/use-mask-input)
6
6
  [![npm downloads](https://img.shields.io/npm/dw/use-mask-input)](https://www.npmjs.com/package/use-mask-input)
@@ -12,7 +12,7 @@
12
12
 
13
13
  ---
14
14
 
15
- **[Documentation](http://use-mask-input.eduardoborges.dev)** · **[API Reference](http://use-mask-input.eduardoborges.dev/api-reference)** · **[Sponsor](https://ko-fi.com/E1E71VQENQ)**
15
+ **[Documentation](http://use-mask-input.eduardoborges.dev)** · **[API Reference](http://use-mask-input.eduardoborges.dev/api-reference)** · **[TanStack Form](http://use-mask-input.eduardoborges.dev/tanstack-form)** · **[Sponsor](https://ko-fi.com/E1E71VQENQ)**
16
16
 
17
17
  ## Install
18
18
 
@@ -44,21 +44,66 @@ function MyForm() {
44
44
  return (
45
45
  <form onSubmit={handleSubmit(console.log)}>
46
46
  <input {...registerWithMask('phone', '(99) 99999-9999')} />
47
- <input {...registerWithMask('cpf', 'cpf')} />
47
+ <input {...registerWithMask('email', 'email')} />
48
48
  <button type="submit">Submit</button>
49
49
  </form>
50
50
  );
51
51
  }
52
52
  ```
53
53
 
54
+ ### With TanStack Form
55
+
56
+ ```tsx
57
+ import { useForm } from '@tanstack/react-form';
58
+ import { useTanStackFormMask } from 'use-mask-input';
59
+
60
+ function MyForm() {
61
+ const maskField = useTanStackFormMask();
62
+ const form = useForm({
63
+ defaultValues: {
64
+ phone: '',
65
+ },
66
+ onSubmit: async ({ value }) => {
67
+ console.log(value);
68
+ },
69
+ });
70
+
71
+ return (
72
+ <form
73
+ onSubmit={(event) => {
74
+ event.preventDefault();
75
+ event.stopPropagation();
76
+ void form.handleSubmit();
77
+ }}
78
+ >
79
+ <form.Field name="phone">
80
+ {(field) => {
81
+ const inputProps = maskField(
82
+ '(99) 99999-9999',
83
+ {
84
+ name: field.name,
85
+ value: field.state.value,
86
+ onBlur: field.handleBlur,
87
+ onChange: (event) => field.handleChange(event.target.value),
88
+ },
89
+ );
90
+
91
+ return <input {...inputProps} placeholder="(00) 00000-0000" />;
92
+ }}
93
+ </form.Field>
94
+ </form>
95
+ );
96
+ }
97
+ ```
98
+
54
99
  ### With Ant Design
55
100
 
56
101
  ```tsx
57
102
  import { Input } from 'antd';
58
103
  import { useMaskInputAntd } from 'use-mask-input/antd';
59
104
 
60
- function CPFInput() {
61
- const ref = useMaskInputAntd({ mask: 'cpf' });
105
+ function EmailInput() {
106
+ const ref = useMaskInputAntd({ mask: 'email' });
62
107
  return <Input ref={ref} />;
63
108
  }
64
109
  ```
@@ -69,8 +114,10 @@ function CPFInput() {
69
114
  |-----|-------------|
70
115
  | `useMaskInput` | Hook. Returns a ref callback. Default choice. |
71
116
  | `useHookFormMask` | Hook. Wraps React Hook Form's `register`. |
117
+ | `useTanStackFormMask` | Hook. Adds mask to TanStack Form field input props. |
72
118
  | `withMask` | Function. Ref callback. Requires `React.memo`. |
73
119
  | `withHookFormMask` | Function. Mask for registered fields. Requires `React.memo`. |
120
+ | `withTanStackFormMask` | Function. Mask for TanStack input props. Requires `React.memo`. |
74
121
  | `useMaskInputAntd` | Hook. `useMaskInput` for Ant Design. |
75
122
  | `useHookFormMaskAntd` | Hook. `useHookFormMask` for Ant Design. |
76
123
 
@@ -80,8 +127,9 @@ function CPFInput() {
80
127
 
81
128
  ## Works With
82
129
 
130
+ - **TanStack Form** (`useTanStackFormMask`, `withTanStackFormMask`). See the [TanStack Form guide](http://use-mask-input.eduardoborges.dev/tanstack-form).
83
131
  - React Hook Form
84
- - Ant Design
132
+ - Ant Design (`use-mask-input/antd`)
85
133
  - React Final Form
86
134
  - Next.js / SSR
87
135
 
package/dist/antd.d.cts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { InputRef } from 'antd';
2
2
  import { RefCallback } from 'react';
3
3
  import { FieldValues, RegisterOptions, UseFormRegister, Path } from 'react-hook-form';
4
- import { U as UseHookFormMaskReturn, M as Mask, O as Options } from './index-S8txl6uK.cjs';
4
+ import { U as UseHookFormMaskReturn, M as Mask, O as Options } from './index-BoaVtWUr.cjs';
5
5
 
6
6
  type UseHookFormMaskAntdReturn<T extends FieldValues> = Omit<UseHookFormMaskReturn<T>, 'ref'> & {
7
7
  ref: RefCallback<InputRef | null>;
package/dist/antd.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { InputRef } from 'antd';
2
2
  import { RefCallback } from 'react';
3
3
  import { FieldValues, RegisterOptions, UseFormRegister, Path } from 'react-hook-form';
4
- import { U as UseHookFormMaskReturn, M as Mask, O as Options } from './index-S8txl6uK.js';
4
+ import { U as UseHookFormMaskReturn, M as Mask, O as Options } from './index-BoaVtWUr.js';
5
5
 
6
6
  type UseHookFormMaskAntdReturn<T extends FieldValues> = Omit<UseHookFormMaskReturn<T>, 'ref'> & {
7
7
  ref: RefCallback<InputRef | null>;
@@ -579,5 +579,14 @@ interface UseHookFormMaskReturn<T extends FieldValues> extends UseFormRegisterRe
579
579
  ref: RefCallback<HTMLElement | null>;
580
580
  prevRef: RefCallback<HTMLElement | null>;
581
581
  }
582
+ interface TanStackFormInputProps {
583
+ name?: string;
584
+ ref?: RefCallback<HTMLElement | null>;
585
+ [key: string]: unknown;
586
+ }
587
+ type UseTanStackFormMaskReturn<T extends TanStackFormInputProps = TanStackFormInputProps> = Omit<T, 'ref'> & {
588
+ ref: RefCallback<HTMLElement | null>;
589
+ prevRef: RefCallback<HTMLElement | null> | undefined;
590
+ };
582
591
 
583
- export type { Input as I, Mask as M, Options as O, UseHookFormMaskReturn as U };
592
+ export type { Input as I, Mask as M, Options as O, TanStackFormInputProps as T, UseHookFormMaskReturn as U, UseTanStackFormMaskReturn as a };
@@ -579,5 +579,14 @@ interface UseHookFormMaskReturn<T extends FieldValues> extends UseFormRegisterRe
579
579
  ref: RefCallback<HTMLElement | null>;
580
580
  prevRef: RefCallback<HTMLElement | null>;
581
581
  }
582
+ interface TanStackFormInputProps {
583
+ name?: string;
584
+ ref?: RefCallback<HTMLElement | null>;
585
+ [key: string]: unknown;
586
+ }
587
+ type UseTanStackFormMaskReturn<T extends TanStackFormInputProps = TanStackFormInputProps> = Omit<T, 'ref'> & {
588
+ ref: RefCallback<HTMLElement | null>;
589
+ prevRef: RefCallback<HTMLElement | null> | undefined;
590
+ };
582
591
 
583
- export type { Input as I, Mask as M, Options as O, UseHookFormMaskReturn as U };
592
+ export type { Input as I, Mask as M, Options as O, TanStackFormInputProps as T, UseHookFormMaskReturn as U, UseTanStackFormMaskReturn as a };
package/dist/index.cjs CHANGED
@@ -74,8 +74,53 @@ function useHookFormMask(registerFn) {
74
74
  }, [registerFn]);
75
75
  }
76
76
 
77
- // src/api/withHookFormMask.ts
77
+ // src/api/withTanStackFormMask.ts
78
78
  var refCache = /* @__PURE__ */ new WeakMap();
79
+ function withTanStackFormMask(inputProps, mask, options) {
80
+ const { ref } = inputProps;
81
+ if (!ref) {
82
+ const result2 = {
83
+ ...inputProps,
84
+ ref: ((input) => {
85
+ if (input) chunkX5SEJVSB_cjs.applyMaskToElement(input, mask, options);
86
+ })
87
+ };
88
+ chunkX5SEJVSB_cjs.setPrevRef(result2, ref);
89
+ return result2;
90
+ }
91
+ if (!refCache.has(ref)) {
92
+ refCache.set(ref, /* @__PURE__ */ new Map());
93
+ }
94
+ const maskCache = refCache.get(ref);
95
+ const cacheKey = chunkX5SEJVSB_cjs.makeMaskCacheKey(inputProps.name ?? "", mask);
96
+ if (!maskCache?.has(cacheKey)) {
97
+ const applyMaskToRef = (_ref) => {
98
+ if (_ref) chunkX5SEJVSB_cjs.applyMaskToElement(_ref, mask, options);
99
+ return _ref;
100
+ };
101
+ maskCache?.set(
102
+ cacheKey,
103
+ chunkX5SEJVSB_cjs.flow(applyMaskToRef, ref)
104
+ );
105
+ }
106
+ const result = {
107
+ ...inputProps,
108
+ ref: maskCache?.get(cacheKey)
109
+ };
110
+ chunkX5SEJVSB_cjs.setPrevRef(result, ref);
111
+ return result;
112
+ }
113
+
114
+ // src/api/useTanStackFormMask.ts
115
+ function useTanStackFormMask() {
116
+ return react.useMemo(
117
+ () => (mask, inputProps, options) => withTanStackFormMask(inputProps, mask, options),
118
+ []
119
+ );
120
+ }
121
+
122
+ // src/api/withHookFormMask.ts
123
+ var refCache2 = /* @__PURE__ */ new WeakMap();
79
124
  function withHookFormMask(register, mask, options) {
80
125
  const { ref } = register;
81
126
  if (!ref) {
@@ -86,10 +131,10 @@ function withHookFormMask(register, mask, options) {
86
131
  chunkX5SEJVSB_cjs.setPrevRef(result2, ref);
87
132
  return result2;
88
133
  }
89
- if (!refCache.has(ref)) {
90
- refCache.set(ref, /* @__PURE__ */ new Map());
134
+ if (!refCache2.has(ref)) {
135
+ refCache2.set(ref, /* @__PURE__ */ new Map());
91
136
  }
92
- const maskCache = refCache.get(ref);
137
+ const maskCache = refCache2.get(ref);
93
138
  const cacheKey = chunkX5SEJVSB_cjs.makeMaskCacheKey(register.name, mask);
94
139
  if (!maskCache?.has(cacheKey)) {
95
140
  const applyMaskToRef = (_ref) => {
@@ -115,6 +160,8 @@ Object.defineProperty(exports, "withMask", {
115
160
  });
116
161
  exports.useHookFormMask = useHookFormMask;
117
162
  exports.useMaskInput = useMaskInput;
163
+ exports.useTanStackFormMask = useTanStackFormMask;
118
164
  exports.withHookFormMask = withHookFormMask;
165
+ exports.withTanStackFormMask = withTanStackFormMask;
119
166
  //# sourceMappingURL=index.cjs.map
120
167
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/api/useMaskInput.ts","../src/api/useHookFormMask.ts","../src/api/withHookFormMask.ts"],"names":["useRef","useCallback","resolveInputRef","withMask","useEffect","isServer_default","useLayoutEffect","useMemo","makeMaskCacheKey","applyMaskToElement","flow","setPrevRef","result"],"mappings":"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","file":"index.cjs","sourcesContent":["import {\n useCallback, useEffect, useRef,\n} from 'react';\n\nimport { resolveInputRef } from '../core';\nimport withMask from './withMask';\nimport isServer from '../utils/isServer';\n\nimport type { Input, Mask, Options } from '../types';\n\ninterface UseMaskInputOptions {\n mask: Mask;\n register?: (element: HTMLElement) => void;\n options?: Options;\n}\n\n/**\n * React hook for applying input masks to form elements.\n * Works with Ant Design and other wrapped components too.\n *\n * @param props - Configuration object\n * @param props.mask - The mask pattern to apply\n * @param props.register - Optional callback that receives the element\n * @param props.options - Optional mask configuration options\n * @returns A ref callback function to attach to the input element\n */\nexport default function useMaskInput(props: UseMaskInputOptions): ((input: Input | null) => void) {\n const { mask, register, options } = props;\n const ref = useRef<HTMLInputElement | null>(null);\n const maskRef = useRef(mask);\n const optionsRef = useRef(options);\n\n const refCallback = useCallback((input: Input | null): void => {\n if (!input) {\n ref.current = null;\n return;\n }\n\n ref.current = resolveInputRef(input);\n withMask(maskRef.current, optionsRef.current)(ref.current);\n }, []);\n\n useEffect(() => {\n if (isServer || !ref.current || !register) return;\n register(ref.current);\n }, [register]);\n\n if (isServer) {\n return (): void => {\n // server doesn't have dom, so just do nothing\n };\n }\n\n return refCallback;\n}\n","import { useLayoutEffect, useMemo, useRef } from 'react';\n\nimport { applyMaskToElement } from '../core';\nimport { flow, makeMaskCacheKey, setPrevRef } from '../utils';\n\nimport type { RefCallback } from 'react';\nimport type {\n FieldValues, Path,\n RegisterOptions,\n UseFormRegister,\n} from 'react-hook-form';\n\nimport type { Mask, Options, UseHookFormMaskReturn } from '../types';\n\ninterface CacheEntry {\n stableRef: RefCallback<HTMLElement | null>;\n element: HTMLElement | null;\n latestRHFRef?: RefCallback<HTMLElement | null>;\n syncedRHFRef?: RefCallback<HTMLElement | null>;\n}\n\n/**\n * Creates a masked version of React Hook Form's register function.\n * Takes react-hook-form's register and adds automatic masking. Like an upgrade.\n *\n * @template T - The form data type\n * @template D - The register options type\n * @param registerFn - The register function from useForm hook\n * @returns A function that registers a field with mask support\n */\nexport default function useHookFormMask<\n T extends FieldValues, D extends RegisterOptions,\n>(registerFn: UseFormRegister<T>): ((fieldName: Path<T>, mask: Mask, options?: (\n D & Options) | Options | D) => UseHookFormMaskReturn<T>) {\n const entryCacheRef = useRef(new Map<string, CacheEntry>());\n\n useLayoutEffect(() => {\n entryCacheRef.current.forEach((entry) => {\n const currentEntry = entry;\n if (!currentEntry.element || !currentEntry.latestRHFRef) return;\n\n // After reset(), RHF gives us a new ref callback. React won't call it\n // because our outward ref identity stays stable, so we replay it here.\n if (currentEntry.latestRHFRef !== currentEntry.syncedRHFRef) {\n currentEntry.latestRHFRef(currentEntry.element);\n currentEntry.syncedRHFRef = currentEntry.latestRHFRef;\n }\n });\n });\n\n return useMemo(() => {\n // registerFn identity changed, so drop cached refs bound to the previous\n // register lifecycle.\n entryCacheRef.current = new Map<string, CacheEntry>();\n\n return (fieldName: Path<T>, mask: Mask, options?: (\n D & Options) | Options | D): UseHookFormMaskReturn<T> => {\n if (!registerFn) throw new Error('registerFn is required');\n\n const registerReturn = registerFn(fieldName, options as Options);\n const { ref } = registerReturn as UseHookFormMaskReturn<T>;\n\n const cacheKey = makeMaskCacheKey(fieldName, mask);\n\n let entry = entryCacheRef.current.get(cacheKey);\n if (!entry) {\n const nextEntry: CacheEntry = {\n element: null,\n latestRHFRef: ref,\n syncedRHFRef: undefined,\n stableRef: null as unknown as RefCallback<HTMLElement | null>,\n };\n\n const applyMaskToRef = (_ref: HTMLElement | null) => {\n nextEntry.element = _ref;\n if (_ref) applyMaskToElement(_ref, mask, options as Options);\n return _ref;\n };\n\n nextEntry.stableRef = (\n nextEntry.latestRHFRef\n ? flow(applyMaskToRef, (_ref: HTMLElement | null) => nextEntry.latestRHFRef?.(_ref))\n : applyMaskToRef\n ) as RefCallback<HTMLElement | null>;\n\n entry = nextEntry;\n entryCacheRef.current.set(cacheKey, nextEntry);\n } else {\n entry.latestRHFRef = ref;\n }\n\n const result = {\n ...registerReturn,\n ref: entry.stableRef,\n } as UseHookFormMaskReturn<T>;\n\n setPrevRef(result, ref);\n\n return result;\n };\n }, [registerFn]);\n}\n","import { applyMaskToElement } from '../core';\nimport { flow, makeMaskCacheKey, setPrevRef } from '../utils';\n\nimport type { RefCallback } from 'react';\nimport type { FieldValues } from 'react-hook-form';\n\nimport type {\n Mask, Options, UseFormRegisterReturn, UseHookFormMaskReturn,\n} from '../types';\n\nconst refCache = new WeakMap<\n RefCallback<HTMLElement | null>,\n Map<string, RefCallback<HTMLElement | null>>\n>();\n\n/**\n * Enhances a React Hook Form register return object with mask support.\n * Takes an already registered field and adds mask to it.\n * Useful when you registered the field before.\n *\n * @param register - The register return object from React Hook Form\n * @param mask - The mask pattern to apply\n * @param options - Optional mask configuration options\n * @returns A new register return object with mask applied\n */\nexport default function withHookFormMask(\n register: UseFormRegisterReturn,\n mask: Mask,\n options?: Options,\n): UseHookFormMaskReturn<FieldValues> {\n const { ref } = register as UseHookFormMaskReturn<FieldValues>;\n\n // null ref — nothing to cache, return as-is.\n if (!ref) {\n const result = {\n ...register,\n ref: null as unknown as RefCallback<HTMLElement | null>,\n } as UseHookFormMaskReturn<FieldValues>;\n setPrevRef(result, ref);\n return result;\n }\n\n if (!refCache.has(ref)) {\n refCache.set(ref, new Map());\n }\n const maskCache = refCache.get(ref);\n const cacheKey = makeMaskCacheKey(register.name, mask);\n\n if (!maskCache?.has(cacheKey)) {\n const applyMaskToRef = (_ref: HTMLElement | null) => {\n if (_ref) applyMaskToElement(_ref, mask, options);\n return _ref;\n };\n maskCache?.set(\n cacheKey,\n flow(applyMaskToRef, ref) as RefCallback<HTMLElement | null>,\n );\n }\n\n const result = {\n ...register,\n ref: maskCache?.get(cacheKey),\n } as UseHookFormMaskReturn<FieldValues>;\n\n setPrevRef(result, ref);\n\n return result;\n}\n"]}
1
+ {"version":3,"sources":["../src/api/useMaskInput.ts","../src/api/useHookFormMask.ts","../src/api/withTanStackFormMask.ts","../src/api/useTanStackFormMask.ts","../src/api/withHookFormMask.ts"],"names":["useRef","useCallback","resolveInputRef","withMask","useEffect","isServer_default","useLayoutEffect","useMemo","makeMaskCacheKey","applyMaskToElement","flow","setPrevRef","result","refCache"],"mappings":"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","file":"index.cjs","sourcesContent":["import {\n useCallback, useEffect, useRef,\n} from 'react';\n\nimport { resolveInputRef } from '../core';\nimport withMask from './withMask';\nimport isServer from '../utils/isServer';\n\nimport type { Input, Mask, Options } from '../types';\n\ninterface UseMaskInputOptions {\n mask: Mask;\n register?: (element: HTMLElement) => void;\n options?: Options;\n}\n\n/**\n * React hook for applying input masks to form elements.\n * Works with Ant Design and other wrapped components too.\n *\n * @param props - Configuration object\n * @param props.mask - The mask pattern to apply\n * @param props.register - Optional callback that receives the element\n * @param props.options - Optional mask configuration options\n * @returns A ref callback function to attach to the input element\n */\nexport default function useMaskInput(props: UseMaskInputOptions): ((input: Input | null) => void) {\n const { mask, register, options } = props;\n const ref = useRef<HTMLInputElement | null>(null);\n const maskRef = useRef(mask);\n const optionsRef = useRef(options);\n\n const refCallback = useCallback((input: Input | null): void => {\n if (!input) {\n ref.current = null;\n return;\n }\n\n ref.current = resolveInputRef(input);\n withMask(maskRef.current, optionsRef.current)(ref.current);\n }, []);\n\n useEffect(() => {\n if (isServer || !ref.current || !register) return;\n register(ref.current);\n }, [register]);\n\n if (isServer) {\n return (): void => {\n // server doesn't have dom, so just do nothing\n };\n }\n\n return refCallback;\n}\n","import { useLayoutEffect, useMemo, useRef } from 'react';\n\nimport { applyMaskToElement } from '../core';\nimport { flow, makeMaskCacheKey, setPrevRef } from '../utils';\n\nimport type { RefCallback } from 'react';\nimport type {\n FieldValues, Path,\n RegisterOptions,\n UseFormRegister,\n} from 'react-hook-form';\n\nimport type { Mask, Options, UseHookFormMaskReturn } from '../types';\n\ninterface CacheEntry {\n stableRef: RefCallback<HTMLElement | null>;\n element: HTMLElement | null;\n latestRHFRef?: RefCallback<HTMLElement | null>;\n syncedRHFRef?: RefCallback<HTMLElement | null>;\n}\n\n/**\n * Creates a masked version of React Hook Form's register function.\n * Takes react-hook-form's register and adds automatic masking. Like an upgrade.\n *\n * @template T - The form data type\n * @template D - The register options type\n * @param registerFn - The register function from useForm hook\n * @returns A function that registers a field with mask support\n */\nexport default function useHookFormMask<\n T extends FieldValues, D extends RegisterOptions,\n>(registerFn: UseFormRegister<T>): ((fieldName: Path<T>, mask: Mask, options?: (\n D & Options) | Options | D) => UseHookFormMaskReturn<T>) {\n const entryCacheRef = useRef(new Map<string, CacheEntry>());\n\n useLayoutEffect(() => {\n entryCacheRef.current.forEach((entry) => {\n const currentEntry = entry;\n if (!currentEntry.element || !currentEntry.latestRHFRef) return;\n\n // After reset(), RHF gives us a new ref callback. React won't call it\n // because our outward ref identity stays stable, so we replay it here.\n if (currentEntry.latestRHFRef !== currentEntry.syncedRHFRef) {\n currentEntry.latestRHFRef(currentEntry.element);\n currentEntry.syncedRHFRef = currentEntry.latestRHFRef;\n }\n });\n });\n\n return useMemo(() => {\n // registerFn identity changed, so drop cached refs bound to the previous\n // register lifecycle.\n entryCacheRef.current = new Map<string, CacheEntry>();\n\n return (fieldName: Path<T>, mask: Mask, options?: (\n D & Options) | Options | D): UseHookFormMaskReturn<T> => {\n if (!registerFn) throw new Error('registerFn is required');\n\n const registerReturn = registerFn(fieldName, options as Options);\n const { ref } = registerReturn as UseHookFormMaskReturn<T>;\n\n const cacheKey = makeMaskCacheKey(fieldName, mask);\n\n let entry = entryCacheRef.current.get(cacheKey);\n if (!entry) {\n const nextEntry: CacheEntry = {\n element: null,\n latestRHFRef: ref,\n syncedRHFRef: undefined,\n stableRef: null as unknown as RefCallback<HTMLElement | null>,\n };\n\n const applyMaskToRef = (_ref: HTMLElement | null) => {\n nextEntry.element = _ref;\n if (_ref) applyMaskToElement(_ref, mask, options as Options);\n return _ref;\n };\n\n nextEntry.stableRef = (\n nextEntry.latestRHFRef\n ? flow(applyMaskToRef, (_ref: HTMLElement | null) => nextEntry.latestRHFRef?.(_ref))\n : applyMaskToRef\n ) as RefCallback<HTMLElement | null>;\n\n entry = nextEntry;\n entryCacheRef.current.set(cacheKey, nextEntry);\n } else {\n entry.latestRHFRef = ref;\n }\n\n const result = {\n ...registerReturn,\n ref: entry.stableRef,\n } as UseHookFormMaskReturn<T>;\n\n setPrevRef(result, ref);\n\n return result;\n };\n }, [registerFn]);\n}\n","import { applyMaskToElement } from '../core';\nimport { flow, makeMaskCacheKey, setPrevRef } from '../utils';\n\nimport type { RefCallback } from 'react';\n\nimport type {\n Mask, Options, TanStackFormInputProps, UseTanStackFormMaskReturn,\n} from '../types';\n\nconst refCache = new WeakMap<\n RefCallback<HTMLElement | null>,\n Map<string, RefCallback<HTMLElement | null>>\n>();\n\n/**\n * Enhances TanStack Form-compatible input props with mask support.\n * Works with objects returned by field.getInputProps().\n */\nexport default function withTanStackFormMask<T extends TanStackFormInputProps>(\n inputProps: T,\n mask: Mask,\n options?: Options,\n): UseTanStackFormMaskReturn<T> {\n const { ref } = inputProps;\n\n if (!ref) {\n const result = {\n ...inputProps,\n ref: ((input: HTMLElement | null) => {\n if (input) applyMaskToElement(input, mask, options);\n }) as RefCallback<HTMLElement | null>,\n } as unknown as UseTanStackFormMaskReturn<T>;\n\n setPrevRef(result, ref);\n return result;\n }\n\n if (!refCache.has(ref)) {\n refCache.set(ref, new Map());\n }\n\n const maskCache = refCache.get(ref);\n const cacheKey = makeMaskCacheKey(inputProps.name ?? '', mask);\n\n if (!maskCache?.has(cacheKey)) {\n const applyMaskToRef = (_ref: HTMLElement | null) => {\n if (_ref) applyMaskToElement(_ref, mask, options);\n return _ref;\n };\n\n maskCache?.set(\n cacheKey,\n flow(applyMaskToRef, ref) as RefCallback<HTMLElement | null>,\n );\n }\n\n const result = {\n ...inputProps,\n ref: maskCache?.get(cacheKey),\n } as unknown as UseTanStackFormMaskReturn<T>;\n\n setPrevRef(result, ref);\n return result;\n}\n","import { useMemo } from 'react';\n\nimport withTanStackFormMask from './withTanStackFormMask';\n\nimport type { Mask, Options, TanStackFormInputProps, UseTanStackFormMaskReturn } from '../types';\n\n/**\n * Creates a helper to mask TanStack Form-compatible input props.\n * Designed for objects returned by field.getInputProps().\n */\nexport default function useTanStackFormMask(): <T extends TanStackFormInputProps>(\n mask: Mask,\n inputProps: T,\n options?: Options,\n) => UseTanStackFormMaskReturn<T> {\n return useMemo(\n () => <T extends TanStackFormInputProps>(\n mask: Mask,\n inputProps: T,\n options?: Options,\n ): UseTanStackFormMaskReturn<T> => withTanStackFormMask(inputProps, mask, options),\n [],\n );\n}\n","import { applyMaskToElement } from '../core';\nimport { flow, makeMaskCacheKey, setPrevRef } from '../utils';\n\nimport type { RefCallback } from 'react';\nimport type { FieldValues } from 'react-hook-form';\n\nimport type {\n Mask, Options, UseFormRegisterReturn, UseHookFormMaskReturn,\n} from '../types';\n\nconst refCache = new WeakMap<\n RefCallback<HTMLElement | null>,\n Map<string, RefCallback<HTMLElement | null>>\n>();\n\n/**\n * Enhances a React Hook Form register return object with mask support.\n * Takes an already registered field and adds mask to it.\n * Useful when you registered the field before.\n *\n * @param register - The register return object from React Hook Form\n * @param mask - The mask pattern to apply\n * @param options - Optional mask configuration options\n * @returns A new register return object with mask applied\n */\nexport default function withHookFormMask(\n register: UseFormRegisterReturn,\n mask: Mask,\n options?: Options,\n): UseHookFormMaskReturn<FieldValues> {\n const { ref } = register as UseHookFormMaskReturn<FieldValues>;\n\n // null ref — nothing to cache, return as-is.\n if (!ref) {\n const result = {\n ...register,\n ref: null as unknown as RefCallback<HTMLElement | null>,\n } as UseHookFormMaskReturn<FieldValues>;\n setPrevRef(result, ref);\n return result;\n }\n\n if (!refCache.has(ref)) {\n refCache.set(ref, new Map());\n }\n const maskCache = refCache.get(ref);\n const cacheKey = makeMaskCacheKey(register.name, mask);\n\n if (!maskCache?.has(cacheKey)) {\n const applyMaskToRef = (_ref: HTMLElement | null) => {\n if (_ref) applyMaskToElement(_ref, mask, options);\n return _ref;\n };\n maskCache?.set(\n cacheKey,\n flow(applyMaskToRef, ref) as RefCallback<HTMLElement | null>,\n );\n }\n\n const result = {\n ...register,\n ref: maskCache?.get(cacheKey),\n } as UseHookFormMaskReturn<FieldValues>;\n\n setPrevRef(result, ref);\n\n return result;\n}\n"]}
package/dist/index.d.cts CHANGED
@@ -1,4 +1,4 @@
1
- import { M as Mask, O as Options, I as Input, U as UseHookFormMaskReturn } from './index-S8txl6uK.cjs';
1
+ import { M as Mask, O as Options, I as Input, U as UseHookFormMaskReturn, T as TanStackFormInputProps, a as UseTanStackFormMaskReturn } from './index-BoaVtWUr.cjs';
2
2
  import { FieldValues, RegisterOptions, UseFormRegister, Path, UseFormRegisterReturn } from 'react-hook-form';
3
3
  export { UseFormRegister, UseFormRegisterReturn } from 'react-hook-form';
4
4
  import 'react';
@@ -31,6 +31,12 @@ declare function useMaskInput(props: UseMaskInputOptions): ((input: Input | null
31
31
  */
32
32
  declare function useHookFormMask<T extends FieldValues, D extends RegisterOptions>(registerFn: UseFormRegister<T>): ((fieldName: Path<T>, mask: Mask, options?: (D & Options) | Options | D) => UseHookFormMaskReturn<T>);
33
33
 
34
+ /**
35
+ * Creates a helper to mask TanStack Form-compatible input props.
36
+ * Designed for objects returned by field.getInputProps().
37
+ */
38
+ declare function useTanStackFormMask(): <T extends TanStackFormInputProps>(mask: Mask, inputProps: T, options?: Options) => UseTanStackFormMaskReturn<T>;
39
+
34
40
  /**
35
41
  * Higher-order function that creates a ref callback for applying input masks.
36
42
  * Simple function to apply mask via ref. No hooks, no drama.
@@ -53,4 +59,10 @@ declare function withMask(mask: Mask, options?: Options): ((input: Input | null)
53
59
  */
54
60
  declare function withHookFormMask(register: UseFormRegisterReturn, mask: Mask, options?: Options): UseHookFormMaskReturn<FieldValues>;
55
61
 
56
- export { Input, Mask, Options, useHookFormMask, useMaskInput, withHookFormMask, withMask };
62
+ /**
63
+ * Enhances TanStack Form-compatible input props with mask support.
64
+ * Works with objects returned by field.getInputProps().
65
+ */
66
+ declare function withTanStackFormMask<T extends TanStackFormInputProps>(inputProps: T, mask: Mask, options?: Options): UseTanStackFormMaskReturn<T>;
67
+
68
+ export { Input, Mask, Options, TanStackFormInputProps, UseTanStackFormMaskReturn, useHookFormMask, useMaskInput, useTanStackFormMask, withHookFormMask, withMask, withTanStackFormMask };
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { M as Mask, O as Options, I as Input, U as UseHookFormMaskReturn } from './index-S8txl6uK.js';
1
+ import { M as Mask, O as Options, I as Input, U as UseHookFormMaskReturn, T as TanStackFormInputProps, a as UseTanStackFormMaskReturn } from './index-BoaVtWUr.js';
2
2
  import { FieldValues, RegisterOptions, UseFormRegister, Path, UseFormRegisterReturn } from 'react-hook-form';
3
3
  export { UseFormRegister, UseFormRegisterReturn } from 'react-hook-form';
4
4
  import 'react';
@@ -31,6 +31,12 @@ declare function useMaskInput(props: UseMaskInputOptions): ((input: Input | null
31
31
  */
32
32
  declare function useHookFormMask<T extends FieldValues, D extends RegisterOptions>(registerFn: UseFormRegister<T>): ((fieldName: Path<T>, mask: Mask, options?: (D & Options) | Options | D) => UseHookFormMaskReturn<T>);
33
33
 
34
+ /**
35
+ * Creates a helper to mask TanStack Form-compatible input props.
36
+ * Designed for objects returned by field.getInputProps().
37
+ */
38
+ declare function useTanStackFormMask(): <T extends TanStackFormInputProps>(mask: Mask, inputProps: T, options?: Options) => UseTanStackFormMaskReturn<T>;
39
+
34
40
  /**
35
41
  * Higher-order function that creates a ref callback for applying input masks.
36
42
  * Simple function to apply mask via ref. No hooks, no drama.
@@ -53,4 +59,10 @@ declare function withMask(mask: Mask, options?: Options): ((input: Input | null)
53
59
  */
54
60
  declare function withHookFormMask(register: UseFormRegisterReturn, mask: Mask, options?: Options): UseHookFormMaskReturn<FieldValues>;
55
61
 
56
- export { Input, Mask, Options, useHookFormMask, useMaskInput, withHookFormMask, withMask };
62
+ /**
63
+ * Enhances TanStack Form-compatible input props with mask support.
64
+ * Works with objects returned by field.getInputProps().
65
+ */
66
+ declare function withTanStackFormMask<T extends TanStackFormInputProps>(inputProps: T, mask: Mask, options?: Options): UseTanStackFormMaskReturn<T>;
67
+
68
+ export { Input, Mask, Options, TanStackFormInputProps, UseTanStackFormMaskReturn, useHookFormMask, useMaskInput, useTanStackFormMask, withHookFormMask, withMask, withTanStackFormMask };
package/dist/index.js CHANGED
@@ -73,8 +73,53 @@ function useHookFormMask(registerFn) {
73
73
  }, [registerFn]);
74
74
  }
75
75
 
76
- // src/api/withHookFormMask.ts
76
+ // src/api/withTanStackFormMask.ts
77
77
  var refCache = /* @__PURE__ */ new WeakMap();
78
+ function withTanStackFormMask(inputProps, mask, options) {
79
+ const { ref } = inputProps;
80
+ if (!ref) {
81
+ const result2 = {
82
+ ...inputProps,
83
+ ref: ((input) => {
84
+ if (input) applyMaskToElement(input, mask, options);
85
+ })
86
+ };
87
+ setPrevRef(result2, ref);
88
+ return result2;
89
+ }
90
+ if (!refCache.has(ref)) {
91
+ refCache.set(ref, /* @__PURE__ */ new Map());
92
+ }
93
+ const maskCache = refCache.get(ref);
94
+ const cacheKey = makeMaskCacheKey(inputProps.name ?? "", mask);
95
+ if (!maskCache?.has(cacheKey)) {
96
+ const applyMaskToRef = (_ref) => {
97
+ if (_ref) applyMaskToElement(_ref, mask, options);
98
+ return _ref;
99
+ };
100
+ maskCache?.set(
101
+ cacheKey,
102
+ flow(applyMaskToRef, ref)
103
+ );
104
+ }
105
+ const result = {
106
+ ...inputProps,
107
+ ref: maskCache?.get(cacheKey)
108
+ };
109
+ setPrevRef(result, ref);
110
+ return result;
111
+ }
112
+
113
+ // src/api/useTanStackFormMask.ts
114
+ function useTanStackFormMask() {
115
+ return useMemo(
116
+ () => (mask, inputProps, options) => withTanStackFormMask(inputProps, mask, options),
117
+ []
118
+ );
119
+ }
120
+
121
+ // src/api/withHookFormMask.ts
122
+ var refCache2 = /* @__PURE__ */ new WeakMap();
78
123
  function withHookFormMask(register, mask, options) {
79
124
  const { ref } = register;
80
125
  if (!ref) {
@@ -85,10 +130,10 @@ function withHookFormMask(register, mask, options) {
85
130
  setPrevRef(result2, ref);
86
131
  return result2;
87
132
  }
88
- if (!refCache.has(ref)) {
89
- refCache.set(ref, /* @__PURE__ */ new Map());
133
+ if (!refCache2.has(ref)) {
134
+ refCache2.set(ref, /* @__PURE__ */ new Map());
90
135
  }
91
- const maskCache = refCache.get(ref);
136
+ const maskCache = refCache2.get(ref);
92
137
  const cacheKey = makeMaskCacheKey(register.name, mask);
93
138
  if (!maskCache?.has(cacheKey)) {
94
139
  const applyMaskToRef = (_ref) => {
@@ -108,6 +153,6 @@ function withHookFormMask(register, mask, options) {
108
153
  return result;
109
154
  }
110
155
 
111
- export { useHookFormMask, useMaskInput, withHookFormMask };
156
+ export { useHookFormMask, useMaskInput, useTanStackFormMask, withHookFormMask, withTanStackFormMask };
112
157
  //# sourceMappingURL=index.js.map
113
158
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/api/useMaskInput.ts","../src/api/useHookFormMask.ts","../src/api/withHookFormMask.ts"],"names":["useRef","result"],"mappings":"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","file":"index.js","sourcesContent":["import {\n useCallback, useEffect, useRef,\n} from 'react';\n\nimport { resolveInputRef } from '../core';\nimport withMask from './withMask';\nimport isServer from '../utils/isServer';\n\nimport type { Input, Mask, Options } from '../types';\n\ninterface UseMaskInputOptions {\n mask: Mask;\n register?: (element: HTMLElement) => void;\n options?: Options;\n}\n\n/**\n * React hook for applying input masks to form elements.\n * Works with Ant Design and other wrapped components too.\n *\n * @param props - Configuration object\n * @param props.mask - The mask pattern to apply\n * @param props.register - Optional callback that receives the element\n * @param props.options - Optional mask configuration options\n * @returns A ref callback function to attach to the input element\n */\nexport default function useMaskInput(props: UseMaskInputOptions): ((input: Input | null) => void) {\n const { mask, register, options } = props;\n const ref = useRef<HTMLInputElement | null>(null);\n const maskRef = useRef(mask);\n const optionsRef = useRef(options);\n\n const refCallback = useCallback((input: Input | null): void => {\n if (!input) {\n ref.current = null;\n return;\n }\n\n ref.current = resolveInputRef(input);\n withMask(maskRef.current, optionsRef.current)(ref.current);\n }, []);\n\n useEffect(() => {\n if (isServer || !ref.current || !register) return;\n register(ref.current);\n }, [register]);\n\n if (isServer) {\n return (): void => {\n // server doesn't have dom, so just do nothing\n };\n }\n\n return refCallback;\n}\n","import { useLayoutEffect, useMemo, useRef } from 'react';\n\nimport { applyMaskToElement } from '../core';\nimport { flow, makeMaskCacheKey, setPrevRef } from '../utils';\n\nimport type { RefCallback } from 'react';\nimport type {\n FieldValues, Path,\n RegisterOptions,\n UseFormRegister,\n} from 'react-hook-form';\n\nimport type { Mask, Options, UseHookFormMaskReturn } from '../types';\n\ninterface CacheEntry {\n stableRef: RefCallback<HTMLElement | null>;\n element: HTMLElement | null;\n latestRHFRef?: RefCallback<HTMLElement | null>;\n syncedRHFRef?: RefCallback<HTMLElement | null>;\n}\n\n/**\n * Creates a masked version of React Hook Form's register function.\n * Takes react-hook-form's register and adds automatic masking. Like an upgrade.\n *\n * @template T - The form data type\n * @template D - The register options type\n * @param registerFn - The register function from useForm hook\n * @returns A function that registers a field with mask support\n */\nexport default function useHookFormMask<\n T extends FieldValues, D extends RegisterOptions,\n>(registerFn: UseFormRegister<T>): ((fieldName: Path<T>, mask: Mask, options?: (\n D & Options) | Options | D) => UseHookFormMaskReturn<T>) {\n const entryCacheRef = useRef(new Map<string, CacheEntry>());\n\n useLayoutEffect(() => {\n entryCacheRef.current.forEach((entry) => {\n const currentEntry = entry;\n if (!currentEntry.element || !currentEntry.latestRHFRef) return;\n\n // After reset(), RHF gives us a new ref callback. React won't call it\n // because our outward ref identity stays stable, so we replay it here.\n if (currentEntry.latestRHFRef !== currentEntry.syncedRHFRef) {\n currentEntry.latestRHFRef(currentEntry.element);\n currentEntry.syncedRHFRef = currentEntry.latestRHFRef;\n }\n });\n });\n\n return useMemo(() => {\n // registerFn identity changed, so drop cached refs bound to the previous\n // register lifecycle.\n entryCacheRef.current = new Map<string, CacheEntry>();\n\n return (fieldName: Path<T>, mask: Mask, options?: (\n D & Options) | Options | D): UseHookFormMaskReturn<T> => {\n if (!registerFn) throw new Error('registerFn is required');\n\n const registerReturn = registerFn(fieldName, options as Options);\n const { ref } = registerReturn as UseHookFormMaskReturn<T>;\n\n const cacheKey = makeMaskCacheKey(fieldName, mask);\n\n let entry = entryCacheRef.current.get(cacheKey);\n if (!entry) {\n const nextEntry: CacheEntry = {\n element: null,\n latestRHFRef: ref,\n syncedRHFRef: undefined,\n stableRef: null as unknown as RefCallback<HTMLElement | null>,\n };\n\n const applyMaskToRef = (_ref: HTMLElement | null) => {\n nextEntry.element = _ref;\n if (_ref) applyMaskToElement(_ref, mask, options as Options);\n return _ref;\n };\n\n nextEntry.stableRef = (\n nextEntry.latestRHFRef\n ? flow(applyMaskToRef, (_ref: HTMLElement | null) => nextEntry.latestRHFRef?.(_ref))\n : applyMaskToRef\n ) as RefCallback<HTMLElement | null>;\n\n entry = nextEntry;\n entryCacheRef.current.set(cacheKey, nextEntry);\n } else {\n entry.latestRHFRef = ref;\n }\n\n const result = {\n ...registerReturn,\n ref: entry.stableRef,\n } as UseHookFormMaskReturn<T>;\n\n setPrevRef(result, ref);\n\n return result;\n };\n }, [registerFn]);\n}\n","import { applyMaskToElement } from '../core';\nimport { flow, makeMaskCacheKey, setPrevRef } from '../utils';\n\nimport type { RefCallback } from 'react';\nimport type { FieldValues } from 'react-hook-form';\n\nimport type {\n Mask, Options, UseFormRegisterReturn, UseHookFormMaskReturn,\n} from '../types';\n\nconst refCache = new WeakMap<\n RefCallback<HTMLElement | null>,\n Map<string, RefCallback<HTMLElement | null>>\n>();\n\n/**\n * Enhances a React Hook Form register return object with mask support.\n * Takes an already registered field and adds mask to it.\n * Useful when you registered the field before.\n *\n * @param register - The register return object from React Hook Form\n * @param mask - The mask pattern to apply\n * @param options - Optional mask configuration options\n * @returns A new register return object with mask applied\n */\nexport default function withHookFormMask(\n register: UseFormRegisterReturn,\n mask: Mask,\n options?: Options,\n): UseHookFormMaskReturn<FieldValues> {\n const { ref } = register as UseHookFormMaskReturn<FieldValues>;\n\n // null ref — nothing to cache, return as-is.\n if (!ref) {\n const result = {\n ...register,\n ref: null as unknown as RefCallback<HTMLElement | null>,\n } as UseHookFormMaskReturn<FieldValues>;\n setPrevRef(result, ref);\n return result;\n }\n\n if (!refCache.has(ref)) {\n refCache.set(ref, new Map());\n }\n const maskCache = refCache.get(ref);\n const cacheKey = makeMaskCacheKey(register.name, mask);\n\n if (!maskCache?.has(cacheKey)) {\n const applyMaskToRef = (_ref: HTMLElement | null) => {\n if (_ref) applyMaskToElement(_ref, mask, options);\n return _ref;\n };\n maskCache?.set(\n cacheKey,\n flow(applyMaskToRef, ref) as RefCallback<HTMLElement | null>,\n );\n }\n\n const result = {\n ...register,\n ref: maskCache?.get(cacheKey),\n } as UseHookFormMaskReturn<FieldValues>;\n\n setPrevRef(result, ref);\n\n return result;\n}\n"]}
1
+ {"version":3,"sources":["../src/api/useMaskInput.ts","../src/api/useHookFormMask.ts","../src/api/withTanStackFormMask.ts","../src/api/useTanStackFormMask.ts","../src/api/withHookFormMask.ts"],"names":["useRef","result","useMemo","refCache"],"mappings":"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","file":"index.js","sourcesContent":["import {\n useCallback, useEffect, useRef,\n} from 'react';\n\nimport { resolveInputRef } from '../core';\nimport withMask from './withMask';\nimport isServer from '../utils/isServer';\n\nimport type { Input, Mask, Options } from '../types';\n\ninterface UseMaskInputOptions {\n mask: Mask;\n register?: (element: HTMLElement) => void;\n options?: Options;\n}\n\n/**\n * React hook for applying input masks to form elements.\n * Works with Ant Design and other wrapped components too.\n *\n * @param props - Configuration object\n * @param props.mask - The mask pattern to apply\n * @param props.register - Optional callback that receives the element\n * @param props.options - Optional mask configuration options\n * @returns A ref callback function to attach to the input element\n */\nexport default function useMaskInput(props: UseMaskInputOptions): ((input: Input | null) => void) {\n const { mask, register, options } = props;\n const ref = useRef<HTMLInputElement | null>(null);\n const maskRef = useRef(mask);\n const optionsRef = useRef(options);\n\n const refCallback = useCallback((input: Input | null): void => {\n if (!input) {\n ref.current = null;\n return;\n }\n\n ref.current = resolveInputRef(input);\n withMask(maskRef.current, optionsRef.current)(ref.current);\n }, []);\n\n useEffect(() => {\n if (isServer || !ref.current || !register) return;\n register(ref.current);\n }, [register]);\n\n if (isServer) {\n return (): void => {\n // server doesn't have dom, so just do nothing\n };\n }\n\n return refCallback;\n}\n","import { useLayoutEffect, useMemo, useRef } from 'react';\n\nimport { applyMaskToElement } from '../core';\nimport { flow, makeMaskCacheKey, setPrevRef } from '../utils';\n\nimport type { RefCallback } from 'react';\nimport type {\n FieldValues, Path,\n RegisterOptions,\n UseFormRegister,\n} from 'react-hook-form';\n\nimport type { Mask, Options, UseHookFormMaskReturn } from '../types';\n\ninterface CacheEntry {\n stableRef: RefCallback<HTMLElement | null>;\n element: HTMLElement | null;\n latestRHFRef?: RefCallback<HTMLElement | null>;\n syncedRHFRef?: RefCallback<HTMLElement | null>;\n}\n\n/**\n * Creates a masked version of React Hook Form's register function.\n * Takes react-hook-form's register and adds automatic masking. Like an upgrade.\n *\n * @template T - The form data type\n * @template D - The register options type\n * @param registerFn - The register function from useForm hook\n * @returns A function that registers a field with mask support\n */\nexport default function useHookFormMask<\n T extends FieldValues, D extends RegisterOptions,\n>(registerFn: UseFormRegister<T>): ((fieldName: Path<T>, mask: Mask, options?: (\n D & Options) | Options | D) => UseHookFormMaskReturn<T>) {\n const entryCacheRef = useRef(new Map<string, CacheEntry>());\n\n useLayoutEffect(() => {\n entryCacheRef.current.forEach((entry) => {\n const currentEntry = entry;\n if (!currentEntry.element || !currentEntry.latestRHFRef) return;\n\n // After reset(), RHF gives us a new ref callback. React won't call it\n // because our outward ref identity stays stable, so we replay it here.\n if (currentEntry.latestRHFRef !== currentEntry.syncedRHFRef) {\n currentEntry.latestRHFRef(currentEntry.element);\n currentEntry.syncedRHFRef = currentEntry.latestRHFRef;\n }\n });\n });\n\n return useMemo(() => {\n // registerFn identity changed, so drop cached refs bound to the previous\n // register lifecycle.\n entryCacheRef.current = new Map<string, CacheEntry>();\n\n return (fieldName: Path<T>, mask: Mask, options?: (\n D & Options) | Options | D): UseHookFormMaskReturn<T> => {\n if (!registerFn) throw new Error('registerFn is required');\n\n const registerReturn = registerFn(fieldName, options as Options);\n const { ref } = registerReturn as UseHookFormMaskReturn<T>;\n\n const cacheKey = makeMaskCacheKey(fieldName, mask);\n\n let entry = entryCacheRef.current.get(cacheKey);\n if (!entry) {\n const nextEntry: CacheEntry = {\n element: null,\n latestRHFRef: ref,\n syncedRHFRef: undefined,\n stableRef: null as unknown as RefCallback<HTMLElement | null>,\n };\n\n const applyMaskToRef = (_ref: HTMLElement | null) => {\n nextEntry.element = _ref;\n if (_ref) applyMaskToElement(_ref, mask, options as Options);\n return _ref;\n };\n\n nextEntry.stableRef = (\n nextEntry.latestRHFRef\n ? flow(applyMaskToRef, (_ref: HTMLElement | null) => nextEntry.latestRHFRef?.(_ref))\n : applyMaskToRef\n ) as RefCallback<HTMLElement | null>;\n\n entry = nextEntry;\n entryCacheRef.current.set(cacheKey, nextEntry);\n } else {\n entry.latestRHFRef = ref;\n }\n\n const result = {\n ...registerReturn,\n ref: entry.stableRef,\n } as UseHookFormMaskReturn<T>;\n\n setPrevRef(result, ref);\n\n return result;\n };\n }, [registerFn]);\n}\n","import { applyMaskToElement } from '../core';\nimport { flow, makeMaskCacheKey, setPrevRef } from '../utils';\n\nimport type { RefCallback } from 'react';\n\nimport type {\n Mask, Options, TanStackFormInputProps, UseTanStackFormMaskReturn,\n} from '../types';\n\nconst refCache = new WeakMap<\n RefCallback<HTMLElement | null>,\n Map<string, RefCallback<HTMLElement | null>>\n>();\n\n/**\n * Enhances TanStack Form-compatible input props with mask support.\n * Works with objects returned by field.getInputProps().\n */\nexport default function withTanStackFormMask<T extends TanStackFormInputProps>(\n inputProps: T,\n mask: Mask,\n options?: Options,\n): UseTanStackFormMaskReturn<T> {\n const { ref } = inputProps;\n\n if (!ref) {\n const result = {\n ...inputProps,\n ref: ((input: HTMLElement | null) => {\n if (input) applyMaskToElement(input, mask, options);\n }) as RefCallback<HTMLElement | null>,\n } as unknown as UseTanStackFormMaskReturn<T>;\n\n setPrevRef(result, ref);\n return result;\n }\n\n if (!refCache.has(ref)) {\n refCache.set(ref, new Map());\n }\n\n const maskCache = refCache.get(ref);\n const cacheKey = makeMaskCacheKey(inputProps.name ?? '', mask);\n\n if (!maskCache?.has(cacheKey)) {\n const applyMaskToRef = (_ref: HTMLElement | null) => {\n if (_ref) applyMaskToElement(_ref, mask, options);\n return _ref;\n };\n\n maskCache?.set(\n cacheKey,\n flow(applyMaskToRef, ref) as RefCallback<HTMLElement | null>,\n );\n }\n\n const result = {\n ...inputProps,\n ref: maskCache?.get(cacheKey),\n } as unknown as UseTanStackFormMaskReturn<T>;\n\n setPrevRef(result, ref);\n return result;\n}\n","import { useMemo } from 'react';\n\nimport withTanStackFormMask from './withTanStackFormMask';\n\nimport type { Mask, Options, TanStackFormInputProps, UseTanStackFormMaskReturn } from '../types';\n\n/**\n * Creates a helper to mask TanStack Form-compatible input props.\n * Designed for objects returned by field.getInputProps().\n */\nexport default function useTanStackFormMask(): <T extends TanStackFormInputProps>(\n mask: Mask,\n inputProps: T,\n options?: Options,\n) => UseTanStackFormMaskReturn<T> {\n return useMemo(\n () => <T extends TanStackFormInputProps>(\n mask: Mask,\n inputProps: T,\n options?: Options,\n ): UseTanStackFormMaskReturn<T> => withTanStackFormMask(inputProps, mask, options),\n [],\n );\n}\n","import { applyMaskToElement } from '../core';\nimport { flow, makeMaskCacheKey, setPrevRef } from '../utils';\n\nimport type { RefCallback } from 'react';\nimport type { FieldValues } from 'react-hook-form';\n\nimport type {\n Mask, Options, UseFormRegisterReturn, UseHookFormMaskReturn,\n} from '../types';\n\nconst refCache = new WeakMap<\n RefCallback<HTMLElement | null>,\n Map<string, RefCallback<HTMLElement | null>>\n>();\n\n/**\n * Enhances a React Hook Form register return object with mask support.\n * Takes an already registered field and adds mask to it.\n * Useful when you registered the field before.\n *\n * @param register - The register return object from React Hook Form\n * @param mask - The mask pattern to apply\n * @param options - Optional mask configuration options\n * @returns A new register return object with mask applied\n */\nexport default function withHookFormMask(\n register: UseFormRegisterReturn,\n mask: Mask,\n options?: Options,\n): UseHookFormMaskReturn<FieldValues> {\n const { ref } = register as UseHookFormMaskReturn<FieldValues>;\n\n // null ref — nothing to cache, return as-is.\n if (!ref) {\n const result = {\n ...register,\n ref: null as unknown as RefCallback<HTMLElement | null>,\n } as UseHookFormMaskReturn<FieldValues>;\n setPrevRef(result, ref);\n return result;\n }\n\n if (!refCache.has(ref)) {\n refCache.set(ref, new Map());\n }\n const maskCache = refCache.get(ref);\n const cacheKey = makeMaskCacheKey(register.name, mask);\n\n if (!maskCache?.has(cacheKey)) {\n const applyMaskToRef = (_ref: HTMLElement | null) => {\n if (_ref) applyMaskToElement(_ref, mask, options);\n return _ref;\n };\n maskCache?.set(\n cacheKey,\n flow(applyMaskToRef, ref) as RefCallback<HTMLElement | null>,\n );\n }\n\n const result = {\n ...register,\n ref: maskCache?.get(cacheKey),\n } as UseHookFormMaskReturn<FieldValues>;\n\n setPrevRef(result, ref);\n\n return result;\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "use-mask-input",
3
- "version": "3.8.0",
3
+ "version": "3.9.0",
4
4
  "private": false,
5
5
  "description": "A react Hook for build elegant input masks. Compatible with React Hook Form",
6
6
  "author": "Eduardo Borges<euduardoborges@gmail.com>",
@@ -42,18 +42,18 @@
42
42
  "@testing-library/dom": "^10.4.1",
43
43
  "@testing-library/react": "^16.3.2",
44
44
  "@types/inputmask": "5.0.7",
45
- "@types/node": "25",
45
+ "@types/node": "^25.5.0",
46
46
  "@types/react": ">=19",
47
47
  "@types/react-dom": ">=19",
48
- "@vitest/coverage-v8": "4.0.18",
49
- "antd": "^6.3.2",
50
- "oxlint": "1.53.0",
48
+ "@vitest/coverage-v8": "4.1.1",
49
+ "antd": "^6.3.4",
50
+ "oxlint": "1.57.0",
51
51
  "inputmask": "5.0.10-beta.61",
52
52
  "jsdom": "^28.1.0",
53
- "react-hook-form": "7.71.2",
53
+ "react-hook-form": "7.72.0",
54
54
  "tsup": "8.5.1",
55
55
  "typescript": "5.9",
56
- "vitest": "4.0.18"
56
+ "vitest": "4.1.1"
57
57
  },
58
58
  "scripts": {
59
59
  "build": "tsup",
package/src/api/index.ts CHANGED
@@ -1,4 +1,6 @@
1
1
  export { default as useMaskInput } from './useMaskInput';
2
2
  export { default as useHookFormMask } from './useHookFormMask';
3
+ export { default as useTanStackFormMask } from './useTanStackFormMask';
3
4
  export { default as withMask } from './withMask';
4
5
  export { default as withHookFormMask } from './withHookFormMask';
6
+ export { default as withTanStackFormMask } from './withTanStackFormMask';
@@ -0,0 +1,24 @@
1
+ import { useMemo } from 'react';
2
+
3
+ import withTanStackFormMask from './withTanStackFormMask';
4
+
5
+ import type { Mask, Options, TanStackFormInputProps, UseTanStackFormMaskReturn } from '../types';
6
+
7
+ /**
8
+ * Creates a helper to mask TanStack Form-compatible input props.
9
+ * Designed for objects returned by field.getInputProps().
10
+ */
11
+ export default function useTanStackFormMask(): <T extends TanStackFormInputProps>(
12
+ mask: Mask,
13
+ inputProps: T,
14
+ options?: Options,
15
+ ) => UseTanStackFormMaskReturn<T> {
16
+ return useMemo(
17
+ () => <T extends TanStackFormInputProps>(
18
+ mask: Mask,
19
+ inputProps: T,
20
+ options?: Options,
21
+ ): UseTanStackFormMaskReturn<T> => withTanStackFormMask(inputProps, mask, options),
22
+ [],
23
+ );
24
+ }
@@ -0,0 +1,76 @@
1
+ import inputmask from 'inputmask';
2
+ import {
3
+ beforeEach,
4
+ describe, expect, it, vi,
5
+ } from 'vitest';
6
+
7
+ import withTanStackFormMask from './withTanStackFormMask';
8
+
9
+ import type { TanStackFormInputProps } from '../types';
10
+
11
+ vi.mock('inputmask', () => ({
12
+ default: vi.fn((options) => ({
13
+ mask: vi.fn(),
14
+ options,
15
+ })),
16
+ }));
17
+
18
+ describe('withTanStackFormMask', () => {
19
+ beforeEach(() => {
20
+ vi.clearAllMocks();
21
+ });
22
+
23
+ it('returns masked input props with stable structure', () => {
24
+ const inputProps: TanStackFormInputProps = {
25
+ name: 'cpf',
26
+ ref: vi.fn(),
27
+ onBlur: vi.fn(),
28
+ onChange: vi.fn(),
29
+ value: '',
30
+ };
31
+
32
+ const result = withTanStackFormMask(inputProps, 'cpf');
33
+
34
+ expect(typeof result.ref).toBe('function');
35
+ expect(result.onBlur).toBe(inputProps.onBlur);
36
+ expect(result.onChange).toBe(inputProps.onChange);
37
+ expect(result.name).toBe('cpf');
38
+ });
39
+
40
+ it('applies mask when ref callback receives an input', () => {
41
+ const maskFn = vi.fn();
42
+ vi.mocked(inputmask).mockReturnValue({ mask: maskFn } as any);
43
+
44
+ const input = document.createElement('input');
45
+ const originalRef = vi.fn();
46
+ const inputProps: TanStackFormInputProps = {
47
+ name: 'phone',
48
+ ref: originalRef,
49
+ onBlur: vi.fn(),
50
+ onChange: vi.fn(),
51
+ value: '',
52
+ };
53
+
54
+ const result = withTanStackFormMask(inputProps, '(99) 99999-9999');
55
+ result.ref(input);
56
+
57
+ expect(maskFn).toHaveBeenCalled();
58
+ expect(originalRef).toHaveBeenCalledWith(input);
59
+ });
60
+
61
+ it('keeps ref cache stable for same ref and mask', () => {
62
+ const originalRef = vi.fn();
63
+ const inputProps: TanStackFormInputProps = {
64
+ name: 'cpf',
65
+ ref: originalRef,
66
+ onBlur: vi.fn(),
67
+ onChange: vi.fn(),
68
+ value: '',
69
+ };
70
+
71
+ const first = withTanStackFormMask(inputProps, 'cpf');
72
+ const second = withTanStackFormMask(inputProps, 'cpf');
73
+
74
+ expect(first.ref).toBe(second.ref);
75
+ });
76
+ });
@@ -0,0 +1,64 @@
1
+ import { applyMaskToElement } from '../core';
2
+ import { flow, makeMaskCacheKey, setPrevRef } from '../utils';
3
+
4
+ import type { RefCallback } from 'react';
5
+
6
+ import type {
7
+ Mask, Options, TanStackFormInputProps, UseTanStackFormMaskReturn,
8
+ } from '../types';
9
+
10
+ const refCache = new WeakMap<
11
+ RefCallback<HTMLElement | null>,
12
+ Map<string, RefCallback<HTMLElement | null>>
13
+ >();
14
+
15
+ /**
16
+ * Enhances TanStack Form-compatible input props with mask support.
17
+ * Works with objects returned by field.getInputProps().
18
+ */
19
+ export default function withTanStackFormMask<T extends TanStackFormInputProps>(
20
+ inputProps: T,
21
+ mask: Mask,
22
+ options?: Options,
23
+ ): UseTanStackFormMaskReturn<T> {
24
+ const { ref } = inputProps;
25
+
26
+ if (!ref) {
27
+ const result = {
28
+ ...inputProps,
29
+ ref: ((input: HTMLElement | null) => {
30
+ if (input) applyMaskToElement(input, mask, options);
31
+ }) as RefCallback<HTMLElement | null>,
32
+ } as unknown as UseTanStackFormMaskReturn<T>;
33
+
34
+ setPrevRef(result, ref);
35
+ return result;
36
+ }
37
+
38
+ if (!refCache.has(ref)) {
39
+ refCache.set(ref, new Map());
40
+ }
41
+
42
+ const maskCache = refCache.get(ref);
43
+ const cacheKey = makeMaskCacheKey(inputProps.name ?? '', mask);
44
+
45
+ if (!maskCache?.has(cacheKey)) {
46
+ const applyMaskToRef = (_ref: HTMLElement | null) => {
47
+ if (_ref) applyMaskToElement(_ref, mask, options);
48
+ return _ref;
49
+ };
50
+
51
+ maskCache?.set(
52
+ cacheKey,
53
+ flow(applyMaskToRef, ref) as RefCallback<HTMLElement | null>,
54
+ );
55
+ }
56
+
57
+ const result = {
58
+ ...inputProps,
59
+ ref: maskCache?.get(cacheKey),
60
+ } as unknown as UseTanStackFormMaskReturn<T>;
61
+
62
+ setPrevRef(result, ref);
63
+ return result;
64
+ }
@@ -5,6 +5,12 @@ import {
5
5
 
6
6
  import { applyMaskToElement, createMaskInstance } from './maskEngine';
7
7
 
8
+ type MaskInstance = ReturnType<typeof createMaskInstance>;
9
+
10
+ function stubMaskInstance(maskFn: ReturnType<typeof vi.fn>): MaskInstance {
11
+ return { mask: maskFn } as unknown as MaskInstance;
12
+ }
13
+
8
14
  vi.mock('inputmask', () => ({
9
15
  default: vi.fn((options) => ({
10
16
  mask: vi.fn(),
@@ -47,7 +53,7 @@ describe('maskEngine', () => {
47
53
  it('applies mask to input element', () => {
48
54
  const input = document.createElement('input');
49
55
  const maskFn = vi.fn();
50
- vi.mocked(inputmask).mockReturnValue({ mask: maskFn } as unknown as Inputmask.Instance);
56
+ vi.mocked(inputmask).mockImplementation(() => stubMaskInstance(maskFn));
51
57
 
52
58
  applyMaskToElement(input, '999-999');
53
59
 
@@ -57,7 +63,7 @@ describe('maskEngine', () => {
57
63
  it('applies mask to textarea element', () => {
58
64
  const textarea = document.createElement('textarea');
59
65
  const maskFn = vi.fn();
60
- vi.mocked(inputmask).mockReturnValue({ mask: maskFn } as unknown as Inputmask.Instance);
66
+ vi.mocked(inputmask).mockImplementation(() => stubMaskInstance(maskFn));
61
67
 
62
68
  applyMaskToElement(textarea, '999-999');
63
69
 
@@ -69,7 +75,7 @@ describe('maskEngine', () => {
69
75
  const input = document.createElement('input');
70
76
  wrapper.appendChild(input);
71
77
  const maskFn = vi.fn();
72
- vi.mocked(inputmask).mockReturnValue({ mask: maskFn } as unknown as Inputmask.Instance);
78
+ vi.mocked(inputmask).mockImplementation(() => stubMaskInstance(maskFn));
73
79
 
74
80
  applyMaskToElement(wrapper, '999-999');
75
81
 
@@ -79,7 +85,7 @@ describe('maskEngine', () => {
79
85
  it('applies mask to wrapper if no input found inside', () => {
80
86
  const wrapper = document.createElement('div');
81
87
  const maskFn = vi.fn();
82
- vi.mocked(inputmask).mockReturnValue({ mask: maskFn } as unknown as Inputmask.Instance);
88
+ vi.mocked(inputmask).mockImplementation(() => stubMaskInstance(maskFn));
83
89
 
84
90
  applyMaskToElement(wrapper, '999-999');
85
91
 
@@ -88,7 +94,7 @@ describe('maskEngine', () => {
88
94
 
89
95
  it('does nothing if element is null', () => {
90
96
  const maskFn = vi.fn();
91
- vi.mocked(inputmask).mockReturnValue({ mask: maskFn } as unknown as Inputmask.Instance);
97
+ vi.mocked(inputmask).mockImplementation(() => stubMaskInstance(maskFn));
92
98
 
93
99
  applyMaskToElement(null as unknown as HTMLElement, '999-999');
94
100
 
@@ -98,7 +104,7 @@ describe('maskEngine', () => {
98
104
  it('applies mask with custom options', () => {
99
105
  const input = document.createElement('input');
100
106
  const maskFn = vi.fn();
101
- vi.mocked(inputmask).mockReturnValue({ mask: maskFn } as unknown as Inputmask.Instance);
107
+ vi.mocked(inputmask).mockImplementation(() => stubMaskInstance(maskFn));
102
108
 
103
109
  applyMaskToElement(input, '999-999', { placeholder: '_' });
104
110
 
package/src/index.tsx CHANGED
@@ -1,14 +1,18 @@
1
1
  export {
2
2
  useHookFormMask,
3
3
  useMaskInput,
4
+ useTanStackFormMask,
4
5
  withHookFormMask,
5
6
  withMask,
7
+ withTanStackFormMask,
6
8
  } from './api';
7
9
 
8
10
  export type {
9
11
  Input,
10
12
  Mask,
11
13
  Options,
14
+ TanStackFormInputProps,
15
+ UseTanStackFormMaskReturn,
12
16
  UseFormRegister,
13
17
  UseFormRegisterReturn,
14
18
  } from './types';
@@ -36,3 +36,15 @@ export interface UseHookFormMaskReturn<
36
36
  ref: RefCallback<HTMLElement | null>;
37
37
  prevRef: RefCallback<HTMLElement | null>;
38
38
  }
39
+
40
+ export interface TanStackFormInputProps {
41
+ name?: string;
42
+ ref?: RefCallback<HTMLElement | null>;
43
+ [key: string]: unknown;
44
+ }
45
+
46
+ export type UseTanStackFormMaskReturn<T extends TanStackFormInputProps = TanStackFormInputProps> =
47
+ Omit<T, 'ref'> & {
48
+ ref: RefCallback<HTMLElement | null>;
49
+ prevRef: RefCallback<HTMLElement | null> | undefined;
50
+ };