@atlaskit/editor-plugin-synced-block 4.2.7 → 4.2.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/afm-cc/tsconfig.json +9 -0
  3. package/afm-jira/tsconfig.json +10 -1
  4. package/afm-products/tsconfig.json +9 -0
  5. package/dist/cjs/nodeviews/bodiedSyncedBlock.js +27 -5
  6. package/dist/cjs/pm-plugins/main.js +83 -39
  7. package/dist/cjs/pm-plugins/utils/ignore-dom-event.js +30 -0
  8. package/dist/cjs/pm-plugins/utils/track-sync-blocks.js +49 -1
  9. package/dist/cjs/pm-plugins/utils/utils.js +6 -2
  10. package/dist/cjs/syncedBlockPlugin.js +16 -1
  11. package/dist/cjs/types/index.js +12 -0
  12. package/dist/cjs/ui/CreateSyncedBlockButton.js +10 -5
  13. package/dist/cjs/ui/CreateSyncedBlockDropdownItem.js +13 -11
  14. package/dist/cjs/ui/CreateSyncedBlockItem.js +17 -15
  15. package/dist/cjs/ui/DeleteConfirmationModal.js +12 -2
  16. package/dist/cjs/ui/Flag.js +67 -0
  17. package/dist/cjs/ui/floating-toolbar.js +3 -2
  18. package/dist/es2019/nodeviews/bodiedSyncedBlock.js +18 -0
  19. package/dist/es2019/pm-plugins/main.js +89 -40
  20. package/dist/es2019/pm-plugins/utils/ignore-dom-event.js +26 -0
  21. package/dist/es2019/pm-plugins/utils/track-sync-blocks.js +41 -0
  22. package/dist/es2019/pm-plugins/utils/utils.js +6 -2
  23. package/dist/es2019/syncedBlockPlugin.js +18 -2
  24. package/dist/es2019/types/index.js +6 -0
  25. package/dist/es2019/ui/CreateSyncedBlockButton.js +10 -4
  26. package/dist/es2019/ui/CreateSyncedBlockDropdownItem.js +13 -11
  27. package/dist/es2019/ui/CreateSyncedBlockItem.js +18 -14
  28. package/dist/es2019/ui/DeleteConfirmationModal.js +13 -2
  29. package/dist/es2019/ui/Flag.js +66 -0
  30. package/dist/es2019/ui/floating-toolbar.js +3 -2
  31. package/dist/esm/nodeviews/bodiedSyncedBlock.js +27 -5
  32. package/dist/esm/pm-plugins/main.js +85 -41
  33. package/dist/esm/pm-plugins/utils/ignore-dom-event.js +24 -0
  34. package/dist/esm/pm-plugins/utils/track-sync-blocks.js +48 -0
  35. package/dist/esm/pm-plugins/utils/utils.js +6 -2
  36. package/dist/esm/syncedBlockPlugin.js +17 -2
  37. package/dist/esm/types/index.js +6 -0
  38. package/dist/esm/ui/CreateSyncedBlockButton.js +10 -5
  39. package/dist/esm/ui/CreateSyncedBlockDropdownItem.js +13 -11
  40. package/dist/esm/ui/CreateSyncedBlockItem.js +17 -15
  41. package/dist/esm/ui/DeleteConfirmationModal.js +12 -2
  42. package/dist/esm/ui/Flag.js +58 -0
  43. package/dist/esm/ui/floating-toolbar.js +3 -2
  44. package/dist/types/nodeviews/bodiedSyncedBlock.d.ts +1 -1
  45. package/dist/types/pm-plugins/main.d.ts +4 -2
  46. package/dist/types/pm-plugins/utils/ignore-dom-event.d.ts +8 -0
  47. package/dist/types/pm-plugins/utils/track-sync-blocks.d.ts +5 -0
  48. package/dist/types/syncedBlockPluginType.d.ts +5 -1
  49. package/dist/types/types/index.d.ts +11 -0
  50. package/dist/types/ui/DeleteConfirmationModal.d.ts +4 -1
  51. package/dist/types/ui/Flag.d.ts +8 -0
  52. package/dist/types-ts4.5/nodeviews/bodiedSyncedBlock.d.ts +1 -1
  53. package/dist/types-ts4.5/pm-plugins/main.d.ts +4 -2
  54. package/dist/types-ts4.5/pm-plugins/utils/ignore-dom-event.d.ts +8 -0
  55. package/dist/types-ts4.5/pm-plugins/utils/track-sync-blocks.d.ts +5 -0
  56. package/dist/types-ts4.5/syncedBlockPluginType.d.ts +5 -1
  57. package/dist/types-ts4.5/types/index.d.ts +11 -0
  58. package/dist/types-ts4.5/ui/DeleteConfirmationModal.d.ts +4 -1
  59. package/dist/types-ts4.5/ui/Flag.d.ts +8 -0
  60. package/package.json +6 -3
@@ -13,17 +13,22 @@ var _messages = require("@atlaskit/editor-common/messages");
13
13
  var _editorToolbar = require("@atlaskit/editor-toolbar");
14
14
  var _blockSynced = _interopRequireDefault(require("@atlaskit/icon-lab/core/block-synced"));
