├── .browserslistrc
├── .editorconfig
├── .flowconfig
├── .github
├── .nvmrc
├── FUNDING.yml
├── ISSUE_TEMPLATE
│ ├── bug_report.md
│ └── feature_request.md
├── actions
│ ├── actions-token-unused
│ │ ├── action.yml
│ │ └── index.js
│ ├── bundle-size-report
│ │ ├── action.yml
│ │ └── index.mjs
│ ├── npm-deprecate
│ │ └── action.yml
│ ├── prepare
│ │ └── action.yml
│ ├── save-versions-to-wf
│ │ ├── action.yml
│ │ └── index.js
│ └── versionLog
│ │ ├── action.yml
│ │ └── index.js
├── dependabot.yml
├── package.json
├── pnpm-lock.yaml
└── workflows
│ ├── deprecate.yml
│ ├── pr.yml
│ ├── release.yml
│ ├── stalebot.yml
│ └── storybook-publish.yml
├── .gitignore
├── .husky
├── commit-msg
├── pre-commit
└── pre-push
├── .npmignore
├── .npmrc
├── .nvmrc
├── .storybook
├── UploadyStoryDecorator.tsx
├── VersionBadge.tsx
├── cypressAddon
│ └── cypressDecorator.ts
├── main.ts
├── manager-head.html
├── manager.tsx
├── preview-head.html
├── preview.tsx
├── theme.ts
├── uploadyPreset.ts
└── welcome.storydoc.mdx
├── CHANGELOG.md
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── LICENSE.md
├── README.md
├── SECURITY.md
├── babel.config.js
├── bundle-size-report.json
├── bundle.config.mjs
├── codecov.yml
├── commitlint.config.mjs
├── cypress.config.mjs
├── cypress
├── component
│ └── upload-preview-ctest.js
├── constants.js
├── e2e-weights.json
├── fixtures
│ ├── flower.jpg
│ └── sea.jpg
├── integration
│ ├── chunked-sender
│ │ ├── ChunkedSender-Progress-spec.js
│ │ └── ChunkedSender-error-spec.js
│ ├── chunked-uploady
│ │ ├── ChunkedUploady-Abort-spec.js
│ │ ├── ChunkedUploady-WithChunkEventHooks-spec.js
│ │ └── ChunkedUploady-custom-success-spec.js
│ ├── dropFile.js
│ ├── intercept.js
│ ├── mock-sender
│ │ └── MockSender-progress-spec.js
│ ├── native-uploady
│ │ └── NativeUploady-simple-spec.js
│ ├── retry-hooks
│ │ ├── RetryHooks-queue-spec.js
│ │ ├── RetryHooks-withRetry-all-spec.js
│ │ ├── RetryHooks-withRetry-batch-spec.js
│ │ └── RetryHooks-withRetry-item-spec.js
│ ├── selectFiles.js
│ ├── tus-uploady
│ │ ├── TusUploady-parallel-spec.js
│ │ ├── TusUploady-parallel-with-data-spec.js
│ │ ├── TusUploady-resume-event-spec.js
│ │ ├── TusUploady-retry-spec.js
│ │ ├── TusUploady-send-data-spec.js
│ │ ├── TusUploady-simple-spec.js
│ │ ├── clearTusPersistStorage.js
│ │ ├── runParallerlUpload.js
│ │ └── tusIntercept.js
│ ├── umd
│ │ ├── all-umd-spec.js
│ │ ├── core-ui-chunked-umd-spec.js
│ │ ├── core-ui-umd-spec.js
│ │ └── core-umd-spec.js
│ ├── upload-button
│ │ ├── UploadButton-asButton-spec.js
│ │ ├── UploadButton-custom-input-button-spec.js
│ │ ├── UploadButton-customInputAndForm-spec.js
│ │ ├── UploadButton-differentConfig-spec.js
│ │ ├── UploadButton-disabled-spec.js
│ │ ├── UploadButton-eventHooks-spec.js
│ │ ├── UploadButton-eventListeners-spec.js
│ │ ├── UploadButton-form-spec.js
│ │ ├── UploadButton-group-spec.js
│ │ ├── UploadButton-progress-spec.js
│ │ ├── UploadButton-simple-multiple-spec.js
│ │ ├── UploadButton-simple-spec.js
│ │ └── UploadButton-styled-spec.js
│ ├── upload-drop-zone
│ │ ├── UploadDropZone-3rd-party-spec.js
│ │ ├── UploadDropZone-custom-remove-spec.js
│ │ ├── UploadDropZone-differentConfig-spec.js
│ │ ├── UploadDropZone-dropHandler-spec.js
│ │ ├── UploadDropZone-get-files-filter-spec.js
│ │ ├── UploadDropZone-handle-only-files-spec.js
│ │ ├── UploadDropZone-keep-on-child-spec.js
│ │ ├── UploadDropZone-no-handle-spec.js
│ │ ├── UploadDropZone-simple-no-contain-check-spec.js
│ │ └── UploadDropZone-simple-spec.js
│ ├── upload-paste
│ │ ├── UploadPaste-dropzone-spec.js
│ │ ├── UploadPaste-element-spec.js
│ │ ├── UploadPaste-simple-spec.js
│ │ ├── UploadPaste-uploadButton-spec.js
│ │ └── UploadPaste-window-spec.js
│ ├── upload-preview
│ │ ├── UploadPreview-clear-spec.js
│ │ ├── UploadPreview-crop-form-spec.js
│ │ ├── UploadPreview-crop-spec.js
│ │ ├── UploadPreview-custom-method-spec.js
│ │ ├── UploadPreview-multi-crop-spec.js
│ │ ├── UploadPreview-progress-spec.js
│ │ ├── UploadPreview-removePreview-spec.js
│ │ ├── UploadPreview-simple-fallback-spec.js
│ │ ├── UploadPreview-simple-multiple-spec.js
│ │ ├── UploadPreview-simple-spec.js
│ │ ├── UploadPreview-two-fields-spec.js
│ │ └── examineCroppedUploadReq.js
│ ├── upload-url-input
│ │ └── UploadUrlInput-simple-spec.js
│ ├── uploadFile.js
│ ├── uploader
│ │ ├── Uploader-data-test-spec.js
│ │ ├── Uploader-no-prepare-pollute-spec.js
│ │ ├── Uploader-proto-pollute-spec.js
│ │ └── Uploader-recover-from-error-spec.js
│ └── uploady
│ │ ├── Uploady-abort-spec.js
│ │ ├── Uploady-autoUpload-off-spec.js
│ │ ├── Uploady-cancel-on-add-spec.js
│ │ ├── Uploady-cancel-with-async-presend-spec.js
│ │ ├── Uploady-custom-success-spec.js
│ │ ├── Uploady-customResponseFormat-spec.js
│ │ ├── Uploady-failed-mock-spec.js
│ │ ├── Uploady-fast-abort-spec.js
│ │ ├── Uploady-filesParamName-spec.js
│ │ ├── Uploady-headerFromPreSend-spec.js
│ │ ├── Uploady-internal-input-spec.js
│ │ ├── Uploady-invalid-batch-start-spec.js
│ │ ├── Uploady-invalid-request-pre-send-spec.js
│ │ ├── Uploady-pending-with-options-spec.js
│ │ ├── Uploady-simple-spec.js
│ │ └── Uploady-undefined-param-spec.js
├── plugins
│ └── index.js
├── reporters-config.json-old
├── support
│ ├── component-index.html
│ ├── component.js
│ ├── e2e.js
│ ├── iframe.js
│ ├── index.d.ts
│ ├── index.js
│ ├── pasteFile.js
│ ├── setUploadOptions.js
│ ├── storyLog.js
│ ├── visitStory.js
│ └── wait.js
├── tsconfig.json
└── webpack.cypress.config.mjs
├── eslint.config.mjs
├── flow-typed.config.json
├── flow-typed
└── npm
│ ├── bom.js
│ ├── cssom.js
│ ├── dom.js
│ ├── flow-bin_v0.x.x.js
│ ├── invariant_v2.x.x.js
│ ├── json-api.js
│ ├── jsx-custom.js
│ ├── node.js
│ ├── serviceworkers.js
│ ├── shelljs_vx.x.x.js
│ ├── streams.js
│ ├── webpack_v5.x.x.js
│ └── yargs_v17.x.x.js
├── guides
├── README.md
└── rpldy-demo.gif
├── lerna.json
├── netlify.toml
├── nx.json
├── package.json
├── packages
├── core
│ ├── abort
│ │ ├── .npmignore
│ │ ├── README.md
│ │ ├── package.json
│ │ ├── src
│ │ │ ├── abort.js
│ │ │ ├── fastAbort.js
│ │ │ ├── getAbortEnhancer.js
│ │ │ ├── index.js
│ │ │ ├── tests
│ │ │ │ ├── abort.test.js
│ │ │ │ ├── fastAbort.test.js
│ │ │ │ └── getAbortEnhancer.test.js
│ │ │ └── types.js
│ │ └── types
│ │ │ ├── index.d.ts
│ │ │ └── index.test-d.ts
│ ├── chunked-sender
│ │ ├── .npmignore
│ │ ├── ChunkedSender.stories.js
│ │ ├── ChunkedSender.storydoc.mdx
│ │ ├── README.md
│ │ ├── package.json
│ │ ├── src
│ │ │ ├── chunkedSender
│ │ │ │ ├── ChunkedSendError.js
│ │ │ │ ├── createChunkedSender.js
│ │ │ │ ├── getChunkedState.js
│ │ │ │ ├── getChunks.js
│ │ │ │ ├── getChunksToSend.js
│ │ │ │ ├── handleChunkRequest.js
│ │ │ │ ├── index.js
│ │ │ │ ├── processChunkProgressData.js
│ │ │ │ ├── processChunks.js
│ │ │ │ ├── sendChunk.js
│ │ │ │ ├── sendChunks.js
│ │ │ │ ├── tests
│ │ │ │ │ ├── createChunkedSender.test.js
│ │ │ │ │ ├── getChunkedState.test.js
│ │ │ │ │ ├── getChunks.test.js
│ │ │ │ │ ├── getChunksToSend.test.js
│ │ │ │ │ ├── handleChunkRequest.test.js
│ │ │ │ │ ├── mocks
│ │ │ │ │ │ └── getChunkedState.mock.js
│ │ │ │ │ ├── processChunkProgressData.test.js
│ │ │ │ │ ├── processChunks.test.js
│ │ │ │ │ ├── sendChunk.test.js
│ │ │ │ │ └── sendChunks.test.js
│ │ │ │ └── types.js
│ │ │ ├── consts.js
│ │ │ ├── defaults.js
│ │ │ ├── getChunkedEnhancer.js
│ │ │ ├── index.js
│ │ │ ├── tests
│ │ │ │ ├── chunkedEnhancer.test.js
│ │ │ │ └── utils.test.js
│ │ │ ├── types.js
│ │ │ └── utils.js
│ │ └── types
│ │ │ ├── index.d.ts
│ │ │ └── index.test-d.tsx
│ ├── life-events
│ │ ├── .npmignore
│ │ ├── README.md
│ │ ├── package.json
│ │ ├── src
│ │ │ ├── consts.js
│ │ │ ├── defaults.js
│ │ │ ├── index.js
│ │ │ ├── lifeEvents.js
│ │ │ ├── lifePack.js
│ │ │ ├── tests
│ │ │ │ ├── lifeEvents.test.js
│ │ │ │ ├── lifePack.test.js
│ │ │ │ ├── mocks
│ │ │ │ │ └── rpldy-life-events.mock.js
│ │ │ │ └── utils.test.js
│ │ │ ├── types.js
│ │ │ └── utils.js
│ │ └── types
│ │ │ ├── index.d.ts
│ │ │ └── index.test-d.tsx
│ ├── mock-sender
│ │ ├── .npmignore
│ │ ├── MockSender.stories.js
│ │ ├── MockSender.storydoc.mdx
│ │ ├── README.md
│ │ ├── package.json
│ │ ├── src
│ │ │ ├── consts.js
│ │ │ ├── defaults.js
│ │ │ ├── getMockSenderEnhancer.js
│ │ │ ├── index.js
│ │ │ ├── mockSender.js
│ │ │ ├── tests
│ │ │ │ ├── getMockSenderEnhancer.test.js
│ │ │ │ └── mockSender.test.js
│ │ │ └── types.js
│ │ └── types
│ │ │ ├── index.d.ts
│ │ │ └── index.test-d.tsx
│ ├── raw-uploader
│ │ ├── .npmignore
│ │ ├── README.md
│ │ ├── package.json
│ │ ├── src
│ │ │ ├── index.js
│ │ │ └── types.js
│ │ └── types
│ │ │ ├── index.d.ts
│ │ │ └── index.test-d.ts
│ ├── retry
│ │ ├── .npmignore
│ │ ├── README.md
│ │ ├── package.json
│ │ ├── src
│ │ │ ├── consts.js
│ │ │ ├── index.js
│ │ │ ├── retry.js
│ │ │ ├── tests
│ │ │ │ └── retry.test.js
│ │ │ └── types.js
│ │ └── types
│ │ │ ├── index.d.ts
│ │ │ └── index.test-d.tsx
│ ├── safe-storage
│ │ ├── .npmignore
│ │ ├── README.md
│ │ ├── package.json
│ │ ├── src
│ │ │ ├── index.js
│ │ │ ├── safeStorageCreator.js
│ │ │ ├── storage.js
│ │ │ ├── tests
│ │ │ │ ├── localStorage.test.js
│ │ │ │ └── sessionStorage.test.js
│ │ │ └── types.js
│ │ └── types
│ │ │ ├── index.d.ts
│ │ │ └── index.test-d.ts
│ ├── sender
│ │ ├── .npmignore
│ │ ├── README.md
│ │ ├── package.json
│ │ ├── src
│ │ │ ├── MissingUrlError.js
│ │ │ ├── consts.js
│ │ │ ├── index.js
│ │ │ ├── types.js
│ │ │ └── xhrSender
│ │ │ │ ├── prepareFormData.js
│ │ │ │ ├── tests
│ │ │ │ ├── prepareFormData.test.js
│ │ │ │ └── xhrSender.test.js
│ │ │ │ └── xhrSender.js
│ │ └── types
│ │ │ ├── index.d.ts
│ │ │ └── index.test-d.tsx
│ ├── shared
│ │ ├── .npmignore
│ │ ├── README.md
│ │ ├── package.json
│ │ ├── src
│ │ │ ├── batchItem.js
│ │ │ ├── consts.js
│ │ │ ├── enums.js
│ │ │ ├── index.js
│ │ │ ├── logger.js
│ │ │ ├── request
│ │ │ │ ├── XhrPromise.js
│ │ │ │ ├── index.js
│ │ │ │ ├── parseResponseHeaders.js
│ │ │ │ ├── request.js
│ │ │ │ └── tests
│ │ │ │ │ ├── parseResponseHeaders.test.js
│ │ │ │ │ └── request.test.js
│ │ │ ├── tests
│ │ │ │ ├── batchItem.test.js
│ │ │ │ ├── logger.test.js
│ │ │ │ ├── mocks
│ │ │ │ │ └── rpldy-shared.mock.js
│ │ │ │ ├── triggerCancellable.test.js
│ │ │ │ └── triggerUpdater.test.js
│ │ │ ├── triggerCancellable.js
│ │ │ ├── triggerUpdater.js
│ │ │ ├── types.js
│ │ │ └── utils
│ │ │ │ ├── clone.js
│ │ │ │ ├── devFreeze.js
│ │ │ │ ├── hasWindow.js
│ │ │ │ ├── index.js
│ │ │ │ ├── isEmpty.js
│ │ │ │ ├── isFunction.js
│ │ │ │ ├── isPlainObject.js
│ │ │ │ ├── isProduction.js
│ │ │ │ ├── isPromise.js
│ │ │ │ ├── isSamePropInArrays.js
│ │ │ │ ├── merge.js
│ │ │ │ ├── pick.js
│ │ │ │ ├── scheduleIdleWork.js
│ │ │ │ └── tests
│ │ │ │ ├── clone.test.js
│ │ │ │ ├── devFreeze.test.js
│ │ │ │ ├── isFunction.test.js
│ │ │ │ ├── isPlainObject.test.js
│ │ │ │ ├── isPromise.test.js
│ │ │ │ ├── isSamePropInArrays.test.js
│ │ │ │ ├── merge.test.js
│ │ │ │ ├── pick.test.js
│ │ │ │ └── scheduleIdleWork.test.js
│ │ └── types
│ │ │ ├── index.d.ts
│ │ │ └── index.test-d.tsx
│ ├── simple-state
│ │ ├── .npmignore
│ │ ├── README.md
│ │ ├── package.json
│ │ ├── src
│ │ │ ├── consts.js
│ │ │ ├── createState.js
│ │ │ ├── index.js
│ │ │ ├── tests
│ │ │ │ ├── createState-prod.test.js
│ │ │ │ ├── createState.test.js
│ │ │ │ ├── mocks
│ │ │ │ │ └── getTestData.mock.js
│ │ │ │ └── unwrap.test.js
│ │ │ ├── types.js
│ │ │ └── utils.js
│ │ └── types
│ │ │ ├── index.d.ts
│ │ │ └── index.test-d.ts
│ ├── tus-sender
│ │ ├── .npmignore
│ │ ├── README.md
│ │ ├── TusSender.stories.js
│ │ ├── TusSender.storydoc.mdx
│ │ ├── package.json
│ │ ├── src
│ │ │ ├── consts.js
│ │ │ ├── defaults.js
│ │ │ ├── featureDetection.js
│ │ │ ├── getTusEnhancer.js
│ │ │ ├── index.js
│ │ │ ├── resumableStore.js
│ │ │ ├── tests
│ │ │ │ ├── featureDetection.test.js
│ │ │ │ ├── getTusEnhancer.test.js
│ │ │ │ ├── resumableStore.test.js
│ │ │ │ ├── tusState.mock.js
│ │ │ │ └── utils.test.js
│ │ │ ├── tusSender
│ │ │ │ ├── createTusSender.js
│ │ │ │ ├── handleEvents.js
│ │ │ │ ├── handleParallelTusUpload.js
│ │ │ │ ├── handleTusUpload.js
│ │ │ │ ├── index.js
│ │ │ │ ├── initTusUpload
│ │ │ │ │ ├── createStateItemData.js
│ │ │ │ │ ├── createUpload.js
│ │ │ │ │ ├── index.js
│ │ │ │ │ ├── initParallelTusUpload.js
│ │ │ │ │ ├── initTusUpload.js
│ │ │ │ │ ├── resumeUpload.js
│ │ │ │ │ └── tests
│ │ │ │ │ │ ├── createStateItemData.test.js
│ │ │ │ │ │ ├── createUpload.test.js
│ │ │ │ │ │ ├── initParallelTusUpload.test.js
│ │ │ │ │ │ ├── initTusUpload.test.js
│ │ │ │ │ │ └── resumeUpload.test.js
│ │ │ │ ├── tests
│ │ │ │ │ ├── createTusSender.test.js
│ │ │ │ │ ├── hadnleEvents-no-chunking.test.js
│ │ │ │ │ ├── hadnleEvents-with-chunking.test.js
│ │ │ │ │ ├── handleParallelTusUpload.test.js
│ │ │ │ │ ├── handleTusUpload.test.js
│ │ │ │ │ ├── tusSend-no-chunking.test.js
│ │ │ │ │ ├── tusSend-with-chunking.test.js
│ │ │ │ │ └── utils.test.js
│ │ │ │ ├── tusSend.js
│ │ │ │ ├── types.js
│ │ │ │ └── utils.js
│ │ │ ├── types.js
│ │ │ └── utils.js
│ │ └── types
│ │ │ ├── index.d.ts
│ │ │ └── index.test-d.ts
│ └── uploader
│ │ ├── .npmignore
│ │ ├── README.md
│ │ ├── Uploader.stories.js
│ │ ├── Uploader.storydoc.mdx
│ │ ├── package.json
│ │ ├── src
│ │ ├── batch.js
│ │ ├── batchItemsSender.js
│ │ ├── composeEnhancers.js
│ │ ├── consts.js
│ │ ├── defaults.js
│ │ ├── index.js
│ │ ├── processor.js
│ │ ├── queue
│ │ │ ├── batchHelpers.js
│ │ │ ├── index.js
│ │ │ ├── itemHelpers.js
│ │ │ ├── preSendPrepare.js
│ │ │ ├── processAbort.js
│ │ │ ├── processBatchItems.js
│ │ │ ├── processFinishedRequest.js
│ │ │ ├── processQueueNext.js
│ │ │ ├── tests
│ │ │ │ ├── batchHelpers.test.js
│ │ │ │ ├── itemHelpers.test.js
│ │ │ │ ├── mocks
│ │ │ │ │ └── getQueueState.mock.js
│ │ │ │ ├── preSendPrepare.test.js
│ │ │ │ ├── processAbort.test.js
│ │ │ │ ├── processBatchItems.test.js
│ │ │ │ ├── processFinishedRequest.test.js
│ │ │ │ ├── processQueueNext.test.js
│ │ │ │ └── uploaderQueue.test.js
│ │ │ ├── types.js
│ │ │ └── uploaderQueue.js
│ │ ├── tests
│ │ │ ├── batch.test.js
│ │ │ ├── batchItemsSender.test.js
│ │ │ ├── composeEnhancers.test.js
│ │ │ ├── mocks
│ │ │ │ └── rpldy-uploader.mock.js
│ │ │ ├── processor.test.js
│ │ │ ├── uploader.test.js
│ │ │ └── utils.test.js
│ │ ├── types.js
│ │ ├── uploader.js
│ │ └── utils.js
│ │ └── types
│ │ ├── index.d.ts
│ │ └── index.test-d.ts
├── native
│ └── native-uploady
│ │ ├── .npmignore
│ │ ├── NativeUploady.stories.js
│ │ ├── NativeUploady.storydoc.mdx
│ │ ├── README.md
│ │ ├── package.json
│ │ ├── src
│ │ ├── NativeUploady.js
│ │ ├── index.js
│ │ ├── tests
│ │ │ └── NativeUploady.test.jsx
│ │ └── types.js
│ │ └── types
│ │ ├── index.d.ts
│ │ └── index.test-d.tsx
└── ui
│ ├── chunked-uploady
│ ├── .npmignore
│ ├── ChunkedUploady.stories.js
│ ├── ChunkedUploady.storydoc.mdx
│ ├── README.md
│ ├── package.json
│ ├── src
│ │ ├── ChunkedUploady.js
│ │ ├── chunkEventListenerHooks.js
│ │ ├── index.js
│ │ ├── tests
│ │ │ ├── ChunkedUploady-no-hunking.test.jsx
│ │ │ ├── ChunkedUploady-with-chunking.test.jsx
│ │ │ └── chunkEventListenerHooks.test.js
│ │ └── types.js
│ └── types
│ │ ├── index.d.ts
│ │ └── index.test-d.tsx
│ ├── retry-hooks
│ ├── .npmignore
│ ├── README.md
│ ├── RetryHooks.stories.js
│ ├── RetryHooks.storydoc.mdx
│ ├── package.json
│ ├── src
│ │ ├── consts.js
│ │ ├── index.js
│ │ ├── tests
│ │ │ ├── useBatchRetry.test.js
│ │ │ ├── useRetry.test.js
│ │ │ └── useRetryListener.test.js
│ │ ├── types.js
│ │ ├── useBatchRetry.js
│ │ ├── useRetry.js
│ │ └── useRetryListener.js
│ └── types
│ │ ├── index.d.ts
│ │ └── index.test-d.tsx
│ ├── shared
│ ├── .npmignore
│ ├── README.md
│ ├── package.json
│ ├── src
│ │ ├── NoDomUploady.js
│ │ ├── UploadyContext.js
│ │ ├── assertContext.js
│ │ ├── consts.js
│ │ ├── hocs
│ │ │ ├── createRequestUpdateHoc.js
│ │ │ ├── tests
│ │ │ │ ├── withBatchStartUpdate.test.jsx
│ │ │ │ └── withRequestPreSendUpdate.test.jsx
│ │ │ ├── withBatchStartUpdate.js
│ │ │ └── withRequestPreSendUpdate.js
│ │ ├── hooks
│ │ │ ├── eventListenerHooks.js
│ │ │ ├── hooksUtils.js
│ │ │ ├── tests
│ │ │ │ ├── eventListenerHooks.test.js
│ │ │ │ ├── hooksUtils.test.js
│ │ │ │ ├── useAbortAll.test.js
│ │ │ │ ├── useAbortBatch.test.js
│ │ │ │ ├── useAbortItem.test.js
│ │ │ │ ├── useUploadOptions.test.js
│ │ │ │ ├── useUploader.test.js
│ │ │ │ └── useUploadyContext.test.js
│ │ │ ├── useAbortAll.js
│ │ │ ├── useAbortBatch.js
│ │ │ ├── useAbortItem.js
│ │ │ ├── useUploadOptions.js
│ │ │ ├── useUploader.js
│ │ │ └── useUploadyContext.js
│ │ ├── index.js
│ │ ├── tests
│ │ │ ├── NoDomUploady.test.jsx
│ │ │ ├── UploadyContext.test.js
│ │ │ ├── assertContext.test.js
│ │ │ ├── mocks
│ │ │ │ ├── UploadyContext.mock.jsx
│ │ │ │ └── rpldy-ui-shared.mock.jsx
│ │ │ ├── uploadyVersion.test.js
│ │ │ └── utils.test.jsx
│ │ ├── types.js
│ │ ├── uploadyVersion.js
│ │ └── utils.js
│ └── types
│ │ ├── index.d.ts
│ │ └── index.test-d.tsx
│ ├── tus-uploady
│ ├── .npmignore
│ ├── README.md
│ ├── TusUploady.stories.js
│ ├── TusUploady.storydoc.mdx
│ ├── package.json
│ ├── src
│ │ ├── TusUploady.js
│ │ ├── consts.js
│ │ ├── index.js
│ │ ├── tests
│ │ │ ├── TusUploady.withChunkSupport.test.jsx
│ │ │ ├── TusUploady.withoutChunkSupport.test.jsx
│ │ │ ├── useClearResumablesStore.test.js
│ │ │ └── useTusResumeStartListener.test.js
│ │ ├── types.js
│ │ ├── useClearResumableStore.js
│ │ └── useTusResumeStartListener.js
│ └── types
│ │ ├── index.d.ts
│ │ └── index.test-d.tsx
│ ├── upload-button
│ ├── .npmignore
│ ├── README.md
│ ├── UploadButton.stories.js
│ ├── UploadButton.storydoc.mdx
│ ├── package.json
│ ├── src
│ │ ├── UploadButton.js
│ │ ├── UploadButton.test.jsx
│ │ ├── asUploadButton.js
│ │ ├── asUploadButton.test.jsx
│ │ ├── index.js
│ │ └── types.js
│ └── types
│ │ ├── index.d.ts
│ │ └── index.test-d.tsx
│ ├── upload-drop-zone
│ ├── .npmignore
│ ├── README.md
│ ├── UploadDropZone.stories.js
│ ├── UploadDropZone.storydoc.mdx
│ ├── package.json
│ ├── src
│ │ ├── UploadDropZone.js
│ │ ├── UploadDropZone.test.jsx
│ │ ├── index.js
│ │ └── types.js
│ └── types
│ │ ├── index.d.ts
│ │ └── index.test-d.tsx
│ ├── upload-paste
│ ├── .npmignore
│ ├── README.md
│ ├── UploadPaste.stories.js
│ ├── UploadPaste.storydoc.mdx
│ ├── package.json
│ ├── src
│ │ ├── index.js
│ │ ├── tests
│ │ │ ├── usePasteHandler.test.jsx
│ │ │ ├── usePasteUpload.test.jsx
│ │ │ └── withPasteUpload.test.jsx
│ │ ├── types.js
│ │ ├── usePasteHandler.js
│ │ ├── usePasteUpload.js
│ │ └── withPasteUpload.js
│ └── types
│ │ ├── index.d.ts
│ │ └── index.test-d.tsx
│ ├── upload-preview
│ ├── .npmignore
│ ├── README.md
│ ├── UploadPreview.stories.js
│ ├── UploadPreview.storydoc.mdx
│ ├── package.json
│ ├── src
│ │ ├── UploadPreview.js
│ │ ├── consts.js
│ │ ├── defaults.js
│ │ ├── index.js
│ │ ├── tests
│ │ │ ├── UploadPreview.test.jsx
│ │ │ ├── usePreviewsLoader.test.js
│ │ │ └── utils.test.js
│ │ ├── types.js
│ │ ├── usePreviewsLoader.js
│ │ └── utils.js
│ └── types
│ │ ├── index.d.ts
│ │ └── index.test-d.tsx
│ ├── upload-url-input
│ ├── .npmignore
│ ├── README.md
│ ├── UploadUrlInput.stories.js
│ ├── UploadUrlInput.storydoc.mdx
│ ├── package.json
│ ├── src
│ │ ├── UploadUrlInput.js
│ │ ├── UploadUrlInput.test.jsx
│ │ ├── index.js
│ │ └── types.js
│ └── types
│ │ ├── index.d.ts
│ │ └── index.test-d.tsx
│ └── uploady
│ ├── .npmignore
│ ├── README.md
│ ├── Uploady.stories.js
│ ├── Uploady.storydoc.mdx
│ ├── all-bundle-entry.js
│ ├── package.json
│ ├── src
│ ├── Uploady.js
│ ├── index.js
│ ├── tests
│ │ ├── Uploady.test.jsx
│ │ ├── mocks
│ │ │ └── rpldy-uploady.mock.jsx
│ │ └── useFileInput.test.jsx
│ └── useFileInput.js
│ └── types
│ ├── index.d.ts
│ └── index.test-d.tsx
├── pnpm-lock.yaml
├── pnpm-workspace.yaml
├── scripts
├── build.mjs
├── bundle.mjs
├── ensureTypes.mjs
├── extractChangeLogNotes.js
├── lernaUtils.mjs
├── parallel-e2e.mjs
├── reportBundleSizeToGithub.mjs
├── upgradeDep.mjs
├── uploadyVersion.mjs
└── utils.mjs
├── story-helpers
├── ComponentsStyles.js
├── ProgressReportTable.jsx
├── ReactCropWithImage.jsx
├── StoryAbortButton.jsx
├── StoryUploadProgress.jsx
├── UmdBundleScript.jsx
├── Welcome.jsx
├── consts.js
├── createUploadyStory.jsx
├── cropImage.js
├── dropZoneCss.js
├── getCsfExport.js
├── helpers.js
├── index.js
├── storySetupControls
│ ├── args.js
│ └── useStoryUploadySetupFromArgs.js
├── uploadDestinations.js
├── uploadyStoryLogger.js
├── useEventsLogUpdater.js
├── useExternalUploadOptionsProvider.js
└── useScript.js
├── test
├── umd-bundle
│ ├── rpldy-all.html
│ ├── rpldy-core.html
│ ├── rpldy-ui-core-chunked.html
│ └── rpldy-ui-core.html
└── vitest-setup.mjs
├── tsconfig.json
└── vite.config.mjs
/.browserslistrc:
--------------------------------------------------------------------------------
1 | defaults and > 1%
2 | not IE 11
3 | not IE_Mob 11
4 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # http://editorconfig.org
2 |
3 | root = true
4 |
5 | [*]
6 | charset = utf-8
7 | indent_style = space
8 | indent_size = 4
9 | end_of_line = lf
10 | insert_final_newline = true
11 | trim_trailing_whitespace = true
12 |
13 | [*.md]
14 | insert_final_newline = false
15 | trim_trailing_whitespace = false
16 |
--------------------------------------------------------------------------------
/.github/.nvmrc:
--------------------------------------------------------------------------------
1 | v20.15.1
2 |
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | github: [yoavniran]
4 | patreon: # Replace with a single Patreon username
5 | open_collective: react-uploady
6 | ko_fi: # Replace with a single Ko-fi username
7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
9 | liberapay: # Replace with a single Liberapay username
10 | issuehunt: # Replace with a single IssueHunt username
11 | otechie: # Replace with a single Otechie username
12 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
13 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a bug report
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 | ---
8 |
9 | > Recommended - Check out the [discussions area](https://github.com/rpldy/react-uploady/discussions) before opening a new issue.
10 |
11 | **Describe the bug**
12 | A clear and concise description of what the bug is.
13 |
14 | **To Reproduce**
15 | Steps to reproduce the behavior:
16 | 1. Go to '...'
17 | 2. Click on '....'
18 | 3. Scroll down to '....'
19 | 4. See error
20 |
21 | **Expected behavior**
22 | A clear and concise description of what you expected to happen.
23 |
24 | **Versions**
25 | Specify the versions of the @rpldy packages you're using.
26 | Which browser this bug reproduces in.
27 |
28 | **Code**
29 | Please provide code sample or better yet, a link to a reproducing repository or best yet, codesandbox (fiddle, etc) where issue can be reproduced easily
30 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for this project
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | > Recommended - Check out the [discussions area](https://github.com/rpldy/react-uploady/discussions) before opening a new issue.
11 |
12 |
13 | **Is your feature request related to a problem? Please describe.**
14 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
15 |
16 | **Describe the solution you'd like**
17 | A clear and concise description of what you want to happen.
18 |
19 | **Describe alternatives you've considered**
20 | A clear and concise description of any alternative solutions or features you've considered.
21 |
22 | **Additional context**
23 | Add any other context or screenshots about the feature request here.
24 |
--------------------------------------------------------------------------------
/.github/actions/actions-token-unused/action.yml:
--------------------------------------------------------------------------------
1 | name: GH Actions Tokens
2 | description: Retrieve env variables for GH Actions
3 |
4 | outputs:
5 | ACTIONS_RUNTIME_TOKEN:
6 | description: The runtime token for GH Actions
7 | value: ${{ steps.get-gh-actions-tokens.outputs.runtimeToken }}
8 | ACTIONS_RESULTS_URL:
9 | description: The results URL for GH Actions
10 | value: ${{ steps.get-gh-actions-tokens.outputs.resultsUrl }}
11 |
12 | runs:
13 | using: composite
14 | steps:
15 | - name: Get GH Actions Tokens
16 | id: get-gh-actions-tokens
17 | uses: actions/github-script@v7
18 | with:
19 | script: |
20 | const script = require('./.github/actions/actions-token/index.js');
21 | return await script({ fetch, github, context, core });
22 |
--------------------------------------------------------------------------------
/.github/actions/actions-token-unused/index.js:
--------------------------------------------------------------------------------
1 |
2 | module.exports = async ({ core }) => {
3 | const runtimeToken = process.env.ACTIONS_RUNTIME_TOKEN;
4 | core.setOutput("runtimeToken", runtimeToken);
5 |
6 | const resultsUrl = process.env.ACTIONS_RESULTS_URL;
7 | core.setOutput("resultsUrl", resultsUrl);
8 |
9 | core.info("Got GH Actions Tokens and set into step output");
10 | };
11 |
--------------------------------------------------------------------------------
/.github/actions/save-versions-to-wf/action.yml:
--------------------------------------------------------------------------------
1 | name: Save Uploady Versions to Workflow
2 | description: Fetch and save Uploady versions to workflow version input
3 |
4 | inputs:
5 | workflow-file:
6 | description: Path to workflow file
7 | required: true
8 | workflow-input:
9 | description: Name of the input to update with version
10 | default: "version"
11 | required: false
12 |
13 | runs:
14 | using: "node20"
15 | main: "index.js"
16 |
--------------------------------------------------------------------------------
/.github/actions/versionLog/action.yml:
--------------------------------------------------------------------------------
1 | name: "Extract Version Changelog"
2 | description: "Take the log information from the changelog"
3 | outputs:
4 | VERSION:
5 | description: "The new semver version to be released"
6 | VERSION_LOG:
7 | description: "The log info for the new release"
8 | runs:
9 | using: "node20"
10 | main: "index.js"
11 |
--------------------------------------------------------------------------------
/.github/actions/versionLog/index.js:
--------------------------------------------------------------------------------
1 | const core = require("@actions/core"),
2 | { extractChangelogNotesForCurrentVersion } = require("../../../scripts/extractChangeLogNotes");
3 |
4 | const extractVersionLog = async () => {
5 | try {
6 | console.log("about to retrieve version info from CHANGELOG");
7 | const { version, versionLog } = await extractChangelogNotesForCurrentVersion();
8 |
9 | if (!versionLog) {
10 | core.setFailed(`Failed to retrieve log info for ${version}`);
11 | } else {
12 | core.setOutput("VERSION", version);
13 | core.setOutput("VERSION_LOG", versionLog);
14 |
15 | console.log(`Retrieved version ${version} log = `, versionLog);
16 | }
17 | } catch (ex) {
18 | core.setFailed(ex.message);
19 | }
20 | };
21 |
22 | extractVersionLog();
23 |
24 | // github = require("@actions/github"),
25 | //core.getInput('who-to-greet');
26 |
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | # To get started with Dependabot version updates, you'll need to specify which
2 | # package ecosystems to update and where the package manifests are located.
3 | # Please see the documentation for all configuration options:
4 | # https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
5 |
6 | version: 2
7 | updates:
8 | - package-ecosystem: "npm"
9 | directory: "/"
10 | schedule:
11 | interval: "monthly"
12 |
--------------------------------------------------------------------------------
/.github/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "uploady github workflows",
3 | "version": "1.0.0",
4 | "private": true,
5 | "dependencies": {
6 | "@actions/core": "^1.10.0",
7 | "@actions/github": "^6.0.0",
8 | "js-yaml": "^4.1.0"
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/.github/workflows/stalebot.yml:
--------------------------------------------------------------------------------
1 | name: 'Close stale issues'
2 | on:
3 | schedule:
4 | - cron: '30 1 * * *'
5 | workflow_dispatch:
6 |
7 | permissions:
8 | issues: write
9 |
10 | jobs:
11 | stale:
12 | runs-on: ubuntu-latest
13 | steps:
14 | - uses: actions/stale@v8
15 | with:
16 | days-before-stale: 10
17 | days-before-close: 5
18 | stale-issue-message: "It's been a while. Waiting for an update... 🧐"
19 | close-issue-message: "It's been too long. Closing issue for now. 😿"
20 | stale-issue-label: 'stale'
21 | exempt-issue-labels: 'in-progress, no-stale'
22 | remove-issue-stale-when-updated: true
23 | remove-stale-when-updated: true
24 |
--------------------------------------------------------------------------------
/.github/workflows/storybook-publish.yml:
--------------------------------------------------------------------------------
1 | name: Uploady Storybook Publish
2 |
3 | on:
4 | workflow_dispatch:
5 | push:
6 | branches:
7 | - master
8 |
9 | defaults:
10 | run:
11 | shell: bash
12 |
13 | permissions:
14 | contents: read
15 |
16 | jobs:
17 | publish:
18 | name: Publish Uploady Storybook
19 | runs-on: ubuntu-latest
20 | steps:
21 | - uses: actions/checkout@v4
22 |
23 | - name: Prepare
24 | uses: ./.github/actions/prepare
25 |
26 | - name: Build Storybook Site
27 | run: pnpm sb:build:prod
28 |
29 | - name: Install Netlify CLI
30 | run: pnpm install -w netlify-cli
31 |
32 | - name: Publish Storybook to Netlify
33 | env:
34 | NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
35 | NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE_ID }}
36 | run: |
37 | pnpm netlify deploy --prod --debug --cwd .sb-static
38 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea/
2 | .vscode/
3 | .DS_Store
4 | .jest-cache/
5 | .jest-coverage/
6 | .sb-static/
7 | .env
8 | .eslintcache
9 |
10 | reports/
11 | coverage
12 | node_modules
13 | stats.json
14 | dist
15 | output
16 | build
17 | bundle/
18 | packages/**/umd
19 | .history
20 | storybook-static
21 | npm-debug.log
22 | lerna-debug.log
23 | yarn-error.log
24 |
25 | jsexample.js
26 | jsexample2.js
27 | test.js
28 | todo.md
29 |
30 | packages/**/lib
31 | packages/**/.flowconfig
32 | packages/**/LICENSE.md
33 |
34 | /test.js
35 | /experiment.js
36 |
37 | cypress/videos/
38 | cypress/screenshots/
39 | cypress/examples
40 | cypress/results/
41 | runner-results/
42 |
43 | tstest/
44 |
45 | server/tusFiles/
46 | test/react-uploady-ssr/
47 |
48 | cypress/test-bundle
49 |
50 | # Local Netlify folder
51 | .netlify
52 | .nx/
53 | /cypress/downloads
54 | /test.mjs
55 | test/test-bundle-size.mjs
56 |
57 | flow-bin-doc.txt
58 |
--------------------------------------------------------------------------------
/.husky/commit-msg:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | . "$(dirname "$0")/_/husky.sh"
3 |
4 | npx --no -- commitlint --edit "$1"
5 |
--------------------------------------------------------------------------------
/.husky/pre-commit:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | . "$(dirname "$0")/_/husky.sh"
3 |
4 | pnpm flow && pnpm lint --quiet
5 |
--------------------------------------------------------------------------------
/.husky/pre-push:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | . "$(dirname "$0")/_/husky.sh"
3 |
4 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | src/
2 | *.story.js
3 | *.stories.js
4 | *.test.js
5 | tests/
6 | mocks/
7 | lerna.json
8 | *.log
9 | jest*
10 | .flowconfig
11 | .editorconfig
12 | .eslintignore
13 | .eslintrc.js
14 | .circleci/
15 | .sb-static/
16 | .env
17 | .jest-cache/
18 | .jest-coverage/
19 | coverage/
20 | .github/
21 |
22 |
--------------------------------------------------------------------------------
/.npmrc:
--------------------------------------------------------------------------------
1 | # pnpm - flow-bin requires hoisting of deps
2 | public-hoist-pattern[]=invariant
3 | public-hoist-pattern[]=html-dir-content
4 | public-hoist-pattern[]=react-dnd
5 | public-hoist-pattern[]=react-image-crop
6 |
--------------------------------------------------------------------------------
/.nvmrc:
--------------------------------------------------------------------------------
1 | v20.15.1
2 |
--------------------------------------------------------------------------------
/.storybook/VersionBadge.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import styled from "styled-components";
3 |
4 | const Badge = styled.span`
5 | font-weight: bold;
6 | background-color: #0a173c;
7 | border-radius: 6px;
8 | padding: 2px 4px;
9 | font-size: 10px;
10 | top: -2px;
11 | position: relative;
12 | color: #ffffff;
13 | margin-left: 4px;
14 | `;
15 |
16 | const VersionBadge = ({ pkg, preText = "", className, withUrl, versions }) => {
17 | const info = !!pkg &&
18 | versions.find(({ name }) => name === pkg);
19 |
20 | const badge = info ?
23 | {preText}{info.version}
24 | : null;
25 |
26 | return info &&
27 | (withUrl ?
28 | {badge} :
33 | badge);
34 | };
35 |
36 | export default VersionBadge;
37 |
--------------------------------------------------------------------------------
/.storybook/cypressAddon/cypressDecorator.ts:
--------------------------------------------------------------------------------
1 | import { makeDecorator } from "@storybook/preview-api";
2 |
3 | export default makeDecorator({
4 | name: "cypressDecorator",
5 | wrapper: (getStory, context) => {
6 | const win = (window.parent && window.parent.Cypress) ?
7 | window.parent :
8 | (window.Cypress ? window : null);
9 |
10 | if (win) {
11 | win.__cypressEnv = process.env.NODE_ENV;
12 | win.__cypressResults = win.__cypressResults || { storyLog: []};
13 |
14 | //clear story log on each story render
15 | win.__cypressResults.storyLog = [];
16 | delete win.__extUploadOptions;
17 | delete win.__extPreSendOptions;
18 |
19 | //TODO: this isnt a good practice to keep these special params in different places in the code. Need to refactor to cleaner solution
20 | }
21 |
22 | return getStory(context);
23 | },
24 | parameterName: "cypressDecorator",
25 | skipIfNoParametersOrOptions: false,
26 | });
27 |
--------------------------------------------------------------------------------
/.storybook/manager.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { addons } from "@storybook/manager-api";
3 | import theme from "./theme";
4 | import VersionBadge from "./VersionBadge";
5 |
6 | addons.setConfig({
7 | // isFullscreen: false,
8 | // showAddonsPanel: true,
9 | // panelPosition: 'right',
10 | selectedPanel: "REACT_STORYBOOK/readme/panel",
11 | theme,
12 | sidebar: {
13 | renderLabel: ({ name }) => {
14 | //storybook doesnt pass all the CSF info for isComponent items :(
15 | const pkg = window._storyToPackage?.[name];
16 |
17 | //before SB7 it was possible to add the Versions through the manager's webpack build but no longer... :(
18 | const versions = document.getElementById("storybook-preview-iframe").contentWindow._getPackageVersions?.();
19 |
20 | return (
21 | {name}
22 | {pkg && versions &&
23 | }
27 |
);
28 | },
29 | }
30 | });
31 |
--------------------------------------------------------------------------------
/.storybook/preview-head.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.storybook/theme.ts:
--------------------------------------------------------------------------------
1 | import { create } from "@storybook/theming";
2 |
3 | export default create({
4 | base: "light",
5 |
6 | colorPrimary: "#092358",
7 | colorSecondary: "#1b5c85",
8 | appBg: "#dbdbdb",
9 | inputBorder: "#15b2d4",
10 | // textColor: 'black',
11 | textInverseColor: "#fff",
12 | barTextColor: "#273f3f",
13 | // barSelectedColor: 'black',
14 | // barBg: 'hotpink',
15 |
16 | // inputBg: 'white',
17 | // inputTextColor: 'black',
18 | // inputBorderRadius: 4,
19 |
20 | brandUrl: "https://react-uploady.org",
21 | brandImage: "https://res.cloudinary.com/yoav-cloud/image/upload/w_300/v22212321/rpldy/logo/react-uploady-text-logo.png",
22 | });
23 |
--------------------------------------------------------------------------------
/.storybook/welcome.storydoc.mdx:
--------------------------------------------------------------------------------
1 | import { Meta } from "@storybook/blocks";
2 | import { WelcomeReactUploady } from "../story-helpers/Welcome";
3 |
4 |
5 |
6 |
7 |
8 |
17 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2024 Yoav Niran
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/SECURITY.md:
--------------------------------------------------------------------------------
1 | # Security Policy
2 |
3 | ## Supported Versions
4 |
5 | The following table describes the versions of this project that are currently supported with security updates:
6 |
7 | | Version | Supported |
8 | | ------- | ------------------ |
9 | | 1.x | :white_check_mark: |
10 | | 0.x | :x: |
11 |
12 | ## Reporting a Vulnerability
13 |
14 | We consider the security a top priority and will work to resolve any vulnerabilities.
15 |
16 | If you believe you have found a security vulnerability in React-Uploady, we encourage you to report it to us know right away.
17 | We will investigate all credible reports, and do our best to quickly fix the issue.
18 |
19 | To report, simply open a new [isuse](https://github.com/rpldy/react-uploady/issues)
20 |
21 | We appreciate any assistance and support by reporting issues quickly and responsibly.
22 |
--------------------------------------------------------------------------------
/bundle-size-report.json:
--------------------------------------------------------------------------------
1 | [{"name":"all","size":"23.66KB","max":25000,"success":true},{"name":"core","size":"10.63KB","max":12000,"success":true},{"name":"ui-core","size":"13.57KB","max":15000,"success":true},{"name":"ui-core-chunked","size":"16.04KB","max":17500,"success":true}]
--------------------------------------------------------------------------------
/codecov.yml:
--------------------------------------------------------------------------------
1 | comment:
2 | layout: "reach, diff, flags, files"
3 | behavior: default
4 | require_changes: false # if true: only post the comment if coverage changes
5 | require_base: no # [yes :: must have a base report to post]
6 | require_head: yes # [yes :: must have a head report to post]
7 | branches: # branch names that can post comment
8 | - "master"
9 |
--------------------------------------------------------------------------------
/commitlint.config.mjs:
--------------------------------------------------------------------------------
1 | export default {
2 | extends: [
3 | "@commitlint/config-conventional",
4 | "@commitlint/config-lerna-scopes"
5 | ],
6 |
7 | "rules": {
8 | "header-max-length": [2, "always", 120],
9 |
10 | }
11 | };
12 |
--------------------------------------------------------------------------------
/cypress/constants.js:
--------------------------------------------------------------------------------
1 | export const DEFAULT_URL = "http://test.upload/url";
2 |
3 | export const UPLOAD_URL = Cypress.env("UPLOAD_URL") || DEFAULT_URL;
4 |
5 | export const DEFAULT_METHOD = "POST";
6 |
7 | export const WAIT_X_SHORT = 200;
8 |
9 | export const WAIT_SHORT = 800;
10 |
11 | export const WAIT_MEDIUM = 1500;
12 |
13 | export const WAIT_LONG = 2500;
14 |
15 | export const WAIT_X_LONG = 3000;
16 |
17 | export const BATCH_ADD = /BATCH_ADD/;
18 |
19 | export const BATCH_ERROR = /BATCH_ERROR/;
20 |
21 | export const BATCH_FINALIZE = /BATCH_FINALIZE/;
22 |
23 | export const BATCH_ABORT = /BATCH_ABORT/;
24 |
25 | export const BATCH_PROGRESS = /BATCH_PROGRESS/;
26 |
27 | export const ITEM_START = /ITEM_START/;
28 |
29 | export const ITEM_ABORT = /ITEM_ABORT/;
30 |
31 | export const ITEM_PROGRESS = /ITEM_PROGRESS/;
32 |
33 | export const ITEM_FINISH = /ITEM_FINISH/;
34 |
35 | export const ITEM_ERROR = /ITEM_ERROR/;
36 |
37 | export const ITEM_CANCEL = /ITEM_CANCEL/;
38 |
39 | export const CHUNK_START = /CHUNK_START/;
40 |
41 | export const CHUNK_FINISH = /CHUNK_FINISH/;
42 |
43 | export const ALL_ABORT = /ALL_ABORT/;
44 |
--------------------------------------------------------------------------------
/cypress/fixtures/flower.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rpldy/react-uploady/2c92e126c8b2e583fc6180b036e49d62e3e00a5c/cypress/fixtures/flower.jpg
--------------------------------------------------------------------------------
/cypress/fixtures/sea.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rpldy/react-uploady/2c92e126c8b2e583fc6180b036e49d62e3e00a5c/cypress/fixtures/sea.jpg
--------------------------------------------------------------------------------
/cypress/integration/chunked-uploady/ChunkedUploady-custom-success-spec.js:
--------------------------------------------------------------------------------
1 | import { interceptWithHandler } from "../intercept";
2 | import uploadFile from "../uploadFile";
3 |
4 | describe("ChunkedUploady - Custom Success Callback", () => {
5 | const fileName = "flower.jpg";
6 |
7 | before(() => {
8 | cy.visitStory(
9 | "chunkedUploady",
10 | "simple",
11 | { useMock: false, chunkSize: 50000 }
12 | );
13 | });
14 |
15 | it("should work with custom success code", () => {
16 | interceptWithHandler((req) => {
17 | req.reply(308, { success: true });
18 | });
19 |
20 | cy.setUploadOptions({ isSuccessfulCall: (xhr) => xhr.status === 308 });
21 |
22 | uploadFile(fileName, () => {
23 | cy.waitShort();
24 |
25 | cy.storyLog().assertFileItemStartFinish(fileName, 1);
26 | });
27 | });
28 | });
29 |
--------------------------------------------------------------------------------
/cypress/integration/dropFile.js:
--------------------------------------------------------------------------------
1 | import selectFiles from "./selectFiles";
2 |
3 | const dropFile = (fixtureName, cb, dropZone = "#upload-drop-zone", options = {}) => {
4 | selectFiles(
5 | fixtureName,
6 | dropZone,
7 | "uploadDropZone",
8 | cb,
9 | { ...options, action: "drag-drop", aliasAsInput: true }
10 | );
11 | };
12 |
13 | export const dropFiles = (fixtureName, times, cb, dropZone = "#upload-drop-zone", options = {}) =>
14 | dropFile(fixtureName, cb, dropZone, { ...options, times });
15 |
16 | export default dropFile;
17 |
--------------------------------------------------------------------------------
/cypress/integration/native-uploady/NativeUploady-simple-spec.js:
--------------------------------------------------------------------------------
1 | import uploadFile from "../uploadFile";
2 |
3 | describe("NativeUploady - Simple", () => {
4 | const fileName = "flower.jpg";
5 |
6 | before(() => {
7 | cy.visitStory(
8 | "nativeUploady",
9 | "simple",
10 | { mockDelay: 100 }
11 | );
12 | });
13 |
14 | it("should use native uploady", () => {
15 | uploadFile(fileName, () => {
16 | cy.waitExtraShort();
17 | cy.storyLog().assertFileItemStartFinish(fileName, 1);
18 | });
19 | });
20 | });
21 |
--------------------------------------------------------------------------------
/cypress/integration/tus-uploady/clearTusPersistStorage.js:
--------------------------------------------------------------------------------
1 | const clearTusPersistStorage = () => {
2 | for (let i = 0; i < localStorage.length; i++) {
3 | const key = localStorage.key(i);
4 |
5 | if (key && !key.startsWith("__rpldy-tus__")) {
6 | localStorage.removeItem(key);
7 | }
8 | }
9 | };
10 |
11 | export default clearTusPersistStorage;
12 |
--------------------------------------------------------------------------------
/cypress/integration/umd/core-ui-umd-spec.js:
--------------------------------------------------------------------------------
1 | import intercept from "../intercept";
2 | import uploadFile from "../uploadFile";
3 | import { ITEM_START, BATCH_ADD } from "../../constants";
4 |
5 | describe("UMD UI CORE - Bundle", () => {
6 | const fileName = "flower.jpg";
7 |
8 | beforeEach(() => {
9 | cy.visitStory("uploady", "umd-core-ui", { useMock: false });
10 | });
11 |
12 | it("should use uploady and upload file", () => {
13 | intercept();
14 |
15 | uploadFile(fileName, () => {
16 | cy.wait("@uploadReq")
17 | .interceptFormData((formData) => {
18 | expect(formData["file"]).to.eq(fileName);
19 | })
20 | .its("response.statusCode")
21 | .should("eq", 200);
22 |
23 | cy.storyLog().assertLogPattern(BATCH_ADD, { times: 1 });
24 | cy.storyLog().assertLogPattern(ITEM_START, { times: 1 });
25 | }, "#upload-button");
26 | });
27 | });
28 |
--------------------------------------------------------------------------------
/cypress/integration/umd/core-umd-spec.js:
--------------------------------------------------------------------------------
1 | import intercept from "../intercept";
2 | import uploadFile from "../uploadFile";
3 | import { ITEM_START, BATCH_ADD } from "../../constants";
4 |
5 | describe("UMD Core - Bundle", () => {
6 | const fileName = "flower.jpg";
7 |
8 | beforeEach(() => {
9 | cy.visitStory("uploader", "umd-core", { useMock: false });
10 | });
11 |
12 | it("should use uploady with uploader", () => {
13 | intercept();
14 |
15 | uploadFile(fileName, () => {
16 | cy.wait("@uploadReq")
17 | .interceptFormData((formData) => {
18 | expect(formData["file"]).to.eq(fileName);
19 | })
20 | .its("response.statusCode").should("eq", 200);
21 |
22 | cy.storyLog().assertLogPattern(BATCH_ADD, { times: 1 });
23 | cy.storyLog().assertLogPattern(ITEM_START, { times: 1 });
24 | }, "#upload-button");
25 | });
26 | });
27 |
--------------------------------------------------------------------------------
/cypress/integration/upload-button/UploadButton-asButton-spec.js:
--------------------------------------------------------------------------------
1 | import uploadFile from "../uploadFile";
2 |
3 | describe("UploadButton - With Component asButton", () => {
4 | const fileName = "flower.jpg";
5 |
6 | before(() => {
7 | cy.visitStory("uploadButton", "with-component-as-button");
8 | });
9 |
10 | it("should make any custom component an upload button", () => {
11 | uploadFile(fileName, () => {
12 | cy.waitMedium();
13 | cy.storyLog().assertFileItemStartFinish(fileName, 1);
14 | }, "#div-upload");
15 | });
16 | });
17 |
--------------------------------------------------------------------------------
/cypress/integration/upload-button/UploadButton-customInputAndForm-spec.js:
--------------------------------------------------------------------------------
1 | import intercept from "../intercept";
2 | import uploadFile from "../uploadFile";
3 |
4 | describe("UploadButton - With Custom File Input And Form", () => {
5 | const fileName = "flower.jpg";
6 |
7 | before(() => {
8 | cy.visitStory("uploadButton", "with-custom-file-input-and-form");
9 | });
10 |
11 | it("should use form attributes ", () => {
12 | intercept("http://react-uploady-dummy-server.comm");
13 |
14 | uploadFile(fileName, () => {
15 | cy.wait("@uploadReq").then((xhr) => {
16 | expect(xhr.response.body.success).to.eq(true);
17 | });
18 | });
19 | });
20 | });
21 |
--------------------------------------------------------------------------------
/cypress/integration/upload-button/UploadButton-differentConfig-spec.js:
--------------------------------------------------------------------------------
1 | import uploadFile from "../uploadFile";
2 |
3 | describe("UploadButton - Different Configuration", () => {
4 | const fileName = "flower.jpg";
5 |
6 | before(() => {
7 | cy.visitStory("uploadButton", "different-configuration");
8 | });
9 |
10 | it("should allow overriding upload options from button", () => {
11 | //test button with autoUpload = false
12 | uploadFile(fileName, () => {
13 | cy.waitExtraShort();
14 | cy.storyLog().assertLogEntryCount(1);
15 | }, "#upload-a");
16 |
17 | //test other button with custom destination header
18 | uploadFile(fileName, () => {
19 | cy.waitExtraShort();
20 |
21 | cy.storyLog().assertLogEntryContains(1, {
22 | destination: {
23 | url: "http://react-uploady-dummy-server.comm",
24 | headers: {
25 | "x-test": "1234"
26 | }
27 | }
28 | });
29 | }, "#upload-b");
30 | });
31 | });
32 |
--------------------------------------------------------------------------------
/cypress/integration/upload-button/UploadButton-disabled-spec.js:
--------------------------------------------------------------------------------
1 | import uploadFile from "../uploadFile";
2 |
3 | describe("UploadButton - Disabled During Upload", () => {
4 | const fileName = "flower.jpg";
5 |
6 | before(() => {
7 | cy.visitStory("uploadButton", "disabled-during-upload");
8 | });
9 |
10 | it("should disable upload button during upload", () => {
11 | cy.get("input")
12 | .should("exist")
13 | .as("fInput");
14 |
15 | uploadFile(fileName, () => {
16 | cy.waitExtraShort();
17 | cy.get("@uploadButton").should("be.disabled");
18 |
19 | cy.waitMedium();
20 | cy.storyLog().assertFileItemStartFinish(fileName, 1);
21 | cy.get("@uploadButton").should("not.be.disabled");
22 | });
23 | });
24 | });
25 |
--------------------------------------------------------------------------------
/cypress/integration/upload-button/UploadButton-eventHooks-spec.js:
--------------------------------------------------------------------------------
1 | import uploadFile from "../uploadFile";
2 |
3 | describe("UploadButton - With Event Hooks", () => {
4 | const fileName = "flower.jpg";
5 |
6 | before(() => {
7 | cy.visitStory("uploadButton", "with-event-hooks", { mockDelay: 100 });
8 | });
9 |
10 | it("should use event hooks", () => {
11 | uploadFile(fileName, () => {
12 | cy.waitExtraShort();
13 |
14 | cy.get("ul[data-test='hooks-events']")
15 | .should("be.visible")
16 | .as("eventsLog");
17 |
18 | const eventsItems = cy.get("@eventsLog").find("li");
19 |
20 | eventsItems.first()
21 | .should("contain", "hooks: Batch Start - batch-1 - item count = 1")
22 | .next()
23 | .should("contain", `hooks: Item Start - batch-1.item-1 : ${fileName}`)
24 | .next()
25 | .should("contain", `hooks: Item Finish - batch-1.item-1 : ${fileName}`)
26 | .next()
27 | .should("contain", "hooks: Batch Finish - batch-1 - item count = 1");
28 | });
29 | });
30 | });
31 |
--------------------------------------------------------------------------------
/cypress/integration/upload-button/UploadButton-eventListeners-spec.js:
--------------------------------------------------------------------------------
1 | import uploadFile from "../uploadFile";
2 |
3 | describe("UploadButton - With Event Listeners", () => {
4 | const fileName = "flower.jpg";
5 |
6 | before(() => {
7 | cy.visitStory("uploadButton", "with-event-listeners", { mockDelay: 100 });
8 | });
9 |
10 | it("should use event listeners", () => {
11 | uploadFile(fileName, () => {
12 | cy.waitExtraShort();
13 |
14 | cy.get("ul[data-test='hooks-events']")
15 | .should("be.visible")
16 | .as("eventsLog");
17 |
18 | const eventsItems = cy.get("@eventsLog").find("li");
19 |
20 | eventsItems.first()
21 | .should("contain", "Batch Start - batch-1 - item count = 1")
22 | .next()
23 | .should("contain", `Item Start - batch-1.item-1 : ${fileName}`)
24 | .next()
25 | .should("contain", `Item Finish - batch-1.item-1 : ${fileName}`)
26 | .next()
27 | .should("contain", "Batch Finish - batch-1 - item count = 1");
28 | });
29 | });
30 | });
31 |
--------------------------------------------------------------------------------
/cypress/integration/upload-button/UploadButton-simple-multiple-spec.js:
--------------------------------------------------------------------------------
1 | import { uploadFileTimes } from "../uploadFile";
2 |
3 | describe("UploadButton - Simple - Multiple files", () => {
4 | const fileName = "flower.jpg";
5 |
6 | before(() => {
7 | cy.visitStory(
8 | "uploadButton",
9 | "simple",
10 | { mockDelay: 100 }
11 | );
12 | });
13 |
14 | it("should use uploady to upload multiple files", () => {
15 | cy.get("input")
16 | .should("exist")
17 | .as("fInput");
18 |
19 | uploadFileTimes(fileName, () => {
20 | cy.waitMedium();
21 |
22 | cy.storyLog().assertFileItemStartFinish(fileName, 1);
23 | cy.storyLog().assertFileItemStartFinish("flower2.jpg", 3);
24 | cy.storyLog().assertFileItemStartFinish("flower3.jpg", 5);
25 | }, 3);
26 | });
27 | });
28 |
--------------------------------------------------------------------------------
/cypress/integration/upload-button/UploadButton-simple-spec.js:
--------------------------------------------------------------------------------
1 | import uploadFile from "../uploadFile";
2 |
3 | describe("UploadButton - Simple", () => {
4 | const fileName = "flower.jpg";
5 |
6 | before(() => {
7 | cy.visitStory(
8 | "uploadButton",
9 | "simple",
10 | { mockDelay: 100 }
11 | );
12 | });
13 |
14 | it("should use uploady", () => {
15 | cy.get("input")
16 | .should("exist")
17 | .as("fInput");
18 |
19 | uploadFile(fileName, () => {
20 | cy.waitShort();
21 | cy.storyLog().assertFileItemStartFinish(fileName, 1);
22 | }, "button");
23 | });
24 | });
25 |
--------------------------------------------------------------------------------
/cypress/integration/upload-button/UploadButton-styled-spec.js:
--------------------------------------------------------------------------------
1 | import uploadFile from "../uploadFile";
2 |
3 | describe("UploadButton - With Styled Component", () => {
4 | const fileName = "flower.jpg";
5 |
6 | before(() => {
7 | cy.visitStory(
8 | "uploadButton",
9 | "with-styled-component",
10 | { mockDelay: 100 }
11 | );
12 | });
13 |
14 | it("should be styled with styled-components", () => {
15 | uploadFile(fileName, () => {
16 | cy.get("@uploadButton")
17 | .should("have.css", "background-color", "rgb(1, 9, 22)")
18 | .should("have.css", "color", "rgb(176, 177, 179)");
19 |
20 | cy.waitShort();
21 | cy.storyLog().assertFileItemStartFinish(fileName, 1);
22 | });
23 | });
24 | });
25 |
--------------------------------------------------------------------------------
/cypress/integration/upload-drop-zone/UploadDropZone-custom-remove-spec.js:
--------------------------------------------------------------------------------
1 | describe("UploadDropZone - Custom Remove", () => {
2 | before(() => {
3 | cy.visitStory("uploadDropZone", "with-full-screen");
4 | });
5 |
6 | it("should remove the drag overlay", () => {
7 | cy.get("#upload-drop-zone")
8 | .trigger("dragenter", { dataTransfer: { items: [ { kind: "file" } ] } });
9 |
10 | cy.get(".dropIndicator")
11 | .should("be.visible");
12 |
13 | cy.get(".dropIndicator")
14 | .trigger("dragleave");
15 |
16 | cy.get(".dropIndicator")
17 | .should("not.be.visible");
18 | });
19 | });
20 |
--------------------------------------------------------------------------------
/cypress/integration/upload-drop-zone/UploadDropZone-differentConfig-spec.js:
--------------------------------------------------------------------------------
1 | import dropFile from "../dropFile";
2 |
3 | describe("UploadDropZone - Different Config", () => {
4 | const fileName = "flower.jpg";
5 |
6 | before(() => {
7 | cy.visitStory("uploadDropZone", "different-configuration");
8 | });
9 |
10 | it("should allow overriding upload options from dropzone", () => {
11 | //test button with autoUpload = false
12 | dropFile(fileName, () => {
13 | cy.waitExtraShort();
14 | cy.storyLog().assertLogEntryCount(1);
15 | }, "#upload-dz-a");
16 |
17 | //test other button with custom destination header
18 | dropFile(fileName, () => {
19 | cy.waitExtraShort();
20 |
21 | cy.storyLog().assertLogEntryContains(1, {
22 | destination: {
23 | headers: {
24 | "x-test": "1234"
25 | }
26 | }
27 | });
28 | }, "#upload-dz-b");
29 | });
30 | });
31 |
--------------------------------------------------------------------------------
/cypress/integration/upload-drop-zone/UploadDropZone-dropHandler-spec.js:
--------------------------------------------------------------------------------
1 | import dropFile from "../dropFile";
2 |
3 | describe("UploadDropZone - Drop Handler", () => {
4 | const fileName = "flower.jpg";
5 |
6 | before(() => {
7 | cy.visitStory(
8 | "uploadDropZone",
9 | "with-drop-handler",
10 | { mockDelay: 100 }
11 | );
12 | });
13 |
14 | it("should upload result from drop handler", () => {
15 | dropFile(fileName, () => {
16 | cy.waitShort();
17 | cy.storyLog().assertUrlItemStartFinish("https://i.pinimg.com/originals/51/bf/9c/51bf9c7fdf0d4303140c4949afd1d7b8.jpg", 1);
18 | });
19 | });
20 | });
21 |
--------------------------------------------------------------------------------
/cypress/integration/upload-drop-zone/UploadDropZone-no-handle-spec.js:
--------------------------------------------------------------------------------
1 | import { ITEM_START } from "../../constants";
2 | import dropFile from "../dropFile";
3 |
4 | describe("UploadDropZone - shouldHandleDrag", () => {
5 | const fileName = "flower.jpg";
6 |
7 | before(() => {
8 | cy.visitStory(
9 | "uploadDropZone",
10 | "with-dnd-turned-off",
11 | { mockDelay: 100 }
12 | );
13 | });
14 |
15 | it("should not do drop when shouldHandleDrag = false", () => {
16 | dropFile(fileName, () => {
17 | cy.waitExtraShort();
18 | cy.storyLog().assertNoLogPattern(ITEM_START);
19 | });
20 | });
21 | });
22 |
--------------------------------------------------------------------------------
/cypress/integration/upload-drop-zone/UploadDropZone-simple-no-contain-check-spec.js:
--------------------------------------------------------------------------------
1 | describe("UploadDropZone - No Contain Check", () => {
2 | before(() => {
3 | cy.visitStory("uploadDropZone", "with-aria-modal-overlay", );
4 | });
5 |
6 | it("should work with aria modal", () => {
7 | cy.get("#aria-modal-modal")
8 | .trigger("dragenter", { dataTransfer: { items: [ { kind: "file" } ] } });
9 |
10 | cy.get(".dropIndicator")
11 | .should("be.visible");
12 |
13 | cy.get(".dropIndicator")
14 | .trigger("dragleave");
15 |
16 | cy.get(".dropIndicator")
17 | .should("not.be.visible");
18 | });
19 | });
20 |
--------------------------------------------------------------------------------
/cypress/integration/upload-drop-zone/UploadDropZone-simple-spec.js:
--------------------------------------------------------------------------------
1 | import dropFile from "../dropFile";
2 |
3 | describe("UploadDropZone - Simple", () => {
4 | const fileName = "flower.jpg";
5 |
6 | before(() => {
7 | cy.visitStory("uploadDropZone", "simple", { mockDelay: 100 });
8 | });
9 |
10 | it("should upload dropped file", () => {
11 | dropFile(fileName, () => {
12 | cy.waitExtraShort();
13 | cy.storyLog().assertFileItemStartFinish(fileName, 1);
14 | });
15 | });
16 | });
17 |
--------------------------------------------------------------------------------
/cypress/integration/upload-paste/UploadPaste-simple-spec.js:
--------------------------------------------------------------------------------
1 | describe("UploadPaste - Simple", () => {
2 | const fileName = "flower.jpg";
3 |
4 | beforeEach(() => {
5 | cy.visitStory("uploadPaste", "simple", { mockDelay: 100 });
6 | });
7 |
8 | it("should upload pasted file", () => {
9 | cy.get("#paste-area")
10 | .should("exist")
11 | .pasteFile(fileName);
12 |
13 | cy.waitMedium();
14 | cy.storyLog().assertFileItemStartFinish(fileName, 1);
15 | });
16 |
17 | it("should upload pasted files", () => {
18 | cy.get("#paste-area")
19 | .should("exist")
20 | .pasteFile(fileName, 2);
21 |
22 | cy.waitLong();
23 | cy.storyLog().assertFileItemStartFinish(fileName, 1);
24 | cy.storyLog().assertFileItemStartFinish("flower2.jpg", 3);
25 | });
26 | });
27 |
--------------------------------------------------------------------------------
/cypress/integration/upload-paste/UploadPaste-window-spec.js:
--------------------------------------------------------------------------------
1 | describe("UploadPaste - Window Listener", () => {
2 | const fileName = "flower.jpg";
3 |
4 | const loadPage = () =>
5 | cy.visitStory(
6 | "uploadPaste",
7 | "with-window-paste",
8 | { mockDelay: 100 }
9 | );
10 |
11 | it("should upload pasted file from anywhere on the page", () => {
12 | loadPage();
13 | //wait for body to render first
14 | cy.get("#storybook-root button");
15 |
16 | cy.get("body")
17 | .pasteFile(fileName);
18 |
19 | cy.waitExtraShort();
20 | cy.storyLog().assertFileItemStartFinish(fileName, 1);
21 | });
22 | });
23 |
--------------------------------------------------------------------------------
/cypress/integration/upload-preview/UploadPreview-clear-spec.js:
--------------------------------------------------------------------------------
1 | import uploadFile from "../uploadFile";
2 |
3 | describe("UploadPreview - Clear", () => {
4 | const fileName = "flower.jpg";
5 |
6 | before(() => {
7 | cy.visitStory("uploadPreview", "with-preview-methods");
8 | });
9 |
10 | it("should show upload preview", () => {
11 | uploadFile(fileName, () => {
12 | uploadFile(fileName, () => {
13 | cy.get("img[data-test='upload-preview']")
14 | .should("be.visible")
15 | .invoke("attr", "src")
16 | .should("match", /blob:/);
17 |
18 | cy.get("img[data-test='upload-preview']")
19 | .should("not.have.length", 2);
20 |
21 | cy.get("#clear-btn")
22 | .should("have.text", "Clear 2 previews")
23 | .click();
24 |
25 | cy.get("img[data-test='upload-preview']")
26 | .should("have.length", 0);
27 | }, "#upload-btn");
28 | }, "#upload-btn");
29 | });
30 | });
31 |
--------------------------------------------------------------------------------
/cypress/integration/upload-preview/UploadPreview-progress-spec.js:
--------------------------------------------------------------------------------
1 | import { interceptWithDelay } from "../intercept";
2 | import uploadFile from "../uploadFile";
3 |
4 | describe("UploadPreview - Progress", () => {
5 | const fileName = "flower.jpg";
6 |
7 | beforeEach(() => {
8 | cy.visitStory(
9 | "uploadPreview",
10 | "with-progress",
11 | { useMock: false }
12 | );
13 | });
14 |
15 | it("should show upload preview", () => {
16 | interceptWithDelay(100, "uploadReq");
17 |
18 | uploadFile(fileName, () => {
19 | cy.get(".preview-img")
20 | .should("have.css", "opacity", "0");
21 |
22 | cy.wait(100);
23 | cy.get(".preview-img")
24 | .should("have.css", "opacity", "1");
25 | });
26 | });
27 | });
28 |
--------------------------------------------------------------------------------
/cypress/integration/upload-preview/UploadPreview-simple-fallback-spec.js:
--------------------------------------------------------------------------------
1 | import uploadFile from "../uploadFile";
2 |
3 | describe("UploadPreview - Simple", () => {
4 | const fileName = "flower.jpg";
5 |
6 | before(() => {
7 | cy.visitStory(
8 | "uploadPreview",
9 | "simple",
10 | { customArgs: { maxPreviewSize: 1000 } }
11 | );
12 | });
13 |
14 | it("should show fallback on image > max size", () => {
15 | uploadFile(fileName, () => {
16 | cy.get("img[data-test='upload-preview']")
17 | .should("be.visible")
18 | .invoke("attr", "src")
19 | .should("match", /https:\/\/picsum.photos\/50/);
20 | });
21 | });
22 | });
23 |
--------------------------------------------------------------------------------
/cypress/integration/upload-preview/UploadPreview-simple-multiple-spec.js:
--------------------------------------------------------------------------------
1 | import { uploadFileTimes } from "../uploadFile";
2 |
3 | describe("UploadPreview - Simple - Multiple files", () => {
4 | const fileName = "flower.jpg";
5 |
6 | before(() => {
7 | cy.visitStory("uploadPreview", "simple");
8 | });
9 |
10 | it("should show upload preview for multiple files", () => {
11 | uploadFileTimes(fileName, () => {
12 | cy.waitShort();
13 | cy.get("img[data-test='upload-preview']")
14 | .should("be.visible")
15 | .should("have.length", 3)
16 | .invoke("attr", "src")
17 | .should("match", /blob:/);
18 | }, 3, "button");
19 | });
20 | });
21 |
--------------------------------------------------------------------------------
/cypress/integration/upload-preview/UploadPreview-simple-spec.js:
--------------------------------------------------------------------------------
1 | import uploadFile from "../uploadFile";
2 |
3 | describe("UploadPreview - Simple", () => {
4 | const fileName = "flower.jpg";
5 |
6 | before(() => {
7 | cy.visitStory("uploadPreview", "simple");
8 | });
9 |
10 | it("should show upload preview", () => {
11 | uploadFile(fileName, () => {
12 | uploadFile(fileName, () => {
13 | cy.get("img[data-test='upload-preview']")
14 | .should("be.visible")
15 | .invoke("attr", "src")
16 | .should("match", /blob:/);
17 |
18 | cy.get("img[data-test='upload-preview']")
19 | .should("have.length", 1);
20 | });
21 | });
22 | });
23 | });
24 |
--------------------------------------------------------------------------------
/cypress/integration/upload-preview/examineCroppedUploadReq.js:
--------------------------------------------------------------------------------
1 | const CROPPED_MAX_SIZE = 70_000;
2 |
3 | const examineCroppedUploadReq = (req, name) =>
4 | req.interceptFormData((formData) => {
5 | expect(formData["file"]).to.eq(name);
6 | })
7 | .its("request.headers")
8 | .its("content-length")
9 | .then((length) => {
10 | expect(parseInt(length)).to.be.lessThan(CROPPED_MAX_SIZE);
11 | });
12 |
13 | const examineFullUploadRequest = (req, name) =>
14 | req.interceptFormData((formData) => {
15 | expect(formData["file"]).to.eq(name);
16 | })
17 | .its("request.headers")
18 | .its("content-length")
19 | .then((length) => {
20 | expect(parseInt(length)).to.be.least(37200);
21 | });
22 |
23 | export {
24 | CROPPED_MAX_SIZE,
25 | examineCroppedUploadReq,
26 | examineFullUploadRequest,
27 | };
28 |
--------------------------------------------------------------------------------
/cypress/integration/upload-url-input/UploadUrlInput-simple-spec.js:
--------------------------------------------------------------------------------
1 | import { BATCH_ADD } from "../../constants";
2 |
3 | describe("UploadButton - Simple", () => {
4 |
5 | before(() => {
6 | cy.visitStory(
7 | "uploadUrlInput",
8 | "simple",
9 | { mockDelay: 100 }
10 | );
11 | });
12 |
13 | it("should use uploady", () => {
14 | cy.get("input#url-input")
15 | .should("exist")
16 | .type("http://test.com{enter}");
17 |
18 | cy.waitExtraShort();
19 | cy.storyLog().assertLogPattern(BATCH_ADD);
20 | cy.storyLog().assertUrlItemStartFinish("http://test.com", 1);
21 | });
22 | });
23 |
--------------------------------------------------------------------------------
/cypress/integration/uploadFile.js:
--------------------------------------------------------------------------------
1 | import selectFiles from "./selectFiles";
2 |
3 | const uploadFile = (fixtureName, cb, button = "button", options = { }) => {
4 | const selectorRoot = cy.config("spec").specType !== "component" ? "#storybook-root " : "";
5 |
6 | selectFiles(
7 | fixtureName,
8 | (button === false ? button : `${selectorRoot}${button}`),
9 | "uploadButton",
10 | cb,
11 | { ...options, force: true }
12 | );
13 | };
14 |
15 | export const uploadFileTimes = (fileName, cb, times, button = "button", options = {}, iframe) =>
16 | uploadFile(fileName, cb, button, { ...options, times, }, iframe);
17 |
18 | export default uploadFile;
19 |
--------------------------------------------------------------------------------
/cypress/integration/uploader/Uploader-recover-from-error-spec.js:
--------------------------------------------------------------------------------
1 | import uploadFile from "../uploadFile";
2 | import { ITEM_START, ITEM_ERROR } from "../../constants";
3 |
4 | describe("Uploader - recover from sender error test", () => {
5 | const fileName = "flower.jpg";
6 |
7 | before(() => {
8 | cy.visitStory(
9 | "uploader",
10 | "with-custom-ui",
11 | { uploadUrl: "http://localhost:8439/not-exist" }
12 | );
13 | });
14 |
15 | it("should upload again after unexpected sender error", () => {
16 | uploadFile(fileName, () => {
17 | uploadFile(fileName, () => {
18 | cy.waitMedium();
19 |
20 | cy.storyLog().assertLogPattern(ITEM_START, { times: 2 });
21 | cy.storyLog().assertLogPattern(ITEM_ERROR, { times: 2 });
22 | }, "#upload-button");
23 | }, "#upload-button");
24 | });
25 | });
26 |
--------------------------------------------------------------------------------
/cypress/integration/uploady/Uploady-customResponseFormat-spec.js:
--------------------------------------------------------------------------------
1 | import intercept from "../intercept";
2 | import uploadFile from "../uploadFile";
3 |
4 | describe("Uploady - Custom Response Formatter", () => {
5 | const fileName = "flower.jpg";
6 |
7 | beforeEach(() => {
8 | cy.visitStory(
9 | "uploady",
10 | "with-custom-response-format",
11 | { useMock: false }
12 | );
13 | });
14 |
15 | it("should use custom response formatter function", () => {
16 | intercept();
17 |
18 | uploadFile(fileName, () => {
19 | cy.wait("@uploadReq");
20 |
21 | cy.storyLog().assertLogEntryContains(2, {
22 | uploadResponse: {
23 | data: "200 - Yay!"
24 | }
25 | });
26 | });
27 | });
28 | });
29 |
--------------------------------------------------------------------------------
/cypress/integration/uploady/Uploady-failed-mock-spec.js:
--------------------------------------------------------------------------------
1 | import uploadFile from "../uploadFile";
2 | import { ITEM_ERROR, ITEM_START } from "../../constants";
3 |
4 | describe("Uploady - Failed Mock Send", () => {
5 | const fileName = "flower.jpg";
6 |
7 | before(() => {
8 | cy.visitStory(
9 | "uploady",
10 | "with-failing-mock-sender",
11 | );
12 | });
13 |
14 | it("failed mock send should trigger item error", () => {
15 | uploadFile(fileName, () => {
16 | cy.waitShort();
17 | cy.storyLog().assertLogPattern(ITEM_START);
18 | cy.waitShort();
19 |
20 | cy.storyLog().assertLogPattern(ITEM_ERROR)
21 | .then((matches) => {
22 | const logIndex = matches[0].index;
23 | cy.storyLog().assertLogEntryContains(logIndex, { state: "error" });
24 | });
25 | });
26 | });
27 | });
28 |
--------------------------------------------------------------------------------
/cypress/integration/uploady/Uploady-filesParamName-spec.js:
--------------------------------------------------------------------------------
1 | import intercept from "../intercept";
2 | import uploadFile from "../uploadFile";
3 |
4 | describe("Uploady - filesParamName", () => {
5 | const fileName = "flower.jpg";
6 |
7 | before(() => {
8 | cy.visitStory(
9 | "uploady",
10 | "with-custom-field-name",
11 | { useMock: false }
12 | );
13 | });
14 |
15 | it("should set the files param name to custom value", () => {
16 | intercept();
17 |
18 | uploadFile(fileName, () => {
19 | cy.get("#upload-button")
20 | .click();
21 |
22 | cy.wait("@uploadReq")
23 | .interceptFormData((formData) => {
24 | expect(formData["customFieldName"]).to.equal(fileName);
25 | cy.waitShort();
26 | cy.storyLog().assertFileItemStartFinish(fileName, 1);
27 | });
28 | });
29 | });
30 | });
31 |
--------------------------------------------------------------------------------
/cypress/integration/uploady/Uploady-headerFromPreSend-spec.js:
--------------------------------------------------------------------------------
1 | import intercept from "../intercept";
2 | import uploadFile from "../uploadFile";
3 |
4 | describe("Uploady - Header from RequestPreSend hook", () => {
5 | const fileName = "flower.jpg";
6 |
7 | beforeEach(() => {
8 | cy.visitStory(
9 | "uploady",
10 | "with-header-from-file-name",
11 | { useMock: false }
12 | );
13 | });
14 |
15 | it("should create a header from pre send hook using file name", () => {
16 | intercept();
17 |
18 | uploadFile(fileName, () => {
19 | cy.wait("@uploadReq")
20 | .then((xhr) => {
21 | cy.storyLog().assertLogEntryContains(2, {
22 | uploadResponse: {
23 | data: { success: true }
24 | }
25 | });
26 |
27 | expect(xhr.request.headers["x-file-names-lengths"]).to.equal("10");
28 | });
29 | });
30 | });
31 | });
32 |
--------------------------------------------------------------------------------
/cypress/integration/uploady/Uploady-internal-input-spec.js:
--------------------------------------------------------------------------------
1 | describe("Uploady - Internal Input", () => {
2 | before(() => {
3 | cy.visitStory("uploady", "with-exposed-internal-input");
4 | });
5 |
6 | it("should use internal file input from useFileInput", () => {
7 | cy.get("#select-input-type")
8 | .select("dir");
9 |
10 | cy.get("input[type='file']")
11 | .should("have.attr", "webkitdirectory", "true");
12 | });
13 | });
14 |
--------------------------------------------------------------------------------
/cypress/integration/uploady/Uploady-invalid-request-pre-send-spec.js:
--------------------------------------------------------------------------------
1 | import { ITEM_ERROR, ITEM_FINISH, ITEM_START } from "../../constants";
2 | import intercept from "../intercept";
3 | import { uploadFileTimes } from "../uploadFile";
4 |
5 | describe("Uploady - invalid requestPreSend", () => {
6 | const fileName = "flower.jpg";
7 |
8 | beforeEach(() => {
9 | cy.visitStory(
10 | "uploady",
11 | "test-invalid-pre-send",
12 | { useMock: false }
13 | );
14 | });
15 |
16 | it("should fail invalid updated data from pre send - forbidden item props", () => {
17 | intercept();
18 |
19 | uploadFileTimes(fileName, () => {
20 | cy.waitShort();
21 |
22 | cy.storyLog().assertLogPattern(ITEM_START, { times: 2 });
23 | cy.storyLog().assertLogPattern(ITEM_FINISH, { times: 1 });
24 | cy.storyLog().assertLogPattern(ITEM_ERROR, { times: 1 });
25 | }, 2, "#upload-button");
26 | });
27 | });
28 |
--------------------------------------------------------------------------------
/cypress/integration/uploady/Uploady-pending-with-options-spec.js:
--------------------------------------------------------------------------------
1 | import intercept from "../intercept";
2 | import uploadFile from "../uploadFile";
3 | import { ITEM_FINISH, ITEM_START } from "../../constants";
4 |
5 | describe("Uploady - autoUpload off tests", () => {
6 | const fileName = "flower.jpg";
7 |
8 | before(() => {
9 | cy.visitStory(
10 | "uploady",
11 | "with-auto-upload-off",
12 | { useMock: false }
13 | );
14 | });
15 |
16 | it("should process pending with options", () => {
17 | intercept();
18 |
19 | uploadFile(fileName, () => {
20 | cy.storyLog().assertLogPattern(ITEM_START, { times: 0 });
21 |
22 | cy.get("#process-pending-param")
23 | .click();
24 |
25 | cy.wait("@uploadReq")
26 | .interceptFormData((formData) => {
27 | expect(formData["test"]).to.equal("123");
28 | });
29 |
30 | cy.storyLog().assertLogPattern(ITEM_START, { times: 1 });
31 | cy.storyLog().assertLogPattern(ITEM_FINISH, { times: 1 });
32 | }, "#upload-button");
33 | });
34 | });
35 |
--------------------------------------------------------------------------------
/cypress/plugins/index.js:
--------------------------------------------------------------------------------
1 | ///
2 | // ***********************************************************
3 | // This example plugins/index.js can be used to load plugins
4 | //
5 | // You can change the location of this file or turn off loading
6 | // the plugins file with the 'pluginsFile' configuration option.
7 | //
8 | // You can read more here:
9 | // https://on.cypress.io/plugins-guide
10 | // ***********************************************************
11 |
12 | // This function is called when a project is opened or re-opened (e.g. due to
13 | // the project's config changing)
14 |
15 | // /**
16 | // * @type {Cypress.PluginConfig}
17 | // */
18 | // module.exports = (on, config) => {
19 | // // `on` is used to hook into various events Cypress emits
20 | // // `config` is the resolved Cypress config
21 | // }
22 |
--------------------------------------------------------------------------------
/cypress/reporters-config.json-old:
--------------------------------------------------------------------------------
1 | {
2 | "reporterEnabled": "json",
3 | "reporterOptions": {
4 | "mochaFile": "cypress/results/results-[hash].xml",
5 | "output-file": "cypress/results/json/results-[hash].json"
6 | },
7 | "jsonReporterReporterOptions": {
8 | "output-file": "cypress/results/json/results-[hash].json"
9 | },
10 | "mochaJunitReporterReporterOptions": {
11 | "mochaFile": "cypress/results/results-[suiteName].xml",
12 | "toConsole": true
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/cypress/support/component-index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Components App
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/cypress/support/component.js:
--------------------------------------------------------------------------------
1 | // ***********************************************************
2 | // This example support/component.js is processed and
3 | // loaded automatically before your test files.
4 | //
5 | // This is a great place to put global configuration and
6 | // behavior that modifies Cypress.
7 | //
8 | // You can change the location of this file or turn off
9 | // automatically serving support files with the
10 | // 'supportFile' configuration option.
11 | //
12 | // You can read more here:
13 | // https://on.cypress.io/configuration
14 | // ***********************************************************
15 |
16 |
17 | // Alternatively you can use CommonJS syntax:
18 | // require('./commands')
19 | import "cypress-intercept-formdata";
20 | import { mount } from "cypress/react18";
21 |
22 | Cypress.Commands.add("mount", mount);
23 |
24 | // Example use:
25 | // cy.mount()
26 |
--------------------------------------------------------------------------------
/cypress/support/e2e.js:
--------------------------------------------------------------------------------
1 | import "cypress-intercept-formdata";
2 | import "cypress-mochawesome-reporter/register";
3 |
4 | import "./storyLog";
5 | import "./visitStory";
6 | import "./pasteFile";
7 | import "./setUploadOptions";
8 | import "./wait";
9 |
--------------------------------------------------------------------------------
/cypress/support/index.js:
--------------------------------------------------------------------------------
1 | import "./e2e";
2 |
--------------------------------------------------------------------------------
/cypress/support/setUploadOptions.js:
--------------------------------------------------------------------------------
1 | Cypress.Commands.add("setUploadOptions", (options) =>
2 | //cy.wrap(window.parent)
3 | cy.window()
4 | .then((w) => {
5 | if (w._setUploadOptions) {
6 | w._setUploadOptions(options);
7 | } else {
8 | w.__extUploadOptions = options;
9 | }
10 | }));
11 |
12 | Cypress.Commands.add("setPreSendOptions", (options) =>
13 | //cy.wrap(window.parent)
14 | cy.window()
15 | .then((w) => {
16 | w.__extPreSendOptions = options;
17 | }));
18 |
--------------------------------------------------------------------------------
/cypress/support/wait.js:
--------------------------------------------------------------------------------
1 | import { WAIT_X_SHORT, WAIT_SHORT, WAIT_MEDIUM, WAIT_LONG, WAIT_X_LONG } from "../constants";
2 |
3 | [
4 | { name: "ExtraShort", time: WAIT_X_SHORT },
5 | { name: "Short", time: WAIT_SHORT },
6 | { name: "Medium", time: WAIT_MEDIUM },
7 | { name: "Long", time: WAIT_LONG },
8 | { name: "ExtraLong", time: WAIT_X_LONG },
9 | ]
10 | .forEach(({ name, time }) =>
11 | Cypress.Commands.add(`wait${name}`, () => {
12 | cy.wait(time);
13 | }));
14 |
15 |
--------------------------------------------------------------------------------
/cypress/tsconfig.json:
--------------------------------------------------------------------------------
1 |
2 | {
3 | "compilerOptions": {
4 | "strict": true,
5 | "baseUrl": "../node_modules",
6 | "target": "es5",
7 | "experimentalDecorators": true,
8 | "skipLibCheck": true,
9 | "noImplicitAny": false,
10 | "lib": ["es6", "dom"],
11 | "types": ["cypress"]
12 | },
13 | "include": ["**/*.ts"]
14 | }
15 |
--------------------------------------------------------------------------------
/cypress/webpack.cypress.config.mjs:
--------------------------------------------------------------------------------
1 | import webpack from "webpack";
2 |
3 | const config = {
4 | plugins: [
5 | new webpack.EnvironmentPlugin({
6 | "BUILD_TIME_VERSION": "test",
7 | }),
8 | ],
9 |
10 | resolve: {
11 | mainFields: ["main:dev", "module", "main"],
12 | },
13 |
14 | module: {
15 | rules: [
16 | {
17 | test: /\.?js$/,
18 | exclude: /node_module/,
19 | use: {
20 | loader: "babel-loader",
21 | }
22 | },
23 | {
24 | test: /\.css$/i,
25 | use: ["style-loader", "css-loader"],
26 | },
27 | ]
28 | },
29 | };
30 |
31 | export default config;
32 |
--------------------------------------------------------------------------------
/flow-typed.config.json:
--------------------------------------------------------------------------------
1 | {
2 | "env": ["node", "dom", "bom", "intl", "cssom", "indexeddb", "serviceworkers", "webassembly", "jsx"],
3 | "ignore": ["@rpldy", "@babel", "@actions", "@testing-library", "@vitest", "babel-plugin-*"],
4 | "workspaces": [
5 | "packages/core/*",
6 | "packages/ui/*",
7 | "packages/native/*"
8 | ]
9 | }
10 |
--------------------------------------------------------------------------------
/flow-typed/npm/flow-bin_v0.x.x.js:
--------------------------------------------------------------------------------
1 | // flow-typed signature: 28fdff7f110e1c75efab63ff205dda30
2 | // flow-typed version: c6154227d1/flow-bin_v0.x.x/flow_>=v0.104.x
3 |
4 | declare module "flow-bin" {
5 | declare module.exports: string;
6 | }
7 |
--------------------------------------------------------------------------------
/flow-typed/npm/invariant_v2.x.x.js:
--------------------------------------------------------------------------------
1 | // flow-typed signature: 4daa25492655417e7c0763d1d0b30fbb
2 | // flow-typed version: c6154227d1/invariant_v2.x.x/flow_>=v0.104.x
3 |
4 | declare module invariant {
5 | declare module.exports: (condition: boolean, message: string) => void;
6 | }
7 |
--------------------------------------------------------------------------------
/guides/README.md:
--------------------------------------------------------------------------------
1 | # Guides
2 |
3 | The Guides section is now housed in our [Documentation Website](https://react-uploady.org/docs/guides/).
4 |
--------------------------------------------------------------------------------
/guides/rpldy-demo.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rpldy/react-uploady/2c92e126c8b2e583fc6180b036e49d62e3e00a5c/guides/rpldy-demo.gif
--------------------------------------------------------------------------------
/lerna.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "1.10.0",
3 | "command": {
4 | "version": {
5 | "allowBranch": "release-*"
6 | },
7 | "publish": {
8 | "ignoreChanges": [
9 | "**/tests/**",
10 | "**/*.test.js"
11 | ],
12 | "message": "chore: publish new version: %v"
13 | }
14 | },
15 | "$schema": "node_modules/lerna/schemas/lerna-schema.json",
16 | "npmClient": "pnpm"
17 | }
--------------------------------------------------------------------------------
/netlify.toml:
--------------------------------------------------------------------------------
1 | [build]
2 | publish = ".sb-static"
3 | command = "pnpm sb:build"
4 |
--------------------------------------------------------------------------------
/nx.json:
--------------------------------------------------------------------------------
1 | {
2 | "tasksRunnerOptions": {
3 | "default": {
4 | "runner": "nx/tasks-runners/default",
5 | "options": {
6 | "cacheableOperations": [
7 | "build"
8 | ]
9 | }
10 | }
11 | },
12 | "targetDefaults": {
13 | "build": {
14 | "outputs": [
15 | "{projectRoot}/lib",
16 | "{projectRoot}/LICENSE.md",
17 | "{projectRoot}/.npmignore"
18 | ]
19 | }
20 | },
21 | "$schema": "./node_modules/nx/schemas/nx-schema.json",
22 | "namedInputs": {
23 | "default": [
24 | "{projectRoot}/**/*",
25 | "sharedGlobals"
26 | ],
27 | "sharedGlobals": [],
28 | "production": [
29 | "default"
30 | ]
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/packages/core/abort/.npmignore:
--------------------------------------------------------------------------------
1 | src/
2 | *.story.js
3 | *.stories.js
4 | *.test.js
5 | tests/
6 | mocks/
7 | lerna.json
8 | *.log
9 | jest*
10 | .flowconfig
11 | .editorconfig
12 | .eslintignore
13 | .eslintrc.js
14 | .circleci/
15 | .sb-static/
16 | .env
17 | .jest-cache/
18 | .jest-coverage/
19 | coverage/
20 | .github/
21 |
22 |
--------------------------------------------------------------------------------
/packages/core/abort/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "1.10.0",
3 | "name": "@rpldy/abort",
4 | "description": "adds the capability to abort/cancel running & pending uploads",
5 | "author": "yoav niran (https://github.com/yoavniran)",
6 | "main": "lib/cjs/index.js",
7 | "module": "lib/esm/index.js",
8 | "main:dev": "src/index.js",
9 | "types": "types/index.d.ts",
10 | "license": "MIT",
11 | "homepage": "https://react-uploady.org",
12 | "repository": {
13 | "type": "git",
14 | "url": "https://github.com/rpldy/react-uploady.git",
15 | "directory": "packages/core/abort"
16 | },
17 | "funding": {
18 | "type": "opencollective",
19 | "url": "https://opencollective.com/react-uploady"
20 | },
21 | "scripts": {
22 | "build": "node ../../../scripts/build.mjs"
23 | },
24 | "dependencies": {
25 | "@rpldy/raw-uploader": "^1.10.0",
26 | "@rpldy/shared": "^1.10.0",
27 | "@rpldy/simple-state": "^1.10.0"
28 | },
29 | "devDependencies": {
30 | "flow-bin": "^0.272.2"
31 | },
32 | "publishConfig": {
33 | "access": "public"
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/packages/core/abort/src/fastAbort.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import type { Batch } from "@rpldy/shared";
3 | import type { AbortsMap } from "./types";
4 |
5 | const fastAbortBatch = (batch: Batch, aborts: AbortsMap) => {
6 | batch.items.forEach(({ id }) => aborts[id]?.());
7 | };
8 |
9 | const fastAbortAll = (aborts: AbortsMap) => {
10 | const runFn = (fn: () => boolean) => fn();
11 |
12 | Object.values(aborts)
13 | .forEach(runFn);
14 | };
15 |
16 | export {
17 | fastAbortAll,
18 | fastAbortBatch,
19 | };
20 |
--------------------------------------------------------------------------------
/packages/core/abort/src/getAbortEnhancer.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import { abortAll, abortBatch, abortItem } from "./abort";
3 | import type { UploaderEnhancer, UploaderType } from "@rpldy/raw-uploader";
4 |
5 | const getAbortEnhancer = (): UploaderEnhancer => {
6 | /**
7 | * an uploader enhancer function to add abort functionality
8 | */
9 | return (uploader: UploaderType): UploaderType => {
10 | //$FlowIssue[incompatible-call]: unsure how to tell Flow this is ok...
11 | uploader.update({ abortAll, abortBatch, abortItem });
12 | return uploader;
13 | };
14 | };
15 |
16 | export default getAbortEnhancer;
17 |
--------------------------------------------------------------------------------
/packages/core/abort/src/index.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import getAbortEnhancer from "./getAbortEnhancer";
3 |
4 | export default getAbortEnhancer;
5 |
6 | export type {
7 | AbortMethod,
8 | AbortsMap,
9 | AbortAllMethod,
10 | AbortBatchMethod,
11 | AbortResult,
12 | AbortItemMethod,
13 | CreateOptionsWithAbort,
14 | } from "./types";
15 |
--------------------------------------------------------------------------------
/packages/core/abort/src/tests/fastAbort.test.js:
--------------------------------------------------------------------------------
1 | import { fastAbortBatch, fastAbortAll } from "../fastAbort";
2 |
3 | describe("fast abort tests", () => {
4 | it("should fast abort batch items", () => {
5 | const abort = vi.fn();
6 |
7 | fastAbortBatch({ items: [{ id: "u1" }, { id: "u2" }, { id: "u3" }] }, { "u1": abort, "u2": abort, "u4": abort });
8 |
9 | expect(abort).toHaveBeenCalledTimes(2);
10 | });
11 |
12 | it("should fast abort all", () => {
13 | const abort = vi.fn();
14 |
15 | fastAbortAll({ "u1": abort, "u2": abort });
16 |
17 | expect(abort).toHaveBeenCalledTimes(2);
18 | });
19 | });
20 |
--------------------------------------------------------------------------------
/packages/core/abort/src/tests/getAbortEnhancer.test.js:
--------------------------------------------------------------------------------
1 | import getAbortEnhancer from "../getAbortEnhancer";
2 | import { abortItem, abortBatch, abortAll } from "../abort";
3 |
4 | vi.mock("../abort");
5 |
6 | describe("getAbortEnhancer tests", () => {
7 | it("should enhance uploader with abort fns", () => {
8 | const enhancer = getAbortEnhancer();
9 | const uploader = { update: vi.fn() };
10 |
11 | const enhanced = enhancer(uploader);
12 |
13 | expect(uploader.update).toHaveBeenCalledWith({
14 | abortAll, abortBatch, abortItem
15 | });
16 |
17 | expect(enhanced).toBe(uploader);
18 | });
19 | });
20 |
--------------------------------------------------------------------------------
/packages/core/abort/types/index.d.ts:
--------------------------------------------------------------------------------
1 | import { RawUploaderEnhancer } from "@rpldy/raw-uploader";
2 |
3 | export const getAbortEnhancer: () => RawUploaderEnhancer;
4 |
5 | export default getAbortEnhancer;
6 |
--------------------------------------------------------------------------------
/packages/core/abort/types/index.test-d.ts:
--------------------------------------------------------------------------------
1 | import getAbortEnhancer from "./index";
2 | import { createRawUploader } from "@rpldy/raw-uploader";
3 |
4 | const testEnhancer = (): void => {
5 | const abortEnhancer = getAbortEnhancer();
6 |
7 | const uploader = createRawUploader({
8 | enhancer: abortEnhancer,
9 | });
10 |
11 | console.log(uploader.getOptions());
12 | };
13 |
14 | export {
15 | testEnhancer,
16 | };
17 |
--------------------------------------------------------------------------------
/packages/core/chunked-sender/.npmignore:
--------------------------------------------------------------------------------
1 | src/
2 | *.story.js
3 | *.stories.js
4 | *.test.js
5 | tests/
6 | mocks/
7 | lerna.json
8 | *.log
9 | jest*
10 | .flowconfig
11 | .editorconfig
12 | .eslintignore
13 | .eslintrc.js
14 | .circleci/
15 | .sb-static/
16 | .env
17 | .jest-cache/
18 | .jest-coverage/
19 | coverage/
20 | .github/
21 |
22 |
--------------------------------------------------------------------------------
/packages/core/chunked-sender/ChunkedSender.storydoc.mdx:
--------------------------------------------------------------------------------
1 | import { Markdown } from "@storybook/blocks";
2 | import readme from "./README.md?raw";
3 |
4 | {readme}
5 |
--------------------------------------------------------------------------------
/packages/core/chunked-sender/src/chunkedSender/ChunkedSendError.js:
--------------------------------------------------------------------------------
1 | // @flow
2 |
3 | export default class ChunkedSendError extends Error {
4 | constructor(message: string) {
5 | super(message);
6 | this.name = "ChunkedSendError";
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/packages/core/chunked-sender/src/chunkedSender/getChunksToSend.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import ChunkedSendError from "./ChunkedSendError";
3 | import type { Chunk, ChunkedState } from "./types";
4 |
5 | const getChunksToSend = (chunkedState: ChunkedState): Array => {
6 | const state = chunkedState.getState();
7 |
8 | const chunks = [],
9 | inProgressIds = Object.keys(state.requests),
10 | parallel = state.parallel || 1;
11 |
12 | for (let i = 0; i < state.chunks.length &&
13 | (inProgressIds.length + chunks.length) < parallel; i++) {
14 | const chunk = state.chunks[i];
15 |
16 | if (!inProgressIds.includes(chunk.id)) {
17 | if (!chunk.attempt || chunk.attempt < state.retries) {
18 | chunks.push(chunk);
19 | } else {
20 | throw new ChunkedSendError("chunk failure");
21 | }
22 | }
23 | }
24 |
25 | return chunks;
26 | };
27 |
28 | export default getChunksToSend;
29 |
--------------------------------------------------------------------------------
/packages/core/chunked-sender/src/chunkedSender/index.js:
--------------------------------------------------------------------------------
1 | import createChunkedSender from "./createChunkedSender";
2 | export default createChunkedSender;
3 |
--------------------------------------------------------------------------------
/packages/core/chunked-sender/src/chunkedSender/processChunkProgressData.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import type { BatchItem } from "@rpldy/shared";
3 | import type { ChunkedState } from "./types";
4 |
5 | const processChunkProgressData = (
6 | chunkedState: ChunkedState,
7 | item: BatchItem,
8 | chunkId: string,
9 | chunkUploaded: number
10 | ): { loaded: number, total: number } => {
11 | chunkedState.updateState((state) => {
12 | state.uploaded[chunkId] = Math.max(chunkUploaded, (state.uploaded[chunkId] || 0));
13 | });
14 |
15 | const state = chunkedState.getState();
16 |
17 | const loadedSum = Object.keys(state.uploaded)
18 | .reduce((res, id) => res + state.uploaded[id],
19 | //we start from the offset of the first chunk to get an accurate progress on resumed uploads
20 | state.startByte);
21 |
22 | const total = item.file.size;
23 |
24 | return { loaded: Math.min(loadedSum, total), total };
25 | };
26 |
27 | export default processChunkProgressData;
28 |
--------------------------------------------------------------------------------
/packages/core/chunked-sender/src/chunkedSender/tests/getChunkedState.test.js:
--------------------------------------------------------------------------------
1 | import getChunkedState from "../getChunkedState";
2 |
3 | vi.mock("@rpldy/simple-state", () => ({
4 | default: (state) => ({
5 | state,
6 | update: (updater) => updater(state),
7 | })
8 | }));
9 |
10 | describe("getChunkedState tests", () => {
11 | it("should return chunked state", () => {
12 | const chunkedState = getChunkedState([{}, {}], "test.com", { method: "PUT", startByte: 3 }, { chunked: true });
13 |
14 | const state = chunkedState.getState();
15 |
16 | expect(state.chunks).toHaveLength(2);
17 | expect(state.url).toBe("test.com");
18 |
19 | let updateCnt = 0;
20 |
21 | chunkedState.updateState((state) => {
22 | updateCnt++;
23 | expect(state.startByte).toBe(3);
24 | });
25 |
26 | expect(updateCnt).toBe(1);
27 | });
28 | });
29 |
--------------------------------------------------------------------------------
/packages/core/chunked-sender/src/chunkedSender/tests/mocks/getChunkedState.mock.js:
--------------------------------------------------------------------------------
1 | const getChunkedState = (state = {}) => {
2 | const getState = vi.fn(() => state);
3 |
4 | const updateState = vi.fn((updater) => {
5 | updater(state);
6 | });
7 |
8 | return {
9 | getState,
10 | updateState,
11 | };
12 | }
13 |
14 | export default getChunkedState;
15 |
--------------------------------------------------------------------------------
/packages/core/chunked-sender/src/consts.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import { devFreeze } from "@rpldy/shared";
3 |
4 | export const CHUNK_EVENTS: Object = devFreeze({
5 | CHUNK_START: "CHUNK_START",
6 | CHUNK_FINISH: "CHUNK_FINISH",
7 | });
8 |
9 | export const CHUNKED_SENDER_TYPE = "rpldy-chunked-sender";
10 |
--------------------------------------------------------------------------------
/packages/core/chunked-sender/src/defaults.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import { devFreeze } from "@rpldy/shared";
3 |
4 | export const DEFAULT_OPTIONS: Object = devFreeze({
5 | chunked: true,
6 | chunkSize: 5242880,
7 | retries: 0,
8 | parallel: 1
9 | });
10 |
--------------------------------------------------------------------------------
/packages/core/chunked-sender/src/getChunkedEnhancer.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import { logger } from "@rpldy/shared";
3 | import createChunkedSender from "./chunkedSender";
4 |
5 | import type { UploaderEnhancer, UploaderType, UploaderCreateOptions } from "@rpldy/uploader";
6 | import type { ChunkedOptions } from "./types";
7 | import type { TriggerMethod } from "@rpldy/life-events";
8 |
9 | const getChunkedEnhancer = (options: ChunkedOptions): UploaderEnhancer => {
10 | //return uploader enhancer
11 | return (uploader: UploaderType, trigger: TriggerMethod): UploaderType => {
12 | const sender = createChunkedSender(options, trigger);
13 | logger.debugLog("chunkedSenderEnhancer: Created chunked-sender instance with options: ", options);
14 | uploader.update({ send: sender.send });
15 | return uploader;
16 | };
17 | };
18 |
19 | export default getChunkedEnhancer;
20 |
--------------------------------------------------------------------------------
/packages/core/chunked-sender/src/index.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import createChunkedSender from "./chunkedSender";
3 | import getChunkedEnhancer from "./getChunkedEnhancer";
4 | import { CHUNKING_SUPPORT, getChunkDataFromFile } from "./utils";
5 | import { CHUNK_EVENTS, CHUNKED_SENDER_TYPE } from "./consts";
6 | import { DEFAULT_OPTIONS as CHUNKED_DEFAULT_OPTIONS } from "./defaults";
7 |
8 | export default getChunkedEnhancer;
9 |
10 | const DEFAULT_CHUNK_SIZE = CHUNKED_DEFAULT_OPTIONS.chunkSize;
11 |
12 | export {
13 | CHUNK_EVENTS,
14 | CHUNKING_SUPPORT,
15 | CHUNKED_SENDER_TYPE,
16 | CHUNKED_DEFAULT_OPTIONS,
17 | DEFAULT_CHUNK_SIZE,
18 |
19 | getChunkedEnhancer,
20 | createChunkedSender,
21 | getChunkDataFromFile,
22 | };
23 |
24 | export type {
25 | ChunkedOptions,
26 | MandatoryChunkedOptions,
27 | ChunkedSender,
28 | ChunkedSendOptions,
29 | ChunkEventData,
30 | ChunkStartEventData,
31 | ChunkFinishEventData,
32 | } from "./types";
33 |
34 | export type {
35 | OnProgress,
36 | SendOptions,
37 | SendResult,
38 | } from "@rpldy/sender";
39 |
--------------------------------------------------------------------------------
/packages/core/chunked-sender/src/tests/chunkedEnhancer.test.js:
--------------------------------------------------------------------------------
1 | import createChunkedSender from "../chunkedSender";
2 | import getChunkedEnhancer from "../getChunkedEnhancer";
3 |
4 | vi.mock("../chunkedSender");
5 |
6 | describe("chunkedEnhancer tests", () => {
7 | it("should enhance uploader with chunked sender", () => {
8 | const options = { chunkSize: 111 };
9 | const uploader = { update: vi.fn() };
10 |
11 | createChunkedSender.mockReturnValueOnce({ send: "chunkedSend" });
12 |
13 | const enhancer = getChunkedEnhancer(options);
14 | const result = enhancer(uploader, "trigger");
15 |
16 | expect(result).toBe(uploader);
17 | expect(createChunkedSender).toHaveBeenCalledWith(options, "trigger");
18 | expect(uploader.update).toHaveBeenCalledWith({ send: "chunkedSend" });
19 | });
20 | });
21 |
--------------------------------------------------------------------------------
/packages/core/chunked-sender/types/index.test-d.tsx:
--------------------------------------------------------------------------------
1 | import getChunkedEnhancer, { createChunkedSender } from "./index";
2 | import createUploader from "@rpldy/uploader";
3 | import { createBatchItem } from "@rpldy/shared";
4 |
5 | const testGetEnhancer = (): void => {
6 | const enhancer = getChunkedEnhancer({ chunkSize: 123 });
7 |
8 | const uploader = createUploader({
9 | enhancer,
10 | });
11 |
12 | console.log(uploader.getOptions());
13 | };
14 |
15 | const testGetSender = (): void => {
16 | const sender = createChunkedSender({ chunkSize: 123 });
17 |
18 | sender.send([createBatchItem({ name: "test.png", size: 123, type: "image", lastModified: 1234 }, "b1")],
19 | "test.com",
20 | {
21 | method: "POST",
22 | paramName: "file",
23 | headers: { "x-test": "aaa" } });
24 | };
25 |
26 | export {
27 | testGetEnhancer,
28 | testGetSender,
29 | };
30 |
--------------------------------------------------------------------------------
/packages/core/life-events/.npmignore:
--------------------------------------------------------------------------------
1 | src/
2 | *.story.js
3 | *.stories.js
4 | *.test.js
5 | tests/
6 | mocks/
7 | lerna.json
8 | *.log
9 | jest*
10 | .flowconfig
11 | .editorconfig
12 | .eslintignore
13 | .eslintrc.js
14 | .circleci/
15 | .sb-static/
16 | .env
17 | .jest-cache/
18 | .jest-coverage/
19 | coverage/
20 | .github/
21 |
22 |
--------------------------------------------------------------------------------
/packages/core/life-events/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "1.10.0",
3 | "name": "@rpldy/life-events",
4 | "description": "events pub/sub management with return values",
5 | "author": "yoav niran (https://github.com/yoavniran)",
6 | "main": "lib/cjs/index.js",
7 | "module": "lib/esm/index.js",
8 | "main:dev": "src/index.js",
9 | "types": "types/index.d.ts",
10 | "license": "MIT",
11 | "keywords": [
12 | "events",
13 | "pubsub",
14 | "emitter"
15 | ],
16 | "homepage": "https://react-uploady.org",
17 | "repository": {
18 | "type": "git",
19 | "url": "https://github.com/rpldy/react-uploady.git",
20 | "directory": "packages/life-events"
21 | },
22 | "funding": {
23 | "type": "opencollective",
24 | "url": "https://opencollective.com/react-uploady"
25 | },
26 | "scripts": {
27 | "build": "node ../../../scripts/build.mjs"
28 | },
29 | "dependencies": {
30 | "@rpldy/shared": "^1.10.0"
31 | },
32 | "devDependencies": {
33 | "flow-bin": "^0.272.2"
34 | },
35 | "publishConfig": {
36 | "access": "public"
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/packages/core/life-events/src/consts.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | export const LESYM: symbol = Symbol.for("__le__");
3 |
4 | export const LE_PACK_SYM: symbol = Symbol.for("__le__pack__");
5 |
--------------------------------------------------------------------------------
/packages/core/life-events/src/defaults.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import { devFreeze } from "@rpldy/shared";
3 | import type { Options } from "./types";
4 |
5 | const defaults: Options = devFreeze({
6 | allowRegisterNonExistent: true,
7 | canAddEvents: true,
8 | canRemoveEvents: true,
9 | collectStats: false,
10 | });
11 |
12 | export default defaults;
13 |
14 |
15 |
--------------------------------------------------------------------------------
/packages/core/life-events/src/index.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | export { default, default as addLife, isLE } from "./lifeEvents";
3 | export { default as createLifePack } from "./lifePack";
4 |
5 | export type {
6 | LifeEventsAPI,
7 | EventCallback,
8 | TriggerMethod,
9 | OnAndOnceMethod,
10 | OffMethod,
11 | } from "./types";
12 |
--------------------------------------------------------------------------------
/packages/core/life-events/src/lifePack.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import { LE_PACK_SYM } from "./consts";
3 |
4 | /**
5 | * create a pack that will only execute when there are registered event handlers
6 | *
7 | * @param creator - a function to return data that will become parameters for the event handler
8 | * array returned means more than 1 parameter. Any other value will be added to an array as a single param
9 | */
10 | const createLifePack = (creator: () => any): {|resolve: () => Array|} => {
11 | const lp = {
12 | resolve: (): any[] => [].concat(creator()),
13 | };
14 |
15 | Object.defineProperty(lp, LE_PACK_SYM, {
16 | value: true,
17 | configurable: false,
18 | });
19 |
20 | return lp;
21 | };
22 |
23 | export default createLifePack;
24 |
--------------------------------------------------------------------------------
/packages/core/life-events/src/tests/lifePack.test.js:
--------------------------------------------------------------------------------
1 | import createLifePack from "../lifePack";
2 |
3 | describe("lifePack tests", () => {
4 | it("should resolve creator with single param", () => {
5 | const creator = vi.fn(() => "value");
6 | const lp = createLifePack(creator);
7 |
8 | expect(creator).not.toHaveBeenCalled();
9 | expect(lp.resolve()).toEqual(["value"]);
10 | });
11 |
12 | it("should resolve creator with multiple params", () => {
13 | const creator = vi.fn(() => ["value", "value2"]);
14 | const lp = createLifePack(creator);
15 |
16 | expect(creator).not.toHaveBeenCalled();
17 | expect(lp.resolve()).toEqual(["value", "value2"]);
18 | });
19 | });
20 |
--------------------------------------------------------------------------------
/packages/core/life-events/src/tests/mocks/rpldy-life-events.mock.js:
--------------------------------------------------------------------------------
1 | import addLife, { createLifePack as orgCreateLifePack } from "@rpldy/life-events";
2 |
3 | const mockTrigger = vi.fn();
4 |
5 | vi.mock("@rpldy/life-events", () => {
6 | return {
7 | default: vi.fn((target) => ({
8 | target,
9 | trigger: mockTrigger,
10 | })),
11 | createLifePack: vi.fn(),
12 | }
13 | });
14 |
15 | export default addLife;
16 |
17 | export const createLifePack = orgCreateLifePack;
18 |
19 | export {
20 | mockTrigger,
21 | };
22 |
--------------------------------------------------------------------------------
/packages/core/life-events/src/types.js:
--------------------------------------------------------------------------------
1 | // @flow
2 |
3 | export type Options = {
4 | //whether clients can register to unspecified events in advance (default: true)
5 | allowRegisterNonExistent?: boolean,
6 | //whether events can be added after initialization (default: true)
7 | canAddEvents?: boolean,
8 | //whether events can be removed (default: true)
9 | canRemoveEvents?: boolean,
10 | //whether to collect registration and trigger stats (default: false)
11 | collectStats?: boolean,
12 | };
13 |
14 | export type TriggerMethod = (name: any, ...args: any[]) => any;
15 |
16 | export type LifeEventsAPI = {
17 | target: Object,
18 | trigger: TriggerMethod,
19 | addEvent: (name: any) => void,
20 | removeEvent: (name: any) => void,
21 | hasEvent: (name: any) => boolean,
22 | hasEventRegistrations: (name: any) => boolean,
23 | };
24 |
25 | export type EventCallback = (...args: any[]) => any;
26 |
27 | export type OffMethod = (name: any, cb?: EventCallback) => void;
28 |
29 | export type OnAndOnceMethod = (name: any, cb: EventCallback) => OffMethod;
30 |
31 | export type RegItem = {
32 | name: any,
33 | cb: EventCallback,
34 | once: boolean,
35 | };
36 |
--------------------------------------------------------------------------------
/packages/core/life-events/src/utils.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import { isFunction } from "@rpldy/shared";
3 |
4 | const validateFunction = (f: any, name: string) => {
5 | if (!isFunction(f)) {
6 | throw new Error(`'${name}' is not a valid function`);
7 | }
8 | };
9 |
10 | const isUndefined = (val: any): boolean => typeof (val) === "undefined";
11 |
12 | export {
13 | validateFunction,
14 | isUndefined,
15 | };
16 |
--------------------------------------------------------------------------------
/packages/core/life-events/types/index.test-d.tsx:
--------------------------------------------------------------------------------
1 | import addLife, { createLifePack } from "./index";
2 |
3 | const testAddLife = (): void => {
4 | const obj = {};
5 |
6 | const lifeApi = addLife(obj, ["test", "test2"], {
7 | allowRegisterNonExistent: true,
8 | canAddEvents: false,
9 | });
10 |
11 | lifeApi.target.on("test", (a: number, b: number) => {
12 | console.log("event test triggered", a, b);
13 | });
14 |
15 | lifeApi.trigger("test", 1, 2);
16 | };
17 |
18 | const testCreateLifePack = (): void => {
19 |
20 | const creator = () => {
21 | return "value";
22 | };
23 |
24 | const lp = createLifePack(creator);
25 |
26 | lp.resolve();
27 | };
28 |
29 | export {
30 | testAddLife,
31 | testCreateLifePack,
32 | };
33 |
--------------------------------------------------------------------------------
/packages/core/mock-sender/.npmignore:
--------------------------------------------------------------------------------
1 | src/
2 | *.story.js
3 | *.stories.js
4 | *.test.js
5 | tests/
6 | mocks/
7 | lerna.json
8 | *.log
9 | jest*
10 | .flowconfig
11 | .editorconfig
12 | .eslintignore
13 | .eslintrc.js
14 | .circleci/
15 | .sb-static/
16 | .env
17 | .jest-cache/
18 | .jest-coverage/
19 | coverage/
20 | .github/
21 |
22 |
--------------------------------------------------------------------------------
/packages/core/mock-sender/MockSender.storydoc.mdx:
--------------------------------------------------------------------------------
1 | import { Markdown } from "@storybook/blocks";
2 | import readme from "./README.md?raw";
3 |
4 | {readme}
5 |
--------------------------------------------------------------------------------
/packages/core/mock-sender/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "1.10.0",
3 | "name": "@rpldy/mock-sender",
4 | "description": "mock sender for testing purposes",
5 | "author": "yoav niran (https://github.com/yoavniran)",
6 | "main": "lib/cjs/index.js",
7 | "module": "lib/esm/index.js",
8 | "main:dev": "src/index.js",
9 | "types": "types/index.d.ts",
10 | "license": "MIT",
11 | "homepage": "https://react-uploady.org",
12 | "repository": {
13 | "type": "git",
14 | "url": "https://github.com/rpldy/react-uploady.git",
15 | "directory": "packages/mock-sender"
16 | },
17 | "funding": {
18 | "type": "opencollective",
19 | "url": "https://opencollective.com/react-uploady"
20 | },
21 | "scripts": {
22 | "build": "node ../../../scripts/build.mjs"
23 | },
24 | "dependencies": {
25 | "@rpldy/shared": "^1.10.0"
26 | },
27 | "devDependencies": {
28 | "@rpldy/sender": "^1.10.0",
29 | "@rpldy/uploader": "^1.10.0",
30 | "flow-bin": "^0.272.2"
31 | },
32 | "publishConfig": {
33 | "access": "public"
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/packages/core/mock-sender/src/consts.js:
--------------------------------------------------------------------------------
1 | // @flow
2 |
3 | export const MOCK_SENDER_TYPE = "rpldy-mock-sender";
--------------------------------------------------------------------------------
/packages/core/mock-sender/src/defaults.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import { devFreeze } from "@rpldy/shared";
3 |
4 | export const MOCK_DEFAULTS: Object = devFreeze({
5 | delay: 500,
6 | progressIntervals: [10, 25, 50, 75, 99],
7 | });
8 |
--------------------------------------------------------------------------------
/packages/core/mock-sender/src/getMockSenderEnhancer.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import type { UploaderEnhancer, UploaderType, UploaderCreateOptions } from "@rpldy/uploader";
3 | import createMockSender from "./mockSender";
4 | import type { MockOptions } from "./types";
5 |
6 | /**
7 | * an uploader enhancer function to set mock sender instead of current sender
8 | */
9 | const getMockSenderEnhancer = (options?: MockOptions): UploaderEnhancer =>
10 | (uploader: UploaderType): UploaderType => {
11 | const mockSender = createMockSender(options);
12 | uploader.update({ send: mockSender.send });
13 | return uploader;
14 | };
15 |
16 | export default getMockSenderEnhancer;
17 |
--------------------------------------------------------------------------------
/packages/core/mock-sender/src/index.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import { MOCK_SENDER_TYPE } from "./consts";
3 | import createMockSender from "./mockSender";
4 | import getMockSenderEnhancer from "./getMockSenderEnhancer";
5 |
6 | export default createMockSender;
7 |
8 | export {
9 | MOCK_SENDER_TYPE,
10 |
11 | createMockSender,
12 | getMockSenderEnhancer
13 | };
14 |
15 | export type {
16 | MockOptions,
17 | } from "./types";
18 |
19 |
--------------------------------------------------------------------------------
/packages/core/mock-sender/src/tests/getMockSenderEnhancer.test.js:
--------------------------------------------------------------------------------
1 | import createMockSender from "../mockSender";
2 | import getMockSenderEnhancer from "../getMockSenderEnhancer";
3 |
4 | vi.mock("../mockSender");
5 |
6 | describe("getMockSenderEnhancer tests", () => {
7 | it("should create enhancer", () => {
8 | createMockSender.mockReturnValueOnce({ send: "mock" });
9 |
10 | const uploader = { update: vi.fn() };
11 | const enhancer = getMockSenderEnhancer({ test: true });
12 |
13 | expect(enhancer(uploader)).toBe(uploader);
14 | expect(uploader.update).toHaveBeenCalledWith({ send: "mock" });
15 | });
16 | });
17 |
--------------------------------------------------------------------------------
/packages/core/mock-sender/src/types.js:
--------------------------------------------------------------------------------
1 | // @flow
2 |
3 | import type { IsSuccessfulCall } from "@rpldy/shared";
4 |
5 | export type MockOptions = {|
6 | //the time in ms it should take to "upload" (default: 500ms)
7 | delay?: number,
8 | //the file size of the mocked upload, used for progress calculation (default: 1M bytes)
9 | fileSize?: number,
10 | //the mock intervals (percentages) to emit progress events at (default: [10, 25, 50, 75, 100])
11 | progressIntervals?: number[],
12 | //the mock server response to use (default: {"mock": true, "success": true})
13 | response?: any,
14 | //the upload request status code (default: 200)
15 | responseStatus?: number,
16 | //callback to use to decide whether upload response is successful or not
17 | isSuccessfulCall?: IsSuccessfulCall
18 | |};
19 |
20 | export type MandatoryMockOptions = {[key in keyof MockOptions]: $NonMaybeType}
21 |
--------------------------------------------------------------------------------
/packages/core/mock-sender/types/index.d.ts:
--------------------------------------------------------------------------------
1 | import { SendMethod } from "@rpldy/sender";
2 | import { UploaderEnhancer } from "@rpldy/uploader";
3 | import type { IsSuccessfulCall } from "@rpldy/shared";
4 |
5 | export interface MockOptions {
6 | delay?: number;
7 | fileSize?: number;
8 | progressIntervals?: number[];
9 | response?: any;
10 | responseStatus?: number;
11 | isSuccessfulCall?: IsSuccessfulCall;
12 | }
13 |
14 | export type MockSender = {
15 | update: (updated: MockOptions) => void;
16 | send: SendMethod;
17 | };
18 |
19 | export const createMockSender: (option: MockOptions) => MockSender;
20 |
21 | export const getMockSenderEnhancer: (options?: MockOptions) => UploaderEnhancer;
22 |
23 | export default createMockSender;
24 |
--------------------------------------------------------------------------------
/packages/core/raw-uploader/.npmignore:
--------------------------------------------------------------------------------
1 | src/
2 | *.story.js
3 | *.stories.js
4 | *.test.js
5 | tests/
6 | mocks/
7 | lerna.json
8 | *.log
9 | jest*
10 | .flowconfig
11 | .editorconfig
12 | .eslintignore
13 | .eslintrc.js
14 | .circleci/
15 | .sb-static/
16 | .env
17 | .jest-cache/
18 | .jest-coverage/
19 | coverage/
20 | .github/
21 |
22 |
--------------------------------------------------------------------------------
/packages/core/raw-uploader/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "1.10.0",
3 | "name": "@rpldy/raw-uploader",
4 | "description": "placeholder package for now",
5 | "author": "yoav niran (https://github.com/yoavniran)",
6 | "main": "lib/cjs/index.js",
7 | "module": "lib/esm/index.js",
8 | "main:dev": "src/index.js",
9 | "types": "types/index.d.ts",
10 | "license": "MIT",
11 | "homepage": "https://react-uploady.org",
12 | "repository": {
13 | "type": "git",
14 | "url": "https://github.com/rpldy/react-uploady.git",
15 | "directory": "packages/raw-uploader"
16 | },
17 | "funding": {
18 | "type": "opencollective",
19 | "url": "https://opencollective.com/react-uploady"
20 | },
21 | "keywords": [
22 | "file",
23 | "upload",
24 | "file upload",
25 | "react",
26 | "hooks"
27 | ],
28 | "scripts": {
29 | "build": "node ../../../scripts/build.mjs"
30 | },
31 | "dependencies": {
32 | "@rpldy/life-events": "^1.10.0",
33 | "@rpldy/shared": "^1.10.0"
34 | },
35 | "devDependencies": {
36 | "flow-bin": "^0.272.2"
37 | },
38 | "publishConfig": {
39 | "access": "public"
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/packages/core/raw-uploader/src/index.js:
--------------------------------------------------------------------------------
1 | // @flow
2 |
3 | export type {
4 | UploaderEnhancer,
5 | RawCreateOptions,
6 | UploaderType,
7 | } from "./types";
8 |
--------------------------------------------------------------------------------
/packages/core/raw-uploader/types/index.test-d.ts:
--------------------------------------------------------------------------------
1 | //TODO: move tests here when this package holds the actual uploader implementation
2 |
--------------------------------------------------------------------------------
/packages/core/retry/.npmignore:
--------------------------------------------------------------------------------
1 | src/
2 | *.story.js
3 | *.stories.js
4 | *.test.js
5 | tests/
6 | mocks/
7 | lerna.json
8 | *.log
9 | jest*
10 | .flowconfig
11 | .editorconfig
12 | .eslintignore
13 | .eslintrc.js
14 | .circleci/
15 | .sb-static/
16 | .env
17 | .jest-cache/
18 | .jest-coverage/
19 | coverage/
20 | .github/
21 |
22 |
--------------------------------------------------------------------------------
/packages/core/retry/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "1.10.0",
3 | "name": "@rpldy/retry",
4 | "description": "adds the capability to retry failed uploads",
5 | "author": "yoav niran (https://github.com/yoavniran)",
6 | "main": "lib/cjs/index.js",
7 | "module": "lib/esm/index.js",
8 | "main:dev": "src/index.js",
9 | "types": "types/index.d.ts",
10 | "license": "MIT",
11 | "homepage": "https://react-uploady.org",
12 | "repository": {
13 | "type": "git",
14 | "url": "https://github.com/rpldy/react-uploady.git",
15 | "directory": "packages/retry"
16 | },
17 | "funding": {
18 | "type": "opencollective",
19 | "url": "https://opencollective.com/react-uploady"
20 | },
21 | "scripts": {
22 | "build": "node ../../../scripts/build.mjs"
23 | },
24 | "dependencies": {
25 | "@rpldy/shared": "^1.10.0",
26 | "@rpldy/simple-state": "^1.10.0",
27 | "@rpldy/uploader": "^1.10.0"
28 | },
29 | "devDependencies": {
30 | "flow-bin": "^0.272.2"
31 | },
32 | "publishConfig": {
33 | "access": "public"
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/packages/core/retry/src/consts.js:
--------------------------------------------------------------------------------
1 | // @flow
2 |
3 | export const RETRY_EXT: symbol = Symbol.for("__upldy-retry__");
4 |
5 | export const RETRY_EVENT = "RETRY_EVENT";
6 |
--------------------------------------------------------------------------------
/packages/core/retry/src/index.js:
--------------------------------------------------------------------------------
1 | // @flow
2 |
3 | export { default, default as retryEnhancer } from "./retry";
4 |
5 | export {
6 | RETRY_EVENT,
7 | RETRY_EXT,
8 | } from "./consts";
9 |
10 | export type {
11 | BatchRetryMethod,
12 | RetryMethod
13 | } from "./types";
14 |
15 |
16 |
--------------------------------------------------------------------------------
/packages/core/retry/src/types.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import type { BatchItem, UploadOptions } from "@rpldy/shared";
3 |
4 | export type State = {
5 | batchIdsMap: {
6 | [string]: string[]
7 | },
8 | failed: {
9 | [string]: BatchItem
10 | },
11 | };
12 |
13 | export type RetryState = {
14 | updateState: ((State) => void) => void,
15 | getState: () => State,
16 | };
17 |
18 | export type BatchRetryMethod = (batchId: string, options?: UploadOptions) => boolean;
19 |
20 | export type RetryMethod = (itemId?: string, options?: UploadOptions) => boolean;
21 |
--------------------------------------------------------------------------------
/packages/core/retry/types/index.d.ts:
--------------------------------------------------------------------------------
1 | import { UploadOptions } from "@rpldy/shared";
2 | import { UploaderEnhancer } from "@rpldy/uploader";
3 |
4 | export const retryEnhancer: UploaderEnhancer;
5 |
6 | export const RETRY_EVENT = "RETRY_EVENT";
7 |
8 | export type RetryMethod = (id?: string, options?: UploadOptions) => boolean;
9 |
10 | export type RetryBatchMethod = (batchId: string, options?: UploadOptions) => boolean;
11 |
12 | export default retryEnhancer;
13 |
--------------------------------------------------------------------------------
/packages/core/retry/types/index.test-d.tsx:
--------------------------------------------------------------------------------
1 | import retryEnhancer from "./index";
2 | import createUploader from "@rpldy/uploader";
3 |
4 | const testEnhancer = (): void => {
5 | const uploader = createUploader({
6 | enhancer: retryEnhancer,
7 | });
8 |
9 | console.log(uploader.getOptions());
10 | };
11 |
12 | export {
13 | testEnhancer,
14 | };
15 |
--------------------------------------------------------------------------------
/packages/core/safe-storage/.npmignore:
--------------------------------------------------------------------------------
1 | src/
2 | *.story.js
3 | *.stories.js
4 | *.test.js
5 | tests/
6 | mocks/
7 | lerna.json
8 | *.log
9 | jest*
10 | .flowconfig
11 | .editorconfig
12 | .eslintignore
13 | .eslintrc.js
14 | .circleci/
15 | .sb-static/
16 | .env
17 | .jest-cache/
18 | .jest-coverage/
19 | coverage/
20 | .github/
21 |
22 |
--------------------------------------------------------------------------------
/packages/core/safe-storage/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "1.10.0",
3 | "name": "@rpldy/safe-storage",
4 | "description": "safe (dont throw) versions of local and session storage",
5 | "author": "yoav niran (https://github.com/yoavniran)",
6 | "main": "lib/cjs/index.js",
7 | "module": "lib/esm/index.js",
8 | "main:dev": "src/index.js",
9 | "types": "types/index.d.ts",
10 | "license": "MIT",
11 | "homepage": "https://react-uploady.org",
12 | "repository": {
13 | "type": "git",
14 | "url": "https://github.com/rpldy/react-uploady.git",
15 | "directory": "packages/safe-storage"
16 | },
17 | "funding": {
18 | "type": "opencollective",
19 | "url": "https://opencollective.com/react-uploady"
20 | },
21 | "scripts": {
22 | "build": "node ../../../scripts/build.mjs"
23 | },
24 | "dependencies": {
25 | "@rpldy/shared": "^1.10.0"
26 | },
27 | "devDependencies": {
28 | "flow-bin": "^0.272.2"
29 | },
30 | "publishConfig": {
31 | "access": "public"
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/packages/core/safe-storage/src/index.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import { safeLocalStorage, safeSessionStorage } from "./storage";
3 |
4 | export {
5 | safeLocalStorage,
6 | safeSessionStorage,
7 | };
8 |
--------------------------------------------------------------------------------
/packages/core/safe-storage/src/storage.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import createSafeStorage from "./safeStorageCreator";
3 | import type { SafeStorage } from "./types";
4 |
5 | const safeLocalStorage = (createSafeStorage("localStorage"): SafeStorage);
6 | const safeSessionStorage = (createSafeStorage("sessionStorage"): SafeStorage);
7 |
8 | export {
9 | safeLocalStorage,
10 | safeSessionStorage,
11 | };
12 |
13 |
--------------------------------------------------------------------------------
/packages/core/safe-storage/src/types.js:
--------------------------------------------------------------------------------
1 | // @flow
2 |
3 | export type SafeStorage = {
4 | length: number;
5 | getItem(key: string): ?string;
6 | setItem(key: string, data: string): void;
7 | clear(): void;
8 | removeItem(key: string): void;
9 | key(index: number): ?string;
10 | isSupported: boolean;
11 | };
12 |
--------------------------------------------------------------------------------
/packages/core/safe-storage/types/index.d.ts:
--------------------------------------------------------------------------------
1 | export const safeLocalStorage: Storage;
2 |
3 | export const safeSessionStorage: Storage;
4 |
5 |
--------------------------------------------------------------------------------
/packages/core/safe-storage/types/index.test-d.ts:
--------------------------------------------------------------------------------
1 | import { safeLocalStorage, safeSessionStorage } from "./index";
2 |
3 | const testSafeLocalStorage = (): string | null => {
4 |
5 | safeLocalStorage.setItem("test", "value");
6 | const value = safeLocalStorage.getItem("test");
7 | safeLocalStorage.removeItem("test");
8 |
9 | return value;
10 | };
11 |
12 | const testSafeSessionStorage = (): string | null => {
13 |
14 | safeSessionStorage.setItem("test", "value");
15 | const value = safeSessionStorage.getItem("test");
16 | safeSessionStorage.removeItem("test");
17 |
18 | return value;
19 | };
20 |
21 | export {
22 | testSafeLocalStorage,
23 | testSafeSessionStorage,
24 | };
--------------------------------------------------------------------------------
/packages/core/sender/.npmignore:
--------------------------------------------------------------------------------
1 | src/
2 | *.story.js
3 | *.stories.js
4 | *.test.js
5 | tests/
6 | mocks/
7 | lerna.json
8 | *.log
9 | jest*
10 | .flowconfig
11 | .editorconfig
12 | .eslintignore
13 | .eslintrc.js
14 | .circleci/
15 | .sb-static/
16 | .env
17 | .jest-cache/
18 | .jest-coverage/
19 | coverage/
20 | .github/
21 |
22 |
--------------------------------------------------------------------------------
/packages/core/sender/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "1.10.0",
3 | "name": "@rpldy/sender",
4 | "description": "react-uploady's default XHR sender",
5 | "author": "yoav niran (https://github.com/yoavniran)",
6 | "main": "lib/cjs/index.js",
7 | "module": "lib/esm/index.js",
8 | "main:dev": "src/index.js",
9 | "types": "types/index.d.ts",
10 | "license": "MIT",
11 | "homepage": "https://react-uploady.org",
12 | "repository": {
13 | "type": "git",
14 | "url": "https://github.com/rpldy/react-uploady.git",
15 | "directory": "packages/sender"
16 | },
17 | "funding": {
18 | "type": "opencollective",
19 | "url": "https://opencollective.com/react-uploady"
20 | },
21 | "scripts": {
22 | "build": "node ../../../scripts/build.mjs"
23 | },
24 | "dependencies": {
25 | "@rpldy/shared": "^1.10.0"
26 | },
27 | "devDependencies": {
28 | "flow-bin": "^0.272.2"
29 | },
30 | "publishConfig": {
31 | "access": "public"
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/packages/core/sender/src/MissingUrlError.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | export default class MissingUrlError extends Error {
3 | constructor(senderType: string) {
4 | super(`${senderType} didn't receive upload URL`);
5 | this.name = "MissingUrlError";
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/packages/core/sender/src/consts.js:
--------------------------------------------------------------------------------
1 | // @flow
2 |
3 | export const XHR_SENDER_TYPE = "rpldy-sender";
--------------------------------------------------------------------------------
/packages/core/sender/src/index.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import getXhrSend from "./xhrSender/xhrSender";
3 | import { XHR_SENDER_TYPE } from "./consts";
4 | import type { SendMethod, SendOptions } from "./types";
5 |
6 | const send: SendMethod = getXhrSend();
7 |
8 | export default send;
9 |
10 | export {
11 | send,
12 | getXhrSend,
13 | XHR_SENDER_TYPE,
14 | };
15 |
16 | export { default as MissingUrlError } from "./MissingUrlError";
17 |
18 | export type {
19 | SendMethod,
20 | OnProgress,
21 | SenderProgressEvent,
22 | SendOptions,
23 | SendResult,
24 | } from "./types";
25 |
26 |
--------------------------------------------------------------------------------
/packages/core/sender/types/index.test-d.tsx:
--------------------------------------------------------------------------------
1 | import send, { OnProgress } from "./index";
2 | import { createBatchItem } from "@rpldy/shared";
3 |
4 | const batchItem = createBatchItem({ name: "test.png", size: 123, type: "image", lastModified: 1234 }, "b1");
5 |
6 | const onProgress: OnProgress = ({ loaded, total }) => {
7 | console.log("uploaded/completed", { loaded, total });
8 | };
9 |
10 | const testSend = async (): Promise => {
11 |
12 | const sendResult = send([batchItem], "test.com", {
13 | method: "POST",
14 | forceJsonResponse: true,
15 | paramName: "file",
16 | headers: {
17 | "x-test": "111"
18 | }
19 | }, onProgress);
20 |
21 | const result = await sendResult.request;
22 |
23 | console.log(result.response);
24 | };
25 |
26 | export {
27 | testSend,
28 | };
29 |
--------------------------------------------------------------------------------
/packages/core/shared/.npmignore:
--------------------------------------------------------------------------------
1 | src/
2 | *.story.js
3 | *.stories.js
4 | *.test.js
5 | tests/
6 | mocks/
7 | lerna.json
8 | *.log
9 | jest*
10 | .flowconfig
11 | .editorconfig
12 | .eslintignore
13 | .eslintrc.js
14 | .circleci/
15 | .sb-static/
16 | .env
17 | .jest-cache/
18 | .jest-coverage/
19 | coverage/
20 | .github/
21 |
22 |
--------------------------------------------------------------------------------
/packages/core/shared/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "1.10.0",
3 | "name": "@rpldy/shared",
4 | "description": "internal set of utils+types for react-uploady",
5 | "author": "yoav niran (https://github.com/yoavniran)",
6 | "main": "lib/cjs/index.js",
7 | "module": "lib/esm/index.js",
8 | "main:dev": "src/index.js",
9 | "types": "types/index.d.ts",
10 | "license": "MIT",
11 | "homepage": "https://react-uploady.org",
12 | "repository": {
13 | "type": "git",
14 | "url": "https://github.com/rpldy/react-uploady.git",
15 | "directory": "packages/shared"
16 | },
17 | "funding": {
18 | "type": "opencollective",
19 | "url": "https://opencollective.com/react-uploady"
20 | },
21 | "scripts": {
22 | "build": "node ../../../scripts/build.mjs"
23 | },
24 | "devDependencies": {
25 | "flow-bin": "^0.272.2"
26 | },
27 | "dependencies": {
28 | "invariant": "^2.2.4",
29 | "just-throttle": "^4.2.0"
30 | },
31 | "publishConfig": {
32 | "access": "public"
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/packages/core/shared/src/consts.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | export const DEBUG_LOG_KEY = "__rpldy-logger-debug__";
3 |
--------------------------------------------------------------------------------
/packages/core/shared/src/enums.js:
--------------------------------------------------------------------------------
1 | // @flow
2 |
3 | enum BATCH_STATES {
4 | PENDING ="pending",
5 | ADDED = "added",
6 | PROCESSING = "processing",
7 | UPLOADING = "uploading",
8 | CANCELLED = "cancelled",
9 | FINISHED = "finished",
10 | ABORTED = "aborted",
11 | ERROR = "error",
12 | }
13 |
14 | enum FILE_STATES {
15 | PENDING = "pending",
16 | ADDED = "added",
17 | UPLOADING = "uploading",
18 | CANCELLED = "cancelled",
19 | FINISHED = "finished",
20 | ERROR = "error",
21 | ABORTED = "aborted",
22 | }
23 |
24 | export {
25 | BATCH_STATES,
26 | FILE_STATES,
27 | };
28 |
--------------------------------------------------------------------------------
/packages/core/shared/src/logger.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import { DEBUG_LOG_KEY } from "./consts";
3 | import hasWindow from "./utils/hasWindow";
4 |
5 | let isDebug: ?boolean = null;
6 |
7 | const isDebugOn = (): boolean | string => {
8 | if (typeof isDebug !== "boolean") {
9 | isDebug = (hasWindow() &&
10 | (("location" in window && !!~window.location.search.indexOf("rpldy_debug=true")) ||
11 | window[DEBUG_LOG_KEY] === true));
12 | }
13 |
14 | return !!isDebug;
15 | };
16 |
17 | const setDebug = (debugOn: boolean) => {
18 | if (hasWindow()) {
19 | window[DEBUG_LOG_KEY] = debugOn;
20 | }
21 |
22 | isDebug = debugOn ? true : null;
23 | };
24 |
25 | const debugLog = (...args: any[]) => {
26 | if (isDebugOn()) {
27 | // eslint-disable-next-line no-console
28 | console.log(...args);
29 | }
30 | };
31 |
32 | export {
33 | isDebugOn,
34 | setDebug,
35 | debugLog,
36 | };
37 |
--------------------------------------------------------------------------------
/packages/core/shared/src/request/XhrPromise.js:
--------------------------------------------------------------------------------
1 | // @flow
2 |
3 | class XhrPromise extends Promise {
4 | xhr: XMLHttpRequest;
5 |
6 | constructor(fn: any, req: XMLHttpRequest) {
7 | super(fn);
8 | this.xhr = req;
9 | }
10 | }
11 |
12 | export default XhrPromise;
13 |
--------------------------------------------------------------------------------
/packages/core/shared/src/request/index.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import request from "./request";
3 |
4 | export { default as parseResponseHeaders } from "./parseResponseHeaders";
5 | export { default as XhrPromise } from "./XhrPromise";
6 |
7 | export default request;
8 |
9 |
--------------------------------------------------------------------------------
/packages/core/shared/src/request/parseResponseHeaders.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import { debugLog } from "../logger";
3 | import type { Headers } from "../types";
4 |
5 | const parseResponseHeaders = (xhr: XMLHttpRequest): ?Headers => {
6 | let resHeaders;
7 |
8 | try {
9 | resHeaders = xhr.getAllResponseHeaders().trim()
10 | .split(/[\r\n]+/)
11 | .reduce((res, line: string) => {
12 | const [key, val] = line.split(": ");
13 | res[key] = val;
14 | return res;
15 | }, ({}: { [string]: any }));
16 | } catch (ex) { // eslint-disable-line
17 | debugLog("uploady.request: failed to read response headers", xhr);
18 | }
19 |
20 | return resHeaders;
21 | };
22 |
23 | export default parseResponseHeaders;
24 |
--------------------------------------------------------------------------------
/packages/core/shared/src/request/request.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import XhrPromise from "./XhrPromise";
3 | import type { RequestOptions } from "../types";
4 |
5 | const setHeaders = (req: XMLHttpRequest, headers: Object): ?Headers => {
6 | if (headers) {
7 | Object.keys(headers).forEach((name) => {
8 | if (headers[name] !== undefined) {
9 | req.setRequestHeader(name, headers[name]);
10 | }
11 | });
12 | }
13 | };
14 |
15 | const request = (url: string, data?: mixed, options: ?RequestOptions = {}): XhrPromise => {
16 | const req = new XMLHttpRequest();
17 |
18 | return new XhrPromise(
19 | (resolve, reject) => {
20 | req.onerror = () => reject(req);
21 | req.ontimeout = () => reject(req);
22 | req.onabort = () => reject(req);
23 | req.onload = () => resolve(req);
24 |
25 | req.open((options?.method || "GET"), url);
26 | setHeaders(req, options?.headers);
27 | req.withCredentials = !!options?.withCredentials;
28 |
29 | options?.preSend?.(req);
30 |
31 | req.send(data);
32 | }, req);
33 | };
34 |
35 | export default request;
36 |
--------------------------------------------------------------------------------
/packages/core/shared/src/request/tests/parseResponseHeaders.test.js:
--------------------------------------------------------------------------------
1 | import parseResponseHeaders from "../parseResponseHeaders";
2 |
3 | describe("parseResponseHeaders tests", () => {
4 | const getAllResponseHeaders = vi.fn(() => `content-type: application/json
5 | x-header: test`);
6 |
7 | it("should parse headers", () => {
8 | const headers = parseResponseHeaders({
9 | getAllResponseHeaders
10 | });
11 |
12 | expect(headers).toEqual({
13 | "content-type": "application/json",
14 | "x-header": "test",
15 | });
16 | });
17 |
18 | it("should fail silently", () => {
19 | getAllResponseHeaders.mockImplementationOnce(() => {
20 | throw new Error("bla");
21 | });
22 |
23 | const headers = parseResponseHeaders({
24 | getAllResponseHeaders
25 | });
26 |
27 | expect(headers).toBeUndefined();
28 | });
29 | });
30 |
--------------------------------------------------------------------------------
/packages/core/shared/src/triggerCancellable.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import type { Trigger, TriggerCancellableOutcome } from "./types";
3 |
4 | const triggerCancellable = (trigger: Trigger, event?: string, ...args?: mixed[]): TriggerCancellableOutcome => {
5 | const doTrigger = (event: string, ...args?: mixed[]): Promise =>
6 | new Promise((resolve, reject) => {
7 | const results: Promise[] = trigger(event, ...args);
8 |
9 | if (results && results.length) {
10 | Promise.all(results)
11 | .catch(reject)
12 | .then((resolvedResults) =>
13 | resolvedResults && resolve(!!~resolvedResults
14 | .findIndex((r: any) => r === false)));
15 | } else {
16 | resolve(false);
17 | }
18 | });
19 |
20 | return event ? doTrigger(event, ...args) : doTrigger;
21 | };
22 |
23 | export default triggerCancellable;
24 |
--------------------------------------------------------------------------------
/packages/core/shared/src/utils/clone.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import merge, { isMergeObj } from "./merge";
3 |
4 | type MergeFn = (target: Object, ...sources: Object[]) => Object;
5 |
6 | /**
7 | * does deep clone to the passed object, returning a new object
8 | * @param obj
9 | * @param mergeFn the merge function to use (default: utils/merge)
10 | * @returns {Object}
11 | */
12 | const clone = (obj: Object, mergeFn: MergeFn = merge): Object =>
13 | isMergeObj(obj) ?
14 | mergeFn((Array.isArray(obj) ? [] : {}), obj) :
15 | obj;
16 |
17 | export default clone;
18 |
--------------------------------------------------------------------------------
/packages/core/shared/src/utils/devFreeze.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import isProduction from "./isProduction";
3 |
4 | const devFreeze = (obj: Object): Object =>
5 | isProduction() ? obj : Object.freeze(obj);
6 |
7 | export default devFreeze;
8 |
--------------------------------------------------------------------------------
/packages/core/shared/src/utils/hasWindow.js:
--------------------------------------------------------------------------------
1 | // @flow
2 |
3 | const hasWindow = (): boolean => {
4 | return typeof window === "object" && !!window.document;
5 | };
6 |
7 | export default hasWindow;
8 |
--------------------------------------------------------------------------------
/packages/core/shared/src/utils/index.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import isFunction from "./isFunction";
3 | import isSamePropInArrays from "./isSamePropInArrays";
4 | import devFreeze from "./devFreeze";
5 | import merge, { getMerge } from "./merge";
6 | import clone from "./clone";
7 | import pick from "./pick";
8 | import isPlainObject from "./isPlainObject";
9 | import hasWindow from "./hasWindow";
10 | import isProduction from "./isProduction";
11 | import isPromise from "./isPromise";
12 | import scheduleIdleWork from "./scheduleIdleWork";
13 | import isEmpty from "./isEmpty";
14 |
15 | export {
16 | isFunction,
17 | isSamePropInArrays,
18 | devFreeze,
19 | merge,
20 | getMerge,
21 | clone,
22 | pick,
23 | isPlainObject,
24 | hasWindow,
25 | isProduction,
26 | isPromise,
27 | scheduleIdleWork,
28 | isEmpty,
29 | };
30 |
--------------------------------------------------------------------------------
/packages/core/shared/src/utils/isEmpty.js:
--------------------------------------------------------------------------------
1 | // @flow
2 |
3 | type Empty = null | void;
4 |
5 | function isEmpty (val: any): val is Empty {
6 | return (val === null || val === undefined);
7 | }
8 |
9 | export default isEmpty;
10 |
--------------------------------------------------------------------------------
/packages/core/shared/src/utils/isFunction.js:
--------------------------------------------------------------------------------
1 | // @flow
2 |
3 | function isFunction(f: any): f is (...args: any[]) => any {
4 | return typeof (f) === "function";
5 | }
6 |
7 | module.exports = isFunction;
8 |
--------------------------------------------------------------------------------
/packages/core/shared/src/utils/isPlainObject.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | const isPlainObject = (obj: Object): boolean => {
3 | const proto = Object.getPrototypeOf(Object(obj));
4 |
5 | return !!obj &&
6 | typeof obj === "object" &&
7 | (proto?.constructor.name === "Object" || proto === null) &&
8 | //$FlowExpectedError[method-unbinding]
9 | !Object.prototype.hasOwnProperty.call(obj, "__proto__");
10 | };
11 |
12 | export default isPlainObject;
13 |
--------------------------------------------------------------------------------
/packages/core/shared/src/utils/isProduction.js:
--------------------------------------------------------------------------------
1 | function isProduction() {
2 | return process.env.NODE_ENV === "production";
3 | }
4 |
5 | module.exports = isProduction;
6 |
--------------------------------------------------------------------------------
/packages/core/shared/src/utils/isPromise.js:
--------------------------------------------------------------------------------
1 | // @flow
2 |
3 | function isPromise(obj: any): implies obj is Promise {
4 | return !!obj && typeof obj === "object" && typeof obj.then === "function";
5 | }
6 |
7 | export default isPromise;
8 |
9 |
--------------------------------------------------------------------------------
/packages/core/shared/src/utils/isSamePropInArrays.js:
--------------------------------------------------------------------------------
1 | // @flow
2 |
3 | const getPropsExtractor = (prop: string | string[]) => {
4 | const props = [].concat(prop);
5 |
6 | return (arr: Object[]) =>
7 | arr.map((i) => props.map((p) => i[p]).join());
8 | };
9 |
10 | /*
11 | stringifies props together - will return true for same type of value (ex: function)
12 | even if refs are different
13 | */
14 | const isSamePropInArrays = (arr1: Object[], arr2: Object[], prop: string | string[]): boolean => {
15 | let diff = true;
16 | const propsExtractor = getPropsExtractor(prop);
17 |
18 | if (arr1 && arr2 && arr1.length === arr2.length) {
19 | const props1 = propsExtractor(arr1),
20 | props2 = propsExtractor(arr2);
21 |
22 | diff = !!props1.find((p, i) => p !== props2[i]);
23 | }
24 |
25 | return !diff;
26 | };
27 |
28 |
29 | export default isSamePropInArrays;
30 |
--------------------------------------------------------------------------------
/packages/core/shared/src/utils/pick.js:
--------------------------------------------------------------------------------
1 | // @flow
2 |
3 | const pick = (obj: Object, props: string[]): ?Object =>
4 | obj && Object.keys(obj).reduce((res, key) => {
5 | if (~props.indexOf(key)) {
6 | res[key] = obj[key];
7 | }
8 | return res;
9 | }, ({}: { [string]: any }));
10 |
11 | export default pick;
12 |
--------------------------------------------------------------------------------
/packages/core/shared/src/utils/scheduleIdleWork.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import hasWindow from "./hasWindow";
3 |
4 | const supportsIdle = (hasWindow() && window.requestIdleCallback);
5 | const scheduler = supportsIdle ? window.requestIdleCallback : setTimeout;
6 | const clear = supportsIdle ? window.cancelIdleCallback : clearTimeout;
7 |
8 | type ClearSchedule = () => void;
9 |
10 | const scheduleIdleWork = (fn: Function, timeout: number = 0): ClearSchedule => {
11 | //$FlowIssue[incompatible-call] flow doesnt understand we only pass object to requestIdle...
12 | const handler = scheduler(fn, supportsIdle ? { timeout } : timeout);
13 |
14 | return () => clear(handler);
15 | };
16 |
17 | export default scheduleIdleWork;
18 |
--------------------------------------------------------------------------------
/packages/core/shared/src/utils/tests/devFreeze.test.js:
--------------------------------------------------------------------------------
1 | import devFreeze from "../devFreeze";
2 |
3 | describe("devFreeze tests", () => {
4 | let env;
5 |
6 | beforeAll(() => {
7 | env = process.env.NODE_ENV;
8 | });
9 |
10 | afterAll(() => {
11 | process.env.NODE_ENV = env;
12 | });
13 |
14 | it("should freeze when not in production", () => {
15 | const obj = {};
16 | const frozen = devFreeze(obj);
17 | expect(() => {
18 | frozen.test = true;
19 | }).toThrow();
20 | });
21 |
22 | it("should not freeze when in production", () => {
23 |
24 | process.env.NODE_ENV = "production";
25 |
26 | const obj = {};
27 | const frozen = devFreeze(obj);
28 | frozen.test = true;
29 | expect(frozen.test).toBe(true);
30 | });
31 |
32 | });
33 |
--------------------------------------------------------------------------------
/packages/core/shared/src/utils/tests/isFunction.test.js:
--------------------------------------------------------------------------------
1 | import isFunction from "../isFunction";
2 |
3 | describe("isFunction tests", () => {
4 |
5 | it("should return true for function", () => {
6 |
7 | const fn1 = () => {
8 | };
9 |
10 | function fn2() {
11 | }
12 |
13 | expect(isFunction(fn1)).toBe(true);
14 | expect(isFunction(fn2)).toBe(true);
15 | });
16 |
17 | it("should return false for anything not a function", () => {
18 |
19 | expect(isFunction({})).toBe(false);
20 | expect(isFunction(null)).toBe(false);
21 | expect(isFunction(false)).toBe(false);
22 | expect(isFunction("a")).toBe(false);
23 | expect(isFunction(true)).toBe(false);
24 | });
25 | });
26 |
--------------------------------------------------------------------------------
/packages/core/shared/src/utils/tests/isPlainObject.test.js:
--------------------------------------------------------------------------------
1 | import isPlainObject from "../isPlainObject";
2 |
3 | describe("isPlainObject tests", () => {
4 | it.each([
5 | [false, JSON.parse(`{"__proto__":{"pollutedKey":123}}`)],
6 | [false, true],
7 | [false, false],
8 | [false, 0],
9 | [false, 1],
10 | [false, [1,2,3]],
11 | [false, []],
12 | [false, ()=>{}],
13 | [false, function(){}],
14 | [false, new Map()],
15 | [false, new Set()],
16 | [true, {}],
17 | [true, Object({})],
18 | [true, Object.create(null)],
19 | [true, new Object({})],
20 | ])("should return %s for %s", (result, val) => {
21 | expect(isPlainObject(val)).toBe(result);
22 | });
23 | });
24 |
--------------------------------------------------------------------------------
/packages/core/shared/src/utils/tests/isPromise.test.js:
--------------------------------------------------------------------------------
1 | import isPromise from "../isPromise";
2 |
3 | describe("isPromise tests", () => {
4 | it.each([
5 | [null, false],
6 | [undefined, false],
7 | [false, false],
8 | ["dasd", false],
9 | [10, false],
10 | [{}, false],
11 | [{ test: true }, false],
12 | [{ then: true }, false],
13 | [new Promise((resolve) => { resolve(); }), true],
14 | [Promise.resolve(false), true],
15 | ])
16 | ("test isPromise with - %s should be: %s", (obj, result) => {
17 | expect(isPromise(obj)).toBe(result);
18 | });
19 | });
20 |
--------------------------------------------------------------------------------
/packages/core/shared/src/utils/tests/pick.test.js:
--------------------------------------------------------------------------------
1 | import pick from "../pick";
2 |
3 | describe("pick tests", () => {
4 | it("should return null for null", () => {
5 | expect(pick(null)).toBeNull();
6 | });
7 |
8 | it("should return empty obj for no props", () => {
9 | expect(pick({ test: true }, [])).toEqual({});
10 | });
11 |
12 | it("should return requested props", () => {
13 | expect(pick({
14 | foo: "aaa",
15 | bar: "bbb",
16 | test: true,
17 | more: { level: 2 }
18 | }, ["foo", "more"])).toEqual({
19 | foo: "aaa",
20 | more: { level: 2 }
21 | });
22 | });
23 |
24 | it("should not allow prototype pollution", () => {
25 | const b = JSON.parse(`{"__proto__":{"pollutedKey":123}, "foo": "bar"}`);
26 |
27 | const res = pick(b, ["foo"]);
28 |
29 | expect(res).toEqual({ foo: "bar" });
30 | expect(res.pollutedKey).toBeUndefined();
31 | expect({}.pollutedKey).toBeUndefined();
32 | });
33 | });
34 |
--------------------------------------------------------------------------------
/packages/core/shared/types/index.test-d.tsx:
--------------------------------------------------------------------------------
1 | import { createBatchItem } from "./index";
2 |
3 |
4 | const testCreateBatchItem = (): void => {
5 |
6 | const batchItemFile = createBatchItem({ name: "test.png", size: 123, type: "image", lastModified: 1234 }, "b1");
7 |
8 | console.log(batchItemFile.id, batchItemFile.batchId, batchItemFile.file.name);
9 |
10 | const batchItemUrl = createBatchItem("https://my-file.com", "b2");
11 |
12 | console.log(batchItemUrl.id, batchItemUrl.batchId, batchItemUrl.url);
13 | };
14 |
15 | export {
16 | testCreateBatchItem,
17 | };
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/packages/core/simple-state/.npmignore:
--------------------------------------------------------------------------------
1 | src/
2 | *.story.js
3 | *.stories.js
4 | *.test.js
5 | tests/
6 | mocks/
7 | lerna.json
8 | *.log
9 | jest*
10 | .flowconfig
11 | .editorconfig
12 | .eslintignore
13 | .eslintrc.js
14 | .circleci/
15 | .sb-static/
16 | .env
17 | .jest-cache/
18 | .jest-coverage/
19 | coverage/
20 | .github/
21 |
22 |
--------------------------------------------------------------------------------
/packages/core/simple-state/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "1.10.0",
3 | "name": "@rpldy/simple-state",
4 | "description": "deep proxy object, so it's only updateable through an update method",
5 | "author": "yoav niran (https://github.com/yoavniran)",
6 | "main": "lib/cjs/index.js",
7 | "module": "lib/esm/index.js",
8 | "main:dev": "src/index.js",
9 | "types": "types/index.d.ts",
10 | "license": "MIT",
11 | "homepage": "https://react-uploady.org",
12 | "repository": {
13 | "type": "git",
14 | "url": "https://github.com/rpldy/react-uploady.git",
15 | "directory": "packages/simple-state"
16 | },
17 | "funding": {
18 | "type": "opencollective",
19 | "url": "https://opencollective.com/react-uploady"
20 | },
21 | "scripts": {
22 | "build": "node ../../../scripts/build.mjs"
23 | },
24 | "dependencies": {
25 | "@rpldy/shared": "^1.10.0"
26 | },
27 | "devDependencies": {
28 | "flow-bin": "^0.272.2"
29 | },
30 | "publishConfig": {
31 | "access": "public"
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/packages/core/simple-state/src/consts.js:
--------------------------------------------------------------------------------
1 |
2 | export const PROXY_SYM = Symbol.for("__rpldy-sstt-proxy__");
3 |
4 | export const STATE_SYM = Symbol.for("__rpldy-sstt-state__");
5 |
--------------------------------------------------------------------------------
/packages/core/simple-state/src/index.js:
--------------------------------------------------------------------------------
1 | // @flow
2 |
3 | export { default, default as createState, unwrap } from "./createState";
4 |
5 | export {
6 | isProxy,
7 | isProxiable,
8 | } from "./utils";
9 |
10 |
--------------------------------------------------------------------------------
/packages/core/simple-state/src/tests/mocks/getTestData.mock.js:
--------------------------------------------------------------------------------
1 | export default () => ({
2 | arr: [1, 2, 3],
3 | sub: {
4 | foo: "bar",
5 | more: {
6 | test: true
7 | },
8 | },
9 | children: [
10 | {
11 | id: 1,
12 | name: "child-a"
13 | },
14 | {
15 | id: 2,
16 | name: "child-b"
17 | }
18 | ],
19 | });
20 |
--------------------------------------------------------------------------------
/packages/core/simple-state/src/types.js:
--------------------------------------------------------------------------------
1 | // @flow
2 |
3 | export type SimpleState = {
4 | state: T,
5 | update: ((T) => void) => T,
6 | unwrap: (?Object) => T | Object,
7 | }
--------------------------------------------------------------------------------
/packages/core/simple-state/src/utils.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import { PROXY_SYM } from "./consts";
3 | import { isPlainObject, isProduction, hasWindow } from "@rpldy/shared";
4 |
5 | const isProxy = (obj: Object): boolean =>
6 | !isProduction() && !!obj &&
7 | !!~Object.getOwnPropertySymbols(obj).indexOf(PROXY_SYM);
8 |
9 | //check if object is File or react-native file object (it wont by instanceof File in react-native)
10 | const isNativeFile = (obj: any) => (hasWindow() && obj instanceof File) || (obj.name && obj.size && obj.uri);
11 |
12 | const isProxiable = (obj: any): boolean =>
13 | Array.isArray(obj) || (isPlainObject(obj) && !isNativeFile(obj));
14 |
15 | export {
16 | isProxy,
17 | isProxiable,
18 | };
19 |
--------------------------------------------------------------------------------
/packages/core/simple-state/types/index.d.ts:
--------------------------------------------------------------------------------
1 |
2 | export interface SimpleState {
3 | state: T;
4 | update: (updater: (state: T) => void) => T;
5 | unwrap: (proxy?: Record) => T | Record;
6 | }
7 |
8 | declare const createState: (obj: Record) => SimpleState;
9 |
10 | export const unwrap: (proxy: unknown) => unknown;
11 |
12 | export const isProxy: (proxy: unknown) => boolean;
13 |
14 | export const isProxiable: (obj: unknown) => boolean;
15 |
16 | export default createState;
17 |
--------------------------------------------------------------------------------
/packages/core/simple-state/types/index.test-d.ts:
--------------------------------------------------------------------------------
1 | import createState from "./index";
2 |
3 | interface State {
4 | foo: string,
5 | bob: string,
6 | }
7 |
8 | const testCreateState = (): void => {
9 |
10 | const { state, update, unwrap } = createState({
11 | foo: "bar",
12 | bob: "mcintyre",
13 | });
14 |
15 | console.log(state.foo);
16 |
17 | update((state)=> {
18 | state.bob = "alice";
19 | });
20 |
21 | const org = unwrap();
22 |
23 | console.log(org);
24 | };
25 |
26 | export {
27 | testCreateState,
28 | };
--------------------------------------------------------------------------------
/packages/core/tus-sender/.npmignore:
--------------------------------------------------------------------------------
1 | src/
2 | *.story.js
3 | *.stories.js
4 | *.test.js
5 | tests/
6 | mocks/
7 | lerna.json
8 | *.log
9 | jest*
10 | .flowconfig
11 | .editorconfig
12 | .eslintignore
13 | .eslintrc.js
14 | .circleci/
15 | .sb-static/
16 | .env
17 | .jest-cache/
18 | .jest-coverage/
19 | coverage/
20 | .github/
21 |
22 |
--------------------------------------------------------------------------------
/packages/core/tus-sender/TusSender.storydoc.mdx:
--------------------------------------------------------------------------------
1 | import { Markdown } from "@storybook/blocks";
2 | import readme from "./README.md?raw";
3 |
4 | {readme}
5 |
--------------------------------------------------------------------------------
/packages/core/tus-sender/src/consts.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import { devFreeze } from "@rpldy/shared";
3 |
4 | export const TUS_EXT: symbol = Symbol.for("__upldy-tus__");
5 |
6 | export const TUS_SENDER_TYPE = "rpldy-tus-sender";
7 |
8 | export const SUCCESS_CODES = [200, 201, 204];
9 |
10 | export const KNOWN_EXTENSIONS = {
11 | CREATION: "creation",
12 | CREATION_WITH_UPLOAD: "creation-with-upload",
13 | TERMINATION: "termination",
14 | CONCATENATION: "concatenation",
15 | CREATION_DEFER_LENGTH: "creation-defer-length",
16 | };
17 |
18 | export const FD_STORAGE_PREFIX = "rpldy_tus_fd_";
19 |
20 | export const TUS_EVENTS: Object = devFreeze({
21 | RESUME_START: "RESUME_START"
22 | });
23 |
--------------------------------------------------------------------------------
/packages/core/tus-sender/src/defaults.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import { devFreeze } from "@rpldy/shared";
3 | import { CHUNKED_DEFAULT_OPTIONS } from "@rpldy/chunked-sender";
4 |
5 | export const DEFAULT_OPTIONS: Object = devFreeze({
6 | ...CHUNKED_DEFAULT_OPTIONS,
7 | featureDetection: false,
8 | featureDetectionUrl: null,
9 | version: "1.0.0",
10 | resume: true,
11 | overrideMethod: false,
12 | deferLength: false,
13 | sendDataOnCreate: false,
14 | storagePrefix: "__rpldy-tus__",
15 | lockedRetryDelay: 2000,
16 | forgetOnSuccess: false,
17 | ignoreModifiedDateInStorage: false,
18 | });
19 |
--------------------------------------------------------------------------------
/packages/core/tus-sender/src/getTusEnhancer.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import createTusSender from "./tusSender";
3 | import { TUS_EXT } from "./consts";
4 |
5 | import type { UploaderType, UploaderEnhancer, UploaderCreateOptions } from "@rpldy/uploader";
6 | import type { TriggerMethod } from "@rpldy/life-events";
7 | import type { TusOptions } from "./types";
8 |
9 | const getTusEnhancer = (options?: TusOptions): UploaderEnhancer => {
10 | //return uploader enhancer
11 | return (uploader: UploaderType, trigger: TriggerMethod): UploaderType => {
12 | const sender = createTusSender(uploader, options, trigger);
13 | uploader.update({ send: sender.send });
14 |
15 | uploader.registerExtension(TUS_EXT, {
16 | getOptions: sender.getOptions,
17 | });
18 |
19 | return uploader;
20 | };
21 | };
22 |
23 | export default getTusEnhancer;
24 |
--------------------------------------------------------------------------------
/packages/core/tus-sender/src/index.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import getTusEnhancer from "./getTusEnhancer";
3 |
4 | export {
5 | CHUNKING_SUPPORT
6 | } from "@rpldy/chunked-sender";
7 |
8 | export {
9 | getTusEnhancer,
10 | };
11 |
12 | export {
13 | clearResumables
14 | } from "./resumableStore";
15 |
16 | export {
17 | TUS_SENDER_TYPE,
18 | TUS_EXT,
19 | TUS_EVENTS,
20 | } from "./consts";
21 |
22 | export default getTusEnhancer;
23 |
24 | export type {
25 | TusOptions,
26 | ResumeStartEventData,
27 | ResumeStartEventResponse,
28 | } from "./types";
29 |
--------------------------------------------------------------------------------
/packages/core/tus-sender/src/tests/getTusEnhancer.test.js:
--------------------------------------------------------------------------------
1 | import createTusSender from "../tusSender";
2 | import getTusEnhancer from "../getTusEnhancer";
3 | import { TUS_EXT } from "../consts";
4 |
5 | vi.mock("../tusSender");
6 |
7 | describe("getTusEnhancer tests", () => {
8 |
9 | it("should enhance uploader", () => {
10 | const options = { parallel: 2 };
11 |
12 | const send = vi.fn(),
13 | getOptions = vi.fn();
14 |
15 | createTusSender.mockReturnValueOnce({
16 | send,
17 | getOptions
18 | });
19 |
20 | const enhancer = getTusEnhancer(options);
21 |
22 | const uploader = {
23 | update: vi.fn(),
24 | registerExtension: vi.fn(),
25 | };
26 |
27 | enhancer(uploader, "trigger");
28 |
29 | expect(uploader.update).toHaveBeenCalledWith({ send });
30 | expect(createTusSender).toHaveBeenCalledWith(uploader, options, "trigger");
31 |
32 | expect(uploader.registerExtension).toHaveBeenCalledWith(TUS_EXT, expect.any(Object));
33 |
34 | uploader.registerExtension.mock.calls[0][1].getOptions();
35 | expect(getOptions).toHaveBeenCalled();
36 | });
37 |
38 | });
39 |
--------------------------------------------------------------------------------
/packages/core/tus-sender/src/tests/tusState.mock.js:
--------------------------------------------------------------------------------
1 |
2 | export default (initState = {}) => {
3 | let state = {
4 | options: {
5 | version: "1",
6 | ...initState?.options,
7 | },
8 | featureDetection: {},
9 | ...initState
10 | };
11 |
12 | return {
13 | getState: vi.fn(() => state),
14 | updateState: vi.fn((updater) => updater(state)),
15 | }
16 | };
17 |
--------------------------------------------------------------------------------
/packages/core/tus-sender/src/tusSender/index.js:
--------------------------------------------------------------------------------
1 | import createTusSender from "./createTusSender";
2 | export default createTusSender;
3 |
--------------------------------------------------------------------------------
/packages/core/tus-sender/src/tusSender/initTusUpload/createStateItemData.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import type { BatchItem } from "@rpldy/shared";
3 | import type { State, TusOptions, TusState } from "../../types";
4 |
5 | const createStateItemData = (
6 | item: BatchItem,
7 | tusState: TusState,
8 | options: TusOptions,
9 | parallelIdentifier: ?string,
10 | orgItemId: ?string
11 | ) => {
12 | tusState.updateState((state: State) => {
13 | state.items[item.id] = {
14 | id: item.id,
15 | uploadUrl: null,
16 | size: item.file.size,
17 | offset: 0,
18 |
19 | //will be populated only for items that represent a parallel part:
20 | parallelIdentifier,
21 | orgItemId,
22 | };
23 | });
24 | };
25 |
26 | export default createStateItemData;
27 |
--------------------------------------------------------------------------------
/packages/core/tus-sender/src/tusSender/initTusUpload/index.js:
--------------------------------------------------------------------------------
1 | export { default as initTusUpload } from "./initTusUpload";
2 | export { default as initParallelTusUpload } from "./initParallelTusUpload";
3 |
--------------------------------------------------------------------------------
/packages/core/tus-sender/src/tusSender/tests/hadnleEvents-no-chunking.test.js:
--------------------------------------------------------------------------------
1 | import handleEvents from "../handleEvents";
2 |
3 | vi.mock("@rpldy/chunked-sender", async () => {
4 | const org = await vi.importActual("@rpldy/chunked-sender");
5 |
6 | return {
7 | ...org,
8 | CHUNKING_SUPPORT: false,
9 | };
10 | });
11 |
12 | describe("handleEvents without chunking support tests", () => {
13 | const uploader = {
14 | on: vi.fn(),
15 | };
16 |
17 | const chunkedSender = {
18 | on: vi.fn(),
19 | };
20 |
21 | it("should do nothing with no chunk support", () => {
22 | handleEvents(uploader, null, chunkedSender);
23 | expect(uploader.on).not.toHaveBeenCalled();
24 | expect(chunkedSender.on).not.toHaveBeenCalled();
25 | });
26 | });
27 |
--------------------------------------------------------------------------------
/packages/core/tus-sender/src/tusSender/tests/tusSend-no-chunking.test.js:
--------------------------------------------------------------------------------
1 | import xhrSend from "@rpldy/sender";
2 | import getTusSend from "../tusSend";
3 |
4 | vi.mock("@rpldy/chunked-sender", async () => {
5 | const org = await vi.importActual("@rpldy/chunked-sender");
6 | return {
7 | ...org,
8 | CHUNKING_SUPPORT: false,
9 | };
10 | });
11 |
12 |
13 | describe("tusSend - no chunking support tests", () => {
14 | const chunkedSender = { send: vi.fn() };
15 |
16 | it("should use xhrSender for no chunk support", () => {
17 | const send = getTusSend(chunkedSender, null);
18 | expect(send).toBe(xhrSend);
19 | });
20 | });
21 |
--------------------------------------------------------------------------------
/packages/core/tus-sender/src/utils.js:
--------------------------------------------------------------------------------
1 | // @flow
2 |
3 | import { merge } from "@rpldy/shared";
4 | import { DEFAULT_OPTIONS } from "./defaults";
5 | import type { TusOptions } from "./types";
6 |
7 | const getMandatoryOptions = (options: ?TusOptions): TusOptions =>
8 | merge({}, DEFAULT_OPTIONS, options);
9 |
10 | export {
11 | getMandatoryOptions,
12 | };
13 |
--------------------------------------------------------------------------------
/packages/core/tus-sender/types/index.test-d.ts:
--------------------------------------------------------------------------------
1 | import getTusEnhancer from "./index";
2 | import createUploader from "@rpldy/uploader";
3 |
4 | const testGetTusEnhancer = (): void => {
5 | const enhancer = getTusEnhancer({
6 | deferLength: true,
7 | parallel: 2,
8 | forgetOnSuccess: true,
9 | });
10 |
11 | const uploader = createUploader({
12 | enhancer,
13 | });
14 |
15 | console.log(uploader.getOptions());
16 | };
17 |
18 | export {
19 | testGetTusEnhancer,
20 | };
--------------------------------------------------------------------------------
/packages/core/uploader/.npmignore:
--------------------------------------------------------------------------------
1 | src/
2 | *.story.js
3 | *.stories.js
4 | *.test.js
5 | tests/
6 | mocks/
7 | lerna.json
8 | *.log
9 | jest*
10 | .flowconfig
11 | .editorconfig
12 | .eslintignore
13 | .eslintrc.js
14 | .circleci/
15 | .sb-static/
16 | .env
17 | .jest-cache/
18 | .jest-coverage/
19 | coverage/
20 | .github/
21 |
22 |
--------------------------------------------------------------------------------
/packages/core/uploader/Uploader.storydoc.mdx:
--------------------------------------------------------------------------------
1 | import { Markdown } from "@storybook/blocks";
2 | import readme from "./README.md?raw";
3 |
4 | {readme}
5 |
--------------------------------------------------------------------------------
/packages/core/uploader/src/composeEnhancers.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import type { UploaderType, UploaderEnhancer } from "@rpldy/raw-uploader";
3 |
4 | const composeEnhancers = (...enhancers: UploaderEnhancer[]): ((uploader: UploaderType, ...args: Array) => UploaderType) =>
5 | (uploader: UploaderType, ...args: any[]) =>
6 | enhancers.reduce((enhanced: UploaderType, e: UploaderEnhancer) =>
7 | e(enhanced, ...args) || enhanced, uploader);
8 |
9 | export default composeEnhancers;
10 |
--------------------------------------------------------------------------------
/packages/core/uploader/src/consts.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import { devFreeze, FILE_STATES } from "@rpldy/shared";
3 |
4 | export const UPLOADER_EVENTS: Object = devFreeze({
5 | BATCH_ADD: "BATCH-ADD",
6 | BATCH_START: "BATCH-START",
7 | BATCH_PROGRESS: "BATCH_PROGRESS",
8 | BATCH_FINISH: "BATCH-FINISH",
9 | BATCH_ABORT: "BATCH-ABORT",
10 | BATCH_CANCEL: "BATCH-CANCEL",
11 | BATCH_ERROR: "BATCH-ERROR",
12 | BATCH_FINALIZE: "BATCH-FINALIZE",
13 |
14 | ITEM_START: "FILE-START",
15 | ITEM_CANCEL: "FILE-CANCEL",
16 | ITEM_PROGRESS: "FILE-PROGRESS",
17 | ITEM_FINISH: "FILE-FINISH",
18 | ITEM_ABORT: "FILE-ABORT",
19 | ITEM_ERROR: "FILE-ERROR",
20 | ITEM_FINALIZE: "FILE-FINALIZE",
21 |
22 | REQUEST_PRE_SEND: "REQUEST_PRE_SEND",
23 |
24 | ALL_ABORT: "ALL_ABORT",
25 | });
26 |
27 | export const PROGRESS_DELAY = 50;
28 |
29 | export const SENDER_EVENTS: Object = devFreeze({
30 | ITEM_PROGRESS: "ITEM_PROGRESS",
31 | BATCH_PROGRESS: "BATCH_PROGRESS",
32 | });
33 |
34 | export const ITEM_FINALIZE_STATES = [
35 | FILE_STATES.FINISHED,
36 | FILE_STATES.ERROR,
37 | FILE_STATES.CANCELLED,
38 | FILE_STATES.ABORTED
39 | ];
40 |
--------------------------------------------------------------------------------
/packages/core/uploader/src/defaults.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import { devFreeze } from "@rpldy/shared";
3 |
4 | export const DEFAULT_PARAM_NAME = "file";
5 |
6 | export const DEFAULT_FILTER = (input: any, i: number, arr: any[]): Promise | boolean => true;
7 |
8 | export const DEFAULT_OPTIONS: Object = devFreeze({
9 | autoUpload: true,
10 | clearPendingOnAdd: false,
11 | inputFieldName: "file",
12 | concurrent: false,
13 | maxConcurrent: 2,
14 | grouped: false,
15 | maxGroupSize: 5,
16 | method: "POST",
17 | params: {},
18 | fileFilter: DEFAULT_FILTER,
19 | forceJsonResponse: false,
20 | withCredentials: false,
21 | destination: {},
22 | send: null,
23 | sendWithFormData: true,
24 | formDataAllowUndefined: false,
25 | fastAbortThreshold: 100,
26 | });
27 |
--------------------------------------------------------------------------------
/packages/core/uploader/src/index.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import createUploader from "./uploader";
3 | import composeEnhancers from "./composeEnhancers";
4 | import { UPLOADER_EVENTS } from "./consts";
5 | import { DEFAULT_OPTIONS } from "./defaults";
6 |
7 | export default createUploader;
8 |
9 | export {
10 | UPLOADER_EVENTS,
11 | DEFAULT_OPTIONS,
12 |
13 | composeEnhancers,
14 | createUploader,
15 | };
16 |
17 | export * from "@rpldy/sender";
18 |
19 | export {
20 | FILE_STATES,
21 | BATCH_STATES,
22 | } from "@rpldy/shared";
23 |
24 | export type {
25 | TriggerMethod
26 | } from "@rpldy/life-events";
27 |
28 | export type {
29 | UploaderType,
30 | UploaderEnhancer,
31 | RawCreateOptions,
32 | } from "@rpldy/raw-uploader";
33 |
34 | export type {
35 | UploaderCreateOptions,
36 | UploadyUploaderType
37 | } from "./types";
38 |
--------------------------------------------------------------------------------
/packages/core/uploader/src/queue/index.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | export { default } from "./uploaderQueue";
3 |
--------------------------------------------------------------------------------
/packages/core/uploader/src/tests/mocks/rpldy-uploader.mock.js:
--------------------------------------------------------------------------------
1 | import { UPLOADER_EVENTS } from "../../consts";
2 | import { DEFAULT_OPTIONS } from "../../defaults";
3 |
4 | const uploader = {
5 | add: vi.fn(),
6 | upload: vi.fn(),
7 | clearPending: vi.fn(),
8 | getOptions: vi.fn(),
9 | on: vi.fn(),
10 | once: vi.fn(),
11 | off: vi.fn(),
12 | update: vi.fn(),
13 | abort: vi.fn(),
14 | abortBatch: vi.fn(),
15 | registerExtension: vi.fn(),
16 | getExtension: vi.fn(),
17 | };
18 |
19 | const createUploader = vi.fn(() => uploader);
20 |
21 | // createUploader.UPLOADER_EVENTS = UPLOADER_EVENTS;
22 | // createUploader.DEFAULT_OPTIONS = DEFAULT_OPTIONS;
23 |
24 | vi.doMock("@rpldy/uploader", () => ({
25 | default: createUploader,
26 | UPLOADER_EVENTS,
27 | DEFAULT_OPTIONS,
28 | }));
29 |
30 | export {
31 | uploader,
32 | createUploader,
33 | UPLOADER_EVENTS,
34 | DEFAULT_OPTIONS,
35 | }
36 |
--------------------------------------------------------------------------------
/packages/native/native-uploady/.npmignore:
--------------------------------------------------------------------------------
1 | src/
2 | *.story.js
3 | *.stories.js
4 | *.test.js
5 | tests/
6 | mocks/
7 | lerna.json
8 | *.log
9 | jest*
10 | .flowconfig
11 | .editorconfig
12 | .eslintignore
13 | .eslintrc.js
14 | .circleci/
15 | .sb-static/
16 | .env
17 | .jest-cache/
18 | .jest-coverage/
19 | coverage/
20 | .github/
21 |
22 |
--------------------------------------------------------------------------------
/packages/native/native-uploady/NativeUploady.storydoc.mdx:
--------------------------------------------------------------------------------
1 | import { Markdown } from "@storybook/blocks";
2 | import readme from "./README.md?raw";
3 |
4 | {readme}
5 |
--------------------------------------------------------------------------------
/packages/native/native-uploady/src/NativeUploady.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import React from "react";
3 | import { NoDomUploady } from "@rpldy/shared-ui";
4 |
5 | import type { Node } from "react";
6 | import type { NativeUploadyProps } from "./types";
7 |
8 | const NativeUploady = (props: NativeUploadyProps): Node => ;
9 |
10 | export default NativeUploady;
11 |
--------------------------------------------------------------------------------
/packages/native/native-uploady/src/index.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import NativeUploady from "./NativeUploady";
3 |
4 | export { NativeUploady };
5 | export default NativeUploady;
6 |
7 | export {
8 | UploadyContext,
9 | NoDomUploady,
10 | assertContext,
11 | useUploadOptions,
12 |
13 | useBatchAddListener,
14 | useBatchStartListener,
15 | useBatchProgressListener,
16 | useBatchFinishListener,
17 | useBatchCancelledListener,
18 | useBatchAbortListener,
19 |
20 | useItemStartListener,
21 | useItemFinishListener,
22 | useItemProgressListener,
23 | useItemCancelListener,
24 | useItemErrorListener,
25 | useItemAbortListener,
26 | useItemFinalizeListener,
27 |
28 | useRequestPreSend,
29 |
30 | useAbortAll,
31 | useAbortBatch,
32 | useAbortItem,
33 | } from "@rpldy/shared-ui";
34 |
35 | export type * from "./types";
36 |
--------------------------------------------------------------------------------
/packages/native/native-uploady/src/tests/NativeUploady.test.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { NoDomUploady } from "@rpldy/shared-ui";
3 | import NativeUploady from "../index";
4 |
5 | vi.mock("@rpldy/shared-ui", () => ({
6 | NoDomUploady: vi.fn(() => ),
7 | }));
8 |
9 | describe("NativeUploady tests", () => {
10 | it("should render NativeUploady", () => {
11 | render();
12 |
13 | expect(NoDomUploady).toHaveBeenCalledWith({ debug: true, autoUpload: true }, {});
14 | });
15 | });
16 |
--------------------------------------------------------------------------------
/packages/native/native-uploady/src/types.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import type { NoDomUploadyProps } from "@rpldy/shared-ui";
3 |
4 | export type NativeUploadyProps = {|
5 | ...NoDomUploadyProps,
6 | |}
7 |
--------------------------------------------------------------------------------
/packages/native/native-uploady/types/index.d.ts:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 | import { NoDomUploadyProps } from "@rpldy/shared-ui";
3 |
4 | export * from "@rpldy/shared-ui";
5 |
6 | export const NativeUploady: React.ComponentType;
7 |
8 | export default NativeUploady;
9 |
--------------------------------------------------------------------------------
/packages/native/native-uploady/types/index.test-d.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 | import NativeUploady from "./";
3 | import { useUploadOptions } from "@rpldy/shared-ui";
4 |
5 | const ListOfUploadOptions = () => {
6 | const options = useUploadOptions();
7 |
8 | return
9 | {JSON.stringify(options)}
10 |
;
11 | };
12 |
13 | const testNativeUploady = (): JSX.Element => {
14 | return
15 |
16 | ;
17 | };
18 |
19 | export {
20 | testNativeUploady,
21 | };
22 |
--------------------------------------------------------------------------------
/packages/ui/chunked-uploady/.npmignore:
--------------------------------------------------------------------------------
1 | src/
2 | *.story.js
3 | *.stories.js
4 | *.test.js
5 | tests/
6 | mocks/
7 | lerna.json
8 | *.log
9 | jest*
10 | .flowconfig
11 | .editorconfig
12 | .eslintignore
13 | .eslintrc.js
14 | .circleci/
15 | .sb-static/
16 | .env
17 | .jest-cache/
18 | .jest-coverage/
19 | coverage/
20 | .github/
21 |
22 |
--------------------------------------------------------------------------------
/packages/ui/chunked-uploady/ChunkedUploady.storydoc.mdx:
--------------------------------------------------------------------------------
1 | import { Markdown } from "@storybook/blocks";
2 | import readme from "./README.md?raw";
3 |
4 | {readme}
5 |
--------------------------------------------------------------------------------
/packages/ui/chunked-uploady/src/chunkEventListenerHooks.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import { generateUploaderEventHook } from "@rpldy/shared-ui";
3 | import { CHUNK_EVENTS } from "@rpldy/chunked-sender";
4 |
5 | import type { ChunkStartListenerHook, ChunkFinishListenerHook } from "./types";
6 |
7 | const useChunkStartListener: ChunkStartListenerHook = generateUploaderEventHook(CHUNK_EVENTS.CHUNK_START, false);
8 |
9 | const useChunkFinishListener: ChunkFinishListenerHook = generateUploaderEventHook(CHUNK_EVENTS.CHUNK_FINISH, false);
10 |
11 | export {
12 | useChunkStartListener,
13 | useChunkFinishListener,
14 | };
15 |
--------------------------------------------------------------------------------
/packages/ui/chunked-uploady/src/index.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import ChunkedUploady from "./ChunkedUploady";
3 |
4 | export default ChunkedUploady;
5 |
6 | export {
7 | ChunkedUploady
8 | };
9 |
10 | export { CHUNK_EVENTS } from "@rpldy/chunked-sender";
11 |
12 | export * from "@rpldy/uploady";
13 |
14 | export * from "./chunkEventListenerHooks";
15 |
16 | export type {
17 | ChunkedUploadyProps,
18 | } from "./types";
19 |
--------------------------------------------------------------------------------
/packages/ui/chunked-uploady/src/tests/ChunkedUploady-no-hunking.test.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { logWarning } from "@rpldy/shared-ui/src/tests/mocks/rpldy-ui-shared.mock";
3 | import "@rpldy/uploady/src/tests/mocks/rpldy-uploady.mock";
4 | import getChunkedEnhancer from "@rpldy/chunked-sender";
5 | import ChunkedUploady from "../ChunkedUploady";
6 |
7 | vi.mock("@rpldy/chunked-sender", () => ({
8 | default: vi.fn(),
9 | CHUNKING_SUPPORT: false
10 | }));
11 |
12 |
13 | describe("ChunkedUploady tests without chunking support", () => {
14 | const chunkedEnhancer = (uploader) => uploader;
15 |
16 | beforeAll(() => {
17 | getChunkedEnhancer.mockImplementation(() => chunkedEnhancer);
18 | });
19 |
20 | it("should render Uploady when no chunk support", () => {
21 | render();
22 |
23 | expect(logWarning).toHaveBeenCalledWith(false, expect.any(String));
24 | expect(getChunkedEnhancer).not.toHaveBeenCalled();
25 | });
26 | });
27 |
--------------------------------------------------------------------------------
/packages/ui/chunked-uploady/src/tests/chunkEventListenerHooks.test.js:
--------------------------------------------------------------------------------
1 | import { CHUNK_EVENTS } from "@rpldy/chunked-sender";
2 | import { generateUploaderEventHook } from "@rpldy/shared-ui";
3 | import "../chunkEventListenerHooks";
4 |
5 | vi.mock("@rpldy/shared-ui");
6 |
7 | describe("eventListenerHooks tests", () => {
8 | describe("generateUploaderEventHook tests", () => {
9 | it.each([
10 | CHUNK_EVENTS.CHUNK_START,
11 | CHUNK_EVENTS.CHUNK_FINISH,
12 | ])("should generate chunk event hooks for: %s", (event) => {
13 | expect(generateUploaderEventHook).toHaveBeenCalledWith(event, false);
14 | });
15 | });
16 | });
17 |
--------------------------------------------------------------------------------
/packages/ui/chunked-uploady/src/types.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import type { UploadyProps } from "@rpldy/uploady";
3 | import type { ChunkedOptions, ChunkFinishEventData, ChunkStartEventData } from "@rpldy/chunked-sender";
4 | import type { SendOptions } from "@rpldy/sender";
5 |
6 | export type StartEventResponse = void | boolean | {
7 | url?: string,
8 | sendOptions?: SendOptions
9 | };
10 |
11 | export type ChunkStartListenerHook = (cb: (data: ChunkStartEventData) => StartEventResponse | Promise) => void;
12 |
13 | export type ChunkFinishListenerHook = (cb: (data: ChunkFinishEventData) => void | Promise) => void;
14 |
15 | export type ChunkedUploadyProps = {|
16 | ...UploadyProps,
17 | ...$Exact,
18 | |};
19 |
--------------------------------------------------------------------------------
/packages/ui/chunked-uploady/types/index.d.ts:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 | import { UploadyProps } from "@rpldy/uploady";
3 | import { ChunkedOptions, ChunkFinishEventData, ChunkStartEventData } from "@rpldy/chunked-sender";
4 | import { SendOptions } from "@rpldy/sender";
5 |
6 | export * from "@rpldy/uploady";
7 |
8 | export interface ChunkedUploadyProps extends UploadyProps, ChunkedOptions {}
9 |
10 | export const ChunkedUploady: React.ComponentType;
11 |
12 | export type StartEventResponse = void | boolean | {
13 | url?: string,
14 | sendOptions?: SendOptions
15 | };
16 |
17 | export const useChunkStartListener: (cb: (data: ChunkStartEventData ) => StartEventResponse | Promise) => void;
18 |
19 | export const useChunkFinishListener: (cb: (data: ChunkFinishEventData) => void | Promise) => void;
20 |
21 | export default ChunkedUploady;
22 |
--------------------------------------------------------------------------------
/packages/ui/retry-hooks/.npmignore:
--------------------------------------------------------------------------------
1 | src/
2 | *.story.js
3 | *.stories.js
4 | *.test.js
5 | tests/
6 | mocks/
7 | lerna.json
8 | *.log
9 | jest*
10 | .flowconfig
11 | .editorconfig
12 | .eslintignore
13 | .eslintrc.js
14 | .circleci/
15 | .sb-static/
16 | .env
17 | .jest-cache/
18 | .jest-coverage/
19 | coverage/
20 | .github/
21 |
22 |
--------------------------------------------------------------------------------
/packages/ui/retry-hooks/RetryHooks.storydoc.mdx:
--------------------------------------------------------------------------------
1 | import { Markdown } from "@storybook/blocks";
2 | import readme from "./README.md?raw";
3 |
4 | {readme}
5 |
--------------------------------------------------------------------------------
/packages/ui/retry-hooks/src/consts.js:
--------------------------------------------------------------------------------
1 | // @flow
2 |
3 | export const NO_EXT = "Uploady - retry was not registered. Make sure you used the enhancer";
4 |
--------------------------------------------------------------------------------
/packages/ui/retry-hooks/src/index.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import retryEnhancer, { RETRY_EVENT } from "@rpldy/retry";
3 | import useRetry from "./useRetry";
4 | import useBatchRetry from "./useBatchRetry";
5 | import useRetryListener from "./useRetryListener";
6 |
7 | export default retryEnhancer;
8 |
9 | export {
10 | RETRY_EVENT,
11 |
12 | retryEnhancer,
13 | useRetry,
14 | useBatchRetry,
15 | useRetryListener,
16 | };
17 |
18 | export type {
19 | RetryEventData,
20 | RetryEventCallback,
21 | RetryListenerHook,
22 | } from "./types";
23 |
--------------------------------------------------------------------------------
/packages/ui/retry-hooks/src/tests/useBatchRetry.test.js:
--------------------------------------------------------------------------------
1 | import { invariant } from "@rpldy/shared/src/tests/mocks/rpldy-shared.mock";
2 | import { UploadyContext } from "@rpldy/shared-ui/src/tests/mocks/rpldy-ui-shared.mock";
3 | import { NO_EXT } from "../consts";
4 | import useBatchRetry from "../useBatchRetry";
5 |
6 | describe("useBatchRetry tests", () => {
7 | beforeEach(() => {
8 | invariant.mockReset();
9 | });
10 |
11 | it("should throw if ext not registered", async() => {
12 | invariant.mockImplementation(() => {
13 | throw new Error("bah");
14 | });
15 |
16 | expect(() => {
17 | renderHookWithError(useBatchRetry);
18 | }).toThrow("bah");
19 |
20 | expect(invariant).toHaveBeenCalledWith(undefined, NO_EXT);
21 | });
22 |
23 | it("should return batchRetry from context", () => {
24 | UploadyContext.getExtension.mockReturnValueOnce({
25 | retryBatch: "test"
26 | });
27 |
28 | const { result } = renderHook(useBatchRetry);
29 |
30 | expect(result.current).toBe("test");
31 | });
32 | });
33 |
--------------------------------------------------------------------------------
/packages/ui/retry-hooks/src/tests/useRetry.test.js:
--------------------------------------------------------------------------------
1 | import { invariant } from "@rpldy/shared/src/tests/mocks/rpldy-shared.mock";
2 | import { UploadyContext } from "@rpldy/shared-ui/src/tests/mocks/rpldy-ui-shared.mock";
3 | import { NO_EXT } from "../consts";
4 | import useRetry from "../useRetry";
5 |
6 | describe("useRetry tests", () => {
7 | beforeEach(() => {
8 | invariant.mockReset();
9 | });
10 |
11 | it("should throw if ext not registered", () => {
12 | invariant.mockImplementation(() => {
13 | throw new Error("bah");
14 | });
15 |
16 | expect(() => {
17 | renderHookWithError(useRetry);
18 | }).toThrow("bah");
19 |
20 | expect(invariant).toHaveBeenCalledWith(undefined, NO_EXT);
21 | });
22 |
23 | it("should return retry from context", () => {
24 | UploadyContext.getExtension.mockReturnValueOnce({
25 | retry: "test"
26 | });
27 |
28 | const { result } = renderHook(useRetry);
29 |
30 | expect(result.current).toBe("test");
31 | });
32 | });
33 |
--------------------------------------------------------------------------------
/packages/ui/retry-hooks/src/tests/useRetryListener.test.js:
--------------------------------------------------------------------------------
1 | import { generateUploaderEventHook } from "@rpldy/shared-ui";
2 | import { RETRY_EVENT } from "@rpldy/retry";
3 | import "../useRetryListener";
4 |
5 | vi.mock("@rpldy/shared-ui", () => ({
6 | generateUploaderEventHook: vi.fn(),
7 | }));
8 |
9 | describe("useRetryListener hook test", () => {
10 | it("should generate retry even listener hook", () => {
11 | expect(generateUploaderEventHook).toHaveBeenCalledWith(RETRY_EVENT, false);
12 | });
13 | });
14 |
--------------------------------------------------------------------------------
/packages/ui/retry-hooks/src/types.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import type { BatchItem, UploadOptions } from "@rpldy/shared";
3 |
4 | export type RetryEventData = {
5 | items: BatchItem[];
6 | options: UploadOptions | void;
7 | };
8 |
9 | export type RetryEventCallback = (data: RetryEventData) => void;
10 |
11 | export type RetryListenerHook = (cb: RetryEventCallback) => void;
12 |
--------------------------------------------------------------------------------
/packages/ui/retry-hooks/src/useBatchRetry.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import { invariant } from "@rpldy/shared";
3 | import { useUploadyContext } from "@rpldy/shared-ui";
4 | import { RETRY_EXT, type BatchRetryMethod } from "@rpldy/retry";
5 | import { NO_EXT } from "./consts";
6 |
7 | const useBatchRetry = (): BatchRetryMethod => {
8 | const context = useUploadyContext();
9 | const ext = context.getExtension(RETRY_EXT);
10 |
11 | invariant(ext, NO_EXT);
12 |
13 | return ext.retryBatch;
14 | };
15 |
16 | export default useBatchRetry;
17 |
--------------------------------------------------------------------------------
/packages/ui/retry-hooks/src/useRetry.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import { invariant } from "@rpldy/shared";
3 | import { useUploadyContext } from "@rpldy/shared-ui";
4 | import { RETRY_EXT, type RetryMethod } from "@rpldy/retry";
5 | import { NO_EXT } from "./consts";
6 |
7 | const useRetry = (): RetryMethod => {
8 | const context = useUploadyContext();
9 | const ext = context.getExtension(RETRY_EXT);
10 |
11 | invariant(ext, NO_EXT);
12 |
13 | return ext.retry;
14 | };
15 |
16 | export default useRetry;
17 |
--------------------------------------------------------------------------------
/packages/ui/retry-hooks/src/useRetryListener.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import { generateUploaderEventHook } from "@rpldy/shared-ui";
3 | import { RETRY_EVENT } from "@rpldy/retry";
4 |
5 | import type { RetryListenerHook } from "./types";
6 |
7 | const useRetryListener = (generateUploaderEventHook(RETRY_EVENT, false): RetryListenerHook);
8 |
9 | export default useRetryListener;
10 |
--------------------------------------------------------------------------------
/packages/ui/retry-hooks/types/index.d.ts:
--------------------------------------------------------------------------------
1 | import { BatchItem, UploadOptions } from "@rpldy/shared";
2 | import retryEnhancer, {
3 | RETRY_EVENT,
4 | RetryMethod,
5 | RetryBatchMethod,
6 | } from "@rpldy/retry";
7 |
8 | export default retryEnhancer;
9 |
10 | export {
11 | RETRY_EVENT,
12 | RetryMethod,
13 | RetryBatchMethod,
14 | retryEnhancer,
15 | };
16 |
17 | export const useRetry: () => RetryMethod;
18 |
19 | export const useBatchRetry: () => RetryBatchMethod;
20 |
21 | export type RetryEventData = {
22 | items: BatchItem[];
23 | options: UploadOptions | void;
24 | };
25 |
26 | export type RetryEventCallback = (data: RetryEventData) => void;
27 |
28 | export const useRetryListener: (cb: RetryEventCallback) => void;
29 |
--------------------------------------------------------------------------------
/packages/ui/retry-hooks/types/index.test-d.tsx:
--------------------------------------------------------------------------------
1 | import { BatchItem } from "@rpldy/shared";
2 | import * as React from "react";
3 | import {
4 | useRetry,
5 | useBatchRetry,
6 | useRetryListener,
7 | RetryBatchMethod,
8 | RetryMethod
9 | } from "./index";
10 |
11 | const TestRetryHooks: React.FC = () => {
12 | const retry: RetryMethod = useRetry();
13 | const retryBatch: RetryBatchMethod = useBatchRetry();
14 |
15 | useRetryListener(({ items, options }) => {
16 | console.log(`retrying ${items.length} items`);
17 | items.forEach((item: BatchItem) => console.log("retrying item: " + item.id));
18 | console.log(options);
19 | });
20 |
21 | retry("item-1");
22 | retryBatch("b1", { destination: { url: "another.url" } });
23 |
24 | return ;
25 | };
26 |
27 | const testRetryHooks = (): JSX.Element => {
28 | return ;
29 | };
30 |
31 | export {
32 | testRetryHooks,
33 | };
34 |
--------------------------------------------------------------------------------
/packages/ui/shared/.npmignore:
--------------------------------------------------------------------------------
1 | src/
2 | *.story.js
3 | *.stories.js
4 | *.test.js
5 | tests/
6 | mocks/
7 | lerna.json
8 | *.log
9 | jest*
10 | .flowconfig
11 | .editorconfig
12 | .eslintignore
13 | .eslintrc.js
14 | .circleci/
15 | .sb-static/
16 | .env
17 | .jest-cache/
18 | .jest-coverage/
19 | coverage/
20 | .github/
21 |
22 |
--------------------------------------------------------------------------------
/packages/ui/shared/src/NoDomUploady.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import React, { useMemo } from "react";
3 | import { logger } from "@rpldy/shared";
4 | import UploadyContext, { createContextApi } from "./UploadyContext";
5 | import useUploader from "./hooks/useUploader";
6 |
7 | import type { Node } from "react";
8 | import type { NoDomUploadyProps } from "./types";
9 |
10 | const NoDomUploady = (props: NoDomUploadyProps): Node => {
11 | const {
12 | listeners,
13 | debug,
14 | children,
15 | inputRef,
16 | ...uploadOptions
17 | } = props;
18 |
19 | logger.setDebug(!!debug);
20 | logger.debugLog("@@@@@@ Uploady Rendering @@@@@@", props);
21 |
22 | const uploader = useUploader(uploadOptions, listeners);
23 |
24 | const api = useMemo(() =>
25 | createContextApi(uploader, inputRef),
26 | [uploader, inputRef]
27 | );
28 |
29 | return
30 | {children}
31 | ;
32 | };
33 |
34 | export default NoDomUploady;
35 |
--------------------------------------------------------------------------------
/packages/ui/shared/src/assertContext.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import { invariant } from "@rpldy/shared";
3 | import { getIsVersionRegisteredAndDifferent, getRegisteredVersion } from "./uploadyVersion";
4 |
5 | import type { UploadyContextType } from "./types";
6 |
7 | export const ERROR_MSG = "Uploady - Valid UploadyContext not found. Make sure you render inside ";
8 | export const DIFFERENT_VERSION_ERROR_MSG = `Uploady - Valid UploadyContext not found.
9 | You may be using packages of different Uploady versions. and all other packages using the context provider must be of the same version: %s`;
10 |
11 | const assertContext = (context: ?UploadyContextType): UploadyContextType => {
12 | invariant(
13 | !getIsVersionRegisteredAndDifferent(),
14 | DIFFERENT_VERSION_ERROR_MSG,
15 | getRegisteredVersion(),
16 | );
17 |
18 | invariant(
19 | context && context.hasUploader(),
20 | ERROR_MSG
21 | );
22 |
23 | return context;
24 | };
25 |
26 | export default assertContext;
27 |
--------------------------------------------------------------------------------
/packages/ui/shared/src/consts.js:
--------------------------------------------------------------------------------
1 | export const UPLOAD_OPTIONS_COMP = Symbol.for("rpldy_component");
2 |
--------------------------------------------------------------------------------
/packages/ui/shared/src/hocs/withBatchStartUpdate.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import { UPLOADER_EVENTS } from "@rpldy/uploader";
3 | import { createRequestUpdateHoc } from "./createRequestUpdateHoc";
4 |
5 | import type { BatchItem, Batch } from "@rpldy/shared";
6 | import type { UploaderCreateOptions } from "@rpldy/uploader";
7 | import type { RequestUpdateHoc } from "./createRequestUpdateHoc";
8 |
9 | type BatchStartRequestData = { batch: Batch, items: BatchItem[], options: UploaderCreateOptions };
10 |
11 | const withBatchStartUpdate: RequestUpdateHoc = createRequestUpdateHoc({
12 | eventType: UPLOADER_EVENTS.BATCH_START,
13 | getIsValidEventData: (id, batch: Batch) => batch.id === id,
14 | getRequestData: (batch, batchOptions) =>
15 | ({ batch, items: batch.items, options: batchOptions }),
16 | });
17 |
18 | export default withBatchStartUpdate;
19 |
--------------------------------------------------------------------------------
/packages/ui/shared/src/hocs/withRequestPreSendUpdate.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import { UPLOADER_EVENTS } from "@rpldy/uploader";
3 | import { createRequestUpdateHoc } from "./createRequestUpdateHoc";
4 |
5 | import type { BatchItem } from "@rpldy/shared";
6 | import type { UploaderCreateOptions } from "@rpldy/uploader";
7 | import type { RequestUpdateHoc } from "./createRequestUpdateHoc";
8 |
9 | type PreSendRequestData = { items: BatchItem[], options: UploaderCreateOptions };
10 |
11 | const withRequestPreSendUpdate: RequestUpdateHoc = createRequestUpdateHoc({
12 | eventType: UPLOADER_EVENTS.REQUEST_PRE_SEND,
13 | getIsValidEventData: (id, { items }) => !!items.find((item) => item.id === id),
14 | getRequestData: ({ items, options }) => ({ items, options }),
15 | });
16 |
17 | export default withRequestPreSendUpdate;
18 |
--------------------------------------------------------------------------------
/packages/ui/shared/src/hooks/tests/useAbortAll.test.js:
--------------------------------------------------------------------------------
1 | import useUploadyContext from "../useUploadyContext";
2 | import useAbortAll from "../useAbortAll";
3 |
4 | vi.mock("../useUploadyContext");
5 |
6 | describe("useAbortItem tests", () => {
7 | const context = {
8 | abort: vi.fn()
9 | };
10 |
11 | beforeAll(() => {
12 | useUploadyContext.mockReturnValue(context);
13 | });
14 |
15 | beforeEach(() => {
16 | clearViMocks(context);
17 | });
18 |
19 | it("should return abort item", () => {
20 | const { result } = renderHook(useAbortAll);
21 |
22 | result.current();
23 |
24 | expect(context.abort).toHaveBeenCalled();
25 | });
26 | });
27 |
--------------------------------------------------------------------------------
/packages/ui/shared/src/hooks/tests/useAbortBatch.test.js:
--------------------------------------------------------------------------------
1 | import useUploadyContext from "../useUploadyContext";
2 | import useAbortBatch from "../useAbortBatch";
3 |
4 | vi.mock("../useUploadyContext");
5 |
6 | describe("useAbortItem tests", () => {
7 | const context = {
8 | abortBatch: vi.fn()
9 | };
10 |
11 | beforeAll(() => {
12 | useUploadyContext.mockReturnValue(context);
13 | });
14 |
15 | beforeEach(() => {
16 | clearViMocks(context);
17 | });
18 |
19 | it("should return abort item", () => {
20 | const { result } = renderHook(useAbortBatch);
21 |
22 | result.current("123");
23 |
24 | expect(context.abortBatch).toHaveBeenCalledWith("123");
25 | });
26 | });
27 |
--------------------------------------------------------------------------------
/packages/ui/shared/src/hooks/tests/useAbortItem.test.js:
--------------------------------------------------------------------------------
1 | import useUploadyContext from "../useUploadyContext";
2 | import useAbortItem from "../useAbortItem";
3 |
4 | vi.mock("../useUploadyContext");
5 |
6 | describe("useAbortItem tests", () => {
7 | const context = {
8 | abort: vi.fn()
9 | };
10 |
11 | beforeAll(() => {
12 | useUploadyContext.mockReturnValue(context);
13 | });
14 |
15 | beforeEach(() => {
16 | clearViMocks(context);
17 | });
18 |
19 | it("should return abort item", () => {
20 | const { result } = renderHook(useAbortItem);
21 |
22 | result.current("123");
23 |
24 | expect(context.abort).toHaveBeenCalledWith("123");
25 | });
26 | });
27 |
--------------------------------------------------------------------------------
/packages/ui/shared/src/hooks/tests/useUploadyContext.test.js:
--------------------------------------------------------------------------------
1 | import assertContext from "../../assertContext";
2 | import useUploadyContext from "../useUploadyContext";
3 |
4 | vi.mock("../../assertContext");
5 |
6 | describe("useUploadyContext tests", () => {
7 | beforeAll(()=>{
8 | assertContext.mockReturnValue("context");
9 | });
10 |
11 | it("should set options on context", () => {
12 | const { result } = renderHook(useUploadyContext);
13 |
14 | expect(result.current).toBe("context");
15 | });
16 | });
17 |
18 |
19 |
--------------------------------------------------------------------------------
/packages/ui/shared/src/hooks/useAbortAll.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import { useCallback } from "react";
3 | import useUploadyContext from "./useUploadyContext";
4 |
5 | const useAbortAll: () => () => void = () => {
6 | const context = useUploadyContext();
7 |
8 | return useCallback(
9 | () => context.abort(),
10 | [context]
11 | );
12 | };
13 |
14 | export default useAbortAll;
15 |
--------------------------------------------------------------------------------
/packages/ui/shared/src/hooks/useAbortBatch.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import { useCallback } from "react";
3 | import useUploadyContext from "./useUploadyContext";
4 |
5 | const useAbortBatch: () => (id: string) => void = () => {
6 | const context = useUploadyContext();
7 |
8 | return useCallback(
9 | (id: string) => context.abortBatch(id),
10 | [context]
11 | );
12 | };
13 |
14 | export default useAbortBatch;
15 |
--------------------------------------------------------------------------------
/packages/ui/shared/src/hooks/useAbortItem.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import { useCallback } from "react";
3 | import useUploadyContext from "./useUploadyContext";
4 |
5 | const useAbortItem = (): ((id: string) => any) => {
6 | const context = useUploadyContext();
7 |
8 | return useCallback(
9 | (id: string) => context.abort(id),
10 | [context]
11 | );
12 | };
13 |
14 | export default useAbortItem;
15 |
--------------------------------------------------------------------------------
/packages/ui/shared/src/hooks/useUploadOptions.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import useUploadyContext from "./useUploadyContext";
3 |
4 | import type { UploaderCreateOptions } from "@rpldy/uploader";
5 |
6 | const useUploadOptions = (options?: UploaderCreateOptions): UploaderCreateOptions => {
7 | const context = useUploadyContext();
8 |
9 | if (options) {
10 | context.setOptions(options);
11 | }
12 |
13 | return context.getOptions();
14 | };
15 |
16 | export default useUploadOptions;
17 |
--------------------------------------------------------------------------------
/packages/ui/shared/src/hooks/useUploadyContext.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import { useContext } from "react";
3 | import UploadyContext from "../UploadyContext";
4 | import assertContext from "../assertContext";
5 |
6 | import type { UploadyContextType } from "../types.js";
7 |
8 | const useUploadyContext = (): UploadyContextType =>
9 | assertContext(useContext(UploadyContext));
10 |
11 | export default useUploadyContext;
12 |
--------------------------------------------------------------------------------
/packages/ui/shared/src/tests/mocks/UploadyContext.mock.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | const UploadyContextMock = {
4 | showFileUpload: vi.fn(),
5 | getOptions: vi.fn(),
6 | setOptions: vi.fn(),
7 | setExternalFileInput: vi.fn(),
8 | getInternalFileInput: vi.fn(),
9 | upload: vi.fn(),
10 | getExtension: vi.fn(),
11 | on: vi.fn(),
12 | off: vi.fn(),
13 | Provider: vi.fn(({ children }) => {
14 | return {children}
;
15 | }),
16 | createContextApi: vi.fn(),
17 | };
18 |
19 | export {
20 | UploadyContextMock
21 | };
22 |
--------------------------------------------------------------------------------
/packages/ui/shared/src/uploadyVersion.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import { hasWindow } from "@rpldy/shared";
3 |
4 | export const GLOBAL_VERSION_SYM: symbol = Symbol.for("_rpldy-version_");
5 |
6 | const getVersion = (): string => process.env.BUILD_TIME_VERSION || "";
7 |
8 | const getGlobal = () =>
9 | /* istanbul ignore next */
10 | hasWindow() ? window : (globalThis || process);
11 |
12 | const getRegisteredVersion = (): string => {
13 | const global = getGlobal();
14 | return (global: Object)[GLOBAL_VERSION_SYM];
15 | };
16 |
17 | const registerUploadyContextVersion = (): void => {
18 | const global =getGlobal();
19 | (global: Object)[GLOBAL_VERSION_SYM] = getVersion();
20 | };
21 |
22 | const getIsVersionRegisteredAndDifferent = (): boolean => {
23 | const registeredVersion = getRegisteredVersion();
24 | return !!registeredVersion && registeredVersion !== getVersion();
25 | };
26 |
27 | export {
28 | getVersion,
29 | getRegisteredVersion,
30 | registerUploadyContextVersion,
31 | getIsVersionRegisteredAndDifferent,
32 | };
33 |
--------------------------------------------------------------------------------
/packages/ui/shared/src/utils.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import { isProduction } from "@rpldy/shared";
3 | import { UPLOAD_OPTIONS_COMP } from "./consts";
4 |
5 | const logWarning = (condition: ?any, msg: string) => {
6 |
7 | if (!isProduction() && !condition) {
8 | // eslint-disable-next-line no-console
9 | console.warn(msg);
10 | }
11 | };
12 |
13 | const markAsUploadOptionsComponent = (Component: React.ComponentType): void => {
14 | Component[UPLOAD_OPTIONS_COMP] = true;
15 | };
16 |
17 | const getIsUploadOptionsComponent = (Component: any): boolean =>
18 | Component[UPLOAD_OPTIONS_COMP] === true ||
19 | Component.target?.[UPLOAD_OPTIONS_COMP] === true ||
20 | Component.render?.[UPLOAD_OPTIONS_COMP] === true;
21 |
22 | export {
23 | logWarning,
24 | markAsUploadOptionsComponent,
25 | getIsUploadOptionsComponent,
26 | };
27 |
--------------------------------------------------------------------------------
/packages/ui/tus-uploady/.npmignore:
--------------------------------------------------------------------------------
1 | src/
2 | *.story.js
3 | *.stories.js
4 | *.test.js
5 | tests/
6 | mocks/
7 | lerna.json
8 | *.log
9 | jest*
10 | .flowconfig
11 | .editorconfig
12 | .eslintignore
13 | .eslintrc.js
14 | .circleci/
15 | .sb-static/
16 | .env
17 | .jest-cache/
18 | .jest-coverage/
19 | coverage/
20 | .github/
21 |
22 |
--------------------------------------------------------------------------------
/packages/ui/tus-uploady/TusUploady.storydoc.mdx:
--------------------------------------------------------------------------------
1 | import { Markdown } from "@storybook/blocks";
2 | import readme from "./README.md?raw";
3 |
4 | {readme}
5 |
--------------------------------------------------------------------------------
/packages/ui/tus-uploady/src/consts.js:
--------------------------------------------------------------------------------
1 | // @flow
2 |
3 | export const NO_EXT = "Uploady - tus was not registered. Make sure you used the enhancer";
4 |
--------------------------------------------------------------------------------
/packages/ui/tus-uploady/src/index.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import TusUploady from "./TusUploady";
3 |
4 | export default TusUploady;
5 |
6 | export {
7 | TusUploady,
8 | };
9 |
10 | export { default as useClearResumableStore } from "./useClearResumableStore";
11 | export { default as useTusResumeStartListener } from "./useTusResumeStartListener";
12 |
13 | export * from "@rpldy/uploady";
14 |
15 | export type {
16 | TusUploadyProps,
17 | } from "./types";
18 |
--------------------------------------------------------------------------------
/packages/ui/tus-uploady/src/tests/TusUploady.withoutChunkSupport.test.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { logWarning } from "@rpldy/shared-ui/src/tests/mocks/rpldy-ui-shared.mock";
3 | import "@rpldy/uploady/src/tests/mocks/rpldy-uploady.mock";
4 | import TusUploady from "../TusUploady";
5 | import { getTusEnhancer } from "@rpldy/tus-sender";
6 |
7 | vi.mock("@rpldy/tus-sender", () => ({
8 | getTusEnhancer: vi.fn(),
9 | CHUNKING_SUPPORT: false
10 | }));
11 |
12 | describe("test TusUploady without chucking support", () => {
13 | it("should render Uploady when no chunk support", () => {
14 | render();
15 |
16 | expect(logWarning).toHaveBeenCalledWith(false, expect.any(String));
17 | expect(getTusEnhancer).not.toHaveBeenCalled();
18 | });
19 | });
20 |
--------------------------------------------------------------------------------
/packages/ui/tus-uploady/src/tests/useTusResumeStartListener.test.js:
--------------------------------------------------------------------------------
1 | import { generateUploaderEventHook } from "@rpldy/shared-ui";
2 | import { TUS_EVENTS } from "@rpldy/tus-sender";
3 | import "../useTusResumeStartListener";
4 |
5 | vi.mock("@rpldy/shared-ui");
6 |
7 | describe("TUS eventListenerHooks tests", () => {
8 | it.each([
9 | TUS_EVENTS.RESUME_START
10 | ])("should generate TUS hooks for: %s", (event) => {
11 | expect(generateUploaderEventHook).toHaveBeenCalledWith(event);
12 | });
13 | });
14 |
--------------------------------------------------------------------------------
/packages/ui/tus-uploady/src/types.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import type { UploadyProps } from "@rpldy/shared-ui";
3 | import type { TusOptions, ResumeStartEventData } from "@rpldy/tus-sender";
4 |
5 | export type TusUploadyProps = {|
6 | ...UploadyProps,
7 | ...$Exact,
8 | |};
9 |
10 | export type ResumeStartEventResponse = void | boolean | {
11 | url?: string,
12 | resumeHeaders?: Object,
13 | };
14 |
15 | export type TusResumeStartListenerHook = (cb: (data: ResumeStartEventData ) => ResumeStartEventResponse) => void;
16 |
--------------------------------------------------------------------------------
/packages/ui/tus-uploady/src/useClearResumableStore.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import { invariant } from "@rpldy/shared";
3 | import { useUploadyContext } from "@rpldy/shared-ui";
4 | import { clearResumables, TUS_EXT } from "@rpldy/tus-sender";
5 | import { NO_EXT } from "./consts";
6 |
7 | const useClearResumableStore = (): (() => void) => {
8 | const context = useUploadyContext();
9 | const ext = context.getExtension(TUS_EXT);
10 | invariant(ext, NO_EXT);
11 |
12 | return () => clearResumables(ext.getOptions());
13 | };
14 |
15 | export default useClearResumableStore;
16 |
--------------------------------------------------------------------------------
/packages/ui/tus-uploady/src/useTusResumeStartListener.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import { generateUploaderEventHook } from "@rpldy/shared-ui";
3 | import { TUS_EVENTS } from "@rpldy/tus-sender";
4 |
5 | import type { TusResumeStartListenerHook } from "./types";
6 |
7 | const useTusResumeStartListener: TusResumeStartListenerHook =
8 | generateUploaderEventHook(TUS_EVENTS.RESUME_START);
9 |
10 | export default useTusResumeStartListener;
11 |
--------------------------------------------------------------------------------
/packages/ui/tus-uploady/types/index.d.ts:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 | import {
3 | TusOptions,
4 | TusResumeStartEventData,
5 | TusResumeStartEventResponse
6 | } from "@rpldy/tus-sender";
7 | import { UploadyProps } from "@rpldy/shared-ui";
8 |
9 | export * from "@rpldy/uploady";
10 |
11 | export interface TusUploadyProps extends UploadyProps, TusOptions {}
12 |
13 | export const TusUploady: React.ComponentType;
14 |
15 | export default TusUploady;
16 |
17 | export type ClearResumableStore = () => void;
18 |
19 | export const useClearResumableStore: () => ClearResumableStore;
20 |
21 | export const useTusResumeStartListener: (cb: (data: TusResumeStartEventData) => TusResumeStartEventResponse) => void;
22 |
23 | export {
24 | TusOptions,
25 | TusResumeStartEventData,
26 | TusResumeStartEventResponse,
27 | };
28 |
--------------------------------------------------------------------------------
/packages/ui/upload-button/.npmignore:
--------------------------------------------------------------------------------
1 | src/
2 | *.story.js
3 | *.stories.js
4 | *.test.js
5 | tests/
6 | mocks/
7 | lerna.json
8 | *.log
9 | jest*
10 | .flowconfig
11 | .editorconfig
12 | .eslintignore
13 | .eslintrc.js
14 | .circleci/
15 | .sb-static/
16 | .env
17 | .jest-cache/
18 | .jest-coverage/
19 | coverage/
20 | .github/
21 |
22 |
--------------------------------------------------------------------------------
/packages/ui/upload-button/UploadButton.storydoc.mdx:
--------------------------------------------------------------------------------
1 | import { Markdown } from "@storybook/blocks";
2 | import readme from "./README.md?raw";
3 |
4 | {readme}
5 |
--------------------------------------------------------------------------------
/packages/ui/upload-button/src/UploadButton.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import React, { forwardRef } from "react";
3 | import asUploadButton from "./asUploadButton";
4 | import type { ComponentType } from "react";
5 |
6 | const UploadButton = (asUploadButton(forwardRef(
7 | (props: any, ref: React.RefSetter | ((null | HTMLButtonElement) => mixed)) =>
8 | )): ComponentType);
9 |
10 | export default UploadButton;
11 |
--------------------------------------------------------------------------------
/packages/ui/upload-button/src/index.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import UploadButton from "./UploadButton";
3 | import asUploadButton from "./asUploadButton";
4 |
5 | export default UploadButton;
6 |
7 | export {
8 | UploadButton,
9 | asUploadButton,
10 | };
11 |
12 | export type {
13 | UploadButtonProps,
14 | } from "./types";
15 |
--------------------------------------------------------------------------------
/packages/ui/upload-button/src/types.js:
--------------------------------------------------------------------------------
1 | // @flow
2 |
3 | import type { Node } from "react";
4 | import type { UploadOptions } from "@rpldy/shared";
5 |
6 | export type ButtonProps = {|
7 | className?: string,
8 | id?: string,
9 | children?: Node,
10 | text?: string,
11 | extraProps?: Object,
12 | onClick?: (SyntheticMouseEvent) => void,
13 | |};
14 |
15 | export type UploadButtonProps = {|
16 | ...UploadOptions,
17 | ...ButtonProps,
18 | |};
19 |
--------------------------------------------------------------------------------
/packages/ui/upload-button/types/index.d.ts:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 | import { UploadOptions } from "@rpldy/shared";
3 |
4 | export interface ButtonProps {
5 | className?: string;
6 | id?: string;
7 | children?: JSX.Element[] | JSX.Element | string;
8 | text?: string;
9 | extraProps?: Record;
10 | ref?: React.RefObject;
11 | onClick?: React.MouseEventHandler
12 | }
13 |
14 | export interface UploadButtonProps extends ButtonProps, UploadOptions {}
15 |
16 | export const UploadButton: React.ComponentType>>;
17 |
18 | export const asUploadButton: (component: React.ForwardRefExoticComponent | React.ComponentType) => React.FC>;
19 |
20 | export default UploadButton;
21 |
--------------------------------------------------------------------------------
/packages/ui/upload-drop-zone/.npmignore:
--------------------------------------------------------------------------------
1 | src/
2 | *.story.js
3 | *.stories.js
4 | *.test.js
5 | tests/
6 | mocks/
7 | lerna.json
8 | *.log
9 | jest*
10 | .flowconfig
11 | .editorconfig
12 | .eslintignore
13 | .eslintrc.js
14 | .circleci/
15 | .sb-static/
16 | .env
17 | .jest-cache/
18 | .jest-coverage/
19 | coverage/
20 | .github/
21 |
22 |
--------------------------------------------------------------------------------
/packages/ui/upload-drop-zone/UploadDropZone.storydoc.mdx:
--------------------------------------------------------------------------------
1 | import { Markdown } from "@storybook/blocks";
2 | import readme from "./README.md?raw";
3 |
4 | {readme}
5 |
--------------------------------------------------------------------------------
/packages/ui/upload-drop-zone/src/index.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import UploadDropZone from "./UploadDropZone";
3 |
4 | export default UploadDropZone;
5 |
6 | export {
7 | UploadDropZone
8 | };
9 |
10 | export type {
11 | GetFilesMethod,
12 | UploadDropZoneProps
13 | } from "./types";
14 |
15 |
--------------------------------------------------------------------------------
/packages/ui/upload-drop-zone/src/types.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import type { UploadOptions } from "@rpldy/shared";
3 |
4 | export type GetFilesMethod = () => Promise;
5 |
6 | export type DropHandlerMethod = (e: SyntheticDragEvent, getFiles: GetFilesMethod) => FileList | mixed[]
7 |
8 | export type ShouldHandleDragMethod = (e: SyntheticDragEvent) => boolean;
9 |
10 | export type ShouldHandleDrag = boolean | ShouldHandleDragMethod;
11 |
12 | export type ShouldRemoveDragOverMethod = (e: SyntheticDragEvent) => boolean;
13 |
14 | export type UploadDropZoneProps = {|
15 | ...UploadOptions,
16 | className?: string,
17 | id?: string,
18 | onDragOverClassName?: string,
19 | dropHandler?: DropHandlerMethod,
20 | htmlDirContentParams?: Object,
21 | shouldRemoveDragOver?: ShouldRemoveDragOverMethod,
22 | shouldHandleDrag?: ShouldHandleDrag,
23 | enableOnContains?: boolean,
24 | noContainCheckForDrag?: boolean,
25 | extraProps?: Object,
26 | children?: React.Node,
27 | |};
28 |
--------------------------------------------------------------------------------
/packages/ui/upload-paste/.npmignore:
--------------------------------------------------------------------------------
1 | src/
2 | *.story.js
3 | *.stories.js
4 | *.test.js
5 | tests/
6 | mocks/
7 | lerna.json
8 | *.log
9 | jest*
10 | .flowconfig
11 | .editorconfig
12 | .eslintignore
13 | .eslintrc.js
14 | .circleci/
15 | .sb-static/
16 | .env
17 | .jest-cache/
18 | .jest-coverage/
19 | coverage/
20 | .github/
21 |
22 |
--------------------------------------------------------------------------------
/packages/ui/upload-paste/UploadPaste.storydoc.mdx:
--------------------------------------------------------------------------------
1 | import { Markdown } from "@storybook/blocks";
2 | import readme from "./README.md?raw";
3 |
4 | {readme}
5 |
--------------------------------------------------------------------------------
/packages/ui/upload-paste/src/index.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import withPasteUpload from "./withPasteUpload";
3 |
4 | export {
5 | withPasteUpload
6 | };
7 |
8 | export default withPasteUpload;
9 |
10 | export { default as usePasteUpload } from "./usePasteUpload";
11 |
12 | export type * from "./types";
13 |
--------------------------------------------------------------------------------
/packages/ui/upload-paste/src/types.js:
--------------------------------------------------------------------------------
1 | // @flow
2 |
3 | import type { Node } from "react";
4 | import type { UploadOptions } from "@rpldy/shared";
5 |
6 | export type PasteUploadData = { count: number };
7 |
8 | export type PasteUploadHandler = (data: PasteUploadData) => void;
9 |
10 | export type PasteCompProps = {|
11 | className?: string,
12 | id?: string,
13 | children?: Node,
14 | extraProps?: Object,
15 | onPasteUpload?: PasteUploadHandler,
16 | |};
17 |
18 | export type PasteProps = {|
19 | ...UploadOptions,
20 | ...PasteCompProps,
21 | |};
22 |
23 | export type PasteUploadHookResult = { toggle: () => boolean, getIsEnabled: () => boolean};
24 |
25 | export type PasteUploadHook = (UploadOptions, ?HTMLElement, PasteUploadHandler) => PasteUploadHookResult;
26 |
27 | export type PasteElementRef = { current: HTMLElement | null | void };
28 |
--------------------------------------------------------------------------------
/packages/ui/upload-paste/src/usePasteHandler.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import { useCallback, useRef } from "react";
3 | import { useUploady } from "@rpldy/shared-ui";
4 |
5 | import type { UploadOptions } from "@rpldy/shared";
6 | import type { PasteUploadHandler } from "./types";
7 |
8 | const usePasteHandler = (uploadOptions: ?UploadOptions, onPasteUpload: ?PasteUploadHandler): ClipboardEventListener => {
9 | const { upload } = useUploady();
10 |
11 | //using ref so paste callback can stay memoized
12 | const uploadOptionsRef = useRef();
13 | uploadOptionsRef.current = uploadOptions;
14 |
15 | return useCallback((e) => {
16 | const files = e.clipboardData?.files;
17 |
18 | if (files?.length) {
19 | upload(files, uploadOptionsRef.current);
20 | onPasteUpload?.({ count: files.length });
21 | }
22 | }, [upload, uploadOptionsRef, onPasteUpload]);
23 | };
24 |
25 | export default usePasteHandler;
26 |
--------------------------------------------------------------------------------
/packages/ui/upload-paste/types/index.d.ts:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 | import { UploadOptions } from "@rpldy/shared";
3 |
4 | export type PasteUploadData = { count: number };
5 |
6 | export type PasteUploadHandler = (data: PasteUploadData) => void;
7 |
8 | export interface PasteCompProps {
9 | className?: string;
10 | id?: string;
11 | children?: JSX.Element[] | JSX.Element | string;
12 | extraProps?: Record;
13 | onPasteUpload?: PasteUploadHandler,
14 | }
15 |
16 | export interface PasteProps extends UploadOptions, PasteCompProps {}
17 |
18 | export type PasteUploadHookResult = { toggle: () => boolean, getIsEnabled: () => boolean};
19 |
20 | export const usePasteUpload: (
21 | uploadOptions?: UploadOptions | null | undefined,
22 | element?: React.RefObject | null | undefined,
23 | onPasteUpload?: PasteUploadHandler
24 | ) => PasteUploadHookResult;
25 |
26 | export const withPasteUpload: (component: React.ForwardRefExoticComponent | React.ComponentType) => React.FC;
27 |
28 | export default withPasteUpload;
29 |
--------------------------------------------------------------------------------
/packages/ui/upload-preview/.npmignore:
--------------------------------------------------------------------------------
1 | src/
2 | *.story.js
3 | *.stories.js
4 | *.test.js
5 | tests/
6 | mocks/
7 | lerna.json
8 | *.log
9 | jest*
10 | .flowconfig
11 | .editorconfig
12 | .eslintignore
13 | .eslintrc.js
14 | .circleci/
15 | .sb-static/
16 | .env
17 | .jest-cache/
18 | .jest-coverage/
19 | coverage/
20 | .github/
21 |
22 |
--------------------------------------------------------------------------------
/packages/ui/upload-preview/UploadPreview.storydoc.mdx:
--------------------------------------------------------------------------------
1 | import { Markdown } from "@storybook/blocks";
2 | import readme from "./README.md?raw";
3 |
4 | {readme}
5 |
--------------------------------------------------------------------------------
/packages/ui/upload-preview/src/consts.js:
--------------------------------------------------------------------------------
1 | //@flow
2 |
3 | export const PREVIEW_TYPES = {
4 | IMAGE: "image",
5 | VIDEO: "video",
6 | };
7 |
--------------------------------------------------------------------------------
/packages/ui/upload-preview/src/defaults.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import { devFreeze } from "@rpldy/shared";
3 | import type { MandatoryPreviewOptions } from "./types";
4 |
5 | export const PREVIEW_DEFAULTS: MandatoryPreviewOptions = devFreeze({
6 | rememberPreviousBatches: false,
7 | loadFirstOnly: false,
8 | maxPreviewImageSize: 2e+7,
9 | maxPreviewVideoSize: 1e+8,
10 | fallbackUrl: "",
11 | imageMimeTypes: ["image/jpeg", "image/webp", "image/gif", "image/png", "image/apng", "image/bmp", "image/x-icon", "image/svg+xml"],
12 | videoMimeTypes: ["video/mp4", "video/webm", "video/ogg"],
13 | previewComponentProps: undefined,
14 | });
15 |
--------------------------------------------------------------------------------
/packages/ui/upload-preview/src/index.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import UploadPreview, { getUploadPreviewForBatchItemsMethod } from "./UploadPreview";
3 | import { getPreviewsLoaderHook } from "./usePreviewsLoader";
4 |
5 | export {
6 | PREVIEW_TYPES,
7 | } from "./consts";
8 |
9 | export type {
10 | PreviewProps,
11 | PreviewItem,
12 | PreviewMethods,
13 | RemovePreviewMethod,
14 | ClearPreviewsMethod,
15 | } from "./types";
16 |
17 | export default UploadPreview;
18 |
19 | export {
20 | getUploadPreviewForBatchItemsMethod,
21 | getPreviewsLoaderHook,
22 | UploadPreview,
23 | };
24 |
25 |
--------------------------------------------------------------------------------
/packages/ui/upload-url-input/.npmignore:
--------------------------------------------------------------------------------
1 | src/
2 | *.story.js
3 | *.stories.js
4 | *.test.js
5 | tests/
6 | mocks/
7 | lerna.json
8 | *.log
9 | jest*
10 | .flowconfig
11 | .editorconfig
12 | .eslintignore
13 | .eslintrc.js
14 | .circleci/
15 | .sb-static/
16 | .env
17 | .jest-cache/
18 | .jest-coverage/
19 | coverage/
20 | .github/
21 |
22 |
--------------------------------------------------------------------------------
/packages/ui/upload-url-input/UploadUrlInput.storydoc.mdx:
--------------------------------------------------------------------------------
1 | import { Markdown } from "@storybook/blocks";
2 | import readme from "./README.md?raw";
3 |
4 | {readme}
5 |
--------------------------------------------------------------------------------
/packages/ui/upload-url-input/src/index.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import UploadUrlInput from "./UploadUrlInput";
3 |
4 | export default UploadUrlInput;
5 |
6 | export {
7 | UploadUrlInput,
8 | };
9 |
10 | export type {
11 | UploadUrlInputProps,
12 | } from "./types";
13 |
--------------------------------------------------------------------------------
/packages/ui/upload-url-input/src/types.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import type { UploadOptions } from "@rpldy/shared";
3 | import type { RefObject } from "@rpldy/shared-ui";
4 |
5 | type ValidateMethod = (value: ?string, input: ?HTMLInputElement) => boolean;
6 |
7 | export type UploadMethod = () => void;
8 |
9 | export type UploadUrlInputProps = {|
10 | ...UploadOptions,
11 | //the class attribute to pass to the button element
12 | className?: string,
13 | //id attribute to pass to the button element
14 | id?: string,
15 | //input's placeholder text
16 | placeholder?: string,
17 | //function to validate input's value before its sent
18 | validate?: ValidateMethod,
19 | //ref will be set to the upload callback so it can be triggered from the outside
20 | uploadRef?: RefObject,
21 | //disable initiating upload by pressing enter (default: false)
22 | ignoreKeyPress?: boolean,
23 | |};
24 |
--------------------------------------------------------------------------------
/packages/ui/upload-url-input/types/index.d.ts:
--------------------------------------------------------------------------------
1 | import { UploadOptions } from "@rpldy/shared";
2 | import * as React from "react";
3 |
4 | export type ValidateMethod = (value: string | undefined, input: HTMLInputElement | undefined) => boolean;
5 |
6 | export type UploadMethod = () => void;
7 |
8 | export interface UploadUrlInputProps extends UploadOptions {
9 | className?: string;
10 | id?: string;
11 | placeholder?: string;
12 | validate?: ValidateMethod;
13 | uploadRef?: React.RefObject;
14 | ignoreKeyPress?: boolean;
15 | ref?: React.RefObject;
16 | }
17 |
18 | export const UploadUrlInput: (props: React.PropsWithRef) => JSX.Element;
19 |
20 | export default UploadUrlInput;
21 |
--------------------------------------------------------------------------------
/packages/ui/upload-url-input/types/index.test-d.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 | import UploadUrlInput from "./index";
3 |
4 | const TestWithRef: React.FC = () => {
5 | const inputRef = React.useRef();
6 |
7 | const onInputChange = React.useCallback(() => {
8 | console.log("INPUT = ", inputRef.current?.value);
9 | }, []);
10 |
11 | React.useEffect(() => {
12 | const input = inputRef.current;
13 | if (input) {
14 | input.addEventListener("input", onInputChange);
15 | }
16 |
17 | return () => {
18 | if (input) {
19 | input.removeEventListener("input", onInputChange);
20 | }
21 | };
22 | });
23 |
24 | return ;
25 | };
26 |
27 | const testUploadUrlInput = (): JSX.Element => {
28 | return ;
29 | };
30 |
31 | export {
32 | testUploadUrlInput,
33 | };
34 |
--------------------------------------------------------------------------------
/packages/ui/uploady/.npmignore:
--------------------------------------------------------------------------------
1 | src/
2 | *.story.js
3 | *.stories.js
4 | *.test.js
5 | tests/
6 | mocks/
7 | lerna.json
8 | *.log
9 | jest*
10 | .flowconfig
11 | .editorconfig
12 | .eslintignore
13 | .eslintrc.js
14 | .circleci/
15 | .sb-static/
16 | .env
17 | .jest-cache/
18 | .jest-coverage/
19 | coverage/
20 | .github/
21 |
22 |
--------------------------------------------------------------------------------
/packages/ui/uploady/Uploady.storydoc.mdx:
--------------------------------------------------------------------------------
1 | import { Markdown } from "@storybook/blocks";
2 | import readme from "./README.md?raw";
3 |
4 | {readme}
5 |
--------------------------------------------------------------------------------
/packages/ui/uploady/all-bundle-entry.js:
--------------------------------------------------------------------------------
1 |
2 | /**
3 | * Special file, needed as entry point for webpack when building the "rpldy-all.umd" bundle
4 | */
5 | export * from "./all.generated.js";
6 |
7 | // export * as lifeEvents from "@rpldy/life-events";
8 |
9 |
10 |
--------------------------------------------------------------------------------
/packages/ui/uploady/src/index.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import Uploady from "./Uploady";
3 |
4 | export default Uploady;
5 |
6 | export { default as useFileInput } from "./useFileInput";
7 |
8 | export {
9 | Uploady,
10 | };
11 |
12 | export * from "@rpldy/uploader";
13 | export * from "@rpldy/shared-ui";
14 |
15 | export type {
16 | UploadyContextType,
17 | UploadyProps,
18 | PreSendData,
19 | } from "@rpldy/shared-ui";
20 |
21 |
--------------------------------------------------------------------------------
/packages/ui/uploady/src/tests/mocks/rpldy-uploady.mock.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | const Uploady = vi.fn(() => );
4 |
5 | const composeEnhancers = vi.fn();
6 |
7 | // Uploady.composeEnhancers = composeEnhancers;
8 |
9 | vi.doMock("@rpldy/uploady", () => ({
10 | default: Uploady,
11 | composeEnhancers,
12 | }));
13 |
14 | export default Uploady;
15 |
16 | export {
17 | Uploady,
18 | composeEnhancers,
19 | };
20 |
21 |
--------------------------------------------------------------------------------
/packages/ui/uploady/types/index.d.ts:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 | import { UploadyProps } from "@rpldy/shared-ui";
3 | import type { InputRef } from "@rpldy/shared-ui";
4 |
5 | export * from "@rpldy/shared-ui";
6 |
7 | export {
8 | FILE_STATES,
9 | BATCH_STATES,
10 | } from "@rpldy/shared";
11 |
12 | export const Uploady: React.ComponentType;
13 |
14 | export default Uploady;
15 |
16 | export const useFileInput: (fileInputRef?: InputRef) => InputRef;
17 |
--------------------------------------------------------------------------------
/pnpm-workspace.yaml:
--------------------------------------------------------------------------------
1 | packages:
2 | - "packages/core/*"
3 | - "packages/ui/*"
4 | - "packages/native/*"
5 |
--------------------------------------------------------------------------------
/scripts/lernaUtils.mjs:
--------------------------------------------------------------------------------
1 | import { getPackages } from "@monorepo-utils/package-utils"
2 |
3 | const getMatchingPackages = () => {
4 | const pkgs = getPackages(".");
5 |
6 | return pkgs.map((pkg) => {
7 | return {
8 | name: pkg.packageJSON.name,
9 | location: pkg.location,
10 | json: pkg.packageJSON,
11 | get(field){
12 | return pkg.packageJSON[field];
13 | }
14 | };
15 | });
16 | };
17 |
18 | export {
19 | getMatchingPackages,
20 | };
21 |
--------------------------------------------------------------------------------
/scripts/reportBundleSizeToGithub.mjs:
--------------------------------------------------------------------------------
1 | import core from "@actions/core";
2 |
3 | const outputBundleSize = (data) => {
4 | core.info("outputting bundle size report...");
5 | const dataStr = JSON.stringify(data);
6 | core.setOutput("BUNDLE_SIZE_REPORT_DATA", dataStr);
7 | };
8 |
9 | export default outputBundleSize;
10 |
--------------------------------------------------------------------------------
/scripts/uploadyVersion.mjs:
--------------------------------------------------------------------------------
1 | import uploadyPkg from "../packages/ui/uploady/package.json" with { type: "json" };
2 |
3 | const getUploadyVersion = () => {
4 | return uploadyPkg.version;
5 | };
6 |
7 | export {
8 | getUploadyVersion
9 | }
10 |
--------------------------------------------------------------------------------
/story-helpers/ComponentsStyles.js:
--------------------------------------------------------------------------------
1 | import { css } from "styled-components";
2 |
3 | const uploadButtonCss = css`
4 | min-width: 100px;
5 | height: 50px;
6 | background-color: #010916;
7 | border: 1px solid #4b5763;
8 | color: #b0b1b3;
9 | font-size: 20px;
10 | display: block;
11 | margin: 10px 0;
12 | cursor: pointer;
13 |
14 | &:disabled {
15 | background-color: rgba(61,71,88,0.54);
16 | color: #afb4b0;
17 | cursor: default;
18 | }
19 | `;
20 |
21 | const uploadUrlInputCss = css`
22 | width: 300px;
23 | font-size: 18px;
24 | line-height: 20px;
25 | height: 20px;
26 | `;
27 |
28 | export {
29 | uploadButtonCss,
30 | uploadUrlInputCss,
31 | };
32 |
--------------------------------------------------------------------------------
/story-helpers/consts.js:
--------------------------------------------------------------------------------
1 |
2 | export const DESTINATION_TYPES = {
3 | "mock": "mock",
4 | "cloudinary": "cloudinary",
5 | "url": "url",
6 | "local": "local",
7 | };
8 |
9 | export const UMD_NAMES = {
10 | "CORE": "CORE",
11 | "CORE_UI": "CORE_UI",
12 | "CORE_CHUNKED_UI": "CORE_CHUNKED_UI",
13 | "ALL": "ALL",
14 | };
15 |
16 | export const DEFAULT_CHUNK_SIZE = 524_288; //~500KB
17 |
--------------------------------------------------------------------------------
/story-helpers/createUploadyStory.jsx:
--------------------------------------------------------------------------------
1 | // @flow
2 | import React from "react";
3 | import useStoryUploadySetupFromArgs from "./storySetupControls/useStoryUploadySetupFromArgs";
4 |
5 | import type { Node } from "react";
6 | import type { Destination } from "@rpldy/shared";
7 | import type { UploaderEnhancer } from "@rpldy/uploader";
8 |
9 | export type UploadyStory = {
10 | render: (args: any) => Node,
11 | args?: any,
12 | argTypes?: any,
13 | };
14 |
15 | export type UploadyStoryParams = {
16 | uploadType?: string,
17 | destination?: Destination,
18 | multiple?: boolean,
19 | enhancer?: UploaderEnhancer
20 | };
21 |
22 | const createUploadyStory = (Component: React.ComponentType, storyParams: {| args?: any, argTypes?: any |} = {}): UploadyStory => {
23 | return {
24 | render: (args): Node => {
25 | const setupProps = useStoryUploadySetupFromArgs(args);
26 | return ;
27 | },
28 | ...storyParams
29 | };
30 | };
31 |
32 | export default createUploadyStory;
33 |
--------------------------------------------------------------------------------
/story-helpers/dropZoneCss.js:
--------------------------------------------------------------------------------
1 | import { css } from "styled-components";
2 |
3 | export default css`
4 | display: flex;
5 | flex-direction: column;
6 | justify-content: center;
7 | align-items: center;
8 | width: 400px;
9 | height: 400px;
10 | border: 1px dotted #000;
11 |
12 | #drop-text {
13 | display: none;
14 | }
15 |
16 | &.drag-over {
17 | background-color: rgba(114,255,59,0.6);
18 | #drop-text {
19 | display: block;
20 | }
21 |
22 | #drag-text {
23 | display: none;
24 | }
25 | }
26 | `;
27 |
--------------------------------------------------------------------------------
/story-helpers/helpers.js:
--------------------------------------------------------------------------------
1 | export const isProd = process.env.NODE_ENV === "production";
2 |
3 | console.log("Storybook runtime - IS PRDD === ", isProd);
4 |
--------------------------------------------------------------------------------
/story-helpers/useEventsLogUpdater.js:
--------------------------------------------------------------------------------
1 | import { useRef, useCallback } from "react";
2 |
3 | export default () => {
4 | const updateFn = useRef();
5 | //need this because we cant update state related to uploads outside Uploady
6 | const setUpdater = useCallback((fn) => {
7 | updateFn.current = fn;
8 | }, []);
9 |
10 | return {
11 | setUpdater,
12 | logEvent: (...args) => updateFn.current(...args),
13 | };
14 | };
15 |
--------------------------------------------------------------------------------
/story-helpers/useExternalUploadOptionsProvider.js:
--------------------------------------------------------------------------------
1 | import { useState } from "react";
2 |
3 | const useExternalUploadOptionsProvider = () => {
4 | const [cyOptions, setCyOptions] = useState(() => {
5 | return window.__extUploadOptions || window.parent.__extUploadOptions || null;
6 | });
7 |
8 | window._setUploadOptions = setCyOptions;
9 |
10 | return cyOptions;
11 | };
12 |
13 | export {
14 | useExternalUploadOptionsProvider,
15 | };
16 |
--------------------------------------------------------------------------------
/test/umd-bundle/rpldy-core.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 | Title
12 |
13 |
14 |
15 | Test: rpldy.core
16 |
17 |
18 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "esnext",
4 | "module": "CommonJS",
5 | "lib": ["dom", "es2017", "esnext"],
6 | "jsx": "react",
7 | "strict": true,
8 | "moduleResolution": "node",
9 | "noImplicitAny": true,
10 | "strictNullChecks": true,
11 | "types": [],
12 | "noEmit": true,
13 | "esModuleInterop": true,
14 | "allowSyntheticDefaultImports": true,
15 | "strictFunctionTypes": true,
16 | "baseUrl": ".",
17 | "paths": {
18 | "~/*": ["packages/*"]
19 | }
20 | },
21 | "include": ["packages/**/types/*.ts", "packages/**/types/*.tsx"],
22 | "exclude": []
23 | }
24 |
--------------------------------------------------------------------------------