hero-editor 1.17.0 → 2.1.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.17.0",
3
+ "version": "2.1.0",
4
4
  "description": "",
5
5
  "main": "dist/lib.js",
6
6
  "scripts": {
@@ -13,15 +13,16 @@
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",
@@ -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
  }
@@ -15,6 +15,10 @@ 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
+ looks_three:
19
+ '<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path d="M360-280h160q33 0 56.5-23.5T600-360v-60q0-26-17-43t-43-17q26 0 43-17t17-43v-60q0-33-23.5-56.5T520-680H360v80h160v80h-80v80h80v80H360v80ZM200-120q-33 0-56.5-23.5T120-200v-560q0-33 23.5-56.5T200-840h560q33 0 56.5 23.5T840-760v560q0 33-23.5 56.5T760-120H200Z"/></svg>',
20
+ looks_four:
21
+ '<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path d="M520-280h80v-400h-80v160h-80v-160h-80v240h160v160ZM200-120q-33 0-56.5-23.5T120-200v-560q0-33 23.5-56.5T200-840h560q33 0 56.5 23.5T840-760v560q0 33-23.5 56.5T760-120H200Z"/></svg>',
18
22
  link: '<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 0 24 24" width="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>',
19
23
  icon: '<svg width="13" height="12" viewBox="0 0 13 12" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M6.75 0C10.0637 0 12.75 2.68629 12.75 6C12.75 9.31371 10.0637 12 6.75 12C3.43629 12 0.75 9.31371 0.75 6C0.75 2.68629 3.43629 0 6.75 0ZM6.75 0.84375C3.90228 0.84375 1.59375 3.15228 1.59375 6C1.59375 8.84772 3.90228 11.1562 6.75 11.1562C9.59772 11.1562 11.9062 8.84772 11.9062 6C11.9062 3.15228 9.59772 0.84375 6.75 0.84375ZM8.83711 7.61484C9.04551 7.71904 9.12998 7.97246 9.02578 8.18086C8.53481 9.16281 7.75947 9.67969 6.75 9.67969C5.74053 9.67969 4.96519 9.16281 4.47422 8.18086C4.37002 7.97246 4.45449 7.71904 4.66289 7.61484C4.87129 7.51064 5.12471 7.59512 5.22891 7.80352C5.58168 8.50907 6.07197 8.83594 6.75 8.83594C7.42803 8.83594 7.91832 8.50907 8.27109 7.80352C8.37529 7.59512 8.62871 7.51064 8.83711 7.61484ZM4.3125 3.76172C4.83027 3.76172 5.25 4.18146 5.25 4.69922C5.25 5.21699 4.83027 5.63672 4.3125 5.63672C3.79473 5.63672 3.375 5.21699 3.375 4.69922C3.375 4.18146 3.79473 3.76172 4.3125 3.76172ZM9.15234 3.76172C9.67011 3.76172 10.0898 4.18146 10.0898 4.69922C10.0898 5.21699 9.67011 5.63672 9.15234 5.63672C8.63458 5.63672 8.21484 5.21699 8.21484 4.69922C8.21484 4.18146 8.63458 3.76172 9.15234 3.76172Z" fill="black"/></svg>',
20
24
  };
@@ -27,6 +27,18 @@ const styles = {
27
27
  margin: '10px 5px',
28
28
  borderRight: 'solid thin #eaeaea',
29
29
  },
30
+ select: {
31
+ margin: 'auto 0',
32
+ height: 28,
33
+ border: 'solid thin silver',
34
+ borderRadius: 4,
35
+ padding: '0 4px',
36
+ cursor: 'pointer',
37
+ background: 'transparent',
38
+ fontSize: 12,
39
+ outline: 'none',
40
+ maxWidth: 120,
41
+ },
30
42
  };
31
43
 
32
44
  const Toolbar = ({ children }) => <div style={styles.toolbar}>{children}</div>;
@@ -44,5 +56,22 @@ Toolbar.Button = forwardRef(({ active, ...props }, ref) => (
44
56
  ));
45
57
 
46
58
  Toolbar.Separator = () => <div style={styles.separator} />;
