├── public ├── favicon.ico ├── manifest.json └── index.html ├── src ├── Components │ ├── FileList │ │ ├── FileListSublist │ │ │ ├── FileListSublist.css │ │ │ └── FileListSublist.jsx │ │ ├── FileListEmptyMessage.css │ │ ├── FileList.css │ │ ├── FileListEmptyMessage.jsx │ │ └── FileList.jsx │ ├── Breadcrumb │ │ ├── BreadcrumbText.css │ │ ├── Breadcrumb.css │ │ ├── Breadcrumb.jsx │ │ └── BreadcrumbText.jsx │ ├── File │ │ ├── File.css │ │ ├── FileSublist │ │ │ └── FileSublist.jsx │ │ └── File.jsx │ ├── ContextMenu │ │ ├── ContextMenu.css │ │ ├── ContextMenuActions │ │ │ ├── RemoveAction.jsx │ │ │ ├── RenameAction.jsx │ │ │ ├── DownloadAction.jsx │ │ │ ├── EditAction.jsx │ │ │ ├── CopyAction.jsx │ │ │ ├── MoveAction.jsx │ │ │ ├── UploadFileAction.jsx │ │ │ ├── CreateFolderAction.jsx │ │ │ └── OpenAction.jsx │ │ └── ContextMenu.jsx │ ├── Loader │ │ └── Loader.jsx │ ├── Dialogs │ │ ├── Dialogs.jsx │ │ ├── Content │ │ │ └── Content.jsx │ │ ├── CreateFolder │ │ │ └── CreateFolder.jsx │ │ ├── Rename │ │ │ └── Rename.jsx │ │ ├── UploadFile │ │ │ └── UploadFile.jsx │ │ ├── Edit │ │ │ └── Edit.jsx │ │ ├── Copy │ │ │ └── Copy.jsx │ │ └── Move │ │ │ └── Move.jsx │ ├── FileUploader │ │ ├── UploadFileList.jsx │ │ └── FileUploader.jsx │ ├── Navbar │ │ ├── ThreeDotsMenu.jsx │ │ └── Navbar.jsx │ └── Notification │ │ ├── DynamicSnackbar.jsx │ │ └── NotificationBar.jsx ├── App.test.js ├── index.js ├── index.css ├── config.js ├── App.js ├── Api │ ├── Api.js │ └── ApiHandler.js ├── serviceWorker.js ├── Reducers │ └── MainReducer.js └── Actions │ └── Actions.js ├── .gitignore ├── README.md └── package.json /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joni2back/react-filemanager/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /src/Components/FileList/FileListSublist/FileListSublist.css: -------------------------------------------------------------------------------- 1 | .FileListSublist { 2 | overflow: auto; 3 | max-height: 20em; 4 | } -------------------------------------------------------------------------------- /src/Components/Breadcrumb/BreadcrumbText.css: -------------------------------------------------------------------------------- 1 | .BreadcrumbText { 2 | } 3 | 4 | .BreadcrumbText span { 5 | cursor: pointer; 6 | text-overflow: ellipsis; 7 | } 8 | 9 | .BreadcrumbText span:hover { 10 | color: #efefef; 11 | } -------------------------------------------------------------------------------- /src/Components/FileList/FileListEmptyMessage.css: -------------------------------------------------------------------------------- 1 | .FileListEmptyMessage { 2 | margin: 5px 10px; 3 | padding: 20px; 4 | display: block; 5 | border-radius: 5px; 6 | background: #efefef; 7 | color: #333; 8 | font-size: 15px; 9 | } -------------------------------------------------------------------------------- /src/App.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import App from './App'; 4 | 5 | it('renders without crashing', () => { 6 | const div = document.createElement('div'); 7 | ReactDOM.render(, div); 8 | ReactDOM.unmountComponentAtNode(div); 9 | }); 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # misc 12 | .DS_Store 13 | .env.local 14 | .env.development.local 15 | .env.test.local 16 | .env.production.local 17 | 18 | npm-debug.log* 19 | yarn-debug.log* 20 | yarn-error.log* 21 | -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React Filemanager", 3 | "name": "React Filemanager", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | } 10 | ], 11 | "start_url": ".", 12 | "display": "standalone", 13 | "theme_color": "#fff", 14 | "background_color": "#2196f3" 15 | } 16 | -------------------------------------------------------------------------------- /src/Components/Breadcrumb/Breadcrumb.css: -------------------------------------------------------------------------------- 1 | .Breadcrumb { 2 | background: gray; 3 | font-size: 13px; 4 | color: #fff; 5 | padding: 10px 25px; 6 | } 7 | 8 | .Breadcrumb > span { 9 | font-weight: bold; 10 | min-width: 20px; 11 | display: inline-block; 12 | text-align: center; 13 | cursor: pointer; 14 | } 15 | 16 | .Breadcrumb > span:hover { 17 | color: #eee; 18 | } -------------------------------------------------------------------------------- /src/Components/File/File.css: -------------------------------------------------------------------------------- 1 | .File { 2 | cursor: pointer; 3 | float: left; 4 | display: block; 5 | width: 100%; 6 | user-select: none; 7 | } 8 | 9 | .File:hover { 10 | cursor: pointer; 11 | background: #fafafa; 12 | } 13 | 14 | .File[data-selected=true] { 15 | background-color: #e8f0fe; 16 | 17 | } 18 | 19 | .File[data-selected=true], 20 | .File[data-selected=true] > li > div > span { 21 | color: #1967d2; 22 | } -------------------------------------------------------------------------------- /src/Components/FileList/FileList.css: -------------------------------------------------------------------------------- 1 | .FileList { 2 | overflow: auto 3 | } 4 | 5 | .FileList .File .filename > span { 6 | text-overflow: ellipsis; 7 | overflow: hidden; 8 | white-space: nowrap; 9 | } 10 | 11 | @media (min-width : 600px) { 12 | .FileList .File { 13 | float: left; 14 | width: 33.3%; 15 | } 16 | } 17 | @media (min-width : 1024px) { 18 | .FileList .File { 19 | float: left; 20 | width: 25%; 21 | } 22 | } -------------------------------------------------------------------------------- /src/Components/ContextMenu/ContextMenu.css: -------------------------------------------------------------------------------- 1 | .XXXXXXXXXXXContextMenu { 2 | max-width: 200px; 3 | position: absolute; 4 | display: block; 5 | background: green; 6 | border: 1px solid black; 7 | padding: 10px; 8 | text-align: left; 9 | } 10 | 11 | .ContextMenu ul { 12 | margin: 0; 13 | padding: 0; 14 | list-style: none; 15 | } 16 | 17 | .ContextMenu ul li { 18 | padding: 10px 20px; 19 | border-bottom: 1px solid #000; 20 | } 21 | 22 | .ContextMenu ul li:last-child { 23 | border: none; 24 | } -------------------------------------------------------------------------------- /src/Components/Breadcrumb/Breadcrumb.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { connect } from 'react-redux'; 3 | import './Breadcrumb.css'; 4 | import BreadcrumbText from './BreadcrumbText.jsx'; 5 | 6 | class Breadcrumb extends Component { 7 | render() { 8 | return
9 | 10 |
11 | } 12 | } 13 | 14 | const mapDispatchToProps = (dispatch) => { 15 | return { 16 | }; 17 | }; 18 | 19 | const mapStateToProps = (state) => { 20 | return { 21 | }; 22 | }; 23 | export default connect(mapStateToProps, mapDispatchToProps)(Breadcrumb); 24 | -------------------------------------------------------------------------------- /src/Components/FileList/FileListEmptyMessage.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { connect } from 'react-redux'; 3 | import './FileListEmptyMessage.css'; 4 | 5 | class FileListEmptyMessage extends Component { 6 | render() { 7 | return ( 8 |
9 | No files in this folder 10 |
11 | ); 12 | } 13 | } 14 | 15 | const mapStateToProps = (state) => { 16 | return { 17 | }; 18 | }; 19 | 20 | 21 | const mapDispatchToProps = (dispatch) => { 22 | return { 23 | }; 24 | }; 25 | 26 | export default connect(mapStateToProps, mapDispatchToProps)(FileListEmptyMessage); 27 | 28 | 29 | -------------------------------------------------------------------------------- /src/Components/Loader/Loader.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import { withStyles } from '@material-ui/core/styles'; 4 | import CircularProgress from '@material-ui/core/CircularProgress'; 5 | import Grid from '@material-ui/core/Grid'; 6 | 7 | const styles = theme => ({ 8 | progress: { 9 | margin: theme.spacing.unit * 10, 10 | }, 11 | }); 12 | 13 | function Loader(props) { 14 | const { classes } = props; 15 | return ( 16 | 17 | 18 | 19 | ); 20 | } 21 | 22 | Loader.propTypes = { 23 | classes: PropTypes.object.isRequired, 24 | }; 25 | 26 | export default withStyles(styles)(Loader); 27 | -------------------------------------------------------------------------------- /src/Components/Dialogs/Dialogs.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import DialogContent from './Content/Content.jsx'; 3 | import DialogEdit from './Edit/Edit.jsx'; 4 | import DialogCreateFolder from './CreateFolder/CreateFolder.jsx'; 5 | import DialogRename from './Rename/Rename.jsx'; 6 | import DialogMove from './Move/Move.jsx'; 7 | import DialogCopy from './Copy/Copy.jsx'; 8 | import DialogUploadFile from './UploadFile/UploadFile.jsx'; 9 | 10 | function Dialogs(props) { 11 | return ( 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 |
21 | ); 22 | } 23 | 24 | export default Dialogs; 25 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import thunk from 'redux-thunk'; 4 | import { Provider } from 'react-redux'; 5 | import { createStore, applyMiddleware } from 'redux' 6 | import MainReducer from './Reducers/MainReducer' 7 | import * as serviceWorker from './serviceWorker'; 8 | import App from './App'; 9 | import './index.css'; 10 | 11 | const store = createStore(MainReducer, applyMiddleware(thunk)); 12 | ReactDOM.render( 13 | 14 | 15 | , 16 | document.getElementById('root') 17 | ); 18 | 19 | // If you want your app to work offline and load faster, you can change 20 | // unregister() to register() below. Note this comes with some pitfalls. 21 | // Learn more about service workers: http://bit.ly/CRA-PWA 22 | serviceWorker.register(); 23 | -------------------------------------------------------------------------------- /src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif; 5 | -webkit-font-smoothing: antialiased; 6 | -moz-osx-font-smoothing: grayscale; 7 | } 8 | 9 | code { 10 | font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", monospace; 11 | } 12 | 13 | *::-webkit-scrollbar-track { 14 | -webkit-box-shadow: inset 0 0 6px rgba(0,0,0,0.3); 15 | border-radius: 0; 16 | background-color: #F5F5F5; 17 | cursor: default; 18 | } 19 | 20 | *::-webkit-scrollbar { 21 | width: 8px; 22 | background-color: #F5F5F5; 23 | cursor: default; 24 | } 25 | 26 | *::-webkit-scrollbar-thumb { 27 | border-radius: 0px; 28 | -webkit-box-shadow: inset 0 0 6px rgba(0,0,0,.3); 29 | background-color: #555; 30 | cursor: default; 31 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # React Filemanager 2 | 3 | Hello ex [angular-filemanager](https://github.com/joni2back/angular-filemanager/) user, this is the new version in React. 4 | 5 | I will try to make it clean and retro-compatible with the previous bridges/connectors 6 | 7 | It's very important for me your collaboration on my development tasks and time. 8 | Please help me to move forward with a donation by paypal :) [![Donate](https://www.paypal.com/en_GB/i/btn/btn_donate_LG.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=XRB7EW72PS982) 9 | 10 | --- 11 | 12 | ### Environment configuration 13 | **1) Install deps using NPM with** 14 | ```npm install``` 15 | 16 | **2) Start development environment** 17 | ```npm start``` 18 | 19 | **3) Run tests** 20 | ```npm run test``` 21 | 22 | **4) Compile for production** 23 | ```npm run build``` 24 | 25 | --- 26 | 27 | ## Connectors 28 | I am also developing a local file connector API in NodeJS in [filemanager-connector-node](https://github.com/joni2back/filemanager-connector-node) 29 | -------------------------------------------------------------------------------- /src/Components/FileUploader/UploadFileList.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import List from '@material-ui/core/List'; 4 | import ListItem from '@material-ui/core/ListItem'; 5 | import ListItemIcon from '@material-ui/core/ListItemIcon'; 6 | import ListItemText from '@material-ui/core/ListItemText'; 7 | import FileIcon from '@material-ui/icons/InsertDriveFile'; 8 | import { getHumanFileSize } from '../../Api/ApiHandler'; 9 | 10 | function UploadFileList(props) { 11 | const { files } = props; 12 | const list = files.map((f, i) => 13 | 14 | 15 | 16 | 17 | 18 | 19 | ); 20 | 21 | return ( 22 |
23 | 24 | {list} 25 | 26 |
27 | ); 28 | } 29 | 30 | UploadFileList.propTypes = { 31 | files: PropTypes.array.isRequired 32 | }; 33 | 34 | export default UploadFileList; 35 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-filemanager", 3 | "version": "0.0.14", 4 | "private": true, 5 | "repository": { 6 | "type": "git", 7 | "url": "git+https://github.com/joni2back/react-filemanager.git" 8 | }, 9 | "keywords": [ 10 | "filemanager" 11 | ], 12 | "author": "Jonas Sciangula Street", 13 | "license": "MIT", 14 | "bugs": { 15 | "url": "https://github.com/joni2back/react-filemanager/issues" 16 | }, 17 | "homepage": "https://joni2back.github.io/react-filemanager", 18 | "dependencies": { 19 | "@material-ui/core": "^4.11.0", 20 | "@material-ui/icons": "^4.9.1", 21 | "react": "^16.6.3", 22 | "react-dom": "^16.6.3", 23 | "react-redux": "~5.1.1", 24 | "react-scripts": "^4.0.0", 25 | "redux": "~4.0.1", 26 | "redux-thunk": "~2.3.0" 27 | }, 28 | "scripts": { 29 | "start": "react-scripts start", 30 | "build": "react-scripts build", 31 | "test": "react-scripts test", 32 | "eject": "react-scripts eject" 33 | }, 34 | "eslintConfig": { 35 | "extends": "react-app" 36 | }, 37 | "browserslist": [ 38 | ">0.2%", 39 | "not dead", 40 | "not ie <= 11", 41 | "not op_mini all" 42 | ] 43 | } 44 | -------------------------------------------------------------------------------- /src/Components/ContextMenu/ContextMenuActions/RemoveAction.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import MenuItem from '@material-ui/core/MenuItem'; 3 | import { connect } from 'react-redux'; 4 | import { removeItems } from '../../../Actions/Actions.js'; 5 | import ListItemIcon from '@material-ui/core/ListItemIcon'; 6 | import Typography from '@material-ui/core/Typography'; 7 | import DeleteIcon from '@material-ui/icons/Delete'; 8 | 9 | function RemoveAction(props) { 10 | const {handleClick, selectedFiles} = props; 11 | return ( 12 | handleClick(e, selectedFiles)}> 13 | 14 | 15 | 16 | 17 | Remove 18 | 19 | 20 | ); 21 | } 22 | 23 | const mapStateToProps = (state) => { 24 | return { 25 | selectedFiles: state.selectedFiles 26 | }; 27 | }; 28 | 29 | const mapDispatchToProps = (dispatch, ownProps) => { 30 | return { 31 | handleClick: (event, selectedFiles) => { 32 | dispatch(removeItems(selectedFiles)); 33 | } 34 | }; 35 | }; 36 | 37 | export default connect(mapStateToProps, mapDispatchToProps)(RemoveAction); 38 | -------------------------------------------------------------------------------- /src/Components/ContextMenu/ContextMenuActions/RenameAction.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import MenuItem from '@material-ui/core/MenuItem'; 3 | import { connect } from 'react-redux'; 4 | import ListItemIcon from '@material-ui/core/ListItemIcon'; 5 | import Typography from '@material-ui/core/Typography'; 6 | import WrapTextIcon from '@material-ui/icons/WrapText'; 7 | import { setVisibleDialogRename } from '../../../Actions/Actions.js'; 8 | 9 | function MoveAction(props) { 10 | const {handleClick, selectedFiles} = props; 11 | 12 | return ( 13 | handleClick(e, selectedFiles)}> 14 | 15 | 16 | 17 | 18 | Rename 19 | 20 | 21 | ); 22 | } 23 | 24 | const mapStateToProps = (state) => { 25 | return { 26 | selectedFiles: state.selectedFiles 27 | }; 28 | }; 29 | 30 | const mapDispatchToProps = (dispatch, ownProps) => { 31 | return { 32 | handleClick: (event, selectedFiles) => { 33 | dispatch(setVisibleDialogRename(true)); 34 | } 35 | }; 36 | }; 37 | 38 | export default connect(mapStateToProps, mapDispatchToProps)(MoveAction); 39 | -------------------------------------------------------------------------------- /src/Components/ContextMenu/ContextMenuActions/DownloadAction.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import MenuItem from '@material-ui/core/MenuItem'; 3 | import { connect } from 'react-redux'; 4 | import { downloadFile } from '../../../Actions/Actions.js'; 5 | import ListItemIcon from '@material-ui/core/ListItemIcon'; 6 | import Typography from '@material-ui/core/Typography'; 7 | import CloudDownloadIcon from '@material-ui/icons/CloudDownload'; 8 | 9 | function DownloadAction(props) { 10 | const {handleClick, selectedFiles} = props; 11 | return ( 12 | handleClick(e, selectedFiles)}> 13 | 14 | 15 | 16 | 17 | Download 18 | 19 | 20 | ); 21 | } 22 | 23 | const mapStateToProps = (state) => { 24 | return { 25 | selectedFiles: state.selectedFiles 26 | }; 27 | }; 28 | 29 | const mapDispatchToProps = (dispatch, ownProps) => { 30 | return { 31 | handleClick: (event, selectedFiles) => { 32 | dispatch(downloadFile(selectedFiles[0].name)); 33 | } 34 | }; 35 | }; 36 | 37 | export default connect(mapStateToProps, mapDispatchToProps)(DownloadAction); 38 | -------------------------------------------------------------------------------- /src/Components/ContextMenu/ContextMenuActions/EditAction.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import MenuItem from '@material-ui/core/MenuItem'; 3 | import { connect } from 'react-redux'; 4 | import { getFileContentForEdit } from '../../../Actions/Actions.js'; 5 | import ListItemIcon from '@material-ui/core/ListItemIcon'; 6 | import Typography from '@material-ui/core/Typography'; 7 | import OpenInBrowserIcon from '@material-ui/icons/OpenInBrowser'; 8 | 9 | function OpenAction(props) { 10 | const {handleClick, selectedFiles} = props; 11 | return ( 12 | handleClick(e, selectedFiles)}> 13 | 14 | 15 | 16 | 17 | Edit 18 | 19 | 20 | ); 21 | } 22 | 23 | const mapStateToProps = (state) => { 24 | return { 25 | selectedFiles: state.selectedFiles 26 | }; 27 | }; 28 | 29 | const mapDispatchToProps = (dispatch, ownProps) => { 30 | return { 31 | handleClick: (event, selectedFiles) => { 32 | dispatch(getFileContentForEdit(selectedFiles[0].name)); 33 | } 34 | }; 35 | }; 36 | 37 | export default connect(mapStateToProps, mapDispatchToProps)(OpenAction); 38 | -------------------------------------------------------------------------------- /src/Components/ContextMenu/ContextMenuActions/CopyAction.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import MenuItem from '@material-ui/core/MenuItem'; 3 | import { connect } from 'react-redux'; 4 | import ListItemIcon from '@material-ui/core/ListItemIcon'; 5 | import Typography from '@material-ui/core/Typography'; 6 | import FileCopyIcon from '@material-ui/icons/FileCopy'; 7 | import { initSubList, setVisibleDialogCopy } from '../../../Actions/Actions.js'; 8 | 9 | function CopyAction(props) { 10 | const {handleClick, selectedFiles} = props; 11 | 12 | return ( 13 | handleClick(e, selectedFiles)}> 14 | 15 | 16 | 17 | 18 | Copy 19 | 20 | 21 | ); 22 | } 23 | 24 | const mapStateToProps = (state) => { 25 | return { 26 | selectedFiles: state.selectedFiles 27 | }; 28 | }; 29 | 30 | const mapDispatchToProps = (dispatch, ownProps) => { 31 | return { 32 | handleClick: (event, selectedFiles) => { 33 | dispatch(initSubList()); 34 | dispatch(setVisibleDialogCopy(true)); 35 | } 36 | }; 37 | }; 38 | 39 | export default connect(mapStateToProps, mapDispatchToProps)(CopyAction); 40 | -------------------------------------------------------------------------------- /src/Components/ContextMenu/ContextMenuActions/MoveAction.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import MenuItem from '@material-ui/core/MenuItem'; 3 | import { connect } from 'react-redux'; 4 | import ListItemIcon from '@material-ui/core/ListItemIcon'; 5 | import Typography from '@material-ui/core/Typography'; 6 | import HowToVoteIcon from '@material-ui/icons/HowToVote'; 7 | import { initSubList, setVisibleDialogMove } from '../../../Actions/Actions.js'; 8 | 9 | function MoveAction(props) { 10 | const {handleClick, selectedFiles} = props; 11 | 12 | return ( 13 | handleClick(e, selectedFiles)}> 14 | 15 | 16 | 17 | 18 | Move 19 | 20 | 21 | ); 22 | } 23 | 24 | const mapStateToProps = (state) => { 25 | return { 26 | selectedFiles: state.selectedFiles 27 | }; 28 | }; 29 | 30 | const mapDispatchToProps = (dispatch, ownProps) => { 31 | return { 32 | handleClick: (event, selectedFiles) => { 33 | dispatch(initSubList()); 34 | dispatch(setVisibleDialogMove(true)); 35 | } 36 | }; 37 | }; 38 | 39 | export default connect(mapStateToProps, mapDispatchToProps)(MoveAction); 40 | -------------------------------------------------------------------------------- /src/Components/ContextMenu/ContextMenuActions/UploadFileAction.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import MenuItem from '@material-ui/core/MenuItem'; 3 | import { connect } from 'react-redux'; 4 | import ListItemIcon from '@material-ui/core/ListItemIcon'; 5 | import Typography from '@material-ui/core/Typography'; 6 | import CloudUploadIcon from '@material-ui/icons/CloudUpload'; 7 | import { setVisibleDialogUploadFile } from '../../../Actions/Actions.js'; 8 | 9 | function UploadFileAction(props) { 10 | const {handleClick, handleClose} = props; 11 | 12 | const handleCloseAfter = (callback) => (event) => { 13 | callback(); 14 | handleClose(); 15 | }; 16 | 17 | return ( 18 | 19 | 20 | 21 | 22 | 23 | Upload files 24 | 25 | 26 | ); 27 | } 28 | 29 | const mapStateToProps = (state) => { 30 | return { 31 | }; 32 | }; 33 | 34 | const mapDispatchToProps = (dispatch, ownProps) => { 35 | return { 36 | handleClick: (event) => { 37 | dispatch(setVisibleDialogUploadFile(true)); 38 | } 39 | }; 40 | }; 41 | 42 | export default connect(mapStateToProps, mapDispatchToProps)(UploadFileAction); 43 | -------------------------------------------------------------------------------- /src/Components/ContextMenu/ContextMenuActions/CreateFolderAction.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import MenuItem from '@material-ui/core/MenuItem'; 3 | import { connect } from 'react-redux'; 4 | import ListItemIcon from '@material-ui/core/ListItemIcon'; 5 | import Typography from '@material-ui/core/Typography'; 6 | import CreateNewFolderIcon from '@material-ui/icons/CreateNewFolder'; 7 | import { setVisibleDialogCreateFolder } from '../../../Actions/Actions.js'; 8 | 9 | function CreateFolderAction(props) { 10 | const {handleClick, handleClose} = props; 11 | 12 | const handleCloseAfter = (callback) => (event) => { 13 | callback(); 14 | handleClose(); 15 | }; 16 | 17 | return ( 18 | 19 | 20 | 21 | 22 | 23 | Create folder 24 | 25 | 26 | ); 27 | } 28 | 29 | const mapStateToProps = (state) => { 30 | return { 31 | }; 32 | }; 33 | 34 | const mapDispatchToProps = (dispatch, ownProps) => { 35 | return { 36 | handleClick: (event) => { 37 | dispatch(setVisibleDialogCreateFolder(true)); 38 | } 39 | }; 40 | }; 41 | 42 | export default connect(mapStateToProps, mapDispatchToProps)(CreateFolderAction); 43 | -------------------------------------------------------------------------------- /src/Components/FileList/FileList.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { connect } from 'react-redux'; 3 | import File from '../File/File.jsx'; 4 | import FileListEmptyMessage from './FileListEmptyMessage'; 5 | import Loader from '../Loader/Loader.jsx'; 6 | import './FileList.css'; 7 | 8 | class FileList extends Component { 9 | render() { 10 | const { fileList, loading } = this.props; 11 | 12 | const fileListComponent = fileList.map((file, key) => { 13 | return 14 | }); 15 | 16 | return
17 | { loading ? 18 | : 19 | fileListComponent.length ? fileListComponent : 20 | } 21 |
22 | } 23 | } 24 | 25 | 26 | const mapStateToProps = (state) => { 27 | const filteredList = state.fileList.filter( 28 | file => state.fileListFilter ? file.name.toLocaleLowerCase().match(state.fileListFilter.toLocaleLowerCase()) : true 29 | ); 30 | return { 31 | fileList: filteredList, 32 | loading: state.loading 33 | }; 34 | }; 35 | 36 | 37 | const mapDispatchToProps = (dispatch) => { 38 | return { 39 | handleClick: (event) => { 40 | } 41 | }; 42 | }; 43 | 44 | export default connect(mapStateToProps, mapDispatchToProps)(FileList); 45 | 46 | 47 | -------------------------------------------------------------------------------- /src/Components/ContextMenu/ContextMenuActions/OpenAction.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import MenuItem from '@material-ui/core/MenuItem'; 3 | import { connect } from 'react-redux'; 4 | import { getFileContent, enterToDirectory } from '../../../Actions/Actions.js'; 5 | import ListItemIcon from '@material-ui/core/ListItemIcon'; 6 | import Typography from '@material-ui/core/Typography'; 7 | import OpenInBrowserIcon from '@material-ui/icons/OpenInBrowser'; 8 | 9 | function OpenAction(props) { 10 | const {handleClick, selectedFiles} = props; 11 | return ( 12 | handleClick(e, selectedFiles)}> 13 | 14 | 15 | 16 | 17 | Open 18 | 19 | 20 | ); 21 | } 22 | 23 | const mapStateToProps = (state) => { 24 | return { 25 | selectedFiles: state.selectedFiles 26 | }; 27 | }; 28 | 29 | const mapDispatchToProps = (dispatch, ownProps) => { 30 | return { 31 | handleClick: (event, selectedFiles) => { 32 | if (selectedFiles[0].type === 'dir') { 33 | dispatch(enterToDirectory(selectedFiles[0].name)); 34 | return; 35 | } 36 | dispatch(getFileContent(selectedFiles[0].name)); 37 | } 38 | }; 39 | }; 40 | 41 | export default connect(mapStateToProps, mapDispatchToProps)(OpenAction); 42 | -------------------------------------------------------------------------------- /src/config.js: -------------------------------------------------------------------------------- 1 | const host = 'http://localhost:8000'; 2 | 3 | export default { 4 | url_list: `${host}/filemanager/list`, 5 | url_create_folder: `${host}/filemanager/dir/create`, 6 | url_get_content: `${host}/filemanager/file/content`, 7 | url_download: `${host}/filemanager/file/content`, 8 | url_upload: `${host}/filemanager/items/upload`, 9 | url_remove: `${host}/filemanager/items/remove`, 10 | url_rename: `${host}/filemanager/item/move`, 11 | url_move: `${host}/filemanager/items/move`, 12 | url_copy: `${host}/filemanager/items/copy`, 13 | url_edit: `${host}/filemanager/file/edit`, 14 | url_compress: `${host}/filemanager/items/compress`, 15 | url_extract: `${host}/filemanager/file/extract`, 16 | 17 | isEditableFilePattern: /\.(txt|diff?|patch|svg|asc|cnf|cfg|conf|html?|cfm|cgi|aspx?|ini|pl|py|md|css|cs|jsx?|jsp|log|htaccess|htpasswd|gitignore|gitattributes|env|json|atom|eml|rss|markdown|sql|xml|xslt?|sh|rb|as|bat|cmd|cob|for|ftn|frm|frx|inc|lisp|scm|coffee|php[3-6]?|java|c|cbl|go|h|scala|vb|tmpl|lock|go|yml|yaml|tsv|lst)$/i, 18 | isImageFilePattern: /\.(jpe?g|gif|bmp|png|svg|tiff?)$/i, 19 | isExtractableFilePattern: /\.(gz|tar|rar|g?zip)$/i, 20 | 21 | actions: { 22 | create_folder: true, 23 | move: true, 24 | copy: true, 25 | copy_folder: true, 26 | compress: true, 27 | extract: true, 28 | edit: true, 29 | remove: true, 30 | upload: true, 31 | upload_by_chunks: true, 32 | preview_images: true, 33 | } 34 | }; 35 | -------------------------------------------------------------------------------- /src/Components/FileList/FileListSublist/FileListSublist.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { connect } from 'react-redux'; 3 | import FileSublist from '../../File//FileSublist/FileSublist.jsx'; 4 | import Loader from '../../Loader/Loader.jsx'; 5 | import FileListEmptyMessage from '../FileListEmptyMessage'; 6 | import './FileListSublist.css'; 7 | 8 | class FileListSublist extends Component { 9 | render() { 10 | const { fileList, loadingSublist } = this.props; 11 | 12 | const fileListComponent = fileList.map((file, key) => { 13 | return 14 | }); 15 | 16 | return
17 | { loadingSublist ? 18 | : 19 | fileListComponent.length ? fileListComponent : 20 | } 21 |
22 | } 23 | } 24 | 25 | const mapStateToProps = (state) => { 26 | const filteredList = state.fileListSublist 27 | .filter(file => file.type === 'dir') 28 | .filter(file => state.path.join('').trim() === state.pathSublist.join('').trim() ? 29 | !state.selectedFiles.find(f => f.name === file.name) : true 30 | ); 31 | return { 32 | fileList: filteredList, 33 | loadingSublist: state.loadingSublist, 34 | }; 35 | }; 36 | 37 | const mapDispatchToProps = (dispatch) => { 38 | return { 39 | }; 40 | }; 41 | 42 | export default connect(mapStateToProps, mapDispatchToProps)(FileListSublist); -------------------------------------------------------------------------------- /src/Components/FileUploader/FileUploader.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import Button from '@material-ui/core/Button'; 4 | import UploadFileList from './UploadFileList'; 5 | 6 | class FileUploader extends Component { 7 | 8 | handleReset(event) { 9 | this.refs.inputfile.value = ''; 10 | this.props.handleReset(event); 11 | } 12 | 13 | render() { 14 | const { fileUploadList, handleSelectedFiles } = this.props; 15 | const styles = { 16 | inputfile: { 17 | display: 'none' 18 | }, inputreset: { 19 | display: fileUploadList.length ? 'inline-flex' : 'none' 20 | } 21 | } 22 | 23 | return ( 24 |
25 | 31 | 32 | 35 | 36 | 37 |
38 | ); 39 | } 40 | } 41 | 42 | FileUploader.propTypes = { 43 | fileUploadList: PropTypes.array.isRequired, 44 | handleReset: PropTypes.func.isRequired, 45 | handleSelectedFiles: PropTypes.func.isRequired, 46 | }; 47 | 48 | export default FileUploader; 49 | -------------------------------------------------------------------------------- /src/Components/Navbar/ThreeDotsMenu.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Menu from '@material-ui/core/Menu'; 3 | import IconButton from '@material-ui/core/IconButton'; 4 | import MoreVertIcon from '@material-ui/icons/MoreVert'; 5 | import { connect } from 'react-redux'; 6 | import CreateFolderAction from '../ContextMenu/ContextMenuActions/CreateFolderAction.jsx'; 7 | import UploadFileAction from '../ContextMenu/ContextMenuActions/UploadFileAction.jsx'; 8 | 9 | class ThreeDotsMenu extends React.Component { 10 | state = { 11 | anchorEl: null, 12 | }; 13 | 14 | handleClick = event => { 15 | this.setState({ anchorEl: event.currentTarget }); 16 | }; 17 | 18 | handleClose = () => { 19 | this.setState({ anchorEl: null }); 20 | }; 21 | 22 | render() { 23 | const { anchorEl } = this.state; 24 | return ( 25 |
26 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 |
39 | ); 40 | } 41 | } 42 | 43 | 44 | const mapStateToProps = (state) => { 45 | return { 46 | }; 47 | }; 48 | 49 | const mapDispatchToProps = (dispatch, ownProps) => { 50 | return { 51 | }; 52 | }; 53 | 54 | export default connect(mapStateToProps, mapDispatchToProps)(ThreeDotsMenu); 55 | -------------------------------------------------------------------------------- /src/App.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import FileList from './Components/FileList/FileList.jsx'; 3 | import Navbar from './Components/Navbar/Navbar.jsx'; 4 | import ContextMenu from './Components/ContextMenu/ContextMenu.jsx'; 5 | import Dialogs from './Components/Dialogs/Dialogs.jsx'; 6 | 7 | import { MuiThemeProvider as MaterialUI, createMuiTheme } from '@material-ui/core/styles'; 8 | import blue from '@material-ui/core/colors/blue'; 9 | import { connect } from 'react-redux'; 10 | import { setContextMenuVisible, refreshFileList } from './Actions/Actions.js'; 11 | import DynamicSnackbar from './Components/Notification/DynamicSnackbar.jsx'; 12 | 13 | const theme = createMuiTheme({ 14 | palette: { 15 | primary: blue, 16 | }, 17 | typography: { 18 | useNextVariants: true, 19 | } 20 | }); 21 | 22 | class App extends Component { 23 | 24 | componentDidMount() { 25 | this.props.init(); 26 | }; 27 | 28 | render() { 29 | return ( 30 | 31 |
32 | 33 | 34 | 35 | 36 | 37 |
38 |
39 | ); 40 | } 41 | } 42 | 43 | const mapStateToProps = (state) => { 44 | return { 45 | }; 46 | }; 47 | 48 | const mapDispatchToProps = (dispatch) => { 49 | return { 50 | init: () => { 51 | dispatch(refreshFileList()); 52 | }, 53 | 54 | handleHideContextMenu: (event) => { 55 | if (! (event.target.tagName === 'INPUT' || /label/i.test(event.target.className))) { 56 | event.preventDefault(); 57 | } 58 | dispatch(setContextMenuVisible(false)); 59 | } 60 | }; 61 | }; 62 | 63 | export default connect(mapStateToProps, mapDispatchToProps)(App); 64 | -------------------------------------------------------------------------------- /src/Components/Notification/DynamicSnackbar.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import { withStyles } from '@material-ui/core/styles'; 4 | import Snackbar from '@material-ui/core/Snackbar'; 5 | import IconButton from '@material-ui/core/IconButton'; 6 | import CloseIcon from '@material-ui/icons/Close'; 7 | import { connect } from 'react-redux'; 8 | 9 | const styles = theme => ({ 10 | close: { 11 | padding: theme.spacing.unit / 2, 12 | }, 13 | }); 14 | 15 | class DynamicSnackbar extends React.Component { 16 | render() { 17 | const { classes, errorMsg, handleClose, open, notificationDuration } = this.props; 18 | return ( 19 |
20 | {errorMsg}} 32 | action={[ 33 | 34 | 35 | , 36 | ]} 37 | /> 38 |
39 | ); 40 | } 41 | } 42 | 43 | DynamicSnackbar.propTypes = { 44 | classes: PropTypes.object.isRequired, 45 | }; 46 | 47 | 48 | const mapStateToProps = (state, ownProps) => { 49 | return { 50 | open: !!state.errorMsg, 51 | errorMsg: state.errorMsg, 52 | notificationDuration: state.notificationDuration || 60000 53 | }; 54 | }; 55 | 56 | const mapDispatchToProps = (dispatch, ownProps) => { 57 | return { 58 | handleClose: (event) => { 59 | dispatch({ 60 | type: 'SET_ERROR_MSG', 61 | value: null 62 | }); 63 | } 64 | }; 65 | }; 66 | 67 | export default connect(mapStateToProps, mapDispatchToProps)(withStyles(styles)(DynamicSnackbar)); 68 | 69 | -------------------------------------------------------------------------------- /src/Components/Breadcrumb/BreadcrumbText.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { connect } from 'react-redux'; 3 | import { withStyles } from '@material-ui/core/styles'; 4 | import KeyboardArrowLeftIcon from '@material-ui/icons/KeyboardArrowLeft'; 5 | import Button from '@material-ui/core/Button'; 6 | import './BreadcrumbText.css'; 7 | 8 | const styles = theme => ({ 9 | lastPath: { 10 | display: 'block', 11 | [theme.breakpoints.up('sm')]: { 12 | display: 'none' 13 | } 14 | }, 15 | paths: { 16 | display: 'none', 17 | [theme.breakpoints.up('sm')]: { 18 | display: 'block', 19 | } 20 | } 21 | }); 22 | 23 | class BreadcrumbText extends Component { 24 | 25 | render() { 26 | const { classes, handleClickPath, path, rootTitle, handleGoBack, canGoBack } = this.props; 27 | 28 | const separator = >; 29 | const rootPath = handleClickPath(e, -1, path)} data-index={0}> 30 | { rootTitle } { path.length ? separator : '' } 31 | ; 32 | const lastPath = [...path].pop() || rootTitle; 33 | 34 | const directories = path.map((dir, index) => { 35 | return handleClickPath(e, index, path)}> 36 | {dir} { path.length -1 !== index ? separator : '' }  37 | 38 | }); 39 | 40 | return ( 41 |
42 |
43 | 46 | {lastPath} 47 |
48 |
{rootPath} {directories}
49 |
50 | ); 51 | } 52 | } 53 | 54 | 55 | const mapDispatchToProps = (dispatch) => { 56 | return { 57 | }; 58 | }; 59 | 60 | const mapStateToProps = (state) => { 61 | return { 62 | }; 63 | }; 64 | 65 | export default withStyles(styles)(connect(mapStateToProps, mapDispatchToProps)(BreadcrumbText)); 66 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 14 | 15 | 24 | 25 | React Filemanager 26 | 45 | 46 | 47 |
48 |
49 |
50 | 51 | 52 | -------------------------------------------------------------------------------- /src/Components/Dialogs/Content/Content.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import Button from '@material-ui/core/Button'; 3 | import Dialog from '@material-ui/core/Dialog'; 4 | import DialogActions from '@material-ui/core/DialogActions'; 5 | import DialogContent from '@material-ui/core/DialogContent'; 6 | import DialogTitle from '@material-ui/core/DialogTitle'; 7 | import { connect } from 'react-redux'; 8 | import { setVisibleDialogContent } from '../../../Actions/Actions.js'; 9 | 10 | class FormDialog extends Component { 11 | 12 | state = { 13 | lastBlobUrl: null, 14 | content: '...', 15 | loading: false 16 | }; 17 | 18 | componentDidUpdate() { 19 | if (this.props.blobUrl !== this.state.lastBlobUrl) { 20 | this.setState({ 21 | lastBlobUrl: this.props.blobUrl 22 | }); 23 | this.setState({ 24 | loading: true 25 | }); 26 | } 27 | } 28 | 29 | render() { 30 | const { handleClose, open } = this.props; 31 | return ( 32 |
33 | 34 | Viewing file 35 | 36 | 37 | 38 | 39 | 42 | 43 | 44 |
45 | ); 46 | } 47 | } 48 | 49 | const mapStateToProps = (state) => { 50 | return { 51 | open: state.visibleDialogContent, 52 | blobUrl: state.fileContentBlobUrl 53 | }; 54 | }; 55 | 56 | const mapDispatchToProps = (dispatch, ownProps) => { 57 | return { 58 | handleClose: (event) => { 59 | dispatch(setVisibleDialogContent(false)); 60 | }, 61 | handleOpen: (event) => { 62 | dispatch(setVisibleDialogContent(true)); 63 | }, 64 | }; 65 | }; 66 | 67 | export default connect(mapStateToProps, mapDispatchToProps)(FormDialog); 68 | -------------------------------------------------------------------------------- /src/Components/Dialogs/CreateFolder/CreateFolder.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import Button from '@material-ui/core/Button'; 3 | import TextField from '@material-ui/core/TextField'; 4 | import Dialog from '@material-ui/core/Dialog'; 5 | import DialogActions from '@material-ui/core/DialogActions'; 6 | import DialogContent from '@material-ui/core/DialogContent'; 7 | import DialogTitle from '@material-ui/core/DialogTitle'; 8 | import { connect } from 'react-redux'; 9 | import { createNewFolder, setVisibleDialogCreateFolder } from '../../../Actions/Actions.js'; 10 | 11 | class FormDialog extends Component { 12 | 13 | render() { 14 | const { handleClose, handleSave, value, open } = this.props; 15 | 16 | return ( 17 | 18 |
19 | Create folder 20 | 21 | 22 | 23 | 24 | 27 | 30 | 31 |
32 |
33 | ); 34 | } 35 | } 36 | 37 | const mapStateToProps = (state) => { 38 | return { 39 | createFolderName: state.createFolderName, 40 | open: state.visibleDialogCreateFolder 41 | }; 42 | }; 43 | 44 | const mapDispatchToProps = (dispatch, ownProps) => { 45 | return { 46 | handleClose: event => { 47 | dispatch(setVisibleDialogCreateFolder(false)); 48 | }, 49 | handleSave: event => { 50 | event.preventDefault(); 51 | const folderName = event.currentTarget.form.querySelector('input').value; 52 | dispatch(createNewFolder(folderName)); 53 | } 54 | }; 55 | }; 56 | 57 | export default connect(mapStateToProps, mapDispatchToProps)(FormDialog); 58 | -------------------------------------------------------------------------------- /src/Components/File/FileSublist/FileSublist.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { connect } from 'react-redux'; 3 | import { 4 | setSelectedFolderSublist, enterToDirectorySublist 5 | } from '../../../Actions/Actions.js'; 6 | 7 | import { withStyles } from '@material-ui/core/styles'; 8 | import ListItem from '@material-ui/core/ListItem'; 9 | import ListItemAvatar from '@material-ui/core/ListItemAvatar'; 10 | import ListItemText from '@material-ui/core/ListItemText'; 11 | import Avatar from '@material-ui/core/Avatar'; 12 | import FolderIcon from '@material-ui/icons/Folder'; 13 | import FileIcon from '@material-ui/icons/InsertDriveFile'; 14 | import blue from '@material-ui/core/colors/blue'; 15 | import '../File.css'; 16 | 17 | const styles = theme => ({ 18 | }); 19 | 20 | 21 | class FileSublist extends Component { 22 | render() { 23 | const { type, name, handleClick, isSelected, handleDoubleClick } = this.props; 24 | const avatarStyle = { 25 | backgroundColor: isSelected ? blue['A200'] : null 26 | }; 27 | return ( 28 |
29 | 30 | 31 | 32 | { type === 'dir' ? : } 33 | 34 | 35 | 36 | 37 |
38 | ); 39 | } 40 | } 41 | 42 | 43 | const mapStateToProps = (state, ownProps) => { 44 | return { 45 | filePath: [...state.path, ownProps.name], 46 | isSelected: state.selectedFolderSublist && (state.selectedFolderSublist.name === ownProps.name) 47 | }; 48 | }; 49 | 50 | const mapDispatchToProps = (dispatch, ownProps) => { 51 | return { 52 | /** 53 | * @param {Object} event 54 | * @returns {undefined} 55 | */ 56 | handleDoubleClick: (event) => { 57 | dispatch(enterToDirectorySublist(ownProps.name)); 58 | dispatch(setSelectedFolderSublist(null)); 59 | }, 60 | 61 | /** 62 | * @param {Object} event 63 | * @returns {undefined} 64 | */ 65 | handleClick: (event) => { 66 | event.stopPropagation(); 67 | dispatch(setSelectedFolderSublist(ownProps)); 68 | } 69 | }; 70 | }; 71 | 72 | export default connect(mapStateToProps, mapDispatchToProps)(withStyles(styles)(FileSublist)); 73 | 74 | -------------------------------------------------------------------------------- /src/Components/Dialogs/Rename/Rename.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import Button from '@material-ui/core/Button'; 3 | import TextField from '@material-ui/core/TextField'; 4 | import Dialog from '@material-ui/core/Dialog'; 5 | import DialogActions from '@material-ui/core/DialogActions'; 6 | import DialogContent from '@material-ui/core/DialogContent'; 7 | import DialogTitle from '@material-ui/core/DialogTitle'; 8 | import { connect } from 'react-redux'; 9 | import { renameItem, setVisibleDialogRename } from '../../../Actions/Actions.js'; 10 | 11 | class FormDialog extends Component { 12 | 13 | state = { 14 | value: '' 15 | }; 16 | 17 | componentWillReceiveProps (props) { 18 | this.setState({value: props.realName}); 19 | } 20 | 21 | handleChange (event) { 22 | this.setState({value: event.currentTarget.form.querySelector('input').value}); 23 | } 24 | 25 | handleSave (event) { 26 | this.props.handleSave(event)(this.props.realName, this.state.value); 27 | } 28 | 29 | render() { 30 | const { value } = this.state; 31 | const { handleClose, open } = this.props; 32 | 33 | return ( 34 | 35 |
36 | Rename 37 | 38 | 39 | 40 | 41 | 44 | 47 | 48 |
49 |
50 | ); 51 | } 52 | } 53 | 54 | const mapStateToProps = (state) => { 55 | return { 56 | open: state.visibleDialogRename, 57 | realName: state.selectedFiles.length ? state.selectedFiles[0].name : '' 58 | }; 59 | }; 60 | 61 | const mapDispatchToProps = (dispatch, ownProps) => { 62 | return { 63 | handleClose: event => { 64 | dispatch(setVisibleDialogRename(false)); 65 | }, 66 | handleSave: event => (realName, newName) => { 67 | event.preventDefault(); 68 | dispatch(renameItem(realName, newName)); 69 | } 70 | }; 71 | }; 72 | 73 | export default connect(mapStateToProps, mapDispatchToProps)(FormDialog); 74 | -------------------------------------------------------------------------------- /src/Components/ContextMenu/ContextMenu.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { connect } from 'react-redux'; 3 | import './ContextMenu.css'; 4 | import Menu from '@material-ui/core/Menu'; 5 | import { getActionsByMultipleFiles } from '../../Api/ApiHandler.js'; 6 | import OpenAction from './ContextMenuActions/OpenAction.jsx'; 7 | import RemoveAction from './ContextMenuActions/RemoveAction.jsx'; 8 | import MoveAction from './ContextMenuActions/MoveAction.jsx'; 9 | import CopyAction from './ContextMenuActions/CopyAction.jsx'; 10 | import EditAction from './ContextMenuActions/EditAction.jsx'; 11 | import RenameAction from './ContextMenuActions/RenameAction.jsx'; 12 | import DownloadAction from './ContextMenuActions/DownloadAction.jsx'; 13 | 14 | class ContextMenu extends Component { 15 | 16 | render() { 17 | const { acts, visible, x, y } = this.props; 18 | const actionsComp = acts.map((act, key) => { 19 | let component; 20 | if (act === 'open') { 21 | component = ; 22 | } 23 | if (act === 'edit') { 24 | component = ; 25 | } 26 | if (act === 'copy') { 27 | component = ; 28 | } 29 | if (act === 'move') { 30 | component = ; 31 | } 32 | if (act === 'rename') { 33 | component = ; 34 | } 35 | if (act === 'download') { 36 | component = ; 37 | } 38 | if (act === 'remove') { 39 | component = ; 40 | } 41 | return component; 42 | }); 43 | 44 | return ( 45 |
46 | {} } 55 | PaperProps={{ style: {width: 170} }}> 56 | { actionsComp } 57 | 58 |
59 | ); 60 | } 61 | } 62 | 63 | const mapStateToProps = (state) => { 64 | return { 65 | x: state.contextMenuPosition[0] || 0, 66 | y: state.contextMenuPosition[1] || 0, 67 | visible: !!state.contextMenuVisible, 68 | acts: getActionsByMultipleFiles(state.selectedFiles), 69 | }; 70 | }; 71 | 72 | const mapDispatchToProps = (dispatch) => { 73 | return { 74 | }; 75 | }; 76 | 77 | export default connect(mapStateToProps, mapDispatchToProps)(ContextMenu); 78 | -------------------------------------------------------------------------------- /src/Components/Dialogs/UploadFile/UploadFile.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import Button from '@material-ui/core/Button'; 3 | import Dialog from '@material-ui/core/Dialog'; 4 | import DialogActions from '@material-ui/core/DialogActions'; 5 | import DialogContent from '@material-ui/core/DialogContent'; 6 | import DialogTitle from '@material-ui/core/DialogTitle'; 7 | import LinearProgress from '@material-ui/core/LinearProgress'; 8 | import { connect } from 'react-redux'; 9 | import { resetFileUploader, uploadFiles, setFileUploadList } from '../../../Actions/Actions.js'; 10 | import FileUploader from '../../FileUploader/FileUploader.jsx'; 11 | 12 | class FormDialog extends Component { 13 | 14 | render() { 15 | const { handleClose, handleReset, handleUpload, open, canUpload, fileUploadProgress, fileUploadList, handleSelectedFiles } = this.props; 16 | 17 | return ( 18 | 19 |
20 | 21 | Upload files 22 | 23 | 24 | 25 | {canUpload ? : null } 26 | 27 | 28 | 31 | 34 | 35 |
36 |
37 | ); 38 | } 39 | } 40 | 41 | const mapStateToProps = (state) => { 42 | return { 43 | open: state.visibleDialogUploadFile, 44 | canUpload: state.fileUploadList.length, 45 | fileUploadList: state.fileUploadList, 46 | fileUploadProgress: state.fileUploadProgress 47 | }; 48 | }; 49 | 50 | const mapDispatchToProps = (dispatch, ownProps) => { 51 | return { 52 | handleClose: (event) => { 53 | dispatch(resetFileUploader()); 54 | }, 55 | handleUpload: (event) => { 56 | event.preventDefault(); 57 | const files = event.currentTarget.form.querySelector('input[type=file]').files; 58 | dispatch(uploadFiles(files)); 59 | }, 60 | handleSelectedFiles: (event) => { 61 | dispatch(setFileUploadList( 62 | [...event.target.files].map(f => ({name: f.name, size: f.size})) 63 | )); 64 | }, 65 | handleReset: (event) => { 66 | dispatch(setFileUploadList([])); 67 | } 68 | }; 69 | }; 70 | 71 | export default connect(mapStateToProps, mapDispatchToProps)(FormDialog); 72 | -------------------------------------------------------------------------------- /src/Components/Dialogs/Edit/Edit.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import Button from '@material-ui/core/Button'; 3 | import Dialog from '@material-ui/core/Dialog'; 4 | import DialogActions from '@material-ui/core/DialogActions'; 5 | import DialogContent from '@material-ui/core/DialogContent'; 6 | import DialogContentText from '@material-ui/core/DialogContentText'; 7 | import DialogTitle from '@material-ui/core/DialogTitle'; 8 | import { connect } from 'react-redux'; 9 | import { setVisibleDialogEdit } from '../../../Actions/Actions.js'; 10 | 11 | class FormDialog extends Component { 12 | 13 | state = { 14 | lastBlobUrl: null, 15 | content: null, 16 | loading: false 17 | }; 18 | 19 | componentDidUpdate() { 20 | if (this.props.blobUrl !== this.state.lastBlobUrl) { 21 | this.setState({ 22 | lastBlobUrl: this.props.blobUrl 23 | }); 24 | this.setState({ 25 | loading: true 26 | }); 27 | 28 | this.props.blobUrl && fetch(this.props.blobUrl).then(r => { 29 | return r.text(); 30 | }).then(t => { 31 | this.setState({ 32 | content: t 33 | }); 34 | this.setState({ 35 | loading: false 36 | }); 37 | }); 38 | } 39 | } 40 | 41 | render() { 42 | const { handleClose, handleSave, open } = this.props; 43 | const textAreaStyle = { 44 | width: '100%', 45 | minHeight: '300px' 46 | }; 47 | const textArea =