@atlaskit/editor-plugin-synced-block 4.2.1 → 4.2.3

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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,21 @@
1
1
  # @atlaskit/editor-plugin-synced-block
2
2
 
3
+ ## 4.2.3
4
+
5
+ ### Patch Changes
6
+
7
+ - [`9aaeaa5054b55`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/9aaeaa5054b55) -
8
+ EDITOR-2863 Add copy sync block option to block menu
9
+ - Updated dependencies
10
+
11
+ ## 4.2.2
12
+
13
+ ### Patch Changes
14
+
15
+ - [`3432c4a4a074c`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/3432c4a4a074c) -
16
+ [ux] EDITOR-2525 update block menu convert to sync block to support all fishfooding node types
17
+ - Updated dependencies
18
+
3
19
  ## 4.2.1
4
20
 
5
21
  ### Patch Changes
@@ -3,9 +3,10 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.removeSyncedBlock = exports.editSyncedBlockSource = exports.createSyncedBlock = exports.copySyncedBlockReferenceToClipboard = void 0;
6
+ exports.removeSyncedBlock = exports.editSyncedBlockSource = exports.createSyncedBlock = exports.copySyncedBlockReferenceToClipboardEditorCommand = exports.copySyncedBlockReferenceToClipboard = void 0;
7
7
  var _analytics = require("@atlaskit/editor-common/analytics");
8
8
  var _copyButton = require("@atlaskit/editor-common/copy-button");
9
+ var _state = require("@atlaskit/editor-prosemirror/state");
9
10
  var _utils = require("@atlaskit/editor-prosemirror/utils");
10
11
  var _utils2 = require("../pm-plugins/utils/utils");
11
12
  var createSyncedBlock = exports.createSyncedBlock = function createSyncedBlock(_ref) {
@@ -48,49 +49,57 @@ var createSyncedBlock = exports.createSyncedBlock = function createSyncedBlock(_
48
49
  // Save the new node with empty content to backend
49
50
  // This is so that the node can be copied and referenced without the source being saved/published
50
51
  syncBlockStore.createBodiedSyncBlockNode(_attrs);
51
- tr.replaceWith(conversionInfo.from - 1, conversionInfo.to, _newBodiedSyncBlockNode).scrollIntoView();
52
+ tr.replaceWith(conversionInfo.from > 0 ? conversionInfo.from - 1 : 0, conversionInfo.to, _newBodiedSyncBlockNode).scrollIntoView();
53
+
54
+ // set selection to the end of the previous selection + 1 for the position taken up by the start of the new synced block
55
+ tr.setSelection(_state.TextSelection.create(tr.doc, conversionInfo.to + 1));
52
56
  }
53
57
 
54
58
  // This transaction will be intercepted in filterTransaction and dispatched when saving to backend succeeds
55
59
  // see filterTransaction for more details
56
60
  return tr;
57
61
  };
