use-mask-input 3.6.1 → 3.7.1
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 +12 -0
- package/dist/antd.cjs +67 -0
- package/dist/antd.cjs.map +1 -0
- package/dist/antd.d.cts +37 -0
- package/dist/antd.d.ts +37 -0
- package/dist/antd.js +64 -0
- package/dist/antd.js.map +1 -0
- package/dist/chunk-4Y2DTPBL.cjs +3841 -0
- package/dist/chunk-4Y2DTPBL.cjs.map +1 -0
- package/dist/chunk-JGOZSJMW.js +3829 -0
- package/dist/chunk-JGOZSJMW.js.map +1 -0
- package/dist/index-F3rlTTTe.d.cts +583 -0
- package/dist/index-F3rlTTTe.d.ts +583 -0
- package/dist/index.cjs +30 -3901
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +4 -582
- package/dist/index.d.ts +4 -582
- package/dist/index.js +22 -3893
- package/dist/index.js.map +1 -1
- package/package.json +7 -1
- package/src/antd/index.ts +9 -0
- package/src/antd/useHookFormMaskAntd.spec.ts +142 -0
- package/src/antd/useHookFormMaskAntd.ts +57 -0
- package/src/antd/useMaskInputAntd-server.spec.tsx +37 -0
- package/src/antd/useMaskInputAntd.spec.tsx +109 -0
- package/src/antd/useMaskInputAntd.ts +77 -0
- package/src/api/useMaskInput.ts +26 -17
- package/src/core/elementResolver.ts +1 -1
- package/src/core/maskConfig.ts +31 -78
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "use-mask-input",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.7.1",
|
|
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>",
|
|
@@ -17,6 +17,11 @@
|
|
|
17
17
|
"types": "./dist/index.d.ts",
|
|
18
18
|
"import": "./dist/index.js",
|
|
19
19
|
"require": "./dist/index.cjs"
|
|
20
|
+
},
|
|
21
|
+
"./antd": {
|
|
22
|
+
"types": "./dist/antd.d.ts",
|
|
23
|
+
"import": "./dist/antd.js",
|
|
24
|
+
"require": "./dist/antd.cjs"
|
|
20
25
|
}
|
|
21
26
|
},
|
|
22
27
|
"engines": {
|
|
@@ -44,6 +49,7 @@
|
|
|
44
49
|
"@types/react": ">=17",
|
|
45
50
|
"@types/react-dom": ">=17",
|
|
46
51
|
"@vitest/coverage-v8": "3.2.4",
|
|
52
|
+
"antd": "^6.2.3",
|
|
47
53
|
"eslint": "^9.35.0",
|
|
48
54
|
"eslint-config-airbnb-extended": "^2.2.0",
|
|
49
55
|
"eslint-import-resolver-typescript": "^4.4.4",
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import {
|
|
2
|
+
beforeEach,
|
|
3
|
+
describe,
|
|
4
|
+
expect,
|
|
5
|
+
it,
|
|
6
|
+
vi,
|
|
7
|
+
} from 'vitest';
|
|
8
|
+
|
|
9
|
+
import { applyMaskToElement, resolveInputRef } from '../core';
|
|
10
|
+
import useHookFormMaskAntd from './useHookFormMaskAntd';
|
|
11
|
+
|
|
12
|
+
import type { InputRef } from 'antd';
|
|
13
|
+
import type {
|
|
14
|
+
FieldValues,
|
|
15
|
+
UseFormRegister,
|
|
16
|
+
} from 'react-hook-form';
|
|
17
|
+
|
|
18
|
+
vi.mock('../core', () => ({
|
|
19
|
+
applyMaskToElement: vi.fn(),
|
|
20
|
+
resolveInputRef: vi.fn(),
|
|
21
|
+
}));
|
|
22
|
+
|
|
23
|
+
describe('useHookFormMaskAntd', () => {
|
|
24
|
+
beforeEach(() => {
|
|
25
|
+
vi.clearAllMocks();
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it('returns a function', () => {
|
|
29
|
+
const registerFn = vi.fn(() => ({
|
|
30
|
+
ref: vi.fn(),
|
|
31
|
+
onChange: vi.fn(),
|
|
32
|
+
onBlur: vi.fn(),
|
|
33
|
+
name: 'test',
|
|
34
|
+
}));
|
|
35
|
+
|
|
36
|
+
const maskedRegister = useHookFormMaskAntd(
|
|
37
|
+
registerFn as UseFormRegister<FieldValues>,
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
expect(typeof maskedRegister).toBe('function');
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it('throws when registerFn is missing', () => {
|
|
44
|
+
const maskedRegister = useHookFormMaskAntd(
|
|
45
|
+
undefined as unknown as UseFormRegister<FieldValues>,
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
expect(() => maskedRegister('field' as never, '999-999'))
|
|
49
|
+
.toThrowError('registerFn is required');
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it('registers field with mask and calls core helpers', () => {
|
|
53
|
+
const inputElement = document.createElement('input');
|
|
54
|
+
const prevRef = vi.fn();
|
|
55
|
+
const registerFn = vi.fn(() => ({
|
|
56
|
+
ref: prevRef,
|
|
57
|
+
onChange: vi.fn(),
|
|
58
|
+
onBlur: vi.fn(),
|
|
59
|
+
name: 'phone',
|
|
60
|
+
}));
|
|
61
|
+
|
|
62
|
+
vi.mocked(resolveInputRef).mockReturnValue(inputElement);
|
|
63
|
+
|
|
64
|
+
const maskedRegister = useHookFormMaskAntd(
|
|
65
|
+
registerFn as UseFormRegister<FieldValues>,
|
|
66
|
+
);
|
|
67
|
+
const options = { placeholder: '_' } as never;
|
|
68
|
+
|
|
69
|
+
const result = maskedRegister('phone' as never, '999-999', options);
|
|
70
|
+
|
|
71
|
+
expect(registerFn).toHaveBeenCalledWith('phone', options);
|
|
72
|
+
expect(result.ref).toBeDefined();
|
|
73
|
+
expect(typeof result.ref).toBe('function');
|
|
74
|
+
|
|
75
|
+
const inputRef = { input: inputElement } as unknown as Parameters<
|
|
76
|
+
ReturnType<typeof useHookFormMaskAntd<FieldValues, never>>
|
|
77
|
+
>[1];
|
|
78
|
+
|
|
79
|
+
result.ref(inputRef as unknown as InputRef);
|
|
80
|
+
|
|
81
|
+
expect(resolveInputRef).toHaveBeenCalledWith(inputElement);
|
|
82
|
+
expect(applyMaskToElement).toHaveBeenCalledWith(
|
|
83
|
+
inputElement,
|
|
84
|
+
'999-999',
|
|
85
|
+
options,
|
|
86
|
+
);
|
|
87
|
+
expect(prevRef).toHaveBeenCalledWith(inputElement);
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it('handles null ref from register', () => {
|
|
91
|
+
const registerFn = vi.fn(() => ({
|
|
92
|
+
ref: undefined,
|
|
93
|
+
onChange: vi.fn(),
|
|
94
|
+
onBlur: vi.fn(),
|
|
95
|
+
name: 'phone',
|
|
96
|
+
}));
|
|
97
|
+
|
|
98
|
+
const maskedRegister = useHookFormMaskAntd(
|
|
99
|
+
registerFn as unknown as UseFormRegister<FieldValues>,
|
|
100
|
+
);
|
|
101
|
+
const result = maskedRegister('phone' as never, '999-999');
|
|
102
|
+
|
|
103
|
+
expect(result.ref).toBeDefined();
|
|
104
|
+
|
|
105
|
+
const inputElement = document.createElement('input');
|
|
106
|
+
vi.mocked(resolveInputRef).mockReturnValue(inputElement);
|
|
107
|
+
|
|
108
|
+
const inputRef = { input: inputElement } as unknown as Parameters<
|
|
109
|
+
ReturnType<typeof useHookFormMaskAntd<FieldValues, never>>
|
|
110
|
+
>[1];
|
|
111
|
+
|
|
112
|
+
result.ref(inputRef as unknown as InputRef);
|
|
113
|
+
|
|
114
|
+
expect(applyMaskToElement).toHaveBeenCalled();
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
it('preserves register return properties and defines non-enumerable prevRef', () => {
|
|
118
|
+
const onChange = vi.fn();
|
|
119
|
+
const onBlur = vi.fn();
|
|
120
|
+
const prevRef = vi.fn();
|
|
121
|
+
const registerFn = vi.fn(() => ({
|
|
122
|
+
ref: prevRef,
|
|
123
|
+
onChange,
|
|
124
|
+
onBlur,
|
|
125
|
+
name: 'phone',
|
|
126
|
+
}));
|
|
127
|
+
|
|
128
|
+
const maskedRegister = useHookFormMaskAntd(
|
|
129
|
+
registerFn as UseFormRegister<FieldValues>,
|
|
130
|
+
);
|
|
131
|
+
const result = maskedRegister('phone' as never, '999-999');
|
|
132
|
+
|
|
133
|
+
expect(result.onChange).toBe(onChange);
|
|
134
|
+
expect(result.onBlur).toBe(onBlur);
|
|
135
|
+
expect(result.name).toBe('phone');
|
|
136
|
+
|
|
137
|
+
const descriptor = Object.getOwnPropertyDescriptor(result, 'prevRef');
|
|
138
|
+
expect(descriptor?.enumerable).toBe(false);
|
|
139
|
+
expect((result as unknown as { prevRef: typeof prevRef }).prevRef)
|
|
140
|
+
.toBe(prevRef);
|
|
141
|
+
});
|
|
142
|
+
});
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { applyMaskToElement, resolveInputRef } from '../core';
|
|
2
|
+
|
|
3
|
+
import type { InputRef } from 'antd';
|
|
4
|
+
import type { RefCallback } from 'react';
|
|
5
|
+
import type {
|
|
6
|
+
FieldValues, Path,
|
|
7
|
+
RegisterOptions,
|
|
8
|
+
UseFormRegister,
|
|
9
|
+
} from 'react-hook-form';
|
|
10
|
+
|
|
11
|
+
import type { Mask, Options, UseHookFormMaskReturn } from '../types';
|
|
12
|
+
|
|
13
|
+
export type UseHookFormMaskAntdReturn<T extends FieldValues> = Omit<
|
|
14
|
+
UseHookFormMaskReturn<T>,
|
|
15
|
+
'ref'
|
|
16
|
+
> & { ref: RefCallback<InputRef | null> };
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Ant Design version of useHookFormMask.
|
|
20
|
+
* Creates a masked register that works with Ant Design Input (ref receives InputRef).
|
|
21
|
+
*
|
|
22
|
+
* @template T - The form data type
|
|
23
|
+
* @template D - The register options type
|
|
24
|
+
* @param registerFn - The register function from useForm hook
|
|
25
|
+
* @returns A function that registers a field with mask support for Ant Design Input
|
|
26
|
+
*/
|
|
27
|
+
export default function useHookFormMaskAntd<
|
|
28
|
+
T extends FieldValues, D extends RegisterOptions,
|
|
29
|
+
>(registerFn: UseFormRegister<T>) {
|
|
30
|
+
return (fieldName: Path<T>, mask: Mask, options?: (
|
|
31
|
+
D & Options) | Options | D): UseHookFormMaskAntdReturn<T> => {
|
|
32
|
+
if (!registerFn) throw new Error('registerFn is required');
|
|
33
|
+
|
|
34
|
+
const registerReturn = registerFn(fieldName, options as Options);
|
|
35
|
+
const { ref } = registerReturn as UseHookFormMaskReturn<T>;
|
|
36
|
+
|
|
37
|
+
const refWithMask: RefCallback<InputRef | null> = (inputRef) => {
|
|
38
|
+
const element = inputRef ? resolveInputRef(inputRef.input) : null;
|
|
39
|
+
if (element) applyMaskToElement(element, mask, options as Options);
|
|
40
|
+
if (ref) ref(element);
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const result = {
|
|
44
|
+
...registerReturn,
|
|
45
|
+
ref: refWithMask,
|
|
46
|
+
} as UseHookFormMaskAntdReturn<T>;
|
|
47
|
+
|
|
48
|
+
Object.defineProperty(result, 'prevRef', {
|
|
49
|
+
value: ref,
|
|
50
|
+
enumerable: false,
|
|
51
|
+
writable: true,
|
|
52
|
+
configurable: true,
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
return result;
|
|
56
|
+
};
|
|
57
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { act, renderHook } from '@testing-library/react';
|
|
2
|
+
import {
|
|
3
|
+
beforeEach,
|
|
4
|
+
describe,
|
|
5
|
+
expect,
|
|
6
|
+
it,
|
|
7
|
+
vi,
|
|
8
|
+
} from 'vitest';
|
|
9
|
+
|
|
10
|
+
import type { InputRef } from 'antd';
|
|
11
|
+
|
|
12
|
+
vi.mock('../utils/isServer', () => ({
|
|
13
|
+
default: true,
|
|
14
|
+
}));
|
|
15
|
+
|
|
16
|
+
describe('useMaskInputAntd server-side', () => {
|
|
17
|
+
beforeEach(() => {
|
|
18
|
+
vi.clearAllMocks();
|
|
19
|
+
vi.resetModules();
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it('returns no-op function on server', async () => {
|
|
23
|
+
const { default: useMaskInputAntd } = await import('./useMaskInputAntd');
|
|
24
|
+
const { result } = renderHook(
|
|
25
|
+
() => useMaskInputAntd({ mask: '999-999' }),
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
expect(typeof result.current).toBe('function');
|
|
29
|
+
|
|
30
|
+
act(() => {
|
|
31
|
+
result.current({ input: document.createElement('input') } as InputRef);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
// should do nothing on server
|
|
35
|
+
expect(result.current).toBeDefined();
|
|
36
|
+
});
|
|
37
|
+
});
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { act, renderHook } from '@testing-library/react';
|
|
2
|
+
import inputmask from 'inputmask';
|
|
3
|
+
import {
|
|
4
|
+
beforeEach,
|
|
5
|
+
describe,
|
|
6
|
+
expect,
|
|
7
|
+
it,
|
|
8
|
+
vi,
|
|
9
|
+
} from 'vitest';
|
|
10
|
+
|
|
11
|
+
import useMaskInputAntd from './useMaskInputAntd';
|
|
12
|
+
|
|
13
|
+
import type { InputRef } from 'antd';
|
|
14
|
+
|
|
15
|
+
vi.mock('inputmask', () => ({
|
|
16
|
+
default: vi.fn((options) => ({
|
|
17
|
+
mask: vi.fn(),
|
|
18
|
+
options,
|
|
19
|
+
})),
|
|
20
|
+
}));
|
|
21
|
+
|
|
22
|
+
vi.mock('../utils/isServer', () => ({
|
|
23
|
+
default: false,
|
|
24
|
+
}));
|
|
25
|
+
|
|
26
|
+
describe('useMaskInputAntd', () => {
|
|
27
|
+
beforeEach(() => {
|
|
28
|
+
vi.clearAllMocks();
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it('returns a ref callback function', () => {
|
|
32
|
+
const { result } = renderHook(() => useMaskInputAntd({ mask: '999-999' }));
|
|
33
|
+
expect(typeof result.current).toBe('function');
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it('handles null input ref', () => {
|
|
37
|
+
const { result } = renderHook(() => useMaskInputAntd({ mask: '999-999' }));
|
|
38
|
+
|
|
39
|
+
act(() => {
|
|
40
|
+
result.current(null);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
expect(result.current).toBeDefined();
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it('applies mask to element when given InputRef with input element', () => {
|
|
47
|
+
const inputElement = document.createElement('input');
|
|
48
|
+
vi.mocked(inputmask).mockReturnValue({
|
|
49
|
+
mask: vi.fn(),
|
|
50
|
+
} as unknown as Inputmask.Instance);
|
|
51
|
+
|
|
52
|
+
const { result, rerender } = renderHook(
|
|
53
|
+
() => useMaskInputAntd({ mask: '999-999' }),
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
act(() => {
|
|
57
|
+
result.current({ input: inputElement } as unknown as InputRef);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
rerender();
|
|
61
|
+
|
|
62
|
+
expect(inputmask).toHaveBeenCalled();
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it('works with custom options', () => {
|
|
66
|
+
const inputElement = document.createElement('input');
|
|
67
|
+
vi.mocked(inputmask).mockReturnValue({
|
|
68
|
+
mask: vi.fn(),
|
|
69
|
+
} as unknown as Inputmask.Instance);
|
|
70
|
+
|
|
71
|
+
const { result, rerender } = renderHook(
|
|
72
|
+
() => useMaskInputAntd({
|
|
73
|
+
mask: '999-999',
|
|
74
|
+
options: { placeholder: '_' },
|
|
75
|
+
}),
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
act(() => {
|
|
79
|
+
result.current({ input: inputElement } as unknown as InputRef);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
rerender();
|
|
83
|
+
|
|
84
|
+
expect(inputmask).toHaveBeenCalled();
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it('accepts register option and applies mask', () => {
|
|
88
|
+
const inputElement = document.createElement('input');
|
|
89
|
+
const register = vi.fn();
|
|
90
|
+
vi.mocked(inputmask).mockReturnValue({
|
|
91
|
+
mask: vi.fn(),
|
|
92
|
+
} as unknown as Inputmask.Instance);
|
|
93
|
+
|
|
94
|
+
const { result, rerender } = renderHook(
|
|
95
|
+
() => useMaskInputAntd({
|
|
96
|
+
mask: '999-999',
|
|
97
|
+
register,
|
|
98
|
+
}),
|
|
99
|
+
);
|
|
100
|
+
|
|
101
|
+
act(() => {
|
|
102
|
+
result.current({ input: inputElement } as unknown as InputRef);
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
rerender();
|
|
106
|
+
|
|
107
|
+
expect(inputmask).toHaveBeenCalled();
|
|
108
|
+
});
|
|
109
|
+
});
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import {
|
|
2
|
+
useCallback,
|
|
3
|
+
useEffect,
|
|
4
|
+
useMemo,
|
|
5
|
+
useRef,
|
|
6
|
+
} from 'react';
|
|
7
|
+
|
|
8
|
+
import {
|
|
9
|
+
createMaskInstance, findInputElement, isHTMLElement, resolveInputRef,
|
|
10
|
+
} from '../core';
|
|
11
|
+
import isServer from '../utils/isServer';
|
|
12
|
+
|
|
13
|
+
import type { InputRef } from 'antd';
|
|
14
|
+
|
|
15
|
+
import type { Mask, Options } from '../types';
|
|
16
|
+
|
|
17
|
+
interface UseMaskInputOptions {
|
|
18
|
+
mask: Mask;
|
|
19
|
+
register?: (element: HTMLElement) => void;
|
|
20
|
+
options?: Options;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* React hook for applying input masks to form elements.
|
|
25
|
+
* Works with Ant Design and other wrapped components too.
|
|
26
|
+
*
|
|
27
|
+
* @param props - Configuration object
|
|
28
|
+
* @param props.mask - The mask pattern to apply
|
|
29
|
+
* @param props.register - Optional callback that receives the element
|
|
30
|
+
* @param props.options - Optional mask configuration options
|
|
31
|
+
* @returns A ref callback function to attach to the input element
|
|
32
|
+
*/
|
|
33
|
+
export default function useMaskInputAntd(props: UseMaskInputOptions): (
|
|
34
|
+
input: InputRef | null
|
|
35
|
+
) => void {
|
|
36
|
+
const { mask, register, options } = props;
|
|
37
|
+
const ref = useRef<InputRef | null>(null);
|
|
38
|
+
const maskInput = useMemo(
|
|
39
|
+
() => (isServer ? null : createMaskInstance(mask, options)),
|
|
40
|
+
[mask, options],
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
const refCallback = useCallback((input: InputRef | null) => {
|
|
44
|
+
if (!input) {
|
|
45
|
+
ref.current = null;
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
ref.current = resolveInputRef(input.input) as unknown as InputRef;
|
|
50
|
+
}, []);
|
|
51
|
+
|
|
52
|
+
useEffect(() => {
|
|
53
|
+
if (isServer || !ref.current) return;
|
|
54
|
+
|
|
55
|
+
if (!isHTMLElement(ref.current)) {
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const inputElement = findInputElement(ref.current);
|
|
60
|
+
|
|
61
|
+
if (maskInput && inputElement && isHTMLElement(inputElement)) {
|
|
62
|
+
maskInput.mask(inputElement);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (register && isHTMLElement(ref.current)) {
|
|
66
|
+
register(ref.current);
|
|
67
|
+
}
|
|
68
|
+
}, [mask, register, options, maskInput]);
|
|
69
|
+
|
|
70
|
+
if (isServer) {
|
|
71
|
+
return (): void => {
|
|
72
|
+
// server doesn't have dom, so just do nothing
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return refCallback;
|
|
77
|
+
}
|
package/src/api/useMaskInput.ts
CHANGED
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
useCallback,
|
|
3
|
+
useEffect,
|
|
4
|
+
useMemo,
|
|
5
|
+
useRef,
|
|
6
|
+
} from 'react';
|
|
2
7
|
|
|
3
8
|
import {
|
|
4
9
|
createMaskInstance, findInputElement, isHTMLElement, resolveInputRef,
|
|
@@ -26,15 +31,20 @@ interface UseMaskInputOptions {
|
|
|
26
31
|
export default function useMaskInput(props: UseMaskInputOptions): ((input: Input | null) => void) {
|
|
27
32
|
const { mask, register, options } = props;
|
|
28
33
|
const ref = useRef<HTMLInputElement | null>(null);
|
|
29
|
-
const maskInput = useMemo(
|
|
34
|
+
const maskInput = useMemo(
|
|
35
|
+
() => (isServer ? null : createMaskInstance(mask, options)),
|
|
36
|
+
[mask, options],
|
|
37
|
+
);
|
|
30
38
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
39
|
+
const refCallback = useCallback((input: Input | null): void => {
|
|
40
|
+
if (!input) {
|
|
41
|
+
ref.current = null;
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
ref.current = resolveInputRef(input);
|
|
46
|
+
}, []);
|
|
36
47
|
|
|
37
|
-
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
38
48
|
useEffect(() => {
|
|
39
49
|
if (isServer || !ref.current) return;
|
|
40
50
|
|
|
@@ -44,21 +54,20 @@ export default function useMaskInput(props: UseMaskInputOptions): ((input: Input
|
|
|
44
54
|
|
|
45
55
|
const inputElement = findInputElement(ref.current);
|
|
46
56
|
|
|
47
|
-
if (inputElement && isHTMLElement(inputElement)) {
|
|
57
|
+
if (maskInput && inputElement && isHTMLElement(inputElement)) {
|
|
48
58
|
maskInput.mask(inputElement);
|
|
49
59
|
}
|
|
50
60
|
|
|
51
61
|
if (register && isHTMLElement(ref.current)) {
|
|
52
62
|
register(ref.current);
|
|
53
63
|
}
|
|
54
|
-
}, [mask, register, options, maskInput
|
|
64
|
+
}, [mask, register, options, maskInput]);
|
|
55
65
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
66
|
+
if (isServer) {
|
|
67
|
+
return (): void => {
|
|
68
|
+
// server doesn't have dom, so just do nothing
|
|
69
|
+
};
|
|
70
|
+
}
|
|
61
71
|
|
|
62
|
-
|
|
63
|
-
};
|
|
72
|
+
return refCallback;
|
|
64
73
|
}
|
|
@@ -41,7 +41,7 @@ export function findInputElement(element: unknown): HTMLElement | null {
|
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
try {
|
|
44
|
-
const inputElement = element.querySelector('input
|
|
44
|
+
const inputElement = element.querySelector('input, textarea');
|
|
45
45
|
|
|
46
46
|
if (inputElement && isHTMLElement(inputElement)) {
|
|
47
47
|
return inputElement;
|
package/src/core/maskConfig.ts
CHANGED
|
@@ -2,6 +2,34 @@
|
|
|
2
2
|
import type { Mask, Options } from '../types';
|
|
3
3
|
import type { Options as InputmaskOptions } from '../types/inputmask.types';
|
|
4
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
|
+
|
|
5
33
|
/**
|
|
6
34
|
* Converts mask and options into Inputmask configuration.
|
|
7
35
|
* Has some ready aliases to make life easier.
|
|
@@ -17,84 +45,9 @@ export function getMaskOptions(mask?: Mask, _options?: Options): Options {
|
|
|
17
45
|
};
|
|
18
46
|
if (!mask) return options;
|
|
19
47
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
...options,
|
|
24
|
-
},
|
|
25
|
-
email: {
|
|
26
|
-
alias: 'email',
|
|
27
|
-
placeholder: '',
|
|
28
|
-
...options,
|
|
29
|
-
},
|
|
30
|
-
numeric: {
|
|
31
|
-
alias: 'numeric',
|
|
32
|
-
placeholder: '',
|
|
33
|
-
...options,
|
|
34
|
-
},
|
|
35
|
-
currency: {
|
|
36
|
-
alias: 'currency',
|
|
37
|
-
prefix: '$ ',
|
|
38
|
-
placeholder: '',
|
|
39
|
-
...options,
|
|
40
|
-
},
|
|
41
|
-
decimal: {
|
|
42
|
-
alias: 'decimal',
|
|
43
|
-
placeholder: '',
|
|
44
|
-
...options,
|
|
45
|
-
},
|
|
46
|
-
integer: {
|
|
47
|
-
alias: 'integer',
|
|
48
|
-
placeholder: '',
|
|
49
|
-
...options,
|
|
50
|
-
},
|
|
51
|
-
percentage: {
|
|
52
|
-
alias: 'percentage',
|
|
53
|
-
placeholder: ' %',
|
|
54
|
-
suffix: ' %',
|
|
55
|
-
...options,
|
|
56
|
-
},
|
|
57
|
-
url: {
|
|
58
|
-
alias: 'url',
|
|
59
|
-
placeholder: 'https://',
|
|
60
|
-
...options,
|
|
61
|
-
},
|
|
62
|
-
ip: {
|
|
63
|
-
alias: 'ip',
|
|
64
|
-
...options,
|
|
65
|
-
},
|
|
66
|
-
mac: {
|
|
67
|
-
alias: 'mac',
|
|
68
|
-
...options,
|
|
69
|
-
},
|
|
70
|
-
ssn: {
|
|
71
|
-
alias: 'ssn',
|
|
72
|
-
...options,
|
|
73
|
-
},
|
|
74
|
-
|
|
75
|
-
// alias for brazilians <3
|
|
76
|
-
'brl-currency': {
|
|
77
|
-
alias: 'currency',
|
|
78
|
-
prefix: 'R$ ',
|
|
79
|
-
placeholder: '0,00',
|
|
80
|
-
displayFormat: 'currency',
|
|
81
|
-
radixPoint: ',',
|
|
82
|
-
autoUnmask: true,
|
|
83
|
-
...options,
|
|
84
|
-
},
|
|
85
|
-
cpf: {
|
|
86
|
-
mask: '999.999.999-99',
|
|
87
|
-
placeholder: '___.___.___-__',
|
|
88
|
-
...options,
|
|
89
|
-
},
|
|
90
|
-
cnpj: {
|
|
91
|
-
mask: ['A|9{2}.A|9{3}.A|9{3}/A|9{4}-9{2}'],
|
|
92
|
-
placeholder: '__.___.___/____-__',
|
|
93
|
-
...options,
|
|
94
|
-
},
|
|
95
|
-
};
|
|
96
|
-
|
|
97
|
-
if (typeof mask === 'string' && masks[mask]) return masks[mask];
|
|
48
|
+
if (typeof mask === 'string' && ALIAS_MASKS[mask]) {
|
|
49
|
+
return { ...ALIAS_MASKS[mask], ...options } as Options;
|
|
50
|
+
}
|
|
98
51
|
|
|
99
52
|
return {
|
|
100
53
|
mask,
|