@atlaskit/editor-plugin-synced-block 4.2.6 → 4.2.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/afm-cc/tsconfig.json +9 -0
  3. package/afm-jira/tsconfig.json +1 -1
  4. package/dist/cjs/nodeviews/bodiedSyncedBlock.js +27 -5
  5. package/dist/cjs/pm-plugins/main.js +83 -39
  6. package/dist/cjs/pm-plugins/utils/ignore-dom-event.js +30 -0
  7. package/dist/cjs/pm-plugins/utils/track-sync-blocks.js +49 -1
  8. package/dist/cjs/syncedBlockPlugin.js +16 -1
  9. package/dist/cjs/syncedBlockPluginType.js +1 -2
  10. package/dist/cjs/types/index.js +12 -0
  11. package/dist/cjs/ui/CreateSyncedBlockButton.js +10 -5
  12. package/dist/cjs/ui/CreateSyncedBlockDropdownItem.js +18 -16
  13. package/dist/cjs/ui/CreateSyncedBlockItem.js +17 -15
  14. package/dist/cjs/ui/DeleteConfirmationModal.js +12 -2
  15. package/dist/cjs/ui/Flag.js +67 -0
  16. package/dist/cjs/ui/floating-toolbar.js +3 -2
  17. package/dist/es2019/nodeviews/bodiedSyncedBlock.js +18 -0
  18. package/dist/es2019/pm-plugins/main.js +89 -40
  19. package/dist/es2019/pm-plugins/utils/ignore-dom-event.js +26 -0
  20. package/dist/es2019/pm-plugins/utils/selection-decorations.js +1 -1
  21. package/dist/es2019/pm-plugins/utils/track-sync-blocks.js +41 -0
  22. package/dist/es2019/syncedBlockPlugin.js +18 -2
  23. package/dist/es2019/syncedBlockPluginType.js +0 -1
  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 +18 -16
  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/selection-decorations.js +1 -1
  35. package/dist/esm/pm-plugins/utils/track-sync-blocks.js +48 -0
  36. package/dist/esm/syncedBlockPlugin.js +17 -2
  37. package/dist/esm/syncedBlockPluginType.js +0 -1
  38. package/dist/esm/types/index.js +6 -0
  39. package/dist/esm/ui/CreateSyncedBlockButton.js +10 -5
  40. package/dist/esm/ui/CreateSyncedBlockDropdownItem.js +18 -16
  41. package/dist/esm/ui/CreateSyncedBlockItem.js +17 -15
  42. package/dist/esm/ui/DeleteConfirmationModal.js +12 -2
  43. package/dist/esm/ui/Flag.js +58 -0
  44. package/dist/esm/ui/floating-toolbar.js +3 -2
  45. package/dist/types/nodeviews/bodiedSyncedBlock.d.ts +1 -1
  46. package/dist/types/pm-plugins/main.d.ts +4 -2
  47. package/dist/types/pm-plugins/utils/ignore-dom-event.d.ts +8 -0
  48. package/dist/types/pm-plugins/utils/selection-decorations.d.ts +1 -1
  49. package/dist/types/pm-plugins/utils/track-sync-blocks.d.ts +5 -0
  50. package/dist/types/syncedBlockPluginType.d.ts +5 -1
  51. package/dist/types/types/index.d.ts +11 -0
  52. package/dist/types/ui/DeleteConfirmationModal.d.ts +4 -1
  53. package/dist/types/ui/Flag.d.ts +8 -0
  54. package/dist/types-ts4.5/nodeviews/bodiedSyncedBlock.d.ts +1 -1
  55. package/dist/types-ts4.5/pm-plugins/main.d.ts +4 -2
  56. package/dist/types-ts4.5/pm-plugins/utils/ignore-dom-event.d.ts +8 -0
  57. package/dist/types-ts4.5/pm-plugins/utils/selection-decorations.d.ts +1 -1
  58. package/dist/types-ts4.5/pm-plugins/utils/track-sync-blocks.d.ts +5 -0
  59. package/dist/types-ts4.5/syncedBlockPluginType.d.ts +5 -1
  60. package/dist/types-ts4.5/types/index.d.ts +11 -0
  61. package/dist/types-ts4.5/ui/DeleteConfirmationModal.d.ts +4 -1
  62. package/dist/types-ts4.5/ui/Flag.d.ts +8 -0
  63. package/package.json +7 -4
@@ -1,14 +1,24 @@
1
1
  import React, { useCallback, useEffect, useState } from 'react';
2
2
  import { useIntl } from 'react-intl-next';
3
3
  import Button from '@atlaskit/button/new';
