Shared media sticky header

Shared media close icon animation
New left sidebar icon menu animation
Debounce opening right sidebar
Fix chat input overflow when multiselecting
Fix chat input height when ESG doesn't fit
This commit is contained in:
Eduard Kuzmenko 2021-02-15 19:49:58 +04:00
parent 307fa42c2b
commit 2d6d47f7e8
18 changed files with 327 additions and 91 deletions

View File

@ -80,7 +80,7 @@ export default class AppSearchSuper {
private searchGroupMedia: SearchGroup;
constructor(public types: {inputFilter: SearchSuperType, name: string}[], public scrollable: Scrollable, public searchGroups?: {[group in SearchGroupType]: SearchGroup}, public asChatList = false) {
constructor(public types: {inputFilter: SearchSuperType, name: string}[], public scrollable: Scrollable, public searchGroups?: {[group in SearchGroupType]: SearchGroup}, public asChatList = false, public groupByMonth = true) {
this.container = document.createElement('div');
this.container.classList.add('search-super');
@ -549,7 +549,7 @@ export default class AppSearchSuper {
const method = append ? 'append' : 'prepend';
elemsToAppend.forEach(details => {
const {element, message} = details;
const monthContainer = this.getMonthContainerByTimestamp(message.date, type);
const monthContainer = this.getMonthContainerByTimestamp(this.groupByMonth ? message.date : 0, type);
element.classList.add('search-super-item');
element.dataset.mid = '' + message.mid;
element.dataset.peerId = '' + message.peerId;

View File

@ -1,4 +1,4 @@
import { findUpTag, whichChild, findUpAsChild } from "../helpers/dom";
import { whichChild, findUpAsChild } from "../helpers/dom";
import { TransitionSlider } from "./transition";
import { ScrollableX } from "./scrollable";
import rootScope from "../lib/rootScope";
@ -105,4 +105,4 @@ export function horizontalMenu(tabs: HTMLElement, content: HTMLElement, onClick?
}
return selectTab;
}
}

View File

@ -52,6 +52,7 @@ export class ScrollableBase {
protected onScroll: () => void;
public isHeavyAnimationInProgress = false;
protected needCheckAfterAnimation = false;
constructor(public el: HTMLElement, logPrefix = '', public container: HTMLElement = document.createElement('div')) {
this.container.classList.add('scrollable');
@ -74,11 +75,16 @@ export class ScrollableBase {
this.isHeavyAnimationInProgress = true;
if(this.onScrollMeasure) {
this.needCheckAfterAnimation = true;
window.cancelAnimationFrame(this.onScrollMeasure);
}
}, () => {
this.isHeavyAnimationInProgress = false;
this.onScroll();
if(this.needCheckAfterAnimation) {
this.onScroll();
this.needCheckAfterAnimation = false;
}
});
}
@ -146,6 +152,7 @@ export default class Scrollable extends ScrollableBase {
window.cancelAnimationFrame(this.onScrollMeasure);
}
this.needCheckAfterAnimation = true;
return;
}

View File

@ -418,6 +418,7 @@ export class AppSidebarLeft extends SidebarSlider {
this.toolsBtn.classList.remove('active');
this.backBtn.classList.add('active');
this.newBtnMenu.classList.add('is-hidden');
this.toolsBtn.parentElement.firstElementChild.classList.toggle('state-back', true);
transition(1);
};
@ -428,6 +429,7 @@ export class AppSidebarLeft extends SidebarSlider {
this.backBtn.addEventListener('click', (e) => {
this.toolsBtn.classList.add('active');
this.backBtn.classList.remove('active');
this.toolsBtn.parentElement.firstElementChild.classList.toggle('state-back', false);
transition(0);
});

View File

@ -103,7 +103,7 @@ export class AppSidebarRight extends SidebarSlider {
this.selectTab(AppSidebarRight.SLIDERITEMSIDS.sharedMedia);
}
const transitionTime = rootScope.settings.animationsEnabled ? mediaSizes.isMobile ? 250 : 200 : 0;
const transitionTime = rootScope.settings.animationsEnabled ? (mediaSizes.isMobile ? 250 : 200) : 0;
const promise = pause(transitionTime);
if(transitionTime) {
dispatchHeavyAnimationEvent(promise, transitionTime);

View File

@ -11,6 +11,9 @@ import AvatarElement from "../../avatar";
import Scrollable from "../../scrollable";
import { SliderTab } from "../../slider";
import CheckboxField from "../../checkbox";
import { attachClickEvent, cancelEvent } from "../../../helpers/dom";
import appSidebarRight from "..";
import { TransitionSlider } from "../../transition";
let setText = (text: string, el: HTMLDivElement) => {
window.requestAnimationFrame(() => {
@ -29,7 +32,7 @@ let setText = (text: string, el: HTMLDivElement) => {
// TODO: отредактированное сообщение не изменится
export default class AppSharedMediaTab implements SliderTab {
public container: HTMLElement;
public closeBtn: HTMLElement;
public closeBtn: HTMLButtonElement;
private peerId = 0;
private threadId = 0;
@ -62,7 +65,8 @@ export default class AppSharedMediaTab implements SliderTab {
public init() {
this.container = document.getElementById('shared-media-container');
this.closeBtn = this.container.querySelector('.sidebar-close-button');
this.closeBtn = this.container.querySelector('.sidebar-header .btn-icon');
this.closeBtn.classList.add('sidebar-close-button');
this.profileContentEl = this.container.querySelector('.profile-content');
this.profileElements = {
@ -86,7 +90,37 @@ export default class AppSharedMediaTab implements SliderTab {
this.profileElements.notificationsRow.prepend(checkboxField.label);
this.scroll = new Scrollable(this.container, 'SR', 400);
const HEADER_HEIGHT = 56;
const closeIcon = this.closeBtn.firstElementChild as HTMLElement;
this.scroll.onAdditionalScroll = () => {
const rect = this.searchSuper.nav.getBoundingClientRect();
if(!rect.width) return;
//console.log('daddy issues', this.searchSuper.nav.getBoundingClientRect());
const top = rect.top;
const isSharedMedia = top <= HEADER_HEIGHT;
closeIcon.classList.toggle('state-back', isSharedMedia);
transition(+isSharedMedia);
};
const transition = TransitionSlider(this.closeBtn.nextElementSibling as HTMLElement, 'slide-fade', 400, null, false);
transition(0);
attachClickEvent(this.closeBtn, (e) => {
if(this.closeBtn.firstElementChild.classList.contains('state-back')) {
this.scroll.scrollIntoViewNew(this.scroll.container.firstElementChild as HTMLElement, 'start');
transition(0);
closeIcon.classList.remove('state-back');
} else if(!this.scroll.isHeavyAnimationInProgress) {
appSidebarRight.closeTab();
}
});
this.container.prepend(this.closeBtn.parentElement);
this.profileElements.notificationsCheckbox.addEventListener('change', () => {
//let checked = this.profileElements.notificationsCheckbox.checked;
appMessagesManager.mutePeer(this.peerId);
@ -134,7 +168,7 @@ export default class AppSharedMediaTab implements SliderTab {
}, {
inputFilter: 'inputMessagesFilterMusic',
name: 'Music'
}], this.scroll);
}], this.scroll/* , undefined, undefined, false */);
this.profileContentEl.append(this.searchSuper.container);
}

View File

@ -2,6 +2,7 @@ import { attachClickEvent } from "../helpers/dom";
import { horizontalMenu } from "./horizontalMenu";
import ButtonIcon from "./buttonIcon";
import Scrollable from "./scrollable";
import { TransitionSlider } from "./transition";
export interface SliderTab {
onOpen?: () => void,
@ -86,7 +87,7 @@ export default class SidebarSlider {
constructor(public sidebarEl: HTMLElement, public tabs: {[id: number]: SliderTab} = {}, private canHideFirst = false) {
this.tabsContainer = this.sidebarEl.querySelector('.sidebar-slider');
this._selectTab = horizontalMenu(null, this.tabsContainer as HTMLDivElement, null, null, TRANSITION_TIME);
this._selectTab = TransitionSlider(this.tabsContainer, 'navigation', TRANSITION_TIME);
if(!canHideFirst) {
this._selectTab(0);
}

View File

@ -46,7 +46,7 @@ function slideTabs(tabContent: HTMLElement, prevTabContent: HTMLElement, toRight
};
}
export const TransitionSlider = (content: HTMLElement, type: 'tabs' | 'navigation' | 'zoom-fade' | 'none'/* | 'counter' */, transitionTime: number, onTransitionEnd?: (id: number) => void) => {
export const TransitionSlider = (content: HTMLElement, type: 'tabs' | 'navigation' | 'zoom-fade' | 'slide-fade' | 'none'/* | 'counter' */, transitionTime: number, onTransitionEnd?: (id: number) => void, isHeavy = true) => {
let animationFunction: TransitionFunction = null;
switch(type) {
@ -62,12 +62,12 @@ export const TransitionSlider = (content: HTMLElement, type: 'tabs' | 'navigatio
content.dataset.animation = type;
return Transition(content, animationFunction, transitionTime, onTransitionEnd);
return Transition(content, animationFunction, transitionTime, onTransitionEnd, isHeavy);
};
type TransitionFunction = (tabContent: HTMLElement, prevTabContent: HTMLElement, toRight: boolean) => void | (() => void);
const Transition = (content: HTMLElement, animationFunction: TransitionFunction, transitionTime: number, onTransitionEnd?: (id: number) => void) => {
const Transition = (content: HTMLElement, animationFunction: TransitionFunction, transitionTime: number, onTransitionEnd?: (id: number) => void, isHeavy = true) => {
const onTransitionEndCallbacks: Map<HTMLElement, Function> = new Map();
let animationDeferred: CancellablePromise<void>;
let animationStarted = 0;
@ -84,10 +84,16 @@ const Transition = (content: HTMLElement, animationFunction: TransitionFunction,
const callback = onTransitionEndCallbacks.get(e.target as HTMLElement);
if(callback) callback();
if(!animationDeferred || e.target !== from) return;
if(e.target !== from) {
return;
}
animationDeferred.resolve();
animationDeferred = undefined;
if(!animationDeferred && isHeavy) return;
if(animationDeferred) {
animationDeferred.resolve();
animationDeferred = undefined;
}
if(onTransitionEnd) {
onTransitionEnd(selectTab.prevId);
@ -180,12 +186,14 @@ const Transition = (content: HTMLElement, animationFunction: TransitionFunction,
});
}
if(!animationDeferred) {
animationDeferred = deferredPromise<void>();
animationStarted = performance.now();
if(isHeavy) {
if(!animationDeferred) {
animationDeferred = deferredPromise<void>();
animationStarted = performance.now();
}
dispatchHeavyAnimationEvent(animationDeferred, transitionTime * 2);
}
dispatchHeavyAnimationEvent(animationDeferred, transitionTime * 2);
}
self.prevId = id;

View File

@ -1,10 +1,12 @@
import DEBUG from '../config/debug';
import _DEBUG from '../config/debug';
import fastBlur from '../vendor/fastBlur';
import pushHeavyTask from './heavyQueue';
const RADIUS = 2;
const ITERATIONS = 2;
const DEBUG = _DEBUG && false;
function processBlur(dataUri: string, radius: number, iterations: number) {
return new Promise<string>((resolve) => {
const img = new Image();

View File

@ -43,7 +43,7 @@ export default function fastSmoothScroll(
return Promise.resolve(); */
}
if(axis === 'y' && isInDOM(element) && container.getBoundingClientRect) {
if(axis === 'y' && element !== container && isInDOM(element) && container.getBoundingClientRect) {
const elementRect = element.getBoundingClientRect();
const containerRect = container.getBoundingClientRect();

View File

@ -98,17 +98,18 @@
<div class="sidebar-slider-item item-main">
<div class="sidebar-header">
<div class="sidebar-header__btn-container">
<div class="btn-icon tgico-menu btn-menu-toggle rp sidebar-tools-button active">
<div class="animated-menu-icon"></div>
<div class="btn-icon btn-menu-toggle rp sidebar-tools-button active">
<div class="btn-menu bottom-right">
<div class="btn-menu-item menu-newGroup tgico-newgroup rp">New Group</div>
<div class="btn-menu-item menu-contacts tgico-user rp">Contacts</div>
<div class="btn-menu-item menu-archived tgico-archive rp">Archived <span class="archived-count"></span></div>
<div class="btn-menu-item menu-archived tgico-archive rp">Archived <span class="archived-count badge badge-24 badge-gray"></span></div>
<div class="btn-menu-item menu-saved tgico-savedmessages rp">Saved</div>
<div class="btn-menu-item menu-settings tgico-settings rp">Settings</div>
<div class="btn-menu-item menu-help tgico-help rp btn-disabled">Help</div>
</div>
</div>
<div class="btn-icon tgico-arrow_back rp sidebar-back-button"></div>
<div class="btn-icon rp sidebar-back-button"></div>
</div>
</div>
<div class="sidebar-content transition zoom-fade">
@ -171,9 +172,18 @@
<div class="sidebar-content sidebar-slider tabs-container">
<div class="sidebar-slider-item profile-container" id="shared-media-container">
<div class="sidebar-header">
<button class="btn-icon tgico sidebar-close-button"></button>
<div class="sidebar-header__title">Info</div>
<div class="btn-icon tgico-more"></div>
<button class="btn-icon">
<div class="animated-close-icon"></div>
</button>
<div class="transition slide-fade">
<div class="transition-item">
<div class="sidebar-header__title">Info</div>
<div class="btn-icon tgico-more"></div>
</div>
<div class="transition-item">
<div class="sidebar-header__title">Shared Media</div>
</div>
</div>
</div>
<div class="profile-content">
<div class="profile-content-wrapper">

View File

@ -518,7 +518,13 @@ export class AppImManager {
if(prevTabId !== -1 && prevTabId !== id && rootScope.settings.animationsEnabled) {
const transitionTime = (mediaSizes.isMobile ? 250 : 200) + 100; // * cause transition time could be > 250ms
dispatchHeavyAnimationEvent(pause(transitionTime), transitionTime);
const promise = pause(transitionTime);
dispatchHeavyAnimationEvent(promise, transitionTime);
this.columnEl.classList.add('disable-hover');
promise.finally(() => {
this.columnEl.classList.remove('disable-hover');
});
}
this.tabId = id;

View File

@ -1,5 +1,5 @@
import {isObject} from './bin_utils';
import { bigStringInt} from './bin_utils';
import {bigStringInt} from './bin_utils';
import {TLDeserialization, TLSerialization} from './tl_utils';
import CryptoWorker from '../crypto/cryptoworker';
import sessionStorage from '../sessionStorage';
@ -11,19 +11,13 @@ import { InvokeApiOptions } from '../../types';
import { longToBytes } from '../crypto/crypto_utils';
import MTTransport from './transports/transport';
import { convertToUint8Array, bufferConcat, bytesCmp, bytesToHex } from '../../helpers/bytes';
import { nextRandomInt, randomLong } from '../../helpers/random';
import { CancellablePromise, deferredPromise } from '../../helpers/cancellablePromise';
import { isSafari } from '../../helpers/userAgent';
import { nextRandomInt } from '../../helpers/random';
import { CancellablePromise } from '../../helpers/cancellablePromise';
import App from '../../config/app';
import DEBUG from '../../config/debug';
import Modes from '../../config/modes';
import Obfuscation from './transports/obfuscation';
/// #if MTPROTO_HTTP_UPLOAD
// @ts-ignore
import HTTP from './transports/http';
/// #elif MTPROTO_HTTP
// @ts-ignore
/// #if MTPROTO_HTTP_UPLOAD || MTPROTO_HTTP
import HTTP from './transports/http';
/// #endif
@ -120,8 +114,6 @@ export default class MTPNetworker {
private debugRequests: Array<{before: Uint8Array, after: Uint8Array}> = [];
private obfuscation: Obfuscation;
constructor(public dcId: number, private authKey: number[], private authKeyId: Uint8Array,
serverSalt: number[], private transport: MTTransport, options: InvokeApiOptions = {}) {
this.authKeyUint8 = convertToUint8Array(this.authKey);
@ -715,7 +707,7 @@ export default class MTPNetworker {
// * correct, fully checked
public async getMsgKey(dataWithPadding: ArrayBuffer, isOut: boolean) {
const x = isOut ? 0 : 8
const x = isOut ? 0 : 8;
const msgKeyLargePlain = bufferConcat(this.authKeyUint8.subarray(88 + x, 88 + x + 32), dataWithPadding);
const msgKeyLarge = await CryptoWorker.sha256Hash(msgKeyLargePlain);
@ -980,13 +972,13 @@ export default class MTPNetworker {
data.storeInt(message.body.length, 'message_data_length');
data.storeRawBytes(message.body, 'message_data');
const des = new TLDeserialization(data.getBuffer().slice(16));
/* const des = new TLDeserialization(data.getBuffer().slice(16));
const desSalt = des.fetchLong();
const desSessionId = des.fetchLong();
if(!this.isOnline) {
this.log.error('trying to send message when offline', message, new Uint8Array(des.buffer), desSalt, desSessionId);
}
} */
/* const messageDataLength = message.body.length;
let canBeLength = 0; // bytes
@ -1004,14 +996,14 @@ export default class MTPNetworker {
} */
const paddingLength = (16 - (data.offset % 16)) + 16 * (1 + nextRandomInt(5));
const padding = (message as any).padding || new Uint8Array(paddingLength).randomize()/* .fill(0) */;
const padding = /* (message as any).padding || */new Uint8Array(paddingLength).randomize()/* .fill(0) */;
/* const padding = [167, 148, 207, 226, 86, 192, 193, 57, 124, 153, 174, 145, 159, 1, 5, 70, 127, 157,
51, 241, 46, 85, 141, 212, 139, 234, 213, 164, 197, 116, 245, 70, 184, 40, 40, 201, 233, 211, 150,
94, 57, 84, 1, 135, 108, 253, 34, 139, 222, 208, 71, 214, 90, 67, 36, 28, 167, 148, 207, 226, 86, 192, 193, 57, 124, 153, 174, 145, 159, 1, 5, 70, 127, 157,
51, 241, 46, 85, 141, 212, 139, 234, 213, 164, 197, 116, 245, 70, 184, 40, 40, 201, 233, 211, 150,
94, 57, 84, 1, 135, 108, 253, 34, 139, 222, 208, 71, 214, 90, 67, 36, 28].slice(0, paddingLength); */
(message as any).padding = padding;
//(message as any).padding = padding;
const dataWithPadding = bufferConcat(dataBuffer, padding);
// this.log('Adding padding', dataBuffer, padding, dataWithPadding)
@ -1040,12 +1032,12 @@ export default class MTPNetworker {
const requestData = request.getBytes(true);
if(this.isFileNetworker) {
//this.log('Send encrypted: requestData length:', requestData.length, requestData.length % 16, paddingLength % 16, paddingLength, data.offset, encryptedResult.msgKey.length % 16, encryptedResult.bytes.length % 16);
//this.log('Send encrypted: messageId:', message.msg_id, requestData.length);
//this.log('Send encrypted:', message, new Uint8Array(bufferConcat(des.buffer, padding)), requestData, this.serverSalt.hex, this.sessionId.hex/* new Uint8Array(des.buffer) */);
this.debugRequests.push({before: new Uint8Array(bufferConcat(des.buffer, padding)), after: requestData});
}
// if(this.isFileNetworker) {
// //this.log('Send encrypted: requestData length:', requestData.length, requestData.length % 16, paddingLength % 16, paddingLength, data.offset, encryptedResult.msgKey.length % 16, encryptedResult.bytes.length % 16);
// //this.log('Send encrypted: messageId:', message.msg_id, requestData.length);
// //this.log('Send encrypted:', message, new Uint8Array(bufferConcat(des.buffer, padding)), requestData, this.serverSalt.hex, this.sessionId.hex/* new Uint8Array(des.buffer) */);
// this.debugRequests.push({before: new Uint8Array(bufferConcat(des.buffer, padding)), after: requestData});
// }
return requestData;
});

View File

@ -0,0 +1,81 @@
.animated-close-icon {
position: absolute;
transform: rotate(-45deg);
pointer-events: none;
&, &:before, &:after {
width: 1.125rem;
height: .125rem;
border-radius: .125rem;
background-color: var(--color-text-secondary);
transition: transform var(--slide-header-transition);
}
&:before, &:after {
position: absolute;
left: 0;
top: 0;
content: "";
}
&:before {
transform: rotate(90deg);
}
&.no-transition {
&, &:before, &:after {
transition: none;
}
}
&.state-back {
transform: rotate(180deg);
&:before {
transform: rotate(45deg) scaleX(.75) translateY(-.375rem);
}
&:after {
transform: rotate(-45deg) scaleX(.75) translateY(.375rem);
}
}
}
.animated-menu-icon {
position: absolute;
&, &:before, &:after {
width: 1.125rem;
height: .125rem;
border-radius: .125rem;
background-color: var(--color-text-secondary);
transition: transform .25s;
transform: rotate(0);
}
&:before, &:after {
position: absolute;
left: 0;
content: "";
}
&:before {
top: -.3125rem;
}
&:after {
top: .3125rem;
}
&.state-back {
transform: rotate(180deg);
&:before {
transform: rotate(45deg) scaleX(.75) translate(.375rem, -.1875rem);
}
&:after {
transform: rotate(-45deg) scaleX(.75) translate(.375rem, .1875rem);
}
}
}

View File

@ -84,11 +84,9 @@ $chat-helper-size: 39px;
--padding-horizontal: #{$chat-padding};
}
@include respond-to(esg-bottom) {
.btn-circle {
height: $chat-input-handhelds-size;
width: $chat-input-handhelds-size;
}
.btn-circle {
width: var(--chat-input-size);
height: var(--chat-input-size);
}
}
@ -376,6 +374,10 @@ $chat-helper-size: 39px;
transition: .2s transform;
}
}
.new-message-wrapper {
pointer-events: none;
}
}
.bubbles.is-selecting:not(.backwards) ~ & {
@ -623,7 +625,7 @@ $chat-helper-size: 39px;
border-radius: 12px;
border-bottom-right-radius: 0;
//box-shadow: 0 1px 2px 0 rgba(16, 35, 47, .07);
box-shadow: 0px 1px 8px 1px rgba(0, 0, 0, 0.18);
box-shadow: 0px 1px 8px 1px rgba(0, 0, 0, .18);
min-height: var(--chat-input-size);
max-height: 30rem;
flex: 0 0 auto;
@ -728,7 +730,6 @@ $chat-helper-size: 39px;
@include respond-to(esg-bottom) {
--padding-vertical: .5px;
--padding-horizontal: .5rem;
min-height: $chat-input-handhelds-size;
}
&:after {

View File

@ -57,6 +57,7 @@
border-bottom: 1px solid #dadce0;
position: relative;
box-shadow: 0px 1px 5px -1px rgba(0, 0, 0, .16);
top: unset;
.scrollable {
position: relative;
@ -198,47 +199,39 @@
.sidebar-header__btn-container {
position: relative;
width: 40px;
height: 40px;
width: 2.5rem;
height: 2.5rem;
flex: 0 0 auto;
display: flex;
align-items: center;
justify-content: center;
> .btn-icon {
visibility: hidden;
opacity: 0;
position: absolute;
left: 0;
top: 0;
right: 0;
bottom: 0;
left: 0;
transition: .2s opacity, .15s background-color;
z-index: 2;
&:before {
transition: .2s transform;
transform: rotate(180deg);
}
margin: 0;
body.animation-level-0 &, body.animation-level-0 &:before {
transition: none;
}
& + * {
margin-left: 0!important;
}
&.active {
//margin-top: 1px;
opacity: 1;
visibility: visible;
color: #707579;
&:before {
transform: rotate(0deg);
}
}
}
.btn-menu {
@include respond-to(handhelds) {
margin-top: -4px;
margin-top: -.25rem;
}
}
}
@ -247,20 +240,10 @@
min-width: 217px;
.archived-count {
border-radius: 12px;
min-width: 24px;
padding: 0 8px;
height: 24px;
text-align: center;
line-height: 24px;
color: #fff;
font-weight: 500;
background-color: #c5c9cc;
justify-self: flex-end;
margin-left: .625rem;
@include respond-to(handhelds) {
font-size: 14px;
font-weight: 600;
}
}

View File

@ -47,7 +47,7 @@
.sidebar-header {
flex: 0 0 auto;
.sidebar-close-button:before {
.sidebar-close-button.tgico:before {
content: $tgico-arrow_back;
@include respond-to(not-handhelds) {
@ -235,6 +235,15 @@
max-height: calc((var(--vh, 1vh) * 100) - 100% - 56px);
}
}
.sidebar-header .transition {
flex-grow: 1;
.transition-item {
display: flex;
align-items: center;
}
}
}
.search-super {
@ -278,7 +287,7 @@
box-shadow: none !important;
position: -webkit-sticky !important;
position: sticky !important;
top: 0;
top: -1px;
z-index: 2;
background-color: #fff;

View File

@ -47,8 +47,10 @@ $chat-padding-handhelds: .5rem;
--color-gray-hover: #{hover-color($color-gray)};
--color-blue-hover: #{hover-color($color-blue)};
--color-red-hover: #{hover-color($color-red)};
--color-text-secondary: #{$color-gray};
--pm-transition: .2s ease-in-out;
--layer-transition: .2s ease-in-out;
--slide-header-transition: .4s ease-in-out;
--tabs-transition: .25s ease-in-out;
//--layer-transition: .3s cubic-bezier(.33, 1, .68, 1);
//--layer-transition: none;
@ -93,6 +95,10 @@ $chat-padding-handhelds: .5rem;
--chat-input-padding: #{$chat-padding};
}
@include respond-to(esg-bottom) {
--chat-input-size: #{$chat-input-handhelds-size};
}
@include respond-to(only-medium-screens) {
--right-column-width: 25vw;
}
@ -101,6 +107,7 @@ $chat-padding-handhelds: .5rem;
@import "partials/ico";
@import "partials/input";
@import "partials/button";
@import "partials/animatedIcon";
@import "partials/badge";
@import "partials/checkbox";
@import "partials/chatlist";
@ -847,6 +854,9 @@ img.emoji {
}
.transition {
--easeOutSine: cubic-bezier(.39, .575, .565, 1);
--easeInSine: cubic-bezier(.47, 0, .745, .715);
.transition-item {
position: absolute;
top: 0;
@ -905,6 +915,57 @@ img.emoji {
}
}
}
/*
* slide-fade
*/
&.slide-fade {
position: relative;
> .from {
transform-origin: left center;
transform: translateX(0);
opacity: 1;
}
> .to {
transform-origin: left center;
transform: translateX(1.5rem);
opacity: 0;
}
&.animating {
> .from {
animation: fade-out-opacity .4s ease-out, slide-fade-out-move .4s;
}
> .to {
animation: fade-in-opacity .4s var(--easeInSine), slide-fade-in-move .4s;
}
}
}
&.slide-fade.backwards {
> .from {
transform: translateX(0);
opacity: 1;
}
> .to {
transform: translateX(-1.5rem);
opacity: 0;
}
&.animating {
> .from {
animation: fade-in-backwards-opacity .4s ease-out, slide-fade-in-backwards-move .4s;
}
> .to {
animation: fade-out-backwards-opacity .4s var(--easeOutSine), slide-fade-out-backwards-move .4s;
}
}
}
}
/*
@ -937,6 +998,45 @@ img.emoji {
}
}
/*
* slide-fade
*/
@keyframes slide-fade-in-move {
0% {
transform: translateX(1.5rem);
}
100% {
transform: translateX(0);
}
}
@keyframes slide-fade-out-move {
0% {
transform: translateX(0);
}
100% {
transform: translateX(-1.5rem);
}
}
@keyframes slide-fade-in-backwards-move {
0% {
transform: translateX(0);
}
100% {
transform: translateX(1.5rem);
}
}
@keyframes slide-fade-out-backwards-move {
0% {
transform: translateX(-1.5rem);
}
100% {
transform: translateX(0);
}
}
/* .zoom-fade {
transition: .15s ease-in-out opacity, .15s ease-in-out transform;
transform: scale3d(1.1, 1.1, 1);