@pie-lib/editable-html-tip-tap 1.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.
Files changed (167) hide show
  1. package/CHANGELOG.json +32 -0
  2. package/CHANGELOG.md +2280 -0
  3. package/lib/__tests__/editor.test.js +470 -0
  4. package/lib/__tests__/serialization.test.js +246 -0
  5. package/lib/__tests__/utils.js +106 -0
  6. package/lib/block-tags.js +25 -0
  7. package/lib/constants.js +16 -0
  8. package/lib/editor.js +1356 -0
  9. package/lib/extensions/MediaView.js +112 -0
  10. package/lib/extensions/characters.js +65 -0
  11. package/lib/extensions/component.js +325 -0
  12. package/lib/extensions/css.js +252 -0
  13. package/lib/extensions/custom-toolbar-wrapper.js +124 -0
  14. package/lib/extensions/image.js +106 -0
  15. package/lib/extensions/math.js +330 -0
  16. package/lib/extensions/media.js +276 -0
  17. package/lib/extensions/responseArea.js +278 -0
  18. package/lib/index.js +1213 -0
  19. package/lib/old-index.js +269 -0
  20. package/lib/parse-html.js +16 -0
  21. package/lib/plugins/characters/custom-popper.js +73 -0
  22. package/lib/plugins/characters/index.js +305 -0
  23. package/lib/plugins/characters/utils.js +381 -0
  24. package/lib/plugins/css/icons/index.js +37 -0
  25. package/lib/plugins/css/index.js +390 -0
  26. package/lib/plugins/customPlugin/index.js +114 -0
  27. package/lib/plugins/html/icons/index.js +38 -0
  28. package/lib/plugins/html/index.js +81 -0
  29. package/lib/plugins/image/__tests__/component.test.js +51 -0
  30. package/lib/plugins/image/__tests__/image-toolbar-logic.test.js +56 -0
  31. package/lib/plugins/image/__tests__/image-toolbar.test.js +26 -0
  32. package/lib/plugins/image/__tests__/index.test.js +98 -0
  33. package/lib/plugins/image/__tests__/insert-image-handler.test.js +125 -0
  34. package/lib/plugins/image/__tests__/mock-change.js +25 -0
  35. package/lib/plugins/image/alt-dialog.js +129 -0
  36. package/lib/plugins/image/component.js +419 -0
  37. package/lib/plugins/image/image-toolbar.js +177 -0
  38. package/lib/plugins/image/index.js +263 -0
  39. package/lib/plugins/image/insert-image-handler.js +117 -0
  40. package/lib/plugins/index.js +413 -0
  41. package/lib/plugins/list/__tests__/index.test.js +79 -0
  42. package/lib/plugins/list/index.js +334 -0
  43. package/lib/plugins/math/__tests__/index.test.js +300 -0
  44. package/lib/plugins/math/index.js +454 -0
  45. package/lib/plugins/media/__tests__/index.test.js +71 -0
  46. package/lib/plugins/media/index.js +387 -0
  47. package/lib/plugins/media/media-dialog.js +709 -0
  48. package/lib/plugins/media/media-toolbar.js +101 -0
  49. package/lib/plugins/media/media-wrapper.js +93 -0
  50. package/lib/plugins/rendering/index.js +46 -0
  51. package/lib/plugins/respArea/drag-in-the-blank/choice.js +289 -0
  52. package/lib/plugins/respArea/drag-in-the-blank/index.js +94 -0
  53. package/lib/plugins/respArea/explicit-constructed-response/index.js +120 -0
  54. package/lib/plugins/respArea/icons/index.js +95 -0
  55. package/lib/plugins/respArea/index.js +341 -0
  56. package/lib/plugins/respArea/inline-dropdown/index.js +126 -0
  57. package/lib/plugins/respArea/math-templated/index.js +130 -0
  58. package/lib/plugins/respArea/utils.js +125 -0
  59. package/lib/plugins/table/CustomTablePlugin.js +133 -0
  60. package/lib/plugins/table/__tests__/index.test.js +442 -0
  61. package/lib/plugins/table/__tests__/table-toolbar.test.js +54 -0
  62. package/lib/plugins/table/icons/index.js +69 -0
  63. package/lib/plugins/table/index.js +483 -0
  64. package/lib/plugins/table/table-toolbar.js +187 -0
  65. package/lib/plugins/textAlign/icons/index.js +194 -0
  66. package/lib/plugins/textAlign/index.js +34 -0
  67. package/lib/plugins/toolbar/__tests__/default-toolbar.test.js +128 -0
  68. package/lib/plugins/toolbar/__tests__/editor-and-toolbar.test.js +51 -0
  69. package/lib/plugins/toolbar/__tests__/toolbar-buttons.test.js +54 -0
  70. package/lib/plugins/toolbar/__tests__/toolbar.test.js +120 -0
  71. package/lib/plugins/toolbar/default-toolbar.js +229 -0
  72. package/lib/plugins/toolbar/done-button.js +53 -0
  73. package/lib/plugins/toolbar/editor-and-toolbar.js +286 -0
  74. package/lib/plugins/toolbar/index.js +34 -0
  75. package/lib/plugins/toolbar/toolbar-buttons.js +194 -0
  76. package/lib/plugins/toolbar/toolbar.js +376 -0
  77. package/lib/plugins/utils.js +62 -0
  78. package/lib/serialization.js +677 -0
  79. package/lib/shared/alert-dialog.js +75 -0
  80. package/lib/theme.js +9 -0
  81. package/package.json +69 -0
  82. package/src/__tests__/editor.test.jsx +363 -0
  83. package/src/__tests__/serialization.test.js +291 -0
  84. package/src/__tests__/utils.js +36 -0
  85. package/src/block-tags.js +17 -0
  86. package/src/constants.js +7 -0
  87. package/src/editor.jsx +1197 -0
  88. package/src/extensions/characters.js +46 -0
  89. package/src/extensions/component.jsx +294 -0
  90. package/src/extensions/css.js +217 -0
  91. package/src/extensions/custom-toolbar-wrapper.jsx +100 -0
  92. package/src/extensions/image.js +55 -0
  93. package/src/extensions/math.js +259 -0
  94. package/src/extensions/media.js +182 -0
  95. package/src/extensions/responseArea.js +205 -0
  96. package/src/index.jsx +1462 -0
  97. package/src/old-index.jsx +162 -0
  98. package/src/parse-html.js +8 -0
  99. package/src/plugins/README.md +27 -0
  100. package/src/plugins/characters/custom-popper.js +48 -0
  101. package/src/plugins/characters/index.jsx +284 -0
  102. package/src/plugins/characters/utils.js +447 -0
  103. package/src/plugins/css/icons/index.jsx +17 -0
  104. package/src/plugins/css/index.jsx +340 -0
  105. package/src/plugins/customPlugin/index.jsx +85 -0
  106. package/src/plugins/html/icons/index.jsx +19 -0
  107. package/src/plugins/html/index.jsx +72 -0
  108. package/src/plugins/image/__tests__/__snapshots__/component.test.jsx.snap +51 -0
  109. package/src/plugins/image/__tests__/__snapshots__/image-toolbar-logic.test.jsx.snap +27 -0
  110. package/src/plugins/image/__tests__/__snapshots__/image-toolbar.test.jsx.snap +44 -0
  111. package/src/plugins/image/__tests__/component.test.jsx +41 -0
  112. package/src/plugins/image/__tests__/image-toolbar-logic.test.jsx +42 -0
  113. package/src/plugins/image/__tests__/image-toolbar.test.jsx +11 -0
  114. package/src/plugins/image/__tests__/index.test.js +95 -0
  115. package/src/plugins/image/__tests__/insert-image-handler.test.js +113 -0
  116. package/src/plugins/image/__tests__/mock-change.js +15 -0
  117. package/src/plugins/image/alt-dialog.jsx +82 -0
  118. package/src/plugins/image/component.jsx +343 -0
  119. package/src/plugins/image/image-toolbar.jsx +100 -0
  120. package/src/plugins/image/index.jsx +227 -0
  121. package/src/plugins/image/insert-image-handler.js +79 -0
  122. package/src/plugins/index.jsx +377 -0
  123. package/src/plugins/list/__tests__/index.test.js +54 -0
  124. package/src/plugins/list/index.jsx +305 -0
  125. package/src/plugins/math/__tests__/__snapshots__/index.test.jsx.snap +48 -0
  126. package/src/plugins/math/__tests__/index.test.jsx +245 -0
  127. package/src/plugins/math/index.jsx +379 -0
  128. package/src/plugins/media/__tests__/index.test.js +75 -0
  129. package/src/plugins/media/index.jsx +325 -0
  130. package/src/plugins/media/media-dialog.js +624 -0
  131. package/src/plugins/media/media-toolbar.jsx +56 -0
  132. package/src/plugins/media/media-wrapper.jsx +43 -0
  133. package/src/plugins/rendering/index.js +31 -0
  134. package/src/plugins/respArea/drag-in-the-blank/choice.jsx +215 -0
  135. package/src/plugins/respArea/drag-in-the-blank/index.jsx +70 -0
  136. package/src/plugins/respArea/explicit-constructed-response/index.jsx +92 -0
  137. package/src/plugins/respArea/icons/index.jsx +71 -0
  138. package/src/plugins/respArea/index.jsx +299 -0
  139. package/src/plugins/respArea/inline-dropdown/index.jsx +108 -0
  140. package/src/plugins/respArea/math-templated/index.jsx +104 -0
  141. package/src/plugins/respArea/utils.jsx +90 -0
  142. package/src/plugins/table/CustomTablePlugin.js +113 -0
  143. package/src/plugins/table/__tests__/__snapshots__/table-toolbar.test.jsx.snap +44 -0
  144. package/src/plugins/table/__tests__/index.test.jsx +401 -0
  145. package/src/plugins/table/__tests__/table-toolbar.test.jsx +42 -0
  146. package/src/plugins/table/icons/index.jsx +53 -0
  147. package/src/plugins/table/index.jsx +427 -0
  148. package/src/plugins/table/table-toolbar.jsx +136 -0
  149. package/src/plugins/textAlign/icons/index.jsx +114 -0
  150. package/src/plugins/textAlign/index.jsx +23 -0
  151. package/src/plugins/toolbar/__tests__/__snapshots__/default-toolbar.test.jsx.snap +923 -0
  152. package/src/plugins/toolbar/__tests__/__snapshots__/editor-and-toolbar.test.jsx.snap +20 -0
  153. package/src/plugins/toolbar/__tests__/__snapshots__/toolbar-buttons.test.jsx.snap +36 -0
  154. package/src/plugins/toolbar/__tests__/__snapshots__/toolbar.test.jsx.snap +46 -0
  155. package/src/plugins/toolbar/__tests__/default-toolbar.test.jsx +94 -0
  156. package/src/plugins/toolbar/__tests__/editor-and-toolbar.test.jsx +37 -0
  157. package/src/plugins/toolbar/__tests__/toolbar-buttons.test.jsx +51 -0
  158. package/src/plugins/toolbar/__tests__/toolbar.test.jsx +106 -0
  159. package/src/plugins/toolbar/default-toolbar.jsx +206 -0
  160. package/src/plugins/toolbar/done-button.jsx +38 -0
  161. package/src/plugins/toolbar/editor-and-toolbar.jsx +257 -0
  162. package/src/plugins/toolbar/index.jsx +23 -0
  163. package/src/plugins/toolbar/toolbar-buttons.jsx +138 -0
  164. package/src/plugins/toolbar/toolbar.jsx +338 -0
  165. package/src/plugins/utils.js +31 -0
  166. package/src/serialization.jsx +621 -0
  167. package/src/theme.js +1 -0
