import * as ko from "knockout"; import * as timeService from "../timeservice"; export class TableSlider { public current: KnockoutObservable; public currentValue: KnockoutComputed; public minimum: KnockoutObservable; public maximum: KnockoutObservable; public position: KnockoutComputed; private step: number; private minRelative: number; private maxRelative: number; private translator: (x: number) => number; constructor() { const self = this; this.current = ko.observable(null); this.currentValue = ko.computed({ read() { const ivalue = self.current(); if (ivalue === null) { return null; } return ivalue.toString(); }, write(value) { let ivalue = parseInt(value, 10); if (isNaN(ivalue) || !isFinite(ivalue)) { return; } if (ivalue >= self.maximum()) { ivalue = self.maximum(); } if (ivalue <= self.minimum()) { ivalue = self.minimum(); } self.current(ivalue); }, }); this.minimum = ko.observable(); this.maximum = ko.observable(); this.position = ko.computed({ read() { const pixelDistance = self.maxRelative - self.minRelative; if (pixelDistance === 0) { return self.maxRelative; } const delta = self.maximum() - self.minimum(); if (delta === 0) { return self.maxRelative; } const ratio = pixelDistance / delta; const currentRelative = Math.floor(ratio * (self.current() - self.minimum())); return self.minRelative + currentRelative; }, write(value) { if (value >= self.maxRelative) { self.current(self.maximum()); return; } if (value <= self.minRelative) { self.current(self.minimum()); return; } const pixelDistance = self.maxRelative - self.minRelative; if (pixelDistance === 0) { self.current(self.minimum()); return; } const delta = self.maximum() - self.minimum(); if (delta === 0) { self.current(self.minimum()); return; } const ratio = delta / pixelDistance; let currentAbsolute = Math.floor(ratio * (value - self.minRelative)); // Round currentAbsolute to step const currentAbsoluteAligned = Math.floor(currentAbsolute / self.step) * self.step; if ((currentAbsolute - currentAbsoluteAligned) < (currentAbsoluteAligned + self.step - currentAbsolute)) { currentAbsolute = currentAbsoluteAligned; } else { currentAbsolute = currentAbsoluteAligned + self.step; } if (currentAbsolute + self.minimum() > self.maximum()) { self.current(self.maximum()); return; } if (currentAbsolute + self.minimum() < self.minimum()) { self.current(self.minimum()); return; } self.current(currentAbsolute + self.minimum()); }, }); } /** * Set bounds for the slider in the relative coordinates. * @param minRelative Number Minimum coordinate in the relative coordinates. * @param maxRelative Number Maximum coordinate in the relative coordinates. * @param translator Function Function which translate the page coordinates to the relative coordinates. */ public setBounds(minRelative: number, maxRelative: number, translator: (x: number) => number) { this.minRelative = minRelative; this.maxRelative = maxRelative; this.translator = translator; } public setParameters(value, step, min, max) { this.current(value); this.minimum(min); this.maximum(max); this.position.notifySubscribers(); this.step = step; } public setStep(step: number) { this.step = step; } public increase() { let current = this.current(); current = Math.min(current + this.step, this.maximum()); this.current(current); this.position.notifySubscribers(); } public decrease() { let current = this.current(); current = Math.max(current - this.step, this.minimum()); this.current(current); this.position.notifySubscribers(); } public withinRange(value: number) { value = Math.max(value, this.minimum()); value = Math.min(value, this.maximum()); return value; } public isWithinRange(value: number) { if (value >= this.minimum() && value <= this.maximum()) { return true; } return false; } public fixupValue() { const self = this; if (this.current() !== null && this.current() !== undefined) { this.currentValue(this.current().toFixed()); timeService.setTimeout(function() { const c = self.current(); self.current(0); self.current(c); }, 10); } } public selectManually(event: MouseEvent) { const relativePosition = this.translator(event.pageX); this.setPosition(relativePosition); } public setPosition(relativePosition: number) { relativePosition = Math.max(relativePosition, this.minRelative); relativePosition = Math.min(relativePosition, this.maxRelative); this.position(relativePosition); } }