uppy/packages/@uppy/box/src/Box.tsx
Prakash 79e6460a6c
Make Generics Optional in uppy.getPlugin (#6057)
fixes #6024.

### Problem
- `getPlugin()` defaults to `UnknownPlugin`, so methods like `openModal`
are not visible , since core is not aware of that plugin type

### Proposed change
- Introduce a types-only registry in core:
- `export interface PluginTypeRegistry<M extends Meta, B extends Body>
{}`
- Overload `getPlugin` to return a precise type when the id is a known
key of the registry.
- add `Dashboard` to  PluginTypeRegistry through module augmentation:
  - `'Dashboard': Dashboard<M, B>`.
- When a project imports from `@uppy/dashboard`, its module augmentation
extends PluginTypeRegistry, adding the correct type into it
- I've added Tests , kept them in a separate file so it's easier to
review , once this approach gets approved I'll add them to
`Uppy.test.ts`

Once this PR gets a positive review I'll add this for other plugins ,
currently only added for `@uppy/dashboard`

**Build with Local tarball can be checked here** 


https://stackblitz.com/~/github.com/qxprakash/uppy-type-test?file=type_test.ts
2025-11-17 18:18:54 +05:30

117 lines
3.3 KiB
TypeScript

import {
type CompanionPluginOptions,
getAllowedHosts,
Provider,
tokenStorage,
} from '@uppy/companion-client'
import type {
AsyncStore,
Body,
Meta,
UnknownProviderPlugin,
UnknownProviderPluginState,
UppyFile,
} from '@uppy/core'
import { UIPlugin, type Uppy } from '@uppy/core'
import { ProviderViews } from '@uppy/provider-views'
import type { LocaleStrings } from '@uppy/utils'
// biome-ignore lint/style/useImportType: h is not a type
import { type ComponentChild, h } from 'preact'
import packageJson from '../package.json' with { type: 'json' }
import locale from './locale.js'
export type BoxOptions = CompanionPluginOptions & {
locale?: LocaleStrings<typeof locale>
}
export default class Box<M extends Meta, B extends Body>
extends UIPlugin<BoxOptions, M, B, UnknownProviderPluginState>
implements UnknownProviderPlugin<M, B>
{
static VERSION = packageJson.version
icon: () => h.JSX.Element
provider: Provider<M, B>
view!: ProviderViews<M, B>
storage: AsyncStore
files: UppyFile<M, B>[]
rootFolderId: string | null = null
constructor(uppy: Uppy<M, B>, opts: BoxOptions) {
super(uppy, opts)
this.id = this.opts.id || 'Box'
this.type = 'acquirer'
this.storage = this.opts.storage || tokenStorage
this.files = []
this.icon = () => (
<svg
className="uppy-DashboardTab-iconBox"
aria-hidden="true"
focusable="false"
width="32"
height="32"
viewBox="0 0 32 32"
>
<g fill="currentcolor" fillRule="nonzero">
<path d="m16.4 13.5c-1.6 0-3 0.9-3.7 2.2-0.7-1.3-2.1-2.2-3.7-2.2-1 0-1.8 0.3-2.5 0.8v-3.6c-0.1-0.3-0.5-0.7-1-0.7s-0.8 0.4-0.8 0.8v7c0 2.3 1.9 4.2 4.2 4.2 1.6 0 3-0.9 3.7-2.2 0.7 1.3 2.1 2.2 3.7 2.2 2.3 0 4.2-1.9 4.2-4.2 0.1-2.4-1.8-4.3-4.1-4.3m-7.5 6.8c-1.4 0-2.5-1.1-2.5-2.5s1.1-2.5 2.5-2.5 2.5 1.1 2.5 2.5-1.1 2.5-2.5 2.5m7.5 0c-1.4 0-2.5-1.1-2.5-2.5s1.1-2.5 2.5-2.5 2.5 1.1 2.5 2.5-1.1 2.5-2.5 2.5" />
<path d="m27.2 20.6l-2.3-2.8 2.3-2.8c0.3-0.4 0.2-0.9-0.2-1.2s-1-0.2-1.3 0.2l-2 2.4-2-2.4c-0.3-0.4-0.9-0.4-1.3-0.2-0.4 0.3-0.5 0.8-0.2 1.2l2.3 2.8-2.3 2.8c-0.3 0.4-0.2 0.9 0.2 1.2s1 0.2 1.3-0.2l2-2.4 2 2.4c0.3 0.4 0.9 0.4 1.3 0.2 0.4-0.3 0.4-0.8 0.2-1.2" />
</g>
</svg>
)
this.opts.companionAllowedHosts = getAllowedHosts(
this.opts.companionAllowedHosts,
this.opts.companionUrl,
)
this.provider = new Provider(uppy, {
companionUrl: this.opts.companionUrl,
companionHeaders: this.opts.companionHeaders,
companionKeysParams: this.opts.companionKeysParams,
companionCookiesRule: this.opts.companionCookiesRule,
provider: 'box',
pluginId: this.id,
supportsRefreshToken: false,
})
this.defaultLocale = locale
this.i18nInit()
this.title = this.i18n('pluginNameBox')
this.render = this.render.bind(this)
}
install(): void {
this.view = new ProviderViews(this, {
provider: this.provider,
loadAllFiles: true,
virtualList: true,
})
const { target } = this.opts
if (target) {
this.mount(target, this)
}
}
uninstall(): void {
this.view.tearDown()
this.unmount()
}
render(state: unknown): ComponentChild {
return this.view.render(state)
}
}
declare module '@uppy/core' {
export interface PluginTypeRegistry<M extends Meta, B extends Body> {
Box: Box<M, B>
}
}