Telegram Web K with changes to work inside I2P
https://web.telegram.i2p/
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
175 lines
5.0 KiB
175 lines
5.0 KiB
/* |
|
* https://github.com/morethanwords/tweb |
|
* Copyright (C) 2019-2021 Eduard Kuzmenko |
|
* https://github.com/morethanwords/tweb/blob/master/LICENSE |
|
*/ |
|
|
|
import { clamp } from "../helpers/number"; |
|
import attachGrabListeners, { GrabEvent } from "../helpers/dom/attachGrabListeners"; |
|
import { safeAssign } from "../helpers/object"; |
|
|
|
export default class RangeSelector { |
|
public container: HTMLDivElement; |
|
protected filled: HTMLDivElement; |
|
protected seek: HTMLInputElement; |
|
|
|
public mousedown = false; |
|
protected rect: DOMRect; |
|
protected _removeListeners: () => void; |
|
|
|
private events: Partial<{ |
|
//onMouseMove: ProgressLine['onMouseMove'], |
|
onMouseDown: RangeSelector['onMouseDown'], |
|
onMouseUp: RangeSelector['onMouseUp'], |
|
onScrub: (value: number) => void |
|
}> = {}; |
|
|
|
protected decimals: number; |
|
|
|
protected step: number; |
|
protected min: number; |
|
protected max: number; |
|
protected withTransition = false; |
|
protected useTransform = false; |
|
protected vertical = false; |
|
|
|
constructor( |
|
options: { |
|
step: RangeSelector['step'], |
|
min: RangeSelector['min'], |
|
max: RangeSelector['max'], |
|
withTransition?: RangeSelector['withTransition'], |
|
useTransform?: RangeSelector['useTransform'], |
|
vertical?: RangeSelector['vertical'] |
|
}, |
|
value = 0 |
|
) { |
|
safeAssign(this, options); |
|
|
|
this.container = document.createElement('div'); |
|
this.container.classList.add('progress-line'); |
|
|
|
// there is no sense in using transition with transform, because it is updating every frame |
|
if(this.useTransform) { |
|
this.container.classList.add('use-transform'); |
|
} else if(this.withTransition) { |
|
this.container.classList.add('with-transition'); |
|
} |
|
|
|
this.filled = document.createElement('div'); |
|
this.filled.classList.add('progress-line__filled'); |
|
|
|
const seek = this.seek = document.createElement('input'); |
|
seek.classList.add('progress-line__seek'); |
|
//seek.setAttribute('max', '0'); |
|
seek.type = 'range'; |
|
seek.step = '' + this.step; |
|
seek.min = '' + this.min; |
|
seek.max = '' + this.max; |
|
seek.value = '' + value; |
|
|
|
if(value) { |
|
this.setProgress(value); |
|
} |
|
|
|
const stepStr = '' + this.step; |
|
const index = stepStr.indexOf('.'); |
|
this.decimals = index === -1 ? 0 : stepStr.length - index - 1; |
|
|
|
//this.setListeners(); |
|
|
|
this.container.append(this.filled, seek); |
|
} |
|
|
|
get value() { |
|
return +this.seek.value; |
|
} |
|
|
|
public setHandlers(events: RangeSelector['events']) { |
|
this.events = events; |
|
} |
|
|
|
protected onMouseMove = (event: GrabEvent) => { |
|
this.scrub(event); |
|
}; |
|
|
|
protected onMouseDown = (event: GrabEvent) => { |
|
this.rect = this.container.getBoundingClientRect(); |
|
this.mousedown = true; |
|
this.scrub(event); |
|
this.container.classList.add('is-focused'); |
|
this.events?.onMouseDown && this.events.onMouseDown(event); |
|
}; |
|
|
|
protected onMouseUp = (event: GrabEvent) => { |
|
this.mousedown = false; |
|
this.container.classList.remove('is-focused'); |
|
this.events?.onMouseUp && this.events.onMouseUp(event); |
|
}; |
|
|
|
public setListeners() { |
|
this.seek.addEventListener('input', this.onInput); |
|
this._removeListeners = attachGrabListeners(this.container, this.onMouseDown, this.onMouseMove, this.onMouseUp); |
|
} |
|
|
|
public onInput = () => { |
|
const value = +this.seek.value; |
|
this.setFilled(value); |
|
this.events?.onScrub && this.events.onScrub(value); |
|
}; |
|
|
|
public setProgress(value: number) { |
|
this.seek.value = '' + value; |
|
this.setFilled(+this.seek.value); // clamp |
|
} |
|
|
|
public addProgress(value: number) { |
|
this.seek.value = '' + (+this.seek.value + value); |
|
this.setFilled(+this.seek.value); // clamp |
|
} |
|
|
|
public setFilled(value: number) { |
|
let percents = (value - this.min) / (this.max - this.min); |
|
percents = clamp(percents, 0, 1); |
|
|
|
// using scaleX and width even with vertical because it will be rotated |
|
if(this.useTransform) { |
|
this.filled.style.transform = `scaleX(${percents})`; |
|
} else { |
|
this.filled.style.width = (percents * 100) + '%'; |
|
} |
|
} |
|
|
|
protected scrub(event: GrabEvent) { |
|
const rectMax = this.vertical ? this.rect.height : this.rect.width; |
|
const offsetAxisValue = clamp(this.vertical ? -(event.y - this.rect.bottom) : event.x - this.rect.left, 0, rectMax); |
|
|
|
let value = this.min + (offsetAxisValue / rectMax * (this.max - this.min)); |
|
|
|
if((value - this.min) < ((this.max - this.min) / 2)) { |
|
value -= this.step / 10; |
|
} |
|
|
|
value = +value.toFixed(this.decimals); |
|
value = clamp(value, this.min, this.max); |
|
|
|
//this.seek.value = '' + value; |
|
//this.onInput(); |
|
|
|
this.setProgress(value); |
|
this.events?.onScrub && this.events.onScrub(value); |
|
|
|
return value; |
|
} |
|
|
|
public removeListeners() { |
|
if(this._removeListeners) { |
|
this._removeListeners(); |
|
this._removeListeners = null; |
|
} |
|
|
|
this.seek.removeEventListener('input', this.onInput); |
|
|
|
this.events = {}; |
|
} |
|
}
|
|
|