4
+ import { useSharedPluginStateWithSelector } from '@atlaskit/editor-common/hooks';
4
5
  import { syncBlockMessages as messages } from '@atlaskit/editor-common/messages';
5
6
  import ModalDialog, { ModalBody, ModalFooter, ModalHeader, ModalTitle, ModalTransition } from '@atlaskit/modal-dialog';
6
7
  import { Text } from '@atlaskit/primitives/compiled';
7
8
  export const DeleteConfirmationModal = ({
8
- syncBlockStoreManager
9
+ syncBlockStoreManager,
10
+ api
9
11
  }) => {
10
12
  const [isOpen, setIsOpen] = useState(false);
11
13
  const [syncBlockCount, setSyncBlockCount] = useState(1);
14
+ const {
15
+ mode
16
+ } = useSharedPluginStateWithSelector(api, ['connectivity'], states => {
17
+ var _states$connectivityS;
18
+ return {
19
+ mode: (_states$connectivityS = states.connectivityState) === null || _states$connectivityS === void 0 ? void 0 : _states$connectivityS.mode
20
+ };
21
+ });
12
22
  const {
13
23
  formatMessage
14
24
  } = useIntl();
@@ -48,6 +58,7 @@ export const DeleteConfirmationModal = ({
48
58
  }, formatMessage(messages.deleteConfirmationModalCancelButton)), /*#__PURE__*/React.createElement(Button, {
49
59
  appearance: "warning",
50
60
  onClick: handleClose(true),
51
- autoFocus: true
61
+ autoFocus: true,
62
+ isDisabled: mode === 'offline'
52
63
  }, formatMessage(messages.deleteConfirmationModalDeleteButton)))));
53
64
  };
@@ -0,0 +1,66 @@
1
+ import React from 'react';
2
+ import { useIntl } from 'react-intl-next';
3
+ import { useSharedPluginStateWithSelector } from '@atlaskit/editor-common/hooks';
4
+ import { syncBlockMessages as messages } from '@atlaskit/editor-common/messages';
5
+ import AkFlag, { FlagGroup } from '@atlaskit/flag';
6
+ import StatusErrorIcon from '@atlaskit/icon/core/status-error';
7
+ import { syncedBlockPluginKey } from '../pm-plugins/main';
8
+ import { FLAG_ID } from '../types';
9
+ const flagMap = {
10
+ [FLAG_ID.CANNOT_DELETE_WHEN_OFFLINE]: {
11
+ title: messages.failToDeleteTitle,
12
+ description: messages.failToDeleteWhenOfflineDescription
13
+ },
14
+ [FLAG_ID.CANNOT_EDIT_WHEN_OFFLINE]: {
15
+ title: messages.failToEditTitle,
16
+ description: messages.failToEditWhenOfflineDescription
17
+ },
18
+ [FLAG_ID.CANNOT_CREATE_WHEN_OFFLINE]: {
19
+ title: messages.failToCreateTitle,
20
+ description: messages.failToCreateWhenOfflineDescription
21
+ }
22
+ };
23
+ export const Flag = ({
24
+ api
25
+ }) => {
26
+ const {
27
+ showFlag
28
+ } = useSharedPluginStateWithSelector(api, ['syncedBlock'], states => {
29
+ var _states$syncedBlockSt;
30
+ return {
31
+ showFlag: (_states$syncedBlockSt = states.syncedBlockState) === null || _states$syncedBlockSt === void 0 ? void 0 : _states$syncedBlockSt.showFlag
32
+ };
33
+ });
34
+ const {
35
+ formatMessage
36
+ } = useIntl();
37
+ if (!showFlag) {
38
+ return;
39
+ }
40
+ const {
41
+ title,
42
+ description
43
+ } = flagMap[showFlag];
44
+ const onDismissed = () => {
45
+ api === null || api === void 0 ? void 0 : api.core.actions.execute(({
46
+ tr
47
+ }) => {
48
+ tr.setMeta(syncedBlockPluginKey, {
49
+ showFlag: false
50
+ });
51
+ return tr;
52
+ });
53
+ api === null || api === void 0 ? void 0 : api.core.actions.focus();
54
+ };
55
+ return /*#__PURE__*/React.createElement(FlagGroup, null, /*#__PURE__*/React.createElement(AkFlag, {
56
+ onDismissed: onDismissed,
57
+ title: formatMessage(title),
58
+ description: formatMessage(description),
59
+ id: showFlag,
60
+ testId: showFlag,
61
+ icon: /*#__PURE__*/React.createElement(StatusErrorIcon, {
62
+ label: "",
63
+ color: "var(--ds-icon-danger, #C9372C)"
64
+ })
65
+ }));
66
+ };
@@ -8,7 +8,7 @@ import LinkExternalIcon from '@atlaskit/icon/core/link-external';
8
8
  import { copySyncedBlockReferenceToClipboard, editSyncedBlockSource, removeSyncedBlock } from '../editor-commands';
