@atlaskit/editor-plugin-type-ahead 13.2.1 → 13.2.3

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 (69) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/dist/cjs/pm-plugins/commands/insert-type-ahead-item.js +96 -1
  3. package/dist/cjs/ui/ContentComponent.js +4 -0
  4. package/dist/cjs/ui/InputQuery.js +21 -14
  5. package/dist/cjs/ui/WrapperTypeAhead.js +23 -1
  6. package/dist/cjs/ui/hooks/use-load-items.js +6 -1
  7. package/dist/cjs/ui/registered-menu/PassThrough.js +10 -0
  8. package/dist/cjs/ui/registered-menu/RegisteredTypeAheadMenu.js +146 -0
  9. package/dist/cjs/ui/registered-menu/TypeAheadContext.js +8 -0
  10. package/dist/cjs/ui/registered-menu/TypeAheadItemRow.js +27 -0
  11. package/dist/cjs/ui/registered-menu/TypeAheadProvider.js +8 -0
  12. package/dist/cjs/ui/registered-menu/buildTypeAheadMenuModel.js +50 -0
  13. package/dist/cjs/ui/registered-menu/getTypeAheadItemId.js +9 -0
  14. package/dist/cjs/ui/registered-menu/quick-insert/TypeAheadQuickInsertItem.js +37 -0
  15. package/dist/cjs/ui/registered-menu/quick-insert/TypeAheadQuickInsertProvider.js +41 -0
  16. package/dist/cjs/ui/registered-menu/typeAheadMenuTypes.js +5 -0
  17. package/dist/cjs/ui/registered-menu/typeAheadSurfaces.js +26 -0
  18. package/dist/cjs/ui/registered-menu/useTypeAheadContext.js +15 -0
  19. package/dist/es2019/pm-plugins/commands/insert-type-ahead-item.js +97 -0
  20. package/dist/es2019/ui/ContentComponent.js +4 -0
  21. package/dist/es2019/ui/InputQuery.js +15 -8
  22. package/dist/es2019/ui/WrapperTypeAhead.js +23 -1
  23. package/dist/es2019/ui/hooks/use-load-items.js +6 -2
  24. package/dist/es2019/ui/registered-menu/PassThrough.js +3 -0
  25. package/dist/es2019/ui/registered-menu/RegisteredTypeAheadMenu.js +127 -0
  26. package/dist/es2019/ui/registered-menu/TypeAheadContext.js +2 -0
  27. package/dist/es2019/ui/registered-menu/TypeAheadItemRow.js +18 -0
  28. package/dist/es2019/ui/registered-menu/TypeAheadProvider.js +2 -0
  29. package/dist/es2019/ui/registered-menu/buildTypeAheadMenuModel.js +24 -0
  30. package/dist/es2019/ui/registered-menu/getTypeAheadItemId.js +1 -0
  31. package/dist/es2019/ui/registered-menu/quick-insert/TypeAheadQuickInsertItem.js +25 -0
  32. package/dist/es2019/ui/registered-menu/quick-insert/TypeAheadQuickInsertProvider.js +33 -0
  33. package/dist/es2019/ui/registered-menu/typeAheadMenuTypes.js +1 -0
  34. package/dist/es2019/ui/registered-menu/typeAheadSurfaces.js +20 -0
  35. package/dist/es2019/ui/registered-menu/useTypeAheadContext.js +9 -0
  36. package/dist/esm/pm-plugins/commands/insert-type-ahead-item.js +95 -0
  37. package/dist/esm/ui/ContentComponent.js +4 -0
  38. package/dist/esm/ui/InputQuery.js +21 -14
  39. package/dist/esm/ui/WrapperTypeAhead.js +23 -1
  40. package/dist/esm/ui/hooks/use-load-items.js +6 -1
  41. package/dist/esm/ui/registered-menu/PassThrough.js +4 -0
  42. package/dist/esm/ui/registered-menu/RegisteredTypeAheadMenu.js +137 -0
  43. package/dist/esm/ui/registered-menu/TypeAheadContext.js +2 -0
  44. package/dist/esm/ui/registered-menu/TypeAheadItemRow.js +19 -0
  45. package/dist/esm/ui/registered-menu/TypeAheadProvider.js +2 -0
  46. package/dist/esm/ui/registered-menu/buildTypeAheadMenuModel.js +44 -0
  47. package/dist/esm/ui/registered-menu/getTypeAheadItemId.js +3 -0
  48. package/dist/esm/ui/registered-menu/quick-insert/TypeAheadQuickInsertItem.js +28 -0
  49. package/dist/esm/ui/registered-menu/quick-insert/TypeAheadQuickInsertProvider.js +33 -0
  50. package/dist/esm/ui/registered-menu/typeAheadMenuTypes.js +1 -0
  51. package/dist/esm/ui/registered-menu/typeAheadSurfaces.js +20 -0
  52. package/dist/esm/ui/registered-menu/useTypeAheadContext.js +9 -0
  53. package/dist/types/pm-plugins/commands/insert-type-ahead-item.d.ts +7 -0
  54. package/dist/types/typeAheadPluginType.d.ts +3 -1
  55. package/dist/types/ui/InputQuery.d.ts +10 -2
  56. package/dist/types/ui/hooks/use-load-items.d.ts +1 -1
  57. package/dist/types/ui/registered-menu/PassThrough.d.ts +2 -0
  58. package/dist/types/ui/registered-menu/RegisteredTypeAheadMenu.d.ts +30 -0
  59. package/dist/types/ui/registered-menu/TypeAheadContext.d.ts +14 -0
  60. package/dist/types/ui/registered-menu/TypeAheadItemRow.d.ts +11 -0
  61. package/dist/types/ui/registered-menu/TypeAheadProvider.d.ts +3 -0
  62. package/dist/types/ui/registered-menu/buildTypeAheadMenuModel.d.ts +7 -0
  63. package/dist/types/ui/registered-menu/getTypeAheadItemId.d.ts +1 -0
  64. package/dist/types/ui/registered-menu/quick-insert/TypeAheadQuickInsertItem.d.ts +3 -0
  65. package/dist/types/ui/registered-menu/quick-insert/TypeAheadQuickInsertProvider.d.ts +2 -0
  66. package/dist/types/ui/registered-menu/typeAheadMenuTypes.d.ts +9 -0
  67. package/dist/types/ui/registered-menu/typeAheadSurfaces.d.ts +17 -0
  68. package/dist/types/ui/registered-menu/useTypeAheadContext.d.ts +2 -0
  69. package/package.json +8 -4
