@modusoperandi/licit 1.0.4 → 1.0.7

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 (360) hide show
  1. package/BlockquoteInsertNewLineCommand.js +16 -18
  2. package/BlockquoteInsertNewLineCommand.js.flow +76 -77
  3. package/BlockquoteNodeSpec.js +3 -4
  4. package/BlockquoteNodeSpec.js.flow +29 -30
  5. package/BlockquoteToggleCommand.js +16 -17
  6. package/BlockquoteToggleCommand.js.flow +56 -56
  7. package/BookmarkNodeSpec.js +2 -2
  8. package/BookmarkNodeSpec.js.flow +39 -39
  9. package/BulletListNodeSpec.js.flow +61 -61
  10. package/CZIProseMirror.js.flow +90 -90
  11. package/CodeBlockCommand.js +17 -18
  12. package/CodeBlockCommand.js.flow +65 -65
  13. package/CodeBlockNodeSpec.js.flow +24 -24
  14. package/CodeMarkSpec.js.flow +14 -14
  15. package/ContentPlaceholderPlugin.js +20 -21
  16. package/ContentPlaceholderPlugin.js.flow +185 -187
  17. package/CursorPlaceholderPlugin.js +11 -11
  18. package/CursorPlaceholderPlugin.js.flow +115 -115
  19. package/DocLayoutCommand.js +18 -21
  20. package/DocLayoutCommand.js.flow +97 -99
  21. package/DocNodeSpec.js.flow +64 -64
  22. package/EMMarkSpec.js.flow +14 -14
  23. package/EditorCommands.js.flow +126 -126
  24. package/EditorKeyMap.js +0 -1
  25. package/EditorKeyMap.js.flow +185 -187
  26. package/EditorMarks.js +2 -2
  27. package/EditorMarks.js.flow +71 -71
  28. package/EditorNodes.js +2 -3
  29. package/EditorNodes.js.flow +58 -59
  30. package/EditorPageLayoutPlugin.js +10 -10
  31. package/EditorPageLayoutPlugin.js.flow +67 -67
  32. package/EditorPlugins.js.flow +8 -8
  33. package/EditorSchema.js.flow +12 -12
  34. package/EditorState.js.flow +7 -7
  35. package/FontSizeMarkSpec.js.flow +49 -49
  36. package/FontTypeMarkSpec.js +1 -1
  37. package/FontTypeMarkSpec.js.flow +80 -80
  38. package/HTMLMutator.js +6 -7
  39. package/HTMLMutator.js.flow +59 -59
  40. package/HardBreakNodeSpec.js.flow +15 -15
  41. package/HeadingNodeSpec.js +3 -13
  42. package/HeadingNodeSpec.js.flow +42 -52
  43. package/HistoryRedoCommand.js +15 -16
  44. package/HistoryRedoCommand.js.flow +41 -41
  45. package/HistoryUndoCommand.js +15 -16
  46. package/HistoryUndoCommand.js.flow +41 -41
  47. package/HorizontalRuleCommand.js +15 -16
  48. package/HorizontalRuleCommand.js.flow +71 -71
  49. package/HorizontalRuleNodeSpec.js.flow +39 -39
  50. package/ImageUploadPlaceholderPlugin.js +15 -17
  51. package/ImageUploadPlaceholderPlugin.js.flow +190 -192
  52. package/LinkMarkSpec.js +6 -1
  53. package/LinkMarkSpec.js.flow +37 -32
  54. package/LinkSetURLCommand.js +130 -60
  55. package/LinkSetURLCommand.js.flow +141 -117
  56. package/LinkTooltipPlugin.js +122 -34
  57. package/LinkTooltipPlugin.js.flow +261 -190
  58. package/ListItemInsertNewLineCommand.js +16 -18
  59. package/ListItemInsertNewLineCommand.js.flow +76 -77
  60. package/ListItemMergeCommand.js +24 -24
  61. package/ListItemMergeCommand.js.flow +198 -199
  62. package/ListItemNodeSpec.js.flow +52 -52
  63. package/ListSplitCommand.js +15 -16
  64. package/ListSplitCommand.js.flow +54 -54
  65. package/ListToggleCommand.js +19 -20
  66. package/ListToggleCommand.js.flow +99 -99
  67. package/MarkNames.js.flow +18 -18
  68. package/MarksClearCommand.js +17 -18
  69. package/MarksClearCommand.js.flow +65 -65
  70. package/MathEditCommand.js +17 -19
  71. package/MathEditCommand.js.flow +119 -120
  72. package/MathNodeSpec.js.flow +46 -46
  73. package/NodeNames.js.flow +23 -23
  74. package/OrderedListNodeSpec.js +2 -2
  75. package/OrderedListNodeSpec.js.flow +132 -132
  76. package/ParagraphNodeSpec.js +11 -2
  77. package/ParagraphNodeSpec.js.flow +163 -156
  78. package/ParagraphSpacingCommand.js +21 -23
  79. package/ParagraphSpacingCommand.js.flow +139 -144
  80. package/PrintCommand.js +17 -18
  81. package/PrintCommand.js.flow +53 -53
  82. package/SelectionPlaceholderPlugin.js +11 -11
  83. package/SelectionPlaceholderPlugin.js.flow +131 -131
  84. package/SpacerMarkSpec.js +2 -2
  85. package/SpacerMarkSpec.js.flow +47 -47
  86. package/StrikeMarkSpec.js.flow +21 -21
  87. package/StrongMarkSpec.js.flow +25 -25
  88. package/StyleView.js +5 -7
  89. package/StyleView.js.flow +18 -19
  90. package/TableBackgroundColorCommand.js +34 -23
  91. package/TableBackgroundColorCommand.js.flow +80 -75
  92. package/TableBorderColorCommand.js +34 -23
  93. package/TableBorderColorCommand.js.flow +81 -75
  94. package/TableCellColorCommand.js +20 -21
  95. package/TableCellColorCommand.js.flow +74 -75
  96. package/TableCellMenuPlugin.js +19 -19
  97. package/TableCellMenuPlugin.js.flow +130 -132
  98. package/TableInsertCommand.js +19 -22
  99. package/TableInsertCommand.js.flow +118 -120
  100. package/TableMergeCellsCommand.js +19 -19
  101. package/TableMergeCellsCommand.js.flow +113 -112
  102. package/TableNodesSpecs.js +4 -2
  103. package/TableNodesSpecs.js.flow +80 -78
  104. package/TablePlugins.js.flow +14 -14
  105. package/TableResizePlugin.js +20 -20
  106. package/TableResizePlugin.js.flow +631 -631
  107. package/TextColorMarkSpec.js.flow +35 -35
  108. package/TextHighlightMarkSpec.js.flow +38 -38
  109. package/TextInsertTabSpaceCommand.js +15 -16
  110. package/TextInsertTabSpaceCommand.js.flow +106 -106
  111. package/TextNoWrapMarkSpec.js.flow +14 -14
  112. package/TextNodeSpec.js.flow +7 -7
  113. package/TextSelectionMarkSpec.js.flow +24 -24
  114. package/TextSubMarkSpec.js.flow +20 -20
  115. package/TextSuperMarkSpec.js.flow +20 -20
  116. package/TextUnderlineMarkSpec.js.flow +27 -27
  117. package/Types.js +6 -0
  118. package/Types.js.flow +77 -75
  119. package/WebFontLoader.js +6 -7
  120. package/WebFontLoader.js.flow +22 -22
  121. package/blockQuoteInputRule.js.flow +36 -36
  122. package/bom.xml +17223 -25722
  123. package/browser.js.flow +7 -7
  124. package/buildEditorPlugins.js +6 -7
  125. package/buildEditorPlugins.js.flow +49 -49
  126. package/buildInputRules.js +8 -4
  127. package/buildInputRules.js.flow +85 -81
  128. package/client/CollabConnector.js +20 -20
  129. package/client/CollabConnector.js.flow +90 -90
  130. package/client/EditorConnection.js +10 -9
  131. package/client/EditorConnection.js.flow +324 -323
  132. package/client/Licit.js +78 -115
  133. package/client/Licit.js.flow +611 -643
  134. package/client/Licit.test.js +49 -39
  135. package/client/Licit.test.js.flow +104 -98
  136. package/client/Reporter.js +5 -6
  137. package/client/Reporter.js.flow +37 -37
  138. package/client/SimpleConnector.js +5 -5
  139. package/client/SimpleConnector.js.flow +61 -61
  140. package/client/http.js +0 -6
  141. package/client/http.js.flow +62 -70
  142. package/client/licit.css +12 -12
  143. package/client/throttle.js +8 -2
  144. package/client/throttle.js.flow +27 -27
  145. package/convertFromDOMElement.js +2 -4
  146. package/convertFromDOMElement.js.flow +33 -36
  147. package/convertFromHTML.js +2 -3
  148. package/convertFromHTML.js.flow +15 -17
  149. package/convertFromJSON.js +1 -2
  150. package/convertFromJSON.js.flow +55 -56
  151. package/convertToCSSPTValue.js +6 -10
  152. package/convertToCSSPTValue.js.flow +19 -22
  153. package/convertToJSON.js.flow +7 -7
  154. package/createCommand.js +16 -17
  155. package/createCommand.js.flow +62 -62
  156. package/createEditorKeyMap.js +2 -2
  157. package/createEditorKeyMap.js.flow +94 -94
  158. package/createEmptyEditorState.js +2 -3
  159. package/createEmptyEditorState.js.flow +29 -31
  160. package/createTableResizingPlugin.js +6 -6
  161. package/createTableResizingPlugin.js.flow +86 -86
  162. package/findActionableCell.js.flow +74 -74
  163. package/findActiveMark.js +1 -1
  164. package/findActiveMark.js.flow +32 -32
  165. package/hyphenize.js.flow +17 -17
  166. package/index.d.ts +165 -165
  167. package/index.js.flow +10 -10
  168. package/insertTable.js +0 -3
  169. package/insertTable.js.flow +54 -56
  170. package/isEditorStateEmpty.js.flow +32 -32
  171. package/isTableNode.js.flow +15 -15
  172. package/joinDown.js +1 -3
  173. package/joinDown.js.flow +25 -27
  174. package/joinListNode.js.flow +55 -55
  175. package/joinUp.js +1 -3
  176. package/joinUp.js.flow +37 -39
  177. package/keymaps.js.flow +185 -185
  178. package/lookUpElement.js +2 -1
  179. package/lookUpElement.js.flow +14 -14
  180. package/nodeAt.js.flow +12 -12
  181. package/normalizeHTML.js +3 -1
  182. package/normalizeHTML.js.flow +80 -78
  183. package/package.json +162 -156
  184. package/patchAnchorElements.js +1 -1
  185. package/patchAnchorElements.js.flow +38 -38
  186. package/patchBreakElements.js +3 -2
  187. package/patchBreakElements.js.flow +22 -22
  188. package/patchElementInlineStyles.js +1 -1
  189. package/patchElementInlineStyles.js.flow +92 -92
  190. package/patchListElements.js +4 -4
  191. package/patchListElements.js.flow +276 -276
  192. package/patchMathElements.js +5 -7
  193. package/patchMathElements.js.flow +58 -60
  194. package/patchParagraphElements.js.flow +20 -20
  195. package/patchStyleElements.js +1 -1
  196. package/patchStyleElements.js.flow +197 -196
  197. package/patchTableElements.js +2 -3
  198. package/patchTableElements.js.flow +88 -89
  199. package/rebaseDocWithSteps.js.flow +42 -42
  200. package/sanitizeURL.js +1 -1
  201. package/sanitizeURL.js.flow +13 -13
  202. package/splitListItem.js +3 -3
  203. package/splitListItem.js.flow +191 -191
  204. package/styles.css +19 -19
  205. package/styles0.css +29 -29
  206. package/toClosestFontPtSize.js.flow +22 -22
  207. package/toSafeHTMLDocument.js.flow +9 -9
  208. package/toggleBlockquote.js +4 -5
  209. package/toggleBlockquote.js.flow +101 -108
  210. package/toggleCodeBlock.js +6 -6
  211. package/toggleCodeBlock.js.flow +102 -102
  212. package/ui/AlertInfo.js +15 -16
  213. package/ui/AlertInfo.js.flow +64 -64
  214. package/ui/BookmarkNodeView.js +20 -21
  215. package/ui/BookmarkNodeView.js.flow +66 -66
  216. package/ui/CommandButton.js +15 -16
  217. package/ui/CommandButton.js.flow +68 -68
  218. package/ui/CommandMenu.js +19 -20
  219. package/ui/CommandMenu.js.flow +75 -75
  220. package/ui/CommandMenuButton.js +23 -25
  221. package/ui/CommandMenuButton.js.flow +130 -131
  222. package/ui/CustomEditorView.js +20 -20
  223. package/ui/CustomEditorView.js.flow +29 -28
  224. package/ui/CustomMenu.js +11 -12
  225. package/ui/CustomMenu.js.flow +17 -17
  226. package/ui/CustomMenuItem.js +16 -18
  227. package/ui/CustomMenuItem.js.flow +36 -36
  228. package/ui/CustomNodeView.js +29 -19
  229. package/ui/CustomNodeView.js.flow +207 -200
  230. package/ui/CustomRadioButton.js +18 -21
  231. package/ui/CustomRadioButton.js.flow +64 -65
  232. package/ui/DocLayoutEditor.js +18 -20
  233. package/ui/DocLayoutEditor.js.flow +145 -146
  234. package/ui/Editor.js +30 -28
  235. package/ui/Editor.js.flow +287 -288
  236. package/ui/EditorFrameset.js +13 -14
  237. package/ui/EditorFrameset.js.flow +81 -81
  238. package/ui/EditorToolbar.js +36 -42
  239. package/ui/EditorToolbar.js.flow +199 -218
  240. package/ui/EditorToolbarConfig.js +5 -5
  241. package/ui/EditorToolbarConfig.js.flow +164 -164
  242. package/ui/FontSizeCommandMenuButton.js +14 -15
  243. package/ui/FontSizeCommandMenuButton.js.flow +66 -66
  244. package/ui/FontTypeCommandMenuButton.js +15 -17
  245. package/ui/FontTypeCommandMenuButton.js.flow +60 -61
  246. package/ui/Frag.js +11 -12
  247. package/ui/Frag.js.flow +13 -13
  248. package/ui/Icon.js +21 -24
  249. package/ui/Icon.js.flow +89 -89
  250. package/ui/ImageInlineEditor.js +14 -15
  251. package/ui/ImageInlineEditor.js.flow +67 -67
  252. package/ui/KeyCodes.js.flow +12 -12
  253. package/ui/LinkTooltip.js +68 -34
  254. package/ui/LinkTooltip.js.flow +118 -85
  255. package/ui/LinkURLEditor.js +160 -43
  256. package/ui/LinkURLEditor.js.flow +243 -117
  257. package/ui/ListItemNodeView.js +6 -7
  258. package/ui/ListItemNodeView.js.flow +98 -98
  259. package/ui/ListTypeButton.js +23 -27
  260. package/ui/ListTypeButton.js.flow +128 -131
  261. package/ui/ListTypeCommandButton.js +14 -15
  262. package/ui/ListTypeCommandButton.js.flow +85 -85
  263. package/ui/ListTypeMenu.js +21 -22
  264. package/ui/ListTypeMenu.js.flow +70 -70
  265. package/ui/LoadingIndicator.js +11 -12
  266. package/ui/LoadingIndicator.js.flow +20 -20
  267. package/ui/MathEditor.js +17 -22
  268. package/ui/MathEditor.js.flow +72 -78
  269. package/ui/MathInlineEditor.js +20 -21
  270. package/ui/MathInlineEditor.js.flow +101 -102
  271. package/ui/MathNodeView.js +29 -30
  272. package/ui/MathNodeView.js.flow +176 -186
  273. package/ui/PasteMenu.js +19 -23
  274. package/ui/PasteMenu.js.flow +53 -57
  275. package/ui/ResizeObserver.js +1 -1
  276. package/ui/ResizeObserver.js.flow +106 -106
  277. package/ui/RichTextEditor.js +17 -18
  278. package/ui/RichTextEditor.js.flow +133 -133
  279. package/ui/SelectionObserver.js +8 -9
  280. package/ui/SelectionObserver.js.flow +134 -134
  281. package/ui/TableCellMenu.js +14 -15
  282. package/ui/TableCellMenu.js.flow +51 -51
  283. package/ui/TableGridSizeEditor.js +30 -33
  284. package/ui/TableGridSizeEditor.js.flow +187 -184
  285. package/ui/TableNodeView.js +18 -17
  286. package/ui/TableNodeView.js.flow +25 -25
  287. package/ui/bindScrollHandler.js +2 -1
  288. package/ui/bindScrollHandler.js.flow +46 -46
  289. package/ui/canUseCSSFont.js +5 -4
  290. package/ui/canUseCSSFont.js.flow +42 -43
  291. package/ui/czi-body-layout-editor.css +16 -16
  292. package/ui/czi-bookmark-view.css +10 -10
  293. package/ui/czi-cursor-placeholder.css +36 -36
  294. package/ui/czi-custom-menu-button.css +18 -18
  295. package/ui/czi-custom-menu-item.css +30 -30
  296. package/ui/czi-custom-menu.css +8 -8
  297. package/ui/czi-custom-radio-button.css +80 -80
  298. package/ui/czi-custom-scrollbar.css +21 -21
  299. package/ui/czi-editor-frameset.css +81 -81
  300. package/ui/czi-editor-toolbar.css +122 -122
  301. package/ui/czi-editor.css +217 -220
  302. package/ui/czi-form.css +201 -107
  303. package/ui/czi-frag.css +3 -3
  304. package/ui/czi-heading.css +40 -40
  305. package/ui/czi-icon.css +72 -72
  306. package/ui/czi-image-resize-box.css +165 -165
  307. package/ui/czi-image-upload-editor.css +57 -57
  308. package/ui/czi-image-upload-placeholder.css +50 -50
  309. package/ui/czi-image-url-editor.css +38 -38
  310. package/ui/czi-image-view.css +121 -125
  311. package/ui/czi-indent.css +137 -137
  312. package/ui/czi-inline-editor.css +20 -20
  313. package/ui/czi-link-tooltip.css +112 -71
  314. package/ui/czi-list.css +406 -410
  315. package/ui/czi-loading-indicator.css +66 -111
  316. package/ui/czi-math-view.css +62 -62
  317. package/ui/czi-selection-placeholder.css +24 -24
  318. package/ui/czi-table-cell-menu.css +16 -14
  319. package/ui/czi-table-grid-size-editor.css +37 -37
  320. package/ui/czi-table.css +87 -87
  321. package/ui/czi-vars.css +2 -2
  322. package/ui/findActiveFontSize.js +2 -1
  323. package/ui/findActiveFontSize.js.flow +55 -58
  324. package/ui/findActiveFontType.js +4 -3
  325. package/ui/findActiveFontType.js.flow +35 -38
  326. package/ui/fonts.css +460 -460
  327. package/ui/handleEditorDrop.js +1 -1
  328. package/ui/handleEditorDrop.js.flow +28 -28
  329. package/ui/handleEditorKeyDown.js +1 -1
  330. package/ui/handleEditorKeyDown.js.flow +39 -39
  331. package/ui/handleEditorPaste.js +1 -1
  332. package/ui/handleEditorPaste.js.flow +33 -33
  333. package/ui/htmlElementToRect.js.flow +18 -18
  334. package/ui/icon-font.css +9 -9
  335. package/ui/injectStyleSheet.js +2 -2
  336. package/ui/injectStyleSheet.js.flow +40 -42
  337. package/ui/isElementFullyVisible.js +1 -10
  338. package/ui/isElementFullyVisible.js.flow +14 -26
  339. package/ui/isOffline.js.flow +8 -8
  340. package/ui/isReactClass.js.flow +12 -12
  341. package/ui/listType.css +21 -21
  342. package/ui/mathquill-editor/MathQuillEditor.js +27 -30
  343. package/ui/mathquill-editor/MathQuillEditor.js.flow +158 -159
  344. package/ui/mathquill-editor/MathQuillEditorSymbols.js.flow +483 -483
  345. package/ui/mathquill-editor/MathQuillEditorSymbolsPanel.js +14 -15
  346. package/ui/mathquill-editor/MathQuillEditorSymbolsPanel.js.flow +50 -50
  347. package/ui/mathquill-editor/czi-mathquill-editor-symbols-panel.css +39 -39
  348. package/ui/mathquill-editor/czi-mathquill-editor.css +50 -50
  349. package/ui/mathquill-editor/mathquill-import-kludge.js +1 -8
  350. package/ui/mathquill-editor/mathquill-import-kludge.js.flow +17 -24
  351. package/ui/renderLaTeXAsHTML.js +4 -4
  352. package/ui/renderLaTeXAsHTML.js.flow +46 -46
  353. package/ui/resolveImage.js +5 -6
  354. package/ui/resolveImage.js.flow +121 -123
  355. package/ui/toCSSColor.js.flow +51 -51
  356. package/ui/toCSSLineSpacing.js +2 -3
  357. package/ui/toCSSLineSpacing.js.flow +53 -55
  358. package/ui/toHexColor.js.flow +26 -26
  359. package/ui/uuid.js.flow +9 -9
  360. package/uuid.js.flow +9 -9
