chore: moves live preview test suite into main app folder
This commit is contained in:
35
test/live-preview/next-app/.gitignore
vendored
35
test/live-preview/next-app/.gitignore
vendored
@@ -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
|
||||
@@ -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.
|
||||
@@ -1,39 +0,0 @@
|
||||
'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 React from 'react'
|
||||
|
||||
import { useLivePreview } from '../../../../../../packages/live-preview-react/src'
|
||||
|
||||
export const PageClient: React.FC<{
|
||||
page: PageType
|
||||
}> = ({ page: initialPage }) => {
|
||||
const { data } = useLivePreview<PageType>({
|
||||
depth: 2,
|
||||
initialData: initialPage,
|
||||
serverURL: PAYLOAD_SERVER_URL,
|
||||
})
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<Hero {...data?.hero} />
|
||||
<Blocks
|
||||
blocks={[
|
||||
...(data?.layout ?? []),
|
||||
{
|
||||
blockName: 'Relationships',
|
||||
blockType: 'relationships',
|
||||
data,
|
||||
},
|
||||
]}
|
||||
disableTopPadding={
|
||||
!data?.hero || data?.hero?.type === 'none' || data?.hero?.type === 'lowImpact'
|
||||
}
|
||||
/>
|
||||
</React.Fragment>
|
||||
)
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
import { fetchDoc } from '@/app/_api/fetchDoc'
|
||||
import { fetchDocs } from '@/app/_api/fetchDocs'
|
||||
import { notFound } from 'next/navigation'
|
||||
import React from 'react'
|
||||
|
||||
import type { Page } from '../../../payload-types'
|
||||
|
||||
import { PageClient } from './page.client'
|
||||
|
||||
export default async function Page({ params: { slug = 'home' } }) {
|
||||
let page: Page | null = null
|
||||
|
||||
try {
|
||||
page = await fetchDoc<Page>({
|
||||
slug,
|
||||
collection: 'pages',
|
||||
})
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
}
|
||||
|
||||
if (!page) {
|
||||
return notFound()
|
||||
}
|
||||
|
||||
return <PageClient page={page} />
|
||||
}
|
||||
|
||||
export async function generateStaticParams() {
|
||||
try {
|
||||
const pages = await fetchDocs<Page>('pages')
|
||||
return pages?.map(({ slug }) => slug)
|
||||
} catch (error) {
|
||||
return []
|
||||
}
|
||||
}
|
||||
@@ -1,68 +0,0 @@
|
||||
'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 React from 'react'
|
||||
|
||||
import { useLivePreview } from '../../../../../../../packages/live-preview-react/src'
|
||||
|
||||
export const PostClient: React.FC<{
|
||||
post: PostType
|
||||
}> = ({ post: initialPost }) => {
|
||||
const { data } = useLivePreview<PostType>({
|
||||
depth: 2,
|
||||
initialData: initialPost,
|
||||
serverURL: PAYLOAD_SERVER_URL,
|
||||
})
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<PostHero post={data} />
|
||||
<Blocks blocks={data?.layout} />
|
||||
<Blocks
|
||||
blocks={[
|
||||
{
|
||||
blockName: 'Related Posts',
|
||||
blockType: 'relatedPosts',
|
||||
docs: data?.relatedPosts,
|
||||
introContent: [
|
||||
{
|
||||
type: 'h4',
|
||||
children: [
|
||||
{
|
||||
text: 'Related posts',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'p',
|
||||
children: [
|
||||
{
|
||||
text: 'The posts displayed here are individually selected for this page. Admins can select any number of related posts to display here and the layout will adjust accordingly. Alternatively, you could swap this out for the "Archive" block to automatically populate posts by category complete with pagination. To manage related posts, ',
|
||||
},
|
||||
{
|
||||
type: 'link',
|
||||
children: [
|
||||
{
|
||||
text: 'navigate to the admin dashboard',
|
||||
},
|
||||
],
|
||||
url: `/admin/collections/posts/${data?.id}`,
|
||||
},
|
||||
{
|
||||
text: '.',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
relationTo: 'posts',
|
||||
},
|
||||
]}
|
||||
disableTopPadding
|
||||
/>
|
||||
</React.Fragment>
|
||||
)
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
import { notFound } from 'next/navigation'
|
||||
import React from 'react'
|
||||
|
||||
import type { Post } from '../../../../payload-types'
|
||||
|
||||
import { fetchDoc } from '../../../_api/fetchDoc'
|
||||
import { fetchDocs } from '../../../_api/fetchDocs'
|
||||
import { PostClient } from './page.client'
|
||||
|
||||
export default async function Post({ params: { slug = '' } }) {
|
||||
let post: Post | null = null
|
||||
|
||||
try {
|
||||
post = await fetchDoc<Post>({
|
||||
slug,
|
||||
collection: 'posts',
|
||||
})
|
||||
} catch (error) {
|
||||
console.error(error) // eslint-disable-line no-console
|
||||
}
|
||||
|
||||
if (!post) {
|
||||
notFound()
|
||||
}
|
||||
|
||||
return <PostClient post={post} />
|
||||
}
|
||||
|
||||
export async function generateStaticParams() {
|
||||
try {
|
||||
const posts = await fetchDocs<Post>('posts')
|
||||
return posts?.map(({ slug }) => slug)
|
||||
} catch (error) {
|
||||
return []
|
||||
}
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
import QueryString from 'qs'
|
||||
|
||||
import type { Config } from '../../payload-types'
|
||||
|
||||
import { PAYLOAD_SERVER_URL } from './serverURL'
|
||||
|
||||
export const fetchDoc = async <T>(args: {
|
||||
collection: keyof Config['collections']
|
||||
depth?: number
|
||||
id?: string
|
||||
slug?: string
|
||||
}): Promise<T> => {
|
||||
const { collection, slug, id, depth = 2 } = args || {}
|
||||
|
||||
const queryString = QueryString.stringify(
|
||||
{
|
||||
...(slug ? { 'where[slug][equals]': slug } : {}),
|
||||
...(depth ? { depth } : {}),
|
||||
},
|
||||
{ addQueryPrefix: true },
|
||||
)
|
||||
|
||||
const doc: T = await fetch(`${PAYLOAD_SERVER_URL}/api/${collection}${queryString}`, {
|
||||
method: 'GET',
|
||||
cache: 'no-store',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
})
|
||||
?.then((res) => res.json())
|
||||
?.then((res) => {
|
||||
if (res.errors) throw new Error(res?.errors?.[0]?.message ?? 'Error fetching doc')
|
||||
return res?.docs?.[0]
|
||||
})
|
||||
|
||||
return doc
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
import type { Config } from '../../payload-types'
|
||||
|
||||
import { PAYLOAD_SERVER_URL } from './serverURL'
|
||||
|
||||
export const fetchDocs = async <T>(collection: keyof Config['collections']): 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',
|
||||
},
|
||||
})
|
||||
?.then((res) => res.json())
|
||||
?.then((res) => {
|
||||
if (res.errors) throw new Error(res?.errors?.[0]?.message ?? 'Error fetching docs')
|
||||
|
||||
return res?.docs
|
||||
})
|
||||
|
||||
return docs
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
import type { Footer } from '../../payload-types'
|
||||
|
||||
import { PAYLOAD_SERVER_URL } from './serverURL'
|
||||
|
||||
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',
|
||||
},
|
||||
})
|
||||
.then((res) => {
|
||||
if (!res.ok) throw new Error('Error fetching doc')
|
||||
return res.json()
|
||||
})
|
||||
?.then((res) => {
|
||||
if (res?.errors) throw new Error(res?.errors[0]?.message || 'Error fetching footer')
|
||||
return res
|
||||
})
|
||||
|
||||
return footer
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
import type { Header } from '../../payload-types'
|
||||
|
||||
import { PAYLOAD_SERVER_URL } from './serverURL'
|
||||
|
||||
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',
|
||||
},
|
||||
})
|
||||
?.then((res) => {
|
||||
if (!res.ok) throw new Error('Error fetching doc')
|
||||
return res.json()
|
||||
})
|
||||
?.then((res) => {
|
||||
if (res?.errors) throw new Error(res?.errors[0]?.message || 'Error fetching header')
|
||||
return res
|
||||
})
|
||||
|
||||
return header
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
export const PAYLOAD_SERVER_URL = 'http://localhost:3000'
|
||||
@@ -1,13 +0,0 @@
|
||||
@import '../../_css/common';
|
||||
|
||||
.archiveBlock {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.introContent {
|
||||
margin-bottom: calc(var(--base) * 2);
|
||||
|
||||
@include mid-break {
|
||||
margin-bottom: calc(var(--base) * 2);
|
||||
}
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
import React from 'react'
|
||||
|
||||
import type { ArchiveBlockProps } from './types'
|
||||
|
||||
import { CollectionArchive } from '../../_components/CollectionArchive'
|
||||
import { Gutter } from '../../_components/Gutter'
|
||||
import RichText from '../../_components/RichText'
|
||||
import classes from './index.module.scss'
|
||||
|
||||
export const ArchiveBlock: React.FC<
|
||||
ArchiveBlockProps & {
|
||||
id?: string
|
||||
}
|
||||
> = (props) => {
|
||||
const {
|
||||
id,
|
||||
categories,
|
||||
introContent,
|
||||
limit,
|
||||
populateBy,
|
||||
populatedDocs,
|
||||
populatedDocsTotal,
|
||||
relationTo,
|
||||
selectedDocs,
|
||||
} = props
|
||||
|
||||
return (
|
||||
<div className={classes.archiveBlock} id={`block-${id}`}>
|
||||
{introContent && (
|
||||
<Gutter className={classes.introContent}>
|
||||
<RichText content={introContent} />
|
||||
</Gutter>
|
||||
)}
|
||||
<CollectionArchive
|
||||
categories={categories}
|
||||
limit={limit}
|
||||
populateBy={populateBy}
|
||||
populatedDocs={populatedDocs}
|
||||
populatedDocsTotal={populatedDocsTotal}
|
||||
relationTo={relationTo}
|
||||
selectedDocs={selectedDocs}
|
||||
sort="-publishedDate"
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
import type { Page } from '../../../payload-types'
|
||||
|
||||
export type ArchiveBlockProps = Extract<
|
||||
Exclude<Page['layout'], undefined>[0],
|
||||
{ blockType: 'archive' }
|
||||
>
|
||||
@@ -1,47 +0,0 @@
|
||||
@use '../../_css/queries.scss' as *;
|
||||
|
||||
$spacer-h: calc(var(--block-padding) / 2);
|
||||
|
||||
.callToAction {
|
||||
padding-left: $spacer-h;
|
||||
padding-right: $spacer-h;
|
||||
position: relative;
|
||||
background-color: var(--color-base-100);
|
||||
color: var(--color-base-1000);
|
||||
}
|
||||
|
||||
.invert {
|
||||
background-color: var(--color-base-1000);
|
||||
color: var(--color-base-0);
|
||||
}
|
||||
|
||||
.wrap {
|
||||
display: flex;
|
||||
gap: $spacer-h;
|
||||
align-items: center;
|
||||
|
||||
@include mid-break {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
}
|
||||
}
|
||||
|
||||
.content {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.linkGroup {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
height: 100%;
|
||||
flex-shrink: 0;
|
||||
|
||||
> * {
|
||||
margin-bottom: calc(var(--base) / 2);
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
import React from 'react'
|
||||
|
||||
import type { Page } from '../../../payload-types'
|
||||
|
||||
import { Gutter } from '../../_components/Gutter'
|
||||
import { CMSLink } from '../../_components/Link'
|
||||
import RichText from '../../_components/RichText'
|
||||
import { VerticalPadding } from '../../_components/VerticalPadding'
|
||||
import classes from './index.module.scss'
|
||||
|
||||
type Props = Extract<Exclude<Page['layout'], undefined>[0], { blockType: 'cta' }>
|
||||
|
||||
export const CallToActionBlock: React.FC<
|
||||
Props & {
|
||||
id?: string
|
||||
}
|
||||
> = ({ invertBackground, links, richText }) => {
|
||||
return (
|
||||
<Gutter>
|
||||
<VerticalPadding
|
||||
className={[classes.callToAction, invertBackground && classes.invert]
|
||||
.filter(Boolean)
|
||||
.join(' ')}
|
||||
>
|
||||
<div className={classes.wrap}>
|
||||
<div className={classes.content}>
|
||||
<RichText className={classes.richText} content={richText} />
|
||||
</div>
|
||||
<div className={classes.linkGroup}>
|
||||
{(links || []).map(({ link }, i) => {
|
||||
return <CMSLink key={i} {...link} invert={invertBackground} />
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</VerticalPadding>
|
||||
</Gutter>
|
||||
)
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
@import '../../_css/common';
|
||||
|
||||
.grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(12, 1fr);
|
||||
gap: var(--base) calc(var(--base) * 2);
|
||||
|
||||
@include mid-break {
|
||||
grid-template-columns: repeat(6, 1fr);
|
||||
gap: var(--base) var(--base);
|
||||
}
|
||||
}
|
||||
|
||||
.column--oneThird {
|
||||
grid-column-end: span 4;
|
||||
}
|
||||
|
||||
.column--half {
|
||||
grid-column-end: span 6;
|
||||
}
|
||||
|
||||
.column--twoThirds {
|
||||
grid-column-end: span 8;
|
||||
}
|
||||
|
||||
.column--full {
|
||||
grid-column-end: span 12;
|
||||
}
|
||||
|
||||
.column {
|
||||
@include mid-break {
|
||||
grid-column-end: span 6;
|
||||
}
|
||||
|
||||
@include small-break {
|
||||
grid-column-end: span 6;
|
||||
}
|
||||
}
|
||||
|
||||
.link {
|
||||
margin-top: var(--base);
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
import React, { Fragment } from 'react'
|
||||
|
||||
import type { Page } from '../../../payload-types'
|
||||
|
||||
import { Gutter } from '../../_components/Gutter'
|
||||
import { CMSLink } from '../../_components/Link'
|
||||
import RichText from '../../_components/RichText'
|
||||
import classes from './index.module.scss'
|
||||
|
||||
type Props = Extract<Exclude<Page['layout'], undefined>[0], { blockType: 'content' }>
|
||||
|
||||
export const ContentBlock: React.FC<
|
||||
Props & {
|
||||
id?: string
|
||||
}
|
||||
> = (props) => {
|
||||
const { columns } = props
|
||||
|
||||
return (
|
||||
<Gutter className={classes.content}>
|
||||
<div className={classes.grid}>
|
||||
{columns && columns.length > 0 ? (
|
||||
<Fragment>
|
||||
{columns.map((col, index) => {
|
||||
const { enableLink, link, richText, size } = col
|
||||
|
||||
return (
|
||||
<div className={[classes.column, classes[`column--${size}`]].join(' ')} key={index}>
|
||||
<RichText content={richText} />
|
||||
{enableLink && <CMSLink className={classes.link} {...link} />}
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</Fragment>
|
||||
) : null}
|
||||
</div>
|
||||
</Gutter>
|
||||
)
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
.mediaBlock {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.caption {
|
||||
color: var(--color-base-500);
|
||||
margin-top: var(--base);
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
import type { StaticImageData } from 'next/image'
|
||||
|
||||
import React from 'react'
|
||||
|
||||
import type { Page } from '../../../payload-types'
|
||||
|
||||
import { Gutter } from '../../_components/Gutter'
|
||||
import { Media } from '../../_components/Media'
|
||||
import RichText from '../../_components/RichText'
|
||||
import classes from './index.module.scss'
|
||||
|
||||
type Props = Extract<Exclude<Page['layout'], undefined>[0], { blockType: 'mediaBlock' }> & {
|
||||
id?: string
|
||||
staticImage?: StaticImageData
|
||||
}
|
||||
|
||||
export const MediaBlock: React.FC<Props> = (props) => {
|
||||
const { media, position = 'default', staticImage } = props
|
||||
|
||||
let caption
|
||||
if (media && typeof media === 'object') caption = media.caption
|
||||
|
||||
return (
|
||||
<div className={classes.mediaBlock}>
|
||||
{position === 'fullscreen' && (
|
||||
<div className={classes.fullscreen}>
|
||||
<Media resource={media} src={staticImage} />
|
||||
</div>
|
||||
)}
|
||||
{position === 'default' && (
|
||||
<Gutter>
|
||||
<Media resource={media} src={staticImage} />
|
||||
</Gutter>
|
||||
)}
|
||||
{caption && (
|
||||
<Gutter className={classes.caption}>
|
||||
<RichText content={caption} />
|
||||
</Gutter>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
@import '../../_css/common';
|
||||
|
||||
.introContent {
|
||||
position: relative;
|
||||
margin-bottom: calc(var(--base) * 2);
|
||||
|
||||
@include mid-break {
|
||||
margin-bottom: var(--base);
|
||||
}
|
||||
}
|
||||
|
||||
.grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(12, 1fr);
|
||||
width: 100%;
|
||||
gap: var(--base) 40px;
|
||||
|
||||
@include mid-break {
|
||||
grid-template-columns: repeat(6, 1fr);
|
||||
gap: calc(var(--base) / 2) var(--base);
|
||||
}
|
||||
}
|
||||
|
||||
.column {
|
||||
grid-column-end: span 12;
|
||||
|
||||
@include mid-break {
|
||||
grid-column-end: span 6;
|
||||
}
|
||||
|
||||
@include small-break {
|
||||
grid-column-end: span 6;
|
||||
}
|
||||
}
|
||||
|
||||
.cols-half {
|
||||
grid-column-end: span 6;
|
||||
|
||||
@include mid-break {
|
||||
grid-column-end: span 6;
|
||||
}
|
||||
|
||||
@include small-break {
|
||||
grid-column-end: span 6;
|
||||
}
|
||||
}
|
||||
|
||||
.cols-thirds {
|
||||
grid-column-end: span 3;
|
||||
|
||||
@include mid-break {
|
||||
grid-column-end: span 6;
|
||||
}
|
||||
|
||||
@include small-break {
|
||||
grid-column-end: span 6;
|
||||
}
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
import React from 'react'
|
||||
|
||||
import type { Post } from '../../../payload-types'
|
||||
|
||||
import { Card } from '../../_components/Card'
|
||||
import { Gutter } from '../../_components/Gutter'
|
||||
import RichText from '../../_components/RichText'
|
||||
import classes from './index.module.scss'
|
||||
|
||||
export type RelatedPostsProps = {
|
||||
blockName: string
|
||||
blockType: 'relatedPosts'
|
||||
docs?: (Post | string)[] | null
|
||||
introContent?: any
|
||||
relationTo: 'posts'
|
||||
}
|
||||
|
||||
export const RelatedPosts: React.FC<RelatedPostsProps> = (props) => {
|
||||
const { docs, introContent, relationTo } = props
|
||||
|
||||
return (
|
||||
<div className={classes.relatedPosts}>
|
||||
{introContent && (
|
||||
<Gutter className={classes.introContent}>
|
||||
<RichText content={introContent} />
|
||||
</Gutter>
|
||||
)}
|
||||
<Gutter>
|
||||
<div className={classes.grid}>
|
||||
{docs?.map((doc, index) => {
|
||||
if (typeof doc === 'string') return null
|
||||
|
||||
return (
|
||||
<div
|
||||
className={[
|
||||
classes.column,
|
||||
docs.length === 2 && classes['cols-half'],
|
||||
docs.length >= 3 && classes['cols-thirds'],
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join(' ')}
|
||||
key={index}
|
||||
>
|
||||
<Card doc={doc} relationTo={relationTo} showCategories />
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</Gutter>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
@import '../../_css/common';
|
||||
|
||||
.relationshipsBlock {
|
||||
}
|
||||
|
||||
.array {
|
||||
border: 1px solid var(--color-base-100);
|
||||
padding: var(--base);
|
||||
|
||||
& > *:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
& > *:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
@@ -1,196 +0,0 @@
|
||||
import { Gutter } from '@/app/_components/Gutter'
|
||||
import RichText from '@/app/_components/RichText'
|
||||
import React, { Fragment } from 'react'
|
||||
|
||||
import type { Page } from '../../../payload-types'
|
||||
|
||||
import classes from './index.module.scss'
|
||||
|
||||
export type RelationshipsBlockProps = {
|
||||
blockName: string
|
||||
blockType: 'relationships'
|
||||
data: Page
|
||||
}
|
||||
|
||||
export const RelationshipsBlock: React.FC<RelationshipsBlockProps> = (props) => {
|
||||
const { data } = props
|
||||
|
||||
return (
|
||||
<div className={classes.relationshipsBlock}>
|
||||
<Gutter>
|
||||
<p>
|
||||
This block is for testing purposes only. It renders every possible type of relationship.
|
||||
</p>
|
||||
<p>
|
||||
<b>Rich Text — Slate:</b>
|
||||
</p>
|
||||
{data?.richTextSlate && <RichText content={data.richTextSlate} renderUploadFilenameOnly />}
|
||||
<p>
|
||||
<b>Rich Text — Lexical:</b>
|
||||
</p>
|
||||
{data?.richTextLexical && (
|
||||
<RichText content={data.richTextLexical} renderUploadFilenameOnly serializer="lexical" />
|
||||
)}
|
||||
<p>
|
||||
<b>Upload:</b>
|
||||
</p>
|
||||
{data?.relationshipAsUpload ? (
|
||||
<div>
|
||||
{typeof data?.relationshipAsUpload === 'string'
|
||||
? data?.relationshipAsUpload
|
||||
: data?.relationshipAsUpload.filename}
|
||||
</div>
|
||||
) : (
|
||||
<div>None</div>
|
||||
)}
|
||||
<p>
|
||||
<b>Monomorphic Has One:</b>
|
||||
</p>
|
||||
{data?.relationshipMonoHasOne ? (
|
||||
<div>
|
||||
{typeof data?.relationshipMonoHasOne === 'string'
|
||||
? data?.relationshipMonoHasOne
|
||||
: data?.relationshipMonoHasOne.title}
|
||||
</div>
|
||||
) : (
|
||||
<div>None</div>
|
||||
)}
|
||||
<p>
|
||||
<b>Monomorphic Has Many:</b>
|
||||
</p>
|
||||
{data?.relationshipMonoHasMany ? (
|
||||
<Fragment>
|
||||
{data?.relationshipMonoHasMany.length
|
||||
? data?.relationshipMonoHasMany?.map((item, index) =>
|
||||
item ? (
|
||||
<div key={index}>{typeof item === 'string' ? item : item.title}</div>
|
||||
) : (
|
||||
'null'
|
||||
),
|
||||
)
|
||||
: 'None'}
|
||||
</Fragment>
|
||||
) : (
|
||||
<div>None</div>
|
||||
)}
|
||||
<p>
|
||||
<b>Polymorphic Has One:</b>
|
||||
</p>
|
||||
{data?.relationshipPolyHasOne ? (
|
||||
<div>
|
||||
{typeof data?.relationshipPolyHasOne.value === 'string'
|
||||
? data?.relationshipPolyHasOne.value
|
||||
: data?.relationshipPolyHasOne.value.title}
|
||||
</div>
|
||||
) : (
|
||||
<div>None</div>
|
||||
)}
|
||||
<p>
|
||||
<b>Polymorphic Has Many:</b>
|
||||
</p>
|
||||
{data?.relationshipPolyHasMany ? (
|
||||
<Fragment>
|
||||
{data?.relationshipPolyHasMany.length
|
||||
? data?.relationshipPolyHasMany?.map((item, index) =>
|
||||
item.value ? (
|
||||
<div key={index}>
|
||||
{typeof item.value === 'string' ? item.value : item.value.title}
|
||||
</div>
|
||||
) : (
|
||||
'null'
|
||||
),
|
||||
)
|
||||
: 'None'}
|
||||
</Fragment>
|
||||
) : (
|
||||
<div>None</div>
|
||||
)}
|
||||
<p>
|
||||
<b>Array of Relationships:</b>
|
||||
</p>
|
||||
{data?.arrayOfRelationships?.map((item, index) => (
|
||||
<div className={classes.array} key={index}>
|
||||
<p>
|
||||
<b>Rich Text:</b>
|
||||
</p>
|
||||
{item?.richTextInArray && <RichText content={item.richTextInArray} />}
|
||||
<p>
|
||||
<b>Upload:</b>
|
||||
</p>
|
||||
{item?.uploadInArray ? (
|
||||
<div>
|
||||
{typeof item?.uploadInArray === 'string'
|
||||
? item?.uploadInArray
|
||||
: item?.uploadInArray.filename}
|
||||
</div>
|
||||
) : (
|
||||
<div>None</div>
|
||||
)}
|
||||
<p>
|
||||
<b>Monomorphic Has One:</b>
|
||||
</p>
|
||||
{item?.relationshipInArrayMonoHasOne ? (
|
||||
<div>
|
||||
{typeof item?.relationshipInArrayMonoHasOne === 'string'
|
||||
? item?.relationshipInArrayMonoHasOne
|
||||
: item?.relationshipInArrayMonoHasOne.title}
|
||||
</div>
|
||||
) : (
|
||||
<div>None</div>
|
||||
)}
|
||||
<p>
|
||||
<b>Monomorphic Has Many:</b>
|
||||
</p>
|
||||
{item?.relationshipInArrayMonoHasMany ? (
|
||||
<Fragment>
|
||||
{item?.relationshipInArrayMonoHasMany.length
|
||||
? item?.relationshipInArrayMonoHasMany?.map((rel, relIndex) =>
|
||||
rel ? (
|
||||
<div key={relIndex}>{typeof rel === 'string' ? rel : rel.title}</div>
|
||||
) : (
|
||||
'null'
|
||||
),
|
||||
)
|
||||
: 'None'}
|
||||
</Fragment>
|
||||
) : (
|
||||
<div>None</div>
|
||||
)}
|
||||
<p>
|
||||
<b>Polymorphic Has One:</b>
|
||||
</p>
|
||||
{item?.relationshipInArrayPolyHasOne ? (
|
||||
<div>
|
||||
{typeof item?.relationshipInArrayPolyHasOne.value === 'string'
|
||||
? item?.relationshipInArrayPolyHasOne.value
|
||||
: item?.relationshipInArrayPolyHasOne.value.title}
|
||||
</div>
|
||||
) : (
|
||||
<div>None</div>
|
||||
)}
|
||||
<p>
|
||||
<b>Polymorphic Has Many:</b>
|
||||
</p>
|
||||
{item?.relationshipInArrayPolyHasMany ? (
|
||||
<Fragment>
|
||||
{item?.relationshipInArrayPolyHasMany.length
|
||||
? item?.relationshipInArrayPolyHasMany?.map((rel, relIndex) =>
|
||||
rel.value ? (
|
||||
<div key={relIndex}>
|
||||
{typeof rel.value === 'string' ? rel.value : rel.value.title}
|
||||
</div>
|
||||
) : (
|
||||
'null'
|
||||
),
|
||||
)
|
||||
: 'None'}
|
||||
</Fragment>
|
||||
) : (
|
||||
<div>None</div>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</Gutter>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
.invert {
|
||||
background-color: var(--color-base-750);
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
import React from 'react'
|
||||
|
||||
import classes from './index.module.scss'
|
||||
|
||||
type Props = {
|
||||
children?: React.ReactNode
|
||||
className?: string
|
||||
id?: string
|
||||
invert?: boolean | null
|
||||
}
|
||||
|
||||
export const BackgroundColor: React.FC<Props> = (props) => {
|
||||
const { id, children, className, invert } = props
|
||||
|
||||
return (
|
||||
<div className={[invert && classes.invert, className].filter(Boolean).join(' ')} id={id}>
|
||||
{children}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,88 +0,0 @@
|
||||
import React, { Fragment } from 'react'
|
||||
|
||||
import type { Page } from '../../../payload-types.js'
|
||||
import type { RelationshipsBlockProps } from '../../_blocks/Relationships'
|
||||
import type { VerticalPaddingOptions } from '../VerticalPadding'
|
||||
|
||||
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'
|
||||
|
||||
const blockComponents = {
|
||||
archive: ArchiveBlock,
|
||||
content: ContentBlock,
|
||||
cta: CallToActionBlock,
|
||||
mediaBlock: MediaBlock,
|
||||
relatedPosts: RelatedPosts,
|
||||
relationships: RelationshipsBlock,
|
||||
}
|
||||
|
||||
type Block = NonNullable<Page['layout']>[number]
|
||||
|
||||
export const Blocks: React.FC<{
|
||||
blocks?: (Block | RelatedPostsProps | RelationshipsBlockProps)[] | null
|
||||
disableTopPadding?: boolean
|
||||
}> = (props) => {
|
||||
const { blocks, disableTopPadding } = props
|
||||
|
||||
const hasBlocks = blocks && Array.isArray(blocks) && blocks.length > 0
|
||||
|
||||
if (hasBlocks) {
|
||||
return (
|
||||
<Fragment>
|
||||
{blocks.map((block, index) => {
|
||||
const { blockName, blockType } = block
|
||||
|
||||
if (blockType && blockType in blockComponents) {
|
||||
const Block = blockComponents[blockType]
|
||||
|
||||
// the cta block is containerized, so we don't consider it to be inverted at the block-level
|
||||
const blockIsInverted =
|
||||
'invertBackground' in block && blockType !== 'cta' ? block.invertBackground : false
|
||||
const prevBlock = blocks[index - 1]
|
||||
|
||||
const prevBlockInverted =
|
||||
prevBlock && 'invertBackground' in prevBlock && prevBlock?.invertBackground
|
||||
|
||||
const isPrevSame = Boolean(blockIsInverted) === Boolean(prevBlockInverted)
|
||||
|
||||
let paddingTop: VerticalPaddingOptions = 'large'
|
||||
let paddingBottom: VerticalPaddingOptions = 'large'
|
||||
|
||||
if (prevBlock && isPrevSame) {
|
||||
paddingTop = 'none'
|
||||
}
|
||||
|
||||
if (index === blocks.length - 1) {
|
||||
paddingBottom = 'large'
|
||||
}
|
||||
|
||||
if (disableTopPadding && index === 0) {
|
||||
paddingTop = 'none'
|
||||
}
|
||||
|
||||
if (Block) {
|
||||
return (
|
||||
<BackgroundColor invert={blockIsInverted} key={index}>
|
||||
<VerticalPadding bottom={paddingBottom} top={paddingTop}>
|
||||
{/* @ts-expect-error */}
|
||||
<Block id={toKebabCase(blockName)} {...block} />
|
||||
</VerticalPadding>
|
||||
</BackgroundColor>
|
||||
)
|
||||
}
|
||||
}
|
||||
return null
|
||||
})}
|
||||
</Fragment>
|
||||
)
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
@@ -1,72 +0,0 @@
|
||||
@import '../../_css/type.scss';
|
||||
|
||||
.button {
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
display: inline-flex;
|
||||
justify-content: center;
|
||||
background-color: transparent;
|
||||
text-decoration: none;
|
||||
display: inline-flex;
|
||||
padding: 12px 24px;
|
||||
font-family: inherit;
|
||||
line-height: inherit;
|
||||
font-size: inherit;
|
||||
}
|
||||
|
||||
.content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-around;
|
||||
|
||||
svg {
|
||||
margin-right: calc(var(--base) / 2);
|
||||
width: var(--base);
|
||||
height: var(--base);
|
||||
}
|
||||
}
|
||||
|
||||
.label {
|
||||
@extend %label;
|
||||
text-align: center;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.appearance--primary {
|
||||
background-color: var(--color-base-1000);
|
||||
color: var(--color-base-0);
|
||||
}
|
||||
|
||||
.appearance--secondary {
|
||||
background-color: transparent;
|
||||
box-shadow: inset 0 0 0 1px var(--color-base-1000);
|
||||
}
|
||||
|
||||
.primary--invert {
|
||||
background-color: var(--color-base-0);
|
||||
color: var(--color-base-1000);
|
||||
}
|
||||
|
||||
.secondary--invert {
|
||||
background-color: var(--color-base-1000);
|
||||
box-shadow: inset 0 0 0 1px var(--color-base-0);
|
||||
}
|
||||
|
||||
.appearance--default {
|
||||
padding: 0;
|
||||
color: var(--theme-text);
|
||||
}
|
||||
|
||||
.appearance--none {
|
||||
padding: 0;
|
||||
color: var(--theme-text);
|
||||
|
||||
&:local() {
|
||||
.label {
|
||||
text-transform: none;
|
||||
line-height: inherit;
|
||||
font-size: inherit;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,78 +0,0 @@
|
||||
'use client'
|
||||
|
||||
import type { ElementType } from 'react'
|
||||
|
||||
import Link from 'next/link'
|
||||
import React from 'react'
|
||||
|
||||
import classes from './index.module.scss'
|
||||
|
||||
export type Props = {
|
||||
appearance?: 'default' | 'none' | 'primary' | 'secondary'
|
||||
className?: string
|
||||
disabled?: boolean
|
||||
el?: 'a' | 'button' | 'link'
|
||||
href?: string
|
||||
invert?: boolean
|
||||
label?: string
|
||||
newTab?: boolean
|
||||
onClick?: () => void
|
||||
type?: 'button' | 'submit'
|
||||
}
|
||||
|
||||
export const Button: React.FC<Props> = ({
|
||||
type = 'button',
|
||||
appearance,
|
||||
className: classNameFromProps,
|
||||
disabled,
|
||||
el: elFromProps = 'link',
|
||||
href,
|
||||
invert,
|
||||
label,
|
||||
newTab,
|
||||
onClick,
|
||||
}) => {
|
||||
let el = elFromProps
|
||||
|
||||
const newTabProps = newTab ? { rel: 'noopener noreferrer', target: '_blank' } : {}
|
||||
|
||||
const className = [
|
||||
classes.button,
|
||||
classNameFromProps,
|
||||
classes[`appearance--${appearance}`],
|
||||
invert && classes[`${appearance}--invert`],
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join(' ')
|
||||
|
||||
const content = (
|
||||
<div className={classes.content}>
|
||||
<span className={classes.label}>{label}</span>
|
||||
</div>
|
||||
)
|
||||
|
||||
if (onClick || type === 'submit') el = 'button'
|
||||
|
||||
if (el === 'link') {
|
||||
return (
|
||||
<Link className={className} href={href || ''} {...newTabProps} onClick={onClick}>
|
||||
{content}
|
||||
</Link>
|
||||
)
|
||||
}
|
||||
|
||||
const Element: ElementType = el
|
||||
|
||||
return (
|
||||
<Element
|
||||
className={className}
|
||||
href={href}
|
||||
type={type}
|
||||
{...newTabProps}
|
||||
disabled={disabled}
|
||||
onClick={onClick}
|
||||
>
|
||||
{content}
|
||||
</Element>
|
||||
)
|
||||
}
|
||||
@@ -1,106 +0,0 @@
|
||||
@import '../../_css/common';
|
||||
|
||||
.card {
|
||||
border: 1px var(--color-base-200) solid;
|
||||
border-radius: 4px;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.vertical {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.horizontal {
|
||||
flex-direction: row;
|
||||
|
||||
&:local() {
|
||||
.mediaWrapper {
|
||||
width: 150px;
|
||||
|
||||
@include mid-break {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@include mid-break {
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
|
||||
.content {
|
||||
padding: var(--base);
|
||||
flex-grow: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: calc(var(--base) / 2);
|
||||
|
||||
@include small-break {
|
||||
padding: calc(var(--base) / 2);
|
||||
gap: calc(var(--base) / 4);
|
||||
}
|
||||
}
|
||||
|
||||
.title {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.titleLink {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.centerAlign {
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.body {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.leader {
|
||||
@extend %label;
|
||||
display: flex;
|
||||
gap: var(--base);
|
||||
}
|
||||
|
||||
.description {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.hideImageOnMobile {
|
||||
@include mid-break {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.mediaWrapper {
|
||||
text-decoration: none;
|
||||
display: block;
|
||||
position: relative;
|
||||
aspect-ratio: 16 / 9;
|
||||
}
|
||||
|
||||
.image {
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.placeholder {
|
||||
background-color: var(--color-base-50);
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
@include mid-break {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
}
|
||||
}
|
||||
@@ -1,86 +0,0 @@
|
||||
import Link from 'next/link'
|
||||
import React, { Fragment } from 'react'
|
||||
|
||||
import type { Post } from '../../../payload-types'
|
||||
|
||||
import { Media } from '../Media'
|
||||
import classes from './index.module.scss'
|
||||
|
||||
export const Card: React.FC<{
|
||||
alignItems?: 'center'
|
||||
className?: string
|
||||
doc?: Post
|
||||
hideImagesOnMobile?: boolean
|
||||
orientation?: 'horizontal' | 'vertical'
|
||||
relationTo?: 'posts'
|
||||
showCategories?: boolean
|
||||
title?: string
|
||||
}> = (props) => {
|
||||
const {
|
||||
className,
|
||||
doc,
|
||||
orientation = 'vertical',
|
||||
relationTo,
|
||||
showCategories,
|
||||
title: titleFromProps,
|
||||
} = props
|
||||
|
||||
const { slug, categories, meta, title } = doc || {}
|
||||
const { description, image: metaImage } = meta || {}
|
||||
|
||||
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}`
|
||||
|
||||
return (
|
||||
<div
|
||||
className={[classes.card, className, orientation && classes[orientation]]
|
||||
.filter(Boolean)
|
||||
.join(' ')}
|
||||
>
|
||||
<Link className={classes.mediaWrapper} href={href}>
|
||||
{!metaImage && <div className={classes.placeholder}>No image</div>}
|
||||
{metaImage && typeof metaImage !== 'string' && (
|
||||
<Media fill imgClassName={classes.image} resource={metaImage} />
|
||||
)}
|
||||
</Link>
|
||||
<div className={classes.content}>
|
||||
{showCategories && hasCategories && (
|
||||
<div className={classes.leader}>
|
||||
{showCategories && hasCategories && (
|
||||
<div>
|
||||
{categories?.map((category, index) => {
|
||||
const titleFromCategory = typeof category === 'string' ? category : category.title
|
||||
|
||||
const categoryTitle = titleFromCategory || 'Untitled category'
|
||||
|
||||
const isLast = index === categories.length - 1
|
||||
|
||||
return (
|
||||
<Fragment key={index}>
|
||||
{categoryTitle}
|
||||
{!isLast && <Fragment>, </Fragment>}
|
||||
</Fragment>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
{titleToUse && (
|
||||
<h4 className={classes.title}>
|
||||
<Link className={classes.titleLink} href={href}>
|
||||
{titleToUse}
|
||||
</Link>
|
||||
</h4>
|
||||
)}
|
||||
{description && (
|
||||
<div className={classes.body}>
|
||||
{description && <p className={classes.description}>{sanitizedDescription}</p>}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
import React from 'react'
|
||||
|
||||
export const Chevron: React.FC<{
|
||||
className?: string
|
||||
rotate?: number
|
||||
}> = ({ className, rotate }) => {
|
||||
return (
|
||||
<svg
|
||||
className={className}
|
||||
height="100%"
|
||||
style={{
|
||||
transform: typeof rotate === 'number' ? `rotate(${rotate || 0}deg)` : undefined,
|
||||
}}
|
||||
viewBox="0 0 24 24"
|
||||
width="100%"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M23.245 4l-11.245 14.374-11.219-14.374-.781.619 12 15.381 12-15.391-.755-.609z"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
vectorEffect="non-scaling-stroke"
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
@@ -1,73 +0,0 @@
|
||||
@import '../../../_css/common';
|
||||
|
||||
// this is to make up for the space taken by the fixed header, since the scroll method does not accept an offset parameter
|
||||
.scrollRef {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: calc(var(--base) * -5);
|
||||
@include mid-break {
|
||||
top: calc(var(--base) * -2);
|
||||
}
|
||||
}
|
||||
|
||||
.introContent {
|
||||
position: relative;
|
||||
margin-bottom: calc(var(--base) * 2);
|
||||
|
||||
@include mid-break {
|
||||
margin-bottom: var(--base);
|
||||
}
|
||||
}
|
||||
|
||||
.resultCountWrapper {
|
||||
display: flex;
|
||||
margin-bottom: calc(var(--base) * 2);
|
||||
|
||||
@include mid-break {
|
||||
margin-bottom: var(--base);
|
||||
}
|
||||
}
|
||||
|
||||
.pageRange {
|
||||
margin-bottom: var(--base);
|
||||
|
||||
@include mid-break {
|
||||
margin-bottom: var(--base);
|
||||
}
|
||||
}
|
||||
|
||||
.list {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(12, 1fr);
|
||||
width: 100%;
|
||||
gap: var(--base) 40px;
|
||||
|
||||
@include mid-break {
|
||||
grid-template-columns: repeat(6, 1fr);
|
||||
gap: calc(var(--base) / 2) var(--base);
|
||||
}
|
||||
}
|
||||
|
||||
.column {
|
||||
grid-column-end: span 4;
|
||||
|
||||
@include mid-break {
|
||||
grid-column-end: span 6;
|
||||
}
|
||||
|
||||
@include small-break {
|
||||
grid-column-end: span 6;
|
||||
}
|
||||
}
|
||||
|
||||
.pagination {
|
||||
margin-top: calc(var(--base) * 2);
|
||||
|
||||
@include mid-break {
|
||||
margin-top: var(--base);
|
||||
}
|
||||
}
|
||||
@@ -1,188 +0,0 @@
|
||||
'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 { Card } from '../../Card'
|
||||
import { Gutter } from '../../Gutter'
|
||||
import { PageRange } from '../../PageRange'
|
||||
import { Pagination } from '../../Pagination'
|
||||
import classes from './index.module.scss'
|
||||
|
||||
type Result = {
|
||||
docs: (Post | string)[]
|
||||
hasNextPage: boolean
|
||||
hasPrevPage: boolean
|
||||
nextPage: number
|
||||
page: number
|
||||
prevPage: number
|
||||
totalDocs: number
|
||||
totalPages: number
|
||||
}
|
||||
|
||||
export type Props = Omit<ArchiveBlockProps, 'blockType'> & {
|
||||
className?: string
|
||||
onResultChange?: (result: Result) => void // eslint-disable-line no-unused-vars
|
||||
showPageRange?: boolean
|
||||
sort?: string
|
||||
}
|
||||
|
||||
export const CollectionArchiveByCollection: React.FC<Props> = (props) => {
|
||||
const {
|
||||
categories: catsFromProps,
|
||||
className,
|
||||
limit = 10,
|
||||
onResultChange,
|
||||
populatedDocs,
|
||||
populatedDocsTotal,
|
||||
relationTo,
|
||||
showPageRange,
|
||||
sort = '-createdAt',
|
||||
} = props
|
||||
|
||||
const [results, setResults] = useState<Result>({
|
||||
docs: populatedDocs?.map((doc) => doc.value) || [],
|
||||
hasNextPage: false,
|
||||
hasPrevPage: false,
|
||||
nextPage: 1,
|
||||
page: 1,
|
||||
prevPage: 1,
|
||||
totalDocs: typeof populatedDocsTotal === 'number' ? populatedDocsTotal : 0,
|
||||
totalPages: 1,
|
||||
})
|
||||
|
||||
const [isLoading, setIsLoading] = useState(false)
|
||||
const [error, setError] = useState<string | undefined>(undefined)
|
||||
const scrollRef = useRef<HTMLDivElement>(null)
|
||||
const hasHydrated = useRef(false)
|
||||
const [page, setPage] = useState(1)
|
||||
|
||||
const scrollToRef = useCallback(() => {
|
||||
const { current } = scrollRef
|
||||
if (current) {
|
||||
// current.scrollIntoView({
|
||||
// behavior: 'smooth',
|
||||
// })
|
||||
}
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
if (!isLoading && typeof results.page !== 'undefined') {
|
||||
// scrollToRef()
|
||||
}
|
||||
}, [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(() => {
|
||||
if (hasHydrated) {
|
||||
setIsLoading(true)
|
||||
}
|
||||
}, 500)
|
||||
|
||||
const searchQuery = qs.stringify(
|
||||
{
|
||||
depth: 1,
|
||||
limit,
|
||||
page,
|
||||
sort,
|
||||
where: {
|
||||
...(catsFromProps && catsFromProps?.length > 0
|
||||
? {
|
||||
categories: {
|
||||
in:
|
||||
typeof catsFromProps === 'string'
|
||||
? [catsFromProps]
|
||||
: catsFromProps
|
||||
.map((cat) => (typeof cat === 'object' && cat !== null ? cat.id : cat))
|
||||
.join(','),
|
||||
},
|
||||
}
|
||||
: {}),
|
||||
},
|
||||
},
|
||||
{ encode: false },
|
||||
)
|
||||
|
||||
const makeRequest = async () => {
|
||||
try {
|
||||
const req = await fetch(`${PAYLOAD_SERVER_URL}/api/${relationTo}?${searchQuery}`)
|
||||
const json = await req.json()
|
||||
clearTimeout(timer)
|
||||
hasHydrated.current = true
|
||||
|
||||
const { docs } = json as { docs: Post[] }
|
||||
|
||||
if (docs && Array.isArray(docs)) {
|
||||
setResults(json)
|
||||
setIsLoading(false)
|
||||
if (typeof onResultChange === 'function') {
|
||||
onResultChange(json)
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.warn(err) // eslint-disable-line no-console
|
||||
setIsLoading(false)
|
||||
setError(`Unable to load "${relationTo} archive" data at this time.`)
|
||||
}
|
||||
}
|
||||
|
||||
makeRequest()
|
||||
|
||||
return () => {
|
||||
if (timer) clearTimeout(timer)
|
||||
}
|
||||
}, [page, catsFromProps, relationTo, onResultChange, sort, limit])
|
||||
|
||||
return (
|
||||
<div className={[classes.collectionArchive, className].filter(Boolean).join(' ')}>
|
||||
<div className={classes.scrollRef} ref={scrollRef} />
|
||||
{!isLoading && error && <Gutter>{error}</Gutter>}
|
||||
<Fragment>
|
||||
{showPageRange !== false && (
|
||||
<Gutter>
|
||||
<div className={classes.pageRange}>
|
||||
<PageRange
|
||||
collection={relationTo}
|
||||
currentPage={results.page}
|
||||
limit={limit}
|
||||
totalDocs={results.totalDocs}
|
||||
/>
|
||||
</div>
|
||||
</Gutter>
|
||||
)}
|
||||
<Gutter>
|
||||
<div className={classes.grid}>
|
||||
{results.docs?.map((result, index) => {
|
||||
if (typeof result === 'string') {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={classes.column} key={index}>
|
||||
<Card doc={result} relationTo="posts" showCategories />
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
{results.totalPages > 1 && (
|
||||
<Pagination
|
||||
className={classes.pagination}
|
||||
onClick={setPage}
|
||||
page={results.page}
|
||||
totalPages={results.totalPages}
|
||||
/>
|
||||
)}
|
||||
</Gutter>
|
||||
</Fragment>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
@import '../../../_css/common';
|
||||
|
||||
.grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(12, 1fr);
|
||||
width: 100%;
|
||||
gap: var(--base) 40px;
|
||||
|
||||
@include mid-break {
|
||||
grid-template-columns: repeat(6, 1fr);
|
||||
gap: calc(var(--base) / 2) var(--base);
|
||||
}
|
||||
}
|
||||
|
||||
.column {
|
||||
grid-column-end: span 4;
|
||||
|
||||
@include mid-break {
|
||||
grid-column-end: span 6;
|
||||
}
|
||||
|
||||
@include small-break {
|
||||
grid-column-end: span 6;
|
||||
}
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
'use client'
|
||||
|
||||
import React, { Fragment } from 'react'
|
||||
|
||||
import type { ArchiveBlockProps } from '../../../_blocks/ArchiveBlock/types'
|
||||
|
||||
import { Card } from '../../Card'
|
||||
import { Gutter } from '../../Gutter'
|
||||
import classes from './index.module.scss'
|
||||
|
||||
export type Props = {
|
||||
className?: string
|
||||
selectedDocs?: ArchiveBlockProps['selectedDocs']
|
||||
}
|
||||
|
||||
export const CollectionArchiveBySelection: React.FC<Props> = (props) => {
|
||||
const { className, selectedDocs } = props
|
||||
|
||||
const result = selectedDocs?.map((doc) => doc.value)
|
||||
|
||||
return (
|
||||
<div className={[classes.collectionArchive, className].filter(Boolean).join(' ')}>
|
||||
<Fragment>
|
||||
<Gutter>
|
||||
<div className={classes.grid}>
|
||||
{result?.map((result, index) => {
|
||||
if (typeof result === 'string') {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={classes.column} key={index}>
|
||||
<Card doc={result} relationTo="posts" showCategories />
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</Gutter>
|
||||
</Fragment>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
import React from 'react'
|
||||
|
||||
import type { ArchiveBlockProps } from '../../_blocks/ArchiveBlock/types'
|
||||
|
||||
import { CollectionArchiveByCollection } from './PopulateByCollection'
|
||||
import { CollectionArchiveBySelection } from './PopulateBySelection'
|
||||
|
||||
export type Props = Omit<ArchiveBlockProps, 'blockType'> & {
|
||||
className?: string
|
||||
sort?: string
|
||||
}
|
||||
|
||||
export const CollectionArchive: React.FC<Props> = (props) => {
|
||||
const { className, populateBy, selectedDocs } = props
|
||||
|
||||
if (populateBy === 'selection') {
|
||||
return <CollectionArchiveBySelection className={className} selectedDocs={selectedDocs} />
|
||||
}
|
||||
|
||||
if (populateBy === 'collection') {
|
||||
return <CollectionArchiveByCollection {...props} className={className} />
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
@use '../../_css/queries.scss' as *;
|
||||
|
||||
.footer {
|
||||
padding: calc(var(--base) * 4) 0;
|
||||
background-color: var(--color-base-1000);
|
||||
color: var(--color-base-0);
|
||||
|
||||
@include small-break {
|
||||
padding: calc(var(--base) * 2) 0;
|
||||
}
|
||||
}
|
||||
|
||||
.wrap {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
flex-wrap: wrap;
|
||||
gap: calc(var(--base) / 2) var(--base);
|
||||
}
|
||||
|
||||
.logo {
|
||||
width: 150px;
|
||||
}
|
||||
|
||||
.nav {
|
||||
display: flex;
|
||||
gap: calc(var(--base) / 4) var(--base);
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
opacity: 1;
|
||||
transition: opacity 100ms linear;
|
||||
visibility: visible;
|
||||
|
||||
> * {
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
import Link from 'next/link'
|
||||
import React from 'react'
|
||||
|
||||
import { fetchFooter } from '../../_api/fetchFooter'
|
||||
import { Gutter } from '../Gutter'
|
||||
import { CMSLink } from '../Link'
|
||||
import classes from './index.module.scss'
|
||||
|
||||
export async function Footer() {
|
||||
const footer = await fetchFooter()
|
||||
|
||||
const navItems = footer?.navItems || []
|
||||
|
||||
return (
|
||||
<footer className={classes.footer}>
|
||||
<Gutter className={classes.wrap}>
|
||||
<Link href="/">
|
||||
<picture>
|
||||
<img
|
||||
alt="Payload Logo"
|
||||
className={classes.logo}
|
||||
src="https://raw.githubusercontent.com/payloadcms/payload/main/packages/payload/src/admin/assets/images/payload-logo-light.svg"
|
||||
/>
|
||||
</picture>
|
||||
</Link>
|
||||
<nav className={classes.nav}>
|
||||
{navItems.map(({ link }, i) => {
|
||||
return <CMSLink key={i} {...link} />
|
||||
})}
|
||||
<Link href="/admin">Admin</Link>
|
||||
<Link href="https://github.com/payloadcms/payload/tree/main/templates/ecommerce">
|
||||
Source Code
|
||||
</Link>
|
||||
<Link href="https://github.com/payloadcms/payload">Payload</Link>
|
||||
</nav>
|
||||
</Gutter>
|
||||
</footer>
|
||||
)
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
.gutter {
|
||||
max-width: 1920px;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
.gutterLeft {
|
||||
padding-left: var(--gutter-h);
|
||||
}
|
||||
|
||||
.gutterRight {
|
||||
padding-right: var(--gutter-h);
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
import type { Ref } from 'react'
|
||||
|
||||
import React, { forwardRef } from 'react'
|
||||
|
||||
import classes from './index.module.scss'
|
||||
|
||||
type Props = {
|
||||
children: React.ReactNode
|
||||
className?: string
|
||||
left?: boolean
|
||||
ref?: Ref<HTMLDivElement>
|
||||
right?: boolean
|
||||
}
|
||||
|
||||
export const Gutter: React.FC<Props> = forwardRef<HTMLDivElement, Props>((props, ref) => {
|
||||
const { children, className, left = true, right = true } = props
|
||||
|
||||
return (
|
||||
<div
|
||||
className={[
|
||||
classes.gutter,
|
||||
left && classes.gutterLeft,
|
||||
right && classes.gutterRight,
|
||||
className,
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join(' ')}
|
||||
ref={ref}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
)
|
||||
})
|
||||
|
||||
Gutter.displayName = 'Gutter'
|
||||
@@ -1,12 +0,0 @@
|
||||
@use '../../../_css/queries.scss' as *;
|
||||
|
||||
.nav {
|
||||
display: flex;
|
||||
gap: calc(var(--base) / 4) var(--base);
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
|
||||
> * {
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
'use client'
|
||||
|
||||
import React from 'react'
|
||||
|
||||
import type { Header as HeaderType } from '../../../../payload-types'
|
||||
|
||||
import { CMSLink } from '../../Link'
|
||||
import classes from './index.module.scss'
|
||||
|
||||
export const HeaderNav: React.FC<{ header: HeaderType }> = ({ header }) => {
|
||||
const navItems = header?.navItems || []
|
||||
|
||||
return (
|
||||
<nav className={classes.nav}>
|
||||
{navItems.map(({ link }, i) => {
|
||||
return <CMSLink key={i} {...link} />
|
||||
})}
|
||||
</nav>
|
||||
)
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
@use '../../_css/queries.scss' as *;
|
||||
|
||||
.header {
|
||||
padding: var(--base) 0;
|
||||
}
|
||||
|
||||
.wrap {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
flex-wrap: wrap;
|
||||
gap: calc(var(--base) / 2) var(--base);
|
||||
}
|
||||
|
||||
.logo {
|
||||
width: 150px;
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
{
|
||||
/* eslint-disable @next/next/no-img-element */
|
||||
}
|
||||
|
||||
import { fetchHeader } from '@/app/_api/fetchHeader'
|
||||
import Link from 'next/link'
|
||||
import React from 'react'
|
||||
|
||||
import { Gutter } from '../Gutter'
|
||||
import { HeaderNav } from './Nav'
|
||||
import classes from './index.module.scss'
|
||||
|
||||
export async function Header() {
|
||||
const header = await fetchHeader()
|
||||
|
||||
return (
|
||||
<header className={classes.header}>
|
||||
<Gutter className={classes.wrap}>
|
||||
<Link href="/">
|
||||
<img
|
||||
alt="Payload Logo"
|
||||
className={classes.logo}
|
||||
src="https://raw.githubusercontent.com/payloadcms/payload/main/packages/payload/src/admin/assets/images/payload-logo-dark.svg"
|
||||
/>
|
||||
</Link>
|
||||
<HeaderNav header={header} />
|
||||
</Gutter>
|
||||
</header>
|
||||
)
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
import React from 'react'
|
||||
|
||||
import type { Page } from '../../../payload-types'
|
||||
|
||||
import { HighImpactHero } from '../../_heros/HighImpact'
|
||||
import { LowImpactHero } from '../../_heros/LowImpact'
|
||||
|
||||
const heroes = {
|
||||
highImpact: HighImpactHero,
|
||||
lowImpact: LowImpactHero,
|
||||
}
|
||||
|
||||
export const Hero: React.FC<Page['hero']> = (props) => {
|
||||
const { type } = props || {}
|
||||
|
||||
if (!type || type === 'none') return null
|
||||
|
||||
const HeroToRender = heroes[type]
|
||||
|
||||
if (!HeroToRender) return null
|
||||
|
||||
return <HeroToRender {...props} />
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
import Link from 'next/link'
|
||||
import React from 'react'
|
||||
|
||||
import type { Page } from '../../../payload-types'
|
||||
import type { Props as ButtonProps } from '../Button'
|
||||
|
||||
import { Button } from '../Button'
|
||||
|
||||
type CMSLinkType = {
|
||||
appearance?: ButtonProps['appearance']
|
||||
children?: React.ReactNode
|
||||
className?: string
|
||||
invert?: ButtonProps['invert']
|
||||
label?: string
|
||||
newTab?: boolean
|
||||
reference?: {
|
||||
relationTo: 'pages' | 'posts'
|
||||
value: Page | string
|
||||
}
|
||||
type?: 'custom' | 'reference'
|
||||
url?: string
|
||||
}
|
||||
|
||||
export const CMSLink: React.FC<CMSLinkType> = ({
|
||||
type,
|
||||
appearance,
|
||||
children,
|
||||
className,
|
||||
invert,
|
||||
label,
|
||||
newTab,
|
||||
reference,
|
||||
url,
|
||||
}) => {
|
||||
const href =
|
||||
type === 'reference' && typeof reference?.value === 'object' && reference.value.slug
|
||||
? `/${reference.value.slug}`
|
||||
: url
|
||||
|
||||
if (!href) return null
|
||||
|
||||
if (!appearance) {
|
||||
const newTabProps = newTab ? { rel: 'noopener noreferrer', target: '_blank' } : {}
|
||||
|
||||
if (href || url) {
|
||||
return (
|
||||
<Link {...newTabProps} className={className} href={href || url || ''}>
|
||||
{label && label}
|
||||
{children && children}
|
||||
</Link>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Button
|
||||
appearance={appearance}
|
||||
className={className}
|
||||
href={href}
|
||||
invert={invert}
|
||||
label={label}
|
||||
newTab={newTab}
|
||||
/>
|
||||
)
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
.placeholder-color-light {
|
||||
background-color: rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.placeholder {
|
||||
background-color: var(--color-base-50);
|
||||
}
|
||||
@@ -1,78 +0,0 @@
|
||||
'use client'
|
||||
|
||||
import type { StaticImageData } from 'next/image'
|
||||
|
||||
import { PAYLOAD_SERVER_URL } from '@/app/_api/serverURL'
|
||||
import NextImage from 'next/image'
|
||||
import React from 'react'
|
||||
|
||||
import type { Props as MediaProps } from '../types'
|
||||
|
||||
import cssVariables from '../../../cssVariables'
|
||||
import classes from './index.module.scss'
|
||||
|
||||
const { breakpoints } = cssVariables
|
||||
|
||||
export const Image: React.FC<MediaProps> = (props) => {
|
||||
const {
|
||||
alt: altFromProps,
|
||||
fill,
|
||||
imgClassName,
|
||||
onClick,
|
||||
onLoad: onLoadFromProps,
|
||||
priority,
|
||||
resource,
|
||||
src: srcFromProps,
|
||||
} = props
|
||||
|
||||
const [isLoading, setIsLoading] = React.useState(true)
|
||||
|
||||
let width: number | undefined
|
||||
let height: number | undefined
|
||||
let alt = altFromProps
|
||||
let src: StaticImageData | string = srcFromProps || ''
|
||||
|
||||
if (!src && resource && typeof resource !== 'string') {
|
||||
const {
|
||||
alt: altFromResource,
|
||||
filename: fullFilename,
|
||||
height: fullHeight,
|
||||
width: fullWidth,
|
||||
} = resource
|
||||
|
||||
width = fullWidth || undefined
|
||||
height = fullHeight || undefined
|
||||
alt = altFromResource
|
||||
|
||||
const filename = fullFilename
|
||||
|
||||
src = `${PAYLOAD_SERVER_URL}/media/${filename}`
|
||||
}
|
||||
|
||||
// NOTE: this is used by the browser to determine which image to download at different screen sizes
|
||||
const sizes = Object.entries(breakpoints)
|
||||
.map(([, value]) => `(max-width: ${value}px) ${value}px`)
|
||||
.join(', ')
|
||||
|
||||
return (
|
||||
<NextImage
|
||||
alt={alt || ''}
|
||||
className={[isLoading && classes.placeholder, classes.image, imgClassName]
|
||||
.filter(Boolean)
|
||||
.join(' ')}
|
||||
fill={fill}
|
||||
height={!fill ? height : undefined}
|
||||
onClick={onClick}
|
||||
onLoad={() => {
|
||||
setIsLoading(false)
|
||||
if (typeof onLoadFromProps === 'function') {
|
||||
onLoadFromProps()
|
||||
}
|
||||
}}
|
||||
priority={priority}
|
||||
sizes={sizes}
|
||||
src={src}
|
||||
width={!fill ? width : undefined}
|
||||
/>
|
||||
)
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
.video {
|
||||
max-width: 100%;
|
||||
width: 100%;
|
||||
background-color: var(--color-base-50);
|
||||
}
|
||||
|
||||
.cover {
|
||||
object-fit: cover;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
'use client'
|
||||
|
||||
import { PAYLOAD_SERVER_URL } from '@/app/_api/serverURL'
|
||||
import React, { useEffect, useRef } from 'react'
|
||||
|
||||
import type { Props as MediaProps } from '../types'
|
||||
|
||||
import classes from './index.module.scss'
|
||||
|
||||
export const Video: React.FC<MediaProps> = (props) => {
|
||||
const { onClick, resource, videoClassName } = props
|
||||
|
||||
const videoRef = useRef<HTMLVideoElement>(null)
|
||||
// const [showFallback] = useState<boolean>()
|
||||
|
||||
useEffect(() => {
|
||||
const { current: video } = videoRef
|
||||
if (video) {
|
||||
video.addEventListener('suspend', () => {
|
||||
// setShowFallback(true);
|
||||
// console.warn('Video was suspended, rendering fallback image.')
|
||||
})
|
||||
}
|
||||
}, [])
|
||||
|
||||
if (resource && typeof resource !== 'string') {
|
||||
const { filename } = resource
|
||||
|
||||
return (
|
||||
<video
|
||||
autoPlay
|
||||
className={[classes.video, videoClassName].filter(Boolean).join(' ')}
|
||||
controls={false}
|
||||
loop
|
||||
muted
|
||||
onClick={onClick}
|
||||
playsInline
|
||||
ref={videoRef}
|
||||
>
|
||||
<source src={`${PAYLOAD_SERVER_URL}/media/${filename}`} />
|
||||
</video>
|
||||
)
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
import type { ElementType } from 'react'
|
||||
|
||||
import React, { Fragment } from 'react'
|
||||
|
||||
import type { Props } from './types'
|
||||
|
||||
import { Image } from './Image'
|
||||
import { Video } from './Video'
|
||||
|
||||
export const Media: React.FC<Props> = (props) => {
|
||||
const { className, htmlElement = 'div', resource } = props
|
||||
|
||||
const isVideo = typeof resource !== 'string' && resource?.mimeType?.includes('video')
|
||||
const Tag = (htmlElement as ElementType) || Fragment
|
||||
|
||||
return (
|
||||
<Tag
|
||||
{...(htmlElement !== null
|
||||
? {
|
||||
className,
|
||||
}
|
||||
: {})}
|
||||
>
|
||||
{isVideo ? (
|
||||
<Video {...props} />
|
||||
) : (
|
||||
<Image {...props} /> // eslint-disable-line
|
||||
)}
|
||||
</Tag>
|
||||
)
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
import type { StaticImageData } from 'next/image'
|
||||
import type { ElementType, Ref } from 'react'
|
||||
|
||||
import type { Media as MediaType } from '../../../payload-types'
|
||||
|
||||
export interface Props {
|
||||
alt?: string
|
||||
className?: string
|
||||
fill?: boolean // for NextImage only
|
||||
htmlElement?: ElementType | null
|
||||
imgClassName?: string
|
||||
onClick?: () => void
|
||||
onLoad?: () => void
|
||||
priority?: boolean // for NextImage only
|
||||
ref?: Ref<HTMLImageElement | HTMLVideoElement | null>
|
||||
resource?: MediaType | string // for Payload media
|
||||
size?: string // for NextImage only
|
||||
src?: StaticImageData // for static media
|
||||
videoClassName?: string
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
@import '../../_css/common';
|
||||
|
||||
.pageRange {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin: 0 var(--base(0.5));
|
||||
}
|
||||
|
||||
.divider {
|
||||
margin: 0 2px;
|
||||
}
|
||||
|
||||
.hyperlink {
|
||||
display: flex;
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
import React from 'react'
|
||||
|
||||
import classes from './index.module.scss'
|
||||
|
||||
const defaultLabels = {
|
||||
plural: 'Docs',
|
||||
singular: 'Doc',
|
||||
}
|
||||
|
||||
const defaultCollectionLabels = {
|
||||
products: {
|
||||
plural: 'Products',
|
||||
singular: 'Product',
|
||||
},
|
||||
}
|
||||
|
||||
export const PageRange: React.FC<{
|
||||
className?: string
|
||||
collection?: string
|
||||
collectionLabels?: {
|
||||
plural?: string
|
||||
singular?: string
|
||||
}
|
||||
currentPage?: number
|
||||
limit?: number
|
||||
totalDocs?: number
|
||||
}> = (props) => {
|
||||
const {
|
||||
className,
|
||||
collection,
|
||||
collectionLabels: collectionLabelsFromProps,
|
||||
currentPage,
|
||||
limit,
|
||||
totalDocs,
|
||||
} = props
|
||||
|
||||
const indexStart = (currentPage ? currentPage - 1 : 1) * (limit || 1) + 1
|
||||
let indexEnd = (currentPage || 1) * (limit || 1)
|
||||
if (totalDocs && indexEnd > totalDocs) indexEnd = totalDocs
|
||||
|
||||
const { plural, singular } =
|
||||
collectionLabelsFromProps || defaultCollectionLabels[collection || ''] || defaultLabels || {}
|
||||
|
||||
return (
|
||||
<div className={[className, classes.pageRange].filter(Boolean).join(' ')}>
|
||||
{(typeof totalDocs === 'undefined' || totalDocs === 0) && 'Search produced no results.'}
|
||||
{typeof totalDocs !== 'undefined' &&
|
||||
totalDocs > 0 &&
|
||||
`Showing ${indexStart} - ${indexEnd} of ${totalDocs} ${totalDocs > 1 ? plural : singular}`}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
@import '../../_css/type.scss';
|
||||
|
||||
.pagination {
|
||||
@extend %label;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: calc(var(--base) / 2);
|
||||
}
|
||||
|
||||
.button {
|
||||
all: unset;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
display: flex;
|
||||
padding: calc(var(--base) / 2);
|
||||
color: var(--color-base-500);
|
||||
border: 1px solid var(--color-base-200);
|
||||
|
||||
&:disabled {
|
||||
cursor: not-allowed;
|
||||
color: var(--color-base-200);
|
||||
border-color: var(--color-base-150);
|
||||
}
|
||||
}
|
||||
|
||||
.icon {
|
||||
width: calc(var(--base) / 2);
|
||||
height: calc(var(--base) / 2);
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
import React from 'react'
|
||||
|
||||
import { Chevron } from '../Chevron'
|
||||
import classes from './index.module.scss'
|
||||
|
||||
export const Pagination: React.FC<{
|
||||
className?: string
|
||||
onClick: (page: number) => void
|
||||
page: number
|
||||
totalPages: number
|
||||
}> = (props) => {
|
||||
const { className, onClick, page, totalPages } = props
|
||||
const hasNextPage = page < totalPages
|
||||
const hasPrevPage = page > 1
|
||||
|
||||
return (
|
||||
<div className={[classes.pagination, className].filter(Boolean).join(' ')}>
|
||||
<button
|
||||
className={classes.button}
|
||||
disabled={!hasPrevPage}
|
||||
onClick={() => {
|
||||
onClick(page - 1)
|
||||
}}
|
||||
type="button"
|
||||
>
|
||||
<Chevron className={classes.icon} rotate={90} />
|
||||
</button>
|
||||
<div className={classes.pageRange}>
|
||||
<span className={classes.pageRangeLabel}>
|
||||
Page {page} of {totalPages}
|
||||
</span>
|
||||
</div>
|
||||
<button
|
||||
className={classes.button}
|
||||
disabled={!hasNextPage}
|
||||
onClick={() => {
|
||||
onClick(page + 1)
|
||||
}}
|
||||
type="button"
|
||||
>
|
||||
<Chevron className={classes.icon} rotate={-90} />
|
||||
</button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
.richText {
|
||||
> *:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
> *:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
import React from 'react'
|
||||
|
||||
import classes from './index.module.scss'
|
||||
import serializeLexical from './serializeLexical'
|
||||
import serializeSlate from './serializeSlate'
|
||||
|
||||
const RichText: React.FC<{
|
||||
className?: string
|
||||
content: any
|
||||
renderUploadFilenameOnly?: boolean
|
||||
serializer?: 'lexical' | 'slate'
|
||||
}> = ({ className, content, renderUploadFilenameOnly, serializer = 'slate' }) => {
|
||||
if (!content) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={[classes.richText, className].filter(Boolean).join(' ')}>
|
||||
{serializer === 'slate'
|
||||
? serializeSlate(content, renderUploadFilenameOnly)
|
||||
: serializeLexical(content, renderUploadFilenameOnly)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default RichText
|
||||
@@ -1,90 +0,0 @@
|
||||
import type { SerializedEditorState } from 'lexical'
|
||||
|
||||
import { CMSLink } from '../Link'
|
||||
import { Media } from '../Media'
|
||||
|
||||
const serializer = (
|
||||
content?: SerializedEditorState['root']['children'],
|
||||
renderUploadFilenameOnly?: boolean,
|
||||
): React.ReactNode | React.ReactNode[] =>
|
||||
content?.map((node, i) => {
|
||||
switch (node.type) {
|
||||
case 'h1':
|
||||
return <h1 key={i}>{serializeLexical(node?.children, renderUploadFilenameOnly)}</h1>
|
||||
|
||||
case 'h2':
|
||||
return <h2 key={i}>{serializeLexical(node?.children, renderUploadFilenameOnly)}</h2>
|
||||
|
||||
case 'h3':
|
||||
return <h3 key={i}>{serializeLexical(node?.children, renderUploadFilenameOnly)}</h3>
|
||||
|
||||
case 'h4':
|
||||
return <h4 key={i}>{serializeLexical(node?.children, renderUploadFilenameOnly)}</h4>
|
||||
|
||||
case 'h5':
|
||||
return <h5 key={i}>{serializeLexical(node?.children, renderUploadFilenameOnly)}</h5>
|
||||
|
||||
case 'h6':
|
||||
return <h6 key={i}>{serializeLexical(node?.children, renderUploadFilenameOnly)}</h6>
|
||||
|
||||
case 'quote':
|
||||
return (
|
||||
<blockquote key={i}>
|
||||
{serializeLexical(node?.children, renderUploadFilenameOnly)}
|
||||
</blockquote>
|
||||
)
|
||||
|
||||
case 'ul':
|
||||
return <ul key={i}>{serializeLexical(node?.children, renderUploadFilenameOnly)}</ul>
|
||||
|
||||
case 'ol':
|
||||
return <ol key={i}>{serializeLexical(node.children, renderUploadFilenameOnly)}</ol>
|
||||
|
||||
case 'li':
|
||||
return <li key={i}>{serializeLexical(node.children, renderUploadFilenameOnly)}</li>
|
||||
|
||||
case 'relationship':
|
||||
return (
|
||||
<span key={i}>
|
||||
{node.value && typeof node.value === 'object'
|
||||
? node.value.title || node.value.id
|
||||
: node.value}
|
||||
</span>
|
||||
)
|
||||
|
||||
case 'link':
|
||||
return (
|
||||
<CMSLink
|
||||
key={i}
|
||||
newTab={Boolean(node?.newTab)}
|
||||
reference={node.doc as any}
|
||||
type={node.linkType === 'internal' ? 'reference' : 'custom'}
|
||||
url={node.url}
|
||||
>
|
||||
{serializer(node?.children, renderUploadFilenameOnly)}
|
||||
</CMSLink>
|
||||
)
|
||||
|
||||
case 'upload':
|
||||
if (renderUploadFilenameOnly) {
|
||||
return <span key={i}>{node.value.filename}</span>
|
||||
}
|
||||
|
||||
return <Media key={i} resource={node?.value} />
|
||||
|
||||
case 'paragraph':
|
||||
return <p key={i}>{serializer(node?.children, renderUploadFilenameOnly)}</p>
|
||||
|
||||
case 'text':
|
||||
return <span key={i}>{node.text}</span>
|
||||
}
|
||||
})
|
||||
|
||||
const serializeLexical = (
|
||||
content?: SerializedEditorState,
|
||||
renderUploadFilenameOnly?: boolean,
|
||||
): React.ReactNode | React.ReactNode[] => {
|
||||
return serializer(content?.root?.children, renderUploadFilenameOnly)
|
||||
}
|
||||
|
||||
export default serializeLexical
|
||||
@@ -1,131 +0,0 @@
|
||||
import escapeHTML from 'escape-html'
|
||||
import React, { Fragment } from 'react'
|
||||
import { Text } from 'slate'
|
||||
|
||||
import { CMSLink } from '../Link'
|
||||
import { Media } from '../Media'
|
||||
|
||||
// eslint-disable-next-line no-use-before-define
|
||||
type Children = Leaf[]
|
||||
|
||||
type Leaf = {
|
||||
[key: string]: unknown
|
||||
children?: Children
|
||||
type: string
|
||||
url?: string
|
||||
value?: any
|
||||
}
|
||||
|
||||
const serializeSlate = (
|
||||
children?: Children,
|
||||
renderUploadFilenameOnly?: boolean,
|
||||
): React.ReactNode[] =>
|
||||
children?.map((node, i) => {
|
||||
if (Text.isText(node)) {
|
||||
let text = <span dangerouslySetInnerHTML={{ __html: escapeHTML(node.text) }} />
|
||||
|
||||
if (node.bold) {
|
||||
text = <strong key={i}>{text}</strong>
|
||||
}
|
||||
|
||||
if (node.code) {
|
||||
text = <code key={i}>{text}</code>
|
||||
}
|
||||
|
||||
if (node.italic) {
|
||||
text = <em key={i}>{text}</em>
|
||||
}
|
||||
|
||||
if (node.underline) {
|
||||
text = (
|
||||
<span key={i} style={{ textDecoration: 'underline' }}>
|
||||
{text}
|
||||
</span>
|
||||
)
|
||||
}
|
||||
|
||||
if (node.strikethrough) {
|
||||
text = (
|
||||
<span key={i} style={{ textDecoration: 'line-through' }}>
|
||||
{text}
|
||||
</span>
|
||||
)
|
||||
}
|
||||
|
||||
return <Fragment key={i}>{text}</Fragment>
|
||||
}
|
||||
|
||||
if (!node) {
|
||||
return null
|
||||
}
|
||||
|
||||
switch (node.type) {
|
||||
case 'h1':
|
||||
return <h1 key={i}>{serializeSlate(node?.children, renderUploadFilenameOnly)}</h1>
|
||||
|
||||
case 'h2':
|
||||
return <h2 key={i}>{serializeSlate(node?.children, renderUploadFilenameOnly)}</h2>
|
||||
|
||||
case 'h3':
|
||||
return <h3 key={i}>{serializeSlate(node?.children, renderUploadFilenameOnly)}</h3>
|
||||
|
||||
case 'h4':
|
||||
return <h4 key={i}>{serializeSlate(node?.children, renderUploadFilenameOnly)}</h4>
|
||||
|
||||
case 'h5':
|
||||
return <h5 key={i}>{serializeSlate(node?.children, renderUploadFilenameOnly)}</h5>
|
||||
|
||||
case 'h6':
|
||||
return <h6 key={i}>{serializeSlate(node?.children, renderUploadFilenameOnly)}</h6>
|
||||
|
||||
case 'quote':
|
||||
return (
|
||||
<blockquote key={i}>
|
||||
{serializeSlate(node?.children, renderUploadFilenameOnly)}
|
||||
</blockquote>
|
||||
)
|
||||
|
||||
case 'ul':
|
||||
return <ul key={i}>{serializeSlate(node?.children, renderUploadFilenameOnly)}</ul>
|
||||
|
||||
case 'ol':
|
||||
return <ol key={i}>{serializeSlate(node.children, renderUploadFilenameOnly)}</ol>
|
||||
|
||||
case 'li':
|
||||
return <li key={i}>{serializeSlate(node.children, renderUploadFilenameOnly)}</li>
|
||||
|
||||
case 'relationship':
|
||||
return (
|
||||
<span key={i}>
|
||||
{node.value && typeof node.value === 'object'
|
||||
? node.value.title || node.value.id
|
||||
: node.value}
|
||||
</span>
|
||||
)
|
||||
|
||||
case 'link':
|
||||
return (
|
||||
<CMSLink
|
||||
key={i}
|
||||
newTab={Boolean(node?.newTab)}
|
||||
reference={node.doc as any}
|
||||
type={node.linkType === 'internal' ? 'reference' : 'custom'}
|
||||
url={node.url}
|
||||
>
|
||||
{serializeSlate(node?.children, renderUploadFilenameOnly)}
|
||||
</CMSLink>
|
||||
)
|
||||
|
||||
case 'upload':
|
||||
if (renderUploadFilenameOnly) {
|
||||
return <span key={i}>{node.value.filename}</span>
|
||||
}
|
||||
|
||||
return <Media key={i} resource={node?.value} />
|
||||
|
||||
default:
|
||||
return <p key={i}>{serializeSlate(node?.children, renderUploadFilenameOnly)}</p>
|
||||
}
|
||||
}) || []
|
||||
|
||||
export default serializeSlate
|
||||
@@ -1,15 +0,0 @@
|
||||
.top-large {
|
||||
padding-top: var(--block-padding);
|
||||
}
|
||||
|
||||
.top-medium {
|
||||
padding-top: calc(var(--block-padding) / 2);
|
||||
}
|
||||
|
||||
.bottom-large {
|
||||
padding-bottom: var(--block-padding);
|
||||
}
|
||||
|
||||
.bottom-medium {
|
||||
padding-bottom: calc(var(--block-padding) / 2);
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
import React from 'react'
|
||||
|
||||
import classes from './index.module.scss'
|
||||
|
||||
export type VerticalPaddingOptions = 'large' | 'medium' | 'none'
|
||||
|
||||
type Props = {
|
||||
bottom?: VerticalPaddingOptions
|
||||
children: React.ReactNode
|
||||
className?: string
|
||||
top?: VerticalPaddingOptions
|
||||
}
|
||||
|
||||
export const VerticalPadding: React.FC<Props> = ({
|
||||
bottom = 'medium',
|
||||
children,
|
||||
className,
|
||||
top = 'medium',
|
||||
}) => {
|
||||
return (
|
||||
<div
|
||||
className={[className, classes[`top-${top}`], classes[`bottom-${bottom}`]]
|
||||
.filter(Boolean)
|
||||
.join(' ')}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,117 +0,0 @@
|
||||
@use './queries.scss' as *;
|
||||
@use './colors.scss' as *;
|
||||
@use './type.scss' as *;
|
||||
|
||||
:root {
|
||||
--base: 24px;
|
||||
--font-body: system-ui;
|
||||
--font-mono: 'Roboto Mono', monospace;
|
||||
|
||||
--gutter-h: 180px;
|
||||
--block-padding: 120px;
|
||||
|
||||
--theme-text: var(--color-base-750);
|
||||
|
||||
@include large-break {
|
||||
--gutter-h: 144px;
|
||||
--block-padding: 96px;
|
||||
}
|
||||
|
||||
@include mid-break {
|
||||
--gutter-h: 24px;
|
||||
--block-padding: 60px;
|
||||
}
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
html {
|
||||
@extend %body;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
|
||||
html,
|
||||
body,
|
||||
#app {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: var(--font-body);
|
||||
margin: 0;
|
||||
color: var(--theme-text);
|
||||
}
|
||||
|
||||
::selection {
|
||||
background: var(--color-success-500);
|
||||
color: var(--color-base-800);
|
||||
}
|
||||
|
||||
::-moz-selection {
|
||||
background: var(--color-success-500);
|
||||
color: var(--color-base-800);
|
||||
}
|
||||
|
||||
img {
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
display: block;
|
||||
}
|
||||
|
||||
h1 {
|
||||
@extend %h1;
|
||||
}
|
||||
|
||||
h2 {
|
||||
@extend %h2;
|
||||
}
|
||||
|
||||
h3 {
|
||||
@extend %h3;
|
||||
}
|
||||
|
||||
h4 {
|
||||
@extend %h4;
|
||||
}
|
||||
|
||||
h5 {
|
||||
@extend %h5;
|
||||
}
|
||||
|
||||
h6 {
|
||||
@extend %h6;
|
||||
}
|
||||
|
||||
p {
|
||||
margin: var(--base) 0;
|
||||
|
||||
@include mid-break {
|
||||
margin: calc(var(--base) * 0.75) 0;
|
||||
}
|
||||
}
|
||||
|
||||
ul,
|
||||
ol {
|
||||
padding-left: var(--base);
|
||||
margin: 0 0 var(--base);
|
||||
}
|
||||
|
||||
a {
|
||||
color: currentColor;
|
||||
|
||||
&:focus {
|
||||
opacity: 0.8;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
&:active {
|
||||
opacity: 0.7;
|
||||
outline: none;
|
||||
}
|
||||
}
|
||||
|
||||
svg {
|
||||
vertical-align: middle;
|
||||
}
|
||||
@@ -1,85 +0,0 @@
|
||||
// Keep these in sync with the colors exported in '../cssVariables.js'
|
||||
|
||||
:root {
|
||||
--color-base-0: rgb(255, 255, 255);
|
||||
--color-base-50: rgb(245, 245, 245);
|
||||
--color-base-100: rgb(235, 235, 235);
|
||||
--color-base-150: rgb(221, 221, 221);
|
||||
--color-base-200: rgb(208, 208, 208);
|
||||
--color-base-250: rgb(195, 195, 195);
|
||||
--color-base-300: rgb(181, 181, 181);
|
||||
--color-base-350: rgb(168, 168, 168);
|
||||
--color-base-400: rgb(154, 154, 154);
|
||||
--color-base-450: rgb(141, 141, 141);
|
||||
--color-base-500: rgb(128, 128, 128);
|
||||
--color-base-550: rgb(114, 114, 114);
|
||||
--color-base-600: rgb(101, 101, 101);
|
||||
--color-base-650: rgb(87, 87, 87);
|
||||
--color-base-700: rgb(74, 74, 74);
|
||||
--color-base-750: rgb(60, 60, 60);
|
||||
--color-base-800: rgb(47, 47, 47);
|
||||
--color-base-850: rgb(34, 34, 34);
|
||||
--color-base-900: rgb(20, 20, 20);
|
||||
--color-base-950: rgb(7, 7, 7);
|
||||
--color-base-1000: rgb(0, 0, 0);
|
||||
|
||||
--color-success-50: rgb(247, 255, 251);
|
||||
--color-success-100: rgb(240, 255, 247);
|
||||
--color-success-150: rgb(232, 255, 243);
|
||||
--color-success-200: rgb(224, 255, 239);
|
||||
--color-success-250: rgb(217, 255, 235);
|
||||
--color-success-300: rgb(209, 255, 230);
|
||||
--color-success-350: rgb(201, 255, 226);
|
||||
--color-success-400: rgb(193, 255, 222);
|
||||
--color-success-450: rgb(186, 255, 218);
|
||||
--color-success-500: rgb(178, 255, 214);
|
||||
--color-success-550: rgb(160, 230, 193);
|
||||
--color-success-600: rgb(142, 204, 171);
|
||||
--color-success-650: rgb(125, 179, 150);
|
||||
--color-success-700: rgb(107, 153, 128);
|
||||
--color-success-750: rgb(89, 128, 107);
|
||||
--color-success-800: rgb(71, 102, 86);
|
||||
--color-success-850: rgb(53, 77, 64);
|
||||
--color-success-900: rgb(36, 51, 43);
|
||||
--color-success-950: rgb(18, 25, 21);
|
||||
|
||||
--color-warning-50: rgb(255, 255, 246);
|
||||
--color-warning-100: rgb(255, 255, 237);
|
||||
--color-warning-150: rgb(254, 255, 228);
|
||||
--color-warning-200: rgb(254, 255, 219);
|
||||
--color-warning-250: rgb(254, 255, 210);
|
||||
--color-warning-300: rgb(254, 255, 200);
|
||||
--color-warning-350: rgb(254, 255, 191);
|
||||
--color-warning-400: rgb(253, 255, 182);
|
||||
--color-warning-450: rgb(253, 255, 173);
|
||||
--color-warning-500: rgb(253, 255, 164);
|
||||
--color-warning-550: rgb(228, 230, 148);
|
||||
--color-warning-600: rgb(202, 204, 131);
|
||||
--color-warning-650: rgb(177, 179, 115);
|
||||
--color-warning-700: rgb(152, 153, 98);
|
||||
--color-warning-750: rgb(127, 128, 82);
|
||||
--color-warning-800: rgb(101, 102, 66);
|
||||
--color-warning-850: rgb(76, 77, 49);
|
||||
--color-warning-900: rgb(51, 51, 33);
|
||||
--color-warning-950: rgb(25, 25, 16);
|
||||
|
||||
--color-error-50: rgb(255, 241, 241);
|
||||
--color-error-100: rgb(255, 226, 228);
|
||||
--color-error-150: rgb(255, 212, 214);
|
||||
--color-error-200: rgb(255, 197, 200);
|
||||
--color-error-250: rgb(255, 183, 187);
|
||||
--color-error-300: rgb(255, 169, 173);
|
||||
--color-error-350: rgb(255, 154, 159);
|
||||
--color-error-400: rgb(255, 140, 145);
|
||||
--color-error-450: rgb(255, 125, 132);
|
||||
--color-error-500: rgb(255, 111, 118);
|
||||
--color-error-550: rgb(230, 100, 106);
|
||||
--color-error-600: rgb(204, 89, 94);
|
||||
--color-error-650: rgb(179, 78, 83);
|
||||
--color-error-700: rgb(153, 67, 71);
|
||||
--color-error-750: rgb(128, 56, 59);
|
||||
--color-error-800: rgb(102, 44, 47);
|
||||
--color-error-850: rgb(77, 33, 35);
|
||||
--color-error-900: rgb(51, 22, 24);
|
||||
--color-error-950: rgb(25, 11, 12);
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
@forward './queries.scss';
|
||||
@forward './type.scss';
|
||||
@@ -1,30 +0,0 @@
|
||||
// Keep these in sync with the breakpoints exported in '../cssVariables.js'
|
||||
|
||||
$breakpoint-xs-width: 400px;
|
||||
$breakpoint-s-width: 768px;
|
||||
$breakpoint-m-width: 1024px;
|
||||
$breakpoint-l-width: 1440px;
|
||||
|
||||
@mixin extra-small-break {
|
||||
@media (max-width: #{$breakpoint-xs-width}) {
|
||||
@content;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin small-break {
|
||||
@media (max-width: #{$breakpoint-s-width}) {
|
||||
@content;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin mid-break {
|
||||
@media (max-width: #{$breakpoint-m-width}) {
|
||||
@content;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin large-break {
|
||||
@media (max-width: #{$breakpoint-l-width}) {
|
||||
@content;
|
||||
}
|
||||
}
|
||||
@@ -1,110 +0,0 @@
|
||||
@use 'queries' as *;
|
||||
|
||||
%h1,
|
||||
%h2,
|
||||
%h3,
|
||||
%h4,
|
||||
%h5,
|
||||
%h6 {
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
%h1 {
|
||||
margin: 40px 0;
|
||||
font-size: 64px;
|
||||
line-height: 70px;
|
||||
font-weight: bold;
|
||||
|
||||
@include mid-break {
|
||||
margin: 24px 0;
|
||||
font-size: 42px;
|
||||
line-height: 42px;
|
||||
}
|
||||
}
|
||||
|
||||
%h2 {
|
||||
margin: 28px 0;
|
||||
font-size: 48px;
|
||||
line-height: 54px;
|
||||
font-weight: bold;
|
||||
|
||||
@include mid-break {
|
||||
margin: 22px 0;
|
||||
font-size: 32px;
|
||||
line-height: 40px;
|
||||
}
|
||||
}
|
||||
|
||||
%h3 {
|
||||
margin: 24px 0;
|
||||
font-size: 32px;
|
||||
line-height: 40px;
|
||||
font-weight: bold;
|
||||
|
||||
@include mid-break {
|
||||
margin: 20px 0;
|
||||
font-size: 26px;
|
||||
line-height: 32px;
|
||||
}
|
||||
}
|
||||
|
||||
%h4 {
|
||||
margin: 20px 0;
|
||||
font-size: 26px;
|
||||
line-height: 32px;
|
||||
font-weight: bold;
|
||||
|
||||
@include mid-break {
|
||||
font-size: 22px;
|
||||
line-height: 30px;
|
||||
}
|
||||
}
|
||||
|
||||
%h5 {
|
||||
margin: 20px 0;
|
||||
font-size: 22px;
|
||||
line-height: 30px;
|
||||
font-weight: bold;
|
||||
|
||||
@include mid-break {
|
||||
font-size: 18px;
|
||||
line-height: 24px;
|
||||
}
|
||||
}
|
||||
|
||||
%h6 {
|
||||
margin: 20px 0;
|
||||
font-size: inherit;
|
||||
line-height: inherit;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
%body {
|
||||
font-size: 18px;
|
||||
line-height: 32px;
|
||||
|
||||
@include mid-break {
|
||||
font-size: 15px;
|
||||
line-height: 24px;
|
||||
}
|
||||
}
|
||||
|
||||
%large-body {
|
||||
font-size: 25px;
|
||||
line-height: 32px;
|
||||
|
||||
@include mid-break {
|
||||
font-size: 22px;
|
||||
line-height: 30px;
|
||||
}
|
||||
}
|
||||
|
||||
%label {
|
||||
font-size: 16px;
|
||||
line-height: 24px;
|
||||
text-transform: uppercase;
|
||||
|
||||
@include mid-break {
|
||||
font-size: 13px;
|
||||
}
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
@import '../../_css/queries.scss';
|
||||
|
||||
.hero {
|
||||
padding-top: calc(var(--base) * 2);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
|
||||
@include large-break {
|
||||
padding-top: var(--base);
|
||||
}
|
||||
}
|
||||
|
||||
.media {
|
||||
width: calc(100% + var(--gutter-h));
|
||||
left: calc(var(--gutter-h) / -2);
|
||||
margin-top: calc(var(--base) * 3);
|
||||
position: relative;
|
||||
|
||||
@include mid-break {
|
||||
left: 0;
|
||||
margin-top: var(--base);
|
||||
margin-left: calc(var(--gutter-h) * -1);
|
||||
width: calc(100% + var(--gutter-h) * 2);
|
||||
}
|
||||
}
|
||||
|
||||
.links {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
display: flex;
|
||||
padding-top: var(--base);
|
||||
flex-wrap: wrap;
|
||||
margin: calc(var(--base) * -0.5);
|
||||
|
||||
& > * {
|
||||
margin: calc(var(--base) / 2);
|
||||
}
|
||||
}
|
||||
|
||||
.caption {
|
||||
margin-top: var(--base);
|
||||
color: var(--color-base-500);
|
||||
left: calc(var(--gutter-h) / 2);
|
||||
width: calc(100% - var(--gutter-h));
|
||||
position: relative;
|
||||
|
||||
@include mid-break {
|
||||
left: var(--gutter-h);
|
||||
}
|
||||
}
|
||||
|
||||
.content {
|
||||
position: relative;
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
import React, { Fragment } from 'react'
|
||||
|
||||
import type { Page } from '../../../payload-types'
|
||||
|
||||
import { Gutter } from '../../_components/Gutter'
|
||||
import { Media } from '../../_components/Media'
|
||||
import RichText from '../../_components/RichText'
|
||||
import classes from './index.module.scss'
|
||||
|
||||
export const HighImpactHero: React.FC<Page['hero']> = ({ media, richText }) => {
|
||||
return (
|
||||
<Gutter className={classes.hero}>
|
||||
<div className={classes.content}>
|
||||
<RichText content={richText} />
|
||||
</div>
|
||||
<div className={classes.media}>
|
||||
{typeof media === 'object' && media !== null && (
|
||||
<Fragment>
|
||||
<Media
|
||||
// fill
|
||||
imgClassName={classes.image}
|
||||
priority
|
||||
resource={media}
|
||||
/>
|
||||
{media?.caption && <RichText className={classes.caption} content={media.caption} />}
|
||||
</Fragment>
|
||||
)}
|
||||
</div>
|
||||
</Gutter>
|
||||
)
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
@use '../../_css/type.scss' as *;
|
||||
|
||||
.lowImpactHero {
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
import React from 'react'
|
||||
|
||||
import type { Page } from '../../../payload-types'
|
||||
|
||||
import { Gutter } from '../../_components/Gutter'
|
||||
import RichText from '../../_components/RichText'
|
||||
import { VerticalPadding } from '../../_components/VerticalPadding'
|
||||
import classes from './index.module.scss'
|
||||
|
||||
export const LowImpactHero: React.FC<Page['hero']> = ({ richText }) => {
|
||||
return (
|
||||
<Gutter className={classes.lowImpactHero}>
|
||||
<div className={classes.content}>
|
||||
<VerticalPadding>
|
||||
<RichText className={classes.richText} content={richText} />
|
||||
</VerticalPadding>
|
||||
</div>
|
||||
</Gutter>
|
||||
)
|
||||
}
|
||||
@@ -1,76 +0,0 @@
|
||||
@use '../../_css/common.scss' as *;
|
||||
|
||||
.postHero {
|
||||
display: flex;
|
||||
gap: calc(var(--base) * 2);
|
||||
|
||||
@include mid-break {
|
||||
flex-direction: column;
|
||||
gap: var(--base);
|
||||
}
|
||||
}
|
||||
|
||||
.content {
|
||||
width: 50%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: flex-start;
|
||||
gap: var(--base);
|
||||
|
||||
@include mid-break {
|
||||
width: 100%;
|
||||
gap: calc(var(--base) / 2);
|
||||
}
|
||||
}
|
||||
|
||||
.warning {
|
||||
margin-bottom: calc(var(--base) * 1.5);
|
||||
}
|
||||
|
||||
.meta {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.description {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.media {
|
||||
width: 50%;
|
||||
|
||||
@include mid-break {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.mediaWrapper {
|
||||
text-decoration: none;
|
||||
display: block;
|
||||
position: relative;
|
||||
aspect-ratio: 5 / 4;
|
||||
margin-bottom: calc(var(--base) / 2);
|
||||
width: calc(100% + calc(var(--gutter-h) / 2));
|
||||
|
||||
@include mid-break {
|
||||
margin-left: calc(var(--gutter-h) * -1);
|
||||
width: calc(100% + var(--gutter-h) * 2);
|
||||
}
|
||||
}
|
||||
|
||||
.image {
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.placeholder {
|
||||
background-color: var(--color-base-50);
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.caption {
|
||||
color: var(--color-base-500);
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
import { PAYLOAD_SERVER_URL } from '@/app/_api/serverURL'
|
||||
import Link from 'next/link'
|
||||
import React, { Fragment } from 'react'
|
||||
|
||||
import type { Post } from '../../../payload-types'
|
||||
|
||||
import { Gutter } from '../../_components/Gutter'
|
||||
import { Media } from '../../_components/Media'
|
||||
import RichText from '../../_components/RichText'
|
||||
import { formatDateTime } from '../../_utilities/formatDateTime'
|
||||
import classes from './index.module.scss'
|
||||
|
||||
export const PostHero: React.FC<{
|
||||
post: Post
|
||||
}> = ({ post }) => {
|
||||
const { id, createdAt, meta: { description, image: metaImage } = {} } = post
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<Gutter className={classes.postHero}>
|
||||
<div className={classes.content}>
|
||||
<RichText className={classes.richText} content={post?.hero?.richText} />
|
||||
<p className={classes.meta}>
|
||||
{createdAt && (
|
||||
<Fragment>
|
||||
{'Created on '}
|
||||
{formatDateTime(createdAt)}
|
||||
</Fragment>
|
||||
)}
|
||||
</p>
|
||||
<div>
|
||||
<p className={classes.description}>
|
||||
{`${description ? `${description} ` : ''}To edit this post, `}
|
||||
<Link href={`${PAYLOAD_SERVER_URL}/admin/collections/posts/${id}`}>
|
||||
navigate to the admin dashboard
|
||||
</Link>
|
||||
.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className={classes.media}>
|
||||
<div className={classes.mediaWrapper}>
|
||||
{!metaImage && <div className={classes.placeholder}>No image</div>}
|
||||
{metaImage && typeof metaImage !== 'string' && (
|
||||
<Media fill imgClassName={classes.image} resource={metaImage} />
|
||||
)}
|
||||
</div>
|
||||
{metaImage && typeof metaImage !== 'string' && metaImage?.caption && (
|
||||
<RichText className={classes.caption} content={metaImage.caption} />
|
||||
)}
|
||||
</div>
|
||||
</Gutter>
|
||||
</Fragment>
|
||||
)
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
export const formatDateTime = (timestamp: string): string => {
|
||||
const now = new Date()
|
||||
let date = now
|
||||
if (timestamp) date = new Date(timestamp)
|
||||
const months = date.getMonth()
|
||||
const days = date.getDate()
|
||||
// const hours = date.getHours();
|
||||
// const minutes = date.getMinutes();
|
||||
// const seconds = date.getSeconds();
|
||||
|
||||
const MM = months + 1 < 10 ? `0${months + 1}` : months + 1
|
||||
const DD = days < 10 ? `0${days}` : days
|
||||
const YYYY = date.getFullYear()
|
||||
// const AMPM = hours < 12 ? 'AM' : 'PM';
|
||||
// const HH = hours > 12 ? hours - 12 : hours;
|
||||
// const MinMin = (minutes < 10) ? `0${minutes}` : minutes;
|
||||
// const SS = (seconds < 10) ? `0${seconds}` : seconds;
|
||||
|
||||
return `${MM}/${DD}/${YYYY}`
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
export const toKebabCase = (string: string): string =>
|
||||
string
|
||||
?.replace(/([a-z])([A-Z])/g, '$1-$2')
|
||||
.replace(/\s+/g, '-')
|
||||
.toLowerCase()
|
||||
@@ -1,17 +0,0 @@
|
||||
// Keep these in sync with the CSS variables in the `_css` directory
|
||||
|
||||
module.exports = {
|
||||
breakpoints: {
|
||||
s: 768,
|
||||
m: 1024,
|
||||
l: 1440,
|
||||
},
|
||||
colors: {
|
||||
base0: 'rgb(255, 255, 255)',
|
||||
base100: 'rgb(235, 235, 235)',
|
||||
base500: 'rgb(128, 128, 128)',
|
||||
base850: 'rgb(34, 34, 34)',
|
||||
base1000: 'rgb(0, 0, 0)',
|
||||
error500: 'rgb(255, 111, 118)',
|
||||
},
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
import type { Metadata } from 'next'
|
||||
|
||||
import { Footer } from './_components/Footer'
|
||||
import { Header } from './_components/Header'
|
||||
import './_css/app.scss'
|
||||
|
||||
export const metadata: Metadata = {
|
||||
description: 'Payload Live Preview',
|
||||
title: 'Payload Live Preview',
|
||||
}
|
||||
|
||||
export default function RootLayout({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<html lang="en">
|
||||
<body>
|
||||
<Header />
|
||||
{children}
|
||||
<Footer />
|
||||
</body>
|
||||
</html>
|
||||
)
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
'use client'
|
||||
|
||||
import React from 'react'
|
||||
|
||||
import { Gutter } from './_components/Gutter'
|
||||
import { VerticalPadding } from './_components/VerticalPadding'
|
||||
|
||||
export default function NotFound() {
|
||||
return (
|
||||
<main>
|
||||
<VerticalPadding bottom="medium" top="none">
|
||||
<Gutter>
|
||||
<h1>404</h1>
|
||||
<p>This page could not be found.</p>
|
||||
</Gutter>
|
||||
</VerticalPadding>
|
||||
</main>
|
||||
)
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
import PageTemplate from './(pages)/[slug]/page'
|
||||
|
||||
export default PageTemplate
|
||||
@@ -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
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
462
test/live-preview/next-app/pnpm-lock.yaml
generated
462
test/live-preview/next-app/pnpm-lock.yaml
generated
@@ -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
|
||||
@@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -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.',
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user