react-dropzone
Advanced tools
Comparing version 10.0.6 to 10.1.0
@@ -120,2 +120,23 @@ function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _nonIterableSpread(); } | ||
/** | ||
* If true, disables click to open the native file selection dialog | ||
*/ | ||
noClick: PropTypes.bool, | ||
/** | ||
* If true, disables SPACE/ENTER to open the native file selection dialog. | ||
* Note that it also stops tracking the focus state. | ||
*/ | ||
noKeyboard: PropTypes.bool, | ||
/** | ||
* If true, disables drag 'n' drop | ||
*/ | ||
noDrag: PropTypes.bool, | ||
/** | ||
* If true, stops drag event propagation to parents | ||
*/ | ||
noDragEventsBubbling: PropTypes.bool, | ||
/** | ||
* Minimum file size (in bytes) | ||
@@ -322,2 +343,7 @@ */ | ||
* @param {boolean} [props.preventDropOnDocument=true] If false, allow dropped items to take over the current browser window | ||
* @param {boolean} [props.noClick=false] If true, disables click to open the native file selection dialog | ||
* @param {boolean} [props.noKeyboard=false] If true, disables SPACE/ENTER to open the native file selection dialog. | ||
* Note that it also stops tracking the focus state. | ||
* @param {boolean} [props.noDrag=false] If true, disables drag 'n' drop | ||
* @param {boolean} [props.noDragEventsBubbling=false] If true, stops drag event propagation to parents | ||
* @param {number} [props.minSize=0] Minimum file size (in bytes) | ||
@@ -383,3 +409,11 @@ * @param {number} [props.maxSize=Infinity] Maximum file size (in bytes) | ||
_ref2$preventDropOnDo = _ref2.preventDropOnDocument, | ||
preventDropOnDocument = _ref2$preventDropOnDo === void 0 ? true : _ref2$preventDropOnDo; | ||
preventDropOnDocument = _ref2$preventDropOnDo === void 0 ? true : _ref2$preventDropOnDo, | ||
_ref2$noClick = _ref2.noClick, | ||
noClick = _ref2$noClick === void 0 ? false : _ref2$noClick, | ||
_ref2$noKeyboard = _ref2.noKeyboard, | ||
noKeyboard = _ref2$noKeyboard === void 0 ? false : _ref2$noKeyboard, | ||
_ref2$noDrag = _ref2.noDrag, | ||
noDrag = _ref2$noDrag === void 0 ? false : _ref2$noDrag, | ||
_ref2$noDragEventsBub = _ref2.noDragEventsBubbling, | ||
noDragEventsBubbling = _ref2$noDragEventsBub === void 0 ? false : _ref2$noDragEventsBub; | ||
@@ -461,5 +495,9 @@ var rootRef = useRef(null); | ||
var onClickCb = useCallback(function () { | ||
// In IE11/Edge the file-browser dialog is blocking, therefore, use setTimeout() | ||
if (noClick) { | ||
return; | ||
} // In IE11/Edge the file-browser dialog is blocking, therefore, use setTimeout() | ||
// to ensure React can handle state changes | ||
// See: https://github.com/react-dropzone/react-dropzone/issues/450 | ||
if (isIeOrEdge()) { | ||
@@ -470,3 +508,3 @@ setTimeout(openFileDialog, 0); | ||
} | ||
}, [inputRef]); | ||
}, [inputRef, noClick]); | ||
@@ -504,3 +542,4 @@ var _useState = useState([]), | ||
event.persist(); // Count the dropzone and any children that are entered. | ||
event.persist(); | ||
stopPropagation(event); // Count the dropzone and any children that are entered. | ||
@@ -513,3 +552,3 @@ if (dragTargets.indexOf(event.target) === -1) { | ||
Promise.resolve(getFilesFromEvent(event)).then(function (draggedFiles) { | ||
if (isPropagationStopped(event)) { | ||
if (isPropagationStopped(event) && !noDragEventsBubbling) { | ||
return; | ||
@@ -528,6 +567,7 @@ } | ||
} | ||
}, [dragTargets, getFilesFromEvent, onDragEnter]); | ||
}, [dragTargets, getFilesFromEvent, onDragEnter, noDragEventsBubbling]); | ||
var onDragOverCb = useCallback(function (event) { | ||
event.preventDefault(); | ||
event.persist(); | ||
stopPropagation(event); | ||
@@ -547,6 +587,7 @@ if (event.dataTransfer) { | ||
return false; | ||
}, [onDragOver]); | ||
}, [onDragOver, noDragEventsBubbling]); | ||
var onDragLeaveCb = useCallback(function (event) { | ||
event.preventDefault(); | ||
event.persist(); // Only deactivate once the dropzone and all children have been left | ||
event.persist(); | ||
stopPropagation(event); // Only deactivate once the dropzone and all children have been left | ||
@@ -571,3 +612,3 @@ var targets = _toConsumableArray(dragTargets.filter(function (target) { | ||
} | ||
}, [rootRef, dragTargets, onDragLeave]); | ||
}, [rootRef, dragTargets, onDragLeave, noDragEventsBubbling]); | ||
var onDropCb = useCallback(function (event) { | ||
@@ -577,2 +618,3 @@ event.preventDefault(); // Persist here because we need the event later after getFilesFromEvent() is done | ||
event.persist(); | ||
stopPropagation(event); | ||
setDragTargets([]); | ||
@@ -585,3 +627,3 @@ dispatch({ | ||
Promise.resolve(getFilesFromEvent(event)).then(function (files) { | ||
if (isPropagationStopped(event)) { | ||
if (isPropagationStopped(event) && !noDragEventsBubbling) { | ||
return; | ||
@@ -623,3 +665,3 @@ } | ||
} | ||
}, [multiple, accept, minSize, maxSize, getFilesFromEvent, onDrop, onDropAccepted, onDropRejected]); | ||
}, [multiple, accept, minSize, maxSize, getFilesFromEvent, onDrop, onDropAccepted, onDropRejected, noDragEventsBubbling]); | ||
@@ -630,6 +672,18 @@ var composeHandler = function composeHandler(fn) { | ||
var composeKeyboardHandler = function composeKeyboardHandler(fn) { | ||
return noKeyboard ? null : composeHandler(fn); | ||
}; | ||
var composeDragHandler = function composeDragHandler(fn) { | ||
return noDrag ? null : composeHandler(fn); | ||
}; | ||
var stopPropagation = function stopPropagation(event) { | ||
if (noDragEventsBubbling) { | ||
event.stopPropagation(); | ||
} | ||
}; | ||
var getRootProps = useMemo(function () { | ||
return function () { | ||
var _objectSpread2; | ||
var _ref3 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}, | ||
@@ -648,14 +702,16 @@ _ref3$refKey = _ref3.refKey, | ||
return _objectSpread((_objectSpread2 = { | ||
onKeyDown: composeHandler(composeEventHandlers(onKeyDown, onKeyDownCb)), | ||
onFocus: composeHandler(composeEventHandlers(onFocus, onFocusCb)), | ||
onBlur: composeHandler(composeEventHandlers(onBlur, onBlurCb)), | ||
return _objectSpread(_defineProperty({ | ||
onKeyDown: composeKeyboardHandler(composeEventHandlers(onKeyDown, onKeyDownCb)), | ||
onFocus: composeKeyboardHandler(composeEventHandlers(onFocus, onFocusCb)), | ||
onBlur: composeKeyboardHandler(composeEventHandlers(onBlur, onBlurCb)), | ||
onClick: composeHandler(composeEventHandlers(onClick, onClickCb)), | ||
onDragEnter: composeHandler(composeEventHandlers(onDragEnter, onDragEnterCb)), | ||
onDragOver: composeHandler(composeEventHandlers(onDragOver, onDragOverCb)), | ||
onDragLeave: composeHandler(composeEventHandlers(onDragLeave, onDragLeaveCb)), | ||
onDrop: composeHandler(composeEventHandlers(onDrop, onDropCb)) | ||
}, _defineProperty(_objectSpread2, refKey, rootRef), _defineProperty(_objectSpread2, "tabIndex", disabled ? -1 : 0), _objectSpread2), rest); | ||
onDragEnter: composeDragHandler(composeEventHandlers(onDragEnter, onDragEnterCb)), | ||
onDragOver: composeDragHandler(composeEventHandlers(onDragOver, onDragOverCb)), | ||
onDragLeave: composeDragHandler(composeEventHandlers(onDragLeave, onDragLeaveCb)), | ||
onDrop: composeDragHandler(composeEventHandlers(onDrop, onDropCb)) | ||
}, refKey, rootRef), !disabled && !noKeyboard ? { | ||
tabIndex: 0 | ||
} : {}, rest); | ||
}; | ||
}, [rootRef, onKeyDownCb, onFocusCb, onBlurCb, onClickCb, onDragEnterCb, onDragOverCb, onDragLeaveCb, onDropCb, disabled]); | ||
}, [rootRef, onKeyDownCb, onFocusCb, onBlurCb, onClickCb, onDragEnterCb, onDragOverCb, onDragLeaveCb, onDropCb, noKeyboard, noDrag, disabled]); | ||
var onInputElementClick = useCallback(function (event) { | ||
@@ -662,0 +718,0 @@ event.stopPropagation(); |
@@ -1,153 +0,3 @@ | ||
Custom event handlers provided in `getRootProps()` (e.g. `onClick`), will be invoked before the dropzone handlers. | ||
Therefore, if you'd like to prevent the default behaviour for: `onClick` and `onKeyDown` (open the file dialog), `onFocus` and `onBlur` (sets the `isFocused` state) and drag events; use the `stopPropagation()` fn on the event: | ||
If you'd like to prevent drag events propagation from the child to parent, you can use the `{noDragEventsBubbling}` property on the child: | ||
```jsx harmony | ||
import React, {useCallback, useReducer} from 'react'; | ||
import {useDropzone} from 'react-dropzone'; | ||
const initialEvtsState = { | ||
preventFocus: true, | ||
preventClick: true, | ||
preventKeyDown: true, | ||
preventDrag: true, | ||
files: [] | ||
}; | ||
function Events(props) { | ||
const [state, dispatch] = useReducer(reducer, initialEvtsState); | ||
const myRootProps = computeRootProps(state); | ||
const createToggleHandler = type => () => dispatch({type}); | ||
const onDrop = useCallback(files => dispatch({ | ||
type: 'setFiles', | ||
payload: files | ||
}), []); | ||
const {getRootProps, getInputProps, isFocused} = useDropzone({onDrop}); | ||
const files = state.files.map(file => <li key={file.path}>{file.path}</li>); | ||
const options = ['preventFocus', 'preventClick', 'preventKeyDown', 'preventDrag'].map(key => ( | ||
<div key={key}> | ||
<input | ||
id={key} | ||
type="checkbox" | ||
onChange={createToggleHandler(key)} | ||
checked={state[key]} | ||
/> | ||
<label htmlFor={key}> | ||
{key} | ||
</label> | ||
</div> | ||
)); | ||
return ( | ||
<section> | ||
<aside> | ||
{options} | ||
</aside> | ||
<div {...getRootProps(myRootProps)}> | ||
<input {...getInputProps()} /> | ||
<p>{getDesc(state)} (<em>{isFocused ? 'focused' : 'not focused'}</em>)</p> | ||
</div> | ||
<aside> | ||
<h4>Files</h4> | ||
<ul>{files}</ul> | ||
</aside> | ||
</section> | ||
); | ||
} | ||
function computeRootProps(state) { | ||
const props = {}; | ||
if (state.preventFocus) { | ||
Object.assign(props, { | ||
onFocus: event => event.stopPropagation(), | ||
onBlur: event => event.stopPropagation() | ||
}); | ||
} | ||
if (state.preventClick) { | ||
Object.assign(props, {onClick: event => event.stopPropagation()}); | ||
} | ||
if (state.preventKeyDown) { | ||
Object.assign(props, { | ||
onKeyDown: event => { | ||
if (event.keyCode === 32 || event.keyCode === 13) { | ||
event.stopPropagation(); | ||
} | ||
} | ||
}); | ||
} | ||
if (state.preventDrag) { | ||
['onDragEnter', 'onDragOver', 'onDragLeave', 'onDrop'].forEach(evtName => { | ||
Object.assign(props, { | ||
[evtName]: event => event.stopPropagation() | ||
}); | ||
}); | ||
} | ||
return props; | ||
} | ||
function getDesc(state) { | ||
if (state.preventClick && state.preventKeyDown && state.preventDrag) { | ||
return `Dropzone will not respond to any events`; | ||
} else if (state.preventClick && state.preventKeyDown) { | ||
return `Drag 'n' drop files here`; | ||
} else if (state.preventClick && state.preventDrag) { | ||
return `Press SPACE/ENTER to open the file dialog`; | ||
} else if (state.preventKeyDown && state.preventDrag) { | ||
return `Click to open the file dialog`; | ||
} else if (state.preventClick) { | ||
return `Drag 'n' drop files here or press SPACE/ENTER to open the file dialog`; | ||
} else if (state.preventKeyDown) { | ||
return `Drag 'n' drop files here or click to open the file dialog`; | ||
} else if (state.preventDrag) { | ||
return `Click/press SPACE/ENTER to open the file dialog`; | ||
} | ||
return `Drag 'n' drop files here or click/press SPACE/ENTER to open the file dialog`; | ||
} | ||
function reducer(state, action) { | ||
switch (action.type) { | ||
case 'preventFocus': | ||
return { | ||
...state, | ||
preventFocus: !state.preventFocus | ||
}; | ||
case 'preventClick': | ||
return { | ||
...state, | ||
preventClick: !state.preventClick | ||
}; | ||
case 'preventKeyDown': | ||
return { | ||
...state, | ||
preventKeyDown: !state.preventKeyDown | ||
}; | ||
case 'preventDrag': | ||
return { | ||
...state, | ||
preventDrag: !state.preventDrag | ||
}; | ||
case 'setFiles': | ||
return { | ||
...state, | ||
files: action.payload | ||
}; | ||
default: | ||
return state; | ||
} | ||
} | ||
<Events /> | ||
``` | ||
This sort of behavior can come in handy when you need to nest dropzone components and prevent any drag events from the child propagate to the parent: | ||
```jsx harmony | ||
import React, {useCallback, useMemo, useReducer} from 'react'; | ||
@@ -157,3 +7,2 @@ import {useDropzone} from 'react-dropzone'; | ||
const initialParentState = { | ||
preventDrag: true, | ||
parent: {}, | ||
@@ -183,32 +32,8 @@ child: {} | ||
const [state, dispatch] = useReducer(parentReducer, initialParentState); | ||
const {preventDrag} = state; | ||
const togglePreventDrag = useCallback(() => dispatch({type: 'preventDrag'}), []); | ||
const dropzoneProps = useMemo(() => computeDropzoneProps({dispatch}, 'parent'), [dispatch]); | ||
const {getRootProps} = useDropzone(dropzoneProps); | ||
const childProps = useMemo(() => ({dispatch}), [dispatch]); | ||
const childProps = useMemo(() => ({ | ||
preventDrag, | ||
dispatch | ||
}), [ | ||
preventDrag, | ||
dispatch | ||
]); | ||
return ( | ||
<section> | ||
<aside> | ||
<div> | ||
<input | ||
id="toggleDrag" | ||
type="checkbox" | ||
onChange={togglePreventDrag} | ||
checked={preventDrag} | ||
/> | ||
<label htmlFor="toggleDrag"> | ||
preventDrag | ||
</label> | ||
</div> | ||
</aside> | ||
<br /> | ||
<div {...getRootProps({style: parentStyle})}> | ||
@@ -226,4 +51,6 @@ <Child {...childProps} /> | ||
function Child(props) { | ||
const dropzoneProps = useMemo(() => computeDropzoneProps(props, 'child'), [ | ||
props.preventDrag, | ||
const dropzoneProps = useMemo(() => ({ | ||
...computeDropzoneProps(props, 'child'), | ||
noDragEventsBubbling: true | ||
}), [ | ||
props.dispatch | ||
@@ -244,7 +71,2 @@ ]); | ||
switch (action.type) { | ||
case 'preventDrag': | ||
return { | ||
...state, | ||
preventDrag: !state.preventDrag | ||
}; | ||
case 'onDragEnter': | ||
@@ -281,5 +103,2 @@ case 'onDragOver': | ||
const event = type === 'onDrop' ? args.pop() : args.shift(); | ||
if (props.preventDrag) { | ||
event.stopPropagation(); | ||
} | ||
props.dispatch({ | ||
@@ -300,2 +119,127 @@ type, | ||
Note that the sort of behavior illustrated above can lead to some confusion. For example, the `onDragLeave` callback of the parent will not be called if the callback for the same event is invoked on the child. This happens because we invoked `stopPropagation()` on the event from the child. | ||
Note that internally we use `event.stopPropagation()` to achieve the behavior illustrated above, but this comes with its own [drawbacks](https://javascript.info/bubbling-and-capturing#stopping-bubbling). | ||
If you'd like to selectively turn off the default dropzone behavior for `onClick`, `onKeyDown` (both open the file dialog), `onFocus` and `onBlur` (sets the `isFocused` state) and drag events; use the `{noClick}`, `{noKeyboard}` and `{noDrag}` properties: | ||
```jsx harmony | ||
import React, {useCallback, useReducer} from 'react'; | ||
import {useDropzone} from 'react-dropzone'; | ||
const initialEvtsState = { | ||
noClick: true, | ||
noKeyboard: true, | ||
noDrag: true, | ||
files: [] | ||
}; | ||
function Events(props) { | ||
const [state, dispatch] = useReducer(reducer, initialEvtsState); | ||
const createToggleHandler = type => () => dispatch({type}); | ||
const onDrop = useCallback(files => dispatch({ | ||
type: 'setFiles', | ||
payload: files | ||
}), []); | ||
const {getRootProps, getInputProps, isFocused} = useDropzone({...state, onDrop}); | ||
const files = state.files.map(file => <li key={file.path}>{file.path}</li>); | ||
const options = ['noClick', 'noKeyboard', 'noDrag'].map(key => ( | ||
<div key={key}> | ||
<input | ||
id={key} | ||
type="checkbox" | ||
onChange={createToggleHandler(key)} | ||
checked={state[key]} | ||
/> | ||
<label htmlFor={key}> | ||
{key} | ||
</label> | ||
</div> | ||
)); | ||
return ( | ||
<section> | ||
<aside> | ||
{options} | ||
</aside> | ||
<div {...getRootProps()}> | ||
<input {...getInputProps()} /> | ||
<p>{getDesc(state)} (<em>{isFocused ? 'focused' : 'not focused'}</em>)</p> | ||
</div> | ||
<aside> | ||
<h4>Files</h4> | ||
<ul>{files}</ul> | ||
</aside> | ||
</section> | ||
); | ||
} | ||
function getDesc(state) { | ||
if (state.noClick && state.noKeyboard && state.noDrag) { | ||
return `Dropzone will not respond to any events`; | ||
} else if (state.noClick && state.noKeyboard) { | ||
return `Drag 'n' drop files here`; | ||
} else if (state.noClick && state.noDrag) { | ||
return `Press SPACE/ENTER to open the file dialog`; | ||
} else if (state.noKeyboard && state.noDrag) { | ||
return `Click to open the file dialog`; | ||
} else if (state.noClick) { | ||
return `Drag 'n' drop files here or press SPACE/ENTER to open the file dialog`; | ||
} else if (state.noKeyboard) { | ||
return `Drag 'n' drop files here or click to open the file dialog`; | ||
} else if (state.noDrag) { | ||
return `Click/press SPACE/ENTER to open the file dialog`; | ||
} | ||
return `Drag 'n' drop files here or click/press SPACE/ENTER to open the file dialog`; | ||
} | ||
function reducer(state, action) { | ||
switch (action.type) { | ||
case 'noClick': | ||
return { | ||
...state, | ||
noClick: !state.noClick | ||
}; | ||
case 'noKeyboard': | ||
return { | ||
...state, | ||
noKeyboard: !state.noKeyboard | ||
}; | ||
case 'noDrag': | ||
return { | ||
...state, | ||
noDrag: !state.noDrag | ||
}; | ||
case 'setFiles': | ||
return { | ||
...state, | ||
files: action.payload | ||
}; | ||
default: | ||
return state; | ||
} | ||
} | ||
<Events /> | ||
``` | ||
Keep in mind that if you provide your own callback handlers as well and use `event.stopPropagation()`, it will prevent the default dropzone behavior: | ||
```jsx harmony | ||
import React from 'react'; | ||
import Dropzone from 'react-dropzone'; | ||
// Note that there will be nothing logged when files are dropped | ||
<Dropzone onDrop={files => console.log(files)}> | ||
{({getRootProps, getInputProps}) => ( | ||
<div | ||
{...getRootProps({ | ||
onDrop: event => event.stopPropagation() | ||
})} | ||
> | ||
<input {...getInputProps()} /> | ||
<p>Drag 'n' drop some files here, or click to select files</p> | ||
</div> | ||
)} | ||
</Dropzone> | ||
``` |
@@ -14,15 +14,10 @@ You can programmatically invoke the default OS file prompt; just use the `open` method returned by the hook. | ||
function Dropzone(props) { | ||
const {getRootProps, getInputProps, open} = useDropzone(); | ||
const rootProps = getRootProps({ | ||
const {getRootProps, getInputProps, open} = useDropzone({ | ||
// Disable click and keydown behavior | ||
onClick: event => event.stopPropagation(), | ||
onKeyDown: event => { | ||
if (event.keyCode === 32 || event.keyCode === 13) { | ||
event.stopPropagation(); | ||
} | ||
} | ||
noClick: true, | ||
noKeyboard: true | ||
}); | ||
return ( | ||
<div {...rootProps}> | ||
<div {...getRootProps()}> | ||
<input {...getInputProps()} /> | ||
@@ -47,17 +42,15 @@ <p>Drag 'n' drop some files here</p> | ||
const dropzoneRef = createRef(); | ||
const openDialog = () => { | ||
// Note that the ref is set async, | ||
// so it might be null at some point | ||
if (dropzoneRef.current) { | ||
dropzoneRef.current.open() | ||
} | ||
}; | ||
<Dropzone ref={dropzoneRef}> | ||
// Disable click and keydown behavior on the <Dropzone> | ||
<Dropzone ref={dropzoneRef} noClick noKeyboard> | ||
{({getRootProps, getInputProps}) => { | ||
const rootProps = getRootProps({ | ||
// Disable click and keydown behavior | ||
onClick: event => event.stopPropagation(), | ||
onKeyDown: event => { | ||
if (event.keyCode === 32 || event.keyCode === 13) { | ||
event.stopPropagation(); | ||
} | ||
} | ||
}); | ||
return ( | ||
<div {...rootProps}> | ||
<div {...getRootProps()}> | ||
<input {...getInputProps()} /> | ||
@@ -67,3 +60,3 @@ <p>Drag 'n' drop some files here</p> | ||
type="button" | ||
onClick={dropzoneRef.current ? dropzoneRef.current.open : null} | ||
onClick={openDialog} | ||
> | ||
@@ -70,0 +63,0 @@ Open File Dialog |
@@ -168,3 +168,3 @@ { | ||
}, | ||
"version": "10.0.6", | ||
"version": "10.1.0", | ||
"engines": { | ||
@@ -171,0 +171,0 @@ "node": ">= 8" |
@@ -55,2 +55,4 @@ ![react-dropzone logo](https://raw.githubusercontent.com/react-dropzone/react-dropzone/master/logo/logo.png) | ||
**IMPORTANT**: Under the hood, this lib makes use of [hooks](https://reactjs.org/docs/hooks-intro.html), therefore, using it requires React `>= 16.8`. | ||
Or the wrapper component for the hook: | ||
@@ -57,0 +59,0 @@ ```jsx static |
@@ -104,2 +104,23 @@ /* eslint prefer-template: 0 */ | ||
/** | ||
* If true, disables click to open the native file selection dialog | ||
*/ | ||
noClick: PropTypes.bool, | ||
/** | ||
* If true, disables SPACE/ENTER to open the native file selection dialog. | ||
* Note that it also stops tracking the focus state. | ||
*/ | ||
noKeyboard: PropTypes.bool, | ||
/** | ||
* If true, disables drag 'n' drop | ||
*/ | ||
noDrag: PropTypes.bool, | ||
/** | ||
* If true, stops drag event propagation to parents | ||
*/ | ||
noDragEventsBubbling: PropTypes.bool, | ||
/** | ||
* Minimum file size (in bytes) | ||
@@ -310,2 +331,7 @@ */ | ||
* @param {boolean} [props.preventDropOnDocument=true] If false, allow dropped items to take over the current browser window | ||
* @param {boolean} [props.noClick=false] If true, disables click to open the native file selection dialog | ||
* @param {boolean} [props.noKeyboard=false] If true, disables SPACE/ENTER to open the native file selection dialog. | ||
* Note that it also stops tracking the focus state. | ||
* @param {boolean} [props.noDrag=false] If true, disables drag 'n' drop | ||
* @param {boolean} [props.noDragEventsBubbling=false] If true, stops drag event propagation to parents | ||
* @param {number} [props.minSize=0] Minimum file size (in bytes) | ||
@@ -362,3 +388,7 @@ * @param {number} [props.maxSize=Infinity] Maximum file size (in bytes) | ||
onFileDialogCancel, | ||
preventDropOnDocument = true | ||
preventDropOnDocument = true, | ||
noClick = false, | ||
noKeyboard = false, | ||
noDrag = false, | ||
noDragEventsBubbling = false | ||
} = {}) { | ||
@@ -432,2 +462,6 @@ const rootRef = useRef(null) | ||
const onClickCb = useCallback(() => { | ||
if (noClick) { | ||
return | ||
} | ||
// In IE11/Edge the file-browser dialog is blocking, therefore, use setTimeout() | ||
@@ -441,3 +475,3 @@ // to ensure React can handle state changes | ||
} | ||
}, [inputRef]) | ||
}, [inputRef, noClick]) | ||
@@ -473,2 +507,3 @@ const [dragTargets, setDragTargets] = useState([]) | ||
event.persist() | ||
stopPropagation(event) | ||
@@ -482,3 +517,3 @@ // Count the dropzone and any children that are entered. | ||
Promise.resolve(getFilesFromEvent(event)).then(draggedFiles => { | ||
if (isPropagationStopped(event)) { | ||
if (isPropagationStopped(event) && !noDragEventsBubbling) { | ||
return | ||
@@ -498,3 +533,3 @@ } | ||
}, | ||
[dragTargets, getFilesFromEvent, onDragEnter] | ||
[dragTargets, getFilesFromEvent, onDragEnter, noDragEventsBubbling] | ||
) | ||
@@ -506,2 +541,3 @@ | ||
event.persist() | ||
stopPropagation(event) | ||
@@ -520,3 +556,3 @@ if (event.dataTransfer) { | ||
}, | ||
[onDragOver] | ||
[onDragOver, noDragEventsBubbling] | ||
) | ||
@@ -528,2 +564,3 @@ | ||
event.persist() | ||
stopPropagation(event) | ||
@@ -550,3 +587,3 @@ // Only deactivate once the dropzone and all children have been left | ||
}, | ||
[rootRef, dragTargets, onDragLeave] | ||
[rootRef, dragTargets, onDragLeave, noDragEventsBubbling] | ||
) | ||
@@ -559,2 +596,3 @@ | ||
event.persist() | ||
stopPropagation(event) | ||
@@ -566,3 +604,3 @@ setDragTargets([]) | ||
Promise.resolve(getFilesFromEvent(event)).then(files => { | ||
if (isPropagationStopped(event)) { | ||
if (isPropagationStopped(event) && !noDragEventsBubbling) { | ||
return | ||
@@ -606,3 +644,13 @@ } | ||
}, | ||
[multiple, accept, minSize, maxSize, getFilesFromEvent, onDrop, onDropAccepted, onDropRejected] | ||
[ | ||
multiple, | ||
accept, | ||
minSize, | ||
maxSize, | ||
getFilesFromEvent, | ||
onDrop, | ||
onDropAccepted, | ||
onDropRejected, | ||
noDragEventsBubbling | ||
] | ||
) | ||
@@ -614,2 +662,16 @@ | ||
const composeKeyboardHandler = fn => { | ||
return noKeyboard ? null : composeHandler(fn) | ||
} | ||
const composeDragHandler = fn => { | ||
return noDrag ? null : composeHandler(fn) | ||
} | ||
const stopPropagation = event => { | ||
if (noDragEventsBubbling) { | ||
event.stopPropagation() | ||
} | ||
} | ||
const getRootProps = useMemo( | ||
@@ -628,12 +690,12 @@ () => ({ | ||
} = {}) => ({ | ||
onKeyDown: composeHandler(composeEventHandlers(onKeyDown, onKeyDownCb)), | ||
onFocus: composeHandler(composeEventHandlers(onFocus, onFocusCb)), | ||
onBlur: composeHandler(composeEventHandlers(onBlur, onBlurCb)), | ||
onKeyDown: composeKeyboardHandler(composeEventHandlers(onKeyDown, onKeyDownCb)), | ||
onFocus: composeKeyboardHandler(composeEventHandlers(onFocus, onFocusCb)), | ||
onBlur: composeKeyboardHandler(composeEventHandlers(onBlur, onBlurCb)), | ||
onClick: composeHandler(composeEventHandlers(onClick, onClickCb)), | ||
onDragEnter: composeHandler(composeEventHandlers(onDragEnter, onDragEnterCb)), | ||
onDragOver: composeHandler(composeEventHandlers(onDragOver, onDragOverCb)), | ||
onDragLeave: composeHandler(composeEventHandlers(onDragLeave, onDragLeaveCb)), | ||
onDrop: composeHandler(composeEventHandlers(onDrop, onDropCb)), | ||
onDragEnter: composeDragHandler(composeEventHandlers(onDragEnter, onDragEnterCb)), | ||
onDragOver: composeDragHandler(composeEventHandlers(onDragOver, onDragOverCb)), | ||
onDragLeave: composeDragHandler(composeEventHandlers(onDragLeave, onDragLeaveCb)), | ||
onDrop: composeDragHandler(composeEventHandlers(onDrop, onDropCb)), | ||
[refKey]: rootRef, | ||
tabIndex: disabled ? -1 : 0, | ||
...(!disabled && !noKeyboard ? { tabIndex: 0 } : {}), | ||
...rest | ||
@@ -651,2 +713,4 @@ }), | ||
onDropCb, | ||
noKeyboard, | ||
noDrag, | ||
disabled | ||
@@ -653,0 +717,0 @@ ] |
@@ -15,2 +15,6 @@ import * as React from "react"; | ||
preventDropOnDocument?: boolean; | ||
noClick?: boolean; | ||
noKeyboard?: boolean; | ||
noDrag?: boolean; | ||
noDragEventsBubbling?: boolean; | ||
disabled?: boolean; | ||
@@ -17,0 +21,0 @@ onDrop?(acceptedFiles: File[], rejectedFiles: File[], event: DropEvent): void; |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
331925
4898
356