@atlaskit/editor-core 187.34.1 → 187.35.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 (94) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/dist/cjs/index.js +10 -42
  3. package/dist/cjs/plugins/block-type/commands/block-type.js +19 -211
  4. package/dist/cjs/plugins/block-type/commands/index.js +2 -28
  5. package/dist/cjs/plugins/block-type/index.js +21 -4
  6. package/dist/cjs/plugins/block-type/pm-plugins/input-rule.js +9 -64
  7. package/dist/cjs/plugins/block-type/pm-plugins/keymap.js +1 -1
  8. package/dist/cjs/plugins/code-block/actions.js +83 -6
  9. package/dist/cjs/plugins/code-block/index.js +27 -12
  10. package/dist/cjs/plugins/code-block/pm-plugins/input-rule.js +68 -0
  11. package/dist/cjs/plugins/copy-button/commands.js +2 -1
  12. package/dist/cjs/plugins/date/ui/DatePicker/index.js +3 -2
  13. package/dist/cjs/plugins/expand/commands.js +3 -3
  14. package/dist/cjs/plugins/expand/index.js +2 -2
  15. package/dist/cjs/plugins/index.js +1 -7
  16. package/dist/cjs/plugins/insert-block/index.js +34 -29
  17. package/dist/cjs/plugins/panel/actions.js +26 -2
  18. package/dist/cjs/plugins/panel/index.js +9 -5
  19. package/dist/cjs/version-wrapper.js +1 -1
  20. package/dist/es2019/index.js +16 -2
  21. package/dist/es2019/plugins/block-type/commands/block-type.js +15 -217
  22. package/dist/es2019/plugins/block-type/commands/index.js +1 -3
  23. package/dist/es2019/plugins/block-type/index.js +21 -4
  24. package/dist/es2019/plugins/block-type/pm-plugins/input-rule.js +3 -56
  25. package/dist/es2019/plugins/block-type/pm-plugins/keymap.js +2 -2
  26. package/dist/es2019/plugins/code-block/actions.js +84 -3
  27. package/dist/es2019/plugins/code-block/index.js +22 -7
  28. package/dist/es2019/plugins/code-block/pm-plugins/input-rule.js +60 -0
  29. package/dist/es2019/plugins/copy-button/commands.js +2 -1
  30. package/dist/es2019/plugins/date/ui/DatePicker/index.js +3 -2
  31. package/dist/es2019/plugins/expand/commands.js +1 -1
  32. package/dist/es2019/plugins/expand/index.js +1 -1
  33. package/dist/es2019/plugins/index.js +0 -1
  34. package/dist/es2019/plugins/insert-block/index.js +47 -42
  35. package/dist/es2019/plugins/panel/actions.js +26 -2
  36. package/dist/es2019/plugins/panel/index.js +8 -4
  37. package/dist/es2019/version-wrapper.js +1 -1
  38. package/dist/esm/index.js +16 -2
  39. package/dist/esm/plugins/block-type/commands/block-type.js +17 -205
  40. package/dist/esm/plugins/block-type/commands/index.js +1 -3
  41. package/dist/esm/plugins/block-type/index.js +21 -4
  42. package/dist/esm/plugins/block-type/pm-plugins/input-rule.js +3 -58
  43. package/dist/esm/plugins/block-type/pm-plugins/keymap.js +2 -2
  44. package/dist/esm/plugins/code-block/actions.js +76 -3
  45. package/dist/esm/plugins/code-block/index.js +25 -11
  46. package/dist/esm/plugins/code-block/pm-plugins/input-rule.js +62 -0
  47. package/dist/esm/plugins/copy-button/commands.js +2 -1
  48. package/dist/esm/plugins/date/ui/DatePicker/index.js +3 -2
  49. package/dist/esm/plugins/expand/commands.js +1 -1
  50. package/dist/esm/plugins/expand/index.js +1 -1
  51. package/dist/esm/plugins/index.js +0 -1
  52. package/dist/esm/plugins/insert-block/index.js +27 -22
  53. package/dist/esm/plugins/panel/actions.js +24 -2
  54. package/dist/esm/plugins/panel/index.js +8 -4
  55. package/dist/esm/version-wrapper.js +1 -1
  56. package/dist/types/index.d.ts +16 -3
  57. package/dist/types/labs/next/presets/default.d.ts +6 -0
  58. package/dist/types/plugins/block-type/commands/block-type.d.ts +1 -25
  59. package/dist/types/plugins/block-type/commands/index.d.ts +1 -3
  60. package/dist/types/plugins/block-type/index.d.ts +10 -2
  61. package/dist/types/plugins/code-block/actions.d.ts +11 -0
  62. package/dist/types/plugins/code-block/index.d.ts +6 -1
  63. package/dist/types/plugins/code-block/pm-plugins/input-rule.d.ts +3 -0
  64. package/dist/types/plugins/date/ui/DatePicker/index.d.ts +2 -2
  65. package/dist/types/plugins/index.d.ts +0 -2
  66. package/dist/types/plugins/insert-block/types.d.ts +7 -1
  67. package/dist/types/plugins/panel/actions.d.ts +4 -1
  68. package/dist/types/plugins/panel/index.d.ts +11 -2
  69. package/dist/types-ts4.5/index.d.ts +16 -3
  70. package/dist/types-ts4.5/labs/next/presets/default.d.ts +6 -0
  71. package/dist/types-ts4.5/plugins/block-type/commands/block-type.d.ts +1 -25
  72. package/dist/types-ts4.5/plugins/block-type/commands/index.d.ts +1 -3
  73. package/dist/types-ts4.5/plugins/block-type/index.d.ts +10 -2
  74. package/dist/types-ts4.5/plugins/code-block/actions.d.ts +11 -0
  75. package/dist/types-ts4.5/plugins/code-block/index.d.ts +6 -1
  76. package/dist/types-ts4.5/plugins/code-block/pm-plugins/input-rule.d.ts +3 -0
  77. package/dist/types-ts4.5/plugins/date/ui/DatePicker/index.d.ts +2 -2
  78. package/dist/types-ts4.5/plugins/index.d.ts +0 -2
  79. package/dist/types-ts4.5/plugins/insert-block/types.d.ts +7 -1
  80. package/dist/types-ts4.5/plugins/panel/actions.d.ts +4 -1
  81. package/dist/types-ts4.5/plugins/panel/index.d.ts +9 -2
  82. package/package.json +5 -5
  83. package/report.api.md +62 -23
  84. package/tmp/api-report-tmp.d.ts +49 -15
  85. package/dist/cjs/plugins/block-type/commands/insert-block.js +0 -60
  86. package/dist/es2019/plugins/block-type/commands/insert-block.js +0 -55
  87. package/dist/esm/plugins/block-type/commands/insert-block.js +0 -53
  88. package/dist/types/plugins/block-type/commands/insert-block.d.ts +0 -5
  89. package/dist/types-ts4.5/plugins/block-type/commands/insert-block.d.ts +0 -5
  90. /package/dist/cjs/plugins/{block-type/commands → code-block}/transform-to-code-block.js +0 -0
  91. /package/dist/es2019/plugins/{block-type/commands → code-block}/transform-to-code-block.js +0 -0
  92. /package/dist/esm/plugins/{block-type/commands → code-block}/transform-to-code-block.js +0 -0
  93. /package/dist/types/plugins/{block-type/commands → code-block}/transform-to-code-block.d.ts +0 -0
  94. /package/dist/types-ts4.5/plugins/{block-type/commands → code-block}/transform-to-code-block.d.ts +0 -0