59
+ Toolbar.Dropdown = forwardRef(
60
+ ({ options = [], currentValue, onChange, ...props }, ref) => (
61
+ <select
62
+ ref={ref}
63
+ onChange={onChange}
64
+ style={styles.select}
65
+ value={currentValue}
66
+ {...props}
67
+ >
68
+ {options.map((option) => (
69
+ <option key={option.value} value={option.value}>
70
+ {option.label}
71
+ </option>
72
+ ))}
73
+ </select>
74
+ ),
75
+ );
47
76
 
48
77
  export default Toolbar;
package/src/constants.js CHANGED
@@ -7,6 +7,8 @@ const EMOJI = 'emoji';
7
7
  const LINK = 'link';
8
8
  const HEADING_ONE = 'heading-one';
9
9
  const HEADING_TWO = 'heading-two';
10
+ const HEADING_THREE = 'heading-three';
11
+ const HEADING_FOUR = 'heading-four';
10
12
  const PARAGRAPH = 'paragraph';
11
13
 
12
14
  // Marks
@@ -49,6 +51,8 @@ export {
49
51
  LINK,
50
52
  HEADING_ONE,
51
53
  HEADING_TWO,
54
+ HEADING_THREE,
55
+ HEADING_FOUR,
52
56
  PARAGRAPH,
53
57
  BOLD,
54
58
  ITALIC,
@@ -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
@@ -199,5 +199,12 @@ export { default as plainSerializer } from './serializers/plain';
199
199
  export { default as makeReactTransformer } from './transformers/react';
200
200
  export { defaultReactTransformer } from './transformers/react';
201
201
  export { isEmptyContent, getUrl, isUrl, getUrlFromNode } from './helpers';
202
- export { EMPTY_VALUE } from './constants';
202
+ export {
203
+ EMPTY_VALUE,
204
+ PARAGRAPH,
205
+ HEADING_ONE,
206
+ HEADING_TWO,
207
+ HEADING_THREE,
208
+ HEADING_FOUR,
209
+ } from './constants';
203
210
  export default HeroEditor;
@@ -0,0 +1,11 @@
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`headingFour plugin renders h4 element 1`] = `
4
+ <body>
5
+ <div>
6
+ <h4>
7
+ Heading four
8
+ </h4>
9
+ </div>
10
+ </body>
11
+ `;
@@ -0,0 +1,11 @@
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`headingThree plugin renders h3 element 1`] = `
4
+ <body>
5
+ <div>
6
+ <h3>
7
+ Heading three
8
+ </h3>
9
+ </div>
10
+ </body>
11
+ `;
@@ -0,0 +1,60 @@
1
+ import { createEditor } from 'slate';
2
+ import { waitFor } from '@testing-library/react';
3
+ import dropdown from '../dropdown';
4
+ import { createPlugin, postMessage, withId } from '../../helpers';
5
+ import { PARAGRAPH, HEADING_ONE, HEADING_TWO } from '../../constants';
6
+
7
+ const OPTIONS = [
8
+ { value: PARAGRAPH, label: 'Normal text' },
9
+ { value: HEADING_ONE, label: 'Heading 1' },
10
+ { value: HEADING_TWO, label: 'Heading 2' },
11
+ ];
12
+
13
+ describe('dropdown plugin', () => {
14
+ it('is a plugin object', () => {
15
+ const plugin = dropdown({ name: 'test-dropdown', options: OPTIONS });
16
+ for (const prop in createPlugin()) {
17
+ expect(plugin[prop]).toBeDefined();
18
+ }
19
+ });
20
+
21
+ it('handles dropdown message and sets block type', async () => {
22
+ const editor = withId('sample')(createEditor());
23
+ dropdown({ name: 'test-dropdown', options: OPTIONS }).handleMessage(editor);
24
+ editor.insertNode({
25
+ type: PARAGRAPH,
26
+ children: [{ text: 'Some text' }],
27
+ });
28
+
29
+ postMessage('test-dropdown', { type: HEADING_ONE }, editor);
30
+
31
+ await waitFor(() => {
32
+ expect(editor.children[0].type).toBe(HEADING_ONE);
33
+ });
34
+ });
35
+
36
+ it('ignores message when data.type is missing', async () => {
37
+ const editor = withId('sample')(createEditor());
38
+ dropdown({ name: 'test-dropdown', options: OPTIONS }).handleMessage(editor);
39
+ editor.insertNode({
40
+ type: PARAGRAPH,
41
+ children: [{ text: 'Some text' }],
42
+ });
43
+
44
+ postMessage('test-dropdown', {}, editor);
45
+
46
+ await waitFor(() => {
47
+ expect(editor.children[0].type).toBe(PARAGRAPH);
48
+ });
49
+ });
50
+
51
+ it('provides a ToolbarButton', () => {
52
+ const plugin = dropdown({ name: 'test-dropdown', options: OPTIONS });
53
+ expect(plugin.ToolbarButton).toBeDefined();
54
+ });
55
+
56
+ it('provides a ToolbarButton even with empty options', () => {
57
+ const plugin = dropdown({ name: 'test-dropdown', options: [] });
58
+ expect(plugin.ToolbarButton).toBeDefined();
59
+ });
60
+ });
@@ -0,0 +1,27 @@
1
+ import { render } from '@testing-library/react';
2
+ import headingFour from '../headingFour';
3
+ import { createPlugin, renderDefaultElement } from '../../helpers';
4
+
5
+ describe('headingFour plugin', () => {
6
+ it('is a plugin object', () => {
7
+ for (const prop in createPlugin()) {
8
+ expect(headingFour()[prop]).toBeDefined();
9
+ }
10
+ });
11
+
12
+ it('renders h4 element', () => {
13
+ const { baseElement } = render(
14
+ headingFour().renderElement(renderDefaultElement)({
15
+ element: { type: 'heading-four' },
16
+ children: 'Heading four',
17
+ }),
18
+ );
19
+ expect(baseElement).toMatchSnapshot();
20
+ });
21
+
22
+ it('still provides ToolbarButton when showToolbarButton is false', () => {
23
+ expect(
24
+ headingFour({ showToolbarButton: false }).ToolbarButton,
25
+ ).toBeDefined();
26
+ });
27
+ });
@@ -18,4 +18,10 @@ describe('headingOne plugin', () => {
18
18
  );
19
19
  expect(baseElement).toMatchSnapshot();
20
20
  });
