@atlaskit/editor-plugin-quick-insert 29.0.4 → 30.0.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 (87) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/dist/cjs/quickInsertPlugin.js +10 -5
  3. package/dist/cjs/ui/QuickInsertRecommendedSlot.js +11 -8
  4. package/dist/cjs/ui/RegistryElementBrowser/RegistryElementBrowser.js +31 -7
  5. package/dist/cjs/ui/RegistryElementBrowser/RegistryElementBrowserSearchResults.js +13 -7
  6. package/dist/cjs/ui/RegistryElementBrowser/model.js +31 -18
  7. package/dist/cjs/ui/getQuickInsertComponents.js +28 -16
  8. package/dist/cjs/ui/getRecommendedComponents.js +2 -2
  9. package/dist/cjs/ui/registered-menu/QuickInsertEmptyState.compiled.css +11 -0
  10. package/dist/cjs/ui/registered-menu/QuickInsertEmptyState.js +61 -0
  11. package/dist/cjs/ui/registered-menu/QuickInsertMenuRoot.js +220 -0
  12. package/dist/cjs/ui/registered-menu/QuickInsertMenuSection.js +23 -0
  13. package/dist/cjs/ui/registered-menu/QuickInsertSearchResultsSection.js +19 -0
  14. package/dist/cjs/ui/registered-menu/RegisteredQuickInsertMenuItem.js +42 -0
  15. package/dist/cjs/ui/registered-menu/ToolbarQuickInsertMenu.compiled.css +11 -0
  16. package/dist/cjs/ui/registered-menu/ToolbarQuickInsertMenu.js +128 -0
  17. package/dist/cjs/ui/registered-menu/TypeAheadQuickInsertMenu.compiled.css +3 -0
  18. package/dist/cjs/ui/registered-menu/TypeAheadQuickInsertMenu.js +90 -0
  19. package/dist/cjs/ui/registered-menu/assets/no-results.js +36 -0
  20. package/dist/cjs/ui/registered-menu/get-quick-insert-menu-model.js +254 -0
  21. package/dist/cjs/ui/registered-menu/getQuickInsertMenuItemId.js +9 -0
  22. package/dist/cjs/ui/registered-menu/selectQuickInsertCategoryItems.js +27 -0
  23. package/dist/cjs/ui/registered-menu/useQuickInsertSearchAnalytics.js +100 -0
  24. package/dist/es2019/quickInsertPlugin.js +10 -5
  25. package/dist/es2019/ui/QuickInsertRecommendedSlot.js +10 -5
  26. package/dist/es2019/ui/RegistryElementBrowser/RegistryElementBrowser.js +19 -2
  27. package/dist/es2019/ui/RegistryElementBrowser/RegistryElementBrowserSearchResults.js +12 -7
  28. package/dist/es2019/ui/RegistryElementBrowser/model.js +27 -16
  29. package/dist/es2019/ui/getQuickInsertComponents.js +24 -10
  30. package/dist/es2019/ui/getRecommendedComponents.js +2 -2
  31. package/dist/es2019/ui/registered-menu/QuickInsertEmptyState.compiled.css +11 -0
  32. package/dist/es2019/ui/registered-menu/QuickInsertEmptyState.js +47 -0
  33. package/dist/es2019/ui/registered-menu/QuickInsertMenuRoot.js +191 -0
  34. package/dist/es2019/ui/registered-menu/QuickInsertMenuSection.js +15 -0
  35. package/dist/es2019/ui/registered-menu/QuickInsertSearchResultsSection.js +14 -0
  36. package/dist/es2019/ui/registered-menu/RegisteredQuickInsertMenuItem.js +34 -0
  37. package/dist/es2019/ui/registered-menu/ToolbarQuickInsertMenu.compiled.css +11 -0
  38. package/dist/es2019/ui/registered-menu/ToolbarQuickInsertMenu.js +116 -0
  39. package/dist/es2019/ui/registered-menu/TypeAheadQuickInsertMenu.compiled.css +3 -0
  40. package/dist/es2019/ui/registered-menu/TypeAheadQuickInsertMenu.js +78 -0
  41. package/dist/es2019/ui/registered-menu/assets/no-results.js +30 -0
  42. package/dist/es2019/ui/registered-menu/get-quick-insert-menu-model.js +191 -0
  43. package/dist/es2019/ui/registered-menu/getQuickInsertMenuItemId.js +1 -0
  44. package/dist/es2019/ui/registered-menu/selectQuickInsertCategoryItems.js +19 -0
  45. package/dist/es2019/ui/registered-menu/useQuickInsertSearchAnalytics.js +91 -0
  46. package/dist/esm/quickInsertPlugin.js +10 -5
  47. package/dist/esm/ui/QuickInsertRecommendedSlot.js +11 -8
  48. package/dist/esm/ui/RegistryElementBrowser/RegistryElementBrowser.js +30 -6
  49. package/dist/esm/ui/RegistryElementBrowser/RegistryElementBrowserSearchResults.js +12 -7
  50. package/dist/esm/ui/RegistryElementBrowser/model.js +31 -18
  51. package/dist/esm/ui/getQuickInsertComponents.js +27 -15
  52. package/dist/esm/ui/getRecommendedComponents.js +2 -2
  53. package/dist/esm/ui/registered-menu/QuickInsertEmptyState.compiled.css +11 -0
  54. package/dist/esm/ui/registered-menu/QuickInsertEmptyState.js +50 -0
  55. package/dist/esm/ui/registered-menu/QuickInsertMenuRoot.js +211 -0
  56. package/dist/esm/ui/registered-menu/QuickInsertMenuSection.js +16 -0
  57. package/dist/esm/ui/registered-menu/QuickInsertSearchResultsSection.js +12 -0
  58. package/dist/esm/ui/registered-menu/RegisteredQuickInsertMenuItem.js +34 -0
  59. package/dist/esm/ui/registered-menu/ToolbarQuickInsertMenu.compiled.css +11 -0
  60. package/dist/esm/ui/registered-menu/ToolbarQuickInsertMenu.js +120 -0
  61. package/dist/esm/ui/registered-menu/TypeAheadQuickInsertMenu.compiled.css +3 -0
  62. package/dist/esm/ui/registered-menu/TypeAheadQuickInsertMenu.js +82 -0
  63. package/dist/esm/ui/registered-menu/assets/no-results.js +29 -0
  64. package/dist/esm/ui/registered-menu/get-quick-insert-menu-model.js +247 -0
  65. package/dist/esm/ui/registered-menu/getQuickInsertMenuItemId.js +3 -0
  66. package/dist/esm/ui/registered-menu/selectQuickInsertCategoryItems.js +21 -0
  67. package/dist/esm/ui/registered-menu/useQuickInsertSearchAnalytics.js +93 -0
  68. package/dist/types/quickInsertPluginType.d.ts +1 -1
  69. package/dist/types/ui/QuickInsertRecommendedSlot.d.ts +3 -3
  70. package/dist/types/ui/RegistryElementBrowser/RegistryElementBrowserSearchResults.d.ts +5 -2
  71. package/dist/types/ui/RegistryElementBrowser/model.d.ts +3 -2
  72. package/dist/types/ui/getQuickInsertComponents.d.ts +8 -2
  73. package/dist/types/ui/registered-menu/QuickInsertEmptyState.d.ts +7 -0
  74. package/dist/types/ui/registered-menu/QuickInsertMenuRoot.d.ts +11 -0
  75. package/dist/types/ui/registered-menu/QuickInsertMenuSection.d.ts +4 -0
  76. package/dist/types/ui/registered-menu/QuickInsertSearchResultsSection.d.ts +2 -0
  77. package/dist/types/ui/registered-menu/RegisteredQuickInsertMenuItem.d.ts +3 -0
  78. package/dist/types/ui/registered-menu/ToolbarQuickInsertMenu.d.ts +28 -0
  79. package/dist/types/ui/registered-menu/TypeAheadQuickInsertMenu.d.ts +27 -0
  80. package/dist/types/ui/registered-menu/assets/no-results.d.ts +7 -0
  81. package/dist/types/ui/registered-menu/get-quick-insert-menu-model.d.ts +19 -0
  82. package/dist/types/ui/registered-menu/getQuickInsertMenuItemId.d.ts +1 -0
  83. package/dist/types/ui/registered-menu/selectQuickInsertCategoryItems.d.ts +7 -0
  84. package/dist/types/ui/registered-menu/useQuickInsertSearchAnalytics.d.ts +12 -0
  85. package/package.json +15 -12
  86. package/registered-menu/model/package.json +10 -0
  87. package/registered-menu/search-analytics/package.json +10 -0
