@paragrav/rhf-utils 0.0.16
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/dist/cjs/main.d.ts +7 -0
- package/dist/cjs/main.js +21 -0
- package/dist/cjs/rhf/errors/orphans/FormErrorNonFieldMarker.d.ts +11 -0
- package/dist/cjs/rhf/errors/orphans/FormErrorNonFieldMarker.js +17 -0
- package/dist/cjs/rhf/errors/orphans/useFormErrorOrphansToConsoleError.d.ts +9 -0
- package/dist/cjs/rhf/errors/orphans/useFormErrorOrphansToConsoleError.js +29 -0
- package/dist/cjs/rhf/errors/orphans/useFormOrphanErrors.d.ts +8 -0
- package/dist/cjs/rhf/errors/orphans/useFormOrphanErrors.js +22 -0
- package/dist/cjs/rhf/errors/orphans/utils.d.ts +3 -0
- package/dist/cjs/rhf/errors/orphans/utils.js +20 -0
- package/dist/cjs/rhf/errors/roots/useFormRootFieldErrors.d.ts +2 -0
- package/dist/cjs/rhf/errors/roots/useFormRootFieldErrors.js +9 -0
- package/dist/cjs/rhf/errors/roots/utils.d.ts +7 -0
- package/dist/cjs/rhf/errors/roots/utils.js +13 -0
- package/dist/cjs/rhf/errors/useFormErrorsEffect.d.ts +3 -0
- package/dist/cjs/rhf/errors/useFormErrorsEffect.js +14 -0
- package/dist/cjs/rhf/errors/useFormErrorsOnChange.d.ts +6 -0
- package/dist/cjs/rhf/errors/useFormErrorsOnChange.js +15 -0
- package/dist/cjs/rhf/errors/useFormRequestSubmit.d.ts +6 -0
- package/dist/cjs/rhf/errors/useFormRequestSubmit.js +13 -0
- package/dist/cjs/rhf/errors/utils.d.ts +9 -0
- package/dist/cjs/rhf/errors/utils.js +65 -0
- package/dist/esm/main.mjs +17 -0
- package/dist/esm/node_modules/flat/index.mjs +22 -0
- package/dist/esm/rhf/errors/orphans/FormErrorNonFieldMarker.mjs +19 -0
- package/dist/esm/rhf/errors/orphans/useFormErrorOrphansToConsoleError.mjs +16 -0
- package/dist/esm/rhf/errors/orphans/useFormOrphanErrors.mjs +13 -0
- package/dist/esm/rhf/errors/orphans/utils.mjs +18 -0
- package/dist/esm/rhf/errors/roots/useFormRootFieldErrors.mjs +6 -0
- package/dist/esm/rhf/errors/roots/utils.mjs +6 -0
- package/dist/esm/rhf/errors/useFormErrorsEffect.mjs +11 -0
- package/dist/esm/rhf/errors/useFormErrorsOnChange.mjs +9 -0
- package/dist/esm/rhf/errors/useFormRequestSubmit.mjs +8 -0
- package/dist/esm/rhf/errors/utils.mjs +14 -0
- package/dist/types/main.d.ts +7 -0
- package/dist/types/rhf/errors/orphans/FormErrorNonFieldMarker.d.ts +11 -0
- package/dist/types/rhf/errors/orphans/useFormErrorOrphansToConsoleError.d.ts +10 -0
- package/dist/types/rhf/errors/orphans/useFormOrphanErrors.d.ts +9 -0
- package/dist/types/rhf/errors/orphans/utils.d.ts +4 -0
- package/dist/types/rhf/errors/roots/useFormRootFieldErrors.d.ts +2 -0
- package/dist/types/rhf/errors/roots/utils.d.ts +8 -0
- package/dist/types/rhf/errors/useFormErrorsEffect.d.ts +4 -0
- package/dist/types/rhf/errors/useFormErrorsOnChange.d.ts +7 -0
- package/dist/types/rhf/errors/useFormRequestSubmit.d.ts +6 -0
- package/dist/types/rhf/errors/utils.d.ts +10 -0
- package/package.json +93 -0
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { default as FormErrorNonFieldMarker, FormErrorNonFieldMarkerDataHtmlAttribute, } from './rhf/errors/orphans/FormErrorNonFieldMarker';
|
|
2
|
+
export { default as useFormErrorOrphansToConsoleError } from './rhf/errors/orphans/useFormErrorOrphansToConsoleError';
|
|
3
|
+
export { default as useFormOrphanErrors } from './rhf/errors/orphans/useFormOrphanErrors';
|
|
4
|
+
export { default as useFormRootFieldErrors } from './rhf/errors/roots/useFormRootFieldErrors';
|
|
5
|
+
export { default as useFormErrorsEffect } from './rhf/errors/useFormErrorsEffect';
|
|
6
|
+
export { default as useFormErrorsOnChange } from './rhf/errors/useFormErrorsOnChange';
|
|
7
|
+
export { default as useFormRequestSubmit } from './rhf/errors/useFormRequestSubmit';
|
package/dist/cjs/main.js
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.useFormRequestSubmit = exports.useFormErrorsOnChange = exports.useFormErrorsEffect = exports.useFormRootFieldErrors = exports.useFormOrphanErrors = exports.useFormErrorOrphansToConsoleError = exports.FormErrorNonFieldMarkerDataHtmlAttribute = exports.FormErrorNonFieldMarker = void 0;
|
|
7
|
+
var FormErrorNonFieldMarker_1 = require("./rhf/errors/orphans/FormErrorNonFieldMarker");
|
|
8
|
+
Object.defineProperty(exports, "FormErrorNonFieldMarker", { enumerable: true, get: function () { return __importDefault(FormErrorNonFieldMarker_1).default; } });
|
|
9
|
+
Object.defineProperty(exports, "FormErrorNonFieldMarkerDataHtmlAttribute", { enumerable: true, get: function () { return FormErrorNonFieldMarker_1.FormErrorNonFieldMarkerDataHtmlAttribute; } });
|
|
10
|
+
var useFormErrorOrphansToConsoleError_1 = require("./rhf/errors/orphans/useFormErrorOrphansToConsoleError");
|
|
11
|
+
Object.defineProperty(exports, "useFormErrorOrphansToConsoleError", { enumerable: true, get: function () { return __importDefault(useFormErrorOrphansToConsoleError_1).default; } });
|
|
12
|
+
var useFormOrphanErrors_1 = require("./rhf/errors/orphans/useFormOrphanErrors");
|
|
13
|
+
Object.defineProperty(exports, "useFormOrphanErrors", { enumerable: true, get: function () { return __importDefault(useFormOrphanErrors_1).default; } });
|
|
14
|
+
var useFormRootFieldErrors_1 = require("./rhf/errors/roots/useFormRootFieldErrors");
|
|
15
|
+
Object.defineProperty(exports, "useFormRootFieldErrors", { enumerable: true, get: function () { return __importDefault(useFormRootFieldErrors_1).default; } });
|
|
16
|
+
var useFormErrorsEffect_1 = require("./rhf/errors/useFormErrorsEffect");
|
|
17
|
+
Object.defineProperty(exports, "useFormErrorsEffect", { enumerable: true, get: function () { return __importDefault(useFormErrorsEffect_1).default; } });
|
|
18
|
+
var useFormErrorsOnChange_1 = require("./rhf/errors/useFormErrorsOnChange");
|
|
19
|
+
Object.defineProperty(exports, "useFormErrorsOnChange", { enumerable: true, get: function () { return __importDefault(useFormErrorsOnChange_1).default; } });
|
|
20
|
+
var useFormRequestSubmit_1 = require("./rhf/errors/useFormRequestSubmit");
|
|
21
|
+
Object.defineProperty(exports, "useFormRequestSubmit", { enumerable: true, get: function () { return __importDefault(useFormRequestSubmit_1).default; } });
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/// <reference types="react" />
|
|
2
|
+
type Props = {
|
|
3
|
+
/** Flat field name. (e.g., `address.street`) */
|
|
4
|
+
path: string;
|
|
5
|
+
};
|
|
6
|
+
/**
|
|
7
|
+
* Empty marker to indicate a non-field error is presented to user.
|
|
8
|
+
*/
|
|
9
|
+
declare const FormErrorNonFieldMarker: React.FC<Props>;
|
|
10
|
+
export default FormErrorNonFieldMarker;
|
|
11
|
+
export declare const FormErrorNonFieldMarkerDataHtmlAttribute = "data-grnwld-rhf-utils-marker-path";
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.FormErrorNonFieldMarkerDataHtmlAttribute = void 0;
|
|
4
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
5
|
+
/**
|
|
6
|
+
* Empty marker to indicate a non-field error is presented to user.
|
|
7
|
+
*/
|
|
8
|
+
const FormErrorNonFieldMarker = ({ path }) => {
|
|
9
|
+
const attribute = {
|
|
10
|
+
[exports.FormErrorNonFieldMarkerDataHtmlAttribute]: path,
|
|
11
|
+
};
|
|
12
|
+
return ((0, jsx_runtime_1.jsx)("div", { role: "alert", "aria-hidden": "true" // hide from screen readers
|
|
13
|
+
, style: { display: 'none' }, ...attribute }));
|
|
14
|
+
};
|
|
15
|
+
exports.default = FormErrorNonFieldMarker;
|
|
16
|
+
// utils
|
|
17
|
+
exports.FormErrorNonFieldMarkerDataHtmlAttribute = 'data-grnwld-rhf-utils-marker-path';
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* Orphans are field errors that do not have a ref.
|
|
4
|
+
* (Most commonly `root(.*)`, but these are filtered out. They are handled by `useFormRootFieldErrors`.)
|
|
5
|
+
* Outputs to `console.error` all errors with `ref: undefined` that do not begin with `root(.*)`.
|
|
6
|
+
* Sentry (or equivalent) should capture.
|
|
7
|
+
*/
|
|
8
|
+
declare const useFormErrorOrphansToConsoleError: (formRef: React.RefObject<HTMLFormElement>) => void;
|
|
9
|
+
export default useFormErrorOrphansToConsoleError;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const react_1 = __importDefault(require("react"));
|
|
7
|
+
const react_hook_form_1 = require("react-hook-form");
|
|
8
|
+
const useFormOrphanErrors_1 = __importDefault(require("./useFormOrphanErrors"));
|
|
9
|
+
/**
|
|
10
|
+
* Orphans are field errors that do not have a ref.
|
|
11
|
+
* (Most commonly `root(.*)`, but these are filtered out. They are handled by `useFormRootFieldErrors`.)
|
|
12
|
+
* Outputs to `console.error` all errors with `ref: undefined` that do not begin with `root(.*)`.
|
|
13
|
+
* Sentry (or equivalent) should capture.
|
|
14
|
+
*/
|
|
15
|
+
const useFormErrorOrphansToConsoleError = (formRef) => {
|
|
16
|
+
const { getValues } = (0, react_hook_form_1.useFormContext)();
|
|
17
|
+
const { errors } = (0, react_hook_form_1.useFormState)();
|
|
18
|
+
const orphans = (0, useFormOrphanErrors_1.default)(formRef);
|
|
19
|
+
react_1.default.useEffect(() => {
|
|
20
|
+
if (!Object.keys(orphans).length)
|
|
21
|
+
return;
|
|
22
|
+
console.error('useFormErrorOrphansToConsoleError', {
|
|
23
|
+
location: window.location,
|
|
24
|
+
values: getValues(),
|
|
25
|
+
orphans,
|
|
26
|
+
});
|
|
27
|
+
}, [errors, getValues, orphans]);
|
|
28
|
+
};
|
|
29
|
+
exports.default = useFormErrorOrphansToConsoleError;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* Root field errors from form.
|
|
4
|
+
* These are typically displayed at the bottom of the form.
|
|
5
|
+
* (Root errors have to processed this way due to (potentially) recursive types.)
|
|
6
|
+
*/
|
|
7
|
+
declare const useFormOrphanErrors: (formRef: React.RefObject<HTMLFormElement>) => Record<string, import("react-hook-form").FieldError>;
|
|
8
|
+
export default useFormOrphanErrors;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const useFormErrorsOnChange_1 = __importDefault(require("../useFormErrorsOnChange"));
|
|
7
|
+
const utils_1 = require("./utils");
|
|
8
|
+
/**
|
|
9
|
+
* Root field errors from form.
|
|
10
|
+
* These are typically displayed at the bottom of the form.
|
|
11
|
+
* (Root errors have to processed this way due to (potentially) recursive types.)
|
|
12
|
+
*/
|
|
13
|
+
const useFormOrphanErrors = (formRef) => (0, useFormErrorsOnChange_1.default)((errors) => {
|
|
14
|
+
if (JSON.stringify(errors) === '{}')
|
|
15
|
+
return {};
|
|
16
|
+
if (!formRef.current) {
|
|
17
|
+
console.error(errors);
|
|
18
|
+
throw new Error('This should not happen.');
|
|
19
|
+
}
|
|
20
|
+
return (0, utils_1.getOrphanFieldErrorsWithMarkerCheck)(errors, formRef.current);
|
|
21
|
+
});
|
|
22
|
+
exports.default = useFormOrphanErrors;
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { FieldErrors } from 'react-hook-form';
|
|
2
|
+
export declare const getOrphanFieldErrors: (errors: FieldErrors) => Record<string, import("react-hook-form").FieldError>;
|
|
3
|
+
export declare const getOrphanFieldErrorsWithMarkerCheck: (errors: FieldErrors, formElement: HTMLFormElement) => Record<string, import("react-hook-form").FieldError>;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getOrphanFieldErrorsWithMarkerCheck = exports.getOrphanFieldErrors = void 0;
|
|
4
|
+
const utils_1 = require("../roots/utils");
|
|
5
|
+
const utils_2 = require("../utils");
|
|
6
|
+
const FormErrorNonFieldMarker_1 = require("./FormErrorNonFieldMarker");
|
|
7
|
+
const getOrphanFieldErrors = (errors) => (0, utils_2.getFieldErrorsByFilter)(errors, (path, flattenedErrors) => {
|
|
8
|
+
return (flattenedErrors[path + '.ref'] === undefined && // no ref
|
|
9
|
+
!(0, utils_1.isFormErrorPathRoot)(path) // ignore root errors, which are always orphans
|
|
10
|
+
);
|
|
11
|
+
});
|
|
12
|
+
exports.getOrphanFieldErrors = getOrphanFieldErrors;
|
|
13
|
+
const getOrphanFieldErrorsWithMarkerCheck = (errors, formElement) => {
|
|
14
|
+
const orphanErrors = (0, exports.getOrphanFieldErrors)(errors);
|
|
15
|
+
return (0, utils_2.getFieldErrorsByFilter)(orphanErrors,
|
|
16
|
+
// if doesn't have alert, then it's an orphan
|
|
17
|
+
(path) => !formElement.querySelector(`[role="alert"][${FormErrorNonFieldMarker_1.FormErrorNonFieldMarkerDataHtmlAttribute}="${path}"]`));
|
|
18
|
+
};
|
|
19
|
+
exports.getOrphanFieldErrorsWithMarkerCheck = getOrphanFieldErrorsWithMarkerCheck;
|
|
20
|
+
//
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const useFormErrorsOnChange_1 = __importDefault(require("../useFormErrorsOnChange"));
|
|
7
|
+
const utils_1 = require("./utils");
|
|
8
|
+
const useFormRootFieldErrors = () => (0, useFormErrorsOnChange_1.default)(utils_1.getRootFieldErrorList);
|
|
9
|
+
exports.default = useFormRootFieldErrors;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { FieldErrors } from 'react-hook-form';
|
|
2
|
+
/**
|
|
3
|
+
* Determine if an error path is root. (e.g., `root` or `root.nested`)
|
|
4
|
+
* @param path Flat path. (e.g., 'root.nested')
|
|
5
|
+
*/
|
|
6
|
+
export declare const isFormErrorPathRoot: (path: string) => boolean;
|
|
7
|
+
export declare const getRootFieldErrorList: (errors: FieldErrors) => import("react-hook-form").FieldError[];
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getRootFieldErrorList = exports.isFormErrorPathRoot = void 0;
|
|
4
|
+
const utils_1 = require("../utils");
|
|
5
|
+
const ROOT = 'root';
|
|
6
|
+
/**
|
|
7
|
+
* Determine if an error path is root. (e.g., `root` or `root.nested`)
|
|
8
|
+
* @param path Flat path. (e.g., 'root.nested')
|
|
9
|
+
*/
|
|
10
|
+
const isFormErrorPathRoot = (path) => path === ROOT || path.startsWith(ROOT + '.');
|
|
11
|
+
exports.isFormErrorPathRoot = isFormErrorPathRoot;
|
|
12
|
+
const getRootFieldErrorList = (errors) => (0, utils_1.getFieldErrorListByFilter)(errors, exports.isFormErrorPathRoot);
|
|
13
|
+
exports.getRootFieldErrorList = getRootFieldErrorList;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const react_1 = __importDefault(require("react"));
|
|
7
|
+
const react_hook_form_1 = require("react-hook-form");
|
|
8
|
+
const useFormErrorsEffect = (callback) => {
|
|
9
|
+
const { errors } = (0, react_hook_form_1.useFormState)();
|
|
10
|
+
react_1.default.useEffect(() => {
|
|
11
|
+
callback(errors);
|
|
12
|
+
}, [callback, errors]);
|
|
13
|
+
};
|
|
14
|
+
exports.default = useFormErrorsEffect;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const react_1 = __importDefault(require("react"));
|
|
7
|
+
const react_hook_form_1 = require("react-hook-form");
|
|
8
|
+
/**
|
|
9
|
+
* Provide a callback to call when RHF errors change.
|
|
10
|
+
*/
|
|
11
|
+
const useFormErrorsOnChange = (callback) => {
|
|
12
|
+
const { errors } = (0, react_hook_form_1.useFormState)();
|
|
13
|
+
return react_1.default.useMemo(() => callback(errors), [callback, errors]);
|
|
14
|
+
};
|
|
15
|
+
exports.default = useFormErrorsOnChange;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Get callback to trigger submit manually on form.
|
|
3
|
+
* @link https://developer.mozilla.org/docs/Web/API/HTMLFormElement/requestSubmit
|
|
4
|
+
*/
|
|
5
|
+
declare const useFormRequestSubmit: (ref: React.RefObject<HTMLFormElement>) => HTMLFormElement['requestSubmit'];
|
|
6
|
+
export default useFormRequestSubmit;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
/**
|
|
4
|
+
* Get callback to trigger submit manually on form.
|
|
5
|
+
* @link https://developer.mozilla.org/docs/Web/API/HTMLFormElement/requestSubmit
|
|
6
|
+
*/
|
|
7
|
+
const useFormRequestSubmit = (ref) => (submitter) => {
|
|
8
|
+
// typeguard (should never happen at runtime when callback is called)
|
|
9
|
+
if (!ref.current)
|
|
10
|
+
throw new Error();
|
|
11
|
+
ref.current.requestSubmit(submitter);
|
|
12
|
+
};
|
|
13
|
+
exports.default = useFormRequestSubmit;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { FieldError, FieldErrors } from 'react-hook-form';
|
|
2
|
+
/**
|
|
3
|
+
* @returns A flattened {@link FieldErrors} record.
|
|
4
|
+
*/
|
|
5
|
+
export declare const getFieldErrorsByFilter: (errors: FieldErrors, where?: ((path: string, flattenedErrors: Record<string, unknown>) => boolean) | undefined) => Record<string, FieldError>;
|
|
6
|
+
/**
|
|
7
|
+
* @returns An array of field errors. (Not a record.)
|
|
8
|
+
*/
|
|
9
|
+
export declare const getFieldErrorListByFilter: (errors: FieldErrors, where?: ((path: string, flattenedErrors: Record<string, unknown>) => boolean) | undefined) => FieldError[];
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getFieldErrorListByFilter = exports.getFieldErrorsByFilter = void 0;
|
|
4
|
+
const flat_1 = require("flat");
|
|
5
|
+
const react_hook_form_1 = require("react-hook-form");
|
|
6
|
+
/**
|
|
7
|
+
* @returns A flattened {@link FieldErrors} record.
|
|
8
|
+
*/
|
|
9
|
+
const getFieldErrorsByFilter = (errors, where) => {
|
|
10
|
+
const keys = getFieldErrorOrphanKeysByFilter(errors, where);
|
|
11
|
+
const record = keys.reduce((acc, path) => {
|
|
12
|
+
acc[path] = (0, react_hook_form_1.get)(errors, path);
|
|
13
|
+
return acc;
|
|
14
|
+
}, {});
|
|
15
|
+
return record;
|
|
16
|
+
};
|
|
17
|
+
exports.getFieldErrorsByFilter = getFieldErrorsByFilter;
|
|
18
|
+
/**
|
|
19
|
+
* @returns An array of field errors. (Not a record.)
|
|
20
|
+
*/
|
|
21
|
+
const getFieldErrorListByFilter = (errors, where) => {
|
|
22
|
+
const keys = getFieldErrorOrphanKeysByFilter(errors, where);
|
|
23
|
+
const fieldErrors = keys.map((key) => {
|
|
24
|
+
return (0, react_hook_form_1.get)(errors, key);
|
|
25
|
+
});
|
|
26
|
+
return fieldErrors;
|
|
27
|
+
};
|
|
28
|
+
exports.getFieldErrorListByFilter = getFieldErrorListByFilter;
|
|
29
|
+
/**
|
|
30
|
+
* RHF uses deeply-nested object for errors, which reflects the structure of the form.
|
|
31
|
+
* And the type is quite complex. All this complicates the process of finding errors.
|
|
32
|
+
* This function automatically looks only for keys that end with `.ref`.
|
|
33
|
+
* (This makes sure we only look at the leaf nodes of the object tree.)
|
|
34
|
+
* This function also automatically removes the `.ref` suffix from the keys.
|
|
35
|
+
*
|
|
36
|
+
* @param where Filter callback.
|
|
37
|
+
* @param map Transformer callback.
|
|
38
|
+
* @returns Tuple of field errors and keys.
|
|
39
|
+
*/
|
|
40
|
+
const getFieldErrorOrphanKeysByFilter = (errors, where) => {
|
|
41
|
+
/**
|
|
42
|
+
* e.g.:
|
|
43
|
+
* ```js
|
|
44
|
+
* { root: { ref: undefined, message: 'Form is invalid.' }, email: { ref: Object, message: 'Invalid.' } }
|
|
45
|
+
* ```
|
|
46
|
+
* flattens to:
|
|
47
|
+
* ```js
|
|
48
|
+
* { 'root.ref': undefined, 'root.message': 'Form is invalid.', 'email.ref': Object, 'email.message': 'Invalid.' }
|
|
49
|
+
* ```
|
|
50
|
+
* returns (if no additional filter/map specified)
|
|
51
|
+
* ```js
|
|
52
|
+
* [ 'root', 'email' ]
|
|
53
|
+
* ```
|
|
54
|
+
*/
|
|
55
|
+
const flattenedErrors = (0, flat_1.flatten)(errors);
|
|
56
|
+
const flattenedErrorObjectKeys = Object.keys(flattenedErrors);
|
|
57
|
+
// look for leaf node, which would have ref prop (e.g., `orphan_field.ref`)
|
|
58
|
+
const flattenedErrorObjectKeysEndingWithDotRef = flattenedErrorObjectKeys.filter((key) => key.match(regexEndsWithDotRef));
|
|
59
|
+
// remove `.ref` suffix to get flat field path (e.g., `name`, `address.street`)
|
|
60
|
+
const paths = flattenedErrorObjectKeysEndingWithDotRef.map((flattenedKey) => flattenedKey.replace(regexEndsWithDotRef, ''));
|
|
61
|
+
// filter via callback
|
|
62
|
+
const flattenedKeysFiltered = paths.filter((path) => where ? where(path, flattenedErrors) : true);
|
|
63
|
+
return flattenedKeysFiltered;
|
|
64
|
+
};
|
|
65
|
+
const regexEndsWithDotRef = /\.ref$/;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { default as e, FormErrorNonFieldMarkerDataHtmlAttribute as t } from "./rhf/errors/orphans/FormErrorNonFieldMarker.mjs";
|
|
2
|
+
import { default as s } from "./rhf/errors/orphans/useFormErrorOrphansToConsoleError.mjs";
|
|
3
|
+
import { default as f } from "./rhf/errors/orphans/useFormOrphanErrors.mjs";
|
|
4
|
+
import { default as l } from "./rhf/errors/roots/useFormRootFieldErrors.mjs";
|
|
5
|
+
import { default as d } from "./rhf/errors/useFormErrorsEffect.mjs";
|
|
6
|
+
import { default as E } from "./rhf/errors/useFormErrorsOnChange.mjs";
|
|
7
|
+
import { default as x } from "./rhf/errors/useFormRequestSubmit.mjs";
|
|
8
|
+
export {
|
|
9
|
+
e as FormErrorNonFieldMarker,
|
|
10
|
+
t as FormErrorNonFieldMarkerDataHtmlAttribute,
|
|
11
|
+
s as useFormErrorOrphansToConsoleError,
|
|
12
|
+
d as useFormErrorsEffect,
|
|
13
|
+
E as useFormErrorsOnChange,
|
|
14
|
+
f as useFormOrphanErrors,
|
|
15
|
+
x as useFormRequestSubmit,
|
|
16
|
+
l as useFormRootFieldErrors
|
|
17
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
function d(t) {
|
|
2
|
+
return t && t.constructor && typeof t.constructor.isBuffer == "function" && t.constructor.isBuffer(t);
|
|
3
|
+
}
|
|
4
|
+
function A(t) {
|
|
5
|
+
return t;
|
|
6
|
+
}
|
|
7
|
+
function B(t, n) {
|
|
8
|
+
n = n || {};
|
|
9
|
+
const l = n.delimiter || ".", m = n.maxDepth, o = n.transformKey || A, f = {};
|
|
10
|
+
function i(s, u, r) {
|
|
11
|
+
r = r || 1, Object.keys(s).forEach(function(c) {
|
|
12
|
+
const e = s[c], x = n.safe && Array.isArray(e), a = Object.prototype.toString.call(e), O = d(e), b = a === "[object Object]" || a === "[object Array]", y = u ? u + l + o(c) : o(c);
|
|
13
|
+
if (!x && !O && b && Object.keys(e).length && (!n.maxDepth || r < m))
|
|
14
|
+
return i(e, y, r + 1);
|
|
15
|
+
f[y] = e;
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
return i(t), f;
|
|
19
|
+
}
|
|
20
|
+
export {
|
|
21
|
+
B as flatten
|
|
22
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { jsx as e } from "react/jsx-runtime";
|
|
2
|
+
const o = ({ path: r }) => {
|
|
3
|
+
const t = {
|
|
4
|
+
[a]: r
|
|
5
|
+
};
|
|
6
|
+
return /* @__PURE__ */ e(
|
|
7
|
+
"div",
|
|
8
|
+
{
|
|
9
|
+
role: "alert",
|
|
10
|
+
"aria-hidden": "true",
|
|
11
|
+
style: { display: "none" },
|
|
12
|
+
...t
|
|
13
|
+
}
|
|
14
|
+
);
|
|
15
|
+
}, i = o, a = "data-grnwld-rhf-utils-marker-path";
|
|
16
|
+
export {
|
|
17
|
+
a as FormErrorNonFieldMarkerDataHtmlAttribute,
|
|
18
|
+
i as default
|
|
19
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import t from "react";
|
|
2
|
+
import { useFormContext as n, useFormState as a } from "react-hook-form";
|
|
3
|
+
import m from "./useFormOrphanErrors.mjs";
|
|
4
|
+
const c = (e) => {
|
|
5
|
+
const { getValues: o } = n(), { errors: s } = a(), r = m(e);
|
|
6
|
+
t.useEffect(() => {
|
|
7
|
+
Object.keys(r).length && console.error("useFormErrorOrphansToConsoleError", {
|
|
8
|
+
location: window.location,
|
|
9
|
+
values: o(),
|
|
10
|
+
orphans: r
|
|
11
|
+
});
|
|
12
|
+
}, [s, o, r]);
|
|
13
|
+
}, E = c;
|
|
14
|
+
export {
|
|
15
|
+
E as default
|
|
16
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import "react";
|
|
2
|
+
import n from "../useFormErrorsOnChange.mjs";
|
|
3
|
+
import { getOrphanFieldErrorsWithMarkerCheck as t } from "./utils.mjs";
|
|
4
|
+
const e = (o) => n((r) => {
|
|
5
|
+
if (JSON.stringify(r) === "{}")
|
|
6
|
+
return {};
|
|
7
|
+
if (!o.current)
|
|
8
|
+
throw console.error(r), new Error("This should not happen.");
|
|
9
|
+
return t(r, o.current);
|
|
10
|
+
}), p = e;
|
|
11
|
+
export {
|
|
12
|
+
p as default
|
|
13
|
+
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { isFormErrorPathRoot as l } from "../roots/utils.mjs";
|
|
2
|
+
import { getFieldErrorsByFilter as e } from "../utils.mjs";
|
|
3
|
+
import { FormErrorNonFieldMarkerDataHtmlAttribute as m } from "./FormErrorNonFieldMarker.mjs";
|
|
4
|
+
const n = (o) => e(o, (r, t) => t[r + ".ref"] === void 0 && // no ref
|
|
5
|
+
!l(r)), p = (o, r) => {
|
|
6
|
+
const t = n(o);
|
|
7
|
+
return e(
|
|
8
|
+
t,
|
|
9
|
+
// if doesn't have alert, then it's an orphan
|
|
10
|
+
(i) => !r.querySelector(
|
|
11
|
+
`[role="alert"][${m}="${i}"]`
|
|
12
|
+
)
|
|
13
|
+
);
|
|
14
|
+
};
|
|
15
|
+
export {
|
|
16
|
+
n as getOrphanFieldErrors,
|
|
17
|
+
p as getOrphanFieldErrorsWithMarkerCheck
|
|
18
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { flatten as f } from "../../node_modules/flat/index.mjs";
|
|
2
|
+
import { get as d } from "react-hook-form";
|
|
3
|
+
const p = (e, t) => i(e, t).reduce((r, s) => (r[s] = d(e, s), r), {}), F = (e, t) => i(e, t).map((r) => d(e, r)), i = (e, t) => {
|
|
4
|
+
const o = f(e);
|
|
5
|
+
return Object.keys(o).filter((n) => n.match(l)).map(
|
|
6
|
+
(n) => n.replace(l, "")
|
|
7
|
+
).filter(
|
|
8
|
+
(n) => t ? t(n, o) : !0
|
|
9
|
+
);
|
|
10
|
+
}, l = /\.ref$/;
|
|
11
|
+
export {
|
|
12
|
+
F as getFieldErrorListByFilter,
|
|
13
|
+
p as getFieldErrorsByFilter
|
|
14
|
+
};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { default as FormErrorNonFieldMarker, FormErrorNonFieldMarkerDataHtmlAttribute, } from './rhf/errors/orphans/FormErrorNonFieldMarker';
|
|
2
|
+
export { default as useFormErrorOrphansToConsoleError } from './rhf/errors/orphans/useFormErrorOrphansToConsoleError';
|
|
3
|
+
export { default as useFormOrphanErrors } from './rhf/errors/orphans/useFormOrphanErrors';
|
|
4
|
+
export { default as useFormRootFieldErrors } from './rhf/errors/roots/useFormRootFieldErrors';
|
|
5
|
+
export { default as useFormErrorsEffect } from './rhf/errors/useFormErrorsEffect';
|
|
6
|
+
export { default as useFormErrorsOnChange } from './rhf/errors/useFormErrorsOnChange';
|
|
7
|
+
export { default as useFormRequestSubmit } from './rhf/errors/useFormRequestSubmit';
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/// <reference types="react" />
|
|
2
|
+
type Props = {
|
|
3
|
+
/** Flat field name. (e.g., `address.street`) */
|
|
4
|
+
path: string;
|
|
5
|
+
};
|
|
6
|
+
/**
|
|
7
|
+
* Empty marker to indicate a non-field error is presented to user.
|
|
8
|
+
*/
|
|
9
|
+
declare const FormErrorNonFieldMarker: React.FC<Props>;
|
|
10
|
+
export default FormErrorNonFieldMarker;
|
|
11
|
+
export declare const FormErrorNonFieldMarkerDataHtmlAttribute = "data-grnwld-rhf-utils-marker-path";
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { default as React } from 'react';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Orphans are field errors that do not have a ref.
|
|
5
|
+
* (Most commonly `root(.*)`, but these are filtered out. They are handled by `useFormRootFieldErrors`.)
|
|
6
|
+
* Outputs to `console.error` all errors with `ref: undefined` that do not begin with `root(.*)`.
|
|
7
|
+
* Sentry (or equivalent) should capture.
|
|
8
|
+
*/
|
|
9
|
+
declare const useFormErrorOrphansToConsoleError: (formRef: React.RefObject<HTMLFormElement>) => void;
|
|
10
|
+
export default useFormErrorOrphansToConsoleError;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { default as React } from 'react';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Root field errors from form.
|
|
5
|
+
* These are typically displayed at the bottom of the form.
|
|
6
|
+
* (Root errors have to processed this way due to (potentially) recursive types.)
|
|
7
|
+
*/
|
|
8
|
+
declare const useFormOrphanErrors: (formRef: React.RefObject<HTMLFormElement>) => Record<string, import('react-hook-form').FieldError>;
|
|
9
|
+
export default useFormOrphanErrors;
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { FieldErrors } from 'react-hook-form';
|
|
2
|
+
|
|
3
|
+
export declare const getOrphanFieldErrors: (errors: FieldErrors) => Record<string, import('react-hook-form').FieldError>;
|
|
4
|
+
export declare const getOrphanFieldErrorsWithMarkerCheck: (errors: FieldErrors, formElement: HTMLFormElement) => Record<string, import('react-hook-form').FieldError>;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { FieldErrors } from 'react-hook-form';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Determine if an error path is root. (e.g., `root` or `root.nested`)
|
|
5
|
+
* @param path Flat path. (e.g., 'root.nested')
|
|
6
|
+
*/
|
|
7
|
+
export declare const isFormErrorPathRoot: (path: string) => boolean;
|
|
8
|
+
export declare const getRootFieldErrorList: (errors: FieldErrors) => import('react-hook-form').FieldError[];
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Get callback to trigger submit manually on form.
|
|
3
|
+
* @link https://developer.mozilla.org/docs/Web/API/HTMLFormElement/requestSubmit
|
|
4
|
+
*/
|
|
5
|
+
declare const useFormRequestSubmit: (ref: React.RefObject<HTMLFormElement>) => HTMLFormElement['requestSubmit'];
|
|
6
|
+
export default useFormRequestSubmit;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { FieldError, FieldErrors } from 'react-hook-form';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @returns A flattened {@link FieldErrors} record.
|
|
5
|
+
*/
|
|
6
|
+
export declare const getFieldErrorsByFilter: (errors: FieldErrors, where?: ((path: string, flattenedErrors: Record<string, unknown>) => boolean) | undefined) => Record<string, FieldError>;
|
|
7
|
+
/**
|
|
8
|
+
* @returns An array of field errors. (Not a record.)
|
|
9
|
+
*/
|
|
10
|
+
export declare const getFieldErrorListByFilter: (errors: FieldErrors, where?: ((path: string, flattenedErrors: Record<string, unknown>) => boolean) | undefined) => FieldError[];
|
package/package.json
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@paragrav/rhf-utils",
|
|
3
|
+
"author": "paragrav.dev",
|
|
4
|
+
"license": "MIT",
|
|
5
|
+
"version": "0.0.16",
|
|
6
|
+
"description": "Integration utilities for react-hook-form.",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"files": [
|
|
9
|
+
"dist"
|
|
10
|
+
],
|
|
11
|
+
"main": "./dist/cjs/main.js",
|
|
12
|
+
"module": "./dist/esm/main.mjs",
|
|
13
|
+
"exports": {
|
|
14
|
+
".": {
|
|
15
|
+
"require": {
|
|
16
|
+
"types": "./dist/types/main.d.ts",
|
|
17
|
+
"default": "./dist/cjs/main.js"
|
|
18
|
+
},
|
|
19
|
+
"import": {
|
|
20
|
+
"types": "./dist/types/main.d.ts",
|
|
21
|
+
"default": "./dist/esm/main.mjs"
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
"sideEffects": false,
|
|
26
|
+
"scripts": {
|
|
27
|
+
"build": "npm run validate && npm run build:cjs && npm run build:esm",
|
|
28
|
+
"build:cjs": "tsc -p ./tsconfig.cjs.json",
|
|
29
|
+
"build:esm": "vite build",
|
|
30
|
+
"lint": "eslint --ignore-path ./.gitignore --ext ts,tsx --report-unused-disable-directives --max-warnings 0 .",
|
|
31
|
+
"preinstall": "npx only-allow npm",
|
|
32
|
+
"prepare": "husky",
|
|
33
|
+
"prepublishOnly": "npm run build",
|
|
34
|
+
"test": "vitest",
|
|
35
|
+
"test:ct": "playwright test -c playwright-ct.config.ts",
|
|
36
|
+
"test:ct:ui": "playwright test -c playwright-ct.config.ts --ui",
|
|
37
|
+
"tsc": "tsc",
|
|
38
|
+
"validate": "npm run tsc & npm run lint & npm run test & npm run test:ct"
|
|
39
|
+
},
|
|
40
|
+
"dependencies": {
|
|
41
|
+
"flat": "^6.0.1"
|
|
42
|
+
},
|
|
43
|
+
"peerDependencies": {
|
|
44
|
+
"react-hook-form": "^7"
|
|
45
|
+
},
|
|
46
|
+
"devDependencies": {
|
|
47
|
+
"@hookform/resolvers": "^3.3.4",
|
|
48
|
+
"@playwright/experimental-ct-react": "^1.42.1",
|
|
49
|
+
"@rollup/plugin-typescript": "^11.1.6",
|
|
50
|
+
"@types/node": "^18.19.26",
|
|
51
|
+
"@types/react": "^18.2.69",
|
|
52
|
+
"@typescript-eslint/eslint-plugin": "^7.3.1",
|
|
53
|
+
"@typescript-eslint/parser": "^7.3.1",
|
|
54
|
+
"@vitejs/plugin-react": "^4.2.1",
|
|
55
|
+
"eslint": "^8.57.0",
|
|
56
|
+
"eslint-config-prettier": "^9.1.0",
|
|
57
|
+
"eslint-plugin-import": "^2.29.1",
|
|
58
|
+
"eslint-plugin-react": "^7.34.1",
|
|
59
|
+
"eslint-plugin-react-hooks": "^4.6.0",
|
|
60
|
+
"eslint-plugin-simple-import-sort": "^12.0.0",
|
|
61
|
+
"husky": "^9.0.11",
|
|
62
|
+
"lint-staged": "^15.2.2",
|
|
63
|
+
"prettier": "^3.2.5",
|
|
64
|
+
"react": "^18.2.0",
|
|
65
|
+
"react-dom": "^18.2.0",
|
|
66
|
+
"rollup": "^4.13.0",
|
|
67
|
+
"typescript": "^5.4.3",
|
|
68
|
+
"vite": "^5.2.5",
|
|
69
|
+
"vite-plugin-dts": "^3.8.1",
|
|
70
|
+
"vite-tsconfig-paths": "^4.3.2",
|
|
71
|
+
"vitest": "^1.4.0",
|
|
72
|
+
"zod": "^3.22.4"
|
|
73
|
+
},
|
|
74
|
+
"engines": {
|
|
75
|
+
"node": ">=18.19.1",
|
|
76
|
+
"npm": "^10.2.4"
|
|
77
|
+
},
|
|
78
|
+
"packageManager": "npm@10.2.4",
|
|
79
|
+
"repository": {
|
|
80
|
+
"type": "git",
|
|
81
|
+
"url": "https://github.com/paragrav/rhf-utils"
|
|
82
|
+
},
|
|
83
|
+
"bugs": {
|
|
84
|
+
"url": "https://github.com/paragrav/rhf-utils/issues"
|
|
85
|
+
},
|
|
86
|
+
"homepage": "https://github.com/paragrav/rhf-utils",
|
|
87
|
+
"keywords": [
|
|
88
|
+
"react-hook-form",
|
|
89
|
+
"zod",
|
|
90
|
+
"form",
|
|
91
|
+
"react"
|
|
92
|
+
]
|
|
93
|
+
}
|