@pie-lib/editable-html 7.19.2 → 7.21.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.md +37 -0
- package/lib/editor.js +2 -0
- package/lib/editor.js.map +1 -1
- package/lib/index.js +7 -0
- package/lib/index.js.map +1 -1
- package/lib/plugins/index.js +3 -1
- package/lib/plugins/index.js.map +1 -1
- package/lib/plugins/list/index.js +73 -5
- package/lib/plugins/list/index.js.map +1 -1
- package/lib/plugins/media/index.js +3 -0
- package/lib/plugins/media/index.js.map +1 -1
- package/lib/plugins/respArea/explicit-constructed-response/index.js +3 -2
- package/lib/plugins/respArea/explicit-constructed-response/index.js.map +1 -1
- package/lib/plugins/respArea/index.js +19 -5
- package/lib/plugins/respArea/index.js.map +1 -1
- package/lib/plugins/toolbar/default-toolbar.js +3 -0
- package/lib/plugins/toolbar/default-toolbar.js.map +1 -1
- package/lib/plugins/toolbar/editor-and-toolbar.js +6 -2
- package/lib/plugins/toolbar/editor-and-toolbar.js.map +1 -1
- package/lib/plugins/toolbar/toolbar-buttons.js +12 -1
- package/lib/plugins/toolbar/toolbar-buttons.js.map +1 -1
- package/package.json +5 -5
- package/src/editor.jsx +2 -0
- package/src/index.jsx +6 -0
- package/src/plugins/index.jsx +1 -1
- package/src/plugins/list/index.jsx +67 -5
- package/src/plugins/media/index.jsx +3 -0
- package/src/plugins/respArea/explicit-constructed-response/index.jsx +2 -2
- package/src/plugins/respArea/index.jsx +21 -2
- package/src/plugins/toolbar/default-toolbar.jsx +8 -0
- package/src/plugins/toolbar/editor-and-toolbar.jsx +6 -2
- package/src/plugins/toolbar/toolbar-buttons.jsx +13 -2
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
+
import { Data } from 'slate';
|
|
3
|
+
import Immutable from 'immutable';
|
|
2
4
|
import PropTypes from 'prop-types';
|
|
3
5
|
import EditList from 'slate-edit-list';
|
|
4
6
|
import debug from 'debug';
|
|
@@ -44,15 +46,12 @@ export const serialization = {
|
|
|
44
46
|
}
|
|
45
47
|
};
|
|
46
48
|
|
|
47
|
-
|
|
48
|
-
const { type, icon } = options;
|
|
49
|
-
|
|
49
|
+
const createEditList = () => {
|
|
50
50
|
const core = EditList({
|
|
51
51
|
typeDefault: 'span'
|
|
52
52
|
});
|
|
53
53
|
|
|
54
54
|
// fix outdated schema
|
|
55
|
-
|
|
56
55
|
if (core.schema && core.schema.blocks) {
|
|
57
56
|
Object.keys(core.schema.blocks).forEach(key => {
|
|
58
57
|
const block = core.schema.blocks[key];
|
|
@@ -65,6 +64,69 @@ export default options => {
|
|
|
65
64
|
});
|
|
66
65
|
}
|
|
67
66
|
|
|
67
|
+
/**
|
|
68
|
+
* This override of the core.changes.wrapInList is needed because the version
|
|
69
|
+
* of immutable that we have does not support getting the element at a specific
|
|
70
|
+
* index with a square bracket (list[0]). We have to use the list.get function instead
|
|
71
|
+
*/
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Returns the highest list of blocks that cover the current selection
|
|
75
|
+
*/
|
|
76
|
+
const getHighestSelectedBlocks = value => {
|
|
77
|
+
const range = value.selection;
|
|
78
|
+
const document = value.document;
|
|
79
|
+
|
|
80
|
+
const startBlock = document.getClosestBlock(range.startKey);
|
|
81
|
+
const endBlock = document.getClosestBlock(range.endKey);
|
|
82
|
+
|
|
83
|
+
if (startBlock === endBlock) {
|
|
84
|
+
return Immutable.List([startBlock]);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const ancestor = document.getCommonAncestor(startBlock.key, endBlock.key);
|
|
88
|
+
const startPath = ancestor.getPath(startBlock.key);
|
|
89
|
+
const endPath = ancestor.getPath(endBlock.key);
|
|
90
|
+
|
|
91
|
+
return ancestor.nodes.slice(startPath.get(0), endPath.get(0) + 1);
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Wrap the blocks in the current selection in a new list. Selected
|
|
96
|
+
* lists are merged together.
|
|
97
|
+
*/
|
|
98
|
+
core.changes.wrapInList = function(change, type, data) {
|
|
99
|
+
const selectedBlocks = getHighestSelectedBlocks(change.value);
|
|
100
|
+
|
|
101
|
+
// Wrap in container
|
|
102
|
+
change.wrapBlock({ type: type, data: Data.create(data) }, { normalize: false });
|
|
103
|
+
|
|
104
|
+
// Wrap in list items
|
|
105
|
+
selectedBlocks.forEach(function(node) {
|
|
106
|
+
if (core.utils.isList(node)) {
|
|
107
|
+
// Merge its items with the created list
|
|
108
|
+
node.nodes.forEach(function(_ref) {
|
|
109
|
+
const key = _ref.key;
|
|
110
|
+
return change.unwrapNodeByKey(key, { normalize: false });
|
|
111
|
+
});
|
|
112
|
+
} else if (node.type !== 'list_item') {
|
|
113
|
+
change.wrapBlockByKey(node.key, 'list_item', {
|
|
114
|
+
normalize: false
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
return change.normalize();
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
return core;
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
export default options => {
|
|
126
|
+
const { type, icon } = options;
|
|
127
|
+
|
|
128
|
+
const core = createEditList();
|
|
129
|
+
|
|
68
130
|
// eslint-disable-next-line react/display-name
|
|
69
131
|
core.renderNode = props => {
|
|
70
132
|
const { node, attributes, children } = props;
|
|
@@ -88,7 +150,7 @@ export default options => {
|
|
|
88
150
|
return false;
|
|
89
151
|
}
|
|
90
152
|
const current = core.utils.getCurrentList(value);
|
|
91
|
-
return current.type === type;
|
|
153
|
+
return current ? current.type === type : false;
|
|
92
154
|
},
|
|
93
155
|
onClick: (value, onChange) => {
|
|
94
156
|
log('[onClick]', value);
|
|
@@ -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 = (
|
|
@@ -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',
|
|
@@ -19,6 +19,8 @@ const elTypesMap = {
|
|
|
19
19
|
const elTypesArray = Object.values(elTypesMap);
|
|
20
20
|
|
|
21
21
|
export default function ResponseAreaPlugin(opts) {
|
|
22
|
+
const isOfCurrentType = d => d.type === opts.type || d.type === elTypesMap[opts.type];
|
|
23
|
+
|
|
22
24
|
const toolbar = {
|
|
23
25
|
icon: <ToolbarIcon />,
|
|
24
26
|
buttonStyles: {
|
|
@@ -27,6 +29,12 @@ export default function ResponseAreaPlugin(opts) {
|
|
|
27
29
|
onClick: (value, onChange) => {
|
|
28
30
|
log('[toolbar] onClick');
|
|
29
31
|
const change = value.change();
|
|
32
|
+
const currentRespAreaList = change.value.document.filterDescendants(isOfCurrentType);
|
|
33
|
+
|
|
34
|
+
if (currentRespAreaList.size >= opts.maxResponseAreas) {
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
|
|
30
38
|
const type = opts.type.replace(/-/g, '_');
|
|
31
39
|
const prevIndex = lastIndexMap[type];
|
|
32
40
|
const newIndex = prevIndex === 0 ? prevIndex : prevIndex + 1;
|
|
@@ -90,7 +98,13 @@ export default function ResponseAreaPlugin(opts) {
|
|
|
90
98
|
if (n.type === 'explicit_constructed_response') {
|
|
91
99
|
const data = n.data.toJSON();
|
|
92
100
|
|
|
93
|
-
return
|
|
101
|
+
return (
|
|
102
|
+
<ExplicitConstructedResponse
|
|
103
|
+
attributes={attributes}
|
|
104
|
+
value={data.value}
|
|
105
|
+
error={opts.error && opts.error[data.value]}
|
|
106
|
+
/>
|
|
107
|
+
);
|
|
94
108
|
}
|
|
95
109
|
|
|
96
110
|
if (n.type === 'drag_in_the_blank') {
|
|
@@ -128,10 +142,15 @@ export default function ResponseAreaPlugin(opts) {
|
|
|
128
142
|
return;
|
|
129
143
|
}
|
|
130
144
|
|
|
131
|
-
const isOfCurrentType = d => d.type === opts.type || d.type === elTypesMap[opts.type];
|
|
132
145
|
const currentRespAreaList = change.value.document.filterDescendants(isOfCurrentType);
|
|
133
146
|
const oldRespAreaList = editor.value.document.filterDescendants(isOfCurrentType);
|
|
134
147
|
|
|
148
|
+
if (currentRespAreaList.size >= opts.maxResponseAreas) {
|
|
149
|
+
toolbar.disabled = true;
|
|
150
|
+
} else {
|
|
151
|
+
toolbar.disabled = false;
|
|
152
|
+
}
|
|
153
|
+
|
|
135
154
|
const arrayToFilter =
|
|
136
155
|
oldRespAreaList.size > currentRespAreaList.size ? oldRespAreaList : currentRespAreaList;
|
|
137
156
|
const arrayToUseForFilter =
|
|
@@ -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
|
|
|
@@ -28,7 +28,8 @@ export class EditorAndToolbar extends React.Component {
|
|
|
28
28
|
pluginProps: PropTypes.object,
|
|
29
29
|
toolbarOpts: PropTypes.shape({
|
|
30
30
|
position: PropTypes.oneOf(['bottom', 'top']),
|
|
31
|
-
alwaysVisible: PropTypes.bool
|
|
31
|
+
alwaysVisible: PropTypes.bool,
|
|
32
|
+
error: PropTypes.string
|
|
32
33
|
})
|
|
33
34
|
};
|
|
34
35
|
|
|
@@ -84,7 +85,7 @@ export class EditorAndToolbar extends React.Component {
|
|
|
84
85
|
);
|
|
85
86
|
|
|
86
87
|
return (
|
|
87
|
-
<div className={classes.root}>
|
|
88
|
+
<div className={classNames(classes.root, toolbarOpts && toolbarOpts.error && classes.error)}>
|
|
88
89
|
<div className={holderNames}>
|
|
89
90
|
<div className={classes.children}>{clonedChildren}</div>
|
|
90
91
|
</div>
|
|
@@ -215,6 +216,9 @@ const style = {
|
|
|
215
216
|
backgroundColor: primary
|
|
216
217
|
}
|
|
217
218
|
}
|
|
219
|
+
},
|
|
220
|
+
error: {
|
|
221
|
+
border: '2px solid red'
|
|
218
222
|
}
|
|
219
223
|
};
|
|
220
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}>
|