📁 Changed TDrive root folder (#16)

📁 Changed TDrive root folder
This commit is contained in:
Montassar Ghanmy
2023-04-11 10:04:29 +01:00
committed by GitHub
parent 3b3f67af07
commit e0615fa867
10548 changed files with 48 additions and 48 deletions
@@ -0,0 +1,206 @@
type TreeItem = { [key: string]: File | TreeItem };
export type FileTreeObject = {
tree: TreeItem;
documentsCount: number;
totalSize: number;
};
export const getFilesTree = (
event: Event & { dataTransfer: DataTransfer },
fcb?: (tree: any, documentsCount: number, totalSize: number) => void,
): Promise<FileTreeObject> => {
return new Promise<FileTreeObject>(function (resolve) {
function newDirectoryApi(input: DataTransfer, cb: (files?: File[], paths?: string[]) => void) {
const fd: any[] = [],
files: any[] = [];
const iterate = function (entries: any[], path: string, resolve: (v: any[]) => void) {
const promises: any[] = [];
entries.forEach(function (entry: any) {
promises.push(
new Promise(function (resolve) {
if ('getFilesAndDirectories' in entry) {
entry.getFilesAndDirectories().then(function (entries: any[]) {
iterate(entries, entry.path + '/', resolve);
});
} else {
if (entry.name) {
const p = (path + entry.name).replace(/^[/\\]/, '');
fd.push(entry);
files.push(p);
if (files.length > 1000000) {
return false;
}
}
resolve(true);
}
}),
);
});
if (files.length > 1000000) {
return false;
}
Promise.all(promises).then(resolve);
};
(input as any).getFilesAndDirectories().then(function (entries: any) {
new Promise(function (resolve) {
iterate(entries, '/', resolve);
}).then(cb.bind(null, fd, files));
});
}
// old prefixed API implemented in Chrome 11+ as well as array fallback
function arrayApi(input: DataTransfer, cb: (files?: File[], paths?: string[]) => void) {
const fd: any[] = [],
files: any[] = [];
[].slice.call(input.files).forEach(function (file: File) {
fd.push(file);
files.push(file.webkitRelativePath || file.name);
if (files.length > 1000000) {
return false;
}
});
if (files.length > 1000000) {
return false;
}
cb(fd, files);
}
// old drag and drop API implemented in Chrome 11+
function entriesApi(
items: DataTransferItemList,
cb: (files?: File[], paths?: string[]) => void,
) {
const fd: any[] = [],
files: any[] = [],
rootPromises: any[] = [];
function readEntries(entry: any, reader: any, oldEntries: any, cb: any) {
const dirReader = reader || entry.createReader();
dirReader.readEntries(function (entries: any) {
const newEntries = oldEntries ? oldEntries.concat(entries) : entries;
if (entries.length) {
setTimeout(readEntries.bind(null, entry, dirReader, newEntries, cb), 0);
} else {
cb(newEntries);
}
});
}
function readDirectory(entry: any, path: null | string, resolve: (v: any) => void) {
if (!path) path = entry.name;
readEntries(entry, 0, 0, function (entries: any[]) {
const promises: Promise<any>[] = [];
entries.forEach(function (entry: any) {
promises.push(
new Promise(function (resolve) {
if (entry.isFile) {
entry.file(function (file: File) {
const p = path + '/' + file.name;
fd.push(file);
files.push(p);
if (files.length > 1000000) {
return false;
}
resolve(true);
}, resolve.bind(null, true));
} else readDirectory(entry, path + '/' + entry.name, resolve);
}),
);
});
Promise.all(promises).then(resolve.bind(null, true));
});
}
[].slice.call(items).forEach(function (entry: any) {
entry = entry.webkitGetAsEntry();
if (entry) {
rootPromises.push(
new Promise(function (resolve) {
if (entry.isFile) {
entry.file(function (file: File) {
fd.push(file);
files.push(file.name);
if (files.length > 1000000) {
return false;
}
resolve(true);
}, resolve.bind(null, true));
} else if (entry.isDirectory) {
readDirectory(entry, null, resolve);
}
}),
);
}
});
if (files.length > 1000000) {
return false;
}
Promise.all(rootPromises).then(cb.bind(null, fd, files));
}
const cb = function (event: Event, files: File[], paths?: string[]) {
const documents_number = paths ? paths.length : 0;
let total_size = 0;
const tree: any = {};
(paths || []).forEach(function (path, file_index) {
let dirs = tree;
const real_file = files[file_index];
total_size += real_file.size;
path.split('/').forEach(function (dir, dir_index) {
if (dir.indexOf('.') === 0) {
return;
}
if (dir_index === path.split('/').length - 1) {
dirs[dir] = real_file;
} else {
if (!dirs[dir]) {
dirs[dir] = {};
}
dirs = dirs[dir];
}
});
});
fcb && fcb(tree, documents_number, total_size);
resolve({ tree, documentsCount: documents_number, totalSize: total_size });
};
if (event.dataTransfer) {
const dt = event.dataTransfer;
if (dt.items && dt.items.length && 'webkitGetAsEntry' in dt.items[0]) {
entriesApi(dt.items, (files, paths) => cb(event, files || [], paths));
} else if ('getFilesAndDirectories' in dt) {
newDirectoryApi(dt, (files, paths) => cb(event, files || [], paths));
} else if (dt.files) {
arrayApi(dt, (files, paths) => cb(event, files || [], paths));
} else cb(event, [], []);
} else if (event.target) {
const t = event.target as any;
if (t.files && t.files.length) {
arrayApi(t, (files, paths) => cb(event, files || [], paths));
} else if ('getFilesAndDirectories' in t) {
newDirectoryApi(t, (files, paths) => cb(event, files || [], paths));
} else {
cb(event, [], []);
}
} else {
fcb && fcb([(event.target as any).files[0]], 1, (event.target as any).files[0].size);
resolve({
tree: (event.target as any).files[0],
documentsCount: 1,
totalSize: (event.target as any).files[0].size,
});
}
});
};
@@ -0,0 +1,329 @@
import Observable from 'app/deprecated/CollectionsV1/observable.js';
import Number from 'app/features/global/utils/Numbers';
import DriveService from 'app/deprecated/Apps/Drive/Drive.js';
import Collections from 'app/deprecated/CollectionsV1/Collections/Collections.js';
import Resumable from 'app/features/files/utils/resumable.js';
import Globals from 'app/features/global/services/globals-tdrive-app-service';
import Api from 'app/features/global/framework/api-service';
import JWTStorage from 'app/features/auth/jwt-storage-service';
class UploadManager extends Observable {
constructor() {
super();
this.setObservableName('upload_manager');
this.reinit();
window.uploadManager = this;
}
reinit() {
if (this.reinitTimeout) clearTimeout(this.reinitTimeout);
if (this.reinitTimeoutBefore) clearTimeout(this.reinitTimeoutBefore);
this.currentUploadTotalSize = 0;
this.currentUploadTotalNumber = 0;
this.currentUploadedTotalSize = 0;
this.currentUploadedFilesNumber = 0;
this.currentUploadingFilesNumber = 0;
this.currentCancelledFilesNumber = 0;
this.currentWaitingFilesNumber = 0;
this.currentErrorFilesNumber = 0;
this.currentUploadFiles = [];
this.currentUploadStartTime = new Date();
this.will_close = false;
this.notify();
}
reinitAfterDelay() {
if (this.reinitTimeout) clearTimeout(this.reinitTimeout);
if (this.reinitTimeoutBefore) clearTimeout(this.reinitTimeoutBefore);
this.reinitTimeoutBefore = setTimeout(() => {
this.will_close = true;
this.notify();
this.reinitTimeout = setTimeout(() => {
this.reinit();
}, 500);
}, 800);
}
startUpload(
elements,
total_number,
total_size,
drive_parent,
upload_options,
driveCollectionKey,
callback,
) {
if (this.addElementsRecursive(elements, '')) {
this.uploadRecursive(elements, drive_parent, upload_options, driveCollectionKey, callback);
} else {
this.currentUploadFiles = [];
this.currentUploadTotalNumber = 0;
this.currentUploadTotalSize = 0;
this.currentWaitingFilesNumber = 0;
this.currentUploadStartTime = new Date();
this.reinit();
}
}
addElementsRecursive(elements, path) {
return Object.keys(elements).every(name => {
var element = elements[name];
if (element.size) {
//File
this.currentUploadTotalSize += element.size;
this.currentUploadTotalNumber++;
this.currentUploadFiles.push({
unid: Number.unid(),
progress: 0,
cancelled: false,
error: false,
name: name,
path: path,
xhr: null,
file: element,
});
} else {
//Directory
if (!this.addElementsRecursive(element, path + '/' + name)) {
return false;
}
}
return true;
});
}
uploadRecursive(elements, drive_parent, upload_options, driveCollectionKey, callback) {
Object.keys(elements).forEach(name => {
var element = elements[name];
if (element.size) {
//File
this.uploadFile(element, drive_parent, upload_options, driveCollectionKey, callback);
} else {
//Directory
this.createDirectory(name, drive_parent, upload_options, driveCollectionKey, directory => {
if (callback) callback(directory);
if (directory) {
this.uploadRecursive(element, directory, upload_options, driveCollectionKey);
}
});
}
});
}
createDirectory(name, parent, upload_options, driveCollectionKey, callback) {
if (this.currentUploadingFilesNumber > 4) {
setTimeout(() => {
this.createDirectory(name, parent, upload_options, driveCollectionKey, callback);
}, 500);
return;
}
this.currentUploadingFilesNumber++;
DriveService.createDirectory(
upload_options.workspace_id,
name,
parent,
driveCollectionKey,
dir => {
this.currentUploadingFilesNumber--;
if (callback) callback(dir);
},
);
}
abort(elements) {
var that = this;
if (elements.length === undefined) {
elements = [elements];
}
elements.forEach(element => {
if (element.resumable) element.resumable.cancel();
element.xhr_cancelled = true;
element.cancelled = true;
that.currentCancelledFilesNumber++;
that.currentUploadingFilesNumber--;
if (
that.currentUploadedFilesNumber +
that.currentCancelledFilesNumber +
that.currentErrorFilesNumber >=
that.currentUploadTotalNumber
) {
that.reinitAfterDelay();
}
});
that.notify();
}
uploadFile(element, drive_parent, upload_options, driveCollectionKey, callback, timeout_count) {
this.currentWaitingFilesNumber++;
if (!timeout_count) {
timeout_count = 0;
}
if (this.currentUploadingFilesNumber > 4) {
setTimeout(() => {
this.currentWaitingFilesNumber--;
this.uploadFile(
element,
drive_parent,
upload_options,
driveCollectionKey,
callback,
timeout_count + 1,
);
}, Math.max(30000, 500 * (this.currentWaitingFilesNumber + 1) + Math.random() * 1000));
return;
}
this.currentWaitingFilesNumber--;
this.currentUploadingFilesNumber++;
// eslint-disable-next-line no-unused-vars
var listIndex = 0;
var current_item = 0;
this.currentUploadFiles.forEach((item, i) => {
if (item.file === element) {
listIndex = i;
current_item = item;
}
});
var that = this;
if (current_item.xhr_cancelled === true) {
return;
}
var extension = element.name.split('.');
var r_data = {
workspace_id: upload_options.workspace_id,
name: element.name,
extension: extension[extension.length - 1],
};
//Use Resumable.js for upload by chunk
var r = new Resumable({
target: `${Globals.api_root_url}/ajax/driveupload/upload`,
chunkSize: 50000000,
testChunks: false,
simultaneousUploads: 5,
withCredentials: true,
maxChunkRetries: 2,
headers: {
Authorization: JWTStorage.getAutorizationHeader(),
},
query: {
object: JSON.stringify({
id: upload_options.new_version ? upload_options.file_id : null,
_once_new_version: upload_options.new_version,
front_id: Number.unid(),
is_directory: false,
name: element.name,
parent_id: drive_parent ? drive_parent.id : null,
workspace_id: upload_options.workspace_id,
detached: upload_options.detached,
}),
},
generateUniqueIdentifier: (file, event) => {
return 'no_id';
},
});
current_item.resumable = r;
current_item.timeout = setTimeout(() => {
this.currentUploadingFilesNumber--;
}, 60000);
r.on('fileSuccess', function (file, message) {
clearTimeout(current_item.timeout);
that.currentUploadedFilesNumber++;
that.currentUploadingFilesNumber--;
current_item.progress = 1;
if (
that.currentUploadedFilesNumber +
that.currentCancelledFilesNumber +
that.currentErrorFilesNumber >=
that.currentUploadTotalNumber
) {
that.reinitAfterDelay();
}
var resp = JSON.parse(message);
if (resp.data && resp.data.object) {
Collections.get('drive').updateObject(resp.data.object);
Collections.get('drive').share(resp.data.object);
if (callback) callback(Collections.get('drive').find(resp.data.object.id));
var parent = Collections.get('drive').find(resp.data.object.parent_id);
if (parent) {
parent.size = parseInt(parent.size) + parseInt(resp.data.object.size || 0);
Collections.get('drive').updateObject(parent);
}
} else {
current_item.error = 1;
}
that.notify();
});
r.on('progress', function (file, message) {
current_item.progress = Math.min(r.progress(), 0.99);
that.notify();
clearTimeout(current_item.timeout);
current_item.timeout = setTimeout(() => {
this.currentUploadingFilesNumber--;
}, 60000);
});
r.on('fileError', function (file, message) {
clearTimeout(current_item.timeout);
that.currentErrorFilesNumber++;
that.currentUploadingFilesNumber--;
current_item.error = true;
if (
that.currentUploadedFilesNumber +
that.currentCancelledFilesNumber +
that.currentErrorFilesNumber >=
that.currentUploadTotalNumber
) {
that.reinitAfterDelay();
}
that.notify();
});
r.addFile(element);
Api.post(
'/ajax/driveupload/preprocess',
{
workspace_id: r_data.workspace_id,
name: r_data.name,
identifier: r_data.identifier,
extension: r_data.extension,
},
res => {
var identifier = res.identifier;
var file = r.getFromUniqueIdentifier('no_id');
file.uniqueIdentifier = identifier;
r.upload();
},
);
}
}
const service = new UploadManager();
export default service;
@@ -0,0 +1,170 @@
import React from 'react';
import UploadManager from './upload-manager.js';
import CloseIcon from '@material-ui/icons/CloseOutlined';
import './uploads.scss';
import moment from 'moment';
import Languages from 'app/features/global/services/languages-service';
export default class UploadViewer extends React.Component {
constructor(props) {
super();
this.state = {
upload_manager: UploadManager,
};
UploadManager.addListener(this);
}
componentWillUnmount() {
UploadManager.removeListener(this);
}
render() {
if (this.state.upload_manager.currentUploadTotalNumber <= 0) {
// eslint-disable-next-line react/no-direct-mutation-state
this.state.large = true;
return '';
}
var documents = this.state.upload_manager.currentUploadFiles.filter(d => !d.path.substr(1));
var folders = {};
var folders_content = {};
this.state.upload_manager.currentUploadFiles
.filter(d => d.path.substr(1))
.forEach(d => {
var folder = d.path.substr(1).split('/')[0];
if (!folders[folder]) {
folders[folder] = {
total: 0,
total_progress: 0,
total_uploaded: 0,
error: true,
cancelled: true,
};
folders_content[folder] = [];
}
if (!d.cancelled) {
folders[folder].cancelled = false;
}
if (!d.error) {
folders[folder].error = false;
}
if (!d.error && !d.cancelled) {
folders[folder].total++;
folders[folder].total_progress += d.progress;
if (d.progress === 1) {
folders[folder].total_uploaded += 1;
}
folders_content[folder].push(d);
}
});
Object.keys(folders).forEach(name => {
documents.push({
progress: folders[name].total_progress / folders[name].total,
name: name,
cancelled: folders[name].cancelled,
error: folders[name].error,
folder_total: folders[name].total,
folder_total_uploaded: folders[name].total_uploaded,
all_files: folders_content[name],
});
});
var total_finished =
this.state.upload_manager.currentUploadedFilesNumber +
this.state.upload_manager.currentCancelledFilesNumber +
this.state.upload_manager.currentErrorFilesNumber;
var todo = this.state.upload_manager.currentUploadTotalNumber;
var total_finished_size = this.state.upload_manager.currentUploadFiles
.map(a => {
if (a.error || a.cancelled) {
return (a.file || {}).size || 0;
}
if (a.progress > 0) {
return ((a.file || {}).size || 0) * a.progress;
}
return 0;
})
.reduce((a, b) => {
return a + b;
});
var todo_size = this.state.upload_manager.currentUploadFiles
.map(a => {
return (a.file || {}).size || 0;
})
.reduce((a, b) => {
return a + b;
});
var remaining_time = 0;
if (total_finished_size > 0) {
remaining_time =
((todo_size - total_finished_size) / 1000000) *
((new Date().getTime() - this.state.upload_manager.currentUploadStartTime) /
(total_finished_size / 1000000));
}
return (
<div
className={
'upload_viewer ' +
(this.state.upload_manager.will_close ? 'fade_out ' : 'skew_in_left_nobounce ')
}
>
<div className="title" onClick={() => this.setState({ large: !this.state.large })}>
{Languages.t('general.uploading')} {total_finished}/{todo}
</div>
{remaining_time > 0 && (
<div className="subtitle">
Will end {moment(new Date().getTime() + remaining_time).fromNow()}
</div>
)}
<div className="uploads" style={{ display: this.state.large ? 'block' : 'none' }}>
{documents
.sort((a, b) => (a.progress === 1) - (b.progress === 1))
.map(item => {
return (
<div
key={item.unid}
className={
'uploadingFile ' +
(item.cancelled || item.error ? 'stopped ' : '') +
(item.progress === 1 && !item.error ? 'done ' : '') +
(item.progress < 1 && !item.error && !item.cancelled ? 'progress ' : '')
}
>
<div
className="progress_bar"
style={{ width: parseInt(item.progress * 100) + '%' }}
/>
<div className="name">
{item.name} {item.folder_total !== undefined && '(Folder)'}
</div>
{item.path && item.path.substr(1) && (
<div className="path">{item.path.substr(1)}</div>
)}
{item.folder_total !== undefined && (
<div className="path">
{item.folder_total_uploaded}/{item.folder_total}
</div>
)}
<div className="progress">{parseInt((item.progress || 0) * 100)}%</div>
<div
className="cancel"
onClick={() => {
UploadManager.abort(item.all_files || item);
}}
>
<CloseIcon className="m-icon-small" />
</div>
</div>
);
})}
</div>
</div>
);
}
}
+287
View File
@@ -0,0 +1,287 @@
/* eslint-disable react/prop-types */
/* eslint-disable @typescript-eslint/no-explicit-any */
import React from 'react';
import UploadManager from './upload-manager';
import Languages from 'app/features/global/services/languages-service';
import { Upload } from 'react-feather';
import classNames from 'classnames';
import './uploads.scss';
import { Typography } from 'antd';
type PropsType = {
[key: string]: any;
onAddFiles: (files: File[], event: Event & { dataTransfer: DataTransfer }) => void;
};
type StateType = { [key: string]: any };
type FileInputType = any;
type FileObjectType = { [key: string]: any };
let sharedFileInput: any = null;
let sharedFolderInput: any = null;
export default class UploadZone extends React.Component<PropsType, StateType> {
file_input: FileInputType = {};
stopHoverTimeout: ReturnType<typeof setTimeout> | undefined;
node: HTMLDivElement | null = null;
constructor(props: PropsType) {
super(props);
this.state = {
upload_manager: UploadManager,
};
UploadManager.addListener(this);
}
componentWillUnmount() {
UploadManager.removeListener(this);
}
componentDidMount() {
this.node && this.watch(this.node, document.body);
if (
(this.props.directory && !sharedFolderInput) ||
(!this.props.directory && !sharedFileInput)
) {
this.file_input = document.createElement('input');
this.file_input.type = 'file';
this.file_input.style.position = 'absolute';
this.file_input.style.top = '-10000px';
this.file_input.style.left = '-10000px';
this.file_input.style.width = '100px';
this.file_input.multiple = this.props.multiple ? true : false;
this.file_input.directory = this.props.directory ? true : false;
this.file_input.webkitdirectory = this.props.directory ? true : false;
this.setCallback();
document.body.appendChild(this.file_input);
if (this.props.directory) {
sharedFolderInput = this.file_input;
} else {
sharedFileInput = this.file_input;
}
} else {
this.file_input = sharedFileInput;
}
}
setCallback() {
this.file_input.onchange = (e: any) => {
this.change(e);
};
}
open() {
if (this.props.disabled) {
return;
}
this.setCallback();
this.file_input.click();
}
/**
*
* @param tree
* @param nb
* @param totalSize
*/
upload(tree: any, nb?: number, totalSize?: number) {
if (this.props.multiple === false) {
nb = 1;
let file: any = null;
Object.keys(tree).every(i => {
const element = tree[i];
if (element.size) {
file = {};
file[i] = element;
totalSize = element.size;
return false;
}
return true;
});
if (!file) {
return;
}
}
UploadManager.startUpload(
tree,
nb,
totalSize,
this.props.parent,
this.props.uploadOptions,
this.props.driveCollectionKey,
this.props.onUploaded,
);
}
/**
*
* @param event
*/
change(event: any) {
if (this.props.disabled) return;
event.preventDefault();
this.hover(false);
const files = event.target.files || event.dataTransfer.files || [];
if (this.props.onAddFiles && files.length > 0) return this.props.onAddFiles([...files], event);
}
/**
*
* @param currentNode
* @param body
*/
watch(currentNode: HTMLElement, body: HTMLElement) {
/**
* DRAGOVER EVENT
*/
currentNode.addEventListener('dragover', () => currentNode.classList.add('input-drag-focus'));
body.addEventListener('dragover', (e: DragEvent) => {
body.classList.add('body-drag-focus');
this.hover(true, e);
e.preventDefault();
});
/**
* DRAGLEAVE EVENT
*/
currentNode.addEventListener('dragleave', () =>
currentNode.classList.remove('input-drag-focus'),
);
body.addEventListener('dragleave', (e: DragEvent) => {
body.classList.remove('body-drag-focus');
if (this.props.onDragLeave) {
this.props.onDragLeave();
}
this.hover(false, e);
e.preventDefault();
});
/**
* DROP EVENT
*/
currentNode.addEventListener('drop', (e: DragEvent) => {
currentNode.classList.contains('input-drag-focus') && this.change(e);
e.preventDefault();
});
body.addEventListener('drop', (e: DragEvent) => {
this.hover(false, e);
e.preventDefault();
});
/**
* DRAGENTER EVENT
*/
body.addEventListener('dragenter', (e: DragEvent) => {
if (!this.props.disabled && this.props.onDragEnter) {
this.props.onDragEnter();
}
this.hover(true, e);
e.preventDefault();
this.setCallback();
});
}
/**
* @param {Blob[]} files
* @returns
*/
uploadFiles(files: any = []) {
if (!this.props.allowPaste || !files.length) {
return;
}
const filesToUpload: any = {};
files.forEach((file: FileObjectType, index: number) => {
const filename = file.name
? file.name.replace(/\.(png|jpeg|jpg|tiff|gif)$/i, '')
: `file-${index}`;
filesToUpload[filename] = file;
});
this.upload(filesToUpload);
}
/**
*
* @param state
* @param event
*/
hover(state: any, event?: any) {
if (
!this.state.dragover &&
(!event || !event.dataTransfer || (event.dataTransfer.types || []).indexOf('Files') < 0)
) {
return;
}
if (!state) {
this.stopHoverTimeout = setTimeout(() => {
this.setState({ dragover: false });
}, 200);
return;
}
if (this.stopHoverTimeout) clearTimeout(this.stopHoverTimeout);
if (this.state.dragover !== state) {
this.setState({ dragover: state });
}
}
render() {
return (
<div
ref={node => node && (this.node = node)}
style={this.props.style}
className={classNames('upload_drop_zone', this.props.className)}
onClick={() => {
if (!this.props.disableClick) {
this.open();
}
}}
>
{!this.props.disabled && (
<div
className={classNames('on_drag_over_background', {
dragover: this.state.dragover,
})}
>
<div className={'dashed ' + this.props.overClassName}>
<div
className={classNames('centered', { skew_in_top_nobounce: !!this.state.dragover })}
>
<div className="subtitle">
<Upload size={18} className="small-right-margin" />
<Typography.Text strong style={{ color: 'var(--primary)' }}>
{Languages.t('components.upload.drop_files')}
</Typography.Text>
</div>
</div>
</div>
</div>
)}
{this.props.children}
</div>
);
}
}
+184
View File
@@ -0,0 +1,184 @@
.upload_viewer {
position: absolute;
right: 20px;
bottom: 20px;
width: auto;
height: auto;
box-shadow: var(--box-shadow-base);
border-radius: var(--border-radius-base);
overflow: hidden;
display: flex;
flex-direction: column;
z-index: 100;
.title {
background: var(--secondary);
color: #eeeeee;
font-size: 14px;
padding: 8px;
margin-bottom: 0px;
cursor: pointer;
}
.subtitle {
background: var(--primary-background);
color: var(--grey-dark);
font-size: 12px;
padding: 4px 8px;
margin-bottom: 0px;
}
.uploads {
background: #fff;
flex: 1;
overflow: auto;
max-height: 70vh;
width: 300px;
max-width: 50vw;
.uploadingFile {
position: relative;
padding: 4px;
border-bottom: 1px solid #eee;
font-size: 13px;
display: flex;
.name {
flex: 1;
padding: 4px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.progress {
padding: 4px;
}
.path {
background: var(--secondary);
padding: 4px;
color: #fff;
border-radius: var(--border-radius-base);
max-width: 100px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.name,
.path,
.progress {
z-index: 1;
}
&.done {
border-bottom: 1px solid #a8efa8;
.progress_bar {
width: 100% !important;
background: #b8ffb8;
}
}
&.stopped {
border-bottom: 1px solid #efa8a8;
.progress_bar {
width: 100% !important;
background: #ffb8b8;
}
}
.progress_bar {
position: absolute;
height: 100%;
z-index: 0;
background: #e5f3fd;
top: 0;
left: 0;
transition: width 0.2s;
}
.cancel {
opacity: 0;
pointer-events: none;
background: #f35e5e;
border-radius: 50%;
color: #fff;
margin-left: 10px;
width: 24px;
height: 24px;
padding: 3px;
box-sizing: border-box;
position: absolute;
right: 5px;
top: 3px;
z-index: 2;
cursor: pointer;
&:hover {
background: #e34e4e;
}
}
&.progress:hover {
.cancel {
opacity: 1;
pointer-events: all;
}
.progress {
opacity: 0;
}
}
}
}
}
.upload_drop_zone {
&.input-drag-focus > .on_drag_over_background.dragover > * {
background: var(--primary-background);
}
.on_drag_over_background {
position: absolute;
top: 0;
left: 0;
height: 100%;
width: 100%;
background: var(--white);
z-index: 100;
transition: opacity 0.5s;
pointer-events: none;
opacity: 0;
&.dragover {
opacity: 1;
}
.dashed {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
box-sizing: border-box;
transition: opacity 0.5s;
border: 2px dashed var(--primary-background);
border-radius: var(--border-radius-base);
.centered {
display: flex;
height: 100%;
align-items: center;
justify-content: center;
.subtitle {
display: flex;
align-items: center;
margin: 0;
color: var(--primary);
}
}
}
}
}