333 lines
9.9 KiB
TypeScript
333 lines
9.9 KiB
TypeScript
import LazyLoadQueue from "../lazyLoadQueue";
|
|
import GifsTab from "./tabs/gifs";
|
|
import { touchSupport } from "../../lib/config";
|
|
import { findUpClassName, findUpTag, whichChild } from "../../lib/utils";
|
|
import { horizontalMenu } from "../horizontalMenu";
|
|
import animationIntersector from "../animationIntersector";
|
|
import appSidebarRight from "../../lib/appManagers/appSidebarRight";
|
|
import appImManager from "../../lib/appManagers/appImManager";
|
|
import Scrollable, { ScrollableX } from "../scrollable_new";
|
|
import EmojiTab from "./tabs/emoji";
|
|
import StickersTab from "./tabs/stickers";
|
|
import StickyIntersector from "../stickyIntersector";
|
|
|
|
export const EMOTICONSSTICKERGROUP = 'emoticons-dropdown';
|
|
|
|
export interface EmoticonsTab {
|
|
init: () => void,
|
|
onCloseAfterTimeout?: () => void
|
|
}
|
|
|
|
const test = false;
|
|
|
|
export class EmoticonsDropdown {
|
|
public static lazyLoadQueue = new LazyLoadQueue();
|
|
private element: HTMLElement;
|
|
|
|
public emojiTab: EmojiTab;
|
|
public stickersTab: StickersTab;
|
|
public gifsTab: GifsTab;
|
|
|
|
private container: HTMLElement;
|
|
private tabsEl: HTMLElement;
|
|
private tabID = -1;
|
|
|
|
private tabs: {[id: number]: EmoticonsTab};
|
|
|
|
public searchButton: HTMLElement;
|
|
public deleteBtn: HTMLElement;
|
|
|
|
public toggleEl: HTMLElement;
|
|
private displayTimeout: number;
|
|
|
|
public events: {
|
|
onClose: Array<() => void>,
|
|
onCloseAfter: Array<() => void>,
|
|
onOpen: Array<() => void>,
|
|
onOpenAfter: Array<() => void>
|
|
} = {
|
|
onClose: [],
|
|
onCloseAfter: [],
|
|
onOpen: [],
|
|
onOpenAfter: []
|
|
};
|
|
|
|
constructor() {
|
|
this.element = document.getElementById('emoji-dropdown') as HTMLDivElement;
|
|
|
|
let firstTime = true;
|
|
this.toggleEl = document.getElementById('toggle-emoticons');
|
|
if(touchSupport) {
|
|
this.toggleEl.addEventListener('click', () => {
|
|
if(firstTime) {
|
|
firstTime = false;
|
|
this.toggle(true);
|
|
} else {
|
|
this.toggle();
|
|
}
|
|
});
|
|
} else {
|
|
this.toggleEl.onmouseover = (e) => {
|
|
clearTimeout(this.displayTimeout);
|
|
//this.displayTimeout = setTimeout(() => {
|
|
if(firstTime) {
|
|
this.toggleEl.onmouseout = this.element.onmouseout = (e) => {
|
|
if(test) return;
|
|
if(!this.element.classList.contains('active')) return;
|
|
|
|
const toElement = (e as any).toElement as Element;
|
|
if(toElement && findUpClassName(toElement, 'emoji-dropdown')) {
|
|
return;
|
|
}
|
|
|
|
clearTimeout(this.displayTimeout);
|
|
this.displayTimeout = window.setTimeout(() => {
|
|
this.toggle(false);
|
|
}, 200);
|
|
};
|
|
|
|
this.element.onmouseover = (e) => {
|
|
clearTimeout(this.displayTimeout);
|
|
};
|
|
|
|
firstTime = false;
|
|
}
|
|
|
|
this.toggle(true);
|
|
//}, 0/* 200 */);
|
|
};
|
|
}
|
|
}
|
|
|
|
private init() {
|
|
this.emojiTab = new EmojiTab();
|
|
this.stickersTab = new StickersTab();
|
|
this.gifsTab = new GifsTab();
|
|
|
|
this.tabs = {
|
|
0: this.emojiTab,
|
|
1: this.stickersTab,
|
|
2: this.gifsTab
|
|
};
|
|
|
|
this.container = this.element.querySelector('.emoji-container .tabs-container') as HTMLDivElement;
|
|
this.tabsEl = this.element.querySelector('.emoji-tabs') as HTMLUListElement;
|
|
horizontalMenu(this.tabsEl, this.container, (id) => {
|
|
animationIntersector.checkAnimations(true, EMOTICONSSTICKERGROUP);
|
|
|
|
this.tabID = id;
|
|
this.searchButton.classList.toggle('hide', this.tabID == 0);
|
|
this.deleteBtn.classList.toggle('hide', this.tabID != 0);
|
|
}, () => {
|
|
const tab = this.tabs[this.tabID];
|
|
if(tab.init) {
|
|
tab.init();
|
|
}
|
|
|
|
tab.onCloseAfterTimeout && tab.onCloseAfterTimeout();
|
|
animationIntersector.checkAnimations(false, EMOTICONSSTICKERGROUP);
|
|
});
|
|
|
|
this.searchButton = this.element.querySelector('.emoji-tabs-search');
|
|
this.searchButton.addEventListener('click', () => {
|
|
if(this.tabID == 1) {
|
|
appSidebarRight.stickersTab.init();
|
|
} else {
|
|
appSidebarRight.gifsTab.init();
|
|
}
|
|
});
|
|
|
|
this.deleteBtn = this.element.querySelector('.emoji-tabs-delete');
|
|
this.deleteBtn.addEventListener('click', () => {
|
|
const input = appImManager.chatInputC.messageInput;
|
|
if((input.lastChild as any)?.tagName) {
|
|
input.lastElementChild.remove();
|
|
} else if(input.lastChild) {
|
|
if(!input.lastChild.textContent.length) {
|
|
input.lastChild.remove();
|
|
} else {
|
|
input.lastChild.textContent = input.lastChild.textContent.slice(0, -1);
|
|
}
|
|
}
|
|
|
|
const event = new Event('input', {bubbles: true, cancelable: true});
|
|
appImManager.chatInputC.messageInput.dispatchEvent(event);
|
|
//appSidebarRight.stickersTab.init();
|
|
});
|
|
|
|
(this.tabsEl.firstElementChild.children[1] as HTMLLIElement).click(); // set emoji tab
|
|
this.tabs[0].init(); // onTransitionEnd не вызовется, т.к. это первая открытая вкладка
|
|
}
|
|
|
|
public toggle = async(enable?: boolean) => {
|
|
//if(!this.element) return;
|
|
const willBeActive = (!!this.element.style.display && enable === undefined) || enable;
|
|
if(this.init) {
|
|
if(willBeActive) {
|
|
this.init();
|
|
this.init = null;
|
|
} else {
|
|
return;
|
|
}
|
|
}
|
|
|
|
if(touchSupport) {
|
|
this.toggleEl.classList.toggle('flip-icon', willBeActive);
|
|
if(willBeActive) {
|
|
appImManager.chatInputC.saveScroll();
|
|
// @ts-ignore
|
|
document.activeElement.blur();
|
|
await new Promise((resolve) => {
|
|
setTimeout(resolve, 100);
|
|
});
|
|
}
|
|
} else {
|
|
this.toggleEl.classList.toggle('active', enable);
|
|
}
|
|
|
|
if((this.element.style.display && enable === undefined) || enable) {
|
|
this.events.onOpen.forEach(cb => cb());
|
|
|
|
EmoticonsDropdown.lazyLoadQueue.lock();
|
|
//EmoticonsDropdown.lazyLoadQueue.unlock();
|
|
animationIntersector.lockIntersectionGroup(EMOTICONSSTICKERGROUP);
|
|
|
|
this.element.style.display = '';
|
|
void this.element.offsetLeft; // reflow
|
|
this.element.classList.add('active');
|
|
|
|
clearTimeout(this.displayTimeout);
|
|
this.displayTimeout = window.setTimeout(() => {
|
|
animationIntersector.unlockIntersectionGroup(EMOTICONSSTICKERGROUP);
|
|
EmoticonsDropdown.lazyLoadQueue.unlock();
|
|
EmoticonsDropdown.lazyLoadQueue.refresh();
|
|
|
|
this.events.onOpenAfter.forEach(cb => cb());
|
|
}, touchSupport ? 0 : 200);
|
|
|
|
/* if(touchSupport) {
|
|
this.restoreScroll();
|
|
} */
|
|
} else {
|
|
this.events.onClose.forEach(cb => cb());
|
|
|
|
EmoticonsDropdown.lazyLoadQueue.lock();
|
|
//EmoticonsDropdown.lazyLoadQueue.lock();
|
|
|
|
// нужно залочить группу и выключить стикеры
|
|
animationIntersector.lockIntersectionGroup(EMOTICONSSTICKERGROUP);
|
|
animationIntersector.checkAnimations(true, EMOTICONSSTICKERGROUP);
|
|
|
|
this.element.classList.remove('active');
|
|
|
|
clearTimeout(this.displayTimeout);
|
|
this.displayTimeout = window.setTimeout(() => {
|
|
this.element.style.display = 'none';
|
|
|
|
// теперь можно убрать visible, чтобы они не включились после фокуса
|
|
animationIntersector.unlockIntersectionGroup(EMOTICONSSTICKERGROUP);
|
|
EmoticonsDropdown.lazyLoadQueue.unlock();
|
|
EmoticonsDropdown.lazyLoadQueue.refresh();
|
|
|
|
this.events.onCloseAfter.forEach(cb => cb());
|
|
}, touchSupport ? 0 : 200);
|
|
|
|
/* if(touchSupport) {
|
|
this.restoreScroll();
|
|
} */
|
|
}
|
|
|
|
//animationIntersector.checkAnimations(false, EMOTICONSSTICKERGROUP);
|
|
};
|
|
|
|
public static menuOnClick = (menu: HTMLUListElement, scroll: Scrollable, menuScroll?: ScrollableX) => {
|
|
let prevId = 0;
|
|
let jumpedTo = -1;
|
|
|
|
const setActive = (id: number) => {
|
|
if(id == prevId) {
|
|
return false;
|
|
}
|
|
|
|
menu.children[prevId].classList.remove('active');
|
|
menu.children[id].classList.add('active');
|
|
prevId = id;
|
|
|
|
return true;
|
|
};
|
|
|
|
const stickyIntersector = new StickyIntersector(scroll.container, (stuck, target) => {
|
|
//console.log('sticky scrollTOp', stuck, target, scroll.container.scrollTop);
|
|
|
|
if(Math.abs(jumpedTo - scroll.container.scrollTop) <= 1) {
|
|
return;
|
|
} else {
|
|
jumpedTo = -1;
|
|
}
|
|
|
|
const which = whichChild(target);
|
|
if(!stuck && which) { // * due to stickyIntersector
|
|
return;
|
|
}
|
|
|
|
setActive(which);
|
|
|
|
if(menuScroll) {
|
|
if(which < menu.childElementCount - 4) {
|
|
menuScroll.container.scrollLeft = (which - 3) * 47;
|
|
} else {
|
|
menuScroll.container.scrollLeft = which * 47;
|
|
}
|
|
}
|
|
});
|
|
|
|
menu.addEventListener('click', (e) => {
|
|
let target = e.target as HTMLElement;
|
|
target = findUpTag(target, 'LI');
|
|
|
|
if(!target) {
|
|
return;
|
|
}
|
|
|
|
const which = whichChild(target);
|
|
|
|
if(!setActive(which)) {
|
|
return;
|
|
}
|
|
|
|
const element = (scroll.splitUp || scroll.container).children[which] as HTMLElement;
|
|
const offsetTop = element.offsetTop + 1; // * due to stickyIntersector
|
|
|
|
scroll.container.scrollTop = jumpedTo = offsetTop;
|
|
|
|
//console.log('set scrollTop:', offsetTop);
|
|
});
|
|
|
|
return stickyIntersector;
|
|
};
|
|
|
|
public static onMediaClick = (e: MouseEvent) => {
|
|
let target = e.target as HTMLElement;
|
|
target = findUpTag(target, 'DIV');
|
|
|
|
if(!target) return;
|
|
|
|
const fileID = target.dataset.docID;
|
|
if(!fileID) return;
|
|
|
|
if(appImManager.chatInputC.sendMessageWithDocument(fileID)) {
|
|
/* dropdown.classList.remove('active');
|
|
toggleEl.classList.remove('active'); */
|
|
emoticonsDropdown.toggle(false);
|
|
} else {
|
|
console.warn('got no doc by id:', fileID);
|
|
}
|
|
};
|
|
}
|
|
|
|
const emoticonsDropdown = new EmoticonsDropdown();
|
|
// @ts-ignore
|
|
if(process.env.NODE_ENV != 'production') {
|
|
(window as any).emoticonsDropdown = emoticonsDropdown;
|
|
}
|
|
export default emoticonsDropdown; |