@atlaskit/editor-plugin-synced-block 2.1.1 → 2.2.1

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,22 @@
1
1
  # @atlaskit/editor-plugin-synced-block
2
2
 
3
+ ## 2.2.1
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies
8
+
9
+ ## 2.2.0
10
+
11
+ ### Minor Changes
12
+
13
+ - [`889f0cb60f68d`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/889f0cb60f68d) -
14
+ EDITOR-1561 implement confirmation before deleting a source sync block
15
+
16
+ ### Patch Changes
17
+
18
+ - Updated dependencies
19
+
3
20
  ## 2.1.1
4
21
 
5
22
  ### Patch Changes
@@ -22,12 +22,18 @@
22
22
  "../src/**/stories/**/*"
23
23
  ],
24
24
  "references": [
25
+ {
26
+ "path": "../../../design-system/button/afm-cc/tsconfig.json"
27
+ },
25
28
  {
26
29
  "path": "../../editor-plugin-selection/afm-cc/tsconfig.json"
27
30
  },
28
31
  {
29
32
  "path": "../../../design-system/icon/afm-cc/tsconfig.json"
30
33
  },
34
+ {
35
+ "path": "../../../design-system/modal-dialog/afm-cc/tsconfig.json"
36
+ },
31
37
  {
32
38
  "path": "../../editor-common/afm-cc/tsconfig.json"
33
39
  }
@@ -22,12 +22,18 @@
22
22
  "../src/**/stories/**/*"
23
23
  ],
24
24
  "references": [
25
+ {
26
+ "path": "../../../design-system/button/afm-dev-agents/tsconfig.json"
27
+ },
25
28
  {
26
29
  "path": "../../editor-plugin-selection/afm-dev-agents/tsconfig.json"
27
30
  },
28
31
  {
29
32
  "path": "../../../design-system/icon/afm-dev-agents/tsconfig.json"
30
33
  },
34
+ {
35
+ "path": "../../../design-system/modal-dialog/afm-dev-agents/tsconfig.json"
36
+ },
31
37
  {
32
38
  "path": "../../editor-common/afm-dev-agents/tsconfig.json"
33
39
  }
@@ -22,12 +22,18 @@
22
22
  "../src/**/stories/**/*"
23
23
  ],
24
24
  "references": [
25
+ {
26
+ "path": "../../../design-system/button/afm-jira/tsconfig.json"
27
+ },
25
28
  {
26
29
  "path": "../../editor-plugin-selection/afm-jira/tsconfig.json"
27
30
  },
28
31
  {
29
32
  "path": "../../../design-system/icon/afm-jira/tsconfig.json"
30
33
  },
34
+ {
35
+ "path": "../../../design-system/modal-dialog/afm-jira/tsconfig.json"
36
+ },
31
37
  {
32
38
  "path": "../../editor-common/afm-jira/tsconfig.json"
33
39
  }
@@ -22,12 +22,18 @@
22
22
  "../src/**/stories/**/*"
23
23
  ],
24
24
  "references": [
25
+ {
26
+ "path": "../../../design-system/button/afm-passionfruit/tsconfig.json"
27
+ },
25
28
  {
26
29
  "path": "../../editor-plugin-selection/afm-passionfruit/tsconfig.json"
27
30
  },
28
31
  {
29
32
  "path": "../../../design-system/icon/afm-passionfruit/tsconfig.json"
30
33
  },
34
+ {
35
+ "path": "../../../design-system/modal-dialog/afm-passionfruit/tsconfig.json"
36
+ },
31
37
  {
32
38
  "path": "../../editor-common/afm-passionfruit/tsconfig.json"
33
39
  }
@@ -22,12 +22,18 @@
22
22
  "../src/**/stories/**/*"
23
23
  ],
24
24
  "references": [
25
+ {
26
+ "path": "../../../design-system/button/afm-post-office/tsconfig.json"
27
+ },
25
28
  {
26
29
  "path": "../../editor-plugin-selection/afm-post-office/tsconfig.json"
27
30
  },
28
31
  {
29
32
  "path": "../../../design-system/icon/afm-post-office/tsconfig.json"
30
33
  },
34
+ {
35
+ "path": "../../../design-system/modal-dialog/afm-post-office/tsconfig.json"
36
+ },
31
37
  {
32
38
  "path": "../../editor-common/afm-post-office/tsconfig.json"
33
39
  }
@@ -22,12 +22,18 @@
22
22
  "../src/**/stories/**/*"
23
23
  ],
24
24
  "references": [
25
+ {
26
+ "path": "../../../design-system/button/afm-rovo-extension/tsconfig.json"
27
+ },
25
28
  {
26
29
  "path": "../../editor-plugin-selection/afm-rovo-extension/tsconfig.json"
27
30
  },
28
31
  {
29
32
  "path": "../../../design-system/icon/afm-rovo-extension/tsconfig.json"
30
33
  },
34
+ {
35
+ "path": "../../../design-system/modal-dialog/afm-rovo-extension/tsconfig.json"
36
+ },
31
37
  {
32
38
  "path": "../../editor-common/afm-rovo-extension/tsconfig.json"
33
39
  }
@@ -22,12 +22,18 @@
22
22
  "../src/**/stories/**/*"
23
23
  ],
