diff --git a/README.md b/README.md index 9034e21..02e6ee3 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,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.1.12?style=for-the-badge +[size-shield]: https://img.shields.io/bundlephobia/minzip/aio-inputs/2.1.13?style=for-the-badge [license-shield]: https://img.shields.io/github/license/klm-lab/inputs?style=for-the-badge diff --git a/examples/README.md b/examples/README.md index 2f6ee49..7907a31 100644 --- a/examples/README.md +++ b/examples/README.md @@ -38,7 +38,7 @@ Tracking inputs [HERE][no-tracking-link] [MIT][license-url] -[size-shield]: https://img.shields.io/bundlephobia/minzip/aio-inputs/2.1.12?style=for-the-badge +[size-shield]: https://img.shields.io/bundlephobia/minzip/aio-inputs/2.1.13?style=for-the-badge [license-shield]: https://img.shields.io/github/license/klm-lab/inputs?style=for-the-badge diff --git a/package-lock.json b/package-lock.json index dd98b13..6a368be 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,15 +1,15 @@ { "name": "aio-inputs", - "version": "2.0.9", + "version": "2.1.13", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "aio-inputs", - "version": "2.0.9", + "version": "2.1.13", "license": "MIT", "dependencies": { - "aio-store": "^2.4.3" + "aio-store": "^2.4.41" }, "devDependencies": { "@rollup/plugin-commonjs": "^25.0.5", @@ -727,9 +727,12 @@ } }, "node_modules/aio-store": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/aio-store/-/aio-store-2.4.3.tgz", - "integrity": "sha512-6H7Q2ad4E1i9SBlBvRbJILnKgkMhnf1lojNRvpstlW9FEukYgr+LAC5xqMGdQElIFuVUNceLRnqtHAVxZdPGSw==" + "version": "2.4.41", + "resolved": "https://registry.npmjs.org/aio-store/-/aio-store-2.4.41.tgz", + "integrity": "sha512-+jgaF0XIudHQqZkZQY4+ouK0j8B0MONogqCOmTE8oTopftA1EQM550kES0Smu16hU5OrIasDFP/J+KFr1Mm3Dw==", + "peerDependencies": { + "react": "^18.2.0" + } }, "node_modules/ajv": { "version": "6.12.6", diff --git a/package.json b/package.json index 66e0a61..4f21118 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "aio-inputs", - "version": "2.1.12", + "version": "2.1.13", "description": "An opinionated inputs state manager for react", "scripts": { "build": "npm ci && npm run build:noci", @@ -79,7 +79,7 @@ "state" ], "dependencies": { - "aio-store": "^2.4.3" + "aio-store": "^2.4.41" }, "peerDependencies": { "react": "^18.2.0" diff --git a/src/inputs/handlers/changes.ts b/src/inputs/handlers/changes.ts index f7e779c..a60b38b 100644 --- a/src/inputs/handlers/changes.ts +++ b/src/inputs/handlers/changes.ts @@ -1,10 +1,10 @@ import type { - Config, Helper, - InputStore, - ParsedFile, Input, - ObjectInput + InputConfig, + InputStore, + ObjectInputs, + ParsedFile } from "../../types"; import { AsyncValidationParams } from "../../types"; import { asyncValidation, validate } from "../../util/validation"; @@ -48,7 +48,7 @@ const onChange = ( input: Input, element: HTMLInputElement | HTMLSelectElement, store: InputStore, - config: Config, + config: InputConfig, isEvent: boolean, helper: Helper ) => { @@ -66,8 +66,12 @@ const onChange = ( config, helper ) - : input.type === "select" && input.multiple - ? createSelectFiles(isEvent, element as HTMLSelectElement, clone, ID) + : input.type === "select" + ? input.multiple + ? createSelectFiles(isEvent, element as HTMLSelectElement, clone, ID) + : element.value !== "" && element.value !== clone[ID].placeholder + ? element.value + : "" : element.value; const toValidate = @@ -120,7 +124,7 @@ const onChange = ( syncChanges(store, clone); }; -const syncChanges = (store: InputStore, data: ObjectInput) => { +const syncChanges = (store: InputStore, data: ObjectInputs) => { store.set((ref) => { ref.entry = data; ref.isValid = validateState(data).isValid; @@ -130,9 +134,9 @@ const syncChanges = (store: InputStore, data: ObjectInput) => { export const inputChange = ( value: any, key: string, - entry: ObjectInput, + entry: ObjectInputs, store: InputStore, - config: Config, + config: InputConfig, helper: Helper ) => { const isEvent = diff --git a/src/inputs/handlers/checkbox.ts b/src/inputs/handlers/checkbox.ts index aa69461..3058483 100644 --- a/src/inputs/handlers/checkbox.ts +++ b/src/inputs/handlers/checkbox.ts @@ -1,7 +1,7 @@ -import type { ObjectInput } from "../../types"; +import type { ObjectInputs } from "../../types"; export const createCheckboxValue = ( - clone: ObjectInput, + clone: ObjectInputs, ID: string, userChange = true ) => { diff --git a/src/inputs/handlers/files.ts b/src/inputs/handlers/files.ts index 6ab84f9..c9fafaf 100644 --- a/src/inputs/handlers/files.ts +++ b/src/inputs/handlers/files.ts @@ -1,20 +1,20 @@ import type { - Config, + InputConfig, Helper, InitFileConfig, InputStore, ParsedFile, - ObjectInput + ObjectInputs } from "../../types"; import { validate } from "../../util/validation"; import { validateState } from "../../util"; export const createFiles = ( files: FileList | null, - clone: ObjectInput, + clone: ObjectInputs, ID: string, store: InputStore, - config: Config, + config: InputConfig, helper: Helper ) => { const entry = clone[ID]; @@ -44,10 +44,10 @@ export const createFiles = ( }; export const parseFile = ( - clone: ObjectInput, + clone: ObjectInputs, ID: string, store: InputStore, - config: Config, + config: InputConfig, url: string, gettingFile: boolean, file: File, @@ -87,26 +87,19 @@ export const parseFile = ( }; }; -// export const getFile = async (url: string, fileConfig: InitFileConfig) => { -// const URL = fileConfig.useDefaultProxyUrl -// ? "cors-anywhere.herokuapp.com/" + url -// : fileConfig.proxyUrl -// ? fileConfig.proxyUrl + "/" + url -// : url; -// const blob = await fetch(URL).then((r) => r.blob()); -// const fileName = url.match(/([a-z0-9_-]+\.\w+)(?!.*\/)/gi); -// const name = fileName ? fileName[0] : ""; -// return new File([blob], name, { -// type: blob.type -// }); -// }; +const getFile = (url: string, blob: Blob) => { + const fileName = url.match(/([a-z0-9_-]+\.\w+)(?!.*\/)/gi); + return new File([blob], fileName ? fileName[0] : "", { + type: blob.type + }); +}; export const blobStringJob = ( value: any, store: InputStore, - clone: ObjectInput, + clone: ObjectInputs, ID: string, - config: Config, + config: InputConfig, fileConfig: InitFileConfig, index: number, valid: boolean, @@ -126,28 +119,21 @@ export const blobStringJob = ( ref.entry[ID].valid = valid; }); if (fileConfig.getBlob) { - Promise.resolve(fileConfig.getBlob(value)).then((file) => { + Promise.resolve(fileConfig.getBlob(value)).then((blob) => { store.set((ref) => { ref.entry[ID].files[index].gettingFile = false; - ref.entry[ID].files[index].file = file; + ref.entry[ID].files[index].file = getFile(value, blob); }); }); } - - // getFile(value, fileConfig).then((file) => { - // store.set((ref) => { - // ref.entry[ID].files[index].gettingFile = false; - // ref.entry[ID].files[index].file = file; - // }); - // }); }; export const retrieveBlob = ( value: any, store: InputStore, - clone: ObjectInput, + clone: ObjectInputs, ID: string, - config: Config, + config: InputConfig, fileConfig: InitFileConfig, valid: boolean, helper: Helper diff --git a/src/inputs/handlers/radio.ts b/src/inputs/handlers/radio.ts index 2ca2a72..6b5d8e2 100644 --- a/src/inputs/handlers/radio.ts +++ b/src/inputs/handlers/radio.ts @@ -1,6 +1,6 @@ -import type { ObjectInput } from "../../types"; +import type { ObjectInputs } from "../../types"; -export const radioIsChecked = (clone: ObjectInput, ID: string) => { +export const radioIsChecked = (clone: ObjectInputs, ID: string) => { let isSelected = false; for (const key in clone) { if ( diff --git a/src/inputs/handlers/select.ts b/src/inputs/handlers/select.ts index 73a113e..6aae19c 100644 --- a/src/inputs/handlers/select.ts +++ b/src/inputs/handlers/select.ts @@ -1,16 +1,18 @@ -import type { ObjectInput } from "../../types"; +import type { ObjectInputs } from "../../types"; export const createSelectFiles = ( isEvent: boolean, element: HTMLSelectElement, - clone: ObjectInput, + clone: ObjectInputs, ID: string ) => { let selected = [] as string[]; if (isEvent) { const els = element.selectedOptions; for (let i = 0; i < els.length; i++) { - selected.push(els[i].value); + els[i].value !== "" && + els[i].value !== clone[ID].placeholder && + selected.push(els[i].value); } } else { // Not need to clone, we keep the same reference safely @@ -18,7 +20,9 @@ export const createSelectFiles = ( if (selected.includes(element.value)) { selected = selected.filter((v: any) => v !== element.value); } else { - selected.push(element.value); + element.value !== "" && + element.value !== clone[ID].placeholder && + selected.push(element.value); } } return selected; diff --git a/src/inputs/index.ts b/src/inputs/index.ts index 2d5815a..daa35f5 100644 --- a/src/inputs/index.ts +++ b/src/inputs/index.ts @@ -1,28 +1,29 @@ import type { ArrayStateOutput, ComputeOnceOut, - Config, - CreateArrayInput, - CreateObjectInput, + CreateArrayInputs, + CreateObjectInputs, ForEachCallback, Helper, InitFileConfig, Input, + InputConfig, InputStore, - InternalInput, IsValid, MapCallback, Method, - ObjectInput, + ObjectInputs, ObjStateOutput, StateType, - StringStateOutput + StringStateOutput, + Unknown } from "../types"; import { commonProps, extractValues, lockProps, matchRules, + O, parseValue, touchInput, transformToArray, @@ -35,11 +36,11 @@ import { retrieveBlob } from "./handlers/files"; import { inputChange } from "./handlers/changes"; import { validate } from "../util/validation"; -const init = ( +const initValue = ( input: Input, - value: unknown, + value: Unknown, store: InputStore, - config: Config, + config: InputConfig, fileConfig: InitFileConfig, helper: Helper ) => { @@ -62,7 +63,7 @@ const init = ( clone[ID].props.checked = clone[ID].value === value; } else if (input.type === "checkbox") { // Toggle the checkbox input - const cbV = (value as unknown[]).includes(clone[ID].value); + const cbV = (value as Unknown[]).includes(clone[ID].value); clone[ID].checked = cbV; clone[ID].props.checked = cbV; } else { @@ -77,8 +78,8 @@ const init = ( }); }; -const populate = (state: any, type: StateType, config: Config) => { - const final = {} as CreateObjectInput; +const populate = (state: any, type: StateType, config: InputConfig) => { + const final = {} as CreateObjectInputs; const helper = He(); for (const stateKey in state) { const parseKey = type === "object" ? stateKey : state[stateKey].id; @@ -92,7 +93,7 @@ const populate = (state: any, type: StateType, config: Config) => { final[parseKey] = v; helper.s[parseKey] = { ...v }; } - const entry = helper.clean(matchRules(final, helper)) as ObjectInput; + const entry = helper.clean(matchRules(final, helper)) as ObjectInputs; const isValid = validateState(entry).isValid; return { entry, @@ -104,9 +105,9 @@ const populate = (state: any, type: StateType, config: Config) => { }; const computeOnce = ( - initialState: unknown, + initialState: Unknown, type: StateType, - config: Config + config: InputConfig ) => { if (config.persistID && persist[config.persistID]) { return persist[config.persistID]; @@ -119,12 +120,22 @@ const computeOnce = ( store.set((ref) => { const entry = ref.entry; for (const key in entry) { + // onChange ref.entry[key].onChange = (value) => inputChange(value, key, entry, store, config, helper); + // Props onChange ref.entry[key].props.onChange = (value) => inputChange(value, key, entry, store, config, helper); - ref.entry[key].init = (value, fileConfig: InitFileConfig = {}) => - init(entry[key], value, store, config, fileConfig, helper); + // initValue + ref.entry[key].initValue = (value, fileConfig: InitFileConfig = {}) => + initValue(entry[key], value, store, config, fileConfig, helper); + // setExtraData + ref.entry[key].setExtraData = (data) => { + store.set((ref) => { + ref.entry[key].extraData = data; + }); + }; + // files ref.entry[key].files = []; } }); @@ -132,17 +143,23 @@ const computeOnce = ( const initialForm = store.get("entry"); const getValues = (name?: string) => { - if (config.lockValuesOnError && !touchInput(store, helper)) { + if ( + config.lockValuesOnError && + !validateState(store.get("entry")).isValid + ) { return null; } const values = extractValues(store.get("entry")); return name ? values[name] : values; }; + const showError = () => { + touchInput(store, helper); + }; const onSubmit = (event: SyntheticEvent) => { event.preventDefault(); event.stopPropagation(); - touchInput(store, helper); + showError(); }; const loop = (callback: ForEachCallback | MapCallback, method: Method) => { @@ -150,7 +167,7 @@ const computeOnce = ( const v = { i: 0, ar: transformToArray(entry), - mapR: [] as unknown[] + mapR: [] as Unknown[] }; for (const key in entry) { v.mapR.push(callback(entry[key], v.i, v.ar)); @@ -162,26 +179,47 @@ const computeOnce = ( }; const forEach = (callback: ForEachCallback) => loop(callback, "forEach"); - const map = (callback: MapCallback) => loop(callback, "map") as unknown[]; + const map = (callback: MapCallback) => loop(callback, "map") as Unknown[]; const toArray = (): Input[] & IsValid => { const r = transformToArray(store.get("entry")) as Input[] & IsValid; r.isValid = store.get("isValid"); return r; }; - const toObject = (): ObjectInput & IsValid => { - const r = store.get("entry") as ObjectInput & IsValid; + const toObject = (): ObjectInputs & IsValid => { + const r = store.get("entry") as ObjectInputs & IsValid; r.isValid = store.get("isValid"); return r; }; const reset = () => { store.set((ref) => { - ref.entry = initialForm; + const entry = ref.entry; + const rForm: ObjectInputs = {}; + O.keys(initialForm).forEach((k) => { + rForm[k] = { + ...initialForm[k], + extraData: entry[k].extraData + }; + }); + ref.entry = rForm; ref.isValid = store.get("initialValid"); }); }; - const length = Object.keys(initialForm).length; + const getInputById = (id: string) => { + return store.get(`entry.${id}`); + }; + + const getInputsByName = (name: string) => { + const entry = store.get("entry"); + const r: Input[] = []; + O.keys(entry).forEach((k) => { + entry[k].name === name && r.push(entry[k]); + }); + return r; + }; + + const length = O.keys(initialForm).length; const result: ComputeOnceOut = { reset, @@ -192,11 +230,17 @@ const computeOnce = ( forEach, map, length, - onSubmit + onSubmit, + showError, + getInputById, + getInputsByName }; if (config.trackID && config.trackID.ID) { config.trackID.getValues = getValues; + config.trackID.showError = showError; + config.trackID.getInputById = getInputById; + config.trackID.getInputsByName = getInputsByName; config.trackID.toArray = toArray; config.trackID.toObject = toObject; config.trackID.forEach = forEach; @@ -218,9 +262,9 @@ const computeOnce = ( }; const parsedInputs = ( - initialState: unknown, + initialState: Unknown, type: StateType, - config: Config, + config: InputConfig, selective?: string ) => { const { store, ...rest } = useMemo( @@ -235,26 +279,37 @@ const parsedInputs = ( const form = useMemo(() => rest, []); const parsedInputs = - type === "object" ? inputs : transformToArray(inputs as ObjectInput); + type === "object" + ? inputs + : transformToArray(inputs as ObjectInputs); (parsedInputs as typeof parsedInputs & IsValid).isValid = isValid; return [parsedInputs, form]; }; -function useInputs( - initialState: CreateObjectInput | S, - config?: Config -): ObjStateOutput; -function useInputs( - initialState: CreateArrayInput, - config?: Config +// External declaration support (Dynamic infer) +function useInputs( + initialState: I extends Array + ? CreateArrayInputs | I + : CreateObjectInputs | I, + config?: InputConfig +): I extends Array ? ArrayStateOutput : ObjStateOutput; +// Internal declaration object +function useInputs>( + initialState: CreateObjectInputs | I, + config?: InputConfig +): ObjStateOutput; +// Internal declaration Array +function useInputs( + initialState: CreateArrayInputs | I, + config?: InputConfig ): ArrayStateOutput; +// string function useInputs( - initialState: (string | InternalInput)[], - config?: Config -): ArrayStateOutput; -function useInputs(initialState: string, config?: Config): StringStateOutput; + initialState: string, + config?: InputConfig +): StringStateOutput; -function useInputs(initialState: unknown, config: Config = {}): unknown { +function useInputs(initialState: Unknown, config: InputConfig = {}): Unknown { if (initialState instanceof Array) { return parsedInputs( initialState.map((entry, i) => diff --git a/src/tracking/index.ts b/src/tracking/index.ts index 2e11e1d..d8cccf6 100644 --- a/src/tracking/index.ts +++ b/src/tracking/index.ts @@ -1,11 +1,13 @@ import type { ForEachCallback, IDTrackUtil, + Input, MapCallback, TrackUtil } from "../types"; +import { O } from "../util"; -const TRACKING_KEYS = Object.freeze([ +const TRACKING_KEYS = O.freeze([ "getValues", "reset", "isValid", @@ -14,7 +16,10 @@ const TRACKING_KEYS = Object.freeze([ "forEach", "map", "length", - "useValues" + "useValues", + "showError", + "getInputById", + "getInputsByName" ]); export const trackInputs = (trackingID: S[]) => { @@ -60,6 +65,31 @@ export const trackInputs = (trackingID: S[]) => { }; }); + track.getInputById = (id: string) => { + let i = undefined; + for (const t in track) { + if ( + !TRACKING_KEYS.includes(t) && + track[t] && + track[t].getInputById && + i === undefined + ) { + i = track[t].getInputById(id); + } + } + return i; + }; + + track.getInputsByName = (name: string) => { + const i: Input[] = []; + for (const t in track) { + if (!TRACKING_KEYS.includes(t) && track[t] && track[t].getInputById) { + i.push(...track[t].getInputsByName(name)); + } + } + return i; + }; + ["forEach", "map"].forEach((func) => { track[func] = (callback: ForEachCallback | MapCallback) => { for (const t in track) { @@ -70,13 +100,15 @@ export const trackInputs = (trackingID: S[]) => { }; }); - track.reset = () => { - for (const t in track) { - if (!TRACKING_KEYS.includes(t) && track[t] && track[t].reset) { - track[t].reset(); + ["reset", "showError"].forEach((func) => { + track[func] = () => { + for (const t in track) { + if (!TRACKING_KEYS.includes(t) && track[t] && track[t][func]) { + track[t][func](); + } } - } - }; + }; + }); track.isValid = () => { let isValid = true; diff --git a/src/types/index.ts b/src/types/index.ts index bd514e1..a013a75 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -119,6 +119,7 @@ type ErrorMessageType = Unknown; interface InternalInput { id?: string; + accept?: string; name?: string; type?: HTMLInputTypeAttribute; label?: Unknown; @@ -131,6 +132,7 @@ interface InternalInput { placeholder?: Unknown; errorMessage?: Unknown; validation?: ValidationStateType; + extraData?: Unknown; } interface ParsedFile { @@ -155,9 +157,9 @@ type ValidateState = { // FOr some reason, Build-in Required doesn't work interface InputProps { id: string; + accept: string; name: string; type: HTMLInputTypeAttribute; - "aria-label": Unknown; value: Unknown; checked: boolean; multiple: boolean; @@ -179,35 +181,37 @@ interface Input extends InputProps { valid: boolean; touched: boolean; - init(value: Unknown, initFileConfig?: InitFileConfig): void; + initValue(value: Unknown, initFileConfig?: InitFileConfig): void; + setExtraData(data: Unknown): void; props: InputProps; + extraData: Unknown; } interface InitFileConfig { // entryFormat?: "url" | "url[]"; // proxyUrl?: string; // useDefaultProxyUrl?: boolean; - getBlob?(url: string): File; + getBlob?(url: string): Blob | Promise; } -type CreateObjectInput = { - [key in string]: InternalInput; +type CreateObjectInputs = { + [key in K & string]: InternalInput; }; -type ObjectInput = { - [key in string]: Input; +type ObjectInputs = { + [key in K & string]: Input; }; -type ArrayInput = Input[]; -type CreateArrayInput = InternalInput[]; +type ArrayInputs = Input[]; +type CreateArrayInputs = (string | InternalInput)[]; -type ObjStateOutput = [{ [k in Key & string]: Input } & IsValid, Form]; +type ObjStateOutput = [{ [k in keyof I & string]: Input } & IsValid, Form]; type StringStateOutput = [Input & IsValid, Form]; -type ArrayStateOutput = [ArrayInput & IsValid, Form]; +type ArrayStateOutput = [ArrayInputs & IsValid, Form]; type StateType = "object" | "array"; -type Config = { +type InputConfig = { asyncDelay?: number; persistID?: string; trackID?: IDTrackUtil; @@ -219,25 +223,30 @@ interface IsValid { } interface CommonForm { - toObject(): ObjectInput & IsValid; + toObject(): ObjectInputs & IsValid; - toArray(): ArrayInput & IsValid; + toArray(): ArrayInputs & IsValid; reset(): void; forEach(callback: ForEachCallback): void; - map(callback: MapCallback): unknown[]; + map(callback: MapCallback): Unknown[]; + + showError(): void; + + getInputById(id: string): Input; + getInputsByName(name: string): Input[]; } type Method = "forEach" | "map"; interface ForEachCallback { - (input: Input, index: number, array: ArrayInput): void; + (input: Input, index: number, array: ArrayInputs): void; } interface MapCallback { - (input: Input, index: number, array: ArrayInput): unknown; + (input: Input, index: number, array: ArrayInputs): Unknown; } interface Form extends CommonForm { @@ -248,8 +257,8 @@ interface Form extends CommonForm { onSubmit(event: SyntheticEvent): void; } -interface IDTrackUtil extends CommonForm { - ID: S; +interface IDTrackUtil extends CommonForm { + ID: I; length: number; isValid(): boolean; @@ -272,7 +281,7 @@ interface TrackUtil extends CommonForm { } type InputStore = StoreType<{ - entry: ObjectInput; + entry: ObjectInputs; isValid: boolean; helper: Helper; initialValid: boolean; @@ -295,16 +304,17 @@ interface ComputeOnceOut extends CommonForm { getValues(name?: string): Unknown; onSubmit(event: SyntheticEvent): void; + showError(): void; } interface Helper { ok: { [k in string]: Set }; - s: CreateObjectInput; + s: CreateObjectInputs; em: { [k in string]: ErrorMessageType | undefined }; tm: { [k in string]: string[] }; a: { [k in string]: Unknown }; - clean(s: CreateObjectInput): CreateObjectInput; + clean(s: CreateObjectInputs): CreateObjectInputs; } export type { @@ -325,13 +335,13 @@ export type { CopyKeyObjType, MergeType, CopyType, - Config, + InputConfig, Input, IDTrackUtil, InputStore, AsyncCallback, AsyncValidationParams, - ObjectInput, + ObjectInputs, ComputeOnceOut, ParsedFile, InitFileConfig, @@ -339,9 +349,9 @@ export type { MapCallback, Method, IsValid, - CreateObjectInput, - ArrayInput, - CreateArrayInput, + CreateObjectInputs, + ArrayInputs, + CreateArrayInputs, InputProps, ValidateState }; diff --git a/src/util/helper.ts b/src/util/helper.ts index 7269a45..ae0b5c7 100644 --- a/src/util/helper.ts +++ b/src/util/helper.ts @@ -1,4 +1,4 @@ -import type { ComputeOnceOut, Helper, CreateObjectInput } from "../types"; +import type { ComputeOnceOut, Helper, CreateObjectInputs } from "../types"; const He = (): Helper => { // omitted keys @@ -12,7 +12,7 @@ const He = (): Helper => { // async delay const a = {}; - const clean = (s: CreateObjectInput) => { + const clean = (s: CreateObjectInputs) => { for (const sKey in s) { delete s[sKey].validation?.copy; delete s[sKey].validation?.match; diff --git a/src/util/index.ts b/src/util/index.ts index bff0362..61e4bf1 100644 --- a/src/util/index.ts +++ b/src/util/index.ts @@ -1,13 +1,13 @@ import type { - CreateObjectInput, - InputProps, + CreateObjectInputs, Helper, Input, + InputProps, InputStore, InternalInput, MatchResultType, MergeType, - ObjectInput, + ObjectInputs, ParsedFile, Unknown, ValidateState, @@ -16,6 +16,7 @@ import type { import { deepMatch, parseCopy, validate } from "./validation"; import { createCheckboxValue } from "../inputs/handlers/checkbox"; import { radioIsChecked } from "../inputs/handlers/radio"; +export const O = Object; const parseValue = (input: Input, value: any) => input.type === "number" || input.validation?.number @@ -26,7 +27,7 @@ const parseValue = (input: Input, value: any) => const initValid = (entry: InternalInput) => { const validation = entry.validation || {}; - const isValid = !Object.keys(validation).length; + const isValid = !O.keys(validation).length; // return !["", 0, null, undefined].includes(entry.value); // return isValid ? !!entry.checked : false; return entry.checked ? true : isValid; @@ -37,7 +38,6 @@ const lockProps = (entry: Input) => { id: entry.id, name: entry.name, type: entry.type, - "aria-label": entry.label, value: entry.value, checked: entry.checked, multiple: entry.multiple, @@ -64,7 +64,8 @@ const commonProps = (entry: InternalInput, id: string) => { touched: false, placeholder: entry.placeholder ?? entry.name ?? defaultID, errorMessage: undefined, - validating: false + validating: false, + extraData: null }; }; @@ -148,7 +149,7 @@ const merge = ( // Match and copy input validation const mcv = ( helper: Helper, - state: CreateObjectInput, + state: CreateObjectInputs, stateKey: string, matchOrCopyKey: string, keyPath: keyof ValidationStateType @@ -255,7 +256,7 @@ const mcv = ( * }. * */ -const matchRules = (state: CreateObjectInput, helper: Helper) => { +const matchRules = (state: CreateObjectInputs, helper: Helper) => { const patch = { checkbox: { tab: [] @@ -312,7 +313,7 @@ const matchRules = (state: CreateObjectInput, helper: Helper) => { } } - Object.keys(patch).forEach((o) => { + O.keys(patch).forEach((o) => { if (patch[o].fv) { patch[o].tab.forEach((id: string) => { // we get the name @@ -342,7 +343,6 @@ const touchInput = (store: InputStore, helper: Helper) => { const { isValid, invalidKey } = validateState(data); if (invalidKey) { const input = data[invalidKey]; - const value = input.type === "file" ? input.files @@ -358,8 +358,6 @@ const touchInput = (store: InputStore, helper: Helper) => { invalidKey, input.type === "radio" ? (radioValid ? value : null) : value ); - data[invalidKey].touched = true; - data[invalidKey].errorMessage = em; store.set((ref) => { ref.entry[invalidKey].touched = true; @@ -372,7 +370,7 @@ const touchInput = (store: InputStore, helper: Helper) => { // Validate the state // Set form is valid -const validateState = (data: ObjectInput): ValidateState => { +const validateState = (data: ObjectInputs): ValidateState => { let isValid = true; let invalidKey = null; for (const formKey in data) { @@ -385,7 +383,7 @@ const validateState = (data: ObjectInput): ValidateState => { return { isValid, invalidKey }; }; // T transform array to object and vice versa -const transformToArray = (state: ObjectInput) => { +const transformToArray = (state: ObjectInputs) => { const result: Input[] = []; for (const key in state) { result.push(state[key]); @@ -404,7 +402,7 @@ const cleanFiles = (files: ParsedFile[]) => { }; // E extract values from state -const extractValues = (state: ObjectInput) => { +const extractValues = (state: ObjectInputs) => { const result = {} as { [k in string]: any }; for (const key in state) { const K = state[key].name; diff --git a/src/util/validation.ts b/src/util/validation.ts index e37de0e..5375f16 100644 --- a/src/util/validation.ts +++ b/src/util/validation.ts @@ -5,9 +5,9 @@ import type { ErrorMessageType, InputStore, MatchResultType, - CreateObjectInput, + CreateObjectInputs, Input, - ObjectInput, + ObjectInputs, ValidationStateType, Unknown, Helper @@ -45,7 +45,7 @@ const parseCopy = ( // Deep match const deepMatch = ( helper: Helper, - state: CreateObjectInput, + state: CreateObjectInputs, stateKey: string, matchKey: string, keyPath: keyof ValidationStateType @@ -121,7 +121,7 @@ const getValue = (rule: any) => { // V is validate const validate = ( helper: Helper, - state: ObjectInput, + state: ObjectInputs, target: string, value: Unknown ) => { @@ -342,7 +342,7 @@ const validate = ( const asyncValidation = ( store: InputStore, helper: Helper, - state: ObjectInput, + state: ObjectInputs, target: string, value: unknown, callback: AsyncCallback