chore: scaffolds ssr rte
This commit is contained in:
@@ -13,6 +13,10 @@ export const sanitizeField = (f) => {
|
|||||||
field.fields = sanitizeFields(field.fields)
|
field.fields = sanitizeFields(field.fields)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ('editor' in field) {
|
||||||
|
delete field.editor
|
||||||
|
}
|
||||||
|
|
||||||
if ('blocks' in field) {
|
if ('blocks' in field) {
|
||||||
field.blocks = field.blocks.map((block) => {
|
field.blocks = field.blocks.map((block) => {
|
||||||
const sanitized = { ...block }
|
const sanitized = { ...block }
|
||||||
@@ -94,6 +98,7 @@ export const createClientConfig = async (
|
|||||||
|
|
||||||
delete clientConfig.endpoints
|
delete clientConfig.endpoints
|
||||||
delete clientConfig.db
|
delete clientConfig.db
|
||||||
|
delete clientConfig.editor
|
||||||
|
|
||||||
'localization' in clientConfig &&
|
'localization' in clientConfig &&
|
||||||
clientConfig.localization &&
|
clientConfig.localization &&
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ type RichTextAdapterBase<
|
|||||||
incomingEditorState: Value
|
incomingEditorState: Value
|
||||||
siblingDoc: Record<string, unknown>
|
siblingDoc: Record<string, unknown>
|
||||||
}) => Promise<void> | null
|
}) => Promise<void> | null
|
||||||
|
generateComponentMap: () => Map<string, React.ReactNode> | Promise<Map<string, React.ReactNode>>
|
||||||
outputSchema?: ({
|
outputSchema?: ({
|
||||||
field,
|
field,
|
||||||
isRequired,
|
isRequired,
|
||||||
|
|||||||
@@ -5,22 +5,23 @@ import type { HistoryEditor } from 'slate-history'
|
|||||||
import type { ReactEditor } from 'slate-react'
|
import type { ReactEditor } from 'slate-react'
|
||||||
|
|
||||||
import { getTranslation } from '@payloadcms/translations'
|
import { getTranslation } from '@payloadcms/translations'
|
||||||
import {
|
import { useEditDepth, useField, useTranslation } from '@payloadcms/ui'
|
||||||
Error,
|
|
||||||
FieldDescription,
|
|
||||||
Label,
|
|
||||||
useEditDepth,
|
|
||||||
useField,
|
|
||||||
useTranslation,
|
|
||||||
} from '@payloadcms/ui'
|
|
||||||
import isHotkey from 'is-hotkey'
|
import isHotkey from 'is-hotkey'
|
||||||
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
||||||
import { Node, Element as SlateElement, Text, Transforms, createEditor } from 'slate'
|
import { Node, Element as SlateElement, Text, Transforms, createEditor } from 'slate'
|
||||||
import { withHistory } from 'slate-history'
|
import { withHistory } from 'slate-history'
|
||||||
import { Editable, Slate, withReact } from 'slate-react'
|
import { Editable, Slate, withReact } from 'slate-react'
|
||||||
|
|
||||||
import type { ElementNode, FieldProps, RichTextElement, RichTextLeaf, TextNode } from '../types'
|
import type { FormFieldBase } from '../../../ui/src/forms/fields/shared'
|
||||||
|
import type {
|
||||||
|
ElementNode,
|
||||||
|
RichTextCustomLeaf,
|
||||||
|
RichTextElement,
|
||||||
|
RichTextLeaf,
|
||||||
|
TextNode,
|
||||||
|
} from '../types'
|
||||||
|
|
||||||
|
import { withCondition } from '../../../ui/src/forms/withCondition'
|
||||||
import { defaultRichTextValue } from '../data/defaultValue'
|
import { defaultRichTextValue } from '../data/defaultValue'
|
||||||
import { richTextValidate } from '../data/validation'
|
import { richTextValidate } from '../data/validation'
|
||||||
import elementTypes from './elements'
|
import elementTypes from './elements'
|
||||||
@@ -33,6 +34,8 @@ import toggleLeaf from './leaves/toggle'
|
|||||||
import mergeCustomFunctions from './mergeCustomFunctions'
|
import mergeCustomFunctions from './mergeCustomFunctions'
|
||||||
import withEnterBreakOut from './plugins/withEnterBreakOut'
|
import withEnterBreakOut from './plugins/withEnterBreakOut'
|
||||||
import withHTML from './plugins/withHTML'
|
import withHTML from './plugins/withHTML'
|
||||||
|
import { LeafButtonProvider } from './providers/LeafButtonProvider'
|
||||||
|
import { LeafProvider } from './providers/LeafProvider'
|
||||||
|
|
||||||
const defaultElements: RichTextElement[] = [
|
const defaultElements: RichTextElement[] = [
|
||||||
'h1',
|
'h1',
|
||||||
@@ -60,127 +63,163 @@ declare module 'slate' {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const RichText: React.FC<FieldProps> = (props) => {
|
const RichText: React.FC<
|
||||||
|
FormFieldBase & {
|
||||||
|
name: string
|
||||||
|
richTextComponentMap: Map<string, React.ReactNode>
|
||||||
|
}
|
||||||
|
> = (props) => {
|
||||||
const {
|
const {
|
||||||
name,
|
name,
|
||||||
admin: {
|
Description,
|
||||||
className,
|
Error,
|
||||||
condition,
|
Label,
|
||||||
description,
|
className,
|
||||||
hideGutter,
|
|
||||||
placeholder,
|
|
||||||
readOnly,
|
|
||||||
style,
|
|
||||||
width,
|
|
||||||
} = {
|
|
||||||
className: undefined,
|
|
||||||
condition: undefined,
|
|
||||||
description: undefined,
|
|
||||||
hideGutter: undefined,
|
|
||||||
placeholder: undefined,
|
|
||||||
readOnly: undefined,
|
|
||||||
style: undefined,
|
|
||||||
width: undefined,
|
|
||||||
},
|
|
||||||
admin,
|
|
||||||
defaultValue: defaultValueFromProps,
|
|
||||||
label,
|
|
||||||
path: pathFromProps,
|
path: pathFromProps,
|
||||||
|
placeholder,
|
||||||
|
readOnly,
|
||||||
required,
|
required,
|
||||||
|
richTextComponentMap,
|
||||||
|
style,
|
||||||
validate = richTextValidate,
|
validate = richTextValidate,
|
||||||
|
width,
|
||||||
} = props
|
} = props
|
||||||
|
|
||||||
const elements: RichTextElement[] = admin?.elements || defaultElements
|
const [leaves] = useState(() => {
|
||||||
const leaves: RichTextLeaf[] = admin?.leaves || defaultLeaves
|
const enabledLeaves: Record<
|
||||||
|
string,
|
||||||
|
{
|
||||||
|
Button: React.ReactNode
|
||||||
|
Leaf: React.ReactNode
|
||||||
|
name: string
|
||||||
|
}
|
||||||
|
> = {}
|
||||||
|
|
||||||
const path = pathFromProps || name
|
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] = {
|
||||||
|
name: leafName,
|
||||||
|
Button: null,
|
||||||
|
Leaf: null,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (key.startsWith('leaf.button')) enabledLeaves[leafName].Button = value
|
||||||
|
if (key.startsWith('leaf.component')) enabledLeaves[leafName].Leaf = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return enabledLeaves
|
||||||
|
})
|
||||||
|
|
||||||
|
const elements: RichTextElement[] = defaultElements
|
||||||
|
|
||||||
const { i18n } = useTranslation()
|
const { i18n } = useTranslation()
|
||||||
const [loaded, setLoaded] = useState(false)
|
|
||||||
const [enabledElements, setEnabledElements] = useState({})
|
|
||||||
const [enabledLeaves, setEnabledLeaves] = useState({})
|
|
||||||
const editorRef = useRef(null)
|
const editorRef = useRef(null)
|
||||||
const toolbarRef = useRef(null)
|
const toolbarRef = useRef(null)
|
||||||
|
|
||||||
const drawerDepth = useEditDepth()
|
const drawerDepth = useEditDepth()
|
||||||
const drawerIsOpen = drawerDepth > 1
|
const drawerIsOpen = drawerDepth > 1
|
||||||
|
|
||||||
const renderElement = useCallback(
|
const memoizedValidate = useCallback(
|
||||||
({ attributes, children, element }) => {
|
(value, validationOptions) => {
|
||||||
const matchedElement = enabledElements[element.type]
|
if (typeof validate === 'function') {
|
||||||
const Element = matchedElement?.Element
|
return validate(value, { ...validationOptions, required })
|
||||||
|
|
||||||
let attr = { ...attributes }
|
|
||||||
|
|
||||||
// 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 } }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Element) {
|
|
||||||
const el = (
|
|
||||||
<Element
|
|
||||||
attributes={attr}
|
|
||||||
editorRef={editorRef}
|
|
||||||
element={element}
|
|
||||||
fieldProps={props}
|
|
||||||
path={path}
|
|
||||||
>
|
|
||||||
{children}
|
|
||||||
</Element>
|
|
||||||
)
|
|
||||||
|
|
||||||
return el
|
|
||||||
}
|
|
||||||
|
|
||||||
return <div {...attr}>{children}</div>
|
|
||||||
},
|
},
|
||||||
[enabledElements, path, props],
|
[validate, required],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const { initialValue, path, schemaPath, setValue, showError, value } = useField({
|
||||||
|
path: pathFromProps || name,
|
||||||
|
validate: memoizedValidate,
|
||||||
|
})
|
||||||
|
|
||||||
|
const renderElement = useCallback(({ attributes, children, element }) => {
|
||||||
|
// const matchedElement = enabledElements[element.type]
|
||||||
|
// const Element = matchedElement?.Element
|
||||||
|
|
||||||
|
const attr = { ...attributes }
|
||||||
|
|
||||||
|
// // 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 } }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if (Element) {
|
||||||
|
// const el = (
|
||||||
|
// <Element
|
||||||
|
// attributes={attr}
|
||||||
|
// editorRef={editorRef}
|
||||||
|
// element={element}
|
||||||
|
// fieldProps={props}
|
||||||
|
// path={path}
|
||||||
|
// >
|
||||||
|
// {children}
|
||||||
|
// </Element>
|
||||||
|
// )
|
||||||
|
|
||||||
|
// return el
|
||||||
|
// }
|
||||||
|
|
||||||
|
return <div {...attr}>{children}</div>
|
||||||
|
}, [])
|
||||||
|
|
||||||
const renderLeaf = useCallback(
|
const renderLeaf = useCallback(
|
||||||
({ attributes, children, leaf }) => {
|
({ attributes, children, leaf }) => {
|
||||||
const matchedLeaves = Object.entries(enabledLeaves).filter(([leafName]) => leaf[leafName])
|
const matchedLeaves = Object.entries(leaves).filter(([leafName]) => leaf[leafName])
|
||||||
|
console.log(matchedLeaves, leaf)
|
||||||
if (matchedLeaves.length > 0) {
|
if (matchedLeaves.length > 0) {
|
||||||
return matchedLeaves.reduce(
|
return matchedLeaves.reduce(
|
||||||
(result, [leafName], i) => {
|
(result, [, leafConfig], i) => {
|
||||||
if (enabledLeaves[leafName]?.Leaf) {
|
if (leafConfig?.Leaf) {
|
||||||
const Leaf = enabledLeaves[leafName]?.Leaf
|
const Leaf = leafConfig.Leaf
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Leaf editorRef={editorRef} fieldProps={props} key={i} leaf={leaf} path={path}>
|
<LeafProvider
|
||||||
{result}
|
attributes={attributes}
|
||||||
</Leaf>
|
editorRef={editorRef}
|
||||||
|
fieldProps={props}
|
||||||
|
key={i}
|
||||||
|
leaf={leaf}
|
||||||
|
path={path}
|
||||||
|
result={result}
|
||||||
|
schemaPath={schemaPath}
|
||||||
|
>
|
||||||
|
{Leaf}
|
||||||
|
</LeafProvider>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -192,31 +231,15 @@ const RichText: React.FC<FieldProps> = (props) => {
|
|||||||
|
|
||||||
return <span {...attributes}>{children}</span>
|
return <span {...attributes}>{children}</span>
|
||||||
},
|
},
|
||||||
[enabledLeaves, path, props],
|
[path, props, schemaPath, richTextComponentMap],
|
||||||
)
|
)
|
||||||
|
|
||||||
const memoizedValidate = useCallback(
|
|
||||||
(value, validationOptions) => {
|
|
||||||
return validate(value, { ...validationOptions, required })
|
|
||||||
},
|
|
||||||
[validate, required],
|
|
||||||
)
|
|
||||||
|
|
||||||
const fieldType = useField({
|
|
||||||
condition,
|
|
||||||
path,
|
|
||||||
validate: memoizedValidate,
|
|
||||||
})
|
|
||||||
|
|
||||||
const { errorMessage, initialValue, setValue, showError, value } = fieldType
|
|
||||||
|
|
||||||
const classes = [
|
const classes = [
|
||||||
baseClass,
|
baseClass,
|
||||||
'field-type',
|
'field-type',
|
||||||
className,
|
className,
|
||||||
showError && 'error',
|
showError && 'error',
|
||||||
readOnly && `${baseClass}--read-only`,
|
readOnly && `${baseClass}--read-only`,
|
||||||
!hideGutter && `${baseClass}--gutter`,
|
|
||||||
]
|
]
|
||||||
.filter(Boolean)
|
.filter(Boolean)
|
||||||
.join(' ')
|
.join(' ')
|
||||||
@@ -225,12 +248,12 @@ const RichText: React.FC<FieldProps> = (props) => {
|
|||||||
let CreatedEditor = withEnterBreakOut(withHistory(withReact(createEditor())))
|
let CreatedEditor = withEnterBreakOut(withHistory(withReact(createEditor())))
|
||||||
|
|
||||||
CreatedEditor = withHTML(CreatedEditor)
|
CreatedEditor = withHTML(CreatedEditor)
|
||||||
CreatedEditor = enablePlugins(CreatedEditor, elements)
|
// CreatedEditor = enablePlugins(CreatedEditor, elements)
|
||||||
CreatedEditor = enablePlugins(CreatedEditor, leaves)
|
// CreatedEditor = enablePlugins(CreatedEditor, leaves)
|
||||||
|
|
||||||
return CreatedEditor
|
return CreatedEditor
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [elements, leaves, path])
|
}, [path])
|
||||||
|
|
||||||
// All slate changes fire the onChange event
|
// All slate changes fire the onChange event
|
||||||
// including selection changes
|
// including selection changes
|
||||||
@@ -254,18 +277,6 @@ const RichText: React.FC<FieldProps> = (props) => {
|
|||||||
[editor.operations, readOnly, setValue, value],
|
[editor.operations, readOnly, setValue, value],
|
||||||
)
|
)
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!loaded) {
|
|
||||||
const mergedElements = mergeCustomFunctions(elements, elementTypes)
|
|
||||||
const mergedLeaves = mergeCustomFunctions(leaves, leafTypes)
|
|
||||||
|
|
||||||
setEnabledElements(mergedElements)
|
|
||||||
setEnabledLeaves(mergedLeaves)
|
|
||||||
|
|
||||||
setLoaded(true)
|
|
||||||
}
|
|
||||||
}, [loaded, elements, leaves])
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
function setClickableState(clickState: 'disabled' | 'enabled') {
|
function setClickableState(clickState: 'disabled' | 'enabled') {
|
||||||
const selectors = 'button, a, [role="button"]'
|
const selectors = 'button, a, [role="button"]'
|
||||||
@@ -280,16 +291,16 @@ const RichText: React.FC<FieldProps> = (props) => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
if (loaded && readOnly) {
|
if (readOnly) {
|
||||||
setClickableState('disabled')
|
setClickableState('disabled')
|
||||||
}
|
}
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
if (loaded && readOnly) {
|
if (readOnly) {
|
||||||
setClickableState('enabled')
|
setClickableState('enabled')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [loaded, readOnly])
|
}, [readOnly])
|
||||||
|
|
||||||
// useEffect(() => {
|
// useEffect(() => {
|
||||||
// // If there is a change to the initial value, we need to reset Slate history
|
// // If there is a change to the initial value, we need to reset Slate history
|
||||||
@@ -301,10 +312,6 @@ const RichText: React.FC<FieldProps> = (props) => {
|
|||||||
// }
|
// }
|
||||||
// }, [path, editor]);
|
// }, [path, editor]);
|
||||||
|
|
||||||
if (!loaded) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
let valueToRender = value
|
let valueToRender = value
|
||||||
|
|
||||||
if (typeof valueToRender === 'string') {
|
if (typeof valueToRender === 'string') {
|
||||||
@@ -316,7 +323,7 @@ const RichText: React.FC<FieldProps> = (props) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!valueToRender) valueToRender = defaultValueFromProps || defaultRichTextValue
|
if (!valueToRender) valueToRender = defaultRichTextValue
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
@@ -327,8 +334,8 @@ const RichText: React.FC<FieldProps> = (props) => {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div className={`${baseClass}__wrap`}>
|
<div className={`${baseClass}__wrap`}>
|
||||||
<Error message={errorMessage} showError={showError} />
|
{Error}
|
||||||
<Label htmlFor={`field-${path.replace(/\./g, '__')}`} label={label} required={required} />
|
{Label}
|
||||||
<Slate
|
<Slate
|
||||||
editor={editor}
|
editor={editor}
|
||||||
key={JSON.stringify({ initialValue, path })}
|
key={JSON.stringify({ initialValue, path })}
|
||||||
@@ -336,7 +343,7 @@ const RichText: React.FC<FieldProps> = (props) => {
|
|||||||
value={valueToRender as any[]}
|
value={valueToRender as any[]}
|
||||||
>
|
>
|
||||||
<div className={`${baseClass}__wrapper`}>
|
<div className={`${baseClass}__wrapper`}>
|
||||||
{elements?.length + leaves?.length > 0 && (
|
{elements?.length + Object.keys(leaves)?.length > 0 && (
|
||||||
<div
|
<div
|
||||||
className={[`${baseClass}__toolbar`, drawerIsOpen && `${baseClass}__drawerIsOpen`]
|
className={[`${baseClass}__toolbar`, drawerIsOpen && `${baseClass}__drawerIsOpen`]
|
||||||
.filter(Boolean)
|
.filter(Boolean)
|
||||||
@@ -344,7 +351,7 @@ const RichText: React.FC<FieldProps> = (props) => {
|
|||||||
ref={toolbarRef}
|
ref={toolbarRef}
|
||||||
>
|
>
|
||||||
<div className={`${baseClass}__toolbar-wrap`}>
|
<div className={`${baseClass}__toolbar-wrap`}>
|
||||||
{elements.map((element, i) => {
|
{/* {elements.map((element, i) => {
|
||||||
let elementName: string
|
let elementName: string
|
||||||
if (typeof element === 'object' && element?.name) elementName = element.name
|
if (typeof element === 'object' && element?.name) elementName = element.name
|
||||||
if (typeof element === 'string') elementName = element
|
if (typeof element === 'string') elementName = element
|
||||||
@@ -357,17 +364,21 @@ const RichText: React.FC<FieldProps> = (props) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return null
|
return null
|
||||||
})}
|
})} */}
|
||||||
{leaves.map((leaf, i) => {
|
{Object.values(leaves).map((leaf, i) => {
|
||||||
let leafName: string
|
const Button = leaf?.Button
|
||||||
if (typeof leaf === 'object' && leaf?.name) leafName = leaf.name
|
|
||||||
if (typeof leaf === 'string') leafName = leaf
|
|
||||||
|
|
||||||
const leafType = enabledLeaves[leafName]
|
|
||||||
const Button = leafType?.Button
|
|
||||||
|
|
||||||
if (Button) {
|
if (Button) {
|
||||||
return <Button fieldProps={props} key={i} path={path} />
|
return (
|
||||||
|
<LeafButtonProvider
|
||||||
|
fieldProps={props}
|
||||||
|
key={i}
|
||||||
|
path={path}
|
||||||
|
schemaPath={schemaPath}
|
||||||
|
>
|
||||||
|
{Button}
|
||||||
|
</LeafButtonProvider>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return null
|
return null
|
||||||
@@ -450,9 +461,10 @@ const RichText: React.FC<FieldProps> = (props) => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Slate>
|
</Slate>
|
||||||
<FieldDescription description={description} path={path} value={value} />
|
{Description}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default withCondition(RichText)
|
export default withCondition(RichText)
|
||||||
|
|||||||
@@ -1,11 +1,18 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
|
||||||
|
import type { RichTextCustomLeaf } from '../../..'
|
||||||
|
|
||||||
import BoldIcon from '../../icons/Bold'
|
import BoldIcon from '../../icons/Bold'
|
||||||
|
import { useLeaf } from '../../providers/LeafProvider'
|
||||||
import LeafButton from '../Button'
|
import LeafButton from '../Button'
|
||||||
|
|
||||||
const Bold = ({ attributes, children }) => <strong {...attributes}>{children}</strong>
|
const Bold = () => {
|
||||||
|
const { attributes, children } = useLeaf()
|
||||||
|
return <strong {...attributes}>{children}</strong>
|
||||||
|
}
|
||||||
|
|
||||||
const bold = {
|
const bold: RichTextCustomLeaf = {
|
||||||
|
name: 'bold',
|
||||||
Button: () => (
|
Button: () => (
|
||||||
<LeafButton format="bold">
|
<LeafButton format="bold">
|
||||||
<BoldIcon />
|
<BoldIcon />
|
||||||
|
|||||||
@@ -1,11 +1,18 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
|
||||||
|
import type { RichTextCustomLeaf } from '../../..'
|
||||||
|
|
||||||
import CodeIcon from '../../icons/Code'
|
import CodeIcon from '../../icons/Code'
|
||||||
|
import { useLeaf } from '../../providers/LeafProvider'
|
||||||
import LeafButton from '../Button'
|
import LeafButton from '../Button'
|
||||||
|
|
||||||
const Code = ({ attributes, children }) => <code {...attributes}>{children}</code>
|
const Code = () => {
|
||||||
|
const { attributes, children } = useLeaf()
|
||||||
|
return <code {...attributes}>{children}</code>
|
||||||
|
}
|
||||||
|
|
||||||
const code = {
|
const code: RichTextCustomLeaf = {
|
||||||
|
name: 'code',
|
||||||
Button: () => (
|
Button: () => (
|
||||||
<LeafButton format="code">
|
<LeafButton format="code">
|
||||||
<CodeIcon />
|
<CodeIcon />
|
||||||
|
|||||||
@@ -1,13 +1,17 @@
|
|||||||
|
import type { RichTextCustomLeaf } from '../..'
|
||||||
|
|
||||||
import bold from './bold'
|
import bold from './bold'
|
||||||
import code from './code'
|
import code from './code'
|
||||||
import italic from './italic'
|
import italic from './italic'
|
||||||
import strikethrough from './strikethrough'
|
import strikethrough from './strikethrough'
|
||||||
import underline from './underline'
|
import underline from './underline'
|
||||||
|
|
||||||
export default {
|
const defaultLeaves: Record<string, RichTextCustomLeaf> = {
|
||||||
bold,
|
bold,
|
||||||
code,
|
code,
|
||||||
italic,
|
italic,
|
||||||
strikethrough,
|
strikethrough,
|
||||||
underline,
|
underline,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export default defaultLeaves
|
||||||
|
|||||||
@@ -1,11 +1,18 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
|
||||||
|
import type { RichTextCustomLeaf } from '../../..'
|
||||||
|
|
||||||
import ItalicIcon from '../../icons/Italic'
|
import ItalicIcon from '../../icons/Italic'
|
||||||
|
import { useLeaf } from '../../providers/LeafProvider'
|
||||||
import LeafButton from '../Button'
|
import LeafButton from '../Button'
|
||||||
|
|
||||||
const Italic = ({ attributes, children }) => <em {...attributes}>{children}</em>
|
const Italic = () => {
|
||||||
|
const { attributes, children } = useLeaf()
|
||||||
|
return <em {...attributes}>{children}</em>
|
||||||
|
}
|
||||||
|
|
||||||
const italic = {
|
const italic: RichTextCustomLeaf = {
|
||||||
|
name: 'italic',
|
||||||
Button: () => (
|
Button: () => (
|
||||||
<LeafButton format="italic">
|
<LeafButton format="italic">
|
||||||
<ItalicIcon />
|
<ItalicIcon />
|
||||||
|
|||||||
@@ -1,11 +1,19 @@
|
|||||||
|
'use client'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
|
||||||
|
import type { RichTextCustomLeaf } from '../../..'
|
||||||
|
|
||||||
import StrikethroughIcon from '../../icons/Strikethrough'
|
import StrikethroughIcon from '../../icons/Strikethrough'
|
||||||
|
import { useLeaf } from '../../providers/LeafProvider'
|
||||||
import LeafButton from '../Button'
|
import LeafButton from '../Button'
|
||||||
|
|
||||||
const Strikethrough = ({ attributes, children }) => <del {...attributes}>{children}</del>
|
const Strikethrough = () => {
|
||||||
|
const { attributes, children } = useLeaf()
|
||||||
|
return <del {...attributes}>{children}</del>
|
||||||
|
}
|
||||||
|
|
||||||
const strikethrough = {
|
const strikethrough: RichTextCustomLeaf = {
|
||||||
|
name: 'strikethrough',
|
||||||
Button: () => (
|
Button: () => (
|
||||||
<LeafButton format="strikethrough">
|
<LeafButton format="strikethrough">
|
||||||
<StrikethroughIcon />
|
<StrikethroughIcon />
|
||||||
|
|||||||
@@ -1,11 +1,18 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
|
||||||
|
import type { RichTextCustomLeaf } from '../../..'
|
||||||
|
|
||||||
import UnderlineIcon from '../../icons/Underline'
|
import UnderlineIcon from '../../icons/Underline'
|
||||||
|
import { useLeaf } from '../../providers/LeafProvider'
|
||||||
import LeafButton from '../Button'
|
import LeafButton from '../Button'
|
||||||
|
|
||||||
const Underline = ({ attributes, children }) => <u {...attributes}>{children}</u>
|
const Underline = () => {
|
||||||
|
const { attributes, children } = useLeaf()
|
||||||
|
return <u {...attributes}>{children}</u>
|
||||||
|
}
|
||||||
|
|
||||||
const underline = {
|
const underline: RichTextCustomLeaf = {
|
||||||
|
name: 'underline',
|
||||||
Button: () => (
|
Button: () => (
|
||||||
<LeafButton format="underline">
|
<LeafButton format="underline">
|
||||||
<UnderlineIcon />
|
<UnderlineIcon />
|
||||||
|
|||||||
@@ -0,0 +1,35 @@
|
|||||||
|
'use client'
|
||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
type ElementContextType = {
|
||||||
|
path: string
|
||||||
|
schemaPath: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const ElementContext = React.createContext<ElementContextType>({
|
||||||
|
path: '',
|
||||||
|
schemaPath: '',
|
||||||
|
})
|
||||||
|
|
||||||
|
export const ElementProvider: React.FC<{
|
||||||
|
children: React.ReactNode
|
||||||
|
path: string
|
||||||
|
schemaPath: string
|
||||||
|
}> = (props) => {
|
||||||
|
const { children, ...rest } = props
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ElementContext.Provider
|
||||||
|
value={{
|
||||||
|
...rest,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</ElementContext.Provider>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useElement = () => {
|
||||||
|
const path = React.useContext(ElementContext)
|
||||||
|
return path
|
||||||
|
}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
'use client'
|
||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
import type { FormFieldBase } from '../../../../ui/src/forms/fields/shared'
|
||||||
|
|
||||||
|
type LeafButtonContextType = {
|
||||||
|
fieldProps: FormFieldBase & {
|
||||||
|
name: string
|
||||||
|
}
|
||||||
|
path: string
|
||||||
|
schemaPath: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const LeafButtonContext = React.createContext<LeafButtonContextType>({
|
||||||
|
fieldProps: {} as any,
|
||||||
|
path: '',
|
||||||
|
schemaPath: '',
|
||||||
|
})
|
||||||
|
|
||||||
|
export const LeafButtonProvider: React.FC<
|
||||||
|
LeafButtonContextType & {
|
||||||
|
children: React.ReactNode
|
||||||
|
}
|
||||||
|
> = (props) => {
|
||||||
|
const { children, ...rest } = props
|
||||||
|
|
||||||
|
return (
|
||||||
|
<LeafButtonContext.Provider
|
||||||
|
value={{
|
||||||
|
...rest,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</LeafButtonContext.Provider>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useLeafButton = () => {
|
||||||
|
const path = React.useContext(LeafButtonContext)
|
||||||
|
return path
|
||||||
|
}
|
||||||
50
packages/richtext-slate/src/field/providers/LeafProvider.tsx
Normal file
50
packages/richtext-slate/src/field/providers/LeafProvider.tsx
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
'use client'
|
||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
import type { FormFieldBase } from '../../../../ui/src/forms/fields/shared'
|
||||||
|
|
||||||
|
type LeafContextType = {
|
||||||
|
attributes: Record<string, unknown>
|
||||||
|
children: React.ReactNode
|
||||||
|
editorRef: React.MutableRefObject<HTMLDivElement>
|
||||||
|
fieldProps: FormFieldBase & {
|
||||||
|
name: string
|
||||||
|
}
|
||||||
|
leaf: string
|
||||||
|
path: string
|
||||||
|
schemaPath: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const LeafContext = React.createContext<LeafContextType>({
|
||||||
|
attributes: {},
|
||||||
|
children: null,
|
||||||
|
editorRef: null,
|
||||||
|
fieldProps: {} as any,
|
||||||
|
leaf: '',
|
||||||
|
path: '',
|
||||||
|
schemaPath: '',
|
||||||
|
})
|
||||||
|
|
||||||
|
export const LeafProvider: React.FC<
|
||||||
|
LeafContextType & {
|
||||||
|
result: React.ReactNode
|
||||||
|
}
|
||||||
|
> = (props) => {
|
||||||
|
const { children, result, ...rest } = props
|
||||||
|
|
||||||
|
return (
|
||||||
|
<LeafContext.Provider
|
||||||
|
value={{
|
||||||
|
...rest,
|
||||||
|
children: result,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</LeafContext.Provider>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useLeaf = () => {
|
||||||
|
const path = React.useContext(LeafContext)
|
||||||
|
return path
|
||||||
|
}
|
||||||
@@ -2,13 +2,15 @@ import type { RichTextAdapter } from 'payload/types'
|
|||||||
|
|
||||||
import { withMergedProps } from '@payloadcms/ui/utilities'
|
import { withMergedProps } from '@payloadcms/ui/utilities'
|
||||||
import { withNullableJSONSchemaType } from 'payload/utilities'
|
import { withNullableJSONSchemaType } from 'payload/utilities'
|
||||||
|
import React from 'react'
|
||||||
|
|
||||||
import type { AdapterArguments } from './types'
|
import type { AdapterArguments, RichTextCustomLeaf } from './types'
|
||||||
|
|
||||||
import RichTextCell from './cell'
|
import RichTextCell from './cell'
|
||||||
import { richTextRelationshipPromise } from './data/richTextRelationshipPromise'
|
import { richTextRelationshipPromise } from './data/richTextRelationshipPromise'
|
||||||
import { richTextValidate } from './data/validation'
|
import { richTextValidate } from './data/validation'
|
||||||
import RichTextField from './field'
|
import RichTextField from './field'
|
||||||
|
import leafTypes from './field/leaves'
|
||||||
|
|
||||||
export function slateEditor(args: AdapterArguments): RichTextAdapter<any[], AdapterArguments, any> {
|
export function slateEditor(args: AdapterArguments): RichTextAdapter<any[], AdapterArguments, any> {
|
||||||
return {
|
return {
|
||||||
@@ -20,6 +22,29 @@ export function slateEditor(args: AdapterArguments): RichTextAdapter<any[], Adap
|
|||||||
Component: RichTextField,
|
Component: RichTextField,
|
||||||
toMergeIntoProps: args,
|
toMergeIntoProps: args,
|
||||||
}),
|
}),
|
||||||
|
generateComponentMap: () => {
|
||||||
|
const componentMap = new Map()
|
||||||
|
|
||||||
|
;(args?.admin?.leaves || Object.values(leafTypes)).forEach((leaf) => {
|
||||||
|
let leafObject: RichTextCustomLeaf
|
||||||
|
|
||||||
|
if (typeof leaf === 'object' && leaf !== null) {
|
||||||
|
leafObject = leaf
|
||||||
|
} else if (typeof leaf === 'string' && leafTypes[leaf]) {
|
||||||
|
leafObject = leafTypes[leaf]
|
||||||
|
}
|
||||||
|
|
||||||
|
if (leafObject) {
|
||||||
|
const LeafButton = leafObject.Button
|
||||||
|
const LeafComponent = leafObject.Leaf
|
||||||
|
|
||||||
|
componentMap.set(`leaf.button.${leafObject.name}`, <LeafButton />)
|
||||||
|
componentMap.set(`leaf.component.${leafObject.name}`, <LeafComponent />)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return componentMap
|
||||||
|
},
|
||||||
outputSchema: ({ isRequired }) => {
|
outputSchema: ({ isRequired }) => {
|
||||||
return {
|
return {
|
||||||
items: {
|
items: {
|
||||||
@@ -10,6 +10,7 @@ export const RenderField: React.FC<{
|
|||||||
const { path: pathFromContext, schemaPath: schemaPathFromContext } = useFieldPath()
|
const { path: pathFromContext, schemaPath: schemaPathFromContext } = useFieldPath()
|
||||||
const path = `${pathFromContext ? `${pathFromContext}.` : ''}${name || ''}`
|
const path = `${pathFromContext ? `${pathFromContext}.` : ''}${name || ''}`
|
||||||
const schemaPath = `${schemaPathFromContext ? `${schemaPathFromContext}.` : ''}${name || ''}`
|
const schemaPath = `${schemaPathFromContext ? `${schemaPathFromContext}.` : ''}${name || ''}`
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FieldPathProvider path={path} schemaPath={schemaPath}>
|
<FieldPathProvider path={path} schemaPath={schemaPath}>
|
||||||
{Field}
|
{Field}
|
||||||
|
|||||||
@@ -1,35 +1,37 @@
|
|||||||
'use client'
|
|
||||||
import React, { useMemo } from 'react'
|
import React, { useMemo } from 'react'
|
||||||
|
|
||||||
import type { RichTextAdapter, RichTextField } from 'payload/types'
|
import type { RichTextAdapter } from 'payload/types'
|
||||||
|
import { Props } from './types'
|
||||||
|
|
||||||
const RichText: React.FC<RichTextField> = (fieldprops) => {
|
const RichText: React.FC<Props> = (props) => {
|
||||||
// eslint-disable-next-line react/destructuring-assignment
|
console.log(props)
|
||||||
const editor: RichTextAdapter = fieldprops.editor
|
return null
|
||||||
|
// // eslint-disable-next-line react/destructuring-assignment
|
||||||
|
// const editor: RichTextAdapter = fieldprops.editor
|
||||||
|
|
||||||
const isLazy = 'LazyFieldComponent' in editor
|
// const isLazy = 'LazyFieldComponent' in editor
|
||||||
|
|
||||||
const ImportedFieldComponent: React.FC<any> = useMemo(() => {
|
// const ImportedFieldComponent: React.FC<any> = useMemo(() => {
|
||||||
return isLazy
|
// return isLazy
|
||||||
? React.lazy(() => {
|
// ? React.lazy(() => {
|
||||||
return editor.LazyFieldComponent().then((resolvedComponent) => ({
|
// return editor.LazyFieldComponent().then((resolvedComponent) => ({
|
||||||
default: resolvedComponent,
|
// default: resolvedComponent,
|
||||||
}))
|
// }))
|
||||||
})
|
// })
|
||||||
: null
|
// : null
|
||||||
}, [editor, isLazy])
|
// }, [editor, isLazy])
|
||||||
|
|
||||||
if (isLazy) {
|
// if (isLazy) {
|
||||||
return (
|
// return (
|
||||||
ImportedFieldComponent && (
|
// ImportedFieldComponent && (
|
||||||
<React.Suspense>
|
// <React.Suspense>
|
||||||
<ImportedFieldComponent {...fieldprops} />
|
// <ImportedFieldComponent {...fieldprops} />
|
||||||
</React.Suspense>
|
// </React.Suspense>
|
||||||
)
|
// )
|
||||||
)
|
// )
|
||||||
}
|
// }
|
||||||
|
|
||||||
return <editor.FieldComponent {...fieldprops} />
|
// return <editor.FieldComponent {...fieldprops} />
|
||||||
}
|
}
|
||||||
|
|
||||||
export default RichText
|
export default RichText
|
||||||
|
|||||||
3
packages/ui/src/forms/fields/RichText/types.ts
Normal file
3
packages/ui/src/forms/fields/RichText/types.ts
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
import { FormFieldBase } from '../shared'
|
||||||
|
|
||||||
|
export type Props = FormFieldBase
|
||||||
@@ -8,6 +8,7 @@ import {
|
|||||||
DocumentPreferences,
|
DocumentPreferences,
|
||||||
JSONField,
|
JSONField,
|
||||||
RelationshipField,
|
RelationshipField,
|
||||||
|
RichTextField,
|
||||||
RowLabel,
|
RowLabel,
|
||||||
UploadField,
|
UploadField,
|
||||||
Validate,
|
Validate,
|
||||||
@@ -108,6 +109,10 @@ export type FormFieldBase = {
|
|||||||
// For `relationship` fields
|
// For `relationship` fields
|
||||||
relationTo?: RelationshipField['relationTo']
|
relationTo?: RelationshipField['relationTo']
|
||||||
}
|
}
|
||||||
|
| {
|
||||||
|
// For `richText` fields
|
||||||
|
richTextComponentMap?: Map<string, React.ReactNode>
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import DefaultLabel from '../../forms/Label'
|
|||||||
import DefaultError from '../../forms/Error'
|
import DefaultError from '../../forms/Error'
|
||||||
import DefaultDescription from '../../forms/FieldDescription'
|
import DefaultDescription from '../../forms/FieldDescription'
|
||||||
import { HiddenInput } from '../..'
|
import { HiddenInput } from '../..'
|
||||||
|
import { richText } from 'payload/fields/validations'
|
||||||
|
|
||||||
export const mapFields = (args: {
|
export const mapFields = (args: {
|
||||||
fieldSchema: FieldWithPath[]
|
fieldSchema: FieldWithPath[]
|
||||||
@@ -224,9 +225,10 @@ export const mapFields = (args: {
|
|||||||
tabs,
|
tabs,
|
||||||
blocks,
|
blocks,
|
||||||
relationTo: 'relationTo' in field ? field.relationTo : undefined,
|
relationTo: 'relationTo' in field ? field.relationTo : undefined,
|
||||||
|
richTextComponentMap: undefined,
|
||||||
}
|
}
|
||||||
|
|
||||||
const Field = <FieldComponent {...fieldComponentProps} />
|
let Field = <FieldComponent {...fieldComponentProps} />
|
||||||
|
|
||||||
const cellComponentProps: CellProps = {
|
const cellComponentProps: CellProps = {
|
||||||
fieldType: field.type,
|
fieldType: field.type,
|
||||||
@@ -247,6 +249,34 @@ export const mapFields = (args: {
|
|||||||
options: 'options' in field ? field.options : undefined,
|
options: 'options' in field ? field.options : undefined,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (field.type === 'richText' && 'editor' in field) {
|
||||||
|
let RichTextComponent
|
||||||
|
|
||||||
|
const isLazy = 'LazyFieldComponent' in field.editor
|
||||||
|
|
||||||
|
if (isLazy) {
|
||||||
|
RichTextComponent = React.lazy(() => {
|
||||||
|
return 'LazyFieldComponent' in field.editor
|
||||||
|
? field.editor.LazyFieldComponent().then((resolvedComponent) => ({
|
||||||
|
default: resolvedComponent,
|
||||||
|
}))
|
||||||
|
: null
|
||||||
|
})
|
||||||
|
} else if ('FieldComponent' in field.editor) {
|
||||||
|
RichTextComponent = field.editor.FieldComponent
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof field.editor.generateComponentMap === 'function') {
|
||||||
|
const result = field.editor.generateComponentMap()
|
||||||
|
// @ts-ignore-next-line // TODO: the `richTextComponentMap` is not found on the union type
|
||||||
|
fieldComponentProps.richTextComponentMap = result
|
||||||
|
}
|
||||||
|
|
||||||
|
if (RichTextComponent) {
|
||||||
|
Field = <RichTextComponent {...fieldComponentProps} />
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const reducedField: MappedField = {
|
const reducedField: MappedField = {
|
||||||
name: 'name' in field ? field.name : '',
|
name: 'name' in field ? field.name : '',
|
||||||
label: 'label' in field && typeof field.label !== 'function' ? field.label : undefined,
|
label: 'label' in field && typeof field.label !== 'function' ? field.label : undefined,
|
||||||
@@ -271,8 +301,8 @@ export const mapFields = (args: {
|
|||||||
'label' in field && field.label && typeof field.label !== 'function'
|
'label' in field && field.label && typeof field.label !== 'function'
|
||||||
? field.label
|
? field.label
|
||||||
: 'name' in field
|
: 'name' in field
|
||||||
? field.name
|
? field.name
|
||||||
: undefined
|
: undefined
|
||||||
}
|
}
|
||||||
name={'name' in field ? field.name : undefined}
|
name={'name' in field ? field.name : undefined}
|
||||||
/>
|
/>
|
||||||
|
|||||||
15
packages/ui/src/utilities/buildComponentMap/mapRichText.tsx
Normal file
15
packages/ui/src/utilities/buildComponentMap/mapRichText.tsx
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import { RichTextField } from 'payload/types'
|
||||||
|
|
||||||
|
type elements = {
|
||||||
|
Button: React.ReactNode
|
||||||
|
}[]
|
||||||
|
|
||||||
|
export const mapRichText = (field: RichTextField) => {
|
||||||
|
let cellComponent = null
|
||||||
|
let leafComponent = null
|
||||||
|
|
||||||
|
let elements
|
||||||
|
|
||||||
|
if ('editor' in field) {
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,6 +7,7 @@ import {
|
|||||||
TabsField,
|
TabsField,
|
||||||
Option,
|
Option,
|
||||||
Labels,
|
Labels,
|
||||||
|
RichTextField,
|
||||||
} from 'payload/types'
|
} from 'payload/types'
|
||||||
import { fieldTypes } from '../../forms/fields'
|
import { fieldTypes } from '../../forms/fields'
|
||||||
|
|
||||||
@@ -52,6 +53,10 @@ export type MappedField = {
|
|||||||
*/
|
*/
|
||||||
options?: Option[]
|
options?: Option[]
|
||||||
hasMany?: boolean
|
hasMany?: boolean
|
||||||
|
/**
|
||||||
|
* On `richText` fields only
|
||||||
|
*/
|
||||||
|
editor?: RichTextField['editor']
|
||||||
}
|
}
|
||||||
|
|
||||||
export type FieldMap = MappedField[]
|
export type FieldMap = MappedField[]
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ export const getFormState = async (args: {
|
|||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
},
|
},
|
||||||
|
credentials: 'include',
|
||||||
body: JSON.stringify(body),
|
body: JSON.stringify(body),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,10 @@ export const PostsCollection: CollectionConfig = {
|
|||||||
name: 'text',
|
name: 'text',
|
||||||
type: 'text',
|
type: 'text',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'richText',
|
||||||
|
type: 'richText',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: 'associatedMedia',
|
name: 'associatedMedia',
|
||||||
access: {
|
access: {
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import type { Config, SanitizedConfig } from '../packages/payload/src/config/typ
|
|||||||
import { mongooseAdapter } from '../packages/db-mongodb/src'
|
import { mongooseAdapter } from '../packages/db-mongodb/src'
|
||||||
import { postgresAdapter } from '../packages/db-postgres/src'
|
import { postgresAdapter } from '../packages/db-postgres/src'
|
||||||
import { buildConfig as buildPayloadConfig } from '../packages/payload/src/config/build'
|
import { buildConfig as buildPayloadConfig } from '../packages/payload/src/config/build'
|
||||||
|
import { slateEditor } from '../packages/richtext-slate/src'
|
||||||
|
|
||||||
// process.env.PAYLOAD_DATABASE = 'postgres'
|
// process.env.PAYLOAD_DATABASE = 'postgres'
|
||||||
|
|
||||||
@@ -24,8 +25,7 @@ const databaseAdapters = {
|
|||||||
export function buildConfigWithDefaults(testConfig?: Partial<Config>): Promise<SanitizedConfig> {
|
export function buildConfigWithDefaults(testConfig?: Partial<Config>): Promise<SanitizedConfig> {
|
||||||
const config: Config = {
|
const config: Config = {
|
||||||
secret: 'TEST_SECRET',
|
secret: 'TEST_SECRET',
|
||||||
// editor: slateEditor({}),
|
editor: slateEditor({}),
|
||||||
editor: undefined,
|
|
||||||
rateLimit: {
|
rateLimit: {
|
||||||
max: 9999999999,
|
max: 9999999999,
|
||||||
window: 15 * 60 * 1000, // 15min default,
|
window: 15 * 60 * 1000, // 15min default,
|
||||||
|
|||||||
Reference in New Issue
Block a user