@atlaskit/editor-plugin-type-ahead 10.2.2 → 10.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +20 -0
- package/dist/cjs/pm-plugins/commands/update-list-items.js +3 -1
- package/dist/cjs/pm-plugins/main.js +1 -0
- package/dist/cjs/pm-plugins/reducer.js +7 -1
- package/dist/cjs/typeAheadPlugin.js +3 -1
- package/dist/cjs/ui/ContentComponent.js +10 -7
- package/dist/cjs/ui/TypeAheadList.js +126 -52
- package/dist/cjs/ui/TypeAheadMenu.js +3 -0
- package/dist/cjs/ui/TypeAheadPopup.js +3 -0
- package/dist/cjs/ui/WrapperTypeAhead.js +3 -1
- package/dist/cjs/ui/hooks/build-sectioned-result.js +131 -0
- package/dist/cjs/ui/hooks/use-load-items.js +12 -4
- package/dist/es2019/pm-plugins/commands/update-list-items.js +3 -2
- package/dist/es2019/pm-plugins/main.js +1 -0
- package/dist/es2019/pm-plugins/reducer.js +6 -1
- package/dist/es2019/typeAheadPlugin.js +3 -1
- package/dist/es2019/ui/ContentComponent.js +10 -7
- package/dist/es2019/ui/TypeAheadList.js +85 -23
- package/dist/es2019/ui/TypeAheadMenu.js +2 -0
- package/dist/es2019/ui/TypeAheadPopup.js +2 -0
- package/dist/es2019/ui/WrapperTypeAhead.js +3 -1
- package/dist/es2019/ui/hooks/build-sectioned-result.js +83 -0
- package/dist/es2019/ui/hooks/use-load-items.js +13 -4
- package/dist/esm/pm-plugins/commands/update-list-items.js +3 -1
- package/dist/esm/pm-plugins/main.js +1 -0
- package/dist/esm/pm-plugins/reducer.js +7 -1
- package/dist/esm/typeAheadPlugin.js +3 -1
- package/dist/esm/ui/ContentComponent.js +10 -7
- package/dist/esm/ui/TypeAheadList.js +126 -52
- package/dist/esm/ui/TypeAheadMenu.js +3 -0
- package/dist/esm/ui/TypeAheadPopup.js +3 -0
- package/dist/esm/ui/WrapperTypeAhead.js +3 -1
- package/dist/esm/ui/hooks/build-sectioned-result.js +124 -0
- package/dist/esm/ui/hooks/use-load-items.js +12 -4
- package/dist/types/pm-plugins/commands/update-list-items.d.ts +2 -1
- package/dist/types/types/index.d.ts +8 -0
- package/dist/types/ui/TypeAheadList.d.ts +3 -1
- package/dist/types/ui/TypeAheadPopup.d.ts +3 -2
- package/dist/types/ui/hooks/build-sectioned-result.d.ts +11 -0
- package/dist/types/ui/hooks/use-load-items.d.ts +2 -1
- package/dist/types-ts4.5/pm-plugins/commands/update-list-items.d.ts +2 -1
- package/dist/types-ts4.5/types/index.d.ts +8 -0
- package/dist/types-ts4.5/ui/TypeAheadList.d.ts +3 -1
- package/dist/types-ts4.5/ui/TypeAheadPopup.d.ts +3 -2
- package/dist/types-ts4.5/ui/hooks/build-sectioned-result.d.ts +11 -0
- package/dist/types-ts4.5/ui/hooks/use-load-items.d.ts +2 -1
- package/package.json +4 -4
|
@@ -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
|
|
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;
|
|
@@ -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) {
|
|
@@ -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
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
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,
|
|
@@ -17,6 +17,7 @@ import { AssistiveText } from '@atlaskit/editor-common/ui';
|
|
|
17
17
|
import { MenuGroup } from '@atlaskit/menu';
|
|
18
18
|
import { Text, Box } from '@atlaskit/primitives/compiled';
|
|
19
19
|
import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
|
|
20
|
+
import { editorExperiment } from '@atlaskit/tmp-editor-statsig/experiments';
|
|
20
21
|
import { closeTypeAhead } from '../pm-plugins/commands/close-type-ahead';
|
|
21
22
|
import { updateSelectedIndex } from '../pm-plugins/commands/update-selected-index';
|
|
22
23
|
import { TYPE_AHEAD_DECORATION_ELEMENT_ID } from '../pm-plugins/constants';
|
|
@@ -32,6 +33,36 @@ const list = css({
|
|
|
32
33
|
padding: `${"var(--ds-space-100, 8px)"} ${"var(--ds-space-150, 12px)"}`
|
|
33
34
|
}
|
|
34
35
|
});
|
|
36
|
+
const buildTypeAheadRows = ({
|
|
37
|
+
itemsLength,
|
|
38
|
+
sections
|
|
39
|
+
}) => {
|
|
40
|
+
if (sections.length === 0) {
|
|
41
|
+
return Array.from({
|
|
42
|
+
length: itemsLength
|
|
43
|
+
}, (_, itemIndex) => ({
|
|
44
|
+
type: 'item',
|
|
45
|
+
itemIndex
|
|
46
|
+
}));
|
|
47
|
+
}
|
|
48
|
+
const sortedSections = [...sections].sort((left, right) => left.startIndex - right.startIndex);
|
|
49
|
+
const sectionsByStartIndex = new Map(sortedSections.map(section => [section.startIndex, section]));
|
|
50
|
+
const rows = [];
|
|
51
|
+
for (let itemIndex = 0; itemIndex < itemsLength; itemIndex++) {
|
|
52
|
+
const section = sectionsByStartIndex.get(itemIndex);
|
|
53
|
+
if (section) {
|
|
54
|
+
rows.push({
|
|
55
|
+
type: 'section',
|
|
56
|
+
section
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
rows.push({
|
|
60
|
+
type: 'item',
|
|
61
|
+
itemIndex
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
return rows;
|
|
65
|
+
};
|
|
35
66
|
const TypeaheadAssistiveTextPureComponent = /*#__PURE__*/React.memo(({
|
|
36
67
|
numberOfResults
|
|
37
68
|
}) => {
|
|
@@ -48,6 +79,7 @@ const TypeaheadAssistiveTextPureComponent = /*#__PURE__*/React.memo(({
|
|
|
48
79
|
});
|
|
49
80
|
const TypeAheadListComponent = /*#__PURE__*/React.memo(({
|
|
50
81
|
items,
|
|
82
|
+
sections,
|
|
51
83
|
emptyItem,
|
|
52
84
|
selectedIndex,
|
|
53
85
|
editorView,
|
|
@@ -61,7 +93,7 @@ const TypeAheadListComponent = /*#__PURE__*/React.memo(({
|
|
|
61
93
|
showMoreOptionsButton,
|
|
62
94
|
onMoreOptionsClicked
|
|
63
95
|
}) => {
|
|
64
|
-
var _triggerHandler$getMo, _decorationElement$qu2;
|
|
96
|
+
var _itemRowIndexByItemIn, _triggerHandler$getMo, _decorationElement$qu2;
|
|
65
97
|
const listRef = useRef();
|
|
66
98
|
const listContainerRef = useRef(null);
|
|
67
99
|
const lastInputMethodRef = useRef('keyboard');
|
|
@@ -82,6 +114,21 @@ const TypeAheadListComponent = /*#__PURE__*/React.memo(({
|
|
|
82
114
|
fixedWidth: true,
|
|
83
115
|
defaultHeight: LIST_ITEM_ESTIMATED_HEIGHT
|
|
84
116
|
}));
|
|
117
|
+
const listRows = useMemo(() => buildTypeAheadRows({
|
|
118
|
+
itemsLength,
|
|
119
|
+
sections: sections || []
|
|
120
|
+
}), [itemsLength, sections]);
|
|
121
|
+
const itemRowIndexByItemIndex = useMemo(() => {
|
|
122
|
+
const rowIndexByItemIndex = new Map();
|
|
123
|
+
listRows.forEach((row, rowIndex) => {
|
|
124
|
+
if (row.type === 'item') {
|
|
125
|
+
rowIndexByItemIndex.set(row.itemIndex, rowIndex);
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
return rowIndexByItemIndex;
|
|
129
|
+
}, [listRows]);
|
|
130
|
+
const populatedSectionCount = useMemo(() => listRows.filter(row => row.type === 'section').length, [listRows]);
|
|
131
|
+
const selectedItemRowIndex = selectedIndex >= 0 ? (_itemRowIndexByItemIn = itemRowIndexByItemIndex.get(selectedIndex)) !== null && _itemRowIndexByItemIn !== void 0 ? _itemRowIndexByItemIn : -1 : -1;
|
|
85
132
|
const onItemsRendered = useCallback(props => {
|
|
86
133
|
lastVisibleIndexes.current = props;
|
|
87
134
|
}, []);
|
|
@@ -132,28 +179,32 @@ const TypeAheadListComponent = /*#__PURE__*/React.memo(({
|
|
|
132
179
|
requestAnimationFrame(() => {
|
|
133
180
|
requestAnimationFrame(() => {
|
|
134
181
|
const isViewMoreSelected = showMoreOptionsButton && selectedIndex === itemsLength;
|
|
135
|
-
const isSelectedItemVisible =
|
|
182
|
+
const isSelectedItemVisible = selectedItemRowIndex >= lastVisibleStartIndex && selectedItemRowIndex <= lastVisibleStopIndex ||
|
|
136
183
|
// view more is always visible, hence no scrolling
|
|
137
184
|
isViewMoreSelected;
|
|
138
185
|
|
|
139
186
|
//Should scroll to the list item only when the selectedIndex >= 0 and item is not visible
|
|
140
187
|
if (!isSelectedItemVisible && selectedIndex !== -1) {
|
|
141
|
-
listRef.current.scrollToRow(
|
|
188
|
+
listRef.current.scrollToRow(selectedItemRowIndex);
|
|
142
189
|
} else if (selectedIndex === -1) {
|
|
143
190
|
listRef.current.scrollToRow(0);
|
|
144
191
|
}
|
|
145
192
|
});
|
|
146
193
|
});
|
|
147
|
-
}, [selectedIndex, lastVisibleStartIndex, lastVisibleStopIndex, itemsLength, showMoreOptionsButton]);
|
|
148
|
-
const onMouseMove = (event,
|
|
194
|
+
}, [selectedIndex, lastVisibleStartIndex, lastVisibleStopIndex, itemsLength, showMoreOptionsButton, selectedItemRowIndex]);
|
|
195
|
+
const onMouseMove = (event, row) => {
|
|
196
|
+
if (row.type !== 'item') {
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
const itemIndex = row.itemIndex;
|
|
149
200
|
event.preventDefault();
|
|
150
201
|
event.stopPropagation();
|
|
151
|
-
if (selectedIndex ===
|
|
202
|
+
if (selectedIndex === itemIndex) {
|
|
152
203
|
return;
|
|
153
204
|
}
|
|
154
205
|
mouseMovedRef.current = true;
|
|
155
206
|
lastInputMethodRef.current = 'mouse';
|
|
156
|
-
updateSelectedIndex(
|
|
207
|
+
updateSelectedIndex(itemIndex, api)(editorView.state, editorView.dispatch);
|
|
157
208
|
};
|
|
158
209
|
useLayoutEffect(() => {
|
|
159
210
|
if (mouseMovedRef.current) {
|
|
@@ -167,17 +218,17 @@ const TypeAheadListComponent = /*#__PURE__*/React.memo(({
|
|
|
167
218
|
return;
|
|
168
219
|
}
|
|
169
220
|
const isViewMoreSelected = showMoreOptionsButton && selectedIndex === itemsLength;
|
|
170
|
-
const isSelectedItemVisible =
|
|
221
|
+
const isSelectedItemVisible = selectedItemRowIndex >= lastVisibleStartIndex && selectedItemRowIndex <= lastVisibleStopIndex ||
|
|
171
222
|
// view more is always visible, hence no scrolling
|
|
172
223
|
isViewMoreSelected;
|
|
173
224
|
|
|
174
225
|
//Should scroll to the list item only when the selectedIndex >= 0 and item is not visible
|
|
175
226
|
if (!isSelectedItemVisible && selectedIndex !== -1) {
|
|
176
|
-
listRef.current.scrollToRow(
|
|
227
|
+
listRef.current.scrollToRow(selectedItemRowIndex);
|
|
177
228
|
} else if (selectedIndex === -1) {
|
|
178
229
|
listRef.current.scrollToRow(0);
|
|
179
230
|
}
|
|
180
|
-
}, [selectedIndex, lastVisibleStartIndex, lastVisibleStopIndex, itemsLength, showMoreOptionsButton]);
|
|
231
|
+
}, [selectedIndex, lastVisibleStartIndex, lastVisibleStopIndex, itemsLength, showMoreOptionsButton, selectedItemRowIndex]);
|
|
181
232
|
useLayoutEffect(() => {
|
|
182
233
|
setCache(new CellMeasurerCache({
|
|
183
234
|
fixedWidth: true,
|
|
@@ -193,10 +244,10 @@ const TypeAheadListComponent = /*#__PURE__*/React.memo(({
|
|
|
193
244
|
listContainerRef.current.firstChild.scrollTo(0, 0);
|
|
194
245
|
}
|
|
195
246
|
});
|
|
196
|
-
}, [items]);
|
|
247
|
+
}, [items, sections]);
|
|
197
248
|
useLayoutEffect(() => {
|
|
198
249
|
// Exclude view more item from the count
|
|
199
|
-
const itemsToRender = showMoreOptionsButton ? items.slice(0, -1) : items;
|
|
250
|
+
const itemsToRender = editorExperiment('platform_editor_agent_mentions', true) ? listRows : showMoreOptionsButton ? items.slice(0, -1) : items;
|
|
200
251
|
const height = Math.min(
|
|
201
252
|
// eslint-disable-next-line @atlassian/perf-linting/no-expensive-computations-in-render -- Ignored via go/ees017 (to be fixed)
|
|
202
253
|
itemsToRender.reduce((prevValue, currentValue, index) => {
|
|
@@ -205,7 +256,7 @@ const TypeAheadListComponent = /*#__PURE__*/React.memo(({
|
|
|
205
256
|
});
|
|
206
257
|
}, 0), fitHeight);
|
|
207
258
|
setHeight(height);
|
|
208
|
-
}, [items, cache, fitHeight, showMoreOptionsButton]);
|
|
259
|
+
}, [listRows, items, cache, fitHeight, showMoreOptionsButton]);
|
|
209
260
|
useLayoutEffect(() => {
|
|
210
261
|
if (!listContainerRef.current) {
|
|
211
262
|
return;
|
|
@@ -286,7 +337,10 @@ const TypeAheadListComponent = /*#__PURE__*/React.memo(({
|
|
|
286
337
|
isScrolling,
|
|
287
338
|
isVisible
|
|
288
339
|
}) => {
|
|
289
|
-
const
|
|
340
|
+
const currentRow = listRows[index];
|
|
341
|
+
if (!currentRow) {
|
|
342
|
+
return null;
|
|
343
|
+
}
|
|
290
344
|
return jsx(CellMeasurer, {
|
|
291
345
|
key: key,
|
|
292
346
|
cache: cache,
|
|
@@ -305,20 +359,28 @@ const TypeAheadListComponent = /*#__PURE__*/React.memo(({
|
|
|
305
359
|
isScrolling: isScrolling
|
|
306
360
|
// eslint-disable-next-line @atlassian/perf-linting/no-unstable-inline-props -- Ignored via go/ees017 (to be fixed)
|
|
307
361
|
,
|
|
308
|
-
onMouseMove: e => onMouseMove(e,
|
|
309
|
-
}, jsx(
|
|
310
|
-
|
|
311
|
-
|
|
362
|
+
onMouseMove: e => onMouseMove(e, currentRow)
|
|
363
|
+
}, currentRow.type === 'section' ? populatedSectionCount === 1 ? null : jsx(Box, {
|
|
364
|
+
paddingInline: "space.150",
|
|
365
|
+
paddingBlock: "space.050"
|
|
366
|
+
}, jsx(Text, {
|
|
367
|
+
as: "span",
|
|
368
|
+
size: "small",
|
|
369
|
+
color: "color.text.subtle",
|
|
370
|
+
weight: "medium"
|
|
371
|
+
}, currentRow.section.title)) : jsx(TypeAheadListItem, {
|
|
372
|
+
key: items[currentRow.itemIndex].title,
|
|
373
|
+
item: items[currentRow.itemIndex],
|
|
312
374
|
firstOnlineSupportedIndex: firstOnlineSupportedRow,
|
|
313
375
|
itemsLength: itemsLength,
|
|
314
|
-
itemIndex:
|
|
376
|
+
itemIndex: currentRow.itemIndex,
|
|
315
377
|
selectedIndex: selectedIndex
|
|
316
378
|
// eslint-disable-next-line @atlassian/perf-linting/no-unstable-inline-props -- Ignored via go/ees017 (to be fixed)
|
|
317
379
|
,
|
|
318
|
-
onItemClick: (mode,
|
|
319
|
-
actions.onItemClick(mode,
|
|
380
|
+
onItemClick: (mode, itemIndex) => {
|
|
381
|
+
actions.onItemClick(mode, itemIndex, INPUT_METHOD.MOUSE);
|
|
320
382
|
},
|
|
321
|
-
ariaLabel: getTypeAheadListAriaLabels(triggerHandler === null || triggerHandler === void 0 ? void 0 : triggerHandler.trigger, intl,
|
|
383
|
+
ariaLabel: getTypeAheadListAriaLabels(triggerHandler === null || triggerHandler === void 0 ? void 0 : triggerHandler.trigger, intl, items[currentRow.itemIndex]).listItemAriaLabel,
|
|
322
384
|
moreElementsInQuickInsertViewEnabled: moreElementsInQuickInsertViewEnabled,
|
|
323
385
|
api: api,
|
|
324
386
|
lastInputMethodRef: lastInputMethodRef
|
|
@@ -351,7 +413,7 @@ const TypeAheadListComponent = /*#__PURE__*/React.memo(({
|
|
|
351
413
|
ref: listRef
|
|
352
414
|
// Skip rendering the view more button in the list
|
|
353
415
|
,
|
|
354
|
-
rowCount:
|
|
416
|
+
rowCount: listRows.length,
|
|
355
417
|
rowHeight: cache.rowHeight,
|
|
356
418
|
onRowsRendered: onItemsRendered,
|
|
357
419
|
width: LIST_WIDTH,
|
|
@@ -17,6 +17,7 @@ export const TypeAheadMenu = /*#__PURE__*/React.memo(({
|
|
|
17
17
|
const {
|
|
18
18
|
triggerHandler,
|
|
19
19
|
items,
|
|
20
|
+
sections = [],
|
|
20
21
|
errorInfo,
|
|
21
22
|
decorationElement,
|
|
22
23
|
decorationSet,
|
|
@@ -94,6 +95,7 @@ export const TypeAheadMenu = /*#__PURE__*/React.memo(({
|
|
|
94
95
|
anchorElement: decorationElement,
|
|
95
96
|
triggerHandler: triggerHandler,
|
|
96
97
|
items: items,
|
|
98
|
+
sections: sections,
|
|
97
99
|
emptyItem: emptyItem,
|
|
98
100
|
errorInfo: errorInfo,
|
|
99
101
|
selectedIndex: selectedIndex,
|
|
@@ -61,6 +61,7 @@ export const TypeAheadPopup = /*#__PURE__*/React.memo(props => {
|
|
|
61
61
|
popupsBoundariesElement,
|
|
62
62
|
popupsScrollableElement,
|
|
63
63
|
items,
|
|
64
|
+
sections = [],
|
|
64
65
|
emptyItem,
|
|
65
66
|
errorInfo,
|
|
66
67
|
selectedIndex,
|
|
@@ -341,6 +342,7 @@ export const TypeAheadPopup = /*#__PURE__*/React.memo(props => {
|
|
|
341
342
|
triggerHandler: triggerHandler
|
|
342
343
|
}), jsx(TypeAheadList, {
|
|
343
344
|
items: items,
|
|
345
|
+
sections: sections,
|
|
344
346
|
emptyItem: emptyItem,
|
|
345
347
|
selectedIndex: selectedIndex
|
|
346
348
|
// eslint-disable-next-line @atlassian/perf-linting/no-unstable-inline-props -- Ignored via go/ees017 (to be fixed)
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import React, { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react';
|
|
2
|
+
import { useIntl } from 'react-intl';
|
|
2
3
|
import { ACTION, INPUT_METHOD } from '@atlaskit/editor-common/analytics';
|
|
3
4
|
import { SelectItemMode } from '@atlaskit/editor-common/type-ahead';
|
|
4
5
|
import { fg } from '@atlaskit/platform-feature-flags';
|
|
@@ -31,11 +32,12 @@ export const WrapperTypeAhead = /*#__PURE__*/React.memo(({
|
|
|
31
32
|
if (editorExperiment('platform_editor_controls', 'variant1')) {
|
|
32
33
|
showMoreOptionsButton = !!(triggerHandler !== null && triggerHandler !== void 0 && triggerHandler.getMoreOptionsButtonConfig);
|
|
33
34
|
}
|
|
35
|
+
const intl = useIntl();
|
|
34
36
|
const [closed, setClosed] = useState(false);
|
|
35
37
|
const [query, setQuery] = useState(reopenQuery || '');
|
|
36
38
|
const queryRef = useRef(query);
|
|
37
39
|
const editorViewRef = useRef(editorView);
|
|
38
|
-
const items = useLoadItems(triggerHandler, editorView, query, showMoreOptionsButton, api);
|
|
40
|
+
const items = useLoadItems(triggerHandler, editorView, query, showMoreOptionsButton, api, intl);
|
|
39
41
|
useEffect(() => {
|
|
40
42
|
if (!closed && fg('platform_editor_ease_of_use_metrics')) {
|
|
41
43
|
var _api$metrics;
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { editorExperiment } from '@atlaskit/tmp-editor-statsig/experiments';
|
|
2
|
+
export const buildSectionedResult = ({
|
|
3
|
+
items,
|
|
4
|
+
triggerHandler,
|
|
5
|
+
intl
|
|
6
|
+
}) => {
|
|
7
|
+
var _triggerHandler$getSe;
|
|
8
|
+
if (!editorExperiment('platform_editor_agent_mentions', true) || !intl) {
|
|
9
|
+
return {
|
|
10
|
+
items,
|
|
11
|
+
sections: []
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
const sectionDefinitions = (_triggerHandler$getSe = triggerHandler.getSections) === null || _triggerHandler$getSe === void 0 ? void 0 : _triggerHandler$getSe.call(triggerHandler, {
|
|
15
|
+
intl
|
|
16
|
+
});
|
|
17
|
+
if (!sectionDefinitions || sectionDefinitions.length === 0) {
|
|
18
|
+
return {
|
|
19
|
+
items,
|
|
20
|
+
sections: []
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Track which item indexes have been claimed by a section, or excluded (matched a section but
|
|
25
|
+
// cut by that section's limit). Items excluded by limit should not appear anywhere in the output.
|
|
26
|
+
const assignedToSection = new Set();
|
|
27
|
+
const excludedByLimit = new Set();
|
|
28
|
+
const groupedBySection = new Map();
|
|
29
|
+
for (const section of sectionDefinitions) {
|
|
30
|
+
const matchingIndexes = [];
|
|
31
|
+
for (let i = 0; i < items.length; i++) {
|
|
32
|
+
if (assignedToSection.has(i) || excludedByLimit.has(i)) {
|
|
33
|
+
continue;
|
|
34
|
+
}
|
|
35
|
+
if (section.filter(items[i])) {
|
|
36
|
+
matchingIndexes.push(i);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
const acceptedIndexes = section.limit !== undefined ? matchingIndexes.slice(0, section.limit) : matchingIndexes;
|
|
40
|
+
const rejectedByLimit = matchingIndexes.slice(acceptedIndexes.length);
|
|
41
|
+
for (const i of acceptedIndexes) {
|
|
42
|
+
assignedToSection.add(i);
|
|
43
|
+
}
|
|
44
|
+
for (const i of rejectedByLimit) {
|
|
45
|
+
excludedByLimit.add(i);
|
|
46
|
+
}
|
|
47
|
+
if (acceptedIndexes.length === 0) {
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
groupedBySection.set(section.id, {
|
|
51
|
+
indexes: acceptedIndexes,
|
|
52
|
+
section
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
const flattenedItems = [];
|
|
56
|
+
const sections = [];
|
|
57
|
+
for (const section of sectionDefinitions) {
|
|
58
|
+
const grouped = groupedBySection.get(section.id);
|
|
59
|
+
if (!grouped) {
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
const startIndex = flattenedItems.length;
|
|
63
|
+
flattenedItems.push(...grouped.indexes.map(i => items[i]));
|
|
64
|
+
const endIndex = flattenedItems.length - 1;
|
|
65
|
+
sections.push({
|
|
66
|
+
endIndex,
|
|
67
|
+
id: section.id,
|
|
68
|
+
startIndex,
|
|
69
|
+
title: section.title
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Append items not claimed by any section and not excluded by a limit
|
|
74
|
+
for (let i = 0; i < items.length; i++) {
|
|
75
|
+
if (!assignedToSection.has(i) && !excludedByLimit.has(i)) {
|
|
76
|
+
flattenedItems.push(items[i]);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return {
|
|
80
|
+
items: flattenedItems,
|
|
81
|
+
sections
|
|
82
|
+
};
|
|
83
|
+
};
|
|
@@ -4,8 +4,9 @@ import { editorExperiment } from '@atlaskit/tmp-editor-statsig/experiments';
|
|
|
4
4
|
import { clearListError } from '../../pm-plugins/commands/clear-list-error';
|
|
5
5
|
import { updateListError } from '../../pm-plugins/commands/update-list-error';
|
|
6
6
|
import { updateListItem } from '../../pm-plugins/commands/update-list-items';
|
|
7
|
+
import { buildSectionedResult } from './build-sectioned-result';
|
|
7
8
|
const EMPTY_LIST_ITEM = [];
|
|
8
|
-
export const useLoadItems = (triggerHandler, editorView, query, showViewMore, api) => {
|
|
9
|
+
export const useLoadItems = (triggerHandler, editorView, query, showViewMore, api, intl) => {
|
|
9
10
|
const [items, setItems] = useState(EMPTY_LIST_ITEM);
|
|
10
11
|
const componentIsMounted = useRef(true);
|
|
11
12
|
const editorViewRef = useRef(editorView);
|
|
@@ -35,7 +36,15 @@ export const useLoadItems = (triggerHandler, editorView, query, showViewMore, ap
|
|
|
35
36
|
const emptyItem = result.length === 0 && expValEquals('platform_editor_insert_menu_ai', 'isEnabled', true) ? (_triggerHandler$getEm = triggerHandler.getEmptyItem) === null || _triggerHandler$getEm === void 0 ? void 0 : _triggerHandler$getEm.call(triggerHandler, {
|
|
36
37
|
editorState: editorView.state
|
|
37
38
|
}) : undefined;
|
|
38
|
-
const
|
|
39
|
+
const rawList = result.length > 0 ? result : emptyItem ? [emptyItem] : EMPTY_LIST_ITEM;
|
|
40
|
+
const {
|
|
41
|
+
items: list,
|
|
42
|
+
sections
|
|
43
|
+
} = buildSectionedResult({
|
|
44
|
+
items: rawList,
|
|
45
|
+
triggerHandler,
|
|
46
|
+
intl: intl !== null && intl !== void 0 ? intl : null
|
|
47
|
+
});
|
|
39
48
|
if (componentIsMounted.current) {
|
|
40
49
|
setItems(list);
|
|
41
50
|
}
|
|
@@ -45,7 +54,7 @@ export const useLoadItems = (triggerHandler, editorView, query, showViewMore, ap
|
|
|
45
54
|
title: 'View more'
|
|
46
55
|
};
|
|
47
56
|
queueMicrotask(() => {
|
|
48
|
-
updateListItem(showViewMore ? list.concat(viewMoreItem) : list)(view.state, view.dispatch);
|
|
57
|
+
updateListItem(showViewMore ? list.concat(viewMoreItem) : list, sections)(view.state, view.dispatch);
|
|
49
58
|
});
|
|
50
59
|
}).catch(e => {
|
|
51
60
|
if (editorExperiment('platform_editor_offline_editing_web', true)) {
|
|
@@ -63,7 +72,7 @@ export const useLoadItems = (triggerHandler, editorView, query, showViewMore, ap
|
|
|
63
72
|
// ignore because EditorView is mutable but we don't want to
|
|
64
73
|
// call loadItems when it changes, only when the query changes
|
|
65
74
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
66
|
-
}, [triggerHandler, query]);
|
|
75
|
+
}, [triggerHandler, query, intl]);
|
|
67
76
|
useEffect(() => {
|
|
68
77
|
return () => {
|
|
69
78
|
componentIsMounted.current = false;
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import { ACTIONS } from '../actions';
|
|
2
2
|
import { pluginKey as typeAheadPluginKey } from '../key';
|
|
3
3
|
export var updateListItem = function updateListItem(items) {
|
|
4
|
+
var sections = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];
|
|
4
5
|
return function (state, dispatch) {
|
|
5
6
|
var tr = state.tr;
|
|
6
7
|
tr.setMeta(typeAheadPluginKey, {
|
|
7
8
|
action: ACTIONS.UPDATE_LIST_ITEMS,
|
|
8
9
|
params: {
|
|
9
|
-
items: items
|
|
10
|
+
items: items,
|
|
11
|
+
sections: sections
|
|
10
12
|
}
|
|
11
13
|
});
|
|
12
14
|
if (dispatch) {
|
|
@@ -69,6 +69,7 @@ export var createReducer = function createReducer(_ref) {
|
|
|
69
69
|
inputMethod: inputMethod,
|
|
70
70
|
selectedIndex: typeof selectedIndex === 'number' ? selectedIndex : -1,
|
|
71
71
|
items: [],
|
|
72
|
+
sections: [],
|
|
72
73
|
query: reopenQuery || '',
|
|
73
74
|
removePrefixTriggerOnCancel: removePrefixTriggerOnCancel
|
|
74
75
|
});
|
|
@@ -87,6 +88,7 @@ export var createReducer = function createReducer(_ref) {
|
|
|
87
88
|
stats: null,
|
|
88
89
|
triggerHandler: undefined,
|
|
89
90
|
items: [],
|
|
91
|
+
sections: [],
|
|
90
92
|
removePrefixTriggerOnCancel: undefined
|
|
91
93
|
});
|
|
92
94
|
};
|
|
@@ -137,13 +139,17 @@ export var createReducer = function createReducer(_ref) {
|
|
|
137
139
|
return _objectSpread(_objectSpread({}, currentPluginState), {}, {
|
|
138
140
|
errorInfo: errorInfo,
|
|
139
141
|
items: [],
|
|
142
|
+
sections: [],
|
|
140
143
|
selectedIndex: -1
|
|
141
144
|
});
|
|
142
145
|
} else if (shouldUpdateListItems) {
|
|
143
|
-
var items = params.items
|
|
146
|
+
var items = params.items,
|
|
147
|
+
_params$sections = params.sections,
|
|
148
|
+
sections = _params$sections === void 0 ? [] : _params$sections;
|
|
144
149
|
var selectedIndex = currentPluginState.selectedIndex;
|
|
145
150
|
return _objectSpread(_objectSpread({}, currentPluginState), {}, {
|
|
146
151
|
items: items,
|
|
152
|
+
sections: sections,
|
|
147
153
|
selectedIndex: Math.max(selectedIndex >= items.length ? items.length - 1 : selectedIndex, -1)
|
|
148
154
|
});
|
|
149
155
|
} else if (shouldUpdateSelectedIndex) {
|