Skip to content

Commit 63b5a64

Browse files
committed
feat(widget-richtext): add shortcode plugin for editor components
1 parent db48a9a commit 63b5a64

File tree

12 files changed

+166
-104
lines changed

12 files changed

+166
-104
lines changed

dev-test/index.html

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,10 @@
125125
h('p', {},
126126
h('small', {}, "Written " + entry.getIn(['data', 'date']))
127127
),
128-
h('div', {"className": "text"}, this.props.widgetFor('body'))
128+
h('h2', {}, "Richtext Widget"),
129+
h('div', {"className": "text"}, this.props.widgetFor('body')),
130+
h('h2', {}, "Markdown Widget"),
131+
h('div', {"className": "text"}, this.props.widgetFor('bodyold'))
129132
);
130133
}
131134
});

package-lock.json

Lines changed: 57 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/decap-cms-core/src/components/Editor/EditorPreviewPane/EditorPreviewContent.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ class PreviewContent extends React.Component {
6767

6868
PreviewContent.propTypes = {
6969
previewComponent: PropTypes.func.isRequired,
70+
getEditorComponents: PropTypes.func,
7071
previewProps: PropTypes.object,
7172
onFieldClick: PropTypes.func,
7273
};

packages/decap-cms-core/src/components/Editor/EditorPreviewPane/EditorPreviewPane.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import {
1313
getPreviewTemplate,
1414
getPreviewStyles,
1515
getRemarkPlugins,
16+
getEditorComponents,
1617
} from '../../../lib/registry';
1718
import { getAllEntries, tryLoadEntry } from '../../../actions/entries';
1819
import { ErrorBoundary } from '../../UI';
@@ -57,6 +58,7 @@ export class PreviewPane extends React.Component {
5758
fieldsMetaData={metadata}
5859
resolveWidget={resolveWidget}
5960
getRemarkPlugins={getRemarkPlugins}
61+
getEditorComponents={getEditorComponents}
6062
/>
6163
);
6264
};
@@ -261,6 +263,7 @@ export class PreviewPane extends React.Component {
261263
this.widgetFor(name, fields, values, fieldsMetaData),
262264
widgetsFor: this.widgetsFor,
263265
getCollection: this.getCollection,
266+
getEditorComponents,
264267
};
265268

