@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,40 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import renderer from 'react-test-renderer';
|
|
3
|
+
import configureStore from 'redux-mock-store';
|
|
4
|
+
import { Provider } from 'react-intl-redux';
|
|
5
|
+
|
|
6
|
+
import Edit from './TableBlockEdit';
|
|
7
|
+
|
|
8
|
+
const mockStore = configureStore();
|
|
9
|
+
|
|
10
|
+
global.__SERVER__ = true; // eslint-disable-line no-underscore-dangle
|
|
11
|
+
|
|
12
|
+
test('renders an edit table block component', () => {
|
|
13
|
+
const store = mockStore({
|
|
14
|
+
intl: {
|
|
15
|
+
locale: 'en',
|
|
16
|
+
messages: {},
|
|
17
|
+
},
|
|
18
|
+
});
|
|
19
|
+
const component = renderer.create(
|
|
20
|
+
<Provider store={store}>
|
|
21
|
+
<Edit
|
|
22
|
+
data={{ table: { rows: [] } }}
|
|
23
|
+
selected={false}
|
|
24
|
+
block="1234"
|
|
25
|
+
onAddBlock={() => {}}
|
|
26
|
+
onChangeBlock={() => {}}
|
|
27
|
+
onSelectBlock={() => {}}
|
|
28
|
+
onDeleteBlock={() => {}}
|
|
29
|
+
onInsertBlock={() => {}}
|
|
30
|
+
onFocusPreviousBlock={() => {}}
|
|
31
|
+
onFocusNextBlock={() => {}}
|
|
32
|
+
handleKeyDown={() => {}}
|
|
33
|
+
onMutateBlock={() => {}}
|
|
34
|
+
index={1}
|
|
35
|
+
/>
|
|
36
|
+
</Provider>,
|
|
37
|
+
);
|
|
38
|
+
const json = component.toJSON();
|
|
39
|
+
expect(json).toMatchSnapshot();
|
|
40
|
+
});
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Slate Table block's View component.
|
|
3
|
+
* @module volto-slate/blocks/Table/View
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import React, { useState, useMemo } from 'react';
|
|
7
|
+
import PropTypes from 'prop-types';
|
|
8
|
+
import { Table } from 'semantic-ui-react';
|
|
9
|
+
import { map } from 'lodash';
|
|
10
|
+
import {
|
|
11
|
+
serializeNodes,
|
|
12
|
+
serializeNodesToText,
|
|
13
|
+
} from '@plone/volto-slate/editor/render';
|
|
14
|
+
import { Node } from 'slate';
|
|
15
|
+
|
|
16
|
+
// TODO: loading LESS files with `volto-slate/...` paths does not work currently
|
|
17
|
+
import '../../editor/plugins/Table/less/public.less';
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Slate Table block's View class.
|
|
21
|
+
* @class View
|
|
22
|
+
* @extends Component
|
|
23
|
+
* @param {object} data The table data to render as a table.
|
|
24
|
+
*/
|
|
25
|
+
const View = ({ data }) => {
|
|
26
|
+
const [state, setState] = useState({
|
|
27
|
+
column: null,
|
|
28
|
+
direction: null,
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
const headers = useMemo(() => {
|
|
32
|
+
return data.table.rows?.[0]?.cells;
|
|
33
|
+
}, [data.table.rows]);
|
|
34
|
+
|
|
35
|
+
const rows = useMemo(() => {
|
|
36
|
+
const items = [];
|
|
37
|
+
if (!data.table.rows) return [];
|
|
38
|
+
data.table.rows.forEach((row, index) => {
|
|
39
|
+
if (index > 0) {
|
|
40
|
+
items[index] = [];
|
|
41
|
+
row.cells.forEach((cell, cellIndex) => {
|
|
42
|
+
items[index][cellIndex] = {
|
|
43
|
+
...cell,
|
|
44
|
+
value:
|
|
45
|
+
cell.value && Node.string({ children: cell.value }).length > 0
|
|
46
|
+
? serializeNodes(cell.value)
|
|
47
|
+
: '\u00A0',
|
|
48
|
+
valueText:
|
|
49
|
+
cell.value && Node.string({ children: cell.value }).length > 0
|
|
50
|
+
? serializeNodesToText(cell.value)
|
|
51
|
+
: '\u00A0',
|
|
52
|
+
};
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
return items;
|
|
57
|
+
}, [data.table.rows]);
|
|
58
|
+
|
|
59
|
+
const sortedRows = useMemo(() => {
|
|
60
|
+
if (state.column === null) return Object.keys(rows);
|
|
61
|
+
return Object.keys(rows).sort((a, b) => {
|
|
62
|
+
const a_text = rows[a][state.column].valueText;
|
|
63
|
+
const b_text = rows[b][state.column].valueText;
|
|
64
|
+
if (state.direction === 'ascending' ? a_text < b_text : a_text > b_text) {
|
|
65
|
+
return -1;
|
|
66
|
+
}
|
|
67
|
+
if (state.direction === 'ascending' ? a_text > b_text : a_text < b_text) {
|
|
68
|
+
return 1;
|
|
69
|
+
}
|
|
70
|
+
return 0;
|
|
71
|
+
});
|
|
72
|
+
}, [state, rows]);
|
|
73
|
+
|
|
74
|
+
return (
|
|
75
|
+
<>
|
|
76
|
+
{data && data.table && (
|
|
77
|
+
<Table
|
|
78
|
+
fixed={data.table.fixed}
|
|
79
|
+
compact={data.table.compact}
|
|
80
|
+
basic={data.table.basic ? 'very' : false}
|
|
81
|
+
celled={data.table.celled}
|
|
82
|
+
inverted={data.table.inverted}
|
|
83
|
+
striped={data.table.striped}
|
|
84
|
+
sortable={data.table.sortable}
|
|
85
|
+
className="slate-table-block"
|
|
86
|
+
>
|
|
87
|
+
{!data.table.hideHeaders ? (
|
|
88
|
+
<Table.Header>
|
|
89
|
+
<Table.Row>
|
|
90
|
+
{headers.map((cell, index) => (
|
|
91
|
+
<Table.HeaderCell
|
|
92
|
+
key={cell.key}
|
|
93
|
+
textAlign="left"
|
|
94
|
+
verticalAlign="middle"
|
|
95
|
+
sorted={state.column === index ? state.direction : null}
|
|
96
|
+
onClick={() => {
|
|
97
|
+
if (!data.table.sortable) return;
|
|
98
|
+
setState({
|
|
99
|
+
column: index,
|
|
100
|
+
direction:
|
|
101
|
+
state.column !== index
|
|
102
|
+
? 'ascending'
|
|
103
|
+
: state.direction === 'ascending'
|
|
104
|
+
? 'descending'
|
|
105
|
+
: 'ascending',
|
|
106
|
+
});
|
|
107
|
+
}}
|
|
108
|
+
>
|
|
109
|
+
{cell.value &&
|
|
110
|
+
Node.string({ children: cell.value }).length > 0
|
|
111
|
+
? serializeNodes(cell.value)
|
|
112
|
+
: '\u00A0'}
|
|
113
|
+
</Table.HeaderCell>
|
|
114
|
+
))}
|
|
115
|
+
</Table.Row>
|
|
116
|
+
</Table.Header>
|
|
117
|
+
) : (
|
|
118
|
+
''
|
|
119
|
+
)}
|
|
120
|
+
<Table.Body>
|
|
121
|
+
{map(sortedRows, (row) => (
|
|
122
|
+
<Table.Row key={row}>
|
|
123
|
+
{map(rows[row], (cell) => (
|
|
124
|
+
<Table.Cell
|
|
125
|
+
key={cell.key}
|
|
126
|
+
textAlign="left"
|
|
127
|
+
verticalAlign="middle"
|
|
128
|
+
>
|
|
129
|
+
{cell.value}
|
|
130
|
+
</Table.Cell>
|
|
131
|
+
))}
|
|
132
|
+
</Table.Row>
|
|
133
|
+
))}
|
|
134
|
+
</Table.Body>
|
|
135
|
+
</Table>
|
|
136
|
+
)}
|
|
137
|
+
</>
|
|
138
|
+
);
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Property types.
|
|
143
|
+
* @property {Object} propTypes Property types.
|
|
144
|
+
* @static
|
|
145
|
+
*/
|
|
146
|
+
View.propTypes = {
|
|
147
|
+
data: PropTypes.objectOf(PropTypes.any).isRequired,
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
export default View;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import renderer from 'react-test-renderer';
|
|
3
|
+
import View from './TableBlockView';
|
|
4
|
+
import config from '@plone/volto/registry';
|
|
5
|
+
|
|
6
|
+
beforeAll(() => {
|
|
7
|
+
config.settings = {
|
|
8
|
+
slate: {
|
|
9
|
+
elements: {
|
|
10
|
+
default: ({ attributes, children }) => (
|
|
11
|
+
<p {...attributes}>{children}</p>
|
|
12
|
+
),
|
|
13
|
+
h2: ({ attributes, children }) => <h2 {...attributes}>{children}</h2>,
|
|
14
|
+
},
|
|
15
|
+
leafs: {},
|
|
16
|
+
},
|
|
17
|
+
};
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
test('renders a view table component', () => {
|
|
21
|
+
const component = renderer.create(
|
|
22
|
+
<View
|
|
23
|
+
data={{
|
|
24
|
+
table: {
|
|
25
|
+
rows: [
|
|
26
|
+
{
|
|
27
|
+
key: 'a',
|
|
28
|
+
cells: [
|
|
29
|
+
{
|
|
30
|
+
type: 'data',
|
|
31
|
+
key: 'b',
|
|
32
|
+
value: [
|
|
33
|
+
{
|
|
34
|
+
type: 'h2',
|
|
35
|
+
children: [{ text: 'My header' }],
|
|
36
|
+
},
|
|
37
|
+
],
|
|
38
|
+
},
|
|
39
|
+
],
|
|
40
|
+
},
|
|
41
|
+
],
|
|
42
|
+
hideHeaders: false,
|
|
43
|
+
},
|
|
44
|
+
}}
|
|
45
|
+
/>,
|
|
46
|
+
);
|
|
47
|
+
const json = component.toJSON();
|
|
48
|
+
expect(json).toMatchSnapshot();
|
|
49
|
+
});
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
2
|
+
|
|
3
|
+
exports[`renders an edit table block component 1`] = `
|
|
4
|
+
<div
|
|
5
|
+
className="block table"
|
|
6
|
+
>
|
|
7
|
+
<table
|
|
8
|
+
className="ui table slate-table-block"
|
|
9
|
+
>
|
|
10
|
+
<thead
|
|
11
|
+
className=""
|
|
12
|
+
>
|
|
13
|
+
<tr
|
|
14
|
+
className="left aligned"
|
|
15
|
+
/>
|
|
16
|
+
</thead>
|
|
17
|
+
<tbody
|
|
18
|
+
className=""
|
|
19
|
+
/>
|
|
20
|
+
</table>
|
|
21
|
+
</div>
|
|
22
|
+
`;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
2
|
+
|
|
3
|
+
exports[`renders a view table component 1`] = `
|
|
4
|
+
<table
|
|
5
|
+
className="ui table slate-table-block"
|
|
6
|
+
>
|
|
7
|
+
<thead
|
|
8
|
+
className=""
|
|
9
|
+
>
|
|
10
|
+
<tr
|
|
11
|
+
className=""
|
|
12
|
+
>
|
|
13
|
+
<th
|
|
14
|
+
className="left aligned middle aligned"
|
|
15
|
+
onClick={[Function]}
|
|
16
|
+
>
|
|
17
|
+
<h2>
|
|
18
|
+
My header
|
|
19
|
+
</h2>
|
|
20
|
+
</th>
|
|
21
|
+
</tr>
|
|
22
|
+
</thead>
|
|
23
|
+
<tbody
|
|
24
|
+
className=""
|
|
25
|
+
/>
|
|
26
|
+
</table>
|
|
27
|
+
`;
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { v4 as uuid } from 'uuid';
|
|
2
|
+
import { Editor, Transforms } from 'slate';
|
|
3
|
+
import {
|
|
4
|
+
TABLE,
|
|
5
|
+
THEAD,
|
|
6
|
+
TBODY,
|
|
7
|
+
TFOOT,
|
|
8
|
+
TD,
|
|
9
|
+
TH,
|
|
10
|
+
TR,
|
|
11
|
+
} from '@plone/volto-slate/constants';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* @param {Array} rows The array of rows that almost completely defines a
|
|
15
|
+
* `table`-typed block.
|
|
16
|
+
* @returns {Array} A tuple `[id, block]` where `id` is the new block's ID and
|
|
17
|
+
* the `block` is all the block's data.
|
|
18
|
+
*/
|
|
19
|
+
export function syncCreateTableBlock(rows) {
|
|
20
|
+
const id = uuid();
|
|
21
|
+
const block = {
|
|
22
|
+
'@type': 'table',
|
|
23
|
+
table: {
|
|
24
|
+
rows,
|
|
25
|
+
},
|
|
26
|
+
};
|
|
27
|
+
return [id, block];
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* @param {Editor} editor The Slate Editor from which to extract tables.
|
|
32
|
+
* @param {PathRef} pathRef Has the current value a `Path` so that the search is
|
|
33
|
+
* done just inside nodes in that `Path`.
|
|
34
|
+
* @returns Extracts tables from a Slate `Editor` into an array of detached
|
|
35
|
+
* `table` blocks.
|
|
36
|
+
*/
|
|
37
|
+
export const extractTables = (editor, pathRef) => {
|
|
38
|
+
const tableNodes = Array.from(
|
|
39
|
+
Editor.nodes(editor, {
|
|
40
|
+
at: pathRef.current,
|
|
41
|
+
match: (node) => node.type === TABLE,
|
|
42
|
+
}),
|
|
43
|
+
);
|
|
44
|
+
const tables = tableNodes.map(([node]) => extractVoltoTable(node));
|
|
45
|
+
|
|
46
|
+
Transforms.removeNodes(editor, {
|
|
47
|
+
at: pathRef.current,
|
|
48
|
+
match: (node) => node.type === TABLE,
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
return tables.map((el) => syncCreateTableBlock(el));
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* @param {Node[]} fragment A Slate document fragment.
|
|
56
|
+
* @returns {Array} An array of rows in the format requested by `table`
|
|
57
|
+
* blocks.
|
|
58
|
+
*/
|
|
59
|
+
function collectRowsFrom(fragment) {
|
|
60
|
+
let rows = [];
|
|
61
|
+
fragment.children.forEach((y) => {
|
|
62
|
+
if (y.type === TR) {
|
|
63
|
+
let row = { key: uuid(), cells: [] };
|
|
64
|
+
|
|
65
|
+
y.children.forEach((z) => {
|
|
66
|
+
let val = JSON.parse(JSON.stringify(z.children));
|
|
67
|
+
if (z.type === TD) {
|
|
68
|
+
row.cells.push({
|
|
69
|
+
key: uuid(),
|
|
70
|
+
type: 'data',
|
|
71
|
+
value: val,
|
|
72
|
+
});
|
|
73
|
+
} else if (z.type === TH) {
|
|
74
|
+
row.cells.push({
|
|
75
|
+
key: uuid(),
|
|
76
|
+
type: 'header',
|
|
77
|
+
value: val,
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
rows.push(row);
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
return rows;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* @param {HTMLElement} el The <table> element from which to extract rows.
|
|
90
|
+
* @returns {Array} A rows array that contains rows in the format required by
|
|
91
|
+
* `table` blocks.
|
|
92
|
+
*/
|
|
93
|
+
function extractVoltoTable(el) {
|
|
94
|
+
let thead = [],
|
|
95
|
+
tfoot = [],
|
|
96
|
+
tbody = [];
|
|
97
|
+
|
|
98
|
+
el.children.forEach((fragment) => {
|
|
99
|
+
if (fragment.type === THEAD) {
|
|
100
|
+
// not supported by View fully, so prepend this to tbody below
|
|
101
|
+
thead = collectRowsFrom(fragment);
|
|
102
|
+
} else if (fragment.type === TBODY) {
|
|
103
|
+
tbody = collectRowsFrom(fragment);
|
|
104
|
+
} else if (fragment.type === TFOOT) {
|
|
105
|
+
// not supported by View fully, so append this to tbody below
|
|
106
|
+
tfoot = collectRowsFrom(fragment);
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
const rows = [...thead, ...tbody, ...tfoot];
|
|
111
|
+
|
|
112
|
+
return rows;
|
|
113
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import TableBlockEdit from './TableBlockEdit';
|
|
2
|
+
import TableBlockView from './TableBlockView';
|
|
3
|
+
import { extractTables } from './deconstruct';
|
|
4
|
+
import { normalizeTable } from './extensions/normalizeTable';
|
|
5
|
+
import { normalizeExternalData } from '../Text/extensions';
|
|
6
|
+
|
|
7
|
+
import tableSVG from '@plone/volto/icons/table.svg';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* @summary Called from Volto to configure new or existing Volto block types.
|
|
11
|
+
* @param {object} config The object received from Volto containing the
|
|
12
|
+
* configuration for all the blocks.
|
|
13
|
+
*/
|
|
14
|
+
export default function install(config) {
|
|
15
|
+
config.settings.slate = {
|
|
16
|
+
...config.settings.slate,
|
|
17
|
+
voltoBlockEmiters: [
|
|
18
|
+
...(config.settings.slate.voltoBlockEmiters || []),
|
|
19
|
+
extractTables,
|
|
20
|
+
],
|
|
21
|
+
tableblockExtensions: [
|
|
22
|
+
// First here gets executed last
|
|
23
|
+
// withLists,
|
|
24
|
+
// withSplitBlocksOnBreak,
|
|
25
|
+
// withDeleteSelectionOnEnter,
|
|
26
|
+
// withDeserializers,
|
|
27
|
+
// breakList,
|
|
28
|
+
normalizeTable,
|
|
29
|
+
normalizeExternalData,
|
|
30
|
+
],
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const tableBlockConfig = {
|
|
34
|
+
id: 'table',
|
|
35
|
+
title: 'Table',
|
|
36
|
+
icon: tableSVG,
|
|
37
|
+
group: 'common',
|
|
38
|
+
view: TableBlockView,
|
|
39
|
+
edit: TableBlockEdit,
|
|
40
|
+
restricted: false,
|
|
41
|
+
mostUsed: false,
|
|
42
|
+
blockHasOwnFocusManagement: true,
|
|
43
|
+
sidebarTab: 1,
|
|
44
|
+
security: {
|
|
45
|
+
addPermission: [],
|
|
46
|
+
view: [],
|
|
47
|
+
},
|
|
48
|
+
// blockHasValue: (data) => {
|
|
49
|
+
// return true;
|
|
50
|
+
// },
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
config.blocks.blocksConfig.table.restricted = true;
|
|
54
|
+
config.blocks.blocksConfig.slateTable = {
|
|
55
|
+
...tableBlockConfig,
|
|
56
|
+
id: 'slateTable',
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
return config;
|
|
60
|
+
}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { defineMessages } from 'react-intl';
|
|
2
|
+
|
|
3
|
+
const messages = defineMessages({
|
|
4
|
+
hideHeaders: {
|
|
5
|
+
id: 'Hide headers',
|
|
6
|
+
defaultMessage: 'Hide headers',
|
|
7
|
+
},
|
|
8
|
+
sortable: {
|
|
9
|
+
id: 'Make the table sortable',
|
|
10
|
+
defaultMessage: 'Make the table sortable',
|
|
11
|
+
},
|
|
12
|
+
sortableDescription: {
|
|
13
|
+
id: 'Visible only in view mode',
|
|
14
|
+
defaultMessage: 'Visible only in view mode',
|
|
15
|
+
},
|
|
16
|
+
fixed: {
|
|
17
|
+
id: 'Fixed width table cells',
|
|
18
|
+
defaultMessage: 'Fixed width table cells',
|
|
19
|
+
},
|
|
20
|
+
compact: {
|
|
21
|
+
id: 'Make the table compact',
|
|
22
|
+
defaultMessage: 'Make the table compact',
|
|
23
|
+
},
|
|
24
|
+
basic: {
|
|
25
|
+
id: 'Reduce complexity',
|
|
26
|
+
defaultMessage: 'Reduce complexity',
|
|
27
|
+
},
|
|
28
|
+
celled: {
|
|
29
|
+
id: 'Divide each row into separate cells',
|
|
30
|
+
defaultMessage: 'Divide each row into separate cells',
|
|
31
|
+
},
|
|
32
|
+
inverted: {
|
|
33
|
+
id: 'Table color inverted',
|
|
34
|
+
defaultMessage: 'Table color inverted',
|
|
35
|
+
},
|
|
36
|
+
striped: {
|
|
37
|
+
id: 'Stripe alternate rows with color',
|
|
38
|
+
defaultMessage: 'Stripe alternate rows with color',
|
|
39
|
+
},
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
function TableSchema(props) {
|
|
43
|
+
const { intl } = props;
|
|
44
|
+
return {
|
|
45
|
+
title: 'Table block',
|
|
46
|
+
fieldsets: [
|
|
47
|
+
{
|
|
48
|
+
id: 'default',
|
|
49
|
+
title: 'Default',
|
|
50
|
+
fields: [
|
|
51
|
+
'hideHeaders',
|
|
52
|
+
'sortable',
|
|
53
|
+
'fixed',
|
|
54
|
+
'celled',
|
|
55
|
+
'striped',
|
|
56
|
+
'compact',
|
|
57
|
+
'basic',
|
|
58
|
+
'inverted',
|
|
59
|
+
],
|
|
60
|
+
},
|
|
61
|
+
],
|
|
62
|
+
properties: {
|
|
63
|
+
hideHeaders: {
|
|
64
|
+
title: intl.formatMessage(messages.hideHeaders),
|
|
65
|
+
type: 'boolean',
|
|
66
|
+
},
|
|
67
|
+
sortable: {
|
|
68
|
+
title: intl.formatMessage(messages.sortable),
|
|
69
|
+
type: 'boolean',
|
|
70
|
+
},
|
|
71
|
+
fixed: {
|
|
72
|
+
title: intl.formatMessage(messages.fixed),
|
|
73
|
+
type: 'boolean',
|
|
74
|
+
},
|
|
75
|
+
celled: {
|
|
76
|
+
title: intl.formatMessage(messages.celled),
|
|
77
|
+
type: 'boolean',
|
|
78
|
+
},
|
|
79
|
+
striped: {
|
|
80
|
+
title: intl.formatMessage(messages.striped),
|
|
81
|
+
type: 'boolean',
|
|
82
|
+
},
|
|
83
|
+
compact: {
|
|
84
|
+
title: intl.formatMessage(messages.compact),
|
|
85
|
+
type: 'boolean',
|
|
86
|
+
},
|
|
87
|
+
basic: {
|
|
88
|
+
title: intl.formatMessage(messages.basic),
|
|
89
|
+
type: 'boolean',
|
|
90
|
+
},
|
|
91
|
+
inverted: {
|
|
92
|
+
title: intl.formatMessage(messages.inverted),
|
|
93
|
+
type: 'boolean',
|
|
94
|
+
},
|
|
95
|
+
},
|
|
96
|
+
required: [],
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export function TableBlockSchema(props) {
|
|
101
|
+
return {
|
|
102
|
+
title: 'Table block',
|
|
103
|
+
fieldsets: [
|
|
104
|
+
{
|
|
105
|
+
id: 'default',
|
|
106
|
+
title: 'Default',
|
|
107
|
+
fields: ['table'],
|
|
108
|
+
},
|
|
109
|
+
],
|
|
110
|
+
properties: {
|
|
111
|
+
table: {
|
|
112
|
+
title: 'Table block',
|
|
113
|
+
widget: 'object',
|
|
114
|
+
schema: TableSchema(props),
|
|
115
|
+
},
|
|
116
|
+
},
|
|
117
|
+
|
|
118
|
+
required: [],
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export default TableBlockSchema;
|