hero-editor 1.16.0 → 2.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hero-editor",
3
- "version": "1.16.0",
3
+ "version": "2.0.0",
4
4
  "description": "",
5
5
  "main": "dist/lib.js",
6
6
  "scripts": {
@@ -13,21 +13,22 @@
13
13
  "author": "",
14
14
  "license": "ISC",
15
15
  "devDependencies": {
16
- "@babel/core": "^7.10.2",
17
- "@babel/preset-env": "^7.10.2",
18
- "@babel/preset-react": "^7.10.1",
16
+ "@babel/core": "^7.26.0",
17
+ "@babel/preset-env": "^7.26.0",
18
+ "@babel/preset-react": "^7.25.9",
19
19
  "@testing-library/react": "^10.2.1",
20
20
  "@testing-library/react-hooks": "^3.3.0",
21
21
  "@webpack-cli/serve": "^2.0.5",
22
- "babel-jest": "^26.0.1",
23
- "babel-loader": "^8.1.0",
24
- "jest": "^26.0.1",
22
+ "babel-jest": "^29.7.0",
23
+ "babel-loader": "^9.2.1",
24
+ "jest": "^29.7.0",
25
+ "jest-environment-jsdom": "^29.7.0",
25
26
  "react": "^16.13.1",
26
27
  "react-dom": "^16.13.1",
27
28
  "react-test-renderer": "^16.13.1",
28
29
  "webpack": "^5.95.0",
29
30
  "webpack-cli": "^5.1.4",
30
- "webpack-dev-server": "^5.1.0"
31
+ "webpack-dev-server": "^5.2.1"
31
32
  },
32
33
  "dependencies": {
33
34
  "@juggle/resize-observer": "^3.4.0",
@@ -40,5 +41,8 @@
40
41
  "peerDependencies": {
41
42
  "react": "^16.13.1",
42
43
  "react-dom": "^16.13.1"
44
+ },
45
+ "jest": {
46
+ "testEnvironment": "jsdom"
43
47
  }
44
48
  }
package/src/constants.js CHANGED
@@ -37,6 +37,7 @@ const EMPTY_VALUE = [
37
37
 
38
38
  // Link actions
39
39
  const ADD_LINK = 'add-link';
40
+ const REQUEST_UPSERT_LINK = 'request-upsert-link';
40
41
 
41
42
  export {
42
43
  EMPTY_VALUE,
@@ -61,4 +62,5 @@ export {
61
62
  EDITOR_DELETE_BACKWARD,
62
63
  EDITOR_BUILT_IN_METHOD,
63
64
  EDITOR_READ_ONLY,
65
+ REQUEST_UPSERT_LINK,
64
66
  };
@@ -1,3 +1,6 @@
1
- const apply = (args = []) => (func) => func(...args);
1
+ const apply =
2
+ (args = []) =>
3
+ (func) =>
4
+ func(...args);
2
5
 
3
6
  export default apply;
@@ -1,4 +1,6 @@
1
- const compose = (...funcs) => (arg) =>
2
- funcs.reduceRight((composed, f) => (f ? f(composed) : composed), arg);
1
+ const compose =
2
+ (...funcs) =>
3
+ (arg) =>
4
+ funcs.reduceRight((composed, f) => (f ? f(composed) : composed), arg);
3
5
 
4
6
  export default compose;
@@ -1,4 +1,6 @@
1
- const flow = (...funcs) => (arg) =>
2
- funcs.reduce((composed, f) => (f ? f(composed) : composed), arg);
1
+ const flow =
2
+ (...funcs) =>
3
+ (arg) =>
4
+ funcs.reduce((composed, f) => (f ? f(composed) : composed), arg);
3
5
 
4
6
  export default flow;
@@ -1,5 +1,13 @@
1
1
  // lmao
2
2
  const y = (f) =>
3
- ((g) => (...a) => f(g(g))(...a))((g) => (...a) => f(g(g))(...a));
3
+ (
4
+ (g) =>
5
+ (...a) =>
6
+ f(g(g))(...a)
7
+ )(
8
+ (g) =>
9
+ (...a) =>
10
+ f(g(g))(...a),
11
+ );
4
12
 
5
13
  export default y;
package/src/lib.js CHANGED
@@ -1,4 +1,10 @@
1
- import React, { useMemo, useEffect, useRef, useState } from 'react';
1
+ import React, {
2
+ useMemo,
3
+ useEffect,
4
+ useRef,
5
+ useState,
6
+ useCallback,
7
+ } from 'react';
2
8
  import { createEditor, Editor, Transforms } from 'slate';
3
9
  import { Slate, Editable, withReact } from 'slate-react';
4
10
  import { withHistory } from 'slate-history';
@@ -81,6 +87,14 @@ const HeroEditor = ({
81
87
  )(),
82
88
  );
83
89
 
90
+ const getToolbarState = useCallback(() => {
91
+ return plugins.reduce((acc, plugin) => {
92
+ if (plugin.isActive) {
93
+ acc[plugin.name] = plugin.isActive(editor);
94
+ }
95
+ return acc;
96
+ }, {});
97
+ }, [editor, plugins]);
84
98
  const renderLeaf = useMemo(() => composeRenderLeaf(plugins), [plugins]);
85
99
 
86
100
  const renderElement = useMemo(() => composeRenderElement(plugins), [plugins]);
@@ -142,7 +156,11 @@ const HeroEditor = ({
142
156
  initialValue={initialValue || value}
143
157
  onChange={(value) => {
144
158
  onChange(value);
145
- postMessage(EDITOR_CHANGE, { value }, editor);
159
+ postMessage(
160
+ EDITOR_CHANGE,
161
+ { value, toolbarState: getToolbarState() },
162
+ editor,
163
+ );
146
164
  }}
147
165
  >
148
166
  {showToolbar ? <Toolbar>{toolBarButtons}</Toolbar> : null}
@@ -42,4 +42,5 @@ export default () =>
42
42
  renderLeaf,
43
43
  handleMessage,
44
44
  ToolbarButton,
45
+ isActive: (editor) => isMarkActive(editor, BOLD),
45
46
  });
@@ -57,4 +57,5 @@ export default () =>
57
57
  renderElement,
58
58
  handleMessage,
59
59
  ToolbarButton,
60
+ isActive: (editor) => isBlockActive(editor, BULLETED_LIST),
60
61
  });
@@ -2,20 +2,38 @@ import { ReactEditor } from 'slate-react';
2
2
  import { createPlugin, addMessageListener, postMessage } from '../helpers';
3
3
  import { EDITOR_CHANGE } from '../constants';
4
4
 
5
+ const getCursorPosition = (domRange) => {
6
+ const rangeRect = domRange.getBoundingClientRect();
7
+ /**
8
+ * On mobile, slate insert a <br> node when adding a new line which causing the DOM range after <br /> not have a bounding client rect
9
+ * so we need to check if the start container is a <br> node and return the bounding client rect of it
10
+ */
11
+ if (domRange.collapsed && domRange.startContainer.nodeName === 'BR') {
12
+ return domRange.startContainer?.getBoundingClientRect();
13
+ }
14
+
15
+ return rangeRect;
16
+ };
17
+
5
18
  const handleMessage = addMessageListener(EDITOR_CHANGE, ({ editor }) => {
6
19
  const { selection } = editor;
7
20
 
8
21
  if (selection) {
9
- const domRange = ReactEditor.toDOMRange(editor, selection);
10
- const rangeRect = domRange.getBoundingClientRect();
22
+ const domRange = ReactEditor.toDOMRange(editor, {
23
+ anchor: selection.focus,
24
+ focus: selection.focus,
25
+ });
26
+ const rangeRect = getCursorPosition(domRange);
27
+ if (!rangeRect) return;
11
28
 
12
29
  postMessage(
13
30
  'cursor-change',
14
31
  {
15
- offset: undefined,
16
32
  position: {
17
33
  top: rangeRect.top,
18
34
  left: rangeRect.left,
35
+ bottom: rangeRect.bottom,
36
+ right: rangeRect.right,
19
37
  },
20
38
  },
21
39
  editor,
@@ -54,4 +54,5 @@ export default () =>
54
54
  renderElement,
55
55
  handleMessage,
56
56
  ToolbarButton,
57
+ isActive: (editor) => isBlockActive(editor, HEADING_ONE),
57
58
  });
@@ -54,4 +54,5 @@ export default () =>
54
54
  renderElement,
55
55
  handleMessage,
56
56
  ToolbarButton,
57
+ isActive: (editor) => isBlockActive(editor, HEADING_TWO),
57
58
  });
@@ -42,4 +42,5 @@ export default () =>
42
42
  renderLeaf,
43
43
  handleMessage,
44
44
  ToolbarButton,
45
+ isActive: (editor) => isMarkActive(editor, ITALIC),
45
46
  });