@@ -0,0 +1,78 @@
1
+ /* TypeAheadQuickInsertMenu.tsx generated by @compiled/babel-plugin v3.0.2 */
2
+ import "./TypeAheadQuickInsertMenu.compiled.css";
3
+ import { ax, ix } from "@compiled/react/runtime";
4
+ import React, { useCallback } from 'react';
5
+ import { RegisteredMenuList } from '@atlaskit/editor-common/registered-menu/list';
6
+ import { Box } from '@atlaskit/primitives/compiled/box';
7
+ import { getQuickInsertMenuItemId } from './getQuickInsertMenuItemId';
8
+ import { QuickInsertEmptyState } from './QuickInsertEmptyState';
9
+ const LIST_WIDTH = 320;
10
+ const MENU_FOOTER_HEIGHT = 45;
11
+ const MENU_FOOTER_HEIGHT_WITH_SCROLL_PADDING = 51;
12
+ const styles = {
13
+ footer: "_1i531l7x _15a5nqa1 _uwhke4h9"
14
+ };
15
+ export const TypeAheadQuickInsertMenu = ({
16
+ Item,
17
+ rows,
18
+ activeItemKey,
19
+ itemCount,
20
+ listId,
21
+ listLabel,
22
+ maxHeight,
23
+ model,
24
+ onItemFocus,
25
+ onItemBlur,
26
+ onItemHover,
27
+ onItemLeave,
28
+ onItemVisibilityChange,
29
+ onRenderedItemsChange,
30
+ query = '',
31
+ selectedItemIndex,
32
+ hasScrollPadding = false
33
+ }) => {
34
+ const footerItemIndex = itemCount - 1;
35
+ const onFooterItemHover = useCallback(() => onItemHover(footerItemIndex), [footerItemIndex, onItemHover]);
36
+ const getItemId = useCallback(({
37
+ itemIndex
38
+ }) => getQuickInsertMenuItemId(listId, itemIndex), [listId]);
39
+ if (!model.root) {
40
+ return null;
41
+ }
42
+ const listMaxHeight = Math.max(0, maxHeight - (model.footer ? hasScrollPadding ? MENU_FOOTER_HEIGHT_WITH_SCROLL_PADDING : MENU_FOOTER_HEIGHT : 0));
43
+ const hasListboxContent = rows.length > 0 || model.footer !== undefined;
44
+ return /*#__PURE__*/React.createElement(Box, {
45
+ "aria-label": hasListboxContent ? listLabel : undefined,
46
+ id: listId,
47
+ role: hasListboxContent ? 'listbox' : undefined
48
+ }, rows.length > 0 ? /*#__PURE__*/React.createElement(RegisteredMenuList, {
49
+ rows: rows,
50
+ Item: Item,
51
+ width: LIST_WIDTH,
52
+ maxHeight: listMaxHeight,
53
+ getItemId: getItemId,
54
+ activeItemKey: activeItemKey,
55
+ selectedItemIndex: selectedItemIndex,
56
+ scrollResetKey: query,
57
+ hasScrollPadding: hasScrollPadding,
58
+ scrollTo: "top-on-reset",
59
+ onItemFocus: onItemFocus,
60
+ onItemBlur: onItemBlur,
61
+ onItemHover: onItemHover,
62
+ onItemLeave: onItemLeave,
63
+ onItemVisibilityChange: onItemVisibilityChange,
64
+ onRenderedItemsChange: onRenderedItemsChange
65
+ }) : query !== '' && /*#__PURE__*/React.createElement(QuickInsertEmptyState, {
66
+ mode: "inline",
67
+ spacing: "spacious"
68
+ }), model.footer && /*#__PURE__*/React.createElement(Box, {
69
+ onMouseMove: onFooterItemHover,
70
+ paddingBlockEnd: hasScrollPadding ? 'space.075' : undefined,
71
+ xcss: styles.footer
72
+ }, /*#__PURE__*/React.createElement(Item, {
73
+ id: getQuickInsertMenuItemId(listId, footerItemIndex),
74
+ isSelected: selectedItemIndex === footerItemIndex,
75
+ isActive: false,
76
+ registration: model.footer
77
+ })));
78
+ };
@@ -0,0 +1,30 @@
1
+ import React from 'react';
2
+ export default function NoResultsIllustration({
3
+ height,
4
+ width
5
+ }) {
6
+ return /*#__PURE__*/React.createElement("svg", {
7
+ width: width,
8
+ height: height,
9
+ viewBox: "0 0 57 63",
10
+ fill: "none",
11
+ xmlns: "http://www.w3.org/2000/svg",
12
+ "aria-hidden": "true",
13
+ focusable: "false"
14
+ }, /*#__PURE__*/React.createElement("path", {
15
+ fill: "#dddee1",
16
+ d: "M23.5 49.223c-5.196 0-10.166-1.955-14.442-5.814a30 30 0 0 1-3.545-3.85 29.6 29.6 0 0 1-2.868-4.456c-4.56-8.775-3.179-18.124 3.79-25.647a32.6 32.6 0 0 1 3.942-3.593 32 32 0 0 1 4.287-2.81c9.017-4.89 18.424-3.869 25.803 2.8a30.3 30.3 0 0 1 3.59 3.897 29.7 29.7 0 0 1 2.837 4.396C51.5 22.96 50.12 32.324 43.106 39.83a31.5 31.5 0 0 1-3.797 3.442 33 33 0 0 1-4.468 2.912c-3.777 2.03-7.62 3.036-11.341 3.036zm2.478-44.539c-2.957 0-6.02.824-9.082 2.489a28 28 0 0 0-3.658 2.396 27.5 27.5 0 0 0-3.37 3.07C4.24 18.711 3.15 25.92 6.798 32.941a25 25 0 0 0 2.42 3.75 25 25 0 0 0 2.981 3.24c5.866 5.299 13.119 6.054 20.427 2.13a27.4 27.4 0 0 0 3.818-2.492 27.5 27.5 0 0 0 3.24-2.936c5.658-6.054 6.741-13.27 3.06-20.318a25 25 0 0 0-2.39-3.698 25.3 25.3 0 0 0-3.021-3.289c-3.418-3.086-7.288-4.645-11.348-4.645z"
17
+ }), /*#__PURE__*/React.createElement("path", {
18
+ fill: "#dddee1",
19
+ d: "M42.822 42.291c-1.21-1.685-2.509-1.675-3.777-.694-1.19.92-1.507 2.164-.24 3.801l1.45 1.873 4.017-3.107z"
20
+ }), /*#__PURE__*/React.createElement("path", {
21
+ fill: "#dddee1",
22
+ d: "m45.28 43.385-6.03 4.663 11.332 14.657 6.03-4.663z"
23
+ }), /*#__PURE__*/React.createElement("path", {
24
+ fill: "#b7b9be",
25
+ d: "m44.31 44.134-4.091 3.163 3.843 4.97 4.091-3.163z"
26
+ }), /*#__PURE__*/React.createElement("path", {
27
+ fill: "#dddee1",
28
+ d: "M26.834 30.328h-3.42v-.217c-.116-2.432 1.264-5.066 3.227-6.143l.037-.02q.08-.042.154-.093c1.071-.69 1.68-1.88 1.643-3.189-.037-1.298-.707-2.439-1.791-3.052a4.5 4.5 0 0 0-2.21-.592c-2.315 0-4.198 1.897-4.198 4.229h-3.42c0-4.23 3.417-7.672 7.618-7.672a7.9 7.9 0 0 1 3.884 1.033c2.14 1.211 3.462 3.437 3.534 5.955.073 2.53-1.125 4.84-3.203 6.18q-.211.139-.424.251c-.815.458-1.497 1.868-1.434 2.97zM24.795 32.303a3.4 3.4 0 0 1 1.096-.004c1.115.183 2.021.965 2.241 2.182l.002.01.002.009a3.4 3.4 0 0 1-.002 1.103v.004c-.216 1.23-1.136 2.009-2.247 2.19a3.417 3.417 0 0 1-1.085-.003c-1.117-.18-2.027-.961-2.249-2.179l-.002-.01-.001-.01a3.3 3.3 0 0 1 0-1.095l.002-.01.001-.01c.223-1.223 1.137-1.992 2.24-2.177z"
29
+ }));
30
+ }
@@ -0,0 +1,191 @@
1
+ import { ASK_ROVO_MENU_ITEM, MENU, SEARCH_RESULTS_SECTION } from '@atlaskit/editor-common/quick-insert/keys';
2
+ import { applyRegisteredItemRanks } from '@atlaskit/editor-common/registered-menu/item-rank';
3
+ import { isMenuFooterSectionKey } from '@atlaskit/editor-common/type-ahead-is-menu-footer-section-key';
4
+ import { isSectionOverflowItemKey } from '@atlaskit/editor-common/type-ahead-is-section-overflow-item-key';
5
+ import { getComponentIdentity, resolveSurface, willComponentRender } from '@atlaskit/editor-ui-control-model/surface-renderer';
6
+ import { QuickInsertSearchResultsSection } from './QuickInsertSearchResultsSection';
7
+ const SEARCH_TOP_MATCH_LIMIT = 5;
8
+ const SEARCH_RESULTS_HEADING = {
9
+ key: SEARCH_RESULTS_SECTION.key,
10
+ type: SEARCH_RESULTS_SECTION.type,
11
+ component: QuickInsertSearchResultsSection,
12
+ parents: [{
13
+ key: MENU.key,
14
+ type: MENU.type,
15
+ rank: 0
16
+ }]
17
+ };
18
+ const getParentRank = (component, parentKey) => {
19
+ var _component$parents$fi, _component$parents, _component$parents$fi2;
20
+ return (_component$parents$fi = (_component$parents = component.parents) === null || _component$parents === void 0 ? void 0 : (_component$parents$fi2 = _component$parents.find(parent => parent.key === parentKey)) === null || _component$parents$fi2 === void 0 ? void 0 : _component$parents$fi2.rank) !== null && _component$parents$fi !== void 0 ? _component$parents$fi : Number.MAX_SAFE_INTEGER;
21
+ };
22
+ const compareMatchedItems = (a, b) => a.sectionRank - b.sectionRank || a.score - b.score || a.itemRank - b.itemRank || a.sourceIndex - b.sourceIndex || a.identity.localeCompare(b.identity);
23
+ const compareSearchResults = (a, b) => a.score - b.score || a.sectionRank - b.sectionRank || a.itemRank - b.itemRank || a.sourceIndex - b.sourceIndex || a.identity.localeCompare(b.identity);
24
+ const comparePrioritizedSearchResults = (a, b) => {
25
+ var _b$priorityGroup, _a$priorityGroup;
26
+ return ((_b$priorityGroup = b.priorityGroup) !== null && _b$priorityGroup !== void 0 ? _b$priorityGroup : 0) - ((_a$priorityGroup = a.priorityGroup) !== null && _a$priorityGroup !== void 0 ? _a$priorityGroup : 0) || compareSearchResults(a, b);
27
+ };
28
+ const getSearchResults = matches => {
29
+ const topMatches = Array.from(matches).sort(compareSearchResults).slice(0, SEARCH_TOP_MATCH_LIMIT);
30
+ const topMatchIdentities = new Set(topMatches.map(({
31
+ identity
32
+ }) => identity));
33
+ const tailMatches = matches.filter(({
34
+ identity
35
+ }) => !topMatchIdentities.has(identity));
36
+ return [...topMatches, ...tailMatches].map(({
37
+ item
38
+ }) => item);
39
+ };
40
+ const getRenderedMenuItems = (section, childrenMap, surfaceContext) => {
41
+ var _childrenMap$get;
42
+ return ((_childrenMap$get = childrenMap.get(getComponentIdentity(section))) !== null && _childrenMap$get !== void 0 ? _childrenMap$get : []).filter(child => child.type === 'menu-item' && willComponentRender(child, childrenMap, surfaceContext));
43
+ };
44
+ const buildBrowseSections = ({
45
+ childrenMap,
46
+ contentSections,
47
+ selectCategoryItems,
48
+ surfaceContext
49
+ }) => {
50
+ const sections = [];
51
+ for (const section of contentSections) {
52
+ const children = getRenderedMenuItems(section, childrenMap, surfaceContext);
53
+ const overflowItems = children.filter(child => isSectionOverflowItemKey(child.key));
54
+ const viewMoreItem = overflowItems[overflowItems.length - 1];
55
+ const items = children.filter(child => !isSectionOverflowItemKey(child.key));
56
+ if (items.length === 0) {
57
+ continue;
58
+ }
59
+ const selectedItems = viewMoreItem !== undefined && selectCategoryItems !== undefined ? selectCategoryItems(items) : items;
60
+ if (selectedItems.length < items.length && viewMoreItem !== undefined) {
61
+ sections.push([section, ...selectedItems, viewMoreItem]);
62
+ } else {
63
+ sections.push([section, ...selectedItems]);
64
+ }
65
+ }
66
+ return {
67
+ fallbackItems: [],
68
+ sections
69
+ };
70
+ };
71
+ const buildSearchSections = ({
72
+ childrenMap,
73
+ components,
74
+ contentSections,
75
+ formatMessage,
76
+ query,
77
+ root,
78
+ surfaceContext
79
+ }) => {
80
+ const matches = new Map();
81
+ const sourceIndexByComponent = new Map(components.map((component, index) => [component, index]));
82
+ for (const section of contentSections) {
83
+ for (const item of getRenderedMenuItems(section, childrenMap, surfaceContext)) {
84
+ var _item$match, _match$score, _sourceIndexByCompone;
85
+ if (isSectionOverflowItemKey(item.key)) {
86
+ continue;
87
+ }
88
+ const match = (_item$match = item.match) === null || _item$match === void 0 ? void 0 : _item$match.call(item, {
89
+ formatMessage,
90
+ itemKey: item.key,
91
+ query
92
+ });
93
+ if (item.match && match === null) {
94
+ continue;
95
+ }
96
+ const candidate = {
97
+ identity: `${item.type}:${item.key}`,
98
+ item,
99
+ itemRank: getParentRank(item, section.key),
100
+ priorityGroup: (match === null || match === void 0 ? void 0 : match.priorityGroup) !== undefined && Number.isFinite(match.priorityGroup) ? match.priorityGroup : undefined,
101
+ score: Math.min(1, Math.max(0, (_match$score = match === null || match === void 0 ? void 0 : match.score) !== null && _match$score !== void 0 ? _match$score : 0)),
102
+ sourceIndex: (_sourceIndexByCompone = sourceIndexByComponent.get(item)) !== null && _sourceIndexByCompone !== void 0 ? _sourceIndexByCompone : Number.MAX_SAFE_INTEGER,
103
+ sectionRank: getParentRank(section, root.key)
104
+ };
105
+ const existing = matches.get(candidate.identity);
106
+ if (!existing || compareMatchedItems(candidate, existing) < 0) {
107
+ matches.set(candidate.identity, candidate);
108
+ }
109
+ }
110
+ }
111
+ const uniqueMatches = Array.from(matches.values()).sort(compareMatchedItems);
112
+ const prioritizedMatches = uniqueMatches.filter(({
113
+ priorityGroup
114
+ }) => priorityGroup !== undefined);
115
+ const unprioritizedMatches = uniqueMatches.filter(({
116
+ priorityGroup
117
+ }) => priorityGroup === undefined);
118
+ const orderedSearchResults = [...prioritizedMatches.sort(comparePrioritizedSearchResults).map(({
119
+ item
120
+ }) => item), ...getSearchResults(unprioritizedMatches)];
121
+ if (orderedSearchResults.length > 0) {
122
+ return {
123
+ fallbackItems: [],
124
+ sections: [[SEARCH_RESULTS_HEADING, ...orderedSearchResults]]
125
+ };
126
+ }
127
+ const fallbackItem = contentSections.flatMap(section => {
128
+ var _childrenMap$get2;
129
+ return (_childrenMap$get2 = childrenMap.get(getComponentIdentity(section))) !== null && _childrenMap$get2 !== void 0 ? _childrenMap$get2 : [];
130
+ }).find(component => component.type === ASK_ROVO_MENU_ITEM.type && component.key === ASK_ROVO_MENU_ITEM.key && willComponentRender(component, childrenMap, surfaceContext));
131
+ return {
132
+ fallbackItems: fallbackItem ? [fallbackItem] : [],
133
+ sections: []
134
+ };
135
+ };
136
+
137
+ /**
138
+ * Builds the registered quick-insert menu model. An empty query browses every
139
+ * category; a non-empty query returns ranked matches. The footer is shared by both.
140
+ */
141
+ export const getQuickInsertMenuModel = ({
142
+ components,
143
+ formatMessage,
144
+ getItemRank,
145
+ query,
146
+ rootComponent,
147
+ selectCategoryItems,
148
+ surfaceContext
149
+ }) => {
150
+ const rankedComponents = applyRegisteredItemRanks(components, getItemRank);
151
+ const {
152
+ childrenMap,
153
+ root,
154
+ topLevelChildren = []
155
+ } = resolveSurface(rankedComponents, rootComponent);
156
+ if (!root || !willComponentRender(root, childrenMap, surfaceContext)) {
157
+ return {
158
+ fallbackItems: [],
159
+ footer: undefined,
160
+ root: undefined,
161
+ sections: []
162
+ };
163
+ }
164
+ const renderedSections = topLevelChildren.filter(section => section.type === 'menu-section' && willComponentRender(section, childrenMap, surfaceContext));
165
+ const footerSection = renderedSections.find(({
166
+ key
167
+ }) => isMenuFooterSectionKey(key));
168
+ const contentSections = renderedSections.filter(({
169
+ key
170
+ }) => !isMenuFooterSectionKey(key));
171
+ const content = query === '' ? buildBrowseSections({
172
+ childrenMap,
173
+ contentSections,
174
+ selectCategoryItems,
175
+ surfaceContext
176
+ }) : buildSearchSections({
177
+ childrenMap,
178
+ components: rankedComponents,
179
+ contentSections,
180
+ formatMessage,
181
+ query,
182
+ root,
183
+ surfaceContext
184
+ });
185
+ const footer = footerSection ? getRenderedMenuItems(footerSection, childrenMap, surfaceContext)[0] : undefined;
186
+ return {
187
+ ...content,
188
+ footer,
189
+ root
190
+ };
191
+ };
@@ -0,0 +1 @@
1
+ export const getQuickInsertMenuItemId = (listId, itemIndex) => `${listId}-${itemIndex}`;
@@ -0,0 +1,19 @@
1
+ import { quickInsertProviderMenuItemKey } from '@atlaskit/editor-common/quick-insert/get-provider-menu-item-key';
2
+ const DEFAULT_ITEM_LIMIT = 30;
3
+ const ECOSYSTEM_ITEM_LIMIT = 10;
4
+ const isEcosystemQuickInsertItem = item => quickInsertProviderMenuItemKey.isEcosystem(item.key);
5
+
6
+ /**
7
+ * Keeps the initial native and ecosystem results while ensuring ecosystem registrations
8
+ * remain discoverable beyond the default category item limit.
9
+ */
10
+ export const selectQuickInsertCategoryItems = items => {
11
+ let ecosystemItemCount = 0;
12
+ return items.filter((item, index) => {
13
+ const isEcosystemItem = isEcosystemQuickInsertItem(item);
14
+ if (isEcosystemItem) {
15
+ ecosystemItemCount++;
16
+ }
17
+ return index < DEFAULT_ITEM_LIMIT || isEcosystemItem && ecosystemItemCount <= ECOSYSTEM_ITEM_LIMIT;
18
+ });
19
+ };
@@ -0,0 +1,91 @@
1
+ import { useCallback, useEffect, useRef } from 'react';
2
+ import { ACTION, ACTION_SUBJECT, EVENT_TYPE } from '@atlaskit/editor-common/analytics/types/enums';
3
+ const SEARCH_DEBOUNCE_MS = 1000;
4
+ export const useQuickInsertSearchAnalytics = ({
5
+ query,
6
+ resultCount,
7
+ surface,
8
+ category,
9
+ isOpen = true,
10
+ fireAnalyticsEvent
11
+ }) => {
12
+ const latestOptions = useRef({
13
+ query,
14
+ resultCount,
15
+ surface,
16
+ category,
17
+ fireAnalyticsEvent
18
+ });
19
+ latestOptions.current = {
20
+ query,
21
+ resultCount,
22
+ surface,
23
+ category,
24
+ fireAnalyticsEvent
25
+ };
26
+ const pendingSearch = useRef(null);
27
+ const effectVersion = useRef(0);
28
+ const flush = useCallback(() => {
29
+ const pending = pendingSearch.current;
30
+ if (!pending) {
31
+ return;
32
+ }
33
+ clearTimeout(pending.timer);
34
+ pendingSearch.current = null;
35
+ const current = latestOptions.current;
36
+ if (current.query.trim() !== pending.query || !current.fireAnalyticsEvent) {
37
+ return;
38
+ }
39
+ const attributes = {
40
+ ...(current.surface === 'element-browser' && current.category !== undefined ? {
41
+ category: current.category
42
+ } : {}),
43
+ isEmpty: current.resultCount === 0,
44
+ queryLength: current.query.trim().length,
45
+ resultCount: current.resultCount,
46
+ surface: current.surface
47
+ };
48
+ const payload = {
49
+ action: ACTION.SEARCHED,
50
+ actionSubject: ACTION_SUBJECT.QUICK_INSERT,
51
+ eventType: EVENT_TYPE.TRACK,
52
+ attributes
53
+ };
54
+ current.fireAnalyticsEvent(payload);
55
+ }, []);
56
+ useEffect(() => {
57
+ const currentVersion = ++effectVersion.current;
58
+ if (!isOpen) {
59
+ flush();
60
+ return;
61
+ }
62
+ const trimmedQuery = query.trim();
63
+ if (!trimmedQuery) {
64
+ if (pendingSearch.current) {
65
+ clearTimeout(pendingSearch.current.timer);
66
+ pendingSearch.current = null;
67
+ }
68
+ return;
69
+ }
70
+ if (pendingSearch.current) {
71
+ clearTimeout(pendingSearch.current.timer);
72
+ }
73
+ const timer = setTimeout(flush, SEARCH_DEBOUNCE_MS);
74
+ pendingSearch.current = {
75
+ query: trimmedQuery,
76
+ timer
77
+ };
78
+ return () => {
79
+ if (pendingSearch.current) {
80
+ clearTimeout(pendingSearch.current.timer);
81
+ }
82
+ Promise.resolve().then(() => {
83
+ // oxlint-disable-next-line react-hooks/exhaustive-deps -- The live effect version distinguishes dependency cleanup from a real unmount.
84
+ if (effectVersion.current === currentVersion) {
85
+ flush();
86
+ }
87
+ });
88
+ };
89
+ }, [query, surface, category, isOpen, flush]);
90
+ return flush;
91
+ };
@@ -8,6 +8,7 @@ import React from 'react';
8
8
  import { useIntl } from 'react-intl';
