@plone/volto-slate 18.0.0-alpha.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (183) hide show
  1. package/.eslintrc.js +6 -0
  2. package/.release-it.json +25 -0
  3. package/CHANGELOG.md +19 -0
  4. package/LICENSE.md +21 -0
  5. package/README.md +10 -0
  6. package/build/messages/src/blocks/Table/TableBlockEdit.json +90 -0
  7. package/build/messages/src/blocks/Text/DefaultTextBlockEditor.json +6 -0
  8. package/build/messages/src/blocks/Text/DetachedTextBlockEditor.json +6 -0
  9. package/build/messages/src/blocks/Text/SlashMenu.json +6 -0
  10. package/build/messages/src/editor/plugins/AdvancedLink/index.json +10 -0
  11. package/build/messages/src/editor/plugins/Link/index.json +10 -0
  12. package/build/messages/src/editor/plugins/Table/index.json +30 -0
  13. package/build/messages/src/elementEditor/messages.json +10 -0
  14. package/build/messages/src/widgets/HtmlSlateWidget.json +6 -0
  15. package/build/messages/src/widgets/RichTextWidgetView.json +6 -0
  16. package/locales/de/LC_MESSAGES/volto.po +148 -0
  17. package/locales/en/LC_MESSAGES/volto.po +148 -0
  18. package/locales/volto.pot +182 -0
  19. package/package.json +42 -0
  20. package/src/actions/content.js +30 -0
  21. package/src/actions/index.js +3 -0
  22. package/src/actions/plugins.js +9 -0
  23. package/src/actions/selection.js +22 -0
  24. package/src/blocks/Table/Cell.jsx +87 -0
  25. package/src/blocks/Table/Cell.test.js +54 -0
  26. package/src/blocks/Table/TableBlockEdit.jsx +694 -0
  27. package/src/blocks/Table/TableBlockEdit.test.js +40 -0
  28. package/src/blocks/Table/TableBlockView.jsx +150 -0
  29. package/src/blocks/Table/TableBlockView.test.js +49 -0
  30. package/src/blocks/Table/__snapshots__/Cell.test.js.snap +3 -0
  31. package/src/blocks/Table/__snapshots__/TableBlockEdit.test.js.snap +22 -0
  32. package/src/blocks/Table/__snapshots__/TableBlockView.test.js.snap +27 -0
  33. package/src/blocks/Table/deconstruct.js +113 -0
  34. package/src/blocks/Table/extensions/normalizeTable.js +5 -0
  35. package/src/blocks/Table/index.js +60 -0
  36. package/src/blocks/Table/schema.js +122 -0
  37. package/src/blocks/Text/DefaultTextBlockEditor.jsx +304 -0
  38. package/src/blocks/Text/DetachedTextBlockEditor.jsx +77 -0
  39. package/src/blocks/Text/MarkdownIntroduction.jsx +59 -0
  40. package/src/blocks/Text/PluginSidebar.jsx +18 -0
  41. package/src/blocks/Text/ShortcutListing.jsx +28 -0
  42. package/src/blocks/Text/SlashMenu.jsx +203 -0
  43. package/src/blocks/Text/TextBlockEdit.jsx +38 -0
  44. package/src/blocks/Text/TextBlockEdit.test.js +107 -0
  45. package/src/blocks/Text/TextBlockSchema.js +54 -0
  46. package/src/blocks/Text/TextBlockView.jsx +31 -0
  47. package/src/blocks/Text/__snapshots__/TextBlockEdit.test.js.snap +62 -0
  48. package/src/blocks/Text/css/editor.css +18 -0
  49. package/src/blocks/Text/extensions/Readme.md +49 -0
  50. package/src/blocks/Text/extensions/breakList.js +100 -0
  51. package/src/blocks/Text/extensions/index.js +6 -0
  52. package/src/blocks/Text/extensions/insertBreak.js +57 -0
  53. package/src/blocks/Text/extensions/isSelected.js +7 -0
  54. package/src/blocks/Text/extensions/normalizeExternalData.js +7 -0
  55. package/src/blocks/Text/extensions/withDeserializers.js +87 -0
  56. package/src/blocks/Text/extensions/withLists.js +5 -0
  57. package/src/blocks/Text/index.js +171 -0
  58. package/src/blocks/Text/keyboard/backspaceInList.js +58 -0
  59. package/src/blocks/Text/keyboard/breakBlocks.js +3 -0
  60. package/src/blocks/Text/keyboard/cancelEsc.js +7 -0
  61. package/src/blocks/Text/keyboard/indentListItems.js +240 -0
  62. package/src/blocks/Text/keyboard/index.js +52 -0
  63. package/src/blocks/Text/keyboard/joinBlocks.js +180 -0
  64. package/src/blocks/Text/keyboard/moveListItems.js +124 -0
  65. package/src/blocks/Text/keyboard/slashMenu.js +19 -0
  66. package/src/blocks/Text/keyboard/softBreak.js +7 -0
  67. package/src/blocks/Text/keyboard/traverseBlocks.js +81 -0
  68. package/src/blocks/Text/keyboard/unwrapEmptyString.js +26 -0
  69. package/src/blocks/Text/schema.js +39 -0
  70. package/src/constants.js +123 -0
  71. package/src/editor/EditorContext.jsx +5 -0
  72. package/src/editor/EditorReference.jsx +22 -0
  73. package/src/editor/SlateEditor.jsx +375 -0
  74. package/src/editor/config.jsx +344 -0
  75. package/src/editor/decorate.js +68 -0
  76. package/src/editor/deserialize.js +185 -0
  77. package/src/editor/extensions/index.js +6 -0
  78. package/src/editor/extensions/insertBreak.js +15 -0
  79. package/src/editor/extensions/insertData.js +161 -0
  80. package/src/editor/extensions/isInline.js +14 -0
  81. package/src/editor/extensions/normalizeExternalData.js +8 -0
  82. package/src/editor/extensions/normalizeNode.js +48 -0
  83. package/src/editor/extensions/withDeserializers.js +15 -0
  84. package/src/editor/extensions/withTestingFeatures.jsx +84 -0
  85. package/src/editor/index.js +14 -0
  86. package/src/editor/less/editor.less +173 -0
  87. package/src/editor/less/globals.less +18 -0
  88. package/src/editor/less/slate.less +28 -0
  89. package/src/editor/plugins/AdvancedLink/deserialize.js +90 -0
  90. package/src/editor/plugins/AdvancedLink/extensions.js +32 -0
  91. package/src/editor/plugins/AdvancedLink/index.js +50 -0
  92. package/src/editor/plugins/AdvancedLink/render.jsx +37 -0
  93. package/src/editor/plugins/AdvancedLink/schema.js +114 -0
  94. package/src/editor/plugins/AdvancedLink/styles.less +8 -0
  95. package/src/editor/plugins/Blockquote/index.js +30 -0
  96. package/src/editor/plugins/Callout/index.js +34 -0
  97. package/src/editor/plugins/Image/deconstruct.js +30 -0
  98. package/src/editor/plugins/Image/extensions.js +51 -0
  99. package/src/editor/plugins/Image/index.js +11 -0
  100. package/src/editor/plugins/Image/render.jsx +22 -0
  101. package/src/editor/plugins/Link/extensions.js +58 -0
  102. package/src/editor/plugins/Link/index.js +159 -0
  103. package/src/editor/plugins/Link/render.jsx +54 -0
  104. package/src/editor/plugins/Markdown/constants.js +81 -0
  105. package/src/editor/plugins/Markdown/extensions.js +336 -0
  106. package/src/editor/plugins/Markdown/index.js +28 -0
  107. package/src/editor/plugins/Markdown/utils.js +198 -0
  108. package/src/editor/plugins/StyleMenu/StyleMenu.jsx +153 -0
  109. package/src/editor/plugins/StyleMenu/index.js +19 -0
  110. package/src/editor/plugins/StyleMenu/style.less +29 -0
  111. package/src/editor/plugins/StyleMenu/utils.js +168 -0
  112. package/src/editor/plugins/Table/TableButton.jsx +142 -0
  113. package/src/editor/plugins/Table/TableCell.jsx +44 -0
  114. package/src/editor/plugins/Table/TableContainer.jsx +37 -0
  115. package/src/editor/plugins/Table/TableSizePicker.jsx +83 -0
  116. package/src/editor/plugins/Table/extensions.js +87 -0
  117. package/src/editor/plugins/Table/index.js +390 -0
  118. package/src/editor/plugins/Table/less/public.less +29 -0
  119. package/src/editor/plugins/Table/less/table.less +28 -0
  120. package/src/editor/plugins/Table/render.jsx +30 -0
  121. package/src/editor/plugins/index.js +19 -0
  122. package/src/editor/render.jsx +224 -0
  123. package/src/editor/ui/BasicToolbar.jsx +11 -0
  124. package/src/editor/ui/BlockButton.jsx +31 -0
  125. package/src/editor/ui/ClearFormattingButton.jsx +21 -0
  126. package/src/editor/ui/ExpandedToolbar.jsx +18 -0
  127. package/src/editor/ui/Expando.jsx +5 -0
  128. package/src/editor/ui/InlineToolbar.jsx +69 -0
  129. package/src/editor/ui/MarkButton.jsx +23 -0
  130. package/src/editor/ui/MarkElementButton.jsx +30 -0
  131. package/src/editor/ui/Menu.jsx +13 -0
  132. package/src/editor/ui/PositionedToolbar.jsx +32 -0
  133. package/src/editor/ui/Separator.jsx +7 -0
  134. package/src/editor/ui/SlateContextToolbar.jsx +13 -0
  135. package/src/editor/ui/SlateToolbar.jsx +96 -0
  136. package/src/editor/ui/Toolbar.jsx +103 -0
  137. package/src/editor/ui/ToolbarButton.jsx +33 -0
  138. package/src/editor/ui/ToolbarButton.test.js +25 -0
  139. package/src/editor/ui/__snapshots__/ToolbarButton.test.js.snap +16 -0
  140. package/src/editor/ui/index.js +15 -0
  141. package/src/editor/utils.js +248 -0
  142. package/src/elementEditor/ContextButtons.jsx +57 -0
  143. package/src/elementEditor/PluginEditor.jsx +124 -0
  144. package/src/elementEditor/Readme.md +6 -0
  145. package/src/elementEditor/SchemaProvider.jsx +4 -0
  146. package/src/elementEditor/SidebarEditor.jsx +46 -0
  147. package/src/elementEditor/ToolbarButton.jsx +44 -0
  148. package/src/elementEditor/index.js +5 -0
  149. package/src/elementEditor/makeInlineElementPlugin.js +100 -0
  150. package/src/elementEditor/messages.js +14 -0
  151. package/src/elementEditor/utils.js +227 -0
  152. package/src/hooks/index.js +3 -0
  153. package/src/hooks/useEditorContext.js +6 -0
  154. package/src/hooks/useIsomorphicLayoutEffect.js +7 -0
  155. package/src/hooks/useSelectionPosition.js +25 -0
  156. package/src/i18n.js +180 -0
  157. package/src/icons/hashlink.svg +57 -0
  158. package/src/index.js +61 -0
  159. package/src/reducers/content.js +74 -0
  160. package/src/reducers/index.js +3 -0
  161. package/src/reducers/plugins.js +17 -0
  162. package/src/reducers/selection.js +16 -0
  163. package/src/utils/blocks.js +379 -0
  164. package/src/utils/blocks.test.js +138 -0
  165. package/src/utils/editor.js +31 -0
  166. package/src/utils/image.js +25 -0
  167. package/src/utils/index.js +11 -0
  168. package/src/utils/internals.js +46 -0
  169. package/src/utils/lists.js +92 -0
  170. package/src/utils/marks.js +104 -0
  171. package/src/utils/mime-types.js +24 -0
  172. package/src/utils/nodes.js +4 -0
  173. package/src/utils/ops.js +20 -0
  174. package/src/utils/random.js +17 -0
  175. package/src/utils/selection.js +236 -0
  176. package/src/utils/slate-string-utils.js +409 -0
  177. package/src/utils/volto-blocks.js +314 -0
  178. package/src/widgets/ErrorBoundary.jsx +27 -0
  179. package/src/widgets/HtmlSlateWidget.jsx +138 -0
  180. package/src/widgets/ObjectByTypeWidget.jsx +49 -0
  181. package/src/widgets/RichTextWidget.jsx +72 -0
  182. package/src/widgets/RichTextWidgetView.jsx +36 -0
  183. package/src/widgets/style.css +21 -0
