@atlaskit/editor-plugin-toolbar 3.2.1 → 3.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (32) hide show
  1. package/CHANGELOG.md +23 -3
  2. package/afm-cc/tsconfig.json +3 -0
  3. package/afm-dev-agents/tsconfig.json +3 -0
  4. package/afm-jira/tsconfig.json +3 -0
  5. package/afm-passionfruit/tsconfig.json +3 -0
  6. package/afm-post-office/tsconfig.json +3 -0
  7. package/afm-rovo-extension/tsconfig.json +3 -0
  8. package/afm-townsquare/tsconfig.json +3 -0
  9. package/dist/cjs/pm-plugins/experiences/ContextualToolbarOpenExperience.js +73 -0
  10. package/dist/cjs/toolbarPlugin.js +17 -1
  11. package/dist/cjs/ui/SelectionToolbar/index.js +66 -93
  12. package/dist/cjs/ui/SelectionToolbar/keyboard-config.js +70 -0
  13. package/dist/cjs/ui/SelectionToolbar/utils.js +42 -0
  14. package/dist/es2019/pm-plugins/experiences/ContextualToolbarOpenExperience.js +61 -0
  15. package/dist/es2019/toolbarPlugin.js +16 -3
  16. package/dist/es2019/ui/SelectionToolbar/index.js +63 -91
  17. package/dist/es2019/ui/SelectionToolbar/keyboard-config.js +64 -0
  18. package/dist/es2019/ui/SelectionToolbar/utils.js +36 -0
  19. package/dist/esm/pm-plugins/experiences/ContextualToolbarOpenExperience.js +66 -0
  20. package/dist/esm/toolbarPlugin.js +19 -3
  21. package/dist/esm/ui/SelectionToolbar/index.js +64 -91
  22. package/dist/esm/ui/SelectionToolbar/keyboard-config.js +64 -0
  23. package/dist/esm/ui/SelectionToolbar/utils.js +36 -0
  24. package/dist/types/pm-plugins/experiences/ContextualToolbarOpenExperience.d.ts +5 -0
  25. package/dist/types/ui/SelectionToolbar/index.d.ts +2 -1
  26. package/dist/types/ui/SelectionToolbar/keyboard-config.d.ts +6 -0
  27. package/dist/types/ui/SelectionToolbar/utils.d.ts +3 -0
  28. package/dist/types-ts4.5/pm-plugins/experiences/ContextualToolbarOpenExperience.d.ts +5 -0
  29. package/dist/types-ts4.5/ui/SelectionToolbar/index.d.ts +2 -1
  30. package/dist/types-ts4.5/ui/SelectionToolbar/keyboard-config.d.ts +6 -0
  31. package/dist/types-ts4.5/ui/SelectionToolbar/utils.d.ts +3 -0
  32. package/package.json +13 -4
@@ -2,12 +2,14 @@ import React from 'react';
2
2
  import { bind } from 'bind-event-listener';
3
3
  import { SafePlugin } from '@atlaskit/editor-common/safe-plugin';
4
4
  import { NodeSelection } from '@atlaskit/editor-prosemirror/state';
5
- import { findSelectedNodeOfType, findParentNodeOfType } from '@atlaskit/editor-prosemirror/utils';
5
+ import { findParentNodeOfType, findSelectedNodeOfType } from '@atlaskit/editor-prosemirror/utils';
6
6
  import { createComponentRegistry } from '@atlaskit/editor-toolbar-model';
7
+ import { fg } from '@atlaskit/platform-feature-flags';
7
8
  import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
9
+ import contextualToolbarOpenExperience from './pm-plugins/experiences/ContextualToolbarOpenExperience';
8
10
  import { editorToolbarPluginKey } from './pm-plugins/plugin-key';
9
11
  import { DEFAULT_POPUP_SELECTORS } from './ui/consts';
10
- import { SelectionToolbar } from './ui/SelectionToolbar';
12
+ import { SelectionToolbar, SelectionToolbarWithErrorBoundary } from './ui/SelectionToolbar';
11
13
  import { getToolbarComponents } from './ui/toolbar-components';
12
14
  import { isEventInContainer } from './ui/utils/toolbar';
13
15
  function getSelectedNode(editorState) {
@@ -168,7 +170,10 @@ export const toolbarPlugin = ({
168
170
  }
169
171
  });
170
172
  }
171
- }];
173
+ }, ...(expValEquals('platform_editor_experience_tracking', 'isEnabled', true) ? [{
174
+ name: 'contextualToolbarOpenExperience',
175
+ plugin: () => contextualToolbarOpenExperience()
176
+ }] : [])];
172
177
  },
