Skip to content

Commit

Permalink
Show file tree
Hide file tree
Showing 15 changed files with 1,731 additions and 12 deletions.
4 changes: 2 additions & 2 deletions flow-typed/npm/babel-plugin-styled-components_vx.x.x.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// flow-typed signature: 2474fc41c4018639b785e941176c4a70
// flow-typed version: <<STUB>>/babel-plugin-styled-components_v1.9.1/flow_v0.87.0
// flow-typed signature: 15770ed308a392cdbc88ab7b300e80a5
// flow-typed version: <<STUB>>/babel-plugin-styled-components_v1.9.2/flow_v0.87.0

/**
* This is an autogenerated libdef stub for:
Expand Down
39 changes: 39 additions & 0 deletions flow-typed/npm/react-input-files_vx.x.x.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// flow-typed signature: 5fa4b5a2cd25d41c13602f88c23a60a0
// flow-typed version: <<STUB>>/react-input-files_v^1.0.0/flow_v0.87.0

/**
* This is an autogenerated libdef stub for:
*
* 'react-input-files'
*
* Fill this stub out by replacing all the `any` types.
*
* Once filled out, we encourage you to share your work with the
* community by sending a pull request to:
* https://github.com/flowtype/flow-typed
*/

declare module 'react-input-files' {
declare module.exports: any;
}

/**
* We include stubs for each file inside this npm package in case you need to
* require those files directly. Feel free to delete any files that aren't
* needed.
*/
declare module 'react-input-files/lib/index' {
declare module.exports: any;
}

declare module 'react-input-files/lib/InputFiles' {
declare module.exports: any;
}

// Filename aliases
declare module 'react-input-files/lib/index.js' {
declare module.exports: $Exports<'react-input-files/lib/index'>;
}
declare module 'react-input-files/lib/InputFiles.js' {
declare module.exports: $Exports<'react-input-files/lib/InputFiles'>;
}
33 changes: 33 additions & 0 deletions flow-typed/npm/round-to_vx.x.x.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// flow-typed signature: a06aad5a31f4e384b859c53728026735
// flow-typed version: <<STUB>>/round-to_v^3.0.0/flow_v0.87.0

/**
* This is an autogenerated libdef stub for:
*
* 'round-to'
*
* Fill this stub out by replacing all the `any` types.
*
* Once filled out, we encourage you to share your work with the
* community by sending a pull request to:
* https://github.com/flowtype/flow-typed
*/

declare module 'round-to' {
declare module.exports: any;
}

/**
* We include stubs for each file inside this npm package in case you need to
* require those files directly. Feel free to delete any files that aren't
* needed.
*/


// Filename aliases
declare module 'round-to/index' {
declare module.exports: $Exports<'round-to'>;
}
declare module 'round-to/index.js' {
declare module.exports: $Exports<'round-to'>;
}
6 changes: 4 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,13 @@
"raf-throttle": "^2.0.3",
"ramda": "^0.26.1",
"react-dnd-dropzone": "^1.0.0",
"react-input-files": "^1.0.0",
"react-overlay-pack": "^3.0.6",
"react-spring": "^6.1.10",
"react-topbar-progress-indicator": "^2.0.0",
"react-virtualized": "^9.21.0",
"recompose": "^0.30.0"
"recompose": "^0.30.0",
"round-to": "^3.0.0"
},
"peerDependencies": {
"react": "^16.0.0",
Expand Down Expand Up @@ -193,7 +195,7 @@
"global": {
"branches": 71,
"functions": 81,
"lines": 89,
"lines": 88,
"statements": 86
}
}
Expand Down
88 changes: 88 additions & 0 deletions src/FileDropzone/FileDropzone.example.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
// @flow
import * as React from 'react';
import { storiesOf } from '@storybook/react';
import { action } from '@storybook/addon-actions';
import FileDropzone from '.';

class StatefulFileDropzone extends React.PureComponent<*, { file: ?File }> {
state = { file: null };

componentWillUnmount() {
if (this.timeoutID) clearTimeout(this.timeoutID);
}

onFileChange = (file, setFileUploading) => {
this.setState({ file });
action('onFileChange')(file);

setFileUploading(true);
this.timeoutID = setTimeout(() => {
setFileUploading(false);
}, 2000);
};

timeoutID: TimeoutID;

render() {
const { file } = this.state;
const { onFileChange } = this;

return <FileDropzone value={file} onFileChange={onFileChange} />;
}
}