9
9
  import { findSyncBlockOrBodiedSyncBlock, isBodiedSyncBlockNode } from '../pm-plugins/utils/utils';
10
10
  export const getToolbarConfig = (state, intl, api, syncBlockStore) => {
11
- var _api$decorations;
11
+ var _api$decorations, _api$connectivity, _api$connectivity$sha;
12
12
  const syncBlockObject = findSyncBlockOrBodiedSyncBlock(state.schema, state.selection);
13
13
  if (!syncBlockObject) {
14
14
  return;
@@ -89,6 +89,7 @@ export const getToolbarConfig = (state, intl, api, syncBlockStore) => {
89
89
  nodeType,
90
90
  items,
91
91
  scrollable: true,
92
- groupLabel: formatMessage(messages.syncBlockGroup)
92
+ groupLabel: formatMessage(messages.syncBlockGroup),
93
+ visible: (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'
93
94
  };
94
95
  };
@@ -22,10 +22,25 @@ var BodiedSyncBlock = /*#__PURE__*/function (_ReactNodeView) {
22
22
  _classCallCheck(this, BodiedSyncBlock);
23
23
  _this = _callSuper(this, BodiedSyncBlock, [props.node, props.view, props.getPos, props.portalProviderAPI, props.eventDispatcher, props]);
24
24
  _this.syncBlockStore = props.syncBlockStore;
25
+ _this.api = props.api;
26
+ _this.handleConnectivityModeChange();
25
27
  return _this;
26
28
  }
27
29
  _inherits(BodiedSyncBlock, _ReactNodeView);
28
30
  return _createClass(BodiedSyncBlock, [{
31
+ key: "handleConnectivityModeChange",
32
+ value: function handleConnectivityModeChange() {
33
+ var _this$api,
34
+ _this2 = this;
35
+ if ((_this$api = this.api) !== null && _this$api !== void 0 && _this$api.connectivity) {
36
+ this.cleanupConnectivityModeListener = this.api.connectivity.sharedState.onChange(function (_ref) {
37
+ var _this2$contentDOM;
38
+ var nextSharedState = _ref.nextSharedState;
39
+ (_this2$contentDOM = _this2.contentDOM) === null || _this2$contentDOM === void 0 || _this2$contentDOM.setAttribute('contenteditable', nextSharedState.mode === 'online' ? 'true' : 'false');
40
+ });
41
+ }
42
+ }
43
+ }, {
29
44
  key: "createDomRef",
30
45
  value: function createDomRef() {
31
46
  var domRef = document.createElement('div');
@@ -55,13 +70,20 @@ var BodiedSyncBlock = /*#__PURE__*/function (_ReactNodeView) {
55
70
  }
56
71
  return undefined;
57
72
  }
73
+ }, {
74
+ key: "destroy",
75
+ value: function destroy() {
76
+ if (this.cleanupConnectivityModeListener) {
77
+ this.cleanupConnectivityModeListener();
78
+ }
79
+ }
58
80
  }]);
59
81
  }(ReactNodeView);
60
- export var bodiedSyncBlockNodeView = function bodiedSyncBlockNodeView(_ref) {
61
- var pluginOptions = _ref.pluginOptions,
62
- pmPluginFactoryParams = _ref.pmPluginFactoryParams,
63
- api = _ref.api,
64
- syncBlockStore = _ref.syncBlockStore;
82
+ export var bodiedSyncBlockNodeView = function bodiedSyncBlockNodeView(_ref2) {
83
+ var pluginOptions = _ref2.pluginOptions,
84
+ pmPluginFactoryParams = _ref2.pmPluginFactoryParams,
85
+ api = _ref2.api,
86
+ syncBlockStore = _ref2.syncBlockStore;
65
87
  return function (node, view, getPos) {
66
88
  var portalProviderAPI = pmPluginFactoryParams.portalProviderAPI,
67
89
  eventDispatcher = pmPluginFactoryParams.eventDispatcher;
@@ -1,14 +1,15 @@
1
- import _defineProperty from "@babel/runtime/helpers/defineProperty";
2
- 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; }
3
- 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) { _defineProperty(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; }
1
+ import _readOnlyError from "@babel/runtime/helpers/readOnlyError";
4
2
  import { SafePlugin } from '@atlaskit/editor-common/safe-plugin';
5
3
  import { createSelectionClickHandler } from '@atlaskit/editor-common/selection';
6
- import { BodiedSyncBlockSharedCssClassName } from '@atlaskit/editor-common/sync-block';
4
+ import { BodiedSyncBlockSharedCssClassName, SyncBlockStateCssClassName } from '@atlaskit/editor-common/sync-block';
7
5
  import { PluginKey } from '@atlaskit/editor-prosemirror/state';
6
+ import { DecorationSet, Decoration } from '@atlaskit/editor-prosemirror/view';
8
7
  import { lazyBodiedSyncBlockView } from '../nodeviews/bodiedLazySyncedBlock';
9
8
  import { lazySyncBlockView } from '../nodeviews/lazySyncedBlock';
9
+ import { FLAG_ID } from '../types';
10
+ import { shouldIgnoreDomEvent } from './utils/ignore-dom-event';
10
11
  import { calculateDecorations } from './utils/selection-decorations';
11
- import { trackSyncBlocks } from './utils/track-sync-blocks';
12
+ import { hasEditInSyncBlock, trackSyncBlocks } from './utils/track-sync-blocks';
12
13
  export var syncedBlockPluginKey = new PluginKey('syncedBlockPlugin');
13
14
  export var createPlugin = function createPlugin(options, pmPluginFactoryParams, syncBlockStore, api) {
14
15
  var _ref = options || {},
@@ -23,25 +24,23 @@ export var createPlugin = function createPlugin(options, pmPluginFactoryParams,
23
24
  });
24
25
  syncBlockStore.fetchSyncBlocksData(syncBlockNodes);
25
26
  return {
26
- decorationSet: calculateDecorations(instance.doc, instance.selection, instance.schema)
27
+ selectionDecorationSet: calculateDecorations(instance.doc, instance.selection, instance.schema),
28
+ showFlag: false
27
29
  };
28
30
  },
29
31
  apply: function apply(tr, currentPluginState, oldEditorState) {
32
+ var _meta$showFlag;
30
33
  var meta = tr.getMeta(syncedBlockPluginKey);
31
- if (meta) {
32
- return meta;
33
- }
34
- var newState = currentPluginState;
34
+ var showFlag = currentPluginState.showFlag,
35
+ selectionDecorationSet = currentPluginState.selectionDecorationSet;
36
+ var newDecorationSet = selectionDecorationSet.map(tr.mapping, tr.doc);
35
37
  if (!tr.selection.eq(oldEditorState.selection)) {
36
- newState = _objectSpread(_objectSpread({}, newState), {}, {
37
- decorationSet: calculateDecorations(tr.doc, tr.selection, tr.doc.type.schema)
38
- });
39
- } else if (newState.decorationSet) {
40
- newState = _objectSpread(_objectSpread({}, newState), {}, {
41
- decorationSet: newState.decorationSet.map(tr.mapping, tr.doc)
42
- });
38
+ newDecorationSet = calculateDecorations(tr.doc, tr.selection, tr.doc.type.schema);
43
39
  }
44
- return newState;
40
+ return {
41
+ showFlag: (_meta$showFlag = meta === null || meta === void 0 ? void 0 : meta.showFlag) !== null && _meta$showFlag !== void 0 ? _meta$showFlag : showFlag,
42
+ selectionDecorationSet: newDecorationSet
43
+ };
45
44
  }
46
45
  },
47
46
  props: {
@@ -60,14 +59,34 @@ export var createPlugin = function createPlugin(options, pmPluginFactoryParams,
60
59
  })
61
60
  },
