@atlaskit/editor-plugin-type-ahead 10.2.2 → 10.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 (56) hide show
  1. package/CHANGELOG.md +34 -0
  2. package/commands/package.json +17 -0
  3. package/dist/cjs/entry-points/commands.js +18 -0
  4. package/dist/cjs/pm-plugins/commands/open-typeahead-at-cursor.js +7 -2
  5. package/dist/cjs/pm-plugins/commands/update-list-items.js +3 -1
  6. package/dist/cjs/pm-plugins/main.js +54 -0
  7. package/dist/cjs/pm-plugins/reducer.js +7 -1
  8. package/dist/cjs/typeAheadPlugin.js +3 -1
  9. package/dist/cjs/ui/ContentComponent.js +10 -7
  10. package/dist/cjs/ui/TypeAheadList.js +126 -52
  11. package/dist/cjs/ui/TypeAheadMenu.js +3 -0
  12. package/dist/cjs/ui/TypeAheadPopup.js +3 -0
  13. package/dist/cjs/ui/WrapperTypeAhead.js +3 -1
  14. package/dist/cjs/ui/hooks/build-sectioned-result.js +131 -0
  15. package/dist/cjs/ui/hooks/use-load-items.js +12 -4
  16. package/dist/es2019/entry-points/commands.js +2 -0
  17. package/dist/es2019/pm-plugins/commands/open-typeahead-at-cursor.js +7 -2
  18. package/dist/es2019/pm-plugins/commands/update-list-items.js +3 -2
  19. package/dist/es2019/pm-plugins/main.js +54 -0
  20. package/dist/es2019/pm-plugins/reducer.js +6 -1
  21. package/dist/es2019/typeAheadPlugin.js +3 -1
  22. package/dist/es2019/ui/ContentComponent.js +10 -7
  23. package/dist/es2019/ui/TypeAheadList.js +85 -23
  24. package/dist/es2019/ui/TypeAheadMenu.js +2 -0
  25. package/dist/es2019/ui/TypeAheadPopup.js +2 -0
  26. package/dist/es2019/ui/WrapperTypeAhead.js +3 -1
  27. package/dist/es2019/ui/hooks/build-sectioned-result.js +83 -0
  28. package/dist/es2019/ui/hooks/use-load-items.js +13 -4
  29. package/dist/esm/entry-points/commands.js +2 -0
  30. package/dist/esm/pm-plugins/commands/open-typeahead-at-cursor.js +7 -2
  31. package/dist/esm/pm-plugins/commands/update-list-items.js +3 -1
  32. package/dist/esm/pm-plugins/main.js +54 -0
  33. package/dist/esm/pm-plugins/reducer.js +7 -1
  34. package/dist/esm/typeAheadPlugin.js +3 -1
  35. package/dist/esm/ui/ContentComponent.js +10 -7
  36. package/dist/esm/ui/TypeAheadList.js +126 -52
  37. package/dist/esm/ui/TypeAheadMenu.js +3 -0
  38. package/dist/esm/ui/TypeAheadPopup.js +3 -0
  39. package/dist/esm/ui/WrapperTypeAhead.js +3 -1
  40. package/dist/esm/ui/hooks/build-sectioned-result.js +124 -0
  41. package/dist/esm/ui/hooks/use-load-items.js +12 -4
  42. package/dist/types/entry-points/commands.d.ts +1 -0
  43. package/dist/types/pm-plugins/commands/update-list-items.d.ts +2 -1
  44. package/dist/types/types/index.d.ts +8 -0
  45. package/dist/types/ui/TypeAheadList.d.ts +3 -1
  46. package/dist/types/ui/TypeAheadPopup.d.ts +3 -2
  47. package/dist/types/ui/hooks/build-sectioned-result.d.ts +11 -0
  48. package/dist/types/ui/hooks/use-load-items.d.ts +2 -1
  49. package/dist/types-ts4.5/entry-points/commands.d.ts +1 -0
  50. package/dist/types-ts4.5/pm-plugins/commands/update-list-items.d.ts +2 -1
  51. package/dist/types-ts4.5/types/index.d.ts +8 -0
  52. package/dist/types-ts4.5/ui/TypeAheadList.d.ts +3 -1
  53. package/dist/types-ts4.5/ui/TypeAheadPopup.d.ts +3 -2
  54. package/dist/types-ts4.5/ui/hooks/build-sectioned-result.d.ts +11 -0
  55. package/dist/types-ts4.5/ui/hooks/use-load-items.d.ts +2 -1
  56. package/package.json +5 -5
