Compare commits
28 Commits
db-postgre
...
db-postgre
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
27589482dd | ||
|
|
d7ab4b7062 | ||
|
|
2c8fbf1be3 | ||
|
|
eec88f8f1b | ||
|
|
1481ef97b5 | ||
|
|
a89e89fb80 | ||
|
|
7e7eeb059d | ||
|
|
dc2a502dcc | ||
|
|
a9a5ba82d8 | ||
|
|
e6e8fae1c5 | ||
|
|
a05868a7f3 | ||
|
|
f27cd26575 | ||
|
|
db835ea5c8 | ||
|
|
d8f265fb94 | ||
|
|
d81d4eb075 | ||
|
|
52f89c0136 | ||
|
|
b0083b7c07 | ||
|
|
21649537a6 | ||
|
|
9be34c9599 | ||
|
|
8ca632e541 | ||
|
|
2ef79145a4 | ||
|
|
a0641a445d | ||
|
|
3a2e78f7f3 | ||
|
|
976d69d154 | ||
|
|
66018362fe | ||
|
|
647fe23d1c | ||
|
|
d7c61861f6 | ||
|
|
2c67eff059 |
@@ -131,6 +131,7 @@ const result = await payload.find({
|
||||
depth: 2,
|
||||
page: 1,
|
||||
limit: 10,
|
||||
pagination: false, // If you want to disable pagination count, etc.
|
||||
where: {}, // pass a `where` query here
|
||||
sort: '-title',
|
||||
locale: 'en',
|
||||
|
||||
@@ -59,3 +59,7 @@ All Payload APIs support the pagination controls below. With them, you can creat
|
||||
| ------- | --------------------------------------- |
|
||||
| `limit` | Limits the number of documents returned |
|
||||
| `page` | Get a specific page number |
|
||||
|
||||
### Disabling pagination within Local API
|
||||
|
||||
For `find` operations within the Local API, you can disable pagination to retrieve all documents from a collection by passing `pagination: false` to the `find` local operation. This is not supported in REST or GraphQL, however, because it could potentially lead to malicious activity.
|
||||
@@ -17,9 +17,9 @@
|
||||
"lint:fix": "eslint --fix --ext .ts,.tsx src"
|
||||
},
|
||||
"dependencies": {
|
||||
"@payloadcms/bundler-webpack": "^1.0.0-beta.5",
|
||||
"@payloadcms/db-mongodb": "^1.0.0-beta.8",
|
||||
"@payloadcms/richtext-slate": "^1.0.0-beta.4",
|
||||
"@payloadcms/bundler-webpack": "latest",
|
||||
"@payloadcms/db-mongodb": "latest",
|
||||
"@payloadcms/richtext-slate": "latest",
|
||||
"dotenv": "^8.2.0",
|
||||
"express": "^4.17.1",
|
||||
"payload": "latest"
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -20,9 +20,9 @@
|
||||
"lint:fix": "eslint --fix --ext .ts,.tsx src"
|
||||
},
|
||||
"dependencies": {
|
||||
"@payloadcms/bundler-webpack": "^1.0.0-beta.5",
|
||||
"@payloadcms/db-mongodb": "^1.0.0-beta.8",
|
||||
"@payloadcms/richtext-slate": "^1.0.0-beta.4",
|
||||
"@payloadcms/bundler-webpack": "latest",
|
||||
"@payloadcms/db-mongodb": "latest",
|
||||
"@payloadcms/richtext-slate": "latest",
|
||||
"dotenv": "^8.2.0",
|
||||
"escape-html": "^1.0.3",
|
||||
"express": "^4.17.1",
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -18,9 +18,9 @@
|
||||
"lint:fix": "eslint --fix --ext .ts,.tsx src"
|
||||
},
|
||||
"dependencies": {
|
||||
"@payloadcms/bundler-webpack": "^1.0.0-beta.5",
|
||||
"@payloadcms/db-mongodb": "^1.0.0-beta.8",
|
||||
"@payloadcms/richtext-slate": "^1.0.0-beta.4",
|
||||
"@payloadcms/bundler-webpack": "latest",
|
||||
"@payloadcms/db-mongodb": "latest",
|
||||
"@payloadcms/richtext-slate": "latest",
|
||||
"dotenv": "^8.2.0",
|
||||
"express": "^4.17.1",
|
||||
"payload": "latest"
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -15,9 +15,9 @@
|
||||
"generate:graphQLSchema": "PAYLOAD_CONFIG_PATH=src/payload.config.ts payload generate:graphQLSchema"
|
||||
},
|
||||
"dependencies": {
|
||||
"@payloadcms/bundler-webpack": "^1.0.0-beta.5",
|
||||
"@payloadcms/db-mongodb": "^1.0.0-beta.8",
|
||||
"@payloadcms/richtext-slate": "^1.0.0-beta.4",
|
||||
"@payloadcms/bundler-webpack": "latest",
|
||||
"@payloadcms/db-mongodb": "latest",
|
||||
"@payloadcms/richtext-slate": "latest",
|
||||
"@faceless-ui/modal": "^2.0.1",
|
||||
"@payloadcms/plugin-form-builder": "^1.0.12",
|
||||
"@payloadcms/plugin-seo": "^1.0.8",
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -18,9 +18,9 @@
|
||||
"lint:fix": "eslint --fix --ext .ts,.tsx src"
|
||||
},
|
||||
"dependencies": {
|
||||
"@payloadcms/bundler-webpack": "^1.0.0-beta.5",
|
||||
"@payloadcms/db-mongodb": "^1.0.0-beta.8",
|
||||
"@payloadcms/richtext-slate": "^1.0.0-beta.4",
|
||||
"@payloadcms/bundler-webpack": "latest",
|
||||
"@payloadcms/db-mongodb": "latest",
|
||||
"@payloadcms/richtext-slate": "latest",
|
||||
"dotenv": "^8.2.0",
|
||||
"express": "^4.17.1",
|
||||
"payload": "latest"
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -18,9 +18,9 @@
|
||||
"lint:fix": "eslint --fix --ext .ts,.tsx src"
|
||||
},
|
||||
"dependencies": {
|
||||
"@payloadcms/bundler-webpack": "^1.0.0-beta.5",
|
||||
"@payloadcms/db-mongodb": "^1.0.0-beta.8",
|
||||
"@payloadcms/richtext-slate": "^1.0.0-beta.4",
|
||||
"@payloadcms/bundler-webpack": "latest",
|
||||
"@payloadcms/db-mongodb": "latest",
|
||||
"@payloadcms/richtext-slate": "latest",
|
||||
"dotenv": "^8.2.0",
|
||||
"express": "^4.17.1",
|
||||
"payload": "latest"
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -17,9 +17,9 @@
|
||||
"lint:fix": "eslint --fix --ext .ts,.tsx src"
|
||||
},
|
||||
"dependencies": {
|
||||
"@payloadcms/bundler-webpack": "^1.0.0-beta.5",
|
||||
"@payloadcms/db-mongodb": "^1.0.0-beta.8",
|
||||
"@payloadcms/richtext-slate": "^1.0.0-beta.4",
|
||||
"@payloadcms/bundler-webpack": "latest",
|
||||
"@payloadcms/db-mongodb": "latest",
|
||||
"@payloadcms/richtext-slate": "latest",
|
||||
"@payloadcms/plugin-redirects": "^1.0.0",
|
||||
"dotenv": "^8.2.0",
|
||||
"express": "^4.17.1",
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/db-postgres",
|
||||
"version": "0.1.5",
|
||||
"version": "0.1.7",
|
||||
"description": "The officially supported Postgres database adapter for Payload",
|
||||
"repository": "https://github.com/payloadcms/payload",
|
||||
"license": "MIT",
|
||||
@@ -34,9 +34,6 @@
|
||||
"@types/to-snake-case": "1.0.0",
|
||||
"payload": "workspace:*"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"better-sqlite3": "^8.5.0"
|
||||
},
|
||||
"publishConfig": {
|
||||
"main": "./dist/index.js",
|
||||
"registry": "https://registry.npmjs.org/",
|
||||
|
||||
@@ -34,12 +34,14 @@ type Args = {
|
||||
aliasTable?: GenericTable
|
||||
collectionPath: string
|
||||
columnPrefix?: string
|
||||
constraintPath?: string
|
||||
constraints?: Constraint[]
|
||||
fields: (Field | TabAsField)[]
|
||||
joinAliases: BuildQueryJoinAliases
|
||||
joins: BuildQueryJoins
|
||||
locale?: string
|
||||
pathSegments: string[]
|
||||
rootTableName?: string
|
||||
selectFields: Record<string, GenericColumn>
|
||||
tableName: string
|
||||
}
|
||||
@@ -53,17 +55,22 @@ export const getTableColumnFromPath = ({
|
||||
aliasTable,
|
||||
collectionPath,
|
||||
columnPrefix = '',
|
||||
constraintPath: incomingConstraintPath,
|
||||
constraints = [],
|
||||
fields,
|
||||
joinAliases,
|
||||
joins,
|
||||
locale: incomingLocale,
|
||||
pathSegments: incomingSegments,
|
||||
rootTableName: incomingRootTableName,
|
||||
selectFields,
|
||||
tableName,
|
||||
}: Args): TableColumn => {
|
||||
const fieldPath = incomingSegments[0]
|
||||
let locale = incomingLocale
|
||||
const rootTableName = incomingRootTableName || tableName
|
||||
let constraintPath = incomingConstraintPath || ''
|
||||
|
||||
const field = flattenTopLevelFields(fields as Field[]).find(
|
||||
(fieldToFind) => fieldAffectsData(fieldToFind) && fieldToFind.name === fieldPath,
|
||||
) as Field | TabAsField
|
||||
@@ -105,6 +112,7 @@ export const getTableColumnFromPath = ({
|
||||
aliasTable,
|
||||
collectionPath,
|
||||
columnPrefix,
|
||||
constraintPath,
|
||||
constraints,
|
||||
fields: field.tabs.map((tab) => ({
|
||||
...tab,
|
||||
@@ -114,6 +122,7 @@ export const getTableColumnFromPath = ({
|
||||
joins,
|
||||
locale,
|
||||
pathSegments: pathSegments.slice(1),
|
||||
rootTableName,
|
||||
selectFields,
|
||||
tableName: newTableName,
|
||||
})
|
||||
@@ -125,12 +134,14 @@ export const getTableColumnFromPath = ({
|
||||
aliasTable,
|
||||
collectionPath,
|
||||
columnPrefix: `${columnPrefix}${field.name}_`,
|
||||
constraintPath,
|
||||
constraints,
|
||||
fields: field.fields,
|
||||
joinAliases,
|
||||
joins,
|
||||
locale,
|
||||
pathSegments: pathSegments.slice(1),
|
||||
rootTableName,
|
||||
selectFields,
|
||||
tableName: newTableName,
|
||||
})
|
||||
@@ -140,12 +151,14 @@ export const getTableColumnFromPath = ({
|
||||
aliasTable,
|
||||
collectionPath,
|
||||
columnPrefix,
|
||||
constraintPath,
|
||||
constraints,
|
||||
fields: field.fields,
|
||||
joinAliases,
|
||||
joins,
|
||||
locale,
|
||||
pathSegments: pathSegments.slice(1),
|
||||
rootTableName,
|
||||
selectFields,
|
||||
tableName: newTableName,
|
||||
})
|
||||
@@ -172,12 +185,14 @@ export const getTableColumnFromPath = ({
|
||||
aliasTable,
|
||||
collectionPath,
|
||||
columnPrefix: `${columnPrefix}${field.name}_`,
|
||||
constraintPath,
|
||||
constraints,
|
||||
fields: field.fields,
|
||||
joinAliases,
|
||||
joins,
|
||||
locale,
|
||||
pathSegments: pathSegments.slice(1),
|
||||
rootTableName,
|
||||
selectFields,
|
||||
tableName: newTableName,
|
||||
})
|
||||
@@ -185,6 +200,7 @@ export const getTableColumnFromPath = ({
|
||||
|
||||
case 'array': {
|
||||
newTableName = `${tableName}_${toSnakeCase(field.name)}`
|
||||
constraintPath = `${constraintPath}${field.name}.%.`
|
||||
if (locale && field.localized && adapter.payload.config.localization) {
|
||||
joins[newTableName] = and(
|
||||
eq(adapter.tables[tableName].id, adapter.tables[newTableName]._parentID),
|
||||
@@ -206,12 +222,14 @@ export const getTableColumnFromPath = ({
|
||||
return getTableColumnFromPath({
|
||||
adapter,
|
||||
collectionPath,
|
||||
constraintPath,
|
||||
constraints,
|
||||
fields: field.fields,
|
||||
joinAliases,
|
||||
joins,
|
||||
locale,
|
||||
pathSegments: pathSegments.slice(1),
|
||||
rootTableName,
|
||||
selectFields,
|
||||
tableName: newTableName,
|
||||
})
|
||||
@@ -229,12 +247,14 @@ export const getTableColumnFromPath = ({
|
||||
result = getTableColumnFromPath({
|
||||
adapter,
|
||||
collectionPath,
|
||||
constraintPath: '',
|
||||
constraints: blockConstraints,
|
||||
fields: block.fields,
|
||||
joinAliases,
|
||||
joins,
|
||||
locale,
|
||||
pathSegments: pathSegments.slice(1),
|
||||
rootTableName,
|
||||
selectFields: blockSelectFields,
|
||||
tableName: newTableName,
|
||||
})
|
||||
@@ -283,9 +303,8 @@ export const getTableColumnFromPath = ({
|
||||
case 'relationship':
|
||||
case 'upload': {
|
||||
let relationshipFields
|
||||
const relationTableName = `${tableName}_rels`
|
||||
const relationTableName = `${rootTableName}_rels`
|
||||
const newCollectionPath = pathSegments.slice(1).join('.')
|
||||
|
||||
const aliasRelationshipTableName = uuid()
|
||||
const aliasRelationshipTable = alias(
|
||||
adapter.tables[relationTableName],
|
||||
@@ -295,7 +314,7 @@ export const getTableColumnFromPath = ({
|
||||
// Join in the relationships table
|
||||
joinAliases.push({
|
||||
condition: eq(
|
||||
(aliasTable || adapter.tables[tableName]).id,
|
||||
(aliasTable || adapter.tables[rootTableName]).id,
|
||||
aliasRelationshipTable.parent,
|
||||
),
|
||||
table: aliasRelationshipTable,
|
||||
@@ -306,7 +325,7 @@ export const getTableColumnFromPath = ({
|
||||
constraints.push({
|
||||
columnName: 'path',
|
||||
table: aliasRelationshipTable,
|
||||
value: field.name,
|
||||
value: `${constraintPath}${field.name}`,
|
||||
})
|
||||
|
||||
let newAliasTable
|
||||
@@ -368,6 +387,7 @@ export const getTableColumnFromPath = ({
|
||||
joins,
|
||||
locale,
|
||||
pathSegments: pathSegments.slice(1),
|
||||
rootTableName: newTableName,
|
||||
selectFields,
|
||||
tableName: newTableName,
|
||||
})
|
||||
|
||||
@@ -100,7 +100,11 @@ export async function parseParams({
|
||||
const val = where[relationOrPath][operator]
|
||||
|
||||
queryConstraints.forEach(({ columnName: col, table: constraintTable, value }) => {
|
||||
constraints.push(operatorMap.equals(constraintTable[col], value))
|
||||
if (typeof value === 'string' && value.indexOf('%') > -1) {
|
||||
constraints.push(operatorMap.like(constraintTable[col], value))
|
||||
} else {
|
||||
constraints.push(operatorMap.equals(constraintTable[col], value))
|
||||
}
|
||||
})
|
||||
|
||||
if (['json', 'richText'].includes(field.type) && Array.isArray(pathSegments)) {
|
||||
|
||||
@@ -252,6 +252,8 @@ export const traverseFields = ({
|
||||
}
|
||||
|
||||
case 'array': {
|
||||
const disableNotNullFromHere = Boolean(field.admin?.condition) || disableNotNull
|
||||
|
||||
const arrayTableName = `${newTableName}_${toSnakeCase(field.name)}`
|
||||
const baseColumns: Record<string, PgColumnBuilder> = {
|
||||
_order: integer('_order').notNull(),
|
||||
@@ -277,7 +279,7 @@ export const traverseFields = ({
|
||||
adapter,
|
||||
baseColumns,
|
||||
baseExtraConfig,
|
||||
disableNotNull,
|
||||
disableNotNull: disableNotNullFromHere,
|
||||
disableUnique,
|
||||
fields: field.fields,
|
||||
rootRelationsToBuild,
|
||||
@@ -314,6 +316,8 @@ export const traverseFields = ({
|
||||
}
|
||||
|
||||
case 'blocks': {
|
||||
const disableNotNullFromHere = Boolean(field.admin?.condition) || disableNotNull
|
||||
|
||||
field.blocks.forEach((block) => {
|
||||
const blockTableName = `${rootTableName}_blocks_${toSnakeCase(block.slug)}`
|
||||
if (!adapter.tables[blockTableName]) {
|
||||
@@ -343,7 +347,7 @@ export const traverseFields = ({
|
||||
adapter,
|
||||
baseColumns,
|
||||
baseExtraConfig,
|
||||
disableNotNull,
|
||||
disableNotNull: disableNotNullFromHere,
|
||||
disableUnique,
|
||||
fields: block.fields,
|
||||
rootRelationsToBuild,
|
||||
@@ -428,6 +432,8 @@ export const traverseFields = ({
|
||||
break
|
||||
}
|
||||
|
||||
const disableNotNullFromHere = Boolean(field.admin?.condition) || disableNotNull
|
||||
|
||||
const {
|
||||
hasLocalizedField: groupHasLocalizedField,
|
||||
hasLocalizedManyNumberField: groupHasLocalizedManyNumberField,
|
||||
@@ -438,7 +444,7 @@ export const traverseFields = ({
|
||||
buildRelationships,
|
||||
columnPrefix: `${columnName}_`,
|
||||
columns,
|
||||
disableNotNull,
|
||||
disableNotNull: disableNotNullFromHere,
|
||||
disableUnique,
|
||||
fieldPrefix: `${fieldName}_`,
|
||||
fields: field.fields,
|
||||
@@ -463,6 +469,8 @@ export const traverseFields = ({
|
||||
}
|
||||
|
||||
case 'tabs': {
|
||||
const disableNotNullFromHere = Boolean(field.admin?.condition) || disableNotNull
|
||||
|
||||
const {
|
||||
hasLocalizedField: tabHasLocalizedField,
|
||||
hasLocalizedManyNumberField: tabHasLocalizedManyNumberField,
|
||||
@@ -473,7 +481,7 @@ export const traverseFields = ({
|
||||
buildRelationships,
|
||||
columnPrefix,
|
||||
columns,
|
||||
disableNotNull,
|
||||
disableNotNull: disableNotNullFromHere,
|
||||
disableUnique,
|
||||
fieldPrefix,
|
||||
fields: field.tabs.map((tab) => ({ ...tab, type: 'tab' })),
|
||||
@@ -500,6 +508,7 @@ export const traverseFields = ({
|
||||
|
||||
case 'row':
|
||||
case 'collapsible': {
|
||||
const disableNotNullFromHere = Boolean(field.admin?.condition) || disableNotNull
|
||||
const {
|
||||
hasLocalizedField: rowHasLocalizedField,
|
||||
hasLocalizedManyNumberField: rowHasLocalizedManyNumberField,
|
||||
@@ -510,7 +519,7 @@ export const traverseFields = ({
|
||||
buildRelationships,
|
||||
columnPrefix,
|
||||
columns,
|
||||
disableNotNull,
|
||||
disableNotNull: disableNotNullFromHere,
|
||||
disableUnique,
|
||||
fieldPrefix,
|
||||
fields: field.fields,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "payload",
|
||||
"version": "2.0.5",
|
||||
"version": "2.0.6",
|
||||
"description": "Node, React and MongoDB Headless CMS and Application Framework",
|
||||
"license": "MIT",
|
||||
"main": "./dist/index.js",
|
||||
|
||||
@@ -18,16 +18,18 @@ export {
|
||||
*/
|
||||
useWatchForm,
|
||||
} from '../../admin/components/forms/Form/context'
|
||||
|
||||
export { createNestedFieldPath } from '../../admin/components/forms/Form/createNestedFieldPath'
|
||||
|
||||
export { default as getSiblingData } from '../../admin/components/forms/Form/getSiblingData'
|
||||
|
||||
export { default as reduceFieldsToValues } from '../../admin/components/forms/Form/reduceFieldsToValues'
|
||||
|
||||
export { default as Label } from '../../admin/components/forms/Label'
|
||||
export { default as RenderFields } from '../../admin/components/forms/RenderFields'
|
||||
|
||||
export { default as RenderFields } from '../../admin/components/forms/RenderFields'
|
||||
export { default as Submit } from '../../admin/components/forms/Submit'
|
||||
|
||||
export { default as FormSubmit } from '../../admin/components/forms/Submit'
|
||||
export { fieldTypes } from '../../admin/components/forms/field-types'
|
||||
export { default as Checkbox } from '../../admin/components/forms/field-types/Checkbox'
|
||||
|
||||
export { default as Collapsible } from '../../admin/components/forms/field-types/Collapsible'
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
export { buildConfig } from '../config/build'
|
||||
export * from '../config/types'
|
||||
|
||||
export { type FieldTypes, fieldTypes } from '../admin/components/forms/field-types'
|
||||
export { type FieldTypes } from '../admin/components/forms/field-types'
|
||||
export { defaults } from '../config/defaults'
|
||||
export { sanitizeConfig } from '../config/sanitize'
|
||||
export { baseBlockFields } from '../fields/baseFields/baseBlockFields'
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
export { withMergedProps } from '../admin/components/utilities/WithMergedProps'
|
||||
export { extractTranslations } from '../translations/extractTranslations'
|
||||
export { i18nInit } from '../translations/init'
|
||||
|
||||
export { i18nInit } from '../translations/init'
|
||||
export { combineMerge } from '../utilities/combineMerge'
|
||||
export { configToJSONSchema, entityToJSONSchema } from '../utilities/configToJSONSchema'
|
||||
export { createArrayFromCommaDelineated } from '../utilities/createArrayFromCommaDelineated'
|
||||
|
||||
export { createArrayFromCommaDelineated } from '../utilities/createArrayFromCommaDelineated'
|
||||
export { deepCopyObject } from '../utilities/deepCopyObject'
|
||||
export { deepMerge } from '../utilities/deepMerge'
|
||||
export { default as flattenTopLevelFields } from '../utilities/flattenTopLevelFields'
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/richtext-lexical",
|
||||
"version": "0.1.8",
|
||||
"version": "0.1.11",
|
||||
"description": "The officially supported Lexical richtext adapter for Payload",
|
||||
"repository": "https://github.com/payloadcms/payload",
|
||||
"license": "MIT",
|
||||
@@ -55,6 +55,9 @@
|
||||
"@types/react": "18.2.15",
|
||||
"payload": "workspace:*"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"payload": "^2.0.6"
|
||||
},
|
||||
"exports": {
|
||||
".": {
|
||||
"default": "./src/index.ts",
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
'use client'
|
||||
import type { SerializedEditorState } from 'lexical'
|
||||
import type { CellComponentProps, RichTextField } from 'payload/types'
|
||||
|
||||
|
||||
@@ -47,7 +47,7 @@ const RichText: React.FC<FieldProps> = (props) => {
|
||||
validate: memoizedValidate,
|
||||
})
|
||||
|
||||
const { errorMessage, initialValue, setValue, showError, value } = fieldType
|
||||
const { errorMessage, setValue, showError, value } = fieldType
|
||||
|
||||
let valueToUse = value
|
||||
|
||||
@@ -87,7 +87,6 @@ const RichText: React.FC<FieldProps> = (props) => {
|
||||
<LexicalProvider
|
||||
editorConfig={editorConfig}
|
||||
fieldProps={props}
|
||||
initialState={initialValue}
|
||||
onChange={(editorState, editor, tags) => {
|
||||
let serializedEditorState = editorState.toJSON()
|
||||
|
||||
@@ -101,7 +100,6 @@ const RichText: React.FC<FieldProps> = (props) => {
|
||||
setValue(serializedEditorState)
|
||||
}}
|
||||
readOnly={readOnly}
|
||||
setValue={setValue}
|
||||
value={value}
|
||||
/>
|
||||
<FieldDescription description={description} value={value} />
|
||||
|
||||
@@ -8,7 +8,7 @@ import { SectionTitle } from 'payload/components/fields/Blocks'
|
||||
import { RenderFields, createNestedFieldPath, useFormSubmitted } from 'payload/components/forms'
|
||||
import { useDocumentInfo } from 'payload/components/utilities'
|
||||
import { getTranslation } from 'payload/utilities'
|
||||
import React, { useCallback, useEffect } from 'react'
|
||||
import React, { useCallback } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
import type { FieldProps } from '../../../../types'
|
||||
|
||||
@@ -2,7 +2,7 @@ import { Drawer } from 'payload/components/elements'
|
||||
import { Form } from 'payload/components/forms'
|
||||
import { RenderFields } from 'payload/components/forms'
|
||||
import { FormSubmit } from 'payload/components/forms'
|
||||
import { fieldTypes } from 'payload/config'
|
||||
import { fieldTypes } from 'payload/components/forms'
|
||||
import React from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import type { Field } from 'payload/types'
|
||||
import LexicalClickableLinkPlugin from '@lexical/react/LexicalClickableLinkPlugin'
|
||||
import { $findMatchingParent } from '@lexical/utils'
|
||||
import { $getSelection, $isRangeSelection } from 'lexical'
|
||||
import { withMergedProps } from 'payload/components/utilities'
|
||||
import { withMergedProps } from 'payload/utilities'
|
||||
|
||||
import type { FeatureProvider } from '../types'
|
||||
import type { LinkFields } from './nodes/LinkNode'
|
||||
|
||||
@@ -4,7 +4,7 @@ import { useModal } from '@faceless-ui/modal'
|
||||
import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext'
|
||||
import { $getNodeByKey } from 'lexical'
|
||||
import { Drawer } from 'payload/components/elements'
|
||||
import { Form, FormSubmit, RenderFields } from 'payload/components/forms'
|
||||
import { Form, FormSubmit, RenderFields, fieldTypes } from 'payload/components/forms'
|
||||
import {
|
||||
buildStateFromSchema,
|
||||
useAuth,
|
||||
@@ -12,7 +12,7 @@ import {
|
||||
useDocumentInfo,
|
||||
useLocale,
|
||||
} from 'payload/components/utilities'
|
||||
import { fieldTypes, sanitizeFields } from 'payload/config'
|
||||
import { sanitizeFields } from 'payload/config'
|
||||
import { deepCopyObject, getTranslation } from 'payload/utilities'
|
||||
import React, { useCallback, useEffect, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
import type { SerializedHeadingNode } from '@lexical/rich-text'
|
||||
|
||||
import type { LexicalPluginNodeConverter } from '../types'
|
||||
|
||||
import { convertLexicalPluginNodesToLexical } from '..'
|
||||
|
||||
export const HeadingConverter: LexicalPluginNodeConverter = {
|
||||
converter({ converters, lexicalPluginNode }) {
|
||||
return {
|
||||
...lexicalPluginNode,
|
||||
children: convertLexicalPluginNodesToLexical({
|
||||
converters,
|
||||
lexicalPluginNodes: (lexicalPluginNode as any).children || [],
|
||||
parentNodeType: 'heading',
|
||||
}),
|
||||
type: 'heading',
|
||||
version: 1,
|
||||
} as const as SerializedHeadingNode
|
||||
},
|
||||
nodeTypes: ['heading'],
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import type { SerializedLinkNode } from '../../../../Link/nodes/LinkNode'
|
||||
import type { LexicalPluginNodeConverter } from '../types'
|
||||
|
||||
import { convertLexicalPluginNodesToLexical } from '..'
|
||||
|
||||
export const LinkConverter: LexicalPluginNodeConverter = {
|
||||
converter({ converters, lexicalPluginNode }) {
|
||||
return {
|
||||
children: convertLexicalPluginNodesToLexical({
|
||||
converters,
|
||||
lexicalPluginNodes: (lexicalPluginNode as any).children || [],
|
||||
parentNodeType: 'link',
|
||||
}),
|
||||
direction: (lexicalPluginNode as any).direction || 'ltr',
|
||||
fields: {
|
||||
doc: (lexicalPluginNode as any).attributes?.doc
|
||||
? {
|
||||
relationTo: (lexicalPluginNode as any).attributes?.doc?.relationTo,
|
||||
value: {
|
||||
id: (lexicalPluginNode as any).attributes?.doc?.value,
|
||||
},
|
||||
}
|
||||
: undefined,
|
||||
linkType: (lexicalPluginNode as any).attributes?.linkType || 'custom',
|
||||
newTab: (lexicalPluginNode as any).attributes?.newTab || false,
|
||||
url: (lexicalPluginNode as any).attributes?.url || undefined,
|
||||
},
|
||||
format: (lexicalPluginNode as any).format || '',
|
||||
indent: (lexicalPluginNode as any).indent || 0,
|
||||
type: 'link',
|
||||
version: 1,
|
||||
} as const as SerializedLinkNode
|
||||
},
|
||||
nodeTypes: ['link'],
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
import type { SerializedListNode } from '@lexical/list'
|
||||
|
||||
import type { LexicalPluginNodeConverter } from '../types'
|
||||
|
||||
import { convertLexicalPluginNodesToLexical } from '..'
|
||||
|
||||
export const ListConverter: LexicalPluginNodeConverter = {
|
||||
converter({ converters, lexicalPluginNode }) {
|
||||
return {
|
||||
...lexicalPluginNode,
|
||||
children: convertLexicalPluginNodesToLexical({
|
||||
converters,
|
||||
lexicalPluginNodes: (lexicalPluginNode as any).children || [],
|
||||
parentNodeType: 'list',
|
||||
}),
|
||||
listType: (lexicalPluginNode as any)?.listType || 'number',
|
||||
tag: (lexicalPluginNode as any)?.tag || 'ol',
|
||||
type: 'list',
|
||||
version: 1,
|
||||
} as const as SerializedListNode
|
||||
},
|
||||
nodeTypes: ['list'],
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
import type { SerializedListItemNode } from '@lexical/list'
|
||||
|
||||
import type { LexicalPluginNodeConverter } from '../types'
|
||||
|
||||
import { convertLexicalPluginNodesToLexical } from '..'
|
||||
|
||||
export const ListItemConverter: LexicalPluginNodeConverter = {
|
||||
converter({ childIndex, converters, lexicalPluginNode }) {
|
||||
return {
|
||||
...lexicalPluginNode,
|
||||
checked: undefined,
|
||||
children: convertLexicalPluginNodesToLexical({
|
||||
converters,
|
||||
lexicalPluginNodes: (lexicalPluginNode as any)?.children || [],
|
||||
parentNodeType: 'listitem',
|
||||
}),
|
||||
type: 'listitem',
|
||||
value: childIndex + 1,
|
||||
version: 1,
|
||||
} as const as SerializedListItemNode
|
||||
},
|
||||
nodeTypes: ['listitem'],
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
import type { SerializedHeadingNode } from '@lexical/rich-text'
|
||||
|
||||
import type { LexicalPluginNodeConverter } from '../types'
|
||||
|
||||
import { convertLexicalPluginNodesToLexical } from '..'
|
||||
|
||||
export const QuoteConverter: LexicalPluginNodeConverter = {
|
||||
converter({ converters, lexicalPluginNode }) {
|
||||
return {
|
||||
...lexicalPluginNode,
|
||||
children: convertLexicalPluginNodesToLexical({
|
||||
converters,
|
||||
lexicalPluginNodes: (lexicalPluginNode as any).children || [],
|
||||
parentNodeType: 'quote',
|
||||
}),
|
||||
type: 'quote',
|
||||
version: 1,
|
||||
} as const as SerializedHeadingNode
|
||||
},
|
||||
nodeTypes: ['quote'],
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
import type { SerializedUnknownConvertedNode } from '../../nodes/unknownConvertedNode'
|
||||
import type { LexicalPluginNodeConverter } from '../types'
|
||||
|
||||
import { convertLexicalPluginNodesToLexical } from '..'
|
||||
|
||||
export const UnknownConverter: LexicalPluginNodeConverter = {
|
||||
converter({ converters, lexicalPluginNode }) {
|
||||
return {
|
||||
children: convertLexicalPluginNodesToLexical({
|
||||
converters,
|
||||
lexicalPluginNodes: (lexicalPluginNode as any)?.children || [],
|
||||
parentNodeType: 'unknownConverted',
|
||||
}),
|
||||
data: {
|
||||
nodeData: lexicalPluginNode,
|
||||
nodeType: lexicalPluginNode.type,
|
||||
},
|
||||
direction: 'ltr',
|
||||
format: '',
|
||||
indent: 0,
|
||||
type: 'unknownConverted',
|
||||
version: 1,
|
||||
} as const as SerializedUnknownConvertedNode
|
||||
},
|
||||
nodeTypes: ['unknown'],
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
import type { SerializedUploadNode } from '../../../../../..'
|
||||
import type { LexicalPluginNodeConverter } from '../types'
|
||||
|
||||
export const UploadConverter: LexicalPluginNodeConverter = {
|
||||
converter({ lexicalPluginNode }) {
|
||||
let fields = {}
|
||||
if ((lexicalPluginNode as any)?.caption?.editorState) {
|
||||
fields = {
|
||||
caption: (lexicalPluginNode as any)?.caption,
|
||||
}
|
||||
}
|
||||
return {
|
||||
fields,
|
||||
format: (lexicalPluginNode as any)?.format || '',
|
||||
relationTo: (lexicalPluginNode as any)?.rawImagePayload?.relationTo,
|
||||
type: 'upload',
|
||||
value: {
|
||||
id: (lexicalPluginNode as any)?.rawImagePayload?.value?.id || '',
|
||||
},
|
||||
version: 1,
|
||||
} as const as SerializedUploadNode
|
||||
},
|
||||
nodeTypes: ['upload'],
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
import type { LexicalPluginNodeConverter } from './types'
|
||||
|
||||
import { HeadingConverter } from './converters/heading'
|
||||
import { LinkConverter } from './converters/link'
|
||||
import { ListConverter } from './converters/list'
|
||||
import { ListItemConverter } from './converters/listItem'
|
||||
import { QuoteConverter } from './converters/quote'
|
||||
import { UnknownConverter } from './converters/unknown'
|
||||
import { UploadConverter } from './converters/upload'
|
||||
|
||||
export const defaultConverters: LexicalPluginNodeConverter[] = [
|
||||
UnknownConverter,
|
||||
UploadConverter,
|
||||
ListConverter,
|
||||
ListItemConverter,
|
||||
LinkConverter,
|
||||
HeadingConverter,
|
||||
QuoteConverter,
|
||||
]
|
||||
@@ -0,0 +1,98 @@
|
||||
import type {
|
||||
SerializedEditorState,
|
||||
SerializedLexicalNode,
|
||||
SerializedParagraphNode,
|
||||
SerializedTextNode,
|
||||
} from 'lexical'
|
||||
|
||||
import type { LexicalPluginNodeConverter, PayloadPluginLexicalData } from './types'
|
||||
|
||||
export function convertLexicalPluginToLexical({
|
||||
converters,
|
||||
lexicalPluginData,
|
||||
}: {
|
||||
converters: LexicalPluginNodeConverter[]
|
||||
lexicalPluginData: PayloadPluginLexicalData
|
||||
}): SerializedEditorState {
|
||||
return {
|
||||
root: {
|
||||
children: convertLexicalPluginNodesToLexical({
|
||||
converters,
|
||||
lexicalPluginNodes: lexicalPluginData?.jsonContent?.root?.children || [],
|
||||
parentNodeType: 'root',
|
||||
}),
|
||||
direction: lexicalPluginData?.jsonContent?.root?.direction || 'ltr',
|
||||
format: lexicalPluginData?.jsonContent?.root?.format || '',
|
||||
indent: lexicalPluginData?.jsonContent?.root?.indent || 0,
|
||||
type: 'root',
|
||||
version: 1,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
export function convertLexicalPluginNodesToLexical({
|
||||
converters,
|
||||
lexicalPluginNodes,
|
||||
parentNodeType,
|
||||
}: {
|
||||
converters: LexicalPluginNodeConverter[]
|
||||
lexicalPluginNodes: SerializedLexicalNode[]
|
||||
/**
|
||||
* Type of the parent lexical node (not the type of the original, parent payload-plugin-lexical type)
|
||||
*/
|
||||
parentNodeType: string
|
||||
}): SerializedLexicalNode[] {
|
||||
const unknownConverter = converters.find((converter) => converter.nodeTypes.includes('unknown'))
|
||||
return (
|
||||
lexicalPluginNodes.map((lexicalPluginNode, i) => {
|
||||
if (lexicalPluginNode.type === 'paragraph') {
|
||||
return convertParagraphNode(converters, lexicalPluginNode)
|
||||
}
|
||||
if (lexicalPluginNode.type === 'text' || !lexicalPluginNode.type) {
|
||||
return convertTextNode(lexicalPluginNode)
|
||||
}
|
||||
|
||||
const converter = converters.find((converter) =>
|
||||
converter.nodeTypes.includes(lexicalPluginNode.type),
|
||||
)
|
||||
|
||||
if (converter) {
|
||||
return converter.converter({ childIndex: i, converters, lexicalPluginNode, parentNodeType })
|
||||
}
|
||||
|
||||
console.warn(
|
||||
'lexicalPluginToLexical > No converter found for node type: ' + lexicalPluginNode.type,
|
||||
)
|
||||
return unknownConverter?.converter({
|
||||
childIndex: i,
|
||||
converters,
|
||||
lexicalPluginNode,
|
||||
parentNodeType,
|
||||
})
|
||||
}) || []
|
||||
)
|
||||
}
|
||||
|
||||
export function convertParagraphNode(
|
||||
converters: LexicalPluginNodeConverter[],
|
||||
node: SerializedLexicalNode,
|
||||
): SerializedParagraphNode {
|
||||
return {
|
||||
...node,
|
||||
children: convertLexicalPluginNodesToLexical({
|
||||
converters,
|
||||
lexicalPluginNodes: (node as any).children || [],
|
||||
parentNodeType: 'paragraph',
|
||||
}),
|
||||
|
||||
type: 'paragraph',
|
||||
version: 1,
|
||||
} as SerializedParagraphNode
|
||||
}
|
||||
export function convertTextNode(node: SerializedLexicalNode): SerializedTextNode {
|
||||
return node as SerializedTextNode
|
||||
}
|
||||
|
||||
export function convertNodeToFormat(node: SerializedLexicalNode): number {
|
||||
return (node as any).format
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
import type { SerializedEditorState, SerializedLexicalNode } from 'lexical'
|
||||
|
||||
export type LexicalPluginNodeConverter<T extends SerializedLexicalNode = SerializedLexicalNode> = {
|
||||
converter: ({
|
||||
childIndex,
|
||||
converters,
|
||||
lexicalPluginNode,
|
||||
parentNodeType,
|
||||
}: {
|
||||
childIndex: number
|
||||
converters: LexicalPluginNodeConverter[]
|
||||
lexicalPluginNode: SerializedLexicalNode
|
||||
parentNodeType: string
|
||||
}) => T
|
||||
nodeTypes: string[]
|
||||
}
|
||||
|
||||
export type PayloadPluginLexicalData = {
|
||||
characters: number
|
||||
comments: unknown[]
|
||||
html?: string
|
||||
jsonContent: SerializedEditorState
|
||||
markdown?: string
|
||||
preview: string
|
||||
words: number
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
import type { FeatureProvider } from '../../types'
|
||||
import type { LexicalPluginNodeConverter, PayloadPluginLexicalData } from './converter/types'
|
||||
|
||||
import { convertLexicalPluginToLexical } from './converter'
|
||||
import { defaultConverters } from './converter/defaultConverters'
|
||||
import { UnknownConvertedNode } from './nodes/unknownConvertedNode'
|
||||
|
||||
type Props = {
|
||||
converters?:
|
||||
| (({
|
||||
defaultConverters,
|
||||
}: {
|
||||
defaultConverters: LexicalPluginNodeConverter[]
|
||||
}) => LexicalPluginNodeConverter[])
|
||||
| LexicalPluginNodeConverter[]
|
||||
}
|
||||
|
||||
export const LexicalPluginToLexicalFeature = (props?: Props): FeatureProvider => {
|
||||
if (!props) {
|
||||
props = {}
|
||||
}
|
||||
|
||||
props.converters =
|
||||
props?.converters && typeof props?.converters === 'function'
|
||||
? props.converters({ defaultConverters: defaultConverters })
|
||||
: (props?.converters as LexicalPluginNodeConverter[]) || defaultConverters
|
||||
|
||||
return {
|
||||
feature: ({ resolvedFeatures, unsanitizedEditorConfig }) => {
|
||||
return {
|
||||
hooks: {
|
||||
load({ incomingEditorState }) {
|
||||
if (!incomingEditorState || !('jsonContent' in incomingEditorState)) {
|
||||
// incomingEditorState null or not from Lexical Plugin
|
||||
return incomingEditorState
|
||||
}
|
||||
// Lexical Plugin => convert to lexical
|
||||
|
||||
return convertLexicalPluginToLexical({
|
||||
converters: props.converters as LexicalPluginNodeConverter[],
|
||||
lexicalPluginData: incomingEditorState as unknown as PayloadPluginLexicalData,
|
||||
})
|
||||
},
|
||||
},
|
||||
nodes: [
|
||||
{
|
||||
node: UnknownConvertedNode,
|
||||
type: UnknownConvertedNode.getType(),
|
||||
},
|
||||
],
|
||||
props,
|
||||
}
|
||||
},
|
||||
key: 'lexicalPluginToLexical',
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
@import 'payload/scss';
|
||||
|
||||
span.unknownConverted {
|
||||
text-transform: uppercase;
|
||||
font-family: 'Roboto Mono', monospace;
|
||||
letter-spacing: 2px;
|
||||
font-size: base(0.5);
|
||||
margin: 0 0 base(1);
|
||||
background: red;
|
||||
color: white;
|
||||
display: inline-block;
|
||||
|
||||
div {
|
||||
background: red;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
import type { SerializedLexicalNode, Spread } from 'lexical'
|
||||
|
||||
import { addClassNamesToElement } from '@lexical/utils'
|
||||
import { DecoratorNode, type EditorConfig, type LexicalNode, type NodeKey } from 'lexical'
|
||||
import React from 'react'
|
||||
|
||||
import './index.scss'
|
||||
|
||||
export type UnknownConvertedNodeData = {
|
||||
nodeData: unknown
|
||||
nodeType: string
|
||||
}
|
||||
|
||||
export type SerializedUnknownConvertedNode = Spread<
|
||||
{
|
||||
data: UnknownConvertedNodeData
|
||||
},
|
||||
SerializedLexicalNode
|
||||
>
|
||||
|
||||
/** @noInheritDoc */
|
||||
export class UnknownConvertedNode extends DecoratorNode<JSX.Element> {
|
||||
__data: UnknownConvertedNodeData
|
||||
|
||||
constructor({ data, key }: { data: UnknownConvertedNodeData; key?: NodeKey }) {
|
||||
super(key)
|
||||
this.__data = data
|
||||
}
|
||||
|
||||
static clone(node: UnknownConvertedNode): UnknownConvertedNode {
|
||||
return new UnknownConvertedNode({
|
||||
data: node.__data,
|
||||
key: node.__key,
|
||||
})
|
||||
}
|
||||
|
||||
static getType(): string {
|
||||
return 'unknownConverted'
|
||||
}
|
||||
|
||||
static importJSON(serializedNode: SerializedUnknownConvertedNode): UnknownConvertedNode {
|
||||
const node = $createUnknownConvertedNode({ data: serializedNode.data })
|
||||
return node
|
||||
}
|
||||
|
||||
canInsertTextAfter(): true {
|
||||
return true
|
||||
}
|
||||
|
||||
canInsertTextBefore(): true {
|
||||
return true
|
||||
}
|
||||
|
||||
createDOM(config: EditorConfig): HTMLElement {
|
||||
const element = document.createElement('span')
|
||||
addClassNamesToElement(element, 'unknownConverted')
|
||||
return element
|
||||
}
|
||||
|
||||
decorate(): JSX.Element | null {
|
||||
return (
|
||||
<div>
|
||||
Unknown converted payload-plugin-lexical node: <strong>{this.__data?.nodeType}</strong>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
exportJSON(): SerializedUnknownConvertedNode {
|
||||
return {
|
||||
data: this.__data,
|
||||
type: this.getType(),
|
||||
version: 1,
|
||||
}
|
||||
}
|
||||
|
||||
// Mutation
|
||||
|
||||
isInline(): boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
updateDOM(prevNode: UnknownConvertedNode, dom: HTMLElement): boolean {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
export function $createUnknownConvertedNode({
|
||||
data,
|
||||
}: {
|
||||
data: UnknownConvertedNodeData
|
||||
}): UnknownConvertedNode {
|
||||
return new UnknownConvertedNode({
|
||||
data,
|
||||
})
|
||||
}
|
||||
|
||||
export function $isUnknownConvertedNode(
|
||||
node: LexicalNode | null | undefined,
|
||||
): node is UnknownConvertedNode {
|
||||
return node instanceof UnknownConvertedNode
|
||||
}
|
||||
@@ -58,7 +58,11 @@ export class UnknownConvertedNode extends DecoratorNode<JSX.Element> {
|
||||
}
|
||||
|
||||
decorate(): JSX.Element | null {
|
||||
return <div>Unknown converted Slate node: {this.__data?.nodeType}</div>
|
||||
return (
|
||||
<div>
|
||||
Unknown converted Slate node: <strong>{this.__data?.nodeType}</strong>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
exportJSON(): SerializedUnknownConvertedNode {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
'use client'
|
||||
import { ShimmerEffect } from 'payload/components'
|
||||
import React, { Suspense, lazy } from 'react'
|
||||
|
||||
|
||||
@@ -17,7 +17,9 @@ import { AddBlockHandlePlugin } from './plugins/handles/AddBlockHandlePlugin'
|
||||
import { DraggableBlockPlugin } from './plugins/handles/DraggableBlockPlugin'
|
||||
import { LexicalContentEditable } from './ui/ContentEditable'
|
||||
|
||||
export const LexicalEditor: React.FC<LexicalProviderProps> = (props) => {
|
||||
export const LexicalEditor: React.FC<Pick<LexicalProviderProps, 'editorConfig' | 'onChange'>> = (
|
||||
props,
|
||||
) => {
|
||||
const { editorConfig, onChange } = props
|
||||
const [editor] = useLexicalComposerContext()
|
||||
|
||||
|
||||
@@ -14,28 +14,21 @@ import { getEnabledNodes } from './nodes'
|
||||
export type LexicalProviderProps = {
|
||||
editorConfig: SanitizedEditorConfig
|
||||
fieldProps: FieldProps
|
||||
initialState: SerializedEditorState
|
||||
onChange: (editorState: EditorState, editor: LexicalEditor, tags: Set<string>) => void
|
||||
readOnly: boolean
|
||||
setValue: (value: SerializedEditorState) => void
|
||||
value: SerializedEditorState
|
||||
}
|
||||
export const LexicalProvider: React.FC<LexicalProviderProps> = (props) => {
|
||||
const { editorConfig, fieldProps, onChange, readOnly, setValue } = props
|
||||
let { initialState, value } = props
|
||||
const { editorConfig, fieldProps, onChange, readOnly } = props
|
||||
let { value } = props
|
||||
|
||||
// Transform initialState through load hooks
|
||||
if (editorConfig?.features?.hooks?.load?.length) {
|
||||
editorConfig.features.hooks.load.forEach((hook) => {
|
||||
initialState = hook({ incomingEditorState: initialState })
|
||||
value = hook({ incomingEditorState: value })
|
||||
})
|
||||
}
|
||||
|
||||
if (
|
||||
(value && Array.isArray(value) && !('root' in value)) ||
|
||||
(initialState && Array.isArray(initialState) && !('root' in initialState))
|
||||
) {
|
||||
if (value && Array.isArray(value) && !('root' in value)) {
|
||||
throw new Error(
|
||||
'You have tried to pass in data from the old, Slate editor, to the new, Lexical editor. This is not supported. There is no automatic conversion from Slate to Lexical data available yet (coming soon). Please remove the data from the field and start again.',
|
||||
)
|
||||
@@ -49,7 +42,7 @@ export const LexicalProvider: React.FC<LexicalProviderProps> = (props) => {
|
||||
|
||||
const initialConfig: InitialConfigType = {
|
||||
editable: readOnly === true ? false : true,
|
||||
editorState: initialState != null ? JSON.stringify(initialState) : undefined,
|
||||
editorState: value != null ? JSON.stringify(value) : undefined,
|
||||
namespace: editorConfig.lexical.namespace,
|
||||
nodes: [...getEnabledNodes({ editorConfig })],
|
||||
onError: (error: Error) => {
|
||||
@@ -62,15 +55,7 @@ export const LexicalProvider: React.FC<LexicalProviderProps> = (props) => {
|
||||
<LexicalComposer initialConfig={initialConfig}>
|
||||
<EditorConfigProvider editorConfig={editorConfig} fieldProps={fieldProps}>
|
||||
<div className="editor-shell">
|
||||
<LexicalEditorComponent
|
||||
editorConfig={editorConfig}
|
||||
fieldProps={fieldProps}
|
||||
initialState={initialState}
|
||||
onChange={onChange}
|
||||
readOnly={readOnly}
|
||||
setValue={setValue}
|
||||
value={value}
|
||||
/>
|
||||
<LexicalEditorComponent editorConfig={editorConfig} onChange={onChange} />
|
||||
</div>
|
||||
</EditorConfigProvider>
|
||||
</LexicalComposer>
|
||||
|
||||
@@ -2,7 +2,7 @@ import type { SerializedEditorState } from 'lexical'
|
||||
import type { EditorConfig as LexicalEditorConfig } from 'lexical/LexicalEditor'
|
||||
import type { RichTextAdapter } from 'payload/types'
|
||||
|
||||
import { withMergedProps } from 'payload/components/utilities'
|
||||
import { withMergedProps } from 'payload/utilities'
|
||||
|
||||
import type { FeatureProvider } from './field/features/types'
|
||||
import type { EditorConfig, SanitizedEditorConfig } from './field/lexical/config/types'
|
||||
@@ -153,7 +153,9 @@ export { IndentFeature } from './field/features/indent'
|
||||
export { CheckListFeature } from './field/features/lists/CheckList'
|
||||
export { OrderedListFeature } from './field/features/lists/OrderedList'
|
||||
export { UnoderedListFeature } from './field/features/lists/UnorderedList'
|
||||
export { LexicalPluginToLexicalFeature } from './field/features/migrations/LexicalPluginToLexical'
|
||||
export { SlateToLexicalFeature } from './field/features/migrations/SlateToLexical'
|
||||
|
||||
export type {
|
||||
AfterReadPromise,
|
||||
Feature,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/richtext-slate",
|
||||
"version": "1.0.3",
|
||||
"version": "1.0.5",
|
||||
"description": "The officially supported Slate richtext adapter for Payload",
|
||||
"repository": "https://github.com/payloadcms/payload",
|
||||
"license": "MIT",
|
||||
@@ -33,6 +33,9 @@
|
||||
"@types/react": "18.2.15",
|
||||
"payload": "workspace:*"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"payload": "^2.0.6"
|
||||
},
|
||||
"exports": {
|
||||
".": {
|
||||
"default": "./src/index.ts",
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { Drawer } from 'payload/components/elements'
|
||||
import { Form, FormSubmit, RenderFields } from 'payload/components/forms'
|
||||
import { fieldTypes } from 'payload/components/forms'
|
||||
import { useHotkey } from 'payload/components/hooks'
|
||||
import { useEditDepth } from 'payload/components/utilities'
|
||||
import { fieldTypes } from 'payload/config'
|
||||
import React, { useRef } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import type { SanitizedCollectionConfig } from 'payload/types'
|
||||
|
||||
import { useModal } from '@faceless-ui/modal'
|
||||
import { Drawer } from 'payload/components/elements'
|
||||
import { Form, FormSubmit, RenderFields } from 'payload/components/forms'
|
||||
import { Form, FormSubmit, RenderFields, fieldTypes } from 'payload/components/forms'
|
||||
import {
|
||||
buildStateFromSchema,
|
||||
useAuth,
|
||||
@@ -10,7 +10,7 @@ import {
|
||||
useDocumentInfo,
|
||||
useLocale,
|
||||
} from 'payload/components/utilities'
|
||||
import { fieldTypes, sanitizeFields } from 'payload/config'
|
||||
import { sanitizeFields } from 'payload/config'
|
||||
import { deepCopyObject, getTranslation } from 'payload/utilities'
|
||||
import React, { useCallback, useEffect, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { RichTextAdapter } from 'payload/types'
|
||||
|
||||
import { withMergedProps } from 'payload/components/utilities'
|
||||
import { withMergedProps } from 'payload/utilities'
|
||||
|
||||
import type { AdapterArguments } from './types'
|
||||
|
||||
|
||||
8
pnpm-lock.yaml
generated
8
pnpm-lock.yaml
generated
@@ -418,9 +418,6 @@ importers:
|
||||
'@libsql/client':
|
||||
specifier: ^0.3.1
|
||||
version: 0.3.4
|
||||
better-sqlite3:
|
||||
specifier: ^8.5.0
|
||||
version: 8.6.0
|
||||
console-table-printer:
|
||||
specifier: 2.11.2
|
||||
version: 2.11.2
|
||||
@@ -429,7 +426,7 @@ importers:
|
||||
version: 0.19.13-e99bac1
|
||||
drizzle-orm:
|
||||
specifier: 0.28.5
|
||||
version: 0.28.5(@libsql/client@0.3.4)(@types/pg@8.10.2)(better-sqlite3@8.6.0)(pg@8.11.3)
|
||||
version: 0.28.5(@libsql/client@0.3.4)(@types/pg@8.10.2)(pg@8.11.3)
|
||||
pg:
|
||||
specifier: 8.11.3
|
||||
version: 8.11.3
|
||||
@@ -8447,7 +8444,7 @@ packages:
|
||||
- supports-color
|
||||
dev: false
|
||||
|
||||
/drizzle-orm@0.28.5(@libsql/client@0.3.4)(@types/pg@8.10.2)(better-sqlite3@8.6.0)(pg@8.11.3):
|
||||
/drizzle-orm@0.28.5(@libsql/client@0.3.4)(@types/pg@8.10.2)(pg@8.11.3):
|
||||
resolution: {integrity: sha512-6r6Iw4c38NAmW6TiKH3TUpGUQ1YdlEoLJOQptn8XPx3Z63+vFNKfAiANqrIiYZiMjKR9+NYAL219nFrmo1duXA==}
|
||||
peerDependencies:
|
||||
'@aws-sdk/client-rds-data': '>=3'
|
||||
@@ -8511,7 +8508,6 @@ packages:
|
||||
dependencies:
|
||||
'@libsql/client': 0.3.4
|
||||
'@types/pg': 8.10.2
|
||||
better-sqlite3: 8.6.0
|
||||
pg: 8.11.3
|
||||
dev: false
|
||||
|
||||
|
||||
@@ -0,0 +1,958 @@
|
||||
export const payloadPluginLexicalData = {
|
||||
words: 49,
|
||||
preview:
|
||||
'paragraph text bold italic underline and all subscript superscript code internal link external link…',
|
||||
comments: [],
|
||||
characters: 493,
|
||||
jsonContent: {
|
||||
root: {
|
||||
type: 'root',
|
||||
format: '',
|
||||
indent: 0,
|
||||
version: 1,
|
||||
children: [
|
||||
{
|
||||
children: [
|
||||
{
|
||||
detail: 0,
|
||||
format: 0,
|
||||
mode: 'normal',
|
||||
style: '',
|
||||
text: 'paragraph text ',
|
||||
type: 'text',
|
||||
version: 1,
|
||||
},
|
||||
{
|
||||
detail: 0,
|
||||
format: 1,
|
||||
mode: 'normal',
|
||||
style: '',
|
||||
text: 'bold',
|
||||
type: 'text',
|
||||
version: 1,
|
||||
},
|
||||
{
|
||||
detail: 0,
|
||||
format: 0,
|
||||
mode: 'normal',
|
||||
style: '',
|
||||
text: ' ',
|
||||
type: 'text',
|
||||
version: 1,
|
||||
},
|
||||
{
|
||||
detail: 0,
|
||||
format: 2,
|
||||
mode: 'normal',
|
||||
style: '',
|
||||
text: 'italic',
|
||||
type: 'text',
|
||||
version: 1,
|
||||
},
|
||||
{
|
||||
detail: 0,
|
||||
format: 0,
|
||||
mode: 'normal',
|
||||
style: '',
|
||||
text: ' ',
|
||||
type: 'text',
|
||||
version: 1,
|
||||
},
|
||||
{
|
||||
detail: 0,
|
||||
format: 8,
|
||||
mode: 'normal',
|
||||
style: '',
|
||||
text: 'underline',
|
||||
type: 'text',
|
||||
version: 1,
|
||||
},
|
||||
{
|
||||
detail: 0,
|
||||
format: 0,
|
||||
mode: 'normal',
|
||||
style: '',
|
||||
text: ' and ',
|
||||
type: 'text',
|
||||
version: 1,
|
||||
},
|
||||
{
|
||||
detail: 0,
|
||||
format: 11,
|
||||
mode: 'normal',
|
||||
style: '',
|
||||
text: 'all',
|
||||
type: 'text',
|
||||
version: 1,
|
||||
},
|
||||
{
|
||||
detail: 0,
|
||||
format: 0,
|
||||
mode: 'normal',
|
||||
style: '',
|
||||
text: ' ',
|
||||
type: 'text',
|
||||
version: 1,
|
||||
},
|
||||
{
|
||||
detail: 0,
|
||||
format: 32,
|
||||
mode: 'normal',
|
||||
style: '',
|
||||
text: 'subscript',
|
||||
type: 'text',
|
||||
version: 1,
|
||||
},
|
||||
{
|
||||
detail: 0,
|
||||
format: 0,
|
||||
mode: 'normal',
|
||||
style: '',
|
||||
text: ' ',
|
||||
type: 'text',
|
||||
version: 1,
|
||||
},
|
||||
{
|
||||
detail: 0,
|
||||
format: 64,
|
||||
mode: 'normal',
|
||||
style: '',
|
||||
text: 'superscript',
|
||||
type: 'text',
|
||||
version: 1,
|
||||
},
|
||||
{
|
||||
detail: 0,
|
||||
format: 0,
|
||||
mode: 'normal',
|
||||
style: '',
|
||||
text: ' ',
|
||||
type: 'text',
|
||||
version: 1,
|
||||
},
|
||||
{
|
||||
detail: 0,
|
||||
format: 16,
|
||||
mode: 'normal',
|
||||
style: '',
|
||||
text: 'code',
|
||||
type: 'text',
|
||||
version: 1,
|
||||
},
|
||||
{
|
||||
detail: 0,
|
||||
format: 0,
|
||||
mode: 'normal',
|
||||
style: '',
|
||||
text: ' ',
|
||||
type: 'text',
|
||||
version: 1,
|
||||
},
|
||||
{
|
||||
children: [
|
||||
{
|
||||
detail: 0,
|
||||
format: 0,
|
||||
mode: 'normal',
|
||||
style: '',
|
||||
text: 'internal link',
|
||||
type: 'text',
|
||||
version: 1,
|
||||
},
|
||||
],
|
||||
direction: 'ltr',
|
||||
format: '',
|
||||
indent: 0,
|
||||
type: 'link',
|
||||
version: 2,
|
||||
attributes: {
|
||||
newTab: true,
|
||||
linkType: 'internal',
|
||||
doc: {
|
||||
value: '{{TEXT_DOC_ID}}',
|
||||
relationTo: 'text-fields',
|
||||
data: {}, // populated data
|
||||
},
|
||||
text: 'internal link',
|
||||
},
|
||||
},
|
||||
{
|
||||
detail: 0,
|
||||
format: 0,
|
||||
mode: 'normal',
|
||||
style: '',
|
||||
text: ' ',
|
||||
type: 'text',
|
||||
version: 1,
|
||||
},
|
||||
{
|
||||
children: [
|
||||
{
|
||||
detail: 0,
|
||||
format: 0,
|
||||
mode: 'normal',
|
||||
style: '',
|
||||
text: 'external link',
|
||||
type: 'text',
|
||||
version: 1,
|
||||
},
|
||||
],
|
||||
direction: 'ltr',
|
||||
format: '',
|
||||
indent: 0,
|
||||
type: 'link',
|
||||
version: 2,
|
||||
attributes: {
|
||||
newTab: true,
|
||||
nofollow: false,
|
||||
url: 'https://fewfwef.de',
|
||||
linkType: 'custom',
|
||||
text: 'external link',
|
||||
},
|
||||
},
|
||||
{
|
||||
detail: 0,
|
||||
format: 0,
|
||||
mode: 'normal',
|
||||
style: '',
|
||||
text: ' s. ',
|
||||
type: 'text',
|
||||
version: 1,
|
||||
},
|
||||
{
|
||||
detail: 0,
|
||||
format: 4,
|
||||
mode: 'normal',
|
||||
style: '',
|
||||
text: 'strikethrough',
|
||||
type: 'text',
|
||||
version: 1,
|
||||
},
|
||||
],
|
||||
direction: 'ltr',
|
||||
format: '',
|
||||
indent: 0,
|
||||
type: 'paragraph',
|
||||
version: 1,
|
||||
},
|
||||
{
|
||||
children: [
|
||||
{
|
||||
detail: 0,
|
||||
format: 0,
|
||||
mode: 'normal',
|
||||
style: '',
|
||||
text: 'heading 1',
|
||||
type: 'text',
|
||||
version: 1,
|
||||
},
|
||||
],
|
||||
direction: 'ltr',
|
||||
format: '',
|
||||
indent: 0,
|
||||
type: 'heading',
|
||||
version: 1,
|
||||
tag: 'h1',
|
||||
},
|
||||
{
|
||||
children: [
|
||||
{
|
||||
detail: 0,
|
||||
format: 0,
|
||||
mode: 'normal',
|
||||
style: '',
|
||||
text: 'heading 2',
|
||||
type: 'text',
|
||||
version: 1,
|
||||
},
|
||||
],
|
||||
direction: 'ltr',
|
||||
format: '',
|
||||
indent: 0,
|
||||
type: 'heading',
|
||||
version: 1,
|
||||
tag: 'h2',
|
||||
},
|
||||
{
|
||||
children: [
|
||||
{
|
||||
children: [
|
||||
{
|
||||
detail: 0,
|
||||
format: 0,
|
||||
mode: 'normal',
|
||||
style: '',
|
||||
text: 'bullet list ',
|
||||
type: 'text',
|
||||
version: 1,
|
||||
},
|
||||
],
|
||||
direction: 'ltr',
|
||||
format: '',
|
||||
indent: 0,
|
||||
type: 'listitem',
|
||||
version: 1,
|
||||
value: 1,
|
||||
},
|
||||
{
|
||||
children: [
|
||||
{
|
||||
detail: 0,
|
||||
format: 0,
|
||||
mode: 'normal',
|
||||
style: '',
|
||||
text: 'item 2',
|
||||
type: 'text',
|
||||
version: 1,
|
||||
},
|
||||
],
|
||||
direction: 'ltr',
|
||||
format: '',
|
||||
indent: 0,
|
||||
type: 'listitem',
|
||||
version: 1,
|
||||
value: 2,
|
||||
},
|
||||
{
|
||||
children: [
|
||||
{
|
||||
detail: 0,
|
||||
format: 0,
|
||||
mode: 'normal',
|
||||
style: '',
|
||||
text: 'item 3',
|
||||
type: 'text',
|
||||
version: 1,
|
||||
},
|
||||
],
|
||||
direction: 'ltr',
|
||||
format: '',
|
||||
indent: 0,
|
||||
type: 'listitem',
|
||||
version: 1,
|
||||
value: 3,
|
||||
},
|
||||
],
|
||||
direction: 'ltr',
|
||||
format: '',
|
||||
indent: 0,
|
||||
type: 'list',
|
||||
version: 1,
|
||||
listType: 'bullet',
|
||||
start: 1,
|
||||
tag: 'ul',
|
||||
},
|
||||
{
|
||||
children: [
|
||||
{
|
||||
children: [
|
||||
{
|
||||
detail: 0,
|
||||
format: 0,
|
||||
mode: 'normal',
|
||||
style: '',
|
||||
text: 'ordered list',
|
||||
type: 'text',
|
||||
version: 1,
|
||||
},
|
||||
],
|
||||
direction: 'ltr',
|
||||
format: '',
|
||||
indent: 0,
|
||||
type: 'listitem',
|
||||
version: 1,
|
||||
value: 1,
|
||||
},
|
||||
{
|
||||
children: [
|
||||
{
|
||||
detail: 0,
|
||||
format: 0,
|
||||
mode: 'normal',
|
||||
style: '',
|
||||
text: 'item 2',
|
||||
type: 'text',
|
||||
version: 1,
|
||||
},
|
||||
],
|
||||
direction: 'ltr',
|
||||
format: '',
|
||||
indent: 0,
|
||||
type: 'listitem',
|
||||
version: 1,
|
||||
value: 2,
|
||||
},
|
||||
{
|
||||
children: [
|
||||
{
|
||||
detail: 0,
|
||||
format: 0,
|
||||
mode: 'normal',
|
||||
style: '',
|
||||
text: 'item 3',
|
||||
type: 'text',
|
||||
version: 1,
|
||||
},
|
||||
],
|
||||
direction: 'ltr',
|
||||
format: '',
|
||||
indent: 0,
|
||||
type: 'listitem',
|
||||
version: 1,
|
||||
value: 3,
|
||||
},
|
||||
],
|
||||
direction: 'ltr',
|
||||
format: '',
|
||||
indent: 0,
|
||||
type: 'list',
|
||||
version: 1,
|
||||
listType: 'number',
|
||||
start: 1,
|
||||
tag: 'ol',
|
||||
},
|
||||
{
|
||||
children: [
|
||||
{
|
||||
children: [
|
||||
{
|
||||
detail: 0,
|
||||
format: 0,
|
||||
mode: 'normal',
|
||||
style: '',
|
||||
text: 'check list',
|
||||
type: 'text',
|
||||
version: 1,
|
||||
},
|
||||
],
|
||||
direction: 'ltr',
|
||||
format: '',
|
||||
indent: 0,
|
||||
type: 'listitem',
|
||||
version: 1,
|
||||
value: 1,
|
||||
},
|
||||
{
|
||||
children: [
|
||||
{
|
||||
detail: 0,
|
||||
format: 0,
|
||||
mode: 'normal',
|
||||
style: '',
|
||||
text: 'item 2',
|
||||
type: 'text',
|
||||
version: 1,
|
||||
},
|
||||
],
|
||||
direction: 'ltr',
|
||||
format: '',
|
||||
indent: 0,
|
||||
type: 'listitem',
|
||||
version: 1,
|
||||
value: 2,
|
||||
},
|
||||
{
|
||||
children: [
|
||||
{
|
||||
detail: 0,
|
||||
format: 0,
|
||||
mode: 'normal',
|
||||
style: '',
|
||||
text: 'item 3',
|
||||
type: 'text',
|
||||
version: 1,
|
||||
},
|
||||
],
|
||||
direction: 'ltr',
|
||||
format: '',
|
||||
indent: 0,
|
||||
type: 'listitem',
|
||||
version: 1,
|
||||
value: 3,
|
||||
},
|
||||
],
|
||||
direction: 'ltr',
|
||||
format: '',
|
||||
indent: 0,
|
||||
type: 'list',
|
||||
version: 1,
|
||||
listType: 'check',
|
||||
start: 1,
|
||||
tag: 'ul',
|
||||
},
|
||||
{
|
||||
children: [
|
||||
{
|
||||
detail: 0,
|
||||
format: 0,
|
||||
mode: 'normal',
|
||||
style: '',
|
||||
text: 'quoteeee',
|
||||
type: 'text',
|
||||
version: 1,
|
||||
},
|
||||
],
|
||||
direction: 'ltr',
|
||||
format: '',
|
||||
indent: 0,
|
||||
type: 'quote',
|
||||
version: 1,
|
||||
},
|
||||
{
|
||||
children: [
|
||||
{
|
||||
detail: 0,
|
||||
format: 0,
|
||||
mode: 'normal',
|
||||
style: '',
|
||||
text: 'code block line ',
|
||||
type: 'code-highlight',
|
||||
version: 1,
|
||||
},
|
||||
{
|
||||
detail: 0,
|
||||
format: 0,
|
||||
mode: 'normal',
|
||||
style: '',
|
||||
text: '1',
|
||||
type: 'code-highlight',
|
||||
version: 1,
|
||||
highlightType: 'number',
|
||||
},
|
||||
{
|
||||
type: 'linebreak',
|
||||
version: 1,
|
||||
},
|
||||
{
|
||||
detail: 0,
|
||||
format: 0,
|
||||
mode: 'normal',
|
||||
style: '',
|
||||
text: 'code block line ',
|
||||
type: 'code-highlight',
|
||||
version: 1,
|
||||
},
|
||||
{
|
||||
detail: 0,
|
||||
format: 0,
|
||||
mode: 'normal',
|
||||
style: '',
|
||||
text: '2',
|
||||
type: 'code-highlight',
|
||||
version: 1,
|
||||
highlightType: 'number',
|
||||
},
|
||||
],
|
||||
direction: 'ltr',
|
||||
format: '',
|
||||
indent: 0,
|
||||
type: 'code',
|
||||
version: 1,
|
||||
language: 'javascript',
|
||||
},
|
||||
{
|
||||
children: [
|
||||
{
|
||||
detail: 0,
|
||||
format: 0,
|
||||
mode: 'normal',
|
||||
style: '',
|
||||
text: 'Upload:',
|
||||
type: 'text',
|
||||
version: 1,
|
||||
},
|
||||
],
|
||||
direction: 'ltr',
|
||||
format: '',
|
||||
indent: 0,
|
||||
type: 'paragraph',
|
||||
version: 1,
|
||||
},
|
||||
{
|
||||
children: [
|
||||
{
|
||||
type: 'upload',
|
||||
version: 1,
|
||||
rawImagePayload: {
|
||||
value: {
|
||||
id: '{{UPLOAD_DOC_ID}}',
|
||||
},
|
||||
relationTo: 'uploads',
|
||||
},
|
||||
caption: {
|
||||
editorState: {
|
||||
root: {
|
||||
children: [
|
||||
{
|
||||
children: [
|
||||
{
|
||||
detail: 0,
|
||||
format: 0,
|
||||
mode: 'normal',
|
||||
style: '',
|
||||
text: 'upload caption',
|
||||
type: 'text',
|
||||
version: 1,
|
||||
},
|
||||
],
|
||||
direction: 'ltr',
|
||||
format: '',
|
||||
indent: 0,
|
||||
type: 'paragraph',
|
||||
version: 1,
|
||||
},
|
||||
],
|
||||
direction: 'ltr',
|
||||
format: '',
|
||||
indent: 0,
|
||||
type: 'root',
|
||||
version: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
showCaption: true,
|
||||
data: {}, // populated upload data
|
||||
},
|
||||
],
|
||||
direction: null,
|
||||
format: '',
|
||||
indent: 0,
|
||||
type: 'paragraph',
|
||||
version: 1,
|
||||
},
|
||||
{
|
||||
children: [],
|
||||
direction: null,
|
||||
format: '',
|
||||
indent: 0,
|
||||
type: 'paragraph',
|
||||
version: 1,
|
||||
},
|
||||
{
|
||||
children: [
|
||||
{
|
||||
children: [
|
||||
{
|
||||
children: [
|
||||
{
|
||||
children: [
|
||||
{
|
||||
detail: 0,
|
||||
format: 0,
|
||||
mode: 'normal',
|
||||
style: '',
|
||||
text: '2x2 table top left',
|
||||
type: 'text',
|
||||
version: 1,
|
||||
},
|
||||
],
|
||||
direction: 'ltr',
|
||||
format: '',
|
||||
indent: 0,
|
||||
type: 'paragraph',
|
||||
version: 1,
|
||||
},
|
||||
],
|
||||
direction: null,
|
||||
format: '',
|
||||
indent: 0,
|
||||
type: 'tablecell',
|
||||
version: 1,
|
||||
colSpan: 1,
|
||||
rowSpan: 1,
|
||||
backgroundColor: null,
|
||||
headerState: 3,
|
||||
},
|
||||
{
|
||||
children: [
|
||||
{
|
||||
children: [
|
||||
{
|
||||
detail: 0,
|
||||
format: 0,
|
||||
mode: 'normal',
|
||||
style: '',
|
||||
text: '2x2 table top right',
|
||||
type: 'text',
|
||||
version: 1,
|
||||
},
|
||||
],
|
||||
direction: 'ltr',
|
||||
format: '',
|
||||
indent: 0,
|
||||
type: 'paragraph',
|
||||
version: 1,
|
||||
},
|
||||
],
|
||||
direction: null,
|
||||
format: '',
|
||||
indent: 0,
|
||||
type: 'tablecell',
|
||||
version: 1,
|
||||
colSpan: 1,
|
||||
rowSpan: 1,
|
||||
backgroundColor: null,
|
||||
headerState: 1,
|
||||
},
|
||||
],
|
||||
direction: null,
|
||||
format: '',
|
||||
indent: 0,
|
||||
type: 'tablerow',
|
||||
version: 1,
|
||||
},
|
||||
{
|
||||
children: [
|
||||
{
|
||||
children: [
|
||||
{
|
||||
children: [
|
||||
{
|
||||
detail: 0,
|
||||
format: 0,
|
||||
mode: 'normal',
|
||||
style: '',
|
||||
text: '2x2 table bottom left',
|
||||
type: 'text',
|
||||
version: 1,
|
||||
},
|
||||
],
|
||||
direction: 'ltr',
|
||||
format: '',
|
||||
indent: 0,
|
||||
type: 'paragraph',
|
||||
version: 1,
|
||||
},
|
||||
],
|
||||
direction: null,
|
||||
format: '',
|
||||
indent: 0,
|
||||
type: 'tablecell',
|
||||
version: 1,
|
||||
colSpan: 1,
|
||||
rowSpan: 1,
|
||||
backgroundColor: null,
|
||||
headerState: 2,
|
||||
},
|
||||
{
|
||||
children: [
|
||||
{
|
||||
children: [
|
||||
{
|
||||
detail: 0,
|
||||
format: 0,
|
||||
mode: 'normal',
|
||||
style: '',
|
||||
text: '2x2 table bottom right',
|
||||
type: 'text',
|
||||
version: 1,
|
||||
},
|
||||
],
|
||||
direction: 'ltr',
|
||||
format: '',
|
||||
indent: 0,
|
||||
type: 'paragraph',
|
||||
version: 1,
|
||||
},
|
||||
],
|
||||
direction: null,
|
||||
format: '',
|
||||
indent: 0,
|
||||
type: 'tablecell',
|
||||
version: 1,
|
||||
colSpan: 1,
|
||||
rowSpan: 1,
|
||||
backgroundColor: null,
|
||||
headerState: 0,
|
||||
},
|
||||
],
|
||||
direction: null,
|
||||
format: '',
|
||||
indent: 0,
|
||||
type: 'tablerow',
|
||||
version: 1,
|
||||
},
|
||||
],
|
||||
direction: null,
|
||||
format: '',
|
||||
indent: 0,
|
||||
type: 'table',
|
||||
version: 1,
|
||||
},
|
||||
{
|
||||
rows: [
|
||||
{
|
||||
cells: [
|
||||
{
|
||||
colSpan: 1,
|
||||
id: 'kafuj',
|
||||
json: '{"root":{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1}],"direction":null,"format":"","indent":0,"type":"root","version":1}}',
|
||||
type: 'header',
|
||||
width: null,
|
||||
},
|
||||
{
|
||||
colSpan: 1,
|
||||
id: 'iussu',
|
||||
json: '{"root":{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1}],"direction":null,"format":"","indent":0,"type":"root","version":1}}',
|
||||
type: 'header',
|
||||
width: null,
|
||||
},
|
||||
],
|
||||
height: null,
|
||||
id: 'tnied',
|
||||
},
|
||||
{
|
||||
cells: [
|
||||
{
|
||||
colSpan: 1,
|
||||
id: 'hpnnv',
|
||||
json: '{"root":{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1}],"direction":null,"format":"","indent":0,"type":"root","version":1}}',
|
||||
type: 'header',
|
||||
width: null,
|
||||
},
|
||||
{
|
||||
colSpan: 1,
|
||||
id: 'ndteg',
|
||||
json: '{"root":{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1}],"direction":null,"format":"","indent":0,"type":"root","version":1}}',
|
||||
type: 'normal',
|
||||
width: null,
|
||||
},
|
||||
],
|
||||
height: null,
|
||||
id: 'rxyey',
|
||||
},
|
||||
{
|
||||
cells: [
|
||||
{
|
||||
colSpan: 1,
|
||||
id: 'rtueq',
|
||||
json: '{"root":{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1}],"direction":null,"format":"","indent":0,"type":"root","version":1}}',
|
||||
type: 'header',
|
||||
width: null,
|
||||
},
|
||||
{
|
||||
colSpan: 1,
|
||||
id: 'vrzoi',
|
||||
json: '{"root":{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1}],"direction":null,"format":"","indent":0,"type":"root","version":1}}',
|
||||
type: 'normal',
|
||||
width: null,
|
||||
},
|
||||
],
|
||||
height: null,
|
||||
id: 'qzglv',
|
||||
},
|
||||
],
|
||||
type: 'tablesheet',
|
||||
version: 1,
|
||||
},
|
||||
{
|
||||
children: [
|
||||
{
|
||||
detail: 0,
|
||||
format: 0,
|
||||
mode: 'normal',
|
||||
style: '',
|
||||
text: 'youtube:',
|
||||
type: 'text',
|
||||
version: 1,
|
||||
},
|
||||
],
|
||||
direction: 'ltr',
|
||||
format: '',
|
||||
indent: 0,
|
||||
type: 'paragraph',
|
||||
version: 1,
|
||||
},
|
||||
{
|
||||
format: '',
|
||||
type: 'youtube',
|
||||
version: 1,
|
||||
videoID: '3Nwt3qu0_UY',
|
||||
},
|
||||
{
|
||||
children: [
|
||||
{
|
||||
equation: '3+3',
|
||||
inline: true,
|
||||
type: 'equation',
|
||||
version: 1,
|
||||
},
|
||||
],
|
||||
direction: null,
|
||||
format: '',
|
||||
indent: 0,
|
||||
type: 'paragraph',
|
||||
version: 1,
|
||||
},
|
||||
{
|
||||
children: [
|
||||
{
|
||||
children: [
|
||||
{
|
||||
detail: 0,
|
||||
format: 0,
|
||||
mode: 'normal',
|
||||
style: '',
|
||||
text: 'collapsible title',
|
||||
type: 'text',
|
||||
version: 1,
|
||||
},
|
||||
],
|
||||
direction: 'ltr',
|
||||
format: '',
|
||||
indent: 0,
|
||||
type: 'collapsible-title',
|
||||
version: 1,
|
||||
},
|
||||
{
|
||||
children: [
|
||||
{
|
||||
children: [
|
||||
{
|
||||
detail: 0,
|
||||
format: 0,
|
||||
mode: 'normal',
|
||||
style: '',
|
||||
text: 'collabsible conteent',
|
||||
type: 'text',
|
||||
version: 1,
|
||||
},
|
||||
],
|
||||
direction: 'ltr',
|
||||
format: '',
|
||||
indent: 0,
|
||||
type: 'paragraph',
|
||||
version: 1,
|
||||
},
|
||||
],
|
||||
direction: 'ltr',
|
||||
format: '',
|
||||
indent: 0,
|
||||
type: 'collapsible-content',
|
||||
version: 1,
|
||||
},
|
||||
],
|
||||
direction: 'ltr',
|
||||
format: '',
|
||||
indent: 0,
|
||||
type: 'collapsible-container',
|
||||
version: 1,
|
||||
open: true,
|
||||
},
|
||||
{
|
||||
children: [],
|
||||
direction: null,
|
||||
format: '',
|
||||
indent: 0,
|
||||
type: 'paragraph',
|
||||
version: 1,
|
||||
},
|
||||
{
|
||||
type: 'horizontalrule',
|
||||
version: 1,
|
||||
},
|
||||
],
|
||||
direction: 'ltr',
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -2,6 +2,7 @@ import type { CollectionConfig } from '../../../../packages/payload/src/collecti
|
||||
|
||||
import {
|
||||
BlocksFeature,
|
||||
LexicalPluginToLexicalFeature,
|
||||
LinkFeature,
|
||||
TreeviewFeature,
|
||||
UploadFeature,
|
||||
@@ -16,6 +17,7 @@ import {
|
||||
UploadAndRichTextBlock,
|
||||
} from './blocks'
|
||||
import { generateLexicalRichText } from './generateLexicalRichText'
|
||||
import { payloadPluginLexicalData } from './generatePayloadPluginLexicalData'
|
||||
|
||||
export const LexicalFields: CollectionConfig = {
|
||||
slug: 'lexical-fields',
|
||||
@@ -32,6 +34,45 @@ export const LexicalFields: CollectionConfig = {
|
||||
type: 'text',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: 'richTextLexicalWithLexicalPluginData',
|
||||
type: 'richText',
|
||||
editor: lexicalEditor({
|
||||
features: ({ defaultFeatures }) => [
|
||||
...defaultFeatures,
|
||||
LexicalPluginToLexicalFeature(),
|
||||
TreeviewFeature(),
|
||||
LinkFeature({
|
||||
fields: [
|
||||
{
|
||||
name: 'rel',
|
||||
label: 'Rel Attribute',
|
||||
type: 'select',
|
||||
hasMany: true,
|
||||
options: ['noopener', 'noreferrer', 'nofollow'],
|
||||
admin: {
|
||||
description:
|
||||
'The rel attribute defines the relationship between a linked resource and the current document. This is a custom link field.',
|
||||
},
|
||||
},
|
||||
],
|
||||
}),
|
||||
UploadFeature({
|
||||
collections: {
|
||||
uploads: {
|
||||
fields: [
|
||||
{
|
||||
name: 'caption',
|
||||
type: 'richText',
|
||||
editor: lexicalEditor(),
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
}),
|
||||
],
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: 'richTextLexicalCustomFields',
|
||||
type: 'richText',
|
||||
@@ -87,4 +128,5 @@ export const LexicalFields: CollectionConfig = {
|
||||
export const LexicalRichTextDoc = {
|
||||
title: 'Rich Text',
|
||||
richTextLexicalCustomFields: generateLexicalRichText(),
|
||||
richTextLexicalWithLexicalPluginData: payloadPluginLexicalData,
|
||||
}
|
||||
|
||||
@@ -3,63 +3,81 @@ import type { CollectionConfig } from '../../../../packages/payload/src/collecti
|
||||
export const relationshipFieldsSlug = 'relationship-fields'
|
||||
|
||||
const RelationshipFields: CollectionConfig = {
|
||||
slug: relationshipFieldsSlug,
|
||||
fields: [
|
||||
{
|
||||
name: 'text',
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
name: 'relationship',
|
||||
type: 'relationship',
|
||||
relationTo: ['text-fields', 'array-fields'],
|
||||
required: true,
|
||||
type: 'relationship',
|
||||
},
|
||||
{
|
||||
name: 'relationToSelf',
|
||||
type: 'relationship',
|
||||
relationTo: relationshipFieldsSlug,
|
||||
type: 'relationship',
|
||||
},
|
||||
{
|
||||
name: 'relationToSelfSelectOnly',
|
||||
type: 'relationship',
|
||||
relationTo: relationshipFieldsSlug,
|
||||
admin: {
|
||||
allowCreate: false,
|
||||
},
|
||||
relationTo: relationshipFieldsSlug,
|
||||
type: 'relationship',
|
||||
},
|
||||
{
|
||||
name: 'relationWithDynamicDefault',
|
||||
type: 'relationship',
|
||||
defaultValue: ({ user }) => user?.id,
|
||||
relationTo: 'users',
|
||||
defaultValue: ({ user }) => user.id,
|
||||
type: 'relationship',
|
||||
},
|
||||
{
|
||||
name: 'relationHasManyWithDynamicDefault',
|
||||
type: 'relationship',
|
||||
defaultValue: ({ user }) =>
|
||||
user
|
||||
? {
|
||||
relationTo: 'users',
|
||||
value: user.id,
|
||||
}
|
||||
: undefined,
|
||||
relationTo: ['users'],
|
||||
defaultValue: ({ user }) => ({
|
||||
relationTo: 'users',
|
||||
value: user.id,
|
||||
}),
|
||||
type: 'relationship',
|
||||
},
|
||||
{
|
||||
name: 'relationshipWithMin',
|
||||
type: 'relationship',
|
||||
relationTo: 'text-fields',
|
||||
hasMany: true,
|
||||
minRows: 2,
|
||||
relationTo: 'text-fields',
|
||||
type: 'relationship',
|
||||
},
|
||||
{
|
||||
name: 'relationshipWithMax',
|
||||
type: 'relationship',
|
||||
relationTo: 'text-fields',
|
||||
hasMany: true,
|
||||
maxRows: 2,
|
||||
relationTo: 'text-fields',
|
||||
type: 'relationship',
|
||||
},
|
||||
{
|
||||
name: 'relationshipHasMany',
|
||||
type: 'relationship',
|
||||
relationTo: 'text-fields',
|
||||
hasMany: true,
|
||||
relationTo: 'text-fields',
|
||||
type: 'relationship',
|
||||
},
|
||||
{
|
||||
name: 'array',
|
||||
fields: [
|
||||
{
|
||||
name: 'relationship',
|
||||
relationTo: 'text-fields',
|
||||
type: 'relationship',
|
||||
},
|
||||
],
|
||||
type: 'array',
|
||||
},
|
||||
],
|
||||
slug: relationshipFieldsSlug,
|
||||
}
|
||||
|
||||
export default RelationshipFields
|
||||
|
||||
@@ -44,18 +44,18 @@ export default buildConfigWithDefaults({
|
||||
collections: [
|
||||
LexicalFields,
|
||||
{
|
||||
slug: 'users',
|
||||
auth: true,
|
||||
admin: {
|
||||
useAsTitle: 'email',
|
||||
},
|
||||
auth: true,
|
||||
fields: [
|
||||
{
|
||||
name: 'canViewConditionalField',
|
||||
type: 'checkbox',
|
||||
defaultValue: true,
|
||||
type: 'checkbox',
|
||||
},
|
||||
],
|
||||
slug: 'users',
|
||||
},
|
||||
ArrayFields,
|
||||
BlockFields,
|
||||
@@ -81,8 +81,8 @@ export default buildConfigWithDefaults({
|
||||
],
|
||||
localization: {
|
||||
defaultLocale: 'en',
|
||||
locales: ['en', 'es'],
|
||||
fallback: true,
|
||||
locales: ['en', 'es'],
|
||||
},
|
||||
onInit: async (payload) => {
|
||||
await payload.create({
|
||||
|
||||
@@ -1093,7 +1093,7 @@ describe('fields', () => {
|
||||
.locator('#field-relationship .relationship-add-new__relation-button--text-fields')
|
||||
.click()
|
||||
|
||||
const textField = page.locator('#field-text')
|
||||
const textField = page.locator('.drawer__content #field-text')
|
||||
const textValue = 'hello'
|
||||
|
||||
await textField.fill(textValue)
|
||||
@@ -1217,7 +1217,7 @@ describe('fields', () => {
|
||||
.locator('#field-relationship .relationship-add-new__relation-button--text-fields')
|
||||
.click()
|
||||
|
||||
await page.locator('#field-text').fill('something')
|
||||
await page.locator('.drawer__content #field-text').fill('something')
|
||||
|
||||
await page.locator('[id^=doc-drawer_text-fields_1_] #action-save').click()
|
||||
await expect(page.locator('.Toastify')).toContainText('successfully')
|
||||
@@ -1290,7 +1290,7 @@ describe('fields', () => {
|
||||
await page.getByRole('button', { name: 'Edit Seeded text document' }).click()
|
||||
|
||||
// Fill 'text' field of 'Seeded text document'
|
||||
await page.locator('#field-text').fill('some updated text value')
|
||||
await page.locator('.drawer__content #field-text').fill('some updated text value')
|
||||
|
||||
// Save drawer (not parent page) with hotkey
|
||||
await saveDocHotkeyAndAssert(page)
|
||||
|
||||
@@ -21,6 +21,7 @@ import {
|
||||
} from './collections/Group'
|
||||
import { defaultNumber, numberDoc } from './collections/Number'
|
||||
import { pointDoc } from './collections/Point'
|
||||
import { relationshipFieldsSlug } from './collections/Relationship'
|
||||
import { tabsDoc } from './collections/Tabs'
|
||||
import {
|
||||
localizedTextValue,
|
||||
@@ -73,6 +74,122 @@ describe('Fields', () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe('relationship', () => {
|
||||
let textDoc
|
||||
let otherTextDoc
|
||||
let selfReferencing
|
||||
let parent
|
||||
let child
|
||||
let grandChild
|
||||
let relationshipInArray
|
||||
const textDocText = 'text document'
|
||||
const otherTextDocText = 'alt text'
|
||||
const relationshipText = 'relationship text'
|
||||
|
||||
beforeAll(async () => {
|
||||
textDoc = await payload.create({
|
||||
collection: 'text-fields',
|
||||
data: {
|
||||
text: textDocText,
|
||||
},
|
||||
})
|
||||
otherTextDoc = await payload.create({
|
||||
collection: 'text-fields',
|
||||
data: {
|
||||
text: otherTextDocText,
|
||||
},
|
||||
})
|
||||
const relationship = { relationTo: 'text-fields', value: textDoc.id }
|
||||
parent = await payload.create({
|
||||
collection: relationshipFieldsSlug,
|
||||
data: {
|
||||
relationship,
|
||||
text: relationshipText,
|
||||
},
|
||||
})
|
||||
|
||||
child = await payload.create({
|
||||
collection: relationshipFieldsSlug,
|
||||
data: {
|
||||
relationToSelf: parent.id,
|
||||
relationship,
|
||||
text: relationshipText,
|
||||
},
|
||||
})
|
||||
|
||||
grandChild = await payload.create({
|
||||
collection: relationshipFieldsSlug,
|
||||
data: {
|
||||
relationToSelf: child.id,
|
||||
relationship,
|
||||
text: relationshipText,
|
||||
},
|
||||
})
|
||||
|
||||
selfReferencing = await payload.create({
|
||||
collection: relationshipFieldsSlug,
|
||||
data: {
|
||||
relationship,
|
||||
text: relationshipText,
|
||||
},
|
||||
})
|
||||
|
||||
relationshipInArray = await payload.create({
|
||||
collection: relationshipFieldsSlug,
|
||||
data: {
|
||||
array: [
|
||||
{
|
||||
relationship: otherTextDoc.id,
|
||||
},
|
||||
],
|
||||
relationship,
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
it('should query parent self-reference', async () => {
|
||||
const childResult = await payload.find({
|
||||
collection: relationshipFieldsSlug,
|
||||
where: {
|
||||
relationToSelf: { equals: parent.id },
|
||||
},
|
||||
})
|
||||
|
||||
const grandChildResult = await payload.find({
|
||||
collection: relationshipFieldsSlug,
|
||||
where: {
|
||||
relationToSelf: { equals: child.id },
|
||||
},
|
||||
})
|
||||
|
||||
const anyChildren = await payload.find({
|
||||
collection: relationshipFieldsSlug,
|
||||
})
|
||||
const allChildren = await payload.find({
|
||||
collection: relationshipFieldsSlug,
|
||||
where: {
|
||||
'relationToSelf.text': { equals: relationshipText },
|
||||
},
|
||||
})
|
||||
|
||||
expect(childResult.docs[0].id).toStrictEqual(child.id)
|
||||
expect(grandChildResult.docs[0].id).toStrictEqual(grandChild.id)
|
||||
expect(allChildren.docs).toHaveLength(2)
|
||||
})
|
||||
|
||||
it('should query relationship inside array', async () => {
|
||||
const result = await payload.find({
|
||||
collection: relationshipFieldsSlug,
|
||||
where: {
|
||||
'array.relationship.text': { equals: otherTextDocText },
|
||||
},
|
||||
})
|
||||
|
||||
expect(result.docs).toHaveLength(1)
|
||||
expect(result.docs[0]).toMatchObject(relationshipInArray)
|
||||
})
|
||||
})
|
||||
|
||||
describe('timestamps', () => {
|
||||
const tenMinutesAgo = new Date(Date.now() - 1000 * 60 * 10)
|
||||
let doc
|
||||
|
||||
Reference in New Issue
Block a user