@pie-lib/editable-html 7.17.4-next.42 → 7.17.4-next.423
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 +165 -0
- package/CHANGELOG.md +286 -0
- package/lib/editor.js +348 -179
- package/lib/editor.js.map +1 -1
- package/lib/index.js +63 -52
- package/lib/index.js.map +1 -1
- package/lib/parse-html.js.map +1 -1
- package/lib/plugins/characters/custom-popover.js +70 -0
- package/lib/plugins/characters/custom-popover.js.map +1 -0
- package/lib/plugins/characters/index.js +266 -0
- package/lib/plugins/characters/index.js.map +1 -0
- package/lib/plugins/characters/utils.js +382 -0
- package/lib/plugins/characters/utils.js.map +1 -0
- package/lib/plugins/image/component.js +251 -77
- package/lib/plugins/image/component.js.map +1 -1
- package/lib/plugins/image/image-toolbar.js +49 -63
- package/lib/plugins/image/image-toolbar.js.map +1 -1
- package/lib/plugins/image/index.js +56 -19
- package/lib/plugins/image/index.js.map +1 -1
- package/lib/plugins/image/insert-image-handler.js +9 -15
- package/lib/plugins/image/insert-image-handler.js.map +1 -1
- package/lib/plugins/index.js +20 -12
- package/lib/plugins/index.js.map +1 -1
- package/lib/plugins/list/index.js +82 -14
- package/lib/plugins/list/index.js.map +1 -1
- package/lib/plugins/math/index.js +50 -55
- package/lib/plugins/math/index.js.map +1 -1
- package/lib/plugins/media/index.js +26 -25
- package/lib/plugins/media/index.js.map +1 -1
- package/lib/plugins/media/media-dialog.js +45 -56
- package/lib/plugins/media/media-dialog.js.map +1 -1
- package/lib/plugins/media/media-toolbar.js +24 -30
- package/lib/plugins/media/media-toolbar.js.map +1 -1
- package/lib/plugins/media/media-wrapper.js +28 -35
- package/lib/plugins/media/media-wrapper.js.map +1 -1
- package/lib/plugins/respArea/drag-in-the-blank/choice.js +68 -46
- package/lib/plugins/respArea/drag-in-the-blank/choice.js.map +1 -1
- package/lib/plugins/respArea/drag-in-the-blank/index.js +12 -12
- package/lib/plugins/respArea/drag-in-the-blank/index.js.map +1 -1
- package/lib/plugins/respArea/explicit-constructed-response/index.js +10 -9
- package/lib/plugins/respArea/explicit-constructed-response/index.js.map +1 -1
- package/lib/plugins/respArea/icons/index.js +11 -11
- package/lib/plugins/respArea/icons/index.js.map +1 -1
- package/lib/plugins/respArea/index.js +58 -42
- package/lib/plugins/respArea/index.js.map +1 -1
- package/lib/plugins/respArea/inline-dropdown/index.js +8 -8
- package/lib/plugins/respArea/inline-dropdown/index.js.map +1 -1
- package/lib/plugins/respArea/utils.js +5 -5
- package/lib/plugins/respArea/utils.js.map +1 -1
- package/lib/plugins/table/icons/index.js +12 -12
- package/lib/plugins/table/icons/index.js.map +1 -1
- package/lib/plugins/table/index.js +83 -27
- package/lib/plugins/table/index.js.map +1 -1
- package/lib/plugins/table/table-toolbar.js +41 -50
- package/lib/plugins/table/table-toolbar.js.map +1 -1
- package/lib/plugins/toolbar/default-toolbar.js +14 -11
- package/lib/plugins/toolbar/default-toolbar.js.map +1 -1
- package/lib/plugins/toolbar/done-button.js +5 -5
- package/lib/plugins/toolbar/done-button.js.map +1 -1
- package/lib/plugins/toolbar/editor-and-toolbar.js +43 -43
- package/lib/plugins/toolbar/editor-and-toolbar.js.map +1 -1
- package/lib/plugins/toolbar/index.js +5 -5
- package/lib/plugins/toolbar/index.js.map +1 -1
- package/lib/plugins/toolbar/toolbar-buttons.js +49 -52
- package/lib/plugins/toolbar/toolbar-buttons.js.map +1 -1
- package/lib/plugins/toolbar/toolbar.js +60 -64
- package/lib/plugins/toolbar/toolbar.js.map +1 -1
- package/lib/plugins/utils.js +1 -1
- package/lib/plugins/utils.js.map +1 -1
- package/lib/serialization.js +32 -9
- package/lib/serialization.js.map +1 -1
- package/lib/theme.js.map +1 -1
- package/package.json +5 -5
- package/src/editor.jsx +189 -31
- package/src/index.jsx +20 -3
- package/src/plugins/characters/custom-popover.js +45 -0
- package/src/plugins/characters/index.jsx +244 -0
- package/src/plugins/characters/utils.js +448 -0
- package/src/plugins/image/component.jsx +202 -21
- package/src/plugins/image/image-toolbar.jsx +25 -20
- package/src/plugins/image/index.jsx +40 -9
- package/src/plugins/index.jsx +4 -1
- package/src/plugins/list/index.jsx +67 -5
- package/src/plugins/math/index.jsx +31 -37
- package/src/plugins/media/index.jsx +3 -0
- package/src/plugins/media/media-dialog.js +1 -1
- package/src/plugins/respArea/drag-in-the-blank/choice.jsx +28 -1
- package/src/plugins/respArea/explicit-constructed-response/index.jsx +3 -3
- package/src/plugins/respArea/index.jsx +51 -31
- package/src/plugins/table/index.jsx +61 -14
- package/src/plugins/toolbar/default-toolbar.jsx +8 -0
- package/src/plugins/toolbar/editor-and-toolbar.jsx +12 -4
- package/src/plugins/toolbar/toolbar-buttons.jsx +13 -2
- package/src/plugins/toolbar/toolbar.jsx +14 -4
- package/src/serialization.jsx +19 -3
|
@@ -20,14 +20,17 @@ const removeDialogs = () => {
|
|
|
20
20
|
export const insertDialog = props => {
|
|
21
21
|
const newEl = document.createElement('div');
|
|
22
22
|
const { type, callback, ...rest } = props;
|
|
23
|
+
const initialBodyOverflow = document.body.style.overflow;
|
|
23
24
|
|
|
24
25
|
removeDialogs();
|
|
25
26
|
|
|
26
27
|
newEl.className = 'insert-media-dialog';
|
|
28
|
+
document.body.style.overflow = 'hidden';
|
|
27
29
|
|
|
28
30
|
const handleClose = (val, data) => {
|
|
29
31
|
callback(val, data);
|
|
30
32
|
newEl.remove();
|
|
33
|
+
document.body.style.overflow = initialBodyOverflow;
|
|
31
34
|
};
|
|
32
35
|
|
|
33
36
|
const el = (
|
|
@@ -348,7 +348,7 @@ export class MediaDialog extends React.Component {
|
|
|
348
348
|
Cancel
|
|
349
349
|
</Button>
|
|
350
350
|
<Button
|
|
351
|
-
disabled={invalid || url === null}
|
|
351
|
+
disabled={invalid || url === null || url === undefined}
|
|
352
352
|
onClick={() => this.handleDone(true)}
|
|
353
353
|
color="primary"
|
|
354
354
|
>
|
|
@@ -2,6 +2,7 @@ import React from 'react';
|
|
|
2
2
|
import PropTypes from 'prop-types';
|
|
3
3
|
import isUndefined from 'lodash/isUndefined';
|
|
4
4
|
import { DragSource, DropTarget } from '@pie-lib/drag';
|
|
5
|
+
import { color } from '@pie-lib/render-ui';
|
|
5
6
|
import { renderMath } from '@pie-lib/math-rendering';
|
|
6
7
|
import { withStyles } from '@material-ui/core/styles';
|
|
7
8
|
import classnames from 'classnames';
|
|
@@ -20,6 +21,9 @@ const useStyles = withStyles(theme => ({
|
|
|
20
21
|
},
|
|
21
22
|
incorrect: {
|
|
22
23
|
border: 'solid 1px red'
|
|
24
|
+
},
|
|
25
|
+
selected: {
|
|
26
|
+
border: `2px solid ${color.primaryDark()} !important`
|
|
23
27
|
}
|
|
24
28
|
}));
|
|
25
29
|
|
|
@@ -30,9 +34,32 @@ export class BlankContent extends React.Component {
|
|
|
30
34
|
isDragging: PropTypes.bool,
|
|
31
35
|
isOver: PropTypes.bool,
|
|
32
36
|
dragItem: PropTypes.object,
|
|
33
|
-
value: PropTypes.object
|
|
37
|
+
value: PropTypes.object,
|
|
38
|
+
classes: PropTypes.object
|
|
34
39
|
};
|
|
35
40
|
|
|
41
|
+
constructor(props) {
|
|
42
|
+
super(props);
|
|
43
|
+
|
|
44
|
+
this.handleClick = this.handleClick.bind(this);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
componentDidMount() {
|
|
48
|
+
document.addEventListener('click', this.handleClick);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
componentWillUnmount() {
|
|
52
|
+
document.removeEventListener('click', this.handleClick);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
handleClick(event) {
|
|
56
|
+
const { classes } = this.props;
|
|
57
|
+
|
|
58
|
+
if (this.elementRef) {
|
|
59
|
+
this.elementRef.className = this.elementRef.contains(event.target) ? classes.selected : '';
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
36
63
|
componentDidUpdate() {
|
|
37
64
|
if (this.elementRef) {
|
|
38
65
|
renderMath(this.elementRef);
|
|
@@ -2,7 +2,7 @@ import React from 'react';
|
|
|
2
2
|
import PropTypes from 'prop-types';
|
|
3
3
|
|
|
4
4
|
const ExplicitConstructedResponse = props => {
|
|
5
|
-
const { attributes, value } = props;
|
|
5
|
+
const { attributes, value, error } = props;
|
|
6
6
|
|
|
7
7
|
return (
|
|
8
8
|
<span
|
|
@@ -23,7 +23,7 @@ const ExplicitConstructedResponse = props => {
|
|
|
23
23
|
minHeight: '36px',
|
|
24
24
|
height: '36px',
|
|
25
25
|
background: '#FFF',
|
|
26
|
-
border:
|
|
26
|
+
border: `1px solid ${error ? 'red' : '#C0C3CF'}`,
|
|
27
27
|
boxSizing: 'border-box',
|
|
28
28
|
borderRadius: '3px',
|
|
29
29
|
overflow: 'hidden',
|
|
@@ -39,7 +39,7 @@ const ExplicitConstructedResponse = props => {
|
|
|
39
39
|
|
|
40
40
|
ExplicitConstructedResponse.propTypes = {
|
|
41
41
|
attributes: PropTypes.object,
|
|
42
|
-
value: PropTypes.
|
|
42
|
+
value: PropTypes.string
|
|
43
43
|
};
|
|
44
44
|
|
|
45
45
|
export default ExplicitConstructedResponse;
|
|
@@ -11,9 +11,16 @@ import { ToolbarIcon } from './icons';
|
|
|
11
11
|
const log = debug('@pie-lib:editable-html:plugins:respArea');
|
|
12
12
|
|
|
13
13
|
const lastIndexMap = {};
|
|
14
|
-
const
|
|
14
|
+
const elTypesMap = {
|
|
15
|
+
'inline-dropdown': 'inline_dropdown',
|
|
16
|
+
'explicit-constructed-response': 'explicit_constructed_response',
|
|
17
|
+
'drag-in-the-blank': 'drag_in_the_blank'
|
|
18
|
+
};
|
|
19
|
+
const elTypesArray = Object.values(elTypesMap);
|
|
15
20
|
|
|
16
21
|
export default function ResponseAreaPlugin(opts) {
|
|
22
|
+
const isOfCurrentType = d => d.type === opts.type || d.type === elTypesMap[opts.type];
|
|
23
|
+
|
|
17
24
|
const toolbar = {
|
|
18
25
|
icon: <ToolbarIcon />,
|
|
19
26
|
buttonStyles: {
|
|
@@ -22,6 +29,12 @@ export default function ResponseAreaPlugin(opts) {
|
|
|
22
29
|
onClick: (value, onChange) => {
|
|
23
30
|
log('[toolbar] onClick');
|
|
24
31
|
const change = value.change();
|
|
32
|
+
const currentRespAreaList = change.value.document.filterDescendants(isOfCurrentType);
|
|
33
|
+
|
|
34
|
+
if (currentRespAreaList.size >= opts.maxResponseAreas) {
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
|
|
25
38
|
const type = opts.type.replace(/-/g, '_');
|
|
26
39
|
const prevIndex = lastIndexMap[type];
|
|
27
40
|
const newIndex = prevIndex === 0 ? prevIndex : prevIndex + 1;
|
|
@@ -35,6 +48,11 @@ export default function ResponseAreaPlugin(opts) {
|
|
|
35
48
|
} else {
|
|
36
49
|
// If the markup is empty and there's no focus
|
|
37
50
|
const lastText = value.document.getLastText();
|
|
51
|
+
|
|
52
|
+
if (!lastText) {
|
|
53
|
+
return;
|
|
54
|
+
|
|
55
|
+
}
|
|
38
56
|
const parentNode = value.document.getParent(lastText.key);
|
|
39
57
|
|
|
40
58
|
if (parentNode) {
|
|
@@ -66,7 +84,7 @@ export default function ResponseAreaPlugin(opts) {
|
|
|
66
84
|
name: 'response_area',
|
|
67
85
|
toolbar,
|
|
68
86
|
filterPlugins: (node, plugins) => {
|
|
69
|
-
if (node.type === 'explicit_constructed_response') {
|
|
87
|
+
if (node.type === 'explicit_constructed_response' || node.type === 'drag_in_the_blank') {
|
|
70
88
|
return [];
|
|
71
89
|
}
|
|
72
90
|
|
|
@@ -84,8 +102,19 @@ export default function ResponseAreaPlugin(opts) {
|
|
|
84
102
|
|
|
85
103
|
if (n.type === 'explicit_constructed_response') {
|
|
86
104
|
const data = n.data.toJSON();
|
|
105
|
+
let error;
|
|
106
|
+
|
|
107
|
+
if (opts.error) {
|
|
108
|
+
error = opts.error();
|
|
109
|
+
}
|
|
87
110
|
|
|
88
|
-
return
|
|
111
|
+
return (
|
|
112
|
+
<ExplicitConstructedResponse
|
|
113
|
+
attributes={attributes}
|
|
114
|
+
value={data.value}
|
|
115
|
+
error={error && error[data.index] && error[data.index][0]}
|
|
116
|
+
/>
|
|
117
|
+
);
|
|
89
118
|
}
|
|
90
119
|
|
|
91
120
|
if (n.type === 'drag_in_the_blank') {
|
|
@@ -102,7 +131,7 @@ export default function ResponseAreaPlugin(opts) {
|
|
|
102
131
|
return <InlineDropdown attributes={attributes} selectedItem={data.value} />;
|
|
103
132
|
}
|
|
104
133
|
},
|
|
105
|
-
onChange(change) {
|
|
134
|
+
onChange(change, editor) {
|
|
106
135
|
const type = opts.type.replace(/-/g, '_');
|
|
107
136
|
|
|
108
137
|
if (isUndefined(lastIndexMap[type])) {
|
|
@@ -118,41 +147,32 @@ export default function ResponseAreaPlugin(opts) {
|
|
|
118
147
|
}
|
|
119
148
|
});
|
|
120
149
|
}
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
if (node.object !== 'document') {
|
|
150
|
+
|
|
151
|
+
if (!editor.value) {
|
|
124
152
|
return;
|
|
125
153
|
}
|
|
126
154
|
|
|
127
|
-
const
|
|
155
|
+
const currentRespAreaList = change.value.document.filterDescendants(isOfCurrentType);
|
|
156
|
+
const oldRespAreaList = editor.value.document.filterDescendants(isOfCurrentType);
|
|
128
157
|
|
|
129
|
-
|
|
158
|
+
if (currentRespAreaList.size >= opts.maxResponseAreas) {
|
|
159
|
+
toolbar.disabled = true;
|
|
160
|
+
} else {
|
|
161
|
+
toolbar.disabled = false;
|
|
162
|
+
}
|
|
130
163
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
164
|
+
const arrayToFilter =
|
|
165
|
+
oldRespAreaList.size > currentRespAreaList.size ? oldRespAreaList : currentRespAreaList;
|
|
166
|
+
const arrayToUseForFilter =
|
|
167
|
+
arrayToFilter === oldRespAreaList ? currentRespAreaList : oldRespAreaList;
|
|
134
168
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
key: prevText.key
|
|
139
|
-
});
|
|
140
|
-
}
|
|
141
|
-
});
|
|
169
|
+
const elementsWithChangedStatus = arrayToFilter.filter(
|
|
170
|
+
d => !arrayToUseForFilter.find(e => e.data.get('index') === d.data.get('index'))
|
|
171
|
+
);
|
|
142
172
|
|
|
143
|
-
if (
|
|
144
|
-
|
|
173
|
+
if (elementsWithChangedStatus.size && oldRespAreaList.size > currentRespAreaList.size) {
|
|
174
|
+
opts.onHandleAreaChange(elementsWithChangedStatus);
|
|
145
175
|
}
|
|
146
|
-
|
|
147
|
-
return change => {
|
|
148
|
-
change.withoutNormalization(() => {
|
|
149
|
-
addSpacesArray.forEach(({ key, nr }) => {
|
|
150
|
-
const node = change.value.document.getNode(key);
|
|
151
|
-
|
|
152
|
-
change.insertTextByKey(key, node.text.length, '\u00A0'.repeat(nr));
|
|
153
|
-
});
|
|
154
|
-
});
|
|
155
|
-
};
|
|
156
176
|
},
|
|
157
177
|
onDrop(event, change, editor) {
|
|
158
178
|
const closestEl = event.target.closest('[data-key]');
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import EditTable from 'slate-edit-table';
|
|
3
|
-
import { Block
|
|
3
|
+
import { Block } from 'slate';
|
|
4
4
|
import debug from 'debug';
|
|
5
5
|
import GridOn from '@material-ui/icons/GridOn';
|
|
6
6
|
import TableToolbar from './table-toolbar';
|
|
@@ -78,6 +78,20 @@ TableCell.propTypes = {
|
|
|
78
78
|
children: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.node), PropTypes.node]).isRequired
|
|
79
79
|
};
|
|
80
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
|
+
|
|
81
95
|
export default (opts, toolbarPlugins /* : {toolbar: {}}[] */) => {
|
|
82
96
|
const core = EditTable({
|
|
83
97
|
typeContent: 'div'
|
|
@@ -117,12 +131,32 @@ export default (opts, toolbarPlugins /* : {toolbar: {}}[] */) => {
|
|
|
117
131
|
return ancestors.findLast(p => p.type === 'table');
|
|
118
132
|
};
|
|
119
133
|
|
|
134
|
+
core.utils.createTableWithOptions = (row, columns, extra) => {
|
|
135
|
+
const createdTable = core.utils.createTable(row, columns);
|
|
136
|
+
const newTable = Block.create({
|
|
137
|
+
...createdTable.toJSON(),
|
|
138
|
+
...extra
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
return newTable;
|
|
142
|
+
};
|
|
143
|
+
|
|
120
144
|
core.toolbar = {
|
|
121
145
|
icon: <GridOn />,
|
|
122
146
|
onClick: (value, onChange) => {
|
|
123
147
|
log('insert table');
|
|
124
|
-
const
|
|
125
|
-
|
|
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);
|
|
126
160
|
},
|
|
127
161
|
supports: (node, value) =>
|
|
128
162
|
node && node.object === 'block' && core.utils.isSelectionInTable(value),
|
|
@@ -132,7 +166,7 @@ export default (opts, toolbarPlugins /* : {toolbar: {}}[] */) => {
|
|
|
132
166
|
customToolbar: (node, value, onToolbarDone) => {
|
|
133
167
|
log('[customToolbar] node.data: ', node.data);
|
|
134
168
|
|
|
135
|
-
const tableBlock = core.utils.getTableBlock(value.document, node
|
|
169
|
+
const tableBlock = core.utils.getTableBlock(value.document, node?.key);
|
|
136
170
|
log('[customToolbar] tableBlock: ', tableBlock);
|
|
137
171
|
|
|
138
172
|
const hasBorder = () =>
|
|
@@ -215,9 +249,16 @@ export default (opts, toolbarPlugins /* : {toolbar: {}}[] */) => {
|
|
|
215
249
|
return;
|
|
216
250
|
}
|
|
217
251
|
|
|
252
|
+
const tableAdded = node.findDescendant(d => d.data && d.data.get('newTable'));
|
|
253
|
+
|
|
254
|
+
if (!tableAdded) {
|
|
255
|
+
return;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
const nodeToSearch = node.getParent(tableAdded.key) || node;
|
|
218
259
|
let shouldAddTextAfterNode = false;
|
|
219
|
-
const indexToNotHaveTableOn =
|
|
220
|
-
const indexOfLastTable =
|
|
260
|
+
const indexToNotHaveTableOn = nodeToSearch.nodes.size - 1;
|
|
261
|
+
const indexOfLastTable = nodeToSearch.nodes.findLastIndex(d => d.type === 'table');
|
|
221
262
|
|
|
222
263
|
// if the last table in the document is of type table, we need to do the change
|
|
223
264
|
if (indexOfLastTable === indexToNotHaveTableOn) {
|
|
@@ -228,15 +269,13 @@ export default (opts, toolbarPlugins /* : {toolbar: {}}[] */) => {
|
|
|
228
269
|
return;
|
|
229
270
|
}
|
|
230
271
|
|
|
231
|
-
const tableNode = node.nodes.get(indexOfLastTable);
|
|
232
|
-
|
|
233
272
|
return change => {
|
|
234
273
|
if (shouldAddTextAfterNode) {
|
|
235
|
-
const tableJSON =
|
|
274
|
+
const tableJSON = tableAdded.toJSON();
|
|
236
275
|
|
|
237
276
|
// we remove the table node because otherwise we can't add the empty block after it
|
|
238
277
|
// we need a block that contains text in order to do it
|
|
239
|
-
change.removeNodeByKey(
|
|
278
|
+
change.removeNodeByKey(tableAdded.key);
|
|
240
279
|
|
|
241
280
|
const newBlock = Block.create({
|
|
242
281
|
object: 'block',
|
|
@@ -256,12 +295,20 @@ export default (opts, toolbarPlugins /* : {toolbar: {}}[] */) => {
|
|
|
256
295
|
if (prevText) {
|
|
257
296
|
// we move focus to the previous text
|
|
258
297
|
change
|
|
259
|
-
.moveFocusTo(prevText.key, prevText.text
|
|
260
|
-
.moveAnchorTo(prevText.key, prevText.text
|
|
298
|
+
.moveFocusTo(prevText.key, prevText.text?.length)
|
|
299
|
+
.moveAnchorTo(prevText.key, prevText.text?.length);
|
|
261
300
|
}
|
|
262
301
|
|
|
263
302
|
// we insert the table block between the first block with text and the last block with text
|
|
264
|
-
change.insertBlock(
|
|
303
|
+
change.insertBlock({
|
|
304
|
+
...tableJSON,
|
|
305
|
+
data: {
|
|
306
|
+
...tableJSON.data,
|
|
307
|
+
newTable: true
|
|
308
|
+
}
|
|
309
|
+
});
|
|
310
|
+
|
|
311
|
+
moveFocusToBeginningOfTable(change);
|
|
265
312
|
});
|
|
266
313
|
}
|
|
267
314
|
};
|
|
@@ -282,7 +329,7 @@ export const parseStyleString = s => {
|
|
|
282
329
|
return result;
|
|
283
330
|
};
|
|
284
331
|
|
|
285
|
-
export const reactAttributes = o => toStyleObject(o, { camelize: true });
|
|
332
|
+
export const reactAttributes = o => toStyleObject(o, { camelize: true, addUnits: false });
|
|
286
333
|
|
|
287
334
|
const attributesToMap = el => (acc, attribute) => {
|
|
288
335
|
const value = el.getAttribute(attribute);
|
|
@@ -14,25 +14,32 @@ const log = debug('@pie-lib:editable-html:plugins:toolbar');
|
|
|
14
14
|
export const ToolbarButton = props => {
|
|
15
15
|
const onToggle = () => {
|
|
16
16
|
const c = props.onToggle(props.value.change(), props);
|
|
17
|
+
|
|
17
18
|
props.onChange(c);
|
|
18
19
|
};
|
|
19
20
|
|
|
20
21
|
if (props.isMark) {
|
|
21
22
|
const isActive = hasMark(props.value, props.type);
|
|
23
|
+
|
|
22
24
|
log('[ToolbarButton] mark:isActive: ', isActive);
|
|
25
|
+
|
|
23
26
|
return (
|
|
24
27
|
<MarkButton active={isActive} label={props.type} onToggle={onToggle} mark={props.type}>
|
|
25
28
|
{props.icon}
|
|
26
29
|
</MarkButton>
|
|
27
30
|
);
|
|
28
31
|
} else {
|
|
32
|
+
const { disabled } = props;
|
|
29
33
|
const isActive = props.isActive
|
|
30
34
|
? props.isActive(props.value, props.type)
|
|
31
35
|
: hasBlock(props.value, props.type);
|
|
36
|
+
|
|
32
37
|
log('[ToolbarButton] block:isActive: ', isActive);
|
|
38
|
+
|
|
33
39
|
return (
|
|
34
40
|
<Button
|
|
35
41
|
active={isActive}
|
|
42
|
+
disabled={disabled}
|
|
36
43
|
onClick={() => props.onClick(props.value, props.onChange)}
|
|
37
44
|
extraStyles={props.buttonStyles}
|
|
38
45
|
>
|
|
@@ -44,6 +51,7 @@ export const ToolbarButton = props => {
|
|
|
44
51
|
|
|
45
52
|
const isActiveToolbarPlugin = props => plugin => {
|
|
46
53
|
const isDisabled = (props[plugin.name] || {}).disabled;
|
|
54
|
+
|
|
47
55
|
return plugin && plugin.toolbar && !isDisabled;
|
|
48
56
|
};
|
|
49
57
|
|
|
@@ -19,6 +19,7 @@ export class EditorAndToolbar extends React.Component {
|
|
|
19
19
|
onChange: PropTypes.func.isRequired,
|
|
20
20
|
onDone: PropTypes.func.isRequired,
|
|
21
21
|
onDataChange: PropTypes.func,
|
|
22
|
+
toolbarRef: PropTypes.func,
|
|
22
23
|
focusedNode: SlatePropTypes.node,
|
|
23
24
|
readOnly: PropTypes.bool,
|
|
24
25
|
disableUnderline: PropTypes.bool,
|
|
@@ -27,7 +28,8 @@ export class EditorAndToolbar extends React.Component {
|
|
|
27
28
|
pluginProps: PropTypes.object,
|
|
28
29
|
toolbarOpts: PropTypes.shape({
|
|
29
30
|
position: PropTypes.oneOf(['bottom', 'top']),
|
|
30
|
-
alwaysVisible: PropTypes.bool
|
|
31
|
+
alwaysVisible: PropTypes.bool,
|
|
32
|
+
error: PropTypes.string
|
|
31
33
|
})
|
|
32
34
|
};
|
|
33
35
|
|
|
@@ -54,7 +56,8 @@ export class EditorAndToolbar extends React.Component {
|
|
|
54
56
|
disableUnderline,
|
|
55
57
|
pluginProps,
|
|
56
58
|
toolbarOpts,
|
|
57
|
-
onDataChange
|
|
59
|
+
onDataChange,
|
|
60
|
+
toolbarRef
|
|
58
61
|
} = this.props;
|
|
59
62
|
|
|
60
63
|
const inFocus = value.isFocused || (focusedNode !== null && focusedNode !== undefined);
|
|
@@ -82,7 +85,7 @@ export class EditorAndToolbar extends React.Component {
|
|
|
82
85
|
);
|
|
83
86
|
|
|
84
87
|
return (
|
|
85
|
-
<div className={classes.root}>
|
|
88
|
+
<div className={classNames(classes.root, toolbarOpts && toolbarOpts.error && classes.error)}>
|
|
86
89
|
<div className={holderNames}>
|
|
87
90
|
<div className={classes.children}>{clonedChildren}</div>
|
|
88
91
|
</div>
|
|
@@ -95,6 +98,7 @@ export class EditorAndToolbar extends React.Component {
|
|
|
95
98
|
onChange={onChange}
|
|
96
99
|
onDone={onDone}
|
|
97
100
|
onDataChange={onDataChange}
|
|
101
|
+
toolbarRef={toolbarRef}
|
|
98
102
|
pluginProps={pluginProps}
|
|
99
103
|
toolbarOpts={toolbarOpts}
|
|
100
104
|
/>
|
|
@@ -114,7 +118,8 @@ const style = {
|
|
|
114
118
|
wordBreak: 'break-word',
|
|
115
119
|
overflow: 'visible',
|
|
116
120
|
maxHeight: '500px',
|
|
117
|
-
|
|
121
|
+
// needed in order to be able to put the focus before a void element when it is the first one in the editor
|
|
122
|
+
padding: '5px'
|
|
118
123
|
}
|
|
119
124
|
},
|
|
120
125
|
children: {
|
|
@@ -211,6 +216,9 @@ const style = {
|
|
|
211
216
|
backgroundColor: primary
|
|
212
217
|
}
|
|
213
218
|
}
|
|
219
|
+
},
|
|
220
|
+
error: {
|
|
221
|
+
border: '2px solid red'
|
|
214
222
|
}
|
|
215
223
|
};
|
|
216
224
|
|
|
@@ -15,6 +15,13 @@ const styles = () => ({
|
|
|
15
15
|
},
|
|
16
16
|
active: {
|
|
17
17
|
color: 'black'
|
|
18
|
+
},
|
|
19
|
+
disabled: {
|
|
20
|
+
opacity: 0.7,
|
|
21
|
+
cursor: 'not-allowed',
|
|
22
|
+
'& :hover': {
|
|
23
|
+
color: 'grey'
|
|
24
|
+
}
|
|
18
25
|
}
|
|
19
26
|
});
|
|
20
27
|
|
|
@@ -26,6 +33,7 @@ export class RawButton extends React.Component {
|
|
|
26
33
|
classes: PropTypes.object.isRequired,
|
|
27
34
|
children: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.node), PropTypes.node]).isRequired,
|
|
28
35
|
active: PropTypes.bool,
|
|
36
|
+
disabled: PropTypes.bool,
|
|
29
37
|
extraStyles: PropTypes.object
|
|
30
38
|
};
|
|
31
39
|
|
|
@@ -41,8 +49,11 @@ export class RawButton extends React.Component {
|
|
|
41
49
|
};
|
|
42
50
|
|
|
43
51
|
render() {
|
|
44
|
-
const { active, classes, children, extraStyles } = this.props;
|
|
45
|
-
const names = classNames(classes.button,
|
|
52
|
+
const { active, classes, children, disabled, extraStyles } = this.props;
|
|
53
|
+
const names = classNames(classes.button, {
|
|
54
|
+
[classes.active]: active,
|
|
55
|
+
[classes.disabled]: disabled
|
|
56
|
+
});
|
|
46
57
|
|
|
47
58
|
return (
|
|
48
59
|
<div style={extraStyles} className={names} onMouseDown={this.onClick}>
|
|
@@ -1,13 +1,15 @@
|
|
|
1
|
-
import
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Change } from 'slate';
|
|
2
3
|
import Delete from '@material-ui/icons/Delete';
|
|
3
4
|
import IconButton from '@material-ui/core/IconButton';
|
|
4
5
|
import PropTypes from 'prop-types';
|
|
5
|
-
import React from 'react';
|
|
6
6
|
import classNames from 'classnames';
|
|
7
7
|
import debug from 'debug';
|
|
8
8
|
import SlatePropTypes from 'slate-prop-types';
|
|
9
9
|
import debounce from 'lodash/debounce';
|
|
10
10
|
|
|
11
|
+
import { DoneButton } from './done-button';
|
|
12
|
+
|
|
11
13
|
import { findSingleNode, findParentNode } from '../utils';
|
|
12
14
|
import { withStyles } from '@material-ui/core/styles';
|
|
13
15
|
import DefaultToolbar from './default-toolbar';
|
|
@@ -43,6 +45,7 @@ export class Toolbar extends React.Component {
|
|
|
43
45
|
plugin: PropTypes.object,
|
|
44
46
|
onImageClick: PropTypes.func,
|
|
45
47
|
onDone: PropTypes.func.isRequired,
|
|
48
|
+
toolbarRef: PropTypes.func.isRequired,
|
|
46
49
|
classes: PropTypes.object.isRequired,
|
|
47
50
|
isFocused: PropTypes.bool,
|
|
48
51
|
autoWidth: PropTypes.bool,
|
|
@@ -52,6 +55,7 @@ export class Toolbar extends React.Component {
|
|
|
52
55
|
position: PropTypes.oneOf(['bottom', 'top']),
|
|
53
56
|
alignment: PropTypes.oneOf(['left', 'right']),
|
|
54
57
|
alwaysVisible: PropTypes.bool,
|
|
58
|
+
ref: PropTypes.func,
|
|
55
59
|
showDone: PropTypes.bool
|
|
56
60
|
}),
|
|
57
61
|
onDataChange: PropTypes.func
|
|
@@ -133,7 +137,8 @@ export class Toolbar extends React.Component {
|
|
|
133
137
|
autoWidth,
|
|
134
138
|
onChange,
|
|
135
139
|
isFocused,
|
|
136
|
-
onDone
|
|
140
|
+
onDone,
|
|
141
|
+
toolbarRef
|
|
137
142
|
} = this.props;
|
|
138
143
|
|
|
139
144
|
const node = findSingleNode(value);
|
|
@@ -164,6 +169,11 @@ export class Toolbar extends React.Component {
|
|
|
164
169
|
log('[render] plugin: ', plugin);
|
|
165
170
|
|
|
166
171
|
const handleDone = (change, done) => {
|
|
172
|
+
// use handler only if this is an actual Slate Change
|
|
173
|
+
if (!(change instanceof Change)) {
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
|
|
167
177
|
let handler = onDone;
|
|
168
178
|
|
|
169
179
|
if (plugin && plugin.toolbar && plugin.toolbar.customToolbar) {
|
|
@@ -223,7 +233,7 @@ export class Toolbar extends React.Component {
|
|
|
223
233
|
});
|
|
224
234
|
|
|
225
235
|
return (
|
|
226
|
-
<div className={names} style={extraStyles} onClick={this.onClick}>
|
|
236
|
+
<div className={names} style={extraStyles} onClick={this.onClick} ref={toolbarRef}>
|
|
227
237
|
{CustomToolbar ? (
|
|
228
238
|
<CustomToolbar
|
|
229
239
|
node={node}
|
package/src/serialization.jsx
CHANGED
|
@@ -58,7 +58,16 @@ export const parseStyleString = s => {
|
|
|
58
58
|
return result;
|
|
59
59
|
};
|
|
60
60
|
|
|
61
|
-
export const
|
|
61
|
+
export const getBase64 = file => {
|
|
62
|
+
return new Promise((resolve, reject) => {
|
|
63
|
+
const reader = new FileReader();
|
|
64
|
+
reader.readAsDataURL(file);
|
|
65
|
+
reader.onload = () => resolve(reader.result);
|
|
66
|
+
reader.onerror = error => reject(error);
|
|
67
|
+
});
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
export const reactAttributes = o => toStyleObject(o, { camelize: true, addUnits: false });
|
|
62
71
|
|
|
63
72
|
const attributesToMap = el => (acc, attribute) => {
|
|
64
73
|
const value = el.getAttribute(attribute);
|
|
@@ -340,7 +349,7 @@ serializer.deserialize = function deserialize(html) {
|
|
|
340
349
|
|
|
341
350
|
let i;
|
|
342
351
|
|
|
343
|
-
for (i = 0; i <
|
|
352
|
+
for (i = 0; i < 3000; i++) {
|
|
344
353
|
json.schema.rules.push({
|
|
345
354
|
match: { object: 'document' },
|
|
346
355
|
nodes: [{ match: { object: 'block' } }]
|
|
@@ -356,7 +365,14 @@ serializer.deserialize = function deserialize(html) {
|
|
|
356
365
|
return null;
|
|
357
366
|
};
|
|
358
367
|
|
|
359
|
-
export const htmlToValue = html =>
|
|
368
|
+
export const htmlToValue = html => {
|
|
369
|
+
try {
|
|
370
|
+
return serializer.deserialize(html);
|
|
371
|
+
} catch (e) {
|
|
372
|
+
console.log("Couldn't parse html: ", e);
|
|
373
|
+
return {};
|
|
374
|
+
}
|
|
375
|
+
};
|
|
360
376
|
|
|
361
377
|
export const valueToHtml = value => serializer.serialize(value);
|
|
362
378
|
|