@atlaskit/editor-plugin-block-menu 5.2.6 → 5.2.8

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 (38) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/afm-cc/tsconfig.json +3 -0
  3. package/afm-jira/tsconfig.json +3 -0
  4. package/afm-products/tsconfig.json +3 -0
  5. package/dist/cjs/editor-commands/transform-node-utils/steps/wrapBlockquoteToDecisionListStep.js +91 -0
  6. package/dist/cjs/editor-commands/transform-node-utils/steps/wrapMixedContentStep.js +1 -5
  7. package/dist/cjs/editor-commands/transform-node-utils/transform.js +11 -3
  8. package/dist/cjs/editor-commands/transform-node-utils/utils.js +17 -1
  9. package/dist/cjs/editor-commands/transform-node-utils/wrapIntoListStep.js +17 -0
  10. package/dist/cjs/editor-commands/transformNode.js +13 -4
  11. package/dist/cjs/ui/block-menu-provider.js +0 -11
  12. package/dist/cjs/ui/move-down.js +4 -8
  13. package/dist/cjs/ui/move-up.js +5 -9
  14. package/dist/es2019/editor-commands/transform-node-utils/steps/wrapBlockquoteToDecisionListStep.js +88 -0
  15. package/dist/es2019/editor-commands/transform-node-utils/steps/wrapMixedContentStep.js +1 -5
  16. package/dist/es2019/editor-commands/transform-node-utils/transform.js +11 -3
  17. package/dist/es2019/editor-commands/transform-node-utils/utils.js +17 -1
  18. package/dist/es2019/editor-commands/transform-node-utils/wrapIntoListStep.js +13 -0
  19. package/dist/es2019/editor-commands/transformNode.js +15 -4
  20. package/dist/es2019/ui/block-menu-provider.js +0 -11
  21. package/dist/es2019/ui/move-down.js +4 -8
  22. package/dist/es2019/ui/move-up.js +5 -9
  23. package/dist/esm/editor-commands/transform-node-utils/steps/wrapBlockquoteToDecisionListStep.js +86 -0
  24. package/dist/esm/editor-commands/transform-node-utils/steps/wrapMixedContentStep.js +1 -5
  25. package/dist/esm/editor-commands/transform-node-utils/transform.js +11 -3
  26. package/dist/esm/editor-commands/transform-node-utils/utils.js +17 -1
  27. package/dist/esm/editor-commands/transform-node-utils/wrapIntoListStep.js +11 -0
  28. package/dist/esm/editor-commands/transformNode.js +13 -4
  29. package/dist/esm/ui/block-menu-provider.js +0 -11
  30. package/dist/esm/ui/move-down.js +4 -8
  31. package/dist/esm/ui/move-up.js +5 -9
  32. package/dist/types/editor-commands/transform-node-utils/steps/wrapBlockquoteToDecisionListStep.d.ts +15 -0
  33. package/dist/types/editor-commands/transform-node-utils/wrapIntoListStep.d.ts +3 -0
  34. package/dist/types/ui/block-menu-provider.d.ts +0 -5
  35. package/dist/types-ts4.5/editor-commands/transform-node-utils/steps/wrapBlockquoteToDecisionListStep.d.ts +15 -0
  36. package/dist/types-ts4.5/editor-commands/transform-node-utils/wrapIntoListStep.d.ts +3 -0
  37. package/dist/types-ts4.5/ui/block-menu-provider.d.ts +0 -5
  38. package/package.json +6 -6
package/CHANGELOG.md CHANGED
@@ -1,5 +1,20 @@
1
1
  # @atlaskit/editor-plugin-block-menu
2
2
 
3
+ ## 5.2.8
4
+
5
+ ### Patch Changes
6
+
7
+ - [`0006edf16b3a3`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/0006edf16b3a3) -
8
+ Editor-2778: Media wrap in container
9
+
10
+ ## 5.2.7
11
+
12
+ ### Patch Changes
13
+
14
+ - [`b2e5262017fa8`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/b2e5262017fa8) -
15
+ Editor-2676: Remove moveFocusTo in block menu provider
16
+ - Updated dependencies
17
+
3
18
  ## 5.2.6
4
19
 
5
20
  ### Patch Changes
@@ -39,6 +39,9 @@
39
39
  {
40
40
  "path": "../../editor-plugin-user-intent/afm-cc/tsconfig.json"
41
41
  },
42
+ {
43
+ "path": "../../editor-prosemirror/afm-cc/tsconfig.json"
44
+ },
42
45
  {
43
46
  "path": "../../editor-shared-styles/afm-cc/tsconfig.json"
44
47
  },
@@ -39,6 +39,9 @@
39
39
  {
40
40
  "path": "../../editor-plugin-user-intent/afm-jira/tsconfig.json"
41
41
  },
42
+ {
43
+ "path": "../../editor-prosemirror/afm-jira/tsconfig.json"
44
+ },
42
45
  {
43
46
  "path": "../../editor-shared-styles/afm-jira/tsconfig.json"
44
47
  },
@@ -39,6 +39,9 @@
39
39
  {
40
40
  "path": "../../editor-plugin-user-intent/afm-products/tsconfig.json"
41
41
  },
42
+ {
43
+ "path": "../../editor-prosemirror/afm-products/tsconfig.json"
44
+ },
42
45
  {
43
46
  "path": "../../editor-shared-styles/afm-products/tsconfig.json"
44
47
  },
@@ -0,0 +1,91 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.wrapBlockquoteToDecisionListStep = void 0;
7
+ var _types = require("../types");
8
+ /**
9
+ * Determines if a node is a text node (heading or paragraph).
10
+ * Only text nodes should have their inline content extracted for decisionItem.
11
+ * All other nodes should break out.
12
+ */
13
+ var isTextNode = function isTextNode(node) {
14
+ var category = _types.NODE_CATEGORY_BY_TYPE[node.type.name];
15
+ return category === 'text';
16
+ };
17
+
18
+ /**
19
+ * Creates a decisionItem with the given inline content.
20
+ */
21
+ var createDecisionItem = function createDecisionItem(inlineContent, schema) {
22
+ var decisionItemType = schema.nodes.decisionItem;
23
+ if (!decisionItemType) {
24
+ return null;
25
+ }
26
+ var canContentBeWrappedInDecisionItem = decisionItemType.validContent(inlineContent);
27
+
28
+ // Check if the content is valid for decisionItem
29
+ if (!canContentBeWrappedInDecisionItem) {
30
+ return null;
31
+ }
32
+ return decisionItemType.createAndFill({}, inlineContent);
33
+ };
34
+
35
+ /**
36
+ * Creates a decisionList containing the given decisionItems.
37
+ */
38
+ var createDecisionListWithItems = function createDecisionListWithItems(decisionItems, schema) {
39
+ var decisionListType = schema.nodes.decisionList;
40
+ if (!decisionListType || decisionItems.length === 0) {
41
+ return null;
42
+ }
43
+ return decisionListType.createAndFill({}, decisionItems);
44
+ };
45
+
46
+ /**
47
+ * Wraps blockquote content into decisionList:
48
+ * - Text nodes (paragraph, heading) have their inline content extracted and wrapped in decisionItems
49
+ * - Consecutive text nodes are grouped into a single decisionList with multiple decisionItems
50
+ * - All other nodes break out (lists, code blocks, media, tables, macros, containers, etc.)
51
+ *
52
+ * The logic follows the transform rules:
53
+ * - Only flatten text nodes into decisionItem (since that's the intent - converting text to decisions)
54
+ * - Structures that can't be represented in a decisionItem should break out unchanged
55
+ * - When a break-out node is encountered, flush accumulated decisionItems into a decisionList
56
+ *
57
+ * Example: blockquote(p('a'), p('b'), ul(...), p('c')) → [decisionList(decisionItem('a'), decisionItem('b')), ul(...), decisionList(decisionItem('c'))]
58
+ */
59
+ var wrapBlockquoteToDecisionListStep = exports.wrapBlockquoteToDecisionListStep = function wrapBlockquoteToDecisionListStep(nodes, context) {
60
+ var schema = context.schema;
61
+ var decisionItemType = schema.nodes.decisionItem;
62
+ if (!decisionItemType) {
63
+ return nodes;
64
+ }
65
+ var result = [];
66
+ var currentDecisionItems = [];
67
+ var flushCurrentDecisionList = function flushCurrentDecisionList() {
68
+ if (currentDecisionItems.length > 0) {
69
+ var decisionList = createDecisionListWithItems(currentDecisionItems, schema);
70
+ if (decisionList) {
71
+ result.push(decisionList);
72
+ }
73
+ currentDecisionItems = [];
74
+ }
75
+ };
76
+ nodes.forEach(function (node) {
77
+ var decisionItem = isTextNode(node) ? createDecisionItem(node.content, schema) : null;
78
+ if (decisionItem) {
79
+ // Accumulate consecutive decisionItems
80
+ currentDecisionItems.push(decisionItem);
81
+ } else {
82
+ // Content can't be wrapped in decisionItem - break out the node
83
+ flushCurrentDecisionList();
84
+ result.push(node);
85
+ }
86
+ });
87
+
88
+ // Flush any remaining decisionItems
89
+ flushCurrentDecisionList();
90
+ return result.length > 0 ? result : nodes;
91
+ };
@@ -45,11 +45,7 @@ var canWrapInTarget = function canWrapInTarget(node, targetNodeType, targetNodeT
45
45
  }