15
15
  var _lozenge = _interopRequireDefault(require("@atlaskit/lozenge"));
16
- var _compiled = require("@atlaskit/primitives/compiled");
17
16
  var _utils = require("../pm-plugins/utils/utils");
18
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); }
19
18
  var CreateSyncedBlockItem = exports.CreateSyncedBlockItem = function CreateSyncedBlockItem(_ref) {
20
19
  var api = _ref.api;
21
- var intl = (0, _reactIntlNext.useIntl)();
22
- var selection = (0, _hooks.useSharedPluginStateWithSelector)(api, ['selection'], function (states) {
23
- var _states$selectionStat;
24
- return (_states$selectionStat = states.selectionState) === null || _states$selectionStat === void 0 ? void 0 : _states$selectionStat.selection;
25
- });
26
- var isDisabled = Boolean(!selection || !(0, _utils.canBeConvertedToSyncBlock)(selection));
20
+ var _useIntl = (0, _reactIntlNext.useIntl)(),
21
+ formatMessage = _useIntl.formatMessage;
22
+ var _useSharedPluginState = (0, _hooks.useSharedPluginStateWithSelector)(api, ['selection', 'connectivity'], function (states) {
23
+ var _states$selectionStat, _states$connectivityS;
24
+ return {
25
+ selection: (_states$selectionStat = states.selectionState) === null || _states$selectionStat === void 0 ? void 0 : _states$selectionStat.selection,
26
+ mode: (_states$connectivityS = states.connectivityState) === null || _states$connectivityS === void 0 ? void 0 : _states$connectivityS.mode
27
+ };
28
+ }),
29
+ selection = _useSharedPluginState.selection,
30
+ mode = _useSharedPluginState.mode;
31
+ var isDisabled = Boolean(!selection || !(0, _utils.canBeConvertedToSyncBlock)(selection) || mode === 'offline');
27
32
  var onClick = (0, _react.useCallback)(function () {
28
33
  var _api$core, _api$core2;
29
34
  api === null || api === void 0 || (_api$core = api.core) === null || _api$core === void 0 || _api$core.actions.execute(function (_ref2) {
@@ -34,18 +39,15 @@ var CreateSyncedBlockItem = exports.CreateSyncedBlockItem = function CreateSynce
34
39
  });
35
40
  api === null || api === void 0 || (_api$core2 = api.core) === null || _api$core2 === void 0 || _api$core2.actions.focus();
36
41
  }, [api]);
37
- var message = intl.formatMessage(_messages.syncBlockMessages.createSyncBlockLabel);
38
42
  return /*#__PURE__*/_react.default.createElement(_editorToolbar.ToolbarDropdownItem, {
39
43
  onClick: onClick,
40
44
  isDisabled: isDisabled,
41
45
  elemBefore: /*#__PURE__*/_react.default.createElement(_blockSynced.default, {
42
46
  size: "small",
43
47
  label: ""
44
- })
45
- }, /*#__PURE__*/_react.default.createElement(_compiled.Flex, {
46
- alignItems: "center",
47
- gap: "space.050"
48
- }, message, /*#__PURE__*/_react.default.createElement(_lozenge.default, {
49
- appearance: "new"
50
- }, intl.formatMessage(_messages.syncBlockMessages.newLozenge))));
48
+ }),
49
+ elemAfter: /*#__PURE__*/_react.default.createElement(_lozenge.default, {
50
+ appearance: "new"
51
+ }, formatMessage(_messages.syncBlockMessages.newLozenge))
52
+ }, formatMessage(_messages.syncBlockMessages.createSyncBlockLabel));
51
53
  };
@@ -10,12 +10,14 @@ var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/sli
10
10
  var _react = _interopRequireWildcard(require("react"));
11
11
  var _reactIntlNext = require("react-intl-next");
12
12
  var _new = _interopRequireDefault(require("@atlaskit/button/new"));
13
+ var _hooks = require("@atlaskit/editor-common/hooks");
13
14
  var _messages = require("@atlaskit/editor-common/messages");
14
15
  var _modalDialog = _interopRequireWildcard(require("@atlaskit/modal-dialog"));
15
16
  var _compiled = require("@atlaskit/primitives/compiled");