62
61
  decorations: function decorations(state) {
63
- var pluginState = syncedBlockPluginKey.getState(state);
64
- return pluginState === null || pluginState === void 0 ? void 0 : pluginState.decorationSet;
62
+ var _syncedBlockPluginKey, _syncedBlockPluginKey2, _api$connectivity;
63
+ var selectionDecorationSet = (_syncedBlockPluginKey = (_syncedBlockPluginKey2 = syncedBlockPluginKey.getState(state)) === null || _syncedBlockPluginKey2 === void 0 ? void 0 : _syncedBlockPluginKey2.selectionDecorationSet) !== null && _syncedBlockPluginKey !== void 0 ? _syncedBlockPluginKey : DecorationSet.empty;
64
+ var doc = state.doc;
65
+ var decorations = [];
66
+ if ((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') {
67
+ state.doc.descendants(function (node, pos) {
68
+ if (node.type.name === 'bodiedSyncBlock') {
69
+ decorations.push(Decoration.node(pos, pos + node.nodeSize, {
70
+ class: SyncBlockStateCssClassName.disabledClassName
71
+ }));
72
+ }
73
+ });
74
+ }
75
+ return selectionDecorationSet.add(doc, decorations);
65
76
  },
66
77
  handleClickOn: createSelectionClickHandler(['bodiedSyncBlock'], function (target) {
67
78
  return !!target.closest(".".concat(BodiedSyncBlockSharedCssClassName.prefix));
68
79
  }, {
69
80
  useLongPressSelection: useLongPressSelection
70
- })
81
+ }),
82
+ handleDOMEvents: {
83
+ mouseover: function mouseover(view, event) {
84
+ return shouldIgnoreDomEvent(view, event, api);
85
+ },
86
+ mousedown: function mousedown(view, event) {
87
+ return shouldIgnoreDomEvent(view, event, api);
88
+ }
89
+ }
71
90
  },
