Skip to content

Commit

Permalink
Merge pull request #107 from prezly/feature/html-blocks
Browse files Browse the repository at this point in the history
[MT-4552] Feature - HTML blocks support
  • Loading branch information
e1himself authored Feb 16, 2022
2 parents ec5e295 + 653672a commit 939ebf7
Show file tree
Hide file tree
Showing 14 changed files with 134 additions and 0 deletions.
32 changes: 32 additions & 0 deletions packages/slate-editor/src/modules/editor-v4-html/HtmlExtension.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import type { Extension } from '@prezly/slate-commons';
import { createDeserializeElement } from '@prezly/slate-commons';
import { HTML_NODE_TYPE, isHtmlNode } from '@prezly/slate-types';
import React from 'react';
import type { RenderElementProps } from 'slate-react';

import { HtmlElement } from './components';
import { HTML_EXTENSION_ID } from './constants';
import { normalizeRedundantHtmlBlockAttributes, parseSerializedElement } from './lib';

export const HtmlExtension = (): Extension => ({
deserialize: {
element: {
[HTML_NODE_TYPE]: createDeserializeElement(parseSerializedElement),
},
},
id: HTML_EXTENSION_ID,
normalizers: [normalizeRedundantHtmlBlockAttributes],
renderElement: ({ attributes, children, element }: RenderElementProps) => {
if (isHtmlNode(element)) {
return (
<HtmlElement attributes={attributes} element={element}>
{children}
</HtmlElement>
);
}

return undefined;
},
rootTypes: [HTML_NODE_TYPE],
voidTypes: [HTML_NODE_TYPE],
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import type { HtmlNode } from '@prezly/slate-types';
import type { PropsWithChildren } from 'react';
import React from 'react';
import type { RenderElementProps } from 'slate-react';

import { EditorBlock } from '#components';

interface Props extends RenderElementProps {
element: HtmlNode;
}

export function HtmlElement({ attributes, children, element }: PropsWithChildren<Props>) {
return (
<EditorBlock
{...attributes} // contains `ref`
element={element}
extendedHitArea
renderBlock={() => <pre>{element.content}</pre>}
void
>
{/* We have to render children or Slate will fail when trying to find the node. */}
{children}
</EditorBlock>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { HtmlElement } from './HtmlElement';
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const HTML_EXTENSION_ID = 'HtmlExtension';
3 changes: 3 additions & 0 deletions packages/slate-editor/src/modules/editor-v4-html/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export { HTML_EXTENSION_ID } from './constants';
export { HtmlExtension } from './HtmlExtension';
export { createHtmlBlock } from './lib';
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import type { HtmlNode } from '@prezly/slate-types';
import { HTML_NODE_TYPE } from '@prezly/slate-types';

export function createHtmlBlock({ content }: Pick<HtmlNode, 'content'>): HtmlNode {
return {
type: HTML_NODE_TYPE,
content,
children: [{ text: '' }],
};
}
3 changes: 3 additions & 0 deletions packages/slate-editor/src/modules/editor-v4-html/lib/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export { createHtmlBlock } from './createHtmlBlock';
export { normalizeRedundantHtmlBlockAttributes } from './normalizeRedundantHtmlBlockAttributes';
export { parseSerializedElement } from './parseSerializedElement';
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { EditorCommands } from '@prezly/slate-commons';
import { isHtmlNode } from '@prezly/slate-types';
import type { Editor, NodeEntry } from 'slate';

import { createHtmlBlock } from './createHtmlBlock';

const ALLOWED_ATTRIBUTES = Object.keys(createHtmlBlock({ content: '' }));

export function normalizeRedundantHtmlBlockAttributes(
editor: Editor,
[node, path]: NodeEntry,
): boolean {
if (!isHtmlNode(node)) {
return false;
}

return EditorCommands.normalizeRedundantAttributes(editor, [node, path], ALLOWED_ATTRIBUTES);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import type { HtmlNode } from '@prezly/slate-types';
import { isHtmlNode } from '@prezly/slate-types';

import { createHtmlBlock } from './createHtmlBlock';

export function parseSerializedElement(serialized: string): HtmlNode | undefined {
const parsed = JSON.parse(serialized);

if (isHtmlNode(parsed)) {
return createHtmlBlock(parsed);
}

return undefined;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { removeHtmlBlock } from './removeHtmlBlock';
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { EditorCommands } from '@prezly/slate-commons';
import { isHtmlNode } from '@prezly/slate-types';
import type { Editor } from 'slate';

export function removeHtmlBlock(editor: Editor): void {
EditorCommands.removeNode(editor, {
match: isHtmlNode,
});
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { EmbedExtension } from '#modules/editor-v4-embed';
import { FileAttachmentExtension } from '#modules/editor-v4-file-attachment';
import { FloatingAddMenuExtension } from '#modules/editor-v4-floating-add-menu';
import { GalleriesExtension } from '#modules/editor-v4-galleries';
import { HtmlExtension } from '#modules/editor-v4-html';
import { ImageExtension } from '#modules/editor-v4-image';
import { LoaderExtension } from '#modules/editor-v4-loader';
import { ParagraphsExtension } from '#modules/editor-v4-paragraphs';
Expand Down Expand Up @@ -149,4 +150,6 @@ export function* getEnabledExtensions({
yield LoaderExtension({ onOperationEnd, onOperationStart });

yield VoidExtension();

yield HtmlExtension();
}
13 changes: 13 additions & 0 deletions packages/slate-types/src/nodes/HtmlNode.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import type { ElementNode } from './ElementNode';
import { isElementNode } from './ElementNode';

export const HTML_NODE_TYPE = 'html';

export interface HtmlNode extends ElementNode {
type: typeof HTML_NODE_TYPE;
content: string;
}

export function isHtmlNode(value: any): value is HtmlNode {
return isElementNode<HtmlNode>(value, HTML_NODE_TYPE);
}
1 change: 1 addition & 0 deletions packages/slate-types/src/nodes/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export * from './ElementNode';
export * from './EmbedNode';
export * from './GalleryNode';
export * from './HeadingNode';
export * from './HtmlNode';
export * from './ImageNode';
export * from './LinkNode';
export * from './ListNode';
Expand Down

0 comments on commit 939ebf7

Please sign in to comment.