46
46
 
47
47
  // Use the schema to determine if this node can be contained in the target
48
- try {
49
- return targetNodeType.validContent(_model.Fragment.from(node));
50
- } catch (_unused) {
51
- return false;
52
- }
48
+ return targetNodeType.validContent(_model.Fragment.from(node));
53
49
  };
54
50
 
55
51
  /**
@@ -12,12 +12,14 @@ var _listToDecisionListStep = require("./steps/listToDecisionListStep");
12
12
  var _listToListStep = require("./steps/listToListStep");
13
13
  var _unwrapLayoutStep = require("./steps/unwrapLayoutStep");
14
14
  var _unwrapListStep = require("./steps/unwrapListStep");
15
+ var _wrapBlockquoteToDecisionListStep = require("./steps/wrapBlockquoteToDecisionListStep");
15
16
  var _wrapMixedContentStep = require("./steps/wrapMixedContentStep");
16
17
  var _stubStep = require("./stubStep");
17
18
  var _types = require("./types");
18
19
  var _unwrapExpandStep = require("./unwrapExpandStep");
19
20
  var _unwrapStep = require("./unwrapStep");
20
21
  var _wrapIntoLayoutStep = require("./wrapIntoLayoutStep");
22
+ var _wrapIntoListStep = require("./wrapIntoListStep");
21
23
  var _wrapStep = require("./wrapStep");
22
24
  // Exampled step for overrides:
23
25
  // - open Block menu on a paragraph, click 'Panel' in the Turn into'
@@ -32,7 +34,7 @@ var TRANSFORM_STEPS = {
32
34
  atomic: {
33
35
  atomic: undefined,
34
36
  container: [_wrapStep.wrapStep],
35
- list: undefined,
37
+ list: [_wrapIntoListStep.wrapIntoListStep],
36
38
  text: undefined
37
39
  },
38
40
  container: {
@@ -83,7 +85,8 @@ var TRANSFORM_STEPS_OVERRIDE = {
83
85
  expand: [_wrapStep.wrapStep],
84
86
  nestedExpand: [_wrapStep.wrapStep],
85
87
  layoutSection: [_wrapIntoLayoutStep.wrapIntoLayoutStep],
86
- codeBlock: [_unwrapStep.unwrapStep, _flattenStep.flattenStep, _wrapStep.wrapStep]
88
+ codeBlock: [_unwrapStep.unwrapStep, _flattenStep.flattenStep, _wrapStep.wrapStep],
89
+ decisionList: [_unwrapStep.unwrapStep, _wrapBlockquoteToDecisionListStep.wrapBlockquoteToDecisionListStep]
87
90
  },
88
91
  layoutSection: {
89
92
  blockquote: [_unwrapLayoutStep.unwrapLayoutStep, _wrapStep.wrapStep],
@@ -119,7 +122,12 @@ var TRANSFORM_STEPS_OVERRIDE = {
119
122
  decisionList: [_flattenListStep.flattenListStep, _listToDecisionListStep.listToDecisionListStep]
120
123
  },
121
124
  table: {
122
- expand: [_wrapStep.wrapStep],
125
+ layoutSection: [_wrapIntoLayoutStep.wrapIntoLayoutStep]
126
+ },
127
+ mediaSingle: {
128
+ layoutSection: [_wrapIntoLayoutStep.wrapIntoLayoutStep]
129
+ },
130
+ mediaGroup: {
123
131
  layoutSection: [_wrapIntoLayoutStep.wrapIntoLayoutStep]
124
132
  },
125
133
  decisionList: {
@@ -76,13 +76,29 @@ var expandSelectionToBlockRange = exports.expandSelectionToBlockRange = function
76
76
  var table = (0, _utils2.findTable)(selection);
77
77
  if (table) {
78
78
  var $from = selection.$from.doc.resolve(table.pos);
79
- var $to = selection.$from.doc.resolve(table.pos + table.node.nodeSize - 1);
79
+ var $to = selection.$from.doc.resolve(table.pos + table.node.nodeSize);
80
80
  return {
81
81
  $from: $from,
82
82
  $to: $to
83
83
  };
84
84
  }
85
85
  }
86
+
87
+ // when selecting a file, selection is on media
88
+ // need to find media group and return its pos
89
+ if (selection instanceof _state.NodeSelection) {
90
+ if (selection.node.type === nodes.media) {
91
+ var mediaGroup = (0, _utils.findParentNodeOfType)(nodes.mediaGroup)(selection);
92
+ if (mediaGroup) {
93
+ var _$from = selection.$from.doc.resolve(mediaGroup.pos);
94
+ var _$to = selection.$from.doc.resolve(mediaGroup.pos + mediaGroup.node.nodeSize);
95
+ return {
96
+ $from: _$from,
97
+ $to: _$to
98
+ };
99
+ }
100
+ }
101
+ }
86
102
  return (0, _selection.expandToBlockRange)(selection.$from, selection.$to, function (node) {
87
103
  if (nodesNeedToExpandRange.includes(node.type)) {
88
104
  return false;
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.wrapIntoListStep = void 0;
7
+ /** wrap nodes into bullet list or numbered list, does not work for task list */
8
+ var wrapIntoListStep = exports.wrapIntoListStep = function wrapIntoListStep(nodes, context) {
9
+ var schema = context.schema,
10
+ targetNodeTypeName = context.targetNodeTypeName;
11
+ var listItemNode = schema.nodes.listItem.createAndFill({}, nodes);
12
+ var outputNode = schema.nodes[targetNodeTypeName].createAndFill({}, listItemNode);
13
+ if (outputNode) {
14
+ return [outputNode];
15
+ }
16
+ return nodes;
17
+ };
@@ -5,6 +5,7 @@ Object.defineProperty(exports, "__esModule", {
5
5
  });
