diff --git a/.changeset/eighty-words-wink.md b/.changeset/eighty-words-wink.md new file mode 100644 index 0000000000..833838691c --- /dev/null +++ b/.changeset/eighty-words-wink.md @@ -0,0 +1,13 @@ +--- +"@uppy/screen-capture": minor +"@uppy/dashboard": minor +"@uppy/core": minor +--- + +Narrow a number of `any` types to real types. Some affect the public interface. + +This also fixes some bugs the new types revealed: + +- The "copy link" helper now actually applies its styles to the temporary textarea it creates. Previously the style object was stringified to `"[object Object]"`, leaving the textarea unstyled and able to scroll the page when selected. +- Cancelling the file card now emits `dashboard:file-edit-complete` with the file being edited, instead of `undefined`. +- The "missing required meta fields" message now passes the file to a `metaFields` callback, which previously received `undefined`, and no longer throws when a field listed in the `requiredMetaFields` restriction has no matching entry in `metaFields`. diff --git a/packages/@uppy/core/src/UIPlugin.ts b/packages/@uppy/core/src/UIPlugin.ts index 774d0b4b44..eb9cde9763 100644 --- a/packages/@uppy/core/src/UIPlugin.ts +++ b/packages/@uppy/core/src/UIPlugin.ts @@ -45,7 +45,7 @@ class UIPlugin< isTargetDOMEl!: boolean - el!: HTMLElement | null + el: HTMLElement | null = null parent: unknown diff --git a/packages/@uppy/dashboard/src/Dashboard.tsx b/packages/@uppy/dashboard/src/Dashboard.tsx index a2a674b331..d4b45c9257 100644 --- a/packages/@uppy/dashboard/src/Dashboard.tsx +++ b/packages/@uppy/dashboard/src/Dashboard.tsx @@ -13,9 +13,10 @@ import { UIPlugin } from '@uppy/core' import { defaultPickerIcon } from '@uppy/core/provider-views' import type { LocaleStrings } from '@uppy/core/utils' import { findAllDOMElements, getDroppedFiles, toArray } from '@uppy/core/utils' -import type { ComponentChild, h, VNode } from '@uppy/core/utils/preact' +import type { ComponentChild, VNode } from '@uppy/core/utils/preact' import ThumbnailGenerator from '@uppy/thumbnail-generator' import { nanoid } from 'nanoid/non-secure' +import type { TargetedEvent } from 'preact' import packageJson from '../package.json' with { type: 'json' } import DashboardUI from './components/Dashboard.js' import locale from './locale.js' @@ -78,11 +79,12 @@ type PreactRender = ( ...children: any[] ) => VNode -interface MetaField { +export interface MetaField { id: string name: string placeholder?: string render?: (field: FieldRenderOptions, h: PreactRender) => VNode + type?: string } interface Target { @@ -747,7 +749,7 @@ export default class Dashboard extends UIPlugin< trapFocus.forModal( event, this.getPluginState().activeOverlayType, - this.el, + this.el!, ) } @@ -773,7 +775,7 @@ export default class Dashboard extends UIPlugin< } private handleInputChange = ( - event: h.JSX.TargetedEvent, + event: TargetedEvent, ) => { event.preventDefault() const files = toArray(event.currentTarget.files || []) @@ -902,7 +904,7 @@ export default class Dashboard extends UIPlugin< trapFocus.forInline( event, this.getPluginState().activeOverlayType, - this.el, + this.el!, ) } @@ -1076,7 +1078,7 @@ export default class Dashboard extends UIPlugin< // try to press space multiple times. Focus will jump to Uppy. (isFocusNowhere && this.ifFocusedOnUppyRecently)) ) { - this.superFocus(this.el, this.getPluginState().activeOverlayType) + this.superFocus(this.el!, this.getPluginState().activeOverlayType) } else { this.superFocus.cancel() } diff --git a/packages/@uppy/dashboard/src/components/AddFiles.tsx b/packages/@uppy/dashboard/src/components/AddFiles.tsx index 267f40016a..c72df20030 100644 --- a/packages/@uppy/dashboard/src/components/AddFiles.tsx +++ b/packages/@uppy/dashboard/src/components/AddFiles.tsx @@ -7,7 +7,7 @@ import { } from '@uppy/core/utils/preact' import type { DashboardState, TargetWithRender } from '../Dashboard.js' -interface AddFilesProps { +export interface AddFilesProps { i18n: I18n i18nArray: Translator['translateArray'] acquirers: TargetWithRender[] diff --git a/packages/@uppy/dashboard/src/components/AddFilesPanel.tsx b/packages/@uppy/dashboard/src/components/AddFilesPanel.tsx index 40964abe90..56581c6432 100644 --- a/packages/@uppy/dashboard/src/components/AddFilesPanel.tsx +++ b/packages/@uppy/dashboard/src/components/AddFilesPanel.tsx @@ -1,9 +1,15 @@ import classNames from 'classnames' +import type { ComponentChildren } from 'preact' +import type { AddFilesProps } from './AddFiles.js' import AddFiles from './AddFiles.js' -type $TSFixMe = any +interface AddFilesPanelProps extends AddFilesProps { + className?: string | undefined + showAddFilesPanel: boolean + toggleAddFilesPanel: (enabled: boolean) => void +} -const AddFilesPanel = (props: $TSFixMe): $TSFixMe => { +const AddFilesPanel = (props: AddFilesPanelProps): ComponentChildren => { return (
{ {props.i18n('back')}
- {} ) diff --git a/packages/@uppy/dashboard/src/components/Dashboard.tsx b/packages/@uppy/dashboard/src/components/Dashboard.tsx index 4f5072dc92..1a6cd2539c 100644 --- a/packages/@uppy/dashboard/src/components/Dashboard.tsx +++ b/packages/@uppy/dashboard/src/components/Dashboard.tsx @@ -108,7 +108,7 @@ type DashboardUIProps = { parentElement: HTMLElement | null allowedFileTypes: string[] | null maxNumberOfFiles: number | null - requiredMetaFields: any + requiredMetaFields: string[] showSelectedFiles: boolean showNativePhotoCameraButton: boolean showNativeVideoCameraButton: boolean @@ -131,9 +131,9 @@ type DashboardUIProps = { export default function Dashboard( props: DashboardUIProps, ) { + const { activePickerPanel, fileCardFor } = props const isNoFiles = props.totalFileCount === 0 const isSingleFile = props.totalFileCount === 1 - const isSizeMD = props.containerWidth > WIDTH_MD const isSizeHeightMD = props.containerHeight > HEIGHT_MD const dashboardClassName = classNames({ @@ -320,17 +320,23 @@ export default function Dashboard( {props.showAddFilesPanel ? ( - + ) : null} - {props.fileCardFor ? : null} + {fileCardFor ? ( + + ) : null} - {props.activePickerPanel ? ( - + {activePickerPanel ? ( + ) : null} diff --git a/packages/@uppy/dashboard/src/components/EditorPanel.tsx b/packages/@uppy/dashboard/src/components/EditorPanel.tsx index e6771b9e3d..2a7da1dff4 100644 --- a/packages/@uppy/dashboard/src/components/EditorPanel.tsx +++ b/packages/@uppy/dashboard/src/components/EditorPanel.tsx @@ -1,9 +1,43 @@ +import type Uppy from '@uppy/core' +import type { Body, State, UIPlugin } from '@uppy/core' +import type { + I18n, + Meta, + Translator, + UppyFile, + UppyFileId, +} from '@uppy/core/utils' import classNames from 'classnames' +import type { ComponentChildren, MouseEventHandler } from 'preact' +import type { TargetWithRender } from '../Dashboard.js' -type $TSFixMe = any +declare module '@uppy/core' { + export interface UppyEventMap { + 'file-editor:cancel': (file: UppyFile) => void + } +} + +type EditorPanelProps = { + className?: string | undefined + closeFileEditor: () => void + editors: TargetWithRender[] + fileCardFor: UppyFileId | null + files: State['files'] + i18n: I18n + i18nArray: Translator['translateArray'] + saveFileEditor: MouseEventHandler + state: State + uppy: Uppy +} + +function EditorPanel( + props: EditorPanelProps, +): ComponentChildren { + const file = props.fileCardFor ? props.files[props.fileCardFor] : undefined -function EditorPanel(props: $TSFixMe) { - const file = props.files[props.fileCardFor] + if (!file) { + return null + } const handleCancel = () => { props.uppy.emit('file-editor:cancel', file) @@ -47,8 +81,15 @@ function EditorPanel(props: $TSFixMe) {
- {props.editors.map((target: $TSFixMe) => { - return props.uppy.getPlugin(target.id).render(props.state) + {props.editors.map((target) => { + return ( + props.uppy.getPlugin(target.id) as UIPlugin< + // biome-ignore lint/complexity/noBannedTypes: {} means anything except null or undefined. + {}, + M, + B + > + ).render(props.state) })}
diff --git a/packages/@uppy/dashboard/src/components/FileCard/RenderMetaFields.tsx b/packages/@uppy/dashboard/src/components/FileCard/RenderMetaFields.tsx index cff940a22d..845f63ae7e 100644 --- a/packages/@uppy/dashboard/src/components/FileCard/RenderMetaFields.tsx +++ b/packages/@uppy/dashboard/src/components/FileCard/RenderMetaFields.tsx @@ -1,8 +1,17 @@ -import { h } from '@uppy/core/utils/preact' +import { type ComponentChildren, h } from '@uppy/core/utils/preact' +import type { MetaField } from '../../Dashboard.js' -type $TSFixMe = any +interface RenderMetaFieldsProps { + computedMetaFields: MetaField[] + requiredMetaFields: string[] + updateMeta: (newVal: string, fieldId: string) => void + form: HTMLFormElement + formState: Record +} -export default function RenderMetaFields(props: $TSFixMe) { +export default function RenderMetaFields( + props: RenderMetaFieldsProps, +): ComponentChildren { const { computedMetaFields, requiredMetaFields, @@ -15,7 +24,7 @@ export default function RenderMetaFields(props: $TSFixMe) { text: 'uppy-u-reset uppy-c-textInput uppy-Dashboard-FileCard-input', } - return computedMetaFields.map((field: $TSFixMe) => { + return computedMetaFields.map((field) => { const id = `uppy-Dashboard-FileCard-input-${field.id}` const required = requiredMetaFields.includes(field.id) return ( @@ -27,7 +36,7 @@ export default function RenderMetaFields(props: $TSFixMe) { field.render( { value: formState[field.id], - onChange: (newVal: $TSFixMe) => updateMeta(newVal, field.id), + onChange: (newVal) => updateMeta(newVal, field.id), fieldCSSClasses, required, form: form.id, diff --git a/packages/@uppy/dashboard/src/components/FileCard/index.tsx b/packages/@uppy/dashboard/src/components/FileCard/index.tsx index dad0b3fb07..9d3c8a7684 100644 --- a/packages/@uppy/dashboard/src/components/FileCard/index.tsx +++ b/packages/@uppy/dashboard/src/components/FileCard/index.tsx @@ -1,3 +1,5 @@ +import type { Body, Meta, State } from '@uppy/core' +import type { I18n, Translator, UppyFile } from '@uppy/core/utils' import { useCallback, useEffect, @@ -6,14 +8,30 @@ import { } from '@uppy/core/utils/preact/hooks' import classNames from 'classnames' import { nanoid } from 'nanoid/non-secure' +import type { ComponentChildren, TargetedMouseEvent } from 'preact' +import type { DashboardState } from '../../Dashboard.js' import getFileTypeIcon from '../../utils/getFileTypeIcon.js' import ignoreEvent from '../../utils/ignoreEvent.js' import FilePreview from '../FilePreview.js' import RenderMetaFields from './RenderMetaFields.js' -type $TSFixMe = any +interface FileCardProps { + canEditFile: (file: UppyFile) => boolean + className?: string | undefined + fileCardFor: string + files: State['files'] + i18n: I18n + i18nArray: Translator['translateArray'] + metaFields: DashboardState['metaFields'] + openFileEditor: (file: UppyFile) => void + requiredMetaFields: string[] + saveFileCard: (meta: M, fileID: string) => void + toggleFileCard: (show: boolean, fileID: string) => void +} -export default function FileCard(props: $TSFixMe) { +export default function FileCard( + props: FileCardProps, +): ComponentChildren { const { files, fileCardFor, @@ -38,22 +56,23 @@ export default function FileCard(props: $TSFixMe) { const computedMetaFields = getMetaFields() ?? [] const showEditButton = canEditFile(file) - const storedMetaData: Record = {} - computedMetaFields.forEach((field: $TSFixMe) => { + const storedMetaData = {} as M + computedMetaFields.forEach((field) => { + // @ts-expect-error TODO fix me storedMetaData[field.id] = file.meta[field.id] ?? '' }) const [formState, setFormState] = useState(storedMetaData) const handleSave = useCallback( - (ev: $TSFixMe) => { + (ev: SubmitEvent | TargetedMouseEvent) => { ev.preventDefault() saveFileCard(formState, fileCardFor) }, [saveFileCard, formState, fileCardFor], ) - const updateMeta = (newVal: $TSFixMe, name: $TSFixMe) => { + const updateMeta = (newVal: string, name: string) => { setFormState({ ...formState, [name]: newVal, @@ -61,7 +80,7 @@ export default function FileCard(props: $TSFixMe) { } const handleCancel = () => { - toggleFileCard(false) + toggleFileCard(false, fileCardFor) } const [form] = useState(() => { @@ -152,7 +171,7 @@ export default function FileCard(props: $TSFixMe) {