@atlaskit/editor-plugin-block-menu 6.0.27 → 6.0.29

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.
@@ -1,8 +1,10 @@
1
1
  import { bind } from 'bind-event-listener';
2
- import { ACTION_SUBJECT_ID } from '@atlaskit/editor-common/analytics';
3
- import { Experience, EXPERIENCE_ID, ExperienceCheckDomMutation, ExperienceCheckTimeout, getPopupContainerFromEditorView, popupWithNestedElement } from '@atlaskit/editor-common/experiences';
2
+ import { ACTION, ACTION_SUBJECT_ID } from '@atlaskit/editor-common/analytics';
3
+ import { BLOCK_MENU_ACTION_TEST_ID } from '@atlaskit/editor-common/block-menu';
4
+ import { Experience, EXPERIENCE_ID, ExperienceCheckDomMutation, ExperienceCheckTimeout, getPopupContainerFromEditorView } from '@atlaskit/editor-common/experiences';
4
5
  import { SafePlugin } from '@atlaskit/editor-common/safe-plugin';
5
6
  import { PluginKey } from '@atlaskit/editor-prosemirror/state';
7
+ import { getParentDOMAtSelection, handleDeleteDomMutation, handleMenuOpenDomMutation, handleMoveDomMutation, isBlockMenuVisible, isDragHandleElement } from './experience-check-utils';
6
8
  const TIMEOUT_DURATION = 1000;
7
9
  const pluginKey = new PluginKey('blockMenuExperiences');
