Browse Source

Updated

tags/0.2.0
Florian Bouillon 2 months ago
parent
commit
80b15c964d
Signed by: Avior <florian.bouillon@delta-wings.net> GPG Key ID: B143FF27EF555D16
19 changed files with 720 additions and 275 deletions
  1. +4
    -0
      .gitignore
  2. +5
    -0
      .npmignore
  3. +69
    -0
      CHANGELOG.md
  4. +95
    -0
      CONTRIBUTING.md
  5. +0
    -76
      FMInput.ts
  6. +21
    -0
      LICENSE.md
  7. +78
    -106
      README.md
  8. +0
    -62
      modules/FMDatalistInput.ts
  9. +10
    -3
      package.json
  10. +103
    -0
      src/FMInput.ts
  11. +32
    -16
      src/FormManager.ts
  12. +0
    -0
      src/Interfaces.ts
  13. +71
    -0
      src/modules/FMDatalistInput.ts
  14. +8
    -4
      src/modules/FMDateInput.ts
  15. +118
    -0
      src/modules/FMFileInput.ts
  16. +12
    -8
      src/modules/FMRepeatInput.ts
  17. +23
    -0
      src/modules/FMSelectInput.ts
  18. +63
    -0
      tsconfig.json
  19. +8
    -0
      yarn.lock

+ 4
- 0
.gitignore View File

@@ -0,0 +1,4 @@
node_modules/
*.js
*.d.ts
build.json

+ 5
- 0
.npmignore View File

@@ -0,0 +1,5 @@
src/
.gitignore
build.json
tsconfig.json
yarn.lock

+ 69
- 0
CHANGELOG.md View File

@@ -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
<!-- [Unreleased]: https://github.com/olivierlacan/keep-a-changelog/compare/v1.0.0...HEAD -->
[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

+ 95
- 0
CONTRIBUTING.md View File

@@ -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/

+ 0
- 76
FMInput.ts View File

@@ -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
}
}

+ 21
- 0
LICENSE.md View File

@@ -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.

+ 78
- 106
README.md View File

@@ -1,137 +1,109 @@
# FormManager
# Form Manager

A powerfull Manager for all your forms

## Table of Content

## Table of Content <!-- omit in toc -->
- [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'
import FMDateInput from '../FormManagerGit/modules/FMDateInput'
fm.assign({
input: FMDateInput,
type: "date",
tagName: "input"
});

// add the modules to the Form
fm.assign(FMRepeatInputAssignment)
fm.assign(FMDateInputAssignement)
fm.assign(FMDatalistAssignement)
// After adding modules run to reffect modules to inputs
fm.setupInputs();

// reload the manager to use the new modules
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)

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
*/
fm.assign({
input: FMRepeatInput,
classes: "custom-class",
attributes: "data-custom",
type: "number", //for input
tagName: "div"
})
```
// 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)

### datalist changes
// change if you only see the form or edit them
fm.setMode(FMMode.ViewMode or FMMode.EditMode)

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
// same thing as before but just for one field
fm.setModeForInput(FMMode.ViewMode or FMMode.EditMode, "inputName")

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`***
// Reset the form to it's defaults values
fm.clear()

ex:
```html
<input name="listing" list="list" data-strict/>
<datalist id="list">
<option data-value="value submitted" value="shown value">value subtitle</option>
<option data-value="value submitted" value="shown valuee">value subtitle</option>
<option data-value="value submitted" value="shown valueq">value subtitle</option>
<option data-value="value submitted" value="shown valuea">value subtitle</option>
</datalist>
```

### date changes
## Modules & Attributes

`FMDateInput` change the result type from a string to a `Date` object
and if `data-default` is set the current date will be set
### Modules

ex:
```html
<input type="date" name="date" data-default />
```
| 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 |

### Repeat Element

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
<div class="fm-repeat" name="repeat-element"> <!-- container -->
<div class="fmr-template"> <!-- template container (won't show) -->
<input data-input type="text"/> <!-- data-input replace the name element -->
<!-- if there is only one input the name will be `testName[x]` with `x` being the index -->
<!-- if there is only multiple inputs the name will be `testName[x][index]` -->
<div class="fmr-del"> <!-- delete button container -->
<button></button>
</div>
</div>
<!-- future content position -->
<!-- EXample content from above
<div>
<input data-input type="text"/>
<div class="fmr-del">
<button></button>
</div>
</div>
-->
<!-- future content position -->
<div class="fmr-add"> <!-- container for add button -->
<button></button>
</div>
</div>
```
### Attributes

| 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 |

## Issues

## TODO LIST
Complete listing [here](https://git.delta-wings.net/dzeio/FormManager/issues)

more Listing [here](https://git.delta-wings.net/dzeio/FormManager/issues)
## Changelog

- [ ] 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)

+ 0
- 62
modules/FMDatalistInput.ts View File

@@ -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"
}

+ 10
- 3
package.json View File

@@ -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"
}
}

+ 103
- 0
src/FMInput.ts View File

@@ -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
}
}

FormManager.ts → src/FormManager.ts View File

@@ -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<HTMLInputElement>).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<HTMLInputElement>).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

Interfaces.ts → src/Interfaces.ts View File


+ 71
- 0
src/modules/FMDatalistInput.ts View File

@@ -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"
}

modules/FMDateInput.ts → src/modules/FMDateInput.ts View File

@@ -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"

+ 118
- 0
src/modules/FMFileInput.ts View File

@@ -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"
}

modules/FMRepeatInput.ts → src/modules/FMRepeatInput.ts View File

@@ -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<HTMLElement>).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"

+ 23
- 0
src/modules/FMSelectInput.ts View File

@@ -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"
}

+ 63
- 0
tsconfig.json View File

@@ -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. */
}
}

+ 8
- 0
yarn.lock View File

@@ -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==

Loading…
Cancel
Save