Merge branch 'feature/files' of https://github.com/ONLYOFFICE/AppServer into feature/files
This commit is contained in:
commit
346be5b443
@ -2,28 +2,17 @@ import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import PropTypes from 'prop-types';
|
||||
import { withRouter } from 'react-router';
|
||||
import { MainButton, DropDownItem, toastr, utils } from "asc-web-components";
|
||||
import { MainButton, DropDownItem } from "asc-web-components";
|
||||
import { withTranslation, I18nextProvider } from "react-i18next";
|
||||
import {
|
||||
setAction,
|
||||
fetchFiles,
|
||||
setTreeFolders,
|
||||
} from "../../../store/files/actions";
|
||||
import { isCanCreate, loopTreeFolders } from "../../../store/files/selectors";
|
||||
import store from "../../../store/store";
|
||||
import { setAction } from "../../../store/files/actions";
|
||||
import { isCanCreate } from "../../../store/files/selectors";
|
||||
import i18n from "../i18n";
|
||||
import { utils as commonUtils, constants, api } from "asc-web-common";
|
||||
import { utils as commonUtils, constants } from "asc-web-common";
|
||||
|
||||
const { changeLanguage } = commonUtils;
|
||||
const { FileAction } = constants;
|
||||
|
||||
class PureArticleMainButtonContent extends React.Component {
|
||||
state = {
|
||||
files: [],
|
||||
uploadedFiles: 0,
|
||||
totalSize: 0,
|
||||
percent: 0
|
||||
};
|
||||
|
||||
onCreate = (format) => {
|
||||
this.props.setAction({
|
||||
@ -34,199 +23,13 @@ class PureArticleMainButtonContent extends React.Component {
|
||||
};
|
||||
|
||||
onUploadFileClick = () => this.inputFilesElement.click();
|
||||
|
||||
onUploadFolderClick = () => this.inputFolderElement.click();
|
||||
|
||||
updateFiles = () => {
|
||||
const { onLoading, filter, currentFolderId, treeFolders, setTreeFolders } = this.props;
|
||||
|
||||
onLoading(true);
|
||||
const newFilter = filter.clone();
|
||||
fetchFiles(currentFolderId, newFilter, store.dispatch, treeFolders)
|
||||
.then((data) => {
|
||||
const path = data.selectedFolder.pathParts;
|
||||
const newTreeFolders = treeFolders;
|
||||
const folders = data.selectedFolder.folders;
|
||||
const foldersCount = data.selectedFolder.foldersCount;
|
||||
loopTreeFolders(path, newTreeFolders, folders, foldersCount);
|
||||
setTreeFolders(newTreeFolders);
|
||||
})
|
||||
.catch((err) => toastr.error(err))
|
||||
.finally(() => {
|
||||
onLoading(false);
|
||||
});
|
||||
};
|
||||
|
||||
sendChunk = (files, location, requestsDataArray, isLatestFile, indexOfFile) => {
|
||||
const sendRequestFunc = (index) => {
|
||||
let newState = {};
|
||||
api.files
|
||||
.uploadFile(location, requestsDataArray[index])
|
||||
.then((res) => {
|
||||
let newPercent = this.state.percent;
|
||||
const percent = (newPercent +=
|
||||
(files[indexOfFile].size / this.state.totalSize) * 100);
|
||||
if (res.data.data && res.data.data.uploaded) {
|
||||
files[indexOfFile].uploaded = true;
|
||||
newState = { files, percent };
|
||||
}
|
||||
if (index + 1 !== requestsDataArray.length) {
|
||||
sendRequestFunc(index + 1);
|
||||
} else if (isLatestFile) {
|
||||
this.updateFiles();
|
||||
newState = Object.assign({}, newState, {
|
||||
uploadedFiles: this.state.uploadedFiles + 1,
|
||||
});
|
||||
return;
|
||||
} else {
|
||||
newState = Object.assign({}, newState, {
|
||||
uploadedFiles: this.state.uploadedFiles + 1,
|
||||
});
|
||||
this.startSessionFunc(indexOfFile + 1);
|
||||
}
|
||||
})
|
||||
.catch((err) => toastr.error(err))
|
||||
.finally(() => {
|
||||
if (
|
||||
newState.hasOwnProperty("files") ||
|
||||
newState.hasOwnProperty("percent") ||
|
||||
newState.hasOwnProperty("uploadedFiles")
|
||||
) {
|
||||
let progressVisible = true;
|
||||
let uploadedFiles = newState.uploadedFiles;
|
||||
let percent = newState.percent;
|
||||
if (newState.uploadedFiles === files.length) {
|
||||
percent = 100;
|
||||
newState.percent = 0;
|
||||
newState.uploadedFiles = 0;
|
||||
progressVisible = false;
|
||||
}
|
||||
this.setState(newState, () => {
|
||||
this.props.setProgressValue(percent);
|
||||
this.props.setProgressLabel(
|
||||
this.props.t("UploadingLabel", {
|
||||
file: uploadedFiles,
|
||||
totalFiles: files.length,
|
||||
})
|
||||
);
|
||||
if (!progressVisible) {
|
||||
this.props.setProgressVisible(false);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
sendRequestFunc(0);
|
||||
};
|
||||
|
||||
startSessionFunc = indexOfFile => {
|
||||
const { files } = this.state;
|
||||
const { currentFolderId } = this.props;
|
||||
const file = files[indexOfFile];
|
||||
const isLatestFile = indexOfFile === files.length - 1;
|
||||
|
||||
const fileName = file.name;
|
||||
const fileSize = file.size;
|
||||
const relativePath = file.webkitRelativePath
|
||||
? file.webkitRelativePath.slice(0, -file.name.length)
|
||||
: "";
|
||||
|
||||
let location;
|
||||
const requestsDataArray = [];
|
||||
const chunkSize = 1024 * 1023; //~0.999mb
|
||||
const chunks = Math.ceil(file.size / chunkSize, chunkSize);
|
||||
let chunk = 0;
|
||||
|
||||
api.files
|
||||
.startUploadSession(currentFolderId, fileName, fileSize, relativePath)
|
||||
.then((res) => {
|
||||
location = res.data.location;
|
||||
while (chunk < chunks) {
|
||||
const offset = chunk * chunkSize;
|
||||
//console.log("current chunk..", chunk);
|
||||
//console.log("file blob from offset...", offset);
|
||||
//console.log(file.slice(offset, offset + chunkSize));
|
||||
|
||||
const formData = new FormData();
|
||||
formData.append("file", file.slice(offset, offset + chunkSize));
|
||||
requestsDataArray.push(formData);
|
||||
chunk++;
|
||||
}
|
||||
})
|
||||
.then(() =>
|
||||
this.sendChunk(
|
||||
files,
|
||||
location,
|
||||
requestsDataArray,
|
||||
isLatestFile,
|
||||
indexOfFile
|
||||
)
|
||||
)
|
||||
.catch((err) => {
|
||||
this.props.setProgressVisible(false, 0);
|
||||
toastr.error(err);
|
||||
});
|
||||
};
|
||||
|
||||
onFileChange = (e) => {
|
||||
const { t, setProgressVisible, setProgressLabel } = this.props;
|
||||
const files = e.target.files;
|
||||
//console.log("files", files);
|
||||
const newFiles = [];
|
||||
if(files) {
|
||||
let total = 0;
|
||||
for (let item of files) {
|
||||
if (item.size !== 0) {
|
||||
newFiles.push(item);
|
||||
total += item.size;
|
||||
} else {
|
||||
toastr.error(t("ErrorUploadMessage"));
|
||||
}
|
||||
}
|
||||
|
||||
if (newFiles.length > 0) {
|
||||
this.setState({ files: newFiles, totalSize: total }, () => {
|
||||
setProgressVisible(true);
|
||||
setProgressLabel(
|
||||
this.props.t("UploadingLabel", {
|
||||
file: 0,
|
||||
totalFiles: newFiles.length,
|
||||
})
|
||||
);
|
||||
this.startSessionFunc(0);
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
onInputClick = e => {
|
||||
e.target.value = null;
|
||||
}
|
||||
onFileChange = (e) => this.props.startUpload(e.target.files);
|
||||
onInputClick = e => e.target.value = null;
|
||||
|
||||
shouldComponentUpdate(nextProps, nextState) {
|
||||
const { files, uploadedFiles, totalSize, percent } = this.state;
|
||||
if (nextProps.isCanCreate !== this.props.isCanCreate) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!utils.array.isArrayEqual(nextState.files, files)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (nextState.uploadedFiles !== uploadedFiles) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (nextState.totalSize !== totalSize) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (nextState.percent !== percent) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
return nextProps.isCanCreate !== this.props.isCanCreate;
|
||||
}
|
||||
|
||||
render() {
|
||||
@ -309,18 +112,14 @@ ArticleMainButtonContent.propTypes = {
|
||||
};
|
||||
|
||||
const mapStateToProps = (state) => {
|
||||
const { selectedFolder, filter, treeFolders } = state.files;
|
||||
const { settings, user } = state.auth;
|
||||
const { selectedFolder } = state.files;
|
||||
const { user } = state.auth;
|
||||
|
||||
return {
|
||||
settings,
|
||||
isCanCreate: isCanCreate(selectedFolder, user),
|
||||
currentFolderId: selectedFolder.id,
|
||||
filter,
|
||||
treeFolders,
|
||||
isCanCreate: isCanCreate(selectedFolder, user)
|
||||
};
|
||||
};
|
||||
|
||||
export default connect(mapStateToProps, { setAction, setTreeFolders })(
|
||||
export default connect(mapStateToProps, { setAction })(
|
||||
withRouter(ArticleMainButtonContent)
|
||||
);
|
||||
|
@ -5,7 +5,5 @@
|
||||
"NewPresentation": "New Presentation",
|
||||
"NewFolder": "New Folder",
|
||||
"UploadFiles": "Upload files",
|
||||
"UploadFolder": "Upload folder",
|
||||
"ErrorUploadMessage": "You cannot upload a folder or an empty file",
|
||||
"UploadingLabel": "Uploading files: {{file}} of {{totalFiles}}"
|
||||
"UploadFolder": "Upload folder"
|
||||
}
|
@ -5,7 +5,5 @@
|
||||
"NewPresentation": "Новая презентация",
|
||||
"NewFolder": "Новая папка",
|
||||
"UploadFiles": "Загрузить файлы",
|
||||
"UploadFolder": "Загрузить папку",
|
||||
"ErrorUploadMessage": "Нельзя загрузить папку или пустой файл",
|
||||
"UploadingLabel": "Загружено файлов: {{file}} из {{totalFiles}}"
|
||||
"UploadFolder": "Загрузить папку"
|
||||
}
|
@ -3,7 +3,7 @@ import { connect } from "react-redux";
|
||||
import PropTypes from "prop-types";
|
||||
import { withRouter } from "react-router";
|
||||
import { RequestLoader, Checkbox, toastr } from "asc-web-components";
|
||||
import { PageLayout, utils } from "asc-web-common";
|
||||
import { PageLayout, utils, api } from "asc-web-common";
|
||||
import { withTranslation, I18nextProvider } from 'react-i18next';
|
||||
import i18n from "./i18n";
|
||||
|
||||
@ -18,7 +18,9 @@ import {
|
||||
SectionFilterContent,
|
||||
SectionPagingContent
|
||||
} from "./Section";
|
||||
import { setSelected } from "../../../store/files/actions";
|
||||
import { setSelected, fetchFiles, setTreeFolders } from "../../../store/files/actions";
|
||||
import { loopTreeFolders } from "../../../store/files/selectors";
|
||||
import store from "../../../store/store";
|
||||
const { changeLanguage } = utils;
|
||||
|
||||
class PureHome extends React.Component {
|
||||
@ -36,7 +38,12 @@ class PureHome extends React.Component {
|
||||
progressBarLabel: "",
|
||||
overwriteSetting: false,
|
||||
uploadOriginalFormatSetting: false,
|
||||
hideWindowSetting: false
|
||||
hideWindowSetting: false,
|
||||
|
||||
files: [],
|
||||
uploadedFiles: 0,
|
||||
totalSize: 0,
|
||||
percent: 0,
|
||||
};
|
||||
}
|
||||
|
||||
@ -45,8 +52,11 @@ class PureHome extends React.Component {
|
||||
|
||||
const headerVisible = selection.length > 0;
|
||||
const headerIndeterminate =
|
||||
headerVisible && selection.length > 0 && selection.length < files.length + folders.length;
|
||||
const headerChecked = headerVisible && selection.length === files.length + folders.length;
|
||||
headerVisible &&
|
||||
selection.length > 0 &&
|
||||
selection.length < files.length + folders.length;
|
||||
const headerChecked =
|
||||
headerVisible && selection.length === files.length + folders.length;
|
||||
|
||||
/*console.log(`renderGroupButtonMenu()
|
||||
headerVisible=${headerVisible}
|
||||
@ -71,17 +81,277 @@ class PureHome extends React.Component {
|
||||
this.setState(newState);
|
||||
};
|
||||
|
||||
//TODO: Refactor this block
|
||||
|
||||
updateFiles = () => {
|
||||
const { filter, currentFolderId, treeFolders, setTreeFolders } = this.props;
|
||||
|
||||
this.onLoading(true);
|
||||
const newFilter = filter.clone();
|
||||
fetchFiles(currentFolderId, newFilter, store.dispatch, treeFolders)
|
||||
.then((data) => {
|
||||
const path = data.selectedFolder.pathParts;
|
||||
const newTreeFolders = treeFolders;
|
||||
const folders = data.selectedFolder.folders;
|
||||
const foldersCount = data.selectedFolder.foldersCount;
|
||||
loopTreeFolders(path, newTreeFolders, folders, foldersCount);
|
||||
setTreeFolders(newTreeFolders);
|
||||
})
|
||||
.catch((err) => toastr.error(err))
|
||||
.finally(() => {
|
||||
this.onLoading(false);
|
||||
});
|
||||
};
|
||||
|
||||
sendChunk = (files, location, requestsDataArray, isLatestFile, indexOfFile) => {
|
||||
const sendRequestFunc = (index) => {
|
||||
let newState = {};
|
||||
api.files
|
||||
.uploadFile(location, requestsDataArray[index])
|
||||
.then((res) => {
|
||||
let newPercent = this.state.percent;
|
||||
const percent = (newPercent +=
|
||||
(files[indexOfFile].size / this.state.totalSize) * 100);
|
||||
if (res.data.data && res.data.data.uploaded) {
|
||||
files[indexOfFile].uploaded = true;
|
||||
newState = { files, percent };
|
||||
}
|
||||
if (index + 1 !== requestsDataArray.length) {
|
||||
sendRequestFunc(index + 1);
|
||||
} else if (isLatestFile) {
|
||||
this.updateFiles();
|
||||
newState = Object.assign({}, newState, {
|
||||
uploadedFiles: this.state.uploadedFiles + 1,
|
||||
});
|
||||
return;
|
||||
} else {
|
||||
newState = Object.assign({}, newState, {
|
||||
uploadedFiles: this.state.uploadedFiles + 1,
|
||||
});
|
||||
this.startSessionFunc(indexOfFile + 1);
|
||||
}
|
||||
})
|
||||
.catch((err) => toastr.error(err))
|
||||
.finally(() => {
|
||||
if (
|
||||
newState.hasOwnProperty("files") ||
|
||||
newState.hasOwnProperty("percent") ||
|
||||
newState.hasOwnProperty("uploadedFiles")
|
||||
) {
|
||||
let progressVisible = true;
|
||||
let uploadedFiles = newState.uploadedFiles;
|
||||
let percent = newState.percent;
|
||||
if (newState.uploadedFiles === files.length) {
|
||||
percent = 100;
|
||||
newState.percent = 0;
|
||||
newState.uploadedFiles = 0;
|
||||
progressVisible = false;
|
||||
}
|
||||
newState.progressBarValue = percent;
|
||||
newState.progressBarLabel = this.props.t("UploadingLabel", {
|
||||
file: uploadedFiles,
|
||||
totalFiles: files.length,
|
||||
});
|
||||
|
||||
this.setState(newState, () => {
|
||||
if (!progressVisible) {
|
||||
this.setProgressVisible(false);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
sendRequestFunc(0);
|
||||
};
|
||||
|
||||
//TODO: Refactor this block
|
||||
|
||||
startSessionFunc = (indexOfFile) => {
|
||||
const { files } = this.state;
|
||||
const { currentFolderId } = this.props;
|
||||
const file = files[indexOfFile];
|
||||
const isLatestFile = indexOfFile === files.length - 1;
|
||||
|
||||
const fileName = file.name;
|
||||
const fileSize = file.size;
|
||||
const relativePath = file.relativePath
|
||||
? file.relativePath.slice(1, -file.name.length)
|
||||
: file.webkitRelativePath
|
||||
? file.webkitRelativePath.slice(0, -file.name.length)
|
||||
: "";
|
||||
|
||||
let location;
|
||||
const requestsDataArray = [];
|
||||
const chunkSize = 1024 * 1023; //~0.999mb
|
||||
const chunks = Math.ceil(file.size / chunkSize, chunkSize);
|
||||
let chunk = 0;
|
||||
|
||||
api.files
|
||||
.startUploadSession(currentFolderId, fileName, fileSize, relativePath)
|
||||
.then((res) => {
|
||||
location = res.data.location;
|
||||
while (chunk < chunks) {
|
||||
const offset = chunk * chunkSize;
|
||||
//console.log("current chunk..", chunk);
|
||||
//console.log("file blob from offset...", offset);
|
||||
//console.log(file.slice(offset, offset + chunkSize));
|
||||
|
||||
const formData = new FormData();
|
||||
formData.append("file", file.slice(offset, offset + chunkSize));
|
||||
requestsDataArray.push(formData);
|
||||
chunk++;
|
||||
}
|
||||
})
|
||||
.then(
|
||||
() => this.sendChunk(files, location, requestsDataArray, isLatestFile, indexOfFile)
|
||||
)
|
||||
.catch((err) => {
|
||||
this.setProgressVisible(false, 0);
|
||||
toastr.error(err);
|
||||
});
|
||||
};
|
||||
|
||||
onDrop = (e) => {
|
||||
// ev.currentTarget.style.background = "lightyellow";
|
||||
const items = e.dataTransfer.items;
|
||||
let files = [];
|
||||
|
||||
const inSeries = (queue, callback) => {
|
||||
let i = 0;
|
||||
let length = queue.length;
|
||||
|
||||
if (!queue || !queue.length) {
|
||||
callback();
|
||||
}
|
||||
|
||||
const callNext = (i) => {
|
||||
if (typeof queue[i] === "function") {
|
||||
queue[i](() => i+1 < length ? callNext(i+1) : callback());
|
||||
}
|
||||
};
|
||||
callNext(i);
|
||||
};
|
||||
|
||||
const readDirEntry = (dirEntry, callback) => {
|
||||
let entries = [];
|
||||
const dirReader = dirEntry.createReader();
|
||||
|
||||
// keep quering recursively till no more entries
|
||||
const getEntries = (func) => {
|
||||
dirReader.readEntries((moreEntries) => {
|
||||
if (moreEntries.length) {
|
||||
entries = [...entries, ...moreEntries];
|
||||
getEntries(func);
|
||||
} else {
|
||||
func();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
getEntries(() => readEntries(entries, callback));
|
||||
};
|
||||
|
||||
const readEntry = (entry, callback) => {
|
||||
if (entry.isFile) {
|
||||
entry.file(file => {
|
||||
addFile(file, entry.fullPath);
|
||||
callback();
|
||||
});
|
||||
} else if (entry.isDirectory) {
|
||||
readDirEntry(entry, callback);
|
||||
}
|
||||
};
|
||||
|
||||
const readEntries = (entries, callback) => {
|
||||
const queue = [];
|
||||
loop(entries, (entry) => {
|
||||
queue.push((func) => readEntry(entry, func));
|
||||
});
|
||||
inSeries(queue, () => callback());
|
||||
};
|
||||
|
||||
const addFile = (file, relativePath) => {
|
||||
file.relativePath = relativePath || "";
|
||||
files.push(file);
|
||||
};
|
||||
|
||||
const loop = (items, callback) => {
|
||||
let length;
|
||||
|
||||
if (items) {
|
||||
length = items.length;
|
||||
// Loop array items
|
||||
for (let i = 0; i < length; i++) {
|
||||
callback(items[i], i);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const readItems = (items, func) => {
|
||||
const entries = [];
|
||||
loop(items, (item) => {
|
||||
const entry = item.webkitGetAsEntry();
|
||||
if (entry) {
|
||||
if (entry.isFile) {
|
||||
addFile(item.getAsFile(), entry.fullPath);
|
||||
} else {
|
||||
entries.push(entry);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (entries.length) {
|
||||
readEntries(entries, func);
|
||||
} else {
|
||||
func();
|
||||
}
|
||||
};
|
||||
|
||||
this.setState({ isLoading: true }, () =>
|
||||
readItems(items, () => this.startUpload(files))
|
||||
);
|
||||
};
|
||||
|
||||
startUpload = files => {
|
||||
const newFiles = [];
|
||||
let total = 0;
|
||||
|
||||
for (let item of files) {
|
||||
if (item.size !== 0) {
|
||||
newFiles.push(item);
|
||||
total += item.size;
|
||||
} else {
|
||||
toastr.error(this.props.t("ErrorUploadMessage"));
|
||||
}
|
||||
}
|
||||
this.startUploadFiles(newFiles, total);
|
||||
}
|
||||
|
||||
startUploadFiles = (files, totalSize) => {
|
||||
if (files.length > 0) {
|
||||
const progressBarLabel = this.props.t("UploadingLabel", {
|
||||
file: 0,
|
||||
totalFiles: files.length,
|
||||
});
|
||||
this.setState({ files, totalSize, progressBarLabel, showProgressBar: true, isLoading: true },
|
||||
() => { this.startSessionFunc(0); });
|
||||
} else if(this.state.isLoading) {
|
||||
this.setState({ isLoading: false });
|
||||
}
|
||||
};
|
||||
|
||||
componentDidUpdate(prevProps) {
|
||||
if (this.props.selection !== prevProps.selection) {
|
||||
this.renderGroupButtonMenu();
|
||||
}
|
||||
}
|
||||
|
||||
onSectionHeaderContentCheck = checked => {
|
||||
onSectionHeaderContentCheck = (checked) => {
|
||||
this.props.setSelected(checked ? "all" : "none");
|
||||
};
|
||||
|
||||
onSectionHeaderContentSelect = selected => {
|
||||
onSectionHeaderContentSelect = (selected) => {
|
||||
this.props.setSelected(selected);
|
||||
};
|
||||
|
||||
@ -96,32 +366,43 @@ class PureHome extends React.Component {
|
||||
}
|
||||
};
|
||||
|
||||
onLoading = status => {
|
||||
onLoading = (status) => {
|
||||
this.setState({ isLoading: status });
|
||||
};
|
||||
|
||||
setProgressVisible = (visible, timeout) => {
|
||||
const newTimeout = timeout ? timeout : 10000;
|
||||
if(visible) {this.setState({ showProgressBar: visible })}
|
||||
else { setTimeout(() => this.setState({ showProgressBar: visible, progressBarValue: 0 }), newTimeout)};
|
||||
if (visible) {
|
||||
this.setState({ showProgressBar: visible });
|
||||
} else {
|
||||
setTimeout(
|
||||
() => this.setState({ showProgressBar: visible, progressBarValue: 0 }),
|
||||
newTimeout
|
||||
);
|
||||
}
|
||||
};
|
||||
setProgressValue = value => this.setState({ progressBarValue: value });
|
||||
setProgressLabel = label => this.setState({ progressBarLabel: label });
|
||||
setProgressValue = (value) => this.setState({ progressBarValue: value });
|
||||
setProgressLabel = (label) => this.setState({ progressBarLabel: label });
|
||||
|
||||
onChangeOverwrite = () => this.setState({overwriteSetting: !this.state.overwriteSetting})
|
||||
onChangeOriginalFormat = () => this.setState({uploadOriginalFormatSetting: !this.state.uploadOriginalFormatSetting})
|
||||
onChangeWindowVisible = () => this.setState({hideWindowSetting: !this.state.hideWindowSetting})
|
||||
onChangeOverwrite = () =>
|
||||
this.setState({ overwriteSetting: !this.state.overwriteSetting });
|
||||
onChangeOriginalFormat = () =>
|
||||
this.setState({
|
||||
uploadOriginalFormatSetting: !this.state.uploadOriginalFormatSetting,
|
||||
});
|
||||
onChangeWindowVisible = () =>
|
||||
this.setState({ hideWindowSetting: !this.state.hideWindowSetting });
|
||||
|
||||
startFilesOperations = progressBarLabel => {
|
||||
this.setState({ isLoading: true, progressBarLabel, showProgressBar: true })
|
||||
}
|
||||
startFilesOperations = (progressBarLabel) => {
|
||||
this.setState({ isLoading: true, progressBarLabel, showProgressBar: true });
|
||||
};
|
||||
|
||||
finishFilesOperations = err => {
|
||||
finishFilesOperations = (err) => {
|
||||
const timeout = err ? 0 : null;
|
||||
err && toastr.error(err);
|
||||
this.onLoading(false);
|
||||
this.setProgressVisible(false, timeout);
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const {
|
||||
@ -135,7 +416,7 @@ class PureHome extends React.Component {
|
||||
progressBarLabel,
|
||||
overwriteSetting,
|
||||
uploadOriginalFormatSetting,
|
||||
hideWindowSetting
|
||||
hideWindowSetting,
|
||||
} = this.state;
|
||||
const { t } = this.props;
|
||||
|
||||
@ -164,15 +445,17 @@ class PureHome extends React.Component {
|
||||
<RequestLoader
|
||||
visible={isLoading}
|
||||
zIndex={256}
|
||||
loaderSize='16px'
|
||||
loaderSize="16px"
|
||||
loaderColor={"#999"}
|
||||
label={`${t('LoadingProcessing')} ${t('LoadingDescription')}`}
|
||||
fontSize='12px'
|
||||
label={`${t("LoadingProcessing")} ${t("LoadingDescription")}`}
|
||||
fontSize="12px"
|
||||
fontColor={"#999"}
|
||||
/>
|
||||
<PageLayout
|
||||
withBodyScroll
|
||||
withBodyAutoFocus
|
||||
uploadFiles
|
||||
onDrop={this.onDrop}
|
||||
showProgressBar={showProgressBar}
|
||||
progressBarValue={progressBarValue}
|
||||
progressBarDropDownContent={progressBarContent}
|
||||
@ -181,11 +464,18 @@ class PureHome extends React.Component {
|
||||
articleMainButtonContent={
|
||||
<ArticleMainButtonContent
|
||||
onLoading={this.onLoading}
|
||||
startUpload={this.startUpload}
|
||||
setProgressVisible={this.setProgressVisible}
|
||||
setProgressValue={this.setProgressValue}
|
||||
setProgressLabel={this.setProgressLabel}
|
||||
/>}
|
||||
articleBodyContent={<ArticleBodyContent onLoading={this.onLoading} isLoading={isLoading} />}
|
||||
/>
|
||||
}
|
||||
articleBodyContent={
|
||||
<ArticleBodyContent
|
||||
onLoading={this.onLoading}
|
||||
isLoading={isLoading}
|
||||
/>
|
||||
}
|
||||
sectionHeaderContent={
|
||||
<SectionHeaderContent
|
||||
isHeaderVisible={isHeaderVisible}
|
||||
@ -201,7 +491,9 @@ class PureHome extends React.Component {
|
||||
finishFilesOperations={this.finishFilesOperations}
|
||||
/>
|
||||
}
|
||||
sectionFilterContent={<SectionFilterContent onLoading={this.onLoading} />}
|
||||
sectionFilterContent={
|
||||
<SectionFilterContent onLoading={this.onLoading} />
|
||||
}
|
||||
sectionBodyContent={
|
||||
<SectionBodyContent
|
||||
selected={selected}
|
||||
@ -241,11 +533,15 @@ function mapStateToProps(state) {
|
||||
folders: state.files.folders,
|
||||
selection: state.files.selection,
|
||||
selected: state.files.selected,
|
||||
isLoaded: state.auth.isLoaded
|
||||
isLoaded: state.auth.isLoaded,
|
||||
|
||||
currentFolderId: state.files.selectedFolder.id,
|
||||
filter: state.files.filter,
|
||||
treeFolders: state.files.treeFolders
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
{ setSelected }
|
||||
{ setSelected, setTreeFolders }
|
||||
)(withRouter(Home));
|
||||
|
@ -74,5 +74,7 @@
|
||||
"HideWindowSetting": "Show this window minimized",
|
||||
"ArchivingData": "Archiving data",
|
||||
"DeleteOperation": "Deleting",
|
||||
"Restore": "Restore"
|
||||
"Restore": "Restore",
|
||||
"ErrorUploadMessage": "You cannot upload a folder or an empty file",
|
||||
"UploadingLabel": "Uploading files: {{file}} of {{totalFiles}}"
|
||||
}
|
@ -74,5 +74,7 @@
|
||||
"HideWindowSetting": "Показывать это окно минимизированным",
|
||||
"ArchivingData": "Архивирование данных",
|
||||
"DeleteOperation": "Удаление",
|
||||
"Restore": "Восстановить"
|
||||
"Restore": "Восстановить",
|
||||
"ErrorUploadMessage": "Нельзя загрузить папку или пустой файл",
|
||||
"UploadingLabel": "Загружено файлов: {{file}} из {{totalFiles}}"
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "asc-web-common",
|
||||
"version": "1.0.149",
|
||||
"version": "1.0.150",
|
||||
"description": "Ascensio System SIA common components and solutions library",
|
||||
"license": "AGPL-3.0",
|
||||
"files": [
|
||||
|
93
web/ASC.Web.Common/src/components/DragAndDrop/DragAndDrop.js
Normal file
93
web/ASC.Web.Common/src/components/DragAndDrop/DragAndDrop.js
Normal file
@ -0,0 +1,93 @@
|
||||
import React, { Component } from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import StyledDragAndDrop from "./StyledDragAndDrop";
|
||||
|
||||
class DragAndDrop extends Component {
|
||||
state = { drag: false };
|
||||
|
||||
dropRef = React.createRef();
|
||||
dragCounter = 0;
|
||||
|
||||
onDragOver = (e) => {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
//this.dragCounter++;
|
||||
this.props.onDragOver && this.props.onDragOver(e);
|
||||
};
|
||||
|
||||
onDragEnter = (e) => {
|
||||
this.dragCounter++;
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
|
||||
this.props.onDragEnter && this.props.onDragEnter(e);
|
||||
this.setState({ drag: true });
|
||||
};
|
||||
|
||||
onDragLeave = (e) => {
|
||||
this.dragCounter--;
|
||||
|
||||
if(this.dragCounter === 0) {
|
||||
this.setState({ drag: false });
|
||||
}
|
||||
this.props.onDragLeave && this.props.onDragLeave(e);
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
};
|
||||
|
||||
onDrop = (e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
this.setState({ drag: false });
|
||||
if (e.dataTransfer.files && e.dataTransfer.files.length > 0) {
|
||||
this.props.onDrop && this.props.onDrop(e);
|
||||
e.dataTransfer.clearData();
|
||||
this.dragCounter = 0;
|
||||
}
|
||||
};
|
||||
|
||||
onDragStart = (e) => {
|
||||
this.props.onDragStart && this.props.onDragStart(e);
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
let div = this.dropRef.current;
|
||||
div.addEventListener("dragstart", this.onDragStart, false);
|
||||
div.addEventListener("dragenter", this.onDragEnter);
|
||||
div.addEventListener("dragleave", this.onDragLeave);
|
||||
div.addEventListener("dragover", this.onDragOver);
|
||||
div.addEventListener("drop", this.onDrop);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
let div = this.dropRef.current;
|
||||
div.removeEventListener("dragstart", this.onDragStart, false);
|
||||
div.removeEventListener("dragenter", this.onDragEnter);
|
||||
div.removeEventListener("dragleave", this.onDragLeave);
|
||||
div.removeEventListener("dragover", this.onDragOver);
|
||||
div.removeEventListener("drop", this.onDrop);
|
||||
}
|
||||
|
||||
render() {
|
||||
//console.log("DND render");
|
||||
const { children, draggable, ...rest } = this.props;
|
||||
return (
|
||||
<StyledDragAndDrop className="drag-and-drop" drag={this.state.drag} draggable={draggable} {...rest} ref={this.dropRef}>
|
||||
{children}
|
||||
</StyledDragAndDrop>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
DragAndDrop.propTypes = {
|
||||
children: PropTypes.any,
|
||||
draggable: PropTypes.bool,
|
||||
onDragStart: PropTypes.func,
|
||||
onDragEnter: PropTypes.func,
|
||||
onDragLeave: PropTypes.func,
|
||||
onDragOver: PropTypes.func,
|
||||
onDrop: PropTypes.func
|
||||
}
|
||||
|
||||
export default DragAndDrop;
|
@ -0,0 +1,43 @@
|
||||
import React from "react";
|
||||
import { storiesOf } from "@storybook/react";
|
||||
import withReadme from "storybook-readme/with-readme";
|
||||
import Readme from "./README.md";
|
||||
import DragAndDrop from "./";
|
||||
import { Text } from "asc-web-components";
|
||||
|
||||
storiesOf("Components| DragAndDrop", module)
|
||||
.addDecorator(withReadme(Readme))
|
||||
.add("base", () => {
|
||||
|
||||
const traverseFileTree = (item, path) => {
|
||||
if (item.isFile) {
|
||||
item.file(file => console.log("File:", path + file.name));
|
||||
} else if (item.isDirectory) {
|
||||
const dirReader = item.createReader();
|
||||
dirReader.readEntries(entries => {
|
||||
for (let i = 0; i < entries.length; i++) {
|
||||
traverseFileTree(entries[i], path + item.name + "/");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const onDrop = event => {
|
||||
console.log("onDrop", event);
|
||||
const items = event.dataTransfer.items;
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
const item = items[i].webkitGetAsEntry();
|
||||
if (item) {
|
||||
traverseFileTree(item, "");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const dropDownStyles = { margin: 16, width: 200, height: 200, border: "5px solid #999" };
|
||||
const textStyles = {textAlign: "center", lineHeight: "9.5em"};
|
||||
|
||||
return(
|
||||
<DragAndDrop onDrop={onDrop} style={dropDownStyles}>
|
||||
<Text style={textStyles} color="#999" fontSize="20px">Drop items here</Text>
|
||||
</DragAndDrop>
|
||||
)});
|
28
web/ASC.Web.Common/src/components/DragAndDrop/README.md
Normal file
28
web/ASC.Web.Common/src/components/DragAndDrop/README.md
Normal file
@ -0,0 +1,28 @@
|
||||
# DragAndDrop
|
||||
|
||||
Drag And Drop component can be used as Dropzone
|
||||
|
||||
### Usage
|
||||
|
||||
```js
|
||||
import { DragAndDrop } from "asc-web-common";
|
||||
```
|
||||
|
||||
```jsx
|
||||
<DragAndDrop onDrop={onDrop} style={width: 200, height: 200, border: "5px solid #999"}>
|
||||
<Text style={textStyles} color="#999" fontSize="20px">
|
||||
Drop items here
|
||||
</Text>
|
||||
</DragAndDrop>
|
||||
```
|
||||
|
||||
### Properties
|
||||
|
||||
| Props | Type | Required | Values | Default | Description |
|
||||
| ------------- | :--------: | :------: | :----: | :-----: | ------------------------------------------------------------- |
|
||||
| `draggable` | `bool` | | | `false` | Sets the value of the draggable attribute to true |
|
||||
| `onDragStart` | `function` | | | | Occurs when the user starts to drag an element |
|
||||
| `onDragEnter` | `function` | | | | Occurs when the dragged element enters the drop target |
|
||||
| `onDragLeave` | `function` | | | | Occurs when the dragged element leaves the drop target |
|
||||
| `onDragOver` | `function` | | | | Occurs when the dragged element is over the drop target |
|
||||
| `onDrop` | `function` | | | | Occurs when the dragged element is dropped on the drop target |
|
@ -0,0 +1,7 @@
|
||||
import styled from "styled-components";
|
||||
|
||||
const StyledDragAndDrop = styled.div`
|
||||
border: ${props => props.drag && "1px dashed #bbb"};
|
||||
`;
|
||||
|
||||
export default StyledDragAndDrop;
|
1
web/ASC.Web.Common/src/components/DragAndDrop/index.js
Normal file
1
web/ASC.Web.Common/src/components/DragAndDrop/index.js
Normal file
@ -0,0 +1 @@
|
||||
export default from "./DragAndDrop";
|
@ -148,7 +148,7 @@ class PageLayoutComponent extends React.PureComponent {
|
||||
};
|
||||
|
||||
render() {
|
||||
const { showProgressBar, progressBarValue, progressBarDropDownContent, withBodyScroll, withBodyAutoFocus, progressBarLabel } = this.props;
|
||||
const { showProgressBar, progressBarValue, progressBarDropDownContent, withBodyScroll, withBodyAutoFocus, progressBarLabel, onDrop, uploadFiles } = this.props;
|
||||
return (
|
||||
<>
|
||||
{this.state.isBackdropAvailable && (
|
||||
@ -197,7 +197,9 @@ class PageLayoutComponent extends React.PureComponent {
|
||||
)}
|
||||
{this.state.isSectionBodyAvailable && (
|
||||
<>
|
||||
<SectionBody
|
||||
<SectionBody
|
||||
onDrop={onDrop}
|
||||
uploadFiles={uploadFiles}
|
||||
withScroll={withBodyScroll}
|
||||
autoFocus={withBodyAutoFocus}
|
||||
pinned={this.state.isArticlePinned}
|
||||
@ -282,7 +284,9 @@ PageLayoutComponent.propTypes = {
|
||||
showProgressBar: PropTypes.bool,
|
||||
progressBarValue: PropTypes.number,
|
||||
progressBarDropDownContent: PropTypes.any,
|
||||
progressBarLabel: PropTypes.string
|
||||
progressBarLabel: PropTypes.string,
|
||||
onDrop: PropTypes.func,
|
||||
uploadFiles: PropTypes.bool
|
||||
};
|
||||
|
||||
PageLayoutComponent.defaultProps = {
|
||||
|
@ -1,19 +1,15 @@
|
||||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import styled from "styled-components";
|
||||
import styled, { css } from "styled-components";
|
||||
import { utils, Scrollbar } from "asc-web-components";
|
||||
import DragAndDrop from "../../DragAndDrop";
|
||||
const { tablet } = utils.device;
|
||||
|
||||
const StyledSectionBody = styled.div`
|
||||
${props => props.displayBorder && `outline: 1px dotted;`}
|
||||
const commonStyles = css`
|
||||
flex-grow: 1;
|
||||
height: 100%;
|
||||
|
||||
${props => props.withScroll && `
|
||||
margin-left: -24px;
|
||||
`}
|
||||
|
||||
.sectionWrapper {
|
||||
.section-wrapper-content {
|
||||
flex: 1 0 auto;
|
||||
padding: 16px 8px 16px 24px;
|
||||
outline: none;
|
||||
@ -21,13 +17,35 @@ const StyledSectionBody = styled.div`
|
||||
@media ${tablet} {
|
||||
padding: 16px 0 16px 24px;
|
||||
}
|
||||
|
||||
.section-wrapper {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 100%;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledSectionWrapper = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 100%;
|
||||
const StyledSectionBody = styled.div`
|
||||
${props => props.displayBorder && `outline: 1px dotted;`}
|
||||
${commonStyles}
|
||||
|
||||
${props => props.withScroll && `
|
||||
margin-left: -24px;
|
||||
`}
|
||||
`;
|
||||
|
||||
const StyledDropZoneBody = styled(DragAndDrop)`
|
||||
${props => props.displayBorder && `outline: 1px dotted;`}
|
||||
${commonStyles}
|
||||
|
||||
.drag-and-drop {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
${props => props.withScroll && `
|
||||
margin-left: -24px;
|
||||
`}
|
||||
`;
|
||||
|
||||
const StyledSpacer = styled.div`
|
||||
@ -54,31 +72,42 @@ class SectionBody extends React.Component {
|
||||
|
||||
render() {
|
||||
//console.log("PageLayout SectionBody render");
|
||||
const { children, withScroll, autoFocus, pinned } = this.props;
|
||||
const { children, withScroll, autoFocus, pinned, onDrop, uploadFiles } = this.props;
|
||||
|
||||
const focusProps = autoFocus ? {
|
||||
ref: this.focusRef,
|
||||
tabIndex: 1
|
||||
} : {};
|
||||
|
||||
return (
|
||||
<StyledSectionBody withScroll={withScroll}>
|
||||
{withScroll ? (
|
||||
const renderBody = () => {
|
||||
return(
|
||||
withScroll ? (
|
||||
<Scrollbar stype="mediumBlack">
|
||||
<StyledSectionWrapper>
|
||||
<div className="sectionWrapper" {...focusProps}>
|
||||
<div className="section-wrapper">
|
||||
<div className="section-wrapper-content" {...focusProps}>
|
||||
{children}
|
||||
<StyledSpacer pinned={pinned}/>
|
||||
<StyledSpacer pinned={pinned} />
|
||||
</div>
|
||||
</StyledSectionWrapper>
|
||||
</div>
|
||||
</Scrollbar>
|
||||
) : (
|
||||
<StyledSectionWrapper>
|
||||
<div className="section-wrapper">
|
||||
{children}
|
||||
<StyledSpacer pinned={pinned}/>
|
||||
</StyledSectionWrapper>
|
||||
)}
|
||||
</StyledSectionBody>
|
||||
<StyledSpacer pinned={pinned} />
|
||||
</div>
|
||||
))
|
||||
};
|
||||
|
||||
return (
|
||||
uploadFiles ? (
|
||||
<StyledDropZoneBody onDrop={onDrop} withScroll={withScroll}>
|
||||
{renderBody()}
|
||||
</StyledDropZoneBody>
|
||||
) : (
|
||||
<StyledSectionBody>
|
||||
{renderBody()}
|
||||
</StyledSectionBody>
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -89,6 +118,8 @@ SectionBody.propTypes = {
|
||||
withScroll: PropTypes.bool,
|
||||
autoFocus: PropTypes.bool,
|
||||
pinned: PropTypes.bool,
|
||||
onDrop: PropTypes.func,
|
||||
uploadFiles: PropTypes.bool,
|
||||
children: PropTypes.oneOfType([
|
||||
PropTypes.arrayOf(PropTypes.node),
|
||||
PropTypes.node,
|
||||
@ -99,7 +130,8 @@ SectionBody.propTypes = {
|
||||
SectionBody.defaultProps = {
|
||||
withScroll: true,
|
||||
autoFocus: false,
|
||||
pinned: false
|
||||
pinned: false,
|
||||
uploadFiles: false
|
||||
};
|
||||
|
||||
export default SectionBody;
|
||||
|
@ -13,4 +13,5 @@ export { default as ProfileMenu } from './ProfileMenu';
|
||||
export { default as ErrorContainer } from './ErrorContainer';
|
||||
export { default as ErrorBoundary } from './ErrorBoundary';
|
||||
export { default as FilterInput } from './FilterInput';
|
||||
export { default as MediaViewer } from './MediaViewer';
|
||||
export { default as MediaViewer } from './MediaViewer';
|
||||
export { default as DragAndDrop } from './DragAndDrop';
|
Loading…
Reference in New Issue
Block a user