Eduard Kuzmenko
4 years ago
24 changed files with 3004 additions and 2020 deletions
@ -0,0 +1,388 @@
@@ -0,0 +1,388 @@
|
||||
import type { AppImManager } from "../../lib/appManagers/appImManager"; |
||||
import type { AppMessagesManager } from "../../lib/appManagers/appMessagesManager"; |
||||
import { ScreenSize } from "../../helpers/mediaSizes"; |
||||
import appPeersManager from "../../lib/appManagers/appPeersManager"; |
||||
import PopupPinMessage from "../popupUnpinMessage"; |
||||
import PinnedContainer from "./pinnedContainer"; |
||||
import PinnedMessageBorder from "./pinnedMessageBorder"; |
||||
import ReplyContainer, { wrapReplyDivAndCaption } from "./replyContainer"; |
||||
import rootScope from "../../lib/rootScope"; |
||||
import { findUpClassName } from "../../helpers/dom"; |
||||
|
||||
class AnimatedSuper { |
||||
static DURATION = 200; |
||||
static BASE_CLASS = 'animated-super'; |
||||
container: HTMLDivElement; |
||||
rows: {[index: string]: {element: HTMLElement, timeout?: number, new?: true}} = {}; |
||||
clearTimeout: number; |
||||
|
||||
constructor() { |
||||
this.container = document.createElement('div'); |
||||
this.container.className = AnimatedSuper.BASE_CLASS; |
||||
} |
||||
|
||||
public getRow(index: number, animateFirst = false) { |
||||
if(this.rows[index]) return this.rows[index].element; |
||||
const row = document.createElement('div'); |
||||
const isFirst = !Object.keys(this.rows).length && !animateFirst; |
||||
row.className = AnimatedSuper.BASE_CLASS + '-row' + (isFirst ? '' : ' is-hiding hide'); |
||||
this.rows[index] = {element: row, new: true}; |
||||
this.container.append(row); |
||||
return row; |
||||
} |
||||
|
||||
public clearRow(index: number) { |
||||
if(!this.rows[index]) return; |
||||
this.rows[index].element.remove(); |
||||
delete this.rows[index]; |
||||
} |
||||
|
||||
public clearRows(currentIndex?: number) { |
||||
if(this.clearTimeout) clearTimeout(this.clearTimeout); |
||||
this.clearTimeout = window.setTimeout(() => { |
||||
for(const i in this.rows) { |
||||
if(+i === currentIndex) continue; |
||||
this.clearRow(+i); |
||||
} |
||||
}, AnimatedSuper.DURATION); |
||||
} |
||||
|
||||
public /* async */ animate(index: number, previousIndex: number, fromTop = index > previousIndex, ignorePrevious = false) { |
||||
if(index == previousIndex) { |
||||
this.clearRows(index); |
||||
return; |
||||
} |
||||
|
||||
//const fromTop = index > previousIndex;
|
||||
|
||||
const row = this.rows[index]; |
||||
const previousRow = this.rows[previousIndex]; |
||||
//const height = this.container.getBoundingClientRect().height;
|
||||
|
||||
if(!previousRow && !ignorePrevious) { |
||||
if(row.new) { |
||||
row.element.classList.remove('hide'); |
||||
} |
||||
|
||||
return; |
||||
} |
||||
|
||||
const sides = ['from-top', 'from-bottom']; |
||||
if(!fromTop) sides.reverse(); |
||||
|
||||
row.element.classList.add(sides[0]); |
||||
row.element.classList.remove(sides[1]); |
||||
if(previousRow) { |
||||
previousRow.element.classList.add(sides[1]); |
||||
previousRow.element.classList.remove(sides[0]); |
||||
} |
||||
|
||||
if(row.new) { |
||||
//await new Promise((resolve) => window.requestAnimationFrame(resolve));
|
||||
row.element.classList.remove('hide'); |
||||
void row.element.offsetLeft; // reflow
|
||||
delete row.new; |
||||
//await new Promise((resolve) => window.requestAnimationFrame(resolve));
|
||||
} |
||||
|
||||
row.element.classList.toggle('is-hiding', false); |
||||
previousRow && previousRow.element.classList.toggle('is-hiding', true); |
||||
//SetTransition(row.element, 'is-hiding', false, AnimatedSuper.DURATION);
|
||||
//previousRow && SetTransition(previousRow.element, 'is-hiding', true, AnimatedSuper.DURATION);
|
||||
|
||||
this.clearRows(index); |
||||
} |
||||
} |
||||
|
||||
class AnimatedCounter { |
||||
static BASE_CLASS = 'animated-counter'; |
||||
container: HTMLElement; |
||||
decimals: { |
||||
container: HTMLElement, |
||||
placeholder: HTMLElement, |
||||
animatedSuper: AnimatedSuper |
||||
}[] = []; |
||||
previousNumber = 0; |
||||
clearTimeout: number; |
||||
|
||||
constructor(private reverse = false) { |
||||
this.container = document.createElement('div'); |
||||
this.container.className = AnimatedCounter.BASE_CLASS; |
||||
} |
||||
|
||||
getDecimal(index: number) { |
||||
if(this.decimals[index]) return this.decimals[index]; |
||||
const item = document.createElement('div'); |
||||
item.className = AnimatedCounter.BASE_CLASS + '-decimal'; |
||||
|
||||
const placeholder = document.createElement('div'); |
||||
placeholder.className = AnimatedCounter.BASE_CLASS + '-decimal-placeholder'; |
||||
|
||||
const animatedSuper = new AnimatedSuper(); |
||||
animatedSuper.container.className = AnimatedCounter.BASE_CLASS + '-decimal-wrapper'; |
||||
|
||||
item.append(placeholder, animatedSuper.container); |
||||
|
||||
this.container.append(item); |
||||
|
||||
return this.decimals[index] = {container: item, placeholder, animatedSuper}; |
||||
} |
||||
|
||||
clear(number: number) { |
||||
if(this.clearTimeout) clearTimeout(this.clearTimeout); |
||||
|
||||
const decimals = ('' + number).length; |
||||
if(decimals >= this.decimals.length) { |
||||
return; |
||||
} |
||||
|
||||
this.clearTimeout = window.setTimeout(() => { |
||||
const byDecimal = this.decimals.splice(decimals, this.decimals.length - decimals); |
||||
byDecimal.forEach((decimal) => { |
||||
decimal.container.remove(); |
||||
}); |
||||
}, AnimatedSuper.DURATION); |
||||
} |
||||
|
||||
/* prepareNumber(number: number) { |
||||
const decimals = ('' + number).length; |
||||
if(this.decimals.length < decimals) { |
||||
for(let i = this.decimals.length; i < decimals; ++i) { |
||||
this.getDecimal(i); |
||||
} |
||||
} |
||||
} */ |
||||
|
||||
hideLeft(number: number) { |
||||
const decimals = ('' + number).length; |
||||
const byDecimal = this.decimals.slice(decimals);//this.decimals.splice(deleteCount, this.decimals.length - deleteCount);
|
||||
const EMPTY_INDEX = 0; |
||||
byDecimal.forEach((decimal) => { |
||||
const row = decimal.animatedSuper.getRow(EMPTY_INDEX, true); |
||||
decimal.animatedSuper.animate(EMPTY_INDEX, this.previousNumber, this.reverse ? number < this.previousNumber : number > this.previousNumber, true); |
||||
//decimal.container.remove();
|
||||
//decimal.animatedSuper.clearRows();
|
||||
}); |
||||
|
||||
this.clear(number); |
||||
} |
||||
|
||||
setCount(number: number) { |
||||
//this.prepareNumber(number);
|
||||
|
||||
const byDecimal = Array.from('' + number).map(n => +n); |
||||
byDecimal.forEach((decimalNumber, idx) => { |
||||
const decimal = this.getDecimal(idx); |
||||
const row = decimal.animatedSuper.getRow(number, true); |
||||
row.innerText = decimal.placeholder.innerText = '' + decimalNumber; |
||||
decimal.animatedSuper.animate(number, this.previousNumber, this.reverse ? number < this.previousNumber : number > this.previousNumber, true); |
||||
}); |
||||
|
||||
/* const sides = ['from-top', 'from-bottom']; |
||||
if(this.reverse) { |
||||
sides.reverse(); |
||||
} |
||||
|
||||
const isHigher = number > this.previousNumber; |
||||
if(!isHigher) { |
||||
sides.reverse(); |
||||
} |
||||
|
||||
this.container.classList.add(sides[0]); |
||||
this.container.classList.remove(sides[1]); */ |
||||
|
||||
this.hideLeft(number); |
||||
//this.clear(number);
|
||||
this.previousNumber = number; |
||||
} |
||||
} |
||||
|
||||
export default class PinnedMessage { |
||||
public pinnedMessageContainer: PinnedContainer; |
||||
public pinnedMessageBorder: PinnedMessageBorder; |
||||
public pinnedIndex = 0; |
||||
public wasPinnedIndex = 0; |
||||
public locked = false; |
||||
public waitForScrollBottom = false; |
||||
|
||||
public animatedSubtitle: AnimatedSuper; |
||||
public animatedMedia: AnimatedSuper; |
||||
public animatedCounter: AnimatedCounter; |
||||
|
||||
constructor(private appImManager: AppImManager, private appMessagesManager: AppMessagesManager) { |
||||
this.pinnedMessageContainer = new PinnedContainer('message', new ReplyContainer('pinned-message'), () => { |
||||
if(appPeersManager.canPinMessage(this.appImManager.peerID)) { |
||||
new PopupPinMessage(this.appImManager.peerID, 0); |
||||
return Promise.resolve(false); |
||||
} |
||||
}); |
||||
|
||||
this.pinnedMessageBorder = new PinnedMessageBorder(); |
||||
this.pinnedMessageContainer.divAndCaption.border.replaceWith(this.pinnedMessageBorder.render(1, 0)); |
||||
this.appImManager.btnJoin.parentElement.insertBefore(this.pinnedMessageContainer.divAndCaption.container, this.appImManager.btnJoin); |
||||
|
||||
this.animatedSubtitle = new AnimatedSuper(); |
||||
this.pinnedMessageContainer.divAndCaption.subtitle.append(this.animatedSubtitle.container); |
||||
|
||||
this.animatedMedia = new AnimatedSuper(); |
||||
this.animatedMedia.container.classList.add('pinned-message-media-container'); |
||||
this.pinnedMessageContainer.divAndCaption.content.prepend(this.animatedMedia.container); |
||||
|
||||
this.animatedCounter = new AnimatedCounter(true); |
||||
this.pinnedMessageContainer.divAndCaption.title.innerHTML = 'Pinned Message '; |
||||
this.pinnedMessageContainer.divAndCaption.title.append(this.animatedCounter.container); |
||||
|
||||
rootScope.on('peer_pinned_messages', (e) => { |
||||
const peerID = e.detail; |
||||
|
||||
if(peerID == this.appImManager.peerID) { |
||||
this.setPinnedMessage(); |
||||
} |
||||
}); |
||||
} |
||||
|
||||
public setCorrectIndex(lastScrollDirection?: number) { |
||||
if(this.locked) { |
||||
return; |
||||
}/* else if(this.waitForScrollBottom) { |
||||
if(lastScrollDirection === 1) { |
||||
this.waitForScrollBottom = false; |
||||
} else { |
||||
return; |
||||
} |
||||
} */ |
||||
|
||||
///const perf = performance.now();
|
||||
const rect = this.appImManager.scrollable.container.getBoundingClientRect(); |
||||
const x = Math.ceil(rect.left + ((rect.right - rect.left) / 2) + 1); |
||||
const y = Math.floor(rect.top + rect.height - 1); |
||||
let el: HTMLElement = document.elementFromPoint(x, y) as any; |
||||
//this.appImManager.log('[PM]: setCorrectIndex: get last element perf:', performance.now() - perf, el, x, y);
|
||||
el = findUpClassName(el, 'bubble'); |
||||
|
||||
if(el && el.dataset.mid !== undefined) { |
||||
const mid = +el.dataset.mid; |
||||
this.appMessagesManager.getPinnedMessages(this.appImManager.peerID).then(mids => { |
||||
let currentIndex = mids.findIndex(_mid => _mid <= mid); |
||||
if(currentIndex === -1) { |
||||
currentIndex = mids.length ? mids.length - 1 : 0; |
||||
} |
||||
|
||||
this.appImManager.log('pinned currentIndex', currentIndex); |
||||
|
||||
const changed = this.pinnedIndex != currentIndex; |
||||
if(changed) { |
||||
if(this.waitForScrollBottom) { |
||||
if(lastScrollDirection === 1) { // если проскроллил вниз - разблокировать
|
||||
this.waitForScrollBottom = false; |
||||
} else if(this.pinnedIndex > currentIndex) { // если не скроллил вниз и пытается поставить нижний пиннед - выйти
|
||||
return; |
||||
} |
||||
} |
||||
|
||||
this.pinnedIndex = currentIndex; |
||||
this.setPinnedMessage(); |
||||
} |
||||
}); |
||||
} |
||||
} |
||||
|
||||
public async followPinnedMessage(mid: number) { |
||||
const message = this.appMessagesManager.getMessage(mid); |
||||
if(message && !message.deleted) { |
||||
this.locked = true; |
||||
|
||||
try { |
||||
const mids = await this.appMessagesManager.getPinnedMessages(message.peerID); |
||||
const index = mids.indexOf(mid); |
||||
|
||||
this.pinnedIndex = index >= (mids.length - 1) ? 0 : index + 1; |
||||
this.setPinnedMessage(); |
||||
|
||||
const setPeerPromise = this.appImManager.setPeer(message.peerID, mid); |
||||
if(setPeerPromise instanceof Promise) { |
||||
await setPeerPromise; |
||||
} |
||||
|
||||
await this.appImManager.scrollable.scrollLockedPromise; |
||||
} catch(err) { |
||||
this.appImManager.log.error('[PM]: followPinnedMessage error:', err); |
||||
} |
||||
|
||||
// подождём, пока скролл остановится
|
||||
setTimeout(() => { |
||||
this.locked = false; |
||||
this.waitForScrollBottom = true; |
||||
}, 50); |
||||
} |
||||
} |
||||
|
||||
public onChangeScreen(from: ScreenSize, to: ScreenSize) { |
||||
this.pinnedMessageContainer.divAndCaption.container.classList.toggle('is-floating', to == ScreenSize.mobile |
||||
/* || (!this.chatAudio.divAndCaption.container.classList.contains('hide') && to == ScreenSize.medium) */); |
||||
} |
||||
|
||||
public setPinnedMessage() { |
||||
/////this.log('setting pinned message', message);
|
||||
//return;
|
||||
const promise: Promise<any> = this.appImManager.setPeerPromise || this.appImManager.messagesQueuePromise || Promise.resolve(); |
||||
Promise.all([ |
||||
this.appMessagesManager.getPinnedMessages(this.appImManager.peerID), |
||||
promise |
||||
]).then(([mids]) => { |
||||
//const mids = results[0];
|
||||
if(mids.length) { |
||||
const pinnedIndex = this.pinnedIndex >= mids.length ? mids.length - 1 : this.pinnedIndex; |
||||
const message = this.appMessagesManager.getMessage(mids[pinnedIndex]); |
||||
|
||||
//this.animatedCounter.prepareNumber(mids.length);
|
||||
|
||||
//setTimeout(() => {
|
||||
const isLast = pinnedIndex === 0; |
||||
this.animatedCounter.container.classList.toggle('is-last', isLast); |
||||
//SetTransition(this.animatedCounter.container, 'is-last', isLast, AnimatedSuper.DURATION);
|
||||
if(!isLast) { |
||||
this.animatedCounter.setCount(mids.length - pinnedIndex); |
||||
} |
||||
//}, 100);
|
||||
|
||||
//this.pinnedMessageContainer.fill(undefined, message.message, message);
|
||||
this.pinnedMessageContainer.toggle(false); |
||||
|
||||
const fromTop = pinnedIndex > this.wasPinnedIndex; |
||||
|
||||
this.appImManager.log('[PM]: setPinnedMessage: fromTop', fromTop, pinnedIndex, this.wasPinnedIndex); |
||||
|
||||
const writeTo = this.animatedSubtitle.getRow(pinnedIndex); |
||||
const writeMediaTo = this.animatedMedia.getRow(pinnedIndex); |
||||
writeMediaTo.classList.add('pinned-message-media'); |
||||
const isMediaSet = wrapReplyDivAndCaption({ |
||||
title: undefined, |
||||
titleEl: null, |
||||
subtitle: message.message, |
||||
subtitleEl: writeTo, |
||||
message, |
||||
mediaEl: writeMediaTo |
||||
}); |
||||
|
||||
this.pinnedMessageContainer.divAndCaption.container.classList.toggle('is-media', isMediaSet); |
||||
|
||||
if(this.wasPinnedIndex != this.pinnedIndex) { |
||||
this.animatedSubtitle.animate(pinnedIndex, this.wasPinnedIndex); |
||||
if(isMediaSet) { |
||||
this.animatedMedia.animate(pinnedIndex, this.wasPinnedIndex); |
||||
} else { |
||||
this.animatedMedia.clearRows(); |
||||
} |
||||
} |
||||
|
||||
this.pinnedMessageBorder.render(mids.length, mids.length - pinnedIndex - 1); |
||||
this.wasPinnedIndex = pinnedIndex; |
||||
this.pinnedMessageContainer.divAndCaption.container.dataset.mid = '' + message.mid; |
||||
} else { |
||||
this.pinnedMessageContainer.toggle(true); |
||||
this.wasPinnedIndex = 0; |
||||
} |
||||
}); |
||||
} |
||||
} |
@ -0,0 +1,177 @@
@@ -0,0 +1,177 @@
|
||||
// https://github.com/evgeny-nadymov/telegram-react/blob/master/src/Components/ColumnMiddle/PinnedMessageBorder.js
|
||||
|
||||
export default class PinnedMessageBorder { |
||||
private border: HTMLElement; |
||||
private wrapper: HTMLElement; |
||||
private svg: SVGSVGElement; |
||||
private defs: SVGDefsElement; |
||||
private clipPath: SVGClipPathElement; |
||||
private path: SVGPathElement; |
||||
private mark: HTMLElement; |
||||
|
||||
private count: number; |
||||
private index: number; |
||||
|
||||
private drawRect = (x: number, y: number, width: number, height: number, radius: number) => { |
||||
return `M${x},${y + radius}a${radius},${radius},0,0,1,${width},0v${height - 2 * radius}a${radius},${radius},0,0,1,${-width},0Z`; |
||||
}; |
||||
|
||||
private getClipPath = (id: string, barHeight: number, count: number) => { |
||||
const radius = 1; |
||||
|
||||
let d = ''; |
||||
if(count === 3) { |
||||
d = this.drawRect(0, 0, 2, barHeight, radius) |
||||
+ this.drawRect(0, 11, 2, barHeight + 1, radius) |
||||
+ this.drawRect(0, 23, 2, barHeight, radius); |
||||
} else { |
||||
for(let i = 0; i < count; i++) { |
||||
d += this.drawRect(0, (barHeight + 2) * i, 2, barHeight, radius); |
||||
} |
||||
} |
||||
|
||||
if(!this.clipPath) { |
||||
this.clipPath = document.createElementNS('http://www.w3.org/2000/svg', 'clipPath'); |
||||
this.path = document.createElementNS('http://www.w3.org/2000/svg', 'path'); |
||||
|
||||
this.clipPath.append(this.path); |
||||
} |
||||
|
||||
this.clipPath.id = id; |
||||
this.path.setAttributeNS(null, 'd', d); |
||||
|
||||
return this.clipPath; |
||||
}; |
||||
|
||||
private getBarHeight = (count: number, index: number) => { |
||||
let barHeight = 32; |
||||
if(count === 1) { |
||||
barHeight = 32; |
||||
} else if(count === 2) { |
||||
barHeight = 15; |
||||
} else if(count === 3) { |
||||
barHeight = 9; |
||||
} else if(count === 4) { |
||||
barHeight = 7; |
||||
} else if(count > 3) { |
||||
barHeight = 7; |
||||
} |
||||
|
||||
return barHeight; |
||||
}; |
||||
|
||||
private getMarkHeight = (count: number, index: number) => { |
||||
let barHeight = 32; |
||||
if(count === 1) { |
||||
barHeight = 32; |
||||
} else if(count === 2) { |
||||
barHeight = 15; |
||||
} else if(count === 3) { |
||||
barHeight = index === 1 ? 10 : 9; |
||||
} else if(count === 4) { |
||||
barHeight = 7; |
||||
} else if(count > 3) { |
||||
barHeight = 7; |
||||
} |
||||
|
||||
return barHeight; |
||||
}; |
||||
|
||||
private getMarkTranslateY = (index: number, barHeight: number, count: number) => { |
||||
if(count === 1) { |
||||
return 0; |
||||
} else if(count === 2) { |
||||
return index === 0 ? 0 : barHeight + 2; |
||||
} |
||||
|
||||
if(count === 3) { |
||||
if(index === 0) { |
||||
return 0; |
||||
} else if (index === 1) { |
||||
return 11; |
||||
} |
||||
|
||||
return 23; |
||||
} else { |
||||
return (barHeight + 2) * index; |
||||
} |
||||
}; |
||||
|
||||
private getTrackTranslateY = (index: number, count: number, barHeight: number, trackHeight: number) => { |
||||
if(count <= 4) { |
||||
return 0; |
||||
} |
||||
|
||||
if(index <= 1) { |
||||
return 0; |
||||
} else if(index >= count - 2) { |
||||
return trackHeight - 32; |
||||
} |
||||
|
||||
return (barHeight + 4) / 2 + (index - 2) * (barHeight + 2); |
||||
}; |
||||
|
||||
private getTrackHeight = (count: number, barHeight: number) => { |
||||
return count <= 3 ? 32 : barHeight * count + 2 * (count - 1); |
||||
}; |
||||
|
||||
public render(count: number, index: number) { |
||||
if(!this.border) { |
||||
this.border = document.createElement('div'); |
||||
this.border.classList.add('pinned-message-border'); |
||||
|
||||
this.wrapper = document.createElement('div'); |
||||
this.border.append(this.wrapper); |
||||
} |
||||
|
||||
if(count === 1) { |
||||
if(this.count !== count) { |
||||
this.wrapper.className = 'pinned-message-border-wrapper-1'; |
||||
this.border.classList.remove('pinned-message-border-mask'); |
||||
this.wrapper.innerHTML = ''; |
||||
} |
||||
|
||||
return this.border; |
||||
} |
||||
|
||||
const barHeight = this.getBarHeight(count, index); |
||||
const markHeight = this.getMarkHeight(count, index); |
||||
const trackHeight = this.getTrackHeight(count, barHeight); |
||||
|
||||
const clipPathId = `clipPath_${count}`; |
||||
const clipPath = this.getClipPath(clipPathId, barHeight, count); |
||||
|
||||
const markTranslateY = this.getMarkTranslateY(index, barHeight, count); |
||||
const trackTranslateY = this.getTrackTranslateY(index, count, barHeight, trackHeight); |
||||
|
||||
this.border.classList.toggle('pinned-message-border-mask', count > 4); |
||||
|
||||
this.wrapper.className = 'pinned-message-border-wrapper'; |
||||
this.wrapper.style.cssText = `clip-path: url(#${clipPathId}); width: 2px; height: ${trackHeight}px; transform: translateY(-${trackTranslateY}px);`; |
||||
|
||||
if(!this.svg) { |
||||
this.svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); |
||||
this.svg.setAttributeNS(null, 'height', '0'); |
||||
this.svg.setAttributeNS(null, 'width', '0'); |
||||
|
||||
this.defs = document.createElementNS('http://www.w3.org/2000/svg', 'defs'); |
||||
this.defs.append(clipPath); |
||||
|
||||
this.svg.append(this.defs); |
||||
|
||||
this.mark = document.createElement('div'); |
||||
this.mark.classList.add('pinned-message-border-mark'); |
||||
} |
||||
|
||||
if(!this.svg.parentElement) { |
||||
this.wrapper.append(this.svg, this.mark); |
||||
} |
||||
|
||||
this.mark.style.cssText = `height: ${markHeight}px; transform: translateY(${markTranslateY}px);`; |
||||
|
||||
this.count = count; |
||||
this.index = index; |
||||
|
||||
return this.border; |
||||
} |
||||
} |
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -0,0 +1,383 @@
@@ -0,0 +1,383 @@
|
||||
.pinned-message { |
||||
&-border { |
||||
position: relative; |
||||
height: 35px; |
||||
width: 2px; |
||||
padding: 1.5px 0; |
||||
//padding: 0 0 6px 6px; |
||||
//overflow: hidden; |
||||
|
||||
&-wrapper-1 { |
||||
height: 32px; |
||||
width: 2px; |
||||
border-radius: 1px; |
||||
background: $color-blue; |
||||
} |
||||
|
||||
&-mask { |
||||
mask-image: linear-gradient(to bottom, transparent 0, black 6px, black 38px, transparent 44px); |
||||
} |
||||
|
||||
&-wrapper { |
||||
color: $color-blue; |
||||
background: #50a2e988; |
||||
position: relative; |
||||
will-change: transform; |
||||
transition: transform 0.25s ease-in-out; |
||||
} |
||||
|
||||
&-bars { |
||||
stroke: currentColor; |
||||
stroke-width: 2px; |
||||
stroke-linecap: round; |
||||
stroke-linejoin: round; |
||||
} |
||||
|
||||
&-mark { |
||||
position: absolute; |
||||
left: 0; |
||||
top: 0; |
||||
width: 2px; |
||||
background: currentColor; |
||||
border-radius: 1px; |
||||
will-change: transform; |
||||
transition: transform 0.25s ease-in-out; |
||||
} |
||||
} |
||||
} |
||||
|
||||
.pinned-message, .reply { |
||||
cursor: pointer; |
||||
display: flex; |
||||
flex-direction: row; |
||||
align-items: center; |
||||
overflow: hidden; |
||||
box-sizing: border-box; |
||||
margin-right: 1rem; |
||||
max-height: 35px; |
||||
position: relative; |
||||
user-select: none; |
||||
/* padding: .25rem; */ |
||||
|
||||
&.is-media { |
||||
.emoji:first-child { |
||||
margin-right: .25rem; |
||||
} |
||||
} |
||||
|
||||
&-content { |
||||
margin-left: 8px; |
||||
flex-grow: 1; |
||||
flex-shrink: 1; |
||||
overflow: hidden; |
||||
pointer-events: none; |
||||
position: relative; |
||||
height: 2rem; |
||||
display: flex; |
||||
flex-direction: column; |
||||
justify-content: space-between; |
||||
} |
||||
|
||||
&-title { |
||||
color: $color-blue; |
||||
} |
||||
|
||||
&-title, &-subtitle { |
||||
font-size: 14px; |
||||
//line-height: 18px; |
||||
//line-height: 1; |
||||
//line-height: 15px; |
||||
line-height: 16px; |
||||
overflow: hidden; |
||||
white-space: nowrap; |
||||
text-overflow: ellipsis; |
||||
|
||||
// @include respond-to(handhelds) { |
||||
// line-height: 13px; |
||||
// } |
||||
} |
||||
|
||||
&-subtitle { |
||||
color: #111; |
||||
} |
||||
|
||||
&-media { |
||||
height: 2rem; |
||||
width: 2rem; |
||||
border-radius: .5rem; |
||||
overflow: hidden; |
||||
position: absolute; |
||||
left: 0; |
||||
top: 0; |
||||
background-repeat: no-repeat; |
||||
background-size: cover; |
||||
background-position: center center; |
||||
|
||||
/* > img, > video { |
||||
|
||||
} */ |
||||
|
||||
// sticker |
||||
/* > img { |
||||
object-fit: contain !important; |
||||
max-width: 100%; |
||||
max-height: 100%; |
||||
} */ |
||||
> img { |
||||
object-fit: cover; |
||||
width: 100%; |
||||
height: 100%; |
||||
} |
||||
} |
||||
|
||||
i { |
||||
font-style: normal; |
||||
//color: $color-blue; |
||||
color: #707579; |
||||
|
||||
/* &.document-title { |
||||
color: #707579; |
||||
} */ |
||||
} |
||||
|
||||
img.emoji { |
||||
height: 1rem; |
||||
width: 1rem; |
||||
vertical-align: top; |
||||
} |
||||
|
||||
/* span.emoji { |
||||
font-size: 1rem; |
||||
vertical-align: unset; |
||||
} */ |
||||
} |
||||
|
||||
.reply { |
||||
&.is-media { |
||||
.reply-content { |
||||
padding-left: 2.5rem; |
||||
} |
||||
} |
||||
|
||||
html.no-touch &:hover { |
||||
background-color: var(--color-gray-hover); |
||||
} |
||||
|
||||
&-border { |
||||
height: 2rem; |
||||
border-radius: 1px; |
||||
min-width: 2px; |
||||
background: $color-blue; |
||||
} |
||||
} |
||||
|
||||
.pinned-container { |
||||
flex: 0 0 auto; |
||||
overflow: visible; |
||||
|
||||
&.is-floating { |
||||
position: absolute !important; |
||||
top: 100%; |
||||
width: 100% !important; |
||||
background: #fff !important; |
||||
left: 0; |
||||
max-height: 100% !important; |
||||
height: 52px; |
||||
|
||||
.pinned-container-close { |
||||
position: absolute; |
||||
font-size: 1.4rem; |
||||
right: 9px; |
||||
display: flex; |
||||
} |
||||
|
||||
.pinned-container-wrapper { |
||||
padding: 0 1rem; |
||||
height: 100%; |
||||
} |
||||
} |
||||
|
||||
@include respond-to(handhelds) { |
||||
box-shadow: 0px 1px 3px 0px rgba(0, 0, 0, .15); |
||||
|
||||
&:before { |
||||
width: 100%; |
||||
content: " "; |
||||
height: 52px; |
||||
left: 0; |
||||
top: 0; |
||||
position: absolute; |
||||
/* box-shadow: inset 0px 2px 3px 0px rgba(0, 0, 0, .15); */ |
||||
box-shadow: inset 0px 1px 2px 0px rgba(0, 0, 0, .15); |
||||
} |
||||
} |
||||
|
||||
&-content { |
||||
width: 100%; |
||||
overflow: hidden; |
||||
} |
||||
|
||||
&-close, .pinned-audio-ico { |
||||
font-size: 1.5rem; |
||||
display: flex; |
||||
justify-content: center; |
||||
z-index: 1; |
||||
flex: 0 0 auto; |
||||
} |
||||
|
||||
&-close { |
||||
display: none; |
||||
} |
||||
|
||||
&-wrapper { |
||||
display: flex; |
||||
flex: 1 1 auto; |
||||
overflow: hidden; |
||||
align-items: center; |
||||
padding: .25rem; |
||||
border-radius: 4px; |
||||
|
||||
html.no-touch &:hover { |
||||
background-color: var(--color-gray-hover); |
||||
} |
||||
} |
||||
} |
||||
|
||||
.pinned-message { |
||||
display: none; |
||||
width: auto; |
||||
|
||||
&-media-container { |
||||
width: 2rem; |
||||
height: 2rem; |
||||
position: absolute; |
||||
transition: transform var(--pm-transition)/* , opacity var(--pm-transition) */; |
||||
} |
||||
|
||||
/* &-media { |
||||
transform: none !important; |
||||
|
||||
&.is-hiding { |
||||
&.backwards { |
||||
|
||||
} |
||||
} |
||||
} */ |
||||
|
||||
// * fix blink in safari, can't add translateX from nowhere... |
||||
&-title, &-subtitle { |
||||
transform: translateX(0); |
||||
} |
||||
|
||||
&.is-media { |
||||
.pinned-message-title, .pinned-message-subtitle { |
||||
transform: translateX(2.5rem); |
||||
} |
||||
} |
||||
|
||||
&:not(.is-media) &-media-container { |
||||
transform: scale(0); |
||||
//opacity: 0; |
||||
} |
||||
|
||||
&:not(.is-floating) { |
||||
width: 15.5rem; |
||||
|
||||
.pinned-message-close { |
||||
display: flex; |
||||
margin-right: .75rem; |
||||
} |
||||
} |
||||
|
||||
&.is-floating .pinned-message-subtitle { |
||||
max-width: 280px; |
||||
} |
||||
|
||||
&-content { |
||||
> .pinned-message-title, > .pinned-message-subtitle { |
||||
position: relative; |
||||
height: 50%; |
||||
overflow: visible; |
||||
transition: transform var(--pm-transition); |
||||
} |
||||
} |
||||
|
||||
&-subtitle { |
||||
.animated-super-row { |
||||
font-size: 14px; |
||||
line-height: 16px; |
||||
overflow: hidden; |
||||
white-space: nowrap; |
||||
text-overflow: ellipsis; |
||||
} |
||||
} |
||||
|
||||
.animated-counter { |
||||
transition: transform var(--pm-transition), opacity var(--pm-transition); |
||||
|
||||
&:before { |
||||
content: "#"; |
||||
} |
||||
|
||||
&.is-last { |
||||
&:not(.backwards) { |
||||
transform: scale(0.68); |
||||
opacity: 0; |
||||
} |
||||
|
||||
/* .animated-super-row { |
||||
transition: none !important; |
||||
} */ |
||||
} |
||||
} |
||||
} |
||||
|
||||
.pinned-audio { |
||||
display: flex; |
||||
flex-direction: column; |
||||
justify-content: center; |
||||
cursor: pointer; |
||||
//width: 210px; |
||||
|
||||
&:not(.is-floating) { |
||||
padding-right: 1.75rem; |
||||
max-width: 210px; |
||||
position: relative; |
||||
} |
||||
|
||||
&.is-floating .pinned-audio-ico { |
||||
margin-left: -.25rem; |
||||
} |
||||
|
||||
&-ico { |
||||
color: #50a2e9; |
||||
margin-right: .375rem; |
||||
|
||||
&:before { |
||||
content: $tgico-largeplay; |
||||
} |
||||
|
||||
&.flip-icon:before { |
||||
content: $tgico-largepause; |
||||
} |
||||
} |
||||
|
||||
&-title { |
||||
font-weight: 500; |
||||
width: 100%; |
||||
max-width: 100%; |
||||
} |
||||
|
||||
&-subtitle { |
||||
color: #707579; |
||||
} |
||||
|
||||
&-title, &-subtitle { |
||||
white-space: nowrap; |
||||
text-overflow: ellipsis; |
||||
font-size: 14px; |
||||
line-height: 1.4; |
||||
overflow: hidden; |
||||
max-width: 240px; |
||||
} |
||||
} |
Loading…
Reference in new issue