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.
 
 
 
 
 

270 lines
8.4 KiB

import { isElementInViewport } from "../lib/utils";
export default class Scrollable {
public container: HTMLDivElement;
public thumb: HTMLDivElement;
public type: string;
public side: string;
public scrollType: string;
public scrollSide: string;
public scrollSize = -1;
public size = 0;
public thumbSize = 0;
public hiddenElements: {
up: Element[],
down: Element[]
} = {
up: [],
down: []
};
public paddings = {up: 0, down: 0};
public paddingTopDiv: HTMLDivElement;
public paddingBottomDiv: HTMLDivElement;
public splitUp: HTMLElement;
public spliceCount = 1;
public useStylePadding = false;
public useOneHeight = false;
constructor(public el: HTMLDivElement, x = false, y = true) {
this.container = document.createElement('div');
this.container.classList.add('scrollable');
if(x) {
this.container.classList.add('scrollable-x');
this.type = 'width';
this.side = 'left';
this.scrollType = 'scrollWidth';
this.scrollSide = 'scrollLeft';
} else if(y) {
this.container.classList.add('scrollable-y');
this.type = 'height';
this.side = 'top';
this.scrollType = 'scrollHeight';
this.scrollSide = 'scrollTop';
} else {
throw new Error('no side for scroll');
}
this.thumb = document.createElement('div');
this.thumb.className = 'scrollbar-thumb';
// @ts-ignore
this.thumb.style[this.type] = '30px';
this.container.addEventListener('mouseover', this.resize.bind(this));
window.addEventListener('resize', this.resize.bind(this));
this.paddingTopDiv = document.createElement('div');
this.paddingTopDiv.classList.add('scroll-padding');
this.paddingBottomDiv = document.createElement('div');
this.paddingBottomDiv.classList.add('scroll-padding');
this.container.addEventListener('scroll', this.onScroll.bind(this));
this.container.append(this.paddingTopDiv);
Array.from(el.children).forEach(c => this.container.append(c));
this.container.append(this.paddingBottomDiv);
el.append(this.container);//container.append(el);
this.container.parentElement.append(this.thumb);
this.resize();
}
public resize() {
// @ts-ignore
this.scrollSize = this.container[this.scrollType];
let rect = this.container.getBoundingClientRect();
// @ts-ignore
this.size = rect[this.type];
if(!this.size || this.size == this.scrollSize) {
this.thumbSize = 0;
// @ts-ignore
this.thumb.style[this.type] = this.thumbSize + 'px';
return;
}
//if(!height) return;
let divider = this.scrollSize / this.size / 0.5;
this.thumbSize = this.size / divider;
if(this.thumbSize < 20) this.thumbSize = 20;
// @ts-ignore
this.thumb.style[this.type] = this.thumbSize + 'px';
// @ts-ignore
//console.log('onresize', thumb.style[type], thumbHeight, height);
}
public setVirtualContainer(el: HTMLElement, spliceCount = 1, useStylePadding = false, useOneHeight = false) {
this.splitUp = el;
this.hiddenElements = {
up: [],
down: []
};
this.paddings = {
up: 0,
down: 0
};
this.spliceCount = spliceCount;
this.useStylePadding = useStylePadding;
this.useOneHeight = useOneHeight;
if(this.paddingTopDiv.parentElement) {
this.paddingTopDiv.style.height = '';
this.paddingBottomDiv.style.height = '';
}
/* if(useStylePadding) {
this.paddingTopDiv.parentElement.removeChild(this.paddingTopDiv);
this.paddingBottomDiv.parentElement.removeChild(this.paddingBottomDiv);
} else { */
el.parentElement.insertBefore(this.paddingTopDiv, el);
el.parentNode.insertBefore(this.paddingBottomDiv, el.nextSibling);
//}
if(useStylePadding) {
this.paddingTopDiv.style.height = '10px';
this.paddingBottomDiv.style.height = '10px';
}
}
public onScroll() {
// @ts-ignore
//let st = container[scrollSide];
if(this.container[this.scrollType] != this.scrollSize || this.thumbSize == 0) {
this.resize();
}
// @ts-ignore
let value = this.container[this.scrollSide] / (this.scrollSize - this.size) * 100;
let maxValue = 100 - (this.thumbSize / this.size * 100);
//console.log('onscroll', container.scrollHeight, thumbHeight, height, value, maxValue);
// @ts-ignore
this.thumb.style[this.side] = (value >= maxValue ? maxValue : value) + '%';
if(!this.splitUp) {
return;
}
let splitUp = this.splitUp;
let children = Array.from(splitUp.children) as HTMLElement[];
let firstVisible = -1, lastVisible = -1;
let length = children.length;
for(let i = 0; i < length; ++i) {
let child = children[i];
if(isElementInViewport(child)) {
if(firstVisible < 0) firstVisible = i;
lastVisible = i;
}
}
console.log('onscroll', firstVisible, lastVisible);
if(firstVisible > 0) {
let sliced = children.slice(0, firstVisible);
let height = 0, singleHeight = sliced[0].scrollHeight;
for(let child of sliced) {
height += child.scrollHeight;
this.hiddenElements.up.push(child);
child.parentElement.removeChild(child);
}
this.paddings.up += this.useOneHeight ? singleHeight : height;
//console.log('sliced up', sliced.length);
//sliced.forEach(child => child.style.display = 'none');
if(this.useStylePadding) splitUp.style.paddingTop = this.paddings.up + 'px';
else this.paddingTopDiv.style.height = this.paddings.up + 'px';
//console.log('onscroll need to add padding: ', paddings.up);
} else if(this.hiddenElements.up.length) {
console.log('onscroll up', isElementInViewport(this.paddingTopDiv), this.paddings.up);
while(isElementInViewport(this.paddingTopDiv) && this.paddings.up) {
//let child = this.hiddenElements.up.pop();
/*
splitUp.prepend(...childs);
this.paddings.up -= child.scrollHeight;
this.paddingTopDiv.style.height = this.paddings.up + 'px';*/
let childs = this.hiddenElements.up.splice(-this.spliceCount).reverse();
let height = 0;
for(let child of childs) {
splitUp.prepend(child);
height += child.scrollHeight;
}
this.paddings.up -= this.useOneHeight ? childs[0].scrollHeight : height;
if(this.useStylePadding) splitUp.style.paddingTop = this.paddings.up + 'px';
else this.paddingTopDiv.style.height = this.paddings.up + 'px';
}
}
if(lastVisible < (length - 1)) {
let sliced = children.slice(lastVisible + 1, this.useOneHeight ? lastVisible + 1 + this.spliceCount : undefined).reverse();
let height = 0, singleHeight = sliced[0].scrollHeight;
for(let child of sliced) {
height += child.scrollHeight;
this.hiddenElements.down.unshift(child);
child.parentElement.removeChild(child);
}
this.paddings.down += this.useOneHeight ? singleHeight : height;
console.log('onscroll sliced down', splitUp, sliced.length, this.paddings.down + 'px');
//sliced.forEach(child => child.style.display = 'none');
/* if(this.useStylePadding) splitUp.style.paddingBottom = this.paddings.down + 'px';
else */ this.paddingBottomDiv.style.height = this.paddings.down + 'px';
//console.log('onscroll need to add padding: ', paddings.up);
} else if(this.hiddenElements.down.length) {
console.log('onscroll down', isElementInViewport(this.paddingBottomDiv), this.paddings.down, this.hiddenElements);
while(isElementInViewport(this.paddingBottomDiv) && this.paddings.down) {
/* let child = this.hiddenElements.down.shift();
splitUp.append(child);
this.paddings.down -= child.scrollHeight;
this.paddingBottomDiv.style.height = this.paddings.down + 'px'; */
let childs = this.hiddenElements.down.splice(0, this.spliceCount);
let height = 0;
for(let child of childs) {
splitUp.append(child);
height += child.scrollHeight;
}
this.paddings.down -= this.useOneHeight ? childs[0].scrollHeight : height;
/* if(this.useStylePadding) splitUp.style.paddingBottom = this.paddings.down + 'px';
else */ this.paddingBottomDiv.style.height = this.paddings.down + 'px';
}
}
//console.log('onscroll', container, firstVisible, lastVisible, hiddenElements);
//lastScrollPos = st;
}
}