Skip to content

Commit

Permalink
Merge pull request #36 from klm-lab/dev
Browse files Browse the repository at this point in the history
Remove copy, regex, string in favor of custom
  • Loading branch information
klm-lab authored Dec 17, 2023
2 parents f1dca53 + d425837 commit 58f79bf
Show file tree
Hide file tree
Showing 21 changed files with 232 additions and 271 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/3.1.2?style=for-the-badge
[size-shield]: https://img.shields.io/bundlephobia/minzip/aio-inputs/3.1.3?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 package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "aio-inputs",
"version": "3.1.2",
"version": "3.1.3",
"description": "An opinionated inputs state manager for react",
"scripts": {
"build": "npm ci && npm run build:noci",
Expand Down
12 changes: 2 additions & 10 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,7 @@
export { useInputs, trackInputs } from "./inputs/hook";
export { required } from "./inputs/validations/required";
export { number, min, max } from "./inputs/validations/number";
export {
minLength,
maxLength,
minLengthWithoutSpace,
maxLengthWithoutSpace
} from "./inputs/validations/length";
export { number, min, max } 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 { copy } from "./inputs/validations/copy";
export { match } from "./inputs/validations/match";
export { asyncCustom } from "./inputs/validations/asyncCustom";
31 changes: 19 additions & 12 deletions src/inputs/handlers/changes.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {
GetFile,
Input,
InputStore,
InternalInput,
Expand All @@ -7,26 +8,32 @@ import {
} from "../../types";
import { validate, validateState } from "../validations";
import { extractValues, setValue } from "./values";
import { retrieveFile } from "./files";
import { parseFile } from "./files";
import { CHECKBOX, FILE, RADIO } from "../../util/helper";
import { setCRValues } from "./checkboxAndRadio";

export const initValue = (
objKey: string,
value: Unknown,
store: InputStore,
type: string
type: string,
getFile?: GetFile
) => {
// Clone inputs
const input = store.get(`i.${objKey}`);

if (type === FILE) {
[value].flat().forEach((v: Unknown, index: number) => {
retrieveFile(v, store, objKey, index);
input.files[index] = parseFile(objKey, store, v, !!getFile, {} as File);
getFile &&
getFile(v).then((r: Unknown) => {
store.set((ref) => {
const f = ref.i[objKey].files[index];
f.fetching = false;
f.file = r as File;
});
});
});
return;
}
if (type === RADIO) {
} else if (type === RADIO) {
// Check right radio input
setValue(input, input.value === value);
} else if (type === CHECKBOX) {
Expand Down Expand Up @@ -65,10 +72,11 @@ export const nextChange = (
} else {
setValue(input, value, false);
}
entry[objKey].validationFailed = false;
// we sync handlers
syncChanges(
store,
setValidAndEm(entry, objKey, validate(store, entry, objKey, value))
setTouchedEm(entry, objKey, validate(store, entry, objKey, value))
);
// run after changes
const r = (input as InternalInput).afterChange;
Expand All @@ -79,14 +87,13 @@ export const nextChange = (
});
};

// Set touched, valid and error message
export const setValidAndEm = (
// Set touched, and error message and return the entry (inputs)
export const setTouchedEm = (
entry: ObjectInputs<string>,
objKey: string,
em: Unknown
) => {
entry[objKey].touched = true;
entry[objKey].valid = !em;
entry[objKey].errorMessage = em;
return entry;
};
Expand All @@ -98,6 +105,6 @@ export const syncChanges = (store: InputStore, data: ObjectInputs<string>) => {
// isTouched
ref.t = true;
// new valid state
ref.iv = validateState(data).iv;
ref.iv = validateState(data);
});
};
7 changes: 3 additions & 4 deletions src/inputs/handlers/checkboxAndRadio.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,8 @@ export const setCRValues = (
fn?: (I: Input) => Unknown
) => {
store.ev[name].o.forEach((c: string) => {
const inp = entry[c];
fn && fn(inp);
inp.valid = true;
inp.errorMessage = null;
fn && fn(entry[c]);
entry[c].valid = true;
entry[c].errorMessage = "";
});
};
64 changes: 27 additions & 37 deletions src/inputs/handlers/files.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,25 @@ export const createFiles = (
return parsed;
};

// export const createFiles = (
// value: Unknown,
// store: InputStore,
// objKey: string,
// input: Input
// ) => {
// const parsed: Unknown = input.merge ? { ...input.files } : {};
// if (!input.merge) {
// // revoke preview file url
// keys(input.files).forEach(R);
// }
// value.forEach((f: Unknown) => {
// // parse and add new file
// const url = C(f);
// parsed[url] = parseFile(objKey, store, url, false, f);
// });
// return parsed;
// };

// return result in r and files in f
const filterOrFindIndex = (
ref: IPS,
Expand All @@ -38,25 +57,25 @@ export const parseFile = (
url: string,
fetching: boolean,
file: File
): ParsedFile => {
) => {
const key = newKey();
return {
fetching,
file,
key,
url,
update: null,
loaded: false,
// update: null,
// loaded: false,
onLoad: () => {
!store.get("c").pid && R(url);
store.set((ref) => {
// const index = ref.i[objKey].files.findIndex((f) => f.key === key);
const { r } = filterOrFindIndex(
const index = filterOrFindIndex(
ref,
objKey,
(f: ParsedFile) => f.key === key
);
ref.i[objKey].files[r].loaded = true;
).r;
ref.i[objKey].files[index].loaded = true;
});
},
selfUpdate: (data: Unknown) => {
Expand Down Expand Up @@ -90,40 +109,11 @@ export const parseFile = (
input.valid = !em;
input.errorMessage = em;
// Validate form
ref.iv = validateState(ref.i).iv;
ref.iv = validateState(ref.i);
});
}
};
} as ParsedFile;
};