6
6
  exports.transformNode = void 0;
7
7
  var _model = require("@atlaskit/editor-prosemirror/model");
8
+ var _state = require("@atlaskit/editor-prosemirror/state");
8
9
  var _isNestedNode = require("../ui/utils/isNestedNode");
9
10
  var _transform = require("./transform-node-utils/transform");
10
11
  var _utils = require("./transform-node-utils/utils");
@@ -20,7 +21,9 @@ var transformNode = exports.transformNode = function transformNode(api) {
20
21
  if (!preservedSelection) {
21
22
  return tr;
22
23
  }
23
- var _expandSelectionToBlo = (0, _utils.expandSelectionToBlockRange)(preservedSelection, tr.doc.type.schema),
24
+ var schema = tr.doc.type.schema;
25
+ var nodes = schema.nodes;
26
+ var _expandSelectionToBlo = (0, _utils.expandSelectionToBlockRange)(preservedSelection, schema),
24
27
  $from = _expandSelectionToBlo.$from,
25
28
  $to = _expandSelectionToBlo.$to;
26
29
  var isNested = (0, _isNestedNode.isNestedNode)(preservedSelection, '');
@@ -39,9 +42,15 @@ var transformNode = exports.transformNode = function transformNode(api) {
39
42
  fragment = fragment.append(_model.Fragment.fromArray(outputNode));
40
43
  }
41
44
  });
42
-
43
- // TODO: ED-12345 - selection is broken post transaction, to fix.
44
- tr.replaceWith(isList ? $from.pos - 1 : $from.pos, $to.pos, fragment);
45
+ var nodesToDeleteAndInsert = [nodes.mediaSingle];
46
+ if (preservedSelection instanceof _state.NodeSelection && nodesToDeleteAndInsert.includes(preservedSelection.node.type)) {
47
+ // when node is media single, use tr.replaceWith freeze editor, if modify position, tr.replaceWith creates duplicats
48
+ tr.deleteRange($from.pos, $to.pos);
49
+ tr.insert($from.pos, fragment);
50
+ } else {
51
+ // TODO: ED-12345 - selection is broken post transaction, to fix.
52
+ tr.replaceWith(isList ? $from.pos - 1 : $from.pos, $to.pos, fragment);
53
+ }
45
54
  return tr;
46
55
  };
47
56
  }
@@ -9,7 +9,6 @@ var _react = _interopRequireWildcard(require("react"));
9
9
  function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function _interopRequireWildcard(e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != _typeof(e) && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (var _t in e) "default" !== _t && {}.hasOwnProperty.call(e, _t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, _t)) && (i.get || i.set) ? o(f, _t, i) : f[_t] = e[_t]); return f; })(e, t); }
10
10
  var BlockMenuContext = /*#__PURE__*/(0, _react.createContext)({
11
11
  onDropdownOpenChanged: function onDropdownOpenChanged() {},
12
- moveFocusTo: function moveFocusTo() {},
13
12
  moveDownRef: /*#__PURE__*/_react.default.createRef(),
14
13
  moveUpRef: /*#__PURE__*/_react.default.createRef()
15
14
  });
@@ -37,19 +36,9 @@ var BlockMenuProvider = exports.BlockMenuProvider = function BlockMenuProvider(_
37
36
  }, 1);
38
37
  }
39
38
  }, [api]);
