Merge pull request #5358 from payloadcms/chore/select-labels

fix(ui): renders select options labels from component map and migrates SanitizedConfig to ClientConfig types
This commit is contained in:
Jacob Fletcher
2024-03-19 10:19:55 -04:00
committed by GitHub
42 changed files with 250 additions and 177 deletions

View File

@@ -1,14 +1,14 @@
'use client'
import type { ClientConfig } from 'payload/types'
import type { ClientCollectionConfig, ClientConfig, ClientGlobalConfig } from 'payload/types'
import { formatDocTitle, useDocumentInfo, useFormFields, useTranslation } from '@payloadcms/ui'
import { useEffect, useRef } from 'react'
export const SetDocumentTitle: React.FC<{
collectionConfig?: ClientConfig['collections'][0]
collectionConfig?: ClientCollectionConfig
config?: ClientConfig
fallback: string
globalConfig?: ClientConfig['globals'][0]
globalConfig?: ClientGlobalConfig
}> = (props) => {
const { collectionConfig, config, fallback, globalConfig } = props

View File

@@ -1,4 +1,7 @@
'use client'
import type { CollectionComponentMap } from '@payloadcms/ui'
import { getTranslation } from '@payloadcms/translations'
import {
Button,
@@ -29,8 +32,6 @@ import LinkImport from 'next/link.js'
import { formatFilesize } from 'payload/utilities'
import React, { Fragment, useEffect } from 'react'
import type { CollectionComponentMap } from '../../../../../ui/src/utilities/buildComponentMap/types.js'
import { RelationshipProvider } from './RelationshipProvider/index.js'
import './index.scss'
@@ -60,7 +61,8 @@ export const DefaultListView: React.FC = () => {
const componentMap = getComponentMap({ collectionSlug }) as CollectionComponentMap
const { AfterList, AfterListTable, BeforeList, BeforeListTable, actionsMap } = componentMap || {}
const { AfterList, AfterListTable, BeforeList, BeforeListTable, actionsMap, fieldMap } =
componentMap || {}
const collectionConfig = config.collections.find(
(collection) => collection.slug === collectionSlug,
@@ -132,6 +134,7 @@ export const DefaultListView: React.FC = () => {
// handleSearchChange={handleSearchChange}
// handleSortChange={handleSortChange}
// handleWhereChange={handleWhereChange}
fieldMap={fieldMap}
// modifySearchQuery={modifySearchParams}
titleField={titleField}
/>
@@ -203,7 +206,7 @@ export const DefaultListView: React.FC = () => {
label={getTranslation(collectionConfig.labels.plural, i18n)}
/>
<div className={`${baseClass}__list-selection-actions`}>
<EditMany collection={collectionConfig} />
<EditMany collection={collectionConfig} fieldMap={fieldMap} />
<PublishMany collection={collectionConfig} />
<UnpublishMany collection={collectionConfig} />
<DeleteMany collection={collectionConfig} />

View File

@@ -1,6 +1,6 @@
'use client'
import type { LivePreviewConfig } from 'payload/config'
import type { Field } from 'payload/types'
import type { ClientConfigField, Field } from 'payload/types'
import { DndContext } from '@dnd-kit/core'
import { fieldSchemaToJSON } from 'payload/utilities'
@@ -20,7 +20,7 @@ export type LivePreviewProviderProps = {
height: number
width: number
}
fieldSchema: Field[]
fieldSchema: ClientConfigField[]
isPopupOpen?: boolean
openPopupWindow?: ReturnType<typeof usePopupWindow>['openPopupWindow']
popupRef?: React.MutableRefObject<Window>

View File

@@ -1,12 +1,7 @@
'use client'
import type { FieldMap, FormProps } from '@payloadcms/ui'
import type { LivePreviewConfig } from 'payload/config'
import type {
ClientConfig,
Data,
SanitizedCollectionConfig,
SanitizedGlobalConfig,
} from 'payload/types'
import type { ClientCollectionConfig, ClientConfig, ClientGlobalConfig, Data } from 'payload/types'
import {
DocumentControls,
@@ -36,10 +31,10 @@ const baseClass = 'live-preview'
type Props = {
apiRoute: string
collectionConfig?: SanitizedCollectionConfig
collectionConfig?: ClientCollectionConfig
config: ClientConfig
fieldMap: FieldMap
globalConfig?: SanitizedGlobalConfig
globalConfig?: ClientGlobalConfig
schemaPath: string
serverURL: string
}

View File

@@ -1,5 +1,5 @@
import type { FieldMap, StepNavItem } from '@payloadcms/ui'
import type { SanitizedCollectionConfig, SanitizedGlobalConfig } from 'payload/types'
import type { ClientCollectionConfig, ClientGlobalConfig } from 'payload/types'
import type React from 'react'
import { getTranslation } from '@payloadcms/translations'
@@ -7,11 +7,11 @@ import { formatDate, useConfig, useLocale, useStepNav, useTranslation } from '@p
import { useEffect } from 'react'
export const SetStepNav: React.FC<{
collectionConfig?: SanitizedCollectionConfig
collectionConfig?: ClientCollectionConfig
collectionSlug?: string
doc: any
fieldMap: FieldMap
globalConfig?: SanitizedGlobalConfig
globalConfig?: ClientGlobalConfig
globalSlug?: string
id?: number | string
mostRecentDoc: any

View File

@@ -1,5 +1,5 @@
'use client'
import type { ClientConfig } from 'payload/types'
import type { ClientCollectionConfig } from 'payload/types'
import { getTranslation } from '@payloadcms/translations'
import { type MappedField, useConfig } from '@payloadcms/ui'
@@ -21,7 +21,7 @@ const baseClass = 'relationship-diff'
type RelationshipValue = Record<string, any>
const generateLabelFromValue = (
collections: ClientConfig['collections'],
collections: ClientCollectionConfig[],
field: MappedField,
locale: string,
value: { relationTo: string; value: RelationshipValue } | RelationshipValue,

View File

@@ -55,31 +55,41 @@ export type ServerOnlyFieldAdminProperties = keyof Pick<
export type ClientConfigField = Omit<Field, 'access' | 'defaultValue' | 'hooks' | 'validate'>
export type ClientConfig = Omit<SanitizedConfig, 'admin' | ServerOnlyRootProperties> & {
export type ClientCollectionConfig = Omit<
SanitizedCollectionConfig,
'admin' | 'fields' | ServerOnlyCollectionProperties
> & {
admin: Omit<
SanitizedCollectionConfig['admin'],
ServerOnlyCollectionAdminProperties & 'fields' & 'livePreview'
> & {
livePreview?: Omit<LivePreviewConfig, ServerOnlyLivePreviewProperties>
}
fields: ClientConfigField[]
}
export type ClientGlobalConfig = Omit<
SanitizedGlobalConfig,
'admin' | 'fields' | ServerOnlyGlobalProperties
> & {
admin: Omit<
SanitizedGlobalConfig['admin'],
ServerOnlyGlobalAdminProperties & 'fields' & 'livePreview'
> & {
livePreview?: Omit<LivePreviewConfig, ServerOnlyLivePreviewProperties>
}
fields: ClientConfigField[]
}
export type ClientConfig = Omit<
SanitizedConfig,
'admin' | 'collections' | 'globals' | ServerOnlyRootProperties
> & {
admin: Omit<SanitizedConfig['admin'], ServerOnlyRootAdminProperties & 'livePreview'> & {
livePreview?: Omit<LivePreviewConfig, ServerOnlyLivePreviewProperties>
}
collections: (Omit<
SanitizedCollectionConfig,
'admin' | 'fields' | ServerOnlyCollectionProperties
> & {
admin: Omit<
SanitizedCollectionConfig['admin'],
ServerOnlyCollectionAdminProperties & 'fields' & 'livePreview'
> & {
livePreview?: Omit<LivePreviewConfig, ServerOnlyLivePreviewProperties>
}
fields: ClientConfigField[]
})[]
globals: (Omit<SanitizedGlobalConfig, 'admin' | 'fields' | ServerOnlyGlobalProperties> & {
admin: Omit<
SanitizedGlobalConfig['admin'],
ServerOnlyGlobalAdminProperties & 'fields' & 'livePreview'
> & {
livePreview?: Omit<LivePreviewConfig, ServerOnlyLivePreviewProperties>
}
fields: ClientConfigField[]
})[]
collections: ClientCollectionConfig[]
globals: ClientGlobalConfig[]
}
export const sanitizeField = (f: Field) => {
@@ -144,7 +154,7 @@ export const sanitizeField = (f: Field) => {
const sanitizeCollections = (
collections: SanitizedConfig['collections'],
): ClientConfig['collections'] =>
): ClientCollectionConfig[] =>
collections.map((collection) => {
const sanitized = { ...collection }
sanitized.fields = sanitizeFields(sanitized.fields)
@@ -202,7 +212,7 @@ const sanitizeCollections = (
return sanitized
})
const sanitizeGlobals = (globals: SanitizedConfig['globals']): ClientConfig['globals'] =>
const sanitizeGlobals = (globals: SanitizedConfig['globals']): ClientGlobalConfig[] =>
globals.map((global) => {
const sanitized = { ...global }
sanitized.fields = sanitizeFields(sanitized.fields)
@@ -299,8 +309,10 @@ export const createClientConfig = async (
}
}
clientConfig.collections = sanitizeCollections(clientConfig.collections)
clientConfig.globals = sanitizeGlobals(clientConfig.globals)
clientConfig.collections = sanitizeCollections(
clientConfig.collections as SanitizedCollectionConfig[],
)
clientConfig.globals = sanitizeGlobals(clientConfig.globals as SanitizedGlobalConfig[])
return clientConfig
}

View File

@@ -680,7 +680,12 @@ export type SanitizedConfig = Omit<
}
}
export type { ClientConfig } from '../config/createClientConfig.js'
export type {
ClientCollectionConfig,
ClientConfig,
ClientConfigField,
ClientGlobalConfig,
} from '../config/createClientConfig.js'
export type EditConfig =
| (

View File

@@ -40,7 +40,10 @@ export type {
export type {
Access,
AccessArgs,
ClientCollectionConfig,
ClientConfig,
ClientConfigField,
ClientGlobalConfig,
EditViewComponent,
SanitizedConfig,
} from './../config/types.js'

View File

@@ -19,6 +19,7 @@ import type { User } from '../../auth/index.js'
import type { SanitizedCollectionConfig, TypeWithID } from '../../collections/config/types.js'
import type { SanitizedGlobalConfig } from '../../globals/config/types.js'
import type { Operation, PayloadRequest, RequestContext, Where } from '../../types/index.js'
import type { ClientConfigField } from './types.js'
export type FieldHookArgs<T extends TypeWithID = any, P = any, S = any> = {
/** The collection which the field belongs to. If the field belongs to a global, this will be null. */
@@ -699,7 +700,7 @@ export type FieldWithMany = RelationshipField | SelectField
export type FieldWithMaxDepth = RelationshipField | UploadField
export function fieldHasSubFields(field: Field): field is FieldWithSubFields {
export function fieldHasSubFields(field: ClientConfigField | Field): field is FieldWithSubFields {
return (
field.type === 'group' ||
field.type === 'array' ||
@@ -742,11 +743,15 @@ export function fieldHasMaxDepth(field: Field): field is FieldWithMaxDepth {
)
}
export function fieldIsPresentationalOnly(field: Field | TabAsField): field is UIField {
export function fieldIsPresentationalOnly(
field: ClientConfigField | Field | TabAsField,
): field is UIField {
return field.type === 'ui'
}
export function fieldAffectsData(field: Field | TabAsField): field is FieldAffectingData {
export function fieldAffectsData(
field: ClientConfigField | Field | TabAsField,
): field is FieldAffectingData {
return 'name' in field && !fieldIsPresentationalOnly(field)
}

View File

@@ -1,5 +1,5 @@
import type { FieldTypes } from '../exports/config.js'
import type { Field } from '../fields/config/types.js'
import type { ClientConfigField } from '../fields/config/types.js'
export type FieldSchemaJSON = {
blocks?: FieldSchemaJSON // TODO: conditionally add based on `type`
@@ -11,15 +11,17 @@ export type FieldSchemaJSON = {
type: keyof FieldTypes
}[]
export const fieldSchemaToJSON = (fields: Field[]): FieldSchemaJSON => {
export const fieldSchemaToJSON = (fields: ClientConfigField[]): FieldSchemaJSON => {
return fields.reduce((acc, field) => {
let result = acc
switch (field.type) {
case 'group':
acc.push({
// @ts-expect-error
name: field.name,
type: field.type,
// @ts-expect-error
fields: fieldSchemaToJSON(field.fields),
})
@@ -27,9 +29,11 @@ export const fieldSchemaToJSON = (fields: Field[]): FieldSchemaJSON => {
case 'array':
acc.push({
// @ts-expect-error
name: field.name,
type: field.type,
fields: fieldSchemaToJSON([
// @ts-expect-error
...field.fields,
{
name: 'id',
@@ -42,8 +46,10 @@ export const fieldSchemaToJSON = (fields: Field[]): FieldSchemaJSON => {
case 'blocks':
acc.push({
// @ts-expect-error
name: field.name,
type: field.type,
// @ts-expect-error
blocks: field.blocks.reduce((acc, block) => {
acc[block.slug] = {
fields: fieldSchemaToJSON([
@@ -63,12 +69,14 @@ export const fieldSchemaToJSON = (fields: Field[]): FieldSchemaJSON => {
case 'row':
case 'collapsible':
// @ts-expect-error
result = result.concat(fieldSchemaToJSON(field.fields))
break
case 'tabs': {
let tabFields = []
// @ts-expect-error
field.tabs.forEach((tab) => {
if ('name' in tab) {
tabFields.push({
@@ -90,9 +98,11 @@ export const fieldSchemaToJSON = (fields: Field[]): FieldSchemaJSON => {
case 'relationship':
case 'upload':
acc.push({
// @ts-expect-error
name: field.name,
type: field.type,
hasMany: 'hasMany' in field ? Boolean(field.hasMany) : false, // TODO: type this
// @ts-expect-error
relationTo: field.relationTo,
})

View File

@@ -1,10 +1,14 @@
import type { Field, FieldAffectingData, FieldPresentationalOnly } from '../fields/config/types.js'
import type {
ClientConfigField,
Field,
FieldAffectingData,
FieldPresentationalOnly,
} from '../fields/config/types.js'
import {
fieldAffectsData,
fieldHasSubFields,
fieldIsPresentationalOnly,
tabHasName,
} from '../fields/config/types.js'
/**
@@ -15,7 +19,7 @@ import {
* @param keepPresentationalFields if true, will skip flattening fields that are presentational only
*/
const flattenFields = (
fields: Field[],
fields: (ClientConfigField | Field)[],
keepPresentationalFields?: boolean,
): (FieldAffectingData | FieldPresentationalOnly)[] => {
return fields.reduce((fieldsToUse, field) => {
@@ -27,19 +31,6 @@ const flattenFields = (
return [...fieldsToUse, ...flattenFields(field.fields, keepPresentationalFields)]
}
if (field.type === 'tabs') {
return [
...fieldsToUse,
...field.tabs.reduce((tabFields, tab) => {
return [
...tabFields,
...(tabHasName(tab)
? [{ ...tab, type: 'tab' }]
: flattenFields(tab.fields, keepPresentationalFields)),
]
}, []),
]
}
return fieldsToUse
}, [])
}

View File

@@ -1,5 +1,5 @@
import type { ClientUser } from 'payload/auth'
import type { SanitizedCollectionConfig } from 'payload/types'
import type { ClientCollectionConfig } from 'payload/types'
import { useAuth, useConfig } from '@payloadcms/ui'
import * as React from 'react'
@@ -10,9 +10,9 @@ type options = {
}
type FilteredCollectionsT = (
collections: SanitizedCollectionConfig[],
collections: ClientCollectionConfig[],
options?: options,
) => SanitizedCollectionConfig[]
) => ClientCollectionConfig[]
const filterRichTextCollections: FilteredCollectionsT = (collections, options) => {
return collections.filter(({ admin: { enableRichTextRelationship, hidden }, upload }) => {
if (hidden === true || (typeof hidden === 'function' && hidden({ user: options.user }))) {

View File

@@ -1,5 +1,5 @@
'use client'
import type { FormState, SanitizedCollectionConfig } from 'payload/types'
import type { ClientCollectionConfig, FormState } from 'payload/types'
import * as facelessUIImport from '@faceless-ui/modal'
import lexicalComposerContextImport from '@lexical/react/LexicalComposerContext.js'
@@ -7,7 +7,6 @@ const { useLexicalComposerContext } = lexicalComposerContextImport
import { getTranslation } from '@payloadcms/translations'
import {
Drawer,
FieldPathProvider,
Form,
type FormProps,
FormSubmit,
@@ -37,7 +36,7 @@ import { useEditorConfigContext } from '../../../../lexical/config/client/Editor
export const ExtraFieldsUploadDrawer: React.FC<
ElementProps & {
drawerSlug: string
relatedCollection: SanitizedCollectionConfig
relatedCollection: ClientCollectionConfig
}
> = (props) => {
const { useModal } = facelessUIImport

View File

@@ -1,5 +1,5 @@
'use client'
import type { SanitizedCollectionConfig } from 'payload/types'
import type { ClientCollectionConfig } from 'payload/types'
import lexicalComposerContextImport from '@lexical/react/LexicalComposerContext.js'
const { useLexicalComposerContext } = lexicalComposerContextImport
@@ -61,7 +61,7 @@ const Component: React.FC<ElementProps> = (props) => {
const { i18n, t } = useTranslation()
const [cacheBust, dispatchCacheBust] = useReducer((state) => state + 1, 0)
const [relatedCollection, setRelatedCollection] = useState<SanitizedCollectionConfig>(() =>
const [relatedCollection, setRelatedCollection] = useState<ClientCollectionConfig>(() =>
collections.find((coll) => coll.slug === relationTo),
)

View File

@@ -1,7 +1,7 @@
'use client'
import type { ClientUser } from 'payload/auth'
import type { SanitizedCollectionConfig } from 'payload/types'
import type { ClientCollectionConfig } from 'payload/types'
import { useAuth, useConfig } from '@payloadcms/ui/providers'
import * as React from 'react'
@@ -12,9 +12,9 @@ type options = {
}
type FilteredCollectionsT = (
collections: SanitizedCollectionConfig[],
collections: ClientCollectionConfig[],
options?: options,
) => SanitizedCollectionConfig[]
) => ClientCollectionConfig[]
const filterRichTextCollections: FilteredCollectionsT = (collections, options) => {
return collections.filter(({ admin: { enableRichTextRelationship, hidden }, upload }) => {
if (hidden === true || (typeof hidden === 'function' && hidden({ user: options.user }))) {

View File

@@ -1,7 +1,7 @@
'use client'
import type { FormFieldBase } from '@payloadcms/ui/types'
import type { SanitizedCollectionConfig } from 'payload/types'
import type { ClientCollectionConfig } from 'payload/types'
import * as facelessUIImport from '@faceless-ui/modal'
import { getTranslation } from '@payloadcms/translations'
@@ -17,7 +17,6 @@ import {
useLocale,
useTranslation,
} from '@payloadcms/ui'
import { FieldPathProvider } from '@payloadcms/ui/forms'
import { deepCopyObject } from 'payload/utilities'
import React, { useCallback, useEffect, useState } from 'react'
import { Transforms } from 'slate'
@@ -34,7 +33,7 @@ export const UploadDrawer: React.FC<{
name: string
richTextComponentMap: Map<string, React.ReactNode>
}
relatedCollection: SanitizedCollectionConfig
relatedCollection: ClientCollectionConfig
schemaPath: string
}> = (props) => {
const { useModal } = facelessUIImport

View File

@@ -1,7 +1,7 @@
'use client'
import type { FormFieldBase } from '@payloadcms/ui/types'
import type { SanitizedCollectionConfig } from 'payload/types'
import type { ClientCollectionConfig } from 'payload/types'
import { getTranslation } from '@payloadcms/translations'
import {
@@ -58,7 +58,7 @@ const UploadElement: React.FC<Props & { enabledCollectionSlugs?: string[] }> = (
} = useConfig()
const { i18n, t } = useTranslation()
const [cacheBust, dispatchCacheBust] = useReducer((state) => state + 1, 0)
const [relatedCollection, setRelatedCollection] = useState<SanitizedCollectionConfig>(() =>
const [relatedCollection, setRelatedCollection] = useState<ClientCollectionConfig>(() =>
collections.find((coll) => coll.slug === relationTo),
)

View File

@@ -1,8 +1,8 @@
import type { SanitizedCollectionConfig, SanitizedGlobalConfig } from 'payload/types'
import type { ClientCollectionConfig, ClientGlobalConfig } from 'payload/types'
export type Props = {
collection?: SanitizedCollectionConfig
global?: SanitizedGlobalConfig
collection?: ClientCollectionConfig
global?: ClientGlobalConfig
id?: number | string
publishedDocUpdatedAt: string
}

View File

@@ -1,6 +1,6 @@
import type { SanitizedCollectionConfig } from 'payload/types'
import type { ClientCollectionConfig } from 'payload/types'
export type Props = {
collection: SanitizedCollectionConfig
collection: ClientCollectionConfig
title?: string
}

View File

@@ -90,10 +90,11 @@ const SaveDraft: React.FC<{ action: string; disabled: boolean }> = ({ action, di
</FormSubmit>
)
}
export const EditMany: React.FC<Props> = (props) => {
const { useModal } = facelessUIImport
const { collection: { slug, fields, labels: { plural } } = {}, collection } = props
const { collection: { slug, fields, labels: { plural } } = {}, collection, fieldMap } = props
const { permissions } = useAuth()
const { closeModal } = useModal()
@@ -219,7 +220,7 @@ export const EditMany: React.FC<Props> = (props) => {
onChange={[onChange]}
onSuccess={onSuccess}
>
<FieldSelect fields={fields} setSelected={setSelected} />
<FieldSelect fieldMap={fieldMap} setSelected={setSelected} />
{reducedFieldMap.length === 0 ? null : (
<RenderFields
fieldMap={reducedFieldMap}

View File

@@ -1,5 +1,8 @@
import type { SanitizedCollectionConfig } from 'payload/types'
import type { ClientCollectionConfig } from 'payload/types'
import type { FieldMap } from '../../index.js'
export type Props = {
collection: SanitizedCollectionConfig
collection: ClientCollectionConfig
fieldMap: FieldMap
}

View File

@@ -1,11 +1,11 @@
import type { Field, FieldWithPath } from 'payload/types'
import type { FieldWithPath } from 'payload/types'
import { getTranslation } from '@payloadcms/translations'
import { fieldAffectsData, fieldHasSubFields, tabHasName } from 'payload/types'
import React, { useState } from 'react'
import React, { Fragment, useState } from 'react'
import type { FieldMap, MappedField } from '../../index.js'
import { useForm } from '../../forms/Form/context.js'
import { createNestedFieldPath } from '../../forms/Form/createNestedFieldPath.js'
import { createNestedClientFieldPath } from '../../forms/Form/createNestedFieldPath.js'
import Label from '../../forms/Label/index.js'
import { useTranslation } from '../../providers/Translation/index.js'
import ReactSelect from '../ReactSelect/index.js'
@@ -14,71 +14,92 @@ import './index.scss'
const baseClass = 'field-select'
type Props = {
fields: Field[]
fieldMap: FieldMap
setSelected: (fields: FieldWithPath[]) => void
}
const combineLabel = (prefix, field, i18n): string =>
`${prefix === '' ? '' : `${prefix} > `}${getTranslation(field.label || field.name, i18n) || ''}`
const combineLabel = (prefix: JSX.Element, field: MappedField): JSX.Element => {
return (
<Fragment>
<span style={{ display: 'inline-block' }}>{prefix}</span>
{prefix ? <Fragment>{' > '}</Fragment> : ''}
<span style={{ display: 'inline-block' }}>{field.fieldComponentProps.Label}</span>
</Fragment>
)
}
const reduceFields = (
fields: Field[],
i18n,
fieldMap: FieldMap,
path = '',
labelPrefix = '',
): { label: string; value: FieldWithPath }[] =>
fields.reduce((fieldsToUse, field) => {
labelPrefix: JSX.Element = null,
): { Label: JSX.Element; value: FieldWithPath }[] => {
if (!fieldMap) {
return []
}
return fieldMap?.reduce((fieldsToUse, field) => {
const { isFieldAffectingData } = field
// escape for a variety of reasons
if (
fieldAffectsData(field) &&
(field.admin?.disableBulkEdit ||
isFieldAffectingData &&
(field.disableBulkEdit ||
field.unique ||
field.hidden ||
field.admin?.hidden ||
field.admin?.readOnly)
field.isHidden ||
field.fieldComponentProps?.readOnly)
) {
return fieldsToUse
}
if (!(field.type === 'array' || field.type === 'blocks') && fieldHasSubFields(field)) {
if (
!(field.type === 'array' || field.type === 'blocks') &&
'fieldMap' in field.fieldComponentProps
) {
return [
...fieldsToUse,
...reduceFields(
field.fields,
i18n,
createNestedFieldPath(path, field),
combineLabel(labelPrefix, field, i18n),
field.fieldComponentProps.fieldMap,
createNestedClientFieldPath(path, field),
combineLabel(labelPrefix, field),
),
]
}
if (field.type === 'tabs') {
if (field.type === 'tabs' && 'fieldMap' in field.fieldComponentProps) {
return [
...fieldsToUse,
...field.tabs.reduce((tabFields, tab) => {
return [
...tabFields,
...reduceFields(
tab.fields,
i18n,
tabHasName(tab) ? createNestedFieldPath(path, field) : path,
combineLabel(labelPrefix, field, i18n),
),
]
...field.fieldComponentProps.fieldMap.reduce((tabFields, tab) => {
if ('fieldMap' in tab.fieldComponentProps) {
return [
...tabFields,
...reduceFields(
tab.fieldComponentProps.fieldMap,
'name' in tab && tab.name ? createNestedClientFieldPath(path, field) : path,
combineLabel(labelPrefix, field),
),
]
}
}, []),
]
}
const formattedField = {
label: combineLabel(labelPrefix, field, i18n),
label: combineLabel(labelPrefix, field),
value: {
...field,
path: createNestedFieldPath(path, field),
path: createNestedClientFieldPath(path, field),
},
}
return [...fieldsToUse, formattedField]
}, [])
export const FieldSelect: React.FC<Props> = ({ fields, setSelected }) => {
const { i18n, t } = useTranslation()
const [options] = useState(() => reduceFields(fields, i18n))
}
export const FieldSelect: React.FC<Props> = ({ fieldMap, setSelected }) => {
const { t } = useTranslation()
const [options] = useState(() => reduceFields(fieldMap))
const { dispatchFields, getFields } = useForm()
const handleChange = (selected) => {
const activeFields = getFields()
if (selected === null) {
@@ -86,6 +107,7 @@ export const FieldSelect: React.FC<Props> = ({ fields, setSelected }) => {
} else {
setSelected(selected.map(({ value }) => value))
}
// remove deselected values from form state
if (selected === null || Object.keys(activeFields || []).length > selected.length) {
Object.keys(activeFields).forEach((path) => {

View File

@@ -36,6 +36,7 @@ export const ListControls: React.FC<Props> = (props) => {
collectionConfig,
enableColumns = true,
enableSort = false,
fieldMap,
handleSearchChange,
handleSortChange,
handleWhereChange,
@@ -73,7 +74,7 @@ export const ListControls: React.FC<Props> = (props) => {
<div className={`${baseClass}__buttons-wrap`}>
{!smallBreak && (
<React.Fragment>
<EditMany collection={collectionConfig} />
<EditMany collection={collectionConfig} fieldMap={fieldMap} />
<PublishMany collection={collectionConfig} />
<UnpublishMany collection={collectionConfig} />
<DeleteMany collection={collectionConfig} />

View File

@@ -1,11 +1,13 @@
import type { FieldAffectingData, SanitizedCollectionConfig, Where } from 'payload/types'
import type { ClientCollectionConfig, FieldAffectingData, Where } from 'payload/types'
import type { FieldMap } from '../../index.js'
import type { Column } from '../Table/types.js'
export type Props = {
collectionConfig: SanitizedCollectionConfig
collectionConfig: ClientCollectionConfig
enableColumns?: boolean
enableSort?: boolean
fieldMap: FieldMap
handleSearchChange?: (search: string) => void
handleSortChange?: (sort: string) => void
handleWhereChange?: (where: Where) => void

View File

@@ -1,5 +1,5 @@
'use client'
import type { SanitizedCollectionConfig } from 'payload/types'
import type { ClientCollectionConfig } from 'payload/types'
import type { Where } from 'payload/types'
import * as facelessUIImport from '@faceless-ui/modal'
@@ -75,13 +75,14 @@ export const ListDrawerContent: React.FC<ListDrawerProps> = ({
return collectionSlugs.includes(slug)
})
const [selectedCollectionConfig, setSelectedCollectionConfig] =
useState<SanitizedCollectionConfig>(() => {
const [selectedCollectionConfig, setSelectedCollectionConfig] = useState<ClientCollectionConfig>(
() => {
return (
enabledCollectionConfigs.find(({ slug }) => slug === selectedCollection) ||
enabledCollectionConfigs?.[0]
)
})
},
)
const { List } = componentMap.collections?.[selectedCollectionConfig?.slug] || {}

View File

@@ -1,5 +1,5 @@
import type { SanitizedCollectionConfig } from 'payload/types'
import type { ClientCollectionConfig } from 'payload/types'
export type Props = {
collection: SanitizedCollectionConfig
collection: ClientCollectionConfig
}

View File

@@ -1,5 +1,5 @@
import type { SanitizedCollectionConfig } from 'payload/types'
import type { ClientCollectionConfig } from 'payload/types'
export type Props = {
collection: SanitizedCollectionConfig
collection: ClientCollectionConfig
}

View File

@@ -1,6 +1,6 @@
import type { I18n } from '@payloadcms/translations'
import type { PaginatedDocs } from 'payload/database'
import type { RelationshipField, SanitizedCollectionConfig } from 'payload/types'
import type { ClientCollectionConfig, RelationshipField } from 'payload/types'
export type Props = {
disabled?: boolean
@@ -22,7 +22,7 @@ type CLEAR = {
}
type ADD = {
collection: SanitizedCollectionConfig
collection: ClientCollectionConfig
data: PaginatedDocs<any>
hasMultipleRelations: boolean
i18n: I18n

View File

@@ -1,5 +1,10 @@
export { mapFields } from '../utilities/buildComponentMap/mapFields.js'
export type { FieldMap, MappedField } from '../utilities/buildComponentMap/types.js'
export type {
CollectionComponentMap,
FieldMap,
GlobalComponentMap,
MappedField,
} from '../utilities/buildComponentMap/types.js'
export { buildFieldSchemaMap } from '../utilities/buildFieldSchemaMap/index.js'
export type { FieldSchemaMap } from '../utilities/buildFieldSchemaMap/types.js'
export { default as canUseDOM } from '../utilities/canUseDOM.js'

View File

@@ -2,6 +2,8 @@ import type { Field } from 'payload/types'
import { fieldAffectsData } from 'payload/types'
import type { MappedField } from '../../index.js'
export const createNestedFieldPath = (parentPath: string, field: Field): string => {
if (parentPath) {
if (fieldAffectsData(field)) {
@@ -17,3 +19,17 @@ export const createNestedFieldPath = (parentPath: string, field: Field): string
return ''
}
export const createNestedClientFieldPath = (parentPath: string, field: MappedField): string => {
if (parentPath) {
if (field.isFieldAffectingData) {
return `${parentPath}.${field.name}`
}
}
if (field.isFieldAffectingData) {
return field.name
}
return field.name
}

View File

@@ -1,5 +1,5 @@
'use client'
import type { SanitizedCollectionConfig } from 'payload/types'
import type { ClientCollectionConfig } from 'payload/types'
import { getTranslation } from '@payloadcms/translations'
import React, { Fragment, useCallback, useEffect, useState } from 'react'
@@ -34,7 +34,7 @@ export const AddNewRelation: React.FC<Props> = ({
const [show, setShow] = useState(false)
const [selectedCollection, setSelectedCollection] = useState<string>()
const relatedToMany = relatedCollections.length > 1
const [collectionConfig, setCollectionConfig] = useState<SanitizedCollectionConfig>(() =>
const [collectionConfig, setCollectionConfig] = useState<ClientCollectionConfig>(() =>
!relatedToMany ? relatedCollections[0] : undefined,
)
const [popupOpen, setPopupOpen] = useState(false)

View File

@@ -1,12 +1,10 @@
import type { ClientConfig, SanitizedCollectionConfig } from 'payload/types'
import type { ClientCollectionConfig } from 'payload/types'
import { useState } from 'react'
import { useConfig } from '../../../../providers/Config/index.js'
export const useRelatedCollections = (
relationTo: string | string[],
): ClientConfig['collections'] => {
export const useRelatedCollections = (relationTo: string | string[]): ClientCollectionConfig[] => {
const config = useConfig()
const [relatedCollections] = useState(() => {

View File

@@ -1,5 +1,5 @@
import type { I18n } from '@payloadcms/translations'
import type { RelationshipField, SanitizedCollectionConfig } from 'payload/types'
import type { ClientCollectionConfig, RelationshipField } from 'payload/types'
import type { SanitizedConfig } from 'payload/types'
import type { FormFieldBase } from '../shared.js'
@@ -38,7 +38,7 @@ type CLEAR = {
}
type UPDATE = {
collection: SanitizedCollectionConfig
collection: ClientCollectionConfig
config: SanitizedConfig
doc: any
i18n: I18n
@@ -46,7 +46,7 @@ type UPDATE = {
}
type ADD = {
collection: SanitizedCollectionConfig
collection: ClientCollectionConfig
config: SanitizedConfig
docs: any[]
i18n: I18n

View File

@@ -1,6 +1,6 @@
'use client'
import type { FilterOptionsResult, SanitizedCollectionConfig, UploadField } from 'payload/types'
import type { ClientCollectionConfig, FilterOptionsResult, UploadField } from 'payload/types'
import { getTranslation } from '@payloadcms/translations'
import React, { useCallback, useEffect, useState } from 'react'
@@ -22,7 +22,7 @@ const baseClass = 'upload'
export type UploadInputProps = Omit<UploadFieldProps, 'filterOptions'> & {
api?: string
collection?: SanitizedCollectionConfig
collection?: ClientCollectionConfig
filterOptions?: FilterOptionsResult
onChange?: (e) => void
relationTo?: UploadField['relationTo']

View File

@@ -1,9 +1,9 @@
import type { FieldAffectingData, SanitizedCollectionConfig } from 'payload/types'
import type { ClientCollectionConfig, FieldAffectingData } from 'payload/types'
import { fieldAffectsData } from 'payload/types'
import { flattenTopLevelFields } from 'payload/utilities'
export const useUseTitleField = (collection: SanitizedCollectionConfig): FieldAffectingData => {
export const useUseTitleField = (collection: ClientCollectionConfig): FieldAffectingData => {
const {
admin: { useAsTitle },
fields,

View File

@@ -1,5 +1,7 @@
import type { PaginatedDocs, TypeWithVersion } from 'payload/database'
import type {
ClientCollectionConfig,
ClientGlobalConfig,
Data,
DocumentPermissions,
DocumentPreferences,
@@ -34,7 +36,7 @@ export type DocumentInfoProps = {
}
export type DocumentInfo = DocumentInfoProps & {
docConfig?: SanitizedCollectionConfig | SanitizedGlobalConfig
docConfig?: ClientCollectionConfig | ClientGlobalConfig
preferencesKey?: string
publishedDoc?: TypeWithID & TypeWithTimestamps & { _status?: string }
slug?: string

View File

@@ -661,6 +661,8 @@ export const mapFields = (args: {
<CustomFieldComponent {...fieldComponentProps} />
) : undefined,
Heading,
disableBulkEdit:
'admin' in field && 'disableBulkEdit' in field.admin && field.admin.disableBulkEdit,
fieldComponentProps,
fieldIsPresentational,
isFieldAffectingData,
@@ -668,6 +670,7 @@ export const mapFields = (args: {
isSidebar:
'admin' in field && 'position' in field.admin && field.admin.position === 'sidebar',
localized: 'localized' in field ? field.localized : false,
unique: 'unique' in field ? field.unique : false,
}
acc.push(reducedField)

View File

@@ -63,6 +63,7 @@ export type MappedField = {
Cell: React.ReactNode
CustomField?: React.ReactNode
Heading: React.ReactNode
disableBulkEdit?: boolean
disabled?: boolean
fieldComponentProps: FieldComponentProps
fieldIsPresentational: boolean
@@ -72,6 +73,7 @@ export type MappedField = {
localized: boolean
name?: string
type: keyof FieldTypes
unique?: boolean
}
export type FieldMap = MappedField[]

View File

@@ -1,8 +1,8 @@
import type { I18n } from '@payloadcms/translations'
import type {
SanitizedCollectionConfig,
ClientCollectionConfig,
ClientGlobalConfig,
SanitizedConfig,
SanitizedGlobalConfig,
TypeWithID,
} from 'payload/types'
@@ -17,11 +17,11 @@ export const formatDocTitle = ({
globalConfig,
i18n,
}: {
collectionConfig?: SanitizedCollectionConfig
collectionConfig?: ClientCollectionConfig
data: TypeWithID
dateFormat: SanitizedConfig['admin']['dateFormat']
fallback?: string
globalConfig?: SanitizedGlobalConfig
globalConfig?: ClientGlobalConfig
i18n: I18n
}): string => {
let title: string
@@ -40,7 +40,9 @@ export const formatDocTitle = ({
const isDate = fieldConfig?.type === 'date'
if (isDate) {
const dateFormat = fieldConfig?.admin?.date?.displayFormat || dateFormatFromConfig
const dateFormat =
('date' in fieldConfig.admin && fieldConfig?.admin?.date?.displayFormat) ||
dateFormatFromConfig
title = formatDate(title, dateFormat, i18n.language) || title
}
}

View File

@@ -1,7 +1,6 @@
import type { I18n } from '@payloadcms/translations'
import type { Permissions } from 'payload/auth'
import type { SanitizedCollectionConfig } from 'payload/types'
import type { SanitizedGlobalConfig } from 'payload/types'
import type { ClientCollectionConfig, ClientGlobalConfig } from 'payload/types'
import { getTranslation } from '@payloadcms/translations'
@@ -12,11 +11,11 @@ export enum EntityType {
export type EntityToGroup =
| {
entity: SanitizedCollectionConfig
entity: ClientCollectionConfig
type: EntityType.collection
}
| {
entity: SanitizedGlobalConfig
entity: ClientGlobalConfig
type: EntityType.global
}

View File

@@ -45,12 +45,6 @@
"@payloadcms/db-mongodb": [
"./packages/db-mongodb/src"
],
"@payloadcms/live-preview": [
"./packages/live-preview/src"
],
"@payloadcms/live-preview-react": [
"./packages/live-preview-react/src/index.ts"
],
"@payloadcms/richtext-lexical": [
"./packages/richtext-lexical/src"
],