@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.
- package/CHANGELOG.md +34 -0
- package/commands/package.json +17 -0
- package/dist/cjs/entry-points/commands.js +18 -0
- package/dist/cjs/pm-plugins/commands/open-typeahead-at-cursor.js +7 -2
- package/dist/cjs/pm-plugins/commands/update-list-items.js +3 -1
- package/dist/cjs/pm-plugins/main.js +54 -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/entry-points/commands.js +2 -0
- package/dist/es2019/pm-plugins/commands/open-typeahead-at-cursor.js +7 -2
- package/dist/es2019/pm-plugins/commands/update-list-items.js +3 -2
- package/dist/es2019/pm-plugins/main.js +54 -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/entry-points/commands.js +2 -0
- package/dist/esm/pm-plugins/commands/open-typeahead-at-cursor.js +7 -2
- package/dist/esm/pm-plugins/commands/update-list-items.js +3 -1
- package/dist/esm/pm-plugins/main.js +54 -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/entry-points/commands.d.ts +1 -0
- 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/entry-points/commands.d.ts +1 -0
- 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 +5 -5
|
@@ -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,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 var openTypeAhead = function openTypeAhead(props) {
|
|
@@ -43,7 +44,7 @@ export var openTypeAheadAtCursor = function openTypeAheadAtCursor(_ref) {
|
|
|
43
44
|
// delete 1 pos before wherever selection is now - that will delete the empty space
|
|
44
45
|
tr.delete(tr.selection.from - 1, tr.selection.from);
|
|
45
46
|
} else {
|
|
46
|
-
var _selection$$head, _selection$$head$
|
|
47
|
+
var _selection$$head$pare, _selection$$head$pare2, _selection$$head;
|
|
47
48
|
if (selection instanceof NodeSelection) {
|
|
48
49
|
if (isInline) {
|
|
49
50
|
tr.deleteSelection();
|
|
@@ -72,7 +73,11 @@ export var openTypeAheadAtCursor = function openTypeAheadAtCursor(_ref) {
|
|
|
72
73
|
// being inserted due to composition by checking if we have the trigger
|
|
73
74
|
// directly before the typeahead. This should not happen unless it has
|
|
74
75
|
// been eroneously added because we require whitespace/newline for typeahead.
|
|
75
|
-
|
|
76
|
+
// Check if the text ends with the trigger character (or any character matched
|
|
77
|
+
// by customRegex, to support wide-char variants like fullwidth slash /)
|
|
78
|
+
var triggerPattern = expValEquals('platform_editor_wide_slash_trigger', 'isEnabled', true) && triggerHandler.customRegex ? new RegExp("(".concat(triggerHandler.customRegex, ")$"), 'u') : null;
|
|
79
|
+
var 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);
|
|
80
|
+
if (cursorPos >= 2 && !!(selection !== null && selection !== void 0 && (_selection$$head = selection.$head) !== null && _selection$$head !== void 0 && (_selection$$head = _selection$$head.parent) !== null && _selection$$head !== void 0 && _selection$$head.textContent) && endsWithTrigger) {
|
|
76
81
|
tr.delete(cursorPos - 1, cursorPos);
|
|
77
82
|
}
|
|
78
83
|
}
|
|
@@ -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) {
|
|
@@ -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(_ref) {
|
|
|
43
46
|
typeAheadHandlers: typeAheadHandlers,
|
|
44
47
|
popupMountRef: 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
|
+
var pendingWideSlashHandler = null;
|
|
46
54
|
return new SafePlugin({
|
|
47
55
|
key: pluginKey,
|
|
48
56
|
state: {
|
|
@@ -53,6 +61,7 @@ export function createPlugin(_ref) {
|
|
|
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(_ref) {
|
|
|
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: function 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
|
+
var handler = pendingWideSlashHandler;
|
|
111
|
+
pendingWideSlashHandler = null;
|
|
112
|
+
// Defer until ProseMirror has flushed the composed text into its state.
|
|
113
|
+
setTimeout(function () {
|
|
114
|
+
var command = openTypeAheadAtCursor({
|
|
115
|
+
triggerHandler: handler,
|
|
116
|
+
inputMethod: INPUT_METHOD.KEYBOARD
|
|
117
|
+
});
|
|
118
|
+
var 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: function 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
|
+
var pendingData = (_event$data = event.data) !== null && _event$data !== void 0 ? _event$data : '';
|
|
138
|
+
pendingWideSlashHandler = (_typeAheadHandlers$fi = typeAheadHandlers.find(function (handler) {
|
|
139
|
+
if (!handler.customRegex) {
|
|
140
|
+
return false;
|
|
141
|
+
}
|
|
142
|
+
// Only match if the entire composition is a trigger character
|
|
143
|
+
var pattern = new RegExp("^(".concat(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: function 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: function click(view, event) {
|
|
@@ -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) {
|
|
@@ -206,7 +206,7 @@ export var typeAheadPlugin = function typeAheadPlugin(_ref) {
|
|
|
206
206
|
}];
|
|
207
207
|
},
|
|
208
208
|
getSharedState: function getSharedState(editorState) {
|
|
209
|
-
var _state$decorationSet, _state$decorationElem, _state$items, _state$errorInfo, _state$selectedIndex;
|
|
209
|
+
var _state$decorationSet, _state$decorationElem, _state$items, _state$sections, _state$errorInfo, _state$selectedIndex;
|
|
210
210
|
if (!editorState) {
|
|
211
211
|
return {
|
|
212
212
|
query: '',
|
|
@@ -217,6 +217,7 @@ export var typeAheadPlugin = function typeAheadPlugin(_ref) {
|
|
|
217
217
|
decorationElement: null,
|
|
218
218
|
triggerHandler: undefined,
|
|
219
219
|
items: [],
|
|
220
|
+
sections: [],
|
|
220
221
|
errorInfo: null,
|
|
221
222
|
selectedIndex: 0
|
|
222
223
|
};
|
|
@@ -232,6 +233,7 @@ export var typeAheadPlugin = function typeAheadPlugin(_ref) {
|
|
|
232
233
|
decorationElement: (_state$decorationElem = state === null || state === void 0 ? void 0 : state.decorationElement) !== null && _state$decorationElem !== void 0 ? _state$decorationElem : null,
|
|
233
234
|
triggerHandler: state === null || state === void 0 ? void 0 : state.triggerHandler,
|
|
234
235
|
items: (_state$items = state === null || state === void 0 ? void 0 : state.items) !== null && _state$items !== void 0 ? _state$items : [],
|
|
236
|
+
sections: (_state$sections = state === null || state === void 0 ? void 0 : state.sections) !== null && _state$sections !== void 0 ? _state$sections : [],
|
|
235
237
|
errorInfo: (_state$errorInfo = state === null || state === void 0 ? void 0 : state.errorInfo) !== null && _state$errorInfo !== void 0 ? _state$errorInfo : null,
|
|
236
238
|
selectedIndex: (_state$selectedIndex = state === null || state === void 0 ? void 0 : state.selectedIndex) !== null && _state$selectedIndex !== void 0 ? _state$selectedIndex : 0
|
|
237
239
|
};
|
|
@@ -6,25 +6,27 @@ export function ContentComponent(_ref) {
|
|
|
6
6
|
editorView = _ref.editorView,
|
|
7
7
|
popupMountRef = _ref.popupMountRef;
|
|
8
8
|
var _useSharedPluginState = useSharedPluginStateWithSelector(api, ['typeAhead'], function (states) {
|
|
9
|
-
var _states$typeAheadStat, _states$typeAheadStat2, _states$typeAheadStat3, _states$typeAheadStat4, _states$typeAheadStat5, _states$typeAheadStat6, _states$typeAheadStat7;
|
|
9
|
+
var _states$typeAheadStat, _states$typeAheadStat2, _states$typeAheadStat3, _states$typeAheadStat4, _states$typeAheadStat5, _states$typeAheadStat6, _states$typeAheadStat7, _states$typeAheadStat8;
|
|
10
10
|
return {
|
|
11
11
|
triggerHandler: (_states$typeAheadStat = states.typeAheadState) === null || _states$typeAheadStat === void 0 ? void 0 : _states$typeAheadStat.triggerHandler,
|
|
12
12
|
items: (_states$typeAheadStat2 = states.typeAheadState) === null || _states$typeAheadStat2 === void 0 ? void 0 : _states$typeAheadStat2.items,
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
13
|
+
sections: (_states$typeAheadStat3 = states.typeAheadState) === null || _states$typeAheadStat3 === void 0 ? void 0 : _states$typeAheadStat3.sections,
|
|
14
|
+
errorInfo: (_states$typeAheadStat4 = states.typeAheadState) === null || _states$typeAheadStat4 === void 0 ? void 0 : _states$typeAheadStat4.errorInfo,
|
|
15
|
+
decorationElement: (_states$typeAheadStat5 = states.typeAheadState) === null || _states$typeAheadStat5 === void 0 ? void 0 : _states$typeAheadStat5.decorationElement,
|
|
16
|
+
decorationSet: (_states$typeAheadStat6 = states.typeAheadState) === null || _states$typeAheadStat6 === void 0 ? void 0 : _states$typeAheadStat6.decorationSet,
|
|
17
|
+
query: (_states$typeAheadStat7 = states.typeAheadState) === null || _states$typeAheadStat7 === void 0 ? void 0 : _states$typeAheadStat7.query,
|
|
18
|
+
selectedIndex: (_states$typeAheadStat8 = states.typeAheadState) === null || _states$typeAheadStat8 === void 0 ? void 0 : _states$typeAheadStat8.selectedIndex
|
|
18
19
|
};
|
|
19
20
|
}),
|
|
20
21
|
triggerHandler = _useSharedPluginState.triggerHandler,
|
|
21
22
|
items = _useSharedPluginState.items,
|
|
23
|
+
sections = _useSharedPluginState.sections,
|
|
22
24
|
errorInfo = _useSharedPluginState.errorInfo,
|
|
23
25
|
decorationElement = _useSharedPluginState.decorationElement,
|
|
24
26
|
decorationSet = _useSharedPluginState.decorationSet,
|
|
25
27
|
query = _useSharedPluginState.query,
|
|
26
28
|
selectedIndex = _useSharedPluginState.selectedIndex;
|
|
27
|
-
if (items === undefined || decorationSet === undefined || errorInfo === undefined || decorationElement === undefined || query === undefined || selectedIndex === undefined) {
|
|
29
|
+
if (items === undefined || sections === undefined || decorationSet === undefined || errorInfo === undefined || decorationElement === undefined || query === undefined || selectedIndex === undefined) {
|
|
28
30
|
return null;
|
|
29
31
|
}
|
|
30
32
|
return /*#__PURE__*/React.createElement(TypeAheadMenu, {
|
|
@@ -35,6 +37,7 @@ export function ContentComponent(_ref) {
|
|
|
35
37
|
typeAheadState: {
|
|
36
38
|
triggerHandler: triggerHandler,
|
|
37
39
|
items: items,
|
|
40
|
+
sections: sections,
|
|
38
41
|
errorInfo: errorInfo,
|
|
39
42
|
decorationElement: decorationElement,
|
|
40
43
|
decorationSet: decorationSet,
|