feat(async-table): no submit on locked inputs

This commit is contained in:
Sarah Vaupel 2019-11-19 17:43:41 +01:00 committed by Gregor Kleen
parent eeabdb1b2b
commit 22b3780efd
2 changed files with 70 additions and 31 deletions

View File

@ -6,6 +6,8 @@ import * as debounce from 'lodash.debounce';
import './async-table-filter.sass';
import './async-table.sass';
const ATTR_SUBMIT_LOCKED = 'submit-locked';
const INPUT_DEBOUNCE = 600;
const HEADER_HEIGHT = 80;
@ -153,21 +155,18 @@ export class AsyncTable {
}
_gatherTableFilterInputs(tableFilterForm) {
Array.from(tableFilterForm.querySelectorAll('input[type="search"]')).forEach((input) => {
this._tableFilterInputs.search.push(input);
Array.from(tableFilterForm.querySelectorAll('input')).forEach((input) => {
const inputType = input.getAttribute('type');
if (inputType === 'search') {
this._tableFilterInputs.search.push(input);
} else if (['text','date','time','datetime-local'].includes(inputType)) {
this._tableFilterInputs.input.push(input);
} else {
this._tableFilterInputs.change.push(input);
}
});
Array.from(tableFilterForm.querySelectorAll('input[type="text"]')).forEach((input) => {
this._tableFilterInputs.input.push(input);
});
Array.from(tableFilterForm.querySelectorAll('input:not([type="text"]):not([type="search"])')).forEach((input) => {
this._tableFilterInputs.change.push(input);
});
Array.from(tableFilterForm.querySelectorAll('select')).forEach((input) => {
this._tableFilterInputs.select.push(input);
});
Array.from(tableFilterForm.querySelectorAll('select')).forEach((input) => this._tableFilterInputs.select.push(input));
}
_addTableFilterEventListeners(tableFilterForm) {
@ -186,7 +185,8 @@ export class AsyncTable {
this._tableFilterInputs.input.forEach((input) => {
const debouncedInput = debounce(() => {
if (input.value.length === 0 || input.value.length > 2) {
const submitLocked = input.getAttribute(ATTR_SUBMIT_LOCKED);
if ((submitLocked === 'false' || submitLocked === null) && (input.value.length === 0 || input.value.length > 2)) {
this._updateFromTableFilter(tableFilterForm);
}
}, INPUT_DEBOUNCE);

View File

@ -6,6 +6,10 @@ import moment from 'moment';
const KEYCODE_ESCAPE = 27;
const Z_INDEX_MODAL = 9999;
// should be the same as ATTR_SUBMIT_LOCKED in async-table util
// TODO move to global config
const ATTR_DATEPICKER_OPEN = 'submit-locked';
// INTERNAL (Uni2work specific) formats for formatting dates and/or times
const FORM_DATE_FORMAT = {
'date': moment.HTML5_FMT.DATE,
@ -26,21 +30,10 @@ const FORM_DATE_FORMAT_MOMENT = {
'datetime-local': `${FORM_DATE_FORMAT_DATE_MOMENT} ${FORM_DATE_FORMAT_TIME_MOMENT}`,
};
/**
* Takes a string representation of a date, an input ('previous') format and a desired output format and returns a reformatted date string.
* If the date string is not valid (i.e. cannot be parsed with the given input format string), returns the original date string;
* @param {*} dateStr string representation of a date (needs to be in format formatIn)
* @param {*} formatIn input format string
* @param {*} formatOut format string of the desired output date string
*/
function reformatDateString(dateStr, formatIn, formatOut) {
const parsedMomentDate = moment(dateStr, [formatIn, formatOut]);
return parsedMomentDate.isValid() ? parsedMomentDate.format(formatOut) : dateStr;
}
const DATEPICKER_UTIL_SELECTOR = 'input[type="date"], input[type="time"], input[type="datetime-local"]';
const DATEPICKER_INITIALIZED_CLASS = 'datepicker--initialized';
const DATEPICKER_OPEN_CLASS = 'calendar-open';
const DATEPICKER_CONFIG = {
'global': {
@ -163,6 +156,21 @@ export class Datepicker {
// mark the form input element as initialized
this._element.classList.add(DATEPICKER_INITIALIZED_CLASS);
// create a mutation observer that observes the datepicker instance class and sets
// the datepicker-open DOM attribute of the input element if the datepicker has been opened
const datepickerInstanceObserver = new MutationObserver((mutations) => {
mutations.forEach(mutation => {
if (!mutation.oldValue.includes(DATEPICKER_OPEN_CLASS) && this.datepickerInstance.dt.getAttribute('class').includes(DATEPICKER_OPEN_CLASS)) {
this._element.setAttribute(ATTR_DATEPICKER_OPEN, true);
}
});
});
datepickerInstanceObserver.observe(this.datepickerInstance.dt, {
attributes: true,
attributeFilter: ['class'],
attributeOldValue: true,
});
const setDatepickerDate = () => {
// try to parse the current input element value with fancy and internal format string
const parsedMomentDate = moment(this._element.value, FORM_DATE_FORMAT_MOMENT[this.elementType]);
@ -188,7 +196,7 @@ export class Datepicker {
const focussedIsNotElement = event.relatedTarget !== this._element;
const focussedIsInDocument = window.document.contains(event.relatedTarget);
if (hasFocus && focussedIsNotTimepicker && focussedIsNotElement && focussedIsInDocument)
this.datepickerInstance.close();
this.closeDatepickerInstance();
});
// close the instance on click on any element outside of the datepicker (except the input element itself)
@ -198,13 +206,13 @@ export class Datepicker {
const targetIsInDocument = window.document.contains(event.target);
const targetIsNotElement = event.target !== this._element;
if (targetIsOutside && targetIsInDocument && targetIsNotElement)
this.datepickerInstance.close();
this.closeDatepickerInstance();
});
// close the instance on escape keydown events
this._element.addEventListener('keydown', event => {
if (event.keyCode === KEYCODE_ESCAPE) {
this.datepickerInstance.close();
this.closeDatepickerInstance();
}
});
@ -216,6 +224,24 @@ export class Datepicker {
this.datepickerInstance.remove();
}
// DATEPICKER INSTANCE CONTROL
/**
* Closes the datepicker instance, releasing the lock on the input element.
*/
closeDatepickerInstance() {
if (!this._element.datepicker-open) {
throw new Error('Cannot close already closed datepicker instance!');
}
this._element.setAttribute(ATTR_DATEPICKER_OPEN, false);
this.datepickerInstance.close();
}
// FORMAT METHODS
/**
* Formats the value of this input element from datepicker format (i.e. DATEPICKER_CONFIG.dateFormat + " " + datetime.defaults.timeFormat) to Uni2work internal date format (i.e. FORM_DATE_FORMAT) required for form submission
* @param {*} toFancy optional target format switch (boolean value; default is false). If set to a truthy value, formats the element value to fancy instead of internal date format.
@ -226,8 +252,6 @@ export class Datepicker {
}
}
/**
* Returns a datestring in internal format from the current state of the input element value.
* @param {*} toFancy Format date from internal to fancy or vice versa. When omitted, toFancy is falsy and results in fancy -> internal
@ -265,3 +289,18 @@ export class Datepicker {
return formData;
}
}
// HELPER FUNCTIONS
/**
* Takes a string representation of a date, an input ('previous') format and a desired output format and returns a reformatted date string.
* If the date string is not valid (i.e. cannot be parsed with the given input format string), returns the original date string;
* @param {*} dateStr string representation of a date (needs to be in format formatIn)
* @param {*} formatIn input format string
* @param {*} formatOut format string of the desired output date string
*/
function reformatDateString(dateStr, formatIn, formatOut) {
const parsedMomentDate = moment(dateStr, [formatIn, formatOut]);
return parsedMomentDate.isValid() ? parsedMomentDate.format(formatOut) : dateStr;
}