@jdlien/validator 1.5.0 → 2.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/LICENSE ADDED
@@ -0,0 +1,7 @@
1
+ ISC License
2
+
3
+ Copyright (c) 2026 JD Lien
4
+
5
+ Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.
6
+
7
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
package/README.md CHANGED
@@ -1,12 +1,12 @@
1
- # Validator - HTML Form Validation Made Easy
1
+ # Validator - Easy, Powerful Form Validation
2
2
 
3
3
  ## Introduction
4
4
 
5
- Validator is a utility class that adds automatic validation to your HTML forms that works much like
5
+ Validator is a lightweight utility class that adds automatic validation to your HTML forms that works much like
6
6
  the HTML5 form validation provided by browsers, but it is much more powerful, flexible, and
7
7
  customizable.
8
8
 
9
- It can sanitize and check user input in forms, resulting in clean, consistent submissions that
9
+ It can normalize and check user input in forms, resulting in clean, consistent submissions that
10
10
  are very user-friendly without unnecessarily constraining the user from entering data in ways that
11
11
  are convenient for them.
12
12
 
@@ -23,8 +23,10 @@ Validator includes the following built-in validation types:
23
23
  - Canadian Postal Codes
24
24
  - Colors (CSS colors, with color picker support)
25
25
  - Dates (optionally constrained to past or future dates)
26
+ - Date and time
26
27
  - Time of day
27
28
  - URLs
29
+ - Files (type, size, count)
28
30
 
29
31
  You can also add custom validation and customize error messages per field or for the whole form.
30
32
 
@@ -37,7 +39,7 @@ npm install @jdlien/validator
37
39
 
38
40
  # or
39
41
 
40
- yarn add @jdlien/validator
42
+ pnpm add @jdlien/validator
41
43
  ```
42
44
 
43
45
  ## Basic Usage
@@ -49,6 +51,18 @@ can use a `data-` variant of these attributes to avoid the browser's built-in va
49
51
 
50
52
  Any input that you want to validate should have a unique name attribute. If you want to display error messages for the input, you must also have a div with an id that is the name of the input + `-error`.
51
53
 
54
+ If you're using a bundler:
55
+
56
+ ```javascript
57
+ import Validator from '@jdlien/validator'
58
+ ```
59
+
60
+ If you're using CommonJS:
61
+
62
+ ```javascript
63
+ const Validator = require('@jdlien/validator')
64
+ ```
65
+
52
66
  Then, create a new Validator instance and pass it the form element as the first argument. An optional second argument allows you to pass in options. Here is a simplified example:
53
67
 
54
68
  ```html
@@ -76,8 +90,16 @@ Then, create a new Validator instance and pass it the form element as the first
76
90
  <input type="submit" value="Submit" />
77
91
  </form>
78
92
 
79
- <!-- Include the validator.js file if you are not using a module bundler -->
80
- <script src="./dist/validator.js"></script>
93
+ <!-- Choose one of the following script tags if you are not using a bundler -->
94
+ <!-- ESM - recommended for modern browsers -->
95
+ <script type="module">
96
+ import Validator from 'https://unpkg.com/@jdlien/validator/dist/validator.mjs'
97
+ const form = document.getElementById('myForm')
98
+ const validator = new Validator(form)
99
+ </script>
100
+
101
+ <!-- UMD - for legacy browser support (exposes global Validator) -->
102
+ <script src="https://unpkg.com/@jdlien/validator/dist/validator.js"></script>
81
103
  <script>
82
104
  const form = document.getElementById('myForm')
83
105
  const validator = new Validator(form)
@@ -107,7 +129,7 @@ There are a few attributes that Validator looks for on the form element:
107
129
 
108
130
  - `data-prevent-submit` - If this attribute is present, the form will never be submitted, even if it is valid. This is useful if you want to handle the submission yourself. (By default, the form will be submitted if it is valid and not if it is invalid.)
109
131
 
