@atlaskit/editor-plugin-block-menu 3.2.0 → 3.2.2

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 (57) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/dist/cjs/blockMenuPlugin.js +5 -2
  3. package/dist/cjs/editor-commands/formatNode.js +48 -1
  4. package/dist/cjs/editor-commands/transforms/container-transforms.js +15 -4
  5. package/dist/cjs/editor-commands/transforms/layout-transforms.js +29 -18
  6. package/dist/cjs/editor-commands/transforms/utils.js +20 -1
  7. package/dist/cjs/ui/block-menu-components.js +5 -14
  8. package/dist/cjs/ui/block-menu-provider.js +40 -0
  9. package/dist/cjs/ui/block-menu.js +14 -4
  10. package/dist/cjs/ui/copy-block.js +11 -3
  11. package/dist/cjs/ui/copy-section.js +7 -0
  12. package/dist/cjs/ui/delete-section.js +23 -0
  13. package/dist/cjs/ui/format-menu-nested.js +28 -0
  14. package/dist/cjs/ui/utils/checkIsFormatMenuHidden.js +35 -1
  15. package/dist/es2019/blockMenuPlugin.js +5 -2
  16. package/dist/es2019/editor-commands/formatNode.js +53 -2
  17. package/dist/es2019/editor-commands/transforms/container-transforms.js +18 -5
  18. package/dist/es2019/editor-commands/transforms/layout-transforms.js +22 -11
  19. package/dist/es2019/editor-commands/transforms/utils.js +13 -0
  20. package/dist/es2019/ui/block-menu-components.js +6 -15
  21. package/dist/es2019/ui/block-menu-provider.js +31 -0
  22. package/dist/es2019/ui/block-menu.js +15 -4
  23. package/dist/es2019/ui/copy-block.js +9 -3
  24. package/dist/es2019/ui/copy-section.js +7 -0
  25. package/dist/es2019/ui/delete-section.js +17 -0
  26. package/dist/es2019/ui/format-menu-nested.js +23 -0
  27. package/dist/es2019/ui/utils/checkIsFormatMenuHidden.js +35 -1
  28. package/dist/esm/blockMenuPlugin.js +5 -2
  29. package/dist/esm/editor-commands/formatNode.js +49 -2
  30. package/dist/esm/editor-commands/transforms/container-transforms.js +16 -5
  31. package/dist/esm/editor-commands/transforms/layout-transforms.js +28 -17
  32. package/dist/esm/editor-commands/transforms/utils.js +18 -0
  33. package/dist/esm/ui/block-menu-components.js +6 -15
  34. package/dist/esm/ui/block-menu-provider.js +32 -0
  35. package/dist/esm/ui/block-menu.js +14 -4
  36. package/dist/esm/ui/copy-block.js +11 -3
  37. package/dist/esm/ui/copy-section.js +7 -0
  38. package/dist/esm/ui/delete-section.js +16 -0
  39. package/dist/esm/ui/format-menu-nested.js +21 -0
  40. package/dist/esm/ui/utils/checkIsFormatMenuHidden.js +35 -1
  41. package/dist/types/editor-commands/transforms/layout-transforms.d.ts +3 -0
  42. package/dist/types/editor-commands/transforms/utils.d.ts +2 -1
  43. package/dist/types/ui/block-menu-components.d.ts +1 -2
  44. package/dist/types/ui/block-menu-provider.d.ts +18 -0
  45. package/dist/types/ui/block-menu.d.ts +1 -1
  46. package/dist/types/ui/copy-section.d.ts +1 -1
  47. package/dist/types/ui/delete-section.d.ts +7 -0
  48. package/dist/types/ui/format-menu-nested.d.ts +4 -0
  49. package/dist/types-ts4.5/editor-commands/transforms/layout-transforms.d.ts +3 -0
  50. package/dist/types-ts4.5/editor-commands/transforms/utils.d.ts +2 -1
  51. package/dist/types-ts4.5/ui/block-menu-components.d.ts +1 -2
  52. package/dist/types-ts4.5/ui/block-menu-provider.d.ts +18 -0
  53. package/dist/types-ts4.5/ui/block-menu.d.ts +1 -1
  54. package/dist/types-ts4.5/ui/copy-section.d.ts +1 -1
  55. package/dist/types-ts4.5/ui/delete-section.d.ts +7 -0
  56. package/dist/types-ts4.5/ui/format-menu-nested.d.ts +4 -0
  57. package/package.json +4 -4
@@ -2,7 +2,7 @@ import _toConsumableArray from "@babel/runtime/helpers/toConsumableArray";
2
2
  import { Fragment, Slice } from '@atlaskit/editor-prosemirror/model';
3
3
  import { findChildrenByType } from '@atlaskit/editor-prosemirror/utils';
4
4
  import { getInlineNodeTextContent } from './inline-node-transforms';