16
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); }
17
18
  var DeleteConfirmationModal = exports.DeleteConfirmationModal = function DeleteConfirmationModal(_ref) {
18
- var syncBlockStoreManager = _ref.syncBlockStoreManager;
19
+ var syncBlockStoreManager = _ref.syncBlockStoreManager,
20
+ api = _ref.api;
19
21
  var _useState = (0, _react.useState)(false),
20
22
  _useState2 = (0, _slicedToArray2.default)(_useState, 2),
21
23
  isOpen = _useState2[0],
@@ -24,6 +26,13 @@ var DeleteConfirmationModal = exports.DeleteConfirmationModal = function DeleteC
24
26
  _useState4 = (0, _slicedToArray2.default)(_useState3, 2),
25
27
  syncBlockCount = _useState4[0],
26
28
  setSyncBlockCount = _useState4[1];
29
+ var _useSharedPluginState = (0, _hooks.useSharedPluginStateWithSelector)(api, ['connectivity'], function (states) {
30
+ var _states$connectivityS;
31
+ return {
32
+ mode: (_states$connectivityS = states.connectivityState) === null || _states$connectivityS === void 0 ? void 0 : _states$connectivityS.mode
33
+ };
34
+ }),
35
+ mode = _useSharedPluginState.mode;
27
36
  var _useIntl = (0, _reactIntlNext.useIntl)(),
28
37
  formatMessage = _useIntl.formatMessage;
29
38
  var resolverRef = _react.default.useRef(undefined);
@@ -64,6 +73,7 @@ var DeleteConfirmationModal = exports.DeleteConfirmationModal = function DeleteC
64
73
  }, formatMessage(_messages.syncBlockMessages.deleteConfirmationModalCancelButton)), /*#__PURE__*/_react.default.createElement(_new.default, {
65
74
  appearance: "warning",
66
75
  onClick: handleClose(true),
67
- autoFocus: true
76
+ autoFocus: true,
77
+ isDisabled: mode === 'offline'
68
78
  }, formatMessage(_messages.syncBlockMessages.deleteConfirmationModalDeleteButton)))));
69
79
  };
@@ -0,0 +1,67 @@
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.Flag = void 0;
9
+ var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
10
+ var _react = _interopRequireDefault(require("react"));
11
+ var _reactIntlNext = require("react-intl-next");
12
+ var _hooks = require("@atlaskit/editor-common/hooks");
13
+ var _messages = require("@atlaskit/editor-common/messages");
14
+ var _flag = _interopRequireWildcard(require("@atlaskit/flag"));
15
+ var _statusError = _interopRequireDefault(require("@atlaskit/icon/core/status-error"));
16
+ var _main = require("../pm-plugins/main");
17
+ var _types = require("../types");
18
+ 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); }
19
+ var flagMap = (0, _defineProperty2.default)((0, _defineProperty2.default)((0, _defineProperty2.default)({}, _types.FLAG_ID.CANNOT_DELETE_WHEN_OFFLINE, {
20
+ title: _messages.syncBlockMessages.failToDeleteTitle,
21
+ description: _messages.syncBlockMessages.failToDeleteWhenOfflineDescription
22
+ }), _types.FLAG_ID.CANNOT_EDIT_WHEN_OFFLINE, {
23
+ title: _messages.syncBlockMessages.failToEditTitle,
24
+ description: _messages.syncBlockMessages.failToEditWhenOfflineDescription
25
+ }), _types.FLAG_ID.CANNOT_CREATE_WHEN_OFFLINE, {
26
+ title: _messages.syncBlockMessages.failToCreateTitle,
27
+ description: _messages.syncBlockMessages.failToCreateWhenOfflineDescription
28
+ });
29
+ var Flag = exports.Flag = function Flag(_ref) {
30
+ var api = _ref.api;
31
+ var _useSharedPluginState = (0, _hooks.useSharedPluginStateWithSelector)(api, ['syncedBlock'], function (states) {
32
+ var _states$syncedBlockSt;
33
+ return {
34
+ showFlag: (_states$syncedBlockSt = states.syncedBlockState) === null || _states$syncedBlockSt === void 0 ? void 0 : _states$syncedBlockSt.showFlag
35
+ };
36
+ }),
37
+ showFlag = _useSharedPluginState.showFlag;
38
+ var _useIntl = (0, _reactIntlNext.useIntl)(),
39
+ formatMessage = _useIntl.formatMessage;
40
+ if (!showFlag) {
41
+ return;
42
+ }
43
+ var _flagMap$showFlag = flagMap[showFlag],
44
+ title = _flagMap$showFlag.title,
45
+ description = _flagMap$showFlag.description;
46
+ var onDismissed = function onDismissed() {
47
+ api === null || api === void 0 || api.core.actions.execute(function (_ref2) {
48
+ var tr = _ref2.tr;
49
+ tr.setMeta(_main.syncedBlockPluginKey, {
50
+ showFlag: false
51
+ });
52
+ return tr;
53
+ });
54
+ api === null || api === void 0 || api.core.actions.focus();
55
+ };
56
+ return /*#__PURE__*/_react.default.createElement(_flag.FlagGroup, null, /*#__PURE__*/_react.default.createElement(_flag.default, {
57
+ onDismissed: onDismissed,
58
+ title: formatMessage(title),
59
+ description: formatMessage(description),
60
+ id: showFlag,
61
+ testId: showFlag,
62
+ icon: /*#__PURE__*/_react.default.createElement(_statusError.default, {
63
+ label: "",
64
+ color: "var(--ds-icon-danger, #C9372C)"
65
+ })
66
+ }));
67
+ };
@@ -20,7 +20,7 @@ function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r
20
20
  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; }
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
- var _api$decorations;
23
+ var _api$decorations, _api$connectivity;
24
24
  var syncBlockObject = (0, _utils2.findSyncBlockOrBodiedSyncBlock)(state.schema, state.selection);