21
+
22
+ it('still provides ToolbarButton when showToolbarButton is false', () => {
23
+ expect(
24
+ headingOne({ showToolbarButton: false }).ToolbarButton,
25
+ ).toBeDefined();
26
+ });
21
27
  });
@@ -0,0 +1,27 @@
1
+ import { render } from '@testing-library/react';
2
+ import headingThree from '../headingThree';
3
+ import { createPlugin, renderDefaultElement } from '../../helpers';
4
+
5
+ describe('headingThree plugin', () => {
6
+ it('is a plugin object', () => {
7
+ for (const prop in createPlugin()) {
8
+ expect(headingThree()[prop]).toBeDefined();
9
+ }
10
+ });
11
+
12
+ it('renders h3 element', () => {
13
+ const { baseElement } = render(
14
+ headingThree().renderElement(renderDefaultElement)({
15
+ element: { type: 'heading-three' },
16
+ children: 'Heading three',
17
+ }),
18
+ );
19
+ expect(baseElement).toMatchSnapshot();
20
+ });
21
+
22
+ it('still provides ToolbarButton when showToolbarButton is false', () => {
23
+ expect(
24
+ headingThree({ showToolbarButton: false }).ToolbarButton,
25
+ ).toBeDefined();
26
+ });
27
+ });
@@ -18,4 +18,10 @@ describe('headingTwo plugin', () => {
18
18
  );
19
19
  expect(baseElement).toMatchSnapshot();
20
20
  });
21
+
22
+ it('still provides ToolbarButton when showToolbarButton is false', () => {
23
+ expect(
24
+ headingTwo({ showToolbarButton: false }).ToolbarButton,
25
+ ).toBeDefined();
26
+ });
21
27
  });
