chore(ui): strictly types fields (#5344)
This commit is contained in:
@@ -50,10 +50,11 @@ export const SetStepNav: React.FC<{
|
||||
|
||||
if (mostRecentDoc) {
|
||||
if (useAsTitle !== 'id') {
|
||||
const titleField = fieldMap.find(
|
||||
({ name: fieldName, isFieldAffectingData }) =>
|
||||
isFieldAffectingData && fieldName === useAsTitle,
|
||||
) as FieldAffectingData
|
||||
const titleField = fieldMap.find((f) => {
|
||||
const { isFieldAffectingData } = f
|
||||
const fieldName = 'name' in f ? f.name : undefined
|
||||
return isFieldAffectingData && fieldName === useAsTitle
|
||||
}) as FieldAffectingData
|
||||
|
||||
if (titleField && mostRecentDoc[useAsTitle]) {
|
||||
if (titleField.localized) {
|
||||
|
||||
@@ -28,7 +28,7 @@ const Iterable: React.FC<Props> = ({
|
||||
|
||||
return (
|
||||
<div className={baseClass}>
|
||||
{field.label && (
|
||||
{'label' in field && field.label && (
|
||||
<Label>
|
||||
{locale && <span className={`${baseClass}__locale-label`}>{locale}</span>}
|
||||
{getTranslation(field.label, i18n)}
|
||||
@@ -40,12 +40,12 @@ const Iterable: React.FC<Props> = ({
|
||||
const versionRow = version?.[i] || {}
|
||||
const comparisonRow = comparison?.[i] || {}
|
||||
|
||||
let subFields: MappedField[] = []
|
||||
let fieldMap: MappedField[] = []
|
||||
|
||||
if (field.type === 'array') subFields = field.subfields
|
||||
if (field.type === 'array' && 'fieldMap' in field) fieldMap = field.fieldMap
|
||||
|
||||
if (field.type === 'blocks') {
|
||||
subFields = [
|
||||
fieldMap = [
|
||||
// {
|
||||
// name: 'blockType',
|
||||
// label: i18n.t('fields:blockType'),
|
||||
@@ -54,25 +54,28 @@ const Iterable: React.FC<Props> = ({
|
||||
]
|
||||
|
||||
if (versionRow?.blockType === comparisonRow?.blockType) {
|
||||
const matchedBlock = field.blocks.find(
|
||||
(block) => block.slug === versionRow?.blockType,
|
||||
) || { subfields: [] }
|
||||
const matchedBlock = ('blocks' in field &&
|
||||
field.blocks?.find((block) => block.slug === versionRow?.blockType)) || {
|
||||
fieldMap: [],
|
||||
}
|
||||
|
||||
subFields = [...subFields, ...matchedBlock.subfields]
|
||||
fieldMap = [...fieldMap, ...matchedBlock.fieldMap]
|
||||
} else {
|
||||
const matchedVersionBlock = field.blocks.find(
|
||||
(block) => block.slug === versionRow?.blockType,
|
||||
) || { subfields: [] }
|
||||
const matchedVersionBlock = ('blocks' in field &&
|
||||
field.blocks?.find((block) => block.slug === versionRow?.blockType)) || {
|
||||
fieldMap: [],
|
||||
}
|
||||
|
||||
const matchedComparisonBlock = field.blocks.find(
|
||||
(block) => block.slug === comparisonRow?.blockType,
|
||||
) || { subfields: [] }
|
||||
const matchedComparisonBlock = ('blocks' in field &&
|
||||
field.blocks?.find((block) => block.slug === comparisonRow?.blockType)) || {
|
||||
fieldMap: [],
|
||||
}
|
||||
|
||||
subFields = getUniqueListBy<MappedField>(
|
||||
fieldMap = getUniqueListBy<MappedField>(
|
||||
[
|
||||
...subFields,
|
||||
...matchedVersionBlock.subfields,
|
||||
...matchedComparisonBlock.subfields,
|
||||
...fieldMap,
|
||||
...matchedVersionBlock.fieldMap,
|
||||
...matchedComparisonBlock.fieldMap,
|
||||
],
|
||||
'name',
|
||||
)
|
||||
@@ -84,7 +87,7 @@ const Iterable: React.FC<Props> = ({
|
||||
<RenderFieldsToDiff
|
||||
comparison={comparisonRow}
|
||||
diffComponents={diffComponents}
|
||||
fieldMap={subFields}
|
||||
fieldMap={fieldMap}
|
||||
fieldPermissions={permissions}
|
||||
i18n={i18n}
|
||||
locales={locales}
|
||||
@@ -98,7 +101,8 @@ const Iterable: React.FC<Props> = ({
|
||||
{maxRows === 0 && (
|
||||
<div className={`${baseClass}__no-rows`}>
|
||||
{i18n.t('version:noRowsFound', {
|
||||
label: field.labels?.plural
|
||||
label:
|
||||
'labels' in field && field.labels?.plural
|
||||
? getTranslation(field.labels?.plural, i18n)
|
||||
: i18n.t('general:rows'),
|
||||
})}
|
||||
|
||||
@@ -23,7 +23,7 @@ const Nested: React.FC<Props> = ({
|
||||
}) => {
|
||||
return (
|
||||
<div className={baseClass}>
|
||||
{field.label && (
|
||||
{'label' in field && field.label && (
|
||||
<Label>
|
||||
{locale && <span className={`${baseClass}__locale-label`}>{locale}</span>}
|
||||
{getTranslation(field.label, i18n)}
|
||||
|
||||
@@ -82,7 +82,7 @@ const Relationship: React.FC<Props> = ({ comparison, field, i18n, locale, versio
|
||||
let versionToRender = version
|
||||
let comparisonToRender = comparison
|
||||
|
||||
if (field.hasMany) {
|
||||
if ('hasMany' in field && field.hasMany) {
|
||||
if (Array.isArray(version))
|
||||
versionToRender = version
|
||||
.map((val) => generateLabelFromValue(collections, field, locale, val))
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import type { I18n } from '@payloadcms/translations'
|
||||
import type { SelectFieldProps } from 'packages/ui/src/forms/fields/Select/types.js'
|
||||
import type { MappedFieldBase } from 'packages/ui/src/utilities/buildComponentMap/types.js'
|
||||
import type { OptionObject, SelectField } from 'payload/types'
|
||||
|
||||
import { getTranslation } from '@payloadcms/translations'
|
||||
@@ -44,7 +46,11 @@ const getTranslatedOptions = (
|
||||
return typeof options === 'string' ? options : getTranslation(options.label, i18n)
|
||||
}
|
||||
|
||||
const Select: React.FC<Props> = ({ comparison, diffMethod, field, i18n, locale, version }) => {
|
||||
const Select: React.FC<
|
||||
Omit<Props, 'field'> & {
|
||||
field: MappedFieldBase & SelectFieldProps
|
||||
}
|
||||
> = ({ comparison, diffMethod, field, i18n, locale, version }) => {
|
||||
let placeholder = ''
|
||||
|
||||
if (version === comparison) placeholder = `[${i18n.t('general:noValue')}]`
|
||||
@@ -63,7 +69,7 @@ const Select: React.FC<Props> = ({ comparison, diffMethod, field, i18n, locale,
|
||||
<div className={baseClass}>
|
||||
<Label>
|
||||
{locale && <span className={`${baseClass}__locale-label`}>{locale}</span>}
|
||||
{getTranslation(field.label || '', i18n)}
|
||||
{'label' in field && getTranslation(field.label || '', i18n)}
|
||||
</Label>
|
||||
<DiffViewer
|
||||
comparisonToRender={comparisonToRender}
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
import type { TabsFieldProps } from 'packages/ui/src/forms/fields/Tabs/types.js'
|
||||
import type { MappedFieldBase } from 'packages/ui/src/utilities/buildComponentMap/types.js'
|
||||
|
||||
import React from 'react'
|
||||
|
||||
import type { Props } from '../types.js'
|
||||
@@ -7,16 +10,11 @@ import Nested from '../Nested/index.js'
|
||||
|
||||
const baseClass = 'tabs-diff'
|
||||
|
||||
const Tabs: React.FC<Props> = ({
|
||||
comparison,
|
||||
diffComponents,
|
||||
field,
|
||||
i18n,
|
||||
locale,
|
||||
locales,
|
||||
permissions,
|
||||
version,
|
||||
}) => {
|
||||
const Tabs: React.FC<
|
||||
Omit<Props, 'field'> & {
|
||||
field: MappedFieldBase & TabsFieldProps
|
||||
}
|
||||
> = ({ comparison, diffComponents, field, i18n, locale, locales, permissions, version }) => {
|
||||
return (
|
||||
<div className={baseClass}>
|
||||
<div className={`${baseClass}__wrap`}>
|
||||
@@ -27,7 +25,7 @@ const Tabs: React.FC<Props> = ({
|
||||
comparison={comparison?.[tab.name]}
|
||||
diffComponents={diffComponents}
|
||||
field={field}
|
||||
fieldMap={tab.subfields}
|
||||
fieldMap={tab.fieldMap}
|
||||
i18n={i18n}
|
||||
key={i}
|
||||
locale={locale}
|
||||
@@ -42,7 +40,7 @@ const Tabs: React.FC<Props> = ({
|
||||
<RenderFieldsToDiff
|
||||
comparison={comparison}
|
||||
diffComponents={diffComponents}
|
||||
fieldMap={tab.subfields}
|
||||
fieldMap={tab.fieldMap}
|
||||
fieldPermissions={permissions}
|
||||
i18n={i18n}
|
||||
key={i}
|
||||
|
||||
@@ -35,7 +35,7 @@ const Text: React.FC<Props> = ({
|
||||
<div className={baseClass}>
|
||||
<Label>
|
||||
{locale && <span className={`${baseClass}__locale-label`}>{locale}</span>}
|
||||
{getTranslation(field?.label || '', i18n)}
|
||||
{'label' in field && getTranslation(field?.label || '', i18n)}
|
||||
</Label>
|
||||
<DiffViewer
|
||||
comparisonToRender={comparisonToRender}
|
||||
|
||||
@@ -23,7 +23,7 @@ const RenderFieldsToDiff: React.FC<Props> = ({
|
||||
return (
|
||||
<div className={baseClass}>
|
||||
{fieldMap?.map((field, i) => {
|
||||
if (field.name === 'id') return null
|
||||
if ('name' in field && field.name === 'id') return null
|
||||
|
||||
const Component = diffComponents[field.type]
|
||||
|
||||
@@ -31,7 +31,7 @@ const RenderFieldsToDiff: React.FC<Props> = ({
|
||||
const diffMethod: DiffMethod = diffMethods[field.type] || 'CHARS'
|
||||
|
||||
if (Component) {
|
||||
if (field.isFieldAffectingData) {
|
||||
if (field.isFieldAffectingData && 'name' in field) {
|
||||
const valueIsObject = field.type === 'code' || field.type === 'json'
|
||||
|
||||
const versionValue = valueIsObject
|
||||
@@ -53,7 +53,7 @@ const RenderFieldsToDiff: React.FC<Props> = ({
|
||||
diffComponents,
|
||||
diffMethod,
|
||||
field,
|
||||
fieldMap: 'subfields' in field ? field.subfields : fieldMap,
|
||||
fieldMap: 'fieldMap' in field ? field.fieldMap : fieldMap,
|
||||
fieldPermissions: subFieldPermissions,
|
||||
i18n,
|
||||
isRichText,
|
||||
@@ -93,7 +93,7 @@ const RenderFieldsToDiff: React.FC<Props> = ({
|
||||
)
|
||||
}
|
||||
|
||||
if (field.type === 'tabs') {
|
||||
if (field.type === 'tabs' && 'fieldMap' in field) {
|
||||
const Tabs = diffComponents.tabs
|
||||
|
||||
return (
|
||||
@@ -101,7 +101,7 @@ const RenderFieldsToDiff: React.FC<Props> = ({
|
||||
comparison={comparison}
|
||||
diffComponents={diffComponents}
|
||||
field={field}
|
||||
fieldMap={field.subfields}
|
||||
fieldMap={field.fieldMap}
|
||||
i18n={i18n}
|
||||
key={i}
|
||||
locales={locales}
|
||||
@@ -111,14 +111,14 @@ const RenderFieldsToDiff: React.FC<Props> = ({
|
||||
}
|
||||
|
||||
// At this point, we are dealing with a `row`, etc
|
||||
if (field.subfields) {
|
||||
if ('fieldMap' in field) {
|
||||
return (
|
||||
<Nested
|
||||
comparison={comparison}
|
||||
diffComponents={diffComponents}
|
||||
disableGutter
|
||||
field={field}
|
||||
fieldMap={field.subfields}
|
||||
fieldMap={field.fieldMap}
|
||||
i18n={i18n}
|
||||
key={i}
|
||||
locales={locales}
|
||||
|
||||
@@ -17,6 +17,7 @@ export const RichText: React.FC<
|
||||
editorConfig: SanitizedClientEditorConfig // With rendered features n stuff
|
||||
name: string
|
||||
richTextComponentMap: Map<string, React.ReactNode>
|
||||
width?: string
|
||||
}
|
||||
> = (props) => {
|
||||
const {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { FieldMap, FormFieldBase } from '@payloadcms/ui'
|
||||
import type { ReducedBlock } from '@payloadcms/ui/types'
|
||||
import type { ReducedBlock } from '@payloadcms/ui'
|
||||
import type { FormState } from 'payload/types'
|
||||
import type { Data } from 'payload/types'
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ import React, { useCallback, useEffect, useMemo, useState } from 'react'
|
||||
import { type BlockFields } from '../nodes/BlocksNode.js'
|
||||
const baseClass = 'lexical-block'
|
||||
|
||||
import type { ReducedBlock } from '@payloadcms/ui/types'
|
||||
import type { ReducedBlock } from '@payloadcms/ui'
|
||||
import type { FormState } from 'payload/types'
|
||||
|
||||
import { v4 as uuid } from 'uuid'
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
'use client'
|
||||
|
||||
import type { ReducedBlock } from '@payloadcms/ui/types'
|
||||
import type { ReducedBlock } from '@payloadcms/ui'
|
||||
|
||||
import { getTranslation } from '@payloadcms/translations'
|
||||
|
||||
|
||||
@@ -43,10 +43,10 @@ export const BlocksFeature: FeatureProviderProviderServer<
|
||||
for (const block of props.blocks) {
|
||||
clientProps.reducedBlocks.push({
|
||||
slug: block.slug,
|
||||
fieldMap: [],
|
||||
imageAltText: block.imageAltText,
|
||||
imageURL: block.imageURL,
|
||||
labels: block.labels,
|
||||
subfields: [],
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@ import type { FormState } from 'payload/types'
|
||||
|
||||
import {
|
||||
Drawer,
|
||||
FieldPathProvider,
|
||||
Form,
|
||||
type FormProps,
|
||||
FormSubmit,
|
||||
@@ -79,7 +78,6 @@ export const LinkDrawer: React.FC<Props> = ({ drawerSlug, handleModalSubmit, sta
|
||||
<Drawer className={baseClass} slug={drawerSlug} title={t('fields:editLink') ?? ''}>
|
||||
{initialState !== false && (
|
||||
<Form
|
||||
// @ts-expect-error // TODO: Fix this type. Is this correct?
|
||||
fields={Array.isArray(fieldMap) ? fieldMap : []}
|
||||
initialState={initialState}
|
||||
onChange={[onChange]}
|
||||
|
||||
@@ -45,8 +45,10 @@ const RichTextField: React.FC<
|
||||
elements: EnabledFeatures['elements']
|
||||
leaves: EnabledFeatures['leaves']
|
||||
name: string
|
||||
placeholder?: string
|
||||
plugins: RichTextPlugin[]
|
||||
richTextComponentMap: Map<string, React.ReactNode>
|
||||
width?: string
|
||||
}
|
||||
> = (props) => {
|
||||
const {
|
||||
|
||||
@@ -123,7 +123,7 @@ export const EditMany: React.FC<Props> = (props) => {
|
||||
const reducedFieldMap = []
|
||||
fieldMap.map((field) => {
|
||||
selected.map((selectedField) => {
|
||||
if (field.name === selectedField.name) {
|
||||
if ('name' in field && field.name === selectedField.name) {
|
||||
reducedFieldMap.push(field)
|
||||
}
|
||||
})
|
||||
|
||||
@@ -27,8 +27,8 @@ export const buildColumns = (args: {
|
||||
// sort the fields to the order of `defaultColumns` or `columnPreferences`
|
||||
// TODO: flatten top level field, i.e. `flattenTopLevelField()` from `payload` but that is typed for `Field`, not `fieldMap`
|
||||
sortedFieldMap = fieldMap.sort((a, b) => {
|
||||
const aIndex = sortTo.findIndex((column) => column.accessor === a.name)
|
||||
const bIndex = sortTo.findIndex((column) => column.accessor === b.name)
|
||||
const aIndex = sortTo.findIndex((column) => 'name' in a && column.accessor === a.name)
|
||||
const bIndex = sortTo.findIndex((column) => 'name' in b && column.accessor === b.name)
|
||||
if (aIndex === -1 && bIndex === -1) return 0
|
||||
if (aIndex === -1) return 1
|
||||
if (bIndex === -1) return -1
|
||||
@@ -40,7 +40,7 @@ export const buildColumns = (args: {
|
||||
|
||||
const sorted = sortedFieldMap.reduce((acc, field, index) => {
|
||||
const columnPreference = columnPreferences?.find(
|
||||
(preference) => preference.accessor === field.name,
|
||||
(preference) => 'name' in field && preference.accessor === field.name,
|
||||
)
|
||||
|
||||
let active = false
|
||||
@@ -48,7 +48,7 @@ export const buildColumns = (args: {
|
||||
if (columnPreference) {
|
||||
active = columnPreference.active
|
||||
} else if (defaultColumns && Array.isArray(defaultColumns) && defaultColumns.length > 0) {
|
||||
active = defaultColumns.includes(field.name)
|
||||
active = 'name' in field && defaultColumns.includes(field.name)
|
||||
} else if (activeColumnsIndices.length < 4) {
|
||||
active = true
|
||||
}
|
||||
@@ -61,8 +61,8 @@ export const buildColumns = (args: {
|
||||
|
||||
if (field) {
|
||||
const column: Column = {
|
||||
name: field.name,
|
||||
accessor: field.name,
|
||||
name: 'name' in field ? field.name : undefined,
|
||||
accessor: 'name' in field ? field.name : undefined,
|
||||
active,
|
||||
cellProps: {
|
||||
...cellProps?.[index],
|
||||
@@ -72,7 +72,7 @@ export const buildColumns = (args: {
|
||||
Cell: field.Cell,
|
||||
Heading: field.Heading,
|
||||
},
|
||||
label: field.label,
|
||||
label: 'label' in field ? field.label : undefined,
|
||||
}
|
||||
|
||||
acc.push(column)
|
||||
|
||||
@@ -23,31 +23,41 @@ export { default as FormSubmit } from '../forms/Submit/index.js'
|
||||
export { default as Submit } from '../forms/Submit/index.js'
|
||||
export { buildStateFromSchema } from '../forms/buildStateFromSchema/index.js'
|
||||
export type { BuildFormStateArgs } from '../forms/buildStateFromSchema/index.js'
|
||||
export type { ArrayFieldProps } from '../forms/fields/Array/types.js'
|
||||
export { default as SectionTitle } from '../forms/fields/Blocks/SectionTitle/index.js'
|
||||
export type { BlocksFieldProps } from '../forms/fields/Blocks/types.js'
|
||||
export { CheckboxInput } from '../forms/fields/Checkbox/Input.js'
|
||||
export { default as Checkbox } from '../forms/fields/Checkbox/index.js'
|
||||
export type { CheckboxFieldProps } from '../forms/fields/Checkbox/types.js'
|
||||
export type { CodeFieldProps } from '../forms/fields/Code/types.js'
|
||||
export { default as ConfirmPassword } from '../forms/fields/ConfirmPassword/index.js'
|
||||
export type { DateFieldProps } from '../forms/fields/DateTime/types.js'
|
||||
export { default as Email } from '../forms/fields/Email/index.js'
|
||||
export type { EmailFieldProps } from '../forms/fields/Email/types.js'
|
||||
export type { GroupFieldProps } from '../forms/fields/Group/types.js'
|
||||
export { default as HiddenInput } from '../forms/fields/HiddenInput/index.js'
|
||||
export type { HiddenInputFieldProps } from '../forms/fields/HiddenInput/types.js'
|
||||
export type { JSONFieldProps } from '../forms/fields/JSON/types.js'
|
||||
export { default as Number } from '../forms/fields/Number/index.js'
|
||||
|
||||
export type { NumberFieldProps } from '../forms/fields/Number/types.js'
|
||||
|
||||
export { default as Password } from '../forms/fields/Password/index.js'
|
||||
export { default as RadioGroupInput } from '../forms/fields/RadioGroup/index.js'
|
||||
export type { OnChange } from '../forms/fields/RadioGroup/types.js'
|
||||
export { default as Select } from '../forms/fields/Select/index.js'
|
||||
export { default as SelectInput } from '../forms/fields/Select/index.js'
|
||||
|
||||
export { TextInput, type TextInputProps } from '../forms/fields/Text/Input.js'
|
||||
|
||||
export { default as Text } from '../forms/fields/Text/index.js'
|
||||
export type { Props as TextFieldProps } from '../forms/fields/Text/types.js'
|
||||
export { type TextAreaInputProps, TextareaInput } from '../forms/fields/Textarea/Input.js'
|
||||
export { default as Textarea } from '../forms/fields/Textarea/index.js'
|
||||
|
||||
export { UploadInput, type UploadInputProps } from '../forms/fields/Upload/Input.js'
|
||||
|
||||
export { default as UploadField } from '../forms/fields/Upload/index.js'
|
||||
export { fieldTypes } from '../forms/fields/index.js'
|
||||
export { fieldBaseClass } from '../forms/fields/shared.js'
|
||||
export { useField } from '../forms/useField/index.js'
|
||||
export type { FieldType, Options } from '../forms/useField/types.js'
|
||||
export { withCondition } from '../forms/withCondition/index.js'
|
||||
|
||||
export { buildComponentMap } from '../utilities/buildComponentMap/index.js'
|
||||
export type { ReducedBlock } from '../utilities/buildComponentMap/types.js'
|
||||
|
||||
@@ -7,4 +7,3 @@ import type React from 'react'
|
||||
// import { Link } from 'next/link'
|
||||
export type LinkType = React.ElementType
|
||||
export type { FormFieldBase } from '../forms/fields/shared.js'
|
||||
export type { ReducedBlock } from '../utilities/buildComponentMap/types.js'
|
||||
|
||||
@@ -53,7 +53,11 @@ export const RenderFields: React.FC<Props> = (props) => {
|
||||
ref={intersectionRef}
|
||||
>
|
||||
{hasRendered &&
|
||||
fieldMap?.map(({ name, Field, disabled, readOnly }, fieldIndex) => {
|
||||
fieldMap?.map((f, fieldIndex) => {
|
||||
const { Field, disabled, readOnly } = f
|
||||
|
||||
const name = 'name' in f ? f.name : undefined
|
||||
|
||||
return (
|
||||
<RenderField
|
||||
Field={Field}
|
||||
|
||||
@@ -2,24 +2,29 @@ import type { FieldMap } from '../../utilities/buildComponentMap/types.js'
|
||||
|
||||
export const buildPathSegments = (parentPath: string, fieldMap: FieldMap): string[] => {
|
||||
const pathNames = fieldMap.reduce((acc, subField) => {
|
||||
if (subField.subfields && subField.isFieldAffectingData) {
|
||||
if ('fieldMap' in subField) {
|
||||
if (subField.fieldMap && subField.isFieldAffectingData) {
|
||||
// group, block, array
|
||||
acc.push(parentPath ? `${parentPath}.${subField.name}.` : `${subField.name}.`)
|
||||
} else if (subField.subfields) {
|
||||
const name = 'name' in subField ? subField.name : 'unnamed'
|
||||
acc.push(parentPath ? `${parentPath}.${name}.` : `${name}.`)
|
||||
} else if (subField.fieldMap) {
|
||||
// rows, collapsibles, unnamed-tab
|
||||
acc.push(...buildPathSegments(parentPath, subField.subfields))
|
||||
acc.push(...buildPathSegments(parentPath, subField.fieldMap))
|
||||
} else if (subField.type === 'tabs') {
|
||||
// tabs
|
||||
subField.tabs.forEach((tab) => {
|
||||
'tabs' in subField &&
|
||||
subField.tabs?.forEach((tab) => {
|
||||
let tabPath = parentPath
|
||||
if ('name' in tab) {
|
||||
tabPath = parentPath ? `${parentPath}.${tab.name}` : tab.name
|
||||
}
|
||||
acc.push(...buildPathSegments(tabPath, tab.subfields))
|
||||
acc.push(...buildPathSegments(tabPath, tab.fieldMap))
|
||||
})
|
||||
} else if (subField.isFieldAffectingData) {
|
||||
// text, number, date, etc.
|
||||
acc.push(parentPath ? `${parentPath}.${subField.name}` : subField.name)
|
||||
const name = 'name' in subField ? subField.name : 'unnamed'
|
||||
acc.push(parentPath ? `${parentPath}.${name}` : name)
|
||||
}
|
||||
}
|
||||
|
||||
return acc
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
import { getTranslation } from '@payloadcms/translations'
|
||||
import React, { useCallback } from 'react'
|
||||
|
||||
import type { Props } from './types.js'
|
||||
import type { ArrayFieldProps } from './types.js'
|
||||
|
||||
import Banner from '../../../elements/Banner/index.js'
|
||||
import { Button } from '../../../elements/Button/index.js'
|
||||
@@ -24,7 +24,7 @@ import './index.scss'
|
||||
|
||||
const baseClass = 'array-field'
|
||||
|
||||
const ArrayFieldType: React.FC<Props> = (props) => {
|
||||
const ArrayFieldType: React.FC<ArrayFieldProps> = (props) => {
|
||||
const {
|
||||
name,
|
||||
Description,
|
||||
@@ -37,6 +37,8 @@ const ArrayFieldType: React.FC<Props> = (props) => {
|
||||
indexPath,
|
||||
label,
|
||||
localized,
|
||||
maxRows,
|
||||
minRows,
|
||||
path: pathFromProps,
|
||||
permissions,
|
||||
readOnly,
|
||||
@@ -46,9 +48,6 @@ const ArrayFieldType: React.FC<Props> = (props) => {
|
||||
|
||||
const Label = LabelFromProps || <LabelComp label={label} required={required} />
|
||||
|
||||
const minRows = 'minRows' in props ? props?.minRows : 0
|
||||
const maxRows = 'maxRows' in props ? props?.maxRows : undefined
|
||||
|
||||
const { setDocFieldPreferences } = useDocumentInfo()
|
||||
const { addFieldRow, dispatchFields, setModified } = useForm()
|
||||
const submitted = useFormSubmitted()
|
||||
@@ -66,7 +65,7 @@ const ArrayFieldType: React.FC<Props> = (props) => {
|
||||
})()
|
||||
|
||||
// Handle labeling for Arrays, Global Arrays, and Blocks
|
||||
const getLabels = (p: Props) => {
|
||||
const getLabels = (p: ArrayFieldProps) => {
|
||||
if ('labels' in p && p?.labels) return p.labels
|
||||
if ('label' in p && p?.label) return { plural: undefined, singular: p.label }
|
||||
return { plural: t('general:rows'), singular: t('general:row') }
|
||||
|
||||
@@ -1,11 +1,19 @@
|
||||
import type { FieldMap } from '@payloadcms/ui'
|
||||
import type { FieldPermissions } from 'payload/auth'
|
||||
import type { ArrayField, FieldBase, RowLabel } from 'payload/types.js'
|
||||
|
||||
import type { FormFieldBase } from '../shared.js'
|
||||
|
||||
export type Props = FormFieldBase & {
|
||||
export type ArrayFieldProps = FormFieldBase & {
|
||||
RowLabel?: React.ReactNode
|
||||
fieldMap: FieldMap
|
||||
forceRender?: boolean
|
||||
indexPath: string
|
||||
label: false | string
|
||||
label?: FieldBase['label']
|
||||
labels?: ArrayField['labels']
|
||||
maxRows?: ArrayField['maxRows']
|
||||
minRows?: ArrayField['minRows']
|
||||
name?: string
|
||||
permissions: FieldPermissions
|
||||
width?: string
|
||||
}
|
||||
|
||||
@@ -96,7 +96,7 @@ export const BlockRow: React.FC<BlockFieldProps> = ({
|
||||
blockType={row.blockType}
|
||||
blocks={blocks}
|
||||
duplicateRow={duplicateRow}
|
||||
fieldMap={block.subfields}
|
||||
fieldMap={block.fieldMap}
|
||||
hasMaxRows={hasMaxRows}
|
||||
labels={labels}
|
||||
moveRow={moveRow}
|
||||
@@ -135,7 +135,7 @@ export const BlockRow: React.FC<BlockFieldProps> = ({
|
||||
<HiddenInput name={`${path}.id`} value={row.id} />
|
||||
<RenderFields
|
||||
className={`${baseClass}__fields`}
|
||||
fieldMap={block.subfields}
|
||||
fieldMap={block.fieldMap}
|
||||
forceRender={forceRender}
|
||||
margins="small"
|
||||
path={path}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
import { getTranslation } from '@payloadcms/translations'
|
||||
import React, { Fragment, useCallback } from 'react'
|
||||
|
||||
import type { Props } from './types.js'
|
||||
import type { BlocksFieldProps } from './types.js'
|
||||
|
||||
import Banner from '../../../elements/Banner/index.js'
|
||||
import { Button } from '../../../elements/Button/index.js'
|
||||
@@ -27,7 +27,7 @@ import './index.scss'
|
||||
|
||||
const baseClass = 'blocks-field'
|
||||
|
||||
const BlocksField: React.FC<Props> = (props) => {
|
||||
const BlocksField: React.FC<BlocksFieldProps> = (props) => {
|
||||
const { i18n, t } = useTranslation()
|
||||
|
||||
const {
|
||||
@@ -35,11 +35,15 @@ const BlocksField: React.FC<Props> = (props) => {
|
||||
Description,
|
||||
Error,
|
||||
Label: LabelFromProps,
|
||||
blocks,
|
||||
className,
|
||||
forceRender = false,
|
||||
indexPath,
|
||||
label,
|
||||
labels: labelsFromProps,
|
||||
localized,
|
||||
maxRows,
|
||||
minRows,
|
||||
path: pathFromProps,
|
||||
readOnly,
|
||||
required,
|
||||
@@ -48,11 +52,6 @@ const BlocksField: React.FC<Props> = (props) => {
|
||||
|
||||
const Label = LabelFromProps || <LabelComp label={label} required={required} />
|
||||
|
||||
const minRows = 'minRows' in props ? props?.minRows : 0
|
||||
const maxRows = 'maxRows' in props ? props?.maxRows : undefined
|
||||
const blocks = 'blocks' in props ? props?.blocks : undefined
|
||||
const labelsFromProps = 'labels' in props ? props?.labels : undefined
|
||||
|
||||
const { setDocFieldPreferences } = useDocumentInfo()
|
||||
const { addFieldRow, dispatchFields, setModified } = useForm()
|
||||
const { code: locale } = useLocale()
|
||||
|
||||
@@ -1,10 +1,20 @@
|
||||
import type { FieldMap, ReducedBlock } from '@payloadcms/ui'
|
||||
import type { FieldPermissions } from 'payload/auth'
|
||||
import type { BlockField, FieldBase } from 'payload/types.js'
|
||||
|
||||
import type { FormFieldBase } from '../shared.js'
|
||||
|
||||
export type Props = FormFieldBase & {
|
||||
export type BlocksFieldProps = FormFieldBase & {
|
||||
blocks?: ReducedBlock[]
|
||||
fieldMap: FieldMap
|
||||
forceRender?: boolean
|
||||
indexPath: string
|
||||
label?: FieldBase['label']
|
||||
labels?: BlockField['labels']
|
||||
maxRows?: number
|
||||
minRows?: number
|
||||
name?: string
|
||||
permissions: FieldPermissions
|
||||
slug?: string
|
||||
width?: string
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ import type { ClientValidate } from 'payload/types'
|
||||
|
||||
import React, { useCallback } from 'react'
|
||||
|
||||
import type { Props } from './types.js'
|
||||
import type { CheckboxFieldProps } from './types.js'
|
||||
|
||||
import { generateFieldID } from '../../../utilities/generateFieldID.js'
|
||||
import { useForm } from '../../Form/context.js'
|
||||
@@ -16,7 +16,7 @@ import './index.scss'
|
||||
|
||||
const baseClass = 'checkbox'
|
||||
|
||||
const Checkbox: React.FC<Props> = (props) => {
|
||||
const Checkbox: React.FC<CheckboxFieldProps> = (props) => {
|
||||
const {
|
||||
id,
|
||||
name,
|
||||
|
||||
@@ -1,11 +1,15 @@
|
||||
import type { FieldBase } from 'payload/types.js'
|
||||
|
||||
import type { FormFieldBase } from '../shared.js'
|
||||
|
||||
export type Props = FormFieldBase & {
|
||||
export type CheckboxFieldProps = FormFieldBase & {
|
||||
checked?: boolean
|
||||
disableFormData?: boolean
|
||||
id?: string
|
||||
label?: FieldBase['label']
|
||||
name?: string
|
||||
onChange?: (val: boolean) => void
|
||||
partialChecked?: boolean
|
||||
path?: string
|
||||
width?: string
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
'use client'
|
||||
import React, { useCallback } from 'react'
|
||||
|
||||
import type { Props } from './types.js'
|
||||
import type { CodeFieldProps } from './types.js'
|
||||
|
||||
import { CodeEditor } from '../../../elements/CodeEditor/index.js'
|
||||
import LabelComp from '../../Label/index.js'
|
||||
@@ -18,7 +18,7 @@ const prismToMonacoLanguageMap = {
|
||||
|
||||
const baseClass = 'code-field'
|
||||
|
||||
const Code: React.FC<Props> = (props) => {
|
||||
const Code: React.FC<CodeFieldProps> = (props) => {
|
||||
const {
|
||||
name,
|
||||
AfterInput,
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
import type { CodeField, FieldBase } from 'payload/types.js'
|
||||
|
||||
import type { FormFieldBase } from '../shared.js'
|
||||
|
||||
export type Props = FormFieldBase & {
|
||||
export type CodeFieldProps = FormFieldBase & {
|
||||
editorOptions?: CodeField['admin']['editorOptions']
|
||||
label?: FieldBase['label']
|
||||
language?: CodeField['admin']['language']
|
||||
name?: string
|
||||
path?: string
|
||||
width: string
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import type { DocumentPreferences } from 'payload/types'
|
||||
|
||||
import React, { Fragment, useCallback, useEffect, useState } from 'react'
|
||||
|
||||
import type { Props } from './types.js'
|
||||
import type { CollapsibleFieldProps } from './types.js'
|
||||
|
||||
import { Collapsible } from '../../../elements/Collapsible/index.js'
|
||||
import { ErrorPill } from '../../../elements/ErrorPill/index.js'
|
||||
@@ -21,7 +21,7 @@ import './index.scss'
|
||||
|
||||
const baseClass = 'collapsible-field'
|
||||
|
||||
const CollapsibleField: React.FC<Props> = (props) => {
|
||||
const CollapsibleField: React.FC<CollapsibleFieldProps> = (props) => {
|
||||
const {
|
||||
Description,
|
||||
Label: LabelFromProps,
|
||||
|
||||
@@ -1,10 +1,16 @@
|
||||
import type { FieldMap } from '@payloadcms/ui'
|
||||
import type { FieldPermissions } from 'payload/auth'
|
||||
import type { FieldTypes } from 'payload/config'
|
||||
import type { FieldBase } from 'payload/types.js'
|
||||
|
||||
import type { FormFieldBase } from '../shared.js'
|
||||
|
||||
export type Props = FormFieldBase & {
|
||||
export type CollapsibleFieldProps = FormFieldBase & {
|
||||
fieldMap: FieldMap
|
||||
fieldTypes: FieldTypes
|
||||
indexPath: string
|
||||
initCollapsed?: boolean
|
||||
label?: FieldBase['label']
|
||||
permissions: FieldPermissions
|
||||
width?: string
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ import type { FormField } from 'payload/types'
|
||||
|
||||
import React, { useCallback } from 'react'
|
||||
|
||||
import type { Props } from './types.js'
|
||||
import type { ConfirmPasswordFieldProps } from './types.js'
|
||||
|
||||
import { useTranslation } from '../../../providers/Translation/index.js'
|
||||
import Error from '../../Error/index.js'
|
||||
@@ -13,7 +13,7 @@ import { useField } from '../../useField/index.js'
|
||||
import { fieldBaseClass } from '../shared.js'
|
||||
import './index.scss'
|
||||
|
||||
const ConfirmPassword: React.FC<Props> = (props) => {
|
||||
const ConfirmPassword: React.FC<ConfirmPasswordFieldProps> = (props) => {
|
||||
const { disabled } = props
|
||||
|
||||
const password = useFormFields<FormField>(([fields]) => fields.password)
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
export type Props = {
|
||||
export type ConfirmPasswordFieldProps = {
|
||||
disabled?: boolean
|
||||
}
|
||||
|
||||
@@ -5,25 +5,27 @@ import type { ClientValidate } from 'payload/types'
|
||||
import { getTranslation } from '@payloadcms/translations'
|
||||
import React, { useCallback } from 'react'
|
||||
|
||||
import type { Props } from './types.js'
|
||||
import type { DateFieldProps } from './types.js'
|
||||
|
||||
import { DatePickerField } from '../../../elements/DatePicker/index.js'
|
||||
import { useTranslation } from '../../../providers/Translation/index.js'
|
||||
import LabelComp from '../../Label/index.js'
|
||||
import { useField } from '../../useField/index.js'
|
||||
import { fieldBaseClass } from '../shared.js'
|
||||
import './index.scss'
|
||||
|
||||
const baseClass = 'date-time-field'
|
||||
|
||||
const DateTime: React.FC<Props> = (props) => {
|
||||
const DateTime: React.FC<DateFieldProps> = (props) => {
|
||||
const {
|
||||
name,
|
||||
AfterInput,
|
||||
BeforeInput,
|
||||
Description,
|
||||
Error,
|
||||
Label: LabelComp,
|
||||
Label: LabelFromProps,
|
||||
className,
|
||||
date: datePickerProps,
|
||||
label,
|
||||
path: pathFromProps,
|
||||
placeholder,
|
||||
@@ -34,9 +36,7 @@ const DateTime: React.FC<Props> = (props) => {
|
||||
width,
|
||||
} = props
|
||||
|
||||
const Label = LabelComp || label
|
||||
|
||||
const datePickerProps = 'date' in props ? props.date : {}
|
||||
const Label = LabelFromProps || <LabelComp label={label} required={required} />
|
||||
|
||||
const { i18n } = useTranslation()
|
||||
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
import type { DateField, FieldBase } from 'payload/types.js'
|
||||
|
||||
import type { FormFieldBase } from '../shared.js'
|
||||
|
||||
export type Props = FormFieldBase & {
|
||||
export type DateFieldProps = FormFieldBase & {
|
||||
date?: DateField['admin']['date']
|
||||
label?: FieldBase['label']
|
||||
name?: string
|
||||
path: string
|
||||
path?: string
|
||||
placeholder?: DateField['admin']['placeholder'] | string
|
||||
width?: string
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import type { ClientValidate } from 'payload/types'
|
||||
import { getTranslation } from '@payloadcms/translations'
|
||||
import React, { useCallback } from 'react'
|
||||
|
||||
import type { Props } from './types.js'
|
||||
import type { EmailFieldProps } from './types.js'
|
||||
|
||||
import { useTranslation } from '../../../providers/Translation/index.js'
|
||||
import LabelComp from '../../Label/index.js'
|
||||
@@ -13,7 +13,7 @@ import { withCondition } from '../../withCondition/index.js'
|
||||
import { fieldBaseClass } from '../shared.js'
|
||||
import './index.scss'
|
||||
|
||||
export const Email: React.FC<Props> = (props) => {
|
||||
export const Email: React.FC<EmailFieldProps> = (props) => {
|
||||
const {
|
||||
name,
|
||||
AfterInput,
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
import type { EmailField, FieldBase } from 'payload/types.js'
|
||||
|
||||
import type { FormFieldBase } from '../shared.js'
|
||||
|
||||
export type Props = FormFieldBase & {
|
||||
export type EmailFieldProps = FormFieldBase & {
|
||||
autoComplete?: string
|
||||
label?: FieldBase['label']
|
||||
name?: string
|
||||
path?: string
|
||||
placeholder?: EmailField['admin']['placeholder']
|
||||
width?: string
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
'use client'
|
||||
import React, { Fragment } from 'react'
|
||||
|
||||
import type { Props } from './types.js'
|
||||
import type { GroupFieldProps } from './types.js'
|
||||
|
||||
import { useCollapsible } from '../../../elements/Collapsible/provider.js'
|
||||
import { ErrorPill } from '../../../elements/ErrorPill/index.js'
|
||||
@@ -19,7 +19,7 @@ import { GroupProvider, useGroup } from './provider.js'
|
||||
|
||||
const baseClass = 'group-field'
|
||||
|
||||
const Group: React.FC<Props> = (props) => {
|
||||
const Group: React.FC<GroupFieldProps> = (props) => {
|
||||
const {
|
||||
Description,
|
||||
Label: LabelFromProps,
|
||||
|
||||
@@ -1,11 +1,16 @@
|
||||
import type { FieldMap } from '@payloadcms/ui'
|
||||
import type { FieldPermissions } from 'payload/auth'
|
||||
import type { FieldBase } from 'payload/types.js'
|
||||
|
||||
import type { FormFieldBase } from '../shared.js'
|
||||
|
||||
export type Props = FormFieldBase & {
|
||||
export type GroupFieldProps = FormFieldBase & {
|
||||
fieldMap: FieldMap
|
||||
forceRender?: boolean
|
||||
hideGutter?: boolean
|
||||
indexPath: string
|
||||
label?: FieldBase['label']
|
||||
name?: string
|
||||
permissions: FieldPermissions
|
||||
width?: string
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
'use client'
|
||||
import React, { useEffect } from 'react'
|
||||
|
||||
import type { Props } from './types.js'
|
||||
import type { HiddenInputFieldProps } from './types.js'
|
||||
|
||||
import { useField } from '../../useField/index.js'
|
||||
import { withCondition } from '../../withCondition/index.js'
|
||||
@@ -10,7 +10,7 @@ import { withCondition } from '../../withCondition/index.js'
|
||||
* This is mainly used to save a value on the form that is not visible to the user.
|
||||
* For example, this sets the `ìd` property of a block in the Blocks field.
|
||||
*/
|
||||
const HiddenInput: React.FC<Props> = (props) => {
|
||||
const HiddenInput: React.FC<HiddenInputFieldProps> = (props) => {
|
||||
const { name, disableModifyingForm = true, path: pathFromProps, value: valueFromProps } = props
|
||||
|
||||
const { path, setValue, value } = useField({
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
export type Props = {
|
||||
export type HiddenInputFieldProps = {
|
||||
disableModifyingForm?: false
|
||||
name: string
|
||||
path?: string
|
||||
|
||||
@@ -3,7 +3,7 @@ import type { ClientValidate } from 'payload/types'
|
||||
|
||||
import React, { useCallback, useEffect, useState } from 'react'
|
||||
|
||||
import type { Props } from './types.js'
|
||||
import type { JSONFieldProps } from './types.js'
|
||||
|
||||
import { CodeEditor } from '../../../elements/CodeEditor/index.js'
|
||||
import LabelComp from '../../Label/index.js'
|
||||
@@ -14,7 +14,7 @@ import './index.scss'
|
||||
|
||||
const baseClass = 'json-field'
|
||||
|
||||
const JSONField: React.FC<Props> = (props) => {
|
||||
const JSONField: React.FC<JSONFieldProps> = (props) => {
|
||||
const {
|
||||
name,
|
||||
AfterInput,
|
||||
@@ -23,6 +23,7 @@ const JSONField: React.FC<Props> = (props) => {
|
||||
Error,
|
||||
Label: LabelFromProps,
|
||||
className,
|
||||
editorOptions,
|
||||
label,
|
||||
path: pathFromProps,
|
||||
readOnly,
|
||||
@@ -34,9 +35,6 @@ const JSONField: React.FC<Props> = (props) => {
|
||||
|
||||
const Label = LabelFromProps || <LabelComp label={label} required={required} />
|
||||
|
||||
// eslint-disable-next-line react/destructuring-assignment
|
||||
const editorOptions = 'editorOptions' in props ? props.editorOptions : {}
|
||||
|
||||
const [stringValue, setStringValue] = useState<string>()
|
||||
const [jsonError, setJsonError] = useState<string>()
|
||||
const [hasLoadedValue, setHasLoadedValue] = useState(false)
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
import type { FieldBase, JSONField } from 'payload/types.js'
|
||||
|
||||
import type { FormFieldBase } from '../shared.js'
|
||||
|
||||
export type Props = FormFieldBase & {
|
||||
export type JSONFieldProps = FormFieldBase & {
|
||||
editorOptions?: JSONField['admin']['editorOptions']
|
||||
label?: FieldBase['label']
|
||||
name?: string
|
||||
path?: string
|
||||
width?: string
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ import { isNumber } from 'payload/utilities'
|
||||
import React, { useCallback, useEffect, useState } from 'react'
|
||||
|
||||
import type { Option } from '../../../elements/ReactSelect/types.js'
|
||||
import type { Props } from './types.js'
|
||||
import type { NumberFieldProps } from './types.js'
|
||||
|
||||
import ReactSelect from '../../../elements/ReactSelect/index.js'
|
||||
import { useTranslation } from '../../../providers/Translation/index.js'
|
||||
@@ -15,7 +15,7 @@ import { withCondition } from '../../withCondition/index.js'
|
||||
import { fieldBaseClass } from '../shared.js'
|
||||
import './index.scss'
|
||||
|
||||
const NumberField: React.FC<Props> = (props) => {
|
||||
const NumberField: React.FC<NumberFieldProps> = (props) => {
|
||||
const {
|
||||
name,
|
||||
AfterInput,
|
||||
@@ -24,12 +24,17 @@ const NumberField: React.FC<Props> = (props) => {
|
||||
Error,
|
||||
Label: LabelFromProps,
|
||||
className,
|
||||
hasMany = false,
|
||||
label,
|
||||
max = Infinity,
|
||||
maxRows = Infinity,
|
||||
min = -Infinity,
|
||||
onChange: onChangeFromProps,
|
||||
path: pathFromProps,
|
||||
placeholder,
|
||||
readOnly,
|
||||
required,
|
||||
step = 1,
|
||||
style,
|
||||
validate,
|
||||
width,
|
||||
@@ -37,12 +42,6 @@ const NumberField: React.FC<Props> = (props) => {
|
||||
|
||||
const Label = LabelFromProps || <LabelComp label={label} required={required} />
|
||||
|
||||
const max = 'max' in props ? props.max : Infinity
|
||||
const min = 'min' in props ? props.min : -Infinity
|
||||
const step = 'step' in props ? props.step : 1
|
||||
const hasMany = 'hasMany' in props ? props.hasMany : false
|
||||
const maxRows = 'maxRows' in props ? props.maxRows : Infinity
|
||||
|
||||
const { i18n, t } = useTranslation()
|
||||
|
||||
const memoizedValidate = useCallback(
|
||||
|
||||
@@ -1,7 +1,17 @@
|
||||
import type { FieldBase, NumberField } from 'payload/types.js'
|
||||
|
||||
import type { FormFieldBase } from '../shared.js'
|
||||
|
||||
export type Props = FormFieldBase & {
|
||||
export type NumberFieldProps = FormFieldBase & {
|
||||
hasMany?: boolean
|
||||
label?: FieldBase['label']
|
||||
max?: number
|
||||
maxRows?: number
|
||||
min?: number
|
||||
name?: string
|
||||
onChange?: (e: number) => void
|
||||
path?: string
|
||||
placeholder?: NumberField['admin']['placeholder']
|
||||
step?: number
|
||||
width?: string
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ import type { ClientValidate } from 'payload/types'
|
||||
|
||||
import React, { useCallback } from 'react'
|
||||
|
||||
import type { Props } from './types.js'
|
||||
import type { PasswordFieldProps } from './types.js'
|
||||
|
||||
import LabelComp from '../../Label/index.js'
|
||||
import { useField } from '../../useField/index.js'
|
||||
@@ -11,7 +11,7 @@ import { withCondition } from '../../withCondition/index.js'
|
||||
import { fieldBaseClass } from '../shared.js'
|
||||
import './index.scss'
|
||||
|
||||
export const Password: React.FC<Props> = (props) => {
|
||||
export const Password: React.FC<PasswordFieldProps> = (props) => {
|
||||
const {
|
||||
name,
|
||||
Error,
|
||||
|
||||
@@ -4,7 +4,7 @@ import type React from 'react'
|
||||
|
||||
import type { FormFieldBase } from '../shared.js'
|
||||
|
||||
export type Props = FormFieldBase & {
|
||||
export type PasswordFieldProps = FormFieldBase & {
|
||||
autoComplete?: string
|
||||
className?: string
|
||||
description?: Description
|
||||
|
||||
@@ -5,7 +5,7 @@ import type { ClientValidate } from 'payload/types'
|
||||
import { getTranslation } from '@payloadcms/translations'
|
||||
import React, { useCallback } from 'react'
|
||||
|
||||
import type { Props } from './types.js'
|
||||
import type { PointFieldProps } from './types.js'
|
||||
|
||||
import { useTranslation } from '../../../providers/Translation/index.js'
|
||||
import LabelComp from '../../Label/index.js'
|
||||
@@ -16,7 +16,7 @@ import './index.scss'
|
||||
|
||||
const baseClass = 'point'
|
||||
|
||||
const PointField: React.FC<Props> = (props) => {
|
||||
const PointField: React.FC<PointFieldProps> = (props) => {
|
||||
const {
|
||||
name,
|
||||
AfterInput,
|
||||
@@ -30,6 +30,7 @@ const PointField: React.FC<Props> = (props) => {
|
||||
placeholder,
|
||||
readOnly,
|
||||
required,
|
||||
step,
|
||||
style,
|
||||
validate,
|
||||
width,
|
||||
@@ -39,8 +40,6 @@ const PointField: React.FC<Props> = (props) => {
|
||||
|
||||
const { i18n } = useTranslation()
|
||||
|
||||
const step = 'step' in props ? props.step : 1
|
||||
|
||||
const memoizedValidate: ClientValidate = useCallback(
|
||||
(value, options) => {
|
||||
if (typeof validate === 'function') {
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
import type { FieldBase } from 'payload/types.js'
|
||||
|
||||
import type { FormFieldBase } from '../shared.js'
|
||||
|
||||
export type Props = FormFieldBase & {
|
||||
export type PointFieldProps = FormFieldBase & {
|
||||
label?: FieldBase['label']
|
||||
name?: string
|
||||
path?: string
|
||||
placeholder?: string
|
||||
step?: number
|
||||
width?: string
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
import { optionIsObject } from 'payload/types'
|
||||
import React, { useCallback } from 'react'
|
||||
|
||||
import type { Props } from './types.js'
|
||||
import type { RadioFieldProps } from './types.js'
|
||||
|
||||
import { useForm } from '../../Form/context.js'
|
||||
import LabelComp from '../../Label/index.js'
|
||||
@@ -16,7 +16,7 @@ import './index.scss'
|
||||
|
||||
const baseClass = 'radio-group'
|
||||
|
||||
const RadioGroup: React.FC<Props> = (props) => {
|
||||
const RadioGroup: React.FC<RadioFieldProps> = (props) => {
|
||||
const {
|
||||
name,
|
||||
Description,
|
||||
@@ -24,7 +24,9 @@ const RadioGroup: React.FC<Props> = (props) => {
|
||||
Label: LabelFromProps,
|
||||
className,
|
||||
label,
|
||||
layout = 'horizontal',
|
||||
onChange: onChangeFromProps,
|
||||
options = [],
|
||||
path: pathFromProps,
|
||||
readOnly,
|
||||
required,
|
||||
@@ -38,10 +40,6 @@ const RadioGroup: React.FC<Props> = (props) => {
|
||||
|
||||
const Label = LabelFromProps || <LabelComp label={label} required={required} />
|
||||
|
||||
const options = 'options' in props ? props.options : []
|
||||
|
||||
const layout = 'layout' in props ? props.layout : 'horizontal'
|
||||
|
||||
const memoizedValidate = useCallback(
|
||||
(value, validationOptions) => {
|
||||
if (typeof validate === 'function')
|
||||
|
||||
@@ -1,10 +1,16 @@
|
||||
import type { FieldBase, Option } from 'payload/types.js'
|
||||
|
||||
import type { FormFieldBase } from '../shared.js'
|
||||
|
||||
export type Props = FormFieldBase & {
|
||||
export type RadioFieldProps = FormFieldBase & {
|
||||
label?: FieldBase['label']
|
||||
layout?: 'horizontal' | 'vertical'
|
||||
name?: string
|
||||
onChange?: OnChange
|
||||
options?: Option[]
|
||||
path?: string
|
||||
value?: string
|
||||
width?: string
|
||||
}
|
||||
|
||||
export type OnChange<T = string> = (value: T) => void
|
||||
|
||||
@@ -7,7 +7,7 @@ import qs from 'qs'
|
||||
import React, { useCallback, useEffect, useReducer, useRef, useState } from 'react'
|
||||
|
||||
import type { DocumentDrawerProps } from '../../../elements/DocumentDrawer/types.js'
|
||||
import type { GetResults, Option, Props, Value } from './types.js'
|
||||
import type { GetResults, Option, RelationshipFieldProps, Value } from './types.js'
|
||||
|
||||
import ReactSelect from '../../../elements/ReactSelect/index.js'
|
||||
import { useDebouncedCallback } from '../../../hooks/useDebouncedCallback.js'
|
||||
@@ -31,7 +31,7 @@ const maxResultsPerRequest = 10
|
||||
|
||||
const baseClass = 'relationship'
|
||||
|
||||
const Relationship: React.FC<Props> = (props) => {
|
||||
const Relationship: React.FC<RelationshipFieldProps> = (props) => {
|
||||
const {
|
||||
name,
|
||||
Description,
|
||||
|
||||
@@ -1,11 +1,17 @@
|
||||
import type { I18n } from '@payloadcms/translations'
|
||||
import type { SanitizedCollectionConfig } from 'payload/types'
|
||||
import type { RelationshipField, SanitizedCollectionConfig } from 'payload/types'
|
||||
import type { SanitizedConfig } from 'payload/types'
|
||||
|
||||
import type { FormFieldBase } from '../shared.js'
|
||||
|
||||
export type Props = FormFieldBase & {
|
||||
export type RelationshipFieldProps = FormFieldBase & {
|
||||
allowCreate?: RelationshipField['admin']['allowCreate']
|
||||
hasMany?: boolean
|
||||
isSortable?: boolean
|
||||
name: string
|
||||
relationTo?: RelationshipField['relationTo']
|
||||
sortOptions?: RelationshipField['admin']['sortOptions']
|
||||
width?: string
|
||||
}
|
||||
|
||||
export type Option = {
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import type React from 'react'
|
||||
|
||||
import type { Props } from './types.js'
|
||||
import type { RichTextFieldProps } from './types.js'
|
||||
|
||||
const RichText: React.FC<Props> = (props) => {
|
||||
const RichText: React.FC<RichTextFieldProps> = (props) => {
|
||||
return null
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
import type { MappedField } from '@payloadcms/ui'
|
||||
|
||||
import type { FormFieldBase } from '../shared.js'
|
||||
|
||||
export type Props = FormFieldBase
|
||||
export type RichTextFieldProps = FormFieldBase & {
|
||||
richTextComponentMap?: Map<string, MappedField[] | React.ReactNode>
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
'use client'
|
||||
import React from 'react'
|
||||
|
||||
import type { Props } from './types.js'
|
||||
import type { RowFieldProps } from './types.js'
|
||||
|
||||
import { useFieldProps } from '../../FieldPropsProvider/index.js'
|
||||
import { RenderFields } from '../../RenderFields/index.js'
|
||||
@@ -12,7 +12,7 @@ import { RowProvider } from './provider.js'
|
||||
|
||||
const baseClass = 'row'
|
||||
|
||||
const Row: React.FC<Props> = (props) => {
|
||||
const Row: React.FC<RowFieldProps> = (props) => {
|
||||
const { className, fieldMap, forceRender = false } = props
|
||||
|
||||
const { path, readOnly, schemaPath, siblingPermissions } = useFieldProps()
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
import type { FieldMap } from '@payloadcms/ui'
|
||||
import type { FieldPermissions } from 'payload/auth'
|
||||
import type { FieldTypes } from 'payload/config'
|
||||
|
||||
import type { FormFieldBase } from '../shared.js'
|
||||
|
||||
export type Props = FormFieldBase & {
|
||||
export type RowFieldProps = FormFieldBase & {
|
||||
fieldMap: FieldMap
|
||||
fieldTypes: FieldTypes
|
||||
forceRender?: boolean
|
||||
indexPath: string
|
||||
path?: string
|
||||
permissions?: FieldPermissions
|
||||
width?: string
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ import type { ClientValidate, Option, OptionObject } from 'payload/types'
|
||||
import { getTranslation } from '@payloadcms/translations'
|
||||
import React, { useCallback, useState } from 'react'
|
||||
|
||||
import type { Props } from './types.js'
|
||||
import type { SelectFieldProps } from './types.js'
|
||||
|
||||
import ReactSelect from '../../../elements/ReactSelect/index.js'
|
||||
import { useTranslation } from '../../../providers/Translation/index.js'
|
||||
@@ -27,7 +27,7 @@ const formatOptions = (options: Option[]): OptionObject[] =>
|
||||
} as OptionObject
|
||||
})
|
||||
|
||||
export const Select: React.FC<Props> = (props) => {
|
||||
export const Select: React.FC<SelectFieldProps> = (props) => {
|
||||
const {
|
||||
name,
|
||||
AfterInput,
|
||||
|
||||
@@ -1,8 +1,16 @@
|
||||
import type { FieldBase, Option } from 'payload/types.js'
|
||||
|
||||
import type { FormFieldBase } from '../shared.js'
|
||||
|
||||
export type Props = FormFieldBase & {
|
||||
export type SelectFieldProps = FormFieldBase & {
|
||||
hasMany?: boolean
|
||||
isClearable?: boolean
|
||||
isSortable?: boolean
|
||||
label?: FieldBase['label']
|
||||
name?: string
|
||||
onChange?: (e: string) => void
|
||||
options?: Option[]
|
||||
path?: string
|
||||
value?: string
|
||||
width?: string
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ export const TabComponent: React.FC<TabProps> = ({ isActive, parentPath, setIsAc
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<WatchChildErrors fieldMap={tab.subfields} path={path} setErrorCount={setErrorCount} />
|
||||
<WatchChildErrors fieldMap={tab.fieldMap} path={path} setErrorCount={setErrorCount} />
|
||||
<button
|
||||
className={[
|
||||
baseClass,
|
||||
|
||||
@@ -5,7 +5,7 @@ import { getTranslation } from '@payloadcms/translations'
|
||||
import { toKebabCase } from 'payload/utilities'
|
||||
import React, { useCallback, useEffect, useState } from 'react'
|
||||
|
||||
import type { Props } from './types.js'
|
||||
import type { TabsFieldProps } from './types.js'
|
||||
|
||||
import { useCollapsible } from '../../../elements/Collapsible/provider.js'
|
||||
import { useDocumentInfo } from '../../../providers/DocumentInfo/index.js'
|
||||
@@ -21,7 +21,7 @@ import { TabsProvider } from './provider.js'
|
||||
|
||||
const baseClass = 'tabs-field'
|
||||
|
||||
const TabsField: React.FC<Props> = (props) => {
|
||||
const TabsField: React.FC<TabsFieldProps> = (props) => {
|
||||
const {
|
||||
name,
|
||||
Description,
|
||||
@@ -130,7 +130,7 @@ const TabsField: React.FC<Props> = (props) => {
|
||||
>
|
||||
{Description}
|
||||
<RenderFields
|
||||
fieldMap={activeTabConfig.subfields}
|
||||
fieldMap={activeTabConfig.fieldMap}
|
||||
forceRender={forceRender}
|
||||
key={
|
||||
activeTabConfig.label
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
import type { FieldPermissions } from 'payload/auth'
|
||||
|
||||
import type { MappedTab } from '../../../utilities/buildComponentMap/types.js'
|
||||
import type { FieldMap, MappedTab } from '../../../utilities/buildComponentMap/types.js'
|
||||
import type { FormFieldBase } from '../shared.js'
|
||||
|
||||
export type Props = FormFieldBase & {
|
||||
export type TabsFieldProps = FormFieldBase & {
|
||||
fieldMap: FieldMap
|
||||
forceRender?: boolean
|
||||
indexPath: string
|
||||
name?: string
|
||||
path?: string
|
||||
permissions: FieldPermissions
|
||||
tabs?: MappedTab[]
|
||||
width?: string
|
||||
}
|
||||
|
||||
@@ -2,16 +2,18 @@
|
||||
import type { ChangeEvent } from 'react'
|
||||
|
||||
import { getTranslation } from '@payloadcms/translations'
|
||||
import { TextField } from 'payload/types.js'
|
||||
import React from 'react'
|
||||
|
||||
import type { Option } from '../../../elements/ReactSelect/types.js'
|
||||
import type { TextareaFieldProps } from '../Textarea/types.js'
|
||||
|
||||
import ReactSelect from '../../../elements/ReactSelect/index.js'
|
||||
import { useTranslation } from '../../../providers/Translation/index.js'
|
||||
import { type FormFieldBase, fieldBaseClass } from '../shared.js'
|
||||
import { fieldBaseClass } from '../shared.js'
|
||||
import './index.scss'
|
||||
|
||||
export type TextInputProps = Omit<FormFieldBase, 'type'> & {
|
||||
export type TextInputProps = Omit<TextareaFieldProps, 'type'> & {
|
||||
hasMany?: boolean
|
||||
inputRef?: React.MutableRefObject<HTMLInputElement>
|
||||
maxRows?: number
|
||||
|
||||
@@ -4,7 +4,7 @@ import type { ClientValidate } from 'payload/types'
|
||||
import React, { useCallback, useEffect, useState } from 'react'
|
||||
|
||||
import type { Option } from '../../../elements/ReactSelect/types.js'
|
||||
import type { Props } from './types.js'
|
||||
import type { TextFieldProps } from './types.js'
|
||||
|
||||
import { useConfig } from '../../../providers/Config/index.js'
|
||||
import { useLocale } from '../../../providers/Locale/index.js'
|
||||
@@ -15,7 +15,7 @@ import { isFieldRTL } from '../shared.js'
|
||||
import { TextInput } from './Input.js'
|
||||
import './index.scss'
|
||||
|
||||
const Text: React.FC<Props> = (props) => {
|
||||
const Text: React.FC<TextFieldProps> = (props) => {
|
||||
const {
|
||||
name,
|
||||
AfterInput,
|
||||
|
||||
@@ -1,11 +1,18 @@
|
||||
import type { FieldBase, TextField } from 'payload/types.js'
|
||||
|
||||
import type { FormFieldBase } from '../shared.js'
|
||||
|
||||
export type Props = FormFieldBase & {
|
||||
export type TextFieldProps = FormFieldBase & {
|
||||
hasMany?: boolean
|
||||
inputRef?: React.MutableRefObject<HTMLInputElement>
|
||||
label?: FieldBase['label']
|
||||
maxLength?: number
|
||||
maxRows?: number
|
||||
minLength?: number
|
||||
minRows?: number
|
||||
name?: string
|
||||
onKeyDown?: React.KeyboardEventHandler<HTMLInputElement>
|
||||
path?: string
|
||||
placeholder?: TextField['admin']['placeholder']
|
||||
width?: string
|
||||
}
|
||||
|
||||
@@ -2,11 +2,13 @@
|
||||
import { getTranslation } from '@payloadcms/translations'
|
||||
import React, { type ChangeEvent } from 'react'
|
||||
|
||||
import type { TextareaFieldProps } from './types.js'
|
||||
|
||||
import { useTranslation } from '../../../providers/Translation/index.js'
|
||||
import { type FormFieldBase, fieldBaseClass } from '../shared.js'
|
||||
import { fieldBaseClass } from '../shared.js'
|
||||
import './index.scss'
|
||||
|
||||
export type TextAreaInputProps = FormFieldBase & {
|
||||
export type TextAreaInputProps = TextareaFieldProps & {
|
||||
onChange?: (e: ChangeEvent<HTMLTextAreaElement>) => void
|
||||
rows?: number
|
||||
showError?: boolean
|
||||
|
||||
@@ -5,7 +5,7 @@ import type { ClientValidate } from 'payload/types'
|
||||
import { getTranslation } from '@payloadcms/translations'
|
||||
import React, { useCallback } from 'react'
|
||||
|
||||
import type { Props } from './types.js'
|
||||
import type { TextareaFieldProps } from './types.js'
|
||||
|
||||
import { useConfig } from '../../../providers/Config/index.js'
|
||||
import { useTranslation } from '../../../providers/Translation/index.js'
|
||||
@@ -16,7 +16,7 @@ import { isFieldRTL } from '../shared.js'
|
||||
import { TextareaInput } from './Input.js'
|
||||
import './index.scss'
|
||||
|
||||
const Textarea: React.FC<Props> = (props) => {
|
||||
const Textarea: React.FC<TextareaFieldProps> = (props) => {
|
||||
const {
|
||||
name,
|
||||
AfterInput,
|
||||
|
||||
@@ -1,6 +1,14 @@
|
||||
import type { FieldBase, TextareaField } from 'payload/types.js'
|
||||
|
||||
import type { FormFieldBase } from '../shared.js'
|
||||
|
||||
export type Props = FormFieldBase & {
|
||||
export type TextareaFieldProps = FormFieldBase & {
|
||||
label?: FieldBase['label']
|
||||
maxLength?: number
|
||||
minLength?: number
|
||||
name?: string
|
||||
path?: string
|
||||
placeholder?: TextareaField['admin']['placeholder']
|
||||
rows?: number
|
||||
width?: string
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import React, { useCallback, useEffect, useState } from 'react'
|
||||
|
||||
import type { DocumentDrawerProps } from '../../../elements/DocumentDrawer/types.js'
|
||||
import type { ListDrawerProps } from '../../../elements/ListDrawer/types.js'
|
||||
import type { UploadFieldProps } from './types.js'
|
||||
|
||||
import { Button } from '../../../elements/Button/index.js'
|
||||
import { useDocumentDrawer } from '../../../elements/DocumentDrawer/index.js'
|
||||
@@ -14,12 +15,12 @@ import FileDetails from '../../../elements/FileDetails/index.js'
|
||||
import { useListDrawer } from '../../../elements/ListDrawer/index.js'
|
||||
import { useTranslation } from '../../../providers/Translation/index.js'
|
||||
import LabelComp from '../../Label/index.js'
|
||||
import { type FormFieldBase, fieldBaseClass } from '../shared.js'
|
||||
import { fieldBaseClass } from '../shared.js'
|
||||
import './index.scss'
|
||||
|
||||
const baseClass = 'upload'
|
||||
|
||||
export type UploadInputProps = FormFieldBase & {
|
||||
export type UploadInputProps = Omit<UploadFieldProps, 'filterOptions'> & {
|
||||
api?: string
|
||||
collection?: SanitizedCollectionConfig
|
||||
filterOptions?: FilterOptionsResult
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
'use client'
|
||||
import React, { useCallback } from 'react'
|
||||
|
||||
import type { Props } from './types.js'
|
||||
import type { UploadFieldProps } from './types.js'
|
||||
|
||||
import { useConfig } from '../../../providers/Config/index.js'
|
||||
import LabelComp from '../../Label/index.js'
|
||||
@@ -9,7 +9,7 @@ import { useField } from '../../useField/index.js'
|
||||
import { UploadInput } from './Input.js'
|
||||
import './index.scss'
|
||||
|
||||
const Upload: React.FC<Props> = (props) => {
|
||||
const Upload: React.FC<UploadFieldProps> = (props) => {
|
||||
const {
|
||||
Description,
|
||||
Error,
|
||||
|
||||
@@ -2,9 +2,11 @@ import type { UploadField } from 'payload/types'
|
||||
|
||||
import type { FormFieldBase } from '../shared.js'
|
||||
|
||||
export type Props = FormFieldBase & {
|
||||
export type UploadFieldProps = FormFieldBase & {
|
||||
filterOptions?: UploadField['filterOptions']
|
||||
label?: UploadField['label']
|
||||
name?: string
|
||||
path?: string
|
||||
relationTo?: UploadField['relationTo']
|
||||
width?: string
|
||||
}
|
||||
|
||||
@@ -1,26 +1,6 @@
|
||||
import type { User } from 'payload/auth'
|
||||
import type { Locale, SanitizedLocalizationConfig } from 'payload/config'
|
||||
import type {
|
||||
ArrayField,
|
||||
BlockField,
|
||||
CodeField,
|
||||
DateField,
|
||||
DocumentPreferences,
|
||||
FormState,
|
||||
JSONField,
|
||||
RelationshipField,
|
||||
RowLabel,
|
||||
UploadField,
|
||||
Validate,
|
||||
} from 'payload/types'
|
||||
import type { Option } from 'payload/types'
|
||||
|
||||
import type {
|
||||
FieldMap,
|
||||
MappedField,
|
||||
MappedTab,
|
||||
ReducedBlock,
|
||||
} from '../../utilities/buildComponentMap/types.js'
|
||||
import type { DocumentPreferences, FormState, Validate } from 'payload/types'
|
||||
|
||||
export const fieldBaseClass = 'field-type'
|
||||
|
||||
@@ -30,99 +10,19 @@ export type FormFieldBase = {
|
||||
Description?: React.ReactNode
|
||||
Error?: React.ReactNode
|
||||
Label?: React.ReactNode
|
||||
RowLabel?: React.ReactNode
|
||||
className?: string
|
||||
disabled?: boolean
|
||||
docPreferences?: DocumentPreferences
|
||||
fieldMap?: FieldMap
|
||||
initialSubfieldState?: FormState
|
||||
label?: string
|
||||
locale?: Locale
|
||||
localized?: boolean
|
||||
maxLength?: number
|
||||
minLength?: number
|
||||
path?: string
|
||||
placeholder?: Record<string, string> | string
|
||||
readOnly?: boolean
|
||||
required?: boolean
|
||||
rtl?: boolean
|
||||
style?: React.CSSProperties
|
||||
user?: User
|
||||
validate?: Validate
|
||||
width?: string
|
||||
} & (
|
||||
| {
|
||||
// For `array` fields
|
||||
label?: RowLabel
|
||||
labels?: ArrayField['labels']
|
||||
maxRows?: ArrayField['maxRows']
|
||||
minRows?: ArrayField['minRows']
|
||||
}
|
||||
| {
|
||||
// For `blocks` fields
|
||||
blocks?: ReducedBlock[]
|
||||
labels?: BlockField['labels']
|
||||
maxRows?: BlockField['maxRows']
|
||||
minRows?: BlockField['minRows']
|
||||
slug?: string
|
||||
}
|
||||
| {
|
||||
// For `code` fields
|
||||
editorOptions?: CodeField['admin']['editorOptions']
|
||||
language?: CodeField['admin']['language']
|
||||
}
|
||||
| {
|
||||
// For `collapsible` fields
|
||||
initCollapsed?: boolean
|
||||
}
|
||||
| {
|
||||
// For `date` fields
|
||||
date?: DateField['admin']['date']
|
||||
}
|
||||
| {
|
||||
// For `json` fields
|
||||
editorOptions?: JSONField['admin']['editorOptions']
|
||||
}
|
||||
| {
|
||||
// For `number` fields
|
||||
hasMany?: boolean
|
||||
max?: number
|
||||
maxRows?: number
|
||||
min?: number
|
||||
step?: number
|
||||
}
|
||||
| {
|
||||
// For `radio` fields
|
||||
layout?: 'horizontal' | 'vertical'
|
||||
options?: Option[]
|
||||
}
|
||||
| {
|
||||
// For `relationship` fields
|
||||
allowCreate?: RelationshipField['admin']['allowCreate']
|
||||
relationTo?: RelationshipField['relationTo']
|
||||
sortOptions?: RelationshipField['admin']['sortOptions']
|
||||
}
|
||||
| {
|
||||
// For `richText` fields
|
||||
richTextComponentMap?: Map<string, MappedField[] | React.ReactNode>
|
||||
}
|
||||
| {
|
||||
// For `select` fields
|
||||
isClearable?: boolean
|
||||
isSortable?: boolean
|
||||
}
|
||||
| {
|
||||
// For `textarea` fields
|
||||
rows?: number
|
||||
}
|
||||
| {
|
||||
// For `upload` fields
|
||||
relationTo?: UploadField['relationTo']
|
||||
}
|
||||
| {
|
||||
tabs?: MappedTab[]
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether a field should be displayed as right-to-left (RTL) based on its configuration, payload's localization configuration and the adming user's currently enabled locale.
|
||||
|
||||
@@ -40,7 +40,7 @@ export const ComponentMapProvider: React.FC<{
|
||||
}
|
||||
|
||||
// TODO: better lookup for nested fields, etc.
|
||||
return fieldMap.find((field) => field.name === path)
|
||||
return fieldMap.find((field) => 'name' in field && field.name === path)
|
||||
},
|
||||
[componentMap],
|
||||
)
|
||||
|
||||
@@ -5,8 +5,32 @@ import { isPlainObject } from 'payload/utilities'
|
||||
import React, { Fragment } from 'react'
|
||||
|
||||
import type { Props as FieldDescription } from '../../forms/FieldDescription/types.js'
|
||||
import type { ArrayFieldProps } from '../../forms/fields/Array/types.js'
|
||||
import type { BlocksFieldProps } from '../../forms/fields/Blocks/types.js'
|
||||
import type { CheckboxFieldProps } from '../../forms/fields/Checkbox/types.js'
|
||||
import type { CodeFieldProps } from '../../forms/fields/Code/types.js'
|
||||
import type { CollapsibleFieldProps } from '../../forms/fields/Collapsible/types.js'
|
||||
import type { DateFieldProps } from '../../forms/fields/DateTime/types.js'
|
||||
import type { EmailFieldProps } from '../../forms/fields/Email/types.js'
|
||||
import type { GroupFieldProps } from '../../forms/fields/Group/types.js'
|
||||
import type { JSONFieldProps } from '../../forms/fields/JSON/types.js'
|
||||
import type { NumberFieldProps } from '../../forms/fields/Number/types.js'
|
||||
import type { PointFieldProps } from '../../forms/fields/Point/types.js'
|
||||
import type { RelationshipFieldProps } from '../../forms/fields/Relationship/types.js'
|
||||
import type { RowFieldProps } from '../../forms/fields/Row/types.js'
|
||||
import type { SelectFieldProps } from '../../forms/fields/Select/types.js'
|
||||
import type { TabsFieldProps } from '../../forms/fields/Tabs/types.js'
|
||||
import type { TextFieldProps } from '../../forms/fields/Text/types.js'
|
||||
import type { TextareaFieldProps } from '../../forms/fields/Textarea/types.js'
|
||||
import type { UploadFieldProps } from '../../forms/fields/Upload/types.js'
|
||||
import type { FormFieldBase } from '../../forms/fields/shared.js'
|
||||
import type { FieldMap, MappedField, MappedTab, ReducedBlock } from './types.js'
|
||||
import type {
|
||||
FieldComponentProps,
|
||||
FieldMap,
|
||||
MappedField,
|
||||
MappedTab,
|
||||
ReducedBlock,
|
||||
} from './types.js'
|
||||
|
||||
import { RenderCustomComponent } from '../../elements/RenderCustomComponent/index.js'
|
||||
import { SortColumn } from '../../elements/SortColumn/index.js'
|
||||
@@ -83,56 +107,95 @@ export const mapFields = (args: {
|
||||
readOnly: readOnlyOverride,
|
||||
})
|
||||
|
||||
// `tabs` fields require a field map of each of its tab's nested fields
|
||||
const tabs =
|
||||
'tabs' in field &&
|
||||
field.tabs &&
|
||||
Array.isArray(field.tabs) &&
|
||||
field.tabs.map((tab) => {
|
||||
const tabFieldMap = mapFields({
|
||||
DefaultCell,
|
||||
config,
|
||||
fieldSchema: tab.fields,
|
||||
filter,
|
||||
parentPath: path,
|
||||
readOnly: readOnlyOverride,
|
||||
})
|
||||
const AfterInput = 'admin' in field &&
|
||||
'components' in field.admin &&
|
||||
'afterInput' in field.admin.components &&
|
||||
Array.isArray(field.admin?.components?.afterInput) && (
|
||||
<Fragment>
|
||||
{field.admin.components.afterInput.map((Component, i) => (
|
||||
<Component key={i} />
|
||||
))}
|
||||
</Fragment>
|
||||
)
|
||||
|
||||
const reducedTab: MappedTab = {
|
||||
name: 'name' in tab ? tab.name : undefined,
|
||||
label: tab.label,
|
||||
subfields: tabFieldMap,
|
||||
const BeforeInput = 'admin' in field &&
|
||||
field.admin?.components &&
|
||||
'beforeInput' in field.admin.components &&
|
||||
Array.isArray(field.admin.components.beforeInput) && (
|
||||
<Fragment>
|
||||
{field.admin.components.beforeInput.map((Component, i) => (
|
||||
<Component key={i} />
|
||||
))}
|
||||
</Fragment>
|
||||
)
|
||||
|
||||
const Description = (
|
||||
<RenderCustomComponent
|
||||
CustomComponent={
|
||||
field.admin &&
|
||||
'description' in field.admin &&
|
||||
field.admin.description &&
|
||||
typeof field.admin.description === 'function' &&
|
||||
(field.admin.description as React.FC<any>)
|
||||
}
|
||||
DefaultComponent={DefaultDescription}
|
||||
componentProps={descriptionProps}
|
||||
/>
|
||||
)
|
||||
|
||||
const Error = (
|
||||
<RenderCustomComponent
|
||||
CustomComponent={
|
||||
'admin' in field &&
|
||||
field.admin.components &&
|
||||
'Error' in field.admin.components &&
|
||||
field.admin?.components?.Error
|
||||
}
|
||||
DefaultComponent={DefaultError}
|
||||
componentProps={{ path }}
|
||||
/>
|
||||
)
|
||||
|
||||
const Label = (
|
||||
<RenderCustomComponent
|
||||
CustomComponent={
|
||||
'admin' in field &&
|
||||
field.admin?.components &&
|
||||
'Label' in field.admin.components &&
|
||||
field.admin?.components?.Label
|
||||
}
|
||||
DefaultComponent={DefaultLabel}
|
||||
componentProps={labelProps}
|
||||
/>
|
||||
)
|
||||
|
||||
const baseFieldProps: FormFieldBase = {
|
||||
AfterInput,
|
||||
BeforeInput,
|
||||
Description,
|
||||
Error,
|
||||
Label,
|
||||
disabled: 'admin' in field && 'disabled' in field.admin ? field.admin?.disabled : false,
|
||||
path,
|
||||
required: 'required' in field ? field.required : undefined,
|
||||
}
|
||||
|
||||
return reducedTab
|
||||
})
|
||||
let fieldComponentProps: FieldComponentProps
|
||||
|
||||
// `blocks` fields require a field map of each of its block's nested fields
|
||||
const blocks =
|
||||
'blocks' in field &&
|
||||
field.blocks &&
|
||||
Array.isArray(field.blocks) &&
|
||||
field.blocks.map((block) => {
|
||||
const blockFieldMap = mapFields({
|
||||
DefaultCell,
|
||||
config,
|
||||
fieldSchema: block.fields,
|
||||
filter,
|
||||
parentPath: `${path}.${block.slug}`,
|
||||
readOnly: readOnlyOverride,
|
||||
})
|
||||
|
||||
const reducedBlock: ReducedBlock = {
|
||||
slug: block.slug,
|
||||
imageAltText: block.imageAltText,
|
||||
imageURL: block.imageURL,
|
||||
labels: block.labels,
|
||||
subfields: blockFieldMap,
|
||||
const cellComponentProps: CellProps = {
|
||||
name: 'name' in field ? field.name : undefined,
|
||||
fieldType: field.type,
|
||||
isFieldAffectingData,
|
||||
label:
|
||||
'label' in field && field.label && typeof field.label !== 'function'
|
||||
? field.label
|
||||
: undefined,
|
||||
labels: 'labels' in field ? field.labels : undefined,
|
||||
options: 'options' in field ? field.options : undefined,
|
||||
}
|
||||
|
||||
return reducedBlock
|
||||
})
|
||||
|
||||
switch (field.type) {
|
||||
case 'array': {
|
||||
let RowLabel: React.ReactNode
|
||||
|
||||
if (
|
||||
@@ -146,125 +209,403 @@ export const mapFields = (args: {
|
||||
RowLabel = <CustomRowLabel />
|
||||
}
|
||||
|
||||
// TODO: these types can get cleaned up
|
||||
// i.e. not all fields have `maxRows` or `min` or `max`
|
||||
// but this is labor intensive and requires consuming components to be updated
|
||||
const fieldComponentProps: FormFieldBase = {
|
||||
AfterInput: 'admin' in field &&
|
||||
'components' in field.admin &&
|
||||
'afterInput' in field.admin.components &&
|
||||
Array.isArray(field.admin?.components?.afterInput) && (
|
||||
<Fragment>
|
||||
{field.admin.components.afterInput.map((Component, i) => (
|
||||
<Component key={i} />
|
||||
))}
|
||||
</Fragment>
|
||||
),
|
||||
BeforeInput: 'admin' in field &&
|
||||
field.admin?.components &&
|
||||
'beforeInput' in field.admin.components &&
|
||||
Array.isArray(field.admin.components.beforeInput) && (
|
||||
<Fragment>
|
||||
{field.admin.components.beforeInput.map((Component, i) => (
|
||||
<Component key={i} />
|
||||
))}
|
||||
</Fragment>
|
||||
),
|
||||
Description: (
|
||||
<RenderCustomComponent
|
||||
CustomComponent={
|
||||
field.admin &&
|
||||
'description' in field.admin &&
|
||||
field.admin.description &&
|
||||
typeof field.admin.description === 'function' &&
|
||||
(field.admin.description as React.FC<any>)
|
||||
}
|
||||
DefaultComponent={DefaultDescription}
|
||||
componentProps={descriptionProps}
|
||||
/>
|
||||
),
|
||||
Error: (
|
||||
<RenderCustomComponent
|
||||
CustomComponent={
|
||||
'admin' in field &&
|
||||
field.admin.components &&
|
||||
'Error' in field.admin.components &&
|
||||
field.admin?.components?.Error
|
||||
}
|
||||
DefaultComponent={DefaultError}
|
||||
componentProps={{ path }}
|
||||
/>
|
||||
),
|
||||
Label: (
|
||||
<RenderCustomComponent
|
||||
CustomComponent={
|
||||
'admin' in field &&
|
||||
field.admin?.components &&
|
||||
'Label' in field.admin.components &&
|
||||
field.admin?.components?.Label
|
||||
}
|
||||
DefaultComponent={DefaultLabel}
|
||||
componentProps={labelProps}
|
||||
/>
|
||||
),
|
||||
const arrayFieldProps: Omit<ArrayFieldProps, 'indexPath' | 'permissions'> = {
|
||||
...baseFieldProps,
|
||||
name: field.name,
|
||||
RowLabel,
|
||||
blocks,
|
||||
className:
|
||||
'admin' in field && 'className' in field.admin ? field?.admin?.className : undefined,
|
||||
date: 'admin' in field && 'date' in field.admin ? field.admin.date : undefined,
|
||||
disabled: field?.admin && 'disabled' in field.admin ? field.admin?.disabled : false,
|
||||
className: field.admin?.className,
|
||||
disabled: field.admin?.disabled,
|
||||
fieldMap: nestedFieldMap,
|
||||
hasMany: 'hasMany' in field ? field.hasMany : undefined,
|
||||
label: 'label' in field && typeof field.label === 'string' ? field.label : undefined,
|
||||
max: 'max' in field ? field.max : undefined,
|
||||
maxRows: 'maxRows' in field ? field.maxRows : undefined,
|
||||
min: 'min' in field ? field.min : undefined,
|
||||
options: 'options' in field ? field.options : undefined,
|
||||
placeholder:
|
||||
'admin' in field && 'placeholder' in field.admin
|
||||
? field?.admin?.placeholder
|
||||
: undefined,
|
||||
readOnly:
|
||||
'admin' in field && 'readOnly' in field.admin ? field.admin.readOnly : undefined,
|
||||
relationTo: 'relationTo' in field ? field.relationTo : undefined,
|
||||
richTextComponentMap: undefined,
|
||||
step: 'admin' in field && 'step' in field.admin ? field.admin.step : undefined,
|
||||
style: 'admin' in field && 'style' in field.admin ? field?.admin?.style : undefined,
|
||||
tabs,
|
||||
width: 'admin' in field && 'width' in field.admin ? field?.admin?.width : undefined,
|
||||
label: field?.label || undefined,
|
||||
labels: field.labels,
|
||||
maxRows: field.maxRows,
|
||||
minRows: field.minRows,
|
||||
readOnly: field.admin?.readOnly,
|
||||
required: field.required,
|
||||
style: field.admin?.style,
|
||||
width: field.admin?.width,
|
||||
}
|
||||
|
||||
if (
|
||||
field.type === 'collapsible' &&
|
||||
typeof field.label === 'object' &&
|
||||
!isPlainObject(field.label)
|
||||
) {
|
||||
const CollapsibleLabel = field.label as unknown as React.ComponentType
|
||||
fieldComponentProps.Label = <CollapsibleLabel />
|
||||
fieldComponentProps = arrayFieldProps
|
||||
break
|
||||
}
|
||||
case 'blocks': {
|
||||
const blocks = field.blocks.map((block) => {
|
||||
const blockFieldMap = mapFields({
|
||||
DefaultCell,
|
||||
config,
|
||||
fieldSchema: block.fields,
|
||||
filter,
|
||||
parentPath: `${path}.${block.slug}`,
|
||||
readOnly: readOnlyOverride,
|
||||
})
|
||||
|
||||
const reducedBlock: ReducedBlock = {
|
||||
slug: block.slug,
|
||||
fieldMap: blockFieldMap,
|
||||
imageAltText: block.imageAltText,
|
||||
imageURL: block.imageURL,
|
||||
labels: block.labels,
|
||||
}
|
||||
|
||||
return reducedBlock
|
||||
})
|
||||
|
||||
const blocksField: Omit<BlocksFieldProps, 'indexPath' | 'permissions'> = {
|
||||
...baseFieldProps,
|
||||
name: field.name,
|
||||
blocks,
|
||||
className: field.admin?.className,
|
||||
disabled: field.admin?.disabled,
|
||||
fieldMap: nestedFieldMap,
|
||||
label: field?.label || undefined,
|
||||
labels: field.labels,
|
||||
maxRows: field.maxRows,
|
||||
minRows: field.minRows,
|
||||
readOnly: field.admin?.readOnly,
|
||||
required: field.required,
|
||||
style: field.admin?.style,
|
||||
width: field.admin?.width,
|
||||
}
|
||||
|
||||
fieldComponentProps = blocksField
|
||||
|
||||
cellComponentProps.blocks = field.blocks.map((b) => ({
|
||||
slug: b.slug,
|
||||
labels: b.labels,
|
||||
}))
|
||||
|
||||
break
|
||||
}
|
||||
case 'checkbox': {
|
||||
const checkboxField: CheckboxFieldProps = {
|
||||
...baseFieldProps,
|
||||
name: field.name,
|
||||
className: field.admin?.className,
|
||||
disabled: field.admin?.disabled,
|
||||
label: field.label,
|
||||
readOnly: field.admin?.readOnly,
|
||||
required: field.required,
|
||||
style: field.admin?.style,
|
||||
width: field.admin?.width,
|
||||
}
|
||||
|
||||
fieldComponentProps = checkboxField
|
||||
break
|
||||
}
|
||||
case 'code': {
|
||||
const codeField: CodeFieldProps = {
|
||||
...baseFieldProps,
|
||||
name: field.name,
|
||||
className: field.admin?.className,
|
||||
disabled: field.admin?.disabled,
|
||||
editorOptions: field.admin?.editorOptions,
|
||||
label: field.label,
|
||||
language: field.admin?.language,
|
||||
readOnly: field.admin?.readOnly,
|
||||
required: field.required,
|
||||
style: field.admin?.style,
|
||||
width: field.admin?.width,
|
||||
}
|
||||
|
||||
fieldComponentProps = codeField
|
||||
break
|
||||
}
|
||||
case 'collapsible': {
|
||||
let CollapsibleLabel: React.ReactNode
|
||||
|
||||
if (typeof field.label === 'object' && !isPlainObject(field.label)) {
|
||||
const LabelToRender = field.label as unknown as React.ComponentType
|
||||
CollapsibleLabel = <LabelToRender />
|
||||
}
|
||||
|
||||
const collapsibleField: Omit<CollapsibleFieldProps, 'indexPath' | 'permissions'> = {
|
||||
...baseFieldProps,
|
||||
Label: CollapsibleLabel,
|
||||
className: field.admin?.className,
|
||||
disabled: field.admin?.disabled,
|
||||
fieldMap: nestedFieldMap,
|
||||
fieldTypes,
|
||||
readOnly: field.admin?.readOnly,
|
||||
required: field.required,
|
||||
style: field.admin?.style,
|
||||
width: field.admin?.width,
|
||||
}
|
||||
|
||||
fieldComponentProps = collapsibleField
|
||||
break
|
||||
}
|
||||
case 'date': {
|
||||
const dateField: DateFieldProps = {
|
||||
...baseFieldProps,
|
||||
name: field.name,
|
||||
className: field.admin?.className,
|
||||
date: field.admin?.date,
|
||||
disabled: field.admin?.disabled,
|
||||
label: field.label,
|
||||
placeholder: field.admin?.placeholder,
|
||||
readOnly: field.admin?.readOnly,
|
||||
required: field.required,
|
||||
style: field.admin?.style,
|
||||
width: field.admin?.width,
|
||||
}
|
||||
|
||||
fieldComponentProps = dateField
|
||||
cellComponentProps.dateDisplayFormat = field.admin?.date?.displayFormat
|
||||
break
|
||||
}
|
||||
case 'email': {
|
||||
const emailField: EmailFieldProps = {
|
||||
...baseFieldProps,
|
||||
name: field.name,
|
||||
className: field.admin?.className,
|
||||
disabled: field.admin?.disabled,
|
||||
placeholder: field.admin?.placeholder,
|
||||
readOnly: field.admin?.readOnly,
|
||||
required: field.required,
|
||||
style: field.admin?.style,
|
||||
width: field.admin?.width,
|
||||
}
|
||||
|
||||
fieldComponentProps = emailField
|
||||
break
|
||||
}
|
||||
case 'group': {
|
||||
const groupField: Omit<GroupFieldProps, 'indexPath' | 'permissions'> = {
|
||||
...baseFieldProps,
|
||||
name: field.name,
|
||||
className: field.admin?.className,
|
||||
disabled: field.admin?.disabled,
|
||||
fieldMap: nestedFieldMap,
|
||||
readOnly: field.admin?.readOnly,
|
||||
style: field.admin?.style,
|
||||
width: field.admin?.width,
|
||||
}
|
||||
|
||||
fieldComponentProps = groupField
|
||||
break
|
||||
}
|
||||
case 'json': {
|
||||
const jsonField: JSONFieldProps = {
|
||||
...baseFieldProps,
|
||||
name: field.name,
|
||||
className: field.admin?.className,
|
||||
disabled: field.admin?.disabled,
|
||||
editorOptions: field.admin?.editorOptions,
|
||||
readOnly: field.admin?.readOnly,
|
||||
required: field.required,
|
||||
style: field.admin?.style,
|
||||
width: field.admin?.width,
|
||||
}
|
||||
|
||||
fieldComponentProps = jsonField
|
||||
break
|
||||
}
|
||||
case 'number': {
|
||||
const numberField: NumberFieldProps = {
|
||||
...baseFieldProps,
|
||||
name: field.name,
|
||||
className: field.admin?.className,
|
||||
disabled: field.admin?.disabled,
|
||||
hasMany: field.hasMany,
|
||||
max: field.max,
|
||||
maxRows: field.maxRows,
|
||||
min: field.min,
|
||||
readOnly: field.admin?.readOnly,
|
||||
required: field.required,
|
||||
step: field.admin?.step,
|
||||
style: field.admin?.style,
|
||||
width: field.admin?.width,
|
||||
}
|
||||
|
||||
fieldComponentProps = numberField
|
||||
break
|
||||
}
|
||||
case 'point': {
|
||||
const pointField: PointFieldProps = {
|
||||
...baseFieldProps,
|
||||
name: field.name,
|
||||
className: field.admin?.className,
|
||||
disabled: field.admin?.disabled,
|
||||
readOnly: field.admin?.readOnly,
|
||||
required: field.required,
|
||||
style: field.admin?.style,
|
||||
width: field.admin?.width,
|
||||
}
|
||||
|
||||
fieldComponentProps = pointField
|
||||
break
|
||||
}
|
||||
case 'relationship': {
|
||||
const relationshipField: RelationshipFieldProps = {
|
||||
...baseFieldProps,
|
||||
name: field.name,
|
||||
allowCreate: field.admin.allowCreate,
|
||||
className: field.admin?.className,
|
||||
disabled: field.admin?.disabled,
|
||||
hasMany: field.hasMany,
|
||||
readOnly: field.admin?.readOnly,
|
||||
relationTo: field.relationTo,
|
||||
required: field.required,
|
||||
sortOptions: field.admin.sortOptions,
|
||||
style: field.admin?.style,
|
||||
width: field.admin?.width,
|
||||
}
|
||||
|
||||
fieldComponentProps = relationshipField
|
||||
break
|
||||
}
|
||||
case 'richText': {
|
||||
const richTextField = {
|
||||
...baseFieldProps,
|
||||
name: field.name,
|
||||
className: field.admin?.className,
|
||||
disabled: field.admin?.disabled,
|
||||
readOnly: field.admin?.readOnly,
|
||||
required: field.required,
|
||||
style: field.admin?.style,
|
||||
width: field.admin?.width,
|
||||
}
|
||||
|
||||
fieldComponentProps = richTextField
|
||||
break
|
||||
}
|
||||
case 'row': {
|
||||
const rowField: Omit<RowFieldProps, 'indexPath' | 'permissions'> = {
|
||||
...baseFieldProps,
|
||||
className: field.admin?.className,
|
||||
disabled: field.admin?.disabled,
|
||||
fieldMap: nestedFieldMap,
|
||||
fieldTypes,
|
||||
readOnly: field.admin?.readOnly,
|
||||
required: field.required,
|
||||
style: field.admin?.style,
|
||||
width: field.admin?.width,
|
||||
}
|
||||
|
||||
fieldComponentProps = rowField
|
||||
break
|
||||
}
|
||||
case 'tabs': {
|
||||
// `tabs` fields require a field map of each of its tab's nested fields
|
||||
const tabs = field.tabs.map((tab) => {
|
||||
const tabFieldMap = mapFields({
|
||||
DefaultCell,
|
||||
config,
|
||||
fieldSchema: tab.fields,
|
||||
filter,
|
||||
parentPath: path,
|
||||
readOnly: readOnlyOverride,
|
||||
})
|
||||
|
||||
const reducedTab: MappedTab = {
|
||||
name: 'name' in tab ? tab.name : undefined,
|
||||
fieldMap: tabFieldMap,
|
||||
label: tab.label,
|
||||
}
|
||||
|
||||
return reducedTab
|
||||
})
|
||||
|
||||
const tabsField: Omit<TabsFieldProps, 'indexPath' | 'permissions'> = {
|
||||
...baseFieldProps,
|
||||
name: 'name' in field ? (field.name as string) : undefined,
|
||||
className: field.admin?.className,
|
||||
disabled: field.admin?.disabled,
|
||||
fieldMap: nestedFieldMap,
|
||||
readOnly: field.admin?.readOnly,
|
||||
required: field.required,
|
||||
style: field.admin?.style,
|
||||
tabs,
|
||||
width: field.admin?.width,
|
||||
}
|
||||
|
||||
fieldComponentProps = tabsField
|
||||
break
|
||||
}
|
||||
case 'text': {
|
||||
const textField: TextFieldProps = {
|
||||
...baseFieldProps,
|
||||
name: field.name,
|
||||
className: field.admin?.className,
|
||||
disabled: field.admin?.disabled,
|
||||
maxLength: field.maxLength,
|
||||
minLength: field.minLength,
|
||||
placeholder: field.admin?.placeholder,
|
||||
readOnly: field.admin?.readOnly,
|
||||
required: field.required,
|
||||
style: field.admin?.style,
|
||||
width: field.admin?.width,
|
||||
}
|
||||
|
||||
fieldComponentProps = textField
|
||||
break
|
||||
}
|
||||
case 'textarea': {
|
||||
const textareaField: TextareaFieldProps = {
|
||||
...baseFieldProps,
|
||||
name: field.name,
|
||||
className: field.admin?.className,
|
||||
disabled: field.admin?.disabled,
|
||||
maxLength: field.maxLength,
|
||||
minLength: field.minLength,
|
||||
placeholder: field.admin?.placeholder,
|
||||
readOnly: field.admin?.readOnly,
|
||||
required: field.required,
|
||||
rows: field.admin?.rows,
|
||||
style: field.admin?.style,
|
||||
width: field.admin?.width,
|
||||
}
|
||||
|
||||
fieldComponentProps = textareaField
|
||||
break
|
||||
}
|
||||
case 'ui': {
|
||||
fieldComponentProps = baseFieldProps
|
||||
break
|
||||
}
|
||||
case 'upload': {
|
||||
const uploadField: UploadFieldProps = {
|
||||
...baseFieldProps,
|
||||
name: field.name,
|
||||
className: field.admin?.className,
|
||||
disabled: field.admin?.disabled,
|
||||
filterOptions: field.filterOptions,
|
||||
readOnly: field.admin?.readOnly,
|
||||
relationTo: field.relationTo,
|
||||
required: field.required,
|
||||
style: field.admin?.style,
|
||||
width: field.admin?.width,
|
||||
}
|
||||
|
||||
fieldComponentProps = uploadField
|
||||
break
|
||||
}
|
||||
case 'select': {
|
||||
const selectField: SelectFieldProps = {
|
||||
...baseFieldProps,
|
||||
name: field.name,
|
||||
className: field.admin?.className,
|
||||
disabled: field.admin?.disabled,
|
||||
hasMany: field.hasMany,
|
||||
isClearable: field.admin?.isClearable,
|
||||
options: field.options,
|
||||
readOnly: field.admin?.readOnly,
|
||||
required: field.required,
|
||||
style: field.admin?.style,
|
||||
width: field.admin?.width,
|
||||
}
|
||||
|
||||
fieldComponentProps = selectField
|
||||
break
|
||||
}
|
||||
default: {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
let Field = <FieldComponent {...fieldComponentProps} />
|
||||
|
||||
const cellComponentProps: CellProps = {
|
||||
name: 'name' in field ? field.name : undefined,
|
||||
blocks:
|
||||
'blocks' in field &&
|
||||
field.blocks.map((b) => ({
|
||||
slug: b.slug,
|
||||
labels: b.labels,
|
||||
})),
|
||||
dateDisplayFormat:
|
||||
'admin' in field && 'date' in field.admin ? field.admin.date.displayFormat : undefined,
|
||||
fieldType: field.type,
|
||||
isFieldAffectingData,
|
||||
label:
|
||||
'label' in field && field.label && typeof field.label !== 'function'
|
||||
? field.label
|
||||
: undefined,
|
||||
labels: 'labels' in field ? field.labels : undefined,
|
||||
options: 'options' in field ? field.options : undefined,
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle RichText Field Components, Cell Components, and component maps
|
||||
*/
|
||||
@@ -288,18 +629,15 @@ export const mapFields = (args: {
|
||||
}
|
||||
}
|
||||
|
||||
const reducedField: MappedField = {
|
||||
name: 'name' in field ? field.name : '',
|
||||
type: field.type,
|
||||
Cell: (
|
||||
const Cell = (
|
||||
<RenderCustomComponent
|
||||
CustomComponent={field.admin?.components?.Cell}
|
||||
DefaultComponent={DefaultCell}
|
||||
componentProps={cellComponentProps}
|
||||
/>
|
||||
),
|
||||
Field,
|
||||
Heading: (
|
||||
)
|
||||
|
||||
const Heading = (
|
||||
<SortColumn
|
||||
disable={
|
||||
('disableSort' in field && Boolean(field.disableSort)) ||
|
||||
@@ -315,22 +653,19 @@ export const mapFields = (args: {
|
||||
}
|
||||
name={'name' in field ? field.name : undefined}
|
||||
/>
|
||||
),
|
||||
blocks,
|
||||
disabled: field?.admin && 'disabled' in field.admin ? field.admin?.disabled : false,
|
||||
)
|
||||
|
||||
const reducedField: MappedField = {
|
||||
...fieldComponentProps,
|
||||
type: field.type,
|
||||
Cell,
|
||||
Field,
|
||||
Heading,
|
||||
fieldIsPresentational,
|
||||
hasMany: 'hasMany' in field ? field.hasMany : undefined,
|
||||
isFieldAffectingData,
|
||||
isSidebar: 'admin' in field && field.admin?.position === 'sidebar',
|
||||
label: 'label' in field && typeof field.label !== 'function' ? field.label : undefined,
|
||||
labels: 'labels' in field ? field.labels : undefined,
|
||||
isSidebar:
|
||||
'admin' in field && 'position' in field.admin && field.admin.position === 'sidebar',
|
||||
localized: 'localized' in field ? field.localized : false,
|
||||
options: 'options' in field ? field.options : undefined,
|
||||
readOnly:
|
||||
'admin' in field && 'readOnly' in field.admin ? field.admin.readOnly : undefined,
|
||||
relationTo: 'relationTo' in field ? field.relationTo : undefined,
|
||||
subfields: nestedFieldMap,
|
||||
tabs,
|
||||
}
|
||||
|
||||
if (FieldComponent) {
|
||||
@@ -343,7 +678,7 @@ export const mapFields = (args: {
|
||||
}, [])
|
||||
|
||||
const hasID =
|
||||
result.findIndex(({ name, isFieldAffectingData }) => isFieldAffectingData && name === 'id') > -1
|
||||
result.findIndex((f) => 'name' in f && f.isFieldAffectingData && f.name === 'id') > -1
|
||||
|
||||
if (!disableAddingID && !hasID) {
|
||||
result.push({
|
||||
@@ -354,13 +689,8 @@ export const mapFields = (args: {
|
||||
Heading: <SortColumn label="ID" name="id" />,
|
||||
fieldIsPresentational: false,
|
||||
isFieldAffectingData: true,
|
||||
isSidebar: false,
|
||||
label: 'ID',
|
||||
labels: undefined,
|
||||
// label: 'ID',
|
||||
localized: undefined,
|
||||
readOnly: false,
|
||||
subfields: [],
|
||||
tabs: [],
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -10,67 +10,74 @@ import type {
|
||||
TabsField,
|
||||
} from 'payload/types'
|
||||
|
||||
import type { ArrayFieldProps } from '../../forms/fields/Array/types.js'
|
||||
import type { BlocksFieldProps } from '../../forms/fields/Blocks/types.js'
|
||||
import type { CheckboxFieldProps } from '../../forms/fields/Checkbox/types.js'
|
||||
import type { CodeFieldProps } from '../../forms/fields/Code/types.js'
|
||||
import type { CollapsibleFieldProps } from '../../forms/fields/Collapsible/types.js'
|
||||
import type { DateFieldProps } from '../../forms/fields/DateTime/types.js'
|
||||
import type { EmailFieldProps } from '../../forms/fields/Email/types.js'
|
||||
import type { GroupFieldProps } from '../../forms/fields/Group/types.js'
|
||||
import type { JSONFieldProps } from '../../forms/fields/JSON/types.js'
|
||||
import type { NumberFieldProps } from '../../forms/fields/Number/types.js'
|
||||
import type { PointFieldProps } from '../../forms/fields/Point/types.js'
|
||||
import type { RelationshipFieldProps } from '../../forms/fields/Relationship/types.js'
|
||||
import type { RowFieldProps } from '../../forms/fields/Row/types.js'
|
||||
import type { SelectFieldProps } from '../../forms/fields/Select/types.js'
|
||||
import type { TabsFieldProps } from '../../forms/fields/Tabs/types.js'
|
||||
import type { TextFieldProps } from '../../forms/fields/Text/types.js'
|
||||
import type { TextareaFieldProps } from '../../forms/fields/Textarea/types.js'
|
||||
import type { UploadFieldProps } from '../../forms/fields/Upload/types.js'
|
||||
import type { fieldTypes } from '../../forms/fields/index.js'
|
||||
|
||||
export type MappedTab = {
|
||||
fieldMap?: FieldMap
|
||||
label: TabsField['tabs'][0]['label']
|
||||
name?: string
|
||||
subfields?: FieldMap
|
||||
}
|
||||
|
||||
export type ReducedBlock = {
|
||||
fieldMap: FieldMap
|
||||
imageAltText?: string
|
||||
imageURL?: string
|
||||
labels: BlockField['labels']
|
||||
slug: string
|
||||
subfields: FieldMap
|
||||
}
|
||||
|
||||
export type MappedField = {
|
||||
export type FieldComponentProps =
|
||||
| ArrayFieldProps
|
||||
| BlocksFieldProps
|
||||
| CheckboxFieldProps
|
||||
| CodeFieldProps
|
||||
| CollapsibleFieldProps
|
||||
| DateFieldProps
|
||||
| EmailFieldProps
|
||||
| GroupFieldProps
|
||||
| JSONFieldProps
|
||||
| NumberFieldProps
|
||||
| PointFieldProps
|
||||
| RelationshipFieldProps
|
||||
| RowFieldProps
|
||||
| SelectFieldProps
|
||||
| TabsFieldProps
|
||||
| TextFieldProps
|
||||
| TextareaFieldProps
|
||||
| UploadFieldProps
|
||||
|
||||
export type MappedFieldBase = {
|
||||
Cell: React.ReactNode
|
||||
Field: React.ReactNode
|
||||
Heading: React.ReactNode
|
||||
/**
|
||||
* On `block` fields only
|
||||
*/
|
||||
blocks?: ReducedBlock[]
|
||||
disabled?: boolean
|
||||
/**
|
||||
* On `richText` fields only
|
||||
*/
|
||||
editor?: RichTextField['editor']
|
||||
fieldIsPresentational: boolean
|
||||
fieldMap?: FieldMap
|
||||
hasMany?: boolean
|
||||
isFieldAffectingData: boolean
|
||||
isSidebar: boolean
|
||||
label: FieldBase['label']
|
||||
labels: Labels
|
||||
isSidebar?: boolean
|
||||
localized: boolean
|
||||
name: string
|
||||
/**
|
||||
* On `select` fields only
|
||||
*/
|
||||
options?: Option[]
|
||||
/**
|
||||
* This is the `admin.readOnly` value from the field's config
|
||||
*/
|
||||
readOnly: boolean
|
||||
/**
|
||||
* On `relationship` fields only
|
||||
*/
|
||||
relationTo?: RelationshipField['relationTo']
|
||||
/**
|
||||
* On `array`, `group`, `collapsible`, and `tabs` fields only
|
||||
*/
|
||||
subfields?: FieldMap
|
||||
/**
|
||||
* On `tabs` fields only
|
||||
*/
|
||||
tabs?: MappedTab[]
|
||||
type: keyof typeof fieldTypes
|
||||
}
|
||||
|
||||
export type MappedField = FieldComponentProps & MappedFieldBase
|
||||
|
||||
export type FieldMap = MappedField[]
|
||||
|
||||
export type ActionMap = {
|
||||
|
||||
@@ -14,10 +14,10 @@ export const PostsCollection: CollectionConfig = {
|
||||
name: 'text',
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
name: 'richText',
|
||||
type: 'richText',
|
||||
},
|
||||
// {
|
||||
// name: 'richText',
|
||||
// type: 'richText',
|
||||
// },
|
||||
{
|
||||
name: 'relationship',
|
||||
type: 'relationship',
|
||||
|
||||
@@ -304,7 +304,7 @@ describe('versions', () => {
|
||||
await expect(
|
||||
page.locator('.autosave:has-text("Last saved less than a minute ago")'),
|
||||
).toBeVisible()
|
||||
expect(await titleField.inputValue()).toBe('global title')
|
||||
await expect(titleField).toHaveValue('global title')
|
||||
|
||||
// refresh the page and ensure value autosaved
|
||||
await page.goto(url.global(autoSaveGlobalSlug))
|
||||
@@ -339,7 +339,7 @@ describe('versions', () => {
|
||||
// change locale back to en
|
||||
await changeLocale(page, en)
|
||||
// verify en loads its own title
|
||||
expect(await titleField.inputValue()).toEqual(title)
|
||||
await expect(titleField).toHaveValue(title)
|
||||
// change non-localized description field
|
||||
await descriptionField.fill(newDescription)
|
||||
await wait(500)
|
||||
@@ -352,8 +352,8 @@ describe('versions', () => {
|
||||
// title should not be english title
|
||||
// description should be new description
|
||||
await page.reload()
|
||||
expect(await titleField.inputValue()).toEqual(spanishTitle)
|
||||
expect(await descriptionField.inputValue()).toEqual(newDescription)
|
||||
await expect(titleField).toHaveValue(spanishTitle)
|
||||
await expect(descriptionField).toHaveValue(newDescription)
|
||||
})
|
||||
|
||||
test('should restore localized docs correctly', async () => {
|
||||
|
||||
Reference in New Issue
Block a user