scroll try #8
This commit is contained in:
parent
a5b2f27b98
commit
1ef3e94989
@ -249,7 +249,7 @@ const initEmoticonsDropdown = (pageEl: HTMLDivElement,
|
|||||||
|
|
||||||
docs.forEach(doc => {
|
docs.forEach(doc => {
|
||||||
let div = document.createElement('div');
|
let div = document.createElement('div');
|
||||||
wrapSticker(doc, div, undefined, lazyLoadQueue, EMOTICONSSTICKERGROUP, true);
|
wrapSticker(doc, div, undefined, lazyLoadQueue, EMOTICONSSTICKERGROUP, true, false, true);
|
||||||
|
|
||||||
categoryDiv.append(div);
|
categoryDiv.append(div);
|
||||||
});
|
});
|
||||||
@ -293,7 +293,7 @@ const initEmoticonsDropdown = (pageEl: HTMLDivElement,
|
|||||||
let categoryDiv = document.createElement('div');
|
let categoryDiv = document.createElement('div');
|
||||||
categoryDiv.classList.add('sticker-category');
|
categoryDiv.classList.add('sticker-category');
|
||||||
|
|
||||||
stickersDiv.prepend(categoryDiv);
|
stickersScroll.prepend(categoryDiv);
|
||||||
|
|
||||||
categoryPush(categoryDiv, stickers.stickers, true);
|
categoryPush(categoryDiv, stickers.stickers, true);
|
||||||
});
|
});
|
||||||
@ -314,14 +314,14 @@ const initEmoticonsDropdown = (pageEl: HTMLDivElement,
|
|||||||
|
|
||||||
menu.append(li);
|
menu.append(li);
|
||||||
|
|
||||||
stickersDiv.append(categoryDiv);
|
stickersScroll.append(categoryDiv);
|
||||||
|
|
||||||
let stickerSet = await appStickersManager.getStickerSet(set);
|
let stickerSet = await appStickersManager.getStickerSet(set);
|
||||||
|
|
||||||
if(stickerSet.set.thumb) {
|
if(stickerSet.set.thumb) {
|
||||||
let thumb = stickerSet.set.thumb;
|
let thumb = stickerSet.set.thumb;
|
||||||
|
|
||||||
appStickersManager.getStickerSetThumb(stickerSet.set).then(async(blob) => {
|
appStickersManager.getStickerSetThumb(stickerSet.set).then((blob) => {
|
||||||
if(thumb.w == 1 && thumb.h == 1) { // means animated
|
if(thumb.w == 1 && thumb.h == 1) { // means animated
|
||||||
const reader = new FileReader();
|
const reader = new FileReader();
|
||||||
|
|
||||||
|
@ -69,7 +69,7 @@ export default () => import('../lib/services').then(services => {
|
|||||||
appDialogsManager.setLastMessage(dialog);
|
appDialogsManager.setLastMessage(dialog);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(performed) {
|
if(performed/* && false */) {
|
||||||
/////////console.log('will sortDom');
|
/////////console.log('will sortDom');
|
||||||
appDialogsManager.sortDom();
|
appDialogsManager.sortDom();
|
||||||
appDialogsManager.sortDom(true);
|
appDialogsManager.sortDom(true);
|
||||||
|
@ -4,6 +4,7 @@ import { cancelEvent } from "../lib/utils";
|
|||||||
import FastDom from 'fastdom';
|
import FastDom from 'fastdom';
|
||||||
import 'fastdom/src/fastdom-strict'; // exclude in production
|
import 'fastdom/src/fastdom-strict'; // exclude in production
|
||||||
import FastDomPromised from 'fastdom/extensions/fastdom-promised';
|
import FastDomPromised from 'fastdom/extensions/fastdom-promised';
|
||||||
|
import { logger } from "../lib/polyfill";
|
||||||
|
|
||||||
//const fastdom = FastDom.extend(FastDomPromised);
|
//const fastdom = FastDom.extend(FastDomPromised);
|
||||||
const fastdom = ((window as any).fastdom as typeof FastDom).extend(FastDomPromised);
|
const fastdom = ((window as any).fastdom as typeof FastDom).extend(FastDomPromised);
|
||||||
@ -14,6 +15,16 @@ setTimeout(() => {
|
|||||||
//(window as any).fastdom.strict(true);
|
//(window as any).fastdom.strict(true);
|
||||||
}, 5e3);
|
}, 5e3);
|
||||||
|
|
||||||
|
/*
|
||||||
|
var el = $0;
|
||||||
|
var height = 0;
|
||||||
|
var checkUp = false;
|
||||||
|
|
||||||
|
do {
|
||||||
|
height += el.scrollHeight;
|
||||||
|
} while(el = (checkUp ? el.previousElementSibling : el.nextElementSibling));
|
||||||
|
console.log(height);
|
||||||
|
*/
|
||||||
|
|
||||||
export default class Scrollable {
|
export default class Scrollable {
|
||||||
public container: HTMLDivElement;
|
public container: HTMLDivElement;
|
||||||
@ -51,40 +62,51 @@ export default class Scrollable {
|
|||||||
public bottomObserver: IntersectionObserver;
|
public bottomObserver: IntersectionObserver;
|
||||||
|
|
||||||
public splitObserver: IntersectionObserver;
|
public splitObserver: IntersectionObserver;
|
||||||
public splitMeasure: Promise<{element: Element, height: number}[]> = null;
|
public splitMeasureTop: Promise<{element: Element, height: number}[]> = null;
|
||||||
|
public splitMeasureBottom: Scrollable['splitMeasureTop'] = null;
|
||||||
public splitMeasureAdd: Promise<number> = null;
|
public splitMeasureAdd: Promise<number> = null;
|
||||||
public splitMutate: Promise<void> = null;
|
public splitMeasureRemoveBad: Promise<Element> = null;
|
||||||
|
public splitMutateTop: Promise<void> = null;
|
||||||
|
public splitMutateBottom: Scrollable['splitMutateTop'] = null;
|
||||||
|
public splitMutateRemoveBad: Promise<void> = null;
|
||||||
|
|
||||||
constructor(public el: HTMLDivElement, x = false, y = true, public splitOffset = 300) {
|
public splitMutateIntersectionTop: Promise<void> = null;
|
||||||
|
public splitMutateIntersectionBottom: Promise<void> = null;
|
||||||
|
|
||||||
|
public onScrollMeasure: Promise<any> = null;
|
||||||
|
|
||||||
|
public lastScrollTop: number = 0;
|
||||||
|
public scrollTopOffset: number = 0;
|
||||||
|
|
||||||
|
private log: ReturnType<typeof logger>;
|
||||||
|
private debug = false;
|
||||||
|
|
||||||
|
constructor(public el: HTMLDivElement, x = false, y = true, public splitOffset = 300, logPrefix = '') {
|
||||||
this.container = document.createElement('div');
|
this.container = document.createElement('div');
|
||||||
this.container.classList.add('scrollable');
|
this.container.classList.add('scrollable');
|
||||||
|
|
||||||
//let arr = [];
|
this.log = logger('SCROLL' + (logPrefix ? '-' + logPrefix : ''));
|
||||||
//for(let i = 0.001; i < 1; i += 0.001) arr.push(i);
|
|
||||||
|
let arr = [];
|
||||||
|
for(let i = 0.001; i < 1; i += 0.001) arr.push(i);
|
||||||
this.topObserver = new IntersectionObserver(entries => {
|
this.topObserver = new IntersectionObserver(entries => {
|
||||||
let entry = entries[0];
|
let entry = entries[entries.length - 1];
|
||||||
|
|
||||||
console.log('top intersection:', entries, entry.isIntersecting, entry.intersectionRatio > 0);
|
//console.log('top intersection:', entries, entry.isIntersecting, entry.intersectionRatio > 0);
|
||||||
if(entry.isIntersecting) {
|
if(entry.isIntersecting) {
|
||||||
//this.onTopIntersection(entry);
|
|
||||||
this.onTopIntersection(entry.intersectionRect.height);
|
|
||||||
|
|
||||||
if(this.onScrolledTop) this.onScrolledTop();
|
if(this.onScrolledTop) this.onScrolledTop();
|
||||||
}
|
}
|
||||||
// console.log('top intersection end');
|
// console.log('top intersection end');
|
||||||
}, {/* threshold: arr, */root: this.el});
|
}, {root: this.el});
|
||||||
|
|
||||||
this.bottomObserver = new IntersectionObserver(entries => {
|
this.bottomObserver = new IntersectionObserver(entries => {
|
||||||
let entry = entries[0];
|
let entry = entries[entries.length - 1];
|
||||||
|
|
||||||
console.log('bottom intersection:', entries, entry.isIntersecting, entry.intersectionRatio > 0);
|
//console.log('bottom intersection:', entries, entry, entry.isIntersecting, entry.intersectionRatio > 0);
|
||||||
if(entry.isIntersecting) {
|
if(entry.isIntersecting) {
|
||||||
//this.onBottomIntersection(entry);
|
|
||||||
this.onBottomIntersection(entry.intersectionRect.height);
|
|
||||||
|
|
||||||
if(this.onScrolledBottom) this.onScrolledBottom();
|
if(this.onScrolledBottom) this.onScrolledBottom();
|
||||||
}
|
}
|
||||||
}, {/* threshold: arr, */root: this.el});
|
}, {root: this.el});
|
||||||
|
|
||||||
if(x) {
|
if(x) {
|
||||||
this.container.classList.add('scrollable-x');
|
this.container.classList.add('scrollable-x');
|
||||||
@ -151,7 +173,10 @@ export default class Scrollable {
|
|||||||
});
|
});
|
||||||
|
|
||||||
//this.container.addEventListener('mouseover', this.resize.bind(this)); // omg
|
//this.container.addEventListener('mouseover', this.resize.bind(this)); // omg
|
||||||
window.addEventListener('resize', this.resize.bind(this));
|
window.addEventListener('resize', () => {
|
||||||
|
//this.resize.bind(this);
|
||||||
|
this.onScroll();
|
||||||
|
});
|
||||||
|
|
||||||
this.paddingTopDiv = document.createElement('div');
|
this.paddingTopDiv = document.createElement('div');
|
||||||
this.paddingTopDiv.classList.add('scroll-padding');
|
this.paddingTopDiv.classList.add('scroll-padding');
|
||||||
@ -170,128 +195,245 @@ export default class Scrollable {
|
|||||||
this.resize();
|
this.resize();
|
||||||
}
|
}
|
||||||
|
|
||||||
public splitObserve(entries: IntersectionObserverEntry[]) {
|
public detachTop(child: Element, needHeight = 0) {
|
||||||
console.log('splitObserver', entries);
|
if(this.splitMeasureBottom) fastdom.clear(this.splitMeasureBottom);
|
||||||
for(let entry of entries) { // there may be duplicates (1st - not intersecting, 2nd - intersecting)
|
if(this.splitMutateBottom) fastdom.clear(this.splitMutateBottom);
|
||||||
//console.log('onscroll entry', entry.target, entry.isIntersecting, entry);
|
|
||||||
if(!entry.target.parentElement || !entry.rootBounds) continue;
|
this.splitMeasureBottom = fastdom.measure(() => {
|
||||||
|
let sliced: {element: Element, height: number}[] = [];
|
||||||
|
|
||||||
|
do {
|
||||||
|
if(needHeight > 0) {
|
||||||
|
needHeight -= child.scrollHeight;
|
||||||
|
} else {
|
||||||
|
sliced.push({element: child, height: child.scrollHeight});
|
||||||
|
}
|
||||||
|
} while(child = child.previousElementSibling);
|
||||||
|
return sliced;
|
||||||
|
});
|
||||||
|
|
||||||
|
return this.splitMeasureBottom.then(sliced => {
|
||||||
|
if(this.splitMutateBottom) fastdom.clear(this.splitMutateBottom);
|
||||||
|
|
||||||
|
return this.splitMutateBottom = fastdom.mutate(() => {
|
||||||
|
sliced.forEachReverse((child) => {
|
||||||
|
let {element, height} = child;
|
||||||
|
if(!this.splitUp.contains(element)) return;
|
||||||
|
|
||||||
|
this.paddings.up += height;
|
||||||
|
this.hiddenElements.up.push(child);
|
||||||
|
this.splitUp.removeChild(element);
|
||||||
|
//element.parentElement.removeChild(element);
|
||||||
|
});
|
||||||
|
|
||||||
|
if(this.debug) {
|
||||||
|
this.log('sliced up', sliced);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.paddingTopDiv.style.height = this.paddings.up + 'px';
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public detachBottom(child: Element, needHeight = 0) {
|
||||||
|
if(this.splitMeasureBottom) fastdom.clear(this.splitMeasureBottom);
|
||||||
|
if(this.splitMutateBottom) fastdom.clear(this.splitMutateBottom);
|
||||||
|
|
||||||
|
this.splitMeasureBottom = fastdom.measure(() => {
|
||||||
|
let sliced: {element: Element, height: number}[] = [];
|
||||||
|
|
||||||
|
do {
|
||||||
|
if(needHeight > 0) {
|
||||||
|
needHeight -= child.scrollHeight;
|
||||||
|
} else {
|
||||||
|
sliced.push({element: child, height: child.scrollHeight});
|
||||||
|
}
|
||||||
|
} while(child = child.nextElementSibling);
|
||||||
|
return sliced;
|
||||||
|
});
|
||||||
|
|
||||||
|
return this.splitMeasureBottom.then(sliced => {
|
||||||
|
if(this.splitMutateBottom) fastdom.clear(this.splitMutateBottom);
|
||||||
|
|
||||||
|
return this.splitMutateBottom = fastdom.mutate(() => {
|
||||||
|
sliced.forEachReverse((child) => {
|
||||||
|
let {element, height} = child;
|
||||||
|
if(!this.splitUp.contains(element)) return;
|
||||||
|
|
||||||
|
this.paddings.down += height;
|
||||||
|
this.hiddenElements.down.unshift(child);
|
||||||
|
this.splitUp.removeChild(element);
|
||||||
|
//element.parentElement.removeChild(element);
|
||||||
|
});
|
||||||
|
|
||||||
|
if(this.debug) {
|
||||||
|
this.log('sliced down', sliced);
|
||||||
|
}
|
||||||
|
|
||||||
let child = entry.target;
|
this.paddingBottomDiv.style.height = this.paddings.down + 'px';
|
||||||
let needHeight = this.splitOffset;
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public detachByPrevScroll(child: Element, prevScrollTop: number, needHeight = 0) {
|
||||||
|
if(this.splitMeasureBottom) fastdom.clear(this.splitMeasureBottom);
|
||||||
|
if(this.splitMutateBottom) fastdom.clear(this.splitMutateBottom);
|
||||||
|
|
||||||
|
let attachToTop = this.paddings.up < prevScrollTop;
|
||||||
|
|
||||||
|
this.splitMeasureBottom = fastdom.measure(() => {
|
||||||
|
let sliced: {element: Element, height: number}[] = [];
|
||||||
|
|
||||||
|
do {
|
||||||
|
if(needHeight > 0) {
|
||||||
|
needHeight -= child.scrollHeight;
|
||||||
|
} else {
|
||||||
|
sliced.push({element: child, height: child.scrollHeight});
|
||||||
|
}
|
||||||
|
} while(child = child.nextElementSibling);
|
||||||
|
return sliced;
|
||||||
|
});
|
||||||
|
|
||||||
|
return this.splitMeasureBottom.then(sliced => {
|
||||||
|
if(this.splitMutateBottom) fastdom.clear(this.splitMutateBottom);
|
||||||
|
|
||||||
|
return this.splitMutateBottom = fastdom.mutate(() => {
|
||||||
|
sliced.forEachReverse((child) => {
|
||||||
|
let {element, height} = child;
|
||||||
|
if(!this.splitUp.contains(element)) return;
|
||||||
|
|
||||||
|
this.paddings.down += height;
|
||||||
|
this.hiddenElements.down.unshift(child);
|
||||||
|
this.splitUp.removeChild(element);
|
||||||
|
//element.parentElement.removeChild(element);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.log('sliced down', sliced);
|
||||||
|
this.paddingBottomDiv.style.height = this.paddings.down + 'px';
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public splitObserve(entries: IntersectionObserverEntry[]) {
|
||||||
|
let sorted: {
|
||||||
|
intersecting: {
|
||||||
|
top?: IntersectionObserverEntry,
|
||||||
|
bottom?: IntersectionObserverEntry
|
||||||
|
},
|
||||||
|
notIntersecting: {
|
||||||
|
top?: IntersectionObserverEntry,
|
||||||
|
bottom?: IntersectionObserverEntry
|
||||||
|
}
|
||||||
|
} = {
|
||||||
|
intersecting: {},
|
||||||
|
notIntersecting: {}
|
||||||
|
};
|
||||||
|
|
||||||
|
for(let entry of entries) { // there may be duplicates (1st - not intersecting, 2nd - intersecting)
|
||||||
|
//console.log('onscroll entry 1', entry.target, entry.isIntersecting, entry);
|
||||||
|
if(!entry.target.parentElement || !entry.rootBounds) continue;
|
||||||
|
|
||||||
if(!entry.isIntersecting) {
|
if(!entry.isIntersecting) {
|
||||||
let isTop = entry.boundingClientRect.top <= 0;
|
let isTop = entry.boundingClientRect.top <= 0;
|
||||||
let isBottom = entry.rootBounds.height <= entry.boundingClientRect.top;
|
let isBottom = entry.rootBounds.height <= entry.boundingClientRect.top;
|
||||||
console.log('onscroll entry', isTop, isBottom, child, entry);
|
//console.log('onscroll entry notIntersecting', isTop, isBottom, entry.target, entry);
|
||||||
|
|
||||||
//console.log('will call measure');
|
if(isTop) {
|
||||||
if(isTop) { // when scrolling down
|
sorted.notIntersecting.top = entry;
|
||||||
//this.onBottomIntersection(entry);
|
} else if(isBottom && !sorted.notIntersecting.bottom) {
|
||||||
|
sorted.notIntersecting.bottom = entry;
|
||||||
if(this.splitMeasure) fastdom.clear(this.splitMeasure);
|
|
||||||
this.splitMeasure = fastdom.measure(() => {
|
|
||||||
let sliced: {element: Element, height: number}[] = [];
|
|
||||||
|
|
||||||
do {
|
|
||||||
if(needHeight > 0) {
|
|
||||||
needHeight -= child.scrollHeight;
|
|
||||||
} else {
|
|
||||||
sliced.push({element: child, height: child.scrollHeight});
|
|
||||||
}
|
|
||||||
} while(child = child.previousElementSibling);
|
|
||||||
return sliced;
|
|
||||||
});
|
|
||||||
|
|
||||||
this.splitMeasure.then(sliced => {
|
|
||||||
if(this.splitMutate) fastdom.clear(this.splitMutate);
|
|
||||||
|
|
||||||
this.splitMutate = fastdom.mutate(() => {
|
|
||||||
sliced.forEachReverse((child) => {
|
|
||||||
let {element, height} = child;
|
|
||||||
if(!this.splitUp.contains(element)) return;
|
|
||||||
|
|
||||||
this.paddings.up += height;
|
|
||||||
this.hiddenElements.up.push(child);
|
|
||||||
this.splitUp.removeChild(element);
|
|
||||||
//element.parentElement.removeChild(element);
|
|
||||||
});
|
|
||||||
|
|
||||||
this.paddingTopDiv.style.height = this.paddings.up + 'px';
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
//console.log('onscroll sliced up', sliced);
|
|
||||||
} else if(isBottom) { // when scrolling top
|
|
||||||
//this.onTopIntersection(entry);
|
|
||||||
|
|
||||||
if(this.splitMeasure) fastdom.clear(this.splitMeasure);
|
|
||||||
this.splitMeasure = fastdom.measure(() => {
|
|
||||||
let sliced: {element: Element, height: number}[] = [];
|
|
||||||
|
|
||||||
do {
|
|
||||||
if(needHeight > 0) {
|
|
||||||
needHeight -= child.scrollHeight;
|
|
||||||
} else {
|
|
||||||
sliced.push({element: child, height: child.scrollHeight});
|
|
||||||
}
|
|
||||||
} while(child = child.nextElementSibling);
|
|
||||||
return sliced;
|
|
||||||
});
|
|
||||||
|
|
||||||
this.splitMeasure.then(sliced => {
|
|
||||||
if(this.splitMutate) fastdom.clear(this.splitMutate);
|
|
||||||
|
|
||||||
this.splitMutate = fastdom.mutate(() => {
|
|
||||||
sliced.forEachReverse((child) => {
|
|
||||||
let {element, height} = child;
|
|
||||||
if(!this.splitUp.contains(element)) return;
|
|
||||||
|
|
||||||
this.paddings.down += height;
|
|
||||||
this.hiddenElements.down.unshift(child);
|
|
||||||
this.splitUp.removeChild(element);
|
|
||||||
//element.parentElement.removeChild(element);
|
|
||||||
});
|
|
||||||
|
|
||||||
this.paddingBottomDiv.style.height = this.paddings.down + 'px';
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
//console.log('onscroll sliced down', sliced);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//console.log('splitObserver', entry, entry.target, isTop);
|
//console.log('splitObserver', entry, entry.target, isTop);
|
||||||
} else {
|
} else {
|
||||||
let isTop = entry.boundingClientRect.top <= entry.rootBounds.top;
|
let isTop = entry.boundingClientRect.top <= entry.rootBounds.top;
|
||||||
let isBottom = entry.boundingClientRect.bottom >= entry.rootBounds.bottom;
|
let isBottom = entry.boundingClientRect.bottom >= entry.rootBounds.bottom;
|
||||||
|
|
||||||
if(isTop) { // when scrolling up
|
if(isTop) {
|
||||||
if(this.splitMeasureAdd) fastdom.clear(this.splitMeasureAdd);
|
sorted.intersecting.top = entry;
|
||||||
this.splitMeasureAdd = fastdom.measure(() => {
|
} else if(isBottom && !sorted.intersecting.bottom) {
|
||||||
while(child = child.previousElementSibling) {
|
sorted.intersecting.bottom = entry;
|
||||||
needHeight -= child.scrollHeight;
|
}
|
||||||
}
|
|
||||||
|
// if(isTop) {
|
||||||
return needHeight;
|
// this.onTopIntersection(entry.boundingClientRect.height);
|
||||||
|
// } else if(isBottom) {
|
||||||
|
// this.onTopIntersection(entry.boundingClientRect.height);
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('splitObserve', entries, sorted);
|
||||||
|
|
||||||
|
let needHeight = this.splitOffset;
|
||||||
|
let isOutOfView: boolean;
|
||||||
|
let entry: IntersectionObserverEntry;
|
||||||
|
if(entry = sorted.notIntersecting.top) { // scrolled bottom
|
||||||
|
let child = entry.target;
|
||||||
|
|
||||||
|
let diff = entry.boundingClientRect.bottom + needHeight;
|
||||||
|
if(diff < 0) { // maybe need <=, means out of view
|
||||||
|
if(!(child = child.nextElementSibling)) {
|
||||||
|
this.detachTop(this.splitUp.lastElementChild, 0);
|
||||||
|
} else {
|
||||||
|
if(this.splitMeasureRemoveBad) fastdom.clear(this.splitMeasureRemoveBad);
|
||||||
|
this.splitMeasureRemoveBad = fastdom.measure(() => {
|
||||||
|
do {
|
||||||
|
diff += child.scrollHeight;
|
||||||
|
} while(diff < 0 && (child = child.nextElementSibling));
|
||||||
|
|
||||||
|
return child || this.splitUp.lastElementChild;
|
||||||
});
|
});
|
||||||
|
|
||||||
this.splitMeasureAdd.then(needHeight => {
|
this.splitMeasureRemoveBad.then(child => {
|
||||||
this.onTopIntersection(needHeight);
|
this.detachTop(child, 0);
|
||||||
});
|
|
||||||
} else if(isBottom) { // when scrolling down
|
|
||||||
if(this.splitMeasureAdd) fastdom.clear(this.splitMeasureAdd);
|
|
||||||
this.splitMeasureAdd = fastdom.measure(() => {
|
|
||||||
while(child = child.nextElementSibling) {
|
|
||||||
needHeight -= child.scrollHeight;
|
|
||||||
}
|
|
||||||
|
|
||||||
return needHeight;
|
|
||||||
});
|
|
||||||
|
|
||||||
this.splitMeasureAdd.then(needHeight => {
|
|
||||||
this.onBottomIntersection(needHeight);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
this.detachTop(child, needHeight);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(entry = sorted.notIntersecting.bottom) { // scrolled top
|
||||||
|
isOutOfView = (entry.boundingClientRect.top - needHeight) >= entry.rootBounds.height;
|
||||||
|
this.detachBottom(entry.target, isOutOfView ? 0 : needHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(entry = sorted.intersecting.top) { // scrolling top
|
||||||
|
let needHeight = this.splitOffset;
|
||||||
|
|
||||||
|
let child = entry.target;
|
||||||
|
if(this.splitMeasureAdd) fastdom.clear(this.splitMeasureAdd);
|
||||||
|
this.splitMeasureAdd = fastdom.measure(() => {
|
||||||
|
while(child = child.previousElementSibling) {
|
||||||
|
needHeight -= child.scrollHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
return needHeight;
|
||||||
|
});
|
||||||
|
|
||||||
|
this.splitMeasureAdd.then(needHeight => {
|
||||||
|
this.onTopIntersection(needHeight);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if(entry = sorted.intersecting.bottom) { // scrolling bottom
|
||||||
|
let needHeight = this.splitOffset;
|
||||||
|
|
||||||
|
let child = entry.target;
|
||||||
|
if(this.splitMeasureAdd) fastdom.clear(this.splitMeasureAdd);
|
||||||
|
this.splitMeasureAdd = fastdom.measure(() => {
|
||||||
|
while(child = child.nextElementSibling) {
|
||||||
|
needHeight -= child.scrollHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
return needHeight;
|
||||||
|
});
|
||||||
|
|
||||||
|
this.splitMeasureAdd.then(needHeight => {
|
||||||
|
this.onBottomIntersection(needHeight);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async resize() {
|
public async resize() {
|
||||||
@ -338,6 +480,7 @@ export default class Scrollable {
|
|||||||
|
|
||||||
this.hiddenElements.up.length = this.hiddenElements.down.length = 0;
|
this.hiddenElements.up.length = this.hiddenElements.down.length = 0;
|
||||||
this.paddings.up = this.paddings.down = 0;
|
this.paddings.up = this.paddings.down = 0;
|
||||||
|
this.lastScrollTop = 0;
|
||||||
|
|
||||||
if(this.paddingTopDiv.parentElement) {
|
if(this.paddingTopDiv.parentElement) {
|
||||||
fastdom.mutate(() => {
|
fastdom.mutate(() => {
|
||||||
@ -346,12 +489,16 @@ export default class Scrollable {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if(this.splitObserver) {
|
/* if(this.splitObserver) {
|
||||||
this.splitObserver.disconnect();
|
this.splitObserver.disconnect();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.splitObserver = new IntersectionObserver((entries) => this.splitObserve(entries), {root: this.el});
|
|
||||||
|
|
||||||
|
this.splitObserver = new IntersectionObserver((entries) => this.splitObserve(entries), {root: this.el}); */
|
||||||
|
|
||||||
|
this.log('setVirtualContainer:', el, this);
|
||||||
|
|
||||||
|
this.getScrollTopOffset();
|
||||||
|
|
||||||
if(el) {
|
if(el) {
|
||||||
fastdom.mutate(() => {
|
fastdom.mutate(() => {
|
||||||
el.parentElement.insertBefore(this.paddingTopDiv, el);
|
el.parentElement.insertBefore(this.paddingTopDiv, el);
|
||||||
@ -359,10 +506,24 @@ export default class Scrollable {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public getScrollTopOffset() {
|
||||||
|
if(this.splitUp && this.splitUp.parentElement && this.splitUp.parentElement != this.container) { // need to find offset
|
||||||
|
fastdom.measure(() => {
|
||||||
|
let rect = this.splitUp.getBoundingClientRect();
|
||||||
|
let containerRect = this.container.getBoundingClientRect();
|
||||||
|
|
||||||
public async onScroll() {
|
this.scrollTopOffset = rect.top - containerRect.top;
|
||||||
//console.time('scroll onScroll');
|
this.log('set scrollTopOffset to:', this.scrollTopOffset);
|
||||||
let {value, maxValue} = await fastdom.measure(() => {
|
});
|
||||||
|
} else {
|
||||||
|
this.scrollTopOffset = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public onScroll() {
|
||||||
|
if(this.onScrollMeasure) fastdom.clear(this.onScrollMeasure);
|
||||||
|
this.onScrollMeasure = fastdom.measure(() => {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
if(this.container[this.scrollType] != this.scrollSize || this.thumbSize == 0) {
|
if(this.container[this.scrollType] != this.scrollSize || this.thumbSize == 0) {
|
||||||
this.resize();
|
this.resize();
|
||||||
@ -372,85 +533,272 @@ export default class Scrollable {
|
|||||||
let value = this.container[this.scrollSide] / (this.scrollSize - this.size) * 100;
|
let value = this.container[this.scrollSide] / (this.scrollSize - this.size) * 100;
|
||||||
let maxValue = 100 - (this.thumbSize / this.size * 100);
|
let maxValue = 100 - (this.thumbSize / this.size * 100);
|
||||||
|
|
||||||
|
let ret = {value, maxValue};
|
||||||
|
|
||||||
|
if(!this.splitUp) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
let perf = performance.now();
|
||||||
|
let scrollTop = this.scrollTop - this.scrollTopOffset;
|
||||||
|
let outerHeight = this.parentElement.scrollHeight;
|
||||||
|
|
||||||
|
let maxScrollTop = this.scrollHeight - this.scrollTopOffset - outerHeight;
|
||||||
|
if(scrollTop < 0) scrollTop = 0;
|
||||||
|
else if(scrollTop > maxScrollTop) scrollTop = maxScrollTop;
|
||||||
|
|
||||||
|
let toBottom = scrollTop > this.lastScrollTop;
|
||||||
|
|
||||||
|
let visibleFrom = /* scrollTop < this.paddings.up ? scrollTop : */scrollTop - this.paddings.up;
|
||||||
|
let visibleUntil = visibleFrom + outerHeight;
|
||||||
|
let sum = 0;
|
||||||
|
|
||||||
|
let firstVisibleElement: Element;
|
||||||
|
let lastVisibleElement: Element;
|
||||||
|
|
||||||
|
let needHeight = this.splitOffset;
|
||||||
|
|
||||||
|
let children = this.splitUp.children;
|
||||||
|
let length = children.length;
|
||||||
|
for(let i = 0; i < length; ++i) {
|
||||||
|
let element = children[i];
|
||||||
|
|
||||||
|
let height = element.scrollHeight;
|
||||||
|
if(sum < visibleUntil && (sum + height) >= visibleFrom && !firstVisibleElement) { // if any part is in viewport
|
||||||
|
firstVisibleElement = element;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(sum < visibleUntil && firstVisibleElement) {
|
||||||
|
lastVisibleElement = element;
|
||||||
|
}
|
||||||
|
|
||||||
|
sum += element.scrollHeight;
|
||||||
|
|
||||||
|
//this.log(sum, element);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!lastVisibleElement && firstVisibleElement) {
|
||||||
|
lastVisibleElement = firstVisibleElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
// возможно устанавливать прошлый скролл нужно уже после этого промиса, т.к. он может очиститься
|
||||||
|
if(scrollTop == this.lastScrollTop) {
|
||||||
|
this.lastScrollTop = scrollTop;
|
||||||
|
if(firstVisibleElement) this.detachTop(firstVisibleElement, needHeight);
|
||||||
|
if(lastVisibleElement) this.detachBottom(lastVisibleElement, needHeight);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* {
|
||||||
|
this.log('onScroll', (performance.now() - perf).toFixed(3), length, scrollTop,
|
||||||
|
toBottom, firstVisibleElement, lastVisibleElement, visibleFrom, visibleUntil);
|
||||||
|
return {value, maxValue};
|
||||||
|
} */
|
||||||
|
|
||||||
|
if(toBottom) { // scrolling bottom
|
||||||
|
if(firstVisibleElement) {
|
||||||
|
if(this.debug) {
|
||||||
|
this.log('will detach top by:', firstVisibleElement, needHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.detachTop(firstVisibleElement, needHeight);
|
||||||
|
|
||||||
|
if(this.splitMeasureAdd) fastdom.clear(this.splitMeasureAdd);
|
||||||
|
|
||||||
|
let child = lastVisibleElement;
|
||||||
|
this.splitMeasureAdd = fastdom.measure(() => {
|
||||||
|
while(child = child.nextElementSibling) {
|
||||||
|
needHeight -= child.scrollHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.onBottomIntersection(needHeight);
|
||||||
|
return needHeight;
|
||||||
|
});
|
||||||
|
|
||||||
|
/* this.splitMeasureAdd.then(needHeight => {
|
||||||
|
this.onBottomIntersection(needHeight);
|
||||||
|
}); */
|
||||||
|
} else if(length) { // scrolled manually or safari
|
||||||
|
if(this.debug) {
|
||||||
|
this.log.warn('will detach all of top', length, this.splitUp.childElementCount, maxScrollTop, this.paddings, this.lastScrollTop);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.detachTop(children[length - 1], 0).then(() => { // now need to move from one hidden array to another one
|
||||||
|
this.onManualScrollBottom(scrollTop, needHeight);
|
||||||
|
});
|
||||||
|
} else if(this.paddings.down) { // scrolled manually or safari
|
||||||
|
this.onManualScrollBottom(scrollTop, needHeight);
|
||||||
|
}
|
||||||
|
} else { // scrolling top
|
||||||
|
if(lastVisibleElement) {
|
||||||
|
if(this.debug) {
|
||||||
|
this.log('will detach bottom by:', lastVisibleElement, needHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.detachBottom(lastVisibleElement, needHeight);
|
||||||
|
|
||||||
|
let child = firstVisibleElement;
|
||||||
|
if(this.splitMeasureAdd) fastdom.clear(this.splitMeasureAdd);
|
||||||
|
this.splitMeasureAdd = fastdom.measure(() => {
|
||||||
|
while(child = child.previousElementSibling) {
|
||||||
|
needHeight -= child.scrollHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.onTopIntersection(needHeight);
|
||||||
|
return needHeight;
|
||||||
|
});
|
||||||
|
|
||||||
|
/* this.splitMeasureAdd.then(needHeight => {
|
||||||
|
this.onTopIntersection(needHeight);
|
||||||
|
}); */
|
||||||
|
} else if(length) { // scrolled manually or safari
|
||||||
|
if(this.debug) {
|
||||||
|
this.log.warn('will detach all of bottom', length, this.splitUp.childElementCount, maxScrollTop, this.paddings, this.lastScrollTop);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.detachBottom(children[0], 0).then(() => { // now need to move from one hidden array to another one
|
||||||
|
this.onManualScrollTop(scrollTop, needHeight, maxScrollTop);
|
||||||
|
});
|
||||||
|
} else if(this.paddings.up) {
|
||||||
|
this.onManualScrollTop(scrollTop, needHeight, maxScrollTop);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(this.debug) {
|
||||||
|
this.log('onScroll', (performance.now() - perf).toFixed(3), length, scrollTop, maxScrollTop, toBottom, firstVisibleElement, lastVisibleElement, visibleFrom, visibleUntil, this.scrollTopOffset);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.lastScrollTop = scrollTop;
|
||||||
|
|
||||||
return {value, maxValue};
|
return {value, maxValue};
|
||||||
});
|
});
|
||||||
|
|
||||||
//console.log('onscroll', container.scrollHeight, thumbHeight, height, value, maxValue);
|
this.onScrollMeasure.then(({value, maxValue}) => {
|
||||||
fastdom.mutate(() => {
|
fastdom.mutate(() => {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
this.thumb.style[this.side] = (value >= maxValue ? maxValue : value) + '%';
|
this.thumb.style[this.side] = (value >= maxValue ? maxValue : value) + '%';
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
//console.timeEnd('scroll onScroll');
|
//console.timeEnd('scroll onScroll');
|
||||||
}
|
}
|
||||||
|
|
||||||
public async onTopIntersection(/* entry: IntersectionObserverEntry */needHeight: number) {
|
public onManualScrollTop(scrollTop: number, needHeight: number, maxScrollTop: number) {
|
||||||
console.log('onTopIntersection', needHeight, this);
|
//if(this.splitMutateRemoveBad) fastdom.clear(this.splitMutateRemoveBad);
|
||||||
|
this.splitMutateRemoveBad = fastdom.mutate(() => {
|
||||||
if(this.hiddenElements.up.length && this.paddings.up) {
|
let h = maxScrollTop - (scrollTop + outerHeight);
|
||||||
//let needHeight = entry.intersectionRect.height || entry.boundingClientRect.height;
|
|
||||||
//let needHeight = entry.intersectionRect.height || await fastdom.measure(() => this.splitUp.lastElementChild.scrollHeight);
|
|
||||||
|
|
||||||
let fragment = document.createDocumentFragment();
|
while(this.paddings.down < h && this.paddings.up) {
|
||||||
while(needHeight > 0 && this.paddings.up) {
|
|
||||||
let child = this.hiddenElements.up.pop();
|
let child = this.hiddenElements.up.pop();
|
||||||
|
this.hiddenElements.down.unshift(child);
|
||||||
// console.log('top returning from hidden', child);
|
this.paddings.down += child.height;
|
||||||
|
|
||||||
if(!child) {
|
|
||||||
this.paddings.up = 0;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
fragment.prepend(child.element);
|
|
||||||
|
|
||||||
needHeight -= child.height;
|
|
||||||
this.paddings.up -= child.height;
|
this.paddings.up -= child.height;
|
||||||
}
|
}
|
||||||
|
|
||||||
await fastdom.mutate(() => {
|
if(this.debug) {
|
||||||
this.splitUp.prepend(fragment);
|
this.log.warn('bait it off now', this, length, this.splitUp.childElementCount, scrollTop, this.paddings.up, h);
|
||||||
this.paddingTopDiv.style.height = this.paddings.up + 'px';
|
}
|
||||||
});
|
|
||||||
} else {
|
this.paddingBottomDiv.style.height = this.paddings.down + 'px';
|
||||||
await fastdom.mutate(() => {
|
this.onTopIntersection((outerHeight * 2) + (needHeight * 2));
|
||||||
this.paddingTopDiv.style.height = '0px';
|
});
|
||||||
});
|
|
||||||
}
|
/* this.splitMutateRemoveBad.then(() => {
|
||||||
|
}); */
|
||||||
}
|
}
|
||||||
|
|
||||||
public async onBottomIntersection(/* entry: IntersectionObserverEntry */needHeight: number) {
|
public onManualScrollBottom(scrollTop: number, needHeight: number) {
|
||||||
console.log('onBottomIntersection', needHeight, this);
|
//if(this.splitMutateRemoveBad) fastdom.clear(this.splitMutateRemoveBad);
|
||||||
|
this.splitMutateRemoveBad = fastdom.mutate(() => {
|
||||||
if(this.hiddenElements.down.length && this.paddings.down) {
|
let h = scrollTop - needHeight;
|
||||||
//let needHeight = entry.intersectionRect.height || entry.boundingClientRect.height;
|
|
||||||
//let needHeight = entry.intersectionRect.height || await fastdom.measure(() => this.splitUp.firstElementChild.scrollHeight);
|
|
||||||
|
|
||||||
let fragment = document.createDocumentFragment();
|
while(this.paddings.up < h && this.paddings.down) {
|
||||||
while(needHeight > 0 && this.paddings.down) {
|
|
||||||
let child = this.hiddenElements.down.shift();
|
let child = this.hiddenElements.down.shift();
|
||||||
|
this.hiddenElements.up.push(child);
|
||||||
if(!child) {
|
this.paddings.up += child.height;
|
||||||
this.paddings.down = 0;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
fragment.appendChild(child.element);
|
|
||||||
|
|
||||||
needHeight -= child.height;
|
|
||||||
this.paddings.down -= child.height;
|
this.paddings.down -= child.height;
|
||||||
}
|
}
|
||||||
|
|
||||||
await fastdom.mutate(() => {
|
if(this.debug) {
|
||||||
|
this.log.warn('shake it off now', this, length, this.splitUp.childElementCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.paddingTopDiv.style.height = this.paddings.up + 'px';
|
||||||
|
this.onBottomIntersection(outerHeight + (needHeight * 2));
|
||||||
|
});
|
||||||
|
|
||||||
|
/* this.splitMutateRemoveBad.then(() => {
|
||||||
|
}); */
|
||||||
|
}
|
||||||
|
|
||||||
|
public onTopIntersection(needHeight: number) {
|
||||||
|
if(this.debug) {
|
||||||
|
this.log('onTopIntersection', needHeight, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(this.splitMutateIntersectionTop) fastdom.clear(this.splitMutateIntersectionTop);
|
||||||
|
this.splitMutateIntersectionTop = fastdom.mutate(() => {
|
||||||
|
if(this.hiddenElements.up.length && this.paddings.up) {
|
||||||
|
let fragment = document.createDocumentFragment();
|
||||||
|
while(needHeight > 0 && this.paddings.up) {
|
||||||
|
let child = this.hiddenElements.up.pop();
|
||||||
|
|
||||||
|
// console.log('top returning from hidden', child);
|
||||||
|
|
||||||
|
if(!child) {
|
||||||
|
this.paddings.up = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
fragment.prepend(child.element);
|
||||||
|
|
||||||
|
needHeight -= child.height;
|
||||||
|
this.paddings.up -= child.height;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.splitUp.prepend(fragment);
|
||||||
|
this.paddingTopDiv.style.height = this.paddings.up + 'px';
|
||||||
|
} else {
|
||||||
|
this.paddingTopDiv.style.height = '0px';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public onBottomIntersection(needHeight: number) {
|
||||||
|
if(this.debug) {
|
||||||
|
this.log('onBottomIntersection', needHeight, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(this.splitMutateIntersectionBottom) fastdom.clear(this.splitMutateIntersectionBottom);
|
||||||
|
this.splitMutateIntersectionBottom = fastdom.mutate(() => {
|
||||||
|
if(this.hiddenElements.down.length && this.paddings.down) {
|
||||||
|
let fragment = document.createDocumentFragment();
|
||||||
|
while(needHeight > 0 && this.paddings.down) {
|
||||||
|
let child = this.hiddenElements.down.shift();
|
||||||
|
|
||||||
|
if(!child) {
|
||||||
|
this.paddings.down = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
fragment.appendChild(child.element);
|
||||||
|
|
||||||
|
needHeight -= child.height;
|
||||||
|
this.paddings.down -= child.height;
|
||||||
|
}
|
||||||
|
|
||||||
this.splitUp.appendChild(fragment);
|
this.splitUp.appendChild(fragment);
|
||||||
this.paddingBottomDiv.style.height = this.paddings.down + 'px';
|
this.paddingBottomDiv.style.height = this.paddings.down + 'px';
|
||||||
});
|
|
||||||
if(this.onAddedBottom) this.onAddedBottom();
|
/* if(this.debug) {
|
||||||
} else {
|
this.log('onBottomIntersection append:', fragment, needHeight);
|
||||||
await fastdom.mutate(() => {
|
} */
|
||||||
|
|
||||||
|
if(this.onAddedBottom) this.onAddedBottom();
|
||||||
|
} else {
|
||||||
this.paddingBottomDiv.style.height = '0px';
|
this.paddingBottomDiv.style.height = '0px';
|
||||||
});
|
}
|
||||||
}
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public prepend(...smth: (string | Node)[]) {
|
public prepend(...smth: (string | Node)[]) {
|
||||||
@ -467,16 +815,11 @@ export default class Scrollable {
|
|||||||
} else {
|
} else {
|
||||||
this.splitUp.prepend(...smth);
|
this.splitUp.prepend(...smth);
|
||||||
}
|
}
|
||||||
|
|
||||||
for(let node of smth) {
|
|
||||||
if(typeof(node) !== 'string') {
|
|
||||||
this.splitObserver.unobserve(node as Element);
|
|
||||||
this.splitObserver.observe(node as Element);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
this.container.prepend(...smth);
|
this.container.prepend(...smth);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.onScroll();
|
||||||
}
|
}
|
||||||
|
|
||||||
public append(...smth: (string | Node)[]) {
|
public append(...smth: (string | Node)[]) {
|
||||||
@ -493,47 +836,45 @@ export default class Scrollable {
|
|||||||
} else {
|
} else {
|
||||||
this.splitUp.append(...smth);
|
this.splitUp.append(...smth);
|
||||||
}
|
}
|
||||||
|
|
||||||
for(let node of smth) {
|
|
||||||
if(typeof(node) !== 'string') {
|
|
||||||
this.splitObserver.unobserve(node as Element);
|
|
||||||
this.splitObserver.observe(node as Element);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
this.container.append(...smth);
|
this.container.append(...smth);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.onScroll();
|
||||||
}
|
}
|
||||||
|
|
||||||
public insertBefore(newChild: Element, refChild: Element) {
|
public insertBefore(newChild: Element, refChild: Element) {
|
||||||
if(this.splitUp) {
|
if(this.splitUp) {
|
||||||
this.splitObserver.unobserve(newChild);
|
|
||||||
this.splitObserver.observe(newChild);
|
|
||||||
|
|
||||||
let index = -1;
|
let index = -1;
|
||||||
index = this.hiddenElements.up.findIndex(c => c.element == refChild);
|
index = this.hiddenElements.up.findIndex(c => c.element == refChild);
|
||||||
|
|
||||||
// возможно здесь нужно очищать предыдущую высоту если newChild уже скрыт (но может и не нужно)
|
// возможно здесь нужно очищать предыдущую высоту если newChild уже скрыт (но может и не нужно)
|
||||||
if(index !== -1) {
|
if(index !== -1) {
|
||||||
this.hiddenElements.up.splice(index, 0, {element: newChild, height: newChild.scrollHeight || 1});
|
this.hiddenElements.up.splice(index, 0, {element: newChild, height: newChild.scrollHeight || 1});
|
||||||
|
this.onScroll();
|
||||||
return index;
|
return index;
|
||||||
} else {
|
} else {
|
||||||
index = this.hiddenElements.down.findIndex(c => c.element == newChild);
|
index = this.hiddenElements.down.findIndex(c => c.element == newChild);
|
||||||
|
|
||||||
if(index !== -1) {
|
if(index !== -1) {
|
||||||
this.hiddenElements.down.splice(index, 0, {element: newChild, height: newChild.scrollHeight || 1});
|
this.hiddenElements.down.splice(index, 0, {element: newChild, height: newChild.scrollHeight || 1});
|
||||||
|
this.onScroll();
|
||||||
return index;
|
return index;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.onScroll();
|
||||||
return this.splitUp.insertBefore(newChild, refChild);
|
return this.splitUp.insertBefore(newChild, refChild);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.onScroll();
|
||||||
return this.container.insertBefore(newChild, refChild);
|
return this.container.insertBefore(newChild, refChild);
|
||||||
}
|
}
|
||||||
|
|
||||||
set scrollTop(y: number) {
|
set scrollTop(y: number) {
|
||||||
this.container.scrollTop = y;
|
fastdom.mutate(() => {
|
||||||
|
this.container.scrollTop = y;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
get scrollTop() {
|
get scrollTop() {
|
||||||
|
660
src/components/scrollable_backwards.ts
Normal file
660
src/components/scrollable_backwards.ts
Normal file
@ -0,0 +1,660 @@
|
|||||||
|
import { cancelEvent } from "../lib/utils";
|
||||||
|
|
||||||
|
//import {measure} from 'fastdom/fastdom.min';
|
||||||
|
import FastDom from 'fastdom';
|
||||||
|
import 'fastdom/src/fastdom-strict'; // exclude in production
|
||||||
|
import FastDomPromised from 'fastdom/extensions/fastdom-promised';
|
||||||
|
|
||||||
|
//const fastdom = FastDom.extend(FastDomPromised);
|
||||||
|
const fastdom = ((window as any).fastdom as typeof FastDom).extend(FastDomPromised);
|
||||||
|
|
||||||
|
(window as any).fastdom.strict(false);
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
//(window as any).fastdom.strict(true);
|
||||||
|
}, 5e3);
|
||||||
|
|
||||||
|
/*
|
||||||
|
var el = $0;
|
||||||
|
var height = 0;
|
||||||
|
var checkUp = false;
|
||||||
|
|
||||||
|
do {
|
||||||
|
height += el.scrollHeight;
|
||||||
|
} while(el = (checkUp ? el.previousElementSibling : el.nextElementSibling));
|
||||||
|
console.log(height);
|
||||||
|
*/
|
||||||
|
|
||||||
|
export default class Scrollable {
|
||||||
|
public container: HTMLDivElement;
|
||||||
|
public thumb: HTMLDivElement;
|
||||||
|
|
||||||
|
public type: string;
|
||||||
|
public side: string;
|
||||||
|
public scrollType: string;
|
||||||
|
public scrollSide: string;
|
||||||
|
public clientAxis: string;
|
||||||
|
|
||||||
|
public scrollSize = -1;
|
||||||
|
public size = 0;
|
||||||
|
public thumbSize = 0;
|
||||||
|
|
||||||
|
public hiddenElements: {
|
||||||
|
up: {element: Element, height: number}[],
|
||||||
|
down: {element: Element, height: number}[]
|
||||||
|
} = {
|
||||||
|
up: [],
|
||||||
|
down: []
|
||||||
|
};
|
||||||
|
public paddings = {up: 0, down: 0};
|
||||||
|
|
||||||
|
public paddingTopDiv: HTMLDivElement;
|
||||||
|
public paddingBottomDiv: HTMLDivElement;
|
||||||
|
|
||||||
|
public splitUp: HTMLElement;
|
||||||
|
|
||||||
|
public onAddedBottom: () => void = null;
|
||||||
|
public onScrolledTop: () => void = null;
|
||||||
|
public onScrolledBottom: () => void = null;
|
||||||
|
|
||||||
|
public topObserver: IntersectionObserver;
|
||||||
|
public bottomObserver: IntersectionObserver;
|
||||||
|
|
||||||
|
public splitObserver: IntersectionObserver;
|
||||||
|
public splitMeasure: Promise<{element: Element, height: number}[]> = null;
|
||||||
|
public splitMeasureAdd: Promise<number> = null;
|
||||||
|
public splitMeasureRemoveBad: Promise<Element> = null;
|
||||||
|
public splitMutate: Promise<void> = null;
|
||||||
|
|
||||||
|
constructor(public el: HTMLDivElement, x = false, y = true, public splitOffset = 300) {
|
||||||
|
this.container = document.createElement('div');
|
||||||
|
this.container.classList.add('scrollable');
|
||||||
|
|
||||||
|
let arr = [];
|
||||||
|
for(let i = 0.001; i < 1; i += 0.001) arr.push(i);
|
||||||
|
this.topObserver = new IntersectionObserver(entries => {
|
||||||
|
let entry = entries[entries.length - 1];
|
||||||
|
|
||||||
|
console.log('top intersection:', entries, entry.isIntersecting, entry.intersectionRatio > 0);
|
||||||
|
if(entry.isIntersecting) {
|
||||||
|
//this.onTopIntersection(entry);
|
||||||
|
this.onTopIntersection(entry.intersectionRect.height);
|
||||||
|
|
||||||
|
if(this.onScrolledTop) this.onScrolledTop();
|
||||||
|
}
|
||||||
|
// console.log('top intersection end');
|
||||||
|
}, {threshold: arr, root: this.el});
|
||||||
|
|
||||||
|
this.bottomObserver = new IntersectionObserver(entries => {
|
||||||
|
let entry = entries[entries.length - 1];
|
||||||
|
|
||||||
|
console.log('bottom intersection:', entries, entry, entry.isIntersecting, entry.intersectionRatio > 0);
|
||||||
|
if(entry.isIntersecting) {
|
||||||
|
//this.onBottomIntersection(entry);
|
||||||
|
let scrollTop = entry.boundingClientRect.top;
|
||||||
|
|
||||||
|
console.log('bottom intersection scrollTop', scrollTop);
|
||||||
|
|
||||||
|
if(scrollTop < 0) {
|
||||||
|
|
||||||
|
} //else this.onBottomIntersection(entry.intersectionRect.height);
|
||||||
|
|
||||||
|
//this.onBottomIntersection(entry.boundingClientRect.height);
|
||||||
|
|
||||||
|
if(this.onScrolledBottom) this.onScrolledBottom();
|
||||||
|
}
|
||||||
|
}, {threshold: arr, root: this.el});
|
||||||
|
|
||||||
|
if(x) {
|
||||||
|
this.container.classList.add('scrollable-x');
|
||||||
|
this.type = 'width';
|
||||||
|
this.side = 'left';
|
||||||
|
this.scrollType = 'scrollWidth';
|
||||||
|
this.scrollSide = 'scrollLeft';
|
||||||
|
this.clientAxis = 'clientX';
|
||||||
|
|
||||||
|
let scrollHorizontally = (e: any) => {
|
||||||
|
e = window.event || e;
|
||||||
|
var delta = Math.max(-1, Math.min(1, (e.wheelDelta || -e.detail)));
|
||||||
|
this.container.scrollLeft -= (delta * 20);
|
||||||
|
e.preventDefault();
|
||||||
|
};
|
||||||
|
if(this.container.addEventListener) {
|
||||||
|
// IE9, Chrome, Safari, Opera
|
||||||
|
this.container.addEventListener("mousewheel", scrollHorizontally, false);
|
||||||
|
// Firefox
|
||||||
|
this.container.addEventListener("DOMMouseScroll", scrollHorizontally, false);
|
||||||
|
} else {
|
||||||
|
// IE 6/7/8
|
||||||
|
// @ts-ignore
|
||||||
|
this.container.attachEvent("onmousewheel", scrollHorizontally);
|
||||||
|
}
|
||||||
|
} else if(y) {
|
||||||
|
this.container.classList.add('scrollable-y');
|
||||||
|
this.type = 'height';
|
||||||
|
this.side = 'top';
|
||||||
|
this.scrollType = 'scrollHeight';
|
||||||
|
this.scrollSide = 'scrollTop';
|
||||||
|
this.clientAxis = 'clientY';
|
||||||
|
} 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';
|
||||||
|
|
||||||
|
// mouse scroll
|
||||||
|
let onMouseMove = (e: MouseEvent) => {
|
||||||
|
let rect = this.thumb.getBoundingClientRect();
|
||||||
|
|
||||||
|
let diff: number;
|
||||||
|
// @ts-ignore
|
||||||
|
diff = e[this.clientAxis] - rect[this.side];
|
||||||
|
// @ts-ignore
|
||||||
|
this.container[this.scrollSide] += diff * 0.5;
|
||||||
|
|
||||||
|
// console.log('onMouseMove', e, diff);
|
||||||
|
|
||||||
|
cancelEvent(e);
|
||||||
|
};
|
||||||
|
|
||||||
|
this.thumb.addEventListener('mousedown', () => {
|
||||||
|
window.addEventListener('mousemove', onMouseMove);
|
||||||
|
|
||||||
|
window.addEventListener('mouseup', () => {
|
||||||
|
window.removeEventListener('mousemove', onMouseMove);
|
||||||
|
}, {once: true});
|
||||||
|
});
|
||||||
|
|
||||||
|
//this.container.addEventListener('mouseover', this.resize.bind(this)); // omg
|
||||||
|
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.topObserver.observe(this.paddingTopDiv);
|
||||||
|
this.bottomObserver.observe(this.paddingBottomDiv);
|
||||||
|
|
||||||
|
this.container.addEventListener('scroll', this.onScroll.bind(this));
|
||||||
|
|
||||||
|
Array.from(el.children).forEach(c => this.container.append(c));
|
||||||
|
|
||||||
|
el.append(this.container);
|
||||||
|
this.container.parentElement.append(this.thumb);
|
||||||
|
this.resize();
|
||||||
|
}
|
||||||
|
|
||||||
|
public detachTop(child: Element, needHeight = 0) {
|
||||||
|
if(this.splitMeasure) fastdom.clear(this.splitMeasure);
|
||||||
|
this.splitMeasure = fastdom.measure(() => {
|
||||||
|
let sliced: {element: Element, height: number}[] = [];
|
||||||
|
|
||||||
|
do {
|
||||||
|
if(needHeight > 0) {
|
||||||
|
needHeight -= child.scrollHeight;
|
||||||
|
} else {
|
||||||
|
sliced.push({element: child, height: child.scrollHeight});
|
||||||
|
}
|
||||||
|
} while(child = child.previousElementSibling);
|
||||||
|
return sliced;
|
||||||
|
});
|
||||||
|
|
||||||
|
return this.splitMeasure.then(sliced => {
|
||||||
|
if(this.splitMutate) fastdom.clear(this.splitMutate);
|
||||||
|
|
||||||
|
return this.splitMutate = fastdom.mutate(() => {
|
||||||
|
sliced.forEachReverse((child) => {
|
||||||
|
let {element, height} = child;
|
||||||
|
if(!this.splitUp.contains(element)) return;
|
||||||
|
|
||||||
|
this.paddings.up += height;
|
||||||
|
this.hiddenElements.up.push(child);
|
||||||
|
this.splitUp.removeChild(element);
|
||||||
|
//element.parentElement.removeChild(element);
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log('onscroll sliced up', sliced);
|
||||||
|
|
||||||
|
this.paddingTopDiv.style.height = this.paddings.up + 'px';
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public detachBottom(child: Element, needHeight = 0) {
|
||||||
|
if(this.splitMeasure) fastdom.clear(this.splitMeasure);
|
||||||
|
this.splitMeasure = fastdom.measure(() => {
|
||||||
|
let sliced: {element: Element, height: number}[] = [];
|
||||||
|
|
||||||
|
do {
|
||||||
|
if(needHeight > 0) {
|
||||||
|
needHeight -= child.scrollHeight;
|
||||||
|
} else {
|
||||||
|
sliced.push({element: child, height: child.scrollHeight});
|
||||||
|
}
|
||||||
|
} while(child = child.nextElementSibling);
|
||||||
|
return sliced;
|
||||||
|
});
|
||||||
|
|
||||||
|
return this.splitMeasure.then(sliced => {
|
||||||
|
if(this.splitMutate) fastdom.clear(this.splitMutate);
|
||||||
|
|
||||||
|
return this.splitMutate = fastdom.mutate(() => {
|
||||||
|
sliced.forEachReverse((child) => {
|
||||||
|
let {element, height} = child;
|
||||||
|
if(!this.splitUp.contains(element)) return;
|
||||||
|
|
||||||
|
this.paddings.down += height;
|
||||||
|
this.hiddenElements.down.unshift(child);
|
||||||
|
this.splitUp.removeChild(element);
|
||||||
|
//element.parentElement.removeChild(element);
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log('onscroll sliced down', sliced);
|
||||||
|
|
||||||
|
this.paddingBottomDiv.style.height = this.paddings.down + 'px';
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public splitObserve(entries: IntersectionObserverEntry[]) {
|
||||||
|
let sorted: {
|
||||||
|
intersecting: {
|
||||||
|
top?: IntersectionObserverEntry,
|
||||||
|
bottom?: IntersectionObserverEntry
|
||||||
|
},
|
||||||
|
notIntersecting: {
|
||||||
|
top?: IntersectionObserverEntry,
|
||||||
|
bottom?: IntersectionObserverEntry
|
||||||
|
}
|
||||||
|
} = {
|
||||||
|
intersecting: {},
|
||||||
|
notIntersecting: {}
|
||||||
|
};
|
||||||
|
|
||||||
|
for(let entry of entries) { // there may be duplicates (1st - not intersecting, 2nd - intersecting)
|
||||||
|
//console.log('onscroll entry 1', entry.target, entry.isIntersecting, entry);
|
||||||
|
if(!entry.target.parentElement || !entry.rootBounds) continue;
|
||||||
|
|
||||||
|
if(!entry.isIntersecting) {
|
||||||
|
let isTop = entry.boundingClientRect.top <= 0;
|
||||||
|
let isBottom = entry.rootBounds.height <= entry.boundingClientRect.top;
|
||||||
|
//console.log('onscroll entry notIntersecting', isTop, isBottom, entry.target, entry);
|
||||||
|
|
||||||
|
if(isTop) {
|
||||||
|
sorted.notIntersecting.top = entry;
|
||||||
|
} else if(isBottom && !sorted.notIntersecting.bottom) {
|
||||||
|
sorted.notIntersecting.bottom = entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
//console.log('splitObserver', entry, entry.target, isTop);
|
||||||
|
} else {
|
||||||
|
let isTop = entry.boundingClientRect.top <= entry.rootBounds.top;
|
||||||
|
let isBottom = entry.boundingClientRect.bottom >= entry.rootBounds.bottom;
|
||||||
|
|
||||||
|
if(isTop) {
|
||||||
|
sorted.intersecting.top = entry;
|
||||||
|
} else if(isBottom && !sorted.intersecting.bottom) {
|
||||||
|
sorted.intersecting.bottom = entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if(isTop) {
|
||||||
|
// this.onTopIntersection(entry.boundingClientRect.height);
|
||||||
|
// } else if(isBottom) {
|
||||||
|
// this.onTopIntersection(entry.boundingClientRect.height);
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('splitObserve', entries, sorted);
|
||||||
|
|
||||||
|
let needHeight = this.splitOffset;
|
||||||
|
let isOutOfView: boolean;
|
||||||
|
let entry: IntersectionObserverEntry;
|
||||||
|
if(entry = sorted.notIntersecting.top) { // scrolled bottom
|
||||||
|
let child = entry.target;
|
||||||
|
|
||||||
|
let diff = entry.boundingClientRect.bottom + needHeight;
|
||||||
|
if(diff < 0) { // maybe need <=, means out of view
|
||||||
|
if(!(child = child.nextElementSibling)) {
|
||||||
|
this.detachTop(this.splitUp.lastElementChild, 0);
|
||||||
|
} else {
|
||||||
|
if(this.splitMeasureRemoveBad) fastdom.clear(this.splitMeasureRemoveBad);
|
||||||
|
this.splitMeasureRemoveBad = fastdom.measure(() => {
|
||||||
|
do {
|
||||||
|
diff += child.scrollHeight;
|
||||||
|
} while(diff < 0 && (child = child.nextElementSibling));
|
||||||
|
|
||||||
|
return child || this.splitUp.lastElementChild;
|
||||||
|
});
|
||||||
|
|
||||||
|
this.splitMeasureRemoveBad.then(child => {
|
||||||
|
this.detachTop(child, 0);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.detachTop(child, needHeight);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(entry = sorted.notIntersecting.bottom) { // scrolled top
|
||||||
|
isOutOfView = (entry.boundingClientRect.top - needHeight) >= entry.rootBounds.height;
|
||||||
|
this.detachBottom(entry.target, isOutOfView ? 0 : needHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(entry = sorted.intersecting.top) { // scrolling top
|
||||||
|
let needHeight = this.splitOffset;
|
||||||
|
|
||||||
|
let child = entry.target;
|
||||||
|
if(this.splitMeasureAdd) fastdom.clear(this.splitMeasureAdd);
|
||||||
|
this.splitMeasureAdd = fastdom.measure(() => {
|
||||||
|
while(child = child.previousElementSibling) {
|
||||||
|
needHeight -= child.scrollHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
return needHeight;
|
||||||
|
});
|
||||||
|
|
||||||
|
this.splitMeasureAdd.then(needHeight => {
|
||||||
|
this.onTopIntersection(needHeight);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if(entry = sorted.intersecting.bottom) { // scrolling bottom
|
||||||
|
let needHeight = this.splitOffset;
|
||||||
|
|
||||||
|
let child = entry.target;
|
||||||
|
if(this.splitMeasureAdd) fastdom.clear(this.splitMeasureAdd);
|
||||||
|
this.splitMeasureAdd = fastdom.measure(() => {
|
||||||
|
while(child = child.nextElementSibling) {
|
||||||
|
needHeight -= child.scrollHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
return needHeight;
|
||||||
|
});
|
||||||
|
|
||||||
|
this.splitMeasureAdd.then(needHeight => {
|
||||||
|
this.onBottomIntersection(needHeight);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async resize() {
|
||||||
|
//console.time('scroll resize');
|
||||||
|
|
||||||
|
await fastdom.measure(() => {
|
||||||
|
// @ts-ignore
|
||||||
|
this.scrollSize = this.container[this.scrollType];
|
||||||
|
|
||||||
|
let rect = this.container.getBoundingClientRect();
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
this.size = rect[this.type];
|
||||||
|
});
|
||||||
|
|
||||||
|
await fastdom.mutate(() => {
|
||||||
|
if(!this.size || this.size == this.scrollSize) {
|
||||||
|
this.thumbSize = 0;
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
this.thumb.style[this.type] = this.thumbSize + 'px';
|
||||||
|
//console.timeEnd('scroll resize');
|
||||||
|
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';
|
||||||
|
});
|
||||||
|
|
||||||
|
//console.timeEnd('scroll resize');
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
//console.log('onresize', thumb.style[type], thumbHeight, height);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async setVirtualContainer(el?: HTMLElement) {
|
||||||
|
this.splitUp = el;
|
||||||
|
|
||||||
|
this.hiddenElements.up.length = this.hiddenElements.down.length = 0;
|
||||||
|
this.paddings.up = this.paddings.down = 0;
|
||||||
|
|
||||||
|
if(this.paddingTopDiv.parentElement) {
|
||||||
|
fastdom.mutate(() => {
|
||||||
|
this.paddingTopDiv.style.height = '';
|
||||||
|
this.paddingBottomDiv.style.height = '';
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if(this.splitObserver) {
|
||||||
|
this.splitObserver.disconnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.splitObserver = new IntersectionObserver((entries) => this.splitObserve(entries), {root: this.el});
|
||||||
|
|
||||||
|
if(el) {
|
||||||
|
fastdom.mutate(() => {
|
||||||
|
el.parentElement.insertBefore(this.paddingTopDiv, el);
|
||||||
|
el.parentNode.insertBefore(this.paddingBottomDiv, el.nextSibling);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async onScroll(e?: any) {
|
||||||
|
//console.log('scroll onScroll', e, this.container.scrollTop);
|
||||||
|
let {value, maxValue} = await fastdom.measure(() => {
|
||||||
|
// @ts-ignore
|
||||||
|
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);
|
||||||
|
|
||||||
|
return {value, maxValue};
|
||||||
|
});
|
||||||
|
|
||||||
|
//console.log('onscroll', container.scrollHeight, thumbHeight, height, value, maxValue);
|
||||||
|
fastdom.mutate(() => {
|
||||||
|
// @ts-ignore
|
||||||
|
this.thumb.style[this.side] = (value >= maxValue ? maxValue : value) + '%';
|
||||||
|
});
|
||||||
|
|
||||||
|
//console.timeEnd('scroll onScroll');
|
||||||
|
}
|
||||||
|
|
||||||
|
public async onTopIntersection(/* entry: IntersectionObserverEntry */needHeight: number) {
|
||||||
|
console.log('onTopIntersection', needHeight, this);
|
||||||
|
|
||||||
|
if(this.hiddenElements.up.length && this.paddings.up) {
|
||||||
|
//let needHeight = entry.intersectionRect.height || entry.boundingClientRect.height;
|
||||||
|
//let needHeight = entry.intersectionRect.height || await fastdom.measure(() => this.splitUp.lastElementChild.scrollHeight);
|
||||||
|
|
||||||
|
let fragment = document.createDocumentFragment();
|
||||||
|
while(needHeight > 0 && this.paddings.up) {
|
||||||
|
let child = this.hiddenElements.up.pop();
|
||||||
|
|
||||||
|
// console.log('top returning from hidden', child);
|
||||||
|
|
||||||
|
if(!child) {
|
||||||
|
this.paddings.up = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
fragment.prepend(child.element);
|
||||||
|
|
||||||
|
needHeight -= child.height;
|
||||||
|
this.paddings.up -= child.height;
|
||||||
|
}
|
||||||
|
|
||||||
|
await fastdom.mutate(() => {
|
||||||
|
this.splitUp.prepend(fragment);
|
||||||
|
this.paddingTopDiv.style.height = this.paddings.up + 'px';
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
await fastdom.mutate(() => {
|
||||||
|
this.paddingTopDiv.style.height = '0px';
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async onBottomIntersection(/* entry: IntersectionObserverEntry */needHeight: number) {
|
||||||
|
console.log('onBottomIntersection', needHeight, this);
|
||||||
|
|
||||||
|
if(this.hiddenElements.down.length && this.paddings.down) {
|
||||||
|
//let needHeight = entry.intersectionRect.height || entry.boundingClientRect.height;
|
||||||
|
//let needHeight = entry.intersectionRect.height || await fastdom.measure(() => this.splitUp.firstElementChild.scrollHeight);
|
||||||
|
|
||||||
|
let fragment = document.createDocumentFragment();
|
||||||
|
while(needHeight > 0 && this.paddings.down) {
|
||||||
|
let child = this.hiddenElements.down.shift();
|
||||||
|
|
||||||
|
if(!child) {
|
||||||
|
this.paddings.down = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
fragment.appendChild(child.element);
|
||||||
|
|
||||||
|
needHeight -= child.height;
|
||||||
|
this.paddings.down -= child.height;
|
||||||
|
}
|
||||||
|
|
||||||
|
await fastdom.mutate(() => {
|
||||||
|
this.splitUp.appendChild(fragment);
|
||||||
|
this.paddingBottomDiv.style.height = this.paddings.down + 'px';
|
||||||
|
});
|
||||||
|
if(this.onAddedBottom) this.onAddedBottom();
|
||||||
|
} else {
|
||||||
|
await fastdom.mutate(() => {
|
||||||
|
this.paddingBottomDiv.style.height = '0px';
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public prepend(...smth: (string | Node)[]) {
|
||||||
|
if(this.splitUp) {
|
||||||
|
if(this.hiddenElements.up.length) {
|
||||||
|
smth.forEach(node => {
|
||||||
|
if(typeof(node) !== 'string') {
|
||||||
|
this.hiddenElements.up.push({
|
||||||
|
element: node as Element,
|
||||||
|
height: (node as Element).scrollHeight || 1
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.splitUp.prepend(...smth);
|
||||||
|
}
|
||||||
|
|
||||||
|
for(let node of smth) {
|
||||||
|
if(typeof(node) !== 'string') {
|
||||||
|
this.splitObserver.unobserve(node as Element);
|
||||||
|
this.splitObserver.observe(node as Element);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.container.prepend(...smth);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public append(...smth: (string | Node)[]) {
|
||||||
|
if(this.splitUp) {
|
||||||
|
if(this.hiddenElements.down.length) {
|
||||||
|
smth.forEachReverse(node => {
|
||||||
|
if(typeof(node) !== 'string') {
|
||||||
|
this.hiddenElements.down.unshift({
|
||||||
|
element: node as Element,
|
||||||
|
height: (node as Element).scrollHeight || 1
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.splitUp.append(...smth);
|
||||||
|
}
|
||||||
|
|
||||||
|
for(let node of smth) {
|
||||||
|
if(typeof(node) !== 'string') {
|
||||||
|
this.splitObserver.unobserve(node as Element);
|
||||||
|
this.splitObserver.observe(node as Element);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.container.append(...smth);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public insertBefore(newChild: Element, refChild: Element) {
|
||||||
|
if(this.splitUp) {
|
||||||
|
this.splitObserver.unobserve(newChild);
|
||||||
|
this.splitObserver.observe(newChild);
|
||||||
|
|
||||||
|
let index = -1;
|
||||||
|
index = this.hiddenElements.up.findIndex(c => c.element == refChild);
|
||||||
|
|
||||||
|
// возможно здесь нужно очищать предыдущую высоту если newChild уже скрыт (но может и не нужно)
|
||||||
|
if(index !== -1) {
|
||||||
|
this.hiddenElements.up.splice(index, 0, {element: newChild, height: newChild.scrollHeight || 1});
|
||||||
|
return index;
|
||||||
|
} else {
|
||||||
|
index = this.hiddenElements.down.findIndex(c => c.element == newChild);
|
||||||
|
|
||||||
|
if(index !== -1) {
|
||||||
|
this.hiddenElements.down.splice(index, 0, {element: newChild, height: newChild.scrollHeight || 1});
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.splitUp.insertBefore(newChild, refChild);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.container.insertBefore(newChild, refChild);
|
||||||
|
}
|
||||||
|
|
||||||
|
set scrollTop(y: number) {
|
||||||
|
fastdom.measure(() => {
|
||||||
|
let scrollTop = this.container.scrollTop;
|
||||||
|
if(scrollTop == y) return scrollTop;
|
||||||
|
|
||||||
|
this.container.scrollTop = y;
|
||||||
|
|
||||||
|
if(this.splitUp) {
|
||||||
|
let isBottom = y > scrollTop;
|
||||||
|
let diff = Math.abs(y - scrollTop);
|
||||||
|
|
||||||
|
if(isBottom) {
|
||||||
|
let el = this.splitUp.lastElementChild;
|
||||||
|
let rect = el.getBoundingClientRect();
|
||||||
|
} else {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
get scrollTop() {
|
||||||
|
return this.container.scrollTop;
|
||||||
|
}
|
||||||
|
|
||||||
|
get scrollHeight() {
|
||||||
|
return this.container.scrollHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
get parentElement() {
|
||||||
|
return this.container.parentElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
get offsetHeight() {
|
||||||
|
return this.container.offsetHeight;
|
||||||
|
}
|
||||||
|
}
|
@ -423,24 +423,51 @@ export function wrapPhoto(this: AppImManager, photo: any, message: any, containe
|
|||||||
return this.loadMediaQueue ? this.loadMediaQueuePush(load) : load();
|
return this.loadMediaQueue ? this.loadMediaQueuePush(load) : load();
|
||||||
}
|
}
|
||||||
|
|
||||||
export function wrapSticker(doc: MTDocument, div: HTMLDivElement, middleware?: () => boolean, lazyLoadQueue?: LazyLoadQueue, group?: string, canvas?: boolean, play = false) {
|
export function wrapSticker(doc: MTDocument, div: HTMLDivElement, middleware?: () => boolean, lazyLoadQueue?: LazyLoadQueue, group?: string, canvas?: boolean, play = false, onlyThumb = false) {
|
||||||
let stickerType = doc.mime_type == "application/x-tgsticker" ? 2 : (doc.mime_type == "image/webp" ? 1 : 0);
|
let stickerType = doc.mime_type == "application/x-tgsticker" ? 2 : (doc.mime_type == "image/webp" ? 1 : 0);
|
||||||
|
|
||||||
if(!stickerType) {
|
if(!stickerType) {
|
||||||
console.error('wrong doc for wrapSticker!', doc, div);
|
console.error('wrong doc for wrapSticker!', doc, div);
|
||||||
}
|
}
|
||||||
|
|
||||||
///////console.log('wrap sticker', doc);
|
//////console.log('wrap sticker', doc, onlyThumb);
|
||||||
|
|
||||||
if(doc.thumbs && !div.firstElementChild) {
|
if(doc.thumbs && !div.firstElementChild) {
|
||||||
let thumb = doc.thumbs[0];
|
let thumb = doc.thumbs[0];
|
||||||
|
|
||||||
|
///////console.log('wrap sticker', thumb);
|
||||||
|
|
||||||
if(thumb.bytes) {
|
if(thumb.bytes) {
|
||||||
apiFileManager.saveSmallFile(thumb.location, thumb.bytes);
|
apiFileManager.saveSmallFile(thumb.location, thumb.bytes);
|
||||||
|
|
||||||
appPhotosManager.setAttachmentPreview(thumb.bytes, div, true);
|
appPhotosManager.setAttachmentPreview(thumb.bytes, div, true);
|
||||||
|
|
||||||
|
if(onlyThumb) return Promise.resolve();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(onlyThumb && doc.thumbs) {
|
||||||
|
let thumb = doc.thumbs[0];
|
||||||
|
|
||||||
|
let load = () => apiFileManager.downloadSmallFile({
|
||||||
|
_: 'inputDocumentFileLocation',
|
||||||
|
access_hash: doc.access_hash,
|
||||||
|
file_reference: doc.file_reference,
|
||||||
|
thumb_size: thumb.type,
|
||||||
|
id: doc.id
|
||||||
|
}, {dcID: doc.dc_id}).then(blob => {
|
||||||
|
let img = new Image();
|
||||||
|
|
||||||
|
appWebpManager.polyfillImage(img, blob);
|
||||||
|
|
||||||
|
div.append(img);
|
||||||
|
|
||||||
|
div.setAttribute('file-id', doc.id);
|
||||||
|
appStickersManager.saveSticker(doc);
|
||||||
|
});
|
||||||
|
|
||||||
|
return lazyLoadQueue ? (lazyLoadQueue.push({div, load}), Promise.resolve()) : load();
|
||||||
|
}
|
||||||
|
|
||||||
let load = () => apiFileManager.downloadSmallFile({
|
let load = () => apiFileManager.downloadSmallFile({
|
||||||
_: 'inputDocumentFileLocation',
|
_: 'inputDocumentFileLocation',
|
||||||
|
@ -826,7 +826,7 @@ export class AppImManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public setScroll() {
|
public setScroll() {
|
||||||
this.scrollable = new Scrollable(this.bubblesContainer, false, true, 750/* 1500 */);
|
this.scrollable = new Scrollable(this.bubblesContainer, false, true, 750, 'IM'/* 1500 */);
|
||||||
this.scroll = this.scrollable.container;
|
this.scroll = this.scrollable.container;
|
||||||
|
|
||||||
this.scrollable.setVirtualContainer(this.chatInner);
|
this.scrollable.setVirtualContainer(this.chatInner);
|
||||||
@ -1192,14 +1192,22 @@ export class AppImManager {
|
|||||||
this.scrollPosition.prepareFor(reverse ? 'up' : 'down'); // лагает из-за этого
|
this.scrollPosition.prepareFor(reverse ? 'up' : 'down'); // лагает из-за этого
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let bubbleContainer: HTMLDivElement;
|
||||||
|
|
||||||
// bubble
|
// bubble
|
||||||
if(!bubble) {
|
if(!bubble) {
|
||||||
|
bubbleContainer = document.createElement('div');
|
||||||
|
bubbleContainer.classList.add('bubble__container');
|
||||||
|
|
||||||
bubble = document.createElement('div');
|
bubble = document.createElement('div');
|
||||||
bubble.classList.add('bubble');
|
bubble.classList.add('bubble');
|
||||||
|
bubble.appendChild(bubbleContainer);
|
||||||
this.bubbles[+message.mid] = bubble;
|
this.bubbles[+message.mid] = bubble;
|
||||||
} else {
|
} else {
|
||||||
bubble.className = 'bubble';
|
bubble.className = 'bubble';
|
||||||
bubble.innerHTML = '';
|
bubbleContainer = bubble.firstElementChild as HTMLDivElement;
|
||||||
|
bubbleContainer.innerHTML = '';
|
||||||
|
//bubble.innerHTML = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
// time section
|
// time section
|
||||||
@ -1243,7 +1251,7 @@ export class AppImManager {
|
|||||||
messageDiv.classList.add('message-empty');
|
messageDiv.classList.add('message-empty');
|
||||||
bubble.classList.add('emoji-' + emojiEntities.length + 'x', 'emoji-big');
|
bubble.classList.add('emoji-' + emojiEntities.length + 'x', 'emoji-big');
|
||||||
|
|
||||||
bubble.append(attachmentDiv);
|
bubbleContainer.append(attachmentDiv);
|
||||||
} else {
|
} else {
|
||||||
messageDiv.innerHTML = richText;
|
messageDiv.innerHTML = richText;
|
||||||
}
|
}
|
||||||
@ -1260,7 +1268,7 @@ export class AppImManager {
|
|||||||
|
|
||||||
timeSpan.appendChild(timeInner);
|
timeSpan.appendChild(timeInner);
|
||||||
messageDiv.append(timeSpan);
|
messageDiv.append(timeSpan);
|
||||||
bubble.prepend(messageDiv);
|
bubbleContainer.prepend(messageDiv);
|
||||||
//bubble.prepend(timeSpan, messageDiv); // that's bad
|
//bubble.prepend(timeSpan, messageDiv); // that's bad
|
||||||
|
|
||||||
if(our) {
|
if(our) {
|
||||||
@ -1409,7 +1417,7 @@ export class AppImManager {
|
|||||||
box.append(quote);
|
box.append(quote);
|
||||||
|
|
||||||
//bubble.prepend(box);
|
//bubble.prepend(box);
|
||||||
bubble.prepend(timeSpan, box);
|
bubbleContainer.prepend(timeSpan, box);
|
||||||
|
|
||||||
//this.log('night running', bubble.scrollHeight);
|
//this.log('night running', bubble.scrollHeight);
|
||||||
|
|
||||||
@ -1433,8 +1441,8 @@ export class AppImManager {
|
|||||||
|
|
||||||
appPhotosManager.setAttachmentSize(doc, attachmentDiv, undefined, undefined, true);
|
appPhotosManager.setAttachmentSize(doc, attachmentDiv, undefined, undefined, true);
|
||||||
let preloader = new ProgressivePreloader(attachmentDiv, false);
|
let preloader = new ProgressivePreloader(attachmentDiv, false);
|
||||||
bubble.style.height = attachmentDiv.style.height;
|
bubbleContainer.style.height = attachmentDiv.style.height;
|
||||||
bubble.style.width = attachmentDiv.style.width;
|
bubbleContainer.style.width = attachmentDiv.style.width;
|
||||||
//appPhotosManager.setAttachmentSize(doc, bubble);
|
//appPhotosManager.setAttachmentSize(doc, bubble);
|
||||||
let load = () => wrapSticker(doc, attachmentDiv, () => {
|
let load = () => wrapSticker(doc, attachmentDiv, () => {
|
||||||
if(this.peerID != peerID) {
|
if(this.peerID != peerID) {
|
||||||
@ -1483,7 +1491,7 @@ export class AppImManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(!processingWebPage) {
|
if(!processingWebPage) {
|
||||||
bubble.append(attachmentDiv);
|
bubbleContainer.append(attachmentDiv);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1512,7 +1520,7 @@ export class AppImManager {
|
|||||||
</defs>
|
</defs>
|
||||||
<use xlink:href="#b13RmHDQtl" opacity="1" fill="#fff" fill-opacity="1"></use>
|
<use xlink:href="#b13RmHDQtl" opacity="1" fill="#fff" fill-opacity="1"></use>
|
||||||
</svg>`;
|
</svg>`;
|
||||||
bubble.append(fwd);
|
bubbleContainer.append(fwd);
|
||||||
bubble.dataset.savedFrom = message.savedFrom;
|
bubble.dataset.savedFrom = message.savedFrom;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1522,7 +1530,7 @@ export class AppImManager {
|
|||||||
nameDiv.innerHTML = 'Forwarded from ' + title;
|
nameDiv.innerHTML = 'Forwarded from ' + title;
|
||||||
nameDiv.dataset.peerID = message.fwdFromID;
|
nameDiv.dataset.peerID = message.fwdFromID;
|
||||||
//nameDiv.style.color = appPeersManager.getPeerColorByID(message.fromID, false);
|
//nameDiv.style.color = appPeersManager.getPeerColorByID(message.fromID, false);
|
||||||
bubble.append(nameDiv);
|
bubbleContainer.append(nameDiv);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if(message.reply_to_mid) {
|
if(message.reply_to_mid) {
|
||||||
@ -1546,7 +1554,7 @@ export class AppImManager {
|
|||||||
bubble.setAttribute('data-original-mid', message.reply_to_mid);
|
bubble.setAttribute('data-original-mid', message.reply_to_mid);
|
||||||
}
|
}
|
||||||
|
|
||||||
bubble.append(wrapReply(originalPeerTitle, originalMessage.message || '', originalMessage.media));
|
bubbleContainer.append(wrapReply(originalPeerTitle, originalMessage.message || '', originalMessage.media));
|
||||||
bubble.classList.add('is-reply');
|
bubble.classList.add('is-reply');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1556,7 +1564,7 @@ export class AppImManager {
|
|||||||
nameDiv.innerHTML = title;
|
nameDiv.innerHTML = title;
|
||||||
nameDiv.style.color = appPeersManager.getPeerColorByID(message.fromID, false);
|
nameDiv.style.color = appPeersManager.getPeerColorByID(message.fromID, false);
|
||||||
nameDiv.dataset.peerID = message.fromID;
|
nameDiv.dataset.peerID = message.fromID;
|
||||||
bubble.append(nameDiv);
|
bubbleContainer.append(nameDiv);
|
||||||
} else /* if(!message.reply_to_mid) */ {
|
} else /* if(!message.reply_to_mid) */ {
|
||||||
bubble.classList.add('hide-name');
|
bubble.classList.add('hide-name');
|
||||||
}
|
}
|
||||||
@ -1585,14 +1593,14 @@ export class AppImManager {
|
|||||||
|
|
||||||
avatarDiv.dataset.peerID = message.fromID;
|
avatarDiv.dataset.peerID = message.fromID;
|
||||||
|
|
||||||
bubble.append(avatarDiv);
|
bubbleContainer.append(avatarDiv);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
bubble.classList.add('hide-name');
|
bubble.classList.add('hide-name');
|
||||||
}
|
}
|
||||||
|
|
||||||
if(message._ == 'messageService') {
|
if(message._ == 'messageService') {
|
||||||
bubble.className = 'service';
|
bubble.className = 'bubble service';
|
||||||
|
|
||||||
let action = message.action;
|
let action = message.action;
|
||||||
|
|
||||||
@ -1608,11 +1616,11 @@ export class AppImManager {
|
|||||||
}
|
}
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
let str = (name.innerText ? name.outerHTML + ' ' : '') + langPack[_];
|
let str = (name.innerText ? name.outerHTML + ' ' : '') + langPack[_];
|
||||||
bubble.innerHTML = `<div class="service-msg">${str}</div>`;
|
bubbleContainer.innerHTML = `<div class="service-msg">${str}</div>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bubble.classList.add(our ? 'is-out' : 'is-in');
|
||||||
if(updatePosition) {
|
if(updatePosition) {
|
||||||
bubble.classList.add(our ? 'is-out' : 'is-in');
|
|
||||||
if(reverse) {
|
if(reverse) {
|
||||||
this.scrollable.prepend(bubble);
|
this.scrollable.prepend(bubble);
|
||||||
} else {
|
} else {
|
||||||
@ -1640,7 +1648,7 @@ export class AppImManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let div = document.createElement('div');
|
let div = document.createElement('div');
|
||||||
div.classList.add('service');
|
div.className = 'bubble service';
|
||||||
div.innerHTML = `<div class="service-msg">${str}</div>`;
|
div.innerHTML = `<div class="service-msg">${str}</div>`;
|
||||||
////////this.log('need to render date message', dateTimestamp, str);
|
////////this.log('need to render date message', dateTimestamp, str);
|
||||||
|
|
||||||
@ -1737,8 +1745,8 @@ export class AppImManager {
|
|||||||
if(!isBackLimit) {
|
if(!isBackLimit) {
|
||||||
this.scrollPosition.prepareFor(reverse ? 'up' : 'down');
|
this.scrollPosition.prepareFor(reverse ? 'up' : 'down');
|
||||||
}
|
}
|
||||||
|
|
||||||
history.forEachReverse((msgID: number) => {
|
/* for(let i = 0; i < 25; ++i) */ history.forEachReverse((msgID: number) => {
|
||||||
let message = appMessagesManager.getMessage(msgID);
|
let message = appMessagesManager.getMessage(msgID);
|
||||||
|
|
||||||
this.renderMessage(message, reverse, true);
|
this.renderMessage(message, reverse, true);
|
||||||
|
@ -139,7 +139,7 @@ export class AppPhotosManager {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public setAttachmentPreview(bytes: Uint8Array, div: HTMLDivElement, isSticker = false, background = false) {
|
public setAttachmentPreview(bytes: Uint8Array, div: HTMLElement, isSticker = false, background = false) {
|
||||||
//image.src = "data:image/jpeg;base64," + bytesToBase64(photo.sizes[0].bytes);
|
//image.src = "data:image/jpeg;base64," + bytesToBase64(photo.sizes[0].bytes);
|
||||||
//photo.sizes[0].bytes = new Uint8Array([...photo.sizes[0].bytes].reverse());
|
//photo.sizes[0].bytes = new Uint8Array([...photo.sizes[0].bytes].reverse());
|
||||||
|
|
||||||
|
@ -9,6 +9,8 @@ import appImManager from "./appImManager";
|
|||||||
import appUsersManager from "./appUsersManager";
|
import appUsersManager from "./appUsersManager";
|
||||||
import { appPeersManager } from "../services";
|
import { appPeersManager } from "../services";
|
||||||
|
|
||||||
|
let testScroll = false;
|
||||||
|
|
||||||
class SearchGroup {
|
class SearchGroup {
|
||||||
container: HTMLDivElement;
|
container: HTMLDivElement;
|
||||||
nameEl: HTMLDivElement;
|
nameEl: HTMLDivElement;
|
||||||
@ -57,7 +59,7 @@ class AppSidebarLeft {
|
|||||||
private chatsArchivedOffsetIndex = 0;
|
private chatsArchivedOffsetIndex = 0;
|
||||||
private chatsOffsetIndex = 0;
|
private chatsOffsetIndex = 0;
|
||||||
private chatsPreloader: HTMLDivElement;
|
private chatsPreloader: HTMLDivElement;
|
||||||
private chatsLoadCount = 0;
|
//private chatsLoadCount = 0;
|
||||||
//private loadDialogsPromise: Promise<any>;
|
//private loadDialogsPromise: Promise<any>;
|
||||||
private loadDialogsPromise: ReturnType<AppMessagesManager["getConversations"]>;
|
private loadDialogsPromise: ReturnType<AppMessagesManager["getConversations"]>;
|
||||||
|
|
||||||
@ -90,15 +92,15 @@ class AppSidebarLeft {
|
|||||||
putPreloader(this.chatsPreloader);
|
putPreloader(this.chatsPreloader);
|
||||||
//this.chatsContainer.append(this.chatsPreloader);
|
//this.chatsContainer.append(this.chatsPreloader);
|
||||||
|
|
||||||
this.chatsLoadCount = Math.round(document.body.scrollHeight / 70 * 1.5);
|
//this.chatsLoadCount = Math.round(document.body.scrollHeight / 70 * 1.5);
|
||||||
|
|
||||||
this.scroll = new Scrollable(this.chatsContainer as HTMLDivElement);
|
this.scroll = new Scrollable(this.chatsContainer as HTMLDivElement, false, true, 300, 'CL');
|
||||||
this.scroll.setVirtualContainer(appDialogsManager.chatList);
|
this.scroll.setVirtualContainer(appDialogsManager.chatList);
|
||||||
this.scroll.onScrolledBottom = this.onChatsScroll.bind(this);
|
this.scroll.onScrolledBottom = this.onChatsScroll.bind(this);
|
||||||
appDialogsManager.chatsHidden = this.scroll.hiddenElements;
|
appDialogsManager.chatsHidden = this.scroll.hiddenElements;
|
||||||
//this.scroll.container.addEventListener('scroll', this.onChatsScroll.bind(this));
|
//this.scroll.container.addEventListener('scroll', this.onChatsScroll.bind(this));
|
||||||
|
|
||||||
this.scrollArchived = new Scrollable(this.chatsArchivedContainer as HTMLDivElement);
|
this.scrollArchived = new Scrollable(this.chatsArchivedContainer as HTMLDivElement, false, true, 300, 'CLA');
|
||||||
this.scrollArchived.setVirtualContainer(appDialogsManager.chatListArchived);
|
this.scrollArchived.setVirtualContainer(appDialogsManager.chatListArchived);
|
||||||
appDialogsManager.chatsArchivedHidden = this.scrollArchived.hiddenElements;
|
appDialogsManager.chatsArchivedHidden = this.scrollArchived.hiddenElements;
|
||||||
//this.scrollArchived.container.addEventListener('scroll', this.onChatsArchivedScroll.bind(this));
|
//this.scrollArchived.container.addEventListener('scroll', this.onChatsArchivedScroll.bind(this));
|
||||||
@ -128,12 +130,14 @@ class AppSidebarLeft {
|
|||||||
//this.toolsBtn.classList.add('tgico-back');
|
//this.toolsBtn.classList.add('tgico-back');
|
||||||
});
|
});
|
||||||
|
|
||||||
/* for(let i = 0; i < 100; ++i) {
|
if(testScroll) {
|
||||||
let li = document.createElement('li');
|
for(let i = 0; i < 1000; ++i) {
|
||||||
li.dataset.id = '' + i;
|
let li = document.createElement('li');
|
||||||
li.innerHTML = `<div class="rp"><div class="user-avatar" style="background-color: rgb(166, 149, 231); font-size: 0px;"><img src="blob:https://localhost:9000/ce99a2a3-f34b-4ca1-a09e-f716f89930d8"></div><div class="user-caption"><p><span class="user-title">${i}</span><span><span class="message-status"></span><span class="message-time">18:33</span></span></p><p><span class="user-last-message"><b>Ильяс: </b>Гагагагга</span><span></span></p></div></div>`;
|
li.dataset.id = '' + i;
|
||||||
this.scroll.append(li);
|
li.innerHTML = `<div class="rp"><div class="user-avatar" style="background-color: rgb(166, 149, 231); font-size: 0px;"><img src="blob:https://localhost:9000/ce99a2a3-f34b-4ca1-a09e-f716f89930d8"></div><div class="user-caption"><p><span class="user-title">${i}</span><span><span class="message-status"></span><span class="message-time">18:33</span></span></p><p><span class="user-last-message"><b>Ильяс: </b>Гагагагга</span><span></span></p></div></div>`;
|
||||||
} */
|
this.scroll.append(li);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
this.listsContainer.addEventListener('scroll', this.onSidebarScroll.bind(this));
|
this.listsContainer.addEventListener('scroll', this.onSidebarScroll.bind(this));
|
||||||
|
|
||||||
@ -196,7 +200,7 @@ class AppSidebarLeft {
|
|||||||
});
|
});
|
||||||
|
|
||||||
window.addEventListener('resize', () => {
|
window.addEventListener('resize', () => {
|
||||||
this.chatsLoadCount = Math.round(document.body.scrollHeight / 70 * 1.5);
|
//this.chatsLoadCount = Math.round(document.body.scrollHeight / 70 * 1.5);
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.onSidebarScroll();
|
this.onSidebarScroll();
|
||||||
@ -212,7 +216,9 @@ class AppSidebarLeft {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async loadDialogs(archived = false) {
|
public async loadDialogs(archived = false) {
|
||||||
//return;
|
if(testScroll) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if(this.loadDialogsPromise/* || 1 == 1 */) return this.loadDialogsPromise;
|
if(this.loadDialogsPromise/* || 1 == 1 */) return this.loadDialogsPromise;
|
||||||
|
|
||||||
@ -225,9 +231,12 @@ class AppSidebarLeft {
|
|||||||
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
this.loadDialogsPromise = appMessagesManager.getConversations('', offset, this.chatsLoadCount, +archived);
|
console.time('getDialogs time');
|
||||||
|
this.loadDialogsPromise = appMessagesManager.getConversations('', offset, 50/*this.chatsLoadCount */, +archived);
|
||||||
|
|
||||||
let result = await this.loadDialogsPromise;
|
let result = await this.loadDialogsPromise;
|
||||||
|
|
||||||
|
console.timeEnd('getDialogs time');
|
||||||
|
|
||||||
if(result && result.dialogs && result.dialogs.length) {
|
if(result && result.dialogs && result.dialogs.length) {
|
||||||
let index = result.dialogs[result.dialogs.length - 1].index;
|
let index = result.dialogs[result.dialogs.length - 1].index;
|
||||||
@ -245,7 +254,7 @@ class AppSidebarLeft {
|
|||||||
this.archivedCount.innerText = '' + count;
|
this.archivedCount.innerText = '' + count;
|
||||||
} */
|
} */
|
||||||
|
|
||||||
/////this.log('loaded ' + this.chatsLoadCount + ' dialogs by offset:', offset, result, this.scroll.hiddenElements);
|
//this.log('getDialogs ' + this.chatsLoadCount + ' dialogs by offset:', offset, result, this.scroll.hiddenElements);
|
||||||
this.scroll.onScroll();
|
this.scroll.onScroll();
|
||||||
} catch(err) {
|
} catch(err) {
|
||||||
this.log.error(err);
|
this.log.error(err);
|
||||||
|
@ -13,6 +13,8 @@ import appMediaViewer from "./appMediaViewer";
|
|||||||
import LazyLoadQueue from "../../components/lazyLoadQueue";
|
import LazyLoadQueue from "../../components/lazyLoadQueue";
|
||||||
import { wrapDocument, wrapAudio } from "../../components/wrappers";
|
import { wrapDocument, wrapAudio } from "../../components/wrappers";
|
||||||
|
|
||||||
|
const testScroll = false;
|
||||||
|
|
||||||
class AppSidebarRight {
|
class AppSidebarRight {
|
||||||
public sidebarEl = document.querySelector('.profile-container') as HTMLDivElement;
|
public sidebarEl = document.querySelector('.profile-container') as HTMLDivElement;
|
||||||
public profileContentEl = document.querySelector('.profile-content') as HTMLDivElement;
|
public profileContentEl = document.querySelector('.profile-content') as HTMLDivElement;
|
||||||
@ -77,7 +79,7 @@ class AppSidebarRight {
|
|||||||
hiddenElements: any,
|
hiddenElements: any,
|
||||||
paddings: any
|
paddings: any
|
||||||
}
|
}
|
||||||
}
|
} = {};
|
||||||
|
|
||||||
private profileTabs: HTMLUListElement;
|
private profileTabs: HTMLUListElement;
|
||||||
private prevTabID = -1;
|
private prevTabID = -1;
|
||||||
@ -90,7 +92,7 @@ class AppSidebarRight {
|
|||||||
let container = this.profileContentEl.querySelector('.profile-tabs-content') as HTMLDivElement;
|
let container = this.profileContentEl.querySelector('.profile-tabs-content') as HTMLDivElement;
|
||||||
this.profileTabs = this.profileContentEl.querySelector('.profile-tabs') as HTMLUListElement;
|
this.profileTabs = this.profileContentEl.querySelector('.profile-tabs') as HTMLUListElement;
|
||||||
|
|
||||||
this.sidebarScroll = new Scrollable(this.sidebarEl);
|
this.sidebarScroll = new Scrollable(this.sidebarEl, false, true, 500, 'SR');
|
||||||
this.sidebarScroll.container.addEventListener('scroll', this.onSidebarScroll.bind(this));
|
this.sidebarScroll.container.addEventListener('scroll', this.onSidebarScroll.bind(this));
|
||||||
|
|
||||||
horizontalMenu(this.profileTabs, container, (id, tabContent) => {
|
horizontalMenu(this.profileTabs, container, (id, tabContent) => {
|
||||||
@ -160,6 +162,23 @@ class AppSidebarRight {
|
|||||||
this.onSidebarScroll();
|
this.onSidebarScroll();
|
||||||
}, 0);
|
}, 0);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if(testScroll) {
|
||||||
|
let div = document.createElement('div');
|
||||||
|
for(let i = 0; i < 500; ++i) {
|
||||||
|
//div.insertAdjacentHTML('beforeend', `<div message-id="0" style="background-image: url(assets/img/camomile.jpg);"></div>`);
|
||||||
|
div.insertAdjacentHTML('beforeend', `<div data-id="${i / 3 | 0}">${i / 3 | 0}</div>`);
|
||||||
|
|
||||||
|
if((i + 1) % 3 == 0) {
|
||||||
|
this.sharedMedia.contentMedia.append(div);
|
||||||
|
div = document.createElement('div');
|
||||||
|
}
|
||||||
|
|
||||||
|
div.dataset.id = '' + (i / 3 | 0);
|
||||||
|
}
|
||||||
|
this.sharedMedia.contentMedia.append(div);
|
||||||
|
(this.profileTabs.children[1] as HTMLLIElement).click(); // set media
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public onSidebarScroll() {
|
public onSidebarScroll() {
|
||||||
@ -196,6 +215,10 @@ class AppSidebarRight {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public loadSidebarMedia(single = false) {
|
public loadSidebarMedia(single = false) {
|
||||||
|
if(testScroll) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
let peerID = this.peerID;
|
let peerID = this.peerID;
|
||||||
|
|
||||||
let typesToLoad = single ? [this.sharedMediaType] : this.sharedMediaTypes;
|
let typesToLoad = single ? [this.sharedMediaType] : this.sharedMediaTypes;
|
||||||
@ -467,6 +490,21 @@ class AppSidebarRight {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(peerID != appImManager.myID) {
|
||||||
|
let dialog: any = appMessagesManager.getDialogByPeerID(peerID);
|
||||||
|
if(dialog.length) {
|
||||||
|
dialog = dialog[0];
|
||||||
|
let muted = false;
|
||||||
|
if(dialog.notify_settings && dialog.notify_settings.mute_until) {
|
||||||
|
muted = new Date(dialog.notify_settings.mute_until * 1000) > new Date();
|
||||||
|
}
|
||||||
|
|
||||||
|
appImManager.setMutedState(muted);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.profileElements.notificationsRow.style.display = 'none';
|
||||||
|
}
|
||||||
|
|
||||||
if(peerID > 0) {
|
if(peerID > 0) {
|
||||||
let user = appUsersManager.getUser(peerID);
|
let user = appUsersManager.getUser(peerID);
|
||||||
if(user.phone && peerID != appImManager.myID) {
|
if(user.phone && peerID != appImManager.myID) {
|
||||||
@ -489,6 +527,8 @@ class AppSidebarRight {
|
|||||||
appImManager.pinnedMsgID = userFull.pinned_msg_id;
|
appImManager.pinnedMsgID = userFull.pinned_msg_id;
|
||||||
appMessagesManager.wrapSingleMessage(userFull.pinned_msg_id);
|
appMessagesManager.wrapSingleMessage(userFull.pinned_msg_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.sidebarScroll.getScrollTopOffset();
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
let chat = appPeersManager.getPeer(peerID);
|
let chat = appPeersManager.getPeer(peerID);
|
||||||
@ -504,24 +544,12 @@ class AppSidebarRight {
|
|||||||
if(chatFull.about) {
|
if(chatFull.about) {
|
||||||
setText(RichTextProcessor.wrapRichText(chatFull.about), this.profileElements.bio);
|
setText(RichTextProcessor.wrapRichText(chatFull.about), this.profileElements.bio);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.sidebarScroll.getScrollTopOffset();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if(peerID != appImManager.myID) {
|
this.sidebarScroll.getScrollTopOffset();
|
||||||
let dialog: any = appMessagesManager.getDialogByPeerID(peerID);
|
|
||||||
if(dialog.length) {
|
|
||||||
dialog = dialog[0];
|
|
||||||
let muted = false;
|
|
||||||
if(dialog.notify_settings && dialog.notify_settings.mute_until) {
|
|
||||||
muted = new Date(dialog.notify_settings.mute_until * 1000) > new Date();
|
|
||||||
}
|
|
||||||
|
|
||||||
appImManager.setMutedState(muted);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this.profileElements.notificationsRow.style.display = 'none';
|
|
||||||
}
|
|
||||||
|
|
||||||
//this.loadSidebarMedia();
|
//this.loadSidebarMedia();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -136,7 +136,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
#bubbles-inner {
|
#bubbles-inner {
|
||||||
max-width: 700px;
|
//max-width: 700px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
@ -148,7 +148,7 @@
|
|||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
|
|
||||||
&.is-chat {
|
&.is-chat {
|
||||||
.is-in {
|
.is-in .bubble__container {
|
||||||
margin-left: 45px;
|
margin-left: 45px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -180,7 +180,6 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.service {
|
.service {
|
||||||
margin: 1rem 0;
|
|
||||||
align-self: center;
|
align-self: center;
|
||||||
|
|
||||||
.service-msg {
|
.service-msg {
|
||||||
@ -203,16 +202,33 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.bubble {
|
.bubble {
|
||||||
min-width: 60px;
|
padding-top: 5px;
|
||||||
max-width: 85%;
|
display: grid;
|
||||||
border-radius: 12px;
|
grid-template-columns: 1fr 700px 1fr;
|
||||||
box-shadow: 0 1px 2px 0 rgba(16, 35, 47, 0.15);
|
grid-row-gap: 0px;
|
||||||
position: relative;
|
|
||||||
display: flex;
|
&:before, &:after {
|
||||||
flex-direction: column-reverse;
|
content: " ";
|
||||||
font-size: 0;
|
width: 100%;
|
||||||
width: max-content;
|
}
|
||||||
height: fit-content;
|
|
||||||
|
&__container {
|
||||||
|
min-width: 60px;
|
||||||
|
max-width: 85%;
|
||||||
|
border-radius: 12px;
|
||||||
|
box-shadow: 0 1px 2px 0 rgba(16, 35, 47, 0.15);
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column-reverse;
|
||||||
|
/* font-size: 0; */
|
||||||
|
width: max-content;
|
||||||
|
height: fit-content;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.service {
|
||||||
|
display: block;
|
||||||
|
padding: 1rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
&.forwarded {
|
&.forwarded {
|
||||||
.forward {
|
.forward {
|
||||||
@ -261,7 +277,9 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
&.photo, &.video {
|
&.photo, &.video {
|
||||||
width: min-content;
|
.bubble__container {
|
||||||
|
width: min-content;
|
||||||
|
}
|
||||||
|
|
||||||
.box.web {
|
.box.web {
|
||||||
/* width: max-content; */ // commented 10.02.2020
|
/* width: max-content; */ // commented 10.02.2020
|
||||||
@ -292,11 +310,14 @@
|
|||||||
|
|
||||||
&.emoji-big {
|
&.emoji-big {
|
||||||
font-size: 0;
|
font-size: 0;
|
||||||
background: none!important;
|
|
||||||
box-shadow: none;
|
.bubble__container {
|
||||||
line-height: 1;
|
background: none!important;
|
||||||
user-select: none;
|
box-shadow: none;
|
||||||
-webkit-user-select: none;
|
line-height: 1;
|
||||||
|
user-select: none;
|
||||||
|
-webkit-user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
.attachment {
|
.attachment {
|
||||||
padding-top: .5rem;
|
padding-top: .5rem;
|
||||||
@ -345,11 +366,13 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
&.sticker, &.round {
|
&.sticker, &.round {
|
||||||
cursor: pointer;
|
.bubble__container {
|
||||||
background: none!important;
|
cursor: pointer;
|
||||||
box-shadow: none;
|
background: none!important;
|
||||||
max-width: 300px;
|
box-shadow: none;
|
||||||
max-height: 300px;
|
max-width: 300px;
|
||||||
|
max-height: 300px;
|
||||||
|
}
|
||||||
|
|
||||||
img {
|
img {
|
||||||
object-fit: contain;
|
object-fit: contain;
|
||||||
@ -672,7 +695,7 @@
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
> .name {
|
&__container > .name {
|
||||||
/* padding: .2675rem .6rem 0 .6rem; */
|
/* padding: .2675rem .6rem 0 .6rem; */
|
||||||
padding: .32rem .6rem 0 .6rem;
|
padding: .32rem .6rem 0 .6rem;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
@ -709,7 +732,7 @@
|
|||||||
margin-top: 6px;
|
margin-top: 6px;
|
||||||
}
|
}
|
||||||
|
|
||||||
&:not(.sticker):not(.emoji-big):not(.round):last-child:after {
|
&:not(.sticker):not(.emoji-big):not(.round):last-child .bubble__container:after {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: -1px;
|
bottom: -1px;
|
||||||
width: 11px;
|
width: 11px;
|
||||||
@ -720,9 +743,9 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.bubble + .bubble {
|
/* .bubble + .bubble {
|
||||||
margin-top: 5px;
|
margin-top: 5px;
|
||||||
}
|
} */
|
||||||
|
|
||||||
.in,
|
.in,
|
||||||
.out {
|
.out {
|
||||||
@ -732,36 +755,37 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.is-in {
|
.is-in {
|
||||||
align-self: flex-start;
|
.bubble__container {
|
||||||
|
margin-right: auto;
|
||||||
background-color: #ffffff;
|
background-color: #ffffff;
|
||||||
border-radius: 6px 12px 12px 6px;
|
border-radius: 6px 12px 12px 6px;
|
||||||
|
}
|
||||||
|
|
||||||
&:first-child {
|
&:first-child .bubble__container {
|
||||||
border-radius: 12px 12px 12px 6px;
|
border-radius: 12px 12px 12px 6px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&:last-child .bubble__container {
|
||||||
|
border-radius: 6px 12px 12px 0px;
|
||||||
|
//border-radius: 12px 12px 12px 0px;
|
||||||
|
|
||||||
&:last-child {
|
&:after {
|
||||||
border-radius: 6px 12px 12px 0px;
|
left: -8.4px;
|
||||||
//border-radius: 12px 12px 12px 0px;
|
background-image: url('../../assets/img/msg-tail-left.svg');
|
||||||
|
|
||||||
&:after {
|
|
||||||
left: -8.4px;
|
|
||||||
background-image: url('../../assets/img/msg-tail-left.svg');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
&:first-child:last-child {
|
&:first-child:last-child .bubble__container {
|
||||||
border-radius: 12px 12px 12px 0px;
|
border-radius: 12px 12px 12px 0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.forwarded .attachment,
|
&.forwarded .attachment,
|
||||||
&.is-reply .attachment,
|
&.is-reply .attachment,
|
||||||
&:not(.hide-name) .message-empty + .attachment/* ,
|
&:not(.hide-name) .message-empty + .attachment/* ,
|
||||||
&:not(.hide-name):not(.sticker) .attachment */ {
|
&:not(.hide-name):not(.sticker) .attachment */ {
|
||||||
border-top-left-radius: 0;
|
border-top-left-radius: 0;
|
||||||
border-top-right-radius: 0;
|
border-top-right-radius: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
a {
|
a {
|
||||||
color: $darkblue;
|
color: $darkblue;
|
||||||
@ -773,7 +797,7 @@
|
|||||||
|
|
||||||
&.is-reply {
|
&.is-reply {
|
||||||
&.emoji-big, &.sticker {
|
&.emoji-big, &.sticker {
|
||||||
.box, .reply {
|
.reply {
|
||||||
left: calc(100% + 10px);
|
left: calc(100% + 10px);
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
}
|
}
|
||||||
@ -805,33 +829,34 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.is-out {
|
.is-out {
|
||||||
align-self: flex-end;
|
.bubble__container {
|
||||||
|
margin-left: auto;
|
||||||
background-color: #eeffde;
|
background-color: #eeffde;
|
||||||
border-radius: 12px 6px 6px 12px;
|
border-radius: 12px 6px 6px 12px;
|
||||||
|
}
|
||||||
|
|
||||||
&:first-child {
|
&:first-child .bubble__container {
|
||||||
border-radius: 12px 12px 6px 12px;
|
border-radius: 12px 12px 6px 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
&:last-child {
|
&:last-child .bubble__container {
|
||||||
border-radius: 12px 6px 0px 12px;
|
border-radius: 12px 6px 0px 12px;
|
||||||
|
|
||||||
&:after {
|
&:after {
|
||||||
right: -8.4px;
|
right: -8.4px;
|
||||||
background-image: url('../../assets/img/msg-tail-right.svg');
|
background-image: url('../../assets/img/msg-tail-right.svg');
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
&:first-child:last-child {
|
&:first-child:last-child .bubble__container {
|
||||||
border-radius: 12px 12px 0px 12px;
|
border-radius: 12px 12px 0px 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.forwarded .attachment,
|
&.forwarded .attachment,
|
||||||
&.is-reply .attachment {
|
&.is-reply .attachment {
|
||||||
border-top-left-radius: 0;
|
border-top-left-radius: 0;
|
||||||
border-top-right-radius: 0;
|
border-top-right-radius: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.quote:hover, .reply:hover {
|
.quote:hover, .reply:hover {
|
||||||
background-color: rgba($green, 0.12);
|
background-color: rgba($green, 0.12);
|
||||||
|
@ -172,7 +172,7 @@
|
|||||||
padding: 0;
|
padding: 0;
|
||||||
} */
|
} */
|
||||||
|
|
||||||
.emoji-padding {
|
.emoji-padding, .stickers-padding {
|
||||||
.menu-horizontal > li {
|
.menu-horizontal > li {
|
||||||
font-size: 1.65rem;
|
font-size: 1.65rem;
|
||||||
}
|
}
|
||||||
|
@ -130,6 +130,7 @@
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
padding-top: 4px;
|
||||||
|
|
||||||
> div {
|
> div {
|
||||||
display: grid;
|
display: grid;
|
||||||
@ -137,10 +138,7 @@
|
|||||||
grid-auto-rows: max-content;
|
grid-auto-rows: max-content;
|
||||||
grid-gap: 3.5px;
|
grid-gap: 3.5px;
|
||||||
place-items: start;
|
place-items: start;
|
||||||
|
padding-top: 3.5px;
|
||||||
& + div {
|
|
||||||
margin-top: 3.5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
> div {
|
> div {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@ -149,6 +147,11 @@
|
|||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
background-size: cover;
|
background-size: cover;
|
||||||
background-position: center center;
|
background-position: center center;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
background-color: #cecece;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
&::before {
|
&::before {
|
||||||
content: "";
|
content: "";
|
||||||
|
Loading…
x
Reference in New Issue
Block a user