fradrive/frontend/src/utils/form/interactive-fieldset.js
2019-09-04 11:36:16 +02:00

116 lines
3.4 KiB
JavaScript

import { Utility } from '../../core/utility';
const INTERACTIVE_FIELDSET_UTIL_TARGET_SELECTOR = '.interactive-fieldset__target';
const INTERACTIVE_FIELDSET_INITIALIZED_CLASS = 'interactive-fieldset--initialized';
const INTERACTIVE_FIELDSET_CHILD_SELECTOR = 'input:not([disabled]), select:not([disabled]), textarea:not([disabled]), button:not([disabled])';
@Utility({
selector: '[uw-interactive-fieldset]',
})
export class InteractiveFieldset {
_element;
conditionalInput;
conditionalValue;
target;
childInputs;
negated;
constructor(element) {
if (!element) {
throw new Error('Interactive Fieldset utility cannot be setup without an element!');
}
this._element = element;
if (this._element.classList.contains(INTERACTIVE_FIELDSET_INITIALIZED_CLASS)) {
return false;
}
if (this._element.querySelector('[uw-interactive-fieldset]')) {
return false;
}
// param conditionalInput
if (!this._element.dataset.conditionalInput) {
throw new Error('Interactive Fieldset needs a selector for a conditional input!');
}
this.conditionalInput = document.querySelector('#' + this._element.dataset.conditionalInput);
if (!this.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 (!('conditionalValue' in this._element.dataset) && !this._isCheckbox()) {
throw new Error('Interactive Fieldset needs a conditional value!');
}
this.conditionalValue = this._element.dataset.conditionalValue;
this.negated = 'conditionalNegated' in this._element.dataset;
this.target = this._element.closest(INTERACTIVE_FIELDSET_UTIL_TARGET_SELECTOR);
if (!this.target || this._element.matches(INTERACTIVE_FIELDSET_UTIL_TARGET_SELECTOR)) {
this.target = this._element;
}
this.childInputs = Array.from(this._element.querySelectorAll(INTERACTIVE_FIELDSET_CHILD_SELECTOR));
// add event listener
const observer = new MutationObserver(() => this._updateVisibility());
observer.observe(this.conditionalInput, { attributes: true, attributeFilter: ['data-interactive-fieldset-hidden'] });
this.conditionalInput.addEventListener('input', () => this._updateVisibility());
// initial visibility update
this._updateVisibility();
// mark as initialized
this._element.classList.add(INTERACTIVE_FIELDSET_INITIALIZED_CLASS);
}
destroy() {
// TODO
}
_updateVisibility() {
const active = this._matchesConditionalValue() && !this.conditionalInput.dataset.interactiveFieldsetHidden;
this.target.classList.toggle('hidden', !active);
this.childInputs.forEach((el) => this._updateChildVisibility(el, active));
}
_updateChildVisibility(el, active) {
el.disabled = !active;
if (active) {
delete el.dataset.interactiveFieldsetHidden;
} else {
el.dataset.interactiveFieldsetHidden = true;
}
}
_matchesConditionalValue() {
var matches;
if (this._isCheckbox()) {
matches = this.conditionalInput.checked === true;
} else {
matches = this.conditionalInput.value === this.conditionalValue;
}
if (this.negated) {
return !matches;
} else {
return matches;
}
}
_isCheckbox() {
return this.conditionalInput.getAttribute('type') === 'checkbox';
}
}