chore(richtext-lexical): improve types of UploadData (#10982)

One step closer to being able to remove `noUncheckedIndexedAccess` in
`packages/richtext-lexical/tsconfig.json`.

I'm introducing UploadData_P4 which is a more precise version of
UploadData. I'm doing it as a different type because there's a chance
it'll be a breaking change for some users.

UploadData is used in many places, but I'm currently replacing it only
in
`packages/richtext-lexical/src/exports/react/components/RichText/converter/converters/upload.tsx`,
because in the other files it's too rooted to other types like
UploadNode.
This commit is contained in:
Germán Jabloñski
2025-02-11 16:24:05 -03:00
committed by GitHub
parent 3098f35537
commit 002e921ede
3 changed files with 50 additions and 26 deletions

View File

@@ -12,6 +12,16 @@ export type FileSize = {
width: null | number
}
// TODO: deprecate in Payload v4.
/**
* FileSizeImproved is a more precise type, and will replace FileSize in Payload v4.
* This type is for internal use only as it will be deprecated in the future.
* @internal
*/
export type FileSizeImproved = {
url: null | string
} & FileSize
export type FileSizes = {
[size: string]: FileSize
}

View File

@@ -1,23 +1,25 @@
import type { FileData, FileSize, TypeWithID } from 'payload'
import type { FileSizeImproved } from 'payload'
import type { UploadDataImproved } from '../../../../../../features/upload/server/nodes/UploadNode.js'
import type { SerializedUploadNode } from '../../../../../../nodeTypes.js'
import type { JSXConverters } from '../types.js'
export const UploadJSXConverter: JSXConverters<SerializedUploadNode> = {
upload: ({ node }) => {
const uploadDocument: {
value?: FileData & TypeWithID
} = node as any
const url = uploadDocument?.value?.url
// TO-DO (v4): SerializedUploadNode should use UploadData_P4
const uploadDocument = node as UploadDataImproved
if (typeof uploadDocument.value !== 'object') {
return null
}
const url = uploadDocument.value.url
/**
* If the upload is not an image, return a link to the upload
*/
if (!uploadDocument?.value?.mimeType?.startsWith('image')) {
if (!uploadDocument.value.mimeType.startsWith('image')) {
return (
<a href={url} rel="noopener noreferrer">
{uploadDocument.value?.filename}
{uploadDocument.value.filename}
</a>
)
}
@@ -25,13 +27,13 @@ export const UploadJSXConverter: JSXConverters<SerializedUploadNode> = {
/**
* If the upload is a simple image with no different sizes, return a simple img tag
*/
if (!uploadDocument?.value?.sizes || !Object.keys(uploadDocument?.value?.sizes).length) {
if (!Object.keys(uploadDocument.value.sizes).length) {
return (
<img
alt={uploadDocument?.value?.filename}
height={uploadDocument?.value?.height}
alt={uploadDocument.value.filename}
height={uploadDocument.value.height}
src={url}
width={uploadDocument?.value?.width}
width={uploadDocument.value.width}
/>
)
}
@@ -42,13 +44,12 @@ export const UploadJSXConverter: JSXConverters<SerializedUploadNode> = {
const pictureJSX: React.ReactNode[] = []
// Iterate through each size in the data.sizes object
for (const size in uploadDocument.value?.sizes) {
const imageSize: {
url?: string
} & FileSize = uploadDocument.value?.sizes[size]
for (const size in uploadDocument.value.sizes) {
const imageSize = uploadDocument.value.sizes[size] as FileSizeImproved
// Skip if any property of the size object is null
if (
!imageSize ||
!imageSize.width ||
!imageSize.height ||
!imageSize.mimeType ||

View File

@@ -8,7 +8,7 @@ import type {
NodeKey,
Spread,
} from 'lexical'
import type { CollectionSlug, DataFromCollectionSlug, JsonObject } from 'payload'
import type { FileData, JsonObject, TypedCollection, TypeWithID } from 'payload'
import type { JSX } from 'react'
import { DecoratorBlockNode } from '@lexical/react/LexicalDecoratorBlockNode.js'
@@ -17,15 +17,28 @@ import { $applyNodeReplacement } from 'lexical'
import * as React from 'react'
export type UploadData<TUploadExtraFieldsData extends JsonObject = JsonObject> = {
[TCollectionSlug in CollectionSlug]: {
fields: TUploadExtraFieldsData
/** Every lexical node that has sub-fields needs to have a unique ID. This is the ID of this upload node, not the ID of the linked upload document */
id: string
relationTo: string
/** Value can be just the document ID, or the full, populated document */
value: number | string | TypedCollection
}
// TODO: deprecate in Payload v4.
/**
* UploadDataImproved is a more precise type, and will replace UploadData in Payload v4.
* This type is for internal use only as it will be deprecated in the future.
* @internal
*/
export type UploadDataImproved<TUploadExtraFieldsData extends JsonObject = JsonObject> = {
fields: TUploadExtraFieldsData
// Every lexical node that has sub-fields needs to have a unique ID. This is the ID of this upload node, not the ID of the linked upload document
id: string
relationTo: TCollectionSlug
relationTo: string
// Value can be just the document ID, or the full, populated document
value: DataFromCollectionSlug<TCollectionSlug> | number | string
}
}[CollectionSlug]
value: (FileData & TypeWithID) | number | string
}
export function isGoogleDocCheckboxImg(img: HTMLImageElement): boolean {
return (