import React from 'react'; import PropTypes from 'prop-types'; import { Link } from 'react-router-dom'; import { DragSource, DropTarget } from 'react-dnd'; import './thing.scss'; import { Card, NgIf, Icon, EventEmitter } from '../../components/'; import { pathBuilder, prompt, leftPad } from '../../helpers/'; const fileSource = { beginDrag(props, monitor, component) { return { path: props.path, name: props.file.name, type: props.file.type }; }, canDrag(props, monitor){ return props.file.icon === 'loading'? false : true; }, endDrag(props, monitor, component){ if(monitor.didDrop() && component.state.icon !== 'loading'){ let result = monitor.getDropResult(); if(result.action === 'rename'){ props.emit.apply(component, ['file.rename'].concat(result.args)); }else{ throw 'unknown action'; } } } }; const fileTarget = { canDrop(props, monitor){ let file = monitor.getItem(); if(props.file.type === 'directory' && file.name !== props.file.name && props.file.icon !== 'loading'){ return true; }else{ return false; } }, drop(props, monitor, component){ let src = monitor.getItem(); let dest = props.file; let from = pathBuilder(props.path, src.name, src.type); let to = pathBuilder(props.path, './'+dest.name+'/'+src.name, src.type); return {action: 'rename', args: [from, to, src.type], ctx: 'existingfile'}; } }; const nativeFileTarget = { canDrop: fileTarget.canDrop, drop(props, monitor){ let path = pathBuilder(props.path, props.file.name, 'directory'); props.emit('file.upload', path, monitor.getItem()); } } @EventEmitter @DropTarget('__NATIVE_FILE__', nativeFileTarget, (connect, monitor) => ({ connectDropNativeFile: connect.dropTarget(), nativeFileIsOver: monitor.isOver(), canDropNativeFile: monitor.canDrop() })) @DropTarget('file', fileTarget, (connect, monitor) => ({ connectDropFile: connect.dropTarget(), fileIsOver: monitor.isOver(), canDropFile: monitor.canDrop() })) @DragSource('file', fileSource, (connect, monitor) => ({ connectDragSource: connect.dragSource(), isDragging: monitor.isDragging() })) export class ExistingThing extends React.Component { constructor(props){ super(props); this.state = { hover: null, message: null, filename: props.file.name, is_renaming: false }; } onRename(newFilename){ this.props.emit( 'file.rename', pathBuilder(this.props.path, this.props.file.name, this.props.file.type), pathBuilder(this.props.path, newFilename, this.props.file.type), this.props.file.type ); this.setState({is_renaming: false}); } onRenameRequest(){ this.setState({is_renaming: !this.state.is_renaming}); } onDeleteRequest(filename){ prompt.now( "Confirm by typing \""+this._confirm_delete_text()+"\"", (answer) => { // click on ok if(answer === this._confirm_delete_text()){ this.setState({icon: 'loading'}); this.props.emit( 'file.delete', pathBuilder(this.props.path, this.props.file.name, this.props.file.type), this.props.file.type ); return Promise.resolve(); }else{ return Promise.reject("Doesn't match"); } }, () => { /* click on cancel */ }); } onDeleteConfirm(answer){ if(answer === this._confirm_delete_text()){ this.setState({icon: 'loading', delete_request: false}); this.props.emit( 'file.delete', pathBuilder(this.props.path, this.props.file.name, this.props.file.type), this.props.file.type ); }else{ this.setState({delete_error: "Doesn't match"}); } } onDeleteCancel(){ this.setState({delete_request: false}); } _confirm_delete_text(){ return this.props.file.name.length > 16? this.props.file.name.substring(0, 10).toLowerCase() : this.props.file.name; } render(highlight){ const { connectDragSource, connectDropFile, connectDropNativeFile } = this.props; let className = ""; if(this.props.isDragging) { className += "is-dragging "; } if((this.props.fileIsOver && this.props.canDropFile) || (this.props.nativeFileIsOver && this.props.canDropNativeFile)) { className += "file-is-hover "; } if(this.state.is_renaming){ className += "highlight "; } if(this.props.file.icon === 'loading'){ className += "loading "; } className = className.trim(); return connectDragSource(connectDropNativeFile(connectDropFile(
))); } } ExistingThing.PropTypes = { connectDragSource: PropTypes.func.isRequired, isDragging: PropTypes.bool.isRequired, fileIsOver: PropTypes.bool.isRequired, nativeFileIsOver: PropTypes.bool.isRequired, canDropFile: PropTypes.bool.isRequired, canDropNativeFile: PropTypes.bool.isRequired } class Filename extends React.Component { constructor(props){ super(props); this.state = { filename: props.filename }; } onRename(e){ e.preventDefault(); e.stopPropagation(); this.props.onRename(this.state.filename); } preventSelect(e){ e.preventDefault(); } render(){ return ( {this.state.filename}
this.setState({filename: e.target.value})} autoFocus />
); } } const ActionButton = (props) => { const onRename = (e) => { e.preventDefault(); props.onClickRename(); }; const onDelete = (e) => { e.preventDefault(); props.onClickDelete(); }; return (
); } const DateTime = (props) => { function displayTime(timestamp){ if(timestamp && Intl && typeof Intl.DateTimeFormat === "function"){ return Intl.DateTimeFormat() .format(new Date(timestamp)) .split("/") .map((n, i) => { return leftPad(n, 2); }) .join("/"); }else if(timestamp){ let t = new Date(timestamp); return t.getFullYear() + "-" + leftPad(t.getMonth().toString(), 2) + "-" + leftPad(t.getDate().toString(), 2); }else{ return ''; } } return ( {displayTime(props.timestamp)} ); }; const FileSize = (props) => { function displaySize(bytes){ if(Number.isNaN(bytes) || bytes === undefined){ return ""; }else if(bytes < 1024){ return "("+bytes+'B'+")"; }else if(bytes < 1048576){ return "("+Math.round(bytes/1024*10)/10+'KB'+")"; }else if(bytes < 1073741824){ return "("+Math.round(bytes/(1024*1024)*10)/10+'MB'+")"; }else if(bytes < 1099511627776){ return "("+Math.round(bytes/(1024*1024*1024)*10)/10+'GB'+")"; }else{ return "("+Math.round(bytes/(1024*1024*1024*1024))+'TB'+")"; } } return ( {displaySize(props.size)} ); }; const Message = (props) => { return ( - {props.message} ); };