@@ -11,7 +11,7 @@ import {
11
11
  isLink,
12
12
  getUrlFromNode,
13
13
  } from '../helpers';
14
- import { LINK, ADD_LINK } from '../constants';
14
+ import { LINK, ADD_LINK, REQUEST_UPSERT_LINK } from '../constants';
15
15
  import Toolbar from '../components/Toolbar';
16
16
  import Icon from '../components/Icon';
17
17
 
@@ -132,6 +132,7 @@ const LinkCustomWrapper = ({ renderLinkCustom }) => {
132
132
  useEffect(() => {
133
133
  const removeLinkCustomListener = addMessageListener(ADD_LINK, () => {
134
134
  setShowLinkCustom(true);
135
+ postMessage(REQUEST_UPSERT_LINK, getSelectedData(editor), editor);
135
136
  })(editor);
136
137
 
137
138
  return () => removeLinkCustomListener();
@@ -184,5 +185,6 @@ export default ({ renderLinkCustom, showToolbarButton } = {}) =>
184
185
  ToolbarButton: () => (
185
186
  <ToolbarButton showToolbarButton={showToolbarButton} />
186
187
  ),
188
+ isActive: (editor) => isBlockActive(editor, LINK),
187
189
  enhanceEditor,
188
190
  });
@@ -60,4 +60,5 @@ export default () =>
60
60
  renderElement,
61
61
  handleMessage,
62
62
  ToolbarButton,
63
+ isActive: (editor) => isBlockActive(editor, NUMBERED_LIST),
63
64
  });
@@ -45,4 +45,5 @@ export default () =>
45
45
  renderLeaf,
46
46
  handleMessage,
47
47
  ToolbarButton,
48
+ isActive: (editor) => isMarkActive(editor, UNDERLINE),
48
49
  });
@@ -1,7 +1,5 @@
1
1
  import React from 'react';
2
2
  import { Text } from 'slate';
3
- import isUrl from 'is-url';
4
- import getUrl from '../helpers/getUrl';
5
3
  import y from '../helpers/why';
6
4
  import { getUrlFromNode } from '../helpers';
7
5