@atlaskit/editor-plugin-block-type 4.0.10 → 4.0.12

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 (62) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/dist/cjs/blockTypePlugin.js +9 -11
  3. package/dist/cjs/pm-plugins/block-types.js +9 -2
  4. package/dist/cjs/pm-plugins/commands/block-type.js +63 -15
  5. package/dist/cjs/pm-plugins/commands/wrapSelectionIn.js +61 -0
  6. package/dist/cjs/pm-plugins/keymap.js +6 -4
  7. package/dist/cjs/pm-plugins/main.js +15 -7
  8. package/dist/cjs/pm-plugins/ui/FloatingToolbarComponent.js +5 -0
  9. package/dist/cjs/pm-plugins/ui/PrimaryToolbarComponent.js +7 -2
  10. package/dist/cjs/pm-plugins/ui/ToolbarBlockType/index.js +22 -19
  11. package/dist/cjs/pm-plugins/ui/ToolbarBlockType/styled.js +1 -1
  12. package/dist/cjs/pm-plugins/utils.js +17 -1
  13. package/dist/es2019/blockTypePlugin.js +11 -9
  14. package/dist/es2019/index.js +3 -0
  15. package/dist/es2019/pm-plugins/block-types.js +8 -1
  16. package/dist/es2019/pm-plugins/commands/block-type.js +61 -13
  17. package/dist/es2019/pm-plugins/commands/wrapSelectionIn.js +54 -0
  18. package/dist/es2019/pm-plugins/keymap.js +3 -1
  19. package/dist/es2019/pm-plugins/main.js +12 -6
  20. package/dist/es2019/pm-plugins/ui/FloatingToolbarComponent.js +5 -0
  21. package/dist/es2019/pm-plugins/ui/PrimaryToolbarComponent.js +7 -2
  22. package/dist/es2019/pm-plugins/ui/ToolbarBlockType/index.js +12 -6
  23. package/dist/es2019/pm-plugins/ui/ToolbarBlockType/styled.js +2 -2
  24. package/dist/es2019/pm-plugins/utils.js +15 -1
  25. package/dist/es2019/ui/consts.js +3 -0
  26. package/dist/esm/blockTypePlugin.js +11 -8
  27. package/dist/esm/index.js +3 -0
  28. package/dist/esm/pm-plugins/block-types.js +8 -1
  29. package/dist/esm/pm-plugins/commands/block-type.js +62 -15
  30. package/dist/esm/pm-plugins/commands/wrapSelectionIn.js +55 -0
  31. package/dist/esm/pm-plugins/keymap.js +3 -1
  32. package/dist/esm/pm-plugins/main.js +14 -6
  33. package/dist/esm/pm-plugins/ui/FloatingToolbarComponent.js +5 -0
  34. package/dist/esm/pm-plugins/ui/PrimaryToolbarComponent.js +7 -2
  35. package/dist/esm/pm-plugins/ui/ToolbarBlockType/index.js +22 -19
  36. package/dist/esm/pm-plugins/ui/ToolbarBlockType/styled.js +2 -2
  37. package/dist/esm/pm-plugins/utils.js +17 -1
  38. package/dist/esm/ui/consts.js +3 -0
  39. package/dist/types/blockTypePlugin.d.ts +0 -2
  40. package/dist/types/blockTypePluginType.d.ts +2 -1
  41. package/dist/types/index.d.ts +1 -1
  42. package/dist/types/pm-plugins/block-types.d.ts +1 -0
  43. package/dist/types/pm-plugins/commands/block-type.d.ts +6 -5
  44. package/dist/types/pm-plugins/commands/wrapSelectionIn.d.ts +3 -0
  45. package/dist/types/pm-plugins/main.d.ts +3 -1
  46. package/dist/types/pm-plugins/types.d.ts +1 -0
  47. package/dist/types/pm-plugins/ui/ToolbarBlockType/index.d.ts +2 -1
  48. package/dist/types-ts4.5/blockTypePlugin.d.ts +0 -2
  49. package/dist/types-ts4.5/blockTypePluginType.d.ts +2 -1
  50. package/dist/types-ts4.5/index.d.ts +1 -1
  51. package/dist/types-ts4.5/pm-plugins/block-types.d.ts +1 -0
  52. package/dist/types-ts4.5/pm-plugins/commands/block-type.d.ts +6 -5
  53. package/dist/types-ts4.5/pm-plugins/commands/wrapSelectionIn.d.ts +3 -0
  54. package/dist/types-ts4.5/pm-plugins/main.d.ts +3 -1
  55. package/dist/types-ts4.5/pm-plugins/types.d.ts +1 -0
  56. package/dist/types-ts4.5/pm-plugins/ui/ToolbarBlockType/index.d.ts +2 -1
  57. package/package.json +6 -6
  58. package/dist/cjs/pm-plugins/commands/index.js +0 -62
  59. package/dist/es2019/pm-plugins/commands/index.js +0 -3
  60. package/dist/esm/pm-plugins/commands/index.js +0 -3
  61. package/dist/types/pm-plugins/commands/index.d.ts +0 -4
  62. package/dist/types-ts4.5/pm-plugins/commands/index.d.ts +0 -4
@@ -7,6 +7,7 @@ exports.areBlockTypesDisabled = areBlockTypesDisabled;
7
7
  exports.isNodeAWrappingBlockNode = exports.createWrappingTextBlockRule = exports.createJoinNodesRule = void 0;
8
8
  var _utils = require("@atlaskit/editor-common/utils");
9
9
  var _platformFeatureFlags = require("@atlaskit/platform-feature-flags");
10
+ var _experiments = require("@atlaskit/tmp-editor-statsig/experiments");
10
11
  var _blockTypes = require("./block-types");
