form-input-fields 1.0.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/README.md +826 -0
- package/dist/controls/FormDateField.d.ts +33 -0
- package/dist/controls/FormDateField.d.ts.map +1 -0
- package/dist/controls/FormMaskField.d.ts +42 -0
- package/dist/controls/FormMaskField.d.ts.map +1 -0
- package/dist/controls/FormMaskFiledUtils.d.ts +10 -0
- package/dist/controls/FormMaskFiledUtils.d.ts.map +1 -0
- package/dist/controls/date.d.ts +13 -0
- package/dist/controls/date.d.ts.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.es.js +27769 -0
- package/dist/index.es.js.map +1 -0
- package/dist/index.js +228 -0
- package/dist/index.js.map +1 -0
- package/dist/test/FormDateField.spec.d.ts +2 -0
- package/dist/test/FormDateField.spec.d.ts.map +1 -0
- package/dist/test/FormMaskField.spec.d.ts +2 -0
- package/dist/test/FormMaskField.spec.d.ts.map +1 -0
- package/dist/test/FormMaskFiledUtils.spec.d.ts +2 -0
- package/dist/test/FormMaskFiledUtils.spec.d.ts.map +1 -0
- package/dist/test/setup.d.ts +1 -0
- package/dist/test/setup.d.ts.map +1 -0
- package/package.json +94 -0
package/README.md
ADDED
|
@@ -0,0 +1,826 @@
|
|
|
1
|
+
# Form Fields Components
|
|
2
|
+
|
|
3
|
+
A collection of Material-UI form field components with Formik integration.
|
|
4
|
+
|
|
5
|
+
## FormDateField
|
|
6
|
+
|
|
7
|
+
A date input field component with Material-UI and Formik integration, powered by MUI X Date Pickers.
|
|
8
|
+
|
|
9
|
+
### Features
|
|
10
|
+
|
|
11
|
+
- **Material-UI Integration**: Consistent styling with other form components
|
|
12
|
+
- **Formik Integration**: Seamless form state management with error handling
|
|
13
|
+
- **Day.js Support**: Lightweight date library for date manipulation
|
|
14
|
+
- **Custom Date Formatting**: Flexible date display formats
|
|
15
|
+
- **Date Validation**: Built-in min/max date validation
|
|
16
|
+
- **Read-Only Support**: Can be configured as read-only
|
|
17
|
+
- **TypeScript Support**: Full type safety with exported interfaces
|
|
18
|
+
- **Helper Text Display**: Show date format or custom helper text
|
|
19
|
+
- **Today Button**: Optional today button in date picker
|
|
20
|
+
|
|
21
|
+
### Available Date Formats
|
|
22
|
+
|
|
23
|
+
The component uses predefined date format constants from `date.ts`:
|
|
24
|
+
|
|
25
|
+
#### FORM_DATE_FORMAT
|
|
26
|
+
|
|
27
|
+
```typescript
|
|
28
|
+
export const FORM_DATE_FORMAT = {
|
|
29
|
+
short: "YYYY-MM-DD",
|
|
30
|
+
long: "MM/DD/YYYY hh:mm A",
|
|
31
|
+
custom: "DD MMMM YYYY hh:mm A",
|
|
32
|
+
};
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
#### DATE_PICKER_DATE_FORMAT
|
|
36
|
+
|
|
37
|
+
```typescript
|
|
38
|
+
export const DATE_PICKER_DATE_FORMAT = {
|
|
39
|
+
short: "DD/MM/YYYY",
|
|
40
|
+
};
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
#### DATE_PICKER_MONTH_YEAR_FORMAT
|
|
44
|
+
|
|
45
|
+
```typescript
|
|
46
|
+
export const DATE_PICKER_MONTH_YEAR_FORMAT = {
|
|
47
|
+
short: "MM/YYYY",
|
|
48
|
+
long: "MMMM YYYY",
|
|
49
|
+
};
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### Importing Date Constants
|
|
53
|
+
|
|
54
|
+
```tsx
|
|
55
|
+
import {
|
|
56
|
+
FORM_DATE_FORMAT,
|
|
57
|
+
DATE_PICKER_DATE_FORMAT,
|
|
58
|
+
DATE_PICKER_MONTH_YEAR_FORMAT
|
|
59
|
+
} from "form-input-mask-field/date";
|
|
60
|
+
|
|
61
|
+
// Use predefined formats
|
|
62
|
+
<Field
|
|
63
|
+
component={FormDateField}
|
|
64
|
+
name="birthDate"
|
|
65
|
+
format={FORM_DATE_FORMAT.short} // 'YYYY-MM-DD'
|
|
66
|
+
/>
|
|
67
|
+
|
|
68
|
+
// Or use custom format
|
|
69
|
+
<Field
|
|
70
|
+
component={FormDateField}
|
|
71
|
+
name="eventDate"
|
|
72
|
+
format="DD/MM/YYYY HH:mm" // Custom format
|
|
73
|
+
/>
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### Usage with Formik
|
|
77
|
+
|
|
78
|
+
```tsx
|
|
79
|
+
import { Formik, Field } from "formik";
|
|
80
|
+
import { FormDateField, FormDateFieldProps } from "form-input-mask-field";
|
|
81
|
+
import { FORM_DATE_FORMAT } from "form-input-mask-field/date";
|
|
82
|
+
import dayjs from "dayjs";
|
|
83
|
+
|
|
84
|
+
<Formik
|
|
85
|
+
initialValues={{ birthDate: null, appointmentDate: null }}
|
|
86
|
+
onSubmit={(values) => console.log(values)}
|
|
87
|
+
>
|
|
88
|
+
{/* Basic usage */}
|
|
89
|
+
<Field
|
|
90
|
+
component={FormDateField}
|
|
91
|
+
name="birthDate"
|
|
92
|
+
label="Birth Date"
|
|
93
|
+
format={FORM_DATE_FORMAT.short}
|
|
94
|
+
/>
|
|
95
|
+
|
|
96
|
+
{/* Advanced usage with validation */}
|
|
97
|
+
<Field
|
|
98
|
+
component={FormDateField}
|
|
99
|
+
name="appointmentDate"
|
|
100
|
+
label="Appointment Date"
|
|
101
|
+
format="DD/MM/YYYY"
|
|
102
|
+
minDate={dayjs()}
|
|
103
|
+
disablePast={true}
|
|
104
|
+
showTodayButton={true}
|
|
105
|
+
showDateFormat={true}
|
|
106
|
+
onChange={(value, context) => {
|
|
107
|
+
console.log("Selected date:", value);
|
|
108
|
+
console.log("Formatted value:", context.formattedValue);
|
|
109
|
+
}}
|
|
110
|
+
/>
|
|
111
|
+
</Formik>;
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### Props
|
|
115
|
+
|
|
116
|
+
The FormDateField component accepts all props from `FormDateFieldProps`, `FieldProps`, and `TextFieldProps`.
|
|
117
|
+
|
|
118
|
+
#### FormDateFieldProps Interface
|
|
119
|
+
|
|
120
|
+
| Prop | Type | Default | Description |
|
|
121
|
+
| ----------------- | ---------- | ------------------------ | --------------------------------------------- |
|
|
122
|
+
| `format` | `string` | `FORM_DATE_FORMAT.short` | Date format string using dayjs format tokens |
|
|
123
|
+
| `minDate` | `Dayjs` | - | Minimum selectable date |
|
|
124
|
+
| `maxDate` | `Dayjs` | - | Maximum selectable date |
|
|
125
|
+
| `disablePast` | `boolean` | `false` | Disable past dates |
|
|
126
|
+
| `disableFuture` | `boolean` | `false` | Disable future dates |
|
|
127
|
+
| `showTodayButton` | `boolean` | `false` | Show today button in the picker |
|
|
128
|
+
| `readOnly` | `boolean` | `false` | Make the field read-only |
|
|
129
|
+
| `showDateFormat` | `boolean` | `false` | Show the date format as helper text |
|
|
130
|
+
| `onChange` | `function` | - | Custom change handler with additional context |
|
|
131
|
+
|
|
132
|
+
#### Common Props (from FieldProps & TextFieldProps)
|
|
133
|
+
|
|
134
|
+
| Prop | Type | Required | Description |
|
|
135
|
+
| ------------ | ---------------- | -------- | --------------------------------------------- |
|
|
136
|
+
| `name` | `string` | Yes | Field name in Formik values |
|
|
137
|
+
| `label` | `string` | No | Field label |
|
|
138
|
+
| `helperText` | `string` | No | Custom helper text |
|
|
139
|
+
| `error` | `boolean` | No | Error state |
|
|
140
|
+
| `disabled` | `boolean` | No | Disabled state |
|
|
141
|
+
| Other | `TextFieldProps` | No | All Material-UI TextField props are supported |
|
|
142
|
+
|
|
143
|
+
### Examples
|
|
144
|
+
|
|
145
|
+
#### Basic Date Input
|
|
146
|
+
|
|
147
|
+
```tsx
|
|
148
|
+
<Field
|
|
149
|
+
component={FormDateField}
|
|
150
|
+
name="birthDate"
|
|
151
|
+
label="Birth Date"
|
|
152
|
+
format="DD/MM/YYYY"
|
|
153
|
+
/>
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
#### Date with Validation
|
|
157
|
+
|
|
158
|
+
```tsx
|
|
159
|
+
<Field
|
|
160
|
+
component={FormDateField}
|
|
161
|
+
name="startDate"
|
|
162
|
+
label="Start Date"
|
|
163
|
+
minDate={dayjs()}
|
|
164
|
+
disablePast={true}
|
|
165
|
+
showDateFormat={true}
|
|
166
|
+
helperText="Select a future date"
|
|
167
|
+
/>
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
#### Appointment Scheduler
|
|
171
|
+
|
|
172
|
+
```tsx
|
|
173
|
+
<Field
|
|
174
|
+
component={FormDateField}
|
|
175
|
+
name="appointmentDate"
|
|
176
|
+
label="Appointment Date"
|
|
177
|
+
format="DD MMMM YYYY"
|
|
178
|
+
minDate={dayjs().add(1, "day")}
|
|
179
|
+
maxDate={dayjs().add(3, "month")}
|
|
180
|
+
showTodayButton={true}
|
|
181
|
+
showDateFormat={true}
|
|
182
|
+
/>
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
#### Read-Only Date Display
|
|
186
|
+
|
|
187
|
+
```tsx
|
|
188
|
+
<Field
|
|
189
|
+
component={FormDateField}
|
|
190
|
+
name="createdDate"
|
|
191
|
+
label="Created Date"
|
|
192
|
+
format="DD/MM/YYYY HH:mm"
|
|
193
|
+
readOnly={true}
|
|
194
|
+
/>
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
#### Custom Change Handler
|
|
198
|
+
|
|
199
|
+
```tsx
|
|
200
|
+
<Field
|
|
201
|
+
component={FormDateField}
|
|
202
|
+
name="eventDate"
|
|
203
|
+
label="Event Date"
|
|
204
|
+
format="YYYY-MM-DD"
|
|
205
|
+
onChange={(value, context) => {
|
|
206
|
+
console.log("Selected date:", value);
|
|
207
|
+
console.log("Formatted value:", context.formattedValue);
|
|
208
|
+
console.log("Validation error:", context.validationError);
|
|
209
|
+
|
|
210
|
+
// Custom logic here
|
|
211
|
+
if (value && value.day() === 0) {
|
|
212
|
+
alert("Events cannot be scheduled on Sundays");
|
|
213
|
+
}
|
|
214
|
+
}}
|
|
215
|
+
/>
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
#### Date Range (Start/End Dates)
|
|
219
|
+
|
|
220
|
+
```tsx
|
|
221
|
+
<Field
|
|
222
|
+
component={FormDateField}
|
|
223
|
+
name="startDate"
|
|
224
|
+
label="Start Date"
|
|
225
|
+
format="DD/MM/YYYY"
|
|
226
|
+
maxDate={values.endDate ? dayjs(values.endDate) : undefined}
|
|
227
|
+
/>
|
|
228
|
+
|
|
229
|
+
<Field
|
|
230
|
+
component={FormDateField}
|
|
231
|
+
name="endDate"
|
|
232
|
+
label="End Date"
|
|
233
|
+
format="DD/MM/YYYY"
|
|
234
|
+
minDate={values.startDate ? dayjs(values.startDate) : undefined}
|
|
235
|
+
/>
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
## FormMaskField
|
|
239
|
+
|
|
240
|
+
A flexible form field component with advanced text masking capabilities.
|
|
241
|
+
|
|
242
|
+
### Features
|
|
243
|
+
|
|
244
|
+
- **Material-UI Integration**: Consistent styling with other form components
|
|
245
|
+
- **Formik Integration**: Seamless form state management
|
|
246
|
+
- **Flexible Masking**: Pattern-based input masking with multiple character types
|
|
247
|
+
- **Uppercase Conversion**: Automatic text transformation
|
|
248
|
+
- **TypeScript Support**: Full type safety and IntelliSense
|
|
249
|
+
- **Clean Value Option**: Return masked or unmasked values to form state
|
|
250
|
+
|
|
251
|
+
### Usage with Formik
|
|
252
|
+
|
|
253
|
+
```tsx
|
|
254
|
+
import { Formik, Field } from "formik";
|
|
255
|
+
import { FormMaskField } from "form-input-mask-field";
|
|
256
|
+
|
|
257
|
+
<Formik
|
|
258
|
+
initialValues={{ phoneNumber: "", gameCode: "" }}
|
|
259
|
+
onSubmit={(values) => console.log(values)}
|
|
260
|
+
>
|
|
261
|
+
<Field
|
|
262
|
+
component={FormMaskField}
|
|
263
|
+
name="phoneNumber"
|
|
264
|
+
label="Phone Number"
|
|
265
|
+
mask="(999) 999-9999"
|
|
266
|
+
placeholder="Enter phone number"
|
|
267
|
+
/>
|
|
268
|
+
|
|
269
|
+
<Field
|
|
270
|
+
component={FormMaskField}
|
|
271
|
+
name="gameCode"
|
|
272
|
+
label="Game Code"
|
|
273
|
+
mask="AAAAA"
|
|
274
|
+
toUpperCase={true}
|
|
275
|
+
returnCleanValue={true}
|
|
276
|
+
/>
|
|
277
|
+
</Formik>;
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
### Mask Pattern Characters
|
|
281
|
+
|
|
282
|
+
| Character | Description | Example |
|
|
283
|
+
| --------- | ---------------------------- | ------------------------ |
|
|
284
|
+
| `9` | Digit (0-9) | `999-99-9999` for SSN |
|
|
285
|
+
| `A` | Letter (a-z, A-Z) | `AAA` for country code |
|
|
286
|
+
| `*` | Alphanumeric (a-z, A-Z, 0-9) | `***-***` for mixed code |
|
|
287
|
+
| `a` | Lowercase letter (a-z) | `aaa` for lowercase only |
|
|
288
|
+
| `Z` | Uppercase letter (A-Z) | `ZZZ` for uppercase only |
|
|
289
|
+
| `#` | Hexadecimal (0-9, A-F, a-f) | `######` for hex color |
|
|
290
|
+
| Any other | Literal character | `-`, `(`, `)`, `/`, etc. |
|
|
291
|
+
|
|
292
|
+
### Props
|
|
293
|
+
|
|
294
|
+
| Prop | Type | Default | Description |
|
|
295
|
+
| ------------------ | ---------- | ------- | -------------------------------------------------------------- |
|
|
296
|
+
| `mask` | `string` | - | Mask pattern using the characters above |
|
|
297
|
+
| `placeholderChar` | `string` | `'_'` | Character shown in placeholder for mask positions |
|
|
298
|
+
| `toUpperCase` | `boolean` | `false` | Convert input to uppercase automatically |
|
|
299
|
+
| `returnCleanValue` | `boolean` | `false` | Return unmasked value to Formik (true) or masked value (false) |
|
|
300
|
+
| `showMaskPattern` | `boolean` | `false` | Show the mask pattern as helper text below the input field |
|
|
301
|
+
| `showPlaceholder` | `boolean` | `false` | Show placeholder text with mask pattern in the input field |
|
|
302
|
+
| `onChange` | `function` | - | Custom change handler with masked, clean, and raw values |
|
|
303
|
+
|
|
304
|
+
Plus all standard Material-UI TextField props and Formik FieldProps.
|
|
305
|
+
|
|
306
|
+
### Examples
|
|
307
|
+
|
|
308
|
+
#### Phone Number
|
|
309
|
+
|
|
310
|
+
```tsx
|
|
311
|
+
<Field
|
|
312
|
+
component={FormMaskField}
|
|
313
|
+
name="phone"
|
|
314
|
+
label="Phone Number"
|
|
315
|
+
mask="(999) 999-9999"
|
|
316
|
+
placeholder="(555) 123-4567"
|
|
317
|
+
/>
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
#### Date Input
|
|
321
|
+
|
|
322
|
+
```tsx
|
|
323
|
+
<Field
|
|
324
|
+
component={FormMaskField}
|
|
325
|
+
name="date"
|
|
326
|
+
label="Date"
|
|
327
|
+
mask="99/99/9999"
|
|
328
|
+
placeholder="MM/DD/YYYY"
|
|
329
|
+
/>
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
#### Product Code (Uppercase)
|
|
333
|
+
|
|
334
|
+
```tsx
|
|
335
|
+
<Field
|
|
336
|
+
component={FormMaskField}
|
|
337
|
+
name="productCode"
|
|
338
|
+
label="Product Code"
|
|
339
|
+
mask="AAA-999-AAA"
|
|
340
|
+
toUpperCase={true}
|
|
341
|
+
returnCleanValue={true}
|
|
342
|
+
/>
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
#### Credit Card
|
|
346
|
+
|
|
347
|
+
```tsx
|
|
348
|
+
<Field
|
|
349
|
+
component={FormMaskField}
|
|
350
|
+
name="creditCard"
|
|
351
|
+
label="Credit Card"
|
|
352
|
+
mask="9999 9999 9999 9999"
|
|
353
|
+
placeholder="1234 5678 9012 3456"
|
|
354
|
+
/>
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
#### Custom Change Handler
|
|
358
|
+
|
|
359
|
+
```tsx
|
|
360
|
+
<Field
|
|
361
|
+
component={FormMaskField}
|
|
362
|
+
name="customField"
|
|
363
|
+
label="Custom Field"
|
|
364
|
+
mask="999-AAA"
|
|
365
|
+
onChange={(event) => {
|
|
366
|
+
console.log("Masked value:", event.target.value);
|
|
367
|
+
console.log("Clean value:", event.target.cleanValue);
|
|
368
|
+
console.log("Raw input:", event.target.rawValue);
|
|
369
|
+
}}
|
|
370
|
+
/>
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
#### Show Mask Pattern
|
|
374
|
+
|
|
375
|
+
```tsx
|
|
376
|
+
<Field
|
|
377
|
+
component={FormMaskField}
|
|
378
|
+
name="gameCode"
|
|
379
|
+
label="Game Code"
|
|
380
|
+
mask="AAA-999"
|
|
381
|
+
showMaskPattern={true}
|
|
382
|
+
toUpperCase={true}
|
|
383
|
+
/>
|
|
384
|
+
// This will show "Pattern: AAA-999" as helper text below the input
|
|
385
|
+
```
|
|
386
|
+
|
|
387
|
+
#### Show Placeholder
|
|
388
|
+
|
|
389
|
+
```tsx
|
|
390
|
+
<Field
|
|
391
|
+
component={FormMaskField}
|
|
392
|
+
name="phoneNumber"
|
|
393
|
+
label="Phone Number"
|
|
394
|
+
mask="(999) 999-9999"
|
|
395
|
+
showPlaceholder={true}
|
|
396
|
+
/>
|
|
397
|
+
// This will show "(___) ___-____" as placeholder text in the input field
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
## Complete Example Application
|
|
401
|
+
|
|
402
|
+
Here's a comprehensive example showing how to build a complete form application using FormMaskField with Material-UI and Formik:
|
|
403
|
+
|
|
404
|
+
### App.tsx
|
|
405
|
+
|
|
406
|
+
```tsx
|
|
407
|
+
import React from "react";
|
|
408
|
+
import { Formik, Form, Field, FormikHelpers, FormikErrors } from "formik";
|
|
409
|
+
import Container from "@mui/material/Container";
|
|
410
|
+
import Typography from "@mui/material/Typography";
|
|
411
|
+
import Box from "@mui/material/Box";
|
|
412
|
+
import Button from "@mui/material/Button";
|
|
413
|
+
import { Grid } from "@mui/material";
|
|
414
|
+
import Paper from "@mui/material/Paper";
|
|
415
|
+
import Alert from "@mui/material/Alert";
|
|
416
|
+
import { ThemeProvider, createTheme } from "@mui/material/styles";
|
|
417
|
+
import CssBaseline from "@mui/material/CssBaseline";
|
|
418
|
+
import { FormMaskField } from "form-input-mask-field";
|
|
419
|
+
import "./App.css";
|
|
420
|
+
|
|
421
|
+
interface FormValues {
|
|
422
|
+
phone: string;
|
|
423
|
+
date: string;
|
|
424
|
+
creditCard: string;
|
|
425
|
+
licensePlate: string;
|
|
426
|
+
hexColor: string;
|
|
427
|
+
customCode: string;
|
|
428
|
+
socialSecurity: string;
|
|
429
|
+
postalCode: string;
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
interface FormFieldEvent
|
|
433
|
+
extends Omit<React.ChangeEvent<HTMLInputElement>, "target"> {
|
|
434
|
+
target: HTMLInputElement & {
|
|
435
|
+
value: string;
|
|
436
|
+
cleanValue: string;
|
|
437
|
+
rawValue: string;
|
|
438
|
+
name: string;
|
|
439
|
+
};
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
// Create a Material-UI theme
|
|
443
|
+
const theme = createTheme({
|
|
444
|
+
palette: {
|
|
445
|
+
primary: {
|
|
446
|
+
main: "#1976d2",
|
|
447
|
+
},
|
|
448
|
+
secondary: {
|
|
449
|
+
main: "#dc004e",
|
|
450
|
+
},
|
|
451
|
+
},
|
|
452
|
+
});
|
|
453
|
+
|
|
454
|
+
// Validation schema
|
|
455
|
+
const validateForm = (values: FormValues): FormikErrors<FormValues> => {
|
|
456
|
+
const errors: Partial<FormValues> = {};
|
|
457
|
+
|
|
458
|
+
if (!values.phone) {
|
|
459
|
+
errors.phone = "Phone number is required";
|
|
460
|
+
} else if (values.phone.replace(/\D/g, "").length < 10) {
|
|
461
|
+
errors.phone = "Phone number must be 10 digits";
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
if (!values.date) {
|
|
465
|
+
errors.date = "Date is required";
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
if (!values.creditCard) {
|
|
469
|
+
errors.creditCard = "Credit card is required";
|
|
470
|
+
} else if (values.creditCard.replace(/\D/g, "").length < 16) {
|
|
471
|
+
errors.creditCard = "Credit card must be 16 digits";
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
if (!values.licensePlate) {
|
|
475
|
+
errors.licensePlate = "License plate is required";
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
return errors;
|
|
479
|
+
};
|
|
480
|
+
|
|
481
|
+
function App() {
|
|
482
|
+
const initialValues: FormValues = {
|
|
483
|
+
phone: "",
|
|
484
|
+
date: "",
|
|
485
|
+
creditCard: "",
|
|
486
|
+
licensePlate: "",
|
|
487
|
+
hexColor: "",
|
|
488
|
+
customCode: "",
|
|
489
|
+
socialSecurity: "",
|
|
490
|
+
postalCode: "",
|
|
491
|
+
};
|
|
492
|
+
|
|
493
|
+
const handleSubmit = (
|
|
494
|
+
values: FormValues,
|
|
495
|
+
{ setSubmitting }: FormikHelpers<FormValues>
|
|
496
|
+
) => {
|
|
497
|
+
console.log("Form Values:", values);
|
|
498
|
+
setTimeout(() => {
|
|
499
|
+
alert("Form submitted! Check console for values.");
|
|
500
|
+
setSubmitting(false);
|
|
501
|
+
}, 400);
|
|
502
|
+
};
|
|
503
|
+
|
|
504
|
+
const handleCustomChange =
|
|
505
|
+
(fieldName: keyof FormValues) => (event: FormFieldEvent) => {
|
|
506
|
+
console.log(`${fieldName} changed:`, {
|
|
507
|
+
masked: event.target.value,
|
|
508
|
+
clean: event.target.cleanValue,
|
|
509
|
+
raw: event.target.rawValue,
|
|
510
|
+
});
|
|
511
|
+
return event;
|
|
512
|
+
};
|
|
513
|
+
|
|
514
|
+
return (
|
|
515
|
+
<ThemeProvider theme={theme}>
|
|
516
|
+
<CssBaseline />
|
|
517
|
+
<Container maxWidth="lg" sx={{ py: 4 }}>
|
|
518
|
+
<Typography variant="h3" component="h1" gutterBottom align="center">
|
|
519
|
+
FormMaskField Demo
|
|
520
|
+
</Typography>
|
|
521
|
+
|
|
522
|
+
<Typography
|
|
523
|
+
variant="subtitle1"
|
|
524
|
+
align="center"
|
|
525
|
+
color="text.secondary"
|
|
526
|
+
paragraph
|
|
527
|
+
>
|
|
528
|
+
A comprehensive example showcasing the FormMaskField component with
|
|
529
|
+
various mask patterns and configurations
|
|
530
|
+
</Typography>
|
|
531
|
+
|
|
532
|
+
<Formik
|
|
533
|
+
initialValues={initialValues}
|
|
534
|
+
validate={validateForm}
|
|
535
|
+
onSubmit={handleSubmit}
|
|
536
|
+
>
|
|
537
|
+
{({ values, isSubmitting, errors, touched }) => (
|
|
538
|
+
<Form>
|
|
539
|
+
<Grid container spacing={3}>
|
|
540
|
+
{/* Basic Masks Section */}
|
|
541
|
+
<Grid>
|
|
542
|
+
<Paper elevation={2} sx={{ p: 3 }}>
|
|
543
|
+
<Typography variant="h5" gutterBottom>
|
|
544
|
+
Basic Input Masks
|
|
545
|
+
</Typography>
|
|
546
|
+
|
|
547
|
+
<Grid container spacing={2}>
|
|
548
|
+
<Grid>
|
|
549
|
+
<Field
|
|
550
|
+
name="phone"
|
|
551
|
+
component={FormMaskField}
|
|
552
|
+
label="Phone Number"
|
|
553
|
+
mask="(999) 999-9999"
|
|
554
|
+
showMaskPattern={true}
|
|
555
|
+
showPlaceholder={true}
|
|
556
|
+
helperText="US phone number format"
|
|
557
|
+
onChange={handleCustomChange("phone")}
|
|
558
|
+
/>
|
|
559
|
+
</Grid>
|
|
560
|
+
|
|
561
|
+
<Grid>
|
|
562
|
+
<Field
|
|
563
|
+
name="date"
|
|
564
|
+
component={FormMaskField}
|
|
565
|
+
label="Date"
|
|
566
|
+
mask="99/99/9999"
|
|
567
|
+
showMaskPattern={true}
|
|
568
|
+
showPlaceholder={true}
|
|
569
|
+
helperText="MM/DD/YYYY format"
|
|
570
|
+
/>
|
|
571
|
+
</Grid>
|
|
572
|
+
|
|
573
|
+
<Grid>
|
|
574
|
+
<Field
|
|
575
|
+
name="socialSecurity"
|
|
576
|
+
component={FormMaskField}
|
|
577
|
+
label="Social Security Number"
|
|
578
|
+
mask="999-99-9999"
|
|
579
|
+
showMaskPattern={true}
|
|
580
|
+
showPlaceholder={true}
|
|
581
|
+
returnCleanValue={true}
|
|
582
|
+
helperText="Clean value returned (no dashes)"
|
|
583
|
+
/>
|
|
584
|
+
</Grid>
|
|
585
|
+
|
|
586
|
+
<Grid>
|
|
587
|
+
<Field
|
|
588
|
+
name="postalCode"
|
|
589
|
+
component={FormMaskField}
|
|
590
|
+
label="ZIP Code"
|
|
591
|
+
mask="99999-9999"
|
|
592
|
+
showMaskPattern={true}
|
|
593
|
+
showPlaceholder={true}
|
|
594
|
+
helperText="US ZIP+4 format"
|
|
595
|
+
/>
|
|
596
|
+
</Grid>
|
|
597
|
+
</Grid>
|
|
598
|
+
</Paper>
|
|
599
|
+
</Grid>
|
|
600
|
+
|
|
601
|
+
{/* Advanced Masks Section */}
|
|
602
|
+
<Grid>
|
|
603
|
+
<Paper elevation={2} sx={{ p: 3 }}>
|
|
604
|
+
<Typography variant="h5" gutterBottom>
|
|
605
|
+
Advanced Input Masks
|
|
606
|
+
</Typography>
|
|
607
|
+
|
|
608
|
+
<Grid container spacing={2}>
|
|
609
|
+
<Grid>
|
|
610
|
+
<Field
|
|
611
|
+
name="creditCard"
|
|
612
|
+
component={FormMaskField}
|
|
613
|
+
label="Credit Card Number"
|
|
614
|
+
mask="9999-9999-9999-9999"
|
|
615
|
+
showMaskPattern={true}
|
|
616
|
+
showPlaceholder={true}
|
|
617
|
+
helperText="16-digit credit card number"
|
|
618
|
+
onChange={handleCustomChange("creditCard")}
|
|
619
|
+
/>
|
|
620
|
+
</Grid>
|
|
621
|
+
|
|
622
|
+
<Grid>
|
|
623
|
+
<Field
|
|
624
|
+
name="licensePlate"
|
|
625
|
+
component={FormMaskField}
|
|
626
|
+
label="License Plate"
|
|
627
|
+
mask="AAA-999"
|
|
628
|
+
toUpperCase={true}
|
|
629
|
+
showMaskPattern={true}
|
|
630
|
+
showPlaceholder={true}
|
|
631
|
+
helperText="3 letters + 3 numbers (auto uppercase)"
|
|
632
|
+
onChange={handleCustomChange("licensePlate")}
|
|
633
|
+
/>
|
|
634
|
+
</Grid>
|
|
635
|
+
|
|
636
|
+
<Grid>
|
|
637
|
+
<Field
|
|
638
|
+
name="hexColor"
|
|
639
|
+
component={FormMaskField}
|
|
640
|
+
label="Hex Color Code"
|
|
641
|
+
mask="#######"
|
|
642
|
+
toUpperCase={true}
|
|
643
|
+
showMaskPattern={true}
|
|
644
|
+
showPlaceholder={true}
|
|
645
|
+
placeholderChar="0"
|
|
646
|
+
helperText="6-digit hex color code"
|
|
647
|
+
/>
|
|
648
|
+
</Grid>
|
|
649
|
+
|
|
650
|
+
<Grid>
|
|
651
|
+
<Field
|
|
652
|
+
name="customCode"
|
|
653
|
+
component={FormMaskField}
|
|
654
|
+
label="Custom Code"
|
|
655
|
+
mask="**-999-AA"
|
|
656
|
+
toUpperCase={true}
|
|
657
|
+
returnCleanValue={true}
|
|
658
|
+
showMaskPattern={true}
|
|
659
|
+
showPlaceholder={true}
|
|
660
|
+
helperText="Alphanumeric + digits + letters"
|
|
661
|
+
onChange={handleCustomChange("customCode")}
|
|
662
|
+
/>
|
|
663
|
+
</Grid>
|
|
664
|
+
</Grid>
|
|
665
|
+
</Paper>
|
|
666
|
+
</Grid>
|
|
667
|
+
|
|
668
|
+
{/* Current Values Display */}
|
|
669
|
+
<Grid>
|
|
670
|
+
<Paper elevation={2} sx={{ p: 3 }}>
|
|
671
|
+
<Typography variant="h5" gutterBottom>
|
|
672
|
+
Current Form Values
|
|
673
|
+
</Typography>
|
|
674
|
+
|
|
675
|
+
<Box sx={{ mt: 2 }}>
|
|
676
|
+
<Typography
|
|
677
|
+
variant="body2"
|
|
678
|
+
component="pre"
|
|
679
|
+
sx={{
|
|
680
|
+
backgroundColor: "#f5f5f5",
|
|
681
|
+
p: 2,
|
|
682
|
+
borderRadius: 1,
|
|
683
|
+
overflow: "auto",
|
|
684
|
+
fontSize: "0.875rem",
|
|
685
|
+
}}
|
|
686
|
+
>
|
|
687
|
+
{JSON.stringify(values, null, 2)}
|
|
688
|
+
</Typography>
|
|
689
|
+
</Box>
|
|
690
|
+
</Paper>
|
|
691
|
+
</Grid>
|
|
692
|
+
|
|
693
|
+
{/* Form Errors Display */}
|
|
694
|
+
{Object.keys(errors).length > 0 &&
|
|
695
|
+
Object.keys(touched).length > 0 && (
|
|
696
|
+
<Grid>
|
|
697
|
+
<Alert severity="error">
|
|
698
|
+
<Typography variant="h6" gutterBottom>
|
|
699
|
+
Form Validation Errors:
|
|
700
|
+
</Typography>
|
|
701
|
+
<ul>
|
|
702
|
+
{Object.entries(errors).map(
|
|
703
|
+
([field, error]) =>
|
|
704
|
+
touched[field] && (
|
|
705
|
+
<li key={field}>
|
|
706
|
+
<strong>{field}:</strong> {error}
|
|
707
|
+
</li>
|
|
708
|
+
)
|
|
709
|
+
)}
|
|
710
|
+
</ul>
|
|
711
|
+
</Alert>
|
|
712
|
+
</Grid>
|
|
713
|
+
)}
|
|
714
|
+
|
|
715
|
+
{/* Submit Button */}
|
|
716
|
+
<Grid>
|
|
717
|
+
<Box
|
|
718
|
+
sx={{ display: "flex", justifyContent: "center", mt: 2 }}
|
|
719
|
+
>
|
|
720
|
+
<Button
|
|
721
|
+
type="submit"
|
|
722
|
+
variant="contained"
|
|
723
|
+
size="large"
|
|
724
|
+
disabled={isSubmitting}
|
|
725
|
+
sx={{ minWidth: 200 }}
|
|
726
|
+
>
|
|
727
|
+
{isSubmitting ? "Submitting..." : "Submit Form"}
|
|
728
|
+
</Button>
|
|
729
|
+
</Box>
|
|
730
|
+
</Grid>
|
|
731
|
+
</Grid>
|
|
732
|
+
</Form>
|
|
733
|
+
)}
|
|
734
|
+
</Formik>
|
|
735
|
+
</Container>
|
|
736
|
+
</ThemeProvider>
|
|
737
|
+
);
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
export default App;
|
|
741
|
+
```
|
|
742
|
+
|
|
743
|
+
### App.css
|
|
744
|
+
|
|
745
|
+
```css
|
|
746
|
+
#root {
|
|
747
|
+
max-width: 1280px;
|
|
748
|
+
margin: 0 auto;
|
|
749
|
+
padding: 2rem;
|
|
750
|
+
text-align: center;
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
.logo {
|
|
754
|
+
height: 6em;
|
|
755
|
+
padding: 1.5em;
|
|
756
|
+
will-change: filter;
|
|
757
|
+
transition: filter 300ms;
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
.logo:hover {
|
|
761
|
+
filter: drop-shadow(0 0 2em #646cffaa);
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
.logo.react:hover {
|
|
765
|
+
filter: drop-shadow(0 0 2em #61dafbaa);
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
@keyframes logo-spin {
|
|
769
|
+
from {
|
|
770
|
+
transform: rotate(0deg);
|
|
771
|
+
}
|
|
772
|
+
to {
|
|
773
|
+
transform: rotate(360deg);
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
778
|
+
a:nth-of-type(2) .logo {
|
|
779
|
+
animation: logo-spin infinite 20s linear;
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
.card {
|
|
784
|
+
padding: 2em;
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
.read-the-docs {
|
|
788
|
+
color: #888;
|
|
789
|
+
}
|
|
790
|
+
```
|
|
791
|
+
|
|
792
|
+
### Key Features Demonstrated
|
|
793
|
+
|
|
794
|
+
This example application showcases:
|
|
795
|
+
|
|
796
|
+
1. **Complete Form Integration**: Full Formik integration with validation and error handling
|
|
797
|
+
2. **Multiple Mask Types**: Phone numbers, dates, credit cards, license plates, hex colors, and custom codes
|
|
798
|
+
3. **Advanced Features**:
|
|
799
|
+
- Uppercase conversion (`toUpperCase`)
|
|
800
|
+
- Clean value return (`returnCleanValue`)
|
|
801
|
+
- Custom placeholder characters (`placeholderChar`)
|
|
802
|
+
- Pattern display (`showMaskPattern`)
|
|
803
|
+
- Placeholder display (`showPlaceholder`)
|
|
804
|
+
4. **Custom Event Handling**: onChange handlers that provide masked, clean, and raw values
|
|
805
|
+
5. **Material-UI Integration**: Consistent styling with Material-UI theme and components
|
|
806
|
+
6. **Real-time Validation**: Form validation with error display
|
|
807
|
+
7. **Live Value Display**: Real-time display of current form values
|
|
808
|
+
8. **Responsive Layout**: Grid-based responsive layout
|
|
809
|
+
|
|
810
|
+
### Running the Example
|
|
811
|
+
|
|
812
|
+
To run this example:
|
|
813
|
+
|
|
814
|
+
1. Install dependencies: `npm install`
|
|
815
|
+
2. Start the development server: `npm run dev`
|
|
816
|
+
3. Open your browser and navigate to the local development URL
|
|
817
|
+
4. Interact with the form fields to see the masking in action
|
|
818
|
+
5. Open browser console to see custom onChange event data
|
|
819
|
+
|
|
820
|
+
The example demonstrates how FormMaskField seamlessly integrates with existing Material-UI and Formik workflows while providing powerful input masking capabilities.
|
|
821
|
+
|
|
822
|
+
## Support
|
|
823
|
+
|
|
824
|
+
If you like my work, you can support me here:
|
|
825
|
+
|
|
826
|
+
[](https://coff.ee/vavanv)
|