24
24
  "references": [
25
+ {
26
+ "path": "../../../design-system/button/afm-townsquare/tsconfig.json"
27
+ },
25
28
  {
26
29
  "path": "../../editor-plugin-selection/afm-townsquare/tsconfig.json"
27
30
  },
28
31
  {
29
32
  "path": "../../../design-system/icon/afm-townsquare/tsconfig.json"
30
33
  },
34
+ {
35
+ "path": "../../../design-system/modal-dialog/afm-townsquare/tsconfig.json"
36
+ },
31
37
  {
32
38
  "path": "../../editor-common/afm-townsquare/tsconfig.json"
33
39
  }
@@ -7,22 +7,19 @@ exports.syncedBlockPluginKey = exports.createPlugin = void 0;
7
7
  var _safePlugin = require("@atlaskit/editor-common/safe-plugin");
8
8
  var _state = require("@atlaskit/editor-prosemirror/state");
9
9
  var _lazySyncedBlock = require("../nodeviews/lazySyncedBlock");
10
+ var _trackSyncBlocks2 = require("./utils/track-sync-blocks");
10
11
  var syncedBlockPluginKey = exports.syncedBlockPluginKey = new _state.PluginKey('syncedBlockPlugin');
11
12
 
12
13
  // eslint-disable-next-line @typescript-eslint/no-empty-object-type
13
14
 
14
- var createPlugin = exports.createPlugin = function createPlugin(options, pmPluginFactoryParams, _syncBlockStore, api) {
15
+ var createPlugin = exports.createPlugin = function createPlugin(options, pmPluginFactoryParams, syncBlockStore, api) {
15
16
  return new _safePlugin.SafePlugin({
16
17
  key: syncedBlockPluginKey,
17
18
  state: {
18
19
  init: function init() {
19
20
  return {};
20
21
  },
21
- apply: function apply(tr, currentPluginState) {
22
- var meta = tr.getMeta(syncedBlockPluginKey);
23
- if (meta) {
24
- return meta;
25
- }
22
+ apply: function apply(_tr, currentPluginState) {
26
23
  return currentPluginState;
27
24
  }
28
25
  },
@@ -34,6 +31,34 @@ var createPlugin = exports.createPlugin = function createPlugin(options, pmPlugi
34
31
  api: api
35
32
  })
36
33
  }
34
+ },
35
+ view: function view(editorView) {
36
+ syncBlockStore.setEditorView(editorView);
37
+ return {
38
+ destroy: function destroy() {
39
+ syncBlockStore.setEditorView(undefined);
40
+ }
41
+ };
42
+ },
43
+ filterTransaction: function filterTransaction(tr, state) {
44
+ // Ignore transactions that don't change the document
45
+ // or are from remote (collab) or already confirmed sync block deletion
46
+ // We only care about local changes that change the document
47
+ // and are not yet confirmed for sync block deletion
48
+ if (!tr.docChanged || !(syncBlockStore !== null && syncBlockStore !== void 0 && syncBlockStore.requireConfirmationBeforeDelete()) || Boolean(tr.getMeta('isRemote')) || Boolean(tr.getMeta('isConfirmedSyncBlockDeletion'))) {
49
+ return true;
50
+ }
51
+ var _trackSyncBlocks = (0, _trackSyncBlocks2.trackSyncBlocks)(syncBlockStore, tr, state),
52
+ removed = _trackSyncBlocks.removed;
53
+ if (removed.length > 0) {
54
+ // If there are source sync blocks being removed, and we need to confirm with user before deleting,
55
+ // we block the transaction here, and wait for user confirmation to proceed with deletion.
56
+ // See editor-common/src/sync-block/sync-block-store-manager.ts for how we handle user confirmation and
57
+ // proceed with deletion.
58
+ syncBlockStore.deleteSyncBlocksWithConfirmation(tr, removed);
59
+ return false;
60
+ }
61
+ return true;
37
62
  }
38
63
  });
39
64
  };
