Browse Source
Right sidebar animation Fix animations speed with translate3d Folders tabs scroll Fix ripple animation Right sidebar translateZ blink fix Miscmaster
morethanwords
4 years ago
77 changed files with 33155 additions and 16871 deletions
File diff suppressed because one or more lines are too long
Binary file not shown.
@ -0,0 +1,63 @@
@@ -0,0 +1,63 @@
|
||||
export interface CancellablePromise<T> extends Promise<T> { |
||||
resolve?: (...args: any[]) => void, |
||||
reject?: (...args: any[]) => void, |
||||
cancel?: () => void, |
||||
|
||||
notify?: (...args: any[]) => void, |
||||
notifyAll?: (...args: any[]) => void, |
||||
lastNotify?: any, |
||||
listeners?: Array<(...args: any[]) => void>, |
||||
addNotifyListener?: (callback: (...args: any[]) => void) => void, |
||||
|
||||
isFulfilled?: boolean, |
||||
isRejected?: boolean |
||||
} |
||||
|
||||
export function deferredPromise<T>() { |
||||
let deferredHelper: any = { |
||||
isFulfilled: false, |
||||
isRejected: false, |
||||
|
||||
notify: () => {}, |
||||
notifyAll: (...args: any[]) => { |
||||
deferredHelper.lastNotify = args; |
||||
deferredHelper.listeners.forEach((callback: any) => callback(...args)); |
||||
}, |
||||
|
||||
lastNotify: undefined, |
||||
listeners: [], |
||||
addNotifyListener: (callback: (...args: any[]) => void) => { |
||||
if(deferredHelper.lastNotify) { |
||||
callback(...deferredHelper.lastNotify); |
||||
} |
||||
|
||||
deferredHelper.listeners.push(callback); |
||||
} |
||||
}; |
||||
|
||||
let deferred: CancellablePromise<T> = new Promise<T>((resolve, reject) => { |
||||
deferredHelper.resolve = (value: T) => { |
||||
if(deferred.isFulfilled) return; |
||||
|
||||
deferred.isFulfilled = true; |
||||
resolve(value); |
||||
}; |
||||
|
||||
deferredHelper.reject = (...args: any[]) => { |
||||
if(deferred.isRejected) return; |
||||
|
||||
deferred.isRejected = true; |
||||
reject(...args); |
||||
}; |
||||
}); |
||||
|
||||
deferred.finally(() => { |
||||
deferred.notify = null; |
||||
deferred.listeners.length = 0; |
||||
deferred.lastNotify = null; |
||||
}); |
||||
|
||||
Object.assign(deferred, deferredHelper); |
||||
|
||||
return deferred; |
||||
} |
@ -0,0 +1,48 @@
@@ -0,0 +1,48 @@
|
||||
import type { ArgumentTypes } from "../types"; |
||||
|
||||
export default class EventListenerBase<Listeners extends {[name: string]: Function}> { |
||||
protected listeners: Partial<{ |
||||
[k in keyof Listeners]: Array<{callback: Listeners[k], once?: true}> |
||||
}> = {}; |
||||
protected listenerResults: Partial<{ |
||||
[k in keyof Listeners]: ArgumentTypes<Listeners[k]> |
||||
}> = {}; |
||||
|
||||
constructor(private reuseResults?: true) { |
||||
|
||||
} |
||||
|
||||
public addListener(name: keyof Listeners, callback: Listeners[typeof name], once?: true) { |
||||
(this.listeners[name] ?? (this.listeners[name] = [])).push({callback, once}); |
||||
|
||||
if(this.listenerResults.hasOwnProperty(name)) { |
||||
callback(this.listenerResults[name]); |
||||
|
||||
if(once) { |
||||
this.removeListener(name, callback); |
||||
} |
||||
} |
||||
} |
||||
|
||||
public removeListener(name: keyof Listeners, callback: Listeners[typeof name]) { |
||||
if(this.listeners[name]) { |
||||
this.listeners[name].findAndSplice(l => l.callback == callback); |
||||
} |
||||
} |
||||
|
||||
protected setListenerResult(name: keyof Listeners, ...args: ArgumentTypes<Listeners[typeof name]>) { |
||||
if(this.reuseResults) { |
||||
this.listenerResults[name] = args; |
||||
} |
||||
|
||||
if(this.listeners[name]) { |
||||
this.listeners[name].forEach(listener => { |
||||
listener.callback(...args); |
||||
|
||||
if(listener.once) { |
||||
this.removeListener(name, listener.callback); |
||||
} |
||||
}); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,105 @@
@@ -0,0 +1,105 @@
|
||||
import EventListenerBase from "./eventListenerBase"; |
||||
|
||||
type Size = {width: number, height: number}; |
||||
type Sizes = { |
||||
regular: Size, |
||||
webpage: Size, |
||||
album: Size |
||||
}; |
||||
|
||||
export enum ScreenSize { |
||||
mobile, |
||||
medium, |
||||
large |
||||
} |
||||
|
||||
const MOBILE_SIZE = 896; |
||||
const MEDIUM_SIZE = 1275; |
||||
const LARGE_SIZE = 1680; |
||||
|
||||
class MediaSizes extends EventListenerBase<{ |
||||
changeScreen: (from: ScreenSize, to: ScreenSize) => void |
||||
}> { |
||||
private screenSizes: {key: ScreenSize, value: number}[] = [ |
||||
{key: ScreenSize.mobile, value: MOBILE_SIZE - 1}, |
||||
{key: ScreenSize.medium, value: MEDIUM_SIZE}, |
||||
{key: ScreenSize.large, value: LARGE_SIZE} |
||||
]; |
||||
|
||||
private sizes: {[k in 'desktop' | 'handhelds']: Sizes} = { |
||||
handhelds: { |
||||
regular: { |
||||
width: 293, |
||||
height: 293 |
||||
}, |
||||
webpage: { |
||||
width: 293, |
||||
height: 213 |
||||
}, |
||||
album: { |
||||
width: 293, |
||||
height: 0 |
||||
} |
||||
}, |
||||
desktop: { |
||||
regular: { |
||||
width: 480, |
||||
height: 480 |
||||
}, |
||||
webpage: { |
||||
width: 480, |
||||
height: 400 |
||||
}, |
||||
album: { |
||||
width: 451, |
||||
height: 0 |
||||
} |
||||
} |
||||
}; |
||||
|
||||
public isMobile = false; |
||||
public active: Sizes; |
||||
public activeScreen: ScreenSize; |
||||
|
||||
constructor() { |
||||
super(); |
||||
|
||||
window.addEventListener('resize', this.handleResize); |
||||
this.handleResize(); |
||||
} |
||||
|
||||
private handleResize = () => { |
||||
const innerWidth = window.innerWidth; |
||||
//this.isMobile = innerWidth <= 720;
|
||||
|
||||
let activeScreen = this.screenSizes[0].key; |
||||
for(let i = this.screenSizes.length - 1; i >= 0; --i) { |
||||
if(this.screenSizes[i].value < innerWidth) { |
||||
activeScreen = (this.screenSizes[i + 1] || this.screenSizes[i]).key; |
||||
break; |
||||
} |
||||
} |
||||
|
||||
if(this.activeScreen != activeScreen) { |
||||
//console.log('changeScreen', this.activeScreen, activeScreen);
|
||||
this.setListenerResult('changeScreen', this.activeScreen, activeScreen); |
||||
} |
||||
|
||||
this.activeScreen = activeScreen; |
||||
|
||||
this.isMobile = this.activeScreen == ScreenSize.mobile; |
||||
|
||||
this.active = this.isMobile ? this.sizes.handhelds : this.sizes.desktop; |
||||
|
||||
/* if(this.isMobile) { |
||||
for(let i in this.active) { |
||||
// @ts-ignore
|
||||
let size = this.active[i]; |
||||
size.width = innerWidth |
||||
} |
||||
} */ |
||||
}; |
||||
} |
||||
|
||||
const mediaSizes = new MediaSizes(); |
||||
export default mediaSizes; |
File diff suppressed because one or more lines are too long
@ -1,368 +0,0 @@
@@ -1,368 +0,0 @@
|
||||
import {blobConstruct, bytesToBase64, blobSafeMimeType, dataUrlToBlob} from './bin_utils'; |
||||
import FileManager from './filemanager'; |
||||
import { logger } from './logger'; |
||||
|
||||
class IdbFileStorage { |
||||
public dbName = 'cachedFiles'; |
||||
public dbStoreName = 'files'; |
||||
public dbVersion = 2; |
||||
public openDbPromise: Promise<IDBDatabase>; |
||||
public storageIsAvailable = true; |
||||
public name = 'IndexedDB'; |
||||
|
||||
private log: ReturnType<typeof logger> = logger('IDB'); |
||||
|
||||
constructor() { |
||||
this.openDatabase(true); |
||||
} |
||||
|
||||
public isAvailable() { |
||||
return this.storageIsAvailable; |
||||
} |
||||
|
||||
public openDatabase(createNew = false): Promise<IDBDatabase> { |
||||
if(this.openDbPromise && !createNew) { |
||||
return this.openDbPromise; |
||||
} |
||||
|
||||
const createObjectStore = (db: IDBDatabase) => { |
||||
db.createObjectStore(this.dbStoreName); |
||||
}; |
||||
|
||||
try { |
||||
var request = indexedDB.open(this.dbName, this.dbVersion); |
||||
|
||||
if(!request) { |
||||
throw new Error(); |
||||
} |
||||
} catch(error) { |
||||
this.log.error('error opening db', error.message) |
||||
this.storageIsAvailable = false; |
||||
return Promise.reject(error); |
||||
} |
||||
|
||||
let finished = false; |
||||
setTimeout(() => { |
||||
if(!finished) { |
||||
request.onerror({type: 'IDB_CREATE_TIMEOUT'} as Event); |
||||
} |
||||
}, 3000); |
||||
|
||||
return this.openDbPromise = new Promise<IDBDatabase>((resolve, reject) => { |
||||
request.onsuccess = (event) => { |
||||
finished = true; |
||||
const db = request.result; |
||||
let calledNew = false; |
||||
|
||||
this.log('Opened'); |
||||
|
||||
db.onerror = (error) => { |
||||
this.storageIsAvailable = false; |
||||
this.log.error('Error creating/accessing IndexedDB database', error); |
||||
reject(error); |
||||
}; |
||||
|
||||
db.onclose = (e) => { |
||||
this.log.error('closed:', e); |
||||
!calledNew && this.openDatabase(); |
||||
}; |
||||
|
||||
db.onabort = (e) => { |
||||
this.log.error('abort:', e); |
||||
const transaction = e.target as IDBTransaction; |
||||
|
||||
this.openDatabase(calledNew = true); |
||||
|
||||
if(transaction.onerror) { |
||||
transaction.onerror(e); |
||||
} |
||||
|
||||
db.close(); |
||||
}; |
||||
|
||||
db.onversionchange = (e) => { |
||||
this.log.error('onversionchange, lol?'); |
||||
}; |
||||
|
||||
resolve(db); |
||||
}; |
||||
|
||||
request.onerror = (event) => { |
||||
finished = true; |
||||
this.storageIsAvailable = false; |
||||
this.log.error('Error creating/accessing IndexedDB database', event); |
||||
reject(event); |
||||
}; |
||||
|
||||
request.onupgradeneeded = (event) => { |
||||
finished = true; |
||||
this.log.warn('performing idb upgrade from', event.oldVersion, 'to', event.newVersion); |
||||
|
||||
// @ts-ignore
|
||||
var db = event.target.result as IDBDatabase; |
||||
if(event.oldVersion == 1) { |
||||
db.deleteObjectStore(this.dbStoreName); |
||||
} |
||||
|
||||
createObjectStore(db); |
||||
}; |
||||
}); |
||||
} |
||||
|
||||
public deleteFile(fileName: string): Promise<void> { |
||||
//return Promise.resolve();
|
||||
return this.openDatabase().then((db) => { |
||||
try { |
||||
this.log('Delete file: `' + fileName + '`'); |
||||
var objectStore = db.transaction([this.dbStoreName], 'readwrite') |
||||
.objectStore(this.dbStoreName); |
||||
|
||||
var request = objectStore.delete(fileName); |
||||
} catch(error) { |
||||
return Promise.reject(error); |
||||
} |
||||
|
||||
return new Promise((resolve, reject) => { |
||||
const timeout = setTimeout(() => { |
||||
this.log.error('deleteFile request not finished!', fileName, request); |
||||
resolve(); |
||||
}, 3000); |
||||
|
||||
request.onsuccess = (event) => { |
||||
this.log('deleted file', event); |
||||
resolve(); |
||||
clearTimeout(timeout); |
||||
}; |
||||
|
||||
request.onerror = (error) => { |
||||
reject(error); |
||||
clearTimeout(timeout); |
||||
}; |
||||
}); |
||||
}); |
||||
} |
||||
|
||||
public saveFile(fileName: string, blob: Blob | Uint8Array): Promise<Blob> { |
||||
return Promise.resolve(blobConstruct([blob])); |
||||
return this.openDatabase().then((db) => { |
||||
if(!(blob instanceof Blob)) { |
||||
blob = blobConstruct([blob]) as Blob; |
||||
} |
||||
|
||||
this.log('saveFile:', fileName, blob); |
||||
|
||||
const handleError = (error: Error) => { |
||||
this.log.error('saveFile transaction error:', fileName, blob, db, error, error && error.name); |
||||
if((!error || error.name === 'InvalidStateError')/* && false */) { |
||||
setTimeout(() => { |
||||
this.saveFile(fileName, blob); |
||||
}, 2e3); |
||||
} else { |
||||
//console.error('IndexedDB saveFile transaction error:', error, error && error.name);
|
||||
} |
||||
}; |
||||
|
||||
try { |
||||
const transaction = db.transaction([this.dbStoreName], 'readwrite'); |
||||
transaction.onerror = (e) => { |
||||
handleError(transaction.error); |
||||
}; |
||||
transaction.oncomplete = (e) => { |
||||
this.log('saveFile transaction complete:', fileName); |
||||
}; |
||||
|
||||
/* transaction.addEventListener('abort', (e) => { |
||||
//handleError();
|
||||
this.log.error('IndexedDB: saveFile transaction abort!', transaction.error); |
||||
}); */ |
||||
|
||||
const objectStore = transaction.objectStore(this.dbStoreName); |
||||
var request = objectStore.put(blob, fileName); |
||||
} catch(error) { |
||||
handleError(error); |
||||
return blob; |
||||
|
||||
/* this.storageIsAvailable = false; |
||||
throw error; */ |
||||
} |
||||
|
||||
return new Promise((resolve, reject) => { |
||||
const timeout = setTimeout(() => { |
||||
this.log.error('saveFile request not finished', fileName, request); |
||||
}, 3000); |
||||
|
||||
request.onsuccess = (event) => { |
||||
resolve(blob as Blob); |
||||
clearTimeout(timeout); |
||||
}; |
||||
|
||||
request.onerror = (error) => { |
||||
reject(error); |
||||
clearTimeout(timeout); |
||||
}; |
||||
}); |
||||
}); |
||||
} |
||||
|
||||
public saveFileBase64(db: IDBDatabase, fileName: string, blob: Blob | any): Promise<Blob> { |
||||
if(this.getBlobSize(blob) > 10 * 1024 * 1024) { |
||||
return Promise.reject(); |
||||
} |
||||
|
||||
if(!(blob instanceof Blob)) { |
||||
var safeMimeType = blobSafeMimeType(blob.type || 'image/jpeg'); |
||||
var address = 'data:' + safeMimeType + ';base64,' + bytesToBase64(blob); |
||||
return this.storagePutB64String(db, fileName, address).then(() => { |
||||
return blob; |
||||
}); |
||||
} |
||||
|
||||
try { |
||||
var reader = new FileReader(); |
||||
} catch (e) { |
||||
this.storageIsAvailable = false; |
||||
return Promise.reject(); |
||||
} |
||||
|
||||
let promise = new Promise<Blob>((resolve, reject) => { |
||||
reader.onloadend = () => { |
||||
this.storagePutB64String(db, fileName, reader.result as string).then(() => { |
||||
resolve(blob); |
||||
}, reject); |
||||
} |
||||
|
||||
reader.onerror = reject; |
||||
}); |
||||
|
||||
|
||||
try { |
||||
reader.readAsDataURL(blob); |
||||
} catch (e) { |
||||
this.storageIsAvailable = false; |
||||
return Promise.reject(); |
||||
} |
||||
|
||||
return promise; |
||||
} |
||||
|
||||
public storagePutB64String(db: IDBDatabase, fileName: string, b64string: string) { |
||||
try { |
||||
var objectStore = db.transaction([this.dbStoreName], 'readwrite') |
||||
.objectStore(this.dbStoreName); |
||||
var request = objectStore.put(b64string, fileName); |
||||
} catch(error) { |
||||
this.storageIsAvailable = false; |
||||
return Promise.reject(error); |
||||
} |
||||
|
||||
return new Promise((resolve, reject) => { |
||||
request.onsuccess = function(event) { |
||||
resolve(); |
||||
}; |
||||
|
||||
request.onerror = reject; |
||||
}); |
||||
} |
||||
|
||||
public getBlobSize(blob: any) { |
||||
return blob.size || blob.byteLength || blob.length; |
||||
} |
||||
|
||||
public getFile(fileName: string): Promise<Blob> { |
||||
//return Promise.reject();
|
||||
return this.openDatabase().then((db) => { |
||||
this.log('getFile pre:', fileName); |
||||
|
||||
try { |
||||
const transaction = db.transaction([this.dbStoreName], 'readonly'); |
||||
transaction.onabort = (e) => { |
||||
this.log.error('getFile transaction onabort?', e); |
||||
}; |
||||
const objectStore = transaction.objectStore(this.dbStoreName); |
||||
var request = objectStore.get(fileName); |
||||
|
||||
//this.log.log('IDB getFile:', fileName, request);
|
||||
} catch(err) { |
||||
this.log.error('getFile error:', err, fileName, request, request.error); |
||||
} |
||||
|
||||
return new Promise((resolve, reject) => { |
||||
const timeout = setTimeout(() => { |
||||
this.log.error('getFile request not finished!', fileName, request); |
||||
reject(); |
||||
}, 3000); |
||||
|
||||
request.onsuccess = function(event) { |
||||
const result = request.result; |
||||
if(result === undefined) { |
||||
reject(); |
||||
} else if(typeof result === 'string' && |
||||
result.substr(0, 5) === 'data:') { |
||||
resolve(dataUrlToBlob(result)); |
||||
} else { |
||||
resolve(result); |
||||
} |
||||
|
||||
clearTimeout(timeout); |
||||
} |
||||
|
||||
request.onerror = () => { |
||||
clearTimeout(timeout); |
||||
reject(); |
||||
}; |
||||
}); |
||||
}); |
||||
} |
||||
|
||||
public getAllKeys(): Promise<Array<string>> { |
||||
console.time('getAllEntries'); |
||||
return this.openDatabase().then((db) => { |
||||
var objectStore = db.transaction([this.dbStoreName], 'readonly') |
||||
.objectStore(this.dbStoreName); |
||||
var request = objectStore.getAllKeys(); |
||||
|
||||
return new Promise((resolve, reject) => { |
||||
request.onsuccess = function(event) { |
||||
// @ts-ignore
|
||||
var result = event.target.result; |
||||
resolve(result); |
||||
console.timeEnd('getAllEntries'); |
||||
} |
||||
|
||||
request.onerror = reject; |
||||
}); |
||||
}); |
||||
} |
||||
|
||||
public isFileExists(fileName: string): Promise<boolean> { |
||||
console.time('isFileExists'); |
||||
return this.openDatabase().then((db) => { |
||||
var objectStore = db.transaction([this.dbStoreName], 'readonly') |
||||
.objectStore(this.dbStoreName); |
||||
var request = objectStore.openCursor(fileName); |
||||
|
||||
return new Promise((resolve, reject) => { |
||||
request.onsuccess = function(event) { |
||||
// @ts-ignore
|
||||
var cursor = event.target.result; |
||||
resolve(!!cursor); |
||||
console.timeEnd('isFileExists'); |
||||
} |
||||
|
||||
request.onerror = reject; |
||||
}); |
||||
}); |
||||
} |
||||
|
||||
public getFileWriter(fileName: string, mimeType: string) { |
||||
var fakeWriter = FileManager.getFakeFileWriter(mimeType, (blob) => { |
||||
return this.saveFile(fileName, blob); |
||||
}); |
||||
|
||||
return Promise.resolve(fakeWriter); |
||||
} |
||||
} |
||||
|
||||
const idbFileStorage = new IdbFileStorage(); |
||||
(window as any).IdbFileStorage = idbFileStorage; |
||||
export default idbFileStorage; |
@ -1,5 +0,0 @@
@@ -1,5 +0,0 @@
|
||||
// @ts-ignore
|
||||
//import LottiePlayer from "lottie-web/build/player/lottie_canvas.min.js";
|
||||
import LottiePlayer from "lottie-web/build/player/lottie_light.min.js"; |
||||
|
||||
(window as any).lottie = LottiePlayer; |
@ -0,0 +1,82 @@
@@ -0,0 +1,82 @@
|
||||
.audio { |
||||
position: relative; |
||||
padding-left: 67px; |
||||
min-height: 58px; |
||||
max-width: 244px; |
||||
overflow: visible!important; |
||||
|
||||
@include respond-to(handhelds) { |
||||
padding-left: 45px; |
||||
} |
||||
|
||||
&-toggle, &-download { |
||||
border-radius: 50%; |
||||
background-color: $color-blue; |
||||
font-size: 2.3rem; |
||||
align-items: center; |
||||
|
||||
@include respond-to(handhelds) { |
||||
font-size: 24px !important; |
||||
} |
||||
} |
||||
|
||||
&-download { |
||||
z-index: 2; |
||||
} |
||||
|
||||
&-waveform { |
||||
height: 23px; |
||||
|
||||
//overflow: visible!important; |
||||
|
||||
rect { |
||||
//overflow: visible!important; |
||||
fill: #CBCBCB; |
||||
|
||||
&.active { |
||||
fill: $color-blue; |
||||
} |
||||
} |
||||
} |
||||
|
||||
&-title { |
||||
font-size: 1rem; |
||||
color: #000; |
||||
user-select: none; |
||||
} |
||||
|
||||
&-time, &-subtitle { |
||||
font-size: 14px; |
||||
color: $color-gray; |
||||
margin-top: 3px; |
||||
margin-left: -1px; |
||||
user-select: none; |
||||
|
||||
@include respond-to(handhelds) { |
||||
margin-top: 1px; |
||||
font-size: 12px; |
||||
} |
||||
} |
||||
|
||||
&-title, &:not(.audio-show-progress) &-subtitle { |
||||
white-space: nowrap; |
||||
overflow: hidden; |
||||
max-width: 100%; |
||||
text-overflow: ellipsis; |
||||
} |
||||
|
||||
|
||||
@include respond-to(handhelds) { |
||||
&-download { |
||||
/* background: transparent; */ |
||||
margin-left: 2px; |
||||
margin-top: 1px; |
||||
} |
||||
|
||||
&.is-voice { |
||||
.audio-download { |
||||
margin: 0; |
||||
} |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,59 @@
@@ -0,0 +1,59 @@
|
||||
avatar-element { |
||||
color: #fff; |
||||
width: 54px; |
||||
height: 54px; |
||||
line-height: 54px; |
||||
border-radius: 50%; |
||||
background-color: $color-blue; |
||||
text-align: center; |
||||
font-size: 1.25em; |
||||
/* overflow: hidden; */ |
||||
position: relative; |
||||
user-select: none; |
||||
|
||||
@include respond-to(handhelds) { |
||||
font-size: 14px; |
||||
} |
||||
|
||||
/* kostil */ |
||||
display: flex; |
||||
align-items: center; |
||||
justify-content: center; |
||||
|
||||
img { |
||||
width: 100%; |
||||
height: 100%; |
||||
border-radius: inherit; |
||||
user-select: none; |
||||
|
||||
&.fade-in { |
||||
animation: fadeIn .2s ease forwards; |
||||
} |
||||
} |
||||
|
||||
&[class*=" tgico-"] { |
||||
line-height: 52px; |
||||
font-size: 28px; |
||||
} |
||||
|
||||
path { |
||||
fill: white; |
||||
} |
||||
|
||||
&.is-online:after { |
||||
position: absolute; |
||||
content: " "; |
||||
display: block; |
||||
border-radius: 50%; |
||||
border: 2px solid white; |
||||
background-color: #0ac630; |
||||
left: 74%; |
||||
top: 73%; |
||||
width: 14px; |
||||
height: 14px; |
||||
} |
||||
|
||||
&.tgico-avatar_deletedaccount { |
||||
font-size: 3rem; |
||||
} |
||||
} |
@ -0,0 +1,155 @@
@@ -0,0 +1,155 @@
|
||||
.document { |
||||
padding-left: 4.5rem; |
||||
height: 70px; |
||||
|
||||
&-ico { |
||||
background-color: $color-blue; |
||||
border-radius: 5px; |
||||
line-height: 10px; |
||||
|
||||
&:after { |
||||
content: ""; |
||||
display: block; |
||||
position: absolute; |
||||
top: 0; |
||||
right: 0; |
||||
width: 1.125rem; |
||||
height: 1.125rem; |
||||
border-bottom-left-radius: .25rem; |
||||
border-left: .5625rem solid rgba(0, 0, 0, .25); |
||||
border-bottom: .5625rem solid rgba(0, 0, 0, .25); |
||||
border-top: .5625rem solid #fff; |
||||
border-right: .5625rem solid #fff; |
||||
} |
||||
} |
||||
|
||||
&-ico, &-download { |
||||
font-weight: 500; |
||||
letter-spacing: 1px; |
||||
font-size: 1.1rem; |
||||
background-size: contain; |
||||
overflow: hidden; |
||||
text-overflow: ellipsis; |
||||
} |
||||
|
||||
&-download { |
||||
background-color: $color-blue; |
||||
border-radius: 8px; |
||||
} |
||||
|
||||
&.ext-zip { |
||||
.document-ico, .document-download { |
||||
background-color: #FB8C00; |
||||
} |
||||
} |
||||
|
||||
&.ext-pdf { |
||||
.document-ico, .document-download { |
||||
background-color: #DF3F40; |
||||
} |
||||
} |
||||
|
||||
&.ext-apk { |
||||
.document-ico, .document-download { |
||||
background-color: #43A047; |
||||
} |
||||
} |
||||
|
||||
&:not(.photo) { |
||||
.document-ico { |
||||
padding-top: 1.5rem; |
||||
//background-image: url('../assets/img/doc-in.svg'); |
||||
} |
||||
} |
||||
|
||||
&.photo { |
||||
.document-ico { |
||||
background: #000; |
||||
border-radius: $border-radius; |
||||
|
||||
.document-thumb { |
||||
object-fit: cover; |
||||
width: 100%; |
||||
height: 100%; |
||||
} |
||||
|
||||
&:after { |
||||
display: none; |
||||
} |
||||
} |
||||
|
||||
.document-download { |
||||
background-color: rgba(0, 0, 0, .15); |
||||
} |
||||
} |
||||
|
||||
&-name { |
||||
white-space: nowrap; |
||||
font-weight: 500; |
||||
line-height: 1.3; |
||||
} |
||||
|
||||
&-size { |
||||
white-space: nowrap; |
||||
color: $color-gray; |
||||
font-size: 14px; |
||||
padding-right: 32px; |
||||
line-height: 1.3; |
||||
} |
||||
} |
||||
|
||||
.document, .audio { |
||||
display: flex; |
||||
flex-direction: column; |
||||
justify-content: center; |
||||
cursor: pointer; |
||||
position: relative; |
||||
|
||||
&-ico, &-download { |
||||
position: absolute; |
||||
left: 0; |
||||
width: 54px; |
||||
height: 54px; |
||||
color: #fff; |
||||
display: flex; |
||||
justify-content: center; |
||||
|
||||
|
||||
&.tgico-largeplay:before { |
||||
margin-right: -1px; |
||||
} |
||||
|
||||
@include respond-to(handhelds) { |
||||
height: 36px; |
||||
width: 36px; |
||||
} |
||||
} |
||||
|
||||
&-download { |
||||
z-index: 1; |
||||
align-items: center; |
||||
font-size: 24px; |
||||
cursor: pointer; |
||||
|
||||
.tgico-download { |
||||
transform: scale(1); |
||||
transition: .2s scale; |
||||
} |
||||
|
||||
&.downloading { |
||||
.tgico-download { |
||||
transform: scale(0); |
||||
} |
||||
} |
||||
} |
||||
|
||||
.preloader-container { |
||||
width: 42px; |
||||
height: 42px; |
||||
|
||||
@include respond-to(handhelds) { |
||||
width: 30px; |
||||
height: 30px; |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,79 @@
@@ -0,0 +1,79 @@
|
||||
.rp { |
||||
position: relative; |
||||
user-select: none; |
||||
} |
||||
|
||||
.rp-overflow, .btn-menu-toggle.rp, .menu-horizontal li.rp/* , html.is-safari .c-ripple */ { |
||||
.c-ripple { |
||||
width: 100%; |
||||
height: 100%; |
||||
overflow: hidden; |
||||
border-radius: inherit; |
||||
|
||||
&__circle { |
||||
overflow: hidden; |
||||
} |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Utility: Ripple |
||||
* -------------------------------------------------- |
||||
*/ |
||||
.c-ripple { |
||||
//display: none !important; |
||||
position: absolute; |
||||
top: 0; |
||||
left: 0; |
||||
bottom: 0; |
||||
right: 0; |
||||
|
||||
// ! with these rules ripple lags |
||||
/* width: 100%; |
||||
height: 100%; */ |
||||
//overflow: hidden; |
||||
/* background: transparent; |
||||
border-radius: inherit; */ |
||||
|
||||
html.is-safari &:not(:empty) { |
||||
-webkit-mask-image: -webkit-radial-gradient(circle, white 100%, black 100%); // fix safari overflow |
||||
border-radius: inherit; |
||||
} |
||||
|
||||
&__circle { |
||||
background-color: rgba(0, 0, 0, .08); |
||||
display: block; |
||||
position: absolute; |
||||
transform: scale(0); |
||||
border-radius: 50%; |
||||
animation: ripple-effect .7s forwards; |
||||
transition: .35s opacity; |
||||
//overflow: hidden; |
||||
|
||||
@include respond-to(handhelds) { |
||||
animation-duration: .2s; |
||||
transition-duration: .1s; |
||||
border-radius: 15%; |
||||
} |
||||
} |
||||
|
||||
&.is-square .c-ripple__circle, .btn-menu & .c-ripple__circle { |
||||
animation-duration: .2s; |
||||
transition-duration: .1s; |
||||
border-radius: 15%; |
||||
} |
||||
|
||||
&__circle.hiding, &__square.hiding { |
||||
opacity: 0; |
||||
} |
||||
} |
||||
|
||||
@keyframes ripple-effect { |
||||
0% { |
||||
transform: scale(0); |
||||
} |
||||
|
||||
to { |
||||
transform: scale(2); |
||||
} |
||||
} |
@ -0,0 +1,462 @@
@@ -0,0 +1,462 @@
|
||||
// credits to https://github.com/iamdustan/smoothscroll
|
||||
|
||||
type ScrollableElement = (Window & typeof globalThis) | Element; |
||||
export type SmoothScrollToOptions = Partial<{ |
||||
top: number, |
||||
left: number, |
||||
behavior: 'smooth' | 'auto' | 'instant', |
||||
scrollTime: number |
||||
}>; |
||||
|
||||
export const SCROLL_TIME = 468; |
||||
|
||||
// polyfill
|
||||
export default function polyfill() { |
||||
// aliases
|
||||
var w = window; |
||||
var d = document; |
||||
|
||||
// return if scroll behavior is supported and polyfill is not forced
|
||||
if( |
||||
'scrollBehavior' in d.documentElement.style && |
||||
(w as any).__forceSmoothScrollPolyfill__ !== true |
||||
) { |
||||
return; |
||||
} |
||||
|
||||
// globals
|
||||
var Element = w.HTMLElement || w.Element; |
||||
|
||||
// object gathering original scroll methods
|
||||
var original = { |
||||
scroll: w.scroll || w.scrollTo, |
||||
scrollBy: w.scrollBy, |
||||
elementScroll: Element.prototype.scroll || scrollElement, |
||||
scrollIntoView: Element.prototype.scrollIntoView |
||||
}; |
||||
|
||||
// define timing method
|
||||
var now = |
||||
w.performance && w.performance.now |
||||
? w.performance.now.bind(w.performance) |
||||
: Date.now; |
||||
|
||||
/** |
||||
* indicates if a the current browser is made by Microsoft |
||||
* @method isMicrosoftBrowser |
||||
* @param {String} userAgent |
||||
* @returns {Boolean} |
||||
*/ |
||||
function isMicrosoftBrowser(userAgent: string) { |
||||
var userAgentPatterns = ['MSIE ', 'Trident/', 'Edge/']; |
||||
|
||||
return new RegExp(userAgentPatterns.join('|')).test(userAgent); |
||||
} |
||||
|
||||
/* |
||||
* IE has rounding bug rounding down clientHeight and clientWidth and |
||||
* rounding up scrollHeight and scrollWidth causing false positives |
||||
* on hasScrollableSpace |
||||
*/ |
||||
var ROUNDING_TOLERANCE = isMicrosoftBrowser(w.navigator.userAgent) ? 1 : 0; |
||||
|
||||
/** |
||||
* changes scroll position inside an element |
||||
* @method scrollElement |
||||
* @param {Number} x |
||||
* @param {Number} y |
||||
* @returns {undefined} |
||||
*/ |
||||
function scrollElement(this: Element, x: number, y: number) { |
||||
this.scrollLeft = x; |
||||
this.scrollTop = y; |
||||
} |
||||
|
||||
/** |
||||
* returns result of applying ease math function to a number |
||||
* @method ease |
||||
* @param {Number} k |
||||
* @returns {Number} |
||||
*/ |
||||
function ease(k: number) { |
||||
return 0.5 * (1 - Math.cos(Math.PI * k)); |
||||
} |
||||
|
||||
/** |
||||
* indicates if a smooth behavior should be applied |
||||
* @method shouldBailOut |
||||
* @param {Number|Object} firstArg |
||||
* @returns {Boolean} |
||||
*/ |
||||
function shouldBailOut(firstArg: SmoothScrollToOptions) { |
||||
if( |
||||
firstArg === null || |
||||
typeof firstArg !== 'object' || |
||||
firstArg.behavior === undefined || |
||||
firstArg.behavior === 'auto' || |
||||
firstArg.behavior === 'instant' |
||||
) { |
||||
// first argument is not an object/null
|
||||
// or behavior is auto, instant or undefined
|
||||
return true; |
||||
} |
||||
|
||||
if(typeof firstArg === 'object' && firstArg.behavior === 'smooth') { |
||||
// first argument is an object and behavior is smooth
|
||||
return false; |
||||
} |
||||
|
||||
// throw error when behavior is not supported
|
||||
throw new TypeError( |
||||
'behavior member of ScrollOptions ' + |
||||
firstArg.behavior + |
||||
' is not a valid value for enumeration ScrollBehavior.' |
||||
); |
||||
} |
||||
|
||||
/** |
||||
* indicates if an element has scrollable space in the provided axis |
||||
* @method hasScrollableSpace |
||||
* @param {Node} el |
||||
* @param {String} axis |
||||
* @returns {Boolean} |
||||
*/ |
||||
function hasScrollableSpace(el: Element, axis: 'X' | 'Y') { |
||||
if(axis === 'Y') { |
||||
return el.clientHeight + ROUNDING_TOLERANCE < el.scrollHeight; |
||||
} |
||||
|
||||
if(axis === 'X') { |
||||
return el.clientWidth + ROUNDING_TOLERANCE < el.scrollWidth; |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* indicates if an element has a scrollable overflow property in the axis |
||||
* @method canOverflow |
||||
* @param {Node} el |
||||
* @param {String} axis |
||||
* @returns {Boolean} |
||||
*/ |
||||
function canOverflow(el: Element, axis: string) { |
||||
// @ts-ignore
|
||||
var overflowValue: string = w.getComputedStyle(el, null)['overflow' + axis]; |
||||
|
||||
return overflowValue === 'auto' || overflowValue === 'scroll'; |
||||
} |
||||
|
||||
/** |
||||
* indicates if an element can be scrolled in either axis |
||||
* @method isScrollable |
||||
* @param {Node} el |
||||
* @param {String} axis |
||||
* @returns {Boolean} |
||||
*/ |
||||
function isScrollable(el: Element) { |
||||
var isScrollableY = hasScrollableSpace(el, 'Y') && canOverflow(el, 'Y'); |
||||
var isScrollableX = hasScrollableSpace(el, 'X') && canOverflow(el, 'X'); |
||||
|
||||
return isScrollableY || isScrollableX; |
||||
} |
||||
|
||||
/** |
||||
* finds scrollable parent of an element |
||||
* @method findScrollableParent |
||||
* @param {Node} el |
||||
* @returns {Node} el |
||||
*/ |
||||
function findScrollableParent(el: Element) { |
||||
while(el !== d.body && isScrollable(el) === false) { |
||||
// @ts-ignore
|
||||
el = el.parentNode || el.host; |
||||
} |
||||
|
||||
return el; |
||||
} |
||||
|
||||
/** |
||||
* self invoked function that, given a context, steps through scrolling |
||||
* @method step |
||||
* @param {Object} context |
||||
* @returns {undefined} |
||||
*/ |
||||
function step(context: { |
||||
startTime: number, |
||||
scrollTime: number, |
||||
startX: number, |
||||
startY: number, |
||||
x: number, |
||||
y: number, |
||||
scrollable: ScrollableElement, |
||||
method: (this: ScrollableElement, currentX: number, currentY: number) => any |
||||
}) { |
||||
var time = now(); |
||||
var value: number; |
||||
var currentX: number; |
||||
var currentY: number; |
||||
var elapsed = (time - context.startTime) / context.scrollTime; |
||||
|
||||
// avoid elapsed times higher than one
|
||||
elapsed = elapsed > 1 ? 1 : elapsed; |
||||
|
||||
// apply easing to elapsed time
|
||||
value = ease(elapsed); |
||||
|
||||
currentX = context.startX + (context.x - context.startX) * value; |
||||
currentY = context.startY + (context.y - context.startY) * value; |
||||
|
||||
context.method.call(context.scrollable, currentX, currentY); |
||||
|
||||
// scroll more if we have not reached our destination
|
||||
if(currentX !== context.x || currentY !== context.y) { |
||||
w.requestAnimationFrame(step.bind(w, context)); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* scrolls window or element with a smooth behavior |
||||
* @method smoothScroll |
||||
* @param {Object|Node} el |
||||
* @param {Number} x |
||||
* @param {Number} y |
||||
* @returns {undefined} |
||||
*/ |
||||
function smoothScroll(el: Element, x: number, y: number, scrollTime = SCROLL_TIME) { |
||||
var scrollable: ScrollableElement; |
||||
var startX: number; |
||||
var startY: number; |
||||
var method: any; |
||||
var startTime = now(); |
||||
|
||||
// define scroll context
|
||||
if(el === d.body) { |
||||
scrollable = w; |
||||
startX = w.scrollX || w.pageXOffset; |
||||
startY = w.scrollY || w.pageYOffset; |
||||
method = original.scroll; |
||||
} else { |
||||
scrollable = el; |
||||
startX = el.scrollLeft; |
||||
startY = el.scrollTop; |
||||
method = scrollElement; |
||||
} |
||||
|
||||
// scroll looping over a frame
|
||||
step({ |
||||
scrollable: scrollable, |
||||
method: method, |
||||
scrollTime, |
||||
startTime: startTime, |
||||
startX: startX, |
||||
startY: startY, |
||||
x: x, |
||||
y: y |
||||
}); |
||||
} |
||||
|
||||
// ORIGINAL METHODS OVERRIDES
|
||||
// w.scroll and w.scrollTo
|
||||
w.scroll = w.scrollTo = function() { |
||||
const options = arguments[0] as SmoothScrollToOptions; |
||||
// avoid action when no arguments are passed
|
||||
if(options === undefined) { |
||||
return; |
||||
} |
||||
|
||||
// avoid smooth behavior if not required
|
||||
if(shouldBailOut(options) === true) { |
||||
original.scroll.call( |
||||
w, |
||||
options.left !== undefined |
||||
? options.left |
||||
: typeof options !== 'object' |
||||
? options |
||||
: w.scrollX || w.pageXOffset, |
||||
// use top prop, second argument if present or fallback to scrollY
|
||||
options.top !== undefined |
||||
? options.top |
||||
: arguments[1] !== undefined |
||||
? arguments[1] |
||||
: w.scrollY || w.pageYOffset |
||||
); |
||||
|
||||
return; |
||||
} |
||||
|
||||
// LET THE SMOOTHNESS BEGIN!
|
||||
smoothScroll.call( |
||||
w, |
||||
d.body, |
||||
options.left !== undefined |
||||
? ~~options.left |
||||
: w.scrollX || w.pageXOffset, |
||||
options.top !== undefined |
||||
? ~~options.top |
||||
: w.scrollY || w.pageYOffset, |
||||
options.scrollTime |
||||
); |
||||
}; |
||||
|
||||
// w.scrollBy
|
||||
w.scrollBy = function() { |
||||
const options = arguments[0] as SmoothScrollToOptions; |
||||
// avoid action when no arguments are passed
|
||||
if(options === undefined) { |
||||
return; |
||||
} |
||||
|
||||
// avoid smooth behavior if not required
|
||||
if(shouldBailOut(options)) { |
||||
original.scrollBy.call( |
||||
w, |
||||
options.left !== undefined |
||||
? options.left |
||||
: typeof options !== 'object' ? options : 0, |
||||
options.top !== undefined |
||||
? options.top |
||||
: arguments[1] !== undefined ? arguments[1] : 0 |
||||
); |
||||
|
||||
return; |
||||
} |
||||
|
||||
// LET THE SMOOTHNESS BEGIN!
|
||||
smoothScroll.call( |
||||
w, |
||||
d.body, |
||||
~~options.left + (w.scrollX || w.pageXOffset), |
||||
~~options.top + (w.scrollY || w.pageYOffset), |
||||
options.scrollTime |
||||
); |
||||
}; |
||||
|
||||
// Element.prototype.scroll and Element.prototype.scrollTo
|
||||
Element.prototype.scroll = Element.prototype.scrollTo = function() { |
||||
const options = arguments[0] as SmoothScrollToOptions; |
||||
// avoid action when no arguments are passed
|
||||
if(options === undefined) { |
||||
return; |
||||
} |
||||
|
||||
// avoid smooth behavior if not required
|
||||
if(shouldBailOut(options) === true) { |
||||
// if one number is passed, throw error to match Firefox implementation
|
||||
if(typeof options === 'number' && arguments[1] === undefined) { |
||||
throw new SyntaxError('Value could not be converted'); |
||||
} |
||||
|
||||
original.elementScroll.call( |
||||
this, |
||||
// use left prop, first number argument or fallback to scrollLeft
|
||||
options.left !== undefined |
||||
? ~~options.left |
||||
: typeof options !== 'object' ? ~~options : this.scrollLeft, |
||||
// use top prop, second argument or fallback to scrollTop
|
||||
options.top !== undefined |
||||
? ~~options.top |
||||
: arguments[1] !== undefined ? ~~arguments[1] : this.scrollTop |
||||
); |
||||
|
||||
return; |
||||
} |
||||
|
||||
var left = options.left; |
||||
var top = options.top; |
||||
|
||||
// LET THE SMOOTHNESS BEGIN!
|
||||
smoothScroll.call( |
||||
this, |
||||
this, |
||||
typeof left === 'undefined' ? this.scrollLeft : ~~left, |
||||
typeof top === 'undefined' ? this.scrollTop : ~~top, |
||||
options.scrollTime |
||||
); |
||||
}; |
||||
|
||||
// Element.prototype.scrollBy
|
||||
Element.prototype.scrollBy = function() { |
||||
const options = arguments[0] as SmoothScrollToOptions; |
||||
// avoid action when no arguments are passed
|
||||
if(options === undefined) { |
||||
return; |
||||
} |
||||
|
||||
// avoid smooth behavior if not required
|
||||
if(shouldBailOut(options) === true) { |
||||
original.elementScroll.call( |
||||
this, |
||||
options.left !== undefined |
||||
? ~~options.left + this.scrollLeft |
||||
: ~~options + this.scrollLeft, |
||||
options.top !== undefined |
||||
? ~~options.top + this.scrollTop |
||||
: ~~arguments[1] + this.scrollTop |
||||
); |
||||
|
||||
return; |
||||
} |
||||
|
||||
this.scroll({ |
||||
left: ~~options.left + this.scrollLeft, |
||||
top: ~~options.top + this.scrollTop, |
||||
behavior: options.behavior as any, |
||||
scrollTime: options.scrollTime |
||||
} as any); |
||||
}; |
||||
|
||||
// Element.prototype.scrollIntoView
|
||||
Element.prototype.scrollIntoView = function() { |
||||
const options = arguments[0] as SmoothScrollToOptions; |
||||
// avoid smooth behavior if not required
|
||||
if(shouldBailOut(options) === true) { |
||||
original.scrollIntoView.call( |
||||
this, |
||||
(options === undefined ? true : options) as any |
||||
); |
||||
|
||||
return; |
||||
} |
||||
|
||||
// LET THE SMOOTHNESS BEGIN!
|
||||
var scrollableParent = findScrollableParent(this); |
||||
var parentRects = scrollableParent.getBoundingClientRect(); |
||||
var clientRects = this.getBoundingClientRect(); |
||||
|
||||
if(scrollableParent !== d.body) { |
||||
// reveal element inside parent
|
||||
smoothScroll.call( |
||||
this, |
||||
scrollableParent, |
||||
scrollableParent.scrollLeft + clientRects.left - parentRects.left, |
||||
scrollableParent.scrollTop + clientRects.top - parentRects.top, |
||||
options.scrollTime |
||||
); |
||||
|
||||
// reveal parent in viewport unless is fixed
|
||||
if(w.getComputedStyle(scrollableParent).position !== 'fixed') { |
||||
w.scrollBy({ |
||||
left: parentRects.left, |
||||
top: parentRects.top, |
||||
behavior: 'smooth', |
||||
scrollTime: options.scrollTime |
||||
} as any); |
||||
} |
||||
} else { |
||||
// reveal element in viewport
|
||||
w.scrollBy({ |
||||
left: clientRects.left, |
||||
top: clientRects.top, |
||||
behavior: 'smooth', |
||||
scrollTime: options.scrollTime |
||||
} as any); |
||||
} |
||||
}; |
||||
} |
||||
|
||||
/* if (typeof exports === 'object' && typeof module !== 'undefined') { |
||||
// commonjs
|
||||
module.exports = { polyfill: polyfill }; |
||||
} else { |
||||
// global
|
||||
polyfill(); |
||||
} */ |
File diff suppressed because one or more lines are too long
Binary file not shown.
Loading…
Reference in new issue