export const retrieveFile = (
value: Unknown,
store: InputStore,
id: string,
index: number
) => {
const fileConfig = store.fc;
store.set((ref) => {
ref.i[id].files[index] = parseFile(
id,
store,
value,
!!fileConfig.getBlob, // true is getBlob is present
{} as File
);
ref.i[id].valid = true;
});
if (fileConfig.getBlob) {
Promise.resolve(fileConfig.getBlob(value)).then((r) => {
store.set((ref) => {
const f = ref.i[id].files[index];
f.fetching = false;
f.file = r as File;
});
});
}
};

// Remove useless tools for db
export const cleanFiles = (files: ParsedFile[]) => {
// Set type to any to break the contract type
Expand Down
3 changes: 2 additions & 1 deletion src/inputs/handlers/values.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { type GetValue, Input, ObjectInputs, type Unknown } from "../../types";
import { FILE } from "../../util/helper";

export const extractValues = (state: ObjectInputs<string>) => {
const result = {} as { [k in string]: Unknown };
Expand All @@ -13,7 +14,7 @@ export const setValue = (input: Input, value: Unknown, cr: boolean = true) => {
if (cr) {
input.checked = value;
input.props.checked = value;
} else {
} else if (input.type !== FILE) {
input.value = value;
input.props.value = value;
}
Expand Down
5 changes: 3 additions & 2 deletions src/inputs/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {
Unknown
} from "../types";
import { finalizeInputs, touchInput, transformToArray } from "../util";
import { persist } from "../util/helper";
import { newSet, persist } from "../util/helper";
import { extractValues } from "./handlers/values";

export const createInputs = (initialState: Unknown, config: InputConfig) => {
Expand Down Expand Up @@ -45,8 +45,9 @@ export const createInputs = (initialState: Unknown, config: InputConfig) => {
const reset = () => {
// clear possibly checkbox value
// st.n => contains all inputs names
// st.i => contains all initial selected value
// st.ev => contains all inputs extra data and validation like total of inputs, selected inputs etc...
st.n.forEach((n: string) => st.ev[n].s.clear());
st.n.forEach((n: string) => (st.ev[n].s = newSet(st.ev[n].i)));
// reset with initial value
st.set((ref) => {
// ref.i => is the inputs
Expand Down
72 changes: 33 additions & 39 deletions src/inputs/validations/asyncCustom.ts
Original file line number Diff line number Diff line change
@@ -1,55 +1,49 @@
import {
AsyncValidateInput,
AsyncValidationParams,
CustomAsyncValidationType
CustomAsyncValidationType,
Unknown
} from "../../types";
import { syncChanges } from "../handlers/changes";

const asyncCallback = ({ em, ok, st, f }: AsyncValidationParams) => {
// Clone inputs
const entry = st.get("i");
const input = entry[ok];
// Finish calling server
entry[ok].validating = false;
entry[ok].validationFailed = !!f;

if (f) {
syncChanges(st, entry);
return;
}
// Add server validation only actual data is valid
entry[ok].valid = input.errorMessage ? false : !em;
// Add server error message only actual data is valid else keep actual error Message
entry[ok].errorMessage = input.errorMessage ?? em;

// Sync handlers
syncChanges(st, entry);
};

export const asyncCustom = (
callback: CustomAsyncValidationType
): AsyncValidateInput => {
return ({ va, ip, ok, st }) => {
return ({ i, va, ip, ok, st }) => {
const timeoutKeys = st.a;
// clear previous task
clearTimeout(timeoutKeys[ip.key]);
// new task with the input key
timeoutKeys[ip.key] = setTimeout(
() => {
// Save the time
// Save the time because, changes can happen before response
const ST = timeoutKeys[ip.key];
Promise.resolve(callback(va))
.then((em) => {
/* we check if time match the request id time
* If not, that means, another request has been sent.
* So we wait for that response
* */
if (ST === timeoutKeys[ip.key]) {
asyncCallback({ em, ok, st });
}
})
.catch((error) => {
console.error(error);
asyncCallback({ f: true, ok, st });
});
const onError = () => {
i[ok].validating = false;
i[ok].validationFailed = true;
syncChanges(st, i);
};

const onSuccess = (errorMessage: Unknown) => {
/* we check if time match the request id time
* If not, that means, another request has been sent.
* So we wait for that response
* */
if (ST === timeoutKeys[ip.key]) {
// sync with latest inputs state
i = st.get(`i`);
// Get latest input errorMessage
const em = i[ok].errorMessage;
// Add server validation because it is always false before calling server
i[ok].valid = !errorMessage;
// Add server error message only if actual data is valid else keep actual error Message
// '' ?? 'not empty' is a js bug and will return '', so we go the other way
//i[ok].errorMessage = em ?? errorMessage;
i[ok].errorMessage = !em ? errorMessage : em;
i[ok].validating = false;
syncChanges(st, i);
}
};
callback(va, onSuccess, onError);
},
st.get("c.asyncDelay") ?? 800
);
Expand Down
20 changes: 10 additions & 10 deletions src/inputs/validations/copy.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { ValidateInput, ValidationStateType } from "../../types";
import { validate } from "./index";

export const copy = (
name: string,
omittedRules?: (keyof ValidationStateType)[]
): ValidateInput => {
return ({ i, ok, st, va, omr }) =>
validate(st, i!, st.ev[name].k, va, omittedRules ?? omr, ok);
};
// import { ValidateInput, ValidationStateType } from "../../types";
// import { validate } from "./index";
//
// export const copy = (
// name: string,
// omittedRules?: (keyof ValidationStateType)[]
// ): ValidateInput => {
// return ({ i, ok, st, va, omr }) =>
// validate(st, i, st.ev[name].k, va, omittedRules ?? omr, ok);
// };
6 changes: 3 additions & 3 deletions src/inputs/validations/email.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { Unknown, ValidateInput } from "../../types";

export const email = (em?: Unknown): ValidateInput => {
export const email = (errorMessage: Unknown): ValidateInput => {
return ({ va }) =>
/^(([^<>()\]\\.,;:\s@"]+(\.[^<>()\]\\.,;:\s@"]+)*)|(".+"))@(([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/.test(
va?.toLowerCase()
)
? null
: em;
? ""
: errorMessage;
};
Loading

0 comments on commit 58f79bf

Please sign in to comment.