demarches-normaliennes/app/javascript/controllers/champ_dropdown_controller.ts
2023-01-31 16:39:00 +01:00

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;
input.focus();
} 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;
}