Improve settings page
This commit is contained in:
parent
09e672442b
commit
cd9aff0362
4 changed files with 89 additions and 26 deletions
44
src/page/collapsible-panel-animator.ts
Normal file
44
src/page/collapsible-panel-animator.ts
Normal file
|
|
@ -0,0 +1,44 @@
|
||||||
|
export class CollapsiblePanelAnimator {
|
||||||
|
private isOpen = false;
|
||||||
|
|
||||||
|
public onOpen: () => unknown = () => {};
|
||||||
|
public onClose: () => unknown = () => {};
|
||||||
|
|
||||||
|
public constructor(
|
||||||
|
infoButton: HTMLButtonElement,
|
||||||
|
private readonly infoPage: HTMLElement
|
||||||
|
) {
|
||||||
|
infoButton.addEventListener('click', this.toggle.bind(this));
|
||||||
|
window.addEventListener('click', (event) => {
|
||||||
|
if ([infoButton, this.infoPage].includes(event.target as HTMLElement)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.infoPage.contains(event.target as Node)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.close();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public open() {
|
||||||
|
this.isOpen = true;
|
||||||
|
this.infoPage.classList.remove('hidden');
|
||||||
|
this.onOpen();
|
||||||
|
}
|
||||||
|
|
||||||
|
public close() {
|
||||||
|
this.isOpen = false;
|
||||||
|
this.infoPage.classList.add('hidden');
|
||||||
|
this.onClose();
|
||||||
|
}
|
||||||
|
|
||||||
|
public toggle() {
|
||||||
|
if (this.isOpen) {
|
||||||
|
this.close();
|
||||||
|
} else {
|
||||||
|
this.open();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,10 +0,0 @@
|
||||||
export class InfoPageHandler {
|
|
||||||
public constructor(
|
|
||||||
private readonly infoButton: HTMLButtonElement,
|
|
||||||
private readonly infoPage: HTMLElement
|
|
||||||
) {
|
|
||||||
infoButton.addEventListener('click', () => {
|
|
||||||
infoPage.classList.toggle('hidden');
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,9 +1,8 @@
|
||||||
export const persist = <T extends Record<string, number>>(wrapee: T): T => {
|
export const persist = <T extends Record<string, number>>(wrapee: T): T => {
|
||||||
const initialState = { ...wrapee };
|
|
||||||
const keys = Object.keys(wrapee);
|
const keys = Object.keys(wrapee);
|
||||||
|
|
||||||
const keysToShortKeys = Object.fromEntries(
|
const keysToShortKeys = Object.fromEntries(
|
||||||
keys.map((key, i) => [key, String.fromCharCode(65 + i)])
|
keys.map((key, i) => [key, String.fromCharCode(97 + i)])
|
||||||
);
|
);
|
||||||
|
|
||||||
const params = new URLSearchParams(window.location.search);
|
const params = new URLSearchParams(window.location.search);
|
||||||
|
|
@ -25,11 +24,7 @@ export const persist = <T extends Record<string, number>>(wrapee: T): T => {
|
||||||
set: (target, key: string, value: number) => {
|
set: (target, key: string, value: number) => {
|
||||||
const params = new URLSearchParams(window.location.search);
|
const params = new URLSearchParams(window.location.search);
|
||||||
|
|
||||||
if (initialState[key] === value) {
|
params.set(keysToShortKeys[key], value.toString());
|
||||||
params.delete(keysToShortKeys[key]);
|
|
||||||
} else {
|
|
||||||
params.set(keysToShortKeys[key], value.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
(target as any)[key] = value;
|
(target as any)[key] = value;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,19 @@
|
||||||
import { formatNumber } from './format-number';
|
import { formatNumber } from './format-number';
|
||||||
|
|
||||||
|
export enum ValueScaling {
|
||||||
|
Linear,
|
||||||
|
Quadratic,
|
||||||
|
Logarithmic,
|
||||||
|
}
|
||||||
|
|
||||||
export interface SliderConfiguration {
|
export interface SliderConfiguration {
|
||||||
min: number;
|
min: number;
|
||||||
max: number;
|
max: number;
|
||||||
unit?: string;
|
unit?: string;
|
||||||
step?: number;
|
step?: number;
|
||||||
onChangeCallback?: (value: number) => unknown;
|
onChangeCallback?: (value: number) => unknown;
|
||||||
|
scaling: ValueScaling;
|
||||||
|
rounding: (value: number) => number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class SettingsSlider<T extends Record<string, number>> {
|
export class SettingsSlider<T extends Record<string, number>> {
|
||||||
|
|
@ -17,6 +25,8 @@ export class SettingsSlider<T extends Record<string, number>> {
|
||||||
private readonly config: SliderConfiguration = {
|
private readonly config: SliderConfiguration = {
|
||||||
min: 0,
|
min: 0,
|
||||||
max: 1,
|
max: 1,
|
||||||
|
scaling: ValueScaling.Linear,
|
||||||
|
rounding: (value) => value,
|
||||||
};
|
};
|
||||||
|
|
||||||
public constructor(
|
public constructor(
|
||||||
|
|
@ -24,7 +34,7 @@ export class SettingsSlider<T extends Record<string, number>> {
|
||||||
private readonly settingName: keyof T & string,
|
private readonly settingName: keyof T & string,
|
||||||
config: Partial<SliderConfiguration> = {}
|
config: Partial<SliderConfiguration> = {}
|
||||||
) {
|
) {
|
||||||
this.slider = SettingsSlider.createSlider(this.settings[this.settingName]);
|
this.slider = SettingsSlider.createSlider();
|
||||||
this.valueDisplay = SettingsSlider.createValueDisplay();
|
this.valueDisplay = SettingsSlider.createValueDisplay();
|
||||||
this.sliderWrapper = SettingsSlider.createSliderWrapper(
|
this.sliderWrapper = SettingsSlider.createSliderWrapper(
|
||||||
this.settingName,
|
this.settingName,
|
||||||
|
|
@ -37,12 +47,9 @@ export class SettingsSlider<T extends Record<string, number>> {
|
||||||
this.updateConfig(config);
|
this.updateConfig(config);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static createSlider(initialValue: any) {
|
private static createSlider() {
|
||||||
const input = document.createElement('input');
|
const input = document.createElement('input');
|
||||||
|
|
||||||
input.type = 'range';
|
input.type = 'range';
|
||||||
input.value = initialValue.toString();
|
|
||||||
|
|
||||||
return input;
|
return input;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -75,7 +82,9 @@ export class SettingsSlider<T extends Record<string, number>> {
|
||||||
}
|
}
|
||||||
|
|
||||||
private onChange() {
|
private onChange() {
|
||||||
this.settings[this.settingName] = Number(this.slider.value) as any;
|
this.settings[this.settingName] = this.config.rounding(
|
||||||
|
this.inverseScaling(Number(this.slider.value))
|
||||||
|
) as any;
|
||||||
this.config.onChangeCallback?.(this.settings[this.settingName]);
|
this.config.onChangeCallback?.(this.settings[this.settingName]);
|
||||||
this.valueDisplay.innerText = formatNumber(
|
this.valueDisplay.innerText = formatNumber(
|
||||||
this.settings[this.settingName],
|
this.settings[this.settingName],
|
||||||
|
|
@ -88,11 +97,14 @@ export class SettingsSlider<T extends Record<string, number>> {
|
||||||
|
|
||||||
if (this.config.step === undefined) {
|
if (this.config.step === undefined) {
|
||||||
this.config.step =
|
this.config.step =
|
||||||
(this.config.max - this.config.min) / SettingsSlider.DEFAULT_STEP_COUNT;
|
this.scaling(this.config.max - this.scaling(this.config.min)) /
|
||||||
|
SettingsSlider.DEFAULT_STEP_COUNT;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.slider.min = this.config.min.toString();
|
this.slider.value = this.scaling(this.settings[this.settingName]).toString();
|
||||||
this.slider.max = this.config.max.toString();
|
this.slider.min = this.scaling(this.config.min).toString();
|
||||||
|
this.slider.max = this.scaling(this.config.max).toString();
|
||||||
|
|
||||||
this.slider.step = this.config.step.toString();
|
this.slider.step = this.config.step.toString();
|
||||||
|
|
||||||
this.onChange();
|
this.onChange();
|
||||||
|
|
@ -101,4 +113,26 @@ export class SettingsSlider<T extends Record<string, number>> {
|
||||||
public get element(): HTMLElement {
|
public get element(): HTMLElement {
|
||||||
return this.sliderWrapper;
|
return this.sliderWrapper;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private get scaling(): (value: number) => number {
|
||||||
|
switch (this.config.scaling) {
|
||||||
|
case ValueScaling.Linear:
|
||||||
|
return (value) => value;
|
||||||
|
case ValueScaling.Quadratic:
|
||||||
|
return (value) => Math.sqrt(value);
|
||||||
|
case ValueScaling.Logarithmic:
|
||||||
|
return (value) => Math.log10(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private get inverseScaling(): (value: number) => number {
|
||||||
|
switch (this.config.scaling) {
|
||||||
|
case ValueScaling.Linear:
|
||||||
|
return (value) => value;
|
||||||
|
case ValueScaling.Quadratic:
|
||||||
|
return (value) => Math.pow(value, 2);
|
||||||
|
case ValueScaling.Logarithmic:
|
||||||
|
return (value) => Math.pow(10, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue