perf: optimize getEntityConfig lookups (#10665)
Replaces array-based lookups in `getEntityConfig` with a map, reducing time complexity from O(n) to O(1).
This commit is contained in:
@@ -831,6 +831,22 @@ const MyComponent: React.FC = () => {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
If you need to retrieve a specific collection or global config by its slug, `getEntityConfig` is the most efficient way to do so:
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
'use client'
|
||||||
|
import { useConfig } from '@payloadcms/ui'
|
||||||
|
|
||||||
|
const MyComponent: React.FC = () => {
|
||||||
|
// highlight-start
|
||||||
|
const { getEntityConfig } = useConfig()
|
||||||
|
const mediaConfig = getEntityConfig({ collectionSlug: 'media'})
|
||||||
|
// highlight-end
|
||||||
|
|
||||||
|
return <span>The media collection has {mediaConfig.fields.length} fields.</span>
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
## useEditDepth
|
## useEditDepth
|
||||||
|
|
||||||
Sends back how many editing levels "deep" the current component is. Edit depth is relevant while adding new documents / editing documents in modal windows and other cases.
|
Sends back how many editing levels "deep" the current component is. Edit depth is relevant while adding new documents / editing documents in modal windows and other cases.
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ export function getRouteInfo({
|
|||||||
let idType = defaultIDType
|
let idType = defaultIDType
|
||||||
|
|
||||||
if (collectionSlug) {
|
if (collectionSlug) {
|
||||||
collectionConfig = config.collections.find((collection) => collection.slug === collectionSlug)
|
collectionConfig = payload.collections?.[collectionSlug]?.config
|
||||||
}
|
}
|
||||||
|
|
||||||
if (globalSlug) {
|
if (globalSlug) {
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
'use client'
|
'use client'
|
||||||
|
|
||||||
import type { ClientCollectionConfig, ClientGlobalConfig } from 'payload'
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
CheckboxField,
|
CheckboxField,
|
||||||
CopyToClipboard,
|
CopyToClipboard,
|
||||||
@@ -41,8 +39,8 @@ export const APIViewClient: React.FC = () => {
|
|||||||
getEntityConfig,
|
getEntityConfig,
|
||||||
} = useConfig()
|
} = useConfig()
|
||||||
|
|
||||||
const collectionConfig = getEntityConfig({ collectionSlug }) as ClientCollectionConfig
|
const collectionConfig = getEntityConfig({ collectionSlug })
|
||||||
const globalConfig = getEntityConfig({ globalSlug }) as ClientGlobalConfig
|
const globalConfig = getEntityConfig({ globalSlug })
|
||||||
|
|
||||||
const localeOptions =
|
const localeOptions =
|
||||||
localization &&
|
localization &&
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ export const Account: React.FC<AdminViewProps> = async ({
|
|||||||
serverURL,
|
serverURL,
|
||||||
} = config
|
} = config
|
||||||
|
|
||||||
const collectionConfig = config.collections.find((collection) => collection.slug === userSlug)
|
const collectionConfig = payload?.collections?.[userSlug]?.config
|
||||||
|
|
||||||
if (collectionConfig && user?.id) {
|
if (collectionConfig && user?.id) {
|
||||||
// Fetch the data required for the view
|
// Fetch the data required for the view
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
'use client'
|
'use client'
|
||||||
import type { FormProps, UserWithToken } from '@payloadcms/ui'
|
import type { FormProps, UserWithToken } from '@payloadcms/ui'
|
||||||
import type {
|
import type {
|
||||||
ClientCollectionConfig,
|
|
||||||
DocumentPreferences,
|
DocumentPreferences,
|
||||||
FormState,
|
FormState,
|
||||||
LoginWithUsernameOptions,
|
LoginWithUsernameOptions,
|
||||||
@@ -45,7 +44,7 @@ export const CreateFirstUserClient: React.FC<{
|
|||||||
|
|
||||||
const abortOnChangeRef = React.useRef<AbortController>(null)
|
const abortOnChangeRef = React.useRef<AbortController>(null)
|
||||||
|
|
||||||
const collectionConfig = getEntityConfig({ collectionSlug: userSlug }) as ClientCollectionConfig
|
const collectionConfig = getEntityConfig({ collectionSlug: userSlug })
|
||||||
|
|
||||||
const onChange: FormProps['onChange'][0] = React.useCallback(
|
const onChange: FormProps['onChange'][0] = React.useCallback(
|
||||||
async ({ formState: prevFormState }) => {
|
async ({ formState: prevFormState }) => {
|
||||||
|
|||||||
@@ -17,15 +17,15 @@ export const CreateFirstUserView: React.FC<AdminViewProps> = async ({ initPageRe
|
|||||||
req,
|
req,
|
||||||
req: {
|
req: {
|
||||||
payload: {
|
payload: {
|
||||||
|
collections,
|
||||||
config: {
|
config: {
|
||||||
admin: { user: userSlug },
|
admin: { user: userSlug },
|
||||||
},
|
},
|
||||||
config,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
} = initPageResult
|
} = initPageResult
|
||||||
|
|
||||||
const collectionConfig = config.collections?.find((collection) => collection?.slug === userSlug)
|
const collectionConfig = collections?.[userSlug]?.config
|
||||||
const { auth: authOptions } = collectionConfig
|
const { auth: authOptions } = collectionConfig
|
||||||
const loginWithUsername = authOptions.loginWithUsername
|
const loginWithUsername = authOptions.loginWithUsername
|
||||||
|
|
||||||
|
|||||||
@@ -148,9 +148,7 @@ export const renderDocumentHandler = async (args: {
|
|||||||
importMap: payload.importMap,
|
importMap: payload.importMap,
|
||||||
initialData,
|
initialData,
|
||||||
initPageResult: {
|
initPageResult: {
|
||||||
collectionConfig: payload.config.collections.find(
|
collectionConfig: payload?.collections?.[collectionSlug]?.config,
|
||||||
(collection) => collection.slug === collectionSlug,
|
|
||||||
),
|
|
||||||
cookies,
|
cookies,
|
||||||
docID,
|
docID,
|
||||||
globalConfig: payload.config.globals.find((global) => global.slug === collectionSlug),
|
globalConfig: payload.config.globals.find((global) => global.slug === collectionSlug),
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import React, { useState } from 'react'
|
|||||||
import { FormHeader } from '../../../elements/FormHeader/index.js'
|
import { FormHeader } from '../../../elements/FormHeader/index.js'
|
||||||
|
|
||||||
export const ForgotPasswordForm: React.FC = () => {
|
export const ForgotPasswordForm: React.FC = () => {
|
||||||
const { config } = useConfig()
|
const { config, getEntityConfig } = useConfig()
|
||||||
|
|
||||||
const {
|
const {
|
||||||
admin: { user: userSlug },
|
admin: { user: userSlug },
|
||||||
@@ -19,7 +19,7 @@ export const ForgotPasswordForm: React.FC = () => {
|
|||||||
|
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const [hasSubmitted, setHasSubmitted] = useState(false)
|
const [hasSubmitted, setHasSubmitted] = useState(false)
|
||||||
const collectionConfig = config.collections?.find((collection) => collection?.slug === userSlug)
|
const collectionConfig = getEntityConfig({ collectionSlug: userSlug })
|
||||||
const loginWithUsername = collectionConfig?.auth?.loginWithUsername
|
const loginWithUsername = collectionConfig?.auth?.loginWithUsername
|
||||||
|
|
||||||
const handleResponse: FormProps['handleResponse'] = (res, successToast, errorToast) => {
|
const handleResponse: FormProps['handleResponse'] = (res, successToast, errorToast) => {
|
||||||
|
|||||||
@@ -139,9 +139,7 @@ export const renderListHandler = async (args: {
|
|||||||
enableRowSelections,
|
enableRowSelections,
|
||||||
importMap: payload.importMap,
|
importMap: payload.importMap,
|
||||||
initPageResult: {
|
initPageResult: {
|
||||||
collectionConfig: payload.config.collections.find(
|
collectionConfig: payload?.collections?.[collectionSlug]?.config,
|
||||||
(collection) => collection.slug === collectionSlug,
|
|
||||||
),
|
|
||||||
cookies,
|
cookies,
|
||||||
globalConfig: payload.config.globals.find((global) => global.slug === collectionSlug),
|
globalConfig: payload.config.globals.find((global) => global.slug === collectionSlug),
|
||||||
languageOptions: undefined, // TODO
|
languageOptions: undefined, // TODO
|
||||||
|
|||||||
@@ -567,9 +567,9 @@ export const LivePreviewClient: React.FC<
|
|||||||
url,
|
url,
|
||||||
})
|
})
|
||||||
|
|
||||||
const collectionConfig = getEntityConfig({ collectionSlug }) as ClientCollectionConfig
|
const collectionConfig = getEntityConfig({ collectionSlug })
|
||||||
|
|
||||||
const globalConfig = getEntityConfig({ globalSlug }) as ClientGlobalConfig
|
const globalConfig = getEntityConfig({ globalSlug })
|
||||||
|
|
||||||
const schemaPath = collectionSlug || globalSlug
|
const schemaPath = collectionSlug || globalSlug
|
||||||
|
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ export const LoginForm: React.FC<{
|
|||||||
prefillUsername?: string
|
prefillUsername?: string
|
||||||
searchParams: { [key: string]: string | string[] | undefined }
|
searchParams: { [key: string]: string | string[] | undefined }
|
||||||
}> = ({ prefillEmail, prefillPassword, prefillUsername, searchParams }) => {
|
}> = ({ prefillEmail, prefillPassword, prefillUsername, searchParams }) => {
|
||||||
const { config } = useConfig()
|
const { config, getEntityConfig } = useConfig()
|
||||||
|
|
||||||
const {
|
const {
|
||||||
admin: {
|
admin: {
|
||||||
@@ -34,7 +34,7 @@ export const LoginForm: React.FC<{
|
|||||||
routes: { admin: adminRoute, api: apiRoute },
|
routes: { admin: adminRoute, api: apiRoute },
|
||||||
} = config
|
} = config
|
||||||
|
|
||||||
const collectionConfig = config.collections?.find((collection) => collection?.slug === userSlug)
|
const collectionConfig = getEntityConfig({ collectionSlug: userSlug })
|
||||||
const { auth: authOptions } = collectionConfig
|
const { auth: authOptions } = collectionConfig
|
||||||
const loginWithUsername = authOptions.loginWithUsername
|
const loginWithUsername = authOptions.loginWithUsername
|
||||||
const { canLoginWithEmail, canLoginWithUsername } = getLoginOptions(loginWithUsername)
|
const { canLoginWithEmail, canLoginWithUsername } = getLoginOptions(loginWithUsername)
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ export const LoginView: React.FC<AdminViewProps> = ({ initPageResult, params, se
|
|||||||
redirect((searchParams.redirect as string) || admin)
|
redirect((searchParams.redirect as string) || admin)
|
||||||
}
|
}
|
||||||
|
|
||||||
const collectionConfig = collections.find(({ slug }) => slug === userSlug)
|
const collectionConfig = payload?.collections?.[userSlug]?.config
|
||||||
|
|
||||||
const prefillAutoLogin =
|
const prefillAutoLogin =
|
||||||
typeof config.admin?.autoLogin === 'object' && config.admin?.autoLogin.prefillOnly
|
typeof config.admin?.autoLogin === 'object' && config.admin?.autoLogin.prefillOnly
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
'use client'
|
'use client'
|
||||||
import type { ClientCollectionConfig, ClientGlobalConfig, OptionObject } from 'payload'
|
import type { OptionObject } from 'payload'
|
||||||
|
|
||||||
import { Gutter, useConfig, useDocumentInfo, usePayloadAPI, useTranslation } from '@payloadcms/ui'
|
import { Gutter, useConfig, useDocumentInfo, usePayloadAPI, useTranslation } from '@payloadcms/ui'
|
||||||
import { formatDate } from '@payloadcms/ui/shared'
|
import { formatDate } from '@payloadcms/ui/shared'
|
||||||
@@ -31,11 +31,9 @@ export const DefaultVersionView: React.FC<DefaultVersionsViewProps> = ({
|
|||||||
const { i18n } = useTranslation()
|
const { i18n } = useTranslation()
|
||||||
const { id, collectionSlug, globalSlug } = useDocumentInfo()
|
const { id, collectionSlug, globalSlug } = useDocumentInfo()
|
||||||
|
|
||||||
const [collectionConfig] = useState(
|
const [collectionConfig] = useState(() => getEntityConfig({ collectionSlug }))
|
||||||
() => getEntityConfig({ collectionSlug }) as ClientCollectionConfig,
|
|
||||||
)
|
|
||||||
|
|
||||||
const [globalConfig] = useState(() => getEntityConfig({ globalSlug }) as ClientGlobalConfig)
|
const [globalConfig] = useState(() => getEntityConfig({ globalSlug }))
|
||||||
|
|
||||||
const [locales, setLocales] = useState<OptionObject[]>(localeOptions)
|
const [locales, setLocales] = useState<OptionObject[]>(localeOptions)
|
||||||
|
|
||||||
|
|||||||
@@ -35,13 +35,13 @@ const Restore: React.FC<Props> = ({
|
|||||||
}) => {
|
}) => {
|
||||||
const {
|
const {
|
||||||
config: {
|
config: {
|
||||||
collections,
|
|
||||||
routes: { admin: adminRoute, api: apiRoute },
|
routes: { admin: adminRoute, api: apiRoute },
|
||||||
serverURL,
|
serverURL,
|
||||||
},
|
},
|
||||||
|
getEntityConfig,
|
||||||
} = useConfig()
|
} = useConfig()
|
||||||
|
|
||||||
const collectionConfig = collections.find((collection) => collection.slug === collectionSlug)
|
const collectionConfig = getEntityConfig({ collectionSlug })
|
||||||
|
|
||||||
const { toggleModal } = useModal()
|
const { toggleModal } = useModal()
|
||||||
const [processing, setProcessing] = useState(false)
|
const [processing, setProcessing] = useState(false)
|
||||||
|
|||||||
@@ -18,9 +18,7 @@ export async function getAccessResults({
|
|||||||
|
|
||||||
const isLoggedIn = !!user
|
const isLoggedIn = !!user
|
||||||
const userCollectionConfig =
|
const userCollectionConfig =
|
||||||
user && user.collection
|
user && user.collection ? payload?.collections?.[user.collection]?.config : null
|
||||||
? payload.config.collections.find((collection) => collection.slug === user.collection)
|
|
||||||
: null
|
|
||||||
|
|
||||||
if (userCollectionConfig && payload.config.admin.user === user?.collection) {
|
if (userCollectionConfig && payload.config.admin.user === user?.collection) {
|
||||||
results.canAccessAdmin = userCollectionConfig.access.admin
|
results.canAccessAdmin = userCollectionConfig.access.admin
|
||||||
|
|||||||
@@ -23,9 +23,8 @@ export const previewHandler: PayloadHandler = async (req) => {
|
|||||||
|
|
||||||
let previewURL: string
|
let previewURL: string
|
||||||
|
|
||||||
const generatePreviewURL = req.payload.config.collections.find(
|
const generatePreviewURL =
|
||||||
(config) => config.slug === collection.config.slug,
|
req.payload?.collections?.[collection.config.slug]?.config?.admin?.preview
|
||||||
)?.admin?.preview
|
|
||||||
|
|
||||||
const token = extractJWT(req)
|
const token = extractJWT(req)
|
||||||
|
|
||||||
|
|||||||
@@ -158,9 +158,7 @@ export const promise = async <T>({
|
|||||||
if (Array.isArray(field.relationTo)) {
|
if (Array.isArray(field.relationTo)) {
|
||||||
if (Array.isArray(value)) {
|
if (Array.isArray(value)) {
|
||||||
value.forEach((relatedDoc: { relationTo: string; value: JsonValue }, i) => {
|
value.forEach((relatedDoc: { relationTo: string; value: JsonValue }, i) => {
|
||||||
const relatedCollection = req.payload.config.collections.find(
|
const relatedCollection = req.payload.collections?.[relatedDoc.relationTo]?.config
|
||||||
(collection) => collection.slug === relatedDoc.relationTo,
|
|
||||||
)
|
|
||||||
|
|
||||||
if (
|
if (
|
||||||
typeof relatedDoc.value === 'object' &&
|
typeof relatedDoc.value === 'object' &&
|
||||||
@@ -185,9 +183,7 @@ export const promise = async <T>({
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
if (field.hasMany !== true && valueIsValueWithRelation(value)) {
|
if (field.hasMany !== true && valueIsValueWithRelation(value)) {
|
||||||
const relatedCollection = req.payload.config.collections.find(
|
const relatedCollection = req.payload.collections?.[value.relationTo]?.config
|
||||||
(collection) => collection.slug === value.relationTo,
|
|
||||||
)
|
|
||||||
|
|
||||||
if (typeof value.value === 'object' && value.value && 'id' in value.value) {
|
if (typeof value.value === 'object' && value.value && 'id' in value.value) {
|
||||||
value.value = (value.value as TypeWithID).id
|
value.value = (value.value as TypeWithID).id
|
||||||
@@ -206,9 +202,9 @@ export const promise = async <T>({
|
|||||||
} else {
|
} else {
|
||||||
if (Array.isArray(value)) {
|
if (Array.isArray(value)) {
|
||||||
value.forEach((relatedDoc: unknown, i) => {
|
value.forEach((relatedDoc: unknown, i) => {
|
||||||
const relatedCollection = req.payload.config.collections.find(
|
const relatedCollection = Array.isArray(field.relationTo)
|
||||||
(collection) => collection.slug === field.relationTo,
|
? undefined
|
||||||
)
|
: req.payload.collections?.[field.relationTo]?.config
|
||||||
|
|
||||||
if (typeof relatedDoc === 'object' && relatedDoc && 'id' in relatedDoc) {
|
if (typeof relatedDoc === 'object' && relatedDoc && 'id' in relatedDoc) {
|
||||||
value[i] = relatedDoc.id
|
value[i] = relatedDoc.id
|
||||||
@@ -226,9 +222,7 @@ export const promise = async <T>({
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
if (field.hasMany !== true && value) {
|
if (field.hasMany !== true && value) {
|
||||||
const relatedCollection = req.payload.config.collections.find(
|
const relatedCollection = req.payload.collections?.[field.relationTo]?.config
|
||||||
(collection) => collection.slug === field.relationTo,
|
|
||||||
)
|
|
||||||
|
|
||||||
if (typeof value === 'object' && value && 'id' in value) {
|
if (typeof value === 'object' && value && 'id' in value) {
|
||||||
siblingData[field.name] = value.id
|
siblingData[field.name] = value.id
|
||||||
|
|||||||
@@ -166,7 +166,7 @@ export const email: EmailFieldValidation = (
|
|||||||
{
|
{
|
||||||
collectionSlug,
|
collectionSlug,
|
||||||
req: {
|
req: {
|
||||||
payload: { config },
|
payload: { collections },
|
||||||
t,
|
t,
|
||||||
},
|
},
|
||||||
required,
|
required,
|
||||||
@@ -174,7 +174,7 @@ export const email: EmailFieldValidation = (
|
|||||||
},
|
},
|
||||||
) => {
|
) => {
|
||||||
if (collectionSlug) {
|
if (collectionSlug) {
|
||||||
const collection = config.collections.find(({ slug }) => slug === collectionSlug)
|
const collection = collections?.[collectionSlug]?.config
|
||||||
|
|
||||||
if (
|
if (
|
||||||
collection.auth.loginWithUsername &&
|
collection.auth.loginWithUsername &&
|
||||||
@@ -201,7 +201,7 @@ export const username: UsernameFieldValidation = (
|
|||||||
{
|
{
|
||||||
collectionSlug,
|
collectionSlug,
|
||||||
req: {
|
req: {
|
||||||
payload: { config },
|
payload: { collections, config },
|
||||||
t,
|
t,
|
||||||
},
|
},
|
||||||
required,
|
required,
|
||||||
@@ -211,7 +211,7 @@ export const username: UsernameFieldValidation = (
|
|||||||
let maxLength: number
|
let maxLength: number
|
||||||
|
|
||||||
if (collectionSlug) {
|
if (collectionSlug) {
|
||||||
const collection = config.collections.find(({ slug }) => slug === collectionSlug)
|
const collection = collections?.[collectionSlug]?.config
|
||||||
|
|
||||||
if (
|
if (
|
||||||
collection.auth.loginWithUsername &&
|
collection.auth.loginWithUsername &&
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ export const checkDocumentLockStatus = async ({
|
|||||||
|
|
||||||
// Retrieve the lockDocuments property for either collection or global
|
// Retrieve the lockDocuments property for either collection or global
|
||||||
const lockDocumentsProp = collectionSlug
|
const lockDocumentsProp = collectionSlug
|
||||||
? payload.config?.collections?.find((c) => c.slug === collectionSlug)?.lockDocuments
|
? payload.collections?.[collectionSlug]?.config?.lockDocuments
|
||||||
: payload.config?.globals?.find((g) => g.slug === globalSlug)?.lockDocuments
|
: payload.config?.globals?.find((g) => g.slug === globalSlug)?.lockDocuments
|
||||||
|
|
||||||
const isLockingEnabled = lockDocumentsProp !== false
|
const isLockingEnabled = lockDocumentsProp !== false
|
||||||
|
|||||||
@@ -36,10 +36,10 @@ export const MetaImageComponent: React.FC<MetaImageProps> = (props) => {
|
|||||||
|
|
||||||
const {
|
const {
|
||||||
config: {
|
config: {
|
||||||
collections,
|
|
||||||
routes: { api },
|
routes: { api },
|
||||||
serverURL,
|
serverURL,
|
||||||
},
|
},
|
||||||
|
getEntityConfig,
|
||||||
} = useConfig()
|
} = useConfig()
|
||||||
|
|
||||||
const field: FieldType<string> = useField({ ...props, path } as Options)
|
const field: FieldType<string> = useField({ ...props, path } as Options)
|
||||||
@@ -109,7 +109,7 @@ export const MetaImageComponent: React.FC<MetaImageProps> = (props) => {
|
|||||||
|
|
||||||
const hasImage = Boolean(value)
|
const hasImage = Boolean(value)
|
||||||
|
|
||||||
const collection = collections?.find((coll) => coll.slug === relationTo) || undefined
|
const collection = getEntityConfig({ collectionSlug: relationTo })
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ export function LinkEditor({ anchorElem }: { anchorElem: HTMLElement }): React.R
|
|||||||
uuid,
|
uuid,
|
||||||
} = useEditorConfigContext()
|
} = useEditorConfigContext()
|
||||||
|
|
||||||
const { config } = useConfig()
|
const { config, getEntityConfig } = useConfig()
|
||||||
|
|
||||||
const { i18n, t } = useTranslation<object, 'lexical:link:loadingWithEllipsis'>()
|
const { i18n, t } = useTranslation<object, 'lexical:link:loadingWithEllipsis'>()
|
||||||
|
|
||||||
@@ -150,7 +150,9 @@ export function LinkEditor({ anchorElem }: { anchorElem: HTMLElement }): React.R
|
|||||||
}`,
|
}`,
|
||||||
)
|
)
|
||||||
|
|
||||||
const relatedField = config.collections.find((coll) => coll.slug === fields?.doc?.relationTo)
|
const relatedField = fields?.doc?.relationTo
|
||||||
|
? getEntityConfig({ collectionSlug: fields?.doc?.relationTo })
|
||||||
|
: undefined
|
||||||
if (!relatedField) {
|
if (!relatedField) {
|
||||||
// Usually happens if the user removed all default fields. In this case, we let them specify the label or do not display the label at all.
|
// Usually happens if the user removed all default fields. In this case, we let them specify the label or do not display the label at all.
|
||||||
// label could be a virtual field the user added. This is useful if they want to use the link feature for things other than links.
|
// label could be a virtual field the user added. This is useful if they want to use the link feature for things other than links.
|
||||||
|
|||||||
@@ -59,14 +59,14 @@ const Component: React.FC<Props> = (props) => {
|
|||||||
} = useEditorConfigContext()
|
} = useEditorConfigContext()
|
||||||
const {
|
const {
|
||||||
config: {
|
config: {
|
||||||
collections,
|
|
||||||
routes: { api },
|
routes: { api },
|
||||||
serverURL,
|
serverURL,
|
||||||
},
|
},
|
||||||
|
getEntityConfig,
|
||||||
} = useConfig()
|
} = useConfig()
|
||||||
|
|
||||||
const [relatedCollection, setRelatedCollection] = useState(
|
const [relatedCollection, setRelatedCollection] = useState(() =>
|
||||||
() => collections.find((coll) => coll.slug === relationTo)!,
|
getEntityConfig({ collectionSlug: relationTo }),
|
||||||
)
|
)
|
||||||
|
|
||||||
const { i18n, t } = useTranslation()
|
const { i18n, t } = useTranslation()
|
||||||
|
|||||||
@@ -64,10 +64,10 @@ const Component: React.FC<ElementProps> = (props) => {
|
|||||||
|
|
||||||
const {
|
const {
|
||||||
config: {
|
config: {
|
||||||
collections,
|
|
||||||
routes: { api },
|
routes: { api },
|
||||||
serverURL,
|
serverURL,
|
||||||
},
|
},
|
||||||
|
getEntityConfig,
|
||||||
} = useConfig()
|
} = useConfig()
|
||||||
const uploadRef = useRef<HTMLDivElement | null>(null)
|
const uploadRef = useRef<HTMLDivElement | null>(null)
|
||||||
const { uuid } = useEditorConfigContext()
|
const { uuid } = useEditorConfigContext()
|
||||||
@@ -82,8 +82,8 @@ const Component: React.FC<ElementProps> = (props) => {
|
|||||||
|
|
||||||
const { i18n, t } = useTranslation()
|
const { i18n, t } = useTranslation()
|
||||||
const [cacheBust, dispatchCacheBust] = useReducer((state) => state + 1, 0)
|
const [cacheBust, dispatchCacheBust] = useReducer((state) => state + 1, 0)
|
||||||
const [relatedCollection] = useState<ClientCollectionConfig>(
|
const [relatedCollection] = useState<ClientCollectionConfig>(() =>
|
||||||
() => collections.find((coll) => coll.slug === relationTo)!,
|
getEntityConfig({ collectionSlug: relationTo }),
|
||||||
)
|
)
|
||||||
|
|
||||||
const componentID = useId()
|
const componentID = useId()
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ export const LinkElement = () => {
|
|||||||
const { id, collectionSlug, docPermissions, getDocPreferences, globalSlug } = useDocumentInfo()
|
const { id, collectionSlug, docPermissions, getDocPreferences, globalSlug } = useDocumentInfo()
|
||||||
|
|
||||||
const editor = useSlate()
|
const editor = useSlate()
|
||||||
const { config } = useConfig()
|
const { config, getEntityConfig } = useConfig()
|
||||||
const { code: locale } = useLocale()
|
const { code: locale } = useLocale()
|
||||||
const { i18n, t } = useTranslation()
|
const { i18n, t } = useTranslation()
|
||||||
const { closeModal, openModal, toggleModal } = useModal()
|
const { closeModal, openModal, toggleModal } = useModal()
|
||||||
@@ -179,8 +179,7 @@ export const LinkElement = () => {
|
|||||||
t={t}
|
t={t}
|
||||||
variables={{
|
variables={{
|
||||||
label: getTranslation(
|
label: getTranslation(
|
||||||
config.collections.find(({ slug }) => slug === element.doc.relationTo)?.labels
|
getEntityConfig({ collectionSlug: element.doc.relationTo })?.labels?.singular,
|
||||||
?.singular,
|
|
||||||
i18n,
|
i18n,
|
||||||
),
|
),
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ const RelationshipElementComponent: React.FC = () => {
|
|||||||
routes: { api },
|
routes: { api },
|
||||||
serverURL,
|
serverURL,
|
||||||
},
|
},
|
||||||
|
getEntityConfig,
|
||||||
} = useConfig()
|
} = useConfig()
|
||||||
const [enabledCollectionSlugs] = useState(() =>
|
const [enabledCollectionSlugs] = useState(() =>
|
||||||
collections
|
collections
|
||||||
@@ -47,7 +48,7 @@ const RelationshipElementComponent: React.FC = () => {
|
|||||||
.map(({ slug }) => slug),
|
.map(({ slug }) => slug),
|
||||||
)
|
)
|
||||||
const [relatedCollection, setRelatedCollection] = useState(() =>
|
const [relatedCollection, setRelatedCollection] = useState(() =>
|
||||||
collections.find((coll) => coll.slug === relationTo),
|
getEntityConfig({ collectionSlug: relationTo }),
|
||||||
)
|
)
|
||||||
|
|
||||||
const selected = useSelected()
|
const selected = useSelected()
|
||||||
@@ -117,7 +118,7 @@ const RelationshipElementComponent: React.FC = () => {
|
|||||||
{ at: elementPath },
|
{ at: elementPath },
|
||||||
)
|
)
|
||||||
|
|
||||||
setRelatedCollection(collections.find((coll) => coll.slug === collectionSlug))
|
setRelatedCollection(getEntityConfig({ collectionSlug }))
|
||||||
|
|
||||||
setParams({
|
setParams({
|
||||||
...initialParams,
|
...initialParams,
|
||||||
@@ -127,7 +128,7 @@ const RelationshipElementComponent: React.FC = () => {
|
|||||||
closeListDrawer()
|
closeListDrawer()
|
||||||
dispatchCacheBust()
|
dispatchCacheBust()
|
||||||
},
|
},
|
||||||
[closeListDrawer, editor, element, cacheBust, setParams, collections],
|
[closeListDrawer, editor, element, cacheBust, setParams, getEntityConfig],
|
||||||
)
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -46,15 +46,15 @@ const UploadElementComponent: React.FC<{ enabledCollectionSlugs?: string[] }> =
|
|||||||
|
|
||||||
const {
|
const {
|
||||||
config: {
|
config: {
|
||||||
collections,
|
|
||||||
routes: { api },
|
routes: { api },
|
||||||
serverURL,
|
serverURL,
|
||||||
},
|
},
|
||||||
|
getEntityConfig,
|
||||||
} = useConfig()
|
} = useConfig()
|
||||||
const { i18n, t } = useTranslation()
|
const { i18n, t } = useTranslation()
|
||||||
const [cacheBust, dispatchCacheBust] = useReducer((state) => state + 1, 0)
|
const [cacheBust, dispatchCacheBust] = useReducer((state) => state + 1, 0)
|
||||||
const [relatedCollection, setRelatedCollection] = useState<ClientCollectionConfig>(() =>
|
const [relatedCollection, setRelatedCollection] = useState<ClientCollectionConfig>(() =>
|
||||||
collections.find((coll) => coll.slug === relationTo),
|
getEntityConfig({ collectionSlug: relationTo }),
|
||||||
)
|
)
|
||||||
|
|
||||||
const drawerSlug = useDrawerSlug('upload-drawer')
|
const drawerSlug = useDrawerSlug('upload-drawer')
|
||||||
@@ -121,14 +121,14 @@ const UploadElementComponent: React.FC<{ enabledCollectionSlugs?: string[] }> =
|
|||||||
|
|
||||||
const elementPath = ReactEditor.findPath(editor, element)
|
const elementPath = ReactEditor.findPath(editor, element)
|
||||||
|
|
||||||
setRelatedCollection(collections.find((coll) => coll.slug === collectionSlug))
|
setRelatedCollection(getEntityConfig({ collectionSlug }))
|
||||||
|
|
||||||
Transforms.setNodes(editor, newNode, { at: elementPath })
|
Transforms.setNodes(editor, newNode, { at: elementPath })
|
||||||
|
|
||||||
dispatchCacheBust()
|
dispatchCacheBust()
|
||||||
closeListDrawer()
|
closeListDrawer()
|
||||||
},
|
},
|
||||||
[closeListDrawer, editor, element, collections],
|
[closeListDrawer, editor, element, getEntityConfig],
|
||||||
)
|
)
|
||||||
|
|
||||||
const relatedFieldSchemaPath = `${uploadFieldsSchemaPath}.${relatedCollection.slug}`
|
const relatedFieldSchemaPath = `${uploadFieldsSchemaPath}.${relatedCollection.slug}`
|
||||||
|
|||||||
@@ -6,14 +6,12 @@ import { useState } from 'react'
|
|||||||
import { useConfig } from '../../providers/Config/index.js'
|
import { useConfig } from '../../providers/Config/index.js'
|
||||||
|
|
||||||
export const useRelatedCollections = (relationTo: string | string[]): ClientCollectionConfig[] => {
|
export const useRelatedCollections = (relationTo: string | string[]): ClientCollectionConfig[] => {
|
||||||
const { config } = useConfig()
|
const { getEntityConfig } = useConfig()
|
||||||
|
|
||||||
const [relatedCollections] = useState(() => {
|
const [relatedCollections] = useState(() => {
|
||||||
if (relationTo) {
|
if (relationTo) {
|
||||||
const relations = typeof relationTo === 'string' ? [relationTo] : relationTo
|
const relations = typeof relationTo === 'string' ? [relationTo] : relationTo
|
||||||
return relations.map((relation) =>
|
return relations.map((relation) => getEntityConfig({ collectionSlug: relation }))
|
||||||
config.collections.find((collection) => collection.slug === relation),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
return []
|
return []
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
'use client'
|
'use client'
|
||||||
|
|
||||||
import type { ClientCollectionConfig } from 'payload'
|
|
||||||
|
|
||||||
import { useModal } from '@faceless-ui/modal'
|
import { useModal } from '@faceless-ui/modal'
|
||||||
import { getTranslation } from '@payloadcms/translations'
|
import { getTranslation } from '@payloadcms/translations'
|
||||||
import { reduceFieldsToValues } from 'payload/shared'
|
import { reduceFieldsToValues } from 'payload/shared'
|
||||||
@@ -38,7 +36,7 @@ export function AddingFilesView() {
|
|||||||
const { user } = useAuth()
|
const { user } = useAuth()
|
||||||
const { openModal } = useModal()
|
const { openModal } = useModal()
|
||||||
|
|
||||||
const collection = getEntityConfig({ collectionSlug }) as ClientCollectionConfig
|
const collection = getEntityConfig({ collectionSlug })
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={baseClass}>
|
<div className={baseClass}>
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ export function EditForm({ submitted }: EditFormProps) {
|
|||||||
|
|
||||||
const abortOnChangeRef = React.useRef<AbortController>(null)
|
const abortOnChangeRef = React.useRef<AbortController>(null)
|
||||||
|
|
||||||
const collectionConfig = getEntityConfig({ collectionSlug: docSlug }) as ClientCollectionConfig
|
const collectionConfig = getEntityConfig({ collectionSlug: docSlug })
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const depth = useEditDepth()
|
const depth = useEditDepth()
|
||||||
const params = useSearchParams()
|
const params = useSearchParams()
|
||||||
|
|||||||
@@ -20,10 +20,10 @@ function DrawerContent() {
|
|||||||
const { addFiles, forms, isInitializing } = useFormsManager()
|
const { addFiles, forms, isInitializing } = useFormsManager()
|
||||||
const { closeModal } = useModal()
|
const { closeModal } = useModal()
|
||||||
const { collectionSlug, drawerSlug } = useBulkUpload()
|
const { collectionSlug, drawerSlug } = useBulkUpload()
|
||||||
const { config } = useConfig()
|
const { getEntityConfig } = useConfig()
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
|
||||||
const uploadCollection = config.collections.find((col) => col.slug === collectionSlug)
|
const uploadCollection = getEntityConfig({ collectionSlug })
|
||||||
const uploadConfig = uploadCollection?.upload
|
const uploadConfig = uploadCollection?.upload
|
||||||
const uploadMimeTypes = uploadConfig?.mimeTypes
|
const uploadMimeTypes = uploadConfig?.mimeTypes
|
||||||
|
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ export const DeleteDocument: React.FC<Props> = (props) => {
|
|||||||
getEntityConfig,
|
getEntityConfig,
|
||||||
} = useConfig()
|
} = useConfig()
|
||||||
|
|
||||||
const collectionConfig = getEntityConfig({ collectionSlug }) as ClientCollectionConfig
|
const collectionConfig = getEntityConfig({ collectionSlug })
|
||||||
|
|
||||||
const { setModified } = useForm()
|
const { setModified } = useForm()
|
||||||
const [deleting, setDeleting] = useState(false)
|
const [deleting, setDeleting] = useState(false)
|
||||||
|
|||||||
@@ -98,9 +98,9 @@ export const DocumentControls: React.FC<{
|
|||||||
|
|
||||||
const { config, getEntityConfig } = useConfig()
|
const { config, getEntityConfig } = useConfig()
|
||||||
|
|
||||||
const collectionConfig = getEntityConfig({ collectionSlug: slug }) as ClientCollectionConfig
|
const collectionConfig = getEntityConfig({ collectionSlug: slug })
|
||||||
|
|
||||||
const globalConfig = getEntityConfig({ globalSlug: slug }) as ClientGlobalConfig
|
const globalConfig = getEntityConfig({ globalSlug: slug })
|
||||||
|
|
||||||
const {
|
const {
|
||||||
admin: { dateFormat },
|
admin: { dateFormat },
|
||||||
|
|||||||
@@ -30,14 +30,10 @@ export const DocumentDrawerContent: React.FC<DocumentDrawerProps> = ({
|
|||||||
redirectAfterDelete,
|
redirectAfterDelete,
|
||||||
redirectAfterDuplicate,
|
redirectAfterDuplicate,
|
||||||
}) => {
|
}) => {
|
||||||
const {
|
const { getEntityConfig } = useConfig()
|
||||||
config: { collections },
|
|
||||||
} = useConfig()
|
|
||||||
const locale = useLocale()
|
const locale = useLocale()
|
||||||
|
|
||||||
const [collectionConfig] = useState(() =>
|
const [collectionConfig] = useState(() => getEntityConfig({ collectionSlug }))
|
||||||
collections.find((collection) => collection.slug === collectionSlug),
|
|
||||||
)
|
|
||||||
|
|
||||||
const abortGetDocumentViewRef = React.useRef<AbortController>(null)
|
const abortGetDocumentViewRef = React.useRef<AbortController>(null)
|
||||||
|
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ export const DuplicateDocument: React.FC<Props> = ({
|
|||||||
getEntityConfig,
|
getEntityConfig,
|
||||||
} = useConfig()
|
} = useConfig()
|
||||||
|
|
||||||
const collectionConfig = getEntityConfig({ collectionSlug: slug }) as ClientCollectionConfig
|
const collectionConfig = getEntityConfig({ collectionSlug: slug })
|
||||||
|
|
||||||
const [hasClicked, setHasClicked] = useState<boolean>(false)
|
const [hasClicked, setHasClicked] = useState<boolean>(false)
|
||||||
const { i18n, t } = useTranslation()
|
const { i18n, t } = useTranslation()
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
'use client'
|
'use client'
|
||||||
import type { ClientCollectionConfig, ListQuery } from 'payload'
|
import type { ListQuery } from 'payload'
|
||||||
|
|
||||||
import { useModal } from '@faceless-ui/modal'
|
import { useModal } from '@faceless-ui/modal'
|
||||||
import React, { useCallback, useEffect, useState } from 'react'
|
import React, { useCallback, useEffect, useState } from 'react'
|
||||||
@@ -45,7 +45,7 @@ export const ListDrawerContent: React.FC<ListDrawerProps> = ({
|
|||||||
|
|
||||||
const [selectedOption, setSelectedOption] = useState<Option<string>>(() => {
|
const [selectedOption, setSelectedOption] = useState<Option<string>>(() => {
|
||||||
const initialSelection = selectedCollectionFromProps || enabledCollections[0]?.slug
|
const initialSelection = selectedCollectionFromProps || enabledCollections[0]?.slug
|
||||||
const found = getEntityConfig({ collectionSlug: initialSelection }) as ClientCollectionConfig
|
const found = getEntityConfig({ collectionSlug: initialSelection })
|
||||||
|
|
||||||
return found
|
return found
|
||||||
? {
|
? {
|
||||||
@@ -63,7 +63,7 @@ export const ListDrawerContent: React.FC<ListDrawerProps> = ({
|
|||||||
() => {
|
() => {
|
||||||
if (selectedCollectionFromProps && selectedCollectionFromProps !== selectedOption?.value) {
|
if (selectedCollectionFromProps && selectedCollectionFromProps !== selectedOption?.value) {
|
||||||
setSelectedOption({
|
setSelectedOption({
|
||||||
label: collections.find(({ slug }) => slug === selectedCollectionFromProps).labels,
|
label: getEntityConfig({ collectionSlug: selectedCollectionFromProps })?.labels,
|
||||||
value: selectedCollectionFromProps,
|
value: selectedCollectionFromProps,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,8 +18,9 @@ export const Localizer: React.FC<{
|
|||||||
className?: string
|
className?: string
|
||||||
}> = (props) => {
|
}> = (props) => {
|
||||||
const { className } = props
|
const { className } = props
|
||||||
const { config } = useConfig()
|
const {
|
||||||
const { localization } = config
|
config: { localization },
|
||||||
|
} = useConfig()
|
||||||
const searchParams = useSearchParams()
|
const searchParams = useSearchParams()
|
||||||
const { setLocaleIsLoading } = useLocaleLoading()
|
const { setLocaleIsLoading } = useLocaleLoading()
|
||||||
|
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ export const PublishButton: React.FC<{ label?: string }> = ({ label: labelProp }
|
|||||||
uploadStatus,
|
uploadStatus,
|
||||||
} = useDocumentInfo()
|
} = useDocumentInfo()
|
||||||
|
|
||||||
const { config } = useConfig()
|
const { config, getEntityConfig } = useConfig()
|
||||||
const { submit } = useForm()
|
const { submit } = useForm()
|
||||||
const modified = useFormModified()
|
const modified = useFormModified()
|
||||||
const editDepth = useEditDepth()
|
const editDepth = useEditDepth()
|
||||||
@@ -51,13 +51,13 @@ export const PublishButton: React.FC<{ label?: string }> = ({ label: labelProp }
|
|||||||
|
|
||||||
const entityConfig = React.useMemo(() => {
|
const entityConfig = React.useMemo(() => {
|
||||||
if (collectionSlug) {
|
if (collectionSlug) {
|
||||||
return config.collections.find(({ slug }) => slug === collectionSlug)
|
return getEntityConfig({ collectionSlug })
|
||||||
}
|
}
|
||||||
|
|
||||||
if (globalSlug) {
|
if (globalSlug) {
|
||||||
return config.globals.find(({ slug }) => slug === globalSlug)
|
return getEntityConfig({ globalSlug })
|
||||||
}
|
}
|
||||||
}, [collectionSlug, globalSlug, config])
|
}, [collectionSlug, globalSlug, getEntityConfig])
|
||||||
|
|
||||||
const hasNewerVersions = unpublishedVersionCount > 0
|
const hasNewerVersions = unpublishedVersionCount > 0
|
||||||
|
|
||||||
|
|||||||
@@ -91,9 +91,7 @@ export const RelationshipTable: React.FC<RelationshipTableComponentProps> = (pro
|
|||||||
const [query, setQuery] = useState<ListQuery>()
|
const [query, setQuery] = useState<ListQuery>()
|
||||||
const [openColumnSelector, setOpenColumnSelector] = useState(false)
|
const [openColumnSelector, setOpenColumnSelector] = useState(false)
|
||||||
|
|
||||||
const [collectionConfig] = useState(
|
const [collectionConfig] = useState(() => getEntityConfig({ collectionSlug: relationTo }))
|
||||||
() => getEntityConfig({ collectionSlug: relationTo }) as ClientCollectionConfig,
|
|
||||||
)
|
|
||||||
|
|
||||||
const [isLoadingTable, setIsLoadingTable] = useState(!disableTable)
|
const [isLoadingTable, setIsLoadingTable] = useState(!disableTable)
|
||||||
const [data, setData] = useState<PaginatedDocs>(initialData)
|
const [data, setData] = useState<PaginatedDocs>(initialData)
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
'use client'
|
'use client'
|
||||||
import type {
|
import type {
|
||||||
ClientCollectionConfig,
|
|
||||||
DefaultCellComponentProps,
|
DefaultCellComponentProps,
|
||||||
JoinFieldClient,
|
JoinFieldClient,
|
||||||
RelationshipFieldClient,
|
RelationshipFieldClient,
|
||||||
@@ -98,7 +97,7 @@ export const RelationshipCell: React.FC<RelationshipCellProps> = ({
|
|||||||
const document = documents[relationTo][value]
|
const document = documents[relationTo][value]
|
||||||
const relatedCollection = getEntityConfig({
|
const relatedCollection = getEntityConfig({
|
||||||
collectionSlug: relationTo,
|
collectionSlug: relationTo,
|
||||||
}) as ClientCollectionConfig
|
})
|
||||||
|
|
||||||
const label = formatDocTitle({
|
const label = formatDocTitle({
|
||||||
collectionConfig: relatedCollection,
|
collectionConfig: relatedCollection,
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ export const TableColumnsProvider: React.FC<Props> = ({
|
|||||||
|
|
||||||
const { admin: { defaultColumns, useAsTitle } = {}, fields } = getEntityConfig({
|
const { admin: { defaultColumns, useAsTitle } = {}, fields } = getEntityConfig({
|
||||||
collectionSlug,
|
collectionSlug,
|
||||||
}) as ClientCollectionConfig
|
})
|
||||||
|
|
||||||
const prevCollection = React.useRef<SanitizedCollectionConfig['slug']>(collectionSlug)
|
const prevCollection = React.useRef<SanitizedCollectionConfig['slug']>(collectionSlug)
|
||||||
const { getPreference } = usePreferences()
|
const { getPreference } = usePreferences()
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
'use client'
|
'use client'
|
||||||
import type { ClientCollectionConfig, PaginatedDocs, Where } from 'payload'
|
import type { PaginatedDocs, Where } from 'payload'
|
||||||
|
|
||||||
import * as qs from 'qs-esm'
|
import * as qs from 'qs-esm'
|
||||||
import React, { useCallback, useEffect, useReducer, useState } from 'react'
|
import React, { useCallback, useEffect, useReducer, useState } from 'react'
|
||||||
@@ -28,7 +28,6 @@ export const RelationshipField: React.FC<Props> = (props) => {
|
|||||||
|
|
||||||
const {
|
const {
|
||||||
config: {
|
config: {
|
||||||
collections,
|
|
||||||
routes: { api },
|
routes: { api },
|
||||||
serverURL,
|
serverURL,
|
||||||
},
|
},
|
||||||
@@ -55,7 +54,7 @@ export const RelationshipField: React.FC<Props> = (props) => {
|
|||||||
|
|
||||||
const addOptions = useCallback(
|
const addOptions = useCallback(
|
||||||
(data, relation) => {
|
(data, relation) => {
|
||||||
const collection = getEntityConfig({ collectionSlug: relation }) as ClientCollectionConfig
|
const collection = getEntityConfig({ collectionSlug: relation })
|
||||||
dispatchOptions({ type: 'ADD', collection, data, hasMultipleRelations, i18n, relation })
|
dispatchOptions({ type: 'ADD', collection, data, hasMultipleRelations, i18n, relation })
|
||||||
},
|
},
|
||||||
[hasMultipleRelations, i18n, getEntityConfig],
|
[hasMultipleRelations, i18n, getEntityConfig],
|
||||||
@@ -72,7 +71,7 @@ export const RelationshipField: React.FC<Props> = (props) => {
|
|||||||
if (relationSlug && partiallyLoadedRelationshipSlugs.current.includes(relationSlug)) {
|
if (relationSlug && partiallyLoadedRelationshipSlugs.current.includes(relationSlug)) {
|
||||||
const collection = getEntityConfig({
|
const collection = getEntityConfig({
|
||||||
collectionSlug: relationSlug,
|
collectionSlug: relationSlug,
|
||||||
}) as ClientCollectionConfig
|
})
|
||||||
const fieldToSearch = collection?.admin?.useAsTitle || 'id'
|
const fieldToSearch = collection?.admin?.useAsTitle || 'id'
|
||||||
const pageIndex = nextPageByRelationshipRef.current.get(relationSlug)
|
const pageIndex = nextPageByRelationshipRef.current.get(relationSlug)
|
||||||
|
|
||||||
@@ -135,7 +134,7 @@ export const RelationshipField: React.FC<Props> = (props) => {
|
|||||||
|
|
||||||
setHasLoadedFirstOptions(true)
|
setHasLoadedFirstOptions(true)
|
||||||
},
|
},
|
||||||
[addOptions, api, collections, debouncedSearch, i18n.language, serverURL, t],
|
[addOptions, api, debouncedSearch, getEntityConfig, i18n.language, serverURL, t],
|
||||||
)
|
)
|
||||||
|
|
||||||
const loadMoreOptions = React.useCallback(() => {
|
const loadMoreOptions = React.useCallback(() => {
|
||||||
|
|||||||
@@ -127,9 +127,7 @@ const JoinFieldComponent: JoinFieldClientComponent = (props) => {
|
|||||||
|
|
||||||
const { id: docID, docConfig } = useDocumentInfo()
|
const { id: docID, docConfig } = useDocumentInfo()
|
||||||
|
|
||||||
const {
|
const { getEntityConfig } = useConfig()
|
||||||
config: { collections },
|
|
||||||
} = useConfig()
|
|
||||||
|
|
||||||
const { customComponents: { AfterInput, BeforeInput, Description, Label } = {}, value } =
|
const { customComponents: { AfterInput, BeforeInput, Description, Label } = {}, value } =
|
||||||
useField<PaginatedDocs>({
|
useField<PaginatedDocs>({
|
||||||
@@ -166,7 +164,7 @@ const JoinFieldComponent: JoinFieldClientComponent = (props) => {
|
|||||||
}, [docID, field.targetField.relationTo, field.where, on, docConfig.slug])
|
}, [docID, field.targetField.relationTo, field.where, on, docConfig.slug])
|
||||||
|
|
||||||
const initialDrawerData = useMemo(() => {
|
const initialDrawerData = useMemo(() => {
|
||||||
const relatedCollection = collections.find((collection) => collection.slug === field.collection)
|
const relatedCollection = getEntityConfig({ collectionSlug: field.collection })
|
||||||
|
|
||||||
return getInitialDrawerData({
|
return getInitialDrawerData({
|
||||||
collectionSlug: docConfig.slug,
|
collectionSlug: docConfig.slug,
|
||||||
@@ -174,7 +172,7 @@ const JoinFieldComponent: JoinFieldClientComponent = (props) => {
|
|||||||
fields: relatedCollection.fields,
|
fields: relatedCollection.fields,
|
||||||
segments: field.on.split('.'),
|
segments: field.on.split('.'),
|
||||||
})
|
})
|
||||||
}, [collections, field.on, field.collection, docConfig.slug, docID])
|
}, [getEntityConfig, field.collection, field.on, docConfig.slug, docID])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
|
|||||||
@@ -60,10 +60,9 @@ const RelationshipFieldComponent: RelationshipFieldClientComponent = (props) =>
|
|||||||
validate,
|
validate,
|
||||||
} = props
|
} = props
|
||||||
|
|
||||||
const { config } = useConfig()
|
const { config, getEntityConfig } = useConfig()
|
||||||
|
|
||||||
const {
|
const {
|
||||||
collections,
|
|
||||||
routes: { api },
|
routes: { api },
|
||||||
serverURL,
|
serverURL,
|
||||||
} = config
|
} = config
|
||||||
@@ -169,7 +168,7 @@ const RelationshipFieldComponent: RelationshipFieldClientComponent = (props) =>
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (resultsFetched < 10) {
|
if (resultsFetched < 10) {
|
||||||
const collection = collections.find((coll) => coll.slug === relation)
|
const collection = getEntityConfig({ collectionSlug: relation })
|
||||||
const fieldToSearch = collection?.admin?.useAsTitle || 'id'
|
const fieldToSearch = collection?.admin?.useAsTitle || 'id'
|
||||||
let fieldToSort = collection?.defaultSort || 'id'
|
let fieldToSort = collection?.defaultSort || 'id'
|
||||||
if (typeof sortOptions === 'string') {
|
if (typeof sortOptions === 'string') {
|
||||||
@@ -275,7 +274,7 @@ const RelationshipFieldComponent: RelationshipFieldClientComponent = (props) =>
|
|||||||
hasMany,
|
hasMany,
|
||||||
errorLoading,
|
errorLoading,
|
||||||
search,
|
search,
|
||||||
collections,
|
getEntityConfig,
|
||||||
locale,
|
locale,
|
||||||
serverURL,
|
serverURL,
|
||||||
sortOptions,
|
sortOptions,
|
||||||
@@ -354,7 +353,7 @@ const RelationshipFieldComponent: RelationshipFieldClientComponent = (props) =>
|
|||||||
method: 'POST',
|
method: 'POST',
|
||||||
})
|
})
|
||||||
|
|
||||||
const collection = collections.find((coll) => coll.slug === relation)
|
const collection = getEntityConfig({ collectionSlug: relation })
|
||||||
let docs = []
|
let docs = []
|
||||||
|
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
@@ -380,7 +379,7 @@ const RelationshipFieldComponent: RelationshipFieldClientComponent = (props) =>
|
|||||||
options,
|
options,
|
||||||
hasMany,
|
hasMany,
|
||||||
errorLoading,
|
errorLoading,
|
||||||
collections,
|
getEntityConfig,
|
||||||
hasMultipleRelations,
|
hasMultipleRelations,
|
||||||
serverURL,
|
serverURL,
|
||||||
api,
|
api,
|
||||||
@@ -395,12 +394,12 @@ const RelationshipFieldComponent: RelationshipFieldClientComponent = (props) =>
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const relations = Array.isArray(relationTo) ? relationTo : [relationTo]
|
const relations = Array.isArray(relationTo) ? relationTo : [relationTo]
|
||||||
const isIdOnly = relations.reduce((idOnly, relation) => {
|
const isIdOnly = relations.reduce((idOnly, relation) => {
|
||||||
const collection = collections.find((coll) => coll.slug === relation)
|
const collection = getEntityConfig({ collectionSlug: relation })
|
||||||
const fieldToSearch = collection?.admin?.useAsTitle || 'id'
|
const fieldToSearch = collection?.admin?.useAsTitle || 'id'
|
||||||
return fieldToSearch === 'id' && idOnly
|
return fieldToSearch === 'id' && idOnly
|
||||||
}, true)
|
}, true)
|
||||||
setEnableWordBoundarySearch(!isIdOnly)
|
setEnableWordBoundarySearch(!isIdOnly)
|
||||||
}, [relationTo, collections])
|
}, [relationTo, getEntityConfig])
|
||||||
|
|
||||||
// When (`relationTo` || `filterOptions` || `locale`) changes, reset component
|
// When (`relationTo` || `filterOptions` || `locale`) changes, reset component
|
||||||
// Note - effect should not run on first run
|
// Note - effect should not run on first run
|
||||||
|
|||||||
@@ -1,26 +1,42 @@
|
|||||||
|
/* eslint-disable perfectionist/sort-object-types */ // Need to disable this rule because the order of the overloads is important
|
||||||
'use client'
|
'use client'
|
||||||
import type { ClientCollectionConfig, ClientConfig, ClientGlobalConfig } from 'payload'
|
import type {
|
||||||
|
ClientCollectionConfig,
|
||||||
|
ClientConfig,
|
||||||
|
ClientGlobalConfig,
|
||||||
|
CollectionSlug,
|
||||||
|
GlobalSlug,
|
||||||
|
} from 'payload'
|
||||||
|
|
||||||
import React, { createContext, useCallback, useContext, useEffect, useState } from 'react'
|
import React, { createContext, useCallback, useContext, useEffect, useMemo, useState } from 'react'
|
||||||
|
|
||||||
|
type GetEntityConfigFn = {
|
||||||
|
// Overload #1: collectionSlug only
|
||||||
|
// @todo remove "{} |" in 4.0, which would be a breaking change
|
||||||
|
(args: { collectionSlug: {} | CollectionSlug; globalSlug?: never }): ClientCollectionConfig
|
||||||
|
|
||||||
|
// Overload #2: globalSlug only
|
||||||
|
// @todo remove "{} |" in 4.0, which would be a breaking change
|
||||||
|
(args: { collectionSlug?: never; globalSlug: {} | GlobalSlug }): ClientGlobalConfig
|
||||||
|
|
||||||
|
// Overload #3: both/none (fall back to union | null)
|
||||||
|
(args: {
|
||||||
|
collectionSlug?: {} | CollectionSlug
|
||||||
|
globalSlug?: {} | GlobalSlug
|
||||||
|
}): ClientCollectionConfig | ClientGlobalConfig | null
|
||||||
|
}
|
||||||
|
|
||||||
export type ClientConfigContext = {
|
export type ClientConfigContext = {
|
||||||
config: ClientConfig
|
config: ClientConfig
|
||||||
getEntityConfig: (args: {
|
/**
|
||||||
collectionSlug?: string
|
* Get a collection or global config by its slug. This is preferred over
|
||||||
globalSlug?: string
|
* using `config.collections.find` or `config.globals.find`, because
|
||||||
}) => ClientCollectionConfig | ClientGlobalConfig | null
|
* getEntityConfig uses a lookup map for O(1) lookups.
|
||||||
|
*/
|
||||||
|
getEntityConfig: GetEntityConfigFn
|
||||||
setConfig: (config: ClientConfig) => void
|
setConfig: (config: ClientConfig) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
export type EntityConfigContext = {
|
|
||||||
collectionConfig?: ClientCollectionConfig
|
|
||||||
globalConfig?: ClientGlobalConfig
|
|
||||||
setEntityConfig: (args: {
|
|
||||||
collectionConfig?: ClientCollectionConfig | null
|
|
||||||
globalConfig?: ClientGlobalConfig | null
|
|
||||||
}) => void
|
|
||||||
}
|
|
||||||
|
|
||||||
const RootConfigContext = createContext<ClientConfigContext | undefined>(undefined)
|
const RootConfigContext = createContext<ClientConfigContext | undefined>(undefined)
|
||||||
|
|
||||||
export const ConfigProvider: React.FC<{
|
export const ConfigProvider: React.FC<{
|
||||||
@@ -35,19 +51,32 @@ export const ConfigProvider: React.FC<{
|
|||||||
setConfig(configFromProps)
|
setConfig(configFromProps)
|
||||||
}, [configFromProps])
|
}, [configFromProps])
|
||||||
|
|
||||||
const getEntityConfig = useCallback(
|
// Build lookup maps for collections and globals so we can do O(1) lookups by slug
|
||||||
({ collectionSlug, globalSlug }: { collectionSlug?: string; globalSlug?: string }) => {
|
const { collectionsBySlug, globalsBySlug } = useMemo(() => {
|
||||||
if (collectionSlug) {
|
const collectionsBySlug: Record<string, ClientCollectionConfig> = {}
|
||||||
return config.collections.find((collection) => collection.slug === collectionSlug)
|
const globalsBySlug: Record<string, ClientGlobalConfig> = {}
|
||||||
}
|
|
||||||
|
|
||||||
if (globalSlug) {
|
for (const collection of config.collections) {
|
||||||
return config.globals.find((global) => global.slug === globalSlug)
|
collectionsBySlug[collection.slug] = collection
|
||||||
}
|
}
|
||||||
|
for (const global of config.globals) {
|
||||||
|
globalsBySlug[global.slug] = global
|
||||||
|
}
|
||||||
|
|
||||||
return null
|
return { collectionsBySlug, globalsBySlug }
|
||||||
|
}, [config])
|
||||||
|
|
||||||
|
const getEntityConfig = useCallback<GetEntityConfigFn>(
|
||||||
|
(args) => {
|
||||||
|
if ('collectionSlug' in args) {
|
||||||
|
return collectionsBySlug[args.collectionSlug] ?? null
|
||||||
|
}
|
||||||
|
if ('globalSlug' in args) {
|
||||||
|
return globalsBySlug[args.globalSlug] ?? null
|
||||||
|
}
|
||||||
|
return null as any
|
||||||
},
|
},
|
||||||
[config],
|
[collectionsBySlug, globalsBySlug],
|
||||||
)
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -1,11 +1,5 @@
|
|||||||
'use client'
|
'use client'
|
||||||
import type {
|
import type { ClientUser, DocumentPreferences, SanitizedDocumentPermissions } from 'payload'
|
||||||
ClientCollectionConfig,
|
|
||||||
ClientGlobalConfig,
|
|
||||||
ClientUser,
|
|
||||||
DocumentPreferences,
|
|
||||||
SanitizedDocumentPermissions,
|
|
||||||
} from 'payload'
|
|
||||||
|
|
||||||
import * as qs from 'qs-esm'
|
import * as qs from 'qs-esm'
|
||||||
import React, {
|
import React, {
|
||||||
@@ -79,8 +73,8 @@ const DocumentInfo: React.FC<
|
|||||||
getEntityConfig,
|
getEntityConfig,
|
||||||
} = useConfig()
|
} = useConfig()
|
||||||
|
|
||||||
const collectionConfig = getEntityConfig({ collectionSlug }) as ClientCollectionConfig
|
const collectionConfig = getEntityConfig({ collectionSlug })
|
||||||
const globalConfig = getEntityConfig({ globalSlug }) as ClientGlobalConfig
|
const globalConfig = getEntityConfig({ globalSlug })
|
||||||
|
|
||||||
const abortControllerRef = useRef(new AbortController())
|
const abortControllerRef = useRef(new AbortController())
|
||||||
const docConfig = collectionConfig || globalConfig
|
const docConfig = collectionConfig || globalConfig
|
||||||
|
|||||||
@@ -14,6 +14,8 @@ type Result = {
|
|||||||
user: TypedUser
|
user: TypedUser
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const lockDurationDefault = 300 // Default 5 minutes in seconds
|
||||||
|
|
||||||
export const handleFormStateLocking = async ({
|
export const handleFormStateLocking = async ({
|
||||||
id,
|
id,
|
||||||
collectionSlug,
|
collectionSlug,
|
||||||
@@ -39,9 +41,8 @@ export const handleFormStateLocking = async ({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const lockDurationDefault = 300 // Default 5 minutes in seconds
|
|
||||||
const lockDocumentsProp = collectionSlug
|
const lockDocumentsProp = collectionSlug
|
||||||
? req.payload.config.collections.find((c) => c.slug === collectionSlug)?.lockDocuments
|
? req.payload.collections?.[collectionSlug]?.config.lockDocuments
|
||||||
: req.payload.config.globals.find((g) => g.slug === globalSlug)?.lockDocuments
|
: req.payload.config.globals.find((g) => g.slug === globalSlug)?.lockDocuments
|
||||||
|
|
||||||
const lockDuration =
|
const lockDuration =
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ import { v4 as uuidv4 } from 'uuid'
|
|||||||
|
|
||||||
import { CopyToClipboard } from '../../../elements/CopyToClipboard/index.js'
|
import { CopyToClipboard } from '../../../elements/CopyToClipboard/index.js'
|
||||||
import { GenerateConfirmation } from '../../../elements/GenerateConfirmation/index.js'
|
import { GenerateConfirmation } from '../../../elements/GenerateConfirmation/index.js'
|
||||||
import { FieldLabel } from '../../../fields/FieldLabel/index.js'
|
|
||||||
import { useFormFields } from '../../../forms/Form/context.js'
|
import { useFormFields } from '../../../forms/Form/context.js'
|
||||||
import { useField } from '../../../forms/useField/index.js'
|
import { useField } from '../../../forms/useField/index.js'
|
||||||
import { useConfig } from '../../../providers/Config/index.js'
|
import { useConfig } from '../../../providers/Config/index.js'
|
||||||
@@ -26,16 +25,14 @@ export const APIKey: React.FC<{ readonly enabled: boolean; readonly readOnly?: b
|
|||||||
const [initialAPIKey] = useState(uuidv4())
|
const [initialAPIKey] = useState(uuidv4())
|
||||||
const [highlightedField, setHighlightedField] = useState(false)
|
const [highlightedField, setHighlightedField] = useState(false)
|
||||||
const { i18n, t } = useTranslation()
|
const { i18n, t } = useTranslation()
|
||||||
const { config } = useConfig()
|
const { config, getEntityConfig } = useConfig()
|
||||||
const { collectionSlug } = useDocumentInfo()
|
const { collectionSlug } = useDocumentInfo()
|
||||||
|
|
||||||
const apiKey = useFormFields(([fields]) => (fields && fields[path]) || null)
|
const apiKey = useFormFields(([fields]) => (fields && fields[path]) || null)
|
||||||
|
|
||||||
const apiKeyField: TextFieldClient = config.collections
|
const apiKeyField: TextFieldClient = getEntityConfig({ collectionSlug })?.fields?.find(
|
||||||
.find((collection) => {
|
(field) => 'name' in field && field.name === 'apiKey',
|
||||||
return collection.slug === collectionSlug
|
) as TextFieldClient
|
||||||
})
|
|
||||||
?.fields?.find((field) => 'name' in field && field.name === 'apiKey') as TextFieldClient
|
|
||||||
|
|
||||||
const validate = (val) =>
|
const validate = (val) =>
|
||||||
text(val, {
|
text(val, {
|
||||||
|
|||||||
@@ -1,12 +1,6 @@
|
|||||||
'use client'
|
'use client'
|
||||||
|
|
||||||
import type {
|
import type { ClientSideEditViewProps, ClientUser, FormState } from 'payload'
|
||||||
ClientCollectionConfig,
|
|
||||||
ClientGlobalConfig,
|
|
||||||
ClientSideEditViewProps,
|
|
||||||
ClientUser,
|
|
||||||
FormState,
|
|
||||||
} from 'payload'
|
|
||||||
|
|
||||||
import { useRouter, useSearchParams } from 'next/navigation.js'
|
import { useRouter, useSearchParams } from 'next/navigation.js'
|
||||||
import React, { Fragment, useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
import React, { Fragment, useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
||||||
@@ -109,8 +103,8 @@ export const DefaultEditView: React.FC<ClientSideEditViewProps> = ({
|
|||||||
getEntityConfig,
|
getEntityConfig,
|
||||||
} = useConfig()
|
} = useConfig()
|
||||||
|
|
||||||
const collectionConfig = getEntityConfig({ collectionSlug }) as ClientCollectionConfig
|
const collectionConfig = getEntityConfig({ collectionSlug })
|
||||||
const globalConfig = getEntityConfig({ globalSlug }) as ClientGlobalConfig
|
const globalConfig = getEntityConfig({ globalSlug })
|
||||||
|
|
||||||
const depth = useEditDepth()
|
const depth = useEditDepth()
|
||||||
|
|
||||||
|
|||||||
@@ -90,6 +90,7 @@ const ListDrawerHeader: React.FC<ListHeaderProps> = ({
|
|||||||
}) => {
|
}) => {
|
||||||
const {
|
const {
|
||||||
config: { collections },
|
config: { collections },
|
||||||
|
getEntityConfig,
|
||||||
} = useConfig()
|
} = useConfig()
|
||||||
|
|
||||||
const { closeModal } = useModal()
|
const { closeModal } = useModal()
|
||||||
@@ -102,7 +103,7 @@ const ListDrawerHeader: React.FC<ListHeaderProps> = ({
|
|||||||
setSelectedOption,
|
setSelectedOption,
|
||||||
} = useListDrawerContext()
|
} = useListDrawerContext()
|
||||||
|
|
||||||
const collectionConfig = collections.find(({ slug }) => slug === selectedOption.value)
|
const collectionConfig = getEntityConfig({ collectionSlug: selectedOption.value })
|
||||||
|
|
||||||
const enabledCollectionConfigs = collections.filter(({ slug }) =>
|
const enabledCollectionConfigs = collections.filter(({ slug }) =>
|
||||||
enabledCollections.includes(slug),
|
enabledCollections.includes(slug),
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
'use client'
|
'use client'
|
||||||
|
|
||||||
import type { ClientCollectionConfig, ListPreferences } from 'payload'
|
import type { ListPreferences } from 'payload'
|
||||||
|
|
||||||
import { getTranslation } from '@payloadcms/translations'
|
import { getTranslation } from '@payloadcms/translations'
|
||||||
import LinkImport from 'next/link.js'
|
import LinkImport from 'next/link.js'
|
||||||
@@ -113,7 +113,7 @@ export const DefaultListView: React.FC<ListViewClientProps> = (props) => {
|
|||||||
const { setCollectionSlug, setCurrentActivePath, setOnSuccess } = useBulkUpload()
|
const { setCollectionSlug, setCurrentActivePath, setOnSuccess } = useBulkUpload()
|
||||||
const { drawerSlug: bulkUploadDrawerSlug } = useBulkUpload()
|
const { drawerSlug: bulkUploadDrawerSlug } = useBulkUpload()
|
||||||
|
|
||||||
const collectionConfig = getEntityConfig({ collectionSlug }) as ClientCollectionConfig
|
const collectionConfig = getEntityConfig({ collectionSlug })
|
||||||
|
|
||||||
const { labels, upload } = collectionConfig
|
const { labels, upload } = collectionConfig
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user