@@ -0,0 +1,20 @@
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`toolbar renders 1`] = `
4
+ <div
5
+ className="root"
6
+ >
7
+ <div
8
+ className="editorHolder editorInFocus"
9
+ >
10
+ <div
11
+ className=""
12
+ >
13
+ children
14
+ </div>
15
+ </div>
16
+ <div>
17
+ ---- toolbar ------
18
+ </div>
19
+ </div>
20
+ `;
@@ -0,0 +1,36 @@
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`Button renders 1`] = `
4
+ <button
5
+ className=""
6
+ onKeyDown={[Function]}
7
+ onMouseDown={[Function]}
8
+ tabIndex={0}
9
+ >
10
+ children
11
+ </button>
12
+ `;
13
+
14
+ exports[`MarkButton renders active 1`] = `
15
+ <button
16
+ aria-pressed={true}
17
+ className="button active"
18
+ onKeyDown={[Function]}
19
+ onMouseDown={[Function]}
20
+ tabIndex={0}
21
+ >
22
+ children
23
+ </button>
24
+ `;
25
+
26
+ exports[`MarkButton renders not active 1`] = `
27
+ <button
28
+ aria-pressed={false}
29
+ className="button"
30
+ onKeyDown={[Function]}
31
+ onMouseDown={[Function]}
32
+ tabIndex={0}
33
+ >
34
+ children
35
+ </button>
36
+ `;
@@ -0,0 +1,46 @@
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`toolbar default renders default toolbar 1`] = `
4
+ <div
5
+ className="toolbar pie-toolbar"
6
+ onClick={[Function]}
7
+ style={Object {}}
8
+ >
9
+ <div
10
+ className="DefaultToolbar-defaultToolbar-1"
11
+ onFocus={[Function]}
12
+ tabIndex="1"
13
+ >
14
+ <div
15
+ className="DefaultToolbar-buttonsContainer-2"
16
+ />
17
+ <div
18
+ ariaLabel="Done"
19
+ className="RawDoneButton-iconRoot-3"
20
+ />
21
+ </div>
22
+ <div
23
+ className="shared"
24
+ />
25
+ </div>
26
+ `;
27
+
28
+ exports[`toolbar renders custom toolbar 1`] = `
29
+ <div
30
+ className="toolbar pie-toolbar"
31
+ onClick={[Function]}
32
+ style={Object {}}
33
+ >
34
+ <div>
35
+ --------- custom toolbar -----------
36
+ </div>
37
+ <div
38
+ className="shared"
39
+ >
40
+ <div
41
+ ariaLabel="Delete"
42
+ className="iconRoot"
43
+ />
44
+ </div>
45
+ </div>
46
+ `;
@@ -0,0 +1,94 @@
1
+ import { classObject, mockIconButton, mockMathInput } from '../../../__tests__/utils';
2
+ import { shallow } from 'enzyme';
3
+ import React from 'react';
4
+
5
+ import { Data, Value, Inline } from 'slate';
6
+ import { DefaultToolbar, ToolbarButton } from '../default-toolbar';
7
+ import { DoneButton } from '../done-button';
8
+ import debug from 'debug';
9
+ import renderer from 'react-test-renderer';
10
+
11
+ mockMathInput();
12
+
13
+ jest.mock('@material-ui/core/IconButton', () => {
14
+ return (props) => <div className={props.className} style={props.style} ariaLabel={props['aria-label']} />;
15
+ });
16
+
17
+ let node = Inline.fromJSON({ type: 'i' });
18
+ let value;
19
+ const log = debug('@pie-lib:editable-html:test:toolbar');
20
+
21
+ describe('default-toolbar', () => {
22
+ let w;
23
+ let onDone = jest.fn();
24
+ let onChange = jest.fn();
25
+ const wrapper = (extras) => {
26
+ const defaults = {
27
+ classes: {},
28
+ value: Value.fromJSON({}),
29
+ plugins: [],
30
+ className: 'className',
31
+ onDone,
32
+ onChange,
33
+ deletable: false,
34
+ showDone: true,
35
+ };
36
+ const props = { ...defaults, ...extras };
37
+ return shallow(<DefaultToolbar {...props} />);
38
+ };
39
+
40
+ describe('snapshot', () => {
41
+ it('renders', () => {
42
+ expect(w).toMatchSnapshot();
43
+ });
44
+ it('renders 1 plugins', () => {
45
+ w = wrapper({
46
+ plugins: [{ toolbar: {}, name: 'plugin-one' }],
47
+ });
48
+ expect(w).toMatchSnapshot();
49
+ expect(w.find(ToolbarButton)).toHaveLength(1);
50
+ });
51
+ it('renders 2 plugins', () => {
52
+ w = wrapper({
53
+ plugins: [
54
+ { toolbar: {}, name: 'plugin-one' },
55
+ { toolbar: {}, name: 'plugin-two' },
56
+ ],
57
+ });
58
+ expect(w).toMatchSnapshot();
59
+ expect(w.find(ToolbarButton)).toHaveLength(2);
60
+ });
61
+ it('renders 1 plugins, 1 is disabled', () => {
62
+ w = wrapper({
63
+ pluginProps: {
64
+ 'plugin-one': {
65
+ disabled: true,
66
+ },
67
+ },
68
+ plugins: [
69
+ { toolbar: {}, name: 'plugin-one' },
70
+ { toolbar: {}, name: 'plugin-two' },
71
+ ],
72
+ });
73
+ expect(w).toMatchSnapshot();
74
+ expect(w.find(ToolbarButton)).toHaveLength(1);
75
+ });
76
+ it('renders without done button', () => {
77
+ w = wrapper({
78
+ deletable: false,
79
+ });
80
+
81
+ expect(w).toMatchSnapshot();
82
+ expect(w.find(DoneButton)).toHaveLength(1);
83
+ });
84
+ it('renders with done button', () => {
85
+ w = wrapper({
86
+ deletable: true,
87
+ });
88
+
89
+ expect(w).toMatchSnapshot();
90
+ expect(w.find(DoneButton)).toHaveLength(0);
91
+ });
92
+ });
93
+ describe('logic', () => {});
94
+ });
@@ -0,0 +1,37 @@
1
+ import { classObject, mockIconButton } from '../../../__tests__/utils';
2
+
3
+ import { Data, Value } from 'slate';
4
+ import { EditorAndToolbar, getClonedChildren } from '../editor-and-toolbar';
5
+ import React from 'react';
6
+ import debug from 'debug';
7
+ import renderer from 'react-test-renderer';
8
+
9
+ jest.mock('../toolbar', () => () => <div>---- toolbar ------ </div>);
10
+
11
+ mockIconButton();
12
+
13
+ const log = debug('@pie-lib:editable-html:test:editor-and-toolbar');
14
+
15
+ describe('toolbar', () => {
16
+ let onDelete, classes;
17
+
18
+ beforeEach(() => {
19
+ onDelete = jest.fn();
20
+ classes = classObject('root', 'editorHolder', 'editorInFocus');
21
+ });
22
+
23
+ it('renders', () => {
24
+ const value = Value.fromJSON({});
25
+ Object.defineProperty(value, 'isFocused', { get: jest.fn(() => true) });
26
+
27
+ const tree = renderer
28
+ .create(
29
+ <EditorAndToolbar classes={classes} value={value} plugins={[]} onDone={jest.fn()} onChange={jest.fn()}>
30
+ children
31
+ </EditorAndToolbar>,
32
+ )
33
+ .toJSON();
34
+ log('tree: ', JSON.stringify(tree, null, ' '));
35
+ expect(tree).toMatchSnapshot();
36
+ });
37
+ });
@@ -0,0 +1,51 @@
1
+ import { classObject, mockIconButton } from '../../../__tests__/utils';
2
+
3
+ import { Data } from 'slate';
4
+ import { RawMarkButton, RawButton } from '../toolbar-buttons';
5
+ import React from 'react';
6
+ import debug from 'debug';
7
+ import renderer from 'react-test-renderer';
8
+
9
+ mockIconButton();
10
+
11
+ const log = debug('@pie-lib:editable-html:test:editor-and-toolbar');
12
+
13
+ describe('Button', () => {
14
+ it('renders', () => {
15
+ const classes = classObject('root');
16
+ const tree = renderer
17
+ .create(
18
+ <RawButton onClick={jest.fn()} classes={classes}>
19
+ children
20
+ </RawButton>,
21
+ )
22
+ .toJSON();
23
+ expect(tree).toMatchSnapshot();
24
+ });
25
+ });
26
+
27
+ describe('MarkButton', () => {
28
+ const classes = classObject('button', 'root', 'active');
29
+
30
+ it('renders not active', () => {
31
+ const tree = renderer
32
+ .create(
33
+ <RawMarkButton mark={'i'} onToggle={jest.fn()} active={false} classes={classes}>
34
+ children
35
+ </RawMarkButton>,
36
+ )
37
+ .toJSON();
38
+ expect(tree).toMatchSnapshot();
39
+ });
40
+
41
+ it('renders active', () => {
42
+ const tree = renderer
43
+ .create(
44
+ <RawMarkButton mark={'i'} onToggle={jest.fn()} active={true} classes={classes}>
45
+ children
46
+ </RawMarkButton>,
47
+ )
48
+ .toJSON();
49
+ expect(tree).toMatchSnapshot();
50
+ });
51
+ });
@@ -0,0 +1,106 @@
1
+ import { classObject, mockIconButton, mockMathInput } from '../../../__tests__/utils';
2
+ import { shallow } from 'enzyme';
3
+
4
+ import { Data, Value, Inline } from 'slate';
5
+ import { Toolbar, DefaultToolbar } from '../toolbar';
6
+ import React from 'react';
7
+ import debug from 'debug';
8
+ import renderer from 'react-test-renderer';
9
+
10
+ mockMathInput();
11
+
12
+ jest.mock('@material-ui/core/IconButton', () => {
13
+ return (props) => <div className={props.className} style={props.style} ariaLabel={props['aria-label']} />;
14
+ });
15
+
16
+ let node = Inline.fromJSON({ type: 'i' });
17
+ let parentNode = Inline.fromJSON({
18
+ type: 'i',
19
+ });
20
+ let value;
21
+ const log = debug('@pie-lib:editable-html:test:toolbar');
22
+
23
+ describe('toolbar', () => {
24
+ let onDelete, classes, document, toolbarOpts;
25
+
26
+ beforeEach(() => {
27
+ onDelete = jest.fn();
28
+
29
+ toolbarOpts = {
30
+ position: 'bottom',
31
+ alwaysVisible: false,
32
+ };
33
+
34
+ value = Value.fromJSON({});
35
+ document = {
36
+ getClosestInline: jest.fn().mockReturnValue(node),
37
+ getParent: jest.fn().mockReturnValue(),
38
+ };
39
+
40
+ Object.defineProperties(value, {
41
+ isCollapsed: {
42
+ get: jest.fn(() => true),
43
+ },
44
+ startKey: {
45
+ get: jest.fn(() => '1'),
46
+ },
47
+ document: {
48
+ get: jest.fn(() => document),
49
+ },
50
+ });
51
+
52
+ classes = classObject('iconRoot', 'inline', 'toolbar', 'focused', 'shared', 'inline', 'pie-toolbar');
53
+ });
54
+
55
+ test('renders custom toolbar', () => {
56
+ const plugins = [
57
+ {
58
+ deleteNode: () => true,
59
+ toolbar: {
60
+ supports: () => true,
61
+ customToolbar: () => () => <div> --------- custom toolbar ----------- </div>,
62
+ },
63
+ },
64
+ ];
65
+
66
+ const tree = renderer
67
+ .create(
68
+ <Toolbar
69
+ toolbarOpts={toolbarOpts}
70
+ plugins={plugins}
71
+ classes={classes}
72
+ value={value}
73
+ onDone={jest.fn()}
74
+ onChange={jest.fn()}
75
+ />,
76
+ )
77
+ .toJSON();
78
+
79
+ log('tree: ', JSON.stringify(tree, null, ' '));
80
+ expect(tree).toMatchSnapshot();
81
+ });
82
+
83
+ describe('default', () => {
84
+ let plugins;
85
+
86
+ beforeEach(() => {
87
+ plugins = [];
88
+ });
89
+
90
+ test('renders default toolbar', () => {
91
+ const tree = renderer
92
+ .create(
93
+ <Toolbar
94
+ toolbarOpts={toolbarOpts}
95
+ plugins={plugins}
96
+ classes={classes}
97
+ value={value}
98
+ onDone={jest.fn()}
99
+ onChange={jest.fn()}
100
+ />,
101
+ )
102
+ .toJSON();
103
+ expect(tree).toMatchSnapshot();
104
+ });
105
+ });
106
+ });
@@ -0,0 +1,206 @@
1
+ import { DoneButton } from './done-button';
2
+ import PropTypes from 'prop-types';
3
+ import React from 'react';
4
+ import SlatePropTypes from 'slate-prop-types';
5
+
6
+ import { hasBlock, hasMark } from '../utils';
7
+ import { withStyles } from '@material-ui/core/styles';
8
+
9
+ import { Button, MarkButton } from './toolbar-buttons';
10
+ import debug from 'debug';
11
+
12
+ const log = debug('@pie-lib:editable-html:plugins:toolbar');
13
+
14
+ export const ToolbarButton = (props) => {
15
+ const onToggle = () => {
16
+ const c = props.onToggle(props.value.change(), props);
17
+
18
+ props.onChange(c);
19
+ };
20
+
21
+ if (props.isMark) {
22
+ const isActive = hasMark(props.value, props.type);
23
+ const fnToCall =
24
+ props.type === 'css' ? () => props.onClick(props.value, props.onChange, props.getFocusedValue) : onToggle;
25
+
26
+ log('[ToolbarButton] mark:isActive: ', isActive);
27
+
28
+ let ariaLabel;
29
+
30
+ if (props.type === 'sup') {
31
+ ariaLabel = 'Superscript (marks text as superscripted)';
32
+ } else if (props.type === 'sub') {
33
+ ariaLabel = 'Subscript (marks text as subscripted)';
34
+ } else {
35
+ ariaLabel = props.type;
36
+ }
37
+
38
+ return (
39
+ <MarkButton active={isActive} onToggle={fnToCall} mark={props.type} label={ariaLabel}>
40
+ {props.icon}
41
+ </MarkButton>
42
+ );
43
+ }
44
+
45
+ const { disabled } = props;
46
+ const isActive = props.isActive ? props.isActive(props.value, props.type) : hasBlock(props.value, props.type);
47
+
48
+ log('[ToolbarButton] block:isActive: ', isActive);
49
+ const newIcon = React.cloneElement(props.icon);
50
+
51
+ return (
52
+ <Button
53
+ ariaLabel={props.ariaLabel}
54
+ active={isActive}
55
+ disabled={disabled}
56
+ onClick={(event) => props.onClick(props.value, props.onChange, props.getFocusedValue, event)}
57
+ extraStyles={props.buttonStyles}
58
+ >
59
+ {newIcon}
60
+ </Button>
61
+ );
62
+ };
63
+
64
+ ToolbarButton.propTypes = {
65
+ buttonStyles: PropTypes.object,
66
+ disabled: PropTypes.bool,
67
+ icon: PropTypes.any,
68
+ isActive: PropTypes.bool,
69
+ isMark: PropTypes.bool,
70
+ getFocusedValue: PropTypes.func,
71
+ onToggle: PropTypes.func,
72
+ onChange: PropTypes.func,
73
+ onClick: PropTypes.func,
74
+ type: PropTypes.string,
75
+ value: PropTypes.object,
76
+ ariaLabel: PropTypes.string,
77
+ };
78
+
79
+ const isActiveToolbarPlugin = (props) => (plugin) => {
80
+ const isDisabled = (props[plugin.name] || {}).disabled;
81
+
82
+ return plugin && plugin.toolbar && !isDisabled;
83
+ };
84
+
85
+ export const DefaultToolbar = ({
86
+ plugins,
87
+ pluginProps,
88
+ value,
89
+ onChange,
90
+ getFocusedValue,
91
+ onDone,
92
+ classes,
93
+ showDone,
94
+ deletable,
95
+ isHtmlMode,
96
+ doneButtonRef,
97
+ onBlur,
98
+ onFocus,
99
+ }) => {
100
+ pluginProps = {
101
+ // disable HTML plugin by default, at least for now
102
+ html: { disabled: true },
103
+ ...pluginProps,
104
+ };
105
+ let filtered;
106
+
107
+ const handleFocus = (event) => {
108
+ const doneButtonClassName = doneButtonRef?.current?.className;
109
+ const isRawDoneButton = doneButtonClassName && event.target?.closest(`[class*="${doneButtonClassName}"]`);
110
+
111
+ if (onFocus && !isRawDoneButton) {
112
+ onFocus(event);
113
+ }
114
+ };
115
+
116
+ if (isHtmlMode) {
117
+ filtered = plugins
118
+ .filter((plugin) => {
119
+ return isActiveToolbarPlugin(pluginProps)(plugin) && (plugin.name === 'characters' || plugin.name === 'html');
120
+ })
121
+ .map((p) => p.toolbar);
122
+ } else {
123
+ filtered = plugins.filter(isActiveToolbarPlugin(pluginProps)).map((p) => p.toolbar);
124
+ }
125
+
126
+ const isListActive = plugins.some(
127
+ (plugin) =>
128
+ isActiveToolbarPlugin(pluginProps)(plugin) &&
129
+ ['ul_list', 'ol_list'].includes(plugin.name) &&
130
+ plugin.toolbar.isActive(value, plugin.name),
131
+ );
132
+
133
+ const isTableActive = plugins.some(
134
+ (plugin) =>
135
+ isActiveToolbarPlugin(pluginProps)(plugin) &&
136
+ plugin.name === 'table' &&
137
+ plugin.utils &&
138
+ plugin.utils.isSelectionInTable &&
139
+ plugin.utils.isSelectionInTable(value),
140
+ );
141
+
142
+ const isToolbarButtonDisabled = (plugin) => {
143
+ if (plugin.type === 'table') {
144
+ return isListActive;
145
+ } else if (plugin.type === 'ul_list' || plugin.type === 'ol_list') {
146
+ return isTableActive;
147
+ }
148
+ return plugin.disabled;
149
+ };
150
+
151
+ return (
152
+ <div className={classes.defaultToolbar} onFocus={handleFocus} tabIndex="1" onBlur={onBlur}>
153
+ <div className={classes.buttonsContainer}>
154
+ {filtered.map((p, index) => {
155
+ return (
156
+ <ToolbarButton
157
+ {...p}
158
+ key={index}
159
+ value={value}
160
+ onChange={onChange}
161
+ getFocusedValue={getFocusedValue}
162
+ disabled={isToolbarButtonDisabled(p)}
163
+ />
164
+ );
165
+ })}
166
+ </div>
167
+ {showDone && !deletable && <DoneButton doneButtonRef={doneButtonRef} onClick={onDone} />}
168
+ </div>
169
+ );
170
+ };
171
+
172
+ DefaultToolbar.propTypes = {
173
+ classes: PropTypes.object.isRequired,
174
+ plugins: PropTypes.array.isRequired,
175
+ pluginProps: PropTypes.object,
176
+ value: SlatePropTypes.value.isRequired,
177
+ onChange: PropTypes.func.isRequired,
178
+ getFocusedValue: PropTypes.func.isRequired,
179
+ onDone: PropTypes.func.isRequired,
180
+ showDone: PropTypes.bool,
181
+ addArea: PropTypes.bool,
182
+ deletable: PropTypes.bool,
183
+ isHtmlMode: PropTypes.bool,
184
+ doneButtonRef: PropTypes.func,
185
+ onBlur: PropTypes.func,
186
+ onFocus: PropTypes.func,
187
+ };
188
+
189
+ DefaultToolbar.defaultProps = {
190
+ pluginProps: {},
191
+ };
192
+
193
+ const toolbarStyles = () => ({
194
+ defaultToolbar: {
195
+ display: 'flex',
196
+ width: '100%',
197
+ justifyContent: 'space-between',
198
+ },
199
+ buttonsContainer: {
200
+ alignItems: 'center',
201
+ display: 'flex',
202
+ width: '100%',
203
+ },
204
+ });
205
+
206
+ export default withStyles(toolbarStyles)(DefaultToolbar);
@@ -0,0 +1,38 @@
1
+ import React from 'react';
2
+
3
+ import IconButton from '@material-ui/core/IconButton';
4
+ import Check from '@material-ui/icons/Check';
5
+ import { withStyles } from '@material-ui/core/styles';
6
+ import PropTypes from 'prop-types';
7
+
8
+ export const RawDoneButton = ({ classes, onClick, doneButtonRef }) => (
9
+ <IconButton
10
+ aria-label="Done"
11
+ className={classes.iconRoot}
12
+ buttonRef={doneButtonRef}
13
+ onClick={onClick}
14
+ classes={{
15
+ label: classes.label,
16
+ root: classes.iconRoot,
17
+ }}
18
+ >
19
+ <Check />
20
+ </IconButton>
21
+ );
22
+
23
+ RawDoneButton.propTypes = {
24
+ classes: PropTypes.object.isRequired,
25
+ onClick: PropTypes.func,
26
+ doneButtonRef: PropTypes.func,
27
+ };
28
+
29
+ const styles = {
30
+ iconRoot: {
31
+ verticalAlign: 'top',
32
+ width: '28px',
33
+ height: '28px',
34
+ color: 'var(--editable-html-toolbar-check, #00bb00)',
35
+ padding: '4px',
36
+ },
37
+ };
38
+ export const DoneButton = withStyles(styles)(RawDoneButton);