@@ -0,0 +1,41 @@
1
+ "use strict";
2
+
3
+ var _typeof = require("@babel/runtime/helpers/typeof");
4
+ Object.defineProperty(exports, "__esModule", {
5
+ value: true
6
+ });
7
+ exports.TypeAheadQuickInsertProvider = void 0;
8
+ var _react = _interopRequireWildcard(require("react"));
9
+ var _provider = require("@atlaskit/editor-common/quick-insert/provider");
10
+ var _insertTypeAheadItem = require("../../../pm-plugins/commands/insert-type-ahead-item");
11
+ var _useTypeAheadContext2 = require("../useTypeAheadContext");
12
+ function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function _interopRequireWildcard(e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != _typeof(e) && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (var _t in e) "default" !== _t && {}.hasOwnProperty.call(e, _t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, _t)) && (i.get || i.set) ? o(f, _t, i) : f[_t] = e[_t]); return f; })(e, t); }
13
+ var TypeAheadQuickInsertProvider = exports.TypeAheadQuickInsertProvider = function TypeAheadQuickInsertProvider(_ref) {
14
+ var children = _ref.children;
15
+ var _useTypeAheadContext = (0, _useTypeAheadContext2.useTypeAheadContext)(),
16
+ editorView = _useTypeAheadContext.editorView,
17
+ inputMethod = _useTypeAheadContext.inputMethod,
18
+ onClose = _useTypeAheadContext.onClose,
19
+ query = _useTypeAheadContext.query,
20
+ triggerHandler = _useTypeAheadContext.triggerHandler;
21
+ var select = (0, _react.useCallback)(function (selectionHandler) {
22
+ onClose();
23
+ queueMicrotask(function () {
24
+ (0, _insertTypeAheadItem.performTypeAheadSelection)(editorView)({
25
+ handler: triggerHandler,
26
+ inputMethod: inputMethod,
27
+ query: query,
28
+ selectionHandler: selectionHandler
29
+ });
30
+ });
31
+ }, [editorView, inputMethod, onClose, query, triggerHandler]);
32
+ var contextValue = (0, _react.useMemo)(function () {
33
+ return {
34
+ editorView: editorView,
35
+ select: select
36
+ };
37
+ }, [editorView, select]);
38
+ return /*#__PURE__*/_react.default.createElement(_provider.QuickInsertProvider, {
39
+ value: contextValue
40
+ }, children);
41
+ };
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
@@ -0,0 +1,26 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.getTypeAheadSurface = void 0;
7
+ var _analytics = require("@atlaskit/editor-common/analytics");
8
+ var _keys = require("@atlaskit/editor-common/quick-insert/keys");
9
+ var _typeAhead = require("@atlaskit/editor-common/type-ahead");
10
+ var _isExperimentEnabled = require("@atlaskit/platform-feature-experiments/is-experiment-enabled");
11
+ var _TypeAheadQuickInsertItem = require("./quick-insert/TypeAheadQuickInsertItem");
12
+ var _TypeAheadQuickInsertProvider = require("./quick-insert/TypeAheadQuickInsertProvider");
13
+ var quickInsertSurface = {
14
+ inputMethod: _analytics.INPUT_METHOD.QUICK_INSERT,
15
+ Item: _TypeAheadQuickInsertItem.TypeAheadQuickInsertItem,
16
+ listIdPrefix: 'quick-insert-menu',
17
+ listLabel: _typeAhead.typeAheadListMessages.quickInsertInputLabel,
18
+ Provider: _TypeAheadQuickInsertProvider.TypeAheadQuickInsertProvider,
19
+ root: _keys.MENU
20
+ };
21
+ var getTypeAheadSurface = exports.getTypeAheadSurface = function getTypeAheadSurface(id) {
22
+ if (id === _typeAhead.TypeAheadAvailableNodes.QUICK_INSERT && (0, _isExperimentEnabled.isExperimentEnabled)('platform_editor_slash_command')) {
23
+ return quickInsertSurface;
24
+ }
25
+ return undefined;
26
+ };
@@ -0,0 +1,15 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.useTypeAheadContext = void 0;
7
+ var _react = require("react");
8
+ var _TypeAheadContext = require("./TypeAheadContext");
9
+ var useTypeAheadContext = exports.useTypeAheadContext = function useTypeAheadContext() {
10
+ var value = (0, _react.useContext)(_TypeAheadContext.TypeAheadContext);
11
+ if (!value) {
12
+ throw new Error('useTypeAheadContext must be used within TypeAheadProvider');
13
+ }
14
+ return value;
15
+ };
@@ -201,4 +201,101 @@ export const insertTypeAheadItem = view => ({
201
201
  closeHistory(tr);
202
202
  view.dispatch(tr);
203
203
  view.focus();
204
+ };
205
+ const executeTypeAheadSelection = view => ({
206
+ selectItem,
207
+ handler,
208
+ mode,
209
+ query,
210
+ sourceListItem
211
+ }) => {
212
+ const pluginState = getPluginState(view.state);
213
+ if (!pluginState) {
214
+ return;
215
+ }
216
+ const stats = (pluginState.stats || new StatsModifier()).serialize();
217
+ const meta = {
218
+ mode,
219
+ query,
220
+ stats,
221
+ sourceListItem
222
+ };
223
+ const {
224
+ tr
225
+ } = view.state;
226
+ const trigger = handler.trigger;
227
+ let text = `${trigger}${query}`;
228
+ if (mode === SelectItemMode.SPACE) {
229
+ text = text.trim().concat(' ');
230
+ }
231
+ const selectedIndex = -1;
232
+ const wasInsertedBySpace = mode === SelectItemMode.SPACE;
233
+ const {
234
+ selection: {
235
+ from: textStartPosition
236
+ }
237
+ } = tr;
238
+ const insertItem = newEditorState => {
239
+ const insertCallback = createInsertCallback({
240
+ editorState: newEditorState,
241
+ query,
242
+ mode,
243
+ handler,
244
+ wasInsertedBySpace,
245
+ selectedIndex,
246
+ textInserted: text,
247
+ textStartPosition
248
+ });
249
+ let wasInsertCallbackCalled = false;
250
+ const insertCallbackProxy = new Proxy(insertCallback, {
251
+ apply(target, _thisContext, argumentsList) {
252
+ wasInsertCallbackCalled = true;
253
+ return target(...argumentsList);
254
+ }
255
+ });
256
+ const nextTr = selectItem(newEditorState, insertCallbackProxy, meta);
257
+ if (!wasInsertCallbackCalled && nextTr) {
258
+ closeHistory(nextTr);
259
+ if (!nextTr.getMeta(pluginKey)) {
260
+ closeTypeAhead(nextTr);
261
+ }
262
+ }
263
+ return nextTr || false;
264
+ };
265
+ const position = {
266
+ start: tr.selection.from,
267
+ end: tr.selection.from + text.length
268
+ };
269
+ tr.setMeta(pluginKey, {
270
+ action: ACTIONS.INSERT_RAW_QUERY,
271
+ params: createDeleteRawTextCallback({
272
+ wasInsertedBySpace,
273
+ selectedIndex,
274
+ insertItem,
275
+ position,
276
+ query,
277
+ trigger
278
+ })
279
+ });
280
+ tr.insertText(text);
281
+ closeHistory(tr);
282
+ view.dispatch(tr);
283
+ view.focus();
284
+ };
285
+ export const performTypeAheadSelection = view => ({
286
+ inputMethod,
287
+ selectionHandler,
288
+ handler,
289
+ query
290
+ }) => {
291
+ executeTypeAheadSelection(view)({
292
+ handler,
293
+ mode: SelectItemMode.SELECTED,
294
+ query,
295
+ sourceListItem: [],
296
+ selectItem: (_state, insert) => selectionHandler({
297
+ insert,
298
+ source: inputMethod
299
+ })
300
+ });
204
301
  };
@@ -1,5 +1,6 @@
1
1
  import React from 'react';
2
2
  import { useSharedPluginStateWithSelector } from '@atlaskit/editor-common/hooks';
3
+ import { getTypeAheadSurface } from './registered-menu/typeAheadSurfaces';
3
4
  import { TypeAheadMenu } from './TypeAheadMenu';
4
5
  export function ContentComponent({
5
6
  api,
@@ -31,6 +32,9 @@ export function ContentComponent({
31
32
  if (items === undefined || sections === undefined || decorationSet === undefined || errorInfo === undefined || decorationElement === undefined || query === undefined || selectedIndex === undefined) {
32
33
  return null;
33
34
  }
35
+ if (getTypeAheadSurface(triggerHandler === null || triggerHandler === void 0 ? void 0 : triggerHandler.id)) {
36
+ return null;
37
+ }
34
38
  return /*#__PURE__*/React.createElement(TypeAheadMenu, {
35
39
  editorView: editorView,
36
40
  popupMountRef: popupMountRef
@@ -81,6 +81,7 @@ const getAriaLabel = (triggerPrefix, _intl) => {
81
81
  }
82
82
  };
83
83
  export const InputQuery = /*#__PURE__*/React.memo(({
84
+ activeDescendantId,
84
85
  triggerQueryPrefix,
85
86
  cancel,
86
87
  onQueryChange,
@@ -92,8 +93,13 @@ export const InputQuery = /*#__PURE__*/React.memo(({
92
93
  onQueryFocus,
93
94
  onUndoRedo,
94
95
  editorView,
95
- items
96
+ items,
97
+ listId,
98
+ optionCount,
99
+ shouldKeepOpenOnRegisteredMenu
96
100
  }) => {
101
+ var _ref;
102
+ const resolvedOptionCount = (_ref = optionCount !== null && optionCount !== void 0 ? optionCount : items === null || items === void 0 ? void 0 : items.length) !== null && _ref !== void 0 ? _ref : 0;
97
103
  const ref = useRef(document.createElement('span'));
98
104
  const inputRef = useRef(null);
99
105
  const [query, setQuery] = useState(null);
@@ -278,7 +284,7 @@ export const InputQuery = /*#__PURE__*/React.memo(({
278
284
  // Given the user is changing the focus
279
285
  // When the target is inside the TypeAhead Popup
280
286
  // Then the popup should stay open
281
- if (relatedTarget instanceof HTMLElement && relatedTarget.closest && relatedTarget.closest(`.${TYPE_AHEAD_POPUP_CONTENT_CLASS}`)) {
287
+ if (relatedTarget instanceof HTMLElement && relatedTarget.closest && (relatedTarget.closest(`.${TYPE_AHEAD_POPUP_CONTENT_CLASS}`) || shouldKeepOpenOnRegisteredMenu && relatedTarget.closest('[data-registered-type-ahead-menu]'))) {
282
288
  return;
283
289
  }
284
290
 
@@ -401,7 +407,7 @@ export const InputQuery = /*#__PURE__*/React.memo(({
401
407
  element.removeEventListener('input', onInput);
402
408
  }
403
409
  };
404
- }, [triggerQueryPrefix, cleanedInputContent, onQueryFocus, cancel, checkKeyEvent, editorView.state, isEditorControlsEnabled]);
410
+ }, [triggerQueryPrefix, cleanedInputContent, onQueryFocus, cancel, checkKeyEvent, editorView.state, isEditorControlsEnabled, shouldKeepOpenOnRegisteredMenu]);
405
411
  useLayoutEffect(() => {
406
412
  const hasReopenQuery = typeof reopenQuery === 'string' && reopenQuery.trim().length > 0;
407
413
  if (ref.current && forceFocus) {
@@ -450,9 +456,10 @@ export const InputQuery = /*#__PURE__*/React.memo(({
450
456
  tabIndex: -1,
451
457
  onInput: isSearchPlaceholderEnabled ? onInput : undefined,
452
458
  role: "combobox",
453
- "aria-controls": TYPE_AHEAD_DECORATION_ELEMENT_ID,
459
+ "aria-activedescendant": activeDescendantId,
460
+ "aria-controls": listId !== null && listId !== void 0 ? listId : TYPE_AHEAD_DECORATION_ELEMENT_ID,
454
461
  "aria-autocomplete": "list",
455
- "aria-expanded": items.length !== 0,
462
+ "aria-expanded": resolvedOptionCount !== 0,
456
463
  "aria-labelledby": assistiveHintID,
457
464
  suppressContentEditableWarning: true,
458
465
  "data-query-prefix": triggerQueryPrefix
@@ -470,10 +477,10 @@ export const InputQuery = /*#__PURE__*/React.memo(({
470
477
  display: 'none'
471
478
  }
472
479
  }, intl.formatMessage(typeAheadListMessages.inputQueryAssistiveLabel)), jsx(AssistiveText, {
473
- assistiveText: items.length === 0 ? intl.formatMessage(typeAheadListMessages.noSearchResultsLabel, {
474
- itemsLength: items.length
480
+ assistiveText: resolvedOptionCount === 0 ? intl.formatMessage(typeAheadListMessages.noSearchResultsLabel, {
481
+ itemsLength: resolvedOptionCount
475
482
  }) : '',
476
- isInFocus: items.length === 0 || isInFocus,
483
+ isInFocus: resolvedOptionCount === 0 || isInFocus,
477
484
  id: TYPE_AHEAD_DECORATION_ELEMENT_ID
478
485
  }));
479
486
  });
@@ -13,6 +13,8 @@ import { useItemInsert } from './hooks/use-item-insert';
13
13
  import { useLoadItems } from './hooks/use-load-items';
14
14
  import { useOnForceSelect } from './hooks/use-on-force-select';
15
15
  import { InputQuery } from './InputQuery';
16
+ import { RegisteredTypeAheadMenu } from './registered-menu/RegisteredTypeAheadMenu';
17
+ import { getTypeAheadSurface } from './registered-menu/typeAheadSurfaces';
16
18
  export const WrapperTypeAhead = /*#__PURE__*/React.memo(({
17
19
  triggerHandler,
18
20
  editorView,
@@ -38,7 +40,8 @@ export const WrapperTypeAhead = /*#__PURE__*/React.memo(({
38
40
  const [query, setQuery] = useState(reopenQuery || '');
39
41
  const queryRef = useRef(query);
40
42
  const editorViewRef = useRef(editorView);
41
- const items = useLoadItems(triggerHandler, editorView, query, showMoreOptionsButton, api, intl);
43
+ const registeredSurface = getTypeAheadSurface(triggerHandler.id);
44
+ const items = useLoadItems(triggerHandler, editorView, query, showMoreOptionsButton, api, intl, !registeredSurface);
42
45
  useEffect(() => {
43
46
  if (!closed && fg('platform_editor_ease_of_use_metrics')) {
44
47
  var _api$metrics;
@@ -141,6 +144,25 @@ export const WrapperTypeAhead = /*#__PURE__*/React.memo(({
141
144
  if (!triggerHandler) {
142
145
  return null;
143
146
  }
147
+ if (registeredSurface) {
148
+ return /*#__PURE__*/React.createElement(RegisteredTypeAheadMenu, {
149
+ anchorElement: anchorElement,
150
+ api: api,
151
+ cancel: cancel,
152
+ editorView: editorView,
153
+ forceFocus: shouldFocusCursorInsideQuery,
154
+ onClose: closePopup,
155
+ onUndoRedo: onUndoRedo,
156
+ popupsBoundariesElement: popupsBoundariesElement,
157
+ popupsMountPoint: popupsMountPoint,
158
+ popupsScrollableElement: popupsScrollableElement,
159
+ query: query,
160
+ reopenQuery: reopenQuery,
161
+ setQuery: setQuery,
162
+ surface: registeredSurface,
163
+ triggerHandler: triggerHandler
164
+ });
165
+ }
144
166
  return /*#__PURE__*/React.createElement(InputQuery, {
145
167
  triggerQueryPrefix: triggerHandler === null || triggerHandler === void 0 ? void 0 : triggerHandler.trigger,
146
168
  onQueryChange: setQuery,
@@ -6,13 +6,17 @@ import { updateListError } from '../../pm-plugins/commands/update-list-error';
6
6
  import { updateListItem } from '../../pm-plugins/commands/update-list-items';
7
7
  import { buildSectionedResult } from './build-sectioned-result';
8
8
  const EMPTY_LIST_ITEM = [];
9
- export const useLoadItems = (triggerHandler, editorView, query, showViewMore, api, intl) => {
9
+ export const useLoadItems = (triggerHandler, editorView, query, showViewMore, api, intl, enabled = true) => {
10
10
  const [items, setItems] = useState(EMPTY_LIST_ITEM);
11
11
  const componentIsMounted = useRef(true);
12
12
  const editorViewRef = useRef(editorView);
13
13
  const latestQueryRef = useRef(query);
14
14
  latestQueryRef.current = query;
15
15
  useEffect(() => {
16
+ if (!enabled) {
17
+ setItems(EMPTY_LIST_ITEM);
18
+ return;
19
+ }
16
20
  const requestQuery = query;
17
21
  const isStaleRequest = () => latestQueryRef.current !== requestQuery;
18
22
  const options = {
@@ -119,7 +123,7 @@ export const useLoadItems = (triggerHandler, editorView, query, showViewMore, ap
119
123
  // ignore because EditorView is mutable but we don't want to
120
124
  // call loadItems when it changes, only when the query changes
121
125
  // eslint-disable-next-line react-hooks/exhaustive-deps
122
- }, [triggerHandler, query, intl]);
126
+ }, [triggerHandler, query, intl, enabled]);
123
127
  useEffect(() => {
124
128
  return () => {
125
129
  componentIsMounted.current = false;
@@ -0,0 +1,3 @@
1
+ export const PassThrough = ({
2
+ children
3
+ }) => children;
@@ -0,0 +1,127 @@
1
+ import React, { useCallback, useId, useLayoutEffect, useMemo, useRef, useState } from 'react';
2
+ import { useIntl } from 'react-intl';
3
+ import { Popup } from '@atlaskit/editor-common/ui';
4
+ import { akEditorFloatingDialogZIndex } from '@atlaskit/editor-shared-styles/constants';
5
+ import { Box } from '@atlaskit/primitives/compiled';
6
+ import { InputQuery } from '../InputQuery';
7
+ import { buildTypeAheadMenuModel } from './buildTypeAheadMenuModel';
8
+ import { getTypeAheadItemId } from './getTypeAheadItemId';
9
+ import { PassThrough } from './PassThrough';
10
+ import { TypeAheadItemRow } from './TypeAheadItemRow';
11
+ import { TypeAheadProvider } from './TypeAheadProvider';
12
+ const POPUP_OFFSET = [0, 8];
13
+ export const RegisteredTypeAheadMenu = ({
14
+ anchorElement,
15
+ api,
16
+ cancel,
17
+ editorView,
18
+ forceFocus,
19
+ onClose,
20
+ onUndoRedo,
21
+ popupsBoundariesElement,
22
+ popupsMountPoint,
23
+ popupsScrollableElement,
24
+ query,
25
+ reopenQuery,
26
+ setQuery,
27
+ surface,
28
+ triggerHandler
29
+ }) => {
30
+ var _model$root$component, _model$root;
31
+ const intl = useIntl();
32
+ const generatedId = useId();
33
+ const listId = `${surface.listIdPrefix}-${generatedId}`;
34
+ const menuRef = useRef(null);
35
+ const [selectedItemIndex, setSelectedItemIndex] = useState(0);
36
+ const components = useMemo(() => {
37
+ var _api$uiControlRegistr, _api$uiControlRegistr2;
38
+ return (_api$uiControlRegistr = api === null || api === void 0 ? void 0 : (_api$uiControlRegistr2 = api.uiControlRegistry) === null || _api$uiControlRegistr2 === void 0 ? void 0 : _api$uiControlRegistr2.actions.getComponents(surface.root.key)) !== null && _api$uiControlRegistr !== void 0 ? _api$uiControlRegistr : [];
39
+ }, [api, surface.root.key]);
40
+ const model = useMemo(() => buildTypeAheadMenuModel(components, surface.root), [components, surface.root]);
41
+ const itemKeySignature = model.items.map(({
42
+ key
43
+ }) => key).join(',');
44
+ useLayoutEffect(() => {
45
+ setSelectedItemIndex(model.items.length > 0 ? 0 : -1);
46
+ }, [itemKeySignature, model.items.length, query]);
47
+ const selectActiveItem = useCallback(_mode => {
48
+ var _menuRef$current, _menuRef$current$owne;
49
+ if (selectedItemIndex < 0) {
50
+ return;
51
+ }
52
+ (_menuRef$current = menuRef.current) === null || _menuRef$current === void 0 ? void 0 : (_menuRef$current$owne = _menuRef$current.ownerDocument.getElementById(getTypeAheadItemId(listId, selectedItemIndex))) === null || _menuRef$current$owne === void 0 ? void 0 : _menuRef$current$owne.click();
53
+ }, [listId, selectedItemIndex]);
54
+ const selectNextItem = useCallback(() => {
55
+ setSelectedItemIndex(currentIndex => {
56
+ if (model.items.length === 0) {
57
+ return -1;
58
+ }
59
+ return currentIndex < 0 || currentIndex === model.items.length - 1 ? 0 : currentIndex + 1;
60
+ });
61
+ }, [model.items.length]);
62
+ const selectPreviousItem = useCallback(() => {
63
+ setSelectedItemIndex(currentIndex => {
64
+ if (model.items.length === 0) {
65
+ return -1;
66
+ }
67
+ return currentIndex <= 0 ? model.items.length - 1 : currentIndex - 1;
68
+ });
69
+ }, [model.items.length]);
70
+ const typeAheadContextValue = useMemo(() => ({
71
+ api,
72
+ editorView,
73
+ inputMethod: surface.inputMethod,
74
+ onClose,
75
+ query,
76
+ triggerHandler
77
+ }), [api, editorView, onClose, query, surface.inputMethod, triggerHandler]);
78
+ const noOp = useCallback(() => {}, []);
79
+ const RootComponent = (_model$root$component = (_model$root = model.root) === null || _model$root === void 0 ? void 0 : _model$root.component) !== null && _model$root$component !== void 0 ? _model$root$component : PassThrough;
80
+ const SurfaceProvider = surface.Provider;
81
+ return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(InputQuery, {
82
+ activeDescendantId: selectedItemIndex >= 0 ? getTypeAheadItemId(listId, selectedItemIndex) : undefined,
83
+ cancel: cancel,
84
+ editorView: editorView,
85
+ forceFocus: forceFocus,
86
+ listId: listId,
87
+ onItemSelect: selectActiveItem,
88
+ onQueryChange: setQuery,
89
+ onQueryFocus: noOp,
90
+ onUndoRedo: onUndoRedo,
91
+ optionCount: model.items.length,
92
+ reopenQuery: reopenQuery,
93
+ selectNextItem: selectNextItem,
94
+ selectPreviousItem: selectPreviousItem,
95
+ shouldKeepOpenOnRegisteredMenu: true,
96
+ triggerQueryPrefix: triggerHandler.trigger
97
+ }), /*#__PURE__*/React.createElement(Popup, {
98
+ ariaLabel: null,
99
+ boundariesElement: popupsBoundariesElement,
100
+ fitWidth: 320,
101
+ mountTo: popupsMountPoint,
102
+ offset: POPUP_OFFSET,
103
+ preventOverflow: true,
104
+ scrollableElement: popupsScrollableElement,
105
+ target: anchorElement,
106
+ zIndex: akEditorFloatingDialogZIndex
107
+ }, /*#__PURE__*/React.createElement(Box, {
108
+ ref: menuRef,
109
+ backgroundColor: "elevation.surface.overlay",
110
+ "data-registered-type-ahead-menu": "",
111
+ padding: "space.050"
112
+ }, /*#__PURE__*/React.createElement(TypeAheadProvider, {
113
+ value: typeAheadContextValue
114
+ }, /*#__PURE__*/React.createElement(SurfaceProvider, null, /*#__PURE__*/React.createElement(RootComponent, null, /*#__PURE__*/React.createElement(Box, {
115
+ "aria-label": intl.formatMessage(surface.listLabel),
116
+ id: listId,
117
+ role: "listbox"
118
+ }, model.items.map((registration, itemIndex) => /*#__PURE__*/React.createElement(TypeAheadItemRow, {
119
+ isSelected: selectedItemIndex === itemIndex,
120
+ itemIndex: itemIndex,
121
+ Item: surface.Item,
122
+ key: `${registration.type}:${registration.key}`,
123
+ listId: listId,
124
+ onItemHover: setSelectedItemIndex,
125
+ registration: registration
126
+ })))))))));
127
+ };
@@ -0,0 +1,2 @@
1
+ import { createContext } from 'react';
2
+ export const TypeAheadContext = /*#__PURE__*/createContext(null);
@@ -0,0 +1,18 @@
1
+ import React, { useCallback } from 'react';
2
+ import { getTypeAheadItemId } from './getTypeAheadItemId';
3
+ export const TypeAheadItemRow = ({
4
+ isSelected,
5
+ itemIndex,
6
+ Item,
7
+ listId,
8
+ onItemHover,
9
+ registration
10
+ }) => {
11
+ const onMouseMove = useCallback(() => onItemHover(itemIndex), [itemIndex, onItemHover]);
12
+ return /*#__PURE__*/React.createElement(Item, {
13
+ id: getTypeAheadItemId(listId, itemIndex),
14
+ isSelected: isSelected,
15
+ onMouseMove: onMouseMove,
16
+ registration: registration
17
+ });
18
+ };
@@ -0,0 +1,2 @@
1
+ import { TypeAheadContext } from './TypeAheadContext';
2
+ export const TypeAheadProvider = TypeAheadContext.Provider;
@@ -0,0 +1,24 @@
1
+ import { resolveSurface, willComponentRender } from '@atlaskit/editor-ui-control-model/surface-renderer';
2
+ export const buildTypeAheadMenuModel = (components, rootComponent) => {
3
+ const {
4
+ root,
5
+ childrenMap,
6
+ topLevelChildren
7
+ } = resolveSurface(components, rootComponent);
8
+ const items = [];
9
+ for (const section of topLevelChildren !== null && topLevelChildren !== void 0 ? topLevelChildren : []) {
10
+ if (section.type !== 'menu-section' || !willComponentRender(section, childrenMap)) {
11
+ continue;
12
+ }
13
+ for (const child of (_childrenMap$get = childrenMap.get(section.key)) !== null && _childrenMap$get !== void 0 ? _childrenMap$get : []) {
14
+ var _childrenMap$get;
15
+ if (child.type === 'menu-item' && willComponentRender(child, childrenMap)) {
16
+ items.push(child);
17
+ }
18
+ }
19
+ }
20
+ return {
21
+ items,
22
+ root
23
+ };
24
+ };
@@ -0,0 +1 @@
1
+ export const getTypeAheadItemId = (listId, itemIndex) => `${listId}--${itemIndex}`;
@@ -0,0 +1,25 @@
1
+ import React, { useMemo } from 'react';
2
+ import { QuickInsertProvider } from '@atlaskit/editor-common/quick-insert/provider';
3
+ import { useQuickInsertContext } from '@atlaskit/editor-common/quick-insert/use-quick-insert-context';
4
+ import { PassThrough } from '../PassThrough';
5
+ export const TypeAheadQuickInsertItem = ({
6
+ id,
7
+ isSelected,
8
+ onMouseMove,
9
+ registration
10
+ }) => {
11
+ var _registration$compone;
12
+ const quickInsertContext = useQuickInsertContext();
13
+ const Component = (_registration$compone = registration.component) !== null && _registration$compone !== void 0 ? _registration$compone : PassThrough;
14
+ const contextValue = useMemo(() => ({
15
+ ...quickInsertContext,
16
+ item: {
17
+ id,
18
+ isSelected,
19
+ onMouseMove
20
+ }
21
+ }), [id, isSelected, onMouseMove, quickInsertContext]);
22
+ return /*#__PURE__*/React.createElement(QuickInsertProvider, {
23
+ value: contextValue
24
+ }, /*#__PURE__*/React.createElement(Component, null, null));
25
+ };
@@ -0,0 +1,33 @@
1
+ import React, { useCallback, useMemo } from 'react';
2
+ import { QuickInsertProvider } from '@atlaskit/editor-common/quick-insert/provider';
3
+ import { performTypeAheadSelection } from '../../../pm-plugins/commands/insert-type-ahead-item';
4
+ import { useTypeAheadContext } from '../useTypeAheadContext';
5
+ export const TypeAheadQuickInsertProvider = ({
6
+ children
7
+ }) => {
8
+ const {
9
+ editorView,
10
+ inputMethod,
11
+ onClose,
12
+ query,
13
+ triggerHandler
14
+ } = useTypeAheadContext();
15
+ const select = useCallback(selectionHandler => {
16
+ onClose();
17
+ queueMicrotask(() => {
18
+ performTypeAheadSelection(editorView)({
19
+ handler: triggerHandler,
20
+ inputMethod,
21
+ query,
22
+ selectionHandler
23
+ });
24
+ });
25
+ }, [editorView, inputMethod, onClose, query, triggerHandler]);
26
+ const contextValue = useMemo(() => ({
27
+ editorView,
28
+ select
29
+ }), [editorView, select]);
30
+ return /*#__PURE__*/React.createElement(QuickInsertProvider, {
31
+ value: contextValue
32
+ }, children);
33
+ };
@@ -0,0 +1,20 @@
1
+ import { INPUT_METHOD } from '@atlaskit/editor-common/analytics';
2
+ import { MENU } from '@atlaskit/editor-common/quick-insert/keys';
3
+ import { TypeAheadAvailableNodes, typeAheadListMessages } from '@atlaskit/editor-common/type-ahead';
4
+ import { isExperimentEnabled } from '@atlaskit/platform-feature-experiments/is-experiment-enabled';
5
+ import { TypeAheadQuickInsertItem } from './quick-insert/TypeAheadQuickInsertItem';
6
+ import { TypeAheadQuickInsertProvider } from './quick-insert/TypeAheadQuickInsertProvider';
7
+ const quickInsertSurface = {
8
+ inputMethod: INPUT_METHOD.QUICK_INSERT,
9
+ Item: TypeAheadQuickInsertItem,
10
+ listIdPrefix: 'quick-insert-menu',
11
+ listLabel: typeAheadListMessages.quickInsertInputLabel,
12
+ Provider: TypeAheadQuickInsertProvider,
13
+ root: MENU
14
+ };
15
+ export const getTypeAheadSurface = id => {
16
+ if (id === TypeAheadAvailableNodes.QUICK_INSERT && isExperimentEnabled('platform_editor_slash_command')) {
17
+ return quickInsertSurface;
18
+ }
19
+ return undefined;
20
+ };
@@ -0,0 +1,9 @@
1
+ import { useContext } from 'react';
2
+ import { TypeAheadContext } from './TypeAheadContext';
3
+ export const useTypeAheadContext = () => {
4
+ const value = useContext(TypeAheadContext);
5
+ if (!value) {
6
+ throw new Error('useTypeAheadContext must be used within TypeAheadProvider');
7
+ }
8
+ return value;
9
+ };