ws4kp-linhanced/server/scripts/modules/utils/setting.mjs

268 lines
7 KiB
JavaScript
Raw Normal View History

2024-04-19 21:05:52 -05:00
import { parseQueryString } from '../share.mjs';
const SETTINGS_KEY = 'Settings';
2025-04-08 10:40:36 -05:00
const DEFAULTS = {
shortName: undefined,
name: undefined,
type: 'checkbox',
defaultValue: undefined,
changeAction: () => { },
sticky: true,
values: [],
visible: true,
};
class Setting {
2025-04-08 10:40:36 -05:00
constructor(shortName, _options) {
if (shortName === undefined) {
throw new Error('No name provided for setting');
}
// merge options with defaults
const options = { ...DEFAULTS, ...(_options ?? {}) };
// store values and combine with defaults
this.shortName = shortName;
2025-04-08 10:40:36 -05:00
this.name = options.name ?? shortName;
this.defaultValue = options.defaultValue;
this.myValue = this.defaultValue;
this.type = options?.type;
this.sticky = options.sticky;
this.values = options.values;
this.visible = options.visible;
this.changeAction = options.changeAction;
2024-04-19 21:05:52 -05:00
// get value from url
2025-04-08 10:40:36 -05:00
const urlValue = parseQueryString()?.[`settings-${shortName}-${this.type}`];
2024-04-19 21:05:52 -05:00
let urlState;
2025-04-08 10:40:36 -05:00
if (this.type === 'checkbox' && urlValue !== undefined) {
2024-04-19 21:05:52 -05:00
urlState = urlValue === 'true';
}
if (this.type === 'boolean' && urlValue !== undefined) {
urlState = urlValue === 'true';
}
2025-04-08 10:40:36 -05:00
if (this.type === 'select' && urlValue !== undefined) {
2024-07-29 23:12:47 -05:00
urlState = parseFloat(urlValue);
}
2025-04-08 10:40:36 -05:00
if (this.type === 'select' && urlValue !== undefined && Number.isNaN(urlState)) {
2025-02-23 23:29:39 -06:00
// couldn't parse as a float, store as a string
urlState = urlValue;
}
2025-06-28 00:22:47 -05:00
if (this.type === 'string' && urlValue !== undefined) {
urlState = urlValue;
}
// get existing value if present
2024-04-19 21:05:52 -05:00
const storedValue = urlState ?? this.getFromLocalStorage();
2025-04-08 10:40:36 -05:00
if ((this.sticky || urlValue !== undefined) && storedValue !== null) {
this.myValue = storedValue;
}
// call the change function on startup
2025-04-08 10:40:36 -05:00
switch (this.type) {
2024-10-21 19:21:05 -05:00
case 'select':
this.selectChange({ target: { value: this.myValue } });
break;
2025-06-28 00:22:47 -05:00
case 'string':
this.stringChange({ target: { value: this.myValue } });
break;
2024-10-21 19:21:05 -05:00
case 'checkbox':
default:
this.checkboxChange({ target: { checked: this.myValue } });
2024-07-29 23:12:47 -05:00
}
}
generateSelect() {
// create a radio button set in the selected displays area
const label = document.createElement('label');
label.for = `settings-${this.shortName}-select`;
label.id = `settings-${this.shortName}-label`;
const span = document.createElement('span');
span.innerHTML = `${this.name} `;
label.append(span);
const select = document.createElement('select');
select.id = `settings-${this.shortName}-select`;
select.name = `settings-${this.shortName}-select`;
select.addEventListener('change', (e) => this.selectChange(e));
this.values.forEach(([value, text]) => {
const option = document.createElement('option');
2025-02-23 23:29:39 -06:00
if (typeof value === 'number') {
option.value = value.toFixed(2);
} else {
option.value = value;
}
2024-07-29 23:12:47 -05:00
option.innerHTML = text;
select.append(option);
});
label.append(select);
this.element = label;
// set the initial value
this.selectHighlight(this.myValue);
return label;
}
generateCheckbox() {
// create a checkbox in the selected displays area
const label = document.createElement('label');
label.for = `settings-${this.shortName}-checkbox`;
label.id = `settings-${this.shortName}-label`;
const checkbox = document.createElement('input');
checkbox.type = 'checkbox';
checkbox.value = true;
checkbox.id = `settings-${this.shortName}-checkbox`;
checkbox.name = `settings-${this.shortName}-checkbox`;
checkbox.checked = this.myValue;
checkbox.addEventListener('change', (e) => this.checkboxChange(e));
const span = document.createElement('span');
span.innerHTML = this.name;
label.append(checkbox, span);
2024-07-29 23:12:47 -05:00
this.element = label;
return label;
}
2025-06-28 00:22:47 -05:00
generateString() {
// create a string input and accompanying set button
const label = document.createElement('label');
label.for = `settings-${this.shortName}-string`;
label.id = `settings-${this.shortName}-label`;
// text input box
const textInput = document.createElement('input');
textInput.type = 'text';
textInput.value = this.myValue;
textInput.id = `settings-${this.shortName}-string`;
textInput.name = `settings-${this.shortName}-string`;
// set button
const setButton = document.createElement('input');
setButton.type = 'button';
setButton.value = 'Set';
setButton.id = `settings-${this.shortName}-button`;
setButton.name = `settings-${this.shortName}-button`;
setButton.addEventListener('click', () => {
this.stringChange({ target: { value: textInput.value } });
});
// assemble
label.append(textInput, setButton);
this.element = label;
return label;
}
checkboxChange(e) {
// update the state
this.myValue = e.target.checked;
this.storeToLocalStorage(this.myValue);
// call change action
this.changeAction(this.myValue);
}
2024-07-29 23:12:47 -05:00
selectChange(e) {
// update the value
this.myValue = parseFloat(e.target.value);
2025-02-23 23:29:39 -06:00
if (Number.isNaN(this.myValue)) {
// was a string, store as such
this.myValue = e.target.value;
}
2024-07-29 23:12:47 -05:00
this.storeToLocalStorage(this.myValue);
// call the change action
this.changeAction(this.myValue);
}
2025-06-28 00:22:47 -05:00
stringChange(e) {
// update the value
this.myValue = e.target.value;
this.storeToLocalStorage(this.myValue);
// call the change action
this.changeAction(this.myValue);
}
storeToLocalStorage(value) {
if (!this.sticky) return;
const allSettingsString = localStorage?.getItem(SETTINGS_KEY) ?? '{}';
const allSettings = JSON.parse(allSettingsString);
allSettings[this.shortName] = value;
localStorage?.setItem(SETTINGS_KEY, JSON.stringify(allSettings));
}
getFromLocalStorage() {
const allSettings = localStorage?.getItem(SETTINGS_KEY);
try {
if (allSettings) {
const storedValue = JSON.parse(allSettings)?.[this.shortName];
if (storedValue !== undefined) {
switch (this.type) {
2024-10-21 19:21:05 -05:00
case 'boolean':
2025-04-08 10:40:36 -05:00
case 'checkbox':
2024-10-21 19:21:05 -05:00
case 'select':
2025-06-28 00:22:47 -05:00
case 'string':
2024-10-21 19:21:05 -05:00
return storedValue;
default:
return null;
}
}
}
} catch {
return null;
}
return null;
}
get value() {
return this.myValue;
}
set value(newValue) {
// update the state
this.myValue = newValue;
2024-07-29 23:12:47 -05:00
switch (this.type) {
2024-10-21 19:21:05 -05:00
case 'select':
this.selectHighlight(newValue);
break;
2025-03-22 15:10:06 +01:00
case 'boolean':
break;
2024-10-21 19:21:05 -05:00
case 'checkbox':
default:
2025-05-29 17:03:50 -05:00
this.element.querySelector('input').checked = newValue;
2024-07-29 23:12:47 -05:00
}
this.storeToLocalStorage(this.myValue);
// call change action
this.changeAction(this.myValue);
}
2024-07-29 23:12:47 -05:00
selectHighlight(newValue) {
// set the dropdown to the provided value
2025-04-06 21:31:42 -05:00
this?.element?.querySelectorAll('option')?.forEach?.((elem) => {
2025-02-23 23:29:39 -06:00
elem.selected = (newValue?.toFixed?.(2) === elem.value) || (newValue === elem.value);
2024-07-29 23:12:47 -05:00
});
}
generate() {
2025-04-08 10:40:36 -05:00
// don't generate a control for not visible items
if (!this.visible) return '';
// call the appropriate control generator
2024-07-29 23:12:47 -05:00
switch (this.type) {
2024-10-21 19:21:05 -05:00
case 'select':
return this.generateSelect();
2025-06-28 00:22:47 -05:00
case 'string':
return this.generateString();
2024-10-21 19:21:05 -05:00
case 'checkbox':
default:
return this.generateCheckbox();
2024-07-29 23:12:47 -05:00
}
}
}
export default Setting;