@@ -0,0 +1,54 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.trackSyncBlocks = void 0;
7
+ var _transform = require("@atlaskit/editor-prosemirror/transform");
8
+ var trackSyncBlocks = exports.trackSyncBlocks = function trackSyncBlocks(storeManager, tr, state) {
9
+ var sourceSyncBlockRemoved = {};
10
+ var sourceSyncBlockAdded = {};
11
+ tr.steps.map(function (step) {
12
+ if (step instanceof _transform.ReplaceStep || step instanceof _transform.ReplaceAroundStep) {
13
+ var from = step.from,
14
+ to = step.to;
15
+ // replaced a range, check for deleted syncBlock
16
+ if (from !== to) {
17
+ state.doc.nodesBetween(step.from, step.to, function (node) {
18
+ if (storeManager.isSourceBlock(node)) {
19
+ if (sourceSyncBlockAdded[node.attrs.localId]) {
20
+ // If a source block added and then removed in the same transaction,
21
+ // we treat it as no-op.
22
+ delete sourceSyncBlockAdded[node.attrs.localId];
23
+ } else {
24
+ sourceSyncBlockRemoved[node.attrs.localId] = node.attrs;
25
+ }
26
+ }
27
+ // we don't need to go deeper
28
+ return false;
29
+ });
30
+ }
31
+
32
+ // replaced content, check for inserted syncBlock
33
+ if (step.slice.content.size > 0) {
34
+ step.slice.content.nodesBetween(0, step.slice.content.size, function (node) {
35
+ if (storeManager.isSourceBlock(node)) {
36
+ if (sourceSyncBlockRemoved[node.attrs.localId]) {
37
+ // If a source block is removed and added back in the same transaction,
38
+ // we treat it as no-op.
39
+ delete sourceSyncBlockRemoved[node.attrs.localId];
40
+ } else {
41
+ sourceSyncBlockAdded[node.attrs.localId] = node.attrs;
42
+ }
43
+ }
44
+ // we don't need to go deeper
45
+ return false;
46
+ });
47
+ }
48
+ }
49
+ });
50
+ return {
51
+ removed: Object.values(sourceSyncBlockRemoved),
52
+ added: Object.values(sourceSyncBlockAdded)
53
+ };
54
+ };
@@ -11,6 +11,7 @@ var _syncBlock = require("@atlaskit/editor-common/sync-block");
11
11
  var _smartLink = _interopRequireDefault(require("@atlaskit/icon/core/smart-link"));
12
12
  var _actions = require("./pm-plugins/actions");
13
13
  var _main = require("./pm-plugins/main");
14
+ var _ContentComponent = require("./ui/ContentComponent");
14
15
  var _floatingToolbar = require("./ui/floating-toolbar");
15
16
  var syncedBlockPlugin = exports.syncedBlockPlugin = function syncedBlockPlugin(_ref) {
16
17
  var config = _ref.config,
@@ -54,6 +55,11 @@ var syncedBlockPlugin = exports.syncedBlockPlugin = function syncedBlockPlugin(_
54
55
  floatingToolbar: function floatingToolbar(state, intl, providerFactory) {
55
56
  return (0, _floatingToolbar.getToolbarConfig)(state, intl, config, providerFactory);
56
57
  }
58
+ },
59
+ contentComponent: function contentComponent() {
60
+ return /*#__PURE__*/_react.default.createElement(_ContentComponent.ContentComponent, {
61
+ syncBlockStoreManager: syncBlockStore
62
+ });
57
63
  }
58
64
  };
59
65
  };
@@ -0,0 +1,55 @@
1
+ "use strict";
2
+
3
+ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4
+ var _typeof = require("@babel/runtime/helpers/typeof");
5
+ Object.defineProperty(exports, "__esModule", {
6
+ value: true
7
+ });
8
+ exports.ContentComponent = void 0;
9
+ var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
10
+ var _react = _interopRequireWildcard(require("react"));
11
+ var _new = _interopRequireDefault(require("@atlaskit/button/new"));
12
+ var _modalDialog = _interopRequireWildcard(require("@atlaskit/modal-dialog"));
13
+ 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); }
14
+ var ContentComponent = exports.ContentComponent = function ContentComponent(_ref) {
15
+ var syncBlockStoreManager = _ref.syncBlockStoreManager;
16
+ var _useState = (0, _react.useState)(false),
17
+ _useState2 = (0, _slicedToArray2.default)(_useState, 2),
18
+ isOpen = _useState2[0],
19
+ setIsOpen = _useState2[1];
20
+ var resolverRef = _react.default.useRef(undefined);
21
+ var handleClose = (0, _react.useCallback)(function (confirm) {
22
+ return function () {
23
+ if (resolverRef.current) {
24
+ resolverRef.current(confirm);
25
+ resolverRef.current = undefined;
26
+ }
27
+ setIsOpen(false);
28
+ };
29
+ }, []);
30
+ var confirmationCallback = (0, _react.useCallback)(function () {
31
+ setIsOpen(true);
32
+ var confirmedPromise = new Promise(function (resolve) {
33
+ resolverRef.current = resolve;
34
+ });
35
+ return confirmedPromise;
36
+ }, []);
37
+ (0, _react.useEffect)(function () {
38
+ var unregister = syncBlockStoreManager.registerConfirmationCallback(confirmationCallback);
39
+ return function () {
40
+ unregister();
41
+ };
42
+ }, [syncBlockStoreManager, confirmationCallback]);
43
+ return /*#__PURE__*/_react.default.createElement(_modalDialog.ModalTransition, null, isOpen && /*#__PURE__*/_react.default.createElement(_modalDialog.default, {
44
+ onClose: handleClose(false)
45
+ }, /*#__PURE__*/_react.default.createElement(_modalDialog.ModalHeader, {
46
+ hasCloseButton: true
47
+ }, /*#__PURE__*/_react.default.createElement(_modalDialog.ModalTitle, null, "Confirmation")), /*#__PURE__*/_react.default.createElement(_modalDialog.ModalBody, null, /*#__PURE__*/_react.default.createElement("div", null, "Are you sure you want to delete this synced block?")), /*#__PURE__*/_react.default.createElement(_modalDialog.ModalFooter, null, /*#__PURE__*/_react.default.createElement(_new.default, {
48
+ appearance: "subtle",
49
+ onClick: handleClose(false)
50
+ }, "Cancel"), /*#__PURE__*/_react.default.createElement(_new.default, {
51
+ appearance: "danger",
52
+ onClick: handleClose(true),
53
+ autoFocus: true
54
+ }, "Delete"))));
55
+ };
@@ -1,22 +1,19 @@
1
1
  import { SafePlugin } from '@atlaskit/editor-common/safe-plugin';