@@ -0,0 +1,48 @@
1
+ import React from 'react';
2
+ import { Transforms } from 'slate';
3
+ import { useSlate } from 'slate-react';
4
+ import {
5
+ createPlugin,
6
+ postMessage,
7
+ addMessageListener,
8
+ isBlockActive,
9
+ isList,
10
+ } from '../helpers';
11
+ import Toolbar from '../components/Toolbar';
12
+
13
+ const makeToolbarButton = (name, options) => () => {
14
+ const editor = useSlate();
15
+ if (options.length === 0) {
16
+ return null;
17
+ }
18
+ const currentValue =
19
+ options.find(({ value }) => isBlockActive(editor, value))?.value ??
20
+ options[0]?.value;
21
+
22
+ return (
23
+ <Toolbar.Dropdown
24
+ options={options}
25
+ currentValue={currentValue}
26
+ onChange={(event) => {
27
+ event.preventDefault();
28
+ postMessage(name, { type: event.target.value }, editor);
29
+ }}
30
+ />
31
+ );
32
+ };
33
+
34
+ export default ({ name, options = [] }) =>
35
+ createPlugin({
36
+ name,
37
+ ToolbarButton: makeToolbarButton(name, options),
38
+ handleMessage: addMessageListener(name, ({ editor, data }) => {
39
+ if (!data?.type) {
40
+ return;
41
+ }
42
+ Transforms.unwrapNodes(editor, {
43
+ match: isList,
44
+ split: true,
45
+ });
46
+ Transforms.setNodes(editor, { type: data.type });
47
+ }),
48
+ });
@@ -0,0 +1,62 @@
1
+ import React from 'react';
2
+ import { Transforms } from 'slate';
3
+ import { useSlate } from 'slate-react';
4
+ import {
5
+ createPlugin,
6
+ isBlockActive,
7
+ makeRenderElement,
8
+ addMessageListener,
9
+ postMessage,
10
+ isList,
11
+ } from '../helpers';
12
+ import Toolbar from '../components/Toolbar';
13
+ import Icon from '../components/Icon';
14
+ import { HEADING_FOUR } from '../constants';
15
+
16
+ const renderElement = makeRenderElement(
17
+ HEADING_FOUR,
18
+ ({ attributes, children }) => <h4 {...attributes}>{children}</h4>,
19
+ );
20
+
21
+ const handleMessage = addMessageListener(HEADING_FOUR, ({ editor }) => {
22
+ const isActive = isBlockActive(editor, HEADING_FOUR);
23
+
24
+ // Unwrap all the items of any list
25
+ Transforms.unwrapNodes(editor, {
26
+ match: isList,
27
+ split: true,
28
+ });
29
+
30
+ Transforms.setNodes(editor, {
31
+ type: isActive ? 'paragraph' : HEADING_FOUR,
32
+ });
33
+ });
34
+
35
+ const ToolbarButton = ({ showToolbarButton }) => {
36
+ const editor = useSlate();
37
+
38
+ if (!showToolbarButton) return null;
39
+
40
+ return (
41
+ <Toolbar.Button
42
+ active={isBlockActive(editor, HEADING_FOUR)}
43
+ onMouseDown={(event) => {
44
+ event.preventDefault();
45
+ postMessage(HEADING_FOUR, {}, editor);
46
+ }}
47
+ >
48
+ <Icon>looks_four</Icon>
49
+ </Toolbar.Button>
50
+ );
51
+ };
52
+
53
+ export default ({ showToolbarButton = true } = {}) =>
54
+ createPlugin({
55
+ name: HEADING_FOUR,
56
+ renderElement,
57
+ handleMessage,
58
+ ToolbarButton: () => (
59
+ <ToolbarButton showToolbarButton={showToolbarButton} />
60
+ ),
61
+ isActive: (editor) => isBlockActive(editor, HEADING_FOUR),
62
+ });
@@ -32,9 +32,11 @@ const handleMessage = addMessageListener(HEADING_ONE, ({ editor }) => {
32
32
  });
33
33
  });
34
34
 
