use-mask-input 3.6.0 → 3.7.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.
Files changed (58) hide show
  1. package/CHANGELOG.md +53 -76
  2. package/README.md +2 -251
  3. package/dist/antd/index.cjs +67 -0
  4. package/dist/antd/index.cjs.map +1 -0
  5. package/dist/antd/index.d.cts +37 -0
  6. package/dist/antd/index.d.ts +37 -0
  7. package/dist/antd/index.js +64 -0
  8. package/dist/antd/index.js.map +1 -0
  9. package/dist/chunk-4Y2DTPBL.cjs +3841 -0
  10. package/dist/chunk-4Y2DTPBL.cjs.map +1 -0
  11. package/dist/chunk-JGOZSJMW.js +3829 -0
  12. package/dist/chunk-JGOZSJMW.js.map +1 -0
  13. package/dist/index-F3rlTTTe.d.cts +583 -0
  14. package/dist/index-F3rlTTTe.d.ts +583 -0
  15. package/dist/index.cjs +72 -3870
  16. package/dist/index.cjs.map +1 -1
  17. package/dist/index.d.cts +48 -585
  18. package/dist/index.d.ts +48 -585
  19. package/dist/index.js +72 -3870
  20. package/dist/index.js.map +1 -1
  21. package/package.json +27 -21
  22. package/src/antd/index.ts +9 -0
  23. package/src/antd/useHookFormMaskAntd.spec.ts +142 -0
  24. package/src/antd/useHookFormMaskAntd.ts +57 -0
  25. package/src/antd/useMaskInputAntd-server.spec.tsx +36 -0
  26. package/src/antd/useMaskInputAntd.spec.tsx +108 -0
  27. package/src/antd/useMaskInputAntd.ts +77 -0
  28. package/src/api/index.ts +4 -0
  29. package/src/api/useHookFormMask.spec.ts +146 -0
  30. package/src/api/useHookFormMask.ts +56 -0
  31. package/src/api/useMaskInput-server.spec.tsx +30 -0
  32. package/src/api/useMaskInput.spec.tsx +220 -0
  33. package/src/api/useMaskInput.ts +73 -0
  34. package/src/api/withHookFormMask.spec.ts +155 -0
  35. package/src/api/withHookFormMask.ts +54 -0
  36. package/src/api/withMask.spec.ts +93 -0
  37. package/src/api/withMask.ts +25 -0
  38. package/src/core/elementResolver.spec.ts +175 -0
  39. package/src/core/elementResolver.ts +84 -0
  40. package/src/core/index.ts +3 -0
  41. package/src/core/maskConfig.spec.ts +183 -0
  42. package/src/core/maskConfig.ts +56 -0
  43. package/src/core/maskEngine.spec.ts +108 -0
  44. package/src/core/maskEngine.ts +47 -0
  45. package/src/index.tsx +12 -5
  46. package/src/{types.ts → types/index.ts} +13 -0
  47. package/src/utils/flow.spec.ts +27 -30
  48. package/src/utils/flow.ts +2 -2
  49. package/src/utils/index.ts +1 -1
  50. package/src/utils/isServer.spec.ts +15 -0
  51. package/src/utils/moduleInterop.spec.ts +37 -0
  52. package/src/useHookFormMask.ts +0 -47
  53. package/src/useMaskInput.ts +0 -41
  54. package/src/utils/getMaskOptions.spec.ts +0 -126
  55. package/src/utils/getMaskOptions.ts +0 -94
  56. package/src/withHookFormMask.ts +0 -34
  57. package/src/withMask.ts +0 -18
  58. /package/src/{inputmask.types.ts → types/inputmask.types.ts} +0 -0
