use-mask-input 3.6.0 → 3.6.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 +47 -76
- package/README.md +2 -251
- package/dist/index.cjs +157 -84
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +52 -11
- package/dist/index.d.ts +52 -11
- package/dist/index.js +158 -85
- package/dist/index.js.map +1 -1
- package/package.json +21 -21
- package/src/api/index.ts +4 -0
- package/src/api/useHookFormMask.spec.ts +146 -0
- package/src/api/useHookFormMask.ts +56 -0
- package/src/api/useMaskInput-server.spec.tsx +30 -0
- package/src/api/useMaskInput.spec.tsx +220 -0
- package/src/api/useMaskInput.ts +64 -0
- package/src/api/withHookFormMask.spec.ts +155 -0
- package/src/api/withHookFormMask.ts +54 -0
- package/src/api/withMask.spec.ts +93 -0
- package/src/api/withMask.ts +25 -0
- package/src/core/elementResolver.spec.ts +175 -0
- package/src/core/elementResolver.ts +84 -0
- package/src/core/index.ts +3 -0
- package/src/core/maskConfig.spec.ts +183 -0
- package/src/{utils/getMaskOptions.ts → core/maskConfig.ts} +12 -3
- package/src/core/maskEngine.spec.ts +108 -0
- package/src/core/maskEngine.ts +47 -0
- package/src/index.tsx +12 -5
- package/src/{types.ts → types/index.ts} +13 -0
- package/src/utils/flow.spec.ts +27 -30
- package/src/utils/flow.ts +2 -2
- package/src/utils/index.ts +1 -1
- package/src/utils/isServer.spec.ts +15 -0
- package/src/utils/moduleInterop.spec.ts +37 -0
- package/src/useHookFormMask.ts +0 -47
- package/src/useMaskInput.ts +0 -41
- package/src/utils/getMaskOptions.spec.ts +0 -126
- package/src/withHookFormMask.ts +0 -34
- package/src/withMask.ts +0 -18
- /package/src/{inputmask.types.ts → types/inputmask.types.ts} +0 -0
|
@@ -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
|
+
});
|
|
@@ -1,13 +1,23 @@
|
|
|
1
|
+
/* eslint-disable import-x/prefer-default-export */
|
|
1
2
|
import type { Mask, Options } from '../types';
|
|
3
|
+
import type { Options as InputmaskOptions } from '../types/inputmask.types';
|
|
2
4
|
|
|
3
|
-
|
|
5
|
+
/**
|
|
6
|
+
* Converts mask and options into Inputmask configuration.
|
|
7
|
+
* Has some ready aliases to make life easier.
|
|
8
|
+
*
|
|
9
|
+
* @param mask - The mask pattern or alias
|
|
10
|
+
* @param _options - Optional configuration options
|
|
11
|
+
* @returns Inputmask configuration object
|
|
12
|
+
*/
|
|
13
|
+
export function getMaskOptions(mask?: Mask, _options?: Options): Options {
|
|
4
14
|
const options: Options = {
|
|
5
15
|
jitMasking: false,
|
|
6
16
|
..._options,
|
|
7
17
|
};
|
|
8
18
|
if (!mask) return options;
|
|
9
19
|
|
|
10
|
-
const masks: Record<string,
|
|
20
|
+
const masks: Record<string, InputmaskOptions> = {
|
|
11
21
|
datetime: {
|
|
12
22
|
alias: 'datetime',
|
|
13
23
|
...options,
|
|
@@ -63,7 +73,6 @@ export default function getMaskOptions(mask?: Mask, _options?: Options): Options
|
|
|
63
73
|
},
|
|
64
74
|
|
|
65
75
|
// alias for brazilians <3
|
|
66
|
-
// ty <3
|
|
67
76
|
'brl-currency': {
|
|
68
77
|
alias: 'currency',
|
|
69
78
|
prefix: 'R$ ',
|
|
@@ -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 {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
export {
|
|
2
|
+
useHookFormMask,
|
|
3
|
+
useMaskInput,
|
|
4
|
+
withHookFormMask,
|
|
5
|
+
withMask,
|
|
6
|
+
} from './api';
|
|
7
|
+
|
|
5
8
|
export type {
|
|
6
|
-
|
|
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
|
+
}
|
package/src/utils/flow.spec.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
/* eslint-disable @typescript-eslint/no-unsafe-function-type */
|
|
2
1
|
import {
|
|
3
2
|
describe, expect, it,
|
|
4
3
|
} from 'vitest';
|
|
@@ -11,50 +10,48 @@ describe('flow', () => {
|
|
|
11
10
|
expect(typeof result).toBe('function');
|
|
12
11
|
});
|
|
13
12
|
|
|
14
|
-
it('returns undefined value if no return
|
|
15
|
-
const result = flow(() => {})
|
|
16
|
-
expect(result).toBe(undefined);
|
|
13
|
+
it('returns undefined value if no return function provided', () => {
|
|
14
|
+
const result = flow(() => { });
|
|
15
|
+
expect(result(42)).toBe(undefined);
|
|
17
16
|
});
|
|
18
17
|
|
|
19
18
|
it('throws an error if any argument is not a function', () => {
|
|
20
19
|
// @ts-expect-error - null is not a function
|
|
21
20
|
expect(() => flow(null)).toThrow(TypeError);
|
|
22
21
|
// @ts-expect-error - null is not a function
|
|
23
|
-
expect(() => flow(() => {}, null)).toThrow(TypeError);
|
|
22
|
+
expect(() => flow(() => { }, null)).toThrow(TypeError);
|
|
23
|
+
expect(() => {
|
|
24
|
+
flow(1 as unknown as () => void, () => { });
|
|
25
|
+
}).toThrow(TypeError);
|
|
24
26
|
});
|
|
25
27
|
|
|
26
28
|
it('returns the original value if no functions are provided', () => {
|
|
27
|
-
const result = flow()
|
|
28
|
-
expect(result).toBe(42);
|
|
29
|
+
const result = flow();
|
|
30
|
+
expect(result(42)).toBe(42);
|
|
31
|
+
expect(result('test')).toBe('test');
|
|
29
32
|
});
|
|
30
33
|
|
|
31
|
-
it
|
|
32
|
-
const
|
|
33
|
-
const
|
|
34
|
-
|
|
35
|
-
};
|
|
36
|
-
const fn2 = function (this: { value: number }, n: number) {
|
|
37
|
-
return this.value + n;
|
|
38
|
-
};
|
|
39
|
-
const fn = flow(fn1, fn2).bind(context);
|
|
40
|
-
const result = fn(10);
|
|
41
|
-
expect(result).toBe(52);
|
|
42
|
-
});
|
|
34
|
+
it('composes functions correctly', () => {
|
|
35
|
+
const addOne = (x: number) => x + 1;
|
|
36
|
+
const multiplyByTwo = (x: number) => x * 2;
|
|
37
|
+
const subtractThree = (x: number) => x - 3;
|
|
43
38
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
expect(typeof result).toBe('function');
|
|
39
|
+
const composed = flow(addOne, multiplyByTwo, subtractThree);
|
|
40
|
+
expect(composed(5)).toBe(9); // (5 + 1) * 2 - 3 = 9
|
|
47
41
|
});
|
|
48
42
|
|
|
49
|
-
it('
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
43
|
+
it('works with single function', () => {
|
|
44
|
+
const double = (x: number) => x * 2;
|
|
45
|
+
const fn = flow(double);
|
|
46
|
+
expect(fn(5)).toBe(10);
|
|
53
47
|
});
|
|
54
48
|
|
|
55
|
-
it('
|
|
56
|
-
const
|
|
57
|
-
const
|
|
58
|
-
|
|
49
|
+
it('works with multiple functions', () => {
|
|
50
|
+
const fn1 = (n: number) => n + 1;
|
|
51
|
+
const fn2 = (n: number) => n * 2;
|
|
52
|
+
const fn3 = (n: number) => n - 1;
|
|
53
|
+
|
|
54
|
+
const composed = flow(fn1, fn2, fn3);
|
|
55
|
+
expect(composed(5)).toBe(11); // ((5 + 1) * 2) - 1 = 11
|
|
59
56
|
});
|
|
60
57
|
});
|
package/src/utils/flow.ts
CHANGED
|
@@ -11,10 +11,10 @@ export default function flow(...funcs: Function[]): Function {
|
|
|
11
11
|
}
|
|
12
12
|
return (...args: Function[]) => {
|
|
13
13
|
let i = 0;
|
|
14
|
-
let result = length ? funcs[i].apply(
|
|
14
|
+
let result = length ? funcs[i].apply(undefined, args) : args[0];
|
|
15
15
|
while (i + 1 < length) {
|
|
16
16
|
i += 1;
|
|
17
|
-
result = funcs[i].call(
|
|
17
|
+
result = funcs[i].call(undefined, result);
|
|
18
18
|
}
|
|
19
19
|
return result;
|
|
20
20
|
};
|
package/src/utils/index.ts
CHANGED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
|
|
3
|
+
describe('isServer', () => {
|
|
4
|
+
it('returns boolean value', async () => {
|
|
5
|
+
const { default: isServer } = await import('./isServer');
|
|
6
|
+
expect(typeof isServer).toBe('boolean');
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
it('returns false in browser environment', async () => {
|
|
10
|
+
// in jsdom environment, window and document exist
|
|
11
|
+
const { default: isServer } = await import('./isServer');
|
|
12
|
+
// in test environment with jsdom, it should be false
|
|
13
|
+
expect(isServer).toBe(false);
|
|
14
|
+
});
|
|
15
|
+
});
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
|
|
3
|
+
import interopDefaultSync from './moduleInterop';
|
|
4
|
+
|
|
5
|
+
describe('interopDefaultSync', () => {
|
|
6
|
+
it('returns module with default export', () => {
|
|
7
|
+
const module = { default: 'test' };
|
|
8
|
+
expect(interopDefaultSync(module)).toBe('test');
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
it('returns module without default export', () => {
|
|
12
|
+
const module = { foo: 'bar' };
|
|
13
|
+
expect(interopDefaultSync(module)).toBe(module);
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it('returns non-object values as-is', () => {
|
|
17
|
+
expect(interopDefaultSync('string')).toBe('string');
|
|
18
|
+
expect(interopDefaultSync(123)).toBe(123);
|
|
19
|
+
expect(interopDefaultSync(null)).toBe(null);
|
|
20
|
+
// eslint-disable-next-line @typescript-eslint/no-confusing-void-expression
|
|
21
|
+
expect(interopDefaultSync(undefined)).toBe(undefined);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it('handles null module', () => {
|
|
25
|
+
expect(interopDefaultSync(null)).toBe(null);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it('handles module with null default', () => {
|
|
29
|
+
const module = { default: null };
|
|
30
|
+
expect(interopDefaultSync(module)).toBe(null);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it('handles module with undefined default', () => {
|
|
34
|
+
const module = { default: undefined };
|
|
35
|
+
expect(interopDefaultSync(module)).toBe(undefined);
|
|
36
|
+
});
|
|
37
|
+
});
|
package/src/useHookFormMask.ts
DELETED
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
-
/* eslint-disable import-x/no-extraneous-dependencies */
|
|
3
|
-
import inputmask from 'inputmask';
|
|
4
|
-
|
|
5
|
-
import { flow, getMaskOptions } from './utils';
|
|
6
|
-
import interopDefaultSync from './utils/moduleInterop';
|
|
7
|
-
|
|
8
|
-
import type { RefCallback } from 'react';
|
|
9
|
-
import type {
|
|
10
|
-
FieldValues, Path,
|
|
11
|
-
RegisterOptions,
|
|
12
|
-
UseFormRegister,
|
|
13
|
-
UseFormRegisterReturn,
|
|
14
|
-
} from 'react-hook-form';
|
|
15
|
-
|
|
16
|
-
import type { Mask, Options } from './types';
|
|
17
|
-
|
|
18
|
-
export default function useHookFormMask<
|
|
19
|
-
T extends FieldValues, D extends RegisterOptions,
|
|
20
|
-
>(registerFn: UseFormRegister<T>) {
|
|
21
|
-
return (fieldName: Path<T>, mask: Mask, options?: (
|
|
22
|
-
D & Options) | Options | D): UseFormRegisterReturn<Path<T>> => {
|
|
23
|
-
if (!registerFn) throw new Error('registerFn is required');
|
|
24
|
-
|
|
25
|
-
const { ref, ...restRegister } = registerFn(fieldName, options as any);
|
|
26
|
-
|
|
27
|
-
const maskInput = interopDefaultSync(inputmask)(getMaskOptions(mask, options as any));
|
|
28
|
-
|
|
29
|
-
const newRef = flow((_ref: HTMLElement) => {
|
|
30
|
-
if (_ref) {
|
|
31
|
-
const { nodeName } = _ref;
|
|
32
|
-
|
|
33
|
-
if (nodeName !== 'INPUT') {
|
|
34
|
-
maskInput.mask(_ref.querySelector('input') as HTMLElement);
|
|
35
|
-
} else {
|
|
36
|
-
maskInput.mask(_ref);
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
return _ref;
|
|
40
|
-
}, ref) as RefCallback<HTMLElement>;
|
|
41
|
-
|
|
42
|
-
return {
|
|
43
|
-
...restRegister,
|
|
44
|
-
ref: newRef,
|
|
45
|
-
};
|
|
46
|
-
};
|
|
47
|
-
}
|
package/src/useMaskInput.ts
DELETED
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
/* eslint-disable import-x/no-extraneous-dependencies */
|
|
2
|
-
import { useEffect, useRef } from 'react';
|
|
3
|
-
|
|
4
|
-
import inputmask from 'inputmask';
|
|
5
|
-
|
|
6
|
-
import { getMaskOptions } from './utils';
|
|
7
|
-
import isServer from './utils/isServer';
|
|
8
|
-
import interopDefaultSync from './utils/moduleInterop';
|
|
9
|
-
|
|
10
|
-
import type { RefObject } from 'react';
|
|
11
|
-
|
|
12
|
-
import type { Mask, Options } from './types';
|
|
13
|
-
|
|
14
|
-
interface UseInputMaskOptions {
|
|
15
|
-
mask: Mask;
|
|
16
|
-
register?: (element: HTMLElement) => void;
|
|
17
|
-
options?: Options;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export default function useInputMask(props: UseInputMaskOptions): RefObject<HTMLInputElement> {
|
|
21
|
-
const { mask, register, options } = props;
|
|
22
|
-
const ref = useRef<HTMLInputElement>(null);
|
|
23
|
-
if (isServer) return ref;
|
|
24
|
-
|
|
25
|
-
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
26
|
-
useEffect(() => {
|
|
27
|
-
if (!isServer && ref.current) {
|
|
28
|
-
if (!ref.current) return;
|
|
29
|
-
|
|
30
|
-
const maskInput = interopDefaultSync(inputmask)(getMaskOptions(mask, options));
|
|
31
|
-
|
|
32
|
-
maskInput.mask(ref.current);
|
|
33
|
-
|
|
34
|
-
if (register && ref.current) {
|
|
35
|
-
register(ref.current);
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
}, [mask, register, options]);
|
|
39
|
-
|
|
40
|
-
return ref;
|
|
41
|
-
}
|