fix(next): dynamic params for custom collection and global views
This commit is contained in:
@@ -17,7 +17,11 @@ import { Settings } from './Settings/index.js'
|
||||
|
||||
export { generateAccountMetadata } from './meta.js'
|
||||
|
||||
export const Account: React.FC<AdminViewProps> = async ({ initPageResult, searchParams }) => {
|
||||
export const Account: React.FC<AdminViewProps> = async ({
|
||||
initPageResult,
|
||||
params,
|
||||
searchParams,
|
||||
}) => {
|
||||
const {
|
||||
locale,
|
||||
permissions,
|
||||
@@ -87,6 +91,7 @@ export const Account: React.FC<AdminViewProps> = async ({ initPageResult, search
|
||||
|
||||
const viewComponentProps: ServerSideEditViewProps = {
|
||||
initPageResult,
|
||||
params,
|
||||
routeSegments: [],
|
||||
searchParams,
|
||||
}
|
||||
|
||||
@@ -1,16 +1,29 @@
|
||||
import type { EditViewComponent } from 'payload/config'
|
||||
import type { SanitizedCollectionConfig, SanitizedGlobalConfig } from 'payload/types'
|
||||
|
||||
export const getCustomViewByPath = (
|
||||
import { isPathMatchingRoute } from '../Root/isPathMatchingRoute.js'
|
||||
|
||||
export const getCustomViewByRoute = ({
|
||||
baseRoute,
|
||||
currentRoute,
|
||||
views,
|
||||
}: {
|
||||
baseRoute: string
|
||||
currentRoute: string
|
||||
views:
|
||||
| SanitizedCollectionConfig['admin']['components']['views']
|
||||
| SanitizedGlobalConfig['admin']['components']['views'],
|
||||
path: string,
|
||||
): EditViewComponent => {
|
||||
| SanitizedGlobalConfig['admin']['components']['views']
|
||||
}): EditViewComponent => {
|
||||
if (typeof views?.Edit === 'object' && typeof views?.Edit !== 'function') {
|
||||
const foundViewConfig = Object.entries(views.Edit).find(([, view]) => {
|
||||
if (typeof view === 'object' && typeof view !== 'function' && 'path' in view) {
|
||||
return view.path === path
|
||||
const viewPath = `${baseRoute}${view.path}`
|
||||
|
||||
return isPathMatchingRoute({
|
||||
currentRoute,
|
||||
exact: true,
|
||||
path: viewPath,
|
||||
})
|
||||
}
|
||||
return false
|
||||
})?.[1]
|
||||
@@ -8,15 +8,17 @@ import type {
|
||||
} from 'payload/types'
|
||||
|
||||
import { isEntityHidden } from 'payload/utilities'
|
||||
import React from 'react'
|
||||
|
||||
import { APIView as DefaultAPIView } from '../API/index.js'
|
||||
import { EditView as DefaultEditView } from '../Edit/index.js'
|
||||
import { LivePreviewView as DefaultLivePreviewView } from '../LivePreview/index.js'
|
||||
import { NotFoundClient } from '../NotFound/index.client.js'
|
||||
import { Unauthorized } from '../Unauthorized/index.js'
|
||||
import { VersionView as DefaultVersionView } from '../Version/index.js'
|
||||
import { VersionsView as DefaultVersionsView } from '../Versions/index.js'
|
||||
import { getCustomViewByKey } from './getCustomViewByKey.js'
|
||||
import { getCustomViewByPath } from './getCustomViewByPath.js'
|
||||
import { getCustomViewByRoute } from './getCustomViewByRoute.js'
|
||||
|
||||
export const getViewsFromConfig = ({
|
||||
collectionConfig,
|
||||
@@ -46,6 +48,10 @@ export const getViewsFromConfig = ({
|
||||
let CustomView: EditViewComponent = null
|
||||
let ErrorView: AdminViewComponent = null
|
||||
|
||||
const {
|
||||
routes: { admin: adminRoute },
|
||||
} = config
|
||||
|
||||
const views =
|
||||
(collectionConfig && collectionConfig?.admin?.components?.views) ||
|
||||
(globalConfig && globalConfig?.admin?.components?.views)
|
||||
@@ -65,8 +71,7 @@ export const getViewsFromConfig = ({
|
||||
}
|
||||
|
||||
if (!EditOverride) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const [collectionEntity, collectionSlug, createOrID, nestedViewSlug, segmentFive] =
|
||||
const [collectionEntity, collectionSlug, segment3, segment4, segment5, ...remainingSegments] =
|
||||
routeSegments
|
||||
|
||||
const {
|
||||
@@ -78,74 +83,110 @@ export const getViewsFromConfig = ({
|
||||
}
|
||||
|
||||
// `../:id`, or `../create`
|
||||
if (routeSegments.length === 3) {
|
||||
switch (createOrID) {
|
||||
case 'create': {
|
||||
if ('create' in docPermissions && docPermissions?.create?.permission) {
|
||||
CustomView = getCustomViewByKey(views, 'Default')
|
||||
DefaultView = DefaultEditView
|
||||
} else {
|
||||
ErrorView = Unauthorized
|
||||
switch (routeSegments.length) {
|
||||
case 3: {
|
||||
switch (segment3) {
|
||||
case 'create': {
|
||||
if ('create' in docPermissions && docPermissions?.create?.permission) {
|
||||
CustomView = getCustomViewByKey(views, 'Default')
|
||||
DefaultView = DefaultEditView
|
||||
} else {
|
||||
ErrorView = Unauthorized
|
||||
}
|
||||
break
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
default: {
|
||||
if (docPermissions?.read?.permission) {
|
||||
CustomView = getCustomViewByKey(views, 'Default')
|
||||
DefaultView = DefaultEditView
|
||||
} else {
|
||||
ErrorView = Unauthorized
|
||||
default: {
|
||||
if (docPermissions?.read?.permission) {
|
||||
CustomView = getCustomViewByKey(views, 'Default')
|
||||
DefaultView = DefaultEditView
|
||||
} else {
|
||||
ErrorView = Unauthorized
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// `../:id/api`, `../:id/preview`, `../:id/versions`, etc
|
||||
if (routeSegments?.length === 4) {
|
||||
switch (nestedViewSlug) {
|
||||
case 'api': {
|
||||
if (collectionConfig?.admin?.hideAPIURL !== true) {
|
||||
CustomView = getCustomViewByKey(views, 'API')
|
||||
DefaultView = DefaultAPIView
|
||||
// `../:id/api`, `../:id/preview`, `../:id/versions`, etc
|
||||
case 4: {
|
||||
switch (segment4) {
|
||||
case 'api': {
|
||||
if (collectionConfig?.admin?.hideAPIURL !== true) {
|
||||
CustomView = getCustomViewByKey(views, 'API')
|
||||
DefaultView = DefaultAPIView
|
||||
}
|
||||
break
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
case 'preview': {
|
||||
if (livePreviewEnabled) {
|
||||
DefaultView = DefaultLivePreviewView
|
||||
case 'preview': {
|
||||
if (livePreviewEnabled) {
|
||||
DefaultView = DefaultLivePreviewView
|
||||
}
|
||||
break
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
case 'versions': {
|
||||
case 'versions': {
|
||||
if (docPermissions?.readVersions?.permission) {
|
||||
CustomView = getCustomViewByKey(views, 'Versions')
|
||||
DefaultView = DefaultVersionsView
|
||||
} else {
|
||||
ErrorView = Unauthorized
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
default: {
|
||||
const baseRoute = [adminRoute, 'collections', collectionSlug, segment3]
|
||||
.filter(Boolean)
|
||||
.join('/')
|
||||
|
||||
const currentRoute = [baseRoute, segment4, segment5, ...remainingSegments]
|
||||
.filter(Boolean)
|
||||
.join('/')
|
||||
|
||||
CustomView = getCustomViewByRoute({
|
||||
baseRoute,
|
||||
currentRoute,
|
||||
views,
|
||||
})
|
||||
|
||||
if (!CustomView) ErrorView = () => <NotFoundClient />
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
// `../:id/versions/:version`, etc
|
||||
default: {
|
||||
if (segment4 === 'versions') {
|
||||
if (docPermissions?.readVersions?.permission) {
|
||||
CustomView = getCustomViewByKey(views, 'Versions')
|
||||
DefaultView = DefaultVersionsView
|
||||
CustomView = getCustomViewByKey(views, 'Version')
|
||||
DefaultView = DefaultVersionView
|
||||
} else {
|
||||
ErrorView = Unauthorized
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
default: {
|
||||
const path = `/${nestedViewSlug}`
|
||||
CustomView = getCustomViewByPath(views, path)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// `../:id/versions/:version`, etc
|
||||
if (routeSegments.length === 5) {
|
||||
if (nestedViewSlug === 'versions') {
|
||||
if (docPermissions?.readVersions?.permission) {
|
||||
CustomView = getCustomViewByKey(views, 'Version')
|
||||
DefaultView = DefaultVersionView
|
||||
} else {
|
||||
ErrorView = Unauthorized
|
||||
const baseRoute = [adminRoute, collectionEntity, collectionSlug, segment3]
|
||||
.filter(Boolean)
|
||||
.join('/')
|
||||
|
||||
const currentRoute = [baseRoute, segment4, segment5, ...remainingSegments]
|
||||
.filter(Boolean)
|
||||
.join('/')
|
||||
|
||||
CustomView = getCustomViewByRoute({
|
||||
baseRoute,
|
||||
currentRoute,
|
||||
views,
|
||||
})
|
||||
|
||||
if (!CustomView) ErrorView = () => <NotFoundClient />
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -161,7 +202,7 @@ export const getViewsFromConfig = ({
|
||||
|
||||
if (!EditOverride) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const [globalEntity, globalSlug, nestedViewSlug] = routeSegments
|
||||
const [globalEntity, globalSlug, segment3, ...remainingSegments] = routeSegments
|
||||
|
||||
const {
|
||||
admin: { hidden },
|
||||
@@ -171,64 +212,83 @@ export const getViewsFromConfig = ({
|
||||
return null
|
||||
}
|
||||
|
||||
if (routeSegments?.length === 2) {
|
||||
if (docPermissions?.read?.permission) {
|
||||
CustomView = getCustomViewByKey(views, 'Default')
|
||||
DefaultView = DefaultEditView
|
||||
} else {
|
||||
ErrorView = Unauthorized
|
||||
}
|
||||
}
|
||||
|
||||
if (routeSegments?.length === 3) {
|
||||
// `../:slug/api`, `../:slug/preview`, `../:slug/versions`, etc
|
||||
switch (nestedViewSlug) {
|
||||
case 'api': {
|
||||
if (globalConfig?.admin?.hideAPIURL !== true) {
|
||||
CustomView = getCustomViewByKey(views, 'API')
|
||||
DefaultView = DefaultAPIView
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
case 'preview': {
|
||||
if (livePreviewEnabled) {
|
||||
DefaultView = DefaultLivePreviewView
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
case 'versions': {
|
||||
if (docPermissions?.readVersions?.permission) {
|
||||
CustomView = getCustomViewByKey(views, 'Versions')
|
||||
DefaultView = DefaultVersionsView
|
||||
} else {
|
||||
ErrorView = Unauthorized
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
default: {
|
||||
if (docPermissions?.read?.permission) {
|
||||
CustomView = getCustomViewByKey(views, 'Default')
|
||||
DefaultView = DefaultEditView
|
||||
} else {
|
||||
ErrorView = Unauthorized
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (routeSegments?.length === 4) {
|
||||
// `../:slug/versions/:version`, etc
|
||||
if (nestedViewSlug === 'versions') {
|
||||
if (docPermissions?.readVersions?.permission) {
|
||||
CustomView = getCustomViewByKey(views, 'Version')
|
||||
DefaultView = DefaultVersionView
|
||||
switch (routeSegments.length) {
|
||||
case 2: {
|
||||
if (docPermissions?.read?.permission) {
|
||||
CustomView = getCustomViewByKey(views, 'Default')
|
||||
DefaultView = DefaultEditView
|
||||
} else {
|
||||
ErrorView = Unauthorized
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
case 3: {
|
||||
// `../:slug/api`, `../:slug/preview`, `../:slug/versions`, etc
|
||||
switch (segment3) {
|
||||
case 'api': {
|
||||
if (globalConfig?.admin?.hideAPIURL !== true) {
|
||||
CustomView = getCustomViewByKey(views, 'API')
|
||||
DefaultView = DefaultAPIView
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
case 'preview': {
|
||||
if (livePreviewEnabled) {
|
||||
DefaultView = DefaultLivePreviewView
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
case 'versions': {
|
||||
if (docPermissions?.readVersions?.permission) {
|
||||
CustomView = getCustomViewByKey(views, 'Versions')
|
||||
DefaultView = DefaultVersionsView
|
||||
} else {
|
||||
ErrorView = Unauthorized
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
default: {
|
||||
if (docPermissions?.read?.permission) {
|
||||
CustomView = getCustomViewByKey(views, 'Default')
|
||||
DefaultView = DefaultEditView
|
||||
} else {
|
||||
ErrorView = Unauthorized
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
default: {
|
||||
// `../:slug/versions/:version`, etc
|
||||
if (segment3 === 'versions') {
|
||||
if (docPermissions?.readVersions?.permission) {
|
||||
CustomView = getCustomViewByKey(views, 'Version')
|
||||
DefaultView = DefaultVersionView
|
||||
} else {
|
||||
ErrorView = Unauthorized
|
||||
}
|
||||
} else {
|
||||
const baseRoute = [adminRoute, 'globals', globalSlug].filter(Boolean).join('/')
|
||||
|
||||
const currentRoute = [baseRoute, segment3, ...remainingSegments]
|
||||
.filter(Boolean)
|
||||
.join('/')
|
||||
|
||||
CustomView = getCustomViewByRoute({
|
||||
baseRoute,
|
||||
currentRoute,
|
||||
views,
|
||||
})
|
||||
|
||||
if (!CustomView) ErrorView = () => <NotFoundClient />
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,23 +1,14 @@
|
||||
import type { EditViewComponent } from 'payload/config'
|
||||
import type {
|
||||
AdminViewComponent,
|
||||
DocumentPreferences,
|
||||
Document as DocumentType,
|
||||
Field,
|
||||
ServerSideEditViewProps,
|
||||
} from 'payload/types'
|
||||
import type { AdminViewComponent, ServerSideEditViewProps } from 'payload/types'
|
||||
import type { DocumentPermissions } from 'payload/types'
|
||||
import type { AdminViewProps } from 'payload/types'
|
||||
|
||||
import { DocumentHeader } from '@payloadcms/ui/elements/DocumentHeader'
|
||||
import { HydrateClientUser } from '@payloadcms/ui/elements/HydrateClientUser'
|
||||
import { RenderCustomComponent } from '@payloadcms/ui/elements/RenderCustomComponent'
|
||||
import { buildStateFromSchema } from '@payloadcms/ui/forms/buildStateFromSchema'
|
||||
import { DocumentInfoProvider } from '@payloadcms/ui/providers/DocumentInfo'
|
||||
import { EditDepthProvider } from '@payloadcms/ui/providers/EditDepth'
|
||||
import { FormQueryParamsProvider } from '@payloadcms/ui/providers/FormQueryParams'
|
||||
import { formatDocTitle } from '@payloadcms/ui/utilities/formatDocTitle'
|
||||
import { formatFields } from '@payloadcms/ui/utilities/formatFields'
|
||||
import { docAccessOperation } from 'payload/operations'
|
||||
import React from 'react'
|
||||
|
||||
@@ -222,6 +213,7 @@ export const Document: React.FC<AdminViewProps> = async ({
|
||||
|
||||
const viewComponentProps: ServerSideEditViewProps = {
|
||||
initPageResult,
|
||||
params,
|
||||
routeSegments: segments,
|
||||
searchParams,
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { AdminViewComponent, SanitizedConfig } from 'payload/types'
|
||||
|
||||
import { pathToRegexp } from 'path-to-regexp'
|
||||
import { isPathMatchingRoute } from './isPathMatchingRoute.js'
|
||||
|
||||
export const getCustomViewByRoute = ({
|
||||
config,
|
||||
@@ -23,22 +23,13 @@ export const getCustomViewByRoute = ({
|
||||
typeof views === 'object' &&
|
||||
Object.entries(views).find(([, view]) => {
|
||||
if (typeof view === 'object') {
|
||||
const { exact, path: viewPath, sensitive, strict } = view
|
||||
|
||||
const keys = []
|
||||
|
||||
// run the view path through `pathToRegexp` to resolve any dynamic segments
|
||||
// i.e. `/admin/custom-view/:id` -> `/admin/custom-view/123`
|
||||
const regex = pathToRegexp(viewPath, keys, {
|
||||
sensitive,
|
||||
strict,
|
||||
return isPathMatchingRoute({
|
||||
currentRoute,
|
||||
exact: view.exact,
|
||||
path: view.path,
|
||||
sensitive: view.sensitive,
|
||||
strict: view.strict,
|
||||
})
|
||||
|
||||
const match = regex.exec(currentRoute)
|
||||
const viewRoute = match?.[0] || viewPath
|
||||
|
||||
if (exact) return currentRoute === viewRoute
|
||||
if (!exact) return viewRoute.startsWith(currentRoute)
|
||||
}
|
||||
})?.[1]
|
||||
|
||||
|
||||
30
packages/next/src/views/Root/isPathMatchingRoute.ts
Normal file
30
packages/next/src/views/Root/isPathMatchingRoute.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
import { pathToRegexp } from 'path-to-regexp'
|
||||
|
||||
export const isPathMatchingRoute = ({
|
||||
currentRoute,
|
||||
exact,
|
||||
path: viewPath,
|
||||
sensitive,
|
||||
strict,
|
||||
}: {
|
||||
currentRoute: string
|
||||
exact?: boolean
|
||||
path?: string
|
||||
sensitive?: boolean
|
||||
strict?: boolean
|
||||
}) => {
|
||||
const keys = []
|
||||
|
||||
// run the view path through `pathToRegexp` to resolve any dynamic segments
|
||||
// i.e. `/admin/custom-view/:id` -> `/admin/custom-view/123`
|
||||
const regex = pathToRegexp(viewPath, keys, {
|
||||
sensitive,
|
||||
strict,
|
||||
})
|
||||
|
||||
const match = regex.exec(currentRoute)
|
||||
const viewRoute = match?.[0] || viewPath
|
||||
|
||||
if (exact) return currentRoute === viewRoute
|
||||
if (!exact) return viewRoute.startsWith(currentRoute)
|
||||
}
|
||||
@@ -42,6 +42,7 @@ export type InitPageResult = {
|
||||
|
||||
export type ServerSideEditViewProps = {
|
||||
initPageResult: InitPageResult
|
||||
params: { [key: string]: string | string[] | undefined }
|
||||
routeSegments: string[]
|
||||
searchParams: { [key: string]: string | string[] | undefined }
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ import type {
|
||||
SanitizedGlobalConfig,
|
||||
} from 'payload/types'
|
||||
|
||||
import { isPlainObject } from 'packages/payload/src/utilities/isPlainObject.js'
|
||||
import { isPlainObject } from 'payload/utilities'
|
||||
import React from 'react'
|
||||
|
||||
import { ShouldRenderTabs } from './ShouldRenderTabs.js'
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
import type { CollectionConfig } from 'payload/types'
|
||||
|
||||
import { CustomTabComponent } from '../components/CustomTabComponent/index.js'
|
||||
import { CustomTabView } from '../components/views/CustomTab/index.js'
|
||||
import { CustomTabView2 } from '../components/views/CustomTab2/index.js'
|
||||
import { CustomTabComponentView } from '../components/views/CustomTabComponent/index.js'
|
||||
import { CustomTabLabelView } from '../components/views/CustomTabLabel/index.js'
|
||||
import { CustomNestedTabView } from '../components/views/CustomTabNested/index.js'
|
||||
import { CustomTabWithParamView } from '../components/views/CustomTabWithParam/index.js'
|
||||
import { CustomVersionsView } from '../components/views/CustomVersions/index.js'
|
||||
import {
|
||||
customCollectionParamViewPath,
|
||||
customCollectionParamViewPathBase,
|
||||
customEditLabel,
|
||||
customNestedTabViewPath,
|
||||
customTabLabel,
|
||||
@@ -26,7 +29,7 @@ export const CustomViews2: CollectionConfig = {
|
||||
},
|
||||
},
|
||||
MyCustomView: {
|
||||
Component: CustomTabView,
|
||||
Component: CustomTabLabelView,
|
||||
Tab: {
|
||||
href: '/custom-tab-view',
|
||||
label: customTabLabel,
|
||||
@@ -34,15 +37,27 @@ export const CustomViews2: CollectionConfig = {
|
||||
path: '/custom-tab-view',
|
||||
},
|
||||
MyCustomViewWithCustomTab: {
|
||||
Component: CustomTabView2,
|
||||
Component: CustomTabComponentView,
|
||||
Tab: CustomTabComponent,
|
||||
path: customTabViewPath,
|
||||
},
|
||||
MyCustomViewWithNestedPath: {
|
||||
Component: CustomNestedTabView,
|
||||
path: customNestedTabViewPath,
|
||||
tab: {
|
||||
label: 'Custom Nested Tab View',
|
||||
href: customNestedTabViewPath,
|
||||
},
|
||||
},
|
||||
Versions: CustomVersionsView,
|
||||
CustomViewWithParam: {
|
||||
Component: CustomTabWithParamView,
|
||||
path: customCollectionParamViewPath,
|
||||
Tab: {
|
||||
label: 'Custom Param View',
|
||||
href: `${customCollectionParamViewPathBase}/123`,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -1,16 +1,21 @@
|
||||
'use client'
|
||||
|
||||
import LinkImport from 'next/link'
|
||||
import { usePathname } from 'next/navigation'
|
||||
import { useConfig } from '@payloadcms/ui/providers/Config'
|
||||
import LinkImport from 'next/link.js'
|
||||
import { useParams } from 'next/navigation'
|
||||
import React from 'react'
|
||||
|
||||
const Link = (LinkImport.default || LinkImport) as unknown as typeof LinkImport.default
|
||||
|
||||
export const CustomTabComponentClient: React.FC<{
|
||||
path: string
|
||||
}> = (props) => {
|
||||
const { path } = props
|
||||
const pathname = usePathname()
|
||||
}> = ({ path }) => {
|
||||
const {
|
||||
routes: { admin: adminRoute },
|
||||
} = useConfig()
|
||||
const params = useParams()
|
||||
|
||||
return <Link href={`${pathname}${path}`}>Custom Tab Component</Link>
|
||||
const baseRoute = (params.segments.slice(0, 3) as string[]).join('/')
|
||||
|
||||
return <Link href={`${adminRoute}/${baseRoute}${path}`}>Custom Tab Component</Link>
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// As this is the demo folder, we import Payload SCSS functions relatively.
|
||||
@import '../../../../../packages/ui/src/exports/scss.scss';
|
||||
@import '../../../../../packages/ui/src/scss/styles.scss';
|
||||
|
||||
// In your own projects, you'd import as follows:
|
||||
// @import '~payload/scss';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// As this is the demo folder, we import Payload SCSS functions relatively.
|
||||
@import '../../../../../packages/ui/src/exports/scss.scss';
|
||||
@import '../../../../../packages/ui/src/scss/styles.scss';
|
||||
|
||||
// In your own projects, you'd import as follows:
|
||||
// @import '~payload/scss';
|
||||
|
||||
@@ -4,9 +4,9 @@ import React, { Fragment } from 'react'
|
||||
|
||||
import type { ServerSideEditViewProps } from '../../../../../packages/payload/types.js'
|
||||
|
||||
import { customTabViewTitle } from '../../../shared.js'
|
||||
import { customTabViewComponentTitle } from '../../../shared.js'
|
||||
|
||||
export const CustomTabView2: React.FC<ServerSideEditViewProps> = ({ initPageResult }) => {
|
||||
export const CustomTabComponentView: React.FC<ServerSideEditViewProps> = ({ initPageResult }) => {
|
||||
if (!initPageResult) {
|
||||
notFound()
|
||||
}
|
||||
@@ -27,7 +27,7 @@ export const CustomTabView2: React.FC<ServerSideEditViewProps> = ({ initPageResu
|
||||
paddingRight: 'var(--gutter-h)',
|
||||
}}
|
||||
>
|
||||
<h1 id="custom-view-title">{customTabViewTitle}</h1>
|
||||
<h1 id="custom-view-title">{customTabViewComponentTitle}</h1>
|
||||
<p>This custom view was added through the Payload config:</p>
|
||||
<ul>
|
||||
<li>
|
||||
@@ -4,9 +4,9 @@ import React, { Fragment } from 'react'
|
||||
|
||||
import type { ServerSideEditViewProps } from '../../../../../packages/payload/types.js'
|
||||
|
||||
import { customTabViewTitle } from '../../../shared.js'
|
||||
import { customTabLabelViewTitle } from '../../../shared.js'
|
||||
|
||||
export const CustomTabView: React.FC<ServerSideEditViewProps> = ({ initPageResult }) => {
|
||||
export const CustomTabLabelView: React.FC<ServerSideEditViewProps> = ({ initPageResult }) => {
|
||||
if (!initPageResult) {
|
||||
notFound()
|
||||
}
|
||||
@@ -27,7 +27,7 @@ export const CustomTabView: React.FC<ServerSideEditViewProps> = ({ initPageResul
|
||||
paddingRight: 'var(--gutter-h)',
|
||||
}}
|
||||
>
|
||||
<h1 id="custom-view-title">{customTabViewTitle}</h1>
|
||||
<h1 id="custom-view-title">{customTabLabelViewTitle}</h1>
|
||||
<p>This custom view was added through the Payload config:</p>
|
||||
<ul>
|
||||
<li>
|
||||
24
test/admin/components/views/CustomTabWithParam/index.tsx
Normal file
24
test/admin/components/views/CustomTabWithParam/index.tsx
Normal file
@@ -0,0 +1,24 @@
|
||||
import type { AdminViewProps } from 'payload/types.js'
|
||||
|
||||
import React from 'react'
|
||||
|
||||
import { customParamViewTitle } from '../../../shared.js'
|
||||
|
||||
export const CustomTabWithParamView: React.FC<AdminViewProps> = ({ params }) => {
|
||||
const paramValue = params?.segments?.[4]
|
||||
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
marginTop: 'calc(var(--base) * 2)',
|
||||
paddingLeft: 'var(--gutter-h)',
|
||||
paddingRight: 'var(--gutter-h)',
|
||||
}}
|
||||
>
|
||||
<h1 id="custom-view-title">{customParamViewTitle}</h1>
|
||||
<p>
|
||||
This custom collection view is using a dynamic URL parameter `slug: {paramValue || 'None'}`
|
||||
</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -2,7 +2,8 @@ import type { GlobalConfig } from 'payload/types'
|
||||
|
||||
import { CustomTabComponent } from '../components/CustomTabComponent/index.js'
|
||||
import { CustomDefaultEditView } from '../components/views/CustomEditDefault/index.js'
|
||||
import { CustomTabView } from '../components/views/CustomTab/index.js'
|
||||
import { CustomTabComponentView } from '../components/views/CustomTabComponent/index.js'
|
||||
import { CustomTabLabelView } from '../components/views/CustomTabLabel/index.js'
|
||||
import { CustomVersionsView } from '../components/views/CustomVersions/index.js'
|
||||
import { customGlobalViews2GlobalSlug } from '../slugs.js'
|
||||
|
||||
@@ -14,7 +15,7 @@ export const CustomGlobalViews2: GlobalConfig = {
|
||||
Edit: {
|
||||
Default: CustomDefaultEditView,
|
||||
MyCustomView: {
|
||||
Component: CustomTabView,
|
||||
Component: CustomTabLabelView,
|
||||
Tab: {
|
||||
href: '/custom-tab-view',
|
||||
label: 'Custom',
|
||||
@@ -22,7 +23,7 @@ export const CustomGlobalViews2: GlobalConfig = {
|
||||
path: '/custom-tab-view',
|
||||
},
|
||||
MyCustomViewWithCustomTab: {
|
||||
Component: CustomTabView,
|
||||
Component: CustomTabComponentView,
|
||||
Tab: CustomTabComponent,
|
||||
path: '/custom-tab-component',
|
||||
},
|
||||
|
||||
@@ -22,8 +22,14 @@ export const customTabLabel = 'Custom Tab Label'
|
||||
|
||||
export const customTabViewPath = '/custom-tab-component'
|
||||
|
||||
export const customTabViewTitle = 'Custom View With Tab Component'
|
||||
export const customTabLabelViewTitle = 'Custom Tab Label View'
|
||||
|
||||
export const customTabViewComponentTitle = 'Custom View With Tab Component'
|
||||
|
||||
export const customNestedTabViewPath = `${customTabViewPath}/nested-view`
|
||||
|
||||
export const customNestedTabViewTitle = 'Custom Nested Tab View'
|
||||
|
||||
export const customCollectionParamViewPathBase = '/custom-param'
|
||||
|
||||
export const customCollectionParamViewPath = `${customCollectionParamViewPathBase}/:slug`
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"@payload-config": ["./test/_community/config.ts"],
|
||||
"@payload-config": ["./test/admin/config.ts"],
|
||||
"@payloadcms/live-preview": ["./packages/live-preview/src"],
|
||||
"@payloadcms/live-preview-react": ["./packages/live-preview-react/src/index.ts"],
|
||||
"@payloadcms/ui/assets": ["./packages/ui/src/assets/index.ts"],
|
||||
|
||||
Reference in New Issue
Block a user