@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.
- package/.eslintrc.js +6 -0
- package/.release-it.json +25 -0
- package/CHANGELOG.md +19 -0
- package/LICENSE.md +21 -0
- package/README.md +10 -0
- package/build/messages/src/blocks/Table/TableBlockEdit.json +90 -0
- package/build/messages/src/blocks/Text/DefaultTextBlockEditor.json +6 -0
- package/build/messages/src/blocks/Text/DetachedTextBlockEditor.json +6 -0
- package/build/messages/src/blocks/Text/SlashMenu.json +6 -0
- package/build/messages/src/editor/plugins/AdvancedLink/index.json +10 -0
- package/build/messages/src/editor/plugins/Link/index.json +10 -0
- package/build/messages/src/editor/plugins/Table/index.json +30 -0
- package/build/messages/src/elementEditor/messages.json +10 -0
- package/build/messages/src/widgets/HtmlSlateWidget.json +6 -0
- package/build/messages/src/widgets/RichTextWidgetView.json +6 -0
- package/locales/de/LC_MESSAGES/volto.po +148 -0
- package/locales/en/LC_MESSAGES/volto.po +148 -0
- package/locales/volto.pot +182 -0
- package/package.json +42 -0
- package/src/actions/content.js +30 -0
- package/src/actions/index.js +3 -0
- package/src/actions/plugins.js +9 -0
- package/src/actions/selection.js +22 -0
- package/src/blocks/Table/Cell.jsx +87 -0
- package/src/blocks/Table/Cell.test.js +54 -0
- package/src/blocks/Table/TableBlockEdit.jsx +694 -0
- package/src/blocks/Table/TableBlockEdit.test.js +40 -0
- package/src/blocks/Table/TableBlockView.jsx +150 -0
- package/src/blocks/Table/TableBlockView.test.js +49 -0
- package/src/blocks/Table/__snapshots__/Cell.test.js.snap +3 -0
- package/src/blocks/Table/__snapshots__/TableBlockEdit.test.js.snap +22 -0
- package/src/blocks/Table/__snapshots__/TableBlockView.test.js.snap +27 -0
- package/src/blocks/Table/deconstruct.js +113 -0
- package/src/blocks/Table/extensions/normalizeTable.js +5 -0
- package/src/blocks/Table/index.js +60 -0
- package/src/blocks/Table/schema.js +122 -0
- package/src/blocks/Text/DefaultTextBlockEditor.jsx +304 -0
- package/src/blocks/Text/DetachedTextBlockEditor.jsx +77 -0
- package/src/blocks/Text/MarkdownIntroduction.jsx +59 -0
- package/src/blocks/Text/PluginSidebar.jsx +18 -0
- package/src/blocks/Text/ShortcutListing.jsx +28 -0
- package/src/blocks/Text/SlashMenu.jsx +203 -0
- package/src/blocks/Text/TextBlockEdit.jsx +38 -0
- package/src/blocks/Text/TextBlockEdit.test.js +107 -0
- package/src/blocks/Text/TextBlockSchema.js +54 -0
- package/src/blocks/Text/TextBlockView.jsx +31 -0
- package/src/blocks/Text/__snapshots__/TextBlockEdit.test.js.snap +62 -0
- package/src/blocks/Text/css/editor.css +18 -0
- package/src/blocks/Text/extensions/Readme.md +49 -0
- package/src/blocks/Text/extensions/breakList.js +100 -0
- package/src/blocks/Text/extensions/index.js +6 -0
- package/src/blocks/Text/extensions/insertBreak.js +57 -0
- package/src/blocks/Text/extensions/isSelected.js +7 -0
- package/src/blocks/Text/extensions/normalizeExternalData.js +7 -0
- package/src/blocks/Text/extensions/withDeserializers.js +87 -0
- package/src/blocks/Text/extensions/withLists.js +5 -0
- package/src/blocks/Text/index.js +171 -0
- package/src/blocks/Text/keyboard/backspaceInList.js +58 -0
- package/src/blocks/Text/keyboard/breakBlocks.js +3 -0
- package/src/blocks/Text/keyboard/cancelEsc.js +7 -0
- package/src/blocks/Text/keyboard/indentListItems.js +240 -0
- package/src/blocks/Text/keyboard/index.js +52 -0
- package/src/blocks/Text/keyboard/joinBlocks.js +180 -0
- package/src/blocks/Text/keyboard/moveListItems.js +124 -0
- package/src/blocks/Text/keyboard/slashMenu.js +19 -0
- package/src/blocks/Text/keyboard/softBreak.js +7 -0
- package/src/blocks/Text/keyboard/traverseBlocks.js +81 -0
- package/src/blocks/Text/keyboard/unwrapEmptyString.js +26 -0
- package/src/blocks/Text/schema.js +39 -0
- package/src/constants.js +123 -0
- package/src/editor/EditorContext.jsx +5 -0
- package/src/editor/EditorReference.jsx +22 -0
- package/src/editor/SlateEditor.jsx +375 -0
- package/src/editor/config.jsx +344 -0
- package/src/editor/decorate.js +68 -0
- package/src/editor/deserialize.js +185 -0
- package/src/editor/extensions/index.js +6 -0
- package/src/editor/extensions/insertBreak.js +15 -0
- package/src/editor/extensions/insertData.js +161 -0
- package/src/editor/extensions/isInline.js +14 -0
- package/src/editor/extensions/normalizeExternalData.js +8 -0
- package/src/editor/extensions/normalizeNode.js +48 -0
- package/src/editor/extensions/withDeserializers.js +15 -0
- package/src/editor/extensions/withTestingFeatures.jsx +84 -0
- package/src/editor/index.js +14 -0
- package/src/editor/less/editor.less +173 -0
- package/src/editor/less/globals.less +18 -0
- package/src/editor/less/slate.less +28 -0
- package/src/editor/plugins/AdvancedLink/deserialize.js +90 -0
- package/src/editor/plugins/AdvancedLink/extensions.js +32 -0
- package/src/editor/plugins/AdvancedLink/index.js +50 -0
- package/src/editor/plugins/AdvancedLink/render.jsx +37 -0
- package/src/editor/plugins/AdvancedLink/schema.js +114 -0
- package/src/editor/plugins/AdvancedLink/styles.less +8 -0
- package/src/editor/plugins/Blockquote/index.js +30 -0
- package/src/editor/plugins/Callout/index.js +34 -0
- package/src/editor/plugins/Image/deconstruct.js +30 -0
- package/src/editor/plugins/Image/extensions.js +51 -0
- package/src/editor/plugins/Image/index.js +11 -0
- package/src/editor/plugins/Image/render.jsx +22 -0
- package/src/editor/plugins/Link/extensions.js +58 -0
- package/src/editor/plugins/Link/index.js +159 -0
- package/src/editor/plugins/Link/render.jsx +54 -0
- package/src/editor/plugins/Markdown/constants.js +81 -0
- package/src/editor/plugins/Markdown/extensions.js +336 -0
- package/src/editor/plugins/Markdown/index.js +28 -0
- package/src/editor/plugins/Markdown/utils.js +198 -0
- package/src/editor/plugins/StyleMenu/StyleMenu.jsx +153 -0
- package/src/editor/plugins/StyleMenu/index.js +19 -0
- package/src/editor/plugins/StyleMenu/style.less +29 -0
- package/src/editor/plugins/StyleMenu/utils.js +168 -0
- package/src/editor/plugins/Table/TableButton.jsx +142 -0
- package/src/editor/plugins/Table/TableCell.jsx +44 -0
- package/src/editor/plugins/Table/TableContainer.jsx +37 -0
- package/src/editor/plugins/Table/TableSizePicker.jsx +83 -0
- package/src/editor/plugins/Table/extensions.js +87 -0
- package/src/editor/plugins/Table/index.js +390 -0
- package/src/editor/plugins/Table/less/public.less +29 -0
- package/src/editor/plugins/Table/less/table.less +28 -0
- package/src/editor/plugins/Table/render.jsx +30 -0
- package/src/editor/plugins/index.js +19 -0
- package/src/editor/render.jsx +224 -0
- package/src/editor/ui/BasicToolbar.jsx +11 -0
- package/src/editor/ui/BlockButton.jsx +31 -0
- package/src/editor/ui/ClearFormattingButton.jsx +21 -0
- package/src/editor/ui/ExpandedToolbar.jsx +18 -0
- package/src/editor/ui/Expando.jsx +5 -0
- package/src/editor/ui/InlineToolbar.jsx +69 -0
- package/src/editor/ui/MarkButton.jsx +23 -0
- package/src/editor/ui/MarkElementButton.jsx +30 -0
- package/src/editor/ui/Menu.jsx +13 -0
- package/src/editor/ui/PositionedToolbar.jsx +32 -0
- package/src/editor/ui/Separator.jsx +7 -0
- package/src/editor/ui/SlateContextToolbar.jsx +13 -0
- package/src/editor/ui/SlateToolbar.jsx +96 -0
- package/src/editor/ui/Toolbar.jsx +103 -0
- package/src/editor/ui/ToolbarButton.jsx +33 -0
- package/src/editor/ui/ToolbarButton.test.js +25 -0
- package/src/editor/ui/__snapshots__/ToolbarButton.test.js.snap +16 -0
- package/src/editor/ui/index.js +15 -0
- package/src/editor/utils.js +248 -0
- package/src/elementEditor/ContextButtons.jsx +57 -0
- package/src/elementEditor/PluginEditor.jsx +124 -0
- package/src/elementEditor/Readme.md +6 -0
- package/src/elementEditor/SchemaProvider.jsx +4 -0
- package/src/elementEditor/SidebarEditor.jsx +46 -0
- package/src/elementEditor/ToolbarButton.jsx +44 -0
- package/src/elementEditor/index.js +5 -0
- package/src/elementEditor/makeInlineElementPlugin.js +100 -0
- package/src/elementEditor/messages.js +14 -0
- package/src/elementEditor/utils.js +227 -0
- package/src/hooks/index.js +3 -0
- package/src/hooks/useEditorContext.js +6 -0
- package/src/hooks/useIsomorphicLayoutEffect.js +7 -0
- package/src/hooks/useSelectionPosition.js +25 -0
- package/src/i18n.js +180 -0
- package/src/icons/hashlink.svg +57 -0
- package/src/index.js +61 -0
- package/src/reducers/content.js +74 -0
- package/src/reducers/index.js +3 -0
- package/src/reducers/plugins.js +17 -0
- package/src/reducers/selection.js +16 -0
- package/src/utils/blocks.js +379 -0
- package/src/utils/blocks.test.js +138 -0
- package/src/utils/editor.js +31 -0
- package/src/utils/image.js +25 -0
- package/src/utils/index.js +11 -0
- package/src/utils/internals.js +46 -0
- package/src/utils/lists.js +92 -0
- package/src/utils/marks.js +104 -0
- package/src/utils/mime-types.js +24 -0
- package/src/utils/nodes.js +4 -0
- package/src/utils/ops.js +20 -0
- package/src/utils/random.js +17 -0
- package/src/utils/selection.js +236 -0
- package/src/utils/slate-string-utils.js +409 -0
- package/src/utils/volto-blocks.js +314 -0
- package/src/widgets/ErrorBoundary.jsx +27 -0
- package/src/widgets/HtmlSlateWidget.jsx +138 -0
- package/src/widgets/ObjectByTypeWidget.jsx +49 -0
- package/src/widgets/RichTextWidget.jsx +72 -0
- package/src/widgets/RichTextWidgetView.jsx +36 -0
- package/src/widgets/style.css +21 -0
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This is the main toolbar button.
|
|
3
|
+
*/
|
|
4
|
+
import React from 'react';
|
|
5
|
+
import { useSlate } from 'slate-react';
|
|
6
|
+
import { useDispatch } from 'react-redux';
|
|
7
|
+
import { omit } from 'lodash';
|
|
8
|
+
|
|
9
|
+
import { ToolbarButton } from '@plone/volto-slate/editor/ui';
|
|
10
|
+
import { hasRangeSelection } from '@plone/volto-slate/utils';
|
|
11
|
+
import { setPluginOptions } from '@plone/volto-slate/actions';
|
|
12
|
+
|
|
13
|
+
const ElementToolbarButton = (props) => {
|
|
14
|
+
const { isActiveElement, insertElement, pluginId, toolbarButtonIcon } = props;
|
|
15
|
+
const editor = useSlate();
|
|
16
|
+
const isElement = isActiveElement(editor);
|
|
17
|
+
const dispatch = useDispatch();
|
|
18
|
+
const pid = `${editor.uid}-${pluginId}`;
|
|
19
|
+
|
|
20
|
+
const omittedProps = [
|
|
21
|
+
'insertElement',
|
|
22
|
+
'pluginId',
|
|
23
|
+
'toolbarButtonIcon',
|
|
24
|
+
'isActiveElement',
|
|
25
|
+
];
|
|
26
|
+
|
|
27
|
+
return (
|
|
28
|
+
<>
|
|
29
|
+
{hasRangeSelection(editor) && (
|
|
30
|
+
<ToolbarButton
|
|
31
|
+
{...omit(props, ...omittedProps)}
|
|
32
|
+
active={isElement}
|
|
33
|
+
onMouseDown={() => {
|
|
34
|
+
if (!isElement) insertElement(editor, {});
|
|
35
|
+
dispatch(setPluginOptions(pid, { show_sidebar_editor: true }));
|
|
36
|
+
}}
|
|
37
|
+
icon={toolbarButtonIcon}
|
|
38
|
+
/>
|
|
39
|
+
)}
|
|
40
|
+
</>
|
|
41
|
+
);
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
export default ElementToolbarButton;
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import SidebarEditor from './SidebarEditor';
|
|
3
|
+
import makeContextButtons from './ContextButtons';
|
|
4
|
+
import PluginEditor from './PluginEditor';
|
|
5
|
+
import {
|
|
6
|
+
_insertElement,
|
|
7
|
+
_unwrapElement,
|
|
8
|
+
_isActiveElement,
|
|
9
|
+
_getActiveElement,
|
|
10
|
+
} from './utils';
|
|
11
|
+
import messages from './messages';
|
|
12
|
+
import ToolbarButton from './ToolbarButton';
|
|
13
|
+
import SchemaProvider from './SchemaProvider';
|
|
14
|
+
import { omit } from 'lodash';
|
|
15
|
+
|
|
16
|
+
import tagSVG from '@plone/volto/icons/tag.svg';
|
|
17
|
+
|
|
18
|
+
export const makeInlineElementPlugin = (options) => {
|
|
19
|
+
const { elementType, isInlineElement, pluginId, title = 'Element' } = options;
|
|
20
|
+
const omittedProps = [
|
|
21
|
+
'pluginEditor',
|
|
22
|
+
'getActiveElement',
|
|
23
|
+
'unwrapElement',
|
|
24
|
+
'schemaProvider',
|
|
25
|
+
'hasValue',
|
|
26
|
+
'elementType',
|
|
27
|
+
'isInlineElement',
|
|
28
|
+
'editSchema',
|
|
29
|
+
'element',
|
|
30
|
+
'persistentHelper',
|
|
31
|
+
];
|
|
32
|
+
|
|
33
|
+
const pluginOptions = {
|
|
34
|
+
pluginEditor: PluginEditor,
|
|
35
|
+
insertElement: _insertElement(elementType),
|
|
36
|
+
getActiveElement: _getActiveElement(elementType),
|
|
37
|
+
isActiveElement: _isActiveElement(elementType),
|
|
38
|
+
unwrapElement: _unwrapElement(elementType),
|
|
39
|
+
messages,
|
|
40
|
+
toolbarButtonIcon: tagSVG,
|
|
41
|
+
title,
|
|
42
|
+
extensions: [],
|
|
43
|
+
|
|
44
|
+
// a component that should provide a schema as a render prop
|
|
45
|
+
schemaProvider: SchemaProvider,
|
|
46
|
+
// schema that can be used to create the edit form for this component
|
|
47
|
+
// editSchema,
|
|
48
|
+
|
|
49
|
+
// A generic "validation" method, just finds that a "positive" value
|
|
50
|
+
// exists. Plugin authors should overwrite it in options
|
|
51
|
+
// If it returns true, the value is saved in the editor, othwerwise the
|
|
52
|
+
// element type is removed from the editor
|
|
53
|
+
hasValue: (data) => Object.values(data).findIndex((v) => !!v) > -1,
|
|
54
|
+
|
|
55
|
+
...options,
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
const PersistentHelper = (props) => (
|
|
59
|
+
<SidebarEditor {...props} {...pluginOptions} />
|
|
60
|
+
);
|
|
61
|
+
PersistentHelper.id = pluginId;
|
|
62
|
+
|
|
63
|
+
const ElementContextButtons = makeContextButtons(pluginOptions);
|
|
64
|
+
ElementContextButtons.id = pluginId;
|
|
65
|
+
|
|
66
|
+
const install = (config) => {
|
|
67
|
+
const { slate } = config.settings;
|
|
68
|
+
if (isInlineElement) {
|
|
69
|
+
slate.inlineElements[elementType] = true;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
slate.buttons[pluginId] = (props) => (
|
|
73
|
+
<ToolbarButton
|
|
74
|
+
{...props}
|
|
75
|
+
title={title}
|
|
76
|
+
{...omit(pluginOptions, omittedProps)}
|
|
77
|
+
/>
|
|
78
|
+
);
|
|
79
|
+
slate.contextToolbarButtons.push(ElementContextButtons);
|
|
80
|
+
slate.persistentHelpers.push(
|
|
81
|
+
options.persistentHelper
|
|
82
|
+
? options.persistentHelper(pluginOptions)
|
|
83
|
+
: PersistentHelper,
|
|
84
|
+
);
|
|
85
|
+
slate.extensions = [
|
|
86
|
+
...(slate.extensions || []),
|
|
87
|
+
...pluginOptions.extensions,
|
|
88
|
+
];
|
|
89
|
+
slate.elements[elementType] = options.element;
|
|
90
|
+
slate.nodeTypesToHighlight.push(elementType);
|
|
91
|
+
|
|
92
|
+
// The plugin authors should manually add the button to the relevant toolbars
|
|
93
|
+
// slate.toolbarButtons = [...(slate.toolbarButtons || []), pluginId];
|
|
94
|
+
// slate.expandedToolbarButtons = [...(slate.expandedToolbarButtons || []), pluginId];
|
|
95
|
+
|
|
96
|
+
return config;
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
return [install, ElementContextButtons, PersistentHelper, pluginOptions];
|
|
100
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { defineMessages } from 'react-intl'; // , defineMessages
|
|
2
|
+
|
|
3
|
+
const messages = defineMessages({
|
|
4
|
+
edit: {
|
|
5
|
+
id: 'Edit element',
|
|
6
|
+
defaultMessage: 'Edit element',
|
|
7
|
+
},
|
|
8
|
+
delete: {
|
|
9
|
+
id: 'Remove element',
|
|
10
|
+
defaultMessage: 'Remove element',
|
|
11
|
+
},
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
export default messages;
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
import { Editor, Transforms, Node } from 'slate';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @description Creates or updates an existing $elementType. It also takes care
|
|
5
|
+
* of the saved selection and uses PathRef.
|
|
6
|
+
*
|
|
7
|
+
* @param {Editor} editor The Slate editor for the context
|
|
8
|
+
* @param {object} data Relevant data for this element
|
|
9
|
+
*
|
|
10
|
+
* @returns {boolean} true if an element was possibly inserted, false otherwise
|
|
11
|
+
* (currently we do not check here if the element was already applied to the
|
|
12
|
+
* editor)
|
|
13
|
+
*/
|
|
14
|
+
export const _insertElement = (elementType) => (editor, data) => {
|
|
15
|
+
if (editor.getSavedSelection()) {
|
|
16
|
+
const selection = editor.selection || editor.getSavedSelection();
|
|
17
|
+
|
|
18
|
+
const rangeRef = Editor.rangeRef(editor, selection);
|
|
19
|
+
|
|
20
|
+
const res = Array.from(
|
|
21
|
+
Editor.nodes(editor, {
|
|
22
|
+
match: (n) => n.type === elementType,
|
|
23
|
+
mode: 'highest',
|
|
24
|
+
at: selection,
|
|
25
|
+
}),
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
if (res.length) {
|
|
29
|
+
const [, path] = res[0];
|
|
30
|
+
Transforms.setNodes(
|
|
31
|
+
editor,
|
|
32
|
+
{ data },
|
|
33
|
+
{
|
|
34
|
+
at: path ? path : null,
|
|
35
|
+
match: path ? (n) => n.type === elementType : null,
|
|
36
|
+
},
|
|
37
|
+
);
|
|
38
|
+
} else {
|
|
39
|
+
Transforms.wrapNodes(
|
|
40
|
+
editor,
|
|
41
|
+
{ type: elementType, data },
|
|
42
|
+
{
|
|
43
|
+
split: true,
|
|
44
|
+
at: selection,
|
|
45
|
+
match: (node) => {
|
|
46
|
+
return Node.string(node).length !== 0;
|
|
47
|
+
},
|
|
48
|
+
}, //,
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const sel = JSON.parse(JSON.stringify(rangeRef.current));
|
|
53
|
+
Transforms.select(editor, sel);
|
|
54
|
+
editor.setSavedSelection(sel);
|
|
55
|
+
|
|
56
|
+
return true;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return false;
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Will unwrap a node that has as type the one received or one from an array.
|
|
64
|
+
* It identifies the current target element and expands the selection to it, in
|
|
65
|
+
* case the selection was just partial. This allows a "clear and reassign"
|
|
66
|
+
* operation, for example for the Link plugin.
|
|
67
|
+
*
|
|
68
|
+
* @param {string|Object[]} elementType - this can be a string or an array of strings
|
|
69
|
+
* @returns {Object|null} - current node
|
|
70
|
+
*/
|
|
71
|
+
export const _unwrapElement = (elementType) => (editor) => {
|
|
72
|
+
const [link] = Editor.nodes(editor, {
|
|
73
|
+
at: editor.selection,
|
|
74
|
+
match: (node) => node?.type === elementType,
|
|
75
|
+
});
|
|
76
|
+
const [, path] = link;
|
|
77
|
+
const [start, end] = Editor.edges(editor, path);
|
|
78
|
+
const range = { anchor: start, focus: end };
|
|
79
|
+
|
|
80
|
+
const ref = Editor.rangeRef(editor, range);
|
|
81
|
+
|
|
82
|
+
Transforms.select(editor, range);
|
|
83
|
+
Transforms.unwrapNodes(editor, {
|
|
84
|
+
match: (n) =>
|
|
85
|
+
Array.isArray(elementType)
|
|
86
|
+
? elementType.includes(n.type)
|
|
87
|
+
: n.type === elementType,
|
|
88
|
+
at: range,
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
const current = ref.current;
|
|
92
|
+
ref.unref();
|
|
93
|
+
|
|
94
|
+
return current;
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
export const _isActiveElement = (elementType) => (editor) => {
|
|
98
|
+
const selection = editor.selection || editor.getSavedSelection();
|
|
99
|
+
let found;
|
|
100
|
+
try {
|
|
101
|
+
found = Array.from(
|
|
102
|
+
Editor.nodes(editor, {
|
|
103
|
+
match: (n) => n.type === elementType,
|
|
104
|
+
at: selection,
|
|
105
|
+
}) || [],
|
|
106
|
+
);
|
|
107
|
+
} catch (e) {
|
|
108
|
+
// eslint-disable-next-line
|
|
109
|
+
// console.warn('Error in finding active element', e);
|
|
110
|
+
return false;
|
|
111
|
+
}
|
|
112
|
+
if (found.length) return true;
|
|
113
|
+
|
|
114
|
+
if (selection) {
|
|
115
|
+
const { path } = selection.anchor;
|
|
116
|
+
const isAtStart =
|
|
117
|
+
selection.anchor.offset === 0 && selection.focus.offset === 0;
|
|
118
|
+
|
|
119
|
+
if (isAtStart) {
|
|
120
|
+
try {
|
|
121
|
+
found = Editor.previous(editor, {
|
|
122
|
+
at: path,
|
|
123
|
+
// match: (n) => n.type === MENTION,
|
|
124
|
+
});
|
|
125
|
+
} catch (ex) {
|
|
126
|
+
found = [];
|
|
127
|
+
}
|
|
128
|
+
if (found && found[0] && found[0].type === elementType) {
|
|
129
|
+
return true;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
return false;
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Will look for a node that has as type the one received or one from an array
|
|
139
|
+
* @param {string|Object[]} elementType - this can be a string or an array of strings
|
|
140
|
+
* @returns {Object|null} - found node
|
|
141
|
+
*/
|
|
142
|
+
export const _getActiveElement =
|
|
143
|
+
(elementType) =>
|
|
144
|
+
(editor, direction = 'any') => {
|
|
145
|
+
const selection = editor.selection || editor.getSavedSelection();
|
|
146
|
+
let found = [];
|
|
147
|
+
|
|
148
|
+
try {
|
|
149
|
+
found = Array.from(
|
|
150
|
+
Editor.nodes(editor, {
|
|
151
|
+
match: (n) =>
|
|
152
|
+
Array.isArray(elementType)
|
|
153
|
+
? elementType.includes(n.type)
|
|
154
|
+
: n.type === elementType,
|
|
155
|
+
at: selection,
|
|
156
|
+
}),
|
|
157
|
+
);
|
|
158
|
+
} catch (e) {
|
|
159
|
+
return null;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
if (found.length) return found[0];
|
|
163
|
+
|
|
164
|
+
if (!selection) return null;
|
|
165
|
+
|
|
166
|
+
if (direction === 'any' || direction === 'backward') {
|
|
167
|
+
const { path } = selection.anchor;
|
|
168
|
+
const isAtStart =
|
|
169
|
+
selection.anchor.offset === 0 && selection.focus.offset === 0;
|
|
170
|
+
|
|
171
|
+
if (isAtStart) {
|
|
172
|
+
let found;
|
|
173
|
+
try {
|
|
174
|
+
found = Editor.previous(editor, {
|
|
175
|
+
at: path,
|
|
176
|
+
});
|
|
177
|
+
} catch (ex) {
|
|
178
|
+
// eslint-disable-next-line no-console
|
|
179
|
+
console.warn('Unable to find previous node', editor, path);
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
if (found && found[0] && found[0].type === elementType) {
|
|
183
|
+
if (
|
|
184
|
+
(Array.isArray(elementType) &&
|
|
185
|
+
elementType.includes(found[0].type)) ||
|
|
186
|
+
found[0].type === elementType
|
|
187
|
+
) {
|
|
188
|
+
return found;
|
|
189
|
+
}
|
|
190
|
+
} else {
|
|
191
|
+
return null;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
if (direction === 'any' || direction === 'forward') {
|
|
197
|
+
const { path } = selection.anchor;
|
|
198
|
+
const isAtStart =
|
|
199
|
+
selection.anchor.offset === 0 && selection.focus.offset === 0;
|
|
200
|
+
|
|
201
|
+
if (isAtStart) {
|
|
202
|
+
let found;
|
|
203
|
+
try {
|
|
204
|
+
found = Editor.next(editor, {
|
|
205
|
+
at: path,
|
|
206
|
+
});
|
|
207
|
+
} catch (e) {
|
|
208
|
+
// eslint-disable-next-line
|
|
209
|
+
console.warn('Unable to find next node', editor, path);
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
if (found && found[0] && found[0].type === elementType) {
|
|
213
|
+
if (
|
|
214
|
+
(Array.isArray(elementType) &&
|
|
215
|
+
elementType.includes(found[0].type)) ||
|
|
216
|
+
found[0].type === elementType
|
|
217
|
+
) {
|
|
218
|
+
return found;
|
|
219
|
+
}
|
|
220
|
+
} else {
|
|
221
|
+
return null;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
return null;
|
|
227
|
+
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { useSlate, ReactEditor, useSlateSelection } from 'slate-react';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Translates the slate selection (text) to window/screen coordinates
|
|
5
|
+
*
|
|
6
|
+
* TODO: we use the anchor as the base for the position. This could be improved
|
|
7
|
+
*/
|
|
8
|
+
export const useSelectionPosition = () => {
|
|
9
|
+
let rect = {};
|
|
10
|
+
|
|
11
|
+
const editor = useSlate();
|
|
12
|
+
|
|
13
|
+
const selection = useSlateSelection();
|
|
14
|
+
if (selection && ReactEditor.isFocused(editor)) {
|
|
15
|
+
try {
|
|
16
|
+
const [textNode] = ReactEditor.toDOMPoint(editor, selection.anchor);
|
|
17
|
+
const parentNode = textNode.parentNode;
|
|
18
|
+
rect = parentNode.getBoundingClientRect();
|
|
19
|
+
} catch {
|
|
20
|
+
// This happens when the selection is outdated because the nodes have
|
|
21
|
+
// been modified? Seems like a bug in slate, then.
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return rect;
|
|
25
|
+
};
|
package/src/i18n.js
ADDED
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
/* eslint no-console: 0 */
|
|
2
|
+
/**
|
|
3
|
+
* i18n script.
|
|
4
|
+
* @module scripts/i18n
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const { find, keys, map, concat, reduce } = require('lodash');
|
|
8
|
+
const glob = require('glob').sync;
|
|
9
|
+
const fs = require('fs');
|
|
10
|
+
const Pofile = require('pofile');
|
|
11
|
+
const babel = require('@babel/core');
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Extract messages into separate JSON files
|
|
15
|
+
* @function extractMessages
|
|
16
|
+
* @return {undefined}
|
|
17
|
+
*/
|
|
18
|
+
function extractMessages() {
|
|
19
|
+
map(glob('src/**/*.js?(x)'), (filename) => {
|
|
20
|
+
babel.transformFileSync(filename, {}, (err) => {
|
|
21
|
+
if (err) {
|
|
22
|
+
console.log(err);
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Get messages from separate JSON files
|
|
30
|
+
* @function getMessages
|
|
31
|
+
* @return {Object} Object with messages
|
|
32
|
+
*/
|
|
33
|
+
function getMessages() {
|
|
34
|
+
return reduce(
|
|
35
|
+
concat(
|
|
36
|
+
{},
|
|
37
|
+
...map(
|
|
38
|
+
// We ignore the existing customized shadowed components ones, since most
|
|
39
|
+
// probably we won't be overriding them
|
|
40
|
+
// If so, we should do it in the config object or somewhere else
|
|
41
|
+
glob('build/messages/src/**/*.json', {
|
|
42
|
+
ignore: 'build/messages/src/customizations/**',
|
|
43
|
+
}),
|
|
44
|
+
(filename) =>
|
|
45
|
+
map(JSON.parse(fs.readFileSync(filename, 'utf8')), (message) => ({
|
|
46
|
+
...message,
|
|
47
|
+
filename: filename.match(/build\/messages\/src\/(.*).json$/)[1],
|
|
48
|
+
})),
|
|
49
|
+
),
|
|
50
|
+
),
|
|
51
|
+
(current, value) => {
|
|
52
|
+
let result = current;
|
|
53
|
+
if (current.id) {
|
|
54
|
+
result = {
|
|
55
|
+
[current.id]: {
|
|
56
|
+
defaultMessage: current.defaultMessage,
|
|
57
|
+
filenames: [current.filename],
|
|
58
|
+
},
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (result[value.id]) {
|
|
63
|
+
result[value.id].filenames.push(value.filename);
|
|
64
|
+
} else {
|
|
65
|
+
result[value.id] = {
|
|
66
|
+
defaultMessage: value.defaultMessage,
|
|
67
|
+
filenames: [value.filename],
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
return result;
|
|
71
|
+
},
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Convert messages to pot format
|
|
77
|
+
* @function messagesToPot
|
|
78
|
+
* @param {Object} messages Messages
|
|
79
|
+
* @return {string} Formatted pot string
|
|
80
|
+
*/
|
|
81
|
+
function messagesToPot(messages) {
|
|
82
|
+
return map(keys(messages).sort(), (key) =>
|
|
83
|
+
[
|
|
84
|
+
...map(messages[key].filenames, (filename) => `#: ${filename}`),
|
|
85
|
+
`# defaultMessage: ${messages[key].defaultMessage}`,
|
|
86
|
+
`msgid "${key}"`,
|
|
87
|
+
'msgstr ""',
|
|
88
|
+
].join('\n'),
|
|
89
|
+
).join('\n\n');
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Pot header
|
|
94
|
+
* @function potHeader
|
|
95
|
+
* @return {string} Formatted pot header
|
|
96
|
+
*/
|
|
97
|
+
function potHeader() {
|
|
98
|
+
return `msgid ""
|
|
99
|
+
msgstr ""
|
|
100
|
+
"Project-Id-Version: Plone\\n"
|
|
101
|
+
"POT-Creation-Date: ${new Date().toISOString()}\\n"
|
|
102
|
+
"Last-Translator: Plone i18n <plone-i18n@lists.sourceforge.net>\\n"
|
|
103
|
+
"Language-Team: Plone i18n <plone-i18n@lists.sourceforge.net>\\n"
|
|
104
|
+
"MIME-Version: 1.0\\n"
|
|
105
|
+
"Content-Type: text/plain; charset=utf-8\\n"
|
|
106
|
+
"Content-Transfer-Encoding: 8bit\\n"
|
|
107
|
+
"Plural-Forms: nplurals=1; plural=0;\\n"
|
|
108
|
+
"Language-Code: en\\n"
|
|
109
|
+
"Language-Name: English\\n"
|
|
110
|
+
"Preferred-Encodings: utf-8\\n"
|
|
111
|
+
"Domain: volto\\n"
|
|
112
|
+
|
|
113
|
+
`;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Format header
|
|
118
|
+
* @function formatHeader
|
|
119
|
+
* @param {Array} comments Array of comments
|
|
120
|
+
* @param {Object} headers Object of header items
|
|
121
|
+
* @return {string} Formatted header
|
|
122
|
+
*/
|
|
123
|
+
function formatHeader(comments, headers) {
|
|
124
|
+
return [
|
|
125
|
+
...map(comments, (comment) => `# ${comment}`),
|
|
126
|
+
'msgid ""',
|
|
127
|
+
'msgstr ""',
|
|
128
|
+
...map(keys(headers), (key) => `"${key}: ${headers[key]}\\n"`),
|
|
129
|
+
'',
|
|
130
|
+
].join('\n');
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Sync po by the pot file
|
|
135
|
+
* @function syncPoByPot
|
|
136
|
+
* @return {undefined}
|
|
137
|
+
*/
|
|
138
|
+
function syncPoByPot() {
|
|
139
|
+
const pot = Pofile.parse(fs.readFileSync('locales/volto.pot', 'utf8'));
|
|
140
|
+
|
|
141
|
+
map(glob('locales/**/*.po'), (filename) => {
|
|
142
|
+
const po = Pofile.parse(fs.readFileSync(filename, 'utf8'));
|
|
143
|
+
|
|
144
|
+
fs.writeFileSync(
|
|
145
|
+
filename,
|
|
146
|
+
`${formatHeader(po.comments, po.headers)}
|
|
147
|
+
${map(pot.items, (item) => {
|
|
148
|
+
const poItem = find(po.items, { msgid: item.msgid });
|
|
149
|
+
return [
|
|
150
|
+
`${map(item.references, (ref) => `#: ${ref}`).join('\n')}`,
|
|
151
|
+
`msgid "${item.msgid}"`,
|
|
152
|
+
`msgstr "${poItem ? poItem.msgstr : ''}"`,
|
|
153
|
+
].join('\n');
|
|
154
|
+
}).join('\n\n')}\n`,
|
|
155
|
+
);
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// Main tasks
|
|
160
|
+
console.log('Extracting messages from source files...');
|
|
161
|
+
extractMessages();
|
|
162
|
+
console.log('Synchronizing messages to pot file...');
|
|
163
|
+
// We only write the pot file if it's really different
|
|
164
|
+
const newPot = `${potHeader()}${messagesToPot(getMessages())}\n`.replace(
|
|
165
|
+
/"POT-Creation-Date:(.*)\\n"/,
|
|
166
|
+
'',
|
|
167
|
+
);
|
|
168
|
+
const oldPot = fs
|
|
169
|
+
.readFileSync('locales/volto.pot', 'utf8')
|
|
170
|
+
.replace(/"POT-Creation-Date:(.*)\\n"/, '');
|
|
171
|
+
|
|
172
|
+
if (newPot !== oldPot) {
|
|
173
|
+
fs.writeFileSync(
|
|
174
|
+
'locales/volto.pot',
|
|
175
|
+
`${potHeader()}${messagesToPot(getMessages())}\n`,
|
|
176
|
+
);
|
|
177
|
+
}
|
|
178
|
+
console.log('Synchronizing messages to po files...');
|
|
179
|
+
syncPoByPot();
|
|
180
|
+
console.log('done!');
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="iso-8859-1"?>
|
|
2
|
+
<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
|
3
|
+
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
|
4
|
+
viewBox="0 0 490.667 490.667" style="enable-background:new 0 0 490.667 490.667;" xml:space="preserve">
|
|
5
|
+
<g>
|
|
6
|
+
<path style="fill:#455A64;" d="M480,181.44H53.333c-5.891,0-10.667-4.776-10.667-10.667c0-5.891,4.776-10.667,10.667-10.667H480
|
|
7
|
+
c5.891,0,10.667,4.776,10.667,10.667C490.667,176.664,485.891,181.44,480,181.44z"/>
|
|
8
|
+
<path style="fill:#455A64;" d="M437.333,330.773H10.667C4.776,330.773,0,325.998,0,320.106c0-5.891,4.776-10.667,10.667-10.667
|
|
9
|
+
h426.667c5.891,0,10.667,4.776,10.667,10.667C448,325.998,443.224,330.773,437.333,330.773z"/>
|
|
10
|
+
<path style="fill:#455A64;" d="M245.333,458.773c-5.891-0.001-10.666-4.778-10.664-10.669c0-1.259,0.223-2.507,0.659-3.688
|
|
11
|
+
L384.661,39.082c1.922-5.569,7.995-8.524,13.563-6.602c5.569,1.922,8.524,7.995,6.602,13.563c-0.049,0.141-0.1,0.281-0.155,0.42
|
|
12
|
+
L255.339,451.797C253.793,455.988,249.8,458.772,245.333,458.773z"/>
|
|
13
|
+
<path style="fill:#455A64;" d="M96,458.773c-5.891-0.001-10.666-4.778-10.664-10.669c0-1.259,0.223-2.507,0.659-3.688
|
|
14
|
+
L235.328,39.082c1.922-5.569,7.995-8.524,13.563-6.602c5.569,1.922,8.524,7.995,6.602,13.563c-0.049,0.141-0.1,0.281-0.155,0.42
|
|
15
|
+
L106.005,451.797C104.46,455.988,100.467,458.772,96,458.773z"/>
|
|
16
|
+
</g>
|
|
17
|
+
<path d="M480,181.44H53.333c-5.891,0-10.667-4.776-10.667-10.667c0-5.891,4.776-10.667,10.667-10.667H480
|
|
18
|
+
c5.891,0,10.667,4.776,10.667,10.667C490.667,176.664,485.891,181.44,480,181.44z"/>
|
|
19
|
+
<path d="M437.333,330.773H10.667C4.776,330.773,0,325.998,0,320.106c0-5.891,4.776-10.667,10.667-10.667h426.667
|
|
20
|
+
c5.891,0,10.667,4.776,10.667,10.667C448,325.998,443.224,330.773,437.333,330.773z"/>
|
|
21
|
+
<path d="M245.333,458.773c-5.891-0.001-10.666-4.778-10.664-10.669c0-1.259,0.223-2.507,0.659-3.688L384.661,39.082
|
|
22
|
+
c1.922-5.569,7.995-8.524,13.563-6.602c5.569,1.922,8.524,7.995,6.602,13.563c-0.049,0.141-0.1,0.281-0.155,0.42L255.339,451.797
|
|
23
|
+
C253.793,455.988,249.8,458.772,245.333,458.773z"/>
|
|
24
|
+
<path d="M96,458.773c-5.891-0.001-10.666-4.778-10.664-10.669c0-1.259,0.223-2.507,0.659-3.688L235.328,39.082
|
|
25
|
+
c1.922-5.569,7.995-8.524,13.563-6.602c5.569,1.922,8.524,7.995,6.602,13.563c-0.049,0.141-0.1,0.281-0.155,0.42L106.005,451.797
|
|
26
|
+
C104.46,455.988,100.467,458.772,96,458.773z"/>
|
|
27
|
+
<g>
|
|
28
|
+
</g>
|
|
29
|
+
<g>
|
|
30
|
+
</g>
|
|
31
|
+
<g>
|
|
32
|
+
</g>
|
|
33
|
+
<g>
|
|
34
|
+
</g>
|
|
35
|
+
<g>
|
|
36
|
+
</g>
|
|
37
|
+
<g>
|
|
38
|
+
</g>
|
|
39
|
+
<g>
|
|
40
|
+
</g>
|
|
41
|
+
<g>
|
|
42
|
+
</g>
|
|
43
|
+
<g>
|
|
44
|
+
</g>
|
|
45
|
+
<g>
|
|
46
|
+
</g>
|
|
47
|
+
<g>
|
|
48
|
+
</g>
|
|
49
|
+
<g>
|
|
50
|
+
</g>
|
|
51
|
+
<g>
|
|
52
|
+
</g>
|
|
53
|
+
<g>
|
|
54
|
+
</g>
|
|
55
|
+
<g>
|
|
56
|
+
</g>
|
|
57
|
+
</svg>
|