aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHugo Hörnquist <hugo@lysator.liu.se>2022-04-21 15:10:17 +0200
committerHugo Hörnquist <hugo@lysator.liu.se>2022-04-23 21:53:39 +0200
commitd4640c76287310b40cc5e76b430635c0a006708c (patch)
treee7bd9bd95c0b1e6c5aaf36ca32654c26d2c82817
parentMade lcov.info phony. (diff)
downloadcalp-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.scm37
-rw-r--r--static/_slider_input.scss10
-rw-r--r--static/components/slider.ts101
-rw-r--r--static/elements.ts2
-rw-r--r--static/style.scss9
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,