From 7949fcdc683d07689bad5da5d20bfa3eeb5a6a46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hugo=20H=C3=B6rnquist?= Date: Tue, 5 Sep 2023 01:25:00 +0200 Subject: Move frontend code to subdirectories, to simplify command line flags. --- static/ts/components/slider.ts | 101 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 101 insertions(+) create mode 100644 static/ts/components/slider.ts (limited to 'static/ts/components/slider.ts') diff --git a/static/ts/components/slider.ts b/static/ts/components/slider.ts new file mode 100644 index 00000000..48abc91b --- /dev/null +++ b/static/ts/components/slider.ts @@ -0,0 +1,101 @@ +export { SliderInput } + +import { makeElement } from '../lib' + +const dflt = { + min: 0, + max: 100, + step: 1, +} + +type Attribute = 'min' | 'max' | 'step' + +class SliderInput extends HTMLElement { + + /* value a string since javascript kind of expects that */ + #value = "0"; + min = 0; + max = 100; + step = 1; + + readonly slider: HTMLInputElement; + readonly textIn: HTMLInputElement; + + constructor(min?: number, max?: number, step?: number, value?: number) { + super(); + + this.min = min || parseFloat(this.getAttribute('min') || "" + dflt['min']); + this.max = max || parseFloat(this.getAttribute('max') || "" + dflt['max']); + this.step = step || parseFloat(this.getAttribute('step') || "" + dflt['step']); + // https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/range#value + const defaultValue + = (this.max < this.min) + ? this.min + : this.min + (this.max - this.min) / 2; + + this.slider = makeElement('input', { + type: 'range', + min: this.min, + max: this.max, + step: this.step, + value: this.value, + }) as HTMLInputElement + this.textIn = makeElement('input', { + type: 'number', + min: this.min, + max: this.max, + step: this.step, + value: this.value, + }) as HTMLInputElement + + this.slider.addEventListener('input', e => this.propagate(e)); + this.textIn.addEventListener('input', e => this.propagate(e)); + + /* MUST be after sub components are bound */ + this.value = "" + (value || this.getAttribute('value') || defaultValue); + } + + connectedCallback() { + this.replaceChildren(this.slider, this.textIn); + } + + + static get observedAttributes(): Attribute[] { + return ['min', 'max', 'step'] + } + + attributeChangedCallback(name: Attribute, _?: string, to?: string): void { + if (to) { + this.slider.setAttribute(name, to); + this.textIn.setAttribute(name, to); + } else { + this.slider.removeAttribute(name); + this.textIn.removeAttribute(name); + } + this[name] = parseFloat(to || "" + dflt[name]) + } + + propagate(e: Event) { + this.value = (e.target as HTMLInputElement).value; + if (e instanceof InputEvent && this.oninput) { + this.oninput(e); + } + } + + set value(value: string) { + this.slider.value = value; + this.textIn.value = value; + this.#value = value; + } + + get value(): string { + return this.#value; + } + + /* TODO do we want to implement this? + * oninput directly on the component already works + * / + addEventListener(type: string, proc: ((e: Event) => void)) { + } + */ +} -- cgit v1.2.3 From e753d721519f72014241b3d2fc804a919f655769 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hugo=20H=C3=B6rnquist?= Date: Thu, 7 Sep 2023 02:58:41 +0200 Subject: Document remaining javascript items. --- static/ts/components/slider.ts | 70 ++++++++++++++++++++++++++++++++++++------ 1 file changed, 60 insertions(+), 10 deletions(-) (limited to 'static/ts/components/slider.ts') diff --git a/static/ts/components/slider.ts b/static/ts/components/slider.ts index 48abc91b..8be66a73 100644 --- a/static/ts/components/slider.ts +++ b/static/ts/components/slider.ts @@ -1,24 +1,58 @@ -export { SliderInput } +/** + + + A Web Component implementing a slider with a corresponding number input. + + TODO rename this file + + ### Parameters + + All of these are optional, see {@linkcode dflt} for defaults. + + #### min + Minimum allowed value. + + #### max + Maximum allowed value. + + #### step + How large each step of the slider/number box should be. + + @module +*/ + +export { SliderInput, Attribute, dflt } import { makeElement } from '../lib' +/** Defalut values for all attributes, if not given */ const dflt = { min: 0, max: 100, step: 1, } +/** Valid attributes for SliderInput */ type Attribute = 'min' | 'max' | 'step' +/** + Component displaying an input slider, together with a corresponding numerical + input +*/ class SliderInput extends HTMLElement { /* value a string since javascript kind of expects that */ - #value = "0"; - min = 0; - max = 100; - step = 1; - + #value = "" + dflt.min + /** Minimum allowed value */ + min = dflt.min + /** Maximum allowed value */ + max = dflt.max + /** How large each step should be */ + step = dflt.step + + /** The HTML slider component */ readonly slider: HTMLInputElement; + /** The HTML number input component */ readonly textIn: HTMLInputElement; constructor(min?: number, max?: number, step?: number, value?: number) { @@ -48,8 +82,8 @@ class SliderInput extends HTMLElement { value: this.value, }) as HTMLInputElement - this.slider.addEventListener('input', e => this.propagate(e)); - this.textIn.addEventListener('input', e => this.propagate(e)); + this.slider.addEventListener('input', e => this.#propagate(e)); + this.textIn.addEventListener('input', e => this.#propagate(e)); /* MUST be after sub components are bound */ this.value = "" + (value || this.getAttribute('value') || defaultValue); @@ -59,7 +93,7 @@ class SliderInput extends HTMLElement { this.replaceChildren(this.slider, this.textIn); } - + /** ['min', 'max', 'step'] */ static get observedAttributes(): Attribute[] { return ['min', 'max', 'step'] } @@ -75,19 +109,35 @@ class SliderInput extends HTMLElement { this[name] = parseFloat(to || "" + dflt[name]) } - propagate(e: Event) { + /** + Helper for updating the value attribute + + Event listeners are bound on both the input elements, which both simply + call this. This procedure then updates the classes value field. + + TODO `oninput`? + */ + #propagate(e: Event) { this.value = (e.target as HTMLInputElement).value; if (e instanceof InputEvent && this.oninput) { this.oninput(e); } } + /** + Set a new numerical value. + + A number not possible due to the current `min`, `max`, and `step` + properties can be set and will work, the slider will however not + properly show it, but rather the closest value it can display. + */ set value(value: string) { this.slider.value = value; this.textIn.value = value; this.#value = value; } + /** Get the current numerical value */ get value(): string { return this.#value; } -- cgit v1.2.3