@@ -1,13 +1,8 @@
1
- import { findWrapping } from '@atlaskit/editor-prosemirror/transform';
2
- import { safeInsert } from '@atlaskit/editor-prosemirror/utils';
3
- import { PanelType } from '@atlaskit/adf-schema';
4
1
  import { CellSelection } from '@atlaskit/editor-tables';
5
- import { shouldSplitSelectedNodeOnNodeInsertion } from '@atlaskit/editor-common/insert';
6
2
  import { ACTION, ACTION_SUBJECT, ACTION_SUBJECT_ID, EVENT_TYPE } from '@atlaskit/editor-common/analytics';
7
3
  import { withAnalytics } from '@atlaskit/editor-common/editor-analytics';
8
- import { filterChildrenBetween, removeBlockMarks } from '@atlaskit/editor-common/utils';
9
- import { CODE_BLOCK, BLOCK_QUOTE, PANEL, HEADINGS_BY_NAME, NORMAL_TEXT } from '../types';
10
- import { transformToCodeBlockAction } from './transform-to-code-block';
4
+ import { filterChildrenBetween, wrapSelectionIn } from '@atlaskit/editor-common/utils';
5
+ import { HEADINGS_BY_NAME, NORMAL_TEXT } from '../types';
11
6
  export function setBlockType(name) {
12
7
  return (state, dispatch) => {
13
8
  const {
@@ -132,27 +127,13 @@ export const setHeadingWithAnalytics = (newHeadingLevel, inputMethod, editorAnal
132
127
  }
133
128
  })(setHeading(newHeadingLevel)));
134
129
  };
