141 lines
4.4 KiB
JavaScript
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]');
|
|
}
|
|
}
|