diff --git a/packages/next/src/routes/rest/buildFormState.ts b/packages/next/src/routes/rest/buildFormState.ts index ac7bf25e9..f60ef919e 100644 --- a/packages/next/src/routes/rest/buildFormState.ts +++ b/packages/next/src/routes/rest/buildFormState.ts @@ -33,7 +33,14 @@ export const buildFormState = async ({ req }: { req: PayloadRequest }) => { const fieldSchemaMap = getFieldSchemaMap(req.payload.config) - const { id, operation, docPreferences, formState, schemaPath } = reqData as BuildFormStateArgs + const { + id, + operation, + docPreferences, + formState, + data: incomingData, + schemaPath, + } = reqData as BuildFormStateArgs const schemaPathSegments = schemaPath.split('.') let fieldSchema: Field[] @@ -59,7 +66,7 @@ export const buildFormState = async ({ req }: { req: PayloadRequest }) => { ) } - const data = reduceFieldsToValues(formState, true) + const data = incomingData || reduceFieldsToValues(formState, true) const result = await buildStateFromSchema({ id, diff --git a/packages/payload/src/admin/RichText.ts b/packages/payload/src/admin/RichText.ts index 8ca301885..436c65eed 100644 --- a/packages/payload/src/admin/RichText.ts +++ b/packages/payload/src/admin/RichText.ts @@ -1,6 +1,7 @@ import type { JSONSchema4 } from 'json-schema' -import type { RichTextField, Validate } from '../fields/config/types' +import type { SanitizedConfig } from '../config/types' +import type { Field, RichTextField, Validate } from '../fields/config/types' import type { PayloadRequest, RequestContext } from '../types' import type { CellComponentProps } from './elements/Cell' @@ -26,7 +27,15 @@ type RichTextAdapterBase< incomingEditorState: Value siblingDoc: Record }) => Promise | null - generateComponentMap: () => Map | Promise> + generateComponentMap: (args: { + config: SanitizedConfig + schemaPath: string + }) => Map + generateSchemaMap: (args: { + config: SanitizedConfig + schemaMap: Map + schemaPath: string + }) => Map outputSchema?: ({ field, isRequired, diff --git a/packages/payload/src/admin/providers/DocumentInfo.ts b/packages/payload/src/admin/providers/DocumentInfo.ts index e450b8aef..269b3a2a7 100644 --- a/packages/payload/src/admin/providers/DocumentInfo.ts +++ b/packages/payload/src/admin/providers/DocumentInfo.ts @@ -6,13 +6,14 @@ import type { } from '../../collections/config/types' import type { PaginatedDocs, TypeWithVersion } from '../../exports/database' import type { SanitizedGlobalConfig } from '../../globals/config/types' +import type { DocumentPreferences } from '../../preferences/types' export type DocumentInfoContext = { collectionSlug?: SanitizedCollectionConfig['slug'] docConfig?: SanitizedCollectionConfig | SanitizedGlobalConfig docPermissions: DocumentPermissions getDocPermissions: () => Promise - getDocPreferences: () => Promise<{ [key: string]: unknown }> + getDocPreferences: () => Promise getVersions: () => Promise globalSlug?: SanitizedGlobalConfig['slug'] id?: number | string diff --git a/packages/payload/src/index.ts b/packages/payload/src/index.ts index 1e8ec01ee..83eacf4fd 100644 --- a/packages/payload/src/index.ts +++ b/packages/payload/src/index.ts @@ -4,7 +4,6 @@ import type { SendMailOptions } from 'nodemailer' import type pino from 'pino' import crypto from 'crypto' -import path from 'path' import type { AuthStrategy } from './auth' import type { Result as ForgotPasswordResult } from './auth/operations/forgotPassword' diff --git a/packages/payload/src/translations/extractTranslations.ts b/packages/payload/src/translations/extractTranslations.ts index ae3097b4e..41ef64d5c 100644 --- a/packages/payload/src/translations/extractTranslations.ts +++ b/packages/payload/src/translations/extractTranslations.ts @@ -11,7 +11,7 @@ export const extractTranslations = (keys: string[]): Record { - const enabledLeaves: Record< - string, - { - Button: React.ReactNode - Leaf: React.ReactNode - name: string - } - > = {} + const [{ elements, leaves }] = useState(() => { + const features: EnabledFeatures = { + elements: {}, + leaves: {}, + } for (const [key, value] of richTextComponentMap) { if (key.startsWith('leaf.button.') || key.startsWith('leaf.component.')) { const leafName = key.replace('leaf.button.', '').replace('leaf.component.', '') - if (!enabledLeaves[leafName]) { - enabledLeaves[leafName] = { + if (!features.leaves[leafName]) { + features.leaves[leafName] = { name: leafName, Button: null, Leaf: null, } } - if (key.startsWith('leaf.button.')) enabledLeaves[leafName].Button = value - if (key.startsWith('leaf.component.')) enabledLeaves[leafName].Leaf = value + if (key.startsWith('leaf.button.')) features.leaves[leafName].Button = value + if (key.startsWith('leaf.component.')) features.leaves[leafName].Leaf = value + } + + if (key.startsWith('element.button.') || key.startsWith('element.component.')) { + const elementName = key.replace('element.button.', '').replace('element.component.', '') + + if (!features.elements[elementName]) { + features.elements[elementName] = { + name: elementName, + Button: null, + Element: null, + } + } + + if (key.startsWith('element.button.')) features.elements[elementName].Button = value + if (key.startsWith('element.component.')) features.elements[elementName].Element = value } } - return enabledLeaves + return features }) - const elements: RichTextElement[] = defaultElements - const { i18n } = useTranslation() const editorRef = useRef(null) const toolbarRef = useRef(null) @@ -128,65 +124,72 @@ const RichText: React.FC< validate: memoizedValidate, }) - const renderElement = useCallback(({ attributes, children, element }) => { - // const matchedElement = enabledElements[element.type] - // const Element = matchedElement?.Element + const renderElement = useCallback( + ({ attributes, children, element }) => { + // return
{children}
- const attr = { ...attributes } + const matchedElement = elements[element.type] + const Element = matchedElement?.Element - // // this converts text alignment to margin when dealing with void elements - // if (element.textAlign) { - // if (element.type === 'relationship' || element.type === 'upload') { - // switch (element.textAlign) { - // case 'left': - // attr = { ...attr, style: { marginRight: 'auto' } } - // break - // case 'right': - // attr = { ...attr, style: { marginLeft: 'auto' } } - // break - // case 'center': - // attr = { ...attr, style: { marginLeft: 'auto', marginRight: 'auto' } } - // break - // default: - // attr = { ...attr, style: { textAlign: element.textAlign } } - // break - // } - // } else if (element.type === 'li') { - // switch (element.textAlign) { - // case 'right': - // attr = { ...attr, style: { listStylePosition: 'inside', textAlign: 'right' } } - // break - // case 'center': - // attr = { ...attr, style: { listStylePosition: 'inside', textAlign: 'center' } } - // break - // case 'left': - // default: - // attr = { ...attr, style: { listStylePosition: 'outside', textAlign: 'left' } } - // break - // } - // } else { - // attr = { ...attr, style: { textAlign: element.textAlign } } - // } - // } + let attr = { ...attributes } - // if (Element) { - // const el = ( - // - // {children} - // - // ) + // this converts text alignment to margin when dealing with void elements + if (element.textAlign) { + if (element.type === 'relationship' || element.type === 'upload') { + switch (element.textAlign) { + case 'left': + attr = { ...attr, style: { marginRight: 'auto' } } + break + case 'right': + attr = { ...attr, style: { marginLeft: 'auto' } } + break + case 'center': + attr = { ...attr, style: { marginLeft: 'auto', marginRight: 'auto' } } + break + default: + attr = { ...attr, style: { textAlign: element.textAlign } } + break + } + } else if (element.type === 'li') { + switch (element.textAlign) { + case 'right': + attr = { ...attr, style: { listStylePosition: 'inside', textAlign: 'right' } } + break + case 'center': + attr = { ...attr, style: { listStylePosition: 'inside', textAlign: 'center' } } + break + case 'left': + default: + attr = { ...attr, style: { listStylePosition: 'outside', textAlign: 'left' } } + break + } + } else { + attr = { ...attr, style: { textAlign: element.textAlign } } + } + } - // return el - // } + if (Element) { + const el = ( + + {Element} + + ) - return
{children}
- }, []) + return el + } + + return
{children}
+ }, + [elements, path, props, schemaPath], + ) const renderLeaf = useCallback( ({ attributes, children, leaf }) => { @@ -334,7 +337,7 @@ const RichText: React.FC< value={valueToRender as any[]} >
- {elements?.length + Object.keys(leaves)?.length > 0 && ( + {Object.keys(elements)?.length + Object.keys(leaves)?.length > 0 && (
- {/* {elements.map((element, i) => { - let elementName: string - if (typeof element === 'object' && element?.name) elementName = element.name - if (typeof element === 'string') elementName = element - - const elementType = enabledElements[elementName] - const Button = elementType?.Button + {Object.values(elements).map((element, i) => { + const Button = element?.Button if (Button) { - return + + + ) +} diff --git a/packages/richtext-slate/src/field/elements/indent/Element.tsx b/packages/richtext-slate/src/field/elements/indent/Element.tsx new file mode 100644 index 000000000..7ab1750eb --- /dev/null +++ b/packages/richtext-slate/src/field/elements/indent/Element.tsx @@ -0,0 +1,15 @@ +'use client' + +import React from 'react' + +import { useElement } from '../../providers/ElementProvider' + +export const IndentElement: React.FC = () => { + const { attributes, children } = useElement() + + return ( +
+ {children} +
+ ) +} diff --git a/packages/richtext-slate/src/field/elements/indent/index.ts b/packages/richtext-slate/src/field/elements/indent/index.ts new file mode 100644 index 000000000..54b84329c --- /dev/null +++ b/packages/richtext-slate/src/field/elements/indent/index.ts @@ -0,0 +1,12 @@ +import { IndentButton } from './Button' +import { IndentElement } from './Element' + +export const indentType = 'indent' + +const indent = { + name: indentType, + Button: IndentButton, + Element: IndentElement, +} + +export default indent diff --git a/packages/richtext-slate/src/field/elements/indent/index.tsx b/packages/richtext-slate/src/field/elements/indent/index.tsx deleted file mode 100644 index 39909eae8..000000000 --- a/packages/richtext-slate/src/field/elements/indent/index.tsx +++ /dev/null @@ -1,226 +0,0 @@ -import React, { useCallback } from 'react' -import { Editor, Element, Text, Transforms } from 'slate' -import { ReactEditor, useSlate } from 'slate-react' - -import type { ElementNode } from '../../../types' - -import IndentLeft from '../../icons/IndentLeft' -import IndentRight from '../../icons/IndentRight' -import { baseClass } from '../Button' -import { getCommonBlock } from '../getCommonBlock' -import isElementActive from '../isActive' -import { isBlockElement } from '../isBlockElement' -import listTypes from '../listTypes' -import { unwrapList } from '../unwrapList' - -const indentType = 'indent' - -const IndentWithPadding = ({ attributes, children }) => ( -
- {children} -
-) - -const indent = { - Button: () => { - const editor = useSlate() - const handleIndent = useCallback( - (e, dir) => { - e.preventDefault() - - if (dir === 'left') { - if (isElementActive(editor, 'li')) { - const [, listPath] = getCommonBlock( - editor, - (n) => Element.isElement(n) && listTypes.includes(n.type), - ) - - const matchedParentList = Editor.above(editor, { - at: listPath, - match: (n: ElementNode) => !Editor.isEditor(n) && isBlockElement(editor, n), - }) - - if (matchedParentList) { - const [parentListItem, parentListItemPath] = matchedParentList - - if (parentListItem.children.length > 1) { - // Remove nested list - Transforms.unwrapNodes(editor, { - at: parentListItemPath, - match: (node, path) => { - const matches = - !Editor.isEditor(node) && - Element.isElement(node) && - listTypes.includes(node.type) && - path.length === parentListItemPath.length + 1 - - return matches - }, - }) - - // Set li type on any children that don't have a type - Transforms.setNodes( - editor, - { type: 'li' }, - { - at: parentListItemPath, - match: (node, path) => { - const matches = - !Editor.isEditor(node) && - Element.isElement(node) && - node.type !== 'li' && - path.length === parentListItemPath.length + 1 - - return matches - }, - }, - ) - - // Parent list item path has changed at this point - // so we need to re-fetch the parent node - const [newParentNode] = Editor.node(editor, parentListItemPath) - - // If the parent node is an li, - // lift all li nodes within - if (Element.isElement(newParentNode) && newParentNode.type === 'li') { - // Lift the nested lis - Transforms.liftNodes(editor, { - at: parentListItemPath, - match: (node, path) => { - const matches = - !Editor.isEditor(node) && - Element.isElement(node) && - path.length === parentListItemPath.length + 1 && - node.type === 'li' - - return matches - }, - }) - } - } else { - Transforms.unwrapNodes(editor, { - at: parentListItemPath, - match: (node, path) => { - return ( - Element.isElement(node) && - node.type === 'li' && - path.length === parentListItemPath.length - ) - }, - }) - - Transforms.unwrapNodes(editor, { - match: (n) => Element.isElement(n) && listTypes.includes(n.type), - }) - } - } else { - unwrapList(editor, listPath) - } - } else { - Transforms.unwrapNodes(editor, { - match: (n) => Element.isElement(n) && n.type === indentType, - mode: 'lowest', - split: true, - }) - } - } - - if (dir === 'right') { - const isCurrentlyOL = isElementActive(editor, 'ol') - const isCurrentlyUL = isElementActive(editor, 'ul') - - if (isCurrentlyOL || isCurrentlyUL) { - // Get the path of the first selected li - - // Multiple lis could be selected - // and the selection may start in the middle of the first li - const [[, firstSelectedLIPath]] = Array.from( - Editor.nodes(editor, { - match: (node) => Element.isElement(node) && node.type === 'li', - mode: 'lowest', - }), - ) - - // Is the first selected li the first in its list? - const hasPrecedingLI = firstSelectedLIPath[firstSelectedLIPath.length - 1] > 0 - - // If the first selected li is NOT the first in its list, - // we need to inject it into the prior li - if (hasPrecedingLI) { - const [, precedingLIPath] = Editor.previous(editor, { - at: firstSelectedLIPath, - }) - - const [precedingLIChildren] = Editor.node(editor, [...precedingLIPath, 0]) - const precedingLIChildrenIsText = Text.isText(precedingLIChildren) - - if (precedingLIChildrenIsText) { - // Wrap the prior li text content so that it can be nested next to a list - Transforms.wrapNodes(editor, { children: [] }, { at: [...precedingLIPath, 0] }) - } - - // Move the selected lis after the prior li contents - Transforms.moveNodes(editor, { - match: (node) => Element.isElement(node) && node.type === 'li', - mode: 'lowest', - to: [...precedingLIPath, 1], - }) - - // Wrap the selected lis in a new list - Transforms.wrapNodes( - editor, - { - children: [], - type: isCurrentlyOL ? 'ol' : 'ul', - }, - { - match: (node) => Element.isElement(node) && node.type === 'li', - mode: 'lowest', - }, - ) - } else { - // Otherwise, just wrap the node in a list / li - Transforms.wrapNodes( - editor, - { - children: [{ children: [], type: 'li' }], - type: isCurrentlyOL ? 'ol' : 'ul', - }, - { - match: (node) => Element.isElement(node) && node.type === 'li', - mode: 'lowest', - }, - ) - } - } else { - Transforms.wrapNodes(editor, { children: [], type: indentType }) - } - } - - ReactEditor.focus(editor) - }, - [editor], - ) - - const canDeIndent = isElementActive(editor, 'li') || isElementActive(editor, indentType) - - return ( - - - - - ) - }, - Element: IndentWithPadding, -} - -export default indent diff --git a/packages/richtext-slate/src/field/elements/index.tsx b/packages/richtext-slate/src/field/elements/index.tsx index 488753665..2884bbfa5 100644 --- a/packages/richtext-slate/src/field/elements/index.tsx +++ b/packages/richtext-slate/src/field/elements/index.tsx @@ -1,3 +1,5 @@ +import type { RichTextCustomElement } from '../..' + import blockquote from './blockquote' import h1 from './h1' import h2 from './h2' @@ -5,16 +7,16 @@ import h3 from './h3' import h4 from './h4' import h5 from './h5' import h6 from './h6' -import indent from './indent' +// import indent from './indent' import li from './li' import link from './link' import ol from './ol' -import relationship from './relationship' -import textAlign from './textAlign' +// import relationship from './relationship' +// import textAlign from './textAlign' import ul from './ul' -import upload from './upload' +// import upload from './upload' -const elements = { +const elements: Record = { blockquote, h1, h2, @@ -22,14 +24,14 @@ const elements = { h4, h5, h6, - indent, + // indent, li, link, ol, - relationship, - textAlign, + // relationship, + // textAlign, ul, - upload, + // upload, } export default elements diff --git a/packages/richtext-slate/src/field/elements/li/ListItem.tsx b/packages/richtext-slate/src/field/elements/li/ListItem.tsx new file mode 100644 index 000000000..8a7d3d4d2 --- /dev/null +++ b/packages/richtext-slate/src/field/elements/li/ListItem.tsx @@ -0,0 +1,30 @@ +'use client' + +import React, { isValidElement } from 'react' + +import { useElement } from '../../providers/ElementProvider' +import listTypes from '../listTypes' +import { Element } from 'slate' + +export const ListItemElement: React.FC = () => { + const { attributes, children, element } = useElement() + + if (!isValidElement(element)) { + return null + } + + const listType = typeof element.children?.[0]?.type === 'string' ? element.children[0].type : '' + const disableListStyle = element.children.length >= 1 && listTypes.includes(listType) + + return ( +
  • + {children} +
  • + ) +} diff --git a/packages/richtext-slate/src/field/elements/li/index.tsx b/packages/richtext-slate/src/field/elements/li/index.tsx index f387db7b2..09bb2d970 100644 --- a/packages/richtext-slate/src/field/elements/li/index.tsx +++ b/packages/richtext-slate/src/field/elements/li/index.tsx @@ -1,25 +1,10 @@ -import React from 'react' +import type { RichTextCustomElement } from '../../..' -import listTypes from '../listTypes' +import { ListItemElement } from './ListItem' -const LI = (props) => { - const { attributes, children, element } = props - const disableListStyle = - element.children.length >= 1 && listTypes.includes(element.children?.[0]?.type) - - return ( -
  • - {children} -
  • - ) +const listItem: RichTextCustomElement = { + name: 'li', + Element: ListItemElement, } -export default { - Element: LI, -} +export default listItem diff --git a/packages/richtext-slate/src/field/elements/link/Button/index.tsx b/packages/richtext-slate/src/field/elements/link/Button/index.tsx index 2bff3cf74..b74dd4a42 100644 --- a/packages/richtext-slate/src/field/elements/link/Button/index.tsx +++ b/packages/richtext-slate/src/field/elements/link/Button/index.tsx @@ -1,30 +1,26 @@ 'use client' -import type { Fields } from '@payloadcms/ui' +import type { FormState } from '@payloadcms/ui' import { useModal } from '@faceless-ui/modal' import { - buildStateFromSchema, + getFormState, reduceFieldsToValues, - useAuth, useConfig, useDocumentInfo, useDrawerSlug, - useLocale, useTranslation, } from '@payloadcms/ui' -import { sanitizeFields } from 'payload/config' import React, { Fragment, useState } from 'react' import { Editor, Range, Transforms } from 'slate' import { ReactEditor, useSlate } from 'slate-react' -import type { FieldProps } from '../../../../types' - import LinkIcon from '../../../icons/Link' +import { useElementButton } from '../../../providers/ElementButtonProvider' import ElementButton from '../../Button' import isElementActive from '../../isActive' import { LinkDrawer } from '../LinkDrawer' -import { transformExtraFields, unwrapLink } from '../utilities' +import { unwrapLink } from '../utilities' /** * This function is called when an new link is created - not when an existing link is edited. @@ -64,35 +60,23 @@ const insertLink = (editor, fields) => { ReactEditor.focus(editor) } -export const LinkButton: React.FC<{ - fieldProps: FieldProps - path: string -}> = ({ fieldProps }) => { - const customFieldSchema = fieldProps?.admin?.link?.fields - const { user } = useAuth() - const { code: locale } = useLocale() - const [initialState, setInitialState] = useState({}) +export const LinkButton: React.FC = () => { + const { fieldProps } = useElementButton() + const [initialState, setInitialState] = useState({}) - const { i18n, t } = useTranslation() + const { t } = useTranslation() const editor = useSlate() const config = useConfig() - const [fieldSchema] = useState(() => { - const fieldsUnsanitized = transformExtraFields(customFieldSchema, config, i18n) - // Sanitize custom fields here - const validRelationships = config.collections.map((c) => c.slug) || [] - const fields = sanitizeFields({ - config: config, - fields: fieldsUnsanitized, - validRelationships, - }) - - return fields - }) - const { closeModal, openModal } = useModal() const drawerSlug = useDrawerSlug('rich-text-link') - const { getDocPreferences } = useDocumentInfo() + const { id, getDocPreferences } = useDocumentInfo() + + const { richTextComponentMap } = fieldProps + + const linkFieldsSchemaPath = `link.fields` + + const fieldMap = richTextComponentMap.get(linkFieldsSchemaPath) return ( @@ -104,24 +88,22 @@ export const LinkButton: React.FC<{ unwrapLink(editor) } else { openModal(drawerSlug) - const isCollapsed = editor.selection && Range.isCollapsed(editor.selection) - if (!isCollapsed) { const data = { text: editor.selection ? Editor.string(editor, editor.selection) : '', } - - const preferences = await getDocPreferences() - const state = await buildStateFromSchema({ - config, - data, - fieldSchema, - locale, - operation: 'create', - preferences, - t, - user, + const docPreferences = await getDocPreferences() + const state = await getFormState({ + apiRoute: config.routes.api, + body: { + id, + data, + docPreferences, + operation: 'update', + schemaPath: linkFieldsSchemaPath, + }, + serverURL: config.serverURL, }) setInitialState(state) } @@ -133,7 +115,7 @@ export const LinkButton: React.FC<{ { closeModal(drawerSlug) }} diff --git a/packages/richtext-slate/src/field/elements/link/Element/index.tsx b/packages/richtext-slate/src/field/elements/link/Element/index.tsx index cdaeefe4f..e87465591 100644 --- a/packages/richtext-slate/src/field/elements/link/Element/index.tsx +++ b/packages/richtext-slate/src/field/elements/link/Element/index.tsx @@ -1,15 +1,13 @@ 'use client' -import type { Fields } from '@payloadcms/ui' -import type { HTMLAttributes } from 'react' - import { useModal } from '@faceless-ui/modal' import { getTranslation } from '@payloadcms/translations' import { Button, + FormState, Popup, Translation, - buildStateFromSchema, + getFormState, reduceFieldsToValues, useAuth, useConfig, @@ -18,17 +16,16 @@ import { useLocale, useTranslation, } from '@payloadcms/ui' -import { sanitizeFields } from 'payload/config' import { deepCopyObject } from 'payload/utilities' import React, { useCallback, useEffect, useState } from 'react' import { Editor, Node, Transforms } from 'slate' import { ReactEditor, useSlate } from 'slate-react' -import type { FieldProps } from '../../../../types' - +import { useElement } from '../../../providers/ElementProvider' import { LinkDrawer } from '../LinkDrawer' -import { transformExtraFields, unwrapLink } from '../utilities' +import { unwrapLink } from '../utilities' import './index.scss' +import { LinkElementType } from '../types' const baseClass = 'rich-text-link' @@ -36,7 +33,7 @@ const baseClass = 'rich-text-link' * This function is called when an existing link is edited. * When a link is first created, another function is called: {@link ../Button/index.tsx#insertLink} */ -const insertChange = (editor, fields, customFieldSchema) => { +const insertChange = (editor, fields) => { const data = reduceFieldsToValues(fields, true) const [, parentPath] = Editor.above(editor) @@ -48,10 +45,6 @@ const insertChange = (editor, fields, customFieldSchema) => { url: data.url, } - if (customFieldSchema) { - newNode.fields = data.fields - } - Transforms.setNodes(editor, newNode, { at: parentPath }) Transforms.delete(editor, { at: editor.selection.focus.path, unit: 'block' }) @@ -61,16 +54,14 @@ const insertChange = (editor, fields, customFieldSchema) => { ReactEditor.focus(editor) } -export const LinkElement: React.FC<{ - attributes: HTMLAttributes - children: React.ReactNode - editorRef: React.RefObject - element: any - fieldProps: FieldProps -}> = (props) => { - const { attributes, children, editorRef, element, fieldProps } = props +export const LinkElement = () => { + const { attributes, children, editorRef, element, fieldProps, schemaPath } = + useElement() - const customFieldSchema = fieldProps?.admin?.link?.fields + const linkFieldsSchemaPath = `${schemaPath}.link.fields` + + const { richTextComponentMap } = fieldProps + const fieldMap = richTextComponentMap.get(linkFieldsSchemaPath) const editor = useSlate() const config = useConfig() @@ -80,20 +71,8 @@ export const LinkElement: React.FC<{ const { closeModal, openModal, toggleModal } = useModal() const [renderModal, setRenderModal] = useState(false) const [renderPopup, setRenderPopup] = useState(false) - const [initialState, setInitialState] = useState({}) - const { getDocPreferences } = useDocumentInfo() - const [fieldSchema] = useState(() => { - const fieldsUnsanitized = transformExtraFields(customFieldSchema, config, i18n) - // Sanitize custom fields here - const validRelationships = config.collections.map((c) => c.slug) || [] - const fields = sanitizeFields({ - config: config, - fields: fieldsUnsanitized, - validRelationships, - }) - - return fields - }) + const [initialState, setInitialState] = useState({}) + const { id, getDocPreferences } = useDocumentInfo() const drawerSlug = useDrawerSlug('rich-text-link') @@ -114,22 +93,25 @@ export const LinkElement: React.FC<{ url: element.url, } - const preferences = await getDocPreferences() - const state = await buildStateFromSchema({ - config, - data, - fieldSchema, - locale, - operation: 'update', - preferences, - t, - user, + const docPreferences = await getDocPreferences() + + const state = await getFormState({ + apiRoute: config.routes.api, + body: { + id, + data, + docPreferences, + operation: 'update', + schemaPath: linkFieldsSchemaPath, + }, + serverURL: config.serverURL, }) + setInitialState(state) } awaitInitialState() - }, [renderModal, element, fieldSchema, user, locale, t, getDocPreferences, config]) + }, [renderModal, element, user, locale, t, getDocPreferences, config]) return ( @@ -137,13 +119,13 @@ export const LinkElement: React.FC<{ {renderModal && ( { toggleModal(drawerSlug) setRenderModal(false) }} handleModalSubmit={(fields) => { - insertChange(editor, fields, customFieldSchema) + insertChange(editor, fields) closeModal(drawerSlug) }} initialState={initialState} diff --git a/packages/richtext-slate/src/field/elements/link/LinkDrawer/index.tsx b/packages/richtext-slate/src/field/elements/link/LinkDrawer/index.tsx index 77b5d1408..4359a7b6b 100644 --- a/packages/richtext-slate/src/field/elements/link/LinkDrawer/index.tsx +++ b/packages/richtext-slate/src/field/elements/link/LinkDrawer/index.tsx @@ -5,7 +5,6 @@ import { Form, FormSubmit, RenderFields, - fieldTypes, useEditDepth, useTranslation, } from '@payloadcms/ui' @@ -20,7 +19,7 @@ const baseClass = 'rich-text-link-edit-modal' export const LinkDrawer: React.FC = ({ drawerSlug, - fieldSchema, + fieldMap, handleModalSubmit, initialState, }) => { @@ -28,13 +27,8 @@ export const LinkDrawer: React.FC = ({ return ( -
    - + +
    diff --git a/packages/richtext-slate/src/field/elements/link/LinkDrawer/types.ts b/packages/richtext-slate/src/field/elements/link/LinkDrawer/types.ts index 9e95486e0..f0e8e08df 100644 --- a/packages/richtext-slate/src/field/elements/link/LinkDrawer/types.ts +++ b/packages/richtext-slate/src/field/elements/link/LinkDrawer/types.ts @@ -1,9 +1,9 @@ -import type { Field, Fields } from 'payload/types' +import type { FieldMap, FormState } from '@payloadcms/ui' export type Props = { drawerSlug: string - fieldSchema: Field[] + fieldMap: FieldMap handleClose: () => void - handleModalSubmit: (fields: Fields, data: Record) => void - initialState?: Fields + handleModalSubmit: (fields: FormState, data: Record) => void + initialState?: FormState } diff --git a/packages/richtext-slate/src/field/elements/link/index.tsx b/packages/richtext-slate/src/field/elements/link/index.tsx index e8ad37c72..8851ae17e 100644 --- a/packages/richtext-slate/src/field/elements/link/index.tsx +++ b/packages/richtext-slate/src/field/elements/link/index.tsx @@ -1,8 +1,11 @@ +import type { RichTextCustomElement } from '../../..' + import { LinkButton } from './Button' import { LinkElement } from './Element' import { withLinks } from './utilities' -const link = { +const link: RichTextCustomElement = { + name: 'link', Button: LinkButton, Element: LinkElement, plugins: [withLinks], diff --git a/packages/richtext-slate/src/field/elements/link/types.ts b/packages/richtext-slate/src/field/elements/link/types.ts new file mode 100644 index 000000000..25fa7a4a5 --- /dev/null +++ b/packages/richtext-slate/src/field/elements/link/types.ts @@ -0,0 +1,9 @@ +import { Element } from 'slate' + +export type LinkElementType = Element & { + doc: Record + fields: Record + linkType: string + newTab: boolean + url: string +} diff --git a/packages/richtext-slate/src/field/elements/ol/OrderedList.tsx b/packages/richtext-slate/src/field/elements/ol/OrderedList.tsx new file mode 100644 index 000000000..1f7d726a1 --- /dev/null +++ b/packages/richtext-slate/src/field/elements/ol/OrderedList.tsx @@ -0,0 +1,15 @@ +'use client' + +import React from 'react' +import { useElement } from '../../providers/ElementProvider' +import './index.scss' + +export const OrderedList: React.FC = () => { + const { attributes, children } = useElement() + + return ( +
      + {children} +
    + ) +} diff --git a/packages/richtext-slate/src/field/elements/ol/index.tsx b/packages/richtext-slate/src/field/elements/ol/index.tsx index 63e85fb5a..dc4fbcb99 100644 --- a/packages/richtext-slate/src/field/elements/ol/index.tsx +++ b/packages/richtext-slate/src/field/elements/ol/index.tsx @@ -1,22 +1,21 @@ import React from 'react' +import type { RichTextCustomElement } from '../../..' + import OLIcon from '../../icons/OrderedList' import ListButton from '../ListButton' -import './index.scss' +import { OrderedList } from './OrderedList' -const OL = ({ attributes, children }) => ( -
      - {children} -
    -) +const name = 'ol' -const ol = { +const ol: RichTextCustomElement = { + name, Button: () => ( - + ), - Element: OL, + Element: OrderedList, } export default ol diff --git a/packages/richtext-slate/src/field/elements/ul/UnorderedList.tsx b/packages/richtext-slate/src/field/elements/ul/UnorderedList.tsx new file mode 100644 index 000000000..fa228cf3b --- /dev/null +++ b/packages/richtext-slate/src/field/elements/ul/UnorderedList.tsx @@ -0,0 +1,15 @@ +'use client' + +import React from 'react' +import { useElement } from '../../providers/ElementProvider' +import './index.scss' + +export const UnorderedList: React.FC = () => { + const { attributes, children } = useElement() + + return ( +
      + {children} +
    + ) +} diff --git a/packages/richtext-slate/src/field/elements/ul/index.tsx b/packages/richtext-slate/src/field/elements/ul/index.tsx index 6e9189a7c..a376d06c7 100644 --- a/packages/richtext-slate/src/field/elements/ul/index.tsx +++ b/packages/richtext-slate/src/field/elements/ul/index.tsx @@ -1,22 +1,21 @@ import React from 'react' +import type { RichTextCustomElement } from '../../..' + import ULIcon from '../../icons/UnorderedList' import ListButton from '../ListButton' -import './index.scss' +import { UnorderedList } from './UnorderedList' -const UL = ({ attributes, children }) => ( -
      - {children} -
    -) +const name = 'ul' -const ul = { +const ul: RichTextCustomElement = { + name, Button: () => ( - + ), - Element: UL, + Element: UnorderedList, } export default ul diff --git a/packages/richtext-slate/src/field/providers/ElementButtonProvider.tsx b/packages/richtext-slate/src/field/providers/ElementButtonProvider.tsx new file mode 100644 index 000000000..ee76684b5 --- /dev/null +++ b/packages/richtext-slate/src/field/providers/ElementButtonProvider.tsx @@ -0,0 +1,42 @@ +'use client' +import React from 'react' + +import type { FormFieldBase } from '../../../../ui/src/forms/fields/shared' + +type ElementButtonContextType = { + fieldProps: FormFieldBase & { + name: string + richTextComponentMap: Map + } + path: string + schemaPath: string +} + +const ElementButtonContext = React.createContext({ + fieldProps: {} as any, + path: '', + schemaPath: '', +}) + +export const ElementButtonProvider: React.FC< + ElementButtonContextType & { + children: React.ReactNode + } +> = (props) => { + const { children, ...rest } = props + + return ( + + {children} + + ) +} + +export const useElementButton = () => { + const path = React.useContext(ElementButtonContext) + return path +} diff --git a/packages/richtext-slate/src/field/providers/ElementProvider.tsx b/packages/richtext-slate/src/field/providers/ElementProvider.tsx index e29c2d7bd..9351b8e6e 100644 --- a/packages/richtext-slate/src/field/providers/ElementProvider.tsx +++ b/packages/richtext-slate/src/field/providers/ElementProvider.tsx @@ -1,27 +1,45 @@ 'use client' +import type { Element } from 'slate' + import React from 'react' -type ElementContextType = { +import type { FormFieldBase } from '../../../../ui/src/forms/fields/shared' + +type ElementContextType = { + attributes: Record + children: React.ReactNode + editorRef: React.MutableRefObject + element: T + fieldProps: FormFieldBase & { + name: string + richTextComponentMap: Map + } path: string schemaPath: string } -const ElementContext = React.createContext({ +const ElementContext = React.createContext>({ + attributes: {}, + children: null, + editorRef: null, + element: {} as Element, + fieldProps: {} as any, path: '', schemaPath: '', }) -export const ElementProvider: React.FC<{ - children: React.ReactNode - path: string - schemaPath: string -}> = (props) => { - const { children, ...rest } = props +export const ElementProvider: React.FC< + ElementContextType & { + childNodes: React.ReactNode + } +> = (props) => { + const { childNodes, children, ...rest } = props return ( {children} @@ -29,7 +47,6 @@ export const ElementProvider: React.FC<{ ) } -export const useElement = () => { - const path = React.useContext(ElementContext) - return path +export const useElement = (): ElementContextType => { + return React.useContext(ElementContext) as ElementContextType } diff --git a/packages/richtext-slate/src/field/types.ts b/packages/richtext-slate/src/field/types.ts new file mode 100644 index 000000000..f96ee64bf --- /dev/null +++ b/packages/richtext-slate/src/field/types.ts @@ -0,0 +1,16 @@ +export type EnabledFeatures = { + elements: { + [name: string]: { + Button: React.ReactNode + Element: React.ReactNode + name: string + } + } + leaves: { + [name: string]: { + Button: React.ReactNode + Leaf: React.ReactNode + name: string + } + } +} diff --git a/packages/richtext-slate/src/index.tsx b/packages/richtext-slate/src/index.tsx index 7134fadc4..86d42b6be 100644 --- a/packages/richtext-slate/src/index.tsx +++ b/packages/richtext-slate/src/index.tsx @@ -1,15 +1,20 @@ -import type { RichTextAdapter } from 'payload/types' +import type { Field, RichTextAdapter } from 'payload/types' -import { withMergedProps } from '@payloadcms/ui/utilities' +import { initI18n } from '@payloadcms/translations' +import { translations } from '@payloadcms/translations/client' +import { mapFields, withMergedProps } from '@payloadcms/ui/utilities' +import { sanitizeFields } from 'payload/config' import { withNullableJSONSchemaType } from 'payload/utilities' import React from 'react' -import type { AdapterArguments, RichTextCustomLeaf } from './types' +import type { AdapterArguments, RichTextCustomElement, RichTextCustomLeaf } from './types' import RichTextCell from './cell' import { richTextRelationshipPromise } from './data/richTextRelationshipPromise' import { richTextValidate } from './data/validation' import RichTextField from './field' +import elementTypes from './field/elements' +import { transformExtraFields } from './field/elements/link/utilities' import leafTypes from './field/leaves' export function slateEditor(args: AdapterArguments): RichTextAdapter { @@ -22,9 +27,12 @@ export function slateEditor(args: AdapterArguments): RichTextAdapter { + generateComponentMap: ({ config }) => { const componentMap = new Map() + const i18n = initI18n({ config: config.i18n, context: 'client', translations }) + const validRelationships = config.collections.map((c) => c.slug) || [] + ;(args?.admin?.leaves || Object.values(leafTypes)).forEach((leaf) => { let leafObject: RichTextCustomLeaf @@ -42,9 +50,92 @@ export function slateEditor(args: AdapterArguments): RichTextAdapter) } }) + ;(args?.admin?.elements || Object.values(elementTypes)).forEach((el) => { + let element: RichTextCustomElement + + if (typeof el === 'object' && el !== null) { + element = el + } else if (typeof el === 'string' && elementTypes[el]) { + element = elementTypes[el] + } + + if (element) { + const ElementButton = element.Button + const ElementComponent = element.Element + + if (ElementButton) componentMap.set(`element.button.${element.name}`, ) + componentMap.set(`element.component.${element.name}`, ) + + switch (element.name) { + case 'link': { + const linkFields = sanitizeFields({ + config: config, + fields: transformExtraFields(args.admin?.link?.fields, config, i18n), + validRelationships, + }) + + const mappedFields = mapFields({ + config, + fieldSchema: linkFields, + operation: 'update', + permissions: {}, + readOnly: false, + }) + + componentMap.set('link.fields', mappedFields) + + return + } + + case 'upload': + break + + case 'relationship': + break + } + } + }) return componentMap }, + generateSchemaMap: ({ config, schemaMap, schemaPath }) => { + const i18n = initI18n({ config: config.i18n, context: 'client', translations }) + const validRelationships = config.collections.map((c) => c.slug) || [] + + ;(args?.admin?.elements || Object.values(elementTypes)).forEach((el) => { + let element: RichTextCustomElement + + if (typeof el === 'object' && el !== null) { + element = el + } else if (typeof el === 'string' && elementTypes[el]) { + element = elementTypes[el] + } + + if (element) { + switch (element.name) { + case 'link': { + const linkFields = sanitizeFields({ + config: config, + fields: transformExtraFields(args.admin?.link?.fields, config, i18n), + validRelationships, + }) + + schemaMap.set(`${schemaPath}.link`, linkFields) + + return + } + + case 'upload': + break + + case 'relationship': + break + } + } + }) + + return schemaMap + }, outputSchema: ({ isRequired }) => { return { items: { diff --git a/packages/richtext-slate/src/types.ts b/packages/richtext-slate/src/types.ts index 1fa09f0a8..cae7806b6 100644 --- a/packages/richtext-slate/src/types.ts +++ b/packages/richtext-slate/src/types.ts @@ -14,7 +14,7 @@ export function nodeIsTextNode(node: ElementNode | TextNode): node is TextNode { type RichTextPlugin = (editor: Editor) => Editor export type RichTextCustomElement = { - Button: React.ComponentType + Button?: React.ComponentType Element: React.ComponentType name: string plugins?: RichTextPlugin[] diff --git a/packages/richtext-slate/tsconfig.json b/packages/richtext-slate/tsconfig.json index fc75072f7..371be9ae0 100644 --- a/packages/richtext-slate/tsconfig.json +++ b/packages/richtext-slate/tsconfig.json @@ -27,5 +27,5 @@ "src/**/*.json", "src/field/leaves/italic/Italic" ], - "references": [{ "path": "../payload" }] // db-mongodb depends on payload + "references": [{ "path": "../payload" }, { "path": "../translations" }, { "path": "../ui" }] // db-mongodb depends on payload } diff --git a/packages/ui/src/elements/DocumentDrawer/DrawerContent.tsx b/packages/ui/src/elements/DocumentDrawer/DrawerContent.tsx index 02d9dde8d..c166d7dd3 100644 --- a/packages/ui/src/elements/DocumentDrawer/DrawerContent.tsx +++ b/packages/ui/src/elements/DocumentDrawer/DrawerContent.tsx @@ -26,7 +26,7 @@ import type { EditViewProps } from '../../views/types' import { DefaultEditView } from '../../views/Edit' import { Gutter } from '../Gutter' import { LoadingOverlay } from '../Loading' -import { getFormState } from '../../views/Edit/getFormState' +import { getFormState } from '../../utilities/getFormState' import { useFieldPath } from '../../forms/FieldPathProvider' const Content: React.FC = ({ collectionSlug, Header, drawerSlug, onSave }) => { diff --git a/packages/ui/src/exports/utilities.ts b/packages/ui/src/exports/utilities.ts index edb158b4c..9d1aacdcc 100644 --- a/packages/ui/src/exports/utilities.ts +++ b/packages/ui/src/exports/utilities.ts @@ -7,3 +7,5 @@ export { withMergedProps } from '../utilities/withMergedProps' export type { FieldMap, MappedField } from '../utilities/buildComponentMap/types' export { buildFieldSchemaMap } from '../utilities/buildFieldSchemaMap' export type { FieldSchemaMap } from '../utilities/buildFieldSchemaMap/types' +export { mapFields } from '../utilities/buildComponentMap/mapFields' +export { getFormState } from '../utilities/getFormState' diff --git a/packages/ui/src/forms/Form/index.tsx b/packages/ui/src/forms/Form/index.tsx index 68a3011ff..434ddac5d 100644 --- a/packages/ui/src/forms/Form/index.tsx +++ b/packages/ui/src/forms/Form/index.tsx @@ -51,7 +51,6 @@ const Form: React.FC = (props) => { className, disableSuccessStatus, disabled, - fields: fieldsFromProps, // fields: fieldsFromProps = collection?.fields || global?.fields, handleResponse, initialState, // fully formed initial field state diff --git a/packages/ui/src/forms/fields/shared.ts b/packages/ui/src/forms/fields/shared.ts index 3c8492a37..f4d9f9166 100644 --- a/packages/ui/src/forms/fields/shared.ts +++ b/packages/ui/src/forms/fields/shared.ts @@ -15,7 +15,12 @@ import { } from 'payload/types' import { Option } from 'payload/types' import { FormState } from '../..' -import type { FieldMap, ReducedBlock, MappedTab } from '../../utilities/buildComponentMap/types' +import type { + FieldMap, + ReducedBlock, + MappedTab, + MappedField, +} from '../../utilities/buildComponentMap/types' export const fieldBaseClass = 'field-type' @@ -111,7 +116,7 @@ export type FormFieldBase = { } | { // For `richText` fields - richTextComponentMap?: Map + richTextComponentMap?: Map } ) diff --git a/packages/ui/src/forms/utilities/buildStateFromSchema/index.ts b/packages/ui/src/forms/utilities/buildStateFromSchema/index.ts index 97f091a38..7a800647b 100644 --- a/packages/ui/src/forms/utilities/buildStateFromSchema/index.ts +++ b/packages/ui/src/forms/utilities/buildStateFromSchema/index.ts @@ -26,6 +26,7 @@ export type BuildFormStateArgs = { operation?: 'create' | 'update' docPreferences: DocumentPreferences formState?: FormState + data?: Record schemaPath: string } diff --git a/packages/ui/src/utilities/buildComponentMap/index.tsx b/packages/ui/src/utilities/buildComponentMap/index.tsx index 3e30a1af4..905137145 100644 --- a/packages/ui/src/utilities/buildComponentMap/index.tsx +++ b/packages/ui/src/utilities/buildComponentMap/index.tsx @@ -45,6 +45,7 @@ export const buildComponentMap = (args: { afterListTable?.map((Component) => ) const mappedFields = mapFields({ + config, fieldSchema: fields, operation, permissions, @@ -70,6 +71,7 @@ export const buildComponentMap = (args: { const { fields, slug } = globalConfig const mappedFields = mapFields({ + config, fieldSchema: fields, operation, permissions, diff --git a/packages/ui/src/utilities/buildComponentMap/mapFields.tsx b/packages/ui/src/utilities/buildComponentMap/mapFields.tsx index 3c81e9ac3..ac4bb98d0 100644 --- a/packages/ui/src/utilities/buildComponentMap/mapFields.tsx +++ b/packages/ui/src/utilities/buildComponentMap/mapFields.tsx @@ -1,7 +1,7 @@ import React, { Fragment } from 'react' import type { FieldPermissions } from 'payload/auth' -import type { CellProps, Field, FieldWithPath, LabelProps } from 'payload/types' +import type { CellProps, Field, FieldWithPath, LabelProps, SanitizedConfig } from 'payload/types' import { fieldAffectsData, fieldIsPresentationalOnly } from 'payload/types' import { fieldTypes } from '../../forms/fields' import { FormFieldBase } from '../../forms/fields/shared' @@ -14,9 +14,9 @@ import DefaultLabel from '../../forms/Label' import DefaultError from '../../forms/Error' import DefaultDescription from '../../forms/FieldDescription' import { HiddenInput } from '../..' -import { richText } from 'payload/fields/validations' export const mapFields = (args: { + config: SanitizedConfig fieldSchema: FieldWithPath[] filter?: (field: Field) => boolean operation?: 'create' | 'update' @@ -29,6 +29,7 @@ export const mapFields = (args: { parentPath?: string }): FieldMap => { const { + config, fieldSchema, filter, operation = 'update', @@ -95,6 +96,7 @@ export const mapFields = (args: { field.fields && Array.isArray(field.fields) && mapFields({ + config, fieldSchema: field.fields, filter, operation, @@ -110,6 +112,7 @@ export const mapFields = (args: { Array.isArray(field.tabs) && field.tabs.map((tab) => { const tabFieldMap = mapFields({ + config, fieldSchema: tab.fields, filter, operation, @@ -134,12 +137,13 @@ export const mapFields = (args: { Array.isArray(field.blocks) && field.blocks.map((block) => { const blockFieldMap = mapFields({ + config, fieldSchema: block.fields, filter, operation, permissions, readOnly: readOnlyOverride, - parentPath: path, + parentPath: `${path}.${block.slug}`, }) const reducedBlock: ReducedBlock = { @@ -267,7 +271,7 @@ export const mapFields = (args: { } if (typeof field.editor.generateComponentMap === 'function') { - const result = field.editor.generateComponentMap() + const result = field.editor.generateComponentMap({ config, schemaPath: path }) // @ts-ignore-next-line // TODO: the `richTextComponentMap` is not found on the union type fieldComponentProps.richTextComponentMap = result } diff --git a/packages/ui/src/utilities/buildFieldSchemaMap/index.ts b/packages/ui/src/utilities/buildFieldSchemaMap/index.ts index fbc6b33ea..ee0f854f0 100644 --- a/packages/ui/src/utilities/buildFieldSchemaMap/index.ts +++ b/packages/ui/src/utilities/buildFieldSchemaMap/index.ts @@ -7,6 +7,7 @@ export const buildFieldSchemaMap = (config: SanitizedConfig): FieldSchemaMap => config.collections.forEach((collection) => { traverseFields({ + config, schemaPath: collection.slug, fields: collection.fields, schemaMap: result, @@ -15,6 +16,7 @@ export const buildFieldSchemaMap = (config: SanitizedConfig): FieldSchemaMap => config.globals.forEach((global) => { traverseFields({ + config, schemaPath: global.slug, fields: global.fields, schemaMap: result, diff --git a/packages/ui/src/utilities/buildFieldSchemaMap/traverseFields.ts b/packages/ui/src/utilities/buildFieldSchemaMap/traverseFields.ts index 26ca8f886..08fb52bdc 100644 --- a/packages/ui/src/utilities/buildFieldSchemaMap/traverseFields.ts +++ b/packages/ui/src/utilities/buildFieldSchemaMap/traverseFields.ts @@ -1,18 +1,20 @@ -import { Field, tabHasName } from 'payload/types' +import { Field, SanitizedConfig, tabHasName } from 'payload/types' import { FieldSchemaMap } from './types' type Args = { + config: SanitizedConfig fields: Field[] schemaMap: FieldSchemaMap schemaPath: string } -export const traverseFields = ({ fields, schemaMap, schemaPath }: Args) => { +export const traverseFields = ({ config, fields, schemaMap, schemaPath }: Args) => { fields.map((field) => { switch (field.type) { case 'group': case 'array': traverseFields({ + config, fields: field.fields, schemaMap, schemaPath: `${schemaPath}.${field.name}`, @@ -22,6 +24,7 @@ export const traverseFields = ({ fields, schemaMap, schemaPath }: Args) => { case 'collapsible': case 'row': traverseFields({ + config, fields: field.fields, schemaMap, schemaPath, @@ -31,6 +34,7 @@ export const traverseFields = ({ fields, schemaMap, schemaPath }: Args) => { case 'blocks': field.blocks.map((block) => { traverseFields({ + config, fields: block.fields, schemaMap, schemaPath: `${schemaPath}.${field.name}.${block.slug}`, @@ -38,10 +42,18 @@ export const traverseFields = ({ fields, schemaMap, schemaPath }: Args) => { }) break + case 'richText': + if (typeof field.editor.generateSchemaMap === 'function') { + field.editor.generateSchemaMap({ schemaPath, config, schemaMap }) + } + + break + case 'tabs': field.tabs.map((tab) => { const tabSchemaPath = tabHasName(tab) ? `${schemaPath}.${tab.name}` : schemaPath traverseFields({ + config, fields: tab.fields, schemaMap, schemaPath: tabSchemaPath, diff --git a/packages/ui/src/views/Edit/getFormState.ts b/packages/ui/src/utilities/getFormState.ts similarity index 86% rename from packages/ui/src/views/Edit/getFormState.ts rename to packages/ui/src/utilities/getFormState.ts index e0e113f22..7f1dbbc77 100644 --- a/packages/ui/src/views/Edit/getFormState.ts +++ b/packages/ui/src/utilities/getFormState.ts @@ -1,6 +1,6 @@ import { SanitizedConfig } from 'payload/types' -import { FormState } from '../../forms/Form/types' -import { BuildFormStateArgs } from '../..' +import { FormState } from '../forms/Form/types' +import { BuildFormStateArgs } from '..' export const getFormState = async (args: { serverURL: SanitizedConfig['serverURL'] diff --git a/packages/ui/src/views/Edit/index.tsx b/packages/ui/src/views/Edit/index.tsx index 9087e415f..a4727436d 100644 --- a/packages/ui/src/views/Edit/index.tsx +++ b/packages/ui/src/views/Edit/index.tsx @@ -20,7 +20,7 @@ import { Props as FormProps, FormState } from '../../forms/Form/types' import './index.scss' import { BuildFormStateArgs } from '../..' -import { getFormState } from './getFormState' +import { getFormState } from '../../utilities/getFormState' import { FieldPathProvider } from '../../forms/FieldPathProvider' const baseClass = 'collection-edit'