58
- var copySyncedBlockReferenceToClipboard = exports.copySyncedBlockReferenceToClipboard = function copySyncedBlockReferenceToClipboard(api) {
59
- return function (state, _dispatch, _view) {
60
- if (!(api !== null && api !== void 0 && api.floatingToolbar)) {
61
- return false;
62
- }
63
- var syncBlockFindResult = (0, _utils2.findSyncBlockOrBodiedSyncBlock)(state);
64
- if (!syncBlockFindResult) {
65
- return false;
66
- }
67
- var isBodiedSyncBlock = (0, _utils2.isBodiedSyncBlockNode)(syncBlockFindResult.node, state.schema.nodes.bodiedSyncBlock);
68
- var referenceSyncBlockNode = null;
69
- if (isBodiedSyncBlock) {
70
- var syncBlock = state.tr.doc.type.schema.nodes.syncBlock;
62
+ var copySyncedBlockReferenceToClipboardEditorCommand = exports.copySyncedBlockReferenceToClipboardEditorCommand = function copySyncedBlockReferenceToClipboardEditorCommand(_ref2) {
63
+ var tr = _ref2.tr;
64
+ if (copySyncedBlockReferenceToClipboardInternal(tr.doc.type.schema, tr.selection)) {
65
+ return tr;
66
+ }
67
+ return null;
68
+ };
69
+ var copySyncedBlockReferenceToClipboard = exports.copySyncedBlockReferenceToClipboard = function copySyncedBlockReferenceToClipboard(state, _dispatch, _view) {
70
+ return copySyncedBlockReferenceToClipboardInternal(state.tr.doc.type.schema, state.tr.selection);
71
+ };
72
+ var copySyncedBlockReferenceToClipboardInternal = function copySyncedBlockReferenceToClipboardInternal(schema, selection) {
73
+ var syncBlockFindResult = (0, _utils2.findSyncBlockOrBodiedSyncBlock)(schema, selection);
74
+ if (!syncBlockFindResult) {
75
+ return false;
76
+ }
77
+ var isBodiedSyncBlock = (0, _utils2.isBodiedSyncBlockNode)(syncBlockFindResult.node, schema.nodes.bodiedSyncBlock);
78
+ var referenceSyncBlockNode = null;
79
+ if (isBodiedSyncBlock) {
80
+ var syncBlock = schema.nodes.syncBlock;
71
81
 
72
- // create sync block reference node
73
- referenceSyncBlockNode = syncBlock.createAndFill({
74
- resourceId: syncBlockFindResult.node.attrs.resourceId
75
- });
76
- if (!referenceSyncBlockNode) {
77
- return false;
78
- }
79
- } else {
80
- referenceSyncBlockNode = syncBlockFindResult.node;
81
- }
82
+ // create sync block reference node
83
+ referenceSyncBlockNode = syncBlock.createAndFill({
84
+ resourceId: syncBlockFindResult.node.attrs.resourceId
85
+ });
82
86
  if (!referenceSyncBlockNode) {
83
87
  return false;
84
88
  }
85
- var domNode = (0, _copyButton.toDOM)(referenceSyncBlockNode, state.tr.doc.type.schema);
86
- (0, _copyButton.copyDomNode)(domNode, referenceSyncBlockNode.type, state.tr.selection);
87
- return true;
88
- };
89
+ } else {
90
+ referenceSyncBlockNode = syncBlockFindResult.node;
91
+ }
92
+ if (!referenceSyncBlockNode) {
93
+ return false;
94
+ }
95
+ var domNode = (0, _copyButton.toDOM)(referenceSyncBlockNode, schema);
96
+ (0, _copyButton.copyDomNode)(domNode, referenceSyncBlockNode.type, selection);
97
+ return true;
89
98
  };
90
99
  var editSyncedBlockSource = exports.editSyncedBlockSource = function editSyncedBlockSource(syncBlockStore, api) {
91
100
  return function (state, dispatch, _view) {
92
101
  var _syncBlock$node;
93
- var syncBlock = (0, _utils2.findSyncBlock)(state);
102
+ var syncBlock = (0, _utils2.findSyncBlock)(state.schema, state.selection);
94
103
  var resourceId = syncBlock === null || syncBlock === void 0 || (_syncBlock$node = syncBlock.node) === null || _syncBlock$node === void 0 || (_syncBlock$node = _syncBlock$node.attrs) === null || _syncBlock$node === void 0 ? void 0 : _syncBlock$node.resourceId;
95
104
  if (!resourceId) {
96
105
  return false;
@@ -5,28 +5,38 @@ Object.defineProperty(exports, "__esModule", {
5
5
  });
6
6
  exports.isBodiedSyncBlockNode = exports.findSyncBlockOrBodiedSyncBlock = exports.findSyncBlock = exports.findBodiedSyncBlock = exports.canBeConvertedToSyncBlock = void 0;
7
7
  var _model = require("@atlaskit/editor-prosemirror/model");
8
+ var _state = require("@atlaskit/editor-prosemirror/state");
8
9
  var _utils = require("@atlaskit/editor-prosemirror/utils");
9
- var _editorSyncedBlockProvider = require("@atlaskit/editor-synced-block-provider");
10
10
  var _editorTables = require("@atlaskit/editor-tables");
11
- var findSyncBlock = exports.findSyncBlock = function findSyncBlock(state, selection) {
12
- var syncBlock = state.schema.nodes.syncBlock;
13
- return (0, _utils.findSelectedNodeOfType)(syncBlock)(selection || state.selection);
11
+ var findSyncBlock = exports.findSyncBlock = function findSyncBlock(schema, selection) {
12
+ var syncBlock = schema.nodes.syncBlock;
13
+ return (0, _utils.findSelectedNodeOfType)(syncBlock)(selection);
14
14
  };
15
- var findBodiedSyncBlock = exports.findBodiedSyncBlock = function findBodiedSyncBlock(state, selection) {
16
- var bodiedSyncBlock = state.schema.nodes.bodiedSyncBlock;
17
- return (0, _utils.findSelectedNodeOfType)(bodiedSyncBlock)(selection || state.selection) || (0, _utils.findParentNodeOfType)(bodiedSyncBlock)(selection || state.selection);
15
+ var findBodiedSyncBlock = exports.findBodiedSyncBlock = function findBodiedSyncBlock(schema, selection) {
16
+ return (0, _utils.findSelectedNodeOfType)(schema.nodes.bodiedSyncBlock)(selection) || (0, _utils.findParentNodeOfType)(schema.nodes.bodiedSyncBlock)(selection);
18
17
  };
19
- var findSyncBlockOrBodiedSyncBlock = exports.findSyncBlockOrBodiedSyncBlock = function findSyncBlockOrBodiedSyncBlock(state, selection) {
20
- return findSyncBlock(state, selection) || findBodiedSyncBlock(state, selection);
18
+ var findSyncBlockOrBodiedSyncBlock = exports.findSyncBlockOrBodiedSyncBlock = function findSyncBlockOrBodiedSyncBlock(schema, selection) {
19
+ return findSyncBlock(schema, selection) || findBodiedSyncBlock(schema, selection);
21
20
  };
22
21
  var isBodiedSyncBlockNode = exports.isBodiedSyncBlockNode = function isBodiedSyncBlockNode(node, bodiedSyncBlock) {
23
22
  return node.type === bodiedSyncBlock;
24
23
  };
24
+ var UNSUPPORTED_NODE_TYPES = new Set(['inlineExtension', 'extension', 'bodiedExtension', 'syncBlock', 'bodiedSyncBlock']);
25
+
26
+ /**
27
+ * Checks whether the selection can be converted to sync block
28
+ *
29
+ * @param selection - the current editor selection to validate for sync block conversion
30
+ * @returns A fragment containing the content to include in the synced block,
31
+ * stripping out unsupported marks (breakout on codeblock/expand/layout), as well as from and to positions,
32
+ * or false if conversion is not possible
33
+ */
25
34
  var canBeConvertedToSyncBlock = exports.canBeConvertedToSyncBlock = function canBeConvertedToSyncBlock(selection) {
35
+ var schema = selection.$from.doc.type.schema;
36
+ var nodes = schema.nodes;
26
37
  var from = selection.from;
27
38
  var to = selection.to;
28
- var depth = selection.$from.depth;
29
- var contentToInclude;
39
+ var contentToInclude = selection.content().content;
30
40
  if (selection instanceof _editorTables.CellSelection) {
31
41
  var table = (0, _editorTables.findTable)(selection);
32
42
  if (!table) {
@@ -35,35 +45,41 @@ var canBeConvertedToSyncBlock = exports.canBeConvertedToSyncBlock = function can
35
45
  contentToInclude = _model.Fragment.from([table.node]);
36
46
  from = table.pos;
37
47
  to = table.pos + table.node.nodeSize;
38
- depth = selection.$from.doc.resolve(table.pos).depth;
39
- } else {
40
- contentToInclude = _model.Fragment.from(selection.content().content);
41
- }
42
-
43
- // sync blocks can't be nested
44
- if (depth > 1) {
45
- return false;
48
+ } else if (selection instanceof _state.TextSelection) {
49
+ var trueParent = (0, _utils.findParentNodeOfType)([nodes.bulletList, nodes.orderedList, nodes.taskList, nodes.blockquote])(selection);
50
+ if (trueParent) {
51
+ contentToInclude = _model.Fragment.from([trueParent.node]);
52
+ from = trueParent.pos;
53
+ to = trueParent.pos + trueParent.node.nodeSize;
54
+ }
46
55
  }
47
- var syncBlockSchema = (0, _editorSyncedBlockProvider.getDefaultSyncBlockSchema)();
48
56
  var canBeConverted = true;
49
57
  selection.$from.doc.nodesBetween(from, to, function (node) {
50
- if (!(node.type.name in syncBlockSchema.nodes)) {
58
+ if (UNSUPPORTED_NODE_TYPES.has(node.type.name)) {
51
59
  canBeConverted = false;
52
60
  return false;
53
61
  }
54
- node.marks.forEach(function (mark) {
55
- if (!(mark.type.name in syncBlockSchema.marks)) {
56
- canBeConverted = false;
57
- return false;
58
- }
59
- });
60
62
  });
61
63
  if (!canBeConverted) {
62
64
  return false;
63
65
  }
66
+ contentToInclude = removeBreakoutMarks(contentToInclude);
64
67
  return {
65
68
  contentToInclude: contentToInclude,
66
69
  from: from,
67
70
  to: to
68
71
  };
72
+ };
73
+ var removeBreakoutMarks = function removeBreakoutMarks(content) {
74
+ var nodes = [];
75
+
76
+ // we only need to recurse at the top level, because breakout has to be on a top level
77
+ content.forEach(function (node) {
78
+ var filteredMarks = node.marks.filter(function (mark) {
79
+ return mark.type.name !== 'breakout';
80
+ });
81
+ var newNode = node.type.create(node.attrs, node.content, filteredMarks);
82
+ nodes.push(newNode);
83
+ });
84
+ return _model.Fragment.from(nodes);
69
85
  };
@@ -45,6 +45,9 @@ var syncedBlockPlugin = exports.syncedBlockPlugin = function syncedBlockPlugin(_
45
45
  }];
46
46
  },
47
47
  commands: {
48
+ copySyncedBlockReferenceToClipboard: function copySyncedBlockReferenceToClipboard() {
49
+ return _editorCommands.copySyncedBlockReferenceToClipboardEditorCommand;
50
+ },
48
51
  insertSyncedBlock: function insertSyncedBlock() {
49
52
  return function (_ref2) {
50
53
  var tr = _ref2.tr;
@@ -1,25 +1,38 @@
1
1
  "use strict";
2
2
 
3
3
  var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4
+ var _typeof = require("@babel/runtime/helpers/typeof");
4
5
  Object.defineProperty(exports, "__esModule", {
5
6
  value: true
6
7
  });
7
- exports.CreateSyncedBlockDropdownItem = void 0;
8
- var _react = _interopRequireDefault(require("react"));
8
+ exports.CreateOrCopySyncedBlockDropdownItem = void 0;
9
+ var _react = _interopRequireWildcard(require("react"));
9
10
  var _reactIntlNext = require("react-intl-next");
11
+ var _hooks = require("@atlaskit/editor-common/hooks");
10
12
  var _messages = require("@atlaskit/editor-common/messages");
11
13
  var _editorToolbar = require("@atlaskit/editor-toolbar");
12
14
  var _lozenge = _interopRequireDefault(require("@atlaskit/lozenge"));
13
15
  var _compiled = require("@atlaskit/primitives/compiled");
14
16
  var _utils = require("../pm-plugins/utils/utils");
15
- var CreateSyncedBlockDropdownItem = exports.CreateSyncedBlockDropdownItem = function CreateSyncedBlockDropdownItem(_ref) {
16
- var _api$selection;
17
+ 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); }
18
+ var CreateSyncedBlockDropdownItem = function CreateSyncedBlockDropdownItem(_ref) {
17
19
  var api = _ref.api;
18
20
  var _useIntl = (0, _reactIntlNext.useIntl)(),
19
21
  formatMessage = _useIntl.formatMessage;
20
- 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;
21
- var canCreateSyncBlock = selection && (0, _utils.canBeConvertedToSyncBlock)(selection);
22
- if (!canCreateSyncBlock) {
22
+ var _useSharedPluginState = (0, _hooks.useSharedPluginStateWithSelector)(api, ['selection', 'blockControls'], function (states) {
23
+ var _states$selectionStat, _states$blockControls;
24
+ return {
25
+ selection: (_states$selectionStat = states.selectionState) === null || _states$selectionStat === void 0 ? void 0 : _states$selectionStat.selection,
26
+ activeNode: (_states$blockControls = states.blockControlsState) === null || _states$blockControls === void 0 ? void 0 : _states$blockControls.activeNode
27
+ };
28
+ }),
29
+ selection = _useSharedPluginState.selection,
30
+ activeNode = _useSharedPluginState.activeNode;
31
+ var isNested = activeNode && activeNode.rootPos !== activeNode.pos;
32
+ var canBeConverted = (0, _react.useMemo)(function () {
33
+ return selection && (0, _utils.canBeConvertedToSyncBlock)(selection);
34
+ }, [selection]);
35
+ if (isNested || !canBeConverted) {
23
36
  return null;
24
37
  }
25
38
  var onClick = function onClick() {
@@ -37,7 +50,49 @@ var CreateSyncedBlockDropdownItem = exports.CreateSyncedBlockDropdownItem = func
37
50
  }, /*#__PURE__*/_react.default.createElement(_compiled.Flex, {
38
51
  alignItems: "center",
39
52
  gap: "space.050"
40
- }, /*#__PURE__*/_react.default.createElement(_compiled.Text, null, selection !== null && selection !== void 0 && selection.empty ? formatMessage(_messages.blockMenuMessages.createSyncedBlock) : formatMessage(_messages.blockMenuMessages.convertToSyncedBlock)), /*#__PURE__*/_react.default.createElement(_lozenge.default, {
53
+ }, /*#__PURE__*/_react.default.createElement(_compiled.Text, null, formatMessage(_messages.blockMenuMessages.createSyncedBlock)), /*#__PURE__*/_react.default.createElement(_lozenge.default, {
41
54
  appearance: "new"
42
55
  }, formatMessage(_messages.blockMenuMessages.newLozenge))));
56
+ };
57
+ var CopySyncedBlockDropdownItem = function CopySyncedBlockDropdownItem(_ref2) {
58
+ var api = _ref2.api;
59
+ var _useIntl2 = (0, _reactIntlNext.useIntl)(),
60
+ formatMessage = _useIntl2.formatMessage;
61
+ var onClick = function onClick() {
62
+ var _api$core3, _api$core4, _api$blockControls2;
63
+ api === null || api === void 0 || (_api$core3 = api.core) === null || _api$core3 === void 0 || _api$core3.actions.execute(api === null || api === void 0 ? void 0 : api.syncedBlock.commands.copySyncedBlockReferenceToClipboard());
64
+ api === null || api === void 0 || (_api$core4 = api.core) === null || _api$core4 === void 0 || _api$core4.actions.execute(api === null || api === void 0 || (_api$blockControls2 = api.blockControls) === null || _api$blockControls2 === void 0 || (_api$blockControls2 = _api$blockControls2.commands) === null || _api$blockControls2 === void 0 ? void 0 : _api$blockControls2.toggleBlockMenu({
65
+ closeMenu: true
66
+ }));
67
+ };
68
+ return /*#__PURE__*/_react.default.createElement(_editorToolbar.ToolbarDropdownItem, {
69
+ elemBefore: /*#__PURE__*/_react.default.createElement(_editorToolbar.SyncBlocksIcon, {
70
+ label: ""
71
+ }),
72
+ onClick: onClick
73
+ }, /*#__PURE__*/_react.default.createElement(_compiled.Flex, {
74
+ alignItems: "center",
75
+ gap: "space.050"
76
+ }, /*#__PURE__*/_react.default.createElement(_compiled.Text, null, formatMessage(_messages.blockMenuMessages.copySyncedBlock)), /*#__PURE__*/_react.default.createElement(_lozenge.default, {
77
+ appearance: "new"
78
+ }, formatMessage(_messages.blockMenuMessages.newLozenge))));
79
+ };
80
+ var CreateOrCopySyncedBlockDropdownItem = exports.CreateOrCopySyncedBlockDropdownItem = function CreateOrCopySyncedBlockDropdownItem(_ref3) {
81
+ var api = _ref3.api;
82
+ var _useSharedPluginState2 = (0, _hooks.useSharedPluginStateWithSelector)(api, ['blockControls'], function (states) {
83
+ var _states$blockControls2;
84
+ return {
85
+ menuTriggerBy: (_states$blockControls2 = states.blockControlsState) === null || _states$blockControls2 === void 0 ? void 0 : _states$blockControls2.menuTriggerBy
86
+ };
87
+ }),
88
+ menuTriggerBy = _useSharedPluginState2.menuTriggerBy;
89
+ if (menuTriggerBy === 'syncBlock' || menuTriggerBy === 'bodiedSyncBlock') {
90
+ return /*#__PURE__*/_react.default.createElement(CopySyncedBlockDropdownItem, {
91
+ api: api
92
+ });
93
+ } else {
94
+ return /*#__PURE__*/_react.default.createElement(CreateSyncedBlockDropdownItem, {
95
+ api: api
96
+ });
97
+ }
43
98
  };
@@ -18,7 +18,7 @@ var getBlockMenuComponents = exports.getBlockMenuComponents = function getBlockM
18
18
  rank: _blockMenu.ADD_BLOCKS_MENU_SECTION_RANK[_blockMenu.CREATE_SYNCED_BLOCK_MENU_ITEM.key]
19
19
  },
20
20
  component: function component() {
21
- return /*#__PURE__*/_react.default.createElement(_CreateSyncedBlockDropdownItem.CreateSyncedBlockDropdownItem, {
21
+ return /*#__PURE__*/_react.default.createElement(_CreateSyncedBlockDropdownItem.CreateOrCopySyncedBlockDropdownItem, {
22
22
  api: api
23
23
  });
24
24
  }
@@ -21,7 +21,7 @@ function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbol
21
21
  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) { (0, _defineProperty2.default)(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; }
22
22
  var getToolbarConfig = exports.getToolbarConfig = function getToolbarConfig(state, intl, api, syncBlockStore) {
23
23
  var _api$decorations;
24
- var syncBlockObject = (0, _utils2.findSyncBlockOrBodiedSyncBlock)(state);
24
+ var syncBlockObject = (0, _utils2.findSyncBlockOrBodiedSyncBlock)(state.schema, state.selection);
25
25
  if (!syncBlockObject) {
26
26
  return;
27
27
  }
@@ -47,7 +47,7 @@ var getToolbarConfig = exports.getToolbarConfig = function getToolbarConfig(stat
47
47
  title: formatMessage(_messages.syncBlockMessages.copySyncBlockLabel),
48
48
  showTitle: false,
49
49
  tooltipContent: formatMessage(_messages.syncBlockMessages.copySyncBlockTooltip),
50
- onClick: (0, _editorCommands.copySyncedBlockReferenceToClipboard)(api)
50
+ onClick: _editorCommands.copySyncedBlockReferenceToClipboard
51
51
  }, hoverDecorationProps(nodeType, _consts.akEditorSelectedNodeClassName));
52
52
  items.push(copyButton);
53
53
  var disabled = !syncBlockStore.getSyncBlockURL(syncBlockObject.node.attrs.resourceId);
@@ -1,5 +1,6 @@
1
1
  import { ACTION, ACTION_SUBJECT, ACTION_SUBJECT_ID, EVENT_TYPE } from '@atlaskit/editor-common/analytics';
2
2
  import { copyDomNode, toDOM } from '@atlaskit/editor-common/copy-button';
3
+ import { TextSelection } from '@atlaskit/editor-prosemirror/state';
3
4
  import { findSelectedNodeOfType, removeParentNodeOfType, removeSelectedNode } from '@atlaskit/editor-prosemirror/utils';
4
5
  import { canBeConvertedToSyncBlock, findSyncBlock, findSyncBlockOrBodiedSyncBlock, isBodiedSyncBlockNode } from '../pm-plugins/utils/utils';
5
6
  export const createSyncedBlock = ({
@@ -48,31 +49,40 @@ export const createSyncedBlock = ({
48
49
  // Save the new node with empty content to backend
49
50
  // This is so that the node can be copied and referenced without the source being saved/published
50
51
  syncBlockStore.createBodiedSyncBlockNode(attrs);
51
- tr.replaceWith(conversionInfo.from - 1, conversionInfo.to, newBodiedSyncBlockNode).scrollIntoView();
52
+ tr.replaceWith(conversionInfo.from > 0 ? conversionInfo.from - 1 : 0, conversionInfo.to, newBodiedSyncBlockNode).scrollIntoView();
53
+
54
+ // set selection to the end of the previous selection + 1 for the position taken up by the start of the new synced block
55
+ tr.setSelection(TextSelection.create(tr.doc, conversionInfo.to + 1));
52
56
  }
53
57
 
54
58
  // This transaction will be intercepted in filterTransaction and dispatched when saving to backend succeeds
55
59
  // see filterTransaction for more details
56
60
  return tr;
57
61
  };
58
- export const copySyncedBlockReferenceToClipboard = api => (state, _dispatch, _view) => {
59
- if (!(api !== null && api !== void 0 && api.floatingToolbar)) {
60
- return false;
62
+ export const copySyncedBlockReferenceToClipboardEditorCommand = ({
63
+ tr
64
+ }) => {
65
+ if (copySyncedBlockReferenceToClipboardInternal(tr.doc.type.schema, tr.selection)) {
66
+ return tr;
61
67
  }
62
- const syncBlockFindResult = findSyncBlockOrBodiedSyncBlock(state);
68
+ return null;
69
+ };
70
+ export const copySyncedBlockReferenceToClipboard = (state, _dispatch, _view) => {
71
+ return copySyncedBlockReferenceToClipboardInternal(state.tr.doc.type.schema, state.tr.selection);
72
+ };
73
+ const copySyncedBlockReferenceToClipboardInternal = (schema, selection) => {
74
+ const syncBlockFindResult = findSyncBlockOrBodiedSyncBlock(schema, selection);
63
75
  if (!syncBlockFindResult) {
64
76
  return false;
65
77
  }
66
- const isBodiedSyncBlock = isBodiedSyncBlockNode(syncBlockFindResult.node, state.schema.nodes.bodiedSyncBlock);
78
+ const isBodiedSyncBlock = isBodiedSyncBlockNode(syncBlockFindResult.node, schema.nodes.bodiedSyncBlock);
67
79
  let referenceSyncBlockNode = null;
68
80
  if (isBodiedSyncBlock) {
69
81
  const {
70
- schema: {
71
- nodes: {
72
- syncBlock
73
- }
82
+ nodes: {
83
+ syncBlock
74
84
  }
75
- } = state.tr.doc.type;
85
+ } = schema;
76
86
 
77
87
  // create sync block reference node
78
88
  referenceSyncBlockNode = syncBlock.createAndFill({
@@ -87,13 +97,13 @@ export const copySyncedBlockReferenceToClipboard = api => (state, _dispatch, _vi
87
97
  if (!referenceSyncBlockNode) {
88
98
  return false;
89
99
  }
90
- const domNode = toDOM(referenceSyncBlockNode, state.tr.doc.type.schema);
91
- copyDomNode(domNode, referenceSyncBlockNode.type, state.tr.selection);
100
+ const domNode = toDOM(referenceSyncBlockNode, schema);
101
+ copyDomNode(domNode, referenceSyncBlockNode.type, selection);
92
102
  return true;
93
103
  };
94
104
  export const editSyncedBlockSource = (syncBlockStore, api) => (state, dispatch, _view) => {
95
105
  var _syncBlock$node, _syncBlock$node$attrs;
96
- const syncBlock = findSyncBlock(state);
106
+ const syncBlock = findSyncBlock(state.schema, state.selection);
97
107
  const resourceId = syncBlock === null || syncBlock === void 0 ? void 0 : (_syncBlock$node = syncBlock.node) === null || _syncBlock$node === void 0 ? void 0 : (_syncBlock$node$attrs = _syncBlock$node.attrs) === null || _syncBlock$node$attrs === void 0 ? void 0 : _syncBlock$node$attrs.resourceId;
98
108
  if (!resourceId) {
99
109
  return false;
@@ -1,28 +1,38 @@
1
1
  import { Fragment } from '@atlaskit/editor-prosemirror/model';
2
+ import { TextSelection } from '@atlaskit/editor-prosemirror/state';
2
3
  import { findParentNodeOfType, findSelectedNodeOfType } from '@atlaskit/editor-prosemirror/utils';
3
- import { getDefaultSyncBlockSchema } from '@atlaskit/editor-synced-block-provider';
4
4
  import { CellSelection, findTable } from '@atlaskit/editor-tables';
5
- export const findSyncBlock = (state, selection) => {
5
+ export const findSyncBlock = (schema, selection) => {
6
6
  const {
7
7
  syncBlock
8
- } = state.schema.nodes;
9
- return findSelectedNodeOfType(syncBlock)(selection || state.selection);
8
+ } = schema.nodes;
9
+ return findSelectedNodeOfType(syncBlock)(selection);
10
10
  };
11
- export const findBodiedSyncBlock = (state, selection) => {
12
- const {
13
- bodiedSyncBlock
14
- } = state.schema.nodes;
15
- return findSelectedNodeOfType(bodiedSyncBlock)(selection || state.selection) || findParentNodeOfType(bodiedSyncBlock)(selection || state.selection);
11
+ export const findBodiedSyncBlock = (schema, selection) => {
12
+ return findSelectedNodeOfType(schema.nodes.bodiedSyncBlock)(selection) || findParentNodeOfType(schema.nodes.bodiedSyncBlock)(selection);
16
13
  };
17
- export const findSyncBlockOrBodiedSyncBlock = (state, selection) => {
18
- return findSyncBlock(state, selection) || findBodiedSyncBlock(state, selection);
14
+ export const findSyncBlockOrBodiedSyncBlock = (schema, selection) => {
15
+ return findSyncBlock(schema, selection) || findBodiedSyncBlock(schema, selection);
19
16
  };
20
17
  export const isBodiedSyncBlockNode = (node, bodiedSyncBlock) => node.type === bodiedSyncBlock;
18
+ const UNSUPPORTED_NODE_TYPES = new Set(['inlineExtension', 'extension', 'bodiedExtension', 'syncBlock', 'bodiedSyncBlock']);
19
+
20
+ /**
21
+ * Checks whether the selection can be converted to sync block
22
+ *
23
+ * @param selection - the current editor selection to validate for sync block conversion
24
+ * @returns A fragment containing the content to include in the synced block,
25
+ * stripping out unsupported marks (breakout on codeblock/expand/layout), as well as from and to positions,
26
+ * or false if conversion is not possible
27
+ */
21
28
  export const canBeConvertedToSyncBlock = selection => {
29
+ const schema = selection.$from.doc.type.schema;
30
+ const {
31
+ nodes
32
+ } = schema;
22
33
  let from = selection.from;
23
34
  let to = selection.to;
24
- let depth = selection.$from.depth;
25
- let contentToInclude;
35
+ let contentToInclude = selection.content().content;
26
36
  if (selection instanceof CellSelection) {
27
37
  const table = findTable(selection);
28
38
  if (!table) {
@@ -31,35 +41,39 @@ export const canBeConvertedToSyncBlock = selection => {
31
41
  contentToInclude = Fragment.from([table.node]);
32
42
  from = table.pos;
33
43
  to = table.pos + table.node.nodeSize;
34
- depth = selection.$from.doc.resolve(table.pos).depth;
35
- } else {
36
- contentToInclude = Fragment.from(selection.content().content);
37
- }
38
-
39
- // sync blocks can't be nested
40
- if (depth > 1) {
41
- return false;
44
+ } else if (selection instanceof TextSelection) {
45
+ const trueParent = findParentNodeOfType([nodes.bulletList, nodes.orderedList, nodes.taskList, nodes.blockquote])(selection);
46
+ if (trueParent) {
47
+ contentToInclude = Fragment.from([trueParent.node]);
48
+ from = trueParent.pos;
49
+ to = trueParent.pos + trueParent.node.nodeSize;
50
+ }
42
51
  }
43
- const syncBlockSchema = getDefaultSyncBlockSchema();
44
52
  let canBeConverted = true;
45
53
  selection.$from.doc.nodesBetween(from, to, node => {
46
- if (!(node.type.name in syncBlockSchema.nodes)) {
54
+ if (UNSUPPORTED_NODE_TYPES.has(node.type.name)) {
47
55
  canBeConverted = false;
48
56
  return false;
49
57
  }
50
- node.marks.forEach(mark => {
51
- if (!(mark.type.name in syncBlockSchema.marks)) {
52
- canBeConverted = false;
53
- return false;
54
- }
55
- });
56
58
  });
57
59
  if (!canBeConverted) {
58
60
  return false;
59
61
  }
62
+ contentToInclude = removeBreakoutMarks(contentToInclude);
60
63
  return {
61
64
  contentToInclude,
62
65
  from,
63
66
  to
64
67
  };
68
+ };
69
+ const removeBreakoutMarks = content => {
70
+ const nodes = [];
71
+
72
+ // we only need to recurse at the top level, because breakout has to be on a top level
73
+ content.forEach(node => {
74
+ const filteredMarks = node.marks.filter(mark => mark.type.name !== 'breakout');
75
+ const newNode = node.type.create(node.attrs, node.content, filteredMarks);
76
+ nodes.push(newNode);
77
+ });
78
+ return Fragment.from(nodes);
65
79
  };
@@ -4,7 +4,7 @@ import { blockTypeMessages } from '@atlaskit/editor-common/messages';
4
4
  import { IconSyncBlock } from '@atlaskit/editor-common/quick-insert';
5
5
  import { SyncBlockStoreManager } from '@atlaskit/editor-synced-block-provider';
6
6
  import { flushBodiedSyncBlocks } from './editor-actions';
7
- import { createSyncedBlock } from './editor-commands';
7
+ import { copySyncedBlockReferenceToClipboardEditorCommand, createSyncedBlock } from './editor-commands';
8
8
  import { createPlugin } from './pm-plugins/main';
9
9
  import { getBlockMenuComponents } from './ui/block-menu-components';
10
10
  import { DeleteConfirmationModal } from './ui/DeleteConfirmationModal';
@@ -37,6 +37,7 @@ export const syncedBlockPlugin = ({
37
37
  }];
38
38
  },
39
39
  commands: {
40
+ copySyncedBlockReferenceToClipboard: () => copySyncedBlockReferenceToClipboardEditorCommand,
40
41
  insertSyncedBlock: () => ({
41
42
  tr
42
43
  }) => createSyncedBlock({
@@ -1,20 +1,30 @@
1
- import React from 'react';
1
+ import React, { useMemo } from 'react';
2
2
  import { useIntl } from 'react-intl-next';
3
+ import { useSharedPluginStateWithSelector } from '@atlaskit/editor-common/hooks';
3
4
  import { blockMenuMessages } from '@atlaskit/editor-common/messages';
4
5
  import { SyncBlocksIcon, ToolbarDropdownItem } from '@atlaskit/editor-toolbar';
5
6
  import Lozenge from '@atlaskit/lozenge';
6
7
  import { Flex, Text } from '@atlaskit/primitives/compiled';
7
8
  import { canBeConvertedToSyncBlock } from '../pm-plugins/utils/utils';
8
- export const CreateSyncedBlockDropdownItem = ({
9
+ const CreateSyncedBlockDropdownItem = ({
9
10
  api
10
11
  }) => {
11
- var _api$selection, _api$selection$shared, _api$selection$shared2;
12
12
  const {
13
13
  formatMessage
14
14
  } = useIntl();
15
- const selection = api === null || api === void 0 ? void 0 : (_api$selection = api.selection) === null || _api$selection === void 0 ? void 0 : (_api$selection$shared = _api$selection.sharedState) === null || _api$selection$shared === void 0 ? void 0 : (_api$selection$shared2 = _api$selection$shared.currentState()) === null || _api$selection$shared2 === void 0 ? void 0 : _api$selection$shared2.selection;
16
- const canCreateSyncBlock = selection && canBeConvertedToSyncBlock(selection);
17
- if (!canCreateSyncBlock) {
15
+ const {
16
+ selection,
17
+ activeNode
18
+ } = useSharedPluginStateWithSelector(api, ['selection', 'blockControls'], states => {
19
+ var _states$selectionStat, _states$blockControls;
20
+ return {
21
+ selection: (_states$selectionStat = states.selectionState) === null || _states$selectionStat === void 0 ? void 0 : _states$selectionStat.selection,
22
+ activeNode: (_states$blockControls = states.blockControlsState) === null || _states$blockControls === void 0 ? void 0 : _states$blockControls.activeNode
23
+ };
24
+ });
25
+ const isNested = activeNode && activeNode.rootPos !== activeNode.pos;
26
+ const canBeConverted = useMemo(() => selection && canBeConvertedToSyncBlock(selection), [selection]);
27
+ if (isNested || !canBeConverted) {
18
28
  return null;
19
29
  }
20
30
  const onClick = () => {
@@ -32,7 +42,53 @@ export const CreateSyncedBlockDropdownItem = ({
32
42
  }, /*#__PURE__*/React.createElement(Flex, {
33
43
  alignItems: "center",
34
44
  gap: "space.050"
35
- }, /*#__PURE__*/React.createElement(Text, null, selection !== null && selection !== void 0 && selection.empty ? formatMessage(blockMenuMessages.createSyncedBlock) : formatMessage(blockMenuMessages.convertToSyncedBlock)), /*#__PURE__*/React.createElement(Lozenge, {
45
+ }, /*#__PURE__*/React.createElement(Text, null, formatMessage(blockMenuMessages.createSyncedBlock)), /*#__PURE__*/React.createElement(Lozenge, {
46
+ appearance: "new"
47
+ }, formatMessage(blockMenuMessages.newLozenge))));
48
+ };
49
+ const CopySyncedBlockDropdownItem = ({
50
+ api
51
+ }) => {
52
+ const {
53
+ formatMessage
54
+ } = useIntl();
55
+ const onClick = () => {
56
+ var _api$core3, _api$core4, _api$blockControls2, _api$blockControls2$c;
57
+ api === null || api === void 0 ? void 0 : (_api$core3 = api.core) === null || _api$core3 === void 0 ? void 0 : _api$core3.actions.execute(api === null || api === void 0 ? void 0 : api.syncedBlock.commands.copySyncedBlockReferenceToClipboard());
58
+ api === null || api === void 0 ? void 0 : (_api$core4 = api.core) === null || _api$core4 === void 0 ? void 0 : _api$core4.actions.execute(api === null || api === void 0 ? void 0 : (_api$blockControls2 = api.blockControls) === null || _api$blockControls2 === void 0 ? void 0 : (_api$blockControls2$c = _api$blockControls2.commands) === null || _api$blockControls2$c === void 0 ? void 0 : _api$blockControls2$c.toggleBlockMenu({
59
+ closeMenu: true
60
+ }));
61
+ };
62
+ return /*#__PURE__*/React.createElement(ToolbarDropdownItem, {
63
+ elemBefore: /*#__PURE__*/React.createElement(SyncBlocksIcon, {
64
+ label: ""
65
+ }),
66
+ onClick: onClick
67
+ }, /*#__PURE__*/React.createElement(Flex, {
68
+ alignItems: "center",
69
+ gap: "space.050"
70
+ }, /*#__PURE__*/React.createElement(Text, null, formatMessage(blockMenuMessages.copySyncedBlock)), /*#__PURE__*/React.createElement(Lozenge, {
36
71
  appearance: "new"
37
72
  }, formatMessage(blockMenuMessages.newLozenge))));
73
+ };
74
+ export const CreateOrCopySyncedBlockDropdownItem = ({
75
+ api
76
+ }) => {
77
+ const {
78
+ menuTriggerBy
79
+ } = useSharedPluginStateWithSelector(api, ['blockControls'], states => {
80
+ var _states$blockControls2;
81
+ return {
82
+ menuTriggerBy: (_states$blockControls2 = states.blockControlsState) === null || _states$blockControls2 === void 0 ? void 0 : _states$blockControls2.menuTriggerBy
83
+ };
84
+ });
85
+ if (menuTriggerBy === 'syncBlock' || menuTriggerBy === 'bodiedSyncBlock') {
86
+ return /*#__PURE__*/React.createElement(CopySyncedBlockDropdownItem, {
87
+ api: api
88
+ });
89
+ } else {
90
+ return /*#__PURE__*/React.createElement(CreateSyncedBlockDropdownItem, {
91
+ api: api
92
+ });
93
+ }
38
94
  };
@@ -1,6 +1,6 @@
1
1
  import React from 'react';
2
2
  import { ADD_BLOCKS_MENU_SECTION, ADD_BLOCKS_MENU_SECTION_RANK, CREATE_SYNCED_BLOCK_MENU_ITEM } from '@atlaskit/editor-common/block-menu';
3
- import { CreateSyncedBlockDropdownItem } from './CreateSyncedBlockDropdownItem';
3
+ import { CreateOrCopySyncedBlockDropdownItem } from './CreateSyncedBlockDropdownItem';
4
4
  export const getBlockMenuComponents = api => {
5
5
  return [{
6
6
  type: 'block-menu-item',
@@ -10,7 +10,7 @@ export const getBlockMenuComponents = api => {
10
10
  key: ADD_BLOCKS_MENU_SECTION.key,
11
11
  rank: ADD_BLOCKS_MENU_SECTION_RANK[CREATE_SYNCED_BLOCK_MENU_ITEM.key]
12
12
  },
13
- component: () => /*#__PURE__*/React.createElement(CreateSyncedBlockDropdownItem, {
13
+ component: () => /*#__PURE__*/React.createElement(CreateOrCopySyncedBlockDropdownItem, {
14
14
  api: api
15
15
  })
16
16
  }];
@@ -9,7 +9,7 @@ import { copySyncedBlockReferenceToClipboard, editSyncedBlockSource, removeSynce
9
9
  import { findSyncBlockOrBodiedSyncBlock, isBodiedSyncBlockNode } from '../pm-plugins/utils/utils';
10
10
  export const getToolbarConfig = (state, intl, api, syncBlockStore) => {
11
11
  var _api$decorations;
12
- const syncBlockObject = findSyncBlockOrBodiedSyncBlock(state);
12
+ const syncBlockObject = findSyncBlockOrBodiedSyncBlock(state.schema, state.selection);
13
13
  if (!syncBlockObject) {
14
14
  return;
15
15
  }
@@ -41,7 +41,7 @@ export const getToolbarConfig = (state, intl, api, syncBlockStore) => {
41
41
  title: formatMessage(messages.copySyncBlockLabel),
42
42
  showTitle: false,
43
43
  tooltipContent: formatMessage(messages.copySyncBlockTooltip),
44
- onClick: copySyncedBlockReferenceToClipboard(api),
44
+ onClick: copySyncedBlockReferenceToClipboard,
45
45
  ...hoverDecorationProps(nodeType, akEditorSelectedNodeClassName)
46
46
  };
47
47
  items.push(copyButton);
@@ -1,5 +1,6 @@
1
1
  import { ACTION, ACTION_SUBJECT, ACTION_SUBJECT_ID, EVENT_TYPE } from '@atlaskit/editor-common/analytics';
2
2
  import { copyDomNode, toDOM } from '@atlaskit/editor-common/copy-button';
3
+ import { TextSelection } from '@atlaskit/editor-prosemirror/state';
3
4
  import { findSelectedNodeOfType, removeParentNodeOfType, removeSelectedNode } from '@atlaskit/editor-prosemirror/utils';
4
5
  import { canBeConvertedToSyncBlock, findSyncBlock, findSyncBlockOrBodiedSyncBlock, isBodiedSyncBlockNode } from '../pm-plugins/utils/utils';
5
6
  export var createSyncedBlock = function createSyncedBlock(_ref) {
@@ -42,49 +43,57 @@ export var createSyncedBlock = function createSyncedBlock(_ref) {
42
43
  // Save the new node with empty content to backend
43
44
  // This is so that the node can be copied and referenced without the source being saved/published
44
45
  syncBlockStore.createBodiedSyncBlockNode(_attrs);
45
- tr.replaceWith(conversionInfo.from - 1, conversionInfo.to, _newBodiedSyncBlockNode).scrollIntoView();
46
+ tr.replaceWith(conversionInfo.from > 0 ? conversionInfo.from - 1 : 0, conversionInfo.to, _newBodiedSyncBlockNode).scrollIntoView();
47
+
48
+ // set selection to the end of the previous selection + 1 for the position taken up by the start of the new synced block
49
+ tr.setSelection(TextSelection.create(tr.doc, conversionInfo.to + 1));
46
50
  }
47
51
 
48
52
  // This transaction will be intercepted in filterTransaction and dispatched when saving to backend succeeds
49
53
  // see filterTransaction for more details
50
54
  return tr;
51
55
  };
52
- export var copySyncedBlockReferenceToClipboard = function copySyncedBlockReferenceToClipboard(api) {
53
- return function (state, _dispatch, _view) {
54
- if (!(api !== null && api !== void 0 && api.floatingToolbar)) {
55
- return false;
56
- }
57
- var syncBlockFindResult = findSyncBlockOrBodiedSyncBlock(state);
58
- if (!syncBlockFindResult) {
59
- return false;
60
- }
61
- var isBodiedSyncBlock = isBodiedSyncBlockNode(syncBlockFindResult.node, state.schema.nodes.bodiedSyncBlock);
62
- var referenceSyncBlockNode = null;
63
- if (isBodiedSyncBlock) {
64
- var syncBlock = state.tr.doc.type.schema.nodes.syncBlock;
56
+ export var copySyncedBlockReferenceToClipboardEditorCommand = function copySyncedBlockReferenceToClipboardEditorCommand(_ref2) {
57
+ var tr = _ref2.tr;
58
+ if (copySyncedBlockReferenceToClipboardInternal(tr.doc.type.schema, tr.selection)) {
59
+ return tr;
60
+ }
61
+ return null;
62
+ };
63
+ export var copySyncedBlockReferenceToClipboard = function copySyncedBlockReferenceToClipboard(state, _dispatch, _view) {
64
+ return copySyncedBlockReferenceToClipboardInternal(state.tr.doc.type.schema, state.tr.selection);
65
+ };
66
+ var copySyncedBlockReferenceToClipboardInternal = function copySyncedBlockReferenceToClipboardInternal(schema, selection) {
67
+ var syncBlockFindResult = findSyncBlockOrBodiedSyncBlock(schema, selection);
68
+ if (!syncBlockFindResult) {
69
+ return false;
70
+ }
71
+ var isBodiedSyncBlock = isBodiedSyncBlockNode(syncBlockFindResult.node, schema.nodes.bodiedSyncBlock);
72
+ var referenceSyncBlockNode = null;
73
+ if (isBodiedSyncBlock) {
74
+ var syncBlock = schema.nodes.syncBlock;
65
75
 
66
- // create sync block reference node
67
- referenceSyncBlockNode = syncBlock.createAndFill({
68
- resourceId: syncBlockFindResult.node.attrs.resourceId
69
- });
70
- if (!referenceSyncBlockNode) {
71
- return false;
72
- }
73
- } else {
74
- referenceSyncBlockNode = syncBlockFindResult.node;
75
- }
76
+ // create sync block reference node
77
+ referenceSyncBlockNode = syncBlock.createAndFill({
78
+ resourceId: syncBlockFindResult.node.attrs.resourceId
79
+ });
76
80
  if (!referenceSyncBlockNode) {
77
81
  return false;
78
82
  }
79
- var domNode = toDOM(referenceSyncBlockNode, state.tr.doc.type.schema);
80
- copyDomNode(domNode, referenceSyncBlockNode.type, state.tr.selection);
81
- return true;
82
- };
83
+ } else {
84
+ referenceSyncBlockNode = syncBlockFindResult.node;
85
+ }
86
+ if (!referenceSyncBlockNode) {
87
+ return false;
88
+ }
89
+ var domNode = toDOM(referenceSyncBlockNode, schema);
90
+ copyDomNode(domNode, referenceSyncBlockNode.type, selection);
91
+ return true;
83
92
  };
84
93
  export var editSyncedBlockSource = function editSyncedBlockSource(syncBlockStore, api) {
85
94
  return function (state, dispatch, _view) {
86
95
  var _syncBlock$node;
87
- var syncBlock = findSyncBlock(state);
96
+ var syncBlock = findSyncBlock(state.schema, state.selection);
88
97
  var resourceId = syncBlock === null || syncBlock === void 0 || (_syncBlock$node = syncBlock.node) === null || _syncBlock$node === void 0 || (_syncBlock$node = _syncBlock$node.attrs) === null || _syncBlock$node === void 0 ? void 0 : _syncBlock$node.resourceId;
89
98
  if (!resourceId) {
90
99
  return false;
@@ -1,26 +1,36 @@
1
1
  import { Fragment } from '@atlaskit/editor-prosemirror/model';
2
+ import { TextSelection } from '@atlaskit/editor-prosemirror/state';
2
3
  import { findParentNodeOfType, findSelectedNodeOfType } from '@atlaskit/editor-prosemirror/utils';
3
- import { getDefaultSyncBlockSchema } from '@atlaskit/editor-synced-block-provider';
4
4
  import { CellSelection, findTable } from '@atlaskit/editor-tables';
5
- export var findSyncBlock = function findSyncBlock(state, selection) {
6
- var syncBlock = state.schema.nodes.syncBlock;
7
- return findSelectedNodeOfType(syncBlock)(selection || state.selection);
5
+ export var findSyncBlock = function findSyncBlock(schema, selection) {
6
+ var syncBlock = schema.nodes.syncBlock;
7
+ return findSelectedNodeOfType(syncBlock)(selection);
8
8
  };
9
- export var findBodiedSyncBlock = function findBodiedSyncBlock(state, selection) {
10
- var bodiedSyncBlock = state.schema.nodes.bodiedSyncBlock;
11
- return findSelectedNodeOfType(bodiedSyncBlock)(selection || state.selection) || findParentNodeOfType(bodiedSyncBlock)(selection || state.selection);
9
+ export var findBodiedSyncBlock = function findBodiedSyncBlock(schema, selection) {
10
+ return findSelectedNodeOfType(schema.nodes.bodiedSyncBlock)(selection) || findParentNodeOfType(schema.nodes.bodiedSyncBlock)(selection);
12
11
  };
13
- export var findSyncBlockOrBodiedSyncBlock = function findSyncBlockOrBodiedSyncBlock(state, selection) {
14
- return findSyncBlock(state, selection) || findBodiedSyncBlock(state, selection);
12
+ export var findSyncBlockOrBodiedSyncBlock = function findSyncBlockOrBodiedSyncBlock(schema, selection) {
13
+ return findSyncBlock(schema, selection) || findBodiedSyncBlock(schema, selection);
15
14
  };
16
15
  export var isBodiedSyncBlockNode = function isBodiedSyncBlockNode(node, bodiedSyncBlock) {
17
16
  return node.type === bodiedSyncBlock;
18
17
  };
18
+ var UNSUPPORTED_NODE_TYPES = new Set(['inlineExtension', 'extension', 'bodiedExtension', 'syncBlock', 'bodiedSyncBlock']);
19
+
20
+ /**
21
+ * Checks whether the selection can be converted to sync block
22
+ *
23
+ * @param selection - the current editor selection to validate for sync block conversion
24
+ * @returns A fragment containing the content to include in the synced block,
25
+ * stripping out unsupported marks (breakout on codeblock/expand/layout), as well as from and to positions,
26
+ * or false if conversion is not possible
27
+ */
19
28
  export var canBeConvertedToSyncBlock = function canBeConvertedToSyncBlock(selection) {
29
+ var schema = selection.$from.doc.type.schema;
30
+ var nodes = schema.nodes;
20
31
  var from = selection.from;
21
32
  var to = selection.to;
22
- var depth = selection.$from.depth;
23
- var contentToInclude;
33
+ var contentToInclude = selection.content().content;
24
34
  if (selection instanceof CellSelection) {
25
35
  var table = findTable(selection);
26
36
  if (!table) {
@@ -29,35 +39,41 @@ export var canBeConvertedToSyncBlock = function canBeConvertedToSyncBlock(select
29
39
  contentToInclude = Fragment.from([table.node]);
30
40
  from = table.pos;
31
41
  to = table.pos + table.node.nodeSize;
32
- depth = selection.$from.doc.resolve(table.pos).depth;
33
- } else {
34
- contentToInclude = Fragment.from(selection.content().content);
35
- }
36
-
37
- // sync blocks can't be nested
38
- if (depth > 1) {
39
- return false;
42
+ } else if (selection instanceof TextSelection) {
43
+ var trueParent = findParentNodeOfType([nodes.bulletList, nodes.orderedList, nodes.taskList, nodes.blockquote])(selection);
44
+ if (trueParent) {
45
+ contentToInclude = Fragment.from([trueParent.node]);
46
+ from = trueParent.pos;
47
+ to = trueParent.pos + trueParent.node.nodeSize;
48
+ }
40
49
  }
41
- var syncBlockSchema = getDefaultSyncBlockSchema();
42
50
  var canBeConverted = true;
43
51
  selection.$from.doc.nodesBetween(from, to, function (node) {
44
- if (!(node.type.name in syncBlockSchema.nodes)) {
52
+ if (UNSUPPORTED_NODE_TYPES.has(node.type.name)) {
45
53
  canBeConverted = false;
46
54
  return false;
47
55
  }
48
- node.marks.forEach(function (mark) {
49
- if (!(mark.type.name in syncBlockSchema.marks)) {
50
- canBeConverted = false;
51
- return false;
52
- }
53
- });
54
56
  });
55
57
  if (!canBeConverted) {
56
58
  return false;
57
59
  }
60
+ contentToInclude = removeBreakoutMarks(contentToInclude);
58
61
  return {
59
62
  contentToInclude: contentToInclude,
60
63
  from: from,
61
64
  to: to
62
65
  };
66
+ };
67
+ var removeBreakoutMarks = function removeBreakoutMarks(content) {
68
+ var nodes = [];
69
+
70
+ // we only need to recurse at the top level, because breakout has to be on a top level
71
+ content.forEach(function (node) {
72
+ var filteredMarks = node.marks.filter(function (mark) {
73
+ return mark.type.name !== 'breakout';
74
+ });
75
+ var newNode = node.type.create(node.attrs, node.content, filteredMarks);
76
+ nodes.push(newNode);
77
+ });
78
+ return Fragment.from(nodes);
63
79
  };
@@ -4,7 +4,7 @@ import { blockTypeMessages } from '@atlaskit/editor-common/messages';
4
4
  import { IconSyncBlock } from '@atlaskit/editor-common/quick-insert';
5
5
  import { SyncBlockStoreManager } from '@atlaskit/editor-synced-block-provider';
6
6
  import { flushBodiedSyncBlocks as _flushBodiedSyncBlocks } from './editor-actions';
7
- import { createSyncedBlock } from './editor-commands';
7
+ import { copySyncedBlockReferenceToClipboardEditorCommand, createSyncedBlock } from './editor-commands';
8
8
  import { createPlugin } from './pm-plugins/main';
9
9
  import { getBlockMenuComponents } from './ui/block-menu-components';
10
10
  import { DeleteConfirmationModal } from './ui/DeleteConfirmationModal';
@@ -38,6 +38,9 @@ export var syncedBlockPlugin = function syncedBlockPlugin(_ref) {
38
38
  }];
39
39
  },
40
40
  commands: {
41
+ copySyncedBlockReferenceToClipboard: function copySyncedBlockReferenceToClipboard() {
42
+ return copySyncedBlockReferenceToClipboardEditorCommand;
43
+ },
41
44
  insertSyncedBlock: function insertSyncedBlock() {
42
45
  return function (_ref2) {
43
46
  var tr = _ref2.tr;
@@ -1,18 +1,29 @@
1
- import React from 'react';
1
+ import React, { useMemo } from 'react';
2
2
  import { useIntl } from 'react-intl-next';
3
+ import { useSharedPluginStateWithSelector } from '@atlaskit/editor-common/hooks';
3
4
  import { blockMenuMessages } from '@atlaskit/editor-common/messages';
4
5
  import { SyncBlocksIcon, ToolbarDropdownItem } from '@atlaskit/editor-toolbar';
5
6
  import Lozenge from '@atlaskit/lozenge';
6
7
  import { Flex, Text } from '@atlaskit/primitives/compiled';
7
8
  import { canBeConvertedToSyncBlock } from '../pm-plugins/utils/utils';
8
- export var CreateSyncedBlockDropdownItem = function CreateSyncedBlockDropdownItem(_ref) {
9
- var _api$selection;
9
+ var CreateSyncedBlockDropdownItem = function CreateSyncedBlockDropdownItem(_ref) {
10
10
  var api = _ref.api;
11
11
  var _useIntl = useIntl(),
12
12
  formatMessage = _useIntl.formatMessage;
13
- 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;
14
- var canCreateSyncBlock = selection && canBeConvertedToSyncBlock(selection);
15
- if (!canCreateSyncBlock) {
13
+ var _useSharedPluginState = useSharedPluginStateWithSelector(api, ['selection', 'blockControls'], function (states) {
14
+ var _states$selectionStat, _states$blockControls;
15
+ return {
16
+ selection: (_states$selectionStat = states.selectionState) === null || _states$selectionStat === void 0 ? void 0 : _states$selectionStat.selection,
17
+ activeNode: (_states$blockControls = states.blockControlsState) === null || _states$blockControls === void 0 ? void 0 : _states$blockControls.activeNode
18
+ };
19
+ }),
20
+ selection = _useSharedPluginState.selection,
21
+ activeNode = _useSharedPluginState.activeNode;
22
+ var isNested = activeNode && activeNode.rootPos !== activeNode.pos;
23
+ var canBeConverted = useMemo(function () {
24
+ return selection && canBeConvertedToSyncBlock(selection);
25
+ }, [selection]);
26
+ if (isNested || !canBeConverted) {
16
27
  return null;
17
28
  }
18
29
  var onClick = function onClick() {
@@ -30,7 +41,49 @@ export var CreateSyncedBlockDropdownItem = function CreateSyncedBlockDropdownIte
30
41
  }, /*#__PURE__*/React.createElement(Flex, {
31
42
  alignItems: "center",
32
43
  gap: "space.050"
33
- }, /*#__PURE__*/React.createElement(Text, null, selection !== null && selection !== void 0 && selection.empty ? formatMessage(blockMenuMessages.createSyncedBlock) : formatMessage(blockMenuMessages.convertToSyncedBlock)), /*#__PURE__*/React.createElement(Lozenge, {
44
+ }, /*#__PURE__*/React.createElement(Text, null, formatMessage(blockMenuMessages.createSyncedBlock)), /*#__PURE__*/React.createElement(Lozenge, {
34
45
  appearance: "new"
35
46
  }, formatMessage(blockMenuMessages.newLozenge))));
47
+ };
48
+ var CopySyncedBlockDropdownItem = function CopySyncedBlockDropdownItem(_ref2) {
49
+ var api = _ref2.api;
50
+ var _useIntl2 = useIntl(),
51
+ formatMessage = _useIntl2.formatMessage;
52
+ var onClick = function onClick() {
53
+ var _api$core3, _api$core4, _api$blockControls2;
54
+ api === null || api === void 0 || (_api$core3 = api.core) === null || _api$core3 === void 0 || _api$core3.actions.execute(api === null || api === void 0 ? void 0 : api.syncedBlock.commands.copySyncedBlockReferenceToClipboard());
55
+ api === null || api === void 0 || (_api$core4 = api.core) === null || _api$core4 === void 0 || _api$core4.actions.execute(api === null || api === void 0 || (_api$blockControls2 = api.blockControls) === null || _api$blockControls2 === void 0 || (_api$blockControls2 = _api$blockControls2.commands) === null || _api$blockControls2 === void 0 ? void 0 : _api$blockControls2.toggleBlockMenu({
56
+ closeMenu: true
57
+ }));
58
+ };
59
+ return /*#__PURE__*/React.createElement(ToolbarDropdownItem, {
60
+ elemBefore: /*#__PURE__*/React.createElement(SyncBlocksIcon, {
61
+ label: ""
62
+ }),
63
+ onClick: onClick
64
+ }, /*#__PURE__*/React.createElement(Flex, {
65
+ alignItems: "center",
66
+ gap: "space.050"
67
+ }, /*#__PURE__*/React.createElement(Text, null, formatMessage(blockMenuMessages.copySyncedBlock)), /*#__PURE__*/React.createElement(Lozenge, {
68
+ appearance: "new"
69
+ }, formatMessage(blockMenuMessages.newLozenge))));
70
+ };
71
+ export var CreateOrCopySyncedBlockDropdownItem = function CreateOrCopySyncedBlockDropdownItem(_ref3) {
72
+ var api = _ref3.api;
73
+ var _useSharedPluginState2 = useSharedPluginStateWithSelector(api, ['blockControls'], function (states) {
74
+ var _states$blockControls2;
75
+ return {
76
+ menuTriggerBy: (_states$blockControls2 = states.blockControlsState) === null || _states$blockControls2 === void 0 ? void 0 : _states$blockControls2.menuTriggerBy
77
+ };
78
+ }),
79
+ menuTriggerBy = _useSharedPluginState2.menuTriggerBy;
80
+ if (menuTriggerBy === 'syncBlock' || menuTriggerBy === 'bodiedSyncBlock') {
81
+ return /*#__PURE__*/React.createElement(CopySyncedBlockDropdownItem, {
82
+ api: api
83
+ });
84
+ } else {
85
+ return /*#__PURE__*/React.createElement(CreateSyncedBlockDropdownItem, {
86
+ api: api
87
+ });
88
+ }
36
89
  };
@@ -1,6 +1,6 @@
1
1
  import React from 'react';
2
2
  import { ADD_BLOCKS_MENU_SECTION, ADD_BLOCKS_MENU_SECTION_RANK, CREATE_SYNCED_BLOCK_MENU_ITEM } from '@atlaskit/editor-common/block-menu';
3
- import { CreateSyncedBlockDropdownItem } from './CreateSyncedBlockDropdownItem';
3
+ import { CreateOrCopySyncedBlockDropdownItem } from './CreateSyncedBlockDropdownItem';
4
4
  export var getBlockMenuComponents = function getBlockMenuComponents(api) {
5
5
  return [{
6
6
  type: 'block-menu-item',
@@ -11,7 +11,7 @@ export var getBlockMenuComponents = function getBlockMenuComponents(api) {
11
11
  rank: ADD_BLOCKS_MENU_SECTION_RANK[CREATE_SYNCED_BLOCK_MENU_ITEM.key]
12
12
  },
13
13
  component: function component() {
14
- return /*#__PURE__*/React.createElement(CreateSyncedBlockDropdownItem, {
14
+ return /*#__PURE__*/React.createElement(CreateOrCopySyncedBlockDropdownItem, {
15
15
  api: api
16
16
  });
17
17
  }
@@ -12,7 +12,7 @@ import { copySyncedBlockReferenceToClipboard, editSyncedBlockSource, removeSynce
12
12
  import { findSyncBlockOrBodiedSyncBlock, isBodiedSyncBlockNode } from '../pm-plugins/utils/utils';
13
13
  export var getToolbarConfig = function getToolbarConfig(state, intl, api, syncBlockStore) {
14
14
  var _api$decorations;
15
- var syncBlockObject = findSyncBlockOrBodiedSyncBlock(state);
15
+ var syncBlockObject = findSyncBlockOrBodiedSyncBlock(state.schema, state.selection);
16
16
  if (!syncBlockObject) {
17
17
  return;
18
18
  }
@@ -38,7 +38,7 @@ export var getToolbarConfig = function getToolbarConfig(state, intl, api, syncBl
38
38
  title: formatMessage(messages.copySyncBlockLabel),
39
39
  showTitle: false,
40
40
  tooltipContent: formatMessage(messages.copySyncBlockTooltip),
41
- onClick: copySyncedBlockReferenceToClipboard(api)
41
+ onClick: copySyncedBlockReferenceToClipboard
42
42
  }, hoverDecorationProps(nodeType, akEditorSelectedNodeClassName));
43
43
  items.push(copyButton);
44
44
  var disabled = !syncBlockStore.getSyncBlockURL(syncBlockObject.node.attrs.resourceId);
@@ -1,4 +1,4 @@
1
- import type { Command, ExtractInjectionAPI, TypeAheadInsert } from '@atlaskit/editor-common/types';
1
+ import type { Command, EditorCommand, ExtractInjectionAPI, TypeAheadInsert } from '@atlaskit/editor-common/types';
2
2
  import { type Transaction } from '@atlaskit/editor-prosemirror/state';
3
3
  import type { SyncBlockStoreManager } from '@atlaskit/editor-synced-block-provider';
4
4
  import type { SyncedBlockPlugin } from '../syncedBlockPluginType';
@@ -8,7 +8,8 @@ type createSyncedBlockProps = {
8
8
  typeAheadInsert?: TypeAheadInsert;
9
9
  };
10
10
  export declare const createSyncedBlock: ({ tr, syncBlockStore, typeAheadInsert, }: createSyncedBlockProps) => false | Transaction;
11
- export declare const copySyncedBlockReferenceToClipboard: (api?: ExtractInjectionAPI<SyncedBlockPlugin>) => Command;
11
+ export declare const copySyncedBlockReferenceToClipboardEditorCommand: EditorCommand;
12
+ export declare const copySyncedBlockReferenceToClipboard: Command;
12
13
  export declare const editSyncedBlockSource: (syncBlockStore: SyncBlockStoreManager, api?: ExtractInjectionAPI<SyncedBlockPlugin>) => Command;
13
14
  export declare const removeSyncedBlock: (api?: ExtractInjectionAPI<SyncedBlockPlugin>) => Command;
14
15
  export {};
@@ -1,14 +1,22 @@
1
1
  import { Fragment } from '@atlaskit/editor-prosemirror/model';
2
- import type { NodeType, Node as PMNode } from '@atlaskit/editor-prosemirror/model';
3
- import type { EditorState, Selection } from '@atlaskit/editor-prosemirror/state';
2
+ import type { NodeType, Node as PMNode, Schema } from '@atlaskit/editor-prosemirror/model';
3
+ import { type Selection } from '@atlaskit/editor-prosemirror/state';
4
4
  import type { ContentNodeWithPos } from '@atlaskit/editor-prosemirror/utils';
5
- export declare const findSyncBlock: (state: EditorState, selection?: Selection | null) => ContentNodeWithPos | undefined;
6
- export declare const findBodiedSyncBlock: (state: EditorState, selection?: Selection | null) => ContentNodeWithPos | undefined;
7
- export declare const findSyncBlockOrBodiedSyncBlock: (state: EditorState, selection?: Selection | null) => ContentNodeWithPos | undefined;
5
+ export declare const findSyncBlock: (schema: Schema, selection: Selection) => ContentNodeWithPos | undefined;
6
+ export declare const findBodiedSyncBlock: (schema: Schema, selection: Selection) => ContentNodeWithPos | undefined;
7
+ export declare const findSyncBlockOrBodiedSyncBlock: (schema: Schema, selection: Selection) => ContentNodeWithPos | undefined;
8
8
  export declare const isBodiedSyncBlockNode: (node: PMNode, bodiedSyncBlock: NodeType) => boolean;
9
9
  export interface SyncBlockConversionInfo {
10
10
  contentToInclude: Fragment;
11
11
  from: number;
12
12
  to: number;
13
13
  }
14
+ /**
15
+ * Checks whether the selection can be converted to sync block
16
+ *
17
+ * @param selection - the current editor selection to validate for sync block conversion
18
+ * @returns A fragment containing the content to include in the synced block,
19
+ * stripping out unsupported marks (breakout on codeblock/expand/layout), as well as from and to positions,
20
+ * or false if conversion is not possible
21
+ */
14
22
  export declare const canBeConvertedToSyncBlock: (selection: Selection) => SyncBlockConversionInfo | false;
@@ -51,6 +51,7 @@ export type SyncedBlockPlugin = NextEditorPlugin<'syncedBlock', {
51
51
  flushBodiedSyncBlocks: () => Promise<boolean>;
52
52
  };
53
53
  commands: {
54
+ copySyncedBlockReferenceToClipboard: () => EditorCommand;
54
55
  insertSyncedBlock: () => EditorCommand;
55
56
  };
56
57
  dependencies: [
@@ -1,6 +1,6 @@
1
1
  import React from 'react';
2
2
  import type { ExtractInjectionAPI } from '@atlaskit/editor-common/types';
3
3
  import type { SyncedBlockPlugin } from '../syncedBlockPluginType';
4
- export declare const CreateSyncedBlockDropdownItem: ({ api, }: {
4
+ export declare const CreateOrCopySyncedBlockDropdownItem: ({ api, }: {
5
5
  api: ExtractInjectionAPI<SyncedBlockPlugin> | undefined;
6
- }) => React.JSX.Element | null;
6
+ }) => React.JSX.Element;
@@ -1,4 +1,4 @@
1
- import type { Command, ExtractInjectionAPI, TypeAheadInsert } from '@atlaskit/editor-common/types';
1
+ import type { Command, EditorCommand, ExtractInjectionAPI, TypeAheadInsert } from '@atlaskit/editor-common/types';
2
2
  import { type Transaction } from '@atlaskit/editor-prosemirror/state';
3
3
  import type { SyncBlockStoreManager } from '@atlaskit/editor-synced-block-provider';
4
4
  import type { SyncedBlockPlugin } from '../syncedBlockPluginType';
@@ -8,7 +8,8 @@ type createSyncedBlockProps = {
8
8
  typeAheadInsert?: TypeAheadInsert;
9
9
  };
10
10
  export declare const createSyncedBlock: ({ tr, syncBlockStore, typeAheadInsert, }: createSyncedBlockProps) => false | Transaction;
11
- export declare const copySyncedBlockReferenceToClipboard: (api?: ExtractInjectionAPI<SyncedBlockPlugin>) => Command;
11
+ export declare const copySyncedBlockReferenceToClipboardEditorCommand: EditorCommand;
12
+ export declare const copySyncedBlockReferenceToClipboard: Command;
12
13
  export declare const editSyncedBlockSource: (syncBlockStore: SyncBlockStoreManager, api?: ExtractInjectionAPI<SyncedBlockPlugin>) => Command;
13
14
  export declare const removeSyncedBlock: (api?: ExtractInjectionAPI<SyncedBlockPlugin>) => Command;
14
15
  export {};
@@ -1,14 +1,22 @@
1
1
  import { Fragment } from '@atlaskit/editor-prosemirror/model';
2
- import type { NodeType, Node as PMNode } from '@atlaskit/editor-prosemirror/model';
3
- import type { EditorState, Selection } from '@atlaskit/editor-prosemirror/state';
2
+ import type { NodeType, Node as PMNode, Schema } from '@atlaskit/editor-prosemirror/model';
3
+ import { type Selection } from '@atlaskit/editor-prosemirror/state';
4
4
  import type { ContentNodeWithPos } from '@atlaskit/editor-prosemirror/utils';
5
- export declare const findSyncBlock: (state: EditorState, selection?: Selection | null) => ContentNodeWithPos | undefined;
6
- export declare const findBodiedSyncBlock: (state: EditorState, selection?: Selection | null) => ContentNodeWithPos | undefined;
7
- export declare const findSyncBlockOrBodiedSyncBlock: (state: EditorState, selection?: Selection | null) => ContentNodeWithPos | undefined;
5
+ export declare const findSyncBlock: (schema: Schema, selection: Selection) => ContentNodeWithPos | undefined;
6
+ export declare const findBodiedSyncBlock: (schema: Schema, selection: Selection) => ContentNodeWithPos | undefined;
7
+ export declare const findSyncBlockOrBodiedSyncBlock: (schema: Schema, selection: Selection) => ContentNodeWithPos | undefined;
8
8
  export declare const isBodiedSyncBlockNode: (node: PMNode, bodiedSyncBlock: NodeType) => boolean;
9
9
  export interface SyncBlockConversionInfo {
10
10
  contentToInclude: Fragment;
11
11
  from: number;
12
12
  to: number;
13
13
  }
14
+ /**
15
+ * Checks whether the selection can be converted to sync block
16
+ *
17
+ * @param selection - the current editor selection to validate for sync block conversion
18
+ * @returns A fragment containing the content to include in the synced block,
19
+ * stripping out unsupported marks (breakout on codeblock/expand/layout), as well as from and to positions,
20
+ * or false if conversion is not possible
21
+ */
14
22
  export declare const canBeConvertedToSyncBlock: (selection: Selection) => SyncBlockConversionInfo | false;
@@ -51,6 +51,7 @@ export type SyncedBlockPlugin = NextEditorPlugin<'syncedBlock', {
51
51
  flushBodiedSyncBlocks: () => Promise<boolean>;
52
52
  };
53
53
  commands: {
54
+ copySyncedBlockReferenceToClipboard: () => EditorCommand;
54
55
  insertSyncedBlock: () => EditorCommand;
55
56
  };
56
57
  dependencies: [
@@ -1,6 +1,6 @@
1
1
  import React from 'react';
2
2
  import type { ExtractInjectionAPI } from '@atlaskit/editor-common/types';
3
3
  import type { SyncedBlockPlugin } from '../syncedBlockPluginType';
4
- export declare const CreateSyncedBlockDropdownItem: ({ api, }: {
4
+ export declare const CreateOrCopySyncedBlockDropdownItem: ({ api, }: {
5
5
  api: ExtractInjectionAPI<SyncedBlockPlugin> | undefined;
6
- }) => React.JSX.Element | null;
6
+ }) => React.JSX.Element;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/editor-plugin-synced-block",
3
- "version": "4.2.1",
3
+ "version": "4.2.3",
4
4
  "description": "SyncedBlock plugin for @atlaskit/editor-core",
5
5
  "author": "Atlassian Pty Ltd",
6
6
  "license": "Apache-2.0",
@@ -37,12 +37,12 @@
37
37
  "@atlaskit/editor-plugin-floating-toolbar": "^8.2.0",
38
38
  "@atlaskit/editor-plugin-selection": "^6.1.0",
39
39
  "@atlaskit/editor-prosemirror": "7.0.0",
40
- "@atlaskit/editor-shared-styles": "^3.8.0",
40
+ "@atlaskit/editor-shared-styles": "^3.9.0",
41
41
  "@atlaskit/editor-synced-block-provider": "^2.7.0",
42
42
  "@atlaskit/editor-tables": "^2.9.0",
43
43
  "@atlaskit/editor-toolbar": "^0.17.0",
44
44
  "@atlaskit/icon": "28.5.4",
45
- "@atlaskit/icon-lab": "^5.11.0",
45
+ "@atlaskit/icon-lab": "^5.12.0",
46
46
  "@atlaskit/logo": "^19.9.0",
47
47
  "@atlaskit/lozenge": "^13.1.0",
48
48
  "@atlaskit/modal-dialog": "^14.6.0",
@@ -53,7 +53,7 @@
53
53
  "react-intl-next": "npm:react-intl@^5.18.1"
54
54
  },
55
55
  "peerDependencies": {
56
- "@atlaskit/editor-common": "^110.25.0",
56
+ "@atlaskit/editor-common": "^110.27.0",
57
57
  "react": "^18.2.0"
58
58
  },
59
59
  "devDependencies": {