2
2
  import { PluginKey } from '@atlaskit/editor-prosemirror/state';
3
3
  import { lazySyncBlockView } from '../nodeviews/lazySyncedBlock';
4
+ import { trackSyncBlocks } from './utils/track-sync-blocks';
4
5
  export const syncedBlockPluginKey = new PluginKey('syncedBlockPlugin');
5
6
 
6
7
  // eslint-disable-next-line @typescript-eslint/no-empty-object-type
7
8
 
8
- export const createPlugin = (options, pmPluginFactoryParams, _syncBlockStore, api) => {
9
+ export const createPlugin = (options, pmPluginFactoryParams, syncBlockStore, api) => {
9
10
  return new SafePlugin({
10
11
  key: syncedBlockPluginKey,
11
12
  state: {
12
13
  init() {
13
14
  return {};
14
15
  },
15
- apply: (tr, currentPluginState) => {
16
- const meta = tr.getMeta(syncedBlockPluginKey);
17
- if (meta) {
18
- return meta;
19
- }
16
+ apply: (_tr, currentPluginState) => {
20
17
  return currentPluginState;
21
18
  }
22
19
  },
@@ -28,6 +25,35 @@ export const createPlugin = (options, pmPluginFactoryParams, _syncBlockStore, ap
28
25
  api
29
26
  })
30
27
  }
28
+ },
29
+ view: editorView => {
30
+ syncBlockStore.setEditorView(editorView);
31
+ return {
32
+ destroy() {
33
+ syncBlockStore.setEditorView(undefined);
34
+ }
35
+ };
36
+ },
37
+ filterTransaction: (tr, state) => {
38
+ // Ignore transactions that don't change the document
39
+ // or are from remote (collab) or already confirmed sync block deletion
40
+ // We only care about local changes that change the document
41
+ // and are not yet confirmed for sync block deletion
42
+ if (!tr.docChanged || !(syncBlockStore !== null && syncBlockStore !== void 0 && syncBlockStore.requireConfirmationBeforeDelete()) || Boolean(tr.getMeta('isRemote')) || Boolean(tr.getMeta('isConfirmedSyncBlockDeletion'))) {
43
+ return true;
44
+ }
45
+ const {
46
+ removed
47
+ } = trackSyncBlocks(syncBlockStore, tr, state);
48
+ if (removed.length > 0) {
49
+ // If there are source sync blocks being removed, and we need to confirm with user before deleting,
50
+ // we block the transaction here, and wait for user confirmation to proceed with deletion.
51
+ // See editor-common/src/sync-block/sync-block-store-manager.ts for how we handle user confirmation and
52
+ // proceed with deletion.
53
+ syncBlockStore.deleteSyncBlocksWithConfirmation(tr, removed);
54
+ return false;
55
+ }
56
+ return true;
31
57
  }
32
58
  });
33
59
  };
