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