hero-editor 1.8.5 → 1.9.2
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/dist/app.js +6 -27
- package/dist/app.js.map +1 -1
- package/dist/lib.js +2 -23
- package/dist/lib.js.map +1 -1
- package/package.json +3 -3
- package/src/app.js +0 -2
- package/src/components/Icon.js +2 -0
- package/src/helpers/elementHelpers.js +4 -1
- package/src/helpers/index.js +0 -1
- package/src/lib.js +1 -11
- package/src/plugins/__tests__/__snapshots__/link.test.js.snap +11 -0
- package/src/plugins/__tests__/link.test.js +140 -0
- package/src/plugins/link.js +70 -9
- package/src/helpers/useAndroidHack.js +0 -148
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "hero-editor",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.9.2",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "dist/lib.js",
|
|
6
6
|
"scripts": {
|
|
@@ -29,8 +29,8 @@
|
|
|
29
29
|
},
|
|
30
30
|
"dependencies": {
|
|
31
31
|
"is-url": "^1.2.4",
|
|
32
|
-
"slate": "^0.
|
|
33
|
-
"slate-react": "^0.
|
|
32
|
+
"slate": "^0.70.0",
|
|
33
|
+
"slate-react": "^0.70.0"
|
|
34
34
|
},
|
|
35
35
|
"peerDependencies": {
|
|
36
36
|
"react": "^16.13.1",
|
package/src/app.js
CHANGED
|
@@ -18,7 +18,6 @@ import HeroEditor, {
|
|
|
18
18
|
const placeholder = window.__editorConfigs?.placeholder;
|
|
19
19
|
const initialValue = window.__editorConfigs?.initialValue;
|
|
20
20
|
const autoFocus = window.__editorConfigs?.autoFocus;
|
|
21
|
-
const isAndroid = window.__editorConfigs?.isAndroid;
|
|
22
21
|
const style = window.__editorConfigs?.style;
|
|
23
22
|
|
|
24
23
|
const App = () => {
|
|
@@ -45,7 +44,6 @@ const App = () => {
|
|
|
45
44
|
<HeroEditor
|
|
46
45
|
id="webview"
|
|
47
46
|
showToolbar={false}
|
|
48
|
-
isAndroid={isAndroid}
|
|
49
47
|
autoFocus={autoFocus}
|
|
50
48
|
plugins={plugins}
|
|
51
49
|
value={value}
|
package/src/components/Icon.js
CHANGED
|
@@ -15,6 +15,8 @@ const svgIcons = {
|
|
|
15
15
|
'<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 0 24 24" width="24"><path d="M0 0h24v24H0z" fill="none"/><path d="M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-5 14h-2V9h-2V7h4v10z"/></svg>',
|
|
16
16
|
looks_two:
|
|
17
17
|
'<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 0 24 24" width="24"><path d="M0 0h24v24H0z" fill="none"/><path d="M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-4 8c0 1.11-.9 2-2 2h-2v2h4v2H9v-4c0-1.11.9-2 2-2h2V9H9V7h4c1.1 0 2 .89 2 2v2z"/></svg>',
|
|
18
|
+
link:
|
|
19
|
+
'<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M13.723 18.654l-3.61 3.609c-2.316 2.315-6.063 2.315-8.378 0-1.12-1.118-1.735-2.606-1.735-4.188 0-1.582.615-3.07 1.734-4.189l4.866-4.865c2.355-2.355 6.114-2.262 8.377 0 .453.453.81.973 1.089 1.527l-1.593 1.592c-.18-.613-.5-1.189-.964-1.652-1.448-1.448-3.93-1.51-5.439-.001l-.001.002-4.867 4.865c-1.5 1.499-1.5 3.941 0 5.44 1.517 1.517 3.958 1.488 5.442 0l2.425-2.424c.993.284 1.791.335 2.654.284zm.161-16.918l-3.574 3.576c.847-.05 1.655 0 2.653.283l2.393-2.389c1.498-1.502 3.94-1.5 5.44-.001 1.517 1.518 1.486 3.959 0 5.442l-4.831 4.831-.003.002c-1.438 1.437-3.886 1.552-5.439-.002-.473-.474-.785-1.042-.956-1.643l-.084.068-1.517 1.515c.28.556.635 1.075 1.088 1.528 2.245 2.245 6.004 2.374 8.378 0l4.832-4.831c2.314-2.316 2.316-6.062-.001-8.377-2.317-2.321-6.067-2.313-8.379-.002z"/></svg>',
|
|
18
20
|
};
|
|
19
21
|
|
|
20
22
|
const styles = {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { Editor } from 'slate';
|
|
3
3
|
import compose from './compose';
|
|
4
|
-
import { NUMBERED_LIST, BULLETED_LIST } from '../constants';
|
|
4
|
+
import { NUMBERED_LIST, BULLETED_LIST, LINK } from '../constants';
|
|
5
5
|
|
|
6
6
|
const isBlockActive = (editor, format) => {
|
|
7
7
|
const [match] = Editor.nodes(editor, {
|
|
@@ -12,6 +12,8 @@ const isBlockActive = (editor, format) => {
|
|
|
12
12
|
|
|
13
13
|
const isList = (node) => [BULLETED_LIST, NUMBERED_LIST].includes(node.type);
|
|
14
14
|
|
|
15
|
+
const isLink = (node) => node.type === LINK;
|
|
16
|
+
|
|
15
17
|
const DefaultElement = React.forwardRef((props, ref) => (
|
|
16
18
|
<p ref={ref} style={{ margin: 0, lineHeight: 1.5 }} {...props} />
|
|
17
19
|
));
|
|
@@ -35,6 +37,7 @@ const composeRenderElement = (plugins) =>
|
|
|
35
37
|
export {
|
|
36
38
|
isBlockActive,
|
|
37
39
|
isList,
|
|
40
|
+
isLink,
|
|
38
41
|
renderDefaultElement,
|
|
39
42
|
makeRenderElement,
|
|
40
43
|
composeRenderElement,
|
package/src/helpers/index.js
CHANGED
|
@@ -12,7 +12,6 @@ export { default as getUrl } from './getUrl';
|
|
|
12
12
|
export { default as isEmptyContent } from './isEmptyContent';
|
|
13
13
|
|
|
14
14
|
export { default as useForceUpdate } from './useForceUpdate';
|
|
15
|
-
export { default as useAndroidHack } from './useAndroidHack';
|
|
16
15
|
|
|
17
16
|
export * from './leafHelpers';
|
|
18
17
|
export * from './elementHelpers';
|
package/src/lib.js
CHANGED
|
@@ -10,7 +10,6 @@ import {
|
|
|
10
10
|
get,
|
|
11
11
|
apply,
|
|
12
12
|
flow,
|
|
13
|
-
useAndroidHack,
|
|
14
13
|
} from './helpers';
|
|
15
14
|
import Toolbar from './components/Toolbar';
|
|
16
15
|
import {
|
|
@@ -59,7 +58,6 @@ const HeroEditor = ({
|
|
|
59
58
|
onLayout = noop,
|
|
60
59
|
wrapperStyle,
|
|
61
60
|
editableStyle,
|
|
62
|
-
isAndroid = false,
|
|
63
61
|
}) => {
|
|
64
62
|
const wrapper = useRef(null);
|
|
65
63
|
const wrapperLayout = useRef({ width: 0, height: 0 });
|
|
@@ -106,13 +104,6 @@ const HeroEditor = ({
|
|
|
106
104
|
}
|
|
107
105
|
}, [value]);
|
|
108
106
|
|
|
109
|
-
// Hack for Slate 0.58 on Android
|
|
110
|
-
let onCompositionEnd, onDOMBeforeInput;
|
|
111
|
-
|
|
112
|
-
if (isAndroid) {
|
|
113
|
-
({ onCompositionEnd, onDOMBeforeInput } = useAndroidHack(editor));
|
|
114
|
-
}
|
|
115
|
-
|
|
116
107
|
return (
|
|
117
108
|
<div
|
|
118
109
|
ref={wrapper}
|
|
@@ -133,8 +124,6 @@ const HeroEditor = ({
|
|
|
133
124
|
className="hero-editor--editable"
|
|
134
125
|
placeholder={placeholder}
|
|
135
126
|
spellCheck
|
|
136
|
-
onCompositionEnd={onCompositionEnd}
|
|
137
|
-
onDOMBeforeInput={onDOMBeforeInput}
|
|
138
127
|
autoFocus={autoFocus}
|
|
139
128
|
renderLeaf={renderLeaf}
|
|
140
129
|
renderElement={renderElement}
|
|
@@ -158,6 +147,7 @@ const HeroEditor = ({
|
|
|
158
147
|
export * from './plugins';
|
|
159
148
|
export { default as JsonViewer } from './components/JsonViewer';
|
|
160
149
|
export { default as Toolbar } from './components/Toolbar';
|
|
150
|
+
export { default as Icon } from './components/Icon';
|
|
161
151
|
export { default as plainSerializer } from './serializers/plain';
|
|
162
152
|
export { default as makeReactTransformer } from './transformers/react';
|
|
163
153
|
export { defaultReactTransformer } from './transformers/react';
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { createEditor, Transforms } from 'slate';
|
|
2
|
+
import { render, waitFor } from '@testing-library/react';
|
|
3
|
+
import link from '../link';
|
|
4
|
+
import {
|
|
5
|
+
createPlugin,
|
|
6
|
+
renderDefaultElement,
|
|
7
|
+
postMessage,
|
|
8
|
+
withId,
|
|
9
|
+
} from '../../helpers';
|
|
10
|
+
|
|
11
|
+
describe('link plugin', () => {
|
|
12
|
+
it('is a plugin object', () => {
|
|
13
|
+
for (const prop in createPlugin()) {
|
|
14
|
+
expect(link()[prop]).toBeDefined();
|
|
15
|
+
}
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it('renders a link element', () => {
|
|
19
|
+
const { baseElement } = render(
|
|
20
|
+
link().renderElement(renderDefaultElement)({
|
|
21
|
+
element: {
|
|
22
|
+
type: 'link',
|
|
23
|
+
data: { url: 'https://www.example.com' },
|
|
24
|
+
children: [
|
|
25
|
+
{
|
|
26
|
+
text: 'Link example',
|
|
27
|
+
},
|
|
28
|
+
],
|
|
29
|
+
},
|
|
30
|
+
}),
|
|
31
|
+
);
|
|
32
|
+
expect(baseElement).toMatchSnapshot();
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
describe('handle add link message properly', () => {
|
|
36
|
+
it('add new text with url', async () => {
|
|
37
|
+
const editor = withId('link-sample')(createEditor());
|
|
38
|
+
link().handleMessage(editor);
|
|
39
|
+
|
|
40
|
+
editor.insertNode({
|
|
41
|
+
type: 'paragraph',
|
|
42
|
+
children: [
|
|
43
|
+
{
|
|
44
|
+
text: '',
|
|
45
|
+
},
|
|
46
|
+
],
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
postMessage(
|
|
50
|
+
'link',
|
|
51
|
+
{ text: 'Link example', url: 'https://www.example.com' },
|
|
52
|
+
editor,
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
await waitFor(() => {
|
|
56
|
+
expect(editor.children).toEqual([
|
|
57
|
+
{
|
|
58
|
+
type: 'paragraph',
|
|
59
|
+
children: [
|
|
60
|
+
{
|
|
61
|
+
text: '',
|
|
62
|
+
},
|
|
63
|
+
],
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
type: 'link',
|
|
67
|
+
children: [
|
|
68
|
+
{
|
|
69
|
+
text: 'Link example',
|
|
70
|
+
},
|
|
71
|
+
],
|
|
72
|
+
data: {
|
|
73
|
+
url: 'https://www.example.com',
|
|
74
|
+
},
|
|
75
|
+
},
|
|
76
|
+
]);
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it('add url to an existing text', async () => {
|
|
81
|
+
const editor = withId('link-sample')(createEditor());
|
|
82
|
+
link().handleMessage(editor);
|
|
83
|
+
|
|
84
|
+
editor.insertNode({
|
|
85
|
+
type: 'paragraph',
|
|
86
|
+
children: [
|
|
87
|
+
{
|
|
88
|
+
text: 'this is an url',
|
|
89
|
+
},
|
|
90
|
+
],
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
Transforms.select(editor, {
|
|
94
|
+
anchor: {
|
|
95
|
+
path: [0, 0],
|
|
96
|
+
offset: 14,
|
|
97
|
+
},
|
|
98
|
+
focus: {
|
|
99
|
+
path: [0, 0],
|
|
100
|
+
offset: 11,
|
|
101
|
+
},
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
postMessage(
|
|
105
|
+
'link',
|
|
106
|
+
{ text: 'url', url: 'https://www.example.com' },
|
|
107
|
+
editor,
|
|
108
|
+
);
|
|
109
|
+
|
|
110
|
+
await waitFor(() => {
|
|
111
|
+
expect(editor.children).toEqual([
|
|
112
|
+
{
|
|
113
|
+
type: 'paragraph',
|
|
114
|
+
children: [
|
|
115
|
+
{
|
|
116
|
+
text: 'this is an ',
|
|
117
|
+
},
|
|
118
|
+
],
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
type: 'link',
|
|
122
|
+
children: [
|
|
123
|
+
{
|
|
124
|
+
children: [
|
|
125
|
+
{
|
|
126
|
+
text: 'url',
|
|
127
|
+
},
|
|
128
|
+
],
|
|
129
|
+
type: 'paragraph',
|
|
130
|
+
},
|
|
131
|
+
],
|
|
132
|
+
data: {
|
|
133
|
+
url: 'https://www.example.com',
|
|
134
|
+
},
|
|
135
|
+
},
|
|
136
|
+
]);
|
|
137
|
+
});
|
|
138
|
+
});
|
|
139
|
+
});
|
|
140
|
+
});
|
package/src/plugins/link.js
CHANGED
|
@@ -1,17 +1,35 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
+
import { useSlate } from 'slate-react';
|
|
2
3
|
import isUrl from 'is-url';
|
|
3
|
-
import { Transforms } from 'slate';
|
|
4
|
+
import { Transforms, Editor, Range } from 'slate';
|
|
4
5
|
import {
|
|
5
6
|
createPlugin,
|
|
6
7
|
makeRenderElement,
|
|
7
8
|
addMessageListener,
|
|
8
9
|
postMessage,
|
|
10
|
+
isBlockActive,
|
|
11
|
+
isLink,
|
|
9
12
|
} from '../helpers';
|
|
10
13
|
import { LINK } from '../constants';
|
|
11
14
|
|
|
12
15
|
const getUrl = (element) =>
|
|
13
16
|
element.children.reduce((url, node) => url + node.text, '');
|
|
14
17
|
|
|
18
|
+
const getUrlFromNodes = (editor) => {
|
|
19
|
+
const [...linkNodes] = Editor.nodes(editor, { match: isLink });
|
|
20
|
+
|
|
21
|
+
if (linkNodes.length === 1) {
|
|
22
|
+
const [nodes] = linkNodes[0];
|
|
23
|
+
return nodes?.data.url;
|
|
24
|
+
}
|
|
25
|
+
return '';
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const getSelectedData = (editor) => ({
|
|
29
|
+
text: Editor.string(editor, editor.selection),
|
|
30
|
+
url: getUrlFromNodes(editor),
|
|
31
|
+
});
|
|
32
|
+
|
|
15
33
|
const LinkElement = ({ attributes, children, element }) => (
|
|
16
34
|
<a {...attributes} href={getUrl(element)}>
|
|
17
35
|
{children}
|
|
@@ -20,13 +38,36 @@ const LinkElement = ({ attributes, children, element }) => (
|
|
|
20
38
|
|
|
21
39
|
const renderElement = makeRenderElement(LINK, LinkElement);
|
|
22
40
|
|
|
23
|
-
const handleMessage = addMessageListener(
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
41
|
+
const handleMessage = addMessageListener(
|
|
42
|
+
LINK,
|
|
43
|
+
({ editor, data: { text, url } }) => {
|
|
44
|
+
if (!text || !url || !isUrl(url)) return;
|
|
45
|
+
|
|
46
|
+
if (isBlockActive(editor, LINK)) {
|
|
47
|
+
Transforms.unwrapNodes(editor, {
|
|
48
|
+
match: isLink,
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const { selection } = editor;
|
|
53
|
+
const isCollapsed = selection && Range.isCollapsed(selection);
|
|
54
|
+
|
|
55
|
+
const linkNode = {
|
|
56
|
+
type: LINK,
|
|
57
|
+
data: { url },
|
|
58
|
+
children: isCollapsed ? [{ text: text ?? url }] : [],
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
if (isCollapsed) {
|
|
62
|
+
Transforms.insertNodes(editor, linkNode);
|
|
63
|
+
} else {
|
|
64
|
+
Transforms.wrapNodes(editor, linkNode, { split: true });
|
|
65
|
+
Transforms.collapse(editor, { edge: 'end' });
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
Transforms.move(editor, { unit: 'offset' });
|
|
69
|
+
},
|
|
70
|
+
);
|
|
30
71
|
|
|
31
72
|
const enhanceEditor = (editor) => {
|
|
32
73
|
const { isInline, insertData } = editor;
|
|
@@ -48,10 +89,30 @@ const enhanceEditor = (editor) => {
|
|
|
48
89
|
return editor;
|
|
49
90
|
};
|
|
50
91
|
|
|
51
|
-
|
|
92
|
+
const ToolbarButton = ({ renderToolbarButton }) => {
|
|
93
|
+
const editor = useSlate();
|
|
94
|
+
|
|
95
|
+
const handleAddLink = (text, url) => {
|
|
96
|
+
postMessage(LINK, { text, url }, editor);
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
return (
|
|
100
|
+
<>
|
|
101
|
+
{renderToolbarButton?.({
|
|
102
|
+
handleAddLink,
|
|
103
|
+
getSelectedData: () => getSelectedData(editor),
|
|
104
|
+
})}
|
|
105
|
+
</>
|
|
106
|
+
);
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
export default ({ renderToolbarButton } = {}) =>
|
|
52
110
|
createPlugin({
|
|
53
111
|
name: LINK,
|
|
54
112
|
renderElement,
|
|
55
113
|
handleMessage,
|
|
114
|
+
ToolbarButton: () => (
|
|
115
|
+
<ToolbarButton renderToolbarButton={renderToolbarButton} />
|
|
116
|
+
),
|
|
56
117
|
enhanceEditor,
|
|
57
118
|
});
|
|
@@ -1,148 +0,0 @@
|
|
|
1
|
-
// https://github.com/ianstormtaylor/slate/issues/3470#issuecomment-639431390
|
|
2
|
-
import { useRef, useCallback } from 'react';
|
|
3
|
-
import { Transforms, Editor as SlateEditor } from 'slate';
|
|
4
|
-
import { ReactEditor } from 'slate-react';
|
|
5
|
-
|
|
6
|
-
const useAndroidHack = (editor) => {
|
|
7
|
-
const lastComposeData = useRef(null);
|
|
8
|
-
|
|
9
|
-
const onDOMBeforeInput = useCallback(
|
|
10
|
-
(e) => {
|
|
11
|
-
switch (e.inputType) {
|
|
12
|
-
case 'insertCompositionText': {
|
|
13
|
-
// We need to push each composition event so we can apply it when finished
|
|
14
|
-
if (!lastComposeData.current) {
|
|
15
|
-
lastComposeData.current = [];
|
|
16
|
-
}
|
|
17
|
-
try {
|
|
18
|
-
// We need to convert the selection right away, as the window.getSelection() will change with the window
|
|
19
|
-
const selection = window.getSelection();
|
|
20
|
-
const sel = ReactEditor.toSlateRange(editor, selection);
|
|
21
|
-
|
|
22
|
-
// The offsets may not match because the composition text might be far ahead etc...
|
|
23
|
-
sel.anchor.offset = selection.anchorOffset;
|
|
24
|
-
sel.focus.offset = selection.focusOffset;
|
|
25
|
-
|
|
26
|
-
lastComposeData.current.push({
|
|
27
|
-
selection: sel,
|
|
28
|
-
value: e.data,
|
|
29
|
-
node: selection?.anchorNode,
|
|
30
|
-
elementNode: selection?.anchorNode?.parentElement.closest(
|
|
31
|
-
'[data-slate-node="element"]',
|
|
32
|
-
),
|
|
33
|
-
});
|
|
34
|
-
} catch {
|
|
35
|
-
lastComposeData.current = null;
|
|
36
|
-
}
|
|
37
|
-
break;
|
|
38
|
-
}
|
|
39
|
-
case 'insertFromComposition':
|
|
40
|
-
case 'deleteByComposition':
|
|
41
|
-
// If we get this event we don't need to apply any sort of fix, this is the correct event to handle things
|
|
42
|
-
lastComposeData.current = null;
|
|
43
|
-
break;
|
|
44
|
-
default:
|
|
45
|
-
break;
|
|
46
|
-
}
|
|
47
|
-
},
|
|
48
|
-
[editor],
|
|
49
|
-
);
|
|
50
|
-
|
|
51
|
-
const onCompositionEnd = useCallback(
|
|
52
|
-
(e) => {
|
|
53
|
-
const { current } = lastComposeData;
|
|
54
|
-
if (current) {
|
|
55
|
-
// Store the current selection so we can move back once we have finished applying queued changes
|
|
56
|
-
// Convert to slate range straight away as the selection can get messed up due to element fixing code
|
|
57
|
-
const {
|
|
58
|
-
anchorNode,
|
|
59
|
-
anchorOffset,
|
|
60
|
-
focusNode,
|
|
61
|
-
focusOffset,
|
|
62
|
-
isCollapsed,
|
|
63
|
-
} = window.getSelection();
|
|
64
|
-
|
|
65
|
-
// Apply each of the changes
|
|
66
|
-
for (const c of current) {
|
|
67
|
-
const { selection, value, node, elementNode } = c;
|
|
68
|
-
Transforms.select(editor, selection);
|
|
69
|
-
SlateEditor.insertText(editor, value);
|
|
70
|
-
|
|
71
|
-
if (value) {
|
|
72
|
-
// HACK #1 - when a new line is created slate creates a zero-width
|
|
73
|
-
// but actually it will be filled in, this causes slate crashes
|
|
74
|
-
// Have to recreate a full element instead of an empty element in slate
|
|
75
|
-
const el = node.parentElement;
|
|
76
|
-
if (el && el.hasAttribute('data-slate-zero-width')) {
|
|
77
|
-
el.removeAttribute('data-slate-length');
|
|
78
|
-
el.removeAttribute('data-slate-zero-width');
|
|
79
|
-
el.setAttribute('data-slate-string', 'true');
|
|
80
|
-
el.innerText = value;
|
|
81
|
-
const { path, offset } = editor.selection.anchor;
|
|
82
|
-
const p = { path, offset: offset - 1 };
|
|
83
|
-
Transforms.select(editor, { anchor: p, focus: p });
|
|
84
|
-
}
|
|
85
|
-
} else {
|
|
86
|
-
// HACK #2 - when an element is made empty during compose
|
|
87
|
-
// the element is removed from dom, need to add it back
|
|
88
|
-
// as a zero-width element to match
|
|
89
|
-
const textNode = node.parentElement.closest(
|
|
90
|
-
'[data-slate-node="text"]',
|
|
91
|
-
);
|
|
92
|
-
const el = node.parentElement;
|
|
93
|
-
if (
|
|
94
|
-
elementNode &&
|
|
95
|
-
textNode &&
|
|
96
|
-
el &&
|
|
97
|
-
elementNode.children.length === 1 &&
|
|
98
|
-
elementNode.children[0].nodeName === 'BR'
|
|
99
|
-
) {
|
|
100
|
-
el.innerHTML = '';
|
|
101
|
-
el.setAttribute('data-slate-length', '0');
|
|
102
|
-
el.setAttribute('data-slate-zero-width', 'n');
|
|
103
|
-
el.removeAttribute('data-slate-string');
|
|
104
|
-
elementNode.replaceChild(textNode, elementNode.children[0]);
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
lastComposeData.current = null;
|
|
110
|
-
|
|
111
|
-
// Move back to existing selection
|
|
112
|
-
try {
|
|
113
|
-
const ss = ReactEditor.toSlateRange(editor, {
|
|
114
|
-
startContainer: anchorNode,
|
|
115
|
-
startOffset: anchorOffset,
|
|
116
|
-
endContainer: focusNode,
|
|
117
|
-
endOffset: focusOffset,
|
|
118
|
-
collapsed: isCollapsed,
|
|
119
|
-
});
|
|
120
|
-
// Small fixup again
|
|
121
|
-
ss.anchor.offset = anchorOffset;
|
|
122
|
-
ss.focus.offset = focusOffset;
|
|
123
|
-
Transforms.select(editor, ss);
|
|
124
|
-
|
|
125
|
-
// Reset the dom selection as well... this can get out of alignment
|
|
126
|
-
const sel = window.getSelection();
|
|
127
|
-
sel.removeAllRanges();
|
|
128
|
-
const newDomRange = ReactEditor.toDOMRange(editor, ss);
|
|
129
|
-
if (newDomRange) {
|
|
130
|
-
sel.addRange(newDomRange);
|
|
131
|
-
}
|
|
132
|
-
} catch {}
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
// Prevent slate from doing anything
|
|
136
|
-
// This prevents the hack that is currently in onCompositionEnd that doesn't work
|
|
137
|
-
e.data = null;
|
|
138
|
-
},
|
|
139
|
-
[editor],
|
|
140
|
-
);
|
|
141
|
-
|
|
142
|
-
return {
|
|
143
|
-
onCompositionEnd,
|
|
144
|
-
onDOMBeforeInput,
|
|
145
|
-
};
|
|
146
|
-
};
|
|
147
|
-
|
|
148
|
-
export default useAndroidHack;
|