135
- export function insertBlockType(name) {
130
+ function insertBlockQuote() {
136
131
  return function (state, dispatch) {
137
132
  const {
138
133
  nodes
139
134
  } = state.schema;
140
- switch (name) {
141
- case BLOCK_QUOTE.name:
142
- if (nodes.paragraph && nodes.blockquote) {
143
- return wrapSelectionIn(nodes.blockquote)(state, dispatch);
144
- }
145
- break;
146
- case CODE_BLOCK.name:
147
- if (nodes.codeBlock) {
148
- return insertCodeBlock()(state, dispatch);
149
- }
150
- break;
151
- case PANEL.name:
152
- if (nodes.panel && nodes.paragraph) {
153
- return wrapSelectionIn(nodes.panel)(state, dispatch);
154
- }
155
- break;
135
+ if (nodes.paragraph && nodes.blockquote) {
136
+ return wrapSelectionIn(nodes.blockquote)(state, dispatch);
156
137
  }
157
138
  return false;
158
139
  };
@@ -166,200 +147,17 @@ export function insertBlockType(name) {
166
147
  * otherwise Editor becomes very sad and crashes
167
148
  * @returns - command that inserts block type
168
149
  */
169
- export const insertBlockTypesWithAnalytics = (name, inputMethod, editorAnalyticsApi) => {
170
- switch (name) {
171
- case BLOCK_QUOTE.name:
172
- return withAnalytics(editorAnalyticsApi, {
173
- action: ACTION.FORMATTED,
174
- actionSubject: ACTION_SUBJECT.TEXT,
175
- eventType: EVENT_TYPE.TRACK,
176
- actionSubjectId: ACTION_SUBJECT_ID.FORMAT_BLOCK_QUOTE,
177
- attributes: {
178
- inputMethod
179
- }
180
- })(insertBlockType(name));
181
- case CODE_BLOCK.name:
182
- return withAnalytics(editorAnalyticsApi, {
183
- action: ACTION.INSERTED,
184
- actionSubject: ACTION_SUBJECT.DOCUMENT,
185
- actionSubjectId: ACTION_SUBJECT_ID.CODE_BLOCK,
186
- attributes: {
187
- inputMethod: inputMethod
188
- },
189
- eventType: EVENT_TYPE.TRACK
190
- })(insertBlockType(name));
191
- case PANEL.name:
192
- return withAnalytics(editorAnalyticsApi, {
193
- action: ACTION.INSERTED,
194
- actionSubject: ACTION_SUBJECT.DOCUMENT,
195
- actionSubjectId: ACTION_SUBJECT_ID.PANEL,
196
- attributes: {
197
- inputMethod: inputMethod,
198
- panelType: PanelType.INFO // only info panels can be inserted from toolbar
199
- },
200
-
201
- eventType: EVENT_TYPE.TRACK
202
- })(insertBlockType(name));
203
- default:
204
- return insertBlockType(name);
205
- }
206
- };
207
-
208
- /**
209
- * This function creates a new transaction that wraps the current selection
210
- * in the specified node type if it results in a valid transaction.
211
- * If not valid, it performs a safe insert operation.
212
- *
213
- * Example of when wrapping might not be valid is when attempting to wrap
214
- * content that is already inside a panel with another panel
215
- */
216
- export function createWrapSelectionTransaction({
217
- state,
218
- type,
219
- nodeAttributes
220
- }) {
221
- let {
222
- tr
223
- } = state;
224
- const {
225
- alignment,
226
- indentation
227
- } = state.schema.marks;
228
-
229
- /** Alignment or Indentation is not valid inside block types */
230
- const removeAlignTr = removeBlockMarks(state, [alignment, indentation]);
231
- tr = removeAlignTr || tr;
232
-
233
- /**Get range and wrapping needed for the selection*/
234
- const {
235
- range,
236
- wrapping
237
- } = getWrappingOptions(state, type, nodeAttributes);
238
- if (wrapping) {
239
- tr.wrap(range, wrapping).scrollIntoView();
240
- } else {
241
- /** We always want to append a block type */
242
- safeInsert(type.createAndFill(nodeAttributes))(tr).scrollIntoView();
243
- }
244
- return tr;
245
- }
246
- function getWrappingOptions(state, type, nodeAttributes) {
247
- const {
248
- $from,
249
- $to
250
- } = state.selection;
251
- const range = $from.blockRange($to);
252
- let isAllowedChild = true;
253
- /**
254
- * Added a check to avoid wrapping codeblock
255
- */
256
- if (state.selection.empty) {
257
- state.doc.nodesBetween($from.pos, $to.pos, node => {
258
- if (!isAllowedChild) {
259
- return false;
260
- }
261
- return isAllowedChild = node.type !== state.schema.nodes.codeBlock;
262
- });
263
- }
264
- const wrapping = isAllowedChild && range && findWrapping(range, type, nodeAttributes);
265
- return {
266
- range,
267
- wrapping
268
- };
269
- }
270
-
271
- /**
272
- * Function will add wrapping node.
273
- * 1. If currently selected blocks can be wrapped in the wrapper type it will wrap them.
274
- * 2. If current block can not be wrapped inside wrapping block it will create a new block below selection,
275
- * and set selection on it.
276
- */
277
- function wrapSelectionIn(type) {
278
- return function (state, dispatch) {
279
- let tr = createWrapSelectionTransaction({
280
- state,
281
- type
282
- });
283
- if (dispatch) {
284
- dispatch(tr);
285
- }
286
- return true;
287
- };
288
- }
289
-
290
- /**
291
- * This function creates a new transaction that inserts a code block,
292
- * if there is text selected it will wrap the current selection if not it will
293
- * append the codeblock to the end of the document.
294
- */
295
- export function createInsertCodeBlockTransaction({
296
- state
297
- }) {
298
- var _state$selection$$fro;
299
- let {
300
- tr
301
- } = state;
302
- const {
303
- from
304
- } = state.selection;
305
- const {
306
- codeBlock
307
- } = state.schema.nodes;
308
- const grandParentNodeType = (_state$selection$$fro = state.selection.$from.node(-1)) === null || _state$selection$$fro === void 0 ? void 0 : _state$selection$$fro.type;
309
- const parentNodeType = state.selection.$from.parent.type;
310
-
311
- /** We always want to append a codeBlock unless we're inserting into a paragraph
312
- * AND it's a valid child of the grandparent node.
313
- * Insert the current selection as codeBlock content unless it contains nodes other
314
- * than paragraphs and inline.
315
- */
316
- const canInsertCodeBlock = shouldSplitSelectedNodeOnNodeInsertion({
317
- parentNodeType,
318
- grandParentNodeType,
319
- content: codeBlock.createAndFill()
320
- }) && contentAllowedInCodeBlock(state);
321
- if (canInsertCodeBlock) {
322
- tr = transformToCodeBlockAction(state, from);
323
- } else {
324
- safeInsert(codeBlock.createAndFill())(tr).scrollIntoView();
325
- }
326
- return tr;
327
- }
328
-
329
- /**
330
- * Function will insert code block at current selection if block is empty or below current selection and set focus on it.
331
- */
332
- function insertCodeBlock() {
333
- return function (state, dispatch) {
334
- let tr = createInsertCodeBlockTransaction({
335
- state
336
- });
337
- if (dispatch) {
338
- dispatch(tr);
339
- }
340
- return true;
341
- };
342
- }
343
-
344
- /**
345
- * Check if the current selection contains any nodes that are not permitted
346
- * as codeBlock child nodes. Note that this allows paragraphs and inline nodes
347
- * as we extract their text content.
348
- */
349
- function contentAllowedInCodeBlock(state) {
350
- const {
351
- $from,
352
- $to
353
- } = state.selection;
354
- let isAllowedChild = true;
355
- state.doc.nodesBetween($from.pos, $to.pos, node => {
356
- if (!isAllowedChild) {
357
- return false;
150
+ export const insertBlockQuoteWithAnalytics = (inputMethod, editorAnalyticsApi) => {
151
+ return withAnalytics(editorAnalyticsApi, {
152
+ action: ACTION.FORMATTED,
153
+ actionSubject: ACTION_SUBJECT.TEXT,
154
+ eventType: EVENT_TYPE.TRACK,
155
+ actionSubjectId: ACTION_SUBJECT_ID.FORMAT_BLOCK_QUOTE,
156
+ attributes: {
157
+ inputMethod: inputMethod
358
158
  }
359
- return isAllowedChild = node.type === state.schema.nodes.listItem || node.type === state.schema.nodes.bulletList || node.type === state.schema.nodes.orderedList || node.type === state.schema.nodes.paragraph || node.isInline || node.isText;
360
- });
361
- return isAllowedChild;
362
- }
159
+ })(insertBlockQuote());
160
+ };
363
161
  export const cleanUpAtTheStartOfDocument = (state, dispatch) => {
364
162
  const {
365
163
  $cursor
@@ -1,5 +1,3 @@
1
- export { cleanUpAtTheStartOfDocument, insertBlockType, insertBlockTypesWithAnalytics, setBlockType, setBlockTypeWithAnalytics, setHeading, setHeadingWithAnalytics, setNormalText, setNormalTextWithAnalytics } from './block-type';
2
- export { insertBlock } from './insert-block';
3
- export { isConvertableToCodeBlock, transformToCodeBlockAction } from './transform-to-code-block';
1
+ export { cleanUpAtTheStartOfDocument, insertBlockQuoteWithAnalytics, setBlockType, setBlockTypeWithAnalytics, setHeading, setHeadingWithAnalytics, setNormalText, setNormalTextWithAnalytics } from './block-type';
4
2
  export { deleteAndMoveCursor } from './delete-and-move-cursor';
5
3
  export { deleteBlockContent } from './delete-block-content';
@@ -11,6 +11,7 @@ import inputRulePlugin from './pm-plugins/input-rule';
11
11
  import ToolbarBlockType from './ui/ToolbarBlockType';
12
12
  import { setBlockTypeWithAnalytics } from './commands';
13
13
  import { messages } from './messages';
14
+ import { insertBlockQuoteWithAnalytics } from './commands/block-type';
14
15
  const headingPluginOptions = ({
15
16
  formatMessage
16
17
  }, isAllowed, editorAnalyticsApi) => {
@@ -135,6 +136,22 @@ const blockTypePlugin = ({
135
136
  }
136
137
  }];
137
138
  },
139
+ actions: {
140
+ insertBlockQuote(inputMethod) {
141
+ var _api$analytics4;
142
+ return insertBlockQuoteWithAnalytics(inputMethod, api === null || api === void 0 ? void 0 : (_api$analytics4 = api.analytics) === null || _api$analytics4 === void 0 ? void 0 : _api$analytics4.actions);
143
+ },
144
+ setBlockType(name, inputMethod) {
145
+ var _api$analytics5;
146
+ return setBlockTypeWithAnalytics(name, inputMethod, api === null || api === void 0 ? void 0 : (_api$analytics5 = api.analytics) === null || _api$analytics5 === void 0 ? void 0 : _api$analytics5.actions);
147
+ }
148
+ },
149
+ getSharedState(editorState) {
150
+ if (!editorState) {
151
+ return;
152
+ }
153
+ return pluginKey.getState(editorState);
154
+ },
138
155
  primaryToolbarComponent({
139
156
  editorView,
140
157
  popupsMountPoint,
@@ -147,8 +164,8 @@ const blockTypePlugin = ({
147
164
  }) {
148
165
  const isSmall = options && options.isUndoRedoButtonsEnabled ? toolbarSize < ToolbarSize.XXL : toolbarSize < ToolbarSize.XL;
149
166
  const boundSetBlockType = name => {
150
- var _api$analytics4;
151
- return setBlockTypeWithAnalytics(name, INPUT_METHOD.TOOLBAR, api === null || api === void 0 ? void 0 : (_api$analytics4 = api.analytics) === null || _api$analytics4 === void 0 ? void 0 : _api$analytics4.actions)(editorView.state, editorView.dispatch);
167
+ var _api$analytics6;
168
+ return setBlockTypeWithAnalytics(name, INPUT_METHOD.TOOLBAR, api === null || api === void 0 ? void 0 : (_api$analytics6 = api.analytics) === null || _api$analytics6 === void 0 ? void 0 : _api$analytics6.actions)(editorView.state, editorView.dispatch);
152
169
  };
153
170
  return /*#__PURE__*/React.createElement(WithPluginState, {
154
171
  editorView: editorView,
@@ -174,9 +191,9 @@ const blockTypePlugin = ({
174
191
  },
175
192
  pluginsOptions: {
176
193
  quickInsert: intl => {
177
- var _api$analytics5, _api$analytics6;
194
+ var _api$analytics7, _api$analytics8;
178
195
  const exclude = options && options.allowBlockType && options.allowBlockType.exclude ? options.allowBlockType.exclude : [];
179
- return [...blockquotePluginOptions(intl, exclude.indexOf('blockquote') === -1, api === null || api === void 0 ? void 0 : (_api$analytics5 = api.analytics) === null || _api$analytics5 === void 0 ? void 0 : _api$analytics5.actions), ...headingPluginOptions(intl, exclude.indexOf('heading') === -1, api === null || api === void 0 ? void 0 : (_api$analytics6 = api.analytics) === null || _api$analytics6 === void 0 ? void 0 : _api$analytics6.actions)];
196
+ return [...blockquotePluginOptions(intl, exclude.indexOf('blockquote') === -1, api === null || api === void 0 ? void 0 : (_api$analytics7 = api.analytics) === null || _api$analytics7 === void 0 ? void 0 : _api$analytics7.actions), ...headingPluginOptions(intl, exclude.indexOf('heading') === -1, api === null || api === void 0 ? void 0 : (_api$analytics8 = api.analytics) === null || _api$analytics8 === void 0 ? void 0 : _api$analytics8.actions)];
180
197
  }
181
198
  }
182
199
  });
@@ -1,9 +1,7 @@
1
- import { safeInsert } from '@atlaskit/editor-prosemirror/utils';
2
1
  import { createRule, createPlugin, leafNodeReplacementCharacter } from '@atlaskit/prosemirror-input-rules';
3
2
  import { INPUT_METHOD, ACTION, ACTION_SUBJECT, EVENT_TYPE, ACTION_SUBJECT_ID } from '@atlaskit/editor-common/analytics';
4
3
  import { inputRuleWithAnalytics } from '@atlaskit/editor-common/utils';
5
- import { isConvertableToCodeBlock, transformToCodeBlockAction } from '../commands/transform-to-code-block';
6
- import { insertBlock } from '../commands/insert-block';
4
+ import { insertBlock } from '@atlaskit/editor-common/commands';
7
5
  import { createJoinNodesRule, createWrappingTextBlockRule } from '../utils';
8
6
  const MAX_HEADING_LEVEL = 6;
9
7
  function getHeadingLevel(match) {
@@ -33,7 +31,7 @@ function getHeadingRules(editorAnalyticsAPI, schema) {
33
31
  const hashRule = headingRule(schema.nodes.heading, MAX_HEADING_LEVEL);
34
32
  const leftNodeReplacementHashRule = createRule(new RegExp(`${leafNodeReplacementCharacter}(#{1,6})\\s$`), (state, match, start, end) => {
35
33
  const level = match[1].length;
36
- return insertBlock(state, schema.nodes.heading, `heading${level}`, start, end, {
34
+ return insertBlock(state, schema.nodes.heading, start, end, {
37
35
  level
38
36
  });
39
37
  });
@@ -62,7 +60,7 @@ function getBlockQuoteRules(editorAnalyticsAPI, schema) {
62
60
  // '> ' for blockquote
63
61
  const greatherThanRule = blockQuoteRule(schema.nodes.blockquote);
64
62
  const leftNodeReplacementGreatherRule = createRule(new RegExp(`${leafNodeReplacementCharacter}\\s*>\\s$`), (state, _match, start, end) => {
65
- return insertBlock(state, schema.nodes.blockquote, 'blockquote', start, end);
63
+ return insertBlock(state, schema.nodes.blockquote, start, end);
66
64
  });
67
65
 
68
66
  // Analytics V3 handler
@@ -77,54 +75,6 @@ function getBlockQuoteRules(editorAnalyticsAPI, schema) {
77
75
  }, editorAnalyticsAPI);
78
76
  return [ruleWithBlockQuoteAnalytics(greatherThanRule), ruleWithBlockQuoteAnalytics(leftNodeReplacementGreatherRule)];
79
77
  }
80
-
81
- /**
82
- * Get all code block input rules
83
- *
84
- * @param {Schema} schema
85
- * @returns {InputRuleWithHandler[]}
86
- */
87
- function getCodeBlockRules(editorAnalyticsAPI, schema) {
88
- const ruleAnalytics = inputRuleWithAnalytics({
89
- action: ACTION.INSERTED,
90
- actionSubject: ACTION_SUBJECT.DOCUMENT,
91
- actionSubjectId: ACTION_SUBJECT_ID.CODE_BLOCK,
92
- attributes: {
93
- inputMethod: INPUT_METHOD.FORMATTING
94
- },
95
- eventType: EVENT_TYPE.TRACK
96
- }, editorAnalyticsAPI);
97
- const validMatchLength = match => match.length > 0 && match[0].length === 3;
98
- const threeTildeRule = createRule(/(?!\s)(`{3,})$/, (state, match, start, end) => {
99
- if (!validMatchLength(match)) {
100
- return null;
101
- }
102
- const attributes = {};
103
- if (match[4]) {
104
- attributes.language = match[4];
105
- }
106
- if (isConvertableToCodeBlock(state)) {
107
- return transformToCodeBlockAction(state, start, attributes);
108
- }
109
- const tr = state.tr;
110
- tr.delete(start, end);
111
- const codeBlock = tr.doc.type.schema.nodes.codeBlock.createChecked();
112
- safeInsert(codeBlock)(tr);
113
- return tr;
114
- });
115
- const leftNodeReplacementThreeTildeRule = createRule(new RegExp(`((${leafNodeReplacementCharacter}\`{3,})|^\\s(\`{3,}))(\\S*)$`), (state, match, start, end) => {
116
- if (!validMatchLength(match)) {
117
- return null;
118
- }
119
- const attributes = {};
120
- if (match[4]) {
121
- attributes.language = match[4];
122
- }
123
- const inlineStart = Math.max(match.index + state.selection.$from.start(), 1);
124
- return insertBlock(state, schema.nodes.codeBlock, 'codeblock', inlineStart, end, attributes);
125
- });
126
- return [ruleAnalytics(threeTildeRule), ruleAnalytics(leftNodeReplacementThreeTildeRule)];
127
- }
128
78
  function inputRulePlugin(editorAnalyticsAPI, schema, featureFlags) {
129
79
  const rules = [];
130
80
  if (schema.nodes.heading) {
@@ -133,9 +83,6 @@ function inputRulePlugin(editorAnalyticsAPI, schema, featureFlags) {
133
83
  if (schema.nodes.blockquote) {
134
84
  rules.push(...getBlockQuoteRules(editorAnalyticsAPI, schema));
135
85
  }
136
- if (schema.nodes.codeBlock) {
137
- rules.push(...getCodeBlockRules(editorAnalyticsAPI, schema));
138
- }
139
86
  if (rules.length !== 0) {
140
87
  return createPlugin('block-type', rules, {
141
88
  isBlockNodeRule: true
@@ -4,7 +4,7 @@ import { INPUT_METHOD } from '@atlaskit/editor-common/analytics';
4
4
  import { bindKeymapWithCommand, moveUp, moveDown, findKeyMapForBrowser, undo as undoKeymap, redo as redoKeymap, backspace, deleteKey, forwardDelete, toggleBlockQuote, keymap, insertNewLine, findShortcutByKeymap } from '@atlaskit/editor-common/keymaps';
5
5
  import { insertNewLineWithAnalytics, createNewParagraphAbove, createNewParagraphBelow, deleteEmptyParagraphAndMoveBlockUp } from '@atlaskit/editor-common/utils';
6
6
  import * as blockTypes from '../types';
7
- import { cleanUpAtTheStartOfDocument, deleteAndMoveCursor, deleteBlockContent, insertBlockTypesWithAnalytics } from '../commands';
7
+ import { cleanUpAtTheStartOfDocument, deleteAndMoveCursor, deleteBlockContent, insertBlockQuoteWithAnalytics } from '../commands';
8
8
  import { isNodeAWrappingBlockNode } from '../utils';
9
9
  const backspaceCommand = chainCommands(cleanUpAtTheStartOfDocument, deleteBlockContent(isNodeAWrappingBlockNode), deleteAndMoveCursor);
10
10
  const del = chainCommands(deleteEmptyParagraphAndMoveBlockUp(isNodeAWrappingBlockNode), deleteBlockContent(isNodeAWrappingBlockNode), deleteAndMoveCursor);
@@ -19,7 +19,7 @@ export default function keymapPlugin(editorAnalyticsApi, schema, _featureFlags)
19
19
  bindKeymapWithCommand(deleteKey.common, del, list);
20
20
  bindKeymapWithCommand(forwardDelete.mac, del, list);
21
21
  if (schema.nodes[blockTypes.BLOCK_QUOTE.nodeName]) {
22
- bindKeymapWithCommand(findShortcutByKeymap(toggleBlockQuote), insertBlockTypesWithAnalytics(blockTypes.BLOCK_QUOTE.name, INPUT_METHOD.KEYBOARD, editorAnalyticsApi), list);
22
+ bindKeymapWithCommand(findShortcutByKeymap(toggleBlockQuote), insertBlockQuoteWithAnalytics(INPUT_METHOD.KEYBOARD, editorAnalyticsApi), list);
23
23
  }
24
24
  return keymap(list);
25
25
  }
@@ -1,11 +1,14 @@
1
- import { findParentNodeOfType, findSelectedNodeOfType, removeSelectedNode, removeParentNodeOfType, isNodeSelection } from '@atlaskit/editor-prosemirror/utils';
1
+ import { findParentNodeOfType, findSelectedNodeOfType, removeSelectedNode, removeParentNodeOfType, isNodeSelection, safeInsert } from '@atlaskit/editor-prosemirror/utils';
2
2
  import { NodeSelection } from '@atlaskit/editor-prosemirror/state';
3
3
  import { pluginKey } from './plugin-key';
4
4
  import { copySelectionPluginKey } from './pm-plugins/codeBlockCopySelectionPlugin';
5
5
  import { ACTIONS } from './pm-plugins/actions';
6
6
  import { copyToClipboard } from '../../utils/clipboard';
7
7
  import { addAnalytics } from '../analytics/utils';
8
- import { ACTION, ACTION_SUBJECT, EVENT_TYPE } from '../analytics/types';
8
+ import { shouldSplitSelectedNodeOnNodeInsertion } from '@atlaskit/editor-common/insert';
9
+ import { transformToCodeBlockAction } from './transform-to-code-block';
10
+ import { withAnalytics } from '@atlaskit/editor-common/editor-analytics';
11
+ import { ACTION, ACTION_SUBJECT, ACTION_SUBJECT_ID, EVENT_TYPE } from '@atlaskit/editor-common/analytics';
9
12
  export const removeCodeBlock = (state, dispatch) => {
10
13
  const {
11
14
  schema: {
@@ -127,4 +130,82 @@ export const resetShouldIgnoreFollowingMutations = (state, dispatch) => {
127
130
  dispatch(ignoreFollowingMutationsTr);
128
131
  }
129
132
  return true;
130
- };
133
+ };
134
+
135
+ /**
136
+ * This function creates a new transaction that inserts a code block,
137
+ * if there is text selected it will wrap the current selection if not it will
138
+ * append the codeblock to the end of the document.
139
+ */
140
+ export function createInsertCodeBlockTransaction({
141
+ state
142
+ }) {
143
+ var _state$selection$$fro;
144
+ let {
145
+ tr
146
+ } = state;
147
+ const {
148
+ from
149
+ } = state.selection;
150
+ const {
151
+ codeBlock
152
+ } = state.schema.nodes;
153
+ const grandParentNodeType = (_state$selection$$fro = state.selection.$from.node(-1)) === null || _state$selection$$fro === void 0 ? void 0 : _state$selection$$fro.type;
154
+ const parentNodeType = state.selection.$from.parent.type;
155
+
156
+ /** We always want to append a codeBlock unless we're inserting into a paragraph
157
+ * AND it's a valid child of the grandparent node.
158
+ * Insert the current selection as codeBlock content unless it contains nodes other
159
+ * than paragraphs and inline.
160
+ */
161
+ const canInsertCodeBlock = shouldSplitSelectedNodeOnNodeInsertion({
162
+ parentNodeType,
163
+ grandParentNodeType,
164
+ content: codeBlock.createAndFill()
165
+ }) && contentAllowedInCodeBlock(state);
166
+ if (canInsertCodeBlock) {
167
+ tr = transformToCodeBlockAction(state, from);
168
+ } else {
169
+ safeInsert(codeBlock.createAndFill())(tr).scrollIntoView();
170
+ }
171
+ return tr;
172
+ }
173
+
174
+ /**
175
+ * Check if the current selection contains any nodes that are not permitted
176
+ * as codeBlock child nodes. Note that this allows paragraphs and inline nodes
177
+ * as we extract their text content.
178
+ */
179
+ function contentAllowedInCodeBlock(state) {
180
+ const {
181
+ $from,
182
+ $to
183
+ } = state.selection;
184
+ let isAllowedChild = true;
185
+ state.doc.nodesBetween($from.pos, $to.pos, node => {
186
+ if (!isAllowedChild) {
187
+ return false;
188
+ }
189
+ return isAllowedChild = node.type === state.schema.nodes.listItem || node.type === state.schema.nodes.bulletList || node.type === state.schema.nodes.orderedList || node.type === state.schema.nodes.paragraph || node.isInline || node.isText;
190
+ });
191
+ return isAllowedChild;
192
+ }
193
+ export function insertCodeBlockWithAnalytics(inputMethod, analyticsAPI) {
194
+ return withAnalytics(analyticsAPI, {
195
+ action: ACTION.INSERTED,
196
+ actionSubject: ACTION_SUBJECT.DOCUMENT,
197
+ actionSubjectId: ACTION_SUBJECT_ID.CODE_BLOCK,
198
+ attributes: {
199
+ inputMethod: inputMethod
200
+ },
201
+ eventType: EVENT_TYPE.TRACK
202
+ })(function (state, dispatch) {
203
+ let tr = createInsertCodeBlockTransaction({
204
+ state
205
+ });
206
+ if (dispatch) {
207
+ dispatch(tr);
208
+ }
209
+ return true;
210
+ });
211
+ }
@@ -9,10 +9,8 @@ import { ACTION, ACTION_SUBJECT, ACTION_SUBJECT_ID, INPUT_METHOD, EVENT_TYPE } f
9
9
  import { IconCode } from '@atlaskit/editor-common/quick-insert';
10
10
  import { messages } from '../block-type/messages';
11
11
  import refreshBrowserSelectionOnChange from './refresh-browser-selection';
12
- // Theres an existing interelationship between these files, where the imported function is being called for code-block
13
- // Insertions via the drop down menu
14
- // tslint-ignore-next-line
15
- import { createInsertCodeBlockTransaction } from '../block-type/commands/block-type';
12
+ import { createInsertCodeBlockTransaction, insertCodeBlockWithAnalytics } from './actions';
13
+ import { createCodeBlockInputRule } from './pm-plugins/input-rule';
16
14
  const codeBlockPlugin = ({
17
15
  config: options,
18
16
  api
@@ -37,6 +35,14 @@ const codeBlockPlugin = ({
37
35
  appearance: (_options$appearance = options === null || options === void 0 ? void 0 : options.appearance) !== null && _options$appearance !== void 0 ? _options$appearance : 'comment'
38
36
  });
39
37
  }
38
+ }, {
39
+ name: 'codeBlockInputRule',
40
+ plugin: ({
41
+ schema
42
+ }) => {
43
+ var _api$analytics;
44
+ return createCodeBlockInputRule(schema, api === null || api === void 0 ? void 0 : (_api$analytics = api.analytics) === null || _api$analytics === void 0 ? void 0 : _api$analytics.actions);
45
+ }
40
46
  }, {
41
47
  name: 'codeBlockIDEKeyBindings',
42
48
  plugin: () => ideUX(api)
@@ -55,6 +61,15 @@ const codeBlockPlugin = ({
55
61
  onEditorViewStateUpdated(props) {
56
62
  refreshBrowserSelectionOnChange(props.originalTransaction, props.newEditorState);
57
63
  },
64
+ actions: {
65
+ /*
66
+ * Function will insert code block at current selection if block is empty or below current selection and set focus on it.
67
+ */
68
+ insertCodeBlock: inputMethod => {
69
+ var _api$analytics2;
70
+ return insertCodeBlockWithAnalytics(inputMethod, api === null || api === void 0 ? void 0 : (_api$analytics2 = api.analytics) === null || _api$analytics2 === void 0 ? void 0 : _api$analytics2.actions);
71
+ }
72
+ },
58
73
  pluginsOptions: {
59
74
  quickInsert: ({
60
75
  formatMessage
@@ -66,12 +81,12 @@ const codeBlockPlugin = ({
66
81
  priority: 700,
67
82
  keyshortcut: '```',
68
83
  icon: () => /*#__PURE__*/React.createElement(IconCode, null),
69
- action(insert, state) {
70
- var _api$analytics;
84
+ action(_insert, state) {
85
+ var _api$analytics3;
71
86
  const tr = createInsertCodeBlockTransaction({
72
87
  state
73
88
  });
74
- api === null || api === void 0 ? void 0 : (_api$analytics = api.analytics) === null || _api$analytics === void 0 ? void 0 : _api$analytics.actions.attachAnalyticsEvent({
89
+ api === null || api === void 0 ? void 0 : (_api$analytics3 = api.analytics) === null || _api$analytics3 === void 0 ? void 0 : _api$analytics3.actions.attachAnalyticsEvent({
75
90
  action: ACTION.INSERTED,
76
91
  actionSubject: ACTION_SUBJECT.DOCUMENT,
77
92
  actionSubjectId: ACTION_SUBJECT_ID.CODE_BLOCK,
@@ -0,0 +1,60 @@
1
+ import { safeInsert } from '@atlaskit/editor-prosemirror/utils';
2
+ import { createRule, createPlugin, leafNodeReplacementCharacter } from '@atlaskit/prosemirror-input-rules';
3
+ import { INPUT_METHOD, ACTION, ACTION_SUBJECT, EVENT_TYPE, ACTION_SUBJECT_ID } from '@atlaskit/editor-common/analytics';
4
+ import { inputRuleWithAnalytics } from '@atlaskit/editor-common/utils';
5
+ import { isConvertableToCodeBlock, transformToCodeBlockAction } from '../transform-to-code-block';
6
+ import { insertBlock } from '@atlaskit/editor-common/commands';
7
+ export function createCodeBlockInputRule(schema, editorAnalyticsAPI) {
8
+ const rules = getCodeBlockRules(editorAnalyticsAPI, schema);
9
+ return createPlugin('code-block-input-rule', rules, {
10
+ isBlockNodeRule: true
11
+ });
12
+ }
13
+
14
+ /**
15
+ * Get all code block input rules
16
+ *
17
+ * @param {Schema} schema
18
+ * @returns {InputRuleWithHandler[]}
19
+ */
20
+ function getCodeBlockRules(editorAnalyticsAPI, schema) {
21
+ const ruleAnalytics = inputRuleWithAnalytics({
22
+ action: ACTION.INSERTED,
23
+ actionSubject: ACTION_SUBJECT.DOCUMENT,
24
+ actionSubjectId: ACTION_SUBJECT_ID.CODE_BLOCK,
25
+ attributes: {
26
+ inputMethod: INPUT_METHOD.FORMATTING
27
+ },
28
+ eventType: EVENT_TYPE.TRACK
29
+ }, editorAnalyticsAPI);
30
+ const validMatchLength = match => match.length > 0 && match[0].length === 3;
31
+ const threeTildeRule = createRule(/(?!\s)(`{3,})$/, (state, match, start, end) => {
32
+ if (!validMatchLength(match)) {
33
+ return null;
34
+ }
35
+ const attributes = {};
36
+ if (match[4]) {
37
+ attributes.language = match[4];
38
+ }
39
+ if (isConvertableToCodeBlock(state)) {
40
+ return transformToCodeBlockAction(state, start, attributes);
41
+ }
42
+ const tr = state.tr;
43
+ tr.delete(start, end);
44
+ const codeBlock = tr.doc.type.schema.nodes.codeBlock.createChecked();
45
+ safeInsert(codeBlock)(tr);
46
+ return tr;
47
+ });
48
+ const leftNodeReplacementThreeTildeRule = createRule(new RegExp(`((${leafNodeReplacementCharacter}\`{3,})|^\\s(\`{3,}))(\\S*)$`), (state, match, start, end) => {
49
+ if (!validMatchLength(match)) {
50
+ return null;
51
+ }
52
+ const attributes = {};
53
+ if (match[4]) {
54
+ attributes.language = match[4];
55
+ }
56
+ const inlineStart = Math.max(match.index + state.selection.$from.start(), 1);
57
+ return insertBlock(state, schema.nodes.codeBlock, inlineStart, end, attributes);
58
+ });
59
+ return [ruleAnalytics(threeTildeRule), ruleAnalytics(leftNodeReplacementThreeTildeRule)];
60
+ }
@@ -123,7 +123,8 @@ export const createToolbarCopyCommandForNode = nodeType => (state, dispatch) =>
123
123
  div.firstChild.setAttribute('data-pm-slice', '0 0 []');
124
124
  }
125
125
  // ED-17083 safari seems have bugs for extension copy because exntension do not have a child text(innerText) and it will not recognized as html in clipboard, this could be merge into one if this extension fixed children issue or safari fix the copy bug
126
- if (browser.safari && state.selection instanceof NodeSelection && state.selection.node.type === state.schema.nodes.extension) {
126
+ // MEX-2528 safari has a bug related to the mediaSingle node with border or link. The image tag within the clipboard is not recognized as HTML when using the ClipboardItem API. To address this, we have to switch to ClipboardPolyfill
127
+ if (browser.safari && state.selection instanceof NodeSelection && (state.selection.node.type === state.schema.nodes.extension || state.selection.node.type === state.schema.nodes.mediaSingle)) {
127
128
  copyHTMLToClipboardPolyfill(div);
128
129
  } else {
129
130
  copyHTMLToClipboard(div);