This repository has been archived on 2024-10-24. You can view files and clone it, but cannot push or open issues or pull requests.
fradrive-old/frontend/src/utils/form/interactive-fieldset.js
2020-05-12 20:40:46 +02:00

141 lines
4.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])';
let fieldsetCounter = 0;
@Utility({
selector: '[uw-interactive-fieldset]',
})
export class InteractiveFieldset {
fieldsetIdent = (fieldsetCounter++).toString();
_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)).filter(child => child.closest('[uw-interactive-fieldset]') === this._element);
// add event listener
const observer = new MutationObserver(this._updateVisibility.bind(this));
observer.observe(this.conditionalInput, { attributes: true, attributeFilter: ['data-interactive-fieldset-hidden'] });
this.conditionalInput.addEventListener('input', this._updateVisibility.bind(this));
// mark as initialized
this._element.classList.add(INTERACTIVE_FIELDSET_INITIALIZED_CLASS);
}
start() {
// initial visibility update
this._updateVisibility();
}
destroy() {
// TODO
}
_updateVisibility() {
const active = this._matchesConditionalValue() && !this.conditionalInput.dataset.interactiveFieldsetHidden;
let hiddenBy = (this.target.dataset.interactiveFieldsetHiddenBy || '').split(',').filter(str => str.length !== 0);
if (active)
hiddenBy = hiddenBy.filter(ident => ident !== this.fieldsetIdent);
else if (hiddenBy.every(ident => ident !== this.fieldsetIdent))
hiddenBy = [ ...hiddenBy, this.fieldsetIdent ];
if (hiddenBy.length !== 0) {
this.target.dataset.interactiveFieldsetHiddenBy = hiddenBy.join(',');
this.target.classList.add('hidden');
} else {
delete this.target.dataset['interactiveFieldsetHiddenBy'];
this.target.classList.remove('hidden');
}
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() {
let matches;
if (this._isCheckbox()) {
matches = this.conditionalInput.checked === true;
} else if (this._isRadio()) {
const radios = Array.from(this.conditionalInput.querySelectorAll('input[type=radio]'));
matches = radios.some(radio => radio.checked && radio.value === this.conditionalValue);
} else {
matches = this.conditionalInput.value === this.conditionalValue;
}
if (this.negated) {
return !matches;
} else {
return matches;
}
}
_isCheckbox() {
return this.conditionalInput.getAttribute('type') === 'checkbox';
}
_isRadio() {
return !!this.conditionalInput.querySelector('input[type=radio]');
}
}