@atlaskit/editor-plugin-synced-block 3.3.2 → 3.3.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,13 @@
1
1
  # @atlaskit/editor-plugin-synced-block
2
2
 
3
+ ## 3.3.3
4
+
5
+ ### Patch Changes
6
+
7
+ - [`e053b5e610ac2`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/e053b5e610ac2) -
8
+ [ux] EDITOR-1652 add convert to sync block to block menu
9
+ - Updated dependencies
10
+
3
11
  ## 3.3.2
4
12
 
5
13
  ### Patch Changes
@@ -46,6 +46,9 @@
46
46
  {
47
47
  "path": "../../editor-synced-block-provider/afm-cc/tsconfig.json"
48
48
  },
49
+ {
50
+ "path": "../../editor-tables/afm-cc/tsconfig.json"
51
+ },
49
52
  {
50
53
  "path": "../../editor-toolbar/afm-cc/tsconfig.json"
51
54
  },
@@ -46,6 +46,9 @@
46
46
  {
47
47
  "path": "../../editor-synced-block-provider/afm-dev-agents/tsconfig.json"
48
48
  },
49
+ {
50
+ "path": "../../editor-tables/afm-dev-agents/tsconfig.json"
51
+ },
49
52
  {
50
53
  "path": "../../editor-toolbar/afm-dev-agents/tsconfig.json"
51
54
  },
@@ -46,6 +46,9 @@
46
46
  {
47
47
  "path": "../../editor-synced-block-provider/afm-jira/tsconfig.json"
48
48
  },
49
+ {
50
+ "path": "../../editor-tables/afm-jira/tsconfig.json"
51
+ },
49
52
  {
50
53
  "path": "../../editor-toolbar/afm-jira/tsconfig.json"
51
54
  },
@@ -46,6 +46,9 @@
46
46
  {
47
47
  "path": "../../editor-synced-block-provider/afm-passionfruit/tsconfig.json"
48
48
  },
49
+ {
50
+ "path": "../../editor-tables/afm-passionfruit/tsconfig.json"
51
+ },
49
52
  {
50
53
  "path": "../../editor-toolbar/afm-passionfruit/tsconfig.json"
51
54
  },
@@ -46,6 +46,9 @@
46
46
  {
47
47
  "path": "../../editor-synced-block-provider/afm-post-office/tsconfig.json"
48
48
  },
49
+ {
50
+ "path": "../../editor-tables/afm-post-office/tsconfig.json"
51
+ },
49
52
  {
50
53
  "path": "../../editor-toolbar/afm-post-office/tsconfig.json"
51
54
  },
@@ -46,6 +46,9 @@
46
46
  {
47
47
  "path": "../../editor-synced-block-provider/afm-rovo-extension/tsconfig.json"
48
48
  },
49
+ {
50
+ "path": "../../editor-tables/afm-rovo-extension/tsconfig.json"
51
+ },
49
52
  {
50
53
  "path": "../../editor-toolbar/afm-rovo-extension/tsconfig.json"
51
54
  },
@@ -46,6 +46,9 @@
46
46
  {
47
47
  "path": "../../editor-synced-block-provider/afm-townsquare/tsconfig.json"
48
48
  },
49
+ {
50
+ "path": "../../editor-tables/afm-townsquare/tsconfig.json"
51
+ },
49
52
  {
50
53
  "path": "../../editor-toolbar/afm-townsquare/tsconfig.json"
51
54
  },
