@atlaskit/editor-plugin-type-ahead 0.6.0 → 0.7.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/.eslintrc.js +5 -0
- package/CHANGELOG.md +12 -0
- package/dist/cjs/api.js +215 -0
- package/dist/cjs/commands/insert-type-ahead-item.js +205 -0
- package/dist/cjs/commands/update-list-items.js +23 -0
- package/dist/cjs/commands/update-query.js +27 -0
- package/dist/cjs/commands/update-selected-index.js +27 -0
- package/dist/cjs/index.js +8 -1
- package/dist/cjs/insert-utils.js +107 -0
- package/dist/cjs/messages.js +79 -0
- package/dist/cjs/plugin.js +382 -0
- package/dist/cjs/pm-plugins/actions.js +16 -0
- package/dist/cjs/pm-plugins/decorations.js +148 -0
- package/dist/cjs/pm-plugins/input-rules.js +36 -0
- package/dist/cjs/pm-plugins/insert-item-plugin.js +22 -0
- package/dist/cjs/pm-plugins/key.js +8 -0
- package/dist/cjs/pm-plugins/main.js +110 -0
- package/dist/cjs/pm-plugins/reducer.js +158 -0
- package/dist/cjs/pm-plugins/utils.js +18 -0
- package/dist/cjs/stats-modifier.js +42 -0
- package/dist/cjs/transforms/close-type-ahead.js +13 -0
- package/dist/cjs/transforms/open-typeahead-at-cursor.js +75 -0
- package/dist/cjs/transforms/set-selection-before-query.js +18 -0
- package/dist/cjs/ui/AssistiveText.js +120 -0
- package/dist/cjs/ui/InputQuery.js +400 -0
- package/dist/cjs/ui/TypeAheadList.js +285 -0
- package/dist/cjs/ui/TypeAheadListItem.js +175 -0
- package/dist/cjs/ui/TypeAheadPopup.js +230 -0
- package/dist/cjs/ui/WrapperTypeAhead.js +127 -0
- package/dist/cjs/ui/hooks/use-item-insert.js +109 -0
- package/dist/cjs/ui/hooks/use-load-items.js +50 -0
- package/dist/cjs/ui/hooks/use-on-force-select.js +41 -0
- package/dist/cjs/utils.js +130 -0
- package/dist/es2019/api.js +205 -0
- package/dist/es2019/commands/insert-type-ahead-item.js +204 -0
- package/dist/es2019/commands/update-list-items.js +17 -0
- package/dist/es2019/commands/update-query.js +21 -0
- package/dist/es2019/commands/update-selected-index.js +21 -0
- package/dist/es2019/index.js +1 -1
- package/dist/es2019/insert-utils.js +106 -0
- package/dist/es2019/messages.js +73 -0
- package/dist/es2019/plugin.js +381 -0
- package/dist/es2019/pm-plugins/actions.js +10 -0
- package/dist/es2019/pm-plugins/decorations.js +148 -0
- package/dist/es2019/pm-plugins/input-rules.js +29 -0
- package/dist/es2019/pm-plugins/insert-item-plugin.js +16 -0
- package/dist/es2019/pm-plugins/key.js +2 -0
- package/dist/es2019/pm-plugins/main.js +106 -0
- package/dist/es2019/pm-plugins/reducer.js +160 -0
- package/dist/es2019/pm-plugins/utils.js +12 -0
- package/dist/es2019/stats-modifier.js +33 -0
- package/dist/es2019/transforms/close-type-ahead.js +7 -0
- package/dist/es2019/transforms/open-typeahead-at-cursor.js +71 -0
- package/dist/es2019/transforms/set-selection-before-query.js +10 -0
- package/dist/es2019/ui/AssistiveText.js +88 -0
- package/dist/es2019/ui/InputQuery.js +392 -0
- package/dist/es2019/ui/TypeAheadList.js +273 -0
- package/dist/es2019/ui/TypeAheadListItem.js +212 -0
- package/dist/es2019/ui/TypeAheadPopup.js +233 -0
- package/dist/es2019/ui/WrapperTypeAhead.js +109 -0
- package/dist/es2019/ui/hooks/use-item-insert.js +112 -0
- package/dist/es2019/ui/hooks/use-load-items.js +41 -0
- package/dist/es2019/ui/hooks/use-on-force-select.js +38 -0
- package/dist/es2019/utils.js +126 -0
- package/dist/esm/api.js +209 -0
- package/dist/esm/commands/insert-type-ahead-item.js +198 -0
- package/dist/esm/commands/update-list-items.js +17 -0
- package/dist/esm/commands/update-query.js +21 -0
- package/dist/esm/commands/update-selected-index.js +21 -0
- package/dist/esm/index.js +1 -1
- package/dist/esm/insert-utils.js +101 -0
- package/dist/esm/messages.js +73 -0
- package/dist/esm/plugin.js +374 -0
- package/dist/esm/pm-plugins/actions.js +10 -0
- package/dist/esm/pm-plugins/decorations.js +141 -0
- package/dist/esm/pm-plugins/input-rules.js +29 -0
- package/dist/esm/pm-plugins/insert-item-plugin.js +16 -0
- package/dist/esm/pm-plugins/key.js +2 -0
- package/dist/esm/pm-plugins/main.js +104 -0
- package/dist/esm/pm-plugins/reducer.js +151 -0
- package/dist/esm/pm-plugins/utils.js +12 -0
- package/dist/esm/stats-modifier.js +35 -0
- package/dist/esm/transforms/close-type-ahead.js +7 -0
- package/dist/esm/transforms/open-typeahead-at-cursor.js +69 -0
- package/dist/esm/transforms/set-selection-before-query.js +12 -0
- package/dist/esm/ui/AssistiveText.js +115 -0
- package/dist/esm/ui/InputQuery.js +389 -0
- package/dist/esm/ui/TypeAheadList.js +276 -0
- package/dist/esm/ui/TypeAheadListItem.js +165 -0
- package/dist/esm/ui/TypeAheadPopup.js +220 -0
- package/dist/esm/ui/WrapperTypeAhead.js +117 -0
- package/dist/esm/ui/hooks/use-item-insert.js +103 -0
- package/dist/esm/ui/hooks/use-load-items.js +43 -0
- package/dist/esm/ui/hooks/use-on-force-select.js +35 -0
- package/dist/esm/utils.js +124 -0
- package/dist/types/api.d.ts +60 -0
- package/dist/types/commands/insert-type-ahead-item.d.ts +12 -0
- package/dist/types/commands/update-list-items.d.ts +3 -0
- package/dist/types/commands/update-query.d.ts +2 -0
- package/dist/types/commands/update-selected-index.d.ts +2 -0
- package/dist/types/index.d.ts +2 -1
- package/dist/types/insert-utils.d.ts +18 -0
- package/dist/types/messages.d.ts +72 -0
- package/dist/types/plugin.d.ts +10 -0
- package/dist/types/pm-plugins/actions.d.ts +9 -0
- package/dist/types/pm-plugins/decorations.d.ts +14 -0
- package/dist/types/pm-plugins/input-rules.d.ts +6 -0
- package/dist/types/pm-plugins/insert-item-plugin.d.ts +2 -0
- package/dist/types/pm-plugins/key.d.ts +3 -0
- package/dist/types/pm-plugins/main.d.ts +14 -0
- package/dist/types/pm-plugins/reducer.d.ts +10 -0
- package/dist/types/pm-plugins/utils.d.ts +4 -0
- package/dist/types/stats-modifier.d.ts +20 -0
- package/dist/types/transforms/close-type-ahead.d.ts +2 -0
- package/dist/types/transforms/open-typeahead-at-cursor.d.ts +11 -0
- package/dist/types/transforms/set-selection-before-query.d.ts +2 -0
- package/dist/types/ui/AssistiveText.d.ts +33 -0
- package/dist/types/ui/InputQuery.d.ts +26 -0
- package/dist/types/ui/TypeAheadList.d.ts +25 -0
- package/dist/types/ui/TypeAheadListItem.d.ts +18 -0
- package/dist/types/ui/TypeAheadPopup.d.ts +29 -0
- package/dist/types/ui/WrapperTypeAhead.d.ts +20 -0
- package/dist/types/ui/hooks/use-item-insert.d.ts +3 -0
- package/dist/types/ui/hooks/use-load-items.d.ts +3 -0
- package/dist/types/ui/hooks/use-on-force-select.d.ts +11 -0
- package/dist/types/utils.d.ts +27 -0
- package/dist/types-ts4.5/api.d.ts +60 -0
- package/dist/types-ts4.5/commands/insert-type-ahead-item.d.ts +12 -0
- package/dist/types-ts4.5/commands/update-list-items.d.ts +3 -0
- package/dist/types-ts4.5/commands/update-query.d.ts +2 -0
- package/dist/types-ts4.5/commands/update-selected-index.d.ts +2 -0
- package/dist/types-ts4.5/index.d.ts +2 -1
- package/dist/types-ts4.5/insert-utils.d.ts +18 -0
- package/dist/types-ts4.5/messages.d.ts +72 -0
- package/dist/types-ts4.5/plugin.d.ts +10 -0
- package/dist/types-ts4.5/pm-plugins/actions.d.ts +9 -0
- package/dist/types-ts4.5/pm-plugins/decorations.d.ts +14 -0
- package/dist/types-ts4.5/pm-plugins/input-rules.d.ts +6 -0
- package/dist/types-ts4.5/pm-plugins/insert-item-plugin.d.ts +2 -0
- package/dist/types-ts4.5/pm-plugins/key.d.ts +3 -0
- package/dist/types-ts4.5/pm-plugins/main.d.ts +14 -0
- package/dist/types-ts4.5/pm-plugins/reducer.d.ts +10 -0
- package/dist/types-ts4.5/pm-plugins/utils.d.ts +4 -0
- package/dist/types-ts4.5/stats-modifier.d.ts +20 -0
- package/dist/types-ts4.5/transforms/close-type-ahead.d.ts +2 -0
- package/dist/types-ts4.5/transforms/open-typeahead-at-cursor.d.ts +11 -0
- package/dist/types-ts4.5/transforms/set-selection-before-query.d.ts +2 -0
- package/dist/types-ts4.5/ui/AssistiveText.d.ts +33 -0
- package/dist/types-ts4.5/ui/InputQuery.d.ts +26 -0
- package/dist/types-ts4.5/ui/TypeAheadList.d.ts +25 -0
- package/dist/types-ts4.5/ui/TypeAheadListItem.d.ts +18 -0
- package/dist/types-ts4.5/ui/TypeAheadPopup.d.ts +29 -0
- package/dist/types-ts4.5/ui/WrapperTypeAhead.d.ts +20 -0
- package/dist/types-ts4.5/ui/hooks/use-item-insert.d.ts +7 -0
- package/dist/types-ts4.5/ui/hooks/use-load-items.d.ts +3 -0
- package/dist/types-ts4.5/ui/hooks/use-on-force-select.d.ts +11 -0
- package/dist/types-ts4.5/utils.d.ts +27 -0
- package/package.json +21 -25
- package/report.api.md +29 -1
- package/tmp/api-report-tmp.d.ts +26 -0
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { normaliseNestedLayout, safeInsert } from '@atlaskit/editor-common/insert';
|
|
2
|
+
import { Fragment, Node as PMNode, Slice } from '@atlaskit/editor-prosemirror/model';
|
|
3
|
+
import { NodeSelection, TextSelection } from '@atlaskit/editor-prosemirror/state';
|
|
4
|
+
function findInsertPoint(doc, pos, nodeToInsert) {
|
|
5
|
+
const $pos = doc.resolve(pos);
|
|
6
|
+
const createInsertPosition = (from, to) => ({
|
|
7
|
+
from,
|
|
8
|
+
to: to || from
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
// Search for a valid position for nodeToInsert in progressively higher levels
|
|
12
|
+
for (let level = $pos.depth; level >= 0; level--) {
|
|
13
|
+
const nodeAtLevel = $pos.node(level);
|
|
14
|
+
|
|
15
|
+
// Try to replace the empty paragraph in the level above
|
|
16
|
+
// Scenario:
|
|
17
|
+
// doc(
|
|
18
|
+
// table(
|
|
19
|
+
// row(
|
|
20
|
+
// cell(
|
|
21
|
+
// p('{<>}'),
|
|
22
|
+
// ),
|
|
23
|
+
// )
|
|
24
|
+
// )
|
|
25
|
+
// )
|
|
26
|
+
const levelAbove = Math.max(level - 1, 0);
|
|
27
|
+
const parentNode = $pos.node(levelAbove);
|
|
28
|
+
// Special case: when this is true, the 'to' position should be the end
|
|
29
|
+
// of the empty paragraph
|
|
30
|
+
const isNodeAtLevelEmptyParagraph = nodeAtLevel.type.name === 'paragraph' && nodeAtLevel.content.size === 0;
|
|
31
|
+
const indexAtLevelAbove = $pos.index(levelAbove);
|
|
32
|
+
const canReplaceNodeAtLevelAbove = parentNode.canReplaceWith(indexAtLevelAbove, indexAtLevelAbove, nodeToInsert.type);
|
|
33
|
+
if (isNodeAtLevelEmptyParagraph && canReplaceNodeAtLevelAbove) {
|
|
34
|
+
const from = $pos.posAtIndex(indexAtLevelAbove, levelAbove);
|
|
35
|
+
return createInsertPosition(from, from + nodeAtLevel.nodeSize);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Try to insert this node right after the node in the level above
|
|
39
|
+
// Scenario:
|
|
40
|
+
// doc(
|
|
41
|
+
// panel(
|
|
42
|
+
// p('{<>}'),
|
|
43
|
+
// )
|
|
44
|
+
// )
|
|
45
|
+
const indexAfterAtLevelAbove = $pos.indexAfter(levelAbove);
|
|
46
|
+
const canInsertNodeAtLevelAbove = parentNode.canReplaceWith(indexAfterAtLevelAbove, indexAfterAtLevelAbove, nodeToInsert.type);
|
|
47
|
+
if (canInsertNodeAtLevelAbove) {
|
|
48
|
+
return createInsertPosition($pos.posAtIndex(indexAfterAtLevelAbove, levelAbove));
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return createInsertPosition(0);
|
|
52
|
+
}
|
|
53
|
+
export const insertBlockNode = ({
|
|
54
|
+
node,
|
|
55
|
+
tr,
|
|
56
|
+
position
|
|
57
|
+
}) => {
|
|
58
|
+
const {
|
|
59
|
+
start,
|
|
60
|
+
end
|
|
61
|
+
} = position;
|
|
62
|
+
if (node.isText) {
|
|
63
|
+
return tr.replaceWith(start, end, node);
|
|
64
|
+
}
|
|
65
|
+
if (node.isBlock) {
|
|
66
|
+
tr.delete(start, end);
|
|
67
|
+
const mappedStart = tr.mapping.map(start);
|
|
68
|
+
const nodeNormalized = normaliseNestedLayout(tr, node);
|
|
69
|
+
|
|
70
|
+
// Handle edge cases for hr and mediaSingle
|
|
71
|
+
const inserted = safeInsert(nodeNormalized, mappedStart)(tr);
|
|
72
|
+
if (inserted) {
|
|
73
|
+
return tr;
|
|
74
|
+
}
|
|
75
|
+
const sliceInserted = Slice.maxOpen(Fragment.from(nodeNormalized));
|
|
76
|
+
const {
|
|
77
|
+
from,
|
|
78
|
+
to
|
|
79
|
+
} = findInsertPoint(tr.doc, mappedStart, nodeNormalized);
|
|
80
|
+
tr.replaceWith(from, to, node);
|
|
81
|
+
const openPosition = Math.min(from + (node.isAtom ? node.nodeSize : sliceInserted.openStart), tr.doc.content.size);
|
|
82
|
+
const FORWARD_DIRECTION = 1;
|
|
83
|
+
const nextSelection = TextSelection.findFrom(tr.doc.resolve(openPosition), FORWARD_DIRECTION, true);
|
|
84
|
+
if (nextSelection) {
|
|
85
|
+
return tr.setSelection(nextSelection);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
return tr;
|
|
89
|
+
};
|
|
90
|
+
export const insertInlineNodeOrFragment = ({
|
|
91
|
+
maybeFragment,
|
|
92
|
+
tr,
|
|
93
|
+
position,
|
|
94
|
+
selectInlineNode
|
|
95
|
+
}) => {
|
|
96
|
+
const {
|
|
97
|
+
start,
|
|
98
|
+
end
|
|
99
|
+
} = position;
|
|
100
|
+
const fragment = maybeFragment instanceof PMNode ? Fragment.from(maybeFragment) : maybeFragment;
|
|
101
|
+
tr.replaceWith(start, end, fragment);
|
|
102
|
+
if (selectInlineNode) {
|
|
103
|
+
return tr.setSelection(NodeSelection.create(tr.doc, start));
|
|
104
|
+
}
|
|
105
|
+
return tr.setSelection(TextSelection.near(tr.doc.resolve(start + fragment.size)));
|
|
106
|
+
};
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { defineMessages } from 'react-intl-next';
|
|
2
|
+
export const typeAheadListMessages = defineMessages({
|
|
3
|
+
typeAheadPopupLabel: {
|
|
4
|
+
id: 'fabric.editor.typeAhead.popupLabel',
|
|
5
|
+
defaultMessage: 'Typeahead results',
|
|
6
|
+
description: 'the result of a typeahead, similar to autocomplete results+'
|
|
7
|
+
},
|
|
8
|
+
quickInsertPopupLabel: {
|
|
9
|
+
id: 'fabric.editor.typeAhead.quickInsertPopupLabel',
|
|
10
|
+
defaultMessage: 'Shortcuts for inserts and formatting',
|
|
11
|
+
description: 'the result of a quick insert typeahead, similar to autocomplete results+'
|
|
12
|
+
},
|
|
13
|
+
quickInsertInputLabel: {
|
|
14
|
+
id: 'fabric.editor.typeAhead.quickInsertInputLabel',
|
|
15
|
+
defaultMessage: 'Begin typing to search or filter shortcut options',
|
|
16
|
+
description: 'assisitve text for typeahed input field'
|
|
17
|
+
},
|
|
18
|
+
emojiPopupLabel: {
|
|
19
|
+
id: 'fabric.editor.typeahead.emojiPopupLabel',
|
|
20
|
+
defaultMessage: 'Emoji shortcuts',
|
|
21
|
+
description: 'the result of a emoji typeahead, similar to autocomplete results+'
|
|
22
|
+
},
|
|
23
|
+
emojiInputLabel: {
|
|
24
|
+
id: 'fabric.editor.typeahead.emojiInputLabel',
|
|
25
|
+
defaultMessage: 'Begin typing to search or filter emoji options',
|
|
26
|
+
description: 'assisitve text for typeahed input field'
|
|
27
|
+
},
|
|
28
|
+
mentionPopupLabel: {
|
|
29
|
+
id: 'fabric.editor.typeahead.mentionPopupLabel',
|
|
30
|
+
defaultMessage: 'Users you can tag',
|
|
31
|
+
description: 'the aria label of a mention typeahead popup'
|
|
32
|
+
},
|
|
33
|
+
mentionInputLabel: {
|
|
34
|
+
id: 'fabric.editor.typeahead.mentionInputLabel',
|
|
35
|
+
defaultMessage: 'Begin typing to search for users to tag',
|
|
36
|
+
description: 'assisitve text for typeahed input field'
|
|
37
|
+
},
|
|
38
|
+
metionListItemLabel: {
|
|
39
|
+
id: 'fabric.editor.typeahead.metionListItemLabel',
|
|
40
|
+
defaultMessage: 'User {name} @{shortName}',
|
|
41
|
+
description: 'assistive text for user mention items username and nickname'
|
|
42
|
+
},
|
|
43
|
+
emojiListItemLabel: {
|
|
44
|
+
id: 'fabric.editor.typeahead.emojiListItemLabel',
|
|
45
|
+
defaultMessage: 'Emoji {name} Text Shortcut {shortcut}',
|
|
46
|
+
description: 'assistive text for emoji name and shortcut'
|
|
47
|
+
},
|
|
48
|
+
inputQueryAssistiveLabel: {
|
|
49
|
+
id: 'fabric.editor.inputQueryAssistiveTxt',
|
|
50
|
+
defaultMessage: 'When autocomplete results are available use up and down arrows to review and enter to select. Touch device users, explore by touch or with swipe gestures.',
|
|
51
|
+
description: 'Assistive text to the user when using typeahead shortcut'
|
|
52
|
+
},
|
|
53
|
+
searchResultsLabel: {
|
|
54
|
+
id: 'fabric.editor.searchResults',
|
|
55
|
+
defaultMessage: '{itemsLength, plural, one {# search result} other {# search results}} available',
|
|
56
|
+
description: 'Assistive text to the user when using typeahead shortcut and it preceeds with a number - Ex: 10 search results available'
|
|
57
|
+
},
|
|
58
|
+
noSearchResultsLabel: {
|
|
59
|
+
id: 'fabric.editor.noSearchResults',
|
|
60
|
+
defaultMessage: 'No search results',
|
|
61
|
+
description: 'Assistive text to the user when using typeahead shortcut'
|
|
62
|
+
},
|
|
63
|
+
descriptionLabel: {
|
|
64
|
+
id: 'fabric.editor.description',
|
|
65
|
+
defaultMessage: 'Description',
|
|
66
|
+
description: 'Description'
|
|
67
|
+
},
|
|
68
|
+
shortcutLabel: {
|
|
69
|
+
id: 'fabric.editor.shortcut',
|
|
70
|
+
defaultMessage: 'Text shortcut',
|
|
71
|
+
description: 'Text shortcut'
|
|
72
|
+
}
|
|
73
|
+
});
|
|
@@ -0,0 +1,381 @@
|
|
|
1
|
+
/**
|
|
2
|
+
*
|
|
3
|
+
* Revamped typeahead using decorations instead of the `typeAheadQuery` mark
|
|
4
|
+
*
|
|
5
|
+
* https://product-fabric.atlassian.net/wiki/spaces/E/pages/2992177582/Technical+TypeAhead+Data+Flow
|
|
6
|
+
*
|
|
7
|
+
*
|
|
8
|
+
*/
|
|
9
|
+
import React from 'react';
|
|
10
|
+
import { typeAheadQuery } from '@atlaskit/adf-schema';
|
|
11
|
+
import { ACTION, ACTION_SUBJECT, EVENT_TYPE, fireAnalyticsEvent, INPUT_METHOD } from '@atlaskit/editor-common/analytics';
|
|
12
|
+
import { SafePlugin } from '@atlaskit/editor-common/safe-plugin';
|
|
13
|
+
import { SelectItemMode } from '@atlaskit/editor-common/type-ahead';
|
|
14
|
+
import { WithPluginState } from '@atlaskit/editor-common/with-plugin-state';
|
|
15
|
+
import { insertTypeAheadItem } from './commands/insert-type-ahead-item';
|
|
16
|
+
import { updateSelectedIndex } from './commands/update-selected-index';
|
|
17
|
+
import { inputRulePlugin } from './pm-plugins/input-rules';
|
|
18
|
+
import { createPlugin as createInsertItemPlugin } from './pm-plugins/insert-item-plugin';
|
|
19
|
+
import { pluginKey as typeAheadPluginKey } from './pm-plugins/key';
|
|
20
|
+
import { createPlugin } from './pm-plugins/main';
|
|
21
|
+
import { StatsModifier } from './stats-modifier';
|
|
22
|
+
import { closeTypeAhead } from './transforms/close-type-ahead';
|
|
23
|
+
import { openTypeAheadAtCursor } from './transforms/open-typeahead-at-cursor';
|
|
24
|
+
import { useItemInsert } from './ui/hooks/use-item-insert';
|
|
25
|
+
import { TypeAheadPopup } from './ui/TypeAheadPopup';
|
|
26
|
+
import { findHandler, getPluginState, getTypeAheadHandler, getTypeAheadQuery, isTypeAheadAllowed, isTypeAheadOpen } from './utils';
|
|
27
|
+
const TypeAheadMenu = /*#__PURE__*/React.memo(({
|
|
28
|
+
editorView,
|
|
29
|
+
popupMountRef,
|
|
30
|
+
typeAheadState,
|
|
31
|
+
fireAnalyticsCallback
|
|
32
|
+
}) => {
|
|
33
|
+
var _popupMountRef$curren, _popupMountRef$curren2, _popupMountRef$curren3;
|
|
34
|
+
const isOpen = typeAheadState.decorationSet.find().length > 0;
|
|
35
|
+
const {
|
|
36
|
+
triggerHandler,
|
|
37
|
+
items,
|
|
38
|
+
selectedIndex,
|
|
39
|
+
decorationElement,
|
|
40
|
+
decorationSet,
|
|
41
|
+
query
|
|
42
|
+
} = typeAheadState;
|
|
43
|
+
const [onItemInsert, onTextInsert, onItemMatch] = useItemInsert(triggerHandler, editorView, items);
|
|
44
|
+
const setSelectedItem = React.useCallback(({
|
|
45
|
+
index: nextIndex
|
|
46
|
+
}) => {
|
|
47
|
+
queueMicrotask(() => {
|
|
48
|
+
updateSelectedIndex(nextIndex)(editorView.state, editorView.dispatch);
|
|
49
|
+
});
|
|
50
|
+
}, [editorView]);
|
|
51
|
+
const insertItem = React.useCallback((mode = SelectItemMode.SELECTED, index) => {
|
|
52
|
+
queueMicrotask(() => {
|
|
53
|
+
onItemInsert({
|
|
54
|
+
mode,
|
|
55
|
+
index,
|
|
56
|
+
query
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
}, [onItemInsert, query]);
|
|
60
|
+
const cancel = React.useCallback(({
|
|
61
|
+
setSelectionAt,
|
|
62
|
+
addPrefixTrigger,
|
|
63
|
+
forceFocusOnEditor
|
|
64
|
+
}) => {
|
|
65
|
+
const fullQuery = addPrefixTrigger ? `${triggerHandler === null || triggerHandler === void 0 ? void 0 : triggerHandler.trigger}${query}` : query;
|
|
66
|
+
onTextInsert({
|
|
67
|
+
forceFocusOnEditor,
|
|
68
|
+
setSelectionAt,
|
|
69
|
+
text: fullQuery
|
|
70
|
+
});
|
|
71
|
+
}, [triggerHandler, onTextInsert, query]);
|
|
72
|
+
React.useEffect(() => {
|
|
73
|
+
if (!isOpen || !query) {
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
const isLastCharSpace = query[query.length - 1] === ' ';
|
|
77
|
+
if (!isLastCharSpace) {
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
const result = onItemMatch({
|
|
81
|
+
mode: SelectItemMode.SPACE,
|
|
82
|
+
query: query.trim()
|
|
83
|
+
});
|
|
84
|
+
if (!result) {
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
}, [isOpen, query, onItemMatch]);
|
|
88
|
+
if (!isOpen || !triggerHandler || !(decorationElement instanceof HTMLElement) || items.length === 0) {
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
91
|
+
return /*#__PURE__*/React.createElement(TypeAheadPopup, {
|
|
92
|
+
editorView: editorView,
|
|
93
|
+
popupsMountPoint: (_popupMountRef$curren = popupMountRef.current) === null || _popupMountRef$curren === void 0 ? void 0 : _popupMountRef$curren.popupsMountPoint,
|
|
94
|
+
popupsBoundariesElement: (_popupMountRef$curren2 = popupMountRef.current) === null || _popupMountRef$curren2 === void 0 ? void 0 : _popupMountRef$curren2.popupsBoundariesElement,
|
|
95
|
+
popupsScrollableElement: (_popupMountRef$curren3 = popupMountRef.current) === null || _popupMountRef$curren3 === void 0 ? void 0 : _popupMountRef$curren3.popupsScrollableElement,
|
|
96
|
+
anchorElement: decorationElement,
|
|
97
|
+
triggerHandler: triggerHandler,
|
|
98
|
+
fireAnalyticsCallback: fireAnalyticsCallback,
|
|
99
|
+
items: items,
|
|
100
|
+
selectedIndex: selectedIndex,
|
|
101
|
+
setSelectedItem: setSelectedItem,
|
|
102
|
+
onItemInsert: insertItem,
|
|
103
|
+
decorationSet: decorationSet,
|
|
104
|
+
isEmptyQuery: !query,
|
|
105
|
+
cancel: cancel
|
|
106
|
+
});
|
|
107
|
+
});
|
|
108
|
+
const createOpenAtTransaction = editorAnalyticsAPI => props => tr => {
|
|
109
|
+
const {
|
|
110
|
+
triggerHandler,
|
|
111
|
+
inputMethod
|
|
112
|
+
} = props;
|
|
113
|
+
openTypeAheadAtCursor({
|
|
114
|
+
triggerHandler,
|
|
115
|
+
inputMethod
|
|
116
|
+
})({
|
|
117
|
+
tr
|
|
118
|
+
});
|
|
119
|
+
editorAnalyticsAPI === null || editorAnalyticsAPI === void 0 ? void 0 : editorAnalyticsAPI.attachAnalyticsEvent({
|
|
120
|
+
action: ACTION.INVOKED,
|
|
121
|
+
actionSubject: ACTION_SUBJECT.TYPEAHEAD,
|
|
122
|
+
actionSubjectId: triggerHandler.id,
|
|
123
|
+
attributes: {
|
|
124
|
+
inputMethod
|
|
125
|
+
},
|
|
126
|
+
eventType: EVENT_TYPE.UI
|
|
127
|
+
})(tr);
|
|
128
|
+
return true;
|
|
129
|
+
};
|
|
130
|
+
const createOpenTypeAhead = (editorViewRef, editorAnalyticsAPI) => props => {
|
|
131
|
+
if (!editorViewRef.current) {
|
|
132
|
+
return false;
|
|
133
|
+
}
|
|
134
|
+
const {
|
|
135
|
+
current: view
|
|
136
|
+
} = editorViewRef;
|
|
137
|
+
const {
|
|
138
|
+
tr
|
|
139
|
+
} = view.state;
|
|
140
|
+
createOpenAtTransaction(editorAnalyticsAPI)(props)(tr);
|
|
141
|
+
view.dispatch(tr);
|
|
142
|
+
return true;
|
|
143
|
+
};
|
|
144
|
+
const createInsertTypeAheadItem = editorViewRef => props => {
|
|
145
|
+
if (!editorViewRef.current) {
|
|
146
|
+
return false;
|
|
147
|
+
}
|
|
148
|
+
const {
|
|
149
|
+
current: view
|
|
150
|
+
} = editorViewRef;
|
|
151
|
+
const {
|
|
152
|
+
triggerHandler,
|
|
153
|
+
contentItem,
|
|
154
|
+
query,
|
|
155
|
+
sourceListItem,
|
|
156
|
+
mode
|
|
157
|
+
} = props;
|
|
158
|
+
insertTypeAheadItem(view)({
|
|
159
|
+
handler: triggerHandler,
|
|
160
|
+
item: contentItem,
|
|
161
|
+
mode: mode || SelectItemMode.SELECTED,
|
|
162
|
+
query,
|
|
163
|
+
sourceListItem
|
|
164
|
+
});
|
|
165
|
+
return true;
|
|
166
|
+
};
|
|
167
|
+
const createFindHandlerByTrigger = editorViewRef => trigger => {
|
|
168
|
+
if (!editorViewRef.current) {
|
|
169
|
+
return null;
|
|
170
|
+
}
|
|
171
|
+
const {
|
|
172
|
+
current: view
|
|
173
|
+
} = editorViewRef;
|
|
174
|
+
return findHandler(trigger, view.state);
|
|
175
|
+
};
|
|
176
|
+
const createCloseTypeAhead = editorViewRef => options => {
|
|
177
|
+
if (!editorViewRef.current) {
|
|
178
|
+
return false;
|
|
179
|
+
}
|
|
180
|
+
const {
|
|
181
|
+
current: view
|
|
182
|
+
} = editorViewRef;
|
|
183
|
+
const currentQuery = getTypeAheadQuery(view.state) || '';
|
|
184
|
+
const {
|
|
185
|
+
state
|
|
186
|
+
} = view;
|
|
187
|
+
let tr = state.tr;
|
|
188
|
+
if (options.attachCommand) {
|
|
189
|
+
const fakeDispatch = customTr => {
|
|
190
|
+
tr = customTr;
|
|
191
|
+
};
|
|
192
|
+
options.attachCommand(state, fakeDispatch);
|
|
193
|
+
}
|
|
194
|
+
closeTypeAhead(tr);
|
|
195
|
+
if (options.insertCurrentQueryAsRawText && currentQuery && currentQuery.length > 0) {
|
|
196
|
+
const handler = getTypeAheadHandler(state);
|
|
197
|
+
if (!handler) {
|
|
198
|
+
return false;
|
|
199
|
+
}
|
|
200
|
+
const text = handler.trigger.concat(currentQuery);
|
|
201
|
+
tr.replaceSelectionWith(state.schema.text(text));
|
|
202
|
+
}
|
|
203
|
+
view.dispatch(tr);
|
|
204
|
+
if (!view.hasFocus()) {
|
|
205
|
+
view.focus();
|
|
206
|
+
}
|
|
207
|
+
return true;
|
|
208
|
+
};
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
*
|
|
212
|
+
* Revamped typeahead using decorations instead of the `typeAheadQuery` mark
|
|
213
|
+
*
|
|
214
|
+
* https://product-fabric.atlassian.net/wiki/spaces/E/pages/2992177582/Technical+TypeAhead+Data+Flow
|
|
215
|
+
*
|
|
216
|
+
*
|
|
217
|
+
*/
|
|
218
|
+
export const typeAheadPlugin = ({
|
|
219
|
+
config: options,
|
|
220
|
+
api
|
|
221
|
+
}) => {
|
|
222
|
+
var _api$analytics, _api$analytics2;
|
|
223
|
+
const fireAnalyticsCallback = fireAnalyticsEvent(options === null || options === void 0 ? void 0 : options.createAnalyticsEvent);
|
|
224
|
+
const popupMountRef = {
|
|
225
|
+
current: null
|
|
226
|
+
};
|
|
227
|
+
const editorViewRef = {
|
|
228
|
+
current: null
|
|
229
|
+
};
|
|
230
|
+
return {
|
|
231
|
+
name: 'typeAhead',
|
|
232
|
+
marks() {
|
|
233
|
+
// We need to keep this to make sure
|
|
234
|
+
// All documents with typeahead marks will be loaded normally
|
|
235
|
+
return [{
|
|
236
|
+
name: 'typeAheadQuery',
|
|
237
|
+
mark: typeAheadQuery
|
|
238
|
+
}];
|
|
239
|
+
},
|
|
240
|
+
pmPlugins(typeAhead = []) {
|
|
241
|
+
return [{
|
|
242
|
+
name: 'typeAhead',
|
|
243
|
+
plugin: ({
|
|
244
|
+
dispatch,
|
|
245
|
+
getIntl
|
|
246
|
+
}) => createPlugin({
|
|
247
|
+
getIntl,
|
|
248
|
+
popupMountRef,
|
|
249
|
+
reactDispatch: dispatch,
|
|
250
|
+
typeAheadHandlers: typeAhead,
|
|
251
|
+
createAnalyticsEvent: options === null || options === void 0 ? void 0 : options.createAnalyticsEvent
|
|
252
|
+
})
|
|
253
|
+
}, {
|
|
254
|
+
name: 'typeAheadEditorViewRef',
|
|
255
|
+
plugin: () => {
|
|
256
|
+
return new SafePlugin({
|
|
257
|
+
view(view) {
|
|
258
|
+
editorViewRef.current = view;
|
|
259
|
+
return {
|
|
260
|
+
destroy() {
|
|
261
|
+
editorViewRef.current = null;
|
|
262
|
+
}
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
}, {
|
|
268
|
+
name: 'typeAheadInsertItem',
|
|
269
|
+
plugin: createInsertItemPlugin
|
|
270
|
+
}, {
|
|
271
|
+
name: 'typeAheadInputRule',
|
|
272
|
+
plugin: ({
|
|
273
|
+
schema,
|
|
274
|
+
featureFlags
|
|
275
|
+
}) => inputRulePlugin(schema, typeAhead, featureFlags)
|
|
276
|
+
}];
|
|
277
|
+
},
|
|
278
|
+
getSharedState(editorState) {
|
|
279
|
+
if (!editorState) {
|
|
280
|
+
return {
|
|
281
|
+
query: '',
|
|
282
|
+
isOpen: false,
|
|
283
|
+
isAllowed: false,
|
|
284
|
+
currentHandler: undefined
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
const isOpen = isTypeAheadOpen(editorState);
|
|
288
|
+
return {
|
|
289
|
+
query: getTypeAheadQuery(editorState) || '',
|
|
290
|
+
currentHandler: getTypeAheadHandler(editorState),
|
|
291
|
+
isOpen,
|
|
292
|
+
isAllowed: !isOpen
|
|
293
|
+
};
|
|
294
|
+
},
|
|
295
|
+
actions: {
|
|
296
|
+
isOpen: isTypeAheadOpen,
|
|
297
|
+
isAllowed: isTypeAheadAllowed,
|
|
298
|
+
open: createOpenTypeAhead(editorViewRef, api === null || api === void 0 ? void 0 : (_api$analytics = api.analytics) === null || _api$analytics === void 0 ? void 0 : _api$analytics.actions),
|
|
299
|
+
openAtTransaction: createOpenAtTransaction(api === null || api === void 0 ? void 0 : (_api$analytics2 = api.analytics) === null || _api$analytics2 === void 0 ? void 0 : _api$analytics2.actions),
|
|
300
|
+
findHandlerByTrigger: createFindHandlerByTrigger(editorViewRef),
|
|
301
|
+
insert: createInsertTypeAheadItem(editorViewRef),
|
|
302
|
+
close: createCloseTypeAhead(editorViewRef)
|
|
303
|
+
},
|
|
304
|
+
contentComponent({
|
|
305
|
+
editorView,
|
|
306
|
+
containerElement,
|
|
307
|
+
popupsMountPoint,
|
|
308
|
+
popupsBoundariesElement,
|
|
309
|
+
popupsScrollableElement,
|
|
310
|
+
wrapperElement
|
|
311
|
+
}) {
|
|
312
|
+
popupMountRef.current = {
|
|
313
|
+
popupsMountPoint: popupsMountPoint || wrapperElement || undefined,
|
|
314
|
+
popupsBoundariesElement,
|
|
315
|
+
popupsScrollableElement: popupsScrollableElement || containerElement || undefined
|
|
316
|
+
};
|
|
317
|
+
return /*#__PURE__*/React.createElement(WithPluginState, {
|
|
318
|
+
plugins: {
|
|
319
|
+
typeAheadState: typeAheadPluginKey
|
|
320
|
+
},
|
|
321
|
+
render: ({
|
|
322
|
+
typeAheadState
|
|
323
|
+
}) => {
|
|
324
|
+
if (!typeAheadState) {
|
|
325
|
+
return null;
|
|
326
|
+
}
|
|
327
|
+
return /*#__PURE__*/React.createElement(TypeAheadMenu, {
|
|
328
|
+
editorView: editorView,
|
|
329
|
+
popupMountRef: popupMountRef,
|
|
330
|
+
typeAheadState: typeAheadState,
|
|
331
|
+
fireAnalyticsCallback: fireAnalyticsCallback
|
|
332
|
+
});
|
|
333
|
+
}
|
|
334
|
+
});
|
|
335
|
+
},
|
|
336
|
+
onEditorViewStateUpdated({
|
|
337
|
+
originalTransaction,
|
|
338
|
+
oldEditorState,
|
|
339
|
+
newEditorState
|
|
340
|
+
}) {
|
|
341
|
+
const oldPluginState = getPluginState(oldEditorState);
|
|
342
|
+
const newPluginState = getPluginState(newEditorState);
|
|
343
|
+
if (!oldPluginState || !newPluginState) {
|
|
344
|
+
return;
|
|
345
|
+
}
|
|
346
|
+
const {
|
|
347
|
+
triggerHandler: oldTriggerHandler
|
|
348
|
+
} = oldPluginState;
|
|
349
|
+
const {
|
|
350
|
+
triggerHandler: newTriggerHandler
|
|
351
|
+
} = newPluginState;
|
|
352
|
+
const isANewHandler = oldTriggerHandler !== newTriggerHandler;
|
|
353
|
+
if (oldTriggerHandler !== null && oldTriggerHandler !== void 0 && oldTriggerHandler.dismiss && isANewHandler) {
|
|
354
|
+
const typeAheadMessage = originalTransaction.getMeta(typeAheadPluginKey);
|
|
355
|
+
const wasItemInserted = typeAheadMessage && typeAheadMessage.action === 'INSERT_RAW_QUERY';
|
|
356
|
+
oldTriggerHandler.dismiss({
|
|
357
|
+
editorState: newEditorState,
|
|
358
|
+
query: oldPluginState.query,
|
|
359
|
+
stats: (oldPluginState.stats || new StatsModifier()).serialize(),
|
|
360
|
+
wasItemInserted
|
|
361
|
+
});
|
|
362
|
+
}
|
|
363
|
+
if (newTriggerHandler !== null && newTriggerHandler !== void 0 && newTriggerHandler.onOpen && isANewHandler) {
|
|
364
|
+
newTriggerHandler.onOpen(newEditorState);
|
|
365
|
+
}
|
|
366
|
+
if (newTriggerHandler && isANewHandler && options !== null && options !== void 0 && options.createAnalyticsEvent) {
|
|
367
|
+
fireAnalyticsCallback({
|
|
368
|
+
payload: {
|
|
369
|
+
action: ACTION.INVOKED,
|
|
370
|
+
actionSubject: ACTION_SUBJECT.TYPEAHEAD,
|
|
371
|
+
actionSubjectId: newTriggerHandler.id || 'not_set',
|
|
372
|
+
attributes: {
|
|
373
|
+
inputMethod: newPluginState.inputMethod || INPUT_METHOD.KEYBOARD
|
|
374
|
+
},
|
|
375
|
+
eventType: EVENT_TYPE.UI
|
|
376
|
+
}
|
|
377
|
+
});
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
};
|
|
381
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export let ACTIONS = /*#__PURE__*/function (ACTIONS) {
|
|
2
|
+
ACTIONS["OPEN_TYPEAHEAD_AT_CURSOR"] = "OPEN_TYPEAHEAD_AT_CURSOR";
|
|
3
|
+
ACTIONS["CLOSE_TYPE_AHEAD"] = "CLOSE_TYPE_AHEAD";
|
|
4
|
+
ACTIONS["CHANGE_QUERY"] = "CHANGE_QUERY";
|
|
5
|
+
ACTIONS["INSERT_ITEM"] = "INSERT_ITEM";
|
|
6
|
+
ACTIONS["INSERT_RAW_QUERY"] = "INSERT_RAW_QUERY";
|
|
7
|
+
ACTIONS["UPDATE_LIST_ITEMS"] = "UPDATE_LIST_ITEMS";
|
|
8
|
+
ACTIONS["UPDATE_SELECTED_INDEX"] = "UPDATE_SELECTED_INDEX";
|
|
9
|
+
return ACTIONS;
|
|
10
|
+
}({});
|