Merge branch 'feature/files' of https://github.com/ONLYOFFICE/AppServer into feature/files

This commit is contained in:
NikolayRechkin 2020-05-29 09:51:12 +03:00
commit 346be5b443
15 changed files with 584 additions and 280 deletions

View File

@ -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)
);

View File

@ -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"
}

View File

@ -5,7 +5,5 @@
"NewPresentation": "Новая презентация",
"NewFolder": "Новая папка",
"UploadFiles": "Загрузить файлы",
"UploadFolder": "Загрузить папку",
"ErrorUploadMessage": "Нельзя загрузить папку или пустой файл",
"UploadingLabel": "Загружено файлов: {{file}} из {{totalFiles}}"
"UploadFolder": "Загрузить папку"
}

View File

@ -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)};
};
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})
startFilesOperations = progressBarLabel => {
this.setState({ isLoading: true, progressBarLabel, showProgressBar: true })
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 });
finishFilesOperations = err => {
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 });
};
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));

View File

@ -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}}"
}

View File

@ -74,5 +74,7 @@
"HideWindowSetting": "Показывать это окно минимизированным",
"ArchivingData": "Архивирование данных",
"DeleteOperation": "Удаление",
"Restore": "Восстановить"
"Restore": "Восстановить",
"ErrorUploadMessage": "Нельзя загрузить папку или пустой файл",
"UploadingLabel": "Загружено файлов: {{file}} из {{totalFiles}}"
}

View File

@ -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": [

View 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;

View File

@ -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>
)});

View 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 |

View File

@ -0,0 +1,7 @@
import styled from "styled-components";
const StyledDragAndDrop = styled.div`
border: ${props => props.drag && "1px dashed #bbb"};
`;
export default StyledDragAndDrop;

View File

@ -0,0 +1 @@
export default from "./DragAndDrop";

View File

@ -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 && (
@ -198,6 +198,8 @@ class PageLayoutComponent extends React.PureComponent {
{this.state.isSectionBodyAvailable && (
<>
<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 = {

View File

@ -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;
}
}
`;
const StyledSectionWrapper = styled.div`
.section-wrapper {
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>
</div>
</StyledSectionWrapper>
</Scrollbar>
) : (
<StyledSectionWrapper>
<div className="section-wrapper">
{children}
<StyledSpacer pinned={pinned}/>
</StyledSectionWrapper>
)}
<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;

View File

@ -14,3 +14,4 @@ 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 DragAndDrop } from './DragAndDrop';