@@ -70,6 +70,7 @@ var SyncBlock = /*#__PURE__*/function (_ReactNodeView) {
70
70
  value: function setInnerEditorView(editorView) {
71
71
  var _this$options;
72
72
  // set inner editor view
73
+ this.syncBlockStore.setSyncBlockNestedEditorView(editorView);
73
74
  var nodes = [(0, _editorSyncedBlockProvider.convertSyncBlockPMNodeToSyncBlockData)(this.node, false)];
74
75
  (_this$options = this.options) === null || _this$options === void 0 || (_this$options = _this$options.dataProvider) === null || _this$options === void 0 || _this$options.fetchNodesData(nodes).then(function (data) {
75
76
  var _data$;
@@ -162,6 +163,7 @@ var SyncBlock = /*#__PURE__*/function (_ReactNodeView) {
162
163
  if (this.fetchIntervalId) {
163
164
  window.clearInterval(this.fetchIntervalId);
164
165
  }
166
+ this.syncBlockStore.setSyncBlockNestedEditorView(undefined);
165
167
  (_this$unsubscribe = this.unsubscribe) === null || _this$unsubscribe === void 0 || _this$unsubscribe.call(this);
166
168
  _superPropGet(SyncBlock, "destroy", this, 3)([]);
167
169
  }
@@ -12,25 +12,47 @@ var _editorSyncedBlockProvider = require("@atlaskit/editor-synced-block-provider
12
12
  var _utils2 = require("./utils/utils");
13
13
  function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
14
14
  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; }
15
- var createSyncedBlock = exports.createSyncedBlock = function createSyncedBlock(tr, syncBlockStore, typeAheadInsert) {
16
- var syncBlock = tr.doc.type.schema.nodes.syncBlock;
15
+ var createSyncedBlock = exports.createSyncedBlock = function createSyncedBlock(_ref) {
16
+ var tr = _ref.tr,
17
+ syncBlockStore = _ref.syncBlockStore,
18
+ typeAheadInsert = _ref.typeAheadInsert;
19
+ var _tr$doc$type$schema$n = tr.doc.type.schema.nodes,
20
+ syncBlock = _tr$doc$type$schema$n.syncBlock,
21
+ doc = _tr$doc$type$schema$n.doc;
17
22
  var syncBlockNode = syncBlockStore.createSyncBlockNode();
23
+ var node = syncBlock.createAndFill(_objectSpread({}, syncBlockNode.attrs));
24
+ if (!node) {
25
+ return false;
26
+ }
18
27
 
19
- // If the selection is empty, we want to insert the panel on a new line
28
+ // If the selection is empty, we want to insert the sync block on a new line
20
29
  if (tr.selection.empty) {
21
- var node = syncBlock.createAndFill(_objectSpread({}, syncBlockNode.attrs));
22
- if (!node) {
23
- return false;
24
- }
25
30
  if (typeAheadInsert) {
26
31
  tr = typeAheadInsert(node);
27
32
  } else {
28
33
  tr = tr.replaceSelectionWith(node).scrollIntoView();
29
34
  }
30
35
  } else {
31
- var _safeInsert;
32
- // TODO: EDITOR-1653 - put selection inside the sync block if possible
33
- (_safeInsert = (0, _utils.safeInsert)(syncBlock.createAndFill(syncBlockNode.attrs))(tr)) === null || _safeInsert === void 0 || _safeInsert.scrollIntoView();
36
+ var conversionInfo = (0, _utils2.canBeConvertedToSyncBlock)(tr.selection);
37
+ if (conversionInfo) {
38
+ tr.replaceWith(conversionInfo.from, conversionInfo.to, node).scrollIntoView();
39
+ var innerNodeJson = doc.create({}, conversionInfo.contentToInclude).toJSON();
40
+
41
+ // TMP solution to wait for the nested editor view to be set
42
+ // this will be removed once we have a proper architecture settled
43
+ setTimeout(function () {
44
+ var editorView = syncBlockStore.getSyncBlockNestedEditorView();
45
+ if (editorView) {
46
+ var innerTr = editorView.state.tr;
47
+ var innerNode = editorView.state.schema.nodeFromJSON(innerNodeJson);
48
+ editorView.dispatch(innerTr.replaceWith(0, editorView.state.doc.nodeSize - 2, innerNode));
49
+ }
50
+ }, 1000);
51
+ } else {
52
+ var _safeInsert;
53
+ // still insert an empty sync block if conversion is not possible
54
+ (_safeInsert = (0, _utils.safeInsert)(syncBlock.createAndFill(syncBlockNode.attrs))(tr)) === null || _safeInsert === void 0 || _safeInsert.scrollIntoView();
55
+ }
34
56
  }
35
57
  return tr;
36
58
  };
@@ -42,6 +42,7 @@ var createPlugin = exports.createPlugin = function createPlugin(options, pmPlugi
42
42
  return {
43
43
  destroy: function destroy() {
44
44
  syncBlockStore.setEditorView(undefined);
45
+ syncBlockStore.setSyncBlockNestedEditorView(undefined);
45
46
  }
46
47
  };
47
48
  },
@@ -3,9 +3,57 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.findSyncBlock = void 0;
6
+ exports.findSyncBlock = exports.canBeConvertedToSyncBlock = void 0;
7
+ var _model = require("@atlaskit/editor-prosemirror/model");
7
8
  var _utils = require("@atlaskit/editor-prosemirror/utils");
9
+ var _editorSyncedBlockProvider = require("@atlaskit/editor-synced-block-provider");
10
+ var _editorTables = require("@atlaskit/editor-tables");
8
11
  var findSyncBlock = exports.findSyncBlock = function findSyncBlock(state, selection) {
9
12
  var syncBlock = state.schema.nodes.syncBlock;
10
13
  return (0, _utils.findSelectedNodeOfType)(syncBlock)(selection || state.selection) || (0, _utils.findParentNodeOfType)(syncBlock)(selection || state.selection);
14
+ };
15
+ var canBeConvertedToSyncBlock = exports.canBeConvertedToSyncBlock = function canBeConvertedToSyncBlock(selection) {
16
+ var from = selection.from;
17
+ var to = selection.to;
18
+ var depth = selection.$from.depth;
19
+ var contentToInclude;
20
+ if (selection instanceof _editorTables.CellSelection) {
21
+ var table = (0, _editorTables.findTable)(selection);
22
+ if (!table) {
23
+ return false;
24
+ }
25
+ contentToInclude = _model.Fragment.from([table.node]);
26
+ from = table.pos;
27
+ to = table.pos + table.node.nodeSize;
28
+ depth = selection.$from.doc.resolve(table.pos).depth;
29
+ } else {
30
+ contentToInclude = _model.Fragment.from(selection.content().content);
31
+ }
32
+
33
+ // sync blocks can't be nested
34
+ if (depth > 1) {
35
+ return false;
36
+ }
37
+ var syncBlockSchema = (0, _editorSyncedBlockProvider.getDefaultSyncBlockSchema)();
38
+ var canBeConverted = true;
39
+ selection.$from.doc.nodesBetween(from, to, function (node) {
40
+ if (!(node.type.name in syncBlockSchema.nodes)) {
41
+ canBeConverted = false;
42
+ return false;
43
+ }
44
+ node.marks.forEach(function (mark) {
45
+ if (!(mark.type.name in syncBlockSchema.marks)) {
46
+ canBeConverted = false;
47
+ return false;
48
+ }
49
+ });
50
+ });
51
+ if (!canBeConverted) {
52
+ return false;
53
+ }
54
+ return {
55
+ contentToInclude: contentToInclude,
56
+ from: from,
57
+ to: to
58
+ };
11
59
  };
@@ -41,7 +41,11 @@ var syncedBlockPlugin = exports.syncedBlockPlugin = function syncedBlockPlugin(_
41
41
  insertSyncedBlock: function insertSyncedBlock() {
42
42
  return function (_ref2) {
43
43
  var tr = _ref2.tr;
44
- return (0, _actions.createSyncedBlock)(tr, syncBlockStore) || null;
44
+ return (0, _actions.createSyncedBlock)({
45
+ tr: tr,
46
+ syncBlockStore: syncBlockStore,
47
+ dataProvider: config === null || config === void 0 ? void 0 : config.dataProvider
48
+ }) || null;
45
49
  };
46
50
  }
47
51
  },
@@ -61,7 +65,12 @@ var syncedBlockPlugin = exports.syncedBlockPlugin = function syncedBlockPlugin(_
61
65
  });
62
66
  },
63
67
  action: function action(insert, state) {
64
- return (0, _actions.createSyncedBlock)(state.tr, syncBlockStore, insert);
68
+ return (0, _actions.createSyncedBlock)({
69
+ tr: state.tr,
70
+ syncBlockStore: syncBlockStore,
71
+ dataProvider: config === null || config === void 0 ? void 0 : config.dataProvider,
72
+ typeAheadInsert: insert
73
+ });
65
74
  }
66
75
  }];
67
76
  },
