import type { Body, Meta, State } from '@uppy/core' import type { I18n, Translator, UppyFile } from '@uppy/core/utils' import { useCallback, useEffect, useRef, useState, } 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' 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: FileCardProps, ): ComponentChildren { const { files, fileCardFor, toggleFileCard, saveFileCard, metaFields, requiredMetaFields, openFileEditor, i18n, i18nArray, className, canEditFile, } = props const getMetaFields = () => { return typeof metaFields === 'function' ? metaFields(files[fileCardFor]) : metaFields } const file = files[fileCardFor] const computedMetaFields = getMetaFields() ?? [] const showEditButton = canEditFile(file) 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: SubmitEvent | TargetedMouseEvent) => { ev.preventDefault() saveFileCard(formState, fileCardFor) }, [saveFileCard, formState, fileCardFor], ) const updateMeta = (newVal: string, name: string) => { setFormState({ ...formState, [name]: newVal, }) } const handleCancel = () => { toggleFileCard(false, fileCardFor) } const [form] = useState(() => { const formEl = document.createElement('form') formEl.setAttribute('tabindex', '-1') formEl.id = nanoid() return formEl }) // We need to know where Uppy is being rendered const domRef = useRef(null) useEffect(() => { /** * Use the "rootNode" of whereever Uppy is rendered, falling back * to `window.document` if domRef isn't initialized for some reason */ const rootNode = domRef.current?.getRootNode() ?? (document as Node) /** * This is the case for the Light DOM and . * In these scenarios, we don't want to append a child to an * element, but to the */ if (rootNode instanceof Document) { rootNode.body.appendChild(form) } // This is the case for the Shadow DOM else if (rootNode instanceof ShadowRoot) { rootNode.appendChild(form) } // Everything else (realistically there isn't) else { rootNode.appendChild(form) } form.addEventListener('submit', handleSave) return () => { form.removeEventListener('submit', handleSave) // check if form is still in the DOM before removing if (form.parentNode) { form.parentNode.removeChild(form) } } }, [form, handleSave]) return ( // biome-ignore lint/a11y/noStaticElementInteractions: ...
{i18nArray('editing', { file: ( {file.meta ? file.meta.name : file.name} ), })}
{showEditButton && ( )}
) }