diff --git a/src/AttributesManager.ts b/src/AttributesManager.ts index 878bebc..ca2f95a 100644 --- a/src/AttributesManager.ts +++ b/src/AttributesManager.ts @@ -1,6 +1,6 @@ import FormManager from "./FormManager"; -import InputAbstract from "./modules/InputAbstract"; -import AttributeAbstract from "./attributes/AttributeAbstract"; +import InputAbstract from "./modules/AbstractInput"; +import AbstractAttribute from "./attributes/AbstractAttribute"; import AttributeIdentity from "./attributes/Interfaces/AttributeIdentity"; import AttributeListeners from "./attributes/AttributeListeners"; @@ -16,17 +16,16 @@ export default class AttributesManager { this.form = form } - public register(...attribute: typeof AttributeAbstract[]) { + public register(...attribute: typeof AbstractAttribute[]) { for (const attr of attribute) { this.attributesArray.push(attr.identity) } } - public trigger(event: AttributeListeners, data?: any): boolean|object { + public trigger(event: AttributeListeners, data?: any): boolean { if (!this.eventsListenersItems[event]) return true for (const el of this.eventsListenersItems[event]) { - const res = el.trigger(event, data) - if (typeof res !== "undefined") return res + el.trigger(event, data) } return true } @@ -83,5 +82,5 @@ export default class AttributesManager { } interface listenerItems { - [key:string]: AttributeAbstract[] + [key:string]: AbstractAttribute[] } diff --git a/src/FormManager.full.ts b/src/FormManager.full.ts index a62a7d8..f20cdb4 100644 --- a/src/FormManager.full.ts +++ b/src/FormManager.full.ts @@ -7,7 +7,8 @@ import RegexAttribute from './attributes/RegexAttribute' import IgnoreAttribute from './attributes/IgnoreAttribute' import DefaultAttribute from './attributes/DefaultAttribute' import AutosetAttribute from './attributes/AutosetAttribute' -import checkboxInput from './modules/CheckboxInput' +import CheckboxInput from './modules/CheckboxInput' +import NumberInput from './modules/NumberInput' /** * This class is Mainly used for (non-npm) browser usage as it contains every buitins extensions @@ -24,7 +25,8 @@ export default class fm extends FormManager { DateInput, RepeatInput, SelectInput, - checkboxInput, + CheckboxInput, + NumberInput, ) this.setupInputs() this.attributeManager.register( diff --git a/src/FormManager.ts b/src/FormManager.ts index 9702e1d..ddd6587 100644 --- a/src/FormManager.ts +++ b/src/FormManager.ts @@ -1,7 +1,7 @@ import AttributesManager from './AttributesManager'; import InputIdentity from './modules/Interfaces/InputIdentity'; import DefaultInput from './modules/DefaultInput'; -import InputAbstract from './modules/InputAbstract'; +import AbstractInput from './modules/AbstractInput'; import InputArray from "./modules/Interfaces/InputArray"; import AttributeListeners from './attributes/AttributeListeners'; @@ -84,10 +84,11 @@ export default class FormManager { /** * Add to the Manager an Input * - * @param {InputIdentity[]} inter the interface used + * @param {...(typeof AbstractInput[])} inter the interface used * @memberof FormManager */ - public assign(...inter: (typeof InputAbstract[])) { + + public assign(...inter: (typeof AbstractInput[])) { for (const input of inter) { this.FMInputs.unshift(input.identity) } @@ -120,7 +121,7 @@ export default class FormManager { * @returns {FMInput} * @memberof FormManager */ - public getInit(element: HTMLElement): InputAbstract|void { + public getInit(element: HTMLElement): AbstractInput|void { inputsLoop: for (const input of this.FMInputs) { if (input.classes != undefined) { let tmpList: string[] = [] @@ -152,11 +153,11 @@ export default class FormManager { * Verify the inputs for errors * * @param {boolean} [quick=false] define if the loop should stop after the first error - * @returns {InputAbstract[]} return an array containing the errored elements (Empty if not error) + * @returns {AbstractInput[]} return an array containing the errored elements (Empty if not error) * @memberof FormManager */ - public validate(quick = false): InputAbstract[] { - const errored: InputAbstract[] = [] + public validate(quick = false): AbstractInput[] { + const errored: AbstractInput[] = [] for (const name in this.inputs) { if (!this.inputs.hasOwnProperty(name)) continue const input = this.inputs[name]; @@ -208,6 +209,20 @@ export default class FormManager { return true } + /** + * Manually set the value of an element + * + * @param {string} name + * @param {*} value + * @memberof FormManager + */ + public setValue(name: string, value: any) { + if (!this.inputs.hasOwnProperty(name)) { + return + } + const input = this.inputs[name] + input.setValue(value) + } /** * Return the JSON `{key: value}` sequence * diff --git a/src/Functions.ts b/src/Functions.ts index aa52911..1d2c725 100644 --- a/src/Functions.ts +++ b/src/Functions.ts @@ -21,11 +21,11 @@ export function evalF(str: string, callback?: (str: string) => void): boolean { * * @param str the string to transform */ -export function toNumber(str: any): number|undefined { +export function toNumber(str: any): number | undefined { if (typeof str === "number") return str - if ((str === "" || str === undefined || typeof(str) === "boolean")) return undefined - // return undefined if it must be shown as string - // console.log("toNumber", str) + if (str === "" || typeof str !== "string") return undefined + + // str is a string if ((str.startsWith("0") || str.startsWith("+")) && str.length > 1) return undefined const n = Number(str) if (!isNaN(n)) { @@ -35,30 +35,33 @@ export function toNumber(str: any): number|undefined { } export function isNumber(el: any): boolean { - // console.log(el) - return typeof el === "number" + return typeof toNumber(el) === "number" } -export function toBoolean(str: any): boolean|undefined { +export function toBoolean(str: any): boolean | undefined { if (typeof str === "boolean") return str if (str === "true") return true if (str === "false") return false return undefined } -export function strToNum(str: string): number|string { +export function isBoolean(el: any): boolean { + return typeof toBoolean(el) === "boolean" +} + +export function strToNum(str: string): number | string { const n = toNumber(str) if (n) return n return str } - -export function realType(el: any): string|number|boolean { +export function realType(el: any): string|number|boolean|undefined { + // If el is `null` or `undefined` + if ((typeof el === "object" && el === null) || typeof el === "undefined") return undefined if (typeof el === "object" && el.hasOwnProperty("id")) { el = el.id } - if (isNumber(el)) return el - const isBool = toBoolean(el) - const isNum = toNumber(el) - return typeof isBool === "boolean" ? isBool : typeof isNum === "number" ? isNum : el + if (isNumber(el)) return toNumber(el) + if (isBoolean(el)) return toBoolean(el) + return el } diff --git a/src/attributes/AttributeAbstract.ts b/src/attributes/AbstractAttribute.ts similarity index 83% rename from src/attributes/AttributeAbstract.ts rename to src/attributes/AbstractAttribute.ts index 20a214c..7ea21d1 100644 --- a/src/attributes/AttributeAbstract.ts +++ b/src/attributes/AbstractAttribute.ts @@ -1,8 +1,8 @@ import AttributeIdentity from './Interfaces/AttributeIdentity'; -import InputAbstract from "../modules/InputAbstract"; +import InputAbstract from "../modules/AbstractInput"; import AttributeListeners from "./AttributeListeners"; -export default abstract class AttributeAbstract { +export default abstract class AbstractAttribute { public input: InputAbstract diff --git a/src/attributes/AutosetAttribute.ts b/src/attributes/AutosetAttribute.ts index e6c14d3..0495a3d 100644 --- a/src/attributes/AutosetAttribute.ts +++ b/src/attributes/AutosetAttribute.ts @@ -1,6 +1,6 @@ import { evalF } from "../Functions"; import AttributeListeners from "./AttributeListeners"; -import AttributeAbstract from "./AttributeAbstract"; +import AbstractAttribute from "./AbstractAttribute"; import AttributeIdentity from "./Interfaces/AttributeIdentity"; /** @@ -24,7 +24,7 @@ import AttributeIdentity from "./Interfaces/AttributeIdentity"; * @implements {FMAFormInitInterface} */ export default class AutosetAttribute -extends AttributeAbstract { +extends AbstractAttribute { public trigger(): boolean | void | object { let str = this.input.element.getAttribute("data-autoset") || "" diff --git a/src/attributes/DefaultAttribute.ts b/src/attributes/DefaultAttribute.ts index 08e52b8..28639d8 100644 --- a/src/attributes/DefaultAttribute.ts +++ b/src/attributes/DefaultAttribute.ts @@ -1,5 +1,5 @@ import { evalF } from "../Functions"; -import AttributeAbstract from "./AttributeAbstract"; +import AbstractAttribute from "./AbstractAttribute"; import AttributeListeners from "./AttributeListeners"; import AttributeIdentity from "./Interfaces/AttributeIdentity"; @@ -24,13 +24,18 @@ import AttributeIdentity from "./Interfaces/AttributeIdentity"; * @implements {FMAFormInitInterface} */ export default class DefaultAttribute -extends AttributeAbstract { +extends AbstractAttribute { public trigger(): boolean | void | object { this.run() return true } private run() { + // Dont override existing value + if (this.input.getValue() !== undefined) { + return + } + let attrVal = this.input.element.getAttribute("data-default") // if element has a date,time,week type @@ -66,7 +71,8 @@ extends AttributeAbstract { // if default is an attribute value if (!el.hasAttribute(splitted[1])) throw Error(`Error: "${this.input.getName()}" element don't have the attribute "${splitted[1]}"`) - return this.input.setValue(el.getAttribute(splitted[1])) + this.input.setValue(el.getAttribute(splitted[1])) + return } this.input.setValue(attrVal) } diff --git a/src/attributes/IgnoreAttribute.ts b/src/attributes/IgnoreAttribute.ts index 703bc84..ec77f02 100644 --- a/src/attributes/IgnoreAttribute.ts +++ b/src/attributes/IgnoreAttribute.ts @@ -1,4 +1,4 @@ -import AttributeAbstract from "./AttributeAbstract"; +import AbstractAttribute from "./AbstractAttribute"; import AttributeListeners from "./AttributeListeners"; import AttributeIdentity from "./Interfaces/AttributeIdentity"; @@ -23,7 +23,7 @@ import AttributeIdentity from "./Interfaces/AttributeIdentity"; * @implements {FMAFormInitInterface} */ export default class IgnoreAttribute -extends AttributeAbstract { +extends AbstractAttribute { public trigger(_: AttributeListeners, data?: any): boolean | void | object { data[this.input.getName()] = undefined return data diff --git a/src/attributes/Interfaces/AttributeInterface.ts b/src/attributes/Interfaces/AttributeInterface.ts index 51c4128..c814fd1 100644 --- a/src/attributes/Interfaces/AttributeInterface.ts +++ b/src/attributes/Interfaces/AttributeInterface.ts @@ -1,5 +1,5 @@ -import InputAbstract from "../../modules/InputAbstract"; -import AttributeAbstract from "../AttributeAbstract"; +import InputAbstract from "../../modules/AbstractInput"; +import AbstractAttribute from "../AbstractAttribute"; import AttributeListeners from "../AttributeListeners"; import AttributeIdentity from "./AttributeIdentity"; @@ -7,7 +7,7 @@ import AttributeIdentity from "./AttributeIdentity"; * Define static elements of `AttributeAbstract` */ export default interface AttributeInterface { - new(input: InputAbstract): AttributeAbstract + new(input: InputAbstract): AbstractAttribute listeners: AttributeListeners[] identity: AttributeIdentity } diff --git a/src/attributes/RegexAttribute.ts b/src/attributes/RegexAttribute.ts index f7de860..bfd345e 100644 --- a/src/attributes/RegexAttribute.ts +++ b/src/attributes/RegexAttribute.ts @@ -1,9 +1,9 @@ import AttributeIdentity from './Interfaces/AttributeIdentity'; -import AttributeAbstract from './AttributeAbstract'; +import AbstractAttribute from './AbstractAttribute'; import AttributeListeners from "./AttributeListeners"; export default class RegexAttribute -extends AttributeAbstract { +extends AbstractAttribute { public trigger(): boolean { const regStr = this.input.element.dataset.regex if (!regStr) return true diff --git a/src/modules/InputAbstract.ts b/src/modules/AbstractInput.ts similarity index 100% rename from src/modules/InputAbstract.ts rename to src/modules/AbstractInput.ts diff --git a/src/modules/CheckboxInput.ts b/src/modules/CheckboxInput.ts index 4128ba8..45e2bf2 100644 --- a/src/modules/CheckboxInput.ts +++ b/src/modules/CheckboxInput.ts @@ -12,9 +12,11 @@ export default class CheckboxInput extends DefaultInput { public setValue(value: any) { this.element.checked = this.formatValue(value) } + public getValue(): boolean { return this.element.checked } + public formatValue(value: any): boolean { value = toBoolean(value) if (typeof value === "undefined") { diff --git a/src/modules/DateInput.ts b/src/modules/DateInput.ts index 140cf47..53090d8 100644 --- a/src/modules/DateInput.ts +++ b/src/modules/DateInput.ts @@ -9,7 +9,6 @@ import DefaultInput from './DefaultInput' export default class DateInput extends DefaultInput { public setValue(value: any) { - // handle GO null value const format = this.formatValue(value) if (typeof format === "object") { this.element.valueAsDate = format @@ -28,6 +27,7 @@ export default class DateInput extends DefaultInput { if (typeof val === "object" && typeof val.getDate === "function") { return (val as Date) } + // handle GO null value if (val === "0001-01-01T00:00:00Z") { return undefined } diff --git a/src/modules/DefaultInput.ts b/src/modules/DefaultInput.ts index 9c5557a..65f1b5f 100644 --- a/src/modules/DefaultInput.ts +++ b/src/modules/DefaultInput.ts @@ -1,11 +1,11 @@ import { realType } from "../Functions" -import InputAbstract from "./InputAbstract" +import AbstractInput from "./AbstractInput" import InputIdentity from "./Interfaces/InputIdentity" -export default class DefaultInput extends InputAbstract { +export default class DefaultInput extends AbstractInput { public setValue(value: any) { - this.element.value = this.formatValue(value) + this.element.value = this.formatValue(value) || "" } public getValue(): any { @@ -13,7 +13,6 @@ export default class DefaultInput extends InputAbstract { } public formatValue(value: any): any { - if (typeof value === "undefined") return "" // if the value is a number return it as a number obj return realType(value) } diff --git a/src/modules/Interfaces/InputArray.ts b/src/modules/Interfaces/InputArray.ts index c271c2b..6dda3a0 100644 --- a/src/modules/Interfaces/InputArray.ts +++ b/src/modules/Interfaces/InputArray.ts @@ -1,4 +1,4 @@ -import InputAbstract from "../InputAbstract"; +import AbstractInput from "../AbstractInput"; /** * this interface is used for fetching and setting `name` to `FMInput` link @@ -6,5 +6,5 @@ import InputAbstract from "../InputAbstract"; * @interface InputArray */ export default interface InputArray { - [key: string]: InputAbstract + [key: string]: AbstractInput } diff --git a/src/modules/Interfaces/InputIdentity.ts b/src/modules/Interfaces/InputIdentity.ts index 9df7a5a..562f519 100644 --- a/src/modules/Interfaces/InputIdentity.ts +++ b/src/modules/Interfaces/InputIdentity.ts @@ -1,8 +1,8 @@ -import InputAbstract from "../InputAbstract"; +import AbstractInput from "../AbstractInput"; import FormManager from "../../FormManager"; interface InputConstructor { - new(element: HTMLElement, form: FormManager): InputAbstract + new(element: HTMLElement, form: FormManager): AbstractInput } /** diff --git a/src/modules/NumberInput.ts b/src/modules/NumberInput.ts new file mode 100644 index 0000000..6e2d45d --- /dev/null +++ b/src/modules/NumberInput.ts @@ -0,0 +1,25 @@ +import InputIdentity from './Interfaces/InputIdentity'; +import DefaultInput from './DefaultInput'; +import { toNumber, isNumber } from '../Functions'; + +/** + * + * @class FMDateInput + * @extends {FMInput} + */ +export default class NumberInput extends DefaultInput { + + public formatValue(value: any): number|undefined { + const n = toNumber(value) + return n + } + + public verify(): boolean { + return typeof this.getValue() === "undefined" || isNumber(this.getValue()) + } + + public static identity: InputIdentity = { + input: NumberInput, + type: "number" + } +} diff --git a/src/modules/RepeatInput.ts b/src/modules/RepeatInput.ts index a23d10d..4d609d3 100644 --- a/src/modules/RepeatInput.ts +++ b/src/modules/RepeatInput.ts @@ -1,6 +1,6 @@ import FormManager from "../FormManager" import DefaultInput from './DefaultInput'; -import InputAbstract from './InputAbstract'; +import InputAbstract from './AbstractInput'; import InputIdentity from './Interfaces/InputIdentity'; /** @@ -89,14 +89,15 @@ export default class RepeatInput extends DefaultInput { input.element.disabled = true } sub.push(input) + // if values is a named array if (values != undefined && values[input.getName()] != undefined) { input.setValue(values[input.getName()]) + return } - // if value is a single string/number/etc - if (typeof(values) != "object" && values != undefined) { - input.setValue(values) - } + + // Set default value + input.setValue(values) }) this.elements.push(sub) diff --git a/src/modules/SelectInput.ts b/src/modules/SelectInput.ts index fc0817f..ca7f0c2 100644 --- a/src/modules/SelectInput.ts +++ b/src/modules/SelectInput.ts @@ -9,14 +9,28 @@ import { realType } from '../Functions'; */ export default class SelectInput extends DefaultInput { - public formatValue(value: any): any { - if (typeof value === "undefined") { - const opt: HTMLOptionElement|null = this.element.querySelector("option[selected]") - if (opt) { - return opt.value - } + public setValue(value: any) { + value = realType(value) + let opt:HTMLOptionElement|undefined = this.element.querySelector(`option[value="${value}"]`) || undefined + if (opt) { + this.element.value = value + return } - return realType(value) + opt = this.element.querySelector("option[selected]") || undefined + if (opt) { + this.element.value = opt.value + } + } + + public formatValue(value: any): any { + if (typeof value !== "undefined" && value !== "") { + return realType(value) + } + const opt: HTMLOptionElement|undefined = this.element.querySelector("option[selected]") || undefined + if (opt) { + return opt.value + } + throw Error(":O it should never come here") } public static identity: InputIdentity = {