@@ -0,0 +1,50 @@
1
+ import { ReplaceAroundStep, ReplaceStep } from '@atlaskit/editor-prosemirror/transform';
2
+ export const trackSyncBlocks = (storeManager, tr, state) => {
3
+ const sourceSyncBlockRemoved = {};
4
+ const sourceSyncBlockAdded = {};
5
+ tr.steps.map(step => {
6
+ if (step instanceof ReplaceStep || step instanceof ReplaceAroundStep) {
7
+ const {
8
+ from,
9
+ to
10
+ } = step;
11
+ // replaced a range, check for deleted syncBlock
12
+ if (from !== to) {
13
+ state.doc.nodesBetween(step.from, step.to, node => {
14
+ if (storeManager.isSourceBlock(node)) {
15
+ if (sourceSyncBlockAdded[node.attrs.localId]) {
16
+ // If a source block added and then removed in the same transaction,
17
+ // we treat it as no-op.
18
+ delete sourceSyncBlockAdded[node.attrs.localId];
19
+ } else {
20
+ sourceSyncBlockRemoved[node.attrs.localId] = node.attrs;
21
+ }
22
+ }
23
+ // we don't need to go deeper
24
+ return false;
25
+ });
26
+ }
27
+
28
+ // replaced content, check for inserted syncBlock
29
+ if (step.slice.content.size > 0) {
30
+ step.slice.content.nodesBetween(0, step.slice.content.size, node => {
31
+ if (storeManager.isSourceBlock(node)) {
32
+ if (sourceSyncBlockRemoved[node.attrs.localId]) {
33
+ // If a source block is removed and added back in the same transaction,
34
+ // we treat it as no-op.
35
+ delete sourceSyncBlockRemoved[node.attrs.localId];
36
+ } else {
37
+ sourceSyncBlockAdded[node.attrs.localId] = node.attrs;
38
+ }
39
+ }
40
+ // we don't need to go deeper
41
+ return false;
42
+ });
43
+ }
44
+ }
45
+ });
46
+ return {
47
+ removed: Object.values(sourceSyncBlockRemoved),
48
+ added: Object.values(sourceSyncBlockAdded)
49
+ };
50
+ };
@@ -4,6 +4,7 @@ import { SyncBlockStoreManager } from '@atlaskit/editor-common/sync-block';
4
4
  import SmartLinkIcon from '@atlaskit/icon/core/smart-link';
5
5
  import { createSyncedBlock } from './pm-plugins/actions';
6
6
  import { createPlugin } from './pm-plugins/main';
7
+ import { ContentComponent } from './ui/ContentComponent';
7
8
  import { getToolbarConfig } from './ui/floating-toolbar';
8
9
  export const syncedBlockPlugin = ({
9
10
  config,
@@ -40,6 +41,11 @@ export const syncedBlockPlugin = ({
40
41
  }
41
42
  }],
42
43
  floatingToolbar: (state, intl, providerFactory) => getToolbarConfig(state, intl, config, providerFactory)
44
+ },
45
+ contentComponent: () => {
46
+ return /*#__PURE__*/React.createElement(ContentComponent, {
47
+ syncBlockStoreManager: syncBlockStore
48
+ });
43
49
  }
44
50
  };
45
51
  };
@@ -0,0 +1,41 @@
1
+ import React, { useCallback, useEffect, useState } from 'react';
2
+ import Button from '@atlaskit/button/new';
3
+ import ModalDialog, { ModalBody, ModalFooter, ModalHeader, ModalTitle, ModalTransition } from '@atlaskit/modal-dialog';
4
+ export const ContentComponent = ({
5
+ syncBlockStoreManager
6
+ }) => {
7
+ const [isOpen, setIsOpen] = useState(false);
8
+ const resolverRef = React.useRef(undefined);
9
+ const handleClose = useCallback(confirm => () => {
10
+ if (resolverRef.current) {
11
+ resolverRef.current(confirm);
12
+ resolverRef.current = undefined;
13
+ }
14
+ setIsOpen(false);
15
+ }, []);
16
+ const confirmationCallback = useCallback(() => {
17
+ setIsOpen(true);
18
+ const confirmedPromise = new Promise(resolve => {
19
+ resolverRef.current = resolve;
20
+ });
21
+ return confirmedPromise;
22
+ }, []);
23
+ useEffect(() => {
24
+ const unregister = syncBlockStoreManager.registerConfirmationCallback(confirmationCallback);
25
+ return () => {
26
+ unregister();
27
+ };
28
+ }, [syncBlockStoreManager, confirmationCallback]);
29
+ return /*#__PURE__*/React.createElement(ModalTransition, null, isOpen && /*#__PURE__*/React.createElement(ModalDialog, {
30
+ onClose: handleClose(false)
31
+ }, /*#__PURE__*/React.createElement(ModalHeader, {
32
+ hasCloseButton: true
33
+ }, /*#__PURE__*/React.createElement(ModalTitle, null, "Confirmation")), /*#__PURE__*/React.createElement(ModalBody, null, /*#__PURE__*/React.createElement("div", null, "Are you sure you want to delete this synced block?")), /*#__PURE__*/React.createElement(ModalFooter, null, /*#__PURE__*/React.createElement(Button, {
34
+ appearance: "subtle",
35
+ onClick: handleClose(false)
36
+ }, "Cancel"), /*#__PURE__*/React.createElement(Button, {
37
+ appearance: "danger",
38
+ onClick: handleClose(true),
39
+ autoFocus: true
40
+ }, "Delete"))));
41
+ };
@@ -1,22 +1,19 @@
1
1
  import { SafePlugin } from '@atlaskit/editor-common/safe-plugin';
2
2
  import { PluginKey } from '@atlaskit/editor-prosemirror/state';
3
3
  import { lazySyncBlockView } from '../nodeviews/lazySyncedBlock';