110
- - `novalidate` - This is a native HTML5 attribute that will disable browser validation on the form. If this attribute is present. Validator adds this by default and removes it if `destroy()` is called. If you add it yourself, it will not be added back by Validator.
132
+ - `novalidate` - This is a native HTML5 attribute that disables browser validation on the form. Validator adds this by default and removes it if `destroy()` is called. If you add it yourself, it will not be added back by Validator.
111
133
 
112
134
  On input (and sometimes select and textarea) elements, the following attributes are supported:
113
135
 
@@ -117,54 +139,134 @@ On input (and sometimes select and textarea) elements, the following attributes
117
139
  - `pattern`/`data-pattern` - The input must match the specified regular expression.
118
140
  - `type`/`data-type` - The input must match the specified type. The following types are supported:
119
141
 
120
- - `number` - The input must be a number (use `data-type` to avoid quirky browser behavior)
142
+ - `number` (also `float`/`decimal`) - The input must be a number (use `data-type` to avoid quirky browser behavior)
121
143
  - `integer` - The input must be a positive whole number.
122
144
  - `tel` - The input must be a valid North American phone number.
123
145
  - `email` - The input must be a valid email address.
124
146
  - `zip` - The input must be a valid US zip code.
125
147
  - `postal` - The input must be a valid Canadian postal code.
126
- - `color` - The input must be a valid CSS color.
127
148
  - `date` - The input must be a valid date.
149
+ - `datetime` - The input must be a valid date and time.
128
150
  - `time` - The input must be a valid time.
129
151
  - `url` - The input must be a valid URL.
130
152
  - `color` - The input must be a valid CSS color. (This can be used in conjunction with a native color input - see Color Picker Support for details.)
131
153
 
