diff --git a/packages/core/src/actions/config.ts b/packages/core/src/actions/config.ts index 7ae16f8a..1596ecaa 100644 --- a/packages/core/src/actions/config.ts +++ b/packages/core/src/actions/config.ts @@ -173,7 +173,7 @@ export function applyDefaults( let collectionI18n = collection[I18N]; if (config.editor && !collection.editor) { - collection.editor = { preview: config.editor.preview, frame: config.editor.frame }; + collection.editor = config.editor; } collection.media_library = { @@ -248,7 +248,7 @@ export function applyDefaults( } if (collection.editor && !file.editor) { - file.editor = { preview: collection.editor.preview, frame: collection.editor.frame }; + file.editor = collection.editor; } } } diff --git a/packages/core/src/components/entry-editor/EditorInterface.tsx b/packages/core/src/components/entry-editor/EditorInterface.tsx index 8ab1a07b..66089ef6 100644 --- a/packages/core/src/components/entry-editor/EditorInterface.tsx +++ b/packages/core/src/components/entry-editor/EditorInterface.tsx @@ -2,6 +2,7 @@ import React, { useCallback, useEffect, useMemo, useState } from 'react'; import { ScrollSyncPane } from 'react-scroll-sync'; import { EDITOR_SIZE_COMPACT } from '@staticcms/core/constants/views'; +import { summaryFormatter } from '@staticcms/core/lib/formatters'; import useBreadcrumbs from '@staticcms/core/lib/hooks/useBreadcrumbs'; import { getI18nInfo, hasI18n } from '@staticcms/core/lib/i18n'; import classNames from '@staticcms/core/lib/util/classNames.util'; @@ -10,6 +11,8 @@ import { selectEntryCollectionTitle, } from '@staticcms/core/lib/util/collection.util'; import { customPathFromSlug } from '@staticcms/core/lib/util/nested.util'; +import { selectConfig } from '@staticcms/core/reducers/selectors/config'; +import { useAppSelector } from '@staticcms/core/store/hooks'; import MainView from '../MainView'; import EditorToolbar from './EditorToolbar'; import EditorControlPane from './editor-control-pane/EditorControlPane'; @@ -95,6 +98,8 @@ const EditorInterface = ({ submitted, slug, }: TranslatedProps) => { + const config = useAppSelector(selectConfig); + const { locales, defaultLocale } = useMemo(() => getI18nInfo(collection), [collection]) ?? {}; const translatedLocales = useMemo( () => locales?.filter(locale => locale !== defaultLocale) ?? [], @@ -151,28 +156,55 @@ const EditorInterface = ({ setSelectedLocale(locale); }, []); - const [showPreviewToggle, previewInFrame, editorSize] = useMemo(() => { - let preview = collection.editor?.preview ?? true; - let frame = collection.editor?.frame ?? true; + const { livePreviewUrlTemplate, showPreviewToggle, previewInFrame, editorSize } = useMemo(() => { + let livePreviewUrlTemplate = + typeof collection.editor?.live_preview === 'string' ? collection.editor.live_preview : false; + + let preview = true; + let frame = true; let size = collection.editor?.size ?? EDITOR_SIZE_COMPACT; - if ('files' in collection) { - const file = getFileFromSlug(collection, entry.slug); - if (file?.editor?.preview !== undefined) { - preview = file.editor.preview; + if (collection.editor) { + if ('preview' in collection.editor) { + preview = collection.editor.preview ?? true; } - if (file?.editor?.frame !== undefined) { - frame = file.editor.frame; - } - - if (file?.editor?.size !== undefined) { - size = file.editor.size; + if ('frame' in collection.editor) { + preview = collection.editor.frame ?? true; } } - return [preview, frame, size]; - }, [collection, entry.slug]); + if ('files' in collection) { + const file = getFileFromSlug(collection, entry.slug); + + if (file?.editor) { + if (typeof file.editor.live_preview === 'string') { + livePreviewUrlTemplate = file.editor.live_preview; + } + + if ('preview' in file.editor && file.editor.preview !== undefined) { + preview = file.editor.preview; + } + + if ('frame' in file.editor && file.editor.frame !== undefined) { + frame = file.editor.frame; + } + + if (file?.editor?.size !== undefined) { + size = file.editor.size; + } + } + } + + return { + livePreviewUrlTemplate: livePreviewUrlTemplate + ? summaryFormatter(livePreviewUrlTemplate, entry, collection, config?.slug) + : undefined, + showPreviewToggle: preview, + previewInFrame: frame, + editorSize: size, + }; + }, [collection, config?.slug, entry]); const finalPreviewActive = useMemo( () => showPreviewToggle && previewActive, @@ -318,6 +350,7 @@ const EditorInterface = ({ ) => { - const { editorSize, entry, collection, fields, previewInFrame, showMobilePreview, t } = props; + const { + editorSize, + entry, + collection, + fields, + previewInFrame, + livePreviewUrlTemplate, + showMobilePreview, + t, + } = props; const config = useAppSelector(selectConfig); @@ -171,6 +183,15 @@ const EditorPreviewPane = (props: TranslatedProps) => { [props, theme, widgetFor, widgetsFor], ); + const livePreviewIframe = useRef(null); + const passEventToIframe = useCallback((event: DataUpdateEvent) => { + if (livePreviewIframe.current) { + livePreviewIframe.current.contentWindow?.postMessage(event); + } + }, []); + + useWindowEvent('data:update', passEventToIframe); + return useMemo(() => { if (!element) { return null; @@ -196,7 +217,13 @@ const EditorPreviewPane = (props: TranslatedProps) => { )} > - {previewInFrame ? ( + {livePreviewUrlTemplate ? ( +