storiesOf('FileDropzone', module)
.add(
'API',
() => <FileDropzone value={null} onFileChange={action('onFileChange')} />,
{
info: {
text: 'default',
inline: true,
},
},
)
.add(
'With custom text',
() => (
<FileDropzone
value={null}
onFileChange={action('onFileChange')}
placeholder="請拖拉檔案至此,或者您可以點擊"
browse="瀏覽"
dot="。"
accept="*"
/>
),
{
info: {
text: 'default',
inline: true,
},
},
)
.add(
'With file value',
() => (
<FileDropzone
// $FlowFixMe
value={{
name: 'text.txt',
size: 12000,
}}
onFileChange={action('onFileChange')}
/>
),
{
info: {
text: 'default',
inline: true,
},
},
)
.add('With state', () => <StatefulFileDropzone />, {
info: {
text: 'default',
inline: true,
},
});
122 changes: 122 additions & 0 deletions src/FileDropzone/FileDropzone.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
// @flow
import * as React from 'react';
import PropTypes from 'prop-types';
import { compose, pure } from 'recompose';
import Dropzone from 'react-dnd-dropzone';
import InputFiles from 'react-input-files';
import Spin from '../Spin';
import A from '../A';
import P from '../P';
import { IconLoading, IconDone, IconFileOutline } from '../Icons';
import withSubmittingState, {
type InjectedProps as WithSubmittingStateInjectedProps,
} from '../HoCs/withSubmittingState';
import { type SetSubmitting } from '../utils/type.flow';
import toMB from '../utils/toMB';
import {
Container,
FileNameDisplay,
FileLoadingIcon,
TextOverflow,
} from './styled-components';

export type Props = {
value: ?File,
onFileChange: (File, setSubmitting: SetSubmitting) => void | Promise<void>,
placeholder?: string,
browse?: string,
dot?: string,
accept?: string,
};
type InnerProps = Props & WithSubmittingStateInjectedProps;

export class PureFileDropzone extends React.PureComponent<InnerProps> {
static propTypes = {
value: PropTypes.shape({
name: PropTypes.string,
size: PropTypes.number,
}),
onFileChange: PropTypes.func.isRequired, // (File, setSubmitting: SetSubmitting) => void | Promise<void>
placeholder: PropTypes.string,
browse: PropTypes.string,
dot: PropTypes.string,
accept: PropTypes.string,
};

static defaultProps = {
placeholder: 'Drag & drop your file here or ',
browse: 'browse',
dot: '.',
accept: '*',
};

onFileChange = async ([file]: Array<File>) => {
const { onFileChange, setSubmitting } = this.props;
try {
await onFileChange(file, setSubmitting);
} catch (error) {
throw new Error(error);
}
};

render() {
const {
value,
isSubmitting,
placeholder,
browse,
dot,
accept,
} = this.props;
const { onFileChange } = this;

return (
<React.Fragment>
<Dropzone
onDrop={onFileChange}
render={({ canDrop }) => (
<Container canDrop={canDrop}>
<div>
{placeholder}
<InputFiles onChange={onFileChange} accept={accept}>
<A>{browse}</A>
</InputFiles>
{dot}
</div>
</Container>
)}
/>

{value && (
<FileNameDisplay>
<div>
<IconFileOutline width={18} height={18} />
<TextOverflow>{value.name}</TextOverflow>
</div>
<div>
<P color="grayDark">{toMB(value.size)}</P>
<FileLoadingIcon isLoading={isSubmitting}>
{isSubmitting ? (
<Spin>
<IconLoading width={18} height={18} />
</Spin>
) : (
<IconDone width={18} height={18} />
)}
</FileLoadingIcon>
</div>
</FileNameDisplay>
)}
</React.Fragment>
);
}
}

const enhance = compose(
pure,
withSubmittingState,
);
const FileDropzone: React.ComponentType<Props> = enhance(PureFileDropzone);
FileDropzone.displayName = 'FileDropzone';

export default FileDropzone;
Loading

0 comments on commit 0d98840

Please sign in to comment.