@atlaskit/editor-plugin-type-ahead 0.5.0 → 0.7.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.
Files changed (167) hide show
  1. package/.eslintrc.js +11 -0
  2. package/CHANGELOG.md +12 -0
  3. package/dist/cjs/api.js +215 -0
  4. package/dist/cjs/commands/insert-type-ahead-item.js +205 -0
  5. package/dist/cjs/commands/update-list-items.js +23 -0
  6. package/dist/cjs/commands/update-query.js +27 -0
  7. package/dist/cjs/commands/update-selected-index.js +27 -0
  8. package/dist/cjs/constants.js +15 -0
  9. package/dist/cjs/index.js +8 -1
  10. package/dist/cjs/insert-utils.js +107 -0
  11. package/dist/cjs/messages.js +79 -0
  12. package/dist/cjs/plugin.js +382 -0
  13. package/dist/cjs/pm-plugins/actions.js +16 -0
  14. package/dist/cjs/pm-plugins/decorations.js +148 -0
  15. package/dist/cjs/pm-plugins/input-rules.js +36 -0
  16. package/dist/cjs/pm-plugins/insert-item-plugin.js +22 -0
  17. package/dist/cjs/pm-plugins/key.js +8 -0
  18. package/dist/cjs/pm-plugins/main.js +110 -0
  19. package/dist/cjs/pm-plugins/reducer.js +158 -0
  20. package/dist/cjs/pm-plugins/utils.js +18 -0
  21. package/dist/cjs/stats-modifier.js +42 -0
  22. package/dist/cjs/transforms/close-type-ahead.js +13 -0
  23. package/dist/cjs/transforms/open-typeahead-at-cursor.js +75 -0
  24. package/dist/cjs/transforms/set-selection-before-query.js +18 -0
  25. package/dist/cjs/ui/AssistiveText.js +120 -0
  26. package/dist/cjs/ui/InputQuery.js +400 -0
  27. package/dist/cjs/ui/TypeAheadList.js +285 -0
  28. package/dist/cjs/ui/TypeAheadListItem.js +181 -0
  29. package/dist/cjs/ui/TypeAheadPopup.js +230 -0
  30. package/dist/cjs/ui/WrapperTypeAhead.js +127 -0
  31. package/dist/cjs/ui/hooks/use-item-insert.js +109 -0
  32. package/dist/cjs/ui/hooks/use-load-items.js +50 -0
  33. package/dist/cjs/ui/hooks/use-on-force-select.js +41 -0
  34. package/dist/cjs/utils.js +130 -0
  35. package/dist/es2019/api.js +205 -0
  36. package/dist/es2019/commands/insert-type-ahead-item.js +204 -0
  37. package/dist/es2019/commands/update-list-items.js +17 -0
  38. package/dist/es2019/commands/update-query.js +21 -0
  39. package/dist/es2019/commands/update-selected-index.js +21 -0
  40. package/dist/es2019/constants.js +9 -0
  41. package/dist/es2019/index.js +1 -1
  42. package/dist/es2019/insert-utils.js +106 -0
  43. package/dist/es2019/messages.js +73 -0
  44. package/dist/es2019/plugin.js +381 -0
  45. package/dist/es2019/pm-plugins/actions.js +10 -0
  46. package/dist/es2019/pm-plugins/decorations.js +148 -0
  47. package/dist/es2019/pm-plugins/input-rules.js +29 -0
  48. package/dist/es2019/pm-plugins/insert-item-plugin.js +16 -0
  49. package/dist/es2019/pm-plugins/key.js +2 -0
  50. package/dist/es2019/pm-plugins/main.js +106 -0
  51. package/dist/es2019/pm-plugins/reducer.js +160 -0
  52. package/dist/es2019/pm-plugins/utils.js +12 -0
  53. package/dist/es2019/stats-modifier.js +33 -0
  54. package/dist/es2019/transforms/close-type-ahead.js +7 -0
  55. package/dist/es2019/transforms/open-typeahead-at-cursor.js +71 -0
  56. package/dist/es2019/transforms/set-selection-before-query.js +10 -0
  57. package/dist/es2019/ui/AssistiveText.js +88 -0
  58. package/dist/es2019/ui/InputQuery.js +393 -0
  59. package/dist/es2019/ui/TypeAheadList.js +273 -0
  60. package/dist/es2019/ui/TypeAheadListItem.js +216 -0
  61. package/dist/es2019/ui/TypeAheadPopup.js +233 -0
  62. package/dist/es2019/ui/WrapperTypeAhead.js +109 -0
  63. package/dist/es2019/ui/hooks/use-item-insert.js +112 -0
  64. package/dist/es2019/ui/hooks/use-load-items.js +41 -0
  65. package/dist/es2019/ui/hooks/use-on-force-select.js +38 -0
  66. package/dist/es2019/utils.js +126 -0
  67. package/dist/esm/api.js +209 -0
  68. package/dist/esm/commands/insert-type-ahead-item.js +198 -0
  69. package/dist/esm/commands/update-list-items.js +17 -0
  70. package/dist/esm/commands/update-query.js +21 -0
  71. package/dist/esm/commands/update-selected-index.js +21 -0
  72. package/dist/esm/constants.js +9 -0
  73. package/dist/esm/index.js +1 -1
  74. package/dist/esm/insert-utils.js +101 -0
  75. package/dist/esm/messages.js +73 -0
  76. package/dist/esm/plugin.js +374 -0
  77. package/dist/esm/pm-plugins/actions.js +10 -0
  78. package/dist/esm/pm-plugins/decorations.js +141 -0
  79. package/dist/esm/pm-plugins/input-rules.js +29 -0
  80. package/dist/esm/pm-plugins/insert-item-plugin.js +16 -0
  81. package/dist/esm/pm-plugins/key.js +2 -0
  82. package/dist/esm/pm-plugins/main.js +104 -0
  83. package/dist/esm/pm-plugins/reducer.js +151 -0
  84. package/dist/esm/pm-plugins/utils.js +12 -0
  85. package/dist/esm/stats-modifier.js +35 -0
  86. package/dist/esm/transforms/close-type-ahead.js +7 -0
  87. package/dist/esm/transforms/open-typeahead-at-cursor.js +69 -0
  88. package/dist/esm/transforms/set-selection-before-query.js +12 -0
  89. package/dist/esm/ui/AssistiveText.js +115 -0
  90. package/dist/esm/ui/InputQuery.js +390 -0
  91. package/dist/esm/ui/TypeAheadList.js +276 -0
  92. package/dist/esm/ui/TypeAheadListItem.js +171 -0
  93. package/dist/esm/ui/TypeAheadPopup.js +220 -0
  94. package/dist/esm/ui/WrapperTypeAhead.js +117 -0
  95. package/dist/esm/ui/hooks/use-item-insert.js +103 -0
  96. package/dist/esm/ui/hooks/use-load-items.js +43 -0
  97. package/dist/esm/ui/hooks/use-on-force-select.js +35 -0
  98. package/dist/esm/utils.js +124 -0
  99. package/dist/types/api.d.ts +61 -0
  100. package/dist/types/commands/insert-type-ahead-item.d.ts +12 -0
  101. package/dist/types/commands/update-list-items.d.ts +3 -0
  102. package/dist/types/commands/update-query.d.ts +2 -0
  103. package/dist/types/commands/update-selected-index.d.ts +2 -0
  104. package/dist/types/constants.d.ts +8 -0
  105. package/dist/types/index.d.ts +2 -1
  106. package/dist/types/insert-utils.d.ts +18 -0
  107. package/dist/types/messages.d.ts +72 -0
  108. package/dist/types/plugin.d.ts +10 -0
  109. package/dist/types/pm-plugins/actions.d.ts +9 -0
  110. package/dist/types/pm-plugins/decorations.d.ts +14 -0
  111. package/dist/types/pm-plugins/input-rules.d.ts +6 -0
  112. package/dist/types/pm-plugins/insert-item-plugin.d.ts +2 -0
  113. package/dist/types/pm-plugins/key.d.ts +3 -0
  114. package/dist/types/pm-plugins/main.d.ts +14 -0
  115. package/dist/types/pm-plugins/reducer.d.ts +10 -0
  116. package/dist/types/pm-plugins/utils.d.ts +4 -0
  117. package/dist/types/stats-modifier.d.ts +20 -0
  118. package/dist/types/transforms/close-type-ahead.d.ts +2 -0
  119. package/dist/types/transforms/open-typeahead-at-cursor.d.ts +11 -0
  120. package/dist/types/transforms/set-selection-before-query.d.ts +2 -0
  121. package/dist/types/types.d.ts +64 -3
  122. package/dist/types/ui/AssistiveText.d.ts +33 -0
  123. package/dist/types/ui/InputQuery.d.ts +26 -0
  124. package/dist/types/ui/TypeAheadList.d.ts +25 -0
  125. package/dist/types/ui/TypeAheadListItem.d.ts +18 -0
  126. package/dist/types/ui/TypeAheadPopup.d.ts +29 -0
  127. package/dist/types/ui/WrapperTypeAhead.d.ts +20 -0
  128. package/dist/types/ui/hooks/use-item-insert.d.ts +3 -0
  129. package/dist/types/ui/hooks/use-load-items.d.ts +3 -0
  130. package/dist/types/ui/hooks/use-on-force-select.d.ts +11 -0
  131. package/dist/types/utils.d.ts +27 -0
  132. package/dist/types-ts4.5/api.d.ts +61 -0
  133. package/dist/types-ts4.5/commands/insert-type-ahead-item.d.ts +12 -0
  134. package/dist/types-ts4.5/commands/update-list-items.d.ts +3 -0
  135. package/dist/types-ts4.5/commands/update-query.d.ts +2 -0
  136. package/dist/types-ts4.5/commands/update-selected-index.d.ts +2 -0
  137. package/dist/types-ts4.5/constants.d.ts +8 -0
  138. package/dist/types-ts4.5/index.d.ts +2 -1
  139. package/dist/types-ts4.5/insert-utils.d.ts +18 -0
  140. package/dist/types-ts4.5/messages.d.ts +72 -0
  141. package/dist/types-ts4.5/plugin.d.ts +10 -0
  142. package/dist/types-ts4.5/pm-plugins/actions.d.ts +9 -0
  143. package/dist/types-ts4.5/pm-plugins/decorations.d.ts +14 -0
  144. package/dist/types-ts4.5/pm-plugins/input-rules.d.ts +6 -0
  145. package/dist/types-ts4.5/pm-plugins/insert-item-plugin.d.ts +2 -0
  146. package/dist/types-ts4.5/pm-plugins/key.d.ts +3 -0
  147. package/dist/types-ts4.5/pm-plugins/main.d.ts +14 -0
  148. package/dist/types-ts4.5/pm-plugins/reducer.d.ts +10 -0
  149. package/dist/types-ts4.5/pm-plugins/utils.d.ts +4 -0
  150. package/dist/types-ts4.5/stats-modifier.d.ts +20 -0
  151. package/dist/types-ts4.5/transforms/close-type-ahead.d.ts +2 -0
  152. package/dist/types-ts4.5/transforms/open-typeahead-at-cursor.d.ts +11 -0
  153. package/dist/types-ts4.5/transforms/set-selection-before-query.d.ts +2 -0
  154. package/dist/types-ts4.5/types.d.ts +64 -3
  155. package/dist/types-ts4.5/ui/AssistiveText.d.ts +33 -0
  156. package/dist/types-ts4.5/ui/InputQuery.d.ts +26 -0
  157. package/dist/types-ts4.5/ui/TypeAheadList.d.ts +25 -0
  158. package/dist/types-ts4.5/ui/TypeAheadListItem.d.ts +18 -0
  159. package/dist/types-ts4.5/ui/TypeAheadPopup.d.ts +29 -0
  160. package/dist/types-ts4.5/ui/WrapperTypeAhead.d.ts +20 -0
  161. package/dist/types-ts4.5/ui/hooks/use-item-insert.d.ts +7 -0
  162. package/dist/types-ts4.5/ui/hooks/use-load-items.d.ts +3 -0
  163. package/dist/types-ts4.5/ui/hooks/use-on-force-select.d.ts +11 -0
  164. package/dist/types-ts4.5/utils.d.ts +27 -0
  165. package/package.json +21 -28
  166. package/report.api.md +32 -1
  167. package/tmp/api-report-tmp.d.ts +29 -0