@@ -70,6 +70,8 @@ var TypeAheadPopup = exports.TypeAheadPopup = /*#__PURE__*/_react.default.memo(f
70
70
  popupsBoundariesElement = props.popupsBoundariesElement,
71
71
  popupsScrollableElement = props.popupsScrollableElement,
72
72
  items = props.items,
73
+ _props$sections = props.sections,
74
+ sections = _props$sections === void 0 ? [] : _props$sections,
73
75
  emptyItem = props.emptyItem,
74
76
  errorInfo = props.errorInfo,
75
77
  selectedIndex = props.selectedIndex,
@@ -345,6 +347,7 @@ var TypeAheadPopup = exports.TypeAheadPopup = /*#__PURE__*/_react.default.memo(f
345
347
  triggerHandler: triggerHandler
346
348
  }), (0, _react2.jsx)(_TypeAheadList.TypeAheadList, {
347
349
  items: items,
350
+ sections: sections,
348
351
  emptyItem: emptyItem,
349
352
  selectedIndex: selectedIndex
350
353
  // eslint-disable-next-line @atlassian/perf-linting/no-unstable-inline-props -- Ignored via go/ees017 (to be fixed)
@@ -8,6 +8,7 @@ Object.defineProperty(exports, "__esModule", {
8
8
  exports.WrapperTypeAhead = void 0;
9
9
  var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
10
10
  var _react = _interopRequireWildcard(require("react"));
11
+ var _reactIntl = require("react-intl");
11
12
  var _analytics = require("@atlaskit/editor-common/analytics");
12
13
  var _typeAhead = require("@atlaskit/editor-common/type-ahead");
13
14
  var _platformFeatureFlags = require("@atlaskit/platform-feature-flags");
@@ -40,6 +41,7 @@ var WrapperTypeAhead = exports.WrapperTypeAhead = /*#__PURE__*/_react.default.me
40
41
  if ((0, _experiments.editorExperiment)('platform_editor_controls', 'variant1')) {
41
42
  showMoreOptionsButton = !!(triggerHandler !== null && triggerHandler !== void 0 && triggerHandler.getMoreOptionsButtonConfig);
42
43
  }
44
+ var intl = (0, _reactIntl.useIntl)();
43
45
  var _useState = (0, _react.useState)(false),
44
46
  _useState2 = (0, _slicedToArray2.default)(_useState, 2),
45
47
  closed = _useState2[0],
@@ -50,7 +52,7 @@ var WrapperTypeAhead = exports.WrapperTypeAhead = /*#__PURE__*/_react.default.me
50
52
  setQuery = _useState4[1];
51
53
  var queryRef = (0, _react.useRef)(query);
52
54
  var editorViewRef = (0, _react.useRef)(editorView);
53
- var items = (0, _useLoadItems.useLoadItems)(triggerHandler, editorView, query, showMoreOptionsButton, api);
55
+ var items = (0, _useLoadItems.useLoadItems)(triggerHandler, editorView, query, showMoreOptionsButton, api, intl);
54
56
  (0, _react.useEffect)(function () {
55
57
  if (!closed && (0, _platformFeatureFlags.fg)('platform_editor_ease_of_use_metrics')) {
56
58
  var _api$metrics;
@@ -0,0 +1,131 @@
1
+ "use strict";
2
+
3
+ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4
+ Object.defineProperty(exports, "__esModule", {
5
+ value: true
6
+ });
7
+ exports.buildSectionedResult = void 0;
8
+ var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray"));
9
+ var _experiments = require("@atlaskit/tmp-editor-statsig/experiments");
10
+ function _createForOfIteratorHelper(r, e) { var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (!t) { if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) { t && (r = t); var _n = 0, F = function F() {}; return { s: F, n: function n() { return _n >= r.length ? { done: !0 } : { done: !1, value: r[_n++] }; }, e: function e(r) { throw r; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var o, a = !0, u = !1; return { s: function s() { t = t.call(r); }, n: function n() { var r = t.next(); return a = r.done, r; }, e: function e(r) { u = !0, o = r; }, f: function f() { try { a || null == t.return || t.return(); } finally { if (u) throw o; } } }; }
11
+ function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
12
+ function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
13
+ var buildSectionedResult = exports.buildSectionedResult = function buildSectionedResult(_ref) {
14
+ var _triggerHandler$getSe;
15
+ var items = _ref.items,
16
+ triggerHandler = _ref.triggerHandler,
17
+ intl = _ref.intl;
18
+ if (!(0, _experiments.editorExperiment)('platform_editor_agent_mentions', true) || !intl) {
19
+ return {
20
+ items: items,
21
+ sections: []
22
+ };
23
+ }
24
+ var sectionDefinitions = (_triggerHandler$getSe = triggerHandler.getSections) === null || _triggerHandler$getSe === void 0 ? void 0 : _triggerHandler$getSe.call(triggerHandler, {
25
+ intl: intl
26
+ });
27
+ if (!sectionDefinitions || sectionDefinitions.length === 0) {
28
+ return {
29
+ items: items,
30
+ sections: []
31
+ };
32
+ }
33
+
34
+ // Track which item indexes have been claimed by a section, or excluded (matched a section but
35
+ // cut by that section's limit). Items excluded by limit should not appear anywhere in the output.
36
+ var assignedToSection = new Set();
37
+ var excludedByLimit = new Set();
38
+ var groupedBySection = new Map();
39
+ var _iterator = _createForOfIteratorHelper(sectionDefinitions),
40
+ _step;
41
+ try {
42
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
43
+ var section = _step.value;
44
+ var matchingIndexes = [];
45
+ for (var _i = 0; _i < items.length; _i++) {
46
+ if (assignedToSection.has(_i) || excludedByLimit.has(_i)) {
47
+ continue;
48
+ }
49
+ if (section.filter(items[_i])) {
50
+ matchingIndexes.push(_i);
51
+ }
52
+ }
53
+ var acceptedIndexes = section.limit !== undefined ? matchingIndexes.slice(0, section.limit) : matchingIndexes;
54
+ var rejectedByLimit = matchingIndexes.slice(acceptedIndexes.length);
55
+ var _iterator3 = _createForOfIteratorHelper(acceptedIndexes),
56
+ _step3;
57
+ try {
58
+ for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
59
+ var _i2 = _step3.value;
60
+ assignedToSection.add(_i2);
61
+ }
62
+ } catch (err) {
63
+ _iterator3.e(err);
64
+ } finally {
65
+ _iterator3.f();
66
+ }
67
+ var _iterator4 = _createForOfIteratorHelper(rejectedByLimit),
68
+ _step4;
69
+ try {
70
+ for (_iterator4.s(); !(_step4 = _iterator4.n()).done;) {
71
+ var _i3 = _step4.value;
72
+ excludedByLimit.add(_i3);
73
+ }
74
+ } catch (err) {
75
+ _iterator4.e(err);
76
+ } finally {
77
+ _iterator4.f();
78
+ }
79
+ if (acceptedIndexes.length === 0) {
80
+ continue;
81
+ }
82
+ groupedBySection.set(section.id, {
83
+ indexes: acceptedIndexes,
84
+ section: section
85
+ });
86
+ }
87
+ } catch (err) {
88
+ _iterator.e(err);
89
+ } finally {
90
+ _iterator.f();
91
+ }
92
+ var flattenedItems = [];
93
+ var sections = [];
94
+ var _iterator2 = _createForOfIteratorHelper(sectionDefinitions),
95
+ _step2;
96
+ try {
97
+ for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
98
+ var _section = _step2.value;
99
+ var grouped = groupedBySection.get(_section.id);
100
+ if (!grouped) {
101
+ continue;
102
+ }
103
+ var startIndex = flattenedItems.length;
104
+ flattenedItems.push.apply(flattenedItems, (0, _toConsumableArray2.default)(grouped.indexes.map(function (i) {
105
+ return items[i];
106
+ })));
107
+ var endIndex = flattenedItems.length - 1;
108
+ sections.push({
109
+ endIndex: endIndex,
110
+ id: _section.id,
111
+ startIndex: startIndex,
112
+ title: _section.title
113
+ });
114
+ }
115
+
116
+ // Append items not claimed by any section and not excluded by a limit
117
+ } catch (err) {
118
+ _iterator2.e(err);
119
+ } finally {
120
+ _iterator2.f();
121
+ }
122
+ for (var i = 0; i < items.length; i++) {
123
+ if (!assignedToSection.has(i) && !excludedByLimit.has(i)) {
124
+ flattenedItems.push(items[i]);
125
+ }
126
+ }
127
+ return {
128
+ items: flattenedItems,
129
+ sections: sections
130
+ };
131
+ };
@@ -12,8 +12,9 @@ var _experiments = require("@atlaskit/tmp-editor-statsig/experiments");
12
12
  var _clearListError = require("../../pm-plugins/commands/clear-list-error");
13
13
  var _updateListError = require("../../pm-plugins/commands/update-list-error");
14
14
  var _updateListItems = require("../../pm-plugins/commands/update-list-items");
15
+ var _buildSectionedResult2 = require("./build-sectioned-result");
15
16
  var EMPTY_LIST_ITEM = [];
16
- var useLoadItems = exports.useLoadItems = function useLoadItems(triggerHandler, editorView, query, showViewMore, api) {
17
+ var useLoadItems = exports.useLoadItems = function useLoadItems(triggerHandler, editorView, query, showViewMore, api, intl) {
17
18
  var _useState = (0, _react.useState)(EMPTY_LIST_ITEM),
18
19
  _useState2 = (0, _slicedToArray2.default)(_useState, 2),
19
20
  items = _useState2[0],
@@ -44,7 +45,14 @@ var useLoadItems = exports.useLoadItems = function useLoadItems(triggerHandler,
44
45
  var emptyItem = result.length === 0 && (0, _expValEquals.expValEquals)('platform_editor_insert_menu_ai', 'isEnabled', true) ? (_triggerHandler$getEm = triggerHandler.getEmptyItem) === null || _triggerHandler$getEm === void 0 ? void 0 : _triggerHandler$getEm.call(triggerHandler, {
45
46
  editorState: editorView.state
46
47
  }) : undefined;
47
- var list = result.length > 0 ? result : emptyItem ? [emptyItem] : EMPTY_LIST_ITEM;
48
+ var rawList = result.length > 0 ? result : emptyItem ? [emptyItem] : EMPTY_LIST_ITEM;
49
+ var _buildSectionedResult = (0, _buildSectionedResult2.buildSectionedResult)({
50
+ items: rawList,
51
+ triggerHandler: triggerHandler,
52
+ intl: intl !== null && intl !== void 0 ? intl : null
53
+ }),
54
+ list = _buildSectionedResult.items,
55
+ sections = _buildSectionedResult.sections;
48
56
  if (componentIsMounted.current) {
49
57
  setItems(list);
50
58
  }
@@ -54,7 +62,7 @@ var useLoadItems = exports.useLoadItems = function useLoadItems(triggerHandler,
54
62
  title: 'View more'
55
63
  };
56
64
  queueMicrotask(function () {
57
- (0, _updateListItems.updateListItem)(showViewMore ? list.concat(viewMoreItem) : list)(view.state, view.dispatch);
65
+ (0, _updateListItems.updateListItem)(showViewMore ? list.concat(viewMoreItem) : list, sections)(view.state, view.dispatch);
58
66
  });
59
67
  }).catch(function (e) {
60
68
  if ((0, _experiments.editorExperiment)('platform_editor_offline_editing_web', true)) {
@@ -72,7 +80,7 @@ var useLoadItems = exports.useLoadItems = function useLoadItems(triggerHandler,
72
80
  // ignore because EditorView is mutable but we don't want to
73
81
  // call loadItems when it changes, only when the query changes
74
82
  // eslint-disable-next-line react-hooks/exhaustive-deps
75
- }, [triggerHandler, query]);
83
+ }, [triggerHandler, query, intl]);
76
84
  (0, _react.useEffect)(function () {
77
85
  return function () {
78
86
  componentIsMounted.current = false;
@@ -0,0 +1,2 @@
1
+ /* eslint-disable @atlaskit/editor/no-re-export */
2
+ export { openTypeAhead, openTypeAheadAtCursor } from '../pm-plugins/commands/open-typeahead-at-cursor';
@@ -1,5 +1,6 @@
1
1
  import { GapCursorSelection } from '@atlaskit/editor-common/selection';
2
2
  import { NodeSelection, TextSelection } from '@atlaskit/editor-prosemirror/state';
3
+ import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
3
4
  import { ACTIONS } from '../actions';
4
5
  import { pluginKey } from '../key';
5
6
  export const openTypeAhead = props => tr => {
@@ -46,7 +47,7 @@ export const openTypeAheadAtCursor = ({
46
47
  // delete 1 pos before wherever selection is now - that will delete the empty space
47
48
  tr.delete(tr.selection.from - 1, tr.selection.from);
48
49
  } else {
49
- var _selection$$head, _selection$$head$pare, _selection$$head$pare2, _selection$$head$pare3;
50
+ var _selection$$head$pare, _selection$$head$pare2, _selection$$head, _selection$$head$pare3;
50
51
  if (selection instanceof NodeSelection) {
51
52
  if (isInline) {
52
53
  tr.deleteSelection();
@@ -75,7 +76,11 @@ export const openTypeAheadAtCursor = ({
75
76
  // being inserted due to composition by checking if we have the trigger
76
77
  // directly before the typeahead. This should not happen unless it has
77
78
  // been eroneously added because we require whitespace/newline for typeahead.
78
- if (cursorPos >= 2 && !!(selection !== null && selection !== void 0 && (_selection$$head = selection.$head) !== null && _selection$$head !== void 0 && (_selection$$head$pare = _selection$$head.parent) !== null && _selection$$head$pare !== void 0 && _selection$$head$pare.textContent) && (_selection$$head$pare2 = (_selection$$head$pare3 = selection.$head.parent.textContent).endsWith) !== null && _selection$$head$pare2 !== void 0 && _selection$$head$pare2.call(_selection$$head$pare3, triggerHandler.trigger)) {
79
+ // Check if the text ends with the trigger character (or any character matched
80
+ // by customRegex, to support wide-char variants like fullwidth slash /)
81
+ const triggerPattern = expValEquals('platform_editor_wide_slash_trigger', 'isEnabled', true) && triggerHandler.customRegex ? new RegExp(`(${triggerHandler.customRegex})$`, 'u') : null;
82
+ const endsWithTrigger = ((_selection$$head$pare = (_selection$$head$pare2 = selection.$head.parent.textContent).endsWith) === null || _selection$$head$pare === void 0 ? void 0 : _selection$$head$pare.call(_selection$$head$pare2, triggerHandler.trigger)) || triggerPattern && triggerPattern.test(selection.$head.parent.textContent);
83
+ if (cursorPos >= 2 && !!(selection !== null && selection !== void 0 && (_selection$$head = selection.$head) !== null && _selection$$head !== void 0 && (_selection$$head$pare3 = _selection$$head.parent) !== null && _selection$$head$pare3 !== void 0 && _selection$$head$pare3.textContent) && endsWithTrigger) {
79
84
  tr.delete(cursorPos - 1, cursorPos);
80
85
  }
81
86
  }
@@ -1,12 +1,13 @@
1
1
  import { ACTIONS } from '../actions';
2
2
  import { pluginKey as typeAheadPluginKey } from '../key';
3
- export const updateListItem = items => {
3
+ export const updateListItem = (items, sections = []) => {
4
4
  return (state, dispatch) => {
5
5
  const tr = state.tr;
6
6
  tr.setMeta(typeAheadPluginKey, {
7
7
  action: ACTIONS.UPDATE_LIST_ITEMS,
8
8
  params: {
9
- items
9
+ items,
10
+ sections
10
11
  }
11
12
  });
12
13
  if (dispatch) {
@@ -1,9 +1,12 @@
1
1
  import { InsertTypeAheadStep } from '@atlaskit/adf-schema/steps';
2
+ import { INPUT_METHOD } from '@atlaskit/editor-common/analytics';
2
3
  import { SafePlugin } from '@atlaskit/editor-common/safe-plugin';
3
4
  import { closest } from '@atlaskit/editor-common/utils';
4
5
  import { DecorationSet } from '@atlaskit/editor-prosemirror/view';
5
6
  import { fg } from '@atlaskit/platform-feature-flags';
7
+ import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
6
8
  import { ACTIONS } from './actions';
9
+ import { openTypeAheadAtCursor } from './commands/open-typeahead-at-cursor';
7
10
  import { TYPE_AHEAD_DECORATION_DATA_ATTRIBUTE } from './constants';
8
11
  import { factoryDecorations } from './decorations';
9
12
  import { isInsertionTransaction } from './isInsertionTransaction';
@@ -43,6 +46,11 @@ export function createPlugin({
43
46
  typeAheadHandlers,
44
47
  popupMountRef
45
48
  });
49
+
50
+ // Tracks a wide-char trigger handler detected during IME composition (e.g. /).
51
+ // Set by compositionupdate, cleared by compositionend. Used by handleKeyDown
52
+ // to intercept Enter that confirms the composition.
53
+ let pendingWideSlashHandler = null;
46
54
  return new SafePlugin({
47
55
  key: pluginKey,
48
56
  state: {
@@ -53,6 +61,7 @@ export function createPlugin({
53
61
  decorationSet: DecorationSet.empty,
54
62
  decorationElement: null,
55
63
  items: [],
64
+ sections: [],
56
65
  errorInfo: null,
57
66
  selectedIndex: -1,
58
67
  stats: null,
@@ -94,8 +103,53 @@ export function createPlugin({
94
103
  var _pluginKey$getState;
95
104
  return (_pluginKey$getState = pluginKey.getState(state)) === null || _pluginKey$getState === void 0 ? void 0 : _pluginKey$getState.decorationSet;
96
105
  },
106
+ handleKeyDown: (view, event) => {
107
+ // When composing a wide-char trigger (e.g. /), intercept the Enter key
108
+ // that confirms the composition so we can open the typeahead instead.
109
+ if (pendingWideSlashHandler && event.isComposing && (event.key === 'Enter' || event.keyCode === 13)) {
110
+ const handler = pendingWideSlashHandler;
111
+ pendingWideSlashHandler = null;
112
+ // Defer until ProseMirror has flushed the composed text into its state.
113
+ setTimeout(() => {
114
+ const command = openTypeAheadAtCursor({
115
+ triggerHandler: handler,
116
+ inputMethod: INPUT_METHOD.KEYBOARD
117
+ });
118
+ const tr = command({
119
+ tr: view.state.tr
120
+ });
121
+ if (tr) {
122
+ view.dispatch(tr);
123
+ }
124
+ }, 0);
125
+ return true;
126
+ }
127
+ return false;
128
+ },
97
129
  handleDOMEvents: {
130
+ compositionupdate: (view, event) => {
131
+ // When the experiment is on, track whether the current composition
132
+ // exactly matches a wide-char trigger (e.g. / from Japanese keyboard).
133
+ // We can't open the typeahead yet because composition is still active,
134
+ // but we record the matching handler so the next keydown (Enter) can use it.
135
+ if (expValEquals('platform_editor_wide_slash_trigger', 'isEnabled', true)) {
136
+ var _event$data, _typeAheadHandlers$fi;
137
+ const pendingData = (_event$data = event.data) !== null && _event$data !== void 0 ? _event$data : '';
138
+ pendingWideSlashHandler = (_typeAheadHandlers$fi = typeAheadHandlers.find(handler => {
139
+ if (!handler.customRegex) {
140
+ return false;
141
+ }
142
+ // Only match if the entire composition is a trigger character
143
+ const pattern = new RegExp(`^(${handler.customRegex})$`, 'u');
144
+ return pattern.test(pendingData);
145
+ })) !== null && _typeAheadHandlers$fi !== void 0 ? _typeAheadHandlers$fi : null;
146
+ }
147
+ return false;
148
+ },
98
149
  compositionend: (view, event) => {
150
+ // Clear the pending handler when composition ends (cancelled or committed
151
+ // via a non-Enter key like Space, which we don't want to intercept).
152
+ pendingWideSlashHandler = null;
99
153
  return false;
100
154
  },
101
155
  click: (view, event) => {
@@ -66,6 +66,7 @@ export const createReducer = ({
66
66
  inputMethod,
67
67
  selectedIndex: typeof selectedIndex === 'number' ? selectedIndex : -1,
68
68
  items: [],
69
+ sections: [],
69
70
  query: reopenQuery || '',
70
71
  removePrefixTriggerOnCancel
71
72
  };
@@ -85,6 +86,7 @@ export const createReducer = ({
85
86
  stats: null,
86
87
  triggerHandler: undefined,
87
88
  items: [],
89
+ sections: [],
88
90
  removePrefixTriggerOnCancel: undefined
89
91
  };
90
92
  };
@@ -140,11 +142,13 @@ export const createReducer = ({
140
142
  ...currentPluginState,
141
143
  errorInfo,
142
144
  items: [],
145
+ sections: [],
143
146
  selectedIndex: -1
144
147
  };
145
148
  } else if (shouldUpdateListItems) {
146
149
  const {
147
- items
150
+ items,
151
+ sections = []
148
152
  } = params;
149
153
  const {
150
154
  selectedIndex
@@ -152,6 +156,7 @@ export const createReducer = ({
152
156
  return {
153
157
  ...currentPluginState,
154
158
  items,
159
+ sections,
155
160
  selectedIndex: Math.max(selectedIndex >= items.length ? items.length - 1 : selectedIndex, -1)
156
161
  };
157
162
  } else if (shouldUpdateSelectedIndex) {
@@ -208,7 +208,7 @@ export const typeAheadPlugin = ({
208
208
  }];
209
209
  },
210
210
  getSharedState(editorState) {
211
- var _state$decorationSet, _state$decorationElem, _state$items, _state$errorInfo, _state$selectedIndex;
211
+ var _state$decorationSet, _state$decorationElem, _state$items, _state$sections, _state$errorInfo, _state$selectedIndex;
212
212
  if (!editorState) {
213
213
  return {
214
214
  query: '',
@@ -219,6 +219,7 @@ export const typeAheadPlugin = ({
219
219
  decorationElement: null,
220
220
  triggerHandler: undefined,
221
221
  items: [],
222
+ sections: [],
222
223
  errorInfo: null,
223
224
  selectedIndex: 0
224
225
  };
@@ -234,6 +235,7 @@ export const typeAheadPlugin = ({
234
235
  decorationElement: (_state$decorationElem = state === null || state === void 0 ? void 0 : state.decorationElement) !== null && _state$decorationElem !== void 0 ? _state$decorationElem : null,
235
236
  triggerHandler: state === null || state === void 0 ? void 0 : state.triggerHandler,
236
237
  items: (_state$items = state === null || state === void 0 ? void 0 : state.items) !== null && _state$items !== void 0 ? _state$items : [],
238
+ sections: (_state$sections = state === null || state === void 0 ? void 0 : state.sections) !== null && _state$sections !== void 0 ? _state$sections : [],
237
239
  errorInfo: (_state$errorInfo = state === null || state === void 0 ? void 0 : state.errorInfo) !== null && _state$errorInfo !== void 0 ? _state$errorInfo : null,
238
240
  selectedIndex: (_state$selectedIndex = state === null || state === void 0 ? void 0 : state.selectedIndex) !== null && _state$selectedIndex !== void 0 ? _state$selectedIndex : 0
239
241
  };
@@ -9,24 +9,26 @@ export function ContentComponent({
9
9
  const {
10
10
  triggerHandler,
11
11
  items,
12
+ sections,
12
13
  errorInfo,
13
14
  decorationElement,
14
15
  decorationSet,
15
16
  query,
16
17
  selectedIndex
17
18
  } = useSharedPluginStateWithSelector(api, ['typeAhead'], states => {
18
- var _states$typeAheadStat, _states$typeAheadStat2, _states$typeAheadStat3, _states$typeAheadStat4, _states$typeAheadStat5, _states$typeAheadStat6, _states$typeAheadStat7;
19
+ var _states$typeAheadStat, _states$typeAheadStat2, _states$typeAheadStat3, _states$typeAheadStat4, _states$typeAheadStat5, _states$typeAheadStat6, _states$typeAheadStat7, _states$typeAheadStat8;
19
20
  return {
20
21
  triggerHandler: (_states$typeAheadStat = states.typeAheadState) === null || _states$typeAheadStat === void 0 ? void 0 : _states$typeAheadStat.triggerHandler,
21
22
  items: (_states$typeAheadStat2 = states.typeAheadState) === null || _states$typeAheadStat2 === void 0 ? void 0 : _states$typeAheadStat2.items,
22
- errorInfo: (_states$typeAheadStat3 = states.typeAheadState) === null || _states$typeAheadStat3 === void 0 ? void 0 : _states$typeAheadStat3.errorInfo,
23
- decorationElement: (_states$typeAheadStat4 = states.typeAheadState) === null || _states$typeAheadStat4 === void 0 ? void 0 : _states$typeAheadStat4.decorationElement,
24
- decorationSet: (_states$typeAheadStat5 = states.typeAheadState) === null || _states$typeAheadStat5 === void 0 ? void 0 : _states$typeAheadStat5.decorationSet,
25
- query: (_states$typeAheadStat6 = states.typeAheadState) === null || _states$typeAheadStat6 === void 0 ? void 0 : _states$typeAheadStat6.query,
26
- selectedIndex: (_states$typeAheadStat7 = states.typeAheadState) === null || _states$typeAheadStat7 === void 0 ? void 0 : _states$typeAheadStat7.selectedIndex
23
+ sections: (_states$typeAheadStat3 = states.typeAheadState) === null || _states$typeAheadStat3 === void 0 ? void 0 : _states$typeAheadStat3.sections,
24
+ errorInfo: (_states$typeAheadStat4 = states.typeAheadState) === null || _states$typeAheadStat4 === void 0 ? void 0 : _states$typeAheadStat4.errorInfo,
25
+ decorationElement: (_states$typeAheadStat5 = states.typeAheadState) === null || _states$typeAheadStat5 === void 0 ? void 0 : _states$typeAheadStat5.decorationElement,
26
+ decorationSet: (_states$typeAheadStat6 = states.typeAheadState) === null || _states$typeAheadStat6 === void 0 ? void 0 : _states$typeAheadStat6.decorationSet,
27
+ query: (_states$typeAheadStat7 = states.typeAheadState) === null || _states$typeAheadStat7 === void 0 ? void 0 : _states$typeAheadStat7.query,
28
+ selectedIndex: (_states$typeAheadStat8 = states.typeAheadState) === null || _states$typeAheadStat8 === void 0 ? void 0 : _states$typeAheadStat8.selectedIndex
27
29
  };
28
30
  });
29
- if (items === undefined || decorationSet === undefined || errorInfo === undefined || decorationElement === undefined || query === undefined || selectedIndex === undefined) {
31
+ if (items === undefined || sections === undefined || decorationSet === undefined || errorInfo === undefined || decorationElement === undefined || query === undefined || selectedIndex === undefined) {
30
32
  return null;
31
33
  }
32
34
  return /*#__PURE__*/React.createElement(TypeAheadMenu, {
@@ -37,6 +39,7 @@ export function ContentComponent({
37
39
  typeAheadState: {
38
40
  triggerHandler,
39
41
  items,
42
+ sections,
40
43
  errorInfo,
41
44
  decorationElement,
42
45
  decorationSet,