@@ -0,0 +1,175 @@
1
+ import {
2
+ describe, expect, it, vi,
3
+ } from 'vitest';
4
+
5
+ import { findInputElement, isHTMLElement, resolveInputRef } from './elementResolver';
6
+
7
+ import type { Input } from '..';
8
+
9
+ describe('elementResolver', () => {
10
+ describe('isHTMLElement', () => {
11
+ it('returns true for valid HTMLElement', () => {
12
+ const element = document.createElement('div');
13
+ expect(isHTMLElement(element)).toBe(true);
14
+ });
15
+
16
+ it('returns false for null', () => {
17
+ expect(isHTMLElement(null)).toBe(false);
18
+ });
19
+
20
+ it('returns false for undefined', () => {
21
+ expect(isHTMLElement(undefined)).toBe(false);
22
+ });
23
+
24
+ it('returns false for string', () => {
25
+ expect(isHTMLElement('string')).toBe(false);
26
+ });
27
+
28
+ it('returns false for number', () => {
29
+ expect(isHTMLElement(123)).toBe(false);
30
+ });
31
+
32
+ it('returns false for object without nodeType', () => {
33
+ expect(isHTMLElement({})).toBe(false);
34
+ });
35
+
36
+ it('returns false for object without querySelector', () => {
37
+ expect(isHTMLElement({ nodeType: 1 })).toBe(false);
38
+ });
39
+
40
+ it('returns false for object with non-function querySelector', () => {
41
+ expect(isHTMLElement({ nodeType: 1, querySelector: 'not a function' })).toBe(false);
42
+ });
43
+ });
44
+
45
+ describe('findInputElement', () => {
46
+ it('returns null for null', () => {
47
+ expect(findInputElement(null)).toBe(null);
48
+ });
49
+
50
+ it('returns null for undefined', () => {
51
+ expect(findInputElement(undefined)).toBe(null);
52
+ });
53
+
54
+ it('returns input element directly if it is an INPUT', () => {
55
+ const input = document.createElement('input');
56
+ expect(findInputElement(input)).toBe(input);
57
+ });
58
+
59
+ it('returns textarea element directly if it is a TEXTAREA', () => {
60
+ const textarea = document.createElement('textarea');
61
+ expect(findInputElement(textarea)).toBe(textarea);
62
+ });
63
+
64
+ it('finds input inside wrapper element', () => {
65
+ const wrapper = document.createElement('div');
66
+ const input = document.createElement('input');
67
+ wrapper.appendChild(input);
68
+ expect(findInputElement(wrapper)).toBe(input);
69
+ });
70
+
71
+ it('finds textarea inside wrapper element', () => {
72
+ const wrapper = document.createElement('div');
73
+ const textarea = document.createElement('textarea');
74
+ wrapper.appendChild(textarea);
75
+ expect(findInputElement(wrapper)).toBe(textarea);
76
+ });
77
+
78
+ it('returns null if no input found inside wrapper', () => {
79
+ const wrapper = document.createElement('div');
80
+ const span = document.createElement('span');
81
+ wrapper.appendChild(span);
82
+ expect(findInputElement(wrapper)).toBe(null);
83
+ });
84
+
85
+ it('returns null for invalid element', () => {
86
+ expect(findInputElement('not an element')).toBe(null);
87
+ });
88
+
89
+ it('handles querySelector error gracefully', () => {
90
+ const element = {
91
+ nodeType: 1,
92
+ nodeName: 'DIV',
93
+ querySelector: vi.fn(() => {
94
+ throw new Error('querySelector error');
95
+ }),
96
+ };
97
+ expect(findInputElement(element)).toBe(null);
98
+ });
99
+
100
+ it('handles element without querySelector method', () => {
101
+ const element = {
102
+ nodeType: 1,
103
+ nodeName: 'DIV',
104
+ };
105
+ expect(findInputElement(element)).toBe(null);
106
+ });
107
+
108
+ it('handles element with non-function querySelector', () => {
109
+ const element = {
110
+ nodeType: 1,
111
+ nodeName: 'DIV',
112
+ querySelector: 'not a function',
113
+ };
114
+ expect(findInputElement(element)).toBe(null);
115
+ });
116
+
117
+ it('handles element where querySelector is not in element', () => {
118
+ const element = {
119
+ nodeType: 1,
120
+ nodeName: 'DIV',
121
+ };
122
+ // element doesn't have querySelector property
123
+ expect(findInputElement(element)).toBe(null);
124
+ });
125
+
126
+ it('handles querySelector returning null', () => {
127
+ const element = {
128
+ nodeType: 1,
129
+ nodeName: 'DIV',
130
+ querySelector: vi.fn(() => null),
131
+ };
132
+ expect(findInputElement(element)).toBe(null);
133
+ });
134
+
135
+ it('handles querySelector returning non-HTMLElement', () => {
136
+ const element = {
137
+ nodeType: 1,
138
+ nodeName: 'DIV',
139
+ querySelector: vi.fn(() => 'not an element'),
140
+ };
141
+ expect(findInputElement(element)).toBe(null);
142
+ });
143
+ });
144
+
145
+ describe('resolveInputRef', () => {
146
+ it('returns null for null', () => {
147
+ expect(resolveInputRef(null)).toBe(null);
148
+ });
149
+
150
+ it('returns element for direct HTMLElement', () => {
151
+ const input = document.createElement('input');
152
+ expect(resolveInputRef(input)).toBe(input);
153
+ });
154
+
155
+ it('returns element from ref object with current', () => {
156
+ const input = document.createElement('input');
157
+ const ref = { current: input };
158
+ expect(resolveInputRef(ref as unknown as Input)).toBe(input);
159
+ });
160
+
161
+ it('returns null for ref object with null current', () => {
162
+ const ref = { current: null } as unknown as Input;
163
+ expect(resolveInputRef(ref)).toBe(null);
164
+ });
165
+
166
+ it('returns null for ref object with invalid current', () => {
167
+ const ref = { current: 'not an element' } as unknown as Input;
168
+ expect(resolveInputRef(ref)).toBe(null);
169
+ });
170
+
171
+ it('returns null for invalid input type', () => {
172
+ expect(resolveInputRef('not an element' as unknown as Input)).toBe(null);
173
+ });
174
+ });
175
+ });
@@ -0,0 +1,84 @@
1
+ import type { Input } from '../types';
2
+
3
+ /**
4
+ * Checks if an element is a valid DOM element (or at least looks like one).
5
+ *
6
+ * @param element - The element to check
7
+ * @returns True if it's a valid HTMLElement
8
+ */
9
+ export function isHTMLElement(element: unknown): element is HTMLElement {
10
+ return (
11
+ element !== null
12
+ && typeof element === 'object'
13
+ && 'nodeType' in element
14
+ && 'querySelector' in element
15
+ && typeof (element as HTMLElement).querySelector === 'function'
16
+ );
17
+ }
18
+
19
+ /**
20
+ * Finds the actual input element from various component structures.
21
+ * Finds the actual input inside wrappers (ant design, etc). Like a detective.
22
+ *
23
+ * @param element - The element to search in
24
+ * @returns The found input element or null
25
+ */
26
+ export function findInputElement(element: unknown): HTMLElement | null {
27
+ if (!element) return null;
28
+
29
+ if (!isHTMLElement(element)) {
30
+ return null;
31
+ }
32
+
33
+ // if it's already an input or textarea, return it directly
34
+ if (element.nodeName === 'INPUT' || element.nodeName === 'TEXTAREA') {
35
+ return element;
36
+ }
37
+
38
+ // tries to find input inside the wrapper
39
+ if (!('querySelector' in element) || typeof element.querySelector !== 'function') {
40
+ return null;
41
+ }
42
+
43
+ try {
44
+ const inputElement = element.querySelector('input, textarea');
45
+
46
+ if (inputElement && isHTMLElement(inputElement)) {
47
+ return inputElement;
48
+ }
49
+ } catch {
50
+ // if it errors, return null and move on
51
+ return null;
52
+ }
53
+
54
+ return null;
55
+ }
56
+
57
+ /**
58
+ * Resolves React refs to a valid HTMLInputElement.
59
+ * Handles ref objects and direct DOM elements.
60
+ *
61
+ * @param input - The input reference to resolve
62
+ * @returns A valid HTMLInputElement or null
63
+ */
64
+ export function resolveInputRef(input: Input | null): HTMLInputElement | null {
65
+ if (!input) {
66
+ return null;
67
+ }
68
+
69
+ // react ref objects
70
+ if (typeof input === 'object' && 'current' in input) {
71
+ const refValue = (input as { current: HTMLElement | null }).current;
72
+ if (isHTMLElement(refValue)) {
73
+ return refValue as HTMLInputElement;
74
+ }
75
+ return null;
76
+ }
77
+
78
+ // direct dom elements
79
+ if (isHTMLElement(input)) {
80
+ return input as HTMLInputElement;
81
+ }
82
+
83
+ return null;
84
+ }
@@ -0,0 +1,3 @@
1
+ export * from './elementResolver';
2
+ export * from './maskEngine';
3
+ export * from './maskConfig';
@@ -0,0 +1,183 @@
1
+ import { describe, expect, it } from 'vitest';
2
+
3
+ import { getMaskOptions } from './maskConfig';
4
+
5
+ describe('maskConfig', () => {
6
+ describe('getMaskOptions', () => {
7
+ it('returns default options when no mask is provided', () => {
8
+ const options = getMaskOptions();
9
+ expect(options).toEqual({
10
+ jitMasking: false,
11
+ });
12
+ });
13
+
14
+ it('merges custom options with defaults', () => {
15
+ const options = getMaskOptions(undefined, { placeholder: '_' });
16
+ expect(options).toEqual({
17
+ jitMasking: false,
18
+ placeholder: '_',
19
+ });
20
+ });
21
+
22
+ it('returns options for datetime mask', () => {
23
+ const options = getMaskOptions('datetime');
24
+ expect(options).toEqual({
25
+ alias: 'datetime',
26
+ jitMasking: false,
27
+ });
28
+ });
29
+
30
+ it('returns options for email mask', () => {
31
+ const options = getMaskOptions('email');
32
+ expect(options).toEqual({
33
+ alias: 'email',
34
+ placeholder: '',
35
+ jitMasking: false,
36
+ });
37
+ });
38
+
39
+ it('returns options for numeric mask', () => {
40
+ const options = getMaskOptions('numeric');
41
+ expect(options).toEqual({
42
+ alias: 'numeric',
43
+ placeholder: '',
44
+ jitMasking: false,
45
+ });
46
+ });
47
+
48
+ it('returns options for currency mask', () => {
49
+ const options = getMaskOptions('currency');
50
+ expect(options).toEqual({
51
+ alias: 'currency',
52
+ prefix: '$ ',
53
+ placeholder: '',
54
+ jitMasking: false,
55
+ });
56
+ });
57
+
58
+ it('returns options for decimal mask', () => {
59
+ const options = getMaskOptions('decimal');
60
+ expect(options).toEqual({
61
+ alias: 'decimal',
62
+ placeholder: '',
63
+ jitMasking: false,
64
+ });
65
+ });
66
+
67
+ it('returns options for integer mask', () => {
68
+ const options = getMaskOptions('integer');
69
+ expect(options).toEqual({
70
+ alias: 'integer',
71
+ placeholder: '',
72
+ jitMasking: false,
73
+ });
74
+ });
75
+
76
+ it('returns options for percentage mask', () => {
77
+ const options = getMaskOptions('percentage');
78
+ expect(options).toEqual({
79
+ alias: 'percentage',
80
+ placeholder: ' %',
81
+ suffix: ' %',
82
+ jitMasking: false,
83
+ });
84
+ });
85
+
86
+ it('returns options for url mask', () => {
87
+ const options = getMaskOptions('url');
88
+ expect(options).toEqual({
89
+ alias: 'url',
90
+ placeholder: 'https://',
91
+ jitMasking: false,
92
+ });
93
+ });
94
+
95
+ it('returns options for ip mask', () => {
96
+ const options = getMaskOptions('ip');
97
+ expect(options).toEqual({
98
+ alias: 'ip',
99
+ jitMasking: false,
100
+ });
101
+ });
102
+
103
+ it('returns options for mac mask', () => {
104
+ const options = getMaskOptions('mac');
105
+ expect(options).toEqual({
106
+ alias: 'mac',
107
+ jitMasking: false,
108
+ });
109
+ });
110
+
111
+ it('returns options for ssn mask', () => {
112
+ const options = getMaskOptions('ssn');
113
+ expect(options).toEqual({
114
+ alias: 'ssn',
115
+ jitMasking: false,
116
+ });
117
+ });
118
+
119
+ it('returns options for brl-currency mask', () => {
120
+ const options = getMaskOptions('brl-currency');
121
+ expect(options).toEqual({
122
+ alias: 'currency',
123
+ prefix: 'R$ ',
124
+ placeholder: '0,00',
125
+ displayFormat: 'currency',
126
+ radixPoint: ',',
127
+ autoUnmask: true,
128
+ jitMasking: false,
129
+ });
130
+ });
131
+
132
+ it('returns options for cpf mask', () => {
133
+ const options = getMaskOptions('cpf');
134
+ expect(options).toEqual({
135
+ mask: '999.999.999-99',
136
+ placeholder: '___.___.___-__',
137
+ jitMasking: false,
138
+ });
139
+ });
140
+
141
+ it('returns options for cnpj mask', () => {
142
+ const options = getMaskOptions('cnpj');
143
+ expect(options).toEqual({
144
+ mask: ['A|9{2}.A|9{3}.A|9{3}/A|9{4}-9{2}'],
145
+ placeholder: '__.___.___/____-__',
146
+ jitMasking: false,
147
+ });
148
+ });
149
+
150
+ it('returns custom mask string', () => {
151
+ const options = getMaskOptions('999-999');
152
+ expect(options).toEqual({
153
+ mask: '999-999',
154
+ jitMasking: false,
155
+ });
156
+ });
157
+
158
+ it('returns custom mask array', () => {
159
+ const mask = ['999-999', '9999-9999'];
160
+ const options = getMaskOptions(mask);
161
+ expect(options).toEqual({
162
+ mask,
163
+ jitMasking: false,
164
+ });
165
+ });
166
+
167
+ it('merges custom options with alias options', () => {
168
+ const options = getMaskOptions('cpf', { placeholder: '___' });
169
+ expect(options).toEqual({
170
+ mask: '999.999.999-99',
171
+ placeholder: '___',
172
+ jitMasking: false,
173
+ });
174
+ });
175
+
176
+ it('handles null mask', () => {
177
+ const options = getMaskOptions(null);
178
+ expect(options).toEqual({
179
+ jitMasking: false,
180
+ });
181
+ });
182
+ });
183
+ });
@@ -0,0 +1,56 @@
1
+ /* eslint-disable import-x/prefer-default-export */
2
+ import type { Mask, Options } from '../types';
3
+ import type { Options as InputmaskOptions } from '../types/inputmask.types';
4
+
5
+ /** Base config for built-in mask aliases. Merged with user options in getMaskOptions. */
6
+ const ALIAS_MASKS: Record<string, InputmaskOptions> = {
7
+ datetime: { alias: 'datetime' },
8
+ email: { alias: 'email', placeholder: '' },
9
+ numeric: { alias: 'numeric', placeholder: '' },
10
+ currency: { alias: 'currency', prefix: '$ ', placeholder: '' },
11
+ decimal: { alias: 'decimal', placeholder: '' },
12
+ integer: { alias: 'integer', placeholder: '' },
13
+ percentage: { alias: 'percentage', placeholder: ' %', suffix: ' %' },
14
+ url: { alias: 'url', placeholder: 'https://' },
15
+ ip: { alias: 'ip' },
16
+ mac: { alias: 'mac' },
17
+ ssn: { alias: 'ssn' },
18
+ 'brl-currency': {
19
+ alias: 'currency',
20
+ prefix: 'R$ ',
21
+ placeholder: '0,00',
22
+ displayFormat: 'currency',
23
+ radixPoint: ',',
24
+ autoUnmask: true,
25
+ },
26
+ cpf: { mask: '999.999.999-99', placeholder: '___.___.___-__' },
27
+ cnpj: {
28
+ mask: ['A|9{2}.A|9{3}.A|9{3}/A|9{4}-9{2}'],
29
+ placeholder: '__.___.___/____-__',
30
+ },
31
+ };
32
+
33
+ /**
34
+ * Converts mask and options into Inputmask configuration.
35
+ * Has some ready aliases to make life easier.
36
+ *
37
+ * @param mask - The mask pattern or alias
38
+ * @param _options - Optional configuration options
39
+ * @returns Inputmask configuration object
40
+ */
41
+ export function getMaskOptions(mask?: Mask, _options?: Options): Options {
42
+ const options: Options = {
43
+ jitMasking: false,
44
+ ..._options,
45
+ };
46
+ if (!mask) return options;
47
+
48
+ if (typeof mask === 'string' && ALIAS_MASKS[mask]) {
49
+ return { ...ALIAS_MASKS[mask], ...options } as Options;
50
+ }
51
+
52
+ return {
53
+ mask,
54
+ ...options,
55
+ };
56
+ }
@@ -0,0 +1,108 @@
1
+ import inputmask from 'inputmask';
2
+ import {
3
+ beforeEach, describe, expect, it, vi,
4
+ } from 'vitest';
5
+
6
+ import { applyMaskToElement, createMaskInstance } from './maskEngine';
7
+
8
+ vi.mock('inputmask', () => ({
9
+ default: vi.fn((options) => ({
10
+ mask: vi.fn(),
11
+ options,
12
+ })),
13
+ }));
14
+
15
+ describe('maskEngine', () => {
16
+ beforeEach(() => {
17
+ vi.clearAllMocks();
18
+ });
19
+
20
+ describe('createMaskInstance', () => {
21
+ it('creates mask instance with string mask', () => {
22
+ const instance = createMaskInstance('999-999');
23
+ expect(inputmask).toHaveBeenCalled();
24
+ expect(instance).toBeDefined();
25
+ });
26
+
27
+ it('creates mask instance with alias', () => {
28
+ const instance = createMaskInstance('cpf');
29
+ expect(inputmask).toHaveBeenCalled();
30
+ expect(instance).toBeDefined();
31
+ });
32
+
33
+ it('creates mask instance with options', () => {
34
+ const instance = createMaskInstance('999-999', { placeholder: '_' });
35
+ expect(inputmask).toHaveBeenCalled();
36
+ expect(instance).toBeDefined();
37
+ });
38
+
39
+ it('creates mask instance with array mask', () => {
40
+ const instance = createMaskInstance(['999-999', '9999-9999']);
41
+ expect(inputmask).toHaveBeenCalled();
42
+ expect(instance).toBeDefined();
43
+ });
44
+ });
45
+
46
+ describe('applyMaskToElement', () => {
47
+ it('applies mask to input element', () => {
48
+ const input = document.createElement('input');
49
+ const maskFn = vi.fn();
50
+ vi.mocked(inputmask).mockReturnValue({ mask: maskFn } as unknown as Inputmask.Instance);
51
+
52
+ applyMaskToElement(input, '999-999');
53
+
54
+ expect(maskFn).toHaveBeenCalledWith(input);
55
+ });
56
+
57
+ it('applies mask to textarea element', () => {
58
+ const textarea = document.createElement('textarea');
59
+ const maskFn = vi.fn();
60
+ vi.mocked(inputmask).mockReturnValue({ mask: maskFn } as unknown as Inputmask.Instance);
61
+
62
+ applyMaskToElement(textarea, '999-999');
63
+
64
+ expect(maskFn).toHaveBeenCalledWith(textarea);
65
+ });
66
+
67
+ it('finds and applies mask to input inside wrapper', () => {
68
+ const wrapper = document.createElement('div');
69
+ const input = document.createElement('input');
70
+ wrapper.appendChild(input);
71
+ const maskFn = vi.fn();
72
+ vi.mocked(inputmask).mockReturnValue({ mask: maskFn } as unknown as Inputmask.Instance);
73
+
74
+ applyMaskToElement(wrapper, '999-999');
75
+
76
+ expect(maskFn).toHaveBeenCalledWith(input);
77
+ });
78
+
79
+ it('applies mask to wrapper if no input found inside', () => {
80
+ const wrapper = document.createElement('div');
81
+ const maskFn = vi.fn();
82
+ vi.mocked(inputmask).mockReturnValue({ mask: maskFn } as unknown as Inputmask.Instance);
83
+
84
+ applyMaskToElement(wrapper, '999-999');
85
+
86
+ expect(maskFn).toHaveBeenCalledWith(wrapper);
87
+ });
88
+
89
+ it('does nothing if element is null', () => {
90
+ const maskFn = vi.fn();
91
+ vi.mocked(inputmask).mockReturnValue({ mask: maskFn } as unknown as Inputmask.Instance);
92
+
93
+ applyMaskToElement(null as unknown as HTMLElement, '999-999');
94
+
95
+ expect(maskFn).not.toHaveBeenCalled();
96
+ });
97
+
98
+ it('applies mask with custom options', () => {
99
+ const input = document.createElement('input');
100
+ const maskFn = vi.fn();
101
+ vi.mocked(inputmask).mockReturnValue({ mask: maskFn } as unknown as Inputmask.Instance);
102
+
103
+ applyMaskToElement(input, '999-999', { placeholder: '_' });
104
+
105
+ expect(maskFn).toHaveBeenCalledWith(input);
106
+ });
107
+ });
108
+ });
@@ -0,0 +1,47 @@
1
+ /* eslint-disable import-x/no-extraneous-dependencies */
2
+ import inputmask from 'inputmask';
3
+
4
+ import { getMaskOptions } from './maskConfig';
5
+ import { moduleInterop } from '../utils';
6
+
7
+ import type { Mask, Options } from '../types';
8
+
9
+ /**
10
+ * Creates a mask instance with the given mask and options.
11
+ * Like a factory, but simpler.
12
+ *
13
+ * @param mask - The mask pattern
14
+ * @param options - Optional configuration options
15
+ * @returns A mask instance
16
+ */
17
+ export function createMaskInstance(mask: Mask, options?: Options): Inputmask.Instance {
18
+ const inputmaskInstance = moduleInterop(inputmask);
19
+ return inputmaskInstance(getMaskOptions(mask, options));
20
+ }
21
+
22
+ /**
23
+ * Applies a mask to an input element.
24
+ * If it's not a direct input, searches inside.
25
+ *
26
+ * @param element - The element to apply mask to
27
+ * @param mask - The mask pattern
28
+ * @param options - Optional configuration options
29
+ */
30
+ export function applyMaskToElement(
31
+ element: HTMLElement | null,
32
+ mask: Mask,
33
+ options?: Options,
34
+ ): void {
35
+ if (!element) return;
36
+
37
+ const maskInstance = createMaskInstance(mask, options);
38
+ const inputElement = element.nodeName === 'INPUT'
39
+ ? element
40
+ : (element.querySelector('input') as HTMLElement);
41
+
42
+ if (inputElement) {
43
+ maskInstance.mask(inputElement);
44
+ } else {
45
+ maskInstance.mask(element);
46
+ }
47
+ }
package/src/index.tsx CHANGED
@@ -1,7 +1,14 @@
1
- export { default as withHookFormMask } from './withHookFormMask';
2
- export { default as withMask } from './withMask';
3
- export { default as useHookFormMask } from './useHookFormMask';
4
- export { default as useMaskInput } from './useMaskInput';
1
+ export {
2
+ useHookFormMask,
3
+ useMaskInput,
4
+ withHookFormMask,
5
+ withMask,
6
+ } from './api';
7
+
5
8
  export type {
6
- Mask, Options, UseFormRegister, UseFormRegisterReturn,
9
+ Input,
10
+ Mask,
11
+ Options,
12
+ UseFormRegister,
13
+ UseFormRegisterReturn,
7
14
  } from './types';
@@ -1,3 +1,9 @@
1
+ import type { RefCallback } from 'react';
2
+ import type {
3
+ FieldValues, Path,
4
+ UseFormRegisterReturn,
5
+ } from 'react-hook-form';
6
+
1
7
  import type { Options as MaskOptions } from './inputmask.types';
2
8
 
3
9
  export type { UseFormRegister, UseFormRegisterReturn } from 'react-hook-form';
@@ -21,3 +27,10 @@ export type Mask = 'datetime'
21
27
  | null;
22
28
  export type Options = MaskOptions;
23
29
  export type Input = HTMLInputElement | HTMLTextAreaElement | HTMLElement;
30
+
31
+ export interface UseHookFormMaskReturn<
32
+ T extends FieldValues,
33
+ > extends UseFormRegisterReturn<Path<T>> {
34
+ ref: RefCallback<HTMLElement | null>;
35
+ prevRef: RefCallback<HTMLElement | null>;
36
+ }