Handle RTL on search inputs
Debounce pinned message animation Debounce lazy load queue
This commit is contained in:
parent
402ae16d98
commit
285e56f233
@ -6,7 +6,7 @@ import appPeersManager from '../lib/appManagers/appPeersManager';
|
||||
import appMessagesManager from "../lib/appManagers/appMessagesManager";
|
||||
import { formatPhoneNumber } from "./misc";
|
||||
import appChatsManager from "../lib/appManagers/appChatsManager";
|
||||
import SearchInput from "./searchInput";
|
||||
import InputSearch from "./inputSearch";
|
||||
import rootScope from "../lib/rootScope";
|
||||
import { escapeRegExp } from "../helpers/string";
|
||||
import searchIndexManager from "../lib/searchIndexManager";
|
||||
@ -81,7 +81,7 @@ export default class AppSearch {
|
||||
|
||||
private scrollable: Scrollable;
|
||||
|
||||
constructor(public container: HTMLElement, public searchInput: SearchInput, public searchGroups: {[group in SearchGroupType]: SearchGroup}, public onSearch?: (count: number) => void) {
|
||||
constructor(public container: HTMLElement, public searchInput: InputSearch, public searchGroups: {[group in SearchGroupType]: SearchGroup}, public onSearch?: (count: number) => void) {
|
||||
this.scrollable = new Scrollable(this.container);
|
||||
this.listsContainer = this.scrollable.container as HTMLDivElement;
|
||||
for(let i in this.searchGroups) {
|
||||
|
@ -35,6 +35,7 @@ import LazyLoadQueue from "../lazyLoadQueue";
|
||||
import { AppChatsManager } from "../../lib/appManagers/appChatsManager";
|
||||
import Chat from "./chat";
|
||||
import ListenerSetter from "../../helpers/listenerSetter";
|
||||
import { pause } from "../../helpers/schedulers";
|
||||
|
||||
const IGNORE_ACTIONS = ['messageActionHistoryClear'];
|
||||
|
||||
@ -1122,10 +1123,13 @@ export default class ChatBubbles {
|
||||
if(!bubble?.parentElement) {
|
||||
bubble = this.findNextMountedBubbleByMsgId(lastMsgId);
|
||||
}
|
||||
|
||||
this.scrollable.scrollIntoView(bubble, samePeer/* , fromUp */);
|
||||
if(!forwardingUnread) {
|
||||
this.highlightBubble(bubble);
|
||||
|
||||
// ! sometimes there can be no bubble
|
||||
if(bubble) {
|
||||
this.scrollable.scrollIntoView(bubble, samePeer/* , fromUp */);
|
||||
if(!forwardingUnread) {
|
||||
this.highlightBubble(bubble);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
this.scrollable.scrollTop = this.scrollable.scrollHeight;
|
||||
@ -1263,7 +1267,9 @@ export default class ChatBubbles {
|
||||
}
|
||||
});
|
||||
|
||||
resolve();
|
||||
//setTimeout(() => {
|
||||
resolve();
|
||||
//}, 500);
|
||||
this.messagesQueuePromise = null;
|
||||
}, reject);
|
||||
}, 0);
|
||||
|
@ -11,6 +11,7 @@ import { cancelEvent, findUpClassName, getElementByPoint, handleScrollSideEvent
|
||||
import Chat from "./chat";
|
||||
import ListenerSetter from "../../helpers/listenerSetter";
|
||||
import ButtonIcon from "../buttonIcon";
|
||||
import { debounce } from "../../helpers/schedulers";
|
||||
|
||||
class AnimatedSuper {
|
||||
static DURATION = 200;
|
||||
@ -94,6 +95,17 @@ class AnimatedSuper {
|
||||
row.element.classList.toggle('is-hiding', false);
|
||||
previousRow && previousRow.element.classList.toggle('is-hiding', true);
|
||||
|
||||
/* const height = row.element.getBoundingClientRect().height;
|
||||
row.element.style.transform = `translateY(${fromTop ? height * -1 : height}px)`;
|
||||
if(previousRow) {
|
||||
previousRow.element.style.transform = `translateY(${fromTop ? height : height * -1}px)`;
|
||||
} */
|
||||
|
||||
/* row.element.style.setProperty('--height', row.element.getBoundingClientRect().height + 'px');
|
||||
if(previousRow) {
|
||||
previousRow.element.style.setProperty('--height', previousRow.element.getBoundingClientRect().height + 'px');
|
||||
} */
|
||||
|
||||
this.clearRows(index);
|
||||
}
|
||||
}
|
||||
@ -227,6 +239,8 @@ export default class ChatPinnedMessage {
|
||||
public getCurrentIndexPromise: Promise<any> = null;
|
||||
public btnOpen: HTMLButtonElement;
|
||||
|
||||
public setPinnedMessage: () => void;
|
||||
|
||||
constructor(private topbar: ChatTopbar, private chat: Chat, private appMessagesManager: AppMessagesManager, private appPeersManager: AppPeersManager) {
|
||||
this.listenerSetter = new ListenerSetter();
|
||||
|
||||
@ -290,6 +304,10 @@ export default class ChatPinnedMessage {
|
||||
this.pinnedMessageContainer.toggle(this.hidden = true);
|
||||
}
|
||||
});
|
||||
|
||||
// * 200 - no lags
|
||||
// * 100 - need test
|
||||
this.setPinnedMessage = debounce(() => this._setPinnedMessage(), 100, true, true);
|
||||
}
|
||||
|
||||
public destroy() {
|
||||
@ -300,6 +318,8 @@ export default class ChatPinnedMessage {
|
||||
}
|
||||
|
||||
public setCorrectIndex(lastScrollDirection?: number) {
|
||||
//return;
|
||||
|
||||
if(this.locked || this.hidden/* || this.chat.setPeerPromise || this.chat.bubbles.messagesQueuePromise */) {
|
||||
return;
|
||||
}
|
||||
@ -315,6 +335,8 @@ export default class ChatPinnedMessage {
|
||||
el = findUpClassName(el, 'bubble');
|
||||
if(!el) return;
|
||||
|
||||
//return;
|
||||
|
||||
const mid = el.dataset.mid;
|
||||
if(el && mid !== undefined) {
|
||||
this.chat.log('[PM]: setCorrectIndex will test mid:', mid);
|
||||
@ -517,7 +539,7 @@ export default class ChatPinnedMessage {
|
||||
/* || (!this.chatAudio.divAndCaption.container.classList.contains('hide') && to == ScreenSize.medium) */);
|
||||
}
|
||||
|
||||
public setPinnedMessage() {
|
||||
public _setPinnedMessage() {
|
||||
/////this.log('setting pinned message', message);
|
||||
//return;
|
||||
/* const promise: Promise<any> = this.chat.setPeerPromise || this.chat.bubbles.messagesQueuePromise || Promise.resolve();
|
||||
|
@ -1,16 +1,15 @@
|
||||
import type ChatTopbar from "./topbar";
|
||||
import rootScope from "../../lib/rootScope";
|
||||
import { cancelEvent, whichChild, findUpTag } from "../../helpers/dom";
|
||||
import AppSearch, { SearchGroup } from "../appSearch";
|
||||
import PopupDatePicker from "../popupDatepicker";
|
||||
import { ripple } from "../ripple";
|
||||
import SearchInput from "../searchInput";
|
||||
import InputSearch from "../inputSearch";
|
||||
import type Chat from "./chat";
|
||||
|
||||
export default class ChatSearch {
|
||||
private element: HTMLElement;
|
||||
private backBtn: HTMLElement;
|
||||
private searchInput: SearchInput;
|
||||
private inputSearch: InputSearch;
|
||||
|
||||
private results: HTMLElement;
|
||||
|
||||
@ -39,7 +38,7 @@ export default class ChatSearch {
|
||||
this.backBtn.addEventListener('click', () => {
|
||||
this.topbar.container.classList.remove('hide-pinned');
|
||||
this.element.remove();
|
||||
this.searchInput.remove();
|
||||
this.inputSearch.remove();
|
||||
this.results.remove();
|
||||
this.footer.remove();
|
||||
this.footer.removeEventListener('click', this.onFooterClick);
|
||||
@ -50,7 +49,7 @@ export default class ChatSearch {
|
||||
this.chat.bubbles.bubblesContainer.classList.remove('search-results-active');
|
||||
}, {once: true});
|
||||
|
||||
this.searchInput = new SearchInput('Search');
|
||||
this.inputSearch = new InputSearch('Search');
|
||||
|
||||
// Results
|
||||
this.results = document.createElement('div');
|
||||
@ -59,13 +58,13 @@ export default class ChatSearch {
|
||||
this.searchGroup = new SearchGroup('', 'messages', undefined, '', false);
|
||||
this.searchGroup.list.addEventListener('click', this.onResultsClick);
|
||||
|
||||
this.appSearch = new AppSearch(this.results, this.searchInput, {
|
||||
this.appSearch = new AppSearch(this.results, this.inputSearch, {
|
||||
messages: this.searchGroup
|
||||
}, (count) => {
|
||||
this.foundCount = count;
|
||||
|
||||
if(!this.foundCount) {
|
||||
this.foundCountEl.innerText = this.searchInput.value ? 'No results' : '';
|
||||
this.foundCountEl.innerText = this.inputSearch.value ? 'No results' : '';
|
||||
this.results.classList.remove('active');
|
||||
this.chat.bubbles.bubblesContainer.classList.remove('search-results-active');
|
||||
this.upBtn.setAttribute('disabled', 'true');
|
||||
@ -113,12 +112,12 @@ export default class ChatSearch {
|
||||
this.topbar.container.parentElement.insertBefore(this.footer, chat.input.chatInput);
|
||||
|
||||
// Append container
|
||||
this.element.append(this.backBtn, this.searchInput.container);
|
||||
this.element.append(this.backBtn, this.inputSearch.container);
|
||||
|
||||
this.topbar.container.classList.add('hide-pinned');
|
||||
this.topbar.container.parentElement.append(this.element);
|
||||
|
||||
this.searchInput.input.focus();
|
||||
this.inputSearch.input.focus();
|
||||
}
|
||||
|
||||
onDateClick = (e: MouseEvent) => {
|
||||
|
@ -13,6 +13,7 @@ import StickyIntersector from "../stickyIntersector";
|
||||
import EmojiTab from "./tabs/emoji";
|
||||
import GifsTab from "./tabs/gifs";
|
||||
import StickersTab from "./tabs/stickers";
|
||||
import { pause } from "../../helpers/schedulers";
|
||||
|
||||
export const EMOTICONSSTICKERGROUP = 'emoticons-dropdown';
|
||||
|
||||
@ -211,9 +212,7 @@ export class EmoticonsDropdown {
|
||||
appImManager.chat.input.saveScroll();
|
||||
// @ts-ignore
|
||||
document.activeElement.blur();
|
||||
await new Promise((resolve) => {
|
||||
setTimeout(resolve, 100);
|
||||
});
|
||||
await pause(100);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -34,6 +34,22 @@ let init = () => {
|
||||
init = null;
|
||||
};
|
||||
|
||||
const checkAndSetRTL = (input: HTMLElement) => {
|
||||
//const isEmpty = isInputEmpty(input);
|
||||
//console.log('input', isEmpty);
|
||||
|
||||
//const char = [...getRichValue(input)][0];
|
||||
const char = (input instanceof HTMLInputElement ? input.value : input.innerText)[0];
|
||||
let direction = 'ltr';
|
||||
if(char && checkRTL(char)) {
|
||||
direction = 'rtl';
|
||||
}
|
||||
|
||||
//console.log('RTL', direction, char);
|
||||
|
||||
input.style.direction = direction;
|
||||
};
|
||||
|
||||
const InputField = (options: {
|
||||
placeholder?: string,
|
||||
label?: string,
|
||||
@ -51,6 +67,7 @@ const InputField = (options: {
|
||||
|
||||
const {placeholder, label, maxLength, showLengthOn, name, plainText} = options;
|
||||
|
||||
let input: HTMLElement;
|
||||
if(!plainText) {
|
||||
if(init) {
|
||||
init();
|
||||
@ -61,21 +78,9 @@ const InputField = (options: {
|
||||
${label ? `<label>${label}</label>` : ''}
|
||||
`;
|
||||
|
||||
const input = div.firstElementChild as HTMLElement;
|
||||
const observer = new MutationObserver((mutationsList, observer) => {
|
||||
//const isEmpty = isInputEmpty(input);
|
||||
//console.log('input', isEmpty);
|
||||
|
||||
//const char = [...getRichValue(input)][0];
|
||||
const char = input.innerText[0];
|
||||
let direction = 'ltr';
|
||||
if(char && checkRTL(char)) {
|
||||
direction = 'rtl';
|
||||
}
|
||||
|
||||
//console.log('RTL', direction, char);
|
||||
|
||||
input.style.direction = direction;
|
||||
input = div.firstElementChild as HTMLElement;
|
||||
const observer = new MutationObserver(() => {
|
||||
checkAndSetRTL(input);
|
||||
|
||||
if(processInput) {
|
||||
processInput();
|
||||
@ -86,21 +91,23 @@ const InputField = (options: {
|
||||
observer.observe(input, {characterData: true, childList: true, subtree: true});
|
||||
} else {
|
||||
div.innerHTML = `
|
||||
<input type="text" name="${name}" ${placeholder ? `placeholder="${placeholder}"` : ''} autocomplete="off" required="" class="input-field-input">
|
||||
<input type="text" ${name ? `name="${name}"` : ''} ${placeholder ? `placeholder="${placeholder}"` : ''} autocomplete="off" ${label ? 'required=""' : ''} class="input-field-input">
|
||||
${label ? `<label>${label}</label>` : ''}
|
||||
`;
|
||||
|
||||
input = div.firstElementChild as HTMLElement;
|
||||
input.addEventListener('input', () => checkAndSetRTL(input));
|
||||
}
|
||||
|
||||
let processInput: () => void;
|
||||
if(maxLength) {
|
||||
const input = div.firstElementChild as HTMLInputElement;
|
||||
const labelEl = div.lastElementChild as HTMLLabelElement;
|
||||
let showingLength = false;
|
||||
|
||||
processInput = () => {
|
||||
const wasError = input.classList.contains('error');
|
||||
// * https://stackoverflow.com/a/54369605 #2 to count emoji as 1 symbol
|
||||
const inputLength = plainText ? input.value.length : [...getRichValue(input)].length;
|
||||
const inputLength = plainText ? (input as HTMLInputElement).value.length : [...getRichValue(input)].length;
|
||||
const diff = maxLength - inputLength;
|
||||
const isError = diff < 0;
|
||||
input.classList.toggle('error', isError);
|
||||
@ -117,7 +124,10 @@ const InputField = (options: {
|
||||
input.addEventListener('input', processInput);
|
||||
}
|
||||
|
||||
return {container: div, input: div.firstElementChild as HTMLInputElement};
|
||||
return {
|
||||
container: div,
|
||||
input: div.firstElementChild as HTMLInputElement
|
||||
};
|
||||
};
|
||||
|
||||
export default InputField;
|
@ -1,4 +1,7 @@
|
||||
export default class SearchInput {
|
||||
//import { getRichValue } from "../helpers/dom";
|
||||
import InputField from "./inputField";
|
||||
|
||||
export default class InputSearch {
|
||||
public container: HTMLElement;
|
||||
public input: HTMLInputElement;
|
||||
public clearBtn: HTMLElement;
|
||||
@ -8,15 +11,19 @@ export default class SearchInput {
|
||||
public onChange: (value: string) => void;
|
||||
|
||||
constructor(placeholder: string, onChange?: (value: string) => void) {
|
||||
this.container = document.createElement('div');
|
||||
const inputField = InputField({
|
||||
placeholder,
|
||||
plainText: true
|
||||
});
|
||||
|
||||
this.container = inputField.container;
|
||||
this.container.classList.remove('input-field');
|
||||
this.container.classList.add('input-search');
|
||||
|
||||
this.onChange = onChange;
|
||||
|
||||
this.input = document.createElement('input');
|
||||
this.input.type = 'text';
|
||||
this.input.placeholder = placeholder;
|
||||
this.input.autocomplete = Math.random().toString(36).substring(7);
|
||||
this.input = inputField.input;
|
||||
this.input.classList.add('input-search-input');
|
||||
|
||||
const searchIcon = document.createElement('span');
|
||||
searchIcon.classList.add('tgico', 'tgico-search');
|
||||
@ -33,7 +40,7 @@ export default class SearchInput {
|
||||
onInput = () => {
|
||||
if(!this.onChange) return;
|
||||
|
||||
let value = this.input.value;
|
||||
let value = this.value;
|
||||
|
||||
//this.input.classList.toggle('is-empty', !value.trim());
|
||||
|
||||
@ -53,12 +60,17 @@ export default class SearchInput {
|
||||
|
||||
get value() {
|
||||
return this.input.value;
|
||||
//return getRichValue(this.input);
|
||||
}
|
||||
|
||||
set value(value: string) {
|
||||
//this.input.innerHTML = value;
|
||||
this.input.value = value;
|
||||
this.prevValue = value;
|
||||
clearTimeout(this.timeout);
|
||||
|
||||
const event = new Event('input', {bubbles: true, cancelable: true});
|
||||
this.input.dispatchEvent(event);
|
||||
}
|
||||
|
||||
public remove() {
|
@ -1,3 +1,4 @@
|
||||
import { debounce } from "../helpers/schedulers";
|
||||
import { logger, LogLevels } from "../lib/logger";
|
||||
import VisibilityIntersector, { OnVisibilityChange } from "./visibilityIntersector";
|
||||
|
||||
@ -22,8 +23,10 @@ export class LazyLoadQueueBase {
|
||||
protected unlockResolve: () => void = null;
|
||||
|
||||
protected log = logger('LL', LogLevels.error);
|
||||
protected processQueue: () => void;
|
||||
|
||||
constructor(protected parallelLimit = PARALLEL_LIMIT) {
|
||||
this.processQueue = debounce(() => this._processQueue(), 20, false, true);
|
||||
}
|
||||
|
||||
public clear() {
|
||||
@ -58,7 +61,7 @@ export class LazyLoadQueueBase {
|
||||
this.processQueue();
|
||||
}
|
||||
|
||||
public async processItem(item: LazyLoadElementBase) {
|
||||
protected async processItem(item: LazyLoadElementBase) {
|
||||
if(this.lockPromise) {
|
||||
return;
|
||||
}
|
||||
@ -96,7 +99,7 @@ export class LazyLoadQueueBase {
|
||||
this.processQueue();
|
||||
}
|
||||
|
||||
public async processQueue(item?: LazyLoadElementBase) {
|
||||
protected _processQueue(item?: LazyLoadElementBase) {
|
||||
if(!this.queue.length || this.lockPromise || (this.parallelLimit > 0 && this.inProcess.size >= this.parallelLimit)) return;
|
||||
|
||||
do {
|
||||
@ -236,9 +239,9 @@ export default class LazyLoadQueue extends LazyLoadQueueIntersector {
|
||||
if(!inserted) return false;
|
||||
|
||||
this.intersector.observe(el.div);
|
||||
if(el.wasSeen) {
|
||||
/* if(el.wasSeen) {
|
||||
this.processQueue(el);
|
||||
} else if(!el.hasOwnProperty('wasSeen')) {
|
||||
} else */if(!el.hasOwnProperty('wasSeen')) {
|
||||
el.wasSeen = false;
|
||||
}
|
||||
|
||||
|
@ -152,6 +152,8 @@ export default class Scrollable extends ScrollableBase {
|
||||
//this.log('onScroll call', this.onScrollMeasure);
|
||||
//}
|
||||
|
||||
//return;
|
||||
|
||||
if(this.onScrollMeasure || ((this.scrollLocked || (!this.onScrolledTop && !this.onScrolledBottom)) && !this.splitUp && !this.onAdditionalScroll)) return;
|
||||
this.onScrollMeasure = window.requestAnimationFrame(() => {
|
||||
this.onScrollMeasure = 0;
|
||||
|
@ -13,7 +13,7 @@ import AppSearch, { SearchGroup } from "../appSearch";
|
||||
import "../avatar";
|
||||
import { parseMenuButtonsTo } from "../misc";
|
||||
import { ScrollableX } from "../scrollable";
|
||||
import SearchInput from "../searchInput";
|
||||
import InputSearch from "../inputSearch";
|
||||
import SidebarSlider from "../slider";
|
||||
import { TransitionSlider } from "../transition";
|
||||
import AppAddMembersTab from "./tabs/addMembers";
|
||||
@ -81,7 +81,7 @@ export class AppSidebarLeft extends SidebarSlider {
|
||||
private backBtn: HTMLButtonElement;
|
||||
private searchContainer: HTMLDivElement;
|
||||
//private searchInput = document.getElementById('global-search') as HTMLInputElement;
|
||||
private searchInput: SearchInput;
|
||||
private inputSearch: InputSearch;
|
||||
|
||||
private menuEl: HTMLElement;
|
||||
private buttons: {
|
||||
@ -140,9 +140,9 @@ export class AppSidebarLeft extends SidebarSlider {
|
||||
|
||||
//this._selectTab(0); // make first tab as default
|
||||
|
||||
this.searchInput = new SearchInput('Telegram Search');
|
||||
this.inputSearch = new InputSearch('Telegram Search');
|
||||
const sidebarHeader = this.sidebarEl.querySelector('.item-main .sidebar-header');
|
||||
sidebarHeader.append(this.searchInput.container);
|
||||
sidebarHeader.append(this.inputSearch.container);
|
||||
|
||||
this.toolsBtn = this.sidebarEl.querySelector('.sidebar-tools-button') as HTMLButtonElement;
|
||||
this.backBtn = this.sidebarEl.querySelector('.sidebar-back-button') as HTMLButtonElement;
|
||||
@ -161,7 +161,7 @@ export class AppSidebarLeft extends SidebarSlider {
|
||||
this.menuEl = this.toolsBtn.querySelector('.btn-menu');
|
||||
this.newBtnMenu = this.sidebarEl.querySelector('#new-menu');
|
||||
|
||||
this.searchInput.input.addEventListener('focus', () => {
|
||||
this.inputSearch.input.addEventListener('focus', () => {
|
||||
this.searchGroups = {
|
||||
//saved: new SearchGroup('', 'contacts'),
|
||||
contacts: new SearchGroup('Chats', 'contacts'),
|
||||
@ -171,8 +171,8 @@ export class AppSidebarLeft extends SidebarSlider {
|
||||
recent: new SearchGroup('Recent', 'contacts', false, 'search-group-recent')
|
||||
};
|
||||
|
||||
this.globalSearch = new AppSearch(this.searchContainer, this.searchInput, this.searchGroups, (count) => {
|
||||
if(!count && !this.searchInput.value.trim()) {
|
||||
this.globalSearch = new AppSearch(this.searchContainer, this.inputSearch, this.searchGroups, (count) => {
|
||||
if(!count && !this.inputSearch.value.trim()) {
|
||||
this.globalSearch.reset();
|
||||
this.searchGroups.people.toggle();
|
||||
this.renderRecentSearch();
|
||||
@ -263,7 +263,7 @@ export class AppSidebarLeft extends SidebarSlider {
|
||||
};
|
||||
|
||||
let firstTime = true;
|
||||
this.searchInput.input.addEventListener('focus', onFocus);
|
||||
this.inputSearch.input.addEventListener('focus', onFocus);
|
||||
onFocus();
|
||||
|
||||
this.backBtn.addEventListener('click', (e) => {
|
||||
@ -339,7 +339,7 @@ export class AppSidebarLeft extends SidebarSlider {
|
||||
this.recentSearchLoaded = true;
|
||||
}
|
||||
|
||||
if(this.searchInput.value.trim()) {
|
||||
if(this.inputSearch.value.trim()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -5,7 +5,7 @@ import appUsersManager from "../../../lib/appManagers/appUsersManager";
|
||||
import appPhotosManager from "../../../lib/appManagers/appPhotosManager";
|
||||
import appSidebarLeft, { AppSidebarLeft } from "..";
|
||||
import rootScope from "../../../lib/rootScope";
|
||||
import SearchInput from "../../searchInput";
|
||||
import InputSearch from "../../inputSearch";
|
||||
|
||||
// TODO: поиск по людям глобальный, если не нашло в контактах никого
|
||||
|
||||
@ -15,7 +15,7 @@ export default class AppContactsTab implements SliderTab {
|
||||
private scrollable: Scrollable;
|
||||
private promise: Promise<void>;
|
||||
|
||||
private searchInput: SearchInput;
|
||||
private inputSearch: InputSearch;
|
||||
|
||||
init() {
|
||||
this.container = document.getElementById('contacts-container');
|
||||
@ -24,12 +24,12 @@ export default class AppContactsTab implements SliderTab {
|
||||
appDialogsManager.setListClickListener(this.list);
|
||||
this.scrollable = new Scrollable(this.list.parentElement);
|
||||
|
||||
this.searchInput = new SearchInput('Search', (value) => {
|
||||
this.inputSearch = new InputSearch('Search', (value) => {
|
||||
this.list.innerHTML = '';
|
||||
this.openContacts(value);
|
||||
});
|
||||
|
||||
this.container.firstElementChild.append(this.searchInput.container);
|
||||
this.container.firstElementChild.append(this.inputSearch.container);
|
||||
|
||||
// preload contacts
|
||||
// appUsersManager.getContacts();
|
||||
@ -43,7 +43,7 @@ export default class AppContactsTab implements SliderTab {
|
||||
|
||||
public onCloseAfterTimeout() {
|
||||
this.list.innerHTML = '';
|
||||
this.searchInput.value = '';
|
||||
this.inputSearch.value = '';
|
||||
}
|
||||
|
||||
public openContacts(query?: string) {
|
||||
|
@ -8,6 +8,7 @@ import AppPrivateSearchTab from "./tabs/search";
|
||||
import AppSharedMediaTab from "./tabs/sharedMedia";
|
||||
//import AppForwardTab from "./tabs/forward";
|
||||
import { MOUNT_CLASS_TO } from "../../lib/mtproto/mtproto_config";
|
||||
import { pause } from "../../helpers/schedulers";
|
||||
|
||||
export const RIGHT_COLUMN_ACTIVE_CLASSNAME = 'is-right-column-shown';
|
||||
|
||||
@ -111,14 +112,10 @@ export class AppSidebarRight extends SidebarSlider {
|
||||
//if(mediaSizes.isMobile) {
|
||||
//appImManager._selectTab(active ? 1 : 2);
|
||||
appImManager.selectTab(active ? 1 : 2);
|
||||
return new Promise(resolve => {
|
||||
setTimeout(resolve, mediaSizes.isMobile ? 250 : 200); // delay of slider animation
|
||||
});
|
||||
return pause(mediaSizes.isMobile ? 250 : 200); // delay of slider animation
|
||||
//}
|
||||
|
||||
return new Promise(resolve => {
|
||||
setTimeout(resolve, 200); // delay for third column open
|
||||
});
|
||||
return pause(200); // delay for third column open
|
||||
//return Promise.resolve();
|
||||
|
||||
/* return new Promise((resolve, reject) => {
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { SliderTab } from "../../slider";
|
||||
import SearchInput from "../../searchInput";
|
||||
import InputSearch from "../../inputSearch";
|
||||
import Scrollable from "../../scrollable";
|
||||
import animationIntersector from "../../animationIntersector";
|
||||
import appSidebarRight, { AppSidebarRight } from "..";
|
||||
@ -18,7 +18,7 @@ export default class AppGifsTab implements SliderTab {
|
||||
private contentDiv = this.container.querySelector('.sidebar-content') as HTMLDivElement;
|
||||
private backBtn = this.container.querySelector('.sidebar-close-button') as HTMLButtonElement;
|
||||
//private input = this.container.querySelector('#stickers-search') as HTMLInputElement;
|
||||
private searchInput: SearchInput;
|
||||
private inputSearch: InputSearch;
|
||||
private gifsDiv = this.contentDiv.firstElementChild as HTMLDivElement;
|
||||
private scrollable: Scrollable;
|
||||
|
||||
@ -35,14 +35,14 @@ export default class AppGifsTab implements SliderTab {
|
||||
|
||||
this.masonry = new GifsMasonry(this.gifsDiv, ANIMATIONGROUP, this.scrollable);
|
||||
|
||||
this.searchInput = new SearchInput('Search GIFs', (value) => {
|
||||
this.inputSearch = new InputSearch('Search GIFs', (value) => {
|
||||
this.reset();
|
||||
this.search(value);
|
||||
});
|
||||
|
||||
this.gifsDiv.addEventListener('click', this.onGifsClick);
|
||||
|
||||
this.backBtn.parentElement.append(this.searchInput.container);
|
||||
this.backBtn.parentElement.append(this.inputSearch.container);
|
||||
}
|
||||
|
||||
onGifsClick = (e: MouseEvent) => {
|
||||
@ -66,7 +66,7 @@ export default class AppGifsTab implements SliderTab {
|
||||
public onCloseAfterTimeout() {
|
||||
this.reset();
|
||||
this.gifsDiv.innerHTML = '';
|
||||
this.searchInput.value = '';
|
||||
this.inputSearch.value = '';
|
||||
animationIntersector.checkAnimations(undefined, ANIMATIONGROUP);
|
||||
}
|
||||
|
||||
@ -86,7 +86,7 @@ export default class AppGifsTab implements SliderTab {
|
||||
this.reset();
|
||||
|
||||
this.scrollable.onScrolledBottom = () => {
|
||||
this.search(this.searchInput.value, false);
|
||||
this.search(this.inputSearch.value, false);
|
||||
};
|
||||
});
|
||||
}
|
||||
@ -102,7 +102,7 @@ export default class AppGifsTab implements SliderTab {
|
||||
this.searchPromise = appInlineBotsManager.getInlineResults(0, this.gifBotPeerId, query, this.nextOffset);
|
||||
const { results, next_offset } = await this.searchPromise;
|
||||
|
||||
if(this.searchInput.value != query) {
|
||||
if(this.inputSearch.value != query) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1,13 +1,13 @@
|
||||
import appSidebarRight, { AppSidebarRight } from "..";
|
||||
import AppSearch, { SearchGroup } from "../../appSearch";
|
||||
import SearchInput from "../../searchInput";
|
||||
import InputSearch from "../../inputSearch";
|
||||
import { SliderTab } from "../../slider";
|
||||
|
||||
export default class AppPrivateSearchTab implements SliderTab {
|
||||
public container: HTMLElement;
|
||||
public closeBtn: HTMLElement;
|
||||
|
||||
private searchInput: SearchInput;
|
||||
private inputSearch: InputSearch;
|
||||
private appSearch: AppSearch;
|
||||
|
||||
private peerId = 0;
|
||||
@ -24,9 +24,9 @@ export default class AppPrivateSearchTab implements SliderTab {
|
||||
public init() {
|
||||
this.container = document.getElementById('search-private-container');
|
||||
this.closeBtn = this.container.querySelector('.sidebar-close-button');
|
||||
this.searchInput = new SearchInput('Search');
|
||||
this.closeBtn.parentElement.append(this.searchInput.container);
|
||||
this.appSearch = new AppSearch(this.container.querySelector('.chatlist-container'), this.searchInput, {
|
||||
this.inputSearch = new InputSearch('Search');
|
||||
this.closeBtn.parentElement.append(this.inputSearch.container);
|
||||
this.appSearch = new AppSearch(this.container.querySelector('.chatlist-container'), this.inputSearch, {
|
||||
messages: new SearchGroup('Private Search', 'messages')
|
||||
});
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { SliderTab } from "../../slider";
|
||||
import SearchInput from "../../searchInput";
|
||||
import InputSearch from "../../inputSearch";
|
||||
import Scrollable from "../../scrollable";
|
||||
import LazyLoadQueue from "../../lazyLoadQueue";
|
||||
import { findUpClassName } from "../../../helpers/dom";
|
||||
@ -17,7 +17,7 @@ export default class AppStickersTab implements SliderTab {
|
||||
private contentDiv = this.container.querySelector('.sidebar-content') as HTMLDivElement;
|
||||
private backBtn = this.container.querySelector('.sidebar-close-button') as HTMLButtonElement;
|
||||
//private input = this.container.querySelector('#stickers-search') as HTMLInputElement;
|
||||
private searchInput: SearchInput;
|
||||
private inputSearch: InputSearch;
|
||||
private setsDiv = this.contentDiv.firstElementChild as HTMLDivElement;
|
||||
private scrollable: Scrollable;
|
||||
private lazyLoadQueue: LazyLoadQueue;
|
||||
@ -27,11 +27,11 @@ export default class AppStickersTab implements SliderTab {
|
||||
|
||||
this.lazyLoadQueue = new LazyLoadQueue();
|
||||
|
||||
this.searchInput = new SearchInput('Search Stickers', (value) => {
|
||||
this.inputSearch = new InputSearch('Search Stickers', (value) => {
|
||||
this.search(value);
|
||||
});
|
||||
|
||||
this.backBtn.parentElement.append(this.searchInput.container);
|
||||
this.backBtn.parentElement.append(this.inputSearch.container);
|
||||
|
||||
this.setsDiv.addEventListener('click', (e) => {
|
||||
const sticker = findUpClassName(e.target, 'sticker-set-sticker');
|
||||
@ -76,7 +76,7 @@ export default class AppStickersTab implements SliderTab {
|
||||
|
||||
public onCloseAfterTimeout() {
|
||||
this.setsDiv.innerHTML = '';
|
||||
this.searchInput.value = '';
|
||||
this.inputSearch.value = '';
|
||||
animationIntersector.checkAnimations(undefined, 'STICKERS-SEARCH');
|
||||
}
|
||||
|
||||
@ -188,7 +188,7 @@ export default class AppStickersTab implements SliderTab {
|
||||
|
||||
public renderFeatured() {
|
||||
return appStickersManager.getFeaturedStickers().then(coveredSets => {
|
||||
if(this.searchInput.value) {
|
||||
if(this.inputSearch.value) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -225,7 +225,7 @@ export default class AppStickersTab implements SliderTab {
|
||||
}
|
||||
|
||||
return appStickersManager.searchStickerSets(query, false).then(coveredSets => {
|
||||
if(this.searchInput.value != query) {
|
||||
if(this.inputSearch.value != query) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -572,7 +572,7 @@ export function wrapPhoto({photo, message, container, boxWidth, boxHeight, withT
|
||||
});
|
||||
};
|
||||
|
||||
return cacheContext.downloaded || !lazyLoadQueue ? load() : (lazyLoadQueue.push({div: container, load: load, wasSeen: true}), Promise.resolve());
|
||||
return cacheContext.downloaded || !lazyLoadQueue ? load() : (lazyLoadQueue.push({div: container, load/* : load, wasSeen: true */}), Promise.resolve());
|
||||
}
|
||||
|
||||
export function wrapSticker({doc, div, middleware, lazyLoadQueue, group, play, onlyThumb, emoji, width, height, withThumb, loop}: {
|
||||
@ -813,7 +813,7 @@ export function wrapSticker({doc, div, middleware, lazyLoadQueue, group, play, o
|
||||
}
|
||||
};
|
||||
|
||||
return lazyLoadQueue && (!doc.downloaded || stickerType == 2) ? (lazyLoadQueue.push({div, load, wasSeen: group == 'chat' && stickerType != 2}), Promise.resolve()) : load();
|
||||
return lazyLoadQueue && (!doc.downloaded || stickerType == 2) ? (lazyLoadQueue.push({div, load/* , wasSeen: group == 'chat' && stickerType != 2 */}), Promise.resolve()) : load();
|
||||
}
|
||||
|
||||
export function wrapReply(title: string, subtitle: string, message?: any) {
|
||||
|
125
src/helpers/schedulers.ts
Normal file
125
src/helpers/schedulers.ts
Normal file
@ -0,0 +1,125 @@
|
||||
// * Jolly Cobra's schedulers
|
||||
import { AnyToVoidFunction } from "../types";
|
||||
|
||||
//type Scheduler = typeof requestAnimationFrame | typeof onTickEnd | typeof runNow;
|
||||
|
||||
export function debounce<F extends AnyToVoidFunction>(
|
||||
fn: F,
|
||||
ms: number,
|
||||
shouldRunFirst = true,
|
||||
shouldRunLast = true,
|
||||
) {
|
||||
let waitingTimeout: number | null = null;
|
||||
|
||||
return (...args: Parameters<F>) => {
|
||||
if(waitingTimeout) {
|
||||
clearTimeout(waitingTimeout);
|
||||
waitingTimeout = null;
|
||||
} else if(shouldRunFirst) {
|
||||
// @ts-ignore
|
||||
fn(...args);
|
||||
}
|
||||
|
||||
waitingTimeout = window.setTimeout(() => {
|
||||
if(shouldRunLast) {
|
||||
// @ts-ignore
|
||||
fn(...args);
|
||||
}
|
||||
|
||||
waitingTimeout = null;
|
||||
}, ms);
|
||||
};
|
||||
}
|
||||
|
||||
/* export function throttle<F extends AnyToVoidFunction>(
|
||||
fn: F,
|
||||
ms: number,
|
||||
shouldRunFirst = true,
|
||||
) {
|
||||
let interval: number | null = null;
|
||||
let isPending: boolean;
|
||||
let args: Parameters<F>;
|
||||
|
||||
return (..._args: Parameters<F>) => {
|
||||
isPending = true;
|
||||
args = _args;
|
||||
|
||||
if (!interval) {
|
||||
if (shouldRunFirst) {
|
||||
isPending = false;
|
||||
// @ts-ignore
|
||||
fn(...args);
|
||||
}
|
||||
|
||||
interval = window.setInterval(() => {
|
||||
if (!isPending) {
|
||||
window.clearInterval(interval!);
|
||||
interval = null;
|
||||
return;
|
||||
}
|
||||
|
||||
isPending = false;
|
||||
// @ts-ignore
|
||||
fn(...args);
|
||||
}, ms);
|
||||
}
|
||||
};
|
||||
} */
|
||||
|
||||
/* export function throttleWithRaf<F extends AnyToVoidFunction>(fn: F) {
|
||||
return throttleWith(fastRaf, fn);
|
||||
}
|
||||
|
||||
export function throttleWithTickEnd<F extends AnyToVoidFunction>(fn: F) {
|
||||
return throttleWith(onTickEnd, fn);
|
||||
}
|
||||
|
||||
export function throttleWithNow<F extends AnyToVoidFunction>(fn: F) {
|
||||
return throttleWith(runNow, fn);
|
||||
}
|
||||
|
||||
export function throttleWith<F extends AnyToVoidFunction>(schedulerFn: Scheduler, fn: F) {
|
||||
let waiting = false;
|
||||
let args: Parameters<F>;
|
||||
|
||||
return (..._args: Parameters<F>) => {
|
||||
args = _args;
|
||||
|
||||
if (!waiting) {
|
||||
waiting = true;
|
||||
|
||||
schedulerFn(() => {
|
||||
waiting = false;
|
||||
// @ts-ignore
|
||||
fn(...args);
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export function onTickEnd(cb: NoneToVoidFunction) {
|
||||
Promise.resolve().then(cb);
|
||||
}
|
||||
|
||||
function runNow(fn: NoneToVoidFunction) {
|
||||
fn();
|
||||
} */
|
||||
|
||||
export const pause = (ms: number) => new Promise((resolve) => {
|
||||
setTimeout(resolve, ms);
|
||||
});
|
||||
|
||||
/* let fastRafCallbacks: NoneToVoidFunction[] | undefined;
|
||||
export function fastRaf(callback: NoneToVoidFunction) {
|
||||
if (!fastRafCallbacks) {
|
||||
fastRafCallbacks = [callback];
|
||||
|
||||
requestAnimationFrame(() => {
|
||||
const currentCallbacks = fastRafCallbacks!;
|
||||
fastRafCallbacks = undefined;
|
||||
currentCallbacks.forEach((cb) => cb());
|
||||
});
|
||||
} else {
|
||||
fastRafCallbacks.push(callback);
|
||||
}
|
||||
} */
|
@ -5,7 +5,7 @@
|
||||
<meta charset="utf-8">
|
||||
<title>Telegram Web</title>
|
||||
<meta name="description" content="">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1">
|
||||
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="assets/img/apple-touch-icon.png">
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="assets/img/favicon-32x32.png">
|
||||
|
@ -210,9 +210,9 @@ export class ApiFileManager {
|
||||
|
||||
this.log('downloadFile', fileName, size, location, options.mimeType, process);
|
||||
|
||||
if(options.queueId) {
|
||||
/* if(options.queueId) {
|
||||
this.log.error('downloadFile queueId:', fileName, options.queueId);
|
||||
}
|
||||
} */
|
||||
|
||||
if(cachedPromise) {
|
||||
//this.log('downloadFile cachedPromise');
|
||||
|
@ -699,7 +699,17 @@ class TLDeserialization {
|
||||
}
|
||||
|
||||
if(!constructorData) {
|
||||
throw new Error('Constructor not found: ' + constructor + ' ' + this.fetchInt() + ' ' + this.fetchInt() + ' ' + field);
|
||||
console.error('Constructor not found:', constructor);
|
||||
|
||||
let int1: number, int2: number;
|
||||
try {
|
||||
int1 = this.fetchInt(field);
|
||||
int2 = this.fetchInt(field);
|
||||
} catch(err) {
|
||||
|
||||
}
|
||||
|
||||
throw new Error('Constructor not found: ' + constructor + ' ' + int1 + ' ' + int2 + ' ' + field);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -8,6 +8,7 @@ import { App } from '../lib/mtproto/mtproto_config';
|
||||
import serverTimeManager from '../lib/mtproto/serverTimeManager';
|
||||
import { AuthAuthorization, AuthLoginToken } from '../layer';
|
||||
import { bytesCmp, bytesToBase64 } from '../helpers/bytes';
|
||||
import { pause } from '../helpers/schedulers';
|
||||
|
||||
let onFirstMount = async() => {
|
||||
const pageElement = page.pageEl;
|
||||
@ -102,7 +103,7 @@ let onFirstMount = async() => {
|
||||
let timestamp = Date.now() / 1000;
|
||||
let diff = loginToken.expires - timestamp - serverTimeManager.serverTimeOffset;
|
||||
|
||||
await new Promise((resolve, reject) => setTimeout(resolve, diff > 5 ? 5e3 : 1e3 * diff | 0));
|
||||
await pause(diff > 5 ? 5e3 : 1e3 * diff | 0);
|
||||
} catch(err) {
|
||||
switch(err.type) {
|
||||
case 'SESSION_PASSWORD_NEEDED':
|
||||
|
@ -337,6 +337,35 @@
|
||||
}
|
||||
}
|
||||
|
||||
.animated-super-row {
|
||||
--translateY: 16px;
|
||||
}
|
||||
|
||||
.pinned-message-media {
|
||||
--translateY: 32px;
|
||||
}
|
||||
|
||||
/* .animated-super-row.is-hiding {
|
||||
&.from-top {
|
||||
transform: translateY(-16px);
|
||||
}
|
||||
|
||||
&.from-bottom {
|
||||
transform: translateY(16px);
|
||||
}
|
||||
}
|
||||
|
||||
.pinned-message-media.is-hiding {
|
||||
&.from-top {
|
||||
transform: translateY(-32px);
|
||||
}
|
||||
|
||||
&.from-bottom {
|
||||
transform: translateY(32px);
|
||||
}
|
||||
} */
|
||||
|
||||
|
||||
&.hide ~ .tgico-pinlist, &:not(.is-many) ~ .tgico-pinlist {
|
||||
display: none;
|
||||
}
|
||||
|
@ -13,73 +13,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
.input-search {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
//Vozmojno nado budet vernut margin-left: 22px;, tak kak eto vrode v levom bare tak po verstke, a v pravom bare dlya mobili nado 16, gde stiker seti
|
||||
margin-left: 22px;
|
||||
margin-right: 4px;
|
||||
|
||||
@include respond-to(handhelds) {
|
||||
margin-left: 16px;
|
||||
}
|
||||
|
||||
input {
|
||||
--border-width: 1px;
|
||||
background-color: var(--color-gray-hover);
|
||||
height: 40px;
|
||||
border-radius: 22px;
|
||||
border: var(--border-width) solid transparent;
|
||||
box-sizing: border-box;
|
||||
padding: 0px calc(1.5rem - var(--border-width)) 0 calc(42px - var(--border-width));
|
||||
transition: background-color .15s ease-in-out, border-color .15s ease-in-out;
|
||||
width: 100%;
|
||||
font-size: 16px;
|
||||
|
||||
&:hover {
|
||||
border-color: var(--color-gray);
|
||||
}
|
||||
|
||||
&:focus {
|
||||
--border-width: 2px;
|
||||
background-color: transparent;
|
||||
border-color: $button-primary-background;
|
||||
|
||||
& + .tgico {
|
||||
color: $button-primary-background;
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.tgico {
|
||||
position: absolute;
|
||||
left: 12px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
text-align: center;
|
||||
font-size: 24px;
|
||||
color: $color-gray;
|
||||
opacity: .6;
|
||||
transition: all .15s ease-out;
|
||||
|
||||
&:before {
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
|
||||
.tgico-close {
|
||||
left: auto;
|
||||
right: 0px;
|
||||
top: 48%;
|
||||
}
|
||||
|
||||
//input.is-empty ~ .tgico-close {
|
||||
input:placeholder-shown ~ .tgico-close {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
ul {
|
||||
margin: 0;
|
||||
//padding: 0 .5rem;
|
||||
|
@ -47,15 +47,17 @@
|
||||
}
|
||||
|
||||
input, &-input {
|
||||
--height: 54px;
|
||||
--padding: 1rem;
|
||||
--border-width: 1px;
|
||||
--border-width-top: 2px;
|
||||
border: var(--border-width) solid #DADCE0;
|
||||
border-radius: $border-radius-medium;
|
||||
//padding: 0 1rem;
|
||||
padding: calc(1rem - var(--border-width-top)) calc(1rem - var(--border-width));
|
||||
padding: calc(var(--padding) - var(--border-width-top)) calc(var(--padding) - var(--border-width));
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
min-height: 54px;
|
||||
min-height: var(--height);
|
||||
transition: .2s border-color;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
@ -126,7 +128,7 @@
|
||||
transform: none;
|
||||
padding: 0 5px;
|
||||
left: .75rem;
|
||||
font-size: 0.75rem!important;
|
||||
font-size: .75rem!important;
|
||||
//color: #666;
|
||||
opacity: 1;
|
||||
}
|
||||
@ -143,11 +145,11 @@
|
||||
}
|
||||
|
||||
:-ms-input-placeholder { /* Internet Explorer 10-11 */
|
||||
color: #a2acb4;
|
||||
color: #909192;
|
||||
}
|
||||
|
||||
::-ms-input-placeholder { /* Microsoft Edge */
|
||||
color: #a2acb4;
|
||||
color: #909192;
|
||||
}
|
||||
|
||||
input:focus, button:focus {
|
||||
@ -180,4 +182,75 @@ input:focus, button:focus {
|
||||
100% {
|
||||
transform: translateX(0);
|
||||
}
|
||||
}
|
||||
|
||||
.input-search {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
//Vozmojno nado budet vernut margin-left: 22px;, tak kak eto vrode v levom bare tak po verstke, a v pravom bare dlya mobili nado 16, gde stiker seti
|
||||
margin-left: 22px;
|
||||
margin-right: 4px;
|
||||
overflow: hidden;
|
||||
|
||||
@include respond-to(handhelds) {
|
||||
margin-left: 16px;
|
||||
}
|
||||
|
||||
&-input {
|
||||
--height: 40px;
|
||||
background-color: var(--color-gray-hover);
|
||||
padding: 0px calc(42px - var(--border-width));
|
||||
height: var(--height);
|
||||
max-height: var(--height);
|
||||
//line-height: calc(var(--height) + 2px - var(--border-width) * 2);
|
||||
border-radius: 22px;
|
||||
transition: background-color .2s ease-in-out, border-color .2s ease-in-out;
|
||||
border-color: transparent;
|
||||
|
||||
&:hover {
|
||||
border-color: var(--color-gray);
|
||||
}
|
||||
|
||||
&:focus {
|
||||
--border-width: 2px;
|
||||
background-color: transparent;
|
||||
border-color: $button-primary-background;
|
||||
|
||||
& + .tgico {
|
||||
color: $button-primary-background;
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* &:empty:before {
|
||||
color: #909192 !important;
|
||||
} */
|
||||
|
||||
/* &:empty ~ .tgico-close, */&:placeholder-shown ~ .tgico-close {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.tgico {
|
||||
position: absolute;
|
||||
left: 12px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
text-align: center;
|
||||
font-size: 24px;
|
||||
color: $color-gray;
|
||||
opacity: .6;
|
||||
transition: all .2s ease-out;
|
||||
|
||||
&:before {
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
|
||||
.tgico-close {
|
||||
left: auto;
|
||||
right: 0px;
|
||||
top: 48%;
|
||||
z-index: 1;
|
||||
}
|
||||
}
|
@ -870,6 +870,7 @@ img.emoji {
|
||||
|
||||
.animated-super {
|
||||
&-row {
|
||||
--translateY: 100%;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
@ -877,15 +878,23 @@ img.emoji {
|
||||
bottom: 0;
|
||||
transition: transform var(--pm-transition), opacity var(--pm-transition);
|
||||
|
||||
/* &:not(.is-hiding) {
|
||||
transform: none !important;
|
||||
} */
|
||||
|
||||
&.is-hiding {
|
||||
opacity: 0;
|
||||
|
||||
&.from-top {
|
||||
transform: translateY(-100%);
|
||||
transform: translate3d(0, calc(var(--translateY) * -1), 0);
|
||||
//transform: translateY(calc(var(--translateY) * -1));
|
||||
//transform: translateY(-100%);
|
||||
}
|
||||
|
||||
&.from-bottom {
|
||||
transform: translateY(100%);
|
||||
transform: translate3d(0, var(--translateY), 0);
|
||||
//transform: translateY(var(--translateY));
|
||||
//transform: translateY(100%);
|
||||
}
|
||||
|
||||
/* &.backwards {
|
||||
|
6
src/types.d.ts
vendored
6
src/types.d.ts
vendored
@ -35,6 +35,12 @@ export type Modify<T, R> = Omit<T, keyof R> & R;
|
||||
|
||||
export type ArgumentTypes<F extends Function> = F extends (...args: infer A) => any ? A : never;
|
||||
|
||||
export type AnyLiteral = Record<string, any>;
|
||||
export type AnyClass = new (...args: any[]) => any;
|
||||
export type AnyFunction = (...args: any) => any;
|
||||
export type AnyToVoidFunction = (...args: any) => void;
|
||||
export type NoneToVoidFunction = () => void;
|
||||
|
||||
export type AuthState = AuthState.signIn | AuthState.authCode | AuthState.password | AuthState.signUp | AuthState.signedIn;
|
||||
export namespace AuthState {
|
||||
export type signIn = {
|
||||
|
Loading…
Reference in New Issue
Block a user