312 lines
9.3 KiB
JavaScript
312 lines
9.3 KiB
JavaScript
(function() {
|
|
'use strict';
|
|
|
|
window.utils = window.utils || {};
|
|
|
|
var formUtilities = [];
|
|
|
|
var JS_INITIALIZED = 'js-form-initialized';
|
|
var AUTOSUBMIT_BUTTON_SELECTOR = '[type="submit"][data-autosubmit]';
|
|
var AJAX_SUBMIT_FLAG = 'ajaxSubmit';
|
|
|
|
var FORM_GROUP_CLASS = 'form-group';
|
|
var FORM_GROUP_WITH_ERRORS_CLASS = 'form-group--has-error';
|
|
|
|
window.utils.form = function(form, options) {
|
|
options = options || {};
|
|
|
|
// dont initialize form if it is in a modal and is not forced
|
|
if (form.closest('.modal') && !options.force) {
|
|
return false;
|
|
}
|
|
|
|
// dont initialize form if already initialized and should not be force-initialized
|
|
if (form.classList.contains(JS_INITIALIZED) && !options.force) {
|
|
return false;
|
|
}
|
|
|
|
var utilInstances = [];
|
|
|
|
// reactive buttons
|
|
utilInstances.push(window.utils.setup('reactiveButton', form));
|
|
|
|
// conditonal fieldsets
|
|
var fieldSets = Array.from(form.querySelectorAll('fieldset[data-conditional-id][data-conditional-value]'));
|
|
utilInstances.push(window.utils.setup('interactiveFieldset', form, { fieldSets }));
|
|
|
|
// hide autoSubmit submit button
|
|
utilInstances.push(window.utils.setup('autoSubmit', form, options));
|
|
|
|
// async form
|
|
if (AJAX_SUBMIT_FLAG in form.dataset) {
|
|
utilInstances.push(window.utils.setup('asyncForm', form, options));
|
|
}
|
|
|
|
// inputs
|
|
utilInstances.push(window.utils.setup('inputs', form, options));
|
|
|
|
// form group errors
|
|
var formGroups = Array.from(form.querySelectorAll('.' + FORM_GROUP_CLASS));
|
|
formGroups.forEach(function(formGroup) {
|
|
utilInstances.push(window.utils.setup('errorRemover', formGroup, options));
|
|
});
|
|
|
|
form.classList.add(JS_INITIALIZED);
|
|
|
|
function destroyUtils() {
|
|
utilInstances.filter(function(utilInstance) {
|
|
return !!utilInstance;
|
|
}).forEach(function(utilInstance) {
|
|
utilInstance.destroy();
|
|
});
|
|
}
|
|
|
|
return {
|
|
scope: form,
|
|
destroy: destroyUtils,
|
|
};
|
|
};
|
|
|
|
/**
|
|
*
|
|
* Reactive Submit Button Utility
|
|
* disables a forms LAST sumit button as long as the required inputs are invalid
|
|
* (only checks if the value of the inputs are not empty)
|
|
*
|
|
* Attribute: [none]
|
|
* (automatically setup on all form tags)
|
|
*
|
|
* Params:
|
|
* data-formnorequired: string
|
|
* If present the submit button will never get disabled
|
|
*
|
|
* Example usage:
|
|
* <form uw-reactive-submit-button>
|
|
* <input type="text" required>
|
|
* <button type="submit">
|
|
* </form>
|
|
*/
|
|
|
|
var REACTIVE_SUBMIT_BUTTON_UTIL_NAME = 'reactiveSubmitButton';
|
|
var REACTIVE_SUBMIT_BUTTON_UTIL_SELECTOR = 'form';
|
|
|
|
var REACTIVE_SUBMIT_BUTTON_INITIALIZED_CLASS = 'reactive-submit-button--initialized';
|
|
|
|
var reactiveButtonUtil = function(element) {
|
|
var requiredInputs;
|
|
var submitButton;
|
|
|
|
function init() {
|
|
if (!element) {
|
|
throw new Error('Reactive Submit Button utility cannot be setup without an element!');
|
|
}
|
|
|
|
if (element.classList.contains(REACTIVE_SUBMIT_BUTTON_INITIALIZED_CLASS)) {
|
|
throw new Error('Reactive Submit Button utility already initialized!');
|
|
}
|
|
|
|
// abort if form has param data-formnorequired
|
|
if (element.dataset.formnorequired !== undefined) {
|
|
throw new Error('Form has formnorequired data attribute. Will skip setup of reactive submit button.');
|
|
}
|
|
|
|
requiredInputs = Array.from(element.querySelectorAll('[required]'));
|
|
if (!requiredInputs) {
|
|
// abort if form has no required inputs
|
|
throw new Error('Submit button has formnorequired data attribute. Will skip setup of reactive submit button.');
|
|
}
|
|
|
|
var submitButtons = Array.from(element.querySelectorAll('[type="submit"]'));
|
|
if (!submitButtons) {
|
|
throw new Error('Reactive Submit Button utility couldn\'t find any submit buttons!');
|
|
}
|
|
submitButton = submitButtons.reverse()[0];
|
|
// abort if form has param data-formnorequired
|
|
if (submitButton.dataset.formnorequired !== undefined) {
|
|
return false;
|
|
}
|
|
|
|
setupInputs();
|
|
updateButtonState();
|
|
|
|
element.classList.add(REACTIVE_SUBMIT_BUTTON_INITIALIZED_CLASS);
|
|
|
|
return {
|
|
name: REACTIVE_SUBMIT_BUTTON_UTIL_NAME,
|
|
element: element,
|
|
destroy: function() {},
|
|
};
|
|
}
|
|
|
|
function setupInputs() {
|
|
requiredInputs.forEach(function(el) {
|
|
var checkbox = el.getAttribute('type') === 'checkbox';
|
|
var eventType = checkbox ? 'change' : 'input';
|
|
el.addEventListener(eventType, function() {
|
|
updateButtonState();
|
|
});
|
|
});
|
|
}
|
|
|
|
function updateButtonState() {
|
|
if (inputsValid()) {
|
|
submitButton.removeAttribute('disabled');
|
|
} else {
|
|
console.log('setting disabled', { submitButton });
|
|
submitButton.setAttribute('disabled', 'true');
|
|
}
|
|
}
|
|
|
|
function inputsValid() {
|
|
var done = true;
|
|
requiredInputs.forEach(function(inp) {
|
|
var len = inp.value.trim().length;
|
|
if (done && len === 0) {
|
|
done = false;
|
|
}
|
|
});
|
|
return done;
|
|
}
|
|
|
|
return init();
|
|
};
|
|
|
|
formUtilities.push({
|
|
name: REACTIVE_SUBMIT_BUTTON_UTIL_NAME,
|
|
selector: REACTIVE_SUBMIT_BUTTON_UTIL_SELECTOR,
|
|
setup: reactiveButtonUtil,
|
|
});
|
|
|
|
/**
|
|
*
|
|
* Interactive Fieldset Utility
|
|
* shows/hides inputs based on value of particular input
|
|
*
|
|
* Attribute: uw-interactive-fieldset
|
|
*
|
|
* Params:
|
|
* data-conditional-input: string
|
|
* Selector for the input that this fieldset watches for changes
|
|
* data-conditional-value: string
|
|
* The value the conditional input needs to be set to for this fieldset to be shown
|
|
*
|
|
* Example usage:
|
|
* <input id="input-0" type="text">
|
|
* <fieldset uw-interactive-fieldset data-conditional-input="#input-0" data-conditional-value="yes">...</fieldset>
|
|
* <fieldset uw-interactive-fieldset data-conditional-input="#input-0" data-conditional-value="no">...</fieldset>
|
|
* ## example with <select>
|
|
* <select id="select-0">
|
|
* <option value="0">Zero
|
|
* <option value="1">One
|
|
* <fieldset uw-interactive-fieldset data-conditional-input="#select-0" data-conditional-value="0">...</fieldset>
|
|
* <fieldset uw-interactive-fieldset data-conditional-input="#select-0" data-conditional-value="1">...</fieldset>
|
|
*/
|
|
|
|
var INTERACTIVE_FIELDSET_UTIL_NAME = 'interactiveFieldset';
|
|
var INTERACTIVE_FIELDSET_UTIL_SELECTOR = '[uw-interactive-fieldset]';
|
|
|
|
var INTERACTIVE_FIELDSET_INITIALIZED_CLASS = 'interactive-fieldset--initialized';
|
|
|
|
var interactiveFieldsetUtil = function(element) {
|
|
var conditionalInput;
|
|
var conditionalValue;
|
|
|
|
function init() {
|
|
if (!element) {
|
|
throw new Error('Interactive Fieldset utility cannot be setup without an element!');
|
|
}
|
|
|
|
if (element.classList.contains(INTERACTIVE_FIELDSET_INITIALIZED_CLASS)) {
|
|
throw new Error('Interactive Fieldset utility already initialized!');
|
|
}
|
|
|
|
// param conditionalInput
|
|
if (!element.dataset.conditionalInput) {
|
|
throw new Error('Interactive Fieldset needs a selector for a conditional input!');
|
|
}
|
|
conditionalInput = document.querySelector(element.dataset.conditionalInput);
|
|
if (!conditionalInput) {
|
|
// abort if form has no required inputs
|
|
throw new Error('Couldn\'t find the conditional input. Aborting setup for interactive fieldset.');
|
|
}
|
|
|
|
// param conditionalValue
|
|
if (!element.dataset.conditionalValue) {
|
|
throw new Error('Interactive Fieldset needs a conditional value!');
|
|
}
|
|
conditionalValue = element.dataset.conditionalValue;
|
|
|
|
// add event listener
|
|
conditionalInput.addEventListener('input', updateVisibility);
|
|
|
|
// initial visibility update
|
|
updateVisibility();
|
|
|
|
// mark as initialized
|
|
element.classList.add(INTERACTIVE_FIELDSET_INITIALIZED_CLASS);
|
|
|
|
return {
|
|
name: INTERACTIVE_FIELDSET_UTIL_NAME,
|
|
element: element,
|
|
destroy: function() {},
|
|
};
|
|
}
|
|
|
|
function updateVisibility() {
|
|
element.classList.toggle('hidden', conditionalInput.value !== conditionalValue);
|
|
}
|
|
|
|
return init();
|
|
};
|
|
|
|
formUtilities.push({
|
|
name: INTERACTIVE_FIELDSET_UTIL_NAME,
|
|
selector: INTERACTIVE_FIELDSET_UTIL_SELECTOR,
|
|
setup: interactiveFieldsetUtil,
|
|
});
|
|
|
|
|
|
window.utils.autoSubmit = function(form, options) {
|
|
var button = form.querySelector(AUTOSUBMIT_BUTTON_SELECTOR);
|
|
if (button) {
|
|
button.classList.add('hidden');
|
|
}
|
|
|
|
return {
|
|
scope: form,
|
|
destroy: function() {},
|
|
};
|
|
};
|
|
|
|
// listens for focus events and removes any errors on an input
|
|
window.utils.errorRemover = function(formGroup, options) {
|
|
|
|
var inputElement = formGroup.querySelector('input:not([type="hidden"]), textarea, select');
|
|
if (!inputElement) {
|
|
return false;
|
|
}
|
|
|
|
inputElement.addEventListener('focus', focusListener);
|
|
|
|
function focusListener() {
|
|
var hasError = formGroup.classList.contains(FORM_GROUP_WITH_ERRORS_CLASS);
|
|
if (hasError) {
|
|
formGroup.classList.remove(FORM_GROUP_WITH_ERRORS_CLASS);
|
|
}
|
|
}
|
|
|
|
return {
|
|
scope: formGroup,
|
|
destroy: function() {
|
|
inputElement.removeEventListener('focus', focusListener);
|
|
},
|
|
};
|
|
};
|
|
|
|
// register the collected form utilities
|
|
if (UtilRegistry) {
|
|
formUtilities.forEach(UtilRegistry.register);
|
|
}
|
|
|
|
})();
|