@atlaskit/editor-plugin-type-ahead 12.0.7 → 12.1.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 CHANGED
@@ -1,5 +1,27 @@
1
1
  # @atlaskit/editor-plugin-type-ahead
2
2
 
3
+ ## 12.1.1
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies
8
+
9
+ ## 12.1.0
10
+
11
+ ### Minor Changes
12
+
13
+ - [`dbf22f118dd9e`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/dbf22f118dd9e) -
14
+ Rewrite Rovo Chat @-mention provider on a single Relay-native RovoChatMentionResource that fires
15
+ people + agents in parallel via Promise. People render as soon as URS resolves and agents pop in
16
+ below when ready. New aliased agentStudio_getAgents query consolidates 3 prefetch round trips into
17
+ 1, drops over-fetched fields, and uses real abort plumbing. Adds opt-in subscribeToItemsUpdates
18
+ multi-emit contract to the editor mention typeahead so progressive emissions are no longer dropped
19
+ after the first frame.
20
+
21
+ ### Patch Changes
22
+
23
+ - Updated dependencies
24
+
3
25
  ## 12.0.7
4
26
 
5
27
  ### Patch Changes
@@ -21,14 +21,15 @@ var useLoadItems = exports.useLoadItems = function useLoadItems(triggerHandler,
21
21
  setItems = _useState2[1];
22
22
  var componentIsMounted = (0, _react.useRef)(true);
23
23
  var editorViewRef = (0, _react.useRef)(editorView);
24
+ var latestQueryRef = (0, _react.useRef)(query);
25
+ latestQueryRef.current = query;
24
26
  (0, _react.useEffect)(function () {
25
- var getItems = triggerHandler === null || triggerHandler === void 0 ? void 0 : triggerHandler.getItems;
26
- if (!getItems) {
27
- setItems(EMPTY_LIST_ITEM);
28
- return;
29
- }
27
+ var requestQuery = query;
28
+ var isStaleRequest = function isStaleRequest() {
29
+ return latestQueryRef.current !== requestQuery;
30
+ };
30
31
  var options = {
31
- query: query || '',
32
+ query: requestQuery || '',
32
33
  editorState: editorView.state
33
34
  };
34
35
  var view = editorViewRef.current;
@@ -38,10 +39,19 @@ var useLoadItems = exports.useLoadItems = function useLoadItems(triggerHandler,
38
39
  api === null || api === void 0 || api.core.actions.execute((0, _clearListError.clearListError)());
39
40
  });
40
41
  }
41
- getItems(options).then(function (result) {
42
+
43
+ /**
44
+ * Shared render pipeline used by both the single-shot `getItems`
45
+ * Promise path and the opt-in multi-emit `subscribeToItemsUpdates`
46
+ * path. Renders the dropdown with whatever items it is given.
47
+ *
48
+ * Both paths share the empty-state injection, section-building
49
+ * and "View more" placeholder logic — extracting this helper
50
+ * avoids any chance the two paths drift in subtle rendering
51
+ * behaviour.
52
+ */
53
+ var renderResult = function renderResult(result) {
42
54
  var _triggerHandler$getEm;
43
- // Inject empty list item here so it flows through the normal list rendering and keyboard
44
- // navigation paths
45
55
  var emptyItem = result.length === 0 && (0, _expValEquals.expValEquals)('platform_editor_insert_menu_ai', 'isEnabled', true) ? (_triggerHandler$getEm = triggerHandler.getEmptyItem) === null || _triggerHandler$getEm === void 0 ? void 0 : _triggerHandler$getEm.call(triggerHandler, {
46
56
  editorState: editorView.state
47
57
  }) : undefined;
@@ -56,15 +66,18 @@ var useLoadItems = exports.useLoadItems = function useLoadItems(triggerHandler,
56
66
  if (componentIsMounted.current) {
57
67
  setItems(list);
58
68
  }
59
-
60
- // Add a placeholder item for view more button so that it can be traversed with arrow keys
61
69
  var viewMoreItem = {
62
70
  title: 'View more'
63
71
  };
64
72
  queueMicrotask(function () {
65
73
  (0, _updateListItems.updateListItem)(showViewMore ? list.concat(viewMoreItem) : list, sections)(view.state, view.dispatch);
66
74
  });
67
- }).catch(function (e) {
75
+ };
76
+
77
+ // Matches the pre-refactor implicit-any contract for the original
78
+ // `.catch((e) => updateListError(e)(...))` path.
79
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
80
+ var handleError = function handleError(e) {
68
81
  if ((0, _experiments.editorExperiment)('platform_editor_offline_editing_web', true)) {
69
82
  if (e) {
70
83
  if (componentIsMounted.current) {
@@ -75,7 +88,42 @@ var useLoadItems = exports.useLoadItems = function useLoadItems(triggerHandler,
75
88
  });
76
89
  }
77
90
  }
78
- });
91
+ };
92
+
93
+ // Multi-emit path: handler opts in by implementing
94
+ // `subscribeToItemsUpdates`. The handler controls when to push
95
+ // updates; we re-render on every push for the lifetime of this
96
+ // effect.
97
+ var subscribeToItemsUpdates = triggerHandler === null || triggerHandler === void 0 ? void 0 : triggerHandler.subscribeToItemsUpdates;
98
+ if (subscribeToItemsUpdates) {
99
+ var stale = false;
100
+ var _subscribeToItemsUpda = subscribeToItemsUpdates(options),
101
+ initial = _subscribeToItemsUpda.initial,
102
+ subscribe = _subscribeToItemsUpda.subscribe;
103
+ initial.then(function (result) {
104
+ if (!stale && !isStaleRequest()) {
105
+ renderResult(result);
106
+ }
107
+ }).catch(handleError);
108
+ var unsubscribe = subscribe(function (items) {
109
+ if (stale || isStaleRequest() || !componentIsMounted.current) {
110
+ return;
111
+ }
112
+ renderResult(items);
113
+ });
114
+ return function () {
115
+ stale = true;
116
+ unsubscribe();
117
+ };
118
+ }
119
+
120
+ // Existing single-shot path. Unchanged in behaviour.
121
+ var getItems = triggerHandler === null || triggerHandler === void 0 ? void 0 : triggerHandler.getItems;
122
+ if (!getItems) {
123
+ setItems(EMPTY_LIST_ITEM);
124
+ return;
125
+ }
126
+ getItems(options).then(renderResult).catch(handleError);
79
127
 
80
128
  // ignore because EditorView is mutable but we don't want to
81
129
  // call loadItems when it changes, only when the query changes
@@ -10,14 +10,13 @@ export const useLoadItems = (triggerHandler, editorView, query, showViewMore, ap
10
10
  const [items, setItems] = useState(EMPTY_LIST_ITEM);
11
11
  const componentIsMounted = useRef(true);
12
12
  const editorViewRef = useRef(editorView);
13
+ const latestQueryRef = useRef(query);
14
+ latestQueryRef.current = query;
13
15
  useEffect(() => {
14
- const getItems = triggerHandler === null || triggerHandler === void 0 ? void 0 : triggerHandler.getItems;
15
- if (!getItems) {
16
- setItems(EMPTY_LIST_ITEM);
17
- return;
18
- }
16
+ const requestQuery = query;
17
+ const isStaleRequest = () => latestQueryRef.current !== requestQuery;
19
18
  const options = {
20
- query: query || '',
19
+ query: requestQuery || '',
21
20
  editorState: editorView.state
22
21
  };
23
22
  const {
@@ -29,10 +28,19 @@ export const useLoadItems = (triggerHandler, editorView, query, showViewMore, ap
29
28
  api === null || api === void 0 ? void 0 : api.core.actions.execute(clearListError());
30
29
  });
31
30
  }
32
- getItems(options).then(result => {
31
+
32
+ /**
33
+ * Shared render pipeline used by both the single-shot `getItems`
34
+ * Promise path and the opt-in multi-emit `subscribeToItemsUpdates`
35
+ * path. Renders the dropdown with whatever items it is given.
36
+ *
37
+ * Both paths share the empty-state injection, section-building
38
+ * and "View more" placeholder logic — extracting this helper
39
+ * avoids any chance the two paths drift in subtle rendering
40
+ * behaviour.
41
+ */
42
+ const renderResult = result => {
33
43
  var _triggerHandler$getEm;
34
- // Inject empty list item here so it flows through the normal list rendering and keyboard
35
- // navigation paths
36
44
  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, {
37
45
  editorState: editorView.state
38
46
  }) : undefined;
@@ -48,15 +56,18 @@ export const useLoadItems = (triggerHandler, editorView, query, showViewMore, ap
48
56
  if (componentIsMounted.current) {
49
57
  setItems(list);
50
58
  }
51
-
52
- // Add a placeholder item for view more button so that it can be traversed with arrow keys
53
59
  const viewMoreItem = {
54
60
  title: 'View more'
55
61
  };
56
62
  queueMicrotask(() => {
57
63
  updateListItem(showViewMore ? list.concat(viewMoreItem) : list, sections)(view.state, view.dispatch);
58
64
  });
59
- }).catch(e => {
65
+ };
66
+
67
+ // Matches the pre-refactor implicit-any contract for the original
68
+ // `.catch((e) => updateListError(e)(...))` path.
69
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
70
+ const handleError = e => {
60
71
  if (editorExperiment('platform_editor_offline_editing_web', true)) {
61
72
  if (e) {
62
73
  if (componentIsMounted.current) {
@@ -67,7 +78,43 @@ export const useLoadItems = (triggerHandler, editorView, query, showViewMore, ap
67
78
  });
68
79
  }
69
80
  }
70
- });
81
+ };
82
+
83
+ // Multi-emit path: handler opts in by implementing
84
+ // `subscribeToItemsUpdates`. The handler controls when to push
85
+ // updates; we re-render on every push for the lifetime of this
86
+ // effect.
87
+ const subscribeToItemsUpdates = triggerHandler === null || triggerHandler === void 0 ? void 0 : triggerHandler.subscribeToItemsUpdates;
88
+ if (subscribeToItemsUpdates) {
89
+ let stale = false;
90
+ const {
91
+ initial,
92
+ subscribe
93
+ } = subscribeToItemsUpdates(options);
94
+ initial.then(result => {
95
+ if (!stale && !isStaleRequest()) {
96
+ renderResult(result);
97
+ }
98
+ }).catch(handleError);
99
+ const unsubscribe = subscribe(items => {
100
+ if (stale || isStaleRequest() || !componentIsMounted.current) {
101
+ return;
102
+ }
103
+ renderResult(items);
104
+ });
105
+ return () => {
106
+ stale = true;
107
+ unsubscribe();
108
+ };
109
+ }
110
+
111
+ // Existing single-shot path. Unchanged in behaviour.
112
+ const getItems = triggerHandler === null || triggerHandler === void 0 ? void 0 : triggerHandler.getItems;
113
+ if (!getItems) {
114
+ setItems(EMPTY_LIST_ITEM);
115
+ return;
116
+ }
117
+ getItems(options).then(renderResult).catch(handleError);
71
118
 
72
119
  // ignore because EditorView is mutable but we don't want to
73
120
  // call loadItems when it changes, only when the query changes
@@ -14,14 +14,15 @@ export var useLoadItems = function useLoadItems(triggerHandler, editorView, quer
14
14
  setItems = _useState2[1];
15
15
  var componentIsMounted = useRef(true);
16
16
  var editorViewRef = useRef(editorView);
17
+ var latestQueryRef = useRef(query);
18
+ latestQueryRef.current = query;
17
19
  useEffect(function () {
18
- var getItems = triggerHandler === null || triggerHandler === void 0 ? void 0 : triggerHandler.getItems;
19
- if (!getItems) {
20
- setItems(EMPTY_LIST_ITEM);
21
- return;
22
- }
20
+ var requestQuery = query;
21
+ var isStaleRequest = function isStaleRequest() {
22
+ return latestQueryRef.current !== requestQuery;
23
+ };
23
24
  var options = {
24
- query: query || '',
25
+ query: requestQuery || '',
25
26
  editorState: editorView.state
26
27
  };
27
28
  var view = editorViewRef.current;
@@ -31,10 +32,19 @@ export var useLoadItems = function useLoadItems(triggerHandler, editorView, quer
31
32
  api === null || api === void 0 || api.core.actions.execute(clearListError());
32
33
  });
33
34
  }
34
- getItems(options).then(function (result) {
35
+
36
+ /**
37
+ * Shared render pipeline used by both the single-shot `getItems`
38
+ * Promise path and the opt-in multi-emit `subscribeToItemsUpdates`
39
+ * path. Renders the dropdown with whatever items it is given.
40
+ *
41
+ * Both paths share the empty-state injection, section-building
42
+ * and "View more" placeholder logic — extracting this helper
43
+ * avoids any chance the two paths drift in subtle rendering
44
+ * behaviour.
45
+ */
46
+ var renderResult = function renderResult(result) {
35
47
  var _triggerHandler$getEm;
36
- // Inject empty list item here so it flows through the normal list rendering and keyboard
37
- // navigation paths
38
48
  var 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, {
39
49
  editorState: editorView.state
40
50
  }) : undefined;
@@ -49,15 +59,18 @@ export var useLoadItems = function useLoadItems(triggerHandler, editorView, quer
49
59
  if (componentIsMounted.current) {
50
60
  setItems(list);
51
61
  }
52
-
53
- // Add a placeholder item for view more button so that it can be traversed with arrow keys
54
62
  var viewMoreItem = {
55
63
  title: 'View more'
56
64
  };
57
65
  queueMicrotask(function () {
58
66
  updateListItem(showViewMore ? list.concat(viewMoreItem) : list, sections)(view.state, view.dispatch);
59
67
  });
60
- }).catch(function (e) {
68
+ };
69
+
70
+ // Matches the pre-refactor implicit-any contract for the original
71
+ // `.catch((e) => updateListError(e)(...))` path.
72
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
73
+ var handleError = function handleError(e) {
61
74
  if (editorExperiment('platform_editor_offline_editing_web', true)) {
62
75
  if (e) {
63
76
  if (componentIsMounted.current) {
@@ -68,7 +81,42 @@ export var useLoadItems = function useLoadItems(triggerHandler, editorView, quer
68
81
  });
69
82
  }
70
83
  }
71
- });
84
+ };
85
+
86
+ // Multi-emit path: handler opts in by implementing
87
+ // `subscribeToItemsUpdates`. The handler controls when to push
88
+ // updates; we re-render on every push for the lifetime of this
89
+ // effect.
90
+ var subscribeToItemsUpdates = triggerHandler === null || triggerHandler === void 0 ? void 0 : triggerHandler.subscribeToItemsUpdates;
91
+ if (subscribeToItemsUpdates) {
92
+ var stale = false;
93
+ var _subscribeToItemsUpda = subscribeToItemsUpdates(options),
94
+ initial = _subscribeToItemsUpda.initial,
95
+ subscribe = _subscribeToItemsUpda.subscribe;
96
+ initial.then(function (result) {
97
+ if (!stale && !isStaleRequest()) {
98
+ renderResult(result);
99
+ }
100
+ }).catch(handleError);
101
+ var unsubscribe = subscribe(function (items) {
102
+ if (stale || isStaleRequest() || !componentIsMounted.current) {
103
+ return;
104
+ }
105
+ renderResult(items);
106
+ });
107
+ return function () {
108
+ stale = true;
109
+ unsubscribe();
110
+ };
111
+ }
112
+
113
+ // Existing single-shot path. Unchanged in behaviour.
114
+ var getItems = triggerHandler === null || triggerHandler === void 0 ? void 0 : triggerHandler.getItems;
115
+ if (!getItems) {
116
+ setItems(EMPTY_LIST_ITEM);
117
+ return;
118
+ }
119
+ getItems(options).then(renderResult).catch(handleError);
72
120
 
73
121
  // ignore because EditorView is mutable but we don't want to
74
122
  // call loadItems when it changes, only when the query changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/editor-plugin-type-ahead",
3
- "version": "12.0.7",
3
+ "version": "12.1.1",
4
4
  "description": "Type-ahead plugin for @atlaskit/editor-core",
5
5
  "author": "Atlassian Pty Ltd",
6
6
  "license": "Apache-2.0",
@@ -36,8 +36,8 @@
36
36
  "@atlaskit/primitives": "^20.0.0",
37
37
  "@atlaskit/prosemirror-history": "^1.0.0",
38
38
  "@atlaskit/prosemirror-input-rules": "^4.0.0",
39
- "@atlaskit/tmp-editor-statsig": "^109.2.0",
40
- "@atlaskit/tokens": "^14.0.0",
39
+ "@atlaskit/tmp-editor-statsig": "^111.0.0",
40
+ "@atlaskit/tokens": "^15.0.0",
41
41
  "@atlaskit/visually-hidden": "^4.0.0",
42
42
  "@babel/runtime": "^7.0.0",
43
43
  "@emotion/react": "^11.7.1",
@@ -48,7 +48,7 @@
48
48
  "w3c-keyname": "^2.1.8"
49
49
  },
50
50
  "peerDependencies": {
51
- "@atlaskit/editor-common": "^116.10.0",
51
+ "@atlaskit/editor-common": "^116.11.0",
52
52
  "react": "^18.2.0",
53
53
  "react-dom": "^18.2.0",
54
54
  "react-intl": "^5.25.1 || ^6.0.0 || ^7.0.0"