diff --git a/packages/next/src/utilities/initReq.ts b/packages/next/src/utilities/initReq.ts index aa9b29b71..e2e4c7cba 100644 --- a/packages/next/src/utilities/initReq.ts +++ b/packages/next/src/utilities/initReq.ts @@ -72,6 +72,7 @@ export const initReq = async function ({ cookies, headers, }) + const i18n: I18nClient = await initI18n({ config: config.i18n, context: 'client', diff --git a/packages/next/src/views/Document/index.tsx b/packages/next/src/views/Document/index.tsx index 2b749bbfc..a823c9444 100644 --- a/packages/next/src/views/Document/index.tsx +++ b/packages/next/src/views/Document/index.tsx @@ -6,7 +6,6 @@ import type { DocumentViewServerProps, DocumentViewServerPropsOnly, EditViewComponent, - LivePreviewConfig, PayloadComponent, RenderDocumentVersionsProperties, } from 'payload' @@ -18,6 +17,7 @@ import { LivePreviewProvider, } from '@payloadcms/ui' import { RenderServerComponent } from '@payloadcms/ui/elements/RenderServerComponent' +import { handleLivePreview } from '@payloadcms/ui/rsc' import { isEditing as getIsEditing } from '@payloadcms/ui/shared' import { buildFormState } from '@payloadcms/ui/utilities/buildFormState' import { notFound, redirect } from 'next/navigation.js' @@ -348,36 +348,14 @@ export const renderDocument = async ({ viewType, } - const isLivePreviewEnabled = Boolean( - config.admin?.livePreview?.collections?.includes(collectionSlug) || - config.admin?.livePreview?.globals?.includes(globalSlug) || - collectionConfig?.admin?.livePreview || - globalConfig?.admin?.livePreview, - ) - - const livePreviewConfig: LivePreviewConfig = { - ...(isLivePreviewEnabled ? config.admin.livePreview : {}), - ...(collectionConfig?.admin?.livePreview || {}), - ...(globalConfig?.admin?.livePreview || {}), - } - - const livePreviewURL = - operation !== 'create' - ? typeof livePreviewConfig?.url === 'function' - ? await livePreviewConfig.url({ - collectionConfig, - data: doc, - globalConfig, - locale, - req, - /** - * @deprecated - * Use `req.payload` instead. This will be removed in the next major version. - */ - payload: initPageResult.req.payload, - }) - : livePreviewConfig?.url - : '' + const { isLivePreviewEnabled, livePreviewConfig, livePreviewURL } = await handleLivePreview({ + collectionSlug, + config, + data: doc, + globalSlug, + operation, + req, + }) return { data: doc, @@ -412,6 +390,7 @@ export const renderDocument = async ({ breakpoints={livePreviewConfig?.breakpoints} isLivePreviewEnabled={isLivePreviewEnabled && operation !== 'create'} isLivePreviewing={entityPreferences?.value?.editViewType === 'live-preview'} + typeofLivePreviewURL={typeof livePreviewConfig?.url as 'function' | 'string' | undefined} url={livePreviewURL} > {showHeader && !drawerSlug && ( diff --git a/packages/payload/src/admin/forms/Form.ts b/packages/payload/src/admin/forms/Form.ts index 0409a14f5..ea89673ef 100644 --- a/packages/payload/src/admin/forms/Form.ts +++ b/packages/payload/src/admin/forms/Form.ts @@ -135,6 +135,12 @@ export type BuildFormStateArgs = { */ renderAllFields?: boolean req: PayloadRequest + /** + * If true, will return a fresh URL for live preview based on the current form state. + * Note: this will run on every form state event, so if your `livePreview.url` function is long running or expensive, + * ensure it caches itself as needed. + */ + returnLivePreviewURL?: boolean returnLockStatus?: boolean schemaPath: string select?: SelectType diff --git a/packages/payload/src/config/types.ts b/packages/payload/src/config/types.ts index 6f8761096..1e4b04293 100644 --- a/packages/payload/src/config/types.ts +++ b/packages/payload/src/config/types.ts @@ -151,10 +151,14 @@ export type LivePreviewConfig = { width: number | string }[] /** - The URL of the frontend application. This will be rendered within an `iframe` as its `src`. - Payload will send a `window.postMessage()` to this URL with the document data in real-time. - The frontend application is responsible for receiving the message and updating the UI accordingly. - Use the `useLivePreview` hook to get started in React applications. + * The URL of the frontend application. This will be rendered within an `iframe` as its `src`. + * Payload will send a `window.postMessage()` to this URL with the document data in real-time. + * The frontend application is responsible for receiving the message and updating the UI accordingly. + * Use the `useLivePreview` hook to get started in React applications. + * + * Note: this function may run often if autosave is enabled with a small interval. + * For performance, avoid long-running tasks or expensive operations within this function, + * or if you need to do something more complex, cache your function as needed. */ url?: | ((args: { diff --git a/packages/payload/src/globals/config/types.ts b/packages/payload/src/globals/config/types.ts index 8d80eeda9..80c0ade3b 100644 --- a/packages/payload/src/globals/config/types.ts +++ b/packages/payload/src/globals/config/types.ts @@ -233,9 +233,7 @@ export type GlobalConfig = { export interface SanitizedGlobalConfig extends Omit, 'endpoints' | 'fields' | 'slug' | 'versions'> { endpoints: Endpoint[] | false - fields: Field[] - /** * Fields in the database schema structure * Rows / collapsible / tabs w/o name `fields` merged to top, UIs are excluded diff --git a/packages/ui/src/elements/LivePreview/IFrame/index.tsx b/packages/ui/src/elements/LivePreview/IFrame/index.tsx index 547185307..20946649b 100644 --- a/packages/ui/src/elements/LivePreview/IFrame/index.tsx +++ b/packages/ui/src/elements/LivePreview/IFrame/index.tsx @@ -6,24 +6,17 @@ import './index.scss' const baseClass = 'live-preview-iframe' -type Props = { - ref: React.RefObject - setIframeHasLoaded: (value: boolean) => void - url: string -} - -export const IFrame: React.FC = (props) => { - const { ref, setIframeHasLoaded, url } = props - - const { zoom } = useLivePreviewContext() +export const IFrame: React.FC = () => { + const { iframeRef, setLoadedURL, url, zoom } = useLivePreviewContext() return (