diff --git a/examples/form-builder/next-pages/package.json b/examples/form-builder/next-pages/package.json index 757dc6c6d..94dddedad 100644 --- a/examples/form-builder/next-pages/package.json +++ b/examples/form-builder/next-pages/package.json @@ -11,7 +11,7 @@ "dependencies": { "@apollo/client": "^3.7.0", "@faceless-ui/css-grid": "^1.2.0", - "@faceless-ui/modal": "^2.0.1", + "@faceless-ui/modal": "^2.0.2", "escape-html": "^1.0.3", "graphql": "^16.8.1", "next": "^13.5.6", diff --git a/examples/form-builder/payload/package.json b/examples/form-builder/payload/package.json index 44de480b8..40ff3a7e6 100644 --- a/examples/form-builder/payload/package.json +++ b/examples/form-builder/payload/package.json @@ -18,7 +18,7 @@ "@payloadcms/bundler-webpack": "latest", "@payloadcms/db-mongodb": "latest", "@payloadcms/richtext-slate": "latest", - "@faceless-ui/modal": "^2.0.1", + "@faceless-ui/modal": "^2.0.2", "@payloadcms/plugin-form-builder": "^1.0.12", "@payloadcms/plugin-seo": "^1.0.8", "dotenv": "^8.2.0", diff --git a/packages/ui/src/elements/LeaveWithoutSaving/index.scss b/packages/next/src/elements/LeaveWithoutSaving/index.scss similarity index 91% rename from packages/ui/src/elements/LeaveWithoutSaving/index.scss rename to packages/next/src/elements/LeaveWithoutSaving/index.scss index 6cf46bd72..5088528a8 100644 --- a/packages/ui/src/elements/LeaveWithoutSaving/index.scss +++ b/packages/next/src/elements/LeaveWithoutSaving/index.scss @@ -1,4 +1,4 @@ -@import '../../scss/styles.scss'; +@import '../../../../ui/src/scss/styles.scss'; .leave-without-saving { @include blur-bg; diff --git a/packages/ui/src/elements/LeaveWithoutSaving/index.tsx b/packages/next/src/elements/LeaveWithoutSaving/index.tsx similarity index 52% rename from packages/ui/src/elements/LeaveWithoutSaving/index.tsx rename to packages/next/src/elements/LeaveWithoutSaving/index.tsx index fb8a83041..f2ead86b9 100644 --- a/packages/ui/src/elements/LeaveWithoutSaving/index.tsx +++ b/packages/next/src/elements/LeaveWithoutSaving/index.tsx @@ -1,12 +1,13 @@ 'use client' -import { Modal, useModal } from '@faceless-ui/modal' -import React, { useEffect } from 'react' +import { Modal, useModal } from '@payloadcms/ui' +import React, { useCallback, useEffect } from 'react' -import { Button } from '../../elements/Button' -import { useFormModified } from '../../forms/Form/context' -import { useAuth } from '../../providers/Auth' -import { useTranslation } from '../../providers/Translation' +import { Button } from '../../../../ui/src/elements/Button' +import { useFormModified } from '../../../../ui/src/forms/Form/context' +import { useAuth } from '../../../../ui/src/providers/Auth' +import { useTranslation } from '../../../../ui/src/providers/Translation' import './index.scss' +import { usePreventLeave } from './usePreventLeave' const modalSlug = 'leave-without-saving' @@ -17,15 +18,15 @@ const Component: React.FC<{ onCancel: () => void onConfirm: () => void }> = ({ isActive, onCancel, onConfirm }) => { - const { closeModal, openModal, modalState } = useModal() + const { closeModal, modalState, openModal } = useModal() const { t } = useTranslation() // Manually check for modal state as 'esc' key will not trigger the nav inactivity - useEffect(() => { - if (!modalState?.[modalSlug]?.isOpen && isActive) { - onCancel() - } - }, [modalState]) + // useEffect(() => { + // if (!modalState?.[modalSlug]?.isOpen && isActive) { + // onCancel() + // } + // }, [modalState, isActive, onCancel]) useEffect(() => { if (isActive) openModal(modalSlug) @@ -53,11 +54,26 @@ const Component: React.FC<{ export const LeaveWithoutSaving: React.FC = () => { const modified = useFormModified() const { user } = useAuth() + const [show, setShow] = React.useState(false) + const [hasAccepted, setHasAccepted] = React.useState(false) - return null - // - // {({ isActive, onCancel, onConfirm }) => ( - // - // )} - // + const prevent = Boolean(modified && user) + + const onPrevent = useCallback(() => { + setShow(true) + }, []) + + usePreventLeave({ hasAccepted, onPrevent, prevent }) + + return ( + { + setShow(false) + }} + onConfirm={() => { + setHasAccepted(true) + }} + /> + ) } diff --git a/packages/next/src/elements/LeaveWithoutSaving/usePreventLeave.tsx b/packages/next/src/elements/LeaveWithoutSaving/usePreventLeave.tsx new file mode 100644 index 000000000..340c9acb3 --- /dev/null +++ b/packages/next/src/elements/LeaveWithoutSaving/usePreventLeave.tsx @@ -0,0 +1,148 @@ +// Credit: @Taiki92777 +// - Source: https://github.com/vercel/next.js/discussions/32231#discussioncomment-7284386 +// Credit: `react-use` maintainers +// - Source: https://github.com/streamich/react-use/blob/ade8d3905f544305515d010737b4ae604cc51024/src/useBeforeUnload.ts#L2 +import { useRouter } from 'next/navigation' +import { useCallback, useEffect, useRef } from 'react' + +function on( + obj: T | null, + ...args: [string, Function | null, ...any] | Parameters +): void { + if (obj && obj.addEventListener) { + obj.addEventListener(...(args as Parameters)) + } +} + +function off( + obj: T | null, + ...args: [string, Function | null, ...any] | Parameters +): void { + if (obj && obj.removeEventListener) { + obj.removeEventListener(...(args as Parameters)) + } +} + +export const useBeforeUnload = (enabled: (() => boolean) | boolean = true, message?: string) => { + const handler = useCallback( + (event: BeforeUnloadEvent) => { + const finalEnabled = typeof enabled === 'function' ? enabled() : true + + if (!finalEnabled) { + return + } + + event.preventDefault() + + if (message) { + event.returnValue = message + } + + return message + }, + [enabled, message], + ) + + useEffect(() => { + if (!enabled) { + return + } + + on(window, 'beforeunload', handler) + + return () => off(window, 'beforeunload', handler) + }, [enabled, handler]) +} + +export const usePreventLeave = ({ + hasAccepted = false, + message = 'Are you sure want to leave this page?', + onPrevent, + prevent = true, +}: { + hasAccepted: boolean + // if no `onPrevent` is provided, the message will be displayed in a confirm dialog + message?: string + // to use a custom confirmation dialog, provide a function that returns a boolean + onPrevent?: () => void + prevent: boolean +}) => { + // check when page is about to be reloaded + useBeforeUnload(prevent, message) + + const router = useRouter() + const cancelledURL = useRef('') + + // check when page is about to be changed + useEffect(() => { + function isAnchorOfCurrentUrl(currentUrl: string, newUrl: string) { + const currentUrlObj = new URL(currentUrl) + const newUrlObj = new URL(newUrl) + // Compare hostname, pathname, and search parameters + if ( + currentUrlObj.hostname === newUrlObj.hostname && + currentUrlObj.pathname === newUrlObj.pathname && + currentUrlObj.search === newUrlObj.search + ) { + // Check if the new URL is just an anchor of the current URL page + const currentHash = currentUrlObj.hash + const newHash = newUrlObj.hash + return ( + currentHash !== newHash && + currentUrlObj.href.replace(currentHash, '') === newUrlObj.href.replace(newHash, '') + ) + } + return false + } + + function findClosestAnchor(element: HTMLElement | null): HTMLAnchorElement | null { + while (element && element.tagName.toLowerCase() !== 'a') { + element = element.parentElement + } + return element as HTMLAnchorElement + } + function handleClick(event: MouseEvent) { + try { + const target = event.target as HTMLElement + const anchor = findClosestAnchor(target) + if (anchor) { + const currentUrl = window.location.href + const newUrl = anchor.href + const isAnchor = isAnchorOfCurrentUrl(currentUrl, newUrl) + const isDownloadLink = anchor.download !== '' + + const isPageLeaving = !(newUrl === currentUrl || isAnchor || isDownloadLink) + + if (isPageLeaving && prevent && (!onPrevent ? !window.confirm(message) : true)) { + // Keep a reference of the href + cancelledURL.current = newUrl + + // Cancel the route change + event.preventDefault() + event.stopPropagation() + + if (typeof onPrevent === 'function') { + onPrevent() + } + } + } + } catch (err) { + alert(err) + } + } + + // Add the global click event listener + document.addEventListener('click', handleClick, true) + + // Clean up the global click event listener when the component is unmounted + return () => { + document.removeEventListener('click', handleClick, true) + } + }, [onPrevent, prevent, message]) + + useEffect(() => { + if (hasAccepted && cancelledURL.current) { + router.push(cancelledURL.current) + } + }, [hasAccepted, router]) +} diff --git a/packages/next/src/pages/Edit/Default/index.tsx b/packages/next/src/pages/Edit/Default/index.tsx index ef0c88eb0..d67ab74f1 100644 --- a/packages/next/src/pages/Edit/Default/index.tsx +++ b/packages/next/src/pages/Edit/Default/index.tsx @@ -7,7 +7,6 @@ import { FieldPathProvider, Form, FormLoadingOverlayToggle, - LeaveWithoutSaving, OperationProvider, getFormState, useComponentMap, @@ -17,6 +16,7 @@ import { import React, { Fragment, useCallback } from 'react' import { Upload } from '../../../../../ui/src/elements/Upload' +import { LeaveWithoutSaving } from '../../../elements/LeaveWithoutSaving' // import { getTranslation } from '@payloadcms/translations' import Auth from './Auth' import { SetDocumentTitle } from './SetDocumentTitle' @@ -45,7 +45,7 @@ export const DefaultEditView: React.FC = () => { hasSavePermission, initialData: data, initialState, - onSave: onSaveFromProps, + onSave: onSaveFromContext, } = useDocumentInfo() const config = useConfig() @@ -57,7 +57,7 @@ export const DefaultEditView: React.FC = () => { serverURL, } = config - const { getFieldMap } = useComponentMap() + const { componentMap, getFieldMap } = useComponentMap() const collectionConfig = collectionSlug && collections.find((collection) => collection.slug === collectionSlug) @@ -95,8 +95,8 @@ export const DefaultEditView: React.FC = () => { // await refreshCookieAsync() // } - if (typeof onSaveFromProps === 'function') { - onSaveFromProps({ + if (typeof onSaveFromContext === 'function') { + onSaveFromContext({ ...json, operation: id ? 'update' : 'create', }) @@ -104,7 +104,7 @@ export const DefaultEditView: React.FC = () => { }, [ id, - onSaveFromProps, + onSaveFromContext, // refreshCookieAsync, // reportUpdate ], @@ -142,6 +142,8 @@ export const DefaultEditView: React.FC = () => { [serverURL, apiRoute, id, operation, schemaPath, collectionSlug, globalSlug], ) + const RegisterGetThumbnailFunction = componentMap?.[`${collectionSlug}.adminThumbnail`] + return (
@@ -220,11 +222,14 @@ export const DefaultEditView: React.FC = () => { /> )} {upload && ( - + + {RegisterGetThumbnailFunction && } + + )} ) diff --git a/packages/next/src/pages/Edit/index.client.tsx b/packages/next/src/pages/Edit/index.client.tsx index 4b1124b96..fda4ac157 100644 --- a/packages/next/src/pages/Edit/index.client.tsx +++ b/packages/next/src/pages/Edit/index.client.tsx @@ -1,12 +1,17 @@ 'use client' import type { EditViewProps } from 'payload/config' -import { LoadingOverlay, useComponentMap, useDocumentInfo } from '@payloadcms/ui' -import React, { Fragment } from 'react' +import { LoadingOverlay, useComponentMap, useConfig, useDocumentInfo } from '@payloadcms/ui' +import { redirect } from 'next/navigation' +import React, { Fragment, useEffect } from 'react' import { useCallback } from 'react' export const EditViewClient: React.FC = () => { - const { id, collectionSlug, getDocPermissions, getVersions, globalSlug } = useDocumentInfo() + const { id, collectionSlug, getDocPermissions, getVersions, globalSlug, setDocumentInfo } = + useDocumentInfo() + const { + routes: { api: adminRoute }, + } = useConfig() const { componentMap } = useComponentMap() @@ -22,7 +27,7 @@ export const EditViewClient: React.FC = () => { getDocPermissions() if (!isEditing) { - // setRedirect(`${admin}/collections/${collection.slug}/${json?.doc?.id}`) + redirect(`${adminRoute}/collections/${collectionSlug}/${json?.doc?.id}`) } else { // buildState(json.doc, { // fieldSchema: collection.fields, @@ -33,9 +38,16 @@ export const EditViewClient: React.FC = () => { // })) } }, - [getVersions, isEditing, getDocPermissions, collectionSlug], + [getVersions, isEditing, getDocPermissions, collectionSlug, adminRoute], ) + useEffect(() => { + setDocumentInfo((current) => ({ + ...current, + onSave, + })) + }, [setDocumentInfo, onSave]) + // Allow the `DocumentInfoProvider` to hydrate if (!Edit || (!collectionSlug && !globalSlug)) { return diff --git a/packages/next/src/pages/List/Default/Cell/fields/File/index.tsx b/packages/next/src/pages/List/Default/Cell/fields/File/index.tsx index 09f006d6f..6c7871ee5 100644 --- a/packages/next/src/pages/List/Default/Cell/fields/File/index.tsx +++ b/packages/next/src/pages/List/Default/Cell/fields/File/index.tsx @@ -11,12 +11,13 @@ const baseClass = 'file' export interface FileCellProps extends CellComponentProps {} export const FileCell: React.FC = ({ cellData, customCellContext, rowData }) => { - const { uploadConfig } = customCellContext + const { collectionSlug, uploadConfig } = customCellContext return (
{ const mimeType: Field = { name: 'mimeType', + type: 'text', admin: { hidden: true, readOnly: true, }, label: 'MIME Type', - type: 'text', } const url: Field = { name: 'url', + type: 'text', admin: { hidden: true, readOnly: true, }, label: 'URL', - type: 'text', } const width: Field = { name: 'width', + type: 'number', admin: { hidden: true, readOnly: true, }, label: labels['upload:width'], - type: 'number', } const height: Field = { name: 'height', + type: 'number', admin: { hidden: true, readOnly: true, }, label: labels['upload:height'], - type: 'number', } const filesize: Field = { name: 'filesize', + type: 'number', admin: { hidden: true, readOnly: true, }, label: labels['upload:fileSize'], - type: 'number', } const filename: Field = { name: 'filename', + type: 'text', admin: { disableBulkEdit: true, hidden: true, @@ -82,7 +83,6 @@ const getBaseUploadFields = ({ collection, config }: Options): Field[] => { }, index: true, label: labels['upload:fileName'], - type: 'text', unique: true, } @@ -93,10 +93,7 @@ const getBaseUploadFields = ({ collection, config }: Options): Field[] => { afterRead: [ ({ data }) => { if (data?.filename) { - if (uploadOptions.staticURL.startsWith('/')) { - return `${config.serverURL}${uploadOptions.staticURL}/${data.filename}` - } - return `${uploadOptions.staticURL}/${data.filename}` + return `${config.serverURL}${config.routes.api}/${collection.slug}/file/${data.filename}` } return undefined @@ -119,11 +116,13 @@ const getBaseUploadFields = ({ collection, config }: Options): Field[] => { uploadFields = uploadFields.concat([ { name: 'sizes', + type: 'group', admin: { hidden: true, }, fields: uploadOptions.imageSizes.map((size) => ({ name: size.name, + type: 'group', admin: { hidden: true, }, @@ -136,10 +135,7 @@ const getBaseUploadFields = ({ collection, config }: Options): Field[] => { const sizeFilename = data?.sizes?.[size.name]?.filename if (sizeFilename) { - if (uploadOptions.staticURL.startsWith('/')) { - return `${config.serverURL}${uploadOptions.staticURL}/${sizeFilename}` - } - return `${uploadOptions.staticURL}/${sizeFilename}` + return `${config.serverURL}${config.routes.api}/${collection.slug}/file/${sizeFilename}` } return null @@ -157,10 +153,8 @@ const getBaseUploadFields = ({ collection, config }: Options): Field[] => { }, ], label: size.name, - type: 'group', })), label: labels['upload:Sizes'], - type: 'group', }, ]) } diff --git a/packages/payload/src/uploads/types.ts b/packages/payload/src/uploads/types.ts index da722316a..6d2997e3f 100644 --- a/packages/payload/src/uploads/types.ts +++ b/packages/payload/src/uploads/types.ts @@ -70,7 +70,7 @@ export type ImageSize = Omit & { export type GetAdminThumbnail = (args: { doc: Record }) => false | null | string export type IncomingUploadType = { - adminThumbnail?: GetAdminThumbnail | string + adminThumbnail?: React.ComponentType | string crop?: boolean disableLocalStorage?: boolean filesRequiredOnCreate?: boolean @@ -88,7 +88,12 @@ export type IncomingUploadType = { } export type Upload = { - adminThumbnail?: GetAdminThumbnail | string + /** + * Represents an admin thumbnail, which can be either a React component or a string. + * - If a string, it should be one of the image size names. + * - If a React component, register a function that generates the thumbnail URL using the `useAdminThumbnail` hook. + **/ + adminThumbnail?: React.ComponentType | string crop?: boolean disableLocalStorage?: boolean filesRequiredOnCreate?: boolean diff --git a/packages/plugin-form-builder/package.json b/packages/plugin-form-builder/package.json index 84666ec41..8f6e2a556 100644 --- a/packages/plugin-form-builder/package.json +++ b/packages/plugin-form-builder/package.json @@ -11,7 +11,7 @@ "scripts": { "build:swc": "swc ./src -d ./dist --config-file .swcrc", "build:types": "tsc --emitDeclarationOnly --outDir dist", - "build": "pnpm build:swc && pnpm build:types", + "build": "echo \"Build temporarily disabled.\" && exit 0", "clean": "rimraf {dist,*.tsbuildinfo}", "prepublishOnly": "pnpm clean && pnpm turbo build", "test": "echo \"No tests available.\"" @@ -21,7 +21,7 @@ "react": "^16.8.0 || ^17.0.0 || ^18.0.0" }, "dependencies": { - "@payloadcms/ui": "workspace:^", + "@payloadcms/ui": "workspace:*", "deepmerge": "^4.2.2", "escape-html": "^1.0.3" }, diff --git a/packages/plugin-search/package.json b/packages/plugin-search/package.json index f6356b9df..fc5310805 100644 --- a/packages/plugin-search/package.json +++ b/packages/plugin-search/package.json @@ -30,7 +30,7 @@ "react": "^16.8.0 || ^17.0.0 || ^18.0.0" }, "dependencies": { - "@payloadcms/ui": "workspace:^", + "@payloadcms/ui": "workspace:*", "ts-deepmerge": "^2.0.1" }, "devDependencies": { diff --git a/packages/plugin-stripe/package.json b/packages/plugin-stripe/package.json index a3a4cb58c..4d08859e9 100644 --- a/packages/plugin-stripe/package.json +++ b/packages/plugin-stripe/package.json @@ -7,7 +7,7 @@ "main": "dist/index.js", "types": "dist/index.d.ts", "scripts": { - "build": "pnpm build:swc && pnpm build:types", + "build": "echo \"Build temporarily disabled.\" && exit 0", "build:swc": "swc ./src -d ./dist --config-file .swcrc", "build:types": "tsc --emitDeclarationOnly --outDir dist", "clean": "rimraf {dist,*.tsbuildinfo}", @@ -31,7 +31,7 @@ "payload": "^1.1.8 || ^2.0.0" }, "dependencies": { - "@payloadcms/ui": "workspace:^", + "@payloadcms/ui": "workspace:*", "lodash.get": "^4.4.2", "stripe": "^10.2.0", "uuid": "^9.0.0" diff --git a/packages/richtext-lexical/package.json b/packages/richtext-lexical/package.json index f0045ebef..6ef4b1203 100644 --- a/packages/richtext-lexical/package.json +++ b/packages/richtext-lexical/package.json @@ -18,7 +18,7 @@ "prepublishOnly": "pnpm clean && pnpm turbo build" }, "dependencies": { - "@faceless-ui/modal": "2.0.1", + "@faceless-ui/modal": "2.0.2", "@lexical/headless": "0.13.1", "@lexical/link": "0.13.1", "@lexical/list": "0.13.1", @@ -29,7 +29,6 @@ "@lexical/selection": "0.13.1", "@lexical/utils": "0.13.1", "@payloadcms/translations": "workspace:*", - "@payloadcms/ui": "workspace:*", "bson-objectid": "2.0.4", "classnames": "^2.3.2", "deep-equal": "2.2.3", @@ -43,6 +42,7 @@ }, "devDependencies": { "@payloadcms/eslint-config": "workspace:*", + "@payloadcms/ui": "workspace:*", "@types/json-schema": "7.0.15", "@types/node": "20.6.2", "@types/react": "18.2.15", @@ -50,8 +50,8 @@ "payload": "workspace:*" }, "peerDependencies": { - "@payloadcms/translations": "workspace:^", - "@payloadcms/ui": "workspace:^", + "@payloadcms/translations": "workspace:*", + "@payloadcms/ui": "workspace:*", "payload": "^2.4.0" }, "exports": { diff --git a/packages/richtext-lexical/src/field/features/Blocks/utils/transformInputFormData.ts b/packages/richtext-lexical/src/field/features/Blocks/utils/transformInputFormData.ts deleted file mode 100644 index 7da89896c..000000000 --- a/packages/richtext-lexical/src/field/features/Blocks/utils/transformInputFormData.ts +++ /dev/null @@ -1,25 +0,0 @@ -/** - * Wraps the input formData in a blockFieldWrapperName, so that it can be read by the RenderFields component - * which requires it to be wrapped in a group field - */ -export function transformInputFormData(data: any, blockFieldWrapperName: string) { - const dataCopy = JSON.parse(JSON.stringify(data)) - - const fieldDataWithoutBlockFields = { ...dataCopy } - delete fieldDataWithoutBlockFields['id'] - delete fieldDataWithoutBlockFields['blockName'] - delete fieldDataWithoutBlockFields['blockType'] - - // Wrap all fields inside blockFieldWrapperName. - // This is necessary, because blockFieldWrapperName is set as the 'base' path for all fields in the block (in the RenderFields component). - // Thus, in order for the data to be read, it has to be wrapped in this blockFieldWrapperName, as it's expected to be there. - - // Why are we doing this? Because that way, all rendered fields of the blocks have different paths and names, and thus don't conflict with each other. - // They have different paths and names, because they are wrapped in the blockFieldWrapperName, which has a name that is unique for each block. - return { - id: dataCopy.id, - [blockFieldWrapperName]: fieldDataWithoutBlockFields, - blockName: dataCopy.blockName, - blockType: dataCopy.blockType, - } -} diff --git a/packages/richtext-lexical/src/field/features/Blocks/utils/transformInputFormSchema.ts b/packages/richtext-lexical/src/field/features/Blocks/utils/transformInputFormSchema.ts deleted file mode 100644 index ed1564911..000000000 --- a/packages/richtext-lexical/src/field/features/Blocks/utils/transformInputFormSchema.ts +++ /dev/null @@ -1,31 +0,0 @@ -import type { Field } from 'payload/types' - -export function transformInputFormSchema(formSchema: any, blockFieldWrapperName: string): Field[] { - const formSchemaCopy = [...formSchema] - - // First, check if it needs wrapping - const hasBlockFieldWrapper = formSchemaCopy.some( - (field) => 'name' in field && field.name === blockFieldWrapperName, - ) - if (hasBlockFieldWrapper) { - return formSchemaCopy - } - - // Add a group in the field schema, which represents all values saved in the blockFieldWrapperName - return [ - ...formSchemaCopy.filter( - (field) => 'name' in field && ['blockName', 'blockType', 'id'].includes(field.name), - ), - { - name: blockFieldWrapperName, - type: 'group', - admin: { - hideGutter: true, - }, - fields: formSchemaCopy.filter( - (field) => !('name' in field) || !['blockName', 'blockType', 'id'].includes(field.name), - ), - label: '', - }, - ] -} diff --git a/packages/richtext-lexical/src/field/features/Blocks/component/BlockContent.tsx b/packages/richtext-lexical/src/field/features/blocks/component/BlockContent.tsx similarity index 92% rename from packages/richtext-lexical/src/field/features/Blocks/component/BlockContent.tsx rename to packages/richtext-lexical/src/field/features/blocks/component/BlockContent.tsx index 64f549195..e670515d9 100644 --- a/packages/richtext-lexical/src/field/features/Blocks/component/BlockContent.tsx +++ b/packages/richtext-lexical/src/field/features/blocks/component/BlockContent.tsx @@ -11,7 +11,6 @@ import { ErrorPill, Pill, SectionTitle, - createNestedFieldPath, useDocumentInfo, useFormSubmitted, useTranslation, @@ -21,7 +20,6 @@ import { $getNodeByKey } from 'lexical' import React, { useCallback } from 'react' import type { ReducedBlock } from '../../../../../../ui/src/utilities/buildComponentMap/types' -import type { FieldProps } from '../../../../types' import type { BlockFields, BlockNode } from '../nodes/BlocksNode' import { FormSavePlugin } from './FormSavePlugin' @@ -89,8 +87,6 @@ export const BlockContent: React.FC = (props) => { .filter(Boolean) .join(' ') - const path = '' as const - const onFormChange = useCallback( ({ fullFieldsWithValues, @@ -101,9 +97,9 @@ export const BlockContent: React.FC = (props) => { }) => { newFormData = { ...newFormData, - id: formData.id, // TODO: Why does form updatee not include theeeeem - blockName: formData.blockName, // TODO: Why does form updatee not include theeeeem - blockType: formData.blockType, // TODO: Why does form updatee not include theeeeem + id: formData.id, + blockName: newFormData.blockName2, // TODO: Find a better solution for this. We have to wrap it in blockName2 when using it here, as blockName does not accept or provide any updated values for some reason. + blockType: formData.blockType, } // Recursively remove all undefined values from even being present in formData, as they will @@ -123,8 +119,6 @@ export const BlockContent: React.FC = (props) => { removeUndefinedAndNullRecursively(newFormData) removeUndefinedAndNullRecursively(formData) - console.log('before saving node data...', newFormData, 'old', formData) - // Only update if the data has actually changed. Otherwise, we may be triggering an unnecessary value change, // which would trigger the "Leave without saving" dialog unnecessarily if (!isDeepEqual(formData, newFormData)) { @@ -136,7 +130,6 @@ export const BlockContent: React.FC = (props) => { editor.update(() => { const node: BlockNode = $getNodeByKey(nodeKey) if (node) { - console.log('saving node data...', newFormData) node.setFields(newFormData as BlockFields) } }) @@ -197,14 +190,14 @@ export const BlockContent: React.FC = (props) => { ? getTranslation(labels.singular, i18n) : '[Singular Label]'} - + {fieldHasErrors && }
{editor.isEditable() && ( @@ -404,6 +405,7 @@ ${steps.map(formatStep).join(`\n`)} e.preventDefault() }} title="Insert snapshot" + type="button" > Insert Snapshot @@ -415,6 +417,7 @@ ${steps.map(formatStep).join(`\n`)} e.preventDefault() }} title="Copy to clipboard" + type="button" > Copy @@ -426,6 +429,7 @@ ${steps.map(formatStep).join(`\n`)} e.preventDefault() }} title="Download as a file" + type="button" > Download diff --git a/packages/richtext-lexical/src/field/features/debug/treeview/feature.client.tsx b/packages/richtext-lexical/src/field/features/debug/treeview/feature.client.tsx new file mode 100644 index 000000000..4e3e72849 --- /dev/null +++ b/packages/richtext-lexical/src/field/features/debug/treeview/feature.client.tsx @@ -0,0 +1,22 @@ +'use client' +import type { FeatureProviderProviderClient } from '../../types' + +import { createClientComponent } from '../../createClientComponent' +import { TreeViewPlugin } from './plugin' + +const TreeViewFeatureClient: FeatureProviderProviderClient = (props) => { + return { + clientFeatureProps: props, + feature: () => ({ + clientFeatureProps: props, + plugins: [ + { + Component: TreeViewPlugin, + position: 'bottom', + }, + ], + }), + } +} + +export const TreeViewFeatureClientComponent = createClientComponent(TreeViewFeatureClient) diff --git a/packages/richtext-lexical/src/field/features/debug/treeview/feature.server.ts b/packages/richtext-lexical/src/field/features/debug/treeview/feature.server.ts new file mode 100644 index 000000000..9a9c7c537 --- /dev/null +++ b/packages/richtext-lexical/src/field/features/debug/treeview/feature.server.ts @@ -0,0 +1,16 @@ +import type { FeatureProviderProviderServer } from '../../types' + +import { TreeViewFeatureClientComponent } from './feature.client' + +export const TreeViewFeature: FeatureProviderProviderServer = (props) => { + return { + feature: () => { + return { + ClientComponent: TreeViewFeatureClientComponent, + serverFeatureProps: props, + } + }, + key: 'treeview', + serverFeatureProps: props, + } +} diff --git a/packages/richtext-lexical/src/field/features/debug/TreeView/index.scss b/packages/richtext-lexical/src/field/features/debug/treeview/plugin/index.scss similarity index 100% rename from packages/richtext-lexical/src/field/features/debug/TreeView/index.scss rename to packages/richtext-lexical/src/field/features/debug/treeview/plugin/index.scss diff --git a/packages/richtext-lexical/src/field/features/debug/TreeView/plugin.tsx b/packages/richtext-lexical/src/field/features/debug/treeview/plugin/index.tsx similarity index 92% rename from packages/richtext-lexical/src/field/features/debug/TreeView/plugin.tsx rename to packages/richtext-lexical/src/field/features/debug/treeview/plugin/index.tsx index e06dd5301..bea3da1f2 100644 --- a/packages/richtext-lexical/src/field/features/debug/TreeView/plugin.tsx +++ b/packages/richtext-lexical/src/field/features/debug/treeview/plugin/index.tsx @@ -5,7 +5,7 @@ import * as React from 'react' import './index.scss' -export function TreeViewPlugin(): JSX.Element { +export function TreeViewPlugin(): React.ReactNode { const [editor] = useLexicalComposerContext() return ( { +const BoldFeatureClient: FeatureProviderProviderClient = (props) => { return { - dependenciesSoft: ['italic'], + clientFeatureProps: props, feature: ({ featureProviderMap }) => { const markdownTransformers = [BOLD_STAR, BOLD_UNDERSCORE] if (featureProviderMap.get('italic')) { @@ -20,13 +23,12 @@ export const BoldTextFeature = (): FeatureProvider => { } return { + clientFeatureProps: props, floatingSelectToolbar: { sections: [ SectionWithEntries([ { - ChildComponent: () => - // @ts-expect-error-next-line - import('../../../lexical/ui/icons/Bold').then((module) => module.BoldIcon), + ChildComponent: BoldIcon, isActive: ({ selection }) => { if ($isRangeSelection(selection)) { return selection.hasFormat('bold') @@ -42,10 +44,10 @@ export const BoldTextFeature = (): FeatureProvider => { ]), ], }, - markdownTransformers: markdownTransformers, - props: null, + markdownTransformers, } }, - key: 'bold', } } + +export const BoldFeatureClientComponent = createClientComponent(BoldFeatureClient) diff --git a/packages/richtext-lexical/src/field/features/format/bold/feature.server.ts b/packages/richtext-lexical/src/field/features/format/bold/feature.server.ts new file mode 100644 index 000000000..bf0357c6d --- /dev/null +++ b/packages/richtext-lexical/src/field/features/format/bold/feature.server.ts @@ -0,0 +1,29 @@ +import type { FeatureProviderProviderServer } from '../../types' + +import { BoldFeatureClientComponent } from './feature.client' +import { + BOLD_ITALIC_STAR, + BOLD_ITALIC_UNDERSCORE, + BOLD_STAR, + BOLD_UNDERSCORE, +} from './markdownTransformers' + +export const BoldFeature: FeatureProviderProviderServer = (props) => { + return { + dependenciesSoft: ['italic'], + feature: ({ featureProviderMap }) => { + const markdownTransformers = [BOLD_STAR, BOLD_UNDERSCORE] + if (featureProviderMap.get('italic')) { + markdownTransformers.push(BOLD_ITALIC_UNDERSCORE, BOLD_ITALIC_STAR) + } + + return { + ClientComponent: BoldFeatureClientComponent, + markdownTransformers, + serverFeatureProps: props, + } + }, + key: 'bold', + serverFeatureProps: props, + } +} diff --git a/packages/richtext-lexical/src/field/features/format/Bold/markdownTransformers.ts b/packages/richtext-lexical/src/field/features/format/bold/markdownTransformers.ts similarity index 100% rename from packages/richtext-lexical/src/field/features/format/Bold/markdownTransformers.ts rename to packages/richtext-lexical/src/field/features/format/bold/markdownTransformers.ts diff --git a/packages/richtext-lexical/src/field/features/format/InlineCode/index.ts b/packages/richtext-lexical/src/field/features/format/inlinecode/feature.client.tsx similarity index 64% rename from packages/richtext-lexical/src/field/features/format/InlineCode/index.ts rename to packages/richtext-lexical/src/field/features/format/inlinecode/feature.client.tsx index 9fa8da06f..92efc2aff 100644 --- a/packages/richtext-lexical/src/field/features/format/InlineCode/index.ts +++ b/packages/richtext-lexical/src/field/features/format/inlinecode/feature.client.tsx @@ -1,21 +1,25 @@ +'use client' + import { $isRangeSelection, FORMAT_TEXT_COMMAND } from 'lexical' -import type { FeatureProvider } from '../../types' +import type { FeatureProviderProviderClient } from '../../types' +import { CodeIcon } from '../../../lexical/ui/icons/Code' +import { createClientComponent } from '../../createClientComponent' import { SectionWithEntries } from '../common/floatingSelectToolbarSection' import { INLINE_CODE } from './markdownTransformers' -export const InlineCodeTextFeature = (): FeatureProvider => { +const InlineCodeFeatureClient: FeatureProviderProviderClient = (props) => { return { + clientFeatureProps: props, feature: () => { return { + clientFeatureProps: props, floatingSelectToolbar: { sections: [ SectionWithEntries([ { - ChildComponent: () => - // @ts-expect-error-next-line - import('../../../lexical/ui/icons/Code').then((module) => module.CodeIcon), + ChildComponent: CodeIcon, isActive: ({ selection }) => { if ($isRangeSelection(selection)) { return selection.hasFormat('code') @@ -31,10 +35,11 @@ export const InlineCodeTextFeature = (): FeatureProvider => { ]), ], }, + markdownTransformers: [INLINE_CODE], - props: null, } }, - key: 'inlineCode', } } + +export const InlineCodeFeatureClientComponent = createClientComponent(InlineCodeFeatureClient) diff --git a/packages/richtext-lexical/src/field/features/format/inlinecode/feature.server.ts b/packages/richtext-lexical/src/field/features/format/inlinecode/feature.server.ts new file mode 100644 index 000000000..3f0eabb1a --- /dev/null +++ b/packages/richtext-lexical/src/field/features/format/inlinecode/feature.server.ts @@ -0,0 +1,18 @@ +import type { FeatureProviderProviderServer } from '../../types' + +import { InlineCodeFeatureClientComponent } from './feature.client' +import { INLINE_CODE } from './markdownTransformers' + +export const InlineCodeFeature: FeatureProviderProviderServer = (props) => { + return { + feature: () => { + return { + ClientComponent: InlineCodeFeatureClientComponent, + markdownTransformers: [INLINE_CODE], + serverFeatureProps: props, + } + }, + key: 'inlinecode', + serverFeatureProps: props, + } +} diff --git a/packages/richtext-lexical/src/field/features/format/InlineCode/markdownTransformers.ts b/packages/richtext-lexical/src/field/features/format/inlinecode/markdownTransformers.ts similarity index 100% rename from packages/richtext-lexical/src/field/features/format/InlineCode/markdownTransformers.ts rename to packages/richtext-lexical/src/field/features/format/inlinecode/markdownTransformers.ts diff --git a/packages/richtext-lexical/src/field/features/format/Italic/index.ts b/packages/richtext-lexical/src/field/features/format/italic/feature.client.tsx similarity index 65% rename from packages/richtext-lexical/src/field/features/format/Italic/index.ts rename to packages/richtext-lexical/src/field/features/format/italic/feature.client.tsx index cdd15dcca..1c874f18f 100644 --- a/packages/richtext-lexical/src/field/features/format/Italic/index.ts +++ b/packages/richtext-lexical/src/field/features/format/italic/feature.client.tsx @@ -1,21 +1,26 @@ +'use client' + import { $isRangeSelection, FORMAT_TEXT_COMMAND } from 'lexical' -import type { FeatureProvider } from '../../types' +import type { FeatureProviderProviderClient } from '../../types' +import { ItalicIcon } from '../../../lexical/ui/icons/Italic' +import { createClientComponent } from '../../createClientComponent' import { SectionWithEntries } from '../common/floatingSelectToolbarSection' import { ITALIC_STAR, ITALIC_UNDERSCORE } from './markdownTransformers' -export const ItalicTextFeature = (): FeatureProvider => { +const ItalicFeatureClient: FeatureProviderProviderClient = (props) => { return { + clientFeatureProps: props, feature: () => { return { + clientFeatureProps: props, + floatingSelectToolbar: { sections: [ SectionWithEntries([ { - ChildComponent: () => - // @ts-expect-error-next-line - import('../../../lexical/ui/icons/Italic').then((module) => module.ItalicIcon), + ChildComponent: ItalicIcon, isActive: ({ selection }) => { if ($isRangeSelection(selection)) { return selection.hasFormat('italic') @@ -32,9 +37,9 @@ export const ItalicTextFeature = (): FeatureProvider => { ], }, markdownTransformers: [ITALIC_STAR, ITALIC_UNDERSCORE], - props: null, } }, - key: 'italic', } } + +export const ItalicFeatureClientComponent = createClientComponent(ItalicFeatureClient) diff --git a/packages/richtext-lexical/src/field/features/format/italic/feature.server.ts b/packages/richtext-lexical/src/field/features/format/italic/feature.server.ts new file mode 100644 index 000000000..db69135e4 --- /dev/null +++ b/packages/richtext-lexical/src/field/features/format/italic/feature.server.ts @@ -0,0 +1,18 @@ +import type { FeatureProviderProviderServer } from '../../types' + +import { ItalicFeatureClientComponent } from './feature.client' +import { ITALIC_STAR, ITALIC_UNDERSCORE } from './markdownTransformers' + +export const ItalicFeature: FeatureProviderProviderServer = (props) => { + return { + feature: () => { + return { + ClientComponent: ItalicFeatureClientComponent, + markdownTransformers: [ITALIC_STAR, ITALIC_UNDERSCORE], + serverFeatureProps: props, + } + }, + key: 'italic', + serverFeatureProps: props, + } +} diff --git a/packages/richtext-lexical/src/field/features/format/Italic/markdownTransformers.ts b/packages/richtext-lexical/src/field/features/format/italic/markdownTransformers.ts similarity index 100% rename from packages/richtext-lexical/src/field/features/format/Italic/markdownTransformers.ts rename to packages/richtext-lexical/src/field/features/format/italic/markdownTransformers.ts diff --git a/packages/richtext-lexical/src/field/features/format/strikethrough/index.ts b/packages/richtext-lexical/src/field/features/format/strikethrough/feature.client.tsx similarity index 63% rename from packages/richtext-lexical/src/field/features/format/strikethrough/index.ts rename to packages/richtext-lexical/src/field/features/format/strikethrough/feature.client.tsx index 949ec80b9..79cc2782c 100644 --- a/packages/richtext-lexical/src/field/features/format/strikethrough/index.ts +++ b/packages/richtext-lexical/src/field/features/format/strikethrough/feature.client.tsx @@ -1,23 +1,26 @@ +'use client' + import { $isRangeSelection, FORMAT_TEXT_COMMAND } from 'lexical' -import type { FeatureProvider } from '../../types' +import type { FeatureProviderProviderClient } from '../../types' +import { StrikethroughIcon } from '../../../lexical/ui/icons/Strikethrough' +import { createClientComponent } from '../../createClientComponent' import { SectionWithEntries } from '../common/floatingSelectToolbarSection' import { STRIKETHROUGH } from './markdownTransformers' -export const StrikethroughTextFeature = (): FeatureProvider => { +const StrikethroughFeatureClient: FeatureProviderProviderClient = (props) => { return { + clientFeatureProps: props, feature: () => { return { + clientFeatureProps: props, + floatingSelectToolbar: { sections: [ SectionWithEntries([ { - ChildComponent: () => - // @ts-expect-error-next-line - import('../../../lexical/ui/icons/Strikethrough').then( - (module) => module.StrikethroughIcon, - ), + ChildComponent: StrikethroughIcon, isActive: ({ selection }) => { if ($isRangeSelection(selection)) { return selection.hasFormat('strikethrough') @@ -34,9 +37,9 @@ export const StrikethroughTextFeature = (): FeatureProvider => { ], }, markdownTransformers: [STRIKETHROUGH], - props: null, } }, - key: 'strikethrough', } } + +export const StrikethroughFeatureClientComponent = createClientComponent(StrikethroughFeatureClient) diff --git a/packages/richtext-lexical/src/field/features/format/strikethrough/feature.server.ts b/packages/richtext-lexical/src/field/features/format/strikethrough/feature.server.ts new file mode 100644 index 000000000..5f98bdbbe --- /dev/null +++ b/packages/richtext-lexical/src/field/features/format/strikethrough/feature.server.ts @@ -0,0 +1,21 @@ +import type { FeatureProviderProviderServer } from '../../types' + +import { StrikethroughFeatureClientComponent } from './feature.client' +import { STRIKETHROUGH } from './markdownTransformers' + +export const StrikethroughFeature: FeatureProviderProviderServer = ( + props, +) => { + return { + feature: () => { + return { + ClientComponent: StrikethroughFeatureClientComponent, + + markdownTransformers: [STRIKETHROUGH], + serverFeatureProps: props, + } + }, + key: 'strikethrough', + serverFeatureProps: props, + } +} diff --git a/packages/richtext-lexical/src/field/features/format/subscript/index.ts b/packages/richtext-lexical/src/field/features/format/subscript/feature.client.tsx similarity index 61% rename from packages/richtext-lexical/src/field/features/format/subscript/index.ts rename to packages/richtext-lexical/src/field/features/format/subscript/feature.client.tsx index 962e816f7..4c1904b0a 100644 --- a/packages/richtext-lexical/src/field/features/format/subscript/index.ts +++ b/packages/richtext-lexical/src/field/features/format/subscript/feature.client.tsx @@ -1,22 +1,24 @@ +'use client' + import { $isRangeSelection, FORMAT_TEXT_COMMAND } from 'lexical' -import type { FeatureProvider } from '../../types' +import type { FeatureProviderProviderClient } from '../../types' +import { SubscriptIcon } from '../../../lexical/ui/icons/Subscript' +import { createClientComponent } from '../../createClientComponent' import { SectionWithEntries } from '../common/floatingSelectToolbarSection' -export const SubscriptTextFeature = (): FeatureProvider => { +const SubscriptFeatureClient: FeatureProviderProviderClient = (props) => { return { + clientFeatureProps: props, feature: () => { return { + clientFeatureProps: props, floatingSelectToolbar: { sections: [ SectionWithEntries([ { - ChildComponent: () => - // @ts-expect-error-next-line - import('../../../lexical/ui/icons/Subscript').then( - (module) => module.SubscriptIcon, - ), + ChildComponent: SubscriptIcon, isActive: ({ selection }) => { if ($isRangeSelection(selection)) { return selection.hasFormat('subscript') @@ -32,9 +34,9 @@ export const SubscriptTextFeature = (): FeatureProvider => { ]), ], }, - props: null, } }, - key: 'subscript', } } + +export const SubscriptFeatureClientComponent = createClientComponent(SubscriptFeatureClient) diff --git a/packages/richtext-lexical/src/field/features/format/subscript/feature.server.ts b/packages/richtext-lexical/src/field/features/format/subscript/feature.server.ts new file mode 100644 index 000000000..a02f058d3 --- /dev/null +++ b/packages/richtext-lexical/src/field/features/format/subscript/feature.server.ts @@ -0,0 +1,16 @@ +import type { FeatureProviderProviderServer } from '../../types' + +import { SubscriptFeatureClientComponent } from './feature.client' + +export const SubscriptFeature: FeatureProviderProviderServer = (props) => { + return { + feature: () => { + return { + ClientComponent: SubscriptFeatureClientComponent, + serverFeatureProps: props, + } + }, + key: 'subscript', + serverFeatureProps: props, + } +} diff --git a/packages/richtext-lexical/src/field/features/format/superscript/index.ts b/packages/richtext-lexical/src/field/features/format/superscript/feature.client.tsx similarity index 60% rename from packages/richtext-lexical/src/field/features/format/superscript/index.ts rename to packages/richtext-lexical/src/field/features/format/superscript/feature.client.tsx index 0e2939c35..e59f48771 100644 --- a/packages/richtext-lexical/src/field/features/format/superscript/index.ts +++ b/packages/richtext-lexical/src/field/features/format/superscript/feature.client.tsx @@ -1,22 +1,24 @@ +'use client' + import { $isRangeSelection, FORMAT_TEXT_COMMAND } from 'lexical' -import type { FeatureProvider } from '../../types' +import type { FeatureProviderProviderClient } from '../../types' +import { SuperscriptIcon } from '../../../lexical/ui/icons/Superscript' +import { createClientComponent } from '../../createClientComponent' import { SectionWithEntries } from '../common/floatingSelectToolbarSection' -export const SuperscriptTextFeature = (): FeatureProvider => { +const SuperscriptFeatureClient: FeatureProviderProviderClient = (props) => { return { + clientFeatureProps: props, feature: () => { return { + clientFeatureProps: props, floatingSelectToolbar: { sections: [ SectionWithEntries([ { - ChildComponent: () => - // @ts-expect-error-next-line - import('../../../lexical/ui/icons/Superscript').then( - (module) => module.SuperscriptIcon, - ), + ChildComponent: SuperscriptIcon, isActive: ({ selection }) => { if ($isRangeSelection(selection)) { return selection.hasFormat('superscript') @@ -32,9 +34,9 @@ export const SuperscriptTextFeature = (): FeatureProvider => { ]), ], }, - props: null, } }, - key: 'superscript', } } + +export const SuperscriptFeatureClientComponent = createClientComponent(SuperscriptFeatureClient) diff --git a/packages/richtext-lexical/src/field/features/format/superscript/feature.server.ts b/packages/richtext-lexical/src/field/features/format/superscript/feature.server.ts new file mode 100644 index 000000000..080b91a5d --- /dev/null +++ b/packages/richtext-lexical/src/field/features/format/superscript/feature.server.ts @@ -0,0 +1,16 @@ +import type { FeatureProviderProviderServer } from '../../types' + +import { SuperscriptFeatureClientComponent } from './feature.client' + +export const SuperscriptFeature: FeatureProviderProviderServer = (props) => { + return { + feature: () => { + return { + ClientComponent: SuperscriptFeatureClientComponent, + serverFeatureProps: props, + } + }, + key: 'superscript', + serverFeatureProps: props, + } +} diff --git a/packages/richtext-lexical/src/field/features/format/underline/index.ts b/packages/richtext-lexical/src/field/features/format/underline/feature.client.tsx similarity index 61% rename from packages/richtext-lexical/src/field/features/format/underline/index.ts rename to packages/richtext-lexical/src/field/features/format/underline/feature.client.tsx index d5e4cf26b..e26bf06ba 100644 --- a/packages/richtext-lexical/src/field/features/format/underline/index.ts +++ b/packages/richtext-lexical/src/field/features/format/underline/feature.client.tsx @@ -1,22 +1,24 @@ +'use client' + import { $isRangeSelection, FORMAT_TEXT_COMMAND } from 'lexical' -import type { FeatureProvider } from '../../types' +import type { FeatureProviderProviderClient } from '../../types' +import { UnderlineIcon } from '../../../lexical/ui/icons/Underline' +import { createClientComponent } from '../../createClientComponent' import { SectionWithEntries } from '../common/floatingSelectToolbarSection' -export const UnderlineTextFeature = (): FeatureProvider => { +const UnderlineFeatureClient: FeatureProviderProviderClient = (props) => { return { + clientFeatureProps: props, feature: () => { return { + clientFeatureProps: props, floatingSelectToolbar: { sections: [ SectionWithEntries([ { - ChildComponent: () => - // @ts-expect-error-next-line - import('../../../lexical/ui/icons/Underline').then( - (module) => module.UnderlineIcon, - ), + ChildComponent: UnderlineIcon, isActive: ({ selection }) => { if ($isRangeSelection(selection)) { return selection.hasFormat('underline') @@ -32,9 +34,9 @@ export const UnderlineTextFeature = (): FeatureProvider => { ]), ], }, - props: null, } }, - key: 'underline', } } + +export const UnderlineFeatureClientComponent = createClientComponent(UnderlineFeatureClient) diff --git a/packages/richtext-lexical/src/field/features/format/underline/feature.server.ts b/packages/richtext-lexical/src/field/features/format/underline/feature.server.ts new file mode 100644 index 000000000..4f9fc087c --- /dev/null +++ b/packages/richtext-lexical/src/field/features/format/underline/feature.server.ts @@ -0,0 +1,16 @@ +import type { FeatureProviderProviderServer } from '../../types' + +import { UnderlineFeatureClientComponent } from './feature.client' + +export const UnderlineFeature: FeatureProviderProviderServer = (props) => { + return { + feature: () => { + return { + ClientComponent: UnderlineFeatureClientComponent, + serverFeatureProps: props, + } + }, + key: 'underline', + serverFeatureProps: props, + } +} diff --git a/packages/richtext-lexical/src/field/features/Heading/index.ts b/packages/richtext-lexical/src/field/features/heading/feature.client.tsx similarity index 51% rename from packages/richtext-lexical/src/field/features/Heading/index.ts rename to packages/richtext-lexical/src/field/features/heading/feature.client.tsx index f4211d0b3..a910bc9ab 100644 --- a/packages/richtext-lexical/src/field/features/Heading/index.ts +++ b/packages/richtext-lexical/src/field/features/heading/feature.client.tsx @@ -1,15 +1,24 @@ -import type { HeadingTagType, SerializedHeadingNode } from '@lexical/rich-text' +'use client' -import { $createHeadingNode, HeadingNode } from '@lexical/rich-text' +import type { HeadingTagType } from '@lexical/rich-text' + +import { HeadingNode } from '@lexical/rich-text' +import { $createHeadingNode } from '@lexical/rich-text' import { $setBlocksType } from '@lexical/selection' import { $getSelection } from 'lexical' -import type { HTMLConverter } from '../converters/html/converter/types' -import type { FeatureProvider } from '../types' +import type { FeatureProviderProviderClient } from '../types' +import type { HeadingFeatureProps } from './feature.server' import { SlashMenuOption } from '../../lexical/plugins/SlashMenu/LexicalTypeaheadMenuPlugin/types' +import { H1Icon } from '../../lexical/ui/icons/H1' +import { H2Icon } from '../../lexical/ui/icons/H2' +import { H3Icon } from '../../lexical/ui/icons/H3' +import { H4Icon } from '../../lexical/ui/icons/H4' +import { H5Icon } from '../../lexical/ui/icons/H5' +import { H6Icon } from '../../lexical/ui/icons/H6' import { TextDropdownSectionWithEntries } from '../common/floatingSelectToolbarTextDropdownSection' -import { convertLexicalNodesToHTML } from '../converters/html/converter' +import { createClientComponent } from '../createClientComponent' import { MarkdownTransformer } from './markdownTransformer' const setHeading = (headingSize: HeadingTagType) => { @@ -17,31 +26,23 @@ const setHeading = (headingSize: HeadingTagType) => { $setBlocksType(selection, () => $createHeadingNode(headingSize)) } -type Props = { - enabledHeadingSizes?: HeadingTagType[] -} - const iconImports = { - // @ts-expect-error-next-line - h1: () => import('../../lexical/ui/icons/H1').then((module) => module.H1Icon), - // @ts-expect-error-next-line - h2: () => import('../../lexical/ui/icons/H2').then((module) => module.H2Icon), - // @ts-expect-error-next-line - h3: () => import('../../lexical/ui/icons/H3').then((module) => module.H3Icon), - // @ts-expect-error-next-line - h4: () => import('../../lexical/ui/icons/H4').then((module) => module.H4Icon), - // @ts-expect-error-next-line - h5: () => import('../../lexical/ui/icons/H5').then((module) => module.H5Icon), - // @ts-expect-error-next-line - h6: () => import('../../lexical/ui/icons/H6').then((module) => module.H6Icon), + h1: H1Icon, + h2: H2Icon, + h3: H3Icon, + h4: H4Icon, + h5: H5Icon, + h6: H6Icon, } -export const HeadingFeature = (props: Props): FeatureProvider => { +const HeadingFeatureClient: FeatureProviderProviderClient = (props) => { const { enabledHeadingSizes = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'] } = props return { + clientFeatureProps: props, feature: () => { return { + clientFeatureProps: props, floatingSelectToolbar: { sections: [ ...enabledHeadingSizes.map((headingSize, i) => @@ -63,30 +64,7 @@ export const HeadingFeature = (props: Props): FeatureProvider => { ], }, markdownTransformers: [MarkdownTransformer(enabledHeadingSizes)], - nodes: [ - { - type: HeadingNode.getType(), - converters: { - html: { - converter: async ({ converters, node, parent }) => { - const childrenText = await convertLexicalNodesToHTML({ - converters, - lexicalNodes: node.children, - parent: { - ...node, - parent, - }, - }) - - return '<' + node?.tag + '>' + childrenText + '' - }, - nodeTypes: [HeadingNode.getType()], - } as HTMLConverter, - }, - node: HeadingNode, - }, - ], - props, + nodes: [HeadingNode], slashMenu: { options: [ ...enabledHeadingSizes.map((headingSize) => { @@ -109,6 +87,7 @@ export const HeadingFeature = (props: Props): FeatureProvider => { }, } }, - key: 'heading', } } + +export const HeadingFeatureClientComponent = createClientComponent(HeadingFeatureClient) diff --git a/packages/richtext-lexical/src/field/features/heading/feature.server.ts b/packages/richtext-lexical/src/field/features/heading/feature.server.ts new file mode 100644 index 000000000..aabb20d33 --- /dev/null +++ b/packages/richtext-lexical/src/field/features/heading/feature.server.ts @@ -0,0 +1,60 @@ +import type { HeadingTagType } from '@lexical/rich-text' + +import { HeadingNode, type SerializedHeadingNode } from '@lexical/rich-text' + +import type { HTMLConverter } from '../converters/html/converter/types' +import type { FeatureProviderProviderServer } from '../types' + +import { convertLexicalNodesToHTML } from '../converters/html/converter' +import { HeadingFeatureClientComponent } from './feature.client' +import { MarkdownTransformer } from './markdownTransformer' + +export type HeadingFeatureProps = { + enabledHeadingSizes?: HeadingTagType[] +} + +export const HeadingFeature: FeatureProviderProviderServer< + HeadingFeatureProps, + HeadingFeatureProps +> = (props) => { + if (!props) { + props = {} + } + + const { enabledHeadingSizes = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'] } = props + + return { + feature: () => { + return { + ClientComponent: HeadingFeatureClientComponent, + markdownTransformers: [MarkdownTransformer(enabledHeadingSizes)], + nodes: [ + { + type: HeadingNode.getType(), + converters: { + html: { + converter: async ({ converters, node, parent }) => { + const childrenText = await convertLexicalNodesToHTML({ + converters, + lexicalNodes: node.children, + parent: { + ...node, + parent, + }, + }) + + return '<' + node?.tag + '>' + childrenText + '' + }, + nodeTypes: [HeadingNode.getType()], + } as HTMLConverter, + }, + node: HeadingNode, + }, + ], + serverFeatureProps: props, + } + }, + key: 'heading', + serverFeatureProps: props, + } +} diff --git a/packages/richtext-lexical/src/field/features/Heading/markdownTransformer.ts b/packages/richtext-lexical/src/field/features/heading/markdownTransformer.ts similarity index 100% rename from packages/richtext-lexical/src/field/features/Heading/markdownTransformer.ts rename to packages/richtext-lexical/src/field/features/heading/markdownTransformer.ts diff --git a/packages/richtext-lexical/src/field/features/lists/CheckList/index.ts b/packages/richtext-lexical/src/field/features/lists/checklist/index.ts similarity index 99% rename from packages/richtext-lexical/src/field/features/lists/CheckList/index.ts rename to packages/richtext-lexical/src/field/features/lists/checklist/index.ts index 96e5ead1f..5749c12e2 100644 --- a/packages/richtext-lexical/src/field/features/lists/CheckList/index.ts +++ b/packages/richtext-lexical/src/field/features/lists/checklist/index.ts @@ -86,6 +86,6 @@ export const CheckListFeature = (): FeatureProvider => { }, } }, - key: 'checkList', + key: 'checklist', } } diff --git a/packages/richtext-lexical/src/field/features/lists/CheckList/markdownTransformers.ts b/packages/richtext-lexical/src/field/features/lists/checklist/markdownTransformers.ts similarity index 100% rename from packages/richtext-lexical/src/field/features/lists/CheckList/markdownTransformers.ts rename to packages/richtext-lexical/src/field/features/lists/checklist/markdownTransformers.ts diff --git a/packages/richtext-lexical/src/field/features/lists/CheckList/plugin/index.tsx b/packages/richtext-lexical/src/field/features/lists/checklist/plugin/index.tsx similarity index 100% rename from packages/richtext-lexical/src/field/features/lists/CheckList/plugin/index.tsx rename to packages/richtext-lexical/src/field/features/lists/checklist/plugin/index.tsx diff --git a/packages/richtext-lexical/src/field/features/lists/OrderedList/index.ts b/packages/richtext-lexical/src/field/features/lists/orderedlist/index.ts similarity index 99% rename from packages/richtext-lexical/src/field/features/lists/OrderedList/index.ts rename to packages/richtext-lexical/src/field/features/lists/orderedlist/index.ts index 845fd99bc..2387c2f79 100644 --- a/packages/richtext-lexical/src/field/features/lists/OrderedList/index.ts +++ b/packages/richtext-lexical/src/field/features/lists/orderedlist/index.ts @@ -85,6 +85,6 @@ export const OrderedListFeature = (): FeatureProvider => { }, } }, - key: 'orderedList', + key: 'orderedlist', } } diff --git a/packages/richtext-lexical/src/field/features/lists/OrderedList/markdownTransformer.ts b/packages/richtext-lexical/src/field/features/lists/orderedlist/markdownTransformer.ts similarity index 100% rename from packages/richtext-lexical/src/field/features/lists/OrderedList/markdownTransformer.ts rename to packages/richtext-lexical/src/field/features/lists/orderedlist/markdownTransformer.ts diff --git a/packages/richtext-lexical/src/field/features/lists/UnorderedList/index.ts b/packages/richtext-lexical/src/field/features/lists/unorderedlist/index.ts similarity index 99% rename from packages/richtext-lexical/src/field/features/lists/UnorderedList/index.ts rename to packages/richtext-lexical/src/field/features/lists/unorderedlist/index.ts index 85c969102..7bbe7b1d5 100644 --- a/packages/richtext-lexical/src/field/features/lists/UnorderedList/index.ts +++ b/packages/richtext-lexical/src/field/features/lists/unorderedlist/index.ts @@ -81,6 +81,6 @@ export const UnorderedListFeature = (): FeatureProvider => { }, } }, - key: 'unorderedList', + key: 'unorderedlist', } } diff --git a/packages/richtext-lexical/src/field/features/lists/UnorderedList/markdownTransformer.ts b/packages/richtext-lexical/src/field/features/lists/unorderedlist/markdownTransformer.ts similarity index 100% rename from packages/richtext-lexical/src/field/features/lists/UnorderedList/markdownTransformer.ts rename to packages/richtext-lexical/src/field/features/lists/unorderedlist/markdownTransformer.ts diff --git a/packages/richtext-lexical/src/field/features/migrations/LexicalPluginToLexical/converter/converters/heading.ts b/packages/richtext-lexical/src/field/features/migrations/lexicalPluginToLexical/converter/converters/heading.ts similarity index 100% rename from packages/richtext-lexical/src/field/features/migrations/LexicalPluginToLexical/converter/converters/heading.ts rename to packages/richtext-lexical/src/field/features/migrations/lexicalPluginToLexical/converter/converters/heading.ts diff --git a/packages/richtext-lexical/src/field/features/migrations/LexicalPluginToLexical/converter/converters/link.ts b/packages/richtext-lexical/src/field/features/migrations/lexicalPluginToLexical/converter/converters/link.ts similarity index 100% rename from packages/richtext-lexical/src/field/features/migrations/LexicalPluginToLexical/converter/converters/link.ts rename to packages/richtext-lexical/src/field/features/migrations/lexicalPluginToLexical/converter/converters/link.ts diff --git a/packages/richtext-lexical/src/field/features/migrations/LexicalPluginToLexical/converter/converters/list.ts b/packages/richtext-lexical/src/field/features/migrations/lexicalPluginToLexical/converter/converters/list.ts similarity index 100% rename from packages/richtext-lexical/src/field/features/migrations/LexicalPluginToLexical/converter/converters/list.ts rename to packages/richtext-lexical/src/field/features/migrations/lexicalPluginToLexical/converter/converters/list.ts diff --git a/packages/richtext-lexical/src/field/features/migrations/LexicalPluginToLexical/converter/converters/listItem.ts b/packages/richtext-lexical/src/field/features/migrations/lexicalPluginToLexical/converter/converters/listItem.ts similarity index 100% rename from packages/richtext-lexical/src/field/features/migrations/LexicalPluginToLexical/converter/converters/listItem.ts rename to packages/richtext-lexical/src/field/features/migrations/lexicalPluginToLexical/converter/converters/listItem.ts diff --git a/packages/richtext-lexical/src/field/features/migrations/LexicalPluginToLexical/converter/converters/quote.ts b/packages/richtext-lexical/src/field/features/migrations/lexicalPluginToLexical/converter/converters/quote.ts similarity index 100% rename from packages/richtext-lexical/src/field/features/migrations/LexicalPluginToLexical/converter/converters/quote.ts rename to packages/richtext-lexical/src/field/features/migrations/lexicalPluginToLexical/converter/converters/quote.ts diff --git a/packages/richtext-lexical/src/field/features/migrations/LexicalPluginToLexical/converter/converters/unknown.ts b/packages/richtext-lexical/src/field/features/migrations/lexicalPluginToLexical/converter/converters/unknown.ts similarity index 100% rename from packages/richtext-lexical/src/field/features/migrations/LexicalPluginToLexical/converter/converters/unknown.ts rename to packages/richtext-lexical/src/field/features/migrations/lexicalPluginToLexical/converter/converters/unknown.ts diff --git a/packages/richtext-lexical/src/field/features/migrations/LexicalPluginToLexical/converter/converters/upload.ts b/packages/richtext-lexical/src/field/features/migrations/lexicalPluginToLexical/converter/converters/upload.ts similarity index 100% rename from packages/richtext-lexical/src/field/features/migrations/LexicalPluginToLexical/converter/converters/upload.ts rename to packages/richtext-lexical/src/field/features/migrations/lexicalPluginToLexical/converter/converters/upload.ts diff --git a/packages/richtext-lexical/src/field/features/migrations/LexicalPluginToLexical/converter/defaultConverters.ts b/packages/richtext-lexical/src/field/features/migrations/lexicalPluginToLexical/converter/defaultConverters.ts similarity index 100% rename from packages/richtext-lexical/src/field/features/migrations/LexicalPluginToLexical/converter/defaultConverters.ts rename to packages/richtext-lexical/src/field/features/migrations/lexicalPluginToLexical/converter/defaultConverters.ts diff --git a/packages/richtext-lexical/src/field/features/migrations/LexicalPluginToLexical/converter/index.ts b/packages/richtext-lexical/src/field/features/migrations/lexicalPluginToLexical/converter/index.ts similarity index 100% rename from packages/richtext-lexical/src/field/features/migrations/LexicalPluginToLexical/converter/index.ts rename to packages/richtext-lexical/src/field/features/migrations/lexicalPluginToLexical/converter/index.ts diff --git a/packages/richtext-lexical/src/field/features/migrations/LexicalPluginToLexical/converter/types.ts b/packages/richtext-lexical/src/field/features/migrations/lexicalPluginToLexical/converter/types.ts similarity index 100% rename from packages/richtext-lexical/src/field/features/migrations/LexicalPluginToLexical/converter/types.ts rename to packages/richtext-lexical/src/field/features/migrations/lexicalPluginToLexical/converter/types.ts diff --git a/packages/richtext-lexical/src/field/features/migrations/LexicalPluginToLexical/index.ts b/packages/richtext-lexical/src/field/features/migrations/lexicalPluginToLexical/index.ts similarity index 100% rename from packages/richtext-lexical/src/field/features/migrations/LexicalPluginToLexical/index.ts rename to packages/richtext-lexical/src/field/features/migrations/lexicalPluginToLexical/index.ts diff --git a/packages/richtext-lexical/src/field/features/migrations/LexicalPluginToLexical/nodes/unknownConvertedNode/Component.tsx b/packages/richtext-lexical/src/field/features/migrations/lexicalPluginToLexical/nodes/unknownConvertedNode/Component.tsx similarity index 100% rename from packages/richtext-lexical/src/field/features/migrations/LexicalPluginToLexical/nodes/unknownConvertedNode/Component.tsx rename to packages/richtext-lexical/src/field/features/migrations/lexicalPluginToLexical/nodes/unknownConvertedNode/Component.tsx diff --git a/packages/richtext-lexical/src/field/features/migrations/LexicalPluginToLexical/nodes/unknownConvertedNode/index.scss b/packages/richtext-lexical/src/field/features/migrations/lexicalPluginToLexical/nodes/unknownConvertedNode/index.scss similarity index 100% rename from packages/richtext-lexical/src/field/features/migrations/LexicalPluginToLexical/nodes/unknownConvertedNode/index.scss rename to packages/richtext-lexical/src/field/features/migrations/lexicalPluginToLexical/nodes/unknownConvertedNode/index.scss diff --git a/packages/richtext-lexical/src/field/features/migrations/LexicalPluginToLexical/nodes/unknownConvertedNode/index.tsx b/packages/richtext-lexical/src/field/features/migrations/lexicalPluginToLexical/nodes/unknownConvertedNode/index.tsx similarity index 100% rename from packages/richtext-lexical/src/field/features/migrations/LexicalPluginToLexical/nodes/unknownConvertedNode/index.tsx rename to packages/richtext-lexical/src/field/features/migrations/lexicalPluginToLexical/nodes/unknownConvertedNode/index.tsx diff --git a/packages/richtext-lexical/src/field/features/migrations/SlateToLexical/converter/converters/blockquote.ts b/packages/richtext-lexical/src/field/features/migrations/slateToLexical/converter/converters/blockquote.ts similarity index 100% rename from packages/richtext-lexical/src/field/features/migrations/SlateToLexical/converter/converters/blockquote.ts rename to packages/richtext-lexical/src/field/features/migrations/slateToLexical/converter/converters/blockquote.ts diff --git a/packages/richtext-lexical/src/field/features/migrations/SlateToLexical/converter/converters/heading.ts b/packages/richtext-lexical/src/field/features/migrations/slateToLexical/converter/converters/heading.ts similarity index 100% rename from packages/richtext-lexical/src/field/features/migrations/SlateToLexical/converter/converters/heading.ts rename to packages/richtext-lexical/src/field/features/migrations/slateToLexical/converter/converters/heading.ts diff --git a/packages/richtext-lexical/src/field/features/migrations/SlateToLexical/converter/converters/indent.ts b/packages/richtext-lexical/src/field/features/migrations/slateToLexical/converter/converters/indent.ts similarity index 100% rename from packages/richtext-lexical/src/field/features/migrations/SlateToLexical/converter/converters/indent.ts rename to packages/richtext-lexical/src/field/features/migrations/slateToLexical/converter/converters/indent.ts diff --git a/packages/richtext-lexical/src/field/features/migrations/SlateToLexical/converter/converters/link.ts b/packages/richtext-lexical/src/field/features/migrations/slateToLexical/converter/converters/link.ts similarity index 100% rename from packages/richtext-lexical/src/field/features/migrations/SlateToLexical/converter/converters/link.ts rename to packages/richtext-lexical/src/field/features/migrations/slateToLexical/converter/converters/link.ts diff --git a/packages/richtext-lexical/src/field/features/migrations/SlateToLexical/converter/converters/listItem.ts b/packages/richtext-lexical/src/field/features/migrations/slateToLexical/converter/converters/listItem.ts similarity index 100% rename from packages/richtext-lexical/src/field/features/migrations/SlateToLexical/converter/converters/listItem.ts rename to packages/richtext-lexical/src/field/features/migrations/slateToLexical/converter/converters/listItem.ts diff --git a/packages/richtext-lexical/src/field/features/migrations/SlateToLexical/converter/converters/orderedList.ts b/packages/richtext-lexical/src/field/features/migrations/slateToLexical/converter/converters/orderedList.ts similarity index 100% rename from packages/richtext-lexical/src/field/features/migrations/SlateToLexical/converter/converters/orderedList.ts rename to packages/richtext-lexical/src/field/features/migrations/slateToLexical/converter/converters/orderedList.ts diff --git a/packages/richtext-lexical/src/field/features/migrations/SlateToLexical/converter/converters/relationship.ts b/packages/richtext-lexical/src/field/features/migrations/slateToLexical/converter/converters/relationship.ts similarity index 100% rename from packages/richtext-lexical/src/field/features/migrations/SlateToLexical/converter/converters/relationship.ts rename to packages/richtext-lexical/src/field/features/migrations/slateToLexical/converter/converters/relationship.ts diff --git a/packages/richtext-lexical/src/field/features/migrations/SlateToLexical/converter/converters/unknown.ts b/packages/richtext-lexical/src/field/features/migrations/slateToLexical/converter/converters/unknown.ts similarity index 100% rename from packages/richtext-lexical/src/field/features/migrations/SlateToLexical/converter/converters/unknown.ts rename to packages/richtext-lexical/src/field/features/migrations/slateToLexical/converter/converters/unknown.ts diff --git a/packages/richtext-lexical/src/field/features/migrations/SlateToLexical/converter/converters/unorderedList.ts b/packages/richtext-lexical/src/field/features/migrations/slateToLexical/converter/converters/unorderedList.ts similarity index 100% rename from packages/richtext-lexical/src/field/features/migrations/SlateToLexical/converter/converters/unorderedList.ts rename to packages/richtext-lexical/src/field/features/migrations/slateToLexical/converter/converters/unorderedList.ts diff --git a/packages/richtext-lexical/src/field/features/migrations/SlateToLexical/converter/converters/upload.ts b/packages/richtext-lexical/src/field/features/migrations/slateToLexical/converter/converters/upload.ts similarity index 100% rename from packages/richtext-lexical/src/field/features/migrations/SlateToLexical/converter/converters/upload.ts rename to packages/richtext-lexical/src/field/features/migrations/slateToLexical/converter/converters/upload.ts diff --git a/packages/richtext-lexical/src/field/features/migrations/SlateToLexical/converter/defaultConverters.ts b/packages/richtext-lexical/src/field/features/migrations/slateToLexical/converter/defaultConverters.ts similarity index 100% rename from packages/richtext-lexical/src/field/features/migrations/SlateToLexical/converter/defaultConverters.ts rename to packages/richtext-lexical/src/field/features/migrations/slateToLexical/converter/defaultConverters.ts diff --git a/packages/richtext-lexical/src/field/features/migrations/SlateToLexical/converter/index.ts b/packages/richtext-lexical/src/field/features/migrations/slateToLexical/converter/index.ts similarity index 100% rename from packages/richtext-lexical/src/field/features/migrations/SlateToLexical/converter/index.ts rename to packages/richtext-lexical/src/field/features/migrations/slateToLexical/converter/index.ts diff --git a/packages/richtext-lexical/src/field/features/migrations/SlateToLexical/converter/types.ts b/packages/richtext-lexical/src/field/features/migrations/slateToLexical/converter/types.ts similarity index 100% rename from packages/richtext-lexical/src/field/features/migrations/SlateToLexical/converter/types.ts rename to packages/richtext-lexical/src/field/features/migrations/slateToLexical/converter/types.ts diff --git a/packages/richtext-lexical/src/field/features/migrations/SlateToLexical/index.ts b/packages/richtext-lexical/src/field/features/migrations/slateToLexical/index.ts similarity index 100% rename from packages/richtext-lexical/src/field/features/migrations/SlateToLexical/index.ts rename to packages/richtext-lexical/src/field/features/migrations/slateToLexical/index.ts diff --git a/packages/richtext-lexical/src/field/features/migrations/SlateToLexical/nodes/unknownConvertedNode/Component.tsx b/packages/richtext-lexical/src/field/features/migrations/slateToLexical/nodes/unknownConvertedNode/Component.tsx similarity index 100% rename from packages/richtext-lexical/src/field/features/migrations/SlateToLexical/nodes/unknownConvertedNode/Component.tsx rename to packages/richtext-lexical/src/field/features/migrations/slateToLexical/nodes/unknownConvertedNode/Component.tsx diff --git a/packages/richtext-lexical/src/field/features/migrations/SlateToLexical/nodes/unknownConvertedNode/index.scss b/packages/richtext-lexical/src/field/features/migrations/slateToLexical/nodes/unknownConvertedNode/index.scss similarity index 100% rename from packages/richtext-lexical/src/field/features/migrations/SlateToLexical/nodes/unknownConvertedNode/index.scss rename to packages/richtext-lexical/src/field/features/migrations/slateToLexical/nodes/unknownConvertedNode/index.scss diff --git a/packages/richtext-lexical/src/field/features/migrations/SlateToLexical/nodes/unknownConvertedNode/index.tsx b/packages/richtext-lexical/src/field/features/migrations/slateToLexical/nodes/unknownConvertedNode/index.tsx similarity index 100% rename from packages/richtext-lexical/src/field/features/migrations/SlateToLexical/nodes/unknownConvertedNode/index.tsx rename to packages/richtext-lexical/src/field/features/migrations/slateToLexical/nodes/unknownConvertedNode/index.tsx diff --git a/packages/richtext-lexical/src/field/features/Paragraph/Component.tsx b/packages/richtext-lexical/src/field/features/paragraph/Component.tsx similarity index 100% rename from packages/richtext-lexical/src/field/features/Paragraph/Component.tsx rename to packages/richtext-lexical/src/field/features/paragraph/Component.tsx diff --git a/packages/richtext-lexical/src/field/features/Paragraph/index.ts b/packages/richtext-lexical/src/field/features/paragraph/index.ts similarity index 99% rename from packages/richtext-lexical/src/field/features/Paragraph/index.ts rename to packages/richtext-lexical/src/field/features/paragraph/index.ts index e0bd9a2df..0a9b119c9 100644 --- a/packages/richtext-lexical/src/field/features/Paragraph/index.ts +++ b/packages/richtext-lexical/src/field/features/paragraph/index.ts @@ -61,6 +61,6 @@ export const ParagraphFeature = (): FeatureProvider => { }, } }, - key: key, + key, } } diff --git a/packages/richtext-lexical/src/field/features/Relationship/drawer/commands.ts b/packages/richtext-lexical/src/field/features/relationship/drawer/commands.ts similarity index 100% rename from packages/richtext-lexical/src/field/features/Relationship/drawer/commands.ts rename to packages/richtext-lexical/src/field/features/relationship/drawer/commands.ts diff --git a/packages/richtext-lexical/src/field/features/Relationship/drawer/index.tsx b/packages/richtext-lexical/src/field/features/relationship/drawer/index.tsx similarity index 100% rename from packages/richtext-lexical/src/field/features/Relationship/drawer/index.tsx rename to packages/richtext-lexical/src/field/features/relationship/drawer/index.tsx diff --git a/packages/richtext-lexical/src/field/features/Relationship/index.ts b/packages/richtext-lexical/src/field/features/relationship/index.ts similarity index 100% rename from packages/richtext-lexical/src/field/features/Relationship/index.ts rename to packages/richtext-lexical/src/field/features/relationship/index.ts diff --git a/packages/richtext-lexical/src/field/features/Relationship/nodes/RelationshipNode.tsx b/packages/richtext-lexical/src/field/features/relationship/nodes/RelationshipNode.tsx similarity index 100% rename from packages/richtext-lexical/src/field/features/Relationship/nodes/RelationshipNode.tsx rename to packages/richtext-lexical/src/field/features/relationship/nodes/RelationshipNode.tsx diff --git a/packages/richtext-lexical/src/field/features/Relationship/nodes/components/RelationshipComponent.tsx b/packages/richtext-lexical/src/field/features/relationship/nodes/components/RelationshipComponent.tsx similarity index 100% rename from packages/richtext-lexical/src/field/features/Relationship/nodes/components/RelationshipComponent.tsx rename to packages/richtext-lexical/src/field/features/relationship/nodes/components/RelationshipComponent.tsx diff --git a/packages/richtext-lexical/src/field/features/Relationship/nodes/components/index.scss b/packages/richtext-lexical/src/field/features/relationship/nodes/components/index.scss similarity index 100% rename from packages/richtext-lexical/src/field/features/Relationship/nodes/components/index.scss rename to packages/richtext-lexical/src/field/features/relationship/nodes/components/index.scss diff --git a/packages/richtext-lexical/src/field/features/Relationship/plugins/index.tsx b/packages/richtext-lexical/src/field/features/relationship/plugins/index.tsx similarity index 100% rename from packages/richtext-lexical/src/field/features/Relationship/plugins/index.tsx rename to packages/richtext-lexical/src/field/features/relationship/plugins/index.tsx diff --git a/packages/richtext-lexical/src/field/features/Relationship/populationPromise.ts b/packages/richtext-lexical/src/field/features/relationship/populationPromise.ts similarity index 100% rename from packages/richtext-lexical/src/field/features/Relationship/populationPromise.ts rename to packages/richtext-lexical/src/field/features/relationship/populationPromise.ts diff --git a/packages/richtext-lexical/src/field/features/Relationship/utils/EnabledRelationshipsCondition.tsx b/packages/richtext-lexical/src/field/features/relationship/utils/EnabledRelationshipsCondition.tsx similarity index 100% rename from packages/richtext-lexical/src/field/features/Relationship/utils/EnabledRelationshipsCondition.tsx rename to packages/richtext-lexical/src/field/features/relationship/utils/EnabledRelationshipsCondition.tsx diff --git a/packages/richtext-lexical/src/field/features/types.ts b/packages/richtext-lexical/src/field/features/types.ts index 8ec9b1fc2..526d548c1 100644 --- a/packages/richtext-lexical/src/field/features/types.ts +++ b/packages/richtext-lexical/src/field/features/types.ts @@ -170,11 +170,11 @@ export type ClientComponentProps = ClientFeatureProps & { } export type ServerFeature = { - ClientComponent: React.FC> + ClientComponent?: React.FC> /** * This determines what props will be available on the Client. */ - clientFeatureProps: ClientFeatureProps + clientFeatureProps?: ClientFeatureProps generateComponentMap?: (args: { config: SanitizedConfig props: ServerProps diff --git a/packages/richtext-lexical/src/field/features/Upload/component/ExtraFieldsDrawer/index.tsx b/packages/richtext-lexical/src/field/features/upload/component/ExtraFieldsDrawer/index.tsx similarity index 100% rename from packages/richtext-lexical/src/field/features/Upload/component/ExtraFieldsDrawer/index.tsx rename to packages/richtext-lexical/src/field/features/upload/component/ExtraFieldsDrawer/index.tsx diff --git a/packages/richtext-lexical/src/field/features/Upload/component/index.scss b/packages/richtext-lexical/src/field/features/upload/component/index.scss similarity index 100% rename from packages/richtext-lexical/src/field/features/Upload/component/index.scss rename to packages/richtext-lexical/src/field/features/upload/component/index.scss diff --git a/packages/richtext-lexical/src/field/features/Upload/component/index.tsx b/packages/richtext-lexical/src/field/features/upload/component/index.tsx similarity index 97% rename from packages/richtext-lexical/src/field/features/Upload/component/index.tsx rename to packages/richtext-lexical/src/field/features/upload/component/index.tsx index b201f1187..31eaae1d1 100644 --- a/packages/richtext-lexical/src/field/features/Upload/component/index.tsx +++ b/packages/richtext-lexical/src/field/features/upload/component/index.tsx @@ -22,7 +22,7 @@ import type { UploadFeatureProps } from '..' import type { UploadData } from '../nodes/UploadNode' import { useEditorConfigContext } from '../../../lexical/config/client/EditorConfigProvider' -import { EnabledRelationshipsCondition } from '../../Relationship/utils/EnabledRelationshipsCondition' +import { EnabledRelationshipsCondition } from '../../relationship/utils/EnabledRelationshipsCondition' import { INSERT_UPLOAD_WITH_DRAWER_COMMAND } from '../drawer/commands' import { ExtraFieldsUploadDrawer } from './ExtraFieldsDrawer' import './index.scss' @@ -74,7 +74,7 @@ const Component: React.FC = (props) => { { initialParams }, ) - const thumbnailSRC = useThumbnail(relatedCollection, data) + const thumbnailSRC = useThumbnail(relatedCollection.slug, relatedCollection.upload, data) const removeUpload = useCallback(() => { editor.update(() => { diff --git a/packages/richtext-lexical/src/field/features/Upload/drawer/commands.ts b/packages/richtext-lexical/src/field/features/upload/drawer/commands.ts similarity index 100% rename from packages/richtext-lexical/src/field/features/Upload/drawer/commands.ts rename to packages/richtext-lexical/src/field/features/upload/drawer/commands.ts diff --git a/packages/richtext-lexical/src/field/features/Upload/drawer/index.tsx b/packages/richtext-lexical/src/field/features/upload/drawer/index.tsx similarity index 97% rename from packages/richtext-lexical/src/field/features/Upload/drawer/index.tsx rename to packages/richtext-lexical/src/field/features/upload/drawer/index.tsx index 95a94b328..06795ca6f 100644 --- a/packages/richtext-lexical/src/field/features/Upload/drawer/index.tsx +++ b/packages/richtext-lexical/src/field/features/upload/drawer/index.tsx @@ -4,7 +4,7 @@ import { useListDrawer } from '@payloadcms/ui' import { $getNodeByKey, COMMAND_PRIORITY_EDITOR, type LexicalEditor } from 'lexical' import React, { useCallback, useEffect, useState } from 'react' -import { EnabledRelationshipsCondition } from '../../Relationship/utils/EnabledRelationshipsCondition' +import { EnabledRelationshipsCondition } from '../../relationship/utils/EnabledRelationshipsCondition' import { $createUploadNode } from '../nodes/UploadNode' import { INSERT_UPLOAD_COMMAND } from '../plugin' import { INSERT_UPLOAD_WITH_DRAWER_COMMAND } from './commands' diff --git a/packages/richtext-lexical/src/field/features/Upload/index.ts b/packages/richtext-lexical/src/field/features/upload/index.ts similarity index 99% rename from packages/richtext-lexical/src/field/features/Upload/index.ts rename to packages/richtext-lexical/src/field/features/upload/index.ts index 182fca298..54f562591 100644 --- a/packages/richtext-lexical/src/field/features/Upload/index.ts +++ b/packages/richtext-lexical/src/field/features/upload/index.ts @@ -104,7 +104,7 @@ export const UploadFeature = (props?: UploadFeatureProps): FeatureProvider => { position: 'normal', }, ], - props: props, + props, slashMenu: { options: [ { diff --git a/packages/richtext-lexical/src/field/features/Upload/nodes/UploadNode.tsx b/packages/richtext-lexical/src/field/features/upload/nodes/UploadNode.tsx similarity index 100% rename from packages/richtext-lexical/src/field/features/Upload/nodes/UploadNode.tsx rename to packages/richtext-lexical/src/field/features/upload/nodes/UploadNode.tsx diff --git a/packages/richtext-lexical/src/field/features/Upload/plugin/index.tsx b/packages/richtext-lexical/src/field/features/upload/plugin/index.tsx similarity index 100% rename from packages/richtext-lexical/src/field/features/Upload/plugin/index.tsx rename to packages/richtext-lexical/src/field/features/upload/plugin/index.tsx diff --git a/packages/richtext-lexical/src/field/features/Upload/populationPromise.ts b/packages/richtext-lexical/src/field/features/upload/populationPromise.ts similarity index 100% rename from packages/richtext-lexical/src/field/features/Upload/populationPromise.ts rename to packages/richtext-lexical/src/field/features/upload/populationPromise.ts diff --git a/packages/richtext-lexical/src/field/features/Upload/validate.ts b/packages/richtext-lexical/src/field/features/upload/validate.ts similarity index 100% rename from packages/richtext-lexical/src/field/features/Upload/validate.ts rename to packages/richtext-lexical/src/field/features/upload/validate.ts diff --git a/packages/richtext-lexical/src/field/lexical/config/client/sanitize.ts b/packages/richtext-lexical/src/field/lexical/config/client/sanitize.ts index ee881abc0..06d00a1de 100644 --- a/packages/richtext-lexical/src/field/lexical/config/client/sanitize.ts +++ b/packages/richtext-lexical/src/field/lexical/config/client/sanitize.ts @@ -13,11 +13,11 @@ export const sanitizeClientFeatures = ( floatingSelectToolbar: { sections: [], }, - hooks: { load: [], save: [], }, + markdownTransformers: [], nodes: [], plugins: [], slashMenu: { @@ -110,6 +110,11 @@ export const sanitizeClientFeatures = ( } } + if (feature.markdownTransformers?.length) { + sanitized.markdownTransformers = sanitized.markdownTransformers.concat( + feature.markdownTransformers, + ) + } sanitized.enabledFeatures.push(feature.key) }) diff --git a/packages/richtext-lexical/src/field/lexical/config/server/default.ts b/packages/richtext-lexical/src/field/lexical/config/server/default.ts index 357b6030c..2c75cfb66 100644 --- a/packages/richtext-lexical/src/field/lexical/config/server/default.ts +++ b/packages/richtext-lexical/src/field/lexical/config/server/default.ts @@ -3,24 +3,24 @@ import type { EditorConfig as LexicalEditorConfig } from 'lexical/LexicalEditor' import type { FeatureProviderServer } from '../../../features/types' import type { SanitizedServerEditorConfig, ServerEditorConfig } from '../types' -import { HeadingFeature } from '../../../features/Heading' -import { ParagraphFeature } from '../../../features/Paragraph' -import { RelationshipFeature } from '../../../features/Relationship' -import { UploadFeature } from '../../../features/Upload' import { AlignFeature } from '../../../features/align/feature.server' import { BlockQuoteFeature } from '../../../features/blockquote/feature.server' -import { BoldTextFeature } from '../../../features/format/Bold' -import { InlineCodeTextFeature } from '../../../features/format/InlineCode' -import { ItalicTextFeature } from '../../../features/format/Italic' -import { StrikethroughTextFeature } from '../../../features/format/strikethrough' -import { SubscriptTextFeature } from '../../../features/format/subscript' -import { SuperscriptTextFeature } from '../../../features/format/superscript' -import { UnderlineTextFeature } from '../../../features/format/underline' +import { BoldFeature } from '../../../features/format/bold/feature.server' +import { InlineCodeFeature } from '../../../features/format/inlinecode/feature.server' +import { ItalicFeature } from '../../../features/format/italic/feature.server' +import { StrikethroughFeature } from '../../../features/format/strikethrough/feature.server' +import { SubscriptFeature } from '../../../features/format/subscript/feature.server' +import { SuperscriptFeature } from '../../../features/format/superscript/feature.server' +import { UnderlineFeature } from '../../../features/format/underline/feature.server' +import { HeadingFeature } from '../../../features/heading/feature.server' import { IndentFeature } from '../../../features/indent' import { LinkFeature } from '../../../features/link/feature.server' -import { CheckListFeature } from '../../../features/lists/CheckList' -import { OrderedListFeature } from '../../../features/lists/OrderedList' -import { UnorderedListFeature } from '../../../features/lists/UnorderedList' +import { CheckListFeature } from '../../../features/lists/checklist' +import { OrderedListFeature } from '../../../features/lists/orderedlist' +import { UnorderedListFeature } from '../../../features/lists/unorderedlist' +import { ParagraphFeature } from '../../../features/paragraph' +import { RelationshipFeature } from '../../../features/relationship' +import { UploadFeature } from '../../../features/upload' import { LexicalEditorTheme } from '../../theme/EditorTheme' import { sanitizeServerEditorConfig } from './sanitize' @@ -30,13 +30,13 @@ export const defaultEditorLexicalConfig: LexicalEditorConfig = { } export const defaultEditorFeatures: FeatureProviderServer[] = [ - BoldTextFeature(), - ItalicTextFeature(), - UnderlineTextFeature(), - StrikethroughTextFeature(), - SubscriptTextFeature(), - SuperscriptTextFeature(), - InlineCodeTextFeature(), + BoldFeature(), + ItalicFeature(), + UnderlineFeature(), + StrikethroughFeature(), + SubscriptFeature(), + SuperscriptFeature(), + InlineCodeFeature(), ParagraphFeature(), HeadingFeature({}), AlignFeature(), diff --git a/packages/richtext-lexical/src/generateComponentMap.tsx b/packages/richtext-lexical/src/generateComponentMap.tsx index e6a09e5f6..e27e7d2ef 100644 --- a/packages/richtext-lexical/src/generateComponentMap.tsx +++ b/packages/richtext-lexical/src/generateComponentMap.tsx @@ -23,81 +23,87 @@ export const getGenerateComponentMap = componentMap.set( `features`, - resolvedFeatureMapArray.map(([featureKey, resolvedFeature]) => { - const ClientComponent = resolvedFeature.ClientComponent - const clientComponentProps = resolvedFeature.clientFeatureProps + resolvedFeatureMapArray + .map(([featureKey, resolvedFeature]) => { + const ClientComponent = resolvedFeature.ClientComponent + const clientComponentProps = resolvedFeature.clientFeatureProps - /** - * Handle Feature Component Maps - */ - if ( - 'generateComponentMap' in resolvedFeature && - typeof resolvedFeature.generateComponentMap === 'function' - ) { - const components = resolvedFeature.generateComponentMap({ - config, - props: resolvedFeature.serverFeatureProps, - schemaPath, - }) + /** + * Handle Feature Component Maps + */ + if ( + 'generateComponentMap' in resolvedFeature && + typeof resolvedFeature.generateComponentMap === 'function' + ) { + const components = resolvedFeature.generateComponentMap({ + config, + props: resolvedFeature.serverFeatureProps, + schemaPath, + }) - for (const componentKey in components) { - const Component = components[componentKey] - if (Component) { - componentMap.set(`feature.${featureKey}.components.${componentKey}`, ) + for (const componentKey in components) { + const Component = components[componentKey] + if (Component) { + componentMap.set(`feature.${featureKey}.components.${componentKey}`, ) + } } } - } - /** - * Handle Feature Schema Maps (rendered fields) - */ - if ( - 'generateSchemaMap' in resolvedFeature && - typeof resolvedFeature.generateSchemaMap === 'function' - ) { - const schemas = resolvedFeature.generateSchemaMap({ - config, - props: resolvedFeature.serverFeatureProps, - schemaMap: new Map(), - schemaPath, - }) - - for (const schemaKey in schemas) { - const fields = schemas[schemaKey] - - const sanitizedFields = sanitizeFields({ + /** + * Handle Feature Schema Maps (rendered fields) + */ + if ( + 'generateSchemaMap' in resolvedFeature && + typeof resolvedFeature.generateSchemaMap === 'function' + ) { + const schemas = resolvedFeature.generateSchemaMap({ config, - fields, - validRelationships, + props: resolvedFeature.serverFeatureProps, + schemaMap: new Map(), + schemaPath, }) - const mappedFields = mapFields({ - config, - fieldSchema: sanitizedFields, - operation: 'update', - permissions: {}, - readOnly: false, - }) + for (const schemaKey in schemas) { + const fields = schemas[schemaKey] - componentMap.set(`feature.${featureKey}.fields.${schemaKey}`, mappedFields) + const sanitizedFields = sanitizeFields({ + config, + fields, + validRelationships, + }) + + const mappedFields = mapFields({ + config, + fieldSchema: sanitizedFields, + operation: 'update', + permissions: {}, + readOnly: false, + }) + + componentMap.set(`feature.${featureKey}.fields.${schemaKey}`, mappedFields) + } } - } - return { - ClientComponent: - clientComponentProps && typeof clientComponentProps === 'object' ? ( - - ) : ( - - ), - key: resolvedFeature.key, - order: resolvedFeature.order, - } as GeneratedFeatureProviderComponent - }), + if (!ClientComponent) { + return null + } + + return { + ClientComponent: + clientComponentProps && typeof clientComponentProps === 'object' ? ( + + ) : ( + + ), + key: resolvedFeature.key, + order: resolvedFeature.order, + } as GeneratedFeatureProviderComponent + }) + .filter((feature) => feature !== null), ) return componentMap diff --git a/packages/richtext-lexical/src/index.ts b/packages/richtext-lexical/src/index.ts index a72b650db..06d559d37 100644 --- a/packages/richtext-lexical/src/index.ts +++ b/packages/richtext-lexical/src/index.ts @@ -232,28 +232,6 @@ export function lexicalEditor(props?: LexicalEditorProps): LexicalRichTextAdapte } } -export { HeadingFeature } from './field/features/Heading' -export { ParagraphFeature } from './field/features/Paragraph' -export { RelationshipFeature } from './field/features/Relationship' -export { - $createRelationshipNode, - $isRelationshipNode, - type RelationshipData, - RelationshipNode, - type SerializedRelationshipNode, -} from './field/features/Relationship/nodes/RelationshipNode' - -export { UploadFeature } from './field/features/Upload' -export type { UploadFeatureProps } from './field/features/Upload' -export type { RawUploadPayload } from './field/features/Upload/nodes/UploadNode' - -export { - $createUploadNode, - $isUploadNode, - type SerializedUploadNode, - type UploadData, - UploadNode, -} from './field/features/Upload/nodes/UploadNode' export { AlignFeature } from './field/features/align/feature.server' export { BlockQuoteFeature } from './field/features/blockquote/feature.server' export { BlocksFeature, type BlocksFeatureProps } from './field/features/blocks/feature.server' @@ -264,36 +242,39 @@ export { BlockNode, type SerializedBlockNode, } from './field/features/blocks/nodes/BlocksNode' + export { TextDropdownSectionWithEntries } from './field/features/common/floatingSelectToolbarTextDropdownSection' -export { - HTMLConverterFeature, - type HTMLConverterFeatureProps, -} from './field/features/converters/html' export { convertLexicalNodesToHTML, convertLexicalToHTML, } from './field/features/converters/html/converter' export { LinebreakHTMLConverter } from './field/features/converters/html/converter/converters/linebreak' + export { ParagraphHTMLConverter } from './field/features/converters/html/converter/converters/paragraph' export { TextHTMLConverter } from './field/features/converters/html/converter/converters/text' export { defaultHTMLConverters } from './field/features/converters/html/converter/defaultConverters' export type { HTMLConverter } from './field/features/converters/html/converter/types' +export { + HTMLConverterFeature, + type HTMLConverterFeatureProps, +} from './field/features/converters/html/feature.server' export { consolidateHTMLConverters } from './field/features/converters/html/field' export { lexicalHTML } from './field/features/converters/html/field' -export { TestRecorderFeature } from './field/features/debug/TestRecorder' -export { TreeViewFeature } from './field/features/debug/TreeView' -export { BoldTextFeature } from './field/features/format/Bold' - -export { InlineCodeTextFeature } from './field/features/format/InlineCode' -export { ItalicTextFeature } from './field/features/format/Italic' - +export { TestRecorderFeature } from './field/features/debug/testrecorder/feature.server' +export { TreeViewFeature } from './field/features/debug/treeview/feature.server' +export { BoldFeature } from './field/features/format/bold/feature.server' export { SectionWithEntries as FormatSectionWithEntries } from './field/features/format/common/floatingSelectToolbarSection' -export { StrikethroughTextFeature } from './field/features/format/strikethrough' -export { SubscriptTextFeature } from './field/features/format/subscript' -export { SuperscriptTextFeature } from './field/features/format/superscript' -export { UnderlineTextFeature } from './field/features/format/underline' +export { InlineCodeFeature } from './field/features/format/inlinecode/feature.server' +export { ItalicFeature } from './field/features/format/italic/feature.server' +export { StrikethroughFeature } from './field/features/format/strikethrough/feature.server' +export { SubscriptFeature } from './field/features/format/subscript/feature.server' +export { SuperscriptFeature } from './field/features/format/superscript/feature.server' +export { UnderlineFeature } from './field/features/format/underline/feature.server' +export { HeadingFeature } from './field/features/heading/feature.server' + export { IndentFeature } from './field/features/indent' export { LinkFeature, type LinkFeatureServerProps } from './field/features/link/feature.server' + export { $createAutoLinkNode, $isAutoLinkNode, @@ -310,35 +291,41 @@ export type { SerializedAutoLinkNode, SerializedLinkNode, } from './field/features/link/nodes/types' -export { CheckListFeature } from './field/features/lists/CheckList' -export { OrderedListFeature } from './field/features/lists/OrderedList' -export { UnorderedListFeature } from './field/features/lists/UnorderedList' -export { LexicalPluginToLexicalFeature } from './field/features/migrations/LexicalPluginToLexical' -export { SlateToLexicalFeature } from './field/features/migrations/SlateToLexical' -export { SlateBlockquoteConverter } from './field/features/migrations/SlateToLexical/converter/converters/blockquote' +export { CheckListFeature } from './field/features/lists/checklist' +export { OrderedListFeature } from './field/features/lists/orderedlist' +export { UnorderedListFeature } from './field/features/lists/unorderedlist' +export { LexicalPluginToLexicalFeature } from './field/features/migrations/lexicalPluginToLexical' +export { SlateToLexicalFeature } from './field/features/migrations/slateToLexical' +export { SlateBlockquoteConverter } from './field/features/migrations/slateToLexical/converter/converters/blockquote' +export { SlateHeadingConverter } from './field/features/migrations/slateToLexical/converter/converters/heading' +export { SlateIndentConverter } from './field/features/migrations/slateToLexical/converter/converters/indent' +export { SlateLinkConverter } from './field/features/migrations/slateToLexical/converter/converters/link' +export { SlateListItemConverter } from './field/features/migrations/slateToLexical/converter/converters/listItem' +export { SlateOrderedListConverter } from './field/features/migrations/slateToLexical/converter/converters/orderedList' +export { SlateRelationshipConverter } from './field/features/migrations/slateToLexical/converter/converters/relationship' +export { SlateUnknownConverter } from './field/features/migrations/slateToLexical/converter/converters/unknown' -export { SlateHeadingConverter } from './field/features/migrations/SlateToLexical/converter/converters/heading' -export { SlateIndentConverter } from './field/features/migrations/SlateToLexical/converter/converters/indent' -export { SlateLinkConverter } from './field/features/migrations/SlateToLexical/converter/converters/link' - -export { SlateListItemConverter } from './field/features/migrations/SlateToLexical/converter/converters/listItem' -export { SlateOrderedListConverter } from './field/features/migrations/SlateToLexical/converter/converters/orderedList' -export { SlateRelationshipConverter } from './field/features/migrations/SlateToLexical/converter/converters/relationship' -export { SlateUnknownConverter } from './field/features/migrations/SlateToLexical/converter/converters/unknown' -export { SlateUnorderedListConverter } from './field/features/migrations/SlateToLexical/converter/converters/unorderedList' -export { SlateUploadConverter } from './field/features/migrations/SlateToLexical/converter/converters/upload' -export { defaultSlateConverters } from './field/features/migrations/SlateToLexical/converter/defaultConverters' +export { SlateUnorderedListConverter } from './field/features/migrations/slateToLexical/converter/converters/unorderedList' +export { SlateUploadConverter } from './field/features/migrations/slateToLexical/converter/converters/upload' +export { defaultSlateConverters } from './field/features/migrations/slateToLexical/converter/defaultConverters' export { convertSlateNodesToLexical, convertSlateToLexical, -} from './field/features/migrations/SlateToLexical/converter/index' - +} from './field/features/migrations/slateToLexical/converter/index' export type { SlateNode, SlateNodeConverter, -} from './field/features/migrations/SlateToLexical/converter/types' - +} from './field/features/migrations/slateToLexical/converter/types' +export { ParagraphFeature } from './field/features/paragraph' +export { RelationshipFeature } from './field/features/relationship' +export { + $createRelationshipNode, + $isRelationshipNode, + type RelationshipData, + RelationshipNode, + type SerializedRelationshipNode, +} from './field/features/relationship/nodes/RelationshipNode' export type { ClientFeature, ClientFeatureProviderMap, @@ -358,6 +345,19 @@ export type { ServerFeature, ServerFeatureProviderMap, } from './field/features/types' +export { UploadFeature } from './field/features/upload' + +export type { UploadFeatureProps } from './field/features/upload' + +export type { RawUploadPayload } from './field/features/upload/nodes/UploadNode' + +export { + $createUploadNode, + $isUploadNode, + type SerializedUploadNode, + type UploadData, + UploadNode, +} from './field/features/upload/nodes/UploadNode' export { EditorConfigProvider, useEditorConfigContext, diff --git a/packages/richtext-slate/package.json b/packages/richtext-slate/package.json index e2532e46d..a1fe79302 100644 --- a/packages/richtext-slate/package.json +++ b/packages/richtext-slate/package.json @@ -6,7 +6,7 @@ "license": "MIT", "homepage": "https://payloadcms.com", "author": "Payload CMS, Inc.", - "main": "./dist/index.js", + "main": "./src/index.ts", "types": "./dist/index.d.ts", "scripts": { "build": "pnpm copyfiles && pnpm build:swc && pnpm build:types", @@ -17,7 +17,7 @@ "prepublishOnly": "pnpm clean && pnpm turbo build" }, "dependencies": { - "@faceless-ui/modal": "2.0.1", + "@faceless-ui/modal": "2.0.2", "is-hotkey": "0.2.0", "react": "18.2.0", "slate": "0.91.4", @@ -26,6 +26,7 @@ "slate-react": "0.92.0" }, "devDependencies": { + "@payloadcms/ui": "workspace:*", "@payloadcms/eslint-config": "workspace:*", "@types/node": "20.5.7", "@types/react": "18.2.15", @@ -33,8 +34,8 @@ }, "peerDependencies": { "payload": "^2.3.0", - "@payloadcms/translations": "workspace:^", - "@payloadcms/ui": "workspace:^" + "@payloadcms/translations": "workspace:*", + "@payloadcms/ui": "workspace:*" }, "exports": { ".": { diff --git a/packages/richtext-slate/src/field/elements/upload/Element/index.tsx b/packages/richtext-slate/src/field/elements/upload/Element/index.tsx index 07526d5b4..0cecff598 100644 --- a/packages/richtext-slate/src/field/elements/upload/Element/index.tsx +++ b/packages/richtext-slate/src/field/elements/upload/Element/index.tsx @@ -84,7 +84,7 @@ const Element: React.FC = ({ { initialParams }, ) - const thumbnailSRC = useThumbnail(relatedCollection.upload, data) + const thumbnailSRC = useThumbnail(relatedCollection.slug, relatedCollection.upload, data) const removeUpload = useCallback(() => { const elementPath = ReactEditor.findPath(editor, element) diff --git a/packages/ui/package.json b/packages/ui/package.json index fff5fb116..858b514c2 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -1,7 +1,7 @@ { "name": "@payloadcms/ui", "version": "0.0.1", - "main": "./dist/index.js", + "main": "./src/index.ts", "types": "./dist/index.d.ts", "scripts": { "build": "pnpm copyfiles && pnpm build:swc && pnpm build:types", @@ -44,7 +44,7 @@ "@faceless-ui/scroll-info": "1.3.0", "@faceless-ui/window-info": "2.1.2", "@monaco-editor/react": "4.5.1", - "@payloadcms/translations": "workspace:^", + "@payloadcms/translations": "workspace:*", "body-scroll-lock": "4.0.0-beta.0", "bson-objectid": "2.0.4", "date-fns": "2.30.0", diff --git a/packages/ui/src/elements/DocumentDrawer/DrawerContent.tsx b/packages/ui/src/elements/DocumentDrawer/DrawerContent.tsx index 8e18f4997..29a0162a9 100644 --- a/packages/ui/src/elements/DocumentDrawer/DrawerContent.tsx +++ b/packages/ui/src/elements/DocumentDrawer/DrawerContent.tsx @@ -20,7 +20,6 @@ import { DocumentInfoProvider } from '../../providers/DocumentInfo' import { useFormQueryParams } from '../../providers/FormQueryParams' import { useLocale } from '../../providers/Locale' import { useTranslation } from '../../providers/Translation' -import { formatFields } from '../../utilities/formatFields' import { getFormState } from '../../utilities/getFormState' import { Gutter } from '../Gutter' import IDLabel from '../IDLabel' @@ -58,8 +57,6 @@ const Content: React.FC = ({ const { Edit } = componentMap[`${collectionSlug ? 'collections' : 'globals'}`][collectionSlug] - const [fields, setFields] = useState(() => formatFields(fieldsFromConfig, true)) - // no need to an additional requests when creating new documents const initialID = useRef(id) @@ -68,10 +65,6 @@ const Content: React.FC = ({ { initialParams: { depth: 0, draft: 'true', 'fallback-locale': 'null' } }, ) - useEffect(() => { - setFields(formatFields(fields, true)) - }, [collectionSlug, collectionConfig, fields]) - useEffect(() => { setIsOpen(Boolean(modalState[drawerSlug]?.isOpen)) }, [modalState, drawerSlug]) diff --git a/packages/ui/src/elements/FileDetails/index.tsx b/packages/ui/src/elements/FileDetails/index.tsx index f93868c8a..1e96b61b2 100644 --- a/packages/ui/src/elements/FileDetails/index.tsx +++ b/packages/ui/src/elements/FileDetails/index.tsx @@ -22,7 +22,12 @@ const FileDetails: React.FC = (props) => { return (
- +
= (props) => { const { className = '', + collectionSlug, doc: { filename } = {}, doc, fileSrc, @@ -20,7 +21,7 @@ const Thumbnail: React.FC = (props) => { uploadConfig, } = props - const thumbnailSRC = uploadConfig && doc ? useThumbnail(uploadConfig, doc) : fileSrc + const thumbnailSRC = useThumbnail(collectionSlug, uploadConfig, doc) || fileSrc const [src, setSrc] = useState(thumbnailSRC) const classes = [baseClass, `${baseClass}--size-${size || 'medium'}`, className].join(' ') diff --git a/packages/ui/src/elements/Thumbnail/types.ts b/packages/ui/src/elements/Thumbnail/types.ts index dd55ff795..0807ad442 100644 --- a/packages/ui/src/elements/Thumbnail/types.ts +++ b/packages/ui/src/elements/Thumbnail/types.ts @@ -2,6 +2,7 @@ import type { SanitizedCollectionConfig } from 'payload/types' export type Props = { className?: string + collectionSlug?: string doc?: Record fileSrc?: string imageCacheTag?: string diff --git a/packages/ui/src/elements/Upload/index.tsx b/packages/ui/src/elements/Upload/index.tsx index 77b2f9694..c800ad3d8 100644 --- a/packages/ui/src/elements/Upload/index.tsx +++ b/packages/ui/src/elements/Upload/index.tsx @@ -4,6 +4,7 @@ import React, { useCallback, useEffect, useState } from 'react' import type { Props } from './types' +import { useClientFunctions } from '../..' import Error from '../../forms/Error' import { useFormSubmitted } from '../../forms/Form/context' import reduceFieldsToValues from '../../forms/Form/reduceFieldsToValues' @@ -52,6 +53,7 @@ export const UploadActions = ({ canEdit, showSizePreviews }) => { export const Upload: React.FC = (props) => { const { collectionSlug, initialState, onChange, updatedAt, uploadConfig } = props + const clientFunctions = useClientFunctions() const submitted = useFormSubmitted() const [replacingFile, setReplacingFile] = useState(false) @@ -152,7 +154,10 @@ export const Upload: React.FC = (props) => { {value && (
- +
= (props) => { onSuccess, redirect, submitted: submittedFromProps, + uuid, waitForAutocomplete, } = props @@ -509,6 +510,7 @@ const Form: React.FC = (props) => { contextRef.current.addFieldRow = addFieldRow contextRef.current.removeFieldRow = removeFieldRow contextRef.current.replaceFieldRow = replaceFieldRow + contextRef.current.uuid = uuid useEffect(() => { if (typeof submittedFromProps === 'boolean') setSubmitted(submittedFromProps) diff --git a/packages/ui/src/forms/Form/types.ts b/packages/ui/src/forms/Form/types.ts index b71a3c661..97aacd2e5 100644 --- a/packages/ui/src/forms/Form/types.ts +++ b/packages/ui/src/forms/Form/types.ts @@ -60,6 +60,7 @@ export type Props = ( onSuccess?: (json: unknown) => void redirect?: string submitted?: boolean + uuid?: string validationOperation?: 'create' | 'update' waitForAutocomplete?: boolean } @@ -220,5 +221,6 @@ export type Context = { setProcessing: SetProcessing setSubmitted: SetSubmitted submit: Submit + uuid?: string validateForm: ValidateForm } diff --git a/packages/ui/src/forms/fields/Blocks/BlocksDrawer/index.tsx b/packages/ui/src/forms/fields/Blocks/BlocksDrawer/index.tsx index a86899207..756a2073f 100644 --- a/packages/ui/src/forms/fields/Blocks/BlocksDrawer/index.tsx +++ b/packages/ui/src/forms/fields/Blocks/BlocksDrawer/index.tsx @@ -60,7 +60,7 @@ export const BlocksDrawer: React.FC = (props) => {
    {filteredBlocks?.map((block, index) => { - const { imageAltText, imageURL, labels: blockLabels, slug } = block + const { slug, imageAltText, imageURL, labels: blockLabels } = block return (
  • diff --git a/packages/ui/src/forms/fields/Checkbox/index.tsx b/packages/ui/src/forms/fields/Checkbox/index.tsx index 594e3ab18..f4904efbb 100644 --- a/packages/ui/src/forms/fields/Checkbox/index.tsx +++ b/packages/ui/src/forms/fields/Checkbox/index.tsx @@ -5,6 +5,7 @@ import React, { useCallback } from 'react' import type { Props } from './types' +import { useForm } from '../../Form/context' import LabelComp from '../../Label' import useField from '../../useField' import { withCondition } from '../../withCondition' @@ -37,6 +38,8 @@ const Checkbox: React.FC = (props) => { width, } = props + const { uuid } = useForm() + const Label = LabelFromProps || const memoizedValidate: ClientValidate = useCallback( @@ -63,7 +66,7 @@ const Checkbox: React.FC = (props) => { const checked = checkedFromProps || Boolean(value) - const fieldID = id || `field-${path?.replace(/\./g, '__')}` + const fieldID = id || `field-${path?.replace(/\./g, '__')}${uuid ? `-${uuid}` : ''}` return (
    = (props) => { - const { isSelected, onChange, option, path } = props + const { isSelected, onChange, option, path, uuid } = props const { i18n } = useTranslation() - const id = `field-${path}-${option.value}` + const id = `field-${path}-${option.value}${uuid ? `-${uuid}` : ''}` return (
  • @@ -109,6 +112,7 @@ const RadioGroup: React.FC = (props) => { }} option={optionIsObject(option) ? option : { label: option, value: option }} path={path} + uuid={uuid} />
  • ) diff --git a/packages/ui/src/hooks/useAdminThumbnail.tsx b/packages/ui/src/hooks/useAdminThumbnail.tsx new file mode 100644 index 000000000..f4088ed12 --- /dev/null +++ b/packages/ui/src/hooks/useAdminThumbnail.tsx @@ -0,0 +1,8 @@ +import type { GetAdminThumbnail } from 'payload/types' + +import { useAddClientFunction, useDocumentInfo } from '..' + +export const useAdminThumbnail = (func: GetAdminThumbnail) => { + const { collectionSlug } = useDocumentInfo() + useAddClientFunction(`${collectionSlug}.adminThumbnail`, func) +} diff --git a/packages/ui/src/hooks/useThumbnail.ts b/packages/ui/src/hooks/useThumbnail.ts index f80169aac..1affd9885 100644 --- a/packages/ui/src/hooks/useThumbnail.ts +++ b/packages/ui/src/hooks/useThumbnail.ts @@ -2,28 +2,34 @@ import type { SanitizedCollectionConfig } from 'payload/types' import { isImage } from 'payload/utilities' +import { useComponentMap } from '..' import { useConfig } from '../providers/Config' const absoluteURLPattern = new RegExp('^(?:[a-z]+:)?//', 'i') const base64Pattern = new RegExp(/^data:image\/[a-z]+;base64,/) +// /api/[collectionSlug]/file/[filename] + const useThumbnail = ( + collectionSlug: string, uploadConfig: SanitizedCollectionConfig['upload'], doc: Record, ): false | string => { - const { adminThumbnail, staticURL } = uploadConfig + const { + routes: { api: apiRoute }, + serverURL, + } = useConfig() + const { componentMap } = useComponentMap() + + if (!collectionSlug || !uploadConfig || !doc) return null + + const { adminThumbnail } = uploadConfig const { filename, mimeType, sizes, url } = doc + const thumbnailSrcFunction = componentMap?.[`${collectionSlug}.adminThumbnail`] - const { serverURL } = useConfig() - let pathURL = `${serverURL}${staticURL || ''}` - - if (absoluteURLPattern.test(staticURL)) { - pathURL = staticURL - } - - if (typeof adminThumbnail === 'function') { - const thumbnailURL = adminThumbnail({ doc }) + if (typeof thumbnailSrcFunction === 'function') { + const thumbnailURL = thumbnailSrcFunction({ doc }) if (!thumbnailURL) return false @@ -31,7 +37,7 @@ const useThumbnail = ( return thumbnailURL } - return `${pathURL}/${thumbnailURL}` + return `${serverURL}/${thumbnailURL}` } if (isImage(mimeType as string)) { @@ -39,19 +45,23 @@ const useThumbnail = ( return url as string } - if (sizes?.[adminThumbnail]?.url) { - return sizes[adminThumbnail].url - } + if (typeof adminThumbnail === 'string') { + if (sizes?.[adminThumbnail]?.url) { + return sizes[adminThumbnail].url + } - if (sizes?.[adminThumbnail]?.filename) { - return `${pathURL}/${sizes[adminThumbnail].filename}` + if (sizes?.[adminThumbnail]?.filename) { + return `${serverURL}${apiRoute}/${collectionSlug}/file/${sizes[adminThumbnail].filename}` + } } if (url) { return url as string } - return `${pathURL}/${filename}` + if (typeof filename === 'string') { + return `${serverURL}${apiRoute}/${collectionSlug}/file/${filename}` + } } return false diff --git a/packages/ui/src/providers/DocumentInfo/types.ts b/packages/ui/src/providers/DocumentInfo/types.ts index b0e22a375..62d9555c4 100644 --- a/packages/ui/src/providers/DocumentInfo/types.ts +++ b/packages/ui/src/providers/DocumentInfo/types.ts @@ -48,6 +48,6 @@ export type DocumentInfoContext = Omit & { getDocPreferences: () => Promise<{ [key: string]: unknown }> getVersions: () => Promise setDocFieldPreferences: (field: string, fieldPreferences: { [key: string]: unknown }) => void - setDocumentInfo?: (info: DocumentInfo) => void + setDocumentInfo?: React.Dispatch> setDocumentTitle: (title: string) => void } diff --git a/packages/ui/src/utilities/buildComponentMap/index.tsx b/packages/ui/src/utilities/buildComponentMap/index.tsx index 870a1cf22..eb66edc59 100644 --- a/packages/ui/src/utilities/buildComponentMap/index.tsx +++ b/packages/ui/src/utilities/buildComponentMap/index.tsx @@ -97,7 +97,15 @@ export const buildComponentMap = (args: { readOnly: readOnlyOverride, }) + let AdminThumbnail = null + if (typeof collectionConfig?.upload?.adminThumbnail === 'function') { + AdminThumbnail = collectionConfig?.upload?.adminThumbnail + } else if (typeof collectionConfig?.upload?.adminThumbnail === 'string') { + AdminThumbnail = () => collectionConfig?.upload?.adminThumbnail + } + const componentMap: CollectionComponentMap = { + AdminThumbnail, AfterList, AfterListTable, BeforeList, diff --git a/packages/ui/src/utilities/buildComponentMap/types.ts b/packages/ui/src/utilities/buildComponentMap/types.ts index 06c1b2e17..06e951782 100644 --- a/packages/ui/src/utilities/buildComponentMap/types.ts +++ b/packages/ui/src/utilities/buildComponentMap/types.ts @@ -72,6 +72,7 @@ export type MappedField = { export type FieldMap = MappedField[] export type CollectionComponentMap = ConfigComponentMapBase & { + AdminThumbnail: React.ReactNode AfterList: React.ReactNode AfterListTable: React.ReactNode BeforeList: React.ReactNode diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 43f618ff6..6885de89a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -594,7 +594,7 @@ importers: packages/payload: dependencies: '@payloadcms/translations': - specifier: workspace:^ + specifier: workspace:* version: link:../translations bson-objectid: specifier: 2.0.4 @@ -906,7 +906,7 @@ importers: packages/plugin-form-builder: dependencies: '@payloadcms/ui': - specifier: workspace:^ + specifier: workspace:* version: link:../ui deepmerge: specifier: ^4.2.2 @@ -976,7 +976,7 @@ importers: packages/plugin-search: dependencies: '@payloadcms/ui': - specifier: workspace:^ + specifier: workspace:* version: link:../ui ts-deepmerge: specifier: ^2.0.1 @@ -1074,7 +1074,7 @@ importers: packages/plugin-stripe: dependencies: '@payloadcms/ui': - specifier: workspace:^ + specifier: workspace:* version: link:../ui lodash.get: specifier: ^4.4.2 @@ -1117,8 +1117,8 @@ importers: packages/richtext-lexical: dependencies: '@faceless-ui/modal': - specifier: 2.0.1 - version: 2.0.1(react-dom@18.2.0)(react@18.2.0) + specifier: 2.0.2 + version: 2.0.2(react-dom@18.2.0)(react@18.2.0) '@lexical/headless': specifier: 0.13.1 version: 0.13.1(lexical@0.13.1) @@ -1149,9 +1149,6 @@ importers: '@payloadcms/translations': specifier: workspace:* version: link:../translations - '@payloadcms/ui': - specifier: workspace:* - version: link:../ui bson-objectid: specifier: 2.0.4 version: 2.0.4 @@ -1186,6 +1183,9 @@ importers: '@payloadcms/eslint-config': specifier: workspace:* version: link:../eslint-config-payload + '@payloadcms/ui': + specifier: workspace:* + version: link:../ui '@types/json-schema': specifier: 7.0.15 version: 7.0.15 @@ -1205,14 +1205,11 @@ importers: packages/richtext-slate: dependencies: '@faceless-ui/modal': - specifier: 2.0.1 - version: 2.0.1(react-dom@18.2.0)(react@18.2.0) + specifier: 2.0.2 + version: 2.0.2(react-dom@18.2.0)(react@18.2.0) '@payloadcms/translations': - specifier: workspace:^ + specifier: workspace:* version: link:../translations - '@payloadcms/ui': - specifier: workspace:^ - version: link:../ui is-hotkey: specifier: 0.2.0 version: 0.2.0 @@ -1235,6 +1232,9 @@ importers: '@payloadcms/eslint-config': specifier: workspace:* version: link:../eslint-config-payload + '@payloadcms/ui': + specifier: workspace:* + version: link:../ui '@types/node': specifier: 20.5.7 version: 20.5.7 @@ -1281,7 +1281,7 @@ importers: specifier: 4.5.1 version: 4.5.1(monaco-editor@0.38.0)(react-dom@18.2.0)(react@18.2.0) '@payloadcms/translations': - specifier: workspace:^ + specifier: workspace:* version: link:../translations body-scroll-lock: specifier: 4.0.0-beta.0 @@ -3138,20 +3138,6 @@ packages: engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dev: false - /@faceless-ui/modal@2.0.1(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-z1PaaLxwuX+1In4vhUxODZndGKdCY+WIqzvtnas3CaYGGCVJBSJ4jfv9UEEGZzcahmSy+71bEL89cUT6d36j1Q==} - peerDependencies: - react: ^18.2.0 - react-dom: ^18.2.0 - dependencies: - body-scroll-lock: 3.1.5 - focus-trap: 6.9.4 - qs: 6.11.2 - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - react-transition-group: 4.4.5(react-dom@18.2.0)(react@18.2.0) - dev: false - /@faceless-ui/modal@2.0.2(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-CtwUn+hHEaoYUjREzQKGRbEp55VzUx7sC+hxIxmCPwg7Yd5KXkQzSfoUfRAHqT/1MFfE1B2QCHVVbhtSnFL9BA==} peerDependencies: diff --git a/test/_community/collections/Media/index.ts b/test/_community/collections/Media/index.ts index 408d73274..16dd434e7 100644 --- a/test/_community/collections/Media/index.ts +++ b/test/_community/collections/Media/index.ts @@ -1,14 +1,9 @@ -import path from 'path' - import type { CollectionConfig } from '../../../../packages/payload/src/collections/config/types' export const mediaSlug = 'media' export const MediaCollection: CollectionConfig = { slug: mediaSlug, - // upload: { - // staticDir: path.resolve(__dirname, './media'), - // }, upload: true, access: { read: () => true, diff --git a/test/buildConfigWithDefaults.ts b/test/buildConfigWithDefaults.ts index f30904e70..7b19282c6 100644 --- a/test/buildConfigWithDefaults.ts +++ b/test/buildConfigWithDefaults.ts @@ -1,10 +1,19 @@ -// import { -// AlignFeature, -// BlockQuoteFeature, -// BlocksFeature, -// LinkFeature, -// lexicalEditor, -// } from '@payloadcms/richtext-lexical' +import { + AlignFeature, + BlockQuoteFeature, + BlocksFeature, + BoldFeature, + HeadingFeature, + InlineCodeFeature, + ItalicFeature, + LinkFeature, + StrikethroughFeature, + SubscriptFeature, + SuperscriptFeature, + TreeViewFeature, + UnderlineFeature, + lexicalEditor, +} from '@payloadcms/richtext-lexical' import path from 'path' import type { Config, SanitizedConfig } from '../packages/payload/src/config/types' @@ -12,7 +21,7 @@ import type { Config, SanitizedConfig } from '../packages/payload/src/config/typ import { mongooseAdapter } from '../packages/db-mongodb/src' import { postgresAdapter } from '../packages/db-postgres/src' import { buildConfig as buildPayloadConfig } from '../packages/payload/src/config/build' -import { slateEditor } from '../packages/richtext-slate/src' +// import { slateEditor } from '../packages/richtext-slate/src' // process.env.PAYLOAD_DATABASE = 'postgres' @@ -62,46 +71,77 @@ export function buildConfigWithDefaults(testConfig?: Partial): Promise { + return value !== 'option2' ? true : 'Cannot be option2' + }, + }, + ], + }, + ], + }), + ], + }), rateLimit: { max: 9999999999, window: 15 * 60 * 1000, // 15min default, diff --git a/test/field-error-states/collections/Upload/index.ts b/test/field-error-states/collections/Upload/index.ts index da72adcf6..c53d69d51 100644 --- a/test/field-error-states/collections/Upload/index.ts +++ b/test/field-error-states/collections/Upload/index.ts @@ -5,7 +5,7 @@ import type { CollectionConfig } from '../../../../packages/payload/src/collecti const Uploads: CollectionConfig = { slug: 'uploads', upload: { - staticDir: path.resolve(__dirname, './uploads'), + staticDir: path.resolve(process.cwd(), 'test/field-error-states/collections/Upload/uploads'), }, fields: [ { diff --git a/test/field-error-states/tsconfig.json b/test/field-error-states/tsconfig.json index 662b955ff..244de73b1 100644 --- a/test/field-error-states/tsconfig.json +++ b/test/field-error-states/tsconfig.json @@ -1,4 +1,5 @@ { + "extends": "../../tsconfig.json", "compilerOptions": { "paths": { "payload/generated-types": ["./payload-types.ts"] diff --git a/test/uploads/collections/admin-thumbnail/RegisterThumbnailFn.tsx b/test/uploads/collections/admin-thumbnail/RegisterThumbnailFn.tsx new file mode 100644 index 000000000..120c95c84 --- /dev/null +++ b/test/uploads/collections/admin-thumbnail/RegisterThumbnailFn.tsx @@ -0,0 +1,35 @@ +'use client' +import type React from 'react' + +import { useAdminThumbnail } from '../../../../packages/ui/src/hooks/useAdminThumbnail' + +type TypeWithFile = { + filename: string + filesize: number + mimeType: string +} & Record + +function docHasFilename(doc: Record): doc is TypeWithFile { + if (typeof doc === 'object' && 'filename' in doc) { + return true + } + return false +} + +export const adminThumbnailSrc = '/media/image-640x480.png' + +function getThumbnailSrc({ doc }) { + if (docHasFilename(doc)) { + if (doc.mimeType.startsWith('image/')) { + return null // Fallback to default admin thumbnail if image + } + return adminThumbnailSrc // Use custom thumbnail if not image + } + return null +} + +export const RegisterAdminThumbnailFn: React.FC = () => { + void useAdminThumbnail(getThumbnailSrc) + + return null +} diff --git a/test/uploads/collections/admin-thumbnail/index.ts b/test/uploads/collections/admin-thumbnail/index.ts index 17bc29ed1..4114b4bf0 100644 --- a/test/uploads/collections/admin-thumbnail/index.ts +++ b/test/uploads/collections/admin-thumbnail/index.ts @@ -2,34 +2,13 @@ import path from 'path' import type { CollectionConfig } from '../../../../packages/payload/src/collections/config/types' -type TypeWithFile = { - filename: string - filesize: number - mimeType: string -} & Record - -function docHasFilename(doc: Record): doc is TypeWithFile { - if (typeof doc === 'object' && 'filename' in doc) { - return true - } - return false -} - -export const adminThumbnailSrc = '/media/image-640x480.png' +import { RegisterAdminThumbnailFn } from './RegisterThumbnailFn' export const AdminThumbnailCol: CollectionConfig = { slug: 'admin-thumbnail', upload: { staticDir: path.resolve(process.cwd(), 'test/uploads/media'), - // adminThumbnail: ({ doc }) => { - // if (docHasFilename(doc)) { - // if (doc.mimeType.startsWith('image/')) { - // return null // Fallback to default admin thumbnail if image - // } - // return adminThumbnailSrc // Use custom thumbnail if not image - // } - // return null - // }, + adminThumbnail: RegisterAdminThumbnailFn, }, fields: [], } diff --git a/turbo.json b/turbo.json index 29a1b5a3c..77b371bd9 100644 --- a/turbo.json +++ b/turbo.json @@ -5,7 +5,7 @@ "dependsOn": ["^clean"] }, "build": { - "cache": false, + "cache": true, "dependsOn": ["^build"], "outputs": ["./dist/**"] },