Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions packages/core/content-manager/admin/src/content-manager.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
/* eslint-disable check-file/filename-naming-convention */

import { INJECTION_ZONES } from './components/InjectionZone';
import { PLUGIN_ID } from './constants/plugin';
import {
DEFAULT_ACTIONS,
type DocumentActionDescription,
} from './pages/EditView/components/DocumentActions';
import { RichTextBlocksStore } from './pages/EditView/components/FormInputs/BlocksInput/BlocksEditor';
import { defaultBlocksStore } from './pages/EditView/components/FormInputs/BlocksInput/DefaultBlocksStore';
import {
DEFAULT_HEADER_ACTIONS,
type HeaderActionDescription,
Expand All @@ -25,6 +28,7 @@ import type { DescriptionComponent, PluginConfig } from '@strapi/admin/strapi-ad
* -----------------------------------------------------------------------------------------------*/

type DescriptionReducer<Config extends object> = (prev: Config[]) => Config[];
type DescriptionObjReducer<Config extends object> = (prev: Config) => Config;

interface EditViewContext {
/**
Expand Down Expand Up @@ -119,6 +123,7 @@ class ContentManagerPlugin {
* application, so instead we collate them and run them later with the complete list incl.
* ones already registered & the context of the view.
*/
richTextBlocksStore: RichTextBlocksStore = { ...defaultBlocksStore };
bulkActions: BulkActionComponent[] = [...DEFAULT_BULK_ACTIONS];
documentActions: DocumentActionComponent[] = [
...DEFAULT_ACTIONS,
Expand All @@ -130,6 +135,22 @@ class ContentManagerPlugin {

constructor() {}

addRichTextBlocks(blocks: RichTextBlocksStore): void;
addRichTextBlocks(blocks: DescriptionObjReducer<RichTextBlocksStore>): void;
addRichTextBlocks(blocks: RichTextBlocksStore | DescriptionObjReducer<RichTextBlocksStore>) {
if (typeof blocks === 'function') {
this.richTextBlocksStore = blocks(this.richTextBlocksStore);
} else if (typeof blocks === 'object') {
this.richTextBlocksStore = { ...this.richTextBlocksStore, ...blocks };
} else {
throw new Error(
`Expected the \`blocks\` passed to \`addRichTextBlocks\` to be an object or a function, but received ${getPrintableType(
blocks
)}`
);
}
}

addEditViewSidePanel(panels: DescriptionReducer<PanelComponent>): void;
addEditViewSidePanel(panels: PanelComponent[]): void;
addEditViewSidePanel(panels: DescriptionReducer<PanelComponent> | PanelComponent[]) {
Expand Down Expand Up @@ -208,10 +229,12 @@ class ContentManagerPlugin {
addDocumentAction: this.addDocumentAction.bind(this),
addDocumentHeaderAction: this.addDocumentHeaderAction.bind(this),
addEditViewSidePanel: this.addEditViewSidePanel.bind(this),
addRichTextBlocks: this.addRichTextBlocks.bind(this),
getBulkActions: () => this.bulkActions,
getDocumentActions: () => this.documentActions,
getEditViewSidePanels: () => this.editViewSidePanels,
getHeaderActions: () => this.headerActions,
getRichTextBlocks: () => this.richTextBlocksStore,
},
} satisfies PluginConfig;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -304,7 +304,7 @@ const baseRenderLeaf = (props: RenderLeafProps, modifiers: ModifiersStore) => {

type BaseRenderElementProps = Direction & {
props: RenderElementProps['children'];
blocks: BlocksStore;
blocks: Partial<BlocksStore>;
editor: Editor;
};

Expand All @@ -317,8 +317,13 @@ const baseRenderElement = ({
}: BaseRenderElementProps) => {
const { element } = props;

const blockMatch = Object.values(blocks).find((block) => block.matchNode(element));
const blockMatch = Object.values(blocks).find((block) => block?.matchNode(element));
const block = blockMatch || blocks.paragraph;

if (!block) {
return <></>;
}

const nodePath = ReactEditor.findPath(editor, element);

// Link is inline block so it cannot be dragged
Expand Down Expand Up @@ -428,7 +433,7 @@ const BlocksContent = ({ placeholder, ariaLabelId }: BlocksContentProps) => {

// Check if the text node starts with a known snippet
const blockMatchingSnippet = Object.values(blocks).find((block) => {
return block.snippets?.includes(textNode.text);
return block?.snippets?.includes(textNode.text);
});

if (blockMatchingSnippet?.handleConvert) {
Expand All @@ -452,7 +457,7 @@ const BlocksContent = ({ placeholder, ariaLabelId }: BlocksContentProps) => {
}

const selectedNode = editor.children[editor.selection.anchor.path[0]];
const selectedBlock = Object.values(blocks).find((block) => block.matchNode(selectedNode));
const selectedBlock = Object.values(blocks).find((block) => block?.matchNode(selectedNode));
if (!selectedBlock) {
return;
}
Expand All @@ -467,7 +472,7 @@ const BlocksContent = ({ placeholder, ariaLabelId }: BlocksContentProps) => {
if (selectedBlock.handleEnterKey) {
selectedBlock.handleEnterKey(editor);
} else {
blocks.paragraph.handleEnterKey!(editor);
blocks.paragraph?.handleEnterKey!(editor);
}
};

Expand All @@ -477,7 +482,7 @@ const BlocksContent = ({ placeholder, ariaLabelId }: BlocksContentProps) => {
}

const selectedNode = editor.children[editor.selection.anchor.path[0]];
const selectedBlock = Object.values(blocks).find((block) => block.matchNode(selectedNode));
const selectedBlock = Object.values(blocks).find((block) => block?.matchNode(selectedNode));

if (!selectedBlock) {
return;
Expand All @@ -494,7 +499,7 @@ const BlocksContent = ({ placeholder, ariaLabelId }: BlocksContentProps) => {
}

const selectedNode = editor.children[editor.selection.anchor.path[0]];
const selectedBlock = Object.values(blocks).find((block) => block.matchNode(selectedNode));
const selectedBlock = Object.values(blocks).find((block) => block?.matchNode(selectedNode));
if (!selectedBlock) {
return;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import * as React from 'react';

import { createContext, type FieldValue } from '@strapi/admin/strapi-admin';
import { createContext, useStrapiApp, type FieldValue } from '@strapi/admin/strapi-admin';
import { IconButton, Divider, VisuallyHidden } from '@strapi/design-system';
import { Expand } from '@strapi/icons';
import { MessageDescriptor, useIntl } from 'react-intl';
Expand All @@ -9,15 +9,9 @@ import { withHistory } from 'slate-history';
import { type RenderElementProps, Slate, withReact, ReactEditor, useSlate } from 'slate-react';
import { styled, type CSSProperties } from 'styled-components';

import { ContentManagerPlugin } from '../../../../../content-manager';
import { getTranslation } from '../../../../../utils/translations';

import { codeBlocks } from './Blocks/Code';
import { headingBlocks } from './Blocks/Heading';
import { imageBlocks } from './Blocks/Image';
import { linkBlocks } from './Blocks/Link';
import { listBlocks } from './Blocks/List';
import { paragraphBlocks } from './Blocks/Paragraph';
import { quoteBlocks } from './Blocks/Quote';
import { BlocksContent, type BlocksContentProps } from './BlocksContent';
import { BlocksToolbar } from './BlocksToolbar';
import { EditorLayout } from './EditorLayout';
Expand All @@ -32,9 +26,15 @@ import type { Schema } from '@strapi/types';
* BlocksEditorProvider
* -----------------------------------------------------------------------------------------------*/

interface CustomNode extends Omit<Schema.Attribute.BlocksNode, 'type'> {
type: Schema.Attribute.BlocksNode['type'] | string;
level?: number;
format?: string;
}

interface BaseBlock {
renderElement: (props: RenderElementProps) => React.JSX.Element;
matchNode: (node: Schema.Attribute.BlocksNode) => boolean;
matchNode: (node: Schema.Attribute.BlocksNode | CustomNode) => boolean;
handleConvert?: (editor: Editor) => void | (() => React.JSX.Element);
handleEnterKey?: (editor: Editor) => void;
handleBackspaceKey?: (editor: Editor, event: React.KeyboardEvent<HTMLElement>) => void;
Expand All @@ -44,12 +44,12 @@ interface BaseBlock {
}

interface NonSelectorBlock extends BaseBlock {
isInBlocksSelector: false;
isInBlocksSelector?: false;
}

interface SelectorBlock extends BaseBlock {
isInBlocksSelector: true;
icon: React.ComponentType;
icon?: React.ComponentType;
label: MessageDescriptor;
}

Expand Down Expand Up @@ -82,8 +82,11 @@ type BlocksStore = {
[K in NonSelectorBlockKey]: NonSelectorBlock;
};

type RichTextBlocksStore = Partial<BlocksStore> & {
[key: string]: SelectorBlock | NonSelectorBlock;
};

interface BlocksEditorContextValue {
blocks: BlocksStore;
modifiers: ModifiersStore;
disabled: boolean;
name: string;
Expand All @@ -94,14 +97,23 @@ interface BlocksEditorContextValue {
const [BlocksEditorProvider, usePartialBlocksEditorContext] =
createContext<BlocksEditorContextValue>('BlocksEditor');

function useBlocksEditorContext(
consumerName: string
): BlocksEditorContextValue & { editor: Editor } {
function useBlocksEditorContext(consumerName: string): BlocksEditorContextValue & {
editor: Editor;
blocks: RichTextBlocksStore;
} {
const context = usePartialBlocksEditorContext(consumerName, (state) => state);
const apis = useStrapiApp(
'useBlocksEditorContext',
(state) => state.plugins['content-manager'].apis as ContentManagerPlugin['config']['apis']
);

const blocks = apis.getRichTextBlocks();

const editor = useSlate();

return {
...context,
blocks,
editor,
};
}
Expand Down Expand Up @@ -209,16 +221,6 @@ const BlocksEditor = React.forwardRef<{ focus: () => void }, BlocksEditorProps>(
}
};

const blocks: BlocksStore = {
...paragraphBlocks,
...headingBlocks,
...listBlocks,
...linkBlocks,
...imageBlocks,
...quoteBlocks,
...codeBlocks,
};

return (
<>
<VisuallyHidden id={ariaDescriptionId}>
Expand All @@ -235,7 +237,6 @@ const BlocksEditor = React.forwardRef<{ focus: () => void }, BlocksEditorProps>(
key={key}
>
<BlocksEditorProvider
blocks={blocks}
modifiers={modifiers}
disabled={disabled}
name={name}
Expand Down Expand Up @@ -272,6 +273,7 @@ const BlocksEditor = React.forwardRef<{ focus: () => void }, BlocksEditorProps>(

export {
type BlocksStore,
type RichTextBlocksStore,
type SelectorBlockKey,
BlocksEditor,
BlocksEditorProvider,
Expand Down
Loading