266269
const styleEls = getPreviewStyles().map((style, i) => {

packages/decap-cms-widget-markdown/src/MarkdownPreview.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ class MarkdownPreview extends React.Component {
2121
if (value === null) {
2222
return null;
2323
}
24-
24+
2525
const html = markdownToHtml(value, { getAsset, resolveWidget }, getRemarkPlugins?.());
2626
const toRender = field?.get('sanitize_preview', false) ? DOMPurify.sanitize(html) : html;
2727

packages/decap-cms-widget-richtext/package.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,14 @@
2828
"class-variance-authority": "^0.7.0",
2929
"lucide-react": "^0.331.0",
3030
"platejs": "^49.2.21",
31+
"rehype-parse": "^6.0.0",
32+
"rehype-remark": "^8.0.0",
33+
"rehype-stringify": "^7.0.0",
3134
"remark-parse": "^6.0.3",
3235
"remark-rehype": "^4.0.0",
36+
"remark-slate": "^1.8.6",
37+
"remark-slate-transformer": "^0.7.4",
38+
"remark-stringify": "^6.0.4",
3339
"unified": "^9.0.0"
3440
},
3541
"peerDependencies": {

packages/decap-cms-widget-richtext/src/RichtextControl.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import ImmutablePropTypes from 'react-immutable-proptypes';
55
import VisualEditor from './RichtextControl/VisualEditor';
66
import { EditorProvider } from './RichtextControl/editorContext';
77

8-
export default class MarkdownControl extends React.Component {
8+
export default class RichtextControl extends React.Component {
99
static propTypes = {
1010
onChange: PropTypes.func.isRequired,
1111
onAddAsset: PropTypes.func.isRequired,

packages/decap-cms-widget-richtext/src/RichtextControl/VisualEditor.js

Lines changed: 9 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,7 @@
11
import React from 'react';
2-
import { KEYS, TrailingBlockPlugin } from 'platejs';
3-
import {
4-
usePlateEditor,
5-
Plate,
6-
ParagraphPlugin,
7-
PlateLeaf,
8-
} from 'platejs/react';
9-
import {
10-
BoldPlugin,
11-
ItalicPlugin,
12-
CodePlugin,
13-
HeadingPlugin,
14-
} from '@platejs/basic-nodes/react';
2+
import { KEYS } from 'platejs';
3+
import { usePlateEditor, Plate, ParagraphPlugin, PlateLeaf } from 'platejs/react';
4+
import { BoldPlugin, ItalicPlugin, CodePlugin, HeadingPlugin } from '@platejs/basic-nodes/react';
155
import { ListPlugin } from '@platejs/list-classic/react';
166
import { LinkPlugin } from '@platejs/link/react';
177
import { ClassNames } from '@emotion/react';
@@ -29,8 +19,8 @@ import HeadingElement from './components/Element/HeadingElement';
2919
import ListElement from './components/Element/ListElement';
3020
import BlockquoteElement from './components/Element/BlockquoteElement';
3121
import LinkElement from './components/Element/LinkElement';
32-
// import ShortcodePlugin from './plugins/ShortcodePlugin';
3322
import ExtendedBlockquotePlugin from './plugins/ExtendedBlockquotePlugin';
23+
import ShortcodePlugin from './plugins/ShortcodePlugin';
3424
// import ShortcodeElement from './components/Element/ShortcodeElement';
3525

3626
function visualEditorStyles({ minimal }) {
@@ -86,22 +76,6 @@ const emptyValue = [
8676
type: ParagraphPlugin.key,
8777
children: [{ text: '' }],
8878
},
89-
// {
90-
// children: [{ text: 'Title' }],
91-
// type: 'h3',
92-
// },
93-
// {
94-
// children: [{ text: 'This is a quote.' }],
95-
// type: 'blockquote',
96-
// },
97-
// {
98-
// children: [
99-
// { text: 'With some ' },
100-
// { bold: true, text: 'bold' },
101-
// { text: ' text for emphasis!' },
102-
// ],
103-
// type: 'p',
104-
// },
10579
];
10680

10781
export default function VisualEditor(props) {
@@ -135,7 +109,9 @@ export default function VisualEditor(props) {
135109
onChange(mdValue);
136110
}
137111

138-
const initialValue = props.value ? markdownToSlate(props.value, {}) : emptyValue;
112+
const initialValue = props.value
113+
? markdownToSlate(props.value, { editorComponents })
114+
: emptyValue;
139115

140116
const editor = usePlateEditor({
141117
override: {
@@ -166,12 +142,9 @@ export default function VisualEditor(props) {
166142
node: { component: LinkElement },
167143
}),
168144
ExtendedBlockquotePlugin.configure({
169-
node: { component: BlockquoteElement }
170-
}),
171-
// ShortcodePlugin,
172-
TrailingBlockPlugin.configure({
173-
options: { type: ParagraphPlugin.key },
145+
node: { component: BlockquoteElement },
174146
}),
147+
ShortcodePlugin,
175148
],
176149
value: initialValue,
177150
});

packages/decap-cms-widget-richtext/src/RichtextControl/components/Element/ShortcodeElement.js

Lines changed: 47 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,13 @@ import { omit } from 'lodash';
55
import { css } from '@emotion/react';
66
import { Range, setNodes } from 'slate';
77
import { zIndex } from 'decap-cms-ui-default';
8-
import { ParagraphPlugin, PlateElement, useEditorRef } from 'platejs/react';
8+
import {
9+
ParagraphPlugin,
10+
PlateElement,
11+
useEditorRef,
12+
useEditorSelection,
13+
useEditorState,
14+
} from 'platejs/react';
915

1016
import { useEditorContext } from '../../editorContext';
1117

@@ -28,21 +34,22 @@ function InsertionPoint(props) {
2834

2935
function ShortcodeElement(props) {
3036
const editor = useEditorRef();
31-
const { element, dataKey = 'shortcodeData', children } = props;
37+
const editorState = useEditorState();
38+
const { attributes, element, dataKey = 'shortcodeData', children } = props;
3239
const { editorControl: EditorControl, editorComponents } = useEditorContext();
33-
const plugin = editorComponents.get(element.id);
40+
const plugin = editorComponents.get(element.data.shortcode);
3441
const fieldKeys = ['id', 'fromBlock', 'toBlock', 'toPreview', 'pattern', 'icon'];
3542

3643
const field = fromJS(omit(plugin, fieldKeys));
3744
const [value, setValue] = useState(fromJS(element?.data[dataKey] ?? { id: '' }));
3845

46+
const selection = useEditorSelection();
3947
const path = editor.api.findPath(element);
4048
const isSelected =
41-
editor.selection &&
42-
path &&
43-
Range.isRange(editor.selection) &&
44-
Range.includes(editor.selection, path);
49+
selection && path && Range.isRange(selection) && Range.includes(selection, path);
4550
const insertBefore = path[0] === 0;
51+
const insertAfter =
52+
path[0] === editorState.children.length - 1 || editor.isVoid(editorState.children[path[0] + 1]);
4653

4754
function handleChange(fieldName, value, metadata) {
4855
const newProperties = {
@@ -65,32 +72,41 @@ function ShortcodeElement(props) {
6572
);
6673
}
6774

75+
function handleInsertAfter(e) {
76+
e.preventDefault();
77+
e.stopPropagation();
78+
79+
editor.tf.insertNodes(
80+
{ type: ParagraphPlugin.key, children: [{ text: '' }] },
81+
{ select: true },
82+
);
83+
}
84+
6885
return (
69-
<>
70-
<PlateElement asChild {...props} contentEditable={false}>
71-
<StyledDiv>
72-
{insertBefore && <InsertionPoint onClick={handleInsertBefore} />}
73-
<EditorControl
74-
css={css`
75-
margin-top: 0;
76-
margin-bottom: 16px;
86+
<PlateElement asChild {...props}>
87+
{insertBefore && <InsertionPoint onClick={handleInsertBefore} />}
88+
<StyledDiv {...attributes} contentEditable={false}>
89+
<EditorControl
90+
css={css`
91+
margin-top: 0;
92+
margin-bottom: 16px;
7793
78-
&:first-of-type {
79-
margin-top: 0;
80-
}
81-
`}
82-
value={value}
83-
field={field}
84-
onChange={handleChange}
85-
isEditorComponent={true}
86-
onValidateObject={() => {}}
87-
isNewEditorComponent={element.data?.shortcodeNew}
88-
isSelected={isSelected}
89-
/>
90-
{children}
91-
</StyledDiv>
92-
</PlateElement>
93-
</>
94+
&:first-of-type {
95+
margin-top: 0;
96+
}
97+
`}
98+
value={value}
99+
field={field}
100+
onChange={handleChange}
101+
isEditorComponent={true}
102+
onValidateObject={() => {}}
103+
isNewEditorComponent={element.data?.shortcodeNew}
104+
isSelected={isSelected}
105+
/>
106+
{children}
107+
</StyledDiv>
108+
{insertAfter && <InsertionPoint onClick={handleInsertAfter} />}
109+
</PlateElement>
94110
);
95111
}
96112

packages/decap-cms-widget-richtext/src/RichtextControl/components/Toolbar/EditorComponentsToolbarButton.js

Lines changed: 15 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -24,30 +24,22 @@ function EditorComponentsToolbarButton({ disabled, editorComponents, allowedEdit
2424
.filter(field => field.has('default'))
2525
.map(field => field.get('default'));
2626

27-
if (editor.api.isAt({ end: true }) && editor.api.isAt({ start: true })) {
28-
// setBlockAboveNode(editor, { // find alternative for removed setBlockAboveNode
29-
// children: [{ text: '' }],
30-
// type: 'shortcode',
31-
// id: plugin.id,
32-
// data: {
33-
// shortcode: plugin.id,
34-
// shortcodeNew: true,
35-
// shortcodeData: defaultValues.toJS(),
36-
// },
37-
// })
38-
return;
39-
}
40-
41-
editor.tf.insertNodes({
42-
children: [{ text: '' }],
43-
type: 'shortcode',
44-
id: plugin.id,
45-
data: {
46-
shortcode: plugin.id,
47-
shortcodeNew: true,
48-
shortcodeData: defaultValues.toJS(),
27+
editor.tf.insertNodes(
28+
{
29+
children: [{ text: '' }],
30+
type: 'shortcode',
31+
isElement: true,
32+
isVoid: true,
33+
data: {
34+
shortcode: plugin.id,
35+
shortcodeNew: true,
36+
shortcodeData: defaultValues.toJS(),
37+
},
38+
},
39+
{
40+
removeEmpty: true,
4941
},
50-
});
42+
);
5143
},
5244
[editor],
5345
);

0 commit comments

Comments
 (0)