@@ -1,643 +1,611 @@
1
- // @flow
2
- import { EditorState, TextSelection, Plugin } from 'prosemirror-state';
3
- import { Node, Schema } from 'prosemirror-model';
4
- import { Transform } from 'prosemirror-transform';
5
- import { EditorView } from 'prosemirror-view';
6
- import * as React from 'react';
7
- import { stringify } from 'flatted';
8
-
9
- import convertFromJSON from '../convertFromJSON.js';
10
- import RichTextEditor from '../ui/RichTextEditor.js';
11
- import uuid from '../uuid.js';
12
- import SimpleConnector from './SimpleConnector.js';
13
- import CollabConnector from './CollabConnector.js';
14
- import {
15
- EMPTY_DOC_JSON
16
- } from '../createEmptyEditorState.js';
17
- import type {
18
- EditorRuntime
19
- } from '../Types.js';
20
- import {
21
- createPopUp,
22
- atViewportCenter,
23
- } from '@modusoperandi/licit-ui-commands';
24
- import AlertInfo from '../ui/AlertInfo.js';
25
- import { SetDocAttrStep } from '@modusoperandi/licit-doc-attrs-step';
26
- import './licit.css';
27
- import DefaultEditorPlugins from '../buildEditorPlugins.js';
28
- import EditorMarks from '../EditorMarks.js';
29
- import EditorNodes from '../EditorNodes.js';
30
- import convertFromHTML from '../convertFromHTML.js';
31
-
32
- export const DataType = Object.freeze({
33
- JSON: Symbol('json'),
34
- HTML: Symbol('html'),
35
- });
36
-
37
- /**
38
- * LICIT properties:
39
- * docID {string} [] Collaborative Doument ID
40
- * collabServiceURL {string} [/collaboration-service] Collaboration Service URL
41
- * debug {boolean} [false] To enable/disable ProseMirror Debug Tools, available only in development.
42
- * width {string} [100%] Width of the editor.
43
- * height {string} [100%] Height of the editor.
44
- * readOnly {boolean} [false] To enable/disable editing mode.
45
- * onChange {@callback} [null] Fires after each significant change.
46
- * @param data {JSON} Modified document data.
47
- * onReady {@callback} [null] Fires when the editor is fully ready.
48
- * @param ref {LICIT} Rerefence of the editor.
49
- * data {JSON|HTML} [null] Document data to be loaded into the editor.
50
- * dataType {JSON|HTML} [JSON] Document data to be loaded into the editor.
51
- * disabled {boolean} [false] Disable the editor.
52
- * embedded {boolean} [false] Disable/Enable inline behaviour.
53
- * plugins [plugins] External Plugins into the editor.
54
- */
55
- class Licit extends React.Component<any, any> {
56
- _runtime: EditorRuntime;
57
- _connector: SimpleConnector;
58
- _clientID: string;
59
- _editorView: EditorView; // This will be handy in updating document's content.
60
- _skipSCU: boolean; // Flag to decide whether to skip shouldComponentUpdate
61
- _defaultEditorSchema: Schema;
62
- _defaultEditorPlugins: Array<Plugin>;
63
- _pasteJSONPlugin: Plugin;
64
-
65
- _popUp = null;
66
-
67
- /**
68
- * Provides access to prosemirror view.
69
- */
70
- get editorView(): EditorView {
71
- return this._editorView;
72
- }
73
-
74
- constructor(props: any, context: any) {
75
- super(props, context);
76
- this.initialize(props);
77
- }
78
-
79
- initialize(props: any) {
80
- this._clientID = uuid();
81
- this._editorView = null;
82
- this._skipSCU = true;
83
-
84
- const noop = function () {};
85
-
86
- // [FS] IRAD-981 2020-06-10
87
- // Component's configurations.
88
- // [FS] IRAD-1552 2021-08-26
89
- // Collaboration server / client should allow string values for document identifiers.
90
- const docID = props.docID || ''; // Empty means collaborative.
91
- const collaborative = docID !== '';
92
- // [FS] IRAD-1553 2021-08-26
93
- // Configurable Collaboration Service URL.
94
- const collabServiceURL = props.collabServiceURL || '/collaboration-service';
95
- const debug = props.debug || false;
96
- // Default width and height to undefined
97
- const width = props.width || undefined;
98
- const height = props.height || undefined;
99
- const onChangeCB =
100
- typeof props.onChange === 'function' ? props.onChange : noop;
101
- const onReadyCB =
102
- typeof props.onReady === 'function' ? props.onReady : noop;
103
- const readOnly = props.readOnly || false;
104
- let data = props.data || null;
105
- const dataType = props.dataType || DataType.JSON;
106
- const disabled = props.disabled || false;
107
- const embedded = props.embedded || false; // [FS] IRAD-996 2020-06-30
108
- // [FS] 2020-07-03
109
- // Handle Image Upload from Angular App
110
- const runtime = props.runtime || null;
111
- const plugins = props.plugins || null;
112
- // This flag decides whether DataType.HTML check is needed when
113
- // changing document. If it forcefully done, it is not needed, otherwise needed.
114
- this.skipDataTypeCheck = false;
115
-
116
- this._defaultEditorSchema = new Schema({
117
- nodes: EditorNodes,
118
- marks: EditorMarks,
119
- });
120
- this._defaultEditorPlugins = new DefaultEditorPlugins(
121
- this._defaultEditorSchema
122
- ).get();
123
- this._pasteJSONPlugin = null;
124
-
125
- const editorState = this.initEditorState(plugins, dataType, data);
126
- data = editorState.doc;
127
-
128
- const setState = this.setState.bind(this);
129
- this._connector = collaborative
130
- ? new CollabConnector(
131
- editorState,
132
- setState,
133
- {
134
- docID,
135
- collabServiceURL,
136
- },
137
- this._defaultEditorSchema,
138
- this._defaultEditorPlugins,
139
- // [FS] IRAD-1578 2021-09-27
140
- this.onReady.bind(this)
141
- )
142
- : new SimpleConnector(editorState, setState);
143
-
144
- this._connector._dataDefined = !!props.data;
145
-
146
- // FS IRAD-989 2020-18-06
147
- // updating properties should automatically render the changes
148
-
149
- this.state = {
150
- docID,
151
- collabServiceURL,
152
- data,
153
- editorState,
154
- width,
155
- height,
156
- readOnly,
157
- onChangeCB,
158
- onReadyCB,
159
- debug,
160
- disabled,
161
- embedded,
162
- runtime,
163
- dataType,
164
- };
165
-
166
- // FS IRAD-1040 2020-26-08
167
- // Get the modified schema from editorstate and send it to collab server
168
- if (this._connector.updateSchema) {
169
- // Use known editorState to update schema.
170
- this._connector.updateSchema(editorState.schema, data);
171
- }
172
- }
173
-
174
- initEditorState(plugins: Array<Plugin>, dataType: DataType, data: any) {
175
- let editorState = null;
176
- const effectivePlugins = this.getEffectivePlugins(
177
- this._defaultEditorSchema,
178
- this._defaultEditorPlugins,
179
- plugins
180
- );
181
- this._pasteJSONPlugin = effectivePlugins.pasteJSONPlugin;
182
- if (DataType.JSON === dataType) {
183
- editorState = convertFromJSON(
184
- data,
185
- null,
186
- effectivePlugins.schema,
187
- effectivePlugins.plugins
188
- );
189
- // [FS] IRAD-1067 2020-09-19
190
- // The editorState will return null if the doc Json is mal-formed
191
- if (null === editorState) {
192
- editorState = convertFromJSON(
193
- EMPTY_DOC_JSON,
194
- null,
195
- effectivePlugins.schema,
196
- effectivePlugins.plugins
197
- );
198
- this.showAlert();
199
- }
200
- } else {
201
- editorState = convertFromHTML(
202
- data,
203
- effectivePlugins.schema,
204
- effectivePlugins.plugins
205
- );
206
- }
207
-
208
- return editorState;
209
- }
210
-
211
- getEffectivePlugins(
212
- schema: Schema,
213
- defaultPlugins: Array<Plugin>,
214
- plugins: Array<Plugin>
215
- ): { plugins: Array<Plugin>, schema: Schema, pasteJSONPlugin: Plugin } {
216
- const effectivePlugins = defaultPlugins;
217
- let pasteJSONPlugin = null;
218
-
219
- if (plugins) {
220
- for (const p of plugins) {
221
- if (!effectivePlugins.includes(p)) {
222
- effectivePlugins.push(p);
223
- if (p.getEffectiveSchema) {
224
- schema = p.getEffectiveSchema(schema);
225
- }
226
-
227
- if (p.initKeyCommands) {
228
- effectivePlugins.push(p.initKeyCommands());
229
- }
230
-
231
- if (p.insert) {
232
- pasteJSONPlugin = p;
233
- }
234
- }
235
- }
236
- }
237
- return { plugins: effectivePlugins, schema, pasteJSONPlugin };
238
- }
239
-
240
- // [FS] IRAD-1578 2021-09-27
241
- onReady(state: EditorState) {
242
- const collabEditing = this.state.docID !== '';
243
-
244
- if (collabEditing) {
245
- this._editorView && this._editorView.focus();
246
- if (this.state.onReadyCB) {
247
- this.state.onReadyCB(this);
248
- }
249
- }
250
- }
251
-
252
- // [FS] IRAD-1067 2020-09-19
253
- // Alert funtion to show document is corrupted
254
- showAlert() {
255
- const anchor = null;
256
- this._popUp = createPopUp(AlertInfo, null, {
257
- anchor,
258
- position: atViewportCenter,
259
- onClose: (val) => {
260
- if (this._popUp) {
261
- this._popUp = null;
262
- }
263
- },
264
- });
265
- }
266
-
267
- resetCounters(transaction: Transform) {
268
- for (let index = 1; index <= 10; index++) {
269
- const counterVar = 'set-cust-style-counter-' + index;
270
- const setCounterVal = window[counterVar];
271
- if (setCounterVal) {
272
- delete window[counterVar];
273
- }
274
- }
275
- this.setCounterFlags(transaction, true);
276
- }
277
-
278
- setCounterFlags(transaction: Transform, reset: boolean) {
279
- let modified = false;
280
- let counterFlags = null;
281
- const existingCFlags = transaction.doc.attrs.counterFlags;
282
- if (reset && !existingCFlags) {
283
- return;
284
- }
285
-
286
- for (let index = 1; index <= 10; index++) {
287
- const counterVar = 'set-cust-style-counter-' + index;
288
-
289
- const setCounterVal = window[counterVar];
290
- if (setCounterVal) {
291
- if (!counterFlags) {
292
- counterFlags = {};
293
- }
294
- counterFlags[counterVar] = true;
295
-
296
- if (!existingCFlags) {
297
- modified = true;
298
- }
299
- }
300
- if (!modified) {
301
- if (existingCFlags) {
302
- if (setCounterVal) {
303
- modified = undefined == existingCFlags[counterVar];
304
- } else {
305
- modified = undefined != existingCFlags[counterVar];
306
- }
307
- } else {
308
- modified = setCounterVal;
309
- }
310
- }
311
- }
312
-
313
- if (modified) {
314
- const tr = this._editorView.state.tr.step(
315
- new SetDocAttrStep('counterFlags', counterFlags)
316
- );
317
- this._editorView.dispatch(tr);
318
- }
319
- }
320
-
321
- getDeletedArtifactIds() {
322
- if (this._connector.getDeletedArtifactIds) {
323
- this._connector.getDeletedArtifactIds(this.state.editorState.schema);
324
- }
325
- }
326
-
327
- isNodeHasAttribute(node: Node, attrName: string) {
328
- return node.attrs && node.attrs[attrName];
329
- }
330
-
331
- getDocument(content: any, editorState: EditorState, dataType: DataType) {
332
- let document = null;
333
- const { schema } = editorState;
334
-
335
- if (DataType.JSON === dataType || this.skipDataTypeCheck) {
336
- document = schema.nodeFromJSON(content ? content : EMPTY_DOC_JSON);
337
- } else {
338
- const tempEState = convertFromHTML(
339
- content ? content : '',
340
- schema,
341
- editorState.plugins
342
- );
343
- document = tempEState.doc;
344
- }
345
-
346
- return document;
347
- }
348
-
349
- insertJSON = (json: { [key: string]: any }): void => {
350
- if (this._pasteJSONPlugin && this._pasteJSONPlugin.insert) {
351
- this._pasteJSONPlugin.insert(json, this._editorView);
352
- }
353
- };
354
-
355
- setContent = (content: any = {}, dataType: DataType): void => {
356
- this.skipDataTypeCheck = false;
357
- // [FS] IRAD-1571 2021-09-27
358
- // dispatch a transaction that MUST start from the view’s current state;
359
- const editorState = this._editorView.state;
360
- const { doc } = editorState;
361
- let { tr } = editorState;
362
- const document = this.getDocument(content, editorState, dataType);
363
- this.skipDataTypeCheck = true;
364
-
365
- // [FS] IRAD-1593 2021-10-12
366
- // Reset lastKeyCode since the content is set dynamically and so lastKeyCode is invalid now.
367
- this._editorView.lastKeyCode = null;
368
-
369
- const selection = TextSelection.create(doc, 0, doc.content.size);
370
-
371
- tr = tr.setSelection(selection).replaceSelectionWith(document, false);
372
- // [FS] IRAD-1092 2020-12-03
373
- // set the value for object metadata and objectId
374
- // Should update all document attributes.
375
- Object.keys(document.attrs).forEach((attr) => {
376
- tr = tr.step(new SetDocAttrStep(attr, document.attrs[attr]));
377
- });
378
-
379
- this._skipSCU = true;
380
- this._editorView.dispatch(tr);
381
- };
382
-
383
- hasDataChanged(nextData: any, nextDataType: DataType) {
384
- let dataChanged = false;
385
-
386
- // [FS] IRAD-1571 2021-09-27
387
- // dispatch a transaction that MUST start from the view’s current state;
388
- // [FS] IRAD-1589 2021-10-04
389
- // Do a proper circular JSON comparison.
390
- if (stringify(this.state.data) !== stringify(nextData)) {
391
- const editorState = this._editorView.state;
392
- const nextDoc = this.getDocument(nextData, editorState, nextDataType);
393
- dataChanged = !nextDoc.eq(editorState.doc);
394
- }
395
-
396
- return dataChanged;
397
- }
398
-
399
- changeContent(data: any, dataType: DataType) {
400
- if (this.hasDataChanged(data, dataType)) {
401
- // FS IRAD-1592 2021-11-10
402
- // Release here quickly, so that update doesn't care about at this point.
403
- // data changed, so update document content
404
- setTimeout(this.setContent.bind(this, data, dataType), 1);
405
- }
406
- }
407
-
408
- shouldComponentUpdate(nextProps: any, nextState: any) {
409
- // Only interested if properties are set from outside.
410
- if (!this._skipSCU) {
411
- this._skipSCU = false;
412
-
413
- this.changeContent(nextState.data, nextState.dataType);
414
-
415
- if (this.state.docID !== nextState.docID) {
416
- setTimeout(this.setDocID.bind(this, nextState), 1);
417
- }
418
- }
419
-
420
- this.skipDataTypeCheck = true;
421
-
422
- return true;
423
- }
424
-
425
- setDocID(nextState: any) {
426
- // Collaborative mode changed
427
- const collabEditing = nextState.docID !== '';
428
- const editorState = this._editorView.state;
429
- const setState = this.setState.bind(this);
430
- const docID = nextState.docID || '';
431
- const collabServiceURL =
432
- nextState.collabServiceURL || '/collaboration-service';
433
-
434
- if (this._connector) {
435
- this._connector.cleanUp();
436
- }
437
- // create new connector
438
- this._connector = collabEditing
439
- ? new CollabConnector(
440
- editorState,
441
- setState,
442
- {
443
- docID,
444
- collabServiceURL,
445
- },
446
- this._defaultEditorSchema,
447
- this._defaultEditorPlugins,
448
- // [FS] IRAD-1578 2021-09-27
449
- this.onReady.bind(this)
450
- )
451
- : new SimpleConnector(editorState, setState);
452
-
453
- // FS IRAD-1592 2021-11-10
454
- // Notify collab server
455
- if (this._connector.updateSchema) {
456
- // Use known editorState to update schema.
457
- this._connector.updateSchema(editorState.schema);
458
- }
459
- }
460
-
461
- render(): React.Element<any> {
462
- const {
463
- editorState,
464
- width,
465
- height,
466
- readOnly,
467
- disabled,
468
- embedded,
469
- runtime,
470
- } = this.state;
471
- // [FS] IRAD-978 2020-06-05
472
- // Using 100vw & 100vh (100% viewport) is not ideal for a component which is expected to be a part of a page,
473
- // so changing it to 100% width & height which will occupy the area relative to its parent.
474
- return (
475
- <RichTextEditor
476
- disabled={disabled}
477
- editorState={editorState}
478
- embedded={embedded}
479
- height={height}
480
- onChange={this._onChange}
481
- onReady={this._onReady}
482
- readOnly={readOnly}
483
- runtime={runtime}
484
- width={width}
485
- />
486
- );
487
- }
488
-
489
- _onChange = (data: { state: EditorState, transaction: Transform }): void => {
490
- const { transaction } = data;
491
-
492
- /*
493
- ** ProseMirror Debug Tool's Snapshot creates a new state and sets that to editor view's state.
494
- ** This results in the connector's state as an orphan and thus transaction mismatch error.
495
- ** To resolve check and update the connector's state to keep in sync.
496
- */
497
-
498
- if (this._editorView) {
499
- const isSameState =
500
- this._connector._editorState == this._editorView.state;
501
- let invokeOnEdit = false;
502
-
503
- if (!isSameState) {
504
- this._connector._editorState = this._editorView.state;
505
- invokeOnEdit = true;
506
- } else {
507
- // [FS] IRAD-1264 2021-03-19
508
- // check if in non-collab mode.
509
- if (!(this._connector instanceof CollabConnector)) {
510
- invokeOnEdit = true;
511
- }
512
- }
513
- if (invokeOnEdit) {
514
- // [FS] IRAD-1236 2020-03-05
515
- // Only need to call if there is any difference in collab mode OR always in non-collab mode.
516
- this._connector.onEdit(transaction, this._editorView);
517
- }
518
-
519
- if (transaction.docChanged) {
520
- const docJson = transaction.doc.toJSON();
521
- let isEmpty = false;
522
-
523
- if (docJson.content && docJson.content.length === 1) {
524
- if (
525
- !docJson.content[0].content ||
526
- (docJson.content[0].content &&
527
- docJson.content[0].content[0].text &&
528
- '' === docJson.content[0].content[0].text.trim())
529
- ) {
530
- isEmpty = true;
531
- }
532
- }
533
-
534
- // setCFlags is/was always the opposite of isEmpty.
535
- if (isEmpty) {
536
- this.resetCounters(transaction);
537
- } else {
538
- this.setCounterFlags(transaction, false);
539
- }
540
-
541
- // Changing 2nd parameter from boolean to object was not in any way
542
- // backwards compatible. Any conditional logic placed on isEmpty was
543
- // broken. Reverting that change, then adding view as a 3rd parameter.
544
- this.state.onChangeCB(docJson, isEmpty, this._editorView);
545
-
546
- this.closeOpenedPopupModels();
547
- }
548
- }
549
- };
550
- // [FS] IRAD-1173 2021-02-25
551
- // Bug fix: Transaction mismatch error when a dialog is opened and keep typing.
552
- closeOpenedPopupModels() {
553
- const element = document.getElementsByClassName('czi-pop-up-element')[0];
554
- if (element && element.parentElement) {
555
- element.parentElement.removeChild(element);
556
- }
557
- }
558
-
559
- _onReady = (editorView: EditorView): void => {
560
- // [FS][06-APR-2020][IRAD-922]
561
- // Showing focus in the editor.
562
- const { state, dispatch } = editorView;
563
- this._editorView = editorView;
564
- const tr = state.tr;
565
- dispatch(tr.scrollIntoView());
566
-
567
- // [FS] IRAD-1578 2021-09-27
568
- // In collab mode, fire onRead only after getting the response from collab server.
569
- if (this.state.onReadyCB && this.state.docID === '') {
570
- editorView.focus();
571
- this.state.onReadyCB(this);
572
- }
573
- };
574
-
575
-
576
-
577
- /**
578
- * LICIT properties:
579
- * docID {number} [0] Collaborative Doument ID
580
- * debug {boolean} [false] To enable/disable ProseMirror Debug Tools, available only in development.
581
- * width {string} [100%] Width of the editor.
582
- * height {height} [100%] Height of the editor.
583
- * readOnly {boolean} [false] To enable/disable editing mode.
584
- * onChange {@callback} [null] Fires after each significant change.
585
- * @param data {JSON} Modified document data.
586
- * onReady {@callback} [null] Fires when the editor is fully ready.
587
- * @param ref {LICIT} Rerefence of the editor.
588
- * data {JSON} [null] Document data to be loaded into the editor.
589
- * disabled {boolean} [false] Disable the editor.
590
- * embedded {boolean} [false] Disable/Enable inline behaviour.
591
- */
592
- setProps = (props: any): void => {
593
- if (this.state.readOnly) {
594
- // It should be possible to load content into the editor in readonly as well.
595
- // It should not be necessary to make the component writable any time during the process
596
- const propsCopy = {};
597
- this._skipSCU = true;
598
- Object.assign(propsCopy, props);
599
- // make writable without content change
600
- propsCopy.readOnly = false;
601
- delete propsCopy.data;
602
- this.setState(propsCopy);
603
- }
604
- this.skipDataTypeCheck = false;
605
- // Need to go through shouldComponentUpdate lifecycle here, when updated from outside,
606
- // so that content is modified gracefully using transaction so that undo/redo works too.
607
- this._skipSCU = false;
608
- this.setState(props);
609
- };
610
-
611
- exportPDF = () => {
612
- new Promise(async (resolve, reject) => {
613
- try {
614
- //
615
- if (Array.isArray(this.props.plugins)) {
616
- this.props.plugins.forEach((plugin) => {
617
- if (plugin['key'].startsWith('exportPDF$')) {
618
- // got the exportPDF instance.
619
- resolve(plugin);
620
- }
621
- });
622
- }
623
- } catch (error) {
624
- reject();
625
- }
626
- }).then((exportPDF) => {
627
- if (exportPDF.perform) {
628
- exportPDF.perform(this._editorView);
629
- }
630
- });
631
- };
632
-
633
- goToEnd = (): void => {
634
- // Return focus to the editor with cursor at end of document.
635
- const view: EditorView = this.editorView;
636
- const tr = view.state.tr;
637
- view.dispatch(tr.setSelection(TextSelection.atEnd(view.state.doc)).scrollIntoView());
638
- view.focus();
639
- };
640
-
641
- }
642
-
643
- export default Licit;
1
+ // @flow
2
+ import { EditorState, TextSelection, Plugin } from 'prosemirror-state';
3
+ import { Node, Schema } from 'prosemirror-model';
4
+ import { Transform } from 'prosemirror-transform';
5
+ import { EditorView } from 'prosemirror-view';
6
+ import * as React from 'react';
7
+ import { stringify } from 'flatted';
8
+
9
+ import convertFromJSON from '../convertFromJSON.js';
10
+ import RichTextEditor from '../ui/RichTextEditor.js';
11
+ import uuid from '../uuid.js';
12
+ import SimpleConnector from './SimpleConnector.js';
13
+ import CollabConnector from './CollabConnector.js';
14
+ import { EMPTY_DOC_JSON } from '../createEmptyEditorState.js';
15
+ import type { EditorRuntime } from '../Types.js';
16
+ import {
17
+ createPopUp,
18
+ atViewportCenter,
19
+ } from '@modusoperandi/licit-ui-commands';
20
+ import AlertInfo from '../ui/AlertInfo.js';
21
+ import { SetDocAttrStep } from '@modusoperandi/licit-doc-attrs-step';
22
+ import './licit.css';
23
+ import DefaultEditorPlugins from '../buildEditorPlugins.js';
24
+ import EditorMarks from '../EditorMarks.js';
25
+ import EditorNodes from '../EditorNodes.js';
26
+ import convertFromHTML from '../convertFromHTML.js';
27
+
28
+ export const DataType = Object.freeze({
29
+ JSON: Symbol('json'),
30
+ HTML: Symbol('html'),
31
+ });
32
+
33
+ /**
34
+ * LICIT properties:
35
+ * docID {string} [] Collaborative Doument ID
36
+ * collabServiceURL {string} [/collaboration-service] Collaboration Service URL
37
+ * debug {boolean} [false] To enable/disable ProseMirror Debug Tools, available only in development.
38
+ * width {string} [100%] Width of the editor.
39
+ * height {string} [100%] Height of the editor.
40
+ * readOnly {boolean} [false] To enable/disable editing mode.
41
+ * onChange {@callback} [null] Fires after each significant change.
42
+ * @param data {JSON} Modified document data.
43
+ * onReady {@callback} [null] Fires when the editor is fully ready.
44
+ * @param ref {LICIT} Rerefence of the editor.
45
+ * data {JSON|HTML} [null] Document data to be loaded into the editor.
46
+ * dataType {JSON|HTML} [JSON] Document data to be loaded into the editor.
47
+ * disabled {boolean} [false] Disable the editor.
48
+ * embedded {boolean} [false] Disable/Enable inline behaviour.
49
+ * plugins [plugins] External Plugins into the editor.
50
+ */
51
+ class Licit extends React.Component<any, any> {
52
+ _runtime: EditorRuntime;
53
+ _connector: SimpleConnector;
54
+ _clientID: string;
55
+ _editorView: EditorView; // This will be handy in updating document's content.
56
+ _skipSCU: boolean; // Flag to decide whether to skip shouldComponentUpdate
57
+ _defaultEditorSchema: Schema;
58
+ _defaultEditorPlugins: Array<Plugin>;
59
+ _pasteJSONPlugin: Plugin;
60
+
61
+ _popUp = null;
62
+
63
+ /**
64
+ * Provides access to prosemirror view.
65
+ */
66
+ get editorView(): EditorView {
67
+ return this._editorView;
68
+ }
69
+
70
+ constructor(props: any, context: any) {
71
+ super(props, context);
72
+ this.initialize(props);
73
+ }
74
+
75
+ initialize(props: any) {
76
+ this._clientID = uuid();
77
+ this._editorView = null;
78
+ this._skipSCU = true;
79
+
80
+ const noop = function () {};
81
+
82
+ // [FS] IRAD-981 2020-06-10
83
+ // Component's configurations.
84
+ // [FS] IRAD-1552 2021-08-26
85
+ // Collaboration server / client should allow string values for document identifiers.
86
+ const docID = props.docID || ''; // Empty means collaborative.
87
+ const collaborative = docID !== '';
88
+ // [FS] IRAD-1553 2021-08-26
89
+ // Configurable Collaboration Service URL.
90
+ const collabServiceURL = props.collabServiceURL || '/collaboration-service';
91
+ const debug = props.debug || false;
92
+ // Default width and height to undefined
93
+ const width = props.width || undefined;
94
+ const height = props.height || undefined;
95
+ const onChangeCB =
96
+ typeof props.onChange === 'function' ? props.onChange : noop;
97
+ const onReadyCB =
98
+ typeof props.onReady === 'function' ? props.onReady : noop;
99
+ const readOnly = props.readOnly || false;
100
+ let data = props.data || null;
101
+ const dataType = props.dataType || DataType.JSON;
102
+ const disabled = props.disabled || false;
103
+ const embedded = props.embedded || false; // [FS] IRAD-996 2020-06-30
104
+ // [FS] 2020-07-03
105
+ // Handle Image Upload from Angular App
106
+ const runtime = props.runtime || null;
107
+ const plugins = props.plugins || null;
108
+ // This flag decides whether DataType.HTML check is needed when
109
+ // changing document. If it forcefully done, it is not needed, otherwise needed.
110
+ this.skipDataTypeCheck = false;
111
+
112
+ this._defaultEditorSchema = new Schema({
113
+ nodes: EditorNodes,
114
+ marks: EditorMarks,
115
+ });
116
+ this._defaultEditorPlugins = new DefaultEditorPlugins(
117
+ this._defaultEditorSchema
118
+ ).get();
119
+ this._pasteJSONPlugin = null;
120
+
121
+ const editorState = this.initEditorState(plugins, dataType, data);
122
+ data = editorState.doc;
123
+
124
+ const setState = this.setState.bind(this);
125
+ this._connector = collaborative
126
+ ? new CollabConnector(
127
+ editorState,
128
+ setState,
129
+ {
130
+ docID,
131
+ collabServiceURL,
132
+ },
133
+ this._defaultEditorSchema,
134
+ this._defaultEditorPlugins,
135
+ // [FS] IRAD-1578 2021-09-27
136
+ this.onReady.bind(this)
137
+ )
138
+ : new SimpleConnector(editorState, setState);
139
+
140
+ this._connector._dataDefined = !!props.data;
141
+
142
+ // FS IRAD-989 2020-18-06
143
+ // updating properties should automatically render the changes
144
+
145
+ this.state = {
146
+ docID,
147
+ collabServiceURL,
148
+ data,
149
+ editorState,
150
+ width,
151
+ height,
152
+ readOnly,
153
+ onChangeCB,
154
+ onReadyCB,
155
+ debug,
156
+ disabled,
157
+ embedded,
158
+ runtime,
159
+ dataType,
160
+ };
161
+
162
+ // FS IRAD-1040 2020-26-08
163
+ // Get the modified schema from editorstate and send it to collab server
164
+ if (this._connector.updateSchema) {
165
+ // Use known editorState to update schema.
166
+ this._connector.updateSchema(editorState.schema, data);
167
+ }
168
+ }
169
+
170
+ initEditorState(plugins: Array<Plugin>, dataType: DataType, data: any) {
171
+ let editorState = null;
172
+ const effectivePlugins = this.getEffectivePlugins(
173
+ this._defaultEditorSchema,
174
+ this._defaultEditorPlugins,
175
+ plugins
176
+ );
177
+ this._pasteJSONPlugin = effectivePlugins.pasteJSONPlugin;
178
+ if (DataType.JSON === dataType) {
179
+ editorState = convertFromJSON(
180
+ data,
181
+ null,
182
+ effectivePlugins.schema,
183
+ effectivePlugins.plugins
184
+ );
185
+ // [FS] IRAD-1067 2020-09-19
186
+ // The editorState will return null if the doc Json is mal-formed
187
+ if (null === editorState) {
188
+ editorState = convertFromJSON(
189
+ EMPTY_DOC_JSON,
190
+ null,
191
+ effectivePlugins.schema,
192
+ effectivePlugins.plugins
193
+ );
194
+ this.showAlert();
195
+ }
196
+ } else {
197
+ editorState = convertFromHTML(
198
+ data,
199
+ effectivePlugins.schema,
200
+ effectivePlugins.plugins
201
+ );
202
+ }
203
+
204
+ return editorState;
205
+ }
206
+
207
+ getEffectivePlugins(
208
+ schema: Schema,
209
+ defaultPlugins: Array<Plugin>,
210
+ plugins: Array<Plugin>
211
+ ): { plugins: Array<Plugin>, schema: Schema, pasteJSONPlugin: Plugin } {
212
+ const effectivePlugins = defaultPlugins;
213
+ let pasteJSONPlugin = null;
214
+
215
+ if (plugins) {
216
+ for (const p of plugins) {
217
+ if (!effectivePlugins.includes(p)) {
218
+ effectivePlugins.push(p);
219
+ if (p.getEffectiveSchema) {
220
+ schema = p.getEffectiveSchema(schema);
221
+ }
222
+
223
+ if (p.initKeyCommands) {
224
+ effectivePlugins.push(p.initKeyCommands());
225
+ }
226
+
227
+ if (p.insert) {
228
+ pasteJSONPlugin = p;
229
+ }
230
+ }
231
+ }
232
+ }
233
+ return { plugins: effectivePlugins, schema, pasteJSONPlugin };
234
+ }
235
+
236
+ // [FS] IRAD-1578 2021-09-27
237
+ onReady(state: EditorState) {
238
+ const collabEditing = this.state.docID !== '';
239
+
240
+ if (collabEditing) {
241
+ this._editorView && this._editorView.focus();
242
+ if (this.state.onReadyCB) {
243
+ this.state.onReadyCB(this);
244
+ }
245
+ }
246
+ }
247
+
248
+ // [FS] IRAD-1067 2020-09-19
249
+ // Alert funtion to show document is corrupted
250
+ showAlert() {
251
+ const anchor = null;
252
+ this._popUp = createPopUp(AlertInfo, null, {
253
+ anchor,
254
+ position: atViewportCenter,
255
+ onClose: (val) => {
256
+ if (this._popUp) {
257
+ this._popUp = null;
258
+ }
259
+ },
260
+ });
261
+ }
262
+
263
+ resetCounters(transaction: Transform) {
264
+ for (let index = 1; index <= 10; index++) {
265
+ const counterVar = 'set-cust-style-counter-' + index;
266
+ const setCounterVal = window[counterVar];
267
+ if (setCounterVal) {
268
+ delete window[counterVar];
269
+ }
270
+ }
271
+ this.setCounterFlags(transaction, true);
272
+ }
273
+
274
+ setCounterFlags(transaction: Transform, reset: boolean) {
275
+ let modified = false;
276
+ let counterFlags = null;
277
+ const existingCFlags = transaction.doc.attrs.counterFlags;
278
+ if (reset && !existingCFlags) {
279
+ return;
280
+ }
281
+
282
+ for (let index = 1; index <= 10; index++) {
283
+ const counterVar = 'set-cust-style-counter-' + index;
284
+
285
+ const setCounterVal = window[counterVar];
286
+ if (setCounterVal) {
287
+ if (!counterFlags) {
288
+ counterFlags = {};
289
+ }
290
+ counterFlags[counterVar] = true;
291
+
292
+ if (!existingCFlags) {
293
+ modified = true;
294
+ }
295
+ }
296
+ if (!modified) {
297
+ if (existingCFlags) {
298
+ if (setCounterVal) {
299
+ modified = undefined == existingCFlags[counterVar];
300
+ } else {
301
+ modified = undefined != existingCFlags[counterVar];
302
+ }
303
+ } else {
304
+ modified = setCounterVal;
305
+ }
306
+ }
307
+ }
308
+
309
+ if (modified) {
310
+ const tr = this._editorView.state.tr.step(
311
+ new SetDocAttrStep('counterFlags', counterFlags)
312
+ );
313
+ this._editorView.dispatch(tr);
314
+ }
315
+ }
316
+
317
+ getDeletedArtifactIds() {
318
+ if (this._connector.getDeletedArtifactIds) {
319
+ this._connector.getDeletedArtifactIds(this.state.editorState.schema);
320
+ }
321
+ }
322
+
323
+ isNodeHasAttribute(node: Node, attrName: string) {
324
+ return node.attrs?.[attrName];
325
+ }
326
+
327
+ getDocument(content: any, editorState: EditorState, dataType: DataType) {
328
+ let document = null;
329
+ const { schema } = editorState;
330
+
331
+ if (DataType.JSON === dataType || this.skipDataTypeCheck) {
332
+ document = schema.nodeFromJSON(content || EMPTY_DOC_JSON);
333
+ } else {
334
+ const tempEState = convertFromHTML(
335
+ content || '',
336
+ schema,
337
+ editorState.plugins
338
+ );
339
+ document = tempEState.doc;
340
+ }
341
+
342
+ return document;
343
+ }
344
+
345
+ insertJSON = (json: { [key: string]: any }): void => {
346
+ if (this._pasteJSONPlugin?.insert) {
347
+ this._pasteJSONPlugin.insert(json, this._editorView);
348
+ }
349
+ };
350
+
351
+ setContent = (content: any = {}, dataType: DataType): void => {
352
+ this.skipDataTypeCheck = false;
353
+ // [FS] IRAD-1571 2021-09-27
354
+ // dispatch a transaction that MUST start from the view’s current state;
355
+ const editorState = this._editorView.state;
356
+ let { tr } = editorState;
357
+ const document = this.getDocument(content, editorState, dataType);
358
+ this.skipDataTypeCheck = true;
359
+
360
+ // [FS] IRAD-1593 2021-10-12
361
+ // Reset lastKeyCode since the content is set dynamically and so lastKeyCode is invalid now.
362
+ this._editorView.lastKeyCode = null;
363
+
364
+ // [FS] IRAD-1092 2020-12-03
365
+ // set the value for object metadata and objectId
366
+ // Should update all document attributes.
367
+ Object.keys(document.attrs).forEach((attr) => {
368
+ tr = tr.step(new SetDocAttrStep(attr, document.attrs[attr]));
369
+ });
370
+
371
+ this._skipSCU = true;
372
+ this._editorView.dispatch(tr);
373
+ };
374
+
375
+ hasDataChanged(nextData: any, nextDataType: DataType) {
376
+ let dataChanged = false;
377
+
378
+ // [FS] IRAD-1571 2021-09-27
379
+ // dispatch a transaction that MUST start from the view’s current state;
380
+ // [FS] IRAD-1589 2021-10-04
381
+ // Do a proper circular JSON comparison.
382
+ if (stringify(this.state.data) !== stringify(nextData)) {
383
+ const editorState = this._editorView.state;
384
+ const nextDoc = this.getDocument(nextData, editorState, nextDataType);
385
+ dataChanged = !nextDoc.eq(editorState.doc);
386
+ }
387
+
388
+ return dataChanged;
389
+ }
390
+
391
+ changeContent(data: any, dataType: DataType) {
392
+ if (this.hasDataChanged(data, dataType)) {
393
+ // FS IRAD-1592 2021-11-10
394
+ // Release here quickly, so that update doesn't care about at this point.
395
+ // data changed, so update document content
396
+ setTimeout(this.setContent.bind(this, data, dataType), 1);
397
+ }
398
+ }
399
+
400
+ shouldComponentUpdate(nextProps: any, nextState: any) {
401
+ // Only interested if properties are set from outside.
402
+ if (!this._skipSCU) {
403
+ this._skipSCU = false;
404
+
405
+ this.changeContent(nextState.data, nextState.dataType);
406
+
407
+ if (this.state.docID !== nextState.docID) {
408
+ setTimeout(this.setDocID.bind(this, nextState), 1);
409
+ }
410
+ }
411
+
412
+ this.skipDataTypeCheck = true;
413
+
414
+ return true;
415
+ }
416
+
417
+ setDocID(nextState: any) {
418
+ // Collaborative mode changed
419
+ const collabEditing = nextState.docID !== '';
420
+ const editorState = this._editorView.state;
421
+ const setState = this.setState.bind(this);
422
+ const docID = nextState.docID || '';
423
+ const collabServiceURL =
424
+ nextState.collabServiceURL || '/collaboration-service';
425
+
426
+ if (this._connector) {
427
+ this._connector.cleanUp();
428
+ }
429
+ // create new connector
430
+ this._connector = collabEditing
431
+ ? new CollabConnector(
432
+ editorState,
433
+ setState,
434
+ {
435
+ docID,
436
+ collabServiceURL,
437
+ },
438
+ this._defaultEditorSchema,
439
+ this._defaultEditorPlugins,
440
+ // [FS] IRAD-1578 2021-09-27
441
+ this.onReady.bind(this)
442
+ )
443
+ : new SimpleConnector(editorState, setState);
444
+
445
+ // FS IRAD-1592 2021-11-10
446
+ // Notify collab server
447
+ if (this._connector.updateSchema) {
448
+ // Use known editorState to update schema.
449
+ this._connector.updateSchema(editorState.schema);
450
+ }
451
+ }
452
+
453
+ render(): React.Element<any> {
454
+ const {
455
+ editorState,
456
+ width,
457
+ height,
458
+ readOnly,
459
+ disabled,
460
+ embedded,
461
+ runtime,
462
+ } = this.state;
463
+ // [FS] IRAD-978 2020-06-05
464
+ // Using 100vw & 100vh (100% viewport) is not ideal for a component which is expected to be a part of a page,
465
+ // so changing it to 100% width & height which will occupy the area relative to its parent.
466
+ return (
467
+ <RichTextEditor
468
+ disabled={disabled}
469
+ editorState={editorState}
470
+ embedded={embedded}
471
+ height={height}
472
+ onChange={this._onChange}
473
+ onReady={this._onReady}
474
+ readOnly={readOnly}
475
+ runtime={runtime}
476
+ width={width}
477
+ />
478
+ );
479
+ }
480
+
481
+ _onChange = (data: { state: EditorState, transaction: Transform }): void => {
482
+ const { transaction } = data;
483
+
484
+ /*
485
+ ** ProseMirror Debug Tool's Snapshot creates a new state and sets that to editor view's state.
486
+ ** This results in the connector's state as an orphan and thus transaction mismatch error.
487
+ ** To resolve check and update the connector's state to keep in sync.
488
+ */
489
+
490
+ if (this._editorView) {
491
+ const isSameState =
492
+ this._connector._editorState == this._editorView.state;
493
+ let invokeOnEdit = false;
494
+
495
+ if (!isSameState) {
496
+ this._connector._editorState = this._editorView.state;
497
+ invokeOnEdit = true;
498
+ } else {
499
+ // [FS] IRAD-1264 2021-03-19
500
+ // check if in non-collab mode.
501
+ if (!(this._connector instanceof CollabConnector)) {
502
+ invokeOnEdit = true;
503
+ }
504
+ }
505
+ if (invokeOnEdit) {
506
+ // [FS] IRAD-1236 2020-03-05
507
+ // Only need to call if there is any difference in collab mode OR always in non-collab mode.
508
+ this._connector.onEdit(transaction, this._editorView);
509
+ }
510
+
511
+ if (transaction.docChanged) {
512
+ const docJson = transaction.doc.toJSON();
513
+ let isEmpty = false;
514
+
515
+ if (docJson.content && docJson.content.length === 1) {
516
+ if (
517
+ !docJson.content[0].content ||
518
+ (docJson.content?.[0]?.content?.[0]?.text &&
519
+ '' === docJson.content?.[0]?.content?.[0]?.text.trim())
520
+ ) {
521
+ isEmpty = true;
522
+ }
523
+ }
524
+
525
+ // setCFlags is/was always the opposite of isEmpty.
526
+ if (isEmpty) {
527
+ this.resetCounters(transaction);
528
+ } else {
529
+ this.setCounterFlags(transaction, false);
530
+ }
531
+
532
+ // Changing 2nd parameter from boolean to object was not in any way
533
+ // backwards compatible. Any conditional logic placed on isEmpty was
534
+ // broken. Reverting that change, then adding view as a 3rd parameter.
535
+ this.state.onChangeCB(docJson, isEmpty, this._editorView);
536
+
537
+ this.closeOpenedPopupModels();
538
+ }
539
+ }
540
+ };
541
+ // [FS] IRAD-1173 2021-02-25
542
+ // Bug fix: Transaction mismatch error when a dialog is opened and keep typing.
543
+ closeOpenedPopupModels() {
544
+ const element = document.getElementsByClassName('czi-pop-up-element')[0];
545
+ if (element?.parentElement) {
546
+ element.parentElement.removeChild(element);
547
+ }
548
+ }
549
+
550
+ _onReady = (editorView: EditorView): void => {
551
+ // [FS][06-APR-2020][IRAD-922]
552
+ // Showing focus in the editor.
553
+ const { state, dispatch } = editorView;
554
+ this._editorView = editorView;
555
+ const tr = state.tr;
556
+ dispatch(tr.scrollIntoView());
557
+
558
+ // [FS] IRAD-1578 2021-09-27
559
+ // In collab mode, fire onRead only after getting the response from collab server.
560
+ if (this.state.onReadyCB && this.state.docID === '') {
561
+ editorView.focus();
562
+ this.state.onReadyCB(this);
563
+ }
564
+ };
565
+
566
+ /**
567
+ * LICIT properties:
568
+ * docID {number} [0] Collaborative Doument ID
569
+ * debug {boolean} [false] To enable/disable ProseMirror Debug Tools, available only in development.
570
+ * width {string} [100%] Width of the editor.
571
+ * height {height} [100%] Height of the editor.
572
+ * readOnly {boolean} [false] To enable/disable editing mode.
573
+ * onChange {@callback} [null] Fires after each significant change.
574
+ * @param data {JSON} Modified document data.
575
+ * onReady {@callback} [null] Fires when the editor is fully ready.
576
+ * @param ref {LICIT} Rerefence of the editor.
577
+ * data {JSON} [null] Document data to be loaded into the editor.
578
+ * disabled {boolean} [false] Disable the editor.
579
+ * embedded {boolean} [false] Disable/Enable inline behaviour.
580
+ */
581
+ setProps = (props: any): void => {
582
+ if (this.state.readOnly) {
583
+ // It should be possible to load content into the editor in readonly as well.
584
+ // It should not be necessary to make the component writable any time during the process
585
+ const propsCopy = {};
586
+ this._skipSCU = true;
587
+ Object.assign(propsCopy, props);
588
+ // make writable without content change
589
+ propsCopy.readOnly = false;
590
+ delete propsCopy.data;
591
+ this.setState(propsCopy);
592
+ }
593
+ this.skipDataTypeCheck = false;
594
+ // Need to go through shouldComponentUpdate lifecycle here, when updated from outside,
595
+ // so that content is modified gracefully using transaction so that undo/redo works too.
596
+ this._skipSCU = false;
597
+ this.setState(props);
598
+ };
599
+
600
+ goToEnd = (): void => {
601
+ // Return focus to the editor with cursor at end of document.
602
+ const view: EditorView = this.editorView;
603
+ const tr = view.state.tr;
604
+ view.dispatch(
605
+ tr.setSelection(TextSelection.atEnd(view.state.doc)).scrollIntoView()
606
+ );
607
+ view.focus();
608
+ };
609
+ }
610
+
611
+ export default Licit;