@@ -9,13 +9,15 @@ var _react = _interopRequireDefault(require("react"));
9
9
  var _reactIntlNext = require("react-intl-next");
10
10
  var _messages = require("@atlaskit/editor-common/messages");
11
11
  var _editorToolbar = require("@atlaskit/editor-toolbar");
12
+ var _utils = require("../pm-plugins/utils/utils");
12
13
  var CreateSyncedBlockDropdownItem = exports.CreateSyncedBlockDropdownItem = function CreateSyncedBlockDropdownItem(_ref) {
13
14
  var _api$selection;
14
15
  var api = _ref.api;
15
16
  var _useIntl = (0, _reactIntlNext.useIntl)(),
16
17
  formatMessage = _useIntl.formatMessage;
17
18
  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;
18
- if (!(selection !== null && selection !== void 0 && selection.empty)) {
19
+ var canCreateSyncBlock = selection && (0, _utils.canBeConvertedToSyncBlock)(selection);
20
+ if (!canCreateSyncBlock) {
19
21
  return null;
20
22
  }
21
23
  var onClick = function onClick() {
@@ -27,5 +29,5 @@ var CreateSyncedBlockDropdownItem = exports.CreateSyncedBlockDropdownItem = func
27
29
  label: ""
28
30
  }),
29
31
  onClick: onClick
30
- }, formatMessage(_messages.blockMenuMessages.createSyncedBlock));
32
+ }, selection !== null && selection !== void 0 && selection.empty ? formatMessage(_messages.blockMenuMessages.createSyncedBlock) : formatMessage(_messages.blockMenuMessages.convertToSyncedBlock));
31
33
  };