8
10
  const START_METHOD = {
@@ -17,13 +19,14 @@ export const getBlockMenuExperiencesPlugin = ({
17
19
  refs,
18
20
  dispatchAnalyticsEvent
19
21
  }) => {
20
- let targetEl;
21
- let editorViewEl;
22
+ let popupTargetEl;
23
+ let editorView;
22
24
  const getPopupsTarget = () => {
23
- if (!targetEl) {
24
- targetEl = refs.popupsMountPoint || getPopupContainerFromEditorView(editorViewEl);
25
+ if (!popupTargetEl) {
26
+ var _editorView;
27
+ popupTargetEl = refs.popupsMountPoint || getPopupContainerFromEditorView((_editorView = editorView) === null || _editorView === void 0 ? void 0 : _editorView.dom);
25
28
  }
26
- return targetEl;
29
+ return popupTargetEl;
27
30
  };
28
31
  const blockMenuOpenExperience = new Experience(EXPERIENCE_ID.MENU_OPEN, {
29
32
  actionSubjectId: ACTION_SUBJECT_ID.BLOCK_MENU,
@@ -31,16 +34,7 @@ export const getBlockMenuExperiencesPlugin = ({
31
34
  checks: [new ExperienceCheckTimeout({
32
35
  durationMs: TIMEOUT_DURATION
33
36
  }), new ExperienceCheckDomMutation({
34
- onDomMutation: ({
35
- mutations
36
- }) => {
37
- if (mutations.some(isBlockMenuAddedInMutation)) {
38
- return {
39
- status: 'success'
40
- };
41
- }
42
- return undefined;
43
- },
37
+ onDomMutation: handleMenuOpenDomMutation,
44
38
  observeConfig: () => ({
45
39
  target: getPopupsTarget(),
46
40
  options: {
@@ -49,26 +43,87 @@ export const getBlockMenuExperiencesPlugin = ({
49
43
  })
50
44
  })]
51
45
  });
46
+ const actionObserveConfig = () => ({
47
+ target: getParentDOMAtSelection(editorView),
48
+ options: {
49
+ childList: true
50
+ }
51
+ });
52
+ const blockMoveUpExperience = new Experience(EXPERIENCE_ID.MENU_ACTION, {
53
+ action: ACTION.MOVED,
54
+ actionSubjectId: ACTION_SUBJECT_ID.MOVE_UP_BLOCK,
55
+ dispatchAnalyticsEvent,
56
+ checks: [new ExperienceCheckTimeout({
57
+ durationMs: TIMEOUT_DURATION
58
+ }), new ExperienceCheckDomMutation({
59
+ onDomMutation: handleMoveDomMutation,
60
+ observeConfig: actionObserveConfig
61
+ })]
62
+ });
63
+ const blockMoveDownExperience = new Experience(EXPERIENCE_ID.MENU_ACTION, {
64
+ action: ACTION.MOVED,
65
+ actionSubjectId: ACTION_SUBJECT_ID.MOVE_DOWN_BLOCK,
66
+ dispatchAnalyticsEvent,
67
+ checks: [new ExperienceCheckTimeout({
68
+ durationMs: TIMEOUT_DURATION
69
+ }), new ExperienceCheckDomMutation({
70
+ onDomMutation: handleMoveDomMutation,
71
+ observeConfig: actionObserveConfig
72
+ })]
73
+ });
74
+ const blockDeleteExperience = new Experience(EXPERIENCE_ID.MENU_ACTION, {
75
+ action: ACTION.DELETED,
76
+ actionSubjectId: ACTION_SUBJECT_ID.DELETE_BLOCK,
77
+ dispatchAnalyticsEvent,
78
+ checks: [new ExperienceCheckTimeout({
79
+ durationMs: TIMEOUT_DURATION
80
+ }), new ExperienceCheckDomMutation({
81
+ onDomMutation: handleDeleteDomMutation,
82
+ observeConfig: actionObserveConfig
83
+ })]
84
+ });
85
+ const handleMenuOpened = method => {
86
+ // Don't start if block menu is already visible
87
+ if (isBlockMenuVisible(getPopupsTarget())) {
88
+ return;
89
+ }
90
+ blockMenuOpenExperience.start({
91
+ method
92
+ });
93
+ };
94
+ const handleItemActioned = target => {
95
+ const button = target.closest('button[data-testid]');
96
+ if (!button || !(button instanceof HTMLButtonElement) || button.disabled || button.getAttribute('aria-disabled') === 'true') {
97
+ return;
98
+ }
99
+ const testId = button.dataset.testid;
100
+ if (!testId) {
101
+ return;
102
+ }
103
+ switch (testId) {
104
+ case BLOCK_MENU_ACTION_TEST_ID.MOVE_UP:
105
+ blockMoveUpExperience.start();
106
+ break;
107
+ case BLOCK_MENU_ACTION_TEST_ID.MOVE_DOWN:
108
+ blockMoveDownExperience.start();
109
+ break;
110
+ case BLOCK_MENU_ACTION_TEST_ID.DELETE:
111
+ blockDeleteExperience.start();
112
+ break;
113
+ }
114
+ };
52
115
  const unbindClickListener = bind(document, {
53
116
  type: 'click',
54
117
  listener: event => {
55
- if (!(event.target instanceof Element)) {
56
- return;
57
- }
58
118
  const target = event.target;
59
-
60
- // Check if the click is on a drag handle
61
- if (!isDragHandleElement(target)) {
119
+ if (!(target instanceof HTMLElement)) {
62
120
  return;
63
121
  }
64
-
65
- // Don't start if block menu is already visible
66
- if (isBlockMenuVisible(getPopupsTarget())) {
67
- return;
122
+ if (isDragHandleElement(target)) {
123
+ handleMenuOpened(START_METHOD.DRAG_HANDLE_CLICK);
124
+ } else {
125
+ handleItemActioned(target);
68
126
  }
69
- blockMenuOpenExperience.start({
70
- method: START_METHOD.DRAG_HANDLE_CLICK
71
- });
72
127
  },
73
128
  options: {
74
129
  capture: true
@@ -77,20 +132,14 @@ export const getBlockMenuExperiencesPlugin = ({
77
132
  const unbindKeydownListener = bind(document, {
78
133
  type: 'keydown',
79
134
  listener: event => {
80
- if (!(event.target instanceof Element)) {
135
+ const target = event.target;
136
+ if (!(target instanceof HTMLElement)) {
81
137
  return;
82
138
  }
83
- const target = event.target;
84
139
 
85
140
  // Check if Enter or Space is pressed on a drag handle
86
141
  if ((event.key === 'Enter' || event.key === ' ') && isDragHandleElement(target)) {
87
- // Don't start if block menu is already visible
88
- if (isBlockMenuVisible(getPopupsTarget())) {
89
- return;
90
- }
91
- blockMenuOpenExperience.start({
92
- method: START_METHOD.KEYBOARD
93
- });
142
+ handleMenuOpened(START_METHOD.KEYBOARD);
94
143
  }
95
144
 
96
145
  // Abort on Escape key if block menu is not yet visible
@@ -106,35 +155,27 @@ export const getBlockMenuExperiencesPlugin = ({
106
155
  });
107
156
  return new SafePlugin({
108
157
  key: pluginKey,
109
- view: editorView => {
110
- editorViewEl = editorView.dom;
158
+ view: view => {
159
+ editorView = view;
111
160
  return {
112
161
  destroy: () => {
113
162
  blockMenuOpenExperience.abort({
114
163
  reason: ABORT_REASON.EDITOR_DESTROYED
115
164
  });
165
+ blockMoveUpExperience.abort({
166
+ reason: ABORT_REASON.EDITOR_DESTROYED
167
+ });
168
+ blockMoveDownExperience.abort({
169
+ reason: ABORT_REASON.EDITOR_DESTROYED
170
+ });
171
+ blockDeleteExperience.abort({
172
+ reason: ABORT_REASON.EDITOR_DESTROYED
173
+ });
174
+ editorView = undefined;
116
175
  unbindClickListener();
117
176
  unbindKeydownListener();
118
177
  }
119
178
  };
120
179
  }
121
180
  });
122
- };
123
- const isBlockMenuAddedInMutation = ({
124
- type,
125
- addedNodes
126
- }) => {
127
- return type === 'childList' && [...addedNodes].some(isBlockMenuWithinNode);
128
- };
129
- const isBlockMenuWithinNode = node => {
130
- return popupWithNestedElement(node, '[data-testid="editor-block-menu"]') !== null;
131
- };
132
- const isDragHandleElement = element => {
133
- return !!(element !== null && element !== void 0 && element.closest('[data-editor-block-ctrl-drag-handle]'));
134
- };
135
- const isBlockMenuVisible = popupsTarget => {
136
- if (!popupsTarget) {
137
- return false;
138
- }
139
- return popupWithNestedElement(popupsTarget, '[data-testid="editor-block-menu"]') !== null;
140
181
  };
@@ -0,0 +1,125 @@
1
+ import { popupWithNestedElement } from '@atlaskit/editor-common/experiences';
2
+ /**
3
+ * Checks if the given element or any of its ancestors is a drag handle element.
4
+ *
5
+ * @param element - The DOM element to check.
6
+ * @returns True if the element is a drag handle, false otherwise.
7
+ */
8
+ export const isDragHandleElement = element => {
9
+ return !!(element !== null && element !== void 0 && element.closest('[data-editor-block-ctrl-drag-handle]'));
10
+ };
11
+
12
+ /**
13
+ * Checks if the block menu is currently visible within the provided popups target element.
14
+ *
15
+ * @param popupsTarget - The container element for popups.
16
+ * @returns True if the block menu is visible, false otherwise.
17
+ */
18
+ export const isBlockMenuVisible = popupsTarget => {
19
+ if (!popupsTarget) {
20
+ return false;
21
+ }
22
+ return popupWithNestedElement(popupsTarget, '[data-testid="editor-block-menu"]') !== null;
23
+ };
24
+
25
+ /**
26
+ * Gets the parent DOM element at the starting position of the current selection
27
+ * from the provided editor view.
28
+ *
29
+ * @param editorView - The editor view from which to get the parent DOM element
30
+ * @returns The parent HTMLElement at the selection start, or null if not found
31
+ */
32
+ export const getParentDOMAtSelection = editorView => {
33
+ if (!editorView) {
34
+ return null;
35
+ }
36
+ const {
37
+ selection
38
+ } = editorView.state;
39
+ const from = selection.from;
40
+ const nodeDOM = editorView.nodeDOM(from);
41
+ if (nodeDOM instanceof HTMLElement) {
42
+ return nodeDOM.parentElement;
43
+ }
44
+ return null;
45
+ };
46
+ const isBlockMenuAddedInMutation = ({
47
+ type,
48
+ addedNodes
49
+ }) => {
50
+ return type === 'childList' && [...addedNodes].some(isBlockMenuWithinNode);
51
+ };
52
+ const isBlockMenuWithinNode = node => {
53
+ return popupWithNestedElement(node, '[data-testid="editor-block-menu"]') !== null;
54
+ };
55
+
56
+ /**
57
+ * Handles DOM mutations to determine if the block menu was opened
58
+ *
59
+ * This function looks for mutations that indicate the block menu
60
+ * has been added to the DOM.
61
+ *
62
+ * @param mutations - The list of DOM mutations to evaluate
63
+ * @returns An ExperienceCheckResult indicating success if the menu was opened, otherwise undefined
64
+ */
65
+ export const handleMenuOpenDomMutation = ({
66
+ mutations
67
+ }) => {
68
+ // Look for a mutation that added the block menu
69
+ for (const mutation of mutations) {
70
+ if (isBlockMenuAddedInMutation(mutation)) {
71
+ return {
72
+ status: 'success'
73
+ };
74
+ }
75
+ }
76
+ return undefined;
77
+ };
78
+
79
+ /**
80
+ * Handles DOM mutations to determine if a move action was performed
81
+ *
82
+ * Move actions typically produce two mutations: one where nodes are removed
83
+ * from their original location, and another where the same number of nodes are
84
+ * added to a new location. This function checks for that pattern.
85
+ *
86
+ * @param mutations - The list of DOM mutations to evaluate
87
+ * @returns An ExperienceCheckResult indicating success if a move was detected, otherwise undefined
88
+ */
89
+ export const handleMoveDomMutation = ({
90
+ mutations
91
+ }) => {
92
+ const removeMutation = mutations.find(m => m.type === 'childList' && m.removedNodes.length > 0);
93
+ const addMutation = mutations.find(m => m.type === 'childList' && m.addedNodes.length > 0);
94
+ if (removeMutation && addMutation && removeMutation.removedNodes.length === addMutation.addedNodes.length) {
95
+ return {
96
+ status: 'success'
97
+ };
98
+ }
99
+ return undefined;
100
+ };
101
+
102
+ /**
103
+ * Handles DOM mutations to determine if a delete action was performed
104
+ *
105
+ * Delete actions typically produce a single mutation where nodes are removed
106
+ * from the DOM without any corresponding additions. This function checks for
107
+ * that specific pattern.
108
+ *
109
+ * @param mutations - The list of DOM mutations to evaluate
110
+ * @returns An ExperienceCheckResult indicating success if a delete was detected, otherwise undefined
111
+ */
112
+ export const handleDeleteDomMutation = ({
113
+ mutations
114
+ }) => {
115
+ // Delete action produces a single childList mutation with only removedNodes
116
+ const childListMutations = mutations.filter(m => m.type === 'childList');
117
+
118
+ // Check for at least one mutation with removedNodes but no addedNodes
119
+ if (childListMutations.some(m => m.removedNodes.length > 0 && m.addedNodes.length === 0)) {
120
+ return {
121
+ status: 'success'
122
+ };
123
+ }
124
+ return undefined;
125
+ };
@@ -1,6 +1,7 @@
1
1
  import React, { useCallback, useEffect } from 'react';
2
2
  import { injectIntl, useIntl } from 'react-intl-next';
3
3
  import { ACTION, ACTION_SUBJECT, EVENT_TYPE, INPUT_METHOD } from '@atlaskit/editor-common/analytics';
4
+ import { BLOCK_MENU_ACTION_TEST_ID } from '@atlaskit/editor-common/block-menu';
4
5
  import { blockMenuMessages } from '@atlaskit/editor-common/messages';
5
6
  import { deleteSelectedRange, getSourceNodesFromSelectionRange } from '@atlaskit/editor-common/selection';
6
7
  import { ToolbarDropdownItem } from '@atlaskit/editor-toolbar';
@@ -101,7 +102,8 @@ const DeleteDropdownItemContent = ({
101
102
  color: "var(--ds-icon-danger, #C9372C)",
102
103
  label: ""
103
104
  }),
104
- onClick: onClick
105
+ onClick: onClick,
106
+ testId: BLOCK_MENU_ACTION_TEST_ID.DELETE
105
107
  }, /*#__PURE__*/React.createElement(Text, {
106
108
  as: "span",
107
109
  color: "color.text.danger"
@@ -1,7 +1,8 @@
1
1
  import React, { useEffect } from 'react';
2
- import { useIntl, injectIntl } from 'react-intl-next';
2
+ import { injectIntl, useIntl } from 'react-intl-next';
3
3
  import { getDocument } from '@atlaskit/browser-apis';
4
4
  import { ACTION, ACTION_SUBJECT, EVENT_TYPE } from '@atlaskit/editor-common/analytics';
5
+ import { BLOCK_MENU_ACTION_TEST_ID } from '@atlaskit/editor-common/block-menu';
5
6
  import { useSharedPluginStateWithSelector } from '@atlaskit/editor-common/hooks';
6
7
  import { blockMenuMessages as messages } from '@atlaskit/editor-common/messages';
7
8
  import { DIRECTION } from '@atlaskit/editor-common/types';
@@ -69,7 +70,8 @@ const MoveDownDropdownItemContent = ({
69
70
  elemBefore: /*#__PURE__*/React.createElement(ArrowDownIcon, {
70
71
  label: ""
71
72
  }),
72
- isDisabled: !canMoveDown
73
+ isDisabled: !canMoveDown,
74
+ testId: BLOCK_MENU_ACTION_TEST_ID.MOVE_DOWN
73
75
  }, formatMessage(messages.moveDownBlock));
74
76
  };
75
77
  export const MoveDownDropdownItem = injectIntl(MoveDownDropdownItemContent);
@@ -1,7 +1,8 @@
1
1
  import React, { useEffect } from 'react';
2
- import { useIntl, injectIntl } from 'react-intl-next';
2
+ import { injectIntl, useIntl } from 'react-intl-next';
3
3
  import { getDocument } from '@atlaskit/browser-apis';
4
4
  import { ACTION, ACTION_SUBJECT, EVENT_TYPE } from '@atlaskit/editor-common/analytics';
5
+ import { BLOCK_MENU_ACTION_TEST_ID } from '@atlaskit/editor-common/block-menu';
5
6
  import { useSharedPluginStateWithSelector } from '@atlaskit/editor-common/hooks';
6
7
  import { blockMenuMessages as messages } from '@atlaskit/editor-common/messages';
7
8
  import { DIRECTION } from '@atlaskit/editor-common/types';
@@ -67,7 +68,8 @@ const MoveUpDropdownItemContent = ({
67
68
  elemBefore: /*#__PURE__*/React.createElement(ArrowUpIcon, {
68
69
  label: ""
69
70
  }),
70
- isDisabled: !canMoveUp
71
+ isDisabled: !canMoveUp,
72
+ testId: BLOCK_MENU_ACTION_TEST_ID.MOVE_UP
71
73
  }, formatMessage(messages.moveUpBlock));
72
74
  };
73
75
  export const MoveUpDropdownItem = injectIntl(MoveUpDropdownItemContent);
@@ -31,6 +31,9 @@ export var transformNode = function transformNode(api) {
31
31
  var typeName = node.type.name;
32
32
  sourceNodeTypes[typeName] = (sourceNodeTypes[typeName] || 0) + 1;
33
33
  });
34
+
35
+ // Check if source node is empty paragraph or heading
36
+ var isEmptyLine = sourceNodes.length === 1 && (sourceNodes[0].type === nodes.paragraph || sourceNodes[0].type === nodes.heading) && (sourceNodes[0].content.size === 0 || sourceNodes[0].textContent.trim() === '');
34
37
  var resultNodes = convertNodesToTargetType({
35
38
  sourceNodes: sourceNodes,
36
39
  targetNodeType: targetType,
@@ -69,12 +72,12 @@ export var transformNode = function transformNode(api) {
69
72
  }
70
73
  stopMeasure(measureId, function (duration, startTime) {
71
74
  var _api$analytics;
72
- api === null || api === void 0 || (_api$analytics = api.analytics) === null || _api$analytics === void 0 || (_api$analytics = _api$analytics.actions) === null || _api$analytics === void 0 || _api$analytics.fireAnalyticsEvent({
75
+ api === null || api === void 0 || (_api$analytics = api.analytics) === null || _api$analytics === void 0 || (_api$analytics = _api$analytics.actions) === null || _api$analytics === void 0 || _api$analytics.attachAnalyticsEvent({
73
76
  action: ACTION.TRANSFORMED,
74
77
  actionSubject: ACTION_SUBJECT.ELEMENT,
75
78
  attributes: {
76
79
  duration: duration,
77
- isList: isList,
80
+ isEmptyLine: isEmptyLine,
78
81
  isNested: isNested,
79
82
  sourceNodesCount: sourceNodes.length,
80
83
  sourceNodesCountByType: sourceNodeTypes,
@@ -85,7 +88,7 @@ export var transformNode = function transformNode(api) {
85
88
  inputMethod: INPUT_METHOD.BLOCK_MENU
86
89
  },
87
90
  eventType: EVENT_TYPE.TRACK
88
- });
91
+ })(tr);
89
92
  });
90
93
  return tr;
91
94
  };
@@ -1,9 +1,10 @@
1
- import _toConsumableArray from "@babel/runtime/helpers/toConsumableArray";
2
1
  import { bind } from 'bind-event-listener';
3
- import { ACTION_SUBJECT_ID } from '@atlaskit/editor-common/analytics';
4
- import { Experience, EXPERIENCE_ID, ExperienceCheckDomMutation, ExperienceCheckTimeout, getPopupContainerFromEditorView, popupWithNestedElement } from '@atlaskit/editor-common/experiences';
2
+ import { ACTION, ACTION_SUBJECT_ID } from '@atlaskit/editor-common/analytics';
3
+ import { BLOCK_MENU_ACTION_TEST_ID } from '@atlaskit/editor-common/block-menu';
4
+ import { Experience, EXPERIENCE_ID, ExperienceCheckDomMutation, ExperienceCheckTimeout, getPopupContainerFromEditorView } from '@atlaskit/editor-common/experiences';
5
5
  import { SafePlugin } from '@atlaskit/editor-common/safe-plugin';
6
6
  import { PluginKey } from '@atlaskit/editor-prosemirror/state';
7
+ import { getParentDOMAtSelection, handleDeleteDomMutation, handleMenuOpenDomMutation, handleMoveDomMutation, isBlockMenuVisible, isDragHandleElement } from './experience-check-utils';
7
8
  var TIMEOUT_DURATION = 1000;
8
9
  var pluginKey = new PluginKey('blockMenuExperiences');
9
10
  var START_METHOD = {
@@ -17,13 +18,14 @@ var ABORT_REASON = {
17
18
  export var getBlockMenuExperiencesPlugin = function getBlockMenuExperiencesPlugin(_ref) {
18
19
  var refs = _ref.refs,
19
20
  dispatchAnalyticsEvent = _ref.dispatchAnalyticsEvent;
20
- var targetEl;
21
- var editorViewEl;
21
+ var popupTargetEl;
22
+ var editorView;
22
23
  var getPopupsTarget = function getPopupsTarget() {
23
- if (!targetEl) {
24
- targetEl = refs.popupsMountPoint || getPopupContainerFromEditorView(editorViewEl);
24
+ if (!popupTargetEl) {
25
+ var _editorView;
26
+ popupTargetEl = refs.popupsMountPoint || getPopupContainerFromEditorView((_editorView = editorView) === null || _editorView === void 0 ? void 0 : _editorView.dom);
25
27
  }
26
- return targetEl;
28
+ return popupTargetEl;
27
29
  };
28
30
  var blockMenuOpenExperience = new Experience(EXPERIENCE_ID.MENU_OPEN, {
29
31
  actionSubjectId: ACTION_SUBJECT_ID.BLOCK_MENU,
@@ -31,15 +33,7 @@ export var getBlockMenuExperiencesPlugin = function getBlockMenuExperiencesPlugi
31
33
  checks: [new ExperienceCheckTimeout({
32
34
  durationMs: TIMEOUT_DURATION
33
35
  }), new ExperienceCheckDomMutation({
34
- onDomMutation: function onDomMutation(_ref2) {
35
- var mutations = _ref2.mutations;
36
- if (mutations.some(isBlockMenuAddedInMutation)) {
37
- return {
38
- status: 'success'
39
- };
40
- }
41
- return undefined;
42
- },
36
+ onDomMutation: handleMenuOpenDomMutation,
43
37
  observeConfig: function observeConfig() {
44
38
  return {
45
39
  target: getPopupsTarget(),
@@ -50,26 +44,89 @@ export var getBlockMenuExperiencesPlugin = function getBlockMenuExperiencesPlugi
50
44
  }
51
45
  })]
52
46
  });
47
+ var actionObserveConfig = function actionObserveConfig() {
48
+ return {
49
+ target: getParentDOMAtSelection(editorView),
50
+ options: {
51
+ childList: true
52
+ }
53
+ };
54
+ };
55
+ var blockMoveUpExperience = new Experience(EXPERIENCE_ID.MENU_ACTION, {
56
+ action: ACTION.MOVED,
57
+ actionSubjectId: ACTION_SUBJECT_ID.MOVE_UP_BLOCK,
58
+ dispatchAnalyticsEvent: dispatchAnalyticsEvent,
59
+ checks: [new ExperienceCheckTimeout({
60
+ durationMs: TIMEOUT_DURATION
61
+ }), new ExperienceCheckDomMutation({
62
+ onDomMutation: handleMoveDomMutation,
63
+ observeConfig: actionObserveConfig
64
+ })]
65
+ });
66
+ var blockMoveDownExperience = new Experience(EXPERIENCE_ID.MENU_ACTION, {
67
+ action: ACTION.MOVED,
68
+ actionSubjectId: ACTION_SUBJECT_ID.MOVE_DOWN_BLOCK,
69
+ dispatchAnalyticsEvent: dispatchAnalyticsEvent,
70
+ checks: [new ExperienceCheckTimeout({
71
+ durationMs: TIMEOUT_DURATION
72
+ }), new ExperienceCheckDomMutation({
73
+ onDomMutation: handleMoveDomMutation,
74
+ observeConfig: actionObserveConfig
75
+ })]
76
+ });
77
+ var blockDeleteExperience = new Experience(EXPERIENCE_ID.MENU_ACTION, {
78
+ action: ACTION.DELETED,
79
+ actionSubjectId: ACTION_SUBJECT_ID.DELETE_BLOCK,
80
+ dispatchAnalyticsEvent: dispatchAnalyticsEvent,
81
+ checks: [new ExperienceCheckTimeout({
82
+ durationMs: TIMEOUT_DURATION
83
+ }), new ExperienceCheckDomMutation({
84
+ onDomMutation: handleDeleteDomMutation,
85
+ observeConfig: actionObserveConfig
86
+ })]
87
+ });
88
+ var handleMenuOpened = function handleMenuOpened(method) {
89
+ // Don't start if block menu is already visible
90
+ if (isBlockMenuVisible(getPopupsTarget())) {
91
+ return;
92
+ }
93
+ blockMenuOpenExperience.start({
94
+ method: method
95
+ });
96
+ };
97
+ var handleItemActioned = function handleItemActioned(target) {
98
+ var button = target.closest('button[data-testid]');
99
+ if (!button || !(button instanceof HTMLButtonElement) || button.disabled || button.getAttribute('aria-disabled') === 'true') {
100
+ return;
101
+ }
102
+ var testId = button.dataset.testid;
103
+ if (!testId) {
104
+ return;
105
+ }
106
+ switch (testId) {
107
+ case BLOCK_MENU_ACTION_TEST_ID.MOVE_UP:
108
+ blockMoveUpExperience.start();
109
+ break;
110
+ case BLOCK_MENU_ACTION_TEST_ID.MOVE_DOWN:
111
+ blockMoveDownExperience.start();
112
+ break;
113
+ case BLOCK_MENU_ACTION_TEST_ID.DELETE:
114
+ blockDeleteExperience.start();
115
+ break;
116
+ }
117
+ };
53
118
  var unbindClickListener = bind(document, {
54
119
  type: 'click',
55
120
  listener: function listener(event) {
56
- if (!(event.target instanceof Element)) {
57
- return;
58
- }
59
121
  var target = event.target;
60
-
61
- // Check if the click is on a drag handle
62
- if (!isDragHandleElement(target)) {
122
+ if (!(target instanceof HTMLElement)) {
63
123
  return;
64
124
  }
65
-
66
- // Don't start if block menu is already visible
67
- if (isBlockMenuVisible(getPopupsTarget())) {
68
- return;
125
+ if (isDragHandleElement(target)) {
126
+ handleMenuOpened(START_METHOD.DRAG_HANDLE_CLICK);
127
+ } else {
128
+ handleItemActioned(target);
69
129
  }
70
- blockMenuOpenExperience.start({
71
- method: START_METHOD.DRAG_HANDLE_CLICK
72
- });
73
130
  },
74
131
  options: {
75
132
  capture: true
@@ -78,20 +135,14 @@ export var getBlockMenuExperiencesPlugin = function getBlockMenuExperiencesPlugi
78
135
  var unbindKeydownListener = bind(document, {
79
136
  type: 'keydown',
80
137
  listener: function listener(event) {
81
- if (!(event.target instanceof Element)) {
138
+ var target = event.target;
139
+ if (!(target instanceof HTMLElement)) {
82
140
  return;
83
141
  }
84
- var target = event.target;
85
142
 
86
143
  // Check if Enter or Space is pressed on a drag handle
87
144
  if ((event.key === 'Enter' || event.key === ' ') && isDragHandleElement(target)) {
88
- // Don't start if block menu is already visible
89
- if (isBlockMenuVisible(getPopupsTarget())) {
90
- return;
91
- }
92
- blockMenuOpenExperience.start({
93
- method: START_METHOD.KEYBOARD
94
- });
145
+ handleMenuOpened(START_METHOD.KEYBOARD);
95
146
  }
96
147
 
97
148
  // Abort on Escape key if block menu is not yet visible
@@ -107,34 +158,27 @@ export var getBlockMenuExperiencesPlugin = function getBlockMenuExperiencesPlugi
107
158
  });
108
159
  return new SafePlugin({
109
160
  key: pluginKey,
110
- view: function view(editorView) {
111
- editorViewEl = editorView.dom;
161
+ view: function view(_view) {
162
+ editorView = _view;
112
163
  return {
113
164
  destroy: function destroy() {
114
165
  blockMenuOpenExperience.abort({
115
166
  reason: ABORT_REASON.EDITOR_DESTROYED
116
167
  });
168
+ blockMoveUpExperience.abort({
169
+ reason: ABORT_REASON.EDITOR_DESTROYED
170
+ });
171
+ blockMoveDownExperience.abort({
172
+ reason: ABORT_REASON.EDITOR_DESTROYED
173
+ });
174
+ blockDeleteExperience.abort({
175
+ reason: ABORT_REASON.EDITOR_DESTROYED
176
+ });
177
+ editorView = undefined;
117
178
  unbindClickListener();
118
179
  unbindKeydownListener();
119
180
  }
120
181
  };
121
182
  }
122
183
  });
123
- };
124
- var isBlockMenuAddedInMutation = function isBlockMenuAddedInMutation(_ref3) {
125
- var type = _ref3.type,
126
- addedNodes = _ref3.addedNodes;
127
- return type === 'childList' && _toConsumableArray(addedNodes).some(isBlockMenuWithinNode);
128
- };
129
- var isBlockMenuWithinNode = function isBlockMenuWithinNode(node) {
130
- return popupWithNestedElement(node, '[data-testid="editor-block-menu"]') !== null;
131
- };
132
- var isDragHandleElement = function isDragHandleElement(element) {
133
- return !!(element !== null && element !== void 0 && element.closest('[data-editor-block-ctrl-drag-handle]'));
134
- };
135
- var isBlockMenuVisible = function isBlockMenuVisible(popupsTarget) {
136
- if (!popupsTarget) {
137
- return false;
138
- }
139
- return popupWithNestedElement(popupsTarget, '[data-testid="editor-block-menu"]') !== null;
140
184
  };