35
- const ToolbarButton = () => {
35
+ const ToolbarButton = ({ showToolbarButton }) => {
36
36
  const editor = useSlate();
37
37
 
38
+ if (!showToolbarButton) return null;
39
+
38
40
  return (
39
41
  <Toolbar.Button
40
42
  active={isBlockActive(editor, HEADING_ONE)}
@@ -48,11 +50,13 @@ const ToolbarButton = () => {
48
50
  );
49
51
  };
50
52
 
51
- export default () =>
53
+ export default ({ showToolbarButton = true } = {}) =>
52
54
  createPlugin({
53
55
  name: HEADING_ONE,
54
56
  renderElement,
55
57
  handleMessage,
56
- ToolbarButton,
58
+ ToolbarButton: () => (
59
+ <ToolbarButton showToolbarButton={showToolbarButton} />
60
+ ),
57
61
  isActive: (editor) => isBlockActive(editor, HEADING_ONE),
58
62
  });
@@ -0,0 +1,62 @@
1
+ import React from 'react';
2
+ import { Transforms } from 'slate';
3
+ import { useSlate } from 'slate-react';
4
+ import {
5
+ createPlugin,
6
+ isBlockActive,
7
+ makeRenderElement,
8
+ addMessageListener,
9
+ postMessage,
10
+ isList,
11
+ } from '../helpers';
12
+ import Toolbar from '../components/Toolbar';
13
+ import Icon from '../components/Icon';
14
+ import { HEADING_THREE } from '../constants';
15
+
16
+ const renderElement = makeRenderElement(
17
+ HEADING_THREE,
18
+ ({ attributes, children }) => <h3 {...attributes}>{children}</h3>,
19
+ );
20
+
21
+ const handleMessage = addMessageListener(HEADING_THREE, ({ editor }) => {
22
+ const isActive = isBlockActive(editor, HEADING_THREE);
23
+
24
+ // Unwrap all the items of any list
25
+ Transforms.unwrapNodes(editor, {
26
+ match: isList,
27
+ split: true,
28
+ });
29
+
30
+ Transforms.setNodes(editor, {
31
+ type: isActive ? 'paragraph' : HEADING_THREE,
32
+ });
33
+ });
34
+
35
+ const ToolbarButton = ({ showToolbarButton }) => {
36
+ const editor = useSlate();
37
+
38
+ if (!showToolbarButton) return null;
39
+
40
+ return (
41
+ <Toolbar.Button
42
+ active={isBlockActive(editor, HEADING_THREE)}
43
+ onMouseDown={(event) => {
44
+ event.preventDefault();
45
+ postMessage(HEADING_THREE, {}, editor);
46
+ }}
47
+ >
48
+ <Icon>looks_three</Icon>
49
+ </Toolbar.Button>
50
+ );
51
+ };
52
+
53
+ export default ({ showToolbarButton = true } = {}) =>
54
+ createPlugin({
55
+ name: HEADING_THREE,
56
+ renderElement,
57
+ handleMessage,
58
+ ToolbarButton: () => (
59
+ <ToolbarButton showToolbarButton={showToolbarButton} />
60
+ ),
61
+ isActive: (editor) => isBlockActive(editor, HEADING_THREE),
62
+ });
@@ -32,9 +32,11 @@ const handleMessage = addMessageListener(HEADING_TWO, ({ editor }) => {
32
32
  });
33
33
  });
34
34
 
35
- const ToolbarButton = () => {
35
+ const ToolbarButton = ({ showToolbarButton }) => {
36
36
  const editor = useSlate();
37
37
 
38
+ if (!showToolbarButton) return null;
39
+
38
40
  return (
39
41
  <Toolbar.Button
40
42
  active={isBlockActive(editor, HEADING_TWO)}
@@ -48,11 +50,13 @@ const ToolbarButton = () => {
48
50
  );
49
51
  };
50
52
 
51
- export default () =>
53
+ export default ({ showToolbarButton = true } = {}) =>
52
54
  createPlugin({
53
55
  name: HEADING_TWO,
54
56
  renderElement,
55
57
  handleMessage,
56
- ToolbarButton,
58
+ ToolbarButton: () => (
59
+ <ToolbarButton showToolbarButton={showToolbarButton} />
60
+ ),
57
61
  isActive: (editor) => isBlockActive(editor, HEADING_TWO),
58
62
  });
@@ -12,5 +12,8 @@ export { default as link } from './link';
12
12
  export { default as cursorTracker } from './cursorTracker';
13
13
  export { default as headingOne } from './headingOne';
14
14
  export { default as headingTwo } from './headingTwo';
15
+ export { default as headingThree } from './headingThree';
16
+ export { default as headingFour } from './headingFour';
17
+ export { default as dropdown } from './dropdown';
15
18
  export { default as editorResetter } from './editorResetter';
16
19
  export { default as editorBuiltInMethod } from './editorBuiltInMethod';
@@ -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
 
@@ -49,6 +47,12 @@ const defaultRules = {
49
47
  case 'heading-two':
50
48
  return <h2 key={key}>{children}</h2>;
51
49
 
50
+ case 'heading-three':
51
+ return <h3 key={key}>{children}</h3>;
52
+
53
+ case 'heading-four':
54
+ return <h4 key={key}>{children}</h4>;
55
+
52
56
  case 'link':
53
57
  // eslint-disable-next-line no-case-declarations
54
58
  const urlFromNode = getUrlFromNode(node);