diff options
author | Hugo Hörnquist <hugo@lysator.liu.se> | 2022-04-21 15:10:17 +0200 |
---|---|---|
committer | Hugo Hörnquist <hugo@lysator.liu.se> | 2022-04-23 21:53:39 +0200 |
commit | d4640c76287310b40cc5e76b430635c0a006708c (patch) | |
tree | e7bd9bd95c0b1e6c5aaf36ca32654c26d2c82817 | |
parent | Made lcov.info phony. (diff) | |
download | calp-d4640c76287310b40cc5e76b430635c0a006708c.tar.gz calp-d4640c76287310b40cc5e76b430635c0a006708c.tar.xz |
Repair and rewrote sliders in HTML.
The old ones where broken since i accidentally removed setVar, instead
of reintrocuding that, I rewrote slider-inputs as web components, which
frees us of having some hacky javascript in the html code.
-rw-r--r-- | module/calp/html/components.scm | 37 | ||||
-rw-r--r-- | static/_slider_input.scss | 10 | ||||
-rw-r--r-- | static/components/slider.ts | 101 | ||||
-rw-r--r-- | static/elements.ts | 2 | ||||
-rw-r--r-- | static/style.scss | 9 |
5 files changed, 125 insertions, 34 deletions
diff --git a/module/calp/html/components.scm b/module/calp/html/components.scm index 6642b1fe..2aa95aa6 100644 --- a/module/calp/html/components.scm +++ b/module/calp/html/components.scm @@ -18,39 +18,22 @@ (xhtml-doc (@) body ...)))) -;; Add a slider with an associated number input. Keeps the two in check. -;; Uses the js function setVar (which must be provided elsewhere) -;; set the the value of @var{variable}. +;; Add a slider with an associated number input. Keeps the two in sync. (define*-public (slider-input key: variable (min 0) (max 10) (step 1) (value 1) (unit "")) - (let ((groupname (symbol->string (gensym "slider")))) - `(div (@ (class "input-group")) - (script - "function " ,groupname "fn (value) {" - "setVar('" ,variable "', value + '" ,unit "');" - "for (let el of document.getElementsByClassName('" ,groupname "')) {" - " el.value = value;" - "}}") - (input (@ (type "range") - (class ,groupname) - (min ,min) - (max ,max) - (step ,step) - (value ,value) - (oninput ,groupname "fn(this.value)") - )) - (input (@ (type "number") - (class ,groupname) - (min ,min) - (max ,max) - (step ,step) - (value ,value) - (oninput ,groupname "fn(this.value)")) - )))) + + `(slider-input + (@ (min ,min) + (max ,max) + (step ,step) + (value ,value) + (oninput + ,(format #f "document.documentElement.style.setProperty('--~a', this.value + '~a')" + variable unit))))) ;; Generates a button or button-like link. ;; TODO <div/> inside <button/> isn't valid. diff --git a/static/_slider_input.scss b/static/_slider_input.scss new file mode 100644 index 00000000..adae56ae --- /dev/null +++ b/static/_slider_input.scss @@ -0,0 +1,10 @@ +/* See components/slider.ts */ + +slider-input { + display: flex; + + input { + min-width: 0; + } +} + diff --git a/static/components/slider.ts b/static/components/slider.ts new file mode 100644 index 00000000..a48d5a40 --- /dev/null +++ b/static/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|null, to: string|null): 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)) { + } + */ +} diff --git a/static/elements.ts b/static/elements.ts index 199839f6..f714815d 100644 --- a/static/elements.ts +++ b/static/elements.ts @@ -8,6 +8,7 @@ import { InputList } from './components/input-list' import { EditRRule } from './components/edit-rrule' import { TabGroupElement } from './components/tab-group-element' import { VEventChangelog } from './components/changelog' +import { SliderInput } from './components/slider' export { initialize_components } @@ -28,6 +29,7 @@ function initialize_components() { customElements.define('date-time-input', DateTimeInput /*, { extends: 'input' } */) customElements.define('input-list', InputList); + customElements.define('slider-input', SliderInput); /* These maybe also require that the global maps are initialized */ customElements.define('popup-element', PopupElement) diff --git a/static/style.scss b/static/style.scss index c5705e24..e351fb5d 100644 --- a/static/style.scss +++ b/static/style.scss @@ -283,15 +283,10 @@ Each event within the eventlist margin: 0; } - .input-group { - display: flex; - - input { - min-width: 0; - } - } } +@import 'slider_input'; + /* Calendar List ---------------------------------------- The list of all the loaded calendars, |