4
+ import { trackSyncBlocks } from './utils/track-sync-blocks';
4
5
  export var syncedBlockPluginKey = new PluginKey('syncedBlockPlugin');
5
6
 
6
7
  // eslint-disable-next-line @typescript-eslint/no-empty-object-type
7
8
 
8
- export var createPlugin = function createPlugin(options, pmPluginFactoryParams, _syncBlockStore, api) {
9
+ export var createPlugin = function createPlugin(options, pmPluginFactoryParams, syncBlockStore, api) {
9
10
  return new SafePlugin({
10
11
  key: syncedBlockPluginKey,
11
12
  state: {
12
13
  init: function init() {
13
14
  return {};
14
15
  },
15
- apply: function apply(tr, currentPluginState) {
16
- var meta = tr.getMeta(syncedBlockPluginKey);
17
- if (meta) {
18
- return meta;
19
- }
16
+ apply: function apply(_tr, currentPluginState) {
20
17
  return currentPluginState;
21
18
  }
22
19
  },
@@ -28,6 +25,34 @@ export var createPlugin = function createPlugin(options, pmPluginFactoryParams,
28
25
  api: api
29
26
  })
30
27
  }
28
+ },
29
+ view: function view(editorView) {
30
+ syncBlockStore.setEditorView(editorView);
31
+ return {
32
+ destroy: function destroy() {
33
+ syncBlockStore.setEditorView(undefined);
34
+ }
35
+ };
36
+ },
37
+ filterTransaction: function filterTransaction(tr, state) {
38
+ // Ignore transactions that don't change the document
39
+ // or are from remote (collab) or already confirmed sync block deletion
40
+ // We only care about local changes that change the document
41
+ // and are not yet confirmed for sync block deletion
42
+ if (!tr.docChanged || !(syncBlockStore !== null && syncBlockStore !== void 0 && syncBlockStore.requireConfirmationBeforeDelete()) || Boolean(tr.getMeta('isRemote')) || Boolean(tr.getMeta('isConfirmedSyncBlockDeletion'))) {
43
+ return true;
44
+ }
45
+ var _trackSyncBlocks = trackSyncBlocks(syncBlockStore, tr, state),
46
+ removed = _trackSyncBlocks.removed;
47
+ if (removed.length > 0) {
48
+ // If there are source sync blocks being removed, and we need to confirm with user before deleting,
49
+ // we block the transaction here, and wait for user confirmation to proceed with deletion.
50
+ // See editor-common/src/sync-block/sync-block-store-manager.ts for how we handle user confirmation and
51
+ // proceed with deletion.
52
+ syncBlockStore.deleteSyncBlocksWithConfirmation(tr, removed);
53
+ return false;
54
+ }
55
+ return true;
31
56
  }
32
57
  });
33
58
  };
@@ -0,0 +1,48 @@
1
+ import { ReplaceAroundStep, ReplaceStep } from '@atlaskit/editor-prosemirror/transform';
2
+ export var trackSyncBlocks = function trackSyncBlocks(storeManager, tr, state) {
3
+ var sourceSyncBlockRemoved = {};
4
+ var sourceSyncBlockAdded = {};
5
+ tr.steps.map(function (step) {
6
+ if (step instanceof ReplaceStep || step instanceof ReplaceAroundStep) {
7
+ var from = step.from,
8
+ to = step.to;
9
+ // replaced a range, check for deleted syncBlock
10
+ if (from !== to) {
11
+ state.doc.nodesBetween(step.from, step.to, function (node) {
12
+ if (storeManager.isSourceBlock(node)) {
13
+ if (sourceSyncBlockAdded[node.attrs.localId]) {
14
+ // If a source block added and then removed in the same transaction,
15
+ // we treat it as no-op.
16
+ delete sourceSyncBlockAdded[node.attrs.localId];
17
+ } else {
18
+ sourceSyncBlockRemoved[node.attrs.localId] = node.attrs;
19
+ }
20
+ }
21
+ // we don't need to go deeper
22
+ return false;
23
+ });
24
+ }
25
+
26
+ // replaced content, check for inserted syncBlock
27
+ if (step.slice.content.size > 0) {
28
+ step.slice.content.nodesBetween(0, step.slice.content.size, function (node) {
29
+ if (storeManager.isSourceBlock(node)) {
30
+ if (sourceSyncBlockRemoved[node.attrs.localId]) {
31
+ // If a source block is removed and added back in the same transaction,
32
+ // we treat it as no-op.
33
+ delete sourceSyncBlockRemoved[node.attrs.localId];
34
+ } else {
35
+ sourceSyncBlockAdded[node.attrs.localId] = node.attrs;
36
+ }
37
+ }
38
+ // we don't need to go deeper
39
+ return false;
40
+ });
41
+ }
42
+ }
43
+ });
44
+ return {
45
+ removed: Object.values(sourceSyncBlockRemoved),
46
+ added: Object.values(sourceSyncBlockAdded)
47
+ };
48
+ };
@@ -4,6 +4,7 @@ import { SyncBlockStoreManager } from '@atlaskit/editor-common/sync-block';
4
4
  import SmartLinkIcon from '@atlaskit/icon/core/smart-link';
