feat(richtext-lexical): relationships
This commit is contained in:
@@ -68,11 +68,11 @@ const RelationshipDrawerComponent: React.FC<Props> = ({ enabledCollectionSlugs }
|
||||
}, [editor, openDrawer])
|
||||
|
||||
const onSelect = useCallback(
|
||||
({ collectionConfig, docID }) => {
|
||||
({ collectionSlug, docID }) => {
|
||||
insertRelationship({
|
||||
id: docID,
|
||||
editor,
|
||||
relationTo: collectionConfig.slug,
|
||||
relationTo: collectionSlug,
|
||||
replaceNodeKey,
|
||||
})
|
||||
closeDrawer()
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
'use client'
|
||||
|
||||
import { withMergedProps } from '@payloadcms/ui'
|
||||
|
||||
import type { FeatureProviderProviderClient } from '../types'
|
||||
import type { RelationshipFeatureProps } from './feature.server'
|
||||
|
||||
import { SlashMenuOption } from '../../lexical/plugins/SlashMenu/LexicalTypeaheadMenuPlugin/types'
|
||||
import { RelationshipIcon } from '../../lexical/ui/icons/Relationship'
|
||||
import { createClientComponent } from '../createClientComponent'
|
||||
import { INSERT_RELATIONSHIP_WITH_DRAWER_COMMAND } from './drawer/commands'
|
||||
import { RelationshipNode } from './nodes/RelationshipNode'
|
||||
import { RelationshipPlugin } from './plugins'
|
||||
|
||||
const RelationshipFeatureClient: FeatureProviderProviderClient<RelationshipFeatureProps> = (
|
||||
props,
|
||||
) => {
|
||||
return {
|
||||
clientFeatureProps: props,
|
||||
feature: () => ({
|
||||
clientFeatureProps: props,
|
||||
nodes: [RelationshipNode],
|
||||
plugins: [
|
||||
{
|
||||
Component: withMergedProps({
|
||||
Component: RelationshipPlugin,
|
||||
toMergeIntoProps: props,
|
||||
}),
|
||||
position: 'normal',
|
||||
},
|
||||
],
|
||||
slashMenu: {
|
||||
options: [
|
||||
{
|
||||
displayName: 'Basic',
|
||||
key: 'basic',
|
||||
options: [
|
||||
new SlashMenuOption('relationship', {
|
||||
Icon: RelationshipIcon,
|
||||
displayName: 'Relationship',
|
||||
keywords: ['relationship', 'relation', 'rel'],
|
||||
onSelect: ({ editor }) => {
|
||||
// dispatch INSERT_RELATIONSHIP_WITH_DRAWER_COMMAND
|
||||
editor.dispatchCommand(INSERT_RELATIONSHIP_WITH_DRAWER_COMMAND, {
|
||||
replace: false,
|
||||
})
|
||||
},
|
||||
}),
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
export const RelationshipFeatureClientComponent = createClientComponent(RelationshipFeatureClient)
|
||||
@@ -0,0 +1,51 @@
|
||||
import type { FeatureProviderProviderServer } from '../types'
|
||||
|
||||
import { RelationshipFeatureClientComponent } from './feature.client'
|
||||
import { RelationshipNode } from './nodes/RelationshipNode'
|
||||
import { relationshipPopulationPromise } from './populationPromise'
|
||||
|
||||
export type RelationshipFeatureProps =
|
||||
| {
|
||||
/**
|
||||
* The collections that should be disabled. Overrides the `enableRichTextRelationship` property in the collection config.
|
||||
* When this property is set, `enabledCollections` will not be available.
|
||||
**/
|
||||
disabledCollections?: string[]
|
||||
|
||||
// Ensures that enabledCollections is not available when disabledCollections is set
|
||||
enabledCollections?: never
|
||||
}
|
||||
| {
|
||||
// Ensures that disabledCollections is not available when enabledCollections is set
|
||||
disabledCollections?: never
|
||||
|
||||
/**
|
||||
* The collections that should be enabled. Overrides the `enableRichTextRelationship` property in the collection config
|
||||
* When this property is set, `disabledCollections` will not be available.
|
||||
**/
|
||||
enabledCollections?: string[]
|
||||
}
|
||||
|
||||
export const RelationshipFeature: FeatureProviderProviderServer<
|
||||
RelationshipFeatureProps,
|
||||
RelationshipFeatureProps
|
||||
> = (props) => {
|
||||
return {
|
||||
feature: () => {
|
||||
return {
|
||||
ClientComponent: RelationshipFeatureClientComponent,
|
||||
clientFeatureProps: props,
|
||||
nodes: [
|
||||
{
|
||||
node: RelationshipNode,
|
||||
populationPromises: [relationshipPopulationPromise],
|
||||
// TODO: Add validation similar to upload
|
||||
},
|
||||
],
|
||||
serverFeatureProps: props,
|
||||
}
|
||||
},
|
||||
key: 'relationship',
|
||||
serverFeatureProps: props,
|
||||
}
|
||||
}
|
||||
@@ -1,88 +0,0 @@
|
||||
import type { FeatureProvider } from '../types'
|
||||
|
||||
import { SlashMenuOption } from '../../lexical/plugins/SlashMenu/LexicalTypeaheadMenuPlugin/types'
|
||||
import { INSERT_RELATIONSHIP_WITH_DRAWER_COMMAND } from './drawer/commands'
|
||||
import { RelationshipNode } from './nodes/RelationshipNode'
|
||||
import { relationshipPopulationPromise } from './populationPromise'
|
||||
|
||||
export type RelationshipFeatureProps =
|
||||
| {
|
||||
/**
|
||||
* The collections that should be disabled. Overrides the `enableRichTextRelationship` property in the collection config.
|
||||
* When this property is set, `enabledCollections` will not be available.
|
||||
**/
|
||||
disabledCollections?: string[]
|
||||
|
||||
// Ensures that enabledCollections is not available when disabledCollections is set
|
||||
enabledCollections?: never
|
||||
}
|
||||
| {
|
||||
// Ensures that disabledCollections is not available when enabledCollections is set
|
||||
disabledCollections?: never
|
||||
|
||||
/**
|
||||
* The collections that should be enabled. Overrides the `enableRichTextRelationship` property in the collection config
|
||||
* When this property is set, `disabledCollections` will not be available.
|
||||
**/
|
||||
enabledCollections?: string[]
|
||||
}
|
||||
|
||||
export const RelationshipFeature = (props?: RelationshipFeatureProps): FeatureProvider => {
|
||||
return {
|
||||
feature: () => {
|
||||
return {
|
||||
nodes: [
|
||||
{
|
||||
type: RelationshipNode.getType(),
|
||||
node: RelationshipNode,
|
||||
populationPromises: [relationshipPopulationPromise],
|
||||
// TODO: Add validation similar to upload
|
||||
},
|
||||
],
|
||||
plugins: [
|
||||
{
|
||||
Component: () =>
|
||||
// @ts-expect-error-next-line
|
||||
import('./plugins').then((module) => {
|
||||
const RelationshipPlugin = module.RelationshipPlugin
|
||||
return import('@payloadcms/ui').then((module2) =>
|
||||
module2.withMergedProps({
|
||||
Component: RelationshipPlugin,
|
||||
toMergeIntoProps: props,
|
||||
}),
|
||||
)
|
||||
}),
|
||||
position: 'normal',
|
||||
},
|
||||
],
|
||||
props: props,
|
||||
slashMenu: {
|
||||
options: [
|
||||
{
|
||||
displayName: 'Basic',
|
||||
key: 'basic',
|
||||
options: [
|
||||
new SlashMenuOption('relationship', {
|
||||
Icon: () =>
|
||||
// @ts-expect-error-next-line
|
||||
import('../../lexical/ui/icons/Relationship').then(
|
||||
(module) => module.RelationshipIcon,
|
||||
),
|
||||
displayName: 'Relationship',
|
||||
keywords: ['relationship', 'relation', 'rel'],
|
||||
onSelect: ({ editor }) => {
|
||||
// dispatch INSERT_RELATIONSHIP_WITH_DRAWER_COMMAND
|
||||
editor.dispatchCommand(INSERT_RELATIONSHIP_WITH_DRAWER_COMMAND, {
|
||||
replace: false,
|
||||
})
|
||||
},
|
||||
}),
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
},
|
||||
key: 'relationship',
|
||||
}
|
||||
}
|
||||
@@ -57,7 +57,7 @@ const Component: React.FC<Props> = (props) => {
|
||||
)
|
||||
|
||||
const [DocumentDrawer, DocumentDrawerToggler, { closeDrawer }] = useDocumentDrawer({
|
||||
id: id,
|
||||
id,
|
||||
collectionSlug: relatedCollection.slug,
|
||||
})
|
||||
|
||||
@@ -93,7 +93,7 @@ const Component: React.FC<Props> = (props) => {
|
||||
</p>
|
||||
<DocumentDrawerToggler className={`${baseClass}__doc-drawer-toggler`}>
|
||||
<p className={`${baseClass}__title`}>
|
||||
{data[relatedCollection?.admin?.useAsTitle || 'id']}
|
||||
{data ? data[relatedCollection?.admin?.useAsTitle || 'id'] : id}
|
||||
</p>
|
||||
</DocumentDrawerToggler>
|
||||
</div>
|
||||
@@ -102,7 +102,7 @@ const Component: React.FC<Props> = (props) => {
|
||||
<Button
|
||||
buttonStyle="icon-label"
|
||||
className={`${baseClass}__swapButton`}
|
||||
disabled={field?.admin?.readOnly}
|
||||
disabled={field?.readOnly}
|
||||
el="div"
|
||||
icon="swap"
|
||||
onClick={() => {
|
||||
@@ -116,7 +116,7 @@ const Component: React.FC<Props> = (props) => {
|
||||
<Button
|
||||
buttonStyle="icon-label"
|
||||
className={`${baseClass}__removeButton`}
|
||||
disabled={field?.admin?.readOnly}
|
||||
disabled={field?.readOnly}
|
||||
icon="x"
|
||||
onClick={(e) => {
|
||||
e.preventDefault()
|
||||
|
||||
@@ -14,7 +14,7 @@ import {
|
||||
import { useEffect } from 'react'
|
||||
import React from 'react'
|
||||
|
||||
import type { RelationshipFeatureProps } from '../index'
|
||||
import type { RelationshipFeatureProps } from '../feature.server'
|
||||
import type { RelationshipData } from '../nodes/RelationshipNode'
|
||||
|
||||
import { RelationshipDrawer } from '../drawer'
|
||||
@@ -24,7 +24,7 @@ export const INSERT_RELATIONSHIP_COMMAND: LexicalCommand<RelationshipData> = cre
|
||||
'INSERT_RELATIONSHIP_COMMAND',
|
||||
)
|
||||
|
||||
export function RelationshipPlugin(props?: RelationshipFeatureProps): JSX.Element | null {
|
||||
export function RelationshipPlugin(props?: RelationshipFeatureProps): React.ReactNode {
|
||||
const [editor] = useLexicalComposerContext()
|
||||
const { collections } = useConfig()
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ import { CheckListFeature } from '../../../features/lists/checklist/feature.serv
|
||||
import { OrderedListFeature } from '../../../features/lists/orderedlist/feature.server'
|
||||
import { UnorderedListFeature } from '../../../features/lists/unorderedlist/feature.server'
|
||||
import { ParagraphFeature } from '../../../features/paragraph/feature.server'
|
||||
import { RelationshipFeature } from '../../../features/relationship'
|
||||
import { RelationshipFeature } from '../../../features/relationship/feature.server'
|
||||
import { UploadFeature } from '../../../features/upload'
|
||||
import { LexicalEditorTheme } from '../../theme/EditorTheme'
|
||||
import { sanitizeServerEditorConfig } from './sanitize'
|
||||
|
||||
@@ -319,7 +319,7 @@ export type {
|
||||
} from './field/features/migrations/slateToLexical/converter/types'
|
||||
export { SlateToLexicalFeature } from './field/features/migrations/slateToLexical/feature.server'
|
||||
export { ParagraphFeature } from './field/features/paragraph/feature.server'
|
||||
export { RelationshipFeature } from './field/features/relationship'
|
||||
export { RelationshipFeature } from './field/features/relationship/feature.server'
|
||||
export {
|
||||
$createRelationshipNode,
|
||||
$isRelationshipNode,
|
||||
|
||||
31
pnpm-lock.yaml
generated
31
pnpm-lock.yaml
generated
@@ -668,9 +668,6 @@ importers:
|
||||
scmp:
|
||||
specifier: 2.1.0
|
||||
version: 2.1.0
|
||||
sharp:
|
||||
specifier: 0.33.2
|
||||
version: 0.33.2
|
||||
devDependencies:
|
||||
'@monaco-editor/react':
|
||||
specifier: 4.5.1
|
||||
@@ -804,6 +801,9 @@ importers:
|
||||
serve-static:
|
||||
specifier: 1.15.0
|
||||
version: 1.15.0
|
||||
sharp:
|
||||
specifier: 0.33.2
|
||||
version: 0.33.2
|
||||
ts-essentials:
|
||||
specifier: 7.0.3
|
||||
version: 7.0.3(typescript@5.2.2)
|
||||
@@ -2581,6 +2581,7 @@ packages:
|
||||
requiresBuild: true
|
||||
dependencies:
|
||||
tslib: 2.6.2
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/@emotion/babel-plugin@11.11.0:
|
||||
@@ -3280,6 +3281,7 @@ packages:
|
||||
requiresBuild: true
|
||||
optionalDependencies:
|
||||
'@img/sharp-libvips-darwin-arm64': 1.0.1
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/@img/sharp-darwin-x64@0.33.2:
|
||||
@@ -3290,6 +3292,7 @@ packages:
|
||||
requiresBuild: true
|
||||
optionalDependencies:
|
||||
'@img/sharp-libvips-darwin-x64': 1.0.1
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/@img/sharp-libvips-darwin-arm64@1.0.1:
|
||||
@@ -3298,6 +3301,7 @@ packages:
|
||||
cpu: [arm64]
|
||||
os: [darwin]
|
||||
requiresBuild: true
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/@img/sharp-libvips-darwin-x64@1.0.1:
|
||||
@@ -3306,6 +3310,7 @@ packages:
|
||||
cpu: [x64]
|
||||
os: [darwin]
|
||||
requiresBuild: true
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/@img/sharp-libvips-linux-arm64@1.0.1:
|
||||
@@ -3314,6 +3319,7 @@ packages:
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
requiresBuild: true
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/@img/sharp-libvips-linux-arm@1.0.1:
|
||||
@@ -3322,6 +3328,7 @@ packages:
|
||||
cpu: [arm]
|
||||
os: [linux]
|
||||
requiresBuild: true
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/@img/sharp-libvips-linux-s390x@1.0.1:
|
||||
@@ -3330,6 +3337,7 @@ packages:
|
||||
cpu: [s390x]
|
||||
os: [linux]
|
||||
requiresBuild: true
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/@img/sharp-libvips-linux-x64@1.0.1:
|
||||
@@ -3338,6 +3346,7 @@ packages:
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
requiresBuild: true
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/@img/sharp-libvips-linuxmusl-arm64@1.0.1:
|
||||
@@ -3346,6 +3355,7 @@ packages:
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
requiresBuild: true
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/@img/sharp-libvips-linuxmusl-x64@1.0.1:
|
||||
@@ -3354,6 +3364,7 @@ packages:
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
requiresBuild: true
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/@img/sharp-linux-arm64@0.33.2:
|
||||
@@ -3364,6 +3375,7 @@ packages:
|
||||
requiresBuild: true
|
||||
optionalDependencies:
|
||||
'@img/sharp-libvips-linux-arm64': 1.0.1
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/@img/sharp-linux-arm@0.33.2:
|
||||
@@ -3374,6 +3386,7 @@ packages:
|
||||
requiresBuild: true
|
||||
optionalDependencies:
|
||||
'@img/sharp-libvips-linux-arm': 1.0.1
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/@img/sharp-linux-s390x@0.33.2:
|
||||
@@ -3384,6 +3397,7 @@ packages:
|
||||
requiresBuild: true
|
||||
optionalDependencies:
|
||||
'@img/sharp-libvips-linux-s390x': 1.0.1
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/@img/sharp-linux-x64@0.33.2:
|
||||
@@ -3394,6 +3408,7 @@ packages:
|
||||
requiresBuild: true
|
||||
optionalDependencies:
|
||||
'@img/sharp-libvips-linux-x64': 1.0.1
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/@img/sharp-linuxmusl-arm64@0.33.2:
|
||||
@@ -3404,6 +3419,7 @@ packages:
|
||||
requiresBuild: true
|
||||
optionalDependencies:
|
||||
'@img/sharp-libvips-linuxmusl-arm64': 1.0.1
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/@img/sharp-linuxmusl-x64@0.33.2:
|
||||
@@ -3414,6 +3430,7 @@ packages:
|
||||
requiresBuild: true
|
||||
optionalDependencies:
|
||||
'@img/sharp-libvips-linuxmusl-x64': 1.0.1
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/@img/sharp-wasm32@0.33.2:
|
||||
@@ -3423,6 +3440,7 @@ packages:
|
||||
requiresBuild: true
|
||||
dependencies:
|
||||
'@emnapi/runtime': 0.45.0
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/@img/sharp-win32-ia32@0.33.2:
|
||||
@@ -3431,6 +3449,7 @@ packages:
|
||||
cpu: [ia32]
|
||||
os: [win32]
|
||||
requiresBuild: true
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/@img/sharp-win32-x64@0.33.2:
|
||||
@@ -3439,6 +3458,7 @@ packages:
|
||||
cpu: [x64]
|
||||
os: [win32]
|
||||
requiresBuild: true
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/@isaacs/cliui@8.0.2:
|
||||
@@ -7309,6 +7329,7 @@ packages:
|
||||
dependencies:
|
||||
color-name: 1.1.4
|
||||
simple-swizzle: 0.2.2
|
||||
dev: true
|
||||
|
||||
/color@4.2.3:
|
||||
resolution: {integrity: sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==}
|
||||
@@ -7316,6 +7337,7 @@ packages:
|
||||
dependencies:
|
||||
color-convert: 2.0.1
|
||||
color-string: 1.9.1
|
||||
dev: true
|
||||
|
||||
/colorette@2.0.20:
|
||||
resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==}
|
||||
@@ -10359,6 +10381,7 @@ packages:
|
||||
|
||||
/is-arrayish@0.3.2:
|
||||
resolution: {integrity: sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==}
|
||||
dev: true
|
||||
|
||||
/is-async-function@2.0.0:
|
||||
resolution: {integrity: sha512-Y1JXKrfykRJGdlDwdKlLpLyMIiWqWvuSd17TvZk68PLAOGOoF4Xyav1z0Xhoi+gCYjZVeC5SI+hYFOfvXmGRCA==}
|
||||
@@ -14328,6 +14351,7 @@ packages:
|
||||
'@img/sharp-wasm32': 0.33.2
|
||||
'@img/sharp-win32-ia32': 0.33.2
|
||||
'@img/sharp-win32-x64': 0.33.2
|
||||
dev: true
|
||||
|
||||
/shebang-command@1.2.0:
|
||||
resolution: {integrity: sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==}
|
||||
@@ -14394,6 +14418,7 @@ packages:
|
||||
resolution: {integrity: sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==}
|
||||
dependencies:
|
||||
is-arrayish: 0.3.2
|
||||
dev: true
|
||||
|
||||
/simple-update-notifier@2.0.0:
|
||||
resolution: {integrity: sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==}
|
||||
|
||||
@@ -5,6 +5,9 @@ import { mediaSlug } from '../Media'
|
||||
export const postsSlug = 'posts'
|
||||
|
||||
export const PostsCollection: CollectionConfig = {
|
||||
admin: {
|
||||
useAsTitle: 'text',
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
name: 'text',
|
||||
|
||||
@@ -10,6 +10,7 @@ import {
|
||||
ItalicFeature,
|
||||
LinkFeature,
|
||||
OrderedListFeature,
|
||||
RelationshipFeature,
|
||||
StrikethroughFeature,
|
||||
SubscriptFeature,
|
||||
SuperscriptFeature,
|
||||
@@ -95,6 +96,7 @@ export function buildConfigWithDefaults(testConfig?: Partial<Config>): Promise<S
|
||||
editor: lexicalEditor({
|
||||
features: [
|
||||
ParagraphFeature(),
|
||||
RelationshipFeature(),
|
||||
LinkFeature(),
|
||||
CheckListFeature(),
|
||||
UnorderedListFeature(),
|
||||
|
||||
Reference in New Issue
Block a user