@atlaskit/editor-plugin-toolbar 3.4.9 → 3.4.10

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 CHANGED
@@ -1,5 +1,13 @@
1
1
  # @atlaskit/editor-plugin-toolbar
2
2
 
3
+ ## 3.4.10
4
+
5
+ ### Patch Changes
6
+
7
+ - [`7af1ad7f6bb6b`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/7af1ad7f6bb6b) -
8
+ Fix toolbar experience tracking failures
9
+ - Updated dependencies
10
+
3
11
  ## 3.4.9
4
12
 
5
13
  ### Patch Changes
@@ -23,31 +23,40 @@ var ABORT_REASON = {
23
23
  * This experience tracks when the selection toolbar is opened.
24
24
  *
25
25
  * Start: When user makes a selection via mouseup or shift+arrow key down
26
- * Success: When the selection toolbar is added to the DOM within 500ms of start
27
- * Failure: When 500ms passes without the selection toolbar being added to the DOM
28
- * Abort: When selection transition to empty or block menu is opened
26
+ * Success: When the selection toolbar is added to the DOM within 1000ms of start
27
+ * Failure: When 1000ms passes without the selection toolbar being added to the DOM
28
+ * Abort: When selection transitions to empty or block menu is opened
29
29
  */
30
30
  var getSelectionToolbarOpenExperiencePlugin = exports.getSelectionToolbarOpenExperiencePlugin = function getSelectionToolbarOpenExperiencePlugin(_ref) {
31
31
  var refs = _ref.refs,
32
32
  dispatchAnalyticsEvent = _ref.dispatchAnalyticsEvent;
33
+ var editorView;
33
34
  var targetEl;
34
- var editorViewEl;
35
+ var shiftArrowKeyPressed = false;
36
+ var mouseDownPos;
35
37
  var getTarget = function getTarget() {
36
38
  if (!targetEl) {
37
- targetEl = refs.popupsMountPoint || (0, _experiences.getPopupContainerFromEditorView)(editorViewEl);
39
+ var _editorView;
40
+ targetEl = refs.popupsMountPoint || (0, _experiences.getPopupContainerFromEditorView)((_editorView = editorView) === null || _editorView === void 0 ? void 0 : _editorView.dom);
38
41
  }
39
42
  return targetEl;
40
43
  };
41
44
  var experience = new _experiences.Experience('selection-toolbar-open', {
42
45
  dispatchAnalyticsEvent: dispatchAnalyticsEvent,
43
46
  checks: [new _experiences.ExperienceCheckTimeout({
44
- durationMs: 500,
47
+ durationMs: 1000,
45
48
  onTimeout: function onTimeout() {
49
+ var _editorView2;
46
50
  if (isBlockMenuWithinNode(getTarget())) {
47
51
  return {
48
52
  status: 'abort',
49
53
  reason: ABORT_REASON.BLOCK_MENU_OPENED
50
54
  };
55
+ } else if (isSelectionWithoutTextContent((_editorView2 = editorView) === null || _editorView2 === void 0 ? void 0 : _editorView2.state.selection)) {
56
+ return {
57
+ status: 'abort',
58
+ reason: ABORT_REASON.SELECTION_CLEARED
59
+ };
51
60
  }
52
61
  }
53
62
  }), new _experiences.ExperienceCheckDomMutation({
@@ -76,36 +85,58 @@ var getSelectionToolbarOpenExperiencePlugin = exports.getSelectionToolbarOpenExp
76
85
  return {};
77
86
  },
78
87
  apply: function apply(_tr, pluginState, oldState, newState) {
79
- if (!oldState.selection.empty && newState.selection.empty) {
88
+ if (!oldState.selection.empty && isSelectionWithoutTextContent(newState.selection)) {
80
89
  experience.abort({
81
90
  reason: ABORT_REASON.SELECTION_CLEARED
82
91
  });
83
92
  }
93
+ if (shiftArrowKeyPressed && !newState.selection.eq(oldState.selection) && !isSelectionWithoutTextContent(newState.selection)) {
94
+ experience.start({
95
+ method: START_METHOD.KEY_DOWN
96
+ });
97
+ shiftArrowKeyPressed = false;
98
+ }
84
99
  return pluginState;
85
100
  }
86
101
  },
87
102
  props: {
88
103
  handleDOMEvents: {
89
- mouseup: function mouseup(view) {
90
- if (!view.state.selection.empty && !isSelectionToolbarWithinNode(getTarget())) {
104
+ mousedown: function mousedown(_view, e) {
105
+ mouseDownPos = {
106
+ x: e.clientX,
107
+ y: e.clientY
108
+ };
109
+ },
110
+ mouseup: function mouseup(view, e) {
111
+ if (!mouseDownPos || isSelectionWithoutTextContent(view.state.selection) || isSelectionWithinCodeBlock(view.state.selection) || isSelectionToolbarWithinNode(getTarget())) {
112
+ return;
113
+ }
114
+ if (e.clientX !== mouseDownPos.x || e.clientY !== mouseDownPos.y) {
91
115
  experience.start({
92
116
  method: START_METHOD.MOUSE_UP
93
117
  });
94
118
  }
95
119
  },
120
+ dblclick: function dblclick(view, e) {
121
+ if (isSelectionWithoutTextContent(view.state.selection) || isSelectionWithinCodeBlock(view.state.selection) || isSelectionToolbarWithinNode(getTarget())) {
122
+ return;
123
+ }
124
+ experience.start({
125
+ method: START_METHOD.MOUSE_UP
126
+ });
127
+ },
96
128
  keydown: function keydown(_view, _ref3) {
97
129
  var shiftKey = _ref3.shiftKey,
98
130
  key = _ref3.key;
99
- if (shiftKey && key.includes('Arrow') && !isSelectionToolbarWithinNode(getTarget())) {
100
- experience.start({
101
- method: START_METHOD.KEY_DOWN
102
- });
103
- }
131
+ shiftArrowKeyPressed = shiftKey && key.includes('Arrow') && !isSelectionToolbarWithinNode(getTarget());
132
+ },
133
+ keyup: function keyup() {
134
+ shiftArrowKeyPressed = false;
104
135
  }
105
136
  }
106
137
  },
107
138
  view: function view(_view2) {
108
- editorViewEl = _view2.dom;
139
+ editorView = _view2;
109
140
  return {
110
141
  destroy: function destroy() {
111
142
  experience.abort({
@@ -126,4 +157,26 @@ var isSelectionToolbarWithinNode = function isSelectionToolbarWithinNode(node) {
126
157
  };
127
158
  var isBlockMenuWithinNode = function isBlockMenuWithinNode(node) {
128
159
  return (0, _experiences.containsPopupWithNestedElement)(node, '[data-testid="editor-block-menu"]');
160
+ };
161
+ var isSelectionWithoutTextContent = function isSelectionWithoutTextContent(selection) {
162
+ if (!selection || selection.empty) {
163
+ return true;
164
+ }
165
+ var hasText = false;
166
+ selection.$from.doc.nodesBetween(selection.from, selection.to, function (node) {
167
+ if (hasText) {
168
+ return false;
169
+ }
170
+ if (node.isText && node.text && node.text.length > 0) {
171
+ hasText = true;
172
+ return false;
173
+ }
174
+ return true;
175
+ });
176
+ return !hasText;
177
+ };
178
+ var isSelectionWithinCodeBlock = function isSelectionWithinCodeBlock(selection) {
179
+ var $from = selection.$from,
180
+ $to = selection.$to;
181
+ return $from.sameParent($to) && $from.parent.type.name === 'codeBlock';
129
182
  };
@@ -15,32 +15,41 @@ const ABORT_REASON = {
15
15
  * This experience tracks when the selection toolbar is opened.
16
16
  *
17
17
  * Start: When user makes a selection via mouseup or shift+arrow key down
18
- * Success: When the selection toolbar is added to the DOM within 500ms of start
19
- * Failure: When 500ms passes without the selection toolbar being added to the DOM
20
- * Abort: When selection transition to empty or block menu is opened
18
+ * Success: When the selection toolbar is added to the DOM within 1000ms of start
19
+ * Failure: When 1000ms passes without the selection toolbar being added to the DOM
20
+ * Abort: When selection transitions to empty or block menu is opened
21
21
  */
22
22
  export const getSelectionToolbarOpenExperiencePlugin = ({
23
23
  refs,
24
24
  dispatchAnalyticsEvent
25
25
  }) => {
26
+ let editorView;
26
27
  let targetEl;
27
- let editorViewEl;
28
+ let shiftArrowKeyPressed = false;
29
+ let mouseDownPos;
28
30
  const getTarget = () => {
29
31
  if (!targetEl) {
30
- targetEl = refs.popupsMountPoint || getPopupContainerFromEditorView(editorViewEl);
32
+ var _editorView;
33
+ targetEl = refs.popupsMountPoint || getPopupContainerFromEditorView((_editorView = editorView) === null || _editorView === void 0 ? void 0 : _editorView.dom);
31
34
  }
32
35
  return targetEl;
33
36
  };
34
37
  const experience = new Experience('selection-toolbar-open', {
35
38
  dispatchAnalyticsEvent,
36
39
  checks: [new ExperienceCheckTimeout({
37
- durationMs: 500,
40
+ durationMs: 1000,
38
41
  onTimeout: () => {
42
+ var _editorView2;
39
43
  if (isBlockMenuWithinNode(getTarget())) {
40
44
  return {
41
45
  status: 'abort',
42
46
  reason: ABORT_REASON.BLOCK_MENU_OPENED
43
47
  };
48
+ } else if (isSelectionWithoutTextContent((_editorView2 = editorView) === null || _editorView2 === void 0 ? void 0 : _editorView2.state.selection)) {
49
+ return {
50
+ status: 'abort',
51
+ reason: ABORT_REASON.SELECTION_CLEARED
52
+ };
44
53
  }
45
54
  }
46
55
  }), new ExperienceCheckDomMutation({
@@ -66,37 +75,59 @@ export const getSelectionToolbarOpenExperiencePlugin = ({
66
75
  state: {
67
76
  init: () => ({}),
68
77
  apply: (_tr, pluginState, oldState, newState) => {
69
- if (!oldState.selection.empty && newState.selection.empty) {
78
+ if (!oldState.selection.empty && isSelectionWithoutTextContent(newState.selection)) {
70
79
  experience.abort({
71
80
  reason: ABORT_REASON.SELECTION_CLEARED
72
81
  });
73
82
  }
83
+ if (shiftArrowKeyPressed && !newState.selection.eq(oldState.selection) && !isSelectionWithoutTextContent(newState.selection)) {
84
+ experience.start({
85
+ method: START_METHOD.KEY_DOWN
86
+ });
87
+ shiftArrowKeyPressed = false;
88
+ }
74
89
  return pluginState;
75
90
  }
76
91
  },
77
92
  props: {
78
93
  handleDOMEvents: {
79
- mouseup: view => {
80
- if (!view.state.selection.empty && !isSelectionToolbarWithinNode(getTarget())) {
94
+ mousedown: (_view, e) => {
95
+ mouseDownPos = {
96
+ x: e.clientX,
97
+ y: e.clientY
98
+ };
99
+ },
100
+ mouseup: (view, e) => {
101
+ if (!mouseDownPos || isSelectionWithoutTextContent(view.state.selection) || isSelectionWithinCodeBlock(view.state.selection) || isSelectionToolbarWithinNode(getTarget())) {
102
+ return;
103
+ }
104
+ if (e.clientX !== mouseDownPos.x || e.clientY !== mouseDownPos.y) {
81
105
  experience.start({
82
106
  method: START_METHOD.MOUSE_UP
83
107
  });
84
108
  }
85
109
  },
110
+ dblclick: (view, e) => {
111
+ if (isSelectionWithoutTextContent(view.state.selection) || isSelectionWithinCodeBlock(view.state.selection) || isSelectionToolbarWithinNode(getTarget())) {
112
+ return;
113
+ }
114
+ experience.start({
115
+ method: START_METHOD.MOUSE_UP
116
+ });
117
+ },
86
118
  keydown: (_view, {
87
119
  shiftKey,
88
120
  key
89
121
  }) => {
90
- if (shiftKey && key.includes('Arrow') && !isSelectionToolbarWithinNode(getTarget())) {
91
- experience.start({
92
- method: START_METHOD.KEY_DOWN
93
- });
94
- }
122
+ shiftArrowKeyPressed = shiftKey && key.includes('Arrow') && !isSelectionToolbarWithinNode(getTarget());
123
+ },
124
+ keyup: () => {
125
+ shiftArrowKeyPressed = false;
95
126
  }
96
127
  }
97
128
  },
98
129
  view: view => {
99
- editorViewEl = view.dom;
130
+ editorView = view;
100
131
  return {
101
132
  destroy: () => {
102
133
  experience.abort({
@@ -118,4 +149,28 @@ const isSelectionToolbarWithinNode = node => {
118
149
  };
119
150
  const isBlockMenuWithinNode = node => {
120
151
  return containsPopupWithNestedElement(node, '[data-testid="editor-block-menu"]');
152
+ };
153
+ const isSelectionWithoutTextContent = selection => {
154
+ if (!selection || selection.empty) {
155
+ return true;
156
+ }
157
+ let hasText = false;
158
+ selection.$from.doc.nodesBetween(selection.from, selection.to, node => {
159
+ if (hasText) {
160
+ return false;
161
+ }
162
+ if (node.isText && node.text && node.text.length > 0) {
163
+ hasText = true;
164
+ return false;
165
+ }
166
+ return true;
167
+ });
168
+ return !hasText;
169
+ };
170
+ const isSelectionWithinCodeBlock = selection => {
171
+ const {
172
+ $from,
173
+ $to
174
+ } = selection;
175
+ return $from.sameParent($to) && $from.parent.type.name === 'codeBlock';
121
176
  };
@@ -16,31 +16,40 @@ var ABORT_REASON = {
16
16
  * This experience tracks when the selection toolbar is opened.
17
17
  *
18
18
  * Start: When user makes a selection via mouseup or shift+arrow key down
19
- * Success: When the selection toolbar is added to the DOM within 500ms of start
20
- * Failure: When 500ms passes without the selection toolbar being added to the DOM
21
- * Abort: When selection transition to empty or block menu is opened
19
+ * Success: When the selection toolbar is added to the DOM within 1000ms of start
20
+ * Failure: When 1000ms passes without the selection toolbar being added to the DOM
21
+ * Abort: When selection transitions to empty or block menu is opened
22
22
  */
23
23
  export var getSelectionToolbarOpenExperiencePlugin = function getSelectionToolbarOpenExperiencePlugin(_ref) {
24
24
  var refs = _ref.refs,
25
25
  dispatchAnalyticsEvent = _ref.dispatchAnalyticsEvent;
26
+ var editorView;
26
27
  var targetEl;
27
- var editorViewEl;
28
+ var shiftArrowKeyPressed = false;
29
+ var mouseDownPos;
28
30
  var getTarget = function getTarget() {
29
31
  if (!targetEl) {
30
- targetEl = refs.popupsMountPoint || getPopupContainerFromEditorView(editorViewEl);
32
+ var _editorView;
33
+ targetEl = refs.popupsMountPoint || getPopupContainerFromEditorView((_editorView = editorView) === null || _editorView === void 0 ? void 0 : _editorView.dom);
31
34
  }
32
35
  return targetEl;
33
36
  };
34
37
  var experience = new Experience('selection-toolbar-open', {
35
38
  dispatchAnalyticsEvent: dispatchAnalyticsEvent,
36
39
  checks: [new ExperienceCheckTimeout({
37
- durationMs: 500,
40
+ durationMs: 1000,
38
41
  onTimeout: function onTimeout() {
42
+ var _editorView2;
39
43
  if (isBlockMenuWithinNode(getTarget())) {
40
44
  return {
41
45
  status: 'abort',
42
46
  reason: ABORT_REASON.BLOCK_MENU_OPENED
43
47
  };
48
+ } else if (isSelectionWithoutTextContent((_editorView2 = editorView) === null || _editorView2 === void 0 ? void 0 : _editorView2.state.selection)) {
49
+ return {
50
+ status: 'abort',
51
+ reason: ABORT_REASON.SELECTION_CLEARED
52
+ };
44
53
  }
45
54
  }
46
55
  }), new ExperienceCheckDomMutation({
@@ -69,36 +78,58 @@ export var getSelectionToolbarOpenExperiencePlugin = function getSelectionToolba
69
78
  return {};
70
79
  },
71
80
  apply: function apply(_tr, pluginState, oldState, newState) {
72
- if (!oldState.selection.empty && newState.selection.empty) {
81
+ if (!oldState.selection.empty && isSelectionWithoutTextContent(newState.selection)) {
73
82
  experience.abort({
74
83
  reason: ABORT_REASON.SELECTION_CLEARED
75
84
  });
76
85
  }
86
+ if (shiftArrowKeyPressed && !newState.selection.eq(oldState.selection) && !isSelectionWithoutTextContent(newState.selection)) {
87
+ experience.start({
88
+ method: START_METHOD.KEY_DOWN
89
+ });
90
+ shiftArrowKeyPressed = false;
91
+ }
77
92
  return pluginState;
78
93
  }
79
94
  },
80
95
  props: {
81
96
  handleDOMEvents: {
82
- mouseup: function mouseup(view) {
83
- if (!view.state.selection.empty && !isSelectionToolbarWithinNode(getTarget())) {
97
+ mousedown: function mousedown(_view, e) {
98
+ mouseDownPos = {
99
+ x: e.clientX,
100
+ y: e.clientY
101
+ };
102
+ },
103
+ mouseup: function mouseup(view, e) {
104
+ if (!mouseDownPos || isSelectionWithoutTextContent(view.state.selection) || isSelectionWithinCodeBlock(view.state.selection) || isSelectionToolbarWithinNode(getTarget())) {
105
+ return;
106
+ }
107
+ if (e.clientX !== mouseDownPos.x || e.clientY !== mouseDownPos.y) {
84
108
  experience.start({
85
109
  method: START_METHOD.MOUSE_UP
86
110
  });
87
111
  }
88
112
  },
113
+ dblclick: function dblclick(view, e) {
114
+ if (isSelectionWithoutTextContent(view.state.selection) || isSelectionWithinCodeBlock(view.state.selection) || isSelectionToolbarWithinNode(getTarget())) {
115
+ return;
116
+ }
117
+ experience.start({
118
+ method: START_METHOD.MOUSE_UP
119
+ });
120
+ },
89
121
  keydown: function keydown(_view, _ref3) {
90
122
  var shiftKey = _ref3.shiftKey,
91
123
  key = _ref3.key;
92
- if (shiftKey && key.includes('Arrow') && !isSelectionToolbarWithinNode(getTarget())) {
93
- experience.start({
94
- method: START_METHOD.KEY_DOWN
95
- });
96
- }
124
+ shiftArrowKeyPressed = shiftKey && key.includes('Arrow') && !isSelectionToolbarWithinNode(getTarget());
125
+ },
126
+ keyup: function keyup() {
127
+ shiftArrowKeyPressed = false;
97
128
  }
98
129
  }
99
130
  },
100
131
  view: function view(_view2) {
101
- editorViewEl = _view2.dom;
132
+ editorView = _view2;
102
133
  return {
103
134
  destroy: function destroy() {
104
135
  experience.abort({
@@ -119,4 +150,26 @@ var isSelectionToolbarWithinNode = function isSelectionToolbarWithinNode(node) {
119
150
  };
120
151
  var isBlockMenuWithinNode = function isBlockMenuWithinNode(node) {
121
152
  return containsPopupWithNestedElement(node, '[data-testid="editor-block-menu"]');
153
+ };
154
+ var isSelectionWithoutTextContent = function isSelectionWithoutTextContent(selection) {
155
+ if (!selection || selection.empty) {
156
+ return true;
157
+ }
158
+ var hasText = false;
159
+ selection.$from.doc.nodesBetween(selection.from, selection.to, function (node) {
160
+ if (hasText) {
161
+ return false;
162
+ }
163
+ if (node.isText && node.text && node.text.length > 0) {
164
+ hasText = true;
165
+ return false;
166
+ }
167
+ return true;
168
+ });
169
+ return !hasText;
170
+ };
171
+ var isSelectionWithinCodeBlock = function isSelectionWithinCodeBlock(selection) {
172
+ var $from = selection.$from,
173
+ $to = selection.$to;
174
+ return $from.sameParent($to) && $from.parent.type.name === 'codeBlock';
122
175
  };
@@ -10,9 +10,9 @@ type SelectionToolbarOpenExperienceOptions = {
10
10
  * This experience tracks when the selection toolbar is opened.
11
11
  *
12
12
  * Start: When user makes a selection via mouseup or shift+arrow key down
13
- * Success: When the selection toolbar is added to the DOM within 500ms of start
14
- * Failure: When 500ms passes without the selection toolbar being added to the DOM
15
- * Abort: When selection transition to empty or block menu is opened
13
+ * Success: When the selection toolbar is added to the DOM within 1000ms of start
14
+ * Failure: When 1000ms passes without the selection toolbar being added to the DOM
15
+ * Abort: When selection transitions to empty or block menu is opened
16
16
  */
17
17
  export declare const getSelectionToolbarOpenExperiencePlugin: ({ refs, dispatchAnalyticsEvent, }: SelectionToolbarOpenExperienceOptions) => SafePlugin<{}>;
18
18
  export {};
@@ -10,9 +10,9 @@ type SelectionToolbarOpenExperienceOptions = {
10
10
  * This experience tracks when the selection toolbar is opened.
11
11
  *
12
12
  * Start: When user makes a selection via mouseup or shift+arrow key down
13
- * Success: When the selection toolbar is added to the DOM within 500ms of start
14
- * Failure: When 500ms passes without the selection toolbar being added to the DOM
15
- * Abort: When selection transition to empty or block menu is opened
13
+ * Success: When the selection toolbar is added to the DOM within 1000ms of start
14
+ * Failure: When 1000ms passes without the selection toolbar being added to the DOM
15
+ * Abort: When selection transitions to empty or block menu is opened
16
16
  */
17
17
  export declare const getSelectionToolbarOpenExperiencePlugin: ({ refs, dispatchAnalyticsEvent, }: SelectionToolbarOpenExperienceOptions) => SafePlugin<{}>;
18
18
  export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/editor-plugin-toolbar",
3
- "version": "3.4.9",
3
+ "version": "3.4.10",
4
4
  "description": "Toolbar plugin for @atlaskit/editor-core",
5
5
  "author": "Atlassian Pty Ltd",
6
6
  "license": "Apache-2.0",
@@ -40,13 +40,13 @@
40
40
  "@atlaskit/editor-toolbar-model": "^0.2.0",
41
41
  "@atlaskit/platform-feature-flags": "^1.1.0",
42
42
  "@atlaskit/platform-feature-flags-react": "^0.4.0",
43
- "@atlaskit/tmp-editor-statsig": "^13.38.0",
43
+ "@atlaskit/tmp-editor-statsig": "^13.42.0",
44
44
  "@babel/runtime": "^7.0.0",
45
45
  "bind-event-listener": "^3.0.0",
46
46
  "react-intl-next": "npm:react-intl@^5.18.1"
47
47
  },
48
48
  "peerDependencies": {
49
- "@atlaskit/editor-common": "^110.32.0",
49
+ "@atlaskit/editor-common": "^110.33.0",
50
50
  "react": "^18.2.0"
51
51
  },
52
52
  "platform-feature-flags": {