@atlaskit/editor-plugin-synced-block 5.2.1 → 5.3.0
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 +25 -0
- package/afm-jira/tsconfig.json +1 -1
- package/dist/cjs/editor-commands/index.js +43 -5
- package/dist/cjs/editor-commands/utils.js +20 -0
- package/dist/cjs/pm-plugins/menu-and-toolbar-experiences.js +267 -0
- package/dist/cjs/syncedBlockPlugin.js +21 -12
- package/dist/cjs/types/index.js +6 -3
- package/dist/cjs/ui/CreateSyncedBlockButton.js +2 -1
- package/dist/cjs/ui/CreateSyncedBlockDropdownItem.js +2 -1
- package/dist/cjs/ui/SyncBlockRefresher.js +18 -4
- package/dist/cjs/ui/SyncedLocationDropdown.js +14 -11
- package/dist/cjs/ui/floating-toolbar.js +21 -2
- package/dist/es2019/editor-commands/index.js +44 -4
- package/dist/es2019/editor-commands/utils.js +14 -0
- package/dist/es2019/pm-plugins/menu-and-toolbar-experiences.js +261 -0
- package/dist/es2019/syncedBlockPlugin.js +19 -12
- package/dist/es2019/types/index.js +5 -2
- package/dist/es2019/ui/CreateSyncedBlockButton.js +2 -1
- package/dist/es2019/ui/CreateSyncedBlockDropdownItem.js +2 -1
- package/dist/es2019/ui/SyncBlockRefresher.js +18 -4
- package/dist/es2019/ui/SyncedLocationDropdown.js +14 -11
- package/dist/es2019/ui/floating-toolbar.js +20 -3
- package/dist/esm/editor-commands/index.js +42 -4
- package/dist/esm/editor-commands/utils.js +14 -0
- package/dist/esm/pm-plugins/menu-and-toolbar-experiences.js +260 -0
- package/dist/esm/syncedBlockPlugin.js +21 -12
- package/dist/esm/types/index.js +5 -2
- package/dist/esm/ui/CreateSyncedBlockButton.js +2 -1
- package/dist/esm/ui/CreateSyncedBlockDropdownItem.js +2 -1
- package/dist/esm/ui/SyncBlockRefresher.js +18 -4
- package/dist/esm/ui/SyncedLocationDropdown.js +14 -11
- package/dist/esm/ui/floating-toolbar.js +22 -3
- package/dist/types/editor-commands/index.d.ts +8 -1
- package/dist/types/editor-commands/utils.d.ts +2 -0
- package/dist/types/pm-plugins/menu-and-toolbar-experiences.d.ts +12 -0
- package/dist/types/types/index.d.ts +5 -14
- package/dist/types-ts4.5/editor-commands/index.d.ts +8 -1
- package/dist/types-ts4.5/editor-commands/utils.d.ts +2 -0
- package/dist/types-ts4.5/pm-plugins/menu-and-toolbar-experiences.d.ts +12 -0
- package/dist/types-ts4.5/types/index.d.ts +5 -14
- package/package.json +3 -3
- package/dist/cjs/pm-plugins/experience-tracking/create-reference-experience.js +0 -113
- package/dist/cjs/pm-plugins/experience-tracking/create-source-experience.js +0 -169
- package/dist/cjs/pm-plugins/experience-tracking/delete-reference-experience.js +0 -175
- package/dist/cjs/pm-plugins/experience-tracking/delete-source-experience.js +0 -103
- package/dist/cjs/pm-plugins/experience-tracking/get-experience-tracking-plugins.js +0 -61
- package/dist/cjs/pm-plugins/experience-tracking/provider-only-experiences.js +0 -128
- package/dist/cjs/pm-plugins/utils/experience-tracking-utils.js +0 -85
- package/dist/es2019/pm-plugins/experience-tracking/create-reference-experience.js +0 -109
- package/dist/es2019/pm-plugins/experience-tracking/create-source-experience.js +0 -166
- package/dist/es2019/pm-plugins/experience-tracking/delete-reference-experience.js +0 -181
- package/dist/es2019/pm-plugins/experience-tracking/delete-source-experience.js +0 -98
- package/dist/es2019/pm-plugins/experience-tracking/get-experience-tracking-plugins.js +0 -46
- package/dist/es2019/pm-plugins/experience-tracking/provider-only-experiences.js +0 -127
- package/dist/es2019/pm-plugins/utils/experience-tracking-utils.js +0 -65
- package/dist/esm/pm-plugins/experience-tracking/create-reference-experience.js +0 -107
- package/dist/esm/pm-plugins/experience-tracking/create-source-experience.js +0 -163
- package/dist/esm/pm-plugins/experience-tracking/delete-reference-experience.js +0 -169
- package/dist/esm/pm-plugins/experience-tracking/delete-source-experience.js +0 -97
- package/dist/esm/pm-plugins/experience-tracking/get-experience-tracking-plugins.js +0 -55
- package/dist/esm/pm-plugins/experience-tracking/provider-only-experiences.js +0 -122
- package/dist/esm/pm-plugins/utils/experience-tracking-utils.js +0 -79
- package/dist/types/pm-plugins/experience-tracking/create-reference-experience.d.ts +0 -10
- package/dist/types/pm-plugins/experience-tracking/create-source-experience.d.ts +0 -10
- package/dist/types/pm-plugins/experience-tracking/delete-reference-experience.d.ts +0 -13
- package/dist/types/pm-plugins/experience-tracking/delete-source-experience.d.ts +0 -12
- package/dist/types/pm-plugins/experience-tracking/get-experience-tracking-plugins.d.ts +0 -5
- package/dist/types/pm-plugins/experience-tracking/provider-only-experiences.d.ts +0 -3
- package/dist/types/pm-plugins/utils/experience-tracking-utils.d.ts +0 -9
- package/dist/types-ts4.5/pm-plugins/experience-tracking/create-reference-experience.d.ts +0 -10
- package/dist/types-ts4.5/pm-plugins/experience-tracking/create-source-experience.d.ts +0 -10
- package/dist/types-ts4.5/pm-plugins/experience-tracking/delete-reference-experience.d.ts +0 -13
- package/dist/types-ts4.5/pm-plugins/experience-tracking/delete-source-experience.d.ts +0 -12
- package/dist/types-ts4.5/pm-plugins/experience-tracking/get-experience-tracking-plugins.d.ts +0 -5
- package/dist/types-ts4.5/pm-plugins/experience-tracking/provider-only-experiences.d.ts +0 -3
- package/dist/types-ts4.5/pm-plugins/utils/experience-tracking-utils.d.ts +0 -9
|
@@ -9,15 +9,18 @@ exports.getToolbarConfig = void 0;
|
|
|
9
9
|
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
|
|
10
10
|
var _react = _interopRequireDefault(require("react"));
|
|
11
11
|
var _messages = _interopRequireWildcard(require("@atlaskit/editor-common/messages"));
|
|
12
|
+
var _ui = require("@atlaskit/editor-common/ui");
|
|
12
13
|
var _utils = require("@atlaskit/editor-prosemirror/utils");
|
|
13
14
|
var _consts = require("@atlaskit/editor-shared-styles/consts");
|
|
14
15
|
var _editorSyncedBlockProvider = require("@atlaskit/editor-synced-block-provider");
|
|
15
16
|
var _copy = _interopRequireDefault(require("@atlaskit/icon/core/copy"));
|
|
16
17
|
var _delete = _interopRequireDefault(require("@atlaskit/icon/core/delete"));
|
|
17
18
|
var _edit = _interopRequireDefault(require("@atlaskit/icon/core/edit"));
|
|
19
|
+
var _linkBroken = _interopRequireDefault(require("@atlaskit/icon/core/link-broken"));
|
|
18
20
|
var _platformFeatureFlags = require("@atlaskit/platform-feature-flags");
|
|
19
21
|
var _editorCommands = require("../editor-commands");
|
|
20
22
|
var _utils2 = require("../pm-plugins/utils/utils");
|
|
23
|
+
var _types = require("../types");
|
|
21
24
|
var _SyncedLocationDropdown = require("./SyncedLocationDropdown");
|
|
22
25
|
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); }
|
|
23
26
|
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; }
|
|
@@ -54,7 +57,7 @@ var getToolbarConfig = exports.getToolbarConfig = function getToolbarConfig(stat
|
|
|
54
57
|
title: formatMessage(_messages.default.delete),
|
|
55
58
|
onClick: (0, _editorCommands.removeSyncedBlock)(api),
|
|
56
59
|
icon: _delete.default,
|
|
57
|
-
testId: (0, _platformFeatureFlags.fg)('platform_synced_block_dogfooding') ?
|
|
60
|
+
testId: (0, _platformFeatureFlags.fg)('platform_synced_block_dogfooding') ? _types.SYNCED_BLOCK_BUTTON_TEST_ID.syncedBlockToolbarReferenceDelete : undefined
|
|
58
61
|
}, hoverDecorationProps(nodeType, _consts.akEditorSelectedNodeClassName));
|
|
59
62
|
items.push(deleteButton);
|
|
60
63
|
} else {
|
|
@@ -72,7 +75,23 @@ var getToolbarConfig = exports.getToolbarConfig = function getToolbarConfig(stat
|
|
|
72
75
|
});
|
|
73
76
|
}
|
|
74
77
|
};
|
|
75
|
-
|
|
78
|
+
var unsyncButton = {
|
|
79
|
+
type: 'custom',
|
|
80
|
+
fallback: [],
|
|
81
|
+
render: function render(view) {
|
|
82
|
+
return /*#__PURE__*/_react.default.createElement(_ui.FloatingToolbarButton, {
|
|
83
|
+
areAnyNewToolbarFlagsEnabled: true,
|
|
84
|
+
icon: /*#__PURE__*/_react.default.createElement(_linkBroken.default, {
|
|
85
|
+
label: ""
|
|
86
|
+
}),
|
|
87
|
+
title: formatMessage(_messages.syncBlockMessages.unsyncButton),
|
|
88
|
+
onClick: function onClick() {
|
|
89
|
+
return (0, _editorCommands.unsync)(syncBlockStore, isBodiedSyncBlock, view);
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
items.push(syncedLocation, unsyncButton);
|
|
76
95
|
}
|
|
77
96
|
var copyButton = _objectSpread({
|
|
78
97
|
id: 'editor.syncedBlock.copy',
|
|
@@ -1,15 +1,19 @@
|
|
|
1
|
+
import { defaultSchema } from '@atlaskit/adf-schema/schema-default';
|
|
1
2
|
import { ACTION, ACTION_SUBJECT, ACTION_SUBJECT_ID, EVENT_TYPE } from '@atlaskit/editor-common/analytics';
|
|
2
3
|
import { copyDomNode, toDOM } from '@atlaskit/editor-common/copy-button';
|
|
4
|
+
import { DOMSerializer, Fragment } from '@atlaskit/editor-prosemirror/model';
|
|
3
5
|
import { TextSelection } from '@atlaskit/editor-prosemirror/state';
|
|
4
6
|
import { findSelectedNodeOfType, removeParentNodeOfType, removeSelectedNode, safeInsert } from '@atlaskit/editor-prosemirror/utils';
|
|
5
7
|
import { fg } from '@atlaskit/platform-feature-flags';
|
|
6
8
|
import { syncedBlockPluginKey } from '../pm-plugins/main';
|
|
7
9
|
import { canBeConvertedToSyncBlock, findSyncBlock, findSyncBlockOrBodiedSyncBlock, isBodiedSyncBlockNode } from '../pm-plugins/utils/utils';
|
|
8
10
|
import { FLAG_ID } from '../types';
|
|
11
|
+
import { pasteSyncBlockHTMLContent } from './utils';
|
|
9
12
|
export const createSyncedBlock = ({
|
|
10
13
|
tr,
|
|
11
14
|
syncBlockStore,
|
|
12
|
-
typeAheadInsert
|
|
15
|
+
typeAheadInsert,
|
|
16
|
+
fireAnalyticsEvent
|
|
13
17
|
}) => {
|
|
14
18
|
const {
|
|
15
19
|
schema: {
|
|
@@ -41,9 +45,14 @@ export const createSyncedBlock = ({
|
|
|
41
45
|
const conversionInfo = canBeConvertedToSyncBlock(tr.selection);
|
|
42
46
|
if (!conversionInfo) {
|
|
43
47
|
if (fg('platform_synced_block_dogfooding')) {
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
48
|
+
fireAnalyticsEvent === null || fireAnalyticsEvent === void 0 ? void 0 : fireAnalyticsEvent({
|
|
49
|
+
action: ACTION.ERROR,
|
|
50
|
+
actionSubject: ACTION_SUBJECT.SYNCED_BLOCK,
|
|
51
|
+
actionSubjectId: ACTION_SUBJECT_ID.SYNCED_BLOCK_CREATE,
|
|
52
|
+
attributes: {
|
|
53
|
+
error: 'Content cannot be converted to sync block'
|
|
54
|
+
},
|
|
55
|
+
eventType: EVENT_TYPE.OPERATIONAL
|
|
47
56
|
});
|
|
48
57
|
}
|
|
49
58
|
return false;
|
|
@@ -170,4 +179,35 @@ export const removeSyncedBlock = api => (state, dispatch, _view) => {
|
|
|
170
179
|
dispatch(removeTr);
|
|
171
180
|
api === null || api === void 0 ? void 0 : api.core.actions.focus();
|
|
172
181
|
return true;
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Deletes (bodied)SyncBlock node and paste its content to the editor
|
|
186
|
+
*/
|
|
187
|
+
export const unsync = (storeManager, isBodiedSyncBlock, view) => {
|
|
188
|
+
var _storeManager$referen, _storeManager$referen2;
|
|
189
|
+
if (!view) {
|
|
190
|
+
return false;
|
|
191
|
+
}
|
|
192
|
+
const {
|
|
193
|
+
state
|
|
194
|
+
} = view;
|
|
195
|
+
const syncBlock = findSyncBlockOrBodiedSyncBlock(state.schema, state.selection);
|
|
196
|
+
if (!syncBlock) {
|
|
197
|
+
return false;
|
|
198
|
+
}
|
|
199
|
+
if (isBodiedSyncBlock) {
|
|
200
|
+
return true;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// handle syncBlock unsync
|
|
204
|
+
const syncBlockContent = (_storeManager$referen = storeManager.referenceManager.getFromCache(syncBlock.node.attrs.resourceId)) === null || _storeManager$referen === void 0 ? void 0 : (_storeManager$referen2 = _storeManager$referen.data) === null || _storeManager$referen2 === void 0 ? void 0 : _storeManager$referen2.content;
|
|
205
|
+
if (!syncBlockContent) {
|
|
206
|
+
return false;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// use defaultSchema for serialization so we can serialize any type of nodes and marks despite current editor's schema might not allow it
|
|
210
|
+
const contentFragment = Fragment.fromJSON(defaultSchema, syncBlockContent);
|
|
211
|
+
const contentDOM = DOMSerializer.fromSchema(defaultSchema).serializeFragment(contentFragment);
|
|
212
|
+
return pasteSyncBlockHTMLContent(contentDOM, view);
|
|
173
213
|
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export const pasteSyncBlockHTMLContent = (contentDOM, view) => {
|
|
2
|
+
const tmpDiv = document.createElement('div');
|
|
3
|
+
tmpDiv.appendChild(contentDOM);
|
|
4
|
+
|
|
5
|
+
// This is required so that prosemirror can read the fragment context and slice properly
|
|
6
|
+
if (tmpDiv.firstChild instanceof HTMLElement) {
|
|
7
|
+
tmpDiv.firstChild.setAttribute('data-pm-slice', '0 0 []');
|
|
8
|
+
|
|
9
|
+
// As per requirement - when unsync reference block, it should render its content as copy&paste behaviour
|
|
10
|
+
// Hence here we call pasteHTML to evoke editor paste logic that handles any unsupported nodes/marks
|
|
11
|
+
return view.pasteHTML(tmpDiv.innerHTML);
|
|
12
|
+
}
|
|
13
|
+
return false;
|
|
14
|
+
};
|
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
import { bind } from 'bind-event-listener';
|
|
2
|
+
import { ACTION, ACTION_SUBJECT_ID } from '@atlaskit/editor-common/analytics';
|
|
3
|
+
import { Experience, EXPERIENCE_ID, ExperienceCheckDomMutation, ExperienceCheckTimeout, getNodeQuery, getPopupContainerFromEditorView, popupWithNestedElement } from '@atlaskit/editor-common/experiences';
|
|
4
|
+
import { SafePlugin } from '@atlaskit/editor-common/safe-plugin';
|
|
5
|
+
import { PluginKey } from '@atlaskit/editor-prosemirror/state';
|
|
6
|
+
import { SYNCED_BLOCK_BUTTON_TEST_ID } from '../types';
|
|
7
|
+
const TIMEOUT_DURATION = 30000;
|
|
8
|
+
const pluginKey = new PluginKey('syncedBlockMenuAndToolbarExperience');
|
|
9
|
+
const SYNCED_BLOCK_BUTTON_TEST_IDS = Object.values(SYNCED_BLOCK_BUTTON_TEST_ID);
|
|
10
|
+
const syncedBlockButtonIds = new Set(SYNCED_BLOCK_BUTTON_TEST_IDS);
|
|
11
|
+
let targetEl;
|
|
12
|
+
export const getMenuAndToolbarExperiencesPlugin = ({
|
|
13
|
+
refs,
|
|
14
|
+
dispatchAnalyticsEvent
|
|
15
|
+
}) => {
|
|
16
|
+
let popupsTargetEl;
|
|
17
|
+
let editorViewEl;
|
|
18
|
+
const getPopupsTarget = () => {
|
|
19
|
+
if (!popupsTargetEl) {
|
|
20
|
+
popupsTargetEl = refs.popupsMountPoint || refs.wrapperElement || getPopupContainerFromEditorView(editorViewEl);
|
|
21
|
+
}
|
|
22
|
+
return popupsTargetEl;
|
|
23
|
+
};
|
|
24
|
+
const createSourcePrimaryToolbarExperience = getCreateSourcePrimaryToolbarExperience({
|
|
25
|
+
refs,
|
|
26
|
+
dispatchAnalyticsEvent
|
|
27
|
+
});
|
|
28
|
+
const createSourceBlockMenuExperience = getCreateSourceBlockMenuExperience({
|
|
29
|
+
refs,
|
|
30
|
+
dispatchAnalyticsEvent
|
|
31
|
+
});
|
|
32
|
+
const createSourceQuickInsertMenuExperience = getCreateSourceQuickInsertMenuExperience({
|
|
33
|
+
refs,
|
|
34
|
+
dispatchAnalyticsEvent
|
|
35
|
+
});
|
|
36
|
+
const deleteReferenceSyncedBlockExperience = getDeleteReferenceSyncedBlockToolbarExperience({
|
|
37
|
+
refs,
|
|
38
|
+
dispatchAnalyticsEvent
|
|
39
|
+
});
|
|
40
|
+
const unbindClickListener = bind(document, {
|
|
41
|
+
type: 'click',
|
|
42
|
+
listener: event => {
|
|
43
|
+
const target = event.target;
|
|
44
|
+
if (!target) {
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
const button = target.closest('button[data-testid]');
|
|
48
|
+
if (!button || !(button instanceof HTMLButtonElement)) {
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
const testId = button.dataset.testid;
|
|
52
|
+
if (!isSyncedBlockButtonId(testId)) {
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
handleButtonClick({
|
|
56
|
+
testId,
|
|
57
|
+
createSourcePrimaryToolbarExperience,
|
|
58
|
+
createSourceBlockMenuExperience,
|
|
59
|
+
createSourceQuickInsertMenuExperience,
|
|
60
|
+
deleteReferenceSyncedBlockExperience
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
const unbindKeydownListener = bind(document, {
|
|
65
|
+
type: 'keydown',
|
|
66
|
+
listener: event => {
|
|
67
|
+
if (isEnterKey(event.key)) {
|
|
68
|
+
const typeaheadPopup = popupWithNestedElement(getPopupsTarget(), '.fabric-editor-typeahead');
|
|
69
|
+
if (!typeaheadPopup || !(typeaheadPopup instanceof HTMLElement)) {
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
const firstItem = typeaheadPopup.querySelector('[role="option"]');
|
|
73
|
+
if (!firstItem || !(firstItem instanceof HTMLElement)) {
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
const testId = firstItem.dataset.testid;
|
|
77
|
+
if (testId === SYNCED_BLOCK_BUTTON_TEST_ID.quickInsertCreate) {
|
|
78
|
+
createSourceQuickInsertMenuExperience.start();
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
},
|
|
82
|
+
options: {
|
|
83
|
+
capture: true
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
return new SafePlugin({
|
|
87
|
+
key: pluginKey,
|
|
88
|
+
view: editorView => {
|
|
89
|
+
editorViewEl = editorView.dom;
|
|
90
|
+
return {
|
|
91
|
+
destroy: () => {
|
|
92
|
+
createSourcePrimaryToolbarExperience.abort({
|
|
93
|
+
reason: 'editor-destroyed'
|
|
94
|
+
});
|
|
95
|
+
createSourceBlockMenuExperience.abort({
|
|
96
|
+
reason: 'editor-destroyed'
|
|
97
|
+
});
|
|
98
|
+
createSourceQuickInsertMenuExperience.abort({
|
|
99
|
+
reason: 'editor-destroyed'
|
|
100
|
+
});
|
|
101
|
+
deleteReferenceSyncedBlockExperience.abort({
|
|
102
|
+
reason: 'editor-destroyed'
|
|
103
|
+
});
|
|
104
|
+
unbindClickListener();
|
|
105
|
+
unbindKeydownListener();
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
};
|
|
111
|
+
const getCreateSourcePrimaryToolbarExperience = ({
|
|
112
|
+
refs,
|
|
113
|
+
dispatchAnalyticsEvent
|
|
114
|
+
}) => {
|
|
115
|
+
return new Experience(EXPERIENCE_ID.TOOLBAR_ACTION, {
|
|
116
|
+
action: ACTION.SYNCED_BLOCK_CREATE,
|
|
117
|
+
actionSubjectId: ACTION_SUBJECT_ID.PRIMARY_TOOLBAR,
|
|
118
|
+
dispatchAnalyticsEvent,
|
|
119
|
+
checks: [new ExperienceCheckTimeout({
|
|
120
|
+
durationMs: TIMEOUT_DURATION
|
|
121
|
+
}), syncedBlockAddedToDomCheck(refs)]
|
|
122
|
+
});
|
|
123
|
+
};
|
|
124
|
+
const getCreateSourceBlockMenuExperience = ({
|
|
125
|
+
refs,
|
|
126
|
+
dispatchAnalyticsEvent
|
|
127
|
+
}) => {
|
|
128
|
+
return new Experience(EXPERIENCE_ID.MENU_ACTION, {
|
|
129
|
+
action: ACTION.SYNCED_BLOCK_CREATE,
|
|
130
|
+
actionSubjectId: ACTION_SUBJECT_ID.BLOCK_MENU,
|
|
131
|
+
dispatchAnalyticsEvent,
|
|
132
|
+
checks: [new ExperienceCheckTimeout({
|
|
133
|
+
durationMs: TIMEOUT_DURATION
|
|
134
|
+
}), syncedBlockAddedToDomCheck(refs)]
|
|
135
|
+
});
|
|
136
|
+
};
|
|
137
|
+
const getCreateSourceQuickInsertMenuExperience = ({
|
|
138
|
+
refs,
|
|
139
|
+
dispatchAnalyticsEvent
|
|
140
|
+
}) => {
|
|
141
|
+
return new Experience(EXPERIENCE_ID.MENU_ACTION, {
|
|
142
|
+
action: ACTION.SYNCED_BLOCK_CREATE,
|
|
143
|
+
actionSubjectId: ACTION_SUBJECT_ID.QUICK_INSERT,
|
|
144
|
+
dispatchAnalyticsEvent,
|
|
145
|
+
checks: [new ExperienceCheckTimeout({
|
|
146
|
+
durationMs: TIMEOUT_DURATION
|
|
147
|
+
}), syncedBlockAddedToDomCheck(refs)]
|
|
148
|
+
});
|
|
149
|
+
};
|
|
150
|
+
const getDeleteReferenceSyncedBlockToolbarExperience = ({
|
|
151
|
+
refs,
|
|
152
|
+
dispatchAnalyticsEvent
|
|
153
|
+
}) => {
|
|
154
|
+
return new Experience(EXPERIENCE_ID.TOOLBAR_ACTION, {
|
|
155
|
+
action: ACTION.REFERENCE_SYNCED_BLOCK_DELETE,
|
|
156
|
+
actionSubjectId: ACTION_SUBJECT_ID.SYNCED_BLOCK_TOOLBAR,
|
|
157
|
+
dispatchAnalyticsEvent,
|
|
158
|
+
checks: [new ExperienceCheckTimeout({
|
|
159
|
+
durationMs: TIMEOUT_DURATION
|
|
160
|
+
}), referenceSyncBlockRemovedFromDomCheck(refs)]
|
|
161
|
+
});
|
|
162
|
+
};
|
|
163
|
+
const isSyncedBlockButtonId = value => {
|
|
164
|
+
return !!value && syncedBlockButtonIds.has(value);
|
|
165
|
+
};
|
|
166
|
+
const handleButtonClick = ({
|
|
167
|
+
testId,
|
|
168
|
+
createSourcePrimaryToolbarExperience,
|
|
169
|
+
createSourceBlockMenuExperience,
|
|
170
|
+
createSourceQuickInsertMenuExperience,
|
|
171
|
+
deleteReferenceSyncedBlockExperience
|
|
172
|
+
}) => {
|
|
173
|
+
switch (testId) {
|
|
174
|
+
case SYNCED_BLOCK_BUTTON_TEST_ID.primaryToolbarCreate:
|
|
175
|
+
createSourcePrimaryToolbarExperience.start();
|
|
176
|
+
break;
|
|
177
|
+
case SYNCED_BLOCK_BUTTON_TEST_ID.blockMenuCreate:
|
|
178
|
+
createSourceBlockMenuExperience.start();
|
|
179
|
+
break;
|
|
180
|
+
case SYNCED_BLOCK_BUTTON_TEST_ID.quickInsertCreate:
|
|
181
|
+
createSourceQuickInsertMenuExperience.start();
|
|
182
|
+
break;
|
|
183
|
+
case SYNCED_BLOCK_BUTTON_TEST_ID.syncedBlockToolbarReferenceDelete:
|
|
184
|
+
deleteReferenceSyncedBlockExperience.start();
|
|
185
|
+
break;
|
|
186
|
+
default:
|
|
187
|
+
{
|
|
188
|
+
// Exhaustiveness check: if a new SyncedBlockToolbarButtonId is added
|
|
189
|
+
// but not handled above, TypeScript will error here.
|
|
190
|
+
const _exhaustiveCheck = testId;
|
|
191
|
+
return _exhaustiveCheck;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
};
|
|
195
|
+
const isEnterKey = key => {
|
|
196
|
+
return key === 'Enter';
|
|
197
|
+
};
|
|
198
|
+
const getTarget = containerElement => {
|
|
199
|
+
if (!targetEl) {
|
|
200
|
+
const element = containerElement === null || containerElement === void 0 ? void 0 : containerElement.querySelector('.ProseMirror');
|
|
201
|
+
if (!element || !(element instanceof HTMLElement)) {
|
|
202
|
+
return null;
|
|
203
|
+
}
|
|
204
|
+
targetEl = element;
|
|
205
|
+
}
|
|
206
|
+
return targetEl;
|
|
207
|
+
};
|
|
208
|
+
const syncedBlockAddedToDomCheck = refs => new ExperienceCheckDomMutation({
|
|
209
|
+
onDomMutation: ({
|
|
210
|
+
mutations
|
|
211
|
+
}) => {
|
|
212
|
+
if (mutations.some(isBodiedSyncBlockAddedInMutation)) {
|
|
213
|
+
return {
|
|
214
|
+
status: 'success'
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
return undefined;
|
|
218
|
+
},
|
|
219
|
+
observeConfig: () => {
|
|
220
|
+
return {
|
|
221
|
+
target: getTarget(refs.containerElement),
|
|
222
|
+
options: {
|
|
223
|
+
childList: true
|
|
224
|
+
}
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
});
|
|
228
|
+
const isBodiedSyncBlockAddedInMutation = ({
|
|
229
|
+
type,
|
|
230
|
+
addedNodes
|
|
231
|
+
}) => {
|
|
232
|
+
return type === 'childList' && [...addedNodes].some(isBodiedSyncBlockWithinNode);
|
|
233
|
+
};
|
|
234
|
+
const isBodiedSyncBlockWithinNode = node => getNodeQuery('[data-prosemirror-node-name="bodiedSyncBlock"]')(node);
|
|
235
|
+
const referenceSyncBlockRemovedFromDomCheck = refs => new ExperienceCheckDomMutation({
|
|
236
|
+
onDomMutation: ({
|
|
237
|
+
mutations
|
|
238
|
+
}) => {
|
|
239
|
+
if (mutations.some(isSyncBlockRemovedInMutation)) {
|
|
240
|
+
return {
|
|
241
|
+
status: 'success'
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
return undefined;
|
|
245
|
+
},
|
|
246
|
+
observeConfig: () => {
|
|
247
|
+
return {
|
|
248
|
+
target: getTarget(refs.containerElement),
|
|
249
|
+
options: {
|
|
250
|
+
childList: true
|
|
251
|
+
}
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
});
|
|
255
|
+
const isSyncBlockRemovedInMutation = ({
|
|
256
|
+
type,
|
|
257
|
+
removedNodes
|
|
258
|
+
}) => {
|
|
259
|
+
return type === 'childList' && [...removedNodes].some(isSyncBlockWithinNode);
|
|
260
|
+
};
|
|
261
|
+
const isSyncBlockWithinNode = node => getNodeQuery('[data-prosemirror-node-name="syncBlock"]')(node);
|
|
@@ -7,8 +7,9 @@ import Lozenge from '@atlaskit/lozenge';
|
|
|
7
7
|
import { fg } from '@atlaskit/platform-feature-flags';
|
|
8
8
|
import { flushBodiedSyncBlocks, flushSyncBlocks } from './editor-actions';
|
|
9
9
|
import { copySyncedBlockReferenceToClipboardEditorCommand, createSyncedBlock } from './editor-commands';
|
|
10
|
-
import { getExperienceTrackingPlugins } from './pm-plugins/experience-tracking/get-experience-tracking-plugins';
|
|
11
10
|
import { createPlugin, syncedBlockPluginKey } from './pm-plugins/main';
|
|
11
|
+
import { getMenuAndToolbarExperiencesPlugin } from './pm-plugins/menu-and-toolbar-experiences';
|
|
12
|
+
import { SYNCED_BLOCK_BUTTON_TEST_ID } from './types';
|
|
12
13
|
import { getBlockMenuComponents } from './ui/block-menu-components';
|
|
13
14
|
import { DeleteConfirmationModal } from './ui/DeleteConfirmationModal';
|
|
14
15
|
import { Flag } from './ui/Flag';
|
|
@@ -40,26 +41,30 @@ export const syncedBlockPlugin = ({
|
|
|
40
41
|
return [{
|
|
41
42
|
name: 'syncedBlockPlugin',
|
|
42
43
|
plugin: params => createPlugin(config, params, syncBlockStore, api)
|
|
43
|
-
}, ...(fg('platform_synced_block_dogfooding') ?
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
44
|
+
}, ...(fg('platform_synced_block_dogfooding') ? [{
|
|
45
|
+
name: 'menuAndToolbarExperiencesPlugin',
|
|
46
|
+
plugin: () => getMenuAndToolbarExperiencesPlugin({
|
|
47
|
+
refs,
|
|
48
|
+
dispatchAnalyticsEvent: payload => {
|
|
49
|
+
var _api$analytics2, _api$analytics2$actio;
|
|
50
|
+
return api === null || api === void 0 ? void 0 : (_api$analytics2 = api.analytics) === null || _api$analytics2 === void 0 ? void 0 : (_api$analytics2$actio = _api$analytics2.actions) === null || _api$analytics2$actio === void 0 ? void 0 : _api$analytics2$actio.fireAnalyticsEvent(payload);
|
|
51
|
+
}
|
|
52
|
+
})
|
|
53
|
+
}] : [])];
|
|
51
54
|
},
|
|
52
55
|
commands: {
|
|
53
56
|
copySyncedBlockReferenceToClipboard: () => copySyncedBlockReferenceToClipboardEditorCommand(syncBlockStore, api),
|
|
54
57
|
insertSyncedBlock: () => ({
|
|
55
58
|
tr
|
|
56
59
|
}) => {
|
|
60
|
+
var _api$analytics3;
|
|
57
61
|
if (!(config !== null && config !== void 0 && config.enableSourceCreation)) {
|
|
58
62
|
return null;
|
|
59
63
|
}
|
|
60
64
|
return createSyncedBlock({
|
|
61
65
|
tr,
|
|
62
|
-
syncBlockStore
|
|
66
|
+
syncBlockStore,
|
|
67
|
+
fireAnalyticsEvent: api === null || api === void 0 ? void 0 : (_api$analytics3 = api.analytics) === null || _api$analytics3 === void 0 ? void 0 : _api$analytics3.actions.fireAnalyticsEvent
|
|
63
68
|
}) || null;
|
|
64
69
|
}
|
|
65
70
|
},
|
|
@@ -93,13 +98,15 @@ export const syncedBlockPlugin = ({
|
|
|
93
98
|
label: formatMessage(blockTypeMessages.syncedBlock)
|
|
94
99
|
}),
|
|
95
100
|
action: (insert, state) => {
|
|
101
|
+
var _api$analytics4;
|
|
96
102
|
return createSyncedBlock({
|
|
97
103
|
tr: state.tr,
|
|
98
104
|
syncBlockStore,
|
|
99
|
-
typeAheadInsert: insert
|
|
105
|
+
typeAheadInsert: insert,
|
|
106
|
+
fireAnalyticsEvent: api === null || api === void 0 ? void 0 : (_api$analytics4 = api.analytics) === null || _api$analytics4 === void 0 ? void 0 : _api$analytics4.actions.fireAnalyticsEvent
|
|
100
107
|
});
|
|
101
108
|
},
|
|
102
|
-
testId: fg('platform_synced_block_dogfooding') ?
|
|
109
|
+
testId: fg('platform_synced_block_dogfooding') ? SYNCED_BLOCK_BUTTON_TEST_ID.quickInsertCreate : undefined
|
|
103
110
|
}];
|
|
104
111
|
},
|
|
105
112
|
floatingToolbar: (state, intl) => getToolbarConfig(state, intl, api, syncBlockStore)
|
|
@@ -6,6 +6,9 @@ export let FLAG_ID = /*#__PURE__*/function (FLAG_ID) {
|
|
|
6
6
|
FLAG_ID["SYNC_BLOCK_COPIED"] = "sync-block-copied";
|
|
7
7
|
return FLAG_ID;
|
|
8
8
|
}({});
|
|
9
|
-
export const
|
|
10
|
-
|
|
9
|
+
export const SYNCED_BLOCK_BUTTON_TEST_ID = {
|
|
10
|
+
primaryToolbarCreate: 'create-synced-block-toolbar-btn',
|
|
11
|
+
blockMenuCreate: 'create-synced-block-block-menu-btn',
|
|
12
|
+
quickInsertCreate: 'create-synced-block-quick-insert-btn',
|
|
13
|
+
syncedBlockToolbarReferenceDelete: 'reference-synced-block-delete-btn'
|
|
11
14
|
};
|
|
@@ -6,6 +6,7 @@ import { isOfflineMode } from '@atlaskit/editor-plugin-connectivity';
|
|
|
6
6
|
import { ToolbarButton, ToolbarTooltip } from '@atlaskit/editor-toolbar';
|
|
7
7
|
import BlockSyncedIcon from '@atlaskit/icon-lab/core/block-synced';
|
|
8
8
|
import { canBeConvertedToSyncBlock } from '../pm-plugins/utils/utils';
|
|
9
|
+
import { SYNCED_BLOCK_BUTTON_TEST_ID } from '../types';
|
|
9
10
|
export const CreateSyncedBlockButton = ({
|
|
10
11
|
api
|
|
11
12
|
}) => {
|
|
@@ -45,7 +46,7 @@ export const CreateSyncedBlockButton = ({
|
|
|
45
46
|
label: ""
|
|
46
47
|
}),
|
|
47
48
|
isDisabled: isDisabled,
|
|
48
|
-
testId:
|
|
49
|
+
testId: SYNCED_BLOCK_BUTTON_TEST_ID.primaryToolbarCreate,
|
|
49
50
|
onClick: onClick
|
|
50
51
|
}));
|
|
51
52
|
};
|
|
@@ -6,6 +6,7 @@ import { isOfflineMode } from '@atlaskit/editor-plugin-connectivity';
|
|
|
6
6
|
import { SyncBlocksIcon, ToolbarDropdownItem } from '@atlaskit/editor-toolbar';
|
|
7
7
|
import Lozenge from '@atlaskit/lozenge';
|
|
8
8
|
import { canBeConvertedToSyncBlock } from '../pm-plugins/utils/utils';
|
|
9
|
+
import { SYNCED_BLOCK_BUTTON_TEST_ID } from '../types';
|
|
9
10
|
const CreateSyncedBlockDropdownItem = ({
|
|
10
11
|
api
|
|
11
12
|
}) => {
|
|
@@ -43,7 +44,7 @@ const CreateSyncedBlockDropdownItem = ({
|
|
|
43
44
|
}),
|
|
44
45
|
onClick: onClick,
|
|
45
46
|
isDisabled: isOffline,
|
|
46
|
-
testId:
|
|
47
|
+
testId: SYNCED_BLOCK_BUTTON_TEST_ID.blockMenuCreate,
|
|
47
48
|
elemAfter: /*#__PURE__*/React.createElement(Lozenge, {
|
|
48
49
|
appearance: "new"
|
|
49
50
|
}, formatMessage(blockMenuMessages.newLozenge))
|
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
import { useEffect } from 'react';
|
|
2
2
|
import { useSharedPluginStateWithSelector } from '@atlaskit/editor-common/hooks';
|
|
3
|
+
import { isOfflineMode } from '@atlaskit/editor-plugin-connectivity';
|
|
4
|
+
import { fg } from '@atlaskit/platform-feature-flags';
|
|
3
5
|
export const SYNC_BLOCK_FETCH_INTERVAL = 3000;
|
|
4
6
|
|
|
5
|
-
// Component that
|
|
6
|
-
//
|
|
7
|
+
// Component that manages synced block data synchronization.
|
|
8
|
+
// When the feature flag 'platform_synced_block_dogfooding' is enabled,
|
|
9
|
+
// it uses provider-based GraphQL subscriptions for updates.
|
|
10
|
+
// When disabled, it falls back to polling at regular intervals.
|
|
7
11
|
export const SyncBlockRefresher = ({
|
|
8
12
|
syncBlockStoreManager,
|
|
9
13
|
api
|
|
@@ -16,9 +20,19 @@ export const SyncBlockRefresher = ({
|
|
|
16
20
|
mode: (_states$connectivityS = states.connectivityState) === null || _states$connectivityS === void 0 ? void 0 : _states$connectivityS.mode
|
|
17
21
|
};
|
|
18
22
|
});
|
|
23
|
+
const featureFlagEnabled = fg('platform_synced_block_dogfooding');
|
|
24
|
+
const isOnline = !isOfflineMode(mode);
|
|
19
25
|
useEffect(() => {
|
|
26
|
+
const useRealTimeSubscriptions = featureFlagEnabled && isOnline;
|
|
27
|
+
syncBlockStoreManager.referenceManager.setRealTimeSubscriptionsEnabled(useRealTimeSubscriptions);
|
|
28
|
+
}, [syncBlockStoreManager, featureFlagEnabled, isOnline]);
|
|
29
|
+
useEffect(() => {
|
|
30
|
+
const useRealTimeSubscriptions = featureFlagEnabled && isOnline;
|
|
31
|
+
if (useRealTimeSubscriptions) {
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
20
34
|
let interval = -1;
|
|
21
|
-
if (
|
|
35
|
+
if (isOnline) {
|
|
22
36
|
interval = window.setInterval(() => {
|
|
23
37
|
var _document;
|
|
24
38
|
// check if document is visible to avoid unnecessary refreshes
|
|
@@ -32,6 +46,6 @@ export const SyncBlockRefresher = ({
|
|
|
32
46
|
return () => {
|
|
33
47
|
window.clearInterval(interval);
|
|
34
48
|
};
|
|
35
|
-
}, [syncBlockStoreManager,
|
|
49
|
+
}, [syncBlockStoreManager, isOnline, featureFlagEnabled]);
|
|
36
50
|
return null;
|
|
37
51
|
};
|
|
@@ -39,17 +39,18 @@ const styles = {
|
|
|
39
39
|
const ItemTitle = ({
|
|
40
40
|
title,
|
|
41
41
|
formatMessage,
|
|
42
|
-
|
|
42
|
+
onSameDocument,
|
|
43
43
|
isSource,
|
|
44
|
-
hasAccess
|
|
44
|
+
hasAccess,
|
|
45
|
+
productType
|
|
45
46
|
}) => {
|
|
46
47
|
return /*#__PURE__*/React.createElement(Inline, null, /*#__PURE__*/React.createElement(Box, {
|
|
47
48
|
as: "span",
|
|
48
49
|
xcss: styles.title
|
|
49
|
-
}, title),
|
|
50
|
+
}, title), onSameDocument && /*#__PURE__*/React.createElement(Box, {
|
|
50
51
|
as: "span",
|
|
51
52
|
xcss: styles.note
|
|
52
|
-
}, "\xA0- ", formatMessage(messages.
|
|
53
|
+
}, "\xA0- ", formatMessage(productType === 'confluence-page' ? messages.syncedLocationDropdownTitleNoteForConfluencePage : messages.syncedLocationDropdownTitleNoteForJiraWorkItem)), isSource && /*#__PURE__*/React.createElement(Box, {
|
|
53
54
|
as: "span",
|
|
54
55
|
xcss: styles.lozenge
|
|
55
56
|
}, /*#__PURE__*/React.createElement(Lozenge, null, formatMessage(messages.syncedLocationDropdownSourceLozenge))), !hasAccess && /*#__PURE__*/React.createElement(Box, {
|
|
@@ -66,7 +67,7 @@ const subTypeIconMap = {
|
|
|
66
67
|
page: PageIcon,
|
|
67
68
|
blogpost: QuotationMarkIcon
|
|
68
69
|
};
|
|
69
|
-
const
|
|
70
|
+
const getConfluenceSubTypeIcon = subType => {
|
|
70
71
|
return subType && subType in subTypeIconMap ? subTypeIconMap[subType] : PageIcon;
|
|
71
72
|
};
|
|
72
73
|
const ProductIcon = ({
|
|
@@ -86,18 +87,19 @@ const ItemIcon = ({
|
|
|
86
87
|
}) => {
|
|
87
88
|
const {
|
|
88
89
|
hasAccess,
|
|
89
|
-
subType
|
|
90
|
+
subType,
|
|
91
|
+
productType
|
|
90
92
|
} = reference;
|
|
91
|
-
if (hasAccess) {
|
|
93
|
+
if (productType === 'confluence-page' && hasAccess) {
|
|
92
94
|
return /*#__PURE__*/React.createElement(IconTile, {
|
|
93
|
-
icon:
|
|
95
|
+
icon: getConfluenceSubTypeIcon(subType),
|
|
94
96
|
label: "",
|
|
95
97
|
appearance: 'gray',
|
|
96
98
|
size: "xsmall"
|
|
97
99
|
});
|
|
98
100
|
}
|
|
99
101
|
return /*#__PURE__*/React.createElement(ProductIcon, {
|
|
100
|
-
product:
|
|
102
|
+
product: productType
|
|
101
103
|
});
|
|
102
104
|
};
|
|
103
105
|
export const processReferenceData = (referenceData, intl) => {
|
|
@@ -230,9 +232,10 @@ const DropdownContent = ({
|
|
|
230
232
|
}, /*#__PURE__*/React.createElement(ItemTitle, {
|
|
231
233
|
title: reference.title || reference.url || '',
|
|
232
234
|
formatMessage: formatMessage,
|
|
233
|
-
|
|
235
|
+
onSameDocument: reference.onSameDocument,
|
|
234
236
|
isSource: reference.isSource,
|
|
235
|
-
hasAccess: reference.hasAccess
|
|
237
|
+
hasAccess: reference.hasAccess,
|
|
238
|
+
productType: reference.productType
|
|
236
239
|
})))))));
|
|
237
240
|
} else {
|
|
238
241
|
return /*#__PURE__*/React.createElement(NoResultScreen, {
|