@pie-lib/editable-html-tip-tap 1.0.0
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/CHANGELOG.json +32 -0
- package/CHANGELOG.md +2280 -0
- package/lib/__tests__/editor.test.js +470 -0
- package/lib/__tests__/serialization.test.js +246 -0
- package/lib/__tests__/utils.js +106 -0
- package/lib/block-tags.js +25 -0
- package/lib/constants.js +16 -0
- package/lib/editor.js +1356 -0
- package/lib/extensions/MediaView.js +112 -0
- package/lib/extensions/characters.js +65 -0
- package/lib/extensions/component.js +325 -0
- package/lib/extensions/css.js +252 -0
- package/lib/extensions/custom-toolbar-wrapper.js +124 -0
- package/lib/extensions/image.js +106 -0
- package/lib/extensions/math.js +330 -0
- package/lib/extensions/media.js +276 -0
- package/lib/extensions/responseArea.js +278 -0
- package/lib/index.js +1213 -0
- package/lib/old-index.js +269 -0
- package/lib/parse-html.js +16 -0
- package/lib/plugins/characters/custom-popper.js +73 -0
- package/lib/plugins/characters/index.js +305 -0
- package/lib/plugins/characters/utils.js +381 -0
- package/lib/plugins/css/icons/index.js +37 -0
- package/lib/plugins/css/index.js +390 -0
- package/lib/plugins/customPlugin/index.js +114 -0
- package/lib/plugins/html/icons/index.js +38 -0
- package/lib/plugins/html/index.js +81 -0
- package/lib/plugins/image/__tests__/component.test.js +51 -0
- package/lib/plugins/image/__tests__/image-toolbar-logic.test.js +56 -0
- package/lib/plugins/image/__tests__/image-toolbar.test.js +26 -0
- package/lib/plugins/image/__tests__/index.test.js +98 -0
- package/lib/plugins/image/__tests__/insert-image-handler.test.js +125 -0
- package/lib/plugins/image/__tests__/mock-change.js +25 -0
- package/lib/plugins/image/alt-dialog.js +129 -0
- package/lib/plugins/image/component.js +419 -0
- package/lib/plugins/image/image-toolbar.js +177 -0
- package/lib/plugins/image/index.js +263 -0
- package/lib/plugins/image/insert-image-handler.js +117 -0
- package/lib/plugins/index.js +413 -0
- package/lib/plugins/list/__tests__/index.test.js +79 -0
- package/lib/plugins/list/index.js +334 -0
- package/lib/plugins/math/__tests__/index.test.js +300 -0
- package/lib/plugins/math/index.js +454 -0
- package/lib/plugins/media/__tests__/index.test.js +71 -0
- package/lib/plugins/media/index.js +387 -0
- package/lib/plugins/media/media-dialog.js +709 -0
- package/lib/plugins/media/media-toolbar.js +101 -0
- package/lib/plugins/media/media-wrapper.js +93 -0
- package/lib/plugins/rendering/index.js +46 -0
- package/lib/plugins/respArea/drag-in-the-blank/choice.js +289 -0
- package/lib/plugins/respArea/drag-in-the-blank/index.js +94 -0
- package/lib/plugins/respArea/explicit-constructed-response/index.js +120 -0
- package/lib/plugins/respArea/icons/index.js +95 -0
- package/lib/plugins/respArea/index.js +341 -0
- package/lib/plugins/respArea/inline-dropdown/index.js +126 -0
- package/lib/plugins/respArea/math-templated/index.js +130 -0
- package/lib/plugins/respArea/utils.js +125 -0
- package/lib/plugins/table/CustomTablePlugin.js +133 -0
- package/lib/plugins/table/__tests__/index.test.js +442 -0
- package/lib/plugins/table/__tests__/table-toolbar.test.js +54 -0
- package/lib/plugins/table/icons/index.js +69 -0
- package/lib/plugins/table/index.js +483 -0
- package/lib/plugins/table/table-toolbar.js +187 -0
- package/lib/plugins/textAlign/icons/index.js +194 -0
- package/lib/plugins/textAlign/index.js +34 -0
- package/lib/plugins/toolbar/__tests__/default-toolbar.test.js +128 -0
- package/lib/plugins/toolbar/__tests__/editor-and-toolbar.test.js +51 -0
- package/lib/plugins/toolbar/__tests__/toolbar-buttons.test.js +54 -0
- package/lib/plugins/toolbar/__tests__/toolbar.test.js +120 -0
- package/lib/plugins/toolbar/default-toolbar.js +229 -0
- package/lib/plugins/toolbar/done-button.js +53 -0
- package/lib/plugins/toolbar/editor-and-toolbar.js +286 -0
- package/lib/plugins/toolbar/index.js +34 -0
- package/lib/plugins/toolbar/toolbar-buttons.js +194 -0
- package/lib/plugins/toolbar/toolbar.js +376 -0
- package/lib/plugins/utils.js +62 -0
- package/lib/serialization.js +677 -0
- package/lib/shared/alert-dialog.js +75 -0
- package/lib/theme.js +9 -0
- package/package.json +69 -0
- package/src/__tests__/editor.test.jsx +363 -0
- package/src/__tests__/serialization.test.js +291 -0
- package/src/__tests__/utils.js +36 -0
- package/src/block-tags.js +17 -0
- package/src/constants.js +7 -0
- package/src/editor.jsx +1197 -0
- package/src/extensions/characters.js +46 -0
- package/src/extensions/component.jsx +294 -0
- package/src/extensions/css.js +217 -0
- package/src/extensions/custom-toolbar-wrapper.jsx +100 -0
- package/src/extensions/image.js +55 -0
- package/src/extensions/math.js +259 -0
- package/src/extensions/media.js +182 -0
- package/src/extensions/responseArea.js +205 -0
- package/src/index.jsx +1462 -0
- package/src/old-index.jsx +162 -0
- package/src/parse-html.js +8 -0
- package/src/plugins/README.md +27 -0
- package/src/plugins/characters/custom-popper.js +48 -0
- package/src/plugins/characters/index.jsx +284 -0
- package/src/plugins/characters/utils.js +447 -0
- package/src/plugins/css/icons/index.jsx +17 -0
- package/src/plugins/css/index.jsx +340 -0
- package/src/plugins/customPlugin/index.jsx +85 -0
- package/src/plugins/html/icons/index.jsx +19 -0
- package/src/plugins/html/index.jsx +72 -0
- package/src/plugins/image/__tests__/__snapshots__/component.test.jsx.snap +51 -0
- package/src/plugins/image/__tests__/__snapshots__/image-toolbar-logic.test.jsx.snap +27 -0
- package/src/plugins/image/__tests__/__snapshots__/image-toolbar.test.jsx.snap +44 -0
- package/src/plugins/image/__tests__/component.test.jsx +41 -0
- package/src/plugins/image/__tests__/image-toolbar-logic.test.jsx +42 -0
- package/src/plugins/image/__tests__/image-toolbar.test.jsx +11 -0
- package/src/plugins/image/__tests__/index.test.js +95 -0
- package/src/plugins/image/__tests__/insert-image-handler.test.js +113 -0
- package/src/plugins/image/__tests__/mock-change.js +15 -0
- package/src/plugins/image/alt-dialog.jsx +82 -0
- package/src/plugins/image/component.jsx +343 -0
- package/src/plugins/image/image-toolbar.jsx +100 -0
- package/src/plugins/image/index.jsx +227 -0
- package/src/plugins/image/insert-image-handler.js +79 -0
- package/src/plugins/index.jsx +377 -0
- package/src/plugins/list/__tests__/index.test.js +54 -0
- package/src/plugins/list/index.jsx +305 -0
- package/src/plugins/math/__tests__/__snapshots__/index.test.jsx.snap +48 -0
- package/src/plugins/math/__tests__/index.test.jsx +245 -0
- package/src/plugins/math/index.jsx +379 -0
- package/src/plugins/media/__tests__/index.test.js +75 -0
- package/src/plugins/media/index.jsx +325 -0
- package/src/plugins/media/media-dialog.js +624 -0
- package/src/plugins/media/media-toolbar.jsx +56 -0
- package/src/plugins/media/media-wrapper.jsx +43 -0
- package/src/plugins/rendering/index.js +31 -0
- package/src/plugins/respArea/drag-in-the-blank/choice.jsx +215 -0
- package/src/plugins/respArea/drag-in-the-blank/index.jsx +70 -0
- package/src/plugins/respArea/explicit-constructed-response/index.jsx +92 -0
- package/src/plugins/respArea/icons/index.jsx +71 -0
- package/src/plugins/respArea/index.jsx +299 -0
- package/src/plugins/respArea/inline-dropdown/index.jsx +108 -0
- package/src/plugins/respArea/math-templated/index.jsx +104 -0
- package/src/plugins/respArea/utils.jsx +90 -0
- package/src/plugins/table/CustomTablePlugin.js +113 -0
- package/src/plugins/table/__tests__/__snapshots__/table-toolbar.test.jsx.snap +44 -0
- package/src/plugins/table/__tests__/index.test.jsx +401 -0
- package/src/plugins/table/__tests__/table-toolbar.test.jsx +42 -0
- package/src/plugins/table/icons/index.jsx +53 -0
- package/src/plugins/table/index.jsx +427 -0
- package/src/plugins/table/table-toolbar.jsx +136 -0
- package/src/plugins/textAlign/icons/index.jsx +114 -0
- package/src/plugins/textAlign/index.jsx +23 -0
- package/src/plugins/toolbar/__tests__/__snapshots__/default-toolbar.test.jsx.snap +923 -0
- package/src/plugins/toolbar/__tests__/__snapshots__/editor-and-toolbar.test.jsx.snap +20 -0
- package/src/plugins/toolbar/__tests__/__snapshots__/toolbar-buttons.test.jsx.snap +36 -0
- package/src/plugins/toolbar/__tests__/__snapshots__/toolbar.test.jsx.snap +46 -0
- package/src/plugins/toolbar/__tests__/default-toolbar.test.jsx +94 -0
- package/src/plugins/toolbar/__tests__/editor-and-toolbar.test.jsx +37 -0
- package/src/plugins/toolbar/__tests__/toolbar-buttons.test.jsx +51 -0
- package/src/plugins/toolbar/__tests__/toolbar.test.jsx +106 -0
- package/src/plugins/toolbar/default-toolbar.jsx +206 -0
- package/src/plugins/toolbar/done-button.jsx +38 -0
- package/src/plugins/toolbar/editor-and-toolbar.jsx +257 -0
- package/src/plugins/toolbar/index.jsx +23 -0
- package/src/plugins/toolbar/toolbar-buttons.jsx +138 -0
- package/src/plugins/toolbar/toolbar.jsx +338 -0
- package/src/plugins/utils.js +31 -0
- package/src/serialization.jsx +621 -0
- package/src/theme.js +1 -0
|
@@ -0,0 +1,427 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Block } from 'slate';
|
|
3
|
+
import debug from 'debug';
|
|
4
|
+
import GridOn from '@material-ui/icons/GridOn';
|
|
5
|
+
import TableToolbar from './table-toolbar';
|
|
6
|
+
import PropTypes from 'prop-types';
|
|
7
|
+
import SlatePropTypes from 'slate-prop-types';
|
|
8
|
+
import { withStyles } from '@material-ui/core/styles';
|
|
9
|
+
import convert from 'react-attr-converter';
|
|
10
|
+
import { object as toStyleObject } from 'to-style';
|
|
11
|
+
import CustomTablePlugin from './CustomTablePlugin';
|
|
12
|
+
|
|
13
|
+
const log = debug('@pie-lib:editable-html:plugins:table');
|
|
14
|
+
|
|
15
|
+
const Table = withStyles(() => ({
|
|
16
|
+
table: {},
|
|
17
|
+
}))((props) => {
|
|
18
|
+
const nodeAttributes = dataToAttributes(props.node.data);
|
|
19
|
+
|
|
20
|
+
return (
|
|
21
|
+
<table
|
|
22
|
+
className={props.classes.table}
|
|
23
|
+
{...props.attributes}
|
|
24
|
+
{...nodeAttributes}
|
|
25
|
+
onFocus={props.onFocus}
|
|
26
|
+
onBlur={props.onBlur}
|
|
27
|
+
>
|
|
28
|
+
<tbody>{props.children}</tbody>
|
|
29
|
+
</table>
|
|
30
|
+
);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
Table.propTypes = {
|
|
34
|
+
attributes: PropTypes.object,
|
|
35
|
+
onFocus: PropTypes.func,
|
|
36
|
+
onBlur: PropTypes.func,
|
|
37
|
+
node: SlatePropTypes.node,
|
|
38
|
+
children: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.node), PropTypes.node]).isRequired,
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
const TableRow = (props) => <tr {...props.attributes}>{props.children}</tr>;
|
|
42
|
+
|
|
43
|
+
TableRow.propTypes = {
|
|
44
|
+
attributes: PropTypes.object,
|
|
45
|
+
onFocus: PropTypes.func,
|
|
46
|
+
onBlur: PropTypes.func,
|
|
47
|
+
children: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.node), PropTypes.node]).isRequired,
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
const TableCell = withStyles(() => ({
|
|
51
|
+
td: {
|
|
52
|
+
minWidth: '25px',
|
|
53
|
+
},
|
|
54
|
+
}))((props) => {
|
|
55
|
+
const Tag = props.node.data.get('header') ? 'th' : 'td';
|
|
56
|
+
|
|
57
|
+
const nodeAttributes = dataToAttributes(props.node.data);
|
|
58
|
+
delete nodeAttributes.header;
|
|
59
|
+
|
|
60
|
+
return (
|
|
61
|
+
<Tag
|
|
62
|
+
{...props.attributes}
|
|
63
|
+
{...nodeAttributes}
|
|
64
|
+
colSpan={props.node.data.get('colspan')}
|
|
65
|
+
className={props.classes[Tag]}
|
|
66
|
+
onFocus={props.onFocus}
|
|
67
|
+
onBlur={props.onBlur}
|
|
68
|
+
>
|
|
69
|
+
{props.children}
|
|
70
|
+
</Tag>
|
|
71
|
+
);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
TableCell.propTypes = {
|
|
75
|
+
attributes: PropTypes.object,
|
|
76
|
+
onFocus: PropTypes.func,
|
|
77
|
+
onBlur: PropTypes.func,
|
|
78
|
+
children: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.node), PropTypes.node]).isRequired,
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
export const moveFocusToBeginningOfTable = (change) => {
|
|
82
|
+
const addedTable = change.value.document.findDescendant((d) => !!d.data && !!d.data.get('newTable'));
|
|
83
|
+
|
|
84
|
+
if (!addedTable) {
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
change.collapseToStartOf(addedTable);
|
|
89
|
+
|
|
90
|
+
const update = addedTable.data.remove('newTable');
|
|
91
|
+
|
|
92
|
+
change.setNodeByKey(addedTable.key, { data: update });
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
export default (opts, toolbarPlugins /* : {toolbar: {}}[] */) => {
|
|
96
|
+
const core = CustomTablePlugin(opts);
|
|
97
|
+
|
|
98
|
+
// fix outdated schema
|
|
99
|
+
|
|
100
|
+
if (core.schema && core.schema.blocks) {
|
|
101
|
+
Object.keys(core.schema.blocks).forEach((key) => {
|
|
102
|
+
const block = core.schema.blocks[key];
|
|
103
|
+
|
|
104
|
+
if (block.parent) {
|
|
105
|
+
if (block.nodes[0].types) {
|
|
106
|
+
block.nodes[0] = {
|
|
107
|
+
type: block.nodes[0].types[0],
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if (block.nodes[0].objects) {
|
|
112
|
+
block.nodes[0] = {
|
|
113
|
+
object: block.nodes[0].objects[0],
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
block.parent = {
|
|
118
|
+
type: block.parent.types[0],
|
|
119
|
+
};
|
|
120
|
+
} else {
|
|
121
|
+
block.nodes[0] = { type: block.nodes[0].types[0] };
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
core.utils.getTableBlock = (containerNode, key) => {
|
|
127
|
+
const node = containerNode.getDescendant(key);
|
|
128
|
+
const ancestors = containerNode.getAncestors(key).push(node);
|
|
129
|
+
return ancestors.findLast((p) => p.type === 'table');
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
core.utils.createTableWithOptions = (row, columns, extra) => {
|
|
133
|
+
const createdTable = core.utils.createTable(row, columns);
|
|
134
|
+
const newTable = Block.create({
|
|
135
|
+
...createdTable.toJSON(),
|
|
136
|
+
...extra,
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
return newTable;
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
core.toolbar = {
|
|
143
|
+
type: 'table',
|
|
144
|
+
icon: <GridOn />,
|
|
145
|
+
ariaLabel: 'Insert Table',
|
|
146
|
+
onClick: (value, onChange) => {
|
|
147
|
+
log('insert table');
|
|
148
|
+
const change = value.change();
|
|
149
|
+
const newTable = core.utils.createTableWithOptions(2, 2, {
|
|
150
|
+
data: {
|
|
151
|
+
border: '1',
|
|
152
|
+
newTable: true,
|
|
153
|
+
},
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
change.insertBlock(newTable);
|
|
157
|
+
|
|
158
|
+
moveFocusToBeginningOfTable(change);
|
|
159
|
+
onChange(change);
|
|
160
|
+
},
|
|
161
|
+
supports: (node, value) => node && node.object === 'block' && core.utils.isSelectionInTable(value),
|
|
162
|
+
/**
|
|
163
|
+
* Note - the node may not be a table node - it may be a node inside a table.
|
|
164
|
+
*/
|
|
165
|
+
customToolbar: (node, value, onToolbarDone, getFocusedValue) => {
|
|
166
|
+
log('[customToolbar] node.data: ', node.data);
|
|
167
|
+
|
|
168
|
+
const tableBlock = core.utils.getTableBlock(value.document, node?.key);
|
|
169
|
+
log('[customToolbar] tableBlock: ', tableBlock);
|
|
170
|
+
|
|
171
|
+
const hasBorder = () => tableBlock.data.get('border') && tableBlock.data.get('border') !== '0';
|
|
172
|
+
const addRow = () => {
|
|
173
|
+
const change = core.changes.insertRow(value.change());
|
|
174
|
+
onToolbarDone(change, false);
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
const addColumn = () => {
|
|
178
|
+
const change = core.changes.insertColumn(value.change());
|
|
179
|
+
onToolbarDone(change, false);
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
const removeRow = () => {
|
|
183
|
+
const change = core.changes.removeRow(value.change());
|
|
184
|
+
onToolbarDone(change, false);
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
const removeColumn = () => {
|
|
188
|
+
const change = core.changes.removeColumn(value.change());
|
|
189
|
+
onToolbarDone(change, false);
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
const removeTable = () => {
|
|
193
|
+
const change = core.changes.removeTable(value.change());
|
|
194
|
+
onToolbarDone(change, false);
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
const toggleBorder = () => {
|
|
198
|
+
const { data } = tableBlock;
|
|
199
|
+
const update = data.set('border', hasBorder() ? '0' : '1');
|
|
200
|
+
log('[toggleBorder] update: ', update);
|
|
201
|
+
const change = value.change().setNodeByKey(tableBlock.key, { data: update });
|
|
202
|
+
onToolbarDone(change, false);
|
|
203
|
+
};
|
|
204
|
+
|
|
205
|
+
const onDone = () => {
|
|
206
|
+
log('[onDone] call onToolbarDone...');
|
|
207
|
+
onToolbarDone(null, true);
|
|
208
|
+
};
|
|
209
|
+
|
|
210
|
+
const Tb = () => (
|
|
211
|
+
<TableToolbar
|
|
212
|
+
getFocusedValue={getFocusedValue}
|
|
213
|
+
plugins={toolbarPlugins}
|
|
214
|
+
onChange={(c) => onToolbarDone(c, false)}
|
|
215
|
+
value={value}
|
|
216
|
+
onAddRow={addRow}
|
|
217
|
+
onRemoveRow={removeRow}
|
|
218
|
+
onAddColumn={addColumn}
|
|
219
|
+
onRemoveColumn={removeColumn}
|
|
220
|
+
onRemoveTable={removeTable}
|
|
221
|
+
hasBorder={hasBorder()}
|
|
222
|
+
onToggleBorder={toggleBorder}
|
|
223
|
+
onDone={onDone}
|
|
224
|
+
/>
|
|
225
|
+
);
|
|
226
|
+
|
|
227
|
+
return Tb;
|
|
228
|
+
},
|
|
229
|
+
};
|
|
230
|
+
|
|
231
|
+
const Node = (props) => {
|
|
232
|
+
switch (props.node.type) {
|
|
233
|
+
case 'table':
|
|
234
|
+
return <Table {...props} onFocus={opts.onFocus} onBlur={opts.onBlur} />;
|
|
235
|
+
case 'table_row':
|
|
236
|
+
return <TableRow {...props} />;
|
|
237
|
+
case 'table_cell':
|
|
238
|
+
return <TableCell {...props} onFocus={opts.onFocus} onBlur={opts.onBlur} />;
|
|
239
|
+
default:
|
|
240
|
+
return null;
|
|
241
|
+
}
|
|
242
|
+
};
|
|
243
|
+
Node.propTypes = {
|
|
244
|
+
node: PropTypes.object,
|
|
245
|
+
};
|
|
246
|
+
|
|
247
|
+
core.normalizeNode = (node) => {
|
|
248
|
+
const addNodeBeforeArray = [];
|
|
249
|
+
|
|
250
|
+
if (node.object !== 'document') return;
|
|
251
|
+
|
|
252
|
+
node.findDescendant((d) => {
|
|
253
|
+
if (d.type === 'table') {
|
|
254
|
+
const tablePath = node.getPath(d.key);
|
|
255
|
+
const prevNode = node.getPreviousNode(tablePath);
|
|
256
|
+
const nextNode = node.getNextNode(tablePath);
|
|
257
|
+
|
|
258
|
+
if (!prevNode || !nextNode) {
|
|
259
|
+
addNodeBeforeArray.push({
|
|
260
|
+
node: d,
|
|
261
|
+
prevNode,
|
|
262
|
+
nextNode,
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
if (!addNodeBeforeArray.length) {
|
|
269
|
+
return;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
return (change) => {
|
|
273
|
+
const newBlock = {
|
|
274
|
+
object: 'block',
|
|
275
|
+
type: 'div',
|
|
276
|
+
};
|
|
277
|
+
|
|
278
|
+
addNodeBeforeArray.forEach((n) => {
|
|
279
|
+
const tablePath = change.value.document.getPath(n.node.key).toJSON();
|
|
280
|
+
// removing tableIndex
|
|
281
|
+
let indexToAdd = tablePath.splice(-1)[0];
|
|
282
|
+
|
|
283
|
+
if (!n.prevNode) {
|
|
284
|
+
// inserting block key before table
|
|
285
|
+
change.insertNodeByPath(tablePath, indexToAdd, newBlock);
|
|
286
|
+
// this will trigger another normalization, which will figure out if there's not
|
|
287
|
+
// a block after the table and add it, so we exit for now
|
|
288
|
+
return;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
if (!n.nextNode) {
|
|
292
|
+
// inserting block key after table
|
|
293
|
+
change.insertNodeByPath(tablePath, indexToAdd + 1, newBlock);
|
|
294
|
+
}
|
|
295
|
+
});
|
|
296
|
+
};
|
|
297
|
+
};
|
|
298
|
+
|
|
299
|
+
core.renderNode = Node;
|
|
300
|
+
core.name = 'table';
|
|
301
|
+
|
|
302
|
+
return core;
|
|
303
|
+
};
|
|
304
|
+
|
|
305
|
+
export const parseStyleString = (s) => {
|
|
306
|
+
const regex = /([\w-]*)\s*:\s*([^;]*)/g;
|
|
307
|
+
let match;
|
|
308
|
+
const result = {};
|
|
309
|
+
while ((match = regex.exec(s))) {
|
|
310
|
+
result[match[1]] = match[2].trim();
|
|
311
|
+
}
|
|
312
|
+
return result;
|
|
313
|
+
};
|
|
314
|
+
|
|
315
|
+
export const reactAttributes = (o) => toStyleObject(o, { camelize: true, addUnits: false });
|
|
316
|
+
|
|
317
|
+
const attributesToMap = (el) => (acc, attribute) => {
|
|
318
|
+
const value = el.getAttribute(attribute);
|
|
319
|
+
if (value) {
|
|
320
|
+
if (attribute === 'style') {
|
|
321
|
+
const styleString = el.getAttribute(attribute);
|
|
322
|
+
const reactStyleObject = reactAttributes(parseStyleString(styleString));
|
|
323
|
+
acc['style'] = reactStyleObject;
|
|
324
|
+
} else {
|
|
325
|
+
acc[attribute] = el.getAttribute(attribute);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
return acc;
|
|
329
|
+
};
|
|
330
|
+
|
|
331
|
+
const dataToAttributes = (data) => {
|
|
332
|
+
if (!data || !data.get) {
|
|
333
|
+
return {};
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
return data.reduce((acc, v, name) => {
|
|
337
|
+
if (v) {
|
|
338
|
+
acc[convert(name)] = v;
|
|
339
|
+
}
|
|
340
|
+
return acc;
|
|
341
|
+
}, {});
|
|
342
|
+
};
|
|
343
|
+
|
|
344
|
+
const attributes = ['border', 'cellpadding', 'cellspacing', 'class', 'style'];
|
|
345
|
+
|
|
346
|
+
const cellAttributes = ['colspan', 'rowspan', 'class', 'style'];
|
|
347
|
+
|
|
348
|
+
export const serialization = {
|
|
349
|
+
deserialize(el, next) {
|
|
350
|
+
const tag = el.tagName.toLowerCase();
|
|
351
|
+
|
|
352
|
+
switch (tag) {
|
|
353
|
+
case 'table': {
|
|
354
|
+
const children =
|
|
355
|
+
el.children.length === 1 && el.children[0].tagName.toLowerCase() === 'tbody'
|
|
356
|
+
? el.children[0].children
|
|
357
|
+
: el.children;
|
|
358
|
+
const c = Array.from(children);
|
|
359
|
+
|
|
360
|
+
return {
|
|
361
|
+
object: 'block',
|
|
362
|
+
type: 'table',
|
|
363
|
+
nodes: next(c),
|
|
364
|
+
data: attributes.reduce(attributesToMap(el), {}),
|
|
365
|
+
};
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
case 'th': {
|
|
369
|
+
return {
|
|
370
|
+
object: 'block',
|
|
371
|
+
type: 'table_cell',
|
|
372
|
+
nodes: next(el.childNodes),
|
|
373
|
+
data: cellAttributes.reduce(attributesToMap(el), { header: true }),
|
|
374
|
+
};
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
case 'tr': {
|
|
378
|
+
return {
|
|
379
|
+
object: 'block',
|
|
380
|
+
type: 'table_row',
|
|
381
|
+
nodes: next(Array.from(el.children)),
|
|
382
|
+
};
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
case 'td': {
|
|
386
|
+
return {
|
|
387
|
+
object: 'block',
|
|
388
|
+
type: 'table_cell',
|
|
389
|
+
nodes: next(Array.from(el.childNodes)),
|
|
390
|
+
data: cellAttributes.reduce(attributesToMap(el), { header: false }),
|
|
391
|
+
};
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
},
|
|
395
|
+
serialize(object, children) {
|
|
396
|
+
if (object.object !== 'block') {
|
|
397
|
+
return;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
switch (object.type) {
|
|
401
|
+
case 'table': {
|
|
402
|
+
const attributes = dataToAttributes(object.data);
|
|
403
|
+
|
|
404
|
+
return (
|
|
405
|
+
<table {...attributes}>
|
|
406
|
+
<tbody>{children}</tbody>
|
|
407
|
+
</table>
|
|
408
|
+
);
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
case 'table_row': {
|
|
412
|
+
return <tr>{children}</tr>;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
case 'table_cell': {
|
|
416
|
+
const attributes = dataToAttributes(object.data);
|
|
417
|
+
delete attributes.header;
|
|
418
|
+
|
|
419
|
+
if (object.data.get('header')) {
|
|
420
|
+
return <th {...attributes}>{children}</th>;
|
|
421
|
+
} else {
|
|
422
|
+
return <td {...attributes}>{children}</td>;
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
},
|
|
427
|
+
};
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { withStyles } from '@material-ui/core/styles';
|
|
3
|
+
import { Button } from '../toolbar/toolbar-buttons';
|
|
4
|
+
import { DoneButton } from '../toolbar/done-button';
|
|
5
|
+
import BorderAll from '@material-ui/icons/BorderAll';
|
|
6
|
+
import { ToolbarButton } from '../toolbar/default-toolbar';
|
|
7
|
+
|
|
8
|
+
import { AddRow, AddColumn, RemoveColumn, RemoveRow, RemoveTable } from './icons';
|
|
9
|
+
import PropTypes from 'prop-types';
|
|
10
|
+
import debug from 'debug';
|
|
11
|
+
|
|
12
|
+
const log = debug('@pie-lib:editable-html:plugins:table-toolbar');
|
|
13
|
+
|
|
14
|
+
export class TableToolbar extends React.Component {
|
|
15
|
+
static propTypes = {
|
|
16
|
+
plugins: PropTypes.array.isRequired,
|
|
17
|
+
value: PropTypes.object.isRequired,
|
|
18
|
+
onChange: PropTypes.func.isRequired,
|
|
19
|
+
onAddRow: PropTypes.func.isRequired,
|
|
20
|
+
onRemoveRow: PropTypes.func.isRequired,
|
|
21
|
+
onAddColumn: PropTypes.func.isRequired,
|
|
22
|
+
onRemoveColumn: PropTypes.func.isRequired,
|
|
23
|
+
onRemoveTable: PropTypes.func.isRequired,
|
|
24
|
+
onToggleBorder: PropTypes.func.isRequired,
|
|
25
|
+
hasBorder: PropTypes.bool,
|
|
26
|
+
onDone: PropTypes.func.isRequired,
|
|
27
|
+
classes: PropTypes.object.isRequired,
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
static defaultProps = {
|
|
31
|
+
plugins: [],
|
|
32
|
+
value: {},
|
|
33
|
+
onChange: () => {},
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
onDone = (e) => {
|
|
37
|
+
const { onDone } = this.props;
|
|
38
|
+
e.preventDefault();
|
|
39
|
+
onDone();
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
render() {
|
|
43
|
+
const {
|
|
44
|
+
getFocusedValue,
|
|
45
|
+
plugins,
|
|
46
|
+
value,
|
|
47
|
+
onChange,
|
|
48
|
+
onAddRow,
|
|
49
|
+
onRemoveRow,
|
|
50
|
+
onAddColumn,
|
|
51
|
+
onRemoveColumn,
|
|
52
|
+
onRemoveTable,
|
|
53
|
+
onToggleBorder,
|
|
54
|
+
hasBorder,
|
|
55
|
+
classes,
|
|
56
|
+
} = this.props;
|
|
57
|
+
log('[render] hasBorder:', hasBorder);
|
|
58
|
+
|
|
59
|
+
// we're separating the response area plugin because we want to display it next to the done button
|
|
60
|
+
const filteredPlugins = (plugins || []).reduce(
|
|
61
|
+
(acc, plugin) => {
|
|
62
|
+
if (plugin.name === 'response_area') {
|
|
63
|
+
return {
|
|
64
|
+
...acc,
|
|
65
|
+
respAreaPlugin: plugin,
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return {
|
|
70
|
+
...acc,
|
|
71
|
+
otherPlugins: [...acc.otherPlugins, plugin],
|
|
72
|
+
};
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
respAreaPlugin: null,
|
|
76
|
+
otherPlugins: [],
|
|
77
|
+
},
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
return (
|
|
81
|
+
<div className={classes.tableToolbar}>
|
|
82
|
+
<div className={classes.toolbarButtons}>
|
|
83
|
+
<Button onClick={onAddRow}>
|
|
84
|
+
<AddRow />
|
|
85
|
+
</Button>
|
|
86
|
+
<Button onClick={onRemoveRow}>
|
|
87
|
+
<RemoveRow />
|
|
88
|
+
</Button>
|
|
89
|
+
<Button onClick={onAddColumn}>
|
|
90
|
+
<AddColumn />
|
|
91
|
+
</Button>
|
|
92
|
+
<Button onClick={onRemoveColumn}>
|
|
93
|
+
<RemoveColumn />
|
|
94
|
+
</Button>
|
|
95
|
+
<Button onClick={onRemoveTable}>
|
|
96
|
+
<RemoveTable />
|
|
97
|
+
</Button>
|
|
98
|
+
{(filteredPlugins.otherPlugins || []).map((p, index) => (
|
|
99
|
+
<ToolbarButton
|
|
100
|
+
key={`plugin-${index}`}
|
|
101
|
+
{...p.toolbar}
|
|
102
|
+
value={value}
|
|
103
|
+
onChange={onChange}
|
|
104
|
+
getFocusedValue={getFocusedValue}
|
|
105
|
+
/>
|
|
106
|
+
))}
|
|
107
|
+
<Button onClick={onToggleBorder} active={hasBorder}>
|
|
108
|
+
<BorderAll />
|
|
109
|
+
</Button>
|
|
110
|
+
</div>
|
|
111
|
+
{filteredPlugins.respAreaPlugin && (
|
|
112
|
+
<ToolbarButton
|
|
113
|
+
key={'plugin-response-area'}
|
|
114
|
+
{...filteredPlugins.respAreaPlugin.toolbar}
|
|
115
|
+
value={value}
|
|
116
|
+
onChange={onChange}
|
|
117
|
+
getFocusedValue={getFocusedValue}
|
|
118
|
+
/>
|
|
119
|
+
)}
|
|
120
|
+
<DoneButton onClick={this.onDone} />
|
|
121
|
+
</div>
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const styles = () => ({
|
|
127
|
+
tableToolbar: {
|
|
128
|
+
width: '100%',
|
|
129
|
+
display: 'flex',
|
|
130
|
+
justifyContent: 'space-between',
|
|
131
|
+
},
|
|
132
|
+
toolbarButtons: {
|
|
133
|
+
display: 'flex',
|
|
134
|
+
},
|
|
135
|
+
});
|
|
136
|
+
export default withStyles(styles)(TableToolbar);
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import React, { useState } from 'react';
|
|
2
|
+
import Collapse from '@material-ui/core/Collapse';
|
|
3
|
+
import List from '@material-ui/core/List';
|
|
4
|
+
import ListItem from '@material-ui/core/ListItem';
|
|
5
|
+
|
|
6
|
+
export const AlignLeft = () => (
|
|
7
|
+
<svg width="20" height="20" viewBox="0 0 66 66" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
8
|
+
<path
|
|
9
|
+
d="M42.1875 4.75C42.1875 7.38672 39.9902 9.4375 37.5 9.4375H4.6875C2.05078 9.4375 0 7.38672 0 4.75C0 2.25977 2.05078 0.0625 4.6875 0.0625H37.5C39.9902 0.0625 42.1875 2.25977 42.1875 4.75ZM42.1875 42.25C42.1875 44.8867 39.9902 46.9375 37.5 46.9375H4.6875C2.05078 46.9375 0 44.8867 0 42.25C0 39.7598 2.05078 37.5625 4.6875 37.5625H37.5C39.9902 37.5625 42.1875 39.7598 42.1875 42.25ZM0 23.5C0 21.0098 2.05078 18.8125 4.6875 18.8125H60.9375C63.4277 18.8125 65.625 21.0098 65.625 23.5C65.625 26.1367 63.4277 28.1875 60.9375 28.1875H4.6875C2.05078 28.1875 0 26.1367 0 23.5ZM65.625 61C65.625 63.6367 63.4277 65.6875 60.9375 65.6875H4.6875C2.05078 65.6875 0 63.6367 0 61C0 58.5098 2.05078 56.3125 4.6875 56.3125H60.9375C63.4277 56.3125 65.625 58.5098 65.625 61Z"
|
|
10
|
+
fill="currentColor"
|
|
11
|
+
/>
|
|
12
|
+
</svg>
|
|
13
|
+
);
|
|
14
|
+
|
|
15
|
+
export const AlignRight = () => (
|
|
16
|
+
<svg width="20" height="20" viewBox="0 0 66 66" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
17
|
+
<path
|
|
18
|
+
d="M65.625 4.75C65.625 7.38672 63.4277 9.4375 60.9375 9.4375H28.125C25.4883 9.4375 23.4375 7.38672 23.4375 4.75C23.4375 2.25977 25.4883 0.0625 28.125 0.0625H60.9375C63.4277 0.0625 65.625 2.25977 65.625 4.75ZM65.625 42.25C65.625 44.8867 63.4277 46.9375 60.9375 46.9375H28.125C25.4883 46.9375 23.4375 44.8867 23.4375 42.25C23.4375 39.7598 25.4883 37.5625 28.125 37.5625H60.9375C63.4277 37.5625 65.625 39.7598 65.625 42.25ZM0 23.5C0 21.0098 2.05078 18.8125 4.6875 18.8125H60.9375C63.4277 18.8125 65.625 21.0098 65.625 23.5C65.625 26.1367 63.4277 28.1875 60.9375 28.1875H4.6875C2.05078 28.1875 0 26.1367 0 23.5ZM65.625 61C65.625 63.6367 63.4277 65.6875 60.9375 65.6875H4.6875C2.05078 65.6875 0 63.6367 0 61C0 58.5098 2.05078 56.3125 4.6875 56.3125H60.9375C63.4277 56.3125 65.625 58.5098 65.625 61Z"
|
|
19
|
+
fill="currentColor"
|
|
20
|
+
/>
|
|
21
|
+
</svg>
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
export const AlignCenter = () => (
|
|
25
|
+
<svg width="20" height="20" viewBox="0 0 66 66" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
26
|
+
<path
|
|
27
|
+
d="M51.5625 4.75C51.5625 7.38672 49.3652 9.4375 46.875 9.4375H18.75C16.1133 9.4375 14.0625 7.38672 14.0625 4.75C14.0625 2.25977 16.1133 0.0625 18.75 0.0625H46.875C49.3652 0.0625 51.5625 2.25977 51.5625 4.75ZM65.625 23.5C65.625 26.1367 63.4277 28.1875 60.9375 28.1875H4.6875C2.05078 28.1875 0 26.1367 0 23.5C0 21.0098 2.05078 18.8125 4.6875 18.8125H60.9375C63.4277 18.8125 65.625 21.0098 65.625 23.5ZM0 61C0 58.5098 2.05078 56.3125 4.6875 56.3125H60.9375C63.4277 56.3125 65.625 58.5098 65.625 61C65.625 63.6367 63.4277 65.6875 60.9375 65.6875H4.6875C2.05078 65.6875 0 63.6367 0 61ZM51.5625 42.25C51.5625 44.8867 49.3652 46.9375 46.875 46.9375H18.75C16.1133 46.9375 14.0625 44.8867 14.0625 42.25C14.0625 39.7598 16.1133 37.5625 18.75 37.5625H46.875C49.3652 37.5625 51.5625 39.7598 51.5625 42.25Z"
|
|
28
|
+
fill="currentColor"
|
|
29
|
+
/>
|
|
30
|
+
</svg>
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
export const AlignJustify = () => (
|
|
34
|
+
<svg width="20" height="20" viewBox="0 0 66 66" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
35
|
+
<path
|
|
36
|
+
d="M65.625 4.75C65.625 7.38672 63.4277 9.4375 60.9375 9.4375H4.6875C2.05078 9.4375 0 7.38672 0 4.75C0 2.25977 2.05078 0.0625 4.6875 0.0625H60.9375C63.4277 0.0625 65.625 2.25977 65.625 4.75ZM65.625 42.25C65.625 44.8867 63.4277 46.9375 60.9375 46.9375H4.6875C2.05078 46.9375 0 44.8867 0 42.25C0 39.7598 2.05078 37.5625 4.6875 37.5625H60.9375C63.4277 37.5625 65.625 39.7598 65.625 42.25ZM0 23.5C0 21.0098 2.05078 18.8125 4.6875 18.8125H60.9375C63.4277 18.8125 65.625 21.0098 65.625 23.5C65.625 26.1367 63.4277 28.1875 60.9375 28.1875H4.6875C2.05078 28.1875 0 26.1367 0 23.5ZM65.625 61C65.625 63.6367 63.4277 65.6875 60.9375 65.6875H4.6875C2.05078 65.6875 0 63.6367 0 61C0 58.5098 2.05078 56.3125 4.6875 56.3125H60.9375C63.4277 56.3125 65.625 58.5098 65.625 61Z"
|
|
37
|
+
fill="currentColor"
|
|
38
|
+
/>
|
|
39
|
+
</svg>
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
export default ({ editor, onChange }) => {
|
|
43
|
+
const [open, setOpen] = useState(false);
|
|
44
|
+
|
|
45
|
+
let icon;
|
|
46
|
+
|
|
47
|
+
switch (true) {
|
|
48
|
+
case editor.isActive({ textAlign: 'right' }):
|
|
49
|
+
icon = <AlignRight />;
|
|
50
|
+
break;
|
|
51
|
+
case editor.isActive({ textAlign: 'center' }):
|
|
52
|
+
icon = <AlignCenter />;
|
|
53
|
+
break;
|
|
54
|
+
case editor.isActive({ textAlign: 'justify' }):
|
|
55
|
+
icon = <AlignJustify />;
|
|
56
|
+
break;
|
|
57
|
+
default:
|
|
58
|
+
icon = <AlignLeft />;
|
|
59
|
+
break;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const applyAlignment = (event) => {
|
|
63
|
+
const alignType = event.target?.closest('div')?.getAttribute('value');
|
|
64
|
+
|
|
65
|
+
if (alignType) {
|
|
66
|
+
editor.commands.setTextAlign(alignType);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
setOpen(false);
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
const onMouseDown = (event) => {
|
|
73
|
+
event.preventDefault();
|
|
74
|
+
event.stopPropagation();
|
|
75
|
+
setOpen(!open);
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
return (
|
|
79
|
+
<div>
|
|
80
|
+
<div style={{ display: 'flex', alignItems: 'center' }} onClick={onMouseDown}>
|
|
81
|
+
{icon}
|
|
82
|
+
<span style={{ marginLeft: '5px', fontSize: '8px' }}>▼</span>
|
|
83
|
+
</div>
|
|
84
|
+
<Collapse in={open} timeout="auto" unmountOnExit style={{ position: 'absolute' }}>
|
|
85
|
+
<List
|
|
86
|
+
component="div"
|
|
87
|
+
disablePadding
|
|
88
|
+
style={{
|
|
89
|
+
background: '#fff',
|
|
90
|
+
display: 'flex',
|
|
91
|
+
flexDirection: 'row',
|
|
92
|
+
padding: 0,
|
|
93
|
+
}}
|
|
94
|
+
>
|
|
95
|
+
<ListItem button type="submit" value="left" aria-label="Align text left" onClick={applyAlignment}>
|
|
96
|
+
<AlignLeft />
|
|
97
|
+
</ListItem>
|
|
98
|
+
|
|
99
|
+
<ListItem button type="submit" value="center" aria-label="Align text center" onClick={applyAlignment}>
|
|
100
|
+
<AlignCenter />
|
|
101
|
+
</ListItem>
|
|
102
|
+
|
|
103
|
+
<ListItem button type="submit" value="right" aria-label="Align text right" onClick={applyAlignment}>
|
|
104
|
+
<AlignRight />
|
|
105
|
+
</ListItem>
|
|
106
|
+
|
|
107
|
+
<ListItem button type="submit" value="justify" aria-label="Justify text" onClick={applyAlignment}>
|
|
108
|
+
<AlignJustify />
|
|
109
|
+
</ListItem>
|
|
110
|
+
</List>
|
|
111
|
+
</Collapse>
|
|
112
|
+
</div>
|
|
113
|
+
);
|
|
114
|
+
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import debug from 'debug';
|
|
3
|
+
import TextAlignIcon from './icons';
|
|
4
|
+
|
|
5
|
+
const log = debug('@pie-lib:editable-html:plugins:characters');
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Plugin in order to be able to change alignment for the selected text(s) element(s).
|
|
9
|
+
* @param opts
|
|
10
|
+
* @constructor
|
|
11
|
+
*/
|
|
12
|
+
export default function TextAlign(opts) {
|
|
13
|
+
const plugin = {
|
|
14
|
+
name: 'textAlign',
|
|
15
|
+
toolbar: {
|
|
16
|
+
icon: <TextAlignIcon {...opts} />,
|
|
17
|
+
ariaLabel: 'Text Align',
|
|
18
|
+
onClick: () => {},
|
|
19
|
+
},
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
return plugin;
|
|
23
|
+
}
|