chore: moves live preview test suite into main app folder

This commit is contained in:
James
2024-03-18 14:27:56 -04:00
parent 70f29785ca
commit 5eaea1c7f1
93 changed files with 276 additions and 1279 deletions

7
.vscode/launch.json vendored
View File

@@ -16,6 +16,13 @@
"request": "launch",
"type": "node-terminal"
},
{
"command": "pnpm run dev live-preview -- --no-turbo",
"cwd": "${workspaceFolder}",
"name": "Run Dev Live Preview",
"request": "launch",
"type": "node-terminal"
},
{
"command": "pnpm run dev plugin-cloud-storage",
"cwd": "${workspaceFolder}",

View File

@@ -1,13 +1,13 @@
'use client'
import type { Page as PageType } from '@/payload-types'
import { PAYLOAD_SERVER_URL } from '@/app/_api/serverURL'
import { Blocks } from '@/app/_components/Blocks'
import { Hero } from '@/app/_components/Hero'
import { useLivePreview } from '@payloadcms/live-preview-react'
import React from 'react'
import { useLivePreview } from '../../../../../../packages/live-preview-react/src'
import type { Page as PageType } from '../../../../test/live-preview/payload-types.js'
import { PAYLOAD_SERVER_URL } from '../../_api/serverURL.js'
import { Blocks } from '../../_components/Blocks/index.js'
import { Hero } from '../../_components/Hero/index.js'
export const PageClient: React.FC<{
page: PageType

View File

@@ -1,12 +1,13 @@
import { fetchDoc } from '@/app/_api/fetchDoc'
import { fetchDocs } from '@/app/_api/fetchDocs'
import { notFound } from 'next/navigation'
import { notFound } from 'next/navigation.js'
import React from 'react'
import type { Page } from '../../../payload-types'
import type { Page } from '../../../../test/live-preview/payload-types.js'
import { PageClient } from './page.client'
import { fetchDoc } from '../../_api/fetchDoc.js'
import { fetchDocs } from '../../_api/fetchDocs.js'
import { PageClient } from './page.client.js'
// eslint-disable-next-line no-restricted-exports
export default async function Page({ params: { slug = 'home' } }) {
let page: Page | null = null

View File

@@ -1,13 +1,13 @@
'use client'
import type { Post as PostType } from '@/payload-types'
import { PAYLOAD_SERVER_URL } from '@/app/_api/serverURL'
import { Blocks } from '@/app/_components/Blocks'
import { PostHero } from '@/app/_heros/PostHero'
import { useLivePreview } from '@payloadcms/live-preview-react'
import React from 'react'
import { useLivePreview } from '../../../../../../../packages/live-preview-react/src'
import type { Post as PostType } from '../../../../../test/live-preview/payload-types.js'
import { PAYLOAD_SERVER_URL } from '../../../_api/serverURL.js'
import { Blocks } from '../../../_components/Blocks/index.js'
import { PostHero } from '../../../_heros/PostHero/index.js'
export const PostClient: React.FC<{
post: PostType

View File

@@ -1,11 +1,11 @@
import { notFound } from 'next/navigation'
import { notFound } from 'next/navigation.js'
import React from 'react'
import type { Post } from '../../../../payload-types'
import type { Post } from '../../../../../test/live-preview/payload-types.js'
import { fetchDoc } from '../../../_api/fetchDoc'
import { fetchDocs } from '../../../_api/fetchDocs'
import { PostClient } from './page.client'
import { fetchDoc } from '../../../_api/fetchDoc.js'
import { fetchDocs } from '../../../_api/fetchDocs.js'
import { PostClient } from './page.client.js'
export default async function Post({ params: { slug = '' } }) {
let post: Post | null = null

View File

@@ -1,16 +1,14 @@
import QueryString from 'qs'
import type { Config } from '../../payload-types'
import { PAYLOAD_SERVER_URL } from './serverURL'
import { PAYLOAD_SERVER_URL } from './serverURL.js'
export const fetchDoc = async <T>(args: {
collection: keyof Config['collections']
collection: string
depth?: number
id?: string
slug?: string
}): Promise<T> => {
const { collection, slug, id, depth = 2 } = args || {}
const { id, slug, collection, depth = 2 } = args || {}
const queryString = QueryString.stringify(
{
@@ -21,11 +19,11 @@ export const fetchDoc = async <T>(args: {
)
const doc: T = await fetch(`${PAYLOAD_SERVER_URL}/api/${collection}${queryString}`, {
method: 'GET',
cache: 'no-store',
headers: {
'Content-Type': 'application/json',
},
method: 'GET',
})
?.then((res) => res.json())
?.then((res) => {

View File

@@ -1,14 +1,12 @@
import type { Config } from '../../payload-types'
import { PAYLOAD_SERVER_URL } from './serverURL.js'
import { PAYLOAD_SERVER_URL } from './serverURL'
export const fetchDocs = async <T>(collection: keyof Config['collections']): Promise<T[]> => {
export const fetchDocs = async <T>(collection: string): Promise<T[]> => {
const docs: T[] = await fetch(`${PAYLOAD_SERVER_URL}/api/${collection}?depth=0&limit=100`, {
method: 'GET',
cache: 'no-store',
headers: {
'Content-Type': 'application/json',
},
method: 'GET',
})
?.then((res) => res.json())
?.then((res) => {

View File

@@ -1,16 +1,16 @@
import type { Footer } from '../../payload-types'
import type { Footer } from '../../../test/live-preview/payload-types.js'
import { PAYLOAD_SERVER_URL } from './serverURL'
import { PAYLOAD_SERVER_URL } from './serverURL.js'
export async function fetchFooter(): Promise<Footer> {
if (!PAYLOAD_SERVER_URL) throw new Error('PAYLOAD_SERVER_URL not found')
const footer = await fetch(`${PAYLOAD_SERVER_URL}/api/globals/footer`, {
method: 'GET',
cache: 'no-store',
headers: {
'Content-Type': 'application/json',
},
method: 'GET',
})
.then((res) => {
if (!res.ok) throw new Error('Error fetching doc')

View File

@@ -1,16 +1,16 @@
import type { Header } from '../../payload-types'
import type { Header } from '../../../test/live-preview/payload-types.js'
import { PAYLOAD_SERVER_URL } from './serverURL'
import { PAYLOAD_SERVER_URL } from './serverURL.js'
export async function fetchHeader(): Promise<Header> {
if (!PAYLOAD_SERVER_URL) throw new Error('PAYLOAD_SERVER_URL not found')
const header = await fetch(`${PAYLOAD_SERVER_URL}/api/globals/header`, {
method: 'GET',
cache: 'no-store',
headers: {
'Content-Type': 'application/json',
},
method: 'GET',
})
?.then((res) => {
if (!res.ok) throw new Error('Error fetching doc')

View File

@@ -1,10 +1,10 @@
import React from 'react'
import type { ArchiveBlockProps } from './types'
import type { ArchiveBlockProps } from './types.js'
import { CollectionArchive } from '../../_components/CollectionArchive'
import { Gutter } from '../../_components/Gutter'
import RichText from '../../_components/RichText'
import { CollectionArchive } from '../../_components/CollectionArchive/index.js'
import { Gutter } from '../../_components/Gutter/index.js'
import RichText from '../../_components/RichText/index.js'
import classes from './index.module.scss'
export const ArchiveBlock: React.FC<

View File

@@ -1,4 +1,4 @@
import type { Page } from '../../../payload-types'
import type { Page } from '../../../../test/live-preview/payload-types.js'
export type ArchiveBlockProps = Extract<
Exclude<Page['layout'], undefined>[0],

View File

@@ -1,11 +1,11 @@
import React from 'react'
import type { Page } from '../../../payload-types'
import type { Page } from '../../../../test/live-preview/payload-types.js'
import { Gutter } from '../../_components/Gutter'
import { CMSLink } from '../../_components/Link'
import RichText from '../../_components/RichText'
import { VerticalPadding } from '../../_components/VerticalPadding'
import { Gutter } from '../../_components/Gutter/index.js'
import { CMSLink } from '../../_components/Link/index.js'
import RichText from '../../_components/RichText/index.js'
import { VerticalPadding } from '../../_components/VerticalPadding/index.js'
import classes from './index.module.scss'
type Props = Extract<Exclude<Page['layout'], undefined>[0], { blockType: 'cta' }>

View File

@@ -1,10 +1,10 @@
import React, { Fragment } from 'react'
import type { Page } from '../../../payload-types'
import type { Page } from '../../../../test/live-preview/payload-types.js'
import { Gutter } from '../../_components/Gutter'
import { CMSLink } from '../../_components/Link'
import RichText from '../../_components/RichText'
import { Gutter } from '../../_components/Gutter/index.js'
import { CMSLink } from '../../_components/Link/index.js'
import RichText from '../../_components/RichText/index.js'
import classes from './index.module.scss'
type Props = Extract<Exclude<Page['layout'], undefined>[0], { blockType: 'content' }>

View File

@@ -1,12 +1,12 @@
import type { StaticImageData } from 'next/image'
import type { StaticImageData } from 'next/image.js'
import React from 'react'
import type { Page } from '../../../payload-types'
import type { Page } from '../../../../test/live-preview/payload-types.js'
import { Gutter } from '../../_components/Gutter'
import { Media } from '../../_components/Media'
import RichText from '../../_components/RichText'
import { Gutter } from '../../_components/Gutter/index.js'
import { Media } from '../../_components/Media/index.js'
import RichText from '../../_components/RichText/index.js'
import classes from './index.module.scss'
type Props = Extract<Exclude<Page['layout'], undefined>[0], { blockType: 'mediaBlock' }> & {

View File

@@ -1,10 +1,10 @@
import React from 'react'
import type { Post } from '../../../payload-types'
import type { Post } from '../../../../test/live-preview/payload-types.js'
import { Card } from '../../_components/Card'
import { Gutter } from '../../_components/Gutter'
import RichText from '../../_components/RichText'
import { Card } from '../../_components/Card/index.js'
import { Gutter } from '../../_components/Gutter/index.js'
import RichText from '../../_components/RichText/index.js'
import classes from './index.module.scss'
export type RelatedPostsProps = {

View File

@@ -1,9 +1,9 @@
import { Gutter } from '@/app/_components/Gutter'
import RichText from '@/app/_components/RichText'
import React, { Fragment } from 'react'
import type { Page } from '../../../payload-types'
import type { Page } from '../../../../test/live-preview/payload-types.js'
import { Gutter } from '../../_components/Gutter/index.js'
import RichText from '../../_components/RichText/index.js'
import classes from './index.module.scss'
export type RelationshipsBlockProps = {

View File

@@ -1,18 +1,18 @@
import React, { Fragment } from 'react'
import type { Page } from '../../../payload-types.js'
import type { RelationshipsBlockProps } from '../../_blocks/Relationships'
import type { VerticalPaddingOptions } from '../VerticalPadding'
import type { Page } from '../../../../test/live-preview/payload-types.js'
import type { RelationshipsBlockProps } from '../../_blocks/Relationships/index.js'
import type { VerticalPaddingOptions } from '../VerticalPadding/index.js'
import { ArchiveBlock } from '../../_blocks/ArchiveBlock'
import { CallToActionBlock } from '../../_blocks/CallToAction'
import { ContentBlock } from '../../_blocks/Content'
import { MediaBlock } from '../../_blocks/MediaBlock'
import { RelatedPosts, type RelatedPostsProps } from '../../_blocks/RelatedPosts'
import { RelationshipsBlock } from '../../_blocks/Relationships'
import { toKebabCase } from '../../_utilities/toKebabCase'
import { BackgroundColor } from '../BackgroundColor'
import { VerticalPadding } from '../VerticalPadding'
import { ArchiveBlock } from '../../_blocks/ArchiveBlock/index.js'
import { CallToActionBlock } from '../../_blocks/CallToAction/index.js'
import { ContentBlock } from '../../_blocks/Content/index.js'
import { MediaBlock } from '../../_blocks/MediaBlock/index.js'
import { RelatedPosts, type RelatedPostsProps } from '../../_blocks/RelatedPosts/index.js'
import { RelationshipsBlock } from '../../_blocks/Relationships/index.js'
import { toKebabCase } from '../../_utilities/toKebabCase.js'
import { BackgroundColor } from '../BackgroundColor/index.js'
import { VerticalPadding } from '../VerticalPadding/index.js'
const blockComponents = {
archive: ArchiveBlock,

View File

@@ -2,11 +2,13 @@
import type { ElementType } from 'react'
import Link from 'next/link'
import LinkWithDefault from 'next/link.js'
import React from 'react'
import classes from './index.module.scss'
const Link = (LinkWithDefault.default || LinkWithDefault) as typeof LinkWithDefault.default
export type Props = {
appearance?: 'default' | 'none' | 'primary' | 'secondary'
className?: string

View File

@@ -1,11 +1,13 @@
import Link from 'next/link'
import LinkWithDefault from 'next/link.js'
import React, { Fragment } from 'react'
import type { Post } from '../../../payload-types'
import type { Post } from '../../../../test/live-preview/payload-types.js'
import { Media } from '../Media'
import { Media } from '../Media/index.js'
import classes from './index.module.scss'
const Link = (LinkWithDefault.default || LinkWithDefault) as typeof LinkWithDefault.default
export const Card: React.FC<{
alignItems?: 'center'
className?: string
@@ -31,7 +33,7 @@ export const Card: React.FC<{
const hasCategories = categories && Array.isArray(categories) && categories.length > 0
const titleToUse = titleFromProps || title
const sanitizedDescription = description?.replace(/\s/g, ' ') // replace non-breaking space with white space
const href = `/${relationTo}/${slug}`
const href = `/live-preview/${relationTo}/${slug}`
return (
<div

View File

@@ -1,16 +1,16 @@
'use client'
import { PAYLOAD_SERVER_URL } from '@/app/_api/serverURL'
import qs from 'qs'
import React, { Fragment, useCallback, useEffect, useRef, useState } from 'react'
import type { Post } from '../../../../payload-types'
import type { ArchiveBlockProps } from '../../../_blocks/ArchiveBlock/types'
import type { Post } from '../../../../../test/live-preview/payload-types.js'
import type { ArchiveBlockProps } from '../../../_blocks/ArchiveBlock/types.js'
import { Card } from '../../Card'
import { Gutter } from '../../Gutter'
import { PageRange } from '../../PageRange'
import { Pagination } from '../../Pagination'
import { PAYLOAD_SERVER_URL } from '../../../_api/serverURL.js'
import { Card } from '../../Card/index.js'
import { Gutter } from '../../Gutter/index.js'
import { PageRange } from '../../PageRange/index.js'
import { Pagination } from '../../Pagination/index.js'
import classes from './index.module.scss'
type Result = {
@@ -77,12 +77,10 @@ export const CollectionArchiveByCollection: React.FC<Props> = (props) => {
}, [isLoading, scrollToRef, results])
useEffect(() => {
let timer: NodeJS.Timeout
// hydrate the block with fresh content after first render
// don't show loader unless the request takes longer than x ms
// and don't show it during initial hydration
timer = setTimeout(() => {
const timer = setTimeout(() => {
if (hasHydrated) {
setIsLoading(true)
}

View File

@@ -2,10 +2,10 @@
import React, { Fragment } from 'react'
import type { ArchiveBlockProps } from '../../../_blocks/ArchiveBlock/types'
import type { ArchiveBlockProps } from '../../../_blocks/ArchiveBlock/types.js'
import { Card } from '../../Card'
import { Gutter } from '../../Gutter'
import { Card } from '../../Card/index.js'
import { Gutter } from '../../Gutter/index.js'
import classes from './index.module.scss'
export type Props = {

View File

@@ -1,9 +1,9 @@
import React from 'react'
import type { ArchiveBlockProps } from '../../_blocks/ArchiveBlock/types'
import type { ArchiveBlockProps } from '../../_blocks/ArchiveBlock/types.js'
import { CollectionArchiveByCollection } from './PopulateByCollection'
import { CollectionArchiveBySelection } from './PopulateBySelection'
import { CollectionArchiveByCollection } from './PopulateByCollection/index.js'
import { CollectionArchiveBySelection } from './PopulateBySelection/index.js'
export type Props = Omit<ArchiveBlockProps, 'blockType'> & {
className?: string

View File

@@ -1,11 +1,13 @@
import Link from 'next/link'
import LinkWithDefault from 'next/link.js'
import React from 'react'
import { fetchFooter } from '../../_api/fetchFooter'
import { Gutter } from '../Gutter'
import { CMSLink } from '../Link'
import { fetchFooter } from '../../_api/fetchFooter.js'
import { Gutter } from '../Gutter/index.js'
import { CMSLink } from '../Link/index.js'
import classes from './index.module.scss'
const Link = (LinkWithDefault.default || LinkWithDefault) as typeof LinkWithDefault.default
export async function Footer() {
const footer = await fetchFooter()

View File

@@ -2,9 +2,9 @@
import React from 'react'
import type { Header as HeaderType } from '../../../../payload-types'
import type { Header as HeaderType } from '../../../../../test/live-preview/payload-types.js'
import { CMSLink } from '../../Link'
import { CMSLink } from '../../Link/index.js'
import classes from './index.module.scss'
export const HeaderNav: React.FC<{ header: HeaderType }> = ({ header }) => {

View File

@@ -1,22 +1,20 @@
{
/* eslint-disable @next/next/no-img-element */
}
import { fetchHeader } from '@/app/_api/fetchHeader'
import Link from 'next/link'
import LinkWithDefault from 'next/link.js'
import React from 'react'
import { Gutter } from '../Gutter'
import { HeaderNav } from './Nav'
import { fetchHeader } from '../../_api/fetchHeader.js'
import { Gutter } from '../Gutter/index.js'
import { HeaderNav } from './Nav/index.js'
import classes from './index.module.scss'
const Link = (LinkWithDefault.default || LinkWithDefault) as typeof LinkWithDefault.default
export async function Header() {
const header = await fetchHeader()
return (
<header className={classes.header}>
<Gutter className={classes.wrap}>
<Link href="/">
<Link href="/live-preview">
<img
alt="Payload Logo"
className={classes.logo}

View File

@@ -1,9 +1,9 @@
import React from 'react'
import type { Page } from '../../../payload-types'
import type { Page } from '../../../../test/live-preview/payload-types.js'
import { HighImpactHero } from '../../_heros/HighImpact'
import { LowImpactHero } from '../../_heros/LowImpact'
import { HighImpactHero } from '../../_heros/HighImpact/index.js'
import { LowImpactHero } from '../../_heros/LowImpact/index.js'
const heroes = {
highImpact: HighImpactHero,

View File

@@ -1,10 +1,12 @@
import Link from 'next/link'
import LinkWithDefault from 'next/link.js'
import React from 'react'
import type { Page } from '../../../payload-types'
import type { Props as ButtonProps } from '../Button'
import type { Page, Post } from '../../../../test/live-preview/payload-types.js'
import type { Props as ButtonProps } from '../Button/index.js'
import { Button } from '../Button'
import { Button } from '../Button/index.js'
const Link = (LinkWithDefault.default || LinkWithDefault) as typeof LinkWithDefault.default
type CMSLinkType = {
appearance?: ButtonProps['appearance']
@@ -15,7 +17,7 @@ type CMSLinkType = {
newTab?: boolean
reference?: {
relationTo: 'pages' | 'posts'
value: Page | string
value: Page | Post | string
}
type?: 'custom' | 'reference'
url?: string

View File

@@ -1,18 +1,21 @@
'use client'
import type { StaticImageData } from 'next/image'
import type { StaticImageData } from 'next/image.js'
import { PAYLOAD_SERVER_URL } from '@/app/_api/serverURL'
import NextImage from 'next/image'
import NextImageWithDefault from 'next/image.js'
import React from 'react'
import type { Props as MediaProps } from '../types'
import type { Props as MediaProps } from '../types.js'
import cssVariables from '../../../cssVariables'
import { PAYLOAD_SERVER_URL } from '../../../_api/serverURL.js'
import cssVariables from '../../../cssVariables.js'
import classes from './index.module.scss'
const { breakpoints } = cssVariables
const NextImage = (NextImageWithDefault.default ||
NextImageWithDefault) as typeof NextImageWithDefault.default
export const Image: React.FC<MediaProps> = (props) => {
const {
alt: altFromProps,
@@ -46,7 +49,7 @@ export const Image: React.FC<MediaProps> = (props) => {
const filename = fullFilename
src = `${PAYLOAD_SERVER_URL}/media/${filename}`
src = `${PAYLOAD_SERVER_URL}/api/media/file/${filename}`
}
// NOTE: this is used by the browser to determine which image to download at different screen sizes

View File

@@ -1,10 +1,10 @@
'use client'
import { PAYLOAD_SERVER_URL } from '@/app/_api/serverURL'
import React, { useEffect, useRef } from 'react'
import type { Props as MediaProps } from '../types'
import type { Props as MediaProps } from '../types.js'
import { PAYLOAD_SERVER_URL } from '../../../_api/serverURL.js'
import classes from './index.module.scss'
export const Video: React.FC<MediaProps> = (props) => {

View File

@@ -2,10 +2,10 @@ import type { ElementType } from 'react'
import React, { Fragment } from 'react'
import type { Props } from './types'
import type { Props } from './types.js'
import { Image } from './Image'
import { Video } from './Video'
import { Image } from './Image/index.js'
import { Video } from './Video/index.js'
export const Media: React.FC<Props> = (props) => {
const { className, htmlElement = 'div', resource } = props

View File

@@ -1,6 +1,6 @@
import React from 'react'
import { Chevron } from '../Chevron'
import { Chevron } from '../Chevron/index.js'
import classes from './index.module.scss'
export const Pagination: React.FC<{

View File

@@ -1,8 +1,8 @@
import React from 'react'
import classes from './index.module.scss'
import serializeLexical from './serializeLexical'
import serializeSlate from './serializeSlate'
import serializeLexical from './serializeLexical.js'
import serializeSlate from './serializeSlate.js'
const RichText: React.FC<{
className?: string

View File

@@ -1,7 +1,9 @@
import type { SerializedEditorState } from 'lexical'
import { CMSLink } from '../Link'
import { Media } from '../Media'
import React from 'react'
import { CMSLink } from '../Link/index.js'
import { Media } from '../Media/index.js'
const serializer = (
content?: SerializedEditorState['root']['children'],

View File

@@ -2,8 +2,8 @@ import escapeHTML from 'escape-html'
import React, { Fragment } from 'react'
import { Text } from 'slate'
import { CMSLink } from '../Link'
import { Media } from '../Media'
import { CMSLink } from '../Link/index.js'
import { Media } from '../Media/index.js'
// eslint-disable-next-line no-use-before-define
type Children = Leaf[]

View File

@@ -1,10 +1,10 @@
import React, { Fragment } from 'react'
import type { Page } from '../../../payload-types'
import type { Page } from '../../../../test/live-preview/payload-types.js'
import { Gutter } from '../../_components/Gutter'
import { Media } from '../../_components/Media'
import RichText from '../../_components/RichText'
import { Gutter } from '../../_components/Gutter/index.js'
import { Media } from '../../_components/Media/index.js'
import RichText from '../../_components/RichText/index.js'
import classes from './index.module.scss'
export const HighImpactHero: React.FC<Page['hero']> = ({ media, richText }) => {

View File

@@ -1,10 +1,10 @@
import React from 'react'
import type { Page } from '../../../payload-types'
import type { Page } from '../../../../test/live-preview/payload-types.js'
import { Gutter } from '../../_components/Gutter'
import RichText from '../../_components/RichText'
import { VerticalPadding } from '../../_components/VerticalPadding'
import { Gutter } from '../../_components/Gutter/index.js'
import RichText from '../../_components/RichText/index.js'
import { VerticalPadding } from '../../_components/VerticalPadding/index.js'
import classes from './index.module.scss'
export const LowImpactHero: React.FC<Page['hero']> = ({ richText }) => {

View File

@@ -1,15 +1,17 @@
import { PAYLOAD_SERVER_URL } from '@/app/_api/serverURL'
import Link from 'next/link'
import LinkWithDefault from 'next/link.js'
import React, { Fragment } from 'react'
import type { Post } from '../../../payload-types'
import type { Post } from '../../../../test/live-preview/payload-types.js'
import { Gutter } from '../../_components/Gutter'
import { Media } from '../../_components/Media'
import RichText from '../../_components/RichText'
import { formatDateTime } from '../../_utilities/formatDateTime'
import { PAYLOAD_SERVER_URL } from '../../_api/serverURL.js'
import { Gutter } from '../../_components/Gutter/index.js'
import { Media } from '../../_components/Media/index.js'
import RichText from '../../_components/RichText/index.js'
import { formatDateTime } from '../../_utilities/formatDateTime.js'
import classes from './index.module.scss'
const Link = (LinkWithDefault.default || LinkWithDefault) as typeof LinkWithDefault.default
export const PostHero: React.FC<{
post: Post
}> = ({ post }) => {

View File

@@ -1,6 +1,5 @@
// Keep these in sync with the CSS variables in the `_css` directory
module.exports = {
export default {
breakpoints: {
s: 768,
m: 1024,

View File

@@ -1,7 +1,9 @@
import type { Metadata } from 'next'
import { Footer } from './_components/Footer'
import { Header } from './_components/Header'
import React from 'react'
import { Footer } from './_components/Footer/index.js'
import { Header } from './_components/Header/index.js'
import './_css/app.scss'
export const metadata: Metadata = {

View File

@@ -2,8 +2,8 @@
import React from 'react'
import { Gutter } from './_components/Gutter'
import { VerticalPadding } from './_components/VerticalPadding'
import { Gutter } from './_components/Gutter/index.js'
import { VerticalPadding } from './_components/VerticalPadding/index.js'
export default function NotFound() {
return (

View File

@@ -0,0 +1,3 @@
import PageTemplate from './(pages)/[slug]/page.js'
export default PageTemplate

View File

@@ -19,6 +19,9 @@ export default withBundleAnalyzer(
},
]
},
images: {
domains: ['localhost'],
},
webpack: (webpackConfig) => {
webpackConfig.resolve.extensionAlias = {
'.cjs': ['.cts', '.cjs'],

View File

@@ -1,7 +1,9 @@
'use client'
import type { LivePreviewConfig } from 'payload/config'
import type { Field } from 'payload/types'
import { DndContext } from '@dnd-kit/core'
import { fieldSchemaToJSON } from 'payload/utilities'
import React, { useCallback, useEffect, useState } from 'react'
import type { usePopupWindow } from '../usePopupWindow.js'
@@ -18,18 +20,26 @@ export type LivePreviewProviderProps = {
height: number
width: number
}
fieldSchema: Field[]
isPopupOpen?: boolean
openPopupWindow?: ReturnType<typeof usePopupWindow>['openPopupWindow']
popupRef?: React.MutableRefObject<Window>
url?: string
}
export const LivePreviewProvider: React.FC<LivePreviewProviderProps> = (props) => {
const { breakpoints, children, isPopupOpen, openPopupWindow, popupRef, url } = props
export const LivePreviewProvider: React.FC<LivePreviewProviderProps> = ({
breakpoints,
children,
fieldSchema,
isPopupOpen,
openPopupWindow,
popupRef,
url,
}) => {
const [previewWindowType, setPreviewWindowType] = useState<'iframe' | 'popup'>('iframe')
const [appIsReady, setAppIsReady] = useState(false)
const [listeningForMessages, setListeningForMessages] = useState(false)
const iframeRef = React.useRef<HTMLIFrameElement>(null)
@@ -49,21 +59,9 @@ export const LivePreviewProvider: React.FC<LivePreviewProviderProps> = (props) =
const [breakpoint, setBreakpoint] =
React.useState<LivePreviewConfig['breakpoints'][0]['name']>('responsive')
// const [fieldSchemaJSON] = useState(() => {
// let fields: Field[]
// if ('collection' in props) {
// const { collection } = props
// fields = collection.fields
// }
// if ('global' in props) {
// const { global } = props
// fields = global.fields
// }
// return fieldSchemaToJSON(fields)
// })
const [fieldSchemaJSON] = useState(() => {
return fieldSchemaToJSON(fieldSchema)
})
// The toolbar needs to freely drag and drop around the page
const handleDragEnd = (ev) => {
@@ -138,10 +136,12 @@ export const LivePreviewProvider: React.FC<LivePreviewProviderProps> = (props) =
window.addEventListener('message', handleMessage)
setListeningForMessages(true)
return () => {
window.removeEventListener('message', handleMessage)
}
}, [url])
}, [url, listeningForMessages])
const handleWindowChange = useCallback(
(type: 'iframe' | 'popup') => {
@@ -155,10 +155,12 @@ export const LivePreviewProvider: React.FC<LivePreviewProviderProps> = (props) =
// when the user closes the popup window, switch back to the iframe
// the `usePopupWindow` reports the `isPopupOpen` state for us to use here
useEffect(() => {
if (!isPopupOpen) {
const newPreviewWindowType = isPopupOpen ? 'popup' : 'iframe'
if (newPreviewWindowType !== previewWindowType) {
handleWindowChange('iframe')
}
}, [isPopupOpen, handleWindowChange])
}, [previewWindowType, isPopupOpen, handleWindowChange])
return (
<LivePreviewContext.Provider
@@ -166,7 +168,7 @@ export const LivePreviewProvider: React.FC<LivePreviewProviderProps> = (props) =
appIsReady,
breakpoint,
breakpoints,
fieldSchemaJSON: [],
fieldSchemaJSON,
iframeHasLoaded,
iframeRef,
isPopupOpen,
@@ -191,7 +193,7 @@ export const LivePreviewProvider: React.FC<LivePreviewProviderProps> = (props) =
}}
>
<DndContext collisionDetection={customCollisionDetection} onDragEnd={handleDragEnd}>
{children}
{listeningForMessages && children}
</DndContext>
</LivePreviewContext.Provider>
)

View File

@@ -1,7 +1,12 @@
'use client'
import type { FormProps } from '@payloadcms/ui'
import type { FieldMap, FormProps } from '@payloadcms/ui'
import type { LivePreviewConfig } from 'payload/config'
import type { Data } from 'payload/types'
import type {
ClientConfig,
Data,
SanitizedCollectionConfig,
SanitizedGlobalConfig,
} from 'payload/types'
import {
DocumentControls,
@@ -29,7 +34,25 @@ import { usePopupWindow } from './usePopupWindow.js'
const baseClass = 'live-preview'
const PreviewView: React.FC = (props) => {
type Props = {
apiRoute: string
collectionConfig?: SanitizedCollectionConfig
config: ClientConfig
fieldMap: FieldMap
globalConfig?: SanitizedGlobalConfig
schemaPath: string
serverURL: string
}
const PreviewView: React.FC<Props> = ({
apiRoute,
collectionConfig,
config,
fieldMap,
globalConfig,
schemaPath,
serverURL,
}) => {
const {
id,
AfterDocument,
@@ -50,29 +73,6 @@ const PreviewView: React.FC = (props) => {
onSave: onSaveFromProps,
} = useDocumentInfo()
const config = useConfig()
const {
collections,
globals,
routes: { api: apiRoute },
serverURL,
} = config
const { getFieldMap } = useComponentMap()
const collectionConfig =
collectionSlug && collections.find((collection) => collection.slug === collectionSlug)
const globalConfig = globalSlug && globals.find((global) => global.slug === globalSlug)
const schemaPath = collectionSlug || globalSlug
const fieldMap = getFieldMap({
collectionSlug: collectionConfig?.slug,
globalSlug: globalConfig?.slug,
})
const operation = id ? 'update' : 'create'
const { t } = useTranslation()
@@ -167,7 +167,7 @@ const PreviewView: React.FC = (props) => {
id={id}
isEditing={Boolean(id)}
permissions={docPermissions}
slug={collectionConfig?.slug}
slug={collectionConfig?.slug || globalConfig?.slug}
/>
<div
className={[baseClass, previewWindowType === 'popup' && `${baseClass}--detached`]
@@ -190,7 +190,7 @@ const PreviewView: React.FC = (props) => {
fieldMap={fieldMap}
forceSidebarWrap
readOnly={!hasSavePermission}
schemaPath={collectionSlug}
schemaPath={collectionSlug || globalSlug}
/>
{AfterDocument}
</div>
@@ -210,26 +210,58 @@ export const LivePreviewClient: React.FC<{
const { breakpoints, url } = props
const { collectionSlug, globalSlug } = useDocumentInfo()
const config = useConfig()
const { isPopupOpen, openPopupWindow, popupRef } = usePopupWindow({
eventType: 'payload-live-preview',
url,
})
const {
collections,
globals,
routes: { api: apiRoute },
serverURL,
} = config
const collectionConfig =
collectionSlug && collections.find((collection) => collection.slug === collectionSlug)
const globalConfig = globalSlug && globals.find((global) => global.slug === globalSlug)
const schemaPath = collectionSlug || globalSlug
const { getComponentMap } = useComponentMap()
const componentMap = getComponentMap({ collectionSlug, globalSlug })
const { getFieldMap } = useComponentMap()
const fieldMap = getFieldMap({
collectionSlug: collectionConfig?.slug,
globalSlug: globalConfig?.slug,
})
return (
<Fragment>
<SetViewActions actions={componentMap?.actionsMap?.Edit?.LivePreview} />
<LivePreviewProvider
breakpoints={breakpoints}
fieldSchema={collectionConfig?.fields || globalConfig?.fields}
isPopupOpen={isPopupOpen}
openPopupWindow={openPopupWindow}
popupRef={popupRef}
url={url}
>
<PreviewView />
<PreviewView
apiRoute={apiRoute}
collectionConfig={collectionConfig}
config={config}
fieldMap={fieldMap}
globalConfig={globalConfig}
schemaPath={schemaPath}
serverURL={serverURL}
/>
</LivePreviewProvider>
</Fragment>
)

View File

@@ -1,35 +0,0 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.js
# testing
/coverage
# next.js
/.next/
/out/
# production
/build
# misc
.DS_Store
*.pem
# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# local env files
.env*.local
# vercel
.vercel
# typescript
*.tsbuildinfo
next-env.d.ts

View File

@@ -1,36 +0,0 @@
This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).
## Getting Started
First, run the development server:
```bash
npm run dev
# or
yarn dev
# or
pnpm dev
# or
bun run dev
```
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font.
## Learn More
To learn more about Next.js, take a look at the following resources:
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!
## Deploy on Vercel
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.

View File

@@ -1,3 +0,0 @@
import PageTemplate from './(pages)/[slug]/page'
export default PageTemplate

View File

@@ -1,13 +0,0 @@
/** @type {import('next').NextConfig} */
const nextConfig = {
// this is only required for local development of the `useLivePreview` hook
// see `./app/page.client.tsx` for more details
experimental: {
externalDir: true,
},
images: {
domains: ['localhost'],
},
}
module.exports = nextConfig

View File

@@ -1,24 +0,0 @@
{
"name": "payload-live-preview",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev -p 3001",
"build": "next build",
"start": "next start",
"lint": "next lint"
},
"dependencies": {
"@types/escape-html": "^1.0.2",
"escape-html": "^1.0.3",
"next": "^13.5.3",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"sass": "^1.68.0",
"slate": "^0.94.1"
},
"devDependencies": {
"@types/node": "^20.6.2",
"@types/react": "^18.2.22"
}
}

View File

@@ -1,459 +0,0 @@
/* tslint:disable */
/* eslint-disable */
/**
* This file was automatically generated by Payload.
* DO NOT MODIFY IT BY HAND. Instead, modify your source Payload config,
* and re-run `payload generate:types` to regenerate this file.
*/
export interface Config {
collections: {
users: User
pages: Page
posts: Post
tenants: Tenant
categories: Category
media: Media
'payload-preferences': PayloadPreference
'payload-migrations': PayloadMigration
}
globals: {
header: Header
footer: Footer
}
}
export interface User {
id: string
updatedAt: string
createdAt: string
email: string
resetPasswordToken?: string | null
resetPasswordExpiration?: string | null
salt?: string | null
hash?: string | null
loginAttempts?: number | null
lockUntil?: string | null
password: string | null
}
export interface Page {
id: string
slug: string
tenant?: (string | null) | Tenant
title: string
hero: {
type: 'none' | 'highImpact' | 'lowImpact'
richText?:
| {
[k: string]: unknown
}[]
| null
media?: string | Media | null
}
layout?:
| (
| {
invertBackground?: boolean | null
richText?:
| {
[k: string]: unknown
}[]
| null
links?:
| {
link: {
type?: ('reference' | 'custom') | null
newTab?: boolean | null
reference?:
| ({
relationTo: 'posts'
value: string | Post
} | null)
| ({
relationTo: 'pages'
value: string | Page
} | null)
url?: string | null
label: string
appearance?: ('primary' | 'secondary') | null
}
id?: string | null
}[]
| null
id?: string | null
blockName?: string | null
blockType: 'cta'
}
| {
invertBackground?: boolean | null
columns?:
| {
size?: ('oneThird' | 'half' | 'twoThirds' | 'full') | null
richText?:
| {
[k: string]: unknown
}[]
| null
enableLink?: boolean | null
link?: {
type?: ('reference' | 'custom') | null
newTab?: boolean | null
reference?:
| ({
relationTo: 'posts'
value: string | Post
} | null)
| ({
relationTo: 'pages'
value: string | Page
} | null)
url?: string | null
label: string
appearance?: ('default' | 'primary' | 'secondary') | null
}
id?: string | null
}[]
| null
id?: string | null
blockName?: string | null
blockType: 'content'
}
| {
invertBackground?: boolean | null
position?: ('default' | 'fullscreen') | null
media: string | Media
id?: string | null
blockName?: string | null
blockType: 'mediaBlock'
}
| {
introContent?:
| {
[k: string]: unknown
}[]
| null
populateBy?: ('collection' | 'selection') | null
relationTo?: 'posts' | null
categories?: (string | Category)[] | null
limit?: number | null
selectedDocs?:
| {
relationTo: 'posts'
value: string | Post
}[]
| null
populatedDocs?:
| {
relationTo: 'posts'
value: string | Post
}[]
| null
populatedDocsTotal?: number | null
id?: string | null
blockName?: string | null
blockType: 'archive'
}
)[]
| null
relationshipAsUpload?: string | Media | null
relationshipMonoHasOne?: (string | null) | Post
relationshipMonoHasMany?: (string | Post)[] | null
relationshipPolyHasOne?: {
relationTo: 'posts'
value: string | Post
} | null
relationshipPolyHasMany?:
| {
relationTo: 'posts'
value: string | Post
}[]
| null
arrayOfRelationships?:
| {
uploadInArray?: string | Media | null
richTextInArray?:
| {
[k: string]: unknown
}[]
| null
relationshipInArrayMonoHasOne?: (string | null) | Post
relationshipInArrayMonoHasMany?: (string | Post)[] | null
relationshipInArrayPolyHasOne?: {
relationTo: 'posts'
value: string | Post
} | null
relationshipInArrayPolyHasMany?:
| {
relationTo: 'posts'
value: string | Post
}[]
| null
id?: string | null
}[]
| null
richTextSlate?:
| {
[k: string]: unknown
}[]
| null
richTextLexical?: {
root: {
children: {
type: string
version: number
[k: string]: unknown
}[]
direction: ('ltr' | 'rtl') | null
format: 'left' | 'start' | 'center' | 'right' | 'end' | 'justify' | ''
indent: number
type: string
version: number
}
[k: string]: unknown
} | null
tab: {
relationshipInTab?: (string | null) | Post
}
meta?: {
title?: string | null
description?: string | null
image?: string | Media | null
}
updatedAt: string
createdAt: string
}
export interface Tenant {
id: string
title: string
clientURL: string
updatedAt: string
createdAt: string
}
export interface Media {
id: string
alt: string
caption?:
| {
[k: string]: unknown
}[]
| null
updatedAt: string
createdAt: string
url?: string | null
filename?: string | null
mimeType?: string | null
filesize?: number | null
width?: number | null
height?: number | null
}
export interface Post {
id: string
slug: string
tenant?: (string | null) | Tenant
title: string
hero: {
type: 'none' | 'highImpact' | 'lowImpact'
richText?:
| {
[k: string]: unknown
}[]
| null
media?: string | Media | null
}
layout?:
| (
| {
invertBackground?: boolean | null
richText?:
| {
[k: string]: unknown
}[]
| null
links?:
| {
link: {
type?: ('reference' | 'custom') | null
newTab?: boolean | null
reference?:
| ({
relationTo: 'posts'
value: string | Post
} | null)
| ({
relationTo: 'pages'
value: string | Page
} | null)
url?: string | null
label: string
appearance?: ('primary' | 'secondary') | null
}
id?: string | null
}[]
| null
id?: string | null
blockName?: string | null
blockType: 'cta'
}
| {
invertBackground?: boolean | null
columns?:
| {
size?: ('oneThird' | 'half' | 'twoThirds' | 'full') | null
richText?:
| {
[k: string]: unknown
}[]
| null
enableLink?: boolean | null
link?: {
type?: ('reference' | 'custom') | null
newTab?: boolean | null
reference?:
| ({
relationTo: 'posts'
value: string | Post
} | null)
| ({
relationTo: 'pages'
value: string | Page
} | null)
url?: string | null
label: string
appearance?: ('default' | 'primary' | 'secondary') | null
}
id?: string | null
}[]
| null
id?: string | null
blockName?: string | null
blockType: 'content'
}
| {
invertBackground?: boolean | null
position?: ('default' | 'fullscreen') | null
media: string | Media
id?: string | null
blockName?: string | null
blockType: 'mediaBlock'
}
| {
introContent?:
| {
[k: string]: unknown
}[]
| null
populateBy?: ('collection' | 'selection') | null
relationTo?: 'posts' | null
categories?: (string | Category)[] | null
limit?: number | null
selectedDocs?:
| {
relationTo: 'posts'
value: string | Post
}[]
| null
populatedDocs?:
| {
relationTo: 'posts'
value: string | Post
}[]
| null
populatedDocsTotal?: number | null
id?: string | null
blockName?: string | null
blockType: 'archive'
}
)[]
| null
relatedPosts?: (string | Post)[] | null
meta?: {
title?: string | null
description?: string | null
image?: string | Media | null
}
updatedAt: string
createdAt: string
}
export interface Category {
id: string
title?: string | null
updatedAt: string
createdAt: string
}
export interface PayloadPreference {
id: string
user: {
relationTo: 'users'
value: string | User
}
key?: string | null
value?:
| {
[k: string]: unknown
}
| unknown[]
| string
| number
| boolean
| null
updatedAt: string
createdAt: string
}
export interface PayloadMigration {
id: string
name?: string | null
batch?: number | null
updatedAt: string
createdAt: string
}
export interface Header {
id: string
navItems?:
| {
link: {
type?: ('reference' | 'custom') | null
newTab?: boolean | null
reference?:
| ({
relationTo: 'posts'
value: string | Post
} | null)
| ({
relationTo: 'pages'
value: string | Page
} | null)
url?: string | null
label: string
appearance?: ('default' | 'primary' | 'secondary') | null
}
id?: string | null
}[]
| null
updatedAt?: string | null
createdAt?: string | null
}
export interface Footer {
id: string
navItems?:
| {
link: {
type?: ('reference' | 'custom') | null
newTab?: boolean | null
reference?:
| ({
relationTo: 'posts'
value: string | Post
} | null)
| ({
relationTo: 'pages'
value: string | Page
} | null)
url?: string | null
label: string
appearance?: ('default' | 'primary' | 'secondary') | null
}
id?: string | null
}[]
| null
updatedAt?: string | null
createdAt?: string | null
}

View File

@@ -1,462 +0,0 @@
lockfileVersion: '6.0'
settings:
autoInstallPeers: true
excludeLinksFromLockfile: false
dependencies:
'@types/escape-html':
specifier: ^1.0.2
version: 1.0.4
escape-html:
specifier: ^1.0.3
version: 1.0.3
next:
specifier: ^13.5.3
version: 13.5.6(react-dom@18.2.0)(react@18.2.0)(sass@1.72.0)
react:
specifier: ^18.2.0
version: 18.2.0
react-dom:
specifier: ^18.2.0
version: 18.2.0(react@18.2.0)
sass:
specifier: ^1.68.0
version: 1.72.0
slate:
specifier: ^0.94.1
version: 0.94.1
devDependencies:
'@types/node':
specifier: ^20.6.2
version: 20.11.28
'@types/react':
specifier: ^18.2.22
version: 18.2.66
packages:
/@next/env@13.5.6:
resolution: {integrity: sha512-Yac/bV5sBGkkEXmAX5FWPS9Mmo2rthrOPRQQNfycJPkjUAUclomCPH7QFVCDQ4Mp2k2K1SSM6m0zrxYrOwtFQw==}
dev: false
/@next/swc-darwin-arm64@13.5.6:
resolution: {integrity: sha512-5nvXMzKtZfvcu4BhtV0KH1oGv4XEW+B+jOfmBdpFI3C7FrB/MfujRpWYSBBO64+qbW8pkZiSyQv9eiwnn5VIQA==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [darwin]
requiresBuild: true
dev: false
optional: true
/@next/swc-darwin-x64@13.5.6:
resolution: {integrity: sha512-6cgBfxg98oOCSr4BckWjLLgiVwlL3vlLj8hXg2b+nDgm4bC/qVXXLfpLB9FHdoDu4057hzywbxKvmYGmi7yUzA==}
engines: {node: '>= 10'}
cpu: [x64]
os: [darwin]
requiresBuild: true
dev: false
optional: true
/@next/swc-linux-arm64-gnu@13.5.6:
resolution: {integrity: sha512-txagBbj1e1w47YQjcKgSU4rRVQ7uF29YpnlHV5xuVUsgCUf2FmyfJ3CPjZUvpIeXCJAoMCFAoGnbtX86BK7+sg==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
requiresBuild: true
dev: false
optional: true
/@next/swc-linux-arm64-musl@13.5.6:
resolution: {integrity: sha512-cGd+H8amifT86ZldVJtAKDxUqeFyLWW+v2NlBULnLAdWsiuuN8TuhVBt8ZNpCqcAuoruoSWynvMWixTFcroq+Q==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
requiresBuild: true
dev: false
optional: true
/@next/swc-linux-x64-gnu@13.5.6:
resolution: {integrity: sha512-Mc2b4xiIWKXIhBy2NBTwOxGD3nHLmq4keFk+d4/WL5fMsB8XdJRdtUlL87SqVCTSaf1BRuQQf1HvXZcy+rq3Nw==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
requiresBuild: true
dev: false
optional: true
/@next/swc-linux-x64-musl@13.5.6:
resolution: {integrity: sha512-CFHvP9Qz98NruJiUnCe61O6GveKKHpJLloXbDSWRhqhkJdZD2zU5hG+gtVJR//tyW897izuHpM6Gtf6+sNgJPQ==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
requiresBuild: true
dev: false
optional: true
/@next/swc-win32-arm64-msvc@13.5.6:
resolution: {integrity: sha512-aFv1ejfkbS7PUa1qVPwzDHjQWQtknzAZWGTKYIAaS4NMtBlk3VyA6AYn593pqNanlicewqyl2jUhQAaFV/qXsg==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [win32]
requiresBuild: true
dev: false
optional: true
/@next/swc-win32-ia32-msvc@13.5.6:
resolution: {integrity: sha512-XqqpHgEIlBHvzwG8sp/JXMFkLAfGLqkbVsyN+/Ih1mR8INb6YCc2x/Mbwi6hsAgUnqQztz8cvEbHJUbSl7RHDg==}
engines: {node: '>= 10'}
cpu: [ia32]
os: [win32]
requiresBuild: true
dev: false
optional: true
/@next/swc-win32-x64-msvc@13.5.6:
resolution: {integrity: sha512-Cqfe1YmOS7k+5mGu92nl5ULkzpKuxJrP3+4AEuPmrpFZ3BHxTY3TnHmU1On3bFmFFs6FbTcdF58CCUProGpIGQ==}
engines: {node: '>= 10'}
cpu: [x64]
os: [win32]
requiresBuild: true
dev: false
optional: true
/@swc/helpers@0.5.2:
resolution: {integrity: sha512-E4KcWTpoLHqwPHLxidpOqQbcrZVgi0rsmmZXUle1jXmJfuIf/UWpczUJ7MZZ5tlxytgJXyp0w4PGkkeLiuIdZw==}
dependencies:
tslib: 2.6.2
dev: false
/@types/escape-html@1.0.4:
resolution: {integrity: sha512-qZ72SFTgUAZ5a7Tj6kf2SHLetiH5S6f8G5frB2SPQ3EyF02kxdyBFf4Tz4banE3xCgGnKgWLt//a6VuYHKYJTg==}
dev: false
/@types/node@20.11.28:
resolution: {integrity: sha512-M/GPWVS2wLkSkNHVeLkrF2fD5Lx5UC4PxA0uZcKc6QqbIQUJyW1jVjueJYi1z8n0I5PxYrtpnPnWglE+y9A0KA==}
dependencies:
undici-types: 5.26.5
dev: true
/@types/prop-types@15.7.11:
resolution: {integrity: sha512-ga8y9v9uyeiLdpKddhxYQkxNDrfvuPrlFb0N1qnZZByvcElJaXthF1UhvCh9TLWJBEHeNtdnbysW7Y6Uq8CVng==}
dev: true
/@types/react@18.2.66:
resolution: {integrity: sha512-OYTmMI4UigXeFMF/j4uv0lBBEbongSgptPrHBxqME44h9+yNov+oL6Z3ocJKo0WyXR84sQUNeyIp9MRfckvZpg==}
dependencies:
'@types/prop-types': 15.7.11
'@types/scheduler': 0.16.8
csstype: 3.1.3
dev: true
/@types/scheduler@0.16.8:
resolution: {integrity: sha512-WZLiwShhwLRmeV6zH+GkbOFT6Z6VklCItrDioxUnv+u4Ll+8vKeFySoFyK/0ctcRpOmwAicELfmys1sDc/Rw+A==}
dev: true
/anymatch@3.1.3:
resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==}
engines: {node: '>= 8'}
dependencies:
normalize-path: 3.0.0
picomatch: 2.3.1
dev: false
/binary-extensions@2.3.0:
resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==}
engines: {node: '>=8'}
dev: false
/braces@3.0.2:
resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==}
engines: {node: '>=8'}
dependencies:
fill-range: 7.0.1
dev: false
/busboy@1.6.0:
resolution: {integrity: sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==}
engines: {node: '>=10.16.0'}
dependencies:
streamsearch: 1.1.0
dev: false
/caniuse-lite@1.0.30001597:
resolution: {integrity: sha512-7LjJvmQU6Sj7bL0j5b5WY/3n7utXUJvAe1lxhsHDbLmwX9mdL86Yjtr+5SRCyf8qME4M7pU2hswj0FpyBVCv9w==}
dev: false
/chokidar@3.6.0:
resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==}
engines: {node: '>= 8.10.0'}
dependencies:
anymatch: 3.1.3
braces: 3.0.2
glob-parent: 5.1.2
is-binary-path: 2.1.0
is-glob: 4.0.3
normalize-path: 3.0.0
readdirp: 3.6.0
optionalDependencies:
fsevents: 2.3.3
dev: false
/client-only@0.0.1:
resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==}
dev: false
/csstype@3.1.3:
resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==}
dev: true
/escape-html@1.0.3:
resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==}
dev: false
/fill-range@7.0.1:
resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==}
engines: {node: '>=8'}
dependencies:
to-regex-range: 5.0.1
dev: false
/fsevents@2.3.3:
resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
os: [darwin]
requiresBuild: true
dev: false
optional: true
/glob-parent@5.1.2:
resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==}
engines: {node: '>= 6'}
dependencies:
is-glob: 4.0.3
dev: false
/glob-to-regexp@0.4.1:
resolution: {integrity: sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==}
dev: false
/graceful-fs@4.2.11:
resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==}
dev: false
/immer@9.0.21:
resolution: {integrity: sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA==}
dev: false
/immutable@4.3.5:
resolution: {integrity: sha512-8eabxkth9gZatlwl5TBuJnCsoTADlL6ftEr7A4qgdaTsPyreilDSnUk57SO+jfKcNtxPa22U5KK6DSeAYhpBJw==}
dev: false
/is-binary-path@2.1.0:
resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==}
engines: {node: '>=8'}
dependencies:
binary-extensions: 2.3.0
dev: false
/is-extglob@2.1.1:
resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==}
engines: {node: '>=0.10.0'}
dev: false
/is-glob@4.0.3:
resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==}
engines: {node: '>=0.10.0'}
dependencies:
is-extglob: 2.1.1
dev: false
/is-number@7.0.0:
resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==}
engines: {node: '>=0.12.0'}
dev: false
/is-plain-object@5.0.0:
resolution: {integrity: sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==}
engines: {node: '>=0.10.0'}
dev: false
/js-tokens@4.0.0:
resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
dev: false
/loose-envify@1.4.0:
resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==}
hasBin: true
dependencies:
js-tokens: 4.0.0
dev: false
/nanoid@3.3.7:
resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==}
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
hasBin: true
dev: false
/next@13.5.6(react-dom@18.2.0)(react@18.2.0)(sass@1.72.0):
resolution: {integrity: sha512-Y2wTcTbO4WwEsVb4A8VSnOsG1I9ok+h74q0ZdxkwM3EODqrs4pasq7O0iUxbcS9VtWMicG7f3+HAj0r1+NtKSw==}
engines: {node: '>=16.14.0'}
hasBin: true
peerDependencies:
'@opentelemetry/api': ^1.1.0
react: ^18.2.0
react-dom: ^18.2.0
sass: ^1.3.0
peerDependenciesMeta:
'@opentelemetry/api':
optional: true
sass:
optional: true
dependencies:
'@next/env': 13.5.6
'@swc/helpers': 0.5.2
busboy: 1.6.0
caniuse-lite: 1.0.30001597
postcss: 8.4.31
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
sass: 1.72.0
styled-jsx: 5.1.1(react@18.2.0)
watchpack: 2.4.0
optionalDependencies:
'@next/swc-darwin-arm64': 13.5.6
'@next/swc-darwin-x64': 13.5.6
'@next/swc-linux-arm64-gnu': 13.5.6
'@next/swc-linux-arm64-musl': 13.5.6
'@next/swc-linux-x64-gnu': 13.5.6
'@next/swc-linux-x64-musl': 13.5.6
'@next/swc-win32-arm64-msvc': 13.5.6
'@next/swc-win32-ia32-msvc': 13.5.6
'@next/swc-win32-x64-msvc': 13.5.6
transitivePeerDependencies:
- '@babel/core'
- babel-plugin-macros
dev: false
/normalize-path@3.0.0:
resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==}
engines: {node: '>=0.10.0'}
dev: false
/picocolors@1.0.0:
resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==}
dev: false
/picomatch@2.3.1:
resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==}
engines: {node: '>=8.6'}
dev: false
/postcss@8.4.31:
resolution: {integrity: sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==}
engines: {node: ^10 || ^12 || >=14}
dependencies:
nanoid: 3.3.7
picocolors: 1.0.0
source-map-js: 1.0.2
dev: false
/react-dom@18.2.0(react@18.2.0):
resolution: {integrity: sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==}
peerDependencies:
react: ^18.2.0
dependencies:
loose-envify: 1.4.0
react: 18.2.0
scheduler: 0.23.0
dev: false
/react@18.2.0:
resolution: {integrity: sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==}
engines: {node: '>=0.10.0'}
dependencies:
loose-envify: 1.4.0
dev: false
/readdirp@3.6.0:
resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==}
engines: {node: '>=8.10.0'}
dependencies:
picomatch: 2.3.1
dev: false
/sass@1.72.0:
resolution: {integrity: sha512-Gpczt3WA56Ly0Mn8Sl21Vj94s1axi9hDIzDFn9Ph9x3C3p4nNyvsqJoQyVXKou6cBlfFWEgRW4rT8Tb4i3XnVA==}
engines: {node: '>=14.0.0'}
hasBin: true
dependencies:
chokidar: 3.6.0
immutable: 4.3.5
source-map-js: 1.0.2
dev: false
/scheduler@0.23.0:
resolution: {integrity: sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==}
dependencies:
loose-envify: 1.4.0
dev: false
/slate@0.94.1:
resolution: {integrity: sha512-GH/yizXr1ceBoZ9P9uebIaHe3dC/g6Plpf9nlUwnvoyf6V1UOYrRwkabtOCd3ZfIGxomY4P7lfgLr7FPH8/BKA==}
dependencies:
immer: 9.0.21
is-plain-object: 5.0.0
tiny-warning: 1.0.3
dev: false
/source-map-js@1.0.2:
resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==}
engines: {node: '>=0.10.0'}
dev: false
/streamsearch@1.1.0:
resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==}
engines: {node: '>=10.0.0'}
dev: false
/styled-jsx@5.1.1(react@18.2.0):
resolution: {integrity: sha512-pW7uC1l4mBZ8ugbiZrcIsiIvVx1UmTfw7UkC3Um2tmfUq9Bhk8IiyEIPl6F8agHgjzku6j0xQEZbfA5uSgSaCw==}
engines: {node: '>= 12.0.0'}
peerDependencies:
'@babel/core': '*'
babel-plugin-macros: '*'
react: '>= 16.8.0 || 17.x.x || ^18.0.0-0'
peerDependenciesMeta:
'@babel/core':
optional: true
babel-plugin-macros:
optional: true
dependencies:
client-only: 0.0.1
react: 18.2.0
dev: false
/tiny-warning@1.0.3:
resolution: {integrity: sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==}
dev: false
/to-regex-range@5.0.1:
resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
engines: {node: '>=8.0'}
dependencies:
is-number: 7.0.0
dev: false
/tslib@2.6.2:
resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==}
dev: false
/undici-types@5.26.5:
resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==}
dev: true
/watchpack@2.4.0:
resolution: {integrity: sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==}
engines: {node: '>=10.13.0'}
dependencies:
glob-to-regexp: 0.4.1
graceful-fs: 4.2.11
dev: false

View File

@@ -1,33 +0,0 @@
{
"compilerOptions": {
"target": "es5",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "bundler",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"incremental": true,
"plugins": [
{
"name": "next"
}
],
"paths": {
"@/*": ["./*"],
"@payloadcms/live-preview": ["../../../packages/live-preview"]
}
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
"exclude": ["node_modules"],
"references": [
{
"path": "../../../packages/live-preview"
}
]
}

View File

@@ -2,7 +2,7 @@ import type { Page } from '../payload-types.js'
export const postsPage: Partial<Page> = {
title: 'Posts',
slug: 'posts',
slug: 'live-preview/posts',
meta: {
title: 'Payload Website Template',
description: 'An open-source website built with Payload and Next.js.',

View File

@@ -1,5 +1,5 @@
export const formatLivePreviewURL = async ({ data, documentInfo }) => {
let baseURL = 'http://localhost:3001'
let baseURL = 'http://localhost:3000/live-preview'
// You can run async requests here, if needed
// For example, multi-tenant apps may need to lookup additional data

View File

@@ -54,6 +54,12 @@
"@payloadcms/plugin-cloud": [
"./packages/plugin-cloud/src"
],
"@payloadcms/live-preview-react": [
"./packages/live-preview-react/src/index.ts"
],
"@payloadcms/live-preview": [
"./packages/live-preview/src/index.ts"
],
"@payloadcms/plugin-cloud-storage": [
"./packages/plugin-cloud-storage/src"
],