diff --git a/src/FMInput.ts b/src/FMInput.ts index a64ba61..7a23711 100644 --- a/src/FMInput.ts +++ b/src/FMInput.ts @@ -1,4 +1,5 @@ import FormManager from "./FormManager" +import { realType } from "./Functions" export default class FMInput { @@ -10,18 +11,18 @@ export default class FMInput { this.element = element as HTMLInputElement this.form = form this.required = element.hasAttribute("required") - - // Set element value to it's default one - this.setToDefault() } /** * Set the element Value * - * @param {*} value + * @param {*} value the input value + * + * _hint: pass it through this.formatValue_ + * * @memberof FMInput */ - setValue(value: any) { + public setValue(value: any) { this.element.value = value } @@ -31,41 +32,24 @@ export default class FMInput { * @returns {*} the value * @memberof FMInput */ - getValue(): any { + public getValue(): any { return this.formatValue(this.element.value) } /** - * Format the value - * ex: if the value is "1" it will return the value as a number 1 + * Format the value into a usable one by the module + * + * for elements like `select` if the value don't correspond to something + * it will return the default `value` * - * @protected * @param {*} value * @returns {*} * @memberof FMInput */ - protected formatValue(value: any): any { + public formatValue(value: any): any { // if the value is a number return it as a number obj - if (!isNaN(Number(value))) return Number(value) - return value - - } - - getDefault(args?: string): any { - // if arg is set and startsWith run: run the function in it - if (args && args.startsWith("run:")) { - args = args.split("run:")[1] - return eval(args) - } - return args - } - - setToDefault() { - if (this.element.hasAttribute("data-default")) { - return this.setValue(this.getDefault(this.element.dataset.default)) - } - return this.setValue(this.getDefault("")) + return realType(value) } /** @@ -74,7 +58,7 @@ export default class FMInput { * @returns {string} * @memberof FMInput */ - getName(): string { + public getName(): string { // while we search for inputs containing [name] we search for the input real name in [name] or [data-name] // (Allow other inputs to play with inputs) let attr = this.element.getAttribute("name") || this.element.dataset.name; @@ -88,16 +72,11 @@ export default class FMInput { * @returns {boolean} * @memberof FMInput */ - verify(): boolean { + public verify(): boolean { let val: any = this.getValue() // if element is required and value is undefined retur false if (this.required && (val === undefined || val === null || val === "")) return false - // check regex - const regex = this.element.dataset.regex - if(regex) { - return new RegExp(regex, 'g').test(val + "") - } return true } } diff --git a/src/FormManager.ts b/src/FormManager.ts index d2c24a0..2a9c8e6 100644 --- a/src/FormManager.ts +++ b/src/FormManager.ts @@ -1,5 +1,7 @@ -import { InputArrayInterface, FMAssignInterface } from './Interfaces'; +import { InputArray, InputAssignment } from './Interfaces'; import FMInput from "./FMInput" +import AttributesManager from './AttributesManager'; +import { FMAttributeListeners } from './FMAttribute'; /*! * FormManager by DZEIO's team @@ -7,100 +9,6 @@ import FMInput from "./FMInput" * https://dze.io */ -/** - * - * `datalist` upgrade: - * the value submitted won't be the `value` attribute but the `data-value` attribute - * a `data-strict` attribute if set will return undefined if input value is not from data-value else it will return the input value if not foudn in options - * ex: - * ```html - * - * - * - * - * - * - * - * ``` - * **ATTENTION if multiple `option` have the same `value` attribute the submitted value will be the first one** - * - * - * a `data-ignore` attribute: - * the input with the data-ignore won't send datas to server _still need the `name` attribute_ - * awesome for usage with `data-autoset` - * - * a `data-regex` attribute: - * when the element is getting verified it will be passed through the regex - * ex: - * ```html - * - * - * - * - * - * ``` - * _please note that you still have to verify that server-side_ - * - * - * a `data-default` attribute: - * if the value is not something controlable by html it will be set by here - * depending on the input type different defaults are possibles - * ex: `date` `datetime` `time` if it's set it will be the current time - * ```html - * - * - * - * ``` - * - * - * a `data-autoset` attribute: - * this attribute will change it's value regarding other inputs - * DON'T name any input `x` as it will break the repeat element - * (you SHOULD use `readonly` or `disabled` attribute with it) - * ex: - * ```html - * - * - * ``` - * the test input should always contain `testing-autoset-(the value of input) - * - * - * a `repeat` type: - * container: `.fm-repeat` with a name attribute - * template (in container): `.fmr-template` - * element (DO NOT PLACE IT YOURSELF) (in container): `.fmr-element` - * add button (in container): `.fmr-add` - * delete button (in template): `.fmr-del` - * - * input *MUST* have `data-name` and *NOT* `name` attributes - * if `data-default` or `data-autoset` contains `{x}` it will be replaced by the index - * - * ```html - *
- *
- * - * - * - *
- * - *
- *
- *
- * - * - *
- * - * - *
- * ``` - * - * - * TODO: - * check if input has attribute `form` and that the value is the current form id - * if so add it to current form - */ - - /** * Manager for Forms * @@ -113,19 +21,19 @@ export default class FormManager { * List of inputs * * @private - * @type {InputArrayInterface} + * @type {InputArray} * @memberof FormManager */ - private inputs: InputArrayInterface = {} + public inputs: InputArray = {} /** * List of interfaces * * @private - * @type {FMAssignInterface[]} + * @type {InputAssignment[]} * @memberof FormManager */ - private FMInputs: FMAssignInterface[] = [] + private FMInputs: InputAssignment[] = [] /** * The last verified `FMInput` that returned an error @@ -143,6 +51,8 @@ export default class FormManager { */ public form: HTMLFormElement + public attributeManager: AttributesManager + /** * Creates an instance of FormManager. @@ -151,6 +61,7 @@ export default class FormManager { */ constructor(form: HTMLFormElement) { this.form = form + this.attributeManager = new AttributesManager(this) //Prevent default submit action form.onsubmit = (e) => { @@ -164,16 +75,6 @@ export default class FormManager { //Setup the system for basic inputs this.setupInputs() - - setInterval(() => { - (this.form.querySelectorAll("[data-autoset]") as NodeListOf).forEach((el: HTMLInputElement) => { - let autosetStr = el.dataset.autoset - if (autosetStr && autosetStr.startsWith("run:")) { - let tmp = autosetStr.split("run:")[1] - el.value = eval(tmp) - } - }) - }, 500) } /** @@ -182,7 +83,17 @@ export default class FormManager { * @param {FMAssignInterface} inter the interface used * @memberof FormManager */ - public assign(inter: FMAssignInterface) { + public assign(...inter: InputAssignment[]) { + this.FMInputs.unshift(...inter) + } + + /** + * Assign a single Module + * + * @param {FMAssignInterface} inter + * @memberof FormManager + */ + public assignSingle(inter: InputAssignment) { this.FMInputs.unshift(inter) } @@ -197,6 +108,7 @@ export default class FormManager { let el = this.getInit(element) if (el) this.inputs[el.getName()] = el }); + this.attributeManager.trigger(FMAttributeListeners.FORM_INIT) } /** @@ -206,7 +118,7 @@ export default class FormManager { * @returns {FMInput} * @memberof FormManager */ - public getInit(element: Element): FMInput|undefined { + public getInit(element: Element): FMInput|void { inputsLoop: for (const input of this.FMInputs) { if (input.classes != undefined) { let tmpList: string[] = [] @@ -246,7 +158,9 @@ export default class FormManager { for (const name in this.inputs) { if (this.inputs.hasOwnProperty(name)) { const input = this.inputs[name]; - if(!input.verify()) { + const res = this.attributeManager.triggerElement(FMAttributeListeners.VERIFY, input) as boolean + if(!input.verify() || !res) { + console.log(input.verify(), res) this.lastErroredInput = input return false } @@ -268,11 +182,16 @@ export default class FormManager { */ public submit(url: string, callback?: (this: XMLHttpRequest, ev: ProgressEvent) => void, verify: boolean = true): boolean { if (verify && !this.verify()) return false + let toSend = this.getJSON() + let event = this.attributeManager.trigger(FMAttributeListeners.FORM_SUBMIT, toSend) + if (typeof event !== "boolean" && event.datas && event.result) { + toSend = event.datas + } let ajax = new XMLHttpRequest ajax.open("POST", url, true) ajax.setRequestHeader("Content-Type", "application/json") if (callback != undefined) ajax.addEventListener("loadend", callback) - ajax.send(JSON.stringify(this.getJSON())) + ajax.send(JSON.stringify(toSend)) return true } @@ -281,7 +200,7 @@ export default class FormManager { * * @memberof FormManager */ - public getJSON(): any { + public getJSON(): {[key: string]: any} { const jsonObject: any = {} for (const name in this.inputs) { if (this.inputs.hasOwnProperty(name)) { @@ -295,7 +214,7 @@ export default class FormManager { /** * Fill the form from JSON * - * Hint: _to see what the json is made of use `fm.getJSON`_ + * Hint: _to see what the json look like, use `fm.getJSON`_ * * @param {*} json the JSON * @memberof FormManager @@ -308,6 +227,7 @@ export default class FormManager { else console.warn(`${key} is not a valid input name`) } } + this.attributeManager.trigger(FMAttributeListeners.FORM_FILL) } /** @@ -353,7 +273,6 @@ export default class FormManager { } public setModeForInput(mode: FMMode, inputName: string) { - console.log(mode) if (mode == FMMode.ViewMode) { if (this.inputs[inputName]) { this.inputs[inputName].element.setAttribute("disabled", "") @@ -373,14 +292,16 @@ export default class FormManager { * @memberof FormManager */ public clear() { + if (this.attributeManager.trigger(FMAttributeListeners.PRE_CLEAR) === false) return (this.form.querySelectorAll("[name]") as NodeListOf).forEach((el: HTMLInputElement) => { for (const name in this.inputs) { if (this.inputs.hasOwnProperty(name)) { const input = this.inputs[name]; - input.setToDefault() + input.setValue(undefined) } } }) + this.attributeManager.trigger(FMAttributeListeners.POST_CLEAR) } } diff --git a/src/Interfaces.ts b/src/Interfaces.ts index 30bdad1..19b751c 100644 --- a/src/Interfaces.ts +++ b/src/Interfaces.ts @@ -3,10 +3,10 @@ import FMInput from "./FMInput" /** * this interface is used for fetching and setting `name` to `FMInput` link * - * @interface InputArrayInterface + * @interface InputArray */ -export interface InputArrayInterface { - [key:string]: FMInput +export interface InputArray { + [key: string]: FMInput } /** @@ -21,3 +21,11 @@ export interface FMAssignInterface { type?: string tagName?: string } + +export interface InputAssignment { + input: typeof FMInput + classes?: string[] | string + attributes?: string[] | string + type?: string + tagName?: string +} diff --git a/src/modules/FMDatalistInput.ts b/src/modules/FMDatalistInput.ts index 65baa07..d58ad3a 100644 --- a/src/modules/FMDatalistInput.ts +++ b/src/modules/FMDatalistInput.ts @@ -1,4 +1,4 @@ -import { FMAssignInterface } from '../Interfaces'; +import { InputAssignment } from '../Interfaces'; import FormManager from "../FormManager" import FMInput from "../FMInput" @@ -29,7 +29,7 @@ export default class FMDatalistInput extends FMInput { setValue(value: string) { // if value is "" set value to "" - if (value == "") { + if (value == "" || value === undefined) { this.element.value = "" return } @@ -64,7 +64,7 @@ export default class FMDatalistInput extends FMInput { } } -export const FMDatalistAssignement: FMAssignInterface = { +export const FMDatalistAssignement: InputAssignment = { input: FMDatalistInput, attributes: "list", tagName: "input" diff --git a/src/modules/FMDateInput.ts b/src/modules/FMDateInput.ts index ffb62dd..8bbc7cb 100644 --- a/src/modules/FMDateInput.ts +++ b/src/modules/FMDateInput.ts @@ -1,4 +1,4 @@ -import { FMAssignInterface } from '../Interfaces'; +import { InputAssignment } from '../Interfaces'; import FMInput from "../FMInput" /** @@ -8,12 +8,13 @@ import FMInput from "../FMInput" */ export default class FMDateInput extends FMInput { - setValue(value: Date|string) { - // if value is a string set value to the date of the string - if (typeof(value) == "string") { - value = new Date(value) + setValue(value: any) { + // handle GO null value + const format = this.formatValue(value) + if (format) { + this.element.valueAsDate = format } - this.element.valueAsDate = value + this.element.value = format } getValue(): Date|undefined { @@ -22,13 +23,21 @@ export default class FMDateInput extends FMInput { return date == null ? undefined : date } - getDefault(args: string): Date { - // if data-default is present return the current date - return new Date + public formatValue(val: any): Date|undefined { + if (val === "0001-01-01T00:00:00Z") { + return undefined + } + if (typeof val === "string" || typeof val === "number") { + return new Date(val) + } + if (typeof val === "object" && typeof val.getDate === "function") { + return (val as Date) + } + return undefined } } -export const FMDateAssignement: FMAssignInterface = { +export const FMDateAssignement: InputAssignment = { input: FMDateInput, type: "date", tagName: "input" diff --git a/src/modules/FMFileInput.ts b/src/modules/FMFileInput.ts index 8879f73..7c7e65a 100644 --- a/src/modules/FMFileInput.ts +++ b/src/modules/FMFileInput.ts @@ -1,4 +1,4 @@ -import { FMAssignInterface } from '../Interfaces'; +import { InputAssignment } from '../Interfaces'; import FMInput from "../FMInput" import FormManager from '../FormManager'; @@ -111,7 +111,7 @@ export default class FMFileInput extends FMInput { } -export const FMFileAssignement: FMAssignInterface = { +export const FMFileAssignement: InputAssignment = { input: FMFileInput, type: "file", tagName: "input" diff --git a/src/modules/FMRepeatInput.ts b/src/modules/FMRepeatInput.ts index 19d3b21..f8c6a60 100644 --- a/src/modules/FMRepeatInput.ts +++ b/src/modules/FMRepeatInput.ts @@ -1,4 +1,4 @@ -import { FMAssignInterface } from './../Interfaces'; +import { InputAssignment } from './../Interfaces'; import FormManager from "../FormManager" import FMInput from "../FMInput" @@ -68,6 +68,9 @@ export default class FMRepeatInput extends FMInput { let sub: FMInput[] = [] node.querySelectorAll("[data-input]").forEach((el: Element) => { let input = this.form.getInit(el) + if (!input) { + return + } if (this.element.hasAttribute("disabled")) { input.element.disabled = true } @@ -142,7 +145,7 @@ export default class FMRepeatInput extends FMInput { } } -export const FMRepeatAssignment: FMAssignInterface = { +export const FMRepeatAssignment: InputAssignment = { input: FMRepeatInput, classes: "fm-repeat", tagName: "div" diff --git a/src/modules/FMSelectInput.ts b/src/modules/FMSelectInput.ts index c61daf0..229976e 100644 --- a/src/modules/FMSelectInput.ts +++ b/src/modules/FMSelectInput.ts @@ -1,5 +1,6 @@ -import { FMAssignInterface } from '../Interfaces'; +import { InputAssignment } from '../Interfaces'; import FMInput from "../FMInput" +import { realType } from '../Functions'; /** * @@ -8,16 +9,16 @@ import FMInput from "../FMInput" */ export default class FMSelectInput extends FMInput { - getDefault(): any { - // check if element as a selected element and if true return it's value - let def = this.element.querySelector("option[selected]") - if (def) { - return (def as HTMLOptionElement).value + public formatValue(val: any): any { + const opt = this.element.querySelector(`option[value="${val}"]`) || this.element.querySelector("option[selected]") + if (opt) { + return realType(opt.getAttribute("value") || "") } + return undefined } } -export const FMSelectAssignement: FMAssignInterface = { +export const FMSelectAssignement: InputAssignment = { input: FMSelectInput, tagName: "select" }