mirror of
https://github.com/transloadit/uppy.git
synced 2026-01-23 02:25:07 +00:00
Merge @uppy/status-bar into @uppy/dashboard (#5825)
This pull request removes the `@uppy/status-bar` plugin and integrates it directly into the `@uppy/dashboard` plugin. ### Breakdown of the merge - The `StatusBar` class was refactored from a `UIPlugin` into a Preact Class component. - The `locale` strings from status-bar were merged into dashboard's locale file. - The Dashboard plugin now integrates the `StatusBar` component directly, controlling its visibility and passing down all props ( i.e. options that were specific to StatusBar (like showProgressDetails). - The standalone StatusBar wrappers for React , Vue , svelte , Angular were removed. - every reference to the @uppy/status-bar package from the monorepo (including in package.json and tsconfig.json files). - fixed failing tests and removed redundant tests. --------- Co-authored-by: Mikael Finstad <finstaden@gmail.com> Co-authored-by: Merlijn Vos <merlijn@soverin.net>
This commit is contained in:
parent
c5b51f6158
commit
e8692434d6
58 changed files with 483 additions and 982 deletions
48
.changeset/huge-dingos-march.md
Normal file
48
.changeset/huge-dingos-march.md
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
---
|
||||
"@uppy/dashboard": major
|
||||
---
|
||||
|
||||
### Merge @uppy/status-bar into @uppy/dashboard
|
||||
|
||||
The `@uppy/status-bar` package has been merged into `@uppy/dashboard`. The plugin gave a false promise of flexibility as a standalone plugin but was always built tightly coupled for `@uppy/dashboard`. With the new headless components and hooks, we want go all in those components and remove the confusing, inflexible ones.
|
||||
|
||||
StatusBar is now rendered as an integrated component within Dashboard rather than as a separate plugin. The standalone `@uppy/status-bar` package is no longer maintained and should be removed from your dependencies.
|
||||
|
||||
#### Migration from standalone StatusBar to Dashboard
|
||||
|
||||
If you were using StatusBar as a separate plugin, you'll need to migrate to using Dashboard with the equivalent options.
|
||||
|
||||
**Before:**
|
||||
|
||||
```javascript
|
||||
import StatusBar from '@uppy/status-bar'
|
||||
|
||||
uppy.use(StatusBar, {
|
||||
target: '#status-bar',
|
||||
showProgressDetails: true,
|
||||
hideUploadButton: false,
|
||||
hideAfterFinish: true
|
||||
})
|
||||
```
|
||||
|
||||
**Now:**
|
||||
|
||||
```javascript
|
||||
import Dashboard from '@uppy/dashboard'
|
||||
|
||||
uppy.use(Dashboard, {
|
||||
target: '#dashboard',
|
||||
hideProgressDetails: false,
|
||||
hideUploadButton: false,
|
||||
hideAfterFinish: true
|
||||
})
|
||||
```
|
||||
|
||||
All StatusBar configuration options are now available directly as Dashboard options:
|
||||
- `hideProgressDetails` - Hide detailed progress information (previously `showProgressDetails` with inverted logic)
|
||||
- `hideUploadButton` - Hide the upload button
|
||||
- `hideAfterFinish` - Hide status bar after upload completion
|
||||
- `hideRetryButton` - Hide the retry button
|
||||
- `hidePauseResumeButton` - Hide pause/resume controls
|
||||
- `hideCancelButton` - Hide the cancel button
|
||||
- `doneButtonHandler` - Custom handler for the done button
|
||||
1
.github/CONTRIBUTING.md
vendored
1
.github/CONTRIBUTING.md
vendored
|
|
@ -440,7 +440,6 @@ Your `package.json` should resemble something like this:
|
|||
"@uppy/dashboard": "workspace:^",
|
||||
"@uppy/drag-drop": "workspace:^",
|
||||
"@uppy/progress-bar": "workspace:^",
|
||||
"@uppy/status-bar": "workspace:^",
|
||||
"@uppy/utils": "workspace:^",
|
||||
"prop-types": "^15.6.1"
|
||||
},
|
||||
|
|
|
|||
43
.github/MIGRATION.md
vendored
43
.github/MIGRATION.md
vendored
|
|
@ -18,6 +18,7 @@ This is a temporary file that can be updated with any pending migration changes,
|
|||
`logout()`, `thumbnail()`. Please use: `providerUserSession`.`accessToken`
|
||||
instead.
|
||||
|
||||
|
||||
### @uppy/informer merged into @uppy/dashboard
|
||||
|
||||
The `@uppy/informer` plugin has been merged into `@uppy/dashboard` to reduce bundle size and improve maintainability. The `@uppy/informer` package is no longer maintained as a standalone package and should be removed from your dependencies.
|
||||
|
|
@ -52,7 +53,7 @@ uppy.on('progress', (progress) => {
|
|||
|
||||
**Migration steps:**
|
||||
1. Remove `@uppy/progress-bar` from your dependencies
|
||||
2. Create a custom progress indicator using Uppy's `progress` or `upload-progress` events
|
||||
2. Create a custom progress indicator using Uppy's `progress` or `upload-progress` events.
|
||||
3. Style your progress bar according to your design system.
|
||||
|
||||
### @uppy/drag-drop and @uppy/file-input removed
|
||||
|
|
@ -134,4 +135,44 @@ import { Dashboard, StatusBar } from '@uppy/react'
|
|||
```javascript
|
||||
import Dashboard from '@uppy/react/dashboard'
|
||||
import StatusBar from '@uppy/react/status-bar'
|
||||
```
|
||||
|
||||
|
||||
### @uppy/status-bar merged into @uppy/dashboard
|
||||
|
||||
The `@uppy/status-bar` package has been merged into `@uppy/dashboard`. The plugin gave a false promise of flexibility as a standalone plugin but was always built tightly coupled for `@uppy/dashboard`. With the new headless components and hooks, we want go all in those components and remove the confusing, inflexible ones.
|
||||
|
||||
**Migration steps:**
|
||||
|
||||
1. Remove `@uppy/status-bar` from your dependencies
|
||||
2. Replace StatusBar usage with Dashboard
|
||||
3. Move all StatusBar options directly to Dashboard options
|
||||
|
||||
All StatusBar configuration options are now available as Dashboard options:
|
||||
- `hideProgressDetails` - Hide detailed progress information
|
||||
- `hideUploadButton` - Hide the upload button
|
||||
- `hideAfterFinish` - Hide status bar after upload completion
|
||||
- `hideRetryButton` - Hide the retry button
|
||||
- `hidePauseResumeButton` - Hide pause/resume controls
|
||||
- `hideCancelButton` - Hide the cancel button
|
||||
- `doneButtonHandler` - Custom handler for the done button
|
||||
|
||||
```js
|
||||
// Before - separate StatusBar plugin
|
||||
import StatusBar from '@uppy/status-bar'
|
||||
uppy.use(StatusBar, {
|
||||
target: '#status-bar',
|
||||
hideProgressDetails: true,
|
||||
hideUploadButton: false,
|
||||
hideAfterFinish: true
|
||||
})
|
||||
|
||||
// After - use Dashboard with StatusBar options
|
||||
import Dashboard from '@uppy/dashboard'
|
||||
uppy.use(Dashboard, {
|
||||
target: '#dashboard',
|
||||
hideProgressDetails: false,
|
||||
hideUploadButton: false,
|
||||
hideAfterFinish: true
|
||||
})
|
||||
```
|
||||
|
|
@ -29,7 +29,6 @@
|
|||
"@angular/core": "^17.0.0 || ^18.0.0 || ^19.0.0",
|
||||
"@uppy/core": "workspace:^",
|
||||
"@uppy/dashboard": "workspace:^",
|
||||
"@uppy/status-bar": "workspace:^",
|
||||
"@uppy/utils": "workspace:^"
|
||||
},
|
||||
"sideEffects": false
|
||||
|
|
|
|||
|
|
@ -1,30 +0,0 @@
|
|||
import { ChangeDetectionStrategy, Component, type OnInit } from "@angular/core";
|
||||
import { Uppy } from "@uppy/core";
|
||||
import FileInput from "@uppy/file-input";
|
||||
import type * as StatusBar from "@uppy/status-bar";
|
||||
import Tus from "@uppy/tus";
|
||||
import type { Body, Meta } from "@uppy/utils/lib/UppyFile";
|
||||
|
||||
@Component({
|
||||
selector: "uppy-status-bar-demo",
|
||||
template: `
|
||||
<div class="UppyInput"></div>
|
||||
<uppy-status-bar [uppy]="uppy" [props]="props"></uppy-status-bar>
|
||||
`,
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class StatusBarDemoComponent<M extends Meta, B extends Body>
|
||||
implements OnInit
|
||||
{
|
||||
uppy: Uppy<M, B> = new Uppy({ debug: true, autoProceed: true });
|
||||
props: StatusBar.StatusBarOptions = {
|
||||
hideUploadButton: true,
|
||||
hideAfterFinish: false,
|
||||
};
|
||||
|
||||
ngOnInit(): void {
|
||||
this.uppy
|
||||
.use(FileInput, { target: ".UppyInput", pretty: false })
|
||||
.use(Tus, { endpoint: "https://master.tus.io/files/" });
|
||||
}
|
||||
}
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
import { async, type ComponentFixture, TestBed } from "@angular/core/testing";
|
||||
|
||||
import { StatusBarComponent } from "./status-bar.component";
|
||||
|
||||
describe("StatusBarComponent", () => {
|
||||
let component: StatusBarComponent;
|
||||
let fixture: ComponentFixture<StatusBarComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [StatusBarComponent],
|
||||
}).compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(StatusBarComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it("should create", () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
|
@ -1,52 +0,0 @@
|
|||
import {
|
||||
ChangeDetectionStrategy,
|
||||
Component,
|
||||
ElementRef,
|
||||
Input,
|
||||
inject,
|
||||
type OnChanges,
|
||||
type OnDestroy,
|
||||
type SimpleChanges,
|
||||
} from "@angular/core";
|
||||
import { Uppy } from "@uppy/core";
|
||||
import type { StatusBarOptions } from "@uppy/status-bar";
|
||||
import StatusBar from "@uppy/status-bar";
|
||||
import type { Body, Meta } from "@uppy/utils/lib/UppyFile";
|
||||
import { UppyAngularWrapper } from "../../utils/wrapper";
|
||||
|
||||
@Component({
|
||||
selector: "uppy-status-bar",
|
||||
template: "",
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class StatusBarComponent<M extends Meta, B extends Body>
|
||||
extends UppyAngularWrapper<M, B, StatusBarOptions>
|
||||
implements OnDestroy, OnChanges
|
||||
{
|
||||
el = inject(ElementRef);
|
||||
|
||||
@Input() uppy: Uppy<M, B> = new Uppy();
|
||||
@Input() props: StatusBarOptions = {};
|
||||
|
||||
/** Inserted by Angular inject() migration for backwards compatibility */
|
||||
constructor(...args: unknown[]);
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.onMount(
|
||||
{ id: "angular:StatusBar", target: this.el.nativeElement },
|
||||
StatusBar,
|
||||
);
|
||||
}
|
||||
|
||||
ngOnChanges(changes: SimpleChanges): void {
|
||||
this.handleChanges(changes, StatusBar);
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this.uninstall();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,15 +0,0 @@
|
|||
import { moduleMetadata } from "@storybook/angular";
|
||||
import { StatusBarDemoComponent } from "./status-bar-demo.component";
|
||||
|
||||
export default {
|
||||
title: "Status Bar",
|
||||
decorators: [
|
||||
moduleMetadata({
|
||||
declarations: [StatusBarDemoComponent],
|
||||
}),
|
||||
],
|
||||
};
|
||||
|
||||
export const Default = () => ({
|
||||
component: StatusBarDemoComponent,
|
||||
});
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
import type { ElementRef, SimpleChanges } from "@angular/core";
|
||||
import type { UIPlugin, UIPluginOptions, Uppy } from "@uppy/core";
|
||||
import type { StatusBarOptions } from "@uppy/status-bar";
|
||||
import type { DashboardOptions } from "@uppy/dashboard";
|
||||
import type { Body, Meta } from "@uppy/utils/lib/UppyFile";
|
||||
|
||||
export abstract class UppyAngularWrapper<
|
||||
|
|
@ -9,7 +9,7 @@ export abstract class UppyAngularWrapper<
|
|||
Opts extends UIPluginOptions,
|
||||
PluginType extends UIPlugin<Opts, M, B> = UIPlugin<Opts, M, B>,
|
||||
> {
|
||||
abstract props: StatusBarOptions;
|
||||
abstract props: DashboardOptions<M, B>;
|
||||
abstract el: ElementRef;
|
||||
abstract uppy: Uppy<M, B>;
|
||||
private options: any;
|
||||
|
|
|
|||
|
|
@ -4,4 +4,3 @@
|
|||
|
||||
export { DashboardComponent } from './lib/components/dashboard/dashboard.component'
|
||||
export { DashboardModalComponent } from './lib/components/dashboard-modal/dashboard-modal.component'
|
||||
export { StatusBarComponent } from './lib/components/status-bar/status-bar.component'
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@
|
|||
"dependsOn": [
|
||||
"@uppy/core#build",
|
||||
"@uppy/dashboard#build",
|
||||
"@uppy/status-bar#build",
|
||||
"@uppy/utils#build"
|
||||
],
|
||||
"inputs": [
|
||||
|
|
|
|||
|
|
@ -40,7 +40,6 @@
|
|||
"dependencies": {
|
||||
"@transloadit/prettier-bytes": "^0.3.4",
|
||||
"@uppy/provider-views": "workspace:^",
|
||||
"@uppy/status-bar": "workspace:^",
|
||||
"@uppy/thumbnail-generator": "workspace:^",
|
||||
"@uppy/utils": "workspace:^",
|
||||
"classnames": "^2.2.6",
|
||||
|
|
@ -52,7 +51,6 @@
|
|||
"devDependencies": {
|
||||
"@uppy/core": "workspace:^",
|
||||
"@uppy/google-drive": "workspace:^",
|
||||
"@uppy/status-bar": "workspace:^",
|
||||
"@uppy/url": "workspace:^",
|
||||
"@uppy/webcam": "workspace:^",
|
||||
"@vitest/browser": "^3.2.4",
|
||||
|
|
|
|||
|
|
@ -11,8 +11,6 @@ import type {
|
|||
} from '@uppy/core'
|
||||
import { UIPlugin } from '@uppy/core'
|
||||
import { defaultPickerIcon } from '@uppy/provider-views'
|
||||
import StatusBar from '@uppy/status-bar'
|
||||
import type StatusBarLocale from '@uppy/status-bar/lib/locale.js'
|
||||
import ThumbnailGenerator from '@uppy/thumbnail-generator'
|
||||
import findAllDOMElements from '@uppy/utils/lib/findAllDOMElements'
|
||||
import getDroppedFiles from '@uppy/utils/lib/getDroppedFiles'
|
||||
|
|
@ -160,7 +158,7 @@ interface DashboardMiscOptions<M extends Meta, B extends Body>
|
|||
showLinkToFileUploadResult?: boolean
|
||||
showNativePhotoCameraButton?: boolean
|
||||
showNativeVideoCameraButton?: boolean
|
||||
showProgressDetails?: boolean
|
||||
hideProgressDetails?: boolean
|
||||
showRemoveButtonAfterComplete?: boolean
|
||||
showSelectedFiles?: boolean
|
||||
singleFileFullScreen?: boolean
|
||||
|
|
@ -170,7 +168,7 @@ interface DashboardMiscOptions<M extends Meta, B extends Body>
|
|||
thumbnailWidth?: number
|
||||
trigger?: string | Element | null
|
||||
waitForThumbnailsBeforeUpload?: boolean
|
||||
locale?: LocaleStrings<typeof locale> & typeof StatusBarLocale
|
||||
locale?: LocaleStrings<typeof locale>
|
||||
}
|
||||
|
||||
export type DashboardOptions<
|
||||
|
|
@ -187,7 +185,7 @@ const defaultOptions = {
|
|||
waitForThumbnailsBeforeUpload: false,
|
||||
defaultPickerIcon,
|
||||
showLinkToFileUploadResult: false,
|
||||
showProgressDetails: false,
|
||||
hideProgressDetails: false,
|
||||
hideUploadButton: false,
|
||||
hideCancelButton: false,
|
||||
hideRetryButton: false,
|
||||
|
|
@ -1239,9 +1237,6 @@ export default class Dashboard<M extends Meta, B extends Body> extends UIPlugin<
|
|||
showLinkToFileUploadResult: this.opts.showLinkToFileUploadResult,
|
||||
fileManagerSelectionType: this.opts.fileManagerSelectionType,
|
||||
proudlyDisplayPoweredByUppy: this.opts.proudlyDisplayPoweredByUppy,
|
||||
hideCancelButton: this.opts.hideCancelButton,
|
||||
hideRetryButton: this.opts.hideRetryButton,
|
||||
hidePauseResumeButton: this.opts.hidePauseResumeButton,
|
||||
showRemoveButtonAfterComplete: this.opts.showRemoveButtonAfterComplete,
|
||||
containerWidth: pluginState.containerWidth,
|
||||
containerHeight: pluginState.containerHeight,
|
||||
|
|
@ -1265,6 +1260,15 @@ export default class Dashboard<M extends Meta, B extends Body> extends UIPlugin<
|
|||
handleDrop: this.handleDrop,
|
||||
// informer props
|
||||
disableInformer: this.opts.disableInformer,
|
||||
// status-bar props
|
||||
disableStatusBar: this.opts.disableStatusBar,
|
||||
hideProgressDetails: this.opts.hideProgressDetails,
|
||||
hideUploadButton: this.opts.hideUploadButton,
|
||||
hideRetryButton: this.opts.hideRetryButton,
|
||||
hidePauseResumeButton: this.opts.hidePauseResumeButton,
|
||||
hideCancelButton: this.opts.hideCancelButton,
|
||||
hideProgressAfterFinish: this.opts.hideProgressAfterFinish,
|
||||
doneButtonHandler: this.opts.doneButtonHandler,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -1302,29 +1306,6 @@ export default class Dashboard<M extends Meta, B extends Body> extends UIPlugin<
|
|||
}
|
||||
}
|
||||
|
||||
#getStatusBarOpts() {
|
||||
const {
|
||||
hideUploadButton,
|
||||
hideRetryButton,
|
||||
hidePauseResumeButton,
|
||||
hideCancelButton,
|
||||
showProgressDetails,
|
||||
hideProgressAfterFinish,
|
||||
locale: l,
|
||||
doneButtonHandler,
|
||||
} = this.opts
|
||||
return {
|
||||
hideUploadButton,
|
||||
hideRetryButton,
|
||||
hidePauseResumeButton,
|
||||
hideCancelButton,
|
||||
showProgressDetails,
|
||||
hideAfterFinish: hideProgressAfterFinish,
|
||||
locale: l,
|
||||
doneButtonHandler,
|
||||
}
|
||||
}
|
||||
|
||||
#getThumbnailGeneratorOpts() {
|
||||
const {
|
||||
thumbnailWidth,
|
||||
|
|
@ -1344,19 +1325,11 @@ export default class Dashboard<M extends Meta, B extends Body> extends UIPlugin<
|
|||
|
||||
setOptions(opts: Partial<DashboardOptions<M, B>>) {
|
||||
super.setOptions(opts)
|
||||
this.uppy
|
||||
.getPlugin(this.#getStatusBarId())
|
||||
?.setOptions(this.#getStatusBarOpts())
|
||||
|
||||
this.uppy
|
||||
.getPlugin(this.#getThumbnailGeneratorId())
|
||||
?.setOptions(this.#getThumbnailGeneratorOpts())
|
||||
}
|
||||
|
||||
#getStatusBarId() {
|
||||
return `${this.id}:StatusBar`
|
||||
}
|
||||
|
||||
#getThumbnailGeneratorId() {
|
||||
return `${this.id}:ThumbnailGenerator`
|
||||
}
|
||||
|
|
@ -1401,14 +1374,6 @@ export default class Dashboard<M extends Meta, B extends Body> extends UIPlugin<
|
|||
this.mount(target, this)
|
||||
}
|
||||
|
||||
if (!this.opts.disableStatusBar) {
|
||||
this.uppy.use(StatusBar, {
|
||||
id: this.#getStatusBarId(),
|
||||
target: this,
|
||||
...this.#getStatusBarOpts(),
|
||||
})
|
||||
}
|
||||
|
||||
if (!this.opts.disableThumbnailGenerator) {
|
||||
this.uppy.use(ThumbnailGenerator, {
|
||||
id: this.#getThumbnailGeneratorId(),
|
||||
|
|
@ -1440,11 +1405,6 @@ export default class Dashboard<M extends Meta, B extends Body> extends UIPlugin<
|
|||
}
|
||||
|
||||
uninstall = (): void => {
|
||||
if (!this.opts.disableStatusBar) {
|
||||
const statusBar = this.uppy.getPlugin(`${this.id}:StatusBar`)
|
||||
if (statusBar) this.uppy.removePlugin(statusBar)
|
||||
}
|
||||
|
||||
if (!this.opts.disableThumbnailGenerator) {
|
||||
const thumbnail = this.uppy.getPlugin(`${this.id}:ThumbnailGenerator`)
|
||||
if (thumbnail) this.uppy.removePlugin(thumbnail)
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ import Informer from './Informer/Informer.js'
|
|||
import PickerPanelContent from './PickerPanelContent.js'
|
||||
import PanelTopBar from './PickerPanelTopBar.js'
|
||||
import Slide from './Slide.js'
|
||||
import StatusBar from './StatusBar/StatusBar.js'
|
||||
|
||||
// http://dev.edenspiekermann.com/2016/02/11/introducing-accessible-modal-dialog
|
||||
// https://github.com/ghosh/micromodal
|
||||
|
|
@ -121,6 +122,11 @@ type DashboardUIProps<M extends Meta, B extends Body> = {
|
|||
handleDragLeave: (event: DragEvent) => void
|
||||
handleDrop: (event: DragEvent) => void
|
||||
disableInformer: boolean
|
||||
disableStatusBar: boolean
|
||||
hideProgressDetails: boolean
|
||||
hideUploadButton: boolean
|
||||
hideProgressAfterFinish: boolean
|
||||
doneButtonHandler: (() => void) | null
|
||||
}
|
||||
|
||||
export default function Dashboard<M extends Meta, B extends Body>(
|
||||
|
|
@ -336,6 +342,20 @@ export default function Dashboard<M extends Meta, B extends Body>(
|
|||
</Slide>
|
||||
|
||||
<div className="uppy-Dashboard-progressindicators">
|
||||
{!props.disableInformer && <Informer uppy={props.uppy} />}
|
||||
{!props.disableStatusBar && (
|
||||
<StatusBar
|
||||
uppy={props.uppy}
|
||||
i18n={props.i18n}
|
||||
hideProgressDetails={props.hideProgressDetails}
|
||||
hideUploadButton={props.hideUploadButton}
|
||||
hideRetryButton={props.hideRetryButton}
|
||||
hidePauseResumeButton={props.hidePauseResumeButton}
|
||||
hideCancelButton={props.hideCancelButton}
|
||||
hideAfterFinish={props.hideProgressAfterFinish}
|
||||
doneButtonHandler={props.doneButtonHandler}
|
||||
/>
|
||||
)}
|
||||
{!props.disableInformer && <Informer uppy={props.uppy} />}
|
||||
{props.progressindicators.map((target: TargetWithRender) => {
|
||||
// TODO
|
||||
|
|
|
|||
|
|
@ -72,7 +72,10 @@ export default function FileCard(props: $TSFixMe) {
|
|||
form.addEventListener('submit', handleSave)
|
||||
return () => {
|
||||
form.removeEventListener('submit', handleSave)
|
||||
document.body.removeChild(form)
|
||||
// check if form is still in the DOM before removing
|
||||
if (form.parentNode) {
|
||||
document.body.removeChild(form)
|
||||
}
|
||||
}
|
||||
}, [form, handleSave])
|
||||
|
||||
|
|
|
|||
|
|
@ -360,7 +360,7 @@ interface ProgressBarUploadingProps {
|
|||
i18n: I18n
|
||||
supportsUploadProgress: boolean
|
||||
totalProgress: number
|
||||
showProgressDetails: boolean | undefined
|
||||
hideProgressDetails: boolean | undefined
|
||||
isUploadStarted: boolean
|
||||
isAllComplete: boolean
|
||||
isAllPaused: boolean
|
||||
|
|
@ -378,7 +378,7 @@ function ProgressBarUploading(props: ProgressBarUploadingProps) {
|
|||
i18n,
|
||||
supportsUploadProgress,
|
||||
totalProgress,
|
||||
showProgressDetails,
|
||||
hideProgressDetails,
|
||||
isUploadStarted,
|
||||
isAllComplete,
|
||||
isAllPaused,
|
||||
|
|
@ -400,7 +400,7 @@ function ProgressBarUploading(props: ProgressBarUploadingProps) {
|
|||
const title = isAllPaused ? i18n('paused') : i18n('uploading')
|
||||
|
||||
function renderProgressDetails() {
|
||||
if (!isAllPaused && !showUploadNewlyAddedFiles && showProgressDetails) {
|
||||
if (!isAllPaused && !showUploadNewlyAddedFiles && !hideProgressDetails) {
|
||||
if (supportsUploadProgress) {
|
||||
return (
|
||||
<ProgressDetails
|
||||
|
|
@ -1,18 +1,8 @@
|
|||
import type {
|
||||
Body,
|
||||
DefinePluginOpts,
|
||||
Meta,
|
||||
State,
|
||||
Uppy,
|
||||
UppyFile,
|
||||
} from '@uppy/core'
|
||||
import { UIPlugin } from '@uppy/core'
|
||||
import type { Body, Meta, Uppy, UppyFile } from '@uppy/core'
|
||||
import emaFilter from '@uppy/utils/lib/emaFilter'
|
||||
import getTextDirection from '@uppy/utils/lib/getTextDirection'
|
||||
import type { I18n } from '@uppy/utils/lib/Translator'
|
||||
import type { ComponentChild } from 'preact'
|
||||
import { h } from 'preact'
|
||||
import packageJson from '../package.json' with { type: 'json' }
|
||||
import locale from './locale.js'
|
||||
import { Component, h } from 'preact'
|
||||
import type { StatusBarOptions } from './StatusBarOptions.js'
|
||||
import statusBarStates from './StatusBarStates.js'
|
||||
import StatusBarUI, { type StatusBarUIProps } from './StatusBarUI.js'
|
||||
|
|
@ -20,6 +10,18 @@ import StatusBarUI, { type StatusBarUIProps } from './StatusBarUI.js'
|
|||
const speedFilterHalfLife = 2000
|
||||
const ETAFilterHalfLife = 2000
|
||||
|
||||
type StatusBarProps<M extends Meta, B extends Body> = {
|
||||
uppy: Uppy<M, B>
|
||||
hideProgressDetails: boolean
|
||||
hideUploadButton: boolean
|
||||
hideRetryButton: boolean
|
||||
hidePauseResumeButton: boolean
|
||||
hideCancelButton: boolean
|
||||
hideAfterFinish: boolean
|
||||
doneButtonHandler: (() => void) | null
|
||||
i18n: I18n
|
||||
}
|
||||
|
||||
function getUploadingState(
|
||||
error: unknown,
|
||||
isAllComplete: boolean,
|
||||
|
|
@ -66,22 +68,15 @@ const defaultOptions = {
|
|||
hideRetryButton: false,
|
||||
hidePauseResumeButton: false,
|
||||
hideCancelButton: false,
|
||||
showProgressDetails: false,
|
||||
hideProgressDetails: false,
|
||||
hideAfterFinish: true,
|
||||
doneButtonHandler: null,
|
||||
} satisfies StatusBarOptions
|
||||
|
||||
/**
|
||||
* StatusBar: renders a status bar with upload/pause/resume/cancel/retry buttons,
|
||||
* progress percentage and time remaining.
|
||||
*/
|
||||
export default class StatusBar<M extends Meta, B extends Body> extends UIPlugin<
|
||||
DefinePluginOpts<StatusBarOptions, keyof typeof defaultOptions>,
|
||||
M,
|
||||
B
|
||||
> {
|
||||
static VERSION = packageJson.version
|
||||
|
||||
export default class StatusBar<
|
||||
M extends Meta,
|
||||
B extends Body,
|
||||
> extends Component<StatusBarProps<M, B>> {
|
||||
#lastUpdateTime!: ReturnType<typeof performance.now>
|
||||
|
||||
#previousUploadedBytes!: number | null
|
||||
|
|
@ -90,24 +85,46 @@ export default class StatusBar<M extends Meta, B extends Body> extends UIPlugin<
|
|||
|
||||
#previousETA!: number | null
|
||||
|
||||
constructor(uppy: Uppy<M, B>, opts?: StatusBarOptions) {
|
||||
super(uppy, { ...defaultOptions, ...opts })
|
||||
this.id = this.opts.id || 'StatusBar'
|
||||
this.title = 'StatusBar'
|
||||
this.type = 'progressindicator'
|
||||
componentDidMount(): void {
|
||||
// Initialize ETA calculation variables
|
||||
this.#lastUpdateTime = performance.now()
|
||||
this.#previousUploadedBytes = this.props.uppy
|
||||
.getFiles()
|
||||
.reduce((pv, file) => pv + (file.progress.bytesUploaded || 0), 0)
|
||||
|
||||
this.defaultLocale = locale
|
||||
// Listen for upload start to reset ETA calculation
|
||||
this.props.uppy.on('upload', this.#onUploadStart)
|
||||
}
|
||||
|
||||
this.i18nInit()
|
||||
componentWillUnmount(): void {
|
||||
this.props.uppy.off('upload', this.#onUploadStart)
|
||||
}
|
||||
|
||||
this.render = this.render.bind(this)
|
||||
this.install = this.install.bind(this)
|
||||
#onUploadStart = (): void => {
|
||||
const { recoveredState } = this.props.uppy.getState()
|
||||
|
||||
this.#previousSpeed = null
|
||||
this.#previousETA = null
|
||||
|
||||
if (recoveredState) {
|
||||
this.#previousUploadedBytes = Object.values(recoveredState.files).reduce(
|
||||
(pv, { progress }) => pv + (progress.bytesUploaded || 0),
|
||||
0,
|
||||
)
|
||||
// We don't set `#lastUpdateTime` at this point because the upload won't
|
||||
// actually resume until the user asks for it.
|
||||
this.props.uppy.emit('restore-confirmed')
|
||||
return
|
||||
}
|
||||
|
||||
this.#lastUpdateTime = performance.now()
|
||||
this.#previousUploadedBytes = 0
|
||||
}
|
||||
|
||||
#computeSmoothETA(totalBytes: {
|
||||
uploaded: number
|
||||
total: number | null // null means indeterminate
|
||||
}) {
|
||||
}): number | null {
|
||||
if (totalBytes.total == null || totalBytes.total === 0) {
|
||||
return null
|
||||
}
|
||||
|
|
@ -124,8 +141,14 @@ export default class StatusBar<M extends Meta, B extends Body> extends UIPlugin<
|
|||
return Math.round((this.#previousETA ?? 0) / 100) / 10
|
||||
}
|
||||
|
||||
// Initialize previousUploadedBytes if it's null
|
||||
if (this.#previousUploadedBytes == null) {
|
||||
this.#previousUploadedBytes = totalBytes.uploaded
|
||||
return null // Can't calculate speed on first call
|
||||
}
|
||||
|
||||
const uploadedBytesSinceLastTick =
|
||||
totalBytes.uploaded - this.#previousUploadedBytes!
|
||||
totalBytes.uploaded - this.#previousUploadedBytes
|
||||
this.#previousUploadedBytes = totalBytes.uploaded
|
||||
|
||||
// uploadedBytesSinceLastTick can be negative in some cases (packet loss?)
|
||||
|
|
@ -134,18 +157,41 @@ export default class StatusBar<M extends Meta, B extends Body> extends UIPlugin<
|
|||
return Math.round((this.#previousETA ?? 0) / 100) / 10
|
||||
}
|
||||
const currentSpeed = uploadedBytesSinceLastTick / dt
|
||||
|
||||
// Guard against invalid speed values
|
||||
if (!Number.isFinite(currentSpeed) || currentSpeed <= 0) {
|
||||
return null
|
||||
}
|
||||
|
||||
const filteredSpeed =
|
||||
this.#previousSpeed == null
|
||||
? currentSpeed
|
||||
: emaFilter(currentSpeed, this.#previousSpeed, speedFilterHalfLife, dt)
|
||||
|
||||
// Guard against invalid filtered speed
|
||||
if (!Number.isFinite(filteredSpeed) || filteredSpeed <= 0) {
|
||||
return null
|
||||
}
|
||||
|
||||
this.#previousSpeed = filteredSpeed
|
||||
const instantETA = remaining / filteredSpeed
|
||||
|
||||
const updatedPreviousETA = Math.max(this.#previousETA! - dt, 0)
|
||||
// Guard against invalid instantETA
|
||||
if (!Number.isFinite(instantETA) || instantETA < 0) {
|
||||
return null
|
||||
}
|
||||
|
||||
const updatedPreviousETA = Math.max((this.#previousETA ?? 0) - dt, 0)
|
||||
const filteredETA =
|
||||
this.#previousETA == null
|
||||
? instantETA
|
||||
: emaFilter(instantETA, updatedPreviousETA, ETAFilterHalfLife, dt)
|
||||
|
||||
// Guard against invalid filteredETA
|
||||
if (!Number.isFinite(filteredETA) || filteredETA < 0) {
|
||||
return null
|
||||
}
|
||||
|
||||
this.#previousETA = filteredETA
|
||||
this.#lastUpdateTime = performance.now()
|
||||
|
||||
|
|
@ -153,12 +199,12 @@ export default class StatusBar<M extends Meta, B extends Body> extends UIPlugin<
|
|||
}
|
||||
|
||||
startUpload = (): ReturnType<Uppy<M, B>['upload']> => {
|
||||
return this.uppy.upload().catch((() => {
|
||||
return this.props.uppy.upload().catch((() => {
|
||||
// Error logged in Core
|
||||
}) as () => undefined)
|
||||
}
|
||||
|
||||
render(state: State<M, B>): ComponentChild {
|
||||
render(): ComponentChild {
|
||||
const {
|
||||
capabilities,
|
||||
files,
|
||||
|
|
@ -166,23 +212,19 @@ export default class StatusBar<M extends Meta, B extends Body> extends UIPlugin<
|
|||
totalProgress,
|
||||
error,
|
||||
recoveredState,
|
||||
} = state
|
||||
} = this.props.uppy.getState()
|
||||
|
||||
const {
|
||||
newFiles,
|
||||
startedFiles,
|
||||
completeFiles,
|
||||
|
||||
isUploadStarted,
|
||||
isAllComplete,
|
||||
isAllPaused,
|
||||
isUploadInProgress,
|
||||
isSomeGhost,
|
||||
} = this.uppy.getObjectOfFilesPerState()
|
||||
} = this.props.uppy.getObjectOfFilesPerState()
|
||||
|
||||
// If some state was recovered, we want to show Upload button/counter
|
||||
// for all the files, because in this case it’s not an Upload button,
|
||||
// but “Confirm Restore Button”
|
||||
const newFilesOrRecovered = recoveredState ? Object.values(files) : newFiles
|
||||
const resumableUploads = !!capabilities.resumableUploads
|
||||
const supportsUploadProgress = capabilities.uploadProgress !== false
|
||||
|
|
@ -213,91 +255,43 @@ export default class StatusBar<M extends Meta, B extends Body> extends UIPlugin<
|
|||
total: totalSize,
|
||||
})
|
||||
|
||||
return StatusBarUI({
|
||||
error,
|
||||
uploadState: getUploadingState(
|
||||
error,
|
||||
isAllComplete,
|
||||
recoveredState,
|
||||
state.files || {},
|
||||
),
|
||||
allowNewUpload,
|
||||
totalProgress,
|
||||
totalSize,
|
||||
totalUploadedSize,
|
||||
isAllComplete: false,
|
||||
isAllPaused,
|
||||
isUploadStarted,
|
||||
isUploadInProgress,
|
||||
isSomeGhost,
|
||||
recoveredState,
|
||||
complete: completeFiles.length,
|
||||
newFiles: newFilesOrRecovered.length,
|
||||
numUploads: startedFiles.length,
|
||||
totalETA,
|
||||
files,
|
||||
i18n: this.i18n,
|
||||
uppy: this.uppy,
|
||||
startUpload: this.startUpload,
|
||||
doneButtonHandler: this.opts.doneButtonHandler,
|
||||
resumableUploads,
|
||||
supportsUploadProgress,
|
||||
showProgressDetails: this.opts.showProgressDetails,
|
||||
hideUploadButton: this.opts.hideUploadButton,
|
||||
hideRetryButton: this.opts.hideRetryButton,
|
||||
hidePauseResumeButton: this.opts.hidePauseResumeButton,
|
||||
hideCancelButton: this.opts.hideCancelButton,
|
||||
hideAfterFinish: this.opts.hideAfterFinish,
|
||||
})
|
||||
}
|
||||
|
||||
onMount(): void {
|
||||
// Set the text direction if the page has not defined one.
|
||||
const element = this.el!
|
||||
const direction = getTextDirection(element)
|
||||
if (!direction) {
|
||||
element.dir = 'ltr'
|
||||
}
|
||||
}
|
||||
|
||||
#onUploadStart = (): void => {
|
||||
const { recoveredState } = this.uppy.getState()
|
||||
|
||||
this.#previousSpeed = null
|
||||
this.#previousETA = null
|
||||
if (recoveredState) {
|
||||
this.#previousUploadedBytes = Object.values(recoveredState.files).reduce(
|
||||
(pv, { progress }) => pv + (progress.bytesUploaded as number),
|
||||
0,
|
||||
)
|
||||
|
||||
// We don't set `#lastUpdateTime` at this point because the upload won't
|
||||
// actually resume until the user asks for it.
|
||||
|
||||
this.uppy.emit('restore-confirmed')
|
||||
return
|
||||
}
|
||||
this.#lastUpdateTime = performance.now()
|
||||
this.#previousUploadedBytes = 0
|
||||
}
|
||||
|
||||
install(): void {
|
||||
const { target } = this.opts
|
||||
if (target) {
|
||||
this.mount(target, this)
|
||||
}
|
||||
this.uppy.on('upload', this.#onUploadStart)
|
||||
|
||||
// To cover the use case where the status bar is installed while the upload
|
||||
// has started, we set `lastUpdateTime` right away.
|
||||
this.#lastUpdateTime = performance.now()
|
||||
this.#previousUploadedBytes = this.uppy
|
||||
.getFiles()
|
||||
.reduce((pv, file) => pv + (file.progress.bytesUploaded as number), 0)
|
||||
}
|
||||
|
||||
uninstall(): void {
|
||||
this.unmount()
|
||||
this.uppy.off('upload', this.#onUploadStart)
|
||||
return (
|
||||
<StatusBarUI
|
||||
error={error}
|
||||
uploadState={getUploadingState(
|
||||
error,
|
||||
isAllComplete,
|
||||
recoveredState,
|
||||
files || {},
|
||||
)}
|
||||
allowNewUpload={allowNewUpload}
|
||||
totalProgress={totalProgress}
|
||||
totalSize={totalSize}
|
||||
totalUploadedSize={totalUploadedSize}
|
||||
isAllComplete={isAllComplete}
|
||||
isAllPaused={isAllPaused}
|
||||
isUploadStarted={isUploadStarted}
|
||||
isUploadInProgress={isUploadInProgress}
|
||||
isSomeGhost={isSomeGhost}
|
||||
recoveredState={recoveredState}
|
||||
complete={completeFiles.length}
|
||||
newFiles={newFilesOrRecovered.length}
|
||||
numUploads={startedFiles.length}
|
||||
totalETA={totalETA}
|
||||
files={files}
|
||||
i18n={this.props.i18n}
|
||||
uppy={this.props.uppy}
|
||||
startUpload={this.startUpload}
|
||||
doneButtonHandler={this.props.doneButtonHandler}
|
||||
resumableUploads={resumableUploads}
|
||||
supportsUploadProgress={supportsUploadProgress}
|
||||
hideProgressDetails={this.props.hideProgressDetails}
|
||||
hideUploadButton={this.props.hideUploadButton}
|
||||
hideRetryButton={this.props.hideRetryButton}
|
||||
hidePauseResumeButton={this.props.hidePauseResumeButton}
|
||||
hideCancelButton={this.props.hideCancelButton}
|
||||
hideAfterFinish={this.props.hideAfterFinish}
|
||||
/>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,13 +1,11 @@
|
|||
import type { UIPluginOptions } from '@uppy/core'
|
||||
import type StatusBarLocale from './locale.js'
|
||||
|
||||
export interface StatusBarOptions extends UIPluginOptions {
|
||||
showProgressDetails?: boolean
|
||||
hideProgressDetails?: boolean
|
||||
hideUploadButton?: boolean
|
||||
hideAfterFinish?: boolean
|
||||
hideRetryButton?: boolean
|
||||
hidePauseResumeButton?: boolean
|
||||
hideCancelButton?: boolean
|
||||
doneButtonHandler?: (() => void) | null
|
||||
locale?: typeof StatusBarLocale
|
||||
}
|
||||
|
|
@ -49,7 +49,7 @@ export interface StatusBarUIProps<M extends Meta, B extends Body> {
|
|||
startUpload: () => void
|
||||
uppy: Uppy<M, B>
|
||||
isAllComplete: boolean
|
||||
showProgressDetails?: boolean
|
||||
hideProgressDetails?: boolean
|
||||
numUploads: number
|
||||
complete: number
|
||||
totalSize: number | null
|
||||
|
|
@ -81,7 +81,7 @@ export default function StatusBarUI<M extends Meta, B extends Body>({
|
|||
startUpload,
|
||||
uppy,
|
||||
isAllComplete,
|
||||
showProgressDetails = undefined,
|
||||
hideProgressDetails = undefined,
|
||||
numUploads,
|
||||
complete,
|
||||
totalSize,
|
||||
|
|
@ -192,7 +192,7 @@ export default function StatusBarUI<M extends Meta, B extends Body>({
|
|||
i18n={i18n}
|
||||
supportsUploadProgress={supportsUploadProgress}
|
||||
totalProgress={totalProgress}
|
||||
showProgressDetails={showProgressDetails}
|
||||
hideProgressDetails={hideProgressDetails}
|
||||
isUploadStarted={isUploadStarted}
|
||||
isAllComplete={isAllComplete}
|
||||
isAllPaused={isAllPaused}
|
||||
|
|
@ -2,6 +2,8 @@ import Uppy from '@uppy/core'
|
|||
import { page, userEvent } from '@vitest/browser/context'
|
||||
import { expect, test } from 'vitest'
|
||||
import Dashboard from './Dashboard.js'
|
||||
import '@uppy/core/css/style.css'
|
||||
import '@uppy/dashboard/css/style.css'
|
||||
|
||||
// Normally you would use one of vitest's framework renderers, such as vitest-browser-react,
|
||||
// but that's overkill for us so we write our own plain HTML renderer.
|
||||
|
|
@ -22,7 +24,9 @@ test('Basic Dashboard functionality works in the browser', async () => {
|
|||
})
|
||||
|
||||
await expect.element(page.getByText('Drop files here')).toBeVisible()
|
||||
const fileInput = document.getElementsByClassName('uppy-Dashboard-input')[0]
|
||||
const fileInput = document.querySelector(
|
||||
'.uppy-Dashboard-input',
|
||||
) as HTMLInputElement
|
||||
await userEvent.upload(fileInput, new File(['Hello, World!'], 'test.txt'))
|
||||
await expect.element(page.getByText('test.txt')).toBeVisible()
|
||||
await page.getByTitle('Edit file test.txt').click()
|
||||
|
|
@ -31,3 +35,172 @@ test('Basic Dashboard functionality works in the browser', async () => {
|
|||
await userEvent.fill(licenseInput.element(), 'MIT')
|
||||
await page.getByText('Save changes').click()
|
||||
})
|
||||
|
||||
test('Upload, pause, and resume functionality', async () => {
|
||||
render('<div id="uppy"></div>')
|
||||
|
||||
let uploadResolve: (() => void) | null = null
|
||||
let uploadPromise = new Promise<void>((resolve) => {
|
||||
uploadResolve = resolve
|
||||
})
|
||||
|
||||
let isPaused = false
|
||||
let progress = 0
|
||||
|
||||
const uppy = new Uppy({
|
||||
// Enable resumable uploads capability for pause/resume functionality
|
||||
restrictions: { maxNumberOfFiles: 1 },
|
||||
}).use(Dashboard, {
|
||||
target: '#uppy',
|
||||
inline: true,
|
||||
hideProgressAfterFinish: false, // Keep StatusBar visible after completion
|
||||
})
|
||||
|
||||
uppy.addUploader(async (fileIDs) => {
|
||||
const files = fileIDs.map((id) => uppy.getFile(id))
|
||||
|
||||
// Emit upload-start event
|
||||
uppy.emit('upload-start', files)
|
||||
|
||||
// Set initial progress state for all files
|
||||
fileIDs.forEach((fileID) => {
|
||||
const file = uppy.getFile(fileID)
|
||||
if (file) {
|
||||
uppy.setFileState(fileID, {
|
||||
progress: {
|
||||
...file.progress,
|
||||
uploadStarted: null,
|
||||
uploadComplete: false,
|
||||
percentage: 0,
|
||||
bytesUploaded: false,
|
||||
bytesTotal: file.size,
|
||||
},
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
// Simulate upload progress with pause/resume support
|
||||
const progressInterval = setInterval(() => {
|
||||
fileIDs.forEach((fileID) => {
|
||||
const file = uppy.getFile(fileID)
|
||||
if (!file) {
|
||||
clearInterval(progressInterval)
|
||||
return
|
||||
}
|
||||
|
||||
// Check if file is paused
|
||||
if (file.isPaused) {
|
||||
isPaused = true
|
||||
return
|
||||
} else if (isPaused) {
|
||||
// Resuming from pause
|
||||
isPaused = false
|
||||
}
|
||||
|
||||
// Only progress if not paused
|
||||
if (!isPaused && progress < 100) {
|
||||
progress += 10
|
||||
|
||||
// Set uploadStarted on first progress update
|
||||
const uploadStarted = file.progress.uploadStarted || Date.now()
|
||||
|
||||
uppy.setFileState(fileID, {
|
||||
progress: {
|
||||
...file.progress,
|
||||
uploadStarted,
|
||||
uploadComplete: false,
|
||||
percentage: progress,
|
||||
bytesUploaded: (progress / 100) * (file.size || 0),
|
||||
bytesTotal: file.size || 0,
|
||||
},
|
||||
})
|
||||
|
||||
// Emit upload-progress event
|
||||
uppy.emit('upload-progress', file, {
|
||||
uploadStarted,
|
||||
bytesUploaded: (progress / 100) * (file.size || 0),
|
||||
bytesTotal: file.size || 0,
|
||||
})
|
||||
|
||||
if (progress >= 100) {
|
||||
clearInterval(progressInterval)
|
||||
|
||||
// Set final progress state
|
||||
uppy.setFileState(fileID, {
|
||||
progress: {
|
||||
...file.progress,
|
||||
uploadComplete: true,
|
||||
percentage: 100,
|
||||
bytesUploaded: (progress / 100) * (file.size || 0),
|
||||
bytesTotal: file.size || 0,
|
||||
} as any, // Type assertion to bypass strict union type checking
|
||||
})
|
||||
|
||||
// Emit upload-success event
|
||||
uppy.emit('upload-success', file, {
|
||||
status: 200,
|
||||
uploadURL: `https://example.com/upload/${file.name}`,
|
||||
})
|
||||
|
||||
uploadResolve?.()
|
||||
}
|
||||
}
|
||||
})
|
||||
}, 200) // 200ms intervals for controlled testing
|
||||
|
||||
return uploadPromise
|
||||
})
|
||||
|
||||
// Set resumable uploads capability
|
||||
uppy.setState({
|
||||
capabilities: {
|
||||
...uppy.getState().capabilities,
|
||||
resumableUploads: true,
|
||||
},
|
||||
})
|
||||
|
||||
const fileInput = document.querySelector(
|
||||
'.uppy-Dashboard-input',
|
||||
) as HTMLInputElement
|
||||
await userEvent.upload(
|
||||
fileInput,
|
||||
new File(['a'.repeat(50000)], 'test.txt'), // 50KB file
|
||||
)
|
||||
|
||||
// Wait for file to be added and upload button to appear
|
||||
await expect.element(page.getByText('test.txt')).toBeVisible()
|
||||
await expect(
|
||||
page.getByRole('button', { name: 'Upload 1 file' }),
|
||||
).toBeInTheDocument()
|
||||
|
||||
// Start the upload
|
||||
await page.getByRole('button', { name: 'Upload 1 file' }).click()
|
||||
|
||||
// Wait for upload to start
|
||||
await new Promise((resolve) => setTimeout(resolve, 300))
|
||||
|
||||
// Verify upload has started by checking StatusBar state
|
||||
await expect(page.getByText(/Uploading: \d+%/)).toBeVisible()
|
||||
|
||||
// Find and click pause button
|
||||
const pauseButton = page.getByTitle('Pause', { exact: true })
|
||||
await expect(pauseButton).toBeVisible()
|
||||
await pauseButton.click()
|
||||
|
||||
// Find and click resume button
|
||||
const resumeButton = page.getByTitle('Resume', { exact: true })
|
||||
await expect(resumeButton).toBeVisible()
|
||||
await resumeButton.click()
|
||||
|
||||
// Verify upload has resumed and is progressing
|
||||
await expect(page.getByText(/Uploading: \d+%/)).toBeVisible()
|
||||
|
||||
// Wait for upload to complete - increase timeout
|
||||
await uploadPromise
|
||||
|
||||
// Add a longer delay to allow StatusBar to render final state
|
||||
await new Promise((resolve) => setTimeout(resolve, 500))
|
||||
|
||||
// Verify upload completion state using Playwright selector
|
||||
await expect(page.getByText('Complete', { exact: true })).toBeVisible()
|
||||
})
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
import Core, { type UIPlugin } from '@uppy/core'
|
||||
import GoogleDrivePlugin from '@uppy/google-drive'
|
||||
import StatusBarPlugin from '@uppy/status-bar'
|
||||
// @ts-ignore untyped
|
||||
import Url from '@uppy/url'
|
||||
// @ts-ignore untyped
|
||||
|
|
@ -22,17 +21,6 @@ describe('Dashboard', () => {
|
|||
delete globalThis.ResizeObserver
|
||||
})
|
||||
|
||||
it('can safely be added together with the StatusBar without id conflicts', () => {
|
||||
const core = new Core()
|
||||
core.use(StatusBarPlugin)
|
||||
|
||||
expect(() => {
|
||||
core.use(DashboardPlugin, { inline: false })
|
||||
}).not.toThrow()
|
||||
|
||||
core.destroy()
|
||||
})
|
||||
|
||||
it('works without any remote provider plugins', () => {
|
||||
const core = new Core()
|
||||
|
||||
|
|
@ -87,7 +75,7 @@ describe('Dashboard', () => {
|
|||
.targets as UIPlugin<any, any, any>[]
|
||||
|
||||
// two built-in plugins + these ones below
|
||||
expect(dashboardPlugins.length).toEqual(3)
|
||||
expect(dashboardPlugins.length).toEqual(2)
|
||||
expect(dashboardPlugins.some((plugin) => plugin.id === 'Url')).toEqual(true)
|
||||
expect(dashboardPlugins.some((plugin) => plugin.id === 'Webcam')).toEqual(
|
||||
true,
|
||||
|
|
@ -107,7 +95,7 @@ describe('Dashboard', () => {
|
|||
.targets as UIPlugin<any, any, any>[]
|
||||
|
||||
// two built-in plugins + these ones below
|
||||
expect(dashboardPlugins.length).toEqual(2)
|
||||
expect(dashboardPlugins.length).toEqual(1)
|
||||
expect(dashboardPlugins.some((plugin) => plugin.id === 'Url')).toEqual(true)
|
||||
expect(dashboardPlugins.some((plugin) => plugin.id === 'Webcam')).toEqual(
|
||||
false,
|
||||
|
|
|
|||
|
|
@ -90,5 +90,50 @@ export default {
|
|||
// Used for native device camera buttons on mobile
|
||||
takePictureBtn: 'Take Picture',
|
||||
recordVideoBtn: 'Record Video',
|
||||
|
||||
// Strings for StatusBar
|
||||
// Shown in the status bar while files are being uploaded.
|
||||
uploading: 'Uploading',
|
||||
// Shown in the status bar once all files have been uploaded.
|
||||
complete: 'Complete',
|
||||
// Shown in the status bar if an upload failed.
|
||||
uploadFailed: 'Upload failed',
|
||||
// Shown in the status bar while the upload is paused.
|
||||
paused: 'Paused',
|
||||
// Used as the label for the button that retries an upload.
|
||||
retry: 'Retry',
|
||||
// Used as the label for the button that pauses an upload.
|
||||
pause: 'Pause',
|
||||
// Used as the label for the button that resumes an upload.
|
||||
resume: 'Resume',
|
||||
// Used as the label for the button that resets the upload state after an upload
|
||||
done: 'Done',
|
||||
// When `hideProgressDetails` is set to false, shows the number of files that have been fully uploaded so far.
|
||||
filesUploadedOfTotal: {
|
||||
0: '%{complete} of %{smart_count} file uploaded',
|
||||
1: '%{complete} of %{smart_count} files uploaded',
|
||||
},
|
||||
// When `hideProgressDetails` is set to false, shows the amount of bytes that have been uploaded so far.
|
||||
dataUploadedOfTotal: '%{complete} of %{total}',
|
||||
dataUploadedOfUnknown: '%{complete} of unknown',
|
||||
// When `hideProgressDetails` is set to false, shows an estimation of how long the upload will take to complete.
|
||||
xTimeLeft: '%{time} left',
|
||||
// Used as the label for the button that starts an upload.
|
||||
uploadXFiles: {
|
||||
0: 'Upload %{smart_count} file',
|
||||
1: 'Upload %{smart_count} files',
|
||||
},
|
||||
// Used as the label for the button that starts an upload, if another upload has been started in the past
|
||||
// and new files were added later.
|
||||
uploadXNewFiles: {
|
||||
0: 'Upload +%{smart_count} file',
|
||||
1: 'Upload +%{smart_count} files',
|
||||
},
|
||||
upload: 'Upload',
|
||||
xMoreFilesAdded: {
|
||||
0: '%{smart_count} more file added',
|
||||
1: '%{smart_count} more files added',
|
||||
},
|
||||
showErrorDetails: 'Show error details',
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
@use "sass:color";
|
||||
@use '@uppy/core/src/_utils.scss';
|
||||
@use '@uppy/core/src/_variables.scss';
|
||||
@use '@uppy/status-bar/src/style.scss' as style2;
|
||||
@use '@uppy/provider-views/src/style.scss' as style3;
|
||||
|
||||
// Component-specific css imports
|
||||
@use 'components/FileItem/index.scss';
|
||||
@use 'components/FileCard/index.scss' as index2;
|
||||
@use 'components/Informer/style.scss';
|
||||
@use 'components/StatusBar/style.scss' as style2;
|
||||
|
||||
// Transitions //
|
||||
|
||||
|
|
|
|||
|
|
@ -10,9 +10,6 @@
|
|||
{
|
||||
"path": "../provider-views/tsconfig.build.json"
|
||||
},
|
||||
{
|
||||
"path": "../status-bar/tsconfig.build.json"
|
||||
},
|
||||
{
|
||||
"path": "../thumbnail-generator/tsconfig.build.json"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -9,9 +9,6 @@
|
|||
{
|
||||
"path": "../provider-views/tsconfig.build.json"
|
||||
},
|
||||
{
|
||||
"path": "../status-bar/tsconfig.build.json"
|
||||
},
|
||||
{
|
||||
"path": "../thumbnail-generator/tsconfig.build.json"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -23,7 +23,6 @@
|
|||
"@uppy/dropbox#build",
|
||||
"@uppy/webdav#build",
|
||||
"@uppy/unsplash#build",
|
||||
"@uppy/status-bar#build",
|
||||
"@uppy/webcam#build",
|
||||
"@uppy/instagram#build"
|
||||
],
|
||||
|
|
|
|||
|
|
@ -1,88 +0,0 @@
|
|||
import type { Body, Meta, UnknownPlugin, Uppy } from '@uppy/core'
|
||||
import StatusBarPlugin, { type StatusBarOptions } from '@uppy/status-bar'
|
||||
import { Component, createElement as h } from 'react'
|
||||
import getHTMLProps from './getHTMLProps.js'
|
||||
import nonHtmlPropsHaveChanged from './nonHtmlPropsHaveChanged.js'
|
||||
|
||||
interface StatusBarProps<M extends Meta, B extends Body>
|
||||
extends StatusBarOptions {
|
||||
uppy: Uppy<M, B>
|
||||
}
|
||||
|
||||
/**
|
||||
* React component that renders a status bar containing upload progress and speed,
|
||||
* processing progress and pause/resume/cancel controls.
|
||||
*/
|
||||
|
||||
class StatusBar<M extends Meta, B extends Body> extends Component<
|
||||
StatusBarProps<M, B>
|
||||
> {
|
||||
private container!: HTMLElement
|
||||
|
||||
private plugin!: UnknownPlugin<M, B>
|
||||
|
||||
componentDidMount(): void {
|
||||
this.installPlugin()
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps: StatusBar<M, B>['props']): void {
|
||||
if (prevProps.uppy !== this.props.uppy) {
|
||||
this.uninstallPlugin(prevProps)
|
||||
this.installPlugin()
|
||||
} else if (nonHtmlPropsHaveChanged(this.props, prevProps)) {
|
||||
const { uppy, ...options } = { ...this.props, target: this.container }
|
||||
this.plugin.setOptions(options)
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount(): void {
|
||||
this.uninstallPlugin()
|
||||
}
|
||||
|
||||
installPlugin(): void {
|
||||
const {
|
||||
uppy,
|
||||
hideUploadButton,
|
||||
hideRetryButton,
|
||||
hidePauseResumeButton,
|
||||
hideCancelButton,
|
||||
showProgressDetails,
|
||||
hideAfterFinish,
|
||||
doneButtonHandler,
|
||||
id,
|
||||
} = this.props
|
||||
const options = {
|
||||
id: id || 'StatusBar',
|
||||
hideUploadButton,
|
||||
hideRetryButton,
|
||||
hidePauseResumeButton,
|
||||
hideCancelButton,
|
||||
showProgressDetails,
|
||||
hideAfterFinish,
|
||||
doneButtonHandler,
|
||||
target: this.container,
|
||||
}
|
||||
|
||||
uppy.use(StatusBarPlugin, options)
|
||||
|
||||
this.plugin = uppy.getPlugin(options.id)!
|
||||
}
|
||||
|
||||
uninstallPlugin(props = this.props): void {
|
||||
const { uppy } = props
|
||||
|
||||
uppy.removePlugin(this.plugin)
|
||||
}
|
||||
|
||||
render() {
|
||||
return h('div', {
|
||||
className: 'uppy-Container',
|
||||
ref: (container: HTMLElement) => {
|
||||
this.container = container
|
||||
},
|
||||
...getHTMLProps(this.props),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export default StatusBar
|
||||
|
|
@ -6,7 +6,6 @@ export {
|
|||
UppyContext,
|
||||
UppyContextProvider,
|
||||
} from './headless/UppyContextProvider.js'
|
||||
export { default as StatusBar } from './StatusBar.js'
|
||||
export { useDropzone } from './useDropzone.js'
|
||||
export { useFileInput } from './useFileInput.js'
|
||||
export { useRemoteSource } from './useRemoteSource.js'
|
||||
|
|
|
|||
|
|
@ -21,9 +21,6 @@
|
|||
{
|
||||
"path": "../dashboard/tsconfig.build.json"
|
||||
},
|
||||
{
|
||||
"path": "../status-bar/tsconfig.build.json"
|
||||
},
|
||||
{
|
||||
"path": "../screen-capture/tsconfig.build.json"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -20,9 +20,6 @@
|
|||
{
|
||||
"path": "../dashboard/tsconfig.build.json"
|
||||
},
|
||||
{
|
||||
"path": "../status-bar/tsconfig.build.json"
|
||||
},
|
||||
{
|
||||
"path": "../screen-capture/tsconfig.build.json"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1 +0,0 @@
|
|||
tsconfig.*
|
||||
|
|
@ -1,187 +0,0 @@
|
|||
# @uppy/status-bar
|
||||
|
||||
## 4.1.2
|
||||
|
||||
Released: 2025-02-25
|
||||
Included in: Uppy v4.13.3
|
||||
|
||||
- @uppy/status-bar: fix aria-hidden warning (Merlijn Vos / #5663)
|
||||
|
||||
## 4.1.1
|
||||
|
||||
Released: 2025-01-09
|
||||
Included in: Uppy v4.12.2
|
||||
|
||||
- @uppy/status-bar: fix double upload progress (Mikael Finstad / #5587)
|
||||
|
||||
## 4.1.0
|
||||
|
||||
Released: 2025-01-06
|
||||
Included in: Uppy v4.11.0
|
||||
|
||||
- @uppy/angular,@uppy/audio,@uppy/aws-s3,@uppy/box,@uppy/companion-client,@uppy/compressor,@uppy/core,@uppy/dashboard,@uppy/drag-drop,@uppy/drop-target,@uppy/dropbox,@uppy/facebook,@uppy/file-input,@uppy/form,@uppy/golden-retriever,@uppy/google-drive-picker,@uppy/google-drive,@uppy/google-photos-picker,@uppy/google-photos,@uppy/image-editor,@uppy/informer,@uppy/instagram,@uppy/locales,@uppy/onedrive,@uppy/progress-bar,@uppy/provider-views,@uppy/react,@uppy/remote-sources,@uppy/screen-capture,@uppy/status-bar,@uppy/thumbnail-generator,@uppy/transloadit,@uppy/tus,@uppy/unsplash,@uppy/url,@uppy/vue,@uppy/webcam,@uppy/webdav,@uppy/xhr-upload,@uppy/zoom: Remove "paths" from all tsconfig's (Merlijn Vos / #5572)
|
||||
|
||||
## 4.0.6
|
||||
|
||||
Released: 2024-12-17
|
||||
Included in: Uppy v4.9.0
|
||||
|
||||
- e2e,@uppy/status-bar,@uppy/utils: Companion stream upload unknown size files (Mikael Finstad / #5489)
|
||||
|
||||
## 4.0.5
|
||||
|
||||
Released: 2024-12-05
|
||||
Included in: Uppy v4.8.0
|
||||
|
||||
- @uppy/audio,@uppy/aws-s3,@uppy/box,@uppy/companion-client,@uppy/compressor,@uppy/core,@uppy/dashboard,@uppy/drag-drop,@uppy/drop-target,@uppy/dropbox,@uppy/facebook,@uppy/file-input,@uppy/form,@uppy/golden-retriever,@uppy/google-drive,@uppy/google-photos,@uppy/image-editor,@uppy/informer,@uppy/instagram,@uppy/locales,@uppy/onedrive,@uppy/progress-bar,@uppy/provider-views,@uppy/react,@uppy/remote-sources,@uppy/screen-capture,@uppy/status-bar,@uppy/store-default,@uppy/thumbnail-generator,@uppy/transloadit,@uppy/tus,@uppy/unsplash,@uppy/url,@uppy/utils,@uppy/vue,@uppy/webcam,@uppy/xhr-upload,@uppy/zoom: cleanup tsconfig (Mikael Finstad / #5520)
|
||||
|
||||
## 4.0.4
|
||||
|
||||
Released: 2024-10-31
|
||||
Included in: Uppy v4.6.0
|
||||
|
||||
- @uppy/aws-s3,@uppy/box,@uppy/companion-client,@uppy/core,@uppy/dashboard,@uppy/drag-drop,@uppy/dropbox,@uppy/facebook,@uppy/file-input,@uppy/form,@uppy/golden-retriever,@uppy/google-drive,@uppy/google-photos,@uppy/image-editor,@uppy/informer,@uppy/instagram,@uppy/locales,@uppy/onedrive,@uppy/progress-bar,@uppy/provider-views,@uppy/react-native,@uppy/react,@uppy/redux-dev-tools,@uppy/screen-capture,@uppy/status-bar,@uppy/store-default,@uppy/store-redux,@uppy/svelte,@uppy/thumbnail-generator,@uppy/transloadit,@uppy/tus,@uppy/unsplash,@uppy/url,@uppy/utils,@uppy/vue,@uppy/webcam,@uppy/xhr-upload,@uppy/zoom: Fix links (Anthony Veaudry / #5492)
|
||||
|
||||
## 4.0.3
|
||||
|
||||
Released: 2024-08-20
|
||||
Included in: Uppy v4.2.0
|
||||
|
||||
- @uppy/status-bar: show upload button when files are recovered (Merlijn Vos / #5418)
|
||||
|
||||
## 4.0.1
|
||||
|
||||
Released: 2024-07-30
|
||||
Included in: Uppy v4.1.0
|
||||
|
||||
- @uppy/status-bar: GoldenRetriever + `hideUploadButton=true` (Evgenia Karunus / #5350)
|
||||
|
||||
## 4.0.0-beta.9
|
||||
|
||||
Released: 2024-06-04
|
||||
Included in: Uppy v4.0.0-beta.10
|
||||
|
||||
- @uppy/status-bar: remove unused component props (Antoine du Hamel / #5211)
|
||||
- @uppy/status-bar: rename `StatusBar` to `StatusBarUI` (Mikael Finstad / #5200)
|
||||
|
||||
## 4.0.0-beta.1
|
||||
|
||||
Released: 2024-03-28
|
||||
Included in: Uppy v4.0.0-beta.1
|
||||
|
||||
- @uppy/companion-client,@uppy/provider-views,@uppy/status-bar: fix type imports (Antoine du Hamel / #5038)
|
||||
- @uppy/status-bar: remove default target (Antoine du Hamel / #4970)
|
||||
- @uppy/status-bar: refine type of private variables (Antoine du Hamel / #5025)
|
||||
- @uppy/status-bar: fix `recoveredState` type (Antoine du Hamel / #4996)
|
||||
|
||||
## 3.3.3
|
||||
|
||||
Released: 2024-05-07
|
||||
Included in: Uppy v3.25.2
|
||||
|
||||
- @uppy/compressor,@uppy/core,@uppy/dashboard,@uppy/status-bar: Upgrade @transloadit/prettier-bytes (Merlijn Vos / #5150)
|
||||
|
||||
## 3.3.1
|
||||
|
||||
Released: 2024-03-27
|
||||
Included in: Uppy v3.24.0
|
||||
|
||||
- @uppy/box,@uppy/companion-client,@uppy/provider-views,@uppy/status-bar: fix type imports (Antoine du Hamel / #5038)
|
||||
- @uppy/status-bar: refine type of private variables (Antoine du Hamel / #5025)
|
||||
- @uppy/status-bar: fix `recoveredState` type (Antoine du Hamel / #4996)
|
||||
|
||||
## 3.2.7
|
||||
|
||||
Released: 2024-02-20
|
||||
Included in: Uppy v3.22.1
|
||||
|
||||
- @uppy/compressor,@uppy/core,@uppy/dashboard,@uppy/status-bar: bump `@transloadit/prettier-bytes` (Antoine du Hamel / #4933)
|
||||
|
||||
## 3.2.6
|
||||
|
||||
Released: 2024-02-19
|
||||
Included in: Uppy v3.22.0
|
||||
|
||||
- @uppy/status-bar: fix `statusbaroptions` type (antoine du hamel / #4883)
|
||||
- @uppy/status-bar: refactor to typescript (antoine du hamel / #4839)
|
||||
|
||||
## 3.2.4
|
||||
|
||||
Released: 2023-08-15
|
||||
Included in: Uppy v3.14.0
|
||||
|
||||
- @uppy/status-bar: e2e: add test for retrying and pausing uploads (Antoine du Hamel / #3599)
|
||||
|
||||
## 3.2.3
|
||||
|
||||
Released: 2023-07-20
|
||||
Included in: Uppy v3.13.0
|
||||
|
||||
- @uppy/status-bar: fix ETA when status bar is installed during upload (Antoine du Hamel / #4588)
|
||||
|
||||
## 3.2.2
|
||||
|
||||
Released: 2023-07-13
|
||||
Included in: Uppy v3.12.0
|
||||
|
||||
- @uppy/status-bar: listen to `upload` event instead of button click (Antoine du Hamel / #4563)
|
||||
|
||||
## 3.2.1
|
||||
|
||||
Released: 2023-07-06
|
||||
Included in: Uppy v3.11.0
|
||||
|
||||
- @uppy/status-bar: remove throttled component (Artur Paikin / #4396)
|
||||
- @uppy/status-bar: fix ETA when Uppy recovers its state (Antoine du Hamel / #4525)
|
||||
|
||||
## 3.2.0
|
||||
|
||||
Released: 2023-06-19
|
||||
Included in: Uppy v3.10.0
|
||||
|
||||
- @uppy/companion,@uppy/core,@uppy/dashboard,@uppy/golden-retriever,@uppy/status-bar,@uppy/utils: Migrate all lodash' per-method-packages usage to lodash. (LinusMain / #4274)
|
||||
- @uppy/status-bar: Filtered ETA (stduhpf / #4458)
|
||||
|
||||
## 3.0.1
|
||||
|
||||
Released: 2022-09-25
|
||||
Included in: Uppy v3.1.0
|
||||
|
||||
- @uppy/audio,@uppy/aws-s3-multipart,@uppy/aws-s3,@uppy/box,@uppy/companion-client,@uppy/companion,@uppy/compressor,@uppy/core,@uppy/dashboard,@uppy/drag-drop,@uppy/drop-target,@uppy/dropbox,@uppy/facebook,@uppy/file-input,@uppy/form,@uppy/golden-retriever,@uppy/google-drive,@uppy/image-editor,@uppy/informer,@uppy/instagram,@uppy/locales,@uppy/onedrive,@uppy/progress-bar,@uppy/provider-views,@uppy/react,@uppy/redux-dev-tools,@uppy/remote-sources,@uppy/screen-capture,@uppy/status-bar,@uppy/store-default,@uppy/store-redux,@uppy/svelte,@uppy/thumbnail-generator,@uppy/transloadit,@uppy/tus,@uppy/unsplash,@uppy/url,@uppy/utils,@uppy/vue,@uppy/webcam,@uppy/xhr-upload,@uppy/zoom: add missing entries to changelog for individual packages (Antoine du Hamel / #4092)
|
||||
|
||||
## 3.0.0
|
||||
|
||||
Released: 2022-08-22
|
||||
Included in: Uppy v3.0.0
|
||||
|
||||
- @uppy/core,@uppy/dashboard,@uppy/status-bar: Style tweaks: use all: initial + other resets (Artur Paikin / #3983)
|
||||
- Switch to ESM
|
||||
|
||||
## 3.0.0-beta.2
|
||||
|
||||
Released: 2022-08-03
|
||||
Included in: Uppy v3.0.0-beta.4
|
||||
|
||||
- @uppy/status-bar: rename internal modules (Antoine du Hamel / #3929)
|
||||
|
||||
## 2.2.1
|
||||
|
||||
Released: 2022-05-30
|
||||
Included in: Uppy v2.11.0
|
||||
|
||||
- @uppy/angular,@uppy/audio,@uppy/aws-s3-multipart,@uppy/aws-s3,@uppy/box,@uppy/core,@uppy/dashboard,@uppy/drag-drop,@uppy/dropbox,@uppy/facebook,@uppy/file-input,@uppy/form,@uppy/golden-retriever,@uppy/google-drive,@uppy/image-editor,@uppy/informer,@uppy/instagram,@uppy/onedrive,@uppy/progress-bar,@uppy/react,@uppy/redux-dev-tools,@uppy/robodog,@uppy/screen-capture,@uppy/status-bar,@uppy/store-default,@uppy/store-redux,@uppy/thumbnail-generator,@uppy/transloadit,@uppy/tus,@uppy/unsplash,@uppy/url,@uppy/vue,@uppy/webcam,@uppy/xhr-upload,@uppy/zoom: doc: update bundler recommendation (Antoine du Hamel / #3763)
|
||||
|
||||
## 2.2.0
|
||||
|
||||
Released: 2022-05-14
|
||||
Included in: Uppy v2.10.0
|
||||
|
||||
- @uppy/status-bar: refactor to ESM (Antoine du Hamel / #3697)
|
||||
|
||||
## 2.1.2
|
||||
|
||||
Released: 2021-12-07
|
||||
Included in: Uppy v2.3.0
|
||||
|
||||
- @uppy/status-bar: Status bar error state improvements (Merlijn Vos / #3299)
|
||||
- @uppy/aws-s3,@uppy/box,@uppy/core,@uppy/dashboard,@uppy/drag-drop,@uppy/dropbox,@uppy/facebook,@uppy/file-input,@uppy/google-drive,@uppy/image-editor,@uppy/instagram,@uppy/locales,@uppy/onedrive,@uppy/screen-capture,@uppy/status-bar,@uppy/thumbnail-generator,@uppy/transloadit,@uppy/url,@uppy/webcam,@uppy/xhr-upload,@uppy/zoom: Refactor locale scripts & generate types and docs (Merlijn Vos / #3276)
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2018 Transloadit
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
|
@ -1,54 +0,0 @@
|
|||
# @uppy/status-bar
|
||||
|
||||
<img src="https://uppy.io/img/logo.svg" width="120" alt="Uppy logo: a smiling puppy above a pink upwards arrow" align="right">
|
||||
|
||||
[](https://www.npmjs.com/package/@uppy/status-bar)
|
||||

|
||||

|
||||

|
||||
|
||||
The status-bar shows upload progress and speed, ETAs, pre- and post-processing
|
||||
information, and allows users to control (pause/resume/cancel) the upload. Best
|
||||
used together with a basic file source plugin, such as
|
||||
[@uppy/file-input](https://uppy.io/docs/file-input) or
|
||||
[@uppy/drag-drop](https://uppy.io/docs/drag-drop), or a custom implementation.
|
||||
It’s also included in the [@uppy/dashboard](https://uppy.io/docs/dashboard)
|
||||
plugin.
|
||||
|
||||
Uppy is being developed by the folks at [Transloadit](https://transloadit.com),
|
||||
a versatile file encoding service.
|
||||
|
||||
## Example
|
||||
|
||||
```js
|
||||
import Uppy from '@uppy/core'
|
||||
import StatusBar from '@uppy/status-bar'
|
||||
|
||||
const uppy = new Uppy()
|
||||
uppy.use(StatusBar, {
|
||||
target: 'body',
|
||||
hideUploadButton: false,
|
||||
showProgressDetails: false,
|
||||
hideAfterFinish: true,
|
||||
})
|
||||
```
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
$ npm install @uppy/status-bar
|
||||
```
|
||||
|
||||
Alternatively, you can also use this plugin in a pre-built bundle from
|
||||
Transloadit’s CDN: Smart CDN. In that case `Uppy` will attach itself to the
|
||||
global `window.Uppy` object. See the
|
||||
[main Uppy documentation](https://uppy.io/docs/#Installation) for instructions.
|
||||
|
||||
## Documentation
|
||||
|
||||
Documentation for this plugin can be found on the
|
||||
[Uppy website](https://uppy.io/docs/status-bar).
|
||||
|
||||
## License
|
||||
|
||||
[The MIT License](./LICENSE).
|
||||
|
|
@ -1,52 +0,0 @@
|
|||
{
|
||||
"name": "@uppy/status-bar",
|
||||
"description": "A progress bar for Uppy, with many bells and whistles.",
|
||||
"version": "4.1.3",
|
||||
"license": "MIT",
|
||||
"main": "lib/index.js",
|
||||
"style": "dist/style.min.css",
|
||||
"type": "module",
|
||||
"sideEffects": [
|
||||
"*.css"
|
||||
],
|
||||
"scripts": {
|
||||
"build": "tsc --build tsconfig.build.json",
|
||||
"build:css": "sass --load-path=../../ src/style.scss dist/style.css && postcss dist/style.css -u cssnano -o dist/style.min.css",
|
||||
"typecheck": "tsc --build"
|
||||
},
|
||||
"keywords": [
|
||||
"file uploader",
|
||||
"uppy",
|
||||
"uppy-plugin",
|
||||
"progress bar",
|
||||
"status bar",
|
||||
"progress",
|
||||
"upload",
|
||||
"eta",
|
||||
"speed"
|
||||
],
|
||||
"homepage": "https://uppy.io",
|
||||
"bugs": {
|
||||
"url": "https://github.com/transloadit/uppy/issues"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/transloadit/uppy.git"
|
||||
},
|
||||
"dependencies": {
|
||||
"@transloadit/prettier-bytes": "^0.3.4",
|
||||
"@uppy/utils": "workspace:^",
|
||||
"classnames": "^2.2.6",
|
||||
"preact": "^10.5.13"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@uppy/core": "workspace:^"
|
||||
},
|
||||
"devDependencies": {
|
||||
"cssnano": "^7.0.7",
|
||||
"postcss": "^8.5.6",
|
||||
"postcss-cli": "^11.0.1",
|
||||
"sass": "^1.89.2",
|
||||
"typescript": "^5.8.3"
|
||||
}
|
||||
}
|
||||
|
|
@ -1,50 +0,0 @@
|
|||
export default {
|
||||
strings: {
|
||||
// Shown in the status bar while files are being uploaded.
|
||||
uploading: 'Uploading',
|
||||
// Shown in the status bar once all files have been uploaded.
|
||||
complete: 'Complete',
|
||||
// Shown in the status bar if an upload failed.
|
||||
uploadFailed: 'Upload failed',
|
||||
// Shown in the status bar while the upload is paused.
|
||||
paused: 'Paused',
|
||||
// Used as the label for the button that retries an upload.
|
||||
retry: 'Retry',
|
||||
// Used as the label for the button that cancels an upload.
|
||||
cancel: 'Cancel',
|
||||
// Used as the label for the button that pauses an upload.
|
||||
pause: 'Pause',
|
||||
// Used as the label for the button that resumes an upload.
|
||||
resume: 'Resume',
|
||||
// Used as the label for the button that resets the upload state after an upload
|
||||
done: 'Done',
|
||||
// When `showProgressDetails` is set, shows the number of files that have been fully uploaded so far.
|
||||
filesUploadedOfTotal: {
|
||||
0: '%{complete} of %{smart_count} file uploaded',
|
||||
1: '%{complete} of %{smart_count} files uploaded',
|
||||
},
|
||||
// When `showProgressDetails` is set, shows the amount of bytes that have been uploaded so far.
|
||||
dataUploadedOfTotal: '%{complete} of %{total}',
|
||||
dataUploadedOfUnknown: '%{complete} of unknown',
|
||||
// When `showProgressDetails` is set, shows an estimation of how long the upload will take to complete.
|
||||
xTimeLeft: '%{time} left',
|
||||
// Used as the label for the button that starts an upload.
|
||||
uploadXFiles: {
|
||||
0: 'Upload %{smart_count} file',
|
||||
1: 'Upload %{smart_count} files',
|
||||
},
|
||||
// Used as the label for the button that starts an upload, if another upload has been started in the past
|
||||
// and new files were added later.
|
||||
uploadXNewFiles: {
|
||||
0: 'Upload +%{smart_count} file',
|
||||
1: 'Upload +%{smart_count} files',
|
||||
},
|
||||
upload: 'Upload',
|
||||
retryUpload: 'Retry upload',
|
||||
xMoreFilesAdded: {
|
||||
0: '%{smart_count} more file added',
|
||||
1: '%{smart_count} more files added',
|
||||
},
|
||||
showErrorDetails: 'Show error details',
|
||||
},
|
||||
}
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
{
|
||||
"extends": "../../../tsconfig.shared",
|
||||
"compilerOptions": {
|
||||
"outDir": "./lib",
|
||||
"rootDir": "./src"
|
||||
},
|
||||
"include": ["./src/**/*.*"],
|
||||
"exclude": ["./src/**/*.test.ts"],
|
||||
"references": [
|
||||
{
|
||||
"path": "../utils/tsconfig.build.json"
|
||||
},
|
||||
{
|
||||
"path": "../core/tsconfig.build.json"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
{
|
||||
"extends": "../../../tsconfig.shared",
|
||||
"compilerOptions": {
|
||||
"emitDeclarationOnly": false,
|
||||
"noEmit": true
|
||||
},
|
||||
"include": ["./package.json", "./src/**/*.*"],
|
||||
"references": [
|
||||
{
|
||||
"path": "../utils/tsconfig.build.json"
|
||||
},
|
||||
{
|
||||
"path": "../core/tsconfig.build.json"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
{
|
||||
"extends": ["//"],
|
||||
"tasks": {
|
||||
"build": {
|
||||
"dependsOn": ["^build", "@uppy/core#build"]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -25,10 +25,6 @@
|
|||
"types": "./dist/components/DashboardModal.svelte.d.ts",
|
||||
"svelte": "./dist/components/DashboardModal.svelte"
|
||||
},
|
||||
"./status-bar": {
|
||||
"types": "./dist/components/StatusBar.svelte.d.ts",
|
||||
"svelte": "./dist/components/StatusBar.svelte"
|
||||
},
|
||||
"./package.json": "./package.json"
|
||||
},
|
||||
"homepage": "https://uppy.io",
|
||||
|
|
@ -75,9 +71,6 @@
|
|||
"peerDependenciesMeta": {
|
||||
"@uppy/dashboard": {
|
||||
"optional": true
|
||||
},
|
||||
"@uppy/status-bar": {
|
||||
"optional": true
|
||||
}
|
||||
},
|
||||
"publishConfig": {
|
||||
|
|
|
|||
|
|
@ -1,43 +0,0 @@
|
|||
<script
|
||||
lang="ts"
|
||||
generics="M extends import('@uppy/utils/lib/UppyFile').Meta, B extends import('@uppy/utils/lib/UppyFile').Body"
|
||||
>
|
||||
import type { Uppy } from "@uppy/core";
|
||||
import StatusBarPlugin from "@uppy/status-bar";
|
||||
import { onDestroy, onMount } from "svelte";
|
||||
|
||||
let container: HTMLElement;
|
||||
let plugin: StatusBarPlugin<M, B>;
|
||||
|
||||
export let uppy: Uppy<M, B>;
|
||||
export const props: Object | undefined = {};
|
||||
|
||||
const installPlugin = () => {
|
||||
const options = {
|
||||
id: "svelte:StatusBar",
|
||||
inline: true,
|
||||
...props,
|
||||
target: container,
|
||||
};
|
||||
|
||||
uppy.use(StatusBarPlugin, options);
|
||||
plugin = uppy.getPlugin(options.id) as StatusBarPlugin<M, B>;
|
||||
};
|
||||
const uninstallPlugin = (uppyInstance: Uppy<M, B> = uppy) => {
|
||||
if (plugin != null) uppyInstance.removePlugin(plugin);
|
||||
};
|
||||
|
||||
onMount(() => installPlugin());
|
||||
|
||||
onDestroy(() => uninstallPlugin());
|
||||
$: {
|
||||
const options = {
|
||||
id: "svelte:StatusBar",
|
||||
...props,
|
||||
target: container,
|
||||
};
|
||||
uppy.setOptions(options);
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="uppy-Container" bind:this={container}></div>
|
||||
|
|
@ -2,7 +2,6 @@ export * from "./components/headless/generated/index.js";
|
|||
// Headless components
|
||||
export { default as UppyContextProvider } from "./components/headless/UppyContextProvider.svelte";
|
||||
|
||||
|
||||
// Hooks
|
||||
export * from "./useDropzone.js";
|
||||
export * from "./useFileInput.js";
|
||||
|
|
|
|||
|
|
@ -38,9 +38,6 @@
|
|||
"peerDependenciesMeta": {
|
||||
"@uppy/dashboard": {
|
||||
"optional": true
|
||||
},
|
||||
"@uppy/status-bar": {
|
||||
"optional": true
|
||||
}
|
||||
},
|
||||
"publishConfig": {
|
||||
|
|
|
|||
|
|
@ -1,39 +0,0 @@
|
|||
import { Uppy } from '@uppy/core'
|
||||
import StatusBarPlugin, { type StatusBarOptions } from '@uppy/status-bar'
|
||||
import { defineComponent, h, type PropType, ref } from 'vue'
|
||||
import useUppy from './useUppy.js'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'StatusBar',
|
||||
props: {
|
||||
uppy: {
|
||||
type: Object as PropType<Uppy<any, any>>,
|
||||
required: true,
|
||||
},
|
||||
props: {
|
||||
type: Object as PropType<StatusBarOptions>,
|
||||
},
|
||||
},
|
||||
setup(props) {
|
||||
const containerRef = ref<string>()
|
||||
const pluginRef = ref<StatusBarPlugin<any, any>>()
|
||||
const propsRef = ref(props.props)
|
||||
const onMount = () => {
|
||||
const { uppy } = props
|
||||
const options = {
|
||||
id: 'StatusBar',
|
||||
...props.props,
|
||||
target: containerRef.value,
|
||||
}
|
||||
uppy.use(StatusBarPlugin, options)
|
||||
pluginRef.value = uppy.getPlugin(options.id) as StatusBarPlugin<any, any>
|
||||
}
|
||||
|
||||
useUppy(onMount, pluginRef, props.uppy, propsRef)
|
||||
|
||||
return () =>
|
||||
h('div', {
|
||||
ref: containerRef,
|
||||
})
|
||||
},
|
||||
})
|
||||
|
|
@ -15,9 +15,6 @@
|
|||
},
|
||||
{
|
||||
"path": "../dashboard/tsconfig.build.json"
|
||||
},
|
||||
{
|
||||
"path": "../status-bar/tsconfig.build.json"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,9 +14,6 @@
|
|||
},
|
||||
{
|
||||
"path": "../dashboard/tsconfig.build.json"
|
||||
},
|
||||
{
|
||||
"path": "../status-bar/tsconfig.build.json"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -52,7 +52,6 @@
|
|||
"@uppy/provider-views": "workspace:^",
|
||||
"@uppy/remote-sources": "workspace:^",
|
||||
"@uppy/screen-capture": "workspace:^",
|
||||
"@uppy/status-bar": "workspace:^",
|
||||
"@uppy/store-default": "workspace:^",
|
||||
"@uppy/thumbnail-generator": "workspace:^",
|
||||
"@uppy/transloadit": "workspace:^",
|
||||
|
|
|
|||
|
|
@ -38,7 +38,6 @@ export { default as Instagram } from '@uppy/instagram'
|
|||
export { default as OneDrive } from '@uppy/onedrive'
|
||||
export { default as RemoteSources } from '@uppy/remote-sources'
|
||||
export { default as ScreenCapture } from '@uppy/screen-capture'
|
||||
export { default as StatusBar } from '@uppy/status-bar'
|
||||
// Stores
|
||||
export { default as DefaultStore } from '@uppy/store-default'
|
||||
export { default as ThumbnailGenerator } from '@uppy/thumbnail-generator'
|
||||
|
|
|
|||
|
|
@ -20,7 +20,6 @@ export type { InstagramOptions } from '@uppy/instagram'
|
|||
export type { OneDriveOptions } from '@uppy/onedrive'
|
||||
export type { RemoteSourcesOptions } from '@uppy/remote-sources'
|
||||
export type { ScreenCaptureOptions } from '@uppy/screen-capture'
|
||||
export type { StatusBarOptions } from '@uppy/status-bar'
|
||||
export type { ThumbnailGeneratorOptions } from '@uppy/thumbnail-generator'
|
||||
export type { TransloaditOptions } from '@uppy/transloadit'
|
||||
export type { TusOptions } from '@uppy/tus'
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
@use '@uppy/core/src/style.scss' as core;
|
||||
@use '@uppy/dashboard/src/style.scss' as dashboard;
|
||||
@use '@uppy/provider-views/src/style.scss' as provider-views;
|
||||
@use '@uppy/status-bar/src/style.scss' as status-bar;
|
||||
@use '@uppy/url/src/style.scss' as url;
|
||||
@use '@uppy/webcam/src/style.scss' as webcam;
|
||||
@use '@uppy/audio/src/style.scss' as audio;
|
||||
|
|
|
|||
|
|
@ -70,9 +70,6 @@
|
|||
{
|
||||
"path": "../@uppy/screen-capture/tsconfig.build.json"
|
||||
},
|
||||
{
|
||||
"path": "../@uppy/status-bar/tsconfig.build.json"
|
||||
},
|
||||
{
|
||||
"path": "../@uppy/store-default/tsconfig.build.json"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -69,9 +69,6 @@
|
|||
{
|
||||
"path": "../@uppy/screen-capture/tsconfig.build.json"
|
||||
},
|
||||
{
|
||||
"path": "../@uppy/status-bar/tsconfig.build.json"
|
||||
},
|
||||
{
|
||||
"path": "../@uppy/store-default/tsconfig.build.json"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -111,7 +111,7 @@ export default () => {
|
|||
{ id: 'license', name: 'License', placeholder: 'specify license' },
|
||||
{ id: 'caption', name: 'Caption', placeholder: 'add caption' },
|
||||
],
|
||||
showProgressDetails: true,
|
||||
hideProgressDetails: true,
|
||||
proudlyDisplayPoweredByUppy: true,
|
||||
note: `${JSON.stringify(restrictions)}`,
|
||||
})
|
||||
|
|
|
|||
25
yarn.lock
25
yarn.lock
|
|
@ -11477,7 +11477,6 @@ __metadata:
|
|||
"@angular/core": ^17.0.0 || ^18.0.0 || ^19.0.0
|
||||
"@uppy/core": "workspace:^"
|
||||
"@uppy/dashboard": "workspace:^"
|
||||
"@uppy/status-bar": "workspace:^"
|
||||
"@uppy/utils": "workspace:^"
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
|
|
@ -11689,7 +11688,6 @@ __metadata:
|
|||
"@uppy/core": "workspace:^"
|
||||
"@uppy/google-drive": "workspace:^"
|
||||
"@uppy/provider-views": "workspace:^"
|
||||
"@uppy/status-bar": "workspace:^"
|
||||
"@uppy/thumbnail-generator": "workspace:^"
|
||||
"@uppy/url": "workspace:^"
|
||||
"@uppy/utils": "workspace:^"
|
||||
|
|
@ -12005,24 +12003,6 @@ __metadata:
|
|||
languageName: unknown
|
||||
linkType: soft
|
||||
|
||||
"@uppy/status-bar@workspace:^, @uppy/status-bar@workspace:packages/@uppy/status-bar":
|
||||
version: 0.0.0-use.local
|
||||
resolution: "@uppy/status-bar@workspace:packages/@uppy/status-bar"
|
||||
dependencies:
|
||||
"@transloadit/prettier-bytes": "npm:^0.3.4"
|
||||
"@uppy/utils": "workspace:^"
|
||||
classnames: "npm:^2.2.6"
|
||||
cssnano: "npm:^7.0.7"
|
||||
postcss: "npm:^8.5.6"
|
||||
postcss-cli: "npm:^11.0.1"
|
||||
preact: "npm:^10.5.13"
|
||||
sass: "npm:^1.89.2"
|
||||
typescript: "npm:^5.8.3"
|
||||
peerDependencies:
|
||||
"@uppy/core": "workspace:^"
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
|
||||
"@uppy/store-default@workspace:^, @uppy/store-default@workspace:packages/@uppy/store-default":
|
||||
version: 0.0.0-use.local
|
||||
resolution: "@uppy/store-default@workspace:packages/@uppy/store-default"
|
||||
|
|
@ -12056,8 +12036,6 @@ __metadata:
|
|||
peerDependenciesMeta:
|
||||
"@uppy/dashboard":
|
||||
optional: true
|
||||
"@uppy/status-bar":
|
||||
optional: true
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
|
||||
|
|
@ -12178,8 +12156,6 @@ __metadata:
|
|||
peerDependenciesMeta:
|
||||
"@uppy/dashboard":
|
||||
optional: true
|
||||
"@uppy/status-bar":
|
||||
optional: true
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
|
||||
|
|
@ -31074,7 +31050,6 @@ __metadata:
|
|||
"@uppy/provider-views": "workspace:^"
|
||||
"@uppy/remote-sources": "workspace:^"
|
||||
"@uppy/screen-capture": "workspace:^"
|
||||
"@uppy/status-bar": "workspace:^"
|
||||
"@uppy/store-default": "workspace:^"
|
||||
"@uppy/thumbnail-generator": "workspace:^"
|
||||
"@uppy/transloadit": "workspace:^"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue