diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..4f9bd2d
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,4 @@
+node_modules/
+*.js
+*.d.ts
+build.json
diff --git a/.npmignore b/.npmignore
new file mode 100644
index 0000000..a8b13b5
--- /dev/null
+++ b/.npmignore
@@ -0,0 +1,5 @@
+src/
+.gitignore
+build.json
+tsconfig.json
+yarn.lock
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..14edd7e
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,69 @@
+# Changelog
+All notable changes to this project will be documented in this file.
+
+The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
+and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+
+## Unreleased
+
+### Added
+### Changed
+### Fixed
+### Removed
+
+## [0.2.0] - 2019-10-24
+
+### Added
+- .gitignore
+- .npmignore
+- CHANGELOG.md
+- CONTRIBUTING.md
+- LICENSE.md
+- README.md
+- too much things
+
+### Changed
+- everything
+
+### Fixed
+- everything
+
+## [0.1.0] - 2019-08-27
+### Added
+- FormManager
+ - assign
+ - setupInputs
+ - getInit
+ - verify
+ - submit
+ - getJSON
+ - fillFromJSON
+ - fillFromURI
+ - clear
+- Interfaces
+ - InputArrayInterface
+ - FMAssignInterface
+- FMInput
+ - setValue
+ - getValue
+ - getDefault
+ - getName
+ - verify
+- modules
+ - FMRepeatInput
+ - loopInputs
+ - setValue
+ - getValue
+ - Assignement
+ - FMDateInput
+ - setValue
+ - getValue
+ - getDefault
+ - Assignement
+ - FMDatalistInput
+ - setValue
+ - getValue
+ - Assignement
+
+[0.2.0]: https://git.delta-wings.net/dzeio/FormManager/src/tag/0.2.0
+[0.1.0]: https://git.delta-wings.net/dzeio/FormManager/src/tag/0.1.0
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 0000000..296b1c3
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,95 @@
+# Contributing
+
+When contributing to this repository, please first discuss the change you wish to make via issue,
+email, or any other method with the owners of this repository before making a change.
+
+Please note we have a code of conduct, please follow it in all your interactions with the project.
+
+## Pull Request Process
+
+1. Ensure any install or build dependencies are removed before the end of the layer when doing a
+ build.
+2. Update the [README.md][readme] with details of changes to the interface, this includes new environment
+ variables, exposed ports, useful file locations and container parameters.
+3. Increase the version numbers in any examples files and the [README.md][readme] to the new version that this
+ Pull Request would represent. The versioning scheme we use is [SemVer][semver].
+4. You may merge the Pull Request in once you have the sign-off of two other developers, or if you
+ do not have permission to do that, you may request the second reviewer to merge it for you.
+
+## Code of Conduct
+
+### Our Pledge
+
+In the interest of fostering an open and welcoming environment, we as
+contributors and maintainers pledge to making participation in our project and
+our community a harassment-free experience for everyone, regardless of age, body
+size, disability, ethnicity, gender identity and expression, level of experience,
+nationality, personal appearance, race, religion, or sexual identity and
+orientation.
+
+### Our Standards
+
+Examples of behavior that contributes to creating a positive environment
+include:
+
+* Using welcoming and inclusive language
+* Being respectful of differing viewpoints and experiences
+* Gracefully accepting constructive criticism
+* Focusing on what is best for the community
+* Showing empathy towards other community members
+
+Examples of unacceptable behavior by participants include:
+
+* The use of sexualized language or imagery and unwelcome sexual attention or
+advances
+* Trolling, insulting/derogatory comments, and personal or political attacks
+* Public or private harassment
+* Publishing others' private information, such as a physical or electronic
+ address, without explicit permission
+* Other conduct which could reasonably be considered inappropriate in a
+ professional setting
+
+### Our Responsibilities
+
+Project maintainers are responsible for clarifying the standards of acceptable
+behavior and are expected to take appropriate and fair corrective action in
+response to any instances of unacceptable behavior.
+
+Project maintainers have the right and responsibility to remove, edit, or
+reject comments, commits, code, wiki edits, issues, and other contributions
+that are not aligned to this Code of Conduct, or to ban temporarily or
+permanently any contributor for other behaviors that they deem inappropriate,
+threatening, offensive, or harmful.
+
+### Scope
+
+This Code of Conduct applies both within project spaces and in public spaces
+when an individual is representing the project or its community. Examples of
+representing a project or community include using an official project e-mail
+address, posting via an official social media account, or acting as an appointed
+representative at an online or offline event. Representation of a project may be
+further defined and clarified by project maintainers.
+
+### Enforcement
+
+Instances of abusive, harassing, or otherwise unacceptable behavior may be
+reported by contacting the project team at [contact@delta-wings.net][email]. All
+complaints will be reviewed and investigated and will result in a response that
+is deemed necessary and appropriate to the circumstances. The project team is
+obligated to maintain confidentiality with regard to the reporter of an incident.
+Further details of specific enforcement policies may be posted separately.
+
+Project maintainers who do not follow or enforce the Code of Conduct in good
+faith may face temporary or permanent repercussions as determined by other
+members of the project's leadership.
+
+### Attribution
+
+This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
+available at [http://contributor-covenant.org/version/1/4][version]
+
+[readme]: ./README.md
+[semver]: http://semver.org/
+[email]: mailto:contact@delta-wings.net
+[homepage]: http://contributor-covenant.org
+[version]: http://contributor-covenant.org/version/1/4/
diff --git a/FMInput.ts b/FMInput.ts
deleted file mode 100644
index 8cbb2b0..0000000
--- a/FMInput.ts
+++ /dev/null
@@ -1,76 +0,0 @@
-import FormManager from "./FormManager"
-
-export default class FMInput {
-
- element: HTMLInputElement
- form: FormManager
-
- constructor(element: HTMLElement, form: FormManager) {
- this.element = element as HTMLInputElement
- this.form = form
- this.setToDefault()
- }
-
- /**
- * Set the element Value
- *
- * @param {*} value
- * @memberof FMInput
- */
- setValue(value: any) {
- this.element.value = value
- this.element.setAttribute("value", value)
- }
-
- /**
- * Get the element value
- *
- * @returns {*} the value
- * @memberof FMInput
- */
- getValue(): any {
- return this.formatValue(this.element.value)
- }
-
- formatValue(value: any): any {
- if (!isNaN(Number(value))) return Number(value)
- return value
-
- }
-
- getDefault(args: string): any {
- if (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("")
- }
-
- getName(): string {
- return this.element.getAttribute("name") == undefined ? this.element.dataset.name : this.element.getAttribute("name")
- }
-
- /**
- * Verify if the element is correct
- *
- * @returns {boolean}
- * @memberof FMInput
- */
- verify(): boolean {
- let val: string = this.getValue()
- if(val == "" && this.element.hasAttribute("required")) {
- return false
- }
- if(this.element.dataset.regex !== undefined) {
- return new RegExp(this.element.dataset.regex, 'g').test(val as string)
- }
- return true
- }
-}
diff --git a/LICENSE.md b/LICENSE.md
new file mode 100644
index 0000000..f0360cd
--- /dev/null
+++ b/LICENSE.md
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2019 Delta Wings
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/README.md b/README.md
index 68c2378..277dedd 100644
--- a/README.md
+++ b/README.md
@@ -1,137 +1,109 @@
-# FormManager
+# Form Manager
A powerfull Manager for all your forms
+## Table of Content
-## Table of Content
+- [Form Manager](#form-manager)
+ - [Table of Content](#table-of-content)
+ - [Installation](#installation)
+ - [usage](#usage)
+ - [Typescript](#typescript)
+ - [Modules & Attributes](#modules--attributes)
+ - [Modules](#modules)
+ - [Attributes](#attributes)
+ - [Issues](#issues)
+ - [Changelog](#changelog)
-- [FormManager](#formmanager)
- - [Base](#base)
- - [modules](#modules)
- - [datalist changes](#datalist-changes)
- - [date changes](#date-changes)
- - [Repeat Element](#repeat-element)
- - [TODO LIST](#todo-list)
+## Installation
-## Base
+nothing difficult
-to have the basic system just import `FormManager`
+```bash
+yarn add @dzeio/form-manager
+or
+npm install @dzeio/form-manager
+```
+
+## usage
+
+### Typescript
```ts
import FormManager from '@dzeio/form-manager';
-const fm = new FormManager(docuement.getElementById("formId"))
-```
+const fm = new FormManager(docuement.getElementById("form"));
-from now on you can get datas by using `fm.getJSON()` fill by using `fm.fillFromJSON()` or `fm.fillFromURI()` verify datas with `fm.verify()`
+// add modules
+import { FMRepeatAssignment } from '@dzeio/FormManager/modules/FMRepeatInput'
-## modules
+fm.assign(FMRepeatAssignment)
-Actually there is 3 modules included in the system _non of them are loaded_
-each modules add new functionality to the form (see later)
+// or
-to load a module:
-```ts
-//the three modules availables
-import { FMRepeatInputAssignment } from '@dzeio/form-manager/modules/FMRepeatInput'
-import { FMDatalistAssignement } from '@dzeio/form-manager/modules/FMDatalistInput'
-import { FMDateInputAssignement } from '@dzeio/form-manager/modules/FMDateInput'
-
-// add the modules to the Form
-fm.assign(FMRepeatInputAssignment)
-fm.assign(FMDateInputAssignement)
-fm.assign(FMDatalistAssignement)
-
-// reload the manager to use the new modules
-fm.setupInputs()
-
-```
-
-you can customise how the module assign himself to an element by doing this:
-```ts
-/*
- input: typeof FMInput
- classes?: string[] | string
- attributes?: string[] | string
- type?: string
- tagName?: string
-*/
+import FMDateInput from '../FormManagerGit/modules/FMDateInput'
fm.assign({
- input: FMRepeatInput,
- classes: "custom-class",
- attributes: "data-custom",
- type: "number", //for input
- tagName: "div"
-})
+ input: FMDateInput,
+ type: "date",
+ tagName: "input"
+});
+
+// After adding modules run to reffect modules to inputs
+fm.setupInputs();
+
+// verify form validity:
+fm.verify(); //return true if valid else return false
+// if it returns false you can use the variable under to see th FMInput that isnt valid
+fm.lastErroredInput
+
+// submit your data to an endpoint
+fm.submit("/api/idk", (ev) => {/* onloaded callback*/}, /* verify datas beforehand default:true*/ true)
+
+// get the json of your form
+fm.getJSON()
+
+// fill form from URI (datas MUST be in JSON (see getJSON for examples))
+fm.fillFromURI("uri")
+
+// same as before but you give the json from ts
+fm.fillFromJSON(json)
+
+// change if you only see the form or edit them
+fm.setMode(FMMode.ViewMode or FMMode.EditMode)
+
+// same thing as before but just for one field
+fm.setModeForInput(FMMode.ViewMode or FMMode.EditMode, "inputName")
+
+// Reset the form to it's defaults values
+fm.clear()
+
```
-### datalist changes
+## Modules & Attributes
-the values for the datalist will not be the `value` attribute anymore but a `data-value` so the end user will see wht you wnt and not the value you want to send
+### Modules
-even if you set `data-strict` the result value will only be set if it's from one of the option
-if not set the value will stay set by what the user wrote
-***ATTENTION if multiple `option` has the same `value` attribute the final value will be the `data-value` of the first `value`***
+| Module name | Description |
+| :---------: | :---------: |
+| [Datalist](https://git.delta-wings.net/dzeio/FormManager/wiki/modules.datalist) | Manage the datalist better than ever ! |
+| [Date](https://git.delta-wings.net/dzeio/FormManager/wiki/modules.date) | Manage the date element |
+| [File](https://git.delta-wings.net/dzeio/FormManager/wiki/modules.file) | Manage single file uploads |
+| [Repeat](https://git.delta-wings.net/dzeio/FormManager/wiki/modules.repeat) | Make your fields repeatable ! |
+| [Select](https://git.delta-wings.net/dzeio/FormManager/wiki/modules.select) | Fix your Select |
-ex:
-```html
-
-
-```
+### Attributes
-### date changes
+| Attribute name | Description |
+| :------------: | :---------: |
+| [data-autoset](https://git.delta-wings.net/dzeio/FormManager/wiki/attribute.data-autoset) | Update your value in _near_ realtime |
+| [data-default](https://git.delta-wings.net/dzeio/FormManager/wiki/attribute.data-default) | a better value than `value` |
+| [data-ignore](https://git.delta-wings.net/dzeio/FormManager/wiki/attribute.data-ignore) | i don't see this |
+| [data-regex](https://git.delta-wings.net/dzeio/FormManager/wiki/attribute.data-regex) | regex your value |
-`FMDateInput` change the result type from a string to a `Date` object
-and if `data-default` is set the current date will be set
+## Issues
-ex:
-```html
-
-```
+Complete listing [here](https://git.delta-wings.net/dzeio/FormManager/issues)
-### Repeat Element
+## Changelog
-the Repeat element allow to add/delete multiple time the sames input(s)
-
-***NOTE***_: actually the filling don't work but the rest work just fine_
-
-Organisation
-```html
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-```
-
-## TODO LIST
-
-more Listing [here](https://git.delta-wings.net/dzeio/FormManager/issues)
-
-- [ ] add `data-autoset` to autofill the input with data from another one _with `readonly` and `disabled`_
-- [ ] allow filling of `FMRepeatInput`
-- [ ] add `data-regex` for verification
-- [ ] add `data-ignore` mainly for `data-autoset`
+[here](./CHANGELOG.md)
diff --git a/modules/FMDatalistInput.ts b/modules/FMDatalistInput.ts
deleted file mode 100644
index ac8b243..0000000
--- a/modules/FMDatalistInput.ts
+++ /dev/null
@@ -1,62 +0,0 @@
-import { FMAssignInterface } from '../Interfaces';
-import FormManager from "../FormManager"
-import FMInput from "../FMInput"
-
-/**
- * the upgraded datalist element
- * @priority 2
- * @class FMDatalistInput
- * @extends {FMInput}
- */
-export default class FMDatalistInput extends FMInput {
-
- datalist: HTMLDataListElement
- isStrict: boolean
-
- constructor(element: HTMLInputElement, form: FormManager) {
- super(element, form)
- this.isStrict = this.element.hasAttribute("data-strict")
- let id = this.element.getAttribute("list")
- let tmpDatalist = document.getElementById(id)
- this.datalist = tmpDatalist !== undefined ? tmpDatalist as HTMLDataListElement : undefined
- }
-
- setValue(value: string) {
- if (value == "") {
- this.element.value = ""
- return
- }
- if (this.datalist) {
- if ((value as any).id != undefined) {
- value = (value as any).id
- }
- let option: HTMLOptionElement = this.datalist.querySelector(`[data-value="${value}"]`)
- if (option != undefined) {
- this.element.value = option.value
- return
- }
- if (option == undefined && !this.isStrict) {
- this.element.value = value
- return
- }
- if (option || !this.isStrict) {
- this.element.value = value
- return
- }
- }
- }
-
- getValue(): string {
- if (this.datalist) {
- let option: HTMLOptionElement = this.datalist.querySelector(`[value="${this.element.value}"]`)
- if (option) return this.formatValue(option.dataset.value)
- }
- return this.isStrict ? undefined : this.formatValue(this.element.value)
- }
-}
-
-export const FMDatalistAssignement: FMAssignInterface = {
- input: FMDatalistInput,
- attributes: "list",
- tagName: "input"
-}
diff --git a/package.json b/package.json
index dc2f405..3e48b7a 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,8 @@
{
"name": "@dzeio/form-manager",
- "version": "0.1.1",
- "main": "./FormManager.ts",
+ "version": "0.2.0",
+ "main": "./dist/FormManager.js",
+ "types": "./dist/FormManager.d.ts",
"description": "A powerfull Form Manager",
"repository": {
"type": "git",
@@ -17,5 +18,11 @@
"url": "https://dze.io"
},
"license": "MIT",
- "private": false
+ "private": false,
+ "devDependencies": {
+ "typescript": "^3.6.4"
+ },
+ "scripts": {
+ "build": "tsc"
+ }
}
diff --git a/src/FMInput.ts b/src/FMInput.ts
new file mode 100644
index 0000000..a64ba61
--- /dev/null
+++ b/src/FMInput.ts
@@ -0,0 +1,103 @@
+import FormManager from "./FormManager"
+
+export default class FMInput {
+
+ element: HTMLInputElement
+ form: FormManager
+ required: boolean
+
+ constructor(element: Element, form: FormManager) {
+ 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
+ * @memberof FMInput
+ */
+ setValue(value: any) {
+ this.element.value = value
+ }
+
+ /**
+ * Get the element value
+ *
+ * @returns {*} the value
+ * @memberof FMInput
+ */
+ 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
+ *
+ * @protected
+ * @param {*} value
+ * @returns {*}
+ * @memberof FMInput
+ */
+ protected 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(""))
+ }
+
+ /**
+ * Retrieve the input name
+ *
+ * @returns {string}
+ * @memberof FMInput
+ */
+ 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;
+ if (attr) return attr
+ throw Error("Error: could not get input name!")
+ }
+
+ /**
+ * Verify if the element is correct
+ *
+ * @returns {boolean}
+ * @memberof FMInput
+ */
+ 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/FormManager.ts b/src/FormManager.ts
similarity index 90%
rename from FormManager.ts
rename to src/FormManager.ts
index f298aae..d2c24a0 100644
--- a/FormManager.ts
+++ b/src/FormManager.ts
@@ -133,19 +133,16 @@ export default class FormManager {
* @type {FMInput}
* @memberof FormManager
*/
- public lastErroredInput: FMInput
+ public lastErroredInput: FMInput|undefined
-
- private _form: HTMLFormElement
/**
* The Form Element of the FM
*
- * @private
* @type {HTMLFormElement}
* @memberof FormManager
*/
- public set form(v : HTMLFormElement) {this._form = v}
- public get form(): HTMLFormElement {return this._form}
+ public form: HTMLFormElement
+
/**
* Creates an instance of FormManager.
@@ -169,9 +166,9 @@ export default class FormManager {
this.setupInputs()
setInterval(() => {
- this.form.querySelectorAll("[data-autoset]").forEach((el: HTMLInputElement) => {
+ (this.form.querySelectorAll("[data-autoset]") as NodeListOf).forEach((el: HTMLInputElement) => {
let autosetStr = el.dataset.autoset
- if (autosetStr.startsWith("run:")) {
+ if (autosetStr && autosetStr.startsWith("run:")) {
let tmp = autosetStr.split("run:")[1]
el.value = eval(tmp)
}
@@ -195,9 +192,10 @@ export default class FormManager {
* @memberof FormManager
*/
public setupInputs() {
- this.form.querySelectorAll("[name]:not([data-name])").forEach((element: HTMLElement) => {
+ this.inputs = {}
+ this.form.querySelectorAll("[name]:not([data-name])").forEach((element: Element) => {
let el = this.getInit(element)
- this.inputs[el.getName()] = el
+ if (el) this.inputs[el.getName()] = el
});
}
@@ -208,10 +206,10 @@ export default class FormManager {
* @returns {FMInput}
* @memberof FormManager
*/
- public getInit(element: HTMLElement): FMInput {
+ public getInit(element: Element): FMInput|undefined {
inputsLoop: for (const input of this.FMInputs) {
if (input.classes != undefined) {
- let tmpList: string[]
+ let tmpList: string[] = []
if (typeof input.classes == "object") tmpList = input.classes
if (typeof input.classes === "string") tmpList = [input.classes]
for (const classe of tmpList) {
@@ -219,7 +217,7 @@ export default class FormManager {
}
}
if (input.attributes != undefined) {
- let tmpList: string[]
+ let tmpList: string[] = []
if (typeof input.attributes == "object") tmpList = input.attributes
if (typeof input.attributes === "string") tmpList = [input.attributes]
for (const classe of tmpList) {
@@ -334,6 +332,7 @@ export default class FormManager {
}
public setMode(mode: FMMode) {
+ console.log(mode)
if (mode == FMMode.ViewMode) {
for (const name in this.inputs) {
if (this.inputs.hasOwnProperty(name)) {
@@ -353,13 +352,28 @@ 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", "")
+ }
+ return
+ }
+ if (mode == FMMode.EditMode) {
+ if (this.inputs[inputName]) {
+ this.inputs[inputName].element.removeAttribute("disabled")
+ }
+ }
+ }
+
/**
* Clear the fields in the form
*
* @memberof FormManager
*/
public clear() {
- this.form.querySelectorAll("[name]").forEach((el: HTMLInputElement) => {
+ (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];
@@ -377,7 +391,7 @@ export enum FMMode {
/**
* TODO: FMFileInput
- * have a data-endpoint with an URI
+ * have a data-type with an typeId linked to an URI
* on file set -> show button to upload
* on file change -> show button "delete and upload"
* on upload -> upload and create hidden field with the result ID
@@ -392,7 +406,9 @@ export enum FMMode {
* retrieve pic: /enpoint?get=pic-id
* return {uri:"/static/pic-name.jpg"} if it exist
* return {error:true,msg:"picture don't exist"} is pic dont exist
- * upload pic: /enpoint?upload
+ * upload pic: /enpoint?upload&type=x
+ * with type is a type id (to set a different location in the system)
+ * _default to type 1_
* return {uploaded:true,id:2}
* delete pic: /endpoint?del=pic-id
* return {deleted=true} if deleted
diff --git a/Interfaces.ts b/src/Interfaces.ts
similarity index 100%
rename from Interfaces.ts
rename to src/Interfaces.ts
diff --git a/src/modules/FMDatalistInput.ts b/src/modules/FMDatalistInput.ts
new file mode 100644
index 0000000..65baa07
--- /dev/null
+++ b/src/modules/FMDatalistInput.ts
@@ -0,0 +1,71 @@
+import { FMAssignInterface } from '../Interfaces';
+import FormManager from "../FormManager"
+import FMInput from "../FMInput"
+
+/**
+ * the upgraded datalist element
+ * @class FMDatalistInput
+ * @extends {FMInput}
+ */
+export default class FMDatalistInput extends FMInput {
+
+ datalist: HTMLDataListElement
+ isStrict: boolean
+
+ constructor(element: HTMLInputElement, form: FormManager) {
+ super(element, form)
+
+ // check if input is strict on inputs
+ this.isStrict = this.element.hasAttribute("data-strict")
+
+ // get datalist id
+ let id = this.element.getAttribute("list")
+ if (!id) throw Error(`Error: your input "${this.getName()}" MUST have a list attribute`);
+
+ // get datalist
+ this.datalist = document.getElementById(id) as HTMLDataListElement
+ if (!this.datalist) throw Error(`Error: Datalist not found for ${this.getName()} input`)
+ }
+
+ setValue(value: string) {
+ // if value is "" set value to ""
+ if (value == "") {
+ this.element.value = ""
+ return
+ }
+ // value is an object containing an id then set value to the id
+ if ((value as any).id != undefined) {
+ value = (value as any).id
+ }
+
+ // get the option element containing the value
+ let option = this.datalist.querySelector(`[data-value="${value}"]`)
+
+ // if it was set set the element value to the option value
+ if (option != undefined) {
+ this.element.value = (option as HTMLOptionElement).value
+ return
+ }
+
+ // if datalist is not strict set it to the value inputted
+ if (!this.isStrict) {
+ this.element.value = value
+ return
+ }
+ }
+
+ getValue(): string {
+ // if element value == option value return option data-value
+ let option = this.datalist.querySelector(`[value="${this.element.value}"]`)
+ if (option) return this.formatValue((option as HTMLOptionElement).dataset.value)
+
+ // if strict return undefined else return element value
+ return this.isStrict ? undefined : this.formatValue(this.element.value)
+ }
+}
+
+export const FMDatalistAssignement: FMAssignInterface = {
+ input: FMDatalistInput,
+ attributes: "list",
+ tagName: "input"
+}
diff --git a/modules/FMDateInput.ts b/src/modules/FMDateInput.ts
similarity index 50%
rename from modules/FMDateInput.ts
rename to src/modules/FMDateInput.ts
index be2313a..ffb62dd 100644
--- a/modules/FMDateInput.ts
+++ b/src/modules/FMDateInput.ts
@@ -1,4 +1,4 @@
-import { FMAssignInterface } from './../Interfaces';
+import { FMAssignInterface } from '../Interfaces';
import FMInput from "../FMInput"
/**
@@ -9,22 +9,26 @@ 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)
}
this.element.valueAsDate = value
}
- getValue(): Date {
- return this.element.valueAsDate
+ getValue(): Date|undefined {
+ // if get date and if null return undefined else return value
+ let date = this.element.valueAsDate
+ return date == null ? undefined : date
}
getDefault(args: string): Date {
+ // if data-default is present return the current date
return new Date
}
}
-export const FMDateInputAssignement: FMAssignInterface = {
+export const FMDateAssignement: FMAssignInterface = {
input: FMDateInput,
type: "date",
tagName: "input"
diff --git a/src/modules/FMFileInput.ts b/src/modules/FMFileInput.ts
new file mode 100644
index 0000000..8879f73
--- /dev/null
+++ b/src/modules/FMFileInput.ts
@@ -0,0 +1,118 @@
+import { FMAssignInterface } from '../Interfaces';
+import FMInput from "../FMInput"
+import FormManager from '../FormManager';
+
+/**
+ *
+ * @class FMFileInput
+ * @extends {FMInput}
+ */
+export default class FMFileInput extends FMInput {
+
+ isUploaded = false
+
+ type = 1
+
+
+
+ button: HTMLButtonElement
+
+ constructor(element: HTMLInputElement, form: FormManager) {
+ super(element, form)
+
+ this.type = this.element.dataset.uploadType ? parseInt(this.element.dataset.uploadType): 1
+
+ element.addEventListener("change", () => {
+ console.log("pouet")
+ let files = element.files
+ if (files && element.parentElement && files.length > 0) {
+ const name = element.parentElement.querySelector(".file-name")
+ if (name) name.textContent = files[0].name
+ }
+ })
+
+ if (this.element.hasAttribute("data-button") && element.parentElement && element.dataset.button) {
+ let btn = element.parentElement.querySelector(element.dataset.button)
+ this.button = btn ? btn as HTMLButtonElement : undefined
+ // this.button = element.parentElement.querySelector(element.dataset.button)
+ }
+
+ if (this.button) {
+ this.button.addEventListener("click", () => {
+ if (!this.element.disabled) {
+ console.log("pouet!")
+ this.upload()
+ }
+ })
+ }
+
+
+ }
+
+ upload() {
+ // if (this.form.getJSON()["id"] == 0 || this.form.getJSON()["id"] == undefined) {
+ // NotificationManager.getNotificationManager().add("Merci de sauvegarder l'offre au moins une fois !")
+ // }
+ let files = this.element.files
+ if (files && files.length > 0) {
+ const file = files[0]
+ const ajax = new XMLHttpRequest
+ let form = new FormData
+ form.append(this.getName(), file, file.name)
+ ajax.open("POST", `/api/file?upload&type=${this.type}`)
+ ajax.addEventListener("load", (ev) => {
+ console.log(ev)
+ })
+ ajax.addEventListener("progress", (ev) => {
+ console.log(ev)
+ })
+ ajax.addEventListener("loadstart", () => {
+ if (this.button) this.button.classList.add("is-loading")
+ if (!this.element.hasAttribute("disabled")) {
+ this.element.setAttribute("disabled", "")
+ this.element.setAttribute("data-uploading", "")
+ }
+ })
+ ajax.addEventListener("loadend", () => {
+ if (this.button) this.button.classList.remove("is-loading")
+ if (this.element.hasAttribute("disabled") && this.element.hasAttribute("data-uploading")) {
+ this.element.removeAttribute("disabled")
+ this.element.removeAttribute("data-uploading")
+ }
+ if (this.button) this.button.innerText = "Uploaded!"
+ this.element.dataset.id = JSON.parse(ajax.responseText).id
+ ajax.responseText
+ })
+ ajax.send(form)
+ }
+ }
+
+ setValue(value: string|number) {
+ if (value == "") {
+ this.element.dataset.id = value + ""
+ if (this.element.parentElement) {
+ const name = this.element.parentElement.querySelector(".file-name")
+ if (name) name.textContent = ""
+ }
+ }
+
+ }
+
+ getValue(): number {
+ return this.element.dataset.id ? parseInt(this.element.dataset.id):0
+ }
+
+ verify() {
+ if (this.element.hasAttribute("required")) {
+ return this.isUploaded
+ }
+ return true
+ }
+
+}
+
+export const FMFileAssignement: FMAssignInterface = {
+ input: FMFileInput,
+ type: "file",
+ tagName: "input"
+}
diff --git a/modules/FMRepeatInput.ts b/src/modules/FMRepeatInput.ts
similarity index 81%
rename from modules/FMRepeatInput.ts
rename to src/modules/FMRepeatInput.ts
index 2becce3..19d3b21 100644
--- a/modules/FMRepeatInput.ts
+++ b/src/modules/FMRepeatInput.ts
@@ -19,20 +19,24 @@ export default class FMRepeatInput extends FMInput {
super(element, form)
//fetch Template
- this.template = element.querySelector(".fmr-template")
+ this.template = element.querySelector(".fmr-template") as HTMLElement
+ if (!this.template) throw Error(`Error: your repeat input "${this.getName()}" MUST have a child with the class .fmr-template`);
+
this.template.style.display = "none"
//fetch add button
- this.addBtn = element.querySelector(".fmr-add")
+ this.addBtn = element.querySelector(".fmr-add") as HTMLElement
+ if (!this.addBtn) throw Error(`Error: your repeat element "${this.getName()}" MUST have a child with the class .fmr-add`);
+
this.addBtn.addEventListener("click", () => {
if (!this.addBtn.hasAttribute("disabled")) this.addLine()
})
- //Observer to handle attributes changes
+ // Observer to handle attributes changes
const observer = new MutationObserver((mutationList: any, observer: any) => {
for (let mutation of mutationList) {
if (mutation.type === 'attributes' && mutation.attributeName === "disabled") {
- this.element.querySelectorAll(".fmr-add, .fmr-del").forEach((el: HTMLElement) => {
+ (this.element.querySelectorAll(".fmr-add, .fmr-del") as NodeListOf).forEach((el: HTMLElement) => {
if (this.element.hasAttribute("disabled")) el.style.display = "none"
else el.style.display = ""
})
@@ -62,7 +66,7 @@ export default class FMRepeatInput extends FMInput {
// loop through inputs ot init them
let sub: FMInput[] = []
- node.querySelectorAll("[data-input]").forEach((el: HTMLElement) => {
+ node.querySelectorAll("[data-input]").forEach((el: Element) => {
let input = this.form.getInit(el)
if (this.element.hasAttribute("disabled")) {
input.element.disabled = true
@@ -81,8 +85,8 @@ export default class FMRepeatInput extends FMInput {
// get the delete button
let del = node.querySelector(".fmr-del")
- del.addEventListener("click", () => {
- if (!del.hasAttribute("disabled")) {
+ if (del) del.addEventListener("click", () => {
+ if (del && !del.hasAttribute("disabled")) {
let id = this.element.querySelectorAll(".fmr-element").length-1
this.elements.splice(id)
node.remove()
@@ -138,7 +142,7 @@ export default class FMRepeatInput extends FMInput {
}
}
-export const FMRepeatInputAssignment: FMAssignInterface = {
+export const FMRepeatAssignment: FMAssignInterface = {
input: FMRepeatInput,
classes: "fm-repeat",
tagName: "div"
diff --git a/src/modules/FMSelectInput.ts b/src/modules/FMSelectInput.ts
new file mode 100644
index 0000000..c61daf0
--- /dev/null
+++ b/src/modules/FMSelectInput.ts
@@ -0,0 +1,23 @@
+import { FMAssignInterface } from '../Interfaces';
+import FMInput from "../FMInput"
+
+/**
+ *
+ * @class FMDateInput
+ * @extends {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
+ }
+ }
+}
+
+export const FMSelectAssignement: FMAssignInterface = {
+ input: FMSelectInput,
+ tagName: "select"
+}
diff --git a/tsconfig.json b/tsconfig.json
new file mode 100644
index 0000000..f9ea7fe
--- /dev/null
+++ b/tsconfig.json
@@ -0,0 +1,63 @@
+{
+ "include": ["src/**/*"],
+ "compilerOptions": {
+ /* Basic Options */
+ "incremental": true, /* Enable incremental compilation */
+ "target": "es5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */
+ "module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
+ // "lib": [], /* Specify library files to be included in the compilation. */
+ // "allowJs": true, /* Allow javascript files to be compiled. */
+ // "checkJs": true, /* Report errors in .js files. */
+ // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */
+ "declaration": true, /* Generates corresponding '.d.ts' file. */
+ // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */
+ // "sourceMap": true, /* Generates corresponding '.map' file. */
+ // "outFile": "./", /* Concatenate and emit output to single file. */
+ "outDir": "./dist", /* Redirect output structure to the directory. */
+ // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
+ // "composite": true, /* Enable project compilation */
+ "tsBuildInfoFile": "./build.json", /* Specify file to store incremental compilation information */
+ "removeComments": true, /* Do not emit comments to output. */
+ // "noEmit": true, /* Do not emit outputs. */
+ // "importHelpers": true, /* Import emit helpers from 'tslib'. */
+ // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
+ // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */
+ /* Strict Type-Checking Options */
+ "strict": true, /* Enable all strict type-checking options. */
+ // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
+ // "strictNullChecks": true, /* Enable strict null checks. */
+ // "strictFunctionTypes": true, /* Enable strict checking of function types. */
+ // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */
+ // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */
+ // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */
+ // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */
+
+ /* Additional Checks */
+ "noUnusedLocals": true, /* Report errors on unused locals. */
+ "noUnusedParameters": true, /* Report errors on unused parameters. */
+ "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
+ "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
+
+ /* Module Resolution Options */
+ // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
+ // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */
+ // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
+ // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
+ // "typeRoots": [], /* List of folders to include type definitions from. */
+ // "types": [], /* Type declaration files to be included in compilation. */
+ // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
+ "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
+ // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */
+ // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
+
+ /* Source Map Options */
+ // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */
+ // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
+ // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */
+ // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */
+
+ /* Experimental Options */
+ // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */
+ // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */
+ }
+}
diff --git a/yarn.lock b/yarn.lock
new file mode 100644
index 0000000..c4a5d4a
--- /dev/null
+++ b/yarn.lock
@@ -0,0 +1,8 @@
+# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
+# yarn lockfile v1
+
+
+typescript@^3.6.4:
+ version "3.6.4"
+ resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.6.4.tgz#b18752bb3792bc1a0281335f7f6ebf1bbfc5b91d"
+ integrity sha512-unoCll1+l+YK4i4F8f22TaNVPRHcD9PA3yCuZ8g5e0qGqlVlJ/8FSateOLLSagn+Yg5+ZwuPkL8LFUc0Jcvksg==