40
- var moveFocusTo = (0, _react.useCallback)(function (direction) {
41
- if (direction === 'moveUp') {
42
- var _moveUpRef$current;
43
- (_moveUpRef$current = moveUpRef.current) === null || _moveUpRef$current === void 0 || _moveUpRef$current.focus();
44
- } else if (direction === 'moveDown') {
45
- var _moveDownRef$current;
46
- (_moveDownRef$current = moveDownRef.current) === null || _moveDownRef$current === void 0 || _moveDownRef$current.focus();
47
- }
48
- }, []);
49
39
  return /*#__PURE__*/_react.default.createElement(BlockMenuContext.Provider, {
50
40
  value: {
51
41
  onDropdownOpenChanged: onDropdownOpenChanged,
52
- moveFocusTo: moveFocusTo,
53
42
  moveDownRef: moveDownRef,
54
43
  moveUpRef: moveUpRef
55
44
  }
@@ -22,7 +22,7 @@ var MoveDownDropdownItemContent = function MoveDownDropdownItemContent(_ref) {
22
22
  var _useIntl = (0, _reactIntlNext.useIntl)(),
23
23
  formatMessage = _useIntl.formatMessage;
24
24
  var _useBlockMenu = (0, _blockMenuProvider.useBlockMenu)(),
25
- moveFocusTo = _useBlockMenu.moveFocusTo,
25
+ moveUpRef = _useBlockMenu.moveUpRef,
26
26
  moveDownRef = _useBlockMenu.moveDownRef;
27
27
  var _useSharedPluginState = (0, _hooks.useSharedPluginStateWithSelector)(api, ['blockControls'], function (_ref2) {
28
28
  var _blockControlsState$b;
@@ -33,14 +33,10 @@ var MoveDownDropdownItemContent = function MoveDownDropdownItemContent(_ref) {
33
33
  }),
34
34
  canMoveDown = _useSharedPluginState.canMoveDown;
35
35
  (0, _react.useEffect)(function () {
36
- var moveDownElement = moveDownRef.current;
37
- if (!moveDownElement) {
38
- return;
36
+ if (!canMoveDown && moveDownRef.current && moveDownRef.current === document.activeElement && moveUpRef.current) {
37
+ moveUpRef.current.focus();
39
38
  }
40
- if (!canMoveDown && moveDownElement === document.activeElement) {
41
- moveFocusTo('moveUp');
42
- }
43
- }, [canMoveDown, moveFocusTo, moveDownRef]);
39
+ }, [canMoveDown, moveUpRef, moveDownRef]);
44
40
  var handleClick = function handleClick() {
45
41
  api === null || api === void 0 || api.core.actions.execute(function (_ref3) {
46
42
  var _api$analytics, _api$blockControls;
@@ -22,8 +22,8 @@ var MoveUpDropdownItemContent = function MoveUpDropdownItemContent(_ref) {
22
22
  var _useIntl = (0, _reactIntlNext.useIntl)(),
23
23
  formatMessage = _useIntl.formatMessage;
24
24
  var _useBlockMenu = (0, _blockMenuProvider.useBlockMenu)(),
25
- moveFocusTo = _useBlockMenu.moveFocusTo,
26
- moveUpRef = _useBlockMenu.moveUpRef;
25
+ moveUpRef = _useBlockMenu.moveUpRef,
26
+ moveDownRef = _useBlockMenu.moveDownRef;
27
27
  var _useSharedPluginState = (0, _hooks.useSharedPluginStateWithSelector)(api, ['blockControls'], function (_ref2) {
28
28
  var _blockControlsState$b;
29
29
  var blockControlsState = _ref2.blockControlsState;
@@ -33,14 +33,10 @@ var MoveUpDropdownItemContent = function MoveUpDropdownItemContent(_ref) {
33
33
  }),
34
34
  canMoveUp = _useSharedPluginState.canMoveUp;
35
35
  (0, _react.useEffect)(function () {
36
- var moveUpElement = moveUpRef.current;
37
- if (!moveUpElement) {
38
- return;
36
+ if (!canMoveUp && moveUpRef.current && moveUpRef.current === document.activeElement && moveDownRef.current) {
37
+ moveDownRef.current.focus();
39
38
  }
40
- if (!canMoveUp && moveUpElement === document.activeElement) {
41
- moveFocusTo('moveDown');
42
- }
43
- }, [canMoveUp, moveFocusTo, moveUpRef]);
39
+ }, [canMoveUp, moveDownRef, moveUpRef]);
44
40
  var handleClick = function handleClick() {
45
41
  api === null || api === void 0 || api.core.actions.execute(function (_ref3) {
46
42
  var _api$analytics, _api$blockControls;
@@ -0,0 +1,88 @@
1
+ import { NODE_CATEGORY_BY_TYPE } from '../types';
2
+
3
+ /**
4
+ * Determines if a node is a text node (heading or paragraph).
5
+ * Only text nodes should have their inline content extracted for decisionItem.
6
+ * All other nodes should break out.
7
+ */
8
+ const isTextNode = node => {
9
+ const category = NODE_CATEGORY_BY_TYPE[node.type.name];
10
+ return category === 'text';
11
+ };
12
+
13
+ /**
14
+ * Creates a decisionItem with the given inline content.
15
+ */
16
+ const createDecisionItem = (inlineContent, schema) => {
17
+ const decisionItemType = schema.nodes.decisionItem;
18
+ if (!decisionItemType) {
19
+ return null;
20
+ }
21
+ const canContentBeWrappedInDecisionItem = decisionItemType.validContent(inlineContent);
22
+
23
+ // Check if the content is valid for decisionItem
24
+ if (!canContentBeWrappedInDecisionItem) {
25
+ return null;
26
+ }
27
+ return decisionItemType.createAndFill({}, inlineContent);
28
+ };
29
+
30
+ /**
31
+ * Creates a decisionList containing the given decisionItems.
32
+ */
33
+ const createDecisionListWithItems = (decisionItems, schema) => {
34
+ const decisionListType = schema.nodes.decisionList;
35
+ if (!decisionListType || decisionItems.length === 0) {
36
+ return null;
37
+ }
38
+ return decisionListType.createAndFill({}, decisionItems);
39
+ };
40
+
41
+ /**
42
+ * Wraps blockquote content into decisionList:
43
+ * - Text nodes (paragraph, heading) have their inline content extracted and wrapped in decisionItems
44
+ * - Consecutive text nodes are grouped into a single decisionList with multiple decisionItems
45
+ * - All other nodes break out (lists, code blocks, media, tables, macros, containers, etc.)
46
+ *
47
+ * The logic follows the transform rules:
48
+ * - Only flatten text nodes into decisionItem (since that's the intent - converting text to decisions)
49
+ * - Structures that can't be represented in a decisionItem should break out unchanged
50
+ * - When a break-out node is encountered, flush accumulated decisionItems into a decisionList
51
+ *
52
+ * Example: blockquote(p('a'), p('b'), ul(...), p('c')) → [decisionList(decisionItem('a'), decisionItem('b')), ul(...), decisionList(decisionItem('c'))]
53
+ */
54
+ export const wrapBlockquoteToDecisionListStep = (nodes, context) => {
55
+ const {
56
+ schema
57
+ } = context;
58
+ const decisionItemType = schema.nodes.decisionItem;
59
+ if (!decisionItemType) {
60
+ return nodes;
61
+ }
62
+ const result = [];
63
+ let currentDecisionItems = [];
64
+ const flushCurrentDecisionList = () => {
65
+ if (currentDecisionItems.length > 0) {
66
+ const decisionList = createDecisionListWithItems(currentDecisionItems, schema);
67
+ if (decisionList) {
68
+ result.push(decisionList);
69
+ }
70
+ currentDecisionItems = [];
71
+ }
72
+ };
73
+ nodes.forEach(node => {
74
+ const decisionItem = isTextNode(node) ? createDecisionItem(node.content, schema) : null;
75
+ if (decisionItem) {
76
+ // Accumulate consecutive decisionItems
77
+ currentDecisionItems.push(decisionItem);
78
+ } else {
79
+ // Content can't be wrapped in decisionItem - break out the node
80
+ flushCurrentDecisionList();
81
+ result.push(node);
82
+ }
83
+ });
84
+
85
+ // Flush any remaining decisionItems
86
+ flushCurrentDecisionList();
87
+ return result.length > 0 ? result : nodes;
88
+ };
@@ -40,11 +40,7 @@ const canWrapInTarget = (node, targetNodeType, targetNodeTypeName) => {
40
40
  }
41
41
 
42
42
  // Use the schema to determine if this node can be contained in the target
43
- try {
44
- return targetNodeType.validContent(Fragment.from(node));
45
- } catch {
46
- return false;
47
- }
43
+ return targetNodeType.validContent(Fragment.from(node));
48
44
  };
49
45
 
50
46
  /**
@@ -6,12 +6,14 @@ import { listToDecisionListStep } from './steps/listToDecisionListStep';
6
6
  import { listToListStep } from './steps/listToListStep';
7
7
  import { unwrapLayoutStep } from './steps/unwrapLayoutStep';
8
8
  import { unwrapListStep } from './steps/unwrapListStep';
9
+ import { wrapBlockquoteToDecisionListStep } from './steps/wrapBlockquoteToDecisionListStep';
9
10
  import { wrapMixedContentStep } from './steps/wrapMixedContentStep';
10
11
  import { stubStep } from './stubStep';
11
12
  import { NODE_CATEGORY_BY_TYPE, toNodeTypeValue } from './types';
12
13
  import { unwrapExpandStep } from './unwrapExpandStep';
13
14
  import { unwrapStep } from './unwrapStep';
14
15
  import { wrapIntoLayoutStep } from './wrapIntoLayoutStep';
16
+ import { wrapIntoListStep } from './wrapIntoListStep';
15
17
  import { wrapStep } from './wrapStep';
16
18
 
17
19
  // Exampled step for overrides:
@@ -27,7 +29,7 @@ const TRANSFORM_STEPS = {
27
29
  atomic: {
28
30
  atomic: undefined,
29
31
  container: [wrapStep],
30
- list: undefined,
32
+ list: [wrapIntoListStep],
31
33
  text: undefined
32
34
  },
33
35
  container: {
@@ -78,7 +80,8 @@ const TRANSFORM_STEPS_OVERRIDE = {
78
80
  expand: [wrapStep],
79
81
  nestedExpand: [wrapStep],
80
82
  layoutSection: [wrapIntoLayoutStep],
81
- codeBlock: [unwrapStep, flattenStep, wrapStep]
83
+ codeBlock: [unwrapStep, flattenStep, wrapStep],
84
+ decisionList: [unwrapStep, wrapBlockquoteToDecisionListStep]
82
85
  },
83
86
  layoutSection: {
84
87
  blockquote: [unwrapLayoutStep, wrapStep],
@@ -114,7 +117,12 @@ const TRANSFORM_STEPS_OVERRIDE = {
114
117
  decisionList: [flattenListStep, listToDecisionListStep]
115
118
  },
116
119
  table: {
117
- expand: [wrapStep],
120
+ layoutSection: [wrapIntoLayoutStep]
121
+ },
122
+ mediaSingle: {
123
+ layoutSection: [wrapIntoLayoutStep]
124
+ },
125
+ mediaGroup: {
118
126
  layoutSection: [wrapIntoLayoutStep]
119
127
  },
120
128
  decisionList: {
@@ -73,13 +73,29 @@ export const expandSelectionToBlockRange = (selection, schema) => {
73
73
  const table = findTable(selection);
74
74
  if (table) {
75
75
  const $from = selection.$from.doc.resolve(table.pos);
76
- const $to = selection.$from.doc.resolve(table.pos + table.node.nodeSize - 1);
76
+ const $to = selection.$from.doc.resolve(table.pos + table.node.nodeSize);
77
77
  return {
78
78
  $from,
79
79
  $to
80
80
  };
81
81
  }
82
82
  }
83
+
84
+ // when selecting a file, selection is on media
85
+ // need to find media group and return its pos
86
+ if (selection instanceof NodeSelection) {
87
+ if (selection.node.type === nodes.media) {
88
+ const mediaGroup = findParentNodeOfType(nodes.mediaGroup)(selection);
89
+ if (mediaGroup) {
90
+ const $from = selection.$from.doc.resolve(mediaGroup.pos);
91
+ const $to = selection.$from.doc.resolve(mediaGroup.pos + mediaGroup.node.nodeSize);
92
+ return {
93
+ $from,
94
+ $to
95
+ };
96
+ }
97
+ }
98
+ }
83
99
  return expandToBlockRange(selection.$from, selection.$to, node => {
84
100
  if (nodesNeedToExpandRange.includes(node.type)) {
85
101
  return false;
@@ -0,0 +1,13 @@
1
+ /** wrap nodes into bullet list or numbered list, does not work for task list */
2
+ export const wrapIntoListStep = (nodes, context) => {
3
+ const {
4
+ schema,
5
+ targetNodeTypeName
6
+ } = context;
7
+ const listItemNode = schema.nodes.listItem.createAndFill({}, nodes);
8
+ const outputNode = schema.nodes[targetNodeTypeName].createAndFill({}, listItemNode);
9
+ if (outputNode) {
10
+ return [outputNode];
11
+ }
12
+ return nodes;
13
+ };
@@ -1,4 +1,5 @@
1
1
  import { Fragment } from '@atlaskit/editor-prosemirror/model';
2
+ import { NodeSelection } from '@atlaskit/editor-prosemirror/state';
2
3
  import { isNestedNode } from '../ui/utils/isNestedNode';
3
4
  import { getOutputNodes } from './transform-node-utils/transform';
4
5
  import { expandSelectionToBlockRange } from './transform-node-utils/utils';
@@ -14,10 +15,14 @@ export const transformNode = api =>
14
15
  if (!preservedSelection) {
15
16
  return tr;
16
17
  }
18
+ const schema = tr.doc.type.schema;
19
+ const {
20
+ nodes
21
+ } = schema;
17
22
  const {
18
23
  $from,
19
24
  $to
20
- } = expandSelectionToBlockRange(preservedSelection, tr.doc.type.schema);
25
+ } = expandSelectionToBlockRange(preservedSelection, schema);
21
26
  const isNested = isNestedNode(preservedSelection, '');
22
27
  const selectedParent = $from.parent;
23
28
  let fragment = Fragment.empty;
@@ -34,9 +39,15 @@ export const transformNode = api =>
34
39
  fragment = fragment.append(Fragment.fromArray(outputNode));
35
40
  }
36
41
  });
37
-
38
- // TODO: ED-12345 - selection is broken post transaction, to fix.
39
- tr.replaceWith(isList ? $from.pos - 1 : $from.pos, $to.pos, fragment);
42
+ const nodesToDeleteAndInsert = [nodes.mediaSingle];
43
+ if (preservedSelection instanceof NodeSelection && nodesToDeleteAndInsert.includes(preservedSelection.node.type)) {
44
+ // when node is media single, use tr.replaceWith freeze editor, if modify position, tr.replaceWith creates duplicats
45
+ tr.deleteRange($from.pos, $to.pos);
46
+ tr.insert($from.pos, fragment);
47
+ } else {
48
+ // TODO: ED-12345 - selection is broken post transaction, to fix.
49
+ tr.replaceWith(isList ? $from.pos - 1 : $from.pos, $to.pos, fragment);
50
+ }
40
51
  return tr;
41
52
  };
42
53
  };
@@ -1,7 +1,6 @@
1
1
  import React, { useCallback, createContext, useContext, useRef } from 'react';
2
2
  const BlockMenuContext = /*#__PURE__*/createContext({
3
3
  onDropdownOpenChanged: () => {},
4
- moveFocusTo: () => {},
5
4
  moveDownRef: /*#__PURE__*/React.createRef(),
6
5
  moveUpRef: /*#__PURE__*/React.createRef()
7
6
  });
@@ -28,19 +27,9 @@ export const BlockMenuProvider = ({
28
27
  }), 1);
29
28
  }
30
29
  }, [api]);