5
5
  import { createSyncedBlock } from './pm-plugins/actions';
6
6
  import { createPlugin } from './pm-plugins/main';
7
+ import { ContentComponent } from './ui/ContentComponent';
7
8
  import { getToolbarConfig } from './ui/floating-toolbar';
8
9
  export var syncedBlockPlugin = function syncedBlockPlugin(_ref) {
9
10
  var config = _ref.config,
@@ -47,6 +48,11 @@ export var syncedBlockPlugin = function syncedBlockPlugin(_ref) {
47
48
  floatingToolbar: function floatingToolbar(state, intl, providerFactory) {
48
49
  return getToolbarConfig(state, intl, config, providerFactory);
49
50
  }
51
+ },
52
+ contentComponent: function contentComponent() {
53
+ return /*#__PURE__*/React.createElement(ContentComponent, {
54
+ syncBlockStoreManager: syncBlockStore
55
+ });
50
56
  }
51
57
  };
52
58
  };
@@ -0,0 +1,46 @@
1
+ import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
2
+ import React, { useCallback, useEffect, useState } from 'react';
3
+ import Button from '@atlaskit/button/new';
4
+ import ModalDialog, { ModalBody, ModalFooter, ModalHeader, ModalTitle, ModalTransition } from '@atlaskit/modal-dialog';
5
+ export var ContentComponent = function ContentComponent(_ref) {
6
+ var syncBlockStoreManager = _ref.syncBlockStoreManager;
7
+ var _useState = useState(false),
8
+ _useState2 = _slicedToArray(_useState, 2),
9
+ isOpen = _useState2[0],
10
+ setIsOpen = _useState2[1];
11
+ var resolverRef = React.useRef(undefined);
12
+ var handleClose = useCallback(function (confirm) {
13
+ return function () {
14
+ if (resolverRef.current) {
15
+ resolverRef.current(confirm);
16
+ resolverRef.current = undefined;
17
+ }
18
+ setIsOpen(false);
19
+ };
20
+ }, []);
21
+ var confirmationCallback = useCallback(function () {
22
+ setIsOpen(true);
23
+ var confirmedPromise = new Promise(function (resolve) {
24
+ resolverRef.current = resolve;
25
+ });
26
+ return confirmedPromise;
27
+ }, []);
28
+ useEffect(function () {
29
+ var unregister = syncBlockStoreManager.registerConfirmationCallback(confirmationCallback);
30
+ return function () {
31
+ unregister();
32
+ };
33
+ }, [syncBlockStoreManager, confirmationCallback]);
34
+ return /*#__PURE__*/React.createElement(ModalTransition, null, isOpen && /*#__PURE__*/React.createElement(ModalDialog, {
35
+ onClose: handleClose(false)
36
+ }, /*#__PURE__*/React.createElement(ModalHeader, {
37
+ hasCloseButton: true
38
+ }, /*#__PURE__*/React.createElement(ModalTitle, null, "Confirmation")), /*#__PURE__*/React.createElement(ModalBody, null, /*#__PURE__*/React.createElement("div", null, "Are you sure you want to delete this synced block?")), /*#__PURE__*/React.createElement(ModalFooter, null, /*#__PURE__*/React.createElement(Button, {
39
+ appearance: "subtle",
40
+ onClick: handleClose(false)
41
+ }, "Cancel"), /*#__PURE__*/React.createElement(Button, {
42
+ appearance: "danger",
43
+ onClick: handleClose(true),
44
+ autoFocus: true
45
+ }, "Delete"))));
46
+ };
@@ -5,5 +5,5 @@ import { PluginKey } from '@atlaskit/editor-prosemirror/state';
5
5
  import type { SyncedBlockPlugin, SyncedBlockPluginOptions } from '../syncedBlockPluginType';
6
6
  export declare const syncedBlockPluginKey: PluginKey<any>;
7
7
  type SyncedBlockPluginState = {};
8
- export declare const createPlugin: (options: SyncedBlockPluginOptions | undefined, pmPluginFactoryParams: PMPluginFactoryParams, _syncBlockStore: SyncBlockStoreManager, api?: ExtractInjectionAPI<SyncedBlockPlugin>) => SafePlugin<SyncedBlockPluginState>;
8
+ export declare const createPlugin: (options: SyncedBlockPluginOptions | undefined, pmPluginFactoryParams: PMPluginFactoryParams, syncBlockStore: SyncBlockStoreManager, api?: ExtractInjectionAPI<SyncedBlockPlugin>) => SafePlugin<SyncedBlockPluginState>;
9
9
  export {};