25
25
  if (!syncBlockObject) {
26
26
  return;
@@ -92,6 +92,7 @@ var getToolbarConfig = exports.getToolbarConfig = function getToolbarConfig(stat
92
92
  nodeType: nodeType,
93
93
  items: items,
94
94
  scrollable: true,
95
- groupLabel: formatMessage(_messages.syncBlockMessages.syncBlockGroup)
95
+ groupLabel: formatMessage(_messages.syncBlockMessages.syncBlockGroup),
96
+ visible: (api === null || api === void 0 || (_api$connectivity = api.connectivity) === null || _api$connectivity === void 0 || (_api$connectivity = _api$connectivity.sharedState.currentState()) === null || _api$connectivity === void 0 ? void 0 : _api$connectivity.mode) !== 'offline'
96
97
  };
97
98
  };
@@ -11,6 +11,19 @@ class BodiedSyncBlock extends ReactNodeView {
11
11
  constructor(props) {
12
12
  super(props.node, props.view, props.getPos, props.portalProviderAPI, props.eventDispatcher, props);
13
13
  this.syncBlockStore = props.syncBlockStore;
14
+ this.api = props.api;
15
+ this.handleConnectivityModeChange();
16
+ }
17
+ handleConnectivityModeChange() {
18
+ var _this$api;
19
+ if ((_this$api = this.api) !== null && _this$api !== void 0 && _this$api.connectivity) {
20
+ this.cleanupConnectivityModeListener = this.api.connectivity.sharedState.onChange(({
21
+ nextSharedState
22
+ }) => {
23
+ var _this$contentDOM;
24
+ (_this$contentDOM = this.contentDOM) === null || _this$contentDOM === void 0 ? void 0 : _this$contentDOM.setAttribute('contenteditable', nextSharedState.mode === 'online' ? 'true' : 'false');
25
+ });
26
+ }
14
27
  }
15
28
  createDomRef() {
16
29
  const domRef = document.createElement('div');
@@ -37,6 +50,11 @@ class BodiedSyncBlock extends ReactNodeView {
37
50
  }
38
51
  return undefined;
39
52
  }
53
+ destroy() {
54
+ if (this.cleanupConnectivityModeListener) {
55
+ this.cleanupConnectivityModeListener();
56
+ }
57
+ }
40
58
  }
41
59
  export const bodiedSyncBlockNodeView = ({
42
60
  pluginOptions,
@@ -1,11 +1,14 @@
1
1
  import { SafePlugin } from '@atlaskit/editor-common/safe-plugin';
2
2
  import { createSelectionClickHandler } from '@atlaskit/editor-common/selection';
3
- import { BodiedSyncBlockSharedCssClassName } from '@atlaskit/editor-common/sync-block';
3
+ import { BodiedSyncBlockSharedCssClassName, SyncBlockStateCssClassName } from '@atlaskit/editor-common/sync-block';
4
4
  import { PluginKey } from '@atlaskit/editor-prosemirror/state';
5
+ import { DecorationSet, Decoration } from '@atlaskit/editor-prosemirror/view';
5
6
  import { lazyBodiedSyncBlockView } from '../nodeviews/bodiedLazySyncedBlock';
6
7
  import { lazySyncBlockView } from '../nodeviews/lazySyncedBlock';
8
+ import { FLAG_ID } from '../types';
9
+ import { shouldIgnoreDomEvent } from './utils/ignore-dom-event';
7
10
  import { calculateDecorations } from './utils/selection-decorations';
8
- import { trackSyncBlocks } from './utils/track-sync-blocks';
11
+ import { hasEditInSyncBlock, trackSyncBlocks } from './utils/track-sync-blocks';
9
12
  export const syncedBlockPluginKey = new PluginKey('syncedBlockPlugin');
10
13
  export const createPlugin = (options, pmPluginFactoryParams, syncBlockStore, api) => {
11
14
  const {
@@ -18,27 +21,25 @@ export const createPlugin = (options, pmPluginFactoryParams, syncBlockStore, api
18
21
  const syncBlockNodes = instance.doc.children.filter(node => node.type.name === 'syncBlock');
19
22
  syncBlockStore.fetchSyncBlocksData(syncBlockNodes);
20
23
  return {
21
- decorationSet: calculateDecorations(instance.doc, instance.selection, instance.schema)
24
+ selectionDecorationSet: calculateDecorations(instance.doc, instance.selection, instance.schema),
25
+ showFlag: false
22
26
  };
23
27
  },
24
28
  apply: (tr, currentPluginState, oldEditorState) => {
29
+ var _meta$showFlag;
25
30
  const meta = tr.getMeta(syncedBlockPluginKey);
26
- if (meta) {
27
- return meta;
28
- }
29
- let newState = currentPluginState;
31
+ const {
32
+ showFlag,
33
+ selectionDecorationSet
34
+ } = currentPluginState;
35
+ let newDecorationSet = selectionDecorationSet.map(tr.mapping, tr.doc);
30
36
  if (!tr.selection.eq(oldEditorState.selection)) {
31
- newState = {
32
- ...newState,
33
- decorationSet: calculateDecorations(tr.doc, tr.selection, tr.doc.type.schema)
34
- };
35
- } else if (newState.decorationSet) {
36
- newState = {
37
- ...newState,
38
- decorationSet: newState.decorationSet.map(tr.mapping, tr.doc)
39
- };
37
+ newDecorationSet = calculateDecorations(tr.doc, tr.selection, tr.doc.type.schema);
40
38
  }
41
- return newState;
39
+ return {
40
+ showFlag: (_meta$showFlag = meta === null || meta === void 0 ? void 0 : meta.showFlag) !== null && _meta$showFlag !== void 0 ? _meta$showFlag : showFlag,
41
+ selectionDecorationSet: newDecorationSet
42
+ };
42
43
  }
43
44
  },
44
45
  props: {
@@ -57,12 +58,34 @@ export const createPlugin = (options, pmPluginFactoryParams, syncBlockStore, api
57
58
  })
58
59
  },
59
60
  decorations: state => {
60
- const pluginState = syncedBlockPluginKey.getState(state);
61
- return pluginState === null || pluginState === void 0 ? void 0 : pluginState.decorationSet;
61
+ var _syncedBlockPluginKey, _syncedBlockPluginKey2, _api$connectivity, _api$connectivity$sha;
62
+ const selectionDecorationSet = (_syncedBlockPluginKey = (_syncedBlockPluginKey2 = syncedBlockPluginKey.getState(state)) === null || _syncedBlockPluginKey2 === void 0 ? void 0 : _syncedBlockPluginKey2.selectionDecorationSet) !== null && _syncedBlockPluginKey !== void 0 ? _syncedBlockPluginKey : DecorationSet.empty;
63
+ const {
64
+ doc
65
+ } = state;
66
+ const decorations = [];
67
+ if ((api === null || api === void 0 ? void 0 : (_api$connectivity = api.connectivity) === null || _api$connectivity === void 0 ? void 0 : (_api$connectivity$sha = _api$connectivity.sharedState.currentState()) === null || _api$connectivity$sha === void 0 ? void 0 : _api$connectivity$sha.mode) === 'offline') {
68
+ state.doc.descendants((node, pos) => {
69
+ if (node.type.name === 'bodiedSyncBlock') {
70
+ decorations.push(Decoration.node(pos, pos + node.nodeSize, {
71
+ class: SyncBlockStateCssClassName.disabledClassName
72
+ }));
73
+ }
74
+ });
75
+ }
76
+ return selectionDecorationSet.add(doc, decorations);
62
77
  },
63
78
  handleClickOn: createSelectionClickHandler(['bodiedSyncBlock'], target => !!target.closest(`.${BodiedSyncBlockSharedCssClassName.prefix}`), {
64
79
  useLongPressSelection
65
- })
80
+ }),
81
+ handleDOMEvents: {
82
+ mouseover(view, event) {
83
+ return shouldIgnoreDomEvent(view, event, api);
84
+ },
85
+ mousedown(view, event) {
86
+ return shouldIgnoreDomEvent(view, event, api);
87
+ }
88
+ }
66
89
  },