@@ -0,0 +1,148 @@
1
+ import React from 'react';
2
+ import ReactDOM from 'react-dom';
3
+ import { IntlProvider } from 'react-intl-next';
4
+ import uuid from 'uuid';
5
+ import { keyName as keyNameNormalized } from 'w3c-keyname';
6
+ import { redo, undo } from '@atlaskit/editor-prosemirror/history';
7
+ import { TextSelection } from '@atlaskit/editor-prosemirror/state';
8
+ import { Decoration, DecorationSet } from '@atlaskit/editor-prosemirror/view';
9
+ import { B400 } from '@atlaskit/theme/colors';
10
+ import { TYPE_AHEAD_DECORATION_DATA_ATTRIBUTE, TYPE_AHEAD_DECORATION_KEY } from '../constants';
11
+ import { StatsModifier } from '../stats-modifier';
12
+ import { closeTypeAhead } from '../transforms/close-type-ahead';
13
+ import { WrapperTypeAhead } from '../ui/WrapperTypeAhead';
14
+ import { getTypeAheadQuery } from '../utils';
15
+ export const factoryDecorations = ({
16
+ intl,
17
+ popupMountRef,
18
+ createAnalyticsEvent
19
+ }) => {
20
+ const createDecorations = (tr, {
21
+ triggerHandler,
22
+ inputMethod,
23
+ reopenQuery
24
+ }) => {
25
+ const {
26
+ selection
27
+ } = tr;
28
+ if (!(selection instanceof TextSelection) || !selection.$cursor) {
29
+ return {
30
+ decorationSet: DecorationSet.empty,
31
+ stats: null,
32
+ decorationElement: null
33
+ };
34
+ }
35
+ const decorationId = `decoration_id_${TYPE_AHEAD_DECORATION_KEY}_${uuid()}`;
36
+ const {
37
+ $cursor
38
+ } = selection;
39
+ const typeaheadComponent = document.createElement('mark');
40
+ const stats = new StatsModifier();
41
+ let shouldFocusCursorInsideQuery = true;
42
+ const deco = Decoration.widget($cursor.pos, (editorView, getDecorationPosition) => {
43
+ var _popupMountRef$curren, _popupMountRef$curren2, _popupMountRef$curren3;
44
+ typeaheadComponent.setAttribute('id', decorationId);
45
+ typeaheadComponent.setAttribute('role', 'search');
46
+ typeaheadComponent.dataset.typeAheadQuery = 'true';
47
+ typeaheadComponent.dataset.trigger = triggerHandler.trigger;
48
+
49
+ // This line below seems weird,
50
+ // we need that cuz the clickAreaHelper
51
+ // will try to hijack any click event coming
52
+ // from the inside of the Editor
53
+ // packages/editor/editor-core/src/ui/Addon/click-area-helper.ts
54
+ typeaheadComponent.dataset.editorPopup = 'true';
55
+ typeaheadComponent.dataset.typeAhead = TYPE_AHEAD_DECORATION_DATA_ATTRIBUTE;
56
+ typeaheadComponent.style.color = `var(--ds-text-accent-blue, ${B400})`;
57
+ typeaheadComponent.style.backgroundColor = 'transparent';
58
+ const onUndoRedo = inputType => {
59
+ if (!['historyUndo', 'historyRedo'].includes(inputType)) {
60
+ return false;
61
+ }
62
+ const hasReopenQuery = typeof reopenQuery === 'string' && reopenQuery.trim().length > 0;
63
+ const currentQuery = getTypeAheadQuery(editorView.state);
64
+ if (hasReopenQuery || (currentQuery === null || currentQuery === void 0 ? void 0 : currentQuery.length) === 0) {
65
+ const command = inputType === 'historyUndo' ? undo : redo;
66
+ let tr = editorView.state.tr;
67
+ const fakeDispatch = customTr => {
68
+ tr = customTr;
69
+ };
70
+ const result = command(editorView.state, fakeDispatch);
71
+ if (result) {
72
+ closeTypeAhead(tr);
73
+ editorView.dispatch(tr);
74
+ editorView.focus();
75
+ }
76
+ return result;
77
+ }
78
+ return false;
79
+ };
80
+ ReactDOM.render( /*#__PURE__*/React.createElement(IntlProvider, {
81
+ locale: intl.locale || 'en',
82
+ messages: intl.messages,
83
+ formats: intl.formats
84
+ }, /*#__PURE__*/React.createElement(WrapperTypeAhead, {
85
+ triggerHandler: triggerHandler,
86
+ editorView: editorView,
87
+ anchorElement: typeaheadComponent,
88
+ createAnalyticsEvent: createAnalyticsEvent,
89
+ inputMethod: inputMethod,
90
+ getDecorationPosition: getDecorationPosition,
91
+ shouldFocusCursorInsideQuery: shouldFocusCursorInsideQuery,
92
+ popupsMountPoint: (_popupMountRef$curren = popupMountRef.current) === null || _popupMountRef$curren === void 0 ? void 0 : _popupMountRef$curren.popupsMountPoint,
93
+ popupsBoundariesElement: (_popupMountRef$curren2 = popupMountRef.current) === null || _popupMountRef$curren2 === void 0 ? void 0 : _popupMountRef$curren2.popupsBoundariesElement,
94
+ popupsScrollableElement: (_popupMountRef$curren3 = popupMountRef.current) === null || _popupMountRef$curren3 === void 0 ? void 0 : _popupMountRef$curren3.popupsScrollableElement,
95
+ onUndoRedo: onUndoRedo,
96
+ reopenQuery: reopenQuery
97
+ })), typeaheadComponent);
98
+ shouldFocusCursorInsideQuery = false;
99
+ return typeaheadComponent;
100
+ }, {
101
+ isTypeAheadDecoration: true,
102
+ key: decorationId,
103
+ side: 0,
104
+ stopEvent: e => {
105
+ const key = keyNameNormalized(e);
106
+ const sel = document.getSelection();
107
+ if ('ArrowLeft' === key && (sel === null || sel === void 0 ? void 0 : sel.anchorOffset) === 0) {
108
+ return false;
109
+ }
110
+ return true;
111
+ },
112
+ ignoreSelection: false
113
+ });
114
+ return {
115
+ decorationSet: DecorationSet.create(tr.doc, [deco]),
116
+ decorationElement: typeaheadComponent,
117
+ stats
118
+ };
119
+ };
120
+ const removeDecorations = decorationSet => {
121
+ if (!decorationSet || decorationSet === DecorationSet.empty) {
122
+ return false;
123
+ }
124
+ const typeAheadDecorations = decorationSet.find(undefined, undefined, spec => {
125
+ return spec.isTypeAheadDecoration;
126
+ });
127
+ if (!typeAheadDecorations || typeAheadDecorations.length === 0) {
128
+ return false;
129
+ }
130
+ typeAheadDecorations.forEach(({
131
+ spec
132
+ }) => {
133
+ if (!spec.key) {
134
+ return;
135
+ }
136
+ const decoElement = document.querySelector(`#${spec.key}`);
137
+ if (!decoElement) {
138
+ return;
139
+ }
140
+ ReactDOM.unmountComponentAtNode(decoElement);
141
+ });
142
+ return true;
143
+ };
144
+ return {
145
+ createDecorations,
146
+ removeDecorations
147
+ };
148
+ };
@@ -0,0 +1,29 @@
1
+ import { INPUT_METHOD } from '@atlaskit/editor-common/analytics';
2
+ import { createPlugin, createRule, leafNodeReplacementCharacter } from '@atlaskit/prosemirror-input-rules';
3
+ import { openTypeAheadAtCursor } from '../transforms/open-typeahead-at-cursor';
4
+ export function inputRulePlugin(schema, typeAheads, featureFlags) {
5
+ if (!typeAheads || typeAheads.length === 0) {
6
+ return;
7
+ }
8
+ const rules = typeAheads.reduce((acc, typeAhead) => {
9
+ const trigger = typeAhead.customRegex || typeAhead.trigger;
10
+ if (!trigger) {
11
+ return acc;
12
+ }
13
+ const regex = new RegExp(`(^|[.!?\\s${leafNodeReplacementCharacter}])(${trigger})$`);
14
+ acc.push(createRule(regex, (state, match) => {
15
+ return openTypeAheadAtCursor({
16
+ triggerHandler: typeAhead,
17
+ inputMethod: INPUT_METHOD.KEYBOARD
18
+ })({
19
+ tr: state.tr
20
+ });
21
+ }));
22
+ return acc;
23
+ }, []);
24
+ const plugin = createPlugin('type-ahead', rules, {
25
+ allowInsertTextOnDocument: false
26
+ });
27
+ return plugin;
28
+ }
29
+ export default inputRulePlugin;
@@ -0,0 +1,16 @@
1
+ import { SafePlugin } from '@atlaskit/editor-common/safe-plugin';
2
+ import { ACTIONS } from './actions';
3
+ import { isInsertionTransaction } from './utils';
4
+ export function createPlugin() {
5
+ return new SafePlugin({
6
+ appendTransaction(transactions, _oldState, newState) {
7
+ const insertItemCallback = isInsertionTransaction(transactions, ACTIONS.INSERT_ITEM);
8
+ if (insertItemCallback) {
9
+ const tr = insertItemCallback(newState);
10
+ if (tr) {
11
+ return tr;
12
+ }
13
+ }
14
+ }
15
+ });
16
+ }
@@ -0,0 +1,2 @@
1
+ import { PluginKey } from '@atlaskit/editor-prosemirror/state';
2
+ export const pluginKey = new PluginKey('typeAheadPlugin');
@@ -0,0 +1,106 @@
1
+ import { InsertTypeAheadStep } from '@atlaskit/adf-schema/steps';
2
+ import { SafePlugin } from '@atlaskit/editor-common/safe-plugin';
3
+ import { closest } from '@atlaskit/editor-common/utils';
4
+ import { DecorationSet } from '@atlaskit/editor-prosemirror/view';
5
+ import { TYPE_AHEAD_DECORATION_DATA_ATTRIBUTE } from '../constants';
6
+ import { ACTIONS } from './actions';
7
+ import { factoryDecorations } from './decorations';
8
+ import { pluginKey } from './key';
9
+ import { createReducer } from './reducer';
10
+ import { isInsertionTransaction } from './utils';
11
+ const hasValidTypeAheadStep = tr => {
12
+ const steps = tr.steps.filter(step => step instanceof InsertTypeAheadStep);
13
+
14
+ // There are some cases, like collab rebase, where the steps are re-applied
15
+ // We should not re open the type-ahead for those cases
16
+ if (steps.length === 1) {
17
+ return steps[0];
18
+ }
19
+ return null;
20
+ };
21
+ export function createPlugin({
22
+ reactDispatch,
23
+ popupMountRef,
24
+ createAnalyticsEvent,
25
+ typeAheadHandlers,
26
+ getIntl
27
+ }) {
28
+ const intl = getIntl();
29
+ const {
30
+ createDecorations,
31
+ removeDecorations
32
+ } = factoryDecorations({
33
+ intl,
34
+ popupMountRef,
35
+ createAnalyticsEvent: createAnalyticsEvent
36
+ });
37
+ const reducer = createReducer({
38
+ createDecorations,
39
+ removeDecorations,
40
+ typeAheadHandlers,
41
+ popupMountRef
42
+ });
43
+ return new SafePlugin({
44
+ key: pluginKey,
45
+ state: {
46
+ init() {
47
+ return {
48
+ typeAheadHandlers,
49
+ query: '',
50
+ decorationSet: DecorationSet.empty,
51
+ decorationElement: null,
52
+ items: [],
53
+ selectedIndex: -1,
54
+ stats: null,
55
+ inputMethod: null
56
+ };
57
+ },
58
+ apply(tr, currentPluginState, oldEditorState, state) {
59
+ const customStep = hasValidTypeAheadStep(tr);
60
+ const nextPluginState = reducer(tr, currentPluginState, customStep);
61
+ if (currentPluginState !== nextPluginState) {
62
+ reactDispatch(pluginKey, nextPluginState);
63
+ }
64
+ return nextPluginState;
65
+ }
66
+ },
67
+ appendTransaction(transactions, _oldState, newState) {
68
+ const insertItemCallback = isInsertionTransaction(transactions, ACTIONS.INSERT_RAW_QUERY);
69
+ if (insertItemCallback) {
70
+ const tr = insertItemCallback(newState);
71
+ if (tr) {
72
+ return tr;
73
+ }
74
+ }
75
+ },
76
+ view() {
77
+ return {
78
+ update(editorView) {}
79
+ };
80
+ },
81
+ props: {
82
+ decorations: state => {
83
+ var _pluginKey$getState;
84
+ return (_pluginKey$getState = pluginKey.getState(state)) === null || _pluginKey$getState === void 0 ? void 0 : _pluginKey$getState.decorationSet;
85
+ },
86
+ handleDOMEvents: {
87
+ compositionend: (view, event) => {
88
+ return false;
89
+ },
90
+ click: (view, event) => {
91
+ const {
92
+ target
93
+ } = event;
94
+ // ProseMirror view listen to any click event inside of it
95
+ // When this event is coming from the typeahead
96
+ // we should tell to ProseMirror to sit down and relax
97
+ // cuz we know what we are doing (I hope)
98
+ if (target instanceof HTMLElement && closest(target, `[data-type-ahead=${TYPE_AHEAD_DECORATION_DATA_ATTRIBUTE}]`)) {
99
+ return true;
100
+ }
101
+ return false;
102
+ }
103
+ }
104
+ }
105
+ });
106
+ }
@@ -0,0 +1,160 @@
1
+ import { InsertTypeAheadStages, InsertTypeAheadStep } from '@atlaskit/adf-schema/steps';
2
+ import { INPUT_METHOD } from '@atlaskit/editor-common/analytics';
3
+ import { DecorationSet } from '@atlaskit/editor-prosemirror/view';
4
+ import { isTypeAheadHandler } from '../utils';
5
+ import { ACTIONS } from './actions';
6
+ import { pluginKey } from './key';
7
+ const shouldForceOpen = step => {
8
+ if (!(step instanceof InsertTypeAheadStep)) {
9
+ return false;
10
+ }
11
+ const isDeletionRawQueryOperation = step.isInsertionStep() && step.stage === InsertTypeAheadStages.DELETING_RAW_QUERY;
12
+ const isUndoingInsertionItem = step.isUndoingStep() && step.stage === InsertTypeAheadStages.INSERTING_ITEM;
13
+ return isDeletionRawQueryOperation || isUndoingInsertionItem;
14
+ };
15
+ const shouldForceClose = step => {
16
+ if (!(step instanceof InsertTypeAheadStep)) {
17
+ return false;
18
+ }
19
+ const isInsertingItem = step.isInsertionStep() && step.stage === InsertTypeAheadStages.INSERTING_ITEM;
20
+ const isUndoingDeletionRawQuery = step.isUndoingStep() && step.stage === InsertTypeAheadStages.DELETING_RAW_QUERY;
21
+ return isInsertingItem || isUndoingDeletionRawQuery;
22
+ };
23
+ const createFindHandler = typeAheadHandlers => step => {
24
+ if (!(step instanceof InsertTypeAheadStep)) {
25
+ return null;
26
+ }
27
+ const handler = typeAheadHandlers.find(h => h.trigger === step.trigger);
28
+ return handler || null;
29
+ };
30
+ export const createReducer = ({
31
+ typeAheadHandlers,
32
+ removeDecorations,
33
+ createDecorations
34
+ }) => {
35
+ const findHandler = createFindHandler(typeAheadHandlers);
36
+ const openMenu = (currentPluginState, {
37
+ tr,
38
+ triggerHandler,
39
+ inputMethod,
40
+ reopenQuery,
41
+ selectedIndex
42
+ }) => {
43
+ removeDecorations(currentPluginState.decorationSet);
44
+ const {
45
+ decorationSet,
46
+ decorationElement,
47
+ stats
48
+ } = createDecorations(tr, {
49
+ triggerHandler,
50
+ inputMethod,
51
+ reopenQuery
52
+ });
53
+ return {
54
+ ...currentPluginState,
55
+ stats,
56
+ decorationSet,
57
+ triggerHandler,
58
+ decorationElement,
59
+ inputMethod,
60
+ selectedIndex: typeof selectedIndex === 'number' ? selectedIndex : -1,
61
+ items: [],
62
+ query: reopenQuery || ''
63
+ };
64
+ };
65
+ const closeMenu = currentPluginState => {
66
+ removeDecorations(currentPluginState.decorationSet);
67
+ return {
68
+ ...currentPluginState,
69
+ inputMethod: null,
70
+ query: '',
71
+ decorationElement: null,
72
+ decorationSet: DecorationSet.empty,
73
+ stats: null,
74
+ triggerHandler: undefined,
75
+ items: []
76
+ };
77
+ };
78
+ return (tr, currentPluginState, typeAheadStepOverride) => {
79
+ const meta = tr.getMeta(pluginKey) || {};
80
+
81
+ // This code below controls when we should force
82
+ // the menu to open or close during undo/redo operations
83
+ const overrideHandler = findHandler(typeAheadStepOverride);
84
+ if (typeAheadStepOverride && overrideHandler && shouldForceOpen(typeAheadStepOverride)) {
85
+ return openMenu(currentPluginState, {
86
+ tr,
87
+ triggerHandler: overrideHandler,
88
+ inputMethod: INPUT_METHOD.KEYBOARD,
89
+ reopenQuery: typeAheadStepOverride.query,
90
+ selectedIndex: typeAheadStepOverride.selectedIndex
91
+ });
92
+ } else if (shouldForceClose(typeAheadStepOverride)) {
93
+ return closeMenu(currentPluginState);
94
+ }
95
+ const {
96
+ action,
97
+ params
98
+ } = meta || {};
99
+ const shouldOpenMenu = action === ACTIONS.OPEN_TYPEAHEAD_AT_CURSOR && isTypeAheadHandler(params === null || params === void 0 ? void 0 : params.triggerHandler);
100
+ const selectionChanged = tr.selectionSet && (tr.isGeneric || Boolean(tr.getMeta('pointer')));
101
+ const shouldCloseMenu = [ACTIONS.CLOSE_TYPE_AHEAD, ACTIONS.INSERT_ITEM].includes(action) || selectionChanged;
102
+ const shouldUpdateQuery = action === ACTIONS.CHANGE_QUERY;
103
+ const shouldUpdateListItems = action === ACTIONS.UPDATE_LIST_ITEMS;
104
+ const shouldUpdateSelectedIndex = action === ACTIONS.UPDATE_SELECTED_INDEX;
105
+ if (shouldOpenMenu) {
106
+ return openMenu(currentPluginState, {
107
+ tr,
108
+ triggerHandler: params.triggerHandler,
109
+ inputMethod: params.inputMethod
110
+ });
111
+ } else if (shouldCloseMenu) {
112
+ return closeMenu(currentPluginState);
113
+ } else if (shouldUpdateQuery) {
114
+ return {
115
+ ...currentPluginState,
116
+ query: params.query
117
+ };
118
+ } else if (shouldUpdateListItems) {
119
+ const {
120
+ items
121
+ } = params;
122
+ const {
123
+ selectedIndex
124
+ } = currentPluginState;
125
+ return {
126
+ ...currentPluginState,
127
+ items,
128
+ selectedIndex: Math.max(selectedIndex >= items.length ? items.length - 1 : selectedIndex, -1)
129
+ };
130
+ } else if (shouldUpdateSelectedIndex) {
131
+ return {
132
+ ...currentPluginState,
133
+ selectedIndex: params.selectedIndex
134
+ };
135
+ }
136
+ if (tr.docChanged) {
137
+ const {
138
+ decorationSet
139
+ } = currentPluginState;
140
+ const onRemove = () => {
141
+ // Make sure we are unmounting the component
142
+ // from the react tree when this decoration is removed
143
+ removeDecorations(currentPluginState.decorationSet);
144
+ };
145
+ const mappedDecorationSet = decorationSet.map(tr.mapping, tr.doc, {
146
+ onRemove
147
+ });
148
+
149
+ // return same pluginState if decorationSet did not change
150
+ if (mappedDecorationSet === currentPluginState.decorationSet) {
151
+ return currentPluginState;
152
+ }
153
+ return {
154
+ ...currentPluginState,
155
+ decorationSet: mappedDecorationSet
156
+ };
157
+ }
158
+ return currentPluginState;
159
+ };
160
+ };
@@ -0,0 +1,12 @@
1
+ import { pluginKey } from './key';
2
+ export const isInsertionTransaction = (transactions, action) => {
3
+ var _tr$getMeta2;
4
+ const tr = transactions.find(tr => {
5
+ var _tr$getMeta;
6
+ return ((_tr$getMeta = tr.getMeta(pluginKey)) === null || _tr$getMeta === void 0 ? void 0 : _tr$getMeta.action) === action;
7
+ });
8
+ if (!tr) {
9
+ return null;
10
+ }
11
+ return (_tr$getMeta2 = tr.getMeta(pluginKey)) === null || _tr$getMeta2 === void 0 ? void 0 : _tr$getMeta2.params;
12
+ };
@@ -0,0 +1,33 @@
1
+ import _defineProperty from "@babel/runtime/helpers/defineProperty";
2
+ export class StatsModifier {
3
+ constructor() {
4
+ _defineProperty(this, "startedAt", 0);
5
+ _defineProperty(this, "endedAt", 0);
6
+ _defineProperty(this, "keyCount", {
7
+ arrowUp: 0,
8
+ arrowDown: 0
9
+ });
10
+ _defineProperty(this, "increaseArrowUp", () => {
11
+ this.keyCount.arrowUp += 1;
12
+ });
13
+ _defineProperty(this, "increaseArrowDown", () => {
14
+ this.keyCount.arrowDown += 1;
15
+ });
16
+ _defineProperty(this, "serialize", () => {
17
+ var _this$keyCount, _this$keyCount2;
18
+ return {
19
+ startedAt: this.startedAt,
20
+ endedAt: performance.now(),
21
+ keyCount: {
22
+ arrowUp: ((_this$keyCount = this.keyCount) === null || _this$keyCount === void 0 ? void 0 : _this$keyCount.arrowUp) || 0,
23
+ arrowDown: ((_this$keyCount2 = this.keyCount) === null || _this$keyCount2 === void 0 ? void 0 : _this$keyCount2.arrowDown) || 0
24
+ }
25
+ };
26
+ });
27
+ this.startedAt = performance.now();
28
+ this.keyCount = {
29
+ arrowUp: 0,
30
+ arrowDown: 0
31
+ };
32
+ }
33
+ }
@@ -0,0 +1,7 @@
1
+ import { ACTIONS } from '../pm-plugins/actions';
2
+ import { pluginKey } from '../pm-plugins/key';
3
+ export const closeTypeAhead = tr => {
4
+ return tr.setMeta(pluginKey, {
5
+ action: ACTIONS.CLOSE_TYPE_AHEAD
6
+ });
7
+ };
@@ -0,0 +1,71 @@
1
+ import { GapCursorSelection } from '@atlaskit/editor-common/selection';
2
+ import { TextSelection } from '@atlaskit/editor-prosemirror/state';
3
+ import { ACTIONS } from '../pm-plugins/actions';
4
+ import { pluginKey } from '../pm-plugins/key';
5
+ export const openTypeAhead = props => tr => {
6
+ const {
7
+ triggerHandler,
8
+ inputMethod,
9
+ query
10
+ } = props;
11
+ tr.setMeta(pluginKey, {
12
+ action: ACTIONS.OPEN_TYPEAHEAD_AT_CURSOR,
13
+ params: {
14
+ triggerHandler,
15
+ inputMethod,
16
+ query
17
+ }
18
+ });
19
+ };
20
+ export const openTypeAheadAtCursor = ({
21
+ triggerHandler,
22
+ inputMethod,
23
+ query
24
+ }) => ({
25
+ tr
26
+ }) => {
27
+ openTypeAhead({
28
+ triggerHandler,
29
+ inputMethod,
30
+ query
31
+ })(tr);
32
+ const {
33
+ selection
34
+ } = tr;
35
+ if (!(selection instanceof TextSelection || selection instanceof GapCursorSelection)) {
36
+ return tr;
37
+ }
38
+ if (selection instanceof GapCursorSelection) {
39
+ // Create space for the typeahead menu in gap cursor
40
+ tr.insertText(' ');
41
+ // delete 1 pos before wherever selection is now - that will delete the empty space
42
+ tr.delete(tr.selection.from - 1, tr.selection.from);
43
+ } else {
44
+ var _selection$$head, _selection$$head$pare, _selection$$head$pare2, _selection$$head$pare3;
45
+ if (!selection.$cursor) {
46
+ tr.deleteSelection();
47
+ return tr;
48
+ }
49
+
50
+ // Search & Destroy placeholder
51
+ const cursorPos = selection.$cursor.pos;
52
+ const nodeAtCursor = tr.doc.nodeAt(cursorPos);
53
+ const isPlaceholderAtCursorPosition = nodeAtCursor && nodeAtCursor.type.name === 'placeholder';
54
+ if (nodeAtCursor && isPlaceholderAtCursorPosition) {
55
+ tr.delete(cursorPos, cursorPos + nodeAtCursor.nodeSize);
56
+ }
57
+
58
+ // ME-2375 remove the superfluous '@' inserted before decoration
59
+ // by composition (https://github.com/ProseMirror/prosemirror/issues/903)
60
+ //
61
+ // Update:
62
+ // Now also handles any use case with superfluous typeahead triggers (ie. '@', ':', '/')
63
+ // being inserted due to composition by checking if we have the trigger
64
+ // directly before the typeahead. This should not happen unless it has
65
+ // been eroneously added because we require whitespace/newline for typeahead.
66
+ if (cursorPos >= 2 && !!(selection !== null && selection !== void 0 && (_selection$$head = selection.$head) !== null && _selection$$head !== void 0 && (_selection$$head$pare = _selection$$head.parent) !== null && _selection$$head$pare !== void 0 && _selection$$head$pare.textContent) && (_selection$$head$pare2 = (_selection$$head$pare3 = selection.$head.parent.textContent).endsWith) !== null && _selection$$head$pare2 !== void 0 && _selection$$head$pare2.call(_selection$$head$pare3, triggerHandler.trigger)) {
67
+ tr.delete(cursorPos - 1, cursorPos);
68
+ }
69
+ }
70
+ return tr;
71
+ };
@@ -0,0 +1,10 @@
1
+ import { TextSelection } from '@atlaskit/editor-prosemirror/state';
2
+ export const setSelectionBeforeQuery = rawText => tr => {
3
+ const currentPosition = tr.selection.$from.pos;
4
+ const positionBeforeRawText = Math.max(currentPosition - rawText.length, 0);
5
+ const resolvedPositionBeforeText = tr.doc.resolve(positionBeforeRawText);
6
+ const nextSelection = TextSelection.findFrom(resolvedPositionBeforeText, -1, true);
7
+ if (nextSelection) {
8
+ tr.setSelection(nextSelection);
9
+ }
10
+ };
@@ -0,0 +1,88 @@
1
+ import _defineProperty from "@babel/runtime/helpers/defineProperty";
2
+ /** @jsx jsx */
3
+
4
+ import React from 'react';
5
+ import { css, jsx } from '@emotion/react';
6
+ import debounce from 'lodash/debounce';
7
+ const statusDebounceMillis = 1400;
8
+ const assitiveTextStyles = css({
9
+ border: 0,
10
+ clip: 'rect(0 0 0 0)',
11
+ height: '1px',
12
+ marginbottom: '-1px',
13
+ marginright: '-1px',
14
+ overflow: 'hidden',
15
+ padding: 0,
16
+ position: 'absolute',
17
+ whitespace: 'nowrap',
18
+ width: '1px'
19
+ });
20
+ class AssistveTextComponent extends React.Component {
21
+ constructor(...args) {
22
+ super(...args);
23
+ _defineProperty(this, "state", {
24
+ bump: false,
25
+ //when the same text needs to be read again, Hence it needs to be toggled between __status--A and __status--B
26
+ debounced: false,
27
+ silenced: false
28
+ });
29
+ }
30
+ componentWillMount() {
31
+ this.debounceStatusUpdate = debounce(() => {
32
+ if (!this.state.debounced) {
33
+ const shouldSilence = !this.props.isInFocus;
34
+ this.setState(({
35
+ bump
36
+ }) => ({
37
+ bump: !bump,
38
+ debounced: true,
39
+ silenced: shouldSilence
40
+ }));
41
+ }
42
+ }, statusDebounceMillis);
43
+ }
44
+ componentWillUnmount() {
45
+ this.debounceStatusUpdate.cancel();
46
+ }
47
+ componentWillReceiveProps() {
48
+ this.setState(({
49
+ bump
50
+ }) => ({
51
+ bump: !bump,
52
+ debounced: false
53
+ }));
54
+ }
55
+ render() {
56
+ const {
57
+ assistiveText,
58
+ id
59
+ } = this.props;
60
+ const {
61
+ bump,
62
+ debounced,
63
+ silenced
64
+ } = this.state;
65
+ this.debounceStatusUpdate();
66
+ return jsx("div", {
67
+ css: assitiveTextStyles
68
+ }, jsx("div", {
69
+ id: id + '__status--A',
70
+ role: "status",
71
+ "aria-atomic": "true",
72
+ "aria-live": "polite"
73
+ }, `${!silenced && debounced && bump ? assistiveText : ''}`), jsx("div", {
74
+ id: id + '__status--B',
75
+ role: "status",
76
+ "aria-atomic": "true",
77
+ "aria-live": "polite"
78
+ }, `${!silenced && debounced && !bump ? assistiveText : ''}`));
79
+ }
80
+ }
81
+ _defineProperty(AssistveTextComponent, "defaultProps", {
82
+ statusDebounceMillis: 1400,
83
+ debounce: true,
84
+ assistiveText: '',
85
+ isInFocus: false,
86
+ id: ''
87
+ });
88
+ export const AssistiveText = AssistveTextComponent;