9
9
  import { isSSR } from '@atlaskit/editor-common/is-ssr';
10
10
  import { toolbarInsertBlockMessages as messages } from '@atlaskit/editor-common/messages/insert-block';
11
+ import { MENU } from '@atlaskit/editor-common/quick-insert/keys';
11
12
  import { createQuickInsertRankResolver } from '@atlaskit/editor-common/quick-insert/quick-insert-rank';
12
13
  import { memoProcessQuickInsertItems } from '@atlaskit/editor-common/quick-insert/utils';
13
14
  import { SafePlugin } from '@atlaskit/editor-common/safe-plugin';
@@ -37,6 +38,7 @@ export var quickInsertPlugin = function quickInsertPlugin(_ref) {
37
38
  var getItemRank = createQuickInsertRankResolver(options === null || options === void 0 ? void 0 : options.getItemRank);
38
39
  var typeAhead = {
39
40
  id: TypeAheadAvailableNodes.QUICK_INSERT,
41
+ registeredMenuKey: isExperimentEnabled('platform_editor_slash_command') ? MENU.key : undefined,
40
42
  getRegisteredItemRank: getItemRank,
41
43
  trigger: '/',
42
44
  // Support fullwidth slash (U+FF0F) used by Japanese/CJK keyboards (e.g. typing / on a Japanese keyboard layout)
@@ -78,12 +80,15 @@ export var quickInsertPlugin = function quickInsertPlugin(_ref) {
78
80
  } : undefined
79
81
  };
80
82
  if (isExperimentEnabled('platform_editor_slash_command')) {
81
- var _api$uiControlRegistr;
83
+ var _api$uiControlRegistr, _api$analytics;
82
84
  api === null || api === void 0 || (_api$uiControlRegistr = api.uiControlRegistry) === null || _api$uiControlRegistr === void 0 || _api$uiControlRegistr.actions.register(getQuickInsertComponents({
83
85
  api: api,
84
- includeElementBrowserItems: (options === null || options === void 0 ? void 0 : options.enableElementBrowser) === true,
86
+ getItemRank: getItemRank,
85
87
  isRecommendedItem: options === null || options === void 0 ? void 0 : options.isRecommendedItem,
86
- maxRecommendedItems: options === null || options === void 0 ? void 0 : options.maxRecommendedItems
88
+ maxRecommendedItems: options === null || options === void 0 ? void 0 : options.maxRecommendedItems,
89
+ emptyStateHandler: options === null || options === void 0 ? void 0 : options.emptyStateHandler,
90
+ fireAnalyticsEvent: api === null || api === void 0 || (_api$analytics = api.analytics) === null || _api$analytics === void 0 ? void 0 : _api$analytics.actions.fireAnalyticsEvent,
91
+ includeElementBrowserItems: (options === null || options === void 0 ? void 0 : options.enableElementBrowser) === true
87
92
  }));
88
93
  }
89
94
  if (isExperimentEnabled('platform_editor_block_control_migration') && (options === null || options === void 0 ? void 0 : options.blockControlButtonEnabled) !== false) {
@@ -142,8 +147,8 @@ export var quickInsertPlugin = function quickInsertPlugin(_ref) {
142
147
  return getQuickInsertOpenExperiencePlugin({
143
148
  refs: refs,
144
149
  dispatchAnalyticsEvent: function dispatchAnalyticsEvent(payload) {
145
- var _api$analytics;
146
- return api === null || api === void 0 || (_api$analytics = api.analytics) === null || _api$analytics === void 0 || (_api$analytics = _api$analytics.actions) === null || _api$analytics === void 0 ? void 0 : _api$analytics.fireAnalyticsEvent(payload);
150
+ var _api$analytics2;
151
+ return api === null || api === void 0 || (_api$analytics2 = api.analytics) === null || _api$analytics2 === void 0 || (_api$analytics2 = _api$analytics2.actions) === null || _api$analytics2 === void 0 ? void 0 : _api$analytics2.fireAnalyticsEvent(payload);
147
152
  }
148
153
  });
149
154
  }
@@ -1,13 +1,16 @@
1
- import _objectWithoutProperties from "@babel/runtime/helpers/objectWithoutProperties";
2
- var _excluded = ["index", "getSnapshot"];
3
1
  import React from 'react';
4
2
  import { useQuickInsertContext } from '@atlaskit/editor-common/quick-insert/use-quick-insert-context';
5
3
  export var QuickInsertRecommendedSlot = function QuickInsertRecommendedSlot(_ref) {
6
- var index = _ref.index,
7
- getSnapshot = _ref.getSnapshot,
8
- props = _objectWithoutProperties(_ref, _excluded);
4
+ var children = _ref.children,
5
+ parents = _ref.parents,
6
+ surfaceContext = _ref.surfaceContext,
7
+ index = _ref.index,
8
+ getSnapshot = _ref.getSnapshot;
9
9
  var _useQuickInsertContex = useQuickInsertContext(),
10
- surfaceContext = _useQuickInsertContex.surfaceContext;
11
- var item = getSnapshot(surfaceContext)[index];
12
- return item !== null && item !== void 0 && item.component ? /*#__PURE__*/React.createElement(item.component, props) : null;
10
+ menuSurfaceContext = _useQuickInsertContex.surfaceContext;
11
+ var item = getSnapshot(menuSurfaceContext)[index];
12
+ return item !== null && item !== void 0 && item.component ? /*#__PURE__*/React.createElement(item.component, {
13
+ parents: parents,
14
+ surfaceContext: surfaceContext
15
+ }, children) : null;
13
16
  };
@@ -11,7 +11,7 @@ import IconButton from '@atlaskit/button/icon/button';
11
11
  // eslint-disable-next-line @atlaskit/platform/no-barrel-entry-imports
12
12
  import { messages } from '@atlaskit/editor-common/quick-insert';
13
13
  import { MENU, RECOMMENDED_SECTION } from '@atlaskit/editor-common/quick-insert/keys';
14
- import { useQuickInsertSearchAnalytics } from '@atlaskit/editor-common/quick-insert/use-search-analytics';
14
+ import { createRegisteredMenuSurfaceContext } from '@atlaskit/editor-common/registered-menu/surface-context';
15
15
  import { getMenuFooterSectionKey } from '@atlaskit/editor-common/type-ahead-get-menu-footer-section-key';
16
16
  import { getSectionOverflowItemKey } from '@atlaskit/editor-common/type-ahead-get-section-overflow-item-key';
17
17
  import Heading from '@atlaskit/heading/heading';
@@ -24,6 +24,7 @@ import ModalTitle from '@atlaskit/modal-dialog/modal-title';
24
24
  import ModalTransition from '@atlaskit/modal-dialog/modal-transition';
25
25
  import { fg } from '@atlaskit/platform-feature-flags/fg';
26
26
  import Textfield from '@atlaskit/textfield/text-field';
27
+ import { useQuickInsertSearchAnalytics } from '../registered-menu/useQuickInsertSearchAnalytics';
27
28
  import { createRegistryElementBrowserModel, getInitialRegistryElementBrowserSection, getRegistryElementBrowserItems } from './model';
28
29
  import { RegistryElementBrowserCategories } from './RegistryElementBrowserCategories';
29
30
  import { RegistryElementBrowserFooter } from './RegistryElementBrowserFooter';
@@ -63,13 +64,34 @@ export var RegistryElementBrowser = function RegistryElementBrowser(_ref) {
63
64
  onSelect = _ref.onSelect;
64
65
  var _useIntl = useIntl(),
65
66
  formatMessage = _useIntl.formatMessage;
66
- var _useState = useState({
67
+ var _useState = useState(function () {
68
+ return {
69
+ isOpen: isOpen,
70
+ menuOpenId: Symbol('element-browser')
71
+ };
72
+ }),
73
+ _useState2 = _slicedToArray(_useState, 2),
74
+ session = _useState2[0],
75
+ setSession = _useState2[1];
76
+ if (session.isOpen !== isOpen) {
77
+ setSession({
78
+ isOpen: isOpen,
79
+ menuOpenId: isOpen ? Symbol('element-browser') : session.menuOpenId
80
+ });
81
+ }
82
+ var surfaceContext = useMemo(function () {
83
+ return createRegisteredMenuSurfaceContext({
84
+ menuOpenId: session.menuOpenId,
85
+ surface: 'element-browser'
86
+ });
87
+ }, [session.menuOpenId]);
88
+ var _useState3 = useState({
67
89
  query: '',
68
90
  section: defaultSection
69
91
  }),
70
- _useState2 = _slicedToArray(_useState, 2),
71
- browserState = _useState2[0],
72
- setBrowserState = _useState2[1];
92
+ _useState4 = _slicedToArray(_useState3, 2),
93
+ browserState = _useState4[0],
94
+ setBrowserState = _useState4[1];
73
95
  var query = browserState.query,
74
96
  section = browserState.section,
75
97
  selectedRegistration = browserState.selectedRegistration;
@@ -77,6 +99,7 @@ export var RegistryElementBrowser = function RegistryElementBrowser(_ref) {
77
99
  var browserModel = useMemo(function () {
78
100
  var model = createRegistryElementBrowserModel(components, {
79
101
  footerKey: getMenuFooterSectionKey(MENU.key),
102
+ surfaceContext: surfaceContext,
80
103
  overflowKeys: new Set(components.filter(function (component) {
81
104
  return component.type === 'menu-section';
82
105
  }).map(function (component) {
@@ -99,7 +122,7 @@ export var RegistryElementBrowser = function RegistryElementBrowser(_ref) {
99
122
  section: effectiveSection
100
123
  })
101
124
  };
102
- }, [components, formatMessage, query, section]);
125
+ }, [components, formatMessage, query, section, surfaceContext]);
103
126
  var model = browserModel.model,
104
127
  effectiveSection = browserModel.effectiveSection,
105
128
  items = browserModel.items;
@@ -353,6 +376,7 @@ export var RegistryElementBrowser = function RegistryElementBrowser(_ref) {
353
376
  fallbackItems: model.fallbackItems,
354
377
  query: query,
355
378
  resultsId: resultsId,
379
+ surfaceContext: surfaceContext,
356
380
  section: effectiveSection,
357
381
  selectedKey: selectedKey,
358
382
  onKeyDown: onKeyDown,
@@ -3,9 +3,8 @@ import "./RegistryElementBrowserSearchResults.compiled.css";
3
3
  import { ax, ix } from "@compiled/react/runtime";
4
4
  import React, { useMemo } from 'react';
5
5
  import { useIntl } from 'react-intl';
6
- import { QuickInsertEmptyState } from '@atlaskit/editor-common/quick-insert/empty-state';
7
6
  import { QuickInsertProvider } from '@atlaskit/editor-common/quick-insert/provider';
8
-
7
+ import { QuickInsertEmptyState } from '../registered-menu/QuickInsertEmptyState';
9
8
  // oxlint-disable-next-line eslint/no-redeclare
10
9
 
11
10
  var resultsGridStyles = null;
@@ -18,7 +17,8 @@ var RegistryElementBrowserItem = function RegistryElementBrowserItem(_ref) {
18
17
  isOffline = _ref.isOffline,
19
18
  isSelected = _ref.isSelected,
20
19
  item = _ref.item,
21
- onSelect = _ref.onSelect;
20
+ onSelect = _ref.onSelect,
21
+ surfaceContext = _ref.surfaceContext;
22
22
  var _item$registration = item.registration,
23
23
  Component = _item$registration.component,
24
24
  key = _item$registration.key;
@@ -34,15 +34,18 @@ var RegistryElementBrowserItem = function RegistryElementBrowserItem(_ref) {
34
34
  select: function select(handler) {
35
35
  return onSelect(key, handler);
36
36
  },
37
- surface: 'element-browser'
37
+ surfaceContext: surfaceContext
38
38
  };
39
- }, [editorView, isOffline, isSelected, item.description, key, onSelect]);
39
+ }, [editorView, isOffline, isSelected, item.description, key, onSelect, surfaceContext]);
40
40
  if (!Component) {
41
41
  return null;
42
42
  }
43
43
  return /*#__PURE__*/React.createElement(QuickInsertProvider, {
44
44
  value: contextValue
45
- }, /*#__PURE__*/React.createElement(Component, undefined, null));
45
+ }, /*#__PURE__*/React.createElement(Component, {
46
+ parents: item.registration.parents,
47
+ surfaceContext: surfaceContext
48
+ }, null));
46
49
  };
47
50
  export var RegistryElementBrowserSearchResults = function RegistryElementBrowserSearchResults(_ref2) {
48
51
  var editorView = _ref2.editorView,
@@ -56,6 +59,7 @@ export var RegistryElementBrowserSearchResults = function RegistryElementBrowser
56
59
  resultsId = _ref2.resultsId,
57
60
  section = _ref2.section,
58
61
  selectedKey = _ref2.selectedKey,
62
+ surfaceContext = _ref2.surfaceContext,
59
63
  onKeyDown = _ref2.onKeyDown,
60
64
  onSelect = _ref2.onSelect;
61
65
  var _useIntl = useIntl(),
@@ -96,7 +100,8 @@ export var RegistryElementBrowserSearchResults = function RegistryElementBrowser
96
100
  isSelected: selectedKey === item.registration.key,
97
101
  item: item,
98
102
  key: item.registration.key,
99
- onSelect: onSelect
103
+ onSelect: onSelect,
104
+ surfaceContext: surfaceContext
100
105
  });
101
106
  })), shouldRenderEmptyState && !hasFallbackItems && query.trim().length > 0 && /*#__PURE__*/React.createElement(QuickInsertEmptyState, {
102
107
  mode: "full"