mfk-mask-input 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +283 -0
- package/dist/index.cjs +2 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +80 -0
- package/dist/index.js +53 -0
- package/dist/index.js.map +1 -0
- package/package.json +52 -0
package/README.md
ADDED
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
# mfk-mask-input
|
|
2
|
+
|
|
3
|
+
A lightweight and flexible vanilla React masked input component built with [IMask](https://imask.js.org/).
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- 🎭 **Flexible Masking** - Supports various mask patterns (phone, credit card, date, custom)
|
|
8
|
+
- 🪶 **Lightweight** - Vanilla React input, no UI framework dependencies
|
|
9
|
+
- 📝 **TypeScript Support** - Full TypeScript support with type definitions
|
|
10
|
+
- ⚡ **Minimal Dependencies** - Only React and IMask as peer dependencies
|
|
11
|
+
- 🔧 **Customizable** - Extensive IMask options support
|
|
12
|
+
- 🎯 **Developer Friendly** - Access both masked and unmasked values in onChange
|
|
13
|
+
|
|
14
|
+
## Installation
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
npm install mfk-mask-input
|
|
18
|
+
# or
|
|
19
|
+
yarn add mfk-mask-input
|
|
20
|
+
# or
|
|
21
|
+
pnpm add mfk-mask-input
|
|
22
|
+
# or
|
|
23
|
+
bun add mfk-mask-input
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
### Peer Dependencies
|
|
27
|
+
|
|
28
|
+
This package requires the following peer dependencies:
|
|
29
|
+
|
|
30
|
+
```json
|
|
31
|
+
{
|
|
32
|
+
"react": "^18.0.0 || ^19.0.0",
|
|
33
|
+
"react-dom": "^18.0.0 || ^19.0.0",
|
|
34
|
+
"imask": "^6.0.0 || ^7.0.0"
|
|
35
|
+
}
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Quick Start
|
|
39
|
+
|
|
40
|
+
```tsx
|
|
41
|
+
import { InputMask } from "mfk-mask-input";
|
|
42
|
+
|
|
43
|
+
function MyComponent() {
|
|
44
|
+
return (
|
|
45
|
+
<InputMask
|
|
46
|
+
mask="(000) 000-0000"
|
|
47
|
+
placeholder="Enter phone number"
|
|
48
|
+
onChange={(e) => {
|
|
49
|
+
console.log("Masked:", e.maskedValue); // "(555) 123-4567"
|
|
50
|
+
console.log("Unmasked:", e.unmaskedValue); // "5551234567"
|
|
51
|
+
}}
|
|
52
|
+
/>
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Usage Examples
|
|
58
|
+
|
|
59
|
+
### Phone Number
|
|
60
|
+
|
|
61
|
+
```tsx
|
|
62
|
+
<InputMask mask="(000) 000-0000" placeholder="(555) 123-4567" />
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### Credit Card
|
|
66
|
+
|
|
67
|
+
```tsx
|
|
68
|
+
<InputMask mask="0000 0000 0000 0000" placeholder="1234 5678 9012 3456" />
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### Date
|
|
72
|
+
|
|
73
|
+
```tsx
|
|
74
|
+
<InputMask mask="00/00/0000" placeholder="DD/MM/YYYY" />
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### Custom Patterns
|
|
78
|
+
|
|
79
|
+
```tsx
|
|
80
|
+
<InputMask
|
|
81
|
+
mask="AAA-000"
|
|
82
|
+
definitions={{
|
|
83
|
+
A: /[A-Z]/,
|
|
84
|
+
"0": /[0-9]/,
|
|
85
|
+
}}
|
|
86
|
+
placeholder="ABC-123"
|
|
87
|
+
/>
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### Multiple Masks (Dynamic)
|
|
91
|
+
|
|
92
|
+
```tsx
|
|
93
|
+
<InputMask
|
|
94
|
+
mask={[
|
|
95
|
+
{ mask: "(000) 000-0000" },
|
|
96
|
+
{ mask: "+0 (000) 000-0000" },
|
|
97
|
+
{ mask: "+00 (000) 000-0000" },
|
|
98
|
+
]}
|
|
99
|
+
placeholder="Phone number"
|
|
100
|
+
/>
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### Controlled Component
|
|
104
|
+
|
|
105
|
+
```tsx
|
|
106
|
+
import { useState } from "react";
|
|
107
|
+
import { InputMask } from "mfk-mask-input";
|
|
108
|
+
|
|
109
|
+
function ControlledExample() {
|
|
110
|
+
const [value, setValue] = useState("");
|
|
111
|
+
|
|
112
|
+
return (
|
|
113
|
+
<InputMask
|
|
114
|
+
mask="0000-0000-0000-0000"
|
|
115
|
+
value={value}
|
|
116
|
+
onChange={(e) => setValue(e.maskedValue)}
|
|
117
|
+
/>
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### With Advanced IMask Options
|
|
123
|
+
|
|
124
|
+
```tsx
|
|
125
|
+
<InputMask
|
|
126
|
+
mask={Number}
|
|
127
|
+
maskOptions={{
|
|
128
|
+
scale: 2,
|
|
129
|
+
thousandsSeparator: ",",
|
|
130
|
+
radix: ".",
|
|
131
|
+
mapToRadix: ["."],
|
|
132
|
+
min: 0,
|
|
133
|
+
max: 999999,
|
|
134
|
+
}}
|
|
135
|
+
placeholder="0.00"
|
|
136
|
+
/>
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### With Custom Styling
|
|
140
|
+
|
|
141
|
+
```tsx
|
|
142
|
+
<InputMask
|
|
143
|
+
mask="(000) 000-0000"
|
|
144
|
+
className="my-input-class"
|
|
145
|
+
style={{ padding: "8px", border: "1px solid #ccc" }}
|
|
146
|
+
/>
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
## API
|
|
150
|
+
|
|
151
|
+
### Props
|
|
152
|
+
|
|
153
|
+
All standard HTML input props are supported, plus the following:
|
|
154
|
+
|
|
155
|
+
| Prop | Type | Required | Description |
|
|
156
|
+
| -------------- | -------------------------------- | -------- | -------------------------------------------------------------------------------------------------------- |
|
|
157
|
+
| `mask` | `MaskType` | Yes | Mask pattern or configuration. Can be a string, RegExp, Date, Number, function, or array of mask objects |
|
|
158
|
+
| `maskOptions` | `InputMaskOptions` | No | Additional IMask configuration options |
|
|
159
|
+
| `definitions` | `object` | No | Custom character definitions (e.g., `{ 'A': /[A-Z]/ }`) |
|
|
160
|
+
| `onChange` | `(event: OnChangeEvent) => void` | No | Change handler with extended event object |
|
|
161
|
+
| `value` | `string` | No | Controlled component value |
|
|
162
|
+
| `defaultValue` | `string` | No | Default value for uncontrolled usage |
|
|
163
|
+
|
|
164
|
+
### Types
|
|
165
|
+
|
|
166
|
+
#### OnChangeEvent
|
|
167
|
+
|
|
168
|
+
The extended onChange event includes:
|
|
169
|
+
|
|
170
|
+
```tsx
|
|
171
|
+
interface OnChangeEvent {
|
|
172
|
+
target: HTMLInputElement;
|
|
173
|
+
maskedValue: string; // Formatted value with mask
|
|
174
|
+
unmaskedValue: string; // Raw value without mask
|
|
175
|
+
// ... standard event properties
|
|
176
|
+
}
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
#### MaskType
|
|
180
|
+
|
|
181
|
+
```tsx
|
|
182
|
+
type MaskType =
|
|
183
|
+
| string
|
|
184
|
+
| RegExp
|
|
185
|
+
| typeof Number
|
|
186
|
+
| typeof Date
|
|
187
|
+
| ((value: string) => string)
|
|
188
|
+
| Array<{ mask: string | RegExp /* other options */ }>;
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
## Advanced Usage
|
|
192
|
+
|
|
193
|
+
### Custom Definitions
|
|
194
|
+
|
|
195
|
+
Define custom placeholder characters:
|
|
196
|
+
|
|
197
|
+
```tsx
|
|
198
|
+
<InputMask
|
|
199
|
+
mask="00/00/0000"
|
|
200
|
+
definitions={{
|
|
201
|
+
"0": /[0-9]/, // Digit
|
|
202
|
+
A: /[A-Z]/, // Uppercase letter
|
|
203
|
+
a: /[a-z]/, // Lowercase letter
|
|
204
|
+
"*": /[A-Za-z0-9]/, // Alphanumeric
|
|
205
|
+
}}
|
|
206
|
+
/>
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
### With Form Libraries
|
|
210
|
+
|
|
211
|
+
#### React Hook Form
|
|
212
|
+
|
|
213
|
+
```tsx
|
|
214
|
+
import { useForm, Controller } from "react-hook-form";
|
|
215
|
+
import { InputMask } from "mfk-mask-input";
|
|
216
|
+
|
|
217
|
+
function FormExample() {
|
|
218
|
+
const { control } = useForm();
|
|
219
|
+
|
|
220
|
+
return (
|
|
221
|
+
<Controller
|
|
222
|
+
name="phone"
|
|
223
|
+
control={control}
|
|
224
|
+
render={({ field }) => (
|
|
225
|
+
<InputMask
|
|
226
|
+
mask="(000) 000-0000"
|
|
227
|
+
{...field}
|
|
228
|
+
onChange={(e) => field.onChange(e.maskedValue)}
|
|
229
|
+
/>
|
|
230
|
+
)}
|
|
231
|
+
/>
|
|
232
|
+
);
|
|
233
|
+
}
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
#### Formik
|
|
237
|
+
|
|
238
|
+
```tsx
|
|
239
|
+
import { Formik, Field } from "formik";
|
|
240
|
+
import { InputMask } from "mfk-mask-input";
|
|
241
|
+
|
|
242
|
+
function FormikExample() {
|
|
243
|
+
return (
|
|
244
|
+
<Formik initialValues={{ phone: "" }}>
|
|
245
|
+
{({ setFieldValue }) => (
|
|
246
|
+
<Field name="phone">
|
|
247
|
+
{({ field }) => (
|
|
248
|
+
<InputMask
|
|
249
|
+
mask="(000) 000-0000"
|
|
250
|
+
value={field.value}
|
|
251
|
+
onChange={(e) => setFieldValue("phone", e.maskedValue)}
|
|
252
|
+
/>
|
|
253
|
+
)}
|
|
254
|
+
</Field>
|
|
255
|
+
)}
|
|
256
|
+
</Formik>
|
|
257
|
+
);
|
|
258
|
+
}
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
## IMask Documentation
|
|
262
|
+
|
|
263
|
+
For complete IMask options and patterns, see the [official IMask documentation](https://imask.js.org/guide.html).
|
|
264
|
+
|
|
265
|
+
## TypeScript
|
|
266
|
+
|
|
267
|
+
This package includes TypeScript definitions. Import types as needed:
|
|
268
|
+
|
|
269
|
+
```tsx
|
|
270
|
+
import type {
|
|
271
|
+
MaskedInputProps,
|
|
272
|
+
OnChangeEvent,
|
|
273
|
+
InputMaskOptions,
|
|
274
|
+
} from "mfk-mask-input";
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
## License
|
|
278
|
+
|
|
279
|
+
MIT
|
|
280
|
+
|
|
281
|
+
## Contributing
|
|
282
|
+
|
|
283
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const O=require("react/jsx-runtime"),d=require("imask"),n=require("react"),E=n.forwardRef(function(y,a){const{mask:l,maskOptions:c,value:s,defaultValue:M,definitions:f,onChange:k,...I}=y,p=n.useRef(null),e=n.useRef(null),r=(typeof s=="string"?s:M)||"",[V,i]=n.useState(r),o=n.useMemo(()=>({mask:l,lazy:!0,definitions:{0:/[0-9]/,...c?.definitions,...f},...c}),[l,c,f]),m=n.useCallback(()=>{const t=p.current;if(!t)return;if(e.current){e.current.updateOptions(o);return}const u=d(t,o);e.current=u,u.on("accept",()=>{const v=u.value,h=u.unmaskedValue;i(v),k?.({target:t,maskedValue:v,unmaskedValue:h})}),r&&(u.value=r,t.value=u.value,i(u.value))},[o,k,r]);n.useEffect(()=>(m(),()=>{e.current?.destroy(),e.current=null}),[m]),n.useEffect(()=>{e.current&&s!==void 0&&e.current.value!==s&&(e.current.value=s,i(e.current.value))},[s]);const g=t=>{p.current=t,a&&(typeof a=="function"?a(t):a.current=t)};return O.jsx("input",{...I,ref:g,value:V,onChange:()=>{}})});exports.IMask=d;exports.InputMask=E;
|
|
2
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.cjs","sources":["../src/InputMask.tsx"],"sourcesContent":["import IMask from 'imask';\r\nimport { forwardRef, useCallback, useEffect, useMemo, useRef, useState } from 'react';\r\nimport type { IMaskOptions, MaskedInputProps, OnChangeEvent } from './types';\r\n\r\n/**\r\n * InputMask - A vanilla masked input component\r\n * \r\n * @example\r\n * ```tsx\r\n * <InputMask\r\n * mask=\"(000) 000-0000\"\r\n * placeholder=\"Phone number\"\r\n * onChange={(e) => console.log(e.maskedValue, e.unmaskedValue)}\r\n * />\r\n * ```\r\n */\r\nconst InputMask = forwardRef<HTMLInputElement, MaskedInputProps>(function MaskedInput(props, forwardedRef) {\r\n const {\r\n mask,\r\n maskOptions: _maskOptions,\r\n value: controlledValue,\r\n defaultValue,\r\n definitions,\r\n onChange,\r\n ...defaultInputProps\r\n } = props;\r\n\r\n const inputRef = useRef<HTMLInputElement | null>(null);\r\n const maskRef = useRef<ReturnType<typeof IMask> | null>(null);\r\n\r\n const initialValue = (typeof controlledValue === 'string' ? controlledValue : defaultValue) || '';\r\n const [value, setValue] = useState(initialValue);\r\n\r\n const maskOptions = useMemo(() => {\r\n return {\r\n mask,\r\n lazy: true,\r\n definitions: {\r\n 0: /[0-9]/,\r\n ..._maskOptions?.definitions,\r\n ...definitions,\r\n },\r\n ..._maskOptions,\r\n } as IMaskOptions;\r\n }, [mask, _maskOptions, definitions]);\r\n\r\n const initMask = useCallback(() => {\r\n const el = inputRef.current;\r\n if (!el) return;\r\n\r\n if (maskRef.current) {\r\n maskRef.current.updateOptions(maskOptions as any);\r\n return;\r\n }\r\n\r\n const maskInstance = IMask(el, maskOptions as any);\r\n maskRef.current = maskInstance;\r\n\r\n maskInstance.on('accept', () => {\r\n const masked = maskInstance.value;\r\n const unmasked = maskInstance.unmaskedValue;\r\n setValue(masked);\r\n\r\n const syntheticEvent = {\r\n target: el,\r\n maskedValue: masked,\r\n unmaskedValue: unmasked,\r\n } as OnChangeEvent;\r\n onChange?.(syntheticEvent);\r\n });\r\n\r\n if (initialValue) {\r\n maskInstance.value = initialValue;\r\n el.value = maskInstance.value;\r\n setValue(maskInstance.value);\r\n }\r\n }, [maskOptions, onChange, initialValue]);\r\n\r\n useEffect(() => {\r\n initMask();\r\n return () => {\r\n maskRef.current?.destroy();\r\n maskRef.current = null;\r\n };\r\n }, [initMask]);\r\n\r\n useEffect(() => {\r\n if (maskRef.current && controlledValue !== undefined) {\r\n if (maskRef.current.value !== controlledValue) {\r\n maskRef.current.value = controlledValue;\r\n setValue(maskRef.current.value);\r\n }\r\n }\r\n }, [controlledValue]);\r\n\r\n const handleRef = (ref: HTMLInputElement | null) => {\r\n inputRef.current = ref;\r\n if (forwardedRef) {\r\n if (typeof forwardedRef === 'function') forwardedRef(ref);\r\n else forwardedRef.current = ref;\r\n }\r\n };\r\n\r\n return <input {...defaultInputProps} ref={handleRef} value={value} onChange={() => {}} />;\r\n});\r\n\r\nexport default InputMask;\r\n"],"names":["InputMask","forwardRef","props","forwardedRef","mask","_maskOptions","controlledValue","defaultValue","definitions","onChange","defaultInputProps","inputRef","useRef","maskRef","initialValue","value","setValue","useState","maskOptions","useMemo","initMask","useCallback","el","maskInstance","IMask","masked","unmasked","useEffect","handleRef","ref","jsx"],"mappings":"2JAgBMA,EAAYC,EAAAA,WAA+C,SAAqBC,EAAOC,EAAc,CACzG,KAAM,CACJ,KAAAC,EACA,YAAaC,EACb,MAAOC,EACP,aAAAC,EACA,YAAAC,EACA,SAAAC,EACA,GAAGC,CAAA,EACDR,EAEES,EAAWC,EAAAA,OAAgC,IAAI,EAC/CC,EAAUD,EAAAA,OAAwC,IAAI,EAEtDE,GAAgB,OAAOR,GAAoB,SAAWA,EAAkBC,IAAiB,GACzF,CAACQ,EAAOC,CAAQ,EAAIC,EAAAA,SAASH,CAAY,EAEzCI,EAAcC,EAAAA,QAAQ,KACnB,CACL,KAAAf,EACA,KAAM,GACN,YAAa,CACX,EAAG,QACH,GAAGC,GAAc,YACjB,GAAGG,CAAA,EAEL,GAAGH,CAAA,GAEJ,CAACD,EAAMC,EAAcG,CAAW,CAAC,EAE9BY,EAAWC,EAAAA,YAAY,IAAM,CACjC,MAAMC,EAAKX,EAAS,QACpB,GAAI,CAACW,EAAI,OAET,GAAIT,EAAQ,QAAS,CACnBA,EAAQ,QAAQ,cAAcK,CAAkB,EAChD,MACF,CAEA,MAAMK,EAAeC,EAAMF,EAAIJ,CAAkB,EACjDL,EAAQ,QAAUU,EAElBA,EAAa,GAAG,SAAU,IAAM,CAC9B,MAAME,EAASF,EAAa,MACtBG,EAAWH,EAAa,cAC9BP,EAASS,CAAM,EAOfhB,IALuB,CACrB,OAAQa,EACR,YAAaG,EACb,cAAeC,CAAA,CAEQ,CAC3B,CAAC,EAEGZ,IACFS,EAAa,MAAQT,EACrBQ,EAAG,MAAQC,EAAa,MACxBP,EAASO,EAAa,KAAK,EAE/B,EAAG,CAACL,EAAaT,EAAUK,CAAY,CAAC,EAExCa,EAAAA,UAAU,KACRP,EAAA,EACO,IAAM,CACXP,EAAQ,SAAS,QAAA,EACjBA,EAAQ,QAAU,IACpB,GACC,CAACO,CAAQ,CAAC,EAEbO,EAAAA,UAAU,IAAM,CACVd,EAAQ,SAAWP,IAAoB,QACrCO,EAAQ,QAAQ,QAAUP,IAC5BO,EAAQ,QAAQ,MAAQP,EACxBU,EAASH,EAAQ,QAAQ,KAAK,EAGpC,EAAG,CAACP,CAAe,CAAC,EAEpB,MAAMsB,EAAaC,GAAiC,CAClDlB,EAAS,QAAUkB,EACf1B,IACE,OAAOA,GAAiB,WAAYA,EAAa0B,CAAG,IACtC,QAAUA,EAEhC,EAEA,OAAOC,EAAAA,IAAC,SAAO,GAAGpB,EAAmB,IAAKkB,EAAW,MAAAb,EAAc,SAAU,IAAM,CAAC,EAAG,CACzF,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { ForwardRefExoticComponent } from 'react';
|
|
2
|
+
import { default as IMask } from 'imask';
|
|
3
|
+
import { RefAttributes } from 'react';
|
|
4
|
+
|
|
5
|
+
declare type GeneralInputProps = React.InputHTMLAttributes<HTMLInputElement>;
|
|
6
|
+
|
|
7
|
+
export { IMask }
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* IMask options with mask field
|
|
11
|
+
*/
|
|
12
|
+
export declare interface IMaskOptions extends Omit<InputMaskOptions, 'mask'> {
|
|
13
|
+
mask: MaskFieldType;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
declare type IMaskOptionsBase = any;
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* InputMask - A vanilla masked input component
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* ```tsx
|
|
23
|
+
* <InputMask
|
|
24
|
+
* mask="(000) 000-0000"
|
|
25
|
+
* placeholder="Phone number"
|
|
26
|
+
* onChange={(e) => console.log(e.maskedValue, e.unmaskedValue)}
|
|
27
|
+
* />
|
|
28
|
+
* ```
|
|
29
|
+
*/
|
|
30
|
+
export declare const InputMask: ForwardRefExoticComponent<MaskedInputProps & RefAttributes<HTMLInputElement>>;
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* IMask options configuration
|
|
34
|
+
*/
|
|
35
|
+
export declare type InputMaskOptions = {
|
|
36
|
+
[K in keyof IMaskOptionsBase]?: IMaskOptionsBase[K];
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
declare type InputOnChangeParam = Parameters<Exclude<React.InputHTMLAttributes<HTMLInputElement>['onChange'], undefined>>[0];
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Props for the InputMask component
|
|
43
|
+
*/
|
|
44
|
+
export declare interface MaskedInputProps extends Omit<GeneralInputProps, 'onChange' | 'value' | 'defaultValue'> {
|
|
45
|
+
/** Mask pattern or configuration */
|
|
46
|
+
mask: MaskType;
|
|
47
|
+
/** Custom character definitions for the mask */
|
|
48
|
+
definitions?: InputMaskOptions['definitions'];
|
|
49
|
+
/** Controlled value */
|
|
50
|
+
value?: string;
|
|
51
|
+
/** Default value for uncontrolled usage */
|
|
52
|
+
defaultValue?: string;
|
|
53
|
+
/** Additional IMask options */
|
|
54
|
+
maskOptions?: InputMaskOptions;
|
|
55
|
+
/** Change handler with masked and unmasked values */
|
|
56
|
+
onChange?: (event: OnChangeEvent) => void;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
declare type MaskFieldType = string | RegExp | ((...args: never) => unknown) | Date | InputMaskOptions;
|
|
60
|
+
|
|
61
|
+
declare type MaskOptionsList = Array<IMaskOptions>;
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Supported mask types
|
|
65
|
+
*/
|
|
66
|
+
export declare type MaskType = MaskFieldType | MaskOptionsList;
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Extended onChange event with masked and unmasked values
|
|
70
|
+
*/
|
|
71
|
+
export declare interface OnChangeEvent extends OnChangeParam {
|
|
72
|
+
/** The formatted value with mask applied */
|
|
73
|
+
maskedValue: string;
|
|
74
|
+
/** The raw value without mask */
|
|
75
|
+
unmaskedValue: string;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
declare type OnChangeParam = InputOnChangeParam;
|
|
79
|
+
|
|
80
|
+
export { }
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { jsx as g } from "react/jsx-runtime";
|
|
2
|
+
import O from "imask";
|
|
3
|
+
import { default as D } from "imask";
|
|
4
|
+
import { forwardRef as x, useRef as d, useState as C, useMemo as E, useCallback as R, useEffect as v } from "react";
|
|
5
|
+
const q = x(function(V, s) {
|
|
6
|
+
const {
|
|
7
|
+
mask: i,
|
|
8
|
+
maskOptions: r,
|
|
9
|
+
value: u,
|
|
10
|
+
defaultValue: h,
|
|
11
|
+
definitions: l,
|
|
12
|
+
onChange: f,
|
|
13
|
+
...y
|
|
14
|
+
} = V, m = d(null), e = d(null), a = (typeof u == "string" ? u : h) || "", [I, o] = C(a), c = E(() => ({
|
|
15
|
+
mask: i,
|
|
16
|
+
lazy: !0,
|
|
17
|
+
definitions: {
|
|
18
|
+
0: /[0-9]/,
|
|
19
|
+
...r?.definitions,
|
|
20
|
+
...l
|
|
21
|
+
},
|
|
22
|
+
...r
|
|
23
|
+
}), [i, r, l]), p = R(() => {
|
|
24
|
+
const t = m.current;
|
|
25
|
+
if (!t) return;
|
|
26
|
+
if (e.current) {
|
|
27
|
+
e.current.updateOptions(c);
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
const n = O(t, c);
|
|
31
|
+
e.current = n, n.on("accept", () => {
|
|
32
|
+
const k = n.value, M = n.unmaskedValue;
|
|
33
|
+
o(k), f?.({
|
|
34
|
+
target: t,
|
|
35
|
+
maskedValue: k,
|
|
36
|
+
unmaskedValue: M
|
|
37
|
+
});
|
|
38
|
+
}), a && (n.value = a, t.value = n.value, o(n.value));
|
|
39
|
+
}, [c, f, a]);
|
|
40
|
+
return v(() => (p(), () => {
|
|
41
|
+
e.current?.destroy(), e.current = null;
|
|
42
|
+
}), [p]), v(() => {
|
|
43
|
+
e.current && u !== void 0 && e.current.value !== u && (e.current.value = u, o(e.current.value));
|
|
44
|
+
}, [u]), /* @__PURE__ */ g("input", { ...y, ref: (t) => {
|
|
45
|
+
m.current = t, s && (typeof s == "function" ? s(t) : s.current = t);
|
|
46
|
+
}, value: I, onChange: () => {
|
|
47
|
+
} });
|
|
48
|
+
});
|
|
49
|
+
export {
|
|
50
|
+
D as IMask,
|
|
51
|
+
q as InputMask
|
|
52
|
+
};
|
|
53
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../src/InputMask.tsx"],"sourcesContent":["import IMask from 'imask';\r\nimport { forwardRef, useCallback, useEffect, useMemo, useRef, useState } from 'react';\r\nimport type { IMaskOptions, MaskedInputProps, OnChangeEvent } from './types';\r\n\r\n/**\r\n * InputMask - A vanilla masked input component\r\n * \r\n * @example\r\n * ```tsx\r\n * <InputMask\r\n * mask=\"(000) 000-0000\"\r\n * placeholder=\"Phone number\"\r\n * onChange={(e) => console.log(e.maskedValue, e.unmaskedValue)}\r\n * />\r\n * ```\r\n */\r\nconst InputMask = forwardRef<HTMLInputElement, MaskedInputProps>(function MaskedInput(props, forwardedRef) {\r\n const {\r\n mask,\r\n maskOptions: _maskOptions,\r\n value: controlledValue,\r\n defaultValue,\r\n definitions,\r\n onChange,\r\n ...defaultInputProps\r\n } = props;\r\n\r\n const inputRef = useRef<HTMLInputElement | null>(null);\r\n const maskRef = useRef<ReturnType<typeof IMask> | null>(null);\r\n\r\n const initialValue = (typeof controlledValue === 'string' ? controlledValue : defaultValue) || '';\r\n const [value, setValue] = useState(initialValue);\r\n\r\n const maskOptions = useMemo(() => {\r\n return {\r\n mask,\r\n lazy: true,\r\n definitions: {\r\n 0: /[0-9]/,\r\n ..._maskOptions?.definitions,\r\n ...definitions,\r\n },\r\n ..._maskOptions,\r\n } as IMaskOptions;\r\n }, [mask, _maskOptions, definitions]);\r\n\r\n const initMask = useCallback(() => {\r\n const el = inputRef.current;\r\n if (!el) return;\r\n\r\n if (maskRef.current) {\r\n maskRef.current.updateOptions(maskOptions as any);\r\n return;\r\n }\r\n\r\n const maskInstance = IMask(el, maskOptions as any);\r\n maskRef.current = maskInstance;\r\n\r\n maskInstance.on('accept', () => {\r\n const masked = maskInstance.value;\r\n const unmasked = maskInstance.unmaskedValue;\r\n setValue(masked);\r\n\r\n const syntheticEvent = {\r\n target: el,\r\n maskedValue: masked,\r\n unmaskedValue: unmasked,\r\n } as OnChangeEvent;\r\n onChange?.(syntheticEvent);\r\n });\r\n\r\n if (initialValue) {\r\n maskInstance.value = initialValue;\r\n el.value = maskInstance.value;\r\n setValue(maskInstance.value);\r\n }\r\n }, [maskOptions, onChange, initialValue]);\r\n\r\n useEffect(() => {\r\n initMask();\r\n return () => {\r\n maskRef.current?.destroy();\r\n maskRef.current = null;\r\n };\r\n }, [initMask]);\r\n\r\n useEffect(() => {\r\n if (maskRef.current && controlledValue !== undefined) {\r\n if (maskRef.current.value !== controlledValue) {\r\n maskRef.current.value = controlledValue;\r\n setValue(maskRef.current.value);\r\n }\r\n }\r\n }, [controlledValue]);\r\n\r\n const handleRef = (ref: HTMLInputElement | null) => {\r\n inputRef.current = ref;\r\n if (forwardedRef) {\r\n if (typeof forwardedRef === 'function') forwardedRef(ref);\r\n else forwardedRef.current = ref;\r\n }\r\n };\r\n\r\n return <input {...defaultInputProps} ref={handleRef} value={value} onChange={() => {}} />;\r\n});\r\n\r\nexport default InputMask;\r\n"],"names":["InputMask","forwardRef","props","forwardedRef","mask","_maskOptions","controlledValue","defaultValue","definitions","onChange","defaultInputProps","inputRef","useRef","maskRef","initialValue","value","setValue","useState","maskOptions","useMemo","initMask","useCallback","el","maskInstance","IMask","masked","unmasked","useEffect","jsx","ref"],"mappings":";;;;AAgBA,MAAMA,IAAYC,EAA+C,SAAqBC,GAAOC,GAAc;AACzG,QAAM;AAAA,IACJ,MAAAC;AAAA,IACA,aAAaC;AAAA,IACb,OAAOC;AAAA,IACP,cAAAC;AAAA,IACA,aAAAC;AAAA,IACA,UAAAC;AAAA,IACA,GAAGC;AAAA,EAAA,IACDR,GAEES,IAAWC,EAAgC,IAAI,GAC/CC,IAAUD,EAAwC,IAAI,GAEtDE,KAAgB,OAAOR,KAAoB,WAAWA,IAAkBC,MAAiB,IACzF,CAACQ,GAAOC,CAAQ,IAAIC,EAASH,CAAY,GAEzCI,IAAcC,EAAQ,OACnB;AAAA,IACL,MAAAf;AAAA,IACA,MAAM;AAAA,IACN,aAAa;AAAA,MACX,GAAG;AAAA,MACH,GAAGC,GAAc;AAAA,MACjB,GAAGG;AAAA,IAAA;AAAA,IAEL,GAAGH;AAAA,EAAA,IAEJ,CAACD,GAAMC,GAAcG,CAAW,CAAC,GAE9BY,IAAWC,EAAY,MAAM;AACjC,UAAMC,IAAKX,EAAS;AACpB,QAAI,CAACW,EAAI;AAET,QAAIT,EAAQ,SAAS;AACnB,MAAAA,EAAQ,QAAQ,cAAcK,CAAkB;AAChD;AAAA,IACF;AAEA,UAAMK,IAAeC,EAAMF,GAAIJ,CAAkB;AACjD,IAAAL,EAAQ,UAAUU,GAElBA,EAAa,GAAG,UAAU,MAAM;AAC9B,YAAME,IAASF,EAAa,OACtBG,IAAWH,EAAa;AAC9B,MAAAP,EAASS,CAAM,GAOfhB,IALuB;AAAA,QACrB,QAAQa;AAAA,QACR,aAAaG;AAAA,QACb,eAAeC;AAAA,MAAA,CAEQ;AAAA,IAC3B,CAAC,GAEGZ,MACFS,EAAa,QAAQT,GACrBQ,EAAG,QAAQC,EAAa,OACxBP,EAASO,EAAa,KAAK;AAAA,EAE/B,GAAG,CAACL,GAAaT,GAAUK,CAAY,CAAC;AAExC,SAAAa,EAAU,OACRP,EAAA,GACO,MAAM;AACX,IAAAP,EAAQ,SAAS,QAAA,GACjBA,EAAQ,UAAU;AAAA,EACpB,IACC,CAACO,CAAQ,CAAC,GAEbO,EAAU,MAAM;AACd,IAAId,EAAQ,WAAWP,MAAoB,UACrCO,EAAQ,QAAQ,UAAUP,MAC5BO,EAAQ,QAAQ,QAAQP,GACxBU,EAASH,EAAQ,QAAQ,KAAK;AAAA,EAGpC,GAAG,CAACP,CAAe,CAAC,GAUb,gBAAAsB,EAAC,WAAO,GAAGlB,GAAmB,KARnB,CAACmB,MAAiC;AAClD,IAAAlB,EAAS,UAAUkB,GACf1B,MACE,OAAOA,KAAiB,aAAYA,EAAa0B,CAAG,MACtC,UAAUA;AAAA,EAEhC,GAEqD,OAAAd,GAAc,UAAU,MAAM;AAAA,EAAC,GAAG;AACzF,CAAC;"}
|
package/package.json
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "mfk-mask-input",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "A vanilla React masked input component using IMask",
|
|
5
|
+
"main": "./dist/index.cjs",
|
|
6
|
+
"module": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"import": "./dist/index.js",
|
|
11
|
+
"require": "./dist/index.cjs",
|
|
12
|
+
"types": "./dist/index.d.ts"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist",
|
|
17
|
+
"README.md"
|
|
18
|
+
],
|
|
19
|
+
"scripts": {
|
|
20
|
+
"build": "tsc && vite build",
|
|
21
|
+
"prepublishOnly": "npm run build"
|
|
22
|
+
},
|
|
23
|
+
"keywords": [
|
|
24
|
+
"react",
|
|
25
|
+
"input",
|
|
26
|
+
"mask",
|
|
27
|
+
"masked-input",
|
|
28
|
+
"imask",
|
|
29
|
+
"form",
|
|
30
|
+
"component"
|
|
31
|
+
],
|
|
32
|
+
"author": "",
|
|
33
|
+
"license": "MIT",
|
|
34
|
+
"repository": {
|
|
35
|
+
"type": "git",
|
|
36
|
+
"url": ""
|
|
37
|
+
},
|
|
38
|
+
"peerDependencies": {
|
|
39
|
+
"imask": "^6.0.0 || ^7.0.0",
|
|
40
|
+
"react": "^18.0.0 || ^19.0.0",
|
|
41
|
+
"react-dom": "^18.0.0 || ^19.0.0"
|
|
42
|
+
},
|
|
43
|
+
"devDependencies": {
|
|
44
|
+
"@types/node": "^24.10.0",
|
|
45
|
+
"@types/react": "^19.2.2",
|
|
46
|
+
"@types/react-dom": "^19.2.2",
|
|
47
|
+
"@vitejs/plugin-react": "^5.1.0",
|
|
48
|
+
"typescript": "~5.9.3",
|
|
49
|
+
"vite": "^7.2.2",
|
|
50
|
+
"vite-plugin-dts": "^4.3.0"
|
|
51
|
+
}
|
|
52
|
+
}
|