101 lines
2.8 KiB
TypeScript
101 lines
2.8 KiB
TypeScript
|
import {
|
||
|
isSelectElement,
|
||
|
isCheckboxOrRadioInputElement,
|
||
|
show,
|
||
|
hide,
|
||
|
enable,
|
||
|
disable
|
||
|
} from '@utils';
|
||
|
import { z } from 'zod';
|
||
|
|
||
|
import { ApplicationController } from './application_controller';
|
||
|
|
||
|
export class ChampDropdownController extends ApplicationController {
|
||
|
connect() {
|
||
|
this.on('change', (event) => this.onChange(event));
|
||
|
}
|
||
|
|
||
|
private onChange(event: Event) {
|
||
|
const target = event.target as HTMLInputElement;
|
||
|
if (!target.disabled) {
|
||
|
if (isSelectElement(target) || isCheckboxOrRadioInputElement(target)) {
|
||
|
this.toggleOtherInput(target);
|
||
|
this.toggleLinkedSelect(target);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private toggleOtherInput(target: HTMLSelectElement | HTMLInputElement) {
|
||
|
const parent = target.closest('.editable-champ-drop_down_list');
|
||
|
const inputGroup = parent?.querySelector<HTMLElement>('.drop_down_other');
|
||
|
if (inputGroup) {
|
||
|
const input = inputGroup.querySelector('input');
|
||
|
if (input) {
|
||
|
if (target.value == '__other__') {
|
||
|
show(inputGroup);
|
||
|
input.disabled = false;
|
||
|
} else {
|
||
|
hide(inputGroup);
|
||
|
input.disabled = true;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private toggleLinkedSelect(target: HTMLSelectElement | HTMLInputElement) {
|
||
|
const secondaryOptions = target.dataset.secondaryOptions;
|
||
|
if (isSelectElement(target) && secondaryOptions) {
|
||
|
const parent = target.closest('.editable-champ-linked_drop_down_list');
|
||
|
const secondary = parent?.querySelector<HTMLSelectElement>(
|
||
|
'select[data-secondary]'
|
||
|
);
|
||
|
if (secondary) {
|
||
|
const options = parseOptions(secondaryOptions);
|
||
|
this.setSecondaryOptions(secondary, options[target.value]);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private setSecondaryOptions(
|
||
|
secondarySelectElement: HTMLSelectElement,
|
||
|
options: string[]
|
||
|
) {
|
||
|
const wrapper = secondarySelectElement.closest('.secondary');
|
||
|
const hidden = wrapper?.nextElementSibling as HTMLInputElement | null;
|
||
|
|
||
|
secondarySelectElement.innerHTML = '';
|
||
|
|
||
|
if (options.length) {
|
||
|
disable(hidden);
|
||
|
|
||
|
if (secondarySelectElement.required) {
|
||
|
secondarySelectElement.appendChild(makeOption(''));
|
||
|
}
|
||
|
for (const option of options) {
|
||
|
secondarySelectElement.appendChild(makeOption(option));
|
||
|
}
|
||
|
|
||
|
secondarySelectElement.selectedIndex = 0;
|
||
|
enable(secondarySelectElement);
|
||
|
show(wrapper);
|
||
|
} else {
|
||
|
hide(wrapper);
|
||
|
disable(secondarySelectElement);
|
||
|
enable(hidden);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
const SecondaryOptions = z.record(z.string().array());
|
||
|
|
||
|
function parseOptions(options: string) {
|
||
|
return SecondaryOptions.parse(JSON.parse(options));
|
||
|
}
|
||
|
|
||
|
function makeOption(option: string) {
|
||
|
const element = document.createElement('option');
|
||
|
element.textContent = option;
|
||
|
element.value = option;
|
||
|
return element;
|
||
|
}
|