@uppy/dashboard
Advanced tools
Comparing version 3.4.0 to 3.4.1
# @uppy/dashboard | ||
## 3.4.1 | ||
Released: 2023-06-19 | ||
Included in: Uppy v3.10.0 | ||
- @uppy/companion,@uppy/core,@uppy/dashboard,@uppy/golden-retriever,@uppy/status-bar,@uppy/utils: Migrate all lodash' per-method-packages usage to lodash. (LinusMain / #4274) | ||
- @uppy/dashboard: include the old state when setting new (Artur Paikin / #4490) | ||
## 3.4.0 | ||
@@ -4,0 +12,0 @@ |
let _Symbol$for; | ||
import { h, Component, Fragment } from 'preact'; | ||
_Symbol$for = Symbol.for('uppy test: disable unused locale key warning'); | ||
class AddFiles extends Component { | ||
constructor() { | ||
super(...arguments); | ||
this.triggerFileInputClick = () => { | ||
this.fileInput.click(); | ||
}; | ||
this.triggerFolderInputClick = () => { | ||
this.folderInput.click(); | ||
}; | ||
this.triggerVideoCameraInputClick = () => { | ||
this.mobileVideoFileInput.click(); | ||
}; | ||
this.triggerPhotoCameraInputClick = () => { | ||
this.mobilePhotoFileInput.click(); | ||
}; | ||
this.onFileInputChange = event => { | ||
this.props.handleInputChange(event); | ||
this.onFileInputChange = event => { | ||
this.props.handleInputChange(event); // We clear the input after a file is selected, because otherwise | ||
// We clear the input after a file is selected, because otherwise | ||
// change event is not fired in Chrome and Safari when a file | ||
@@ -33,6 +28,4 @@ // with the same name is selected. | ||
// Chrome will not trigger change if we drop the same file twice (Issue #768). | ||
event.target.value = null; // eslint-disable-line no-param-reassign | ||
}; | ||
this.renderHiddenInput = (isFolder, refCallback) => { | ||
@@ -53,3 +46,2 @@ return h("input", { | ||
}; | ||
this.renderHiddenCameraInput = (type, nativeCameraFacingMode, refCallback) => { | ||
@@ -74,3 +66,2 @@ const typeToAccept = { | ||
}; | ||
this.renderMyDeviceAcquirer = () => { | ||
@@ -105,3 +96,2 @@ return h("div", { | ||
}; | ||
this.renderPhotoCamera = () => { | ||
@@ -135,3 +125,2 @@ return h("div", { | ||
}; | ||
this.renderVideoCamera = () => { | ||
@@ -164,3 +153,2 @@ return h("div", { | ||
}; | ||
this.renderBrowseButton = (text, onClickFn) => { | ||
@@ -175,8 +163,8 @@ const numberOfAcquirers = this.props.acquirers.length; | ||
}; | ||
this.renderDropPasteBrowseTagline = numberOfAcquirers => { | ||
const browseFiles = this.renderBrowseButton(this.props.i18n('browseFiles'), this.triggerFileInputClick); | ||
const browseFolders = this.renderBrowseButton(this.props.i18n('browseFolders'), this.triggerFolderInputClick); // in order to keep the i18n CamelCase and options lower (as are defaults) we will want to transform a lower | ||
const browseFolders = this.renderBrowseButton(this.props.i18n('browseFolders'), this.triggerFolderInputClick); | ||
// in order to keep the i18n CamelCase and options lower (as are defaults) we will want to transform a lower | ||
// to Camel | ||
const lowerFMSelectionType = this.props.fileManagerSelectionType; | ||
@@ -186,3 +174,4 @@ const camelFMSelectionType = lowerFMSelectionType.charAt(0).toUpperCase() + lowerFMSelectionType.slice(1); | ||
class: "uppy-Dashboard-AddFiles-title" | ||
}, // eslint-disable-next-line no-nested-ternary | ||
}, | ||
// eslint-disable-next-line no-nested-ternary | ||
this.props.disableLocalFiles ? this.props.i18n('importFiles') : numberOfAcquirers > 0 ? this.props.i18nArray(`dropPasteImport${camelFMSelectionType}`, { | ||
@@ -198,3 +187,2 @@ browseFiles, | ||
}; | ||
this.renderAcquirer = acquirer => { | ||
@@ -221,3 +209,2 @@ return h("div", { | ||
}; | ||
this.renderAcquirers = acquirers => { | ||
@@ -235,3 +222,2 @@ // Group last two buttons, so we don’t end up with | ||
}; | ||
this.renderSourcesList = (acquirers, disableLocalFiles) => { | ||
@@ -244,3 +230,2 @@ const { | ||
const myDeviceKey = 'myDevice'; | ||
if (!disableLocalFiles) { | ||
@@ -260,15 +245,15 @@ list.push({ | ||
} | ||
list.push(...acquirers.map(acquirer => ({ | ||
key: acquirer.id, | ||
elements: this.renderAcquirer(acquirer) | ||
}))); // doesn't make sense to show only a lonely "My Device" | ||
}))); | ||
// doesn't make sense to show only a lonely "My Device" | ||
const hasOnlyMyDevice = list.length === 1 && list[0].key === myDeviceKey; | ||
if (hasOnlyMyDevice) list = []; // Group last two buttons, so we don’t end up with | ||
if (hasOnlyMyDevice) list = []; | ||
// Group last two buttons, so we don’t end up with | ||
// just one button on a new line | ||
const listWithoutLastTwo = [...list]; | ||
const lastTwo = listWithoutLastTwo.splice(list.length - 2, list.length); | ||
const renderList = l => l.map(_ref => { | ||
@@ -283,3 +268,2 @@ let { | ||
}); | ||
return h(Fragment, null, this.renderDropPasteBrowseTagline(list.length), h("div", { | ||
@@ -296,3 +280,2 @@ className: "uppy-Dashboard-AddFiles-list", | ||
} | ||
[_Symbol$for]() { | ||
@@ -307,3 +290,2 @@ // Those are actually used in `renderDropPasteBrowseTagline` method. | ||
} | ||
renderPoweredByUppy() { | ||
@@ -337,3 +319,2 @@ const { | ||
} | ||
render() { | ||
@@ -361,5 +342,3 @@ const { | ||
} | ||
} | ||
export default AddFiles; |
import { h } from 'preact'; | ||
import classNames from 'classnames'; | ||
import AddFiles from "./AddFiles.js"; | ||
const AddFilesPanel = props => { | ||
@@ -22,3 +21,2 @@ return h("div", { | ||
}; | ||
export default AddFilesPanel; |
function _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); } | ||
import { h } from 'preact'; | ||
@@ -13,3 +12,5 @@ import classNames from 'classnames'; | ||
import FileCard from "./FileCard/index.js"; | ||
import Slide from "./Slide.js"; // http://dev.edenspiekermann.com/2016/02/11/introducing-accessible-modal-dialog | ||
import Slide from "./Slide.js"; | ||
// http://dev.edenspiekermann.com/2016/02/11/introducing-accessible-modal-dialog | ||
// https://github.com/ghosh/micromodal | ||
@@ -20,3 +21,4 @@ | ||
const WIDTH_MD = 576; | ||
const HEIGHT_MD = 330; // We might want to enable this in the future | ||
const HEIGHT_MD = 330; | ||
// We might want to enable this in the future | ||
// const HEIGHT_LG = 400 | ||
@@ -48,6 +50,6 @@ // const HEIGHT_XL = 460 | ||
'uppy-Dashboard--singleFile': props.singleFileFullScreen && isSingleFile && isSizeHeightMD | ||
}); // Important: keep these in sync with the percent width values in `src/components/FileItem/index.scss`. | ||
}); | ||
// Important: keep these in sync with the percent width values in `src/components/FileItem/index.scss`. | ||
let itemsPerRow = 1; // mobile | ||
if (props.containerWidth > WIDTH_XL) { | ||
@@ -60,7 +62,5 @@ itemsPerRow = 5; | ||
} | ||
const showFileList = props.showSelectedFiles && !isNoFiles; | ||
const numberOfFilesForRecovery = props.recoveredState ? Object.keys(props.recoveredState.files).length : null; | ||
const numberOfGhosts = props.files ? Object.keys(props.files).filter(fileID => props.files[fileID].isGhost).length : null; | ||
const renderRestoredText = () => { | ||
@@ -72,6 +72,4 @@ if (numberOfGhosts > 0) { | ||
} | ||
return props.i18n('recoveredAllFiles'); | ||
}; | ||
const dashboard = h("div", { | ||
@@ -167,3 +165,4 @@ className: dashboardClassName, | ||
itemsPerRow: itemsPerRow | ||
}) : // eslint-disable-next-line react/jsx-props-no-spreading | ||
}) : | ||
// eslint-disable-next-line react/jsx-props-no-spreading | ||
h(AddFiles, _extends({}, props, { | ||
@@ -170,0 +169,0 @@ isSizeMD: isSizeMD |
import { h } from 'preact'; | ||
import classNames from 'classnames'; | ||
function EditorPanel(props) { | ||
@@ -35,3 +34,2 @@ const file = props.files[props.fileCardFor]; | ||
} | ||
export default EditorPanel; |
@@ -11,3 +11,2 @@ import { h } from 'preact'; | ||
var _getMetaFields; | ||
const { | ||
@@ -27,7 +26,5 @@ uppy, | ||
} = props; | ||
const getMetaFields = () => { | ||
return typeof metaFields === 'function' ? metaFields(files[fileCardFor]) : metaFields; | ||
}; | ||
const file = files[fileCardFor]; | ||
@@ -39,3 +36,2 @@ const computedMetaFields = (_getMetaFields = getMetaFields()) != null ? _getMetaFields : []; | ||
var _file$meta$field$id; | ||
storedMetaData[field.id] = (_file$meta$field$id = file.meta[field.id]) != null ? _file$meta$field$id : ''; | ||
@@ -48,9 +44,8 @@ }); | ||
}, [saveFileCard, formState, fileCardFor]); | ||
const updateMeta = (newVal, name) => { | ||
setFormState({ | ||
...formState, | ||
[name]: newVal | ||
}); | ||
}; | ||
const handleCancel = () => { | ||
@@ -60,3 +55,2 @@ uppy.emit('file-editor:cancel', file); | ||
}; | ||
const [form] = useState(() => { | ||
@@ -130,3 +124,4 @@ const formEl = document.createElement('form'); | ||
}, h("button", { | ||
className: "uppy-u-reset uppy-c-btn uppy-c-btn-primary uppy-Dashboard-FileCard-actionsBtn" // If `form` attribute is supported, we want a submit button to trigger the form validation. | ||
className: "uppy-u-reset uppy-c-btn uppy-c-btn-primary uppy-Dashboard-FileCard-actionsBtn" | ||
// If `form` attribute is supported, we want a submit button to trigger the form validation. | ||
// Otherwise, fallback to a classic button with a onClick event handler. | ||
@@ -133,0 +128,0 @@ , |
import { h } from 'preact'; | ||
import copyToClipboard from '../../../utils/copyToClipboard.js'; | ||
function EditButton(_ref) { | ||
@@ -13,3 +12,2 @@ let { | ||
} = _ref; | ||
if (!uploadInProgressOrComplete && metaFields && metaFields.length > 0 || !uploadInProgressOrComplete && canEditFile(file)) { | ||
@@ -49,6 +47,4 @@ return h("button", { | ||
} | ||
return null; | ||
} | ||
function RemoveButton(_ref2) { | ||
@@ -84,3 +80,2 @@ let { | ||
} | ||
const copyLinkToClipboard = (event, props) => { | ||
@@ -90,3 +85,4 @@ copyToClipboard(props.file.uploadURL, props.i18n('copyLinkToClipboardFallback')).then(() => { | ||
props.uppy.info(props.i18n('copyLinkToClipboardSuccess'), 'info', 3000); | ||
}).catch(props.uppy.log) // avoid losing focus | ||
}).catch(props.uppy.log) | ||
// avoid losing focus | ||
.then(() => event.target.focus({ | ||
@@ -96,3 +92,2 @@ preventScroll: true | ||
}; | ||
function CopyLinkButton(props) { | ||
@@ -119,3 +114,2 @@ const { | ||
} | ||
export default function Buttons(props) { | ||
@@ -134,3 +128,2 @@ const { | ||
} = props; | ||
const editAction = () => { | ||
@@ -143,3 +136,2 @@ if (metaFields && metaFields.length > 0) { | ||
}; | ||
return h("div", { | ||
@@ -146,0 +138,0 @@ className: "uppy-Dashboard-Item-actionWrapper" |
@@ -5,3 +5,2 @@ import { h, Fragment } from 'preact'; | ||
import MetaErrorMessage from "../MetaErrorMessage.js"; | ||
const renderFileName = props => { | ||
@@ -12,3 +11,2 @@ const { | ||
} = props.file.meta; | ||
function getMaxNameLength() { | ||
@@ -18,17 +16,13 @@ if (props.isSingleFile && props.containerHeight >= 350) { | ||
} | ||
if (props.containerWidth <= 352) { | ||
return 35; | ||
} | ||
if (props.containerWidth <= 576) { | ||
return 60; | ||
} // When `author` is present, we want to make sure | ||
} | ||
// When `author` is present, we want to make sure | ||
// the file name fits on one line so we can place | ||
// the author on the second line. | ||
return author ? 20 : 30; | ||
} | ||
return h("div", { | ||
@@ -39,3 +33,2 @@ className: "uppy-Dashboard-Item-name", | ||
}; | ||
const renderAuthor = props => { | ||
@@ -49,7 +42,5 @@ const { | ||
const dot = `\u00B7`; | ||
if (!author) { | ||
return null; | ||
} | ||
return h("div", { | ||
@@ -63,7 +54,5 @@ className: "uppy-Dashboard-Item-author" | ||
}; | ||
const renderFileSize = props => props.file.size && h("div", { | ||
className: "uppy-Dashboard-Item-statusSize" | ||
}, prettierBytes(props.file.size)); | ||
const ReSelectButton = props => props.file.isGhost && h("span", null, ' \u2022 ', h("button", { | ||
@@ -74,3 +63,2 @@ className: "uppy-u-reset uppy-c-btn uppy-Dashboard-Item-reSelect", | ||
}, props.i18n('reSelect'))); | ||
const ErrorButton = _ref => { | ||
@@ -81,3 +69,2 @@ let { | ||
} = _ref; | ||
if (file.error) { | ||
@@ -93,6 +80,4 @@ return h("button", { | ||
} | ||
return null; | ||
}; | ||
export default function FileInfo(props) { | ||
@@ -108,6 +93,6 @@ const { | ||
}, renderFileName(props), h(ErrorButton, { | ||
file: props.file // eslint-disable-next-line no-alert | ||
file: props.file | ||
// eslint-disable-next-line no-alert | ||
, | ||
onClick: () => alert(props.file.error) // TODO: move to a custom alert implementation | ||
})), h("div", { | ||
@@ -114,0 +99,0 @@ className: "uppy-Dashboard-Item-status" |
import { h } from 'preact'; | ||
function onPauseResumeCancelRetry(props) { | ||
if (props.isUploaded) return; | ||
if (props.error && !props.hideRetryButton) { | ||
@@ -10,3 +8,2 @@ props.uppy.retryUpload(props.file.id); | ||
} | ||
if (props.resumableUploads && !props.hidePauseResumeButton) { | ||
@@ -18,3 +15,2 @@ props.uppy.pauseResume(props.file.id); | ||
} | ||
function progressIndicatorTitle(props) { | ||
@@ -24,7 +20,5 @@ if (props.isUploaded) { | ||
} | ||
if (props.error) { | ||
return props.i18n('retryUpload'); | ||
} | ||
if (props.resumableUploads) { | ||
@@ -34,13 +28,9 @@ if (props.file.isPaused) { | ||
} | ||
return props.i18n('pauseUpload'); | ||
} | ||
if (props.individualCancellation) { | ||
return props.i18n('cancelUpload'); | ||
} | ||
return ''; | ||
} | ||
function ProgressIndicatorButton(props) { | ||
@@ -57,3 +47,2 @@ return h("div", { | ||
} | ||
function ProgressCircleContainer(_ref) { | ||
@@ -72,3 +61,2 @@ let { | ||
} | ||
function ProgressCircle(_ref2) { | ||
@@ -99,3 +87,2 @@ let { | ||
} | ||
export default function FileProgress(props) { | ||
@@ -105,5 +92,5 @@ // Nothing if upload has not started | ||
return null; | ||
} // Green checkmark when complete | ||
} | ||
// Green checkmark when complete | ||
if (props.isUploaded) { | ||
@@ -125,10 +112,10 @@ return h("div", { | ||
} | ||
if (props.recoveredState) { | ||
return undefined; | ||
} // Retry button for error | ||
} | ||
// Retry button for error | ||
if (props.error && !props.hideRetryButton) { | ||
return (// eslint-disable-next-line react/jsx-props-no-spreading | ||
return ( | ||
// eslint-disable-next-line react/jsx-props-no-spreading | ||
h(ProgressIndicatorButton, props, h("svg", { | ||
@@ -151,7 +138,8 @@ "aria-hidden": "true", | ||
); | ||
} // Pause/resume button for resumable uploads | ||
} | ||
// Pause/resume button for resumable uploads | ||
if (props.resumableUploads && !props.hidePauseResumeButton) { | ||
return (// eslint-disable-next-line react/jsx-props-no-spreading | ||
return ( | ||
// eslint-disable-next-line react/jsx-props-no-spreading | ||
h(ProgressIndicatorButton, props, h(ProgressCircleContainer, null, h(ProgressCircle, { | ||
@@ -180,7 +168,8 @@ progress: props.file.progress.percentage | ||
); | ||
} // Cancel button for non-resumable uploads if individualCancellation is supported (not bundled) | ||
} | ||
// Cancel button for non-resumable uploads if individualCancellation is supported (not bundled) | ||
if (!props.resumableUploads && props.individualCancellation && !props.hideCancelButton) { | ||
return (// eslint-disable-next-line react/jsx-props-no-spreading | ||
return ( | ||
// eslint-disable-next-line react/jsx-props-no-spreading | ||
h(ProgressIndicatorButton, props, h(ProgressCircleContainer, null, h(ProgressCircle, { | ||
@@ -194,5 +183,5 @@ progress: props.file.progress.percentage | ||
); | ||
} // Just progress when buttons are disabled | ||
} | ||
// Just progress when buttons are disabled | ||
return h("div", { | ||
@@ -199,0 +188,0 @@ className: "uppy-Dashboard-Item-progress" |
@@ -13,3 +13,2 @@ import { h, Component } from 'preact'; | ||
} = this.props; | ||
if (!file.preview) { | ||
@@ -19,9 +18,8 @@ this.props.handleRequestThumbnail(file); | ||
} | ||
shouldComponentUpdate(nextProps) { | ||
return !shallowEqual(this.props, nextProps); | ||
} // VirtualList mounts FileItems again and they emit `thumbnail:request` | ||
} | ||
// VirtualList mounts FileItems again and they emit `thumbnail:request` | ||
// Otherwise thumbnails are broken or missing after Golden Retriever restores files | ||
componentDidUpdate() { | ||
@@ -31,3 +29,2 @@ const { | ||
} = this.props; | ||
if (!file.preview) { | ||
@@ -37,3 +34,2 @@ this.props.handleRequestThumbnail(file); | ||
} | ||
componentWillUnmount() { | ||
@@ -43,3 +39,2 @@ const { | ||
} = this.props; | ||
if (!file.preview) { | ||
@@ -49,3 +44,2 @@ this.props.handleCancelThumbnail(file); | ||
} | ||
render() { | ||
@@ -59,5 +53,6 @@ const { | ||
const uploadInProgress = file.progress.uploadStarted && !file.progress.uploadComplete || isProcessing; | ||
const error = file.error || false; // File that Golden Retriever was able to partly restore (only meta, not blob), | ||
const error = file.error || false; | ||
// File that Golden Retriever was able to partly restore (only meta, not blob), | ||
// users still need to re-add it, so it’s a ghost | ||
const { | ||
@@ -67,7 +62,5 @@ isGhost | ||
let showRemoveButton = this.props.individualCancellation ? !isUploaded : !uploadInProgress && !isUploaded; | ||
if (isUploaded && this.props.showRemoveButtonAfterComplete) { | ||
showRemoveButton = true; | ||
} | ||
const dashboardItemClass = classNames({ | ||
@@ -134,3 +127,2 @@ 'uppy-Dashboard-Item': true, | ||
} | ||
} |
import { h } from 'preact'; | ||
const metaFieldIdToName = (metaFieldId, metaFields) => { | ||
@@ -8,3 +7,2 @@ const fields = typeof metaFields === 'function' ? metaFields() : metaFields; | ||
}; | ||
export default function renderMissingMetaFieldsError(props) { | ||
@@ -20,7 +18,5 @@ const { | ||
} = file; | ||
if (!(missingRequiredMetaFields != null && missingRequiredMetaFields.length)) { | ||
return null; | ||
} | ||
const metaFieldsString = missingRequiredMetaFields.map(missingMetaField => metaFieldIdToName(missingMetaField, metaFields)).join(', '); | ||
@@ -27,0 +23,0 @@ return h("div", { |
@@ -5,3 +5,2 @@ import { h } from 'preact'; | ||
import VirtualList from "./VirtualList.js"; | ||
function chunks(list, size) { | ||
@@ -21,3 +20,2 @@ const chunked = []; | ||
} | ||
export default (_ref => { | ||
@@ -54,9 +52,11 @@ let { | ||
// It's ESPECIALLY not great that this is checking against `itemsPerRow`! | ||
const rowHeight = itemsPerRow === 1 // Mobile | ||
? 71 // 190px height + 2 * 5px margin | ||
: 200; // Sort files by file.isGhost, ghost files first, only if recoveredState is present | ||
const rowHeight = itemsPerRow === 1 | ||
// Mobile | ||
? 71 | ||
// 190px height + 2 * 5px margin | ||
: 200; | ||
// Sort files by file.isGhost, ghost files first, only if recoveredState is present | ||
const rows = useMemo(() => { | ||
const sortByGhostComesFirst = (file1, file2) => files[file2].isGhost - files[file1].isGhost; | ||
const fileIds = Object.keys(files); | ||
@@ -66,4 +66,4 @@ if (recoveredState) fileIds.sort(sortByGhostComesFirst); | ||
}, [files, itemsPerRow, recoveredState]); | ||
const renderRow = row => // The `role="presentation` attribute ensures that the list items are properly | ||
const renderRow = row => | ||
// The `role="presentation` attribute ensures that the list items are properly | ||
// associated with the `VirtualList` element. | ||
@@ -77,12 +77,16 @@ // We use the first file ID as the key—this should not change across scroll rerenders | ||
key: fileID, | ||
uppy: uppy // FIXME This is confusing, it's actually the Dashboard's plugin ID | ||
uppy: uppy | ||
// FIXME This is confusing, it's actually the Dashboard's plugin ID | ||
, | ||
id: id, | ||
error: error // TODO move this to context | ||
error: error | ||
// TODO move this to context | ||
, | ||
i18n: i18n // features | ||
i18n: i18n | ||
// features | ||
, | ||
acquirers: acquirers, | ||
resumableUploads: resumableUploads, | ||
individualCancellation: individualCancellation // visual options | ||
individualCancellation: individualCancellation | ||
// visual options | ||
, | ||
@@ -99,3 +103,4 @@ hideRetryButton: hideRetryButton, | ||
containerWidth: containerWidth, | ||
containerHeight: containerHeight // callbacks | ||
containerHeight: containerHeight | ||
// callbacks | ||
, | ||
@@ -111,3 +116,2 @@ toggleFileCard: toggleFileCard, | ||
}))); | ||
if (isSingleFile) { | ||
@@ -118,3 +122,2 @@ return h("div", { | ||
} | ||
return h(VirtualList, { | ||
@@ -121,0 +124,0 @@ class: "uppy-Dashboard-files", |
@@ -7,3 +7,2 @@ import { h } from 'preact'; | ||
} = props; | ||
if (file.preview) { | ||
@@ -16,3 +15,2 @@ return h("img", { | ||
} | ||
const { | ||
@@ -19,0 +17,0 @@ color, |
import { h } from 'preact'; | ||
import classNames from 'classnames'; | ||
import ignoreEvent from '../utils/ignoreEvent.js'; | ||
function PickerPanelContent(_ref) { | ||
@@ -39,3 +38,2 @@ let { | ||
} | ||
export default PickerPanelContent; |
@@ -11,3 +11,2 @@ import { h } from 'preact'; | ||
}; | ||
function getUploadingState(isAllErrored, isAllComplete, isAllPaused, files) { | ||
@@ -17,35 +16,28 @@ if (files === void 0) { | ||
} | ||
if (isAllErrored) { | ||
return uploadStates.STATE_ERROR; | ||
} | ||
if (isAllComplete) { | ||
return uploadStates.STATE_COMPLETE; | ||
} | ||
if (isAllPaused) { | ||
return uploadStates.STATE_PAUSED; | ||
} | ||
let state = uploadStates.STATE_WAITING; | ||
const fileIDs = Object.keys(files); | ||
for (let i = 0; i < fileIDs.length; i++) { | ||
const { | ||
progress | ||
} = files[fileIDs[i]]; // If ANY files are being uploaded right now, show the uploading state. | ||
} = files[fileIDs[i]]; | ||
// If ANY files are being uploaded right now, show the uploading state. | ||
if (progress.uploadStarted && !progress.uploadComplete) { | ||
return uploadStates.STATE_UPLOADING; | ||
} // If files are being preprocessed AND postprocessed at this time, we show the | ||
} | ||
// If files are being preprocessed AND postprocessed at this time, we show the | ||
// preprocess state. If any files are being uploaded we show uploading. | ||
if (progress.preprocess && state !== uploadStates.STATE_UPLOADING) { | ||
state = uploadStates.STATE_PREPROCESSING; | ||
} // If NO files are being preprocessed or uploaded right now, but some files are | ||
} | ||
// If NO files are being preprocessed or uploaded right now, but some files are | ||
// being postprocessed, show the postprocess state. | ||
if (progress.postprocess && state !== uploadStates.STATE_UPLOADING && state !== uploadStates.STATE_PREPROCESSING) { | ||
@@ -55,6 +47,4 @@ state = uploadStates.STATE_POSTPROCESSING; | ||
} | ||
return state; | ||
} | ||
function UploadStatus(_ref) { | ||
@@ -72,3 +62,2 @@ let { | ||
const uploadingState = getUploadingState(isAllErrored, isAllComplete, isAllPaused, files); | ||
switch (uploadingState) { | ||
@@ -79,3 +68,2 @@ case 'uploading': | ||
}); | ||
case 'preprocessing': | ||
@@ -86,6 +74,4 @@ case 'postprocessing': | ||
}); | ||
case 'paused': | ||
return i18n('uploadPaused'); | ||
case 'waiting': | ||
@@ -95,13 +81,9 @@ return i18n('xFilesSelected', { | ||
}); | ||
case 'complete': | ||
return i18n('uploadComplete'); | ||
case 'error': | ||
return i18n('error'); | ||
default: | ||
} | ||
} | ||
function PanelTopBar(props) { | ||
@@ -118,4 +100,4 @@ const { | ||
allowNewUpload | ||
} = props; // TODO maybe this should be done in ../Dashboard.jsx, then just pass that down as `allowNewUpload` | ||
} = props; | ||
// TODO maybe this should be done in ../Dashboard.jsx, then just pass that down as `allowNewUpload` | ||
if (allowNewUpload && maxNumberOfFiles) { | ||
@@ -125,3 +107,2 @@ // eslint-disable-next-line react/destructuring-assignment | ||
} | ||
return h("div", { | ||
@@ -156,3 +137,2 @@ className: "uppy-DashboardContent-bar" | ||
} | ||
export default PanelTopBar; |
@@ -5,2 +5,3 @@ import { cloneElement, Component, toChildArray } from 'preact'; | ||
const duration = 250; | ||
/** | ||
@@ -15,3 +16,2 @@ * Vertical slide transition. | ||
*/ | ||
class Slide extends Component { | ||
@@ -24,6 +24,6 @@ constructor(props) { | ||
}; | ||
} // TODO: refactor to stable lifecycle method | ||
} | ||
// TODO: refactor to stable lifecycle method | ||
// eslint-disable-next-line | ||
componentWillUpdate(nextProps) { | ||
@@ -37,4 +37,5 @@ const { | ||
cachedChildren: child | ||
}; // Enter transition | ||
}; | ||
// Enter transition | ||
if (child && !cachedChildren) { | ||
@@ -48,2 +49,3 @@ patch.className = `${transitionName}-enter`; | ||
// this.base.getBoundingClientRect() | ||
this.setState({ | ||
@@ -58,5 +60,5 @@ className: `${transitionName}-enter ${transitionName}-enter-active` | ||
}); | ||
} // Leave transition | ||
} | ||
// Leave transition | ||
if (cachedChildren && !child && this.leaveTimeout === undefined) { | ||
@@ -79,8 +81,7 @@ patch.cachedChildren = cachedChildren; | ||
}); | ||
} // eslint-disable-next-line | ||
} | ||
// eslint-disable-next-line | ||
this.setState(patch); | ||
} | ||
render() { | ||
@@ -91,7 +92,5 @@ const { | ||
} = this.state; | ||
if (!cachedChildren) { | ||
return null; | ||
} | ||
return cloneElement(cachedChildren, { | ||
@@ -101,5 +100,3 @@ className: classNames(className, cachedChildren.props.className) | ||
} | ||
} | ||
export default Slide; |
function _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); } | ||
/** | ||
@@ -30,2 +29,3 @@ * Adapted from preact-virtual-list: https://github.com/developit/preact-virtual-list | ||
*/ | ||
import { h, Component } from 'preact'; | ||
@@ -53,8 +53,8 @@ const STYLE_INNER = { | ||
}; | ||
class VirtualList extends Component { | ||
constructor(props) { | ||
super(props); // The currently focused node, used to retain focus when the visible rows change. | ||
super(props); | ||
// The currently focused node, used to retain focus when the visible rows change. | ||
// To avoid update loops, this should not cause state updates, so it's kept as a plain property. | ||
this.handleScroll = () => { | ||
@@ -65,7 +65,5 @@ this.setState({ | ||
}; | ||
this.handleResize = () => { | ||
this.resize(); | ||
}; | ||
this.focusElement = null; | ||
@@ -77,10 +75,9 @@ this.state = { | ||
} | ||
componentDidMount() { | ||
this.resize(); | ||
window.addEventListener('resize', this.handleResize); | ||
} // TODO: refactor to stable lifecycle method | ||
} | ||
// TODO: refactor to stable lifecycle method | ||
// eslint-disable-next-line | ||
componentWillUpdate() { | ||
@@ -91,3 +88,2 @@ if (this.base.contains(document.activeElement)) { | ||
} | ||
componentDidUpdate() { | ||
@@ -98,11 +94,8 @@ // Maintain focus when rows are added and removed. | ||
} | ||
this.focusElement = null; | ||
this.resize(); | ||
} | ||
componentWillUnmount() { | ||
window.removeEventListener('resize', this.handleResize); | ||
} | ||
resize() { | ||
@@ -112,3 +105,2 @@ const { | ||
} = this.state; | ||
if (height !== this.base.offsetHeight) { | ||
@@ -120,3 +112,2 @@ this.setState({ | ||
} | ||
render(_ref) { | ||
@@ -133,27 +124,34 @@ let { | ||
height | ||
} = this.state; // first visible row index | ||
} = this.state; | ||
// first visible row index | ||
let start = Math.floor(offset / rowHeight); | ||
let start = Math.floor(offset / rowHeight); // actual number of visible rows (without overscan) | ||
// actual number of visible rows (without overscan) | ||
let visibleRowCount = Math.floor(height / rowHeight); | ||
let visibleRowCount = Math.floor(height / rowHeight); // Overscan: render blocks of rows modulo an overscan row count | ||
// Overscan: render blocks of rows modulo an overscan row count | ||
// This dramatically reduces DOM writes during scrolling | ||
if (overscanCount) { | ||
start = Math.max(0, start - start % overscanCount); | ||
visibleRowCount += overscanCount; | ||
} // last visible + overscan row index + padding to allow keyboard focus to travel past the visible area | ||
} | ||
// last visible + overscan row index + padding to allow keyboard focus to travel past the visible area | ||
const end = start + visibleRowCount + 4; | ||
const end = start + visibleRowCount + 4; // data slice currently in viewport plus overscan items | ||
// data slice currently in viewport plus overscan items | ||
const selection = data.slice(start, end); | ||
const styleInner = { ...STYLE_INNER, | ||
const styleInner = { | ||
...STYLE_INNER, | ||
height: data.length * rowHeight | ||
}; | ||
const styleContent = { ...STYLE_CONTENT, | ||
const styleContent = { | ||
...STYLE_CONTENT, | ||
top: start * rowHeight | ||
}; // The `role="presentation"` attributes ensure that these wrapper elements are not treated as list | ||
}; | ||
// The `role="presentation"` attributes ensure that these wrapper elements are not treated as list | ||
// items by accessibility and outline tools. | ||
return (// eslint-disable-next-line react/jsx-props-no-spreading | ||
return ( | ||
// eslint-disable-next-line react/jsx-props-no-spreading | ||
h("div", _extends({ | ||
@@ -170,5 +168,3 @@ onScroll: this.handleScroll | ||
} | ||
} | ||
export default VirtualList; |
function _classPrivateFieldLooseBase(receiver, privateKey) { if (!Object.prototype.hasOwnProperty.call(receiver, privateKey)) { throw new TypeError("attempted to use private field on non-instance"); } return receiver; } | ||
var id = 0; | ||
function _classPrivateFieldLooseKey(name) { return "__private_" + id++ + "_" + name; } | ||
import { h } from 'preact'; | ||
@@ -21,3 +18,3 @@ import { UIPlugin } from '@uppy/core'; | ||
const packageJson = { | ||
"version": "3.4.0" | ||
"version": "3.4.1" | ||
}; | ||
@@ -28,3 +25,2 @@ import locale from './locale.js'; | ||
const ESC_KEY = 27; | ||
function createPromise() { | ||
@@ -38,3 +34,2 @@ const o = {}; | ||
} | ||
function defaultPickerIcon() { | ||
@@ -51,27 +46,17 @@ return h("svg", { | ||
} | ||
/** | ||
* Dashboard UI with previews, metadata editing, tabs for various services and more | ||
*/ | ||
var _disabledNodes = /*#__PURE__*/_classPrivateFieldLooseKey("disabledNodes"); | ||
var _generateLargeThumbnailIfSingleFile = /*#__PURE__*/_classPrivateFieldLooseKey("generateLargeThumbnailIfSingleFile"); | ||
var _openFileEditorWhenFilesAdded = /*#__PURE__*/_classPrivateFieldLooseKey("openFileEditorWhenFilesAdded"); | ||
var _attachRenderFunctionToTarget = /*#__PURE__*/_classPrivateFieldLooseKey("attachRenderFunctionToTarget"); | ||
var _isTargetSupported = /*#__PURE__*/_classPrivateFieldLooseKey("isTargetSupported"); | ||
var _getAcquirers = /*#__PURE__*/_classPrivateFieldLooseKey("getAcquirers"); | ||
var _getProgressIndicators = /*#__PURE__*/_classPrivateFieldLooseKey("getProgressIndicators"); | ||
var _getEditors = /*#__PURE__*/_classPrivateFieldLooseKey("getEditors"); | ||
export default class Dashboard extends UIPlugin { | ||
constructor(uppy, _opts) { | ||
var _this; | ||
super(uppy, _opts); | ||
@@ -83,6 +68,5 @@ _this = this; | ||
}); | ||
this.removeTarget = plugin => { | ||
const pluginState = this.getPluginState(); // filter out the one we want to remove | ||
const pluginState = this.getPluginState(); | ||
// filter out the one we want to remove | ||
const newTargets = pluginState.targets.filter(target => target.id !== plugin.id); | ||
@@ -93,3 +77,2 @@ this.setPluginState({ | ||
}; | ||
this.addTarget = plugin => { | ||
@@ -99,3 +82,2 @@ const callerPluginId = plugin.id || plugin.constructor.name; | ||
const callerPluginType = plugin.type; | ||
if (callerPluginType !== 'acquirer' && callerPluginType !== 'progressindicator' && callerPluginType !== 'editor') { | ||
@@ -106,3 +88,2 @@ const msg = 'Dashboard: can only be targeted by plugins of types: acquirer, progressindicator, editor'; | ||
} | ||
const target = { | ||
@@ -121,3 +102,2 @@ id: callerPluginId, | ||
}; | ||
this.hideAllPanels = () => { | ||
@@ -132,3 +112,2 @@ const state = this.getPluginState(); | ||
}; | ||
if (state.activePickerPanel === update.activePickerPanel && state.showAddFilesPanel === update.showAddFilesPanel && state.showFileEditor === update.showFileEditor && state.activeOverlayType === update.activeOverlayType) { | ||
@@ -138,6 +117,4 @@ // avoid doing a state update if nothing changed | ||
} | ||
this.setPluginState(update); | ||
}; | ||
this.showPanel = id => { | ||
@@ -156,3 +133,2 @@ const { | ||
}; | ||
this.canEditFile = file => { | ||
@@ -162,8 +138,5 @@ const { | ||
} = this.getPluginState(); | ||
const editors = _classPrivateFieldLooseBase(this, _getEditors)[_getEditors](targets); | ||
return editors.some(target => this.uppy.getPlugin(target.id).canEditFile(file)); | ||
}; | ||
this.openFileEditor = file => { | ||
@@ -173,5 +146,3 @@ const { | ||
} = this.getPluginState(); | ||
const editors = _classPrivateFieldLooseBase(this, _getEditors)[_getEditors](targets); | ||
this.setPluginState({ | ||
@@ -186,3 +157,2 @@ showFileEditor: true, | ||
}; | ||
this.saveFileEditor = () => { | ||
@@ -192,5 +162,3 @@ const { | ||
} = this.getPluginState(); | ||
const editors = _classPrivateFieldLooseBase(this, _getEditors)[_getEditors](targets); | ||
editors.forEach(editor => { | ||
@@ -201,3 +169,2 @@ this.uppy.getPlugin(editor.id).save(); | ||
}; | ||
this.openModal = () => { | ||
@@ -207,12 +174,10 @@ const { | ||
resolve | ||
} = createPromise(); // save scroll position | ||
this.savedScrollPosition = window.pageYOffset; // save active element, so we can restore focus when modal is closed | ||
} = createPromise(); | ||
// save scroll position | ||
this.savedScrollPosition = window.pageYOffset; | ||
// save active element, so we can restore focus when modal is closed | ||
this.savedActiveElement = document.activeElement; | ||
if (this.opts.disablePageScrollWhenModalOpen) { | ||
document.body.classList.add('uppy-Dashboard-isFixed'); | ||
} | ||
if (this.opts.animateOpenClose && this.getPluginState().isClosing) { | ||
@@ -226,3 +191,2 @@ const handler = () => { | ||
}; | ||
this.el.addEventListener('animationend', handler, false); | ||
@@ -235,8 +199,7 @@ } else { | ||
} | ||
if (this.opts.browserBackButtonClose) { | ||
this.updateBrowserHistory(); | ||
} // handle ESC and TAB keys in modal dialog | ||
} | ||
// handle ESC and TAB keys in modal dialog | ||
document.addEventListener('keydown', this.handleKeyDownInModal); | ||
@@ -246,3 +209,2 @@ this.uppy.emit('dashboard:modal-open'); | ||
}; | ||
this.closeModal = function (opts) { | ||
@@ -252,3 +214,2 @@ if (opts === void 0) { | ||
} | ||
const { | ||
@@ -258,3 +219,2 @@ // Whether the modal is being closed by the user (`true`) or by other means (e.g. browser back button) | ||
} = opts; | ||
const { | ||
@@ -264,3 +224,2 @@ isHidden, | ||
} = _this.getPluginState(); | ||
if (isHidden || isClosing) { | ||
@@ -270,3 +229,2 @@ // short-circuit if animation is ongoing | ||
} | ||
const { | ||
@@ -276,7 +234,5 @@ promise, | ||
} = createPromise(); | ||
if (_this.opts.disablePageScrollWhenModalOpen) { | ||
document.body.classList.remove('uppy-Dashboard-isFixed'); | ||
} | ||
if (_this.opts.animateOpenClose) { | ||
@@ -286,3 +242,2 @@ _this.setPluginState({ | ||
}); | ||
const handler = () => { | ||
@@ -293,12 +248,7 @@ _this.setPluginState({ | ||
}); | ||
_this.superFocus.cancel(); | ||
_this.savedActiveElement.focus(); | ||
_this.el.removeEventListener('animationend', handler, false); | ||
resolve(); | ||
}; | ||
_this.el.addEventListener('animationend', handler, false); | ||
@@ -309,17 +259,12 @@ } else { | ||
}); | ||
_this.superFocus.cancel(); | ||
_this.savedActiveElement.focus(); | ||
resolve(); | ||
} // handle ESC and TAB keys in modal dialog | ||
} | ||
// handle ESC and TAB keys in modal dialog | ||
document.removeEventListener('keydown', _this.handleKeyDownInModal); | ||
if (manualClose) { | ||
if (_this.opts.browserBackButtonClose) { | ||
var _history$state; | ||
// Make sure that the latest entry in the history state is our modal name | ||
@@ -334,12 +279,8 @@ // eslint-disable-next-line no-restricted-globals | ||
} | ||
_this.uppy.emit('dashboard:modal-closed'); | ||
return promise; | ||
}; | ||
this.isModalOpen = () => { | ||
return !this.getPluginState().isHidden || false; | ||
}; | ||
this.requestCloseModal = () => { | ||
@@ -349,6 +290,4 @@ if (this.opts.onRequestCloseModal) { | ||
} | ||
return this.closeModal(); | ||
}; | ||
this.setDarkModeCapability = isDarkModeOn => { | ||
@@ -359,3 +298,4 @@ const { | ||
this.uppy.setState({ | ||
capabilities: { ...capabilities, | ||
capabilities: { | ||
...capabilities, | ||
darkMode: isDarkModeOn | ||
@@ -365,3 +305,2 @@ } | ||
}; | ||
this.handleSystemDarkModeChange = event => { | ||
@@ -372,6 +311,4 @@ const isDarkModeOnNow = event.matches; | ||
}; | ||
this.toggleFileCard = (show, fileID) => { | ||
const file = this.uppy.getFile(fileID); | ||
if (show) { | ||
@@ -382,3 +319,2 @@ this.uppy.emit('dashboard:file-edit-start', file); | ||
} | ||
this.setPluginState({ | ||
@@ -389,3 +325,2 @@ fileCardFor: show ? fileID : null, | ||
}; | ||
this.toggleAddFilesPanel = show => { | ||
@@ -397,3 +332,2 @@ this.setPluginState({ | ||
}; | ||
this.addFiles = files => { | ||
@@ -411,3 +345,2 @@ const descriptors = files.map(file => ({ | ||
})); | ||
try { | ||
@@ -419,3 +352,8 @@ this.uppy.addFiles(descriptors); | ||
}; | ||
// ___Why make insides of Dashboard invisible until first ResizeObserver event is emitted? | ||
// ResizeOberserver doesn't emit the first resize event fast enough, users can see the jump from one .uppy-size-- to | ||
// another (e.g. in Safari) | ||
// ___Why not apply visibility property to .uppy-Dashboard-inner? | ||
// Because ideally, acc to specs, ResizeObserver should see invisible elements as of width 0. So even though applying | ||
// invisibility to .uppy-Dashboard-inner works now, it may not work in the future. | ||
this.startListeningToResize = () => { | ||
@@ -437,11 +375,14 @@ // Watch for Dashboard container (`.uppy-Dashboard-inner`) resize | ||
}); | ||
this.resizeObserver.observe(this.el.querySelector('.uppy-Dashboard-inner')); // If ResizeObserver fails to emit an event telling us what size to use - default to the mobile view | ||
this.resizeObserver.observe(this.el.querySelector('.uppy-Dashboard-inner')); | ||
// If ResizeObserver fails to emit an event telling us what size to use - default to the mobile view | ||
this.makeDashboardInsidesVisibleAnywayTimeout = setTimeout(() => { | ||
const pluginState = this.getPluginState(); | ||
const isModalAndClosed = !this.opts.inline && pluginState.isHidden; | ||
if ( | ||
// We might want to enable this in the future | ||
if ( // We might want to enable this in the future | ||
// if ResizeObserver hasn't yet fired, | ||
!pluginState.areInsidesReadyToBeVisible // and it's not due to the modal being closed | ||
!pluginState.areInsidesReadyToBeVisible | ||
// and it's not due to the modal being closed | ||
&& !isModalAndClosed) { | ||
@@ -455,3 +396,2 @@ this.uppy.log('[Dashboard] resize event didn’t fire on time: defaulted to mobile layout', 'warning'); | ||
}; | ||
this.stopListeningToResize = () => { | ||
@@ -461,3 +401,4 @@ this.resizeObserver.disconnect(); | ||
}; | ||
// Records whether we have been interacting with uppy right now, | ||
// which is then used to determine whether state updates should trigger a refocusing. | ||
this.recordIfFocusedOnUppyRecently = event => { | ||
@@ -467,16 +408,13 @@ if (this.el.contains(event.target)) { | ||
} else { | ||
this.ifFocusedOnUppyRecently = false; // ___Why run this.superFocus.cancel here when it already runs in superFocusOnEachUpdate? | ||
this.ifFocusedOnUppyRecently = false; | ||
// ___Why run this.superFocus.cancel here when it already runs in superFocusOnEachUpdate? | ||
// Because superFocus is debounced, when we move from Uppy to some other element on the page, | ||
// previously run superFocus sometimes hits and moves focus back to Uppy. | ||
this.superFocus.cancel(); | ||
} | ||
}; | ||
this.disableInteractiveElements = disable => { | ||
var _classPrivateFieldLoo; | ||
const NODES_TO_DISABLE = ['a[href]', 'input:not([disabled])', 'select:not([disabled])', 'textarea:not([disabled])', 'button:not([disabled])', '[role="button"]:not([disabled])']; | ||
const nodesToDisable = (_classPrivateFieldLoo = _classPrivateFieldLooseBase(this, _disabledNodes)[_disabledNodes]) != null ? _classPrivateFieldLoo : toArray(this.el.querySelectorAll(NODES_TO_DISABLE)).filter(node => !node.classList.contains('uppy-Dashboard-close')); | ||
for (const node of nodesToDisable) { | ||
@@ -490,3 +428,2 @@ // Links can’t have `disabled` attr, so we use `aria-disabled` for a11y | ||
} | ||
if (disable) { | ||
@@ -497,9 +434,6 @@ _classPrivateFieldLooseBase(this, _disabledNodes)[_disabledNodes] = nodesToDisable; | ||
} | ||
this.dashboardIsDisabled = disable; | ||
}; | ||
this.updateBrowserHistory = () => { | ||
var _history$state2; | ||
// Ensure history state does not already contain our modal name to avoid double-pushing | ||
@@ -510,15 +444,14 @@ // eslint-disable-next-line no-restricted-globals | ||
// eslint-disable-next-line no-restricted-globals | ||
history.pushState({ // eslint-disable-next-line no-restricted-globals | ||
history.pushState({ | ||
// eslint-disable-next-line no-restricted-globals | ||
...history.state, | ||
[this.modalName]: true | ||
}, ''); | ||
} // Listen for back button presses | ||
} | ||
// Listen for back button presses | ||
window.addEventListener('popstate', this.handlePopState, false); | ||
}; | ||
this.handlePopState = event => { | ||
var _event$state; | ||
// Close the modal if the history state no longer contains our modal name | ||
@@ -529,3 +462,5 @@ if (this.isModalOpen() && (!event.state || !event.state[this.modalName])) { | ||
}); | ||
} // When the browser back button is pressed and uppy is now the latest entry | ||
} | ||
// When the browser back button is pressed and uppy is now the latest entry | ||
// in the history but the modal is closed, fix the history by removing the | ||
@@ -536,4 +471,2 @@ // uppy history entry. | ||
// Solves PR #575 (https://github.com/transloadit/uppy/pull/575) | ||
if (!this.isModalOpen() && (_event$state = event.state) != null && _event$state[this.modalName]) { | ||
@@ -544,14 +477,11 @@ // eslint-disable-next-line no-restricted-globals | ||
}; | ||
this.handleKeyDownInModal = event => { | ||
// close modal on esc key press | ||
if (event.keyCode === ESC_KEY) this.requestCloseModal(event); // trap focus on tab key press | ||
if (event.keyCode === ESC_KEY) this.requestCloseModal(event); | ||
// trap focus on tab key press | ||
if (event.keyCode === TAB_KEY) trapFocus.forModal(event, this.getPluginState().activeOverlayType, this.el); | ||
}; | ||
this.handleClickOutside = () => { | ||
if (this.opts.closeModalOnClickOutside) this.requestCloseModal(); | ||
}; | ||
this.handlePaste = event => { | ||
@@ -564,6 +494,6 @@ // Let any acquirer plugin (Url/Webcam/etc.) handle pastes to the root | ||
} | ||
}); // Add all dropped files | ||
}); | ||
// Add all dropped files | ||
const files = toArray(event.clipboardData.files); | ||
if (files.length > 0) { | ||
@@ -574,7 +504,5 @@ this.uppy.log('[Dashboard] Files pasted'); | ||
}; | ||
this.handleInputChange = event => { | ||
event.preventDefault(); | ||
const files = toArray(event.target.files); | ||
if (files.length > 0) { | ||
@@ -585,10 +513,9 @@ this.uppy.log('[Dashboard] Files selected through input'); | ||
}; | ||
this.handleDragOver = event => { | ||
var _this$opts$onDragOver, _this$opts; | ||
event.preventDefault(); | ||
event.stopPropagation(); | ||
event.preventDefault(); | ||
event.stopPropagation(); // Check if some plugin can handle the datatransfer without files — | ||
// Check if some plugin can handle the datatransfer without files — | ||
// for instance, the Url plugin can import a url | ||
const canSomePluginHandleRootDrop = () => { | ||
@@ -602,5 +529,5 @@ let somePluginCanHandleRootDrop = true; | ||
return somePluginCanHandleRootDrop; | ||
}; // Check if the "type" of the datatransfer object includes files | ||
}; | ||
// Check if the "type" of the datatransfer object includes files | ||
const doesEventHaveFiles = () => { | ||
@@ -611,21 +538,20 @@ const { | ||
return types.some(type => type === 'Files'); | ||
}; // Deny drop, if no plugins can handle datatransfer, there are no files, | ||
}; | ||
// Deny drop, if no plugins can handle datatransfer, there are no files, | ||
// or when opts.disabled is set, or new uploads are not allowed | ||
const somePluginCanHandleRootDrop = canSomePluginHandleRootDrop(event); | ||
const hasFiles = doesEventHaveFiles(event); | ||
if (!somePluginCanHandleRootDrop && !hasFiles || this.opts.disabled // opts.disableLocalFiles should only be taken into account if no plugins | ||
if (!somePluginCanHandleRootDrop && !hasFiles || this.opts.disabled | ||
// opts.disableLocalFiles should only be taken into account if no plugins | ||
// can handle the datatransfer | ||
|| this.opts.disableLocalFiles && (hasFiles || !somePluginCanHandleRootDrop) || !this.uppy.getState().allowNewUpload) { | ||
event.dataTransfer.dropEffect = 'none'; // eslint-disable-line no-param-reassign | ||
clearTimeout(this.removeDragOverClassTimeout); | ||
return; | ||
} // Add a small (+) icon on drop | ||
} | ||
// Add a small (+) icon on drop | ||
// (and prevent browsers from interpreting this as files being _moved_ into the | ||
// browser, https://github.com/transloadit/uppy/issues/1978). | ||
event.dataTransfer.dropEffect = 'copy'; // eslint-disable-line no-param-reassign | ||
@@ -639,11 +565,9 @@ | ||
}; | ||
this.handleDragLeave = event => { | ||
var _this$opts$onDragLeav, _this$opts2; | ||
event.preventDefault(); | ||
event.stopPropagation(); | ||
clearTimeout(this.removeDragOverClassTimeout); // Timeout against flickering, this solution is taken from drag-drop library. | ||
clearTimeout(this.removeDragOverClassTimeout); | ||
// Timeout against flickering, this solution is taken from drag-drop library. | ||
// Solution with 'pointer-events: none' didn't work across browsers. | ||
this.removeDragOverClassTimeout = setTimeout(() => { | ||
@@ -656,6 +580,4 @@ this.setPluginState({ | ||
}; | ||
this.handleDrop = async event => { | ||
var _this$opts$onDrop, _this$opts3; | ||
event.preventDefault(); | ||
@@ -666,4 +588,5 @@ event.stopPropagation(); | ||
isDraggingOver: false | ||
}); // Let any acquirer plugin (Url/Webcam/etc.) handle drops to the root | ||
}); | ||
// Let any acquirer plugin (Url/Webcam/etc.) handle drops to the root | ||
this.uppy.iteratePlugins(plugin => { | ||
@@ -674,10 +597,11 @@ if (plugin.type === 'acquirer') { | ||
} | ||
}); // Add all dropped files | ||
}); | ||
// Add all dropped files | ||
let executedDropErrorOnce = false; | ||
const logDropError = error => { | ||
this.uppy.log(error, 'error'); | ||
const logDropError = error => { | ||
this.uppy.log(error, 'error'); // In practice all drop errors are most likely the same, | ||
// In practice all drop errors are most likely the same, | ||
// so let's just show one to avoid overwhelming the user | ||
if (!executedDropErrorOnce) { | ||
@@ -688,9 +612,8 @@ this.uppy.info(error.message, 'error'); | ||
}; | ||
this.uppy.log('[Dashboard] Processing dropped files'); | ||
this.uppy.log('[Dashboard] Processing dropped files'); // Add all dropped files | ||
// Add all dropped files | ||
const files = await getDroppedFiles(event.dataTransfer, { | ||
logDropError | ||
}); | ||
if (files.length > 0) { | ||
@@ -700,6 +623,4 @@ this.uppy.log('[Dashboard] Files dropped'); | ||
} | ||
(_this$opts$onDrop = (_this$opts3 = this.opts).onDrop) == null ? void 0 : _this$opts$onDrop.call(_this$opts3, event); | ||
}; | ||
this.handleRequestThumbnail = file => { | ||
@@ -710,3 +631,6 @@ if (!this.opts.waitForThumbnailsBeforeUpload) { | ||
}; | ||
/** | ||
* We cancel thumbnail requests when a file item component unmounts to avoid | ||
* clogging up the queue when the user scrolls past many elements. | ||
*/ | ||
this.handleCancelThumbnail = file => { | ||
@@ -717,3 +641,2 @@ if (!this.opts.waitForThumbnailsBeforeUpload) { | ||
}; | ||
this.handleKeyDownInInline = event => { | ||
@@ -723,6 +646,13 @@ // Trap focus on tab key press. | ||
}; | ||
// ___Why do we listen to the 'paste' event on a document instead of onPaste={props.handlePaste} prop, | ||
// or this.el.addEventListener('paste')? | ||
// Because (at least) Chrome doesn't handle paste if focus is on some button, e.g. 'My Device'. | ||
// => Therefore, the best option is to listen to all 'paste' events, and only react to them when we are focused on our | ||
// particular Uppy instance. | ||
// ___Why do we still need onPaste={props.handlePaste} for the DashboardUi? | ||
// Because if we click on the 'Drop files here' caption e.g., `document.activeElement` will be 'body'. Which means our | ||
// standard determination of whether we're pasting into our Uppy instance won't work. | ||
// => Therefore, we need a traditional onPaste={props.handlePaste} handler too. | ||
this.handlePasteOnBody = event => { | ||
const isFocusInOverlay = this.el.contains(document.activeElement); | ||
if (isFocusInOverlay) { | ||
@@ -732,3 +662,2 @@ this.handlePaste(event); | ||
}; | ||
this.handleComplete = _ref => { | ||
@@ -738,3 +667,2 @@ let { | ||
} = _ref; | ||
if (this.opts.closeAfterFinish && failed.length === 0) { | ||
@@ -745,7 +673,5 @@ // All uploads are done | ||
}; | ||
this.handleCancelRestore = () => { | ||
this.uppy.emit('restore-canceled'); | ||
}; | ||
Object.defineProperty(this, _generateLargeThumbnailIfSingleFile, { | ||
@@ -757,6 +683,4 @@ writable: true, | ||
} | ||
const LARGE_THUMBNAIL = 600; | ||
const files = this.uppy.getFiles(); | ||
if (files.length === 1) { | ||
@@ -767,3 +691,4 @@ const thumbnailGenerator = this.uppy.getPlugin(`${this.id}:ThumbnailGenerator`); | ||
}); | ||
const fileForThumbnail = { ...files[0], | ||
const fileForThumbnail = { | ||
...files[0], | ||
preview: undefined | ||
@@ -783,3 +708,2 @@ }; | ||
const firstFile = files[0]; | ||
if (this.canEditFile(firstFile)) { | ||
@@ -790,3 +714,2 @@ this.openFileEditor(firstFile); | ||
}); | ||
this.initEvents = () => { | ||
@@ -796,3 +719,2 @@ // Modal open button | ||
const showModalTrigger = findAllDOMElements(this.opts.trigger); | ||
if (showModalTrigger) { | ||
@@ -804,3 +726,2 @@ showModalTrigger.forEach(trigger => trigger.addEventListener('click', this.openModal)); | ||
} | ||
this.startListeningToResize(); | ||
@@ -814,12 +735,11 @@ document.addEventListener('paste', this.handlePasteOnBody); | ||
this.uppy.on('files-added', _classPrivateFieldLooseBase(this, _generateLargeThumbnailIfSingleFile)[_generateLargeThumbnailIfSingleFile]); | ||
this.uppy.on('file-removed', _classPrivateFieldLooseBase(this, _generateLargeThumbnailIfSingleFile)[_generateLargeThumbnailIfSingleFile]); // ___Why fire on capture? | ||
this.uppy.on('file-removed', _classPrivateFieldLooseBase(this, _generateLargeThumbnailIfSingleFile)[_generateLargeThumbnailIfSingleFile]); | ||
// ___Why fire on capture? | ||
// Because this.ifFocusedOnUppyRecently needs to change before onUpdate() fires. | ||
document.addEventListener('focus', this.recordIfFocusedOnUppyRecently, true); | ||
document.addEventListener('click', this.recordIfFocusedOnUppyRecently, true); | ||
if (this.opts.inline) { | ||
this.el.addEventListener('keydown', this.handleKeyDownInInline); | ||
} | ||
if (this.opts.autoOpenFileEditor) { | ||
@@ -829,10 +749,7 @@ this.uppy.on('files-added', _classPrivateFieldLooseBase(this, _openFileEditorWhenFilesAdded)[_openFileEditorWhenFilesAdded]); | ||
}; | ||
this.removeEvents = () => { | ||
const showModalTrigger = findAllDOMElements(this.opts.trigger); | ||
if (!this.opts.inline && showModalTrigger) { | ||
showModalTrigger.forEach(trigger => trigger.removeEventListener('click', this.openModal)); | ||
} | ||
this.stopListeningToResize(); | ||
@@ -850,7 +767,5 @@ document.removeEventListener('paste', this.handlePasteOnBody); | ||
document.removeEventListener('click', this.recordIfFocusedOnUppyRecently); | ||
if (this.opts.inline) { | ||
this.el.removeEventListener('keydown', this.handleKeyDownInInline); | ||
} | ||
if (this.opts.autoOpenFileEditor) { | ||
@@ -860,15 +775,17 @@ this.uppy.off('files-added', _classPrivateFieldLooseBase(this, _openFileEditorWhenFilesAdded)[_openFileEditorWhenFilesAdded]); | ||
}; | ||
this.superFocusOnEachUpdate = () => { | ||
const isFocusInUppy = this.el.contains(document.activeElement); // When focus is lost on the page (== focus is on body for most browsers, or focus is null for IE11) | ||
const isFocusInUppy = this.el.contains(document.activeElement); | ||
// When focus is lost on the page (== focus is on body for most browsers, or focus is null for IE11) | ||
const isFocusNowhere = document.activeElement === document.body || document.activeElement === null; | ||
const isInformerHidden = this.uppy.getState().info.length === 0; | ||
const isModal = !this.opts.inline; | ||
if ( // If update is connected to showing the Informer - let the screen reader calmly read it. | ||
isInformerHidden && ( // If we are in a modal - always superfocus without concern for other elements | ||
if ( | ||
// If update is connected to showing the Informer - let the screen reader calmly read it. | ||
isInformerHidden && ( | ||
// If we are in a modal - always superfocus without concern for other elements | ||
// on the page (user is unlikely to want to interact with the rest of the page) | ||
isModal // If we are already inside of Uppy, or | ||
|| isFocusInUppy // If we are not focused on anything BUT we have already, at least once, focused on uppy | ||
isModal | ||
// If we are already inside of Uppy, or | ||
|| isFocusInUppy | ||
// If we are not focused on anything BUT we have already, at least once, focused on uppy | ||
// 1. We focus when isFocusNowhere, because when the element we were focused | ||
@@ -888,3 +805,2 @@ // on disappears (e.g. an overlay), - focus gets lost. If user is typing | ||
}; | ||
this.afterUpdate = () => { | ||
@@ -895,10 +811,7 @@ if (this.opts.disabled && !this.dashboardIsDisabled) { | ||
} | ||
if (!this.opts.disabled && this.dashboardIsDisabled) { | ||
this.disableInteractiveElements(false); | ||
} | ||
this.superFocusOnEachUpdate(); | ||
}; | ||
this.saveFileCard = (meta, fileID) => { | ||
@@ -908,3 +821,2 @@ this.uppy.setFileMeta(fileID, meta); | ||
}; | ||
Object.defineProperty(this, _attachRenderFunctionToTarget, { | ||
@@ -914,3 +826,4 @@ writable: true, | ||
const plugin = this.uppy.getPlugin(target.id); | ||
return { ...target, | ||
return { | ||
...target, | ||
icon: plugin.icon || this.opts.defaultPickerIcon, | ||
@@ -924,8 +837,7 @@ render: plugin.render | ||
value: target => { | ||
const plugin = this.uppy.getPlugin(target.id); // If the plugin does not provide a `supported` check, assume the plugin works everywhere. | ||
const plugin = this.uppy.getPlugin(target.id); | ||
// If the plugin does not provide a `supported` check, assume the plugin works everywhere. | ||
if (typeof plugin.isSupported !== 'function') { | ||
return true; | ||
} | ||
return plugin.isSupported(); | ||
@@ -952,3 +864,2 @@ } | ||
}); | ||
this.render = state => { | ||
@@ -974,11 +885,6 @@ const pluginState = this.getPluginState(); | ||
} = this.uppy.getObjectOfFilesPerState(); | ||
const acquirers = _classPrivateFieldLooseBase(this, _getAcquirers)[_getAcquirers](pluginState.targets); | ||
const progressindicators = _classPrivateFieldLooseBase(this, _getProgressIndicators)[_getProgressIndicators](pluginState.targets); | ||
const editors = _classPrivateFieldLooseBase(this, _getEditors)[_getEditors](pluginState.targets); | ||
let theme; | ||
if (this.opts.theme === 'auto') { | ||
@@ -989,9 +895,7 @@ theme = capabilities.darkMode ? 'dark' : 'light'; | ||
} | ||
if (['files', 'folders', 'both'].indexOf(this.opts.fileManagerSelectionType) < 0) { | ||
this.opts.fileManagerSelectionType = 'files'; // eslint-disable-next-line no-console | ||
this.opts.fileManagerSelectionType = 'files'; | ||
// eslint-disable-next-line no-console | ||
console.warn(`Unsupported option for "fileManagerSelectionType". Using default of "${this.opts.fileManagerSelectionType}".`); | ||
} | ||
return DashboardUI({ | ||
@@ -1085,3 +989,2 @@ state, | ||
}; | ||
this.discoverProviderPlugins = () => { | ||
@@ -1094,3 +997,2 @@ this.uppy.iteratePlugins(plugin => { | ||
}; | ||
this.install = () => { | ||
@@ -1115,7 +1017,5 @@ // Set default state for Dashboard | ||
} = this.opts; | ||
if (inline && closeAfterFinish) { | ||
throw new Error('[Dashboard] `closeAfterFinish: true` cannot be used on an inline Dashboard, because an inline Dashboard cannot be closed at all. Either set `inline: false`, or disable the `closeAfterFinish` option.'); | ||
} | ||
const { | ||
@@ -1125,19 +1025,14 @@ allowMultipleUploads, | ||
} = this.uppy.opts; | ||
if ((allowMultipleUploads || allowMultipleUploadBatches) && closeAfterFinish) { | ||
this.uppy.log('[Dashboard] When using `closeAfterFinish`, we recommended setting the `allowMultipleUploadBatches` option to `false` in the Uppy constructor. See https://uppy.io/docs/uppy/#allowMultipleUploads-true', 'warning'); | ||
} | ||
const { | ||
target | ||
} = this.opts; | ||
if (target) { | ||
this.mount(target, this); | ||
} | ||
const plugins = this.opts.plugins || []; | ||
plugins.forEach(pluginID => { | ||
const plugin = this.uppy.getPlugin(pluginID); | ||
if (plugin) { | ||
@@ -1147,3 +1042,2 @@ plugin.mount(this, plugin); | ||
}); | ||
if (!this.opts.disableStatusBar) { | ||
@@ -1163,3 +1057,2 @@ this.uppy.use(StatusBar, { | ||
} | ||
if (!this.opts.disableInformer) { | ||
@@ -1171,3 +1064,2 @@ this.uppy.use(Informer, { | ||
} | ||
if (!this.opts.disableThumbnailGenerator) { | ||
@@ -1183,5 +1075,5 @@ this.uppy.use(ThumbnailGenerator, { | ||
}); | ||
} // Dark Mode / theme | ||
} | ||
// Dark Mode / theme | ||
this.darkModeMediaQuery = typeof window !== 'undefined' && window.matchMedia ? window.matchMedia('(prefers-color-scheme: dark)') : null; | ||
@@ -1191,19 +1083,15 @@ const isDarkModeOnFromTheStart = this.darkModeMediaQuery ? this.darkModeMediaQuery.matches : false; | ||
this.setDarkModeCapability(isDarkModeOnFromTheStart); | ||
if (this.opts.theme === 'auto') { | ||
this.darkModeMediaQuery.addListener(this.handleSystemDarkModeChange); | ||
} | ||
this.discoverProviderPlugins(); | ||
this.initEvents(); | ||
}; | ||
this.uninstall = () => { | ||
if (!this.opts.disableInformer) { | ||
const informer = this.uppy.getPlugin(`${this.id}:Informer`); // Checking if this plugin exists, in case it was removed by uppy-core | ||
const informer = this.uppy.getPlugin(`${this.id}:Informer`); | ||
// Checking if this plugin exists, in case it was removed by uppy-core | ||
// before the Dashboard was. | ||
if (informer) this.uppy.removePlugin(informer); | ||
} | ||
if (!this.opts.disableStatusBar) { | ||
@@ -1213,3 +1101,2 @@ const statusBar = this.uppy.getPlugin(`${this.id}:StatusBar`); | ||
} | ||
if (!this.opts.disableThumbnailGenerator) { | ||
@@ -1219,3 +1106,2 @@ const thumbnail = this.uppy.getPlugin(`${this.id}:ThumbnailGenerator`); | ||
} | ||
const plugins = this.opts.plugins || []; | ||
@@ -1226,11 +1112,8 @@ plugins.forEach(pluginID => { | ||
}); | ||
if (this.opts.theme === 'auto') { | ||
this.darkModeMediaQuery.removeListener(this.handleSystemDarkModeChange); | ||
} | ||
this.unmount(); | ||
this.removeEvents(); | ||
}; | ||
this.id = this.opts.id || 'Dashboard'; | ||
@@ -1240,4 +1123,5 @@ this.title = 'Dashboard'; | ||
this.modalName = `uppy-Dashboard-${nanoid()}`; | ||
this.defaultLocale = locale; // set default options, must be kept in sync with packages/@uppy/react/src/DashboardModal.js | ||
this.defaultLocale = locale; | ||
// set default options, must be kept in sync with packages/@uppy/react/src/DashboardModal.js | ||
const defaultOptions = { | ||
@@ -1286,5 +1170,7 @@ target: 'body', | ||
disableLocalFiles: false | ||
}; // merge default options with the ones set by user | ||
}; | ||
this.opts = { ...defaultOptions, | ||
// merge default options with the ones set by user | ||
this.opts = { | ||
...defaultOptions, | ||
..._opts | ||
@@ -1294,9 +1180,9 @@ }; | ||
this.superFocus = createSuperFocus(); | ||
this.ifFocusedOnUppyRecently = false; // Timeouts | ||
this.ifFocusedOnUppyRecently = false; | ||
// Timeouts | ||
this.makeDashboardInsidesVisibleAnywayTimeout = null; | ||
this.removeDragOverClassTimeout = null; | ||
} | ||
} | ||
Dashboard.VERSION = packageJson.version; |
@@ -15,3 +15,2 @@ /** | ||
} | ||
return new Promise(resolve => { | ||
@@ -34,17 +33,13 @@ const textArea = document.createElement('textarea'); | ||
textArea.select(); | ||
const magicCopyFailed = () => { | ||
document.body.removeChild(textArea); // eslint-disable-next-line no-alert | ||
document.body.removeChild(textArea); | ||
// eslint-disable-next-line no-alert | ||
window.prompt(fallbackString, textToCopy); | ||
resolve(); | ||
}; | ||
try { | ||
const successful = document.execCommand('copy'); | ||
if (!successful) { | ||
return magicCopyFailed('copy command unavailable'); | ||
} | ||
document.body.removeChild(textArea); | ||
@@ -51,0 +46,0 @@ return resolve(); |
@@ -1,4 +0,5 @@ | ||
import debounce from 'lodash.debounce'; | ||
import debounce from 'lodash/debounce.js'; | ||
import FOCUSABLE_ELEMENTS from '@uppy/utils/lib/FOCUSABLE_ELEMENTS'; | ||
import getActiveOverlayEl from './getActiveOverlayEl.js'; | ||
/* | ||
@@ -14,19 +15,16 @@ Focuses on some element in the currently topmost overlay. | ||
*/ | ||
export default function createSuperFocus() { | ||
let lastFocusWasOnSuperFocusableEl = false; | ||
const superFocus = (dashboardEl, activeOverlayType) => { | ||
const overlayEl = getActiveOverlayEl(dashboardEl, activeOverlayType); | ||
const isFocusInOverlay = overlayEl.contains(document.activeElement); // If focus is already in the topmost overlay, AND on last update we focused on the superfocusable | ||
const isFocusInOverlay = overlayEl.contains(document.activeElement); | ||
// If focus is already in the topmost overlay, AND on last update we focused on the superfocusable | ||
// element - then leave focus up to the user. | ||
// [Practical check] without this line, typing in the search input in googledrive overlay won't work. | ||
if (isFocusInOverlay && lastFocusWasOnSuperFocusableEl) return; | ||
const superFocusableEl = overlayEl.querySelector('[data-uppy-super-focusable]'); // If we are already in the topmost overlay, AND there are no super focusable elements yet, - leave focus up to the user. | ||
const superFocusableEl = overlayEl.querySelector('[data-uppy-super-focusable]'); | ||
// If we are already in the topmost overlay, AND there are no super focusable elements yet, - leave focus up to the user. | ||
// [Practical check] without this line, if you are in an empty folder in google drive, and something's uploading in the | ||
// bg, - focus will be jumping to Done all the time. | ||
if (isFocusInOverlay && !superFocusableEl) return; | ||
if (superFocusableEl) { | ||
@@ -44,3 +42,5 @@ superFocusableEl.focus({ | ||
} | ||
}; // ___Why do we need to debounce? | ||
}; | ||
// ___Why do we need to debounce? | ||
// 1. To deal with animations: overlay changes via animations, which results in the DOM updating AFTER plugin.update() | ||
@@ -51,5 +51,3 @@ // already executed. | ||
// 2. Performance: there can be many state update()s in a second, and this function is called every time. | ||
return debounce(superFocus, 260); | ||
} |
@@ -6,8 +6,7 @@ /** | ||
if (activeOverlayType) { | ||
const overlayEl = dashboardEl.querySelector(`[data-uppy-paneltype="${activeOverlayType}"]`); // if an overlay is already mounted | ||
const overlayEl = dashboardEl.querySelector(`[data-uppy-paneltype="${activeOverlayType}"]`); | ||
// if an overlay is already mounted | ||
if (overlayEl) return overlayEl; | ||
} | ||
return dashboardEl; | ||
} |
import { h } from 'preact'; | ||
function iconImage() { | ||
@@ -25,3 +24,2 @@ return h("svg", { | ||
} | ||
function iconAudio() { | ||
@@ -41,3 +39,2 @@ return h("svg", { | ||
} | ||
function iconVideo() { | ||
@@ -57,3 +54,2 @@ return h("svg", { | ||
} | ||
function iconPDF() { | ||
@@ -73,3 +69,2 @@ return h("svg", { | ||
} | ||
function iconArchive() { | ||
@@ -88,3 +83,2 @@ return h("svg", { | ||
} | ||
function iconFile() { | ||
@@ -107,3 +101,2 @@ return h("svg", { | ||
} | ||
function iconText() { | ||
@@ -123,3 +116,2 @@ return h("svg", { | ||
} | ||
export default function getIconByMime(fileType) { | ||
@@ -132,4 +124,5 @@ const defaultChoice = { | ||
const fileTypeGeneral = fileType.split('/')[0]; | ||
const fileTypeSpecific = fileType.split('/')[1]; // Text | ||
const fileTypeSpecific = fileType.split('/')[1]; | ||
// Text | ||
if (fileTypeGeneral === 'text') { | ||
@@ -140,5 +133,5 @@ return { | ||
}; | ||
} // Image | ||
} | ||
// Image | ||
if (fileTypeGeneral === 'image') { | ||
@@ -149,5 +142,5 @@ return { | ||
}; | ||
} // Audio | ||
} | ||
// Audio | ||
if (fileTypeGeneral === 'audio') { | ||
@@ -158,5 +151,5 @@ return { | ||
}; | ||
} // Video | ||
} | ||
// Video | ||
if (fileTypeGeneral === 'video') { | ||
@@ -167,5 +160,5 @@ return { | ||
}; | ||
} | ||
if (fileTypeGeneral === 'application' && fileTypeSpecific === 'pdf') { | ||
@@ -176,7 +169,6 @@ return { | ||
}; | ||
} // Archive | ||
} | ||
// Archive | ||
const archiveTypes = ['zip', 'x-7z-compressed', 'x-rar-compressed', 'x-tar', 'x-gzip', 'x-apple-diskimage']; | ||
if (fileTypeGeneral === 'application' && archiveTypes.indexOf(fileTypeSpecific) !== -1) { | ||
@@ -188,4 +180,3 @@ return { | ||
} | ||
return defaultChoice; | ||
} |
@@ -5,2 +5,3 @@ // ignore drop/paste events if they are not in input or textarea — | ||
// Url treats them as URLs that need to be imported | ||
function ignoreEvent(ev) { | ||
@@ -10,3 +11,2 @@ const { | ||
} = ev.target; | ||
if (tagName === 'INPUT' || tagName === 'TEXTAREA') { | ||
@@ -16,7 +16,5 @@ ev.stopPropagation(); | ||
} | ||
ev.preventDefault(); | ||
ev.stopPropagation(); | ||
} | ||
export default ignoreEvent; |
import toArray from '@uppy/utils/lib/toArray'; | ||
import FOCUSABLE_ELEMENTS from '@uppy/utils/lib/FOCUSABLE_ELEMENTS'; | ||
import getActiveOverlayEl from './getActiveOverlayEl.js'; | ||
function focusOnFirstNode(event, nodes) { | ||
const node = nodes[0]; | ||
if (node) { | ||
@@ -13,6 +11,4 @@ node.focus(); | ||
} | ||
function focusOnLastNode(event, nodes) { | ||
const node = nodes[nodes.length - 1]; | ||
if (node) { | ||
@@ -22,3 +18,5 @@ node.focus(); | ||
} | ||
} // ___Why not just use (focusedItemIndex === -1)? | ||
} | ||
// ___Why not just use (focusedItemIndex === -1)? | ||
// Firefox thinks <ul> is focusable, but we don't have <ul>s in our FOCUSABLE_ELEMENTS. Which means that if we tab into | ||
@@ -29,32 +27,34 @@ // the <ul>, code will think that we are not in the active overlay, and we should focusOnFirstNode() of the currently | ||
// in the <ul>. | ||
function isFocusInOverlay(activeOverlayEl) { | ||
return activeOverlayEl.contains(document.activeElement); | ||
} | ||
function trapFocus(event, activeOverlayType, dashboardEl) { | ||
const activeOverlayEl = getActiveOverlayEl(dashboardEl, activeOverlayType); | ||
const focusableNodes = toArray(activeOverlayEl.querySelectorAll(FOCUSABLE_ELEMENTS)); | ||
const focusedItemIndex = focusableNodes.indexOf(document.activeElement); // If we pressed tab, and focus is not yet within the current overlay - focus on | ||
const focusedItemIndex = focusableNodes.indexOf(document.activeElement); | ||
// If we pressed tab, and focus is not yet within the current overlay - focus on | ||
// the first element within the current overlay. | ||
// This is a safety measure (for when user returns from another tab e.g.), most | ||
// plugins will try to focus on some important element as it loads. | ||
if (!isFocusInOverlay(activeOverlayEl)) { | ||
focusOnFirstNode(event, focusableNodes); // If we pressed shift + tab, and we're on the first element of a modal | ||
focusOnFirstNode(event, focusableNodes); | ||
// If we pressed shift + tab, and we're on the first element of a modal | ||
} else if (event.shiftKey && focusedItemIndex === 0) { | ||
focusOnLastNode(event, focusableNodes); // If we pressed tab, and we're on the last element of the modal | ||
focusOnLastNode(event, focusableNodes); | ||
// If we pressed tab, and we're on the last element of the modal | ||
} else if (!event.shiftKey && focusedItemIndex === focusableNodes.length - 1) { | ||
focusOnFirstNode(event, focusableNodes); | ||
} | ||
} // Traps focus inside of the currently open overlay (e.g. Dashboard, or e.g. Instagram), | ||
} | ||
// Traps focus inside of the currently open overlay (e.g. Dashboard, or e.g. Instagram), | ||
// never lets focus disappear from the modal. | ||
export { trapFocus as forModal }; | ||
export { trapFocus as forModal }; // Traps focus inside of the currently open overlay, unless overlay is null - then let the user tab away. | ||
// Traps focus inside of the currently open overlay, unless overlay is null - then let the user tab away. | ||
export function forInline(event, activeOverlayType, dashboardEl) { | ||
// ___When we're in the bare 'Drop files here, paste, browse or import from' screen | ||
if (activeOverlayType === null) {// Do nothing and let the browser handle it, user can tab away from Uppy to other elements on the page | ||
if (activeOverlayType === null) { | ||
// Do nothing and let the browser handle it, user can tab away from Uppy to other elements on the page | ||
// ___When there is some overlay with 'Done' button | ||
@@ -61,0 +61,0 @@ } else { |
{ | ||
"name": "@uppy/dashboard", | ||
"description": "Universal UI plugin for Uppy.", | ||
"version": "3.4.0", | ||
"version": "3.4.1", | ||
"license": "MIT", | ||
@@ -28,9 +28,9 @@ "main": "lib/index.js", | ||
"@uppy/informer": "^3.0.2", | ||
"@uppy/provider-views": "^3.3.0", | ||
"@uppy/status-bar": "^3.1.1", | ||
"@uppy/provider-views": "^3.3.1", | ||
"@uppy/status-bar": "^3.2.0", | ||
"@uppy/thumbnail-generator": "^3.0.3", | ||
"@uppy/utils": "^5.3.0", | ||
"@uppy/utils": "^5.4.0", | ||
"classnames": "^2.2.6", | ||
"is-shallow-equal": "^1.0.1", | ||
"lodash.debounce": "^4.0.8", | ||
"lodash": "^4.17.21", | ||
"memoize-one": "^6.0.0", | ||
@@ -43,8 +43,8 @@ "nanoid": "^4.0.0", | ||
"@uppy/google-drive": "^3.1.1", | ||
"@uppy/status-bar": "^3.1.1", | ||
"@uppy/status-bar": "^3.2.0", | ||
"resize-observer-polyfill": "^1.5.0" | ||
}, | ||
"peerDependencies": { | ||
"@uppy/core": "^3.2.0" | ||
"@uppy/core": "^3.2.1" | ||
} | ||
} |
@@ -49,3 +49,6 @@ import { h } from 'preact' | ||
const updateMeta = (newVal, name) => { | ||
setFormState({ [name]: newVal }) | ||
setFormState({ | ||
...formState, | ||
[name]: newVal, | ||
}) | ||
} | ||
@@ -52,0 +55,0 @@ |
@@ -1,2 +0,2 @@ | ||
import debounce from 'lodash.debounce' | ||
import debounce from 'lodash/debounce.js' | ||
import FOCUSABLE_ELEMENTS from '@uppy/utils/lib/FOCUSABLE_ELEMENTS' | ||
@@ -3,0 +3,0 @@ import getActiveOverlayEl from './getActiveOverlayEl.js' |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
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
698528
10003
+ Addedlodash@^4.17.21
- Removedlodash.debounce@^4.0.8
- Removedlodash.debounce@4.0.8(transitive)
Updated@uppy/provider-views@^3.3.1
Updated@uppy/status-bar@^3.2.0
Updated@uppy/utils@^5.4.0