72
91
  view: function view(editorView) {
73
92
  syncBlockStore.setEditorView(editorView);
@@ -78,36 +97,61 @@ export var createPlugin = function createPlugin(options, pmPluginFactoryParams,
78
97
  };
79
98
  },
80
99
  filterTransaction: function filterTransaction(tr, state) {
100
+ var _api$connectivity2;
101
+ var isOffline = (api === null || api === void 0 || (_api$connectivity2 = api.connectivity) === null || _api$connectivity2 === void 0 || (_api$connectivity2 = _api$connectivity2.sharedState.currentState()) === null || _api$connectivity2 === void 0 ? void 0 : _api$connectivity2.mode) === 'offline';
102
+ var isConfirmedSyncBlockDeletion = Boolean(tr.getMeta('isConfirmedSyncBlockDeletion'));
81
103
  // Ignore transactions that don't change the document
82
104
  // or are from remote (collab) or already confirmed sync block deletion
83
105
  // We only care about local changes that change the document
84
106
  // and are not yet confirmed for sync block deletion
85
- if (!tr.docChanged || !(syncBlockStore !== null && syncBlockStore !== void 0 && syncBlockStore.requireConfirmationBeforeDelete()) && !syncBlockStore.hasPendingCreation() || Boolean(tr.getMeta('isRemote')) || Boolean(tr.getMeta('isConfirmedSyncBlockDeletion')) || Boolean(tr.getMeta('isCommitSyncBlockCreation'))) {
107
+ if (!tr.docChanged || !(syncBlockStore !== null && syncBlockStore !== void 0 && syncBlockStore.requireConfirmationBeforeDelete()) && !syncBlockStore.hasPendingCreation() || Boolean(tr.getMeta('isRemote')) || Boolean(tr.getMeta('isCommitSyncBlockCreation')) || !isOffline && isConfirmedSyncBlockDeletion) {
86
108
  return true;
87
109
  }
88
110
  var _trackSyncBlocks = trackSyncBlocks(syncBlockStore, tr, state),
89
111
  removed = _trackSyncBlocks.removed,
90
112
  added = _trackSyncBlocks.added;
91
- if (removed.length > 0) {
92
- // If there are source sync blocks being removed, and we need to confirm with user before deleting,
93
- // we block the transaction here, and wait for user confirmation to proceed with deletion.
94
- // See editor-common/src/sync-block/sync-block-store-manager.ts for how we handle user confirmation and
95
- // proceed with deletion.
96
- syncBlockStore.deleteSyncBlocksWithConfirmation(tr, removed);
97
- return false;
98
- }
99
- if (added.length > 0) {
100
- // If there is bodiedSyncBlock node addition and it's waiting for the result of saving the node to backend (syncBlockStore.hasPendingCreation()),
101
- // 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
102
- // The callback will be evoked by in SourceSyncBlockStoreManager.commitPendingCreation
103
- syncBlockStore.registerCreationCallback(function () {
104
- var _api$core;
105
- api === null || api === void 0 || (_api$core = api.core) === null || _api$core === void 0 || _api$core.actions.execute(function () {
106
- 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(function () {
127
+ var _api$core;
128
+ api === null || api === void 0 || (_api$core = api.core) === null || _api$core === void 0 || _api$core.actions.execute(function () {
129
+ return tr.setMeta('isCommitSyncBlockCreation', true);
130
+ });
131
+ api === null || api === void 0 || api.core.actions.focus();
107
132
  });
108
- api === null || api === void 0 || api.core.actions.focus();
109
- });
110
- return false;
133
+ return false;
134
+ }
135
+ } else {
136
+ // Disable node deletion/creation/edition in offline mode and trigger an error flag instead
137
+ var 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 || api.core.actions.execute(function (_ref2) {
147
+ var tr = _ref2.tr;
148
+ tr.setMeta(syncedBlockPluginKey, {
149
+ showFlag: errorFlag
150
+ });
151
+ return tr;
152
+ });
153
+ return false;
154
+ }
111
155
  }
112
156
  return true;
113
157
  },
@@ -0,0 +1,24 @@
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 var shouldIgnoreDomEvent = function shouldIgnoreDomEvent(view, event, api) {
7
+ var _api$connectivity, _view$posAtCoords;
8
+ if ((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') {
9
+ return;
10
+ }
11
+ var bodiedSyncBlock = view.state.schema.nodes.bodiedSyncBlock;
12
+ var pos = (_view$posAtCoords = view.posAtCoords({
13
+ left: event.clientX,
14
+ top: event.clientY
15
+ })) === null || _view$posAtCoords === void 0 ? void 0 : _view$posAtCoords.pos;
16
+ if (pos === undefined) {
17
+ return;
18
+ }
19
+ var $pos = view.state.doc.resolve(pos);
20
+ var maybeNode = findParentNodeOfTypeClosestToPos($pos, bodiedSyncBlock);
21
+ if (maybeNode) {
22
+ return true;
23
+ }
24
+ };
@@ -1,6 +1,6 @@
1
1
  import { BodiedSyncBlockSharedCssClassName } from '@atlaskit/editor-common/sync-block';
2
2
  import { findParentNodeOfType } from '@atlaskit/editor-prosemirror/utils';
3
- import { Decoration, DecorationSet } from "@atlaskit/editor-prosemirror/view";
3
+ import { Decoration, DecorationSet } from '@atlaskit/editor-prosemirror/view';
4
4
  export var calculateDecorations = function calculateDecorations(doc, selection, schema) {
5
5
  var bodiedSyncBlock = schema.nodes.bodiedSyncBlock;
6
6
  var syncBlockParent = findParentNodeOfType(bodiedSyncBlock)(selection);
@@ -1,4 +1,8 @@
1
+ function _createForOfIteratorHelper(r, e) { var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (!t) { if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) { t && (r = t); var _n = 0, F = function F() {}; return { s: F, n: function n() { return _n >= r.length ? { done: !0 } : { done: !1, value: r[_n++] }; }, e: function e(r) { throw r; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var o, a = !0, u = !1; return { s: function s() { t = t.call(r); }, n: function n() { var r = t.next(); return a = r.done, r; }, e: function e(r) { u = !0, o = r; }, f: function f() { try { a || null == t.return || t.return(); } finally { if (u) throw o; } } }; }
2
+ function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
3
+ function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
1
4
  import { ReplaceAroundStep, ReplaceStep } from '@atlaskit/editor-prosemirror/transform';
5
+ import { findParentNodeOfTypeClosestToPos } from '@atlaskit/editor-prosemirror/utils';
2
6
  export var trackSyncBlocks = function trackSyncBlocks(storeManager, tr, state) {
3
7
  var sourceSyncBlockRemoved = {};
4
8
  var sourceSyncBlockAdded = {};
@@ -55,4 +59,48 @@ export var trackSyncBlocks = function trackSyncBlocks(storeManager, tr, state) {
55
59
  removed: Object.values(sourceSyncBlockRemoved),
56
60
  added: Object.values(sourceSyncBlockAdded)
57
61
  };
62
+ };
63
+
64
+ /**
65
+ *
66
+ * @returns true if steps modifies children node within bodiedSyncBlock
67
+ */
68
+ export var hasEditInSyncBlock = function hasEditInSyncBlock(tr, state) {
69
+ var bodiedSyncBlock = state.schema.nodes.bodiedSyncBlock;
70
+ var _iterator = _createForOfIteratorHelper(tr.steps),
71
+ _step;
72
+ try {
73
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
74
+ var step = _step.value;
75
+ var map = step.getMap();
76
+ var doc = tr.doc;
77
+ var positions = [];
78
+
79
+ // Extract positions from steps dynamically based on applicable properties
80
+ if ('from' in step && typeof step.from === 'number' && 'to' in step && typeof step.to === 'number') {
81
+ var _ref = step,
82
+ from = _ref.from,
83
+ to = _ref.to;
84
+ positions.push(from, to);
85
+ } else if ('pos' in step && typeof step.pos === 'number') {
86
+ var _ref2 = step,
87
+ pos = _ref2.pos;
88
+ positions.push(pos);
89
+ }
90
+ for (var _i = 0, _positions = positions; _i < _positions.length; _i++) {
91
+ var _pos = _positions[_i];
92
+ var newPos = map.map(_pos);
93
+ if (newPos >= 0 && newPos <= doc.content.size) {
94
+ if (findParentNodeOfTypeClosestToPos(doc.resolve(newPos), bodiedSyncBlock)) {
95
+ return true;
96
+ }
97
+ }
98
+ }
99
+ }
100
+ } catch (err) {
101
+ _iterator.e(err);
102
+ } finally {
103
+ _iterator.f();
104
+ }
105
+ return false;
58
106
  };
@@ -6,9 +6,10 @@ import { SyncBlockStoreManager } from '@atlaskit/editor-synced-block-provider';
6
6
  import Lozenge from '@atlaskit/lozenge';
7
7
  import { flushBodiedSyncBlocks as _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';
@@ -66,6 +67,7 @@ export var syncedBlockPlugin = function syncedBlockPlugin(_ref) {
66
67
  description: formatMessage(blockTypeMessages.syncedBlockDescription),
67
68
  priority: 800,
68
69
  keywords: ['synced', 'block', 'synced-block', 'sync', 'sync-block', 'auto', 'update', 'excerpt', 'connect'],
70
+ isDisabledOffline: true,
69
71
  keyshortcut: '',
70
72
  lozenge: /*#__PURE__*/React.createElement(Lozenge, {
71
73
  appearance: "new"
@@ -92,8 +94,21 @@ export var syncedBlockPlugin = function syncedBlockPlugin(_ref) {
92
94
  return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(SyncBlockRefresher, {
93
95
  syncBlockStoreManager: syncBlockStore
94
96
  }), /*#__PURE__*/React.createElement(DeleteConfirmationModal, {
95
- syncBlockStoreManager: syncBlockStore
97
+ syncBlockStoreManager: syncBlockStore,
98
+ api: api
99
+ }), /*#__PURE__*/React.createElement(Flag, {
100
+ api: api
96
101
  }));
102
+ },
103
+ getSharedState: function getSharedState(editorState) {
104
+ if (!editorState) {
105
+ return;
106
+ }
107
+ var _syncedBlockPluginKey = syncedBlockPluginKey.getState(editorState),
108
+ showFlag = _syncedBlockPluginKey.showFlag;
109
+ return {
110
+ showFlag: showFlag
111
+ };
97
112
  }
98
113
  };
99
114
  };
@@ -1,2 +1 @@
1
- ;
2
1
  export {};
@@ -0,0 +1,6 @@
1
+ export var 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
+ }({});
@@ -8,16 +8,21 @@ import { canBeConvertedToSyncBlock } from '../pm-plugins/utils/utils';
8
8
  export var CreateSyncedBlockButton = function CreateSyncedBlockButton(_ref) {
9
9
  var api = _ref.api;
10
10
  var intl = useIntl();
11
- var selection = useSharedPluginStateWithSelector(api, ['selection'], function (states) {
12
- var _states$selectionStat;
13
- return (_states$selectionStat = states.selectionState) === null || _states$selectionStat === void 0 ? void 0 : _states$selectionStat.selection;
14
- });
11
+ var _useSharedPluginState = useSharedPluginStateWithSelector(api, ['selection', 'connectivity'], function (states) {
12
+ var _states$selectionStat, _states$connectivityS;
13
+ return {
14
+ selection: (_states$selectionStat = states.selectionState) === null || _states$selectionStat === void 0 ? void 0 : _states$selectionStat.selection,
15
+ mode: (_states$connectivityS = states.connectivityState) === null || _states$connectivityS === void 0 ? void 0 : _states$connectivityS.mode
16
+ };
17
+ }),
18
+ selection = _useSharedPluginState.selection,
19
+ mode = _useSharedPluginState.mode;
15
20
 
16
21
  // for toolbar button, we allow both creating a new synced block
17
22
  // and converting existing block to synced block
18
23
  var canBeConverted = Boolean(selection && canBeConvertedToSyncBlock(selection));
19
24
  var canInsertEmptyBlock = Boolean(selection === null || selection === void 0 ? void 0 : selection.empty);
20
- var isDisabled = Boolean(!canBeConverted && !canInsertEmptyBlock);
25
+ var isDisabled = Boolean(mode === 'offline' || !canBeConverted && !canInsertEmptyBlock);
21
26
  var onClick = useCallback(function () {
22
27
  var _api$core, _api$core2;
23
28
  api === null || api === void 0 || (_api$core = api.core) === null || _api$core === void 0 || _api$core.actions.execute(function (_ref2) {
@@ -10,16 +10,18 @@ var CreateSyncedBlockDropdownItem = function CreateSyncedBlockDropdownItem(_ref)
10
10
  var api = _ref.api;
11
11
  var _useIntl = useIntl(),
12
12
  formatMessage = _useIntl.formatMessage;
13
- var _useSharedPluginState = useSharedPluginStateWithSelector(api, ['selection', 'blockControls'], function (states) {
14
- var _states$selectionStat, _states$blockControls;
13
+ var _useSharedPluginState = useSharedPluginStateWithSelector(api, ['selection', 'blockControls', 'connectivity'], function (states) {
14
+ var _states$selectionStat, _states$blockControls, _states$blockControls2, _states$connectivityS;
15
15
  return {
16
16
  selection: (_states$selectionStat = states.selectionState) === null || _states$selectionStat === void 0 ? void 0 : _states$selectionStat.selection,
17
- activeNode: (_states$blockControls = states.blockControlsState) === null || _states$blockControls === void 0 ? void 0 : _states$blockControls.activeNode
17
+ 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,
18
+ mode: (_states$connectivityS = states.connectivityState) === null || _states$connectivityS === void 0 ? void 0 : _states$connectivityS.mode
18
19
  };
19
20
  }),
20
21
  selection = _useSharedPluginState.selection,
21
- activeNode = _useSharedPluginState.activeNode;
22
- var isNested = activeNode && activeNode.rootPos !== activeNode.pos;
22
+ menuTriggerByNode = _useSharedPluginState.menuTriggerByNode,
23
+ mode = _useSharedPluginState.mode;
24
+ var isNested = menuTriggerByNode && menuTriggerByNode.rootPos !== menuTriggerByNode.pos;
23
25
  var canBeConverted = useMemo(function () {
24
26
  return selection && canBeConvertedToSyncBlock(selection);
25
27
  }, [selection]);
@@ -33,17 +35,17 @@ var CreateSyncedBlockDropdownItem = function CreateSyncedBlockDropdownItem(_ref)
33
35
  closeMenu: true
34
36
  }));
35
37
  };
38
+ var isOffline = mode === 'offline';
36
39
  return /*#__PURE__*/React.createElement(ToolbarDropdownItem, {
37
40
  elemBefore: /*#__PURE__*/React.createElement(SyncBlocksIcon, {
38
41
  label: ""
39
42
  }),
40
- onClick: onClick
41
- }, /*#__PURE__*/React.createElement(Flex, {
42
- alignItems: "center",
43
- gap: "space.050"
44
- }, /*#__PURE__*/React.createElement(Text, null, formatMessage(blockMenuMessages.createSyncedBlock)), /*#__PURE__*/React.createElement(Lozenge, {
45
- appearance: "new"
46
- }, formatMessage(blockMenuMessages.newLozenge))));
43
+ onClick: onClick,
44
+ isDisabled: isOffline,
45
+ elemAfter: /*#__PURE__*/React.createElement(Lozenge, {
46
+ appearance: "new"
47
+ }, formatMessage(blockMenuMessages.newLozenge))
48
+ }, formatMessage(blockMenuMessages.createSyncedBlock));
47
49
  };
48
50
  var CopySyncedBlockDropdownItem = function CopySyncedBlockDropdownItem(_ref2) {
49
51
  var api = _ref2.api;
@@ -71,13 +73,13 @@ var CopySyncedBlockDropdownItem = function CopySyncedBlockDropdownItem(_ref2) {
71
73
  export var CreateOrCopySyncedBlockDropdownItem = function CreateOrCopySyncedBlockDropdownItem(_ref3) {
72
74
  var api = _ref3.api;
73
75
  var _useSharedPluginState2 = useSharedPluginStateWithSelector(api, ['blockControls'], function (states) {
74
- var _states$blockControls2, _states$blockControls3;
76
+ var _states$blockControls3, _states$blockControls4;
75
77
  return {
76
- activeNodeType: (_states$blockControls2 = (_states$blockControls3 = states.blockControlsState) === null || _states$blockControls3 === void 0 || (_states$blockControls3 = _states$blockControls3.activeNode) === null || _states$blockControls3 === void 0 ? void 0 : _states$blockControls3.nodeType) !== null && _states$blockControls2 !== void 0 ? _states$blockControls2 : undefined
78
+ menuTriggerByNode: (_states$blockControls3 = (_states$blockControls4 = states.blockControlsState) === null || _states$blockControls4 === void 0 ? void 0 : _states$blockControls4.menuTriggerByNode) !== null && _states$blockControls3 !== void 0 ? _states$blockControls3 : undefined
77
79
  };
78
80
  }),
79
- activeNodeType = _useSharedPluginState2.activeNodeType;
80
- if (activeNodeType === 'syncBlock' || activeNodeType === 'bodiedSyncBlock') {
81
+ menuTriggerByNode = _useSharedPluginState2.menuTriggerByNode;
82
+ if ((menuTriggerByNode === null || menuTriggerByNode === void 0 ? void 0 : menuTriggerByNode.nodeType) === 'syncBlock' || (menuTriggerByNode === null || menuTriggerByNode === void 0 ? void 0 : menuTriggerByNode.nodeType) === 'bodiedSyncBlock') {
81
83
  return /*#__PURE__*/React.createElement(CopySyncedBlockDropdownItem, {
82
84
  api: api
83
85
  });