diff --git a/.changeset/happy-pianos-boil.md b/.changeset/happy-pianos-boil.md
new file mode 100644
index 000000000..118408854
--- /dev/null
+++ b/.changeset/happy-pianos-boil.md
@@ -0,0 +1,8 @@
+---
+"@uppy/angular": minor
+"@uppy/svelte": minor
+"@uppy/react": minor
+"@uppy/vue": minor
+---
+
+Add back framework wrappers for @uppy/status-bar plugin
diff --git a/packages/@uppy/angular/projects/uppy/angular/src/lib/components/status-bar/status-bar-demo.component.ts b/packages/@uppy/angular/projects/uppy/angular/src/lib/components/status-bar/status-bar-demo.component.ts
new file mode 100644
index 000000000..7dbcf4338
--- /dev/null
+++ b/packages/@uppy/angular/projects/uppy/angular/src/lib/components/status-bar/status-bar-demo.component.ts
@@ -0,0 +1,31 @@
+import { ChangeDetectionStrategy, Component, type OnInit } from "@angular/core";
+import { Uppy } from "@uppy/core";
+import type * as StatusBar from "@uppy/status-bar";
+import Tus from "@uppy/tus";
+import type { Body, Meta } from "@uppy/utils/lib/UppyFile";
+import { StatusBarComponent } from "./status-bar.component";
+
+@Component({
+ selector: "uppy-status-bar-demo",
+ template: `
+
+
+ `,
+ changeDetection: ChangeDetectionStrategy.OnPush,
+ standalone: true,
+ imports: [StatusBarComponent],
+})
+export class StatusBarDemoComponent
+ implements OnInit
+{
+ uppy: Uppy = new Uppy({ debug: true, autoProceed: true });
+ props: StatusBar.StatusBarOptions = {
+ hideUploadButton: true,
+ hideAfterFinish: false,
+ };
+
+ ngOnInit(): void {
+ this.uppy
+ .use(Tus, { endpoint: "https://master.tus.io/files/" });
+ }
+}
diff --git a/packages/@uppy/angular/projects/uppy/angular/src/lib/components/status-bar/status-bar.component.spec.ts b/packages/@uppy/angular/projects/uppy/angular/src/lib/components/status-bar/status-bar.component.spec.ts
new file mode 100644
index 000000000..38a9cc644
--- /dev/null
+++ b/packages/@uppy/angular/projects/uppy/angular/src/lib/components/status-bar/status-bar.component.spec.ts
@@ -0,0 +1,24 @@
+import { async, type ComponentFixture, TestBed } from "@angular/core/testing";
+
+import { StatusBarComponent } from "./status-bar.component";
+
+describe("StatusBarComponent", () => {
+ let component: StatusBarComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async(() => {
+ TestBed.configureTestingModule({
+ declarations: [StatusBarComponent],
+ }).compileComponents();
+ }));
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(StatusBarComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it("should create", () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/packages/@uppy/angular/projects/uppy/angular/src/lib/components/status-bar/status-bar.component.ts b/packages/@uppy/angular/projects/uppy/angular/src/lib/components/status-bar/status-bar.component.ts
new file mode 100644
index 000000000..5ca1aa176
--- /dev/null
+++ b/packages/@uppy/angular/projects/uppy/angular/src/lib/components/status-bar/status-bar.component.ts
@@ -0,0 +1,53 @@
+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";
+import { UppyAngularWrapper } from "../../utils/wrapper";
+
+@Component({
+ selector: "uppy-status-bar",
+ template: "",
+ changeDetection: ChangeDetectionStrategy.OnPush,
+ standalone: true,
+})
+export class StatusBarComponent
+ extends UppyAngularWrapper
+ implements OnDestroy, OnChanges
+{
+ el = inject(ElementRef);
+
+ @Input() uppy: Uppy = 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();
+ }
+}
diff --git a/packages/@uppy/angular/projects/uppy/angular/src/lib/components/status-bar/status-bar.stories.ts b/packages/@uppy/angular/projects/uppy/angular/src/lib/components/status-bar/status-bar.stories.ts
new file mode 100644
index 000000000..a8329b7ab
--- /dev/null
+++ b/packages/@uppy/angular/projects/uppy/angular/src/lib/components/status-bar/status-bar.stories.ts
@@ -0,0 +1,15 @@
+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,
+});
diff --git a/packages/@uppy/angular/projects/uppy/angular/src/public-api.ts b/packages/@uppy/angular/projects/uppy/angular/src/public-api.ts
index 4b3c8fe7e..5ca6394a4 100644
--- a/packages/@uppy/angular/projects/uppy/angular/src/public-api.ts
+++ b/packages/@uppy/angular/projects/uppy/angular/src/public-api.ts
@@ -4,3 +4,4 @@
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'
diff --git a/packages/@uppy/react/package.json b/packages/@uppy/react/package.json
index 216f43064..15d32f0f7 100644
--- a/packages/@uppy/react/package.json
+++ b/packages/@uppy/react/package.json
@@ -57,6 +57,7 @@
".": "./lib/index.js",
"./css/style.css": "./dist/styles.css",
"./dashboard": "./lib/Dashboard.js",
+ "./status-bar": "./lib/StatusBar.js",
"./dashboard-modal": "./lib/DashboardModal.js",
"./package.json": "./package.json"
},
@@ -75,6 +76,9 @@
"@uppy/screen-capture": {
"optional": true
},
+ "@uppy/status-bar": {
+ "optional": true
+ },
"@uppy/webcam": {
"optional": true
}
diff --git a/packages/@uppy/react/src/StatusBar.ts b/packages/@uppy/react/src/StatusBar.ts
new file mode 100644
index 000000000..fc01368a3
--- /dev/null
+++ b/packages/@uppy/react/src/StatusBar.ts
@@ -0,0 +1,88 @@
+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
+ extends StatusBarOptions {
+ uppy: Uppy
+}
+
+/**
+ * React component that renders a status bar containing upload progress and speed,
+ * processing progress and pause/resume/cancel controls.
+ */
+
+class StatusBar extends Component<
+ StatusBarProps
+> {
+ private container!: HTMLElement
+
+ private plugin!: UnknownPlugin
+
+ componentDidMount(): void {
+ this.installPlugin()
+ }
+
+ componentDidUpdate(prevProps: StatusBar['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
diff --git a/packages/@uppy/react/tsconfig.build.json b/packages/@uppy/react/tsconfig.build.json
index 35036c8ce..2985e040a 100644
--- a/packages/@uppy/react/tsconfig.build.json
+++ b/packages/@uppy/react/tsconfig.build.json
@@ -26,6 +26,9 @@
},
{
"path": "../webcam/tsconfig.build.json"
+ },
+ {
+ "path": "../status-bar/tsconfig.build.json"
}
]
}
diff --git a/packages/@uppy/react/tsconfig.json b/packages/@uppy/react/tsconfig.json
index 9a55b7185..1c5aba285 100644
--- a/packages/@uppy/react/tsconfig.json
+++ b/packages/@uppy/react/tsconfig.json
@@ -25,6 +25,9 @@
},
{
"path": "../webcam/tsconfig.build.json"
+ },
+ {
+ "path": "../status-bar/tsconfig.build.json"
}
]
}
diff --git a/packages/@uppy/svelte/package.json b/packages/@uppy/svelte/package.json
index 5371b3850..bf1af7773 100644
--- a/packages/@uppy/svelte/package.json
+++ b/packages/@uppy/svelte/package.json
@@ -25,6 +25,10 @@
"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",
@@ -71,6 +75,9 @@
"peerDependenciesMeta": {
"@uppy/dashboard": {
"optional": true
+ },
+ "@uppy/status-bar": {
+ "optional": true
}
},
"publishConfig": {
diff --git a/packages/@uppy/svelte/src/lib/components/StatusBar.svelte b/packages/@uppy/svelte/src/lib/components/StatusBar.svelte
new file mode 100644
index 000000000..e47870e55
--- /dev/null
+++ b/packages/@uppy/svelte/src/lib/components/StatusBar.svelte
@@ -0,0 +1,42 @@
+
+
+
diff --git a/packages/@uppy/vue/package.json b/packages/@uppy/vue/package.json
index 64a41ebf3..612e5e97b 100644
--- a/packages/@uppy/vue/package.json
+++ b/packages/@uppy/vue/package.json
@@ -31,6 +31,7 @@
".": "./lib/index.js",
"./css/style.css": "./dist/styles.css",
"./dashboard": "./lib/dashboard.js",
+ "./status-bar": "./lib/status-bar.js",
"./dashboard-modal": "./lib/dashboard-modal.js",
"./package.json": "./package.json"
},
@@ -42,6 +43,9 @@
"peerDependenciesMeta": {
"@uppy/dashboard": {
"optional": true
+ },
+ "@uppy/status-bar": {
+ "optional": true
}
},
"publishConfig": {
diff --git a/packages/@uppy/vue/src/status-bar.ts b/packages/@uppy/vue/src/status-bar.ts
new file mode 100644
index 000000000..3e7138323
--- /dev/null
+++ b/packages/@uppy/vue/src/status-bar.ts
@@ -0,0 +1,39 @@
+import type { 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>,
+ required: true,
+ },
+ props: {
+ type: Object as PropType,
+ },
+ },
+ setup(props) {
+ const containerRef = ref()
+ const pluginRef = ref>()
+ 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
+ }
+
+ useUppy(onMount, pluginRef, props.uppy, propsRef)
+
+ return () =>
+ h('div', {
+ ref: containerRef,
+ })
+ },
+})
diff --git a/packages/@uppy/vue/tsconfig.build.json b/packages/@uppy/vue/tsconfig.build.json
index 0f2644574..714d752f1 100644
--- a/packages/@uppy/vue/tsconfig.build.json
+++ b/packages/@uppy/vue/tsconfig.build.json
@@ -15,6 +15,9 @@
},
{
"path": "../dashboard/tsconfig.build.json"
+ },
+ {
+ "path": "../status-bar/tsconfig.build.json"
}
]
}
diff --git a/packages/@uppy/vue/tsconfig.json b/packages/@uppy/vue/tsconfig.json
index b6c8ef6d2..b043fa3b8 100644
--- a/packages/@uppy/vue/tsconfig.json
+++ b/packages/@uppy/vue/tsconfig.json
@@ -14,6 +14,9 @@
},
{
"path": "../dashboard/tsconfig.build.json"
+ },
+ {
+ "path": "../status-bar/tsconfig.build.json"
}
]
}
diff --git a/yarn.lock b/yarn.lock
index ec6fff9c0..12fdf66f4 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -10001,6 +10001,8 @@ __metadata:
optional: true
"@uppy/screen-capture":
optional: true
+ "@uppy/status-bar":
+ optional: true
"@uppy/webcam":
optional: true
languageName: unknown
@@ -10097,6 +10099,8 @@ __metadata:
peerDependenciesMeta:
"@uppy/dashboard":
optional: true
+ "@uppy/status-bar":
+ optional: true
languageName: unknown
linkType: soft
@@ -10218,6 +10222,8 @@ __metadata:
peerDependenciesMeta:
"@uppy/dashboard":
optional: true
+ "@uppy/status-bar":
+ optional: true
languageName: unknown
linkType: soft