173
178
  contentComponent: !disableSelectionToolbar ? ({
174
179
  editorView,
@@ -177,6 +182,14 @@ export const toolbarPlugin = ({
177
182
  if (!editorView) {
178
183
  return null;
179
184
  }
185
+ if (fg('platform_editor_toolbar_aifc_patch_7')) {
186
+ return /*#__PURE__*/React.createElement(SelectionToolbarWithErrorBoundary, {
187
+ api: api,
188
+ editorView: editorView,
189
+ mountPoint: popupsMountPoint,
190
+ disableSelectionToolbarWhenPinned: disableSelectionToolbarWhenPinned !== null && disableSelectionToolbarWhenPinned !== void 0 ? disableSelectionToolbarWhenPinned : false
191
+ });
192
+ }
180
193
  return /*#__PURE__*/React.createElement(SelectionToolbar, {
181
194
  api: api,
182
195
  editorView: editorView,
@@ -1,22 +1,24 @@
1
1
  import React, { useCallback, useMemo } from 'react';
2
2
  import { useIntl } from 'react-intl-next';
3
- import { getDocument } from '@atlaskit/browser-apis';
3
+ import { ACTION_SUBJECT, ACTION_SUBJECT_ID } from '@atlaskit/editor-common/analytics';
4
4
  import { isSSR } from '@atlaskit/editor-common/core-utils';
5
+ import { ErrorBoundary } from '@atlaskit/editor-common/error-boundary';
5
6
  import { useSharedPluginStateWithSelector } from '@atlaskit/editor-common/hooks';
6
- import { fullPageMessages } from '@atlaskit/editor-common/messages';
7
+ import { logException } from '@atlaskit/editor-common/monitoring';
7
8
  import { EditorToolbarProvider, EditorToolbarUIProvider } from '@atlaskit/editor-common/toolbar';
8
- import { Popup, EDIT_AREA_ID } from '@atlaskit/editor-common/ui';
9
+ import { Popup } from '@atlaskit/editor-common/ui';
9
10
  import { useSharedPluginStateSelector } from '@atlaskit/editor-common/use-shared-plugin-state-selector';
10
11
  import { calculateToolbarPositionTrackHead, calculateToolbarPositionOnCellSelection } from '@atlaskit/editor-common/utils';
11
12
  import { AllSelection, TextSelection } from '@atlaskit/editor-prosemirror/state';
12
- import { findDomRefAtPos } from '@atlaskit/editor-prosemirror/utils';
13
13
  import { ToolbarSection, ToolbarButtonGroup, ToolbarDropdownItemSection, useToolbarUI } from '@atlaskit/editor-toolbar';
14
14
  import { ToolbarModelRenderer } from '@atlaskit/editor-toolbar-model';
15
+ import { fg } from '@atlaskit/platform-feature-flags';
15
16
  import { conditionalHooksFactory } from '@atlaskit/platform-feature-flags-react';
16
17
  import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
17
18
  import { expValEqualsNoExposure } from '@atlaskit/tmp-editor-statsig/exp-val-equals-no-exposure';
18
19
  import { SELECTION_TOOLBAR_LABEL } from '../consts';
19
- import { getFocusableElements, isShortcutToFocusToolbar } from '../utils/toolbar';
20
+ import { getKeyboardNavigationConfig } from './keyboard-config';
21
+ import { getDomRefFromSelection } from './utils';
20
22
  const isToolbarComponent = component => {
21
23
  return component.type === 'toolbar' && component.key === 'inline-text-toolbar';
22
24
  };
@@ -58,6 +60,40 @@ const usePluginState = conditionalHooksFactory(() => expValEquals('platform_edit
58
60
  selectedNode
59
61
  };
60
62
  });
63
+ const useOnPositionCalculated = conditionalHooksFactory(() => fg('platform_editor_toolbar_aifc_patch_7'), editorView => {
64
+ const onPositionCalculated = useCallback(position => {
65
+ try {
66
+ const toolbarTitle = SELECTION_TOOLBAR_LABEL;
67
+
68
+ // Show special position on cell selection only when editor controls experiment is enabled
69
+ const isEditorControlsEnabled = expValEquals('platform_editor_controls', 'cohort', 'variant1');
70
+ const isCellSelection = ('$anchorCell' in editorView.state.selection);
71
+ if (isCellSelection && isEditorControlsEnabled) {
72
+ return calculateToolbarPositionOnCellSelection(toolbarTitle)(editorView, position);
73
+ }
74
+ return calculateToolbarPositionTrackHead(toolbarTitle)(editorView, position);
75
+ } catch (error) {
76
+ logException(error, {
77
+ location: 'editor-plugin-toolbar/selectionToolbar'
78
+ });
79
+ return position;
80
+ }
81
+ }, [editorView]);
82
+ return onPositionCalculated;
83
+ }, editorView => {
84
+ const onPositionCalculated = useCallback(position => {
85
+ const toolbarTitle = SELECTION_TOOLBAR_LABEL;
86
+
87
+ // Show special position on cell selection only when editor controls experiment is enabled
88
+ const isEditorControlsEnabled = expValEquals('platform_editor_controls', 'cohort', 'variant1');
89
+ const isCellSelection = ('$anchorCell' in editorView.state.selection);
90
+ if (isCellSelection && isEditorControlsEnabled) {
91
+ return calculateToolbarPositionOnCellSelection(toolbarTitle)(editorView, position);
92
+ }
93
+ return calculateToolbarPositionTrackHead(toolbarTitle)(editorView, position);
94
+ }, [editorView]);
95
+ return onPositionCalculated;
96
+ });
61
97
  export const SelectionToolbar = ({
62
98
  api,
63
99
  editorView,
@@ -88,21 +124,13 @@ export const SelectionToolbar = ({
88
124
  const isTextSelection = !editorView.state.selection.empty && editorView.state.selection instanceof TextSelection;
89
125
  const isAllSelection = !editorView.state.selection.empty && editorView.state.selection instanceof AllSelection;
90
126
  const isCellSelection = !editorView.state.selection.empty && '$anchorCell' in editorView.state.selection;
91
- const onPositionCalculated = useCallback(position => {
92
- const toolbarTitle = SELECTION_TOOLBAR_LABEL;
93
-
94
- // Show special position on cell selection only when editor controls experiment is enabled
95
- const isEditorControlsEnabled = expValEquals('platform_editor_controls', 'cohort', 'variant1');
96
- const isCellSelection = ('$anchorCell' in editorView.state.selection);
97
- if (isCellSelection && isEditorControlsEnabled) {
98
- return calculateToolbarPositionOnCellSelection(toolbarTitle)(editorView, position);
99
- }
100
- return calculateToolbarPositionTrackHead(toolbarTitle)(editorView, position);
101
- }, [editorView]);
127
+ const onPositionCalculated = useOnPositionCalculated(editorView);
102
128
  if (expValEquals('platform_editor_toolbar_aifc_template_editor', 'isEnabled', true) && editorToolbarDockingPreference === 'top' && disableSelectionToolbarWhenPinned || !components || !toolbar) {
103
129
  return null;
104
130
  }
105
- if (!(isTextSelection || isCellSelection || isAllSelection) || currentUserIntent === 'dragging' || !shouldShowToolbar || currentUserIntent === 'blockMenuOpen' && expValEqualsNoExposure('platform_editor_block_menu', 'isEnabled', true) || currentUserIntent && currentUserIntent !== 'default' || isSSR()) {
131
+ if (!(isTextSelection || isCellSelection || isAllSelection) || currentUserIntent === 'dragging' || !shouldShowToolbar || currentUserIntent === 'blockMenuOpen' && expValEqualsNoExposure('platform_editor_block_menu', 'isEnabled', true) || (fg('platform_editor_toolbar_aifc_user_intent_fix') ?
132
+ // hide toolbar when user intent is not default, except when it's dragHandleSelected without cell selection
133
+ currentUserIntent && currentUserIntent !== 'default' && !(currentUserIntent === 'dragHandleSelected' && !isCellSelection) : currentUserIntent && currentUserIntent !== 'default') || isSSR()) {
106
134
  return null;
107
135
  }
108
136
  return /*#__PURE__*/React.createElement(Popup, {
@@ -135,78 +163,22 @@ export const SelectionToolbar = ({
135
163
  }
136
164
  }))));
137
165
  };
138
- const getDomRefFromSelection = (view
139
- // dispatchAnalyticsEvent?: DispatchAnalyticsEvent,
140
- ) => {
141
- try {
142
- // Ignored via go/ees005
143
- // eslint-disable-next-line @atlaskit/editor/no-as-casting
144
- return findDomRefAtPos(view.state.selection.from, view.domAtPos.bind(view));
145
- // Ignored via go/ees005
146
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
147
- } catch (error) {
148
- // // eslint-disable-next-line no-console
149
- // console.warn(error);
150
- // if (dispatchAnalyticsEvent) {
151
- // const payload: AnalyticsEventPayload = {
152
- // action: ACTION.ERRORED,
153
- // actionSubject: ACTION_SUBJECT.CONTENT_COMPONENT,
154
- // eventType: EVENT_TYPE.OPERATIONAL,
155
- // attributes: {
156
- // component: CONTENT_COMPONENT.FLOATING_TOOLBAR,
157
- // selection: view.state.selection.toJSON(),
158
- // position: view.state.selection.from,
159
- // docSize: view.state.doc.nodeSize,
160
- // error: error.toString(),
161
- // // @ts-expect-error - Object literal may only specify known properties, 'errorStack' does not exist in type
162
- // // This error was introduced after upgrading to TypeScript 5
163
- // errorStack: error.stack || undefined,
164
- // },
165
- // };
166
- // dispatchAnalyticsEvent(payload);
167
- // }
168
- }
169
- };
170
- const getKeyboardNavigationConfig = (editorView, intl, api) => {
171
- if (!(editorView.dom instanceof HTMLElement)) {
172
- return;
173
- }
174
- const toolbarSelector = "[data-testid='editor-floating-toolbar']";
175
- return {
176
- childComponentSelector: toolbarSelector,
177
- dom: editorView.dom,
178
- isShortcutToFocusToolbar: isShortcutToFocusToolbar,
179
- handleFocus: event => {
180
- var _getDocument, _filteredFocusableEle, _filteredFocusableEle2, _filteredFocusableEle3;
181
- const toolbar = (_getDocument = getDocument()) === null || _getDocument === void 0 ? void 0 : _getDocument.querySelector(toolbarSelector);
182
- if (!(toolbar instanceof HTMLElement)) {
183
- return;
184
- }
185
- const filteredFocusableElements = getFocusableElements(toolbar);
186
- (_filteredFocusableEle = filteredFocusableElements[0]) === null || _filteredFocusableEle === void 0 ? void 0 : _filteredFocusableEle.focus();
187
-
188
- // the button element removes the focus ring so this class adds it back
189
- if (((_filteredFocusableEle2 = filteredFocusableElements[0]) === null || _filteredFocusableEle2 === void 0 ? void 0 : _filteredFocusableEle2.tagName) === 'BUTTON') {
190
- filteredFocusableElements[0].classList.add('first-floating-toolbar-button');
191
- }
192
- (_filteredFocusableEle3 = filteredFocusableElements[0]) === null || _filteredFocusableEle3 === void 0 ? void 0 : _filteredFocusableEle3.scrollIntoView({
193
- behavior: 'smooth',
194
- block: 'center',
195
- inline: 'nearest'
196
- });
197
- event.preventDefault();
198
- event.stopPropagation();
199
- },
200
- handleEscape: event => {
201
- const isDropdownOpen = !!document.querySelector('[data-toolbar-component="menu-section"]');
202
- if (isDropdownOpen) {
203
- return;
204
- }
205
- api === null || api === void 0 ? void 0 : api.core.actions.focus();
206
- event.preventDefault();
207
- event.stopPropagation();
208
- },
209
- ariaControls: EDIT_AREA_ID,
210
- ariaLabel: intl.formatMessage(fullPageMessages.toolbarLabel)
211
- };
166
+ export const SelectionToolbarWithErrorBoundary = ({
167
+ api,
168
+ editorView,
169
+ mountPoint,
170
+ disableSelectionToolbarWhenPinned
171
+ }) => {
172
+ var _api$analytics2;
173
+ return /*#__PURE__*/React.createElement(ErrorBoundary, {
174
+ component: ACTION_SUBJECT.TOOLBAR,
175
+ componentId: ACTION_SUBJECT_ID.SELECTION_TOOLBAR,
176
+ dispatchAnalyticsEvent: api === null || api === void 0 ? void 0 : (_api$analytics2 = api.analytics) === null || _api$analytics2 === void 0 ? void 0 : _api$analytics2.actions.fireAnalyticsEvent,
177
+ fallbackComponent: null
178
+ }, /*#__PURE__*/React.createElement(SelectionToolbar, {
179
+ api: api,
180
+ editorView: editorView,
181
+ mountPoint: mountPoint,
182
+ disableSelectionToolbarWhenPinned: disableSelectionToolbarWhenPinned
183
+ }));
212
184
  };
@@ -0,0 +1,64 @@
1
+ import { getDocument } from '@atlaskit/browser-apis';
2
+ import { fullPageMessages } from '@atlaskit/editor-common/messages';
3
+ import { logException } from '@atlaskit/editor-common/monitoring';
4
+ import { EDIT_AREA_ID } from '@atlaskit/editor-common/ui';
5
+ import { getFocusableElements, isShortcutToFocusToolbar } from '../utils/toolbar';
6
+ export const getKeyboardNavigationConfig = (editorView, intl, api) => {
7
+ if (!(editorView.dom instanceof HTMLElement)) {
8
+ return;
9
+ }
10
+ const toolbarSelector = "[data-testid='editor-floating-toolbar']";
11
+ return {
12
+ childComponentSelector: toolbarSelector,
13
+ dom: editorView.dom,
14
+ isShortcutToFocusToolbar: isShortcutToFocusToolbar,
15
+ handleFocus: event => {
16
+ try {
17
+ var _getDocument, _filteredFocusableEle, _filteredFocusableEle2, _filteredFocusableEle3;
18
+ const toolbar = (_getDocument = getDocument()) === null || _getDocument === void 0 ? void 0 : _getDocument.querySelector(toolbarSelector);
19
+ if (!(toolbar instanceof HTMLElement)) {
20
+ return;
21
+ }
22
+ const filteredFocusableElements = getFocusableElements(toolbar);
23
+ (_filteredFocusableEle = filteredFocusableElements[0]) === null || _filteredFocusableEle === void 0 ? void 0 : _filteredFocusableEle.focus();
24
+
25
+ // the button element removes the focus ring so this class adds it back
26
+ if (((_filteredFocusableEle2 = filteredFocusableElements[0]) === null || _filteredFocusableEle2 === void 0 ? void 0 : _filteredFocusableEle2.tagName) === 'BUTTON') {
27
+ filteredFocusableElements[0].classList.add('first-floating-toolbar-button');
28
+ }
29
+ (_filteredFocusableEle3 = filteredFocusableElements[0]) === null || _filteredFocusableEle3 === void 0 ? void 0 : _filteredFocusableEle3.scrollIntoView({
30
+ behavior: 'smooth',
31
+ block: 'center',
32
+ inline: 'nearest'
33
+ });
34
+ event.preventDefault();
35
+ event.stopPropagation();
36
+ } catch (error) {
37
+ if (error instanceof Error) {
38
+ logException(error, {
39
+ location: 'editor-plugin-toolbar/selectionToolbar'
40
+ });
41
+ }
42
+ }
43
+ },
44
+ handleEscape: event => {
45
+ try {
46
+ const isDropdownOpen = !!document.querySelector('[data-toolbar-component="menu-section"]');
47
+ if (isDropdownOpen) {
48
+ return;
49
+ }
50
+ api === null || api === void 0 ? void 0 : api.core.actions.focus();
51
+ event.preventDefault();
52
+ event.stopPropagation();
53
+ } catch (error) {
54
+ if (error instanceof Error) {
55
+ logException(error, {
56
+ location: 'editor-plugin-toolbar/selectionToolbar'
57
+ });
58
+ }
59
+ }
60
+ },
61
+ ariaControls: EDIT_AREA_ID,
62
+ ariaLabel: intl.formatMessage(fullPageMessages.toolbarLabel)
63
+ };
64
+ };
@@ -0,0 +1,36 @@
1
+ import { ACTION, ACTION_SUBJECT, CONTENT_COMPONENT, EVENT_TYPE } from '@atlaskit/editor-common/analytics';
2
+ import { logException } from '@atlaskit/editor-common/monitoring';
3
+ import { findDomRefAtPos } from '@atlaskit/editor-prosemirror/utils';
4
+ export const getDomRefFromSelection = (view, dispatchAnalyticsEvent) => {
5
+ try {
6
+ const domRef = findDomRefAtPos(view.state.selection.from, view.domAtPos.bind(view));
7
+ if (domRef instanceof HTMLElement) {
8
+ return domRef;
9
+ }
10
+ throw new Error('Invalid DOM reference');
11
+ } catch (error) {
12
+ if (dispatchAnalyticsEvent) {
13
+ const payload = {
14
+ action: ACTION.ERRORED,
15
+ actionSubject: ACTION_SUBJECT.CONTENT_COMPONENT,
16
+ eventType: EVENT_TYPE.OPERATIONAL,
17
+ attributes: {
18
+ component: CONTENT_COMPONENT.SELECTION_TOOLBAR,
19
+ selection: view.state.selection.toJSON(),
20
+ position: view.state.selection.from,
21
+ docSize: view.state.doc.nodeSize,
22
+ error: error instanceof Error ? error.toString() : String(error),
23
+ // @ts-expect-error - Object literal may only specify known properties, 'errorStack' does not exist in type
24
+ // This error was introduced after upgrading to TypeScript 5
25
+ errorStack: error instanceof Error ? error.stack : undefined
26
+ }
27
+ };
28
+ dispatchAnalyticsEvent(payload);
29
+ }
30
+ if (error instanceof Error) {
31
+ logException(error, {
32
+ location: 'editor-plugin-toolbar/selectionToolbar'
33
+ });
34
+ }
35
+ }
36
+ };
@@ -0,0 +1,66 @@
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; }
4
+ import { Experience, ExperienceCheckDomMutation, ExperienceCheckTimeout } from '@atlaskit/editor-common/experiences';
5
+ import { SafePlugin } from '@atlaskit/editor-common/safe-plugin';
6
+ import { PluginKey, TextSelection } from '@atlaskit/editor-prosemirror/state';
7
+ var pluginKey = new PluginKey('contextualToolbarOpenExperience');
8
+ export default (function () {
9
+ var contextualToolbarOpenExperience = new Experience('platform-editor-contextual-toolbar-open-experience', {
10
+ checks: [new ExperienceCheckTimeout(500), new ExperienceCheckDomMutation({
11
+ onDomMutation: function onDomMutation(_ref) {
12
+ var mutations = _ref.mutations;
13
+ if (mutations.some(isMutationAddingContextualToolbar)) {
14
+ return {
15
+ status: 'success'
16
+ };
17
+ }
18
+ },
19
+ observeConfig: function observeConfig() {
20
+ return {
21
+ target: document.body,
22
+ options: {
23
+ childList: true,
24
+ subtree: true
25
+ }
26
+ };
27
+ }
28
+ })]
29
+ });
30
+ return new SafePlugin({
31
+ key: pluginKey,
32
+ state: {
33
+ init: function init() {
34
+ return {
35
+ shouldShowContextualToolbar: false
36
+ };
37
+ },
38
+ apply: function apply(_tr, pluginState, _, newState) {
39
+ var isTextSelection = newState.selection instanceof TextSelection;
40
+ var isNotEmptySelection = !newState.selection.empty;
41
+ var shouldShowContextualToolbar = isTextSelection && isNotEmptySelection;
42
+ if (shouldShowContextualToolbar && !pluginState.shouldShowContextualToolbar) {
43
+ contextualToolbarOpenExperience.start();
44
+ } else if (!shouldShowContextualToolbar && pluginState.shouldShowContextualToolbar) {
45
+ contextualToolbarOpenExperience.abort();
46
+ }
47
+ return _objectSpread(_objectSpread({}, pluginState), {}, {
48
+ shouldShowContextualToolbar: shouldShowContextualToolbar
49
+ });
50
+ }
51
+ },
52
+ view: function view() {
53
+ return {
54
+ destroy: function destroy() {
55
+ contextualToolbarOpenExperience.abort();
56
+ }
57
+ };
58
+ }
59
+ });
60
+ });
61
+ var isMutationAddingContextualToolbar = function isMutationAddingContextualToolbar(mutation) {
62
+ return mutation.type === 'childList' && Array.from(mutation.addedNodes).some(nodeIncludesContextualToolbar);
63
+ };
64
+ var nodeIncludesContextualToolbar = function nodeIncludesContextualToolbar(node) {
65
+ return node instanceof HTMLElement && node.getAttribute('data-testid') === 'popup-wrapper' && node.querySelector('[data-testid="text-section"]');
66
+ };
@@ -1,16 +1,19 @@
1
1
  import _defineProperty from "@babel/runtime/helpers/defineProperty";
2
+ import _toConsumableArray from "@babel/runtime/helpers/toConsumableArray";
2
3
  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
4
  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; }
4
5
  import React from 'react';
5
6
  import { bind } from 'bind-event-listener';
6
7
  import { SafePlugin } from '@atlaskit/editor-common/safe-plugin';
7
8
  import { NodeSelection } from '@atlaskit/editor-prosemirror/state';
8
- import { findSelectedNodeOfType, findParentNodeOfType } from '@atlaskit/editor-prosemirror/utils';
9
+ import { findParentNodeOfType, findSelectedNodeOfType } from '@atlaskit/editor-prosemirror/utils';
9
10
  import { createComponentRegistry } from '@atlaskit/editor-toolbar-model';
11
+ import { fg } from '@atlaskit/platform-feature-flags';
10
12
  import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
13
+ import contextualToolbarOpenExperience from './pm-plugins/experiences/ContextualToolbarOpenExperience';
11
14
  import { editorToolbarPluginKey } from './pm-plugins/plugin-key';
12
15
  import { DEFAULT_POPUP_SELECTORS } from './ui/consts';
13
- import { SelectionToolbar } from './ui/SelectionToolbar';
16
+ import { SelectionToolbar, SelectionToolbarWithErrorBoundary } from './ui/SelectionToolbar';
14
17
  import { getToolbarComponents } from './ui/toolbar-components';
15
18
  import { isEventInContainer } from './ui/utils/toolbar';
16
19
  function getSelectedNode(editorState) {
@@ -169,7 +172,12 @@ export var toolbarPlugin = function toolbarPlugin(_ref) {
169
172
  }
170
173
  });
171
174
  }
172
- }];
175
+ }].concat(_toConsumableArray(expValEquals('platform_editor_experience_tracking', 'isEnabled', true) ? [{
176
+ name: 'contextualToolbarOpenExperience',
177
+ plugin: function plugin() {
178
+ return contextualToolbarOpenExperience();
179
+ }
180
+ }] : []));
173
181
  },