132
- - `data-date-format`/`data-time-format` - Applies formatting to time input types (these are interchangeable). The format must be a valid moment.js format string. See [moment.js docs](https://momentjs.com/docs/#/displaying/format/) for more information.
133
- - `data-date-range` - Applies to date input types. Supported values are `past` and `future`.
154
+ - `data-date-format`/`data-time-format` - Applies formatting to date, time, or datetime inputs (these are interchangeable). The format must be a valid moment.js format string. See [moment.js docs](https://momentjs.com/docs/#/displaying/format/) for more information.
155
+ - `data-date-range` - Applies to date input types. Supported values are `past`, `future`, and `today`.
156
+ - `data-min`/`data-max` - Applies to numeric input types (`number`, `integer`, `float`, `decimal`). Validates that the numeric value is within the specified range. Also respects the native `min`/`max` attributes, but `data-` attributes take precedence.
157
+ - `data-arrow-step` - Applies to numeric input types (`number`, `integer`, `float`, `decimal`). Sets the arrow key step size (defaults to `1`). Set `data-arrow-step=""` to disable arrow key handling for the field.
134
158
  - `data-error-default` - A custom error message to display if the input is invalid. This will be used for required, pattern, and date-range validation failures.
135
159
  - `data-validation` - The name of a custom validation function.
136
160
  - `data-novalidate` - If this attribute is present, the input will not be validated when `input` or `change` events are triggered on it.
161
+ - `data-max-files` - Applies to file inputs. Limits the number of files a user can upload.
162
+ - `data-min-file-size`/`data-max-file-size` - Applies to file inputs. Enforces min/max size per file. Accepts human-readable sizes like `200kb` (base 10), `2mib` (base 2), `1.5g`.
163
+ - `accept`/`data-accept` - Applies to file inputs. Restricts allowed file types using MIME types and/or extensions. `data-accept` takes precedence over `accept`.
137
164
 
138
165
  A validation function will be called with the input value as the argument. The function may either return a boolean (true/false) or an object with a `valid` property that is a boolean. If the function returns a string or an object with a `message` property, that will be used as the error message for the input. A `messages` array may also be specified which will be used to display multiple error messages for the input.
139
166
 
140
167
  You may also use a promise that resolves to such an object for asynchronous validation.
141
168
 
142
- An example of such a function is:
169
+ ### Registering Custom Validators
170
+
171
+ There are three ways to register custom validators, listed in order of lookup priority:
172
+
173
+ #### 1. Instance Registry (Recommended)
174
+
175
+ Pass validators directly to the Validator constructor. This is the recommended approach as it provides the best type safety and keeps validators scoped to specific forms.
143
176
 
144
177
  ```javascript
145
- function customValidation(value) {
146
- if (value === 'foo') return 'The value cannot be foo.'
178
+ const validator = new Validator(form, {
179
+ validators: {
180
+ validateUsername: (value) => {
181
+ if (value.length < 3) return 'Username must be at least 3 characters'
182
+ return true
183
+ },
184
+ validateEmail: async (value) => {
185
+ const res = await fetch(`/api/check-email?email=${encodeURIComponent(value)}`)
186
+ return res.ok ? true : 'Email already taken'
187
+ },
188
+ },
189
+ })
190
+ ```
147
191
 
148
- return true
192
+ #### 2. Static Registry (For Shared Validators)
193
+
194
+ Use `Validator.registerValidator()` to make validators available to all Validator instances. Useful for reusable validators across your application.
195
+
196
+ ```javascript
197
+ // Register globally (available to all forms)
198
+ Validator.registerValidator('validatePhone', (value) => {
199
+ return /^\d{10}$/.test(value) ? true : 'Enter a 10-digit phone number'
200
+ })
201
+
202
+ // Later, in any form...
203
+ const validator = new Validator(form)
204
+ // Input with data-validation="validatePhone" will use the registered validator
205
+
206
+ // Other static methods:
207
+ Validator.unregisterValidator('validatePhone') // Remove a validator
208
+ Validator.getValidators() // Get all registered validators (returns a copy)
209
+ Validator.clearValidators() // Remove all registered validators
210
+ ```
211
+
212
+ #### 3. Window Object (Legacy)
213
+
214
+ For backward compatibility, validators can be defined on the window object. This is not recommended for new code.
215
+
216
+ ```javascript
217
+ window.validateCustom = (value) => {
218
+ return value === 'valid' ? true : 'Invalid value'
149
219
  }
150
220
  ```
151
221
 
152
- Here is an example of a custom validation function that uses a promise:
222
+ ### Lookup Priority
223
+
224
+ When resolving a validator by name, Validator checks in this order:
225
+
226
+ 1. **Instance registry** (validators passed to constructor)
227
+ 2. **Static registry** (Validator.registerValidator)
228
+ 3. **Window object** (legacy fallback)
229
+
230
+ This allows you to override global validators for specific forms when needed.
231
+
232
+ ### Validator Function Examples
233
+
234
+ Custom validators are functions you can write that receive the input value and can return:
235
+
236
+ - `true` — valid
237
+ - `false` — invalid (uses default error message)
238
+ - `string` — invalid with custom error message
239
+ - `{ valid: boolean, message?: string, messages?: string[] }` — structured result
240
+
241
+ All return types can be wrapped in a `Promise` for async validation.
153
242
 
154
243
  ```javascript
155
- function customValidationPromise(value) {
156
- return fetch(`https://api.example.com/validate-username?username=${value}`)
157
- .then((response) => response.json())
158
- .then((result) => {
159
- if (result.valid) return true
160
- else return 'Email is invalid'
161
- })
244
+ // Simple synchronous validator:
245
+ function customValidation(value) {
246
+ if (value === 'foo') return 'The value cannot be foo.'
247
+ return true
248
+ }
249
+
250
+ // Async validator using fetch:
251
+ async function customValidationPromise(value) {
252
+ const response = await fetch(`/api/validate-username?username=${value}`)
253
+ const { valid, error } = await response.json()
254
+ return valid ? true : error
255
+ }
256
+
257
+ // Structured result with multiple errors:
258
+ function validatePassword(value) {
259
+ const errors = []
260
+ if (value.length < 8) errors.push('Must be at least 8 characters')
261
+ if (!/[A-Z]/.test(value)) errors.push('Must contain an uppercase letter')
262
+ if (!/[0-9]/.test(value)) errors.push('Must contain a number')
263
+ return errors.length ? { valid: false, messages: errors } : true
162
264
  }
163
265
  ```
164
266
 
165
267
  ## Displaying Error Messages
166
268
 
167
- If any form validation fails on submission, Validator displays a main error message near the top of the form. By default, it looks for an element with the ID `form-error-main`. However, if the form itself has an `id` attribute (e.g., `<form id="contact-form">`), Validator will first look for a main error element with the ID `{form.id}-error-main` (e.g., `contact-form-error-main`). If that form-specific element is not found, it falls back to looking for `form-error-main`.
269
+ If any form validation fails on submission, Validator displays a main error message directly beneath the form. By default, it looks for an element with the ID `form-error-main`. However, if the form itself has an `id` attribute (e.g., `<form id="contact-form">`), Validator will first look for a main error element with the ID `{form.id}-error-main` (e.g., `contact-form-error-main`). If that form-specific element is not found, it falls back to looking for `form-error-main`.
168
270
 
169
271
  This allows for more targeted styling and placement of the main error message per form. You can disable the display of this main error message entirely by setting the `showMainError` option to `false`.
170
272
 
@@ -174,11 +276,11 @@ If you do not use `aria-describedby`, Validator will fall back to displaying err
174
276
 
175
277
  It is recommended to initially hide error elements with a class that sets properties such as `display: none;`, `visibility: hidden;`, or `opacity: 0;`.
176
278
 
177
- You can customize the class(es) that Validator uses to hide the error messages by passing in a `hideErrorClass` option to the Validator constructor. The default is `hidden opacity-0`.
279
+ You can customize the class(es) that Validator uses to hide the error messages by passing in a `hiddenClasses` option to the Validator constructor. The default is `hidden opacity-0`.
178
280
 
179
281
  ## Color Picker Support
180
282
 
181
- If you need to allow a user to pick a color, you can use data-type="color" and the input will be required to be any valid CSS color supported by the browser. This type can also work in conjunction with a native color input. If you do this, you will need to add an input with `type="color"` and the name of the data-color input + `-color`. This should be inside a linked label, which will become the color preview swatch. Such a label should have an ID of the color input's name + `-color-label` so that Validator can change the background to the specified color.
283
+ Use data-type="color" and the input must contain any valid CSS color supported by the browser. To use this with a native color input, add an input with `type="color"` and the ID of the data-color input + `-color`. This should be inside a linked label, which will become the color preview swatch. Such a label should have an ID of the color input's name + `-color-label` so that Validator can change the background to the specified color.
182
284
 
183
285
  A basic example that would work:
184
286
 
@@ -198,7 +300,7 @@ A basic example that would work:
198
300
 
199
301
  The second parameter to the Validator constructor is an options object. The following options are available:
200
302
 
201
- - `messages - An object containing custom error messages. The default messages can be overridden by `passing in a custom message object. These are all the default messages:
303
+ - `messages` - An object containing custom error messages. The default messages can be overridden by passing in a custom message object. These are all the default messages:
202
304
 
203
305
  ```javascript
204
306
  messages = {
@@ -209,6 +311,8 @@ messages = {
209
311
  CHECKED_REQUIRED: 'This must be checked.',
210
312
  ERROR_MAXLENGTH: 'This must be ${val} characters or fewer.',
211
313
  ERROR_MINLENGTH: 'This must be at least ${val} characters.',
314
+ ERROR_MIN_VALUE: 'The value must be at least ${val}.',
315
+ ERROR_MAX_VALUE: 'The value must be at most ${val}.',
212
316
  ERROR_NUMBER: 'This must be a number.',
213
317
  ERROR_INTEGER: 'This must be a whole number.',
214
318
  ERROR_TEL: 'This is not a valid telephone number.',
@@ -219,10 +323,14 @@ messages = {
219
323
  ERROR_DATE_PAST: 'The date must be in the past.',
220
324
  ERROR_DATE_FUTURE: 'The date must be in the future.',
221
325
  ERROR_DATE_RANGE: 'The date is outside the allowed range.',
326
+ ERROR_DATETIME: 'This is not a valid date and time.',
222
327
  ERROR_TIME: 'This is not a valid time.',
223
- ERROR_TIME_RANGE: 'The time is outside the allowed range.',
224
328
  ERROR_URL: 'This is not a valid URL.',
225
329
  ERROR_COLOR: 'This is not a valid CSS colour.',
330
+ ERROR_FILE_TYPE: 'This file type is not allowed.',
331
+ ERROR_FILE_MAX_FILES: 'You can upload up to ${val} file(s).',
332
+ ERROR_FILE_MAX_SIZE: 'Each file must be ${val} or smaller.',
333
+ ERROR_FILE_MIN_SIZE: 'Each file must be at least ${val}.',
226
334
  ERROR_CUSTOM_VALIDATION: 'There was a problem validating this field.',
227
335
  }
228
336
  ```
@@ -232,15 +340,19 @@ messages = {
232
340
  - `preventSubmit` - A boolean indicating whether or not to prevent form submission if validation is successful. Defaults to false.
233
341
  - `hiddenClasses` - A string containing one or more space-separated classes to toggle the hidden mode (e.g., `display: none` CSS property) on hidden elements. Defaults to `hidden opacity-0`.
234
342
  - `errorMainClasses` - A string containing one or more space-separated classes to apply to the main error message.
235
- - `errorInputClasses` - A string containing one or more space-separated classes to apply to invalid `inputs.
343
+ - `errorInputClasses` - A string containing one or more space-separated classes to apply to invalid inputs.
236
344
  - `showMainError` - A boolean indicating whether or not to show the main error message. Defaults to `true`.
345
+ - `scrollToError` - A boolean indicating whether to scroll to and focus the first invalid input when validation fails. Defaults to `false`.
346
+ - `scrollToErrorDelay` - A number (ms) to delay scroll-to-error behavior. Defaults to `0`.
347
+ - `validateOnBlur` - A boolean indicating whether to validate fields when they lose focus, even if the value hasn't changed. Useful for showing errors on touched-but-empty required fields. Defaults to `false`.
237
348
  - `validationSuccessCallback` - A function to be called when validation is successful.
238
349
  - `validationErrorCallback` - A function to be called when validation fails.
350
+ - `validators` - An object mapping validator names to functions. These validators have the highest lookup priority (see Registering Custom Validators).
239
351
 
240
352
  ### Example:
241
353
 
242
354
  ```javascript
243
- import Validator from 'validator'
355
+ import Validator from '@jdlien/validator'
244
356
 
245
357
  const myForm = document.querySelector('form')
246
358
  const myValidator = new Validator(myForm, {
@@ -258,6 +370,125 @@ const myValidator = new Validator(myForm, {
258
370
  })
259
371
  ```
260
372
 
373
+ ## Methods
374
+
375
+ ### Instance Methods
376
+
377
+ #### `validateSingle(input): Promise<boolean>`
378
+
379
+ Validates a single input that belongs to the form and displays any error messages. Returns `true` if the input is valid, `false` otherwise.
380
+
381
+ This is useful for:
382
+
383
+ - **Multi-step forms/wizards** - Validate each step before allowing progression
384
+ - **Dependent field validation** - Validate field A when field B changes
385
+ - **Custom validation triggers** - Validate on demand rather than relying on events
386
+ - **Dynamic form updates** - Validate after programmatically updating a field's value
387
+
388
+ ```javascript
389
+ const validator = new Validator(form)
390
+ const emailInput = document.getElementById('email')
391
+
392
+ // Validate a single input on demand
393
+ const isValid = await validator.validateSingle(emailInput)
394
+
395
+ if (isValid) {
396
+ // Proceed to next step
397
+ } else {
398
+ // Error messages are automatically displayed
399
+ }
400
+ ```
401
+
402
+ Notes:
403
+
404
+ - Only validates inputs that belong to the validator's form
405
+ - Returns `true` for inputs not in the form (with console warning) or disabled inputs (use `Validator.validateSingle()` for standalone inputs)
406
+ - Clears previous errors before validating
407
+ - Displays error messages in the associated error element
408
+ - Works with all form control types (input, select, textarea) except radio/checkbox groups
409
+
410
+ #### `clearInputErrors(input): void`
411
+
412
+ Clears validation errors from a specific input element.
413
+
414
+ ```javascript
415
+ validator.clearInputErrors(emailInput)
416
+ ```
417
+
418
+ #### `clearAllErrors(): void`
419
+
420
+ Clears all validation errors from the form, including the main error message and all input errors.
421
+
422
+ ```javascript
423
+ validator.clearAllErrors()
424
+ ```
425
+
426
+ #### `init(): void`
427
+
428
+ Re-initializes the validator, refreshing the list of form inputs. Call this after dynamically adding or removing inputs.
429
+
430
+ #### `destroy(): void`
431
+
432
+ Removes all event listeners and restores the form's original `novalidate` state. Call this before removing the form from the DOM.
433
+
434
+ ### Static Methods
435
+
436
+ These methods allow validation without creating a Validator instance, useful for standalone inputs outside of forms.
437
+
438
+ #### `Validator.validateSingle(input, options?): Promise<boolean>`
439
+
440
+ Validates any input element without needing a Validator instance. Useful for standalone fields outside of forms.
441
+
442
+ ```javascript
443
+ // Validate a standalone input (not in a form)
444
+ const standaloneInput = document.getElementById('standalone-email')
445
+ const isValid = await Validator.validateSingle(standaloneInput)
446
+
447
+ // With custom options
448
+ const isValid = await Validator.validateSingle(standaloneInput, {
449
+ validators: {
450
+ customCheck: (value) => value.length > 5 || 'Too short',
451
+ },
452
+ })
453
+ ```
454
+
455
+ #### `Validator.clearInputErrors(input, options?): void`
456
+
457
+ Clears validation errors from any input element without needing a Validator instance.
458
+
459
+ ```javascript
460
+ Validator.clearInputErrors(standaloneInput)
461
+ ```
462
+
463
+ ## Events
464
+
465
+ Validator dispatches two custom events on the form during submission validation:
466
+
467
+ - `validationSuccess` - Fired when the form is valid.
468
+ - `validationError` - Fired when the form is invalid.
469
+
470
+ Both events are instances of `ValidationEvent` and include the original submit event as `submitEvent`.
471
+
472
+ ## Dynamic Forms and Cleanup
473
+
474
+ Validator does not watch the DOM for changes. If you add or remove inputs after initialization
475
+ (for example, injecting fields with JavaScript or swapping Blade partials), call `init()` again
476
+ to refresh the list of inputs and their error state.
477
+
478
+ If you remove the form from the page (such as when closing a modal or navigating in an SPA),
479
+ call `destroy()` first to remove event listeners and restore the form's original `novalidate` state.
480
+
481
+ ```javascript
482
+ const validator = new Validator(form)
483
+
484
+ // After dynamically adding/removing inputs:
485
+ validator.init()
486
+
487
+ // Before removing the form from the DOM:
488
+ validator.destroy()
489
+ form.remove()
490
+ ```
491
+
261
492
  ## Utility Functions
262
493
 
263
494
  Validator uses its own `@jdlien/validator-utils` for several utility functions that may be useful in your own code. You may use this package directly if you need any of these functions without using the Validator class.
@@ -268,54 +499,116 @@ If you wish to use these, you may import the functions directly from the module
268
499
  // Import all the functions into a validatorUtils object
269
500
  import * as validatorUtils from '@jdlien/validator-utils'
270
501
  // Or just import the functions you need
271
- import { dateFormat, formatDateTime } from '@jdlien/validator-utils'
502
+ import { parseDate, formatDateTime } from '@jdlien/validator-utils'
272
503
  ```
273
504
 
274
505
  Here is a list of the utility functions:
275
506
 
276
- - **isFormControl**: Determines if an element is an HTML input, select, or textarea element.
277
- - **isType**: Checks if an element has a type or data-type attribute matching one of the passed values.
278
- - **momentToFPFormat**: Converts a moment.js-style format string to the flatpickr format.
279
- - **monthToNumber**: Converts month string or number to a zero-based month number (January == 0).
280
- - **yearToFull**: Converts a year string or number to a 4-digit year.
507
+ **Date & Time:**
281
508
  - **parseDate**: Parses a date string or Date object into a Date object.
509
+ - **parseDateTime**: Parses a datetime string (e.g., "tomorrow 3pm") into a Date object.
282
510
  - **parseTime**: Parses a time string into an object with hour, minute, and second properties.
283
- - **parseTimeToString**: Parses a time string into a formatted string.
284
- - **formatDateTime**: Formats a date string or Date object into a string with a specified format.
285
- - **parseDateToString**: Parses a date string or Date object into a formatted string with the specified moment.js-style date format.
511
+ - **parseRelativeDate**: Parses relative date strings like "+3d", "-1w", "+2m" into a Date object.
512
+ - **parseDateToString**: Formats a date into a string with the specified moment.js-style format.
513
+ - **parseDateTimeToString**: Formats a datetime into a string with the specified format.
514
+ - **parseTimeToString**: Formats a time string into a formatted string.
515
+ - **formatDateTime**: Formats a Date object into a string with a specified format.
516
+ - **momentToFPFormat**: Converts a moment.js-style format string to flatpickr format.
517
+ - **monthToNumber**: Converts month string or number to zero-based month number (January = 0).
518
+ - **yearToFull**: Converts a 2-digit year to a 4-digit year.
286
519
  - **isDate**: Determines if a value is a valid date.
287
- - **isDateInRange**: Determines if a date falls within a specified range (either past or future).
520
+ - **isDateTime**: Determines if a value is a valid datetime.
288
521
  - **isTime**: Determines if a value is a valid time.
289
- - **isEmail**: Determines if a value is a valid email address.
290
- - **parseNANPTel**: Parses a North American phone number string into a standardized format.
291
- - **isNANPTel**: Determines if a value is a valid North American phone number.
292
- - **parseInteger**: Parses an integer string into a standardized format.
522
+ - **isMeridiem**: Determines if a value is "am" or "pm".
523
+ - **isDateInRange**: Determines if a date falls within a specified range (past, future, or custom).
524
+
525
+ **Numbers & Bytes:**
526
+ - **parseNumber**: Parses a number string, stripping non-numeric characters.
527
+ - **parseInteger**: Parses an integer string, stripping non-digit characters.
528
+ - **parseBytes**: Parses human-readable byte sizes ("1.5MB", "2GiB") into bytes.
529
+ - **formatBytes**: Formats bytes into human-readable string (e.g., "1.5 MB").
293
530
  - **isNumber**: Determines if a value is a valid number.
294
- - **parseNumber**: Parses a number string into a standardized format.
295
531
  - **isInteger**: Determines if a value is a valid integer.
296
- - **parseUrl**: Parses a URL string into a standardized format.
297
- - **isUrl**: Determines if a value is a valid URL.
298
- - **parseZip**: Parses a zip code string into a standardized format.
299
- - **isZip**: Determines if a value is a valid zip code.
300
- - **parsePostalCA**: Parses a Canadian postal code string into a standardized format.
532
+
533
+ **Contact & Location:**
534
+ - **parseNANPTel**: Parses a North American phone number into standardized format.
535
+ - **isNANPTel**: Determines if a value is a valid North American phone number.
536
+ - **isEmail**: Determines if a value is a valid email address.
537
+ - **parseZip**: Parses a US zip code into standardized format.
538
+ - **isZip**: Determines if a value is a valid US zip code.
539
+ - **parsePostalCA**: Parses a Canadian postal code into standardized format.
301
540
  - **isPostalCA**: Determines if a value is a valid Canadian postal code.
302
- - **isColor**: Determines if a value is a valid color.
303
- - **parseColor**: Parses a color string into a standardized format.
304
- - **normalizeValidationResult**: Normalizes a validation result (like a boolean or string) into an object with a valid property and a messages array of strings.
541
+ - **parseUrl**: Parses a URL, adding https:// if missing.
542
+ - **isUrl**: Determines if a value is a valid URL.
543
+
544
+ **Color:**
545
+ - **parseColor**: Parses any CSS color into a normalized format.
546
+ - **isColor**: Determines if a value is a valid CSS color.
547
+
548
+ **Utilities:**
549
+ - **isFormControl**: Determines if an element is an input, select, or textarea.
550
+ - **isType**: Checks if an element's type or data-type matches specified values.
551
+ - **normalizeValidationResult**: Normalizes validation results (boolean/string/object) into a standard format.
552
+
553
+ ## Breaking Changes in v2.0.0
554
+
555
+ ### Manual Lifecycle Management Required
556
+
557
+ Previously, Validator used MutationObservers to automatically reinitialize when inputs changed and clean up
558
+ when forms were removed. This was inefficient and unreliable, so it has been removed.
559
+
560
+ Now, if you're dynamically changing forms/form elements in a webpage, you must run the following:
561
+ ```javascript
562
+ // After dynamically adding/removing inputs:
563
+ validator.init()
564
+
565
+ // Before removing a form from the DOM:
566
+ validator.destroy()
567
+ ```
568
+
569
+ ### Event Classes Consolidated
570
+
571
+ The separate `ValidationSuccessEvent` and `ValidationErrorEvent` classes have been replaced with a unified `ValidationEvent` class:
572
+
573
+ ```javascript
574
+ // Before (v1.x)
575
+ form.addEventListener('validationSuccess', (e: ValidationSuccessEvent) => { ... })
576
+
577
+ // After (v2.0)
578
+ form.addEventListener('validationSuccess', (e: ValidationEvent) => { ... })
579
+ ```
580
+
581
+ The `ValidationEvent` class has a `type` property that is either `'validationSuccess'` or `'validationError'`, and a `submitEvent` property containing the original form submission event.
582
+
583
+ ### TypeScript Changes
584
+
585
+ - **`messages` option typing:** Now `Record<string, string>` instead of `object`
586
+ - **`types.d.ts` removed:** Import types directly from the package instead:
587
+
588
+ ```typescript
589
+ // Before
590
+ import type { ValidatorOptions } from '@jdlien/validator/types'
591
+
592
+ // After
593
+ import type { ValidatorOptions } from '@jdlien/validator'
594
+ ```
305
595
 
306
596
  ## Contributing
307
597
 
308
598
  Install dev dependencies:
309
599
 
310
600
  ```bash
311
- npm install
601
+ pnpm install
312
602
  ```
313
603
 
314
- When running Vite, you may get an error like
604
+ Run tests with coverage (100% required for release):
315
605
 
606
+ ```bash
607
+ pnpm coverage
316
608
  ```
317
- Module did not self-register: '...\node_modules\canvas\build\Release\canvas.node'
318
- ```
319
609
 
320
- If that happens, you
321
- need to install the canvas module manually: `npm rebuild canvas --update-binary`
610
+ Build the project and demo page for testing/release:
611
+
612
+ ```bash
613
+ pnpm build
614
+ ```