@@ -0,0 +1,7 @@
1
+ import type { SyncBlockStoreManager } from '@atlaskit/editor-common/sync-block';
2
+ import type { EditorState, Transaction } from '@atlaskit/editor-prosemirror/state';
3
+ import type { SyncBlockAttrs } from '../../syncedBlockPluginType';
4
+ export declare const trackSyncBlocks: (storeManager: SyncBlockStoreManager, tr: Transaction, state: EditorState) => {
5
+ removed: SyncBlockAttrs[];
6
+ added: SyncBlockAttrs[];
7
+ };
@@ -39,3 +39,7 @@ export type SyncedBlockPlugin = NextEditorPlugin<'syncedBlock', {
39
39
  dependencies: [SelectionPlugin];
40
40
  pluginConfiguration: SyncedBlockPluginOptions | undefined;
41
41
  }>;
42
+ export type SyncBlockAttrs = {
43
+ localId: string;
44
+ resourceId: string;
45
+ };
@@ -0,0 +1,5 @@
1
+ import React from 'react';
2
+ import type { SyncBlockStoreManager } from '@atlaskit/editor-common/sync-block';
3
+ export declare const ContentComponent: ({ syncBlockStoreManager, }: {
4
+ syncBlockStoreManager: SyncBlockStoreManager;
5
+ }) => React.JSX.Element;
@@ -5,5 +5,5 @@ import { PluginKey } from '@atlaskit/editor-prosemirror/state';
5
5
  import type { SyncedBlockPlugin, SyncedBlockPluginOptions } from '../syncedBlockPluginType';
6
6
  export declare const syncedBlockPluginKey: PluginKey<any>;
7
7
  type SyncedBlockPluginState = {};
8
- export declare const createPlugin: (options: SyncedBlockPluginOptions | undefined, pmPluginFactoryParams: PMPluginFactoryParams, _syncBlockStore: SyncBlockStoreManager, api?: ExtractInjectionAPI<SyncedBlockPlugin>) => SafePlugin<SyncedBlockPluginState>;
8
+ export declare const createPlugin: (options: SyncedBlockPluginOptions | undefined, pmPluginFactoryParams: PMPluginFactoryParams, syncBlockStore: SyncBlockStoreManager, api?: ExtractInjectionAPI<SyncedBlockPlugin>) => SafePlugin<SyncedBlockPluginState>;
9
9
  export {};
@@ -0,0 +1,7 @@
1
+ import type { SyncBlockStoreManager } from '@atlaskit/editor-common/sync-block';
2
+ import type { EditorState, Transaction } from '@atlaskit/editor-prosemirror/state';
3
+ import type { SyncBlockAttrs } from '../../syncedBlockPluginType';
4
+ export declare const trackSyncBlocks: (storeManager: SyncBlockStoreManager, tr: Transaction, state: EditorState) => {
5
+ removed: SyncBlockAttrs[];
6
+ added: SyncBlockAttrs[];
7
+ };
@@ -41,3 +41,7 @@ export type SyncedBlockPlugin = NextEditorPlugin<'syncedBlock', {
41
41
  ];
42
42
  pluginConfiguration: SyncedBlockPluginOptions | undefined;
43
43
  }>;
44
+ export type SyncBlockAttrs = {
45
+ localId: string;
46
+ resourceId: string;
47
+ };
@@ -0,0 +1,5 @@
1
+ import React from 'react';
2
+ import type { SyncBlockStoreManager } from '@atlaskit/editor-common/sync-block';
3
+ export declare const ContentComponent: ({ syncBlockStoreManager, }: {
4
+ syncBlockStoreManager: SyncBlockStoreManager;
5
+ }) => React.JSX.Element;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/editor-plugin-synced-block",
3
- "version": "2.1.1",
3
+ "version": "2.2.1",
4
4
  "description": "SyncedBlock plugin for @atlaskit/editor-core",
5
5
  "author": "Atlassian Pty Ltd",
6
6
  "license": "Apache-2.0",
@@ -29,15 +29,17 @@
29
29
  "atlaskit:src": "src/index.ts",
30
30
  "dependencies": {
31
31
  "@atlaskit/adf-schema": "^51.1.2",
32
+ "@atlaskit/button": "23.4.9",
32
33
  "@atlaskit/editor-plugin-selection": "^5.0.0",
33
34
  "@atlaskit/editor-prosemirror": "7.0.0",
34
- "@atlaskit/icon": "28.2.1",
35
+ "@atlaskit/icon": "28.3.0",
36
+ "@atlaskit/modal-dialog": "^14.3.0",
35
37
  "@babel/runtime": "^7.0.0",
36
38
  "react-intl-next": "npm:react-intl@^5.18.1",
37
39
  "uuid": "^3.1.0"
38
40
  },
39
41
  "peerDependencies": {
40
- "@atlaskit/editor-common": "^109.10.0",
42
+ "@atlaskit/editor-common": "^109.14.0",
41
43
  "react": "^18.2.0"
42
44
  },
43
45
  "devDependencies": {