11
12
  var isNodeAWrappingBlockNode = exports.isNodeAWrappingBlockNode = function isNodeAWrappingBlockNode(node) {
12
13
  if (!node) {
@@ -82,7 +83,22 @@ function getSelectedWrapperNodes(state) {
82
83
  */
83
84
  function areBlockTypesDisabled(state) {
84
85
  var nodesTypes = getSelectedWrapperNodes(state);
85
- var panel = state.schema.nodes.panel;
86
+ var _state$schema$nodes2 = state.schema.nodes,
87
+ panel = _state$schema$nodes2.panel,
88
+ blockquote = _state$schema$nodes2.blockquote;
89
+ if ((0, _experiments.editorExperiment)('platform_editor_blockquote_in_text_formatting_menu', true)) {
90
+ var hasQuote = false;
91
+ var _state$selection2 = state.selection,
92
+ $from = _state$selection2.$from,
93
+ $to = _state$selection2.$to;
94
+ state.doc.nodesBetween($from.pos, $to.pos, function (node) {
95
+ hasQuote = node.type === blockquote;
96
+ return !hasQuote;
97
+ });
98
+ return nodesTypes.filter(function (type) {
99
+ return type !== panel;
100
+ }).length > 0 || hasQuote;
101
+ }
86
102
  return nodesTypes.filter(function (type) {
87
103
  return type !== panel;
88
104
  }).length > 0;
@@ -7,8 +7,7 @@ import { IconHeading, IconQuote } from '@atlaskit/editor-common/quick-insert';
7
7
  import { ToolbarSize } from '@atlaskit/editor-common/types';
8
8
  import { fg } from '@atlaskit/platform-feature-flags';
9
9
  import { editorExperiment } from '@atlaskit/tmp-editor-statsig/experiments';
10
- import { setBlockTypeWithAnalytics } from './pm-plugins/commands';
11
- import { insertBlockQuoteWithAnalytics } from './pm-plugins/commands/block-type';
10
+ import { setBlockTypeWithAnalytics, insertBlockQuoteWithAnalytics, insertBlockQuoteWithAnalyticsCommand } from './pm-plugins/commands/block-type';
12
11
  import inputRulePlugin from './pm-plugins/input-rule';
13
12
  import keymapPlugin from './pm-plugins/keymap';
14
13
  import { createPlugin, pluginKey } from './pm-plugins/main';
@@ -144,7 +143,7 @@ const blockTypePlugin = ({
144
143
  name: 'blockType',
145
144
  plugin: ({
146
145
  dispatch
147
- }) => createPlugin(api, dispatch, options && options.lastNodeMustBeParagraph)
146
+ }) => createPlugin(api, dispatch, options && options.lastNodeMustBeParagraph, options === null || options === void 0 ? void 0 : options.includeBlockQuoteAsTextstyleOption)
148
147
  }, {
149
148
  name: 'blockTypeInputRule',
150
149
  plugin: ({
@@ -175,9 +174,13 @@ const blockTypePlugin = ({
175
174
  }
176
175
  },
177
176
  commands: {
178
- setTextLevel(level, inputMethod) {
177
+ setTextLevel(level, inputMethod, fromBlockQuote = false) {
179
178
  var _api$analytics4;
180
- return setBlockTypeWithAnalytics(level, inputMethod, api === null || api === void 0 ? void 0 : (_api$analytics4 = api.analytics) === null || _api$analytics4 === void 0 ? void 0 : _api$analytics4.actions);
179
+ return setBlockTypeWithAnalytics(level, inputMethod, api === null || api === void 0 ? void 0 : (_api$analytics4 = api.analytics) === null || _api$analytics4 === void 0 ? void 0 : _api$analytics4.actions, fromBlockQuote);
180
+ },
181
+ insertBlockQuote(inputMethod) {
182
+ var _api$analytics5;
183
+ return insertBlockQuoteWithAnalyticsCommand(inputMethod, api === null || api === void 0 ? void 0 : (_api$analytics5 = api.analytics) === null || _api$analytics5 === void 0 ? void 0 : _api$analytics5.actions);
181
184
  }
182
185
  },
183
186
  getSharedState(editorState) {
@@ -214,12 +217,11 @@ const blockTypePlugin = ({
214
217
  }
215
218
  },
216
219
  quickInsert: intl => {
217
- var _api$analytics5, _api$analytics6;
220
+ var _api$analytics6, _api$analytics7;
218
221
  const exclude = options && options.allowBlockType && options.allowBlockType.exclude ? options.allowBlockType.exclude : [];
219
- 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)];
222
+ return [...blockquotePluginOptions(intl, exclude.indexOf('blockquote') === -1, api === null || api === void 0 ? void 0 : (_api$analytics6 = api.analytics) === null || _api$analytics6 === void 0 ? void 0 : _api$analytics6.actions), ...headingPluginOptions(intl, exclude.indexOf('heading') === -1, api === null || api === void 0 ? void 0 : (_api$analytics7 = api.analytics) === null || _api$analytics7 === void 0 ? void 0 : _api$analytics7.actions)];
220
223
  }
221
224
  }
222
225
  };
223
226
  };
224
- export { blockTypePlugin };
225
- export { pluginKey } from './pm-plugins/main';
227
+ export { blockTypePlugin };
@@ -1 +1,4 @@
1
+ // Disable no-re-export rule for entry point files
2
+ /* eslint-disable @atlaskit/editor/no-re-export */
3
+
1
4
  export { blockTypePlugin } from './blockTypePlugin';
@@ -1,4 +1,5 @@
1
1
  import { blockTypeMessages as messages } from '@atlaskit/editor-common/messages';
2
+ import { editorExperiment } from '@atlaskit/tmp-editor-statsig/experiments';
2
3
  export const NORMAL_TEXT = {
3
4
  name: 'normal',
4
5
  title: messages.normal,
@@ -50,7 +51,8 @@ export const HEADING_6 = {
50
51
  export const BLOCK_QUOTE = {
51
52
  name: 'blockquote',
52
53
  title: messages.blockquote,
53
- nodeName: 'blockquote'
54
+ nodeName: 'blockquote',
55
+ tagName: 'blockquote'
54
56
  };
55
57
  export const CODE_BLOCK = {
56
58
  name: 'codeblock',
@@ -70,6 +72,11 @@ export const OTHER = {
70
72
  export const TEXT_BLOCK_TYPES = [NORMAL_TEXT, HEADING_1, HEADING_2, HEADING_3, HEADING_4, HEADING_5, HEADING_6];
71
73
  export const WRAPPER_BLOCK_TYPES = [BLOCK_QUOTE, CODE_BLOCK, PANEL];
72
74
  export const ALL_BLOCK_TYPES = TEXT_BLOCK_TYPES.concat(WRAPPER_BLOCK_TYPES);
75
+ export const getBlockTypesInDropdown = includeBlockQuoteAsTextstyleOption => {
76
+ return editorExperiment('platform_editor_blockquote_in_text_formatting_menu', true, {
77
+ exposure: true
78
+ }) && includeBlockQuoteAsTextstyleOption ? [...TEXT_BLOCK_TYPES, BLOCK_QUOTE] : TEXT_BLOCK_TYPES;
79
+ };
73
80
  export const HEADINGS_BY_LEVEL = TEXT_BLOCK_TYPES.reduce((acc, blockType) => {
74
81
  if (blockType.level && blockType.nodeName === 'heading') {
75
82
  acc[blockType.level] = blockType;
@@ -1,8 +1,10 @@
1
1
  import { ACTION, ACTION_SUBJECT, ACTION_SUBJECT_ID, EVENT_TYPE } from '@atlaskit/editor-common/analytics';
2
2
  import { withAnalytics } from '@atlaskit/editor-common/editor-analytics';
3
3
  import { filterChildrenBetween, wrapSelectionIn } from '@atlaskit/editor-common/utils';
4
+ import { Slice, Fragment } from '@atlaskit/editor-prosemirror/model';
4
5
  import { CellSelection } from '@atlaskit/editor-tables';
5
6
  import { HEADINGS_BY_NAME, NORMAL_TEXT } from '../block-types';
7
+ import { wrapSelectionInBlockType } from './wrapSelectionIn';
6
8
  export function setBlockType(name) {
7
9
  return ({
8
10
  tr
@@ -24,7 +26,7 @@ export function setBlockType(name) {
24
26
  return null;
25
27
  };
26
28
  }
27
- export function setHeading(level) {
29
+ export function setHeading(level, fromBlockQuote) {
28
30
  return function ({
29
31
  tr
30
32
  }) {
@@ -41,14 +43,27 @@ export function setHeading(level) {
41
43
  $from,
42
44
  $to
43
45
  }) => {
44
- tr.setBlockType($from.pos, $to.pos, schema.nodes.heading, {
45
- level
46
- });
46
+ if (fromBlockQuote) {
47
+ const range = $from.blockRange($to);
48
+ if (!range) {
49
+ return;
50
+ }
51
+ const content = $from.node().content;
52
+ const headingNode = schema.nodes.heading.createChecked({
53
+ level
54
+ }, content);
55
+ const slice = new Slice(Fragment.from(headingNode), 0, 0);
56
+ tr.replaceRange(range.start, range.end, slice);
57
+ } else {
58
+ tr.setBlockType($from.pos, $to.pos, schema.nodes.heading, {
59
+ level
60
+ });
61
+ }
47
62
  });
48
63
  return tr;
49
64
  };
50
65
  }
51
- export function setBlockTypeWithAnalytics(name, inputMethod, editorAnalyticsApi) {
66
+ export function setBlockTypeWithAnalytics(name, inputMethod, editorAnalyticsApi, fromBlockQuote) {
52
67
  return ({
53
68
  tr
54
69
  }) => {
@@ -56,20 +71,20 @@ export function setBlockTypeWithAnalytics(name, inputMethod, editorAnalyticsApi)
56
71
  nodes
57
72
  } = tr.doc.type.schema;
58
73
  if (name === 'normal' && nodes.paragraph) {
59
- return setNormalTextWithAnalytics(inputMethod, editorAnalyticsApi)({
74
+ return setNormalTextWithAnalytics(inputMethod, editorAnalyticsApi, fromBlockQuote)({
60
75
  tr
61
76
  });
62
77
  }
63
78
  const headingBlockType = HEADINGS_BY_NAME[name];
64
79
  if (headingBlockType && nodes.heading && headingBlockType.level) {
65
- return setHeadingWithAnalytics(headingBlockType.level, inputMethod, editorAnalyticsApi)({
80
+ return setHeadingWithAnalytics(headingBlockType.level, inputMethod, editorAnalyticsApi, fromBlockQuote)({
66
81
  tr
67
82
  });
68
83
  }
69
84
  return null;
70
85
  };
71
86
  }
72
- export function setNormalText() {
87
+ export function setNormalText(fromBlockQuote) {
73
88
  return function ({
74
89
  tr
75
90
  }) {
@@ -86,7 +101,15 @@ export function setNormalText() {
86
101
  $from,
87
102
  $to
88
103
  }) => {
89
- tr.setBlockType($from.pos, $to.pos, schema.nodes.paragraph);
104
+ if (fromBlockQuote) {
105
+ const range = $from.blockRange($to);
106
+ if (!range) {
107
+ return;
108
+ }
109
+ tr.lift(range, 0);
110
+ } else {
111
+ tr.setBlockType($from.pos, $to.pos, schema.nodes.paragraph);
112
+ }
90
113
  });
91
114
  return tr;
92
115
  };
@@ -121,7 +144,7 @@ function withCurrentHeadingLevel(fn) {
121
144
  });
122
145
  };
123
146
  }
124
- export function setNormalTextWithAnalytics(inputMethod, editorAnalyticsApi) {
147
+ export function setNormalTextWithAnalytics(inputMethod, editorAnalyticsApi, fromBlockQuote) {
125
148
  return withCurrentHeadingLevel(previousHeadingLevel => ({
126
149
  tr
127
150
  }) => {
@@ -136,12 +159,12 @@ export function setNormalTextWithAnalytics(inputMethod, editorAnalyticsApi) {
136
159
  previousHeadingLevel
137
160
  }
138
161
  })(tr);
139
- return setNormalText()({
162
+ return setNormalText(fromBlockQuote)({
140
163
  tr
141
164
  });
142
165
  });
143
166
  }
144
- export const setHeadingWithAnalytics = (newHeadingLevel, inputMethod, editorAnalyticsApi) => {
167
+ export const setHeadingWithAnalytics = (newHeadingLevel, inputMethod, editorAnalyticsApi, fromBlockQuote) => {
145
168
  return withCurrentHeadingLevel(previousHeadingLevel => ({
146
169
  tr
147
170
  }) => {
@@ -156,7 +179,7 @@ export const setHeadingWithAnalytics = (newHeadingLevel, inputMethod, editorAnal
156
179
  previousHeadingLevel
157
180
  }
158
181
  })(tr);
159
- return setHeading(newHeadingLevel)({
182
+ return setHeading(newHeadingLevel, fromBlockQuote)({
160
183
  tr
161
184
  });
162
185
  });
@@ -192,6 +215,31 @@ export const insertBlockQuoteWithAnalytics = (inputMethod, editorAnalyticsApi) =
192
215
  }
193
216
  })(insertBlockQuote());
194
217
  };
218
+ export function insertBlockQuoteWithAnalyticsCommand(inputMethod, editorAnalyticsApi) {
219
+ return withCurrentHeadingLevel(previousHeadingLevel => ({
220
+ tr
221
+ }) => {
222
+ const {
223
+ nodes
224
+ } = tr.doc.type.schema;
225
+
226
+ // TODO: analytics event
227
+
228
+ // editorAnalyticsApi?.attachAnalyticsEvent({
229
+ // action: ACTION.FORMATTED,
230
+ // actionSubject: ACTION_SUBJECT.TEXT,
231
+ // eventType: EVENT_TYPE.TRACK,
232
+ // actionSubjectId: ACTION_SUBJECT_ID.FORMAT_BLOCK_QUOTE,
233
+ // attributes: {
234
+ // inputMethod: inputMethod,
235
+ // },
236
+ // })(tr);
237
+
238
+ return wrapSelectionInBlockType(nodes.blockquote)({
239
+ tr
240
+ });
241
+ });
242
+ }
195
243
  export const cleanUpAtTheStartOfDocument = (state, dispatch) => {
196
244
  const {
197
245
  $cursor
@@ -0,0 +1,54 @@
1
+ import { Slice, Fragment } from '@atlaskit/editor-prosemirror/model';
2
+ import { findWrapping } from '@atlaskit/editor-prosemirror/transform';
3
+ export function wrapSelectionInBlockType(nodeType) {
4
+ return ({
5
+ tr
6
+ }) => {
7
+ const {
8
+ nodes
9
+ } = tr.doc.type.schema;
10
+ const {
11
+ alignment,
12
+ indentation
13
+ } = tr.doc.type.schema.marks;
14
+ if (nodes.paragraph && nodes.blockquote) {
15
+ /**Remove alignment and indentation marks from the selection */
16
+ const marksToRemove = [alignment, indentation];
17
+ const hasMark = mark => marksToRemove.indexOf(mark.type) > -1;
18
+ const not = fn => arg => !fn(arg);
19
+
20
+ /**
21
+ * When you need to toggle the selection
22
+ * when another type which does not allow alignment is applied
23
+ */
24
+ tr.doc.nodesBetween(tr.selection.from, tr.selection.to, (node, pos) => {
25
+ if (node.type === nodes.paragraph && node.marks.some(hasMark)) {
26
+ const resolvedPos = tr.doc.resolve(pos);
27
+ const withoutBlockMarks = node.marks.filter(not(hasMark));
28
+ tr = tr.setNodeMarkup(resolvedPos.pos, undefined, node.attrs, withoutBlockMarks);
29
+ }
30
+ });
31
+
32
+ /** Get range and wrapping needed for the selection */
33
+ const {
34
+ $from,
35
+ $to
36
+ } = tr.selection;
37
+ const range = $from.blockRange($to);
38
+ const wrapping = range && findWrapping(range, nodes.blockquote);
39
+ if (wrapping) {
40
+ /** Wrap the selection */
41
+ tr.wrap(range, wrapping).scrollIntoView();
42
+ } else {
43
+ /** If wrapping is not possible, replace with a blockquote */
44
+ const start = $from.start();
45
+ const end = $to.end();
46
+ const content = $from.node().content;
47
+ const blockquote = nodes.blockquote.create({}, nodes.paragraph.create({}, content));
48
+ const slice = new Slice(Fragment.from(blockquote), 0, 0);
49
+ tr.replaceRange(start, end, slice).scrollIntoView();
50
+ }
51
+ }
52
+ return tr;
53
+ };
54
+ }
@@ -4,7 +4,9 @@ import { createNewParagraphAbove, createNewParagraphBelow, deleteEmptyParagraphA
4
4
  import { chainCommands } from '@atlaskit/editor-prosemirror/commands';
5
5
  import { redo, undo } from '@atlaskit/editor-prosemirror/history';
6
6
  import * as blockTypes from './block-types';
7
- import { cleanUpAtTheStartOfDocument, deleteAndMoveCursor, deleteBlockContent, insertBlockQuoteWithAnalytics } from './commands';
7
+ import { cleanUpAtTheStartOfDocument, insertBlockQuoteWithAnalytics } from './commands/block-type';
8
+ import { deleteAndMoveCursor } from './commands/delete-and-move-cursor';
9
+ import { deleteBlockContent } from './commands/delete-block-content';
8
10
  import { isNodeAWrappingBlockNode } from './utils';
9
11
  const backspaceCommand = chainCommands(cleanUpAtTheStartOfDocument, deleteBlockContent(isNodeAWrappingBlockNode), deleteAndMoveCursor);
10
12
  const del = chainCommands(deleteEmptyParagraphAndMoveBlockUp(isNodeAWrappingBlockNode), deleteBlockContent(isNodeAWrappingBlockNode), deleteAndMoveCursor);
@@ -3,8 +3,8 @@ import { browser } from '@atlaskit/editor-common/browser';
3
3
  import { SafePlugin } from '@atlaskit/editor-common/safe-plugin';
4
4
  import { PluginKey } from '@atlaskit/editor-prosemirror/state';
5
5
  import { editorExperiment } from '@atlaskit/tmp-editor-statsig/experiments';
6
- import { BLOCK_QUOTE, CODE_BLOCK, HEADING_1, HEADING_2, HEADING_3, HEADING_4, HEADING_5, HEADING_6, HEADINGS_BY_LEVEL, NORMAL_TEXT, OTHER, PANEL, TEXT_BLOCK_TYPES, WRAPPER_BLOCK_TYPES } from './block-types';
7
- import { setHeadingWithAnalytics, setNormalTextWithAnalytics } from './commands';
6
+ import { BLOCK_QUOTE, CODE_BLOCK, HEADING_1, HEADING_2, HEADING_3, HEADING_4, HEADING_5, HEADING_6, HEADINGS_BY_LEVEL, NORMAL_TEXT, OTHER, PANEL, TEXT_BLOCK_TYPES, WRAPPER_BLOCK_TYPES, getBlockTypesInDropdown } from './block-types';
7
+ import { setHeadingWithAnalytics, setNormalTextWithAnalytics } from './commands/block-type';
8
8
  import { HEADING_KEYS } from './consts';
9
9
  import { areBlockTypesDisabled } from './utils';
10
10
  const blockTypeForNode = (node, schema) => {
@@ -15,6 +15,8 @@ const blockTypeForNode = (node, schema) => {
15
15
  }
16
16
  } else if (node.type === schema.nodes.paragraph) {
17
17
  return NORMAL_TEXT;
18
+ } else if (node.type === schema.nodes.blockquote) {
19
+ return BLOCK_QUOTE;
18
20
  }
19
21
  return OTHER;
20
22
  };
@@ -56,6 +58,7 @@ const detectBlockType = (availableBlockTypes, state) => {
56
58
  } else if (blockType !== OTHER && blockType !== nodeBlockType[0]) {
57
59
  blockType = OTHER;
58
60
  }
61
+ return false;
59
62
  }
60
63
  });
61
64
  return blockType || OTHER;
@@ -67,7 +70,7 @@ const autoformatHeading = (headingLevel, editorAnalyticsApi) => {
67
70
  return setHeadingWithAnalytics(headingLevel, INPUT_METHOD.FORMATTING, editorAnalyticsApi);
68
71
  };
69
72
  export const pluginKey = new PluginKey('blockTypePlugin');
70
- export const createPlugin = (editorAPI, dispatch, lastNodeMustBeParagraph) => {
73
+ export const createPlugin = (editorAPI, dispatch, lastNodeMustBeParagraph, includeBlockQuoteAsTextstyleOption) => {
71
74
  var _editorAPI$analytics;
72
75
  const editorAnalyticsApi = editorAPI === null || editorAPI === void 0 ? void 0 : (_editorAPI$analytics = editorAPI.analytics) === null || _editorAPI$analytics === void 0 ? void 0 : _editorAPI$analytics.actions;
73
76
  let altKeyLocation = 0;
@@ -88,17 +91,20 @@ export const createPlugin = (editorAPI, dispatch, lastNodeMustBeParagraph) => {
88
91
  init(_config, state) {
89
92
  const availableBlockTypes = TEXT_BLOCK_TYPES.filter(blockType => isBlockTypeSchemaSupported(blockType, state));
90
93
  const availableWrapperBlockTypes = WRAPPER_BLOCK_TYPES.filter(blockType => isBlockTypeSchemaSupported(blockType, state));
94
+ const BLOCK_TYPES_IN_DROPDOWN = getBlockTypesInDropdown(includeBlockQuoteAsTextstyleOption);
95
+ const availableBlockTypesInDropdown = BLOCK_TYPES_IN_DROPDOWN.filter(blockType => isBlockTypeSchemaSupported(blockType, state));
91
96
  return {
92
- currentBlockType: detectBlockType(availableBlockTypes, state),
97
+ currentBlockType: detectBlockType(availableBlockTypesInDropdown, state),
93
98
  blockTypesDisabled: areBlockTypesDisabled(state),
94
99
  availableBlockTypes,
95
- availableWrapperBlockTypes
100
+ availableWrapperBlockTypes,
101
+ availableBlockTypesInDropdown
96
102
  };
97
103
  },
98
104
  apply(_tr, oldPluginState, _oldState, newState) {
99
105
  const newPluginState = {
100
106
  ...oldPluginState,
101
- currentBlockType: detectBlockType(oldPluginState.availableBlockTypes, newState),
107
+ currentBlockType: detectBlockType(oldPluginState.availableBlockTypesInDropdown, newState),
102
108
  blockTypesDisabled: areBlockTypesDisabled(newState)
103
109
  };
104
110
  if (newPluginState.currentBlockType !== oldPluginState.currentBlockType || newPluginState.blockTypesDisabled !== oldPluginState.blockTypesDisabled) {
@@ -18,12 +18,17 @@ export function FloatingToolbarComponent({
18
18
  var _api$core, _api$blockType, _api$blockType$comman;
19
19
  return api === null || api === void 0 ? void 0 : (_api$core = api.core) === null || _api$core === void 0 ? void 0 : _api$core.actions.execute(api === null || api === void 0 ? void 0 : (_api$blockType = api.blockType) === null || _api$blockType === void 0 ? void 0 : (_api$blockType$comman = _api$blockType.commands) === null || _api$blockType$comman === void 0 ? void 0 : _api$blockType$comman.setTextLevel(name, INPUT_METHOD.FLOATING_TB));
20
20
  }, [api]);
21
+ const wrapBlockQuote = useCallback(() => {
22
+ var _api$core2, _api$blockType2, _api$blockType2$comma;
23
+ return api === null || api === void 0 ? void 0 : (_api$core2 = api.core) === null || _api$core2 === void 0 ? void 0 : _api$core2.actions.execute(api === null || api === void 0 ? void 0 : (_api$blockType2 = api.blockType) === null || _api$blockType2 === void 0 ? void 0 : (_api$blockType2$comma = _api$blockType2.commands) === null || _api$blockType2$comma === void 0 ? void 0 : _api$blockType2$comma.insertBlockQuote(INPUT_METHOD.TOOLBAR));
24
+ }, [api]);
21
25
  return /*#__PURE__*/React.createElement(ToolbarBlockType, {
22
26
  isSmall: FloatingToolbarSettings.isSmall,
23
27
  isDisabled: FloatingToolbarSettings.disabled,
24
28
  isReducedSpacing: FloatingToolbarSettings.isToolbarReducedSpacing,
25
29
  setTextLevel: boundSetBlockType,
26
30
  pluginState: blockTypeState,
31
+ wrapBlockQuote: wrapBlockQuote,
27
32
  shouldUseDefaultRole: FloatingToolbarSettings.shouldUseDefaultRole,
28
33
  api: api
29
34
  });
@@ -15,15 +15,20 @@ export function PrimaryToolbarComponent({
15
15
  const {
16
16
  blockTypeState
17
17
  } = useSharedPluginState(api, ['blockType']);
18
- const boundSetBlockType = name => {
18
+ const boundSetBlockType = (name, fromBlockQuote) => {
19
19
  var _api$core, _api$blockType, _api$blockType$comman;
20
- return api === null || api === void 0 ? void 0 : (_api$core = api.core) === null || _api$core === void 0 ? void 0 : _api$core.actions.execute(api === null || api === void 0 ? void 0 : (_api$blockType = api.blockType) === null || _api$blockType === void 0 ? void 0 : (_api$blockType$comman = _api$blockType.commands) === null || _api$blockType$comman === void 0 ? void 0 : _api$blockType$comman.setTextLevel(name, INPUT_METHOD.TOOLBAR));
20
+ return api === null || api === void 0 ? void 0 : (_api$core = api.core) === null || _api$core === void 0 ? void 0 : _api$core.actions.execute(api === null || api === void 0 ? void 0 : (_api$blockType = api.blockType) === null || _api$blockType === void 0 ? void 0 : (_api$blockType$comman = _api$blockType.commands) === null || _api$blockType$comman === void 0 ? void 0 : _api$blockType$comman.setTextLevel(name, INPUT_METHOD.TOOLBAR, fromBlockQuote));
21
+ };
22
+ const wrapBlockQuote = () => {
23
+ var _api$core2, _api$blockType2, _api$blockType2$comma;
24
+ return api === null || api === void 0 ? void 0 : (_api$core2 = api.core) === null || _api$core2 === void 0 ? void 0 : _api$core2.actions.execute(api === null || api === void 0 ? void 0 : (_api$blockType2 = api.blockType) === null || _api$blockType2 === void 0 ? void 0 : (_api$blockType2$comma = _api$blockType2.commands) === null || _api$blockType2$comma === void 0 ? void 0 : _api$blockType2$comma.insertBlockQuote(INPUT_METHOD.TOOLBAR));
21
25
  };
22
26
  return /*#__PURE__*/React.createElement(ToolbarBlockType, {
23
27
  isSmall: isSmall,
24
28
  isDisabled: disabled,
25
29
  isReducedSpacing: isToolbarReducedSpacing,
26
30
  setTextLevel: boundSetBlockType,
31
+ wrapBlockQuote: wrapBlockQuote,
27
32
  pluginState: blockTypeState,
28
33
  popupsMountPoint: popupsMountPoint,
29
34
  popupsBoundariesElement: popupsBoundariesElement,
@@ -56,9 +56,9 @@ class ToolbarBlockType extends React.PureComponent {
56
56
  } = this.props;
57
57
  const {
58
58
  currentBlockType,
59
- availableBlockTypes
59
+ availableBlockTypesInDropdown
60
60
  } = this.props.pluginState;
61
- const items = availableBlockTypes.map((blockType, index) => {
61
+ const items = availableBlockTypesInDropdown.map((blockType, index) => {
62
62
  const isActive = currentBlockType === blockType;
63
63
  const tagName = blockType.tagName || 'p';
64
64
  const Tag = tagName;
@@ -91,7 +91,12 @@ class ToolbarBlockType extends React.PureComponent {
91
91
  shouldCloseMenu = true
92
92
  }) => {
93
93
  const blockType = item.value;
94
- this.props.setTextLevel(blockType.name);
94
+ if (blockType.name === 'blockquote') {
95
+ this.props.wrapBlockQuote(blockType.name);
96
+ } else {
97
+ const fromBlockQuote = this.props.pluginState.currentBlockType.name === 'blockquote';
98
+ this.props.setTextLevel(blockType.name, fromBlockQuote);
99
+ }
95
100
  if (shouldCloseMenu) {
96
101
  this.setState({
97
102
  ...this.state,
@@ -133,7 +138,8 @@ class ToolbarBlockType extends React.PureComponent {
133
138
  pluginState: {
134
139
  currentBlockType,
135
140
  blockTypesDisabled,
136
- availableBlockTypes
141
+ availableBlockTypes,
142
+ availableBlockTypesInDropdown
137
143
  },
138
144
  shouldUseDefaultRole,
139
145
  intl: {
@@ -145,8 +151,8 @@ class ToolbarBlockType extends React.PureComponent {
145
151
  if (isHeadingDisabled) {
146
152
  return null;
147
153
  }
148
- const blockTypeTitles = availableBlockTypes.filter(blockType => blockType.name === currentBlockType.name).map(blockType => blockType.title);
149
- if (!this.props.isDisabled && !blockTypesDisabled) {
154
+ const blockTypeTitles = availableBlockTypesInDropdown.filter(blockType => blockType.name === currentBlockType.name).map(blockType => blockType.title);
155
+ if (!this.props.isDisabled && (!blockTypesDisabled || currentBlockType.name === 'blockquote')) {
150
156
  const items = this.createItems();
151
157
  return (
152
158
  // eslint-disable-next-line @atlaskit/design-system/consistent-css-prop-usage, @atlaskit/ui-styling-standard/no-imported-style-values -- Ignored via go/DSP-18766
@@ -4,7 +4,7 @@
4
4
  */
5
5
  // eslint-disable-next-line @atlaskit/ui-styling-standard/use-compiled -- Ignored via go/DSP-18766
6
6
  import { css } from '@emotion/react';
7
- import { headingsSharedStyles } from '@atlaskit/editor-common/styles';
7
+ import { headingsSharedStyles, blockquoteSharedStyles } from '@atlaskit/editor-common/styles';
8
8
  import { shortcutStyle } from '@atlaskit/editor-shared-styles/shortcut';
9
9
  import { N400 } from '@atlaskit/theme/colors';
10
10
  export const blockTypeMenuItemStyle = (tagName, selected, typographyTheme) => {
@@ -12,7 +12,7 @@ export const blockTypeMenuItemStyle = (tagName, selected, typographyTheme) => {
12
12
  const selectedStyle = selected ? `${tagName} { color: ${"var(--ds-text, white)"} !important; }` : '';
13
13
  return () => css(
14
14
  // eslint-disable-next-line @atlaskit/ui-styling-standard/no-imported-style-values, @atlaskit/ui-styling-standard/no-unsafe-values -- Ignored via go/DSP-18766
15
- headingsSharedStyles(typographyTheme), {
15
+ tagName === 'blockquote' ? blockquoteSharedStyles : headingsSharedStyles(typographyTheme), {
16
16
  // eslint-disable-next-line @atlaskit/ui-styling-standard/no-nested-selectors -- Ignored via go/DSP-18766
17
17
  '>': {
18
18
  // eslint-disable-next-line @atlaskit/ui-styling-standard/no-nested-selectors -- Ignored via go/DSP-18766
@@ -1,5 +1,6 @@
1
1
  import { createRule, createWrappingJoinRule } from '@atlaskit/editor-common/utils';
2
2
  import { fg } from '@atlaskit/platform-feature-flags';
3
+ import { editorExperiment } from '@atlaskit/tmp-editor-statsig/experiments';
3
4
  import { WRAPPER_BLOCK_TYPES } from './block-types';
4
5
  export const isNodeAWrappingBlockNode = node => {
5
6
  if (!node) {
@@ -75,7 +76,20 @@ function getSelectedWrapperNodes(state) {
75
76
  export function areBlockTypesDisabled(state) {
76
77
  const nodesTypes = getSelectedWrapperNodes(state);
77
78
  const {
78
- panel
79
+ panel,
80
+ blockquote
79
81
  } = state.schema.nodes;
82
+ if (editorExperiment('platform_editor_blockquote_in_text_formatting_menu', true)) {
83
+ let hasQuote = false;
84
+ const {
85
+ $from,
86
+ $to
87
+ } = state.selection;
88
+ state.doc.nodesBetween($from.pos, $to.pos, node => {
89
+ hasQuote = node.type === blockquote;
90
+ return !hasQuote;
91
+ });
92
+ return nodesTypes.filter(type => type !== panel).length > 0 || hasQuote;
93
+ }
80
94
  return nodesTypes.filter(type => type !== panel).length > 0;
81
95
  }
@@ -1 +1,4 @@
1
+ // Disable no-re-export rule for entry point files
2
+ /* eslint-disable @atlaskit/editor/no-re-export */
3
+
1
4
  export { BLOCK_QUOTE, CODE_BLOCK, HEADING_1, HEADING_2, HEADING_3, HEADING_4, HEADING_5, HEADING_6, NORMAL_TEXT, PANEL } from '../pm-plugins/block-types';
@@ -8,8 +8,7 @@ import { IconHeading, IconQuote } from '@atlaskit/editor-common/quick-insert';
8
8
  import { ToolbarSize } from '@atlaskit/editor-common/types';
9
9
  import { fg } from '@atlaskit/platform-feature-flags';
10
10
  import { editorExperiment } from '@atlaskit/tmp-editor-statsig/experiments';
11
- import { setBlockTypeWithAnalytics } from './pm-plugins/commands';
12
- import { insertBlockQuoteWithAnalytics } from './pm-plugins/commands/block-type';
11
+ import { setBlockTypeWithAnalytics, insertBlockQuoteWithAnalytics, insertBlockQuoteWithAnalyticsCommand } from './pm-plugins/commands/block-type';
13
12
  import inputRulePlugin from './pm-plugins/input-rule';
14
13
  import keymapPlugin from './pm-plugins/keymap';
15
14
  import { createPlugin, pluginKey } from './pm-plugins/main';
@@ -147,7 +146,7 @@ var blockTypePlugin = function blockTypePlugin(_ref3) {
147
146
  name: 'blockType',
148
147
  plugin: function plugin(_ref5) {
149
148
  var dispatch = _ref5.dispatch;
150
- return createPlugin(api, dispatch, options && options.lastNodeMustBeParagraph);
149
+ return createPlugin(api, dispatch, options && options.lastNodeMustBeParagraph, options === null || options === void 0 ? void 0 : options.includeBlockQuoteAsTextstyleOption);
151
150
  }
152
151
  }, {
153
152
  name: 'blockTypeInputRule',
@@ -179,7 +178,12 @@ var blockTypePlugin = function blockTypePlugin(_ref3) {
179
178
  commands: {
180
179
  setTextLevel: function setTextLevel(level, inputMethod) {
181
180
  var _api$analytics4;
182
- return setBlockTypeWithAnalytics(level, inputMethod, api === null || api === void 0 || (_api$analytics4 = api.analytics) === null || _api$analytics4 === void 0 ? void 0 : _api$analytics4.actions);
181
+ var fromBlockQuote = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
182
+ return setBlockTypeWithAnalytics(level, inputMethod, api === null || api === void 0 || (_api$analytics4 = api.analytics) === null || _api$analytics4 === void 0 ? void 0 : _api$analytics4.actions, fromBlockQuote);
183
+ },
184
+ insertBlockQuote: function insertBlockQuote(inputMethod) {
185
+ var _api$analytics5;
186
+ return insertBlockQuoteWithAnalyticsCommand(inputMethod, api === null || api === void 0 || (_api$analytics5 = api.analytics) === null || _api$analytics5 === void 0 ? void 0 : _api$analytics5.actions);
183
187
  }
184
188
  },
185
189
  getSharedState: function getSharedState(editorState) {
@@ -216,12 +220,11 @@ var blockTypePlugin = function blockTypePlugin(_ref3) {
216
220
  }
217
221
  },
218
222
  quickInsert: function quickInsert(intl) {
219
- var _api$analytics5, _api$analytics6;
223
+ var _api$analytics6, _api$analytics7;
220
224
  var exclude = options && options.allowBlockType && options.allowBlockType.exclude ? options.allowBlockType.exclude : [];
221
- return [].concat(_toConsumableArray(blockquotePluginOptions(intl, exclude.indexOf('blockquote') === -1, api === null || api === void 0 || (_api$analytics5 = api.analytics) === null || _api$analytics5 === void 0 ? void 0 : _api$analytics5.actions)), _toConsumableArray(headingPluginOptions(intl, exclude.indexOf('heading') === -1, api === null || api === void 0 || (_api$analytics6 = api.analytics) === null || _api$analytics6 === void 0 ? void 0 : _api$analytics6.actions)));
225
+ return [].concat(_toConsumableArray(blockquotePluginOptions(intl, exclude.indexOf('blockquote') === -1, api === null || api === void 0 || (_api$analytics6 = api.analytics) === null || _api$analytics6 === void 0 ? void 0 : _api$analytics6.actions)), _toConsumableArray(headingPluginOptions(intl, exclude.indexOf('heading') === -1, api === null || api === void 0 || (_api$analytics7 = api.analytics) === null || _api$analytics7 === void 0 ? void 0 : _api$analytics7.actions)));
222
226
  }
223
227
  }
224
228
  };
225
229
  };
226
- export { blockTypePlugin };
227
- export { pluginKey } from './pm-plugins/main';
230
+ export { blockTypePlugin };
package/dist/esm/index.js CHANGED
@@ -1 +1,4 @@
1
+ // Disable no-re-export rule for entry point files
2
+ /* eslint-disable @atlaskit/editor/no-re-export */
3
+
1
4
  export { blockTypePlugin } from './blockTypePlugin';
@@ -1,4 +1,5 @@
1
1
  import { blockTypeMessages as messages } from '@atlaskit/editor-common/messages';
2
+ import { editorExperiment } from '@atlaskit/tmp-editor-statsig/experiments';
2
3
  export var NORMAL_TEXT = {
3
4
  name: 'normal',
4
5
  title: messages.normal,
@@ -50,7 +51,8 @@ export var HEADING_6 = {
50
51
  export var BLOCK_QUOTE = {
51
52
  name: 'blockquote',
52
53
  title: messages.blockquote,
53
- nodeName: 'blockquote'
54
+ nodeName: 'blockquote',
55
+ tagName: 'blockquote'
54
56
  };
55
57
  export var CODE_BLOCK = {
56
58
  name: 'codeblock',
@@ -70,6 +72,11 @@ export var OTHER = {
70
72
  export var TEXT_BLOCK_TYPES = [NORMAL_TEXT, HEADING_1, HEADING_2, HEADING_3, HEADING_4, HEADING_5, HEADING_6];
71
73
  export var WRAPPER_BLOCK_TYPES = [BLOCK_QUOTE, CODE_BLOCK, PANEL];
72
74
  export var ALL_BLOCK_TYPES = TEXT_BLOCK_TYPES.concat(WRAPPER_BLOCK_TYPES);
75
+ export var getBlockTypesInDropdown = function getBlockTypesInDropdown(includeBlockQuoteAsTextstyleOption) {
76
+ return editorExperiment('platform_editor_blockquote_in_text_formatting_menu', true, {
77
+ exposure: true
78
+ }) && includeBlockQuoteAsTextstyleOption ? [].concat(TEXT_BLOCK_TYPES, [BLOCK_QUOTE]) : TEXT_BLOCK_TYPES;
79
+ };
73
80
  export var HEADINGS_BY_LEVEL = TEXT_BLOCK_TYPES.reduce(function (acc, blockType) {
74
81
  if (blockType.level && blockType.nodeName === 'heading') {
75
82
  acc[blockType.level] = blockType;