@atlaskit/editor-plugin-type-ahead 2.1.0 → 2.1.2

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.
@@ -2,6 +2,8 @@
2
2
  * @jsxRuntime classic
3
3
  * @jsx jsx
4
4
  */
5
+ import { useEffect, useRef } from 'react';
6
+
5
7
  // eslint-disable-next-line @atlaskit/ui-styling-standard/use-compiled -- Ignored via go/DSP-18766
6
8
  import { css, jsx } from '@emotion/react';
7
9
  import { useIntl } from 'react-intl-next';
@@ -13,19 +15,61 @@ const buttonStyles = css({
13
15
  // eslint-disable-next-line @atlaskit/ui-styling-standard/no-nested-selectors -- Ignored via go/DSP-18766
14
16
  '& > button:hover': {
15
17
  backgroundColor: `var(--ds-background-neutral-subtle-hovered, ${N30})`
18
+ },
19
+ // eslint-disable-next-line @atlaskit/ui-styling-standard/no-nested-selectors -- Ignored via go/DSP-18766
20
+ '& > button:focus': {
21
+ backgroundColor: `var(--ds-background-neutral-subtle-hovered, ${N30})`,
22
+ outline: 'none'
16
23
  }
17
24
  });
18
25
  export const ViewMore = ({
19
- onClick
26
+ onClick,
27
+ isFocused
20
28
  }) => {
21
29
  const {
22
30
  formatMessage
23
31
  } = useIntl();
32
+ const ref = useRef(null);
33
+ useEffect(() => {
34
+ if (isFocused && ref.current) {
35
+ ref.current.focus();
36
+ }
37
+ }, [isFocused]);
38
+ useEffect(() => {
39
+ if (!ref.current) {
40
+ return;
41
+ }
42
+ const {
43
+ current: element
44
+ } = ref;
45
+ const handleEnter = e => {
46
+ if (e.key === 'Enter') {
47
+ onClick();
48
+ // Prevent keydown listener in TypeaheadList from handling Enter pressed
49
+ e.stopPropagation();
50
+ } else if (e.key === 'Tab') {
51
+ // TypeaheadList will try to insert selected item on Tab press
52
+ // hence stop propagation to prevent that and treat this as noop
53
+ e.stopPropagation();
54
+ e.preventDefault();
55
+ }
56
+ };
57
+
58
+ // Ignored via go/ees005
59
+ // eslint-disable-next-line @repo/internal/dom-events/no-unsafe-event-listeners
60
+ element === null || element === void 0 ? void 0 : element.addEventListener('keydown', handleEnter);
61
+ return () => {
62
+ // Ignored via go/ees005
63
+ // eslint-disable-next-line @repo/internal/dom-events/no-unsafe-event-listeners
64
+ element === null || element === void 0 ? void 0 : element.removeEventListener('keydown', handleEnter);
65
+ };
66
+ });
24
67
  return jsx(Section, {
25
68
  hasSeparator: true
26
69
  }, jsx("span", {
27
70
  css: buttonStyles
28
71
  }, jsx(ButtonItem, {
72
+ ref: ref,
29
73
  onClick: onClick,
30
74
  iconBefore: jsx(ShowMoreHorizontalIcon, {
31
75
  label: ""
@@ -1,5 +1,6 @@
1
1
  import React, { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react';
2
- import { SelectItemMode } from '@atlaskit/editor-common/type-ahead';
2
+ import { SelectItemMode, TypeAheadAvailableNodes } from '@atlaskit/editor-common/type-ahead';
3
+ import { editorExperiment } from '@atlaskit/tmp-editor-statsig/experiments';
3
4
  import { updateQuery } from '../pm-plugins/commands/update-query';
4
5
  import { itemIsDisabled } from '../pm-plugins/item-is-disabled';
5
6
  import { getPluginState, moveSelectedIndex, skipForwardToSafeItem } from '../pm-plugins/utils';
@@ -21,11 +22,14 @@ export const WrapperTypeAhead = /*#__PURE__*/React.memo(({
21
22
  onUndoRedo,
22
23
  api
23
24
  }) => {
25
+ // @ts-ignore
26
+ const openElementBrowserModal = triggerHandler === null || triggerHandler === void 0 ? void 0 : triggerHandler.openElementBrowserModal;
27
+ const showViewMore = (triggerHandler === null || triggerHandler === void 0 ? void 0 : triggerHandler.id) === TypeAheadAvailableNodes.QUICK_INSERT && !!openElementBrowserModal && editorExperiment('platform_editor_controls', 'variant1');
24
28
  const [closed, setClosed] = useState(false);
25
29
  const [query, setQuery] = useState(reopenQuery || '');
26
30
  const queryRef = useRef(query);
27
31
  const editorViewRef = useRef(editorView);
28
- const items = useLoadItems(triggerHandler, editorView, query);
32
+ const items = useLoadItems(triggerHandler, editorView, query, showViewMore);
29
33
  useLayoutEffect(() => {
30
34
  queryRef.current = query;
31
35
  }, [query]);
@@ -3,7 +3,7 @@ import { fg } from '@atlaskit/platform-feature-flags';
3
3
  import { updateListError } from '../../pm-plugins/commands/update-list-error';
4
4
  import { updateListItem } from '../../pm-plugins/commands/update-list-items';
5
5
  const EMPTY_LIST_ITEM = [];
6
- export const useLoadItems = (triggerHandler, editorView, query) => {
6
+ export const useLoadItems = (triggerHandler, editorView, query, showViewMore) => {
7
7
  const [items, setItems] = useState(EMPTY_LIST_ITEM);
8
8
  const componentIsMounted = useRef(true);
9
9
  const editorViewRef = useRef(editorView);
@@ -25,8 +25,11 @@ export const useLoadItems = (triggerHandler, editorView, query) => {
25
25
  if (componentIsMounted.current) {
26
26
  setItems(list);
27
27
  }
28
+ const viewMoreItem = {
29
+ title: 'View more'
30
+ };
28
31
  queueMicrotask(() => {
29
- updateListItem(list)(view.state, view.dispatch);
32
+ updateListItem(showViewMore ? list.concat(viewMoreItem) : list)(view.state, view.dispatch);
30
33
  });
31
34
  }).catch(e => {
32
35
  if (fg('platform_editor_offline_editing_ga')) {
@@ -199,22 +199,31 @@ export const TypeAheadPopup = /*#__PURE__*/React.memo(props => {
199
199
  var _window$getSelection2;
200
200
  // Check if new focus point is inside the current editor. If it is not we
201
201
  // want to close the typeahead popup regardless of text selection state
202
- const focusNode = (_window$getSelection2 = window.getSelection()) === null || _window$getSelection2 === void 0 ? void 0 : _window$getSelection2.focusNode;
202
+ const currentFocus = (_window$getSelection2 = window.getSelection()) === null || _window$getSelection2 === void 0 ? void 0 : _window$getSelection2.focusNode; // the focusNode is either TextNode, ElementNode
203
+ // if currentFocus is not HTMLElement, take its parent node as focusNode
204
+ const focusNode = currentFocus instanceof HTMLElement ? currentFocus : currentFocus === null || currentFocus === void 0 ? void 0 : currentFocus.parentNode;
203
205
  if (focusNode instanceof HTMLElement) {
204
206
  const innerEditor = focusNode.closest('.extension-editable-area');
205
- // When there is no related target, we default to not closing the popup
206
- let newFocusInsideCurrentEditor = !relatedTarget;
207
- if (relatedTarget instanceof HTMLElement) {
208
- if (innerEditor) {
209
- // check if the new focus is inside inner editor, keep popup opens
210
- newFocusInsideCurrentEditor = innerEditor.contains(relatedTarget);
211
- } else {
212
- // if the new focus contains current focus node, the popup won't close
213
- newFocusInsideCurrentEditor = relatedTarget.contains(focusNode);
207
+ if (innerEditor) {
208
+ // When there is no related target, we default to not closing the popup
209
+ let newFocusInsideCurrentEditor = !relatedTarget;
210
+ if (relatedTarget instanceof HTMLElement) {
211
+ if (innerEditor) {
212
+ // check if the new focus is inside inner editor, keep popup opens
213
+ newFocusInsideCurrentEditor = innerEditor.contains(relatedTarget);
214
+ } else {
215
+ // if the new focus contains current focus node, the popup won't close
216
+ newFocusInsideCurrentEditor = relatedTarget.contains(focusNode);
217
+ }
218
+ }
219
+ if (!isTextSelected && newFocusInsideCurrentEditor) {
220
+ return;
221
+ }
222
+ } else {
223
+ // if the current focus in outer editor, keep the existing behaviour, do not close the pop up if text is not selected
224
+ if (!isTextSelected) {
225
+ return;
214
226
  }
215
- }
216
- if (!isTextSelected && newFocusInsideCurrentEditor) {
217
- return;
218
227
  }
219
228
  }
220
229
  } else {
@@ -20,6 +20,7 @@ import { TYPE_AHEAD_DECORATION_ELEMENT_ID } from '../pm-plugins/constants';
20
20
  import { getTypeAheadListAriaLabels, moveSelectedIndex } from '../pm-plugins/utils';
21
21
  import { AssistiveText } from './AssistiveText';
22
22
  import { TypeAheadListItem } from './TypeAheadListItem';
23
+ import { ViewMore } from './ViewMore';
23
24
  var LIST_ITEM_ESTIMATED_HEIGHT = 64;
24
25
  var LIST_WIDTH = 320;
25
26
  var list = css({
@@ -53,7 +54,8 @@ var TypeAheadListComponent = /*#__PURE__*/React.memo(function (_ref2) {
53
54
  triggerHandler = _ref2.triggerHandler,
54
55
  moreElementsInQuickInsertViewEnabled = _ref2.moreElementsInQuickInsertViewEnabled,
55
56
  api = _ref2.api,
56
- showViewMore = _ref2.showViewMore;
57
+ showViewMore = _ref2.showViewMore,
58
+ onViewMoreClick = _ref2.onViewMoreClick;
57
59
  var listRef = useRef();
58
60
  var listContainerRef = useRef(null);
59
61
  var lastVisibleIndexes = useRef({
@@ -62,7 +64,10 @@ var TypeAheadListComponent = /*#__PURE__*/React.memo(function (_ref2) {
62
64
  startIndex: 0,
63
65
  stopIndex: 0
64
66
  });
65
- var estimatedHeight = items.length * LIST_ITEM_ESTIMATED_HEIGHT;
67
+
68
+ // Exclude view more item from the count
69
+ var itemsLength = showViewMore ? items.length - 1 : items.length;
70
+ var estimatedHeight = itemsLength * LIST_ITEM_ESTIMATED_HEIGHT;
66
71
  var _useState = useState(Math.min(estimatedHeight, fitHeight)),
67
72
  _useState2 = _slicedToArray(_useState, 2),
68
73
  height = _useState2[0],
@@ -128,7 +133,10 @@ var TypeAheadListComponent = /*#__PURE__*/React.memo(function (_ref2) {
128
133
  // to calculate each height. THen, we can schedule a new frame when this one finishs.
129
134
  requestAnimationFrame(function () {
130
135
  requestAnimationFrame(function () {
131
- var isSelectedItemVisible = selectedIndex >= lastVisibleStartIndex && selectedIndex <= lastVisibleStopIndex;
136
+ var isViewMoreSelected = showViewMore && selectedIndex === itemsLength;
137
+ var isSelectedItemVisible = selectedIndex >= lastVisibleStartIndex && selectedIndex <= lastVisibleStopIndex ||
138
+ // view more is always visible, hence no scrolling
139
+ isViewMoreSelected;
132
140
 
133
141
  //Should scroll to the list item only when the selectedIndex >= 0 and item is not visible
134
142
  if (!isSelectedItemVisible && selectedIndex !== -1) {
@@ -138,7 +146,7 @@ var TypeAheadListComponent = /*#__PURE__*/React.memo(function (_ref2) {
138
146
  }
139
147
  });
140
148
  });
141
- }, [selectedIndex, lastVisibleStartIndex, lastVisibleStopIndex]);
149
+ }, [selectedIndex, lastVisibleStartIndex, lastVisibleStopIndex, itemsLength, showViewMore]);
142
150
  var _onMouseMove = function onMouseMove(event, index) {
143
151
  event.preventDefault();
144
152
  event.stopPropagation();
@@ -151,7 +159,10 @@ var TypeAheadListComponent = /*#__PURE__*/React.memo(function (_ref2) {
151
159
  if (!listRef.current) {
152
160
  return;
153
161
  }
154
- var isSelectedItemVisible = selectedIndex >= lastVisibleStartIndex && selectedIndex <= lastVisibleStopIndex;
162
+ var isViewMoreSelected = showViewMore && selectedIndex === itemsLength;
163
+ var isSelectedItemVisible = selectedIndex >= lastVisibleStartIndex && selectedIndex <= lastVisibleStopIndex ||
164
+ // view more is always visible, hence no scrolling
165
+ isViewMoreSelected;
155
166
 
156
167
  //Should scroll to the list item only when the selectedIndex >= 0 and item is not visible
157
168
  if (!isSelectedItemVisible && selectedIndex !== -1) {
@@ -159,7 +170,7 @@ var TypeAheadListComponent = /*#__PURE__*/React.memo(function (_ref2) {
159
170
  } else if (selectedIndex === -1) {
160
171
  listRef.current.scrollToRow(0);
161
172
  }
162
- }, [selectedIndex, lastVisibleStartIndex, lastVisibleStopIndex]);
173
+ }, [selectedIndex, lastVisibleStartIndex, lastVisibleStopIndex, itemsLength, showViewMore]);
163
174
  useLayoutEffect(function () {
164
175
  setCache(new CellMeasurerCache({
165
176
  fixedWidth: true,
@@ -177,13 +188,15 @@ var TypeAheadListComponent = /*#__PURE__*/React.memo(function (_ref2) {
177
188
  });
178
189
  }, [items]);
179
190
  useLayoutEffect(function () {
180
- var height = Math.min(items.reduce(function (prevValue, currentValue, index) {
191
+ // Exclude view more item from the count
192
+ var itemsToRender = showViewMore ? items.slice(0, -1) : items;
193
+ var height = Math.min(itemsToRender.reduce(function (prevValue, currentValue, index) {
181
194
  return prevValue + cache.rowHeight({
182
195
  index: index
183
196
  });
184
197
  }, 0), fitHeight);
185
198
  setHeight(height);
186
- }, [items, cache, fitHeight]);
199
+ }, [items, cache, fitHeight, showViewMore]);
187
200
  useLayoutEffect(function () {
188
201
  if (!listContainerRef.current) {
189
202
  return;
@@ -236,7 +249,7 @@ var TypeAheadListComponent = /*#__PURE__*/React.memo(function (_ref2) {
236
249
  // eslint-disable-next-line @repo/internal/dom-events/no-unsafe-event-listeners
237
250
  element === null || element === void 0 || element.removeEventListener('keydown', handleKeyDown);
238
251
  };
239
- }, [editorView.state, focusTargetElement, selectNextItem, selectPreviousItem, selectedIndex, onItemClick, items.length]);
252
+ }, [editorView.state, focusTargetElement, selectNextItem, selectPreviousItem, selectedIndex, onItemClick, itemsLength]);
240
253
  var firstOnlineSupportedRow = useMemo(function () {
241
254
  return items.findIndex(function (item) {
242
255
  return item.isDisabledOffline !== true;
@@ -266,7 +279,7 @@ var TypeAheadListComponent = /*#__PURE__*/React.memo(function (_ref2) {
266
279
  key: items[index].title,
267
280
  item: currentItem,
268
281
  firstOnlineSupportedIndex: firstOnlineSupportedRow,
269
- itemsLength: items.length,
282
+ itemsLength: itemsLength,
270
283
  itemIndex: index,
271
284
  selectedIndex: selectedIndex,
272
285
  onItemClick: actions.onItemClick,
@@ -296,8 +309,10 @@ var TypeAheadListComponent = /*#__PURE__*/React.memo(function (_ref2) {
296
309
  })));
297
310
  var ListContent = jsx(List, {
298
311
  rowRenderer: renderRow,
299
- ref: listRef,
300
- rowCount: items.length,
312
+ ref: listRef
313
+ // Skip rendering the view more button in the list
314
+ ,
315
+ rowCount: itemsLength,
301
316
  rowHeight: cache.rowHeight,
302
317
  onRowsRendered: onItemsRendered,
303
318
  width: LIST_WIDTH,
@@ -331,8 +346,11 @@ var TypeAheadListComponent = /*#__PURE__*/React.memo(function (_ref2) {
331
346
  }, jsx("div", {
332
347
  id: menuGroupId,
333
348
  ref: listContainerRef
334
- }, !showViewMore || items.length ? ListContent : EmptyResultView, jsx(TypeaheadAssistiveTextPureComponent, {
335
- numberOfResults: items.length.toString()
349
+ }, !showViewMore || itemsLength ? ListContent : EmptyResultView, showViewMore && onViewMoreClick && jsx(ViewMore, {
350
+ onClick: onViewMoreClick,
351
+ isFocused: selectedIndex === itemsLength
352
+ }), jsx(TypeaheadAssistiveTextPureComponent, {
353
+ numberOfResults: itemsLength.toString()
336
354
  })));
337
355
  });
338
356
  export var TypeAheadList = injectIntl(TypeAheadListComponent);
@@ -62,7 +62,7 @@ var itemTitle = css({
62
62
  lineHeight: '1.4'
63
63
  });
64
64
  var itemTitleOverride = css({
65
- font: "var(--ds-font-body, normal 400 14px/20px ui-sans-serif, -apple-system, BlinkMacSystemFont, \"Segoe UI\", Ubuntu, system-ui, \"Helvetica Neue\", sans-serif)"
65
+ font: "var(--ds-font-body, normal 400 14px/20px ui-sans-serif, -apple-system, BlinkMacSystemFont, \"Segoe UI\", Ubuntu, \"Helvetica Neue\", sans-serif)"
66
66
  });
67
67
  var itemDescription = css({
68
68
  // eslint-disable-next-line @atlaskit/ui-styling-standard/no-imported-style-values, @atlaskit/ui-styling-standard/no-unsafe-values, @atlaskit/design-system/use-tokens-typography
@@ -71,7 +71,7 @@ var itemDescription = css({
71
71
  marginTop: "var(--ds-space-050, 4px)".concat(";")
72
72
  });
73
73
  var itemDescriptionOverride = css({
74
- font: "var(--ds-font-body-small, normal 400 11px/16px ui-sans-serif, -apple-system, BlinkMacSystemFont, \"Segoe UI\", Ubuntu, system-ui, \"Helvetica Neue\", sans-serif)",
74
+ font: "var(--ds-font-body-small, normal 400 11px/16px ui-sans-serif, -apple-system, BlinkMacSystemFont, \"Segoe UI\", Ubuntu, \"Helvetica Neue\", sans-serif)",
75
75
  marginTop: 0
76
76
  });
77
77
 
@@ -18,7 +18,6 @@ import { N0, N50A, N60A } from '@atlaskit/theme/colors';
18
18
  import { CloseSelectionOptions, TYPE_AHEAD_DECORATION_DATA_ATTRIBUTE, TYPE_AHEAD_POPUP_CONTENT_CLASS } from '../pm-plugins/constants';
19
19
  import { TypeAheadErrorFallback } from './TypeAheadErrorFallback';
20
20
  import { TypeAheadList } from './TypeAheadList';
21
- import { ViewMore } from './ViewMore';
22
21
  var DEFAULT_TYPEAHEAD_MENU_HEIGHT = 380;
23
22
  var VIEWMORE_BUTTON_HEIGHT = 53;
24
23
  var DEFAULT_TYPEAHEAD_MENU_HEIGHT_NEW = 480;
@@ -209,22 +208,31 @@ export var TypeAheadPopup = /*#__PURE__*/React.memo(function (props) {
209
208
  var _window$getSelection2;
210
209
  // Check if new focus point is inside the current editor. If it is not we
211
210
  // want to close the typeahead popup regardless of text selection state
212
- var focusNode = (_window$getSelection2 = window.getSelection()) === null || _window$getSelection2 === void 0 ? void 0 : _window$getSelection2.focusNode;
211
+ var currentFocus = (_window$getSelection2 = window.getSelection()) === null || _window$getSelection2 === void 0 ? void 0 : _window$getSelection2.focusNode; // the focusNode is either TextNode, ElementNode
212
+ // if currentFocus is not HTMLElement, take its parent node as focusNode
213
+ var focusNode = currentFocus instanceof HTMLElement ? currentFocus : currentFocus === null || currentFocus === void 0 ? void 0 : currentFocus.parentNode;
213
214
  if (focusNode instanceof HTMLElement) {
214
215
  var innerEditor = focusNode.closest('.extension-editable-area');
215
- // When there is no related target, we default to not closing the popup
216
- var newFocusInsideCurrentEditor = !relatedTarget;
217
- if (relatedTarget instanceof HTMLElement) {
218
- if (innerEditor) {
219
- // check if the new focus is inside inner editor, keep popup opens
220
- newFocusInsideCurrentEditor = innerEditor.contains(relatedTarget);
221
- } else {
222
- // if the new focus contains current focus node, the popup won't close
223
- newFocusInsideCurrentEditor = relatedTarget.contains(focusNode);
216
+ if (innerEditor) {
217
+ // When there is no related target, we default to not closing the popup
218
+ var newFocusInsideCurrentEditor = !relatedTarget;
219
+ if (relatedTarget instanceof HTMLElement) {
220
+ if (innerEditor) {
221
+ // check if the new focus is inside inner editor, keep popup opens
222
+ newFocusInsideCurrentEditor = innerEditor.contains(relatedTarget);
223
+ } else {
224
+ // if the new focus contains current focus node, the popup won't close
225
+ newFocusInsideCurrentEditor = relatedTarget.contains(focusNode);
226
+ }
227
+ }
228
+ if (!isTextSelected && newFocusInsideCurrentEditor) {
229
+ return;
230
+ }
231
+ } else {
232
+ // if the current focus in outer editor, keep the existing behaviour, do not close the pop up if text is not selected
233
+ if (!isTextSelected) {
234
+ return;
224
235
  }
225
- }
226
- if (!isTextSelected && newFocusInsideCurrentEditor) {
227
- return;
228
236
  }
229
237
  }
230
238
  } else {
@@ -317,9 +325,8 @@ export var TypeAheadPopup = /*#__PURE__*/React.memo(function (props) {
317
325
  triggerHandler: triggerHandler,
318
326
  moreElementsInQuickInsertViewEnabled: moreElementsInQuickInsertViewEnabled,
319
327
  api: api,
320
- showViewMore: showViewMore
321
- }), showViewMore && jsx(ViewMore, {
322
- onClick: onViewMoreClick
328
+ showViewMore: showViewMore,
329
+ onViewMoreClick: onViewMoreClick
323
330
  }))));
324
331
  });
325
332
  TypeAheadPopup.displayName = 'TypeAheadPopup';
@@ -2,6 +2,8 @@
2
2
  * @jsxRuntime classic
3
3
  * @jsx jsx
4
4
  */
5
+ import { useEffect, useRef } from 'react';
6
+
5
7
  // eslint-disable-next-line @atlaskit/ui-styling-standard/use-compiled -- Ignored via go/DSP-18766
6
8
  import { css, jsx } from '@emotion/react';
7
9
  import { useIntl } from 'react-intl-next';
@@ -13,17 +15,57 @@ var buttonStyles = css({
13
15
  // eslint-disable-next-line @atlaskit/ui-styling-standard/no-nested-selectors -- Ignored via go/DSP-18766
14
16
  '& > button:hover': {
15
17
  backgroundColor: "var(--ds-background-neutral-subtle-hovered, ".concat(N30, ")")
18
+ },
19
+ // eslint-disable-next-line @atlaskit/ui-styling-standard/no-nested-selectors -- Ignored via go/DSP-18766
20
+ '& > button:focus': {
21
+ backgroundColor: "var(--ds-background-neutral-subtle-hovered, ".concat(N30, ")"),
22
+ outline: 'none'
16
23
  }
17
24
  });
18
25
  export var ViewMore = function ViewMore(_ref) {
19
- var onClick = _ref.onClick;
26
+ var onClick = _ref.onClick,
27
+ isFocused = _ref.isFocused;
20
28
  var _useIntl = useIntl(),
21
29
  formatMessage = _useIntl.formatMessage;
30
+ var ref = useRef(null);
31
+ useEffect(function () {
32
+ if (isFocused && ref.current) {
33
+ ref.current.focus();
34
+ }
35
+ }, [isFocused]);
36
+ useEffect(function () {
37
+ if (!ref.current) {
38
+ return;
39
+ }
40
+ var element = ref.current;
41
+ var handleEnter = function handleEnter(e) {
42
+ if (e.key === 'Enter') {
43
+ onClick();
44
+ // Prevent keydown listener in TypeaheadList from handling Enter pressed
45
+ e.stopPropagation();
46
+ } else if (e.key === 'Tab') {
47
+ // TypeaheadList will try to insert selected item on Tab press
48
+ // hence stop propagation to prevent that and treat this as noop
49
+ e.stopPropagation();
50
+ e.preventDefault();
51
+ }
52
+ };
53
+
54
+ // Ignored via go/ees005
55
+ // eslint-disable-next-line @repo/internal/dom-events/no-unsafe-event-listeners
56
+ element === null || element === void 0 || element.addEventListener('keydown', handleEnter);
57
+ return function () {
58
+ // Ignored via go/ees005
59
+ // eslint-disable-next-line @repo/internal/dom-events/no-unsafe-event-listeners
60
+ element === null || element === void 0 || element.removeEventListener('keydown', handleEnter);
61
+ };
62
+ });
22
63
  return jsx(Section, {
23
64
  hasSeparator: true
24
65
  }, jsx("span", {
25
66
  css: buttonStyles
26
67
  }, jsx(ButtonItem, {
68
+ ref: ref,
27
69
  onClick: onClick,
28
70
  iconBefore: jsx(ShowMoreHorizontalIcon, {
29
71
  label: ""
@@ -1,6 +1,7 @@
1
1
  import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
2
2
  import React, { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react';
3
- import { SelectItemMode } from '@atlaskit/editor-common/type-ahead';
3
+ import { SelectItemMode, TypeAheadAvailableNodes } from '@atlaskit/editor-common/type-ahead';
4
+ import { editorExperiment } from '@atlaskit/tmp-editor-statsig/experiments';
4
5
  import { updateQuery } from '../pm-plugins/commands/update-query';
5
6
  import { itemIsDisabled as _itemIsDisabled } from '../pm-plugins/item-is-disabled';
6
7
  import { getPluginState, moveSelectedIndex, skipForwardToSafeItem } from '../pm-plugins/utils';
@@ -21,6 +22,9 @@ export var WrapperTypeAhead = /*#__PURE__*/React.memo(function (_ref) {
21
22
  reopenQuery = _ref.reopenQuery,
22
23
  onUndoRedo = _ref.onUndoRedo,
23
24
  api = _ref.api;
25
+ // @ts-ignore
26
+ var openElementBrowserModal = triggerHandler === null || triggerHandler === void 0 ? void 0 : triggerHandler.openElementBrowserModal;
27
+ var showViewMore = (triggerHandler === null || triggerHandler === void 0 ? void 0 : triggerHandler.id) === TypeAheadAvailableNodes.QUICK_INSERT && !!openElementBrowserModal && editorExperiment('platform_editor_controls', 'variant1');
24
28
  var _useState = useState(false),
25
29
  _useState2 = _slicedToArray(_useState, 2),
26
30
  closed = _useState2[0],
@@ -31,7 +35,7 @@ export var WrapperTypeAhead = /*#__PURE__*/React.memo(function (_ref) {
31
35
  setQuery = _useState4[1];
32
36
  var queryRef = useRef(query);
33
37
  var editorViewRef = useRef(editorView);
34
- var items = useLoadItems(triggerHandler, editorView, query);
38
+ var items = useLoadItems(triggerHandler, editorView, query, showViewMore);
35
39
  useLayoutEffect(function () {
36
40
  queryRef.current = query;
37
41
  }, [query]);
@@ -4,7 +4,7 @@ import { fg } from '@atlaskit/platform-feature-flags';
4
4
  import { updateListError } from '../../pm-plugins/commands/update-list-error';
5
5
  import { updateListItem } from '../../pm-plugins/commands/update-list-items';
6
6
  var EMPTY_LIST_ITEM = [];
7
- export var useLoadItems = function useLoadItems(triggerHandler, editorView, query) {
7
+ export var useLoadItems = function useLoadItems(triggerHandler, editorView, query, showViewMore) {
8
8
  var _useState = useState(EMPTY_LIST_ITEM),
9
9
  _useState2 = _slicedToArray(_useState, 2),
10
10
  items = _useState2[0],
@@ -27,8 +27,11 @@ export var useLoadItems = function useLoadItems(triggerHandler, editorView, quer
27
27
  if (componentIsMounted.current) {
28
28
  setItems(list);
29
29
  }
30
+ var viewMoreItem = {
31
+ title: 'View more'
32
+ };
30
33
  queueMicrotask(function () {
31
- updateListItem(list)(view.state, view.dispatch);
34
+ updateListItem(showViewMore ? list.concat(viewMoreItem) : list)(view.state, view.dispatch);
32
35
  });
33
36
  }).catch(function (e) {
34
37
  if (fg('platform_editor_offline_editing_ga')) {
@@ -198,22 +198,31 @@ export var TypeAheadPopup = /*#__PURE__*/React.memo(function (props) {
198
198
  var _window$getSelection2;
199
199
  // Check if new focus point is inside the current editor. If it is not we
200
200
  // want to close the typeahead popup regardless of text selection state
201
- var focusNode = (_window$getSelection2 = window.getSelection()) === null || _window$getSelection2 === void 0 ? void 0 : _window$getSelection2.focusNode;
201
+ var currentFocus = (_window$getSelection2 = window.getSelection()) === null || _window$getSelection2 === void 0 ? void 0 : _window$getSelection2.focusNode; // the focusNode is either TextNode, ElementNode
202
+ // if currentFocus is not HTMLElement, take its parent node as focusNode
203
+ var focusNode = currentFocus instanceof HTMLElement ? currentFocus : currentFocus === null || currentFocus === void 0 ? void 0 : currentFocus.parentNode;
202
204
  if (focusNode instanceof HTMLElement) {
203
205
  var innerEditor = focusNode.closest('.extension-editable-area');
204
- // When there is no related target, we default to not closing the popup
205
- var newFocusInsideCurrentEditor = !relatedTarget;
206
- if (relatedTarget instanceof HTMLElement) {
207
- if (innerEditor) {
208
- // check if the new focus is inside inner editor, keep popup opens
209
- newFocusInsideCurrentEditor = innerEditor.contains(relatedTarget);
210
- } else {
211
- // if the new focus contains current focus node, the popup won't close
212
- newFocusInsideCurrentEditor = relatedTarget.contains(focusNode);
206
+ if (innerEditor) {
207
+ // When there is no related target, we default to not closing the popup
208
+ var newFocusInsideCurrentEditor = !relatedTarget;
209
+ if (relatedTarget instanceof HTMLElement) {
210
+ if (innerEditor) {
211
+ // check if the new focus is inside inner editor, keep popup opens
212
+ newFocusInsideCurrentEditor = innerEditor.contains(relatedTarget);
213
+ } else {
214
+ // if the new focus contains current focus node, the popup won't close
215
+ newFocusInsideCurrentEditor = relatedTarget.contains(focusNode);
216
+ }
217
+ }
218
+ if (!isTextSelected && newFocusInsideCurrentEditor) {
219
+ return;
220
+ }
221
+ } else {
222
+ // if the current focus in outer editor, keep the existing behaviour, do not close the pop up if text is not selected
223
+ if (!isTextSelected) {
224
+ return;
213
225
  }
214
- }
215
- if (!isTextSelected && newFocusInsideCurrentEditor) {
216
- return;
217
226
  }
218
227
  }
219
228
  } else {
@@ -20,6 +20,7 @@ export declare const TypeAheadList: React.FC<import("react-intl-next").WithIntlP
20
20
  moreElementsInQuickInsertViewEnabled?: boolean | undefined;
21
21
  api: ExtractInjectionAPI<TypeAheadPlugin> | undefined;
22
22
  showViewMore?: boolean | undefined;
23
+ onViewMoreClick?: (() => void) | undefined;
23
24
  } & WrappedComponentProps>> & {
24
25
  WrappedComponent: React.ComponentType<{
25
26
  items: Array<TypeAheadItem>;
@@ -32,5 +33,6 @@ export declare const TypeAheadList: React.FC<import("react-intl-next").WithIntlP
32
33
  moreElementsInQuickInsertViewEnabled?: boolean | undefined;
33
34
  api: ExtractInjectionAPI<TypeAheadPlugin> | undefined;
34
35
  showViewMore?: boolean | undefined;
36
+ onViewMoreClick?: (() => void) | undefined;
35
37
  } & WrappedComponentProps>;
36
38
  };
@@ -1,8 +1,5 @@
1
- /**
2
- * @jsxRuntime classic
3
- * @jsx jsx
4
- */
5
1
  import { jsx } from '@emotion/react';
6
- export declare const ViewMore: ({ onClick, }: {
7
- onClick: (e: React.MouseEvent<HTMLElement> | React.KeyboardEvent<HTMLElement>) => void;
2
+ export declare const ViewMore: ({ onClick, isFocused }: {
3
+ onClick: () => void;
4
+ isFocused: boolean;
8
5
  }) => jsx.JSX.Element;
@@ -1,3 +1,3 @@
1
1
  import type { TypeAheadHandler, TypeAheadItem } from '@atlaskit/editor-common/types';
2
2
  import type { EditorView } from '@atlaskit/editor-prosemirror/view';
3
- export declare const useLoadItems: (triggerHandler: TypeAheadHandler, editorView: EditorView, query: string) => Array<TypeAheadItem>;
3
+ export declare const useLoadItems: (triggerHandler: TypeAheadHandler, editorView: EditorView, query: string, showViewMore?: boolean) => Array<TypeAheadItem>;
@@ -20,6 +20,7 @@ export declare const TypeAheadList: React.FC<import("react-intl-next").WithIntlP
20
20
  moreElementsInQuickInsertViewEnabled?: boolean | undefined;
21
21
  api: ExtractInjectionAPI<TypeAheadPlugin> | undefined;
22
22
  showViewMore?: boolean | undefined;
23
+ onViewMoreClick?: (() => void) | undefined;
23
24
  } & WrappedComponentProps>> & {
24
25
  WrappedComponent: React.ComponentType<{
25
26
  items: Array<TypeAheadItem>;
@@ -32,5 +33,6 @@ export declare const TypeAheadList: React.FC<import("react-intl-next").WithIntlP
32
33
  moreElementsInQuickInsertViewEnabled?: boolean | undefined;
33
34
  api: ExtractInjectionAPI<TypeAheadPlugin> | undefined;
34
35
  showViewMore?: boolean | undefined;
36
+ onViewMoreClick?: (() => void) | undefined;
35
37
  } & WrappedComponentProps>;
36
38
  };
@@ -1,8 +1,5 @@
1
- /**
2
- * @jsxRuntime classic
3
- * @jsx jsx
4
- */
5
1
  import { jsx } from '@emotion/react';
6
- export declare const ViewMore: ({ onClick, }: {
7
- onClick: (e: React.MouseEvent<HTMLElement> | React.KeyboardEvent<HTMLElement>) => void;
2
+ export declare const ViewMore: ({ onClick, isFocused }: {
3
+ onClick: () => void;
4
+ isFocused: boolean;
8
5
  }) => jsx.JSX.Element;
@@ -1,3 +1,3 @@
1
1
  import type { TypeAheadHandler, TypeAheadItem } from '@atlaskit/editor-common/types';
2
2
  import type { EditorView } from '@atlaskit/editor-prosemirror/view';
3
- export declare const useLoadItems: (triggerHandler: TypeAheadHandler, editorView: EditorView, query: string) => Array<TypeAheadItem>;
3
+ export declare const useLoadItems: (triggerHandler: TypeAheadHandler, editorView: EditorView, query: string, showViewMore?: boolean) => Array<TypeAheadItem>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/editor-plugin-type-ahead",
3
- "version": "2.1.0",
3
+ "version": "2.1.2",
4
4
  "description": "Type-ahead plugin for @atlaskit/editor-core",
5
5
  "author": "Atlassian Pty Ltd",
6
6
  "license": "Apache-2.0",
@@ -34,7 +34,7 @@
34
34
  },
35
35
  "dependencies": {
36
36
  "@atlaskit/adf-schema": "^47.6.0",
37
- "@atlaskit/editor-common": "^100.5.0",
37
+ "@atlaskit/editor-common": "^101.1.0",
38
38
  "@atlaskit/editor-element-browser": "^0.1.0",
39
39
  "@atlaskit/editor-plugin-analytics": "^2.1.0",
40
40
  "@atlaskit/editor-plugin-connectivity": "^2.0.0",
@@ -47,7 +47,7 @@
47
47
  "@atlaskit/platform-feature-flags": "^1.1.0",
48
48
  "@atlaskit/primitives": "^14.1.0",
49
49
  "@atlaskit/prosemirror-input-rules": "^3.3.0",
50
- "@atlaskit/theme": "^17.0.0",
50
+ "@atlaskit/theme": "^18.0.0",
51
51
  "@atlaskit/tmp-editor-statsig": "^3.4.0",
52
52
  "@atlaskit/tokens": "^4.3.0",
53
53
  "@babel/runtime": "^7.0.0",