@@ -0,0 +1,27 @@
1
+ import React from 'react';
2
+
3
+ export class ErrorBoundary extends React.Component {
4
+ constructor(props) {
5
+ super(props);
6
+ this.state = { hasError: false };
7
+ }
8
+
9
+ static getDerivedStateFromError(error) {
10
+ // Update state so the next render will show the fallback UI.
11
+ return { hasError: true };
12
+ }
13
+
14
+ componentDidCatch(error, errorInfo) {
15
+ // eslint-disable-next-line
16
+ console.error(error, errorInfo);
17
+ }
18
+
19
+ render() {
20
+ if (this.state.hasError) {
21
+ // You can render any custom fallback UI
22
+ return <pre className="slate error">{`ERROR: ${this.props.name}`}</pre>;
23
+ }
24
+
25
+ return this.props.children;
26
+ }
27
+ }
@@ -0,0 +1,138 @@
1
+ /**
2
+ * HtmlSlateWidget, a slate widget variant that saves its data as HTML
3
+ */
4
+
5
+ import React from 'react';
6
+ import ReactDOMServer from 'react-dom/server';
7
+ import configureStore from 'redux-mock-store';
8
+ import { MemoryRouter } from 'react-router-dom';
9
+ import { Provider, useSelector } from 'react-redux';
10
+ import { defineMessages, injectIntl } from 'react-intl';
11
+
12
+ import { FormFieldWrapper } from '@plone/volto/components';
13
+ import SlateEditor from '@plone/volto-slate/editor/SlateEditor';
14
+ import { serializeNodes } from '@plone/volto-slate/editor/render';
15
+ import { makeEditor } from '@plone/volto-slate/utils';
16
+ import deserialize from '@plone/volto-slate/editor/deserialize';
17
+
18
+ import {
19
+ createEmptyParagraph,
20
+ normalizeExternalData,
21
+ } from '@plone/volto-slate/utils';
22
+ import { ErrorBoundary } from './ErrorBoundary';
23
+
24
+ import './style.css';
25
+
26
+ const messages = defineMessages({
27
+ error: {
28
+ id: 'An error has occurred while editing "{name}" field. We have been notified and we are looking into it. Please save your work and retry. If the issue persists please contact the site administrator.',
29
+ defaultMessage:
30
+ 'An error has occurred while editing "{name}" field. We have been notified and we are looking into it. Please save your work and retry. If the issue persists please contact the site administrator.',
31
+ },
32
+ });
33
+
34
+ const HtmlSlateWidget = (props) => {
35
+ const {
36
+ id,
37
+ onChange,
38
+ value,
39
+ focus,
40
+ className,
41
+ block,
42
+ placeholder,
43
+ properties,
44
+ intl,
45
+ } = props;
46
+
47
+ const [selected, setSelected] = React.useState(focus);
48
+
49
+ const editor = React.useMemo(() => makeEditor(), []);
50
+
51
+ const token = useSelector((state) => state.userSession.token);
52
+
53
+ const toHtml = React.useCallback(
54
+ (value) => {
55
+ const mockStore = configureStore();
56
+ const html = ReactDOMServer.renderToStaticMarkup(
57
+ <Provider store={mockStore({ userSession: { token } })}>
58
+ <MemoryRouter>{serializeNodes(value || [])}</MemoryRouter>
59
+ </Provider>,
60
+ );
61
+ // console.log('toHtml value', JSON.stringify(value));
62
+ // console.log('toHtml html', html);
63
+
64
+ return {
65
+ 'content-type': value ? value['content-type'] : 'text/html',
66
+ encoding: value ? value.encoding : 'utf8',
67
+ data: html,
68
+ };
69
+ },
70
+ [token],
71
+ );
72
+
73
+ const fromHtml = React.useCallback(
74
+ (value) => {
75
+ const html = value?.data || '';
76
+
77
+ const parsed = new DOMParser().parseFromString(html, 'text/html');
78
+ const body =
79
+ parsed.getElementsByTagName('google-sheets-html-origin').length > 0
80
+ ? parsed.querySelector('google-sheets-html-origin > table')
81
+ : parsed.body;
82
+ let data = deserialize(editor, body, { collapseWhitespace: false });
83
+ data = normalizeExternalData(editor, data);
84
+
85
+ // editor.children = data;
86
+ // Editor.normalize(editor);
87
+ // TODO: need to add {text: ""} placeholders between elements
88
+ const res = data.length ? data : [createEmptyParagraph()];
89
+ // console.log('from html', { html: value?.data, res });
90
+ return res;
91
+ },
92
+ [editor],
93
+ );
94
+
95
+ const valueFromHtml = React.useMemo(() => {
96
+ return fromHtml(value);
97
+ }, [value, fromHtml]);
98
+
99
+ const handleChange = React.useCallback(
100
+ (newValue) => {
101
+ onChange(id, toHtml(newValue));
102
+ },
103
+ [onChange, toHtml, id],
104
+ );
105
+
106
+ const handleClick = React.useCallback(() => {
107
+ setSelected(true);
108
+ }, []);
109
+
110
+ return (
111
+ <FormFieldWrapper {...props} draggable={false} className="slate_wysiwyg">
112
+ <div
113
+ className="slate_wysiwyg_box"
114
+ role="textbox"
115
+ tabIndex="-1"
116
+ style={{ boxSizing: 'initial' }}
117
+ onClick={handleClick}
118
+ onKeyDown={() => {}}
119
+ >
120
+ <ErrorBoundary name={intl.formatMessage(messages.error, { name: id })}>
121
+ <SlateEditor
122
+ className={className}
123
+ id={id}
124
+ name={id}
125
+ value={valueFromHtml}
126
+ onChange={handleChange}
127
+ block={block}
128
+ selected={selected}
129
+ properties={properties}
130
+ placeholder={placeholder}
131
+ />
132
+ </ErrorBoundary>
133
+ </div>
134
+ </FormFieldWrapper>
135
+ );
136
+ };
137
+
138
+ export default injectIntl(HtmlSlateWidget);
@@ -0,0 +1,49 @@
1
+ import React from 'react';
2
+ import { Menu, Tab } from 'semantic-ui-react';
3
+ import { Icon, ObjectWidget } from '@plone/volto/components';
4
+
5
+ export const ObjectByTypeWidget = (props) => {
6
+ const { schemas, value = {}, onChange, errors = {}, id } = props;
7
+ const objectId = id;
8
+
9
+ const schemaIds = schemas.map(({ id }) => id);
10
+ const defaultActiveTab = value
11
+ ? schemaIds.indexOf(Object.keys(value)[0])
12
+ : null;
13
+
14
+ const [activeTab, setActiveTab] = React.useState(
15
+ defaultActiveTab > -1 ? defaultActiveTab : 0,
16
+ );
17
+ const createTab = ({ schema, id, icon }, index) => {
18
+ return {
19
+ menuItem: () => (
20
+ <Menu.Item
21
+ onClick={() => setActiveTab(index)}
22
+ active={activeTab === index}
23
+ key={id}
24
+ >
25
+ <Icon size="24px" name={icon} title={schema.title} />
26
+ </Menu.Item>
27
+ ),
28
+ render: () => {
29
+ return (
30
+ <Tab.Pane>
31
+ <ObjectWidget
32
+ schema={schema}
33
+ id={id}
34
+ errors={errors}
35
+ value={value[id] || {}}
36
+ onChange={(schemaId, v) => {
37
+ onChange(objectId, { [schemaId]: v });
38
+ }}
39
+ />
40
+ </Tab.Pane>
41
+ );
42
+ },
43
+ };
44
+ };
45
+
46
+ return <Tab panes={schemas.map(createTab)} activeIndex={activeTab} />;
47
+ };
48
+
49
+ export default ObjectByTypeWidget;
@@ -0,0 +1,72 @@
1
+ /**
2
+ * A Slate widget that uses internal JSON representation as its value
3
+ *
4
+ */
5
+
6
+ import React from 'react';
7
+ import isUndefined from 'lodash/isUndefined';
8
+ import isString from 'lodash/isString';
9
+ import { FormFieldWrapper } from '@plone/volto/components';
10
+ import SlateEditor from '@plone/volto-slate/editor/SlateEditor';
11
+
12
+ import { createEmptyParagraph, createParagraph } from '../utils/blocks';
13
+
14
+ import './style.css';
15
+
16
+ const getValue = (value) => {
17
+ if (isUndefined(value) || !isUndefined(value?.data)) {
18
+ return [createEmptyParagraph()];
19
+ }
20
+ // Previously this was a text field
21
+ if (isString(value)) {
22
+ return [createParagraph(value)];
23
+ }
24
+ return value;
25
+ };
26
+
27
+ const SlateRichTextWidget = (props) => {
28
+ const {
29
+ id,
30
+ onChange,
31
+ value,
32
+ focus,
33
+ className,
34
+ block,
35
+ placeholder,
36
+ properties,
37
+ readOnly = false,
38
+ } = props;
39
+ const [selected, setSelected] = React.useState(focus);
40
+
41
+ return (
42
+ <FormFieldWrapper {...props} draggable={false} className="slate_wysiwyg">
43
+ <div
44
+ className="slate_wysiwyg_box"
45
+ role="textbox"
46
+ tabIndex="-1"
47
+ style={{ boxSizing: 'initial' }}
48
+ onClick={() => {
49
+ setSelected(true);
50
+ }}
51
+ onKeyDown={() => {}}
52
+ >
53
+ <SlateEditor
54
+ className={className}
55
+ readOnly={readOnly}
56
+ id={id}
57
+ name={id}
58
+ value={getValue(value)}
59
+ onChange={(newValue) => {
60
+ onChange(id, newValue);
61
+ }}
62
+ block={block}
63
+ selected={selected}
64
+ properties={properties}
65
+ placeholder={placeholder}
66
+ />
67
+ </div>
68
+ </FormFieldWrapper>
69
+ );
70
+ };
71
+
72
+ export default SlateRichTextWidget;
@@ -0,0 +1,36 @@
1
+ import React from 'react';
2
+ import cx from 'classnames';
3
+ import config from '@plone/volto/registry';
4
+ import { ErrorBoundary } from './ErrorBoundary';
5
+ import { defineMessages, injectIntl } from 'react-intl';
6
+ import './style.css';
7
+
8
+ const messages = defineMessages({
9
+ error: {
10
+ id: 'An error has occurred while rendering "{name}" field. We have been notified and we are looking into it. If the issue persists please contact the site administrator.',
11
+ defaultMessage:
12
+ 'An error has occurred while rendering "{name}" field. We have been notified and we are looking into it. If the issue persists please contact the site administrator.',
13
+ },
14
+ });
15
+
16
+ export const SlateRichTextWidgetView = ({
17
+ value,
18
+ children,
19
+ className,
20
+ intl,
21
+ }) => {
22
+ const Block = config.blocks.blocksConfig.slate.view;
23
+ return value ? (
24
+ <ErrorBoundary
25
+ name={intl.formatMessage(messages.error, { name: className })}
26
+ >
27
+ <div className={cx(className, 'slate', 'widget')}>
28
+ <Block data={{ value: value }}>{children}</Block>
29
+ </div>
30
+ </ErrorBoundary>
31
+ ) : (
32
+ ''
33
+ );
34
+ };
35
+
36
+ export default injectIntl(SlateRichTextWidgetView);
@@ -0,0 +1,21 @@
1
+ .slate_wysiwyg_box {
2
+ padding: 2px 3px;
3
+ border-bottom: 1px solid #c7d5d8;
4
+ }
5
+
6
+ .metadata.mention.slate.widget {
7
+ display: inline;
8
+ }
9
+
10
+ .metadata.mention.slate.widget > :first-child {
11
+ display: inline;
12
+ }
13
+
14
+ .metadata.mention.slate.widget > :last-child {
15
+ display: inline;
16
+ }
17
+
18
+ .slate.error {
19
+ color: #f00;
20
+ white-space: normal;
21
+ }