31
- const moveFocusTo = useCallback(direction => {
32
- if (direction === 'moveUp') {
33
- var _moveUpRef$current;
34
- (_moveUpRef$current = moveUpRef.current) === null || _moveUpRef$current === void 0 ? void 0 : _moveUpRef$current.focus();
35
- } else if (direction === 'moveDown') {
36
- var _moveDownRef$current;
37
- (_moveDownRef$current = moveDownRef.current) === null || _moveDownRef$current === void 0 ? void 0 : _moveDownRef$current.focus();
38
- }
39
- }, []);
40
30
  return /*#__PURE__*/React.createElement(BlockMenuContext.Provider, {
41
31
  value: {
42
32
  onDropdownOpenChanged,
43
- moveFocusTo,
44
33
  moveDownRef,
45
34
  moveUpRef
46
35
  }
@@ -15,7 +15,7 @@ const MoveDownDropdownItemContent = ({
15
15
  formatMessage
16
16
  } = useIntl();
17
17
  const {
18
- moveFocusTo,
18
+ moveUpRef,
19
19
  moveDownRef
20
20
  } = useBlockMenu();
21
21
  const {
@@ -29,14 +29,10 @@ const MoveDownDropdownItemContent = ({
29
29
  };
30
30
  });
31
31
  useEffect(() => {
32
- const moveDownElement = moveDownRef.current;
33
- if (!moveDownElement) {
34
- return;
32
+ if (!canMoveDown && moveDownRef.current && moveDownRef.current === document.activeElement && moveUpRef.current) {
33
+ moveUpRef.current.focus();
35
34
  }
36
- if (!canMoveDown && moveDownElement === document.activeElement) {
37
- moveFocusTo('moveUp');
38
- }
39
- }, [canMoveDown, moveFocusTo, moveDownRef]);
35
+ }, [canMoveDown, moveUpRef, moveDownRef]);
40
36
  const handleClick = () => {
41
37
  api === null || api === void 0 ? void 0 : api.core.actions.execute(({
42
38
  tr
@@ -15,8 +15,8 @@ const MoveUpDropdownItemContent = ({
15
15
  formatMessage
16
16
  } = useIntl();
17
17
  const {
18
- moveFocusTo,
19
- moveUpRef
18
+ moveUpRef,
19
+ moveDownRef
20
20
  } = useBlockMenu();
21
21
  const {
22
22
  canMoveUp
@@ -29,14 +29,10 @@ const MoveUpDropdownItemContent = ({
29
29
  };
30
30
  });
31
31
  useEffect(() => {
32
- const moveUpElement = moveUpRef.current;
33
- if (!moveUpElement) {
34
- return;
32
+ if (!canMoveUp && moveUpRef.current && moveUpRef.current === document.activeElement && moveDownRef.current) {
33
+ moveDownRef.current.focus();
35
34
  }
36
- if (!canMoveUp && moveUpElement === document.activeElement) {
37
- moveFocusTo('moveDown');
38
- }
39
- }, [canMoveUp, moveFocusTo, moveUpRef]);
35
+ }, [canMoveUp, moveDownRef, moveUpRef]);
40
36
  const handleClick = () => {
41
37
  api === null || api === void 0 ? void 0 : api.core.actions.execute(({
42
38
  tr
@@ -0,0 +1,86 @@
1
+ import { NODE_CATEGORY_BY_TYPE } from '../types';
2
+
3
+ /**
4
+ * Determines if a node is a text node (heading or paragraph).
5
+ * Only text nodes should have their inline content extracted for decisionItem.
6
+ * All other nodes should break out.
7
+ */
8
+ var isTextNode = function isTextNode(node) {
9
+ var category = NODE_CATEGORY_BY_TYPE[node.type.name];
10
+ return category === 'text';
11
+ };
12
+
13
+ /**
14
+ * Creates a decisionItem with the given inline content.
15
+ */
16
+ var createDecisionItem = function createDecisionItem(inlineContent, schema) {
17
+ var decisionItemType = schema.nodes.decisionItem;
18
+ if (!decisionItemType) {
19
+ return null;
20
+ }
21
+ var canContentBeWrappedInDecisionItem = decisionItemType.validContent(inlineContent);
22
+
23
+ // Check if the content is valid for decisionItem
24
+ if (!canContentBeWrappedInDecisionItem) {
25
+ return null;
26
+ }
27
+ return decisionItemType.createAndFill({}, inlineContent);
28
+ };
29
+
30
+ /**
31
+ * Creates a decisionList containing the given decisionItems.
32
+ */
33
+ var createDecisionListWithItems = function createDecisionListWithItems(decisionItems, schema) {
34
+ var decisionListType = schema.nodes.decisionList;
35
+ if (!decisionListType || decisionItems.length === 0) {
36
+ return null;
37
+ }
38
+ return decisionListType.createAndFill({}, decisionItems);
39
+ };
40
+
41
+ /**
42
+ * Wraps blockquote content into decisionList:
43
+ * - Text nodes (paragraph, heading) have their inline content extracted and wrapped in decisionItems
44
+ * - Consecutive text nodes are grouped into a single decisionList with multiple decisionItems
45
+ * - All other nodes break out (lists, code blocks, media, tables, macros, containers, etc.)
46
+ *
47
+ * The logic follows the transform rules:
48
+ * - Only flatten text nodes into decisionItem (since that's the intent - converting text to decisions)
49
+ * - Structures that can't be represented in a decisionItem should break out unchanged
50
+ * - When a break-out node is encountered, flush accumulated decisionItems into a decisionList
51
+ *
52
+ * Example: blockquote(p('a'), p('b'), ul(...), p('c')) → [decisionList(decisionItem('a'), decisionItem('b')), ul(...), decisionList(decisionItem('c'))]
53
+ */
54
+ export var wrapBlockquoteToDecisionListStep = function wrapBlockquoteToDecisionListStep(nodes, context) {
55
+ var schema = context.schema;
56
+ var decisionItemType = schema.nodes.decisionItem;
57
+ if (!decisionItemType) {
58
+ return nodes;
59
+ }
60
+ var result = [];
61
+ var currentDecisionItems = [];
62
+ var flushCurrentDecisionList = function flushCurrentDecisionList() {
63
+ if (currentDecisionItems.length > 0) {
64
+ var decisionList = createDecisionListWithItems(currentDecisionItems, schema);
65
+ if (decisionList) {
66
+ result.push(decisionList);
67
+ }
68
+ currentDecisionItems = [];
69
+ }
70
+ };
71
+ nodes.forEach(function (node) {
72
+ var decisionItem = isTextNode(node) ? createDecisionItem(node.content, schema) : null;
73
+ if (decisionItem) {
74
+ // Accumulate consecutive decisionItems
75
+ currentDecisionItems.push(decisionItem);
76
+ } else {
77
+ // Content can't be wrapped in decisionItem - break out the node
78
+ flushCurrentDecisionList();
79
+ result.push(node);
80
+ }
81
+ });
82
+
83
+ // Flush any remaining decisionItems
84
+ flushCurrentDecisionList();
85
+ return result.length > 0 ? result : nodes;
86
+ };
@@ -40,11 +40,7 @@ var canWrapInTarget = function canWrapInTarget(node, targetNodeType, targetNodeT
40
40
  }
41
41
 
42
42
  // Use the schema to determine if this node can be contained in the target
43
- try {
44
- return targetNodeType.validContent(Fragment.from(node));
45
- } catch (_unused) {
46
- return false;
47
- }
43
+ return targetNodeType.validContent(Fragment.from(node));
48
44
  };
49
45
 
50
46
  /**
@@ -6,12 +6,14 @@ import { listToDecisionListStep } from './steps/listToDecisionListStep';
6
6
  import { listToListStep } from './steps/listToListStep';
7
7
  import { unwrapLayoutStep } from './steps/unwrapLayoutStep';
8
8
  import { unwrapListStep } from './steps/unwrapListStep';
9
+ import { wrapBlockquoteToDecisionListStep } from './steps/wrapBlockquoteToDecisionListStep';
9
10
  import { wrapMixedContentStep } from './steps/wrapMixedContentStep';
10
11
  import { stubStep } from './stubStep';
11
12
  import { NODE_CATEGORY_BY_TYPE, toNodeTypeValue } from './types';
12
13
  import { unwrapExpandStep } from './unwrapExpandStep';
13
14
  import { unwrapStep } from './unwrapStep';
14
15
  import { wrapIntoLayoutStep } from './wrapIntoLayoutStep';
16
+ import { wrapIntoListStep } from './wrapIntoListStep';
15
17
  import { wrapStep } from './wrapStep';
16
18
 
17
19
  // Exampled step for overrides:
@@ -27,7 +29,7 @@ var TRANSFORM_STEPS = {
27
29
  atomic: {
28
30
  atomic: undefined,
29
31
  container: [wrapStep],
30
- list: undefined,
32
+ list: [wrapIntoListStep],
31
33
  text: undefined
32
34
  },
33
35
  container: {
@@ -78,7 +80,8 @@ var TRANSFORM_STEPS_OVERRIDE = {
78
80
  expand: [wrapStep],
79
81
  nestedExpand: [wrapStep],
80
82
  layoutSection: [wrapIntoLayoutStep],
81
- codeBlock: [unwrapStep, flattenStep, wrapStep]
83
+ codeBlock: [unwrapStep, flattenStep, wrapStep],
84
+ decisionList: [unwrapStep, wrapBlockquoteToDecisionListStep]
82
85
  },
83
86
  layoutSection: {
84
87
  blockquote: [unwrapLayoutStep, wrapStep],
@@ -114,7 +117,12 @@ var TRANSFORM_STEPS_OVERRIDE = {
114
117
  decisionList: [flattenListStep, listToDecisionListStep]
115
118
  },
116
119
  table: {
117
- expand: [wrapStep],
120
+ layoutSection: [wrapIntoLayoutStep]
121
+ },
122
+ mediaSingle: {
123
+ layoutSection: [wrapIntoLayoutStep]
124
+ },
125
+ mediaGroup: {
118
126
  layoutSection: [wrapIntoLayoutStep]
119
127
  },
120
128
  decisionList: {
@@ -70,13 +70,29 @@ export var expandSelectionToBlockRange = function expandSelectionToBlockRange(se
70
70
  var table = findTable(selection);
71
71
  if (table) {
72
72
  var $from = selection.$from.doc.resolve(table.pos);
73
- var $to = selection.$from.doc.resolve(table.pos + table.node.nodeSize - 1);
73
+ var $to = selection.$from.doc.resolve(table.pos + table.node.nodeSize);
74
74
  return {
75
75
  $from: $from,
76
76
  $to: $to
77
77
  };
78
78
  }
79
79
  }
80
+
81
+ // when selecting a file, selection is on media
82
+ // need to find media group and return its pos
83
+ if (selection instanceof NodeSelection) {
84
+ if (selection.node.type === nodes.media) {
85
+ var mediaGroup = findParentNodeOfType(nodes.mediaGroup)(selection);
86
+ if (mediaGroup) {
87
+ var _$from = selection.$from.doc.resolve(mediaGroup.pos);
88
+ var _$to = selection.$from.doc.resolve(mediaGroup.pos + mediaGroup.node.nodeSize);
89
+ return {
90
+ $from: _$from,
91
+ $to: _$to
92
+ };
93
+ }
94
+ }
95
+ }
80
96
  return expandToBlockRange(selection.$from, selection.$to, function (node) {
81
97
  if (nodesNeedToExpandRange.includes(node.type)) {
82
98
  return false;
@@ -0,0 +1,11 @@
1
+ /** wrap nodes into bullet list or numbered list, does not work for task list */
2
+ export var wrapIntoListStep = function wrapIntoListStep(nodes, context) {
3
+ var schema = context.schema,
4
+ targetNodeTypeName = context.targetNodeTypeName;
5
+ var listItemNode = schema.nodes.listItem.createAndFill({}, nodes);
6
+ var outputNode = schema.nodes[targetNodeTypeName].createAndFill({}, listItemNode);
7
+ if (outputNode) {
8
+ return [outputNode];
9
+ }
10
+ return nodes;
11
+ };
@@ -1,4 +1,5 @@
1
1
  import { Fragment } from '@atlaskit/editor-prosemirror/model';
2
+ import { NodeSelection } from '@atlaskit/editor-prosemirror/state';
2
3
  import { isNestedNode } from '../ui/utils/isNestedNode';
3
4
  import { getOutputNodes } from './transform-node-utils/transform';
4
5
  import { expandSelectionToBlockRange } from './transform-node-utils/utils';
@@ -14,7 +15,9 @@ export var transformNode = function transformNode(api) {
14
15
  if (!preservedSelection) {
15
16
  return tr;
16
17
  }
17
- var _expandSelectionToBlo = expandSelectionToBlockRange(preservedSelection, tr.doc.type.schema),
18
+ var schema = tr.doc.type.schema;
19
+ var nodes = schema.nodes;
20
+ var _expandSelectionToBlo = expandSelectionToBlockRange(preservedSelection, schema),
18
21
  $from = _expandSelectionToBlo.$from,
19
22
  $to = _expandSelectionToBlo.$to;
20
23
  var isNested = isNestedNode(preservedSelection, '');
@@ -33,9 +36,15 @@ export var transformNode = function transformNode(api) {
33
36
  fragment = fragment.append(Fragment.fromArray(outputNode));
34
37
  }
35
38
  });
36
-
37
- // TODO: ED-12345 - selection is broken post transaction, to fix.
38
- tr.replaceWith(isList ? $from.pos - 1 : $from.pos, $to.pos, fragment);
39
+ var nodesToDeleteAndInsert = [nodes.mediaSingle];
40
+ if (preservedSelection instanceof NodeSelection && nodesToDeleteAndInsert.includes(preservedSelection.node.type)) {
41
+ // when node is media single, use tr.replaceWith freeze editor, if modify position, tr.replaceWith creates duplicats
42
+ tr.deleteRange($from.pos, $to.pos);
43
+ tr.insert($from.pos, fragment);
44
+ } else {
45
+ // TODO: ED-12345 - selection is broken post transaction, to fix.
46
+ tr.replaceWith(isList ? $from.pos - 1 : $from.pos, $to.pos, fragment);
47
+ }
39
48
  return tr;
40
49
  };
41
50
  }
@@ -1,7 +1,6 @@
1
1
  import React, { useCallback, createContext, useContext, useRef } from 'react';
2
2
  var BlockMenuContext = /*#__PURE__*/createContext({
3
3
  onDropdownOpenChanged: function onDropdownOpenChanged() {},
4
- moveFocusTo: function moveFocusTo() {},
5
4
  moveDownRef: /*#__PURE__*/React.createRef(),
6
5
  moveUpRef: /*#__PURE__*/React.createRef()
7
6
  });
@@ -29,19 +28,9 @@ export var BlockMenuProvider = function BlockMenuProvider(_ref) {
29
28
  }, 1);
30
29
  }
31
30
  }, [api]);
32
- var moveFocusTo = useCallback(function (direction) {
33
- if (direction === 'moveUp') {
34
- var _moveUpRef$current;
35
- (_moveUpRef$current = moveUpRef.current) === null || _moveUpRef$current === void 0 || _moveUpRef$current.focus();
36
- } else if (direction === 'moveDown') {
37
- var _moveDownRef$current;
38
- (_moveDownRef$current = moveDownRef.current) === null || _moveDownRef$current === void 0 || _moveDownRef$current.focus();
39
- }
40
- }, []);
41
31
  return /*#__PURE__*/React.createElement(BlockMenuContext.Provider, {
42
32
  value: {
43
33
  onDropdownOpenChanged: onDropdownOpenChanged,
44
- moveFocusTo: moveFocusTo,
45
34
  moveDownRef: moveDownRef,
46
35
  moveUpRef: moveUpRef
47
36
  }
@@ -13,7 +13,7 @@ var MoveDownDropdownItemContent = function MoveDownDropdownItemContent(_ref) {
13
13
  var _useIntl = useIntl(),
14
14
  formatMessage = _useIntl.formatMessage;
15
15
  var _useBlockMenu = useBlockMenu(),
16
- moveFocusTo = _useBlockMenu.moveFocusTo,
16
+ moveUpRef = _useBlockMenu.moveUpRef,
17
17
  moveDownRef = _useBlockMenu.moveDownRef;
18
18
  var _useSharedPluginState = useSharedPluginStateWithSelector(api, ['blockControls'], function (_ref2) {
19
19
  var _blockControlsState$b;
@@ -24,14 +24,10 @@ var MoveDownDropdownItemContent = function MoveDownDropdownItemContent(_ref) {
24
24
  }),
25
25
  canMoveDown = _useSharedPluginState.canMoveDown;
26
26
  useEffect(function () {
27
- var moveDownElement = moveDownRef.current;
28
- if (!moveDownElement) {
29
- return;
27
+ if (!canMoveDown && moveDownRef.current && moveDownRef.current === document.activeElement && moveUpRef.current) {
28
+ moveUpRef.current.focus();
30
29
  }
31
- if (!canMoveDown && moveDownElement === document.activeElement) {
32
- moveFocusTo('moveUp');
33
- }
34
- }, [canMoveDown, moveFocusTo, moveDownRef]);
30
+ }, [canMoveDown, moveUpRef, moveDownRef]);
35
31
  var handleClick = function handleClick() {
36
32
  api === null || api === void 0 || api.core.actions.execute(function (_ref3) {
37
33
  var _api$analytics, _api$blockControls;
@@ -13,8 +13,8 @@ var MoveUpDropdownItemContent = function MoveUpDropdownItemContent(_ref) {
13
13
  var _useIntl = useIntl(),
14
14
  formatMessage = _useIntl.formatMessage;
15
15
  var _useBlockMenu = useBlockMenu(),
16
- moveFocusTo = _useBlockMenu.moveFocusTo,
17
- moveUpRef = _useBlockMenu.moveUpRef;
16
+ moveUpRef = _useBlockMenu.moveUpRef,
17
+ moveDownRef = _useBlockMenu.moveDownRef;
18
18
  var _useSharedPluginState = useSharedPluginStateWithSelector(api, ['blockControls'], function (_ref2) {
19
19
  var _blockControlsState$b;
20
20
  var blockControlsState = _ref2.blockControlsState;
@@ -24,14 +24,10 @@ var MoveUpDropdownItemContent = function MoveUpDropdownItemContent(_ref) {
24
24
  }),
25
25
  canMoveUp = _useSharedPluginState.canMoveUp;
26
26
  useEffect(function () {
27
- var moveUpElement = moveUpRef.current;
28
- if (!moveUpElement) {
29
- return;
27
+ if (!canMoveUp && moveUpRef.current && moveUpRef.current === document.activeElement && moveDownRef.current) {
28
+ moveDownRef.current.focus();
30
29
  }
31
- if (!canMoveUp && moveUpElement === document.activeElement) {
32
- moveFocusTo('moveDown');
33
- }
34
- }, [canMoveUp, moveFocusTo, moveUpRef]);
30
+ }, [canMoveUp, moveDownRef, moveUpRef]);
35
31
  var handleClick = function handleClick() {
36
32
  api === null || api === void 0 || api.core.actions.execute(function (_ref3) {
37
33
  var _api$analytics, _api$blockControls;
@@ -0,0 +1,15 @@
1
+ import type { TransformStep } from '../types';
2
+ /**
3
+ * Wraps blockquote content into decisionList:
4
+ * - Text nodes (paragraph, heading) have their inline content extracted and wrapped in decisionItems
5
+ * - Consecutive text nodes are grouped into a single decisionList with multiple decisionItems
6
+ * - All other nodes break out (lists, code blocks, media, tables, macros, containers, etc.)
7
+ *
8
+ * The logic follows the transform rules:
9
+ * - Only flatten text nodes into decisionItem (since that's the intent - converting text to decisions)
10
+ * - Structures that can't be represented in a decisionItem should break out unchanged
11
+ * - When a break-out node is encountered, flush accumulated decisionItems into a decisionList
12
+ *
13
+ * Example: blockquote(p('a'), p('b'), ul(...), p('c')) → [decisionList(decisionItem('a'), decisionItem('b')), ul(...), decisionList(decisionItem('c'))]
14
+ */
15
+ export declare const wrapBlockquoteToDecisionListStep: TransformStep;
@@ -0,0 +1,3 @@
1
+ import type { TransformStep } from './types';
2
+ /** wrap nodes into bullet list or numbered list, does not work for task list */
3
+ export declare const wrapIntoListStep: TransformStep;
@@ -8,11 +8,6 @@ type BlockMenuProviderProps = {
8
8
  };
9
9
  export type BlockMenuContextType = {
10
10
  moveDownRef: React.MutableRefObject<HTMLButtonElement | null>;
11
- /**
12
- * Function to move focus between move up and move down items.
13
- * Used when one item is disabled and focused.
14
- */
15
- moveFocusTo: (direction: Direction) => void;
16
11
  moveUpRef: React.MutableRefObject<HTMLButtonElement | null>;
17
12
  /**
18
13
  * Callback for when the dropdown is open/closed. Receives an object with `isOpen` state.
@@ -0,0 +1,15 @@
1
+ import type { TransformStep } from '../types';
2
+ /**
3
+ * Wraps blockquote content into decisionList:
4
+ * - Text nodes (paragraph, heading) have their inline content extracted and wrapped in decisionItems
5
+ * - Consecutive text nodes are grouped into a single decisionList with multiple decisionItems
6
+ * - All other nodes break out (lists, code blocks, media, tables, macros, containers, etc.)
7
+ *
8
+ * The logic follows the transform rules:
9
+ * - Only flatten text nodes into decisionItem (since that's the intent - converting text to decisions)
10
+ * - Structures that can't be represented in a decisionItem should break out unchanged
11
+ * - When a break-out node is encountered, flush accumulated decisionItems into a decisionList
12
+ *
13
+ * Example: blockquote(p('a'), p('b'), ul(...), p('c')) → [decisionList(decisionItem('a'), decisionItem('b')), ul(...), decisionList(decisionItem('c'))]
14
+ */
15
+ export declare const wrapBlockquoteToDecisionListStep: TransformStep;
@@ -0,0 +1,3 @@
1
+ import type { TransformStep } from './types';
2
+ /** wrap nodes into bullet list or numbered list, does not work for task list */
3
+ export declare const wrapIntoListStep: TransformStep;
@@ -8,11 +8,6 @@ type BlockMenuProviderProps = {
8
8
  };
9
9
  export type BlockMenuContextType = {
10
10
  moveDownRef: React.MutableRefObject<HTMLButtonElement | null>;
11
- /**
12
- * Function to move focus between move up and move down items.
13
- * Used when one item is disabled and focused.
14
- */
15
- moveFocusTo: (direction: Direction) => void;
16
11
  moveUpRef: React.MutableRefObject<HTMLButtonElement | null>;
17
12
  /**
18
13
  * Callback for when the dropdown is open/closed. Receives an object with `isOpen` state.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/editor-plugin-block-menu",
3
- "version": "5.2.6",
3
+ "version": "5.2.8",
4
4
  "description": "BlockMenu plugin for @atlaskit/editor-core",
5
5
  "author": "Atlassian Pty Ltd",
6
6
  "license": "Apache-2.0",
@@ -31,25 +31,25 @@
31
31
  "@atlaskit/css": "^0.17.0",
32
32
  "@atlaskit/dropdown-menu": "^16.3.0",
33
33
  "@atlaskit/editor-plugin-analytics": "^6.2.0",
34
- "@atlaskit/editor-plugin-block-controls": "^7.13.0",
34
+ "@atlaskit/editor-plugin-block-controls": "^7.14.0",
35
35
  "@atlaskit/editor-plugin-decorations": "^6.1.0",
36
36
  "@atlaskit/editor-plugin-selection": "^6.1.0",
37
37
  "@atlaskit/editor-plugin-user-intent": "^4.0.0",
38
- "@atlaskit/editor-prosemirror": "7.0.0",
38
+ "@atlaskit/editor-prosemirror": "^7.2.0",
39
39
  "@atlaskit/editor-shared-styles": "^3.10.0",
40
40
  "@atlaskit/editor-tables": "^2.9.0",
41
41
  "@atlaskit/editor-toolbar": "^0.18.0",
42
42
  "@atlaskit/flag": "^17.6.0",
43
- "@atlaskit/icon": "^29.0.0",
43
+ "@atlaskit/icon": "^29.1.0",
44
44
  "@atlaskit/platform-feature-flags": "^1.1.0",
45
45
  "@atlaskit/platform-feature-flags-react": "^0.4.0",
46
46
  "@atlaskit/primitives": "^16.4.0",
47
- "@atlaskit/tmp-editor-statsig": "^15.9.0",
47
+ "@atlaskit/tmp-editor-statsig": "^15.10.0",
48
48
  "@atlaskit/tokens": "^8.4.0",
49
49
  "@babel/runtime": "^7.0.0"
50
50
  },
51
51
  "peerDependencies": {
52
- "@atlaskit/editor-common": "^110.41.0",
52
+ "@atlaskit/editor-common": "^110.42.0",
53
53
  "react": "^18.2.0",
54
54
  "react-intl-next": "npm:react-intl@^5.18.1"
55
55
  },