5
- import { isBlockNodeType, isListNodeType, isContainerNodeType, isBlockNodeForExtraction, convertNodeToInlineContent, getContentSupportChecker, convertCodeBlockContentToParagraphs, filterMarksForTargetNodeType } from './utils';
5
+ import { isBlockNodeType, isListNodeType, isContainerNodeType, isBlockNodeForExtraction, convertNodeToInlineContent, getContentSupportChecker, convertCodeBlockContentToParagraphs, filterMarksForTargetNodeType, getMarksWithBreakout } from './utils';
6
6
  var convertInvalidNodeToValidNodeType = function convertInvalidNodeToValidNodeType(sourceContent, sourceNodeType, validNodeType, withMarks) {
7
7
  var validTransformedContent = [];
8
8
  // Headings are not valid inside headings so convert heading nodes to paragraphs
@@ -28,9 +28,11 @@ export var transformToContainer = function transformToContainer(_ref) {
28
28
  var schema = tr.doc.type.schema;
29
29
  var content = selection.content().content;
30
30
  var transformedContent = content;
31
+ var marks = [];
31
32
  if (sourceNode.type === schema.nodes.codeBlock) {
32
33
  var paragraphNodes = convertCodeBlockContentToParagraphs(sourceNode, schema);
33
34
  transformedContent = Fragment.fromArray(paragraphNodes);
35
+ marks = getMarksWithBreakout(sourceNode, targetNodeType);
34
36
  }
35
37
  if (targetNodeType === schema.nodes.blockquote) {
36
38
  transformedContent = convertInvalidNodeToValidNodeType(transformedContent, schema.nodes.heading, schema.nodes.paragraph, true);
@@ -42,7 +44,7 @@ export var transformToContainer = function transformToContainer(_ref) {
42
44
  if (sourceNode.type === schema.nodes.paragraph || sourceNode.type === schema.nodes.heading) {
43
45
  transformedContent = filterMarksForTargetNodeType(transformedContent, targetNodeType);
44
46
  }
45
- var newNode = targetNodeType.createAndFill(targetAttrs, transformedContent);
47
+ var newNode = targetNodeType.createAndFill(targetAttrs, transformedContent, marks);
46
48
  if (!newNode) {
47
49
  return null;
48
50
  }
@@ -168,7 +170,8 @@ export var unwrapAndConvertToBlockType = function unwrapAndConvertToBlockType(co
168
170
  var codeBlockContent = sourceChildren.map(function (node) {
169
171
  return node.content.textBetween(0, node.content.size, '\n');
170
172
  }).join('\n');
171
- transformedContent = [codeBlock.createChecked({}, schema.text(codeBlockContent))];
173
+ var marks = getMarksWithBreakout(sourceNode, targetNodeType);
174
+ transformedContent = [codeBlock.createChecked({}, schema.text(codeBlockContent), marks)];
172
175
  }
173
176
  var slice = new Slice(Fragment.fromArray(transformedContent), 0, 0);
174
177
  tr.replaceRange(rangeStart, rangeStart + sourceNode.nodeSize, slice);
@@ -265,6 +268,12 @@ export var transformBetweenContainerTypes = function transformBetweenContainerTy
265
268
  // Special handling for codeBlock target
266
269
  if (targetNodeType.name === 'codeBlock') {
267
270
  var _contentSplits = splitContentForCodeBlock(sourceNode, targetNodeType, targetAttrs, tr.doc.type.schema);
271
+ if (_contentSplits.length === 0) {
272
+ var schema = tr.doc.type.schema;
273
+ var marks = getMarksWithBreakout(sourceNode, targetNodeType);
274
+ var codeBlock = schema.nodes.codeBlock.create(targetAttrs, null, marks);
275
+ return tr.replaceWith(sourcePos, sourcePos + sourceNode.nodeSize, codeBlock);
276
+ }
268
277
  return applySplitsToTransaction(tr, sourcePos, sourceNode.nodeSize, _contentSplits);
269
278
  }
270
279
 
@@ -312,7 +321,8 @@ var splitContentForCodeBlock = function splitContentForCodeBlock(sourceNode, tar
312
321
  var flushCurrentCodeBlock = function flushCurrentCodeBlock() {
313
322
  if (currentTextContent.length > 0) {
314
323
  var codeText = currentTextContent.join('\n');
315
- var codeBlockNode = targetNodeType.create(targetAttrs, schema.text(codeText));
324
+ var marks = getMarksWithBreakout(sourceNode, targetNodeType);
325
+ var codeBlockNode = targetNodeType.create(targetAttrs, schema.text(codeText), marks);
316
326
  splits.push(codeBlockNode);
317
327
  currentTextContent = [];
318
328
  }
@@ -383,7 +393,8 @@ var splitContentAroundUnsupportedBlocks = function splitContentAroundUnsupported
383
393
  }
384
394
  var flushCurrentContainer = function flushCurrentContainer() {
385
395
  if (currentContainerContent.length > 0) {
386
- var containerNode = targetNodeType.create(targetAttrs, Fragment.fromArray(currentContainerContent));
396
+ var marks = getMarksWithBreakout(sourceNode, targetNodeType);
397
+ var containerNode = targetNodeType.create(targetAttrs, Fragment.fromArray(currentContainerContent), marks);
387
398
  splits.push(containerNode);
388
399
  currentContainerContent = [];
389
400
  }
@@ -2,26 +2,37 @@ import _toConsumableArray from "@babel/runtime/helpers/toConsumableArray";
2
2
  import { DEFAULT_TWO_COLUMN_LAYOUT_COLUMN_WIDTH } from '@atlaskit/editor-common/styles';
3
3
  import { Fragment } from '@atlaskit/editor-prosemirror/model';
4
4
  import { convertUnwrappedLayoutContent, unwrapLayoutNodesToTextNodes } from './layout/utils';
5
+ import { getMarksWithBreakout, isHeadingOrParagraphNode } from './utils';
6
+ export var createDefaultLayoutSection = function createDefaultLayoutSection(schema, content, marks) {
7
+ var _schema$nodes = schema.nodes,
8
+ layoutSection = _schema$nodes.layoutSection,
9
+ layoutColumn = _schema$nodes.layoutColumn,
10
+ paragraph = _schema$nodes.paragraph;
11
+ var layoutContent = Fragment.fromArray([layoutColumn.createChecked({
12
+ width: DEFAULT_TWO_COLUMN_LAYOUT_COLUMN_WIDTH
13
+ }, content), layoutColumn.create({
14
+ width: DEFAULT_TWO_COLUMN_LAYOUT_COLUMN_WIDTH
15
+ }, paragraph.createAndFill())]);
16
+ return layoutSection.createChecked(undefined, layoutContent, marks);
17
+ };
5
18
  export var convertToLayout = function convertToLayout(context) {
6
19
  var tr = context.tr,
7
20
  sourceNode = context.sourceNode,
8
21
  sourcePos = context.sourcePos;
9
- var _ref = tr.doc.type.schema.nodes || {},
10
- layoutSection = _ref.layoutSection,
11
- layoutColumn = _ref.layoutColumn,
12
- paragraph = _ref.paragraph;
13
22
  var content = sourceNode.mark(sourceNode.marks.filter(function (mark) {
14
23
  return mark.type.name !== 'breakout';
15
24
  }));
16
- var layoutContent = Fragment.fromArray([layoutColumn.createChecked({
17
- width: DEFAULT_TWO_COLUMN_LAYOUT_COLUMN_WIDTH
18
- }, content), layoutColumn.create({
19
- width: DEFAULT_TWO_COLUMN_LAYOUT_COLUMN_WIDTH
20
- }, paragraph.createAndFill())]);
21
- var layoutSectionNode = layoutSection.createChecked(undefined, layoutContent);
22
25
 
23
- // Replace the original node with the new layout node
24
- tr.replaceRangeWith(sourcePos, sourcePos + sourceNode.nodeSize, layoutSectionNode);
26
+ // Layout supports breakout mark that can have width attribute
27
+ // When other nodes with breakout (codeBlock and expand) are converted to a layout, the layout should get width of original node
28
+ var marks = getMarksWithBreakout(sourceNode, tr.doc.type.schema.nodes.layoutSection);
29
+ var layoutSectionNode = createDefaultLayoutSection(tr.doc.type.schema, content, marks);
30
+ if (isHeadingOrParagraphNode(sourceNode)) {
31
+ // -1 to fix when sourceNode is the last node in the document, unable to convert to layout
32
+ tr.replaceRangeWith(sourcePos > 0 ? sourcePos - 1 : sourcePos, sourcePos + sourceNode.nodeSize - 1, layoutSectionNode);
33
+ } else {
34
+ tr.replaceRangeWith(sourcePos, sourcePos + sourceNode.nodeSize, layoutSectionNode);
35
+ }
25
36
  return tr;
26
37
  };
27
38
  export var transformLayoutNode = function transformLayoutNode(context) {
@@ -31,11 +42,11 @@ export var transformLayoutNode = function transformLayoutNode(context) {
31
42
  sourcePos = context.sourcePos,
32
43
  targetAttrs = context.targetAttrs;
33
44
  var schema = tr.doc.type.schema || {};
34
- var _ref2 = schema.nodes || {},
35
- layoutSection = _ref2.layoutSection,
36
- layoutColumn = _ref2.layoutColumn,
37
- paragraph = _ref2.paragraph,
38
- heading = _ref2.heading;
45
+ var _ref = schema.nodes || {},
46
+ layoutSection = _ref.layoutSection,
47
+ layoutColumn = _ref.layoutColumn,
48
+ paragraph = _ref.paragraph,
49
+ heading = _ref.heading;
39
50
  var layoutColumnNodes = [];
40
51
  var targetTextNodeType = targetNodeType === heading ? heading : paragraph;
41
52
  sourceNode.children.forEach(function (child) {
@@ -1,3 +1,4 @@
1
+ import _toConsumableArray from "@babel/runtime/helpers/toConsumableArray";
1
2
  import { Fragment } from '@atlaskit/editor-prosemirror/model';
2
3
  export var getTargetNodeInfo = function getTargetNodeInfo(targetType, nodes) {
3
4
  switch (targetType) {
@@ -196,4 +197,21 @@ export var convertCodeBlockContentToParagraphs = function convertCodeBlockConten
196
197
  paragraphNodes.push(paragraphNode);
197
198
  });
198
199
  return paragraphNodes;
200
+ };
201
+ var isBreakoutMarkSupported = function isBreakoutMarkSupported(nodeType) {
202
+ return ['codeBlock', 'expand', 'layoutSection'].includes(nodeType.name);
203
+ };
204
+ export var getMarksWithBreakout = function getMarksWithBreakout(sourceNode, targetNodeType) {
205
+ var allowedMarks = targetNodeType.allowedMarks(sourceNode.marks);
206
+ var sourceBreakoutMark = sourceNode.marks.find(function (mark) {
207
+ return mark.type.name === 'breakout';
208
+ });
209
+ if (sourceBreakoutMark && isBreakoutMarkSupported(targetNodeType)) {
210
+ // Check if breakout mark is already in allowedMarks to avoid duplicates
211
+ var hasBreakoutMark = allowedMarks.some(function (mark) {
212
+ return mark.type.name === 'breakout';
213
+ });
214
+ return hasBreakoutMark ? allowedMarks : [].concat(_toConsumableArray(allowedMarks), [sourceBreakoutMark]);
215
+ }
216
+ return allowedMarks;
199
217
  };
@@ -1,13 +1,13 @@
1
1
  import _toConsumableArray from "@babel/runtime/helpers/toConsumableArray";
2
2
  import React from 'react';
3
3
  import { MOVE_UP_MENU_ITEM, MOVE_UP_DOWN_MENU_SECTION, MOVE_DOWN_MENU_ITEM, MOVE_BLOCK_SECTION_RANK, PRIMARY_MENU_SECTION, BLOCK_MENU_SECTION_RANK, COPY_MENU_SECTION, COPY_BLOCK_MENU_ITEM, COPY_MENU_SECTION_RANK, COPY_LINK_MENU_ITEM, DELETE_MENU_SECTION, DELETE_MENU_ITEM, DELETE_SECTION_RANK, NESTED_FORMAT_MENU_SECTION, NESTED_FORMAT_MENU } from '@atlaskit/editor-common/block-menu';
4
- import { ToolbarDropdownItemSection, ToolbarNestedDropdownMenu } from '@atlaskit/editor-toolbar';
5
- import ChangesIcon from '@atlaskit/icon/core/changes';
6
- import ChevronRightIcon from '@atlaskit/icon/core/chevron-right';
4
+ import { ToolbarDropdownItemSection } from '@atlaskit/editor-toolbar';
7
5
  import CopyBlockMenuItem from './copy-block';
8
6
  import { CopyLinkDropdownItem } from './copy-link';
9
7
  import { CopySection } from './copy-section';
10
8
  import { DeleteDropdownItem } from './delete-button';
9
+ import { DeleteSection } from './delete-section';
10
+ import { FormatMenuComponent } from './format-menu-nested';
11
11
  import { FormatMenuSection } from './format-menu-section';
12
12
  import { MoveDownDropdownItem } from './move-down';
13
13
  import { MoveUpDropdownItem } from './move-up';
@@ -54,16 +54,7 @@ var getFormatMenuComponents = function getFormatMenuComponents(api) {
54
54
  children: null
55
55
  },
56
56
  children = _ref.children;
57
- return /*#__PURE__*/React.createElement(ToolbarNestedDropdownMenu, {
58
- text: "Format",
59
- elemBefore: /*#__PURE__*/React.createElement(ChangesIcon, {
60
- label: ""
61
- }),
62
- elemAfter: /*#__PURE__*/React.createElement(ChevronRightIcon, {
63
- label: 'example nested menu'
64
- }),
65
- enableMaxHeight: true
66
- }, children);
57
+ return /*#__PURE__*/React.createElement(FormatMenuComponent, null, children);
67
58
  }
68
59
  }, {
69
60
  type: 'block-menu-section',
@@ -148,8 +139,8 @@ export var getBlockMenuComponents = function getBlockMenuComponents(_ref4) {
148
139
  rank: BLOCK_MENU_SECTION_RANK[DELETE_MENU_SECTION.key],
149
140
  component: function component(_ref7) {
150
141
  var children = _ref7.children;
151
- return /*#__PURE__*/React.createElement(ToolbarDropdownItemSection, {
152
- hasSeparator: true
142
+ return /*#__PURE__*/React.createElement(DeleteSection, {
143
+ api: api
153
144
  }, children);
154
145
  }
155
146
  }], _toConsumableArray(getMoveUpMoveDownMenuComponents(api)), [{
@@ -0,0 +1,32 @@
1
+ import React, { useCallback, createContext, useContext } from 'react';
2
+ var BlockMenuContext = /*#__PURE__*/createContext({
3
+ onDropdownOpenChanged: function onDropdownOpenChanged() {}
4
+ });
5
+ export var useBlockMenu = function useBlockMenu() {
6
+ var context = useContext(BlockMenuContext);
7
+ if (!context) {
8
+ throw new Error('useBlockMenu must be used within BlockMenuProvider');
9
+ }
10
+ return context;
11
+ };
12
+ export var BlockMenuProvider = function BlockMenuProvider(_ref) {
13
+ var children = _ref.children,
14
+ api = _ref.api;
15
+ var onDropdownOpenChanged = useCallback(function (isOpen) {
16
+ if (!isOpen) {
17
+ // On Dropdown closed, return focus to editor
18
+ setTimeout(function () {
19
+ return requestAnimationFrame(function () {
20
+ api === null || api === void 0 || api.core.actions.focus({
21
+ scrollIntoView: false
22
+ });
23
+ });
24
+ }, 1);
25
+ }
26
+ }, [api]);
27
+ return /*#__PURE__*/React.createElement(BlockMenuContext.Provider, {
28
+ value: {
29
+ onDropdownOpenChanged: onDropdownOpenChanged
30
+ }
31
+ }, children);
32
+ };
@@ -1,4 +1,4 @@
1
- /* block-menu.tsx generated by @compiled/babel-plugin v0.36.1 */
1
+ /* block-menu.tsx generated by @compiled/babel-plugin v0.38.1 */
2
2
  import "./block-menu.compiled.css";
3
3
  import { ax, ix } from "@compiled/react/runtime";
4
4
  import React, { useContext, useEffect } from 'react';
@@ -11,6 +11,8 @@ import { OutsideClickTargetRefContext, withReactEditorViewOuterListeners } from
11
11
  import { akEditorFloatingOverlapPanelZIndex } from '@atlaskit/editor-shared-styles';
12
12
  import { ToolbarDropdownItem, ToolbarDropdownItemSection, ToolbarNestedDropdownMenu } from '@atlaskit/editor-toolbar';
13
13
  import { Box } from '@atlaskit/primitives/compiled';
14
+ import { expValEqualsNoExposure } from '@atlaskit/tmp-editor-statsig/exp-val-equals-no-exposure';
15
+ import { useBlockMenu } from './block-menu-provider';
14
16
  import { BlockMenuRenderer } from './block-menu-renderer';
15
17
  var styles = {
16
18
  base: "_2rko12b0 _bfhk1bhr _16qs1cd0"
@@ -65,15 +67,22 @@ var BlockMenu = function BlockMenu(_ref2) {
65
67
  isSelectedViaDragHandle = _useSharedPluginState.isSelectedViaDragHandle,
66
68
  isMenuOpen = _useSharedPluginState.isMenuOpen,
67
69
  currentUserIntent = _useSharedPluginState.currentUserIntent;
70
+ var _useBlockMenu = useBlockMenu(),
71
+ onDropdownOpenChanged = _useBlockMenu.onDropdownOpenChanged;
68
72
  var hasFocus = (_editorView$hasFocus = editorView === null || editorView === void 0 ? void 0 : editorView.hasFocus()) !== null && _editorView$hasFocus !== void 0 ? _editorView$hasFocus : false;
69
73
  var hasSelection = !!editorView && !editorView.state.selection.empty;
74
+ var emptyLineEnabled = expValEqualsNoExposure('platform_editor_block_menu_empty_line', 'isEnabled', true);
75
+
76
+ // hasSelection true, always show block menu
77
+ // hasSelection false, only show block menu when empty line experiment is enabled
78
+ var shouldShowBlockMenuForEmptyLine = hasSelection || emptyLineEnabled && !hasSelection;
70
79
  useEffect(function () {
71
80
  var _api$userIntent;
72
- if (!isMenuOpen || !menuTriggerBy || !isSelectedViaDragHandle || !hasFocus || !hasSelection || ['resizing', 'dragging'].includes(currentUserIntent || '')) {
81
+ if (!isMenuOpen || !menuTriggerBy || !isSelectedViaDragHandle || !hasFocus || !shouldShowBlockMenuForEmptyLine || ['resizing', 'dragging'].includes(currentUserIntent || '')) {
73
82
  return;
74
83
  }
75
84
  api === null || api === void 0 || api.core.actions.execute(api === null || api === void 0 || (_api$userIntent = api.userIntent) === null || _api$userIntent === void 0 ? void 0 : _api$userIntent.commands.setCurrentUserIntent('blockMenuOpen'));
76
- }, [api, isMenuOpen, menuTriggerBy, isSelectedViaDragHandle, hasFocus, hasSelection, currentUserIntent]);
85
+ }, [api, isMenuOpen, menuTriggerBy, isSelectedViaDragHandle, hasFocus, shouldShowBlockMenuForEmptyLine, currentUserIntent]);
77
86
  if (!isMenuOpen) {
78
87
  return null;
79
88
  }
@@ -86,13 +95,14 @@ var BlockMenu = function BlockMenu(_ref2) {
86
95
  })({
87
96
  tr: tr
88
97
  });
98
+ onDropdownOpenChanged(false);
89
99
  api === null || api === void 0 || (_api$userIntent2 = api.userIntent) === null || _api$userIntent2 === void 0 || _api$userIntent2.commands.setCurrentUserIntent(currentUserIntent === 'blockMenuOpen' ? 'default' : currentUserIntent || 'default')({
90
100
  tr: tr
91
101
  });
92
102
  return tr;
93
103
  });
94
104
  };
95
- if (!menuTriggerBy || !isSelectedViaDragHandle || !hasFocus || !hasSelection || ['resizing', 'dragging'].includes(currentUserIntent || '')) {
105
+ if (!menuTriggerBy || !isSelectedViaDragHandle || !hasFocus || !shouldShowBlockMenuForEmptyLine || ['resizing', 'dragging'].includes(currentUserIntent || '')) {
96
106
  closeMenu();
97
107
  return null;
98
108
  }
@@ -1,3 +1,6 @@
1
+ import _defineProperty from "@babel/runtime/helpers/defineProperty";
2
+ function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
3
+ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
1
4
  import React from 'react';
2
5
  import { injectIntl, useIntl } from 'react-intl-next';
3
6
  import { messages } from '@atlaskit/editor-common/block-menu';
@@ -73,10 +76,15 @@ var CopyBlockMenuItem = function CopyBlockMenuItem(_ref) {
73
76
  // When nodeType.inlineContent is true, it will be treated as an inline node in the copyDomNode function,
74
77
  // but we want to treat it as a block node when copying, hence setting it to false here
75
78
  if (selection.node.type.name === 'codeBlock') {
76
- _nodeType.inlineContent = false;
79
+ var codeBlockNodeType = _objectSpread(_objectSpread({}, _nodeType), {}, {
80
+ inlineContent: false
81
+ });
82
+ var _domNode2 = toDOM(selection.node, schema);
83
+ copyDomNode(_domNode2, codeBlockNodeType, selection);
84
+ } else {
85
+ var _domNode3 = toDOM(selection.node, schema);
86
+ copyDomNode(_domNode3, _nodeType, selection);
77
87
  }
78
- var _domNode2 = toDOM(selection.node, schema);
79
- copyDomNode(_domNode2, _nodeType, selection);
80
88
  }
81
89
 
82
90
  // close the block menu after copying
@@ -1,12 +1,19 @@
1
1
  import React, { useCallback } from 'react';
2
2
  import { ToolbarDropdownItemSection } from '@atlaskit/editor-toolbar';
3
+ import { expValEqualsNoExposure } from '@atlaskit/tmp-editor-statsig/exp-val-equals-no-exposure';
3
4
  import { checkIsFormatMenuHidden } from './utils/checkIsFormatMenuHidden';
4
5
  export var CopySection = function CopySection(_ref) {
6
+ var _api$selection;
5
7
  var api = _ref.api,
6
8
  children = _ref.children;
7
9
  var isFormatMenuHidden = useCallback(function () {
8
10
  return checkIsFormatMenuHidden(api);
9
11
  }, [api]);
12
+ var selection = api === null || api === void 0 || (_api$selection = api.selection) === null || _api$selection === void 0 || (_api$selection = _api$selection.sharedState) === null || _api$selection === void 0 || (_api$selection = _api$selection.currentState()) === null || _api$selection === void 0 ? void 0 : _api$selection.selection;
13
+ var isEmptyLineSelected = !!(selection !== null && selection !== void 0 && selection.empty) && expValEqualsNoExposure('platform_editor_block_menu_empty_line', 'isEnabled', true);
14
+ if (isEmptyLineSelected) {
15
+ return null;
16
+ }
10
17
  return /*#__PURE__*/React.createElement(ToolbarDropdownItemSection, {
11
18
  hasSeparator: !isFormatMenuHidden()
12
19
  }, children);
@@ -0,0 +1,16 @@
1
+ import React from 'react';
2
+ import { ToolbarDropdownItemSection } from '@atlaskit/editor-toolbar';
3
+ import { expValEqualsNoExposure } from '@atlaskit/tmp-editor-statsig/exp-val-equals-no-exposure';
4
+ export var DeleteSection = function DeleteSection(_ref) {
5
+ var _api$selection;
6
+ var api = _ref.api,
7
+ children = _ref.children;
8
+ var selection = api === null || api === void 0 || (_api$selection = api.selection) === null || _api$selection === void 0 || (_api$selection = _api$selection.sharedState) === null || _api$selection === void 0 || (_api$selection = _api$selection.currentState()) === null || _api$selection === void 0 ? void 0 : _api$selection.selection;
9
+ var isEmptyLineSelected = !!(selection !== null && selection !== void 0 && selection.empty) && expValEqualsNoExposure('platform_editor_block_menu_empty_line', 'isEnabled', true);
10
+ if (isEmptyLineSelected) {
11
+ return null;
12
+ }
13
+ return /*#__PURE__*/React.createElement(ToolbarDropdownItemSection, {
14
+ hasSeparator: true
15
+ }, children);
16
+ };
@@ -0,0 +1,21 @@
1
+ import React from 'react';
2
+ import { useIntl } from 'react-intl-next';
3
+ import { messages } from '@atlaskit/editor-common/block-menu';
4
+ import { ToolbarNestedDropdownMenu } from '@atlaskit/editor-toolbar';
5
+ import ChangesIcon from '@atlaskit/icon/core/changes';
6
+ import ChevronRightIcon from '@atlaskit/icon/core/chevron-right';
7
+ export var FormatMenuComponent = function FormatMenuComponent(_ref) {
8
+ var children = _ref.children;
9
+ var _useIntl = useIntl(),
10
+ formatMessage = _useIntl.formatMessage;
11
+ return /*#__PURE__*/React.createElement(ToolbarNestedDropdownMenu, {
12
+ text: formatMessage(messages.turnInto),
13
+ elemBefore: /*#__PURE__*/React.createElement(ChangesIcon, {
14
+ label: ""
15
+ }),
16
+ elemAfter: /*#__PURE__*/React.createElement(ChevronRightIcon, {
17
+ label: ""
18
+ }),
19
+ enableMaxHeight: true
20
+ }, children);
21
+ };
@@ -1,5 +1,6 @@
1
1
  import { findParentNodeOfType, findSelectedNodeOfType } from '@atlaskit/editor-prosemirror/utils';
2
2
  import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
3
+ import { expValEqualsNoExposure } from '@atlaskit/tmp-editor-statsig/exp-val-equals-no-exposure';
3
4
  import { isNestedNode } from './isNestedNode';
4
5
  var getIsFormatMenuHidden = function getIsFormatMenuHidden(selection, schema, menuTriggerBy) {
5
6
  var nodes = schema.nodes;
@@ -26,6 +27,39 @@ var getIsFormatMenuHidden = function getIsFormatMenuHidden(selection, schema, me
26
27
  var isNested = isNestedNode(selection, menuTriggerBy);
27
28
  return !content || isNested;
28
29
  };
30
+ var getIsFormatMenuHiddenEmptyLine = function getIsFormatMenuHiddenEmptyLine(selection, schema, menuTriggerBy) {
31
+ var nodes = schema.nodes;
32
+ if (!nodes) {
33
+ return false;
34
+ }
35
+ var isNested = isNestedNode(selection, menuTriggerBy);
36
+ if (selection.empty || selection.content().size === 0) {
37
+ // if empty selection, show format menu
38
+ return false;
39
+ } else if (isNested) {
40
+ // if nested, always hide format menu
41
+ return true;
42
+ } else {
43
+ var content;
44
+ var allowedNodes = [nodes.paragraph, nodes.heading, nodes.blockquote, nodes.panel, nodes.codeBlock, nodes.bulletList, nodes.orderedList, nodes.taskList];
45
+ if (expValEquals('platform_editor_block_menu_layout_format', 'isEnabled', true)) {
46
+ allowedNodes.push(nodes.layoutSection);
47
+ }
48
+ if (expValEquals('platform_editor_block_menu_expand_format', 'isEnabled', true)) {
49
+ allowedNodes.push(nodes.expand);
50
+ }
51
+ var selectedNode = findSelectedNodeOfType(allowedNodes)(selection);
52
+ if (selectedNode) {
53
+ content = selectedNode.node;
54
+ } else {
55
+ var listTypeOrBlockQuoteNode = findParentNodeOfType([nodes.paragraph, nodes.heading, nodes.blockquote, nodes.listItem, nodes.taskItem])(selection);
56
+ if (listTypeOrBlockQuoteNode) {
57
+ content = listTypeOrBlockQuoteNode.node;
58
+ }
59
+ }
60
+ return !content;
61
+ }
62
+ };
29
63
  export var checkIsFormatMenuHidden = function checkIsFormatMenuHidden(api) {
30
64
  var _api$selection, _api$core$sharedState, _api$blockControls;
31
65
  var selection = api === null || api === void 0 || (_api$selection = api.selection) === null || _api$selection === void 0 || (_api$selection = _api$selection.sharedState) === null || _api$selection === void 0 || (_api$selection = _api$selection.currentState()) === null || _api$selection === void 0 ? void 0 : _api$selection.selection;
@@ -34,5 +68,5 @@ export var checkIsFormatMenuHidden = function checkIsFormatMenuHidden(api) {
34
68
  if (!selection || !schema || !menuTriggerBy) {
35
69
  return false;
36
70
  }
37
- return getIsFormatMenuHidden(selection, schema, menuTriggerBy);
71
+ return expValEqualsNoExposure('platform_editor_block_menu_empty_line', 'isEnabled', true) ? getIsFormatMenuHiddenEmptyLine(selection, schema, menuTriggerBy) : getIsFormatMenuHidden(selection, schema, menuTriggerBy);
38
72
  };
@@ -1,3 +1,6 @@
1
1
  import type { TransformContext } from '@atlaskit/editor-common/transforms';
2
+ import type { Mark, Node as PMNode } from '@atlaskit/editor-prosemirror/model';
3
+ import { type Schema } from '@atlaskit/editor-prosemirror/model';
4
+ export declare const createDefaultLayoutSection: (schema: Schema, content: PMNode, marks?: readonly Mark[]) => PMNode;
2
5
  export declare const convertToLayout: (context: TransformContext) => import("prosemirror-state").Transaction;
3
6
  export declare const transformLayoutNode: (context: TransformContext) => import("prosemirror-state").Transaction;
@@ -1,4 +1,4 @@
1
- import type { Node as PMNode, NodeType, Schema } from '@atlaskit/editor-prosemirror/model';
1
+ import type { Mark, Node as PMNode, NodeType, Schema } from '@atlaskit/editor-prosemirror/model';
2
2
  import { Fragment } from '@atlaskit/editor-prosemirror/model';
3
3
  import type { FormatNodeTargetType } from './types';
4
4
  export declare const getTargetNodeInfo: (targetType: FormatNodeTargetType, nodes: Record<string, NodeType>) => {
@@ -37,3 +37,4 @@ export declare const filterMarksForTargetNodeType: (content: Fragment, targetNod
37
37
  /** * Convert content from a code block node into multiple paragraph nodes
38
38
  */
39
39
  export declare const convertCodeBlockContentToParagraphs: (codeBlockNode: PMNode, schema: Schema) => PMNode[];
40
+ export declare const getMarksWithBreakout: (sourceNode: PMNode, targetNodeType: NodeType) => readonly Mark[];
@@ -1,6 +1,5 @@
1
1
  import type { ExtractInjectionAPI } from '@atlaskit/editor-common/types';
2
- import type { BlockMenuPlugin, BlockMenuPluginOptions } from '../blockMenuPluginType';
3
- import { type RegisterBlockMenuComponent } from '../blockMenuPluginType';
2
+ import type { BlockMenuPlugin, BlockMenuPluginOptions, RegisterBlockMenuComponent } from '../blockMenuPluginType';
4
3
  export declare const getBlockMenuComponents: ({ api, config, }: {
5
4
  api: ExtractInjectionAPI<BlockMenuPlugin> | undefined;
6
5
  config: BlockMenuPluginOptions | undefined;
@@ -0,0 +1,18 @@
1
+ import React from 'react';
2
+ import type { ExtractInjectionAPI } from '@atlaskit/editor-common/types';
3
+ import type { BlockMenuPlugin } from '../blockMenuPluginType';
4
+ type BlockMenuProviderProps = {
5
+ api: ExtractInjectionAPI<BlockMenuPlugin> | undefined;
6
+ children: React.ReactNode;
7
+ };
8
+ export type BlockMenuContextType = {
9
+ /**
10
+ * Callback for when the dropdown is open/closed. Receives an object with `isOpen` state.
11
+ *
12
+ * If the dropdown was closed programmatically, the `event` parameter will be `null`.
13
+ */
14
+ onDropdownOpenChanged: (isOpen: boolean) => void;
15
+ };
16
+ export declare const useBlockMenu: () => BlockMenuContextType;
17
+ export declare const BlockMenuProvider: ({ children, api }: BlockMenuProviderProps) => React.JSX.Element;
18
+ export {};
@@ -3,7 +3,7 @@ import type { WrappedComponentProps } from 'react-intl-next';
3
3
  import type { ExtractInjectionAPI } from '@atlaskit/editor-common/types';
4
4
  import { type EditorView } from '@atlaskit/editor-prosemirror/view';
5
5
  import type { BlockMenuPlugin } from '../blockMenuPluginType';
6
- type BlockMenuProps = {
6
+ export type BlockMenuProps = {
7
7
  api: ExtractInjectionAPI<BlockMenuPlugin> | undefined;
8
8
  boundariesElement?: HTMLElement;
9
9
  editorView: EditorView | undefined;
@@ -4,4 +4,4 @@ import type { BlockMenuPlugin } from '../blockMenuPluginType';
4
4
  export declare const CopySection: ({ api, children, }: {
5
5
  api: ExtractInjectionAPI<BlockMenuPlugin> | undefined;
6
6
  children: React.ReactNode;
7
- }) => React.JSX.Element;
7
+ }) => React.JSX.Element | null;
@@ -0,0 +1,7 @@
1
+ import React from 'react';
2
+ import type { ExtractInjectionAPI } from '@atlaskit/editor-common/types';
3
+ import type { BlockMenuPlugin } from '../blockMenuPluginType';
4
+ export declare const DeleteSection: ({ api, children, }: {
5
+ api: ExtractInjectionAPI<BlockMenuPlugin> | undefined;
6
+ children: React.ReactNode;
7
+ }) => React.JSX.Element | null;
@@ -0,0 +1,4 @@
1
+ import React from 'react';
2
+ export declare const FormatMenuComponent: ({ children }: {
3
+ children: React.ReactNode;
4
+ }) => React.JSX.Element;
@@ -1,3 +1,6 @@
1
1
  import type { TransformContext } from '@atlaskit/editor-common/transforms';
2
+ import type { Mark, Node as PMNode } from '@atlaskit/editor-prosemirror/model';
3
+ import { type Schema } from '@atlaskit/editor-prosemirror/model';
4
+ export declare const createDefaultLayoutSection: (schema: Schema, content: PMNode, marks?: readonly Mark[]) => PMNode;
2
5
  export declare const convertToLayout: (context: TransformContext) => import("prosemirror-state").Transaction;
3
6
  export declare const transformLayoutNode: (context: TransformContext) => import("prosemirror-state").Transaction;
@@ -1,4 +1,4 @@
1
- import type { Node as PMNode, NodeType, Schema } from '@atlaskit/editor-prosemirror/model';
1
+ import type { Mark, Node as PMNode, NodeType, Schema } from '@atlaskit/editor-prosemirror/model';
2
2
  import { Fragment } from '@atlaskit/editor-prosemirror/model';
3
3
  import type { FormatNodeTargetType } from './types';
4
4
  export declare const getTargetNodeInfo: (targetType: FormatNodeTargetType, nodes: Record<string, NodeType>) => {
@@ -37,3 +37,4 @@ export declare const filterMarksForTargetNodeType: (content: Fragment, targetNod
37
37
  /** * Convert content from a code block node into multiple paragraph nodes
38
38
  */
39
39
  export declare const convertCodeBlockContentToParagraphs: (codeBlockNode: PMNode, schema: Schema) => PMNode[];
40
+ export declare const getMarksWithBreakout: (sourceNode: PMNode, targetNodeType: NodeType) => readonly Mark[];
@@ -1,6 +1,5 @@
1
1
  import type { ExtractInjectionAPI } from '@atlaskit/editor-common/types';
2
- import type { BlockMenuPlugin, BlockMenuPluginOptions } from '../blockMenuPluginType';
3
- import { type RegisterBlockMenuComponent } from '../blockMenuPluginType';
2
+ import type { BlockMenuPlugin, BlockMenuPluginOptions, RegisterBlockMenuComponent } from '../blockMenuPluginType';
4
3
  export declare const getBlockMenuComponents: ({ api, config, }: {
5
4
  api: ExtractInjectionAPI<BlockMenuPlugin> | undefined;
6
5
  config: BlockMenuPluginOptions | undefined;
@@ -0,0 +1,18 @@
1
+ import React from 'react';
2
+ import type { ExtractInjectionAPI } from '@atlaskit/editor-common/types';
3
+ import type { BlockMenuPlugin } from '../blockMenuPluginType';
4
+ type BlockMenuProviderProps = {
5
+ api: ExtractInjectionAPI<BlockMenuPlugin> | undefined;
6
+ children: React.ReactNode;
7
+ };
8
+ export type BlockMenuContextType = {
9
+ /**
10
+ * Callback for when the dropdown is open/closed. Receives an object with `isOpen` state.
11
+ *
12
+ * If the dropdown was closed programmatically, the `event` parameter will be `null`.
13
+ */
14
+ onDropdownOpenChanged: (isOpen: boolean) => void;
15
+ };
16
+ export declare const useBlockMenu: () => BlockMenuContextType;
17
+ export declare const BlockMenuProvider: ({ children, api }: BlockMenuProviderProps) => React.JSX.Element;
18
+ export {};
@@ -3,7 +3,7 @@ import type { WrappedComponentProps } from 'react-intl-next';
3
3
  import type { ExtractInjectionAPI } from '@atlaskit/editor-common/types';
4
4
  import { type EditorView } from '@atlaskit/editor-prosemirror/view';
5
5
  import type { BlockMenuPlugin } from '../blockMenuPluginType';
6
- type BlockMenuProps = {
6
+ export type BlockMenuProps = {
7
7
  api: ExtractInjectionAPI<BlockMenuPlugin> | undefined;
8
8
  boundariesElement?: HTMLElement;
9
9
  editorView: EditorView | undefined;
@@ -4,4 +4,4 @@ import type { BlockMenuPlugin } from '../blockMenuPluginType';
4
4
  export declare const CopySection: ({ api, children, }: {
5
5
  api: ExtractInjectionAPI<BlockMenuPlugin> | undefined;
6
6
  children: React.ReactNode;
7
- }) => React.JSX.Element;
7
+ }) => React.JSX.Element | null;
@@ -0,0 +1,7 @@
1
+ import React from 'react';
2
+ import type { ExtractInjectionAPI } from '@atlaskit/editor-common/types';
3
+ import type { BlockMenuPlugin } from '../blockMenuPluginType';
4
+ export declare const DeleteSection: ({ api, children, }: {
5
+ api: ExtractInjectionAPI<BlockMenuPlugin> | undefined;
6
+ children: React.ReactNode;
7
+ }) => React.JSX.Element | null;
@@ -0,0 +1,4 @@
1
+ import React from 'react';
2
+ export declare const FormatMenuComponent: ({ children }: {
3
+ children: React.ReactNode;
4
+ }) => React.JSX.Element;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/editor-plugin-block-menu",
3
- "version": "3.2.0",
3
+ "version": "3.2.2",
4
4
  "description": "BlockMenu plugin for @atlaskit/editor-core",
5
5
  "author": "Atlassian Pty Ltd",
6
6
  "license": "Apache-2.0",
@@ -41,13 +41,13 @@
41
41
  "@atlaskit/icon": "^28.2.0",
42
42
  "@atlaskit/icon-lab": "^5.7.0",
43
43
  "@atlaskit/platform-feature-flags": "^1.1.0",
44
- "@atlaskit/primitives": "^14.14.0",
45
- "@atlaskit/tmp-editor-statsig": "^12.25.0",
44
+ "@atlaskit/primitives": "^14.15.0",
45
+ "@atlaskit/tmp-editor-statsig": "^12.26.0",
46
46
  "@atlaskit/tokens": "^6.3.0",
47
47
  "@babel/runtime": "^7.0.0"
48
48
  },
49
49
  "peerDependencies": {
50
- "@atlaskit/editor-common": "^109.10.0",
50
+ "@atlaskit/editor-common": "^109.11.0",
51
51
  "react": "^18.2.0",
52
52
  "react-intl-next": "npm:react-intl@^5.18.1"
53
53
  },