174
182
  contentComponent: !disableSelectionToolbar ? function (_ref2) {
175
183
  var editorView = _ref2.editorView,
@@ -177,6 +185,14 @@ export var toolbarPlugin = function toolbarPlugin(_ref) {
177
185
  if (!editorView) {
178
186
  return null;
179
187
  }
188
+ if (fg('platform_editor_toolbar_aifc_patch_7')) {
189
+ return /*#__PURE__*/React.createElement(SelectionToolbarWithErrorBoundary, {
190
+ api: api,
191
+ editorView: editorView,
192
+ mountPoint: popupsMountPoint,
193
+ disableSelectionToolbarWhenPinned: disableSelectionToolbarWhenPinned !== null && disableSelectionToolbarWhenPinned !== void 0 ? disableSelectionToolbarWhenPinned : false
194
+ });
195
+ }
180
196
  return /*#__PURE__*/React.createElement(SelectionToolbar, {
181
197
  api: api,
182
198
  editorView: editorView,
@@ -1,22 +1,24 @@
1
1
  import React, { useCallback, useMemo } from 'react';
2
2
  import { useIntl } from 'react-intl-next';
3
- import { getDocument } from '@atlaskit/browser-apis';
3
+ import { ACTION_SUBJECT, ACTION_SUBJECT_ID } from '@atlaskit/editor-common/analytics';
4
4
  import { isSSR } from '@atlaskit/editor-common/core-utils';
5
+ import { ErrorBoundary } from '@atlaskit/editor-common/error-boundary';
5
6
  import { useSharedPluginStateWithSelector } from '@atlaskit/editor-common/hooks';
6
- import { fullPageMessages } from '@atlaskit/editor-common/messages';
7
+ import { logException } from '@atlaskit/editor-common/monitoring';
7
8
  import { EditorToolbarProvider, EditorToolbarUIProvider } from '@atlaskit/editor-common/toolbar';
8
- import { Popup, EDIT_AREA_ID } from '@atlaskit/editor-common/ui';
9
+ import { Popup } from '@atlaskit/editor-common/ui';
9
10
  import { useSharedPluginStateSelector } from '@atlaskit/editor-common/use-shared-plugin-state-selector';
10
11
  import { calculateToolbarPositionTrackHead, calculateToolbarPositionOnCellSelection } from '@atlaskit/editor-common/utils';
11
12
  import { AllSelection, TextSelection } from '@atlaskit/editor-prosemirror/state';
12
- import { findDomRefAtPos } from '@atlaskit/editor-prosemirror/utils';
13
13
  import { ToolbarSection, ToolbarButtonGroup, ToolbarDropdownItemSection, useToolbarUI } from '@atlaskit/editor-toolbar';
14
14
  import { ToolbarModelRenderer } from '@atlaskit/editor-toolbar-model';
15
+ import { fg } from '@atlaskit/platform-feature-flags';
15
16
  import { conditionalHooksFactory } from '@atlaskit/platform-feature-flags-react';
16
17
  import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
17
18
  import { expValEqualsNoExposure } from '@atlaskit/tmp-editor-statsig/exp-val-equals-no-exposure';
18
19
  import { SELECTION_TOOLBAR_LABEL } from '../consts';
19
- import { getFocusableElements, isShortcutToFocusToolbar } from '../utils/toolbar';
20
+ import { getKeyboardNavigationConfig } from './keyboard-config';
21
+ import { getDomRefFromSelection } from './utils';
20
22
  var isToolbarComponent = function isToolbarComponent(component) {
21
23
  return component.type === 'toolbar' && component.key === 'inline-text-toolbar';
22
24
  };
@@ -59,6 +61,42 @@ var usePluginState = conditionalHooksFactory(function () {
59
61
  selectedNode: selectedNode
60
62
  };
61
63
  });
64
+ var useOnPositionCalculated = conditionalHooksFactory(function () {
65
+ return fg('platform_editor_toolbar_aifc_patch_7');
66
+ }, function (editorView) {
67
+ var onPositionCalculated = useCallback(function (position) {
68
+ try {
69
+ var toolbarTitle = SELECTION_TOOLBAR_LABEL;
70
+
71
+ // Show special position on cell selection only when editor controls experiment is enabled
72
+ var isEditorControlsEnabled = expValEquals('platform_editor_controls', 'cohort', 'variant1');
73
+ var isCellSelection = ('$anchorCell' in editorView.state.selection);
74
+ if (isCellSelection && isEditorControlsEnabled) {
75
+ return calculateToolbarPositionOnCellSelection(toolbarTitle)(editorView, position);
76
+ }
77
+ return calculateToolbarPositionTrackHead(toolbarTitle)(editorView, position);
78
+ } catch (error) {
79
+ logException(error, {
80
+ location: 'editor-plugin-toolbar/selectionToolbar'
81
+ });
82
+ return position;
83
+ }
84
+ }, [editorView]);
85
+ return onPositionCalculated;
86
+ }, function (editorView) {
87
+ var onPositionCalculated = useCallback(function (position) {
88
+ var toolbarTitle = SELECTION_TOOLBAR_LABEL;
89
+
90
+ // Show special position on cell selection only when editor controls experiment is enabled
91
+ var isEditorControlsEnabled = expValEquals('platform_editor_controls', 'cohort', 'variant1');
92
+ var isCellSelection = ('$anchorCell' in editorView.state.selection);
93
+ if (isCellSelection && isEditorControlsEnabled) {
94
+ return calculateToolbarPositionOnCellSelection(toolbarTitle)(editorView, position);
95
+ }
96
+ return calculateToolbarPositionTrackHead(toolbarTitle)(editorView, position);
97
+ }, [editorView]);
98
+ return onPositionCalculated;
99
+ });
62
100
  export var SelectionToolbar = function SelectionToolbar(_ref) {
63
101
  var _api$toolbar;
64
102
  var api = _ref.api,
@@ -87,21 +125,13 @@ export var SelectionToolbar = function SelectionToolbar(_ref) {
87
125
  var isTextSelection = !editorView.state.selection.empty && editorView.state.selection instanceof TextSelection;
88
126
  var isAllSelection = !editorView.state.selection.empty && editorView.state.selection instanceof AllSelection;
89
127
  var isCellSelection = !editorView.state.selection.empty && '$anchorCell' in editorView.state.selection;
90
- var onPositionCalculated = useCallback(function (position) {
91
- var toolbarTitle = SELECTION_TOOLBAR_LABEL;
92
-
93
- // Show special position on cell selection only when editor controls experiment is enabled
94
- var isEditorControlsEnabled = expValEquals('platform_editor_controls', 'cohort', 'variant1');
95
- var isCellSelection = ('$anchorCell' in editorView.state.selection);
96
- if (isCellSelection && isEditorControlsEnabled) {
97
- return calculateToolbarPositionOnCellSelection(toolbarTitle)(editorView, position);
98
- }
99
- return calculateToolbarPositionTrackHead(toolbarTitle)(editorView, position);
100
- }, [editorView]);
128
+ var onPositionCalculated = useOnPositionCalculated(editorView);
101
129
  if (expValEquals('platform_editor_toolbar_aifc_template_editor', 'isEnabled', true) && editorToolbarDockingPreference === 'top' && disableSelectionToolbarWhenPinned || !components || !toolbar) {
102
130
  return null;
103
131
  }
104
- if (!(isTextSelection || isCellSelection || isAllSelection) || currentUserIntent === 'dragging' || !shouldShowToolbar || currentUserIntent === 'blockMenuOpen' && expValEqualsNoExposure('platform_editor_block_menu', 'isEnabled', true) || currentUserIntent && currentUserIntent !== 'default' || isSSR()) {
132
+ if (!(isTextSelection || isCellSelection || isAllSelection) || currentUserIntent === 'dragging' || !shouldShowToolbar || currentUserIntent === 'blockMenuOpen' && expValEqualsNoExposure('platform_editor_block_menu', 'isEnabled', true) || (fg('platform_editor_toolbar_aifc_user_intent_fix') ?
133
+ // hide toolbar when user intent is not default, except when it's dragHandleSelected without cell selection
134
+ currentUserIntent && currentUserIntent !== 'default' && !(currentUserIntent === 'dragHandleSelected' && !isCellSelection) : currentUserIntent && currentUserIntent !== 'default') || isSSR()) {
105
135
  return null;
106
136
  }
107
137
  return /*#__PURE__*/React.createElement(Popup, {
@@ -134,78 +164,21 @@ export var SelectionToolbar = function SelectionToolbar(_ref) {
134
164
  }
135
165
  }))));
136
166
  };
137
- var getDomRefFromSelection = function getDomRefFromSelection(view
138
- // dispatchAnalyticsEvent?: DispatchAnalyticsEvent,
139
- ) {
140
- try {
141
- // Ignored via go/ees005
142
- // eslint-disable-next-line @atlaskit/editor/no-as-casting
143
- return findDomRefAtPos(view.state.selection.from, view.domAtPos.bind(view));
144
- // Ignored via go/ees005
145
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
146
- } catch (error) {
147
- // // eslint-disable-next-line no-console
148
- // console.warn(error);
149
- // if (dispatchAnalyticsEvent) {
150
- // const payload: AnalyticsEventPayload = {
151
- // action: ACTION.ERRORED,
152
- // actionSubject: ACTION_SUBJECT.CONTENT_COMPONENT,
153
- // eventType: EVENT_TYPE.OPERATIONAL,
154
- // attributes: {
155
- // component: CONTENT_COMPONENT.FLOATING_TOOLBAR,
156
- // selection: view.state.selection.toJSON(),
157
- // position: view.state.selection.from,
158
- // docSize: view.state.doc.nodeSize,
159
- // error: error.toString(),
160
- // // @ts-expect-error - Object literal may only specify known properties, 'errorStack' does not exist in type
161
- // // This error was introduced after upgrading to TypeScript 5
162
- // errorStack: error.stack || undefined,
163
- // },
164
- // };
165
- // dispatchAnalyticsEvent(payload);
166
- // }
167
- }
168
- };
169
- var getKeyboardNavigationConfig = function getKeyboardNavigationConfig(editorView, intl, api) {
170
- if (!(editorView.dom instanceof HTMLElement)) {
171
- return;
172
- }
173
- var toolbarSelector = "[data-testid='editor-floating-toolbar']";
174
- return {
175
- childComponentSelector: toolbarSelector,
176
- dom: editorView.dom,
177
- isShortcutToFocusToolbar: isShortcutToFocusToolbar,
178
- handleFocus: function handleFocus(event) {
179
- var _getDocument, _filteredFocusableEle, _filteredFocusableEle2, _filteredFocusableEle3;
180
- var toolbar = (_getDocument = getDocument()) === null || _getDocument === void 0 ? void 0 : _getDocument.querySelector(toolbarSelector);
181
- if (!(toolbar instanceof HTMLElement)) {
182
- return;
183
- }
184
- var filteredFocusableElements = getFocusableElements(toolbar);
185
- (_filteredFocusableEle = filteredFocusableElements[0]) === null || _filteredFocusableEle === void 0 || _filteredFocusableEle.focus();
186
-
187
- // the button element removes the focus ring so this class adds it back
188
- if (((_filteredFocusableEle2 = filteredFocusableElements[0]) === null || _filteredFocusableEle2 === void 0 ? void 0 : _filteredFocusableEle2.tagName) === 'BUTTON') {
189
- filteredFocusableElements[0].classList.add('first-floating-toolbar-button');
190
- }
191
- (_filteredFocusableEle3 = filteredFocusableElements[0]) === null || _filteredFocusableEle3 === void 0 || _filteredFocusableEle3.scrollIntoView({
192
- behavior: 'smooth',
193
- block: 'center',
194
- inline: 'nearest'
195
- });
196
- event.preventDefault();
197
- event.stopPropagation();
198
- },
199
- handleEscape: function handleEscape(event) {
200
- var isDropdownOpen = !!document.querySelector('[data-toolbar-component="menu-section"]');
201
- if (isDropdownOpen) {
202
- return;
203
- }
204
- api === null || api === void 0 || api.core.actions.focus();
205
- event.preventDefault();
206
- event.stopPropagation();
207
- },
208
- ariaControls: EDIT_AREA_ID,
209
- ariaLabel: intl.formatMessage(fullPageMessages.toolbarLabel)
210
- };
167
+ export var SelectionToolbarWithErrorBoundary = function SelectionToolbarWithErrorBoundary(_ref2) {
168
+ var _api$analytics2;
169
+ var api = _ref2.api,
170
+ editorView = _ref2.editorView,
171
+ mountPoint = _ref2.mountPoint,
172
+ disableSelectionToolbarWhenPinned = _ref2.disableSelectionToolbarWhenPinned;
173
+ return /*#__PURE__*/React.createElement(ErrorBoundary, {
174
+ component: ACTION_SUBJECT.TOOLBAR,
175
+ componentId: ACTION_SUBJECT_ID.SELECTION_TOOLBAR,
176
+ dispatchAnalyticsEvent: api === null || api === void 0 || (_api$analytics2 = api.analytics) === null || _api$analytics2 === void 0 ? void 0 : _api$analytics2.actions.fireAnalyticsEvent,
177
+ fallbackComponent: null
178
+ }, /*#__PURE__*/React.createElement(SelectionToolbar, {
179
+ api: api,
180
+ editorView: editorView,
181
+ mountPoint: mountPoint,
182
+ disableSelectionToolbarWhenPinned: disableSelectionToolbarWhenPinned
183
+ }));
211
184
  };