@@ -44,6 +44,7 @@ class SyncBlock extends ReactNodeView {
44
44
  setInnerEditorView(editorView) {
45
45
  var _this$options, _this$options$dataPro;
46
46
  // set inner editor view
47
+ this.syncBlockStore.setSyncBlockNestedEditorView(editorView);
47
48
  const nodes = [convertSyncBlockPMNodeToSyncBlockData(this.node, false)];
48
49
  (_this$options = this.options) === null || _this$options === void 0 ? void 0 : (_this$options$dataPro = _this$options.dataProvider) === null || _this$options$dataPro === void 0 ? void 0 : _this$options$dataPro.fetchNodesData(nodes).then(data => {
49
50
  var _data$;
@@ -118,6 +119,7 @@ class SyncBlock extends ReactNodeView {
118
119
  if (this.fetchIntervalId) {
119
120
  window.clearInterval(this.fetchIntervalId);
120
121
  }
122
+ this.syncBlockStore.setSyncBlockNestedEditorView(undefined);
121
123
  (_this$unsubscribe = this.unsubscribe) === null || _this$unsubscribe === void 0 ? void 0 : _this$unsubscribe.call(this);
122
124
  super.destroy();
123
125
  }
@@ -1,34 +1,56 @@
1
1
  import { INPUT_METHOD } from '@atlaskit/editor-common/analytics';
2
2
  import { safeInsert } from '@atlaskit/editor-prosemirror/utils';
3
3
  import { generateSyncBlockSourceUrl } from '@atlaskit/editor-synced-block-provider';
4
- import { findSyncBlock } from './utils/utils';
5
- export const createSyncedBlock = (tr, syncBlockStore, typeAheadInsert) => {
4
+ import { canBeConvertedToSyncBlock, findSyncBlock } from './utils/utils';
5
+ export const createSyncedBlock = ({
6
+ tr,
7
+ syncBlockStore,
8
+ typeAheadInsert
9
+ }) => {
6
10
  const {
7
11
  schema: {
8
12
  nodes: {
9
- syncBlock
13
+ syncBlock,
14
+ doc
10
15
  }
11
16
  }
12
17
  } = tr.doc.type;
13
18
  const syncBlockNode = syncBlockStore.createSyncBlockNode();
19
+ const node = syncBlock.createAndFill({
20
+ ...syncBlockNode.attrs
21
+ });
22
+ if (!node) {
23
+ return false;
24
+ }
14
25
 
15
- // If the selection is empty, we want to insert the panel on a new line
26
+ // If the selection is empty, we want to insert the sync block on a new line
16
27
  if (tr.selection.empty) {
17
- const node = syncBlock.createAndFill({
18
- ...syncBlockNode.attrs
19
- });
20
- if (!node) {
21
- return false;
22
- }
23
28
  if (typeAheadInsert) {
24
29
  tr = typeAheadInsert(node);
25
30
  } else {
26
31
  tr = tr.replaceSelectionWith(node).scrollIntoView();
27
32
  }
28
33
  } else {
29
- var _safeInsert;
30
- // TODO: EDITOR-1653 - put selection inside the sync block if possible
31
- (_safeInsert = safeInsert(syncBlock.createAndFill(syncBlockNode.attrs))(tr)) === null || _safeInsert === void 0 ? void 0 : _safeInsert.scrollIntoView();
34
+ const conversionInfo = canBeConvertedToSyncBlock(tr.selection);
35
+ if (conversionInfo) {
36
+ tr.replaceWith(conversionInfo.from, conversionInfo.to, node).scrollIntoView();
37
+ const innerNodeJson = doc.create({}, conversionInfo.contentToInclude).toJSON();
38
+
39
+ // TMP solution to wait for the nested editor view to be set
40
+ // this will be removed once we have a proper architecture settled
41
+ setTimeout(() => {
42
+ const editorView = syncBlockStore.getSyncBlockNestedEditorView();
43
+ if (editorView) {
44
+ const innerTr = editorView.state.tr;
45
+ const innerNode = editorView.state.schema.nodeFromJSON(innerNodeJson);
46
+ editorView.dispatch(innerTr.replaceWith(0, editorView.state.doc.nodeSize - 2, innerNode));
47
+ }
48
+ }, 1000);
49
+ } else {
50
+ var _safeInsert;
51
+ // still insert an empty sync block if conversion is not possible
52
+ (_safeInsert = safeInsert(syncBlock.createAndFill(syncBlockNode.attrs))(tr)) === null || _safeInsert === void 0 ? void 0 : _safeInsert.scrollIntoView();
53
+ }
32
54
  }
33
55
  return tr;
34
56
  };
@@ -36,6 +36,7 @@ export const createPlugin = (options, pmPluginFactoryParams, syncBlockStore, api
36
36
  return {
37
37
  destroy() {
38
38
  syncBlockStore.setEditorView(undefined);
39
+ syncBlockStore.setSyncBlockNestedEditorView(undefined);
39
40
  }
40
41
  };
41
42
  },
@@ -1,7 +1,55 @@
1
+ import { Fragment } from '@atlaskit/editor-prosemirror/model';
1
2
  import { findParentNodeOfType, findSelectedNodeOfType } from '@atlaskit/editor-prosemirror/utils';
3
+ import { getDefaultSyncBlockSchema } from '@atlaskit/editor-synced-block-provider';
4
+ import { CellSelection, findTable } from '@atlaskit/editor-tables';
2
5
  export const findSyncBlock = (state, selection) => {
3
6
  const {
4
7
  syncBlock
5
8
  } = state.schema.nodes;
6
9
  return findSelectedNodeOfType(syncBlock)(selection || state.selection) || findParentNodeOfType(syncBlock)(selection || state.selection);
10
+ };
11
+ export const canBeConvertedToSyncBlock = selection => {
12
+ let from = selection.from;
13
+ let to = selection.to;
14
+ let depth = selection.$from.depth;
15
+ let contentToInclude;
16
+ if (selection instanceof CellSelection) {
17
+ const table = findTable(selection);
18
+ if (!table) {
19
+ return false;
20
+ }
21
+ contentToInclude = Fragment.from([table.node]);
22
+ from = table.pos;
23
+ to = table.pos + table.node.nodeSize;
24
+ depth = selection.$from.doc.resolve(table.pos).depth;
25
+ } else {
26
+ contentToInclude = Fragment.from(selection.content().content);
27
+ }
28
+
29
+ // sync blocks can't be nested
30
+ if (depth > 1) {
31
+ return false;
32
+ }
33
+ const syncBlockSchema = getDefaultSyncBlockSchema();
34
+ let canBeConverted = true;
35
+ selection.$from.doc.nodesBetween(from, to, node => {
36
+ if (!(node.type.name in syncBlockSchema.nodes)) {
37
+ canBeConverted = false;
38
+ return false;
39
+ }
40
+ node.marks.forEach(mark => {
41
+ if (!(mark.type.name in syncBlockSchema.marks)) {
42
+ canBeConverted = false;
43
+ return false;
44
+ }
45
+ });
46
+ });
47
+ if (!canBeConverted) {
48
+ return false;
49
+ }
50
+ return {
51
+ contentToInclude,
52
+ from,
53
+ to
54
+ };
7
55
  };
@@ -32,7 +32,11 @@ export const syncedBlockPlugin = ({
32
32
  commands: {
33
33
  insertSyncedBlock: () => ({
34
34
  tr
35
- }) => createSyncedBlock(tr, syncBlockStore) || null
35
+ }) => createSyncedBlock({
36
+ tr,
37
+ syncBlockStore,
38
+ dataProvider: config === null || config === void 0 ? void 0 : config.dataProvider
39
+ }) || null
36
40
  },
37
41
  pluginsOptions: {
38
42
  quickInsert: ({
@@ -48,7 +52,12 @@ export const syncedBlockPlugin = ({
48
52
  label: formatMessage(blockTypeMessages.syncedBlock)
49
53
  }),
50
54
  action: (insert, state) => {
51
- return createSyncedBlock(state.tr, syncBlockStore, insert);
55
+ return createSyncedBlock({
56
+ tr: state.tr,
57
+ syncBlockStore,
58
+ dataProvider: config === null || config === void 0 ? void 0 : config.dataProvider,
59
+ typeAheadInsert: insert
60
+ });
52
61
  }
53
62
  }],
54
63
  floatingToolbar: (state, intl, providerFactory) => getToolbarConfig(state, intl, config, providerFactory, api, syncBlockStore)
@@ -2,6 +2,7 @@ import React from 'react';
2
2
  import { useIntl } from 'react-intl-next';
3
3
  import { blockMenuMessages } from '@atlaskit/editor-common/messages';
4
4
  import { SyncBlocksIcon, ToolbarDropdownItem } from '@atlaskit/editor-toolbar';
5
+ import { canBeConvertedToSyncBlock } from '../pm-plugins/utils/utils';
5
6
  export const CreateSyncedBlockDropdownItem = ({
6
7
  api
7
8
  }) => {
@@ -10,7 +11,8 @@ export const CreateSyncedBlockDropdownItem = ({
10
11
  formatMessage
11
12
  } = useIntl();
12
13
  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;
13
- if (!(selection !== null && selection !== void 0 && selection.empty)) {
14
+ const canCreateSyncBlock = selection && canBeConvertedToSyncBlock(selection);
15
+ if (!canCreateSyncBlock) {
14
16
  return null;
15
17
  }
16
18
  const onClick = () => {
@@ -22,5 +24,5 @@ export const CreateSyncedBlockDropdownItem = ({
22
24
  label: ""
23
25
  }),
24
26
  onClick: onClick
25
- }, formatMessage(blockMenuMessages.createSyncedBlock));
27
+ }, selection !== null && selection !== void 0 && selection.empty ? formatMessage(blockMenuMessages.createSyncedBlock) : formatMessage(blockMenuMessages.convertToSyncedBlock));
26
28
  };
@@ -63,6 +63,7 @@ var SyncBlock = /*#__PURE__*/function (_ReactNodeView) {
63
63
  value: function setInnerEditorView(editorView) {
64
64
  var _this$options;
65
65
  // set inner editor view
66
+ this.syncBlockStore.setSyncBlockNestedEditorView(editorView);
66
67
  var nodes = [convertSyncBlockPMNodeToSyncBlockData(this.node, false)];
67
68
  (_this$options = this.options) === null || _this$options === void 0 || (_this$options = _this$options.dataProvider) === null || _this$options === void 0 || _this$options.fetchNodesData(nodes).then(function (data) {
68
69
  var _data$;
@@ -155,6 +156,7 @@ var SyncBlock = /*#__PURE__*/function (_ReactNodeView) {
155
156
  if (this.fetchIntervalId) {
156
157
  window.clearInterval(this.fetchIntervalId);
157
158
  }
159
+ this.syncBlockStore.setSyncBlockNestedEditorView(undefined);
158
160
  (_this$unsubscribe = this.unsubscribe) === null || _this$unsubscribe === void 0 || _this$unsubscribe.call(this);
159
161
  _superPropGet(SyncBlock, "destroy", this, 3)([]);
160
162
  }
@@ -4,26 +4,48 @@ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t =
4
4
  import { INPUT_METHOD } from '@atlaskit/editor-common/analytics';
5
5
  import { safeInsert } from '@atlaskit/editor-prosemirror/utils';
6
6
  import { generateSyncBlockSourceUrl } from '@atlaskit/editor-synced-block-provider';
7
- import { findSyncBlock } from './utils/utils';
8
- export var createSyncedBlock = function createSyncedBlock(tr, syncBlockStore, typeAheadInsert) {
9
- var syncBlock = tr.doc.type.schema.nodes.syncBlock;
7
+ import { canBeConvertedToSyncBlock, findSyncBlock } from './utils/utils';
8
+ export var createSyncedBlock = function createSyncedBlock(_ref) {
9
+ var tr = _ref.tr,
10
+ syncBlockStore = _ref.syncBlockStore,
11
+ typeAheadInsert = _ref.typeAheadInsert;
12
+ var _tr$doc$type$schema$n = tr.doc.type.schema.nodes,
13
+ syncBlock = _tr$doc$type$schema$n.syncBlock,
14
+ doc = _tr$doc$type$schema$n.doc;
10
15
  var syncBlockNode = syncBlockStore.createSyncBlockNode();
16
+ var node = syncBlock.createAndFill(_objectSpread({}, syncBlockNode.attrs));
17
+ if (!node) {
18
+ return false;
19
+ }
11
20
 
12
- // If the selection is empty, we want to insert the panel on a new line
21
+ // If the selection is empty, we want to insert the sync block on a new line
13
22
  if (tr.selection.empty) {
14
- var node = syncBlock.createAndFill(_objectSpread({}, syncBlockNode.attrs));
15
- if (!node) {
16
- return false;
17
- }
18
23
  if (typeAheadInsert) {
19
24
  tr = typeAheadInsert(node);
20
25
  } else {
21
26
  tr = tr.replaceSelectionWith(node).scrollIntoView();
22
27
  }
23
28
  } else {
24
- var _safeInsert;
25
- // TODO: EDITOR-1653 - put selection inside the sync block if possible
26
- (_safeInsert = safeInsert(syncBlock.createAndFill(syncBlockNode.attrs))(tr)) === null || _safeInsert === void 0 || _safeInsert.scrollIntoView();
29
+ var conversionInfo = canBeConvertedToSyncBlock(tr.selection);
30
+ if (conversionInfo) {
31
+ tr.replaceWith(conversionInfo.from, conversionInfo.to, node).scrollIntoView();
32
+ var innerNodeJson = doc.create({}, conversionInfo.contentToInclude).toJSON();
33
+
34
+ // TMP solution to wait for the nested editor view to be set
35
+ // this will be removed once we have a proper architecture settled
36
+ setTimeout(function () {
37
+ var editorView = syncBlockStore.getSyncBlockNestedEditorView();
38
+ if (editorView) {
39
+ var innerTr = editorView.state.tr;
40
+ var innerNode = editorView.state.schema.nodeFromJSON(innerNodeJson);
41
+ editorView.dispatch(innerTr.replaceWith(0, editorView.state.doc.nodeSize - 2, innerNode));
42
+ }
43
+ }, 1000);
44
+ } else {
45
+ var _safeInsert;
46
+ // still insert an empty sync block if conversion is not possible
47
+ (_safeInsert = safeInsert(syncBlock.createAndFill(syncBlockNode.attrs))(tr)) === null || _safeInsert === void 0 || _safeInsert.scrollIntoView();
48
+ }
27
49
  }
28
50
  return tr;
29
51
  };
@@ -36,6 +36,7 @@ export var createPlugin = function createPlugin(options, pmPluginFactoryParams,
36
36
  return {
37
37
  destroy: function destroy() {
38
38
  syncBlockStore.setEditorView(undefined);
39
+ syncBlockStore.setSyncBlockNestedEditorView(undefined);
39
40
  }
40
41
  };
41
42
  },
@@ -1,5 +1,53 @@
1
+ import { Fragment } from '@atlaskit/editor-prosemirror/model';
1
2
  import { findParentNodeOfType, findSelectedNodeOfType } from '@atlaskit/editor-prosemirror/utils';
3
+ import { getDefaultSyncBlockSchema } from '@atlaskit/editor-synced-block-provider';
4
+ import { CellSelection, findTable } from '@atlaskit/editor-tables';
2
5
  export var findSyncBlock = function findSyncBlock(state, selection) {
3
6
  var syncBlock = state.schema.nodes.syncBlock;
4
7
  return findSelectedNodeOfType(syncBlock)(selection || state.selection) || findParentNodeOfType(syncBlock)(selection || state.selection);
8
+ };
9
+ export var canBeConvertedToSyncBlock = function canBeConvertedToSyncBlock(selection) {
10
+ var from = selection.from;
11
+ var to = selection.to;
12
+ var depth = selection.$from.depth;
13
+ var contentToInclude;
14
+ if (selection instanceof CellSelection) {
15
+ var table = findTable(selection);
16
+ if (!table) {
17
+ return false;
18
+ }
19
+ contentToInclude = Fragment.from([table.node]);
20
+ from = table.pos;
21
+ to = table.pos + table.node.nodeSize;
22
+ depth = selection.$from.doc.resolve(table.pos).depth;
23
+ } else {
24
+ contentToInclude = Fragment.from(selection.content().content);
25
+ }
26
+
27
+ // sync blocks can't be nested
28
+ if (depth > 1) {
29
+ return false;
30
+ }
31
+ var syncBlockSchema = getDefaultSyncBlockSchema();
32
+ var canBeConverted = true;
33
+ selection.$from.doc.nodesBetween(from, to, function (node) {
34
+ if (!(node.type.name in syncBlockSchema.nodes)) {
35
+ canBeConverted = false;
36
+ return false;
37
+ }
38
+ node.marks.forEach(function (mark) {
39
+ if (!(mark.type.name in syncBlockSchema.marks)) {
40
+ canBeConverted = false;
41
+ return false;
42
+ }
43
+ });
44
+ });
45
+ if (!canBeConverted) {
46
+ return false;
47
+ }
48
+ return {
49
+ contentToInclude: contentToInclude,
50
+ from: from,
51
+ to: to
52
+ };
5
53
  };
@@ -34,7 +34,11 @@ export var syncedBlockPlugin = function syncedBlockPlugin(_ref) {
34
34
  insertSyncedBlock: function insertSyncedBlock() {
35
35
  return function (_ref2) {
36
36
  var tr = _ref2.tr;
37
- return createSyncedBlock(tr, syncBlockStore) || null;
37
+ return createSyncedBlock({
38
+ tr: tr,
39
+ syncBlockStore: syncBlockStore,
40
+ dataProvider: config === null || config === void 0 ? void 0 : config.dataProvider
41
+ }) || null;
38
42
  };
39
43
  }
40
44
  },
@@ -54,7 +58,12 @@ export var syncedBlockPlugin = function syncedBlockPlugin(_ref) {
54
58
  });
55
59
  },
56
60
  action: function action(insert, state) {
57
- return createSyncedBlock(state.tr, syncBlockStore, insert);
61
+ return createSyncedBlock({
62
+ tr: state.tr,
63
+ syncBlockStore: syncBlockStore,
64
+ dataProvider: config === null || config === void 0 ? void 0 : config.dataProvider,
65
+ typeAheadInsert: insert
66
+ });
58
67
  }
59
68
  }];
60
69
  },
@@ -2,13 +2,15 @@ import React from 'react';
2
2
  import { useIntl } from 'react-intl-next';
3
3
  import { blockMenuMessages } from '@atlaskit/editor-common/messages';
4
4
  import { SyncBlocksIcon, ToolbarDropdownItem } from '@atlaskit/editor-toolbar';
5
+ import { canBeConvertedToSyncBlock } from '../pm-plugins/utils/utils';
5
6
  export var CreateSyncedBlockDropdownItem = function CreateSyncedBlockDropdownItem(_ref) {
6
7
  var _api$selection;
7
8
  var api = _ref.api;
8
9
  var _useIntl = useIntl(),
9
10
  formatMessage = _useIntl.formatMessage;
10
11
  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;
11
- if (!(selection !== null && selection !== void 0 && selection.empty)) {
12
+ var canCreateSyncBlock = selection && canBeConvertedToSyncBlock(selection);
13
+ if (!canCreateSyncBlock) {
12
14
  return null;
13
15
  }
14
16
  var onClick = function onClick() {
@@ -20,5 +22,5 @@ export var CreateSyncedBlockDropdownItem = function CreateSyncedBlockDropdownIte
20
22
  label: ""
21
23
  }),
22
24
  onClick: onClick
23
- }, formatMessage(blockMenuMessages.createSyncedBlock));
25
+ }, selection !== null && selection !== void 0 && selection.empty ? formatMessage(blockMenuMessages.createSyncedBlock) : formatMessage(blockMenuMessages.convertToSyncedBlock));
24
26
  };
@@ -1,8 +1,15 @@
1
1
  import type { Command, ExtractInjectionAPI, TypeAheadInsert } from '@atlaskit/editor-common/types';
2
2
  import { type Transaction } from '@atlaskit/editor-prosemirror/state';
3
- import type { SyncBlockStoreManager } from '@atlaskit/editor-synced-block-provider';
3
+ import type { SyncBlockDataProvider, SyncBlockStoreManager } from '@atlaskit/editor-synced-block-provider';
4
4
  import type { SyncedBlockPlugin } from '../syncedBlockPluginType';
5
- export declare const createSyncedBlock: (tr: Transaction, syncBlockStore: SyncBlockStoreManager, typeAheadInsert?: TypeAheadInsert) => false | Transaction;
5
+ type createSyncedBlockProps = {
6
+ dataProvider?: SyncBlockDataProvider;
7
+ syncBlockStore: SyncBlockStoreManager;
8
+ tr: Transaction;
9
+ typeAheadInsert?: TypeAheadInsert;
10
+ };
11
+ export declare const createSyncedBlock: ({ tr, syncBlockStore, typeAheadInsert, }: createSyncedBlockProps) => false | Transaction;
6
12
  export declare const copySyncedBlockReferenceToClipboard: (api?: ExtractInjectionAPI<SyncedBlockPlugin>) => Command;
7
13
  export declare const editSyncedBlockSource: (_api?: ExtractInjectionAPI<SyncedBlockPlugin>) => Command;
8
14
  export declare const removeSyncedBlock: (api?: ExtractInjectionAPI<SyncedBlockPlugin>) => Command;
15
+ export {};
@@ -1,3 +1,10 @@
1
+ import { Fragment } from '@atlaskit/editor-prosemirror/model';
1
2
  import type { EditorState, Selection } from '@atlaskit/editor-prosemirror/state';
2
3
  import { findParentNodeOfType, findSelectedNodeOfType } from '@atlaskit/editor-prosemirror/utils';
3
4
  export declare const findSyncBlock: (state: EditorState, selection?: Selection | null) => ReturnType<ReturnType<typeof findSelectedNodeOfType>> | ReturnType<ReturnType<typeof findParentNodeOfType>>;
5
+ export interface SyncBlockConversionInfo {
6
+ contentToInclude: Fragment;
7
+ from: number;
8
+ to: number;
9
+ }
10
+ export declare const canBeConvertedToSyncBlock: (selection: Selection) => SyncBlockConversionInfo | false;
@@ -1,8 +1,15 @@
1
1
  import type { Command, ExtractInjectionAPI, TypeAheadInsert } from '@atlaskit/editor-common/types';
2
2
  import { type Transaction } from '@atlaskit/editor-prosemirror/state';
3
- import type { SyncBlockStoreManager } from '@atlaskit/editor-synced-block-provider';
3
+ import type { SyncBlockDataProvider, SyncBlockStoreManager } from '@atlaskit/editor-synced-block-provider';
4
4
  import type { SyncedBlockPlugin } from '../syncedBlockPluginType';
5
- export declare const createSyncedBlock: (tr: Transaction, syncBlockStore: SyncBlockStoreManager, typeAheadInsert?: TypeAheadInsert) => false | Transaction;
5
+ type createSyncedBlockProps = {
6
+ dataProvider?: SyncBlockDataProvider;
7
+ syncBlockStore: SyncBlockStoreManager;
8
+ tr: Transaction;
9
+ typeAheadInsert?: TypeAheadInsert;
10
+ };
11
+ export declare const createSyncedBlock: ({ tr, syncBlockStore, typeAheadInsert, }: createSyncedBlockProps) => false | Transaction;
6
12
  export declare const copySyncedBlockReferenceToClipboard: (api?: ExtractInjectionAPI<SyncedBlockPlugin>) => Command;
7
13
  export declare const editSyncedBlockSource: (_api?: ExtractInjectionAPI<SyncedBlockPlugin>) => Command;
8
14
  export declare const removeSyncedBlock: (api?: ExtractInjectionAPI<SyncedBlockPlugin>) => Command;
15
+ export {};
@@ -1,3 +1,10 @@
1
+ import { Fragment } from '@atlaskit/editor-prosemirror/model';
1
2
  import type { EditorState, Selection } from '@atlaskit/editor-prosemirror/state';
2
3
  import { findParentNodeOfType, findSelectedNodeOfType } from '@atlaskit/editor-prosemirror/utils';
3
4
  export declare const findSyncBlock: (state: EditorState, selection?: Selection | null) => ReturnType<ReturnType<typeof findSelectedNodeOfType>> | ReturnType<ReturnType<typeof findParentNodeOfType>>;
5
+ export interface SyncBlockConversionInfo {
6
+ contentToInclude: Fragment;
7
+ from: number;
8
+ to: number;
9
+ }
10
+ export declare const canBeConvertedToSyncBlock: (selection: Selection) => SyncBlockConversionInfo | false;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/editor-plugin-synced-block",
3
- "version": "3.3.2",
3
+ "version": "3.3.3",
4
4
  "description": "SyncedBlock plugin for @atlaskit/editor-core",
5
5
  "author": "Atlassian Pty Ltd",
6
6
  "license": "Apache-2.0",
@@ -38,6 +38,7 @@
38
38
  "@atlaskit/editor-prosemirror": "7.0.0",
39
39
  "@atlaskit/editor-shared-styles": "^3.6.0",
40
40
  "@atlaskit/editor-synced-block-provider": "^0.5.0",
41
+ "@atlaskit/editor-tables": "^2.9.0",
41
42
  "@atlaskit/editor-toolbar": "^0.14.0",
42
43
  "@atlaskit/icon": "28.5.1",
43
44
  "@atlaskit/modal-dialog": "^14.4.0",
@@ -45,7 +46,7 @@
45
46
  "react-intl-next": "npm:react-intl@^5.18.1"
46
47
  },
47
48
  "peerDependencies": {
48
- "@atlaskit/editor-common": "^110.8.0",
49
+ "@atlaskit/editor-common": "^110.9.0",
49
50
  "react": "^18.2.0"
50
51
  },
51
52
  "devDependencies": {