67
90
  view: editorView => {
68
91
  syncBlockStore.setEditorView(editorView);
@@ -73,37 +96,63 @@ export const createPlugin = (options, pmPluginFactoryParams, syncBlockStore, api
73
96
  };
74
97
  },
75
98
  filterTransaction: (tr, state) => {
99
+ var _api$connectivity2, _api$connectivity2$sh;
100
+ const isOffline = (api === null || api === void 0 ? void 0 : (_api$connectivity2 = api.connectivity) === null || _api$connectivity2 === void 0 ? void 0 : (_api$connectivity2$sh = _api$connectivity2.sharedState.currentState()) === null || _api$connectivity2$sh === void 0 ? void 0 : _api$connectivity2$sh.mode) === 'offline';
101
+ const isConfirmedSyncBlockDeletion = Boolean(tr.getMeta('isConfirmedSyncBlockDeletion'));
76
102
  // Ignore transactions that don't change the document
77
103
  // or are from remote (collab) or already confirmed sync block deletion
78
104
  // We only care about local changes that change the document
79
105
  // and are not yet confirmed for sync block deletion
80
- if (!tr.docChanged || !(syncBlockStore !== null && syncBlockStore !== void 0 && syncBlockStore.requireConfirmationBeforeDelete()) && !syncBlockStore.hasPendingCreation() || Boolean(tr.getMeta('isRemote')) || Boolean(tr.getMeta('isConfirmedSyncBlockDeletion')) || Boolean(tr.getMeta('isCommitSyncBlockCreation'))) {
106
+ if (!tr.docChanged || !(syncBlockStore !== null && syncBlockStore !== void 0 && syncBlockStore.requireConfirmationBeforeDelete()) && !syncBlockStore.hasPendingCreation() || Boolean(tr.getMeta('isRemote')) || Boolean(tr.getMeta('isCommitSyncBlockCreation')) || !isOffline && isConfirmedSyncBlockDeletion) {
81
107
  return true;
82
108
  }
83
109
  const {
84
110
  removed,
85
111
  added
86
112
  } = trackSyncBlocks(syncBlockStore, tr, state);
87
- if (removed.length > 0) {
88
- // If there are source sync blocks being removed, and we need to confirm with user before deleting,
89
- // we block the transaction here, and wait for user confirmation to proceed with deletion.
90
- // See editor-common/src/sync-block/sync-block-store-manager.ts for how we handle user confirmation and
91
- // proceed with deletion.
92
- syncBlockStore.deleteSyncBlocksWithConfirmation(tr, removed);
93
- return false;
94
- }
95
- if (added.length > 0) {
96
- // If there is bodiedSyncBlock node addition and it's waiting for the result of saving the node to backend (syncBlockStore.hasPendingCreation()),
97
- // we need to intercept the transaction and save it in insert callback so that we only insert it to the document when backend call if backend call is successful
98
- // The callback will be evoked by in SourceSyncBlockStoreManager.commitPendingCreation
99
- syncBlockStore.registerCreationCallback(() => {
100
- var _api$core;
101
- api === null || api === void 0 ? void 0 : (_api$core = api.core) === null || _api$core === void 0 ? void 0 : _api$core.actions.execute(() => {
102
- return tr.setMeta('isCommitSyncBlockCreation', true);
113
+ if (!isOffline) {
114
+ if (removed.length > 0) {
115
+ // If there are source sync blocks being removed, and we need to confirm with user before deleting,
116
+ // we block the transaction here, and wait for user confirmation to proceed with deletion.
117
+ // See editor-common/src/sync-block/sync-block-store-manager.ts for how we handle user confirmation and
118
+ // proceed with deletion.
119
+ syncBlockStore.deleteSyncBlocksWithConfirmation(tr, removed);
120
+ return false;
121
+ }
122
+ if (added.length > 0) {
123
+ // If there is bodiedSyncBlock node addition and it's waiting for the result of saving the node to backend (syncBlockStore.hasPendingCreation()),
124
+ // we need to intercept the transaction and save it in insert callback so that we only insert it to the document when backend call if backend call is successful
125
+ // The callback will be evoked by in SourceSyncBlockStoreManager.commitPendingCreation
126
+ syncBlockStore.registerCreationCallback(() => {
127
+ var _api$core;
128
+ api === null || api === void 0 ? void 0 : (_api$core = api.core) === null || _api$core === void 0 ? void 0 : _api$core.actions.execute(() => {
129
+ return tr.setMeta('isCommitSyncBlockCreation', true);
130
+ });
131
+ api === null || api === void 0 ? void 0 : api.core.actions.focus();
132
+ });
133
+ return false;
134
+ }
135
+ } else {
136
+ // Disable node deletion/creation/edition in offline mode and trigger an error flag instead
137
+ let errorFlag = false;
138
+ if (isConfirmedSyncBlockDeletion || removed.length > 0) {
139
+ errorFlag = FLAG_ID.CANNOT_DELETE_WHEN_OFFLINE;
140
+ } else if (added.length > 0) {
141
+ errorFlag = FLAG_ID.CANNOT_CREATE_WHEN_OFFLINE;
142
+ } else if (hasEditInSyncBlock(tr, state)) {
143
+ errorFlag = FLAG_ID.CANNOT_EDIT_WHEN_OFFLINE;
144
+ }
145
+ if (errorFlag) {
146
+ api === null || api === void 0 ? void 0 : api.core.actions.execute(({
147
+ tr
148
+ }) => {
149
+ tr.setMeta(syncedBlockPluginKey, {
150
+ showFlag: errorFlag
151
+ });
152
+ return tr;
103
153
  });
104
- api === null || api === void 0 ? void 0 : api.core.actions.focus();
105
- });
106
- return false;
154
+ return false;
155
+ }
107
156
  }
108
157
  return true;
109
158
  },
@@ -0,0 +1,26 @@
1
+ import { findParentNodeOfTypeClosestToPos } from '@atlaskit/editor-prosemirror/utils';
2
+ /**
3
+ *
4
+ * @returns true if should ignore event happens within bodiedSyncBlock node when offline
5
+ */
6
+ export const shouldIgnoreDomEvent = (view, event, api) => {
7
+ var _api$connectivity, _api$connectivity$sha, _view$posAtCoords;
8
+ if ((api === null || api === void 0 ? void 0 : (_api$connectivity = api.connectivity) === null || _api$connectivity === void 0 ? void 0 : (_api$connectivity$sha = _api$connectivity.sharedState.currentState()) === null || _api$connectivity$sha === void 0 ? void 0 : _api$connectivity$sha.mode) !== 'offline') {
9
+ return;
10
+ }
11
+ const {
12
+ bodiedSyncBlock
13
+ } = view.state.schema.nodes;
14
+ const pos = (_view$posAtCoords = view.posAtCoords({
15
+ left: event.clientX,
16
+ top: event.clientY
17
+ })) === null || _view$posAtCoords === void 0 ? void 0 : _view$posAtCoords.pos;
18
+ if (pos === undefined) {
19
+ return;
20
+ }
21
+ const $pos = view.state.doc.resolve(pos);
22
+ const maybeNode = findParentNodeOfTypeClosestToPos($pos, bodiedSyncBlock);
23
+ if (maybeNode) {
24
+ return true;
25
+ }
26
+ };
@@ -1,4 +1,5 @@
1
1
  import { ReplaceAroundStep, ReplaceStep } from '@atlaskit/editor-prosemirror/transform';
2
+ import { findParentNodeOfTypeClosestToPos } from '@atlaskit/editor-prosemirror/utils';
2
3
  export const trackSyncBlocks = (storeManager, tr, state) => {
3
4
  const sourceSyncBlockRemoved = {};
4
5
  const sourceSyncBlockAdded = {};
@@ -55,4 +56,44 @@ export const trackSyncBlocks = (storeManager, tr, state) => {
55
56
  removed: Object.values(sourceSyncBlockRemoved),
56
57
  added: Object.values(sourceSyncBlockAdded)
57
58
  };
59
+ };
60
+
61
+ /**
62
+ *
63
+ * @returns true if steps modifies children node within bodiedSyncBlock
64
+ */
65
+ export const hasEditInSyncBlock = (tr, state) => {
66
+ const {
67
+ bodiedSyncBlock
68
+ } = state.schema.nodes;
69
+ for (const step of tr.steps) {
70
+ const map = step.getMap();
71
+ const {
72
+ doc
73
+ } = tr;
74
+ const positions = [];
75
+
76
+ // Extract positions from steps dynamically based on applicable properties
77
+ if ('from' in step && typeof step.from === 'number' && 'to' in step && typeof step.to === 'number') {
78
+ const {
79
+ from,
80
+ to
81
+ } = step;
82
+ positions.push(from, to);
83
+ } else if ('pos' in step && typeof step.pos === 'number') {
84
+ const {
85
+ pos
86
+ } = step;
87
+ positions.push(pos);
88
+ }
89
+ for (const pos of positions) {
90
+ const newPos = map.map(pos);
91
+ if (newPos >= 0 && newPos <= doc.content.size) {
92
+ if (findParentNodeOfTypeClosestToPos(doc.resolve(newPos), bodiedSyncBlock)) {
93
+ return true;
94
+ }
95
+ }
96
+ }
97
+ }
98
+ return false;
58
99
  };
@@ -72,8 +72,12 @@ const removeBreakoutMarks = content => {
72
72
  // we only need to recurse at the top level, because breakout has to be on a top level
73
73
  content.forEach(node => {
74
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);
75
+ if (node.isText) {
76
+ nodes.push(node);
77
+ } else {
78
+ const newNode = node.type.create(node.attrs, node.content, filteredMarks);
79
+ nodes.push(newNode);
80
+ }
77
81
  });
78
82
  return Fragment.from(nodes);
79
83
  };
@@ -6,9 +6,10 @@ import { SyncBlockStoreManager } from '@atlaskit/editor-synced-block-provider';
6
6
  import Lozenge from '@atlaskit/lozenge';
7
7
  import { flushBodiedSyncBlocks } from './editor-actions';
8
8
  import { copySyncedBlockReferenceToClipboardEditorCommand, createSyncedBlock } from './editor-commands';
9
- import { createPlugin } from './pm-plugins/main';
9
+ import { createPlugin, syncedBlockPluginKey } from './pm-plugins/main';
10
10
  import { getBlockMenuComponents } from './ui/block-menu-components';
11
11
  import { DeleteConfirmationModal } from './ui/DeleteConfirmationModal';
12
+ import { Flag } from './ui/Flag';
12
13
  import { getToolbarConfig } from './ui/floating-toolbar';
13
14
  import { SyncBlockRefresher } from './ui/SyncBlockRefresher';
14
15
  import { getToolbarComponents } from './ui/toolbar-components';
@@ -60,6 +61,7 @@ export const syncedBlockPlugin = ({
60
61
  description: formatMessage(blockTypeMessages.syncedBlockDescription),
61
62
  priority: 800,
62
63
  keywords: ['synced', 'block', 'synced-block', 'sync', 'sync-block', 'auto', 'update', 'excerpt', 'connect'],
64
+ isDisabledOffline: true,
63
65
  keyshortcut: '',
64
66
  lozenge: /*#__PURE__*/React.createElement(Lozenge, {
65
67
  appearance: "new"
@@ -81,8 +83,22 @@ export const syncedBlockPlugin = ({
81
83
  return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(SyncBlockRefresher, {
82
84
  syncBlockStoreManager: syncBlockStore
83
85
  }), /*#__PURE__*/React.createElement(DeleteConfirmationModal, {
84
- syncBlockStoreManager: syncBlockStore
86
+ syncBlockStoreManager: syncBlockStore,
87
+ api: api
88
+ }), /*#__PURE__*/React.createElement(Flag, {
89
+ api: api
85
90
  }));
91
+ },
92
+ getSharedState: editorState => {
93
+ if (!editorState) {
94
+ return;
95
+ }
96
+ const {
97
+ showFlag
98
+ } = syncedBlockPluginKey.getState(editorState);
99
+ return {
100
+ showFlag
101
+ };
86
102
  }
87
103
  };
88
104
  };
@@ -0,0 +1,6 @@
1
+ export let FLAG_ID = /*#__PURE__*/function (FLAG_ID) {
2
+ FLAG_ID["CANNOT_DELETE_WHEN_OFFLINE"] = "cannot-delete-when-offline";
3
+ FLAG_ID["CANNOT_EDIT_WHEN_OFFLINE"] = "cannot-edit-when-offline";
4
+ FLAG_ID["CANNOT_CREATE_WHEN_OFFLINE"] = "cannot-create-when-offline";
5
+ return FLAG_ID;
6
+ }({});
@@ -9,16 +9,22 @@ export const CreateSyncedBlockButton = ({
9
9
  api
10
10
  }) => {
11
11
  const intl = useIntl();
12
- const selection = useSharedPluginStateWithSelector(api, ['selection'], states => {
13
- var _states$selectionStat;
14
- return (_states$selectionStat = states.selectionState) === null || _states$selectionStat === void 0 ? void 0 : _states$selectionStat.selection;
12
+ const {
13
+ selection,
14
+ mode
15
+ } = useSharedPluginStateWithSelector(api, ['selection', 'connectivity'], states => {
16
+ var _states$selectionStat, _states$connectivityS;
17
+ return {
18
+ selection: (_states$selectionStat = states.selectionState) === null || _states$selectionStat === void 0 ? void 0 : _states$selectionStat.selection,
19
+ mode: (_states$connectivityS = states.connectivityState) === null || _states$connectivityS === void 0 ? void 0 : _states$connectivityS.mode
20
+ };
15
21
  });
16
22
 
17
23
  // for toolbar button, we allow both creating a new synced block
18
24
  // and converting existing block to synced block
19
25
  const canBeConverted = Boolean(selection && canBeConvertedToSyncBlock(selection));
20
26
  const canInsertEmptyBlock = Boolean(selection === null || selection === void 0 ? void 0 : selection.empty);
21
- const isDisabled = Boolean(!canBeConverted && !canInsertEmptyBlock);
27
+ const isDisabled = Boolean(mode === 'offline' || !canBeConverted && !canInsertEmptyBlock);
22
28
  const onClick = useCallback(() => {
23
29
  var _api$core, _api$core2;
24
30
  api === null || api === void 0 ? void 0 : (_api$core = api.core) === null || _api$core === void 0 ? void 0 : _api$core.actions.execute(({
@@ -14,12 +14,14 @@ const CreateSyncedBlockDropdownItem = ({
14
14
  } = useIntl();
15
15
  const {
16
16
  selection,
17
- menuTriggerByNode
18
- } = useSharedPluginStateWithSelector(api, ['selection', 'blockControls'], states => {
19
- var _states$selectionStat, _states$blockControls, _states$blockControls2;
17
+ menuTriggerByNode,
18
+ mode
19
+ } = useSharedPluginStateWithSelector(api, ['selection', 'blockControls', 'connectivity'], states => {
20
+ var _states$selectionStat, _states$blockControls, _states$blockControls2, _states$connectivityS;
20
21
  return {
21
22
  selection: (_states$selectionStat = states.selectionState) === null || _states$selectionStat === void 0 ? void 0 : _states$selectionStat.selection,
22
- menuTriggerByNode: (_states$blockControls = (_states$blockControls2 = states.blockControlsState) === null || _states$blockControls2 === void 0 ? void 0 : _states$blockControls2.menuTriggerByNode) !== null && _states$blockControls !== void 0 ? _states$blockControls : undefined
23
+ menuTriggerByNode: (_states$blockControls = (_states$blockControls2 = states.blockControlsState) === null || _states$blockControls2 === void 0 ? void 0 : _states$blockControls2.menuTriggerByNode) !== null && _states$blockControls !== void 0 ? _states$blockControls : undefined,
24
+ mode: (_states$connectivityS = states.connectivityState) === null || _states$connectivityS === void 0 ? void 0 : _states$connectivityS.mode
23
25
  };
24
26
  });
25
27
  const isNested = menuTriggerByNode && menuTriggerByNode.rootPos !== menuTriggerByNode.pos;
@@ -34,17 +36,17 @@ const CreateSyncedBlockDropdownItem = ({
34
36
  closeMenu: true
35
37
  }));
36
38
  };
39
+ const isOffline = mode === 'offline';
37
40
  return /*#__PURE__*/React.createElement(ToolbarDropdownItem, {
38
41
  elemBefore: /*#__PURE__*/React.createElement(SyncBlocksIcon, {
39
42
  label: ""
40
43
  }),
41
- onClick: onClick
42
- }, /*#__PURE__*/React.createElement(Flex, {
43
- alignItems: "center",
44
- gap: "space.050"
45
- }, /*#__PURE__*/React.createElement(Text, null, formatMessage(blockMenuMessages.createSyncedBlock)), /*#__PURE__*/React.createElement(Lozenge, {
46
- appearance: "new"
47
- }, formatMessage(blockMenuMessages.newLozenge))));
44
+ onClick: onClick,
45
+ isDisabled: isOffline,
46
+ elemAfter: /*#__PURE__*/React.createElement(Lozenge, {
47
+ appearance: "new"
48
+ }, formatMessage(blockMenuMessages.newLozenge))
49
+ }, formatMessage(blockMenuMessages.createSyncedBlock));
48
50
  };
49
51
  const CopySyncedBlockDropdownItem = ({
50
52
  api