Skip to content

Commit

Permalink
Split validation to tree shake
Browse files Browse the repository at this point in the history
  • Loading branch information
klm-lab committed Dec 3, 2023
1 parent 85ad197 commit 075a5ab
Show file tree
Hide file tree
Showing 28 changed files with 594 additions and 1,007 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ An input state management for React. It comes with useful common validations if
[MIT][license-url]


[size-shield]: https://img.shields.io/bundlephobia/minzip/aio-inputs/2.2.0?style=for-the-badge
[size-shield]: https://img.shields.io/bundlephobia/minzip/aio-inputs/3.0.0?style=for-the-badge

[license-shield]: https://img.shields.io/github/license/klm-lab/inputs?style=for-the-badge

Expand Down
2 changes: 1 addition & 1 deletion examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ Tracking inputs [HERE][no-tracking-link]
[MIT][license-url]


[size-shield]: https://img.shields.io/bundlephobia/minzip/aio-inputs/2.2.0?style=for-the-badge
[size-shield]: https://img.shields.io/bundlephobia/minzip/aio-inputs/3.0.0?style=for-the-badge

[license-shield]: https://img.shields.io/github/license/klm-lab/inputs?style=for-the-badge

Expand Down
12 changes: 6 additions & 6 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 5 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "aio-inputs",
"version": "2.2.0",
"version": "3.0.0",
"description": "An opinionated inputs state manager for react",
"scripts": {
"build": "npm ci && npm run build:noci",
Expand Down Expand Up @@ -57,6 +57,9 @@
"rollup-plugin-copy": "^3.5.0",
"typescript": "^5.2.2"
},
"types": "./index.d.ts",
"main": "./index.js",
"module": "./index.mjs",
"exports": {
".": {
"types": "./index.d.ts",
Expand All @@ -74,7 +77,7 @@
"state"
],
"dependencies": {
"aio-store": "^2.4.42"
"aio-store": "^2.4.43"
},
"peerDependencies": {
"react": "^18.2.0"
Expand Down
14 changes: 12 additions & 2 deletions rollup.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,11 @@ module.exports = [
{
file: "lib/index.js",
format: "cjs",
plugins: [terser()]
plugins: [
terser({
compress: true
})
]
},
// {
// file: "lib/index.min.js",
Expand All @@ -21,7 +25,13 @@ module.exports = [
{
file: "lib/index.mjs",
format: "esm",
plugins: [terser()]
plugins: [
terser({
ecma: 2020,
compress: true,
module: true
})
]
}
// {
// file: "lib/index.esm.min.mjs",
Expand Down
15 changes: 15 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,17 @@
export { useInputs } from "./inputs";
export { trackInputs } from "./tracking";
export { required } from "./inputs/validations/required";
export { number, min, max } from "./inputs/validations/number";
export {
minLength,
maxLength,
minLengthWithoutSpace,
maxLengthWithoutSpace
} from "./inputs/validations/length";
export { email } from "./inputs/validations/email";
export { regex } from "./inputs/validations/regex";
export { startsWith, endsWith } from "./inputs/validations/string";
export { copy } from "./inputs/validations/copy";
export { match } from "./inputs/validations/match";
export { custom } from "./inputs/validations/custom";
export { asyncCustom } from "./inputs/validations/asyncCustom";
125 changes: 59 additions & 66 deletions src/inputs/handlers/changes.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import type {
Helper,
Input,
InputConfig,
InputStore,
ObjectInputs,
ParsedFile
ParsedFile,
Unknown
} from "../../types";
import { AsyncValidationParams } from "../../types";
import { asyncValidation, validate } from "../../util/validation";
import { validate } from "../validations";
import { validateState } from "../../util";
import { createFiles } from "./files";
import { createSelectFiles } from "./select";
Expand All @@ -16,112 +16,105 @@ import { createCheckboxValue } from "./checkbox";
const asyncCallback = ({
valid: asyncValid,
em: asyncErrorMessage,
entry,
input,
store,
failed,
helper
}: AsyncValidationParams) => {
// Clone inputs
const clone = store.get("entry");
const ID = entry.id;
const entry = store.get("entry");
const id = input.id;

if (failed) {
clone[ID].validating = false;
clone[ID].asyncValidationFailed = true;
syncChanges(store, clone);
entry[id].validating = false;
entry[id].asyncValidationFailed = true;
syncChanges(store, entry);
return;
}

// Revalidate input, maybe a change occurs before server response
const { valid, em } = validate(helper, clone, ID, clone[ID].value);
const { valid, em } = validate(helper, entry, id, entry[id].value);
// Add server validation only actual data is valid
clone[ID].valid = valid && asyncValid;
entry[id].valid = valid && asyncValid;
// Add server error message only actual data is valid else keep actual error Message
clone[ID].errorMessage = valid ? asyncErrorMessage : em;
entry[id].errorMessage = valid ? asyncErrorMessage : em;
// Finish calling server
clone[ID].validating = false;
entry[id].validating = false;
// Sync handlers
syncChanges(store, clone);
syncChanges(store, entry);
};

const onChange = (
input: Input,
element: HTMLInputElement | HTMLSelectElement,
element: (HTMLInputElement | HTMLSelectElement) & { ip: Input },
store: InputStore,
config: InputConfig,
isEvent: boolean,
helper: Helper
) => {
// Clone inputs
const clone = store.get("entry");
const ID = input.id;
const entry = store.get("entry");
const id = element.ip.id;
const input = element.ip;
// Get the value based on type
const value =
input.type === "file"
? createFiles(
(element as HTMLInputElement).files,
clone,
ID,
store,
config,
helper
)
? createFiles(element as Unknown, store, helper)
: input.type === "select"
? input.multiple
? createSelectFiles(isEvent, element as HTMLSelectElement, clone, ID)
: element.value !== "" && element.value !== clone[ID].placeholder
? createSelectFiles(element as Unknown)
: element.value !== "" && element.value !== entry[id].placeholder
? element.value
: ""
: element.value;

const toValidate =
input.type === "checkbox" ? createCheckboxValue(clone, ID) : value;

// Validate inputs
const { valid, em } = validate(helper, clone, ID, toValidate);
// Handle type file
if (input.type === "file") {
clone[ID].files = value as ParsedFile[];
entry[id].files = value as ParsedFile[];
} else if (input.type === "radio") {
// Check right radio input
for (const key in clone) {
if (clone[key].type === "radio" && clone[key].name === clone[ID].name) {
clone[key].checked = clone[key].value === value;
clone[key].props.checked = clone[key].value === value;
clone[key].valid = true;
for (const key in entry) {
if (entry[key].type === "radio" && entry[key].name === entry[id].name) {
entry[key].checked = entry[key].value === value;
entry[key].props.checked = entry[key].value === value;
entry[key].valid = true;
}
}
} else if (input.type === "checkbox") {
// Toggle the checkbox input
clone[ID].checked = !clone[ID].checked;
clone[ID].props.checked = !clone[ID].props.checked;
entry[id].checked = !entry[id].checked;
entry[id].props.checked = !entry[id].props.checked;
} else {
clone[ID].value = value;
clone[ID].props.value = value;
entry[id].value = value;
entry[id].props.value = value;
}

const toValidate =
input.type === "checkbox" ? createCheckboxValue(entry, id) : value;

const { valid, em } = validate(helper, entry, id, toValidate);

// Touched input
clone[ID].touched = true;
entry[id].touched = true;
// Set valid to false if async is present else keep validation result
clone[ID].valid = (input.validation?.asyncCustom as unknown) ? false : valid;
entry[id].valid = (input.validation?.asyncCustom as unknown) ? false : valid;
// Set errorMessage only if invalid if not keep the default errorMessage structure, Object or undefined
clone[ID].errorMessage = !valid ? em : em instanceof Object ? {} : undefined;
/* if it is valid then if async is true, we set validating to true otherwise false
* valid === false mean no need to call async,
* valid === true means we can call async if async is set to true by the user.
*
* validating prop is responsible to show async validation loading
* */
// If all change are valid and async is there, we set valid to false else true
clone[ID].validating = valid ? !!input.validation?.asyncCustom : false;
entry[id].errorMessage = !valid ? em : em instanceof Object ? {} : undefined;

entry[id].validating = valid ? !!input.validation?.asyncCustom : false;
// asyncValidationFailed by default because call asyncCustom
clone[ID].asyncValidationFailed = false;
entry[id].asyncValidationFailed = false;

// if valid and async is there, we call async validation
valid &&
(input.validation?.asyncCustom as unknown) &&
asyncValidation(store, helper, clone, ID, value, asyncCallback);
input.validation?.asyncCustom({
store,
helper,
input,
target: id,
value,
asyncCallback
});
// we sync handlers
syncChanges(store, clone);
syncChanges(store, entry);
};

const syncChanges = (store: InputStore, data: ObjectInputs<string>) => {
Expand All @@ -132,17 +125,15 @@ const syncChanges = (store: InputStore, data: ObjectInputs<string>) => {
};

export const inputChange = (
value: any,
key: string,
entry: ObjectInputs<string>,
value: Unknown,
input: Input,
store: InputStore,
config: InputConfig,
helper: Helper
) => {
const isEvent = typeof value.preventDefault === "function";
const element = {} as any;
const element = {} as Unknown;
if (!isEvent) {
if (entry[key].type === "file") {
if (input.type === "file") {
element.files = value;
} else {
element.value = value;
Expand All @@ -152,5 +143,7 @@ export const inputChange = (
element.files = value.target.files || [];
element.selectedOptions = value.target.selectedOptions || [];
}
onChange(entry[key], element, store, config, isEvent, helper);
element.isEvent = isEvent;
element.ip = input;
onChange(element, store, helper);
};
14 changes: 7 additions & 7 deletions src/inputs/handlers/checkbox.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
import type { ObjectInputs } from "../../types";

export const createCheckboxValue = (
clone: ObjectInputs<string>,
entry: ObjectInputs<string>,
ID: string,
userChange = true
) => {
const selected = [] as string[];
userChange && !clone[ID].checked && selected.push(clone[ID].value);
for (const key in clone) {
userChange && !entry[ID].checked && selected.push(entry[ID].value);
for (const key in entry) {
if (
clone[key].type === "checkbox" &&
entry[key].type === "checkbox" &&
(userChange ? key !== ID : true) &&
clone[key].name === clone[ID].name
entry[key].name === entry[ID].name
) {
clone[key].checked && selected.push(clone[key].value);
entry[key].checked && selected.push(entry[key].value);
if (userChange) {
clone[key].valid = true;
entry[key].valid = true;
}
}
}
Expand Down
Loading

0 comments on commit 075a5ab

Please sign in to comment.