From 7540a4fe5fe0f61d449bc7cc5f5aa7d3da034f55 Mon Sep 17 00:00:00 2001 From: Gregor Kleen Date: Wed, 15 Apr 2020 16:32:45 +0200 Subject: [PATCH] feat(mass-input): automatic add before submit --- frontend/src/utils/mass-input/mass-input.js | 113 ++++++++++++++---- frontend/src/utils/mass-input/mass-input.sass | 13 ++ 2 files changed, 103 insertions(+), 23 deletions(-) diff --git a/frontend/src/utils/mass-input/mass-input.js b/frontend/src/utils/mass-input/mass-input.js index dee124a1a..84850cc2a 100644 --- a/frontend/src/utils/mass-input/mass-input.js +++ b/frontend/src/utils/mass-input/mass-input.js @@ -1,3 +1,5 @@ +/* global global:writable */ + import { Utility } from '../../core/utility'; import { Datepicker } from '../form/datepicker'; import './mass-input.sass'; @@ -7,6 +9,11 @@ const MASS_INPUT_ADD_CELL_SELECTOR = '.massinput__cell--add'; const MASS_INPUT_SUBMIT_BUTTON_CLASS = 'massinput__submit-button'; const MASS_INPUT_INITIALIZED_CLASS = 'mass-input--initialized'; +const MASS_INPUT_ADD_CHANGE_FIELD_SELECTOR = 'select, input[type=radio]'; + +// const MASS_INPUT_SAFETY_SUBMITTED_CLASS = 'massinput--safety-submitted'; +// const MASS_INPUT_SAFETY_SUBMITTED_TIMEOUT = 1000; + @Utility({ selector: '[uw-mass-input]', }) @@ -14,11 +21,14 @@ export class MassInput { _element; _app; + _global; _massInputId; _massInputFormSubmitHandler; _massInputForm; + _changedAdd = new Array(); + constructor(element, app) { if (!element) { throw new Error('Mass Input utility cannot be setup without an element!'); @@ -27,6 +37,14 @@ export class MassInput { this._element = element; this._app = app; + if (global !== undefined) + this._global = global; + else if (window !== undefined) + this._global = window; + else + throw new Error('Cannot setup Mass Input utility without window or global'); + + if (this._element.classList.contains(MASS_INPUT_INITIALIZED_CLASS)) { return false; } @@ -47,8 +65,10 @@ export class MassInput { this._setupSubmitButton(button); }); - this._massInputForm.addEventListener('submit', this._massInputFormSubmitHandler); - this._massInputForm.addEventListener('keypress', this._keypressHandler); + this._massInputForm.addEventListener('submit', this._massInputFormSubmitHandler.bind(this)); + this._massInputForm.addEventListener('keypress', this._keypressHandler.bind(this)); + + Array.from(this._element.querySelectorAll(MASS_INPUT_ADD_CELL_SELECTOR)).forEach(this._setupChangedHandlers.bind(this)); // mark initialized this._element.classList.add(MASS_INPUT_INITIALIZED_CLASS); @@ -58,6 +78,26 @@ export class MassInput { this._reset(); } + _setupChangedHandlers(addCell) { + Array.from(addCell.querySelectorAll(MASS_INPUT_ADD_CHANGE_FIELD_SELECTOR)).forEach(inputElem => { + if (inputElem.closest('[uw-mass-input]') !== this._element) + return; + + inputElem.addEventListener('change', () => { this._changedAdd.push(addCell); }); + }); + } + + _unsafeAddCells() { + let changedAdd = this._changedAdd; + + Array.from(this._element.querySelectorAll(MASS_INPUT_ADD_CELL_SELECTOR)).forEach(addCell => addCell.querySelectorAll('input:not([type=checkbox]):not([type=radio])').forEach(inputElem => { + if (inputElem.closest('[uw-mass-input]') === this._element && inputElem.value !== '') + changedAdd.push(addCell); + })); + + return changedAdd; + } + _makeSubmitHandler() { const method = this._massInputForm.getAttribute('method') || 'POST'; const url = this._massInputForm.getAttribute('action') || window.location.href; @@ -69,31 +109,58 @@ export class MassInput { } return (event) => { - let activeElement; + let submitButton; + let isAddCell; + + let isMassInputSubmit = (() => { + let activeElement; - // check if event occured from either a mass input add/delete button or - // from inside one of massinput's inputs (i.e. a child is focused/active) - activeElement = this._element.querySelector(':focus, :active'); + // check if event occured from either a mass input add/delete button or + // from inside one of massinput's inputs (i.e. a child is focused/active) + activeElement = this._element.querySelector(':focus, :active'); - if (!activeElement) { - return false; + if (!activeElement) { + return false; + } + + // find the according massinput cell thats hosts the element that triggered the submit + const massInputCell = activeElement.closest(MASS_INPUT_CELL_SELECTOR); + if (!massInputCell) { + return false; + } + + submitButton = massInputCell.querySelector('.' + MASS_INPUT_SUBMIT_BUTTON_CLASS); + if (!submitButton) { + return false; + } + + isAddCell = massInputCell.matches(MASS_INPUT_ADD_CELL_SELECTOR); + const submitButtonIsActive = submitButton.matches(':focus, :active'); + // if the cell is not an add cell the active element must at least be the cells submit button + if (!isAddCell && !submitButtonIsActive) { + return false; + } + + return true; + })(); + + let unsafeAddCells = this._unsafeAddCells(); + + if (unsafeAddCells.length > 0 && !isMassInputSubmit) { + let addButtons = Array.from(unsafeAddCells[0].querySelectorAll('.' + MASS_INPUT_SUBMIT_BUTTON_CLASS)).filter(addButton => addButton.closest('[uw-mass-input]') === this._element); + + if (addButtons.length > 0) { + submitButton = addButtons[0]; + isMassInputSubmit = true; + isAddCell = false; + + this._element.scrollIntoView(); + // this._element.classList.add(MASS_INPUT_SAFETY_SUBMITTED_CLASS); + // this._global.setTimeout(() => { this._element.classList.remove(MASS_INPUT_SAFETY_SUBMITTED_CLASS) }, MASS_INPUT_SAFETY_SUBMITTED_TIMEOUT) + } } - // find the according massinput cell thats hosts the element that triggered the submit - const massInputCell = activeElement.closest(MASS_INPUT_CELL_SELECTOR); - if (!massInputCell) { - return false; - } - - const submitButton = massInputCell.querySelector('.' + MASS_INPUT_SUBMIT_BUTTON_CLASS); - if (!submitButton) { - return false; - } - - const isAddCell = massInputCell.matches(MASS_INPUT_ADD_CELL_SELECTOR); - const submitButtonIsActive = submitButton.matches(':focus, :active'); - // if the cell is not an add cell the active element must at least be the cells submit button - if (!isAddCell && !submitButtonIsActive) { + if (!isMassInputSubmit) { return false; } diff --git a/frontend/src/utils/mass-input/mass-input.sass b/frontend/src/utils/mass-input/mass-input.sass index 89bdf08c9..1339a2253 100644 --- a/frontend/src/utils/mass-input/mass-input.sass +++ b/frontend/src/utils/mass-input/mass-input.sass @@ -1,3 +1,5 @@ +@use "../../app" as * + .massinput-list__wrapper, .massinput-list__cell display: grid grid: auto / auto 50px @@ -12,3 +14,14 @@ .massinput-list__cell grid-column: 1 / 3 + +/* .massinput--safety-submitted +/* animation: massinput--safety-submitted linear 1s + +/* @keyframes massinput--safety-submitted +/* 0% +/* background-color: rgba(252, 153, 0, 0) +/* 50% +/* background-color: rgba(252, 153, 0, 0.8) +/* 100% +/* background-color: rgba(252, 153, 0, 0)