2021-04-21 00:42:32 +00:00
|
|
|
import Prefs from '../prefs';
|
|
|
|
import MicroModal from 'micromodal';
|
|
|
|
|
|
|
|
export const BOOLEAN_OPTION = 'BOOLEAN_OPTION';
|
|
|
|
export const SELECT_OPTION = 'SELECT_OPTION';
|
|
|
|
|
|
|
|
export interface Option {
|
|
|
|
name: string
|
|
|
|
label: string
|
|
|
|
type: string
|
|
|
|
defaultVal: string | boolean
|
|
|
|
}
|
|
|
|
|
|
|
|
export interface BooleanOption extends Option {
|
|
|
|
type: typeof BOOLEAN_OPTION
|
|
|
|
defaultVal: boolean
|
|
|
|
}
|
|
|
|
|
|
|
|
export interface SelectOption extends Option {
|
|
|
|
type: typeof SELECT_OPTION
|
|
|
|
defaultVal: string
|
|
|
|
values: Array<{name: string, value: string}>
|
|
|
|
}
|
|
|
|
|
|
|
|
export interface OptionSection {
|
|
|
|
name: string
|
|
|
|
options: Option[]
|
|
|
|
}
|
|
|
|
|
|
|
|
export interface OptionHandler {
|
|
|
|
getOptions: () => OptionSection[]
|
|
|
|
setOption: (name: string, value: string | boolean) => void
|
|
|
|
}
|
|
|
|
|
|
|
|
export class OptionsModal {
|
|
|
|
private prefs: Prefs = new Prefs();
|
2021-11-29 00:20:25 +00:00
|
|
|
private options: Record<string, Option> = {};
|
|
|
|
private handlers: Record<string, OptionHandler> = {};
|
|
|
|
private sections: OptionSection[] = [];
|
2021-04-21 00:42:32 +00:00
|
|
|
|
|
|
|
addOptions(handler: OptionHandler) {
|
|
|
|
const sections = handler.getOptions();
|
|
|
|
for (const section of sections) {
|
|
|
|
const { options } = section;
|
|
|
|
for (const option of options) {
|
|
|
|
const { name } = option;
|
|
|
|
this.handlers[name] = handler;
|
|
|
|
this.options[name] = option;
|
|
|
|
const value = this.getOption(name);
|
|
|
|
if (value != null) {
|
|
|
|
handler.setOption(name, value);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
this.sections.push(section);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
getOption(name: string): string | boolean | undefined {
|
|
|
|
const option = this.options[name];
|
|
|
|
if (option) {
|
|
|
|
const { name, defaultVal, type } = option;
|
|
|
|
const stringVal = String(defaultVal);
|
|
|
|
const prefVal = this.prefs.readPref(name, stringVal);
|
|
|
|
switch (type) {
|
|
|
|
case BOOLEAN_OPTION:
|
|
|
|
return prefVal === 'true';
|
|
|
|
default:
|
|
|
|
return prefVal;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
setOption(name: string, value: string | boolean) {
|
|
|
|
if (name in this.options) {
|
|
|
|
const handler = this.handlers[name];
|
|
|
|
const option = this.options[name];
|
|
|
|
this.prefs.writePref(name, String(value));
|
|
|
|
switch (option.type) {
|
|
|
|
case BOOLEAN_OPTION:
|
|
|
|
handler.setOption(name, Boolean(value));
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
handler.setOption(name, String(value));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
openModal() {
|
|
|
|
const content = document.querySelector('#options-modal-content');
|
|
|
|
if (content) {
|
|
|
|
content.innerHTML = '';
|
|
|
|
for (const section of this.sections) {
|
|
|
|
const { name, options } = section;
|
|
|
|
|
|
|
|
// Section header
|
|
|
|
const header = document.createElement('h3');
|
|
|
|
header.textContent = name;
|
|
|
|
content.appendChild(header);
|
|
|
|
|
|
|
|
// Preferences
|
|
|
|
const list = document.createElement('ul');
|
|
|
|
for (const option of options) {
|
|
|
|
const { name, label, defaultVal, type } = option;
|
|
|
|
const onChange = (evt: InputEvent) => {
|
|
|
|
if (evt.target) {
|
|
|
|
const inputElement = evt.target as HTMLInputElement;
|
|
|
|
switch (type) {
|
|
|
|
case BOOLEAN_OPTION:
|
|
|
|
this.setOption(name, inputElement.checked);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
this.setOption(name, inputElement.value);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
const listItem = document.createElement('li');
|
|
|
|
|
|
|
|
let element: HTMLElement;
|
|
|
|
switch (type) {
|
|
|
|
case BOOLEAN_OPTION:
|
|
|
|
{
|
|
|
|
const inputElement = document.createElement('input');
|
|
|
|
const checked = this.prefs.readPref(name, String(defaultVal)) === 'true';
|
|
|
|
inputElement.setAttribute('type', 'checkbox');
|
|
|
|
inputElement.checked = checked;
|
|
|
|
element = inputElement;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case SELECT_OPTION:
|
|
|
|
{
|
|
|
|
const selectOption = option as SelectOption;
|
|
|
|
const selectElement = document.createElement('select');
|
|
|
|
const selected = this.prefs.readPref(name, String(defaultVal));
|
|
|
|
for (const value of selectOption.values) {
|
|
|
|
const optionElement = document.createElement('option');
|
|
|
|
optionElement.value = value.value;
|
|
|
|
optionElement.textContent = value.name;
|
|
|
|
optionElement.selected = value.value === selected;
|
|
|
|
selectElement.appendChild(optionElement);
|
|
|
|
}
|
|
|
|
element = selectElement;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
{
|
|
|
|
const inputElement = document.createElement('input');
|
|
|
|
const value = this.prefs.readPref(name, String(defaultVal));
|
|
|
|
inputElement.value = value;
|
|
|
|
element = inputElement;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
element.id = name;
|
|
|
|
element.addEventListener('change', onChange);
|
|
|
|
listItem.appendChild(element);
|
|
|
|
const labelElement = document.createElement('label');
|
|
|
|
|
|
|
|
labelElement.textContent = label;
|
|
|
|
labelElement.setAttribute('for', name);
|
|
|
|
listItem.appendChild(labelElement);
|
|
|
|
|
|
|
|
list.appendChild(listItem);
|
|
|
|
}
|
|
|
|
content.appendChild(list);
|
|
|
|
}
|
|
|
|
const reloadElement = document.createElement('i');
|
|
|
|
reloadElement.textContent = '* Reload page to take effect';
|
|
|
|
content.append(reloadElement);
|
|
|
|
} else {
|
|
|
|
console.error('Cannot find target div#options-modal-content');
|
|
|
|
}
|
|
|
|
MicroModal.show('options-modal');
|
|
|
|
}
|
|
|
|
}
|